From 64caa7dcf46ed6139b766dbe77fbd7353899417f Mon Sep 17 00:00:00 2001 From: Jeff Hao Date: Thu, 29 Aug 2013 11:18:01 -0700 Subject: [PATCH 0001/2402] Change IsMethodTracingActive to GetMethodTracingMode for art. This allows traceview to tell whether sampling or just normal method profiling is enabled. Change-Id: I518a1888a90bc50568fe56bf708d801027ac98d7 --- runtime/native/dalvik_system_VMDebug.cc | 6 +++--- runtime/trace.cc | 12 +++++++++--- runtime/trace.h | 8 +++++++- 3 files changed, 19 insertions(+), 7 deletions(-) diff --git a/runtime/native/dalvik_system_VMDebug.cc b/runtime/native/dalvik_system_VMDebug.cc index ae45701a531..96c3e78a43e 100644 --- a/runtime/native/dalvik_system_VMDebug.cc +++ b/runtime/native/dalvik_system_VMDebug.cc @@ -96,8 +96,8 @@ static void VMDebug_startMethodTracingFilename(JNIEnv* env, jclass, jstring java Trace::Start(traceFilename.c_str(), -1, bufferSize, flags, false, false, 0); } -static jboolean VMDebug_isMethodTracingActive(JNIEnv*, jclass) { - return Trace::IsMethodTracingActive(); +static jint VMDebug_getMethodTracingMode(JNIEnv*, jclass) { + return Trace::GetMethodTracingMode(); } static void VMDebug_stopMethodTracing(JNIEnv*, jclass) { @@ -317,7 +317,7 @@ static JNINativeMethod gMethods[] = { NATIVE_METHOD(VMDebug, infopoint, "(I)V"), NATIVE_METHOD(VMDebug, isDebuggerConnected, "()Z"), NATIVE_METHOD(VMDebug, isDebuggingEnabled, "()Z"), - NATIVE_METHOD(VMDebug, isMethodTracingActive, "()Z"), + NATIVE_METHOD(VMDebug, getMethodTracingMode, "()I"), NATIVE_METHOD(VMDebug, lastDebuggerActivity, "()J"), NATIVE_METHOD(VMDebug, printLoadedClasses, "(I)V"), NATIVE_METHOD(VMDebug, resetAllocCount, "(I)V"), diff --git a/runtime/trace.cc b/runtime/trace.cc index 6d040e15dc1..7b25306177c 100644 --- a/runtime/trace.cc +++ b/runtime/trace.cc @@ -427,14 +427,20 @@ void Trace::Stop() { } void Trace::Shutdown() { - if (IsMethodTracingActive()) { + if (GetMethodTracingMode() != kTracingInactive) { Stop(); } } -bool Trace::IsMethodTracingActive() { +TracingMode Trace::GetMethodTracingMode() { MutexLock mu(Thread::Current(), *Locks::trace_lock_); - return the_trace_ != NULL; + if (the_trace_ == NULL) { + return kTracingInactive; + } else if (the_trace_->sampling_enabled_) { + return kSampleProfilingActive; + } else { + return kMethodTracingActive; + } } Trace::Trace(File* trace_file, int buffer_size, int flags, bool sampling_enabled) diff --git a/runtime/trace.h b/runtime/trace.h index 06cb6a6d5be..ffcb36d2941 100644 --- a/runtime/trace.h +++ b/runtime/trace.h @@ -42,6 +42,12 @@ enum ProfilerClockSource { kProfilerClockSourceDual, // Both wall and thread CPU clocks. }; +enum TracingMode { + kTracingInactive, + kMethodTracingActive, + kSampleProfilingActive, +}; + class Trace : public instrumentation::InstrumentationListener { public: enum TraceFlag { @@ -58,7 +64,7 @@ class Trace : public instrumentation::InstrumentationListener { Locks::trace_lock_); static void Stop() LOCKS_EXCLUDED(Locks::trace_lock_); static void Shutdown() LOCKS_EXCLUDED(Locks::trace_lock_); - static bool IsMethodTracingActive() LOCKS_EXCLUDED(Locks::trace_lock_); + static TracingMode GetMethodTracingMode() LOCKS_EXCLUDED(Locks::trace_lock_); bool UseWallClock(); bool UseThreadCpuClock(); From 6e288203c06c71c945935830b91a215a273bf8e6 Mon Sep 17 00:00:00 2001 From: Brian Carlstrom Date: Thu, 29 Aug 2013 18:19:14 -0700 Subject: [PATCH 0002/2402] Revert "Update ART for LLVM merge up to r187914." This reverts commit 70814f7746793934a29e010211ef6e652ad75cd2. --- compiler/dex/portable/mir_to_gbc.cc | 2 +- compiler/llvm/llvm_compilation_unit.cc | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/compiler/dex/portable/mir_to_gbc.cc b/compiler/dex/portable/mir_to_gbc.cc index 90cec750398..7831cf6f7a1 100644 --- a/compiler/dex/portable/mir_to_gbc.cc +++ b/compiler/dex/portable/mir_to_gbc.cc @@ -1972,7 +1972,7 @@ void MirConverter::MethodMIR2Bitcode() { ::llvm::OwningPtr< ::llvm::tool_output_file> out_file( new ::llvm::tool_output_file(fname.c_str(), errmsg, - ::llvm::sys::fs::F_Binary)); + ::llvm::raw_fd_ostream::F_Binary)); if (!errmsg.empty()) { LOG(ERROR) << "Failed to create bitcode output file: " << errmsg; diff --git a/compiler/llvm/llvm_compilation_unit.cc b/compiler/llvm/llvm_compilation_unit.cc index 139100bee9e..aa439ccbaed 100644 --- a/compiler/llvm/llvm_compilation_unit.cc +++ b/compiler/llvm/llvm_compilation_unit.cc @@ -214,6 +214,7 @@ bool LlvmCompilationUnit::MaterializeToRawOStream(::llvm::raw_ostream& out_strea ::llvm::TargetOptions target_options; target_options.FloatABIType = ::llvm::FloatABI::Soft; target_options.NoFramePointerElim = true; + target_options.NoFramePointerElimNonLeaf = true; target_options.UseSoftFloat = false; target_options.EnableFastISel = false; @@ -257,7 +258,7 @@ bool LlvmCompilationUnit::MaterializeToRawOStream(::llvm::raw_ostream& out_strea ::llvm::OwningPtr< ::llvm::tool_output_file> out_file( new ::llvm::tool_output_file(bitcode_filename_.c_str(), errmsg, - ::llvm::sys::fs::F_Binary)); + ::llvm::raw_fd_ostream::F_Binary)); if (!errmsg.empty()) { @@ -277,6 +278,7 @@ bool LlvmCompilationUnit::MaterializeToRawOStream(::llvm::raw_ostream& out_strea // pm_builder.Inliner = ::llvm::createAlwaysInlinerPass(); // pm_builder.Inliner = ::llvm::createPartialInliningPass(); pm_builder.OptLevel = 3; + pm_builder.DisableSimplifyLibCalls = 1; pm_builder.DisableUnitAtATime = 1; pm_builder.populateFunctionPassManager(fpm); pm_builder.populateModulePassManager(pm); From 919b11c3edbc9aacff979cb5e702ae40f10615ae Mon Sep 17 00:00:00 2001 From: Brian Carlstrom Date: Fri, 30 Aug 2013 13:30:36 -0700 Subject: [PATCH 0003/2402] Remove conscrypt dependency from CommonTest Bug: 10092469 Change-Id: I0c5dc72625ca4b5083ef3137d40334127bde8252 --- compiler/oat_test.cc | 2 +- runtime/common_test.h | 10 ---------- 2 files changed, 1 insertion(+), 11 deletions(-) diff --git a/compiler/oat_test.cc b/compiler/oat_test.cc index 74b5da9eff8..9ed264288bb 100644 --- a/compiler/oat_test.cc +++ b/compiler/oat_test.cc @@ -104,7 +104,7 @@ TEST_F(OatTest, WriteRead) { ASSERT_TRUE(oat_file.get() != NULL); const OatHeader& oat_header = oat_file->GetOatHeader(); ASSERT_TRUE(oat_header.IsValid()); - ASSERT_EQ(2U, oat_header.GetDexFileCount()); // core and conscrypt + ASSERT_EQ(1U, oat_header.GetDexFileCount()); // core ASSERT_EQ(42U, oat_header.GetImageFileLocationOatChecksum()); ASSERT_EQ(4096U, oat_header.GetImageFileLocationOatDataBegin()); ASSERT_EQ("lue.art", oat_header.GetImageFileLocation()); diff --git a/runtime/common_test.h b/runtime/common_test.h index dc1f5922d91..fe54d0341d7 100644 --- a/runtime/common_test.h +++ b/runtime/common_test.h @@ -286,12 +286,7 @@ class CommonTest : public testing::Test { if (java_lang_dex_file_ == NULL) { LOG(FATAL) << "Could not open .dex file '" << GetLibCoreDexFileName() << "'\n"; } - conscrypt_file_ = DexFile::Open(GetConscryptFileName(), GetConscryptFileName()); - if (conscrypt_file_ == NULL) { - LOG(FATAL) << "Could not open .dex file '" << GetConscryptFileName() << "'\n"; - } boot_class_path_.push_back(java_lang_dex_file_); - boot_class_path_.push_back(conscrypt_file_); std::string min_heap_string(StringPrintf("-Xms%zdm", gc::Heap::kDefaultInitialSize / MB)); std::string max_heap_string(StringPrintf("-Xmx%zdm", gc::Heap::kDefaultMaximumSize / MB)); @@ -398,10 +393,6 @@ class CommonTest : public testing::Test { return GetDexFileName("core-libart"); } - std::string GetConscryptFileName() { - return GetDexFileName("conscrypt"); - } - std::string GetDexFileName(const std::string& jar_prefix) { if (IsHost()) { const char* host_dir = getenv("ANDROID_HOST_OUT"); @@ -520,7 +511,6 @@ class CommonTest : public testing::Test { std::string android_data_; std::string dalvik_cache_; const DexFile* java_lang_dex_file_; // owned by runtime_ - const DexFile* conscrypt_file_; // owned by runtime_ std::vector boot_class_path_; UniquePtr runtime_; // Owned by the runtime From 8e4d3ed463df1a9f5fdc1e927a6afe6d208558e1 Mon Sep 17 00:00:00 2001 From: Elliott Hughes Date: Tue, 3 Sep 2013 17:41:50 -0700 Subject: [PATCH 0004/2402] Remove redundant complexity from JNI aborts. Change-Id: I7aca6c661f0dc868e33fd354d0ed27bf17361b28 --- runtime/check_jni.cc | 17 ++--------------- 1 file changed, 2 insertions(+), 15 deletions(-) diff --git a/runtime/check_jni.cc b/runtime/check_jni.cc index 6a7ceeed0b9..54f314391b4 100644 --- a/runtime/check_jni.cc +++ b/runtime/check_jni.cc @@ -35,10 +35,6 @@ #include "scoped_thread_state_change.h" #include "thread.h" -#define LIBCORE_CPP_JNI_HELPERS -#include // from libcore -#undef LIBCORE_CPP_JNI_HELPERS - namespace art { static void JniAbort(const char* jni_function_name, const char* msg) { @@ -814,17 +810,8 @@ class ScopedCheck { ThrowLocation throw_location; mirror::Throwable* exception = self->GetException(&throw_location); std::string type(PrettyTypeOf(exception)); - // TODO: write native code that doesn't require allocation for dumping an exception. - // TODO: do we care any more? art always dumps pending exceptions on aborting threads. - bool with_stack_trace = (type != "java.lang.OutOfMemoryError"); - if (with_stack_trace) { - JniAbortF(function_name_, "JNI %s called with pending exception '%s' thrown in %s\n%s", - function_name_, type.c_str(), throw_location.Dump().c_str(), - jniGetStackTrace(soa_.Env()).c_str()); - } else { - JniAbortF(function_name_, "JNI %s called with pending exception '%s' thrown in %s", - function_name_, type.c_str(), throw_location.Dump().c_str()); - } + JniAbortF(function_name_, "JNI %s called with pending exception '%s' thrown in %s", + function_name_, type.c_str(), throw_location.Dump().c_str()); return; } } From df6931437880e1f7da9f777f5e54474d7ed7a630 Mon Sep 17 00:00:00 2001 From: Anwar Ghuloum Date: Wed, 4 Sep 2013 12:22:30 -0700 Subject: [PATCH 0005/2402] Remove memory leaks Change-Id: I13a74791b1d39edfbba7c7884057fa163c343a9a --- compiler/driver/compiler_driver.cc | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc index cbd9020df4d..31aec63b02e 100644 --- a/compiler/driver/compiler_driver.cc +++ b/compiler/driver/compiler_driver.cc @@ -1585,13 +1585,11 @@ void CompilerDriver::ResolveDexFile(jobject class_loader, const DexFile& dex_fil if (IsImage()) { // For images we resolve all types, such as array, whereas for applications just those with // classdefs are resolved by ResolveClassFieldsAndMethods. - // TODO: strdup memory leak. - timings.NewSplit(strdup(("Resolve " + dex_file.GetLocation() + " Types").c_str())); + timings.NewSplit("Resolve Types"); context.ForAll(0, dex_file.NumTypeIds(), ResolveType, thread_count_); } - // TODO: strdup memory leak. - timings.NewSplit(strdup(("Resolve " + dex_file.GetLocation() + " MethodsAndFields").c_str())); + timings.NewSplit("Resolve MethodsAndFields"); context.ForAll(0, dex_file.NumClassDefs(), ResolveClassFieldsAndMethods, thread_count_); } @@ -1652,8 +1650,7 @@ static void VerifyClass(const ParallelCompilationManager* manager, size_t class_ void CompilerDriver::VerifyDexFile(jobject class_loader, const DexFile& dex_file, ThreadPool& thread_pool, base::TimingLogger& timings) { - // TODO: strdup memory leak. - timings.NewSplit(strdup(("Verify " + dex_file.GetLocation()).c_str())); + timings.NewSplit("Verify Dex File"); ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); ParallelCompilationManager context(class_linker, class_loader, this, &dex_file, thread_pool); context.ForAll(0, dex_file.NumClassDefs(), VerifyClass, thread_count_); @@ -2150,8 +2147,7 @@ static void InitializeClass(const ParallelCompilationManager* manager, size_t cl void CompilerDriver::InitializeClasses(jobject jni_class_loader, const DexFile& dex_file, ThreadPool& thread_pool, base::TimingLogger& timings) { - // TODO: strdup memory leak. - timings.NewSplit(strdup(("InitializeNoClinit " + dex_file.GetLocation()).c_str())); + timings.NewSplit("InitializeNoClinit"); #ifndef NDEBUG // Sanity check blacklist descriptors. if (IsImage()) { @@ -2258,8 +2254,7 @@ void CompilerDriver::CompileClass(const ParallelCompilationManager* manager, siz void CompilerDriver::CompileDexFile(jobject class_loader, const DexFile& dex_file, ThreadPool& thread_pool, base::TimingLogger& timings) { - // TODO: strdup memory leak. - timings.NewSplit(strdup(("Compile " + dex_file.GetLocation()).c_str())); + timings.NewSplit("Compile Dex File"); ParallelCompilationManager context(Runtime::Current()->GetClassLinker(), class_loader, this, &dex_file, thread_pool); context.ForAll(0, dex_file.NumClassDefs(), CompilerDriver::CompileClass, thread_count_); From f38ea80594ad9cc0d68fe88a1dcfba6c7f70705c Mon Sep 17 00:00:00 2001 From: Hiroshi Yamauchi Date: Tue, 27 Aug 2013 13:04:26 -0700 Subject: [PATCH 0006/2402] A char array copy optimization (art). - Based on measurements, copy char by char for arrays of length <= 64. - With this change, the Ritz MemAllocBench got ~25% faster on Nexus 4 and ~20% faster on host. - This change only handles arraycopy calls in the core libraries and char arrays with the rest future work. Bug: 7103825 Change-Id: If536f9ea68ecf277b604199130d2060c446f640c --- runtime/native/java_lang_System.cc | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/runtime/native/java_lang_System.cc b/runtime/native/java_lang_System.cc index 30b4dc7ef52..100f5a9b186 100644 --- a/runtime/native/java_lang_System.cc +++ b/runtime/native/java_lang_System.cc @@ -316,6 +316,28 @@ static void System_arraycopy(JNIEnv* env, jclass, jobject javaSrc, jint srcPos, } } +static void System_arraycopyCharUnchecked(JNIEnv* env, jclass, jobject javaSrc, jint srcPos, jobject javaDst, jint dstPos, jint length) { + ScopedObjectAccess soa(env); + DCHECK(javaSrc != NULL); + DCHECK(javaDst != NULL); + mirror::Object* srcObject = soa.Decode(javaSrc); + mirror::Object* dstObject = soa.Decode(javaDst); + DCHECK(srcObject->IsArrayInstance()); + DCHECK(dstObject->IsArrayInstance()); + mirror::Array* srcArray = srcObject->AsArray(); + mirror::Array* dstArray = dstObject->AsArray(); + DCHECK(srcPos >= 0 && dstPos >= 0 && length >= 0 && + srcPos + length <= srcArray->GetLength() && dstPos + length <= dstArray->GetLength()); + DCHECK_EQ(srcArray->GetClass()->GetComponentType(), dstArray->GetClass()->GetComponentType()); + DCHECK(srcArray->GetClass()->GetComponentType()->IsPrimitive()); + DCHECK(dstArray->GetClass()->GetComponentType()->IsPrimitive()); + DCHECK_EQ(srcArray->GetClass()->GetComponentSize(), static_cast(2)); + DCHECK_EQ(dstArray->GetClass()->GetComponentSize(), static_cast(2)); + uint8_t* dstBytes = reinterpret_cast(dstArray->GetRawData(2)); + const uint8_t* srcBytes = reinterpret_cast(srcArray->GetRawData(2)); + move16(dstBytes + dstPos * 2, srcBytes + srcPos * 2, length * 2); +} + static jint System_identityHashCode(JNIEnv* env, jclass, jobject javaObject) { ScopedObjectAccess soa(env); mirror::Object* o = soa.Decode(javaObject); @@ -324,6 +346,7 @@ static jint System_identityHashCode(JNIEnv* env, jclass, jobject javaObject) { static JNINativeMethod gMethods[] = { NATIVE_METHOD(System, arraycopy, "(Ljava/lang/Object;ILjava/lang/Object;II)V"), + NATIVE_METHOD(System, arraycopyCharUnchecked, "([CI[CII)V"), NATIVE_METHOD(System, identityHashCode, "(Ljava/lang/Object;)I"), }; From c642ec8987746a2a44b990bd5354306242d709da Mon Sep 17 00:00:00 2001 From: Jeff Hao Date: Wed, 4 Sep 2013 16:11:55 -0700 Subject: [PATCH 0007/2402] Fix verifier upcasting type after instance_of. The verifier automatically changed the type of a register to be the checked type in an instance_of instruction, even if the checked type was the register type's superclass. This would loosen the type information of the register and cause problems later. Bug: 10614872 Change-Id: I67aa2c66be754d946e928b8a64431f193539b842 --- runtime/verifier/method_verifier.cc | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc index 34a0f73fdf4..fa00c610172 100644 --- a/runtime/verifier/method_verifier.cc +++ b/runtime/verifier/method_verifier.cc @@ -1921,9 +1921,12 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { (instance_of_inst->VRegA_22c() != instance_of_inst->VRegB_22c())) { // Check that the we are not attempting conversion to interface types, // which is not done because of the multiple inheritance implications. + // Also don't change the type if it would result in an upcast. + const RegType& orig_type = work_line_->GetRegisterType(instance_of_inst->VRegB_22c()); const RegType& cast_type = ResolveClassAndCheckAccess(instance_of_inst->VRegC_22c()); - if (!cast_type.IsUnresolvedTypes() && !cast_type.GetClass()->IsInterface()) { + if (!cast_type.IsUnresolvedTypes() && !cast_type.GetClass()->IsInterface() && + !cast_type.IsAssignableFrom(orig_type)) { RegisterLine* update_line = new RegisterLine(code_item_->registers_size_, this); if (inst->Opcode() == Instruction::IF_EQZ) { fallthrough_line.reset(update_line); From d133b97b1ccae88f6ee7040e288fd7a239ee4492 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Thu, 5 Sep 2013 11:01:30 -0700 Subject: [PATCH 0008/2402] Shard dedupe set locks. We're seeing contention during compilation on the dedupe locks, sharding 4 ways on an occam brings down contention by > 5x. Improve dedupe hash function to have a FNV hash function at its heart. Improve naming of dedupe locks. Tidy portable JNI compiler paramters to be pointers, given that's their primary use. Change-Id: I95d905f2ca5fee4e83a0034926a5f6501b4aeb79 --- compiler/driver/compiler_driver.cc | 6 ++- compiler/driver/compiler_driver.h | 36 +++++++++++------ compiler/jni/portable/jni_compiler.cc | 4 +- compiler/jni/portable/jni_compiler.h | 4 +- compiler/llvm/compiler_llvm.cc | 2 +- compiler/utils/dedupe_set.h | 57 ++++++++++++++------------- compiler/utils/dedupe_set_test.cc | 2 +- 7 files changed, 65 insertions(+), 46 deletions(-) diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc index 31aec63b02e..495458efab0 100644 --- a/compiler/driver/compiler_driver.cc +++ b/compiler/driver/compiler_driver.cc @@ -355,7 +355,11 @@ CompilerDriver::CompilerDriver(CompilerBackend compiler_backend, InstructionSet jni_compiler_(NULL), compiler_enable_auto_elf_loading_(NULL), compiler_get_method_code_addr_(NULL), - support_boot_image_fixup_(true) { + support_boot_image_fixup_(true), + dedupe_code_("dedupe code"), + dedupe_mapping_table_("dedupe mapping table"), + dedupe_vmap_table_("dedupe vmap table"), + dedupe_gc_map_("dedupe gc map") { CHECK_PTHREAD_CALL(pthread_key_create, (&tls_key_, NULL), "compiler tls key"); diff --git a/compiler/driver/compiler_driver.h b/compiler/driver/compiler_driver.h index cd6b5fab022..c5c53e387d3 100644 --- a/compiler/driver/compiler_driver.h +++ b/compiler/driver/compiler_driver.h @@ -449,27 +449,39 @@ class CompilerDriver { class DedupeHashFunc { public: size_t operator()(const std::vector& array) const { - // Take a random sample of bytes. + // For small arrays compute a hash using every byte. static const size_t kSmallArrayThreshold = 16; - static const size_t kRandomHashCount = 16; - size_t hash = 0; - if (array.size() < kSmallArrayThreshold) { - for (auto c : array) { - hash = hash * 54 + c; + size_t hash = 0x811c9dc5; + if (array.size() <= kSmallArrayThreshold) { + for (uint8_t b : array) { + hash = (hash * 16777619) ^ b; } } else { - for (size_t i = 0; i < kRandomHashCount; ++i) { + // For larger arrays use the first 4 bytes and then select a number of other values at + // random. + static const size_t kRandomHashCount = 16; + for (size_t i = 0; i < 4; ++i) { + uint8_t b = array[i]; + hash = (hash * 16777619) ^ b; + } + for (size_t i = 4; i < kRandomHashCount; ++i) { size_t r = i * 1103515245 + 12345; - hash = hash * 54 + array[r % array.size()]; + uint8_t b = array[r % array.size()]; + hash = (hash * 16777619) ^ b; } } + hash += hash << 13; + hash ^= hash >> 7; + hash += hash << 3; + hash ^= hash >> 17; + hash += hash << 5; return hash; } }; - DedupeSet, size_t, DedupeHashFunc> dedupe_code_; - DedupeSet, size_t, DedupeHashFunc> dedupe_mapping_table_; - DedupeSet, size_t, DedupeHashFunc> dedupe_vmap_table_; - DedupeSet, size_t, DedupeHashFunc> dedupe_gc_map_; + DedupeSet, size_t, DedupeHashFunc, 4> dedupe_code_; + DedupeSet, size_t, DedupeHashFunc, 4> dedupe_mapping_table_; + DedupeSet, size_t, DedupeHashFunc, 4> dedupe_vmap_table_; + DedupeSet, size_t, DedupeHashFunc, 4> dedupe_gc_map_; DISALLOW_COPY_AND_ASSIGN(CompilerDriver); }; diff --git a/compiler/jni/portable/jni_compiler.cc b/compiler/jni/portable/jni_compiler.cc index 43408a7d64b..0c14346ad84 100644 --- a/compiler/jni/portable/jni_compiler.cc +++ b/compiler/jni/portable/jni_compiler.cc @@ -50,9 +50,9 @@ using ::art::llvm::runtime_support::JniMethodStartSynchronized; using ::art::llvm::runtime_support::RuntimeId; JniCompiler::JniCompiler(LlvmCompilationUnit* cunit, - CompilerDriver& driver, + CompilerDriver* driver, const DexCompilationUnit* dex_compilation_unit) - : cunit_(cunit), driver_(&driver), module_(cunit_->GetModule()), + : cunit_(cunit), driver_(driver), module_(cunit_->GetModule()), context_(cunit_->GetLLVMContext()), irb_(*cunit_->GetIRBuilder()), dex_compilation_unit_(dex_compilation_unit), func_(NULL), elf_func_idx_(0) { diff --git a/compiler/jni/portable/jni_compiler.h b/compiler/jni/portable/jni_compiler.h index d20c63bc1e6..ffabfe61c27 100644 --- a/compiler/jni/portable/jni_compiler.h +++ b/compiler/jni/portable/jni_compiler.h @@ -54,7 +54,7 @@ class IRBuilder; class JniCompiler { public: JniCompiler(LlvmCompilationUnit* cunit, - CompilerDriver& driver, + CompilerDriver* driver, const DexCompilationUnit* dex_compilation_unit); CompiledMethod* Compile(); @@ -67,7 +67,7 @@ class JniCompiler { private: LlvmCompilationUnit* cunit_; - CompilerDriver* driver_; + CompilerDriver* const driver_; ::llvm::Module* module_; ::llvm::LLVMContext* context_; diff --git a/compiler/llvm/compiler_llvm.cc b/compiler/llvm/compiler_llvm.cc index fd440d5bf08..83b0c75e049 100644 --- a/compiler/llvm/compiler_llvm.cc +++ b/compiler/llvm/compiler_llvm.cc @@ -164,7 +164,7 @@ CompileNativeMethod(DexCompilationUnit* dex_compilation_unit) { UniquePtr cunit(AllocateCompilationUnit()); UniquePtr jni_compiler( - new JniCompiler(cunit.get(), *compiler_driver_, dex_compilation_unit)); + new JniCompiler(cunit.get(), compiler_driver_, dex_compilation_unit)); return jni_compiler->Compile(); } diff --git a/compiler/utils/dedupe_set.h b/compiler/utils/dedupe_set.h index f3d35d728c2..53c1afa6983 100644 --- a/compiler/utils/dedupe_set.h +++ b/compiler/utils/dedupe_set.h @@ -18,62 +18,65 @@ #define ART_COMPILER_UTILS_DEDUPE_SET_H_ #include +#include #include "base/mutex.h" #include "base/stl_util.h" namespace art { -// A simple data structure to handle hashed deduplication. Add is thread safe. -template +// A set of Keys that support a HashFunc returning HashType. Used to find duplicates of Key in the +// Add method. The data-structure is thread-safe through the use of internal locks, it also +// supports the lock being sharded. +template class DedupeSet { typedef std::pair HashedKey; class Comparator { public: bool operator()(const HashedKey& a, const HashedKey& b) const { - if (a.first < b.first) return true; - if (a.first > b.first) return true; - return *a.second < *b.second; + if (a.first != b.first) { + return a.first < b.first; + } else { + return *a.second < *b.second; + } } }; - typedef std::set Keys; - public: - typedef typename Keys::iterator iterator; - typedef typename Keys::const_iterator const_iterator; - typedef typename Keys::size_type size_type; - typedef typename Keys::value_type value_type; - - iterator begin() { return keys_.begin(); } - const_iterator begin() const { return keys_.begin(); } - iterator end() { return keys_.end(); } - const_iterator end() const { return keys_.end(); } - Key* Add(Thread* self, const Key& key) { - HashType hash = HashFunc()(key); - HashedKey hashed_key(hash, const_cast(&key)); - MutexLock lock(self, lock_); - auto it = keys_.find(hashed_key); - if (it != keys_.end()) { + HashType raw_hash = HashFunc()(key); + HashType shard_hash = raw_hash / kShard; + HashType shard_bin = raw_hash % kShard; + HashedKey hashed_key(shard_hash, const_cast(&key)); + MutexLock lock(self, *lock_[shard_bin]); + auto it = keys_[shard_bin].find(hashed_key); + if (it != keys_[shard_bin].end()) { return it->second; } hashed_key.second = new Key(key); - keys_.insert(hashed_key); + keys_[shard_bin].insert(hashed_key); return hashed_key.second; } - DedupeSet() : lock_("dedupe lock") { + explicit DedupeSet(const char* set_name) { + for (HashType i = 0; i < kShard; ++i) { + lock_name_[i] = StringPrintf("%s lock %d", set_name, i); + lock_[i].reset(new Mutex(lock_name_[i].c_str())); + } } ~DedupeSet() { - STLDeleteValues(&keys_); + for (HashType i = 0; i < kShard; ++i) { + STLDeleteValues(&keys_[i]); + } } private: - Mutex lock_ DEFAULT_MUTEX_ACQUIRED_AFTER; - Keys keys_; + std::string lock_name_[kShard]; + UniquePtr lock_[kShard]; + std::set keys_[kShard]; + DISALLOW_COPY_AND_ASSIGN(DedupeSet); }; diff --git a/compiler/utils/dedupe_set_test.cc b/compiler/utils/dedupe_set_test.cc index 9f5e292f530..03d8b961fa5 100644 --- a/compiler/utils/dedupe_set_test.cc +++ b/compiler/utils/dedupe_set_test.cc @@ -38,7 +38,7 @@ class DedupeHashFunc { TEST_F(DedupeSetTest, Test) { Thread* self = Thread::Current(); typedef std::vector ByteArray; - DedupeSet deduplicator; + DedupeSet deduplicator("test"); ByteArray* array1; { ByteArray test1; From 1e54d68ce8e77dfe63340275d11a072c5184c89a Mon Sep 17 00:00:00 2001 From: Sebastien Hertz Date: Fri, 6 Sep 2013 14:52:10 +0200 Subject: [PATCH 0009/2402] Disable devirtualization detection in DEX-to-DEX compiler. This CL allows the DEX-to-DEX compiler to disable devirtualization detection. This allows to quicken invoke-virtual/range instructions that used to be eligible for devirtualization. Bug: 10632943 Change-Id: I6c9f4d3249cf42b47f004be5825b3186fa83501e --- compiler/dex/dex_to_dex_compiler.cc | 5 +++-- compiler/dex/mir_dataflow.cc | 2 +- compiler/dex/quick/gen_invoke.cc | 2 +- compiler/driver/compiler_driver.cc | 10 +++++----- compiler/driver/compiler_driver.h | 3 ++- compiler/llvm/gbc_expander.cc | 2 +- 6 files changed, 13 insertions(+), 11 deletions(-) diff --git a/compiler/dex/dex_to_dex_compiler.cc b/compiler/dex/dex_to_dex_compiler.cc index a0e2c1e9aad..2a4d3d5ee62 100644 --- a/compiler/dex/dex_to_dex_compiler.cc +++ b/compiler/dex/dex_to_dex_compiler.cc @@ -298,11 +298,12 @@ void DexCompiler::CompileInvokeVirtual(Instruction* inst, int vtable_idx; uintptr_t direct_code; uintptr_t direct_method; + // TODO: support devirtualization. + const bool kEnableDevirtualization = false; bool fast_path = driver_.ComputeInvokeInfo(&unit_, dex_pc, invoke_type, target_method, vtable_idx, direct_code, direct_method, - false); - // TODO: support devirtualization. + false, kEnableDevirtualization); if (fast_path && original_invoke_type == invoke_type) { if (vtable_idx >= 0 && IsUint(16, vtable_idx)) { VLOG(compiler) << "Quickening " << Instruction::Name(inst->Opcode()) diff --git a/compiler/dex/mir_dataflow.cc b/compiler/dex/mir_dataflow.cc index 3a73717a7b0..42ca5dcfdf5 100644 --- a/compiler/dex/mir_dataflow.cc +++ b/compiler/dex/mir_dataflow.cc @@ -1224,7 +1224,7 @@ bool MIRGraph::InvokeUsesMethodStar(MIR* mir) { type, target_method, vtable_idx, direct_code, direct_method, - false) && + false, true) && !(cu_->enable_debug & (1 << kDebugSlowInvokePath)); return (((type == kDirect) || (type == kStatic)) && fast_path && ((direct_code == 0) || (direct_method == 0))); diff --git a/compiler/dex/quick/gen_invoke.cc b/compiler/dex/quick/gen_invoke.cc index 073b550d782..a4ab15db5ed 100644 --- a/compiler/dex/quick/gen_invoke.cc +++ b/compiler/dex/quick/gen_invoke.cc @@ -1359,7 +1359,7 @@ void Mir2Lir::GenInvoke(CallInfo* info) { info->type, target_method, vtable_idx, direct_code, direct_method, - true) && !SLOW_INVOKE_PATH; + true, true) && !SLOW_INVOKE_PATH; if (info->type == kInterface) { if (fast_path) { p_null_ck = &null_ck; diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc index 634d3bc9c0a..1468b87e4ee 100644 --- a/compiler/driver/compiler_driver.cc +++ b/compiler/driver/compiler_driver.cc @@ -1099,7 +1099,7 @@ bool CompilerDriver::ComputeInvokeInfo(const DexCompilationUnit* mUnit, const ui MethodReference& target_method, int& vtable_idx, uintptr_t& direct_code, uintptr_t& direct_method, - bool update_stats) { + bool update_stats, bool enable_devirtualization) { ScopedObjectAccess soa(Thread::Current()); vtable_idx = -1; direct_code = 0; @@ -1130,7 +1130,7 @@ bool CompilerDriver::ComputeInvokeInfo(const DexCompilationUnit* mUnit, const ui } if (referrer_class->CanAccess(methods_class) && referrer_class->CanAccessMember(methods_class, resolved_method->GetAccessFlags())) { - const bool kEnableFinalBasedSharpening = true; + const bool enableFinalBasedSharpening = enable_devirtualization; // Sharpen a virtual call into a direct call when the target is known not to have been // overridden (ie is final). bool can_sharpen_virtual_based_on_type = @@ -1142,7 +1142,7 @@ bool CompilerDriver::ComputeInvokeInfo(const DexCompilationUnit* mUnit, const ui resolved_method->GetMethodIndex() < methods_class->GetVTable()->GetLength() && (methods_class->GetVTable()->Get(resolved_method->GetMethodIndex()) == resolved_method); - if (kEnableFinalBasedSharpening && (can_sharpen_virtual_based_on_type || + if (enableFinalBasedSharpening && (can_sharpen_virtual_based_on_type || can_sharpen_super_based_on_type)) { // Sharpen a virtual call into a direct call. The method_idx is into referrer's // dex cache, check that this resolved method is where we expect it. @@ -1157,8 +1157,8 @@ bool CompilerDriver::ComputeInvokeInfo(const DexCompilationUnit* mUnit, const ui invoke_type = kDirect; return true; } - const bool kEnableVerifierBasedSharpening = true; - if (kEnableVerifierBasedSharpening && (invoke_type == kVirtual || + const bool enableVerifierBasedSharpening = enable_devirtualization; + if (enableVerifierBasedSharpening && (invoke_type == kVirtual || invoke_type == kInterface)) { // Did the verifier record a more precise invoke target based on its type information? const MethodReference caller_method(mUnit->GetDexFile(), mUnit->GetDexMethodIndex()); diff --git a/compiler/driver/compiler_driver.h b/compiler/driver/compiler_driver.h index fa1b8f9854c..c324590d13e 100644 --- a/compiler/driver/compiler_driver.h +++ b/compiler/driver/compiler_driver.h @@ -183,7 +183,8 @@ class CompilerDriver { // index. bool ComputeInvokeInfo(const DexCompilationUnit* mUnit, const uint32_t dex_pc, InvokeType& type, MethodReference& target_method, int& vtable_idx, - uintptr_t& direct_code, uintptr_t& direct_method, bool update_stats) + uintptr_t& direct_code, uintptr_t& direct_method, bool update_stats, + bool enable_devirtualization) LOCKS_EXCLUDED(Locks::mutator_lock_); bool IsSafeCast(const MethodReference& mr, uint32_t dex_pc); diff --git a/compiler/llvm/gbc_expander.cc b/compiler/llvm/gbc_expander.cc index 4f6fa0a2dfe..19c80498bd2 100644 --- a/compiler/llvm/gbc_expander.cc +++ b/compiler/llvm/gbc_expander.cc @@ -849,7 +849,7 @@ llvm::Value* GBCExpanderPass::EmitInvoke(llvm::CallInst& call_inst) { invoke_type, target_method, vtable_idx, direct_code, direct_method, - true); + true, true); // Load the method object llvm::Value* callee_method_object_addr = NULL; From 2672a9f93caa66add6ca48a8e38ba1661ef43959 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Thu, 5 Sep 2013 17:24:22 -0700 Subject: [PATCH 0010/2402] Use exit to avoid destructors in dex2oat. Change-Id: I5f8b396dfa614af2935053c219b09b406a632618 --- dex2oat/dex2oat.cc | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc index 3848b6def01..c4cce2ff732 100644 --- a/dex2oat/dex2oat.cc +++ b/dex2oat/dex2oat.cc @@ -17,6 +17,7 @@ #include #include #include +#include #include #include @@ -1072,6 +1073,13 @@ static int dex2oat(int argc, char** argv) { if (dump_timing || (dump_slow_timing && timings.GetTotalNs() > MsToNs(1000))) { LOG(INFO) << Dumpable(timings); } + + // Everything was successfully written, do an explicit exit here to avoid running Runtime + // destructors that take time (bug 10645725) unless we're a debug build or running on valgrind. + if (!kIsDebugBuild || (RUNNING_ON_VALGRIND == 0)) { + exit(EXIT_SUCCESS); + } + return EXIT_SUCCESS; } From 41c65c19c15ffac41089fa9f37502f94c046960d Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Thu, 5 Sep 2013 16:57:17 -0700 Subject: [PATCH 0011/2402] Add a pool of small cat1 constants to the verifier. This avoids creating constants in roughly half the cases seen in boot/core. Also, strengthen the types on a few of the RegTypeCache routines, add some documentation and move methods to the appropriate location and out of being public when possible. Change-Id: I384189d51d8f097bb7f744c0f6275dee4bb11302 --- runtime/verifier/reg_type.cc | 11 ++- runtime/verifier/reg_type_cache-inl.h | 21 +++--- runtime/verifier/reg_type_cache.cc | 96 ++++++++++++++++++--------- runtime/verifier/reg_type_cache.h | 63 +++++++++++------- 4 files changed, 119 insertions(+), 72 deletions(-) diff --git a/runtime/verifier/reg_type.cc b/runtime/verifier/reg_type.cc index 25f840cc563..857acb8743a 100644 --- a/runtime/verifier/reg_type.cc +++ b/runtime/verifier/reg_type.cc @@ -99,7 +99,7 @@ std::string PreciseConstType::Dump() const { } std::string BooleanType::Dump() const { - return "boolean"; + return "Boolean"; } std::string ConflictType::Dump() const { @@ -111,7 +111,7 @@ std::string ByteType::Dump() const { } std::string ShortType::Dump() const { - return "short"; + return "Short"; } std::string CharType::Dump() const { @@ -119,15 +119,15 @@ std::string CharType::Dump() const { } std::string FloatType::Dump() const { - return "float"; + return "Float"; } std::string LongLoType::Dump() const { - return "long (Low Half)"; + return "Long (Low Half)"; } std::string LongHiType::Dump() const { - return "long (High Half)"; + return "Long (High Half)"; } std::string DoubleLoType::Dump() const { @@ -461,7 +461,6 @@ std::string ImpreciseConstType::Dump() const { std::stringstream result; uint32_t val = ConstantValue(); if (val == 0) { - CHECK(IsPreciseConstant()); result << "Zero/null"; } else { result << "Imprecise "; diff --git a/runtime/verifier/reg_type_cache-inl.h b/runtime/verifier/reg_type_cache-inl.h index 295e27198d5..fc9e5c98f7b 100644 --- a/runtime/verifier/reg_type_cache-inl.h +++ b/runtime/verifier/reg_type_cache-inl.h @@ -23,17 +23,6 @@ namespace art { namespace verifier { -template -Type* RegTypeCache::CreatePrimitiveTypeInstance(const std::string& descriptor) { - mirror::Class* klass = NULL; - // Try loading the class from linker. - if (!descriptor.empty()) { - klass = art::Runtime::Current()->GetClassLinker()->FindSystemClass(descriptor.c_str()); - } - Type* entry = Type::CreateInstance(klass, descriptor, RegTypeCache::primitive_count_); - RegTypeCache::primitive_count_++; - return entry; -} inline const art::verifier::RegType& RegTypeCache::GetFromId(uint16_t id) const { DCHECK_LT(id, entries_.size()); @@ -41,6 +30,16 @@ inline const art::verifier::RegType& RegTypeCache::GetFromId(uint16_t id) const DCHECK(result != NULL); return *result; } + +inline const ConstantType& RegTypeCache::FromCat1Const(int32_t value, bool precise) { + // We only expect 0 to be a precise constant. + DCHECK(value != 0 || precise); + if (precise && (value >= kMinSmallConstant) && (value <= kMaxSmallConstant)) { + return *small_precise_constants_[value - kMinSmallConstant]; + } + return FromCat1NonSmallConstant(value, precise); +} + } // namespace verifier } // namespace art #endif // ART_RUNTIME_VERIFIER_REG_TYPE_CACHE_INL_H_ diff --git a/runtime/verifier/reg_type_cache.cc b/runtime/verifier/reg_type_cache.cc index 2c18132c0b8..ce465a415d7 100644 --- a/runtime/verifier/reg_type_cache.cc +++ b/runtime/verifier/reg_type_cache.cc @@ -26,8 +26,8 @@ namespace art { namespace verifier { bool RegTypeCache::primitive_initialized_ = false; -uint16_t RegTypeCache::primitive_start_ = 0; uint16_t RegTypeCache::primitive_count_ = 0; +PreciseConstType* RegTypeCache::small_precise_constants_[kMaxSmallConstant - kMinSmallConstant + 1]; static bool MatchingPrecisionForClass(RegType* entry, bool precise) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { @@ -44,7 +44,7 @@ static bool MatchingPrecisionForClass(RegType* entry, bool precise) } } -void RegTypeCache::FillPrimitiveTypes() { +void RegTypeCache::FillPrimitiveAndSmallConstantTypes() { entries_.push_back(UndefinedType::GetInstance()); entries_.push_back(ConflictType::GetInstance()); entries_.push_back(BooleanType::GetInstance()); @@ -57,6 +57,11 @@ void RegTypeCache::FillPrimitiveTypes() { entries_.push_back(FloatType::GetInstance()); entries_.push_back(DoubleLoType::GetInstance()); entries_.push_back(DoubleHiType::GetInstance()); + for (int32_t value = kMinSmallConstant; value <= kMaxSmallConstant; ++value) { + int32_t i = value - kMinSmallConstant; + DCHECK_EQ(entries_.size(), small_precise_constants_[i]->GetId()); + entries_.push_back(small_precise_constants_[i]); + } DCHECK_EQ(entries_.size(), primitive_count_); } @@ -232,12 +237,12 @@ const RegType& RegTypeCache::FromClass(const char* descriptor, mirror::Class* kl RegTypeCache::~RegTypeCache() { CHECK_LE(primitive_count_, entries_.size()); // Delete only the non primitive types. - if (entries_.size() == kNumPrimitives) { - // All entries are primitive, nothing to delete. + if (entries_.size() == kNumPrimitivesAndSmallConstants) { + // All entries are from the global pool, nothing to delete. return; } std::vector::iterator non_primitive_begin = entries_.begin(); - std::advance(non_primitive_begin, kNumPrimitives); + std::advance(non_primitive_begin, kNumPrimitivesAndSmallConstants); STLDeleteContainerPointers(non_primitive_begin, entries_.end()); } @@ -255,12 +260,29 @@ void RegTypeCache::ShutDown() { FloatType::Destroy(); DoubleLoType::Destroy(); DoubleHiType::Destroy(); + for (uint16_t value = kMinSmallConstant; value <= kMaxSmallConstant; ++value) { + PreciseConstType* type = small_precise_constants_[value - kMinSmallConstant]; + delete type; + } + RegTypeCache::primitive_initialized_ = false; RegTypeCache::primitive_count_ = 0; } } -void RegTypeCache::CreatePrimitiveTypes() { +template +Type* RegTypeCache::CreatePrimitiveTypeInstance(const std::string& descriptor) { + mirror::Class* klass = NULL; + // Try loading the class from linker. + if (!descriptor.empty()) { + klass = art::Runtime::Current()->GetClassLinker()->FindSystemClass(descriptor.c_str()); + } + Type* entry = Type::CreateInstance(klass, descriptor, RegTypeCache::primitive_count_); + RegTypeCache::primitive_count_++; + return entry; +} + +void RegTypeCache::CreatePrimitiveAndSmallConstantTypes() { CreatePrimitiveTypeInstance(""); CreatePrimitiveTypeInstance(""); CreatePrimitiveTypeInstance("Z"); @@ -273,6 +295,11 @@ void RegTypeCache::CreatePrimitiveTypes() { CreatePrimitiveTypeInstance("F"); CreatePrimitiveTypeInstance("D"); CreatePrimitiveTypeInstance("D"); + for (int32_t value = kMinSmallConstant; value <= kMaxSmallConstant; ++value) { + PreciseConstType* type = new PreciseConstType(value, primitive_count_); + small_precise_constants_[value - kMinSmallConstant] = type; + primitive_count_++; + } } const RegType& RegTypeCache::FromUnresolvedMerge(const RegType& left, const RegType& right) { @@ -331,29 +358,28 @@ const RegType& RegTypeCache::FromUnresolvedSuperClass(const RegType& child) { return *entry; } -const RegType& RegTypeCache::Uninitialized(const RegType& type, uint32_t allocation_pc) { - RegType* entry = NULL; - RegType* cur_entry = NULL; +const UninitializedType& RegTypeCache::Uninitialized(const RegType& type, uint32_t allocation_pc) { + UninitializedType* entry = NULL; const std::string& descriptor(type.GetDescriptor()); if (type.IsUnresolvedTypes()) { for (size_t i = primitive_count_; i < entries_.size(); i++) { - cur_entry = entries_[i]; + RegType* cur_entry = entries_[i]; if (cur_entry->IsUnresolvedAndUninitializedReference() && down_cast(cur_entry)->GetAllocationPc() == allocation_pc && (cur_entry->GetDescriptor() == descriptor)) { - return *cur_entry; + return *down_cast(cur_entry); } } entry = new UnresolvedUninitializedRefType(descriptor, allocation_pc, entries_.size()); } else { mirror::Class* klass = type.GetClass(); for (size_t i = primitive_count_; i < entries_.size(); i++) { - cur_entry = entries_[i]; + RegType* cur_entry = entries_[i]; if (cur_entry->IsUninitializedReference() && down_cast(cur_entry) ->GetAllocationPc() == allocation_pc && cur_entry->GetClass() == klass) { - return *cur_entry; + return *down_cast(cur_entry); } } entry = new UninitializedReferenceType(klass, descriptor, allocation_pc, entries_.size()); @@ -404,27 +430,33 @@ const RegType& RegTypeCache::FromUninitialized(const RegType& uninit_type) { return *entry; } -const RegType& RegTypeCache::ByteConstant() { - return FromCat1Const(std::numeric_limits::min(), false); +const ImpreciseConstType& RegTypeCache::ByteConstant() { + const ConstantType& result = FromCat1Const(std::numeric_limits::min(), false); + DCHECK(result.IsImpreciseConstant()); + return *down_cast(&result); } -const RegType& RegTypeCache::ShortConstant() { - return FromCat1Const(std::numeric_limits::min(), false); +const ImpreciseConstType& RegTypeCache::ShortConstant() { + const ConstantType& result = FromCat1Const(std::numeric_limits::min(), false); + DCHECK(result.IsImpreciseConstant()); + return *down_cast(&result); } -const RegType& RegTypeCache::IntConstant() { - return FromCat1Const(std::numeric_limits::max(), false); +const ImpreciseConstType& RegTypeCache::IntConstant() { + const ConstantType& result = FromCat1Const(std::numeric_limits::max(), false); + DCHECK(result.IsImpreciseConstant()); + return *down_cast(&result); } -const RegType& RegTypeCache::UninitializedThisArgument(const RegType& type) { - RegType* entry; +const UninitializedType& RegTypeCache::UninitializedThisArgument(const RegType& type) { + UninitializedType* entry; const std::string& descriptor(type.GetDescriptor()); if (type.IsUnresolvedTypes()) { for (size_t i = primitive_count_; i < entries_.size(); i++) { RegType* cur_entry = entries_[i]; if (cur_entry->IsUnresolvedAndUninitializedThisReference() && cur_entry->GetDescriptor() == descriptor) { - return *cur_entry; + return *down_cast(cur_entry); } } entry = new UnresolvedUninitializedThisRefType(descriptor, entries_.size()); @@ -433,7 +465,7 @@ const RegType& RegTypeCache::UninitializedThisArgument(const RegType& type) { for (size_t i = primitive_count_; i < entries_.size(); i++) { RegType* cur_entry = entries_[i]; if (cur_entry->IsUninitializedThisReference() && cur_entry->GetClass() == klass) { - return *cur_entry; + return *down_cast(cur_entry); } } entry = new UninitializedThisReferenceType(klass, descriptor, entries_.size()); @@ -442,16 +474,16 @@ const RegType& RegTypeCache::UninitializedThisArgument(const RegType& type) { return *entry; } -const RegType& RegTypeCache::FromCat1Const(int32_t value, bool precise) { +const ConstantType& RegTypeCache::FromCat1NonSmallConstant(int32_t value, bool precise) { for (size_t i = primitive_count_; i < entries_.size(); i++) { RegType* cur_entry = entries_[i]; if (cur_entry->klass_ == NULL && cur_entry->IsConstant() && cur_entry->IsPreciseConstant() == precise && (down_cast(cur_entry))->ConstantValue() == value) { - return *cur_entry; + return *down_cast(cur_entry); } } - RegType* entry; + ConstantType* entry; if (precise) { entry = new PreciseConstType(value, entries_.size()); } else { @@ -461,15 +493,15 @@ const RegType& RegTypeCache::FromCat1Const(int32_t value, bool precise) { return *entry; } -const RegType& RegTypeCache::FromCat2ConstLo(int32_t value, bool precise) { +const ConstantType& RegTypeCache::FromCat2ConstLo(int32_t value, bool precise) { for (size_t i = primitive_count_; i < entries_.size(); i++) { RegType* cur_entry = entries_[i]; if (cur_entry->IsConstantLo() && (cur_entry->IsPrecise() == precise) && (down_cast(cur_entry))->ConstantValueLo() == value) { - return *cur_entry; + return *down_cast(cur_entry); } } - RegType* entry; + ConstantType* entry; if (precise) { entry = new PreciseConstLoType(value, entries_.size()); } else { @@ -479,15 +511,15 @@ const RegType& RegTypeCache::FromCat2ConstLo(int32_t value, bool precise) { return *entry; } -const RegType& RegTypeCache::FromCat2ConstHi(int32_t value, bool precise) { +const ConstantType& RegTypeCache::FromCat2ConstHi(int32_t value, bool precise) { for (size_t i = primitive_count_; i < entries_.size(); i++) { RegType* cur_entry = entries_[i]; if (cur_entry->IsConstantHi() && (cur_entry->IsPrecise() == precise) && (down_cast(cur_entry))->ConstantValueHi() == value) { - return *cur_entry; + return *down_cast(cur_entry); } } - RegType* entry; + ConstantType* entry; if (precise) { entry = new PreciseConstHiType(value, entries_.size()); } else { diff --git a/runtime/verifier/reg_type_cache.h b/runtime/verifier/reg_type_cache.h index 77f58934da8..a9f8bff784c 100644 --- a/runtime/verifier/reg_type_cache.h +++ b/runtime/verifier/reg_type_cache.h @@ -35,19 +35,18 @@ namespace verifier { class RegType; -const size_t kNumPrimitives = 12; class RegTypeCache { public: explicit RegTypeCache(bool can_load_classes) : can_load_classes_(can_load_classes) { entries_.reserve(64); - FillPrimitiveTypes(); + FillPrimitiveAndSmallConstantTypes(); } ~RegTypeCache(); static void Init() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { if (!RegTypeCache::primitive_initialized_) { CHECK_EQ(RegTypeCache::primitive_count_, 0); - CreatePrimitiveTypes(); - CHECK_EQ(RegTypeCache::primitive_count_, kNumPrimitives); + CreatePrimitiveAndSmallConstantTypes(); + CHECK_EQ(RegTypeCache::primitive_count_, kNumPrimitivesAndSmallConstants); RegTypeCache::primitive_initialized_ = true; } } @@ -55,17 +54,13 @@ class RegTypeCache { const art::verifier::RegType& GetFromId(uint16_t id) const; const RegType& From(mirror::ClassLoader* loader, const char* descriptor, bool precise) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - template - static Type* CreatePrimitiveTypeInstance(const std::string& descriptor) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - void FillPrimitiveTypes() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); const RegType& FromClass(const char* descriptor, mirror::Class* klass, bool precise) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - const RegType& FromCat1Const(int32_t value, bool precise) + const ConstantType& FromCat1Const(int32_t value, bool precise) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - const RegType& FromCat2ConstLo(int32_t value, bool precise) + const ConstantType& FromCat2ConstLo(int32_t value, bool precise) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - const RegType& FromCat2ConstHi(int32_t value, bool precise) + const ConstantType& FromCat2ConstHi(int32_t value, bool precise) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); const RegType& FromDescriptor(mirror::ClassLoader* loader, const char* descriptor, bool precise) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); @@ -129,34 +124,56 @@ class RegTypeCache { const RegType& JavaLangObject(bool precise) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { return From(NULL, "Ljava/lang/Object;", precise); } - const RegType& Uninitialized(const RegType& type, uint32_t allocation_pc) + const UninitializedType& Uninitialized(const RegType& type, uint32_t allocation_pc) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); // Create an uninitialized 'this' argument for the given type. - const RegType& UninitializedThisArgument(const RegType& type) + const UninitializedType& UninitializedThisArgument(const RegType& type) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); const RegType& FromUninitialized(const RegType& uninit_type) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - const RegType& ByteConstant() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - const RegType& ShortConstant() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - const RegType& IntConstant() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + const ImpreciseConstType& ByteConstant() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + const ImpreciseConstType& ShortConstant() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + const ImpreciseConstType& IntConstant() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); const RegType& GetComponentType(const RegType& array, mirror::ClassLoader* loader) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); void Dump(std::ostream& os) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); const RegType& RegTypeFromPrimitiveType(Primitive::Type) const; private: - std::vector entries_; - static bool primitive_initialized_; - static uint16_t primitive_start_; - static uint16_t primitive_count_; - static void CreatePrimitiveTypes() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - // Whether or not we're allowed to load classes. - const bool can_load_classes_; + void FillPrimitiveAndSmallConstantTypes() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); mirror::Class* ResolveClass(const char* descriptor, mirror::ClassLoader* loader) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); void ClearException(); bool MatchDescriptor(size_t idx, const char* descriptor, bool precise) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + const ConstantType& FromCat1NonSmallConstant(int32_t value, bool precise) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + template + static Type* CreatePrimitiveTypeInstance(const std::string& descriptor) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + static void CreatePrimitiveAndSmallConstantTypes() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + // The actual storage for the RegTypes. + std::vector entries_; + + // A quick look up for popular small constants. + static constexpr int32_t kMinSmallConstant = -1; + static constexpr int32_t kMaxSmallConstant = 4; + static PreciseConstType* small_precise_constants_[kMaxSmallConstant - kMinSmallConstant + 1]; + + static constexpr size_t kNumPrimitivesAndSmallConstants = + 12 + (kMaxSmallConstant - kMinSmallConstant + 1); + + // Have the well known global primitives been created? + static bool primitive_initialized_; + + // Number of well known primitives that will be copied into a RegTypeCache upon construction. + static uint16_t primitive_count_; + + // Whether or not we're allowed to load classes. + const bool can_load_classes_; + DISALLOW_COPY_AND_ASSIGN(RegTypeCache); }; From 936bf024b26f84f9332d195a581912e95d9cbe4b Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Fri, 6 Sep 2013 08:44:06 -0700 Subject: [PATCH 0012/2402] Tweak dedupe hash function. Change-Id: I6c3450e53a3654969aa7627cea1474835bfc52d7 --- compiler/driver/compiler_driver.h | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/compiler/driver/compiler_driver.h b/compiler/driver/compiler_driver.h index c5c53e387d3..92c9d9fe992 100644 --- a/compiler/driver/compiler_driver.h +++ b/compiler/driver/compiler_driver.h @@ -457,14 +457,15 @@ class CompilerDriver { hash = (hash * 16777619) ^ b; } } else { - // For larger arrays use the first 4 bytes and then select a number of other values at - // random. + // For larger arrays use the 2 bytes at 6 bytes (the location of a push registers + // instruction field for quick generated code on ARM) and then select a number of other + // values at random. static const size_t kRandomHashCount = 16; - for (size_t i = 0; i < 4; ++i) { - uint8_t b = array[i]; + for (size_t i = 0; i < 2; ++i) { + uint8_t b = array[i + 6]; hash = (hash * 16777619) ^ b; } - for (size_t i = 4; i < kRandomHashCount; ++i) { + for (size_t i = 2; i < kRandomHashCount; ++i) { size_t r = i * 1103515245 + 12345; uint8_t b = array[r % array.size()]; hash = (hash * 16777619) ^ b; From 65ec92cf13c9d11c83711443a02e4249163d47f1 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Fri, 6 Sep 2013 10:49:58 -0700 Subject: [PATCH 0013/2402] Refactor CompilerDriver::ComputeInvokeInfo Don't use non-const reference arguments. Move ins before outs. Change-Id: I4a7b8099abe91ea60f93a56077f4989303fa4876 --- compiler/dex/dex_to_dex_compiler.cc | 9 ++- compiler/dex/mir_dataflow.cc | 8 +- compiler/dex/quick/gen_invoke.cc | 8 +- compiler/driver/compiler_driver.cc | 112 ++++++++++++++-------------- compiler/driver/compiler_driver.h | 10 +-- compiler/llvm/gbc_expander.cc | 8 +- 6 files changed, 78 insertions(+), 77 deletions(-) diff --git a/compiler/dex/dex_to_dex_compiler.cc b/compiler/dex/dex_to_dex_compiler.cc index 3fa0eab96bc..7eef62c6cfc 100644 --- a/compiler/dex/dex_to_dex_compiler.cc +++ b/compiler/dex/dex_to_dex_compiler.cc @@ -248,10 +248,11 @@ void DexCompiler::CompileInvokeVirtual(Instruction* inst, uintptr_t direct_method; // TODO: support devirtualization. const bool kEnableDevirtualization = false; - bool fast_path = driver_.ComputeInvokeInfo(&unit_, dex_pc, invoke_type, - target_method, vtable_idx, - direct_code, direct_method, - false, kEnableDevirtualization); + bool fast_path = driver_.ComputeInvokeInfo(&unit_, dex_pc, + false, kEnableDevirtualization, + &invoke_type, + &target_method, &vtable_idx, + &direct_code, &direct_method); if (fast_path && original_invoke_type == invoke_type) { if (vtable_idx >= 0 && IsUint(16, vtable_idx)) { VLOG(compiler) << "Quickening " << Instruction::Name(inst->Opcode()) diff --git a/compiler/dex/mir_dataflow.cc b/compiler/dex/mir_dataflow.cc index 42ca5dcfdf5..be622762b4e 100644 --- a/compiler/dex/mir_dataflow.cc +++ b/compiler/dex/mir_dataflow.cc @@ -1221,10 +1221,10 @@ bool MIRGraph::InvokeUsesMethodStar(MIR* mir) { uint32_t current_offset = static_cast(current_offset_); bool fast_path = cu_->compiler_driver->ComputeInvokeInfo(&m_unit, current_offset, - type, target_method, - vtable_idx, - direct_code, direct_method, - false, true) && + false, true, + &type, &target_method, + &vtable_idx, + &direct_code, &direct_method) && !(cu_->enable_debug & (1 << kDebugSlowInvokePath)); return (((type == kDirect) || (type == kStatic)) && fast_path && ((direct_code == 0) || (direct_method == 0))); diff --git a/compiler/dex/quick/gen_invoke.cc b/compiler/dex/quick/gen_invoke.cc index a4ab15db5ed..fa608183bea 100644 --- a/compiler/dex/quick/gen_invoke.cc +++ b/compiler/dex/quick/gen_invoke.cc @@ -1356,10 +1356,10 @@ void Mir2Lir::GenInvoke(CallInfo* info) { bool fast_path = cu_->compiler_driver->ComputeInvokeInfo(mir_graph_->GetCurrentDexCompilationUnit(), current_dalvik_offset_, - info->type, target_method, - vtable_idx, - direct_code, direct_method, - true, true) && !SLOW_INVOKE_PATH; + true, true, + &info->type, &target_method, + &vtable_idx, + &direct_code, &direct_method) && !SLOW_INVOKE_PATH; if (info->type == kInterface) { if (fast_path) { p_null_ck = &null_ck; diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc index dcf5fd90079..13e60acd279 100644 --- a/compiler/driver/compiler_driver.cc +++ b/compiler/driver/compiler_driver.cc @@ -916,9 +916,9 @@ static mirror::ArtField* ComputeFieldReferencedFromCompilingMethod(ScopedObjectA } static mirror::ArtMethod* ComputeMethodReferencedFromCompilingMethod(ScopedObjectAccess& soa, - const DexCompilationUnit* mUnit, - uint32_t method_idx, - InvokeType type) + const DexCompilationUnit* mUnit, + uint32_t method_idx, + InvokeType type) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { mirror::DexCache* dex_cache = mUnit->GetClassLinker()->FindDexCache(*mUnit->GetDexFile()); mirror::ClassLoader* class_loader = soa.Decode(mUnit->GetClassLoader()); @@ -1062,15 +1062,15 @@ bool CompilerDriver::ComputeStaticFieldInfo(uint32_t field_idx, const DexCompila void CompilerDriver::GetCodeAndMethodForDirectCall(InvokeType type, InvokeType sharp_type, mirror::Class* referrer_class, mirror::ArtMethod* method, - uintptr_t& direct_code, - uintptr_t& direct_method, - bool update_stats) { + bool update_stats, + uintptr_t* direct_code, + uintptr_t* direct_method) { // For direct and static methods compute possible direct_code and direct_method values, ie // an address for the Method* being invoked and an address of the code for that Method*. // For interface calls compute a value for direct_method that is the interface method being // invoked, so this can be passed to the out-of-line runtime support code. - direct_code = 0; - direct_method = 0; + *direct_code = 0; + *direct_method = 0; if (compiler_backend_ == kPortable) { if (sharp_type != kStatic && sharp_type != kDirect) { return; @@ -1102,38 +1102,37 @@ void CompilerDriver::GetCodeAndMethodForDirectCall(InvokeType type, InvokeType s if (IsImageClass(mh.GetDeclaringClassDescriptor())) { // We can only branch directly to Methods that are resolved in the DexCache. // Otherwise we won't invoke the resolution trampoline. - direct_method = -1; - direct_code = -1; + *direct_method = -1; + *direct_code = -1; } } } else { if (Runtime::Current()->GetHeap()->FindSpaceFromObject(method, false)->IsImageSpace()) { - direct_method = reinterpret_cast(method); + *direct_method = reinterpret_cast(method); } - direct_code = reinterpret_cast(method->GetEntryPointFromCompiledCode()); + *direct_code = reinterpret_cast(method->GetEntryPointFromCompiledCode()); } } bool CompilerDriver::ComputeInvokeInfo(const DexCompilationUnit* mUnit, const uint32_t dex_pc, - InvokeType& invoke_type, - MethodReference& target_method, - int& vtable_idx, - uintptr_t& direct_code, uintptr_t& direct_method, - bool update_stats, bool enable_devirtualization) { + bool update_stats, bool enable_devirtualization, + InvokeType* invoke_type, MethodReference* target_method, + int* vtable_idx, uintptr_t* direct_code, + uintptr_t* direct_method) { ScopedObjectAccess soa(Thread::Current()); - vtable_idx = -1; - direct_code = 0; - direct_method = 0; + *vtable_idx = -1; + *direct_code = 0; + *direct_method = 0; mirror::ArtMethod* resolved_method = - ComputeMethodReferencedFromCompilingMethod(soa, mUnit, target_method.dex_method_index, - invoke_type); + ComputeMethodReferencedFromCompilingMethod(soa, mUnit, target_method->dex_method_index, + *invoke_type); if (resolved_method != NULL) { // Don't try to fast-path if we don't understand the caller's class or this appears to be an // Incompatible Class Change Error. mirror::Class* referrer_class = ComputeCompilingMethodsClass(soa, resolved_method->GetDeclaringClass()->GetDexCache(), mUnit); - bool icce = resolved_method->CheckIncompatibleClassChange(invoke_type); + bool icce = resolved_method->CheckIncompatibleClassChange(*invoke_type); if (referrer_class != NULL && !icce) { mirror::Class* methods_class = resolved_method->GetDeclaringClass(); if (!referrer_class->CanAccess(methods_class) || @@ -1144,8 +1143,8 @@ bool CompilerDriver::ComputeInvokeInfo(const DexCompilationUnit* mUnit, const ui // method public. Resort to the dex file to determine the correct class for the access // check. uint16_t class_idx = - target_method.dex_file->GetMethodId(target_method.dex_method_index).class_idx_; - methods_class = mUnit->GetClassLinker()->ResolveType(*target_method.dex_file, + target_method->dex_file->GetMethodId(target_method->dex_method_index).class_idx_; + methods_class = mUnit->GetClassLinker()->ResolveType(*target_method->dex_file, class_idx, referrer_class); } if (referrer_class->CanAccess(methods_class) && @@ -1154,10 +1153,10 @@ bool CompilerDriver::ComputeInvokeInfo(const DexCompilationUnit* mUnit, const ui // Sharpen a virtual call into a direct call when the target is known not to have been // overridden (ie is final). bool can_sharpen_virtual_based_on_type = - (invoke_type == kVirtual) && (resolved_method->IsFinal() || methods_class->IsFinal()); + (*invoke_type == kVirtual) && (resolved_method->IsFinal() || methods_class->IsFinal()); // For invoke-super, ensure the vtable index will be correct to dispatch in the vtable of // the super class. - bool can_sharpen_super_based_on_type = (invoke_type == kSuper) && + bool can_sharpen_super_based_on_type = (*invoke_type == kSuper) && (referrer_class != methods_class) && referrer_class->IsSubClass(methods_class) && resolved_method->GetMethodIndex() < methods_class->GetVTable()->GetLength() && (methods_class->GetVTable()->Get(resolved_method->GetMethodIndex()) == resolved_method); @@ -1166,20 +1165,20 @@ bool CompilerDriver::ComputeInvokeInfo(const DexCompilationUnit* mUnit, const ui can_sharpen_super_based_on_type)) { // Sharpen a virtual call into a direct call. The method_idx is into referrer's // dex cache, check that this resolved method is where we expect it. - CHECK(referrer_class->GetDexCache()->GetResolvedMethod(target_method.dex_method_index) == + CHECK(referrer_class->GetDexCache()->GetResolvedMethod(target_method->dex_method_index) == resolved_method) << PrettyMethod(resolved_method); if (update_stats) { - stats_->ResolvedMethod(invoke_type); - stats_->VirtualMadeDirect(invoke_type); + stats_->ResolvedMethod(*invoke_type); + stats_->VirtualMadeDirect(*invoke_type); } - GetCodeAndMethodForDirectCall(invoke_type, kDirect, referrer_class, resolved_method, - direct_code, direct_method, update_stats); - invoke_type = kDirect; + GetCodeAndMethodForDirectCall(*invoke_type, kDirect, referrer_class, resolved_method, + update_stats, direct_code, direct_method); + *invoke_type = kDirect; return true; } const bool enableVerifierBasedSharpening = enable_devirtualization; - if (enableVerifierBasedSharpening && (invoke_type == kVirtual || - invoke_type == kInterface)) { + if (enableVerifierBasedSharpening && (*invoke_type == kVirtual || + *invoke_type == kInterface)) { // Did the verifier record a more precise invoke target based on its type information? const MethodReference caller_method(mUnit->GetDexFile(), mUnit->GetDexMethodIndex()); const MethodReference* devirt_map_target = @@ -1196,14 +1195,14 @@ bool CompilerDriver::ComputeInvokeInfo(const DexCompilationUnit* mUnit, const ui kVirtual); CHECK(called_method != NULL); CHECK(!called_method->IsAbstract()); - GetCodeAndMethodForDirectCall(invoke_type, kDirect, referrer_class, called_method, - direct_code, direct_method, update_stats); + GetCodeAndMethodForDirectCall(*invoke_type, kDirect, referrer_class, called_method, + update_stats, direct_code, direct_method); bool compiler_needs_dex_cache = (GetCompilerBackend() == kPortable) || (GetCompilerBackend() == kQuick && instruction_set_ != kThumb2) || - (direct_code == 0) || (direct_code == static_cast(-1)) || - (direct_method == 0) || (direct_method == static_cast(-1)); - if ((devirt_map_target->dex_file != target_method.dex_file) && + (*direct_code == 0) || (*direct_code == static_cast(-1)) || + (*direct_method == 0) || (*direct_method == static_cast(-1)); + if ((devirt_map_target->dex_file != target_method->dex_file) && compiler_needs_dex_cache) { // We need to use the dex cache to find either the method or code, and the dex file // containing the method isn't the one expected for the target method. Try to find @@ -1213,7 +1212,7 @@ bool CompilerDriver::ComputeInvokeInfo(const DexCompilationUnit* mUnit, const ui // TODO: quick only supports direct pointers with Thumb2. // TODO: the following should be factored into a common helper routine to find // one dex file's method within another. - const DexFile* dexfile = target_method.dex_file; + const DexFile* dexfile = target_method->dex_file; const DexFile* cm_dexfile = called_method->GetDeclaringClass()->GetDexCache()->GetDexFile(); const DexFile::MethodId& cm_method_id = @@ -1239,12 +1238,13 @@ bool CompilerDriver::ComputeInvokeInfo(const DexCompilationUnit* mUnit, const ui *name, *sig); if (method_id != NULL) { if (update_stats) { - stats_->ResolvedMethod(invoke_type); - stats_->VirtualMadeDirect(invoke_type); + stats_->ResolvedMethod(*invoke_type); + stats_->VirtualMadeDirect(*invoke_type); stats_->PreciseTypeDevirtualization(); } - target_method.dex_method_index = dexfile->GetIndexForMethodId(*method_id); - invoke_type = kDirect; + target_method->dex_method_index = + dexfile->GetIndexForMethodId(*method_id); + *invoke_type = kDirect; return true; } } @@ -1256,28 +1256,28 @@ bool CompilerDriver::ComputeInvokeInfo(const DexCompilationUnit* mUnit, const ui // method in the referring method's dex cache/file. } else { if (update_stats) { - stats_->ResolvedMethod(invoke_type); - stats_->VirtualMadeDirect(invoke_type); + stats_->ResolvedMethod(*invoke_type); + stats_->VirtualMadeDirect(*invoke_type); stats_->PreciseTypeDevirtualization(); } - target_method = *devirt_map_target; - invoke_type = kDirect; + *target_method = *devirt_map_target; + *invoke_type = kDirect; return true; } } } - if (invoke_type == kSuper) { + if (*invoke_type == kSuper) { // Unsharpened super calls are suspicious so go slow-path. } else { // Sharpening failed so generate a regular resolved method dispatch. if (update_stats) { - stats_->ResolvedMethod(invoke_type); + stats_->ResolvedMethod(*invoke_type); } - if (invoke_type == kVirtual || invoke_type == kSuper) { - vtable_idx = resolved_method->GetMethodIndex(); + if (*invoke_type == kVirtual || *invoke_type == kSuper) { + *vtable_idx = resolved_method->GetMethodIndex(); } - GetCodeAndMethodForDirectCall(invoke_type, invoke_type, referrer_class, resolved_method, - direct_code, direct_method, update_stats); + GetCodeAndMethodForDirectCall(*invoke_type, *invoke_type, referrer_class, resolved_method, + update_stats, direct_code, direct_method); return true; } } @@ -1288,7 +1288,7 @@ bool CompilerDriver::ComputeInvokeInfo(const DexCompilationUnit* mUnit, const ui soa.Self()->ClearException(); } if (update_stats) { - stats_->UnresolvedMethod(invoke_type); + stats_->UnresolvedMethod(*invoke_type); } return false; // Incomplete knowledge needs slow path. } diff --git a/compiler/driver/compiler_driver.h b/compiler/driver/compiler_driver.h index 87be9d7415c..43218cf184b 100644 --- a/compiler/driver/compiler_driver.h +++ b/compiler/driver/compiler_driver.h @@ -183,9 +183,9 @@ class CompilerDriver { // Can we fastpath a interface, super class or virtual method call? Computes method's vtable // index. bool ComputeInvokeInfo(const DexCompilationUnit* mUnit, const uint32_t dex_pc, - InvokeType& type, MethodReference& target_method, int& vtable_idx, - uintptr_t& direct_code, uintptr_t& direct_method, bool update_stats, - bool enable_devirtualization) + bool update_stats, bool enable_devirtualization, + InvokeType* type, MethodReference* target_method, int* vtable_idx, + uintptr_t* direct_code, uintptr_t* direct_method) LOCKS_EXCLUDED(Locks::mutator_lock_); bool IsSafeCast(const MethodReference& mr, uint32_t dex_pc); @@ -315,8 +315,8 @@ class CompilerDriver { void GetCodeAndMethodForDirectCall(InvokeType type, InvokeType sharp_type, mirror::Class* referrer_class, mirror::ArtMethod* method, - uintptr_t& direct_code, uintptr_t& direct_method, - bool update_stats) + bool update_stats, + uintptr_t* direct_code, uintptr_t* direct_method) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); void PreCompile(jobject class_loader, const std::vector& dex_files, diff --git a/compiler/llvm/gbc_expander.cc b/compiler/llvm/gbc_expander.cc index 19c80498bd2..2459fde114d 100644 --- a/compiler/llvm/gbc_expander.cc +++ b/compiler/llvm/gbc_expander.cc @@ -846,10 +846,10 @@ llvm::Value* GBCExpanderPass::EmitInvoke(llvm::CallInst& call_inst) { uintptr_t direct_code = 0; uintptr_t direct_method = 0; bool is_fast_path = driver_->ComputeInvokeInfo(dex_compilation_unit_, dex_pc, - invoke_type, target_method, - vtable_idx, - direct_code, direct_method, - true, true); + true, true, + &invoke_type, &target_method, + &vtable_idx, + &direct_code, &direct_method); // Load the method object llvm::Value* callee_method_object_addr = NULL; From 9b297bfc588c7d38efd12a6f38cd2710fc513ee3 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Fri, 6 Sep 2013 11:11:25 -0700 Subject: [PATCH 0014/2402] Refactor CompilerDriver::Compute..FieldInfo Don't use non-const reference arguments. Move ins before outs. Change-Id: I7b251156388d8f07513b3da62ebfd29e5fd9ff76 --- compiler/dex/dex_to_dex_compiler.cc | 4 +-- compiler/dex/quick/arm/call_arm.cc | 4 +-- compiler/dex/quick/codegen_util.cc | 4 +-- compiler/dex/quick/gen_common.cc | 13 +++++---- compiler/dex/quick/mir_to_lir.h | 2 +- compiler/driver/compiler_driver.cc | 41 ++++++++++++++--------------- compiler/driver/compiler_driver.h | 10 +++---- compiler/llvm/gbc_expander.cc | 12 ++++----- 8 files changed, 44 insertions(+), 46 deletions(-) diff --git a/compiler/dex/dex_to_dex_compiler.cc b/compiler/dex/dex_to_dex_compiler.cc index 7eef62c6cfc..4a724b109ad 100644 --- a/compiler/dex/dex_to_dex_compiler.cc +++ b/compiler/dex/dex_to_dex_compiler.cc @@ -216,8 +216,8 @@ void DexCompiler::CompileInstanceFieldAccess(Instruction* inst, uint32_t field_idx = inst->VRegC_22c(); int field_offset; bool is_volatile; - bool fast_path = driver_.ComputeInstanceFieldInfo(field_idx, &unit_, field_offset, - is_volatile, is_put); + bool fast_path = driver_.ComputeInstanceFieldInfo(field_idx, &unit_, is_put, + &field_offset, &is_volatile); if (fast_path && !is_volatile && IsUint(16, field_offset)) { VLOG(compiler) << "Quickening " << Instruction::Name(inst->Opcode()) << " to " << Instruction::Name(new_opcode) diff --git a/compiler/dex/quick/arm/call_arm.cc b/compiler/dex/quick/arm/call_arm.cc index 2dbe5f5c36d..e0e198a50db 100644 --- a/compiler/dex/quick/arm/call_arm.cc +++ b/compiler/dex/quick/arm/call_arm.cc @@ -130,7 +130,7 @@ MIR* ArmMir2Lir::SpecialIGet(BasicBlock** bb, MIR* mir, int field_offset; bool is_volatile; uint32_t field_idx = mir->dalvikInsn.vC; - bool fast_path = FastInstance(field_idx, field_offset, is_volatile, false); + bool fast_path = FastInstance(field_idx, false, &field_offset, &is_volatile); if (!fast_path || !(mir->optimization_flags & MIR_IGNORE_NULL_CHECK)) { return NULL; } @@ -155,7 +155,7 @@ MIR* ArmMir2Lir::SpecialIPut(BasicBlock** bb, MIR* mir, int field_offset; bool is_volatile; uint32_t field_idx = mir->dalvikInsn.vC; - bool fast_path = FastInstance(field_idx, field_offset, is_volatile, false); + bool fast_path = FastInstance(field_idx, false, &field_offset, &is_volatile); if (!fast_path || !(mir->optimization_flags & MIR_IGNORE_NULL_CHECK)) { return NULL; } diff --git a/compiler/dex/quick/codegen_util.cc b/compiler/dex/quick/codegen_util.cc index e081c16bb55..dcb0a99d08d 100644 --- a/compiler/dex/quick/codegen_util.cc +++ b/compiler/dex/quick/codegen_util.cc @@ -50,9 +50,9 @@ void Mir2Lir::MarkSafepointPC(LIR* inst) { DCHECK_EQ(safepoint_pc->def_mask, ENCODE_ALL); } -bool Mir2Lir::FastInstance(uint32_t field_idx, int& field_offset, bool& is_volatile, bool is_put) { +bool Mir2Lir::FastInstance(uint32_t field_idx, bool is_put, int* field_offset, bool* is_volatile) { return cu_->compiler_driver->ComputeInstanceFieldInfo( - field_idx, mir_graph_->GetCurrentDexCompilationUnit(), field_offset, is_volatile, is_put); + field_idx, mir_graph_->GetCurrentDexCompilationUnit(), is_put, field_offset, is_volatile); } /* Convert an instruction to a NOP */ diff --git a/compiler/dex/quick/gen_common.cc b/compiler/dex/quick/gen_common.cc index f018c61819b..aa45d98cf65 100644 --- a/compiler/dex/quick/gen_common.cc +++ b/compiler/dex/quick/gen_common.cc @@ -337,8 +337,8 @@ void Mir2Lir::GenSput(uint32_t field_idx, RegLocation rl_src, bool is_long_or_do bool is_volatile; bool is_referrers_class; bool fast_path = cu_->compiler_driver->ComputeStaticFieldInfo( - field_idx, mir_graph_->GetCurrentDexCompilationUnit(), field_offset, ssb_index, - is_referrers_class, is_volatile, true); + field_idx, mir_graph_->GetCurrentDexCompilationUnit(), true, + &field_offset, &ssb_index, &is_referrers_class, &is_volatile); if (fast_path && !SLOW_FIELD_PATH) { DCHECK_GE(field_offset, 0); int rBase; @@ -423,8 +423,8 @@ void Mir2Lir::GenSget(uint32_t field_idx, RegLocation rl_dest, bool is_volatile; bool is_referrers_class; bool fast_path = cu_->compiler_driver->ComputeStaticFieldInfo( - field_idx, mir_graph_->GetCurrentDexCompilationUnit(), field_offset, ssb_index, - is_referrers_class, is_volatile, false); + field_idx, mir_graph_->GetCurrentDexCompilationUnit(), false, + &field_offset, &ssb_index, &is_referrers_class, &is_volatile); if (fast_path && !SLOW_FIELD_PATH) { DCHECK_GE(field_offset, 0); int rBase; @@ -626,7 +626,7 @@ void Mir2Lir::GenIGet(uint32_t field_idx, int opt_flags, OpSize size, int field_offset; bool is_volatile; - bool fast_path = FastInstance(field_idx, field_offset, is_volatile, false); + bool fast_path = FastInstance(field_idx, false, &field_offset, &is_volatile); if (fast_path && !SLOW_FIELD_PATH) { RegLocation rl_result; @@ -687,8 +687,7 @@ void Mir2Lir::GenIPut(uint32_t field_idx, int opt_flags, OpSize size, int field_offset; bool is_volatile; - bool fast_path = FastInstance(field_idx, field_offset, is_volatile, - true); + bool fast_path = FastInstance(field_idx, true, &field_offset, &is_volatile); if (fast_path && !SLOW_FIELD_PATH) { RegisterClass reg_class = oat_reg_class_by_size(size); DCHECK_GE(field_offset, 0); diff --git a/compiler/dex/quick/mir_to_lir.h b/compiler/dex/quick/mir_to_lir.h index a37ebd173f8..85d90c86571 100644 --- a/compiler/dex/quick/mir_to_lir.h +++ b/compiler/dex/quick/mir_to_lir.h @@ -250,7 +250,7 @@ class Mir2Lir : public Backend { virtual void Materialize(); virtual CompiledMethod* GetCompiledMethod(); void MarkSafepointPC(LIR* inst); - bool FastInstance(uint32_t field_idx, int& field_offset, bool& is_volatile, bool is_put); + bool FastInstance(uint32_t field_idx, bool is_put, int* field_offset, bool* is_volatile); void SetupResourceMasks(LIR* lir); void AssembleLIR(); void SetMemRefType(LIR* lir, bool is_load, int mem_type); diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc index 13e60acd279..8d521de72fe 100644 --- a/compiler/driver/compiler_driver.cc +++ b/compiler/driver/compiler_driver.cc @@ -927,11 +927,11 @@ static mirror::ArtMethod* ComputeMethodReferencedFromCompilingMethod(ScopedObjec } bool CompilerDriver::ComputeInstanceFieldInfo(uint32_t field_idx, const DexCompilationUnit* mUnit, - int& field_offset, bool& is_volatile, bool is_put) { + bool is_put, int* field_offset, bool* is_volatile) { ScopedObjectAccess soa(Thread::Current()); // Conservative defaults. - field_offset = -1; - is_volatile = true; + *field_offset = -1; + *is_volatile = true; // Try to resolve field and ignore if an Incompatible Class Change Error (ie is static). mirror::ArtField* resolved_field = ComputeFieldReferencedFromCompilingMethod(soa, mUnit, field_idx); if (resolved_field != NULL && !resolved_field->IsStatic()) { @@ -958,8 +958,8 @@ bool CompilerDriver::ComputeInstanceFieldInfo(uint32_t field_idx, const DexCompi bool is_write_to_final_from_wrong_class = is_put && resolved_field->IsFinal() && fields_class != referrer_class; if (access_ok && !is_write_to_final_from_wrong_class) { - field_offset = resolved_field->GetOffset().Int32Value(); - is_volatile = resolved_field->IsVolatile(); + *field_offset = resolved_field->GetOffset().Int32Value(); + *is_volatile = resolved_field->IsVolatile(); stats_->ResolvedInstanceField(); return true; // Fast path. } @@ -974,15 +974,14 @@ bool CompilerDriver::ComputeInstanceFieldInfo(uint32_t field_idx, const DexCompi } bool CompilerDriver::ComputeStaticFieldInfo(uint32_t field_idx, const DexCompilationUnit* mUnit, - int& field_offset, int& ssb_index, - bool& is_referrers_class, bool& is_volatile, - bool is_put) { + bool is_put, int* field_offset, int* ssb_index, + bool* is_referrers_class, bool* is_volatile) { ScopedObjectAccess soa(Thread::Current()); // Conservative defaults. - field_offset = -1; - ssb_index = -1; - is_referrers_class = false; - is_volatile = true; + *field_offset = -1; + *ssb_index = -1; + *is_referrers_class = false; + *is_volatile = true; // Try to resolve field and ignore if an Incompatible Class Change Error (ie isn't static). mirror::ArtField* resolved_field = ComputeFieldReferencedFromCompilingMethod(soa, mUnit, field_idx); if (resolved_field != NULL && resolved_field->IsStatic()) { @@ -992,9 +991,9 @@ bool CompilerDriver::ComputeStaticFieldInfo(uint32_t field_idx, const DexCompila if (referrer_class != NULL) { mirror::Class* fields_class = resolved_field->GetDeclaringClass(); if (fields_class == referrer_class) { - is_referrers_class = true; // implies no worrying about class initialization - field_offset = resolved_field->GetOffset().Int32Value(); - is_volatile = resolved_field->IsVolatile(); + *is_referrers_class = true; // implies no worrying about class initialization + *field_offset = resolved_field->GetOffset().Int32Value(); + *is_volatile = resolved_field->IsVolatile(); stats_->ResolvedLocalStaticField(); return true; // fast path } else { @@ -1025,9 +1024,9 @@ bool CompilerDriver::ComputeStaticFieldInfo(uint32_t field_idx, const DexCompila if (fields_class->GetDexCache() == dex_cache) { // common case where the dex cache of both the referrer and the field are the same, // no need to search the dex file - ssb_index = fields_class->GetDexTypeIndex(); - field_offset = resolved_field->GetOffset().Int32Value(); - is_volatile = resolved_field->IsVolatile(); + *ssb_index = fields_class->GetDexTypeIndex(); + *field_offset = resolved_field->GetOffset().Int32Value(); + *is_volatile = resolved_field->IsVolatile(); stats_->ResolvedStaticField(); return true; } @@ -1040,9 +1039,9 @@ bool CompilerDriver::ComputeStaticFieldInfo(uint32_t field_idx, const DexCompila mUnit->GetDexFile()->FindTypeId(mUnit->GetDexFile()->GetIndexForStringId(*string_id)); if (type_id != NULL) { // medium path, needs check of static storage base being initialized - ssb_index = mUnit->GetDexFile()->GetIndexForTypeId(*type_id); - field_offset = resolved_field->GetOffset().Int32Value(); - is_volatile = resolved_field->IsVolatile(); + *ssb_index = mUnit->GetDexFile()->GetIndexForTypeId(*type_id); + *field_offset = resolved_field->GetOffset().Int32Value(); + *is_volatile = resolved_field->IsVolatile(); stats_->ResolvedStaticField(); return true; } diff --git a/compiler/driver/compiler_driver.h b/compiler/driver/compiler_driver.h index 43218cf184b..b4ec0c134bf 100644 --- a/compiler/driver/compiler_driver.h +++ b/compiler/driver/compiler_driver.h @@ -169,15 +169,15 @@ class CompilerDriver { LOCKS_EXCLUDED(Locks::mutator_lock_); // Can we fast path instance field access? Computes field's offset and volatility. - bool ComputeInstanceFieldInfo(uint32_t field_idx, const DexCompilationUnit* mUnit, - int& field_offset, bool& is_volatile, bool is_put) + bool ComputeInstanceFieldInfo(uint32_t field_idx, const DexCompilationUnit* mUnit, bool is_put, + int* field_offset, bool* is_volatile) LOCKS_EXCLUDED(Locks::mutator_lock_); // Can we fastpath static field access? Computes field's offset, volatility and whether the // field is within the referrer (which can avoid checking class initialization). - bool ComputeStaticFieldInfo(uint32_t field_idx, const DexCompilationUnit* mUnit, - int& field_offset, int& ssb_index, - bool& is_referrers_class, bool& is_volatile, bool is_put) + bool ComputeStaticFieldInfo(uint32_t field_idx, const DexCompilationUnit* mUnit, bool is_put, + int* field_offset, int* ssb_index, + bool* is_referrers_class, bool* is_volatile) LOCKS_EXCLUDED(Locks::mutator_lock_); // Can we fastpath a interface, super class or virtual method call? Computes method's vtable diff --git a/compiler/llvm/gbc_expander.cc b/compiler/llvm/gbc_expander.cc index 2459fde114d..b206a25f258 100644 --- a/compiler/llvm/gbc_expander.cc +++ b/compiler/llvm/gbc_expander.cc @@ -1630,7 +1630,7 @@ llvm::Value* GBCExpanderPass::Expand_HLIGet(llvm::CallInst& call_inst, int field_offset; bool is_volatile; bool is_fast_path = driver_->ComputeInstanceFieldInfo( - field_idx, dex_compilation_unit_, field_offset, is_volatile, false); + field_idx, dex_compilation_unit_, false, &field_offset, &is_volatile); if (!is_fast_path) { llvm::Function* runtime_func; @@ -1692,7 +1692,7 @@ void GBCExpanderPass::Expand_HLIPut(llvm::CallInst& call_inst, int field_offset; bool is_volatile; bool is_fast_path = driver_->ComputeInstanceFieldInfo( - field_idx, dex_compilation_unit_, field_offset, is_volatile, true); + field_idx, dex_compilation_unit_, true, &field_offset, &is_volatile); if (!is_fast_path) { llvm::Function* runtime_func; @@ -1897,8 +1897,8 @@ llvm::Value* GBCExpanderPass::Expand_HLSget(llvm::CallInst& call_inst, bool is_volatile; bool is_fast_path = driver_->ComputeStaticFieldInfo( - field_idx, dex_compilation_unit_, field_offset, ssb_index, - is_referrers_class, is_volatile, false); + field_idx, dex_compilation_unit_, false, + &field_offset, &ssb_index, &is_referrers_class, &is_volatile); llvm::Value* static_field_value; @@ -1981,8 +1981,8 @@ void GBCExpanderPass::Expand_HLSput(llvm::CallInst& call_inst, bool is_volatile; bool is_fast_path = driver_->ComputeStaticFieldInfo( - field_idx, dex_compilation_unit_, field_offset, ssb_index, - is_referrers_class, is_volatile, true); + field_idx, dex_compilation_unit_, true, + &field_offset, &ssb_index, &is_referrers_class, &is_volatile); if (!is_fast_path) { llvm::Function* runtime_func; From 816432e297f9d440902fd827ca2e78e8c28f9f6b Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Fri, 6 Sep 2013 15:47:45 -0700 Subject: [PATCH 0015/2402] Deopt for method entry/exit when not sampling. Bug 9968239. Note, this makes profiling performance horrendously slow. The real fix is to get the sampling enabled flag to be set to true by frameworks. Change-Id: I101e524fe5bbff99142015e69177218796ae20da --- runtime/instrumentation.cc | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/runtime/instrumentation.cc b/runtime/instrumentation.cc index 6caad0110d3..fe38d32d1ce 100644 --- a/runtime/instrumentation.cc +++ b/runtime/instrumentation.cc @@ -41,6 +41,11 @@ namespace art { namespace instrumentation { +// Do we want to deoptimize for method entry and exit listeners or just try to intercept +// invocations? Deoptimization forces all code to run in the interpreter and considerably hurts the +// application's performance. +static constexpr bool kDeoptimizeForAccurateMethodEntryExitListeners = true; + static bool InstallStubsClassVisitor(mirror::Class* klass, void* arg) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { Instrumentation* instrumentation = reinterpret_cast(arg); @@ -264,12 +269,14 @@ void Instrumentation::AddListener(InstrumentationListener* listener, uint32_t ev bool require_interpreter = false; if ((events & kMethodEntered) != 0) { method_entry_listeners_.push_back(listener); - require_entry_exit_stubs = true; + require_interpreter = kDeoptimizeForAccurateMethodEntryExitListeners; + require_entry_exit_stubs = !kDeoptimizeForAccurateMethodEntryExitListeners; have_method_entry_listeners_ = true; } if ((events & kMethodExited) != 0) { method_exit_listeners_.push_back(listener); - require_entry_exit_stubs = true; + require_interpreter = kDeoptimizeForAccurateMethodEntryExitListeners; + require_entry_exit_stubs = !kDeoptimizeForAccurateMethodEntryExitListeners; have_method_exit_listeners_ = true; } if ((events & kMethodUnwind) != 0) { @@ -300,7 +307,10 @@ void Instrumentation::RemoveListener(InstrumentationListener* listener, uint32_t method_entry_listeners_.remove(listener); } have_method_entry_listeners_ = method_entry_listeners_.size() > 0; - require_entry_exit_stubs |= have_method_entry_listeners_; + require_entry_exit_stubs |= have_method_entry_listeners_ && + !kDeoptimizeForAccurateMethodEntryExitListeners; + require_interpreter = have_method_entry_listeners_ && + kDeoptimizeForAccurateMethodEntryExitListeners; } if ((events & kMethodExited) != 0) { bool contains = std::find(method_exit_listeners_.begin(), method_exit_listeners_.end(), @@ -309,7 +319,10 @@ void Instrumentation::RemoveListener(InstrumentationListener* listener, uint32_t method_exit_listeners_.remove(listener); } have_method_exit_listeners_ = method_exit_listeners_.size() > 0; - require_entry_exit_stubs |= have_method_exit_listeners_; + require_entry_exit_stubs |= have_method_exit_listeners_ && + !kDeoptimizeForAccurateMethodEntryExitListeners; + require_interpreter = have_method_exit_listeners_ && + kDeoptimizeForAccurateMethodEntryExitListeners; } if ((events & kMethodUnwind) != 0) { method_unwind_listeners_.remove(listener); From 56c717860df2d71d66fb77aa77f29dd346e559d3 Mon Sep 17 00:00:00 2001 From: buzbee Date: Thu, 5 Sep 2013 17:13:19 -0700 Subject: [PATCH 0016/2402] Compile-time tuning Specialized the dataflow iterators and did a few other minor tweaks. Showing ~5% compile-time improvement in a single-threaded environment; less in multi-threaded (presumably because we're blocked by something else). Change-Id: I2e2ed58d881414b9fc97e04cd0623e188259afd2 --- compiler/dex/dataflow_iterator-inl.h | 71 ++++++++----- compiler/dex/dataflow_iterator.h | 118 ++++++++++++---------- compiler/dex/growable_array.h | 5 + compiler/dex/mir_analysis.cc | 2 +- compiler/dex/mir_dataflow.cc | 4 +- compiler/dex/mir_optimization.cc | 14 +-- compiler/dex/portable/mir_to_gbc.cc | 4 +- compiler/dex/quick/codegen_util.cc | 12 ++- compiler/dex/quick/local_optimizations.cc | 5 +- compiler/dex/quick/mir_to_lir-inl.h | 7 +- compiler/dex/quick/mir_to_lir.cc | 7 +- compiler/dex/quick/mir_to_lir.h | 2 +- compiler/dex/quick/ralloc_util.cc | 91 +++++------------ compiler/dex/ssa_transformation.cc | 20 ++-- compiler/dex/vreg_analysis.cc | 2 +- 15 files changed, 183 insertions(+), 181 deletions(-) diff --git a/compiler/dex/dataflow_iterator-inl.h b/compiler/dex/dataflow_iterator-inl.h index 06cc505a9ae..236c6f49401 100644 --- a/compiler/dex/dataflow_iterator-inl.h +++ b/compiler/dex/dataflow_iterator-inl.h @@ -21,42 +21,63 @@ namespace art { -inline BasicBlock* DataflowIterator::NextBody(bool had_change) { +// Single forward pass over the nodes. +inline BasicBlock* DataflowIterator::ForwardSingleNext() { + BasicBlock* res = NULL; + if (idx_ < end_idx_) { + int bb_id = block_id_list_->Get(idx_++); + res = mir_graph_->GetBasicBlock(bb_id); + } + return res; +} + +// Repeat full forward passes over all nodes until no change occurs during a complete pass. +inline BasicBlock* DataflowIterator::ForwardRepeatNext(bool had_change) { changed_ |= had_change; BasicBlock* res = NULL; - if (reverse_) { - if (is_iterative_ && changed_ && (idx_ < 0)) { - idx_ = start_idx_; - changed_ = false; - } - if (idx_ >= 0) { - int bb_id = block_id_list_->Get(idx_--); - res = mir_graph_->GetBasicBlock(bb_id); - } - } else { - if (is_iterative_ && changed_ && (idx_ >= end_idx_)) { - idx_ = start_idx_; - changed_ = false; - } - if (idx_ < end_idx_) { - int bb_id = block_id_list_->Get(idx_++); - res = mir_graph_->GetBasicBlock(bb_id); - } + if ((idx_ >= end_idx_) && changed_) { + idx_ = start_idx_; + changed_ = false; + } + if (idx_ < end_idx_) { + int bb_id = block_id_list_->Get(idx_++); + res = mir_graph_->GetBasicBlock(bb_id); } return res; } -// AllNodes uses the existing GrowableArray iterator, so use different NextBody(). -inline BasicBlock* AllNodesIterator::NextBody(bool had_change) { +// Single reverse pass over the nodes. +inline BasicBlock* DataflowIterator::ReverseSingleNext() { + BasicBlock* res = NULL; + if (idx_ >= 0) { + int bb_id = block_id_list_->Get(idx_--); + res = mir_graph_->GetBasicBlock(bb_id); + } + return res; +} + +// Repeat full backwards passes over all nodes until no change occurs during a complete pass. +inline BasicBlock* DataflowIterator::ReverseRepeatNext(bool had_change) { changed_ |= had_change; + BasicBlock* res = NULL; + if ((idx_ < 0) && changed_) { + idx_ = start_idx_; + changed_ = false; + } + if (idx_ >= 0) { + int bb_id = block_id_list_->Get(idx_--); + res = mir_graph_->GetBasicBlock(bb_id); + } + return res; +} + +// AllNodes uses the existing GrowableArray iterator, and should be considered unordered. +inline BasicBlock* AllNodesIterator::Next() { BasicBlock* res = NULL; bool keep_looking = true; while (keep_looking) { res = all_nodes_iterator_->Next(); - if (is_iterative_ && changed_ && (res == NULL)) { - all_nodes_iterator_->Reset(); - changed_ = false; - } else if ((res == NULL) || (!res->hidden)) { + if ((res == NULL) || (!res->hidden)) { keep_looking = false; } } diff --git a/compiler/dex/dataflow_iterator.h b/compiler/dex/dataflow_iterator.h index da44ffd99c5..1dab54ea728 100644 --- a/compiler/dex/dataflow_iterator.h +++ b/compiler/dex/dataflow_iterator.h @@ -27,124 +27,130 @@ namespace art { * interesting orders. Note that for efficiency, the visit orders have been pre-computed. * The order itself will not change during the iteration. However, for some uses, * auxiliary data associated with the basic blocks may be changed during the iteration, - * necessitating another pass over the list. - * - * To support this usage, we have is_iterative_. If false, the iteration is a one-shot - * pass through the pre-computed list using Next(). If true, the caller must tell the - * iterator whether a change has been made that necessitates another pass. Use - * Next(had_change) for this. The general idea is that the iterative_ use case means - * that the iterator will keep repeating the full basic block list until a complete pass - * is made through it with no changes. Note that calling Next(true) does not affect - * the iteration order or short-curcuit the current pass - it simply tells the iterator - * that once it has finished walking through the block list it should reset and do another - * full pass through the list. + * necessitating another pass over the list. If this behavior is required, use the + * "Repeating" variant. For the repeating variant, the caller must tell the iterator + * whether a change has been made that necessitates another pass. Note that calling Next(true) + * does not affect the iteration order or short-circuit the current pass - it simply tells + * the iterator that once it has finished walking through the block list it should reset and + * do another full pass through the list. */ class DataflowIterator { public: virtual ~DataflowIterator() {} - // Return the next BasicBlock* to visit. - BasicBlock* Next() { - DCHECK(!is_iterative_); - return NextBody(false); - } - - /* - * Return the next BasicBlock* to visit, and tell the iterator whether any change - * has occurred that requires another full pass over the block list. - */ - BasicBlock* Next(bool had_change) { - DCHECK(is_iterative_); - return NextBody(had_change); - } - protected: - DataflowIterator(MIRGraph* mir_graph, bool is_iterative, int start_idx, int end_idx, - bool reverse) + DataflowIterator(MIRGraph* mir_graph, int start_idx, int end_idx) : mir_graph_(mir_graph), - is_iterative_(is_iterative), start_idx_(start_idx), end_idx_(end_idx), - reverse_(reverse), block_id_list_(NULL), idx_(0), changed_(false) {} - virtual BasicBlock* NextBody(bool had_change) ALWAYS_INLINE; + virtual BasicBlock* ForwardSingleNext() ALWAYS_INLINE; + virtual BasicBlock* ReverseSingleNext() ALWAYS_INLINE; + virtual BasicBlock* ForwardRepeatNext(bool had_change) ALWAYS_INLINE; + virtual BasicBlock* ReverseRepeatNext(bool had_change) ALWAYS_INLINE; MIRGraph* const mir_graph_; - const bool is_iterative_; const int start_idx_; const int end_idx_; - const bool reverse_; GrowableArray* block_id_list_; int idx_; bool changed_; }; // DataflowIterator - class ReachableNodesIterator : public DataflowIterator { + class PreOrderDfsIterator : public DataflowIterator { public: - ReachableNodesIterator(MIRGraph* mir_graph, bool is_iterative) - : DataflowIterator(mir_graph, is_iterative, 0, - mir_graph->GetNumReachableBlocks(), false) { + explicit PreOrderDfsIterator(MIRGraph* mir_graph) + : DataflowIterator(mir_graph, 0, mir_graph->GetNumReachableBlocks()) { idx_ = start_idx_; block_id_list_ = mir_graph->GetDfsOrder(); } + + BasicBlock* Next() { + return ForwardSingleNext(); + } }; - class PreOrderDfsIterator : public DataflowIterator { + class RepeatingPreOrderDfsIterator : public DataflowIterator { public: - PreOrderDfsIterator(MIRGraph* mir_graph, bool is_iterative) - : DataflowIterator(mir_graph, is_iterative, 0, - mir_graph->GetNumReachableBlocks(), false) { + explicit RepeatingPreOrderDfsIterator(MIRGraph* mir_graph) + : DataflowIterator(mir_graph, 0, mir_graph->GetNumReachableBlocks()) { idx_ = start_idx_; block_id_list_ = mir_graph->GetDfsOrder(); } + + BasicBlock* Next(bool had_change) { + return ForwardRepeatNext(had_change); + } }; - class PostOrderDfsIterator : public DataflowIterator { + class RepeatingPostOrderDfsIterator : public DataflowIterator { public: - PostOrderDfsIterator(MIRGraph* mir_graph, bool is_iterative) - : DataflowIterator(mir_graph, is_iterative, 0, - mir_graph->GetNumReachableBlocks(), false) { + explicit RepeatingPostOrderDfsIterator(MIRGraph* mir_graph) + : DataflowIterator(mir_graph, 0, mir_graph->GetNumReachableBlocks()) { idx_ = start_idx_; block_id_list_ = mir_graph->GetDfsPostOrder(); } + + BasicBlock* Next(bool had_change) { + return ForwardRepeatNext(had_change); + } }; class ReversePostOrderDfsIterator : public DataflowIterator { public: - ReversePostOrderDfsIterator(MIRGraph* mir_graph, bool is_iterative) - : DataflowIterator(mir_graph, is_iterative, - mir_graph->GetNumReachableBlocks() -1, 0, true) { + explicit ReversePostOrderDfsIterator(MIRGraph* mir_graph) + : DataflowIterator(mir_graph, mir_graph->GetNumReachableBlocks() -1, 0) { + idx_ = start_idx_; + block_id_list_ = mir_graph->GetDfsPostOrder(); + } + + BasicBlock* Next() { + return ReverseSingleNext(); + } + }; + + class RepeatingReversePostOrderDfsIterator : public DataflowIterator { + public: + explicit RepeatingReversePostOrderDfsIterator(MIRGraph* mir_graph) + : DataflowIterator(mir_graph, mir_graph->GetNumReachableBlocks() -1, 0) { idx_ = start_idx_; block_id_list_ = mir_graph->GetDfsPostOrder(); } + + BasicBlock* Next(bool had_change) { + return ReverseRepeatNext(had_change); + } }; class PostOrderDOMIterator : public DataflowIterator { public: - PostOrderDOMIterator(MIRGraph* mir_graph, bool is_iterative) - : DataflowIterator(mir_graph, is_iterative, 0, - mir_graph->GetNumReachableBlocks(), false) { + explicit PostOrderDOMIterator(MIRGraph* mir_graph) + : DataflowIterator(mir_graph, 0, mir_graph->GetNumReachableBlocks()) { idx_ = start_idx_; block_id_list_ = mir_graph->GetDomPostOrder(); } + + BasicBlock* Next() { + return ForwardSingleNext(); + } }; class AllNodesIterator : public DataflowIterator { public: - AllNodesIterator(MIRGraph* mir_graph, bool is_iterative) - : DataflowIterator(mir_graph, is_iterative, 0, 0, false) { - all_nodes_iterator_ = - new (mir_graph->GetArena()) GrowableArray::Iterator(mir_graph->GetBlockList()); + explicit AllNodesIterator(MIRGraph* mir_graph) + : DataflowIterator(mir_graph, 0, 0) { + all_nodes_iterator_ = new + (mir_graph->GetArena()) GrowableArray::Iterator(mir_graph->GetBlockList()); } void Reset() { all_nodes_iterator_->Reset(); } - BasicBlock* NextBody(bool had_change) ALWAYS_INLINE; + BasicBlock* Next() ALWAYS_INLINE; private: GrowableArray::Iterator* all_nodes_iterator_; diff --git a/compiler/dex/growable_array.h b/compiler/dex/growable_array.h index 8e2abfbaf18..9053285af09 100644 --- a/compiler/dex/growable_array.h +++ b/compiler/dex/growable_array.h @@ -150,6 +150,11 @@ class GrowableArray { size_t Size() const { return num_used_; } + void SetSize(size_t new_size) { + Resize(new_size); + num_used_ = new_size; + } + T* GetRawStorage() const { return elem_list_; } static void* operator new(size_t size, ArenaAllocator* arena) { diff --git a/compiler/dex/mir_analysis.cc b/compiler/dex/mir_analysis.cc index d7a4136a01a..8472a3c011b 100644 --- a/compiler/dex/mir_analysis.cc +++ b/compiler/dex/mir_analysis.cc @@ -1061,7 +1061,7 @@ bool MIRGraph::SkipCompilation(Runtime::CompilerFilter compiler_filter) { memset(&stats, 0, sizeof(stats)); ClearAllVisitedFlags(); - AllNodesIterator iter(this, false /* not iterative */); + AllNodesIterator iter(this); for (BasicBlock* bb = iter.Next(); bb != NULL; bb = iter.Next()) { AnalyzeBlock(bb, &stats); } diff --git a/compiler/dex/mir_dataflow.cc b/compiler/dex/mir_dataflow.cc index 3a73717a7b0..addfd6b6b7a 100644 --- a/compiler/dex/mir_dataflow.cc +++ b/compiler/dex/mir_dataflow.cc @@ -1287,7 +1287,7 @@ void MIRGraph::MethodUseCount() { if (cu_->disable_opt & (1 << kPromoteRegs)) { return; } - AllNodesIterator iter(this, false /* not iterative */); + AllNodesIterator iter(this); for (BasicBlock* bb = iter.Next(); bb != NULL; bb = iter.Next()) { CountUses(bb); } @@ -1331,7 +1331,7 @@ bool MIRGraph::VerifyPredInfo(BasicBlock* bb) { void MIRGraph::VerifyDataflow() { /* Verify if all blocks are connected as claimed */ - AllNodesIterator iter(this, false /* not iterative */); + AllNodesIterator iter(this); for (BasicBlock* bb = iter.Next(); bb != NULL; bb = iter.Next()) { VerifyPredInfo(bb); } diff --git a/compiler/dex/mir_optimization.cc b/compiler/dex/mir_optimization.cc index b7611f8f5bf..05e428e1788 100644 --- a/compiler/dex/mir_optimization.cc +++ b/compiler/dex/mir_optimization.cc @@ -96,7 +96,7 @@ void MIRGraph::PropagateConstants() { is_constant_v_ = new (arena_) ArenaBitVector(arena_, GetNumSSARegs(), false); constant_values_ = static_cast(arena_->Alloc(sizeof(int) * GetNumSSARegs(), ArenaAllocator::kAllocDFInfo)); - AllNodesIterator iter(this, false /* not iterative */); + AllNodesIterator iter(this); for (BasicBlock* bb = iter.Next(); bb != NULL; bb = iter.Next()) { DoConstantPropogation(bb); } @@ -762,11 +762,11 @@ bool MIRGraph::EliminateNullChecks(struct BasicBlock* bb) { void MIRGraph::NullCheckElimination() { if (!(cu_->disable_opt & (1 << kNullCheckElimination))) { DCHECK(temp_ssa_register_v_ != NULL); - AllNodesIterator iter(this, false /* not iterative */); + AllNodesIterator iter(this); for (BasicBlock* bb = iter.Next(); bb != NULL; bb = iter.Next()) { NullCheckEliminationInit(bb); } - PreOrderDfsIterator iter2(this, true /* iterative */); + RepeatingPreOrderDfsIterator iter2(this); bool change = false; for (BasicBlock* bb = iter2.Next(change); bb != NULL; bb = iter2.Next(change)) { change = EliminateNullChecks(bb); @@ -778,7 +778,7 @@ void MIRGraph::NullCheckElimination() { } void MIRGraph::BasicBlockCombine() { - PreOrderDfsIterator iter(this, false /* not iterative */); + PreOrderDfsIterator iter(this); for (BasicBlock* bb = iter.Next(); bb != NULL; bb = iter.Next()) { CombineBlocks(bb); } @@ -791,7 +791,7 @@ void MIRGraph::CodeLayout() { if (cu_->enable_debug & (1 << kDebugVerifyDataflow)) { VerifyDataflow(); } - AllNodesIterator iter(this, false /* not iterative */); + AllNodesIterator iter(this); for (BasicBlock* bb = iter.Next(); bb != NULL; bb = iter.Next()) { LayoutBlocks(bb); } @@ -804,7 +804,7 @@ void MIRGraph::DumpCheckStats() { Checkstats* stats = static_cast(arena_->Alloc(sizeof(Checkstats), ArenaAllocator::kAllocDFInfo)); checkstats_ = stats; - AllNodesIterator iter(this, false /* not iterative */); + AllNodesIterator iter(this); for (BasicBlock* bb = iter.Next(); bb != NULL; bb = iter.Next()) { CountChecks(bb); } @@ -858,7 +858,7 @@ void MIRGraph::BasicBlockOptimization() { if (!(cu_->disable_opt & (1 << kBBOpt))) { DCHECK_EQ(cu_->num_compiler_temps, 0); ClearAllVisitedFlags(); - PreOrderDfsIterator iter2(this, false /* not iterative */); + PreOrderDfsIterator iter2(this); for (BasicBlock* bb = iter2.Next(); bb != NULL; bb = iter2.Next()) { BuildExtendedBBList(bb); } diff --git a/compiler/dex/portable/mir_to_gbc.cc b/compiler/dex/portable/mir_to_gbc.cc index 7831cf6f7a1..2cf55e7ea5c 100644 --- a/compiler/dex/portable/mir_to_gbc.cc +++ b/compiler/dex/portable/mir_to_gbc.cc @@ -1877,7 +1877,7 @@ void MirConverter::MethodMIR2Bitcode() { CreateFunction(); // Create an LLVM basic block for each MIR block in dfs preorder - PreOrderDfsIterator iter(mir_graph_, false /* not iterative */); + PreOrderDfsIterator iter(mir_graph_); for (BasicBlock* bb = iter.Next(); bb != NULL; bb = iter.Next()) { CreateLLVMBasicBlock(bb); } @@ -1909,7 +1909,7 @@ void MirConverter::MethodMIR2Bitcode() { } } - PreOrderDfsIterator iter2(mir_graph_, false /* not iterative */); + PreOrderDfsIterator iter2(mir_graph_); for (BasicBlock* bb = iter2.Next(); bb != NULL; bb = iter2.Next()) { BlockBitcodeConversion(bb); } diff --git a/compiler/dex/quick/codegen_util.cc b/compiler/dex/quick/codegen_util.cc index e081c16bb55..8c5949b4df8 100644 --- a/compiler/dex/quick/codegen_util.cc +++ b/compiler/dex/quick/codegen_util.cc @@ -710,7 +710,6 @@ int Mir2Lir::AssignInsnOffsets() { } /* Pseudo opcodes don't consume space */ } - return offset; } @@ -789,15 +788,15 @@ void Mir2Lir::AssembleLIR() { */ LIR* Mir2Lir::InsertCaseLabel(int vaddr, int keyVal) { SafeMap::iterator it; - it = boundary_map_.find(vaddr); - if (it == boundary_map_.end()) { + LIR* boundary_lir = boundary_map_.Get(vaddr); + if (boundary_lir == NULL) { LOG(FATAL) << "Error: didn't find vaddr 0x" << std::hex << vaddr; } LIR* new_label = static_cast(arena_->Alloc(sizeof(LIR), ArenaAllocator::kAllocLIR)); new_label->dalvik_offset = vaddr; new_label->opcode = kPseudoCaseLabel; new_label->operands[0] = keyVal; - InsertLIRAfter(it->second, new_label); + InsertLIRAfter(boundary_lir, new_label); return new_label; } @@ -889,7 +888,7 @@ void Mir2Lir::DumpPackedSwitchTable(const uint16_t* table) { */ LIR* Mir2Lir::MarkBoundary(int offset, const char* inst_str) { LIR* res = NewLIR1(kPseudoDalvikByteCodeBoundary, reinterpret_cast(inst_str)); - if (boundary_map_.find(offset) == boundary_map_.end()) { + if (boundary_map_.Get(offset) == NULL) { boundary_map_.Put(offset, res); } return res; @@ -947,6 +946,7 @@ Mir2Lir::Mir2Lir(CompilationUnit* cu, MIRGraph* mir_graph, ArenaAllocator* arena throw_launchpads_(arena, 2048, kGrowableArrayThrowLaunchPads), suspend_launchpads_(arena, 4, kGrowableArraySuspendLaunchPads), intrinsic_launchpads_(arena, 2048, kGrowableArrayMisc), + boundary_map_(arena, 0, kGrowableArrayMisc), data_offset_(0), total_size_(0), block_label_list_(NULL), @@ -963,6 +963,8 @@ Mir2Lir::Mir2Lir(CompilationUnit* cu, MIRGraph* mir_graph, ArenaAllocator* arena promotion_map_ = static_cast (arena_->Alloc((cu_->num_dalvik_registers + cu_->num_compiler_temps + 1) * sizeof(promotion_map_[0]), ArenaAllocator::kAllocRegAlloc)); + // Pre-fill with nulls. + boundary_map_.SetSize(cu->code_item->insns_size_in_code_units_); } void Mir2Lir::Materialize() { diff --git a/compiler/dex/quick/local_optimizations.cc b/compiler/dex/quick/local_optimizations.cc index 630e990733f..41adb946a2c 100644 --- a/compiler/dex/quick/local_optimizations.cc +++ b/compiler/dex/quick/local_optimizations.cc @@ -99,12 +99,11 @@ void Mir2Lir::ApplyLoadStoreElimination(LIR* head_lir, LIR* tail_lir) { int native_reg_id; if (cu_->instruction_set == kX86) { // If x86, location differs depending on whether memory/reg operation. - native_reg_id = (GetTargetInstFlags(this_lir->opcode) & IS_STORE) ? this_lir->operands[2] - : this_lir->operands[0]; + native_reg_id = (target_flags & IS_STORE) ? this_lir->operands[2] : this_lir->operands[0]; } else { native_reg_id = this_lir->operands[0]; } - bool is_this_lir_load = GetTargetInstFlags(this_lir->opcode) & IS_LOAD; + bool is_this_lir_load = target_flags & IS_LOAD; LIR* check_lir; /* Use the mem mask to determine the rough memory location */ uint64_t this_mem_mask = (this_lir->use_mask | this_lir->def_mask) & ENCODE_MEM; diff --git a/compiler/dex/quick/mir_to_lir-inl.h b/compiler/dex/quick/mir_to_lir-inl.h index 440df2afa67..f9ec19951ed 100644 --- a/compiler/dex/quick/mir_to_lir-inl.h +++ b/compiler/dex/quick/mir_to_lir-inl.h @@ -33,7 +33,12 @@ inline void Mir2Lir::ClobberBody(RegisterInfo* p) { p->def_end = NULL; if (p->pair) { p->pair = false; - Clobber(p->partner); + p = GetRegInfo(p->partner); + p->pair = false; + p->live = false; + p->s_reg = INVALID_SREG; + p->def_start = NULL; + p->def_end = NULL; } } } diff --git a/compiler/dex/quick/mir_to_lir.cc b/compiler/dex/quick/mir_to_lir.cc index c41feb13484..7c79f598533 100644 --- a/compiler/dex/quick/mir_to_lir.cc +++ b/compiler/dex/quick/mir_to_lir.cc @@ -706,16 +706,15 @@ bool Mir2Lir::MethodBlockCodeGen(BasicBlock* bb) { } // Free temp registers and reset redundant store tracking. - ResetRegPool(); - ResetDefTracking(); - ClobberAllRegs(); if (bb->block_type == kEntryBlock) { + ResetRegPool(); int start_vreg = cu_->num_dalvik_registers - cu_->num_ins; GenEntrySequence(&mir_graph_->reg_location_[start_vreg], mir_graph_->reg_location_[mir_graph_->GetMethodSReg()]); } else if (bb->block_type == kExitBlock) { + ResetRegPool(); GenExitSequence(); } @@ -815,7 +814,7 @@ void Mir2Lir::MethodMIR2LIR() { static_cast(arena_->Alloc(sizeof(LIR) * mir_graph_->GetNumBlocks(), ArenaAllocator::kAllocLIR)); - PreOrderDfsIterator iter(mir_graph_, false /* not iterative */); + PreOrderDfsIterator iter(mir_graph_); for (BasicBlock* bb = iter.Next(); bb != NULL; bb = iter.Next()) { MethodBlockCodeGen(bb); } diff --git a/compiler/dex/quick/mir_to_lir.h b/compiler/dex/quick/mir_to_lir.h index a37ebd173f8..7e6d21a440c 100644 --- a/compiler/dex/quick/mir_to_lir.h +++ b/compiler/dex/quick/mir_to_lir.h @@ -727,7 +727,7 @@ class Mir2Lir : public Backend { GrowableArray throw_launchpads_; GrowableArray suspend_launchpads_; GrowableArray intrinsic_launchpads_; - SafeMap boundary_map_; // boundary lookup cache. + GrowableArray boundary_map_; /* * Holds mapping from native PC to dex PC for safepoints where we may deoptimize. * Native PC is on the return address of the safepointed operation. Dex PC is for diff --git a/compiler/dex/quick/ralloc_util.cc b/compiler/dex/quick/ralloc_util.cc index 71b74a4a68a..db110aabd50 100644 --- a/compiler/dex/quick/ralloc_util.cc +++ b/compiler/dex/quick/ralloc_util.cc @@ -379,7 +379,7 @@ Mir2Lir::RegisterInfo* Mir2Lir::AllocLiveBody(RegisterInfo* p, int num_regs, int if (s_reg == -1) return NULL; for (int i = 0; i < num_regs; i++) { - if (p[i].live && (p[i].s_reg == s_reg)) { + if ((p[i].s_reg == s_reg) && p[i].live) { if (p[i].is_temp) p[i].in_use = true; return &p[i]; @@ -412,47 +412,16 @@ Mir2Lir::RegisterInfo* Mir2Lir::AllocLive(int s_reg, int reg_class) { } void Mir2Lir::FreeTemp(int reg) { - RegisterInfo* p = reg_pool_->core_regs; - int num_regs = reg_pool_->num_core_regs; - for (int i = 0; i< num_regs; i++) { - if (p[i].reg == reg) { - if (p[i].is_temp) { - p[i].in_use = false; - } - p[i].pair = false; - return; - } - } - p = reg_pool_->FPRegs; - num_regs = reg_pool_->num_fp_regs; - for (int i = 0; i< num_regs; i++) { - if (p[i].reg == reg) { - if (p[i].is_temp) { - p[i].in_use = false; - } - p[i].pair = false; - return; - } + RegisterInfo* p = GetRegInfo(reg); + if (p->is_temp) { + p->in_use = false; } - LOG(FATAL) << "Tried to free a non-existant temp: r" << reg; + p->pair = false; } Mir2Lir::RegisterInfo* Mir2Lir::IsLive(int reg) { - RegisterInfo* p = reg_pool_->core_regs; - int num_regs = reg_pool_->num_core_regs; - for (int i = 0; i< num_regs; i++) { - if (p[i].reg == reg) { - return p[i].live ? &p[i] : NULL; - } - } - p = reg_pool_->FPRegs; - num_regs = reg_pool_->num_fp_regs; - for (int i = 0; i< num_regs; i++) { - if (p[i].reg == reg) { - return p[i].live ? &p[i] : NULL; - } - } - return NULL; + RegisterInfo* p = GetRegInfo(reg); + return p->live ? p : NULL; } Mir2Lir::RegisterInfo* Mir2Lir::IsTemp(int reg) { @@ -476,27 +445,10 @@ bool Mir2Lir::IsDirty(int reg) { * allocated. Use with caution. */ void Mir2Lir::LockTemp(int reg) { - RegisterInfo* p = reg_pool_->core_regs; - int num_regs = reg_pool_->num_core_regs; - for (int i = 0; i< num_regs; i++) { - if (p[i].reg == reg) { - DCHECK(p[i].is_temp); - p[i].in_use = true; - p[i].live = false; - return; - } - } - p = reg_pool_->FPRegs; - num_regs = reg_pool_->num_fp_regs; - for (int i = 0; i< num_regs; i++) { - if (p[i].reg == reg) { - DCHECK(p[i].is_temp); - p[i].in_use = true; - p[i].live = false; - return; - } - } - LOG(FATAL) << "Tried to lock a non-existant temp: r" << reg; + RegisterInfo* p = GetRegInfo(reg); + DCHECK(p->is_temp); + p->in_use = true; + p->live = false; } void Mir2Lir::ResetDef(int reg) { @@ -599,11 +551,24 @@ void Mir2Lir::ResetDefTracking() { } void Mir2Lir::ClobberAllRegs() { - for (int i = 0; i< reg_pool_->num_core_regs; i++) { - ClobberBody(®_pool_->core_regs[i]); + RegisterInfo* p; + for (p = reg_pool_->core_regs; p < reg_pool_->core_regs + reg_pool_->num_core_regs; p++) { + if (p->is_temp) { + p->live = false; + p->s_reg = INVALID_SREG; + p->def_start = NULL; + p->def_end = NULL; + p->pair = false; + } } - for (int i = 0; i< reg_pool_->num_fp_regs; i++) { - ClobberBody(®_pool_->FPRegs[i]); + for (p = reg_pool_->FPRegs; p < reg_pool_->FPRegs + reg_pool_->num_fp_regs; p++) { + if (p->is_temp) { + p->live = false; + p->s_reg = INVALID_SREG; + p->def_start = NULL; + p->def_end = NULL; + p->pair = false; + } } } diff --git a/compiler/dex/ssa_transformation.cc b/compiler/dex/ssa_transformation.cc index cd1602f6746..366d7f26be0 100644 --- a/compiler/dex/ssa_transformation.cc +++ b/compiler/dex/ssa_transformation.cc @@ -22,7 +22,7 @@ namespace art { void MIRGraph::ClearAllVisitedFlags() { - AllNodesIterator iter(this, false /* not iterative */); + AllNodesIterator iter(this); for (BasicBlock* bb = iter.Next(); bb != NULL; bb = iter.Next()) { bb->visited = false; } @@ -145,11 +145,11 @@ void MIRGraph::ComputeDefBlockMatrix() { def_block_matrix_[i] = new (arena_) ArenaBitVector(arena_, GetNumBlocks(), false, kBitMapBMatrix); } - AllNodesIterator iter(this, false /* not iterative */); + AllNodesIterator iter(this); for (BasicBlock* bb = iter.Next(); bb != NULL; bb = iter.Next()) { FindLocalLiveIn(bb); } - AllNodesIterator iter2(this, false /* not iterative */); + AllNodesIterator iter2(this); for (BasicBlock* bb = iter2.Next(); bb != NULL; bb = iter2.Next()) { FillDefBlockMatrix(bb); } @@ -377,7 +377,7 @@ void MIRGraph::ComputeDominators() { int num_total_blocks = GetBasicBlockListCount(); /* Initialize domination-related data structures */ - ReachableNodesIterator iter(this, false /* not iterative */); + PreOrderDfsIterator iter(this); for (BasicBlock* bb = iter.Next(); bb != NULL; bb = iter.Next()) { InitializeDominationInfo(bb); } @@ -396,7 +396,7 @@ void MIRGraph::ComputeDominators() { i_dom_list_[GetEntryBlock()->dfs_id] = GetEntryBlock()->dfs_id; /* Compute the immediate dominators */ - ReversePostOrderDfsIterator iter2(this, true /* iterative */); + RepeatingReversePostOrderDfsIterator iter2(this); bool change = false; for (BasicBlock* bb = iter2.Next(false); bb != NULL; bb = iter2.Next(change)) { change = ComputeblockIDom(bb); @@ -414,19 +414,19 @@ void MIRGraph::ComputeDominators() { } GetEntryBlock()->i_dom = NULL; - ReachableNodesIterator iter3(this, false /* not iterative */); + PreOrderDfsIterator iter3(this); for (BasicBlock* bb = iter3.Next(); bb != NULL; bb = iter3.Next()) { SetDominators(bb); } - ReversePostOrderDfsIterator iter4(this, false /* not iterative */); + ReversePostOrderDfsIterator iter4(this); for (BasicBlock* bb = iter4.Next(); bb != NULL; bb = iter4.Next()) { ComputeBlockDominators(bb); } // Compute the dominance frontier for each block. ComputeDomPostOrderTraversal(GetEntryBlock()); - PostOrderDOMIterator iter5(this, false /* not iterative */); + PostOrderDOMIterator iter5(this); for (BasicBlock* bb = iter5.Next(); bb != NULL; bb = iter5.Next()) { ComputeDominanceFrontier(bb); } @@ -503,7 +503,7 @@ void MIRGraph::InsertPhiNodes() { temp_dalvik_register_v_ = new (arena_) ArenaBitVector(arena_, cu_->num_dalvik_registers, false, kBitMapRegisterV); - PostOrderDfsIterator iter(this, true /* iterative */); + RepeatingPostOrderDfsIterator iter(this); bool change = false; for (BasicBlock* bb = iter.Next(false); bb != NULL; bb = iter.Next(change)) { change = ComputeBlockLiveIns(bb); @@ -700,7 +700,7 @@ void MIRGraph::SSATransformation() { new (arena_) ArenaBitVector(arena_, GetNumSSARegs(), false, kBitMapTempSSARegisterV); /* Insert phi-operands with latest SSA names from predecessor blocks */ - ReachableNodesIterator iter2(this, false /* not iterative */); + PreOrderDfsIterator iter2(this); for (BasicBlock* bb = iter2.Next(); bb != NULL; bb = iter2.Next()) { InsertPhiNodeOperands(bb); } diff --git a/compiler/dex/vreg_analysis.cc b/compiler/dex/vreg_analysis.cc index 07f37bbbbb3..25510684d1f 100644 --- a/compiler/dex/vreg_analysis.cc +++ b/compiler/dex/vreg_analysis.cc @@ -444,7 +444,7 @@ void MIRGraph::BuildRegLocations() { } /* Do type & size inference pass */ - PreOrderDfsIterator iter(this, true /* iterative */); + RepeatingPreOrderDfsIterator iter(this); bool change = false; for (BasicBlock* bb = iter.Next(false); bb != NULL; bb = iter.Next(change)) { change = InferTypeAndSize(bb); From 28c2300d9a85f4e7288fb5d94280332f923b4df3 Mon Sep 17 00:00:00 2001 From: buzbee Date: Sat, 7 Sep 2013 09:12:27 -0700 Subject: [PATCH 0017/2402] More compile-time tuning Small, but measurable, improvement. Change-Id: Ie3c7180f9f9cbfb1729588e7a4b2cf6c6d291c95 --- compiler/dex/arena_bit_vector.cc | 6 - compiler/dex/arena_bit_vector.h | 8 +- compiler/dex/mir_graph.h | 23 ++++ compiler/dex/quick/codegen_util.cc | 4 +- compiler/dex/vreg_analysis.cc | 199 ++++++++++++++++++----------- 5 files changed, 155 insertions(+), 85 deletions(-) diff --git a/compiler/dex/arena_bit_vector.cc b/compiler/dex/arena_bit_vector.cc index 3fa92952767..b921f615b6e 100644 --- a/compiler/dex/arena_bit_vector.cc +++ b/compiler/dex/arena_bit_vector.cc @@ -87,12 +87,6 @@ void ArenaBitVector::ClearBit(unsigned int num) { storage_[num >> 5] &= ~check_masks[num & 0x1f]; } -// Copy a whole vector to the other. Sizes must match. -void ArenaBitVector::Copy(ArenaBitVector* src) { - DCHECK_EQ(storage_size_, src->GetStorageSize()); - memcpy(storage_, src->GetRawStorage(), sizeof(uint32_t) * storage_size_); -} - // Intersect with another bit vector. Sizes and expandability must be the same. void ArenaBitVector::Intersect(const ArenaBitVector* src) { DCHECK_EQ(storage_size_, src->GetStorageSize()); diff --git a/compiler/dex/arena_bit_vector.h b/compiler/dex/arena_bit_vector.h index 8bcd628dc0a..24a7ce96015 100644 --- a/compiler/dex/arena_bit_vector.h +++ b/compiler/dex/arena_bit_vector.h @@ -44,7 +44,7 @@ class ArenaBitVector { DCHECK_EQ(bit_size_, p_bits_->GetStorageSize() * sizeof(uint32_t) * 8); DCHECK_EQ(bit_storage_, p_bits_->GetRawStorage()); - if (bit_index_ >= bit_size_) return -1; + if (UNLIKELY(bit_index_ >= bit_size_)) return -1; uint32_t word_index = bit_index_ / 32; uint32_t word = bit_storage_[word_index]; @@ -54,7 +54,7 @@ class ArenaBitVector { bit_index_ &= ~0x1f; do { word_index++; - if ((word_index * 32) >= bit_size_) { + if (UNLIKELY((word_index * 32) >= bit_size_)) { bit_index_ = bit_size_; return -1; } @@ -95,7 +95,9 @@ class ArenaBitVector { bool IsBitSet(unsigned int num); void ClearAllBits(); void SetInitialBits(unsigned int num_bits); - void Copy(ArenaBitVector* src); + void Copy(ArenaBitVector* src) { + memcpy(storage_, src->GetRawStorage(), sizeof(uint32_t) * storage_size_); + } void Intersect(const ArenaBitVector* src2); void Union(const ArenaBitVector* src); // Are we equal to another bit vector? Note: expandability attributes must also match. diff --git a/compiler/dex/mir_graph.h b/compiler/dex/mir_graph.h index 28ab2834e1c..15e15c380f8 100644 --- a/compiler/dex/mir_graph.h +++ b/compiler/dex/mir_graph.h @@ -580,11 +580,34 @@ class MIRGraph { void SSATransformation(); void CheckForDominanceFrontier(BasicBlock* dom_bb, const BasicBlock* succ_bb); void NullCheckElimination(); + /* + * Type inference handling helpers. Because Dalvik's bytecode is not fully typed, + * we have to do some work to figure out the sreg type. For some operations it is + * clear based on the opcode (i.e. ADD_FLOAT v0, v1, v2), but for others (MOVE), we + * may never know the "real" type. + * + * We perform the type inference operation by using an iterative walk over + * the graph, propagating types "defined" by typed opcodes to uses and defs in + * non-typed opcodes (such as MOVE). The Setxx(index) helpers are used to set defined + * types on typed opcodes (such as ADD_INT). The Setxx(index, is_xx) form is used to + * propagate types through non-typed opcodes such as PHI and MOVE. The is_xx flag + * tells whether our guess of the type is based on a previously typed definition. + * If so, the defined type takes precedence. Note that it's possible to have the same sreg + * show multiple defined types because dx treats constants as untyped bit patterns. + * The return value of the Setxx() helpers says whether or not the Setxx() action changed + * the current guess, and is used to know when to terminate the iterative walk. + */ bool SetFp(int index, bool is_fp); + bool SetFp(int index); bool SetCore(int index, bool is_core); + bool SetCore(int index); bool SetRef(int index, bool is_ref); + bool SetRef(int index); bool SetWide(int index, bool is_wide); + bool SetWide(int index); bool SetHigh(int index, bool is_high); + bool SetHigh(int index); + void AppendMIR(BasicBlock* bb, MIR* mir); void PrependMIR(BasicBlock* bb, MIR* mir); void InsertMIRAfter(BasicBlock* bb, MIR* current_mir, MIR* new_mir); diff --git a/compiler/dex/quick/codegen_util.cc b/compiler/dex/quick/codegen_util.cc index 9e0159ecc01..e9db6a68d05 100644 --- a/compiler/dex/quick/codegen_util.cc +++ b/compiler/dex/quick/codegen_util.cc @@ -696,11 +696,11 @@ int Mir2Lir::AssignInsnOffsets() { for (lir = first_lir_insn_; lir != NULL; lir = NEXT_LIR(lir)) { lir->offset = offset; - if (lir->opcode >= 0) { + if (LIKELY(lir->opcode >= 0)) { if (!lir->flags.is_nop) { offset += lir->flags.size; } - } else if (lir->opcode == kPseudoPseudoAlign4) { + } else if (UNLIKELY(lir->opcode == kPseudoPseudoAlign4)) { if (offset & 0x2) { offset += 2; lir->operands[0] = 1; diff --git a/compiler/dex/vreg_analysis.cc b/compiler/dex/vreg_analysis.cc index 25510684d1f..32fac0b393f 100644 --- a/compiler/dex/vreg_analysis.cc +++ b/compiler/dex/vreg_analysis.cc @@ -29,6 +29,16 @@ bool MIRGraph::SetFp(int index, bool is_fp) { return change; } +bool MIRGraph::SetFp(int index) { + bool change = false; + if (!reg_location_[index].fp) { + reg_location_[index].fp = true; + reg_location_[index].defined = true; + change = true; + } + return change; +} + bool MIRGraph::SetCore(int index, bool is_core) { bool change = false; if (is_core && !reg_location_[index].defined) { @@ -39,6 +49,16 @@ bool MIRGraph::SetCore(int index, bool is_core) { return change; } +bool MIRGraph::SetCore(int index) { + bool change = false; + if (!reg_location_[index].defined) { + reg_location_[index].core = true; + reg_location_[index].defined = true; + change = true; + } + return change; +} + bool MIRGraph::SetRef(int index, bool is_ref) { bool change = false; if (is_ref && !reg_location_[index].defined) { @@ -49,6 +69,16 @@ bool MIRGraph::SetRef(int index, bool is_ref) { return change; } +bool MIRGraph::SetRef(int index) { + bool change = false; + if (!reg_location_[index].defined) { + reg_location_[index].ref = true; + reg_location_[index].defined = true; + change = true; + } + return change; +} + bool MIRGraph::SetWide(int index, bool is_wide) { bool change = false; if (is_wide && !reg_location_[index].wide) { @@ -58,6 +88,15 @@ bool MIRGraph::SetWide(int index, bool is_wide) { return change; } +bool MIRGraph::SetWide(int index) { + bool change = false; + if (!reg_location_[index].wide) { + reg_location_[index].wide = true; + change = true; + } + return change; +} + bool MIRGraph::SetHigh(int index, bool is_high) { bool change = false; if (is_high && !reg_location_[index].high_word) { @@ -67,6 +106,16 @@ bool MIRGraph::SetHigh(int index, bool is_high) { return change; } +bool MIRGraph::SetHigh(int index) { + bool change = false; + if (!reg_location_[index].high_word) { + reg_location_[index].high_word = true; + change = true; + } + return change; +} + + /* * Infer types and sizes. We don't need to track change on sizes, * as it doesn't propagate. We're guaranteed at least one pass through @@ -84,21 +133,23 @@ bool MIRGraph::InferTypeAndSize(BasicBlock* bb) { SSARepresentation *ssa_rep = mir->ssa_rep; if (ssa_rep) { int attrs = oat_data_flow_attributes_[mir->dalvikInsn.opcode]; + const int* uses = ssa_rep->uses; + const int* defs = ssa_rep->defs; // Handle defs if (attrs & DF_DA) { if (attrs & DF_CORE_A) { - changed |= SetCore(ssa_rep->defs[0], true); + changed |= SetCore(defs[0]); } if (attrs & DF_REF_A) { - changed |= SetRef(ssa_rep->defs[0], true); + changed |= SetRef(defs[0]); } if (attrs & DF_A_WIDE) { - reg_location_[ssa_rep->defs[0]].wide = true; - reg_location_[ssa_rep->defs[1]].wide = true; - reg_location_[ssa_rep->defs[1]].high_word = true; - DCHECK_EQ(SRegToVReg(ssa_rep->defs[0])+1, - SRegToVReg(ssa_rep->defs[1])); + reg_location_[defs[0]].wide = true; + reg_location_[defs[1]].wide = true; + reg_location_[defs[1]].high_word = true; + DCHECK_EQ(SRegToVReg(defs[0])+1, + SRegToVReg(defs[1])); } } @@ -106,17 +157,17 @@ bool MIRGraph::InferTypeAndSize(BasicBlock* bb) { int next = 0; if (attrs & DF_UA) { if (attrs & DF_CORE_A) { - changed |= SetCore(ssa_rep->uses[next], true); + changed |= SetCore(uses[next]); } if (attrs & DF_REF_A) { - changed |= SetRef(ssa_rep->uses[next], true); + changed |= SetRef(uses[next]); } if (attrs & DF_A_WIDE) { - reg_location_[ssa_rep->uses[next]].wide = true; - reg_location_[ssa_rep->uses[next + 1]].wide = true; - reg_location_[ssa_rep->uses[next + 1]].high_word = true; - DCHECK_EQ(SRegToVReg(ssa_rep->uses[next])+1, - SRegToVReg(ssa_rep->uses[next + 1])); + reg_location_[uses[next]].wide = true; + reg_location_[uses[next + 1]].wide = true; + reg_location_[uses[next + 1]].high_word = true; + DCHECK_EQ(SRegToVReg(uses[next])+1, + SRegToVReg(uses[next + 1])); next += 2; } else { next++; @@ -124,17 +175,17 @@ bool MIRGraph::InferTypeAndSize(BasicBlock* bb) { } if (attrs & DF_UB) { if (attrs & DF_CORE_B) { - changed |= SetCore(ssa_rep->uses[next], true); + changed |= SetCore(uses[next]); } if (attrs & DF_REF_B) { - changed |= SetRef(ssa_rep->uses[next], true); + changed |= SetRef(uses[next]); } if (attrs & DF_B_WIDE) { - reg_location_[ssa_rep->uses[next]].wide = true; - reg_location_[ssa_rep->uses[next + 1]].wide = true; - reg_location_[ssa_rep->uses[next + 1]].high_word = true; - DCHECK_EQ(SRegToVReg(ssa_rep->uses[next])+1, - SRegToVReg(ssa_rep->uses[next + 1])); + reg_location_[uses[next]].wide = true; + reg_location_[uses[next + 1]].wide = true; + reg_location_[uses[next + 1]].high_word = true; + DCHECK_EQ(SRegToVReg(uses[next])+1, + SRegToVReg(uses[next + 1])); next += 2; } else { next++; @@ -142,17 +193,17 @@ bool MIRGraph::InferTypeAndSize(BasicBlock* bb) { } if (attrs & DF_UC) { if (attrs & DF_CORE_C) { - changed |= SetCore(ssa_rep->uses[next], true); + changed |= SetCore(uses[next]); } if (attrs & DF_REF_C) { - changed |= SetRef(ssa_rep->uses[next], true); + changed |= SetRef(uses[next]); } if (attrs & DF_C_WIDE) { - reg_location_[ssa_rep->uses[next]].wide = true; - reg_location_[ssa_rep->uses[next + 1]].wide = true; - reg_location_[ssa_rep->uses[next + 1]].high_word = true; - DCHECK_EQ(SRegToVReg(ssa_rep->uses[next])+1, - SRegToVReg(ssa_rep->uses[next + 1])); + reg_location_[uses[next]].wide = true; + reg_location_[uses[next + 1]].wide = true; + reg_location_[uses[next + 1]].high_word = true; + DCHECK_EQ(SRegToVReg(uses[next])+1, + SRegToVReg(uses[next + 1])); } } @@ -162,27 +213,27 @@ bool MIRGraph::InferTypeAndSize(BasicBlock* bb) { (mir->dalvikInsn.opcode == Instruction::RETURN_OBJECT)) { switch (cu_->shorty[0]) { case 'I': - changed |= SetCore(ssa_rep->uses[0], true); + changed |= SetCore(uses[0]); break; case 'J': - changed |= SetCore(ssa_rep->uses[0], true); - changed |= SetCore(ssa_rep->uses[1], true); - reg_location_[ssa_rep->uses[0]].wide = true; - reg_location_[ssa_rep->uses[1]].wide = true; - reg_location_[ssa_rep->uses[1]].high_word = true; + changed |= SetCore(uses[0]); + changed |= SetCore(uses[1]); + reg_location_[uses[0]].wide = true; + reg_location_[uses[1]].wide = true; + reg_location_[uses[1]].high_word = true; break; case 'F': - changed |= SetFp(ssa_rep->uses[0], true); + changed |= SetFp(uses[0]); break; case 'D': - changed |= SetFp(ssa_rep->uses[0], true); - changed |= SetFp(ssa_rep->uses[1], true); - reg_location_[ssa_rep->uses[0]].wide = true; - reg_location_[ssa_rep->uses[1]].wide = true; - reg_location_[ssa_rep->uses[1]].high_word = true; + changed |= SetFp(uses[0]); + changed |= SetFp(uses[1]); + reg_location_[uses[0]].wide = true; + reg_location_[uses[1]].wide = true; + reg_location_[uses[1]].high_word = true; break; case 'L': - changed |= SetRef(ssa_rep->uses[0], true); + changed |= SetRef(uses[0]); break; default: break; } @@ -206,10 +257,10 @@ bool MIRGraph::InferTypeAndSize(BasicBlock* bb) { SSARepresentation* tgt_rep = move_result_mir->ssa_rep; DCHECK(tgt_rep != NULL); tgt_rep->fp_def[0] = true; - changed |= SetFp(tgt_rep->defs[0], true); + changed |= SetFp(tgt_rep->defs[0]); if (shorty[0] == 'D') { tgt_rep->fp_def[1] = true; - changed |= SetFp(tgt_rep->defs[1], true); + changed |= SetFp(tgt_rep->defs[1]); } } } @@ -217,8 +268,8 @@ bool MIRGraph::InferTypeAndSize(BasicBlock* bb) { // If this is a non-static invoke, mark implicit "this" if (((mir->dalvikInsn.opcode != Instruction::INVOKE_STATIC) && (mir->dalvikInsn.opcode != Instruction::INVOKE_STATIC_RANGE))) { - reg_location_[ssa_rep->uses[next]].defined = true; - reg_location_[ssa_rep->uses[next]].ref = true; + reg_location_[uses[next]].defined = true; + reg_location_[uses[next]].ref = true; next++; } uint32_t cpos = 1; @@ -229,28 +280,28 @@ bool MIRGraph::InferTypeAndSize(BasicBlock* bb) { case 'D': ssa_rep->fp_use[i] = true; ssa_rep->fp_use[i+1] = true; - reg_location_[ssa_rep->uses[i]].wide = true; - reg_location_[ssa_rep->uses[i+1]].wide = true; - reg_location_[ssa_rep->uses[i+1]].high_word = true; - DCHECK_EQ(SRegToVReg(ssa_rep->uses[i])+1, SRegToVReg(ssa_rep->uses[i+1])); + reg_location_[uses[i]].wide = true; + reg_location_[uses[i+1]].wide = true; + reg_location_[uses[i+1]].high_word = true; + DCHECK_EQ(SRegToVReg(uses[i])+1, SRegToVReg(uses[i+1])); i++; break; case 'J': - reg_location_[ssa_rep->uses[i]].wide = true; - reg_location_[ssa_rep->uses[i+1]].wide = true; - reg_location_[ssa_rep->uses[i+1]].high_word = true; - DCHECK_EQ(SRegToVReg(ssa_rep->uses[i])+1, SRegToVReg(ssa_rep->uses[i+1])); - changed |= SetCore(ssa_rep->uses[i], true); + reg_location_[uses[i]].wide = true; + reg_location_[uses[i+1]].wide = true; + reg_location_[uses[i+1]].high_word = true; + DCHECK_EQ(SRegToVReg(uses[i])+1, SRegToVReg(uses[i+1])); + changed |= SetCore(uses[i]); i++; break; case 'F': ssa_rep->fp_use[i] = true; break; case 'L': - changed |= SetRef(ssa_rep->uses[i], true); + changed |= SetRef(uses[i]); break; default: - changed |= SetCore(ssa_rep->uses[i], true); + changed |= SetCore(uses[i]); break; } i++; @@ -260,11 +311,11 @@ bool MIRGraph::InferTypeAndSize(BasicBlock* bb) { for (int i = 0; ssa_rep->fp_use && i< ssa_rep->num_uses; i++) { if (ssa_rep->fp_use[i]) - changed |= SetFp(ssa_rep->uses[i], true); + changed |= SetFp(uses[i]); } for (int i = 0; ssa_rep->fp_def && i< ssa_rep->num_defs; i++) { if (ssa_rep->fp_def[i]) - changed |= SetFp(ssa_rep->defs[i], true); + changed |= SetFp(defs[i]); } // Special-case handling for moves & Phi if (attrs & (DF_IS_MOVE | DF_NULL_TRANSFER_N)) { @@ -276,14 +327,14 @@ bool MIRGraph::InferTypeAndSize(BasicBlock* bb) { */ bool is_phi = (static_cast(mir->dalvikInsn.opcode) == kMirOpPhi); - RegLocation rl_temp = reg_location_[ssa_rep->defs[0]]; + RegLocation rl_temp = reg_location_[defs[0]]; bool defined_fp = rl_temp.defined && rl_temp.fp; bool defined_core = rl_temp.defined && rl_temp.core; bool defined_ref = rl_temp.defined && rl_temp.ref; bool is_wide = rl_temp.wide || ((attrs & DF_A_WIDE) != 0); bool is_high = is_phi && rl_temp.wide && rl_temp.high_word; for (int i = 0; i < ssa_rep->num_uses; i++) { - rl_temp = reg_location_[ssa_rep->uses[i]]; + rl_temp = reg_location_[uses[i]]; defined_fp |= rl_temp.defined && rl_temp.fp; defined_core |= rl_temp.defined && rl_temp.core; defined_ref |= rl_temp.defined && rl_temp.ref; @@ -303,26 +354,26 @@ bool MIRGraph::InferTypeAndSize(BasicBlock* bb) { << " has both fp and core/ref uses for same def."; cu_->disable_opt |= (1 << kPromoteRegs); } - changed |= SetFp(ssa_rep->defs[0], defined_fp); - changed |= SetCore(ssa_rep->defs[0], defined_core); - changed |= SetRef(ssa_rep->defs[0], defined_ref); - changed |= SetWide(ssa_rep->defs[0], is_wide); - changed |= SetHigh(ssa_rep->defs[0], is_high); + changed |= SetFp(defs[0], defined_fp); + changed |= SetCore(defs[0], defined_core); + changed |= SetRef(defs[0], defined_ref); + changed |= SetWide(defs[0], is_wide); + changed |= SetHigh(defs[0], is_high); if (attrs & DF_A_WIDE) { - changed |= SetWide(ssa_rep->defs[1], true); - changed |= SetHigh(ssa_rep->defs[1], true); + changed |= SetWide(defs[1]); + changed |= SetHigh(defs[1]); } for (int i = 0; i < ssa_rep->num_uses; i++) { - changed |= SetFp(ssa_rep->uses[i], defined_fp); - changed |= SetCore(ssa_rep->uses[i], defined_core); - changed |= SetRef(ssa_rep->uses[i], defined_ref); - changed |= SetWide(ssa_rep->uses[i], is_wide); - changed |= SetHigh(ssa_rep->uses[i], is_high); + changed |= SetFp(uses[i], defined_fp); + changed |= SetCore(uses[i], defined_core); + changed |= SetRef(uses[i], defined_ref); + changed |= SetWide(uses[i], is_wide); + changed |= SetHigh(uses[i], is_high); } if (attrs & DF_A_WIDE) { DCHECK_EQ(ssa_rep->num_uses, 2); - changed |= SetWide(ssa_rep->uses[1], true); - changed |= SetHigh(ssa_rep->uses[1], true); + changed |= SetWide(uses[1]); + changed |= SetHigh(uses[1]); } } } From 02ed4c04468ca5f5540c5b704ac3e2f30eb9e8f4 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Fri, 6 Sep 2013 13:10:04 -0700 Subject: [PATCH 0018/2402] Move disassembler out of runtime. Bug: 9877500. Change-Id: Ica6d9f5ecfd20c86e5230a2213827bd78cd29a29 --- Android.mk | 1 + compiler/dex/arena_allocator.cc | 1 + compiler/dex/dex_to_dex_compiler.cc | 1 + compiler/dex/portable/mir_to_gbc.cc | 4 +- compiler/dex/quick/mir_to_lir.cc | 1 + compiler/jni/quick/jni_compiler.cc | 6 - compiler/llvm/compiler_llvm.cc | 1 + disassembler/Android.mk | 120 ++++++++++++++++++ {runtime => disassembler}/disassembler.cc | 0 {runtime => disassembler}/disassembler.h | 6 +- {runtime => disassembler}/disassembler_arm.cc | 0 {runtime => disassembler}/disassembler_arm.h | 6 +- .../disassembler_mips.cc | 0 {runtime => disassembler}/disassembler_mips.h | 6 +- {runtime => disassembler}/disassembler_x86.cc | 0 {runtime => disassembler}/disassembler_x86.h | 6 +- oatdump/Android.mk | 8 +- runtime/Android.mk | 4 - runtime/thread-inl.h | 13 ++ runtime/thread.h | 13 +- 20 files changed, 157 insertions(+), 40 deletions(-) create mode 100644 disassembler/Android.mk rename {runtime => disassembler}/disassembler.cc (100%) rename {runtime => disassembler}/disassembler.h (90%) rename {runtime => disassembler}/disassembler_arm.cc (100%) rename {runtime => disassembler}/disassembler_arm.h (91%) rename {runtime => disassembler}/disassembler_mips.cc (100%) rename {runtime => disassembler}/disassembler_mips.h (87%) rename {runtime => disassembler}/disassembler_x86.cc (100%) rename {runtime => disassembler}/disassembler_x86.h (88%) diff --git a/Android.mk b/Android.mk index 46a7c1ec3e2..0b4b2316fd8 100644 --- a/Android.mk +++ b/Android.mk @@ -85,6 +85,7 @@ ifneq ($(art_dont_bother),true) include $(art_path)/runtime/Android.mk include $(art_path)/compiler/Android.mk include $(art_path)/dex2oat/Android.mk +include $(art_path)/disassembler/Android.mk include $(art_path)/oatdump/Android.mk include $(art_path)/dalvikvm/Android.mk include $(art_path)/jdwpspy/Android.mk diff --git a/compiler/dex/arena_allocator.cc b/compiler/dex/arena_allocator.cc index 36393e73877..5a91d27ef5d 100644 --- a/compiler/dex/arena_allocator.cc +++ b/compiler/dex/arena_allocator.cc @@ -19,6 +19,7 @@ #include "arena_allocator.h" #include "base/logging.h" #include "base/mutex.h" +#include "thread-inl.h" namespace art { diff --git a/compiler/dex/dex_to_dex_compiler.cc b/compiler/dex/dex_to_dex_compiler.cc index 4a724b109ad..ffd7905dfe7 100644 --- a/compiler/dex/dex_to_dex_compiler.cc +++ b/compiler/dex/dex_to_dex_compiler.cc @@ -24,6 +24,7 @@ #include "mirror/art_method-inl.h" #include "mirror/class-inl.h" #include "mirror/dex_cache.h" +#include "thread-inl.h" namespace art { namespace optimizer { diff --git a/compiler/dex/portable/mir_to_gbc.cc b/compiler/dex/portable/mir_to_gbc.cc index 2cf55e7ea5c..df10f7eda0d 100644 --- a/compiler/dex/portable/mir_to_gbc.cc +++ b/compiler/dex/portable/mir_to_gbc.cc @@ -30,10 +30,10 @@ #include "dex/compiler_internals.h" #include "dex/dataflow_iterator-inl.h" #include "dex/frontend.h" -#include "mir_to_gbc.h" - #include "llvm/llvm_compilation_unit.h" #include "llvm/utils_llvm.h" +#include "mir_to_gbc.h" +#include "thread-inl.h" const char* kLabelFormat = "%c0x%x_%d"; const char kInvalidBlock = 0xff; diff --git a/compiler/dex/quick/mir_to_lir.cc b/compiler/dex/quick/mir_to_lir.cc index 7c79f598533..22dd6dadb9a 100644 --- a/compiler/dex/quick/mir_to_lir.cc +++ b/compiler/dex/quick/mir_to_lir.cc @@ -18,6 +18,7 @@ #include "dex/dataflow_iterator-inl.h" #include "mir_to_lir-inl.h" #include "object_utils.h" +#include "thread-inl.h" namespace art { diff --git a/compiler/jni/quick/jni_compiler.cc b/compiler/jni/quick/jni_compiler.cc index 1417fb9e406..b6b15f94ebd 100644 --- a/compiler/jni/quick/jni_compiler.cc +++ b/compiler/jni/quick/jni_compiler.cc @@ -24,7 +24,6 @@ #include "compiled_method.h" #include "dex_file-inl.h" #include "driver/compiler_driver.h" -#include "disassembler.h" #include "entrypoints/quick/quick_entrypoints.h" #include "jni_internal.h" #include "utils/assembler.h" @@ -85,7 +84,6 @@ CompiledMethod* ArtJniCompileMethodInternal(CompilerDriver& compiler, // Assembler that holds generated instructions UniquePtr jni_asm(Assembler::Create(instruction_set)); - bool should_disassemble = false; // Offsets into data structures // TODO: if cross compiling these offsets are for the host not the target @@ -366,10 +364,6 @@ CompiledMethod* ArtJniCompileMethodInternal(CompilerDriver& compiler, std::vector managed_code(cs); MemoryRegion code(&managed_code[0], managed_code.size()); __ FinalizeInstructions(code); - if (should_disassemble) { - UniquePtr disassembler(Disassembler::Create(instruction_set)); - disassembler->Dump(LOG(INFO), &managed_code[0], &managed_code[managed_code.size()]); - } return new CompiledMethod(compiler, instruction_set, managed_code, diff --git a/compiler/llvm/compiler_llvm.cc b/compiler/llvm/compiler_llvm.cc index 83b0c75e049..0df3c476fc5 100644 --- a/compiler/llvm/compiler_llvm.cc +++ b/compiler/llvm/compiler_llvm.cc @@ -26,6 +26,7 @@ #include "ir_builder.h" #include "jni/portable/jni_compiler.h" #include "llvm_compilation_unit.h" +#include "thread-inl.h" #include "utils_llvm.h" #include "verifier/method_verifier.h" diff --git a/disassembler/Android.mk b/disassembler/Android.mk new file mode 100644 index 00000000000..f8001a45249 --- /dev/null +++ b/disassembler/Android.mk @@ -0,0 +1,120 @@ +# +# Copyright (C) 2012 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +LOCAL_PATH := $(call my-dir) + +include art/build/Android.common.mk + +LIBART_DISASSEMBLER_SRC_FILES := \ + disassembler.cc \ + disassembler_arm.cc \ + disassembler_mips.cc \ + disassembler_x86.cc + +# $(1): target or host +# $(2): ndebug or debug +define build-libart-disassembler + ifneq ($(1),target) + ifneq ($(1),host) + $$(error expected target or host for argument 1, received $(1)) + endif + endif + ifneq ($(2),ndebug) + ifneq ($(2),debug) + $$(error expected ndebug or debug for argument 2, received $(2)) + endif + endif + + art_target_or_host := $(1) + art_ndebug_or_debug := $(2) + + include $(CLEAR_VARS) + ifeq ($$(art_target_or_host),target) + include external/stlport/libstlport.mk + else + LOCAL_IS_HOST_MODULE := true + endif + LOCAL_CPP_EXTENSION := $(ART_CPP_EXTENSION) + ifeq ($$(art_ndebug_or_debug),ndebug) + LOCAL_MODULE := libart-disassembler + else # debug + LOCAL_MODULE := libartd-disassembler + endif + + LOCAL_MODULE_TAGS := optional + LOCAL_MODULE_CLASS := SHARED_LIBRARIES + + LOCAL_SRC_FILES := $$(LIBART_DISASSEMBLER_SRC_FILES) + + GENERATED_SRC_DIR := $$(call intermediates-dir-for,$$(LOCAL_MODULE_CLASS),$$(LOCAL_MODULE),$$(LOCAL_IS_HOST_MODULE),) + + ifeq ($$(art_target_or_host),target) + LOCAL_CLANG := $(ART_TARGET_CLANG) + LOCAL_CFLAGS += $(ART_TARGET_CFLAGS) + else # host + LOCAL_CLANG := $(ART_HOST_CLANG) + LOCAL_CFLAGS += $(ART_HOST_CFLAGS) + endif + + LOCAL_SHARED_LIBRARIES += liblog + ifeq ($$(art_ndebug_or_debug),debug) + ifeq ($$(art_target_or_host),target) + LOCAL_CFLAGS += $(ART_TARGET_DEBUG_CFLAGS) + else # host + LOCAL_CFLAGS += $(ART_HOST_DEBUG_CFLAGS) + endif + LOCAL_SHARED_LIBRARIES += libartd + else + ifeq ($$(art_target_or_host),target) + LOCAL_CFLAGS += $(ART_TARGET_NON_DEBUG_CFLAGS) + else # host + LOCAL_CFLAGS += $(ART_HOST_NON_DEBUG_CFLAGS) + endif + LOCAL_SHARED_LIBRARIES += libart + endif + + LOCAL_C_INCLUDES += $(ART_C_INCLUDES) art/runtime + + LOCAL_ADDITIONAL_DEPENDENCIES := art/build/Android.common.mk + LOCAL_ADDITIONAL_DEPENDENCIES += $(LOCAL_PATH)/Android.mk + ifeq ($$(art_target_or_host),target) + LOCAL_SHARED_LIBRARIES += libcutils + include $(LLVM_GEN_INTRINSICS_MK) + include $(LLVM_DEVICE_BUILD_MK) + include $(BUILD_SHARED_LIBRARY) + else # host + LOCAL_STATIC_LIBRARIES += libcutils + include $(LLVM_GEN_INTRINSICS_MK) + include $(LLVM_HOST_BUILD_MK) + include $(BUILD_HOST_SHARED_LIBRARY) + endif +endef + +ifeq ($(ART_BUILD_TARGET_NDEBUG),true) + $(eval $(call build-libart-disassembler,target,ndebug)) +endif +ifeq ($(ART_BUILD_TARGET_DEBUG),true) + $(eval $(call build-libart-disassembler,target,debug)) +endif +ifeq ($(WITH_HOST_DALVIK),true) + # We always build dex2oat and dependencies, even if the host build is otherwise disabled, since they are used to cross compile for the target. + ifeq ($(ART_BUILD_NDEBUG),true) + $(eval $(call build-libart-disassembler,host,ndebug)) + endif + ifeq ($(ART_BUILD_DEBUG),true) + $(eval $(call build-libart-disassembler,host,debug)) + endif +endif diff --git a/runtime/disassembler.cc b/disassembler/disassembler.cc similarity index 100% rename from runtime/disassembler.cc rename to disassembler/disassembler.cc diff --git a/runtime/disassembler.h b/disassembler/disassembler.h similarity index 90% rename from runtime/disassembler.h rename to disassembler/disassembler.h index 805ff4d079d..7547ab722b2 100644 --- a/runtime/disassembler.h +++ b/disassembler/disassembler.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef ART_RUNTIME_DISASSEMBLER_H_ -#define ART_RUNTIME_DISASSEMBLER_H_ +#ifndef ART_DISASSEMBLER_DISASSEMBLER_H_ +#define ART_DISASSEMBLER_DISASSEMBLER_H_ #include @@ -45,4 +45,4 @@ class Disassembler { } // namespace art -#endif // ART_RUNTIME_DISASSEMBLER_H_ +#endif // ART_DISASSEMBLER_DISASSEMBLER_H_ diff --git a/runtime/disassembler_arm.cc b/disassembler/disassembler_arm.cc similarity index 100% rename from runtime/disassembler_arm.cc rename to disassembler/disassembler_arm.cc diff --git a/runtime/disassembler_arm.h b/disassembler/disassembler_arm.h similarity index 91% rename from runtime/disassembler_arm.h rename to disassembler/disassembler_arm.h index cab91501084..2e699ffe88f 100644 --- a/runtime/disassembler_arm.h +++ b/disassembler/disassembler_arm.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef ART_RUNTIME_DISASSEMBLER_ARM_H_ -#define ART_RUNTIME_DISASSEMBLER_ARM_H_ +#ifndef ART_DISASSEMBLER_DISASSEMBLER_ARM_H_ +#define ART_DISASSEMBLER_DISASSEMBLER_ARM_H_ #include @@ -48,4 +48,4 @@ class DisassemblerArm : public Disassembler { } // namespace arm } // namespace art -#endif // ART_RUNTIME_DISASSEMBLER_ARM_H_ +#endif // ART_DISASSEMBLER_DISASSEMBLER_ARM_H_ diff --git a/runtime/disassembler_mips.cc b/disassembler/disassembler_mips.cc similarity index 100% rename from runtime/disassembler_mips.cc rename to disassembler/disassembler_mips.cc diff --git a/runtime/disassembler_mips.h b/disassembler/disassembler_mips.h similarity index 87% rename from runtime/disassembler_mips.h rename to disassembler/disassembler_mips.h index e248503963c..d3862676a0a 100644 --- a/runtime/disassembler_mips.h +++ b/disassembler/disassembler_mips.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef ART_RUNTIME_DISASSEMBLER_MIPS_H_ -#define ART_RUNTIME_DISASSEMBLER_MIPS_H_ +#ifndef ART_DISASSEMBLER_DISASSEMBLER_MIPS_H_ +#define ART_DISASSEMBLER_DISASSEMBLER_MIPS_H_ #include @@ -37,4 +37,4 @@ class DisassemblerMips : public Disassembler { } // namespace mips } // namespace art -#endif // ART_RUNTIME_DISASSEMBLER_MIPS_H_ +#endif // ART_DISASSEMBLER_DISASSEMBLER_MIPS_H_ diff --git a/runtime/disassembler_x86.cc b/disassembler/disassembler_x86.cc similarity index 100% rename from runtime/disassembler_x86.cc rename to disassembler/disassembler_x86.cc diff --git a/runtime/disassembler_x86.h b/disassembler/disassembler_x86.h similarity index 88% rename from runtime/disassembler_x86.h rename to disassembler/disassembler_x86.h index ff4322c8b81..9adaff7048b 100644 --- a/runtime/disassembler_x86.h +++ b/disassembler/disassembler_x86.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef ART_RUNTIME_DISASSEMBLER_X86_H_ -#define ART_RUNTIME_DISASSEMBLER_X86_H_ +#ifndef ART_DISASSEMBLER_DISASSEMBLER_X86_H_ +#define ART_DISASSEMBLER_DISASSEMBLER_X86_H_ #include "disassembler.h" @@ -35,4 +35,4 @@ class DisassemblerX86 : public Disassembler { } // namespace x86 } // namespace art -#endif // ART_RUNTIME_DISASSEMBLER_X86_H_ +#endif // ART_DISASSEMBLER_DISASSEMBLER_X86_H_ diff --git a/oatdump/Android.mk b/oatdump/Android.mk index a63b229846a..7cee00e1828 100644 --- a/oatdump/Android.mk +++ b/oatdump/Android.mk @@ -22,17 +22,17 @@ OATDUMP_SRC_FILES := \ include art/build/Android.executable.mk ifeq ($(ART_BUILD_TARGET_NDEBUG),true) - $(eval $(call build-art-executable,oatdump,$(OATDUMP_SRC_FILES),libcutils,,target,ndebug)) + $(eval $(call build-art-executable,oatdump,$(OATDUMP_SRC_FILES),libcutils libart-disassembler,art/disassembler,target,ndebug)) endif ifeq ($(ART_BUILD_TARGET_DEBUG),true) - $(eval $(call build-art-executable,oatdump,$(OATDUMP_SRC_FILES),libcutils,,target,debug)) + $(eval $(call build-art-executable,oatdump,$(OATDUMP_SRC_FILES),libcutils libartd-disassembler,art/disassembler,target,debug)) endif ifeq ($(WITH_HOST_DALVIK),true) ifeq ($(ART_BUILD_HOST_NDEBUG),true) - $(eval $(call build-art-executable,oatdump,$(OATDUMP_SRC_FILES),,,host,ndebug)) + $(eval $(call build-art-executable,oatdump,$(OATDUMP_SRC_FILES),libart-disassembler,art/disassembler,host,ndebug)) endif ifeq ($(ART_BUILD_HOST_DEBUG),true) - $(eval $(call build-art-executable,oatdump,$(OATDUMP_SRC_FILES),,,host,debug)) + $(eval $(call build-art-executable,oatdump,$(OATDUMP_SRC_FILES),libartd-disassembler,art/disassembler,host,debug)) endif endif diff --git a/runtime/Android.mk b/runtime/Android.mk index a8d505e1f5c..b04cd6ae0b8 100644 --- a/runtime/Android.mk +++ b/runtime/Android.mk @@ -38,10 +38,6 @@ LIBART_COMMON_SRC_FILES := \ dex_file.cc \ dex_file_verifier.cc \ dex_instruction.cc \ - disassembler.cc \ - disassembler_arm.cc \ - disassembler_mips.cc \ - disassembler_x86.cc \ elf_file.cc \ gc/allocator/dlmalloc.cc \ gc/accounting/card_table.cc \ diff --git a/runtime/thread-inl.h b/runtime/thread-inl.h index c22f2cd9210..4552062319b 100644 --- a/runtime/thread-inl.h +++ b/runtime/thread-inl.h @@ -19,11 +19,24 @@ #include "thread.h" +#include + #include "base/mutex-inl.h" #include "cutils/atomic-inline.h" namespace art { +inline Thread* Thread::Current() { + // We rely on Thread::Current returning NULL for a detached thread, so it's not obvious + // that we can replace this with a direct %fs access on x86. + if (!is_started_) { + return NULL; + } else { + void* thread = pthread_getspecific(Thread::pthread_key_self_); + return reinterpret_cast(thread); + } +} + inline ThreadState Thread::SetState(ThreadState new_state) { // Cannot use this code to change into Runnable as changing to Runnable should fail if // old_state_and_flags.suspend_request is true. diff --git a/runtime/thread.h b/runtime/thread.h index 40e3f5fbb2f..f5f8f563fa8 100644 --- a/runtime/thread.h +++ b/runtime/thread.h @@ -17,8 +17,6 @@ #ifndef ART_RUNTIME_THREAD_H_ #define ART_RUNTIME_THREAD_H_ -#include - #include #include #include @@ -104,16 +102,7 @@ class PACKED(4) Thread { // Reset internal state of child thread after fork. void InitAfterFork(); - static Thread* Current() { - // We rely on Thread::Current returning NULL for a detached thread, so it's not obvious - // that we can replace this with a direct %fs access on x86. - if (!is_started_) { - return NULL; - } else { - void* thread = pthread_getspecific(Thread::pthread_key_self_); - return reinterpret_cast(thread); - } - } + static Thread* Current(); static Thread* FromManagedThread(const ScopedObjectAccessUnchecked& ts, mirror::Object* thread_peer) From 8ece050d85fc244c72610244e440b0e00aa618fa Mon Sep 17 00:00:00 2001 From: Sebastien Hertz Date: Wed, 7 Aug 2013 11:26:41 +0200 Subject: [PATCH 0019/2402] Add an interpreter using computed goto table. This CL adds a new implementation of the interpreter using computed goto table. In order to keep the switch-based implementation, it reorders things as the following: - Keep interpreter entrypoints into interpreter.h/.cc files. - Move common interpreter parts to interpreter_common.h/.cc files. - Separate both implementations to their own modules. The interpreter implementation can be selected by changing the value of the kInterpreterImplKind global variable (see interpreter.cc file). The default one remains the switch-based implementation. Also updates the exception handling (FindNextInstructionFollowingException) and SPARSE_SWITCH switch handling (DoSparseSwitch) routines to share code between both implementations. Finally, adds a PACKED_SWITCH handling routine (DoPackedSwitch) so we are consistent with SPARSE_SWITCH handling. The computed goto implementation use two handlers table: one for normal instruction handling and one for instrumentation handling. The current handlers table to be used is updated on backward branch depending on whether there is listener to DEX pc change. Bug: 10602809 Change-Id: Ibb53bcc68be75c473fe5440835e78fc9a74381b3 --- runtime/Android.mk | 3 + runtime/interpreter/interpreter.cc | 2812 +---------------- runtime/interpreter/interpreter_common.cc | 389 +++ runtime/interpreter/interpreter_common.h | 516 +++ .../interpreter_goto_table_impl.cc | 2343 ++++++++++++++ .../interpreter/interpreter_switch_impl.cc | 2030 ++++++++++++ 6 files changed, 5307 insertions(+), 2786 deletions(-) create mode 100644 runtime/interpreter/interpreter_common.cc create mode 100644 runtime/interpreter/interpreter_common.h create mode 100644 runtime/interpreter/interpreter_goto_table_impl.cc create mode 100644 runtime/interpreter/interpreter_switch_impl.cc diff --git a/runtime/Android.mk b/runtime/Android.mk index a8d505e1f5c..d4f1a3937ad 100644 --- a/runtime/Android.mk +++ b/runtime/Android.mk @@ -64,6 +64,9 @@ LIBART_COMMON_SRC_FILES := \ instrumentation.cc \ intern_table.cc \ interpreter/interpreter.cc \ + interpreter/interpreter_common.cc \ + interpreter/interpreter_goto_table_impl.cc \ + interpreter/interpreter_switch_impl.cc \ jdwp/jdwp_event.cc \ jdwp/jdwp_expand_buf.cc \ jdwp/jdwp_handler.cc \ diff --git a/runtime/interpreter/interpreter.cc b/runtime/interpreter/interpreter.cc index 1677e801a6c..f35cfa3bca4 100644 --- a/runtime/interpreter/interpreter.cc +++ b/runtime/interpreter/interpreter.cc @@ -14,153 +14,11 @@ * limitations under the License. */ -#include "interpreter.h" - -#include - -#include "base/logging.h" -#include "class_linker-inl.h" -#include "common_throws.h" -#include "dex_file-inl.h" -#include "dex_instruction-inl.h" -#include "dex_instruction.h" -#include "entrypoints/entrypoint_utils.h" -#include "gc/accounting/card_table-inl.h" -#include "invoke_arg_array_builder.h" -#include "nth_caller_visitor.h" -#include "mirror/art_field-inl.h" -#include "mirror/art_method.h" -#include "mirror/art_method-inl.h" -#include "mirror/class.h" -#include "mirror/class-inl.h" -#include "mirror/object-inl.h" -#include "mirror/object_array-inl.h" -#include "object_utils.h" -#include "ScopedLocalRef.h" -#include "scoped_thread_state_change.h" -#include "thread.h" -#include "well_known_classes.h" - -using ::art::mirror::ArtField; -using ::art::mirror::ArtMethod; -using ::art::mirror::Array; -using ::art::mirror::BooleanArray; -using ::art::mirror::ByteArray; -using ::art::mirror::CharArray; -using ::art::mirror::Class; -using ::art::mirror::ClassLoader; -using ::art::mirror::IntArray; -using ::art::mirror::LongArray; -using ::art::mirror::Object; -using ::art::mirror::ObjectArray; -using ::art::mirror::ShortArray; -using ::art::mirror::String; -using ::art::mirror::Throwable; +#include "interpreter_common.h" namespace art { - namespace interpreter { -static const int32_t kMaxInt = std::numeric_limits::max(); -static const int32_t kMinInt = std::numeric_limits::min(); -static const int64_t kMaxLong = std::numeric_limits::max(); -static const int64_t kMinLong = std::numeric_limits::min(); - -static void UnstartedRuntimeInvoke(Thread* self, MethodHelper& mh, - const DexFile::CodeItem* code_item, ShadowFrame* shadow_frame, - JValue* result, size_t arg_offset) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - // In a runtime that's not started we intercept certain methods to avoid complicated dependency - // problems in core libraries. - std::string name(PrettyMethod(shadow_frame->GetMethod())); - if (name == "java.lang.Class java.lang.Class.forName(java.lang.String)") { - std::string descriptor(DotToDescriptor(shadow_frame->GetVRegReference(arg_offset)->AsString()->ToModifiedUtf8().c_str())); - ClassLoader* class_loader = NULL; // shadow_frame.GetMethod()->GetDeclaringClass()->GetClassLoader(); - Class* found = Runtime::Current()->GetClassLinker()->FindClass(descriptor.c_str(), - class_loader); - CHECK(found != NULL) << "Class.forName failed in un-started runtime for class: " - << PrettyDescriptor(descriptor); - result->SetL(found); - } else if (name == "java.lang.Object java.lang.Class.newInstance()") { - Class* klass = shadow_frame->GetVRegReference(arg_offset)->AsClass(); - ArtMethod* c = klass->FindDeclaredDirectMethod("", "()V"); - CHECK(c != NULL); - SirtRef obj(self, klass->AllocObject(self)); - CHECK(obj.get() != NULL); - EnterInterpreterFromInvoke(self, c, obj.get(), NULL, NULL); - result->SetL(obj.get()); - } else if (name == "java.lang.reflect.Field java.lang.Class.getDeclaredField(java.lang.String)") { - // Special managed code cut-out to allow field lookup in a un-started runtime that'd fail - // going the reflective Dex way. - Class* klass = shadow_frame->GetVRegReference(arg_offset)->AsClass(); - String* name = shadow_frame->GetVRegReference(arg_offset + 1)->AsString(); - ArtField* found = NULL; - FieldHelper fh; - ObjectArray* fields = klass->GetIFields(); - for (int32_t i = 0; i < fields->GetLength() && found == NULL; ++i) { - ArtField* f = fields->Get(i); - fh.ChangeField(f); - if (name->Equals(fh.GetName())) { - found = f; - } - } - if (found == NULL) { - fields = klass->GetSFields(); - for (int32_t i = 0; i < fields->GetLength() && found == NULL; ++i) { - ArtField* f = fields->Get(i); - fh.ChangeField(f); - if (name->Equals(fh.GetName())) { - found = f; - } - } - } - CHECK(found != NULL) - << "Failed to find field in Class.getDeclaredField in un-started runtime. name=" - << name->ToModifiedUtf8() << " class=" << PrettyDescriptor(klass); - // TODO: getDeclaredField calls GetType once the field is found to ensure a - // NoClassDefFoundError is thrown if the field's type cannot be resolved. - Class* jlr_Field = self->DecodeJObject(WellKnownClasses::java_lang_reflect_Field)->AsClass(); - SirtRef field(self, jlr_Field->AllocObject(self)); - CHECK(field.get() != NULL); - ArtMethod* c = jlr_Field->FindDeclaredDirectMethod("", "(Ljava/lang/reflect/ArtField;)V"); - uint32_t args[1]; - args[0] = reinterpret_cast(found); - EnterInterpreterFromInvoke(self, c, field.get(), args, NULL); - result->SetL(field.get()); - } else if (name == "void java.lang.System.arraycopy(java.lang.Object, int, java.lang.Object, int, int)" || - name == "void java.lang.System.arraycopy(char[], int, char[], int, int)") { - // Special case array copying without initializing System. - Class* ctype = shadow_frame->GetVRegReference(arg_offset)->GetClass()->GetComponentType(); - jint srcPos = shadow_frame->GetVReg(arg_offset + 1); - jint dstPos = shadow_frame->GetVReg(arg_offset + 3); - jint length = shadow_frame->GetVReg(arg_offset + 4); - if (!ctype->IsPrimitive()) { - ObjectArray* src = shadow_frame->GetVRegReference(arg_offset)->AsObjectArray(); - ObjectArray* dst = shadow_frame->GetVRegReference(arg_offset + 2)->AsObjectArray(); - for (jint i = 0; i < length; ++i) { - dst->Set(dstPos + i, src->Get(srcPos + i)); - } - } else if (ctype->IsPrimitiveChar()) { - CharArray* src = shadow_frame->GetVRegReference(arg_offset)->AsCharArray(); - CharArray* dst = shadow_frame->GetVRegReference(arg_offset + 2)->AsCharArray(); - for (jint i = 0; i < length; ++i) { - dst->Set(dstPos + i, src->Get(srcPos + i)); - } - } else if (ctype->IsPrimitiveInt()) { - IntArray* src = shadow_frame->GetVRegReference(arg_offset)->AsIntArray(); - IntArray* dst = shadow_frame->GetVRegReference(arg_offset + 2)->AsIntArray(); - for (jint i = 0; i < length; ++i) { - dst->Set(dstPos + i, src->Get(srcPos + i)); - } - } else { - UNIMPLEMENTED(FATAL) << "System.arraycopy of unexpected type: " << PrettyDescriptor(ctype); - } - } else { - // Not special, continue with regular interpreter execution. - artInterpreterToInterpreterBridge(self, mh, code_item, shadow_frame, result); - } -} - // Hand select a number of methods to be run in a not yet started runtime without using JNI. static void UnstartedRuntimeJni(Thread* self, ArtMethod* method, Object* receiver, uint32_t* args, JValue* result) @@ -406,2658 +264,40 @@ static void InterpreterJni(Thread* self, ArtMethod* method, StringPiece shorty, } } -static void DoMonitorEnter(Thread* self, Object* ref) NO_THREAD_SAFETY_ANALYSIS { - ref->MonitorEnter(self); -} - -static void DoMonitorExit(Thread* self, Object* ref) NO_THREAD_SAFETY_ANALYSIS { - ref->MonitorExit(self); -} - -// TODO: should be SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) which is failing due to template -// specialization. -template -static bool DoInvoke(Thread* self, ShadowFrame& shadow_frame, - const Instruction* inst, JValue* result) NO_THREAD_SAFETY_ANALYSIS; - -template -static bool DoInvoke(Thread* self, ShadowFrame& shadow_frame, - const Instruction* inst, JValue* result) { - uint32_t method_idx = (is_range) ? inst->VRegB_3rc() : inst->VRegB_35c(); - uint32_t vregC = (is_range) ? inst->VRegC_3rc() : inst->VRegC_35c(); - Object* receiver = (type == kStatic) ? NULL : shadow_frame.GetVRegReference(vregC); - ArtMethod* method = FindMethodFromCode(method_idx, receiver, shadow_frame.GetMethod(), self, - do_access_check, type); - if (UNLIKELY(method == NULL)) { - CHECK(self->IsExceptionPending()); - result->SetJ(0); - return false; - } else if (UNLIKELY(method->IsAbstract())) { - ThrowAbstractMethodError(method); - result->SetJ(0); - return false; - } - - MethodHelper mh(method); - const DexFile::CodeItem* code_item = mh.GetCodeItem(); - uint16_t num_regs; - uint16_t num_ins; - if (LIKELY(code_item != NULL)) { - num_regs = code_item->registers_size_; - num_ins = code_item->ins_size_; - } else { - DCHECK(method->IsNative() || method->IsProxyMethod()); - num_regs = num_ins = ArtMethod::NumArgRegisters(mh.GetShorty()); - if (!method->IsStatic()) { - num_regs++; - num_ins++; - } - } - - void* memory = alloca(ShadowFrame::ComputeSize(num_regs)); - ShadowFrame* new_shadow_frame(ShadowFrame::Create(num_regs, &shadow_frame, method, 0, memory)); - size_t cur_reg = num_regs - num_ins; - if (receiver != NULL) { - new_shadow_frame->SetVRegReference(cur_reg, receiver); - ++cur_reg; - } - - size_t arg_offset = (receiver == NULL) ? 0 : 1; - const char* shorty = mh.GetShorty(); - uint32_t arg[5]; - if (!is_range) { - inst->GetArgs(arg); - } - for (size_t shorty_pos = 0; cur_reg < num_regs; ++shorty_pos, cur_reg++, arg_offset++) { - DCHECK_LT(shorty_pos + 1, mh.GetShortyLength()); - size_t arg_pos = is_range ? vregC + arg_offset : arg[arg_offset]; - switch (shorty[shorty_pos + 1]) { - case 'L': { - Object* o = shadow_frame.GetVRegReference(arg_pos); - new_shadow_frame->SetVRegReference(cur_reg, o); - break; - } - case 'J': case 'D': { - uint64_t wide_value = (static_cast(shadow_frame.GetVReg(arg_pos + 1)) << 32) | - static_cast(shadow_frame.GetVReg(arg_pos)); - new_shadow_frame->SetVRegLong(cur_reg, wide_value); - cur_reg++; - arg_offset++; - break; - } - default: - new_shadow_frame->SetVReg(cur_reg, shadow_frame.GetVReg(arg_pos)); - break; - } - } - - if (LIKELY(Runtime::Current()->IsStarted())) { - (method->GetEntryPointFromInterpreter())(self, mh, code_item, new_shadow_frame, result); - } else { - UnstartedRuntimeInvoke(self, mh, code_item, new_shadow_frame, result, num_regs - num_ins); - } - return !self->IsExceptionPending(); -} - -// TODO: should be SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) which is failing due to template -// specialization. -template -static bool DoInvokeVirtualQuick(Thread* self, ShadowFrame& shadow_frame, - const Instruction* inst, JValue* result) - NO_THREAD_SAFETY_ANALYSIS; - -template -static bool DoInvokeVirtualQuick(Thread* self, ShadowFrame& shadow_frame, - const Instruction* inst, JValue* result) { - uint32_t vregC = (is_range) ? inst->VRegC_3rc() : inst->VRegC_35c(); - Object* receiver = shadow_frame.GetVRegReference(vregC); - if (UNLIKELY(receiver == NULL)) { - // We lost the reference to the method index so we cannot get a more - // precised exception message. - ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow()); - return false; - } - uint32_t vtable_idx = (is_range) ? inst->VRegB_3rc() : inst->VRegB_35c(); - // TODO: use ObjectArray::GetWithoutChecks ? - ArtMethod* method = receiver->GetClass()->GetVTable()->Get(vtable_idx); - if (UNLIKELY(method == NULL)) { - CHECK(self->IsExceptionPending()); - result->SetJ(0); - return false; - } else if (UNLIKELY(method->IsAbstract())) { - ThrowAbstractMethodError(method); - result->SetJ(0); - return false; - } - - MethodHelper mh(method); - const DexFile::CodeItem* code_item = mh.GetCodeItem(); - uint16_t num_regs; - uint16_t num_ins; - if (code_item != NULL) { - num_regs = code_item->registers_size_; - num_ins = code_item->ins_size_; - } else { - DCHECK(method->IsNative() || method->IsProxyMethod()); - num_regs = num_ins = ArtMethod::NumArgRegisters(mh.GetShorty()); - if (!method->IsStatic()) { - num_regs++; - num_ins++; - } - } - - void* memory = alloca(ShadowFrame::ComputeSize(num_regs)); - ShadowFrame* new_shadow_frame(ShadowFrame::Create(num_regs, &shadow_frame, - method, 0, memory)); - size_t cur_reg = num_regs - num_ins; - if (receiver != NULL) { - new_shadow_frame->SetVRegReference(cur_reg, receiver); - ++cur_reg; - } - - size_t arg_offset = (receiver == NULL) ? 0 : 1; - const char* shorty = mh.GetShorty(); - uint32_t arg[5]; - if (!is_range) { - inst->GetArgs(arg); - } - for (size_t shorty_pos = 0; cur_reg < num_regs; ++shorty_pos, cur_reg++, arg_offset++) { - DCHECK_LT(shorty_pos + 1, mh.GetShortyLength()); - size_t arg_pos = is_range ? vregC + arg_offset : arg[arg_offset]; - switch (shorty[shorty_pos + 1]) { - case 'L': { - Object* o = shadow_frame.GetVRegReference(arg_pos); - new_shadow_frame->SetVRegReference(cur_reg, o); - break; - } - case 'J': case 'D': { - uint64_t wide_value = (static_cast(shadow_frame.GetVReg(arg_pos + 1)) << 32) | - static_cast(shadow_frame.GetVReg(arg_pos)); - new_shadow_frame->SetVRegLong(cur_reg, wide_value); - cur_reg++; - arg_offset++; - break; - } - default: - new_shadow_frame->SetVReg(cur_reg, shadow_frame.GetVReg(arg_pos)); - break; - } - } - - if (LIKELY(Runtime::Current()->IsStarted())) { - (method->GetEntryPointFromInterpreter())(self, mh, code_item, new_shadow_frame, result); - } else { - UnstartedRuntimeInvoke(self, mh, code_item, new_shadow_frame, result, num_regs - num_ins); - } - return !self->IsExceptionPending(); -} - -// We use template functions to optimize compiler inlining process. Otherwise, -// some parts of the code (like a switch statement) which depend on a constant -// parameter would not be inlined while it should be. These constant parameters -// are now part of the template arguments. -// Note these template functions are static and inlined so they should not be -// part of the final object file. -// TODO: should be SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) which is failing due to template -// specialization. -template -static bool DoFieldGet(Thread* self, ShadowFrame& shadow_frame, - const Instruction* inst) - NO_THREAD_SAFETY_ANALYSIS ALWAYS_INLINE; - -template -static inline bool DoFieldGet(Thread* self, ShadowFrame& shadow_frame, - const Instruction* inst) { - bool is_static = (find_type == StaticObjectRead) || (find_type == StaticPrimitiveRead); - uint32_t field_idx = is_static ? inst->VRegB_21c() : inst->VRegC_22c(); - ArtField* f = FindFieldFromCode(field_idx, shadow_frame.GetMethod(), self, - find_type, Primitive::FieldSize(field_type), - do_access_check); - if (UNLIKELY(f == NULL)) { - CHECK(self->IsExceptionPending()); - return false; - } - Object* obj; - if (is_static) { - obj = f->GetDeclaringClass(); - } else { - obj = shadow_frame.GetVRegReference(inst->VRegB_22c()); - if (UNLIKELY(obj == NULL)) { - ThrowNullPointerExceptionForFieldAccess(shadow_frame.GetCurrentLocationForThrow(), f, true); - return false; - } - } - uint32_t vregA = is_static ? inst->VRegA_21c() : inst->VRegA_22c(); - switch (field_type) { - case Primitive::kPrimBoolean: - shadow_frame.SetVReg(vregA, f->GetBoolean(obj)); - break; - case Primitive::kPrimByte: - shadow_frame.SetVReg(vregA, f->GetByte(obj)); - break; - case Primitive::kPrimChar: - shadow_frame.SetVReg(vregA, f->GetChar(obj)); - break; - case Primitive::kPrimShort: - shadow_frame.SetVReg(vregA, f->GetShort(obj)); - break; - case Primitive::kPrimInt: - shadow_frame.SetVReg(vregA, f->GetInt(obj)); - break; - case Primitive::kPrimLong: - shadow_frame.SetVRegLong(vregA, f->GetLong(obj)); - break; - case Primitive::kPrimNot: - shadow_frame.SetVRegReference(vregA, f->GetObject(obj)); - break; - default: - LOG(FATAL) << "Unreachable: " << field_type; - } - return true; -} - -// TODO: should be SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) which is failing due to template -// specialization. -template -static bool DoIGetQuick(Thread* self, ShadowFrame& shadow_frame, - const Instruction* inst) - NO_THREAD_SAFETY_ANALYSIS ALWAYS_INLINE; - -template -static inline bool DoIGetQuick(Thread* self, ShadowFrame& shadow_frame, - const Instruction* inst) { - Object* obj = shadow_frame.GetVRegReference(inst->VRegB_22c()); - if (UNLIKELY(obj == NULL)) { - // We lost the reference to the field index so we cannot get a more - // precised exception message. - ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow()); - return false; - } - MemberOffset field_offset(inst->VRegC_22c()); - const bool is_volatile = false; // iget-x-quick only on non volatile fields. - const uint32_t vregA = inst->VRegA_22c(); - switch (field_type) { - case Primitive::kPrimInt: - shadow_frame.SetVReg(vregA, static_cast(obj->GetField32(field_offset, is_volatile))); - break; - case Primitive::kPrimLong: - shadow_frame.SetVRegLong(vregA, static_cast(obj->GetField64(field_offset, is_volatile))); - break; - case Primitive::kPrimNot: - shadow_frame.SetVRegReference(vregA, obj->GetFieldObject(field_offset, is_volatile)); - break; - default: - LOG(FATAL) << "Unreachable: " << field_type; - } - return true; -} - -// TODO: should be SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) which is failing due to template -// specialization. -template -static bool DoFieldPut(Thread* self, const ShadowFrame& shadow_frame, - const Instruction* inst) - NO_THREAD_SAFETY_ANALYSIS ALWAYS_INLINE; - -template -static inline bool DoFieldPut(Thread* self, const ShadowFrame& shadow_frame, - const Instruction* inst) { - bool is_static = (find_type == StaticObjectWrite) || (find_type == StaticPrimitiveWrite); - uint32_t field_idx = is_static ? inst->VRegB_21c() : inst->VRegC_22c(); - ArtField* f = FindFieldFromCode(field_idx, shadow_frame.GetMethod(), self, - find_type, Primitive::FieldSize(field_type), - do_access_check); - if (UNLIKELY(f == NULL)) { - CHECK(self->IsExceptionPending()); - return false; - } - Object* obj; - if (is_static) { - obj = f->GetDeclaringClass(); - } else { - obj = shadow_frame.GetVRegReference(inst->VRegB_22c()); - if (UNLIKELY(obj == NULL)) { - ThrowNullPointerExceptionForFieldAccess(shadow_frame.GetCurrentLocationForThrow(), - f, false); - return false; - } - } - uint32_t vregA = is_static ? inst->VRegA_21c() : inst->VRegA_22c(); - switch (field_type) { - case Primitive::kPrimBoolean: - f->SetBoolean(obj, shadow_frame.GetVReg(vregA)); - break; - case Primitive::kPrimByte: - f->SetByte(obj, shadow_frame.GetVReg(vregA)); - break; - case Primitive::kPrimChar: - f->SetChar(obj, shadow_frame.GetVReg(vregA)); - break; - case Primitive::kPrimShort: - f->SetShort(obj, shadow_frame.GetVReg(vregA)); - break; - case Primitive::kPrimInt: - f->SetInt(obj, shadow_frame.GetVReg(vregA)); - break; - case Primitive::kPrimLong: - f->SetLong(obj, shadow_frame.GetVRegLong(vregA)); - break; - case Primitive::kPrimNot: - f->SetObj(obj, shadow_frame.GetVRegReference(vregA)); - break; - default: - LOG(FATAL) << "Unreachable: " << field_type; - } - return true; -} - -// TODO: should be SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) which is failing due to template -// specialization. -template -static bool DoIPutQuick(Thread* self, ShadowFrame& shadow_frame, - const Instruction* inst) - NO_THREAD_SAFETY_ANALYSIS ALWAYS_INLINE; - -template -static inline bool DoIPutQuick(Thread* self, ShadowFrame& shadow_frame, - const Instruction* inst) { - Object* obj = shadow_frame.GetVRegReference(inst->VRegB_22c()); - if (UNLIKELY(obj == NULL)) { - // We lost the reference to the field index so we cannot get a more - // precised exception message. - ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow()); - return false; - } - MemberOffset field_offset(inst->VRegC_22c()); - const bool is_volatile = false; // iput-x-quick only on non volatile fields. - const uint32_t vregA = inst->VRegA_22c(); - switch (field_type) { - case Primitive::kPrimInt: - obj->SetField32(field_offset, shadow_frame.GetVReg(vregA), is_volatile); - break; - case Primitive::kPrimLong: - obj->SetField64(field_offset, shadow_frame.GetVRegLong(vregA), is_volatile); - break; - case Primitive::kPrimNot: - obj->SetFieldObject(field_offset, shadow_frame.GetVRegReference(vregA), is_volatile); - break; - default: - LOG(FATAL) << "Unreachable: " << field_type; - } - return true; -} - -static inline String* ResolveString(Thread* self, MethodHelper& mh, uint32_t string_idx) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - Class* java_lang_string_class = String::GetJavaLangString(); - if (UNLIKELY(!java_lang_string_class->IsInitialized())) { - ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); - if (UNLIKELY(!class_linker->EnsureInitialized(java_lang_string_class, - true, true))) { - DCHECK(self->IsExceptionPending()); - return NULL; - } - } - return mh.ResolveString(string_idx); -} - -static inline bool DoIntDivide(ShadowFrame& shadow_frame, size_t result_reg, - int32_t dividend, int32_t divisor) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - if (UNLIKELY(divisor == 0)) { - ThrowArithmeticExceptionDivideByZero(); - return false; - } - if (UNLIKELY(dividend == kMinInt && divisor == -1)) { - shadow_frame.SetVReg(result_reg, kMinInt); - } else { - shadow_frame.SetVReg(result_reg, dividend / divisor); - } - return true; -} - -static inline bool DoIntRemainder(ShadowFrame& shadow_frame, size_t result_reg, - int32_t dividend, int32_t divisor) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - if (UNLIKELY(divisor == 0)) { - ThrowArithmeticExceptionDivideByZero(); - return false; - } - if (UNLIKELY(dividend == kMinInt && divisor == -1)) { - shadow_frame.SetVReg(result_reg, 0); - } else { - shadow_frame.SetVReg(result_reg, dividend % divisor); - } - return true; -} +enum InterpreterImplKind { + kSwitchImpl, // switch-based interpreter implementation. + kComputedGotoImplKind // computed-goto-based interpreter implementation. +}; -static inline bool DoLongDivide(ShadowFrame& shadow_frame, size_t result_reg, - int64_t dividend, int64_t divisor) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - if (UNLIKELY(divisor == 0)) { - ThrowArithmeticExceptionDivideByZero(); - return false; - } - if (UNLIKELY(dividend == kMinLong && divisor == -1)) { - shadow_frame.SetVRegLong(result_reg, kMinLong); - } else { - shadow_frame.SetVRegLong(result_reg, dividend / divisor); - } - return true; -} +static const InterpreterImplKind kInterpreterImplKind = kSwitchImpl; -static inline bool DoLongRemainder(ShadowFrame& shadow_frame, size_t result_reg, - int64_t dividend, int64_t divisor) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - if (UNLIKELY(divisor == 0)) { - ThrowArithmeticExceptionDivideByZero(); - return false; - } - if (UNLIKELY(dividend == kMinLong && divisor == -1)) { - shadow_frame.SetVRegLong(result_reg, 0); - } else { - shadow_frame.SetVRegLong(result_reg, dividend % divisor); - } - return true; -} +static JValue Execute(Thread* self, MethodHelper& mh, const DexFile::CodeItem* code_item, + ShadowFrame& shadow_frame, JValue result_register) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); -// TODO: should be SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) which is failing due to template -// specialization. -// Returns true on success, otherwise throws an exception and returns false. -template -static bool DoFilledNewArray(const Instruction* inst, const ShadowFrame& shadow_frame, - Thread* self, JValue* result) - NO_THREAD_SAFETY_ANALYSIS ALWAYS_INLINE; +static inline JValue Execute(Thread* self, MethodHelper& mh, const DexFile::CodeItem* code_item, + ShadowFrame& shadow_frame, JValue result_register) { + DCHECK(shadow_frame.GetMethod() == mh.GetMethod() || + shadow_frame.GetMethod()->GetDeclaringClass()->IsProxyClass()); + DCHECK(!shadow_frame.GetMethod()->IsAbstract()); + DCHECK(!shadow_frame.GetMethod()->IsNative()); -template -static inline bool DoFilledNewArray(const Instruction* inst, - const ShadowFrame& shadow_frame, - Thread* self, JValue* result) { - DCHECK(inst->Opcode() == Instruction::FILLED_NEW_ARRAY || - inst->Opcode() == Instruction::FILLED_NEW_ARRAY_RANGE); - const int32_t length = is_range ? inst->VRegA_3rc() : inst->VRegA_35c(); - if (!is_range) { - // Checks FILLED_NEW_ARRAY's length does not exceed 5 arguments. - CHECK_LE(length, 5); - } - if (UNLIKELY(length < 0)) { - ThrowNegativeArraySizeException(length); - return false; - } - uint16_t type_idx = is_range ? inst->VRegB_3rc() : inst->VRegB_35c(); - Class* arrayClass = ResolveVerifyAndClinit(type_idx, shadow_frame.GetMethod(), - self, false, do_access_check); - if (UNLIKELY(arrayClass == NULL)) { - DCHECK(self->IsExceptionPending()); - return false; - } - CHECK(arrayClass->IsArrayClass()); - Class* componentClass = arrayClass->GetComponentType(); - if (UNLIKELY(componentClass->IsPrimitive() && !componentClass->IsPrimitiveInt())) { - if (componentClass->IsPrimitiveLong() || componentClass->IsPrimitiveDouble()) { - ThrowRuntimeException("Bad filled array request for type %s", - PrettyDescriptor(componentClass).c_str()); + if (LIKELY(shadow_frame.GetMethod()->IsPreverified())) { + // Enter the "without access check" interpreter. + if (kInterpreterImplKind == kSwitchImpl) { + return ExecuteSwitchImpl(self, mh, code_item, shadow_frame, result_register); } else { - self->ThrowNewExceptionF(shadow_frame.GetCurrentLocationForThrow(), - "Ljava/lang/InternalError;", - "Found type %s; filled-new-array not implemented for anything but \'int\'", - PrettyDescriptor(componentClass).c_str()); - } - return false; - } - Object* newArray = Array::Alloc(self, arrayClass, length); - if (UNLIKELY(newArray == NULL)) { - DCHECK(self->IsExceptionPending()); - return false; - } - if (is_range) { - uint32_t vregC = inst->VRegC_3rc(); - const bool is_primitive_int_component = componentClass->IsPrimitiveInt(); - for (int32_t i = 0; i < length; ++i) { - if (is_primitive_int_component) { - newArray->AsIntArray()->Set(i, shadow_frame.GetVReg(vregC + i)); - } else { - newArray->AsObjectArray()->Set(i, shadow_frame.GetVRegReference(vregC + i)); - } + DCHECK_EQ(kInterpreterImplKind, kComputedGotoImplKind); + return ExecuteGotoImpl(self, mh, code_item, shadow_frame, result_register); } } else { - uint32_t arg[5]; - inst->GetArgs(arg); - const bool is_primitive_int_component = componentClass->IsPrimitiveInt(); - for (int32_t i = 0; i < length; ++i) { - if (is_primitive_int_component) { - newArray->AsIntArray()->Set(i, shadow_frame.GetVReg(arg[i])); - } else { - newArray->AsObjectArray()->Set(i, shadow_frame.GetVRegReference(arg[i])); - } - } - } - - result->SetL(newArray); - return true; -} - -static inline const Instruction* DoSparseSwitch(const Instruction* inst, - const ShadowFrame& shadow_frame) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - DCHECK(inst->Opcode() == Instruction::SPARSE_SWITCH); - const uint16_t* switch_data = reinterpret_cast(inst) + inst->VRegB_31t(); - int32_t test_val = shadow_frame.GetVReg(inst->VRegA_31t()); - DCHECK_EQ(switch_data[0], static_cast(Instruction::kSparseSwitchSignature)); - uint16_t size = switch_data[1]; - DCHECK_GT(size, 0); - const int32_t* keys = reinterpret_cast(&switch_data[2]); - DCHECK(IsAligned<4>(keys)); - const int32_t* entries = keys + size; - DCHECK(IsAligned<4>(entries)); - int lo = 0; - int hi = size - 1; - while (lo <= hi) { - int mid = (lo + hi) / 2; - int32_t foundVal = keys[mid]; - if (test_val < foundVal) { - hi = mid - 1; - } else if (test_val > foundVal) { - lo = mid + 1; + // Enter the "with access check" interpreter. + if (kInterpreterImplKind == kSwitchImpl) { + return ExecuteSwitchImpl(self, mh, code_item, shadow_frame, result_register); } else { - return inst->RelativeAt(entries[mid]); - } - } - return inst->Next_3xx(); -} - -static inline const Instruction* FindNextInstructionFollowingException(Thread* self, - ShadowFrame& shadow_frame, - uint32_t dex_pc, - const uint16_t* insns, - SirtRef& this_object_ref, - instrumentation::Instrumentation* instrumentation) - ALWAYS_INLINE; - -static inline const Instruction* FindNextInstructionFollowingException(Thread* self, - ShadowFrame& shadow_frame, - uint32_t dex_pc, - const uint16_t* insns, - SirtRef& this_object_ref, - instrumentation::Instrumentation* instrumentation) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - self->VerifyStack(); - ThrowLocation throw_location; - mirror::Throwable* exception = self->GetException(&throw_location); - bool clear_exception; - uint32_t found_dex_pc = shadow_frame.GetMethod()->FindCatchBlock(exception->GetClass(), dex_pc, - &clear_exception); - if (found_dex_pc == DexFile::kDexNoIndex) { - instrumentation->MethodUnwindEvent(self, this_object_ref.get(), - shadow_frame.GetMethod(), dex_pc); - return NULL; - } else { - instrumentation->ExceptionCaughtEvent(self, throw_location, - shadow_frame.GetMethod(), - found_dex_pc, exception); - if (clear_exception) { - self->ClearException(); - } - return Instruction::At(insns + found_dex_pc); - } -} - -#define HANDLE_PENDING_EXCEPTION() \ - CHECK(self->IsExceptionPending()); \ - inst = FindNextInstructionFollowingException(self, shadow_frame, inst->GetDexPc(insns), insns, \ - this_object_ref, instrumentation); \ - if (inst == NULL) { \ - return JValue(); /* Handled in caller. */ \ - } - -#define POSSIBLY_HANDLE_PENDING_EXCEPTION(is_exception_pending, next_function) \ - if (UNLIKELY(is_exception_pending)) { \ - HANDLE_PENDING_EXCEPTION(); \ - } else { \ - inst = inst->next_function(); \ - } - -static void UnexpectedOpcode(const Instruction* inst, MethodHelper& mh) - __attribute__((cold, noreturn, noinline)); - -static void UnexpectedOpcode(const Instruction* inst, MethodHelper& mh) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - LOG(FATAL) << "Unexpected instruction: " << inst->DumpString(&mh.GetDexFile()); - exit(0); // Unreachable, keep GCC happy. -} - -// Code to run before each dex instruction. -#define PREAMBLE() - -// TODO: should be SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) which is failing due to template -// specialization. -template -static JValue ExecuteImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* code_item, - ShadowFrame& shadow_frame, JValue result_register) - NO_THREAD_SAFETY_ANALYSIS __attribute__((hot)); - -template -static JValue ExecuteImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* code_item, - ShadowFrame& shadow_frame, JValue result_register) { - if (UNLIKELY(!shadow_frame.HasReferenceArray())) { - LOG(FATAL) << "Invalid shadow frame for interpreter use"; - return JValue(); - } - self->VerifyStack(); - instrumentation::Instrumentation* const instrumentation = Runtime::Current()->GetInstrumentation(); - - // As the 'this' object won't change during the execution of current code, we - // want to cache it in local variables. Nevertheless, in order to let the - // garbage collector access it, we store it into sirt references. - SirtRef this_object_ref(self, shadow_frame.GetThisObject(code_item->ins_size_)); - - uint32_t dex_pc = shadow_frame.GetDexPC(); - if (LIKELY(dex_pc == 0)) { // We are entering the method as opposed to deoptimizing.. - if (UNLIKELY(instrumentation->HasMethodEntryListeners())) { - instrumentation->MethodEnterEvent(self, this_object_ref.get(), - shadow_frame.GetMethod(), 0); - } - } - const uint16_t* const insns = code_item->insns_; - const Instruction* inst = Instruction::At(insns + dex_pc); - while (true) { - dex_pc = inst->GetDexPc(insns); - shadow_frame.SetDexPC(dex_pc); - if (UNLIKELY(self->TestAllFlags())) { - CheckSuspend(self); + DCHECK_EQ(kInterpreterImplKind, kComputedGotoImplKind); + return ExecuteGotoImpl(self, mh, code_item, shadow_frame, result_register); } - if (UNLIKELY(instrumentation->HasDexPcListeners())) { - instrumentation->DexPcMovedEvent(self, this_object_ref.get(), - shadow_frame.GetMethod(), dex_pc); - } - const bool kTracing = false; - if (kTracing) { -#define TRACE_LOG std::cerr - TRACE_LOG << PrettyMethod(shadow_frame.GetMethod()) - << StringPrintf("\n0x%x: ", dex_pc) - << inst->DumpString(&mh.GetDexFile()) << "\n"; - for (size_t i = 0; i < shadow_frame.NumberOfVRegs(); ++i) { - uint32_t raw_value = shadow_frame.GetVReg(i); - Object* ref_value = shadow_frame.GetVRegReference(i); - TRACE_LOG << StringPrintf(" vreg%d=0x%08X", i, raw_value); - if (ref_value != NULL) { - if (ref_value->GetClass()->IsStringClass() && - ref_value->AsString()->GetCharArray() != NULL) { - TRACE_LOG << "/java.lang.String \"" << ref_value->AsString()->ToModifiedUtf8() << "\""; - } else { - TRACE_LOG << "/" << PrettyTypeOf(ref_value); - } - } - } - TRACE_LOG << "\n"; -#undef TRACE_LOG - } - switch (inst->Opcode()) { - case Instruction::NOP: - PREAMBLE(); - inst = inst->Next_1xx(); - break; - case Instruction::MOVE: - PREAMBLE(); - shadow_frame.SetVReg(inst->VRegA_12x(), - shadow_frame.GetVReg(inst->VRegB_12x())); - inst = inst->Next_1xx(); - break; - case Instruction::MOVE_FROM16: - PREAMBLE(); - shadow_frame.SetVReg(inst->VRegA_22x(), - 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(), - shadow_frame.GetVRegLong(inst->VRegB_12x())); - inst = inst->Next_1xx(); - break; - case Instruction::MOVE_WIDE_FROM16: - PREAMBLE(); - shadow_frame.SetVRegLong(inst->VRegA_22x(), - 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(), - shadow_frame.GetVRegReference(inst->VRegB_12x())); - inst = inst->Next_1xx(); - break; - case Instruction::MOVE_OBJECT_FROM16: - PREAMBLE(); - shadow_frame.SetVRegReference(inst->VRegA_22x(), - 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(), result_register.GetI()); - inst = inst->Next_1xx(); - break; - case Instruction::MOVE_RESULT_WIDE: - PREAMBLE(); - shadow_frame.SetVRegLong(inst->VRegA_11x(), result_register.GetJ()); - inst = inst->Next_1xx(); - break; - case Instruction::MOVE_RESULT_OBJECT: - PREAMBLE(); - shadow_frame.SetVRegReference(inst->VRegA_11x(), result_register.GetL()); - inst = inst->Next_1xx(); - break; - case Instruction::MOVE_EXCEPTION: { - PREAMBLE(); - Throwable* exception = self->GetException(NULL); - self->ClearException(); - shadow_frame.SetVRegReference(inst->VRegA_11x(), exception); - inst = inst->Next_1xx(); - break; - } - case Instruction::RETURN_VOID: { - PREAMBLE(); - JValue result; - if (UNLIKELY(instrumentation->HasMethodExitListeners())) { - instrumentation->MethodExitEvent(self, this_object_ref.get(), - shadow_frame.GetMethod(), inst->GetDexPc(insns), - result); - } - return result; - } - case Instruction::RETURN_VOID_BARRIER: { - PREAMBLE(); - ANDROID_MEMBAR_STORE(); - JValue result; - if (UNLIKELY(instrumentation->HasMethodExitListeners())) { - instrumentation->MethodExitEvent(self, this_object_ref.get(), - shadow_frame.GetMethod(), inst->GetDexPc(insns), - result); - } - return result; - } - case Instruction::RETURN: { - PREAMBLE(); - JValue result; - result.SetJ(0); - result.SetI(shadow_frame.GetVReg(inst->VRegA_11x())); - if (UNLIKELY(instrumentation->HasMethodExitListeners())) { - instrumentation->MethodExitEvent(self, this_object_ref.get(), - shadow_frame.GetMethod(), inst->GetDexPc(insns), - result); - } - return result; - } - case Instruction::RETURN_WIDE: { - PREAMBLE(); - JValue result; - result.SetJ(shadow_frame.GetVRegLong(inst->VRegA_11x())); - if (UNLIKELY(instrumentation->HasMethodExitListeners())) { - instrumentation->MethodExitEvent(self, this_object_ref.get(), - shadow_frame.GetMethod(), inst->GetDexPc(insns), - result); - } - return result; - } - case Instruction::RETURN_OBJECT: { - PREAMBLE(); - JValue result; - result.SetJ(0); - result.SetL(shadow_frame.GetVRegReference(inst->VRegA_11x())); - if (UNLIKELY(instrumentation->HasMethodExitListeners())) { - instrumentation->MethodExitEvent(self, this_object_ref.get(), - shadow_frame.GetMethod(), inst->GetDexPc(insns), - result); - } - return result; - } - case Instruction::CONST_4: { - PREAMBLE(); - uint4_t dst = inst->VRegA_11n(); - int4_t val = inst->VRegB_11n(); - shadow_frame.SetVReg(dst, val); - if (val == 0) { - shadow_frame.SetVRegReference(dst, NULL); - } - inst = inst->Next_1xx(); - break; - } - case Instruction::CONST_16: { - PREAMBLE(); - uint8_t dst = inst->VRegA_21s(); - int16_t val = inst->VRegB_21s(); - shadow_frame.SetVReg(dst, val); - if (val == 0) { - shadow_frame.SetVRegReference(dst, NULL); - } - inst = inst->Next_2xx(); - break; - } - case Instruction::CONST: { - PREAMBLE(); - uint8_t dst = inst->VRegA_31i(); - int32_t val = inst->VRegB_31i(); - shadow_frame.SetVReg(dst, val); - if (val == 0) { - shadow_frame.SetVRegReference(dst, NULL); - } - inst = inst->Next_3xx(); - break; - } - case Instruction::CONST_HIGH16: { - PREAMBLE(); - uint8_t dst = inst->VRegA_21h(); - int32_t val = static_cast(inst->VRegB_21h() << 16); - shadow_frame.SetVReg(dst, val); - if (val == 0) { - shadow_frame.SetVRegReference(dst, NULL); - } - inst = inst->Next_2xx(); - break; - } - case Instruction::CONST_WIDE_16: - PREAMBLE(); - shadow_frame.SetVRegLong(inst->VRegA_21s(), inst->VRegB_21s()); - inst = inst->Next_2xx(); - break; - case Instruction::CONST_WIDE_32: - PREAMBLE(); - shadow_frame.SetVRegLong(inst->VRegA_31i(), inst->VRegB_31i()); - inst = inst->Next_3xx(); - break; - case Instruction::CONST_WIDE: - PREAMBLE(); - shadow_frame.SetVRegLong(inst->VRegA_51l(), inst->VRegB_51l()); - inst = inst->Next_51l(); - break; - case Instruction::CONST_WIDE_HIGH16: - shadow_frame.SetVRegLong(inst->VRegA_21h(), - static_cast(inst->VRegB_21h()) << 48); - inst = inst->Next_2xx(); - break; - case Instruction::CONST_STRING: { - PREAMBLE(); - String* s = ResolveString(self, mh, inst->VRegB_21c()); - if (UNLIKELY(s == NULL)) { - HANDLE_PENDING_EXCEPTION(); - } else { - shadow_frame.SetVRegReference(inst->VRegA_21c(), s); - inst = inst->Next_2xx(); - } - break; - } - case Instruction::CONST_STRING_JUMBO: { - PREAMBLE(); - String* s = ResolveString(self, mh, inst->VRegB_31c()); - if (UNLIKELY(s == NULL)) { - HANDLE_PENDING_EXCEPTION(); - } else { - shadow_frame.SetVRegReference(inst->VRegA_31c(), s); - inst = inst->Next_3xx(); - } - break; - } - case Instruction::CONST_CLASS: { - PREAMBLE(); - Class* c = ResolveVerifyAndClinit(inst->VRegB_21c(), shadow_frame.GetMethod(), - self, false, do_access_check); - if (UNLIKELY(c == NULL)) { - HANDLE_PENDING_EXCEPTION(); - } else { - shadow_frame.SetVRegReference(inst->VRegA_21c(), c); - inst = inst->Next_2xx(); - } - break; - } - case Instruction::MONITOR_ENTER: { - PREAMBLE(); - Object* obj = shadow_frame.GetVRegReference(inst->VRegA_11x()); - if (UNLIKELY(obj == NULL)) { - ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow()); - HANDLE_PENDING_EXCEPTION(); - } else { - DoMonitorEnter(self, obj); - POSSIBLY_HANDLE_PENDING_EXCEPTION(self->IsExceptionPending(), Next_1xx); - } - break; - } - case Instruction::MONITOR_EXIT: { - PREAMBLE(); - Object* obj = shadow_frame.GetVRegReference(inst->VRegA_11x()); - if (UNLIKELY(obj == NULL)) { - ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow()); - HANDLE_PENDING_EXCEPTION(); - } else { - DoMonitorExit(self, obj); - POSSIBLY_HANDLE_PENDING_EXCEPTION(self->IsExceptionPending(), Next_1xx); - } - break; - } - case Instruction::CHECK_CAST: { - PREAMBLE(); - Class* c = ResolveVerifyAndClinit(inst->VRegB_21c(), shadow_frame.GetMethod(), - self, false, do_access_check); - if (UNLIKELY(c == NULL)) { - HANDLE_PENDING_EXCEPTION(); - } else { - Object* obj = shadow_frame.GetVRegReference(inst->VRegA_21c()); - if (UNLIKELY(obj != NULL && !obj->InstanceOf(c))) { - ThrowClassCastException(c, obj->GetClass()); - HANDLE_PENDING_EXCEPTION(); - } else { - inst = inst->Next_2xx(); - } - } - break; - } - case Instruction::INSTANCE_OF: { - PREAMBLE(); - Class* c = ResolveVerifyAndClinit(inst->VRegC_22c(), shadow_frame.GetMethod(), - self, false, do_access_check); - if (UNLIKELY(c == NULL)) { - HANDLE_PENDING_EXCEPTION(); - } else { - Object* obj = shadow_frame.GetVRegReference(inst->VRegB_22c()); - shadow_frame.SetVReg(inst->VRegA_22c(), (obj != NULL && obj->InstanceOf(c)) ? 1 : 0); - inst = inst->Next_2xx(); - } - break; - } - case Instruction::ARRAY_LENGTH: { - PREAMBLE(); - Object* array = shadow_frame.GetVRegReference(inst->VRegB_12x()); - if (UNLIKELY(array == NULL)) { - ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow()); - HANDLE_PENDING_EXCEPTION(); - } else { - shadow_frame.SetVReg(inst->VRegA_12x(), array->AsArray()->GetLength()); - inst = inst->Next_1xx(); - } - break; - } - case Instruction::NEW_INSTANCE: { - PREAMBLE(); - Object* obj = AllocObjectFromCode(inst->VRegB_21c(), shadow_frame.GetMethod(), - self, do_access_check); - if (UNLIKELY(obj == NULL)) { - HANDLE_PENDING_EXCEPTION(); - } else { - shadow_frame.SetVRegReference(inst->VRegA_21c(), obj); - inst = inst->Next_2xx(); - } - break; - } - case Instruction::NEW_ARRAY: { - PREAMBLE(); - int32_t length = shadow_frame.GetVReg(inst->VRegB_22c()); - Object* obj = AllocArrayFromCode(inst->VRegC_22c(), shadow_frame.GetMethod(), - length, self, do_access_check); - if (UNLIKELY(obj == NULL)) { - HANDLE_PENDING_EXCEPTION(); - } else { - shadow_frame.SetVRegReference(inst->VRegA_22c(), obj); - inst = inst->Next_2xx(); - } - break; - } - case Instruction::FILLED_NEW_ARRAY: { - PREAMBLE(); - bool success = DoFilledNewArray(inst, shadow_frame, - self, &result_register); - POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_3xx); - break; - } - case Instruction::FILLED_NEW_ARRAY_RANGE: { - PREAMBLE(); - bool success = DoFilledNewArray(inst, shadow_frame, - self, &result_register); - POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_3xx); - break; - } - case Instruction::FILL_ARRAY_DATA: { - PREAMBLE(); - Object* obj = shadow_frame.GetVRegReference(inst->VRegA_31t()); - if (UNLIKELY(obj == NULL)) { - ThrowNullPointerException(NULL, "null array in FILL_ARRAY_DATA"); - HANDLE_PENDING_EXCEPTION(); - break; - } - Array* array = obj->AsArray(); - DCHECK(array->IsArrayInstance() && !array->IsObjectArray()); - const uint16_t* payload_addr = reinterpret_cast(inst) + inst->VRegB_31t(); - const Instruction::ArrayDataPayload* payload = - reinterpret_cast(payload_addr); - if (UNLIKELY(static_cast(payload->element_count) > array->GetLength())) { - self->ThrowNewExceptionF(shadow_frame.GetCurrentLocationForThrow(), - "Ljava/lang/ArrayIndexOutOfBoundsException;", - "failed FILL_ARRAY_DATA; length=%d, index=%d", - array->GetLength(), payload->element_count); - HANDLE_PENDING_EXCEPTION(); - break; - } - uint32_t size_in_bytes = payload->element_count * payload->element_width; - memcpy(array->GetRawData(payload->element_width), payload->data, size_in_bytes); - inst = inst->Next_3xx(); - break; - } - case Instruction::THROW: { - PREAMBLE(); - Object* exception = shadow_frame.GetVRegReference(inst->VRegA_11x()); - if (UNLIKELY(exception == NULL)) { - ThrowNullPointerException(NULL, "throw with null exception"); - } else { - self->SetException(shadow_frame.GetCurrentLocationForThrow(), exception->AsThrowable()); - } - HANDLE_PENDING_EXCEPTION(); - break; - } - case Instruction::GOTO: { - PREAMBLE(); - inst = inst->RelativeAt(inst->VRegA_10t()); - break; - } - case Instruction::GOTO_16: { - PREAMBLE(); - inst = inst->RelativeAt(inst->VRegA_20t()); - break; - } - case Instruction::GOTO_32: { - PREAMBLE(); - inst = inst->RelativeAt(inst->VRegA_30t()); - break; - } - case Instruction::PACKED_SWITCH: { - PREAMBLE(); - const uint16_t* switch_data = reinterpret_cast(inst) + inst->VRegB_31t(); - int32_t test_val = shadow_frame.GetVReg(inst->VRegA_31t()); - DCHECK_EQ(switch_data[0], static_cast(Instruction::kPackedSwitchSignature)); - uint16_t size = switch_data[1]; - DCHECK_GT(size, 0); - const int32_t* keys = reinterpret_cast(&switch_data[2]); - DCHECK(IsAligned<4>(keys)); - int32_t first_key = keys[0]; - const int32_t* targets = reinterpret_cast(&switch_data[4]); - DCHECK(IsAligned<4>(targets)); - int32_t index = test_val - first_key; - if (index >= 0 && index < size) { - inst = inst->RelativeAt(targets[index]); - } else { - inst = inst->Next_3xx(); - } - break; - } - case Instruction::SPARSE_SWITCH: { - PREAMBLE(); - inst = DoSparseSwitch(inst, shadow_frame); - break; - } - 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(), 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(), 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(), 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(), result); - inst = inst->Next_2xx(); - break; - } - 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(), result); - inst = inst->Next_2xx(); - break; - } - case Instruction::IF_EQ: { - PREAMBLE(); - if (shadow_frame.GetVReg(inst->VRegA_22t()) == shadow_frame.GetVReg(inst->VRegB_22t())) { - inst = inst->RelativeAt(inst->VRegC_22t()); - } else { - inst = inst->Next_2xx(); - } - break; - } - case Instruction::IF_NE: { - PREAMBLE(); - if (shadow_frame.GetVReg(inst->VRegA_22t()) != shadow_frame.GetVReg(inst->VRegB_22t())) { - inst = inst->RelativeAt(inst->VRegC_22t()); - } else { - inst = inst->Next_2xx(); - } - break; - } - case Instruction::IF_LT: { - PREAMBLE(); - if (shadow_frame.GetVReg(inst->VRegA_22t()) < shadow_frame.GetVReg(inst->VRegB_22t())) { - inst = inst->RelativeAt(inst->VRegC_22t()); - } else { - inst = inst->Next_2xx(); - } - break; - } - case Instruction::IF_GE: { - PREAMBLE(); - if (shadow_frame.GetVReg(inst->VRegA_22t()) >= shadow_frame.GetVReg(inst->VRegB_22t())) { - inst = inst->RelativeAt(inst->VRegC_22t()); - } else { - inst = inst->Next_2xx(); - } - break; - } - case Instruction::IF_GT: { - PREAMBLE(); - if (shadow_frame.GetVReg(inst->VRegA_22t()) > shadow_frame.GetVReg(inst->VRegB_22t())) { - inst = inst->RelativeAt(inst->VRegC_22t()); - } else { - inst = inst->Next_2xx(); - } - break; - } - case Instruction::IF_LE: { - PREAMBLE(); - if (shadow_frame.GetVReg(inst->VRegA_22t()) <= shadow_frame.GetVReg(inst->VRegB_22t())) { - inst = inst->RelativeAt(inst->VRegC_22t()); - } else { - inst = inst->Next_2xx(); - } - break; - } - case Instruction::IF_EQZ: { - PREAMBLE(); - if (shadow_frame.GetVReg(inst->VRegA_21t()) == 0) { - inst = inst->RelativeAt(inst->VRegB_21t()); - } else { - inst = inst->Next_2xx(); - } - break; - } - case Instruction::IF_NEZ: { - PREAMBLE(); - if (shadow_frame.GetVReg(inst->VRegA_21t()) != 0) { - inst = inst->RelativeAt(inst->VRegB_21t()); - } else { - inst = inst->Next_2xx(); - } - break; - } - case Instruction::IF_LTZ: { - PREAMBLE(); - if (shadow_frame.GetVReg(inst->VRegA_21t()) < 0) { - inst = inst->RelativeAt(inst->VRegB_21t()); - } else { - inst = inst->Next_2xx(); - } - break; - } - case Instruction::IF_GEZ: { - PREAMBLE(); - if (shadow_frame.GetVReg(inst->VRegA_21t()) >= 0) { - inst = inst->RelativeAt(inst->VRegB_21t()); - } else { - inst = inst->Next_2xx(); - } - break; - } - case Instruction::IF_GTZ: { - PREAMBLE(); - if (shadow_frame.GetVReg(inst->VRegA_21t()) > 0) { - inst = inst->RelativeAt(inst->VRegB_21t()); - } else { - inst = inst->Next_2xx(); - } - break; - } - case Instruction::IF_LEZ: { - PREAMBLE(); - if (shadow_frame.GetVReg(inst->VRegA_21t()) <= 0) { - inst = inst->RelativeAt(inst->VRegB_21t()); - } else { - inst = inst->Next_2xx(); - } - break; - } - case Instruction::AGET_BOOLEAN: { - PREAMBLE(); - Object* a = shadow_frame.GetVRegReference(inst->VRegB_23x()); - if (UNLIKELY(a == NULL)) { - ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow()); - HANDLE_PENDING_EXCEPTION(); - break; - } - int32_t index = shadow_frame.GetVReg(inst->VRegC_23x()); - BooleanArray* array = a->AsBooleanArray(); - if (LIKELY(array->IsValidIndex(index))) { - shadow_frame.SetVReg(inst->VRegA_23x(), array->GetData()[index]); - inst = inst->Next_2xx(); - } else { - HANDLE_PENDING_EXCEPTION(); - } - break; - } - case Instruction::AGET_BYTE: { - PREAMBLE(); - Object* a = shadow_frame.GetVRegReference(inst->VRegB_23x()); - if (UNLIKELY(a == NULL)) { - ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow()); - HANDLE_PENDING_EXCEPTION(); - break; - } - int32_t index = shadow_frame.GetVReg(inst->VRegC_23x()); - ByteArray* array = a->AsByteArray(); - if (LIKELY(array->IsValidIndex(index))) { - shadow_frame.SetVReg(inst->VRegA_23x(), array->GetData()[index]); - inst = inst->Next_2xx(); - } else { - HANDLE_PENDING_EXCEPTION(); - } - break; - } - case Instruction::AGET_CHAR: { - PREAMBLE(); - Object* a = shadow_frame.GetVRegReference(inst->VRegB_23x()); - if (UNLIKELY(a == NULL)) { - ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow()); - HANDLE_PENDING_EXCEPTION(); - break; - } - int32_t index = shadow_frame.GetVReg(inst->VRegC_23x()); - CharArray* array = a->AsCharArray(); - if (LIKELY(array->IsValidIndex(index))) { - shadow_frame.SetVReg(inst->VRegA_23x(), array->GetData()[index]); - inst = inst->Next_2xx(); - } else { - HANDLE_PENDING_EXCEPTION(); - } - break; - } - case Instruction::AGET_SHORT: { - PREAMBLE(); - Object* a = shadow_frame.GetVRegReference(inst->VRegB_23x()); - if (UNLIKELY(a == NULL)) { - ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow()); - HANDLE_PENDING_EXCEPTION(); - break; - } - int32_t index = shadow_frame.GetVReg(inst->VRegC_23x()); - ShortArray* array = a->AsShortArray(); - if (LIKELY(array->IsValidIndex(index))) { - shadow_frame.SetVReg(inst->VRegA_23x(), array->GetData()[index]); - inst = inst->Next_2xx(); - } else { - HANDLE_PENDING_EXCEPTION(); - } - break; - } - case Instruction::AGET: { - PREAMBLE(); - Object* a = shadow_frame.GetVRegReference(inst->VRegB_23x()); - if (UNLIKELY(a == NULL)) { - ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow()); - HANDLE_PENDING_EXCEPTION(); - break; - } - int32_t index = shadow_frame.GetVReg(inst->VRegC_23x()); - IntArray* array = a->AsIntArray(); - if (LIKELY(array->IsValidIndex(index))) { - shadow_frame.SetVReg(inst->VRegA_23x(), array->GetData()[index]); - inst = inst->Next_2xx(); - } else { - HANDLE_PENDING_EXCEPTION(); - } - break; - } - case Instruction::AGET_WIDE: { - PREAMBLE(); - Object* a = shadow_frame.GetVRegReference(inst->VRegB_23x()); - if (UNLIKELY(a == NULL)) { - ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow()); - HANDLE_PENDING_EXCEPTION(); - break; - } - int32_t index = shadow_frame.GetVReg(inst->VRegC_23x()); - LongArray* array = a->AsLongArray(); - if (LIKELY(array->IsValidIndex(index))) { - shadow_frame.SetVRegLong(inst->VRegA_23x(), array->GetData()[index]); - inst = inst->Next_2xx(); - } else { - HANDLE_PENDING_EXCEPTION(); - } - break; - } - case Instruction::AGET_OBJECT: { - PREAMBLE(); - Object* a = shadow_frame.GetVRegReference(inst->VRegB_23x()); - if (UNLIKELY(a == NULL)) { - ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow()); - HANDLE_PENDING_EXCEPTION(); - break; - } - int32_t index = shadow_frame.GetVReg(inst->VRegC_23x()); - ObjectArray* array = a->AsObjectArray(); - if (LIKELY(array->IsValidIndex(index))) { - shadow_frame.SetVRegReference(inst->VRegA_23x(), array->GetWithoutChecks(index)); - inst = inst->Next_2xx(); - } else { - HANDLE_PENDING_EXCEPTION(); - } - break; - } - case Instruction::APUT_BOOLEAN: { - PREAMBLE(); - Object* a = shadow_frame.GetVRegReference(inst->VRegB_23x()); - if (UNLIKELY(a == NULL)) { - ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow()); - HANDLE_PENDING_EXCEPTION(); - break; - } - uint8_t val = shadow_frame.GetVReg(inst->VRegA_23x()); - int32_t index = shadow_frame.GetVReg(inst->VRegC_23x()); - BooleanArray* array = a->AsBooleanArray(); - if (LIKELY(array->IsValidIndex(index))) { - array->GetData()[index] = val; - inst = inst->Next_2xx(); - } else { - HANDLE_PENDING_EXCEPTION(); - } - break; - } - case Instruction::APUT_BYTE: { - PREAMBLE(); - Object* a = shadow_frame.GetVRegReference(inst->VRegB_23x()); - if (UNLIKELY(a == NULL)) { - ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow()); - HANDLE_PENDING_EXCEPTION(); - break; - } - int8_t val = shadow_frame.GetVReg(inst->VRegA_23x()); - int32_t index = shadow_frame.GetVReg(inst->VRegC_23x()); - ByteArray* array = a->AsByteArray(); - if (LIKELY(array->IsValidIndex(index))) { - array->GetData()[index] = val; - inst = inst->Next_2xx(); - } else { - HANDLE_PENDING_EXCEPTION(); - } - break; - } - case Instruction::APUT_CHAR: { - PREAMBLE(); - Object* a = shadow_frame.GetVRegReference(inst->VRegB_23x()); - if (UNLIKELY(a == NULL)) { - ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow()); - HANDLE_PENDING_EXCEPTION(); - break; - } - uint16_t val = shadow_frame.GetVReg(inst->VRegA_23x()); - int32_t index = shadow_frame.GetVReg(inst->VRegC_23x()); - CharArray* array = a->AsCharArray(); - if (LIKELY(array->IsValidIndex(index))) { - array->GetData()[index] = val; - inst = inst->Next_2xx(); - } else { - HANDLE_PENDING_EXCEPTION(); - } - break; - } - case Instruction::APUT_SHORT: { - PREAMBLE(); - Object* a = shadow_frame.GetVRegReference(inst->VRegB_23x()); - if (UNLIKELY(a == NULL)) { - ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow()); - HANDLE_PENDING_EXCEPTION(); - break; - } - int16_t val = shadow_frame.GetVReg(inst->VRegA_23x()); - int32_t index = shadow_frame.GetVReg(inst->VRegC_23x()); - ShortArray* array = a->AsShortArray(); - if (LIKELY(array->IsValidIndex(index))) { - array->GetData()[index] = val; - inst = inst->Next_2xx(); - } else { - HANDLE_PENDING_EXCEPTION(); - } - break; - } - case Instruction::APUT: { - PREAMBLE(); - Object* a = shadow_frame.GetVRegReference(inst->VRegB_23x()); - if (UNLIKELY(a == NULL)) { - ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow()); - HANDLE_PENDING_EXCEPTION(); - break; - } - int32_t val = shadow_frame.GetVReg(inst->VRegA_23x()); - int32_t index = shadow_frame.GetVReg(inst->VRegC_23x()); - IntArray* array = a->AsIntArray(); - if (LIKELY(array->IsValidIndex(index))) { - array->GetData()[index] = val; - inst = inst->Next_2xx(); - } else { - HANDLE_PENDING_EXCEPTION(); - } - break; - } - case Instruction::APUT_WIDE: { - PREAMBLE(); - Object* a = shadow_frame.GetVRegReference(inst->VRegB_23x()); - if (UNLIKELY(a == NULL)) { - ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow()); - HANDLE_PENDING_EXCEPTION(); - break; - } - int64_t val = shadow_frame.GetVRegLong(inst->VRegA_23x()); - int32_t index = shadow_frame.GetVReg(inst->VRegC_23x()); - LongArray* array = a->AsLongArray(); - if (LIKELY(array->IsValidIndex(index))) { - array->GetData()[index] = val; - inst = inst->Next_2xx(); - } else { - HANDLE_PENDING_EXCEPTION(); - } - break; - } - case Instruction::APUT_OBJECT: { - PREAMBLE(); - Object* a = shadow_frame.GetVRegReference(inst->VRegB_23x()); - if (UNLIKELY(a == NULL)) { - ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow()); - HANDLE_PENDING_EXCEPTION(); - break; - } - int32_t index = shadow_frame.GetVReg(inst->VRegC_23x()); - Object* val = shadow_frame.GetVRegReference(inst->VRegA_23x()); - ObjectArray* array = a->AsObjectArray(); - if (LIKELY(array->IsValidIndex(index) && array->CheckAssignable(val))) { - array->SetWithoutChecks(index, val); - inst = inst->Next_2xx(); - } else { - HANDLE_PENDING_EXCEPTION(); - } - break; - } - case Instruction::IGET_BOOLEAN: { - PREAMBLE(); - bool success = DoFieldGet(self, shadow_frame, inst); - POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); - break; - } - case Instruction::IGET_BYTE: { - PREAMBLE(); - bool success = DoFieldGet(self, shadow_frame, inst); - POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); - break; - } - case Instruction::IGET_CHAR: { - PREAMBLE(); - bool success = DoFieldGet(self, shadow_frame, inst); - POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); - break; - } - case Instruction::IGET_SHORT: { - PREAMBLE(); - bool success = DoFieldGet(self, shadow_frame, inst); - POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); - break; - } - case Instruction::IGET: { - PREAMBLE(); - bool success = DoFieldGet(self, shadow_frame, inst); - POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); - break; - } - case Instruction::IGET_WIDE: { - PREAMBLE(); - bool success = DoFieldGet(self, shadow_frame, inst); - POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); - break; - } - case Instruction::IGET_OBJECT: { - PREAMBLE(); - bool success = DoFieldGet(self, shadow_frame, inst); - POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); - break; - } - case Instruction::IGET_QUICK: { - PREAMBLE(); - bool success = DoIGetQuick(self, shadow_frame, inst); - POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); - break; - } - case Instruction::IGET_WIDE_QUICK: { - PREAMBLE(); - bool success = DoIGetQuick(self, shadow_frame, inst); - POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); - break; - } - case Instruction::IGET_OBJECT_QUICK: { - PREAMBLE(); - bool success = DoIGetQuick(self, shadow_frame, inst); - POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); - break; - } - case Instruction::SGET_BOOLEAN: { - PREAMBLE(); - bool success = DoFieldGet(self, shadow_frame, inst); - POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); - break; - } - case Instruction::SGET_BYTE: { - PREAMBLE(); - bool success = DoFieldGet(self, shadow_frame, inst); - POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); - break; - } - case Instruction::SGET_CHAR: { - PREAMBLE(); - bool success = DoFieldGet(self, shadow_frame, inst); - POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); - break; - } - case Instruction::SGET_SHORT: { - PREAMBLE(); - bool success = DoFieldGet(self, shadow_frame, inst); - POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); - break; - } - case Instruction::SGET: { - PREAMBLE(); - bool success = DoFieldGet(self, shadow_frame, inst); - POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); - break; - } - case Instruction::SGET_WIDE: { - PREAMBLE(); - bool success = DoFieldGet(self, shadow_frame, inst); - POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); - break; - } - case Instruction::SGET_OBJECT: { - PREAMBLE(); - bool success = DoFieldGet(self, shadow_frame, inst); - POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); - break; - } - case Instruction::IPUT_BOOLEAN: { - PREAMBLE(); - bool success = DoFieldPut(self, shadow_frame, inst); - POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); - break; - } - case Instruction::IPUT_BYTE: { - PREAMBLE(); - bool success = DoFieldPut(self, shadow_frame, inst); - POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); - break; - } - case Instruction::IPUT_CHAR: { - PREAMBLE(); - bool success = DoFieldPut(self, shadow_frame, inst); - POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); - break; - } - case Instruction::IPUT_SHORT: { - PREAMBLE(); - bool success = DoFieldPut(self, shadow_frame, inst); - POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); - break; - } - case Instruction::IPUT: { - PREAMBLE(); - bool success = DoFieldPut(self, shadow_frame, inst); - POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); - break; - } - case Instruction::IPUT_WIDE: { - PREAMBLE(); - bool success = DoFieldPut(self, shadow_frame, inst); - POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); - break; - } - case Instruction::IPUT_OBJECT: { - PREAMBLE(); - bool success = DoFieldPut(self, shadow_frame, inst); - POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); - break; - } - case Instruction::IPUT_QUICK: { - PREAMBLE(); - bool success = DoIPutQuick(self, shadow_frame, inst); - POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); - break; - } - case Instruction::IPUT_WIDE_QUICK: { - PREAMBLE(); - bool success = DoIPutQuick(self, shadow_frame, inst); - POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); - break; - } - case Instruction::IPUT_OBJECT_QUICK: { - PREAMBLE(); - bool success = DoIPutQuick(self, shadow_frame, inst); - POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); - break; - } - case Instruction::SPUT_BOOLEAN: { - PREAMBLE(); - bool success = DoFieldPut(self, shadow_frame, inst); - POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); - break; - } - case Instruction::SPUT_BYTE: { - PREAMBLE(); - bool success = DoFieldPut(self, shadow_frame, inst); - POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); - break; - } - case Instruction::SPUT_CHAR: { - PREAMBLE(); - bool success = DoFieldPut(self, shadow_frame, inst); - POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); - break; - } - case Instruction::SPUT_SHORT: { - PREAMBLE(); - bool success = DoFieldPut(self, shadow_frame, inst); - POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); - break; - } - case Instruction::SPUT: { - PREAMBLE(); - bool success = DoFieldPut(self, shadow_frame, inst); - POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); - break; - } - case Instruction::SPUT_WIDE: { - PREAMBLE(); - bool success = DoFieldPut(self, shadow_frame, inst); - POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); - break; - } - case Instruction::SPUT_OBJECT: { - PREAMBLE(); - bool success = DoFieldPut(self, shadow_frame, inst); - POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); - break; - } - case Instruction::INVOKE_VIRTUAL: { - PREAMBLE(); - bool success = DoInvoke(self, shadow_frame, inst, &result_register); - POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_3xx); - break; - } - case Instruction::INVOKE_VIRTUAL_RANGE: { - PREAMBLE(); - bool success = DoInvoke(self, shadow_frame, inst, &result_register); - POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_3xx); - break; - } - case Instruction::INVOKE_SUPER: { - PREAMBLE(); - bool success = DoInvoke(self, shadow_frame, inst, &result_register); - POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_3xx); - break; - } - case Instruction::INVOKE_SUPER_RANGE: { - PREAMBLE(); - bool success = DoInvoke(self, shadow_frame, inst, &result_register); - POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_3xx); - break; - } - case Instruction::INVOKE_DIRECT: { - PREAMBLE(); - bool success = DoInvoke(self, shadow_frame, inst, &result_register); - POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_3xx); - break; - } - case Instruction::INVOKE_DIRECT_RANGE: { - PREAMBLE(); - bool success = DoInvoke(self, shadow_frame, inst, &result_register); - POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_3xx); - break; - } - case Instruction::INVOKE_INTERFACE: { - PREAMBLE(); - bool success = DoInvoke(self, shadow_frame, inst, &result_register); - POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_3xx); - break; - } - case Instruction::INVOKE_INTERFACE_RANGE: { - PREAMBLE(); - bool success = DoInvoke(self, shadow_frame, inst, &result_register); - POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_3xx); - break; - } - case Instruction::INVOKE_STATIC: { - PREAMBLE(); - bool success = DoInvoke(self, shadow_frame, inst, &result_register); - POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_3xx); - break; - } - case Instruction::INVOKE_STATIC_RANGE: { - PREAMBLE(); - bool success = DoInvoke(self, shadow_frame, inst, &result_register); - POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_3xx); - break; - } - case Instruction::INVOKE_VIRTUAL_QUICK: { - PREAMBLE(); - bool success = DoInvokeVirtualQuick(self, shadow_frame, inst, &result_register); - POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_3xx); - break; - } - case Instruction::INVOKE_VIRTUAL_RANGE_QUICK: { - PREAMBLE(); - bool success = DoInvokeVirtualQuick(self, shadow_frame, inst, &result_register); - POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_3xx); - break; - } - case Instruction::NEG_INT: - PREAMBLE(); - shadow_frame.SetVReg(inst->VRegA_12x(), -shadow_frame.GetVReg(inst->VRegB_12x())); - inst = inst->Next_1xx(); - break; - case Instruction::NOT_INT: - PREAMBLE(); - shadow_frame.SetVReg(inst->VRegA_12x(), ~shadow_frame.GetVReg(inst->VRegB_12x())); - inst = inst->Next_1xx(); - break; - case Instruction::NEG_LONG: - PREAMBLE(); - shadow_frame.SetVRegLong(inst->VRegA_12x(), -shadow_frame.GetVRegLong(inst->VRegB_12x())); - inst = inst->Next_1xx(); - break; - case Instruction::NOT_LONG: - PREAMBLE(); - shadow_frame.SetVRegLong(inst->VRegA_12x(), ~shadow_frame.GetVRegLong(inst->VRegB_12x())); - inst = inst->Next_1xx(); - break; - case Instruction::NEG_FLOAT: - PREAMBLE(); - shadow_frame.SetVRegFloat(inst->VRegA_12x(), -shadow_frame.GetVRegFloat(inst->VRegB_12x())); - inst = inst->Next_1xx(); - break; - case Instruction::NEG_DOUBLE: - PREAMBLE(); - shadow_frame.SetVRegDouble(inst->VRegA_12x(), -shadow_frame.GetVRegDouble(inst->VRegB_12x())); - inst = inst->Next_1xx(); - break; - case Instruction::INT_TO_LONG: - PREAMBLE(); - shadow_frame.SetVRegLong(inst->VRegA_12x(), shadow_frame.GetVReg(inst->VRegB_12x())); - inst = inst->Next_1xx(); - break; - case Instruction::INT_TO_FLOAT: - PREAMBLE(); - shadow_frame.SetVRegFloat(inst->VRegA_12x(), shadow_frame.GetVReg(inst->VRegB_12x())); - inst = inst->Next_1xx(); - break; - case Instruction::INT_TO_DOUBLE: - PREAMBLE(); - shadow_frame.SetVRegDouble(inst->VRegA_12x(), shadow_frame.GetVReg(inst->VRegB_12x())); - inst = inst->Next_1xx(); - break; - case Instruction::LONG_TO_INT: - PREAMBLE(); - shadow_frame.SetVReg(inst->VRegA_12x(), shadow_frame.GetVRegLong(inst->VRegB_12x())); - inst = inst->Next_1xx(); - break; - case Instruction::LONG_TO_FLOAT: - PREAMBLE(); - shadow_frame.SetVRegFloat(inst->VRegA_12x(), shadow_frame.GetVRegLong(inst->VRegB_12x())); - inst = inst->Next_1xx(); - break; - case Instruction::LONG_TO_DOUBLE: - PREAMBLE(); - shadow_frame.SetVRegDouble(inst->VRegA_12x(), shadow_frame.GetVRegLong(inst->VRegB_12x())); - inst = inst->Next_1xx(); - break; - case Instruction::FLOAT_TO_INT: { - PREAMBLE(); - float val = shadow_frame.GetVRegFloat(inst->VRegB_12x()); - int32_t result; - if (val != val) { - result = 0; - } else if (val > static_cast(kMaxInt)) { - result = kMaxInt; - } else if (val < static_cast(kMinInt)) { - result = kMinInt; - } else { - result = val; - } - shadow_frame.SetVReg(inst->VRegA_12x(), result); - inst = inst->Next_1xx(); - break; - } - case Instruction::FLOAT_TO_LONG: { - PREAMBLE(); - float val = shadow_frame.GetVRegFloat(inst->VRegB_12x()); - int64_t result; - if (val != val) { - result = 0; - } else if (val > static_cast(kMaxLong)) { - result = kMaxLong; - } else if (val < static_cast(kMinLong)) { - result = kMinLong; - } else { - result = val; - } - shadow_frame.SetVRegLong(inst->VRegA_12x(), result); - inst = inst->Next_1xx(); - break; - } - case Instruction::FLOAT_TO_DOUBLE: - PREAMBLE(); - shadow_frame.SetVRegDouble(inst->VRegA_12x(), shadow_frame.GetVRegFloat(inst->VRegB_12x())); - inst = inst->Next_1xx(); - break; - case Instruction::DOUBLE_TO_INT: { - PREAMBLE(); - double val = shadow_frame.GetVRegDouble(inst->VRegB_12x()); - int32_t result; - if (val != val) { - result = 0; - } else if (val > static_cast(kMaxInt)) { - result = kMaxInt; - } else if (val < static_cast(kMinInt)) { - result = kMinInt; - } else { - result = val; - } - shadow_frame.SetVReg(inst->VRegA_12x(), result); - inst = inst->Next_1xx(); - break; - } - case Instruction::DOUBLE_TO_LONG: { - PREAMBLE(); - double val = shadow_frame.GetVRegDouble(inst->VRegB_12x()); - int64_t result; - if (val != val) { - result = 0; - } else if (val > static_cast(kMaxLong)) { - result = kMaxLong; - } else if (val < static_cast(kMinLong)) { - result = kMinLong; - } else { - result = val; - } - shadow_frame.SetVRegLong(inst->VRegA_12x(), result); - inst = inst->Next_1xx(); - break; - } - case Instruction::DOUBLE_TO_FLOAT: - PREAMBLE(); - shadow_frame.SetVRegFloat(inst->VRegA_12x(), shadow_frame.GetVRegDouble(inst->VRegB_12x())); - inst = inst->Next_1xx(); - break; - case Instruction::INT_TO_BYTE: - PREAMBLE(); - shadow_frame.SetVReg(inst->VRegA_12x(), - static_cast(shadow_frame.GetVReg(inst->VRegB_12x()))); - inst = inst->Next_1xx(); - break; - case Instruction::INT_TO_CHAR: - PREAMBLE(); - shadow_frame.SetVReg(inst->VRegA_12x(), - static_cast(shadow_frame.GetVReg(inst->VRegB_12x()))); - inst = inst->Next_1xx(); - break; - case Instruction::INT_TO_SHORT: - PREAMBLE(); - shadow_frame.SetVReg(inst->VRegA_12x(), - static_cast(shadow_frame.GetVReg(inst->VRegB_12x()))); - inst = inst->Next_1xx(); - break; - case Instruction::ADD_INT: - PREAMBLE(); - shadow_frame.SetVReg(inst->VRegA_23x(), - 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(), - 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(), - 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(), - 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(), - 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(), - 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(), - 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(), - static_cast(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(), - 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(), - 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(), - 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(), - 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(), - 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(), - 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(), - 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(), - 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(), - 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(), - 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(), - 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(), - 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(), - 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(), - static_cast(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(), - 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(), - 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(), - 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(), - 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(), - 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(), - 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(), - 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(), - 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(), - 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(), - 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(); - shadow_frame.SetVReg(vregA, - shadow_frame.GetVReg(vregA) + - shadow_frame.GetVReg(inst->VRegB_12x())); - inst = inst->Next_1xx(); - break; - } - case Instruction::SUB_INT_2ADDR: { - PREAMBLE(); - uint4_t vregA = inst->VRegA_12x(); - shadow_frame.SetVReg(vregA, - shadow_frame.GetVReg(vregA) - - shadow_frame.GetVReg(inst->VRegB_12x())); - inst = inst->Next_1xx(); - break; - } - case Instruction::MUL_INT_2ADDR: { - PREAMBLE(); - uint4_t vregA = inst->VRegA_12x(); - shadow_frame.SetVReg(vregA, - shadow_frame.GetVReg(vregA) * - shadow_frame.GetVReg(inst->VRegB_12x())); - inst = inst->Next_1xx(); - break; - } - case Instruction::DIV_INT_2ADDR: { - PREAMBLE(); - uint4_t vregA = inst->VRegA_12x(); - bool success = DoIntDivide(shadow_frame, vregA, shadow_frame.GetVReg(vregA), - shadow_frame.GetVReg(inst->VRegB_12x())); - POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_1xx); - break; - } - case Instruction::REM_INT_2ADDR: { - PREAMBLE(); - uint4_t vregA = inst->VRegA_12x(); - bool success = DoIntRemainder(shadow_frame, vregA, shadow_frame.GetVReg(vregA), - shadow_frame.GetVReg(inst->VRegB_12x())); - POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_1xx); - break; - } - case Instruction::SHL_INT_2ADDR: { - PREAMBLE(); - uint4_t vregA = inst->VRegA_12x(); - shadow_frame.SetVReg(vregA, - shadow_frame.GetVReg(vregA) << - (shadow_frame.GetVReg(inst->VRegB_12x()) & 0x1f)); - inst = inst->Next_1xx(); - break; - } - case Instruction::SHR_INT_2ADDR: { - PREAMBLE(); - uint4_t vregA = inst->VRegA_12x(); - shadow_frame.SetVReg(vregA, - shadow_frame.GetVReg(vregA) >> - (shadow_frame.GetVReg(inst->VRegB_12x()) & 0x1f)); - inst = inst->Next_1xx(); - break; - } - case Instruction::USHR_INT_2ADDR: { - PREAMBLE(); - uint4_t vregA = inst->VRegA_12x(); - shadow_frame.SetVReg(vregA, - static_cast(shadow_frame.GetVReg(vregA)) >> - (shadow_frame.GetVReg(inst->VRegB_12x()) & 0x1f)); - inst = inst->Next_1xx(); - break; - } - case Instruction::AND_INT_2ADDR: { - PREAMBLE(); - uint4_t vregA = inst->VRegA_12x(); - shadow_frame.SetVReg(vregA, - shadow_frame.GetVReg(vregA) & - shadow_frame.GetVReg(inst->VRegB_12x())); - inst = inst->Next_1xx(); - break; - } - case Instruction::OR_INT_2ADDR: { - PREAMBLE(); - uint4_t vregA = inst->VRegA_12x(); - shadow_frame.SetVReg(vregA, - shadow_frame.GetVReg(vregA) | - shadow_frame.GetVReg(inst->VRegB_12x())); - inst = inst->Next_1xx(); - break; - } - case Instruction::XOR_INT_2ADDR: { - PREAMBLE(); - uint4_t vregA = inst->VRegA_12x(); - shadow_frame.SetVReg(vregA, - shadow_frame.GetVReg(vregA) ^ - shadow_frame.GetVReg(inst->VRegB_12x())); - inst = inst->Next_1xx(); - break; - } - case Instruction::ADD_LONG_2ADDR: { - PREAMBLE(); - uint4_t vregA = inst->VRegA_12x(); - shadow_frame.SetVRegLong(vregA, - shadow_frame.GetVRegLong(vregA) + - shadow_frame.GetVRegLong(inst->VRegB_12x())); - inst = inst->Next_1xx(); - break; - } - case Instruction::SUB_LONG_2ADDR: { - PREAMBLE(); - uint4_t vregA = inst->VRegA_12x(); - shadow_frame.SetVRegLong(vregA, - shadow_frame.GetVRegLong(vregA) - - shadow_frame.GetVRegLong(inst->VRegB_12x())); - inst = inst->Next_1xx(); - break; - } - case Instruction::MUL_LONG_2ADDR: { - PREAMBLE(); - uint4_t vregA = inst->VRegA_12x(); - shadow_frame.SetVRegLong(vregA, - shadow_frame.GetVRegLong(vregA) * - shadow_frame.GetVRegLong(inst->VRegB_12x())); - inst = inst->Next_1xx(); - break; - } - case Instruction::DIV_LONG_2ADDR: { - PREAMBLE(); - uint4_t vregA = inst->VRegA_12x(); - DoLongDivide(shadow_frame, vregA, shadow_frame.GetVRegLong(vregA), - shadow_frame.GetVRegLong(inst->VRegB_12x())); - POSSIBLY_HANDLE_PENDING_EXCEPTION(self->IsExceptionPending(), Next_1xx); - break; - } - case Instruction::REM_LONG_2ADDR: { - PREAMBLE(); - uint4_t vregA = inst->VRegA_12x(); - DoLongRemainder(shadow_frame, vregA, shadow_frame.GetVRegLong(vregA), - shadow_frame.GetVRegLong(inst->VRegB_12x())); - POSSIBLY_HANDLE_PENDING_EXCEPTION(self->IsExceptionPending(), Next_1xx); - break; - } - case Instruction::AND_LONG_2ADDR: { - PREAMBLE(); - uint4_t vregA = inst->VRegA_12x(); - shadow_frame.SetVRegLong(vregA, - shadow_frame.GetVRegLong(vregA) & - shadow_frame.GetVRegLong(inst->VRegB_12x())); - inst = inst->Next_1xx(); - break; - } - case Instruction::OR_LONG_2ADDR: { - PREAMBLE(); - uint4_t vregA = inst->VRegA_12x(); - shadow_frame.SetVRegLong(vregA, - shadow_frame.GetVRegLong(vregA) | - shadow_frame.GetVRegLong(inst->VRegB_12x())); - inst = inst->Next_1xx(); - break; - } - case Instruction::XOR_LONG_2ADDR: { - PREAMBLE(); - uint4_t vregA = inst->VRegA_12x(); - shadow_frame.SetVRegLong(vregA, - shadow_frame.GetVRegLong(vregA) ^ - shadow_frame.GetVRegLong(inst->VRegB_12x())); - inst = inst->Next_1xx(); - break; - } - case Instruction::SHL_LONG_2ADDR: { - PREAMBLE(); - uint4_t vregA = inst->VRegA_12x(); - shadow_frame.SetVRegLong(vregA, - shadow_frame.GetVRegLong(vregA) << - (shadow_frame.GetVReg(inst->VRegB_12x()) & 0x3f)); - inst = inst->Next_1xx(); - break; - } - case Instruction::SHR_LONG_2ADDR: { - PREAMBLE(); - uint4_t vregA = inst->VRegA_12x(); - shadow_frame.SetVRegLong(vregA, - shadow_frame.GetVRegLong(vregA) >> - (shadow_frame.GetVReg(inst->VRegB_12x()) & 0x3f)); - inst = inst->Next_1xx(); - break; - } - case Instruction::USHR_LONG_2ADDR: { - PREAMBLE(); - uint4_t vregA = inst->VRegA_12x(); - shadow_frame.SetVRegLong(vregA, - static_cast(shadow_frame.GetVRegLong(vregA)) >> - (shadow_frame.GetVReg(inst->VRegB_12x()) & 0x3f)); - inst = inst->Next_1xx(); - break; - } - case Instruction::ADD_FLOAT_2ADDR: { - PREAMBLE(); - uint4_t vregA = inst->VRegA_12x(); - shadow_frame.SetVRegFloat(vregA, - shadow_frame.GetVRegFloat(vregA) + - shadow_frame.GetVRegFloat(inst->VRegB_12x())); - inst = inst->Next_1xx(); - break; - } - case Instruction::SUB_FLOAT_2ADDR: { - PREAMBLE(); - uint4_t vregA = inst->VRegA_12x(); - shadow_frame.SetVRegFloat(vregA, - shadow_frame.GetVRegFloat(vregA) - - shadow_frame.GetVRegFloat(inst->VRegB_12x())); - inst = inst->Next_1xx(); - break; - } - case Instruction::MUL_FLOAT_2ADDR: { - PREAMBLE(); - uint4_t vregA = inst->VRegA_12x(); - shadow_frame.SetVRegFloat(vregA, - shadow_frame.GetVRegFloat(vregA) * - shadow_frame.GetVRegFloat(inst->VRegB_12x())); - inst = inst->Next_1xx(); - break; - } - case Instruction::DIV_FLOAT_2ADDR: { - PREAMBLE(); - uint4_t vregA = inst->VRegA_12x(); - shadow_frame.SetVRegFloat(vregA, - shadow_frame.GetVRegFloat(vregA) / - shadow_frame.GetVRegFloat(inst->VRegB_12x())); - inst = inst->Next_1xx(); - break; - } - case Instruction::REM_FLOAT_2ADDR: { - PREAMBLE(); - uint4_t vregA = inst->VRegA_12x(); - shadow_frame.SetVRegFloat(vregA, - fmodf(shadow_frame.GetVRegFloat(vregA), - shadow_frame.GetVRegFloat(inst->VRegB_12x()))); - inst = inst->Next_1xx(); - break; - } - case Instruction::ADD_DOUBLE_2ADDR: { - PREAMBLE(); - uint4_t vregA = inst->VRegA_12x(); - shadow_frame.SetVRegDouble(vregA, - shadow_frame.GetVRegDouble(vregA) + - shadow_frame.GetVRegDouble(inst->VRegB_12x())); - inst = inst->Next_1xx(); - break; - } - case Instruction::SUB_DOUBLE_2ADDR: { - PREAMBLE(); - uint4_t vregA = inst->VRegA_12x(); - shadow_frame.SetVRegDouble(vregA, - shadow_frame.GetVRegDouble(vregA) - - shadow_frame.GetVRegDouble(inst->VRegB_12x())); - inst = inst->Next_1xx(); - break; - } - case Instruction::MUL_DOUBLE_2ADDR: { - PREAMBLE(); - uint4_t vregA = inst->VRegA_12x(); - shadow_frame.SetVRegDouble(vregA, - shadow_frame.GetVRegDouble(vregA) * - shadow_frame.GetVRegDouble(inst->VRegB_12x())); - inst = inst->Next_1xx(); - break; - } - case Instruction::DIV_DOUBLE_2ADDR: { - PREAMBLE(); - uint4_t vregA = inst->VRegA_12x(); - shadow_frame.SetVRegDouble(vregA, - shadow_frame.GetVRegDouble(vregA) / - shadow_frame.GetVRegDouble(inst->VRegB_12x())); - inst = inst->Next_1xx(); - break; - } - case Instruction::REM_DOUBLE_2ADDR: { - PREAMBLE(); - uint4_t vregA = inst->VRegA_12x(); - shadow_frame.SetVRegDouble(vregA, - fmod(shadow_frame.GetVRegDouble(vregA), - shadow_frame.GetVRegDouble(inst->VRegB_12x()))); - inst = inst->Next_1xx(); - break; - } - case Instruction::ADD_INT_LIT16: - PREAMBLE(); - shadow_frame.SetVReg(inst->VRegA_22s(), - shadow_frame.GetVReg(inst->VRegB_22s()) + - inst->VRegC_22s()); - inst = inst->Next_2xx(); - break; - case Instruction::RSUB_INT: - PREAMBLE(); - shadow_frame.SetVReg(inst->VRegA_22s(), - inst->VRegC_22s() - - shadow_frame.GetVReg(inst->VRegB_22s())); - inst = inst->Next_2xx(); - break; - case Instruction::MUL_INT_LIT16: - PREAMBLE(); - shadow_frame.SetVReg(inst->VRegA_22s(), - shadow_frame.GetVReg(inst->VRegB_22s()) * - inst->VRegC_22s()); - inst = inst->Next_2xx(); - break; - case Instruction::DIV_INT_LIT16: { - PREAMBLE(); - bool success = DoIntDivide(shadow_frame, inst->VRegA_22s(), - shadow_frame.GetVReg(inst->VRegB_22s()), 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(), - shadow_frame.GetVReg(inst->VRegB_22s()), inst->VRegC_22s()); - POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); - break; - } - case Instruction::AND_INT_LIT16: - PREAMBLE(); - shadow_frame.SetVReg(inst->VRegA_22s(), - shadow_frame.GetVReg(inst->VRegB_22s()) & - inst->VRegC_22s()); - inst = inst->Next_2xx(); - break; - case Instruction::OR_INT_LIT16: - PREAMBLE(); - shadow_frame.SetVReg(inst->VRegA_22s(), - shadow_frame.GetVReg(inst->VRegB_22s()) | - inst->VRegC_22s()); - inst = inst->Next_2xx(); - break; - case Instruction::XOR_INT_LIT16: - PREAMBLE(); - shadow_frame.SetVReg(inst->VRegA_22s(), - shadow_frame.GetVReg(inst->VRegB_22s()) ^ - inst->VRegC_22s()); - inst = inst->Next_2xx(); - break; - case Instruction::ADD_INT_LIT8: - PREAMBLE(); - shadow_frame.SetVReg(inst->VRegA_22b(), - 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->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(), - 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(), - 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(), - 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(), - 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(), - 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(), - 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(), - 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(), - 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(), - static_cast(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_EB ... Instruction::UNUSED_FF: - case Instruction::UNUSED_79: - case Instruction::UNUSED_7A: - UnexpectedOpcode(inst, mh); - } - } -} // NOLINT(readability/fn_size) - -static JValue Execute(Thread* self, MethodHelper& mh, const DexFile::CodeItem* code_item, - ShadowFrame& shadow_frame, JValue result_register) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - -static inline JValue Execute(Thread* self, MethodHelper& mh, const DexFile::CodeItem* code_item, - ShadowFrame& shadow_frame, JValue result_register) { - DCHECK(shadow_frame.GetMethod() == mh.GetMethod() || - shadow_frame.GetMethod()->GetDeclaringClass()->IsProxyClass()); - DCHECK(!shadow_frame.GetMethod()->IsAbstract()); - DCHECK(!shadow_frame.GetMethod()->IsNative()); - if (shadow_frame.GetMethod()->IsPreverified()) { - // Enter the "without access check" interpreter. - return ExecuteImpl(self, mh, code_item, shadow_frame, result_register); - } else { - // Enter the "with access check" interpreter. - return ExecuteImpl(self, mh, code_item, shadow_frame, result_register); } } diff --git a/runtime/interpreter/interpreter_common.cc b/runtime/interpreter/interpreter_common.cc new file mode 100644 index 00000000000..86a6aea0535 --- /dev/null +++ b/runtime/interpreter/interpreter_common.cc @@ -0,0 +1,389 @@ +/* + * Copyright (C) 2012 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 "interpreter_common.h" + +namespace art { +namespace interpreter { + +template +bool DoInvoke(Thread* self, ShadowFrame& shadow_frame, + const Instruction* inst, JValue* result) { + uint32_t method_idx = (is_range) ? inst->VRegB_3rc() : inst->VRegB_35c(); + uint32_t vregC = (is_range) ? inst->VRegC_3rc() : inst->VRegC_35c(); + Object* receiver = (type == kStatic) ? NULL : shadow_frame.GetVRegReference(vregC); + ArtMethod* method = FindMethodFromCode(method_idx, receiver, shadow_frame.GetMethod(), self, + do_access_check, type); + if (UNLIKELY(method == NULL)) { + CHECK(self->IsExceptionPending()); + result->SetJ(0); + return false; + } else if (UNLIKELY(method->IsAbstract())) { + ThrowAbstractMethodError(method); + result->SetJ(0); + return false; + } + + MethodHelper mh(method); + const DexFile::CodeItem* code_item = mh.GetCodeItem(); + uint16_t num_regs; + uint16_t num_ins; + if (LIKELY(code_item != NULL)) { + num_regs = code_item->registers_size_; + num_ins = code_item->ins_size_; + } else { + DCHECK(method->IsNative() || method->IsProxyMethod()); + num_regs = num_ins = ArtMethod::NumArgRegisters(mh.GetShorty()); + if (!method->IsStatic()) { + num_regs++; + num_ins++; + } + } + + void* memory = alloca(ShadowFrame::ComputeSize(num_regs)); + ShadowFrame* new_shadow_frame(ShadowFrame::Create(num_regs, &shadow_frame, method, 0, memory)); + size_t cur_reg = num_regs - num_ins; + if (receiver != NULL) { + new_shadow_frame->SetVRegReference(cur_reg, receiver); + ++cur_reg; + } + + size_t arg_offset = (receiver == NULL) ? 0 : 1; + const char* shorty = mh.GetShorty(); + uint32_t arg[5]; + if (!is_range) { + inst->GetArgs(arg); + } + for (size_t shorty_pos = 0; cur_reg < num_regs; ++shorty_pos, cur_reg++, arg_offset++) { + DCHECK_LT(shorty_pos + 1, mh.GetShortyLength()); + size_t arg_pos = is_range ? vregC + arg_offset : arg[arg_offset]; + switch (shorty[shorty_pos + 1]) { + case 'L': { + Object* o = shadow_frame.GetVRegReference(arg_pos); + new_shadow_frame->SetVRegReference(cur_reg, o); + break; + } + case 'J': case 'D': { + uint64_t wide_value = (static_cast(shadow_frame.GetVReg(arg_pos + 1)) << 32) | + static_cast(shadow_frame.GetVReg(arg_pos)); + new_shadow_frame->SetVRegLong(cur_reg, wide_value); + cur_reg++; + arg_offset++; + break; + } + default: + new_shadow_frame->SetVReg(cur_reg, shadow_frame.GetVReg(arg_pos)); + break; + } + } + + if (LIKELY(Runtime::Current()->IsStarted())) { + (method->GetEntryPointFromInterpreter())(self, mh, code_item, new_shadow_frame, result); + } else { + UnstartedRuntimeInvoke(self, mh, code_item, new_shadow_frame, result, num_regs - num_ins); + } + return !self->IsExceptionPending(); +} + +template +bool DoInvokeVirtualQuick(Thread* self, ShadowFrame& shadow_frame, + const Instruction* inst, JValue* result) { + uint32_t vregC = (is_range) ? inst->VRegC_3rc() : inst->VRegC_35c(); + Object* receiver = shadow_frame.GetVRegReference(vregC); + if (UNLIKELY(receiver == NULL)) { + // We lost the reference to the method index so we cannot get a more + // precised exception message. + ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow()); + return false; + } + uint32_t vtable_idx = (is_range) ? inst->VRegB_3rc() : inst->VRegB_35c(); + // TODO: use ObjectArray::GetWithoutChecks ? + ArtMethod* method = receiver->GetClass()->GetVTable()->Get(vtable_idx); + if (UNLIKELY(method == NULL)) { + CHECK(self->IsExceptionPending()); + result->SetJ(0); + return false; + } else if (UNLIKELY(method->IsAbstract())) { + ThrowAbstractMethodError(method); + result->SetJ(0); + return false; + } + + MethodHelper mh(method); + const DexFile::CodeItem* code_item = mh.GetCodeItem(); + uint16_t num_regs; + uint16_t num_ins; + if (code_item != NULL) { + num_regs = code_item->registers_size_; + num_ins = code_item->ins_size_; + } else { + DCHECK(method->IsNative() || method->IsProxyMethod()); + num_regs = num_ins = ArtMethod::NumArgRegisters(mh.GetShorty()); + if (!method->IsStatic()) { + num_regs++; + num_ins++; + } + } + + void* memory = alloca(ShadowFrame::ComputeSize(num_regs)); + ShadowFrame* new_shadow_frame(ShadowFrame::Create(num_regs, &shadow_frame, + method, 0, memory)); + size_t cur_reg = num_regs - num_ins; + if (receiver != NULL) { + new_shadow_frame->SetVRegReference(cur_reg, receiver); + ++cur_reg; + } + + size_t arg_offset = (receiver == NULL) ? 0 : 1; + const char* shorty = mh.GetShorty(); + uint32_t arg[5]; + if (!is_range) { + inst->GetArgs(arg); + } + for (size_t shorty_pos = 0; cur_reg < num_regs; ++shorty_pos, cur_reg++, arg_offset++) { + DCHECK_LT(shorty_pos + 1, mh.GetShortyLength()); + size_t arg_pos = is_range ? vregC + arg_offset : arg[arg_offset]; + switch (shorty[shorty_pos + 1]) { + case 'L': { + Object* o = shadow_frame.GetVRegReference(arg_pos); + new_shadow_frame->SetVRegReference(cur_reg, o); + break; + } + case 'J': case 'D': { + uint64_t wide_value = (static_cast(shadow_frame.GetVReg(arg_pos + 1)) << 32) | + static_cast(shadow_frame.GetVReg(arg_pos)); + new_shadow_frame->SetVRegLong(cur_reg, wide_value); + cur_reg++; + arg_offset++; + break; + } + default: + new_shadow_frame->SetVReg(cur_reg, shadow_frame.GetVReg(arg_pos)); + break; + } + } + + if (LIKELY(Runtime::Current()->IsStarted())) { + (method->GetEntryPointFromInterpreter())(self, mh, code_item, new_shadow_frame, result); + } else { + UnstartedRuntimeInvoke(self, mh, code_item, new_shadow_frame, result, num_regs - num_ins); + } + return !self->IsExceptionPending(); +} + +template +bool DoFilledNewArray(const Instruction* inst, const ShadowFrame& shadow_frame, + Thread* self, JValue* result) { + DCHECK(inst->Opcode() == Instruction::FILLED_NEW_ARRAY || + inst->Opcode() == Instruction::FILLED_NEW_ARRAY_RANGE); + const int32_t length = is_range ? inst->VRegA_3rc() : inst->VRegA_35c(); + if (!is_range) { + // Checks FILLED_NEW_ARRAY's length does not exceed 5 arguments. + CHECK_LE(length, 5); + } + if (UNLIKELY(length < 0)) { + ThrowNegativeArraySizeException(length); + return false; + } + uint16_t type_idx = is_range ? inst->VRegB_3rc() : inst->VRegB_35c(); + Class* arrayClass = ResolveVerifyAndClinit(type_idx, shadow_frame.GetMethod(), + self, false, do_access_check); + if (UNLIKELY(arrayClass == NULL)) { + DCHECK(self->IsExceptionPending()); + return false; + } + CHECK(arrayClass->IsArrayClass()); + Class* componentClass = arrayClass->GetComponentType(); + if (UNLIKELY(componentClass->IsPrimitive() && !componentClass->IsPrimitiveInt())) { + if (componentClass->IsPrimitiveLong() || componentClass->IsPrimitiveDouble()) { + ThrowRuntimeException("Bad filled array request for type %s", + PrettyDescriptor(componentClass).c_str()); + } else { + self->ThrowNewExceptionF(shadow_frame.GetCurrentLocationForThrow(), + "Ljava/lang/InternalError;", + "Found type %s; filled-new-array not implemented for anything but \'int\'", + PrettyDescriptor(componentClass).c_str()); + } + return false; + } + Object* newArray = Array::Alloc(self, arrayClass, length); + if (UNLIKELY(newArray == NULL)) { + DCHECK(self->IsExceptionPending()); + return false; + } + if (is_range) { + uint32_t vregC = inst->VRegC_3rc(); + const bool is_primitive_int_component = componentClass->IsPrimitiveInt(); + for (int32_t i = 0; i < length; ++i) { + if (is_primitive_int_component) { + newArray->AsIntArray()->Set(i, shadow_frame.GetVReg(vregC + i)); + } else { + newArray->AsObjectArray()->Set(i, shadow_frame.GetVRegReference(vregC + i)); + } + } + } else { + uint32_t arg[5]; + inst->GetArgs(arg); + const bool is_primitive_int_component = componentClass->IsPrimitiveInt(); + for (int32_t i = 0; i < length; ++i) { + if (is_primitive_int_component) { + newArray->AsIntArray()->Set(i, shadow_frame.GetVReg(arg[i])); + } else { + newArray->AsObjectArray()->Set(i, shadow_frame.GetVRegReference(arg[i])); + } + } + } + + result->SetL(newArray); + return true; +} + +void UnstartedRuntimeInvoke(Thread* self, MethodHelper& mh, + const DexFile::CodeItem* code_item, ShadowFrame* shadow_frame, + JValue* result, size_t arg_offset) { + // In a runtime that's not started we intercept certain methods to avoid complicated dependency + // problems in core libraries. + std::string name(PrettyMethod(shadow_frame->GetMethod())); + if (name == "java.lang.Class java.lang.Class.forName(java.lang.String)") { + std::string descriptor(DotToDescriptor(shadow_frame->GetVRegReference(arg_offset)->AsString()->ToModifiedUtf8().c_str())); + ClassLoader* class_loader = NULL; // shadow_frame.GetMethod()->GetDeclaringClass()->GetClassLoader(); + Class* found = Runtime::Current()->GetClassLinker()->FindClass(descriptor.c_str(), + class_loader); + CHECK(found != NULL) << "Class.forName failed in un-started runtime for class: " + << PrettyDescriptor(descriptor); + result->SetL(found); + } else if (name == "java.lang.Object java.lang.Class.newInstance()") { + Class* klass = shadow_frame->GetVRegReference(arg_offset)->AsClass(); + ArtMethod* c = klass->FindDeclaredDirectMethod("", "()V"); + CHECK(c != NULL); + SirtRef obj(self, klass->AllocObject(self)); + CHECK(obj.get() != NULL); + EnterInterpreterFromInvoke(self, c, obj.get(), NULL, NULL); + result->SetL(obj.get()); + } else if (name == "java.lang.reflect.Field java.lang.Class.getDeclaredField(java.lang.String)") { + // Special managed code cut-out to allow field lookup in a un-started runtime that'd fail + // going the reflective Dex way. + Class* klass = shadow_frame->GetVRegReference(arg_offset)->AsClass(); + String* name = shadow_frame->GetVRegReference(arg_offset + 1)->AsString(); + ArtField* found = NULL; + FieldHelper fh; + ObjectArray* fields = klass->GetIFields(); + for (int32_t i = 0; i < fields->GetLength() && found == NULL; ++i) { + ArtField* f = fields->Get(i); + fh.ChangeField(f); + if (name->Equals(fh.GetName())) { + found = f; + } + } + if (found == NULL) { + fields = klass->GetSFields(); + for (int32_t i = 0; i < fields->GetLength() && found == NULL; ++i) { + ArtField* f = fields->Get(i); + fh.ChangeField(f); + if (name->Equals(fh.GetName())) { + found = f; + } + } + } + CHECK(found != NULL) + << "Failed to find field in Class.getDeclaredField in un-started runtime. name=" + << name->ToModifiedUtf8() << " class=" << PrettyDescriptor(klass); + // TODO: getDeclaredField calls GetType once the field is found to ensure a + // NoClassDefFoundError is thrown if the field's type cannot be resolved. + Class* jlr_Field = self->DecodeJObject(WellKnownClasses::java_lang_reflect_Field)->AsClass(); + SirtRef field(self, jlr_Field->AllocObject(self)); + CHECK(field.get() != NULL); + ArtMethod* c = jlr_Field->FindDeclaredDirectMethod("", "(Ljava/lang/reflect/ArtField;)V"); + uint32_t args[1]; + args[0] = reinterpret_cast(found); + EnterInterpreterFromInvoke(self, c, field.get(), args, NULL); + result->SetL(field.get()); + } else if (name == "void java.lang.System.arraycopy(java.lang.Object, int, java.lang.Object, int, int)" || + name == "void java.lang.System.arraycopy(char[], int, char[], int, int)") { + // Special case array copying without initializing System. + Class* ctype = shadow_frame->GetVRegReference(arg_offset)->GetClass()->GetComponentType(); + jint srcPos = shadow_frame->GetVReg(arg_offset + 1); + jint dstPos = shadow_frame->GetVReg(arg_offset + 3); + jint length = shadow_frame->GetVReg(arg_offset + 4); + if (!ctype->IsPrimitive()) { + ObjectArray* src = shadow_frame->GetVRegReference(arg_offset)->AsObjectArray(); + ObjectArray* dst = shadow_frame->GetVRegReference(arg_offset + 2)->AsObjectArray(); + for (jint i = 0; i < length; ++i) { + dst->Set(dstPos + i, src->Get(srcPos + i)); + } + } else if (ctype->IsPrimitiveChar()) { + CharArray* src = shadow_frame->GetVRegReference(arg_offset)->AsCharArray(); + CharArray* dst = shadow_frame->GetVRegReference(arg_offset + 2)->AsCharArray(); + for (jint i = 0; i < length; ++i) { + dst->Set(dstPos + i, src->Get(srcPos + i)); + } + } else if (ctype->IsPrimitiveInt()) { + IntArray* src = shadow_frame->GetVRegReference(arg_offset)->AsIntArray(); + IntArray* dst = shadow_frame->GetVRegReference(arg_offset + 2)->AsIntArray(); + for (jint i = 0; i < length; ++i) { + dst->Set(dstPos + i, src->Get(srcPos + i)); + } + } else { + UNIMPLEMENTED(FATAL) << "System.arraycopy of unexpected type: " << PrettyDescriptor(ctype); + } + } else { + // Not special, continue with regular interpreter execution. + artInterpreterToInterpreterBridge(self, mh, code_item, shadow_frame, result); + } +} + +// Explicit DoInvoke template function declarations. +#define EXPLICIT_DO_INVOKE_TEMPLATE_DECL(_type, _is_range_, _check) \ + template bool DoInvoke<_type, _is_range_, _check>(Thread* self, ShadowFrame& shadow_frame, \ + const Instruction* inst, JValue* result) + +#define EXPLICIT_DO_INVOKE_TEMPLATE_DECL_VARIANTS(_type) \ + EXPLICIT_DO_INVOKE_TEMPLATE_DECL(_type, false, false); \ + EXPLICIT_DO_INVOKE_TEMPLATE_DECL(_type, false, true); \ + EXPLICIT_DO_INVOKE_TEMPLATE_DECL(_type, true, false); \ + EXPLICIT_DO_INVOKE_TEMPLATE_DECL(_type, true, true) + +EXPLICIT_DO_INVOKE_TEMPLATE_DECL_VARIANTS(kStatic); +EXPLICIT_DO_INVOKE_TEMPLATE_DECL_VARIANTS(kDirect); +EXPLICIT_DO_INVOKE_TEMPLATE_DECL_VARIANTS(kVirtual); +EXPLICIT_DO_INVOKE_TEMPLATE_DECL_VARIANTS(kSuper); +EXPLICIT_DO_INVOKE_TEMPLATE_DECL_VARIANTS(kInterface); + +#undef EXPLICIT_DO_INVOKE_TEMPLATE_DECL_VARIANTS +#undef EXPLICIT_DO_INVOKE_TEMPLATE_DECL + +// Explicit DoInvokeVirtualQuick template function declarations. +#define EXPLICIT_DO_INVOKE_VIRTUAL_QUICK_TEMPLATE_DECL(_is_range) \ +template bool DoInvokeVirtualQuick<_is_range>(Thread* self, ShadowFrame& shadow_frame, \ + const Instruction* inst, JValue* result) + +EXPLICIT_DO_INVOKE_VIRTUAL_QUICK_TEMPLATE_DECL(false); +EXPLICIT_DO_INVOKE_VIRTUAL_QUICK_TEMPLATE_DECL(true); +#undef EXPLICIT_DO_INVOKE_VIRTUAL_QUICK_TEMPLATE_DECL + +// Explicit DoFilledNewArray template function declarations. +#define EXPLICIT_DO_FILLED_NEW_ARRAY_TEMPLATE_DECL(_is_range_, _check) \ + template bool DoFilledNewArray<_is_range_, _check>(const Instruction* inst, \ + const ShadowFrame& shadow_frame, \ + Thread* self, JValue* result) +EXPLICIT_DO_FILLED_NEW_ARRAY_TEMPLATE_DECL(false, false); +EXPLICIT_DO_FILLED_NEW_ARRAY_TEMPLATE_DECL(false, true); +EXPLICIT_DO_FILLED_NEW_ARRAY_TEMPLATE_DECL(true, false); +EXPLICIT_DO_FILLED_NEW_ARRAY_TEMPLATE_DECL(true, true); +#undef EXPLICIT_DO_FILLED_NEW_ARRAY_TEMPLATE_DECL + +} // namespace interpreter +} // namespace art diff --git a/runtime/interpreter/interpreter_common.h b/runtime/interpreter/interpreter_common.h new file mode 100644 index 00000000000..4d9317f6ce3 --- /dev/null +++ b/runtime/interpreter/interpreter_common.h @@ -0,0 +1,516 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ART_RUNTIME_INTERPRETER_INTERPRETER_COMMON_H_ +#define ART_RUNTIME_INTERPRETER_INTERPRETER_COMMON_H_ + +#include "interpreter.h" + +#include + +#include "base/logging.h" +#include "class_linker-inl.h" +#include "common_throws.h" +#include "dex_file-inl.h" +#include "dex_instruction-inl.h" +#include "dex_instruction.h" +#include "entrypoints/entrypoint_utils.h" +#include "gc/accounting/card_table-inl.h" +#include "invoke_arg_array_builder.h" +#include "nth_caller_visitor.h" +#include "mirror/art_field-inl.h" +#include "mirror/art_method.h" +#include "mirror/art_method-inl.h" +#include "mirror/class.h" +#include "mirror/class-inl.h" +#include "mirror/object-inl.h" +#include "mirror/object_array-inl.h" +#include "object_utils.h" +#include "ScopedLocalRef.h" +#include "scoped_thread_state_change.h" +#include "thread.h" +#include "well_known_classes.h" + +using ::art::mirror::ArtField; +using ::art::mirror::ArtMethod; +using ::art::mirror::Array; +using ::art::mirror::BooleanArray; +using ::art::mirror::ByteArray; +using ::art::mirror::CharArray; +using ::art::mirror::Class; +using ::art::mirror::ClassLoader; +using ::art::mirror::IntArray; +using ::art::mirror::LongArray; +using ::art::mirror::Object; +using ::art::mirror::ObjectArray; +using ::art::mirror::ShortArray; +using ::art::mirror::String; +using ::art::mirror::Throwable; + +namespace art { +namespace interpreter { + +// External references to both interpreter implementations. + +// TODO: should be SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) which is failing due to template +// specialization. +template +extern JValue ExecuteSwitchImpl(Thread* self, MethodHelper& mh, + const DexFile::CodeItem* code_item, + ShadowFrame& shadow_frame, JValue result_register) + NO_THREAD_SAFETY_ANALYSIS __attribute__((hot)); + +// TODO: should be SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) which is failing due to template +// specialization. +template +extern JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, + const DexFile::CodeItem* code_item, + ShadowFrame& shadow_frame, JValue result_register) + NO_THREAD_SAFETY_ANALYSIS __attribute__((hot)); + +// Common part of both implementations. +static const int32_t kMaxInt = std::numeric_limits::max(); +static const int32_t kMinInt = std::numeric_limits::min(); +static const int64_t kMaxLong = std::numeric_limits::max(); +static const int64_t kMinLong = std::numeric_limits::min(); + +void UnstartedRuntimeInvoke(Thread* self, MethodHelper& mh, + const DexFile::CodeItem* code_item, ShadowFrame* shadow_frame, + JValue* result, size_t arg_offset) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + +static inline void DoMonitorEnter(Thread* self, Object* ref) NO_THREAD_SAFETY_ANALYSIS { + ref->MonitorEnter(self); +} + +static inline void DoMonitorExit(Thread* self, Object* ref) NO_THREAD_SAFETY_ANALYSIS { + ref->MonitorExit(self); +} + +// TODO: should be SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) which is failing due to template +// specialization. +template +bool DoInvoke(Thread* self, ShadowFrame& shadow_frame, + const Instruction* inst, JValue* result) NO_THREAD_SAFETY_ANALYSIS; + +// TODO: should be SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) which is failing due to template +// specialization. +template +bool DoInvokeVirtualQuick(Thread* self, ShadowFrame& shadow_frame, + const Instruction* inst, JValue* result) + NO_THREAD_SAFETY_ANALYSIS; + +// We use template functions to optimize compiler inlining process. Otherwise, +// some parts of the code (like a switch statement) which depend on a constant +// parameter would not be inlined while it should be. These constant parameters +// are now part of the template arguments. +// Note these template functions are static and inlined so they should not be +// part of the final object file. +// TODO: should be SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) which is failing due to template +// specialization. +template +static bool DoFieldGet(Thread* self, ShadowFrame& shadow_frame, + const Instruction* inst) + NO_THREAD_SAFETY_ANALYSIS ALWAYS_INLINE; + +template +static inline bool DoFieldGet(Thread* self, ShadowFrame& shadow_frame, + const Instruction* inst) { + bool is_static = (find_type == StaticObjectRead) || (find_type == StaticPrimitiveRead); + uint32_t field_idx = is_static ? inst->VRegB_21c() : inst->VRegC_22c(); + ArtField* f = FindFieldFromCode(field_idx, shadow_frame.GetMethod(), self, + find_type, Primitive::FieldSize(field_type), + do_access_check); + if (UNLIKELY(f == NULL)) { + CHECK(self->IsExceptionPending()); + return false; + } + Object* obj; + if (is_static) { + obj = f->GetDeclaringClass(); + } else { + obj = shadow_frame.GetVRegReference(inst->VRegB_22c()); + if (UNLIKELY(obj == NULL)) { + ThrowNullPointerExceptionForFieldAccess(shadow_frame.GetCurrentLocationForThrow(), f, true); + return false; + } + } + uint32_t vregA = is_static ? inst->VRegA_21c() : inst->VRegA_22c(); + switch (field_type) { + case Primitive::kPrimBoolean: + shadow_frame.SetVReg(vregA, f->GetBoolean(obj)); + break; + case Primitive::kPrimByte: + shadow_frame.SetVReg(vregA, f->GetByte(obj)); + break; + case Primitive::kPrimChar: + shadow_frame.SetVReg(vregA, f->GetChar(obj)); + break; + case Primitive::kPrimShort: + shadow_frame.SetVReg(vregA, f->GetShort(obj)); + break; + case Primitive::kPrimInt: + shadow_frame.SetVReg(vregA, f->GetInt(obj)); + break; + case Primitive::kPrimLong: + shadow_frame.SetVRegLong(vregA, f->GetLong(obj)); + break; + case Primitive::kPrimNot: + shadow_frame.SetVRegReference(vregA, f->GetObject(obj)); + break; + default: + LOG(FATAL) << "Unreachable: " << field_type; + } + return true; +} + +// TODO: should be SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) which is failing due to template +// specialization. +template +static bool DoIGetQuick(Thread* self, ShadowFrame& shadow_frame, + const Instruction* inst) + NO_THREAD_SAFETY_ANALYSIS ALWAYS_INLINE; + +template +static inline bool DoIGetQuick(Thread* self, ShadowFrame& shadow_frame, + const Instruction* inst) { + Object* obj = shadow_frame.GetVRegReference(inst->VRegB_22c()); + if (UNLIKELY(obj == NULL)) { + // We lost the reference to the field index so we cannot get a more + // precised exception message. + ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow()); + return false; + } + MemberOffset field_offset(inst->VRegC_22c()); + const bool is_volatile = false; // iget-x-quick only on non volatile fields. + const uint32_t vregA = inst->VRegA_22c(); + switch (field_type) { + case Primitive::kPrimInt: + shadow_frame.SetVReg(vregA, static_cast(obj->GetField32(field_offset, is_volatile))); + break; + case Primitive::kPrimLong: + shadow_frame.SetVRegLong(vregA, static_cast(obj->GetField64(field_offset, is_volatile))); + break; + case Primitive::kPrimNot: + shadow_frame.SetVRegReference(vregA, obj->GetFieldObject(field_offset, is_volatile)); + break; + default: + LOG(FATAL) << "Unreachable: " << field_type; + } + return true; +} + +// TODO: should be SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) which is failing due to template +// specialization. +template +static bool DoFieldPut(Thread* self, const ShadowFrame& shadow_frame, + const Instruction* inst) + NO_THREAD_SAFETY_ANALYSIS ALWAYS_INLINE; + +template +static inline bool DoFieldPut(Thread* self, const ShadowFrame& shadow_frame, + const Instruction* inst) { + bool is_static = (find_type == StaticObjectWrite) || (find_type == StaticPrimitiveWrite); + uint32_t field_idx = is_static ? inst->VRegB_21c() : inst->VRegC_22c(); + ArtField* f = FindFieldFromCode(field_idx, shadow_frame.GetMethod(), self, + find_type, Primitive::FieldSize(field_type), + do_access_check); + if (UNLIKELY(f == NULL)) { + CHECK(self->IsExceptionPending()); + return false; + } + Object* obj; + if (is_static) { + obj = f->GetDeclaringClass(); + } else { + obj = shadow_frame.GetVRegReference(inst->VRegB_22c()); + if (UNLIKELY(obj == NULL)) { + ThrowNullPointerExceptionForFieldAccess(shadow_frame.GetCurrentLocationForThrow(), + f, false); + return false; + } + } + uint32_t vregA = is_static ? inst->VRegA_21c() : inst->VRegA_22c(); + switch (field_type) { + case Primitive::kPrimBoolean: + f->SetBoolean(obj, shadow_frame.GetVReg(vregA)); + break; + case Primitive::kPrimByte: + f->SetByte(obj, shadow_frame.GetVReg(vregA)); + break; + case Primitive::kPrimChar: + f->SetChar(obj, shadow_frame.GetVReg(vregA)); + break; + case Primitive::kPrimShort: + f->SetShort(obj, shadow_frame.GetVReg(vregA)); + break; + case Primitive::kPrimInt: + f->SetInt(obj, shadow_frame.GetVReg(vregA)); + break; + case Primitive::kPrimLong: + f->SetLong(obj, shadow_frame.GetVRegLong(vregA)); + break; + case Primitive::kPrimNot: + f->SetObj(obj, shadow_frame.GetVRegReference(vregA)); + break; + default: + LOG(FATAL) << "Unreachable: " << field_type; + } + return true; +} + +// TODO: should be SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) which is failing due to template +// specialization. +template +static bool DoIPutQuick(Thread* self, ShadowFrame& shadow_frame, + const Instruction* inst) + NO_THREAD_SAFETY_ANALYSIS ALWAYS_INLINE; + +template +static inline bool DoIPutQuick(Thread* self, ShadowFrame& shadow_frame, + const Instruction* inst) { + Object* obj = shadow_frame.GetVRegReference(inst->VRegB_22c()); + if (UNLIKELY(obj == NULL)) { + // We lost the reference to the field index so we cannot get a more + // precised exception message. + ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow()); + return false; + } + MemberOffset field_offset(inst->VRegC_22c()); + const bool is_volatile = false; // iput-x-quick only on non volatile fields. + const uint32_t vregA = inst->VRegA_22c(); + switch (field_type) { + case Primitive::kPrimInt: + obj->SetField32(field_offset, shadow_frame.GetVReg(vregA), is_volatile); + break; + case Primitive::kPrimLong: + obj->SetField64(field_offset, shadow_frame.GetVRegLong(vregA), is_volatile); + break; + case Primitive::kPrimNot: + obj->SetFieldObject(field_offset, shadow_frame.GetVRegReference(vregA), is_volatile); + break; + default: + LOG(FATAL) << "Unreachable: " << field_type; + } + return true; +} + +static inline String* ResolveString(Thread* self, MethodHelper& mh, uint32_t string_idx) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + Class* java_lang_string_class = String::GetJavaLangString(); + if (UNLIKELY(!java_lang_string_class->IsInitialized())) { + ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); + if (UNLIKELY(!class_linker->EnsureInitialized(java_lang_string_class, + true, true))) { + DCHECK(self->IsExceptionPending()); + return NULL; + } + } + return mh.ResolveString(string_idx); +} + +static inline bool DoIntDivide(ShadowFrame& shadow_frame, size_t result_reg, + int32_t dividend, int32_t divisor) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + if (UNLIKELY(divisor == 0)) { + ThrowArithmeticExceptionDivideByZero(); + return false; + } + if (UNLIKELY(dividend == kMinInt && divisor == -1)) { + shadow_frame.SetVReg(result_reg, kMinInt); + } else { + shadow_frame.SetVReg(result_reg, dividend / divisor); + } + return true; +} + +static inline bool DoIntRemainder(ShadowFrame& shadow_frame, size_t result_reg, + int32_t dividend, int32_t divisor) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + if (UNLIKELY(divisor == 0)) { + ThrowArithmeticExceptionDivideByZero(); + return false; + } + if (UNLIKELY(dividend == kMinInt && divisor == -1)) { + shadow_frame.SetVReg(result_reg, 0); + } else { + shadow_frame.SetVReg(result_reg, dividend % divisor); + } + return true; +} + +static inline bool DoLongDivide(ShadowFrame& shadow_frame, size_t result_reg, + int64_t dividend, int64_t divisor) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + if (UNLIKELY(divisor == 0)) { + ThrowArithmeticExceptionDivideByZero(); + return false; + } + if (UNLIKELY(dividend == kMinLong && divisor == -1)) { + shadow_frame.SetVRegLong(result_reg, kMinLong); + } else { + shadow_frame.SetVRegLong(result_reg, dividend / divisor); + } + return true; +} + +static inline bool DoLongRemainder(ShadowFrame& shadow_frame, size_t result_reg, + int64_t dividend, int64_t divisor) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + if (UNLIKELY(divisor == 0)) { + ThrowArithmeticExceptionDivideByZero(); + return false; + } + if (UNLIKELY(dividend == kMinLong && divisor == -1)) { + shadow_frame.SetVRegLong(result_reg, 0); + } else { + shadow_frame.SetVRegLong(result_reg, dividend % divisor); + } + return true; +} + +// TODO: should be SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) which is failing due to template +// specialization. +// Returns true on success, otherwise throws an exception and returns false. +template +bool DoFilledNewArray(const Instruction* inst, const ShadowFrame& shadow_frame, + Thread* self, JValue* result) NO_THREAD_SAFETY_ANALYSIS; + +static inline int32_t DoPackedSwitch(const Instruction* inst, + const ShadowFrame& shadow_frame) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + DCHECK(inst->Opcode() == Instruction::PACKED_SWITCH); + const uint16_t* switch_data = reinterpret_cast(inst) + inst->VRegB_31t(); + int32_t test_val = shadow_frame.GetVReg(inst->VRegA_31t()); + DCHECK_EQ(switch_data[0], static_cast(Instruction::kPackedSwitchSignature)); + uint16_t size = switch_data[1]; + DCHECK_GT(size, 0); + const int32_t* keys = reinterpret_cast(&switch_data[2]); + DCHECK(IsAligned<4>(keys)); + int32_t first_key = keys[0]; + const int32_t* targets = reinterpret_cast(&switch_data[4]); + DCHECK(IsAligned<4>(targets)); + int32_t index = test_val - first_key; + if (index >= 0 && index < size) { + return targets[index]; + } else { + // No corresponding value: move forward by 3 (size of PACKED_SWITCH). + return 3; + } +} + +static inline int32_t DoSparseSwitch(const Instruction* inst, + const ShadowFrame& shadow_frame) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + DCHECK(inst->Opcode() == Instruction::SPARSE_SWITCH); + const uint16_t* switch_data = reinterpret_cast(inst) + inst->VRegB_31t(); + int32_t test_val = shadow_frame.GetVReg(inst->VRegA_31t()); + DCHECK_EQ(switch_data[0], static_cast(Instruction::kSparseSwitchSignature)); + uint16_t size = switch_data[1]; + DCHECK_GT(size, 0); + const int32_t* keys = reinterpret_cast(&switch_data[2]); + DCHECK(IsAligned<4>(keys)); + const int32_t* entries = keys + size; + DCHECK(IsAligned<4>(entries)); + int lo = 0; + int hi = size - 1; + while (lo <= hi) { + int mid = (lo + hi) / 2; + int32_t foundVal = keys[mid]; + if (test_val < foundVal) { + hi = mid - 1; + } else if (test_val > foundVal) { + lo = mid + 1; + } else { + return entries[mid]; + } + } + // No corresponding value: move forward by 3 (size of SPARSE_SWITCH). + return 3; +} + +static inline uint32_t FindNextInstructionFollowingException(Thread* self, + ShadowFrame& shadow_frame, + uint32_t dex_pc, + SirtRef& this_object_ref, + instrumentation::Instrumentation* instrumentation) + ALWAYS_INLINE; + +static inline uint32_t FindNextInstructionFollowingException(Thread* self, + ShadowFrame& shadow_frame, + uint32_t dex_pc, + SirtRef& this_object_ref, + instrumentation::Instrumentation* instrumentation) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + self->VerifyStack(); + ThrowLocation throw_location; + mirror::Throwable* exception = self->GetException(&throw_location); + bool clear_exception; + uint32_t found_dex_pc = shadow_frame.GetMethod()->FindCatchBlock(exception->GetClass(), dex_pc, + &clear_exception); + if (found_dex_pc == DexFile::kDexNoIndex) { + instrumentation->MethodUnwindEvent(self, this_object_ref.get(), + shadow_frame.GetMethod(), dex_pc); + } else { + instrumentation->ExceptionCaughtEvent(self, throw_location, + shadow_frame.GetMethod(), + found_dex_pc, exception); + if (clear_exception) { + self->ClearException(); + } + } + return found_dex_pc; +} + +static void UnexpectedOpcode(const Instruction* inst, MethodHelper& mh) + __attribute__((cold, noreturn, noinline)); + +static void UnexpectedOpcode(const Instruction* inst, MethodHelper& mh) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + LOG(FATAL) << "Unexpected instruction: " << inst->DumpString(&mh.GetDexFile()); + exit(0); // Unreachable, keep GCC happy. +} + +static inline void TraceExecution(const ShadowFrame& shadow_frame, const Instruction* inst, + const uint32_t dex_pc, MethodHelper& mh) { + const bool kTracing = false; + if (kTracing) { +#define TRACE_LOG std::cerr + TRACE_LOG << PrettyMethod(shadow_frame.GetMethod()) + << StringPrintf("\n0x%x: ", dex_pc) + << inst->DumpString(&mh.GetDexFile()) << "\n"; + for (size_t i = 0; i < shadow_frame.NumberOfVRegs(); ++i) { + uint32_t raw_value = shadow_frame.GetVReg(i); + Object* ref_value = shadow_frame.GetVRegReference(i); + TRACE_LOG << StringPrintf(" vreg%d=0x%08X", i, raw_value); + if (ref_value != NULL) { + if (ref_value->GetClass()->IsStringClass() && + ref_value->AsString()->GetCharArray() != NULL) { + TRACE_LOG << "/java.lang.String \"" << ref_value->AsString()->ToModifiedUtf8() << "\""; + } else { + TRACE_LOG << "/" << PrettyTypeOf(ref_value); + } + } + } + TRACE_LOG << "\n"; +#undef TRACE_LOG + } +} + +} // namespace interpreter +} // namespace art + +#endif // ART_RUNTIME_INTERPRETER_INTERPRETER_COMMON_H_ diff --git a/runtime/interpreter/interpreter_goto_table_impl.cc b/runtime/interpreter/interpreter_goto_table_impl.cc new file mode 100644 index 00000000000..f76fbfb30f5 --- /dev/null +++ b/runtime/interpreter/interpreter_goto_table_impl.cc @@ -0,0 +1,2343 @@ +/* + * Copyright (C) 2012 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 "interpreter_common.h" + +namespace art { +namespace interpreter { + +// In the following macros, we expect the following local variables exist: +// - "self": the current Thread*. +// - "inst" : the current Instruction*. +// - "dex_pc": the current pc. +// - "shadow_frame": the current shadow frame. +// - "insns": the start of current method's code item. +// - "mh": the current MethodHelper. +// - "currentHandlersTable": the current table of pointer to each instruction handler. + +// Advance to the next instruction and updates interpreter state. +// TODO: move check suspend to backward branch, return and exception handling. +#define ADVANCE(_offset) \ + do { \ + int32_t disp = static_cast(_offset); \ + inst = inst->RelativeAt(disp); \ + dex_pc = static_cast(static_cast(dex_pc) + disp); \ + shadow_frame.SetDexPC(dex_pc); \ + if (UNLIKELY(self->TestAllFlags())) { \ + CheckSuspend(self); \ + } \ + TraceExecution(shadow_frame, inst, dex_pc, mh); \ + goto *currentHandlersTable[inst->Opcode()]; \ + } while (false) + +#define HANDLE_PENDING_EXCEPTION() goto exception_pending_label + +#define POSSIBLY_HANDLE_PENDING_EXCEPTION(_is_exception_pending, _offset) \ + do { \ + if (UNLIKELY(_is_exception_pending)) { \ + HANDLE_PENDING_EXCEPTION(); \ + } else { \ + ADVANCE(_offset); \ + } \ + } while (false) + +#define UNREACHABLE_CODE_CHECK() \ + do { \ + if (kIsDebugBuild) { \ + LOG(FATAL) << "We should not be here !"; \ + } \ + } while (false) + +#define HANDLE_INSTRUCTION_START(opcode) op_##opcode: // NOLINT(whitespace/labels) +#define HANDLE_INSTRUCTION_END() UNREACHABLE_CODE_CHECK() + +static inline bool IsBackwardBranch(int32_t branch_offset) { + return branch_offset <= 0; +} + +template +JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* code_item, + ShadowFrame& shadow_frame, JValue result_register) { + if (UNLIKELY(!shadow_frame.HasReferenceArray())) { + LOG(FATAL) << "Invalid shadow frame for interpreter use"; + return JValue(); + } + self->VerifyStack(); + instrumentation::Instrumentation* const instrumentation = Runtime::Current()->GetInstrumentation(); + + // As the 'this' object won't change during the execution of current code, we + // want to cache it in local variables. Nevertheless, in order to let the + // garbage collector access it, we store it into sirt references. + SirtRef this_object_ref(self, shadow_frame.GetThisObject(code_item->ins_size_)); + + uint32_t dex_pc = shadow_frame.GetDexPC(); + if (LIKELY(dex_pc == 0)) { // We are entering the method as opposed to deoptimizing.. + if (UNLIKELY(instrumentation->HasMethodEntryListeners())) { + instrumentation->MethodEnterEvent(self, this_object_ref.get(), + shadow_frame.GetMethod(), 0); + } + } + const uint16_t* const insns = code_item->insns_; + const Instruction* inst = Instruction::At(insns + dex_pc); + + // Define handlers table. + static const void* handlersTable[kNumPackedOpcodes] = { +#define INSTRUCTION_HANDLER(o, code, n, f, r, i, a, v) &&op_##code, +#include "dex_instruction_list.h" + DEX_INSTRUCTION_LIST(INSTRUCTION_HANDLER) +#undef DEX_INSTRUCTION_LIST +#undef INSTRUCTION_HANDLER + }; + + static const void* instrumentationHandlersTable[kNumPackedOpcodes] = { +#define INSTRUCTION_HANDLER(o, code, n, f, r, i, a, v) &&instrumentation_op_##code, +#include "dex_instruction_list.h" + DEX_INSTRUCTION_LIST(INSTRUCTION_HANDLER) +#undef DEX_INSTRUCTION_LIST +#undef INSTRUCTION_HANDLER + }; + + const void** currentHandlersTable; + if (UNLIKELY(instrumentation->HasDexPcListeners())) { + currentHandlersTable = instrumentationHandlersTable; + } else { + currentHandlersTable = handlersTable; + } + + // Jump to first instruction. + ADVANCE(0); + UNREACHABLE_CODE_CHECK(); + + HANDLE_INSTRUCTION_START(NOP) + ADVANCE(1); + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(MOVE) + shadow_frame.SetVReg(inst->VRegA_12x(), + shadow_frame.GetVReg(inst->VRegB_12x())); + ADVANCE(1); + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(MOVE_FROM16) + shadow_frame.SetVReg(inst->VRegA_22x(), + shadow_frame.GetVReg(inst->VRegB_22x())); + ADVANCE(2); + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(MOVE_16) + shadow_frame.SetVReg(inst->VRegA_32x(), + shadow_frame.GetVReg(inst->VRegB_32x())); + ADVANCE(3); + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(MOVE_WIDE) + shadow_frame.SetVRegLong(inst->VRegA_12x(), + shadow_frame.GetVRegLong(inst->VRegB_12x())); + ADVANCE(1); + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(MOVE_WIDE_FROM16) + shadow_frame.SetVRegLong(inst->VRegA_22x(), + shadow_frame.GetVRegLong(inst->VRegB_22x())); + ADVANCE(2); + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(MOVE_WIDE_16) + shadow_frame.SetVRegLong(inst->VRegA_32x(), + shadow_frame.GetVRegLong(inst->VRegB_32x())); + ADVANCE(3); + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(MOVE_OBJECT) + shadow_frame.SetVRegReference(inst->VRegA_12x(), + shadow_frame.GetVRegReference(inst->VRegB_12x())); + ADVANCE(1); + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(MOVE_OBJECT_FROM16) + shadow_frame.SetVRegReference(inst->VRegA_22x(), + shadow_frame.GetVRegReference(inst->VRegB_22x())); + ADVANCE(2); + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(MOVE_OBJECT_16) + shadow_frame.SetVRegReference(inst->VRegA_32x(), + shadow_frame.GetVRegReference(inst->VRegB_32x())); + ADVANCE(3); + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(MOVE_RESULT) + shadow_frame.SetVReg(inst->VRegA_11x(), result_register.GetI()); + ADVANCE(1); + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(MOVE_RESULT_WIDE) + shadow_frame.SetVRegLong(inst->VRegA_11x(), result_register.GetJ()); + ADVANCE(1); + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(MOVE_RESULT_OBJECT) + shadow_frame.SetVRegReference(inst->VRegA_11x(), result_register.GetL()); + ADVANCE(1); + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(MOVE_EXCEPTION) { + Throwable* exception = self->GetException(NULL); + self->ClearException(); + shadow_frame.SetVRegReference(inst->VRegA_11x(), exception); + ADVANCE(1); + } + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(RETURN_VOID) { + JValue result; + if (UNLIKELY(instrumentation->HasMethodExitListeners())) { + instrumentation->MethodExitEvent(self, this_object_ref.get(), + shadow_frame.GetMethod(), dex_pc, + result); + } + return result; + } + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(RETURN_VOID_BARRIER) { + ANDROID_MEMBAR_STORE(); + JValue result; + if (UNLIKELY(instrumentation->HasMethodExitListeners())) { + instrumentation->MethodExitEvent(self, this_object_ref.get(), + shadow_frame.GetMethod(), dex_pc, + result); + } + return result; + } + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(RETURN) { + JValue result; + result.SetJ(0); + result.SetI(shadow_frame.GetVReg(inst->VRegA_11x())); + if (UNLIKELY(instrumentation->HasMethodExitListeners())) { + instrumentation->MethodExitEvent(self, this_object_ref.get(), + shadow_frame.GetMethod(), dex_pc, + result); + } + return result; + } + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(RETURN_WIDE) { + JValue result; + result.SetJ(shadow_frame.GetVRegLong(inst->VRegA_11x())); + if (UNLIKELY(instrumentation->HasMethodExitListeners())) { + instrumentation->MethodExitEvent(self, this_object_ref.get(), + shadow_frame.GetMethod(), dex_pc, + result); + } + return result; + } + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(RETURN_OBJECT) { + JValue result; + result.SetJ(0); + result.SetL(shadow_frame.GetVRegReference(inst->VRegA_11x())); + if (UNLIKELY(instrumentation->HasMethodExitListeners())) { + instrumentation->MethodExitEvent(self, this_object_ref.get(), + shadow_frame.GetMethod(), dex_pc, + result); + } + return result; + } + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(CONST_4) { + uint32_t dst = inst->VRegA_11n(); + int32_t val = inst->VRegB_11n(); + shadow_frame.SetVReg(dst, val); + if (val == 0) { + shadow_frame.SetVRegReference(dst, NULL); + } + ADVANCE(1); + } + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(CONST_16) { + uint32_t dst = inst->VRegA_21s(); + int32_t val = inst->VRegB_21s(); + shadow_frame.SetVReg(dst, val); + if (val == 0) { + shadow_frame.SetVRegReference(dst, NULL); + } + ADVANCE(2); + } + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(CONST) { + uint32_t dst = inst->VRegA_31i(); + int32_t val = inst->VRegB_31i(); + shadow_frame.SetVReg(dst, val); + if (val == 0) { + shadow_frame.SetVRegReference(dst, NULL); + } + ADVANCE(3); + } + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(CONST_HIGH16) { + uint32_t dst = inst->VRegA_21h(); + int32_t val = static_cast(inst->VRegB_21h() << 16); + shadow_frame.SetVReg(dst, val); + if (val == 0) { + shadow_frame.SetVRegReference(dst, NULL); + } + ADVANCE(2); + } + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(CONST_WIDE_16) + shadow_frame.SetVRegLong(inst->VRegA_21s(), inst->VRegB_21s()); + ADVANCE(2); + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(CONST_WIDE_32) + shadow_frame.SetVRegLong(inst->VRegA_31i(), inst->VRegB_31i()); + ADVANCE(3); + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(CONST_WIDE) + shadow_frame.SetVRegLong(inst->VRegA_51l(), inst->VRegB_51l()); + ADVANCE(5); + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(CONST_WIDE_HIGH16) + shadow_frame.SetVRegLong(inst->VRegA_21h(), + static_cast(inst->VRegB_21h()) << 48); + ADVANCE(2); + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(CONST_STRING) { + String* s = ResolveString(self, mh, inst->VRegB_21c()); + if (UNLIKELY(s == NULL)) { + HANDLE_PENDING_EXCEPTION(); + } else { + shadow_frame.SetVRegReference(inst->VRegA_21c(), s); + ADVANCE(2); + } + } + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(CONST_STRING_JUMBO) { + String* s = ResolveString(self, mh, inst->VRegB_31c()); + if (UNLIKELY(s == NULL)) { + HANDLE_PENDING_EXCEPTION(); + } else { + shadow_frame.SetVRegReference(inst->VRegA_31c(), s); + ADVANCE(3); + } + } + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(CONST_CLASS) { + Class* c = ResolveVerifyAndClinit(inst->VRegB_21c(), shadow_frame.GetMethod(), + self, false, do_access_check); + if (UNLIKELY(c == NULL)) { + HANDLE_PENDING_EXCEPTION(); + } else { + shadow_frame.SetVRegReference(inst->VRegA_21c(), c); + ADVANCE(2); + } + } + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(MONITOR_ENTER) { + Object* obj = shadow_frame.GetVRegReference(inst->VRegA_11x()); + if (UNLIKELY(obj == NULL)) { + ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow()); + HANDLE_PENDING_EXCEPTION(); + } else { + DoMonitorEnter(self, obj); + POSSIBLY_HANDLE_PENDING_EXCEPTION(self->IsExceptionPending(), 1); + } + } + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(MONITOR_EXIT) { + Object* obj = shadow_frame.GetVRegReference(inst->VRegA_11x()); + if (UNLIKELY(obj == NULL)) { + ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow()); + HANDLE_PENDING_EXCEPTION(); + } else { + DoMonitorExit(self, obj); + POSSIBLY_HANDLE_PENDING_EXCEPTION(self->IsExceptionPending(), 1); + } + } + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(CHECK_CAST) { + Class* c = ResolveVerifyAndClinit(inst->VRegB_21c(), shadow_frame.GetMethod(), + self, false, do_access_check); + if (UNLIKELY(c == NULL)) { + HANDLE_PENDING_EXCEPTION(); + } else { + Object* obj = shadow_frame.GetVRegReference(inst->VRegA_21c()); + if (UNLIKELY(obj != NULL && !obj->InstanceOf(c))) { + ThrowClassCastException(c, obj->GetClass()); + HANDLE_PENDING_EXCEPTION(); + } else { + ADVANCE(2); + } + } + } + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(INSTANCE_OF) { + Class* c = ResolveVerifyAndClinit(inst->VRegC_22c(), shadow_frame.GetMethod(), + self, false, do_access_check); + if (UNLIKELY(c == NULL)) { + HANDLE_PENDING_EXCEPTION(); + } else { + Object* obj = shadow_frame.GetVRegReference(inst->VRegB_22c()); + shadow_frame.SetVReg(inst->VRegA_22c(), (obj != NULL && obj->InstanceOf(c)) ? 1 : 0); + ADVANCE(2); + } + } + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(ARRAY_LENGTH) { + Object* array = shadow_frame.GetVRegReference(inst->VRegB_12x()); + if (UNLIKELY(array == NULL)) { + ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow()); + HANDLE_PENDING_EXCEPTION(); + } else { + shadow_frame.SetVReg(inst->VRegA_12x(), array->AsArray()->GetLength()); + ADVANCE(1); + } + } + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(NEW_INSTANCE) { + Object* obj = AllocObjectFromCode(inst->VRegB_21c(), shadow_frame.GetMethod(), + self, do_access_check); + if (UNLIKELY(obj == NULL)) { + HANDLE_PENDING_EXCEPTION(); + } else { + shadow_frame.SetVRegReference(inst->VRegA_21c(), obj); + ADVANCE(2); + } + } + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(NEW_ARRAY) { + int32_t length = shadow_frame.GetVReg(inst->VRegB_22c()); + Object* obj = AllocArrayFromCode(inst->VRegC_22c(), shadow_frame.GetMethod(), + length, self, do_access_check); + if (UNLIKELY(obj == NULL)) { + HANDLE_PENDING_EXCEPTION(); + } else { + shadow_frame.SetVRegReference(inst->VRegA_22c(), obj); + ADVANCE(2); + } + } + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(FILLED_NEW_ARRAY) { + bool success = DoFilledNewArray(inst, shadow_frame, + self, &result_register); + POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 3); + } + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(FILLED_NEW_ARRAY_RANGE) { + bool success = DoFilledNewArray(inst, shadow_frame, + self, &result_register); + POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 3); + } + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(FILL_ARRAY_DATA) { + Object* obj = shadow_frame.GetVRegReference(inst->VRegA_31t()); + if (UNLIKELY(obj == NULL)) { + ThrowNullPointerException(NULL, "null array in FILL_ARRAY_DATA"); + HANDLE_PENDING_EXCEPTION(); + } else { + Array* array = obj->AsArray(); + DCHECK(array->IsArrayInstance() && !array->IsObjectArray()); + const uint16_t* payload_addr = reinterpret_cast(inst) + inst->VRegB_31t(); + const Instruction::ArrayDataPayload* payload = + reinterpret_cast(payload_addr); + if (UNLIKELY(static_cast(payload->element_count) > array->GetLength())) { + self->ThrowNewExceptionF(shadow_frame.GetCurrentLocationForThrow(), + "Ljava/lang/ArrayIndexOutOfBoundsException;", + "failed FILL_ARRAY_DATA; length=%d, index=%d", + array->GetLength(), payload->element_count); + HANDLE_PENDING_EXCEPTION(); + } else { + uint32_t size_in_bytes = payload->element_count * payload->element_width; + memcpy(array->GetRawData(payload->element_width), payload->data, size_in_bytes); + ADVANCE(3); + } + } + } + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(THROW) { + Object* exception = shadow_frame.GetVRegReference(inst->VRegA_11x()); + if (UNLIKELY(exception == NULL)) { + ThrowNullPointerException(NULL, "throw with null exception"); + } else { + self->SetException(shadow_frame.GetCurrentLocationForThrow(), exception->AsThrowable()); + } + HANDLE_PENDING_EXCEPTION(); + } + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(GOTO) { + int8_t offset = inst->VRegA_10t(); + if (IsBackwardBranch(offset)) { + if (UNLIKELY(instrumentation->HasDexPcListeners())) { + currentHandlersTable = instrumentationHandlersTable; + } else { + currentHandlersTable = handlersTable; + } + } + ADVANCE(offset); + } + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(GOTO_16) { + int16_t offset = inst->VRegA_20t(); + if (IsBackwardBranch(offset)) { + if (UNLIKELY(instrumentation->HasDexPcListeners())) { + currentHandlersTable = instrumentationHandlersTable; + } else { + currentHandlersTable = handlersTable; + } + } + ADVANCE(offset); + } + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(GOTO_32) { + int32_t offset = inst->VRegA_30t(); + if (IsBackwardBranch(offset)) { + if (UNLIKELY(instrumentation->HasDexPcListeners())) { + currentHandlersTable = instrumentationHandlersTable; + } else { + currentHandlersTable = handlersTable; + } + } + ADVANCE(offset); + } + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(PACKED_SWITCH) { + int32_t offset = DoPackedSwitch(inst, shadow_frame); + if (IsBackwardBranch(offset)) { + if (UNLIKELY(instrumentation->HasDexPcListeners())) { + currentHandlersTable = instrumentationHandlersTable; + } else { + currentHandlersTable = handlersTable; + } + } + ADVANCE(offset); + } + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(SPARSE_SWITCH) { + int32_t offset = DoSparseSwitch(inst, shadow_frame); + if (IsBackwardBranch(offset)) { + if (UNLIKELY(instrumentation->HasDexPcListeners())) { + currentHandlersTable = instrumentationHandlersTable; + } else { + currentHandlersTable = handlersTable; + } + } + ADVANCE(offset); + } + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(CMPL_FLOAT) { + 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(), result); + ADVANCE(2); + } + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(CMPG_FLOAT) { + 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(), result); + ADVANCE(2); + } + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(CMPL_DOUBLE) { + 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(), result); + ADVANCE(2); + } + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(CMPG_DOUBLE) { + 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(), result); + ADVANCE(2); + } + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(CMP_LONG) { + 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(), result); + ADVANCE(2); + } + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(IF_EQ) { + if (shadow_frame.GetVReg(inst->VRegA_22t()) == shadow_frame.GetVReg(inst->VRegB_22t())) { + int16_t offset = inst->VRegC_22t(); + if (IsBackwardBranch(offset)) { + if (UNLIKELY(instrumentation->HasDexPcListeners())) { + currentHandlersTable = instrumentationHandlersTable; + } else { + currentHandlersTable = handlersTable; + } + } + ADVANCE(offset); + } else { + ADVANCE(2); + } + } + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(IF_NE) { + if (shadow_frame.GetVReg(inst->VRegA_22t()) != shadow_frame.GetVReg(inst->VRegB_22t())) { + int16_t offset = inst->VRegC_22t(); + if (IsBackwardBranch(offset)) { + if (UNLIKELY(instrumentation->HasDexPcListeners())) { + currentHandlersTable = instrumentationHandlersTable; + } else { + currentHandlersTable = handlersTable; + } + } + ADVANCE(offset); + } else { + ADVANCE(2); + } + } + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(IF_LT) { + if (shadow_frame.GetVReg(inst->VRegA_22t()) < shadow_frame.GetVReg(inst->VRegB_22t())) { + int16_t offset = inst->VRegC_22t(); + if (IsBackwardBranch(offset)) { + if (UNLIKELY(instrumentation->HasDexPcListeners())) { + currentHandlersTable = instrumentationHandlersTable; + } else { + currentHandlersTable = handlersTable; + } + } + ADVANCE(offset); + } else { + ADVANCE(2); + } + } + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(IF_GE) { + if (shadow_frame.GetVReg(inst->VRegA_22t()) >= shadow_frame.GetVReg(inst->VRegB_22t())) { + int16_t offset = inst->VRegC_22t(); + if (IsBackwardBranch(offset)) { + if (UNLIKELY(instrumentation->HasDexPcListeners())) { + currentHandlersTable = instrumentationHandlersTable; + } else { + currentHandlersTable = handlersTable; + } + } + ADVANCE(offset); + } else { + ADVANCE(2); + } + } + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(IF_GT) { + if (shadow_frame.GetVReg(inst->VRegA_22t()) > shadow_frame.GetVReg(inst->VRegB_22t())) { + int16_t offset = inst->VRegC_22t(); + if (IsBackwardBranch(offset)) { + if (UNLIKELY(instrumentation->HasDexPcListeners())) { + currentHandlersTable = instrumentationHandlersTable; + } else { + currentHandlersTable = handlersTable; + } + } + ADVANCE(offset); + } else { + ADVANCE(2); + } + } + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(IF_LE) { + if (shadow_frame.GetVReg(inst->VRegA_22t()) <= shadow_frame.GetVReg(inst->VRegB_22t())) { + int16_t offset = inst->VRegC_22t(); + if (IsBackwardBranch(offset)) { + if (UNLIKELY(instrumentation->HasDexPcListeners())) { + currentHandlersTable = instrumentationHandlersTable; + } else { + currentHandlersTable = handlersTable; + } + } + ADVANCE(offset); + } else { + ADVANCE(2); + } + } + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(IF_EQZ) { + if (shadow_frame.GetVReg(inst->VRegA_21t()) == 0) { + int16_t offset = inst->VRegB_21t(); + if (IsBackwardBranch(offset)) { + if (UNLIKELY(instrumentation->HasDexPcListeners())) { + currentHandlersTable = instrumentationHandlersTable; + } else { + currentHandlersTable = handlersTable; + } + } + ADVANCE(offset); + } else { + ADVANCE(2); + } + } + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(IF_NEZ) { + if (shadow_frame.GetVReg(inst->VRegA_21t()) != 0) { + int16_t offset = inst->VRegB_21t(); + if (IsBackwardBranch(offset)) { + if (UNLIKELY(instrumentation->HasDexPcListeners())) { + currentHandlersTable = instrumentationHandlersTable; + } else { + currentHandlersTable = handlersTable; + } + } + ADVANCE(offset); + } else { + ADVANCE(2); + } + } + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(IF_LTZ) { + if (shadow_frame.GetVReg(inst->VRegA_21t()) < 0) { + int16_t offset = inst->VRegB_21t(); + if (IsBackwardBranch(offset)) { + if (UNLIKELY(instrumentation->HasDexPcListeners())) { + currentHandlersTable = instrumentationHandlersTable; + } else { + currentHandlersTable = handlersTable; + } + } + ADVANCE(offset); + } else { + ADVANCE(2); + } + } + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(IF_GEZ) { + if (shadow_frame.GetVReg(inst->VRegA_21t()) >= 0) { + int16_t offset = inst->VRegB_21t(); + if (IsBackwardBranch(offset)) { + if (UNLIKELY(instrumentation->HasDexPcListeners())) { + currentHandlersTable = instrumentationHandlersTable; + } else { + currentHandlersTable = handlersTable; + } + } + ADVANCE(offset); + } else { + ADVANCE(2); + } + } + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(IF_GTZ) { + if (shadow_frame.GetVReg(inst->VRegA_21t()) > 0) { + int16_t offset = inst->VRegB_21t(); + if (IsBackwardBranch(offset)) { + if (UNLIKELY(instrumentation->HasDexPcListeners())) { + currentHandlersTable = instrumentationHandlersTable; + } else { + currentHandlersTable = handlersTable; + } + } + ADVANCE(offset); + } else { + ADVANCE(2); + } + } + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(IF_LEZ) { + if (shadow_frame.GetVReg(inst->VRegA_21t()) <= 0) { + int16_t offset = inst->VRegB_21t(); + if (IsBackwardBranch(offset)) { + if (UNLIKELY(instrumentation->HasDexPcListeners())) { + currentHandlersTable = instrumentationHandlersTable; + } else { + currentHandlersTable = handlersTable; + } + } + ADVANCE(offset); + } else { + ADVANCE(2); + } + } + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(AGET_BOOLEAN) { + Object* a = shadow_frame.GetVRegReference(inst->VRegB_23x()); + if (UNLIKELY(a == NULL)) { + ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow()); + HANDLE_PENDING_EXCEPTION(); + } else { + int32_t index = shadow_frame.GetVReg(inst->VRegC_23x()); + BooleanArray* array = a->AsBooleanArray(); + if (LIKELY(array->IsValidIndex(index))) { + shadow_frame.SetVReg(inst->VRegA_23x(), array->GetData()[index]); + ADVANCE(2); + } else { + HANDLE_PENDING_EXCEPTION(); + } + } + } + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(AGET_BYTE) { + Object* a = shadow_frame.GetVRegReference(inst->VRegB_23x()); + if (UNLIKELY(a == NULL)) { + ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow()); + HANDLE_PENDING_EXCEPTION(); + } else { + int32_t index = shadow_frame.GetVReg(inst->VRegC_23x()); + ByteArray* array = a->AsByteArray(); + if (LIKELY(array->IsValidIndex(index))) { + shadow_frame.SetVReg(inst->VRegA_23x(), array->GetData()[index]); + ADVANCE(2); + } else { + HANDLE_PENDING_EXCEPTION(); + } + } + } + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(AGET_CHAR) { + Object* a = shadow_frame.GetVRegReference(inst->VRegB_23x()); + if (UNLIKELY(a == NULL)) { + ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow()); + HANDLE_PENDING_EXCEPTION(); + } else { + int32_t index = shadow_frame.GetVReg(inst->VRegC_23x()); + CharArray* array = a->AsCharArray(); + if (LIKELY(array->IsValidIndex(index))) { + shadow_frame.SetVReg(inst->VRegA_23x(), array->GetData()[index]); + ADVANCE(2); + } else { + HANDLE_PENDING_EXCEPTION(); + } + } + } + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(AGET_SHORT) { + Object* a = shadow_frame.GetVRegReference(inst->VRegB_23x()); + if (UNLIKELY(a == NULL)) { + ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow()); + HANDLE_PENDING_EXCEPTION(); + } else { + int32_t index = shadow_frame.GetVReg(inst->VRegC_23x()); + ShortArray* array = a->AsShortArray(); + if (LIKELY(array->IsValidIndex(index))) { + shadow_frame.SetVReg(inst->VRegA_23x(), array->GetData()[index]); + ADVANCE(2); + } else { + HANDLE_PENDING_EXCEPTION(); + } + } + } + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(AGET) { + Object* a = shadow_frame.GetVRegReference(inst->VRegB_23x()); + if (UNLIKELY(a == NULL)) { + ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow()); + HANDLE_PENDING_EXCEPTION(); + } else { + int32_t index = shadow_frame.GetVReg(inst->VRegC_23x()); + IntArray* array = a->AsIntArray(); + if (LIKELY(array->IsValidIndex(index))) { + shadow_frame.SetVReg(inst->VRegA_23x(), array->GetData()[index]); + ADVANCE(2); + } else { + HANDLE_PENDING_EXCEPTION(); + } + } + } + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(AGET_WIDE) { + Object* a = shadow_frame.GetVRegReference(inst->VRegB_23x()); + if (UNLIKELY(a == NULL)) { + ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow()); + HANDLE_PENDING_EXCEPTION(); + } else { + int32_t index = shadow_frame.GetVReg(inst->VRegC_23x()); + LongArray* array = a->AsLongArray(); + if (LIKELY(array->IsValidIndex(index))) { + shadow_frame.SetVRegLong(inst->VRegA_23x(), array->GetData()[index]); + ADVANCE(2); + } else { + HANDLE_PENDING_EXCEPTION(); + } + } + } + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(AGET_OBJECT) { + Object* a = shadow_frame.GetVRegReference(inst->VRegB_23x()); + if (UNLIKELY(a == NULL)) { + ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow()); + HANDLE_PENDING_EXCEPTION(); + } else { + int32_t index = shadow_frame.GetVReg(inst->VRegC_23x()); + ObjectArray* array = a->AsObjectArray(); + if (LIKELY(array->IsValidIndex(index))) { + shadow_frame.SetVRegReference(inst->VRegA_23x(), array->GetWithoutChecks(index)); + ADVANCE(2); + } else { + HANDLE_PENDING_EXCEPTION(); + } + } + } + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(APUT_BOOLEAN) { + Object* a = shadow_frame.GetVRegReference(inst->VRegB_23x()); + if (UNLIKELY(a == NULL)) { + ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow()); + HANDLE_PENDING_EXCEPTION(); + } else { + uint8_t val = shadow_frame.GetVReg(inst->VRegA_23x()); + int32_t index = shadow_frame.GetVReg(inst->VRegC_23x()); + BooleanArray* array = a->AsBooleanArray(); + if (LIKELY(array->IsValidIndex(index))) { + array->GetData()[index] = val; + ADVANCE(2); + } else { + HANDLE_PENDING_EXCEPTION(); + } + } + } + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(APUT_BYTE) { + Object* a = shadow_frame.GetVRegReference(inst->VRegB_23x()); + if (UNLIKELY(a == NULL)) { + ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow()); + HANDLE_PENDING_EXCEPTION(); + } else { + int8_t val = shadow_frame.GetVReg(inst->VRegA_23x()); + int32_t index = shadow_frame.GetVReg(inst->VRegC_23x()); + ByteArray* array = a->AsByteArray(); + if (LIKELY(array->IsValidIndex(index))) { + array->GetData()[index] = val; + ADVANCE(2); + } else { + HANDLE_PENDING_EXCEPTION(); + } + } + } + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(APUT_CHAR) { + Object* a = shadow_frame.GetVRegReference(inst->VRegB_23x()); + if (UNLIKELY(a == NULL)) { + ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow()); + HANDLE_PENDING_EXCEPTION(); + } else { + uint16_t val = shadow_frame.GetVReg(inst->VRegA_23x()); + int32_t index = shadow_frame.GetVReg(inst->VRegC_23x()); + CharArray* array = a->AsCharArray(); + if (LIKELY(array->IsValidIndex(index))) { + array->GetData()[index] = val; + ADVANCE(2); + } else { + HANDLE_PENDING_EXCEPTION(); + } + } + } + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(APUT_SHORT) { + Object* a = shadow_frame.GetVRegReference(inst->VRegB_23x()); + if (UNLIKELY(a == NULL)) { + ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow()); + HANDLE_PENDING_EXCEPTION(); + } else { + int16_t val = shadow_frame.GetVReg(inst->VRegA_23x()); + int32_t index = shadow_frame.GetVReg(inst->VRegC_23x()); + ShortArray* array = a->AsShortArray(); + if (LIKELY(array->IsValidIndex(index))) { + array->GetData()[index] = val; + ADVANCE(2); + } else { + HANDLE_PENDING_EXCEPTION(); + } + } + } + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(APUT) { + Object* a = shadow_frame.GetVRegReference(inst->VRegB_23x()); + if (UNLIKELY(a == NULL)) { + ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow()); + HANDLE_PENDING_EXCEPTION(); + } else { + int32_t val = shadow_frame.GetVReg(inst->VRegA_23x()); + int32_t index = shadow_frame.GetVReg(inst->VRegC_23x()); + IntArray* array = a->AsIntArray(); + if (LIKELY(array->IsValidIndex(index))) { + array->GetData()[index] = val; + ADVANCE(2); + } else { + HANDLE_PENDING_EXCEPTION(); + } + } + } + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(APUT_WIDE) { + Object* a = shadow_frame.GetVRegReference(inst->VRegB_23x()); + if (UNLIKELY(a == NULL)) { + ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow()); + HANDLE_PENDING_EXCEPTION(); + } else { + int64_t val = shadow_frame.GetVRegLong(inst->VRegA_23x()); + int32_t index = shadow_frame.GetVReg(inst->VRegC_23x()); + LongArray* array = a->AsLongArray(); + if (LIKELY(array->IsValidIndex(index))) { + array->GetData()[index] = val; + ADVANCE(2); + } else { + HANDLE_PENDING_EXCEPTION(); + } + } + } + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(APUT_OBJECT) { + Object* a = shadow_frame.GetVRegReference(inst->VRegB_23x()); + if (UNLIKELY(a == NULL)) { + ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow()); + HANDLE_PENDING_EXCEPTION(); + } else { + int32_t index = shadow_frame.GetVReg(inst->VRegC_23x()); + Object* val = shadow_frame.GetVRegReference(inst->VRegA_23x()); + ObjectArray* array = a->AsObjectArray(); + if (LIKELY(array->IsValidIndex(index) && array->CheckAssignable(val))) { + array->SetWithoutChecks(index, val); + ADVANCE(2); + } else { + HANDLE_PENDING_EXCEPTION(); + } + } + } + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(IGET_BOOLEAN) { + bool success = DoFieldGet(self, shadow_frame, inst); + POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2); + } + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(IGET_BYTE) { + bool success = DoFieldGet(self, shadow_frame, inst); + POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2); + } + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(IGET_CHAR) { + bool success = DoFieldGet(self, shadow_frame, inst); + POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2); + } + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(IGET_SHORT) { + bool success = DoFieldGet(self, shadow_frame, inst); + POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2); + } + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(IGET) { + bool success = DoFieldGet(self, shadow_frame, inst); + POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2); + } + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(IGET_WIDE) { + bool success = DoFieldGet(self, shadow_frame, inst); + POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2); + } + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(IGET_OBJECT) { + bool success = DoFieldGet(self, shadow_frame, inst); + POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2); + } + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(IGET_QUICK) { + bool success = DoIGetQuick(self, shadow_frame, inst); + POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2); + } + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(IGET_WIDE_QUICK) { + bool success = DoIGetQuick(self, shadow_frame, inst); + POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2); + } + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(IGET_OBJECT_QUICK) { + bool success = DoIGetQuick(self, shadow_frame, inst); + POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2); + } + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(SGET_BOOLEAN) { + bool success = DoFieldGet(self, shadow_frame, inst); + POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2); + } + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(SGET_BYTE) { + bool success = DoFieldGet(self, shadow_frame, inst); + POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2); + } + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(SGET_CHAR) { + bool success = DoFieldGet(self, shadow_frame, inst); + POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2); + } + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(SGET_SHORT) { + bool success = DoFieldGet(self, shadow_frame, inst); + POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2); + } + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(SGET) { + bool success = DoFieldGet(self, shadow_frame, inst); + POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2); + } + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(SGET_WIDE) { + bool success = DoFieldGet(self, shadow_frame, inst); + POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2); + } + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(SGET_OBJECT) { + bool success = DoFieldGet(self, shadow_frame, inst); + POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2); + } + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(IPUT_BOOLEAN) { + bool success = DoFieldPut(self, shadow_frame, inst); + POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2); + } + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(IPUT_BYTE) { + bool success = DoFieldPut(self, shadow_frame, inst); + POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2); + } + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(IPUT_CHAR) { + bool success = DoFieldPut(self, shadow_frame, inst); + POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2); + } + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(IPUT_SHORT) { + bool success = DoFieldPut(self, shadow_frame, inst); + POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2); + } + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(IPUT) { + bool success = DoFieldPut(self, shadow_frame, inst); + POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2); + } + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(IPUT_WIDE) { + bool success = DoFieldPut(self, shadow_frame, inst); + POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2); + } + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(IPUT_OBJECT) { + bool success = DoFieldPut(self, shadow_frame, inst); + POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2); + } + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(IPUT_QUICK) { + bool success = DoIPutQuick(self, shadow_frame, inst); + POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2); + } + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(IPUT_WIDE_QUICK) { + bool success = DoIPutQuick(self, shadow_frame, inst); + POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2); + } + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(IPUT_OBJECT_QUICK) { + bool success = DoIPutQuick(self, shadow_frame, inst); + POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2); + } + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(SPUT_BOOLEAN) { + bool success = DoFieldPut(self, shadow_frame, inst); + POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2); + } + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(SPUT_BYTE) { + bool success = DoFieldPut(self, shadow_frame, inst); + POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2); + } + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(SPUT_CHAR) { + bool success = DoFieldPut(self, shadow_frame, inst); + POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2); + } + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(SPUT_SHORT) { + bool success = DoFieldPut(self, shadow_frame, inst); + POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2); + } + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(SPUT) { + bool success = DoFieldPut(self, shadow_frame, inst); + POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2); + } + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(SPUT_WIDE) { + bool success = DoFieldPut(self, shadow_frame, inst); + POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2); + } + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(SPUT_OBJECT) { + bool success = DoFieldPut(self, shadow_frame, inst); + POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2); + } + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(INVOKE_VIRTUAL) { + bool success = DoInvoke(self, shadow_frame, inst, &result_register); + POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 3); + } + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(INVOKE_VIRTUAL_RANGE) { + bool success = DoInvoke(self, shadow_frame, inst, &result_register); + POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 3); + } + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(INVOKE_SUPER) { + bool success = DoInvoke(self, shadow_frame, inst, &result_register); + POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 3); + } + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(INVOKE_SUPER_RANGE) { + bool success = DoInvoke(self, shadow_frame, inst, &result_register); + POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 3); + } + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(INVOKE_DIRECT) { + bool success = DoInvoke(self, shadow_frame, inst, &result_register); + POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 3); + } + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(INVOKE_DIRECT_RANGE) { + bool success = DoInvoke(self, shadow_frame, inst, &result_register); + POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 3); + } + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(INVOKE_INTERFACE) { + bool success = DoInvoke(self, shadow_frame, inst, &result_register); + POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 3); + } + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(INVOKE_INTERFACE_RANGE) { + bool success = DoInvoke(self, shadow_frame, inst, &result_register); + POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 3); + } + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(INVOKE_STATIC) { + bool success = DoInvoke(self, shadow_frame, inst, &result_register); + POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 3); + } + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(INVOKE_STATIC_RANGE) { + bool success = DoInvoke(self, shadow_frame, inst, &result_register); + POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 3); + } + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(INVOKE_VIRTUAL_QUICK) { + bool success = DoInvokeVirtualQuick(self, shadow_frame, inst, &result_register); + POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 3); + } + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(INVOKE_VIRTUAL_RANGE_QUICK) { + bool success = DoInvokeVirtualQuick(self, shadow_frame, inst, &result_register); + POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 3); + } + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(NEG_INT) + shadow_frame.SetVReg(inst->VRegA_12x(), -shadow_frame.GetVReg(inst->VRegB_12x())); + ADVANCE(1); + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(NOT_INT) + shadow_frame.SetVReg(inst->VRegA_12x(), ~shadow_frame.GetVReg(inst->VRegB_12x())); + ADVANCE(1); + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(NEG_LONG) + shadow_frame.SetVRegLong(inst->VRegA_12x(), -shadow_frame.GetVRegLong(inst->VRegB_12x())); + ADVANCE(1); + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(NOT_LONG) + shadow_frame.SetVRegLong(inst->VRegA_12x(), ~shadow_frame.GetVRegLong(inst->VRegB_12x())); + ADVANCE(1); + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(NEG_FLOAT) + shadow_frame.SetVRegFloat(inst->VRegA_12x(), -shadow_frame.GetVRegFloat(inst->VRegB_12x())); + ADVANCE(1); + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(NEG_DOUBLE) + shadow_frame.SetVRegDouble(inst->VRegA_12x(), -shadow_frame.GetVRegDouble(inst->VRegB_12x())); + ADVANCE(1); + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(INT_TO_LONG) + shadow_frame.SetVRegLong(inst->VRegA_12x(), shadow_frame.GetVReg(inst->VRegB_12x())); + ADVANCE(1); + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(INT_TO_FLOAT) + shadow_frame.SetVRegFloat(inst->VRegA_12x(), shadow_frame.GetVReg(inst->VRegB_12x())); + ADVANCE(1); + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(INT_TO_DOUBLE) + shadow_frame.SetVRegDouble(inst->VRegA_12x(), shadow_frame.GetVReg(inst->VRegB_12x())); + ADVANCE(1); + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(LONG_TO_INT) + shadow_frame.SetVReg(inst->VRegA_12x(), shadow_frame.GetVRegLong(inst->VRegB_12x())); + ADVANCE(1); + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(LONG_TO_FLOAT) + shadow_frame.SetVRegFloat(inst->VRegA_12x(), shadow_frame.GetVRegLong(inst->VRegB_12x())); + ADVANCE(1); + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(LONG_TO_DOUBLE) + shadow_frame.SetVRegDouble(inst->VRegA_12x(), shadow_frame.GetVRegLong(inst->VRegB_12x())); + ADVANCE(1); + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(FLOAT_TO_INT) { + float val = shadow_frame.GetVRegFloat(inst->VRegB_12x()); + int32_t result; + if (val != val) { + result = 0; + } else if (val > static_cast(kMaxInt)) { + result = kMaxInt; + } else if (val < static_cast(kMinInt)) { + result = kMinInt; + } else { + result = val; + } + shadow_frame.SetVReg(inst->VRegA_12x(), result); + ADVANCE(1); + } + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(FLOAT_TO_LONG) { + float val = shadow_frame.GetVRegFloat(inst->VRegB_12x()); + int64_t result; + if (val != val) { + result = 0; + } else if (val > static_cast(kMaxLong)) { + result = kMaxLong; + } else if (val < static_cast(kMinLong)) { + result = kMinLong; + } else { + result = val; + } + shadow_frame.SetVRegLong(inst->VRegA_12x(), result); + ADVANCE(1); + } + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(FLOAT_TO_DOUBLE) + shadow_frame.SetVRegDouble(inst->VRegA_12x(), shadow_frame.GetVRegFloat(inst->VRegB_12x())); + ADVANCE(1); + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(DOUBLE_TO_INT) { + double val = shadow_frame.GetVRegDouble(inst->VRegB_12x()); + int32_t result; + if (val != val) { + result = 0; + } else if (val > static_cast(kMaxInt)) { + result = kMaxInt; + } else if (val < static_cast(kMinInt)) { + result = kMinInt; + } else { + result = val; + } + shadow_frame.SetVReg(inst->VRegA_12x(), result); + ADVANCE(1); + } + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(DOUBLE_TO_LONG) { + double val = shadow_frame.GetVRegDouble(inst->VRegB_12x()); + int64_t result; + if (val != val) { + result = 0; + } else if (val > static_cast(kMaxLong)) { + result = kMaxLong; + } else if (val < static_cast(kMinLong)) { + result = kMinLong; + } else { + result = val; + } + shadow_frame.SetVRegLong(inst->VRegA_12x(), result); + ADVANCE(1); + } + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(DOUBLE_TO_FLOAT) + shadow_frame.SetVRegFloat(inst->VRegA_12x(), shadow_frame.GetVRegDouble(inst->VRegB_12x())); + ADVANCE(1); + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(INT_TO_BYTE) + shadow_frame.SetVReg(inst->VRegA_12x(), + static_cast(shadow_frame.GetVReg(inst->VRegB_12x()))); + ADVANCE(1); + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(INT_TO_CHAR) + shadow_frame.SetVReg(inst->VRegA_12x(), + static_cast(shadow_frame.GetVReg(inst->VRegB_12x()))); + ADVANCE(1); + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(INT_TO_SHORT) + shadow_frame.SetVReg(inst->VRegA_12x(), + static_cast(shadow_frame.GetVReg(inst->VRegB_12x()))); + ADVANCE(1); + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(ADD_INT) + shadow_frame.SetVReg(inst->VRegA_23x(), + shadow_frame.GetVReg(inst->VRegB_23x()) + + shadow_frame.GetVReg(inst->VRegC_23x())); + ADVANCE(2); + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(SUB_INT) + shadow_frame.SetVReg(inst->VRegA_23x(), + shadow_frame.GetVReg(inst->VRegB_23x()) - + shadow_frame.GetVReg(inst->VRegC_23x())); + ADVANCE(2); + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(MUL_INT) + shadow_frame.SetVReg(inst->VRegA_23x(), + shadow_frame.GetVReg(inst->VRegB_23x()) * + shadow_frame.GetVReg(inst->VRegC_23x())); + ADVANCE(2); + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(DIV_INT) { + bool success = DoIntDivide(shadow_frame, inst->VRegA_23x(), + shadow_frame.GetVReg(inst->VRegB_23x()), + shadow_frame.GetVReg(inst->VRegC_23x())); + POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2); + } + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(REM_INT) { + bool success = DoIntRemainder(shadow_frame, inst->VRegA_23x(), + shadow_frame.GetVReg(inst->VRegB_23x()), + shadow_frame.GetVReg(inst->VRegC_23x())); + POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2); + } + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(SHL_INT) + shadow_frame.SetVReg(inst->VRegA_23x(), + shadow_frame.GetVReg(inst->VRegB_23x()) << + (shadow_frame.GetVReg(inst->VRegC_23x()) & 0x1f)); + ADVANCE(2); + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(SHR_INT) + shadow_frame.SetVReg(inst->VRegA_23x(), + shadow_frame.GetVReg(inst->VRegB_23x()) >> + (shadow_frame.GetVReg(inst->VRegC_23x()) & 0x1f)); + ADVANCE(2); + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(USHR_INT) + shadow_frame.SetVReg(inst->VRegA_23x(), + static_cast(shadow_frame.GetVReg(inst->VRegB_23x())) >> + (shadow_frame.GetVReg(inst->VRegC_23x()) & 0x1f)); + ADVANCE(2); + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(AND_INT) + shadow_frame.SetVReg(inst->VRegA_23x(), + shadow_frame.GetVReg(inst->VRegB_23x()) & + shadow_frame.GetVReg(inst->VRegC_23x())); + ADVANCE(2); + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(OR_INT) + shadow_frame.SetVReg(inst->VRegA_23x(), + shadow_frame.GetVReg(inst->VRegB_23x()) | + shadow_frame.GetVReg(inst->VRegC_23x())); + ADVANCE(2); + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(XOR_INT) + shadow_frame.SetVReg(inst->VRegA_23x(), + shadow_frame.GetVReg(inst->VRegB_23x()) ^ + shadow_frame.GetVReg(inst->VRegC_23x())); + ADVANCE(2); + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(ADD_LONG) + shadow_frame.SetVRegLong(inst->VRegA_23x(), + shadow_frame.GetVRegLong(inst->VRegB_23x()) + + shadow_frame.GetVRegLong(inst->VRegC_23x())); + ADVANCE(2); + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(SUB_LONG) + shadow_frame.SetVRegLong(inst->VRegA_23x(), + shadow_frame.GetVRegLong(inst->VRegB_23x()) - + shadow_frame.GetVRegLong(inst->VRegC_23x())); + ADVANCE(2); + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(MUL_LONG) + shadow_frame.SetVRegLong(inst->VRegA_23x(), + shadow_frame.GetVRegLong(inst->VRegB_23x()) * + shadow_frame.GetVRegLong(inst->VRegC_23x())); + ADVANCE(2); + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(DIV_LONG) { + bool success = DoLongDivide(shadow_frame, inst->VRegA_23x(), + shadow_frame.GetVRegLong(inst->VRegB_23x()), + shadow_frame.GetVRegLong(inst->VRegC_23x())); + POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2); + } + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(REM_LONG) { + bool success = DoLongRemainder(shadow_frame, inst->VRegA_23x(), + shadow_frame.GetVRegLong(inst->VRegB_23x()), + shadow_frame.GetVRegLong(inst->VRegC_23x())); + POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2); + } + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(AND_LONG) + shadow_frame.SetVRegLong(inst->VRegA_23x(), + shadow_frame.GetVRegLong(inst->VRegB_23x()) & + shadow_frame.GetVRegLong(inst->VRegC_23x())); + ADVANCE(2); + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(OR_LONG) + shadow_frame.SetVRegLong(inst->VRegA_23x(), + shadow_frame.GetVRegLong(inst->VRegB_23x()) | + shadow_frame.GetVRegLong(inst->VRegC_23x())); + ADVANCE(2); + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(XOR_LONG) + shadow_frame.SetVRegLong(inst->VRegA_23x(), + shadow_frame.GetVRegLong(inst->VRegB_23x()) ^ + shadow_frame.GetVRegLong(inst->VRegC_23x())); + ADVANCE(2); + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(SHL_LONG) + shadow_frame.SetVRegLong(inst->VRegA_23x(), + shadow_frame.GetVRegLong(inst->VRegB_23x()) << + (shadow_frame.GetVReg(inst->VRegC_23x()) & 0x3f)); + ADVANCE(2); + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(SHR_LONG) + shadow_frame.SetVRegLong(inst->VRegA_23x(), + shadow_frame.GetVRegLong(inst->VRegB_23x()) >> + (shadow_frame.GetVReg(inst->VRegC_23x()) & 0x3f)); + ADVANCE(2); + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(USHR_LONG) + shadow_frame.SetVRegLong(inst->VRegA_23x(), + static_cast(shadow_frame.GetVRegLong(inst->VRegB_23x())) >> + (shadow_frame.GetVReg(inst->VRegC_23x()) & 0x3f)); + ADVANCE(2); + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(ADD_FLOAT) + shadow_frame.SetVRegFloat(inst->VRegA_23x(), + shadow_frame.GetVRegFloat(inst->VRegB_23x()) + + shadow_frame.GetVRegFloat(inst->VRegC_23x())); + ADVANCE(2); + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(SUB_FLOAT) + shadow_frame.SetVRegFloat(inst->VRegA_23x(), + shadow_frame.GetVRegFloat(inst->VRegB_23x()) - + shadow_frame.GetVRegFloat(inst->VRegC_23x())); + ADVANCE(2); + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(MUL_FLOAT) + shadow_frame.SetVRegFloat(inst->VRegA_23x(), + shadow_frame.GetVRegFloat(inst->VRegB_23x()) * + shadow_frame.GetVRegFloat(inst->VRegC_23x())); + ADVANCE(2); + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(DIV_FLOAT) + shadow_frame.SetVRegFloat(inst->VRegA_23x(), + shadow_frame.GetVRegFloat(inst->VRegB_23x()) / + shadow_frame.GetVRegFloat(inst->VRegC_23x())); + ADVANCE(2); + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(REM_FLOAT) + shadow_frame.SetVRegFloat(inst->VRegA_23x(), + fmodf(shadow_frame.GetVRegFloat(inst->VRegB_23x()), + shadow_frame.GetVRegFloat(inst->VRegC_23x()))); + ADVANCE(2); + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(ADD_DOUBLE) + shadow_frame.SetVRegDouble(inst->VRegA_23x(), + shadow_frame.GetVRegDouble(inst->VRegB_23x()) + + shadow_frame.GetVRegDouble(inst->VRegC_23x())); + ADVANCE(2); + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(SUB_DOUBLE) + shadow_frame.SetVRegDouble(inst->VRegA_23x(), + shadow_frame.GetVRegDouble(inst->VRegB_23x()) - + shadow_frame.GetVRegDouble(inst->VRegC_23x())); + ADVANCE(2); + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(MUL_DOUBLE) + shadow_frame.SetVRegDouble(inst->VRegA_23x(), + shadow_frame.GetVRegDouble(inst->VRegB_23x()) * + shadow_frame.GetVRegDouble(inst->VRegC_23x())); + ADVANCE(2); + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(DIV_DOUBLE) + shadow_frame.SetVRegDouble(inst->VRegA_23x(), + shadow_frame.GetVRegDouble(inst->VRegB_23x()) / + shadow_frame.GetVRegDouble(inst->VRegC_23x())); + ADVANCE(2); + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(REM_DOUBLE) + shadow_frame.SetVRegDouble(inst->VRegA_23x(), + fmod(shadow_frame.GetVRegDouble(inst->VRegB_23x()), + shadow_frame.GetVRegDouble(inst->VRegC_23x()))); + ADVANCE(2); + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(ADD_INT_2ADDR) { + uint32_t vregA = inst->VRegA_12x(); + shadow_frame.SetVReg(vregA, + shadow_frame.GetVReg(vregA) + + shadow_frame.GetVReg(inst->VRegB_12x())); + ADVANCE(1); + } + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(SUB_INT_2ADDR) { + uint32_t vregA = inst->VRegA_12x(); + shadow_frame.SetVReg(vregA, + shadow_frame.GetVReg(vregA) - + shadow_frame.GetVReg(inst->VRegB_12x())); + ADVANCE(1); + } + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(MUL_INT_2ADDR) { + uint32_t vregA = inst->VRegA_12x(); + shadow_frame.SetVReg(vregA, + shadow_frame.GetVReg(vregA) * + shadow_frame.GetVReg(inst->VRegB_12x())); + ADVANCE(1); + } + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(DIV_INT_2ADDR) { + uint32_t vregA = inst->VRegA_12x(); + bool success = DoIntDivide(shadow_frame, vregA, shadow_frame.GetVReg(vregA), + shadow_frame.GetVReg(inst->VRegB_12x())); + POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 1); + } + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(REM_INT_2ADDR) { + uint32_t vregA = inst->VRegA_12x(); + bool success = DoIntRemainder(shadow_frame, vregA, shadow_frame.GetVReg(vregA), + shadow_frame.GetVReg(inst->VRegB_12x())); + POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 1); + } + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(SHL_INT_2ADDR) { + uint32_t vregA = inst->VRegA_12x(); + shadow_frame.SetVReg(vregA, + shadow_frame.GetVReg(vregA) << + (shadow_frame.GetVReg(inst->VRegB_12x()) & 0x1f)); + ADVANCE(1); + } + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(SHR_INT_2ADDR) { + uint32_t vregA = inst->VRegA_12x(); + shadow_frame.SetVReg(vregA, + shadow_frame.GetVReg(vregA) >> + (shadow_frame.GetVReg(inst->VRegB_12x()) & 0x1f)); + ADVANCE(1); + } + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(USHR_INT_2ADDR) { + uint32_t vregA = inst->VRegA_12x(); + shadow_frame.SetVReg(vregA, + static_cast(shadow_frame.GetVReg(vregA)) >> + (shadow_frame.GetVReg(inst->VRegB_12x()) & 0x1f)); + ADVANCE(1); + } + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(AND_INT_2ADDR) { + uint32_t vregA = inst->VRegA_12x(); + shadow_frame.SetVReg(vregA, + shadow_frame.GetVReg(vregA) & + shadow_frame.GetVReg(inst->VRegB_12x())); + ADVANCE(1); + } + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(OR_INT_2ADDR) { + uint32_t vregA = inst->VRegA_12x(); + shadow_frame.SetVReg(vregA, + shadow_frame.GetVReg(vregA) | + shadow_frame.GetVReg(inst->VRegB_12x())); + ADVANCE(1); + } + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(XOR_INT_2ADDR) { + uint32_t vregA = inst->VRegA_12x(); + shadow_frame.SetVReg(vregA, + shadow_frame.GetVReg(vregA) ^ + shadow_frame.GetVReg(inst->VRegB_12x())); + ADVANCE(1); + } + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(ADD_LONG_2ADDR) { + uint32_t vregA = inst->VRegA_12x(); + shadow_frame.SetVRegLong(vregA, + shadow_frame.GetVRegLong(vregA) + + shadow_frame.GetVRegLong(inst->VRegB_12x())); + ADVANCE(1); + } + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(SUB_LONG_2ADDR) { + uint32_t vregA = inst->VRegA_12x(); + shadow_frame.SetVRegLong(vregA, + shadow_frame.GetVRegLong(vregA) - + shadow_frame.GetVRegLong(inst->VRegB_12x())); + ADVANCE(1); + } + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(MUL_LONG_2ADDR) { + uint32_t vregA = inst->VRegA_12x(); + shadow_frame.SetVRegLong(vregA, + shadow_frame.GetVRegLong(vregA) * + shadow_frame.GetVRegLong(inst->VRegB_12x())); + ADVANCE(1); + } + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(DIV_LONG_2ADDR) { + uint32_t vregA = inst->VRegA_12x(); + bool success = DoLongDivide(shadow_frame, vregA, shadow_frame.GetVRegLong(vregA), + shadow_frame.GetVRegLong(inst->VRegB_12x())); + POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 1); + } + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(REM_LONG_2ADDR) { + uint32_t vregA = inst->VRegA_12x(); + bool success = DoLongRemainder(shadow_frame, vregA, shadow_frame.GetVRegLong(vregA), + shadow_frame.GetVRegLong(inst->VRegB_12x())); + POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 1); + } + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(AND_LONG_2ADDR) { + uint32_t vregA = inst->VRegA_12x(); + shadow_frame.SetVRegLong(vregA, + shadow_frame.GetVRegLong(vregA) & + shadow_frame.GetVRegLong(inst->VRegB_12x())); + ADVANCE(1); + } + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(OR_LONG_2ADDR) { + uint32_t vregA = inst->VRegA_12x(); + shadow_frame.SetVRegLong(vregA, + shadow_frame.GetVRegLong(vregA) | + shadow_frame.GetVRegLong(inst->VRegB_12x())); + ADVANCE(1); + } + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(XOR_LONG_2ADDR) { + uint32_t vregA = inst->VRegA_12x(); + shadow_frame.SetVRegLong(vregA, + shadow_frame.GetVRegLong(vregA) ^ + shadow_frame.GetVRegLong(inst->VRegB_12x())); + ADVANCE(1); + } + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(SHL_LONG_2ADDR) { + uint32_t vregA = inst->VRegA_12x(); + shadow_frame.SetVRegLong(vregA, + shadow_frame.GetVRegLong(vregA) << + (shadow_frame.GetVReg(inst->VRegB_12x()) & 0x3f)); + ADVANCE(1); + } + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(SHR_LONG_2ADDR) { + uint32_t vregA = inst->VRegA_12x(); + shadow_frame.SetVRegLong(vregA, + shadow_frame.GetVRegLong(vregA) >> + (shadow_frame.GetVReg(inst->VRegB_12x()) & 0x3f)); + ADVANCE(1); + } + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(USHR_LONG_2ADDR) { + uint32_t vregA = inst->VRegA_12x(); + shadow_frame.SetVRegLong(vregA, + static_cast(shadow_frame.GetVRegLong(vregA)) >> + (shadow_frame.GetVReg(inst->VRegB_12x()) & 0x3f)); + ADVANCE(1); + } + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(ADD_FLOAT_2ADDR) { + uint32_t vregA = inst->VRegA_12x(); + shadow_frame.SetVRegFloat(vregA, + shadow_frame.GetVRegFloat(vregA) + + shadow_frame.GetVRegFloat(inst->VRegB_12x())); + ADVANCE(1); + } + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(SUB_FLOAT_2ADDR) { + uint32_t vregA = inst->VRegA_12x(); + shadow_frame.SetVRegFloat(vregA, + shadow_frame.GetVRegFloat(vregA) - + shadow_frame.GetVRegFloat(inst->VRegB_12x())); + ADVANCE(1); + } + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(MUL_FLOAT_2ADDR) { + uint32_t vregA = inst->VRegA_12x(); + shadow_frame.SetVRegFloat(vregA, + shadow_frame.GetVRegFloat(vregA) * + shadow_frame.GetVRegFloat(inst->VRegB_12x())); + ADVANCE(1); + } + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(DIV_FLOAT_2ADDR) { + uint32_t vregA = inst->VRegA_12x(); + shadow_frame.SetVRegFloat(vregA, + shadow_frame.GetVRegFloat(vregA) / + shadow_frame.GetVRegFloat(inst->VRegB_12x())); + ADVANCE(1); + } + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(REM_FLOAT_2ADDR) { + uint32_t vregA = inst->VRegA_12x(); + shadow_frame.SetVRegFloat(vregA, + fmodf(shadow_frame.GetVRegFloat(vregA), + shadow_frame.GetVRegFloat(inst->VRegB_12x()))); + ADVANCE(1); + } + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(ADD_DOUBLE_2ADDR) { + uint32_t vregA = inst->VRegA_12x(); + shadow_frame.SetVRegDouble(vregA, + shadow_frame.GetVRegDouble(vregA) + + shadow_frame.GetVRegDouble(inst->VRegB_12x())); + ADVANCE(1); + } + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(SUB_DOUBLE_2ADDR) { + uint32_t vregA = inst->VRegA_12x(); + shadow_frame.SetVRegDouble(vregA, + shadow_frame.GetVRegDouble(vregA) - + shadow_frame.GetVRegDouble(inst->VRegB_12x())); + ADVANCE(1); + } + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(MUL_DOUBLE_2ADDR) { + uint32_t vregA = inst->VRegA_12x(); + shadow_frame.SetVRegDouble(vregA, + shadow_frame.GetVRegDouble(vregA) * + shadow_frame.GetVRegDouble(inst->VRegB_12x())); + ADVANCE(1); + } + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(DIV_DOUBLE_2ADDR) { + uint32_t vregA = inst->VRegA_12x(); + shadow_frame.SetVRegDouble(vregA, + shadow_frame.GetVRegDouble(vregA) / + shadow_frame.GetVRegDouble(inst->VRegB_12x())); + ADVANCE(1); + } + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(REM_DOUBLE_2ADDR) { + uint32_t vregA = inst->VRegA_12x(); + shadow_frame.SetVRegDouble(vregA, + fmod(shadow_frame.GetVRegDouble(vregA), + shadow_frame.GetVRegDouble(inst->VRegB_12x()))); + ADVANCE(1); + } + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(ADD_INT_LIT16) + shadow_frame.SetVReg(inst->VRegA_22s(), + shadow_frame.GetVReg(inst->VRegB_22s()) + + inst->VRegC_22s()); + ADVANCE(2); + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(RSUB_INT) + shadow_frame.SetVReg(inst->VRegA_22s(), + inst->VRegC_22s() - + shadow_frame.GetVReg(inst->VRegB_22s())); + ADVANCE(2); + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(MUL_INT_LIT16) + shadow_frame.SetVReg(inst->VRegA_22s(), + shadow_frame.GetVReg(inst->VRegB_22s()) * + inst->VRegC_22s()); + ADVANCE(2); + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(DIV_INT_LIT16) { + bool success = DoIntDivide(shadow_frame, inst->VRegA_22s(), + shadow_frame.GetVReg(inst->VRegB_22s()), inst->VRegC_22s()); + POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2); + } + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(REM_INT_LIT16) { + bool success = DoIntRemainder(shadow_frame, inst->VRegA_22s(), + shadow_frame.GetVReg(inst->VRegB_22s()), inst->VRegC_22s()); + POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2); + } + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(AND_INT_LIT16) + shadow_frame.SetVReg(inst->VRegA_22s(), + shadow_frame.GetVReg(inst->VRegB_22s()) & + inst->VRegC_22s()); + ADVANCE(2); + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(OR_INT_LIT16) + shadow_frame.SetVReg(inst->VRegA_22s(), + shadow_frame.GetVReg(inst->VRegB_22s()) | + inst->VRegC_22s()); + ADVANCE(2); + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(XOR_INT_LIT16) + shadow_frame.SetVReg(inst->VRegA_22s(), + shadow_frame.GetVReg(inst->VRegB_22s()) ^ + inst->VRegC_22s()); + ADVANCE(2); + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(ADD_INT_LIT8) + shadow_frame.SetVReg(inst->VRegA_22b(), + shadow_frame.GetVReg(inst->VRegB_22b()) + + inst->VRegC_22b()); + ADVANCE(2); + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(RSUB_INT_LIT8) + shadow_frame.SetVReg(inst->VRegA_22b(), + inst->VRegC_22b() - + shadow_frame.GetVReg(inst->VRegB_22b())); + ADVANCE(2); + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(MUL_INT_LIT8) + shadow_frame.SetVReg(inst->VRegA_22b(), + shadow_frame.GetVReg(inst->VRegB_22b()) * + inst->VRegC_22b()); + ADVANCE(2); + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(DIV_INT_LIT8) { + bool success = DoIntDivide(shadow_frame, inst->VRegA_22b(), + shadow_frame.GetVReg(inst->VRegB_22b()), inst->VRegC_22b()); + POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2); + } + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(REM_INT_LIT8) { + bool success = DoIntRemainder(shadow_frame, inst->VRegA_22b(), + shadow_frame.GetVReg(inst->VRegB_22b()), inst->VRegC_22b()); + POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2); + } + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(AND_INT_LIT8) + shadow_frame.SetVReg(inst->VRegA_22b(), + shadow_frame.GetVReg(inst->VRegB_22b()) & + inst->VRegC_22b()); + ADVANCE(2); + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(OR_INT_LIT8) + shadow_frame.SetVReg(inst->VRegA_22b(), + shadow_frame.GetVReg(inst->VRegB_22b()) | + inst->VRegC_22b()); + ADVANCE(2); + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(XOR_INT_LIT8) + shadow_frame.SetVReg(inst->VRegA_22b(), + shadow_frame.GetVReg(inst->VRegB_22b()) ^ + inst->VRegC_22b()); + ADVANCE(2); + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(SHL_INT_LIT8) + shadow_frame.SetVReg(inst->VRegA_22b(), + shadow_frame.GetVReg(inst->VRegB_22b()) << + (inst->VRegC_22b() & 0x1f)); + ADVANCE(2); + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(SHR_INT_LIT8) + shadow_frame.SetVReg(inst->VRegA_22b(), + shadow_frame.GetVReg(inst->VRegB_22b()) >> + (inst->VRegC_22b() & 0x1f)); + ADVANCE(2); + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(USHR_INT_LIT8) + shadow_frame.SetVReg(inst->VRegA_22b(), + static_cast(shadow_frame.GetVReg(inst->VRegB_22b())) >> + (inst->VRegC_22b() & 0x1f)); + ADVANCE(2); + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(UNUSED_3E) + UnexpectedOpcode(inst, mh); + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(UNUSED_3F) + UnexpectedOpcode(inst, mh); + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(UNUSED_40) + UnexpectedOpcode(inst, mh); + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(UNUSED_41) + UnexpectedOpcode(inst, mh); + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(UNUSED_42) + UnexpectedOpcode(inst, mh); + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(UNUSED_43) + UnexpectedOpcode(inst, mh); + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(UNUSED_79) + UnexpectedOpcode(inst, mh); + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(UNUSED_7A) + UnexpectedOpcode(inst, mh); + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(UNUSED_EB) + UnexpectedOpcode(inst, mh); + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(UNUSED_EC) + UnexpectedOpcode(inst, mh); + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(UNUSED_ED) + UnexpectedOpcode(inst, mh); + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(UNUSED_EE) + UnexpectedOpcode(inst, mh); + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(UNUSED_EF) + UnexpectedOpcode(inst, mh); + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(UNUSED_F0) + UnexpectedOpcode(inst, mh); + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(UNUSED_F1) + UnexpectedOpcode(inst, mh); + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(UNUSED_F2) + UnexpectedOpcode(inst, mh); + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(UNUSED_F3) + UnexpectedOpcode(inst, mh); + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(UNUSED_F4) + UnexpectedOpcode(inst, mh); + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(UNUSED_F5) + UnexpectedOpcode(inst, mh); + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(UNUSED_F6) + UnexpectedOpcode(inst, mh); + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(UNUSED_F7) + UnexpectedOpcode(inst, mh); + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(UNUSED_F8) + UnexpectedOpcode(inst, mh); + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(UNUSED_F9) + UnexpectedOpcode(inst, mh); + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(UNUSED_FA) + UnexpectedOpcode(inst, mh); + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(UNUSED_FB) + UnexpectedOpcode(inst, mh); + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(UNUSED_FC) + UnexpectedOpcode(inst, mh); + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(UNUSED_FD) + UnexpectedOpcode(inst, mh); + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(UNUSED_FE) + UnexpectedOpcode(inst, mh); + HANDLE_INSTRUCTION_END(); + + HANDLE_INSTRUCTION_START(UNUSED_FF) + UnexpectedOpcode(inst, mh); + HANDLE_INSTRUCTION_END(); + + exception_pending_label: { + CHECK(self->IsExceptionPending()); + uint32_t found_dex_pc = FindNextInstructionFollowingException(self, shadow_frame, dex_pc, + this_object_ref, + instrumentation); + if (found_dex_pc == DexFile::kDexNoIndex) { + return JValue(); /* Handled in caller. */ + } else { + int32_t displacement = static_cast(found_dex_pc) - static_cast(dex_pc); + ADVANCE(displacement); + } + } + + // Create alternative instruction handlers dedicated to instrumentation. +#define INSTRUMENTATION_INSTRUCTION_HANDLER(o, code, n, f, r, i, a, v) \ + instrumentation_op_##code: { \ + instrumentation->DexPcMovedEvent(self, this_object_ref.get(), \ + shadow_frame.GetMethod(), dex_pc); \ + goto *handlersTable[Instruction::code]; \ + } +#include "dex_instruction_list.h" + DEX_INSTRUCTION_LIST(INSTRUMENTATION_INSTRUCTION_HANDLER) +#undef DEX_INSTRUCTION_LIST +#undef INSTRUMENTATION_INSTRUCTION_HANDLER +} // NOLINT(readability/fn_size) + +// Explicit definitions of ExecuteGotoImpl. +template JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, + const DexFile::CodeItem* code_item, + ShadowFrame& shadow_frame, JValue result_register); +template JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, + const DexFile::CodeItem* code_item, + ShadowFrame& shadow_frame, JValue result_register); + +} // namespace interpreter +} // namespace art diff --git a/runtime/interpreter/interpreter_switch_impl.cc b/runtime/interpreter/interpreter_switch_impl.cc new file mode 100644 index 00000000000..ee2aaf69b42 --- /dev/null +++ b/runtime/interpreter/interpreter_switch_impl.cc @@ -0,0 +1,2030 @@ +/* + * Copyright (C) 2012 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 "interpreter_common.h" + +namespace art { +namespace interpreter { + +#define HANDLE_PENDING_EXCEPTION() \ + do { \ + CHECK(self->IsExceptionPending()); \ + uint32_t found_dex_pc = FindNextInstructionFollowingException(self, shadow_frame, \ + inst->GetDexPc(insns), \ + this_object_ref, \ + instrumentation); \ + if (found_dex_pc == DexFile::kDexNoIndex) { \ + return JValue(); /* Handled in caller. */ \ + } else { \ + int32_t displacement = static_cast(found_dex_pc) - static_cast(dex_pc); \ + inst = inst->RelativeAt(displacement); \ + } \ + } while (false) + +#define POSSIBLY_HANDLE_PENDING_EXCEPTION(_is_exception_pending, _next_function) \ + do { \ + if (UNLIKELY(_is_exception_pending)) { \ + HANDLE_PENDING_EXCEPTION(); \ + } else { \ + inst = inst->_next_function(); \ + } \ + } while (false) + +// Code to run before each dex instruction. +#define PREAMBLE() + +template +static JValue ExecuteSwitchImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* code_item, + ShadowFrame& shadow_frame, JValue result_register) { + if (UNLIKELY(!shadow_frame.HasReferenceArray())) { + LOG(FATAL) << "Invalid shadow frame for interpreter use"; + return JValue(); + } + self->VerifyStack(); + instrumentation::Instrumentation* const instrumentation = Runtime::Current()->GetInstrumentation(); + + // As the 'this' object won't change during the execution of current code, we + // want to cache it in local variables. Nevertheless, in order to let the + // garbage collector access it, we store it into sirt references. + SirtRef this_object_ref(self, shadow_frame.GetThisObject(code_item->ins_size_)); + + uint32_t dex_pc = shadow_frame.GetDexPC(); + if (LIKELY(dex_pc == 0)) { // We are entering the method as opposed to deoptimizing.. + if (UNLIKELY(instrumentation->HasMethodEntryListeners())) { + instrumentation->MethodEnterEvent(self, this_object_ref.get(), + shadow_frame.GetMethod(), 0); + } + } + const uint16_t* const insns = code_item->insns_; + const Instruction* inst = Instruction::At(insns + dex_pc); + while (true) { + dex_pc = inst->GetDexPc(insns); + shadow_frame.SetDexPC(dex_pc); + if (UNLIKELY(self->TestAllFlags())) { + CheckSuspend(self); + } + if (UNLIKELY(instrumentation->HasDexPcListeners())) { + instrumentation->DexPcMovedEvent(self, this_object_ref.get(), + shadow_frame.GetMethod(), dex_pc); + } + TraceExecution(shadow_frame, inst, dex_pc, mh); + switch (inst->Opcode()) { + case Instruction::NOP: + PREAMBLE(); + inst = inst->Next_1xx(); + break; + case Instruction::MOVE: + PREAMBLE(); + shadow_frame.SetVReg(inst->VRegA_12x(), + shadow_frame.GetVReg(inst->VRegB_12x())); + inst = inst->Next_1xx(); + break; + case Instruction::MOVE_FROM16: + PREAMBLE(); + shadow_frame.SetVReg(inst->VRegA_22x(), + 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(), + shadow_frame.GetVRegLong(inst->VRegB_12x())); + inst = inst->Next_1xx(); + break; + case Instruction::MOVE_WIDE_FROM16: + PREAMBLE(); + shadow_frame.SetVRegLong(inst->VRegA_22x(), + 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(), + shadow_frame.GetVRegReference(inst->VRegB_12x())); + inst = inst->Next_1xx(); + break; + case Instruction::MOVE_OBJECT_FROM16: + PREAMBLE(); + shadow_frame.SetVRegReference(inst->VRegA_22x(), + 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(), result_register.GetI()); + inst = inst->Next_1xx(); + break; + case Instruction::MOVE_RESULT_WIDE: + PREAMBLE(); + shadow_frame.SetVRegLong(inst->VRegA_11x(), result_register.GetJ()); + inst = inst->Next_1xx(); + break; + case Instruction::MOVE_RESULT_OBJECT: + PREAMBLE(); + shadow_frame.SetVRegReference(inst->VRegA_11x(), result_register.GetL()); + inst = inst->Next_1xx(); + break; + case Instruction::MOVE_EXCEPTION: { + PREAMBLE(); + Throwable* exception = self->GetException(NULL); + self->ClearException(); + shadow_frame.SetVRegReference(inst->VRegA_11x(), exception); + inst = inst->Next_1xx(); + break; + } + case Instruction::RETURN_VOID: { + PREAMBLE(); + JValue result; + if (UNLIKELY(instrumentation->HasMethodExitListeners())) { + instrumentation->MethodExitEvent(self, this_object_ref.get(), + shadow_frame.GetMethod(), inst->GetDexPc(insns), + result); + } + return result; + } + case Instruction::RETURN_VOID_BARRIER: { + PREAMBLE(); + ANDROID_MEMBAR_STORE(); + JValue result; + if (UNLIKELY(instrumentation->HasMethodExitListeners())) { + instrumentation->MethodExitEvent(self, this_object_ref.get(), + shadow_frame.GetMethod(), inst->GetDexPc(insns), + result); + } + return result; + } + case Instruction::RETURN: { + PREAMBLE(); + JValue result; + result.SetJ(0); + result.SetI(shadow_frame.GetVReg(inst->VRegA_11x())); + if (UNLIKELY(instrumentation->HasMethodExitListeners())) { + instrumentation->MethodExitEvent(self, this_object_ref.get(), + shadow_frame.GetMethod(), inst->GetDexPc(insns), + result); + } + return result; + } + case Instruction::RETURN_WIDE: { + PREAMBLE(); + JValue result; + result.SetJ(shadow_frame.GetVRegLong(inst->VRegA_11x())); + if (UNLIKELY(instrumentation->HasMethodExitListeners())) { + instrumentation->MethodExitEvent(self, this_object_ref.get(), + shadow_frame.GetMethod(), inst->GetDexPc(insns), + result); + } + return result; + } + case Instruction::RETURN_OBJECT: { + PREAMBLE(); + JValue result; + result.SetJ(0); + result.SetL(shadow_frame.GetVRegReference(inst->VRegA_11x())); + if (UNLIKELY(instrumentation->HasMethodExitListeners())) { + instrumentation->MethodExitEvent(self, this_object_ref.get(), + shadow_frame.GetMethod(), inst->GetDexPc(insns), + result); + } + return result; + } + case Instruction::CONST_4: { + PREAMBLE(); + uint4_t dst = inst->VRegA_11n(); + int4_t val = inst->VRegB_11n(); + shadow_frame.SetVReg(dst, val); + if (val == 0) { + shadow_frame.SetVRegReference(dst, NULL); + } + inst = inst->Next_1xx(); + break; + } + case Instruction::CONST_16: { + PREAMBLE(); + uint8_t dst = inst->VRegA_21s(); + int16_t val = inst->VRegB_21s(); + shadow_frame.SetVReg(dst, val); + if (val == 0) { + shadow_frame.SetVRegReference(dst, NULL); + } + inst = inst->Next_2xx(); + break; + } + case Instruction::CONST: { + PREAMBLE(); + uint8_t dst = inst->VRegA_31i(); + int32_t val = inst->VRegB_31i(); + shadow_frame.SetVReg(dst, val); + if (val == 0) { + shadow_frame.SetVRegReference(dst, NULL); + } + inst = inst->Next_3xx(); + break; + } + case Instruction::CONST_HIGH16: { + PREAMBLE(); + uint8_t dst = inst->VRegA_21h(); + int32_t val = static_cast(inst->VRegB_21h() << 16); + shadow_frame.SetVReg(dst, val); + if (val == 0) { + shadow_frame.SetVRegReference(dst, NULL); + } + inst = inst->Next_2xx(); + break; + } + case Instruction::CONST_WIDE_16: + PREAMBLE(); + shadow_frame.SetVRegLong(inst->VRegA_21s(), inst->VRegB_21s()); + inst = inst->Next_2xx(); + break; + case Instruction::CONST_WIDE_32: + PREAMBLE(); + shadow_frame.SetVRegLong(inst->VRegA_31i(), inst->VRegB_31i()); + inst = inst->Next_3xx(); + break; + case Instruction::CONST_WIDE: + PREAMBLE(); + shadow_frame.SetVRegLong(inst->VRegA_51l(), inst->VRegB_51l()); + inst = inst->Next_51l(); + break; + case Instruction::CONST_WIDE_HIGH16: + shadow_frame.SetVRegLong(inst->VRegA_21h(), + static_cast(inst->VRegB_21h()) << 48); + inst = inst->Next_2xx(); + break; + case Instruction::CONST_STRING: { + PREAMBLE(); + String* s = ResolveString(self, mh, inst->VRegB_21c()); + if (UNLIKELY(s == NULL)) { + HANDLE_PENDING_EXCEPTION(); + } else { + shadow_frame.SetVRegReference(inst->VRegA_21c(), s); + inst = inst->Next_2xx(); + } + break; + } + case Instruction::CONST_STRING_JUMBO: { + PREAMBLE(); + String* s = ResolveString(self, mh, inst->VRegB_31c()); + if (UNLIKELY(s == NULL)) { + HANDLE_PENDING_EXCEPTION(); + } else { + shadow_frame.SetVRegReference(inst->VRegA_31c(), s); + inst = inst->Next_3xx(); + } + break; + } + case Instruction::CONST_CLASS: { + PREAMBLE(); + Class* c = ResolveVerifyAndClinit(inst->VRegB_21c(), shadow_frame.GetMethod(), + self, false, do_access_check); + if (UNLIKELY(c == NULL)) { + HANDLE_PENDING_EXCEPTION(); + } else { + shadow_frame.SetVRegReference(inst->VRegA_21c(), c); + inst = inst->Next_2xx(); + } + break; + } + case Instruction::MONITOR_ENTER: { + PREAMBLE(); + Object* obj = shadow_frame.GetVRegReference(inst->VRegA_11x()); + if (UNLIKELY(obj == NULL)) { + ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow()); + HANDLE_PENDING_EXCEPTION(); + } else { + DoMonitorEnter(self, obj); + POSSIBLY_HANDLE_PENDING_EXCEPTION(self->IsExceptionPending(), Next_1xx); + } + break; + } + case Instruction::MONITOR_EXIT: { + PREAMBLE(); + Object* obj = shadow_frame.GetVRegReference(inst->VRegA_11x()); + if (UNLIKELY(obj == NULL)) { + ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow()); + HANDLE_PENDING_EXCEPTION(); + } else { + DoMonitorExit(self, obj); + POSSIBLY_HANDLE_PENDING_EXCEPTION(self->IsExceptionPending(), Next_1xx); + } + break; + } + case Instruction::CHECK_CAST: { + PREAMBLE(); + Class* c = ResolveVerifyAndClinit(inst->VRegB_21c(), shadow_frame.GetMethod(), + self, false, do_access_check); + if (UNLIKELY(c == NULL)) { + HANDLE_PENDING_EXCEPTION(); + } else { + Object* obj = shadow_frame.GetVRegReference(inst->VRegA_21c()); + if (UNLIKELY(obj != NULL && !obj->InstanceOf(c))) { + ThrowClassCastException(c, obj->GetClass()); + HANDLE_PENDING_EXCEPTION(); + } else { + inst = inst->Next_2xx(); + } + } + break; + } + case Instruction::INSTANCE_OF: { + PREAMBLE(); + Class* c = ResolveVerifyAndClinit(inst->VRegC_22c(), shadow_frame.GetMethod(), + self, false, do_access_check); + if (UNLIKELY(c == NULL)) { + HANDLE_PENDING_EXCEPTION(); + } else { + Object* obj = shadow_frame.GetVRegReference(inst->VRegB_22c()); + shadow_frame.SetVReg(inst->VRegA_22c(), (obj != NULL && obj->InstanceOf(c)) ? 1 : 0); + inst = inst->Next_2xx(); + } + break; + } + case Instruction::ARRAY_LENGTH: { + PREAMBLE(); + Object* array = shadow_frame.GetVRegReference(inst->VRegB_12x()); + if (UNLIKELY(array == NULL)) { + ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow()); + HANDLE_PENDING_EXCEPTION(); + } else { + shadow_frame.SetVReg(inst->VRegA_12x(), array->AsArray()->GetLength()); + inst = inst->Next_1xx(); + } + break; + } + case Instruction::NEW_INSTANCE: { + PREAMBLE(); + Object* obj = AllocObjectFromCode(inst->VRegB_21c(), shadow_frame.GetMethod(), + self, do_access_check); + if (UNLIKELY(obj == NULL)) { + HANDLE_PENDING_EXCEPTION(); + } else { + shadow_frame.SetVRegReference(inst->VRegA_21c(), obj); + inst = inst->Next_2xx(); + } + break; + } + case Instruction::NEW_ARRAY: { + PREAMBLE(); + int32_t length = shadow_frame.GetVReg(inst->VRegB_22c()); + Object* obj = AllocArrayFromCode(inst->VRegC_22c(), shadow_frame.GetMethod(), + length, self, do_access_check); + if (UNLIKELY(obj == NULL)) { + HANDLE_PENDING_EXCEPTION(); + } else { + shadow_frame.SetVRegReference(inst->VRegA_22c(), obj); + inst = inst->Next_2xx(); + } + break; + } + case Instruction::FILLED_NEW_ARRAY: { + PREAMBLE(); + bool success = DoFilledNewArray(inst, shadow_frame, + self, &result_register); + POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_3xx); + break; + } + case Instruction::FILLED_NEW_ARRAY_RANGE: { + PREAMBLE(); + bool success = DoFilledNewArray(inst, shadow_frame, + self, &result_register); + POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_3xx); + break; + } + case Instruction::FILL_ARRAY_DATA: { + PREAMBLE(); + Object* obj = shadow_frame.GetVRegReference(inst->VRegA_31t()); + if (UNLIKELY(obj == NULL)) { + ThrowNullPointerException(NULL, "null array in FILL_ARRAY_DATA"); + HANDLE_PENDING_EXCEPTION(); + break; + } + Array* array = obj->AsArray(); + DCHECK(array->IsArrayInstance() && !array->IsObjectArray()); + const uint16_t* payload_addr = reinterpret_cast(inst) + inst->VRegB_31t(); + const Instruction::ArrayDataPayload* payload = + reinterpret_cast(payload_addr); + if (UNLIKELY(static_cast(payload->element_count) > array->GetLength())) { + self->ThrowNewExceptionF(shadow_frame.GetCurrentLocationForThrow(), + "Ljava/lang/ArrayIndexOutOfBoundsException;", + "failed FILL_ARRAY_DATA; length=%d, index=%d", + array->GetLength(), payload->element_count); + HANDLE_PENDING_EXCEPTION(); + break; + } + uint32_t size_in_bytes = payload->element_count * payload->element_width; + memcpy(array->GetRawData(payload->element_width), payload->data, size_in_bytes); + inst = inst->Next_3xx(); + break; + } + case Instruction::THROW: { + PREAMBLE(); + Object* exception = shadow_frame.GetVRegReference(inst->VRegA_11x()); + if (UNLIKELY(exception == NULL)) { + ThrowNullPointerException(NULL, "throw with null exception"); + } else { + self->SetException(shadow_frame.GetCurrentLocationForThrow(), exception->AsThrowable()); + } + HANDLE_PENDING_EXCEPTION(); + break; + } + case Instruction::GOTO: { + PREAMBLE(); + inst = inst->RelativeAt(inst->VRegA_10t()); + break; + } + case Instruction::GOTO_16: { + PREAMBLE(); + inst = inst->RelativeAt(inst->VRegA_20t()); + break; + } + case Instruction::GOTO_32: { + PREAMBLE(); + inst = inst->RelativeAt(inst->VRegA_30t()); + break; + } + case Instruction::PACKED_SWITCH: { + PREAMBLE(); + int32_t offset = DoPackedSwitch(inst, shadow_frame); + inst = inst->RelativeAt(offset); + break; + } + case Instruction::SPARSE_SWITCH: { + PREAMBLE(); + int32_t offset = DoSparseSwitch(inst, shadow_frame); + inst = inst->RelativeAt(offset); + break; + } + 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(), 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(), 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(), 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(), result); + inst = inst->Next_2xx(); + break; + } + 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(), result); + inst = inst->Next_2xx(); + break; + } + case Instruction::IF_EQ: { + PREAMBLE(); + if (shadow_frame.GetVReg(inst->VRegA_22t()) == shadow_frame.GetVReg(inst->VRegB_22t())) { + inst = inst->RelativeAt(inst->VRegC_22t()); + } else { + inst = inst->Next_2xx(); + } + break; + } + case Instruction::IF_NE: { + PREAMBLE(); + if (shadow_frame.GetVReg(inst->VRegA_22t()) != shadow_frame.GetVReg(inst->VRegB_22t())) { + inst = inst->RelativeAt(inst->VRegC_22t()); + } else { + inst = inst->Next_2xx(); + } + break; + } + case Instruction::IF_LT: { + PREAMBLE(); + if (shadow_frame.GetVReg(inst->VRegA_22t()) < shadow_frame.GetVReg(inst->VRegB_22t())) { + inst = inst->RelativeAt(inst->VRegC_22t()); + } else { + inst = inst->Next_2xx(); + } + break; + } + case Instruction::IF_GE: { + PREAMBLE(); + if (shadow_frame.GetVReg(inst->VRegA_22t()) >= shadow_frame.GetVReg(inst->VRegB_22t())) { + inst = inst->RelativeAt(inst->VRegC_22t()); + } else { + inst = inst->Next_2xx(); + } + break; + } + case Instruction::IF_GT: { + PREAMBLE(); + if (shadow_frame.GetVReg(inst->VRegA_22t()) > shadow_frame.GetVReg(inst->VRegB_22t())) { + inst = inst->RelativeAt(inst->VRegC_22t()); + } else { + inst = inst->Next_2xx(); + } + break; + } + case Instruction::IF_LE: { + PREAMBLE(); + if (shadow_frame.GetVReg(inst->VRegA_22t()) <= shadow_frame.GetVReg(inst->VRegB_22t())) { + inst = inst->RelativeAt(inst->VRegC_22t()); + } else { + inst = inst->Next_2xx(); + } + break; + } + case Instruction::IF_EQZ: { + PREAMBLE(); + if (shadow_frame.GetVReg(inst->VRegA_21t()) == 0) { + inst = inst->RelativeAt(inst->VRegB_21t()); + } else { + inst = inst->Next_2xx(); + } + break; + } + case Instruction::IF_NEZ: { + PREAMBLE(); + if (shadow_frame.GetVReg(inst->VRegA_21t()) != 0) { + inst = inst->RelativeAt(inst->VRegB_21t()); + } else { + inst = inst->Next_2xx(); + } + break; + } + case Instruction::IF_LTZ: { + PREAMBLE(); + if (shadow_frame.GetVReg(inst->VRegA_21t()) < 0) { + inst = inst->RelativeAt(inst->VRegB_21t()); + } else { + inst = inst->Next_2xx(); + } + break; + } + case Instruction::IF_GEZ: { + PREAMBLE(); + if (shadow_frame.GetVReg(inst->VRegA_21t()) >= 0) { + inst = inst->RelativeAt(inst->VRegB_21t()); + } else { + inst = inst->Next_2xx(); + } + break; + } + case Instruction::IF_GTZ: { + PREAMBLE(); + if (shadow_frame.GetVReg(inst->VRegA_21t()) > 0) { + inst = inst->RelativeAt(inst->VRegB_21t()); + } else { + inst = inst->Next_2xx(); + } + break; + } + case Instruction::IF_LEZ: { + PREAMBLE(); + if (shadow_frame.GetVReg(inst->VRegA_21t()) <= 0) { + inst = inst->RelativeAt(inst->VRegB_21t()); + } else { + inst = inst->Next_2xx(); + } + break; + } + case Instruction::AGET_BOOLEAN: { + PREAMBLE(); + Object* a = shadow_frame.GetVRegReference(inst->VRegB_23x()); + if (UNLIKELY(a == NULL)) { + ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow()); + HANDLE_PENDING_EXCEPTION(); + break; + } + int32_t index = shadow_frame.GetVReg(inst->VRegC_23x()); + BooleanArray* array = a->AsBooleanArray(); + if (LIKELY(array->IsValidIndex(index))) { + shadow_frame.SetVReg(inst->VRegA_23x(), array->GetData()[index]); + inst = inst->Next_2xx(); + } else { + HANDLE_PENDING_EXCEPTION(); + } + break; + } + case Instruction::AGET_BYTE: { + PREAMBLE(); + Object* a = shadow_frame.GetVRegReference(inst->VRegB_23x()); + if (UNLIKELY(a == NULL)) { + ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow()); + HANDLE_PENDING_EXCEPTION(); + break; + } + int32_t index = shadow_frame.GetVReg(inst->VRegC_23x()); + ByteArray* array = a->AsByteArray(); + if (LIKELY(array->IsValidIndex(index))) { + shadow_frame.SetVReg(inst->VRegA_23x(), array->GetData()[index]); + inst = inst->Next_2xx(); + } else { + HANDLE_PENDING_EXCEPTION(); + } + break; + } + case Instruction::AGET_CHAR: { + PREAMBLE(); + Object* a = shadow_frame.GetVRegReference(inst->VRegB_23x()); + if (UNLIKELY(a == NULL)) { + ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow()); + HANDLE_PENDING_EXCEPTION(); + break; + } + int32_t index = shadow_frame.GetVReg(inst->VRegC_23x()); + CharArray* array = a->AsCharArray(); + if (LIKELY(array->IsValidIndex(index))) { + shadow_frame.SetVReg(inst->VRegA_23x(), array->GetData()[index]); + inst = inst->Next_2xx(); + } else { + HANDLE_PENDING_EXCEPTION(); + } + break; + } + case Instruction::AGET_SHORT: { + PREAMBLE(); + Object* a = shadow_frame.GetVRegReference(inst->VRegB_23x()); + if (UNLIKELY(a == NULL)) { + ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow()); + HANDLE_PENDING_EXCEPTION(); + break; + } + int32_t index = shadow_frame.GetVReg(inst->VRegC_23x()); + ShortArray* array = a->AsShortArray(); + if (LIKELY(array->IsValidIndex(index))) { + shadow_frame.SetVReg(inst->VRegA_23x(), array->GetData()[index]); + inst = inst->Next_2xx(); + } else { + HANDLE_PENDING_EXCEPTION(); + } + break; + } + case Instruction::AGET: { + PREAMBLE(); + Object* a = shadow_frame.GetVRegReference(inst->VRegB_23x()); + if (UNLIKELY(a == NULL)) { + ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow()); + HANDLE_PENDING_EXCEPTION(); + break; + } + int32_t index = shadow_frame.GetVReg(inst->VRegC_23x()); + IntArray* array = a->AsIntArray(); + if (LIKELY(array->IsValidIndex(index))) { + shadow_frame.SetVReg(inst->VRegA_23x(), array->GetData()[index]); + inst = inst->Next_2xx(); + } else { + HANDLE_PENDING_EXCEPTION(); + } + break; + } + case Instruction::AGET_WIDE: { + PREAMBLE(); + Object* a = shadow_frame.GetVRegReference(inst->VRegB_23x()); + if (UNLIKELY(a == NULL)) { + ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow()); + HANDLE_PENDING_EXCEPTION(); + break; + } + int32_t index = shadow_frame.GetVReg(inst->VRegC_23x()); + LongArray* array = a->AsLongArray(); + if (LIKELY(array->IsValidIndex(index))) { + shadow_frame.SetVRegLong(inst->VRegA_23x(), array->GetData()[index]); + inst = inst->Next_2xx(); + } else { + HANDLE_PENDING_EXCEPTION(); + } + break; + } + case Instruction::AGET_OBJECT: { + PREAMBLE(); + Object* a = shadow_frame.GetVRegReference(inst->VRegB_23x()); + if (UNLIKELY(a == NULL)) { + ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow()); + HANDLE_PENDING_EXCEPTION(); + break; + } + int32_t index = shadow_frame.GetVReg(inst->VRegC_23x()); + ObjectArray* array = a->AsObjectArray(); + if (LIKELY(array->IsValidIndex(index))) { + shadow_frame.SetVRegReference(inst->VRegA_23x(), array->GetWithoutChecks(index)); + inst = inst->Next_2xx(); + } else { + HANDLE_PENDING_EXCEPTION(); + } + break; + } + case Instruction::APUT_BOOLEAN: { + PREAMBLE(); + Object* a = shadow_frame.GetVRegReference(inst->VRegB_23x()); + if (UNLIKELY(a == NULL)) { + ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow()); + HANDLE_PENDING_EXCEPTION(); + break; + } + uint8_t val = shadow_frame.GetVReg(inst->VRegA_23x()); + int32_t index = shadow_frame.GetVReg(inst->VRegC_23x()); + BooleanArray* array = a->AsBooleanArray(); + if (LIKELY(array->IsValidIndex(index))) { + array->GetData()[index] = val; + inst = inst->Next_2xx(); + } else { + HANDLE_PENDING_EXCEPTION(); + } + break; + } + case Instruction::APUT_BYTE: { + PREAMBLE(); + Object* a = shadow_frame.GetVRegReference(inst->VRegB_23x()); + if (UNLIKELY(a == NULL)) { + ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow()); + HANDLE_PENDING_EXCEPTION(); + break; + } + int8_t val = shadow_frame.GetVReg(inst->VRegA_23x()); + int32_t index = shadow_frame.GetVReg(inst->VRegC_23x()); + ByteArray* array = a->AsByteArray(); + if (LIKELY(array->IsValidIndex(index))) { + array->GetData()[index] = val; + inst = inst->Next_2xx(); + } else { + HANDLE_PENDING_EXCEPTION(); + } + break; + } + case Instruction::APUT_CHAR: { + PREAMBLE(); + Object* a = shadow_frame.GetVRegReference(inst->VRegB_23x()); + if (UNLIKELY(a == NULL)) { + ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow()); + HANDLE_PENDING_EXCEPTION(); + break; + } + uint16_t val = shadow_frame.GetVReg(inst->VRegA_23x()); + int32_t index = shadow_frame.GetVReg(inst->VRegC_23x()); + CharArray* array = a->AsCharArray(); + if (LIKELY(array->IsValidIndex(index))) { + array->GetData()[index] = val; + inst = inst->Next_2xx(); + } else { + HANDLE_PENDING_EXCEPTION(); + } + break; + } + case Instruction::APUT_SHORT: { + PREAMBLE(); + Object* a = shadow_frame.GetVRegReference(inst->VRegB_23x()); + if (UNLIKELY(a == NULL)) { + ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow()); + HANDLE_PENDING_EXCEPTION(); + break; + } + int16_t val = shadow_frame.GetVReg(inst->VRegA_23x()); + int32_t index = shadow_frame.GetVReg(inst->VRegC_23x()); + ShortArray* array = a->AsShortArray(); + if (LIKELY(array->IsValidIndex(index))) { + array->GetData()[index] = val; + inst = inst->Next_2xx(); + } else { + HANDLE_PENDING_EXCEPTION(); + } + break; + } + case Instruction::APUT: { + PREAMBLE(); + Object* a = shadow_frame.GetVRegReference(inst->VRegB_23x()); + if (UNLIKELY(a == NULL)) { + ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow()); + HANDLE_PENDING_EXCEPTION(); + break; + } + int32_t val = shadow_frame.GetVReg(inst->VRegA_23x()); + int32_t index = shadow_frame.GetVReg(inst->VRegC_23x()); + IntArray* array = a->AsIntArray(); + if (LIKELY(array->IsValidIndex(index))) { + array->GetData()[index] = val; + inst = inst->Next_2xx(); + } else { + HANDLE_PENDING_EXCEPTION(); + } + break; + } + case Instruction::APUT_WIDE: { + PREAMBLE(); + Object* a = shadow_frame.GetVRegReference(inst->VRegB_23x()); + if (UNLIKELY(a == NULL)) { + ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow()); + HANDLE_PENDING_EXCEPTION(); + break; + } + int64_t val = shadow_frame.GetVRegLong(inst->VRegA_23x()); + int32_t index = shadow_frame.GetVReg(inst->VRegC_23x()); + LongArray* array = a->AsLongArray(); + if (LIKELY(array->IsValidIndex(index))) { + array->GetData()[index] = val; + inst = inst->Next_2xx(); + } else { + HANDLE_PENDING_EXCEPTION(); + } + break; + } + case Instruction::APUT_OBJECT: { + PREAMBLE(); + Object* a = shadow_frame.GetVRegReference(inst->VRegB_23x()); + if (UNLIKELY(a == NULL)) { + ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow()); + HANDLE_PENDING_EXCEPTION(); + break; + } + int32_t index = shadow_frame.GetVReg(inst->VRegC_23x()); + Object* val = shadow_frame.GetVRegReference(inst->VRegA_23x()); + ObjectArray* array = a->AsObjectArray(); + if (LIKELY(array->IsValidIndex(index) && array->CheckAssignable(val))) { + array->SetWithoutChecks(index, val); + inst = inst->Next_2xx(); + } else { + HANDLE_PENDING_EXCEPTION(); + } + break; + } + case Instruction::IGET_BOOLEAN: { + PREAMBLE(); + bool success = DoFieldGet(self, shadow_frame, inst); + POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); + break; + } + case Instruction::IGET_BYTE: { + PREAMBLE(); + bool success = DoFieldGet(self, shadow_frame, inst); + POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); + break; + } + case Instruction::IGET_CHAR: { + PREAMBLE(); + bool success = DoFieldGet(self, shadow_frame, inst); + POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); + break; + } + case Instruction::IGET_SHORT: { + PREAMBLE(); + bool success = DoFieldGet(self, shadow_frame, inst); + POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); + break; + } + case Instruction::IGET: { + PREAMBLE(); + bool success = DoFieldGet(self, shadow_frame, inst); + POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); + break; + } + case Instruction::IGET_WIDE: { + PREAMBLE(); + bool success = DoFieldGet(self, shadow_frame, inst); + POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); + break; + } + case Instruction::IGET_OBJECT: { + PREAMBLE(); + bool success = DoFieldGet(self, shadow_frame, inst); + POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); + break; + } + case Instruction::IGET_QUICK: { + PREAMBLE(); + bool success = DoIGetQuick(self, shadow_frame, inst); + POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); + break; + } + case Instruction::IGET_WIDE_QUICK: { + PREAMBLE(); + bool success = DoIGetQuick(self, shadow_frame, inst); + POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); + break; + } + case Instruction::IGET_OBJECT_QUICK: { + PREAMBLE(); + bool success = DoIGetQuick(self, shadow_frame, inst); + POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); + break; + } + case Instruction::SGET_BOOLEAN: { + PREAMBLE(); + bool success = DoFieldGet(self, shadow_frame, inst); + POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); + break; + } + case Instruction::SGET_BYTE: { + PREAMBLE(); + bool success = DoFieldGet(self, shadow_frame, inst); + POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); + break; + } + case Instruction::SGET_CHAR: { + PREAMBLE(); + bool success = DoFieldGet(self, shadow_frame, inst); + POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); + break; + } + case Instruction::SGET_SHORT: { + PREAMBLE(); + bool success = DoFieldGet(self, shadow_frame, inst); + POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); + break; + } + case Instruction::SGET: { + PREAMBLE(); + bool success = DoFieldGet(self, shadow_frame, inst); + POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); + break; + } + case Instruction::SGET_WIDE: { + PREAMBLE(); + bool success = DoFieldGet(self, shadow_frame, inst); + POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); + break; + } + case Instruction::SGET_OBJECT: { + PREAMBLE(); + bool success = DoFieldGet(self, shadow_frame, inst); + POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); + break; + } + case Instruction::IPUT_BOOLEAN: { + PREAMBLE(); + bool success = DoFieldPut(self, shadow_frame, inst); + POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); + break; + } + case Instruction::IPUT_BYTE: { + PREAMBLE(); + bool success = DoFieldPut(self, shadow_frame, inst); + POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); + break; + } + case Instruction::IPUT_CHAR: { + PREAMBLE(); + bool success = DoFieldPut(self, shadow_frame, inst); + POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); + break; + } + case Instruction::IPUT_SHORT: { + PREAMBLE(); + bool success = DoFieldPut(self, shadow_frame, inst); + POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); + break; + } + case Instruction::IPUT: { + PREAMBLE(); + bool success = DoFieldPut(self, shadow_frame, inst); + POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); + break; + } + case Instruction::IPUT_WIDE: { + PREAMBLE(); + bool success = DoFieldPut(self, shadow_frame, inst); + POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); + break; + } + case Instruction::IPUT_OBJECT: { + PREAMBLE(); + bool success = DoFieldPut(self, shadow_frame, inst); + POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); + break; + } + case Instruction::IPUT_QUICK: { + PREAMBLE(); + bool success = DoIPutQuick(self, shadow_frame, inst); + POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); + break; + } + case Instruction::IPUT_WIDE_QUICK: { + PREAMBLE(); + bool success = DoIPutQuick(self, shadow_frame, inst); + POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); + break; + } + case Instruction::IPUT_OBJECT_QUICK: { + PREAMBLE(); + bool success = DoIPutQuick(self, shadow_frame, inst); + POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); + break; + } + case Instruction::SPUT_BOOLEAN: { + PREAMBLE(); + bool success = DoFieldPut(self, shadow_frame, inst); + POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); + break; + } + case Instruction::SPUT_BYTE: { + PREAMBLE(); + bool success = DoFieldPut(self, shadow_frame, inst); + POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); + break; + } + case Instruction::SPUT_CHAR: { + PREAMBLE(); + bool success = DoFieldPut(self, shadow_frame, inst); + POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); + break; + } + case Instruction::SPUT_SHORT: { + PREAMBLE(); + bool success = DoFieldPut(self, shadow_frame, inst); + POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); + break; + } + case Instruction::SPUT: { + PREAMBLE(); + bool success = DoFieldPut(self, shadow_frame, inst); + POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); + break; + } + case Instruction::SPUT_WIDE: { + PREAMBLE(); + bool success = DoFieldPut(self, shadow_frame, inst); + POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); + break; + } + case Instruction::SPUT_OBJECT: { + PREAMBLE(); + bool success = DoFieldPut(self, shadow_frame, inst); + POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); + break; + } + case Instruction::INVOKE_VIRTUAL: { + PREAMBLE(); + bool success = DoInvoke(self, shadow_frame, inst, &result_register); + POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_3xx); + break; + } + case Instruction::INVOKE_VIRTUAL_RANGE: { + PREAMBLE(); + bool success = DoInvoke(self, shadow_frame, inst, &result_register); + POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_3xx); + break; + } + case Instruction::INVOKE_SUPER: { + PREAMBLE(); + bool success = DoInvoke(self, shadow_frame, inst, &result_register); + POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_3xx); + break; + } + case Instruction::INVOKE_SUPER_RANGE: { + PREAMBLE(); + bool success = DoInvoke(self, shadow_frame, inst, &result_register); + POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_3xx); + break; + } + case Instruction::INVOKE_DIRECT: { + PREAMBLE(); + bool success = DoInvoke(self, shadow_frame, inst, &result_register); + POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_3xx); + break; + } + case Instruction::INVOKE_DIRECT_RANGE: { + PREAMBLE(); + bool success = DoInvoke(self, shadow_frame, inst, &result_register); + POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_3xx); + break; + } + case Instruction::INVOKE_INTERFACE: { + PREAMBLE(); + bool success = DoInvoke(self, shadow_frame, inst, &result_register); + POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_3xx); + break; + } + case Instruction::INVOKE_INTERFACE_RANGE: { + PREAMBLE(); + bool success = DoInvoke(self, shadow_frame, inst, &result_register); + POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_3xx); + break; + } + case Instruction::INVOKE_STATIC: { + PREAMBLE(); + bool success = DoInvoke(self, shadow_frame, inst, &result_register); + POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_3xx); + break; + } + case Instruction::INVOKE_STATIC_RANGE: { + PREAMBLE(); + bool success = DoInvoke(self, shadow_frame, inst, &result_register); + POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_3xx); + break; + } + case Instruction::INVOKE_VIRTUAL_QUICK: { + PREAMBLE(); + bool success = DoInvokeVirtualQuick(self, shadow_frame, inst, &result_register); + POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_3xx); + break; + } + case Instruction::INVOKE_VIRTUAL_RANGE_QUICK: { + PREAMBLE(); + bool success = DoInvokeVirtualQuick(self, shadow_frame, inst, &result_register); + POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_3xx); + break; + } + case Instruction::NEG_INT: + PREAMBLE(); + shadow_frame.SetVReg(inst->VRegA_12x(), -shadow_frame.GetVReg(inst->VRegB_12x())); + inst = inst->Next_1xx(); + break; + case Instruction::NOT_INT: + PREAMBLE(); + shadow_frame.SetVReg(inst->VRegA_12x(), ~shadow_frame.GetVReg(inst->VRegB_12x())); + inst = inst->Next_1xx(); + break; + case Instruction::NEG_LONG: + PREAMBLE(); + shadow_frame.SetVRegLong(inst->VRegA_12x(), -shadow_frame.GetVRegLong(inst->VRegB_12x())); + inst = inst->Next_1xx(); + break; + case Instruction::NOT_LONG: + PREAMBLE(); + shadow_frame.SetVRegLong(inst->VRegA_12x(), ~shadow_frame.GetVRegLong(inst->VRegB_12x())); + inst = inst->Next_1xx(); + break; + case Instruction::NEG_FLOAT: + PREAMBLE(); + shadow_frame.SetVRegFloat(inst->VRegA_12x(), -shadow_frame.GetVRegFloat(inst->VRegB_12x())); + inst = inst->Next_1xx(); + break; + case Instruction::NEG_DOUBLE: + PREAMBLE(); + shadow_frame.SetVRegDouble(inst->VRegA_12x(), -shadow_frame.GetVRegDouble(inst->VRegB_12x())); + inst = inst->Next_1xx(); + break; + case Instruction::INT_TO_LONG: + PREAMBLE(); + shadow_frame.SetVRegLong(inst->VRegA_12x(), shadow_frame.GetVReg(inst->VRegB_12x())); + inst = inst->Next_1xx(); + break; + case Instruction::INT_TO_FLOAT: + PREAMBLE(); + shadow_frame.SetVRegFloat(inst->VRegA_12x(), shadow_frame.GetVReg(inst->VRegB_12x())); + inst = inst->Next_1xx(); + break; + case Instruction::INT_TO_DOUBLE: + PREAMBLE(); + shadow_frame.SetVRegDouble(inst->VRegA_12x(), shadow_frame.GetVReg(inst->VRegB_12x())); + inst = inst->Next_1xx(); + break; + case Instruction::LONG_TO_INT: + PREAMBLE(); + shadow_frame.SetVReg(inst->VRegA_12x(), shadow_frame.GetVRegLong(inst->VRegB_12x())); + inst = inst->Next_1xx(); + break; + case Instruction::LONG_TO_FLOAT: + PREAMBLE(); + shadow_frame.SetVRegFloat(inst->VRegA_12x(), shadow_frame.GetVRegLong(inst->VRegB_12x())); + inst = inst->Next_1xx(); + break; + case Instruction::LONG_TO_DOUBLE: + PREAMBLE(); + shadow_frame.SetVRegDouble(inst->VRegA_12x(), shadow_frame.GetVRegLong(inst->VRegB_12x())); + inst = inst->Next_1xx(); + break; + case Instruction::FLOAT_TO_INT: { + PREAMBLE(); + float val = shadow_frame.GetVRegFloat(inst->VRegB_12x()); + int32_t result; + if (val != val) { + result = 0; + } else if (val > static_cast(kMaxInt)) { + result = kMaxInt; + } else if (val < static_cast(kMinInt)) { + result = kMinInt; + } else { + result = val; + } + shadow_frame.SetVReg(inst->VRegA_12x(), result); + inst = inst->Next_1xx(); + break; + } + case Instruction::FLOAT_TO_LONG: { + PREAMBLE(); + float val = shadow_frame.GetVRegFloat(inst->VRegB_12x()); + int64_t result; + if (val != val) { + result = 0; + } else if (val > static_cast(kMaxLong)) { + result = kMaxLong; + } else if (val < static_cast(kMinLong)) { + result = kMinLong; + } else { + result = val; + } + shadow_frame.SetVRegLong(inst->VRegA_12x(), result); + inst = inst->Next_1xx(); + break; + } + case Instruction::FLOAT_TO_DOUBLE: + PREAMBLE(); + shadow_frame.SetVRegDouble(inst->VRegA_12x(), shadow_frame.GetVRegFloat(inst->VRegB_12x())); + inst = inst->Next_1xx(); + break; + case Instruction::DOUBLE_TO_INT: { + PREAMBLE(); + double val = shadow_frame.GetVRegDouble(inst->VRegB_12x()); + int32_t result; + if (val != val) { + result = 0; + } else if (val > static_cast(kMaxInt)) { + result = kMaxInt; + } else if (val < static_cast(kMinInt)) { + result = kMinInt; + } else { + result = val; + } + shadow_frame.SetVReg(inst->VRegA_12x(), result); + inst = inst->Next_1xx(); + break; + } + case Instruction::DOUBLE_TO_LONG: { + PREAMBLE(); + double val = shadow_frame.GetVRegDouble(inst->VRegB_12x()); + int64_t result; + if (val != val) { + result = 0; + } else if (val > static_cast(kMaxLong)) { + result = kMaxLong; + } else if (val < static_cast(kMinLong)) { + result = kMinLong; + } else { + result = val; + } + shadow_frame.SetVRegLong(inst->VRegA_12x(), result); + inst = inst->Next_1xx(); + break; + } + case Instruction::DOUBLE_TO_FLOAT: + PREAMBLE(); + shadow_frame.SetVRegFloat(inst->VRegA_12x(), shadow_frame.GetVRegDouble(inst->VRegB_12x())); + inst = inst->Next_1xx(); + break; + case Instruction::INT_TO_BYTE: + PREAMBLE(); + shadow_frame.SetVReg(inst->VRegA_12x(), + static_cast(shadow_frame.GetVReg(inst->VRegB_12x()))); + inst = inst->Next_1xx(); + break; + case Instruction::INT_TO_CHAR: + PREAMBLE(); + shadow_frame.SetVReg(inst->VRegA_12x(), + static_cast(shadow_frame.GetVReg(inst->VRegB_12x()))); + inst = inst->Next_1xx(); + break; + case Instruction::INT_TO_SHORT: + PREAMBLE(); + shadow_frame.SetVReg(inst->VRegA_12x(), + static_cast(shadow_frame.GetVReg(inst->VRegB_12x()))); + inst = inst->Next_1xx(); + break; + case Instruction::ADD_INT: + PREAMBLE(); + shadow_frame.SetVReg(inst->VRegA_23x(), + 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(), + 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(), + 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(), + 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(), + 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(), + 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(), + 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(), + static_cast(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(), + 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(), + 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(), + 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(), + 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(), + 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(), + 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(), + 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(), + 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(), + 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(), + 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(), + 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(), + 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(), + 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(), + static_cast(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(), + 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(), + 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(), + 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(), + 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(), + 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(), + 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(), + 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(), + 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(), + 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(), + 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(); + shadow_frame.SetVReg(vregA, + shadow_frame.GetVReg(vregA) + + shadow_frame.GetVReg(inst->VRegB_12x())); + inst = inst->Next_1xx(); + break; + } + case Instruction::SUB_INT_2ADDR: { + PREAMBLE(); + uint4_t vregA = inst->VRegA_12x(); + shadow_frame.SetVReg(vregA, + shadow_frame.GetVReg(vregA) - + shadow_frame.GetVReg(inst->VRegB_12x())); + inst = inst->Next_1xx(); + break; + } + case Instruction::MUL_INT_2ADDR: { + PREAMBLE(); + uint4_t vregA = inst->VRegA_12x(); + shadow_frame.SetVReg(vregA, + shadow_frame.GetVReg(vregA) * + shadow_frame.GetVReg(inst->VRegB_12x())); + inst = inst->Next_1xx(); + break; + } + case Instruction::DIV_INT_2ADDR: { + PREAMBLE(); + uint4_t vregA = inst->VRegA_12x(); + bool success = DoIntDivide(shadow_frame, vregA, shadow_frame.GetVReg(vregA), + shadow_frame.GetVReg(inst->VRegB_12x())); + POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_1xx); + break; + } + case Instruction::REM_INT_2ADDR: { + PREAMBLE(); + uint4_t vregA = inst->VRegA_12x(); + bool success = DoIntRemainder(shadow_frame, vregA, shadow_frame.GetVReg(vregA), + shadow_frame.GetVReg(inst->VRegB_12x())); + POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_1xx); + break; + } + case Instruction::SHL_INT_2ADDR: { + PREAMBLE(); + uint4_t vregA = inst->VRegA_12x(); + shadow_frame.SetVReg(vregA, + shadow_frame.GetVReg(vregA) << + (shadow_frame.GetVReg(inst->VRegB_12x()) & 0x1f)); + inst = inst->Next_1xx(); + break; + } + case Instruction::SHR_INT_2ADDR: { + PREAMBLE(); + uint4_t vregA = inst->VRegA_12x(); + shadow_frame.SetVReg(vregA, + shadow_frame.GetVReg(vregA) >> + (shadow_frame.GetVReg(inst->VRegB_12x()) & 0x1f)); + inst = inst->Next_1xx(); + break; + } + case Instruction::USHR_INT_2ADDR: { + PREAMBLE(); + uint4_t vregA = inst->VRegA_12x(); + shadow_frame.SetVReg(vregA, + static_cast(shadow_frame.GetVReg(vregA)) >> + (shadow_frame.GetVReg(inst->VRegB_12x()) & 0x1f)); + inst = inst->Next_1xx(); + break; + } + case Instruction::AND_INT_2ADDR: { + PREAMBLE(); + uint4_t vregA = inst->VRegA_12x(); + shadow_frame.SetVReg(vregA, + shadow_frame.GetVReg(vregA) & + shadow_frame.GetVReg(inst->VRegB_12x())); + inst = inst->Next_1xx(); + break; + } + case Instruction::OR_INT_2ADDR: { + PREAMBLE(); + uint4_t vregA = inst->VRegA_12x(); + shadow_frame.SetVReg(vregA, + shadow_frame.GetVReg(vregA) | + shadow_frame.GetVReg(inst->VRegB_12x())); + inst = inst->Next_1xx(); + break; + } + case Instruction::XOR_INT_2ADDR: { + PREAMBLE(); + uint4_t vregA = inst->VRegA_12x(); + shadow_frame.SetVReg(vregA, + shadow_frame.GetVReg(vregA) ^ + shadow_frame.GetVReg(inst->VRegB_12x())); + inst = inst->Next_1xx(); + break; + } + case Instruction::ADD_LONG_2ADDR: { + PREAMBLE(); + uint4_t vregA = inst->VRegA_12x(); + shadow_frame.SetVRegLong(vregA, + shadow_frame.GetVRegLong(vregA) + + shadow_frame.GetVRegLong(inst->VRegB_12x())); + inst = inst->Next_1xx(); + break; + } + case Instruction::SUB_LONG_2ADDR: { + PREAMBLE(); + uint4_t vregA = inst->VRegA_12x(); + shadow_frame.SetVRegLong(vregA, + shadow_frame.GetVRegLong(vregA) - + shadow_frame.GetVRegLong(inst->VRegB_12x())); + inst = inst->Next_1xx(); + break; + } + case Instruction::MUL_LONG_2ADDR: { + PREAMBLE(); + uint4_t vregA = inst->VRegA_12x(); + shadow_frame.SetVRegLong(vregA, + shadow_frame.GetVRegLong(vregA) * + shadow_frame.GetVRegLong(inst->VRegB_12x())); + inst = inst->Next_1xx(); + break; + } + case Instruction::DIV_LONG_2ADDR: { + PREAMBLE(); + uint4_t vregA = inst->VRegA_12x(); + DoLongDivide(shadow_frame, vregA, shadow_frame.GetVRegLong(vregA), + shadow_frame.GetVRegLong(inst->VRegB_12x())); + POSSIBLY_HANDLE_PENDING_EXCEPTION(self->IsExceptionPending(), Next_1xx); + break; + } + case Instruction::REM_LONG_2ADDR: { + PREAMBLE(); + uint4_t vregA = inst->VRegA_12x(); + DoLongRemainder(shadow_frame, vregA, shadow_frame.GetVRegLong(vregA), + shadow_frame.GetVRegLong(inst->VRegB_12x())); + POSSIBLY_HANDLE_PENDING_EXCEPTION(self->IsExceptionPending(), Next_1xx); + break; + } + case Instruction::AND_LONG_2ADDR: { + PREAMBLE(); + uint4_t vregA = inst->VRegA_12x(); + shadow_frame.SetVRegLong(vregA, + shadow_frame.GetVRegLong(vregA) & + shadow_frame.GetVRegLong(inst->VRegB_12x())); + inst = inst->Next_1xx(); + break; + } + case Instruction::OR_LONG_2ADDR: { + PREAMBLE(); + uint4_t vregA = inst->VRegA_12x(); + shadow_frame.SetVRegLong(vregA, + shadow_frame.GetVRegLong(vregA) | + shadow_frame.GetVRegLong(inst->VRegB_12x())); + inst = inst->Next_1xx(); + break; + } + case Instruction::XOR_LONG_2ADDR: { + PREAMBLE(); + uint4_t vregA = inst->VRegA_12x(); + shadow_frame.SetVRegLong(vregA, + shadow_frame.GetVRegLong(vregA) ^ + shadow_frame.GetVRegLong(inst->VRegB_12x())); + inst = inst->Next_1xx(); + break; + } + case Instruction::SHL_LONG_2ADDR: { + PREAMBLE(); + uint4_t vregA = inst->VRegA_12x(); + shadow_frame.SetVRegLong(vregA, + shadow_frame.GetVRegLong(vregA) << + (shadow_frame.GetVReg(inst->VRegB_12x()) & 0x3f)); + inst = inst->Next_1xx(); + break; + } + case Instruction::SHR_LONG_2ADDR: { + PREAMBLE(); + uint4_t vregA = inst->VRegA_12x(); + shadow_frame.SetVRegLong(vregA, + shadow_frame.GetVRegLong(vregA) >> + (shadow_frame.GetVReg(inst->VRegB_12x()) & 0x3f)); + inst = inst->Next_1xx(); + break; + } + case Instruction::USHR_LONG_2ADDR: { + PREAMBLE(); + uint4_t vregA = inst->VRegA_12x(); + shadow_frame.SetVRegLong(vregA, + static_cast(shadow_frame.GetVRegLong(vregA)) >> + (shadow_frame.GetVReg(inst->VRegB_12x()) & 0x3f)); + inst = inst->Next_1xx(); + break; + } + case Instruction::ADD_FLOAT_2ADDR: { + PREAMBLE(); + uint4_t vregA = inst->VRegA_12x(); + shadow_frame.SetVRegFloat(vregA, + shadow_frame.GetVRegFloat(vregA) + + shadow_frame.GetVRegFloat(inst->VRegB_12x())); + inst = inst->Next_1xx(); + break; + } + case Instruction::SUB_FLOAT_2ADDR: { + PREAMBLE(); + uint4_t vregA = inst->VRegA_12x(); + shadow_frame.SetVRegFloat(vregA, + shadow_frame.GetVRegFloat(vregA) - + shadow_frame.GetVRegFloat(inst->VRegB_12x())); + inst = inst->Next_1xx(); + break; + } + case Instruction::MUL_FLOAT_2ADDR: { + PREAMBLE(); + uint4_t vregA = inst->VRegA_12x(); + shadow_frame.SetVRegFloat(vregA, + shadow_frame.GetVRegFloat(vregA) * + shadow_frame.GetVRegFloat(inst->VRegB_12x())); + inst = inst->Next_1xx(); + break; + } + case Instruction::DIV_FLOAT_2ADDR: { + PREAMBLE(); + uint4_t vregA = inst->VRegA_12x(); + shadow_frame.SetVRegFloat(vregA, + shadow_frame.GetVRegFloat(vregA) / + shadow_frame.GetVRegFloat(inst->VRegB_12x())); + inst = inst->Next_1xx(); + break; + } + case Instruction::REM_FLOAT_2ADDR: { + PREAMBLE(); + uint4_t vregA = inst->VRegA_12x(); + shadow_frame.SetVRegFloat(vregA, + fmodf(shadow_frame.GetVRegFloat(vregA), + shadow_frame.GetVRegFloat(inst->VRegB_12x()))); + inst = inst->Next_1xx(); + break; + } + case Instruction::ADD_DOUBLE_2ADDR: { + PREAMBLE(); + uint4_t vregA = inst->VRegA_12x(); + shadow_frame.SetVRegDouble(vregA, + shadow_frame.GetVRegDouble(vregA) + + shadow_frame.GetVRegDouble(inst->VRegB_12x())); + inst = inst->Next_1xx(); + break; + } + case Instruction::SUB_DOUBLE_2ADDR: { + PREAMBLE(); + uint4_t vregA = inst->VRegA_12x(); + shadow_frame.SetVRegDouble(vregA, + shadow_frame.GetVRegDouble(vregA) - + shadow_frame.GetVRegDouble(inst->VRegB_12x())); + inst = inst->Next_1xx(); + break; + } + case Instruction::MUL_DOUBLE_2ADDR: { + PREAMBLE(); + uint4_t vregA = inst->VRegA_12x(); + shadow_frame.SetVRegDouble(vregA, + shadow_frame.GetVRegDouble(vregA) * + shadow_frame.GetVRegDouble(inst->VRegB_12x())); + inst = inst->Next_1xx(); + break; + } + case Instruction::DIV_DOUBLE_2ADDR: { + PREAMBLE(); + uint4_t vregA = inst->VRegA_12x(); + shadow_frame.SetVRegDouble(vregA, + shadow_frame.GetVRegDouble(vregA) / + shadow_frame.GetVRegDouble(inst->VRegB_12x())); + inst = inst->Next_1xx(); + break; + } + case Instruction::REM_DOUBLE_2ADDR: { + PREAMBLE(); + uint4_t vregA = inst->VRegA_12x(); + shadow_frame.SetVRegDouble(vregA, + fmod(shadow_frame.GetVRegDouble(vregA), + shadow_frame.GetVRegDouble(inst->VRegB_12x()))); + inst = inst->Next_1xx(); + break; + } + case Instruction::ADD_INT_LIT16: + PREAMBLE(); + shadow_frame.SetVReg(inst->VRegA_22s(), + shadow_frame.GetVReg(inst->VRegB_22s()) + + inst->VRegC_22s()); + inst = inst->Next_2xx(); + break; + case Instruction::RSUB_INT: + PREAMBLE(); + shadow_frame.SetVReg(inst->VRegA_22s(), + inst->VRegC_22s() - + shadow_frame.GetVReg(inst->VRegB_22s())); + inst = inst->Next_2xx(); + break; + case Instruction::MUL_INT_LIT16: + PREAMBLE(); + shadow_frame.SetVReg(inst->VRegA_22s(), + shadow_frame.GetVReg(inst->VRegB_22s()) * + inst->VRegC_22s()); + inst = inst->Next_2xx(); + break; + case Instruction::DIV_INT_LIT16: { + PREAMBLE(); + bool success = DoIntDivide(shadow_frame, inst->VRegA_22s(), + shadow_frame.GetVReg(inst->VRegB_22s()), 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(), + shadow_frame.GetVReg(inst->VRegB_22s()), inst->VRegC_22s()); + POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); + break; + } + case Instruction::AND_INT_LIT16: + PREAMBLE(); + shadow_frame.SetVReg(inst->VRegA_22s(), + shadow_frame.GetVReg(inst->VRegB_22s()) & + inst->VRegC_22s()); + inst = inst->Next_2xx(); + break; + case Instruction::OR_INT_LIT16: + PREAMBLE(); + shadow_frame.SetVReg(inst->VRegA_22s(), + shadow_frame.GetVReg(inst->VRegB_22s()) | + inst->VRegC_22s()); + inst = inst->Next_2xx(); + break; + case Instruction::XOR_INT_LIT16: + PREAMBLE(); + shadow_frame.SetVReg(inst->VRegA_22s(), + shadow_frame.GetVReg(inst->VRegB_22s()) ^ + inst->VRegC_22s()); + inst = inst->Next_2xx(); + break; + case Instruction::ADD_INT_LIT8: + PREAMBLE(); + shadow_frame.SetVReg(inst->VRegA_22b(), + 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->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(), + 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(), + 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(), + 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(), + 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(), + 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(), + 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(), + 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(), + 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(), + static_cast(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_EB ... Instruction::UNUSED_FF: + case Instruction::UNUSED_79: + case Instruction::UNUSED_7A: + UnexpectedOpcode(inst, mh); + } + } +} // NOLINT(readability/fn_size) + +// Explicit definitions of ExecuteSwitchImpl. +template JValue ExecuteSwitchImpl(Thread* self, MethodHelper& mh, + const DexFile::CodeItem* code_item, + ShadowFrame& shadow_frame, JValue result_register); +template JValue ExecuteSwitchImpl(Thread* self, MethodHelper& mh, + const DexFile::CodeItem* code_item, + ShadowFrame& shadow_frame, JValue result_register); + +} // namespace interpreter +} // namespace art From 1eda2268e84d384256814cb6c2ba2440a848f9ed Mon Sep 17 00:00:00 2001 From: Sebastien Hertz Date: Mon, 9 Sep 2013 16:53:14 +0200 Subject: [PATCH 0020/2402] Move thread suspend check at safepoints. Move CheckSuspend on backward branch, return and exception handling. Bug: 10603072 Change-Id: Ic6c2c5066f133a345323d46edca7afde350849d8 --- runtime/interpreter/interpreter_common.h | 4 + .../interpreter_goto_table_impl.cc | 76 ++++++++- .../interpreter/interpreter_switch_impl.cc | 151 +++++++++++++++--- 3 files changed, 206 insertions(+), 25 deletions(-) diff --git a/runtime/interpreter/interpreter_common.h b/runtime/interpreter/interpreter_common.h index 4d9317f6ce3..8cd526a79ba 100644 --- a/runtime/interpreter/interpreter_common.h +++ b/runtime/interpreter/interpreter_common.h @@ -510,6 +510,10 @@ static inline void TraceExecution(const ShadowFrame& shadow_frame, const Instruc } } +static inline bool IsBackwardBranch(int32_t branch_offset) { + return branch_offset <= 0; +} + } // namespace interpreter } // namespace art diff --git a/runtime/interpreter/interpreter_goto_table_impl.cc b/runtime/interpreter/interpreter_goto_table_impl.cc index f76fbfb30f5..67517ea0f67 100644 --- a/runtime/interpreter/interpreter_goto_table_impl.cc +++ b/runtime/interpreter/interpreter_goto_table_impl.cc @@ -36,9 +36,6 @@ namespace interpreter { inst = inst->RelativeAt(disp); \ dex_pc = static_cast(static_cast(dex_pc) + disp); \ shadow_frame.SetDexPC(dex_pc); \ - if (UNLIKELY(self->TestAllFlags())) { \ - CheckSuspend(self); \ - } \ TraceExecution(shadow_frame, inst, dex_pc, mh); \ goto *currentHandlersTable[inst->Opcode()]; \ } while (false) @@ -64,10 +61,6 @@ namespace interpreter { #define HANDLE_INSTRUCTION_START(opcode) op_##opcode: // NOLINT(whitespace/labels) #define HANDLE_INSTRUCTION_END() UNREACHABLE_CODE_CHECK() -static inline bool IsBackwardBranch(int32_t branch_offset) { - return branch_offset <= 0; -} - template JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* code_item, ShadowFrame& shadow_frame, JValue result_register) { @@ -204,6 +197,9 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* HANDLE_INSTRUCTION_START(RETURN_VOID) { JValue result; + if (UNLIKELY(self->TestAllFlags())) { + CheckSuspend(self); + } if (UNLIKELY(instrumentation->HasMethodExitListeners())) { instrumentation->MethodExitEvent(self, this_object_ref.get(), shadow_frame.GetMethod(), dex_pc, @@ -216,6 +212,9 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* HANDLE_INSTRUCTION_START(RETURN_VOID_BARRIER) { ANDROID_MEMBAR_STORE(); JValue result; + if (UNLIKELY(self->TestAllFlags())) { + CheckSuspend(self); + } if (UNLIKELY(instrumentation->HasMethodExitListeners())) { instrumentation->MethodExitEvent(self, this_object_ref.get(), shadow_frame.GetMethod(), dex_pc, @@ -229,6 +228,9 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* JValue result; result.SetJ(0); result.SetI(shadow_frame.GetVReg(inst->VRegA_11x())); + if (UNLIKELY(self->TestAllFlags())) { + CheckSuspend(self); + } if (UNLIKELY(instrumentation->HasMethodExitListeners())) { instrumentation->MethodExitEvent(self, this_object_ref.get(), shadow_frame.GetMethod(), dex_pc, @@ -241,6 +243,9 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* HANDLE_INSTRUCTION_START(RETURN_WIDE) { JValue result; result.SetJ(shadow_frame.GetVRegLong(inst->VRegA_11x())); + if (UNLIKELY(self->TestAllFlags())) { + CheckSuspend(self); + } if (UNLIKELY(instrumentation->HasMethodExitListeners())) { instrumentation->MethodExitEvent(self, this_object_ref.get(), shadow_frame.GetMethod(), dex_pc, @@ -254,6 +259,9 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* JValue result; result.SetJ(0); result.SetL(shadow_frame.GetVRegReference(inst->VRegA_11x())); + if (UNLIKELY(self->TestAllFlags())) { + CheckSuspend(self); + } if (UNLIKELY(instrumentation->HasMethodExitListeners())) { instrumentation->MethodExitEvent(self, this_object_ref.get(), shadow_frame.GetMethod(), dex_pc, @@ -507,6 +515,9 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* HANDLE_INSTRUCTION_START(GOTO) { int8_t offset = inst->VRegA_10t(); if (IsBackwardBranch(offset)) { + if (UNLIKELY(self->TestAllFlags())) { + CheckSuspend(self); + } if (UNLIKELY(instrumentation->HasDexPcListeners())) { currentHandlersTable = instrumentationHandlersTable; } else { @@ -520,6 +531,9 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* HANDLE_INSTRUCTION_START(GOTO_16) { int16_t offset = inst->VRegA_20t(); if (IsBackwardBranch(offset)) { + if (UNLIKELY(self->TestAllFlags())) { + CheckSuspend(self); + } if (UNLIKELY(instrumentation->HasDexPcListeners())) { currentHandlersTable = instrumentationHandlersTable; } else { @@ -533,6 +547,9 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* HANDLE_INSTRUCTION_START(GOTO_32) { int32_t offset = inst->VRegA_30t(); if (IsBackwardBranch(offset)) { + if (UNLIKELY(self->TestAllFlags())) { + CheckSuspend(self); + } if (UNLIKELY(instrumentation->HasDexPcListeners())) { currentHandlersTable = instrumentationHandlersTable; } else { @@ -546,6 +563,9 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* HANDLE_INSTRUCTION_START(PACKED_SWITCH) { int32_t offset = DoPackedSwitch(inst, shadow_frame); if (IsBackwardBranch(offset)) { + if (UNLIKELY(self->TestAllFlags())) { + CheckSuspend(self); + } if (UNLIKELY(instrumentation->HasDexPcListeners())) { currentHandlersTable = instrumentationHandlersTable; } else { @@ -559,6 +579,9 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* HANDLE_INSTRUCTION_START(SPARSE_SWITCH) { int32_t offset = DoSparseSwitch(inst, shadow_frame); if (IsBackwardBranch(offset)) { + if (UNLIKELY(self->TestAllFlags())) { + CheckSuspend(self); + } if (UNLIKELY(instrumentation->HasDexPcListeners())) { currentHandlersTable = instrumentationHandlersTable; } else { @@ -653,6 +676,9 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* if (shadow_frame.GetVReg(inst->VRegA_22t()) == shadow_frame.GetVReg(inst->VRegB_22t())) { int16_t offset = inst->VRegC_22t(); if (IsBackwardBranch(offset)) { + if (UNLIKELY(self->TestAllFlags())) { + CheckSuspend(self); + } if (UNLIKELY(instrumentation->HasDexPcListeners())) { currentHandlersTable = instrumentationHandlersTable; } else { @@ -670,6 +696,9 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* if (shadow_frame.GetVReg(inst->VRegA_22t()) != shadow_frame.GetVReg(inst->VRegB_22t())) { int16_t offset = inst->VRegC_22t(); if (IsBackwardBranch(offset)) { + if (UNLIKELY(self->TestAllFlags())) { + CheckSuspend(self); + } if (UNLIKELY(instrumentation->HasDexPcListeners())) { currentHandlersTable = instrumentationHandlersTable; } else { @@ -687,6 +716,9 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* if (shadow_frame.GetVReg(inst->VRegA_22t()) < shadow_frame.GetVReg(inst->VRegB_22t())) { int16_t offset = inst->VRegC_22t(); if (IsBackwardBranch(offset)) { + if (UNLIKELY(self->TestAllFlags())) { + CheckSuspend(self); + } if (UNLIKELY(instrumentation->HasDexPcListeners())) { currentHandlersTable = instrumentationHandlersTable; } else { @@ -704,6 +736,9 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* if (shadow_frame.GetVReg(inst->VRegA_22t()) >= shadow_frame.GetVReg(inst->VRegB_22t())) { int16_t offset = inst->VRegC_22t(); if (IsBackwardBranch(offset)) { + if (UNLIKELY(self->TestAllFlags())) { + CheckSuspend(self); + } if (UNLIKELY(instrumentation->HasDexPcListeners())) { currentHandlersTable = instrumentationHandlersTable; } else { @@ -721,6 +756,9 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* if (shadow_frame.GetVReg(inst->VRegA_22t()) > shadow_frame.GetVReg(inst->VRegB_22t())) { int16_t offset = inst->VRegC_22t(); if (IsBackwardBranch(offset)) { + if (UNLIKELY(self->TestAllFlags())) { + CheckSuspend(self); + } if (UNLIKELY(instrumentation->HasDexPcListeners())) { currentHandlersTable = instrumentationHandlersTable; } else { @@ -738,6 +776,9 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* if (shadow_frame.GetVReg(inst->VRegA_22t()) <= shadow_frame.GetVReg(inst->VRegB_22t())) { int16_t offset = inst->VRegC_22t(); if (IsBackwardBranch(offset)) { + if (UNLIKELY(self->TestAllFlags())) { + CheckSuspend(self); + } if (UNLIKELY(instrumentation->HasDexPcListeners())) { currentHandlersTable = instrumentationHandlersTable; } else { @@ -755,6 +796,9 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* if (shadow_frame.GetVReg(inst->VRegA_21t()) == 0) { int16_t offset = inst->VRegB_21t(); if (IsBackwardBranch(offset)) { + if (UNLIKELY(self->TestAllFlags())) { + CheckSuspend(self); + } if (UNLIKELY(instrumentation->HasDexPcListeners())) { currentHandlersTable = instrumentationHandlersTable; } else { @@ -772,6 +816,9 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* if (shadow_frame.GetVReg(inst->VRegA_21t()) != 0) { int16_t offset = inst->VRegB_21t(); if (IsBackwardBranch(offset)) { + if (UNLIKELY(self->TestAllFlags())) { + CheckSuspend(self); + } if (UNLIKELY(instrumentation->HasDexPcListeners())) { currentHandlersTable = instrumentationHandlersTable; } else { @@ -789,6 +836,9 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* if (shadow_frame.GetVReg(inst->VRegA_21t()) < 0) { int16_t offset = inst->VRegB_21t(); if (IsBackwardBranch(offset)) { + if (UNLIKELY(self->TestAllFlags())) { + CheckSuspend(self); + } if (UNLIKELY(instrumentation->HasDexPcListeners())) { currentHandlersTable = instrumentationHandlersTable; } else { @@ -806,6 +856,9 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* if (shadow_frame.GetVReg(inst->VRegA_21t()) >= 0) { int16_t offset = inst->VRegB_21t(); if (IsBackwardBranch(offset)) { + if (UNLIKELY(self->TestAllFlags())) { + CheckSuspend(self); + } if (UNLIKELY(instrumentation->HasDexPcListeners())) { currentHandlersTable = instrumentationHandlersTable; } else { @@ -823,6 +876,9 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* if (shadow_frame.GetVReg(inst->VRegA_21t()) > 0) { int16_t offset = inst->VRegB_21t(); if (IsBackwardBranch(offset)) { + if (UNLIKELY(self->TestAllFlags())) { + CheckSuspend(self); + } if (UNLIKELY(instrumentation->HasDexPcListeners())) { currentHandlersTable = instrumentationHandlersTable; } else { @@ -840,6 +896,9 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* if (shadow_frame.GetVReg(inst->VRegA_21t()) <= 0) { int16_t offset = inst->VRegB_21t(); if (IsBackwardBranch(offset)) { + if (UNLIKELY(self->TestAllFlags())) { + CheckSuspend(self); + } if (UNLIKELY(instrumentation->HasDexPcListeners())) { currentHandlersTable = instrumentationHandlersTable; } else { @@ -2307,6 +2366,9 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* exception_pending_label: { CHECK(self->IsExceptionPending()); + if (UNLIKELY(self->TestAllFlags())) { + CheckSuspend(self); + } uint32_t found_dex_pc = FindNextInstructionFollowingException(self, shadow_frame, dex_pc, this_object_ref, instrumentation); diff --git a/runtime/interpreter/interpreter_switch_impl.cc b/runtime/interpreter/interpreter_switch_impl.cc index ee2aaf69b42..2f0b5e91c49 100644 --- a/runtime/interpreter/interpreter_switch_impl.cc +++ b/runtime/interpreter/interpreter_switch_impl.cc @@ -22,6 +22,9 @@ namespace interpreter { #define HANDLE_PENDING_EXCEPTION() \ do { \ CHECK(self->IsExceptionPending()); \ + if (UNLIKELY(self->TestAllFlags())) { \ + CheckSuspend(self); \ + } \ uint32_t found_dex_pc = FindNextInstructionFollowingException(self, shadow_frame, \ inst->GetDexPc(insns), \ this_object_ref, \ @@ -73,9 +76,6 @@ static JValue ExecuteSwitchImpl(Thread* self, MethodHelper& mh, const DexFile::C while (true) { dex_pc = inst->GetDexPc(insns); shadow_frame.SetDexPC(dex_pc); - if (UNLIKELY(self->TestAllFlags())) { - CheckSuspend(self); - } if (UNLIKELY(instrumentation->HasDexPcListeners())) { instrumentation->DexPcMovedEvent(self, this_object_ref.get(), shadow_frame.GetMethod(), dex_pc); @@ -166,6 +166,9 @@ static JValue ExecuteSwitchImpl(Thread* self, MethodHelper& mh, const DexFile::C case Instruction::RETURN_VOID: { PREAMBLE(); JValue result; + if (UNLIKELY(self->TestAllFlags())) { + CheckSuspend(self); + } if (UNLIKELY(instrumentation->HasMethodExitListeners())) { instrumentation->MethodExitEvent(self, this_object_ref.get(), shadow_frame.GetMethod(), inst->GetDexPc(insns), @@ -177,6 +180,9 @@ static JValue ExecuteSwitchImpl(Thread* self, MethodHelper& mh, const DexFile::C PREAMBLE(); ANDROID_MEMBAR_STORE(); JValue result; + if (UNLIKELY(self->TestAllFlags())) { + CheckSuspend(self); + } if (UNLIKELY(instrumentation->HasMethodExitListeners())) { instrumentation->MethodExitEvent(self, this_object_ref.get(), shadow_frame.GetMethod(), inst->GetDexPc(insns), @@ -189,6 +195,9 @@ static JValue ExecuteSwitchImpl(Thread* self, MethodHelper& mh, const DexFile::C JValue result; result.SetJ(0); result.SetI(shadow_frame.GetVReg(inst->VRegA_11x())); + if (UNLIKELY(self->TestAllFlags())) { + CheckSuspend(self); + } if (UNLIKELY(instrumentation->HasMethodExitListeners())) { instrumentation->MethodExitEvent(self, this_object_ref.get(), shadow_frame.GetMethod(), inst->GetDexPc(insns), @@ -200,6 +209,9 @@ static JValue ExecuteSwitchImpl(Thread* self, MethodHelper& mh, const DexFile::C PREAMBLE(); JValue result; result.SetJ(shadow_frame.GetVRegLong(inst->VRegA_11x())); + if (UNLIKELY(self->TestAllFlags())) { + CheckSuspend(self); + } if (UNLIKELY(instrumentation->HasMethodExitListeners())) { instrumentation->MethodExitEvent(self, this_object_ref.get(), shadow_frame.GetMethod(), inst->GetDexPc(insns), @@ -212,6 +224,9 @@ static JValue ExecuteSwitchImpl(Thread* self, MethodHelper& mh, const DexFile::C JValue result; result.SetJ(0); result.SetL(shadow_frame.GetVRegReference(inst->VRegA_11x())); + if (UNLIKELY(self->TestAllFlags())) { + CheckSuspend(self); + } if (UNLIKELY(instrumentation->HasMethodExitListeners())) { instrumentation->MethodExitEvent(self, this_object_ref.get(), shadow_frame.GetMethod(), inst->GetDexPc(insns), @@ -461,28 +476,56 @@ static JValue ExecuteSwitchImpl(Thread* self, MethodHelper& mh, const DexFile::C } case Instruction::GOTO: { PREAMBLE(); - inst = inst->RelativeAt(inst->VRegA_10t()); + int8_t offset = inst->VRegA_10t(); + if (IsBackwardBranch(offset)) { + if (UNLIKELY(self->TestAllFlags())) { + CheckSuspend(self); + } + } + inst = inst->RelativeAt(offset); break; } case Instruction::GOTO_16: { PREAMBLE(); - inst = inst->RelativeAt(inst->VRegA_20t()); + int16_t offset = inst->VRegA_20t(); + if (IsBackwardBranch(offset)) { + if (UNLIKELY(self->TestAllFlags())) { + CheckSuspend(self); + } + } + inst = inst->RelativeAt(offset); break; } case Instruction::GOTO_32: { PREAMBLE(); - inst = inst->RelativeAt(inst->VRegA_30t()); + int32_t offset = inst->VRegA_30t(); + if (IsBackwardBranch(offset)) { + if (UNLIKELY(self->TestAllFlags())) { + CheckSuspend(self); + } + } + inst = inst->RelativeAt(offset); break; } case Instruction::PACKED_SWITCH: { PREAMBLE(); int32_t offset = DoPackedSwitch(inst, shadow_frame); + if (IsBackwardBranch(offset)) { + if (UNLIKELY(self->TestAllFlags())) { + CheckSuspend(self); + } + } inst = inst->RelativeAt(offset); break; } case Instruction::SPARSE_SWITCH: { PREAMBLE(); int32_t offset = DoSparseSwitch(inst, shadow_frame); + if (IsBackwardBranch(offset)) { + if (UNLIKELY(self->TestAllFlags())) { + CheckSuspend(self); + } + } inst = inst->RelativeAt(offset); break; } @@ -570,7 +613,13 @@ static JValue ExecuteSwitchImpl(Thread* self, MethodHelper& mh, const DexFile::C case Instruction::IF_EQ: { PREAMBLE(); if (shadow_frame.GetVReg(inst->VRegA_22t()) == shadow_frame.GetVReg(inst->VRegB_22t())) { - inst = inst->RelativeAt(inst->VRegC_22t()); + int16_t offset = inst->VRegC_22t(); + if (IsBackwardBranch(offset)) { + if (UNLIKELY(self->TestAllFlags())) { + CheckSuspend(self); + } + } + inst = inst->RelativeAt(offset); } else { inst = inst->Next_2xx(); } @@ -579,7 +628,13 @@ static JValue ExecuteSwitchImpl(Thread* self, MethodHelper& mh, const DexFile::C case Instruction::IF_NE: { PREAMBLE(); if (shadow_frame.GetVReg(inst->VRegA_22t()) != shadow_frame.GetVReg(inst->VRegB_22t())) { - inst = inst->RelativeAt(inst->VRegC_22t()); + int16_t offset = inst->VRegC_22t(); + if (IsBackwardBranch(offset)) { + if (UNLIKELY(self->TestAllFlags())) { + CheckSuspend(self); + } + } + inst = inst->RelativeAt(offset); } else { inst = inst->Next_2xx(); } @@ -588,7 +643,13 @@ static JValue ExecuteSwitchImpl(Thread* self, MethodHelper& mh, const DexFile::C case Instruction::IF_LT: { PREAMBLE(); if (shadow_frame.GetVReg(inst->VRegA_22t()) < shadow_frame.GetVReg(inst->VRegB_22t())) { - inst = inst->RelativeAt(inst->VRegC_22t()); + int16_t offset = inst->VRegC_22t(); + if (IsBackwardBranch(offset)) { + if (UNLIKELY(self->TestAllFlags())) { + CheckSuspend(self); + } + } + inst = inst->RelativeAt(offset); } else { inst = inst->Next_2xx(); } @@ -597,7 +658,13 @@ static JValue ExecuteSwitchImpl(Thread* self, MethodHelper& mh, const DexFile::C case Instruction::IF_GE: { PREAMBLE(); if (shadow_frame.GetVReg(inst->VRegA_22t()) >= shadow_frame.GetVReg(inst->VRegB_22t())) { - inst = inst->RelativeAt(inst->VRegC_22t()); + int16_t offset = inst->VRegC_22t(); + if (IsBackwardBranch(offset)) { + if (UNLIKELY(self->TestAllFlags())) { + CheckSuspend(self); + } + } + inst = inst->RelativeAt(offset); } else { inst = inst->Next_2xx(); } @@ -606,7 +673,13 @@ static JValue ExecuteSwitchImpl(Thread* self, MethodHelper& mh, const DexFile::C case Instruction::IF_GT: { PREAMBLE(); if (shadow_frame.GetVReg(inst->VRegA_22t()) > shadow_frame.GetVReg(inst->VRegB_22t())) { - inst = inst->RelativeAt(inst->VRegC_22t()); + int16_t offset = inst->VRegC_22t(); + if (IsBackwardBranch(offset)) { + if (UNLIKELY(self->TestAllFlags())) { + CheckSuspend(self); + } + } + inst = inst->RelativeAt(offset); } else { inst = inst->Next_2xx(); } @@ -615,7 +688,13 @@ static JValue ExecuteSwitchImpl(Thread* self, MethodHelper& mh, const DexFile::C case Instruction::IF_LE: { PREAMBLE(); if (shadow_frame.GetVReg(inst->VRegA_22t()) <= shadow_frame.GetVReg(inst->VRegB_22t())) { - inst = inst->RelativeAt(inst->VRegC_22t()); + int16_t offset = inst->VRegC_22t(); + if (IsBackwardBranch(offset)) { + if (UNLIKELY(self->TestAllFlags())) { + CheckSuspend(self); + } + } + inst = inst->RelativeAt(offset); } else { inst = inst->Next_2xx(); } @@ -624,7 +703,13 @@ static JValue ExecuteSwitchImpl(Thread* self, MethodHelper& mh, const DexFile::C case Instruction::IF_EQZ: { PREAMBLE(); if (shadow_frame.GetVReg(inst->VRegA_21t()) == 0) { - inst = inst->RelativeAt(inst->VRegB_21t()); + int16_t offset = inst->VRegB_21t(); + if (IsBackwardBranch(offset)) { + if (UNLIKELY(self->TestAllFlags())) { + CheckSuspend(self); + } + } + inst = inst->RelativeAt(offset); } else { inst = inst->Next_2xx(); } @@ -633,7 +718,13 @@ static JValue ExecuteSwitchImpl(Thread* self, MethodHelper& mh, const DexFile::C case Instruction::IF_NEZ: { PREAMBLE(); if (shadow_frame.GetVReg(inst->VRegA_21t()) != 0) { - inst = inst->RelativeAt(inst->VRegB_21t()); + int16_t offset = inst->VRegB_21t(); + if (IsBackwardBranch(offset)) { + if (UNLIKELY(self->TestAllFlags())) { + CheckSuspend(self); + } + } + inst = inst->RelativeAt(offset); } else { inst = inst->Next_2xx(); } @@ -642,7 +733,13 @@ static JValue ExecuteSwitchImpl(Thread* self, MethodHelper& mh, const DexFile::C case Instruction::IF_LTZ: { PREAMBLE(); if (shadow_frame.GetVReg(inst->VRegA_21t()) < 0) { - inst = inst->RelativeAt(inst->VRegB_21t()); + int16_t offset = inst->VRegB_21t(); + if (IsBackwardBranch(offset)) { + if (UNLIKELY(self->TestAllFlags())) { + CheckSuspend(self); + } + } + inst = inst->RelativeAt(offset); } else { inst = inst->Next_2xx(); } @@ -651,7 +748,13 @@ static JValue ExecuteSwitchImpl(Thread* self, MethodHelper& mh, const DexFile::C case Instruction::IF_GEZ: { PREAMBLE(); if (shadow_frame.GetVReg(inst->VRegA_21t()) >= 0) { - inst = inst->RelativeAt(inst->VRegB_21t()); + int16_t offset = inst->VRegB_21t(); + if (IsBackwardBranch(offset)) { + if (UNLIKELY(self->TestAllFlags())) { + CheckSuspend(self); + } + } + inst = inst->RelativeAt(offset); } else { inst = inst->Next_2xx(); } @@ -660,7 +763,13 @@ static JValue ExecuteSwitchImpl(Thread* self, MethodHelper& mh, const DexFile::C case Instruction::IF_GTZ: { PREAMBLE(); if (shadow_frame.GetVReg(inst->VRegA_21t()) > 0) { - inst = inst->RelativeAt(inst->VRegB_21t()); + int16_t offset = inst->VRegB_21t(); + if (IsBackwardBranch(offset)) { + if (UNLIKELY(self->TestAllFlags())) { + CheckSuspend(self); + } + } + inst = inst->RelativeAt(offset); } else { inst = inst->Next_2xx(); } @@ -669,7 +778,13 @@ static JValue ExecuteSwitchImpl(Thread* self, MethodHelper& mh, const DexFile::C case Instruction::IF_LEZ: { PREAMBLE(); if (shadow_frame.GetVReg(inst->VRegA_21t()) <= 0) { - inst = inst->RelativeAt(inst->VRegB_21t()); + int16_t offset = inst->VRegB_21t(); + if (IsBackwardBranch(offset)) { + if (UNLIKELY(self->TestAllFlags())) { + CheckSuspend(self); + } + } + inst = inst->RelativeAt(offset); } else { inst = inst->Next_2xx(); } From 043036f67fa37b3cfa3b05c41e5a824058730378 Mon Sep 17 00:00:00 2001 From: Sebastien Hertz Date: Mon, 9 Sep 2013 18:26:48 +0200 Subject: [PATCH 0021/2402] Add missing memory barrier. When DEX-to-DEX compiler is not run onto a method (because it's not preverified at the time of compilation), we do not replace RETURN where a barrier is needed into RETURN_VOID_BARRIER. This CL fixes this by placing a barrier on RETURN instruction only when the checks are enabled (non-preverified method). Change-Id: I4eb4cf79bb4a74684579c578318e27f62f4d9e8a --- runtime/interpreter/interpreter_goto_table_impl.cc | 6 ++++++ runtime/interpreter/interpreter_switch_impl.cc | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/runtime/interpreter/interpreter_goto_table_impl.cc b/runtime/interpreter/interpreter_goto_table_impl.cc index 67517ea0f67..10c50f3cce9 100644 --- a/runtime/interpreter/interpreter_goto_table_impl.cc +++ b/runtime/interpreter/interpreter_goto_table_impl.cc @@ -197,6 +197,12 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* HANDLE_INSTRUCTION_START(RETURN_VOID) { JValue result; + if (do_access_check) { + // If access checks are required then the dex-to-dex compiler and analysis of + // whether the class has final fields hasn't been performed. Conservatively + // perform the memory barrier now. + ANDROID_MEMBAR_STORE(); + } if (UNLIKELY(self->TestAllFlags())) { CheckSuspend(self); } diff --git a/runtime/interpreter/interpreter_switch_impl.cc b/runtime/interpreter/interpreter_switch_impl.cc index 2f0b5e91c49..5253e9dbbf4 100644 --- a/runtime/interpreter/interpreter_switch_impl.cc +++ b/runtime/interpreter/interpreter_switch_impl.cc @@ -166,6 +166,12 @@ static JValue ExecuteSwitchImpl(Thread* self, MethodHelper& mh, const DexFile::C case Instruction::RETURN_VOID: { PREAMBLE(); JValue result; + if (do_access_check) { + // If access checks are required then the dex-to-dex compiler and analysis of + // whether the class has final fields hasn't been performed. Conservatively + // perform the memory barrier now. + ANDROID_MEMBAR_STORE(); + } if (UNLIKELY(self->TestAllFlags())) { CheckSuspend(self); } From 252254b130067cd7a5071865e793966871ae0246 Mon Sep 17 00:00:00 2001 From: buzbee Date: Sun, 8 Sep 2013 16:20:53 -0700 Subject: [PATCH 0022/2402] More Quick compile-time tuning: labels & branches This CL represents a roughly 3.5% performance improvement for the compile phase of dex2oat. Move of the gain comes from avoiding the generation of dex boundary LIR labels unless a debug listing is requested. The other significant change is moving from a basic block ending branch model of "always generate a fall-through branch, and then delete it if we can" to a "only generate a fall-through branch if we need it" model. The data motivating these changes follow. Note that two area of potentially attractive gain remain: restructing the assembler model and reworking the register handling utilities. These will be addressed in subsequent CLs. --- data follows The Quick compiler's assembler has shown up on profile reports a bit more than seems reasonable. We've tried a few quick fixes to apparently hot portions of the code, but without much gain. So, I've been looking at the assembly process at a somewhat higher level. There look to be several potentially good opportunities. First, an analysis of the makeup of the LIR graph showed a surprisingly high proportion of LIR pseudo ops. Using the boot classpath as a basis, we get: 32.8% of all LIR nodes are pseudo ops. 10.4% are LIR instructions which require pc-relative fixups. 11.8% are LIR instructions that have been nop'd by the various optimization passes. Looking only at the LIR pseudo ops, we get: kPseudoDalvikByteCodeBoundary 43.46% kPseudoNormalBlockLabel 21.14% kPseudoSafepointPC 20.20% kPseudoThrowTarget 6.94% kPseudoTarget 3.03% kPseudoSuspendTarget 1.95% kPseudoMethodExit 1.26% kPseudoMethodEntry 1.26% kPseudoExportedPC 0.37% kPseudoCaseLabel 0.30% kPseudoBarrier 0.07% kPseudoIntrinsicRetry 0.02% Total LIR count: 10167292 The standout here is the Dalvik opcode boundary marker. This is just a label inserted at the beginning of the codegen for each Dalvik bytecode. If we're also doing a verbose listing, this is also where we hang the pretty-print disassembly string. However, this label was also being used as a convenient way to find the target of switch case statements (and, I think at one point was used in the Mir->GBC conversion process). This CL moves the use of kPseudoDalvikByteCodeBoundary labels to only verbose listing runs, and replaces the codegen uses of the label with the kPseudoNormalBlockLabel attached to the basic block that contains the switch case target. Great savings here - 14.3% reduction in the number of LIR nodes needed. After this CL, our LIR pseudo proportions drop to 21.6% of all LIR. That's still a lot, but much better. Possible further improvements via combining normal labels with kPseudoSafepointPC labels where appropriate, and also perhaps reduce memory usage by using a short-hand form for labels rather than a full LIR node. Also, many of the basic block labels are no longer branch targets by the time we get to assembly - cheaper to delete, or just ingore? Here's the "after" LIR pseudo op breakdown: kPseudoNormalBlockLabel 37.39% kPseudoSafepointPC 35.72% kPseudoThrowTarget 12.28% kPseudoTarget 5.36% kPseudoSuspendTarget 3.45% kPseudoMethodEntry 2.24% kPseudoMethodExit 2.22% kPseudoExportedPC 0.65% kPseudoCaseLabel 0.53% kPseudoBarrier 0.12% kPseudoIntrinsicRetry 0.04% Total LIR count: 5748232 Not done in this CL, but it will be worth experimenting with actually deleting LIR nodes from the graph when they are optimized away, rather than just setting the NOP bit. Keeping them around is invaluable during debugging - but when not debugging it may pay off if the cost of node removal is less than the cost of traversing through dead nodes in subsequent passes. Next up (and partially in this CL - but mostly to be done in follow-on CLs) is the overall assembly process. Inherited from the trace JIT, the Quick compiler has a fairly simple-minded approach to instruction assembly. First, a pass is made over the LIR list to assign offsets to each instruction. Then, the assembly pass is made - which generates the actual machine instruction bit patterns and pushes the instruction data into the code_buffer. However, the code generator takes the "always optimistic" approach to instruction selection and emits the shortest instruction. If, during assembly, we find that a branch or load doesn't reach, that short-form instruction is replaces with a longer sequence. Of course, this invalidates the previously-computed offset calculations. Assembly thus is an iterative process: compute offsets and then assemble until we survive an assembly pass without invalidation. This seems like a likely candidate for improvement. First, I analyzed the number of retries required, and the reason for invalidation over the boot classpath load. The results: more than half of methods don't require a retry, and very few require more than 1 extra pass: 5 or more: 6 of 96334 4 or more: 22 of 96334 3 or more: 140 of 96334 2 or more: 1794 of 96334 - 2% 1 or more: 40911 of 96334 - 40% 0 retries: 55423 of 96334 - 58% The interesting group here is the one that requires 1 retry. Looking at the reason, we see three typical reasons: 1. A cbnz/cbz doesn't reach (only 7 bits of offset) 2. A 16-bit Thumb1 unconditional branch doesn't reach. 3. An unconditional branch which branches to the next instruction is encountered, and deleted. The first 2 cases are the cost of the optimistic strategy - nothing much to change there. However, the interesting case is #3 - dead branch elimination. A further analysis of the single retry group showed that 42% of the methods (16305) that required a single retry did so *only* because of dead branch elimination. The big question here is why so many dead branches survive to the assembly stage. We have a dead branch elimination pass which is supposed to catch these - perhaps it's not working correctly, should be moved later in the optimization process, or perhaps run multiple times. Other things to consider: o Combine the offset generation pass with the assembly pass. Skip pc-relative fixup assembly (other than assigning offset), but push LIR* for them into work list. Following the main pass, zip through the work list and assemble the pc-relative instructions (now that we know the offsets). This would significantly cut back on traversal costs. o Store the assembled bits into both the code buffer and the LIR. In the event we have to retry, only the pc-relative instructions would need to be assembled, and we'd finish with a pass over the LIR just to dumb the bits into the code buffer. Change-Id: I50029d216fa14f273f02b6f1c8b6a0dde5a7d6a6 --- compiler/dex/growable_array.h | 5 --- compiler/dex/quick/arm/assemble_arm.cc | 4 +- compiler/dex/quick/arm/call_arm.cc | 7 +-- compiler/dex/quick/arm/int_arm.cc | 16 ++++--- compiler/dex/quick/codegen_util.cc | 54 +++++++++++++---------- compiler/dex/quick/gen_common.cc | 3 -- compiler/dex/quick/local_optimizations.cc | 47 +++----------------- compiler/dex/quick/mips/assemble_mips.cc | 4 +- compiler/dex/quick/mir_to_lir.cc | 35 ++++++++------- compiler/dex/quick/mir_to_lir.h | 5 +-- compiler/dex/quick/x86/assemble_x86.cc | 2 +- 11 files changed, 75 insertions(+), 107 deletions(-) diff --git a/compiler/dex/growable_array.h b/compiler/dex/growable_array.h index 9053285af09..8e2abfbaf18 100644 --- a/compiler/dex/growable_array.h +++ b/compiler/dex/growable_array.h @@ -150,11 +150,6 @@ class GrowableArray { size_t Size() const { return num_used_; } - void SetSize(size_t new_size) { - Resize(new_size); - num_used_ = new_size; - } - T* GetRawStorage() const { return elem_list_; } static void* operator new(size_t size, ArenaAllocator* arena) { diff --git a/compiler/dex/quick/arm/assemble_arm.cc b/compiler/dex/quick/arm/assemble_arm.cc index 0649c9f319d..8ba56dfa0ea 100644 --- a/compiler/dex/quick/arm/assemble_arm.cc +++ b/compiler/dex/quick/arm/assemble_arm.cc @@ -1170,7 +1170,7 @@ AssemblerStatus ArmMir2Lir::AssembleInstructions(uintptr_t start_addr) { lir->operands[0] = delta >> 1; if (!(cu_->disable_opt & (1 << kSafeOptimizations)) && lir->operands[0] == 0) { // Useless branch - lir->flags.is_nop = true; + NopLIR(lir); res = kRetryAll; } } else if (lir->opcode == kThumbBUncond) { @@ -1188,7 +1188,7 @@ AssemblerStatus ArmMir2Lir::AssembleInstructions(uintptr_t start_addr) { lir->operands[0] = delta >> 1; if (!(cu_->disable_opt & (1 << kSafeOptimizations)) && lir->operands[0] == -1) { // Useless branch - lir->flags.is_nop = true; + NopLIR(lir); res = kRetryAll; } } diff --git a/compiler/dex/quick/arm/call_arm.cc b/compiler/dex/quick/arm/call_arm.cc index e0e198a50db..bba2ec5c4e7 100644 --- a/compiler/dex/quick/arm/call_arm.cc +++ b/compiler/dex/quick/arm/call_arm.cc @@ -120,9 +120,10 @@ MIR* ArmMir2Lir::GetNextMir(BasicBlock** p_bb, MIR* mir) { // TODO: move to common code void ArmMir2Lir::GenPrintLabel(MIR* mir) { /* Mark the beginning of a Dalvik instruction for line tracking */ - char* inst_str = cu_->verbose ? - mir_graph_->GetDalvikDisassembly(mir) : NULL; - MarkBoundary(mir->offset, inst_str); + if (cu_->verbose) { + char* inst_str = mir_graph_->GetDalvikDisassembly(mir); + MarkBoundary(mir->offset, inst_str); + } } MIR* ArmMir2Lir::SpecialIGet(BasicBlock** bb, MIR* mir, diff --git a/compiler/dex/quick/arm/int_arm.cc b/compiler/dex/quick/arm/int_arm.cc index f2ff58ee0a7..bd659e6c2bc 100644 --- a/compiler/dex/quick/arm/int_arm.cc +++ b/compiler/dex/quick/arm/int_arm.cc @@ -234,11 +234,17 @@ void ArmMir2Lir::GenSelect(BasicBlock* bb, MIR* mir) { rl_false = LoadValue(rl_false, kCoreReg); rl_result = EvalLoc(rl_dest, kCoreReg, true); OpRegImm(kOpCmp, rl_src.low_reg, 0); - OpIT(kCondEq, "E"); - LIR* l1 = OpRegCopy(rl_result.low_reg, rl_true.low_reg); - l1->flags.is_nop = false; // Make sure this instruction isn't optimized away - LIR* l2 = OpRegCopy(rl_result.low_reg, rl_false.low_reg); - l2->flags.is_nop = false; // Make sure this instruction isn't optimized away + if (rl_result.low_reg == rl_true.low_reg) { // Is the "true" case already in place? + OpIT(kCondNe, ""); + OpRegCopy(rl_result.low_reg, rl_false.low_reg); + } else if (rl_result.low_reg == rl_false.low_reg) { // False case in place? + OpIT(kCondEq, ""); + OpRegCopy(rl_result.low_reg, rl_true.low_reg); + } else { // Normal - select between the two. + OpIT(kCondEq, "E"); + OpRegCopy(rl_result.low_reg, rl_true.low_reg); + OpRegCopy(rl_result.low_reg, rl_false.low_reg); + } GenBarrier(); // Add a scheduling barrier to keep the IT shadow intact } StoreValue(rl_dest, rl_result); diff --git a/compiler/dex/quick/codegen_util.cc b/compiler/dex/quick/codegen_util.cc index e9db6a68d05..3aa2f647c20 100644 --- a/compiler/dex/quick/codegen_util.cc +++ b/compiler/dex/quick/codegen_util.cc @@ -55,9 +55,32 @@ bool Mir2Lir::FastInstance(uint32_t field_idx, bool is_put, int* field_offset, b field_idx, mir_graph_->GetCurrentDexCompilationUnit(), is_put, field_offset, is_volatile); } +/* Remove a LIR from the list. */ +void Mir2Lir::UnlinkLIR(LIR* lir) { + if (UNLIKELY(lir == first_lir_insn_)) { + first_lir_insn_ = lir->next; + if (lir->next != NULL) { + lir->next->prev = NULL; + } else { + DCHECK(lir->next == NULL); + DCHECK(lir == last_lir_insn_); + last_lir_insn_ = NULL; + } + } else if (lir == last_lir_insn_) { + last_lir_insn_ = lir->prev; + lir->prev->next = NULL; + } else if ((lir->prev != NULL) && (lir->next != NULL)) { + lir->prev->next = lir->next; + lir->next->prev = lir->prev; + } +} + /* Convert an instruction to a NOP */ void Mir2Lir::NopLIR(LIR* lir) { lir->flags.is_nop = true; + if (!cu_->verbose) { + UnlinkLIR(lir); + } } void Mir2Lir::SetMemRefType(LIR* lir, bool is_load, int mem_type) { @@ -782,20 +805,16 @@ void Mir2Lir::AssembleLIR() { /* * Insert a kPseudoCaseLabel at the beginning of the Dalvik * offset vaddr. This label will be used to fix up the case - * branch table during the assembly phase. Be sure to set - * all resource flags on this to prevent code motion across - * target boundaries. KeyVal is just there for debugging. + * branch table during the assembly phase. All resource flags + * are set to prevent code motion. KeyVal is just there for debugging. */ LIR* Mir2Lir::InsertCaseLabel(int vaddr, int keyVal) { - SafeMap::iterator it; - LIR* boundary_lir = boundary_map_.Get(vaddr); - if (boundary_lir == NULL) { - LOG(FATAL) << "Error: didn't find vaddr 0x" << std::hex << vaddr; - } + LIR* boundary_lir = &block_label_list_[mir_graph_->FindBlock(vaddr)->id]; LIR* new_label = static_cast(arena_->Alloc(sizeof(LIR), ArenaAllocator::kAllocLIR)); new_label->dalvik_offset = vaddr; new_label->opcode = kPseudoCaseLabel; new_label->operands[0] = keyVal; + new_label->def_mask = ENCODE_ALL; InsertLIRAfter(boundary_lir, new_label); return new_label; } @@ -880,18 +899,9 @@ void Mir2Lir::DumpPackedSwitchTable(const uint16_t* table) { } } -/* - * Set up special LIR to mark a Dalvik byte-code instruction start and - * record it in the boundary_map. NOTE: in cases such as kMirOpCheck in - * which we split a single Dalvik instruction, only the first MIR op - * associated with a Dalvik PC should be entered into the map. - */ -LIR* Mir2Lir::MarkBoundary(int offset, const char* inst_str) { - LIR* res = NewLIR1(kPseudoDalvikByteCodeBoundary, reinterpret_cast(inst_str)); - if (boundary_map_.Get(offset) == NULL) { - boundary_map_.Put(offset, res); - } - return res; +/* Set up special LIR to mark a Dalvik byte-code instruction start for pretty printing */ +void Mir2Lir::MarkBoundary(int offset, const char* inst_str) { + NewLIR1(kPseudoDalvikByteCodeBoundary, reinterpret_cast(inst_str)); } bool Mir2Lir::EvaluateBranch(Instruction::Code opcode, int32_t src1, int32_t src2) { @@ -946,7 +956,6 @@ Mir2Lir::Mir2Lir(CompilationUnit* cu, MIRGraph* mir_graph, ArenaAllocator* arena throw_launchpads_(arena, 2048, kGrowableArrayThrowLaunchPads), suspend_launchpads_(arena, 4, kGrowableArraySuspendLaunchPads), intrinsic_launchpads_(arena, 2048, kGrowableArrayMisc), - boundary_map_(arena, 0, kGrowableArrayMisc), data_offset_(0), total_size_(0), block_label_list_(NULL), @@ -963,8 +972,6 @@ Mir2Lir::Mir2Lir(CompilationUnit* cu, MIRGraph* mir_graph, ArenaAllocator* arena promotion_map_ = static_cast (arena_->Alloc((cu_->num_dalvik_registers + cu_->num_compiler_temps + 1) * sizeof(promotion_map_[0]), ArenaAllocator::kAllocRegAlloc)); - // Pre-fill with nulls. - boundary_map_.SetSize(cu->code_item->insns_size_in_code_units_); } void Mir2Lir::Materialize() { @@ -1091,5 +1098,4 @@ void Mir2Lir::InsertLIRAfter(LIR* current_lir, LIR* new_lir) { new_lir->next->prev = new_lir; } - } // namespace art diff --git a/compiler/dex/quick/gen_common.cc b/compiler/dex/quick/gen_common.cc index aa45d98cf65..4dd55d763a1 100644 --- a/compiler/dex/quick/gen_common.cc +++ b/compiler/dex/quick/gen_common.cc @@ -127,13 +127,11 @@ void Mir2Lir::GenCompareAndBranch(Instruction::Code opcode, RegLocation rl_src1, InexpensiveConstantInt(mir_graph_->ConstantValue(rl_src2))) { // OK - convert this to a compare immediate and branch OpCmpImmBranch(cond, rl_src1.low_reg, mir_graph_->ConstantValue(rl_src2), taken); - OpUnconditionalBranch(fall_through); return; } } rl_src2 = LoadValue(rl_src2, kCoreReg); OpCmpBranch(cond, rl_src1.low_reg, rl_src2.low_reg, taken); - OpUnconditionalBranch(fall_through); } void Mir2Lir::GenCompareZeroAndBranch(Instruction::Code opcode, RegLocation rl_src, LIR* taken, @@ -164,7 +162,6 @@ void Mir2Lir::GenCompareZeroAndBranch(Instruction::Code opcode, RegLocation rl_s LOG(FATAL) << "Unexpected opcode " << opcode; } OpCmpImmBranch(cond, rl_src.low_reg, 0, taken); - OpUnconditionalBranch(fall_through); } void Mir2Lir::GenIntToLong(RegLocation rl_dest, RegLocation rl_src) { diff --git a/compiler/dex/quick/local_optimizations.cc b/compiler/dex/quick/local_optimizations.cc index 41adb946a2c..cb7694de687 100644 --- a/compiler/dex/quick/local_optimizations.cc +++ b/compiler/dex/quick/local_optimizations.cc @@ -168,7 +168,7 @@ void Mir2Lir::ApplyLoadStoreElimination(LIR* head_lir, LIR* tail_lir) { if (check_lir->operands[0] != native_reg_id) { ConvertMemOpIntoMove(check_lir, check_lir->operands[0], native_reg_id); } - check_lir->flags.is_nop = true; + NopLIR(check_lir); } } else if (alias_condition == ENCODE_DALVIK_REG) { /* Must alias */ @@ -187,7 +187,7 @@ void Mir2Lir::ApplyLoadStoreElimination(LIR* head_lir, LIR* tail_lir) { native_reg_id) { ConvertMemOpIntoMove(check_lir, check_lir->operands[0], native_reg_id); } - check_lir->flags.is_nop = true; + NopLIR(check_lir); } else { /* * Destinaions are of different types - @@ -201,7 +201,7 @@ void Mir2Lir::ApplyLoadStoreElimination(LIR* head_lir, LIR* tail_lir) { stop_here = true; } else if (!is_this_lir_load && !is_check_lir_load) { /* WAW - nuke the earlier store */ - this_lir->flags.is_nop = true; + NopLIR(this_lir); stop_here = true; } /* Partial overlap */ @@ -256,7 +256,7 @@ void Mir2Lir::ApplyLoadStoreElimination(LIR* head_lir, LIR* tail_lir) { * top-down order. */ InsertLIRBefore(check_lir, new_store_lir); - this_lir->flags.is_nop = true; + NopLIR(this_lir); } break; } else if (!check_lir->flags.is_nop) { @@ -452,7 +452,7 @@ void Mir2Lir::ApplyLoadHoisting(LIR* head_lir, LIR* tail_lir) { * is never the first LIR on the list */ InsertLIRBefore(cur_lir, new_load_lir); - this_lir->flags.is_nop = true; + NopLIR(this_lir); } } } @@ -467,41 +467,4 @@ void Mir2Lir::ApplyLocalOptimizations(LIR* head_lir, LIR* tail_lir) { } } -/* - * Nop any unconditional branches that go to the next instruction. - * Note: new redundant branches may be inserted later, and we'll - * use a check in final instruction assembly to nop those out. - */ -void Mir2Lir::RemoveRedundantBranches() { - LIR* this_lir; - - for (this_lir = first_lir_insn_; this_lir != last_lir_insn_; this_lir = NEXT_LIR(this_lir)) { - /* Branch to the next instruction */ - if (IsUnconditionalBranch(this_lir)) { - LIR* next_lir = this_lir; - - while (true) { - next_lir = NEXT_LIR(next_lir); - - /* - * Is the branch target the next instruction? - */ - if (next_lir == this_lir->target) { - this_lir->flags.is_nop = true; - break; - } - - /* - * Found real useful stuff between the branch and the target. - * Need to explicitly check the last_lir_insn_ here because it - * might be the last real instruction. - */ - if (!is_pseudo_opcode(next_lir->opcode) || - (next_lir == last_lir_insn_)) - break; - } - } - } -} - } // namespace art diff --git a/compiler/dex/quick/mips/assemble_mips.cc b/compiler/dex/quick/mips/assemble_mips.cc index cd25232c216..dbd668b3307 100644 --- a/compiler/dex/quick/mips/assemble_mips.cc +++ b/compiler/dex/quick/mips/assemble_mips.cc @@ -503,7 +503,7 @@ void MipsMir2Lir::ConvertShortToLongBranch(LIR* lir) { if (!unconditional) { InsertLIRBefore(lir, hop_target); } - lir->flags.is_nop = true; + NopLIR(lir); } /* @@ -561,7 +561,7 @@ AssemblerStatus MipsMir2Lir::AssembleInstructions(uintptr_t start_addr) { RawLIR(lir->dalvik_offset, kMipsAddu, lir->operands[0], lir->operands[0], r_RA); InsertLIRBefore(lir, new_addu); - lir->flags.is_nop = true; + NopLIR(lir); res = kRetryAll; } } else if (lir->opcode == kMipsDeltaLo) { diff --git a/compiler/dex/quick/mir_to_lir.cc b/compiler/dex/quick/mir_to_lir.cc index 7c79f598533..e1ac1365f0c 100644 --- a/compiler/dex/quick/mir_to_lir.cc +++ b/compiler/dex/quick/mir_to_lir.cc @@ -735,16 +735,16 @@ bool Mir2Lir::MethodBlockCodeGen(BasicBlock* bb) { current_dalvik_offset_ = mir->offset; int opcode = mir->dalvikInsn.opcode; - LIR* boundary_lir; // Mark the beginning of a Dalvik instruction for line tracking. - char* inst_str = cu_->verbose ? - mir_graph_->GetDalvikDisassembly(mir) : NULL; - boundary_lir = MarkBoundary(mir->offset, inst_str); + if (cu_->verbose) { + char* inst_str = mir_graph_->GetDalvikDisassembly(mir); + MarkBoundary(mir->offset, inst_str); + } // Remember the first LIR for this block. if (head_lir == NULL) { - head_lir = boundary_lir; - // Set the first boundary_lir as a scheduling barrier. + head_lir = &block_label_list_[bb->id]; + // Set the first label as a scheduling barrier. head_lir->def_mask = ENCODE_ALL; } @@ -770,11 +770,6 @@ bool Mir2Lir::MethodBlockCodeGen(BasicBlock* bb) { if (head_lir) { // Eliminate redundant loads/stores and delay stores into later slots. ApplyLocalOptimizations(head_lir, last_lir_insn_); - - // Generate an unconditional branch to the fallthrough block. - if (bb->fall_through) { - OpUnconditionalBranch(&block_label_list_[bb->fall_through->id]); - } } return false; } @@ -815,8 +810,18 @@ void Mir2Lir::MethodMIR2LIR() { ArenaAllocator::kAllocLIR)); PreOrderDfsIterator iter(mir_graph_); - for (BasicBlock* bb = iter.Next(); bb != NULL; bb = iter.Next()) { - MethodBlockCodeGen(bb); + BasicBlock* curr_bb = iter.Next(); + BasicBlock* next_bb = iter.Next(); + while (curr_bb != NULL) { + MethodBlockCodeGen(curr_bb); + // If the fall_through block is no longer laid out consecutively, drop in a branch. + if ((curr_bb->fall_through != NULL) && (curr_bb->fall_through != next_bb)) { + OpUnconditionalBranch(&block_label_list_[curr_bb->fall_through->id]); + } + curr_bb = next_bb; + do { + next_bb = iter.Next(); + } while ((next_bb != NULL) && (next_bb->block_type == kDead)); } HandleSuspendLaunchPads(); @@ -824,10 +829,6 @@ void Mir2Lir::MethodMIR2LIR() { HandleThrowLaunchPads(); HandleIntrinsicLaunchPads(); - - if (!(cu_->disable_opt & (1 << kSafeOptimizations))) { - RemoveRedundantBranches(); - } } } // namespace art diff --git a/compiler/dex/quick/mir_to_lir.h b/compiler/dex/quick/mir_to_lir.h index 981bbe6e7d5..5b4813416b3 100644 --- a/compiler/dex/quick/mir_to_lir.h +++ b/compiler/dex/quick/mir_to_lir.h @@ -274,8 +274,9 @@ class Mir2Lir : public Backend { void ProcessSwitchTables(); void DumpSparseSwitchTable(const uint16_t* table); void DumpPackedSwitchTable(const uint16_t* table); - LIR* MarkBoundary(int offset, const char* inst_str); + void MarkBoundary(int offset, const char* inst_str); void NopLIR(LIR* lir); + void UnlinkLIR(LIR* lir); bool EvaluateBranch(Instruction::Code opcode, int src1, int src2); bool IsInexpensiveConstant(RegLocation rl_src); ConditionCode FlipComparisonOrder(ConditionCode before); @@ -302,7 +303,6 @@ class Mir2Lir : public Backend { void ApplyLoadStoreElimination(LIR* head_lir, LIR* tail_lir); void ApplyLoadHoisting(LIR* head_lir, LIR* tail_lir); void ApplyLocalOptimizations(LIR* head_lir, LIR* tail_lir); - void RemoveRedundantBranches(); // Shared by all targets - implemented in ralloc_util.cc int GetSRegHi(int lowSreg); @@ -727,7 +727,6 @@ class Mir2Lir : public Backend { GrowableArray throw_launchpads_; GrowableArray suspend_launchpads_; GrowableArray intrinsic_launchpads_; - GrowableArray boundary_map_; /* * Holds mapping from native PC to dex PC for safepoints where we may deoptimize. * Native PC is on the return address of the safepointed operation. Dex PC is for diff --git a/compiler/dex/quick/x86/assemble_x86.cc b/compiler/dex/quick/x86/assemble_x86.cc index e8834320a9b..3e768837ff1 100644 --- a/compiler/dex/quick/x86/assemble_x86.cc +++ b/compiler/dex/quick/x86/assemble_x86.cc @@ -1237,7 +1237,7 @@ AssemblerStatus X86Mir2Lir::AssembleInstructions(uintptr_t start_addr) { delta = target - pc; if (!(cu_->disable_opt & (1 << kSafeOptimizations)) && delta == 0) { // Useless branch - lir->flags.is_nop = true; + NopLIR(lir); if (kVerbosePcFixup) { LOG(INFO) << "Retry for useless branch at " << lir->offset; } From 7b6da36ac600178f0cdc60a1c36843c3a4fd300e Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Wed, 11 Sep 2013 09:29:40 -0700 Subject: [PATCH 0023/2402] Don't deopt for tracing due to stability. Change-Id: I49525f950c669a6dab4f7038b10fccb663087acf --- runtime/instrumentation.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/instrumentation.cc b/runtime/instrumentation.cc index fe38d32d1ce..3787e1c7594 100644 --- a/runtime/instrumentation.cc +++ b/runtime/instrumentation.cc @@ -44,7 +44,7 @@ namespace instrumentation { // Do we want to deoptimize for method entry and exit listeners or just try to intercept // invocations? Deoptimization forces all code to run in the interpreter and considerably hurts the // application's performance. -static constexpr bool kDeoptimizeForAccurateMethodEntryExitListeners = true; +static constexpr bool kDeoptimizeForAccurateMethodEntryExitListeners = false; static bool InstallStubsClassVisitor(mirror::Class* klass, void* arg) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { From 049e7a35366937b1b7b28afcaccc88c0c69ea3ec Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Wed, 11 Sep 2013 10:35:34 -0700 Subject: [PATCH 0024/2402] Tweak an unusual monitor log message. Change-Id: I0a62b0d04ce4ee4c3b6576737d58beacef09f520 --- runtime/monitor.cc | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/runtime/monitor.cc b/runtime/monitor.cc index 66c51e6eb66..92e6541d470 100644 --- a/runtime/monitor.cc +++ b/runtime/monitor.cc @@ -260,8 +260,7 @@ static void ThrowIllegalMonitorStateExceptionF(const char* fmt, ...) if (!Runtime::Current()->IsStarted()) { std::ostringstream ss; self->Dump(ss); - std::string str(ss.str()); - LOG(ERROR) << "IllegalMonitorStateException: " << str; + LOG(ERROR) << self->GetException(NULL)->Dump() << "\n" << ss.str(); } va_end(args); } From bd663de599b16229085759366c56e2ed5a1dc7ec Mon Sep 17 00:00:00 2001 From: buzbee Date: Tue, 10 Sep 2013 15:41:31 -0700 Subject: [PATCH 0025/2402] Compile-time tuning: register/bb utilities This CL yeilds about a 4% improvement in the compilation phase of dex2oat (single-threaded; multi-threaded compilation is more difficult to accurately measure). The register utilities could stand to be completely rewritten, but this gets most of the easy benefit. Next up: the assembly phase. Change-Id: Ife5a474e9b1a6d9e501e888dda6749d34eb77e96 --- compiler/dex/growable_array.h | 10 ++++++ compiler/dex/mir_graph.cc | 17 ++++++---- compiler/dex/mir_graph.h | 2 +- compiler/dex/quick/arm/codegen_arm.h | 1 - compiler/dex/quick/arm/target_arm.cc | 5 --- compiler/dex/quick/codegen_util.cc | 2 ++ compiler/dex/quick/mips/codegen_mips.h | 1 - compiler/dex/quick/mips/target_mips.cc | 5 --- compiler/dex/quick/mir_to_lir-inl.h | 5 +++ compiler/dex/quick/mir_to_lir.h | 4 ++- compiler/dex/quick/ralloc_util.cc | 47 ++++++++++++-------------- compiler/dex/quick/x86/codegen_x86.h | 1 - compiler/dex/quick/x86/target_x86.cc | 5 --- 13 files changed, 52 insertions(+), 53 deletions(-) diff --git a/compiler/dex/growable_array.h b/compiler/dex/growable_array.h index 8e2abfbaf18..639120a2ba2 100644 --- a/compiler/dex/growable_array.h +++ b/compiler/dex/growable_array.h @@ -131,6 +131,11 @@ class GrowableArray { elem_list_[index]++; } + /* + * Remove an existing element from list. If there are more than one copy + * of the element, only the first one encountered will be deleted. + */ + // TODO: consider renaming this. void Delete(T element) { bool found = false; for (size_t i = 0; i < num_used_ - 1; i++) { @@ -150,6 +155,11 @@ class GrowableArray { size_t Size() const { return num_used_; } + void SetSize(size_t new_size) { + Resize(new_size); + num_used_ = new_size; + } + T* GetRawStorage() const { return elem_list_; } static void* operator new(size_t size, ArenaAllocator* arena) { diff --git a/compiler/dex/mir_graph.cc b/compiler/dex/mir_graph.cc index 81702e3842e..c72283e689b 100644 --- a/compiler/dex/mir_graph.cc +++ b/compiler/dex/mir_graph.cc @@ -99,6 +99,7 @@ MIRGraph::MIRGraph(CompilationUnit* cu, ArenaAllocator* arena) cur_block_(NULL), num_blocks_(0), current_code_item_(NULL), + block_map_(arena, 0, kGrowableArrayMisc), current_method_(kInvalidEntry), current_offset_(kInvalidEntry), def_count_(0), @@ -210,18 +211,18 @@ BasicBlock* MIRGraph::FindBlock(unsigned int code_offset, bool split, bool creat BasicBlock** immed_pred_block_p) { BasicBlock* bb; unsigned int i; - SafeMap::iterator it; - it = block_map_.find(code_offset); - if (it != block_map_.end()) { - return it->second; - } else if (!create) { + if (code_offset >= cu_->code_item->insns_size_in_code_units_) { return NULL; } + bb = block_map_.Get(code_offset); + if ((bb != NULL) || !create) { + return bb; + } if (split) { - for (i = 0; i < block_list_.Size(); i++) { - bb = block_list_.Get(i); + for (i = block_list_.Size(); i > 0; i--) { + bb = block_list_.Get(i - 1); if (bb->block_type != kDalvikByteCode) continue; /* Check if a branch jumps into the middle of an existing block */ if ((code_offset > bb->start_offset) && (bb->last_mir_insn != NULL) && @@ -518,6 +519,8 @@ void MIRGraph::InlineMethod(const DexFile::CodeItem* code_item, uint32_t access_ // TODO: need to rework expansion of block list & try_block_addr when inlining activated. block_list_.Resize(block_list_.Size() + current_code_item_->insns_size_in_code_units_); + block_map_.SetSize(block_map_.Size() + current_code_item_->insns_size_in_code_units_); + // TODO: replace with explicit resize routine. Using automatic extension side effect for now. try_block_addr_->SetBit(current_code_item_->insns_size_in_code_units_); try_block_addr_->ClearBit(current_code_item_->insns_size_in_code_units_); diff --git a/compiler/dex/mir_graph.h b/compiler/dex/mir_graph.h index 15e15c380f8..0244daec9dd 100644 --- a/compiler/dex/mir_graph.h +++ b/compiler/dex/mir_graph.h @@ -728,7 +728,7 @@ class MIRGraph { BasicBlock* cur_block_; int num_blocks_; const DexFile::CodeItem* current_code_item_; - SafeMap block_map_; // FindBlock lookup cache. + GrowableArray block_map_; // FindBlock lookup cache. std::vector m_units_; // List of methods included in this graph typedef std::pair MIRLocation; // Insert point, (m_unit_ index, offset) std::vector method_stack_; // Include stack diff --git a/compiler/dex/quick/arm/codegen_arm.h b/compiler/dex/quick/arm/codegen_arm.h index 291319f258a..1954fbac51d 100644 --- a/compiler/dex/quick/arm/codegen_arm.h +++ b/compiler/dex/quick/arm/codegen_arm.h @@ -51,7 +51,6 @@ class ArmMir2Lir : public Mir2Lir { int AllocTypedTempPair(bool fp_hint, int reg_class); int S2d(int low_reg, int high_reg); int TargetReg(SpecialTargetRegister reg); - RegisterInfo* GetRegInfo(int reg); RegLocation GetReturnAlt(); RegLocation GetReturnWideAlt(); RegLocation LocCReturn(); diff --git a/compiler/dex/quick/arm/target_arm.cc b/compiler/dex/quick/arm/target_arm.cc index 6cc3052da14..203a8cc55df 100644 --- a/compiler/dex/quick/arm/target_arm.cc +++ b/compiler/dex/quick/arm/target_arm.cc @@ -691,11 +691,6 @@ RegLocation ArmMir2Lir::GetReturnAlt() { return res; } -ArmMir2Lir::RegisterInfo* ArmMir2Lir::GetRegInfo(int reg) { - return ARM_FPREG(reg) ? ®_pool_->FPRegs[reg & ARM_FP_REG_MASK] - : ®_pool_->core_regs[reg]; -} - /* To be used when explicitly managing register use */ void ArmMir2Lir::LockCallTemps() { LockTemp(r0); diff --git a/compiler/dex/quick/codegen_util.cc b/compiler/dex/quick/codegen_util.cc index 3aa2f647c20..f13ab2db017 100644 --- a/compiler/dex/quick/codegen_util.cc +++ b/compiler/dex/quick/codegen_util.cc @@ -956,6 +956,8 @@ Mir2Lir::Mir2Lir(CompilationUnit* cu, MIRGraph* mir_graph, ArenaAllocator* arena throw_launchpads_(arena, 2048, kGrowableArrayThrowLaunchPads), suspend_launchpads_(arena, 4, kGrowableArraySuspendLaunchPads), intrinsic_launchpads_(arena, 2048, kGrowableArrayMisc), + tempreg_info_(arena, 20, kGrowableArrayMisc), + reginfo_map_(arena, 64, kGrowableArrayMisc), data_offset_(0), total_size_(0), block_label_list_(NULL), diff --git a/compiler/dex/quick/mips/codegen_mips.h b/compiler/dex/quick/mips/codegen_mips.h index b9cb7209623..8d0b347a34c 100644 --- a/compiler/dex/quick/mips/codegen_mips.h +++ b/compiler/dex/quick/mips/codegen_mips.h @@ -52,7 +52,6 @@ class MipsMir2Lir : public Mir2Lir { int AllocTypedTempPair(bool fp_hint, int reg_class); int S2d(int low_reg, int high_reg); int TargetReg(SpecialTargetRegister reg); - RegisterInfo* GetRegInfo(int reg); RegLocation GetReturnAlt(); RegLocation GetReturnWideAlt(); RegLocation LocCReturn(); diff --git a/compiler/dex/quick/mips/target_mips.cc b/compiler/dex/quick/mips/target_mips.cc index 4ee5b23eb90..8e768dcf182 100644 --- a/compiler/dex/quick/mips/target_mips.cc +++ b/compiler/dex/quick/mips/target_mips.cc @@ -399,11 +399,6 @@ RegLocation MipsMir2Lir::GetReturnAlt() { return res; } -MipsMir2Lir::RegisterInfo* MipsMir2Lir::GetRegInfo(int reg) { - return MIPS_FPREG(reg) ? ®_pool_->FPRegs[reg & MIPS_FP_REG_MASK] - : ®_pool_->core_regs[reg]; -} - /* To be used when explicitly managing register use */ void MipsMir2Lir::LockCallTemps() { LockTemp(rMIPS_ARG0); diff --git a/compiler/dex/quick/mir_to_lir-inl.h b/compiler/dex/quick/mir_to_lir-inl.h index f9ec19951ed..0ca8d8de112 100644 --- a/compiler/dex/quick/mir_to_lir-inl.h +++ b/compiler/dex/quick/mir_to_lir-inl.h @@ -201,6 +201,11 @@ inline void Mir2Lir::SetupResourceMasks(LIR* lir) { SetupTargetResourceMasks(lir); } +inline art::Mir2Lir::RegisterInfo* Mir2Lir::GetRegInfo(int reg) { + DCHECK(reginfo_map_.Get(reg) != NULL); + return reginfo_map_.Get(reg); +} + } // namespace art #endif // ART_COMPILER_DEX_QUICK_MIR_TO_LIR_INL_H_ diff --git a/compiler/dex/quick/mir_to_lir.h b/compiler/dex/quick/mir_to_lir.h index 5b4813416b3..fdbc1d02b3d 100644 --- a/compiler/dex/quick/mir_to_lir.h +++ b/compiler/dex/quick/mir_to_lir.h @@ -374,6 +374,7 @@ class Mir2Lir : public Backend { int SRegOffset(int s_reg); RegLocation GetReturnWide(bool is_double); RegLocation GetReturn(bool is_float); + RegisterInfo* GetRegInfo(int reg); // Shared by all targets - implemented in gen_common.cc. bool HandleEasyDivRem(Instruction::Code dalvik_opcode, bool is_div, @@ -550,7 +551,6 @@ class Mir2Lir : public Backend { virtual int AllocTypedTempPair(bool fp_hint, int reg_class) = 0; virtual int S2d(int low_reg, int high_reg) = 0; virtual int TargetReg(SpecialTargetRegister reg) = 0; - virtual RegisterInfo* GetRegInfo(int reg) = 0; virtual RegLocation GetReturnAlt() = 0; virtual RegLocation GetReturnWideAlt() = 0; virtual RegLocation LocCReturn() = 0; @@ -727,6 +727,8 @@ class Mir2Lir : public Backend { GrowableArray throw_launchpads_; GrowableArray suspend_launchpads_; GrowableArray intrinsic_launchpads_; + GrowableArray tempreg_info_; + GrowableArray reginfo_map_; /* * Holds mapping from native PC to dex PC for safepoints where we may deoptimize. * Native PC is on the return address of the safepointed operation. Dex PC is for diff --git a/compiler/dex/quick/ralloc_util.cc b/compiler/dex/quick/ralloc_util.cc index db110aabd50..a0f22fc576a 100644 --- a/compiler/dex/quick/ralloc_util.cc +++ b/compiler/dex/quick/ralloc_util.cc @@ -28,13 +28,9 @@ namespace art { * live until it is either explicitly killed or reallocated. */ void Mir2Lir::ResetRegPool() { - for (int i = 0; i < reg_pool_->num_core_regs; i++) { - if (reg_pool_->core_regs[i].is_temp) - reg_pool_->core_regs[i].in_use = false; - } - for (int i = 0; i < reg_pool_->num_fp_regs; i++) { - if (reg_pool_->FPRegs[i].is_temp) - reg_pool_->FPRegs[i].in_use = false; + GrowableArray::Iterator iter(&tempreg_info_); + for (RegisterInfo* info = iter.Next(); info != NULL; info = iter.Next()) { + info->in_use = false; } // Reset temp tracking sanity check. if (kIsDebugBuild) { @@ -48,13 +44,21 @@ void Mir2Lir::ResetRegPool() { */ void Mir2Lir::CompilerInitPool(RegisterInfo* regs, int* reg_nums, int num) { for (int i = 0; i < num; i++) { - regs[i].reg = reg_nums[i]; + uint32_t reg_number = reg_nums[i]; + regs[i].reg = reg_number; regs[i].in_use = false; regs[i].is_temp = false; regs[i].pair = false; regs[i].live = false; regs[i].dirty = false; regs[i].s_reg = INVALID_SREG; + size_t map_size = reginfo_map_.Size(); + if (reg_number >= map_size) { + for (uint32_t i = 0; i < ((reg_number - map_size) + 1); i++) { + reginfo_map_.Insert(NULL); + } + } + reginfo_map_.Put(reg_number, ®s[i]); } } @@ -551,24 +555,13 @@ void Mir2Lir::ResetDefTracking() { } void Mir2Lir::ClobberAllRegs() { - RegisterInfo* p; - for (p = reg_pool_->core_regs; p < reg_pool_->core_regs + reg_pool_->num_core_regs; p++) { - if (p->is_temp) { - p->live = false; - p->s_reg = INVALID_SREG; - p->def_start = NULL; - p->def_end = NULL; - p->pair = false; - } - } - for (p = reg_pool_->FPRegs; p < reg_pool_->FPRegs + reg_pool_->num_fp_regs; p++) { - if (p->is_temp) { - p->live = false; - p->s_reg = INVALID_SREG; - p->def_start = NULL; - p->def_end = NULL; - p->pair = false; - } + GrowableArray::Iterator iter(&tempreg_info_); + for (RegisterInfo* info = iter.Next(); info != NULL; info = iter.Next()) { + info->live = false; + info->s_reg = INVALID_SREG; + info->def_start = NULL; + info->def_end = NULL; + info->pair = false; } } @@ -624,11 +617,13 @@ void Mir2Lir::MarkLive(int reg, int s_reg) { void Mir2Lir::MarkTemp(int reg) { RegisterInfo* info = GetRegInfo(reg); + tempreg_info_.Insert(info); info->is_temp = true; } void Mir2Lir::UnmarkTemp(int reg) { RegisterInfo* info = GetRegInfo(reg); + tempreg_info_.Delete(info); info->is_temp = false; } diff --git a/compiler/dex/quick/x86/codegen_x86.h b/compiler/dex/quick/x86/codegen_x86.h index 478654d0b41..0f281106b27 100644 --- a/compiler/dex/quick/x86/codegen_x86.h +++ b/compiler/dex/quick/x86/codegen_x86.h @@ -52,7 +52,6 @@ class X86Mir2Lir : public Mir2Lir { int AllocTypedTempPair(bool fp_hint, int reg_class); int S2d(int low_reg, int high_reg); int TargetReg(SpecialTargetRegister reg); - RegisterInfo* GetRegInfo(int reg); RegLocation GetReturnAlt(); RegLocation GetReturnWideAlt(); RegLocation LocCReturn(); diff --git a/compiler/dex/quick/x86/target_x86.cc b/compiler/dex/quick/x86/target_x86.cc index 26accab3600..94dd759e91a 100644 --- a/compiler/dex/quick/x86/target_x86.cc +++ b/compiler/dex/quick/x86/target_x86.cc @@ -375,11 +375,6 @@ RegLocation X86Mir2Lir::GetReturnAlt() { return res; } -X86Mir2Lir::RegisterInfo* X86Mir2Lir::GetRegInfo(int reg) { - return X86_FPREG(reg) ? ®_pool_->FPRegs[reg & X86_FP_REG_MASK] - : ®_pool_->core_regs[reg]; -} - /* To be used when explicitly managing register use */ void X86Mir2Lir::LockCallTemps() { LockTemp(rX86_ARG0); From f409f8134fa7222bc56f50a96cd541bb76ce75e9 Mon Sep 17 00:00:00 2001 From: Kenny Root Date: Wed, 11 Sep 2013 15:15:09 -0700 Subject: [PATCH 0026/2402] Add explicit dependency on algorithm Change-Id: I3edb34b3c3a1e89a195db18ad8e6a71bc7b4a570 --- dalvikvm/dalvikvm.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/dalvikvm/dalvikvm.cc b/dalvikvm/dalvikvm.cc index 4607f9ae540..ea8461f97df 100644 --- a/dalvikvm/dalvikvm.cc +++ b/dalvikvm/dalvikvm.cc @@ -16,6 +16,7 @@ #include +#include #include #include #include From 967a0adf8b93a23d2a8fef82e06bd913db94ac19 Mon Sep 17 00:00:00 2001 From: Hiroshi Yamauchi Date: Tue, 10 Sep 2013 16:24:21 -0700 Subject: [PATCH 0027/2402] More allocation code optimizations. - Inline Class::AllocObject() and Array::Alloc(). - Inline some short Mutex functions and add LIKELY/UNLIKELY to some Mutex functions. - This change improves the Ritz MemAllocTest by ~6% on Nexus 4 and ~10% on host. Bug: 9986565 Change-Id: I1606c74ddb21676cbc1de1a40e9b076fc23eaea4 --- runtime/base/mutex-inl.h | 126 ++++++++++++++++++++ runtime/base/mutex.cc | 142 ++--------------------- runtime/gc/space/large_object_space.cc | 1 + runtime/indirect_reference_table_test.cc | 1 + runtime/mirror/array-inl.h | 35 ++++++ runtime/mirror/array.cc | 33 ------ runtime/mirror/class-inl.h | 9 ++ runtime/mirror/class.cc | 9 -- runtime/mirror/stack_trace_element.cc | 1 + runtime/mirror/string.cc | 2 +- runtime/thread_list.cc | 1 + 11 files changed, 183 insertions(+), 177 deletions(-) diff --git a/runtime/base/mutex-inl.h b/runtime/base/mutex-inl.h index 7e8365eaba0..c0cfee2463d 100644 --- a/runtime/base/mutex-inl.h +++ b/runtime/base/mutex-inl.h @@ -41,6 +41,54 @@ static inline int futex(volatile int *uaddr, int op, int val, const struct times } #endif // ART_USE_FUTEXES +#if defined(__APPLE__) + +// This works on Mac OS 10.6 but hasn't been tested on older releases. +struct __attribute__((__may_alias__)) darwin_pthread_mutex_t { + long padding0; // NOLINT(runtime/int) exact match to darwin type + int padding1; + uint32_t padding2; + int16_t padding3; + int16_t padding4; + uint32_t padding5; + pthread_t darwin_pthread_mutex_owner; + // ...other stuff we don't care about. +}; + +struct __attribute__((__may_alias__)) darwin_pthread_rwlock_t { + long padding0; // NOLINT(runtime/int) exact match to darwin type + pthread_mutex_t padding1; + int padding2; + pthread_cond_t padding3; + pthread_cond_t padding4; + int padding5; + int padding6; + pthread_t darwin_pthread_rwlock_owner; + // ...other stuff we don't care about. +}; + +#endif // __APPLE__ + +#if defined(__GLIBC__) + +struct __attribute__((__may_alias__)) glibc_pthread_mutex_t { + int32_t padding0[2]; + int owner; + // ...other stuff we don't care about. +}; + +struct __attribute__((__may_alias__)) glibc_pthread_rwlock_t { +#ifdef __LP64__ + int32_t padding0[6]; +#else + int32_t padding0[7]; +#endif + int writer; + // ...other stuff we don't care about. +}; + +#endif // __GLIBC__ + class ScopedContentionRecorder { public: ScopedContentionRecorder(BaseMutex* mutex, uint64_t blocked_tid, uint64_t owner_tid) @@ -185,6 +233,84 @@ inline void ReaderWriterMutex::SharedUnlock(Thread* self) { #endif } +inline bool Mutex::IsExclusiveHeld(const Thread* self) const { + DCHECK(self == NULL || self == Thread::Current()); + bool result = (GetExclusiveOwnerTid() == SafeGetTid(self)); + if (kDebugLocking) { + // Sanity debug check that if we think it is locked we have it in our held mutexes. + if (result && self != NULL && level_ != kMonitorLock && !gAborting) { + CHECK_EQ(self->GetHeldMutex(level_), this); + } + } + return result; +} + +inline uint64_t Mutex::GetExclusiveOwnerTid() const { +#if ART_USE_FUTEXES + return exclusive_owner_; +#elif defined(__BIONIC__) + return static_cast((mutex_.value >> 16) & 0xffff); +#elif defined(__GLIBC__) + return reinterpret_cast(&mutex_)->owner; +#elif defined(__APPLE__) + const darwin_pthread_mutex_t* dpmutex = reinterpret_cast(&mutex_); + pthread_t owner = dpmutex->darwin_pthread_mutex_owner; + // 0 for unowned, -1 for PTHREAD_MTX_TID_SWITCHING + // TODO: should we make darwin_pthread_mutex_owner volatile and recheck until not -1? + if ((owner == (pthread_t)0) || (owner == (pthread_t)-1)) { + return 0; + } + uint64_t tid; + CHECK_PTHREAD_CALL(pthread_threadid_np, (owner, &tid), __FUNCTION__); // Requires Mac OS 10.6 + return tid; +#else +#error unsupported C library +#endif +} + +inline bool ReaderWriterMutex::IsExclusiveHeld(const Thread* self) const { + DCHECK(self == NULL || self == Thread::Current()); + bool result = (GetExclusiveOwnerTid() == SafeGetTid(self)); + if (kDebugLocking) { + // Sanity that if the pthread thinks we own the lock the Thread agrees. + if (self != NULL && result) { + CHECK_EQ(self->GetHeldMutex(level_), this); + } + } + return result; +} + +inline uint64_t ReaderWriterMutex::GetExclusiveOwnerTid() const { +#if ART_USE_FUTEXES + int32_t state = state_; + if (state == 0) { + return 0; // No owner. + } else if (state > 0) { + return -1; // Shared. + } else { + return exclusive_owner_; + } +#else +#if defined(__BIONIC__) + return rwlock_.writerThreadId; +#elif defined(__GLIBC__) + return reinterpret_cast(&rwlock_)->writer; +#elif defined(__APPLE__) + const darwin_pthread_rwlock_t* + dprwlock = reinterpret_cast(&rwlock_); + pthread_t owner = dprwlock->darwin_pthread_rwlock_owner; + if (owner == (pthread_t)0) { + return 0; + } + uint64_t tid; + CHECK_PTHREAD_CALL(pthread_threadid_np, (owner, &tid), __FUNCTION__); // Requires Mac OS 10.6 + return tid; +#else +#error unsupported C library +#endif +#endif +} + } // namespace art #endif // ART_RUNTIME_BASE_MUTEX_INL_H_ diff --git a/runtime/base/mutex.cc b/runtime/base/mutex.cc index b99e7c9281d..b048bbb1ec5 100644 --- a/runtime/base/mutex.cc +++ b/runtime/base/mutex.cc @@ -31,54 +31,6 @@ namespace art { -#if defined(__APPLE__) - -// This works on Mac OS 10.6 but hasn't been tested on older releases. -struct __attribute__((__may_alias__)) darwin_pthread_mutex_t { - long padding0; // NOLINT(runtime/int) exact match to darwin type - int padding1; - uint32_t padding2; - int16_t padding3; - int16_t padding4; - uint32_t padding5; - pthread_t darwin_pthread_mutex_owner; - // ...other stuff we don't care about. -}; - -struct __attribute__((__may_alias__)) darwin_pthread_rwlock_t { - long padding0; // NOLINT(runtime/int) exact match to darwin type - pthread_mutex_t padding1; - int padding2; - pthread_cond_t padding3; - pthread_cond_t padding4; - int padding5; - int padding6; - pthread_t darwin_pthread_rwlock_owner; - // ...other stuff we don't care about. -}; - -#endif // __APPLE__ - -#if defined(__GLIBC__) - -struct __attribute__((__may_alias__)) glibc_pthread_mutex_t { - int32_t padding0[2]; - int owner; - // ...other stuff we don't care about. -}; - -struct __attribute__((__may_alias__)) glibc_pthread_rwlock_t { -#ifdef __LP64__ - int32_t padding0[6]; -#else - int32_t padding0[7]; -#endif - int writer; - // ...other stuff we don't care about. -}; - -#endif // __GLIBC__ - #if ART_USE_FUTEXES static bool ComputeRelativeTimeSpec(timespec* result_ts, const timespec& lhs, const timespec& rhs) { const int32_t one_sec = 1000 * 1000 * 1000; // one second in nanoseconds. @@ -346,7 +298,7 @@ void Mutex::ExclusiveLock(Thread* self) { bool done = false; do { int32_t cur_state = state_; - if (cur_state == 0) { + if (LIKELY(cur_state == 0)) { // Change state from 0 to 1. done = android_atomic_acquire_cas(0, 1, &state_) == 0; } else { @@ -432,14 +384,14 @@ void Mutex::ExclusiveUnlock(Thread* self) { bool done = false; do { int32_t cur_state = state_; - if (cur_state == 1) { + if (LIKELY(cur_state == 1)) { // We're no longer the owner. exclusive_owner_ = 0; // Change state to 0. done = android_atomic_release_cas(cur_state, 0, &state_) == 0; - if (done) { // Spurious fail? + if (LIKELY(done)) { // Spurious fail? // Wake a contender - if (num_contenders_ > 0) { + if (UNLIKELY(num_contenders_ > 0)) { futex(&state_, FUTEX_WAKE, 1, NULL, NULL, 0); } } @@ -461,41 +413,6 @@ void Mutex::ExclusiveUnlock(Thread* self) { } } -bool Mutex::IsExclusiveHeld(const Thread* self) const { - DCHECK(self == NULL || self == Thread::Current()); - bool result = (GetExclusiveOwnerTid() == SafeGetTid(self)); - if (kDebugLocking) { - // Sanity debug check that if we think it is locked we have it in our held mutexes. - if (result && self != NULL && level_ != kMonitorLock && !gAborting) { - CHECK_EQ(self->GetHeldMutex(level_), this); - } - } - return result; -} - -uint64_t Mutex::GetExclusiveOwnerTid() const { -#if ART_USE_FUTEXES - return exclusive_owner_; -#elif defined(__BIONIC__) - return static_cast((mutex_.value >> 16) & 0xffff); -#elif defined(__GLIBC__) - return reinterpret_cast(&mutex_)->owner; -#elif defined(__APPLE__) - const darwin_pthread_mutex_t* dpmutex = reinterpret_cast(&mutex_); - pthread_t owner = dpmutex->darwin_pthread_mutex_owner; - // 0 for unowned, -1 for PTHREAD_MTX_TID_SWITCHING - // TODO: should we make darwin_pthread_mutex_owner volatile and recheck until not -1? - if ((owner == (pthread_t)0) || (owner == (pthread_t)-1)) { - return 0; - } - uint64_t tid; - CHECK_PTHREAD_CALL(pthread_threadid_np, (owner, &tid), __FUNCTION__); // Requires Mac OS 10.6 - return tid; -#else -#error unsupported C library -#endif -} - void Mutex::Dump(std::ostream& os) const { os << (recursive_ ? "recursive " : "non-recursive ") << name_ @@ -549,7 +466,7 @@ void ReaderWriterMutex::ExclusiveLock(Thread* self) { bool done = false; do { int32_t cur_state = state_; - if (cur_state == 0) { + if (LIKELY(cur_state == 0)) { // Change state from 0 to -1. done = android_atomic_acquire_cas(0, -1, &state_) == 0; } else { @@ -583,14 +500,14 @@ void ReaderWriterMutex::ExclusiveUnlock(Thread* self) { bool done = false; do { int32_t cur_state = state_; - if (cur_state == -1) { + if (LIKELY(cur_state == -1)) { // We're no longer the owner. exclusive_owner_ = 0; // Change state from -1 to 0. done = android_atomic_release_cas(-1, 0, &state_) == 0; - if (done) { // cmpxchg may fail due to noise? + if (LIKELY(done)) { // cmpxchg may fail due to noise? // Wake any waiters. - if (num_pending_readers_ > 0 || num_pending_writers_ > 0) { + if (UNLIKELY(num_pending_readers_ > 0 || num_pending_writers_ > 0)) { futex(&state_, FUTEX_WAKE, -1, NULL, NULL, 0); } } @@ -687,18 +604,6 @@ bool ReaderWriterMutex::SharedTryLock(Thread* self) { return true; } -bool ReaderWriterMutex::IsExclusiveHeld(const Thread* self) const { - DCHECK(self == NULL || self == Thread::Current()); - bool result = (GetExclusiveOwnerTid() == SafeGetTid(self)); - if (kDebugLocking) { - // Sanity that if the pthread thinks we own the lock the Thread agrees. - if (self != NULL && result) { - CHECK_EQ(self->GetHeldMutex(level_), this); - } - } - return result; -} - bool ReaderWriterMutex::IsSharedHeld(const Thread* self) const { DCHECK(self == NULL || self == Thread::Current()); bool result; @@ -710,37 +615,6 @@ bool ReaderWriterMutex::IsSharedHeld(const Thread* self) const { return result; } -uint64_t ReaderWriterMutex::GetExclusiveOwnerTid() const { -#if ART_USE_FUTEXES - int32_t state = state_; - if (state == 0) { - return 0; // No owner. - } else if (state > 0) { - return -1; // Shared. - } else { - return exclusive_owner_; - } -#else -#if defined(__BIONIC__) - return rwlock_.writerThreadId; -#elif defined(__GLIBC__) - return reinterpret_cast(&rwlock_)->writer; -#elif defined(__APPLE__) - const darwin_pthread_rwlock_t* - dprwlock = reinterpret_cast(&rwlock_); - pthread_t owner = dprwlock->darwin_pthread_rwlock_owner; - if (owner == (pthread_t)0) { - return 0; - } - uint64_t tid; - CHECK_PTHREAD_CALL(pthread_threadid_np, (owner, &tid), __FUNCTION__); // Requires Mac OS 10.6 - return tid; -#else -#error unsupported C library -#endif -#endif -} - void ReaderWriterMutex::Dump(std::ostream& os) const { os << name_ << " level=" << static_cast(level_) diff --git a/runtime/gc/space/large_object_space.cc b/runtime/gc/space/large_object_space.cc index a174c0a1dc3..c6d028eed50 100644 --- a/runtime/gc/space/large_object_space.cc +++ b/runtime/gc/space/large_object_space.cc @@ -17,6 +17,7 @@ #include "large_object_space.h" #include "base/logging.h" +#include "base/mutex-inl.h" #include "base/stl_util.h" #include "UniquePtr.h" #include "image.h" diff --git a/runtime/indirect_reference_table_test.cc b/runtime/indirect_reference_table_test.cc index bd2890c497b..b6c6cb4be37 100644 --- a/runtime/indirect_reference_table_test.cc +++ b/runtime/indirect_reference_table_test.cc @@ -17,6 +17,7 @@ #include "common_test.h" #include "indirect_reference_table.h" +#include "mirror/object-inl.h" namespace art { diff --git a/runtime/mirror/array-inl.h b/runtime/mirror/array-inl.h index eb73c7dd385..c7b370f877f 100644 --- a/runtime/mirror/array-inl.h +++ b/runtime/mirror/array-inl.h @@ -20,6 +20,8 @@ #include "array.h" #include "class.h" +#include "thread.h" +#include "utils.h" namespace art { namespace mirror { @@ -33,6 +35,39 @@ inline size_t Array::SizeOf() const { return header_size + data_size; } +inline Array* Array::Alloc(Thread* self, Class* array_class, int32_t component_count, + size_t component_size) { + DCHECK(array_class != NULL); + DCHECK_GE(component_count, 0); + DCHECK(array_class->IsArrayClass()); + + size_t header_size = sizeof(Object) + (component_size == sizeof(int64_t) ? 8 : 4); + size_t data_size = component_count * component_size; + size_t size = header_size + data_size; + + // Check for overflow and throw OutOfMemoryError if this was an unreasonable request. + size_t component_shift = sizeof(size_t) * 8 - 1 - CLZ(component_size); + if (UNLIKELY(data_size >> component_shift != size_t(component_count) || size < data_size)) { + self->ThrowOutOfMemoryError(StringPrintf("%s of length %d would overflow", + PrettyDescriptor(array_class).c_str(), + component_count).c_str()); + return NULL; + } + + gc::Heap* heap = Runtime::Current()->GetHeap(); + Array* array = down_cast(heap->AllocObject(self, array_class, size)); + if (LIKELY(array != NULL)) { + DCHECK(array->IsArrayInstance()); + array->SetLength(component_count); + } + return array; +} + +inline Array* Array::Alloc(Thread* self, Class* array_class, int32_t component_count) { + DCHECK(array_class->IsArrayClass()); + return Alloc(self, array_class, component_count, array_class->GetComponentSize()); +} + } // namespace mirror } // namespace art diff --git a/runtime/mirror/array.cc b/runtime/mirror/array.cc index 88cd309eeb1..020085dbf03 100644 --- a/runtime/mirror/array.cc +++ b/runtime/mirror/array.cc @@ -32,39 +32,6 @@ namespace art { namespace mirror { -Array* Array::Alloc(Thread* self, Class* array_class, int32_t component_count, - size_t component_size) { - DCHECK(array_class != NULL); - DCHECK_GE(component_count, 0); - DCHECK(array_class->IsArrayClass()); - - size_t header_size = sizeof(Object) + (component_size == sizeof(int64_t) ? 8 : 4); - size_t data_size = component_count * component_size; - size_t size = header_size + data_size; - - // Check for overflow and throw OutOfMemoryError if this was an unreasonable request. - size_t component_shift = sizeof(size_t) * 8 - 1 - CLZ(component_size); - if (UNLIKELY(data_size >> component_shift != size_t(component_count) || size < data_size)) { - self->ThrowOutOfMemoryError(StringPrintf("%s of length %d would overflow", - PrettyDescriptor(array_class).c_str(), - component_count).c_str()); - return NULL; - } - - gc::Heap* heap = Runtime::Current()->GetHeap(); - Array* array = down_cast(heap->AllocObject(self, array_class, size)); - if (array != NULL) { - DCHECK(array->IsArrayInstance()); - array->SetLength(component_count); - } - return array; -} - -Array* Array::Alloc(Thread* self, Class* array_class, int32_t component_count) { - DCHECK(array_class->IsArrayClass()); - return Alloc(self, array_class, component_count, array_class->GetComponentSize()); -} - // Create a multi-dimensional array of Objects or primitive types. // // We have to generate the names for X[], X[][], X[][][], and so on. The diff --git a/runtime/mirror/class-inl.h b/runtime/mirror/class-inl.h index 1e1138745d2..438ce81db25 100644 --- a/runtime/mirror/class-inl.h +++ b/runtime/mirror/class-inl.h @@ -342,6 +342,15 @@ inline void Class::SetName(String* name) { SetFieldObject(OFFSET_OF_OBJECT_MEMBER(Class, name_), name, false); } +inline Object* Class::AllocObject(Thread* self) { + DCHECK(!IsArrayClass()) << PrettyClass(this); + DCHECK(IsInstantiable()) << PrettyClass(this); + // TODO: decide whether we want this check. It currently fails during bootstrap. + // DCHECK(!Runtime::Current()->IsStarted() || IsInitializing()) << PrettyClass(this); + DCHECK_GE(this->object_size_, sizeof(Object)); + return Runtime::Current()->GetHeap()->AllocObject(self, this, this->object_size_); +} + } // namespace mirror } // namespace art diff --git a/runtime/mirror/class.cc b/runtime/mirror/class.cc index 5e8b82793af..328c67deb11 100644 --- a/runtime/mirror/class.cc +++ b/runtime/mirror/class.cc @@ -118,15 +118,6 @@ void Class::SetDexCache(DexCache* new_dex_cache) { SetFieldObject(OFFSET_OF_OBJECT_MEMBER(Class, dex_cache_), new_dex_cache, false); } -Object* Class::AllocObject(Thread* self) { - DCHECK(!IsArrayClass()) << PrettyClass(this); - DCHECK(IsInstantiable()) << PrettyClass(this); - // TODO: decide whether we want this check. It currently fails during bootstrap. - // DCHECK(!Runtime::Current()->IsStarted() || IsInitializing()) << PrettyClass(this); - DCHECK_GE(this->object_size_, sizeof(Object)); - return Runtime::Current()->GetHeap()->AllocObject(self, this, this->object_size_); -} - void Class::SetClassSize(size_t new_class_size) { DCHECK_GE(new_class_size, GetClassSize()) << " class=" << PrettyTypeOf(this); SetField32(OFFSET_OF_OBJECT_MEMBER(Class, class_size_), new_class_size, false); diff --git a/runtime/mirror/stack_trace_element.cc b/runtime/mirror/stack_trace_element.cc index a505ed00a33..9d76c6bc117 100644 --- a/runtime/mirror/stack_trace_element.cc +++ b/runtime/mirror/stack_trace_element.cc @@ -17,6 +17,7 @@ #include "stack_trace_element.h" #include "class.h" +#include "class-inl.h" #include "gc/accounting/card_table-inl.h" #include "object-inl.h" #include "string.h" diff --git a/runtime/mirror/string.cc b/runtime/mirror/string.cc index f8a0e531eb0..7d968c774d5 100644 --- a/runtime/mirror/string.cc +++ b/runtime/mirror/string.cc @@ -17,6 +17,7 @@ #include "string.h" #include "array.h" +#include "class-inl.h" #include "gc/accounting/card_table-inl.h" #include "intern_table.h" #include "object-inl.h" @@ -285,4 +286,3 @@ int32_t String::CompareTo(String* rhs) const { } // namespace mirror } // namespace art - diff --git a/runtime/thread_list.cc b/runtime/thread_list.cc index 671924a6204..aba81feeff5 100644 --- a/runtime/thread_list.cc +++ b/runtime/thread_list.cc @@ -21,6 +21,7 @@ #include #include "base/mutex.h" +#include "base/mutex-inl.h" #include "base/timing_logger.h" #include "debugger.h" #include "thread.h" From 570c009c9994225db8cb94838e0adcaf306aa9b9 Mon Sep 17 00:00:00 2001 From: Kenny Root Date: Wed, 11 Sep 2013 15:15:09 -0700 Subject: [PATCH 0028/2402] Add explicit dependency on algorithm (cherry picked from commit f409f8134fa7222bc56f50a96cd541bb76ce75e9) Change-Id: I3edb34b3c3a1e89a195db18ad8e6a71bc7b4a570 --- dalvikvm/dalvikvm.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/dalvikvm/dalvikvm.cc b/dalvikvm/dalvikvm.cc index 4607f9ae540..ea8461f97df 100644 --- a/dalvikvm/dalvikvm.cc +++ b/dalvikvm/dalvikvm.cc @@ -16,6 +16,7 @@ #include +#include #include #include #include From 78765e84a3654357a03f84b76985556cf7d9731a Mon Sep 17 00:00:00 2001 From: Jeff Hao Date: Wed, 11 Sep 2013 18:04:26 -0700 Subject: [PATCH 0029/2402] Fix CAS intrinsic to clear exclusive if values don't match. The LDREX has a matching STREX if the values match, but it needed a CLREX for the case where they didn't. Bug: 10530407 Change-Id: I46b474cca326a251536e7f214c80486694431386 --- compiler/dex/quick/arm/int_arm.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/compiler/dex/quick/arm/int_arm.cc b/compiler/dex/quick/arm/int_arm.cc index bd659e6c2bc..01399539fd3 100644 --- a/compiler/dex/quick/arm/int_arm.cc +++ b/compiler/dex/quick/arm/int_arm.cc @@ -555,10 +555,11 @@ bool ArmMir2Lir::GenInlinedCas32(CallInfo* info, bool need_write_barrier) { OpRegReg(kOpCmp, r_old_value, rl_expected.low_reg); FreeTemp(r_old_value); // Now unneeded. RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true); - OpIT(kCondEq, "TE"); + OpIT(kCondEq, "TEE"); NewLIR4(kThumb2Strex, rl_result.low_reg, rl_new_value.low_reg, r_ptr, 0); FreeTemp(r_ptr); // Now unneeded. OpRegImm(kOpXor, rl_result.low_reg, 1); + NewLIR0(kThumb2Clrex); OpRegReg(kOpXor, rl_result.low_reg, rl_result.low_reg); StoreValue(rl_dest, rl_result); From 75165d015cc737f951d4264f8c55012298abdb18 Mon Sep 17 00:00:00 2001 From: Mathieu Chartier Date: Thu, 12 Sep 2013 14:00:31 -0700 Subject: [PATCH 0030/2402] Add valgrind support to compiler arena allocator. Change-Id: Id9974301d3810bfac968ee562b01a11098e402c2 --- build/Android.common.mk | 1 + compiler/dex/arena_allocator.cc | 31 +++++++++++++++++++++++++++++- compiler/dex/arena_allocator.h | 9 ++++++--- runtime/gc/space/dlmalloc_space.cc | 2 +- 4 files changed, 38 insertions(+), 5 deletions(-) diff --git a/build/Android.common.mk b/build/Android.common.mk index ac1be1e28ad..70fa6d8c9f9 100644 --- a/build/Android.common.mk +++ b/build/Android.common.mk @@ -104,6 +104,7 @@ ART_HOST_SHLIB_EXTENSION ?= .so ART_C_INCLUDES := \ external/gtest/include \ external/valgrind/main/include \ + external/valgrind/main \ external/zlib \ frameworks/compile/mclinker/include diff --git a/compiler/dex/arena_allocator.cc b/compiler/dex/arena_allocator.cc index 5a91d27ef5d..2da806437c7 100644 --- a/compiler/dex/arena_allocator.cc +++ b/compiler/dex/arena_allocator.cc @@ -20,12 +20,14 @@ #include "base/logging.h" #include "base/mutex.h" #include "thread-inl.h" +#include namespace art { // Memmap is a bit slower than malloc according to my measurements. static constexpr bool kUseMemMap = false; static constexpr bool kUseMemSet = true && kUseMemMap; +static constexpr size_t kValgrindRedZoneBytes = 8; static const char* alloc_names[ArenaAllocator::kNumAllocKinds] = { "Misc ", @@ -108,6 +110,9 @@ Arena* ArenaPool::AllocArena(size_t size) { void ArenaPool::FreeArena(Arena* arena) { Thread* self = Thread::Current(); + if (UNLIKELY(RUNNING_ON_VALGRIND)) { + VALGRIND_MAKE_MEM_UNDEFINED(arena->memory_, arena->bytes_allocated_); + } { MutexLock lock(self, lock_); arena->next_ = free_arenas_; @@ -129,7 +134,8 @@ ArenaAllocator::ArenaAllocator(ArenaPool* pool) end_(nullptr), ptr_(nullptr), arena_head_(nullptr), - num_allocations_(0) { + num_allocations_(0), + running_on_valgrind_(RUNNING_ON_VALGRIND) { memset(&alloc_stats_[0], 0, sizeof(alloc_stats_)); } @@ -141,6 +147,29 @@ void ArenaAllocator::UpdateBytesAllocated() { } } +void* ArenaAllocator::AllocValgrind(size_t bytes, ArenaAllocKind kind) { + size_t rounded_bytes = (bytes + 3 + kValgrindRedZoneBytes) & ~3; + if (UNLIKELY(ptr_ + rounded_bytes > end_)) { + // Obtain a new block. + ObtainNewArenaForAllocation(rounded_bytes); + if (UNLIKELY(ptr_ == nullptr)) { + return nullptr; + } + } + if (kCountAllocations) { + alloc_stats_[kind] += rounded_bytes; + ++num_allocations_; + } + uint8_t* ret = ptr_; + ptr_ += rounded_bytes; + // Check that the memory is already zeroed out. + for (uint8_t* ptr = ret; ptr < ptr_; ++ptr) { + CHECK_EQ(*ptr, 0U); + } + VALGRIND_MAKE_MEM_NOACCESS(ret + bytes, rounded_bytes - bytes); + return ret; +} + ArenaAllocator::~ArenaAllocator() { // Reclaim all the arenas by giving them back to the thread pool. UpdateBytesAllocated(); diff --git a/compiler/dex/arena_allocator.h b/compiler/dex/arena_allocator.h index dda52a2ed0a..d11d67c7957 100644 --- a/compiler/dex/arena_allocator.h +++ b/compiler/dex/arena_allocator.h @@ -103,6 +103,9 @@ class ArenaAllocator { // Returns zeroed memory. void* Alloc(size_t bytes, ArenaAllocKind kind) ALWAYS_INLINE { + if (UNLIKELY(running_on_valgrind_)) { + return AllocValgrind(bytes, kind); + } bytes = (bytes + 3) & ~3; if (UNLIKELY(ptr_ + bytes > end_)) { // Obtain a new block. @@ -120,6 +123,7 @@ class ArenaAllocator { return ret; } + void* AllocValgrind(size_t bytes, ArenaAllocKind kind); void ObtainNewArenaForAllocation(size_t allocation_size); size_t BytesAllocated() const; void DumpMemStats(std::ostream& os) const; @@ -132,10 +136,9 @@ class ArenaAllocator { uint8_t* end_; uint8_t* ptr_; Arena* arena_head_; - - // Statistics. size_t num_allocations_; - size_t alloc_stats_[kNumAllocKinds]; // Bytes used by various allocation kinds. + size_t alloc_stats_[kNumAllocKinds]; // Bytes used by various allocation kinds. + bool running_on_valgrind_; DISALLOW_COPY_AND_ASSIGN(ArenaAllocator); }; // ArenaAllocator diff --git a/runtime/gc/space/dlmalloc_space.cc b/runtime/gc/space/dlmalloc_space.cc index 8b99e96ceb7..0de676e4385 100644 --- a/runtime/gc/space/dlmalloc_space.cc +++ b/runtime/gc/space/dlmalloc_space.cc @@ -22,7 +22,7 @@ #include "utils.h" #include -#include <../memcheck/memcheck.h> +#include namespace art { namespace gc { From 2de2aa1a96dfa5bebc004f29b5dbfafd37039cee Mon Sep 17 00:00:00 2001 From: Jeff Hao Date: Thu, 12 Sep 2013 17:20:31 -0700 Subject: [PATCH 0031/2402] Make inlined CAS32 loop until store is successful if values match. The native implementation of compareAndSwap uses android_atomic_cas, which will repeat the strex until it succeeds. The compiled version was changed to do the same. Bug: 10530407 Change-Id: I7efb3f92d0d0610fcc5a885e2c97f1d701b5a4ea --- compiler/dex/quick/arm/int_arm.cc | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/compiler/dex/quick/arm/int_arm.cc b/compiler/dex/quick/arm/int_arm.cc index bd659e6c2bc..07782d957f9 100644 --- a/compiler/dex/quick/arm/int_arm.cc +++ b/compiler/dex/quick/arm/int_arm.cc @@ -541,25 +541,26 @@ bool ArmMir2Lir::GenInlinedCas32(CallInfo* info, bool need_write_barrier) { ClobberSReg(rl_offset.s_reg_low); FreeTemp(rl_offset.low_reg); - int r_old_value = AllocTemp(); - NewLIR3(kThumb2Ldrex, r_old_value, r_ptr, 0); // r_old_value := [r_ptr] - - RegLocation rl_expected = LoadValue(rl_src_expected, kCoreReg); + RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true); + LoadConstant(rl_result.low_reg, 0); // r_result := 0 - // if (r_old_value == rExpected) { + // while ([r_ptr] == rExpected && r_result == 0) { // [r_ptr] <- r_new_value && r_result := success ? 0 : 1 // r_result ^= 1 - // } else { - // r_result := 0 // } + int r_old_value = AllocTemp(); + LIR* target = NewLIR0(kPseudoTargetLabel); + NewLIR3(kThumb2Ldrex, r_old_value, r_ptr, 0); + + RegLocation rl_expected = LoadValue(rl_src_expected, kCoreReg); OpRegReg(kOpCmp, r_old_value, rl_expected.low_reg); FreeTemp(r_old_value); // Now unneeded. - RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true); - OpIT(kCondEq, "TE"); - NewLIR4(kThumb2Strex, rl_result.low_reg, rl_new_value.low_reg, r_ptr, 0); + OpIT(kCondEq, "TT"); + NewLIR4(kThumb2Strex /* eq */, rl_result.low_reg, rl_new_value.low_reg, r_ptr, 0); FreeTemp(r_ptr); // Now unneeded. - OpRegImm(kOpXor, rl_result.low_reg, 1); - OpRegReg(kOpXor, rl_result.low_reg, rl_result.low_reg); + OpRegImm(kOpXor /* eq */, rl_result.low_reg, 1); + OpRegImm(kOpCmp /* eq */, rl_result.low_reg, 0); + OpCondBranch(kCondEq, target); StoreValue(rl_dest, rl_result); From cdf2d4cbbb1d0cf3e9f80d3dca609a3435c74427 Mon Sep 17 00:00:00 2001 From: Sebastien Hertz Date: Fri, 13 Sep 2013 14:57:51 +0200 Subject: [PATCH 0032/2402] Update interpreter handler table after invoke. And introduce UPDATE_HANDLER_TABLE macro to reduce copy & paste. Change-Id: I9ea7565df0db2b44581d0e6bcefb5f104e76ed01 --- .../interpreter_goto_table_impl.cc | 129 ++++++------------ 1 file changed, 39 insertions(+), 90 deletions(-) diff --git a/runtime/interpreter/interpreter_goto_table_impl.cc b/runtime/interpreter/interpreter_goto_table_impl.cc index 10c50f3cce9..7f4c7c83022 100644 --- a/runtime/interpreter/interpreter_goto_table_impl.cc +++ b/runtime/interpreter/interpreter_goto_table_impl.cc @@ -51,6 +51,15 @@ namespace interpreter { } \ } while (false) +#define UPDATE_HANDLER_TABLE() \ + do { \ + if (UNLIKELY(instrumentation->HasDexPcListeners())) { \ + currentHandlersTable = instrumentationHandlersTable; \ + } else { \ + currentHandlersTable = handlersTable; \ + } \ + } while (false); + #define UNREACHABLE_CODE_CHECK() \ do { \ if (kIsDebugBuild) { \ @@ -104,11 +113,7 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* }; const void** currentHandlersTable; - if (UNLIKELY(instrumentation->HasDexPcListeners())) { - currentHandlersTable = instrumentationHandlersTable; - } else { - currentHandlersTable = handlersTable; - } + UPDATE_HANDLER_TABLE(); // Jump to first instruction. ADVANCE(0); @@ -524,11 +529,7 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* if (UNLIKELY(self->TestAllFlags())) { CheckSuspend(self); } - if (UNLIKELY(instrumentation->HasDexPcListeners())) { - currentHandlersTable = instrumentationHandlersTable; - } else { - currentHandlersTable = handlersTable; - } + UPDATE_HANDLER_TABLE(); } ADVANCE(offset); } @@ -540,11 +541,7 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* if (UNLIKELY(self->TestAllFlags())) { CheckSuspend(self); } - if (UNLIKELY(instrumentation->HasDexPcListeners())) { - currentHandlersTable = instrumentationHandlersTable; - } else { - currentHandlersTable = handlersTable; - } + UPDATE_HANDLER_TABLE(); } ADVANCE(offset); } @@ -556,11 +553,7 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* if (UNLIKELY(self->TestAllFlags())) { CheckSuspend(self); } - if (UNLIKELY(instrumentation->HasDexPcListeners())) { - currentHandlersTable = instrumentationHandlersTable; - } else { - currentHandlersTable = handlersTable; - } + UPDATE_HANDLER_TABLE(); } ADVANCE(offset); } @@ -572,11 +565,7 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* if (UNLIKELY(self->TestAllFlags())) { CheckSuspend(self); } - if (UNLIKELY(instrumentation->HasDexPcListeners())) { - currentHandlersTable = instrumentationHandlersTable; - } else { - currentHandlersTable = handlersTable; - } + UPDATE_HANDLER_TABLE(); } ADVANCE(offset); } @@ -588,11 +577,7 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* if (UNLIKELY(self->TestAllFlags())) { CheckSuspend(self); } - if (UNLIKELY(instrumentation->HasDexPcListeners())) { - currentHandlersTable = instrumentationHandlersTable; - } else { - currentHandlersTable = handlersTable; - } + UPDATE_HANDLER_TABLE(); } ADVANCE(offset); } @@ -685,11 +670,7 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* if (UNLIKELY(self->TestAllFlags())) { CheckSuspend(self); } - if (UNLIKELY(instrumentation->HasDexPcListeners())) { - currentHandlersTable = instrumentationHandlersTable; - } else { - currentHandlersTable = handlersTable; - } + UPDATE_HANDLER_TABLE(); } ADVANCE(offset); } else { @@ -705,11 +686,7 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* if (UNLIKELY(self->TestAllFlags())) { CheckSuspend(self); } - if (UNLIKELY(instrumentation->HasDexPcListeners())) { - currentHandlersTable = instrumentationHandlersTable; - } else { - currentHandlersTable = handlersTable; - } + UPDATE_HANDLER_TABLE(); } ADVANCE(offset); } else { @@ -725,11 +702,7 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* if (UNLIKELY(self->TestAllFlags())) { CheckSuspend(self); } - if (UNLIKELY(instrumentation->HasDexPcListeners())) { - currentHandlersTable = instrumentationHandlersTable; - } else { - currentHandlersTable = handlersTable; - } + UPDATE_HANDLER_TABLE(); } ADVANCE(offset); } else { @@ -745,11 +718,7 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* if (UNLIKELY(self->TestAllFlags())) { CheckSuspend(self); } - if (UNLIKELY(instrumentation->HasDexPcListeners())) { - currentHandlersTable = instrumentationHandlersTable; - } else { - currentHandlersTable = handlersTable; - } + UPDATE_HANDLER_TABLE(); } ADVANCE(offset); } else { @@ -765,11 +734,7 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* if (UNLIKELY(self->TestAllFlags())) { CheckSuspend(self); } - if (UNLIKELY(instrumentation->HasDexPcListeners())) { - currentHandlersTable = instrumentationHandlersTable; - } else { - currentHandlersTable = handlersTable; - } + UPDATE_HANDLER_TABLE(); } ADVANCE(offset); } else { @@ -785,11 +750,7 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* if (UNLIKELY(self->TestAllFlags())) { CheckSuspend(self); } - if (UNLIKELY(instrumentation->HasDexPcListeners())) { - currentHandlersTable = instrumentationHandlersTable; - } else { - currentHandlersTable = handlersTable; - } + UPDATE_HANDLER_TABLE(); } ADVANCE(offset); } else { @@ -805,11 +766,7 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* if (UNLIKELY(self->TestAllFlags())) { CheckSuspend(self); } - if (UNLIKELY(instrumentation->HasDexPcListeners())) { - currentHandlersTable = instrumentationHandlersTable; - } else { - currentHandlersTable = handlersTable; - } + UPDATE_HANDLER_TABLE(); } ADVANCE(offset); } else { @@ -825,11 +782,7 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* if (UNLIKELY(self->TestAllFlags())) { CheckSuspend(self); } - if (UNLIKELY(instrumentation->HasDexPcListeners())) { - currentHandlersTable = instrumentationHandlersTable; - } else { - currentHandlersTable = handlersTable; - } + UPDATE_HANDLER_TABLE(); } ADVANCE(offset); } else { @@ -845,11 +798,7 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* if (UNLIKELY(self->TestAllFlags())) { CheckSuspend(self); } - if (UNLIKELY(instrumentation->HasDexPcListeners())) { - currentHandlersTable = instrumentationHandlersTable; - } else { - currentHandlersTable = handlersTable; - } + UPDATE_HANDLER_TABLE(); } ADVANCE(offset); } else { @@ -865,11 +814,7 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* if (UNLIKELY(self->TestAllFlags())) { CheckSuspend(self); } - if (UNLIKELY(instrumentation->HasDexPcListeners())) { - currentHandlersTable = instrumentationHandlersTable; - } else { - currentHandlersTable = handlersTable; - } + UPDATE_HANDLER_TABLE(); } ADVANCE(offset); } else { @@ -885,11 +830,7 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* if (UNLIKELY(self->TestAllFlags())) { CheckSuspend(self); } - if (UNLIKELY(instrumentation->HasDexPcListeners())) { - currentHandlersTable = instrumentationHandlersTable; - } else { - currentHandlersTable = handlersTable; - } + UPDATE_HANDLER_TABLE(); } ADVANCE(offset); } else { @@ -905,11 +846,7 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* if (UNLIKELY(self->TestAllFlags())) { CheckSuspend(self); } - if (UNLIKELY(instrumentation->HasDexPcListeners())) { - currentHandlersTable = instrumentationHandlersTable; - } else { - currentHandlersTable = handlersTable; - } + UPDATE_HANDLER_TABLE(); } ADVANCE(offset); } else { @@ -1383,72 +1320,84 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* HANDLE_INSTRUCTION_START(INVOKE_VIRTUAL) { bool success = DoInvoke(self, shadow_frame, inst, &result_register); + UPDATE_HANDLER_TABLE(); POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 3); } HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(INVOKE_VIRTUAL_RANGE) { bool success = DoInvoke(self, shadow_frame, inst, &result_register); + UPDATE_HANDLER_TABLE(); POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 3); } HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(INVOKE_SUPER) { bool success = DoInvoke(self, shadow_frame, inst, &result_register); + UPDATE_HANDLER_TABLE(); POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 3); } HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(INVOKE_SUPER_RANGE) { bool success = DoInvoke(self, shadow_frame, inst, &result_register); + UPDATE_HANDLER_TABLE(); POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 3); } HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(INVOKE_DIRECT) { bool success = DoInvoke(self, shadow_frame, inst, &result_register); + UPDATE_HANDLER_TABLE(); POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 3); } HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(INVOKE_DIRECT_RANGE) { bool success = DoInvoke(self, shadow_frame, inst, &result_register); + UPDATE_HANDLER_TABLE(); POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 3); } HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(INVOKE_INTERFACE) { bool success = DoInvoke(self, shadow_frame, inst, &result_register); + UPDATE_HANDLER_TABLE(); POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 3); } HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(INVOKE_INTERFACE_RANGE) { bool success = DoInvoke(self, shadow_frame, inst, &result_register); + UPDATE_HANDLER_TABLE(); POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 3); } HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(INVOKE_STATIC) { bool success = DoInvoke(self, shadow_frame, inst, &result_register); + UPDATE_HANDLER_TABLE(); POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 3); } HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(INVOKE_STATIC_RANGE) { bool success = DoInvoke(self, shadow_frame, inst, &result_register); + UPDATE_HANDLER_TABLE(); POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 3); } HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(INVOKE_VIRTUAL_QUICK) { bool success = DoInvokeVirtualQuick(self, shadow_frame, inst, &result_register); + UPDATE_HANDLER_TABLE(); POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 3); } HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(INVOKE_VIRTUAL_RANGE_QUICK) { bool success = DoInvokeVirtualQuick(self, shadow_frame, inst, &result_register); + UPDATE_HANDLER_TABLE(); POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 3); } HANDLE_INSTRUCTION_END(); From 423d2a3dcbb260b020efb5da59f784c9f02accbf Mon Sep 17 00:00:00 2001 From: Mathieu Chartier Date: Thu, 12 Sep 2013 17:33:56 -0700 Subject: [PATCH 0033/2402] Add support for changing roots through the root visitor callback. Needed for copying collectors. Change-Id: Icc4a342a57e0cfb79587edb02ef8c85e08808877 --- runtime/class_linker.cc | 17 ++-- runtime/class_linker_test.cc | 3 +- runtime/gc/collector/mark_sweep.cc | 32 +++--- runtime/gc/collector/mark_sweep.h | 8 +- runtime/gc/heap.cc | 8 +- runtime/hprof/hprof.cc | 8 +- runtime/indirect_reference_table.cc | 16 +-- runtime/indirect_reference_table.h | 12 +-- runtime/intern_table.cc | 8 +- runtime/jni_internal.cc | 6 +- runtime/mirror/string.cc | 4 + runtime/mirror/string.h | 1 + runtime/reference_table.cc | 10 +- runtime/reference_table.h | 6 +- runtime/root_visitor.h | 3 +- runtime/runtime.cc | 13 ++- runtime/sirt_ref.h | 2 +- runtime/stack.cc | 4 +- runtime/stack.h | 12 ++- runtime/thread.cc | 146 +++++++++++----------------- runtime/thread.h | 3 - runtime/thread_list.cc | 16 ++- runtime/throw_location.cc | 11 ++- 23 files changed, 175 insertions(+), 174 deletions(-) diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index 37b62ad7c6e..179fb1ac5e9 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -1089,26 +1089,31 @@ void ClassLinker::InitFromImage() { // reinit references to when reinitializing a ClassLinker from a // mapped image. void ClassLinker::VisitRoots(RootVisitor* visitor, void* arg, bool clean_dirty) { - visitor(class_roots_, arg); + class_roots_ = reinterpret_cast*>(visitor(class_roots_, arg)); + DCHECK(class_roots_ != nullptr); + Thread* self = Thread::Current(); { ReaderMutexLock mu(self, dex_lock_); - for (mirror::DexCache* dex_cache : dex_caches_) { - visitor(dex_cache, arg); + for (mirror::DexCache*& dex_cache : dex_caches_) { + dex_cache = reinterpret_cast(visitor(dex_cache, arg)); + DCHECK(dex_cache != nullptr); } } { ReaderMutexLock mu(self, *Locks::classlinker_classes_lock_); - for (const std::pair& it : class_table_) { - visitor(it.second, arg); + for (std::pair& it : class_table_) { + it.second = reinterpret_cast(visitor(it.second, arg)); + DCHECK(it.second != nullptr); } // We deliberately ignore the class roots in the image since we // handle image roots by using the MS/CMS rescanning of dirty cards. } - visitor(array_iftable_, arg); + array_iftable_ = reinterpret_cast(visitor(array_iftable_, arg)); + DCHECK(array_iftable_ != nullptr); if (clean_dirty) { is_dirty_ = false; } diff --git a/runtime/class_linker_test.cc b/runtime/class_linker_test.cc index 6442f5ad63a..192111fa3be 100644 --- a/runtime/class_linker_test.cc +++ b/runtime/class_linker_test.cc @@ -340,8 +340,9 @@ class ClassLinkerTest : public CommonTest { } } - static void TestRootVisitor(const mirror::Object* root, void*) { + static mirror::Object* TestRootVisitor(mirror::Object* root, void*) { EXPECT_TRUE(root != NULL); + return root; } }; diff --git a/runtime/gc/collector/mark_sweep.cc b/runtime/gc/collector/mark_sweep.cc index 953fbf98483..f724cdbf189 100644 --- a/runtime/gc/collector/mark_sweep.cc +++ b/runtime/gc/collector/mark_sweep.cc @@ -497,24 +497,18 @@ void MarkSweep::MarkRoot(const Object* obj) { } } -void MarkSweep::MarkRootParallelCallback(const Object* root, void* arg) { +Object* MarkSweep::MarkRootParallelCallback(Object* root, void* arg) { DCHECK(root != NULL); DCHECK(arg != NULL); reinterpret_cast(arg)->MarkObjectNonNullParallel(root); + return root; } -void MarkSweep::MarkObjectCallback(const Object* root, void* arg) { - DCHECK(root != NULL); - DCHECK(arg != NULL); - MarkSweep* mark_sweep = reinterpret_cast(arg); - mark_sweep->MarkObjectNonNull(root); -} - -void MarkSweep::ReMarkObjectVisitor(const Object* root, void* arg) { - DCHECK(root != NULL); - DCHECK(arg != NULL); - MarkSweep* mark_sweep = reinterpret_cast(arg); - mark_sweep->MarkObjectNonNull(root); +Object* MarkSweep::MarkRootCallback(Object* root, void* arg) { + DCHECK(root != nullptr); + DCHECK(arg != nullptr); + reinterpret_cast(arg)->MarkObjectNonNull(root); + return root; } void MarkSweep::VerifyRootCallback(const Object* root, void* arg, size_t vreg, @@ -542,20 +536,20 @@ void MarkSweep::VerifyRoots() { // Marks all objects in the root set. void MarkSweep::MarkRoots() { timings_.StartSplit("MarkRoots"); - Runtime::Current()->VisitNonConcurrentRoots(MarkObjectCallback, this); + Runtime::Current()->VisitNonConcurrentRoots(MarkRootCallback, this); timings_.EndSplit(); } void MarkSweep::MarkNonThreadRoots() { timings_.StartSplit("MarkNonThreadRoots"); - Runtime::Current()->VisitNonThreadRoots(MarkObjectCallback, this); + Runtime::Current()->VisitNonThreadRoots(MarkRootCallback, this); timings_.EndSplit(); } void MarkSweep::MarkConcurrentRoots() { timings_.StartSplit("MarkConcurrentRoots"); // Visit all runtime roots and clear dirty flags. - Runtime::Current()->VisitConcurrentRoots(MarkObjectCallback, this, false, true); + Runtime::Current()->VisitConcurrentRoots(MarkRootCallback, this, false, true); timings_.EndSplit(); } @@ -963,14 +957,14 @@ void MarkSweep::RecursiveMarkDirtyObjects(bool paused, byte minimum_age) { void MarkSweep::ReMarkRoots() { timings_.StartSplit("ReMarkRoots"); - Runtime::Current()->VisitRoots(ReMarkObjectVisitor, this, true, true); + Runtime::Current()->VisitRoots(MarkRootCallback, this, true, true); timings_.EndSplit(); } void MarkSweep::SweepJniWeakGlobals(IsMarkedTester is_marked, void* arg) { JavaVMExt* vm = Runtime::Current()->GetJavaVM(); WriterMutexLock mu(Thread::Current(), vm->weak_globals_lock); - for (const Object** entry : vm->weak_globals) { + for (Object** entry : vm->weak_globals) { if (!is_marked(*entry, arg)) { *entry = kClearedJniWeakGlobal; } @@ -1053,7 +1047,7 @@ void MarkSweep::VerifySystemWeaks() { JavaVMExt* vm = runtime->GetJavaVM(); ReaderMutexLock mu(Thread::Current(), vm->weak_globals_lock); - for (const Object** entry : vm->weak_globals) { + for (Object** entry : vm->weak_globals) { VerifyIsLive(*entry); } } diff --git a/runtime/gc/collector/mark_sweep.h b/runtime/gc/collector/mark_sweep.h index fdd0c867248..8b6ac154021 100644 --- a/runtime/gc/collector/mark_sweep.h +++ b/runtime/gc/collector/mark_sweep.h @@ -223,11 +223,11 @@ class MarkSweep : public GarbageCollector { SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_, Locks::mutator_lock_); - static void MarkObjectCallback(const mirror::Object* root, void* arg) + static mirror::Object* MarkRootCallback(mirror::Object* root, void* arg) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_); - static void MarkRootParallelCallback(const mirror::Object* root, void* arg); + static mirror::Object* MarkRootParallelCallback(mirror::Object* root, void* arg); // Marks an object. void MarkObject(const mirror::Object* obj) @@ -252,10 +252,6 @@ class MarkSweep : public GarbageCollector { static bool IsMarkedArrayCallback(const mirror::Object* object, void* arg) SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_); - static void ReMarkObjectVisitor(const mirror::Object* root, void* arg) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) - EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_); - static void VerifyImageRootVisitor(mirror::Object* root, void* arg) SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_, Locks::mutator_lock_); diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc index e0048a05f43..916d38e18b7 100644 --- a/runtime/gc/heap.cc +++ b/runtime/gc/heap.cc @@ -1322,11 +1322,12 @@ void Heap::UpdateAndMarkModUnion(collector::MarkSweep* mark_sweep, base::TimingL image_mod_union_table_->MarkReferences(mark_sweep); } -static void RootMatchesObjectVisitor(const mirror::Object* root, void* arg) { +static mirror::Object* RootMatchesObjectVisitor(mirror::Object* root, void* arg) { mirror::Object* obj = reinterpret_cast(arg); if (root == obj) { LOG(INFO) << "Object " << obj << " is a root"; } + return root; } class ScanVisitor { @@ -1414,9 +1415,10 @@ class VerifyReferenceVisitor { return heap_->IsLiveObjectLocked(obj); } - static void VerifyRoots(const mirror::Object* root, void* arg) { + static mirror::Object* VerifyRoots(mirror::Object* root, void* arg) { VerifyReferenceVisitor* visitor = reinterpret_cast(arg); - (*visitor)(NULL, root, MemberOffset(0), true); + (*visitor)(nullptr, root, MemberOffset(0), true); + return root; } private: diff --git a/runtime/hprof/hprof.cc b/runtime/hprof/hprof.cc index 0b2e7415276..67620a09f1b 100644 --- a/runtime/hprof/hprof.cc +++ b/runtime/hprof/hprof.cc @@ -484,11 +484,11 @@ class Hprof { } private: - static void RootVisitor(const mirror::Object* obj, void* arg) + static mirror::Object* RootVisitor(mirror::Object* obj, void* arg) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - CHECK(arg != NULL); - Hprof* hprof = reinterpret_cast(arg); - hprof->VisitRoot(obj); + DCHECK(arg != NULL); + reinterpret_cast(arg)->VisitRoot(obj); + return obj; } static void HeapBitmapCallback(mirror::Object* obj, void* arg) diff --git a/runtime/indirect_reference_table.cc b/runtime/indirect_reference_table.cc index 8af4d7eaab0..2bd83538ac0 100644 --- a/runtime/indirect_reference_table.cc +++ b/runtime/indirect_reference_table.cc @@ -40,7 +40,7 @@ IndirectReferenceTable::IndirectReferenceTable(size_t initialCount, CHECK_LE(initialCount, maxCount); CHECK_NE(desiredKind, kSirtOrInvalid); - table_ = reinterpret_cast(malloc(initialCount * sizeof(const mirror::Object*))); + table_ = reinterpret_cast(malloc(initialCount * sizeof(const mirror::Object*))); CHECK(table_ != NULL); memset(table_, 0xd1, initialCount * sizeof(const mirror::Object*)); @@ -75,7 +75,7 @@ bool IndirectReferenceTable::CheckEntry(const char* what, IndirectRef iref, int return true; } -IndirectRef IndirectReferenceTable::Add(uint32_t cookie, const mirror::Object* obj) { +IndirectRef IndirectReferenceTable::Add(uint32_t cookie, mirror::Object* obj) { IRTSegmentState prevState; prevState.all = cookie; size_t topIndex = segment_state_.parts.topIndex; @@ -101,7 +101,7 @@ IndirectRef IndirectReferenceTable::Add(uint32_t cookie, const mirror::Object* o } DCHECK_GT(newSize, alloc_entries_); - table_ = reinterpret_cast(realloc(table_, newSize * sizeof(const mirror::Object*))); + table_ = reinterpret_cast(realloc(table_, newSize * sizeof(mirror::Object*))); slot_data_ = reinterpret_cast(realloc(slot_data_, newSize * sizeof(IndirectRefSlot))); if (table_ == NULL || slot_data_ == NULL) { @@ -126,7 +126,7 @@ IndirectRef IndirectReferenceTable::Add(uint32_t cookie, const mirror::Object* o if (numHoles > 0) { DCHECK_GT(topIndex, 1U); // Find the first hole; likely to be near the end of the list. - const mirror::Object** pScan = &table_[topIndex - 1]; + mirror::Object** pScan = &table_[topIndex - 1]; DCHECK(*pScan != NULL); while (*--pScan != NULL) { DCHECK_GE(pScan, table_ + prevState.parts.topIndex); @@ -194,7 +194,8 @@ bool IndirectReferenceTable::GetChecked(IndirectRef iref) const { return true; } -static int Find(mirror::Object* direct_pointer, int bottomIndex, int topIndex, const mirror::Object** table) { +static int Find(mirror::Object* direct_pointer, int bottomIndex, int topIndex, + mirror::Object** table) { for (int i = bottomIndex; i < topIndex; ++i) { if (table[i] == direct_pointer) { return i; @@ -310,13 +311,14 @@ bool IndirectReferenceTable::Remove(uint32_t cookie, IndirectRef iref) { void IndirectReferenceTable::VisitRoots(RootVisitor* visitor, void* arg) { for (auto ref : *this) { - visitor(*ref, arg); + *ref = visitor(const_cast(*ref), arg); + DCHECK(*ref != nullptr); } } void IndirectReferenceTable::Dump(std::ostream& os) const { os << kind_ << " table dump:\n"; - std::vector entries(table_, table_ + Capacity()); + ReferenceTable::Table entries(table_, table_ + Capacity()); // Remove NULLs. for (int i = entries.size() - 1; i >= 0; --i) { if (entries[i] == NULL) { diff --git a/runtime/indirect_reference_table.h b/runtime/indirect_reference_table.h index 26f53db4ad8..51b238c527f 100644 --- a/runtime/indirect_reference_table.h +++ b/runtime/indirect_reference_table.h @@ -206,7 +206,7 @@ union IRTSegmentState { class IrtIterator { public: - explicit IrtIterator(const mirror::Object** table, size_t i, size_t capacity) + explicit IrtIterator(mirror::Object** table, size_t i, size_t capacity) : table_(table), i_(i), capacity_(capacity) { SkipNullsAndTombstones(); } @@ -217,7 +217,7 @@ class IrtIterator { return *this; } - const mirror::Object** operator*() { + mirror::Object** operator*() { return &table_[i_]; } @@ -233,7 +233,7 @@ class IrtIterator { } } - const mirror::Object** table_; + mirror::Object** table_; size_t i_; size_t capacity_; }; @@ -258,7 +258,7 @@ class IndirectReferenceTable { * Returns NULL if the table is full (max entries reached, or alloc * failed during expansion). */ - IndirectRef Add(uint32_t cookie, const mirror::Object* obj) + IndirectRef Add(uint32_t cookie, mirror::Object* obj) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); /* @@ -266,7 +266,7 @@ class IndirectReferenceTable { * * Returns kInvalidIndirectRefObject if iref is invalid. */ - const mirror::Object* Get(IndirectRef iref) const { + mirror::Object* Get(IndirectRef iref) const { if (!GetChecked(iref)) { return kInvalidIndirectRefObject; } @@ -363,7 +363,7 @@ class IndirectReferenceTable { IRTSegmentState segment_state_; /* bottom of the stack */ - const mirror::Object** table_; + mirror::Object** table_; /* bit mask, ORed into all irefs */ IndirectRefKind kind_; /* extended debugging info */ diff --git a/runtime/intern_table.cc b/runtime/intern_table.cc index e3a75cf3f9b..6b0a51bdd0f 100644 --- a/runtime/intern_table.cc +++ b/runtime/intern_table.cc @@ -44,14 +44,14 @@ void InternTable::DumpForSigQuit(std::ostream& os) const { void InternTable::VisitRoots(RootVisitor* visitor, void* arg, bool clean_dirty) { MutexLock mu(Thread::Current(), intern_table_lock_); - for (const auto& strong_intern : strong_interns_) { - visitor(strong_intern.second, arg); + for (auto& strong_intern : strong_interns_) { + strong_intern.second = reinterpret_cast(visitor(strong_intern.second, arg)); + DCHECK(strong_intern.second != nullptr); } if (clean_dirty) { is_dirty_ = false; } - // Note: we deliberately don't visit the weak_interns_ table and the immutable - // image roots. + // Note: we deliberately don't visit the weak_interns_ table and the immutable image roots. } mirror::String* InternTable::Lookup(Table& table, mirror::String* s, diff --git a/runtime/jni_internal.cc b/runtime/jni_internal.cc index d72ddf688d9..b4715998ec6 100644 --- a/runtime/jni_internal.cc +++ b/runtime/jni_internal.cc @@ -314,14 +314,14 @@ static jfieldID FindFieldID(const ScopedObjectAccess& soa, jclass jni_class, con return soa.EncodeField(field); } -static void PinPrimitiveArray(const ScopedObjectAccess& soa, const Array* array) +static void PinPrimitiveArray(const ScopedObjectAccess& soa, Array* array) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { JavaVMExt* vm = soa.Vm(); MutexLock mu(soa.Self(), vm->pins_lock); vm->pin_table.Add(array); } -static void UnpinPrimitiveArray(const ScopedObjectAccess& soa, const Array* array) +static void UnpinPrimitiveArray(const ScopedObjectAccess& soa, Array* array) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { JavaVMExt* vm = soa.Vm(); MutexLock mu(soa.Self(), vm->pins_lock); @@ -1997,7 +1997,7 @@ class JNI { CHECK_NON_NULL_ARGUMENT(GetStringUTFRegion, java_string); ScopedObjectAccess soa(env); String* s = soa.Decode(java_string); - const CharArray* chars = s->GetCharArray(); + CharArray* chars = s->GetCharArray(); PinPrimitiveArray(soa, chars); if (is_copy != NULL) { *is_copy = JNI_FALSE; diff --git a/runtime/mirror/string.cc b/runtime/mirror/string.cc index 7d968c774d5..b82683e5b36 100644 --- a/runtime/mirror/string.cc +++ b/runtime/mirror/string.cc @@ -33,6 +33,10 @@ const CharArray* String::GetCharArray() const { return GetFieldObject(ValueOffset(), false); } +CharArray* String::GetCharArray() { + return GetFieldObject(ValueOffset(), false); +} + void String::ComputeHashCode() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { SetHashCode(ComputeUtf16Hash(GetCharArray(), GetOffset(), GetLength())); } diff --git a/runtime/mirror/string.h b/runtime/mirror/string.h index bf545eaefb8..01d8f318ffc 100644 --- a/runtime/mirror/string.h +++ b/runtime/mirror/string.h @@ -44,6 +44,7 @@ class MANAGED String : public Object { } const CharArray* GetCharArray() const; + CharArray* GetCharArray(); int32_t GetOffset() const { int32_t result = GetField32(OffsetOffset(), false); diff --git a/runtime/reference_table.cc b/runtime/reference_table.cc index 8e23cbb153e..e95fdb92269 100644 --- a/runtime/reference_table.cc +++ b/runtime/reference_table.cc @@ -38,16 +38,16 @@ ReferenceTable::ReferenceTable(const char* name, size_t initial_size, size_t max ReferenceTable::~ReferenceTable() { } -void ReferenceTable::Add(const mirror::Object* obj) { +void ReferenceTable::Add(mirror::Object* obj) { DCHECK(obj != NULL); - if (entries_.size() == max_size_) { + if (entries_.size() >= max_size_) { LOG(FATAL) << "ReferenceTable '" << name_ << "' " << "overflowed (" << max_size_ << " entries)"; } entries_.push_back(obj); } -void ReferenceTable::Remove(const mirror::Object* obj) { +void ReferenceTable::Remove(mirror::Object* obj) { // We iterate backwards on the assumption that references are LIFO. for (int i = entries_.size() - 1; i >= 0; --i) { if (entries_[i] == obj) { @@ -232,8 +232,8 @@ void ReferenceTable::Dump(std::ostream& os, const Table& entries) { } void ReferenceTable::VisitRoots(RootVisitor* visitor, void* arg) { - for (const auto& ref : entries_) { - visitor(ref, arg); + for (auto& ref : entries_) { + ref = visitor(const_cast(ref), arg); } } diff --git a/runtime/reference_table.h b/runtime/reference_table.h index e369fd0de53..37b31723ae4 100644 --- a/runtime/reference_table.h +++ b/runtime/reference_table.h @@ -39,9 +39,9 @@ class ReferenceTable { ReferenceTable(const char* name, size_t initial_size, size_t max_size); ~ReferenceTable(); - void Add(const mirror::Object* obj); + void Add(mirror::Object* obj); - void Remove(const mirror::Object* obj); + void Remove(mirror::Object* obj); size_t Size() const; @@ -50,7 +50,7 @@ class ReferenceTable { void VisitRoots(RootVisitor* visitor, void* arg); private: - typedef std::vector Table; + typedef std::vector Table; static void Dump(std::ostream& os, const Table& entries) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); friend class IndirectReferenceTable; // For Dump. diff --git a/runtime/root_visitor.h b/runtime/root_visitor.h index 3aa9b4bac08..a2d898b43cb 100644 --- a/runtime/root_visitor.h +++ b/runtime/root_visitor.h @@ -23,7 +23,8 @@ class Object; } // namespace mirror class StackVisitor; -typedef void (RootVisitor)(const mirror::Object* root, void* arg); +typedef mirror::Object* (RootVisitor)(mirror::Object* root, void* arg) + __attribute__((warn_unused_result)); typedef void (VerifyRootVisitor)(const mirror::Object* root, void* arg, size_t vreg, const StackVisitor* visitor); typedef bool (IsMarkedTester)(const mirror::Object* object, void* arg); diff --git a/runtime/runtime.cc b/runtime/runtime.cc index 09cbd0bf6c3..477fcafc24b 100644 --- a/runtime/runtime.cc +++ b/runtime/runtime.cc @@ -1142,12 +1142,17 @@ void Runtime::VisitConcurrentRoots(RootVisitor* visitor, void* arg, bool only_di void Runtime::VisitNonThreadRoots(RootVisitor* visitor, void* arg) { java_vm_->VisitRoots(visitor, arg); - if (pre_allocated_OutOfMemoryError_ != NULL) { - visitor(pre_allocated_OutOfMemoryError_, arg); + if (pre_allocated_OutOfMemoryError_ != nullptr) { + pre_allocated_OutOfMemoryError_ = reinterpret_cast( + visitor(pre_allocated_OutOfMemoryError_, arg)); + DCHECK(pre_allocated_OutOfMemoryError_ != nullptr); } - visitor(resolution_method_, arg); + resolution_method_ = reinterpret_cast(visitor(resolution_method_, arg)); + DCHECK(resolution_method_ != nullptr); for (int i = 0; i < Runtime::kLastCalleeSaveType; i++) { - visitor(callee_save_methods_[i], arg); + callee_save_methods_[i] = reinterpret_cast( + visitor(callee_save_methods_[i], arg)); + DCHECK(callee_save_methods_[i] != nullptr); } } diff --git a/runtime/sirt_ref.h b/runtime/sirt_ref.h index 81f0dff217b..25d6fb3c11b 100644 --- a/runtime/sirt_ref.h +++ b/runtime/sirt_ref.h @@ -30,7 +30,7 @@ class SirtRef { self_->PushSirt(&sirt_); } ~SirtRef() { - CHECK(self_->PopSirt() == &sirt_); + CHECK_EQ(self_->PopSirt(), &sirt_); } T& operator*() const { return *get(); } diff --git a/runtime/stack.cc b/runtime/stack.cc index 206bff34258..17156647ebd 100644 --- a/runtime/stack.cc +++ b/runtime/stack.cc @@ -148,8 +148,8 @@ uint32_t StackVisitor::GetVReg(mirror::ArtMethod* m, uint16_t vreg, VRegKind kin const DexFile::CodeItem* code_item = MethodHelper(m).GetCodeItem(); DCHECK(code_item != NULL) << PrettyMethod(m); // Can't be NULL or how would we compile its instructions? size_t frame_size = m->GetFrameSizeInBytes(); - return GetVReg(cur_quick_frame_, code_item, m->GetCoreSpillMask(), m->GetFpSpillMask(), - frame_size, vreg); + return *GetVRegAddr(cur_quick_frame_, code_item, m->GetCoreSpillMask(), m->GetFpSpillMask(), + frame_size, vreg); } } else { return cur_shadow_frame_->GetVReg(vreg); diff --git a/runtime/stack.h b/runtime/stack.h index 8ecf8f05714..bd29cebc8b1 100644 --- a/runtime/stack.h +++ b/runtime/stack.h @@ -150,7 +150,12 @@ class ShadowFrame { mirror::Object* GetVRegReference(size_t i) const { DCHECK_LT(i, NumberOfVRegs()); if (HasReferenceArray()) { - return References()[i]; + mirror::Object* ref = References()[i]; + // If the vreg reference is not equal to the vreg then the vreg reference is stale. + if (reinterpret_cast(ref) != vregs_[i]) { + return nullptr; + } + return ref; } else { const uint32_t* vreg = &vregs_[i]; return *reinterpret_cast(vreg); @@ -459,13 +464,14 @@ class StackVisitor { uintptr_t GetGPR(uint32_t reg) const; void SetGPR(uint32_t reg, uintptr_t value); - uint32_t GetVReg(mirror::ArtMethod** cur_quick_frame, const DexFile::CodeItem* code_item, + // This is a fast-path for getting/setting values in a quick frame. + uint32_t* GetVRegAddr(mirror::ArtMethod** cur_quick_frame, const DexFile::CodeItem* code_item, uint32_t core_spills, uint32_t fp_spills, size_t frame_size, uint16_t vreg) const { int offset = GetVRegOffset(code_item, core_spills, fp_spills, frame_size, vreg); DCHECK_EQ(cur_quick_frame, GetCurrentQuickFrame()); byte* vreg_addr = reinterpret_cast(cur_quick_frame) + offset; - return *reinterpret_cast(vreg_addr); + return reinterpret_cast(vreg_addr); } uintptr_t GetReturnPc() const; diff --git a/runtime/thread.cc b/runtime/thread.cc index a4541953168..d7d4b1fa974 100644 --- a/runtime/thread.cc +++ b/runtime/thread.cc @@ -1012,9 +1012,10 @@ void Thread::AssertNoPendingException() const { } } -static void MonitorExitVisitor(const mirror::Object* object, void* arg) NO_THREAD_SAFETY_ANALYSIS { +static mirror::Object* MonitorExitVisitor(mirror::Object* object, void* arg) + NO_THREAD_SAFETY_ANALYSIS { Thread* self = reinterpret_cast(arg); - mirror::Object* entered_monitor = const_cast(object); + mirror::Object* entered_monitor = object; if (self->HoldsLock(entered_monitor)) { LOG(WARNING) << "Calling MonitorExit on object " << object << " (" << PrettyTypeOf(object) << ")" @@ -1022,6 +1023,7 @@ static void MonitorExitVisitor(const mirror::Object* object, void* arg) NO_THREA << *Thread::Current() << " which is detaching"; entered_monitor->MonitorExit(self); } + return object; } void Thread::Destroy() { @@ -1151,8 +1153,12 @@ void Thread::SirtVisitRoots(RootVisitor* visitor, void* arg) { size_t num_refs = cur->NumberOfReferences(); for (size_t j = 0; j < num_refs; j++) { mirror::Object* object = cur->GetReference(j); - if (object != NULL) { - visitor(object, arg); + if (object != nullptr) { + const mirror::Object* new_obj = visitor(object, arg); + DCHECK(new_obj != nullptr); + if (new_obj != object) { + cur->SetReference(j, const_cast(new_obj)); + } } } } @@ -2019,8 +2025,11 @@ class ReferenceMapVisitor : public StackVisitor { // SIRT for JNI or References for interpreter. for (size_t reg = 0; reg < num_regs; ++reg) { mirror::Object* ref = shadow_frame->GetVRegReference(reg); - if (ref != NULL) { - visitor_(ref, reg, this); + if (ref != nullptr) { + mirror::Object* new_ref = visitor_(ref, reg, this); + if (new_ref != ref) { + shadow_frame->SetVRegReference(reg, new_ref); + } } } } else { @@ -2040,8 +2049,11 @@ class ReferenceMapVisitor : public StackVisitor { for (size_t reg = 0; reg < num_regs; ++reg) { if (TestBitmap(reg, reg_bitmap)) { mirror::Object* ref = shadow_frame->GetVRegReference(reg); - if (ref != NULL) { - visitor_(ref, reg, this); + if (ref != nullptr) { + mirror::Object* new_ref = visitor_(ref, reg, this); + if (new_ref != ref) { + shadow_frame->SetVRegReference(reg, new_ref); + } } } } @@ -2072,19 +2084,25 @@ class ReferenceMapVisitor : public StackVisitor { // Does this register hold a reference? if (TestBitmap(reg, reg_bitmap)) { uint32_t vmap_offset; - mirror::Object* ref; if (vmap_table.IsInContext(reg, kReferenceVReg, &vmap_offset)) { - uintptr_t val = GetGPR(vmap_table.ComputeRegister(core_spills, vmap_offset, - kReferenceVReg)); - ref = reinterpret_cast(val); + int vmap_reg = vmap_table.ComputeRegister(core_spills, vmap_offset, kReferenceVReg); + mirror::Object* ref = reinterpret_cast(GetGPR(vmap_reg)); + if (ref != nullptr) { + mirror::Object* new_ref = visitor_(ref, reg, this); + if (ref != new_ref) { + SetGPR(vmap_reg, reinterpret_cast(new_ref)); + } + } } else { - ref = reinterpret_cast(GetVReg(cur_quick_frame, code_item, - core_spills, fp_spills, frame_size, - reg)); - } - - if (ref != NULL) { - visitor_(ref, reg, this); + uint32_t* reg_addr = + GetVRegAddr(cur_quick_frame, code_item, core_spills, fp_spills, frame_size, reg); + mirror::Object* ref = reinterpret_cast(*reg_addr); + if (ref != nullptr) { + mirror::Object* new_ref = visitor_(ref, reg, this); + if (ref != new_ref) { + *reg_addr = reinterpret_cast(new_ref); + } + } } } } @@ -2110,8 +2128,8 @@ class RootCallbackVisitor { public: RootCallbackVisitor(RootVisitor* visitor, void* arg) : visitor_(visitor), arg_(arg) {} - void operator()(const mirror::Object* obj, size_t, const StackVisitor*) const { - visitor_(obj, arg_); + mirror::Object* operator()(mirror::Object* obj, size_t, const StackVisitor*) const { + return visitor_(obj, arg_); } private: @@ -2135,67 +2153,17 @@ class VerifyCallbackVisitor { void* const arg_; }; -struct VerifyRootWrapperArg { - VerifyRootVisitor* visitor; - void* arg; -}; - -static void VerifyRootWrapperCallback(const mirror::Object* root, void* arg) { - VerifyRootWrapperArg* wrapperArg = reinterpret_cast(arg); - wrapperArg->visitor(root, wrapperArg->arg, 0, NULL); -} - -void Thread::VerifyRoots(VerifyRootVisitor* visitor, void* arg) { - // We need to map from a RootVisitor to VerifyRootVisitor, so pass in nulls for arguments we - // don't have. - VerifyRootWrapperArg wrapperArg; - wrapperArg.arg = arg; - wrapperArg.visitor = visitor; - - if (opeer_ != NULL) { - VerifyRootWrapperCallback(opeer_, &wrapperArg); - } - if (exception_ != NULL) { - VerifyRootWrapperCallback(exception_, &wrapperArg); - } - throw_location_.VisitRoots(VerifyRootWrapperCallback, &wrapperArg); - if (class_loader_override_ != NULL) { - VerifyRootWrapperCallback(class_loader_override_, &wrapperArg); - } - jni_env_->locals.VisitRoots(VerifyRootWrapperCallback, &wrapperArg); - jni_env_->monitors.VisitRoots(VerifyRootWrapperCallback, &wrapperArg); - - SirtVisitRoots(VerifyRootWrapperCallback, &wrapperArg); - - // Visit roots on this thread's stack - Context* context = GetLongJumpContext(); - VerifyCallbackVisitor visitorToCallback(visitor, arg); - ReferenceMapVisitor mapper(this, context, visitorToCallback); - mapper.WalkStack(); - ReleaseLongJumpContext(context); - - std::deque* instrumentation_stack = GetInstrumentationStack(); - typedef std::deque::const_iterator It; - for (It it = instrumentation_stack->begin(), end = instrumentation_stack->end(); it != end; ++it) { - mirror::Object* this_object = (*it).this_object_; - if (this_object != NULL) { - VerifyRootWrapperCallback(this_object, &wrapperArg); - } - mirror::ArtMethod* method = (*it).method_; - VerifyRootWrapperCallback(method, &wrapperArg); - } -} - void Thread::VisitRoots(RootVisitor* visitor, void* arg) { - if (opeer_ != NULL) { - visitor(opeer_, arg); + if (opeer_ != nullptr) { + opeer_ = visitor(opeer_, arg); } - if (exception_ != NULL) { - visitor(exception_, arg); + if (exception_ != nullptr) { + exception_ = reinterpret_cast(visitor(exception_, arg)); } throw_location_.VisitRoots(visitor, arg); - if (class_loader_override_ != NULL) { - visitor(class_loader_override_, arg); + if (class_loader_override_ != nullptr) { + class_loader_override_ = reinterpret_cast( + visitor(class_loader_override_, arg)); } jni_env_->locals.VisitRoots(visitor, arg); jni_env_->monitors.VisitRoots(visitor, arg); @@ -2209,24 +2177,26 @@ void Thread::VisitRoots(RootVisitor* visitor, void* arg) { mapper.WalkStack(); ReleaseLongJumpContext(context); - for (const instrumentation::InstrumentationStackFrame& frame : *GetInstrumentationStack()) { - mirror::Object* this_object = frame.this_object_; - if (this_object != NULL) { - visitor(this_object, arg); + for (instrumentation::InstrumentationStackFrame& frame : *GetInstrumentationStack()) { + if (frame.this_object_ != nullptr) { + frame.this_object_ = visitor(frame.this_object_, arg); + DCHECK(frame.this_object_ != nullptr); } - mirror::ArtMethod* method = frame.method_; - visitor(method, arg); + frame.method_ = reinterpret_cast(visitor(frame.method_, arg)); + DCHECK(frame.method_ != nullptr); } } -static void VerifyObject(const mirror::Object* root, void* arg) { - gc::Heap* heap = reinterpret_cast(arg); - heap->VerifyObject(root); +static mirror::Object* VerifyRoot(mirror::Object* root, void* arg) { + DCHECK(root != nullptr); + DCHECK(arg != nullptr); + reinterpret_cast(arg)->VerifyObject(root); + return root; } void Thread::VerifyStackImpl() { UniquePtr context(Context::Create()); - RootCallbackVisitor visitorToCallback(VerifyObject, Runtime::Current()->GetHeap()); + RootCallbackVisitor visitorToCallback(VerifyRoot, Runtime::Current()->GetHeap()); ReferenceMapVisitor mapper(this, context.get(), visitorToCallback); mapper.WalkStack(); } diff --git a/runtime/thread.h b/runtime/thread.h index f5f8f563fa8..dbf973619fa 100644 --- a/runtime/thread.h +++ b/runtime/thread.h @@ -395,9 +395,6 @@ class PACKED(4) Thread { void VisitRoots(RootVisitor* visitor, void* arg) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - void VerifyRoots(VerifyRootVisitor* visitor, void* arg) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - void VerifyStack() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); // diff --git a/runtime/thread_list.cc b/runtime/thread_list.cc index aba81feeff5..44cf8101782 100644 --- a/runtime/thread_list.cc +++ b/runtime/thread_list.cc @@ -569,10 +569,24 @@ void ThreadList::VisitRoots(RootVisitor* visitor, void* arg) const { } } +struct VerifyRootWrapperArg { + VerifyRootVisitor* visitor; + void* arg; +}; + +static mirror::Object* VerifyRootWrapperCallback(mirror::Object* root, void* arg) { + VerifyRootWrapperArg* wrapperArg = reinterpret_cast(arg); + wrapperArg->visitor(root, wrapperArg->arg, 0, NULL); + return root; +} + void ThreadList::VerifyRoots(VerifyRootVisitor* visitor, void* arg) const { + VerifyRootWrapperArg wrapper; + wrapper.visitor = visitor; + wrapper.arg = arg; MutexLock mu(Thread::Current(), *Locks::thread_list_lock_); for (const auto& thread : list_) { - thread->VerifyRoots(visitor, arg); + thread->VisitRoots(VerifyRootWrapperCallback, &wrapper); } } diff --git a/runtime/throw_location.cc b/runtime/throw_location.cc index e428511d484..01497ef0e50 100644 --- a/runtime/throw_location.cc +++ b/runtime/throw_location.cc @@ -34,11 +34,14 @@ std::string ThrowLocation::Dump() const { } void ThrowLocation::VisitRoots(RootVisitor* visitor, void* arg) { - if (this_object_ != NULL) { - visitor(this_object_, arg); + if (this_object_ != nullptr) { + this_object_ = const_cast(visitor(this_object_, arg)); + DCHECK(this_object_ != nullptr); } - if (method_ != NULL) { - visitor(method_, arg); + if (method_ != nullptr) { + method_ = const_cast( + reinterpret_cast(visitor(method_, arg))); + DCHECK(method_ != nullptr); } } From 0160d991571fedc4c1c252a8865c59a7c8cd4973 Mon Sep 17 00:00:00 2001 From: Tsu Chiang Chuang Date: Fri, 13 Sep 2013 14:17:42 -0700 Subject: [PATCH 0034/2402] Keep the right output file for tests. Bug: 10748067 Change-Id: I732fe23d53f27e7bb030d10c5e4955d9d78b4024 --- test/run-test | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/run-test b/test/run-test index 11dcfc57def..c449e84c35a 100755 --- a/test/run-test +++ b/test/run-test @@ -269,7 +269,7 @@ elif [ "$build_only" = "yes" ]; then fi fi # Clean up extraneous files that are not used by tests. - find $tmp_dir -mindepth 1 ! -regex ".*/\(.*jar\|$build_output\|$expected\)" | xargs rm -rf + find $tmp_dir -mindepth 1 ! -regex ".*/\(.*jar\|$output\|$expected\)" | xargs rm -rf exit 0 else "./${build}" >"$build_output" 2>&1 From d0a03b8099347dee6e4bab3af95e14cd5a03b29c Mon Sep 17 00:00:00 2001 From: buzbee Date: Sat, 14 Sep 2013 08:21:05 -0700 Subject: [PATCH 0035/2402] Timely color fix See b/10690000 For efficiency, the Quick compiler will not flush incoming register arguments to the frame if their underlying Dalvik virtual registers have been promoted to physical registers. In this case, though, there was a bug on Arm devices in that an incoming Double was promoted to physical floating point registers, but not in a usable form. The entry code generation saw that both halves of the double were promoted, but failed to check if it was in a form usable as a double. In this particular case, it meant that subsequent uses of the incoming argument referred to the uninitialized home location in the frame, resulting in garbage color values. That's the bug. Another problem is that the incoming double should never have been promoted to an unusable state in the first place - but that's merely an efficiency issue and will be addressed in another CL. Note: no good way to generate a regression test for this issue. The bug triggered because of an unusual sequence of events driving register promotion that can't easily (or robustly) be triggered from Java source. Change-Id: I7242422277193a04376461134dde71e9dec55576 --- compiler/dex/quick/gen_invoke.cc | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/compiler/dex/quick/gen_invoke.cc b/compiler/dex/quick/gen_invoke.cc index fa608183bea..72ae91eebb6 100644 --- a/compiler/dex/quick/gen_invoke.cc +++ b/compiler/dex/quick/gen_invoke.cc @@ -283,11 +283,28 @@ void Mir2Lir::FlushIns(RegLocation* ArgLocs, RegLocation rl_method) { need_flush = true; } - // For wide args, force flush if only half is promoted + // For wide args, force flush if not fully promoted if (t_loc->wide) { PromotionMap* p_map = v_map + (t_loc->high_word ? -1 : +1); + // Is only half promoted? need_flush |= (p_map->core_location != v_map->core_location) || (p_map->fp_location != v_map->fp_location); + if ((cu_->instruction_set == kThumb2) && t_loc->fp && !need_flush) { + /* + * In Arm, a double is represented as a pair of consecutive single float + * registers starting at an even number. It's possible that both Dalvik vRegs + * representing the incoming double were independently promoted as singles - but + * not in a form usable as a double. If so, we need to flush - even though the + * incoming arg appears fully in register. At this point in the code, both + * halves of the double are promoted. Make sure they are in a usable form. + */ + int lowreg_index = start_vreg + i + (t_loc->high_word ? -1 : 0); + int low_reg = promotion_map_[lowreg_index].FpReg; + int high_reg = promotion_map_[lowreg_index + 1].FpReg; + if (((low_reg & 0x1) != 0) || (high_reg != (low_reg + 1))) { + need_flush = true; + } + } } if (need_flush) { StoreBaseDisp(TargetReg(kSp), SRegOffset(start_vreg + i), From c729a6b936d59562bd9fb830a595d9ff65dfd129 Mon Sep 17 00:00:00 2001 From: buzbee Date: Sat, 14 Sep 2013 16:04:31 -0700 Subject: [PATCH 0036/2402] Improve promotion of double-precision regs Minor rework of the double allocation mechanism to more explicitly manage the allocation of preserved floating point single pairs as doubles. Change-Id: Id9db4b0e86e5ef54a5db587f367e00efdf7e98d6 --- compiler/dex/quick/mir_to_lir.h | 13 ++-- compiler/dex/quick/ralloc_util.cc | 102 +++++++++++++----------------- 2 files changed, 51 insertions(+), 64 deletions(-) diff --git a/compiler/dex/quick/mir_to_lir.h b/compiler/dex/quick/mir_to_lir.h index fdbc1d02b3d..401e3d5f93e 100644 --- a/compiler/dex/quick/mir_to_lir.h +++ b/compiler/dex/quick/mir_to_lir.h @@ -158,6 +158,10 @@ Mir2Lir* X86CodeGenerator(CompilationUnit* const cu, MIRGraph* const mir_graph, #define ENCODE_ALL (~0ULL) #define ENCODE_MEM (ENCODE_DALVIK_REG | ENCODE_LITERAL | \ ENCODE_HEAP_REF | ENCODE_MUST_NOT_ALIAS) + +// Mask to denote sreg as the start of a double. Must not interfere with low 16 bits. +#define STARTING_DOUBLE_SREG 0x10000 + // TODO: replace these macros #define SLOW_FIELD_PATH (cu_->enable_debug & (1 << kDebugSlowFieldPath)) #define SLOW_INVOKE_PATH (cu_->enable_debug & (1 << kDebugSlowInvokePath)) @@ -187,7 +191,6 @@ class Mir2Lir : public Backend { struct RefCounts { int count; int s_reg; - bool double_start; // Starting v_reg for a double }; /* @@ -324,11 +327,9 @@ class Mir2Lir : public Backend { void RecordCorePromotion(int reg, int s_reg); int AllocPreservedCoreReg(int s_reg); void RecordFpPromotion(int reg, int s_reg); - int AllocPreservedSingle(int s_reg, bool even); + int AllocPreservedSingle(int s_reg); int AllocPreservedDouble(int s_reg); - int AllocPreservedFPReg(int s_reg, bool double_start); - int AllocTempBody(RegisterInfo* p, int num_regs, int* next_temp, - bool required); + int AllocTempBody(RegisterInfo* p, int num_regs, int* next_temp, bool required); int AllocTempDouble(); int AllocFreeTemp(); int AllocTemp(); @@ -367,7 +368,7 @@ class Mir2Lir : public Backend { RegLocation UpdateRawLoc(RegLocation loc); RegLocation EvalLocWide(RegLocation loc, int reg_class, bool update); RegLocation EvalLoc(RegLocation loc, int reg_class, bool update); - void CountRefs(RefCounts* core_counts, RefCounts* fp_counts); + void CountRefs(RefCounts* core_counts, RefCounts* fp_counts, size_t num_regs); void DumpCounts(const RefCounts* arr, int size, const char* msg); void DoPromotion(); int VRegOffset(int v_reg); diff --git a/compiler/dex/quick/ralloc_util.cc b/compiler/dex/quick/ralloc_util.cc index a0f22fc576a..7927ff9864f 100644 --- a/compiler/dex/quick/ralloc_util.cc +++ b/compiler/dex/quick/ralloc_util.cc @@ -174,17 +174,12 @@ void Mir2Lir::RecordFpPromotion(int reg, int s_reg) { promotion_map_[p_map_idx].FpReg = reg; } -/* - * Reserve a callee-save fp single register. Try to fullfill request for - * even/odd allocation, but go ahead and allocate anything if not - * available. If nothing's available, return -1. - */ -int Mir2Lir::AllocPreservedSingle(int s_reg, bool even) { - int res = -1; +// Reserve a callee-save fp single register. +int Mir2Lir::AllocPreservedSingle(int s_reg) { + int res = -1; // Return code if none available. RegisterInfo* FPRegs = reg_pool_->FPRegs; for (int i = 0; i < reg_pool_->num_fp_regs; i++) { - if (!FPRegs[i].is_temp && !FPRegs[i].in_use && - ((FPRegs[i].reg & 0x1) == 0) == even) { + if (!FPRegs[i].is_temp && !FPRegs[i].in_use) { res = FPRegs[i].reg; RecordFpPromotion(res, s_reg); break; @@ -250,26 +245,6 @@ int Mir2Lir::AllocPreservedDouble(int s_reg) { return res; } - -/* - * Reserve a callee-save fp register. If this register can be used - * as the first of a double, attempt to allocate an even pair of fp - * single regs (but if can't still attempt to allocate a single, preferring - * first to allocate an odd register. - */ -int Mir2Lir::AllocPreservedFPReg(int s_reg, bool double_start) { - int res = -1; - if (double_start) { - res = AllocPreservedDouble(s_reg); - } - if (res == -1) { - res = AllocPreservedSingle(s_reg, false /* try odd # */); - } - if (res == -1) - res = AllocPreservedSingle(s_reg, true /* try even # */); - return res; -} - int Mir2Lir::AllocTempBody(RegisterInfo* p, int num_regs, int* next_temp, bool required) { int next = *next_temp; @@ -872,18 +847,22 @@ RegLocation Mir2Lir::EvalLoc(RegLocation loc, int reg_class, bool update) { } /* USE SSA names to count references of base Dalvik v_regs. */ -void Mir2Lir::CountRefs(RefCounts* core_counts, RefCounts* fp_counts) { +void Mir2Lir::CountRefs(RefCounts* core_counts, RefCounts* fp_counts, size_t num_regs) { for (int i = 0; i < mir_graph_->GetNumSSARegs(); i++) { RegLocation loc = mir_graph_->reg_location_[i]; RefCounts* counts = loc.fp ? fp_counts : core_counts; int p_map_idx = SRegToPMap(loc.s_reg_low); - // Don't count easily regenerated immediates - if (loc.fp || !IsInexpensiveConstant(loc)) { + if (loc.fp) { + if (loc.wide) { + // Treat doubles as a unit, using upper half of fp_counts array. + counts[p_map_idx + num_regs].count += mir_graph_->GetUseCount(i); + i++; + } else { + counts[p_map_idx].count += mir_graph_->GetUseCount(i); + } + } else if (!IsInexpensiveConstant(loc)) { counts[p_map_idx].count += mir_graph_->GetUseCount(i); } - if (loc.wide && loc.fp && !loc.high_word) { - counts[p_map_idx].double_start = true; - } } } @@ -902,7 +881,11 @@ static int SortCounts(const void *val1, const void *val2) { void Mir2Lir::DumpCounts(const RefCounts* arr, int size, const char* msg) { LOG(INFO) << msg; for (int i = 0; i < size; i++) { - LOG(INFO) << "s_reg[" << arr[i].s_reg << "]: " << arr[i].count; + if ((arr[i].s_reg & STARTING_DOUBLE_SREG) != 0) { + LOG(INFO) << "s_reg[D" << (arr[i].s_reg & ~STARTING_DOUBLE_SREG) << "]: " << arr[i].count; + } else { + LOG(INFO) << "s_reg[" << arr[i].s_reg << "]: " << arr[i].count; + } } } @@ -925,7 +908,7 @@ void Mir2Lir::DoPromotion() { * count based on original Dalvik register name. Count refs * separately based on type in order to give allocation * preference to fp doubles - which must be allocated sequential - * physical single fp registers started with an even-numbered + * physical single fp registers starting with an even-numbered * reg. * TUNING: replace with linear scan once we have the ability * to describe register live ranges for GC. @@ -934,7 +917,7 @@ void Mir2Lir::DoPromotion() { static_cast(arena_->Alloc(sizeof(RefCounts) * num_regs, ArenaAllocator::kAllocRegAlloc)); RefCounts *FpRegs = - static_cast(arena_->Alloc(sizeof(RefCounts) * num_regs, + static_cast(arena_->Alloc(sizeof(RefCounts) * num_regs * 2, ArenaAllocator::kAllocRegAlloc)); // Set ssa names for original Dalvik registers for (int i = 0; i < dalvik_regs; i++) { @@ -942,46 +925,49 @@ void Mir2Lir::DoPromotion() { } // Set ssa name for Method* core_regs[dalvik_regs].s_reg = mir_graph_->GetMethodSReg(); - FpRegs[dalvik_regs].s_reg = mir_graph_->GetMethodSReg(); // For consistecy + FpRegs[dalvik_regs].s_reg = mir_graph_->GetMethodSReg(); // For consistecy. + FpRegs[dalvik_regs + num_regs].s_reg = mir_graph_->GetMethodSReg(); // for consistency. // Set ssa names for compiler_temps for (int i = 1; i <= cu_->num_compiler_temps; i++) { CompilerTemp* ct = mir_graph_->compiler_temps_.Get(i); core_regs[dalvik_regs + i].s_reg = ct->s_reg; FpRegs[dalvik_regs + i].s_reg = ct->s_reg; + FpRegs[num_regs + dalvik_regs + i].s_reg = ct->s_reg; } - // Sum use counts of SSA regs by original Dalvik vreg. - CountRefs(core_regs, FpRegs); - - /* - * Ideally, we'd allocate doubles starting with an even-numbered - * register. Bias the counts to try to allocate any vreg that's - * used as the start of a pair first. - */ + // Duplicate in upper half to represent possible fp double starting sregs. for (int i = 0; i < num_regs; i++) { - if (FpRegs[i].double_start) { - FpRegs[i].count *= 2; - } + FpRegs[num_regs + i].s_reg = FpRegs[i].s_reg | STARTING_DOUBLE_SREG; } + // Sum use counts of SSA regs by original Dalvik vreg. + CountRefs(core_regs, FpRegs, num_regs); + + // Sort the count arrays qsort(core_regs, num_regs, sizeof(RefCounts), SortCounts); - qsort(FpRegs, num_regs, sizeof(RefCounts), SortCounts); + qsort(FpRegs, num_regs * 2, sizeof(RefCounts), SortCounts); if (cu_->verbose) { DumpCounts(core_regs, num_regs, "Core regs after sort"); - DumpCounts(FpRegs, num_regs, "Fp regs after sort"); + DumpCounts(FpRegs, num_regs * 2, "Fp regs after sort"); } if (!(cu_->disable_opt & (1 << kPromoteRegs))) { // Promote FpRegs - for (int i = 0; (i < num_regs) && (FpRegs[i].count >= promotion_threshold); i++) { - int p_map_idx = SRegToPMap(FpRegs[i].s_reg); - if (promotion_map_[p_map_idx].fp_location != kLocPhysReg) { - int reg = AllocPreservedFPReg(FpRegs[i].s_reg, - FpRegs[i].double_start); + for (int i = 0; (i < (num_regs * 2)) && (FpRegs[i].count >= promotion_threshold); i++) { + int p_map_idx = SRegToPMap(FpRegs[i].s_reg & ~STARTING_DOUBLE_SREG); + if ((FpRegs[i].s_reg & STARTING_DOUBLE_SREG) != 0) { + if ((promotion_map_[p_map_idx].fp_location != kLocPhysReg) && + (promotion_map_[p_map_idx + 1].fp_location != kLocPhysReg)) { + int low_sreg = FpRegs[i].s_reg & ~STARTING_DOUBLE_SREG; + // Ignore result - if can't alloc double may still be able to alloc singles. + AllocPreservedDouble(low_sreg); + } + } else if (promotion_map_[p_map_idx].fp_location != kLocPhysReg) { + int reg = AllocPreservedSingle(FpRegs[i].s_reg); if (reg < 0) { - break; // No more left + break; // No more left. } } } From 947ff080753c786a74f1cd7aeb09f717bb7074bd Mon Sep 17 00:00:00 2001 From: Sebastien Hertz Date: Tue, 17 Sep 2013 14:10:13 +0200 Subject: [PATCH 0037/2402] Load shadow frame's this object only upon instrumentation. We used to load the shadow frame's this object when entering the interpreter and push it into thread's SIRT to make it visible to garbage collector. But it is only used by instrumentation listeners. We now move this load at each place an instrumentation listener is called. This avoids loading it when instrumentation is disabled. This also allows to remove the SIRT reference and the push/pop management it implies when entering/exiting the interpreter. The performance improvement is 6% in average on our benchmarks suite. This CL also makes method Instrumentation::ExceptionCaughtEvent const so we can use a "const instrumentation::Instrumentation*" object in interpreter. Change-Id: I2caccba9a906f244c8057b24031250f9824cc711 --- runtime/instrumentation.cc | 2 +- runtime/instrumentation.h | 2 +- runtime/interpreter/interpreter_common.h | 12 +++---- .../interpreter_goto_table_impl.cc | 36 ++++++++----------- .../interpreter/interpreter_switch_impl.cc | 24 ++++++------- 5 files changed, 33 insertions(+), 43 deletions(-) diff --git a/runtime/instrumentation.cc b/runtime/instrumentation.cc index 3787e1c7594..481cbad3b84 100644 --- a/runtime/instrumentation.cc +++ b/runtime/instrumentation.cc @@ -468,7 +468,7 @@ void Instrumentation::DexPcMovedEventImpl(Thread* thread, mirror::Object* this_o void Instrumentation::ExceptionCaughtEvent(Thread* thread, const ThrowLocation& throw_location, mirror::ArtMethod* catch_method, uint32_t catch_dex_pc, - mirror::Throwable* exception_object) { + mirror::Throwable* exception_object) const { if (have_exception_caught_listeners_) { DCHECK_EQ(thread->GetException(NULL), exception_object); thread->ClearException(); diff --git a/runtime/instrumentation.h b/runtime/instrumentation.h index 6c80b41b642..28f95553f8e 100644 --- a/runtime/instrumentation.h +++ b/runtime/instrumentation.h @@ -186,7 +186,7 @@ class Instrumentation { // Inform listeners that an exception was caught. void ExceptionCaughtEvent(Thread* thread, const ThrowLocation& throw_location, mirror::ArtMethod* catch_method, uint32_t catch_dex_pc, - mirror::Throwable* exception_object) + mirror::Throwable* exception_object) const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); // Called when an instrumented method is entered. The intended link register (lr) is saved so diff --git a/runtime/interpreter/interpreter_common.h b/runtime/interpreter/interpreter_common.h index 8cd526a79ba..794891e501c 100644 --- a/runtime/interpreter/interpreter_common.h +++ b/runtime/interpreter/interpreter_common.h @@ -445,24 +445,24 @@ static inline int32_t DoSparseSwitch(const Instruction* inst, static inline uint32_t FindNextInstructionFollowingException(Thread* self, ShadowFrame& shadow_frame, uint32_t dex_pc, - SirtRef& this_object_ref, - instrumentation::Instrumentation* instrumentation) + mirror::Object* this_object, + const instrumentation::Instrumentation* instrumentation) ALWAYS_INLINE; static inline uint32_t FindNextInstructionFollowingException(Thread* self, ShadowFrame& shadow_frame, uint32_t dex_pc, - SirtRef& this_object_ref, - instrumentation::Instrumentation* instrumentation) + mirror::Object* this_object, + const instrumentation::Instrumentation* instrumentation) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { self->VerifyStack(); ThrowLocation throw_location; mirror::Throwable* exception = self->GetException(&throw_location); - bool clear_exception; + bool clear_exception = false; uint32_t found_dex_pc = shadow_frame.GetMethod()->FindCatchBlock(exception->GetClass(), dex_pc, &clear_exception); if (found_dex_pc == DexFile::kDexNoIndex) { - instrumentation->MethodUnwindEvent(self, this_object_ref.get(), + instrumentation->MethodUnwindEvent(self, this_object, shadow_frame.GetMethod(), dex_pc); } else { instrumentation->ExceptionCaughtEvent(self, throw_location, diff --git a/runtime/interpreter/interpreter_goto_table_impl.cc b/runtime/interpreter/interpreter_goto_table_impl.cc index 7f4c7c83022..4bef7704083 100644 --- a/runtime/interpreter/interpreter_goto_table_impl.cc +++ b/runtime/interpreter/interpreter_goto_table_impl.cc @@ -24,7 +24,6 @@ namespace interpreter { // - "inst" : the current Instruction*. // - "dex_pc": the current pc. // - "shadow_frame": the current shadow frame. -// - "insns": the start of current method's code item. // - "mh": the current MethodHelper. // - "currentHandlersTable": the current table of pointer to each instruction handler. @@ -78,22 +77,16 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* return JValue(); } self->VerifyStack(); - instrumentation::Instrumentation* const instrumentation = Runtime::Current()->GetInstrumentation(); - - // As the 'this' object won't change during the execution of current code, we - // want to cache it in local variables. Nevertheless, in order to let the - // garbage collector access it, we store it into sirt references. - SirtRef this_object_ref(self, shadow_frame.GetThisObject(code_item->ins_size_)); uint32_t dex_pc = shadow_frame.GetDexPC(); + const instrumentation::Instrumentation* const instrumentation = Runtime::Current()->GetInstrumentation(); if (LIKELY(dex_pc == 0)) { // We are entering the method as opposed to deoptimizing.. if (UNLIKELY(instrumentation->HasMethodEntryListeners())) { - instrumentation->MethodEnterEvent(self, this_object_ref.get(), + instrumentation->MethodEnterEvent(self, shadow_frame.GetThisObject(code_item->ins_size_), shadow_frame.GetMethod(), 0); } } - const uint16_t* const insns = code_item->insns_; - const Instruction* inst = Instruction::At(insns + dex_pc); + const Instruction* inst = Instruction::At(code_item->insns_ + dex_pc); // Define handlers table. static const void* handlersTable[kNumPackedOpcodes] = { @@ -212,7 +205,7 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* CheckSuspend(self); } if (UNLIKELY(instrumentation->HasMethodExitListeners())) { - instrumentation->MethodExitEvent(self, this_object_ref.get(), + instrumentation->MethodExitEvent(self, shadow_frame.GetThisObject(code_item->ins_size_), shadow_frame.GetMethod(), dex_pc, result); } @@ -227,7 +220,7 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* CheckSuspend(self); } if (UNLIKELY(instrumentation->HasMethodExitListeners())) { - instrumentation->MethodExitEvent(self, this_object_ref.get(), + instrumentation->MethodExitEvent(self, shadow_frame.GetThisObject(code_item->ins_size_), shadow_frame.GetMethod(), dex_pc, result); } @@ -243,7 +236,7 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* CheckSuspend(self); } if (UNLIKELY(instrumentation->HasMethodExitListeners())) { - instrumentation->MethodExitEvent(self, this_object_ref.get(), + instrumentation->MethodExitEvent(self, shadow_frame.GetThisObject(code_item->ins_size_), shadow_frame.GetMethod(), dex_pc, result); } @@ -258,7 +251,7 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* CheckSuspend(self); } if (UNLIKELY(instrumentation->HasMethodExitListeners())) { - instrumentation->MethodExitEvent(self, this_object_ref.get(), + instrumentation->MethodExitEvent(self, shadow_frame.GetThisObject(code_item->ins_size_), shadow_frame.GetMethod(), dex_pc, result); } @@ -274,7 +267,7 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* CheckSuspend(self); } if (UNLIKELY(instrumentation->HasMethodExitListeners())) { - instrumentation->MethodExitEvent(self, this_object_ref.get(), + instrumentation->MethodExitEvent(self, shadow_frame.GetThisObject(code_item->ins_size_), shadow_frame.GetMethod(), dex_pc, result); } @@ -2324,8 +2317,9 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* if (UNLIKELY(self->TestAllFlags())) { CheckSuspend(self); } + Object* this_object = shadow_frame.GetThisObject(code_item->ins_size_); uint32_t found_dex_pc = FindNextInstructionFollowingException(self, shadow_frame, dex_pc, - this_object_ref, + this_object, instrumentation); if (found_dex_pc == DexFile::kDexNoIndex) { return JValue(); /* Handled in caller. */ @@ -2336,11 +2330,11 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* } // Create alternative instruction handlers dedicated to instrumentation. -#define INSTRUMENTATION_INSTRUCTION_HANDLER(o, code, n, f, r, i, a, v) \ - instrumentation_op_##code: { \ - instrumentation->DexPcMovedEvent(self, this_object_ref.get(), \ - shadow_frame.GetMethod(), dex_pc); \ - goto *handlersTable[Instruction::code]; \ +#define INSTRUMENTATION_INSTRUCTION_HANDLER(o, code, n, f, r, i, a, v) \ + instrumentation_op_##code: { \ + instrumentation->DexPcMovedEvent(self, shadow_frame.GetThisObject(code_item->ins_size_), \ + shadow_frame.GetMethod(), dex_pc); \ + goto *handlersTable[Instruction::code]; \ } #include "dex_instruction_list.h" DEX_INSTRUCTION_LIST(INSTRUMENTATION_INSTRUCTION_HANDLER) diff --git a/runtime/interpreter/interpreter_switch_impl.cc b/runtime/interpreter/interpreter_switch_impl.cc index 5253e9dbbf4..01a0e4b4571 100644 --- a/runtime/interpreter/interpreter_switch_impl.cc +++ b/runtime/interpreter/interpreter_switch_impl.cc @@ -25,9 +25,10 @@ namespace interpreter { if (UNLIKELY(self->TestAllFlags())) { \ CheckSuspend(self); \ } \ + Object* this_object = shadow_frame.GetThisObject(code_item->ins_size_); \ uint32_t found_dex_pc = FindNextInstructionFollowingException(self, shadow_frame, \ inst->GetDexPc(insns), \ - this_object_ref, \ + this_object, \ instrumentation); \ if (found_dex_pc == DexFile::kDexNoIndex) { \ return JValue(); /* Handled in caller. */ \ @@ -57,17 +58,12 @@ static JValue ExecuteSwitchImpl(Thread* self, MethodHelper& mh, const DexFile::C return JValue(); } self->VerifyStack(); - instrumentation::Instrumentation* const instrumentation = Runtime::Current()->GetInstrumentation(); - - // As the 'this' object won't change during the execution of current code, we - // want to cache it in local variables. Nevertheless, in order to let the - // garbage collector access it, we store it into sirt references. - SirtRef this_object_ref(self, shadow_frame.GetThisObject(code_item->ins_size_)); uint32_t dex_pc = shadow_frame.GetDexPC(); + const instrumentation::Instrumentation* const instrumentation = Runtime::Current()->GetInstrumentation(); if (LIKELY(dex_pc == 0)) { // We are entering the method as opposed to deoptimizing.. if (UNLIKELY(instrumentation->HasMethodEntryListeners())) { - instrumentation->MethodEnterEvent(self, this_object_ref.get(), + instrumentation->MethodEnterEvent(self, shadow_frame.GetThisObject(code_item->ins_size_), shadow_frame.GetMethod(), 0); } } @@ -77,7 +73,7 @@ static JValue ExecuteSwitchImpl(Thread* self, MethodHelper& mh, const DexFile::C dex_pc = inst->GetDexPc(insns); shadow_frame.SetDexPC(dex_pc); if (UNLIKELY(instrumentation->HasDexPcListeners())) { - instrumentation->DexPcMovedEvent(self, this_object_ref.get(), + instrumentation->DexPcMovedEvent(self, shadow_frame.GetThisObject(code_item->ins_size_), shadow_frame.GetMethod(), dex_pc); } TraceExecution(shadow_frame, inst, dex_pc, mh); @@ -176,7 +172,7 @@ static JValue ExecuteSwitchImpl(Thread* self, MethodHelper& mh, const DexFile::C CheckSuspend(self); } if (UNLIKELY(instrumentation->HasMethodExitListeners())) { - instrumentation->MethodExitEvent(self, this_object_ref.get(), + instrumentation->MethodExitEvent(self, shadow_frame.GetThisObject(code_item->ins_size_), shadow_frame.GetMethod(), inst->GetDexPc(insns), result); } @@ -190,7 +186,7 @@ static JValue ExecuteSwitchImpl(Thread* self, MethodHelper& mh, const DexFile::C CheckSuspend(self); } if (UNLIKELY(instrumentation->HasMethodExitListeners())) { - instrumentation->MethodExitEvent(self, this_object_ref.get(), + instrumentation->MethodExitEvent(self, shadow_frame.GetThisObject(code_item->ins_size_), shadow_frame.GetMethod(), inst->GetDexPc(insns), result); } @@ -205,7 +201,7 @@ static JValue ExecuteSwitchImpl(Thread* self, MethodHelper& mh, const DexFile::C CheckSuspend(self); } if (UNLIKELY(instrumentation->HasMethodExitListeners())) { - instrumentation->MethodExitEvent(self, this_object_ref.get(), + instrumentation->MethodExitEvent(self, shadow_frame.GetThisObject(code_item->ins_size_), shadow_frame.GetMethod(), inst->GetDexPc(insns), result); } @@ -219,7 +215,7 @@ static JValue ExecuteSwitchImpl(Thread* self, MethodHelper& mh, const DexFile::C CheckSuspend(self); } if (UNLIKELY(instrumentation->HasMethodExitListeners())) { - instrumentation->MethodExitEvent(self, this_object_ref.get(), + instrumentation->MethodExitEvent(self, shadow_frame.GetThisObject(code_item->ins_size_), shadow_frame.GetMethod(), inst->GetDexPc(insns), result); } @@ -234,7 +230,7 @@ static JValue ExecuteSwitchImpl(Thread* self, MethodHelper& mh, const DexFile::C CheckSuspend(self); } if (UNLIKELY(instrumentation->HasMethodExitListeners())) { - instrumentation->MethodExitEvent(self, this_object_ref.get(), + instrumentation->MethodExitEvent(self, shadow_frame.GetThisObject(code_item->ins_size_), shadow_frame.GetMethod(), inst->GetDexPc(insns), result); } From 6aa3df965395566ed6a4fec4af37c2b7577992e9 Mon Sep 17 00:00:00 2001 From: Mathieu Chartier Date: Tue, 17 Sep 2013 15:17:28 -0700 Subject: [PATCH 0038/2402] Refactor system weak sweeping, add support for modification. Required for moving collectors. Change-Id: Ib97ba4a05af1139f8d388077a15e62bcb9534855 --- runtime/gc/collector/mark_sweep.cc | 54 ++++++++++-------------------- runtime/gc/collector/mark_sweep.h | 9 ++--- runtime/intern_table.cc | 8 +++-- runtime/intern_table.h | 3 +- runtime/intern_table_test.cc | 9 +++-- runtime/jni_internal.cc | 12 +++++++ runtime/jni_internal.h | 2 ++ runtime/monitor.cc | 14 ++++++-- runtime/monitor.h | 6 ++-- runtime/runtime.cc | 6 ++++ runtime/runtime.h | 4 +++ 11 files changed, 71 insertions(+), 56 deletions(-) diff --git a/runtime/gc/collector/mark_sweep.cc b/runtime/gc/collector/mark_sweep.cc index f724cdbf189..5d9db83918f 100644 --- a/runtime/gc/collector/mark_sweep.cc +++ b/runtime/gc/collector/mark_sweep.cc @@ -944,10 +944,12 @@ void MarkSweep::RecursiveMark() { ProcessMarkStack(false); } -bool MarkSweep::IsMarkedCallback(const Object* object, void* arg) { - return - reinterpret_cast(arg)->IsMarked(object) || - !reinterpret_cast(arg)->GetHeap()->GetLiveBitmap()->Test(object); +mirror::Object* MarkSweep::SystemWeakIsMarkedCallback(Object* object, void* arg) { + if (reinterpret_cast(arg)->IsMarked(object) || + !reinterpret_cast(arg)->GetHeap()->GetLiveBitmap()->Test(object)) { + return object; + } + return nullptr; } void MarkSweep::RecursiveMarkDirtyObjects(bool paused, byte minimum_age) { @@ -961,29 +963,22 @@ void MarkSweep::ReMarkRoots() { timings_.EndSplit(); } -void MarkSweep::SweepJniWeakGlobals(IsMarkedTester is_marked, void* arg) { - JavaVMExt* vm = Runtime::Current()->GetJavaVM(); - WriterMutexLock mu(Thread::Current(), vm->weak_globals_lock); - for (Object** entry : vm->weak_globals) { - if (!is_marked(*entry, arg)) { - *entry = kClearedJniWeakGlobal; - } - } -} - struct ArrayMarkedCheck { accounting::ObjectStack* live_stack; MarkSweep* mark_sweep; }; // Either marked or not live. -bool MarkSweep::IsMarkedArrayCallback(const Object* object, void* arg) { +mirror::Object* MarkSweep::SystemWeakIsMarkedArrayCallback(Object* object, void* arg) { ArrayMarkedCheck* array_check = reinterpret_cast(arg); if (array_check->mark_sweep->IsMarked(object)) { - return true; + return object; } accounting::ObjectStack* live_stack = array_check->live_stack; - return std::find(live_stack->Begin(), live_stack->End(), object) == live_stack->End(); + if (std::find(live_stack->Begin(), live_stack->End(), object) == live_stack->End()) { + return object; + } + return nullptr; } void MarkSweep::SweepSystemWeaksArray(accounting::ObjectStack* allocations) { @@ -993,14 +988,11 @@ void MarkSweep::SweepSystemWeaksArray(accounting::ObjectStack* allocations) { // !IsMarked && IsLive // So compute !(!IsMarked && IsLive) which is equal to (IsMarked || !IsLive). // Or for swapped (IsLive || !IsMarked). - timings_.StartSplit("SweepSystemWeaksArray"); ArrayMarkedCheck visitor; visitor.live_stack = allocations; visitor.mark_sweep = this; - runtime->GetInternTable()->SweepInternTableWeaks(IsMarkedArrayCallback, &visitor); - runtime->GetMonitorList()->SweepMonitorList(IsMarkedArrayCallback, &visitor); - SweepJniWeakGlobals(IsMarkedArrayCallback, &visitor); + runtime->SweepSystemWeaks(SystemWeakIsMarkedArrayCallback, &visitor); timings_.EndSplit(); } @@ -1012,16 +1004,14 @@ void MarkSweep::SweepSystemWeaks() { // So compute !(!IsMarked && IsLive) which is equal to (IsMarked || !IsLive). // Or for swapped (IsLive || !IsMarked). timings_.StartSplit("SweepSystemWeaks"); - runtime->GetInternTable()->SweepInternTableWeaks(IsMarkedCallback, this); - runtime->GetMonitorList()->SweepMonitorList(IsMarkedCallback, this); - SweepJniWeakGlobals(IsMarkedCallback, this); + runtime->SweepSystemWeaks(SystemWeakIsMarkedCallback, this); timings_.EndSplit(); } -bool MarkSweep::VerifyIsLiveCallback(const Object* obj, void* arg) { +mirror::Object* MarkSweep::VerifySystemWeakIsLiveCallback(Object* obj, void* arg) { reinterpret_cast(arg)->VerifyIsLive(obj); // We don't actually want to sweep the object, so lets return "marked" - return true; + return obj; } void MarkSweep::VerifyIsLive(const Object* obj) { @@ -1040,16 +1030,8 @@ void MarkSweep::VerifyIsLive(const Object* obj) { } void MarkSweep::VerifySystemWeaks() { - Runtime* runtime = Runtime::Current(); - // Verify system weaks, uses a special IsMarked callback which always returns true. - runtime->GetInternTable()->SweepInternTableWeaks(VerifyIsLiveCallback, this); - runtime->GetMonitorList()->SweepMonitorList(VerifyIsLiveCallback, this); - - JavaVMExt* vm = runtime->GetJavaVM(); - ReaderMutexLock mu(Thread::Current(), vm->weak_globals_lock); - for (Object** entry : vm->weak_globals) { - VerifyIsLive(*entry); - } + // Verify system weaks, uses a special object visitor which returns the input object. + Runtime::Current()->SweepSystemWeaks(VerifySystemWeakIsLiveCallback, this); } struct SweepCallbackContext { diff --git a/runtime/gc/collector/mark_sweep.h b/runtime/gc/collector/mark_sweep.h index 8b6ac154021..a857dab2321 100644 --- a/runtime/gc/collector/mark_sweep.h +++ b/runtime/gc/collector/mark_sweep.h @@ -208,7 +208,7 @@ class MarkSweep : public GarbageCollector { void SweepSystemWeaksArray(accounting::ObjectStack* allocations) SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_); - static bool VerifyIsLiveCallback(const mirror::Object* obj, void* arg) + static mirror::Object* VerifySystemWeakIsLiveCallback(mirror::Object* obj, void* arg) SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_); void VerifySystemWeaks() @@ -246,10 +246,10 @@ class MarkSweep : public GarbageCollector { // Returns true if the object has its bit set in the mark bitmap. bool IsMarked(const mirror::Object* object) const; - static bool IsMarkedCallback(const mirror::Object* object, void* arg) + static mirror::Object* SystemWeakIsMarkedCallback(mirror::Object* object, void* arg) SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_); - static bool IsMarkedArrayCallback(const mirror::Object* object, void* arg) + static mirror::Object* SystemWeakIsMarkedArrayCallback(mirror::Object* object, void* arg) SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_); static void VerifyImageRootVisitor(mirror::Object* root, void* arg) @@ -390,9 +390,6 @@ class MarkSweep : public GarbageCollector { EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - void SweepJniWeakGlobals(IsMarkedTester is_marked, void* arg) - SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_); - // Whether or not we count how many of each type of object were scanned. static const bool kCountScannedTypes = false; diff --git a/runtime/intern_table.cc b/runtime/intern_table.cc index 6b0a51bdd0f..cfed9aba863 100644 --- a/runtime/intern_table.cc +++ b/runtime/intern_table.cc @@ -194,14 +194,16 @@ bool InternTable::ContainsWeak(mirror::String* s) { return found == s; } -void InternTable::SweepInternTableWeaks(IsMarkedTester is_marked, void* arg) { +void InternTable::SweepInternTableWeaks(RootVisitor visitor, void* arg) { MutexLock mu(Thread::Current(), intern_table_lock_); - // TODO: std::remove_if + lambda. for (auto it = weak_interns_.begin(), end = weak_interns_.end(); it != end;) { mirror::Object* object = it->second; - if (!is_marked(object, arg)) { + mirror::Object* new_object = visitor(object, arg); + if (new_object == nullptr) { + // TODO: use it = weak_interns_.erase(it) when we get a c++11 stl. weak_interns_.erase(it++); } else { + it->second = down_cast(new_object); ++it; } } diff --git a/runtime/intern_table.h b/runtime/intern_table.h index a804d1f30e9..e5e19b71f5c 100644 --- a/runtime/intern_table.h +++ b/runtime/intern_table.h @@ -55,8 +55,7 @@ class InternTable { // Interns a potentially new string in the 'weak' table. (See above.) mirror::String* InternWeak(mirror::String* s) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - void SweepInternTableWeaks(IsMarkedTester is_marked, void* arg) - SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_); + void SweepInternTableWeaks(RootVisitor visitor, void* arg); bool ContainsWeak(mirror::String* s) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); diff --git a/runtime/intern_table_test.cc b/runtime/intern_table_test.cc index d79d2c4b311..aa2502d81ff 100644 --- a/runtime/intern_table_test.cc +++ b/runtime/intern_table_test.cc @@ -81,8 +81,11 @@ class TestPredicate { mutable std::vector expected_; }; -bool IsMarked(const mirror::Object* object, void* arg) { - return reinterpret_cast(arg)->IsMarked(object); +mirror::Object* IsMarkedSweepingVisitor(mirror::Object* object, void* arg) { + if (reinterpret_cast(arg)->IsMarked(object)) { + return object; + } + return nullptr; } TEST_F(InternTableTest, SweepInternTableWeaks) { @@ -105,7 +108,7 @@ TEST_F(InternTableTest, SweepInternTableWeaks) { p.Expect(s1.get()); { ReaderMutexLock mu(soa.Self(), *Locks::heap_bitmap_lock_); - t.SweepInternTableWeaks(IsMarked, &p); + t.SweepInternTableWeaks(IsMarkedSweepingVisitor, &p); } EXPECT_EQ(2U, t.Size()); diff --git a/runtime/jni_internal.cc b/runtime/jni_internal.cc index b4715998ec6..7f0fde48ca9 100644 --- a/runtime/jni_internal.cc +++ b/runtime/jni_internal.cc @@ -3217,6 +3217,18 @@ void* JavaVMExt::FindCodeForNativeMethod(ArtMethod* m) { return native_method; } +void JavaVMExt::SweepJniWeakGlobals(RootVisitor visitor, void* arg) { + WriterMutexLock mu(Thread::Current(), weak_globals_lock); + for (mirror::Object** entry : weak_globals) { + mirror::Object* obj = *entry; + mirror::Object* new_obj = visitor(obj, arg); + if (new_obj == nullptr) { + new_obj = kClearedJniWeakGlobal; + } + *entry = new_obj; + } +} + void JavaVMExt::VisitRoots(RootVisitor* visitor, void* arg) { Thread* self = Thread::Current(); { diff --git a/runtime/jni_internal.h b/runtime/jni_internal.h index bad3841c88b..2fcebf03dcb 100644 --- a/runtime/jni_internal.h +++ b/runtime/jni_internal.h @@ -89,6 +89,8 @@ struct JavaVMExt : public JavaVM { void SetCheckJniEnabled(bool enabled); + void SweepJniWeakGlobals(RootVisitor visitor, void* arg); + void VisitRoots(RootVisitor*, void*); Runtime* runtime; diff --git a/runtime/monitor.cc b/runtime/monitor.cc index 92e6541d470..570c2bef944 100644 --- a/runtime/monitor.cc +++ b/runtime/monitor.cc @@ -194,6 +194,10 @@ mirror::Object* Monitor::GetObject() { return obj_; } +void Monitor::SetObject(mirror::Object* object) { + obj_ = object; +} + void Monitor::Lock(Thread* self) { if (owner_ == self) { lock_count_++; @@ -1001,15 +1005,19 @@ void MonitorList::Add(Monitor* m) { list_.push_front(m); } -void MonitorList::SweepMonitorList(IsMarkedTester is_marked, void* arg) { +void MonitorList::SweepMonitorList(RootVisitor visitor, void* arg) { MutexLock mu(Thread::Current(), monitor_list_lock_); for (auto it = list_.begin(); it != list_.end(); ) { Monitor* m = *it; - if (!is_marked(m->GetObject(), arg)) { - VLOG(monitor) << "freeing monitor " << m << " belonging to unmarked object " << m->GetObject(); + mirror::Object* obj = m->GetObject(); + mirror::Object* new_obj = visitor(obj, arg); + if (new_obj == nullptr) { + VLOG(monitor) << "freeing monitor " << m << " belonging to unmarked object " + << m->GetObject(); delete m; it = list_.erase(it); } else { + m->SetObject(new_obj); ++it; } } diff --git a/runtime/monitor.h b/runtime/monitor.h index 66517683c2d..42493161f69 100644 --- a/runtime/monitor.h +++ b/runtime/monitor.h @@ -103,6 +103,7 @@ class Monitor { static bool IsValidLockWord(int32_t lock_word); mirror::Object* GetObject(); + void SetObject(mirror::Object* object); private: explicit Monitor(Thread* owner, mirror::Object* obj) @@ -159,7 +160,7 @@ class Monitor { int lock_count_ GUARDED_BY(monitor_lock_); // What object are we part of (for debugging). - mirror::Object* const obj_; + mirror::Object* obj_; // Threads currently waiting on this monitor. Thread* wait_set_ GUARDED_BY(monitor_lock_); @@ -183,8 +184,7 @@ class MonitorList { void Add(Monitor* m); - void SweepMonitorList(IsMarkedTester is_marked, void* arg) - SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_); + void SweepMonitorList(RootVisitor visitor, void* arg); private: Mutex monitor_list_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER; diff --git a/runtime/runtime.cc b/runtime/runtime.cc index 477fcafc24b..6cb4c49ddf9 100644 --- a/runtime/runtime.cc +++ b/runtime/runtime.cc @@ -319,6 +319,12 @@ size_t ParseIntegerOrDie(const std::string& s) { return result; } +void Runtime::SweepSystemWeaks(RootVisitor* visitor, void* arg) { + GetInternTable()->SweepInternTableWeaks(visitor, arg); + GetMonitorList()->SweepMonitorList(visitor, arg); + GetJavaVM()->SweepJniWeakGlobals(visitor, arg); +} + Runtime::ParsedOptions* Runtime::ParsedOptions::Create(const Options& options, bool ignore_unrecognized) { UniquePtr parsed(new ParsedOptions()); const char* boot_class_path_string = getenv("BOOTCLASSPATH"); diff --git a/runtime/runtime.h b/runtime/runtime.h index 21161a03b47..5acd5d7e159 100644 --- a/runtime/runtime.h +++ b/runtime/runtime.h @@ -317,6 +317,10 @@ class Runtime { void VisitNonConcurrentRoots(RootVisitor* visitor, void* arg) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + // Sweep system weaks, the system weak is deleted if the visitor return nullptr. Otherwise, the + // system weak is updated to be the visitor's returned value. + void SweepSystemWeaks(RootVisitor* visitor, void* arg); + // Returns a special method that calls into a trampoline for runtime method resolution mirror::ArtMethod* GetResolutionMethod() const { CHECK(HasResolutionMethod()); From 3b588e09eac6fb2aff64595e2232e479703850fc Mon Sep 17 00:00:00 2001 From: Sebastien Hertz Date: Wed, 11 Sep 2013 14:33:18 +0200 Subject: [PATCH 0039/2402] Optimize instruction data fetch in interpreter. The computed goto implementation prevents the compiler from detecting we are loading the first 16 bits of instruction twice: first one to get the opcode and second one to fetch first instruction's operand(s) like vA and vB. We now load the 16 bits into a local variable and decode opcode and operands from this variable. And do the same in the switch-based implementation for consistency. The performance improvement is 5% in average on benchmark applications suite. Also remove unused "Thread* self" parameter from DoIGetQuick and DoIPutQuick. Bug: 10703860 Change-Id: I83026ed6e78f642ac3dcdc6edbb6056fe012005f --- runtime/dex_instruction-inl.h | 104 ++-- runtime/dex_instruction.h | 178 ++++-- runtime/interpreter/interpreter_common.h | 50 +- .../interpreter_goto_table_impl.cc | 543 +++++++++--------- .../interpreter/interpreter_switch_impl.cc | 522 ++++++++--------- 5 files changed, 754 insertions(+), 643 deletions(-) diff --git a/runtime/dex_instruction-inl.h b/runtime/dex_instruction-inl.h index 6e212733586..4d390240135 100644 --- a/runtime/dex_instruction-inl.h +++ b/runtime/dex_instruction-inl.h @@ -24,29 +24,29 @@ namespace art { //------------------------------------------------------------------------------ // VRegA //------------------------------------------------------------------------------ -inline int8_t Instruction::VRegA_10t() const { +inline int8_t Instruction::VRegA_10t(uint16_t inst_data) const { DCHECK_EQ(FormatOf(Opcode()), k10t); - return static_cast(InstAA()); + return static_cast(InstAA(inst_data)); } -inline uint8_t Instruction::VRegA_10x() const { +inline uint8_t Instruction::VRegA_10x(uint16_t inst_data) const { DCHECK_EQ(FormatOf(Opcode()), k10x); - return InstAA(); + return InstAA(inst_data); } -inline uint4_t Instruction::VRegA_11n() const { +inline uint4_t Instruction::VRegA_11n(uint16_t inst_data) const { DCHECK_EQ(FormatOf(Opcode()), k11n); - return InstA(); + return InstA(inst_data); } -inline uint8_t Instruction::VRegA_11x() const { +inline uint8_t Instruction::VRegA_11x(uint16_t inst_data) const { DCHECK_EQ(FormatOf(Opcode()), k11x); - return InstAA(); + return InstAA(inst_data); } -inline uint4_t Instruction::VRegA_12x() const { +inline uint4_t Instruction::VRegA_12x(uint16_t inst_data) const { DCHECK_EQ(FormatOf(Opcode()), k12x); - return InstA(); + return InstA(inst_data); } inline int16_t Instruction::VRegA_20t() const { @@ -54,54 +54,54 @@ inline int16_t Instruction::VRegA_20t() const { return static_cast(Fetch16(1)); } -inline uint8_t Instruction::VRegA_21c() const { +inline uint8_t Instruction::VRegA_21c(uint16_t inst_data) const { DCHECK_EQ(FormatOf(Opcode()), k21c); - return InstAA(); + return InstAA(inst_data); } -inline uint8_t Instruction::VRegA_21h() const { +inline uint8_t Instruction::VRegA_21h(uint16_t inst_data) const { DCHECK_EQ(FormatOf(Opcode()), k21h); - return InstAA(); + return InstAA(inst_data); } -inline uint8_t Instruction::VRegA_21s() const { +inline uint8_t Instruction::VRegA_21s(uint16_t inst_data) const { DCHECK_EQ(FormatOf(Opcode()), k21s); - return InstAA(); + return InstAA(inst_data); } -inline uint8_t Instruction::VRegA_21t() const { +inline uint8_t Instruction::VRegA_21t(uint16_t inst_data) const { DCHECK_EQ(FormatOf(Opcode()), k21t); - return InstAA(); + return InstAA(inst_data); } -inline uint8_t Instruction::VRegA_22b() const { +inline uint8_t Instruction::VRegA_22b(uint16_t inst_data) const { DCHECK_EQ(FormatOf(Opcode()), k22b); - return InstAA(); + return InstAA(inst_data); } -inline uint4_t Instruction::VRegA_22c() const { +inline uint4_t Instruction::VRegA_22c(uint16_t inst_data) const { DCHECK_EQ(FormatOf(Opcode()), k22c); - return InstA(); + return InstA(inst_data); } -inline uint4_t Instruction::VRegA_22s() const { +inline uint4_t Instruction::VRegA_22s(uint16_t inst_data) const { DCHECK_EQ(FormatOf(Opcode()), k22s); - return InstA(); + return InstA(inst_data); } -inline uint4_t Instruction::VRegA_22t() const { +inline uint4_t Instruction::VRegA_22t(uint16_t inst_data) const { DCHECK_EQ(FormatOf(Opcode()), k22t); - return InstA(); + return InstA(inst_data); } -inline uint8_t Instruction::VRegA_22x() const { +inline uint8_t Instruction::VRegA_22x(uint16_t inst_data) const { DCHECK_EQ(FormatOf(Opcode()), k22x); - return InstAA(); + return InstAA(inst_data); } -inline uint8_t Instruction::VRegA_23x() const { +inline uint8_t Instruction::VRegA_23x(uint16_t inst_data) const { DCHECK_EQ(FormatOf(Opcode()), k23x); - return InstAA(); + return InstAA(inst_data); } inline int32_t Instruction::VRegA_30t() const { @@ -109,19 +109,19 @@ inline int32_t Instruction::VRegA_30t() const { return static_cast(Fetch32(1)); } -inline uint8_t Instruction::VRegA_31c() const { +inline uint8_t Instruction::VRegA_31c(uint16_t inst_data) const { DCHECK_EQ(FormatOf(Opcode()), k31c); - return InstAA(); + return InstAA(inst_data); } -inline uint8_t Instruction::VRegA_31i() const { +inline uint8_t Instruction::VRegA_31i(uint16_t inst_data) const { DCHECK_EQ(FormatOf(Opcode()), k31i); - return InstAA(); + return InstAA(inst_data); } -inline uint8_t Instruction::VRegA_31t() const { +inline uint8_t Instruction::VRegA_31t(uint16_t inst_data) const { DCHECK_EQ(FormatOf(Opcode()), k31t); - return InstAA(); + return InstAA(inst_data); } inline uint16_t Instruction::VRegA_32x() const { @@ -129,32 +129,32 @@ inline uint16_t Instruction::VRegA_32x() const { return Fetch16(1); } -inline uint4_t Instruction::VRegA_35c() const { +inline uint4_t Instruction::VRegA_35c(uint16_t inst_data) const { DCHECK_EQ(FormatOf(Opcode()), k35c); - return InstB(); // This is labeled A in the spec. + return InstB(inst_data); // This is labeled A in the spec. } -inline uint8_t Instruction::VRegA_3rc() const { +inline uint8_t Instruction::VRegA_3rc(uint16_t inst_data) const { DCHECK_EQ(FormatOf(Opcode()), k3rc); - return InstAA(); + return InstAA(inst_data); } -inline uint8_t Instruction::VRegA_51l() const { +inline uint8_t Instruction::VRegA_51l(uint16_t inst_data) const { DCHECK_EQ(FormatOf(Opcode()), k51l); - return InstAA(); + return InstAA(inst_data); } //------------------------------------------------------------------------------ // VRegB //------------------------------------------------------------------------------ -inline int4_t Instruction::VRegB_11n() const { +inline int4_t Instruction::VRegB_11n(uint16_t inst_data) const { DCHECK_EQ(FormatOf(Opcode()), k11n); - return static_cast((InstB() << 28) >> 28); + return static_cast((InstB(inst_data) << 28) >> 28); } -inline uint4_t Instruction::VRegB_12x() const { +inline uint4_t Instruction::VRegB_12x(uint16_t inst_data) const { DCHECK_EQ(FormatOf(Opcode()), k12x); - return InstB(); + return InstB(inst_data); } inline uint16_t Instruction::VRegB_21c() const { @@ -182,19 +182,19 @@ inline uint8_t Instruction::VRegB_22b() const { return static_cast(Fetch16(1) & 0xff); } -inline uint4_t Instruction::VRegB_22c() const { +inline uint4_t Instruction::VRegB_22c(uint16_t inst_data) const { DCHECK_EQ(FormatOf(Opcode()), k22c); - return InstB(); + return InstB(inst_data); } -inline uint4_t Instruction::VRegB_22s() const { +inline uint4_t Instruction::VRegB_22s(uint16_t inst_data) const { DCHECK_EQ(FormatOf(Opcode()), k22s); - return InstB(); + return InstB(inst_data); } -inline uint4_t Instruction::VRegB_22t() const { +inline uint4_t Instruction::VRegB_22t(uint16_t inst_data) const { DCHECK_EQ(FormatOf(Opcode()), k22t); - return InstB(); + return InstB(inst_data); } inline uint16_t Instruction::VRegB_22x() const { diff --git a/runtime/dex_instruction.h b/runtime/dex_instruction.h index 13b0f1c270e..e8db3bcf4f6 100644 --- a/runtime/dex_instruction.h +++ b/runtime/dex_instruction.h @@ -217,44 +217,122 @@ class Instruction { // VRegA bool HasVRegA() const; int32_t VRegA() const; - int8_t VRegA_10t() const; - uint8_t VRegA_10x() const; - uint4_t VRegA_11n() const; - uint8_t VRegA_11x() const; - uint4_t VRegA_12x() const; + + int8_t VRegA_10t() const { + return VRegA_10t(Fetch16(0)); + } + uint8_t VRegA_10x() const { + return VRegA_10x(Fetch16(0)); + } + uint4_t VRegA_11n() const { + return VRegA_11n(Fetch16(0)); + } + uint8_t VRegA_11x() const { + return VRegA_11x(Fetch16(0)); + } + uint4_t VRegA_12x() const { + return VRegA_12x(Fetch16(0)); + } int16_t VRegA_20t() const; - uint8_t VRegA_21c() const; - uint8_t VRegA_21h() const; - uint8_t VRegA_21s() const; - uint8_t VRegA_21t() const; - uint8_t VRegA_22b() const; - uint4_t VRegA_22c() const; - uint4_t VRegA_22s() const; - uint4_t VRegA_22t() const; - uint8_t VRegA_22x() const; - uint8_t VRegA_23x() const; + uint8_t VRegA_21c() const { + return VRegA_21c(Fetch16(0)); + } + uint8_t VRegA_21h() const { + return VRegA_21h(Fetch16(0)); + } + uint8_t VRegA_21s() const { + return VRegA_21s(Fetch16(0)); + } + uint8_t VRegA_21t() const { + return VRegA_21t(Fetch16(0)); + } + uint8_t VRegA_22b() const { + return VRegA_22b(Fetch16(0)); + } + uint4_t VRegA_22c() const { + return VRegA_22c(Fetch16(0)); + } + uint4_t VRegA_22s() const { + return VRegA_22s(Fetch16(0)); + } + uint4_t VRegA_22t() const { + return VRegA_22t(Fetch16(0)); + } + uint8_t VRegA_22x() const { + return VRegA_22x(Fetch16(0)); + } + uint8_t VRegA_23x() const { + return VRegA_23x(Fetch16(0)); + } int32_t VRegA_30t() const; - uint8_t VRegA_31c() const; - uint8_t VRegA_31i() const; - uint8_t VRegA_31t() const; + uint8_t VRegA_31c() const { + return VRegA_31c(Fetch16(0)); + } + uint8_t VRegA_31i() const { + return VRegA_31i(Fetch16(0)); + } + uint8_t VRegA_31t() const { + return VRegA_31t(Fetch16(0)); + } uint16_t VRegA_32x() const; - uint4_t VRegA_35c() const; - uint8_t VRegA_3rc() const; - uint8_t VRegA_51l() const; + uint4_t VRegA_35c() const { + return VRegA_35c(Fetch16(0)); + } + uint8_t VRegA_3rc() const { + return VRegA_3rc(Fetch16(0)); + } + uint8_t VRegA_51l() const { + return VRegA_51l(Fetch16(0)); + } + + // The following methods return the vA operand for various instruction formats. The "inst_data" + // parameter holds the first 16 bits of instruction which the returned value is decoded from. + int8_t VRegA_10t(uint16_t inst_data) const; + uint8_t VRegA_10x(uint16_t inst_data) const; + uint4_t VRegA_11n(uint16_t inst_data) const; + uint8_t VRegA_11x(uint16_t inst_data) const; + uint4_t VRegA_12x(uint16_t inst_data) const; + uint8_t VRegA_21c(uint16_t inst_data) const; + uint8_t VRegA_21h(uint16_t inst_data) const; + uint8_t VRegA_21s(uint16_t inst_data) const; + uint8_t VRegA_21t(uint16_t inst_data) const; + uint8_t VRegA_22b(uint16_t inst_data) const; + uint4_t VRegA_22c(uint16_t inst_data) const; + uint4_t VRegA_22s(uint16_t inst_data) const; + uint4_t VRegA_22t(uint16_t inst_data) const; + uint8_t VRegA_22x(uint16_t inst_data) const; + uint8_t VRegA_23x(uint16_t inst_data) const; + uint8_t VRegA_31c(uint16_t inst_data) const; + uint8_t VRegA_31i(uint16_t inst_data) const; + uint8_t VRegA_31t(uint16_t inst_data) const; + uint4_t VRegA_35c(uint16_t inst_data) const; + uint8_t VRegA_3rc(uint16_t inst_data) const; + uint8_t VRegA_51l(uint16_t inst_data) const; // VRegB bool HasVRegB() const; int32_t VRegB() const; - int4_t VRegB_11n() const; - uint4_t VRegB_12x() const; + + int4_t VRegB_11n() const { + return VRegB_11n(Fetch16(0)); + } + uint4_t VRegB_12x() const { + return VRegB_12x(Fetch16(0)); + } uint16_t VRegB_21c() const; uint16_t VRegB_21h() const; int16_t VRegB_21s() const; int16_t VRegB_21t() const; uint8_t VRegB_22b() const; - uint4_t VRegB_22c() const; - uint4_t VRegB_22s() const; - uint4_t VRegB_22t() const; + uint4_t VRegB_22c() const { + return VRegB_22c(Fetch16(0)); + } + uint4_t VRegB_22s() const { + return VRegB_22s(Fetch16(0)); + } + uint4_t VRegB_22t() const { + return VRegB_22t(Fetch16(0)); + } uint16_t VRegB_22x() const; uint8_t VRegB_23x() const; uint32_t VRegB_31c() const; @@ -265,9 +343,19 @@ class Instruction { uint16_t VRegB_3rc() const; uint64_t VRegB_51l() const; // vB_wide + // The following methods return the vB operand for all instruction formats where it is encoded in + // the first 16 bits of instruction. The "inst_data" parameter holds these 16 bits. The returned + // value is decoded from it. + int4_t VRegB_11n(uint16_t inst_data) const; + uint4_t VRegB_12x(uint16_t inst_data) const; + uint4_t VRegB_22c(uint16_t inst_data) const; + uint4_t VRegB_22s(uint16_t inst_data) const; + uint4_t VRegB_22t(uint16_t inst_data) const; + // VRegC bool HasVRegC() const; int32_t VRegC() const; + int8_t VRegC_22b() const; uint16_t VRegC_22c() const; int16_t VRegC_22s() const; @@ -279,9 +367,16 @@ class Instruction { // Fills the given array with the 'arg' array of the instruction. void GetArgs(uint32_t args[5]) const; - // Returns the opcode field of the instruction. + // Returns the opcode field of the instruction. The given "inst_data" parameter must be the first + // 16 bits of instruction. + Code Opcode(uint16_t inst_data) const { + DCHECK_EQ(inst_data, Fetch16(0)); + return static_cast(inst_data & 0xFF); + } + + // Returns the opcode field of the instruction from the first 16 bits of instruction. Code Opcode() const { - return static_cast(Fetch16(0) & 0xFF); + return Opcode(Fetch16(0)); } void SetOpcode(Code opcode) { @@ -395,28 +490,43 @@ class Instruction { // Dump code_units worth of this instruction, padding to code_units for shorter instructions std::string DumpHex(size_t code_units) const; - private: - size_t SizeInCodeUnitsComplexOpcode() const; - uint16_t Fetch16(size_t offset) const { const uint16_t* insns = reinterpret_cast(this); return insns[offset]; } + private: + size_t SizeInCodeUnitsComplexOpcode() const; + uint32_t Fetch32(size_t offset) const { return (Fetch16(offset) | ((uint32_t) Fetch16(offset + 1) << 16)); } uint4_t InstA() const { - return static_cast((Fetch16(0) >> 8) & 0x0f); + return InstA(Fetch16(0)); } uint4_t InstB() const { - return static_cast(Fetch16(0) >> 12); + return InstB(Fetch16(0)); } uint8_t InstAA() const { - return static_cast(Fetch16(0) >> 8); + return InstAA(Fetch16(0)); + } + + uint4_t InstA(uint16_t inst_data) const { + DCHECK_EQ(inst_data, Fetch16(0)); + return static_cast((inst_data >> 8) & 0x0f); + } + + uint4_t InstB(uint16_t inst_data) const { + DCHECK_EQ(inst_data, Fetch16(0)); + return static_cast(inst_data >> 12); + } + + uint8_t InstAA(uint16_t inst_data) const { + DCHECK_EQ(inst_data, Fetch16(0)); + return static_cast(inst_data >> 8); } static const char* const kInstructionNames[]; diff --git a/runtime/interpreter/interpreter_common.h b/runtime/interpreter/interpreter_common.h index 794891e501c..ec1f9426bf9 100644 --- a/runtime/interpreter/interpreter_common.h +++ b/runtime/interpreter/interpreter_common.h @@ -123,12 +123,12 @@ bool DoInvokeVirtualQuick(Thread* self, ShadowFrame& shadow_frame, // specialization. template static bool DoFieldGet(Thread* self, ShadowFrame& shadow_frame, - const Instruction* inst) + const Instruction* inst, uint16_t inst_data) NO_THREAD_SAFETY_ANALYSIS ALWAYS_INLINE; template static inline bool DoFieldGet(Thread* self, ShadowFrame& shadow_frame, - const Instruction* inst) { + const Instruction* inst, uint16_t inst_data) { bool is_static = (find_type == StaticObjectRead) || (find_type == StaticPrimitiveRead); uint32_t field_idx = is_static ? inst->VRegB_21c() : inst->VRegC_22c(); ArtField* f = FindFieldFromCode(field_idx, shadow_frame.GetMethod(), self, @@ -142,13 +142,13 @@ static inline bool DoFieldGet(Thread* self, ShadowFrame& shadow_frame, if (is_static) { obj = f->GetDeclaringClass(); } else { - obj = shadow_frame.GetVRegReference(inst->VRegB_22c()); + obj = shadow_frame.GetVRegReference(inst->VRegB_22c(inst_data)); if (UNLIKELY(obj == NULL)) { ThrowNullPointerExceptionForFieldAccess(shadow_frame.GetCurrentLocationForThrow(), f, true); return false; } } - uint32_t vregA = is_static ? inst->VRegA_21c() : inst->VRegA_22c(); + uint32_t vregA = is_static ? inst->VRegA_21c(inst_data) : inst->VRegA_22c(inst_data); switch (field_type) { case Primitive::kPrimBoolean: shadow_frame.SetVReg(vregA, f->GetBoolean(obj)); @@ -180,14 +180,12 @@ static inline bool DoFieldGet(Thread* self, ShadowFrame& shadow_frame, // TODO: should be SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) which is failing due to template // specialization. template -static bool DoIGetQuick(Thread* self, ShadowFrame& shadow_frame, - const Instruction* inst) +static bool DoIGetQuick(ShadowFrame& shadow_frame, const Instruction* inst, uint16_t inst_data) NO_THREAD_SAFETY_ANALYSIS ALWAYS_INLINE; template -static inline bool DoIGetQuick(Thread* self, ShadowFrame& shadow_frame, - const Instruction* inst) { - Object* obj = shadow_frame.GetVRegReference(inst->VRegB_22c()); +static inline bool DoIGetQuick(ShadowFrame& shadow_frame, const Instruction* inst, uint16_t inst_data) { + Object* obj = shadow_frame.GetVRegReference(inst->VRegB_22c(inst_data)); if (UNLIKELY(obj == NULL)) { // We lost the reference to the field index so we cannot get a more // precised exception message. @@ -196,7 +194,7 @@ static inline bool DoIGetQuick(Thread* self, ShadowFrame& shadow_frame, } MemberOffset field_offset(inst->VRegC_22c()); const bool is_volatile = false; // iget-x-quick only on non volatile fields. - const uint32_t vregA = inst->VRegA_22c(); + const uint32_t vregA = inst->VRegA_22c(inst_data); switch (field_type) { case Primitive::kPrimInt: shadow_frame.SetVReg(vregA, static_cast(obj->GetField32(field_offset, is_volatile))); @@ -217,12 +215,12 @@ static inline bool DoIGetQuick(Thread* self, ShadowFrame& shadow_frame, // specialization. template static bool DoFieldPut(Thread* self, const ShadowFrame& shadow_frame, - const Instruction* inst) + const Instruction* inst, uint16_t inst_data) NO_THREAD_SAFETY_ANALYSIS ALWAYS_INLINE; template static inline bool DoFieldPut(Thread* self, const ShadowFrame& shadow_frame, - const Instruction* inst) { + const Instruction* inst, uint16_t inst_data) { bool is_static = (find_type == StaticObjectWrite) || (find_type == StaticPrimitiveWrite); uint32_t field_idx = is_static ? inst->VRegB_21c() : inst->VRegC_22c(); ArtField* f = FindFieldFromCode(field_idx, shadow_frame.GetMethod(), self, @@ -236,14 +234,14 @@ static inline bool DoFieldPut(Thread* self, const ShadowFrame& shadow_frame, if (is_static) { obj = f->GetDeclaringClass(); } else { - obj = shadow_frame.GetVRegReference(inst->VRegB_22c()); + obj = shadow_frame.GetVRegReference(inst->VRegB_22c(inst_data)); if (UNLIKELY(obj == NULL)) { ThrowNullPointerExceptionForFieldAccess(shadow_frame.GetCurrentLocationForThrow(), f, false); return false; } } - uint32_t vregA = is_static ? inst->VRegA_21c() : inst->VRegA_22c(); + uint32_t vregA = is_static ? inst->VRegA_21c(inst_data) : inst->VRegA_22c(inst_data); switch (field_type) { case Primitive::kPrimBoolean: f->SetBoolean(obj, shadow_frame.GetVReg(vregA)); @@ -275,14 +273,12 @@ static inline bool DoFieldPut(Thread* self, const ShadowFrame& shadow_frame, // TODO: should be SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) which is failing due to template // specialization. template -static bool DoIPutQuick(Thread* self, ShadowFrame& shadow_frame, - const Instruction* inst) +static bool DoIPutQuick(ShadowFrame& shadow_frame, const Instruction* inst, uint16_t inst_data) NO_THREAD_SAFETY_ANALYSIS ALWAYS_INLINE; template -static inline bool DoIPutQuick(Thread* self, ShadowFrame& shadow_frame, - const Instruction* inst) { - Object* obj = shadow_frame.GetVRegReference(inst->VRegB_22c()); +static inline bool DoIPutQuick(ShadowFrame& shadow_frame, const Instruction* inst, uint16_t inst_data) { + Object* obj = shadow_frame.GetVRegReference(inst->VRegB_22c(inst_data)); if (UNLIKELY(obj == NULL)) { // We lost the reference to the field index so we cannot get a more // precised exception message. @@ -291,7 +287,7 @@ static inline bool DoIPutQuick(Thread* self, ShadowFrame& shadow_frame, } MemberOffset field_offset(inst->VRegC_22c()); const bool is_volatile = false; // iput-x-quick only on non volatile fields. - const uint32_t vregA = inst->VRegA_22c(); + const uint32_t vregA = inst->VRegA_22c(inst_data); switch (field_type) { case Primitive::kPrimInt: obj->SetField32(field_offset, shadow_frame.GetVReg(vregA), is_volatile); @@ -387,14 +383,14 @@ static inline bool DoLongRemainder(ShadowFrame& shadow_frame, size_t result_reg, // Returns true on success, otherwise throws an exception and returns false. template bool DoFilledNewArray(const Instruction* inst, const ShadowFrame& shadow_frame, - Thread* self, JValue* result) NO_THREAD_SAFETY_ANALYSIS; + Thread* self, JValue* result) NO_THREAD_SAFETY_ANALYSIS; -static inline int32_t DoPackedSwitch(const Instruction* inst, - const ShadowFrame& shadow_frame) +static inline int32_t DoPackedSwitch(const Instruction* inst, const ShadowFrame& shadow_frame, + uint16_t inst_data) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { DCHECK(inst->Opcode() == Instruction::PACKED_SWITCH); const uint16_t* switch_data = reinterpret_cast(inst) + inst->VRegB_31t(); - int32_t test_val = shadow_frame.GetVReg(inst->VRegA_31t()); + int32_t test_val = shadow_frame.GetVReg(inst->VRegA_31t(inst_data)); DCHECK_EQ(switch_data[0], static_cast(Instruction::kPackedSwitchSignature)); uint16_t size = switch_data[1]; DCHECK_GT(size, 0); @@ -412,12 +408,12 @@ static inline int32_t DoPackedSwitch(const Instruction* inst, } } -static inline int32_t DoSparseSwitch(const Instruction* inst, - const ShadowFrame& shadow_frame) +static inline int32_t DoSparseSwitch(const Instruction* inst, const ShadowFrame& shadow_frame, + uint16_t inst_data) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { DCHECK(inst->Opcode() == Instruction::SPARSE_SWITCH); const uint16_t* switch_data = reinterpret_cast(inst) + inst->VRegB_31t(); - int32_t test_val = shadow_frame.GetVReg(inst->VRegA_31t()); + int32_t test_val = shadow_frame.GetVReg(inst->VRegA_31t(inst_data)); DCHECK_EQ(switch_data[0], static_cast(Instruction::kSparseSwitchSignature)); uint16_t size = switch_data[1]; DCHECK_GT(size, 0); diff --git a/runtime/interpreter/interpreter_goto_table_impl.cc b/runtime/interpreter/interpreter_goto_table_impl.cc index 4bef7704083..b55c2c242da 100644 --- a/runtime/interpreter/interpreter_goto_table_impl.cc +++ b/runtime/interpreter/interpreter_goto_table_impl.cc @@ -22,6 +22,7 @@ namespace interpreter { // In the following macros, we expect the following local variables exist: // - "self": the current Thread*. // - "inst" : the current Instruction*. +// - "inst_data" : the current instruction's first 16 bits. // - "dex_pc": the current pc. // - "shadow_frame": the current shadow frame. // - "mh": the current MethodHelper. @@ -36,7 +37,8 @@ namespace interpreter { dex_pc = static_cast(static_cast(dex_pc) + disp); \ shadow_frame.SetDexPC(dex_pc); \ TraceExecution(shadow_frame, inst, dex_pc, mh); \ - goto *currentHandlersTable[inst->Opcode()]; \ + inst_data = inst->Fetch16(0); \ + goto *currentHandlersTable[inst->Opcode(inst_data)]; \ } while (false) #define HANDLE_PENDING_EXCEPTION() goto exception_pending_label @@ -87,6 +89,7 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* } } const Instruction* inst = Instruction::At(code_item->insns_ + dex_pc); + uint16_t inst_data; // Define handlers table. static const void* handlersTable[kNumPackedOpcodes] = { @@ -117,13 +120,13 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(MOVE) - shadow_frame.SetVReg(inst->VRegA_12x(), - shadow_frame.GetVReg(inst->VRegB_12x())); + shadow_frame.SetVReg(inst->VRegA_12x(inst_data), + shadow_frame.GetVReg(inst->VRegB_12x(inst_data))); ADVANCE(1); HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(MOVE_FROM16) - shadow_frame.SetVReg(inst->VRegA_22x(), + shadow_frame.SetVReg(inst->VRegA_22x(inst_data), shadow_frame.GetVReg(inst->VRegB_22x())); ADVANCE(2); HANDLE_INSTRUCTION_END(); @@ -135,13 +138,13 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(MOVE_WIDE) - shadow_frame.SetVRegLong(inst->VRegA_12x(), - shadow_frame.GetVRegLong(inst->VRegB_12x())); + shadow_frame.SetVRegLong(inst->VRegA_12x(inst_data), + shadow_frame.GetVRegLong(inst->VRegB_12x(inst_data))); ADVANCE(1); HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(MOVE_WIDE_FROM16) - shadow_frame.SetVRegLong(inst->VRegA_22x(), + shadow_frame.SetVRegLong(inst->VRegA_22x(inst_data), shadow_frame.GetVRegLong(inst->VRegB_22x())); ADVANCE(2); HANDLE_INSTRUCTION_END(); @@ -153,13 +156,13 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(MOVE_OBJECT) - shadow_frame.SetVRegReference(inst->VRegA_12x(), - shadow_frame.GetVRegReference(inst->VRegB_12x())); + shadow_frame.SetVRegReference(inst->VRegA_12x(inst_data), + shadow_frame.GetVRegReference(inst->VRegB_12x(inst_data))); ADVANCE(1); HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(MOVE_OBJECT_FROM16) - shadow_frame.SetVRegReference(inst->VRegA_22x(), + shadow_frame.SetVRegReference(inst->VRegA_22x(inst_data), shadow_frame.GetVRegReference(inst->VRegB_22x())); ADVANCE(2); HANDLE_INSTRUCTION_END(); @@ -171,24 +174,24 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(MOVE_RESULT) - shadow_frame.SetVReg(inst->VRegA_11x(), result_register.GetI()); + shadow_frame.SetVReg(inst->VRegA_11x(inst_data), result_register.GetI()); ADVANCE(1); HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(MOVE_RESULT_WIDE) - shadow_frame.SetVRegLong(inst->VRegA_11x(), result_register.GetJ()); + shadow_frame.SetVRegLong(inst->VRegA_11x(inst_data), result_register.GetJ()); ADVANCE(1); HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(MOVE_RESULT_OBJECT) - shadow_frame.SetVRegReference(inst->VRegA_11x(), result_register.GetL()); + shadow_frame.SetVRegReference(inst->VRegA_11x(inst_data), result_register.GetL()); ADVANCE(1); HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(MOVE_EXCEPTION) { Throwable* exception = self->GetException(NULL); self->ClearException(); - shadow_frame.SetVRegReference(inst->VRegA_11x(), exception); + shadow_frame.SetVRegReference(inst->VRegA_11x(inst_data), exception); ADVANCE(1); } HANDLE_INSTRUCTION_END(); @@ -231,7 +234,7 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* HANDLE_INSTRUCTION_START(RETURN) { JValue result; result.SetJ(0); - result.SetI(shadow_frame.GetVReg(inst->VRegA_11x())); + result.SetI(shadow_frame.GetVReg(inst->VRegA_11x(inst_data))); if (UNLIKELY(self->TestAllFlags())) { CheckSuspend(self); } @@ -246,7 +249,7 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* HANDLE_INSTRUCTION_START(RETURN_WIDE) { JValue result; - result.SetJ(shadow_frame.GetVRegLong(inst->VRegA_11x())); + result.SetJ(shadow_frame.GetVRegLong(inst->VRegA_11x(inst_data))); if (UNLIKELY(self->TestAllFlags())) { CheckSuspend(self); } @@ -262,7 +265,7 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* HANDLE_INSTRUCTION_START(RETURN_OBJECT) { JValue result; result.SetJ(0); - result.SetL(shadow_frame.GetVRegReference(inst->VRegA_11x())); + result.SetL(shadow_frame.GetVRegReference(inst->VRegA_11x(inst_data))); if (UNLIKELY(self->TestAllFlags())) { CheckSuspend(self); } @@ -276,8 +279,8 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(CONST_4) { - uint32_t dst = inst->VRegA_11n(); - int32_t val = inst->VRegB_11n(); + uint32_t dst = inst->VRegA_11n(inst_data); + int32_t val = inst->VRegB_11n(inst_data); shadow_frame.SetVReg(dst, val); if (val == 0) { shadow_frame.SetVRegReference(dst, NULL); @@ -287,7 +290,7 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(CONST_16) { - uint32_t dst = inst->VRegA_21s(); + uint32_t dst = inst->VRegA_21s(inst_data); int32_t val = inst->VRegB_21s(); shadow_frame.SetVReg(dst, val); if (val == 0) { @@ -298,7 +301,7 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(CONST) { - uint32_t dst = inst->VRegA_31i(); + uint32_t dst = inst->VRegA_31i(inst_data); int32_t val = inst->VRegB_31i(); shadow_frame.SetVReg(dst, val); if (val == 0) { @@ -309,7 +312,7 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(CONST_HIGH16) { - uint32_t dst = inst->VRegA_21h(); + uint32_t dst = inst->VRegA_21h(inst_data); int32_t val = static_cast(inst->VRegB_21h() << 16); shadow_frame.SetVReg(dst, val); if (val == 0) { @@ -320,22 +323,22 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(CONST_WIDE_16) - shadow_frame.SetVRegLong(inst->VRegA_21s(), inst->VRegB_21s()); + shadow_frame.SetVRegLong(inst->VRegA_21s(inst_data), inst->VRegB_21s()); ADVANCE(2); HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(CONST_WIDE_32) - shadow_frame.SetVRegLong(inst->VRegA_31i(), inst->VRegB_31i()); + shadow_frame.SetVRegLong(inst->VRegA_31i(inst_data), inst->VRegB_31i()); ADVANCE(3); HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(CONST_WIDE) - shadow_frame.SetVRegLong(inst->VRegA_51l(), inst->VRegB_51l()); + shadow_frame.SetVRegLong(inst->VRegA_51l(inst_data), inst->VRegB_51l()); ADVANCE(5); HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(CONST_WIDE_HIGH16) - shadow_frame.SetVRegLong(inst->VRegA_21h(), + shadow_frame.SetVRegLong(inst->VRegA_21h(inst_data), static_cast(inst->VRegB_21h()) << 48); ADVANCE(2); HANDLE_INSTRUCTION_END(); @@ -345,7 +348,7 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* if (UNLIKELY(s == NULL)) { HANDLE_PENDING_EXCEPTION(); } else { - shadow_frame.SetVRegReference(inst->VRegA_21c(), s); + shadow_frame.SetVRegReference(inst->VRegA_21c(inst_data), s); ADVANCE(2); } } @@ -356,7 +359,7 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* if (UNLIKELY(s == NULL)) { HANDLE_PENDING_EXCEPTION(); } else { - shadow_frame.SetVRegReference(inst->VRegA_31c(), s); + shadow_frame.SetVRegReference(inst->VRegA_31c(inst_data), s); ADVANCE(3); } } @@ -368,14 +371,14 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* if (UNLIKELY(c == NULL)) { HANDLE_PENDING_EXCEPTION(); } else { - shadow_frame.SetVRegReference(inst->VRegA_21c(), c); + shadow_frame.SetVRegReference(inst->VRegA_21c(inst_data), c); ADVANCE(2); } } HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(MONITOR_ENTER) { - Object* obj = shadow_frame.GetVRegReference(inst->VRegA_11x()); + Object* obj = shadow_frame.GetVRegReference(inst->VRegA_11x(inst_data)); if (UNLIKELY(obj == NULL)) { ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow()); HANDLE_PENDING_EXCEPTION(); @@ -387,7 +390,7 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(MONITOR_EXIT) { - Object* obj = shadow_frame.GetVRegReference(inst->VRegA_11x()); + Object* obj = shadow_frame.GetVRegReference(inst->VRegA_11x(inst_data)); if (UNLIKELY(obj == NULL)) { ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow()); HANDLE_PENDING_EXCEPTION(); @@ -404,7 +407,7 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* if (UNLIKELY(c == NULL)) { HANDLE_PENDING_EXCEPTION(); } else { - Object* obj = shadow_frame.GetVRegReference(inst->VRegA_21c()); + Object* obj = shadow_frame.GetVRegReference(inst->VRegA_21c(inst_data)); if (UNLIKELY(obj != NULL && !obj->InstanceOf(c))) { ThrowClassCastException(c, obj->GetClass()); HANDLE_PENDING_EXCEPTION(); @@ -421,20 +424,20 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* if (UNLIKELY(c == NULL)) { HANDLE_PENDING_EXCEPTION(); } else { - Object* obj = shadow_frame.GetVRegReference(inst->VRegB_22c()); - shadow_frame.SetVReg(inst->VRegA_22c(), (obj != NULL && obj->InstanceOf(c)) ? 1 : 0); + Object* obj = shadow_frame.GetVRegReference(inst->VRegB_22c(inst_data)); + shadow_frame.SetVReg(inst->VRegA_22c(inst_data), (obj != NULL && obj->InstanceOf(c)) ? 1 : 0); ADVANCE(2); } } HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(ARRAY_LENGTH) { - Object* array = shadow_frame.GetVRegReference(inst->VRegB_12x()); + Object* array = shadow_frame.GetVRegReference(inst->VRegB_12x(inst_data)); if (UNLIKELY(array == NULL)) { ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow()); HANDLE_PENDING_EXCEPTION(); } else { - shadow_frame.SetVReg(inst->VRegA_12x(), array->AsArray()->GetLength()); + shadow_frame.SetVReg(inst->VRegA_12x(inst_data), array->AsArray()->GetLength()); ADVANCE(1); } } @@ -446,20 +449,20 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* if (UNLIKELY(obj == NULL)) { HANDLE_PENDING_EXCEPTION(); } else { - shadow_frame.SetVRegReference(inst->VRegA_21c(), obj); + shadow_frame.SetVRegReference(inst->VRegA_21c(inst_data), obj); ADVANCE(2); } } HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(NEW_ARRAY) { - int32_t length = shadow_frame.GetVReg(inst->VRegB_22c()); + int32_t length = shadow_frame.GetVReg(inst->VRegB_22c(inst_data)); Object* obj = AllocArrayFromCode(inst->VRegC_22c(), shadow_frame.GetMethod(), length, self, do_access_check); if (UNLIKELY(obj == NULL)) { HANDLE_PENDING_EXCEPTION(); } else { - shadow_frame.SetVRegReference(inst->VRegA_22c(), obj); + shadow_frame.SetVRegReference(inst->VRegA_22c(inst_data), obj); ADVANCE(2); } } @@ -480,7 +483,7 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(FILL_ARRAY_DATA) { - Object* obj = shadow_frame.GetVRegReference(inst->VRegA_31t()); + Object* obj = shadow_frame.GetVRegReference(inst->VRegA_31t(inst_data)); if (UNLIKELY(obj == NULL)) { ThrowNullPointerException(NULL, "null array in FILL_ARRAY_DATA"); HANDLE_PENDING_EXCEPTION(); @@ -506,7 +509,7 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(THROW) { - Object* exception = shadow_frame.GetVRegReference(inst->VRegA_11x()); + Object* exception = shadow_frame.GetVRegReference(inst->VRegA_11x(inst_data)); if (UNLIKELY(exception == NULL)) { ThrowNullPointerException(NULL, "throw with null exception"); } else { @@ -517,7 +520,7 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(GOTO) { - int8_t offset = inst->VRegA_10t(); + int8_t offset = inst->VRegA_10t(inst_data); if (IsBackwardBranch(offset)) { if (UNLIKELY(self->TestAllFlags())) { CheckSuspend(self); @@ -553,7 +556,7 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(PACKED_SWITCH) { - int32_t offset = DoPackedSwitch(inst, shadow_frame); + int32_t offset = DoPackedSwitch(inst, shadow_frame, inst_data); if (IsBackwardBranch(offset)) { if (UNLIKELY(self->TestAllFlags())) { CheckSuspend(self); @@ -565,7 +568,7 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(SPARSE_SWITCH) { - int32_t offset = DoSparseSwitch(inst, shadow_frame); + int32_t offset = DoSparseSwitch(inst, shadow_frame, inst_data); if (IsBackwardBranch(offset)) { if (UNLIKELY(self->TestAllFlags())) { CheckSuspend(self); @@ -587,7 +590,7 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* } else { result = -1; } - shadow_frame.SetVReg(inst->VRegA_23x(), result); + shadow_frame.SetVReg(inst->VRegA_23x(inst_data), result); ADVANCE(2); } HANDLE_INSTRUCTION_END(); @@ -603,7 +606,7 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* } else { result = 1; } - shadow_frame.SetVReg(inst->VRegA_23x(), result); + shadow_frame.SetVReg(inst->VRegA_23x(inst_data), result); ADVANCE(2); } HANDLE_INSTRUCTION_END(); @@ -619,7 +622,7 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* } else { result = -1; } - shadow_frame.SetVReg(inst->VRegA_23x(), result); + shadow_frame.SetVReg(inst->VRegA_23x(inst_data), result); ADVANCE(2); } HANDLE_INSTRUCTION_END(); @@ -635,7 +638,7 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* } else { result = 1; } - shadow_frame.SetVReg(inst->VRegA_23x(), result); + shadow_frame.SetVReg(inst->VRegA_23x(inst_data), result); ADVANCE(2); } HANDLE_INSTRUCTION_END(); @@ -651,13 +654,13 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* } else { result = -1; } - shadow_frame.SetVReg(inst->VRegA_23x(), result); + shadow_frame.SetVReg(inst->VRegA_23x(inst_data), result); ADVANCE(2); } HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(IF_EQ) { - if (shadow_frame.GetVReg(inst->VRegA_22t()) == shadow_frame.GetVReg(inst->VRegB_22t())) { + if (shadow_frame.GetVReg(inst->VRegA_22t(inst_data)) == shadow_frame.GetVReg(inst->VRegB_22t(inst_data))) { int16_t offset = inst->VRegC_22t(); if (IsBackwardBranch(offset)) { if (UNLIKELY(self->TestAllFlags())) { @@ -673,7 +676,7 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(IF_NE) { - if (shadow_frame.GetVReg(inst->VRegA_22t()) != shadow_frame.GetVReg(inst->VRegB_22t())) { + if (shadow_frame.GetVReg(inst->VRegA_22t(inst_data)) != shadow_frame.GetVReg(inst->VRegB_22t(inst_data))) { int16_t offset = inst->VRegC_22t(); if (IsBackwardBranch(offset)) { if (UNLIKELY(self->TestAllFlags())) { @@ -689,7 +692,7 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(IF_LT) { - if (shadow_frame.GetVReg(inst->VRegA_22t()) < shadow_frame.GetVReg(inst->VRegB_22t())) { + if (shadow_frame.GetVReg(inst->VRegA_22t(inst_data)) < shadow_frame.GetVReg(inst->VRegB_22t(inst_data))) { int16_t offset = inst->VRegC_22t(); if (IsBackwardBranch(offset)) { if (UNLIKELY(self->TestAllFlags())) { @@ -705,7 +708,7 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(IF_GE) { - if (shadow_frame.GetVReg(inst->VRegA_22t()) >= shadow_frame.GetVReg(inst->VRegB_22t())) { + if (shadow_frame.GetVReg(inst->VRegA_22t(inst_data)) >= shadow_frame.GetVReg(inst->VRegB_22t(inst_data))) { int16_t offset = inst->VRegC_22t(); if (IsBackwardBranch(offset)) { if (UNLIKELY(self->TestAllFlags())) { @@ -721,7 +724,7 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(IF_GT) { - if (shadow_frame.GetVReg(inst->VRegA_22t()) > shadow_frame.GetVReg(inst->VRegB_22t())) { + if (shadow_frame.GetVReg(inst->VRegA_22t(inst_data)) > shadow_frame.GetVReg(inst->VRegB_22t(inst_data))) { int16_t offset = inst->VRegC_22t(); if (IsBackwardBranch(offset)) { if (UNLIKELY(self->TestAllFlags())) { @@ -737,7 +740,7 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(IF_LE) { - if (shadow_frame.GetVReg(inst->VRegA_22t()) <= shadow_frame.GetVReg(inst->VRegB_22t())) { + if (shadow_frame.GetVReg(inst->VRegA_22t(inst_data)) <= shadow_frame.GetVReg(inst->VRegB_22t(inst_data))) { int16_t offset = inst->VRegC_22t(); if (IsBackwardBranch(offset)) { if (UNLIKELY(self->TestAllFlags())) { @@ -753,7 +756,7 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(IF_EQZ) { - if (shadow_frame.GetVReg(inst->VRegA_21t()) == 0) { + if (shadow_frame.GetVReg(inst->VRegA_21t(inst_data)) == 0) { int16_t offset = inst->VRegB_21t(); if (IsBackwardBranch(offset)) { if (UNLIKELY(self->TestAllFlags())) { @@ -769,7 +772,7 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(IF_NEZ) { - if (shadow_frame.GetVReg(inst->VRegA_21t()) != 0) { + if (shadow_frame.GetVReg(inst->VRegA_21t(inst_data)) != 0) { int16_t offset = inst->VRegB_21t(); if (IsBackwardBranch(offset)) { if (UNLIKELY(self->TestAllFlags())) { @@ -785,7 +788,7 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(IF_LTZ) { - if (shadow_frame.GetVReg(inst->VRegA_21t()) < 0) { + if (shadow_frame.GetVReg(inst->VRegA_21t(inst_data)) < 0) { int16_t offset = inst->VRegB_21t(); if (IsBackwardBranch(offset)) { if (UNLIKELY(self->TestAllFlags())) { @@ -801,7 +804,7 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(IF_GEZ) { - if (shadow_frame.GetVReg(inst->VRegA_21t()) >= 0) { + if (shadow_frame.GetVReg(inst->VRegA_21t(inst_data)) >= 0) { int16_t offset = inst->VRegB_21t(); if (IsBackwardBranch(offset)) { if (UNLIKELY(self->TestAllFlags())) { @@ -817,7 +820,7 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(IF_GTZ) { - if (shadow_frame.GetVReg(inst->VRegA_21t()) > 0) { + if (shadow_frame.GetVReg(inst->VRegA_21t(inst_data)) > 0) { int16_t offset = inst->VRegB_21t(); if (IsBackwardBranch(offset)) { if (UNLIKELY(self->TestAllFlags())) { @@ -833,7 +836,7 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(IF_LEZ) { - if (shadow_frame.GetVReg(inst->VRegA_21t()) <= 0) { + if (shadow_frame.GetVReg(inst->VRegA_21t(inst_data)) <= 0) { int16_t offset = inst->VRegB_21t(); if (IsBackwardBranch(offset)) { if (UNLIKELY(self->TestAllFlags())) { @@ -857,7 +860,7 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* int32_t index = shadow_frame.GetVReg(inst->VRegC_23x()); BooleanArray* array = a->AsBooleanArray(); if (LIKELY(array->IsValidIndex(index))) { - shadow_frame.SetVReg(inst->VRegA_23x(), array->GetData()[index]); + shadow_frame.SetVReg(inst->VRegA_23x(inst_data), array->GetData()[index]); ADVANCE(2); } else { HANDLE_PENDING_EXCEPTION(); @@ -875,7 +878,7 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* int32_t index = shadow_frame.GetVReg(inst->VRegC_23x()); ByteArray* array = a->AsByteArray(); if (LIKELY(array->IsValidIndex(index))) { - shadow_frame.SetVReg(inst->VRegA_23x(), array->GetData()[index]); + shadow_frame.SetVReg(inst->VRegA_23x(inst_data), array->GetData()[index]); ADVANCE(2); } else { HANDLE_PENDING_EXCEPTION(); @@ -893,7 +896,7 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* int32_t index = shadow_frame.GetVReg(inst->VRegC_23x()); CharArray* array = a->AsCharArray(); if (LIKELY(array->IsValidIndex(index))) { - shadow_frame.SetVReg(inst->VRegA_23x(), array->GetData()[index]); + shadow_frame.SetVReg(inst->VRegA_23x(inst_data), array->GetData()[index]); ADVANCE(2); } else { HANDLE_PENDING_EXCEPTION(); @@ -911,7 +914,7 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* int32_t index = shadow_frame.GetVReg(inst->VRegC_23x()); ShortArray* array = a->AsShortArray(); if (LIKELY(array->IsValidIndex(index))) { - shadow_frame.SetVReg(inst->VRegA_23x(), array->GetData()[index]); + shadow_frame.SetVReg(inst->VRegA_23x(inst_data), array->GetData()[index]); ADVANCE(2); } else { HANDLE_PENDING_EXCEPTION(); @@ -929,7 +932,7 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* int32_t index = shadow_frame.GetVReg(inst->VRegC_23x()); IntArray* array = a->AsIntArray(); if (LIKELY(array->IsValidIndex(index))) { - shadow_frame.SetVReg(inst->VRegA_23x(), array->GetData()[index]); + shadow_frame.SetVReg(inst->VRegA_23x(inst_data), array->GetData()[index]); ADVANCE(2); } else { HANDLE_PENDING_EXCEPTION(); @@ -947,7 +950,7 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* int32_t index = shadow_frame.GetVReg(inst->VRegC_23x()); LongArray* array = a->AsLongArray(); if (LIKELY(array->IsValidIndex(index))) { - shadow_frame.SetVRegLong(inst->VRegA_23x(), array->GetData()[index]); + shadow_frame.SetVRegLong(inst->VRegA_23x(inst_data), array->GetData()[index]); ADVANCE(2); } else { HANDLE_PENDING_EXCEPTION(); @@ -965,7 +968,7 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* int32_t index = shadow_frame.GetVReg(inst->VRegC_23x()); ObjectArray* array = a->AsObjectArray(); if (LIKELY(array->IsValidIndex(index))) { - shadow_frame.SetVRegReference(inst->VRegA_23x(), array->GetWithoutChecks(index)); + shadow_frame.SetVRegReference(inst->VRegA_23x(inst_data), array->GetWithoutChecks(index)); ADVANCE(2); } else { HANDLE_PENDING_EXCEPTION(); @@ -980,7 +983,7 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow()); HANDLE_PENDING_EXCEPTION(); } else { - uint8_t val = shadow_frame.GetVReg(inst->VRegA_23x()); + uint8_t val = shadow_frame.GetVReg(inst->VRegA_23x(inst_data)); int32_t index = shadow_frame.GetVReg(inst->VRegC_23x()); BooleanArray* array = a->AsBooleanArray(); if (LIKELY(array->IsValidIndex(index))) { @@ -999,7 +1002,7 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow()); HANDLE_PENDING_EXCEPTION(); } else { - int8_t val = shadow_frame.GetVReg(inst->VRegA_23x()); + int8_t val = shadow_frame.GetVReg(inst->VRegA_23x(inst_data)); int32_t index = shadow_frame.GetVReg(inst->VRegC_23x()); ByteArray* array = a->AsByteArray(); if (LIKELY(array->IsValidIndex(index))) { @@ -1018,7 +1021,7 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow()); HANDLE_PENDING_EXCEPTION(); } else { - uint16_t val = shadow_frame.GetVReg(inst->VRegA_23x()); + uint16_t val = shadow_frame.GetVReg(inst->VRegA_23x(inst_data)); int32_t index = shadow_frame.GetVReg(inst->VRegC_23x()); CharArray* array = a->AsCharArray(); if (LIKELY(array->IsValidIndex(index))) { @@ -1037,7 +1040,7 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow()); HANDLE_PENDING_EXCEPTION(); } else { - int16_t val = shadow_frame.GetVReg(inst->VRegA_23x()); + int16_t val = shadow_frame.GetVReg(inst->VRegA_23x(inst_data)); int32_t index = shadow_frame.GetVReg(inst->VRegC_23x()); ShortArray* array = a->AsShortArray(); if (LIKELY(array->IsValidIndex(index))) { @@ -1056,7 +1059,7 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow()); HANDLE_PENDING_EXCEPTION(); } else { - int32_t val = shadow_frame.GetVReg(inst->VRegA_23x()); + int32_t val = shadow_frame.GetVReg(inst->VRegA_23x(inst_data)); int32_t index = shadow_frame.GetVReg(inst->VRegC_23x()); IntArray* array = a->AsIntArray(); if (LIKELY(array->IsValidIndex(index))) { @@ -1075,7 +1078,7 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow()); HANDLE_PENDING_EXCEPTION(); } else { - int64_t val = shadow_frame.GetVRegLong(inst->VRegA_23x()); + int64_t val = shadow_frame.GetVRegLong(inst->VRegA_23x(inst_data)); int32_t index = shadow_frame.GetVReg(inst->VRegC_23x()); LongArray* array = a->AsLongArray(); if (LIKELY(array->IsValidIndex(index))) { @@ -1095,7 +1098,7 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* HANDLE_PENDING_EXCEPTION(); } else { int32_t index = shadow_frame.GetVReg(inst->VRegC_23x()); - Object* val = shadow_frame.GetVRegReference(inst->VRegA_23x()); + Object* val = shadow_frame.GetVRegReference(inst->VRegA_23x(inst_data)); ObjectArray* array = a->AsObjectArray(); if (LIKELY(array->IsValidIndex(index) && array->CheckAssignable(val))) { array->SetWithoutChecks(index, val); @@ -1108,205 +1111,205 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(IGET_BOOLEAN) { - bool success = DoFieldGet(self, shadow_frame, inst); + bool success = DoFieldGet(self, shadow_frame, inst, inst_data); POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2); } HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(IGET_BYTE) { - bool success = DoFieldGet(self, shadow_frame, inst); + bool success = DoFieldGet(self, shadow_frame, inst, inst_data); POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2); } HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(IGET_CHAR) { - bool success = DoFieldGet(self, shadow_frame, inst); + bool success = DoFieldGet(self, shadow_frame, inst, inst_data); POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2); } HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(IGET_SHORT) { - bool success = DoFieldGet(self, shadow_frame, inst); + bool success = DoFieldGet(self, shadow_frame, inst, inst_data); POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2); } HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(IGET) { - bool success = DoFieldGet(self, shadow_frame, inst); + bool success = DoFieldGet(self, shadow_frame, inst, inst_data); POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2); } HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(IGET_WIDE) { - bool success = DoFieldGet(self, shadow_frame, inst); + bool success = DoFieldGet(self, shadow_frame, inst, inst_data); POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2); } HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(IGET_OBJECT) { - bool success = DoFieldGet(self, shadow_frame, inst); + bool success = DoFieldGet(self, shadow_frame, inst, inst_data); POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2); } HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(IGET_QUICK) { - bool success = DoIGetQuick(self, shadow_frame, inst); + bool success = DoIGetQuick(shadow_frame, inst, inst_data); POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2); } HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(IGET_WIDE_QUICK) { - bool success = DoIGetQuick(self, shadow_frame, inst); + bool success = DoIGetQuick(shadow_frame, inst, inst_data); POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2); } HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(IGET_OBJECT_QUICK) { - bool success = DoIGetQuick(self, shadow_frame, inst); + bool success = DoIGetQuick(shadow_frame, inst, inst_data); POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2); } HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(SGET_BOOLEAN) { - bool success = DoFieldGet(self, shadow_frame, inst); + bool success = DoFieldGet(self, shadow_frame, inst, inst_data); POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2); } HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(SGET_BYTE) { - bool success = DoFieldGet(self, shadow_frame, inst); + bool success = DoFieldGet(self, shadow_frame, inst, inst_data); POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2); } HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(SGET_CHAR) { - bool success = DoFieldGet(self, shadow_frame, inst); + bool success = DoFieldGet(self, shadow_frame, inst, inst_data); POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2); } HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(SGET_SHORT) { - bool success = DoFieldGet(self, shadow_frame, inst); + bool success = DoFieldGet(self, shadow_frame, inst, inst_data); POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2); } HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(SGET) { - bool success = DoFieldGet(self, shadow_frame, inst); + bool success = DoFieldGet(self, shadow_frame, inst, inst_data); POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2); } HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(SGET_WIDE) { - bool success = DoFieldGet(self, shadow_frame, inst); + bool success = DoFieldGet(self, shadow_frame, inst, inst_data); POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2); } HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(SGET_OBJECT) { - bool success = DoFieldGet(self, shadow_frame, inst); + bool success = DoFieldGet(self, shadow_frame, inst, inst_data); POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2); } HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(IPUT_BOOLEAN) { - bool success = DoFieldPut(self, shadow_frame, inst); + bool success = DoFieldPut(self, shadow_frame, inst, inst_data); POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2); } HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(IPUT_BYTE) { - bool success = DoFieldPut(self, shadow_frame, inst); + bool success = DoFieldPut(self, shadow_frame, inst, inst_data); POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2); } HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(IPUT_CHAR) { - bool success = DoFieldPut(self, shadow_frame, inst); + bool success = DoFieldPut(self, shadow_frame, inst, inst_data); POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2); } HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(IPUT_SHORT) { - bool success = DoFieldPut(self, shadow_frame, inst); + bool success = DoFieldPut(self, shadow_frame, inst, inst_data); POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2); } HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(IPUT) { - bool success = DoFieldPut(self, shadow_frame, inst); + bool success = DoFieldPut(self, shadow_frame, inst, inst_data); POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2); } HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(IPUT_WIDE) { - bool success = DoFieldPut(self, shadow_frame, inst); + bool success = DoFieldPut(self, shadow_frame, inst, inst_data); POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2); } HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(IPUT_OBJECT) { - bool success = DoFieldPut(self, shadow_frame, inst); + bool success = DoFieldPut(self, shadow_frame, inst, inst_data); POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2); } HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(IPUT_QUICK) { - bool success = DoIPutQuick(self, shadow_frame, inst); + bool success = DoIPutQuick(shadow_frame, inst, inst_data); POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2); } HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(IPUT_WIDE_QUICK) { - bool success = DoIPutQuick(self, shadow_frame, inst); + bool success = DoIPutQuick(shadow_frame, inst, inst_data); POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2); } HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(IPUT_OBJECT_QUICK) { - bool success = DoIPutQuick(self, shadow_frame, inst); + bool success = DoIPutQuick(shadow_frame, inst, inst_data); POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2); } HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(SPUT_BOOLEAN) { - bool success = DoFieldPut(self, shadow_frame, inst); + bool success = DoFieldPut(self, shadow_frame, inst, inst_data); POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2); } HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(SPUT_BYTE) { - bool success = DoFieldPut(self, shadow_frame, inst); + bool success = DoFieldPut(self, shadow_frame, inst, inst_data); POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2); } HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(SPUT_CHAR) { - bool success = DoFieldPut(self, shadow_frame, inst); + bool success = DoFieldPut(self, shadow_frame, inst, inst_data); POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2); } HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(SPUT_SHORT) { - bool success = DoFieldPut(self, shadow_frame, inst); + bool success = DoFieldPut(self, shadow_frame, inst, inst_data); POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2); } HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(SPUT) { - bool success = DoFieldPut(self, shadow_frame, inst); + bool success = DoFieldPut(self, shadow_frame, inst, inst_data); POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2); } HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(SPUT_WIDE) { - bool success = DoFieldPut(self, shadow_frame, inst); + bool success = DoFieldPut(self, shadow_frame, inst, inst_data); POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2); } HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(SPUT_OBJECT) { - bool success = DoFieldPut(self, shadow_frame, inst); + bool success = DoFieldPut(self, shadow_frame, inst, inst_data); POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2); } HANDLE_INSTRUCTION_END(); @@ -1396,67 +1399,67 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(NEG_INT) - shadow_frame.SetVReg(inst->VRegA_12x(), -shadow_frame.GetVReg(inst->VRegB_12x())); + shadow_frame.SetVReg(inst->VRegA_12x(inst_data), -shadow_frame.GetVReg(inst->VRegB_12x(inst_data))); ADVANCE(1); HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(NOT_INT) - shadow_frame.SetVReg(inst->VRegA_12x(), ~shadow_frame.GetVReg(inst->VRegB_12x())); + shadow_frame.SetVReg(inst->VRegA_12x(inst_data), ~shadow_frame.GetVReg(inst->VRegB_12x(inst_data))); ADVANCE(1); HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(NEG_LONG) - shadow_frame.SetVRegLong(inst->VRegA_12x(), -shadow_frame.GetVRegLong(inst->VRegB_12x())); + shadow_frame.SetVRegLong(inst->VRegA_12x(inst_data), -shadow_frame.GetVRegLong(inst->VRegB_12x(inst_data))); ADVANCE(1); HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(NOT_LONG) - shadow_frame.SetVRegLong(inst->VRegA_12x(), ~shadow_frame.GetVRegLong(inst->VRegB_12x())); + shadow_frame.SetVRegLong(inst->VRegA_12x(inst_data), ~shadow_frame.GetVRegLong(inst->VRegB_12x(inst_data))); ADVANCE(1); HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(NEG_FLOAT) - shadow_frame.SetVRegFloat(inst->VRegA_12x(), -shadow_frame.GetVRegFloat(inst->VRegB_12x())); + shadow_frame.SetVRegFloat(inst->VRegA_12x(inst_data), -shadow_frame.GetVRegFloat(inst->VRegB_12x(inst_data))); ADVANCE(1); HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(NEG_DOUBLE) - shadow_frame.SetVRegDouble(inst->VRegA_12x(), -shadow_frame.GetVRegDouble(inst->VRegB_12x())); + shadow_frame.SetVRegDouble(inst->VRegA_12x(inst_data), -shadow_frame.GetVRegDouble(inst->VRegB_12x(inst_data))); ADVANCE(1); HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(INT_TO_LONG) - shadow_frame.SetVRegLong(inst->VRegA_12x(), shadow_frame.GetVReg(inst->VRegB_12x())); + shadow_frame.SetVRegLong(inst->VRegA_12x(inst_data), shadow_frame.GetVReg(inst->VRegB_12x(inst_data))); ADVANCE(1); HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(INT_TO_FLOAT) - shadow_frame.SetVRegFloat(inst->VRegA_12x(), shadow_frame.GetVReg(inst->VRegB_12x())); + shadow_frame.SetVRegFloat(inst->VRegA_12x(inst_data), shadow_frame.GetVReg(inst->VRegB_12x(inst_data))); ADVANCE(1); HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(INT_TO_DOUBLE) - shadow_frame.SetVRegDouble(inst->VRegA_12x(), shadow_frame.GetVReg(inst->VRegB_12x())); + shadow_frame.SetVRegDouble(inst->VRegA_12x(inst_data), shadow_frame.GetVReg(inst->VRegB_12x(inst_data))); ADVANCE(1); HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(LONG_TO_INT) - shadow_frame.SetVReg(inst->VRegA_12x(), shadow_frame.GetVRegLong(inst->VRegB_12x())); + shadow_frame.SetVReg(inst->VRegA_12x(inst_data), shadow_frame.GetVRegLong(inst->VRegB_12x(inst_data))); ADVANCE(1); HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(LONG_TO_FLOAT) - shadow_frame.SetVRegFloat(inst->VRegA_12x(), shadow_frame.GetVRegLong(inst->VRegB_12x())); + shadow_frame.SetVRegFloat(inst->VRegA_12x(inst_data), shadow_frame.GetVRegLong(inst->VRegB_12x(inst_data))); ADVANCE(1); HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(LONG_TO_DOUBLE) - shadow_frame.SetVRegDouble(inst->VRegA_12x(), shadow_frame.GetVRegLong(inst->VRegB_12x())); + shadow_frame.SetVRegDouble(inst->VRegA_12x(inst_data), shadow_frame.GetVRegLong(inst->VRegB_12x(inst_data))); ADVANCE(1); HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(FLOAT_TO_INT) { - float val = shadow_frame.GetVRegFloat(inst->VRegB_12x()); + float val = shadow_frame.GetVRegFloat(inst->VRegB_12x(inst_data)); int32_t result; if (val != val) { result = 0; @@ -1467,13 +1470,13 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* } else { result = val; } - shadow_frame.SetVReg(inst->VRegA_12x(), result); + shadow_frame.SetVReg(inst->VRegA_12x(inst_data), result); ADVANCE(1); } HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(FLOAT_TO_LONG) { - float val = shadow_frame.GetVRegFloat(inst->VRegB_12x()); + float val = shadow_frame.GetVRegFloat(inst->VRegB_12x(inst_data)); int64_t result; if (val != val) { result = 0; @@ -1484,18 +1487,18 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* } else { result = val; } - shadow_frame.SetVRegLong(inst->VRegA_12x(), result); + shadow_frame.SetVRegLong(inst->VRegA_12x(inst_data), result); ADVANCE(1); } HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(FLOAT_TO_DOUBLE) - shadow_frame.SetVRegDouble(inst->VRegA_12x(), shadow_frame.GetVRegFloat(inst->VRegB_12x())); + shadow_frame.SetVRegDouble(inst->VRegA_12x(inst_data), shadow_frame.GetVRegFloat(inst->VRegB_12x(inst_data))); ADVANCE(1); HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(DOUBLE_TO_INT) { - double val = shadow_frame.GetVRegDouble(inst->VRegB_12x()); + double val = shadow_frame.GetVRegDouble(inst->VRegB_12x(inst_data)); int32_t result; if (val != val) { result = 0; @@ -1506,13 +1509,13 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* } else { result = val; } - shadow_frame.SetVReg(inst->VRegA_12x(), result); + shadow_frame.SetVReg(inst->VRegA_12x(inst_data), result); ADVANCE(1); } HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(DOUBLE_TO_LONG) { - double val = shadow_frame.GetVRegDouble(inst->VRegB_12x()); + double val = shadow_frame.GetVRegDouble(inst->VRegB_12x(inst_data)); int64_t result; if (val != val) { result = 0; @@ -1523,674 +1526,674 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* } else { result = val; } - shadow_frame.SetVRegLong(inst->VRegA_12x(), result); + shadow_frame.SetVRegLong(inst->VRegA_12x(inst_data), result); ADVANCE(1); } HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(DOUBLE_TO_FLOAT) - shadow_frame.SetVRegFloat(inst->VRegA_12x(), shadow_frame.GetVRegDouble(inst->VRegB_12x())); + shadow_frame.SetVRegFloat(inst->VRegA_12x(inst_data), shadow_frame.GetVRegDouble(inst->VRegB_12x(inst_data))); ADVANCE(1); HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(INT_TO_BYTE) - shadow_frame.SetVReg(inst->VRegA_12x(), - static_cast(shadow_frame.GetVReg(inst->VRegB_12x()))); + shadow_frame.SetVReg(inst->VRegA_12x(inst_data), + static_cast(shadow_frame.GetVReg(inst->VRegB_12x(inst_data)))); ADVANCE(1); HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(INT_TO_CHAR) - shadow_frame.SetVReg(inst->VRegA_12x(), - static_cast(shadow_frame.GetVReg(inst->VRegB_12x()))); + shadow_frame.SetVReg(inst->VRegA_12x(inst_data), + static_cast(shadow_frame.GetVReg(inst->VRegB_12x(inst_data)))); ADVANCE(1); HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(INT_TO_SHORT) - shadow_frame.SetVReg(inst->VRegA_12x(), - static_cast(shadow_frame.GetVReg(inst->VRegB_12x()))); + shadow_frame.SetVReg(inst->VRegA_12x(inst_data), + static_cast(shadow_frame.GetVReg(inst->VRegB_12x(inst_data)))); ADVANCE(1); HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(ADD_INT) - shadow_frame.SetVReg(inst->VRegA_23x(), + shadow_frame.SetVReg(inst->VRegA_23x(inst_data), shadow_frame.GetVReg(inst->VRegB_23x()) + shadow_frame.GetVReg(inst->VRegC_23x())); ADVANCE(2); HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(SUB_INT) - shadow_frame.SetVReg(inst->VRegA_23x(), + shadow_frame.SetVReg(inst->VRegA_23x(inst_data), shadow_frame.GetVReg(inst->VRegB_23x()) - shadow_frame.GetVReg(inst->VRegC_23x())); ADVANCE(2); HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(MUL_INT) - shadow_frame.SetVReg(inst->VRegA_23x(), + shadow_frame.SetVReg(inst->VRegA_23x(inst_data), shadow_frame.GetVReg(inst->VRegB_23x()) * shadow_frame.GetVReg(inst->VRegC_23x())); ADVANCE(2); HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(DIV_INT) { - bool success = DoIntDivide(shadow_frame, inst->VRegA_23x(), - shadow_frame.GetVReg(inst->VRegB_23x()), - shadow_frame.GetVReg(inst->VRegC_23x())); + 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, 2); } HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(REM_INT) { - bool success = DoIntRemainder(shadow_frame, inst->VRegA_23x(), - shadow_frame.GetVReg(inst->VRegB_23x()), - shadow_frame.GetVReg(inst->VRegC_23x())); + 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, 2); } HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(SHL_INT) - shadow_frame.SetVReg(inst->VRegA_23x(), + shadow_frame.SetVReg(inst->VRegA_23x(inst_data), shadow_frame.GetVReg(inst->VRegB_23x()) << (shadow_frame.GetVReg(inst->VRegC_23x()) & 0x1f)); ADVANCE(2); HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(SHR_INT) - shadow_frame.SetVReg(inst->VRegA_23x(), + shadow_frame.SetVReg(inst->VRegA_23x(inst_data), shadow_frame.GetVReg(inst->VRegB_23x()) >> (shadow_frame.GetVReg(inst->VRegC_23x()) & 0x1f)); ADVANCE(2); HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(USHR_INT) - shadow_frame.SetVReg(inst->VRegA_23x(), + shadow_frame.SetVReg(inst->VRegA_23x(inst_data), static_cast(shadow_frame.GetVReg(inst->VRegB_23x())) >> (shadow_frame.GetVReg(inst->VRegC_23x()) & 0x1f)); ADVANCE(2); HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(AND_INT) - shadow_frame.SetVReg(inst->VRegA_23x(), + shadow_frame.SetVReg(inst->VRegA_23x(inst_data), shadow_frame.GetVReg(inst->VRegB_23x()) & shadow_frame.GetVReg(inst->VRegC_23x())); ADVANCE(2); HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(OR_INT) - shadow_frame.SetVReg(inst->VRegA_23x(), + shadow_frame.SetVReg(inst->VRegA_23x(inst_data), shadow_frame.GetVReg(inst->VRegB_23x()) | shadow_frame.GetVReg(inst->VRegC_23x())); ADVANCE(2); HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(XOR_INT) - shadow_frame.SetVReg(inst->VRegA_23x(), + shadow_frame.SetVReg(inst->VRegA_23x(inst_data), shadow_frame.GetVReg(inst->VRegB_23x()) ^ shadow_frame.GetVReg(inst->VRegC_23x())); ADVANCE(2); HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(ADD_LONG) - shadow_frame.SetVRegLong(inst->VRegA_23x(), + shadow_frame.SetVRegLong(inst->VRegA_23x(inst_data), shadow_frame.GetVRegLong(inst->VRegB_23x()) + shadow_frame.GetVRegLong(inst->VRegC_23x())); ADVANCE(2); HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(SUB_LONG) - shadow_frame.SetVRegLong(inst->VRegA_23x(), + shadow_frame.SetVRegLong(inst->VRegA_23x(inst_data), shadow_frame.GetVRegLong(inst->VRegB_23x()) - shadow_frame.GetVRegLong(inst->VRegC_23x())); ADVANCE(2); HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(MUL_LONG) - shadow_frame.SetVRegLong(inst->VRegA_23x(), + shadow_frame.SetVRegLong(inst->VRegA_23x(inst_data), shadow_frame.GetVRegLong(inst->VRegB_23x()) * shadow_frame.GetVRegLong(inst->VRegC_23x())); ADVANCE(2); HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(DIV_LONG) { - bool success = DoLongDivide(shadow_frame, inst->VRegA_23x(), - shadow_frame.GetVRegLong(inst->VRegB_23x()), - shadow_frame.GetVRegLong(inst->VRegC_23x())); + bool success = DoLongDivide(shadow_frame, inst->VRegA_23x(inst_data), + shadow_frame.GetVRegLong(inst->VRegB_23x()), + shadow_frame.GetVRegLong(inst->VRegC_23x())); POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2); } HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(REM_LONG) { - bool success = DoLongRemainder(shadow_frame, inst->VRegA_23x(), - shadow_frame.GetVRegLong(inst->VRegB_23x()), - shadow_frame.GetVRegLong(inst->VRegC_23x())); + bool success = DoLongRemainder(shadow_frame, inst->VRegA_23x(inst_data), + shadow_frame.GetVRegLong(inst->VRegB_23x()), + shadow_frame.GetVRegLong(inst->VRegC_23x())); POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2); } HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(AND_LONG) - shadow_frame.SetVRegLong(inst->VRegA_23x(), + shadow_frame.SetVRegLong(inst->VRegA_23x(inst_data), shadow_frame.GetVRegLong(inst->VRegB_23x()) & shadow_frame.GetVRegLong(inst->VRegC_23x())); ADVANCE(2); HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(OR_LONG) - shadow_frame.SetVRegLong(inst->VRegA_23x(), + shadow_frame.SetVRegLong(inst->VRegA_23x(inst_data), shadow_frame.GetVRegLong(inst->VRegB_23x()) | shadow_frame.GetVRegLong(inst->VRegC_23x())); ADVANCE(2); HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(XOR_LONG) - shadow_frame.SetVRegLong(inst->VRegA_23x(), + shadow_frame.SetVRegLong(inst->VRegA_23x(inst_data), shadow_frame.GetVRegLong(inst->VRegB_23x()) ^ shadow_frame.GetVRegLong(inst->VRegC_23x())); ADVANCE(2); HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(SHL_LONG) - shadow_frame.SetVRegLong(inst->VRegA_23x(), + shadow_frame.SetVRegLong(inst->VRegA_23x(inst_data), shadow_frame.GetVRegLong(inst->VRegB_23x()) << (shadow_frame.GetVReg(inst->VRegC_23x()) & 0x3f)); ADVANCE(2); HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(SHR_LONG) - shadow_frame.SetVRegLong(inst->VRegA_23x(), + shadow_frame.SetVRegLong(inst->VRegA_23x(inst_data), shadow_frame.GetVRegLong(inst->VRegB_23x()) >> (shadow_frame.GetVReg(inst->VRegC_23x()) & 0x3f)); ADVANCE(2); HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(USHR_LONG) - shadow_frame.SetVRegLong(inst->VRegA_23x(), + shadow_frame.SetVRegLong(inst->VRegA_23x(inst_data), static_cast(shadow_frame.GetVRegLong(inst->VRegB_23x())) >> (shadow_frame.GetVReg(inst->VRegC_23x()) & 0x3f)); ADVANCE(2); HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(ADD_FLOAT) - shadow_frame.SetVRegFloat(inst->VRegA_23x(), + shadow_frame.SetVRegFloat(inst->VRegA_23x(inst_data), shadow_frame.GetVRegFloat(inst->VRegB_23x()) + shadow_frame.GetVRegFloat(inst->VRegC_23x())); ADVANCE(2); HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(SUB_FLOAT) - shadow_frame.SetVRegFloat(inst->VRegA_23x(), + shadow_frame.SetVRegFloat(inst->VRegA_23x(inst_data), shadow_frame.GetVRegFloat(inst->VRegB_23x()) - shadow_frame.GetVRegFloat(inst->VRegC_23x())); ADVANCE(2); HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(MUL_FLOAT) - shadow_frame.SetVRegFloat(inst->VRegA_23x(), + shadow_frame.SetVRegFloat(inst->VRegA_23x(inst_data), shadow_frame.GetVRegFloat(inst->VRegB_23x()) * shadow_frame.GetVRegFloat(inst->VRegC_23x())); ADVANCE(2); HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(DIV_FLOAT) - shadow_frame.SetVRegFloat(inst->VRegA_23x(), + shadow_frame.SetVRegFloat(inst->VRegA_23x(inst_data), shadow_frame.GetVRegFloat(inst->VRegB_23x()) / shadow_frame.GetVRegFloat(inst->VRegC_23x())); ADVANCE(2); HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(REM_FLOAT) - shadow_frame.SetVRegFloat(inst->VRegA_23x(), + shadow_frame.SetVRegFloat(inst->VRegA_23x(inst_data), fmodf(shadow_frame.GetVRegFloat(inst->VRegB_23x()), shadow_frame.GetVRegFloat(inst->VRegC_23x()))); ADVANCE(2); HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(ADD_DOUBLE) - shadow_frame.SetVRegDouble(inst->VRegA_23x(), + shadow_frame.SetVRegDouble(inst->VRegA_23x(inst_data), shadow_frame.GetVRegDouble(inst->VRegB_23x()) + shadow_frame.GetVRegDouble(inst->VRegC_23x())); ADVANCE(2); HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(SUB_DOUBLE) - shadow_frame.SetVRegDouble(inst->VRegA_23x(), + shadow_frame.SetVRegDouble(inst->VRegA_23x(inst_data), shadow_frame.GetVRegDouble(inst->VRegB_23x()) - shadow_frame.GetVRegDouble(inst->VRegC_23x())); ADVANCE(2); HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(MUL_DOUBLE) - shadow_frame.SetVRegDouble(inst->VRegA_23x(), + shadow_frame.SetVRegDouble(inst->VRegA_23x(inst_data), shadow_frame.GetVRegDouble(inst->VRegB_23x()) * shadow_frame.GetVRegDouble(inst->VRegC_23x())); ADVANCE(2); HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(DIV_DOUBLE) - shadow_frame.SetVRegDouble(inst->VRegA_23x(), + shadow_frame.SetVRegDouble(inst->VRegA_23x(inst_data), shadow_frame.GetVRegDouble(inst->VRegB_23x()) / shadow_frame.GetVRegDouble(inst->VRegC_23x())); ADVANCE(2); HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(REM_DOUBLE) - shadow_frame.SetVRegDouble(inst->VRegA_23x(), + shadow_frame.SetVRegDouble(inst->VRegA_23x(inst_data), fmod(shadow_frame.GetVRegDouble(inst->VRegB_23x()), shadow_frame.GetVRegDouble(inst->VRegC_23x()))); ADVANCE(2); HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(ADD_INT_2ADDR) { - uint32_t vregA = inst->VRegA_12x(); + uint32_t vregA = inst->VRegA_12x(inst_data); shadow_frame.SetVReg(vregA, shadow_frame.GetVReg(vregA) + - shadow_frame.GetVReg(inst->VRegB_12x())); + shadow_frame.GetVReg(inst->VRegB_12x(inst_data))); ADVANCE(1); } HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(SUB_INT_2ADDR) { - uint32_t vregA = inst->VRegA_12x(); + uint32_t vregA = inst->VRegA_12x(inst_data); shadow_frame.SetVReg(vregA, shadow_frame.GetVReg(vregA) - - shadow_frame.GetVReg(inst->VRegB_12x())); + shadow_frame.GetVReg(inst->VRegB_12x(inst_data))); ADVANCE(1); } HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(MUL_INT_2ADDR) { - uint32_t vregA = inst->VRegA_12x(); + uint32_t vregA = inst->VRegA_12x(inst_data); shadow_frame.SetVReg(vregA, shadow_frame.GetVReg(vregA) * - shadow_frame.GetVReg(inst->VRegB_12x())); + shadow_frame.GetVReg(inst->VRegB_12x(inst_data))); ADVANCE(1); } HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(DIV_INT_2ADDR) { - uint32_t vregA = inst->VRegA_12x(); + uint32_t vregA = inst->VRegA_12x(inst_data); bool success = DoIntDivide(shadow_frame, vregA, shadow_frame.GetVReg(vregA), - shadow_frame.GetVReg(inst->VRegB_12x())); + shadow_frame.GetVReg(inst->VRegB_12x(inst_data))); POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 1); } HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(REM_INT_2ADDR) { - uint32_t vregA = inst->VRegA_12x(); + uint32_t vregA = inst->VRegA_12x(inst_data); bool success = DoIntRemainder(shadow_frame, vregA, shadow_frame.GetVReg(vregA), - shadow_frame.GetVReg(inst->VRegB_12x())); + shadow_frame.GetVReg(inst->VRegB_12x(inst_data))); POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 1); } HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(SHL_INT_2ADDR) { - uint32_t vregA = inst->VRegA_12x(); + uint32_t vregA = inst->VRegA_12x(inst_data); shadow_frame.SetVReg(vregA, shadow_frame.GetVReg(vregA) << - (shadow_frame.GetVReg(inst->VRegB_12x()) & 0x1f)); + (shadow_frame.GetVReg(inst->VRegB_12x(inst_data)) & 0x1f)); ADVANCE(1); } HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(SHR_INT_2ADDR) { - uint32_t vregA = inst->VRegA_12x(); + uint32_t vregA = inst->VRegA_12x(inst_data); shadow_frame.SetVReg(vregA, shadow_frame.GetVReg(vregA) >> - (shadow_frame.GetVReg(inst->VRegB_12x()) & 0x1f)); + (shadow_frame.GetVReg(inst->VRegB_12x(inst_data)) & 0x1f)); ADVANCE(1); } HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(USHR_INT_2ADDR) { - uint32_t vregA = inst->VRegA_12x(); + uint32_t vregA = inst->VRegA_12x(inst_data); shadow_frame.SetVReg(vregA, static_cast(shadow_frame.GetVReg(vregA)) >> - (shadow_frame.GetVReg(inst->VRegB_12x()) & 0x1f)); + (shadow_frame.GetVReg(inst->VRegB_12x(inst_data)) & 0x1f)); ADVANCE(1); } HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(AND_INT_2ADDR) { - uint32_t vregA = inst->VRegA_12x(); + uint32_t vregA = inst->VRegA_12x(inst_data); shadow_frame.SetVReg(vregA, shadow_frame.GetVReg(vregA) & - shadow_frame.GetVReg(inst->VRegB_12x())); + shadow_frame.GetVReg(inst->VRegB_12x(inst_data))); ADVANCE(1); } HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(OR_INT_2ADDR) { - uint32_t vregA = inst->VRegA_12x(); + uint32_t vregA = inst->VRegA_12x(inst_data); shadow_frame.SetVReg(vregA, shadow_frame.GetVReg(vregA) | - shadow_frame.GetVReg(inst->VRegB_12x())); + shadow_frame.GetVReg(inst->VRegB_12x(inst_data))); ADVANCE(1); } HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(XOR_INT_2ADDR) { - uint32_t vregA = inst->VRegA_12x(); + uint32_t vregA = inst->VRegA_12x(inst_data); shadow_frame.SetVReg(vregA, shadow_frame.GetVReg(vregA) ^ - shadow_frame.GetVReg(inst->VRegB_12x())); + shadow_frame.GetVReg(inst->VRegB_12x(inst_data))); ADVANCE(1); } HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(ADD_LONG_2ADDR) { - uint32_t vregA = inst->VRegA_12x(); + uint32_t vregA = inst->VRegA_12x(inst_data); shadow_frame.SetVRegLong(vregA, shadow_frame.GetVRegLong(vregA) + - shadow_frame.GetVRegLong(inst->VRegB_12x())); + shadow_frame.GetVRegLong(inst->VRegB_12x(inst_data))); ADVANCE(1); } HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(SUB_LONG_2ADDR) { - uint32_t vregA = inst->VRegA_12x(); + uint32_t vregA = inst->VRegA_12x(inst_data); shadow_frame.SetVRegLong(vregA, shadow_frame.GetVRegLong(vregA) - - shadow_frame.GetVRegLong(inst->VRegB_12x())); + shadow_frame.GetVRegLong(inst->VRegB_12x(inst_data))); ADVANCE(1); } HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(MUL_LONG_2ADDR) { - uint32_t vregA = inst->VRegA_12x(); + uint32_t vregA = inst->VRegA_12x(inst_data); shadow_frame.SetVRegLong(vregA, shadow_frame.GetVRegLong(vregA) * - shadow_frame.GetVRegLong(inst->VRegB_12x())); + shadow_frame.GetVRegLong(inst->VRegB_12x(inst_data))); ADVANCE(1); } HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(DIV_LONG_2ADDR) { - uint32_t vregA = inst->VRegA_12x(); + uint32_t vregA = inst->VRegA_12x(inst_data); bool success = DoLongDivide(shadow_frame, vregA, shadow_frame.GetVRegLong(vregA), - shadow_frame.GetVRegLong(inst->VRegB_12x())); + shadow_frame.GetVRegLong(inst->VRegB_12x(inst_data))); POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 1); } HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(REM_LONG_2ADDR) { - uint32_t vregA = inst->VRegA_12x(); + uint32_t vregA = inst->VRegA_12x(inst_data); bool success = DoLongRemainder(shadow_frame, vregA, shadow_frame.GetVRegLong(vregA), - shadow_frame.GetVRegLong(inst->VRegB_12x())); + shadow_frame.GetVRegLong(inst->VRegB_12x(inst_data))); POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 1); } HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(AND_LONG_2ADDR) { - uint32_t vregA = inst->VRegA_12x(); + uint32_t vregA = inst->VRegA_12x(inst_data); shadow_frame.SetVRegLong(vregA, shadow_frame.GetVRegLong(vregA) & - shadow_frame.GetVRegLong(inst->VRegB_12x())); + shadow_frame.GetVRegLong(inst->VRegB_12x(inst_data))); ADVANCE(1); } HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(OR_LONG_2ADDR) { - uint32_t vregA = inst->VRegA_12x(); + uint32_t vregA = inst->VRegA_12x(inst_data); shadow_frame.SetVRegLong(vregA, shadow_frame.GetVRegLong(vregA) | - shadow_frame.GetVRegLong(inst->VRegB_12x())); + shadow_frame.GetVRegLong(inst->VRegB_12x(inst_data))); ADVANCE(1); } HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(XOR_LONG_2ADDR) { - uint32_t vregA = inst->VRegA_12x(); + uint32_t vregA = inst->VRegA_12x(inst_data); shadow_frame.SetVRegLong(vregA, shadow_frame.GetVRegLong(vregA) ^ - shadow_frame.GetVRegLong(inst->VRegB_12x())); + shadow_frame.GetVRegLong(inst->VRegB_12x(inst_data))); ADVANCE(1); } HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(SHL_LONG_2ADDR) { - uint32_t vregA = inst->VRegA_12x(); + uint32_t vregA = inst->VRegA_12x(inst_data); shadow_frame.SetVRegLong(vregA, shadow_frame.GetVRegLong(vregA) << - (shadow_frame.GetVReg(inst->VRegB_12x()) & 0x3f)); + (shadow_frame.GetVReg(inst->VRegB_12x(inst_data)) & 0x3f)); ADVANCE(1); } HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(SHR_LONG_2ADDR) { - uint32_t vregA = inst->VRegA_12x(); + uint32_t vregA = inst->VRegA_12x(inst_data); shadow_frame.SetVRegLong(vregA, shadow_frame.GetVRegLong(vregA) >> - (shadow_frame.GetVReg(inst->VRegB_12x()) & 0x3f)); + (shadow_frame.GetVReg(inst->VRegB_12x(inst_data)) & 0x3f)); ADVANCE(1); } HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(USHR_LONG_2ADDR) { - uint32_t vregA = inst->VRegA_12x(); + uint32_t vregA = inst->VRegA_12x(inst_data); shadow_frame.SetVRegLong(vregA, static_cast(shadow_frame.GetVRegLong(vregA)) >> - (shadow_frame.GetVReg(inst->VRegB_12x()) & 0x3f)); + (shadow_frame.GetVReg(inst->VRegB_12x(inst_data)) & 0x3f)); ADVANCE(1); } HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(ADD_FLOAT_2ADDR) { - uint32_t vregA = inst->VRegA_12x(); + uint32_t vregA = inst->VRegA_12x(inst_data); shadow_frame.SetVRegFloat(vregA, shadow_frame.GetVRegFloat(vregA) + - shadow_frame.GetVRegFloat(inst->VRegB_12x())); + shadow_frame.GetVRegFloat(inst->VRegB_12x(inst_data))); ADVANCE(1); } HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(SUB_FLOAT_2ADDR) { - uint32_t vregA = inst->VRegA_12x(); + uint32_t vregA = inst->VRegA_12x(inst_data); shadow_frame.SetVRegFloat(vregA, shadow_frame.GetVRegFloat(vregA) - - shadow_frame.GetVRegFloat(inst->VRegB_12x())); + shadow_frame.GetVRegFloat(inst->VRegB_12x(inst_data))); ADVANCE(1); } HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(MUL_FLOAT_2ADDR) { - uint32_t vregA = inst->VRegA_12x(); + uint32_t vregA = inst->VRegA_12x(inst_data); shadow_frame.SetVRegFloat(vregA, shadow_frame.GetVRegFloat(vregA) * - shadow_frame.GetVRegFloat(inst->VRegB_12x())); + shadow_frame.GetVRegFloat(inst->VRegB_12x(inst_data))); ADVANCE(1); } HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(DIV_FLOAT_2ADDR) { - uint32_t vregA = inst->VRegA_12x(); + uint32_t vregA = inst->VRegA_12x(inst_data); shadow_frame.SetVRegFloat(vregA, shadow_frame.GetVRegFloat(vregA) / - shadow_frame.GetVRegFloat(inst->VRegB_12x())); + shadow_frame.GetVRegFloat(inst->VRegB_12x(inst_data))); ADVANCE(1); } HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(REM_FLOAT_2ADDR) { - uint32_t vregA = inst->VRegA_12x(); + uint32_t vregA = inst->VRegA_12x(inst_data); shadow_frame.SetVRegFloat(vregA, fmodf(shadow_frame.GetVRegFloat(vregA), - shadow_frame.GetVRegFloat(inst->VRegB_12x()))); + shadow_frame.GetVRegFloat(inst->VRegB_12x(inst_data)))); ADVANCE(1); } HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(ADD_DOUBLE_2ADDR) { - uint32_t vregA = inst->VRegA_12x(); + uint32_t vregA = inst->VRegA_12x(inst_data); shadow_frame.SetVRegDouble(vregA, shadow_frame.GetVRegDouble(vregA) + - shadow_frame.GetVRegDouble(inst->VRegB_12x())); + shadow_frame.GetVRegDouble(inst->VRegB_12x(inst_data))); ADVANCE(1); } HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(SUB_DOUBLE_2ADDR) { - uint32_t vregA = inst->VRegA_12x(); + uint32_t vregA = inst->VRegA_12x(inst_data); shadow_frame.SetVRegDouble(vregA, shadow_frame.GetVRegDouble(vregA) - - shadow_frame.GetVRegDouble(inst->VRegB_12x())); + shadow_frame.GetVRegDouble(inst->VRegB_12x(inst_data))); ADVANCE(1); } HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(MUL_DOUBLE_2ADDR) { - uint32_t vregA = inst->VRegA_12x(); + uint32_t vregA = inst->VRegA_12x(inst_data); shadow_frame.SetVRegDouble(vregA, shadow_frame.GetVRegDouble(vregA) * - shadow_frame.GetVRegDouble(inst->VRegB_12x())); + shadow_frame.GetVRegDouble(inst->VRegB_12x(inst_data))); ADVANCE(1); } HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(DIV_DOUBLE_2ADDR) { - uint32_t vregA = inst->VRegA_12x(); + uint32_t vregA = inst->VRegA_12x(inst_data); shadow_frame.SetVRegDouble(vregA, shadow_frame.GetVRegDouble(vregA) / - shadow_frame.GetVRegDouble(inst->VRegB_12x())); + shadow_frame.GetVRegDouble(inst->VRegB_12x(inst_data))); ADVANCE(1); } HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(REM_DOUBLE_2ADDR) { - uint32_t vregA = inst->VRegA_12x(); + uint32_t vregA = inst->VRegA_12x(inst_data); shadow_frame.SetVRegDouble(vregA, fmod(shadow_frame.GetVRegDouble(vregA), - shadow_frame.GetVRegDouble(inst->VRegB_12x()))); + shadow_frame.GetVRegDouble(inst->VRegB_12x(inst_data)))); ADVANCE(1); } HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(ADD_INT_LIT16) - shadow_frame.SetVReg(inst->VRegA_22s(), - shadow_frame.GetVReg(inst->VRegB_22s()) + + shadow_frame.SetVReg(inst->VRegA_22s(inst_data), + shadow_frame.GetVReg(inst->VRegB_22s(inst_data)) + inst->VRegC_22s()); ADVANCE(2); HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(RSUB_INT) - shadow_frame.SetVReg(inst->VRegA_22s(), + shadow_frame.SetVReg(inst->VRegA_22s(inst_data), inst->VRegC_22s() - - shadow_frame.GetVReg(inst->VRegB_22s())); + shadow_frame.GetVReg(inst->VRegB_22s(inst_data))); ADVANCE(2); HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(MUL_INT_LIT16) - shadow_frame.SetVReg(inst->VRegA_22s(), - shadow_frame.GetVReg(inst->VRegB_22s()) * + shadow_frame.SetVReg(inst->VRegA_22s(inst_data), + shadow_frame.GetVReg(inst->VRegB_22s(inst_data)) * inst->VRegC_22s()); ADVANCE(2); HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(DIV_INT_LIT16) { - bool success = DoIntDivide(shadow_frame, inst->VRegA_22s(), - shadow_frame.GetVReg(inst->VRegB_22s()), inst->VRegC_22s()); + 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, 2); } HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(REM_INT_LIT16) { - bool success = DoIntRemainder(shadow_frame, inst->VRegA_22s(), - shadow_frame.GetVReg(inst->VRegB_22s()), inst->VRegC_22s()); + 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, 2); } HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(AND_INT_LIT16) - shadow_frame.SetVReg(inst->VRegA_22s(), - shadow_frame.GetVReg(inst->VRegB_22s()) & + shadow_frame.SetVReg(inst->VRegA_22s(inst_data), + shadow_frame.GetVReg(inst->VRegB_22s(inst_data)) & inst->VRegC_22s()); ADVANCE(2); HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(OR_INT_LIT16) - shadow_frame.SetVReg(inst->VRegA_22s(), - shadow_frame.GetVReg(inst->VRegB_22s()) | + shadow_frame.SetVReg(inst->VRegA_22s(inst_data), + shadow_frame.GetVReg(inst->VRegB_22s(inst_data)) | inst->VRegC_22s()); ADVANCE(2); HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(XOR_INT_LIT16) - shadow_frame.SetVReg(inst->VRegA_22s(), - shadow_frame.GetVReg(inst->VRegB_22s()) ^ + shadow_frame.SetVReg(inst->VRegA_22s(inst_data), + shadow_frame.GetVReg(inst->VRegB_22s(inst_data)) ^ inst->VRegC_22s()); ADVANCE(2); HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(ADD_INT_LIT8) - shadow_frame.SetVReg(inst->VRegA_22b(), + shadow_frame.SetVReg(inst->VRegA_22b(inst_data), shadow_frame.GetVReg(inst->VRegB_22b()) + inst->VRegC_22b()); ADVANCE(2); HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(RSUB_INT_LIT8) - shadow_frame.SetVReg(inst->VRegA_22b(), + shadow_frame.SetVReg(inst->VRegA_22b(inst_data), inst->VRegC_22b() - shadow_frame.GetVReg(inst->VRegB_22b())); ADVANCE(2); HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(MUL_INT_LIT8) - shadow_frame.SetVReg(inst->VRegA_22b(), + shadow_frame.SetVReg(inst->VRegA_22b(inst_data), shadow_frame.GetVReg(inst->VRegB_22b()) * inst->VRegC_22b()); ADVANCE(2); HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(DIV_INT_LIT8) { - bool success = DoIntDivide(shadow_frame, inst->VRegA_22b(), - shadow_frame.GetVReg(inst->VRegB_22b()), inst->VRegC_22b()); + bool success = DoIntDivide(shadow_frame, inst->VRegA_22b(inst_data), + shadow_frame.GetVReg(inst->VRegB_22b()), inst->VRegC_22b()); POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2); } HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(REM_INT_LIT8) { - bool success = DoIntRemainder(shadow_frame, inst->VRegA_22b(), - shadow_frame.GetVReg(inst->VRegB_22b()), inst->VRegC_22b()); + bool success = DoIntRemainder(shadow_frame, inst->VRegA_22b(inst_data), + shadow_frame.GetVReg(inst->VRegB_22b()), inst->VRegC_22b()); POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2); } HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(AND_INT_LIT8) - shadow_frame.SetVReg(inst->VRegA_22b(), + shadow_frame.SetVReg(inst->VRegA_22b(inst_data), shadow_frame.GetVReg(inst->VRegB_22b()) & inst->VRegC_22b()); ADVANCE(2); HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(OR_INT_LIT8) - shadow_frame.SetVReg(inst->VRegA_22b(), + shadow_frame.SetVReg(inst->VRegA_22b(inst_data), shadow_frame.GetVReg(inst->VRegB_22b()) | inst->VRegC_22b()); ADVANCE(2); HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(XOR_INT_LIT8) - shadow_frame.SetVReg(inst->VRegA_22b(), + shadow_frame.SetVReg(inst->VRegA_22b(inst_data), shadow_frame.GetVReg(inst->VRegB_22b()) ^ inst->VRegC_22b()); ADVANCE(2); HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(SHL_INT_LIT8) - shadow_frame.SetVReg(inst->VRegA_22b(), + shadow_frame.SetVReg(inst->VRegA_22b(inst_data), shadow_frame.GetVReg(inst->VRegB_22b()) << (inst->VRegC_22b() & 0x1f)); ADVANCE(2); HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(SHR_INT_LIT8) - shadow_frame.SetVReg(inst->VRegA_22b(), + shadow_frame.SetVReg(inst->VRegA_22b(inst_data), shadow_frame.GetVReg(inst->VRegB_22b()) >> (inst->VRegC_22b() & 0x1f)); ADVANCE(2); HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(USHR_INT_LIT8) - shadow_frame.SetVReg(inst->VRegA_22b(), + shadow_frame.SetVReg(inst->VRegA_22b(inst_data), static_cast(shadow_frame.GetVReg(inst->VRegB_22b())) >> (inst->VRegC_22b() & 0x1f)); ADVANCE(2); diff --git a/runtime/interpreter/interpreter_switch_impl.cc b/runtime/interpreter/interpreter_switch_impl.cc index 01a0e4b4571..b2e480f3a41 100644 --- a/runtime/interpreter/interpreter_switch_impl.cc +++ b/runtime/interpreter/interpreter_switch_impl.cc @@ -69,6 +69,7 @@ static JValue ExecuteSwitchImpl(Thread* self, MethodHelper& mh, const DexFile::C } const uint16_t* const insns = code_item->insns_; const Instruction* inst = Instruction::At(insns + dex_pc); + uint16_t inst_data; while (true) { dex_pc = inst->GetDexPc(insns); shadow_frame.SetDexPC(dex_pc); @@ -77,20 +78,21 @@ static JValue ExecuteSwitchImpl(Thread* self, MethodHelper& mh, const DexFile::C shadow_frame.GetMethod(), dex_pc); } TraceExecution(shadow_frame, inst, dex_pc, mh); - switch (inst->Opcode()) { + 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(), - shadow_frame.GetVReg(inst->VRegB_12x())); + 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(), + shadow_frame.SetVReg(inst->VRegA_22x(inst_data), shadow_frame.GetVReg(inst->VRegB_22x())); inst = inst->Next_2xx(); break; @@ -102,13 +104,13 @@ static JValue ExecuteSwitchImpl(Thread* self, MethodHelper& mh, const DexFile::C break; case Instruction::MOVE_WIDE: PREAMBLE(); - shadow_frame.SetVRegLong(inst->VRegA_12x(), - shadow_frame.GetVRegLong(inst->VRegB_12x())); + 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(), + shadow_frame.SetVRegLong(inst->VRegA_22x(inst_data), shadow_frame.GetVRegLong(inst->VRegB_22x())); inst = inst->Next_2xx(); break; @@ -120,13 +122,13 @@ static JValue ExecuteSwitchImpl(Thread* self, MethodHelper& mh, const DexFile::C break; case Instruction::MOVE_OBJECT: PREAMBLE(); - shadow_frame.SetVRegReference(inst->VRegA_12x(), - shadow_frame.GetVRegReference(inst->VRegB_12x())); + 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(), + shadow_frame.SetVRegReference(inst->VRegA_22x(inst_data), shadow_frame.GetVRegReference(inst->VRegB_22x())); inst = inst->Next_2xx(); break; @@ -138,24 +140,24 @@ static JValue ExecuteSwitchImpl(Thread* self, MethodHelper& mh, const DexFile::C break; case Instruction::MOVE_RESULT: PREAMBLE(); - shadow_frame.SetVReg(inst->VRegA_11x(), result_register.GetI()); + 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(), result_register.GetJ()); + shadow_frame.SetVRegLong(inst->VRegA_11x(inst_data), result_register.GetJ()); inst = inst->Next_1xx(); break; case Instruction::MOVE_RESULT_OBJECT: PREAMBLE(); - shadow_frame.SetVRegReference(inst->VRegA_11x(), result_register.GetL()); + shadow_frame.SetVRegReference(inst->VRegA_11x(inst_data), result_register.GetL()); inst = inst->Next_1xx(); break; case Instruction::MOVE_EXCEPTION: { PREAMBLE(); Throwable* exception = self->GetException(NULL); self->ClearException(); - shadow_frame.SetVRegReference(inst->VRegA_11x(), exception); + shadow_frame.SetVRegReference(inst->VRegA_11x(inst_data), exception); inst = inst->Next_1xx(); break; } @@ -196,7 +198,7 @@ static JValue ExecuteSwitchImpl(Thread* self, MethodHelper& mh, const DexFile::C PREAMBLE(); JValue result; result.SetJ(0); - result.SetI(shadow_frame.GetVReg(inst->VRegA_11x())); + result.SetI(shadow_frame.GetVReg(inst->VRegA_11x(inst_data))); if (UNLIKELY(self->TestAllFlags())) { CheckSuspend(self); } @@ -210,7 +212,7 @@ static JValue ExecuteSwitchImpl(Thread* self, MethodHelper& mh, const DexFile::C case Instruction::RETURN_WIDE: { PREAMBLE(); JValue result; - result.SetJ(shadow_frame.GetVRegLong(inst->VRegA_11x())); + result.SetJ(shadow_frame.GetVRegLong(inst->VRegA_11x(inst_data))); if (UNLIKELY(self->TestAllFlags())) { CheckSuspend(self); } @@ -225,7 +227,7 @@ static JValue ExecuteSwitchImpl(Thread* self, MethodHelper& mh, const DexFile::C PREAMBLE(); JValue result; result.SetJ(0); - result.SetL(shadow_frame.GetVRegReference(inst->VRegA_11x())); + result.SetL(shadow_frame.GetVRegReference(inst->VRegA_11x(inst_data))); if (UNLIKELY(self->TestAllFlags())) { CheckSuspend(self); } @@ -238,8 +240,8 @@ static JValue ExecuteSwitchImpl(Thread* self, MethodHelper& mh, const DexFile::C } case Instruction::CONST_4: { PREAMBLE(); - uint4_t dst = inst->VRegA_11n(); - int4_t val = inst->VRegB_11n(); + 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, NULL); @@ -249,7 +251,7 @@ static JValue ExecuteSwitchImpl(Thread* self, MethodHelper& mh, const DexFile::C } case Instruction::CONST_16: { PREAMBLE(); - uint8_t dst = inst->VRegA_21s(); + uint8_t dst = inst->VRegA_21s(inst_data); int16_t val = inst->VRegB_21s(); shadow_frame.SetVReg(dst, val); if (val == 0) { @@ -260,7 +262,7 @@ static JValue ExecuteSwitchImpl(Thread* self, MethodHelper& mh, const DexFile::C } case Instruction::CONST: { PREAMBLE(); - uint8_t dst = inst->VRegA_31i(); + uint8_t dst = inst->VRegA_31i(inst_data); int32_t val = inst->VRegB_31i(); shadow_frame.SetVReg(dst, val); if (val == 0) { @@ -271,7 +273,7 @@ static JValue ExecuteSwitchImpl(Thread* self, MethodHelper& mh, const DexFile::C } case Instruction::CONST_HIGH16: { PREAMBLE(); - uint8_t dst = inst->VRegA_21h(); + uint8_t dst = inst->VRegA_21h(inst_data); int32_t val = static_cast(inst->VRegB_21h() << 16); shadow_frame.SetVReg(dst, val); if (val == 0) { @@ -282,21 +284,21 @@ static JValue ExecuteSwitchImpl(Thread* self, MethodHelper& mh, const DexFile::C } case Instruction::CONST_WIDE_16: PREAMBLE(); - shadow_frame.SetVRegLong(inst->VRegA_21s(), inst->VRegB_21s()); + 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->VRegB_31i()); + 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->VRegB_51l()); + shadow_frame.SetVRegLong(inst->VRegA_51l(inst_data), inst->VRegB_51l()); inst = inst->Next_51l(); break; case Instruction::CONST_WIDE_HIGH16: - shadow_frame.SetVRegLong(inst->VRegA_21h(), + shadow_frame.SetVRegLong(inst->VRegA_21h(inst_data), static_cast(inst->VRegB_21h()) << 48); inst = inst->Next_2xx(); break; @@ -306,7 +308,7 @@ static JValue ExecuteSwitchImpl(Thread* self, MethodHelper& mh, const DexFile::C if (UNLIKELY(s == NULL)) { HANDLE_PENDING_EXCEPTION(); } else { - shadow_frame.SetVRegReference(inst->VRegA_21c(), s); + shadow_frame.SetVRegReference(inst->VRegA_21c(inst_data), s); inst = inst->Next_2xx(); } break; @@ -317,7 +319,7 @@ static JValue ExecuteSwitchImpl(Thread* self, MethodHelper& mh, const DexFile::C if (UNLIKELY(s == NULL)) { HANDLE_PENDING_EXCEPTION(); } else { - shadow_frame.SetVRegReference(inst->VRegA_31c(), s); + shadow_frame.SetVRegReference(inst->VRegA_31c(inst_data), s); inst = inst->Next_3xx(); } break; @@ -329,14 +331,14 @@ static JValue ExecuteSwitchImpl(Thread* self, MethodHelper& mh, const DexFile::C if (UNLIKELY(c == NULL)) { HANDLE_PENDING_EXCEPTION(); } else { - shadow_frame.SetVRegReference(inst->VRegA_21c(), c); + shadow_frame.SetVRegReference(inst->VRegA_21c(inst_data), c); inst = inst->Next_2xx(); } break; } case Instruction::MONITOR_ENTER: { PREAMBLE(); - Object* obj = shadow_frame.GetVRegReference(inst->VRegA_11x()); + Object* obj = shadow_frame.GetVRegReference(inst->VRegA_11x(inst_data)); if (UNLIKELY(obj == NULL)) { ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow()); HANDLE_PENDING_EXCEPTION(); @@ -348,7 +350,7 @@ static JValue ExecuteSwitchImpl(Thread* self, MethodHelper& mh, const DexFile::C } case Instruction::MONITOR_EXIT: { PREAMBLE(); - Object* obj = shadow_frame.GetVRegReference(inst->VRegA_11x()); + Object* obj = shadow_frame.GetVRegReference(inst->VRegA_11x(inst_data)); if (UNLIKELY(obj == NULL)) { ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow()); HANDLE_PENDING_EXCEPTION(); @@ -365,7 +367,7 @@ static JValue ExecuteSwitchImpl(Thread* self, MethodHelper& mh, const DexFile::C if (UNLIKELY(c == NULL)) { HANDLE_PENDING_EXCEPTION(); } else { - Object* obj = shadow_frame.GetVRegReference(inst->VRegA_21c()); + Object* obj = shadow_frame.GetVRegReference(inst->VRegA_21c(inst_data)); if (UNLIKELY(obj != NULL && !obj->InstanceOf(c))) { ThrowClassCastException(c, obj->GetClass()); HANDLE_PENDING_EXCEPTION(); @@ -382,20 +384,20 @@ static JValue ExecuteSwitchImpl(Thread* self, MethodHelper& mh, const DexFile::C if (UNLIKELY(c == NULL)) { HANDLE_PENDING_EXCEPTION(); } else { - Object* obj = shadow_frame.GetVRegReference(inst->VRegB_22c()); - shadow_frame.SetVReg(inst->VRegA_22c(), (obj != NULL && obj->InstanceOf(c)) ? 1 : 0); + Object* obj = shadow_frame.GetVRegReference(inst->VRegB_22c(inst_data)); + shadow_frame.SetVReg(inst->VRegA_22c(inst_data), (obj != NULL && obj->InstanceOf(c)) ? 1 : 0); inst = inst->Next_2xx(); } break; } case Instruction::ARRAY_LENGTH: { PREAMBLE(); - Object* array = shadow_frame.GetVRegReference(inst->VRegB_12x()); + Object* array = shadow_frame.GetVRegReference(inst->VRegB_12x(inst_data)); if (UNLIKELY(array == NULL)) { ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow()); HANDLE_PENDING_EXCEPTION(); } else { - shadow_frame.SetVReg(inst->VRegA_12x(), array->AsArray()->GetLength()); + shadow_frame.SetVReg(inst->VRegA_12x(inst_data), array->AsArray()->GetLength()); inst = inst->Next_1xx(); } break; @@ -407,20 +409,20 @@ static JValue ExecuteSwitchImpl(Thread* self, MethodHelper& mh, const DexFile::C if (UNLIKELY(obj == NULL)) { HANDLE_PENDING_EXCEPTION(); } else { - shadow_frame.SetVRegReference(inst->VRegA_21c(), obj); + 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()); + int32_t length = shadow_frame.GetVReg(inst->VRegB_22c(inst_data)); Object* obj = AllocArrayFromCode(inst->VRegC_22c(), shadow_frame.GetMethod(), length, self, do_access_check); if (UNLIKELY(obj == NULL)) { HANDLE_PENDING_EXCEPTION(); } else { - shadow_frame.SetVRegReference(inst->VRegA_22c(), obj); + shadow_frame.SetVRegReference(inst->VRegA_22c(inst_data), obj); inst = inst->Next_2xx(); } break; @@ -441,7 +443,7 @@ static JValue ExecuteSwitchImpl(Thread* self, MethodHelper& mh, const DexFile::C } case Instruction::FILL_ARRAY_DATA: { PREAMBLE(); - Object* obj = shadow_frame.GetVRegReference(inst->VRegA_31t()); + Object* obj = shadow_frame.GetVRegReference(inst->VRegA_31t(inst_data)); if (UNLIKELY(obj == NULL)) { ThrowNullPointerException(NULL, "null array in FILL_ARRAY_DATA"); HANDLE_PENDING_EXCEPTION(); @@ -467,7 +469,7 @@ static JValue ExecuteSwitchImpl(Thread* self, MethodHelper& mh, const DexFile::C } case Instruction::THROW: { PREAMBLE(); - Object* exception = shadow_frame.GetVRegReference(inst->VRegA_11x()); + Object* exception = shadow_frame.GetVRegReference(inst->VRegA_11x(inst_data)); if (UNLIKELY(exception == NULL)) { ThrowNullPointerException(NULL, "throw with null exception"); } else { @@ -478,7 +480,7 @@ static JValue ExecuteSwitchImpl(Thread* self, MethodHelper& mh, const DexFile::C } case Instruction::GOTO: { PREAMBLE(); - int8_t offset = inst->VRegA_10t(); + int8_t offset = inst->VRegA_10t(inst_data); if (IsBackwardBranch(offset)) { if (UNLIKELY(self->TestAllFlags())) { CheckSuspend(self); @@ -511,7 +513,7 @@ static JValue ExecuteSwitchImpl(Thread* self, MethodHelper& mh, const DexFile::C } case Instruction::PACKED_SWITCH: { PREAMBLE(); - int32_t offset = DoPackedSwitch(inst, shadow_frame); + int32_t offset = DoPackedSwitch(inst, shadow_frame, inst_data); if (IsBackwardBranch(offset)) { if (UNLIKELY(self->TestAllFlags())) { CheckSuspend(self); @@ -522,7 +524,7 @@ static JValue ExecuteSwitchImpl(Thread* self, MethodHelper& mh, const DexFile::C } case Instruction::SPARSE_SWITCH: { PREAMBLE(); - int32_t offset = DoSparseSwitch(inst, shadow_frame); + int32_t offset = DoSparseSwitch(inst, shadow_frame, inst_data); if (IsBackwardBranch(offset)) { if (UNLIKELY(self->TestAllFlags())) { CheckSuspend(self); @@ -543,7 +545,7 @@ static JValue ExecuteSwitchImpl(Thread* self, MethodHelper& mh, const DexFile::C } else { result = -1; } - shadow_frame.SetVReg(inst->VRegA_23x(), result); + shadow_frame.SetVReg(inst->VRegA_23x(inst_data), result); inst = inst->Next_2xx(); break; } @@ -559,7 +561,7 @@ static JValue ExecuteSwitchImpl(Thread* self, MethodHelper& mh, const DexFile::C } else { result = 1; } - shadow_frame.SetVReg(inst->VRegA_23x(), result); + shadow_frame.SetVReg(inst->VRegA_23x(inst_data), result); inst = inst->Next_2xx(); break; } @@ -575,7 +577,7 @@ static JValue ExecuteSwitchImpl(Thread* self, MethodHelper& mh, const DexFile::C } else { result = -1; } - shadow_frame.SetVReg(inst->VRegA_23x(), result); + shadow_frame.SetVReg(inst->VRegA_23x(inst_data), result); inst = inst->Next_2xx(); break; } @@ -592,7 +594,7 @@ static JValue ExecuteSwitchImpl(Thread* self, MethodHelper& mh, const DexFile::C } else { result = 1; } - shadow_frame.SetVReg(inst->VRegA_23x(), result); + shadow_frame.SetVReg(inst->VRegA_23x(inst_data), result); inst = inst->Next_2xx(); break; } @@ -608,13 +610,13 @@ static JValue ExecuteSwitchImpl(Thread* self, MethodHelper& mh, const DexFile::C } else { result = -1; } - shadow_frame.SetVReg(inst->VRegA_23x(), result); + 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()) == shadow_frame.GetVReg(inst->VRegB_22t())) { + if (shadow_frame.GetVReg(inst->VRegA_22t(inst_data)) == shadow_frame.GetVReg(inst->VRegB_22t(inst_data))) { int16_t offset = inst->VRegC_22t(); if (IsBackwardBranch(offset)) { if (UNLIKELY(self->TestAllFlags())) { @@ -629,7 +631,7 @@ static JValue ExecuteSwitchImpl(Thread* self, MethodHelper& mh, const DexFile::C } case Instruction::IF_NE: { PREAMBLE(); - if (shadow_frame.GetVReg(inst->VRegA_22t()) != shadow_frame.GetVReg(inst->VRegB_22t())) { + if (shadow_frame.GetVReg(inst->VRegA_22t(inst_data)) != shadow_frame.GetVReg(inst->VRegB_22t(inst_data))) { int16_t offset = inst->VRegC_22t(); if (IsBackwardBranch(offset)) { if (UNLIKELY(self->TestAllFlags())) { @@ -644,7 +646,7 @@ static JValue ExecuteSwitchImpl(Thread* self, MethodHelper& mh, const DexFile::C } case Instruction::IF_LT: { PREAMBLE(); - if (shadow_frame.GetVReg(inst->VRegA_22t()) < shadow_frame.GetVReg(inst->VRegB_22t())) { + if (shadow_frame.GetVReg(inst->VRegA_22t(inst_data)) < shadow_frame.GetVReg(inst->VRegB_22t(inst_data))) { int16_t offset = inst->VRegC_22t(); if (IsBackwardBranch(offset)) { if (UNLIKELY(self->TestAllFlags())) { @@ -659,7 +661,7 @@ static JValue ExecuteSwitchImpl(Thread* self, MethodHelper& mh, const DexFile::C } case Instruction::IF_GE: { PREAMBLE(); - if (shadow_frame.GetVReg(inst->VRegA_22t()) >= shadow_frame.GetVReg(inst->VRegB_22t())) { + if (shadow_frame.GetVReg(inst->VRegA_22t(inst_data)) >= shadow_frame.GetVReg(inst->VRegB_22t(inst_data))) { int16_t offset = inst->VRegC_22t(); if (IsBackwardBranch(offset)) { if (UNLIKELY(self->TestAllFlags())) { @@ -674,7 +676,7 @@ static JValue ExecuteSwitchImpl(Thread* self, MethodHelper& mh, const DexFile::C } case Instruction::IF_GT: { PREAMBLE(); - if (shadow_frame.GetVReg(inst->VRegA_22t()) > shadow_frame.GetVReg(inst->VRegB_22t())) { + if (shadow_frame.GetVReg(inst->VRegA_22t(inst_data)) > shadow_frame.GetVReg(inst->VRegB_22t(inst_data))) { int16_t offset = inst->VRegC_22t(); if (IsBackwardBranch(offset)) { if (UNLIKELY(self->TestAllFlags())) { @@ -689,7 +691,7 @@ static JValue ExecuteSwitchImpl(Thread* self, MethodHelper& mh, const DexFile::C } case Instruction::IF_LE: { PREAMBLE(); - if (shadow_frame.GetVReg(inst->VRegA_22t()) <= shadow_frame.GetVReg(inst->VRegB_22t())) { + if (shadow_frame.GetVReg(inst->VRegA_22t(inst_data)) <= shadow_frame.GetVReg(inst->VRegB_22t(inst_data))) { int16_t offset = inst->VRegC_22t(); if (IsBackwardBranch(offset)) { if (UNLIKELY(self->TestAllFlags())) { @@ -704,7 +706,7 @@ static JValue ExecuteSwitchImpl(Thread* self, MethodHelper& mh, const DexFile::C } case Instruction::IF_EQZ: { PREAMBLE(); - if (shadow_frame.GetVReg(inst->VRegA_21t()) == 0) { + if (shadow_frame.GetVReg(inst->VRegA_21t(inst_data)) == 0) { int16_t offset = inst->VRegB_21t(); if (IsBackwardBranch(offset)) { if (UNLIKELY(self->TestAllFlags())) { @@ -719,7 +721,7 @@ static JValue ExecuteSwitchImpl(Thread* self, MethodHelper& mh, const DexFile::C } case Instruction::IF_NEZ: { PREAMBLE(); - if (shadow_frame.GetVReg(inst->VRegA_21t()) != 0) { + if (shadow_frame.GetVReg(inst->VRegA_21t(inst_data)) != 0) { int16_t offset = inst->VRegB_21t(); if (IsBackwardBranch(offset)) { if (UNLIKELY(self->TestAllFlags())) { @@ -734,7 +736,7 @@ static JValue ExecuteSwitchImpl(Thread* self, MethodHelper& mh, const DexFile::C } case Instruction::IF_LTZ: { PREAMBLE(); - if (shadow_frame.GetVReg(inst->VRegA_21t()) < 0) { + if (shadow_frame.GetVReg(inst->VRegA_21t(inst_data)) < 0) { int16_t offset = inst->VRegB_21t(); if (IsBackwardBranch(offset)) { if (UNLIKELY(self->TestAllFlags())) { @@ -749,7 +751,7 @@ static JValue ExecuteSwitchImpl(Thread* self, MethodHelper& mh, const DexFile::C } case Instruction::IF_GEZ: { PREAMBLE(); - if (shadow_frame.GetVReg(inst->VRegA_21t()) >= 0) { + if (shadow_frame.GetVReg(inst->VRegA_21t(inst_data)) >= 0) { int16_t offset = inst->VRegB_21t(); if (IsBackwardBranch(offset)) { if (UNLIKELY(self->TestAllFlags())) { @@ -764,7 +766,7 @@ static JValue ExecuteSwitchImpl(Thread* self, MethodHelper& mh, const DexFile::C } case Instruction::IF_GTZ: { PREAMBLE(); - if (shadow_frame.GetVReg(inst->VRegA_21t()) > 0) { + if (shadow_frame.GetVReg(inst->VRegA_21t(inst_data)) > 0) { int16_t offset = inst->VRegB_21t(); if (IsBackwardBranch(offset)) { if (UNLIKELY(self->TestAllFlags())) { @@ -779,7 +781,7 @@ static JValue ExecuteSwitchImpl(Thread* self, MethodHelper& mh, const DexFile::C } case Instruction::IF_LEZ: { PREAMBLE(); - if (shadow_frame.GetVReg(inst->VRegA_21t()) <= 0) { + if (shadow_frame.GetVReg(inst->VRegA_21t(inst_data)) <= 0) { int16_t offset = inst->VRegB_21t(); if (IsBackwardBranch(offset)) { if (UNLIKELY(self->TestAllFlags())) { @@ -803,7 +805,7 @@ static JValue ExecuteSwitchImpl(Thread* self, MethodHelper& mh, const DexFile::C int32_t index = shadow_frame.GetVReg(inst->VRegC_23x()); BooleanArray* array = a->AsBooleanArray(); if (LIKELY(array->IsValidIndex(index))) { - shadow_frame.SetVReg(inst->VRegA_23x(), array->GetData()[index]); + shadow_frame.SetVReg(inst->VRegA_23x(inst_data), array->GetData()[index]); inst = inst->Next_2xx(); } else { HANDLE_PENDING_EXCEPTION(); @@ -821,7 +823,7 @@ static JValue ExecuteSwitchImpl(Thread* self, MethodHelper& mh, const DexFile::C int32_t index = shadow_frame.GetVReg(inst->VRegC_23x()); ByteArray* array = a->AsByteArray(); if (LIKELY(array->IsValidIndex(index))) { - shadow_frame.SetVReg(inst->VRegA_23x(), array->GetData()[index]); + shadow_frame.SetVReg(inst->VRegA_23x(inst_data), array->GetData()[index]); inst = inst->Next_2xx(); } else { HANDLE_PENDING_EXCEPTION(); @@ -839,7 +841,7 @@ static JValue ExecuteSwitchImpl(Thread* self, MethodHelper& mh, const DexFile::C int32_t index = shadow_frame.GetVReg(inst->VRegC_23x()); CharArray* array = a->AsCharArray(); if (LIKELY(array->IsValidIndex(index))) { - shadow_frame.SetVReg(inst->VRegA_23x(), array->GetData()[index]); + shadow_frame.SetVReg(inst->VRegA_23x(inst_data), array->GetData()[index]); inst = inst->Next_2xx(); } else { HANDLE_PENDING_EXCEPTION(); @@ -857,7 +859,7 @@ static JValue ExecuteSwitchImpl(Thread* self, MethodHelper& mh, const DexFile::C int32_t index = shadow_frame.GetVReg(inst->VRegC_23x()); ShortArray* array = a->AsShortArray(); if (LIKELY(array->IsValidIndex(index))) { - shadow_frame.SetVReg(inst->VRegA_23x(), array->GetData()[index]); + shadow_frame.SetVReg(inst->VRegA_23x(inst_data), array->GetData()[index]); inst = inst->Next_2xx(); } else { HANDLE_PENDING_EXCEPTION(); @@ -875,7 +877,7 @@ static JValue ExecuteSwitchImpl(Thread* self, MethodHelper& mh, const DexFile::C int32_t index = shadow_frame.GetVReg(inst->VRegC_23x()); IntArray* array = a->AsIntArray(); if (LIKELY(array->IsValidIndex(index))) { - shadow_frame.SetVReg(inst->VRegA_23x(), array->GetData()[index]); + shadow_frame.SetVReg(inst->VRegA_23x(inst_data), array->GetData()[index]); inst = inst->Next_2xx(); } else { HANDLE_PENDING_EXCEPTION(); @@ -893,7 +895,7 @@ static JValue ExecuteSwitchImpl(Thread* self, MethodHelper& mh, const DexFile::C int32_t index = shadow_frame.GetVReg(inst->VRegC_23x()); LongArray* array = a->AsLongArray(); if (LIKELY(array->IsValidIndex(index))) { - shadow_frame.SetVRegLong(inst->VRegA_23x(), array->GetData()[index]); + shadow_frame.SetVRegLong(inst->VRegA_23x(inst_data), array->GetData()[index]); inst = inst->Next_2xx(); } else { HANDLE_PENDING_EXCEPTION(); @@ -911,7 +913,7 @@ static JValue ExecuteSwitchImpl(Thread* self, MethodHelper& mh, const DexFile::C int32_t index = shadow_frame.GetVReg(inst->VRegC_23x()); ObjectArray* array = a->AsObjectArray(); if (LIKELY(array->IsValidIndex(index))) { - shadow_frame.SetVRegReference(inst->VRegA_23x(), array->GetWithoutChecks(index)); + shadow_frame.SetVRegReference(inst->VRegA_23x(inst_data), array->GetWithoutChecks(index)); inst = inst->Next_2xx(); } else { HANDLE_PENDING_EXCEPTION(); @@ -926,7 +928,7 @@ static JValue ExecuteSwitchImpl(Thread* self, MethodHelper& mh, const DexFile::C HANDLE_PENDING_EXCEPTION(); break; } - uint8_t val = shadow_frame.GetVReg(inst->VRegA_23x()); + uint8_t val = shadow_frame.GetVReg(inst->VRegA_23x(inst_data)); int32_t index = shadow_frame.GetVReg(inst->VRegC_23x()); BooleanArray* array = a->AsBooleanArray(); if (LIKELY(array->IsValidIndex(index))) { @@ -945,7 +947,7 @@ static JValue ExecuteSwitchImpl(Thread* self, MethodHelper& mh, const DexFile::C HANDLE_PENDING_EXCEPTION(); break; } - int8_t val = shadow_frame.GetVReg(inst->VRegA_23x()); + int8_t val = shadow_frame.GetVReg(inst->VRegA_23x(inst_data)); int32_t index = shadow_frame.GetVReg(inst->VRegC_23x()); ByteArray* array = a->AsByteArray(); if (LIKELY(array->IsValidIndex(index))) { @@ -964,7 +966,7 @@ static JValue ExecuteSwitchImpl(Thread* self, MethodHelper& mh, const DexFile::C HANDLE_PENDING_EXCEPTION(); break; } - uint16_t val = shadow_frame.GetVReg(inst->VRegA_23x()); + uint16_t val = shadow_frame.GetVReg(inst->VRegA_23x(inst_data)); int32_t index = shadow_frame.GetVReg(inst->VRegC_23x()); CharArray* array = a->AsCharArray(); if (LIKELY(array->IsValidIndex(index))) { @@ -983,7 +985,7 @@ static JValue ExecuteSwitchImpl(Thread* self, MethodHelper& mh, const DexFile::C HANDLE_PENDING_EXCEPTION(); break; } - int16_t val = shadow_frame.GetVReg(inst->VRegA_23x()); + int16_t val = shadow_frame.GetVReg(inst->VRegA_23x(inst_data)); int32_t index = shadow_frame.GetVReg(inst->VRegC_23x()); ShortArray* array = a->AsShortArray(); if (LIKELY(array->IsValidIndex(index))) { @@ -1002,7 +1004,7 @@ static JValue ExecuteSwitchImpl(Thread* self, MethodHelper& mh, const DexFile::C HANDLE_PENDING_EXCEPTION(); break; } - int32_t val = shadow_frame.GetVReg(inst->VRegA_23x()); + int32_t val = shadow_frame.GetVReg(inst->VRegA_23x(inst_data)); int32_t index = shadow_frame.GetVReg(inst->VRegC_23x()); IntArray* array = a->AsIntArray(); if (LIKELY(array->IsValidIndex(index))) { @@ -1021,7 +1023,7 @@ static JValue ExecuteSwitchImpl(Thread* self, MethodHelper& mh, const DexFile::C HANDLE_PENDING_EXCEPTION(); break; } - int64_t val = shadow_frame.GetVRegLong(inst->VRegA_23x()); + int64_t val = shadow_frame.GetVRegLong(inst->VRegA_23x(inst_data)); int32_t index = shadow_frame.GetVReg(inst->VRegC_23x()); LongArray* array = a->AsLongArray(); if (LIKELY(array->IsValidIndex(index))) { @@ -1041,7 +1043,7 @@ static JValue ExecuteSwitchImpl(Thread* self, MethodHelper& mh, const DexFile::C break; } int32_t index = shadow_frame.GetVReg(inst->VRegC_23x()); - Object* val = shadow_frame.GetVRegReference(inst->VRegA_23x()); + Object* val = shadow_frame.GetVRegReference(inst->VRegA_23x(inst_data)); ObjectArray* array = a->AsObjectArray(); if (LIKELY(array->IsValidIndex(index) && array->CheckAssignable(val))) { array->SetWithoutChecks(index, val); @@ -1053,205 +1055,205 @@ static JValue ExecuteSwitchImpl(Thread* self, MethodHelper& mh, const DexFile::C } case Instruction::IGET_BOOLEAN: { PREAMBLE(); - bool success = DoFieldGet(self, shadow_frame, inst); + bool success = DoFieldGet(self, shadow_frame, inst, inst_data); POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); break; } case Instruction::IGET_BYTE: { PREAMBLE(); - bool success = DoFieldGet(self, shadow_frame, inst); + bool success = DoFieldGet(self, shadow_frame, inst, inst_data); POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); break; } case Instruction::IGET_CHAR: { PREAMBLE(); - bool success = DoFieldGet(self, shadow_frame, inst); + bool success = DoFieldGet(self, shadow_frame, inst, inst_data); POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); break; } case Instruction::IGET_SHORT: { PREAMBLE(); - bool success = DoFieldGet(self, shadow_frame, inst); + bool success = DoFieldGet(self, shadow_frame, inst, inst_data); POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); break; } case Instruction::IGET: { PREAMBLE(); - bool success = DoFieldGet(self, shadow_frame, inst); + bool success = DoFieldGet(self, shadow_frame, inst, inst_data); POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); break; } case Instruction::IGET_WIDE: { PREAMBLE(); - bool success = DoFieldGet(self, shadow_frame, inst); + bool success = DoFieldGet(self, shadow_frame, inst, inst_data); POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); break; } case Instruction::IGET_OBJECT: { PREAMBLE(); - bool success = DoFieldGet(self, shadow_frame, inst); + bool success = DoFieldGet(self, shadow_frame, inst, inst_data); POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); break; } case Instruction::IGET_QUICK: { PREAMBLE(); - bool success = DoIGetQuick(self, shadow_frame, inst); + bool success = DoIGetQuick(shadow_frame, inst, inst_data); POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); break; } case Instruction::IGET_WIDE_QUICK: { PREAMBLE(); - bool success = DoIGetQuick(self, shadow_frame, inst); + bool success = DoIGetQuick(shadow_frame, inst, inst_data); POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); break; } case Instruction::IGET_OBJECT_QUICK: { PREAMBLE(); - bool success = DoIGetQuick(self, shadow_frame, inst); + bool success = DoIGetQuick(shadow_frame, inst, inst_data); POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); break; } case Instruction::SGET_BOOLEAN: { PREAMBLE(); - bool success = DoFieldGet(self, shadow_frame, inst); + bool success = DoFieldGet(self, shadow_frame, inst, inst_data); POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); break; } case Instruction::SGET_BYTE: { PREAMBLE(); - bool success = DoFieldGet(self, shadow_frame, inst); + bool success = DoFieldGet(self, shadow_frame, inst, inst_data); POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); break; } case Instruction::SGET_CHAR: { PREAMBLE(); - bool success = DoFieldGet(self, shadow_frame, inst); + bool success = DoFieldGet(self, shadow_frame, inst, inst_data); POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); break; } case Instruction::SGET_SHORT: { PREAMBLE(); - bool success = DoFieldGet(self, shadow_frame, inst); + bool success = DoFieldGet(self, shadow_frame, inst, inst_data); POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); break; } case Instruction::SGET: { PREAMBLE(); - bool success = DoFieldGet(self, shadow_frame, inst); + bool success = DoFieldGet(self, shadow_frame, inst, inst_data); POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); break; } case Instruction::SGET_WIDE: { PREAMBLE(); - bool success = DoFieldGet(self, shadow_frame, inst); + bool success = DoFieldGet(self, shadow_frame, inst, inst_data); POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); break; } case Instruction::SGET_OBJECT: { PREAMBLE(); - bool success = DoFieldGet(self, shadow_frame, inst); + bool success = DoFieldGet(self, shadow_frame, inst, inst_data); POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); break; } case Instruction::IPUT_BOOLEAN: { PREAMBLE(); - bool success = DoFieldPut(self, shadow_frame, inst); + bool success = DoFieldPut(self, shadow_frame, inst, inst_data); POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); break; } case Instruction::IPUT_BYTE: { PREAMBLE(); - bool success = DoFieldPut(self, shadow_frame, inst); + bool success = DoFieldPut(self, shadow_frame, inst, inst_data); POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); break; } case Instruction::IPUT_CHAR: { PREAMBLE(); - bool success = DoFieldPut(self, shadow_frame, inst); + bool success = DoFieldPut(self, shadow_frame, inst, inst_data); POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); break; } case Instruction::IPUT_SHORT: { PREAMBLE(); - bool success = DoFieldPut(self, shadow_frame, inst); + bool success = DoFieldPut(self, shadow_frame, inst, inst_data); POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); break; } case Instruction::IPUT: { PREAMBLE(); - bool success = DoFieldPut(self, shadow_frame, inst); + bool success = DoFieldPut(self, shadow_frame, inst, inst_data); POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); break; } case Instruction::IPUT_WIDE: { PREAMBLE(); - bool success = DoFieldPut(self, shadow_frame, inst); + bool success = DoFieldPut(self, shadow_frame, inst, inst_data); POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); break; } case Instruction::IPUT_OBJECT: { PREAMBLE(); - bool success = DoFieldPut(self, shadow_frame, inst); + bool success = DoFieldPut(self, shadow_frame, inst, inst_data); POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); break; } case Instruction::IPUT_QUICK: { PREAMBLE(); - bool success = DoIPutQuick(self, shadow_frame, inst); + bool success = DoIPutQuick(shadow_frame, inst, inst_data); POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); break; } case Instruction::IPUT_WIDE_QUICK: { PREAMBLE(); - bool success = DoIPutQuick(self, shadow_frame, inst); + bool success = DoIPutQuick(shadow_frame, inst, inst_data); POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); break; } case Instruction::IPUT_OBJECT_QUICK: { PREAMBLE(); - bool success = DoIPutQuick(self, shadow_frame, inst); + bool success = DoIPutQuick(shadow_frame, inst, inst_data); POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); break; } case Instruction::SPUT_BOOLEAN: { PREAMBLE(); - bool success = DoFieldPut(self, shadow_frame, inst); + bool success = DoFieldPut(self, shadow_frame, inst, inst_data); POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); break; } case Instruction::SPUT_BYTE: { PREAMBLE(); - bool success = DoFieldPut(self, shadow_frame, inst); + bool success = DoFieldPut(self, shadow_frame, inst, inst_data); POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); break; } case Instruction::SPUT_CHAR: { PREAMBLE(); - bool success = DoFieldPut(self, shadow_frame, inst); + bool success = DoFieldPut(self, shadow_frame, inst, inst_data); POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); break; } case Instruction::SPUT_SHORT: { PREAMBLE(); - bool success = DoFieldPut(self, shadow_frame, inst); + bool success = DoFieldPut(self, shadow_frame, inst, inst_data); POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); break; } case Instruction::SPUT: { PREAMBLE(); - bool success = DoFieldPut(self, shadow_frame, inst); + bool success = DoFieldPut(self, shadow_frame, inst, inst_data); POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); break; } case Instruction::SPUT_WIDE: { PREAMBLE(); - bool success = DoFieldPut(self, shadow_frame, inst); + bool success = DoFieldPut(self, shadow_frame, inst, inst_data); POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); break; } case Instruction::SPUT_OBJECT: { PREAMBLE(); - bool success = DoFieldPut(self, shadow_frame, inst); + bool success = DoFieldPut(self, shadow_frame, inst, inst_data); POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); break; } @@ -1329,67 +1331,67 @@ static JValue ExecuteSwitchImpl(Thread* self, MethodHelper& mh, const DexFile::C } case Instruction::NEG_INT: PREAMBLE(); - shadow_frame.SetVReg(inst->VRegA_12x(), -shadow_frame.GetVReg(inst->VRegB_12x())); + 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(), ~shadow_frame.GetVReg(inst->VRegB_12x())); + 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(), -shadow_frame.GetVRegLong(inst->VRegB_12x())); + 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(), ~shadow_frame.GetVRegLong(inst->VRegB_12x())); + 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(), -shadow_frame.GetVRegFloat(inst->VRegB_12x())); + 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(), -shadow_frame.GetVRegDouble(inst->VRegB_12x())); + 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(), shadow_frame.GetVReg(inst->VRegB_12x())); + 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(), shadow_frame.GetVReg(inst->VRegB_12x())); + 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(), shadow_frame.GetVReg(inst->VRegB_12x())); + 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(), shadow_frame.GetVRegLong(inst->VRegB_12x())); + 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(), shadow_frame.GetVRegLong(inst->VRegB_12x())); + 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(), shadow_frame.GetVRegLong(inst->VRegB_12x())); + 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()); + float val = shadow_frame.GetVRegFloat(inst->VRegB_12x(inst_data)); int32_t result; if (val != val) { result = 0; @@ -1400,13 +1402,13 @@ static JValue ExecuteSwitchImpl(Thread* self, MethodHelper& mh, const DexFile::C } else { result = val; } - shadow_frame.SetVReg(inst->VRegA_12x(), result); + 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()); + float val = shadow_frame.GetVRegFloat(inst->VRegB_12x(inst_data)); int64_t result; if (val != val) { result = 0; @@ -1417,18 +1419,18 @@ static JValue ExecuteSwitchImpl(Thread* self, MethodHelper& mh, const DexFile::C } else { result = val; } - shadow_frame.SetVRegLong(inst->VRegA_12x(), result); + 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(), shadow_frame.GetVRegFloat(inst->VRegB_12x())); + 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()); + double val = shadow_frame.GetVRegDouble(inst->VRegB_12x(inst_data)); int32_t result; if (val != val) { result = 0; @@ -1439,13 +1441,13 @@ static JValue ExecuteSwitchImpl(Thread* self, MethodHelper& mh, const DexFile::C } else { result = val; } - shadow_frame.SetVReg(inst->VRegA_12x(), result); + 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()); + double val = shadow_frame.GetVRegDouble(inst->VRegB_12x(inst_data)); int64_t result; if (val != val) { result = 0; @@ -1456,57 +1458,57 @@ static JValue ExecuteSwitchImpl(Thread* self, MethodHelper& mh, const DexFile::C } else { result = val; } - shadow_frame.SetVRegLong(inst->VRegA_12x(), result); + 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(), shadow_frame.GetVRegDouble(inst->VRegB_12x())); + 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(), - static_cast(shadow_frame.GetVReg(inst->VRegB_12x()))); + shadow_frame.SetVReg(inst->VRegA_12x(inst_data), + static_cast(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(), - static_cast(shadow_frame.GetVReg(inst->VRegB_12x()))); + shadow_frame.SetVReg(inst->VRegA_12x(inst_data), + static_cast(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(), - static_cast(shadow_frame.GetVReg(inst->VRegB_12x()))); + shadow_frame.SetVReg(inst->VRegA_12x(inst_data), + static_cast(shadow_frame.GetVReg(inst->VRegB_12x(inst_data)))); inst = inst->Next_1xx(); break; case Instruction::ADD_INT: PREAMBLE(); - shadow_frame.SetVReg(inst->VRegA_23x(), + 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::SUB_INT: PREAMBLE(); - shadow_frame.SetVReg(inst->VRegA_23x(), + 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::MUL_INT: PREAMBLE(); - shadow_frame.SetVReg(inst->VRegA_23x(), + 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::DIV_INT: { PREAMBLE(); - bool success = DoIntDivide(shadow_frame, inst->VRegA_23x(), + 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); @@ -1514,7 +1516,7 @@ static JValue ExecuteSwitchImpl(Thread* self, MethodHelper& mh, const DexFile::C } case Instruction::REM_INT: { PREAMBLE(); - bool success = DoIntRemainder(shadow_frame, inst->VRegA_23x(), + 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); @@ -1522,606 +1524,606 @@ static JValue ExecuteSwitchImpl(Thread* self, MethodHelper& mh, const DexFile::C } case Instruction::SHL_INT: PREAMBLE(); - shadow_frame.SetVReg(inst->VRegA_23x(), + 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(), + 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(), + shadow_frame.SetVReg(inst->VRegA_23x(inst_data), static_cast(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(), + 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(), + 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(), + 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(), + 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::SUB_LONG: PREAMBLE(); - shadow_frame.SetVRegLong(inst->VRegA_23x(), + 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::MUL_LONG: PREAMBLE(); - shadow_frame.SetVRegLong(inst->VRegA_23x(), + 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::DIV_LONG: PREAMBLE(); - DoLongDivide(shadow_frame, inst->VRegA_23x(), + 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(), + 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(), + 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(), + 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(), + 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(), + 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(), + 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(), + shadow_frame.SetVRegLong(inst->VRegA_23x(inst_data), static_cast(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(), + 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(), + 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(), + 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(), + 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(), + 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(), + 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(), + 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(), + 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(), + 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(), + 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(); + uint4_t vregA = inst->VRegA_12x(inst_data); shadow_frame.SetVReg(vregA, shadow_frame.GetVReg(vregA) + - shadow_frame.GetVReg(inst->VRegB_12x())); + 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(); + uint4_t vregA = inst->VRegA_12x(inst_data); shadow_frame.SetVReg(vregA, shadow_frame.GetVReg(vregA) - - shadow_frame.GetVReg(inst->VRegB_12x())); + 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(); + uint4_t vregA = inst->VRegA_12x(inst_data); shadow_frame.SetVReg(vregA, shadow_frame.GetVReg(vregA) * - shadow_frame.GetVReg(inst->VRegB_12x())); + 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(); + uint4_t vregA = inst->VRegA_12x(inst_data); bool success = DoIntDivide(shadow_frame, vregA, shadow_frame.GetVReg(vregA), - shadow_frame.GetVReg(inst->VRegB_12x())); + 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(); + uint4_t vregA = inst->VRegA_12x(inst_data); bool success = DoIntRemainder(shadow_frame, vregA, shadow_frame.GetVReg(vregA), - shadow_frame.GetVReg(inst->VRegB_12x())); + 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(); + uint4_t vregA = inst->VRegA_12x(inst_data); shadow_frame.SetVReg(vregA, shadow_frame.GetVReg(vregA) << - (shadow_frame.GetVReg(inst->VRegB_12x()) & 0x1f)); + (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(); + uint4_t vregA = inst->VRegA_12x(inst_data); shadow_frame.SetVReg(vregA, shadow_frame.GetVReg(vregA) >> - (shadow_frame.GetVReg(inst->VRegB_12x()) & 0x1f)); + (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(); + uint4_t vregA = inst->VRegA_12x(inst_data); shadow_frame.SetVReg(vregA, static_cast(shadow_frame.GetVReg(vregA)) >> - (shadow_frame.GetVReg(inst->VRegB_12x()) & 0x1f)); + (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(); + uint4_t vregA = inst->VRegA_12x(inst_data); shadow_frame.SetVReg(vregA, shadow_frame.GetVReg(vregA) & - shadow_frame.GetVReg(inst->VRegB_12x())); + 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(); + uint4_t vregA = inst->VRegA_12x(inst_data); shadow_frame.SetVReg(vregA, shadow_frame.GetVReg(vregA) | - shadow_frame.GetVReg(inst->VRegB_12x())); + 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(); + uint4_t vregA = inst->VRegA_12x(inst_data); shadow_frame.SetVReg(vregA, shadow_frame.GetVReg(vregA) ^ - shadow_frame.GetVReg(inst->VRegB_12x())); + 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(); + uint4_t vregA = inst->VRegA_12x(inst_data); shadow_frame.SetVRegLong(vregA, shadow_frame.GetVRegLong(vregA) + - shadow_frame.GetVRegLong(inst->VRegB_12x())); + 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(); + uint4_t vregA = inst->VRegA_12x(inst_data); shadow_frame.SetVRegLong(vregA, shadow_frame.GetVRegLong(vregA) - - shadow_frame.GetVRegLong(inst->VRegB_12x())); + 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(); + uint4_t vregA = inst->VRegA_12x(inst_data); shadow_frame.SetVRegLong(vregA, shadow_frame.GetVRegLong(vregA) * - shadow_frame.GetVRegLong(inst->VRegB_12x())); + 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(); + uint4_t vregA = inst->VRegA_12x(inst_data); DoLongDivide(shadow_frame, vregA, shadow_frame.GetVRegLong(vregA), - shadow_frame.GetVRegLong(inst->VRegB_12x())); + 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(); + uint4_t vregA = inst->VRegA_12x(inst_data); DoLongRemainder(shadow_frame, vregA, shadow_frame.GetVRegLong(vregA), - shadow_frame.GetVRegLong(inst->VRegB_12x())); + 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(); + uint4_t vregA = inst->VRegA_12x(inst_data); shadow_frame.SetVRegLong(vregA, shadow_frame.GetVRegLong(vregA) & - shadow_frame.GetVRegLong(inst->VRegB_12x())); + 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(); + uint4_t vregA = inst->VRegA_12x(inst_data); shadow_frame.SetVRegLong(vregA, shadow_frame.GetVRegLong(vregA) | - shadow_frame.GetVRegLong(inst->VRegB_12x())); + 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(); + uint4_t vregA = inst->VRegA_12x(inst_data); shadow_frame.SetVRegLong(vregA, shadow_frame.GetVRegLong(vregA) ^ - shadow_frame.GetVRegLong(inst->VRegB_12x())); + 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(); + uint4_t vregA = inst->VRegA_12x(inst_data); shadow_frame.SetVRegLong(vregA, shadow_frame.GetVRegLong(vregA) << - (shadow_frame.GetVReg(inst->VRegB_12x()) & 0x3f)); + (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(); + uint4_t vregA = inst->VRegA_12x(inst_data); shadow_frame.SetVRegLong(vregA, shadow_frame.GetVRegLong(vregA) >> - (shadow_frame.GetVReg(inst->VRegB_12x()) & 0x3f)); + (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(); + uint4_t vregA = inst->VRegA_12x(inst_data); shadow_frame.SetVRegLong(vregA, static_cast(shadow_frame.GetVRegLong(vregA)) >> - (shadow_frame.GetVReg(inst->VRegB_12x()) & 0x3f)); + (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(); + uint4_t vregA = inst->VRegA_12x(inst_data); shadow_frame.SetVRegFloat(vregA, shadow_frame.GetVRegFloat(vregA) + - shadow_frame.GetVRegFloat(inst->VRegB_12x())); + 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(); + uint4_t vregA = inst->VRegA_12x(inst_data); shadow_frame.SetVRegFloat(vregA, shadow_frame.GetVRegFloat(vregA) - - shadow_frame.GetVRegFloat(inst->VRegB_12x())); + 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(); + uint4_t vregA = inst->VRegA_12x(inst_data); shadow_frame.SetVRegFloat(vregA, shadow_frame.GetVRegFloat(vregA) * - shadow_frame.GetVRegFloat(inst->VRegB_12x())); + 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(); + uint4_t vregA = inst->VRegA_12x(inst_data); shadow_frame.SetVRegFloat(vregA, shadow_frame.GetVRegFloat(vregA) / - shadow_frame.GetVRegFloat(inst->VRegB_12x())); + 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(); + uint4_t vregA = inst->VRegA_12x(inst_data); shadow_frame.SetVRegFloat(vregA, fmodf(shadow_frame.GetVRegFloat(vregA), - shadow_frame.GetVRegFloat(inst->VRegB_12x()))); + 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(); + uint4_t vregA = inst->VRegA_12x(inst_data); shadow_frame.SetVRegDouble(vregA, shadow_frame.GetVRegDouble(vregA) + - shadow_frame.GetVRegDouble(inst->VRegB_12x())); + 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(); + uint4_t vregA = inst->VRegA_12x(inst_data); shadow_frame.SetVRegDouble(vregA, shadow_frame.GetVRegDouble(vregA) - - shadow_frame.GetVRegDouble(inst->VRegB_12x())); + 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(); + uint4_t vregA = inst->VRegA_12x(inst_data); shadow_frame.SetVRegDouble(vregA, shadow_frame.GetVRegDouble(vregA) * - shadow_frame.GetVRegDouble(inst->VRegB_12x())); + 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(); + uint4_t vregA = inst->VRegA_12x(inst_data); shadow_frame.SetVRegDouble(vregA, shadow_frame.GetVRegDouble(vregA) / - shadow_frame.GetVRegDouble(inst->VRegB_12x())); + 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(); + uint4_t vregA = inst->VRegA_12x(inst_data); shadow_frame.SetVRegDouble(vregA, fmod(shadow_frame.GetVRegDouble(vregA), - shadow_frame.GetVRegDouble(inst->VRegB_12x()))); + 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(), - shadow_frame.GetVReg(inst->VRegB_22s()) + + 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::RSUB_INT: PREAMBLE(); - shadow_frame.SetVReg(inst->VRegA_22s(), + shadow_frame.SetVReg(inst->VRegA_22s(inst_data), inst->VRegC_22s() - - shadow_frame.GetVReg(inst->VRegB_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(), - shadow_frame.GetVReg(inst->VRegB_22s()) * + 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::DIV_INT_LIT16: { PREAMBLE(); - bool success = DoIntDivide(shadow_frame, inst->VRegA_22s(), - shadow_frame.GetVReg(inst->VRegB_22s()), inst->VRegC_22s()); + 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(), - shadow_frame.GetVReg(inst->VRegB_22s()), inst->VRegC_22s()); + 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(), - shadow_frame.GetVReg(inst->VRegB_22s()) & + 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(), - shadow_frame.GetVReg(inst->VRegB_22s()) | + 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(), - shadow_frame.GetVReg(inst->VRegB_22s()) ^ + 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(), + shadow_frame.SetVReg(inst->VRegA_22b(inst_data), 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(), + shadow_frame.SetVReg(inst->VRegA_22b(inst_data), 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(), + shadow_frame.SetVReg(inst->VRegA_22b(inst_data), 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(), + 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(), + 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(), + 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(), + 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(), + 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(), + 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(), + 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(), + shadow_frame.SetVReg(inst->VRegA_22b(inst_data), static_cast(shadow_frame.GetVReg(inst->VRegB_22b())) >> (inst->VRegC_22b() & 0x1f)); inst = inst->Next_2xx(); From d910172d7acc5d62617e40d1c264a8268eb75a7e Mon Sep 17 00:00:00 2001 From: Ying Wang Date: Wed, 18 Sep 2013 09:36:27 -0700 Subject: [PATCH 0040/2402] Don't fail if llvm.mk is not available. This enables doing dalvik minimal host build without external/llvm. Change-Id: I1f44f4d29b941086a79758c816674c9f9966eb31 --- build/Android.common.mk | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/build/Android.common.mk b/build/Android.common.mk index ac1be1e28ad..dd0ba4d1d3b 100644 --- a/build/Android.common.mk +++ b/build/Android.common.mk @@ -82,7 +82,8 @@ ART_USE_PORTABLE_COMPILER := true endif LLVM_ROOT_PATH := external/llvm -include $(LLVM_ROOT_PATH)/llvm.mk +# Don't fail a dalvik minimal host build. +-include $(LLVM_ROOT_PATH)/llvm.mk # Clang build. # ART_TARGET_CLANG := true From d7d7f6e6916d4cdba89a28670440ec3a59d1a862 Mon Sep 17 00:00:00 2001 From: Elliott Hughes Date: Wed, 18 Sep 2013 12:00:45 -0700 Subject: [PATCH 0041/2402] Add JNI tests missing from 4ffdc6bd962c37bca407267c0858b37bb18a9857. Change-Id: I4fa61246b5e92936698e1c608189d51768da456f --- runtime/jni_internal_test.cc | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/runtime/jni_internal_test.cc b/runtime/jni_internal_test.cc index 79d156de277..c389580ebfc 100644 --- a/runtime/jni_internal_test.cc +++ b/runtime/jni_internal_test.cc @@ -1012,31 +1012,50 @@ TEST_F(JniInternalTest, RegisterNatives) { scalar_type, \ expected_class_descriptor) \ jsize size = 4; \ + \ /* Allocate an array and check it has the right type and length. */ \ scalar_type ## Array a = env_->new_fn(size); \ EXPECT_TRUE(a != NULL); \ EXPECT_TRUE(env_->IsInstanceOf(a, env_->FindClass(expected_class_descriptor))); \ EXPECT_EQ(size, env_->GetArrayLength(a)); \ + \ + /* GetPrimitiveArrayRegion/SetPrimitiveArrayRegion */ \ /* AIOOBE for negative start offset. */ \ env_->get_region_fn(a, -1, 1, NULL); \ EXPECT_EXCEPTION(aioobe_); \ env_->set_region_fn(a, -1, 1, NULL); \ EXPECT_EXCEPTION(aioobe_); \ + \ /* AIOOBE for negative length. */ \ env_->get_region_fn(a, 0, -1, NULL); \ EXPECT_EXCEPTION(aioobe_); \ env_->set_region_fn(a, 0, -1, NULL); \ EXPECT_EXCEPTION(aioobe_); \ + \ /* AIOOBE for buffer overrun. */ \ env_->get_region_fn(a, size - 1, size, NULL); \ EXPECT_EXCEPTION(aioobe_); \ env_->set_region_fn(a, size - 1, size, NULL); \ EXPECT_EXCEPTION(aioobe_); \ + \ + /* It's okay for the buffer to be NULL as long as the length is 0. */ \ + env_->get_region_fn(a, 2, 0, NULL); \ + /* Even if the offset is invalid... */ \ + env_->get_region_fn(a, 123, 0, NULL); \ + EXPECT_EXCEPTION(aioobe_); \ + \ + /* It's okay for the buffer to be NULL as long as the length is 0. */ \ + env_->set_region_fn(a, 2, 0, NULL); \ + /* Even if the offset is invalid... */ \ + env_->set_region_fn(a, 123, 0, NULL); \ + EXPECT_EXCEPTION(aioobe_); \ + \ /* Prepare a couple of buffers. */ \ UniquePtr src_buf(new scalar_type[size]); \ UniquePtr dst_buf(new scalar_type[size]); \ for (jsize i = 0; i < size; ++i) { src_buf[i] = scalar_type(i); } \ for (jsize i = 0; i < size; ++i) { dst_buf[i] = scalar_type(-1); } \ + \ /* Copy all of src_buf onto the heap. */ \ env_->set_region_fn(a, 0, size, &src_buf[0]); \ /* Copy back only part. */ \ @@ -1252,6 +1271,12 @@ TEST_F(JniInternalTest, GetStringRegion_GetStringUTFRegion) { EXPECT_EQ('l', chars[2]); EXPECT_EQ('x', chars[3]); + // It's okay for the buffer to be NULL as long as the length is 0. + env_->GetStringRegion(s, 2, 0, NULL); + // Even if the offset is invalid... + env_->GetStringRegion(s, 123, 0, NULL); + EXPECT_EXCEPTION(sioobe_); + env_->GetStringUTFRegion(s, -1, 0, NULL); EXPECT_EXCEPTION(sioobe_); env_->GetStringUTFRegion(s, 0, -1, NULL); @@ -1267,6 +1292,12 @@ TEST_F(JniInternalTest, GetStringRegion_GetStringUTFRegion) { EXPECT_EQ('e', bytes[1]); EXPECT_EQ('l', bytes[2]); EXPECT_EQ('x', bytes[3]); + + // It's okay for the buffer to be NULL as long as the length is 0. + env_->GetStringUTFRegion(s, 2, 0, NULL); + // Even if the offset is invalid... + env_->GetStringUTFRegion(s, 123, 0, NULL); + EXPECT_EXCEPTION(sioobe_); } TEST_F(JniInternalTest, GetStringUTFChars_ReleaseStringUTFChars) { From 1fc5800def46a2fa6cbd235fcb8af099ee35a127 Mon Sep 17 00:00:00 2001 From: buzbee Date: Wed, 18 Sep 2013 12:49:36 -0700 Subject: [PATCH 0042/2402] Art compiler: minor instruction assembler fix During the assembly phase, we iteratively walk through the LIR encoding instructions until we can complete a full pass without without having to change the sequence because of displacement overflow. In the (fairly common) situation in which a 16-bit cbnz/cbz can't reach the target, we expand it to a compare and branch sequence Initially, we use a 16-bit Thumb1 unconditional branch, which itself may be expanded in a future pass to a 32-bit branch. The original cbnz/cbz LIR is converted into a cmp, and a new branch instruction is inserted following. The problem here is that by doing a following insertion, that new instruction will be the next one considered to determine if it can reach it's target. Because it is new, though, it's starting offset will show as zero - making it much more likely that it will be treated as a displacement overflow and be converted to a 32-bit branch. This is not a correctness issue - the bad offset will be corrected on the next pass, but it does result in unnecessary uses of 32-bit branches where 16-bit ones would work. Change-Id: Ie68a93fd319f0f7c603e1d870588047ad6a0779f --- compiler/dex/quick/arm/assemble_arm.cc | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/compiler/dex/quick/arm/assemble_arm.cc b/compiler/dex/quick/arm/assemble_arm.cc index 8ba56dfa0ea..2d69d935ca2 100644 --- a/compiler/dex/quick/arm/assemble_arm.cc +++ b/compiler/dex/quick/arm/assemble_arm.cc @@ -1122,6 +1122,12 @@ AssemblerStatus ArmMir2Lir::AssembleInstructions(uintptr_t start_addr) { lir->operands[1] = 0; lir->target = 0; SetupResourceMasks(lir); + /* + * Because we just added this new instruction after the current one, + * advance lir so that this new instruction won't be checked for displacement + * overflow until the next pass (when its base offset will be properly established). + */ + lir = new_inst; res = kRetryAll; } else { lir->operands[1] = delta >> 1; From 8b2c0b9abc3f520495f4387ea040132ba85cae69 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Thu, 19 Sep 2013 02:56:49 -0700 Subject: [PATCH 0043/2402] Use class def index from java.lang.Class. Bug: 10244719 Depends on: https://googleplex-android-review.git.corp.google.com/362363 This removes the computation of the dex file index, when necessary this is computed by searching the dex file. Its only necessary in dalvik.system.DexFile.defineClassNative and DexFile::FindInClassPath, the latter not showing up significantly in profiling with this change. Change-Id: I20c73a3b17d86286428ab0fd21bc13f51f36c85c --- compiler/dex/compiler_ir.h | 2 +- compiler/dex/dex_to_dex_compiler.cc | 2 +- compiler/dex/frontend.cc | 6 +- compiler/dex/frontend.h | 2 +- compiler/dex/mir_graph.cc | 2 +- compiler/dex/mir_graph.h | 2 +- compiler/dex/quick/codegen_util.cc | 22 ++-- compiler/driver/compiler_driver.cc | 49 ++++---- compiler/driver/compiler_driver.h | 27 +++-- compiler/driver/dex_compilation_unit.cc | 2 +- compiler/driver/dex_compilation_unit.h | 6 +- compiler/image_writer.cc | 1 + compiler/llvm/compiler_llvm.cc | 4 +- compiler/sea_ir/frontend.cc | 6 +- compiler/sea_ir/ir/sea.cc | 4 +- compiler/sea_ir/ir/sea.h | 6 +- oatdump/oatdump.cc | 32 ++--- runtime/Android.mk | 1 + runtime/class_linker.cc | 111 ++++++++---------- runtime/class_linker.h | 4 +- runtime/class_linker_test.cc | 12 +- runtime/dex_file.cc | 91 +++++--------- runtime/dex_file.h | 44 +++---- .../quick/quick_invoke_entrypoints.cc | 2 +- runtime/mirror/art_method-inl.h | 2 +- runtime/mirror/class.cc | 23 +--- runtime/mirror/class.h | 31 +++-- runtime/mirror/dex_cache.h | 1 + runtime/mirror/proxy.h | 3 + runtime/mirror/string.h | 2 +- runtime/native/dalvik_system_DexFile.cc | 2 +- runtime/native/java_lang_Class.cc | 31 ----- runtime/native/java_lang_DexCache.cc | 56 +++++++++ runtime/oat_file.cc | 2 +- runtime/oat_file.h | 2 +- runtime/object_utils.h | 87 ++++++-------- runtime/runtime.cc | 5 +- runtime/thread.cc | 2 +- runtime/verifier/method_verifier.cc | 96 +++++++-------- runtime/verifier/method_verifier.h | 19 +-- runtime/verifier/method_verifier_test.cc | 3 +- test/100-reflect2/expected.txt | 2 +- 42 files changed, 397 insertions(+), 412 deletions(-) create mode 100644 runtime/native/java_lang_DexCache.cc diff --git a/compiler/dex/compiler_ir.h b/compiler/dex/compiler_ir.h index 26d0923baa4..6607562b134 100644 --- a/compiler/dex/compiler_ir.h +++ b/compiler/dex/compiler_ir.h @@ -77,7 +77,7 @@ struct CompilationUnit { ClassLinker* class_linker; // Linker to resolve fields and methods. const DexFile* dex_file; // DexFile containing the method being compiled. jobject class_loader; // compiling method's class loader. - uint32_t class_def_idx; // compiling method's defining class definition index. + uint16_t class_def_idx; // compiling method's defining class definition index. uint32_t method_idx; // compiling method's index into method_ids of DexFile. const DexFile::CodeItem* code_item; // compiling method's DexFile code_item. uint32_t access_flags; // compiling method's access flags. diff --git a/compiler/dex/dex_to_dex_compiler.cc b/compiler/dex/dex_to_dex_compiler.cc index ffd7905dfe7..abafbc58307 100644 --- a/compiler/dex/dex_to_dex_compiler.cc +++ b/compiler/dex/dex_to_dex_compiler.cc @@ -280,7 +280,7 @@ void DexCompiler::CompileInvokeVirtual(Instruction* inst, extern "C" void ArtCompileDEX(art::CompilerDriver& compiler, const art::DexFile::CodeItem* code_item, uint32_t access_flags, art::InvokeType invoke_type, - uint32_t class_def_idx, uint32_t method_idx, jobject class_loader, + uint16_t class_def_idx, uint32_t method_idx, jobject class_loader, const art::DexFile& dex_file, art::DexToDexCompilationLevel dex_to_dex_compilation_level) { if (dex_to_dex_compilation_level != art::kDontDexToDexCompile) { diff --git a/compiler/dex/frontend.cc b/compiler/dex/frontend.cc index 23036495ce5..fefcab9e87c 100644 --- a/compiler/dex/frontend.cc +++ b/compiler/dex/frontend.cc @@ -110,7 +110,7 @@ static CompiledMethod* CompileMethod(CompilerDriver& compiler, const CompilerBackend compiler_backend, const DexFile::CodeItem* code_item, uint32_t access_flags, InvokeType invoke_type, - uint32_t class_def_idx, uint32_t method_idx, + uint16_t class_def_idx, uint32_t method_idx, jobject class_loader, const DexFile& dex_file #if defined(ART_USE_PORTABLE_COMPILER) , llvm::LlvmCompilationUnit* llvm_compilation_unit @@ -273,7 +273,7 @@ CompiledMethod* CompileOneMethod(CompilerDriver& compiler, const DexFile::CodeItem* code_item, uint32_t access_flags, InvokeType invoke_type, - uint32_t class_def_idx, + uint16_t class_def_idx, uint32_t method_idx, jobject class_loader, const DexFile& dex_file, @@ -292,7 +292,7 @@ extern "C" art::CompiledMethod* ArtQuickCompileMethod(art::CompilerDriver& compiler, const art::DexFile::CodeItem* code_item, uint32_t access_flags, art::InvokeType invoke_type, - uint32_t class_def_idx, uint32_t method_idx, jobject class_loader, + uint16_t class_def_idx, uint32_t method_idx, jobject class_loader, const art::DexFile& dex_file) { // TODO: check method fingerprint here to determine appropriate backend type. Until then, use build default art::CompilerBackend backend = compiler.GetCompilerBackend(); diff --git a/compiler/dex/frontend.h b/compiler/dex/frontend.h index bafa46892c4..6c33d109e31 100644 --- a/compiler/dex/frontend.h +++ b/compiler/dex/frontend.h @@ -117,7 +117,7 @@ extern "C" art::CompiledMethod* ArtCompileMethod(art::CompilerDriver& driver, const art::DexFile::CodeItem* code_item, uint32_t access_flags, art::InvokeType invoke_type, - uint32_t class_dex_idx, + uint16_t class_def_idx, uint32_t method_idx, jobject class_loader, const art::DexFile& dex_file); diff --git a/compiler/dex/mir_graph.cc b/compiler/dex/mir_graph.cc index c72283e689b..c234298a88e 100644 --- a/compiler/dex/mir_graph.cc +++ b/compiler/dex/mir_graph.cc @@ -504,7 +504,7 @@ BasicBlock* MIRGraph::ProcessCanThrow(BasicBlock* cur_block, MIR* insn, int cur_ /* Parse a Dex method and insert it into the MIRGraph at the current insert point. */ void MIRGraph::InlineMethod(const DexFile::CodeItem* code_item, uint32_t access_flags, - InvokeType invoke_type, uint32_t class_def_idx, + InvokeType invoke_type, uint16_t class_def_idx, uint32_t method_idx, jobject class_loader, const DexFile& dex_file) { current_code_item_ = code_item; method_stack_.push_back(std::make_pair(current_method_, current_offset_)); diff --git a/compiler/dex/mir_graph.h b/compiler/dex/mir_graph.h index 0244daec9dd..9d4ab98f67c 100644 --- a/compiler/dex/mir_graph.h +++ b/compiler/dex/mir_graph.h @@ -357,7 +357,7 @@ class MIRGraph { * actually the index of the method in the m_units_ array). */ void InlineMethod(const DexFile::CodeItem* code_item, uint32_t access_flags, - InvokeType invoke_type, uint32_t class_def_idx, + InvokeType invoke_type, uint16_t class_def_idx, uint32_t method_idx, jobject class_loader, const DexFile& dex_file); /* Find existing block */ diff --git a/compiler/dex/quick/codegen_util.cc b/compiler/dex/quick/codegen_util.cc index f13ab2db017..4ce752fb398 100644 --- a/compiler/dex/quick/codegen_util.cc +++ b/compiler/dex/quick/codegen_util.cc @@ -385,11 +385,12 @@ void Mir2Lir::InstallLiteralPools() { while (data_lir != NULL) { uint32_t target = data_lir->operands[0]; cu_->compiler_driver->AddCodePatch(cu_->dex_file, - cu_->method_idx, - cu_->invoke_type, - target, - static_cast(data_lir->operands[1]), - code_buffer_.size()); + cu_->class_def_idx, + cu_->method_idx, + cu_->invoke_type, + target, + static_cast(data_lir->operands[1]), + code_buffer_.size()); const DexFile::MethodId& id = cu_->dex_file->GetMethodId(target); // unique based on target to ensure code deduplication works uint32_t unique_patch_value = reinterpret_cast(&id); @@ -400,11 +401,12 @@ void Mir2Lir::InstallLiteralPools() { while (data_lir != NULL) { uint32_t target = data_lir->operands[0]; cu_->compiler_driver->AddMethodPatch(cu_->dex_file, - cu_->method_idx, - cu_->invoke_type, - target, - static_cast(data_lir->operands[1]), - code_buffer_.size()); + cu_->class_def_idx, + cu_->method_idx, + cu_->invoke_type, + target, + static_cast(data_lir->operands[1]), + code_buffer_.size()); const DexFile::MethodId& id = cu_->dex_file->GetMethodId(target); // unique based on target to ensure code deduplication works uint32_t unique_patch_value = reinterpret_cast(&id); diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc index 8d521de72fe..658370f1fd7 100644 --- a/compiler/driver/compiler_driver.cc +++ b/compiler/driver/compiler_driver.cc @@ -293,7 +293,7 @@ extern "C" art::CompiledMethod* ArtCompileMethod(art::CompilerDriver& driver, const art::DexFile::CodeItem* code_item, uint32_t access_flags, art::InvokeType invoke_type, - uint32_t class_def_idx, + uint16_t class_def_idx, uint32_t method_idx, jobject class_loader, const art::DexFile& dex_file); @@ -301,7 +301,7 @@ extern "C" art::CompiledMethod* ArtQuickCompileMethod(art::CompilerDriver& compi const art::DexFile::CodeItem* code_item, uint32_t access_flags, art::InvokeType invoke_type, - uint32_t class_def_idx, + uint16_t class_def_idx, uint32_t method_idx, jobject class_loader, const art::DexFile& dex_file); @@ -310,7 +310,7 @@ extern "C" art::CompiledMethod* ArtCompileDEX(art::CompilerDriver& compiler, const art::DexFile::CodeItem* code_item, uint32_t access_flags, art::InvokeType invoke_type, - uint32_t class_def_idx, + uint16_t class_def_idx, uint32_t method_idx, jobject class_loader, const art::DexFile& dex_file); @@ -319,7 +319,7 @@ extern "C" art::CompiledMethod* SeaIrCompileMethod(art::CompilerDriver& compiler const art::DexFile::CodeItem* code_item, uint32_t access_flags, art::InvokeType invoke_type, - uint32_t class_def_idx, + uint16_t class_def_idx, uint32_t method_idx, jobject class_loader, const art::DexFile& dex_file); @@ -540,7 +540,7 @@ void CompilerDriver::CompileOne(const mirror::ArtMethod* method, base::TimingLog Thread* self = Thread::Current(); jobject jclass_loader; const DexFile* dex_file; - uint32_t class_def_idx; + uint16_t class_def_idx; { ScopedObjectAccessUnchecked soa(self); ScopedLocalRef @@ -1304,13 +1304,15 @@ bool CompilerDriver::IsSafeCast(const MethodReference& mr, uint32_t dex_pc) { void CompilerDriver::AddCodePatch(const DexFile* dex_file, - uint32_t referrer_method_idx, - InvokeType referrer_invoke_type, - uint32_t target_method_idx, - InvokeType target_invoke_type, - size_t literal_offset) { + uint16_t referrer_class_def_idx, + uint32_t referrer_method_idx, + InvokeType referrer_invoke_type, + uint32_t target_method_idx, + InvokeType target_invoke_type, + size_t literal_offset) { MutexLock mu(Thread::Current(), compiled_methods_lock_); code_to_patch_.push_back(new PatchInformation(dex_file, + referrer_class_def_idx, referrer_method_idx, referrer_invoke_type, target_method_idx, @@ -1318,13 +1320,15 @@ void CompilerDriver::AddCodePatch(const DexFile* dex_file, literal_offset)); } void CompilerDriver::AddMethodPatch(const DexFile* dex_file, - uint32_t referrer_method_idx, - InvokeType referrer_invoke_type, - uint32_t target_method_idx, - InvokeType target_invoke_type, - size_t literal_offset) { + uint16_t referrer_class_def_idx, + uint32_t referrer_method_idx, + InvokeType referrer_invoke_type, + uint32_t target_method_idx, + InvokeType target_invoke_type, + size_t literal_offset) { MutexLock mu(Thread::Current(), compiled_methods_lock_); methods_to_patch_.push_back(new PatchInformation(dex_file, + referrer_class_def_idx, referrer_method_idx, referrer_invoke_type, target_method_idx, @@ -1625,10 +1629,12 @@ static void VerifyClass(const ParallelCompilationManager* manager, size_t class_ */ mirror::DexCache* dex_cache = manager->GetClassLinker()->FindDexCache(*manager->GetDexFile()); std::string error_msg; - if (verifier::MethodVerifier::VerifyClass(manager->GetDexFile(), + const DexFile* dex_file = manager->GetDexFile(); + const DexFile::ClassDef* class_def = &dex_file->GetClassDef(class_def_index); + if (verifier::MethodVerifier::VerifyClass(dex_file, dex_cache, soa.Decode(manager->GetClassLoader()), - class_def_index, error_msg, true) == + class_def, true, &error_msg) == verifier::MethodVerifier::kHardFailure) { const DexFile::ClassDef& class_def = manager->GetDexFile()->GetClassDef(class_def_index); LOG(ERROR) << "Verification failed on class " @@ -2137,7 +2143,8 @@ static void InitializeClass(const ParallelCompilationManager* manager, size_t cl } // If successfully initialized place in SSB array. if (klass->IsInitialized()) { - klass->GetDexCache()->GetInitializedStaticStorage()->Set(klass->GetDexTypeIndex(), klass); + int32_t ssb_index = klass->GetDexTypeIndex(); + klass->GetDexCache()->GetInitializedStaticStorage()->Set(ssb_index, klass); } } // Record the final class status if necessary. @@ -2264,7 +2271,7 @@ void CompilerDriver::CompileDexFile(jobject class_loader, const DexFile& dex_fil } void CompilerDriver::CompileMethod(const DexFile::CodeItem* code_item, uint32_t access_flags, - InvokeType invoke_type, uint32_t class_def_idx, + InvokeType invoke_type, uint16_t class_def_idx, uint32_t method_idx, jobject class_loader, const DexFile& dex_file, DexToDexCompilationLevel dex_to_dex_compilation_level) { @@ -2387,13 +2394,13 @@ void CompilerDriver::SetBitcodeFileName(std::string const& filename) { void CompilerDriver::AddRequiresConstructorBarrier(Thread* self, const DexFile* dex_file, - size_t class_def_index) { + uint16_t class_def_index) { WriterMutexLock mu(self, freezing_constructor_lock_); freezing_constructor_classes_.insert(ClassReference(dex_file, class_def_index)); } bool CompilerDriver::RequiresConstructorBarrier(Thread* self, const DexFile* dex_file, - size_t class_def_index) { + uint16_t class_def_index) { ReaderMutexLock mu(self, freezing_constructor_lock_); return freezing_constructor_classes_.count(ClassReference(dex_file, class_def_index)) != 0; } diff --git a/compiler/driver/compiler_driver.h b/compiler/driver/compiler_driver.h index b4ec0c134bf..66c9cbf91a1 100644 --- a/compiler/driver/compiler_driver.h +++ b/compiler/driver/compiler_driver.h @@ -145,8 +145,9 @@ class CompilerDriver { CompiledMethod* GetCompiledMethod(MethodReference ref) const LOCKS_EXCLUDED(compiled_methods_lock_); - void AddRequiresConstructorBarrier(Thread* self, const DexFile* dex_file, size_t class_def_index); - bool RequiresConstructorBarrier(Thread* self, const DexFile* dex_file, size_t class_def_index); + void AddRequiresConstructorBarrier(Thread* self, const DexFile* dex_file, + uint16_t class_def_index); + bool RequiresConstructorBarrier(Thread* self, const DexFile* dex_file, uint16_t class_def_index); // Callbacks from compiler to see what runtime checks must be generated. @@ -192,6 +193,7 @@ class CompilerDriver { // Record patch information for later fix up. void AddCodePatch(const DexFile* dex_file, + uint16_t referrer_class_def_idx, uint32_t referrer_method_idx, InvokeType referrer_invoke_type, uint32_t target_method_idx, @@ -199,6 +201,7 @@ class CompilerDriver { size_t literal_offset) LOCKS_EXCLUDED(compiled_methods_lock_); void AddMethodPatch(const DexFile* dex_file, + uint16_t referrer_class_def_idx, uint32_t referrer_method_idx, InvokeType referrer_invoke_type, uint32_t target_method_idx, @@ -249,6 +252,9 @@ class CompilerDriver { const DexFile& GetDexFile() const { return *dex_file_; } + uint16_t GetReferrerClassDefIdx() const { + return referrer_class_def_idx_; + } uint32_t GetReferrerMethodIdx() const { return referrer_method_idx_; } @@ -267,12 +273,14 @@ class CompilerDriver { private: PatchInformation(const DexFile* dex_file, + uint16_t referrer_class_def_idx, uint32_t referrer_method_idx, InvokeType referrer_invoke_type, uint32_t target_method_idx, InvokeType target_invoke_type, size_t literal_offset) : dex_file_(dex_file), + referrer_class_def_idx_(referrer_class_def_idx), referrer_method_idx_(referrer_method_idx), referrer_invoke_type_(referrer_invoke_type), target_method_idx_(target_method_idx), @@ -281,12 +289,13 @@ class CompilerDriver { CHECK(dex_file_ != NULL); } - const DexFile* dex_file_; - uint32_t referrer_method_idx_; - InvokeType referrer_invoke_type_; - uint32_t target_method_idx_; - InvokeType target_invoke_type_; - size_t literal_offset_; + const DexFile* const dex_file_; + const uint16_t referrer_class_def_idx_; + const uint32_t referrer_method_idx_; + const InvokeType referrer_invoke_type_; + const uint32_t target_method_idx_; + const InvokeType target_invoke_type_; + const size_t literal_offset_; friend class CompilerDriver; DISALLOW_COPY_AND_ASSIGN(PatchInformation); @@ -358,7 +367,7 @@ class CompilerDriver { ThreadPool& thread_pool, base::TimingLogger& timings) LOCKS_EXCLUDED(Locks::mutator_lock_); void CompileMethod(const DexFile::CodeItem* code_item, uint32_t access_flags, - InvokeType invoke_type, uint32_t class_def_idx, uint32_t method_idx, + InvokeType invoke_type, uint16_t class_def_idx, uint32_t method_idx, jobject class_loader, const DexFile& dex_file, DexToDexCompilationLevel dex_to_dex_compilation_level) LOCKS_EXCLUDED(compiled_methods_lock_); diff --git a/compiler/driver/dex_compilation_unit.cc b/compiler/driver/dex_compilation_unit.cc index eb8941b15f4..c441d09ab2c 100644 --- a/compiler/driver/dex_compilation_unit.cc +++ b/compiler/driver/dex_compilation_unit.cc @@ -39,7 +39,7 @@ DexCompilationUnit::DexCompilationUnit(CompilationUnit* cu, ClassLinker* class_linker, const DexFile& dex_file, const DexFile::CodeItem* code_item, - uint32_t class_def_idx, + uint16_t class_def_idx, uint32_t method_idx, uint32_t access_flags) : cu_(cu), diff --git a/compiler/driver/dex_compilation_unit.h b/compiler/driver/dex_compilation_unit.h index 465139b34f3..3df50ffec6c 100644 --- a/compiler/driver/dex_compilation_unit.h +++ b/compiler/driver/dex_compilation_unit.h @@ -36,7 +36,7 @@ class DexCompilationUnit { DexCompilationUnit(CompilationUnit* cu, jobject class_loader, ClassLinker* class_linker, const DexFile& dex_file, const DexFile::CodeItem* code_item, - uint32_t class_def_idx, uint32_t method_idx, uint32_t access_flags); + uint16_t class_def_idx, uint32_t method_idx, uint32_t access_flags); CompilationUnit* GetCompilationUnit() const { return cu_; @@ -54,7 +54,7 @@ class DexCompilationUnit { return dex_file_; } - uint32_t GetClassDefIndex() const { + uint16_t GetClassDefIndex() const { return class_def_idx_; } @@ -108,7 +108,7 @@ class DexCompilationUnit { const DexFile* const dex_file_; const DexFile::CodeItem* const code_item_; - const uint32_t class_def_idx_; + const uint16_t class_def_idx_; const uint32_t dex_method_idx_; const uint32_t access_flags_; diff --git a/compiler/image_writer.cc b/compiler/image_writer.cc index d1859e6f984..f82c6fb40f3 100644 --- a/compiler/image_writer.cc +++ b/compiler/image_writer.cc @@ -699,6 +699,7 @@ void ImageWriter::PatchOatCodeAndMethods() { void ImageWriter::SetPatchLocation(const CompilerDriver::PatchInformation* patch, uint32_t value) { ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); const void* oat_code = class_linker->GetOatCodeFor(patch->GetDexFile(), + patch->GetReferrerClassDefIdx(), patch->GetReferrerMethodIdx()); OatHeader& oat_header = const_cast(oat_file_->GetOatHeader()); // TODO: make this Thumb2 specific diff --git a/compiler/llvm/compiler_llvm.cc b/compiler/llvm/compiler_llvm.cc index 0df3c476fc5..d59afd48b77 100644 --- a/compiler/llvm/compiler_llvm.cc +++ b/compiler/llvm/compiler_llvm.cc @@ -40,7 +40,7 @@ void CompileOneMethod(CompilerDriver& driver, const CompilerBackend compilerBackend, const DexFile::CodeItem* code_item, uint32_t access_flags, InvokeType invoke_type, - uint32_t class_def_idx, uint32_t method_idx, jobject class_loader, + uint16_t class_def_idx, uint32_t method_idx, jobject class_loader, const DexFile& dex_file, llvm::LlvmCompilationUnit* llvm_info); } @@ -203,7 +203,7 @@ extern "C" art::CompiledMethod* ArtCompileMethod(art::CompilerDriver& driver, const art::DexFile::CodeItem* code_item, uint32_t access_flags, art::InvokeType invoke_type, - uint32_t class_def_idx, + uint16_t class_def_idx, uint32_t method_idx, jobject class_loader, const art::DexFile& dex_file) { diff --git a/compiler/sea_ir/frontend.cc b/compiler/sea_ir/frontend.cc index 93f6f25461b..3512911f439 100644 --- a/compiler/sea_ir/frontend.cc +++ b/compiler/sea_ir/frontend.cc @@ -41,7 +41,7 @@ static CompiledMethod* CompileMethodWithSeaIr(CompilerDriver& compiler, const CompilerBackend compiler_backend, const DexFile::CodeItem* code_item, uint32_t method_access_flags, InvokeType invoke_type, - uint32_t class_def_idx, uint32_t method_idx, + uint16_t class_def_idx, uint32_t method_idx, jobject class_loader, const DexFile& dex_file #if defined(ART_USE_PORTABLE_COMPILER) , llvm::LlvmCompilationUnit* llvm_compilation_unit @@ -69,7 +69,7 @@ CompiledMethod* SeaIrCompileOneMethod(CompilerDriver& compiler, const DexFile::CodeItem* code_item, uint32_t method_access_flags, InvokeType invoke_type, - uint32_t class_def_idx, + uint16_t class_def_idx, uint32_t method_idx, jobject class_loader, const DexFile& dex_file, @@ -86,7 +86,7 @@ extern "C" art::CompiledMethod* SeaIrCompileMethod(art::CompilerDriver& compiler, const art::DexFile::CodeItem* code_item, uint32_t method_access_flags, art::InvokeType invoke_type, - uint32_t class_def_idx, uint32_t method_idx, jobject class_loader, + uint16_t class_def_idx, uint32_t method_idx, jobject class_loader, const art::DexFile& dex_file) { // TODO: Check method fingerprint here to determine appropriate backend type. // Until then, use build default diff --git a/compiler/sea_ir/ir/sea.cc b/compiler/sea_ir/ir/sea.cc index 5ccaba6ad90..0734b21f128 100644 --- a/compiler/sea_ir/ir/sea.cc +++ b/compiler/sea_ir/ir/sea.cc @@ -191,7 +191,7 @@ void SeaGraph::InsertSignatureNodes(const art::DexFile::CodeItem* code_item, Reg } void SeaGraph::BuildMethodSeaGraph(const art::DexFile::CodeItem* code_item, - const art::DexFile& dex_file, uint32_t class_def_idx, + const art::DexFile& dex_file, uint16_t class_def_idx, uint32_t method_idx, uint32_t method_access_flags) { code_item_ = code_item; class_def_idx_ = class_def_idx; @@ -409,7 +409,7 @@ CodeGenData* SeaGraph::GenerateLLVM(const std::string& function_name, CodeGenData* SeaGraph::CompileMethod( const std::string& function_name, - const art::DexFile::CodeItem* code_item, uint32_t class_def_idx, + const art::DexFile::CodeItem* code_item, uint16_t class_def_idx, uint32_t method_idx, uint32_t method_access_flags, const art::DexFile& dex_file) { // Two passes: Builds the intermediate structure (non-SSA) of the sea-ir for the function. BuildMethodSeaGraph(code_item, dex_file, class_def_idx, method_idx, method_access_flags); diff --git a/compiler/sea_ir/ir/sea.h b/compiler/sea_ir/ir/sea.h index 92c2043dbd4..26b16be0195 100644 --- a/compiler/sea_ir/ir/sea.h +++ b/compiler/sea_ir/ir/sea.h @@ -262,7 +262,7 @@ class SeaGraph: IVisitable { static SeaGraph* GetGraph(const art::DexFile&); CodeGenData* CompileMethod(const std::string& function_name, - const art::DexFile::CodeItem* code_item, uint32_t class_def_idx, + const art::DexFile::CodeItem* code_item, uint16_t class_def_idx, uint32_t method_idx, uint32_t method_access_flags, const art::DexFile& dex_file); // Returns all regions corresponding to this SeaGraph. std::vector* GetRegions() { @@ -288,7 +288,7 @@ class SeaGraph: IVisitable { } TypeInference* ti_; - uint32_t class_def_idx_; + uint16_t class_def_idx_; uint32_t method_idx_; uint32_t method_access_flags_; @@ -311,7 +311,7 @@ class SeaGraph: IVisitable { // Builds the non-SSA sea-ir representation of the function @code_item from @dex_file // with class id @class_def_idx and method id @method_idx. void BuildMethodSeaGraph(const art::DexFile::CodeItem* code_item, - const art::DexFile& dex_file, uint32_t class_def_idx, + const art::DexFile& dex_file, uint16_t class_def_idx, uint32_t method_idx, uint32_t method_access_flags); // Computes immediate dominators for each region. // Precondition: ComputeMethodSeaGraph() diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc index cf1b6af8096..fc9e00c2cb6 100644 --- a/oatdump/oatdump.cc +++ b/oatdump/oatdump.cc @@ -176,9 +176,10 @@ class OatDumper { CHECK(oat_dex_file != NULL); UniquePtr dex_file(oat_dex_file->OpenDexFile()); if (dex_file.get() != NULL) { - uint32_t class_def_index; - bool found = dex_file->FindClassDefIndex(mh.GetDeclaringClassDescriptor(), class_def_index); - if (found) { + const DexFile::ClassDef* class_def = + dex_file->FindClassDef(mh.GetDeclaringClassDescriptor()); + if (class_def != NULL) { + uint16_t class_def_index = dex_file->GetIndexForClassDef(*class_def); const OatFile::OatClass* oat_class = oat_dex_file->GetOatClass(class_def_index); CHECK(oat_class != NULL); size_t method_index = m->GetMethodIndex(); @@ -284,18 +285,17 @@ class OatDumper { } ClassDataItemIterator it(dex_file, class_data); SkipAllFields(it); - uint32_t class_def_idx = dex_file.GetIndexForClassDef(class_def); uint32_t class_method_idx = 0; while (it.HasNextDirectMethod()) { const OatFile::OatMethod oat_method = oat_class.GetOatMethod(class_method_idx); - DumpOatMethod(os, class_def_idx, class_method_idx, oat_method, dex_file, + DumpOatMethod(os, class_def, class_method_idx, oat_method, dex_file, it.GetMemberIndex(), it.GetMethodCodeItem(), it.GetMemberAccessFlags()); class_method_idx++; it.Next(); } while (it.HasNextVirtualMethod()) { const OatFile::OatMethod oat_method = oat_class.GetOatMethod(class_method_idx); - DumpOatMethod(os, class_def_idx, class_method_idx, oat_method, dex_file, + DumpOatMethod(os, class_def, class_method_idx, oat_method, dex_file, it.GetMemberIndex(), it.GetMethodCodeItem(), it.GetMemberAccessFlags()); class_method_idx++; it.Next(); @@ -304,7 +304,8 @@ class OatDumper { os << std::flush; } - void DumpOatMethod(std::ostream& os, uint32_t class_def_idx, uint32_t class_method_index, + void DumpOatMethod(std::ostream& os, const DexFile::ClassDef& class_def, + uint32_t class_method_index, const OatFile::OatMethod& oat_method, const DexFile& dex_file, uint32_t dex_method_idx, const DexFile::CodeItem* code_item, uint32_t method_access_flags) { @@ -323,7 +324,8 @@ class OatDumper { indent1_os << "VERIFIER TYPE ANALYSIS:\n"; Indenter indent2_filter(indent1_os.rdbuf(), kIndentChar, kIndentBy1Count); std::ostream indent2_os(&indent2_filter); - DumpVerifier(indent2_os, dex_method_idx, &dex_file, class_def_idx, code_item, method_access_flags); + DumpVerifier(indent2_os, dex_method_idx, &dex_file, class_def, code_item, + method_access_flags); } { indent1_os << "OAT DATA:\n"; @@ -363,7 +365,7 @@ class OatDumper { oat_method.GetCode() != NULL ? "..." : ""); Indenter indent2_filter(indent1_os.rdbuf(), kIndentChar, kIndentBy1Count); std::ostream indent2_os(&indent2_filter); - DumpCode(indent2_os, oat_method, dex_method_idx, &dex_file, class_def_idx, code_item, + DumpCode(indent2_os, oat_method, dex_method_idx, &dex_file, class_def, code_item, method_access_flags); } } @@ -554,7 +556,7 @@ class OatDumper { void DumpVRegsAtDexPc(std::ostream& os, const OatFile::OatMethod& oat_method, uint32_t dex_method_idx, const DexFile* dex_file, - uint32_t class_def_idx, const DexFile::CodeItem* code_item, + const DexFile::ClassDef& class_def, const DexFile::CodeItem* code_item, uint32_t method_access_flags, uint32_t dex_pc) { static UniquePtr verifier; static const DexFile* verified_dex_file = NULL; @@ -563,7 +565,7 @@ class OatDumper { ScopedObjectAccess soa(Thread::Current()); mirror::DexCache* dex_cache = Runtime::Current()->GetClassLinker()->FindDexCache(*dex_file); mirror::ClassLoader* class_loader = NULL; - verifier.reset(new verifier::MethodVerifier(dex_file, dex_cache, class_loader, class_def_idx, + verifier.reset(new verifier::MethodVerifier(dex_file, dex_cache, class_loader, &class_def, code_item, dex_method_idx, NULL, method_access_flags, true, true)); verifier->Verify(); @@ -615,21 +617,21 @@ class OatDumper { } void DumpVerifier(std::ostream& os, uint32_t dex_method_idx, const DexFile* dex_file, - uint32_t class_def_idx, const DexFile::CodeItem* code_item, + const DexFile::ClassDef& class_def, const DexFile::CodeItem* code_item, uint32_t method_access_flags) { if ((method_access_flags & kAccNative) == 0) { ScopedObjectAccess soa(Thread::Current()); mirror::DexCache* dex_cache = Runtime::Current()->GetClassLinker()->FindDexCache(*dex_file); mirror::ClassLoader* class_loader = NULL; verifier::MethodVerifier::VerifyMethodAndDump(os, dex_method_idx, dex_file, dex_cache, - class_loader, class_def_idx, code_item, NULL, + class_loader, &class_def, code_item, NULL, method_access_flags); } } void DumpCode(std::ostream& os, const OatFile::OatMethod& oat_method, uint32_t dex_method_idx, const DexFile* dex_file, - uint32_t class_def_idx, const DexFile::CodeItem* code_item, + const DexFile::ClassDef& class_def, const DexFile::CodeItem* code_item, uint32_t method_access_flags) { const void* code = oat_method.GetCode(); size_t code_size = oat_method.GetCodeSize(); @@ -647,7 +649,7 @@ class OatDumper { if (dex_pc != DexFile::kDexNoIndex) { DumpGcMapAtNativePcOffset(os, oat_method, code_item, offset); if (kDumpVRegs) { - DumpVRegsAtDexPc(os, oat_method, dex_method_idx, dex_file, class_def_idx, code_item, + DumpVRegsAtDexPc(os, oat_method, dex_method_idx, dex_file, class_def, code_item, method_access_flags, dex_pc); } } diff --git a/runtime/Android.mk b/runtime/Android.mk index e324060ecb2..5edf7592d94 100644 --- a/runtime/Android.mk +++ b/runtime/Android.mk @@ -91,6 +91,7 @@ LIBART_COMMON_SRC_FILES := \ native/dalvik_system_VMStack.cc \ native/dalvik_system_Zygote.cc \ native/java_lang_Class.cc \ + native/java_lang_DexCache.cc \ native/java_lang_Object.cc \ native/java_lang_Runtime.cc \ native/java_lang_String.cc \ diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index c19f8724bb8..15eab9d1653 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -475,40 +475,33 @@ void ClassLinker::FinishInit() { // as the types of the field can't be resolved prior to the runtime being // fully initialized mirror::Class* java_lang_ref_Reference = GetClassRoot(kJavaLangRefReference); - mirror::Class* java_lang_ref_ReferenceQueue = FindSystemClass("Ljava/lang/ref/ReferenceQueue;"); - mirror::Class* java_lang_ref_FinalizerReference = FindSystemClass("Ljava/lang/ref/FinalizerReference;"); - - const DexFile& java_lang_dex = *java_lang_ref_Reference->GetDexCache()->GetDexFile(); + mirror::Class* java_lang_ref_FinalizerReference = + FindSystemClass("Ljava/lang/ref/FinalizerReference;"); mirror::ArtField* pendingNext = java_lang_ref_Reference->GetInstanceField(0); FieldHelper fh(pendingNext, this); CHECK_STREQ(fh.GetName(), "pendingNext"); - CHECK_EQ(java_lang_dex.GetFieldId(pendingNext->GetDexFieldIndex()).type_idx_, - java_lang_ref_Reference->GetDexTypeIndex()); + CHECK_STREQ(fh.GetTypeDescriptor(), "Ljava/lang/ref/Reference;"); mirror::ArtField* queue = java_lang_ref_Reference->GetInstanceField(1); fh.ChangeField(queue); CHECK_STREQ(fh.GetName(), "queue"); - CHECK_EQ(java_lang_dex.GetFieldId(queue->GetDexFieldIndex()).type_idx_, - java_lang_ref_ReferenceQueue->GetDexTypeIndex()); + CHECK_STREQ(fh.GetTypeDescriptor(), "Ljava/lang/ref/ReferenceQueue;"); mirror::ArtField* queueNext = java_lang_ref_Reference->GetInstanceField(2); fh.ChangeField(queueNext); CHECK_STREQ(fh.GetName(), "queueNext"); - CHECK_EQ(java_lang_dex.GetFieldId(queueNext->GetDexFieldIndex()).type_idx_, - java_lang_ref_Reference->GetDexTypeIndex()); + CHECK_STREQ(fh.GetTypeDescriptor(), "Ljava/lang/ref/Reference;"); mirror::ArtField* referent = java_lang_ref_Reference->GetInstanceField(3); fh.ChangeField(referent); CHECK_STREQ(fh.GetName(), "referent"); - CHECK_EQ(java_lang_dex.GetFieldId(referent->GetDexFieldIndex()).type_idx_, - GetClassRoot(kJavaLangObject)->GetDexTypeIndex()); + CHECK_STREQ(fh.GetTypeDescriptor(), "Ljava/lang/Object;"); mirror::ArtField* zombie = java_lang_ref_FinalizerReference->GetInstanceField(2); fh.ChangeField(zombie); CHECK_STREQ(fh.GetName(), "zombie"); - CHECK_EQ(java_lang_dex.GetFieldId(zombie->GetDexFieldIndex()).type_idx_, - GetClassRoot(kJavaLangObject)->GetDexTypeIndex()); + CHECK_STREQ(fh.GetTypeDescriptor(), "Ljava/lang/Object;"); gc::Heap* heap = Runtime::Current()->GetHeap(); heap->SetReferenceOffsets(referent->GetOffset(), @@ -1234,8 +1227,10 @@ mirror::Class* ClassLinker::AllocClass(Thread* self, mirror::Class* java_lang_Cl return NULL; } mirror::Class* klass = k->AsClass(); - klass->SetPrimitiveType(Primitive::kPrimNot); // default to not being primitive + klass->SetPrimitiveType(Primitive::kPrimNot); // Default to not being primitive. klass->SetClassSize(class_size); + klass->SetDexClassDefIndex(DexFile::kDexNoIndex16); // Default to no valid class def index. + klass->SetDexTypeIndex(DexFile::kDexNoIndex16); // Default to no valid type index. return klass; } @@ -1499,26 +1494,21 @@ size_t ClassLinker::SizeOfClass(const DexFile& dex_file, return size; } -const OatFile::OatClass* ClassLinker::GetOatClass(const DexFile& dex_file, const char* descriptor) { - DCHECK(descriptor != NULL); +const OatFile::OatClass* ClassLinker::GetOatClass(const DexFile& dex_file, uint16_t class_def_idx) { + DCHECK_NE(class_def_idx, DexFile::kDexNoIndex16); const OatFile* oat_file = FindOpenedOatFileForDexFile(dex_file); - CHECK(oat_file != NULL) << dex_file.GetLocation() << " " << descriptor; + CHECK(oat_file != NULL) << dex_file.GetLocation(); const OatFile::OatDexFile* oat_dex_file = oat_file->GetOatDexFile(dex_file.GetLocation()); - CHECK(oat_dex_file != NULL) << dex_file.GetLocation() << " " << descriptor; - uint32_t class_def_index; - bool found = dex_file.FindClassDefIndex(descriptor, class_def_index); - CHECK(found) << dex_file.GetLocation() << " " << descriptor; - const OatFile::OatClass* oat_class = oat_dex_file->GetOatClass(class_def_index); - CHECK(oat_class != NULL) << dex_file.GetLocation() << " " << descriptor; + CHECK(oat_dex_file != NULL) << dex_file.GetLocation(); + const OatFile::OatClass* oat_class = oat_dex_file->GetOatClass(class_def_idx); + CHECK(oat_class != NULL) << dex_file.GetLocation() << " " << class_def_idx; return oat_class; } -static uint32_t GetOatMethodIndexFromMethodIndex(const DexFile& dex_file, uint32_t method_idx) { - const DexFile::MethodId& method_id = dex_file.GetMethodId(method_idx); - const DexFile::TypeId& type_id = dex_file.GetTypeId(method_id.class_idx_); - const DexFile::ClassDef* class_def = dex_file.FindClassDef(dex_file.GetTypeDescriptor(type_id)); - CHECK(class_def != NULL); - const byte* class_data = dex_file.GetClassData(*class_def); +static uint32_t GetOatMethodIndexFromMethodIndex(const DexFile& dex_file, uint16_t class_def_idx, + uint32_t method_idx) { + const DexFile::ClassDef& class_def = dex_file.GetClassDef(class_def_idx); + const byte* class_data = dex_file.GetClassData(class_def); CHECK(class_data != NULL); ClassDataItemIterator it(dex_file, class_data); // Skip fields @@ -1572,11 +1562,13 @@ const OatFile::OatMethod ClassLinker::GetOatMethodFor(const mirror::ArtMethod* m } CHECK(found) << "Didn't find oat method index for virtual method: " << PrettyMethod(method); } - ClassHelper kh(declaring_class); - UniquePtr oat_class(GetOatClass(kh.GetDexFile(), kh.GetDescriptor())); + UniquePtr + oat_class(GetOatClass(*declaring_class->GetDexCache()->GetDexFile(), + declaring_class->GetDexClassDefIndex())); CHECK(oat_class.get() != NULL); DCHECK_EQ(oat_method_index, GetOatMethodIndexFromMethodIndex(*declaring_class->GetDexCache()->GetDexFile(), + method->GetDeclaringClass()->GetDexClassDefIndex(), method->GetDexMethodIndex())); return oat_class->GetOatMethod(oat_method_index); @@ -1600,12 +1592,11 @@ const void* ClassLinker::GetOatCodeFor(const mirror::ArtMethod* method) { return result; } -const void* ClassLinker::GetOatCodeFor(const DexFile& dex_file, uint32_t method_idx) { - const DexFile::MethodId& method_id = dex_file.GetMethodId(method_idx); - const char* descriptor = dex_file.GetTypeDescriptor(dex_file.GetTypeId(method_id.class_idx_)); - uint32_t oat_method_idx = GetOatMethodIndexFromMethodIndex(dex_file, method_idx); - UniquePtr oat_class(GetOatClass(dex_file, descriptor)); - CHECK(oat_class.get() != NULL); +const void* ClassLinker::GetOatCodeFor(const DexFile& dex_file, uint16_t class_def_idx, + uint32_t method_idx) { + UniquePtr oat_class(GetOatClass(dex_file, class_def_idx)); + CHECK(oat_class.get() != nullptr); + uint32_t oat_method_idx = GetOatMethodIndexFromMethodIndex(dex_file, class_def_idx, method_idx); return oat_class->GetOatMethod(oat_method_idx).GetCode(); } @@ -1642,7 +1633,7 @@ void ClassLinker::FixupStaticTrampolines(mirror::Class* klass) { // OAT file unavailable return; } - UniquePtr oat_class(GetOatClass(dex_file, kh.GetDescriptor())); + UniquePtr oat_class(GetOatClass(dex_file, klass->GetDexClassDefIndex())); CHECK(oat_class.get() != NULL); ClassDataItemIterator it(dex_file, class_data); // Skip fields @@ -1734,6 +1725,7 @@ void ClassLinker::LoadClass(const DexFile& dex_file, DCHECK_EQ(klass->GetPrimitiveType(), Primitive::kPrimNot); klass->SetStatus(mirror::Class::kStatusIdx, NULL); + klass->SetDexClassDefIndex(dex_file.GetIndexForClassDef(dex_class_def)); klass->SetDexTypeIndex(dex_class_def.class_idx_); // Load fields fields. @@ -1781,7 +1773,7 @@ void ClassLinker::LoadClass(const DexFile& dex_file, UniquePtr oat_class; if (Runtime::Current()->IsStarted() && !Runtime::Current()->UseCompileTimeClassPath()) { - oat_class.reset(GetOatClass(dex_file, descriptor)); + oat_class.reset(GetOatClass(dex_file, klass->GetDexClassDefIndex())); } // Load methods. @@ -1876,7 +1868,8 @@ mirror::ArtMethod* ClassLinker::LoadMethod(Thread* self, const DexFile& dex_file if (klass->GetClassLoader() != NULL) { // All non-boot finalizer methods are flagged klass->SetFinalizable(); } else { - StringPiece klass_descriptor(dex_file.StringByTypeIdx(klass->GetDexTypeIndex())); + ClassHelper kh(klass.get()); + StringPiece klass_descriptor(kh.GetDescriptor()); // The Enum class declares a "final" finalize() method to prevent subclasses from // introducing a finalizer. We don't want to set the finalizable flag for Enum or its // subclasses, so we exclude it here. @@ -2342,12 +2335,16 @@ mirror::Class* ClassLinker::LookupClassFromImage(const char* descriptor) { const DexFile* dex_file = dex_cache->GetDexFile(); // First search using the class def map, but don't bother for non-class types. if (descriptor[0] == 'L') { - const DexFile::ClassDef* class_def = dex_file->FindClassDef(descriptor); - if (class_def != NULL) { - mirror::Class* klass = dex_cache->GetResolvedType(class_def->class_idx_); - if (klass != NULL) { - self->EndAssertNoThreadSuspension(old_no_suspend_cause); - return klass; + const DexFile::StringId* descriptor_string_id = dex_file->FindStringId(descriptor); + if (descriptor_string_id != NULL) { + const DexFile::TypeId* type_id = + dex_file->FindTypeId(dex_file->GetIndexForStringId(*descriptor_string_id)); + if (type_id != NULL) { + mirror::Class* klass = dex_cache->GetResolvedType(dex_file->GetIndexForTypeId(*type_id)); + if (klass != NULL) { + self->EndAssertNoThreadSuspension(old_no_suspend_cause); + return klass; + } } } } @@ -2458,8 +2455,9 @@ void ClassLinker::VerifyClass(mirror::Class* klass) { verifier::MethodVerifier::FailureKind verifier_failure = verifier::MethodVerifier::kNoFailure; std::string error_msg; if (!preverified) { - verifier_failure = verifier::MethodVerifier::VerifyClass(klass, error_msg, - Runtime::Current()->IsCompiler()); + verifier_failure = verifier::MethodVerifier::VerifyClass(klass, + Runtime::Current()->IsCompiler(), + &error_msg); } if (preverified || verifier_failure != verifier::MethodVerifier::kHardFailure) { if (!preverified && verifier_failure != verifier::MethodVerifier::kNoFailure) { @@ -2530,7 +2528,6 @@ bool ClassLinker::VerifyClassUsingOatFile(const DexFile& dex_file, mirror::Class } } - const OatFile* oat_file = FindOpenedOatFileForDexFile(dex_file); // Make this work with gtests, which do not set up the image properly. // TODO: we should clean up gtests to set up the image path properly. @@ -2542,9 +2539,7 @@ bool ClassLinker::VerifyClassUsingOatFile(const DexFile& dex_file, mirror::Class const OatFile::OatDexFile* oat_dex_file = oat_file->GetOatDexFile(dex_file.GetLocation()); CHECK(oat_dex_file != NULL) << dex_file.GetLocation() << " " << PrettyClass(klass); const char* descriptor = ClassHelper(klass).GetDescriptor(); - uint32_t class_def_index; - bool found = dex_file.FindClassDefIndex(descriptor, class_def_index); - CHECK(found) << dex_file.GetLocation() << " " << PrettyClass(klass) << " " << descriptor; + uint16_t class_def_index = klass->GetDexClassDefIndex(); UniquePtr oat_class(oat_dex_file->GetOatClass(class_def_index)); CHECK(oat_class.get() != NULL) << dex_file.GetLocation() << " " << PrettyClass(klass) << " " << descriptor; @@ -2657,8 +2652,6 @@ mirror::Class* ClassLinker::CreateProxyClass(mirror::String* name, klass->SetStatus(mirror::Class::kStatusIdx, self); - klass->SetDexTypeIndex(DexFile::kDexNoIndex16); - // Instance fields are inherited, but we add a couple of static fields... { mirror::ObjectArray* sfields = AllocArtFieldArray(self, 2); @@ -3265,10 +3258,8 @@ bool ClassLinker::LinkClass(SirtRef& klass, bool ClassLinker::LoadSuperAndInterfaces(SirtRef& klass, const DexFile& dex_file) { CHECK_EQ(mirror::Class::kStatusIdx, klass->GetStatus()); - StringPiece descriptor(dex_file.StringByTypeIdx(klass->GetDexTypeIndex())); - const DexFile::ClassDef* class_def = dex_file.FindClassDef(descriptor); - CHECK(class_def != NULL); - uint16_t super_class_idx = class_def->superclass_idx_; + const DexFile::ClassDef& class_def = dex_file.GetClassDef(klass->GetDexClassDefIndex()); + uint16_t super_class_idx = class_def.superclass_idx_; if (super_class_idx != DexFile::kDexNoIndex16) { mirror::Class* super_class = ResolveType(dex_file, super_class_idx, klass.get()); if (super_class == NULL) { @@ -3284,7 +3275,7 @@ bool ClassLinker::LoadSuperAndInterfaces(SirtRef& klass, const De } klass->SetSuperClass(super_class); } - const DexFile::TypeList* interfaces = dex_file.GetInterfacesList(*class_def); + const DexFile::TypeList* interfaces = dex_file.GetInterfacesList(class_def); if (interfaces != NULL) { for (size_t i = 0; i < interfaces->Size(); i++) { uint16_t idx = interfaces->GetTypeItem(i).type_idx_; diff --git a/runtime/class_linker.h b/runtime/class_linker.h index 20efbb43a9f..3ffcf14447c 100644 --- a/runtime/class_linker.h +++ b/runtime/class_linker.h @@ -329,7 +329,7 @@ class ClassLinker { SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); // Get the oat code for a method from a method index. - const void* GetOatCodeFor(const DexFile& dex_file, uint32_t method_idx) + const void* GetOatCodeFor(const DexFile& dex_file, uint16_t class_def_idx, uint32_t method_idx) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); pid_t GetClassesLockOwner(); // For SignalCatcher. @@ -424,7 +424,7 @@ class ClassLinker { void FixupStaticTrampolines(mirror::Class* klass) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); // Finds the associated oat class for a dex_file and descriptor - const OatFile::OatClass* GetOatClass(const DexFile& dex_file, const char* descriptor) + const OatFile::OatClass* GetOatClass(const DexFile& dex_file, uint16_t class_def_idx) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); void RegisterDexFileLocked(const DexFile& dex_file, SirtRef& dex_cache) diff --git a/runtime/class_linker_test.cc b/runtime/class_linker_test.cc index 0fa0ffbc56d..bea1139679b 100644 --- a/runtime/class_linker_test.cc +++ b/runtime/class_linker_test.cc @@ -508,6 +508,7 @@ struct ClassOffsets : public CheckOffsets { offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::Class, access_flags_), "accessFlags")); offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::Class, class_size_), "classSize")); offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::Class, clinit_thread_id_), "clinitThreadId")); + offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::Class, dex_class_def_idx_), "dexClassDefIndex")); offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::Class, dex_type_idx_), "dexTypeIndex")); offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::Class, num_reference_instance_fields_), "numReferenceInstanceFields")); offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::Class, num_reference_static_fields_), "numReferenceStaticFields")); @@ -570,10 +571,6 @@ struct ProxyOffsets : public CheckOffsets { struct ClassClassOffsets : public CheckOffsets { ClassClassOffsets() : CheckOffsets(true, "Ljava/lang/Class;") { - // padding 32-bit - CHECK_EQ(OFFSETOF_MEMBER(mirror::ClassClass, padding_) + 4, - OFFSETOF_MEMBER(mirror::ClassClass, serialVersionUID_)); - // alphabetical 64-bit offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::ClassClass, serialVersionUID_), "serialVersionUID")); }; @@ -585,11 +582,11 @@ struct StringClassOffsets : public CheckOffsets { offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::StringClass, ASCII_), "ASCII")); offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::StringClass, CASE_INSENSITIVE_ORDER_), "CASE_INSENSITIVE_ORDER")); - // padding 32-bit - offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::StringClass, REPLACEMENT_CHAR_), "REPLACEMENT_CHAR")); - // alphabetical 64-bit offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::StringClass, serialVersionUID_), "serialVersionUID")); + + // alphabetical 32-bit + offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::StringClass, REPLACEMENT_CHAR_), "REPLACEMENT_CHAR")); }; }; @@ -606,6 +603,7 @@ struct ArtMethodClassOffsets : public CheckOffsets { struct DexCacheOffsets : public CheckOffsets { DexCacheOffsets() : CheckOffsets(false, "Ljava/lang/DexCache;") { // alphabetical references + offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::DexCache, dex_), "dex")); offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::DexCache, initialized_static_storage_), "initializedStaticStorage")); offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::DexCache, location_), "location")); offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::DexCache, resolved_fields_), "resolvedFields")); diff --git a/runtime/dex_file.cc b/runtime/dex_file.cc index 4fd9a608c17..e81c456ccf8 100644 --- a/runtime/dex_file.cc +++ b/runtime/dex_file.cc @@ -48,7 +48,7 @@ namespace art { const byte DexFile::kDexMagic[] = { 'd', 'e', 'x', '\n' }; const byte DexFile::kDexMagicVersion[] = { '0', '3', '5', '\0' }; -DexFile::ClassPathEntry DexFile::FindInClassPath(const StringPiece& descriptor, +DexFile::ClassPathEntry DexFile::FindInClassPath(const char* descriptor, const ClassPath& class_path) { for (size_t i = 0; i != class_path.size(); ++i) { const DexFile* dex_file = class_path[i]; @@ -251,56 +251,11 @@ DexFile::~DexFile() { // the global reference table is otherwise empty! } -class ScopedJniMonitorLock { - public: - ScopedJniMonitorLock(JNIEnv* env, jobject locked) : env_(env), locked_(locked) { - env->MonitorEnter(locked_); - } - ~ScopedJniMonitorLock() { - env_->MonitorExit(locked_); - } - private: - JNIEnv* const env_; - const jobject locked_; -}; - -jobject DexFile::GetDexObject(JNIEnv* env) const { - { - ScopedJniMonitorLock lock(env, WellKnownClasses::com_android_dex_Dex); - if (dex_object_ != NULL) { - return dex_object_; - } - } - void* address = const_cast(reinterpret_cast(begin_)); - jobject byte_buffer = env->NewDirectByteBuffer(address, size_); - if (byte_buffer == NULL) { - return NULL; - } - - ScopedJniMonitorLock lock(env, WellKnownClasses::com_android_dex_Dex); - // Re-test to see if someone beat us to the creation when we had the lock released. - if (dex_object_ != NULL) { - return dex_object_; - } - jvalue args[1]; - args[0].l = byte_buffer; - jobject local = env->CallStaticObjectMethodA(WellKnownClasses::com_android_dex_Dex, - WellKnownClasses::com_android_dex_Dex_create, - args); - if (local == NULL) { - return NULL; - } - - dex_object_ = env->NewGlobalRef(local); - return dex_object_; -} - bool DexFile::Init() { InitMembers(); if (!CheckMagicAndVersion()) { return false; } - InitIndex(); return true; } @@ -351,28 +306,36 @@ uint32_t DexFile::GetVersion() const { return atoi(version); } -void DexFile::InitIndex() { - CHECK_EQ(index_.size(), 0U) << GetLocation(); - for (size_t i = 0; i < NumClassDefs(); ++i) { - const ClassDef& class_def = GetClassDef(i); - const char* descriptor = GetClassDescriptor(class_def); - index_.Put(descriptor, i); +const DexFile::ClassDef* DexFile::FindClassDef(const char* descriptor) const { + size_t num_class_defs = NumClassDefs(); + if (num_class_defs == 0) { + return NULL; } -} - -bool DexFile::FindClassDefIndex(const StringPiece& descriptor, uint32_t& idx) const { - Index::const_iterator it = index_.find(descriptor); - if (it == index_.end()) { - return false; + const StringId* string_id = FindStringId(descriptor); + if (string_id == NULL) { + return NULL; } - idx = it->second; - return true; + const TypeId* type_id = FindTypeId(GetIndexForStringId(*string_id)); + if (type_id == NULL) { + return NULL; + } + uint16_t type_idx = GetIndexForTypeId(*type_id); + for (size_t i = 0; i < num_class_defs; ++i) { + const ClassDef& class_def = GetClassDef(i); + if (class_def.class_idx_ == type_idx) { + return &class_def; + } + } + return NULL; } -const DexFile::ClassDef* DexFile::FindClassDef(const StringPiece& descriptor) const { - uint32_t idx; - if (FindClassDefIndex(descriptor, idx)) { - return &GetClassDef(idx); +const DexFile::ClassDef* DexFile::FindClassDef(uint16_t type_idx) const { + size_t num_class_defs = NumClassDefs(); + for (size_t i = 0; i < num_class_defs; ++i) { + const ClassDef& class_def = GetClassDef(i); + if (class_def.class_idx_ == type_idx) { + return &class_def; + } } return NULL; } diff --git a/runtime/dex_file.h b/runtime/dex_file.h index 26635ae255f..7be5cb848f5 100644 --- a/runtime/dex_file.h +++ b/runtime/dex_file.h @@ -339,7 +339,7 @@ class DexFile { typedef std::vector ClassPath; // Search a collection of DexFiles for a descriptor - static ClassPathEntry FindInClassPath(const StringPiece& descriptor, + static ClassPathEntry FindInClassPath(const char* descriptor, const ClassPath& class_path); // Returns the checksum of a file for comparison with GetLocationChecksum(). @@ -376,10 +376,6 @@ class DexFile { return location_checksum_; } - // Returns a com.android.dex.Dex object corresponding to the mapped-in dex file. - // Used by managed code to implement annotations. - jobject GetDexObject(JNIEnv* env) const; - const Header& GetHeader() const { DCHECK(header_ != NULL) << GetLocation(); return *header_; @@ -584,12 +580,12 @@ class DexFile { } // Returns the ClassDef at the specified index. - const ClassDef& GetClassDef(uint32_t idx) const { + const ClassDef& GetClassDef(uint16_t idx) const { DCHECK_LT(idx, NumClassDefs()) << GetLocation(); return class_defs_[idx]; } - uint32_t GetIndexForClassDef(const ClassDef& class_def) const { + uint16_t GetIndexForClassDef(const ClassDef& class_def) const { CHECK_GE(&class_def, class_defs_) << GetLocation(); CHECK_LT(&class_def, class_defs_ + header_->class_defs_size_) << GetLocation(); return &class_def - class_defs_; @@ -601,10 +597,10 @@ class DexFile { } // Looks up a class definition by its class descriptor. - const ClassDef* FindClassDef(const StringPiece& descriptor) const; + const ClassDef* FindClassDef(const char* descriptor) const; - // Looks up a class definition index by its class descriptor. - bool FindClassDefIndex(const StringPiece& descriptor, uint32_t& idx) const; + // Looks up a class definition by its type index. + const ClassDef* FindClassDef(uint16_t type_idx) const; const TypeList* GetInterfacesList(const ClassDef& class_def) const { if (class_def.interfaces_off_ == 0) { @@ -809,6 +805,14 @@ class DexFile { bool DisableWrite() const; + const byte* Begin() const { + return begin_; + } + + size_t Size() const { + return size_; + } + private: // Opens a .dex file static const DexFile* OpenFile(const std::string& filename, @@ -840,7 +844,6 @@ class DexFile { location_(location), location_checksum_(location_checksum), mem_map_(mem_map), - dex_object_(NULL), modification_lock("DEX modification lock"), header_(0), string_ids_(0), @@ -853,23 +856,12 @@ class DexFile { CHECK_GT(size_, 0U) << GetLocation(); } - const byte* Begin() const { - return begin_; - } - - size_t Size() const { - return size_; - } - // Top-level initializer that calls other Init methods. bool Init(); // Caches pointers into to the various file sections. void InitMembers(); - // Builds the index of descriptors to class definitions. - void InitIndex(); - // Returns true if the header magic and version numbers are of the expected values. bool CheckMagicAndVersion() const; @@ -877,10 +869,6 @@ class DexFile { DexDebugNewPositionCb position_cb, DexDebugNewLocalCb local_cb, void* context, const byte* stream, LocalInfo* local_in_reg) const; - // The index of descriptors to class definition indexes (as opposed to type id indexes) - typedef SafeMap Index; - Index index_; - // The base address of the memory mapping. const byte* const begin_; @@ -898,10 +886,6 @@ class DexFile { // Manages the underlying memory allocation. UniquePtr mem_map_; - // A cached com.android.dex.Dex instance, possibly NULL. Use GetDexObject. - // TODO: this is mutable as it shouldn't be here. We should move it to the dex cache or similar. - mutable jobject dex_object_; - // The DEX-to-DEX compiler uses this lock to ensure thread safety when // enabling write access to a read-only DEX file. // TODO: move to Locks::dex_file_modification_lock. diff --git a/runtime/entrypoints/quick/quick_invoke_entrypoints.cc b/runtime/entrypoints/quick/quick_invoke_entrypoints.cc index 1d8022f8034..07c1c015aa5 100644 --- a/runtime/entrypoints/quick/quick_invoke_entrypoints.cc +++ b/runtime/entrypoints/quick/quick_invoke_entrypoints.cc @@ -32,7 +32,7 @@ extern "C" uint64_t artInvokeInterfaceTrampoline(mirror::ArtMethod* interface_me Thread* self, mirror::ArtMethod** sp) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { mirror::ArtMethod* method; - if (LIKELY(interface_method->GetDexMethodIndex() != DexFile::kDexNoIndex16)) { + if (LIKELY(interface_method->GetDexMethodIndex() != DexFile::kDexNoIndex)) { method = this_object->GetClass()->FindVirtualMethodForInterface(interface_method); if (UNLIKELY(method == NULL)) { FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsAndArgs); diff --git a/runtime/mirror/art_method-inl.h b/runtime/mirror/art_method-inl.h index 224b2ba0d47..ccf3e59f189 100644 --- a/runtime/mirror/art_method-inl.h +++ b/runtime/mirror/art_method-inl.h @@ -178,7 +178,7 @@ inline uint32_t ArtMethod::GetOatNativeGcMapOffset() const { } inline bool ArtMethod::IsRuntimeMethod() const { - return GetDexMethodIndex() == DexFile::kDexNoIndex16; + return GetDexMethodIndex() == DexFile::kDexNoIndex; } inline bool ArtMethod::IsCalleeSaveMethod() const { diff --git a/runtime/mirror/class.cc b/runtime/mirror/class.cc index 328c67deb11..c128eded0a6 100644 --- a/runtime/mirror/class.cc +++ b/runtime/mirror/class.cc @@ -119,7 +119,10 @@ void Class::SetDexCache(DexCache* new_dex_cache) { } void Class::SetClassSize(size_t new_class_size) { - DCHECK_GE(new_class_size, GetClassSize()) << " class=" << PrettyTypeOf(this); + if (kIsDebugBuild && (new_class_size < GetClassSize())) { + DumpClass(LOG(ERROR), kDumpClassFullDetail); + CHECK_GE(new_class_size, GetClassSize()) << " class=" << PrettyTypeOf(this); + } SetField32(OFFSET_OF_OBJECT_MEMBER(Class, class_size_), new_class_size, false); } @@ -291,22 +294,8 @@ bool Class::IsInSamePackage(const Class* that) const { return true; } // Compare the package part of the descriptor string. - if (LIKELY(!klass1->IsProxyClass() && !klass2->IsProxyClass())) { - ClassHelper kh(klass1); - const DexFile* dex_file1 = &kh.GetDexFile(); - const DexFile::TypeId* type_id1 = &dex_file1->GetTypeId(klass1->GetDexTypeIndex()); - const char* descriptor1 = dex_file1->GetTypeDescriptor(*type_id1); - kh.ChangeClass(klass2); - const DexFile* dex_file2 = &kh.GetDexFile(); - const DexFile::TypeId* type_id2 = &dex_file2->GetTypeId(klass2->GetDexTypeIndex()); - const char* descriptor2 = dex_file2->GetTypeDescriptor(*type_id2); - return IsInSamePackage(descriptor1, descriptor2); - } - ClassHelper kh(klass1); - std::string descriptor1(kh.GetDescriptor()); - kh.ChangeClass(klass2); - std::string descriptor2(kh.GetDescriptor()); - return IsInSamePackage(descriptor1, descriptor2); + return IsInSamePackage(ClassHelper(klass1).GetDescriptor(), + ClassHelper(klass2).GetDescriptor()); } bool Class::IsClassClass() const { diff --git a/runtime/mirror/class.h b/runtime/mirror/class.h index 99f3850b9be..d97b603ad8e 100644 --- a/runtime/mirror/class.h +++ b/runtime/mirror/class.h @@ -726,6 +726,14 @@ class MANAGED Class : public StaticStorageBase { return GetFieldObject(OFFSET_OF_OBJECT_MEMBER(Class, verify_error_class_), false); } + uint16_t GetDexClassDefIndex() const { + return GetField32(OFFSET_OF_OBJECT_MEMBER(Class, dex_class_def_idx_), false); + } + + void SetDexClassDefIndex(uint16_t class_def_idx) { + SetField32(OFFSET_OF_OBJECT_MEMBER(Class, dex_class_def_idx_), class_def_idx, false); + } + uint16_t GetDexTypeIndex() const { return GetField32(OFFSET_OF_OBJECT_MEMBER(Class, dex_type_idx_), false); } @@ -807,7 +815,7 @@ class MANAGED Class : public StaticStorageBase { // If class verify fails, we must return same error on subsequent tries. Class* verify_error_class_; - // virtual methods defined in this class; invoked through vtable + // Virtual methods defined in this class; invoked through vtable. ObjectArray* virtual_methods_; // Virtual method table (vtable), for use by "invoke-virtual". The vtable from the superclass is @@ -816,24 +824,28 @@ class MANAGED Class : public StaticStorageBase { // virtual_ methods_ for miranda methods. ObjectArray* vtable_; - // access flags; low 16 bits are defined by VM spec + // Access flags; low 16 bits are defined by VM spec. uint32_t access_flags_; // Total size of the Class instance; used when allocating storage on gc heap. // See also object_size_. size_t class_size_; - // tid used to check for recursive invocation + // Tid used to check for recursive invocation. pid_t clinit_thread_id_; - // type index from dex file + // ClassDef index in dex file, -1 if no class definition such as an array. + // TODO: really 16bits + int32_t dex_class_def_idx_; + + // Type index in dex file. // TODO: really 16bits - uint32_t dex_type_idx_; + int32_t dex_type_idx_; - // number of instance fields that are object refs + // Number of instance fields that are object refs. size_t num_reference_instance_fields_; - // number of static fields that are object refs + // Number of static fields that are object refs, size_t num_reference_static_fields_; // Total object size; used when allocating storage on gc heap. @@ -841,7 +853,7 @@ class MANAGED Class : public StaticStorageBase { // See also class_size_. size_t object_size_; - // primitive type value, or Primitive::kPrimNot (0); set for generated prim classes + // Primitive type value, or Primitive::kPrimNot (0); set for generated primitive classes. Primitive::Type primitive_type_; // Bitmap of offsets of ifields. @@ -850,7 +862,7 @@ class MANAGED Class : public StaticStorageBase { // Bitmap of offsets of sfields. uint32_t reference_static_offsets_; - // state of class initialization + // State of class initialization. Status status_; // TODO: ? @@ -873,7 +885,6 @@ std::ostream& operator<<(std::ostream& os, const Class::Status& rhs); class MANAGED ClassClass : public Class { private: - int32_t padding_; int64_t serialVersionUID_; friend struct art::ClassClassOffsets; // for verifying offset information DISALLOW_IMPLICIT_CONSTRUCTORS(ClassClass); diff --git a/runtime/mirror/dex_cache.h b/runtime/mirror/dex_cache.h index 6cfab9e425f..0522f134afa 100644 --- a/runtime/mirror/dex_cache.h +++ b/runtime/mirror/dex_cache.h @@ -164,6 +164,7 @@ class MANAGED DexCache : public Object { } private: + Object* dex_; ObjectArray* initialized_static_storage_; String* location_; ObjectArray* resolved_fields_; diff --git a/runtime/mirror/proxy.h b/runtime/mirror/proxy.h index 7c5bc39429e..18a84dcbdba 100644 --- a/runtime/mirror/proxy.h +++ b/runtime/mirror/proxy.h @@ -25,6 +25,8 @@ struct ProxyOffsets; namespace mirror { +// All proxy objects have a class which is a synthesized proxy class. The synthesized proxy class +// has the static fields used to implement reflection on proxy objects. class MANAGED SynthesizedProxyClass : public Class { public: ObjectArray* GetInterfaces() { @@ -41,6 +43,7 @@ class MANAGED SynthesizedProxyClass : public Class { DISALLOW_IMPLICIT_CONSTRUCTORS(SynthesizedProxyClass); }; +// C++ mirror of java.lang.reflect.Proxy. class MANAGED Proxy : public Object { private: Object* h_; diff --git a/runtime/mirror/string.h b/runtime/mirror/string.h index 01d8f318ffc..1879f04befe 100644 --- a/runtime/mirror/string.h +++ b/runtime/mirror/string.h @@ -156,8 +156,8 @@ class MANAGED StringClass : public Class { private: CharArray* ASCII_; Object* CASE_INSENSITIVE_ORDER_; - uint32_t REPLACEMENT_CHAR_; int64_t serialVersionUID_; + uint32_t REPLACEMENT_CHAR_; friend struct art::StringClassOffsets; // for verifying offset information DISALLOW_IMPLICIT_CONSTRUCTORS(StringClass); }; diff --git a/runtime/native/dalvik_system_DexFile.cc b/runtime/native/dalvik_system_DexFile.cc index 2f4e427bb6d..d2a6c0edb4e 100644 --- a/runtime/native/dalvik_system_DexFile.cc +++ b/runtime/native/dalvik_system_DexFile.cc @@ -150,7 +150,7 @@ static jclass DexFile_defineClassNative(JNIEnv* env, jclass, jstring javaName, j return NULL; } const std::string descriptor(DotToDescriptor(class_name.c_str())); - const DexFile::ClassDef* dex_class_def = dex_file->FindClassDef(descriptor); + const DexFile::ClassDef* dex_class_def = dex_file->FindClassDef(descriptor.c_str()); if (dex_class_def == NULL) { VLOG(class_linker) << "Failed to find dex_class_def"; return NULL; diff --git a/runtime/native/java_lang_Class.cc b/runtime/native/java_lang_Class.cc index a7296996da5..d3011cb0132 100644 --- a/runtime/native/java_lang_Class.cc +++ b/runtime/native/java_lang_Class.cc @@ -78,35 +78,6 @@ static jclass Class_classForName(JNIEnv* env, jclass, jstring javaName, jboolean return soa.AddLocalReference(c); } -static jint Class_getAnnotationDirectoryOffset(JNIEnv* env, jclass javaClass) { - ScopedObjectAccess soa(env); - mirror::Class* c = DecodeClass(soa, javaClass); - if (c->IsPrimitive() || c->IsArrayClass() || c->IsProxyClass()) { - return 0; // primitive, array and proxy classes don't have class definitions - } - const DexFile::ClassDef* class_def = ClassHelper(c).GetClassDef(); - if (class_def == NULL) { - return 0; // not found - } else { - return class_def->annotations_off_; - } -} - -static jobject Class_getDex(JNIEnv* env, jobject javaClass) { - ScopedObjectAccess soa(env); - mirror::Class* c = DecodeClass(soa, javaClass); - - mirror::DexCache* dex_cache = c->GetDexCache(); - if (dex_cache == NULL) { - return NULL; - } - const DexFile* dex_file = dex_cache->GetDexFile(); - if (dex_file == NULL) { - return NULL; - } - return dex_file->GetDexObject(env); -} - static jstring Class_getNameNative(JNIEnv* env, jobject javaThis) { ScopedObjectAccess soa(env); mirror::Class* c = DecodeClass(soa, javaThis); @@ -122,8 +93,6 @@ static jobjectArray Class_getProxyInterfaces(JNIEnv* env, jobject javaThis) { static JNINativeMethod gMethods[] = { NATIVE_METHOD(Class, classForName, "(Ljava/lang/String;ZLjava/lang/ClassLoader;)Ljava/lang/Class;"), - NATIVE_METHOD(Class, getAnnotationDirectoryOffset, "()I"), - NATIVE_METHOD(Class, getDex, "()Lcom/android/dex/Dex;"), NATIVE_METHOD(Class, getNameNative, "()Ljava/lang/String;"), NATIVE_METHOD(Class, getProxyInterfaces, "()[Ljava/lang/Class;"), }; diff --git a/runtime/native/java_lang_DexCache.cc b/runtime/native/java_lang_DexCache.cc new file mode 100644 index 00000000000..f8eeb2906ec --- /dev/null +++ b/runtime/native/java_lang_DexCache.cc @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2008 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 "dex_file.h" +#include "mirror/dex_cache.h" +#include "mirror/object-inl.h" +#include "scoped_thread_state_change.h" +#include "well_known_classes.h" + +namespace art { + +static jobject DexCache_getDexNative(JNIEnv* env, jobject javaDexCache) { + ScopedObjectAccess soa(env); + mirror::DexCache* dex_cache = soa.Decode(javaDexCache); + // Should only be called while holding the lock on the dex cache. + DCHECK_EQ(dex_cache->GetThinLockId(), soa.Self()->GetThinLockId()); + const DexFile* dex_file = dex_cache->GetDexFile(); + if (dex_file == NULL) { + return NULL; + } + void* address = const_cast(reinterpret_cast(dex_file->Begin())); + jobject byte_buffer = env->NewDirectByteBuffer(address, dex_file->Size()); + if (byte_buffer == NULL) { + DCHECK(soa.Self()->IsExceptionPending()); + return NULL; + } + + jvalue args[1]; + args[0].l = byte_buffer; + return env->CallStaticObjectMethodA(WellKnownClasses::com_android_dex_Dex, + WellKnownClasses::com_android_dex_Dex_create, + args); +} + +static JNINativeMethod gMethods[] = { + NATIVE_METHOD(DexCache, getDexNative, "()Lcom/android/dex/Dex;"), +}; + +void register_java_lang_DexCache(JNIEnv* env) { + REGISTER_NATIVE_METHODS("java/lang/DexCache"); +} + +} // namespace art diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc index afa823dbd9a..4c970172b48 100644 --- a/runtime/oat_file.cc +++ b/runtime/oat_file.cc @@ -365,7 +365,7 @@ const DexFile* OatFile::OatDexFile::OpenDexFile() const { dex_file_location_checksum_); } -const OatFile::OatClass* OatFile::OatDexFile::GetOatClass(uint32_t class_def_index) const { +const OatFile::OatClass* OatFile::OatDexFile::GetOatClass(uint16_t class_def_index) const { uint32_t oat_class_offset = oat_class_offsets_pointer_[class_def_index]; const byte* oat_class_pointer = oat_file_->Begin() + oat_class_offset; diff --git a/runtime/oat_file.h b/runtime/oat_file.h index 325ebb29148..bbd2615b668 100644 --- a/runtime/oat_file.h +++ b/runtime/oat_file.h @@ -183,7 +183,7 @@ class OatFile { } // Returns the OatClass for the class specified by the given DexFile class_def_index. - const OatClass* GetOatClass(uint32_t class_def_index) const; + const OatClass* GetOatClass(uint16_t class_def_index) const; ~OatDexFile(); diff --git a/runtime/object_utils.h b/runtime/object_utils.h index 29102437a2e..6ee3016179f 100644 --- a/runtime/object_utils.h +++ b/runtime/object_utils.h @@ -68,8 +68,7 @@ class ClassHelper { public: ClassHelper(const mirror::Class* c = NULL, ClassLinker* l = NULL) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) - : class_def_(NULL), - class_linker_(l), + : class_linker_(l), dex_cache_(NULL), dex_file_(NULL), interface_type_list_(NULL), @@ -92,7 +91,6 @@ class ClassHelper { } klass_ = new_c; interface_type_list_ = NULL; - class_def_ = NULL; } // The returned const char* is only guaranteed to be valid for the lifetime of the ClassHelper. @@ -108,7 +106,7 @@ class ClassHelper { return descriptor_.c_str(); } else { const DexFile& dex_file = GetDexFile(); - const DexFile::TypeId& type_id = dex_file.GetTypeId(klass_->GetDexTypeIndex()); + const DexFile::TypeId& type_id = dex_file.GetTypeId(GetClassDef()->class_idx_); return dex_file.GetTypeDescriptor(type_id); } } @@ -124,14 +122,13 @@ class ClassHelper { return descriptor_.c_str(); } - const DexFile::ClassDef* GetClassDef() - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - const DexFile::ClassDef* result = class_def_; - if (result == NULL) { - result = GetDexFile().FindClassDef(GetDescriptor()); - class_def_ = result; + const DexFile::ClassDef* GetClassDef() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + DCHECK(klass_ != nullptr); + uint16_t class_def_idx = klass_->GetDexClassDefIndex(); + if (class_def_idx == DexFile::kDexNoIndex16) { + return nullptr; } - return result; + return &GetDexFile().GetClassDef(class_def_idx); } uint32_t NumDirectInterfaces() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { @@ -187,7 +184,7 @@ class ClassHelper { const char* GetSourceFile() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { std::string descriptor(GetDescriptor()); const DexFile& dex_file = GetDexFile(); - const DexFile::ClassDef* dex_class_def = dex_file.FindClassDef(descriptor); + const DexFile::ClassDef* dex_class_def = GetClassDef(); CHECK(dex_class_def != NULL); return dex_file.GetSourceFile(*dex_class_def); } @@ -242,7 +239,6 @@ class ClassHelper { return result; } - const DexFile::ClassDef* class_def_; ClassLinker* class_linker_; mirror::DexCache* dex_cache_; const DexFile* dex_file_; @@ -327,12 +323,15 @@ class FieldHelper { // If you need it longer, copy it into a std::string. const char* GetDeclaringClassDescriptor() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - uint16_t type_idx = field_->GetDeclaringClass()->GetDexTypeIndex(); - if (type_idx != DexFile::kDexNoIndex16) { + uint32_t field_index = field_->GetDexFieldIndex(); + if (!field_->GetDeclaringClass()->IsProxyClass()) { const DexFile& dex_file = GetDexFile(); - return dex_file.GetTypeDescriptor(dex_file.GetTypeId(type_idx)); + const DexFile::FieldId& field_id = dex_file.GetFieldId(field_index); + return dex_file.GetFieldDeclaringClassDescriptor(field_id); } else { - // Most likely a proxy class. + DCHECK(field_->IsStatic()); + DCHECK_LT(field_index, 2U); + // 0 == Class[] interfaces; 1 == Class[][] throws; ClassHelper kh(field_->GetDeclaringClass()); declaring_class_descriptor_ = kh.GetDescriptor(); return declaring_class_descriptor_.c_str(); @@ -418,7 +417,7 @@ class MethodHelper { const char* GetName() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { const DexFile& dex_file = GetDexFile(); uint32_t dex_method_idx = method_->GetDexMethodIndex(); - if (dex_method_idx != DexFile::kDexNoIndex16) { + if (dex_method_idx != DexFile::kDexNoIndex) { return dex_file.GetMethodName(dex_file.GetMethodId(dex_method_idx)); } else { Runtime* runtime = Runtime::Current(); @@ -464,21 +463,19 @@ class MethodHelper { const std::string GetSignature() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { const DexFile& dex_file = GetDexFile(); uint32_t dex_method_idx = method_->GetDexMethodIndex(); - if (dex_method_idx != DexFile::kDexNoIndex16) { + if (dex_method_idx != DexFile::kDexNoIndex) { return dex_file.GetMethodSignature(dex_file.GetMethodId(dex_method_idx)); } else { return ""; } } - const DexFile::ProtoId& GetPrototype() - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + const DexFile::ProtoId& GetPrototype() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { const DexFile& dex_file = GetDexFile(); return dex_file.GetMethodPrototype(dex_file.GetMethodId(method_->GetDexMethodIndex())); } - const DexFile::TypeList* GetParameterTypeList() - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + const DexFile::TypeList* GetParameterTypeList() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { const DexFile::ProtoId& proto = GetPrototype(); return GetDexFile().GetProtoParameters(proto); } @@ -491,8 +488,7 @@ class MethodHelper { return GetClassFromTypeIdx(return_type_idx); } - const char* GetReturnTypeDescriptor() - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + const char* GetReturnTypeDescriptor() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { const DexFile& dex_file = GetDexFile(); const DexFile::MethodId& method_id = dex_file.GetMethodId(method_->GetDexMethodIndex()); const DexFile::ProtoId& proto_id = dex_file.GetMethodPrototype(method_id); @@ -500,8 +496,7 @@ class MethodHelper { return dex_file.GetTypeDescriptor(dex_file.GetTypeId(return_type_idx)); } - int32_t GetLineNumFromDexPC(uint32_t dex_pc) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + int32_t GetLineNumFromDexPC(uint32_t dex_pc) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { if (dex_pc == DexFile::kDexNoIndex) { return method_->IsNative() ? -2 : -1; } else { @@ -510,35 +505,29 @@ class MethodHelper { } } - const char* GetDeclaringClassDescriptor() - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - mirror::Class* klass = method_->GetDeclaringClass(); - DCHECK(!klass->IsProxyClass()); - uint16_t type_idx = klass->GetDexTypeIndex(); + const char* GetDeclaringClassDescriptor() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { const DexFile& dex_file = GetDexFile(); - return dex_file.GetTypeDescriptor(dex_file.GetTypeId(type_idx)); + uint32_t dex_method_idx = method_->GetDexMethodIndex(); + if (dex_method_idx != DexFile::kDexNoIndex) { + return dex_file.GetMethodDeclaringClassDescriptor(dex_file.GetMethodId(dex_method_idx)); + } else { + return ""; + } } - const char* GetDeclaringClassSourceFile() - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - const char* descriptor = GetDeclaringClassDescriptor(); - const DexFile& dex_file = GetDexFile(); - const DexFile::ClassDef* dex_class_def = dex_file.FindClassDef(descriptor); - CHECK(dex_class_def != NULL); - return dex_file.GetSourceFile(*dex_class_def); + const char* GetDeclaringClassSourceFile() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + return ClassHelper(method_->GetDeclaringClass()).GetSourceFile(); } - uint32_t GetClassDefIndex() - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - const char* descriptor = GetDeclaringClassDescriptor(); - const DexFile& dex_file = GetDexFile(); - uint32_t index; - CHECK(dex_file.FindClassDefIndex(descriptor, index)); - return index; + uint16_t GetClassDefIndex() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + return method_->GetDeclaringClass()->GetDexClassDefIndex(); } - mirror::ClassLoader* GetClassLoader() - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + const DexFile::ClassDef& GetClassDef() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + return GetDexFile().GetClassDef(GetClassDefIndex()); + } + + mirror::ClassLoader* GetClassLoader() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { return method_->GetDeclaringClass()->GetClassLoader(); } diff --git a/runtime/runtime.cc b/runtime/runtime.cc index c37b7830c85..05f4566d85e 100644 --- a/runtime/runtime.cc +++ b/runtime/runtime.cc @@ -1007,6 +1007,7 @@ void Runtime::RegisterRuntimeNativeMethods(JNIEnv* env) { REGISTER(register_dalvik_system_VMStack); REGISTER(register_dalvik_system_Zygote); REGISTER(register_java_lang_Class); + REGISTER(register_java_lang_DexCache); REGISTER(register_java_lang_Object); REGISTER(register_java_lang_Runtime); REGISTER(register_java_lang_String); @@ -1175,7 +1176,7 @@ mirror::ArtMethod* Runtime::CreateResolutionMethod() { method(self, down_cast(method_class->AllocObject(self))); method->SetDeclaringClass(method_class); // TODO: use a special method for resolution method saves - method->SetDexMethodIndex(DexFile::kDexNoIndex16); + method->SetDexMethodIndex(DexFile::kDexNoIndex); // When compiling, the code pointer will get set later when the image is loaded. Runtime* r = Runtime::Current(); ClassLinker* cl = r->GetClassLinker(); @@ -1191,7 +1192,7 @@ mirror::ArtMethod* Runtime::CreateCalleeSaveMethod(InstructionSet instruction_se method(self, down_cast(method_class->AllocObject(self))); method->SetDeclaringClass(method_class); // TODO: use a special method for callee saves - method->SetDexMethodIndex(DexFile::kDexNoIndex16); + method->SetDexMethodIndex(DexFile::kDexNoIndex); method->SetEntryPointFromCompiledCode(NULL); if ((instruction_set == kThumb2) || (instruction_set == kArm)) { uint32_t ref_spills = (1 << art::arm::R5) | (1 << art::arm::R6) | (1 << art::arm::R7) | diff --git a/runtime/thread.cc b/runtime/thread.cc index d7d4b1fa974..68370508d38 100644 --- a/runtime/thread.cc +++ b/runtime/thread.cc @@ -1816,7 +1816,7 @@ class CatchBlockStackVisitor : public StackVisitor { uint32_t new_dex_pc = dex_pc + inst->SizeInCodeUnits(); ShadowFrame* new_frame = ShadowFrame::Create(num_regs, NULL, m, new_dex_pc); verifier::MethodVerifier verifier(&mh.GetDexFile(), mh.GetDexCache(), mh.GetClassLoader(), - mh.GetClassDefIndex(), code_item, + &mh.GetClassDef(), code_item, m->GetDexMethodIndex(), m, m->GetAccessFlags(), false, true); verifier.Verify(); std::vector kinds = verifier.DescribeVRegs(dex_pc); diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc index fa00c610172..9811926b168 100644 --- a/runtime/verifier/method_verifier.cc +++ b/runtime/verifier/method_verifier.cc @@ -74,50 +74,51 @@ void PcToRegisterLineTable::Init(RegisterTrackingMode mode, InstructionFlags* fl } MethodVerifier::FailureKind MethodVerifier::VerifyClass(const mirror::Class* klass, - std::string& error, - bool allow_soft_failures) { + bool allow_soft_failures, + std::string* error) { if (klass->IsVerified()) { return kNoFailure; } mirror::Class* super = klass->GetSuperClass(); if (super == NULL && StringPiece(ClassHelper(klass).GetDescriptor()) != "Ljava/lang/Object;") { - error = "Verifier rejected class "; - error += PrettyDescriptor(klass); - error += " that has no super class"; + *error = "Verifier rejected class "; + *error += PrettyDescriptor(klass); + *error += " that has no super class"; return kHardFailure; } if (super != NULL && super->IsFinal()) { - error = "Verifier rejected class "; - error += PrettyDescriptor(klass); - error += " that attempts to sub-class final class "; - error += PrettyDescriptor(super); + *error = "Verifier rejected class "; + *error += PrettyDescriptor(klass); + *error += " that attempts to sub-class final class "; + *error += PrettyDescriptor(super); return kHardFailure; } ClassHelper kh(klass); const DexFile& dex_file = kh.GetDexFile(); - uint32_t class_def_idx; - if (!dex_file.FindClassDefIndex(kh.GetDescriptor(), class_def_idx)) { - error = "Verifier rejected class "; - error += PrettyDescriptor(klass); - error += " that isn't present in dex file "; - error += dex_file.GetLocation(); + const DexFile::ClassDef* class_def = kh.GetClassDef(); + if (class_def == NULL) { + *error = "Verifier rejected class "; + *error += PrettyDescriptor(klass); + *error += " that isn't present in dex file "; + *error += dex_file.GetLocation(); return kHardFailure; } return VerifyClass(&dex_file, kh.GetDexCache(), klass->GetClassLoader(), - class_def_idx, error, - allow_soft_failures); + class_def, + allow_soft_failures, + error); } MethodVerifier::FailureKind MethodVerifier::VerifyClass(const DexFile* dex_file, mirror::DexCache* dex_cache, mirror::ClassLoader* class_loader, - uint32_t class_def_idx, - std::string& error, - bool allow_soft_failures) { - const DexFile::ClassDef& class_def = dex_file->GetClassDef(class_def_idx); - const byte* class_data = dex_file->GetClassData(class_def); + const DexFile::ClassDef* class_def, + bool allow_soft_failures, + std::string* error) { + DCHECK(class_def != nullptr); + const byte* class_data = dex_file->GetClassData(*class_def); if (class_data == NULL) { // empty class, probably a marker interface return kNoFailure; @@ -139,7 +140,7 @@ MethodVerifier::FailureKind MethodVerifier::VerifyClass(const DexFile* dex_file, continue; } previous_direct_method_idx = method_idx; - InvokeType type = it.GetMethodInvokeType(class_def); + InvokeType type = it.GetMethodInvokeType(*class_def); mirror::ArtMethod* method = linker->ResolveMethod(*dex_file, method_idx, dex_cache, class_loader, NULL, type); if (method == NULL) { @@ -151,7 +152,7 @@ MethodVerifier::FailureKind MethodVerifier::VerifyClass(const DexFile* dex_file, dex_file, dex_cache, class_loader, - class_def_idx, + class_def, it.GetMethodCodeItem(), method, it.GetMemberAccessFlags(), @@ -160,12 +161,12 @@ MethodVerifier::FailureKind MethodVerifier::VerifyClass(const DexFile* dex_file, if (result == kHardFailure) { hard_fail = true; if (error_count > 0) { - error += "\n"; + *error += "\n"; } - error = "Verifier rejected class "; - error += PrettyDescriptor(dex_file->GetClassDescriptor(class_def)); - error += " due to bad method "; - error += PrettyMethod(method_idx, *dex_file); + *error = "Verifier rejected class "; + *error += PrettyDescriptor(dex_file->GetClassDescriptor(*class_def)); + *error += " due to bad method "; + *error += PrettyMethod(method_idx, *dex_file); } ++error_count; } @@ -181,7 +182,7 @@ MethodVerifier::FailureKind MethodVerifier::VerifyClass(const DexFile* dex_file, continue; } previous_virtual_method_idx = method_idx; - InvokeType type = it.GetMethodInvokeType(class_def); + InvokeType type = it.GetMethodInvokeType(*class_def); mirror::ArtMethod* method = linker->ResolveMethod(*dex_file, method_idx, dex_cache, class_loader, NULL, type); if (method == NULL) { @@ -193,7 +194,7 @@ MethodVerifier::FailureKind MethodVerifier::VerifyClass(const DexFile* dex_file, dex_file, dex_cache, class_loader, - class_def_idx, + class_def, it.GetMethodCodeItem(), method, it.GetMemberAccessFlags(), @@ -202,12 +203,12 @@ MethodVerifier::FailureKind MethodVerifier::VerifyClass(const DexFile* dex_file, if (result == kHardFailure) { hard_fail = true; if (error_count > 0) { - error += "\n"; + *error += "\n"; } - error = "Verifier rejected class "; - error += PrettyDescriptor(dex_file->GetClassDescriptor(class_def)); - error += " due to bad method "; - error += PrettyMethod(method_idx, *dex_file); + *error = "Verifier rejected class "; + *error += PrettyDescriptor(dex_file->GetClassDescriptor(*class_def)); + *error += " due to bad method "; + *error += PrettyMethod(method_idx, *dex_file); } ++error_count; } @@ -224,7 +225,7 @@ MethodVerifier::FailureKind MethodVerifier::VerifyMethod(uint32_t method_idx, const DexFile* dex_file, mirror::DexCache* dex_cache, mirror::ClassLoader* class_loader, - uint32_t class_def_idx, + const DexFile::ClassDef* class_def, const DexFile::CodeItem* code_item, mirror::ArtMethod* method, uint32_t method_access_flags, @@ -232,7 +233,7 @@ MethodVerifier::FailureKind MethodVerifier::VerifyMethod(uint32_t method_idx, MethodVerifier::FailureKind result = kNoFailure; uint64_t start_ns = NanoTime(); - MethodVerifier verifier_(dex_file, dex_cache, class_loader, class_def_idx, code_item, method_idx, + MethodVerifier verifier_(dex_file, dex_cache, class_loader, class_def, code_item, method_idx, method, method_access_flags, true, allow_soft_failures); if (verifier_.Verify()) { // Verification completed, however failures may be pending that didn't cause the verification @@ -267,11 +268,12 @@ MethodVerifier::FailureKind MethodVerifier::VerifyMethod(uint32_t method_idx, void MethodVerifier::VerifyMethodAndDump(std::ostream& os, uint32_t dex_method_idx, const DexFile* dex_file, mirror::DexCache* dex_cache, - mirror::ClassLoader* class_loader, uint32_t class_def_idx, + mirror::ClassLoader* class_loader, + const DexFile::ClassDef* class_def, const DexFile::CodeItem* code_item, mirror::ArtMethod* method, uint32_t method_access_flags) { - MethodVerifier verifier(dex_file, dex_cache, class_loader, class_def_idx, code_item, + MethodVerifier verifier(dex_file, dex_cache, class_loader, class_def, code_item, dex_method_idx, method, method_access_flags, true, true); verifier.Verify(); verifier.DumpFailures(os); @@ -280,7 +282,8 @@ void MethodVerifier::VerifyMethodAndDump(std::ostream& os, uint32_t dex_method_i } MethodVerifier::MethodVerifier(const DexFile* dex_file, mirror::DexCache* dex_cache, - mirror::ClassLoader* class_loader, uint32_t class_def_idx, + mirror::ClassLoader* class_loader, + const DexFile::ClassDef* class_def, const DexFile::CodeItem* code_item, uint32_t dex_method_idx, mirror::ArtMethod* method, uint32_t method_access_flags, bool can_load_classes, @@ -293,7 +296,7 @@ MethodVerifier::MethodVerifier(const DexFile* dex_file, mirror::DexCache* dex_ca dex_file_(dex_file), dex_cache_(dex_cache), class_loader_(class_loader), - class_def_idx_(class_def_idx), + class_def_(class_def), code_item_(code_item), declaring_class_(NULL), interesting_dex_pc_(-1), @@ -306,13 +309,14 @@ MethodVerifier::MethodVerifier(const DexFile* dex_file, mirror::DexCache* dex_ca allow_soft_failures_(allow_soft_failures), has_check_casts_(false), has_virtual_or_interface_invokes_(false) { + DCHECK(class_def != NULL); } void MethodVerifier::FindLocksAtDexPc(mirror::ArtMethod* m, uint32_t dex_pc, std::vector& monitor_enter_dex_pcs) { MethodHelper mh(m); MethodVerifier verifier(&mh.GetDexFile(), mh.GetDexCache(), mh.GetClassLoader(), - mh.GetClassDefIndex(), mh.GetCodeItem(), m->GetDexMethodIndex(), + &mh.GetClassDef(), mh.GetCodeItem(), m->GetDexMethodIndex(), m, m->GetAccessFlags(), false, true); verifier.interesting_dex_pc_ = dex_pc; verifier.monitor_enter_dex_pcs_ = &monitor_enter_dex_pcs; @@ -334,7 +338,7 @@ mirror::ArtField* MethodVerifier::FindAccessedFieldAtDexPc(mirror::ArtMethod* m, uint32_t dex_pc) { MethodHelper mh(m); MethodVerifier verifier(&mh.GetDexFile(), mh.GetDexCache(), mh.GetClassLoader(), - mh.GetClassDefIndex(), mh.GetCodeItem(), m->GetDexMethodIndex(), + &mh.GetClassDef(), mh.GetCodeItem(), m->GetDexMethodIndex(), m, m->GetAccessFlags(), false, true); return verifier.FindAccessedFieldAtDexPc(dex_pc); } @@ -362,7 +366,7 @@ mirror::ArtMethod* MethodVerifier::FindInvokedMethodAtDexPc(mirror::ArtMethod* m uint32_t dex_pc) { MethodHelper mh(m); MethodVerifier verifier(&mh.GetDexFile(), mh.GetDexCache(), mh.GetClassLoader(), - mh.GetClassDefIndex(), mh.GetCodeItem(), m->GetDexMethodIndex(), + &mh.GetClassDef(), mh.GetCodeItem(), m->GetDexMethodIndex(), m, m->GetAccessFlags(), false, true); return verifier.FindInvokedMethodAtDexPc(dex_pc); } @@ -448,7 +452,7 @@ std::ostream& MethodVerifier::Fail(VerifyError error) { // marked as rejected to prevent it from being compiled. case VERIFY_ERROR_BAD_CLASS_HARD: { if (Runtime::Current()->IsCompiler()) { - ClassReference ref(dex_file_, class_def_idx_); + ClassReference ref(dex_file_, dex_file_->GetIndexForClassDef(*class_def_)); AddRejectedClass(ref); } have_pending_hard_failure_ = true; diff --git a/runtime/verifier/method_verifier.h b/runtime/verifier/method_verifier.h index 70442fbc046..073a2f76be1 100644 --- a/runtime/verifier/method_verifier.h +++ b/runtime/verifier/method_verifier.h @@ -145,17 +145,19 @@ class MethodVerifier { }; /* Verify a class. Returns "kNoFailure" on success. */ - static FailureKind VerifyClass(const mirror::Class* klass, std::string& error, - bool allow_soft_failures) + static FailureKind VerifyClass(const mirror::Class* klass, bool allow_soft_failures, + std::string* error) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); static FailureKind VerifyClass(const DexFile* dex_file, mirror::DexCache* dex_cache, - mirror::ClassLoader* class_loader, uint32_t class_def_idx, - std::string& error, bool allow_soft_failures) + mirror::ClassLoader* class_loader, + const DexFile::ClassDef* class_def, + bool allow_soft_failures, std::string* error) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); static void VerifyMethodAndDump(std::ostream& os, uint32_t method_idx, const DexFile* dex_file, mirror::DexCache* dex_cache, mirror::ClassLoader* class_loader, - uint32_t class_def_idx, const DexFile::CodeItem* code_item, + const DexFile::ClassDef* class_def, + const DexFile::CodeItem* code_item, mirror::ArtMethod* method, uint32_t method_access_flags) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); @@ -222,7 +224,7 @@ class MethodVerifier { } MethodVerifier(const DexFile* dex_file, mirror::DexCache* dex_cache, - mirror::ClassLoader* class_loader, uint32_t class_def_idx, + mirror::ClassLoader* class_loader, const DexFile::ClassDef* class_def, const DexFile::CodeItem* code_item, uint32_t method_idx, mirror::ArtMethod* method, uint32_t access_flags, bool can_load_classes, bool allow_soft_failures) @@ -262,7 +264,8 @@ class MethodVerifier { */ static FailureKind VerifyMethod(uint32_t method_idx, const DexFile* dex_file, mirror::DexCache* dex_cache, - mirror::ClassLoader* class_loader, uint32_t class_def_idx, + mirror::ClassLoader* class_loader, + const DexFile::ClassDef* class_def_idx, const DexFile::CodeItem* code_item, mirror::ArtMethod* method, uint32_t method_access_flags, bool allow_soft_failures) @@ -690,7 +693,7 @@ class MethodVerifier { mirror::DexCache* dex_cache_ GUARDED_BY(Locks::mutator_lock_); // The class loader for the declaring class of the method. mirror::ClassLoader* class_loader_ GUARDED_BY(Locks::mutator_lock_); - const uint32_t class_def_idx_; // The class def index of the declaring class of the method. + const DexFile::ClassDef* const class_def_; // The class def of the declaring class of the method. const DexFile::CodeItem* const code_item_; // The code item containing the code for the method. const RegType* declaring_class_; // Lazily computed reg type of the method's declaring class. // Instruction widths and flags, one entry per code unit. diff --git a/runtime/verifier/method_verifier_test.cc b/runtime/verifier/method_verifier_test.cc index 611b7c06ebf..a56abba3cf3 100644 --- a/runtime/verifier/method_verifier_test.cc +++ b/runtime/verifier/method_verifier_test.cc @@ -34,7 +34,8 @@ class MethodVerifierTest : public CommonTest { // Verify the class std::string error_msg; - ASSERT_TRUE(MethodVerifier::VerifyClass(klass, error_msg, true) == MethodVerifier::kNoFailure) << error_msg; + ASSERT_TRUE(MethodVerifier::VerifyClass(klass, true, &error_msg) == MethodVerifier::kNoFailure) + << error_msg; } void VerifyDexFile(const DexFile* dex) diff --git a/test/100-reflect2/expected.txt b/test/100-reflect2/expected.txt index 3d87ebc5596..967f167f37c 100644 --- a/test/100-reflect2/expected.txt +++ b/test/100-reflect2/expected.txt @@ -35,7 +35,7 @@ z (class java.lang.Character) 62 (class java.lang.Long) 14 (class java.lang.Short) [public java.lang.String(), java.lang.String(int,int,char[]), public java.lang.String(java.lang.String), public java.lang.String(java.lang.StringBuffer), public java.lang.String(java.lang.StringBuilder), public java.lang.String(byte[]), public java.lang.String(byte[],int), public java.lang.String(byte[],int,int), public java.lang.String(byte[],int,int,int), public java.lang.String(byte[],int,int,java.lang.String) throws java.io.UnsupportedEncodingException, public java.lang.String(byte[],int,int,java.nio.charset.Charset), public java.lang.String(byte[],java.lang.String) throws java.io.UnsupportedEncodingException, public java.lang.String(byte[],java.nio.charset.Charset), public java.lang.String(char[]), public java.lang.String(char[],int,int), public java.lang.String(int[],int,int)] -[private final char[] java.lang.String.value, private final int java.lang.String.count, private int java.lang.String.hashCode, private final int java.lang.String.offset, private static final char[] java.lang.String.ASCII, public static final java.util.Comparator java.lang.String.CASE_INSENSITIVE_ORDER, private static final char java.lang.String.REPLACEMENT_CHAR, private static final long java.lang.String.serialVersionUID] +[private final char[] java.lang.String.value, private final int java.lang.String.count, private int java.lang.String.hashCode, private final int java.lang.String.offset, private static final char[] java.lang.String.ASCII, public static final java.util.Comparator java.lang.String.CASE_INSENSITIVE_ORDER, private static final long java.lang.String.serialVersionUID, private static final char java.lang.String.REPLACEMENT_CHAR] [void java.lang.String._getChars(int,int,char[],int), public char java.lang.String.charAt(int), public int java.lang.String.codePointAt(int), public int java.lang.String.codePointBefore(int), public int java.lang.String.codePointCount(int,int), public volatile int java.lang.String.compareTo(java.lang.Object), public native int java.lang.String.compareTo(java.lang.String), public int java.lang.String.compareToIgnoreCase(java.lang.String), public java.lang.String java.lang.String.concat(java.lang.String), public boolean java.lang.String.contains(java.lang.CharSequence), public boolean java.lang.String.contentEquals(java.lang.CharSequence), public boolean java.lang.String.contentEquals(java.lang.StringBuffer), public boolean java.lang.String.endsWith(java.lang.String), public boolean java.lang.String.equals(java.lang.Object), public boolean java.lang.String.equalsIgnoreCase(java.lang.String), public void java.lang.String.getBytes(int,int,byte[],int), public [B java.lang.String.getBytes(), public [B java.lang.String.getBytes(java.lang.String) throws java.io.UnsupportedEncodingException, public [B java.lang.String.getBytes(java.nio.charset.Charset), public void java.lang.String.getChars(int,int,char[],int), public int java.lang.String.hashCode(), public int java.lang.String.indexOf(int), public int java.lang.String.indexOf(int,int), public int java.lang.String.indexOf(java.lang.String), public int java.lang.String.indexOf(java.lang.String,int), public native java.lang.String java.lang.String.intern(), public boolean java.lang.String.isEmpty(), public int java.lang.String.lastIndexOf(int), public int java.lang.String.lastIndexOf(int,int), public int java.lang.String.lastIndexOf(java.lang.String), public int java.lang.String.lastIndexOf(java.lang.String,int), public int java.lang.String.length(), public boolean java.lang.String.matches(java.lang.String), public int java.lang.String.offsetByCodePoints(int,int), public boolean java.lang.String.regionMatches(int,java.lang.String,int,int), public boolean java.lang.String.regionMatches(boolean,int,java.lang.String,int,int), public java.lang.String java.lang.String.replace(char,char), public java.lang.String java.lang.String.replace(java.lang.CharSequence,java.lang.CharSequence), public java.lang.String java.lang.String.replaceAll(java.lang.String,java.lang.String), public java.lang.String java.lang.String.replaceFirst(java.lang.String,java.lang.String), public [Ljava.lang.String; java.lang.String.split(java.lang.String), public [Ljava.lang.String; java.lang.String.split(java.lang.String,int), public boolean java.lang.String.startsWith(java.lang.String), public boolean java.lang.String.startsWith(java.lang.String,int), public java.lang.CharSequence java.lang.String.subSequence(int,int), public java.lang.String java.lang.String.substring(int), public java.lang.String java.lang.String.substring(int,int), public [C java.lang.String.toCharArray(), public java.lang.String java.lang.String.toLowerCase(), public java.lang.String java.lang.String.toLowerCase(java.util.Locale), public java.lang.String java.lang.String.toString(), public java.lang.String java.lang.String.toUpperCase(), public java.lang.String java.lang.String.toUpperCase(java.util.Locale), public java.lang.String java.lang.String.trim(), public static java.lang.String java.lang.String.copyValueOf(char[]), public static java.lang.String java.lang.String.copyValueOf(char[],int,int), private java.lang.StringIndexOutOfBoundsException java.lang.String.failedBoundsCheck(int,int,int), private native int java.lang.String.fastIndexOf(int,int), private char java.lang.String.foldCase(char), public static transient java.lang.String java.lang.String.format(java.lang.String,java.lang.Object[]), public static transient java.lang.String java.lang.String.format(java.util.Locale,java.lang.String,java.lang.Object[]), private java.lang.StringIndexOutOfBoundsException java.lang.String.indexAndLength(int), private static int java.lang.String.indexOf(java.lang.String,java.lang.String,int,int,char), private int java.lang.String.indexOfSupplementary(int,int), private int java.lang.String.lastIndexOfSupplementary(int,int), private java.lang.StringIndexOutOfBoundsException java.lang.String.startEndAndLength(int,int), public static java.lang.String java.lang.String.valueOf(char), public static java.lang.String java.lang.String.valueOf(double), public static java.lang.String java.lang.String.valueOf(float), public static java.lang.String java.lang.String.valueOf(int), public static java.lang.String java.lang.String.valueOf(long), public static java.lang.String java.lang.String.valueOf(java.lang.Object), public static java.lang.String java.lang.String.valueOf(boolean), public static java.lang.String java.lang.String.valueOf(char[]), public static java.lang.String java.lang.String.valueOf(char[],int,int)] [] [interface java.io.Serializable, interface java.lang.Comparable, interface java.lang.CharSequence] From e47637cc7c96e654d2c340e6006c232c8078673b Mon Sep 17 00:00:00 2001 From: Jeff Hao Date: Thu, 19 Sep 2013 15:13:16 -0700 Subject: [PATCH 0044/2402] 4-byte align 64-bit values in Get/Set Double/Long for GCC 4.8. Bug: 10837416 Change-Id: Ibb562407d81c2085666ae8824e7570f22e56eaa7 --- runtime/stack.h | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/runtime/stack.h b/runtime/stack.h index 8ecf8f05714..7c87f4555ca 100644 --- a/runtime/stack.h +++ b/runtime/stack.h @@ -138,13 +138,17 @@ class ShadowFrame { int64_t GetVRegLong(size_t i) const { DCHECK_LT(i, NumberOfVRegs()); const uint32_t* vreg = &vregs_[i]; - return *reinterpret_cast(vreg); + // Alignment attribute required for GCC 4.8 + typedef const int64_t unaligned_int64 __attribute__ ((aligned (4))); + return *reinterpret_cast(vreg); } double GetVRegDouble(size_t i) const { DCHECK_LT(i, NumberOfVRegs()); const uint32_t* vreg = &vregs_[i]; - return *reinterpret_cast(vreg); + // Alignment attribute required for GCC 4.8 + typedef const double unaligned_double __attribute__ ((aligned (4))); + return *reinterpret_cast(vreg); } mirror::Object* GetVRegReference(size_t i) const { @@ -177,13 +181,17 @@ class ShadowFrame { void SetVRegLong(size_t i, int64_t val) { DCHECK_LT(i, NumberOfVRegs()); uint32_t* vreg = &vregs_[i]; - *reinterpret_cast(vreg) = val; + // Alignment attribute required for GCC 4.8 + typedef int64_t unaligned_int64 __attribute__ ((aligned (4))); + *reinterpret_cast(vreg) = val; } void SetVRegDouble(size_t i, double val) { DCHECK_LT(i, NumberOfVRegs()); uint32_t* vreg = &vregs_[i]; - *reinterpret_cast(vreg) = val; + // Alignment attribute required for GCC 4.8 + typedef double unaligned_double __attribute__ ((aligned (4))); + *reinterpret_cast(vreg) = val; } void SetVRegReference(size_t i, mirror::Object* val) { From 450dcb56ecbf6f729401e753f0a27e4170177ddd Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Fri, 20 Sep 2013 17:36:02 -0700 Subject: [PATCH 0045/2402] Improve float to integral conversion. Change-Id: I1597083cb2c04084ce825fe2e3c753fde8309cd8 --- runtime/entrypoints/entrypoint_utils.h | 21 +++++- runtime/entrypoints/math_entrypoints.cc | 50 ++------------ .../interpreter_goto_table_impl.cc | 44 ++---------- .../interpreter/interpreter_switch_impl.cc | 68 ++++++------------- 4 files changed, 50 insertions(+), 133 deletions(-) diff --git a/runtime/entrypoints/entrypoint_utils.h b/runtime/entrypoints/entrypoint_utils.h index 8b58cb332c9..fff7b71e4bf 100644 --- a/runtime/entrypoints/entrypoint_utils.h +++ b/runtime/entrypoints/entrypoint_utils.h @@ -16,7 +16,8 @@ #ifndef ART_RUNTIME_ENTRYPOINTS_ENTRYPOINT_UTILS_H_ #define ART_RUNTIME_ENTRYPOINTS_ENTRYPOINT_UTILS_H_ -#include "object_utils.h" + +#include "base/macros.h" #include "class_linker.h" #include "common_throws.h" #include "dex_file.h" @@ -27,6 +28,7 @@ #include "mirror/array.h" #include "mirror/class-inl.h" #include "mirror/throwable.h" +#include "object_utils.h" #include "thread.h" @@ -416,6 +418,23 @@ static inline void* GetJniDlsymLookupStub() { return reinterpret_cast(art_jni_dlsym_lookup_stub); } +template +static inline INT_TYPE art_float_to_integral(FLOAT_TYPE f) { + const INT_TYPE kMaxInt = static_cast(std::numeric_limits::max()); + const INT_TYPE kMinInt = static_cast(std::numeric_limits::min()); + const FLOAT_TYPE kMaxIntAsFloat = static_cast(kMaxInt); + const FLOAT_TYPE kMinIntAsFloat = static_cast(kMinInt); + if (LIKELY(f > kMinIntAsFloat)) { + if (LIKELY(f < kMaxIntAsFloat)) { + return static_cast(f); + } else { + return kMaxInt; + } + } else { + return (f != f) ? 0 : kMinInt; // f != f implies NaN + } +} + } // namespace art #endif // ART_RUNTIME_ENTRYPOINTS_ENTRYPOINT_UTILS_H_ diff --git a/runtime/entrypoints/math_entrypoints.cc b/runtime/entrypoints/math_entrypoints.cc index 31d13c8cd59..b839b6317d3 100644 --- a/runtime/entrypoints/math_entrypoints.cc +++ b/runtime/entrypoints/math_entrypoints.cc @@ -16,6 +16,8 @@ #include "math_entrypoints.h" +#include "entrypoint_utils.h" + namespace art { extern "C" double art_l2d(int64_t l) { @@ -31,59 +33,19 @@ extern "C" float art_l2f(int64_t l) { * target doesn't support this normally, use these. */ extern "C" int64_t art_d2l(double d) { - static const double kMaxLong = static_cast(static_cast(0x7fffffffffffffffULL)); - static const double kMinLong = static_cast(static_cast(0x8000000000000000ULL)); - if (d >= kMaxLong) { - return static_cast(0x7fffffffffffffffULL); - } else if (d <= kMinLong) { - return static_cast(0x8000000000000000ULL); - } else if (d != d) { // NaN case - return 0; - } else { - return static_cast(d); - } + return art_float_to_integral(d); } extern "C" int64_t art_f2l(float f) { - static const float kMaxLong = static_cast(static_cast(0x7fffffffffffffffULL)); - static const float kMinLong = static_cast(static_cast(0x8000000000000000ULL)); - if (f >= kMaxLong) { - return static_cast(0x7fffffffffffffffULL); - } else if (f <= kMinLong) { - return static_cast(0x8000000000000000ULL); - } else if (f != f) { // NaN case - return 0; - } else { - return static_cast(f); - } + return art_float_to_integral(f); } extern "C" int32_t art_d2i(double d) { - static const double kMaxInt = static_cast(static_cast(0x7fffffffUL)); - static const double kMinInt = static_cast(static_cast(0x80000000UL)); - if (d >= kMaxInt) { - return static_cast(0x7fffffffUL); - } else if (d <= kMinInt) { - return static_cast(0x80000000UL); - } else if (d != d) { // NaN case - return 0; - } else { - return static_cast(d); - } + return art_float_to_integral(d); } extern "C" int32_t art_f2i(float f) { - static const float kMaxInt = static_cast(static_cast(0x7fffffffUL)); - static const float kMinInt = static_cast(static_cast(0x80000000UL)); - if (f >= kMaxInt) { - return static_cast(0x7fffffffUL); - } else if (f <= kMinInt) { - return static_cast(0x80000000UL); - } else if (f != f) { // NaN case - return 0; - } else { - return static_cast(f); - } + return art_float_to_integral(f); } } // namespace art diff --git a/runtime/interpreter/interpreter_goto_table_impl.cc b/runtime/interpreter/interpreter_goto_table_impl.cc index b55c2c242da..d70b80eb937 100644 --- a/runtime/interpreter/interpreter_goto_table_impl.cc +++ b/runtime/interpreter/interpreter_goto_table_impl.cc @@ -1460,16 +1460,7 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* HANDLE_INSTRUCTION_START(FLOAT_TO_INT) { float val = shadow_frame.GetVRegFloat(inst->VRegB_12x(inst_data)); - int32_t result; - if (val != val) { - result = 0; - } else if (val > static_cast(kMaxInt)) { - result = kMaxInt; - } else if (val < static_cast(kMinInt)) { - result = kMinInt; - } else { - result = val; - } + int32_t result = art_float_to_integral(val); shadow_frame.SetVReg(inst->VRegA_12x(inst_data), result); ADVANCE(1); } @@ -1477,16 +1468,7 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* HANDLE_INSTRUCTION_START(FLOAT_TO_LONG) { float val = shadow_frame.GetVRegFloat(inst->VRegB_12x(inst_data)); - int64_t result; - if (val != val) { - result = 0; - } else if (val > static_cast(kMaxLong)) { - result = kMaxLong; - } else if (val < static_cast(kMinLong)) { - result = kMinLong; - } else { - result = val; - } + int64_t result = art_float_to_integral(val); shadow_frame.SetVRegLong(inst->VRegA_12x(inst_data), result); ADVANCE(1); } @@ -1499,16 +1481,7 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* HANDLE_INSTRUCTION_START(DOUBLE_TO_INT) { double val = shadow_frame.GetVRegDouble(inst->VRegB_12x(inst_data)); - int32_t result; - if (val != val) { - result = 0; - } else if (val > static_cast(kMaxInt)) { - result = kMaxInt; - } else if (val < static_cast(kMinInt)) { - result = kMinInt; - } else { - result = val; - } + int32_t result = art_float_to_integral(val); shadow_frame.SetVReg(inst->VRegA_12x(inst_data), result); ADVANCE(1); } @@ -1516,16 +1489,7 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* HANDLE_INSTRUCTION_START(DOUBLE_TO_LONG) { double val = shadow_frame.GetVRegDouble(inst->VRegB_12x(inst_data)); - int64_t result; - if (val != val) { - result = 0; - } else if (val > static_cast(kMaxLong)) { - result = kMaxLong; - } else if (val < static_cast(kMinLong)) { - result = kMinLong; - } else { - result = val; - } + int64_t result = art_float_to_integral(val); shadow_frame.SetVRegLong(inst->VRegA_12x(inst_data), result); ADVANCE(1); } diff --git a/runtime/interpreter/interpreter_switch_impl.cc b/runtime/interpreter/interpreter_switch_impl.cc index b2e480f3a41..d49807cc5dd 100644 --- a/runtime/interpreter/interpreter_switch_impl.cc +++ b/runtime/interpreter/interpreter_switch_impl.cc @@ -1361,47 +1361,44 @@ static JValue ExecuteSwitchImpl(Thread* self, MethodHelper& mh, const DexFile::C break; case Instruction::INT_TO_LONG: PREAMBLE(); - shadow_frame.SetVRegLong(inst->VRegA_12x(inst_data), shadow_frame.GetVReg(inst->VRegB_12x(inst_data))); + 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))); + 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))); + 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))); + 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))); + 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))); + 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; - if (val != val) { - result = 0; - } else if (val > static_cast(kMaxInt)) { - result = kMaxInt; - } else if (val < static_cast(kMinInt)) { - result = kMinInt; - } else { - result = val; - } + int32_t result = art_float_to_integral(val); shadow_frame.SetVReg(inst->VRegA_12x(inst_data), result); inst = inst->Next_1xx(); break; @@ -1409,38 +1406,21 @@ static JValue ExecuteSwitchImpl(Thread* self, MethodHelper& mh, const DexFile::C case Instruction::FLOAT_TO_LONG: { PREAMBLE(); float val = shadow_frame.GetVRegFloat(inst->VRegB_12x(inst_data)); - int64_t result; - if (val != val) { - result = 0; - } else if (val > static_cast(kMaxLong)) { - result = kMaxLong; - } else if (val < static_cast(kMinLong)) { - result = kMinLong; - } else { - result = val; - } + int64_t result = art_float_to_integral(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))); + 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; - if (val != val) { - result = 0; - } else if (val > static_cast(kMaxInt)) { - result = kMaxInt; - } else if (val < static_cast(kMinInt)) { - result = kMinInt; - } else { - result = val; - } + int32_t result = art_float_to_integral(val); shadow_frame.SetVReg(inst->VRegA_12x(inst_data), result); inst = inst->Next_1xx(); break; @@ -1448,23 +1428,15 @@ static JValue ExecuteSwitchImpl(Thread* self, MethodHelper& mh, const DexFile::C case Instruction::DOUBLE_TO_LONG: { PREAMBLE(); double val = shadow_frame.GetVRegDouble(inst->VRegB_12x(inst_data)); - int64_t result; - if (val != val) { - result = 0; - } else if (val > static_cast(kMaxLong)) { - result = kMaxLong; - } else if (val < static_cast(kMinLong)) { - result = kMinLong; - } else { - result = val; - } + int64_t result = art_float_to_integral(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))); + 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: From a67249065e4c9b3cf4a7c081d95a78df28291ee9 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Mon, 23 Sep 2013 09:23:37 -0700 Subject: [PATCH 0046/2402] Move hot utf routines into -inl.h. Change-Id: I7050d8282a7e5870b2bf671d6867c57625e00ccc --- runtime/dex_file.cc | 2 +- runtime/dex_file_verifier.cc | 2 +- runtime/mirror/string.cc | 2 +- runtime/utf-inl.h | 61 ++++++++++++++++++++++++++++++++++++ runtime/utf.cc | 36 +-------------------- runtime/utf.h | 5 +-- runtime/utils.cc | 2 +- 7 files changed, 69 insertions(+), 41 deletions(-) create mode 100644 runtime/utf-inl.h diff --git a/runtime/dex_file.cc b/runtime/dex_file.cc index e81c456ccf8..098ab674f4f 100644 --- a/runtime/dex_file.cc +++ b/runtime/dex_file.cc @@ -38,7 +38,7 @@ #include "safe_map.h" #include "thread.h" #include "UniquePtr.h" -#include "utf.h" +#include "utf-inl.h" #include "utils.h" #include "well_known_classes.h" #include "zip_archive.h" diff --git a/runtime/dex_file_verifier.cc b/runtime/dex_file_verifier.cc index 5b076e07b77..7dc2b3172e0 100644 --- a/runtime/dex_file_verifier.cc +++ b/runtime/dex_file_verifier.cc @@ -21,7 +21,7 @@ #include "leb128.h" #include "safe_map.h" #include "UniquePtr.h" -#include "utf.h" +#include "utf-inl.h" #include "utils.h" #include "zip_archive.h" diff --git a/runtime/mirror/string.cc b/runtime/mirror/string.cc index b82683e5b36..9c93f17f8e3 100644 --- a/runtime/mirror/string.cc +++ b/runtime/mirror/string.cc @@ -24,7 +24,7 @@ #include "runtime.h" #include "sirt_ref.h" #include "thread.h" -#include "utf.h" +#include "utf-inl.h" namespace art { namespace mirror { diff --git a/runtime/utf-inl.h b/runtime/utf-inl.h new file mode 100644 index 00000000000..d8c258b5d91 --- /dev/null +++ b/runtime/utf-inl.h @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ART_RUNTIME_UTF_INL_H_ +#define ART_RUNTIME_UTF_INL_H_ + +#include "utf.h" + +namespace art { + +inline uint16_t GetUtf16FromUtf8(const char** utf8_data_in) { + uint8_t one = *(*utf8_data_in)++; + if ((one & 0x80) == 0) { + // one-byte encoding + return one; + } + // two- or three-byte encoding + uint8_t two = *(*utf8_data_in)++; + if ((one & 0x20) == 0) { + // two-byte encoding + return ((one & 0x1f) << 6) | (two & 0x3f); + } + // three-byte encoding + uint8_t three = *(*utf8_data_in)++; + return ((one & 0x0f) << 12) | ((two & 0x3f) << 6) | (three & 0x3f); +} + +inline int CompareModifiedUtf8ToModifiedUtf8AsUtf16CodePointValues(const char* utf8_1, + const char* utf8_2) { + for (;;) { + if (*utf8_1 == '\0') { + return (*utf8_2 == '\0') ? 0 : -1; + } else if (*utf8_2 == '\0') { + return 1; + } + + int c1 = GetUtf16FromUtf8(&utf8_1); + int c2 = GetUtf16FromUtf8(&utf8_2); + + if (c1 != c2) { + return c1 > c2 ? 1 : -1; + } + } +} + +} // namespace art + +#endif // ART_RUNTIME_UTF_INL_H_ diff --git a/runtime/utf.cc b/runtime/utf.cc index 1add7d9a684..5ec2ea1c366 100644 --- a/runtime/utf.cc +++ b/runtime/utf.cc @@ -19,6 +19,7 @@ #include "base/logging.h" #include "mirror/array.h" #include "mirror/object-inl.h" +#include "utf-inl.h" namespace art { @@ -84,41 +85,6 @@ int32_t ComputeUtf16Hash(const uint16_t* chars, size_t char_count) { return hash; } - -uint16_t GetUtf16FromUtf8(const char** utf8_data_in) { - uint8_t one = *(*utf8_data_in)++; - if ((one & 0x80) == 0) { - // one-byte encoding - return one; - } - // two- or three-byte encoding - uint8_t two = *(*utf8_data_in)++; - if ((one & 0x20) == 0) { - // two-byte encoding - return ((one & 0x1f) << 6) | (two & 0x3f); - } - // three-byte encoding - uint8_t three = *(*utf8_data_in)++; - return ((one & 0x0f) << 12) | ((two & 0x3f) << 6) | (three & 0x3f); -} - -int CompareModifiedUtf8ToModifiedUtf8AsUtf16CodePointValues(const char* utf8_1, const char* utf8_2) { - for (;;) { - if (*utf8_1 == '\0') { - return (*utf8_2 == '\0') ? 0 : -1; - } else if (*utf8_2 == '\0') { - return 1; - } - - int c1 = GetUtf16FromUtf8(&utf8_1); - int c2 = GetUtf16FromUtf8(&utf8_2); - - if (c1 != c2) { - return c1 > c2 ? 1 : -1; - } - } -} - int CompareModifiedUtf8ToUtf16AsCodePointValues(const char* utf8_1, const uint16_t* utf8_2) { for (;;) { if (*utf8_1 == '\0') { diff --git a/runtime/utf.h b/runtime/utf.h index 4c9a1d959e2..cc5e6d48c2a 100644 --- a/runtime/utf.h +++ b/runtime/utf.h @@ -29,9 +29,10 @@ * See http://en.wikipedia.org/wiki/UTF-8#Modified_UTF-8 for the details. */ namespace art { + namespace mirror { -template class PrimitiveArray; -typedef PrimitiveArray CharArray; + template class PrimitiveArray; + typedef PrimitiveArray CharArray; } // namespace mirror /* diff --git a/runtime/utils.cc b/runtime/utils.cc index dcfe8a75c2e..28ee58c5afd 100644 --- a/runtime/utils.cc +++ b/runtime/utils.cc @@ -34,7 +34,7 @@ #include "mirror/string.h" #include "object_utils.h" #include "os.h" -#include "utf.h" +#include "utf-inl.h" #if !defined(HAVE_POSIX_CLOCKS) #include From 2e2deeb6df3e5a952c194276146706e63ab644a1 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Mon, 23 Sep 2013 11:58:57 -0700 Subject: [PATCH 0047/2402] Fix compiler warning in interpreter. Change-Id: I320a8dbbd27bf7d20cf8b60a3e5d0aaebcda861e --- runtime/interpreter/interpreter_common.h | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/runtime/interpreter/interpreter_common.h b/runtime/interpreter/interpreter_common.h index ec1f9426bf9..a6c5e6284c7 100644 --- a/runtime/interpreter/interpreter_common.h +++ b/runtime/interpreter/interpreter_common.h @@ -81,12 +81,6 @@ extern JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, ShadowFrame& shadow_frame, JValue result_register) NO_THREAD_SAFETY_ANALYSIS __attribute__((hot)); -// Common part of both implementations. -static const int32_t kMaxInt = std::numeric_limits::max(); -static const int32_t kMinInt = std::numeric_limits::min(); -static const int64_t kMaxLong = std::numeric_limits::max(); -static const int64_t kMinLong = std::numeric_limits::min(); - void UnstartedRuntimeInvoke(Thread* self, MethodHelper& mh, const DexFile::CodeItem* code_item, ShadowFrame* shadow_frame, JValue* result, size_t arg_offset) @@ -321,6 +315,7 @@ static inline String* ResolveString(Thread* self, MethodHelper& mh, uint32_t str static inline bool DoIntDivide(ShadowFrame& shadow_frame, size_t result_reg, int32_t dividend, int32_t divisor) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + const int32_t kMinInt = std::numeric_limits::min(); if (UNLIKELY(divisor == 0)) { ThrowArithmeticExceptionDivideByZero(); return false; @@ -336,6 +331,7 @@ static inline bool DoIntDivide(ShadowFrame& shadow_frame, size_t result_reg, static inline bool DoIntRemainder(ShadowFrame& shadow_frame, size_t result_reg, int32_t dividend, int32_t divisor) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + const int32_t kMinInt = std::numeric_limits::min(); if (UNLIKELY(divisor == 0)) { ThrowArithmeticExceptionDivideByZero(); return false; @@ -351,6 +347,7 @@ static inline bool DoIntRemainder(ShadowFrame& shadow_frame, size_t result_reg, static inline bool DoLongDivide(ShadowFrame& shadow_frame, size_t result_reg, int64_t dividend, int64_t divisor) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + const int64_t kMinLong = std::numeric_limits::min(); if (UNLIKELY(divisor == 0)) { ThrowArithmeticExceptionDivideByZero(); return false; @@ -366,6 +363,7 @@ static inline bool DoLongDivide(ShadowFrame& shadow_frame, size_t result_reg, static inline bool DoLongRemainder(ShadowFrame& shadow_frame, size_t result_reg, int64_t dividend, int64_t divisor) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + const int64_t kMinLong = std::numeric_limits::min(); if (UNLIKELY(divisor == 0)) { ThrowArithmeticExceptionDivideByZero(); return false; From b605a4f9a8797046ea1aa05f3405a77fb9a80a76 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Mon, 23 Sep 2013 12:57:09 -0700 Subject: [PATCH 0048/2402] Avoid std::string allocations in HasSameNameAndSignature. Creating a signature requires a std::string that's only used for the purpose of a comparison. Avoid the std::string by comparing the elements of the method's proto_ids. Change-Id: I4394df2ac20bb5896936954f68937fad7e9f7e91 --- runtime/object_utils.h | 58 ++++++++++++++++++++++++++++++++++++++---- 1 file changed, 53 insertions(+), 5 deletions(-) diff --git a/runtime/object_utils.h b/runtime/object_utils.h index 6ee3016179f..c55e10ebc8b 100644 --- a/runtime/object_utils.h +++ b/runtime/object_utils.h @@ -569,16 +569,64 @@ class MethodHelper { bool HasSameNameAndSignature(MethodHelper* other) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + const DexFile& dex_file = GetDexFile(); + const DexFile::MethodId& mid = dex_file.GetMethodId(method_->GetDexMethodIndex()); if (GetDexCache() == other->GetDexCache()) { - const DexFile& dex_file = GetDexFile(); - const DexFile::MethodId& mid = dex_file.GetMethodId(method_->GetDexMethodIndex()); const DexFile::MethodId& other_mid = dex_file.GetMethodId(other->method_->GetDexMethodIndex()); return mid.name_idx_ == other_mid.name_idx_ && mid.proto_idx_ == other_mid.proto_idx_; } - StringPiece name(GetName()); - StringPiece other_name(other->GetName()); - return name == other_name && GetSignature() == other->GetSignature(); + const DexFile& other_dex_file = other->GetDexFile(); + const DexFile::MethodId& other_mid = + other_dex_file.GetMethodId(other->method_->GetDexMethodIndex()); + uint32_t length, other_length; + const char* data = dex_file.StringDataAndLengthByIdx(mid.name_idx_, &length); + const char* other_data = other_dex_file.StringDataAndLengthByIdx(other_mid.name_idx_, + &other_length); + if ((length != other_length) || (strcmp(data, other_data) != 0)) { + return false; // Name mismatch. + } + const DexFile::ProtoId& proto_id = dex_file.GetMethodPrototype(mid); + const DexFile::ProtoId& other_proto_id = other_dex_file.GetMethodPrototype(other_mid); + data = dex_file.StringDataAndLengthByIdx(proto_id.shorty_idx_, &length); + other_data = dex_file.StringDataAndLengthByIdx(proto_id.shorty_idx_, &other_length); + if ((length != other_length) || (strcmp(data, other_data) != 0)) { + return false; // Shorty mismatch. + } + const DexFile::TypeId& return_type_id = dex_file.GetTypeId(proto_id.return_type_idx_); + const DexFile::TypeId& other_return_type_id = + other_dex_file.GetTypeId(other_proto_id.return_type_idx_); + data = dex_file.StringDataAndLengthByIdx(return_type_id.descriptor_idx_, &length); + other_data = other_dex_file.StringDataAndLengthByIdx(other_return_type_id.descriptor_idx_, + &other_length); + if ((length != other_length) || (strcmp(data, other_data) != 0)) { + return false; // Return type mismatch. + } + const DexFile::TypeList* params = dex_file.GetProtoParameters(proto_id); + const DexFile::TypeList* other_params = other_dex_file.GetProtoParameters(other_proto_id); + if (params == nullptr) { + return other_params == nullptr; // Check both lists are empty. + } + if (other_params == nullptr) { + return false; // Parameter list size mismatch. + } + uint32_t params_size = params->Size(); + uint32_t other_params_size = other_params->Size(); + if (params_size != other_params_size) { + return false; // Parameter list size mismatch. + } + for (uint32_t i = 0; i < params_size; ++i) { + const DexFile::TypeId& param_id = dex_file.GetTypeId(params->GetTypeItem(i).type_idx_); + const DexFile::TypeId& other_param_id = + other_dex_file.GetTypeId(other_params->GetTypeItem(i).type_idx_); + data = dex_file.StringDataAndLengthByIdx(param_id.descriptor_idx_, &length); + other_data = other_dex_file.StringDataAndLengthByIdx(other_param_id.descriptor_idx_, + &other_length); + if ((length != other_length) || (strcmp(data, other_data) != 0)) { + return false; // Parameter type mismatch. + } + } + return true; } const DexFile::CodeItem* GetCodeItem() From 2921201dce37ba40c55b89c0deca3c34bf64168e Mon Sep 17 00:00:00 2001 From: Brian Carlstrom Date: Thu, 12 Sep 2013 22:18:30 -0700 Subject: [PATCH 0049/2402] Remove some non-const references in utils Change-Id: Ib2d3c39fadb6a750c68c37b0aa53daab155a65a3 --- ...pache_harmony_dalvik_ddmc_DdmVmInternal.cc | 6 +++-- runtime/thread.cc | 4 ++-- runtime/utils.cc | 22 +++++++++---------- runtime/utils.h | 4 ++-- 4 files changed, 19 insertions(+), 17 deletions(-) diff --git a/runtime/native/org_apache_harmony_dalvik_ddmc_DdmVmInternal.cc b/runtime/native/org_apache_harmony_dalvik_ddmc_DdmVmInternal.cc index 13cd978ef39..06769687bf5 100644 --- a/runtime/native/org_apache_harmony_dalvik_ddmc_DdmVmInternal.cc +++ b/runtime/native/org_apache_harmony_dalvik_ddmc_DdmVmInternal.cc @@ -109,8 +109,10 @@ static void ThreadStatsGetterCallback(Thread* t, void* context) { * be removed from a future version. */ char native_thread_state; - int utime, stime, task_cpu; - GetTaskStats(t->GetTid(), native_thread_state, utime, stime, task_cpu); + int utime; + int stime; + int task_cpu; + GetTaskStats(t->GetTid(), &native_thread_state, &utime, &stime, &task_cpu); std::vector& bytes = *reinterpret_cast*>(context); JDWP::Append4BE(bytes, t->GetThinLockId()); diff --git a/runtime/thread.cc b/runtime/thread.cc index 558ceb47f9e..3e413ab73a0 100644 --- a/runtime/thread.cc +++ b/runtime/thread.cc @@ -417,7 +417,7 @@ void Thread::SetThreadName(const char* name) { void Thread::InitStackHwm() { void* stack_base; size_t stack_size; - GetThreadStack(pthread_self_, stack_base, stack_size); + GetThreadStack(pthread_self_, &stack_base, &stack_size); // TODO: include this in the thread dumps; potentially useful in SIGQUIT output? VLOG(threads) << StringPrintf("Native stack is at %p (%s)", stack_base, PrettySize(stack_size).c_str()); @@ -757,7 +757,7 @@ void Thread::DumpState(std::ostream& os, const Thread* thread, pid_t tid) { int utime = 0; int stime = 0; int task_cpu = 0; - GetTaskStats(tid, native_thread_state, utime, stime, task_cpu); + GetTaskStats(tid, &native_thread_state, &utime, &stime, &task_cpu); os << " | state=" << native_thread_state << " schedstat=( " << scheduler_stats << " )" diff --git a/runtime/utils.cc b/runtime/utils.cc index dcfe8a75c2e..ae10c766d11 100644 --- a/runtime/utils.cc +++ b/runtime/utils.cc @@ -79,23 +79,23 @@ std::string GetThreadName(pid_t tid) { return result; } -void GetThreadStack(pthread_t thread, void*& stack_base, size_t& stack_size) { +void GetThreadStack(pthread_t thread, void** stack_base, size_t* stack_size) { #if defined(__APPLE__) - stack_size = pthread_get_stacksize_np(thread); + *stack_size = pthread_get_stacksize_np(thread); void* stack_addr = pthread_get_stackaddr_np(thread); // Check whether stack_addr is the base or end of the stack. // (On Mac OS 10.7, it's the end.) int stack_variable; if (stack_addr > &stack_variable) { - stack_base = reinterpret_cast(stack_addr) - stack_size; + *stack_base = reinterpret_cast(stack_addr) - *stack_size; } else { - stack_base = stack_addr; + *stack_base = stack_addr; } #else pthread_attr_t attributes; CHECK_PTHREAD_CALL(pthread_getattr_np, (thread, &attributes), __FUNCTION__); - CHECK_PTHREAD_CALL(pthread_attr_getstack, (&attributes, &stack_base, &stack_size), __FUNCTION__); + CHECK_PTHREAD_CALL(pthread_attr_getstack, (&attributes, stack_base, stack_size), __FUNCTION__); CHECK_PTHREAD_CALL(pthread_attr_destroy, (&attributes), __FUNCTION__); #endif } @@ -955,8 +955,8 @@ void SetThreadName(const char* thread_name) { #endif } -void GetTaskStats(pid_t tid, char& state, int& utime, int& stime, int& task_cpu) { - utime = stime = task_cpu = 0; +void GetTaskStats(pid_t tid, char* state, int* utime, int* stime, int* task_cpu) { + *utime = *stime = *task_cpu = 0; std::string stats; if (!ReadFileToString(StringPrintf("/proc/self/task/%d/stat", tid), &stats)) { return; @@ -966,10 +966,10 @@ void GetTaskStats(pid_t tid, char& state, int& utime, int& stime, int& task_cpu) // Extract the three fields we care about. std::vector fields; Split(stats, ' ', fields); - state = fields[0][0]; - utime = strtoull(fields[11].c_str(), NULL, 10); - stime = strtoull(fields[12].c_str(), NULL, 10); - task_cpu = strtoull(fields[36].c_str(), NULL, 10); + *state = fields[0][0]; + *utime = strtoull(fields[11].c_str(), NULL, 10); + *stime = strtoull(fields[12].c_str(), NULL, 10); + *task_cpu = strtoull(fields[36].c_str(), NULL, 10); } std::string GetSchedulerGroupName(pid_t tid) { diff --git a/runtime/utils.h b/runtime/utils.h index c506fbaad03..34e94592a41 100644 --- a/runtime/utils.h +++ b/runtime/utils.h @@ -323,10 +323,10 @@ pid_t GetTid(); std::string GetThreadName(pid_t tid); // Returns details of the given thread's stack. -void GetThreadStack(pthread_t thread, void*& stack_base, size_t& stack_size); +void GetThreadStack(pthread_t thread, void** stack_base, size_t* stack_size); // Reads data from "/proc/self/task/${tid}/stat". -void GetTaskStats(pid_t tid, char& state, int& utime, int& stime, int& task_cpu); +void GetTaskStats(pid_t tid, char* state, int* utime, int* stime, int* task_cpu); // Returns the name of the scheduler group for the given thread the current process, or the empty string. std::string GetSchedulerGroupName(pid_t tid); From e4ef0953b6a27f00a93b4807cd1ff668f4fbeb22 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Tue, 24 Sep 2013 07:31:04 +0000 Subject: [PATCH 0050/2402] Revert "Avoid std::string allocations in HasSameNameAndSignature." This reverts commit b605a4f9a8797046ea1aa05f3405a77fb9a80a76. Change-Id: I4f185a1ea1e453fcff5325062be8ff1d8af20396 --- runtime/object_utils.h | 58 ++++-------------------------------------- 1 file changed, 5 insertions(+), 53 deletions(-) diff --git a/runtime/object_utils.h b/runtime/object_utils.h index c55e10ebc8b..6ee3016179f 100644 --- a/runtime/object_utils.h +++ b/runtime/object_utils.h @@ -569,64 +569,16 @@ class MethodHelper { bool HasSameNameAndSignature(MethodHelper* other) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - const DexFile& dex_file = GetDexFile(); - const DexFile::MethodId& mid = dex_file.GetMethodId(method_->GetDexMethodIndex()); if (GetDexCache() == other->GetDexCache()) { + const DexFile& dex_file = GetDexFile(); + const DexFile::MethodId& mid = dex_file.GetMethodId(method_->GetDexMethodIndex()); const DexFile::MethodId& other_mid = dex_file.GetMethodId(other->method_->GetDexMethodIndex()); return mid.name_idx_ == other_mid.name_idx_ && mid.proto_idx_ == other_mid.proto_idx_; } - const DexFile& other_dex_file = other->GetDexFile(); - const DexFile::MethodId& other_mid = - other_dex_file.GetMethodId(other->method_->GetDexMethodIndex()); - uint32_t length, other_length; - const char* data = dex_file.StringDataAndLengthByIdx(mid.name_idx_, &length); - const char* other_data = other_dex_file.StringDataAndLengthByIdx(other_mid.name_idx_, - &other_length); - if ((length != other_length) || (strcmp(data, other_data) != 0)) { - return false; // Name mismatch. - } - const DexFile::ProtoId& proto_id = dex_file.GetMethodPrototype(mid); - const DexFile::ProtoId& other_proto_id = other_dex_file.GetMethodPrototype(other_mid); - data = dex_file.StringDataAndLengthByIdx(proto_id.shorty_idx_, &length); - other_data = dex_file.StringDataAndLengthByIdx(proto_id.shorty_idx_, &other_length); - if ((length != other_length) || (strcmp(data, other_data) != 0)) { - return false; // Shorty mismatch. - } - const DexFile::TypeId& return_type_id = dex_file.GetTypeId(proto_id.return_type_idx_); - const DexFile::TypeId& other_return_type_id = - other_dex_file.GetTypeId(other_proto_id.return_type_idx_); - data = dex_file.StringDataAndLengthByIdx(return_type_id.descriptor_idx_, &length); - other_data = other_dex_file.StringDataAndLengthByIdx(other_return_type_id.descriptor_idx_, - &other_length); - if ((length != other_length) || (strcmp(data, other_data) != 0)) { - return false; // Return type mismatch. - } - const DexFile::TypeList* params = dex_file.GetProtoParameters(proto_id); - const DexFile::TypeList* other_params = other_dex_file.GetProtoParameters(other_proto_id); - if (params == nullptr) { - return other_params == nullptr; // Check both lists are empty. - } - if (other_params == nullptr) { - return false; // Parameter list size mismatch. - } - uint32_t params_size = params->Size(); - uint32_t other_params_size = other_params->Size(); - if (params_size != other_params_size) { - return false; // Parameter list size mismatch. - } - for (uint32_t i = 0; i < params_size; ++i) { - const DexFile::TypeId& param_id = dex_file.GetTypeId(params->GetTypeItem(i).type_idx_); - const DexFile::TypeId& other_param_id = - other_dex_file.GetTypeId(other_params->GetTypeItem(i).type_idx_); - data = dex_file.StringDataAndLengthByIdx(param_id.descriptor_idx_, &length); - other_data = other_dex_file.StringDataAndLengthByIdx(other_param_id.descriptor_idx_, - &other_length); - if ((length != other_length) || (strcmp(data, other_data) != 0)) { - return false; // Parameter type mismatch. - } - } - return true; + StringPiece name(GetName()); + StringPiece other_name(other->GetName()); + return name == other_name && GetSignature() == other->GetSignature(); } const DexFile::CodeItem* GetCodeItem() From a3faaf4bece7f42529c013fe87bd41de59798656 Mon Sep 17 00:00:00 2001 From: Jeff Hao Date: Tue, 3 Sep 2013 19:07:00 -0700 Subject: [PATCH 0051/2402] Fix handling of unresolved references in verifier. The verifier should not treat use of unresolved references as a reason to reject the entire class. Instead, the verifier treats the instruction as a throw. If that class is run, the interpreter with extra checks will throw an exception. Bug: 10457426 Change-Id: I3799da843a7ffb3519bbf6dc13a6276519d9cb95 --- runtime/interpreter/interpreter_common.cc | 22 +++++++++++++ runtime/interpreter/interpreter_common.h | 23 +++++++++++-- .../interpreter_goto_table_impl.cc | 26 ++++++++++++++- .../interpreter/interpreter_switch_impl.cc | 26 ++++++++++++++- runtime/mirror/object-inl.h | 6 ++++ runtime/mirror/object.h | 5 +++ runtime/verifier/method_verifier.cc | 33 +++++++++++-------- runtime/verifier/register_line.cc | 20 +++++------ 8 files changed, 132 insertions(+), 29 deletions(-) diff --git a/runtime/interpreter/interpreter_common.cc b/runtime/interpreter/interpreter_common.cc index 86a6aea0535..36b250ca425 100644 --- a/runtime/interpreter/interpreter_common.cc +++ b/runtime/interpreter/interpreter_common.cc @@ -22,6 +22,7 @@ namespace interpreter { template bool DoInvoke(Thread* self, ShadowFrame& shadow_frame, const Instruction* inst, JValue* result) { + bool do_assignability_check = do_access_check; uint32_t method_idx = (is_range) ? inst->VRegB_3rc() : inst->VRegB_35c(); uint32_t vregC = (is_range) ? inst->VRegC_3rc() : inst->VRegC_35c(); Object* receiver = (type == kStatic) ? NULL : shadow_frame.GetVRegReference(vregC); @@ -61,6 +62,10 @@ bool DoInvoke(Thread* self, ShadowFrame& shadow_frame, ++cur_reg; } + const DexFile::TypeList* params; + if (do_assignability_check) { + params = mh.GetParameterTypeList(); + } size_t arg_offset = (receiver == NULL) ? 0 : 1; const char* shorty = mh.GetShorty(); uint32_t arg[5]; @@ -73,6 +78,23 @@ bool DoInvoke(Thread* self, ShadowFrame& shadow_frame, switch (shorty[shorty_pos + 1]) { case 'L': { Object* o = shadow_frame.GetVRegReference(arg_pos); + if (do_assignability_check && o != NULL) { + Class* arg_type = mh.GetClassFromTypeIdx(params->GetTypeItem(shorty_pos).type_idx_); + if (arg_type == NULL) { + CHECK(self->IsExceptionPending()); + return false; + } + if (!o->VerifierInstanceOf(arg_type)) { + // This should never happen. + self->ThrowNewExceptionF(self->GetCurrentLocationForThrow(), + "Ljava/lang/VirtualMachineError;", + "Invoking %s with bad arg %d, type '%s' not instance of '%s'", + mh.GetName(), shorty_pos, + ClassHelper(o->GetClass()).GetDescriptor(), + ClassHelper(arg_type).GetDescriptor()); + return false; + } + } new_shadow_frame->SetVRegReference(cur_reg, o); break; } diff --git a/runtime/interpreter/interpreter_common.h b/runtime/interpreter/interpreter_common.h index a6c5e6284c7..3ad1935c942 100644 --- a/runtime/interpreter/interpreter_common.h +++ b/runtime/interpreter/interpreter_common.h @@ -215,6 +215,7 @@ static bool DoFieldPut(Thread* self, const ShadowFrame& shadow_frame, template static inline bool DoFieldPut(Thread* self, const ShadowFrame& shadow_frame, const Instruction* inst, uint16_t inst_data) { + bool do_assignability_check = do_access_check; bool is_static = (find_type == StaticObjectWrite) || (find_type == StaticPrimitiveWrite); uint32_t field_idx = is_static ? inst->VRegB_21c() : inst->VRegC_22c(); ArtField* f = FindFieldFromCode(field_idx, shadow_frame.GetMethod(), self, @@ -255,9 +256,24 @@ static inline bool DoFieldPut(Thread* self, const ShadowFrame& shadow_frame, case Primitive::kPrimLong: f->SetLong(obj, shadow_frame.GetVRegLong(vregA)); break; - case Primitive::kPrimNot: - f->SetObj(obj, shadow_frame.GetVRegReference(vregA)); + case Primitive::kPrimNot: { + Object* reg = shadow_frame.GetVRegReference(vregA); + if (do_assignability_check && reg != NULL) { + Class* field_class = FieldHelper(f).GetType(); + if (!reg->VerifierInstanceOf(field_class)) { + // This should never happen. + self->ThrowNewExceptionF(self->GetCurrentLocationForThrow(), + "Ljava/lang/VirtualMachineError;", + "Put '%s' that is not instance of field '%s' in '%s'", + ClassHelper(reg->GetClass()).GetDescriptor(), + ClassHelper(field_class).GetDescriptor(), + ClassHelper(f->GetDeclaringClass()).GetDescriptor()); + return false; + } + } + f->SetObj(obj, reg); break; + } default: LOG(FATAL) << "Unreachable: " << field_type; } @@ -479,7 +495,8 @@ static void UnexpectedOpcode(const Instruction* inst, MethodHelper& mh) } static inline void TraceExecution(const ShadowFrame& shadow_frame, const Instruction* inst, - const uint32_t dex_pc, MethodHelper& mh) { + const uint32_t dex_pc, MethodHelper& mh) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { const bool kTracing = false; if (kTracing) { #define TRACE_LOG std::cerr diff --git a/runtime/interpreter/interpreter_goto_table_impl.cc b/runtime/interpreter/interpreter_goto_table_impl.cc index d70b80eb937..018add30070 100644 --- a/runtime/interpreter/interpreter_goto_table_impl.cc +++ b/runtime/interpreter/interpreter_goto_table_impl.cc @@ -74,6 +74,7 @@ namespace interpreter { template JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* code_item, ShadowFrame& shadow_frame, JValue result_register) { + bool do_assignability_check = do_access_check; if (UNLIKELY(!shadow_frame.HasReferenceArray())) { LOG(FATAL) << "Invalid shadow frame for interpreter use"; return JValue(); @@ -264,11 +265,28 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* HANDLE_INSTRUCTION_START(RETURN_OBJECT) { JValue result; + Object* obj_result = shadow_frame.GetVRegReference(inst->VRegA_11x(inst_data)); result.SetJ(0); - result.SetL(shadow_frame.GetVRegReference(inst->VRegA_11x(inst_data))); + result.SetL(obj_result); if (UNLIKELY(self->TestAllFlags())) { CheckSuspend(self); } + if (do_assignability_check && obj_result != NULL) { + Class* return_type = MethodHelper(shadow_frame.GetMethod()).GetReturnType(); + if (return_type == NULL) { + // Return the pending exception. + HANDLE_PENDING_EXCEPTION(); + } + if (!obj_result->VerifierInstanceOf(return_type)) { + // This should never happen. + self->ThrowNewExceptionF(self->GetCurrentLocationForThrow(), + "Ljava/lang/VirtualMachineError;", + "Returning '%s' that is not instance of return type '%s'", + ClassHelper(obj_result->GetClass()).GetDescriptor(), + ClassHelper(return_type).GetDescriptor()); + HANDLE_PENDING_EXCEPTION(); + } + } if (UNLIKELY(instrumentation->HasMethodExitListeners())) { instrumentation->MethodExitEvent(self, shadow_frame.GetThisObject(code_item->ins_size_), shadow_frame.GetMethod(), dex_pc, @@ -512,6 +530,12 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* Object* exception = shadow_frame.GetVRegReference(inst->VRegA_11x(inst_data)); if (UNLIKELY(exception == NULL)) { ThrowNullPointerException(NULL, "throw with null exception"); + } else if (do_assignability_check && !exception->GetClass()->IsThrowableClass()) { + // This should never happen. + self->ThrowNewExceptionF(self->GetCurrentLocationForThrow(), + "Ljava/lang/VirtualMachineError;", + "Throwing '%s' that is not instance of Throwable", + ClassHelper(exception->GetClass()).GetDescriptor()); } else { self->SetException(shadow_frame.GetCurrentLocationForThrow(), exception->AsThrowable()); } diff --git a/runtime/interpreter/interpreter_switch_impl.cc b/runtime/interpreter/interpreter_switch_impl.cc index d49807cc5dd..2d658b3784f 100644 --- a/runtime/interpreter/interpreter_switch_impl.cc +++ b/runtime/interpreter/interpreter_switch_impl.cc @@ -53,6 +53,7 @@ namespace interpreter { template static JValue ExecuteSwitchImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* code_item, ShadowFrame& shadow_frame, JValue result_register) { + bool do_assignability_check = do_access_check; if (UNLIKELY(!shadow_frame.HasReferenceArray())) { LOG(FATAL) << "Invalid shadow frame for interpreter use"; return JValue(); @@ -226,11 +227,28 @@ static JValue ExecuteSwitchImpl(Thread* self, MethodHelper& mh, const DexFile::C case Instruction::RETURN_OBJECT: { PREAMBLE(); JValue result; + Object* obj_result = shadow_frame.GetVRegReference(inst->VRegA_11x(inst_data)); result.SetJ(0); - result.SetL(shadow_frame.GetVRegReference(inst->VRegA_11x(inst_data))); + result.SetL(obj_result); if (UNLIKELY(self->TestAllFlags())) { CheckSuspend(self); } + if (do_assignability_check && obj_result != NULL) { + Class* return_type = MethodHelper(shadow_frame.GetMethod()).GetReturnType(); + if (return_type == NULL) { + // Return the pending exception. + HANDLE_PENDING_EXCEPTION(); + } + if (!obj_result->VerifierInstanceOf(return_type)) { + // This should never happen. + self->ThrowNewExceptionF(self->GetCurrentLocationForThrow(), + "Ljava/lang/VirtualMachineError;", + "Returning '%s' that is not instance of return type '%s'", + ClassHelper(obj_result->GetClass()).GetDescriptor(), + ClassHelper(return_type).GetDescriptor()); + HANDLE_PENDING_EXCEPTION(); + } + } if (UNLIKELY(instrumentation->HasMethodExitListeners())) { instrumentation->MethodExitEvent(self, shadow_frame.GetThisObject(code_item->ins_size_), shadow_frame.GetMethod(), inst->GetDexPc(insns), @@ -472,6 +490,12 @@ static JValue ExecuteSwitchImpl(Thread* self, MethodHelper& mh, const DexFile::C Object* exception = shadow_frame.GetVRegReference(inst->VRegA_11x(inst_data)); if (UNLIKELY(exception == NULL)) { ThrowNullPointerException(NULL, "throw with null exception"); + } else if (do_assignability_check && !exception->GetClass()->IsThrowableClass()) { + // This should never happen. + self->ThrowNewExceptionF(self->GetCurrentLocationForThrow(), + "Ljava/lang/VirtualMachineError;", + "Throwing '%s' that is not instance of Throwable", + ClassHelper(exception->GetClass()).GetDescriptor()); } else { self->SetException(shadow_frame.GetCurrentLocationForThrow(), exception->AsThrowable()); } diff --git a/runtime/mirror/object-inl.h b/runtime/mirror/object-inl.h index 63396d2f599..5ed3db342ca 100644 --- a/runtime/mirror/object-inl.h +++ b/runtime/mirror/object-inl.h @@ -71,6 +71,12 @@ inline void Object::Wait(Thread* self, int64_t ms, int32_t ns) { Monitor::Wait(self, this, ms, ns, true, kTimedWaiting); } +inline bool Object::VerifierInstanceOf(const Class* klass) const { + DCHECK(klass != NULL); + DCHECK(GetClass() != NULL); + return klass->IsInterface() || InstanceOf(klass); +} + inline bool Object::InstanceOf(const Class* klass) const { DCHECK(klass != NULL); DCHECK(GetClass() != NULL); diff --git a/runtime/mirror/object.h b/runtime/mirror/object.h index efaa1834984..e105525f79f 100644 --- a/runtime/mirror/object.h +++ b/runtime/mirror/object.h @@ -71,6 +71,11 @@ class MANAGED Object { void SetClass(Class* new_klass); + // The verifier treats all interfaces as java.lang.Object and relies on runtime checks in + // invoke-interface to detect incompatible interface types. + bool VerifierInstanceOf(const Class* klass) const + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + bool InstanceOf(const Class* klass) const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc index 9811926b168..924a1bb3771 100644 --- a/runtime/verifier/method_verifier.cc +++ b/runtime/verifier/method_verifier.cc @@ -439,6 +439,8 @@ std::ostream& MethodVerifier::Fail(VerifyError error) { // to an interpreter. error = VERIFY_ERROR_BAD_CLASS_SOFT; } else { + // If we fail again at runtime, mark that this instruction would throw and force this + // method to be executed using the interpreter with checks. have_pending_runtime_throw_failure_ = true; } break; @@ -1580,11 +1582,13 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { Fail(VERIFY_ERROR_BAD_CLASS_SOFT) << "returning uninitialized object '" << reg_type << "'"; } else if (!return_type.IsAssignableFrom(reg_type)) { - Fail(reg_type.IsUnresolvedTypes() ? - VERIFY_ERROR_BAD_CLASS_SOFT : - VERIFY_ERROR_BAD_CLASS_HARD) - << "returning '" << reg_type << "', but expected from declaration '" - << return_type << "'"; + if (reg_type.IsUnresolvedTypes() || return_type.IsUnresolvedTypes()) { + Fail(VERIFY_ERROR_NO_CLASS) << " can't resolve returned type '" << return_type + << "' or '" << reg_type << "'"; + } else { + Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "returning '" << reg_type + << "', but expected from declaration '" << return_type << "'"; + } } } } @@ -1804,8 +1808,8 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { case Instruction::THROW: { const RegType& res_type = work_line_->GetRegisterType(inst->VRegA_11x()); if (!reg_types_.JavaLangThrowable(false).IsAssignableFrom(res_type)) { - Fail(VERIFY_ERROR_BAD_CLASS_SOFT) << "thrown class " << res_type - << " not instanceof Throwable"; + Fail(res_type.IsUnresolvedTypes() ? VERIFY_ERROR_NO_CLASS : VERIFY_ERROR_BAD_CLASS_SOFT) + << "thrown class " << res_type << " not instanceof Throwable"; } break; } @@ -1929,8 +1933,8 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { const RegType& orig_type = work_line_->GetRegisterType(instance_of_inst->VRegB_22c()); const RegType& cast_type = ResolveClassAndCheckAccess(instance_of_inst->VRegC_22c()); - if (!cast_type.IsUnresolvedTypes() && !cast_type.GetClass()->IsInterface() && - !cast_type.IsAssignableFrom(orig_type)) { + if (!cast_type.IsUnresolvedTypes() && !orig_type.IsUnresolvedTypes() && + !cast_type.GetClass()->IsInterface() && !cast_type.IsAssignableFrom(orig_type)) { RegisterLine* update_line = new RegisterLine(code_item_->registers_size_, this); if (inst->Opcode() == Instruction::IF_EQZ) { fallthrough_line.reset(update_line); @@ -2610,7 +2614,7 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { info_messages_ << "Rejecting opcode " << inst->DumpString(dex_file_); return false; } else if (have_pending_runtime_throw_failure_) { - /* slow path will throw, mark following code as unreachable */ + /* checking interpreter will throw, mark following code as unreachable */ opcode_flags = Instruction::kThrow; } /* @@ -2861,7 +2865,8 @@ const RegType& MethodVerifier::GetCaughtExceptionType() { } else if (!reg_types_.JavaLangThrowable(false).IsAssignableFrom(exception)) { // We don't know enough about the type and the common path merge will result in // Conflict. Fail here knowing the correct thing can be done at runtime. - Fail(VERIFY_ERROR_BAD_CLASS_SOFT) << "unexpected non-exception class " << exception; + Fail(exception.IsUnresolvedTypes() ? VERIFY_ERROR_NO_CLASS : + VERIFY_ERROR_BAD_CLASS_SOFT) << "unexpected non-exception class " << exception; return reg_types_.Conflict(); } else if (common_super->Equals(exception)) { // odd case, but nothing to do @@ -3042,7 +3047,8 @@ mirror::ArtMethod* MethodVerifier::VerifyInvocationArgs(const Instruction* inst, reg_types_.FromClass(ClassHelper(klass).GetDescriptor(), klass, klass->CannotBeAssignedFromOtherTypes()); if (!res_method_class.IsAssignableFrom(actual_arg_type)) { - Fail(VERIFY_ERROR_BAD_CLASS_SOFT) << "'this' argument '" << actual_arg_type + Fail(actual_arg_type.IsUnresolvedTypes() ? VERIFY_ERROR_NO_CLASS: + VERIFY_ERROR_BAD_CLASS_SOFT) << "'this' argument '" << actual_arg_type << "' not instance of '" << res_method_class << "'"; return NULL; } @@ -3166,7 +3172,8 @@ mirror::ArtMethod* MethodVerifier::VerifyInvokeVirtualQuickArgs(const Instructio reg_types_.FromClass(ClassHelper(klass).GetDescriptor(), klass, klass->CannotBeAssignedFromOtherTypes()); if (!res_method_class.IsAssignableFrom(actual_arg_type)) { - Fail(VERIFY_ERROR_BAD_CLASS_SOFT) << "'this' argument '" << actual_arg_type + Fail(actual_arg_type.IsUnresolvedTypes() ? VERIFY_ERROR_NO_CLASS : + VERIFY_ERROR_BAD_CLASS_SOFT) << "'this' argument '" << actual_arg_type << "' not instance of '" << res_method_class << "'"; return NULL; } diff --git a/runtime/verifier/register_line.cc b/runtime/verifier/register_line.cc index 5affe4759aa..a615cc1273e 100644 --- a/runtime/verifier/register_line.cc +++ b/runtime/verifier/register_line.cc @@ -44,12 +44,6 @@ bool RegisterLine::SetRegisterType(uint32_t vdst, const RegType& new_type) { } else if (new_type.IsConflict()) { // should only be set during a merge verifier_->Fail(VERIFY_ERROR_BAD_CLASS_SOFT) << "Set register to unknown type " << new_type; return false; - } else if (verifier_->CanLoadClasses() && !Runtime::Current()->IsCompiler() && - new_type.IsUnresolvedTypes()) { - // Unresolvable classes at runtime are bad and marked as a rewrite error. - verifier_->Fail(VERIFY_ERROR_NO_CLASS) << "Set register to unresolved class '" - << new_type << "' at runtime"; - return false; } else { line_[vdst] = new_type.GetId(); } @@ -116,11 +110,15 @@ bool RegisterLine::VerifyRegisterType(uint32_t vsrc, // Verify the src register type against the check type refining the type of the register const RegType& src_type = GetRegisterType(vsrc); if (!(check_type.IsAssignableFrom(src_type))) { - // Hard fail if one of the types is primitive, since they are concretely known. - enum VerifyError fail_type = (!check_type.IsNonZeroReferenceTypes() || - !src_type.IsNonZeroReferenceTypes()) - ? VERIFY_ERROR_BAD_CLASS_HARD - : VERIFY_ERROR_BAD_CLASS_SOFT; + enum VerifyError fail_type; + if (!check_type.IsNonZeroReferenceTypes() || !src_type.IsNonZeroReferenceTypes()) { + // Hard fail if one of the types is primitive, since they are concretely known. + fail_type = VERIFY_ERROR_BAD_CLASS_HARD; + } else if (check_type.IsUnresolvedTypes() || src_type.IsUnresolvedTypes()) { + fail_type = VERIFY_ERROR_NO_CLASS; + } else { + fail_type = VERIFY_ERROR_BAD_CLASS_SOFT; + } verifier_->Fail(fail_type) << "register v" << vsrc << " has type " << src_type << " but expected " << check_type; return false; From fc0e94bed3f88ed7e50854fd8dfaf5dcb345250f Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Mon, 23 Sep 2013 23:51:32 -0700 Subject: [PATCH 0052/2402] StringPiece clean up. Profile guided clean up. Try to avoid creating StringPieces with the contents of a dex file where the length is known. Try to avoid RegTypeCache::FromDescriptor when there's a class available. Make ConstantType::ConstantValue inlinable. Saving of about 50ms from a 2 threaded ThinkFree compile on host. Change-Id: I47a12c3c76f46e2c9805be1c3a3e3870fe1f5d85 --- compiler/dex/quick/gen_invoke.cc | 4 +- compiler/driver/compiler_driver.cc | 27 +++-- compiler/driver/compiler_driver.h | 2 +- compiler/image_writer.cc | 4 +- oatdump/oatdump.cc | 2 +- runtime/class_linker.cc | 107 +++++++++--------- runtime/debugger.cc | 13 ++- runtime/debugger.h | 2 +- runtime/dex_file-inl.h | 11 ++ runtime/dex_file.h | 3 + runtime/gc/heap.cc | 2 +- runtime/interpreter/interpreter.cc | 2 +- runtime/jdwp/jdwp_handler.cc | 4 +- runtime/mirror/class.cc | 14 +-- runtime/object_utils.h | 167 ++++++++++++++++++++++------ runtime/reflection.cc | 4 +- runtime/verifier/method_verifier.cc | 155 ++++++++++++++++---------- runtime/verifier/method_verifier.h | 1 + runtime/verifier/reg_type.cc | 5 - runtime/verifier/reg_type.h | 9 +- runtime/verifier/reg_type_cache.cc | 1 + 21 files changed, 350 insertions(+), 189 deletions(-) diff --git a/compiler/dex/quick/gen_invoke.cc b/compiler/dex/quick/gen_invoke.cc index 72ae91eebb6..ed838637338 100644 --- a/compiler/dex/quick/gen_invoke.cc +++ b/compiler/dex/quick/gen_invoke.cc @@ -1219,8 +1219,10 @@ bool Mir2Lir::GenIntrinsic(CallInfo* info) { * method. By doing this during basic block construction, we can also * take advantage of/generate new useful dataflow info. */ + const DexFile::MethodId& target_mid = cu_->dex_file->GetMethodId(info->index); + const DexFile::TypeId& declaring_type = cu_->dex_file->GetTypeId(target_mid.class_idx_); StringPiece tgt_methods_declaring_class( - cu_->dex_file->GetMethodDeclaringClassDescriptor(cu_->dex_file->GetMethodId(info->index))); + cu_->dex_file->StringDataAsStringPieceByIdx(declaring_type.descriptor_idx_)); if (tgt_methods_declaring_class.starts_with("Ljava/lang/Double;")) { std::string tgt_method(PrettyMethod(info->index, *cu_->dex_file)); if (tgt_method == "long java.lang.Double.doubleToRawLongBits(double)") { diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc index c12a7d964ae..e2277156053 100644 --- a/compiler/driver/compiler_driver.cc +++ b/compiler/driver/compiler_driver.cc @@ -600,12 +600,11 @@ void CompilerDriver::PreCompile(jobject class_loader, const std::vectorfind(descriptor) != image_classes_->end(); + return image_classes_->find(descriptor.data()) != image_classes_->end(); } } @@ -780,7 +779,8 @@ void CompilerDriver::UpdateImageClasses(base::TimingLogger& timings) { bool CompilerDriver::CanAssumeTypeIsPresentInDexCache(const DexFile& dex_file, uint32_t type_idx) { - if (IsImage() && IsImageClass(dex_file.GetTypeDescriptor(dex_file.GetTypeId(type_idx)))) { + if (IsImage() && + IsImageClass(dex_file.StringDataAsStringPieceByIdx(dex_file.GetTypeId(type_idx).descriptor_idx_))) { if (kIsDebugBuild) { ScopedObjectAccess soa(Thread::Current()); mirror::DexCache* dex_cache = Runtime::Current()->GetClassLinker()->FindDexCache(dex_file); @@ -1098,7 +1098,7 @@ void CompilerDriver::GetCodeAndMethodForDirectCall(InvokeType type, InvokeType s if (compiling_boot) { if (support_boot_image_fixup_) { MethodHelper mh(method); - if (IsImageClass(mh.GetDeclaringClassDescriptor())) { + if (IsImageClass(mh.GetDeclaringClassDescriptorAsStringPiece())) { // We can only branch directly to Methods that are resolved in the DexCache. // Otherwise we won't invoke the resolution trampoline. *direct_method = -1; @@ -1572,8 +1572,8 @@ static void ResolveType(const ParallelCompilationManager* manager, size_t type_i CHECK(soa.Self()->IsExceptionPending()); mirror::Throwable* exception = soa.Self()->GetException(NULL); VLOG(compiler) << "Exception during type resolution: " << exception->Dump(); - if (strcmp(ClassHelper(exception->GetClass()).GetDescriptor(), - "Ljava/lang/OutOfMemoryError;") == 0) { + if (ClassHelper(exception->GetClass()).GetDescriptorAsStringPiece() == + "Ljava/lang/OutOfMemoryError;") { // There's little point continuing compilation if the heap is exhausted. LOG(FATAL) << "Out of memory during type resolution for compilation"; } @@ -2084,11 +2084,14 @@ static const char* class_initializer_black_list[] = { static void InitializeClass(const ParallelCompilationManager* manager, size_t class_def_index) LOCKS_EXCLUDED(Locks::mutator_lock_) { ATRACE_CALL(); - const DexFile::ClassDef& class_def = manager->GetDexFile()->GetClassDef(class_def_index); + const DexFile* dex_file = manager->GetDexFile(); + const DexFile::ClassDef& class_def = dex_file->GetClassDef(class_def_index); + const DexFile::TypeId& class_type_id = dex_file->GetTypeId(class_def.class_idx_); + StringPiece descriptor(dex_file->StringDataAsStringPieceByIdx(class_type_id.descriptor_idx_)); + ScopedObjectAccess soa(Thread::Current()); mirror::ClassLoader* class_loader = soa.Decode(manager->GetClassLoader()); - const char* descriptor = manager->GetDexFile()->GetClassDescriptor(class_def); - mirror::Class* klass = manager->GetClassLinker()->FindClass(descriptor, class_loader); + mirror::Class* klass = manager->GetClassLinker()->FindClass(descriptor.data(), class_loader); if (klass != NULL) { // Only try to initialize classes that were successfully verified. if (klass->IsVerified()) { @@ -2118,7 +2121,7 @@ static void InitializeClass(const ParallelCompilationManager* manager, size_t cl bool is_black_listed = StringPiece(descriptor).ends_with("$NoPreloadHolder;"); if (!is_black_listed) { for (size_t i = 0; i < arraysize(class_initializer_black_list); ++i) { - if (StringPiece(descriptor) == class_initializer_black_list[i]) { + if (descriptor == class_initializer_black_list[i]) { is_black_listed = true; break; } @@ -2126,7 +2129,7 @@ static void InitializeClass(const ParallelCompilationManager* manager, size_t cl } if (!is_black_listed) { VLOG(compiler) << "Initializing: " << descriptor; - if (StringPiece(descriptor) == "Ljava/lang/Void;") { + if (descriptor == "Ljava/lang/Void;") { // Hand initialize j.l.Void to avoid Dex file operations in un-started runtime. ObjectLock lock(soa.Self(), klass); mirror::ObjectArray* fields = klass->GetSFields(); diff --git a/compiler/driver/compiler_driver.h b/compiler/driver/compiler_driver.h index 66c9cbf91a1..7657af5cee3 100644 --- a/compiler/driver/compiler_driver.h +++ b/compiler/driver/compiler_driver.h @@ -309,7 +309,7 @@ class CompilerDriver { } // Checks if class specified by type_idx is one of the image_classes_ - bool IsImageClass(const char* descriptor) const; + bool IsImageClass(const StringPiece& descriptor) const; void RecordClassStatus(ClassReference ref, mirror::Class::Status status) LOCKS_EXCLUDED(compiled_classes_lock_); diff --git a/compiler/image_writer.cc b/compiler/image_writer.cc index f82c6fb40f3..bcdc1c15c94 100644 --- a/compiler/image_writer.cc +++ b/compiler/image_writer.cc @@ -241,7 +241,7 @@ void ImageWriter::ComputeEagerResolvedStrings() } bool ImageWriter::IsImageClass(const Class* klass) { - return compiler_driver_.IsImageClass(ClassHelper(klass).GetDescriptor()); + return compiler_driver_.IsImageClass(ClassHelper(klass).GetDescriptorAsStringPiece()); } struct NonImageClasses { @@ -296,7 +296,7 @@ void ImageWriter::PruneNonImageClasses() { bool ImageWriter::NonImageClassesVisitor(Class* klass, void* arg) { NonImageClasses* context = reinterpret_cast(arg); if (!context->image_writer->IsImageClass(klass)) { - context->non_image_classes->insert(ClassHelper(klass).GetDescriptor()); + context->non_image_classes->insert(ClassHelper(klass).GetDescriptorAsStringPiece().as_string()); } return true; } diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc index fc9e00c2cb6..cc6b5d78660 100644 --- a/oatdump/oatdump.cc +++ b/oatdump/oatdump.cc @@ -1119,7 +1119,7 @@ class ImageDumper { typedef SafeMap SizeAndCountTable; SizeAndCountTable sizes_and_counts; - void Update(const std::string& descriptor, size_t object_bytes) { + void Update(const char* descriptor, size_t object_bytes) { SizeAndCountTable::iterator it = sizes_and_counts.find(descriptor); if (it != sizes_and_counts.end()) { it->second.bytes += object_bytes; diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index 91db8835a82..6aae63ee2c6 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -1840,7 +1840,7 @@ mirror::ArtMethod* ClassLinker::LoadMethod(Thread* self, const DexFile& dex_file SirtRef& klass) { uint32_t dex_method_idx = it.GetMemberIndex(); const DexFile::MethodId& method_id = dex_file.GetMethodId(dex_method_idx); - StringPiece method_name(dex_file.GetMethodName(method_id)); + StringPiece method_name(dex_file.StringDataAsStringPieceByIdx(method_id.name_idx_)); mirror::ArtMethod* dst = AllocArtMethod(self); if (UNLIKELY(dst == NULL)) { @@ -1869,7 +1869,7 @@ mirror::ArtMethod* ClassLinker::LoadMethod(Thread* self, const DexFile& dex_file klass->SetFinalizable(); } else { ClassHelper kh(klass.get()); - StringPiece klass_descriptor(kh.GetDescriptor()); + StringPiece klass_descriptor(kh.GetDescriptorAsStringPiece()); // The Enum class declares a "final" finalize() method to prevent subclasses from // introducing a finalizer. We don't want to set the finalizable flag for Enum or its // subclasses, so we exclude it here. @@ -2219,7 +2219,7 @@ bool ClassLinker::RemoveClass(const char* descriptor, const mirror::ClassLoader* ++it) { mirror::Class* klass = it->second; kh.ChangeClass(klass); - if (strcmp(kh.GetDescriptor(), descriptor) == 0 && klass->GetClassLoader() == class_loader) { + if (kh.GetDescriptorAsStringPiece() == descriptor && klass->GetClassLoader() == class_loader) { class_table_.erase(it); return true; } @@ -2265,15 +2265,16 @@ mirror::Class* ClassLinker::LookupClassFromTableLocked(const char* descriptor, for (auto it = class_table_.lower_bound(hash); it != end && it->first == hash; ++it) { mirror::Class* klass = it->second; kh.ChangeClass(klass); - if (klass->GetClassLoader() == class_loader && strcmp(descriptor, kh.GetDescriptor()) == 0) { + if (klass->GetClassLoader() == class_loader && kh.GetDescriptorAsStringPiece() == descriptor) { if (kIsDebugBuild) { // Check for duplicates in the table. for (++it; it != end && it->first == hash; ++it) { mirror::Class* klass2 = it->second; kh.ChangeClass(klass2); - CHECK(!(strcmp(descriptor, kh.GetDescriptor()) == 0 && klass2->GetClassLoader() == class_loader)) - << PrettyClass(klass) << " " << klass << " " << klass->GetClassLoader() << " " - << PrettyClass(klass2) << " " << klass2 << " " << klass2->GetClassLoader(); + CHECK(!(kh.GetDescriptorAsStringPiece() == descriptor && + klass2->GetClassLoader() == class_loader)) + << PrettyClass(klass) << " " << klass << " " << klass->GetClassLoader() << " " + << PrettyClass(klass2) << " " << klass2 << " " << klass2->GetClassLoader(); } } return klass; @@ -2379,7 +2380,7 @@ void ClassLinker::LookupClasses(const char* descriptor, std::vectorfirst == hash; ++it) { mirror::Class* klass = it->second; kh.ChangeClass(klass); - if (strcmp(descriptor, kh.GetDescriptor()) == 0) { + if (kh.GetDescriptorAsStringPiece() == descriptor) { result.push_back(klass); } } @@ -2538,11 +2539,11 @@ bool ClassLinker::VerifyClassUsingOatFile(const DexFile& dex_file, mirror::Class CHECK(oat_file != NULL) << dex_file.GetLocation() << " " << PrettyClass(klass); const OatFile::OatDexFile* oat_dex_file = oat_file->GetOatDexFile(dex_file.GetLocation()); CHECK(oat_dex_file != NULL) << dex_file.GetLocation() << " " << PrettyClass(klass); - const char* descriptor = ClassHelper(klass).GetDescriptor(); uint16_t class_def_index = klass->GetDexClassDefIndex(); UniquePtr oat_class(oat_dex_file->GetOatClass(class_def_index)); CHECK(oat_class.get() != NULL) - << dex_file.GetLocation() << " " << PrettyClass(klass) << " " << descriptor; + << dex_file.GetLocation() << " " << PrettyClass(klass) << " " + << ClassHelper(klass).GetDescriptor(); oat_file_class_status = oat_class->GetStatus(); if (oat_file_class_status == mirror::Class::kStatusVerified || oat_file_class_status == mirror::Class::kStatusInitialized) { @@ -2581,7 +2582,8 @@ bool ClassLinker::VerifyClassUsingOatFile(const DexFile& dex_file, mirror::Class return false; } LOG(FATAL) << "Unexpected class status: " << oat_file_class_status - << " " << dex_file.GetLocation() << " " << PrettyClass(klass) << " " << descriptor; + << " " << dex_file.GetLocation() << " " << PrettyClass(klass) << " " + << ClassHelper(klass).GetDescriptor(); return false; } @@ -3727,10 +3729,10 @@ struct LinkFieldsComparator { // same basic group? then sort by string. fh_->ChangeField(field1); - StringPiece name1(fh_->GetName()); + const char* name1 = fh_->GetName(); fh_->ChangeField(field2); - StringPiece name2(fh_->GetName()); - return name1 < name2; + const char* name2 = fh_->GetName(); + return strcmp(name1, name2) < 0; } FieldHelper* fh_; @@ -3764,7 +3766,9 @@ bool ClassLinker::LinkFields(SirtRef& klass, bool is_static) { // minimizes disruption of C++ version such as Class and Method. std::deque grouped_and_sorted_fields; for (size_t i = 0; i < num_fields; i++) { - grouped_and_sorted_fields.push_back(fields->Get(i)); + mirror::ArtField* f = fields->Get(i); + CHECK(f != NULL); + grouped_and_sorted_fields.push_back(f); } FieldHelper fh(NULL, this); std::sort(grouped_and_sorted_fields.begin(), @@ -3831,7 +3835,7 @@ bool ClassLinker::LinkFields(SirtRef& klass, bool is_static) { // We lie to the GC about the java.lang.ref.Reference.referent field, so it doesn't scan it. if (!is_static && - StringPiece(ClassHelper(klass.get(), this).GetDescriptor()) == "Ljava/lang/ref/Reference;") { + (ClassHelper(klass.get(), this).GetDescriptorAsStringPiece() == "Ljava/lang/ref/Reference;")) { // We know there are no non-reference fields in the Reference classes, and we know // that 'referent' is alphabetically last, so this is easy... CHECK_EQ(num_reference_fields, num_fields); @@ -3840,39 +3844,39 @@ bool ClassLinker::LinkFields(SirtRef& klass, bool is_static) { --num_reference_fields; } -#ifndef NDEBUG - // Make sure that all reference fields appear before - // non-reference fields, and all double-wide fields are aligned. - bool seen_non_ref = false; - for (size_t i = 0; i < num_fields; i++) { - mirror::ArtField* field = fields->Get(i); - if (false) { // enable to debug field layout - LOG(INFO) << "LinkFields: " << (is_static ? "static" : "instance") - << " class=" << PrettyClass(klass.get()) - << " field=" << PrettyField(field) - << " offset=" << field->GetField32(MemberOffset(mirror::ArtField::OffsetOffset()), - false); - } - fh.ChangeField(field); - Primitive::Type type = fh.GetTypeAsPrimitiveType(); - bool is_primitive = type != Primitive::kPrimNot; - if (StringPiece(ClassHelper(klass.get(), this).GetDescriptor()) == "Ljava/lang/ref/Reference;" && - StringPiece(fh.GetName()) == "referent") { - is_primitive = true; // We lied above, so we have to expect a lie here. - } - if (is_primitive) { - if (!seen_non_ref) { - seen_non_ref = true; - DCHECK_EQ(num_reference_fields, i); + if (kIsDebugBuild) { + // Make sure that all reference fields appear before + // non-reference fields, and all double-wide fields are aligned. + bool seen_non_ref = false; + for (size_t i = 0; i < num_fields; i++) { + mirror::ArtField* field = fields->Get(i); + if (false) { // enable to debug field layout + LOG(INFO) << "LinkFields: " << (is_static ? "static" : "instance") + << " class=" << PrettyClass(klass.get()) + << " field=" << PrettyField(field) + << " offset=" << field->GetField32(MemberOffset(mirror::ArtField::OffsetOffset()), + false); + } + fh.ChangeField(field); + Primitive::Type type = fh.GetTypeAsPrimitiveType(); + bool is_primitive = type != Primitive::kPrimNot; + if (ClassHelper(klass.get(), this).GetDescriptorAsStringPiece() == "Ljava/lang/ref/Reference;" && + fh.GetNameAsStringPiece() == "referent") { + is_primitive = true; // We lied above, so we have to expect a lie here. + } + if (is_primitive) { + if (!seen_non_ref) { + seen_non_ref = true; + DCHECK_EQ(num_reference_fields, i); + } + } else { + DCHECK(!seen_non_ref); } - } else { - DCHECK(!seen_non_ref); + } + if (!seen_non_ref) { + DCHECK_EQ(num_fields, num_reference_fields); } } - if (!seen_non_ref) { - DCHECK_EQ(num_fields, num_reference_fields); - } -#endif size = field_offset.Uint32Value(); // Update klass if (is_static) { @@ -4175,9 +4179,9 @@ mirror::ArtField* ClassLinker::ResolveField(const DexFile& dex_file, } mirror::ArtField* ClassLinker::ResolveFieldJLS(const DexFile& dex_file, - uint32_t field_idx, - mirror::DexCache* dex_cache, - mirror::ClassLoader* class_loader) { + uint32_t field_idx, + mirror::DexCache* dex_cache, + mirror::ClassLoader* class_loader) { DCHECK(dex_cache != NULL); mirror::ArtField* resolved = dex_cache->GetResolvedField(field_idx); if (resolved != NULL) { @@ -4190,8 +4194,9 @@ mirror::ArtField* ClassLinker::ResolveFieldJLS(const DexFile& dex_file, return NULL; } - const char* name = dex_file.GetFieldName(field_id); - const char* type = dex_file.GetFieldTypeDescriptor(field_id); + StringPiece name(dex_file.StringDataAsStringPieceByIdx(field_id.name_idx_)); + StringPiece type(dex_file.StringDataAsStringPieceByIdx( + dex_file.GetTypeId(field_id.type_idx_).descriptor_idx_)); resolved = klass->FindField(name, type); if (resolved != NULL) { dex_cache->SetResolvedField(field_idx, resolved); diff --git a/runtime/debugger.cc b/runtime/debugger.cc index 88269e5578f..2eca7344578 100644 --- a/runtime/debugger.cc +++ b/runtime/debugger.cc @@ -891,7 +891,7 @@ JDWP::JdwpError Dbg::GetClassInfo(JDWP::RefTypeId class_id, JDWP::JdwpTypeTag* p } if (pDescriptor != NULL) { - *pDescriptor = ClassHelper(c).GetDescriptor(); + *pDescriptor = ClassHelper(c).GetDescriptorAsStringPiece().as_string(); } return JDWP::ERR_NONE; } @@ -928,13 +928,13 @@ JDWP::JdwpError Dbg::GetReferenceType(JDWP::ObjectId object_id, JDWP::ExpandBuf* return JDWP::ERR_NONE; } -JDWP::JdwpError Dbg::GetSignature(JDWP::RefTypeId class_id, std::string& signature) { +JDWP::JdwpError Dbg::GetSignature(JDWP::RefTypeId class_id, std::string* signature) { JDWP::JdwpError status; mirror::Class* c = DecodeClass(class_id, status); if (c == NULL) { return status; } - signature = ClassHelper(c).GetDescriptor(); + *signature = ClassHelper(c).GetDescriptorAsStringPiece().as_string(); return JDWP::ERR_NONE; } @@ -1065,8 +1065,8 @@ JDWP::JdwpError Dbg::SetArrayElements(JDWP::ObjectId array_id, int offset, int c LOG(WARNING) << __FUNCTION__ << " access out of bounds: offset=" << offset << "; count=" << count; return JDWP::ERR_INVALID_LENGTH; } - std::string descriptor(ClassHelper(dst->GetClass()).GetDescriptor()); - JDWP::JdwpTag tag = BasicTagFromDescriptor(descriptor.c_str() + 1); + const char* descriptor = ClassHelper(dst->GetClass()).GetDescriptor(); + JDWP::JdwpTag tag = BasicTagFromDescriptor(descriptor + 1); if (IsPrimitiveTag(tag)) { size_t width = GetTagWidth(tag); @@ -2287,7 +2287,8 @@ void Dbg::PostClassPrepare(mirror::Class* c) { // since the class may not yet be verified. int state = JDWP::CS_VERIFIED | JDWP::CS_PREPARED; JDWP::JdwpTypeTag tag = c->IsInterface() ? JDWP::TT_INTERFACE : JDWP::TT_CLASS; - gJdwpState->PostClassPrepare(tag, gRegistry->Add(c), ClassHelper(c).GetDescriptor(), state); + gJdwpState->PostClassPrepare(tag, gRegistry->Add(c), + ClassHelper(c).GetDescriptorAsStringPiece().as_string(), state); } void Dbg::UpdateDebugger(Thread* thread, mirror::Object* this_object, diff --git a/runtime/debugger.h b/runtime/debugger.h index d0fe445df17..8574a3308f2 100644 --- a/runtime/debugger.h +++ b/runtime/debugger.h @@ -149,7 +149,7 @@ class Dbg { SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); static JDWP::JdwpError GetReferenceType(JDWP::ObjectId object_id, JDWP::ExpandBuf* pReply) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - static JDWP::JdwpError GetSignature(JDWP::RefTypeId ref_type_id, std::string& signature) + static JDWP::JdwpError GetSignature(JDWP::RefTypeId ref_type_id, std::string* signature) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); static JDWP::JdwpError GetSourceFile(JDWP::RefTypeId ref_type_id, std::string& source_file) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); diff --git a/runtime/dex_file-inl.h b/runtime/dex_file-inl.h index dee80269d66..2ee9244bf23 100644 --- a/runtime/dex_file-inl.h +++ b/runtime/dex_file-inl.h @@ -18,6 +18,7 @@ #define ART_RUNTIME_DEX_FILE_INL_H_ #include "base/logging.h" +#include "base/stringpiece.h" #include "dex_file.h" #include "leb128.h" #include "utils.h" @@ -36,6 +37,16 @@ inline const char* DexFile::GetStringDataAndLength(const StringId& string_id, ui return reinterpret_cast(ptr); } +inline StringPiece DexFile::StringDataAsStringPieceByIdx(uint32_t idx) const { + if (idx == kDexNoIndex) { + return StringPiece(); + } + const StringId& string_id = GetStringId(idx); + uint32_t length; + const char* data = GetStringDataAndLength(string_id, &length); + return StringPiece(data, static_cast(length)); +} + inline const DexFile::TryItem* DexFile::GetTryItems(const CodeItem& code_item, uint32_t offset) { const uint16_t* insns_end_ = &code_item.insns_[code_item.insns_size_in_code_units_]; return reinterpret_cast diff --git a/runtime/dex_file.h b/runtime/dex_file.h index 7be5cb848f5..4534b41e92d 100644 --- a/runtime/dex_file.h +++ b/runtime/dex_file.h @@ -40,6 +40,7 @@ namespace mirror { class DexCache; } // namespace mirror class ClassLinker; +class StringPiece; class ZipArchive; // TODO: move all of the macro functionality into the DexCache class. @@ -432,6 +433,8 @@ class DexFile { return GetStringDataAndLength(string_id, unicode_length); } + StringPiece StringDataAsStringPieceByIdx(uint32_t idx) const; + const char* StringDataByIdx(uint32_t idx) const { uint32_t unicode_length; return StringDataAndLengthByIdx(idx, &unicode_length); diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc index ccd9aaca81b..2ad6117d756 100644 --- a/runtime/gc/heap.cc +++ b/runtime/gc/heap.cc @@ -557,7 +557,7 @@ static void MSpaceChunkCallback(void* start, void* end, size_t used_bytes, void* mirror::Object* Heap::AllocObject(Thread* self, mirror::Class* c, size_t byte_count) { DCHECK(c == NULL || (c->IsClassClass() && byte_count >= sizeof(mirror::Class)) || (c->IsVariableSize() || c->GetObjectSize() == byte_count) || - strlen(ClassHelper(c).GetDescriptor()) == 0); + ClassHelper(c).GetDescriptorAsStringPiece().length() == 0); DCHECK_GE(byte_count, sizeof(mirror::Object)); mirror::Object* obj = NULL; diff --git a/runtime/interpreter/interpreter.cc b/runtime/interpreter/interpreter.cc index f35cfa3bca4..fd92e06fb3a 100644 --- a/runtime/interpreter/interpreter.cc +++ b/runtime/interpreter/interpreter.cc @@ -82,7 +82,7 @@ static void UnstartedRuntimeJni(Thread* self, ArtMethod* method, } } -static void InterpreterJni(Thread* self, ArtMethod* method, StringPiece shorty, +static void InterpreterJni(Thread* self, ArtMethod* method, const StringPiece& shorty, Object* receiver, uint32_t* args, JValue* result) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { // TODO: The following enters JNI code using a typedef-ed function rather than the JNI compiler, diff --git a/runtime/jdwp/jdwp_handler.cc b/runtime/jdwp/jdwp_handler.cc index a2efc48c848..523d89278a5 100644 --- a/runtime/jdwp/jdwp_handler.cc +++ b/runtime/jdwp/jdwp_handler.cc @@ -47,7 +47,7 @@ std::string DescribeMethod(const MethodId& method_id) { std::string DescribeRefTypeId(const RefTypeId& ref_type_id) { std::string signature("unknown"); - Dbg::GetSignature(ref_type_id, signature); + Dbg::GetSignature(ref_type_id, &signature); return StringPrintf("%#llx (%s)", ref_type_id, signature.c_str()); } @@ -547,7 +547,7 @@ static JdwpError RT_Signature(JdwpState*, Request& request, ExpandBuf* pReply, b RefTypeId refTypeId = request.ReadRefTypeId(); std::string signature; - JdwpError status = Dbg::GetSignature(refTypeId, signature); + JdwpError status = Dbg::GetSignature(refTypeId, &signature); if (status != ERR_NONE) { return status; } diff --git a/runtime/mirror/class.cc b/runtime/mirror/class.cc index c128eded0a6..287e8b0959f 100644 --- a/runtime/mirror/class.cc +++ b/runtime/mirror/class.cc @@ -135,7 +135,7 @@ String* Class::ComputeName() { if (name != NULL) { return name; } - std::string descriptor(ClassHelper(this).GetDescriptor()); + std::string descriptor(ClassHelper(this).GetDescriptorAsStringPiece().as_string()); if ((descriptor[0] != 'L') && (descriptor[0] != '[')) { // The descriptor indicates that this is the class for // a primitive type; special-case the return value. @@ -294,8 +294,8 @@ bool Class::IsInSamePackage(const Class* that) const { return true; } // Compare the package part of the descriptor string. - return IsInSamePackage(ClassHelper(klass1).GetDescriptor(), - ClassHelper(klass2).GetDescriptor()); + return IsInSamePackage(ClassHelper(klass1).GetDescriptorAsStringPiece(), + ClassHelper(klass2).GetDescriptorAsStringPiece()); } bool Class::IsClassClass() const { @@ -367,7 +367,7 @@ ArtMethod* Class::FindDeclaredDirectMethod(const StringPiece& name, const String for (size_t i = 0; i < NumDirectMethods(); ++i) { ArtMethod* method = GetDirectMethod(i); mh.ChangeMethod(method); - if (name == mh.GetName() && signature == mh.GetSignature()) { + if (name == mh.GetNameAsStringPiece() && signature == mh.GetSignature()) { return method; } } @@ -412,7 +412,7 @@ ArtMethod* Class::FindDeclaredVirtualMethod(const StringPiece& name, for (size_t i = 0; i < NumVirtualMethods(); ++i) { ArtMethod* method = GetVirtualMethod(i); mh.ChangeMethod(method); - if (name == mh.GetName() && signature == mh.GetSignature()) { + if (name == mh.GetNameAsStringPiece() && signature == mh.GetSignature()) { return method; } } @@ -458,7 +458,7 @@ ArtField* Class::FindDeclaredInstanceField(const StringPiece& name, const String for (size_t i = 0; i < NumInstanceFields(); ++i) { ArtField* f = GetInstanceField(i); fh.ChangeField(f); - if (name == fh.GetName() && type == fh.GetTypeDescriptor()) { + if (name == fh.GetNameAsStringPiece() && type == fh.GetTypeDescriptorAsStringPiece()) { return f; } } @@ -507,7 +507,7 @@ ArtField* Class::FindDeclaredStaticField(const StringPiece& name, const StringPi for (size_t i = 0; i < NumStaticFields(); ++i) { ArtField* f = GetStaticField(i); fh.ChangeField(f); - if (name == fh.GetName() && type == fh.GetTypeDescriptor()) { + if (name == fh.GetNameAsStringPiece() && type == fh.GetTypeDescriptorAsStringPiece()) { return f; } } diff --git a/runtime/object_utils.h b/runtime/object_utils.h index 6ee3016179f..9e107a46d5f 100644 --- a/runtime/object_utils.h +++ b/runtime/object_utils.h @@ -111,6 +111,17 @@ class ClassHelper { } } + StringPiece GetDescriptorAsStringPiece() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + CHECK(klass_ != NULL); + if (UNLIKELY(klass_->IsArrayClass() || klass_->IsPrimitive() || klass_->IsProxyClass())) { + return StringPiece(GetDescriptor()); + } else { + const DexFile& dex_file = GetDexFile(); + const DexFile::TypeId& type_id = dex_file.GetTypeId(GetClassDef()->class_idx_); + return dex_file.StringDataAsStringPieceByIdx(type_id.descriptor_idx_); + } + } + const char* GetArrayDescriptor() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { std::string result("["); const mirror::Class* saved_klass = klass_; @@ -182,7 +193,7 @@ class ClassHelper { } const char* GetSourceFile() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - std::string descriptor(GetDescriptor()); + std::string descriptor(GetDescriptorAsStringPiece().as_string()); const DexFile& dex_file = GetDexFile(); const DexFile::ClassDef* dex_class_def = GetClassDef(); CHECK(dex_class_def != NULL); @@ -267,53 +278,77 @@ class FieldHelper { } field_ = new_f; } + const char* GetName() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { uint32_t field_index = field_->GetDexFieldIndex(); - if (!field_->GetDeclaringClass()->IsProxyClass()) { - const DexFile& dex_file = GetDexFile(); - return dex_file.GetFieldName(dex_file.GetFieldId(field_index)); - } else { + if (UNLIKELY(field_->GetDeclaringClass()->IsProxyClass())) { DCHECK(field_->IsStatic()); DCHECK_LT(field_index, 2U); return field_index == 0 ? "interfaces" : "throws"; } + const DexFile& dex_file = GetDexFile(); + return dex_file.GetFieldName(dex_file.GetFieldId(field_index)); } + + StringPiece GetNameAsStringPiece() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + uint32_t field_index = field_->GetDexFieldIndex(); + if (UNLIKELY(field_->GetDeclaringClass()->IsProxyClass())) { + return StringPiece(GetName()); + } + const DexFile& dex_file = GetDexFile(); + const DexFile::FieldId& field_id = dex_file.GetFieldId(field_index); + return dex_file.StringDataAsStringPieceByIdx(field_id.name_idx_); + } + mirror::Class* GetType(bool resolve = true) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { uint32_t field_index = field_->GetDexFieldIndex(); - if (!field_->GetDeclaringClass()->IsProxyClass()) { - const DexFile& dex_file = GetDexFile(); - const DexFile::FieldId& field_id = dex_file.GetFieldId(field_index); - mirror::Class* type = GetDexCache()->GetResolvedType(field_id.type_idx_); - if (resolve && (type == NULL)) { - type = GetClassLinker()->ResolveType(field_id.type_idx_, field_); - CHECK(type != NULL || Thread::Current()->IsExceptionPending()); - } - return type; - } else { + if (UNLIKELY(field_->GetDeclaringClass()->IsProxyClass())) { return GetClassLinker()->FindSystemClass(GetTypeDescriptor()); } + const DexFile& dex_file = GetDexFile(); + const DexFile::FieldId& field_id = dex_file.GetFieldId(field_index); + mirror::Class* type = GetDexCache()->GetResolvedType(field_id.type_idx_); + if (resolve && (type == NULL)) { + type = GetClassLinker()->ResolveType(field_id.type_idx_, field_); + CHECK(type != NULL || Thread::Current()->IsExceptionPending()); + } + return type; } + const char* GetTypeDescriptor() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { uint32_t field_index = field_->GetDexFieldIndex(); - if (!field_->GetDeclaringClass()->IsProxyClass()) { - const DexFile& dex_file = GetDexFile(); - const DexFile::FieldId& field_id = dex_file.GetFieldId(field_index); - return dex_file.GetFieldTypeDescriptor(field_id); - } else { + if (UNLIKELY(field_->GetDeclaringClass()->IsProxyClass())) { DCHECK(field_->IsStatic()); DCHECK_LT(field_index, 2U); // 0 == Class[] interfaces; 1 == Class[][] throws; return field_index == 0 ? "[Ljava/lang/Class;" : "[[Ljava/lang/Class;"; } + const DexFile& dex_file = GetDexFile(); + const DexFile::FieldId& field_id = dex_file.GetFieldId(field_index); + return dex_file.GetFieldTypeDescriptor(field_id); + } + + StringPiece GetTypeDescriptorAsStringPiece() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + uint32_t field_index = field_->GetDexFieldIndex(); + if (UNLIKELY(field_->GetDeclaringClass()->IsProxyClass())) { + return StringPiece(GetTypeDescriptor()); + } + const DexFile& dex_file = GetDexFile(); + const DexFile::FieldId& field_id = dex_file.GetFieldId(field_index); + const DexFile::TypeId& type_id = dex_file.GetTypeId(field_id.type_idx_); + return dex_file.StringDataAsStringPieceByIdx(type_id.descriptor_idx_); } + Primitive::Type GetTypeAsPrimitiveType() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { return Primitive::GetType(GetTypeDescriptor()[0]); } + bool IsPrimitiveType() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { Primitive::Type type = GetTypeAsPrimitiveType(); return type != Primitive::kPrimNot; } + size_t FieldSize() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { Primitive::Type type = GetTypeAsPrimitiveType(); return Primitive::FieldSize(type); @@ -324,18 +359,17 @@ class FieldHelper { const char* GetDeclaringClassDescriptor() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { uint32_t field_index = field_->GetDexFieldIndex(); - if (!field_->GetDeclaringClass()->IsProxyClass()) { - const DexFile& dex_file = GetDexFile(); - const DexFile::FieldId& field_id = dex_file.GetFieldId(field_index); - return dex_file.GetFieldDeclaringClassDescriptor(field_id); - } else { + if (UNLIKELY(field_->GetDeclaringClass()->IsProxyClass())) { DCHECK(field_->IsStatic()); DCHECK_LT(field_index, 2U); // 0 == Class[] interfaces; 1 == Class[][] throws; ClassHelper kh(field_->GetDeclaringClass()); - declaring_class_descriptor_ = kh.GetDescriptor(); + declaring_class_descriptor_ = kh.GetDescriptorAsStringPiece().as_string(); return declaring_class_descriptor_.c_str(); } + const DexFile& dex_file = GetDexFile(); + const DexFile::FieldId& field_id = dex_file.GetFieldId(field_index); + return dex_file.GetFieldDeclaringClassDescriptor(field_id); } private: @@ -417,7 +451,7 @@ class MethodHelper { const char* GetName() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { const DexFile& dex_file = GetDexFile(); uint32_t dex_method_idx = method_->GetDexMethodIndex(); - if (dex_method_idx != DexFile::kDexNoIndex) { + if (LIKELY(dex_method_idx != DexFile::kDexNoIndex)) { return dex_file.GetMethodName(dex_file.GetMethodId(dex_method_idx)); } else { Runtime* runtime = Runtime::Current(); @@ -435,6 +469,16 @@ class MethodHelper { } } + StringPiece GetNameAsStringPiece() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + const DexFile& dex_file = GetDexFile(); + uint32_t dex_method_idx = method_->GetDexMethodIndex(); + if (UNLIKELY(dex_method_idx == DexFile::kDexNoIndex)) { + return StringPiece(GetName()); + } + const DexFile::MethodId& method_id = dex_file.GetMethodId(dex_method_idx); + return dex_file.StringDataAsStringPieceByIdx(method_id.name_idx_); + } + mirror::String* GetNameAsString() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { const DexFile& dex_file = GetDexFile(); uint32_t dex_method_idx = method_->GetDexMethodIndex(); @@ -508,11 +552,22 @@ class MethodHelper { const char* GetDeclaringClassDescriptor() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { const DexFile& dex_file = GetDexFile(); uint32_t dex_method_idx = method_->GetDexMethodIndex(); - if (dex_method_idx != DexFile::kDexNoIndex) { - return dex_file.GetMethodDeclaringClassDescriptor(dex_file.GetMethodId(dex_method_idx)); - } else { + if (UNLIKELY(dex_method_idx == DexFile::kDexNoIndex)) { return ""; } + return dex_file.GetMethodDeclaringClassDescriptor(dex_file.GetMethodId(dex_method_idx)); + } + + StringPiece GetDeclaringClassDescriptorAsStringPiece() + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + const DexFile& dex_file = GetDexFile(); + uint32_t dex_method_idx = method_->GetDexMethodIndex(); + if (UNLIKELY(dex_method_idx == DexFile::kDexNoIndex)) { + return StringPiece(""); + } + const DexFile::MethodId& mid = dex_file.GetMethodId(dex_method_idx); + const DexFile::TypeId& type_id = dex_file.GetTypeId(mid.class_idx_); + return dex_file.StringDataAsStringPieceByIdx(type_id.descriptor_idx_); } const char* GetDeclaringClassSourceFile() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { @@ -536,7 +591,7 @@ class MethodHelper { } bool IsClassInitializer() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - return IsStatic() && StringPiece(GetName()) == ""; + return IsStatic() && GetNameAsStringPiece() == ""; } size_t NumArgs() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { @@ -569,16 +624,56 @@ class MethodHelper { bool HasSameNameAndSignature(MethodHelper* other) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + const DexFile& dex_file = GetDexFile(); + const DexFile::MethodId& mid = dex_file.GetMethodId(method_->GetDexMethodIndex()); if (GetDexCache() == other->GetDexCache()) { - const DexFile& dex_file = GetDexFile(); - const DexFile::MethodId& mid = dex_file.GetMethodId(method_->GetDexMethodIndex()); const DexFile::MethodId& other_mid = dex_file.GetMethodId(other->method_->GetDexMethodIndex()); return mid.name_idx_ == other_mid.name_idx_ && mid.proto_idx_ == other_mid.proto_idx_; } - StringPiece name(GetName()); - StringPiece other_name(other->GetName()); - return name == other_name && GetSignature() == other->GetSignature(); + const DexFile& other_dex_file = other->GetDexFile(); + const DexFile::MethodId& other_mid = + other_dex_file.GetMethodId(other->method_->GetDexMethodIndex()); + if (dex_file.StringDataAsStringPieceByIdx(mid.name_idx_) != + other_dex_file.StringDataAsStringPieceByIdx(other_mid.name_idx_)) { + return false; // Name mismatch. + } + const DexFile::ProtoId& proto_id = dex_file.GetMethodPrototype(mid); + const DexFile::ProtoId& other_proto_id = other_dex_file.GetMethodPrototype(other_mid); + if (dex_file.StringDataAsStringPieceByIdx(proto_id.shorty_idx_) != + other_dex_file.StringDataAsStringPieceByIdx(other_proto_id.shorty_idx_)) { + return false; // Shorty mismatch. + } + const DexFile::TypeId& return_type_id = dex_file.GetTypeId(proto_id.return_type_idx_); + const DexFile::TypeId& other_return_type_id = + other_dex_file.GetTypeId(other_proto_id.return_type_idx_); + if (dex_file.StringDataAsStringPieceByIdx(return_type_id.descriptor_idx_) != + other_dex_file.StringDataAsStringPieceByIdx(other_return_type_id.descriptor_idx_)) { + return false; // Return type mismatch. + } + const DexFile::TypeList* params = dex_file.GetProtoParameters(proto_id); + const DexFile::TypeList* other_params = other_dex_file.GetProtoParameters(other_proto_id); + if (params == nullptr) { + return other_params == nullptr; // Check both lists are empty. + } + if (other_params == nullptr) { + return false; // Parameter list size mismatch. + } + uint32_t params_size = params->Size(); + uint32_t other_params_size = other_params->Size(); + if (params_size != other_params_size) { + return false; // Parameter list size mismatch. + } + for (uint32_t i = 0; i < params_size; ++i) { + const DexFile::TypeId& param_id = dex_file.GetTypeId(params->GetTypeItem(i).type_idx_); + const DexFile::TypeId& other_param_id = + other_dex_file.GetTypeId(other_params->GetTypeItem(i).type_idx_); + if (dex_file.StringDataAsStringPieceByIdx(param_id.descriptor_idx_) != + other_dex_file.StringDataAsStringPieceByIdx(other_param_id.descriptor_idx_)) { + return false; // Parameter type mismatch. + } + } + return true; } const DexFile::CodeItem* GetCodeItem() diff --git a/runtime/reflection.cc b/runtime/reflection.cc index 3e58b4bd94d..4ff73498336 100644 --- a/runtime/reflection.cc +++ b/runtime/reflection.cc @@ -323,7 +323,7 @@ static bool UnboxPrimitive(const ThrowLocation* throw_location, mirror::Object* } JValue boxed_value; - std::string src_descriptor(ClassHelper(o->GetClass()).GetDescriptor()); + const StringPiece src_descriptor(ClassHelper(o->GetClass()).GetDescriptorAsStringPiece()); mirror::Class* src_class = NULL; ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); mirror::ArtField* primitive_field = o->GetClass()->GetIFields()->Get(0); @@ -356,7 +356,7 @@ static bool UnboxPrimitive(const ThrowLocation* throw_location, mirror::Object* StringPrintf("%s has type %s, got %s", UnboxingFailureKind(m, index, f).c_str(), PrettyDescriptor(dst_class).c_str(), - PrettyDescriptor(src_descriptor.c_str()).c_str()).c_str()); + PrettyDescriptor(src_descriptor.data()).c_str()).c_str()); return false; } diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc index 924a1bb3771..4442ee13d90 100644 --- a/runtime/verifier/method_verifier.cc +++ b/runtime/verifier/method_verifier.cc @@ -80,7 +80,7 @@ MethodVerifier::FailureKind MethodVerifier::VerifyClass(const mirror::Class* kla return kNoFailure; } mirror::Class* super = klass->GetSuperClass(); - if (super == NULL && StringPiece(ClassHelper(klass).GetDescriptor()) != "Ljava/lang/Object;") { + if (super == NULL && ClassHelper(klass).GetDescriptorAsStringPiece() != "Ljava/lang/Object;") { *error = "Verifier rejected class "; *error += PrettyDescriptor(klass); *error += " that has no super class"; @@ -293,6 +293,7 @@ MethodVerifier::MethodVerifier(const DexFile* dex_file, mirror::DexCache* dex_ca dex_method_idx_(dex_method_idx), mirror_method_(method), method_access_flags_(method_access_flags), + return_type_(nullptr), dex_file_(dex_file), dex_cache_(dex_cache), class_loader_(class_loader), @@ -300,7 +301,7 @@ MethodVerifier::MethodVerifier(const DexFile* dex_file, mirror::DexCache* dex_ca code_item_(code_item), declaring_class_(NULL), interesting_dex_pc_(-1), - monitor_enter_dex_pcs_(NULL), + monitor_enter_dex_pcs_(nullptr), have_pending_hard_failure_(false), have_pending_runtime_throw_failure_(false), new_instance_count_(0), @@ -309,7 +310,7 @@ MethodVerifier::MethodVerifier(const DexFile* dex_file, mirror::DexCache* dex_ca allow_soft_failures_(allow_soft_failures), has_check_casts_(false), has_virtual_or_interface_invokes_(false) { - DCHECK(class_def != NULL); + DCHECK(class_def != nullptr); } void MethodVerifier::FindLocksAtDexPc(mirror::ArtMethod* m, uint32_t dex_pc, @@ -2131,20 +2132,30 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { inst->Opcode() == Instruction::INVOKE_SUPER_RANGE); mirror::ArtMethod* called_method = VerifyInvocationArgs(inst, METHOD_VIRTUAL, is_range, is_super); - const char* descriptor; - if (called_method == NULL) { + const RegType* return_type = nullptr; + if (called_method != nullptr) { + MethodHelper mh(called_method); + mirror::Class* return_type_class = mh.GetReturnType(); + if (return_type_class != nullptr) { + return_type = ®_types_.FromClass(mh.GetReturnTypeDescriptor(), return_type_class, + return_type_class->CannotBeAssignedFromOtherTypes()); + } else { + Thread* self = Thread::Current(); + DCHECK(self->IsExceptionPending()); + self->ClearException(); + } + } + if (return_type == nullptr) { uint32_t method_idx = (is_range) ? inst->VRegB_3rc() : inst->VRegB_35c(); const DexFile::MethodId& method_id = dex_file_->GetMethodId(method_idx); uint32_t return_type_idx = dex_file_->GetProtoId(method_id.proto_idx_).return_type_idx_; - descriptor = dex_file_->StringByTypeIdx(return_type_idx); - } else { - descriptor = MethodHelper(called_method).GetReturnTypeDescriptor(); + const char* descriptor = dex_file_->StringByTypeIdx(return_type_idx); + return_type = ®_types_.FromDescriptor(class_loader_, descriptor, false); } - const RegType& return_type = reg_types_.FromDescriptor(class_loader_, descriptor, false); - if (!return_type.IsLowHalf()) { - work_line_->SetResultRegisterType(return_type); + if (!return_type->IsLowHalf()) { + work_line_->SetResultRegisterType(*return_type); } else { - work_line_->SetResultRegisterTypeWide(return_type, return_type.HighHalf(®_types_)); + work_line_->SetResultRegisterTypeWide(*return_type, return_type->HighHalf(®_types_)); } just_set_result = true; break; @@ -2159,7 +2170,7 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { if (called_method == NULL) { uint32_t method_idx = (is_range) ? inst->VRegB_3rc() : inst->VRegB_35c(); const DexFile::MethodId& method_id = dex_file_->GetMethodId(method_idx); - is_constructor = StringPiece(dex_file_->GetMethodName(method_id)) == ""; + is_constructor = dex_file_->StringDataAsStringPieceByIdx(method_id.name_idx_) == ""; uint32_t return_type_idx = dex_file_->GetProtoId(method_id.proto_idx_).return_type_idx_; return_type_descriptor = dex_file_->StringByTypeIdx(return_type_idx); } else { @@ -3497,22 +3508,26 @@ void MethodVerifier::VerifyISGet(const Instruction* inst, const RegType& insn_ty const RegType& object_type = work_line_->GetRegisterType(inst->VRegB_22c()); field = GetInstanceField(object_type, field_idx); } - const char* descriptor; - mirror::ClassLoader* loader; + const RegType* field_type = nullptr; if (field != NULL) { - descriptor = FieldHelper(field).GetTypeDescriptor(); - loader = field->GetDeclaringClass()->GetClassLoader(); - } else { + FieldHelper fh(field); + mirror::Class* field_type_class = fh.GetType(false); + if (field_type_class != nullptr) { + field_type = ®_types_.FromClass(fh.GetTypeDescriptor(), field_type_class, + field_type_class->CannotBeAssignedFromOtherTypes()); + } + } + if (field_type == nullptr) { const DexFile::FieldId& field_id = dex_file_->GetFieldId(field_idx); - descriptor = dex_file_->GetFieldTypeDescriptor(field_id); - loader = class_loader_; + const char* descriptor = dex_file_->GetFieldTypeDescriptor(field_id); + mirror::ClassLoader* loader = class_loader_; + field_type = ®_types_.FromDescriptor(loader, descriptor, false); } - const RegType& field_type = reg_types_.FromDescriptor(loader, descriptor, false); const uint32_t vregA = (is_static) ? inst->VRegA_21c() : inst->VRegA_22c(); if (is_primitive) { - if (field_type.Equals(insn_type) || - (field_type.IsFloat() && insn_type.IsInteger()) || - (field_type.IsDouble() && insn_type.IsLong())) { + if (field_type->Equals(insn_type) || + (field_type->IsFloat() && insn_type.IsInteger()) || + (field_type->IsDouble() && insn_type.IsLong())) { // expected that read is of the correct primitive type or that int reads are reading // floats or long reads are reading doubles } else { @@ -3525,7 +3540,7 @@ void MethodVerifier::VerifyISGet(const Instruction* inst, const RegType& insn_ty return; } } else { - if (!insn_type.IsAssignableFrom(field_type)) { + if (!insn_type.IsAssignableFrom(*field_type)) { Fail(VERIFY_ERROR_BAD_CLASS_SOFT) << "expected field " << PrettyField(field) << " to be compatible with type '" << insn_type << "' but found type '" << field_type @@ -3534,10 +3549,10 @@ void MethodVerifier::VerifyISGet(const Instruction* inst, const RegType& insn_ty return; } } - if (!field_type.IsLowHalf()) { - work_line_->SetRegisterType(vregA, field_type); + if (!field_type->IsLowHalf()) { + work_line_->SetRegisterType(vregA, *field_type); } else { - work_line_->SetRegisterTypeWide(vregA, field_type, field_type.HighHalf(®_types_)); + work_line_->SetRegisterTypeWide(vregA, *field_type, field_type->HighHalf(®_types_)); } } @@ -3551,36 +3566,38 @@ void MethodVerifier::VerifyISPut(const Instruction* inst, const RegType& insn_ty const RegType& object_type = work_line_->GetRegisterType(inst->VRegB_22c()); field = GetInstanceField(object_type, field_idx); } - const char* descriptor; - mirror::ClassLoader* loader; - if (field != NULL) { - descriptor = FieldHelper(field).GetTypeDescriptor(); - loader = field->GetDeclaringClass()->GetClassLoader(); - } else { - const DexFile::FieldId& field_id = dex_file_->GetFieldId(field_idx); - descriptor = dex_file_->GetFieldTypeDescriptor(field_id); - loader = class_loader_; - } - const RegType& field_type = reg_types_.FromDescriptor(loader, descriptor, false); + const RegType* field_type = nullptr; if (field != NULL) { if (field->IsFinal() && field->GetDeclaringClass() != GetDeclaringClass().GetClass()) { Fail(VERIFY_ERROR_ACCESS_FIELD) << "cannot modify final field " << PrettyField(field) << " from other class " << GetDeclaringClass(); return; } + FieldHelper fh(field); + mirror::Class* field_type_class = fh.GetType(false); + if (field_type_class != nullptr) { + field_type = ®_types_.FromClass(fh.GetTypeDescriptor(), field_type_class, + field_type_class->CannotBeAssignedFromOtherTypes()); + } + } + if (field_type == nullptr) { + const DexFile::FieldId& field_id = dex_file_->GetFieldId(field_idx); + const char* descriptor = dex_file_->GetFieldTypeDescriptor(field_id); + mirror::ClassLoader* loader = class_loader_; + field_type = ®_types_.FromDescriptor(loader, descriptor, false); } const uint32_t vregA = (is_static) ? inst->VRegA_21c() : inst->VRegA_22c(); if (is_primitive) { - VerifyPrimitivePut(field_type, insn_type, vregA); + VerifyPrimitivePut(*field_type, insn_type, vregA); } else { - if (!insn_type.IsAssignableFrom(field_type)) { + if (!insn_type.IsAssignableFrom(*field_type)) { Fail(VERIFY_ERROR_BAD_CLASS_SOFT) << "expected field " << PrettyField(field) << " to be compatible with type '" << insn_type << "' but found type '" << field_type << "' in put-object"; return; } - work_line_->VerifyRegisterType(vregA, field_type); + work_line_->VerifyRegisterType(vregA, *field_type); } } @@ -3648,14 +3665,21 @@ void MethodVerifier::VerifyIGetQuick(const Instruction* inst, const RegType& ins Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Cannot infer field from " << inst->Name(); return; } - const char* descriptor = FieldHelper(field).GetTypeDescriptor(); - mirror::ClassLoader* loader = field->GetDeclaringClass()->GetClassLoader(); - const RegType& field_type = reg_types_.FromDescriptor(loader, descriptor, false); + FieldHelper fh(field); + mirror::Class* field_type_class = fh.GetType(false); + const RegType* field_type; + if (field_type_class != nullptr) { + field_type = ®_types_.FromClass(fh.GetTypeDescriptor(), field_type_class, + field_type_class->CannotBeAssignedFromOtherTypes()); + } else { + field_type = ®_types_.FromDescriptor(field->GetDeclaringClass()->GetClassLoader(), + fh.GetTypeDescriptor(), false); + } const uint32_t vregA = inst->VRegA_22c(); if (is_primitive) { - if (field_type.Equals(insn_type) || - (field_type.IsFloat() && insn_type.IsIntegralTypes()) || - (field_type.IsDouble() && insn_type.IsLongTypes())) { + if (field_type->Equals(insn_type) || + (field_type->IsFloat() && insn_type.IsIntegralTypes()) || + (field_type->IsDouble() && insn_type.IsLongTypes())) { // expected that read is of the correct primitive type or that int reads are reading // floats or long reads are reading doubles } else { @@ -3668,7 +3692,7 @@ void MethodVerifier::VerifyIGetQuick(const Instruction* inst, const RegType& ins return; } } else { - if (!insn_type.IsAssignableFrom(field_type)) { + if (!insn_type.IsAssignableFrom(*field_type)) { Fail(VERIFY_ERROR_BAD_CLASS_SOFT) << "expected field " << PrettyField(field) << " to be compatible with type '" << insn_type << "' but found type '" << field_type @@ -3677,10 +3701,10 @@ void MethodVerifier::VerifyIGetQuick(const Instruction* inst, const RegType& ins return; } } - if (!field_type.IsLowHalf()) { - work_line_->SetRegisterType(vregA, field_type); + if (!field_type->IsLowHalf()) { + work_line_->SetRegisterType(vregA, *field_type); } else { - work_line_->SetRegisterTypeWide(vregA, field_type, field_type.HighHalf(®_types_)); + work_line_->SetRegisterTypeWide(vregA, *field_type, field_type->HighHalf(®_types_)); } } @@ -3822,11 +3846,28 @@ InstructionFlags* MethodVerifier::CurrentInsnFlags() { } const RegType& MethodVerifier::GetMethodReturnType() { - const DexFile::MethodId& method_id = dex_file_->GetMethodId(dex_method_idx_); - const DexFile::ProtoId& proto_id = dex_file_->GetMethodPrototype(method_id); - uint16_t return_type_idx = proto_id.return_type_idx_; - const char* descriptor = dex_file_->GetTypeDescriptor(dex_file_->GetTypeId(return_type_idx)); - return reg_types_.FromDescriptor(class_loader_, descriptor, false); + if (return_type_ == nullptr) { + if (mirror_method_ != NULL) { + MethodHelper mh(mirror_method_); + mirror::Class* return_type_class = mh.GetReturnType(); + if (return_type_class != nullptr) { + return_type_ =®_types_.FromClass(mh.GetReturnTypeDescriptor(), return_type_class, + return_type_class->CannotBeAssignedFromOtherTypes()); + } else { + Thread* self = Thread::Current(); + DCHECK(self->IsExceptionPending()); + self->ClearException(); + } + } + if (return_type_ == nullptr) { + const DexFile::MethodId& method_id = dex_file_->GetMethodId(dex_method_idx_); + const DexFile::ProtoId& proto_id = dex_file_->GetMethodPrototype(method_id); + uint16_t return_type_idx = proto_id.return_type_idx_; + const char* descriptor = dex_file_->GetTypeDescriptor(dex_file_->GetTypeId(return_type_idx)); + return_type_ = ®_types_.FromDescriptor(class_loader_, descriptor, false); + } + } + return *return_type_; } const RegType& MethodVerifier::GetDeclaringClass() { diff --git a/runtime/verifier/method_verifier.h b/runtime/verifier/method_verifier.h index 073a2f76be1..f1437517e90 100644 --- a/runtime/verifier/method_verifier.h +++ b/runtime/verifier/method_verifier.h @@ -688,6 +688,7 @@ class MethodVerifier { // Its object representation if known. mirror::ArtMethod* mirror_method_ GUARDED_BY(Locks::mutator_lock_); const uint32_t method_access_flags_; // Method's access flags. + const RegType* return_type_; // Lazily computed return type of the method. const DexFile* const dex_file_; // The dex file containing the method. // The dex_cache for the declaring class of the method. mirror::DexCache* dex_cache_ GUARDED_BY(Locks::mutator_lock_); diff --git a/runtime/verifier/reg_type.cc b/runtime/verifier/reg_type.cc index 857acb8743a..50d1583bbba 100644 --- a/runtime/verifier/reg_type.cc +++ b/runtime/verifier/reg_type.cc @@ -761,11 +761,6 @@ bool RegType::IsStrictlyAssignableFrom(const RegType& src) const { return AssignableFrom(*this, src, true); } -int32_t ConstantType::ConstantValue() const { - DCHECK(IsConstantTypes()); - return constant_; -} - int32_t ConstantType::ConstantValueLo() const { DCHECK(IsConstantLo()); return constant_; diff --git a/runtime/verifier/reg_type.h b/runtime/verifier/reg_type.h index 865ba20d44c..f3717330eb8 100644 --- a/runtime/verifier/reg_type.h +++ b/runtime/verifier/reg_type.h @@ -574,9 +574,12 @@ class ConstantType : public RegType { // If this is a 32-bit constant, what is the value? This value may be imprecise in which case // the value represents part of the integer range of values that may be held in the register. - virtual int32_t ConstantValue() const; - virtual int32_t ConstantValueLo() const; - virtual int32_t ConstantValueHi() const; + int32_t ConstantValue() const { + DCHECK(IsConstantTypes()); + return constant_; + } + int32_t ConstantValueLo() const; + int32_t ConstantValueHi() const; bool IsZero() const { return IsPreciseConstant() && ConstantValue() == 0; diff --git a/runtime/verifier/reg_type_cache.cc b/runtime/verifier/reg_type_cache.cc index ce465a415d7..fd7030011d0 100644 --- a/runtime/verifier/reg_type_cache.cc +++ b/runtime/verifier/reg_type_cache.cc @@ -210,6 +210,7 @@ const RegType& RegTypeCache::From(mirror::ClassLoader* loader, const char* descr } const RegType& RegTypeCache::FromClass(const char* descriptor, mirror::Class* klass, bool precise) { + DCHECK(klass != nullptr); if (klass->IsPrimitive()) { // Note: precise isn't used for primitive classes. A char is assignable to an int. All // primitive classes are final. From d0fbd85a82a266c21d6b72c61d6dc098ec362de7 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Tue, 24 Sep 2013 18:17:04 -0700 Subject: [PATCH 0053/2402] Reduce memory allocation in verifier. Remove the use of a map PcToRegisterLineTable for efficiency (matches Dalvik). Place the register line register values inside the RegisterLine, saves a secondary allocation and indirection. Avoid the use of a deque in RegisterLine to avoid an allocation. Simplify the SirtRef destructor in non-debug builds. Saves >100ms from the two threaded compile time of ThinkFree on host. Change-Id: I2dacba61dbaf284ca02d4c194413e1da221dcb76 --- runtime/sirt_ref.h | 3 ++- runtime/verifier/method_verifier.cc | 22 ++++++++++++++------ runtime/verifier/method_verifier.h | 18 ++++++---------- runtime/verifier/register_line.cc | 3 +-- runtime/verifier/register_line.h | 32 ++++++++++++++++------------- 5 files changed, 43 insertions(+), 35 deletions(-) diff --git a/runtime/sirt_ref.h b/runtime/sirt_ref.h index 25d6fb3c11b..a1f8a6693f7 100644 --- a/runtime/sirt_ref.h +++ b/runtime/sirt_ref.h @@ -30,7 +30,8 @@ class SirtRef { self_->PushSirt(&sirt_); } ~SirtRef() { - CHECK_EQ(self_->PopSirt(), &sirt_); + StackIndirectReferenceTable* top_sirt = self_->PopSirt(); + DCHECK_EQ(top_sirt, &sirt_); } T& operator*() const { return *get(); } diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc index 4442ee13d90..6b13517fdc8 100644 --- a/runtime/verifier/method_verifier.cc +++ b/runtime/verifier/method_verifier.cc @@ -51,7 +51,8 @@ void PcToRegisterLineTable::Init(RegisterTrackingMode mode, InstructionFlags* fl uint32_t insns_size, uint16_t registers_size, MethodVerifier* verifier) { DCHECK_GT(insns_size, 0U); - + register_lines_.reset(new RegisterLine*[insns_size]()); + size_ = insns_size; for (uint32_t i = 0; i < insns_size; i++) { bool interesting = false; switch (mode) { @@ -68,7 +69,16 @@ void PcToRegisterLineTable::Init(RegisterTrackingMode mode, InstructionFlags* fl break; } if (interesting) { - pc_to_register_line_.Put(i, new RegisterLine(registers_size, verifier)); + register_lines_[i] = RegisterLine::Create(registers_size, verifier); + } + } +} + +PcToRegisterLineTable::~PcToRegisterLineTable() { + for (size_t i = 0; i < size_; i++) { + delete register_lines_[i]; + if (kIsDebugBuild) { + register_lines_[i] = nullptr; } } } @@ -1035,8 +1045,8 @@ bool MethodVerifier::VerifyCodeFlow() { this); - work_line_.reset(new RegisterLine(registers_size, this)); - saved_line_.reset(new RegisterLine(registers_size, this)); + work_line_.reset(RegisterLine::Create(registers_size, this)); + saved_line_.reset(RegisterLine::Create(registers_size, this)); /* Initialize register types of method arguments. */ if (!SetTypesFromSignature()) { @@ -1936,7 +1946,7 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { if (!cast_type.IsUnresolvedTypes() && !orig_type.IsUnresolvedTypes() && !cast_type.GetClass()->IsInterface() && !cast_type.IsAssignableFrom(orig_type)) { - RegisterLine* update_line = new RegisterLine(code_item_->registers_size_, this); + RegisterLine* update_line = RegisterLine::Create(code_item_->registers_size_, this); if (inst->Opcode() == Instruction::IF_EQZ) { fallthrough_line.reset(update_line); } else { @@ -3818,7 +3828,7 @@ bool MethodVerifier::UpdateRegisters(uint32_t next_insn, const RegisterLine* mer } } else { UniquePtr copy(gDebugVerify ? - new RegisterLine(target_line->NumRegs(), this) : + RegisterLine::Create(target_line->NumRegs(), this) : NULL); if (gDebugVerify) { copy->CopyFromLine(target_line); diff --git a/runtime/verifier/method_verifier.h b/runtime/verifier/method_verifier.h index f1437517e90..7f337419a4d 100644 --- a/runtime/verifier/method_verifier.h +++ b/runtime/verifier/method_verifier.h @@ -110,10 +110,8 @@ enum RegisterTrackingMode { // execution of that instruction. class PcToRegisterLineTable { public: - PcToRegisterLineTable() {} - ~PcToRegisterLineTable() { - STLDeleteValues(&pc_to_register_line_); - } + PcToRegisterLineTable() : size_(0) {} + ~PcToRegisterLineTable(); // Initialize the RegisterTable. Every instruction address can have a different set of information // about what's in which register, but for verification purposes we only need to store it at @@ -122,17 +120,13 @@ class PcToRegisterLineTable { uint16_t registers_size, MethodVerifier* verifier); RegisterLine* GetLine(size_t idx) { - auto result = pc_to_register_line_.find(idx); - if (result == pc_to_register_line_.end()) { - return NULL; - } else { - return result->second; - } + DCHECK_LT(idx, size_); + return register_lines_[idx]; } private: - typedef SafeMap Table; - Table pc_to_register_line_; + UniquePtr register_lines_; + size_t size_; }; // The verifier diff --git a/runtime/verifier/register_line.cc b/runtime/verifier/register_line.cc index a615cc1273e..1a41657264b 100644 --- a/runtime/verifier/register_line.cc +++ b/runtime/verifier/register_line.cc @@ -456,8 +456,7 @@ bool RegisterLine::VerifyMonitorStackEmpty() const { bool RegisterLine::MergeRegisters(const RegisterLine* incoming_line) { bool changed = false; - CHECK(NULL != incoming_line); - CHECK(NULL != line_.get()); + DCHECK(incoming_line != nullptr); for (size_t idx = 0; idx < num_regs_; idx++) { if (line_[idx] != incoming_line->line_[idx]) { const RegType& incoming_reg_type = incoming_line->GetRegisterType(idx); diff --git a/runtime/verifier/register_line.h b/runtime/verifier/register_line.h index f19dccac170..8b2dadb119d 100644 --- a/runtime/verifier/register_line.h +++ b/runtime/verifier/register_line.h @@ -17,7 +17,6 @@ #ifndef ART_RUNTIME_VERIFIER_REGISTER_LINE_H_ #define ART_RUNTIME_VERIFIER_REGISTER_LINE_H_ -#include #include #include "dex_instruction.h" @@ -51,12 +50,10 @@ enum TypeCategory { // stack of entered monitors (identified by code unit offset). class RegisterLine { public: - RegisterLine(size_t num_regs, MethodVerifier* verifier) - : line_(new uint16_t[num_regs]), - verifier_(verifier), - num_regs_(num_regs) { - memset(line_.get(), 0, num_regs_ * sizeof(uint16_t)); - SetResultTypeToUnknown(); + static RegisterLine* Create(size_t num_regs, MethodVerifier* verifier) { + uint8_t* memory = new uint8_t[sizeof(RegisterLine) + (num_regs * sizeof(uint16_t))]; + RegisterLine* rl = new (memory) RegisterLine(num_regs, verifier); + return rl; } // Implement category-1 "move" instructions. Copy a 32-bit value from "vsrc" to "vdst". @@ -108,7 +105,7 @@ class RegisterLine { void CopyFromLine(const RegisterLine* src) { DCHECK_EQ(num_regs_, src->num_regs_); - memcpy(line_.get(), src->line_.get(), num_regs_ * sizeof(uint16_t)); + memcpy(&line_, &src->line_, num_regs_ * sizeof(uint16_t)); monitors_ = src->monitors_; reg_to_lock_depths_ = src->reg_to_lock_depths_; } @@ -116,7 +113,7 @@ class RegisterLine { std::string Dump() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); void FillWithGarbage() { - memset(line_.get(), 0xf1, num_regs_ * sizeof(uint16_t)); + memset(&line_, 0xf1, num_regs_ * sizeof(uint16_t)); while (!monitors_.empty()) { monitors_.pop_back(); } @@ -161,7 +158,7 @@ class RegisterLine { int CompareLine(const RegisterLine* line2) const { DCHECK(monitors_ == line2->monitors_); // TODO: DCHECK(reg_to_lock_depths_ == line2->reg_to_lock_depths_); - return memcmp(line_.get(), line2->line_.get(), num_regs_ * sizeof(uint16_t)); + return memcmp(&line_, &line2->line_, num_regs_ * sizeof(uint16_t)); } size_t NumRegs() const { @@ -339,23 +336,30 @@ class RegisterLine { reg_to_lock_depths_.erase(reg); } + RegisterLine(size_t num_regs, MethodVerifier* verifier) + : verifier_(verifier), + num_regs_(num_regs) { + memset(&line_, 0, num_regs_ * sizeof(uint16_t)); + SetResultTypeToUnknown(); + } + // Storage for the result register's type, valid after an invocation uint16_t result_[2]; - // An array of RegType Ids associated with each dex register - UniquePtr line_; - // Back link to the verifier MethodVerifier* verifier_; // Length of reg_types_ const uint32_t num_regs_; // A stack of monitor enter locations - std::deque monitors_; + std::vector monitors_; // A map from register to a bit vector of indices into the monitors_ stack. As we pop the monitor // stack we verify that monitor-enter/exit are correctly nested. That is, if there was a // monitor-enter on v5 and then on v6, we expect the monitor-exit to be on v6 then on v5 SafeMap reg_to_lock_depths_; + + // An array of RegType Ids associated with each dex register. + uint16_t line_[0]; }; std::ostream& operator<<(std::ostream& os, const RegisterLine& rhs); From 11409ae81a3eaf84d7fd2b3c85b8b06d2bae27f0 Mon Sep 17 00:00:00 2001 From: Mathieu Chartier Date: Mon, 23 Sep 2013 11:49:36 -0700 Subject: [PATCH 0054/2402] Refactor and improve mod-union tables. Allow support for adding more mod union tables, reduces the amount of baked in logic. Adds support for updating mod union table references from compaction (not for ReferenceCache table yet). Change-Id: I1beeda00839ed86ef0e853beff5ce10d0ab2b9d1 --- runtime/gc/accounting/mod_union_table-inl.h | 14 +- runtime/gc/accounting/mod_union_table.cc | 146 +++++++++++--------- runtime/gc/accounting/mod_union_table.h | 55 ++++---- runtime/gc/accounting/space_bitmap.h | 4 +- runtime/gc/collector/mark_sweep-inl.h | 27 ++-- runtime/gc/collector/mark_sweep.cc | 51 ++++--- runtime/gc/collector/mark_sweep.h | 25 ++-- runtime/gc/collector/sticky_mark_sweep.h | 4 + runtime/gc/heap.cc | 106 +++++++------- runtime/gc/heap.h | 15 +- runtime/mirror/object.h | 5 + 11 files changed, 254 insertions(+), 198 deletions(-) diff --git a/runtime/gc/accounting/mod_union_table-inl.h b/runtime/gc/accounting/mod_union_table-inl.h index 29450c1d348..fb425df78a4 100644 --- a/runtime/gc/accounting/mod_union_table-inl.h +++ b/runtime/gc/accounting/mod_union_table-inl.h @@ -28,9 +28,11 @@ namespace accounting { // A mod-union table to record image references to the Zygote and alloc space. class ModUnionTableToZygoteAllocspace : public ModUnionTableReferenceCache { public: - explicit ModUnionTableToZygoteAllocspace(Heap* heap) : ModUnionTableReferenceCache(heap) {} + explicit ModUnionTableToZygoteAllocspace(const std::string& name, Heap* heap, + space::ContinuousSpace* space) + : ModUnionTableReferenceCache(name, heap, space) {} - bool AddReference(const mirror::Object* /* obj */, const mirror::Object* ref) { + bool AddReference(const mirror::Object* /* obj */, const mirror::Object* ref) ALWAYS_INLINE { const std::vector& spaces = GetHeap()->GetContinuousSpaces(); typedef std::vector::const_iterator It; for (It it = spaces.begin(); it != spaces.end(); ++it) { @@ -47,16 +49,18 @@ class ModUnionTableToZygoteAllocspace : public ModUnionTableReferenceCache { // A mod-union table to record Zygote references to the alloc space. class ModUnionTableToAllocspace : public ModUnionTableReferenceCache { public: - explicit ModUnionTableToAllocspace(Heap* heap) : ModUnionTableReferenceCache(heap) {} + explicit ModUnionTableToAllocspace(const std::string& name, Heap* heap, + space::ContinuousSpace* space) + : ModUnionTableReferenceCache(name, heap, space) {} - bool AddReference(const mirror::Object* /* obj */, const mirror::Object* ref) { + bool AddReference(const mirror::Object* /* obj */, const mirror::Object* ref) ALWAYS_INLINE { const std::vector& spaces = GetHeap()->GetContinuousSpaces(); typedef std::vector::const_iterator It; for (It it = spaces.begin(); it != spaces.end(); ++it) { space::ContinuousSpace* space = *it; if (space->Contains(ref)) { // The allocation space is always considered for collection whereas the Zygote space is - // + // only considered for full GC. return space->GetGcRetentionPolicy() == space::kGcRetentionPolicyAlwaysCollect; } } diff --git a/runtime/gc/accounting/mod_union_table.cc b/runtime/gc/accounting/mod_union_table.cc index 486521951aa..7cbe94d3d2b 100644 --- a/runtime/gc/accounting/mod_union_table.cc +++ b/runtime/gc/accounting/mod_union_table.cc @@ -19,6 +19,7 @@ #include "base/stl_util.h" #include "card_table-inl.h" #include "heap_bitmap.h" +#include "gc/collector/mark_sweep.h" #include "gc/collector/mark_sweep-inl.h" #include "gc/heap.h" #include "gc/space/space.h" @@ -67,60 +68,87 @@ class ModUnionClearCardVisitor { std::vector* const cleared_cards_; }; +class ModUnionUpdateObjectReferencesVisitor { + public: + ModUnionUpdateObjectReferencesVisitor(RootVisitor visitor, void* arg) + : visitor_(visitor), + arg_(arg) { + } + + // Extra parameters are required since we use this same visitor signature for checking objects. + void operator()(Object* obj, Object* ref, const MemberOffset& offset, + bool /* is_static */) const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + // Only add the reference if it is non null and fits our criteria. + if (ref != nullptr) { + Object* new_ref = visitor_(ref, arg_); + if (new_ref != ref) { + obj->SetFieldObject(offset, ref, false, true); + } + } + } + + private: + RootVisitor* visitor_; + void* arg_; +}; + class ModUnionScanImageRootVisitor { public: - explicit ModUnionScanImageRootVisitor(collector::MarkSweep* const mark_sweep) - : mark_sweep_(mark_sweep) {} + ModUnionScanImageRootVisitor(RootVisitor visitor, void* arg) + : visitor_(visitor), arg_(arg) {} - void operator()(const Object* root) const + void operator()(Object* root) const EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { DCHECK(root != NULL); - mark_sweep_->ScanRoot(root); + ModUnionUpdateObjectReferencesVisitor ref_visitor(visitor_, arg_); + collector::MarkSweep::VisitObjectReferences(root, ref_visitor, true); } private: - collector::MarkSweep* const mark_sweep_; + RootVisitor* visitor_; + void* arg_; }; -void ModUnionTableReferenceCache::ClearCards(space::ContinuousSpace* space) { +void ModUnionTableReferenceCache::ClearCards() { CardTable* card_table = GetHeap()->GetCardTable(); ModUnionClearCardSetVisitor visitor(&cleared_cards_); // Clear dirty cards in the this space and update the corresponding mod-union bits. - card_table->ModifyCardsAtomic(space->Begin(), space->End(), AgeCardVisitor(), visitor); + card_table->ModifyCardsAtomic(space_->Begin(), space_->End(), AgeCardVisitor(), visitor); } class AddToReferenceArrayVisitor { public: explicit AddToReferenceArrayVisitor(ModUnionTableReferenceCache* mod_union_table, - std::vector* references) + std::vector* references) : mod_union_table_(mod_union_table), references_(references) { } // Extra parameters are required since we use this same visitor signature for checking objects. - void operator()(const Object* obj, const Object* ref, const MemberOffset& /* offset */, + void operator()(Object* obj, Object* ref, const MemberOffset& offset, bool /* is_static */) const { // Only add the reference if it is non null and fits our criteria. - if (ref != NULL && mod_union_table_->AddReference(obj, ref)) { - references_->push_back(ref); + if (ref != nullptr && mod_union_table_->AddReference(obj, ref)) { + // Push the adddress of the reference. + references_->push_back(obj->GetFieldObjectAddr(offset)); } } private: ModUnionTableReferenceCache* const mod_union_table_; - std::vector* const references_; + std::vector* const references_; }; class ModUnionReferenceVisitor { public: explicit ModUnionReferenceVisitor(ModUnionTableReferenceCache* const mod_union_table, - std::vector* references) + std::vector* references) : mod_union_table_(mod_union_table), references_(references) { } - void operator()(const Object* obj) const + void operator()(Object* obj) const SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_, Locks::mutator_lock_) { DCHECK(obj != NULL); // We don't have an early exit since we use the visitor pattern, an early @@ -130,7 +158,7 @@ class ModUnionReferenceVisitor { } private: ModUnionTableReferenceCache* const mod_union_table_; - std::vector* const references_; + std::vector* const references_; }; class CheckReferenceVisitor { @@ -143,8 +171,8 @@ class CheckReferenceVisitor { // Extra parameters are required since we use this same visitor signature for checking objects. // TODO: Fixme when anotatalysis works with visitors. - void operator()(const Object* obj, const Object* ref, const MemberOffset& /* offset */, - bool /* is_static */) const + void operator()(const Object* obj, const Object* ref, + const MemberOffset& /* offset */, bool /* is_static */) const SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_, Locks::mutator_lock_) { Heap* heap = mod_union_table_->GetHeap(); if (ref != NULL && mod_union_table_->AddReference(obj, ref) && @@ -174,7 +202,7 @@ class ModUnionCheckReferences { : mod_union_table_(mod_union_table), references_(references) { } - void operator()(const Object* obj) const NO_THREAD_SAFETY_ANALYSIS { + void operator()(Object* obj) const NO_THREAD_SAFETY_ANALYSIS { Locks::heap_bitmap_lock_->AssertSharedHeld(Thread::Current()); DCHECK(obj != NULL); CheckReferenceVisitor visitor(mod_union_table_, references_); @@ -188,26 +216,25 @@ class ModUnionCheckReferences { void ModUnionTableReferenceCache::Verify() { // Start by checking that everything in the mod union table is marked. - Heap* heap = GetHeap(); - for (const std::pair >& it : references_) { - for (const Object* ref : it.second) { - CHECK(heap->IsLiveObjectLocked(ref)); + for (const auto& ref_pair : references_) { + for (Object** ref : ref_pair.second) { + CHECK(heap_->IsLiveObjectLocked(*ref)); } } // Check the references of each clean card which is also in the mod union table. - CardTable* card_table = heap->GetCardTable(); - for (const std::pair > & it : references_) { - const byte* card = it.first; + CardTable* card_table = heap_->GetCardTable(); + SpaceBitmap* live_bitmap = space_->GetLiveBitmap(); + for (const auto& ref_pair : references_) { + const byte* card = ref_pair.first; if (*card == CardTable::kCardClean) { - std::set reference_set(it.second.begin(), it.second.end()); + std::set reference_set; + for (Object** obj_ptr : ref_pair.second) { + reference_set.insert(*obj_ptr); + } ModUnionCheckReferences visitor(this, reference_set); uintptr_t start = reinterpret_cast(card_table->AddrFromCard(card)); - uintptr_t end = start + CardTable::kCardSize; - auto* space = heap->FindContinuousSpaceFromObject(reinterpret_cast(start), false); - DCHECK(space != nullptr); - SpaceBitmap* live_bitmap = space->GetLiveBitmap(); - live_bitmap->VisitMarkedRange(start, end, visitor); + live_bitmap->VisitMarkedRange(start, start + CardTable::kCardSize, visitor); } } } @@ -221,24 +248,24 @@ void ModUnionTableReferenceCache::Dump(std::ostream& os) { os << reinterpret_cast(start) << "-" << reinterpret_cast(end) << ","; } os << "]\nModUnionTable references: ["; - for (const std::pair >& it : references_) { - const byte* card_addr = it.first; + for (const auto& ref_pair : references_) { + const byte* card_addr = ref_pair.first; uintptr_t start = reinterpret_cast(card_table->AddrFromCard(card_addr)); uintptr_t end = start + CardTable::kCardSize; os << reinterpret_cast(start) << "-" << reinterpret_cast(end) << "->{"; - for (const mirror::Object* ref : it.second) { - os << reinterpret_cast(ref) << ","; + for (Object** ref : ref_pair.second) { + os << reinterpret_cast(*ref) << ","; } os << "},"; } } -void ModUnionTableReferenceCache::Update() { +void ModUnionTableReferenceCache::UpdateAndMarkReferences(RootVisitor visitor, void* arg) { Heap* heap = GetHeap(); CardTable* card_table = heap->GetCardTable(); - std::vector cards_references; - ModUnionReferenceVisitor visitor(this, &cards_references); + std::vector cards_references; + ModUnionReferenceVisitor add_visitor(this, &cards_references); for (const auto& card : cleared_cards_) { // Clear and re-compute alloc space references associated with this card. @@ -248,7 +275,7 @@ void ModUnionTableReferenceCache::Update() { auto* space = heap->FindContinuousSpaceFromObject(reinterpret_cast(start), false); DCHECK(space != nullptr); SpaceBitmap* live_bitmap = space->GetLiveBitmap(); - live_bitmap->VisitMarkedRange(start, end, visitor); + live_bitmap->VisitMarkedRange(start, end, add_visitor); // Update the corresponding references for the card. auto found = references_.find(card); @@ -263,46 +290,41 @@ void ModUnionTableReferenceCache::Update() { } } cleared_cards_.clear(); -} - -void ModUnionTableReferenceCache::MarkReferences(collector::MarkSweep* mark_sweep) { size_t count = 0; - for (const auto& ref : references_) { - for (const auto& obj : ref.second) { - mark_sweep->MarkRoot(obj); - ++count; + for (const auto& obj_ptr : ref.second) { + Object* obj = *obj_ptr; + if (obj != nullptr) { + Object* new_obj = visitor(obj, arg); + // Avoid dirtying pages in the image unless necessary. + if (new_obj != obj) { + *obj_ptr = new_obj; + } + } } + count += ref.second.size(); } if (VLOG_IS_ON(heap)) { VLOG(gc) << "Marked " << count << " references in mod union table"; } } -void ModUnionTableCardCache::ClearCards(space::ContinuousSpace* space) { +void ModUnionTableCardCache::ClearCards() { CardTable* card_table = GetHeap()->GetCardTable(); ModUnionClearCardSetVisitor visitor(&cleared_cards_); // Clear dirty cards in the this space and update the corresponding mod-union bits. - card_table->ModifyCardsAtomic(space->Begin(), space->End(), AgeCardVisitor(), visitor); + card_table->ModifyCardsAtomic(space_->Begin(), space_->End(), AgeCardVisitor(), visitor); } // Mark all references to the alloc space(s). -void ModUnionTableCardCache::MarkReferences(collector::MarkSweep* mark_sweep) { +void ModUnionTableCardCache::UpdateAndMarkReferences(RootVisitor visitor, void* arg) { CardTable* card_table = heap_->GetCardTable(); - ModUnionScanImageRootVisitor visitor(mark_sweep); - space::ContinuousSpace* space = nullptr; - SpaceBitmap* bitmap = nullptr; + ModUnionScanImageRootVisitor scan_visitor(visitor, arg); + SpaceBitmap* bitmap = space_->GetLiveBitmap(); for (const byte* card_addr : cleared_cards_) { - auto start = reinterpret_cast(card_table->AddrFromCard(card_addr)); - auto end = start + CardTable::kCardSize; - auto obj_start = reinterpret_cast(start); - if (UNLIKELY(space == nullptr || !space->Contains(obj_start))) { - space = heap_->FindContinuousSpaceFromObject(obj_start, false); - DCHECK(space != nullptr); - bitmap = space->GetLiveBitmap(); - DCHECK(bitmap != nullptr); - } - bitmap->VisitMarkedRange(start, end, visitor); + uintptr_t start = reinterpret_cast(card_table->AddrFromCard(card_addr)); + DCHECK(space_->HasAddress(reinterpret_cast(start))); + bitmap->VisitMarkedRange(start, start + CardTable::kCardSize, scan_visitor); } } diff --git a/runtime/gc/accounting/mod_union_table.h b/runtime/gc/accounting/mod_union_table.h index eb7a754d246..d874c6080c1 100644 --- a/runtime/gc/accounting/mod_union_table.h +++ b/runtime/gc/accounting/mod_union_table.h @@ -19,6 +19,7 @@ #include "gc_allocator.h" #include "globals.h" +#include "root_visitor.h" #include "safe_map.h" #include @@ -52,21 +53,23 @@ class ModUnionTable { public: typedef std::set, GCAllocator > CardSet; - explicit ModUnionTable(Heap* heap) : heap_(heap) {} + explicit ModUnionTable(const std::string& name, Heap* heap, space::ContinuousSpace* space) + : name_(name), + heap_(heap), + space_(space) { + } virtual ~ModUnionTable() {} // Clear cards which map to a memory range of a space. This doesn't immediately update the // mod-union table, as updating the mod-union table may have an associated cost, such as // determining references to track. - virtual void ClearCards(space::ContinuousSpace* space) = 0; + virtual void ClearCards() = 0; // Update the mod-union table using data stored by ClearCards. There may be multiple ClearCards - // before a call to update, for example, back-to-back sticky GCs. - virtual void Update() = 0; - - // Mark the bitmaps for all references which are stored in the mod-union table. - virtual void MarkReferences(collector::MarkSweep* mark_sweep) = 0; + // before a call to update, for example, back-to-back sticky GCs. Also mark references to other + // spaces which are stored in the mod-union table. + virtual void UpdateAndMarkReferences(RootVisitor visitor, void* arg) = 0; // Verification, sanity checks that we don't have clean cards which conflict with out cached data // for said cards. Exclusive lock is required since verify sometimes uses @@ -75,31 +78,35 @@ class ModUnionTable { virtual void Verify() EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_) = 0; virtual void Dump(std::ostream& os) = 0; - + space::ContinuousSpace* GetSpace() { + return space_; + } Heap* GetHeap() const { return heap_; } + const std::string& GetName() const { + return name_; + } protected: + const std::string name_; Heap* const heap_; + space::ContinuousSpace* const space_; }; // Reference caching implementation. Caches references pointing to alloc space(s) for each card. class ModUnionTableReferenceCache : public ModUnionTable { public: - explicit ModUnionTableReferenceCache(Heap* heap) : ModUnionTable(heap) {} + explicit ModUnionTableReferenceCache(const std::string& name, Heap* heap, + space::ContinuousSpace* space) + : ModUnionTable(name, heap, space) {} virtual ~ModUnionTableReferenceCache() {} // Clear and store cards for a space. - void ClearCards(space::ContinuousSpace* space); + void ClearCards(); - // Update table based on cleared cards. - void Update() - EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - - // Mark all references to the alloc space(s). - void MarkReferences(collector::MarkSweep* mark_sweep) + // Update table based on cleared cards and mark all references to the other spaces. + void UpdateAndMarkReferences(RootVisitor visitor, void* arg) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_); @@ -117,24 +124,22 @@ class ModUnionTableReferenceCache : public ModUnionTable { ModUnionTable::CardSet cleared_cards_; // Maps from dirty cards to their corresponding alloc space references. - SafeMap, std::less, - GCAllocator > > > references_; + SafeMap, std::less, + GCAllocator > > > references_; }; // Card caching implementation. Keeps track of which cards we cleared and only this information. class ModUnionTableCardCache : public ModUnionTable { public: - explicit ModUnionTableCardCache(Heap* heap) : ModUnionTable(heap) {} + explicit ModUnionTableCardCache(const std::string& name, Heap* heap, space::ContinuousSpace* space) + : ModUnionTable(name, heap, space) {} virtual ~ModUnionTableCardCache() {} // Clear and store cards for a space. - void ClearCards(space::ContinuousSpace* space); - - // Nothing to update as all dirty cards were placed into cleared cards during clearing. - void Update() {} + void ClearCards(); // Mark all references to the alloc space(s). - void MarkReferences(collector::MarkSweep* mark_sweep) + void UpdateAndMarkReferences(RootVisitor visitor, void* arg) EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); diff --git a/runtime/gc/accounting/space_bitmap.h b/runtime/gc/accounting/space_bitmap.h index f975692a3f5..4cf88724b92 100644 --- a/runtime/gc/accounting/space_bitmap.h +++ b/runtime/gc/accounting/space_bitmap.h @@ -247,8 +247,8 @@ class SpaceSetMap { template void Visit(const Visitor& visitor) NO_THREAD_SAFETY_ANALYSIS { - for (Objects::iterator it = contained_.begin(); it != contained_.end(); ++it) { - visitor(*it); + for (const mirror::Object* obj : contained_) { + visitor(const_cast(obj)); } } diff --git a/runtime/gc/collector/mark_sweep-inl.h b/runtime/gc/collector/mark_sweep-inl.h index d0b0b5c9300..270c9efde97 100644 --- a/runtime/gc/collector/mark_sweep-inl.h +++ b/runtime/gc/collector/mark_sweep-inl.h @@ -29,7 +29,7 @@ namespace gc { namespace collector { template -inline void MarkSweep::ScanObjectVisit(const mirror::Object* obj, const MarkVisitor& visitor) { +inline void MarkSweep::ScanObjectVisit(mirror::Object* obj, const MarkVisitor& visitor) { DCHECK(obj != NULL); if (kIsDebugBuild && !IsMarked(obj)) { heap_->DumpSpaces(); @@ -62,7 +62,8 @@ inline void MarkSweep::ScanObjectVisit(const mirror::Object* obj, const MarkVisi } template -inline void MarkSweep::VisitObjectReferences(const mirror::Object* obj, const Visitor& visitor) +inline void MarkSweep::VisitObjectReferences(mirror::Object* obj, const Visitor& visitor, + bool visit_class) SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_, Locks::mutator_lock_) { DCHECK(obj != NULL); @@ -70,6 +71,9 @@ inline void MarkSweep::VisitObjectReferences(const mirror::Object* obj, const Vi mirror::Class* klass = obj->GetClass(); DCHECK(klass != NULL); + if (visit_class) { + visitor(obj, klass, MemberOffset(0), false); + } if (klass == mirror::Class::GetJavaLangClass()) { DCHECK_EQ(klass->GetClass(), mirror::Class::GetJavaLangClass()); VisitClassReferences(klass, obj, visitor); @@ -86,8 +90,8 @@ inline void MarkSweep::VisitObjectReferences(const mirror::Object* obj, const Vi } template -inline void MarkSweep::VisitInstanceFieldsReferences(const mirror::Class* klass, - const mirror::Object* obj, +inline void MarkSweep::VisitInstanceFieldsReferences(mirror::Class* klass, + mirror::Object* obj, const Visitor& visitor) SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_, Locks::mutator_lock_) { DCHECK(obj != NULL); @@ -96,7 +100,7 @@ inline void MarkSweep::VisitInstanceFieldsReferences(const mirror::Class* klass, } template -inline void MarkSweep::VisitClassReferences(const mirror::Class* klass, const mirror::Object* obj, +inline void MarkSweep::VisitClassReferences(mirror::Class* klass, mirror::Object* obj, const Visitor& visitor) SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_, Locks::mutator_lock_) { VisitInstanceFieldsReferences(klass, obj, visitor); @@ -104,15 +108,14 @@ inline void MarkSweep::VisitClassReferences(const mirror::Class* klass, const mi } template -inline void MarkSweep::VisitStaticFieldsReferences(const mirror::Class* klass, - const Visitor& visitor) +inline void MarkSweep::VisitStaticFieldsReferences(mirror::Class* klass, const Visitor& visitor) SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_, Locks::mutator_lock_) { DCHECK(klass != NULL); VisitFieldsReferences(klass, klass->GetReferenceStaticOffsets(), true, visitor); } template -inline void MarkSweep::VisitFieldsReferences(const mirror::Object* obj, uint32_t ref_offsets, +inline void MarkSweep::VisitFieldsReferences(mirror::Object* obj, uint32_t ref_offsets, bool is_static, const Visitor& visitor) { if (LIKELY(ref_offsets != CLASS_WALK_SUPER)) { // Found a reference offset bitmap. Mark the specified offsets. @@ -124,7 +127,7 @@ inline void MarkSweep::VisitFieldsReferences(const mirror::Object* obj, uint32_t while (ref_offsets != 0) { size_t right_shift = CLZ(ref_offsets); MemberOffset field_offset = CLASS_OFFSET_FROM_CLZ(right_shift); - const mirror::Object* ref = obj->GetFieldObject(field_offset, false); + mirror::Object* ref = obj->GetFieldObject(field_offset, false); visitor(obj, ref, field_offset, is_static); ref_offsets &= ~(CLASS_HIGH_BIT >> right_shift); } @@ -143,7 +146,7 @@ inline void MarkSweep::VisitFieldsReferences(const mirror::Object* obj, uint32_t mirror::ArtField* field = (is_static ? klass->GetStaticField(i) : klass->GetInstanceField(i)); MemberOffset field_offset = field->GetOffset(); - const mirror::Object* ref = obj->GetFieldObject(field_offset, false); + mirror::Object* ref = obj->GetFieldObject(field_offset, false); visitor(obj, ref, field_offset, is_static); } } @@ -151,11 +154,11 @@ inline void MarkSweep::VisitFieldsReferences(const mirror::Object* obj, uint32_t } template -inline void MarkSweep::VisitObjectArrayReferences(const mirror::ObjectArray* array, +inline void MarkSweep::VisitObjectArrayReferences(mirror::ObjectArray* array, const Visitor& visitor) { const size_t length = static_cast(array->GetLength()); for (size_t i = 0; i < length; ++i) { - const mirror::Object* element = array->GetWithoutChecks(static_cast(i)); + mirror::Object* element = array->GetWithoutChecks(static_cast(i)); const size_t width = sizeof(mirror::Object*); MemberOffset offset(i * width + mirror::Array::DataOffset(width).Int32Value()); visitor(array, element, offset, false); diff --git a/runtime/gc/collector/mark_sweep.cc b/runtime/gc/collector/mark_sweep.cc index 1625ba684dc..a5e66d20df0 100644 --- a/runtime/gc/collector/mark_sweep.cc +++ b/runtime/gc/collector/mark_sweep.cc @@ -28,6 +28,7 @@ #include "base/timing_logger.h" #include "gc/accounting/card_table-inl.h" #include "gc/accounting/heap_bitmap.h" +#include "gc/accounting/mod_union_table.h" #include "gc/accounting/space_bitmap-inl.h" #include "gc/heap.h" #include "gc/space/image_space.h" @@ -99,7 +100,7 @@ void MarkSweep::ImmuneSpace(space::ContinuousSpace* space) { } else { const space::ContinuousSpace* prev_space = nullptr; // Find out if the previous space is immune. - for (space::ContinuousSpace* cur_space : GetHeap()->GetContinuousSpaces()) { + for (const space::ContinuousSpace* cur_space : GetHeap()->GetContinuousSpaces()) { if (cur_space == space) { break; } @@ -107,15 +108,19 @@ void MarkSweep::ImmuneSpace(space::ContinuousSpace* space) { } // If previous space was immune, then extend the immune region. Relies on continuous spaces // being sorted by Heap::AddContinuousSpace. - if (prev_space != NULL && - immune_begin_ <= reinterpret_cast(prev_space->Begin()) && - immune_end_ >= reinterpret_cast(prev_space->End())) { + if (prev_space != NULL && IsImmuneSpace(prev_space)) { immune_begin_ = std::min(reinterpret_cast(space->Begin()), immune_begin_); immune_end_ = std::max(reinterpret_cast(space->End()), immune_end_); } } } +bool MarkSweep::IsImmuneSpace(const space::ContinuousSpace* space) { + return + immune_begin_ <= reinterpret_cast(space->Begin()) && + immune_end_ >= reinterpret_cast(space->End()); +} + void MarkSweep::BindBitmaps() { timings_.StartSplit("BindBitmaps"); WriterMutexLock mu(Thread::Current(), *Locks::heap_bitmap_lock_); @@ -263,11 +268,23 @@ void MarkSweep::MarkingPhase() { } live_stack_freeze_size_ = heap_->GetLiveStack()->Size(); MarkConcurrentRoots(); - - heap_->UpdateAndMarkModUnion(this, timings_, GetGcType()); + UpdateAndMarkModUnion(); MarkReachableObjects(); } +void MarkSweep::UpdateAndMarkModUnion() { + for (const auto& space : heap_->GetContinuousSpaces()) { + if (IsImmuneSpace(space)) { + const char* name = space->IsZygoteSpace() ? "UpdateAndMarkZygoteModUnionTable" : + "UpdateAndMarkImageModUnionTable"; + base::TimingLogger::ScopedSplit split(name, &timings_); + accounting::ModUnionTable* mod_union_table = heap_->FindModUnionTableFromSpace(space); + CHECK(mod_union_table != nullptr); + mod_union_table->UpdateAndMarkReferences(MarkRootCallback, this); + } + } +} + void MarkSweep::MarkThreadRoots(Thread* self) { MarkRootsCheckpoint(self); } @@ -577,11 +594,11 @@ void MarkSweep::MarkConcurrentRoots() { void MarkSweep::CheckObject(const Object* obj) { DCHECK(obj != NULL); - VisitObjectReferences(obj, [this](const Object* obj, const Object* ref, MemberOffset offset, - bool is_static) NO_THREAD_SAFETY_ANALYSIS { + VisitObjectReferences(const_cast(obj), [this](const Object* obj, const Object* ref, + MemberOffset offset, bool is_static) NO_THREAD_SAFETY_ANALYSIS { Locks::heap_bitmap_lock_->AssertSharedHeld(Thread::Current()); CheckReference(obj, ref, offset, is_static); - }); + }, true); } void MarkSweep::VerifyImageRootVisitor(Object* root, void* arg) { @@ -647,11 +664,11 @@ class MarkStackTask : public Task { explicit ScanObjectParallelVisitor(MarkStackTask* chunk_task) ALWAYS_INLINE : chunk_task_(chunk_task) {} - void operator()(const Object* obj) const { + void operator()(Object* obj) const { MarkSweep* mark_sweep = chunk_task_->mark_sweep_; mark_sweep->ScanObjectVisit(obj, - [mark_sweep, this](const Object* /* obj */, const Object* ref, - const MemberOffset& /* offset */, bool /* is_static */) ALWAYS_INLINE { + [mark_sweep, this](Object* /* obj */, Object* ref, const MemberOffset& /* offset */, + bool /* is_static */) ALWAYS_INLINE { if (ref != nullptr && mark_sweep->MarkObjectParallel(ref)) { if (kUseFinger) { android_memory_barrier(); @@ -708,11 +725,11 @@ class MarkStackTask : public Task { static const size_t kFifoSize = 4; BoundedFifoPowerOfTwo prefetch_fifo; for (;;) { - const Object* obj = NULL; + const Object* obj = nullptr; if (kUseMarkStackPrefetch) { while (mark_stack_pos_ != 0 && prefetch_fifo.size() < kFifoSize) { const Object* obj = mark_stack_[--mark_stack_pos_]; - DCHECK(obj != NULL); + DCHECK(obj != nullptr); __builtin_prefetch(obj); prefetch_fifo.push_back(obj); } @@ -727,8 +744,8 @@ class MarkStackTask : public Task { } obj = mark_stack_[--mark_stack_pos_]; } - DCHECK(obj != NULL); - visitor(obj); + DCHECK(obj != nullptr); + visitor(const_cast(obj)); } } }; @@ -1366,7 +1383,7 @@ class MarkObjectVisitor { // and dispatches to a specialized scanning routine. void MarkSweep::ScanObject(const Object* obj) { MarkObjectVisitor visitor(this); - ScanObjectVisit(obj, visitor); + ScanObjectVisit(const_cast(obj), visitor); } void MarkSweep::ProcessMarkStackParallel(size_t thread_count) { diff --git a/runtime/gc/collector/mark_sweep.h b/runtime/gc/collector/mark_sweep.h index 76e71fda04a..19df2da0b67 100644 --- a/runtime/gc/collector/mark_sweep.h +++ b/runtime/gc/collector/mark_sweep.h @@ -114,6 +114,9 @@ class MarkSweep : public GarbageCollector { EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + bool IsImmuneSpace(const space::ContinuousSpace* space) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + // Bind the live bits to the mark bits of bitmaps for spaces that are never collected, ie // the image. Mark that portion of the heap as immune. virtual void BindBitmaps() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); @@ -137,6 +140,9 @@ class MarkSweep : public GarbageCollector { void ProcessReferences(Thread* self) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + virtual void UpdateAndMarkModUnion() + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + // Sweeps unmarked objects to complete the garbage collection. virtual void Sweep(bool swap_bitmaps) EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_); @@ -163,7 +169,7 @@ class MarkSweep : public GarbageCollector { // TODO: enable thread safety analysis when in use by multiple worker threads. template - void ScanObjectVisit(const mirror::Object* obj, const MarkVisitor& visitor) + void ScanObjectVisit(mirror::Object* obj, const MarkVisitor& visitor) NO_THREAD_SAFETY_ANALYSIS; size_t GetFreedBytes() const { @@ -215,7 +221,8 @@ class MarkSweep : public GarbageCollector { SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_); template - static void VisitObjectReferences(const mirror::Object* obj, const Visitor& visitor) + static void VisitObjectReferences(mirror::Object* obj, const Visitor& visitor, + bool visit_class = false) SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_, Locks::mutator_lock_); @@ -306,7 +313,7 @@ class MarkSweep : public GarbageCollector { size_t GetThreadCount(bool paused) const; // Returns true if an object is inside of the immune region (assumed to be marked). - bool IsImmune(const mirror::Object* obj) const { + bool IsImmune(const mirror::Object* obj) const ALWAYS_INLINE { return obj >= immune_begin_ && obj < immune_end_; } @@ -317,34 +324,34 @@ class MarkSweep : public GarbageCollector { NO_THREAD_SAFETY_ANALYSIS; template - static void VisitInstanceFieldsReferences(const mirror::Class* klass, const mirror::Object* obj, + static void VisitInstanceFieldsReferences(mirror::Class* klass, mirror::Object* obj, const Visitor& visitor) SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_, Locks::mutator_lock_); // Visit the header, static field references, and interface pointers of a class object. template - static void VisitClassReferences(const mirror::Class* klass, const mirror::Object* obj, + static void VisitClassReferences(mirror::Class* klass, mirror::Object* obj, const Visitor& visitor) SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_, Locks::mutator_lock_); template - static void VisitStaticFieldsReferences(const mirror::Class* klass, const Visitor& visitor) + static void VisitStaticFieldsReferences(mirror::Class* klass, const Visitor& visitor) SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_, Locks::mutator_lock_); template - static void VisitFieldsReferences(const mirror::Object* obj, uint32_t ref_offsets, bool is_static, + static void VisitFieldsReferences(mirror::Object* obj, uint32_t ref_offsets, bool is_static, const Visitor& visitor) SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_, Locks::mutator_lock_); // Visit all of the references in an object array. template - static void VisitObjectArrayReferences(const mirror::ObjectArray* array, + static void VisitObjectArrayReferences(mirror::ObjectArray* array, const Visitor& visitor) SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_, Locks::mutator_lock_); // Visits the header and field references of a data object. template - static void VisitOtherReferences(const mirror::Class* klass, const mirror::Object* obj, + static void VisitOtherReferences(mirror::Class* klass, mirror::Object* obj, const Visitor& visitor) SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_, Locks::mutator_lock_) { return VisitInstanceFieldsReferences(klass, obj, visitor); diff --git a/runtime/gc/collector/sticky_mark_sweep.h b/runtime/gc/collector/sticky_mark_sweep.h index 79c4359901b..8bee00f0b8f 100644 --- a/runtime/gc/collector/sticky_mark_sweep.h +++ b/runtime/gc/collector/sticky_mark_sweep.h @@ -31,6 +31,10 @@ class StickyMarkSweep : public PartialMarkSweep { return kGcTypeSticky; } + // Don't need to do anything special here since we scan all the cards which may have references + // to the newly allocated objects. + virtual void UpdateAndMarkModUnion() { } + explicit StickyMarkSweep(Heap* heap, bool is_concurrent, const std::string& name_prefix = ""); ~StickyMarkSweep() {} diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc index ccd9aaca81b..49a8c3a004f 100644 --- a/runtime/gc/heap.cc +++ b/runtime/gc/heap.cc @@ -191,11 +191,11 @@ Heap::Heap(size_t initial_size, size_t growth_limit, size_t min_free, size_t max card_table_.reset(accounting::CardTable::Create(heap_begin, heap_capacity)); CHECK(card_table_.get() != NULL) << "Failed to create card table"; - image_mod_union_table_.reset(new accounting::ModUnionTableToZygoteAllocspace(this)); - CHECK(image_mod_union_table_.get() != NULL) << "Failed to create image mod-union table"; - - zygote_mod_union_table_.reset(new accounting::ModUnionTableCardCache(this)); - CHECK(zygote_mod_union_table_.get() != NULL) << "Failed to create Zygote mod-union table"; + accounting::ModUnionTable* mod_union_table = + new accounting::ModUnionTableToZygoteAllocspace("Image mod-union table", this, + GetImageSpace()); + CHECK(mod_union_table != nullptr) << "Failed to create image mod-union table"; + AddModUnionTable(mod_union_table); // TODO: Count objects in the image space here. num_bytes_allocated_ = 0; @@ -489,10 +489,7 @@ Heap::~Heap() { live_stack_->Reset(); VLOG(heap) << "~Heap()"; - // We can't take the heap lock here because there might be a daemon thread suspended with the - // heap lock held. We know though that no non-daemon threads are executing, and we know that - // all daemon threads are suspended, and we also know that the threads list have been deleted, so - // those threads can't resume. We're the only running thread, and we can do whatever we like... + STLDeleteValues(&mod_union_tables_); STLDeleteElements(&continuous_spaces_); STLDeleteElements(&discontinuous_spaces_); delete gc_complete_lock_; @@ -1084,15 +1081,15 @@ class ReferringObjectsFinder { // For bitmap Visit. // TODO: Fix lock analysis to not use NO_THREAD_SAFETY_ANALYSIS, requires support for // annotalysis on visitors. - void operator()(const mirror::Object* o) const NO_THREAD_SAFETY_ANALYSIS { - collector::MarkSweep::VisitObjectReferences(o, *this); + void operator()(mirror::Object* obj) const NO_THREAD_SAFETY_ANALYSIS { + collector::MarkSweep::VisitObjectReferences(obj, *this, true); } // For MarkSweep::VisitObjectReferences. - void operator()(const mirror::Object* referrer, const mirror::Object* object, + void operator()(mirror::Object* referrer, mirror::Object* object, const MemberOffset&, bool) const { if (object == object_ && (max_count_ == 0 || referring_objects_.size() < max_count_)) { - referring_objects_.push_back(const_cast(referrer)); + referring_objects_.push_back(referrer); } } @@ -1157,6 +1154,12 @@ void Heap::PreZygoteFork() { AddContinuousSpace(alloc_space_); have_zygote_space_ = true; + // Create the zygote space mod union table. + accounting::ModUnionTable* mod_union_table = + new accounting::ModUnionTableCardCache("zygote space mod-union table", this, zygote_space); + CHECK(mod_union_table != nullptr) << "Failed to create zygote space mod-union table"; + AddModUnionTable(mod_union_table); + // Reset the cumulative loggers since we now have a few additional timing phases. for (const auto& collector : mark_sweep_collectors_) { collector->ResetCumulativeStatistics(); @@ -1313,33 +1316,6 @@ collector::GcType Heap::CollectGarbageInternal(collector::GcType gc_type, GcCaus return gc_type; } -void Heap::UpdateAndMarkModUnion(collector::MarkSweep* mark_sweep, base::TimingLogger& timings, - collector::GcType gc_type) { - if (gc_type == collector::kGcTypeSticky) { - // Don't need to do anything for mod union table in this case since we are only scanning dirty - // cards. - return; - } - - base::TimingLogger::ScopedSplit split("UpdateModUnionTable", &timings); - // Update zygote mod union table. - if (gc_type == collector::kGcTypePartial) { - base::TimingLogger::ScopedSplit split("UpdateZygoteModUnionTable", &timings); - zygote_mod_union_table_->Update(); - - timings.NewSplit("ZygoteMarkReferences"); - zygote_mod_union_table_->MarkReferences(mark_sweep); - } - - // Processes the cards we cleared earlier and adds their objects into the mod-union table. - timings.NewSplit("UpdateModUnionTable"); - image_mod_union_table_->Update(); - - // Scans all objects in the mod-union table. - timings.NewSplit("MarkImageToAllocSpaceReferences"); - image_mod_union_table_->MarkReferences(mark_sweep); -} - static mirror::Object* RootMatchesObjectVisitor(mirror::Object* root, void* arg) { mirror::Object* obj = reinterpret_cast(arg); if (root == obj) { @@ -1483,7 +1459,7 @@ class VerifyObjectVisitor { VerifyReferenceVisitor visitor(heap_); // The class doesn't count as a reference but we should verify it anyways. visitor(obj, obj->GetClass(), MemberOffset(0), false); - collector::MarkSweep::VisitObjectReferences(obj, visitor); + collector::MarkSweep::VisitObjectReferences(const_cast(obj), visitor, true); failed_ = failed_ || visitor.Failed(); } @@ -1516,8 +1492,10 @@ bool Heap::VerifyHeapReferences() { // pointing to dead objects if they are not reachable. if (visitor.Failed()) { // Dump mod-union tables. - image_mod_union_table_->Dump(LOG(ERROR) << "Image mod-union table: "); - zygote_mod_union_table_->Dump(LOG(ERROR) << "Zygote mod-union table: "); + for (const auto& table_pair : mod_union_tables_) { + accounting::ModUnionTable* mod_union_table = table_pair.second; + mod_union_table->Dump(LOG(ERROR) << mod_union_table->GetName() << ": "); + } DumpSpaces(); return false; } @@ -1601,10 +1579,10 @@ class VerifyLiveStackReferences { : heap_(heap), failed_(false) {} - void operator()(const mirror::Object* obj) const + void operator()(mirror::Object* obj) const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_, Locks::heap_bitmap_lock_) { VerifyReferenceCardVisitor visitor(heap_, const_cast(&failed_)); - collector::MarkSweep::VisitObjectReferences(obj, visitor); + collector::MarkSweep::VisitObjectReferences(obj, visitor, true); } bool Failed() const { @@ -1640,15 +1618,23 @@ void Heap::SwapStacks() { allocation_stack_.swap(live_stack_); } +accounting::ModUnionTable* Heap::FindModUnionTableFromSpace(space::Space* space) { + auto it = mod_union_tables_.find(space); + if (it == mod_union_tables_.end()) { + return nullptr; + } + return it->second; +} + void Heap::ProcessCards(base::TimingLogger& timings) { // Clear cards and keep track of cards cleared in the mod-union table. for (const auto& space : continuous_spaces_) { - if (space->IsImageSpace()) { - base::TimingLogger::ScopedSplit split("ImageModUnionClearCards", &timings); - image_mod_union_table_->ClearCards(space); - } else if (space->IsZygoteSpace()) { - base::TimingLogger::ScopedSplit split("ZygoteModUnionClearCards", &timings); - zygote_mod_union_table_->ClearCards(space); + accounting::ModUnionTable* table = FindModUnionTableFromSpace(space); + if (table != nullptr) { + const char* name = space->IsZygoteSpace() ? "ZygoteModUnionClearCards" : + "ImageModUnionClearCards"; + base::TimingLogger::ScopedSplit split(name, &timings); + table->ClearCards(); } else { base::TimingLogger::ScopedSplit split("AllocSpaceClearCards", &timings); // No mod union table for the AllocSpace. Age the cards so that the GC knows that these cards @@ -1658,6 +1644,10 @@ void Heap::ProcessCards(base::TimingLogger& timings) { } } +static mirror::Object* IdentityCallback(mirror::Object* obj, void*) { + return obj; +} + void Heap::PreGcVerification(collector::GarbageCollector* gc) { ThreadList* thread_list = Runtime::Current()->GetThreadList(); Thread* self = Thread::Current(); @@ -1691,10 +1681,11 @@ void Heap::PreGcVerification(collector::GarbageCollector* gc) { if (verify_mod_union_table_) { thread_list->SuspendAll(); ReaderMutexLock reader_lock(self, *Locks::heap_bitmap_lock_); - zygote_mod_union_table_->Update(); - zygote_mod_union_table_->Verify(); - image_mod_union_table_->Update(); - image_mod_union_table_->Verify(); + for (const auto& table_pair : mod_union_tables_) { + accounting::ModUnionTable* mod_union_table = table_pair.second; + mod_union_table->UpdateAndMarkReferences(IdentityCallback, nullptr); + mod_union_table->Verify(); + } thread_list->ResumeAll(); } } @@ -2148,5 +2139,10 @@ int64_t Heap::GetTotalMemory() const { return ret; } +void Heap::AddModUnionTable(accounting::ModUnionTable* mod_union_table) { + DCHECK(mod_union_table != nullptr); + mod_union_tables_.Put(mod_union_table->GetSpace(), mod_union_table); +} + } // namespace gc } // namespace art diff --git a/runtime/gc/heap.h b/runtime/gc/heap.h index 0b64261fa13..0ac3cf0abdc 100644 --- a/runtime/gc/heap.h +++ b/runtime/gc/heap.h @@ -368,11 +368,6 @@ class Heap { accounting::ObjectStack* stack) EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_); - // Update and mark mod union table based on gc type. - void UpdateAndMarkModUnion(collector::MarkSweep* mark_sweep, base::TimingLogger& timings, - collector::GcType gc_type) - EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_); - // Gets called when we get notified by ActivityThread that the process state has changed. void ListenForProcessStateChange(); @@ -426,6 +421,8 @@ class Heap { size_t GetConcGCThreadCount() const { return conc_gc_threads_; } + accounting::ModUnionTable* FindModUnionTableFromSpace(space::Space* space); + void AddModUnionTable(accounting::ModUnionTable* mod_union_table); private: // Allocates uninitialized storage. Passing in a null space tries to place the object in the @@ -522,12 +519,8 @@ class Heap { // The card table, dirtied by the write barrier. UniquePtr card_table_; - // The mod-union table remembers all of the references from the image space to the alloc / - // zygote spaces to allow the card table to be cleared. - UniquePtr image_mod_union_table_; - - // This table holds all of the references from the zygote space to the alloc space. - UniquePtr zygote_mod_union_table_; + // A mod-union table remembers all of the references from the it's space to other spaces. + SafeMap mod_union_tables_; // What kind of concurrency behavior is the runtime after? True for concurrent mark sweep GC, // false for stop-the-world mark sweep. diff --git a/runtime/mirror/object.h b/runtime/mirror/object.h index efaa1834984..4db0c404151 100644 --- a/runtime/mirror/object.h +++ b/runtime/mirror/object.h @@ -184,6 +184,11 @@ class MANAGED Object { } } + Object** GetFieldObjectAddr(MemberOffset field_offset) ALWAYS_INLINE { + VerifyObject(this); + return reinterpret_cast(reinterpret_cast(this) + field_offset.Int32Value()); + } + uint32_t GetField32(MemberOffset field_offset, bool is_volatile) const { VerifyObject(this); const byte* raw_addr = reinterpret_cast(this) + field_offset.Int32Value(); From 530825ba92e317822116efffd470577ddfd142d4 Mon Sep 17 00:00:00 2001 From: Mathieu Chartier Date: Wed, 25 Sep 2013 17:56:49 -0700 Subject: [PATCH 0055/2402] Make InternalStackTraceToStackTraceElementArray compaction safe. Added more SOA decodes. Change-Id: Idbfd546467582a848be91c358c6926a6e472fbff --- runtime/thread.cc | 31 ++++++++++++++----------------- 1 file changed, 14 insertions(+), 17 deletions(-) diff --git a/runtime/thread.cc b/runtime/thread.cc index 7e79ce17f22..99a7d1e384e 100644 --- a/runtime/thread.cc +++ b/runtime/thread.cc @@ -1387,24 +1387,23 @@ jobjectArray Thread::InternalStackTraceToStackTraceElementArray(JNIEnv* env, job // Transition into runnable state to work on Object*/Array* ScopedObjectAccess soa(env); // Decode the internal stack trace into the depth, method trace and PC trace - mirror::ObjectArray* method_trace = - soa.Decode*>(internal); - int32_t depth = method_trace->GetLength() - 1; - mirror::IntArray* pc_trace = down_cast(method_trace->Get(depth)); + int32_t depth = soa.Decode*>(internal)->GetLength() - 1; ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); jobjectArray result; - mirror::ObjectArray* java_traces; + if (output_array != NULL) { // Reuse the array we were given. result = output_array; - java_traces = soa.Decode*>(output_array); // ...adjusting the number of frames we'll write to not exceed the array length. - depth = std::min(depth, java_traces->GetLength()); + const int32_t traces_length = + soa.Decode*>(result)->GetLength(); + depth = std::min(depth, traces_length); } else { // Create java_trace array and place in local reference table - java_traces = class_linker->AllocStackTraceElementArray(soa.Self(), depth); + mirror::ObjectArray* java_traces = + class_linker->AllocStackTraceElementArray(soa.Self(), depth); if (java_traces == NULL) { return NULL; } @@ -1417,9 +1416,12 @@ jobjectArray Thread::InternalStackTraceToStackTraceElementArray(JNIEnv* env, job MethodHelper mh; for (int32_t i = 0; i < depth; ++i) { + mirror::ObjectArray* method_trace = + soa.Decode*>(internal); // Prepare parameters for StackTraceElement(String cls, String method, String file, int line) mirror::ArtMethod* method = down_cast(method_trace->Get(i)); mh.ChangeMethod(method); + mirror::IntArray* pc_trace = down_cast(method_trace->Get(depth)); uint32_t dex_pc = pc_trace->Get(i); int32_t line_number = mh.GetLineNumFromDexPC(dex_pc); // Allocate element, potentially triggering GC @@ -1442,8 +1444,9 @@ jobjectArray Thread::InternalStackTraceToStackTraceElementArray(JNIEnv* env, job return NULL; } const char* source_file = mh.GetDeclaringClassSourceFile(); - SirtRef source_name_object(soa.Self(), mirror::String::AllocFromModifiedUtf8(soa.Self(), - source_file)); + SirtRef source_name_object(soa.Self(), + mirror::String::AllocFromModifiedUtf8(soa.Self(), + source_file)); mirror::StackTraceElement* obj = mirror::StackTraceElement::Alloc(soa.Self(), class_name_object.get(), method_name_object.get(), @@ -1452,13 +1455,7 @@ jobjectArray Thread::InternalStackTraceToStackTraceElementArray(JNIEnv* env, job if (obj == NULL) { return NULL; } -#ifdef MOVING_GARBAGE_COLLECTOR - // Re-read after potential GC - java_traces = Decode*>(soa.Env(), result); - method_trace = down_cast*>(Decode(soa.Env(), internal)); - pc_trace = down_cast(method_trace->Get(depth)); -#endif - java_traces->Set(i, obj); + soa.Decode*>(result)->Set(i, obj); } return result; } From 3b4c18933c24b8a33f38573c2ebcdb9aa16efeb5 Mon Sep 17 00:00:00 2001 From: Hiroshi Yamauchi Date: Thu, 12 Sep 2013 21:33:12 -0700 Subject: [PATCH 0056/2402] Split the allocation path into 'instrumented' and 'uninstrumented' ones. The instrumented path is equivalent to the existing allocation path that checks for three instrumentation mechanisms (the debugger allocation tracking, the runtime allocation stats collection, and valgrind) for every allocation. The uinstrumented path does not perform these checks. We use the uninstrumented path by default and enable the instrumented path only when any of the three mechanisms is enabled. The uninstrumented version of Heap::AllocObject() is inlined. This change improves the Ritz MemAllocTest by ~4% on Nexus 4 and ~3% on Host/x86. Bug: 9986565 Change-Id: I3e68dfff6789d77bbdcea98457b694e1b5fcef5f --- runtime/arch/arm/entrypoints_init_arm.cc | 38 +++- runtime/arch/arm/quick_entrypoints_arm.S | 90 +++++++++ runtime/arch/mips/entrypoints_init_mips.cc | 38 +++- runtime/arch/mips/quick_entrypoints_mips.S | 64 ++++++ runtime/arch/x86/entrypoints_init_x86.cc | 38 +++- runtime/arch/x86/quick_entrypoints_x86.S | 7 + runtime/debugger.cc | 2 + runtime/entrypoints/entrypoint_utils.cc | 57 ++++-- runtime/entrypoints/entrypoint_utils.h | 98 ++++++--- .../quick/quick_alloc_entrypoints.cc | 53 +++++ runtime/gc/heap-inl.h | 188 ++++++++++++++++++ runtime/gc/heap.cc | 148 ++++++-------- runtime/gc/heap.h | 71 ++++++- runtime/gc/space/dlmalloc_space-inl.h | 4 +- runtime/mirror/array-inl.h | 46 ++++- runtime/mirror/array.h | 14 ++ runtime/mirror/class-inl.h | 14 +- runtime/mirror/class.h | 9 +- runtime/runtime.cc | 48 ++++- runtime/runtime.h | 5 + runtime/thread.cc | 6 + runtime/thread.h | 2 + 22 files changed, 872 insertions(+), 168 deletions(-) create mode 100644 runtime/gc/heap-inl.h diff --git a/runtime/arch/arm/entrypoints_init_arm.cc b/runtime/arch/arm/entrypoints_init_arm.cc index 9e6902de3f7..e6e13be0b28 100644 --- a/runtime/arch/arm/entrypoints_init_arm.cc +++ b/runtime/arch/arm/entrypoints_init_arm.cc @@ -42,6 +42,13 @@ extern "C" void* art_quick_alloc_object_with_access_check(uint32_t type_idx, voi extern "C" void* art_quick_check_and_alloc_array(uint32_t, void*, int32_t); extern "C" void* art_quick_check_and_alloc_array_with_access_check(uint32_t, void*, int32_t); +extern "C" void* art_quick_alloc_array_instrumented(uint32_t, void*, int32_t); +extern "C" void* art_quick_alloc_array_with_access_check_instrumented(uint32_t, void*, int32_t); +extern "C" void* art_quick_alloc_object_instrumented(uint32_t type_idx, void* method); +extern "C" void* art_quick_alloc_object_with_access_check_instrumented(uint32_t type_idx, void* method); +extern "C" void* art_quick_check_and_alloc_array_instrumented(uint32_t, void*, int32_t); +extern "C" void* art_quick_check_and_alloc_array_with_access_check_instrumented(uint32_t, void*, int32_t); + // Cast entrypoints. extern "C" uint32_t artIsAssignableFromCode(const mirror::Class* klass, const mirror::Class* ref_class); @@ -133,6 +140,30 @@ extern "C" void art_quick_throw_no_such_method(int32_t method_idx); extern "C" void art_quick_throw_null_pointer_exception(); extern "C" void art_quick_throw_stack_overflow(void*); +static bool quick_alloc_entry_points_instrumented = false; + +void SetQuickAllocEntryPointsInstrumented(bool instrumented) { + quick_alloc_entry_points_instrumented = instrumented; +} + +void ResetQuickAllocEntryPoints(QuickEntryPoints* qpoints) { + if (quick_alloc_entry_points_instrumented) { + qpoints->pAllocArray = art_quick_alloc_array_instrumented; + qpoints->pAllocArrayWithAccessCheck = art_quick_alloc_array_with_access_check_instrumented; + qpoints->pAllocObject = art_quick_alloc_object_instrumented; + qpoints->pAllocObjectWithAccessCheck = art_quick_alloc_object_with_access_check_instrumented; + qpoints->pCheckAndAllocArray = art_quick_check_and_alloc_array_instrumented; + qpoints->pCheckAndAllocArrayWithAccessCheck = art_quick_check_and_alloc_array_with_access_check_instrumented; + } else { + qpoints->pAllocArray = art_quick_alloc_array; + qpoints->pAllocArrayWithAccessCheck = art_quick_alloc_array_with_access_check; + qpoints->pAllocObject = art_quick_alloc_object; + qpoints->pAllocObjectWithAccessCheck = art_quick_alloc_object_with_access_check; + qpoints->pCheckAndAllocArray = art_quick_check_and_alloc_array; + qpoints->pCheckAndAllocArrayWithAccessCheck = art_quick_check_and_alloc_array_with_access_check; + } +} + void InitEntryPoints(InterpreterEntryPoints* ipoints, JniEntryPoints* jpoints, PortableEntryPoints* ppoints, QuickEntryPoints* qpoints) { // Interpreter @@ -147,12 +178,7 @@ void InitEntryPoints(InterpreterEntryPoints* ipoints, JniEntryPoints* jpoints, ppoints->pPortableToInterpreterBridge = art_portable_to_interpreter_bridge; // Alloc - qpoints->pAllocArray = art_quick_alloc_array; - qpoints->pAllocArrayWithAccessCheck = art_quick_alloc_array_with_access_check; - qpoints->pAllocObject = art_quick_alloc_object; - qpoints->pAllocObjectWithAccessCheck = art_quick_alloc_object_with_access_check; - qpoints->pCheckAndAllocArray = art_quick_check_and_alloc_array; - qpoints->pCheckAndAllocArrayWithAccessCheck = art_quick_check_and_alloc_array_with_access_check; + ResetQuickAllocEntryPoints(qpoints); // Cast qpoints->pInstanceofNonTrivial = artIsAssignableFromCode; diff --git a/runtime/arch/arm/quick_entrypoints_arm.S b/runtime/arch/arm/quick_entrypoints_arm.S index a77ce01562c..5b2dd6c7333 100644 --- a/runtime/arch/arm/quick_entrypoints_arm.S +++ b/runtime/arch/arm/quick_entrypoints_arm.S @@ -706,6 +706,17 @@ ENTRY art_quick_alloc_object DELIVER_PENDING_EXCEPTION END art_quick_alloc_object + .extern artAllocObjectFromCodeInstrumented +ENTRY art_quick_alloc_object_instrumented + SETUP_REF_ONLY_CALLEE_SAVE_FRAME @ save callee saves in case of GC + mov r2, r9 @ pass Thread::Current + mov r3, sp @ pass SP + bl artAllocObjectFromCodeInstrumented @ (uint32_t type_idx, Method* method, Thread*, SP) + RESTORE_REF_ONLY_CALLEE_SAVE_FRAME + RETURN_IF_RESULT_IS_NON_ZERO + DELIVER_PENDING_EXCEPTION +END art_quick_alloc_object_instrumented + /* * Called by managed code to allocate an object when the caller doesn't know whether it has * access to the created type. @@ -721,6 +732,17 @@ ENTRY art_quick_alloc_object_with_access_check DELIVER_PENDING_EXCEPTION END art_quick_alloc_object_with_access_check + .extern artAllocObjectFromCodeWithAccessCheckInstrumented +ENTRY art_quick_alloc_object_with_access_check_instrumented + SETUP_REF_ONLY_CALLEE_SAVE_FRAME @ save callee saves in case of GC + mov r2, r9 @ pass Thread::Current + mov r3, sp @ pass SP + bl artAllocObjectFromCodeWithAccessCheckInstrumented @ (uint32_t type_idx, Method* method, Thread*, SP) + RESTORE_REF_ONLY_CALLEE_SAVE_FRAME + RETURN_IF_RESULT_IS_NON_ZERO + DELIVER_PENDING_EXCEPTION +END art_quick_alloc_object_with_access_check_instrumented + /* * Called by managed code to allocate an array. */ @@ -741,6 +763,23 @@ ENTRY art_quick_alloc_array DELIVER_PENDING_EXCEPTION END art_quick_alloc_array + .extern artAllocArrayFromCodeInstrumented +ENTRY art_quick_alloc_array_instrumented + SETUP_REF_ONLY_CALLEE_SAVE_FRAME @ save callee saves in case of GC + mov r3, r9 @ pass Thread::Current + mov r12, sp + str r12, [sp, #-16]! @ expand the frame and pass SP + .pad #16 + .cfi_adjust_cfa_offset 16 + @ artAllocArrayFromCodeInstrumented(uint32_t type_idx, Method* method, int32_t component_count, Thread*, SP) + bl artAllocArrayFromCodeInstrumented + add sp, #16 @ strip the extra frame + .cfi_adjust_cfa_offset -16 + RESTORE_REF_ONLY_CALLEE_SAVE_FRAME + RETURN_IF_RESULT_IS_NON_ZERO + DELIVER_PENDING_EXCEPTION +END art_quick_alloc_array_instrumented + /* * Called by managed code to allocate an array when the caller doesn't know whether it has * access to the created type. @@ -762,6 +801,23 @@ ENTRY art_quick_alloc_array_with_access_check DELIVER_PENDING_EXCEPTION END art_quick_alloc_array_with_access_check + .extern artAllocArrayFromCodeWithAccessCheckInstrumented +ENTRY art_quick_alloc_array_with_access_check_instrumented + SETUP_REF_ONLY_CALLEE_SAVE_FRAME @ save callee saves in case of GC + mov r3, r9 @ pass Thread::Current + mov r12, sp + str r12, [sp, #-16]! @ expand the frame and pass SP + .pad #16 + .cfi_adjust_cfa_offset 16 + @ artAllocArrayFromCodeWithAccessCheckInstrumented(type_idx, method, component_count, Thread*, SP) + bl artAllocArrayFromCodeWithAccessCheckInstrumented + add sp, #16 @ strip the extra frame + .cfi_adjust_cfa_offset -16 + RESTORE_REF_ONLY_CALLEE_SAVE_FRAME + RETURN_IF_RESULT_IS_NON_ZERO + DELIVER_PENDING_EXCEPTION +END art_quick_alloc_array_with_access_check_instrumented + /* * Called by managed code to allocate an array in a special case for FILLED_NEW_ARRAY. */ @@ -782,6 +838,23 @@ ENTRY art_quick_check_and_alloc_array DELIVER_PENDING_EXCEPTION END art_quick_check_and_alloc_array + .extern artCheckAndAllocArrayFromCodeInstrumented +ENTRY art_quick_check_and_alloc_array_instrumented + SETUP_REF_ONLY_CALLEE_SAVE_FRAME @ save callee saves in case of GC + mov r3, r9 @ pass Thread::Current + mov r12, sp + str r12, [sp, #-16]! @ expand the frame and pass SP + .pad #16 + .cfi_adjust_cfa_offset 16 + @ artCheckAndAllocArrayFromCodeInstrumented(uint32_t type_idx, Method* method, int32_t count, Thread* , SP) + bl artCheckAndAllocArrayFromCodeInstrumented + add sp, #16 @ strip the extra frame + .cfi_adjust_cfa_offset -16 + RESTORE_REF_ONLY_CALLEE_SAVE_FRAME + RETURN_IF_RESULT_IS_NON_ZERO + DELIVER_PENDING_EXCEPTION +END art_quick_check_and_alloc_array_instrumented + /* * Called by managed code to allocate an array in a special case for FILLED_NEW_ARRAY. */ @@ -802,6 +875,23 @@ ENTRY art_quick_check_and_alloc_array_with_access_check DELIVER_PENDING_EXCEPTION END art_quick_check_and_alloc_array_with_access_check + .extern artCheckAndAllocArrayFromCodeWithAccessCheckInstrumented +ENTRY art_quick_check_and_alloc_array_with_access_check_instrumented + SETUP_REF_ONLY_CALLEE_SAVE_FRAME @ save callee saves in case of GC + mov r3, r9 @ pass Thread::Current + mov r12, sp + str r12, [sp, #-16]! @ expand the frame and pass SP + .pad #16 + .cfi_adjust_cfa_offset 16 + @ artCheckAndAllocArrayFromCodeWithAccessCheckInstrumented(type_idx, method, count, Thread* , SP) + bl artCheckAndAllocArrayFromCodeWithAccessCheckInstrumented + add sp, #16 @ strip the extra frame + .cfi_adjust_cfa_offset -16 + RESTORE_REF_ONLY_CALLEE_SAVE_FRAME + RETURN_IF_RESULT_IS_NON_ZERO + DELIVER_PENDING_EXCEPTION +END art_quick_check_and_alloc_array_with_access_check_instrumented + /* * Called by managed code when the value in rSUSPEND has been decremented to 0. */ diff --git a/runtime/arch/mips/entrypoints_init_mips.cc b/runtime/arch/mips/entrypoints_init_mips.cc index 40d7cd913c1..3d08298151d 100644 --- a/runtime/arch/mips/entrypoints_init_mips.cc +++ b/runtime/arch/mips/entrypoints_init_mips.cc @@ -41,6 +41,13 @@ extern "C" void* art_quick_alloc_object_with_access_check(uint32_t type_idx, voi extern "C" void* art_quick_check_and_alloc_array(uint32_t, void*, int32_t); extern "C" void* art_quick_check_and_alloc_array_with_access_check(uint32_t, void*, int32_t); +extern "C" void* art_quick_alloc_array_instrumented(uint32_t, void*, int32_t); +extern "C" void* art_quick_alloc_array_with_access_check_instrumented(uint32_t, void*, int32_t); +extern "C" void* art_quick_alloc_object_instrumented(uint32_t type_idx, void* method); +extern "C" void* art_quick_alloc_object_with_access_check_instrumented(uint32_t type_idx, void* method); +extern "C" void* art_quick_check_and_alloc_array_instrumented(uint32_t, void*, int32_t); +extern "C" void* art_quick_check_and_alloc_array_with_access_check_instrumented(uint32_t, void*, int32_t); + // Cast entrypoints. extern "C" uint32_t artIsAssignableFromCode(const mirror::Class* klass, const mirror::Class* ref_class); @@ -134,6 +141,30 @@ extern "C" void art_quick_throw_no_such_method(int32_t method_idx); extern "C" void art_quick_throw_null_pointer_exception(); extern "C" void art_quick_throw_stack_overflow(void*); +static bool quick_alloc_entry_points_instrumented = false; + +void SetQuickAllocEntryPointsInstrumented(bool instrumented) { + quick_alloc_entry_points_instrumented = instrumented; +} + +void ResetQuickAllocEntryPoints(QuickEntryPoints* qpoints) { + if (quick_alloc_entry_points_instrumented) { + qpoints->pAllocArray = art_quick_alloc_array_instrumented; + qpoints->pAllocArrayWithAccessCheck = art_quick_alloc_array_with_access_check_instrumented; + qpoints->pAllocObject = art_quick_alloc_object_instrumented; + qpoints->pAllocObjectWithAccessCheck = art_quick_alloc_object_with_access_check_instrumented; + qpoints->pCheckAndAllocArray = art_quick_check_and_alloc_array_instrumented; + qpoints->pCheckAndAllocArrayWithAccessCheck = art_quick_check_and_alloc_array_with_access_check_instrumented; + } else { + qpoints->pAllocArray = art_quick_alloc_array; + qpoints->pAllocArrayWithAccessCheck = art_quick_alloc_array_with_access_check; + qpoints->pAllocObject = art_quick_alloc_object; + qpoints->pAllocObjectWithAccessCheck = art_quick_alloc_object_with_access_check; + qpoints->pCheckAndAllocArray = art_quick_check_and_alloc_array; + qpoints->pCheckAndAllocArrayWithAccessCheck = art_quick_check_and_alloc_array_with_access_check; + } +} + void InitEntryPoints(InterpreterEntryPoints* ipoints, JniEntryPoints* jpoints, PortableEntryPoints* ppoints, QuickEntryPoints* qpoints) { // Interpreter @@ -148,12 +179,7 @@ void InitEntryPoints(InterpreterEntryPoints* ipoints, JniEntryPoints* jpoints, ppoints->pPortableToInterpreterBridge = art_portable_to_interpreter_bridge; // Alloc - qpoints->pAllocArray = art_quick_alloc_array; - qpoints->pAllocArrayWithAccessCheck = art_quick_alloc_array_with_access_check; - qpoints->pAllocObject = art_quick_alloc_object; - qpoints->pAllocObjectWithAccessCheck = art_quick_alloc_object_with_access_check; - qpoints->pCheckAndAllocArray = art_quick_check_and_alloc_array; - qpoints->pCheckAndAllocArrayWithAccessCheck = art_quick_check_and_alloc_array_with_access_check; + ResetQuickAllocEntryPoints(qpoints); // Cast qpoints->pInstanceofNonTrivial = artIsAssignableFromCode; diff --git a/runtime/arch/mips/quick_entrypoints_mips.S b/runtime/arch/mips/quick_entrypoints_mips.S index 004fda60f1c..cb8260669f5 100644 --- a/runtime/arch/mips/quick_entrypoints_mips.S +++ b/runtime/arch/mips/quick_entrypoints_mips.S @@ -770,6 +770,16 @@ ENTRY art_quick_alloc_object RETURN_IF_NONZERO END art_quick_alloc_object + .extern artAllocObjectFromCodeInstrumented +ENTRY art_quick_alloc_object_instrumented + GENERATE_GLOBAL_POINTER + SETUP_REF_ONLY_CALLEE_SAVE_FRAME # save callee saves in case of GC + move $a2, rSELF # pass Thread::Current + jal artAllocObjectFromCodeInstrumented # (uint32_t type_idx, Method* method, Thread*, $sp) + move $a3, $sp # pass $sp + RETURN_IF_NONZERO +END art_quick_alloc_object_instrumented + /* * Called by managed code to allocate an object when the caller doesn't know whether it has * access to the created type. @@ -784,6 +794,16 @@ ENTRY art_quick_alloc_object_with_access_check RETURN_IF_NONZERO END art_quick_alloc_object_with_access_check + .extern artAllocObjectFromCodeWithAccessCheckInstrumented +ENTRY art_quick_alloc_object_with_access_check_instrumented + GENERATE_GLOBAL_POINTER + SETUP_REF_ONLY_CALLEE_SAVE_FRAME # save callee saves in case of GC + move $a2, rSELF # pass Thread::Current + jal artAllocObjectFromCodeWithAccessCheckInstrumented # (uint32_t type_idx, Method* method, Thread*, $sp) + move $a3, $sp # pass $sp + RETURN_IF_NONZERO +END art_quick_alloc_object_with_access_check_instrumented + /* * Called by managed code to allocate an array. */ @@ -798,6 +818,17 @@ ENTRY art_quick_alloc_array RETURN_IF_NONZERO END art_quick_alloc_array + .extern artAllocArrayFromCodeInstrumented +ENTRY art_quick_alloc_array_instrumented + GENERATE_GLOBAL_POINTER + SETUP_REF_ONLY_CALLEE_SAVE_FRAME # save callee saves in case of GC + move $a3, rSELF # pass Thread::Current + # artAllocArrayFromCodeInstrumented(uint32_t type_idx, Method* method, int32_t component_count, Thread*, $sp) + jal artAllocArrayFromCodeInstrumented + sw $sp, 16($sp) # pass $sp + RETURN_IF_NONZERO +END art_quick_alloc_array_instrumented + /* * Called by managed code to allocate an array when the caller doesn't know whether it has * access to the created type. @@ -813,6 +844,17 @@ ENTRY art_quick_alloc_array_with_access_check RETURN_IF_NONZERO END art_quick_alloc_array_with_access_check + .extern artAllocArrayFromCodeWithAccessCheckInstrumented +ENTRY art_quick_alloc_array_with_access_check_instrumented + GENERATE_GLOBAL_POINTER + SETUP_REF_ONLY_CALLEE_SAVE_FRAME # save callee saves in case of GC + move $a3, rSELF # pass Thread::Current + # artAllocArrayFromCodeWithAccessCheckInstrumented(type_idx, method, component_count, Thread*, $sp) + jal artAllocArrayFromCodeWithAccessCheckInstrumented + sw $sp, 16($sp) # pass $sp + RETURN_IF_NONZERO +END art_quick_alloc_array_with_access_check_instrumented + /* * Called by managed code to allocate an array in a special case for FILLED_NEW_ARRAY. */ @@ -827,6 +869,17 @@ ENTRY art_quick_check_and_alloc_array RETURN_IF_NONZERO END art_quick_check_and_alloc_array + .extern artCheckAndAllocArrayFromCodeInstrumented +ENTRY art_quick_check_and_alloc_array_instrumented + GENERATE_GLOBAL_POINTER + SETUP_REF_ONLY_CALLEE_SAVE_FRAME # save callee saves in case of GC + move $a3, rSELF # pass Thread::Current + # artCheckAndAllocArrayFromCodeInstrumented(uint32_t type_idx, Method* method, int32_t count, Thread* , $sp) + jal artCheckAndAllocArrayFromCodeInstrumented + sw $sp, 16($sp) # pass $sp + RETURN_IF_NONZERO +END art_quick_check_and_alloc_array_instrumented + /* * Called by managed code to allocate an array in a special case for FILLED_NEW_ARRAY. */ @@ -841,6 +894,17 @@ ENTRY art_quick_check_and_alloc_array_with_access_check RETURN_IF_NONZERO END art_quick_check_and_alloc_array_with_access_check + .extern artCheckAndAllocArrayFromCodeWithAccessCheckInstrumented +ENTRY art_quick_check_and_alloc_array_with_access_check_instrumented + GENERATE_GLOBAL_POINTER + SETUP_REF_ONLY_CALLEE_SAVE_FRAME # save callee saves in case of GC + move $a3, rSELF # pass Thread::Current + # artCheckAndAllocArrayFromCodeWithAccessCheckInstrumented(type_idx, method, count, Thread* , $sp) + jal artCheckAndAllocArrayFromCodeWithAccessCheckInstrumented + sw $sp, 16($sp) # pass $sp + RETURN_IF_NONZERO +END art_quick_check_and_alloc_array_with_access_check_instrumented + /* * Called by managed code when the value in rSUSPEND has been decremented to 0. */ diff --git a/runtime/arch/x86/entrypoints_init_x86.cc b/runtime/arch/x86/entrypoints_init_x86.cc index abc2990cc0a..4c87e076080 100644 --- a/runtime/arch/x86/entrypoints_init_x86.cc +++ b/runtime/arch/x86/entrypoints_init_x86.cc @@ -40,6 +40,13 @@ extern "C" void* art_quick_alloc_object_with_access_check(uint32_t type_idx, voi extern "C" void* art_quick_check_and_alloc_array(uint32_t, void*, int32_t); extern "C" void* art_quick_check_and_alloc_array_with_access_check(uint32_t, void*, int32_t); +extern "C" void* art_quick_alloc_array_instrumented(uint32_t, void*, int32_t); +extern "C" void* art_quick_alloc_array_with_access_check_instrumented(uint32_t, void*, int32_t); +extern "C" void* art_quick_alloc_object_instrumented(uint32_t type_idx, void* method); +extern "C" void* art_quick_alloc_object_with_access_check_instrumented(uint32_t type_idx, void* method); +extern "C" void* art_quick_check_and_alloc_array_instrumented(uint32_t, void*, int32_t); +extern "C" void* art_quick_check_and_alloc_array_with_access_check_instrumented(uint32_t, void*, int32_t); + // Cast entrypoints. extern "C" uint32_t art_quick_is_assignable(const mirror::Class* klass, const mirror::Class* ref_class); @@ -116,6 +123,30 @@ extern "C" void art_quick_throw_no_such_method(int32_t method_idx); extern "C" void art_quick_throw_null_pointer_exception(); extern "C" void art_quick_throw_stack_overflow(void*); +static bool quick_alloc_entry_points_instrumented = false; + +void SetQuickAllocEntryPointsInstrumented(bool instrumented) { + quick_alloc_entry_points_instrumented = instrumented; +} + +void ResetQuickAllocEntryPoints(QuickEntryPoints* qpoints) { + if (quick_alloc_entry_points_instrumented) { + qpoints->pAllocArray = art_quick_alloc_array_instrumented; + qpoints->pAllocArrayWithAccessCheck = art_quick_alloc_array_with_access_check_instrumented; + qpoints->pAllocObject = art_quick_alloc_object_instrumented; + qpoints->pAllocObjectWithAccessCheck = art_quick_alloc_object_with_access_check_instrumented; + qpoints->pCheckAndAllocArray = art_quick_check_and_alloc_array_instrumented; + qpoints->pCheckAndAllocArrayWithAccessCheck = art_quick_check_and_alloc_array_with_access_check_instrumented; + } else { + qpoints->pAllocArray = art_quick_alloc_array; + qpoints->pAllocArrayWithAccessCheck = art_quick_alloc_array_with_access_check; + qpoints->pAllocObject = art_quick_alloc_object; + qpoints->pAllocObjectWithAccessCheck = art_quick_alloc_object_with_access_check; + qpoints->pCheckAndAllocArray = art_quick_check_and_alloc_array; + qpoints->pCheckAndAllocArrayWithAccessCheck = art_quick_check_and_alloc_array_with_access_check; + } +} + void InitEntryPoints(InterpreterEntryPoints* ipoints, JniEntryPoints* jpoints, PortableEntryPoints* ppoints, QuickEntryPoints* qpoints) { // Interpreter @@ -130,12 +161,7 @@ void InitEntryPoints(InterpreterEntryPoints* ipoints, JniEntryPoints* jpoints, ppoints->pPortableToInterpreterBridge = art_portable_to_interpreter_bridge; // Alloc - qpoints->pAllocArray = art_quick_alloc_array; - qpoints->pAllocArrayWithAccessCheck = art_quick_alloc_array_with_access_check; - qpoints->pAllocObject = art_quick_alloc_object; - qpoints->pAllocObjectWithAccessCheck = art_quick_alloc_object_with_access_check; - qpoints->pCheckAndAllocArray = art_quick_check_and_alloc_array; - qpoints->pCheckAndAllocArrayWithAccessCheck = art_quick_check_and_alloc_array_with_access_check; + ResetQuickAllocEntryPoints(qpoints); // Cast qpoints->pInstanceofNonTrivial = art_quick_is_assignable; diff --git a/runtime/arch/x86/quick_entrypoints_x86.S b/runtime/arch/x86/quick_entrypoints_x86.S index dbf552faafd..06b220391f2 100644 --- a/runtime/arch/x86/quick_entrypoints_x86.S +++ b/runtime/arch/x86/quick_entrypoints_x86.S @@ -389,6 +389,13 @@ THREE_ARG_DOWNCALL art_quick_alloc_array_with_access_check, artAllocArrayFromCod THREE_ARG_DOWNCALL art_quick_check_and_alloc_array, artCheckAndAllocArrayFromCode, RETURN_IF_EAX_NOT_ZERO THREE_ARG_DOWNCALL art_quick_check_and_alloc_array_with_access_check, artCheckAndAllocArrayFromCodeWithAccessCheck, RETURN_IF_EAX_NOT_ZERO +TWO_ARG_DOWNCALL art_quick_alloc_object_instrumented, artAllocObjectFromCodeInstrumented, RETURN_IF_EAX_NOT_ZERO +TWO_ARG_DOWNCALL art_quick_alloc_object_with_access_check_instrumented, artAllocObjectFromCodeWithAccessCheckInstrumented, RETURN_IF_EAX_NOT_ZERO +THREE_ARG_DOWNCALL art_quick_alloc_array_instrumented, artAllocArrayFromCodeInstrumented, RETURN_IF_EAX_NOT_ZERO +THREE_ARG_DOWNCALL art_quick_alloc_array_with_access_check_instrumented, artAllocArrayFromCodeWithAccessCheckInstrumented, RETURN_IF_EAX_NOT_ZERO +THREE_ARG_DOWNCALL art_quick_check_and_alloc_array_instrumented, artCheckAndAllocArrayFromCodeInstrumented, RETURN_IF_EAX_NOT_ZERO +THREE_ARG_DOWNCALL art_quick_check_and_alloc_array_with_access_check_instrumented, artCheckAndAllocArrayFromCodeWithAccessCheckInstrumented, RETURN_IF_EAX_NOT_ZERO + TWO_ARG_DOWNCALL art_quick_resolve_string, artResolveStringFromCode, RETURN_IF_EAX_NOT_ZERO TWO_ARG_DOWNCALL art_quick_initialize_static_storage, artInitializeStaticStorageFromCode, RETURN_IF_EAX_NOT_ZERO TWO_ARG_DOWNCALL art_quick_initialize_type, artInitializeTypeFromCode, RETURN_IF_EAX_NOT_ZERO diff --git a/runtime/debugger.cc b/runtime/debugger.cc index 2eca7344578..e57137fb963 100644 --- a/runtime/debugger.cc +++ b/runtime/debugger.cc @@ -3487,7 +3487,9 @@ void Dbg::SetAllocTrackingEnabled(bool enabled) { recent_allocation_records_ = new AllocRecord[gAllocRecordMax]; CHECK(recent_allocation_records_ != NULL); } + Runtime::Current()->InstrumentQuickAllocEntryPoints(); } else { + Runtime::Current()->UninstrumentQuickAllocEntryPoints(); delete[] recent_allocation_records_; recent_allocation_records_ = NULL; } diff --git a/runtime/entrypoints/entrypoint_utils.cc b/runtime/entrypoints/entrypoint_utils.cc index 52f8c81ab61..d9c9e3141ab 100644 --- a/runtime/entrypoints/entrypoint_utils.cc +++ b/runtime/entrypoints/entrypoint_utils.cc @@ -33,20 +33,20 @@ namespace art { -// Helper function to allocate array for FILLED_NEW_ARRAY. -mirror::Array* CheckAndAllocArrayFromCode(uint32_t type_idx, mirror::ArtMethod* referrer, - int32_t component_count, Thread* self, - bool access_check) { +static inline bool CheckFilledNewArrayAlloc(uint32_t type_idx, mirror::ArtMethod* referrer, + int32_t component_count, Thread* self, + bool access_check, mirror::Class** klass_ptr) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { if (UNLIKELY(component_count < 0)) { ThrowNegativeArraySizeException(component_count); - return NULL; // Failure + return false; // Failure } - mirror::Class* klass = referrer->GetDexCacheResolvedTypes()->Get(type_idx); + mirror::Class* klass = referrer->GetDexCacheResolvedTypes()->GetWithoutChecks(type_idx); if (UNLIKELY(klass == NULL)) { // Not in dex cache so try to resolve klass = Runtime::Current()->GetClassLinker()->ResolveType(type_idx, referrer); if (klass == NULL) { // Error DCHECK(self->IsExceptionPending()); - return NULL; // Failure + return false; // Failure } } if (UNLIKELY(klass->IsPrimitive() && !klass->IsPrimitiveInt())) { @@ -60,18 +60,40 @@ mirror::Array* CheckAndAllocArrayFromCode(uint32_t type_idx, mirror::ArtMethod* "Found type %s; filled-new-array not implemented for anything but \'int\'", PrettyDescriptor(klass).c_str()); } - return NULL; // Failure - } else { - if (access_check) { - mirror::Class* referrer_klass = referrer->GetDeclaringClass(); - if (UNLIKELY(!referrer_klass->CanAccess(klass))) { - ThrowIllegalAccessErrorClass(referrer_klass, klass); - return NULL; // Failure - } + return false; // Failure + } + if (access_check) { + mirror::Class* referrer_klass = referrer->GetDeclaringClass(); + if (UNLIKELY(!referrer_klass->CanAccess(klass))) { + ThrowIllegalAccessErrorClass(referrer_klass, klass); + return false; // Failure } - DCHECK(klass->IsArrayClass()) << PrettyClass(klass); - return mirror::Array::Alloc(self, klass, component_count); } + DCHECK(klass->IsArrayClass()) << PrettyClass(klass); + *klass_ptr = klass; + return true; +} + +// Helper function to allocate array for FILLED_NEW_ARRAY. +mirror::Array* CheckAndAllocArrayFromCode(uint32_t type_idx, mirror::ArtMethod* referrer, + int32_t component_count, Thread* self, + bool access_check) { + mirror::Class* klass; + if (UNLIKELY(!CheckFilledNewArrayAlloc(type_idx, referrer, component_count, self, access_check, &klass))) { + return NULL; + } + return mirror::Array::AllocUninstrumented(self, klass, component_count); +} + +// Helper function to allocate array for FILLED_NEW_ARRAY. +mirror::Array* CheckAndAllocArrayFromCodeInstrumented(uint32_t type_idx, mirror::ArtMethod* referrer, + int32_t component_count, Thread* self, + bool access_check) { + mirror::Class* klass; + if (UNLIKELY(!CheckFilledNewArrayAlloc(type_idx, referrer, component_count, self, access_check, &klass))) { + return NULL; + } + return mirror::Array::AllocInstrumented(self, klass, component_count); } mirror::ArtField* FindFieldFromCode(uint32_t field_idx, const mirror::ArtMethod* referrer, @@ -405,5 +427,4 @@ JValue InvokeProxyInvocationHandler(ScopedObjectAccessUnchecked& soa, const char return zero; } } - } // namespace art diff --git a/runtime/entrypoints/entrypoint_utils.h b/runtime/entrypoints/entrypoint_utils.h index fff7b71e4bf..e87dc96c975 100644 --- a/runtime/entrypoints/entrypoint_utils.h +++ b/runtime/entrypoints/entrypoint_utils.h @@ -40,21 +40,18 @@ namespace mirror { class Object; } // namespace mirror -// Given the context of a calling Method, use its DexCache to resolve a type to a Class. If it -// cannot be resolved, throw an error. If it can, use it to create an instance. -// When verification/compiler hasn't been able to verify access, optionally perform an access -// check. -static inline mirror::Object* AllocObjectFromCode(uint32_t type_idx, mirror::ArtMethod* method, - Thread* self, - bool access_check) +static inline bool CheckObjectAlloc(uint32_t type_idx, mirror::ArtMethod* method, + Thread* self, + bool access_check, + mirror::Class** klass_ptr) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - mirror::Class* klass = method->GetDexCacheResolvedTypes()->Get(type_idx); + mirror::Class* klass = method->GetDexCacheResolvedTypes()->GetWithoutChecks(type_idx); Runtime* runtime = Runtime::Current(); if (UNLIKELY(klass == NULL)) { klass = runtime->GetClassLinker()->ResolveType(type_idx, method); if (klass == NULL) { DCHECK(self->IsExceptionPending()); - return NULL; // Failure + return false; // Failure } } if (access_check) { @@ -62,40 +59,63 @@ static inline mirror::Object* AllocObjectFromCode(uint32_t type_idx, mirror::Art ThrowLocation throw_location = self->GetCurrentLocationForThrow(); self->ThrowNewException(throw_location, "Ljava/lang/InstantiationError;", PrettyDescriptor(klass).c_str()); - return NULL; // Failure + return false; // Failure } mirror::Class* referrer = method->GetDeclaringClass(); if (UNLIKELY(!referrer->CanAccess(klass))) { ThrowIllegalAccessErrorClass(referrer, klass); - return NULL; // Failure + return false; // Failure } } if (!klass->IsInitialized() && !runtime->GetClassLinker()->EnsureInitialized(klass, true, true)) { DCHECK(self->IsExceptionPending()); - return NULL; // Failure + return false; // Failure } - return klass->AllocObject(self); + *klass_ptr = klass; + return true; } -// Given the context of a calling Method, use its DexCache to resolve a type to an array Class. If -// it cannot be resolved, throw an error. If it can, use it to create an array. +// Given the context of a calling Method, use its DexCache to resolve a type to a Class. If it +// cannot be resolved, throw an error. If it can, use it to create an instance. // When verification/compiler hasn't been able to verify access, optionally perform an access // check. -static inline mirror::Array* AllocArrayFromCode(uint32_t type_idx, mirror::ArtMethod* method, - int32_t component_count, - Thread* self, bool access_check) +static inline mirror::Object* AllocObjectFromCode(uint32_t type_idx, mirror::ArtMethod* method, + Thread* self, + bool access_check) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + mirror::Class* klass; + if (UNLIKELY(!CheckObjectAlloc(type_idx, method, self, access_check, &klass))) { + return NULL; + } + return klass->AllocObjectUninstrumented(self); +} + +static inline mirror::Object* AllocObjectFromCodeInstrumented(uint32_t type_idx, mirror::ArtMethod* method, + Thread* self, + bool access_check) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + mirror::Class* klass; + if (UNLIKELY(!CheckObjectAlloc(type_idx, method, self, access_check, &klass))) { + return NULL; + } + return klass->AllocObjectInstrumented(self); +} + +static inline bool CheckArrayAlloc(uint32_t type_idx, mirror::ArtMethod* method, + int32_t component_count, + bool access_check, mirror::Class** klass_ptr) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { if (UNLIKELY(component_count < 0)) { ThrowNegativeArraySizeException(component_count); - return NULL; // Failure + return false; // Failure } - mirror::Class* klass = method->GetDexCacheResolvedTypes()->Get(type_idx); + mirror::Class* klass = method->GetDexCacheResolvedTypes()->GetWithoutChecks(type_idx); if (UNLIKELY(klass == NULL)) { // Not in dex cache so try to resolve klass = Runtime::Current()->GetClassLinker()->ResolveType(type_idx, method); if (klass == NULL) { // Error DCHECK(Thread::Current()->IsExceptionPending()); - return NULL; // Failure + return false; // Failure } CHECK(klass->IsArrayClass()) << PrettyClass(klass); } @@ -103,10 +123,37 @@ static inline mirror::Array* AllocArrayFromCode(uint32_t type_idx, mirror::ArtMe mirror::Class* referrer = method->GetDeclaringClass(); if (UNLIKELY(!referrer->CanAccess(klass))) { ThrowIllegalAccessErrorClass(referrer, klass); - return NULL; // Failure + return false; // Failure } } - return mirror::Array::Alloc(self, klass, component_count); + *klass_ptr = klass; + return true; +} + +// Given the context of a calling Method, use its DexCache to resolve a type to an array Class. If +// it cannot be resolved, throw an error. If it can, use it to create an array. +// When verification/compiler hasn't been able to verify access, optionally perform an access +// check. +static inline mirror::Array* AllocArrayFromCode(uint32_t type_idx, mirror::ArtMethod* method, + int32_t component_count, + Thread* self, bool access_check) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + mirror::Class* klass; + if (UNLIKELY(!CheckArrayAlloc(type_idx, method, component_count, access_check, &klass))) { + return NULL; + } + return mirror::Array::AllocUninstrumented(self, klass, component_count); +} + +static inline mirror::Array* AllocArrayFromCodeInstrumented(uint32_t type_idx, mirror::ArtMethod* method, + int32_t component_count, + Thread* self, bool access_check) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + mirror::Class* klass; + if (UNLIKELY(!CheckArrayAlloc(type_idx, method, component_count, access_check, &klass))) { + return NULL; + } + return mirror::Array::AllocInstrumented(self, klass, component_count); } extern mirror::Array* CheckAndAllocArrayFromCode(uint32_t type_idx, mirror::ArtMethod* method, @@ -114,6 +161,11 @@ extern mirror::Array* CheckAndAllocArrayFromCode(uint32_t type_idx, mirror::ArtM Thread* self, bool access_check) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); +extern mirror::Array* CheckAndAllocArrayFromCodeInstrumented(uint32_t type_idx, mirror::ArtMethod* method, + int32_t component_count, + Thread* self, bool access_check) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + // Type of find field operation for fast and slow case. enum FindFieldType { InstanceObjectRead, diff --git a/runtime/entrypoints/quick/quick_alloc_entrypoints.cc b/runtime/entrypoints/quick/quick_alloc_entrypoints.cc index 420e63a1bb7..6f7b1ab19bf 100644 --- a/runtime/entrypoints/quick/quick_alloc_entrypoints.cc +++ b/runtime/entrypoints/quick/quick_alloc_entrypoints.cc @@ -76,4 +76,57 @@ extern "C" mirror::Array* artCheckAndAllocArrayFromCodeWithAccessCheck(uint32_t return CheckAndAllocArrayFromCode(type_idx, method, component_count, self, true); } +extern "C" mirror::Object* artAllocObjectFromCodeInstrumented(uint32_t type_idx, mirror::ArtMethod* method, + Thread* self, mirror::ArtMethod** sp) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly); + return AllocObjectFromCodeInstrumented(type_idx, method, self, false); +} + +extern "C" mirror::Object* artAllocObjectFromCodeWithAccessCheckInstrumented(uint32_t type_idx, + mirror::ArtMethod* method, + Thread* self, + mirror::ArtMethod** sp) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly); + return AllocObjectFromCodeInstrumented(type_idx, method, self, true); +} + +extern "C" mirror::Array* artAllocArrayFromCodeInstrumented(uint32_t type_idx, mirror::ArtMethod* method, + int32_t component_count, Thread* self, + mirror::ArtMethod** sp) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly); + return AllocArrayFromCodeInstrumented(type_idx, method, component_count, self, false); +} + +extern "C" mirror::Array* artAllocArrayFromCodeWithAccessCheckInstrumented(uint32_t type_idx, + mirror::ArtMethod* method, + int32_t component_count, + Thread* self, + mirror::ArtMethod** sp) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly); + return AllocArrayFromCodeInstrumented(type_idx, method, component_count, self, true); +} + +extern "C" mirror::Array* artCheckAndAllocArrayFromCodeInstrumented(uint32_t type_idx, + mirror::ArtMethod* method, + int32_t component_count, Thread* self, + mirror::ArtMethod** sp) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly); + return CheckAndAllocArrayFromCodeInstrumented(type_idx, method, component_count, self, false); +} + +extern "C" mirror::Array* artCheckAndAllocArrayFromCodeWithAccessCheckInstrumented(uint32_t type_idx, + mirror::ArtMethod* method, + int32_t component_count, + Thread* self, + mirror::ArtMethod** sp) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly); + return CheckAndAllocArrayFromCodeInstrumented(type_idx, method, component_count, self, true); +} + } // namespace art diff --git a/runtime/gc/heap-inl.h b/runtime/gc/heap-inl.h new file mode 100644 index 00000000000..b7ef77c35a2 --- /dev/null +++ b/runtime/gc/heap-inl.h @@ -0,0 +1,188 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ART_RUNTIME_GC_HEAP_INL_H_ +#define ART_RUNTIME_GC_HEAP_INL_H_ + +#include "heap.h" + +#include "debugger.h" +#include "gc/space/dlmalloc_space-inl.h" +#include "gc/space/large_object_space.h" +#include "object_utils.h" +#include "runtime.h" +#include "thread.h" +#include "thread-inl.h" + +namespace art { +namespace gc { + +inline mirror::Object* Heap::AllocObjectUninstrumented(Thread* self, mirror::Class* c, size_t byte_count) { + DebugCheckPreconditionsForAllobObject(c, byte_count); + mirror::Object* obj; + size_t bytes_allocated; + AllocationTimer alloc_timer(this, &obj); + bool large_object_allocation = TryAllocLargeObjectUninstrumented(self, c, byte_count, + &obj, &bytes_allocated); + if (LIKELY(!large_object_allocation)) { + // Non-large object allocation. + obj = AllocateUninstrumented(self, alloc_space_, byte_count, &bytes_allocated); + // Ensure that we did not allocate into a zygote space. + DCHECK(obj == NULL || !have_zygote_space_ || !FindSpaceFromObject(obj, false)->IsZygoteSpace()); + } + if (LIKELY(obj != NULL)) { + obj->SetClass(c); + // Record allocation after since we want to use the atomic add for the atomic fence to guard + // the SetClass since we do not want the class to appear NULL in another thread. + size_t new_num_bytes_allocated = RecordAllocationUninstrumented(bytes_allocated, obj); + DCHECK(!Dbg::IsAllocTrackingEnabled()); + CheckConcurrentGC(self, new_num_bytes_allocated, obj); + if (kDesiredHeapVerification > kNoHeapVerification) { + VerifyObject(obj); + } + return obj; + } + ThrowOutOfMemoryError(self, byte_count, large_object_allocation); + return NULL; +} + +inline size_t Heap::RecordAllocationUninstrumented(size_t size, mirror::Object* obj) { + DCHECK(obj != NULL); + DCHECK_GT(size, 0u); + size_t old_num_bytes_allocated = static_cast(num_bytes_allocated_.fetch_add(size)); + + DCHECK(!Runtime::Current()->HasStatsEnabled()); + + // This is safe to do since the GC will never free objects which are neither in the allocation + // stack or the live bitmap. + while (!allocation_stack_->AtomicPushBack(obj)) { + CollectGarbageInternal(collector::kGcTypeSticky, kGcCauseForAlloc, false); + } + + return old_num_bytes_allocated + size; +} + +inline mirror::Object* Heap::TryToAllocateUninstrumented(Thread* self, space::AllocSpace* space, size_t alloc_size, + bool grow, size_t* bytes_allocated) { + if (UNLIKELY(IsOutOfMemoryOnAllocation(alloc_size, grow))) { + return NULL; + } + DCHECK(!running_on_valgrind_); + return space->Alloc(self, alloc_size, bytes_allocated); +} + +// DlMallocSpace-specific version. +inline mirror::Object* Heap::TryToAllocateUninstrumented(Thread* self, space::DlMallocSpace* space, size_t alloc_size, + bool grow, size_t* bytes_allocated) { + if (UNLIKELY(IsOutOfMemoryOnAllocation(alloc_size, grow))) { + return NULL; + } + DCHECK(!running_on_valgrind_); + return space->AllocNonvirtual(self, alloc_size, bytes_allocated); +} + +template +inline mirror::Object* Heap::AllocateUninstrumented(Thread* self, T* space, size_t alloc_size, + size_t* bytes_allocated) { + // Since allocation can cause a GC which will need to SuspendAll, make sure all allocations are + // done in the runnable state where suspension is expected. + DCHECK_EQ(self->GetState(), kRunnable); + self->AssertThreadSuspensionIsAllowable(); + + mirror::Object* ptr = TryToAllocateUninstrumented(self, space, alloc_size, false, bytes_allocated); + if (LIKELY(ptr != NULL)) { + return ptr; + } + return AllocateInternalWithGc(self, space, alloc_size, bytes_allocated); +} + +inline bool Heap::TryAllocLargeObjectUninstrumented(Thread* self, mirror::Class* c, size_t byte_count, + mirror::Object** obj_ptr, size_t* bytes_allocated) { + bool large_object_allocation = ShouldAllocLargeObject(c, byte_count); + if (UNLIKELY(large_object_allocation)) { + mirror::Object* obj = AllocateUninstrumented(self, large_object_space_, byte_count, bytes_allocated); + // Make sure that our large object didn't get placed anywhere within the space interval or else + // it breaks the immune range. + DCHECK(obj == NULL || + reinterpret_cast(obj) < continuous_spaces_.front()->Begin() || + reinterpret_cast(obj) >= continuous_spaces_.back()->End()); + *obj_ptr = obj; + } + return large_object_allocation; +} + +inline void Heap::DebugCheckPreconditionsForAllobObject(mirror::Class* c, size_t byte_count) { + DCHECK(c == NULL || (c->IsClassClass() && byte_count >= sizeof(mirror::Class)) || + (c->IsVariableSize() || c->GetObjectSize() == byte_count) || + ClassHelper(c).GetDescriptorAsStringPiece().length() == 0); + DCHECK_GE(byte_count, sizeof(mirror::Object)); +} + +inline Heap::AllocationTimer::AllocationTimer(Heap* heap, mirror::Object** allocated_obj_ptr) + : heap_(heap), allocated_obj_ptr_(allocated_obj_ptr) { + if (kMeasureAllocationTime) { + allocation_start_time_ = NanoTime() / kTimeAdjust; + } +} + +inline Heap::AllocationTimer::~AllocationTimer() { + if (kMeasureAllocationTime) { + mirror::Object* allocated_obj = *allocated_obj_ptr_; + // Only if the allocation succeeded, record the time. + if (allocated_obj != NULL) { + uint64_t allocation_end_time = NanoTime() / kTimeAdjust; + heap_->total_allocation_time_.fetch_add(allocation_end_time - allocation_start_time_); + } + } +}; + +inline bool Heap::ShouldAllocLargeObject(mirror::Class* c, size_t byte_count) { + // We need to have a zygote space or else our newly allocated large object can end up in the + // Zygote resulting in it being prematurely freed. + // We can only do this for primitive objects since large objects will not be within the card table + // range. This also means that we rely on SetClass not dirtying the object's card. + return byte_count >= kLargeObjectThreshold && have_zygote_space_ && c->IsPrimitiveArray(); +} + +inline bool Heap::IsOutOfMemoryOnAllocation(size_t alloc_size, bool grow) { + size_t new_footprint = num_bytes_allocated_ + alloc_size; + if (UNLIKELY(new_footprint > max_allowed_footprint_)) { + if (UNLIKELY(new_footprint > growth_limit_)) { + return true; + } + if (!concurrent_gc_) { + if (!grow) { + return true; + } else { + max_allowed_footprint_ = new_footprint; + } + } + } + return false; +} + +inline void Heap::CheckConcurrentGC(Thread* self, size_t new_num_bytes_allocated, mirror::Object* obj) { + if (UNLIKELY(new_num_bytes_allocated >= concurrent_start_bytes_)) { + // The SirtRef is necessary since the calls in RequestConcurrentGC are a safepoint. + SirtRef ref(self, obj); + RequestConcurrentGC(self); + } +} + +} // namespace gc +} // namespace art + +#endif // ART_RUNTIME_GC_HEAP_INL_H_ diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc index cefde041386..c0e46ac1658 100644 --- a/runtime/gc/heap.cc +++ b/runtime/gc/heap.cc @@ -39,6 +39,7 @@ #include "gc/space/image_space.h" #include "gc/space/large_object_space.h" #include "gc/space/space-inl.h" +#include "heap-inl.h" #include "image.h" #include "invoke_arg_array_builder.h" #include "mirror/art_field-inl.h" @@ -63,8 +64,6 @@ static constexpr size_t kGcAlotInterval = KB; static constexpr bool kDumpGcPerformanceOnShutdown = false; // Minimum amount of remaining bytes before a concurrent GC is triggered. static constexpr size_t kMinConcurrentRemainingBytes = 128 * KB; -// If true, measure the total allocation time. -static constexpr bool kMeasureAllocationTime = false; Heap::Heap(size_t initial_size, size_t growth_limit, size_t min_free, size_t max_free, double target_utilization, size_t capacity, const std::string& original_image_file_name, @@ -105,7 +104,6 @@ Heap::Heap(size_t initial_size, size_t growth_limit, size_t min_free, size_t max : std::numeric_limits::max()), total_bytes_freed_ever_(0), total_objects_freed_ever_(0), - large_object_threshold_(3 * kPageSize), num_bytes_allocated_(0), native_bytes_allocated_(0), gc_memory_overhead_(0), @@ -238,6 +236,11 @@ Heap::Heap(size_t initial_size, size_t growth_limit, size_t min_free, size_t max } CHECK_NE(max_allowed_footprint_, 0U); + + if (running_on_valgrind_) { + Runtime::Current()->InstrumentQuickAllocEntryPoints(); + } + if (VLOG_IS_ON(heap) || VLOG_IS_ON(startup)) { LOG(INFO) << "Heap() exiting"; } @@ -551,81 +554,69 @@ static void MSpaceChunkCallback(void* start, void* end, size_t used_bytes, void* } } -mirror::Object* Heap::AllocObject(Thread* self, mirror::Class* c, size_t byte_count) { - DCHECK(c == NULL || (c->IsClassClass() && byte_count >= sizeof(mirror::Class)) || - (c->IsVariableSize() || c->GetObjectSize() == byte_count) || - ClassHelper(c).GetDescriptorAsStringPiece().length() == 0); - DCHECK_GE(byte_count, sizeof(mirror::Object)); - - mirror::Object* obj = NULL; - size_t bytes_allocated = 0; - uint64_t allocation_start = 0; - if (UNLIKELY(kMeasureAllocationTime)) { - allocation_start = NanoTime() / kTimeAdjust; +void Heap::ThrowOutOfMemoryError(Thread* self, size_t byte_count, bool large_object_allocation) { + std::ostringstream oss; + int64_t total_bytes_free = GetFreeMemory(); + oss << "Failed to allocate a " << byte_count << " byte allocation with " << total_bytes_free + << " free bytes"; + // If the allocation failed due to fragmentation, print out the largest continuous allocation. + if (!large_object_allocation && total_bytes_free >= byte_count) { + size_t max_contiguous_allocation = 0; + for (const auto& space : continuous_spaces_) { + if (space->IsDlMallocSpace()) { + space->AsDlMallocSpace()->Walk(MSpaceChunkCallback, &max_contiguous_allocation); + } + } + oss << "; failed due to fragmentation (largest possible contiguous allocation " + << max_contiguous_allocation << " bytes)"; } + self->ThrowOutOfMemoryError(oss.str().c_str()); +} - // We need to have a zygote space or else our newly allocated large object can end up in the - // Zygote resulting in it being prematurely freed. - // We can only do this for primitive objects since large objects will not be within the card table - // range. This also means that we rely on SetClass not dirtying the object's card. - bool large_object_allocation = - byte_count >= large_object_threshold_ && have_zygote_space_ && c->IsPrimitiveArray(); +inline bool Heap::TryAllocLargeObjectInstrumented(Thread* self, mirror::Class* c, size_t byte_count, + mirror::Object** obj_ptr, size_t* bytes_allocated) { + bool large_object_allocation = ShouldAllocLargeObject(c, byte_count); if (UNLIKELY(large_object_allocation)) { - obj = Allocate(self, large_object_space_, byte_count, &bytes_allocated); + mirror::Object* obj = AllocateInstrumented(self, large_object_space_, byte_count, bytes_allocated); // Make sure that our large object didn't get placed anywhere within the space interval or else // it breaks the immune range. DCHECK(obj == NULL || reinterpret_cast(obj) < continuous_spaces_.front()->Begin() || reinterpret_cast(obj) >= continuous_spaces_.back()->End()); - } else { - obj = Allocate(self, alloc_space_, byte_count, &bytes_allocated); + *obj_ptr = obj; + } + return large_object_allocation; +} + +mirror::Object* Heap::AllocObjectInstrumented(Thread* self, mirror::Class* c, size_t byte_count) { + DebugCheckPreconditionsForAllobObject(c, byte_count); + mirror::Object* obj; + size_t bytes_allocated; + AllocationTimer alloc_timer(this, &obj); + bool large_object_allocation = TryAllocLargeObjectInstrumented(self, c, byte_count, + &obj, &bytes_allocated); + if (LIKELY(!large_object_allocation)) { + // Non-large object allocation. + obj = AllocateInstrumented(self, alloc_space_, byte_count, &bytes_allocated); // Ensure that we did not allocate into a zygote space. DCHECK(obj == NULL || !have_zygote_space_ || !FindSpaceFromObject(obj, false)->IsZygoteSpace()); } - if (LIKELY(obj != NULL)) { obj->SetClass(c); - // Record allocation after since we want to use the atomic add for the atomic fence to guard // the SetClass since we do not want the class to appear NULL in another thread. - RecordAllocation(bytes_allocated, obj); - + size_t new_num_bytes_allocated = RecordAllocationInstrumented(bytes_allocated, obj); if (Dbg::IsAllocTrackingEnabled()) { Dbg::RecordAllocation(c, byte_count); } - if (UNLIKELY(static_cast(num_bytes_allocated_) >= concurrent_start_bytes_)) { - // The SirtRef is necessary since the calls in RequestConcurrentGC are a safepoint. - SirtRef ref(self, obj); - RequestConcurrentGC(self); - } + CheckConcurrentGC(self, new_num_bytes_allocated, obj); if (kDesiredHeapVerification > kNoHeapVerification) { VerifyObject(obj); } - - if (UNLIKELY(kMeasureAllocationTime)) { - total_allocation_time_.fetch_add(NanoTime() / kTimeAdjust - allocation_start); - } - return obj; - } else { - std::ostringstream oss; - int64_t total_bytes_free = GetFreeMemory(); - oss << "Failed to allocate a " << byte_count << " byte allocation with " << total_bytes_free - << " free bytes"; - // If the allocation failed due to fragmentation, print out the largest continuous allocation. - if (!large_object_allocation && total_bytes_free >= byte_count) { - size_t max_contiguous_allocation = 0; - for (const auto& space : continuous_spaces_) { - if (space->IsDlMallocSpace()) { - space->AsDlMallocSpace()->Walk(MSpaceChunkCallback, &max_contiguous_allocation); - } - } - oss << "; failed due to fragmentation (largest possible contiguous allocation " - << max_contiguous_allocation << " bytes)"; - } - self->ThrowOutOfMemoryError(oss.str().c_str()); - return NULL; } + ThrowOutOfMemoryError(self, byte_count, large_object_allocation); + return NULL; } bool Heap::IsHeapAddress(const mirror::Object* obj) { @@ -768,10 +759,10 @@ void Heap::VerifyHeap() { GetLiveBitmap()->Walk(Heap::VerificationCallback, this); } -inline void Heap::RecordAllocation(size_t size, mirror::Object* obj) { +inline size_t Heap::RecordAllocationInstrumented(size_t size, mirror::Object* obj) { DCHECK(obj != NULL); DCHECK_GT(size, 0u); - num_bytes_allocated_.fetch_add(size); + size_t old_num_bytes_allocated = static_cast(num_bytes_allocated_.fetch_add(size)); if (Runtime::Current()->HasStatsEnabled()) { RuntimeStats* thread_stats = Thread::Current()->GetStats(); @@ -789,6 +780,8 @@ inline void Heap::RecordAllocation(size_t size, mirror::Object* obj) { while (!allocation_stack_->AtomicPushBack(obj)) { CollectGarbageInternal(collector::kGcTypeSticky, kGcCauseForAlloc, false); } + + return old_num_bytes_allocated + size; } void Heap::RecordFree(size_t freed_objects, size_t freed_bytes) { @@ -807,25 +800,8 @@ void Heap::RecordFree(size_t freed_objects, size_t freed_bytes) { } } -inline bool Heap::IsOutOfMemoryOnAllocation(size_t alloc_size, bool grow) { - size_t new_footprint = num_bytes_allocated_ + alloc_size; - if (UNLIKELY(new_footprint > max_allowed_footprint_)) { - if (UNLIKELY(new_footprint > growth_limit_)) { - return true; - } - if (!concurrent_gc_) { - if (!grow) { - return true; - } else { - max_allowed_footprint_ = new_footprint; - } - } - } - return false; -} - -inline mirror::Object* Heap::TryToAllocate(Thread* self, space::AllocSpace* space, size_t alloc_size, - bool grow, size_t* bytes_allocated) { +inline mirror::Object* Heap::TryToAllocateInstrumented(Thread* self, space::AllocSpace* space, size_t alloc_size, + bool grow, size_t* bytes_allocated) { if (UNLIKELY(IsOutOfMemoryOnAllocation(alloc_size, grow))) { return NULL; } @@ -833,8 +809,8 @@ inline mirror::Object* Heap::TryToAllocate(Thread* self, space::AllocSpace* spac } // DlMallocSpace-specific version. -inline mirror::Object* Heap::TryToAllocate(Thread* self, space::DlMallocSpace* space, size_t alloc_size, - bool grow, size_t* bytes_allocated) { +inline mirror::Object* Heap::TryToAllocateInstrumented(Thread* self, space::DlMallocSpace* space, size_t alloc_size, + bool grow, size_t* bytes_allocated) { if (UNLIKELY(IsOutOfMemoryOnAllocation(alloc_size, grow))) { return NULL; } @@ -846,15 +822,15 @@ inline mirror::Object* Heap::TryToAllocate(Thread* self, space::DlMallocSpace* s } template -inline mirror::Object* Heap::Allocate(Thread* self, T* space, size_t alloc_size, - size_t* bytes_allocated) { +inline mirror::Object* Heap::AllocateInstrumented(Thread* self, T* space, size_t alloc_size, + size_t* bytes_allocated) { // Since allocation can cause a GC which will need to SuspendAll, make sure all allocations are // done in the runnable state where suspension is expected. DCHECK_EQ(self->GetState(), kRunnable); self->AssertThreadSuspensionIsAllowable(); - mirror::Object* ptr = TryToAllocate(self, space, alloc_size, false, bytes_allocated); - if (ptr != NULL) { + mirror::Object* ptr = TryToAllocateInstrumented(self, space, alloc_size, false, bytes_allocated); + if (LIKELY(ptr != NULL)) { return ptr; } return AllocateInternalWithGc(self, space, alloc_size, bytes_allocated); @@ -869,7 +845,7 @@ mirror::Object* Heap::AllocateInternalWithGc(Thread* self, space::AllocSpace* sp collector::GcType last_gc = WaitForConcurrentGcToComplete(self); if (last_gc != collector::kGcTypeNone) { // A GC was in progress and we blocked, retry allocation now that memory has been freed. - ptr = TryToAllocate(self, space, alloc_size, false, bytes_allocated); + ptr = TryToAllocateInstrumented(self, space, alloc_size, false, bytes_allocated); if (ptr != NULL) { return ptr; } @@ -904,7 +880,7 @@ mirror::Object* Heap::AllocateInternalWithGc(Thread* self, space::AllocSpace* sp i = static_cast(gc_type_ran); // Did we free sufficient memory for the allocation to succeed? - ptr = TryToAllocate(self, space, alloc_size, false, bytes_allocated); + ptr = TryToAllocateInstrumented(self, space, alloc_size, false, bytes_allocated); if (ptr != NULL) { return ptr; } @@ -913,7 +889,7 @@ mirror::Object* Heap::AllocateInternalWithGc(Thread* self, space::AllocSpace* sp // Allocations have failed after GCs; this is an exceptional state. // Try harder, growing the heap if necessary. - ptr = TryToAllocate(self, space, alloc_size, true, bytes_allocated); + ptr = TryToAllocateInstrumented(self, space, alloc_size, true, bytes_allocated); if (ptr != NULL) { return ptr; } @@ -928,7 +904,7 @@ mirror::Object* Heap::AllocateInternalWithGc(Thread* self, space::AllocSpace* sp // We don't need a WaitForConcurrentGcToComplete here either. CollectGarbageInternal(collector::kGcTypeFull, kGcCauseForAlloc, true); - return TryToAllocate(self, space, alloc_size, true, bytes_allocated); + return TryToAllocateInstrumented(self, space, alloc_size, true, bytes_allocated); } void Heap::SetTargetHeapUtilization(float target) { diff --git a/runtime/gc/heap.h b/runtime/gc/heap.h index 0ac3cf0abdc..ffd3034674b 100644 --- a/runtime/gc/heap.h +++ b/runtime/gc/heap.h @@ -101,6 +101,11 @@ enum HeapVerificationMode { }; static constexpr HeapVerificationMode kDesiredHeapVerification = kNoHeapVerification; +// If true, measure the total allocation time. +static constexpr bool kMeasureAllocationTime = false; +// Primitive arrays larger than this size are put in the large object space. +static constexpr size_t kLargeObjectThreshold = 3 * kPageSize; + class Heap { public: static constexpr size_t kDefaultInitialSize = 2 * MB; @@ -129,8 +134,18 @@ class Heap { // Allocates and initializes storage for an object instance. mirror::Object* AllocObject(Thread* self, mirror::Class* klass, size_t num_bytes) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + return AllocObjectInstrumented(self, klass, num_bytes); + } + mirror::Object* AllocObjectInstrumented(Thread* self, mirror::Class* klass, size_t num_bytes) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + mirror::Object* AllocObjectUninstrumented(Thread* self, mirror::Class* klass, size_t num_bytes) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + void DebugCheckPreconditionsForAllobObject(mirror::Class* c, size_t byte_count) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + void ThrowOutOfMemoryError(size_t byte_count, bool large_object_allocation); + void RegisterNativeAllocation(int bytes) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); void RegisterNativeFree(int bytes) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); @@ -425,9 +440,24 @@ class Heap { void AddModUnionTable(accounting::ModUnionTable* mod_union_table); private: + bool TryAllocLargeObjectInstrumented(Thread* self, mirror::Class* c, size_t byte_count, + mirror::Object** obj_ptr, size_t* bytes_allocated) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + bool TryAllocLargeObjectUninstrumented(Thread* self, mirror::Class* c, size_t byte_count, + mirror::Object** obj_ptr, size_t* bytes_allocated) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + bool ShouldAllocLargeObject(mirror::Class* c, size_t byte_count); + void CheckConcurrentGC(Thread* self, size_t new_num_bytes_allocated, mirror::Object* obj); + // Allocates uninitialized storage. Passing in a null space tries to place the object in the // large object space. - template mirror::Object* Allocate(Thread* self, T* space, size_t num_bytes, size_t* bytes_allocated) + template mirror::Object* AllocateInstrumented(Thread* self, T* space, size_t num_bytes, + size_t* bytes_allocated) + LOCKS_EXCLUDED(Locks::thread_suspend_count_lock_) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + template mirror::Object* AllocateUninstrumented(Thread* self, T* space, size_t num_bytes, + size_t* bytes_allocated) LOCKS_EXCLUDED(Locks::thread_suspend_count_lock_) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); @@ -439,17 +469,29 @@ class Heap { SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); // Try to allocate a number of bytes, this function never does any GCs. - mirror::Object* TryToAllocate(Thread* self, space::AllocSpace* space, size_t alloc_size, bool grow, - size_t* bytes_allocated) + mirror::Object* TryToAllocateInstrumented(Thread* self, space::AllocSpace* space, size_t alloc_size, + bool grow, size_t* bytes_allocated) LOCKS_EXCLUDED(Locks::thread_suspend_count_lock_) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); // Try to allocate a number of bytes, this function never does any GCs. DlMallocSpace-specialized version. - mirror::Object* TryToAllocate(Thread* self, space::DlMallocSpace* space, size_t alloc_size, bool grow, - size_t* bytes_allocated) + mirror::Object* TryToAllocateInstrumented(Thread* self, space::DlMallocSpace* space, size_t alloc_size, + bool grow, size_t* bytes_allocated) LOCKS_EXCLUDED(Locks::thread_suspend_count_lock_) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + mirror::Object* TryToAllocateUninstrumented(Thread* self, space::AllocSpace* space, size_t alloc_size, + bool grow, size_t* bytes_allocated) + LOCKS_EXCLUDED(Locks::thread_suspend_count_lock_) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + mirror::Object* TryToAllocateUninstrumented(Thread* self, space::DlMallocSpace* space, size_t alloc_size, + bool grow, size_t* bytes_allocated) + LOCKS_EXCLUDED(Locks::thread_suspend_count_lock_) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + void ThrowOutOfMemoryError(Thread* self, size_t byte_count, bool large_object_allocation) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); bool IsOutOfMemoryOnAllocation(size_t alloc_size, bool grow); // Pushes a list of cleared references out to the managed heap. @@ -459,7 +501,11 @@ class Heap { void RequestConcurrentGC(Thread* self) LOCKS_EXCLUDED(Locks::runtime_shutdown_lock_); bool IsGCRequestPending() const; - void RecordAllocation(size_t size, mirror::Object* object) + size_t RecordAllocationInstrumented(size_t size, mirror::Object* object) + LOCKS_EXCLUDED(GlobalSynchronization::heap_bitmap_lock_) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + size_t RecordAllocationUninstrumented(size_t size, mirror::Object* object) LOCKS_EXCLUDED(GlobalSynchronization::heap_bitmap_lock_) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); @@ -608,9 +654,6 @@ class Heap { // Since the heap was created, how many objects have been freed. size_t total_objects_freed_ever_; - // Primitive objects larger than this size are put in the large object space. - const size_t large_object_threshold_; - // Number of bytes allocated. Adjusted after each allocation and free. AtomicInteger num_bytes_allocated_; @@ -712,6 +755,16 @@ class Heap { friend class ScopedHeapLock; friend class space::SpaceTest; + class AllocationTimer { + private: + Heap* heap_; + mirror::Object** allocated_obj_ptr_; + uint64_t allocation_start_time_; + public: + AllocationTimer(Heap* heap, mirror::Object** allocated_obj_ptr); + ~AllocationTimer(); + }; + DISALLOW_IMPLICIT_CONSTRUCTORS(Heap); }; diff --git a/runtime/gc/space/dlmalloc_space-inl.h b/runtime/gc/space/dlmalloc_space-inl.h index 54811414e91..242ef6886e9 100644 --- a/runtime/gc/space/dlmalloc_space-inl.h +++ b/runtime/gc/space/dlmalloc_space-inl.h @@ -30,7 +30,7 @@ inline mirror::Object* DlMallocSpace::AllocNonvirtual(Thread* self, size_t num_b MutexLock mu(self, lock_); obj = AllocWithoutGrowthLocked(num_bytes, bytes_allocated); } - if (obj != NULL) { + if (LIKELY(obj != NULL)) { // Zero freshly allocated memory, done while not holding the space's lock. memset(obj, 0, num_bytes); } @@ -39,7 +39,7 @@ inline mirror::Object* DlMallocSpace::AllocNonvirtual(Thread* self, size_t num_b inline mirror::Object* DlMallocSpace::AllocWithoutGrowthLocked(size_t num_bytes, size_t* bytes_allocated) { mirror::Object* result = reinterpret_cast(mspace_malloc(mspace_, num_bytes)); - if (result != NULL) { + if (LIKELY(result != NULL)) { if (kDebugSpaces) { CHECK(Contains(result)) << "Allocation (" << reinterpret_cast(result) << ") not in bounds of allocation space " << *this; diff --git a/runtime/mirror/array-inl.h b/runtime/mirror/array-inl.h index c7b370f877f..c60e714d442 100644 --- a/runtime/mirror/array-inl.h +++ b/runtime/mirror/array-inl.h @@ -20,6 +20,7 @@ #include "array.h" #include "class.h" +#include "gc/heap-inl.h" #include "thread.h" #include "utils.h" @@ -35,8 +36,9 @@ inline size_t Array::SizeOf() const { return header_size + data_size; } -inline Array* Array::Alloc(Thread* self, Class* array_class, int32_t component_count, - size_t component_size) { +static inline size_t ComputeArraySize(Thread* self, Class* array_class, int32_t component_count, + size_t component_size) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { DCHECK(array_class != NULL); DCHECK_GE(component_count, 0); DCHECK(array_class->IsArrayClass()); @@ -51,21 +53,49 @@ inline Array* Array::Alloc(Thread* self, Class* array_class, int32_t component_c self->ThrowOutOfMemoryError(StringPrintf("%s of length %d would overflow", PrettyDescriptor(array_class).c_str(), component_count).c_str()); - return NULL; + return 0; // failure } + return size; +} - gc::Heap* heap = Runtime::Current()->GetHeap(); - Array* array = down_cast(heap->AllocObject(self, array_class, size)); +static inline Array* SetArrayLength(Array* array, size_t length) { if (LIKELY(array != NULL)) { DCHECK(array->IsArrayInstance()); - array->SetLength(component_count); + array->SetLength(length); } return array; } -inline Array* Array::Alloc(Thread* self, Class* array_class, int32_t component_count) { +inline Array* Array::AllocInstrumented(Thread* self, Class* array_class, int32_t component_count, + size_t component_size) { + size_t size = ComputeArraySize(self, array_class, component_count, component_size); + if (UNLIKELY(size == 0)) { + return NULL; + } + gc::Heap* heap = Runtime::Current()->GetHeap(); + Array* array = down_cast(heap->AllocObjectInstrumented(self, array_class, size)); + return SetArrayLength(array, component_count); +} + +inline Array* Array::AllocUninstrumented(Thread* self, Class* array_class, int32_t component_count, + size_t component_size) { + size_t size = ComputeArraySize(self, array_class, component_count, component_size); + if (UNLIKELY(size == 0)) { + return NULL; + } + gc::Heap* heap = Runtime::Current()->GetHeap(); + Array* array = down_cast(heap->AllocObjectUninstrumented(self, array_class, size)); + return SetArrayLength(array, component_count); +} + +inline Array* Array::AllocInstrumented(Thread* self, Class* array_class, int32_t component_count) { + DCHECK(array_class->IsArrayClass()); + return AllocInstrumented(self, array_class, component_count, array_class->GetComponentSize()); +} + +inline Array* Array::AllocUninstrumented(Thread* self, Class* array_class, int32_t component_count) { DCHECK(array_class->IsArrayClass()); - return Alloc(self, array_class, component_count, array_class->GetComponentSize()); + return AllocUninstrumented(self, array_class, component_count, array_class->GetComponentSize()); } } // namespace mirror diff --git a/runtime/mirror/array.h b/runtime/mirror/array.h index db6132df59b..570dcaa2926 100644 --- a/runtime/mirror/array.h +++ b/runtime/mirror/array.h @@ -27,10 +27,24 @@ class MANAGED Array : public Object { // A convenience for code that doesn't know the component size, // and doesn't want to have to work it out itself. static Array* Alloc(Thread* self, Class* array_class, int32_t component_count) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + return AllocInstrumented(self, array_class, component_count); + } + static Array* AllocUninstrumented(Thread* self, Class* array_class, int32_t component_count) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + static Array* AllocInstrumented(Thread* self, Class* array_class, int32_t component_count) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); static Array* Alloc(Thread* self, Class* array_class, int32_t component_count, size_t component_size) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + return AllocInstrumented(self, array_class, component_count, component_size); + } + static Array* AllocUninstrumented(Thread* self, Class* array_class, int32_t component_count, + size_t component_size) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + static Array* AllocInstrumented(Thread* self, Class* array_class, int32_t component_count, + size_t component_size) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); static Array* CreateMultiArray(Thread* self, Class* element_class, IntArray* dimensions) diff --git a/runtime/mirror/class-inl.h b/runtime/mirror/class-inl.h index 438ce81db25..88cffb77fc5 100644 --- a/runtime/mirror/class-inl.h +++ b/runtime/mirror/class-inl.h @@ -23,6 +23,7 @@ #include "art_method.h" #include "class_loader.h" #include "dex_cache.h" +#include "gc/heap-inl.h" #include "iftable.h" #include "object_array-inl.h" #include "runtime.h" @@ -342,13 +343,22 @@ inline void Class::SetName(String* name) { SetFieldObject(OFFSET_OF_OBJECT_MEMBER(Class, name_), name, false); } -inline Object* Class::AllocObject(Thread* self) { +inline void Class::CheckObjectAlloc() { DCHECK(!IsArrayClass()) << PrettyClass(this); DCHECK(IsInstantiable()) << PrettyClass(this); // TODO: decide whether we want this check. It currently fails during bootstrap. // DCHECK(!Runtime::Current()->IsStarted() || IsInitializing()) << PrettyClass(this); DCHECK_GE(this->object_size_, sizeof(Object)); - return Runtime::Current()->GetHeap()->AllocObject(self, this, this->object_size_); +} + +inline Object* Class::AllocObjectInstrumented(Thread* self) { + CheckObjectAlloc(); + return Runtime::Current()->GetHeap()->AllocObjectInstrumented(self, this, this->object_size_); +} + +inline Object* Class::AllocObjectUninstrumented(Thread* self) { + CheckObjectAlloc(); + return Runtime::Current()->GetHeap()->AllocObjectUninstrumented(self, this, this->object_size_); } } // namespace mirror diff --git a/runtime/mirror/class.h b/runtime/mirror/class.h index d97b603ad8e..4f8ab7d90a0 100644 --- a/runtime/mirror/class.h +++ b/runtime/mirror/class.h @@ -371,7 +371,12 @@ class MANAGED Class : public StaticStorageBase { } // Creates a raw object instance but does not invoke the default constructor. - Object* AllocObject(Thread* self) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + Object* AllocObject(Thread* self) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + return AllocObjectInstrumented(self); + } + + Object* AllocObjectUninstrumented(Thread* self) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + Object* AllocObjectInstrumented(Thread* self) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); bool IsVariableSize() const { // Classes and arrays vary in size, and so the object_size_ field cannot @@ -764,6 +769,8 @@ class MANAGED Class : public StaticStorageBase { bool IsAssignableFromArray(const Class* klass) const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + void CheckObjectAlloc() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + // defining class loader, or NULL for the "bootstrap" system loader ClassLoader* class_loader_; diff --git a/runtime/runtime.cc b/runtime/runtime.cc index 86a8f1b4180..b4ce37fe1cf 100644 --- a/runtime/runtime.cc +++ b/runtime/runtime.cc @@ -99,7 +99,8 @@ Runtime::Runtime() instrumentation_(), use_compile_time_class_path_(false), main_thread_group_(NULL), - system_thread_group_(NULL) { + system_thread_group_(NULL), + quick_alloc_entry_points_instrumentation_counter_(0) { for (int i = 0; i < Runtime::kLastCalleeSaveType; i++) { callee_save_methods_[i] = NULL; } @@ -1055,6 +1056,9 @@ void Runtime::SetStatsEnabled(bool new_state) { GetStats()->Clear(~0); // TODO: wouldn't it make more sense to clear _all_ threads' stats? Thread::Current()->GetStats()->Clear(~0); + InstrumentQuickAllocEntryPoints(); + } else { + UninstrumentQuickAllocEntryPoints(); } stats_enabled_ = new_state; } @@ -1282,4 +1286,46 @@ void Runtime::SetCompileTimeClassPath(jobject class_loader, std::vectorResetQuickAllocEntryPointsForThread(); +} + +void SetQuickAllocEntryPointsInstrumented(bool instrumented); + +void Runtime::InstrumentQuickAllocEntryPoints() { + ThreadList* tl = thread_list_; + Thread* self = Thread::Current(); + tl->SuspendAll(); + { + MutexLock mu(self, *Locks::runtime_shutdown_lock_); + MutexLock mu2(self, *Locks::thread_list_lock_); + DCHECK_LE(quick_alloc_entry_points_instrumentation_counter_, 0); + int old_counter = quick_alloc_entry_points_instrumentation_counter_++; + if (old_counter == 0) { + // If it was disabled, enable it. + SetQuickAllocEntryPointsInstrumented(true); + tl->ForEach(ResetQuickAllocEntryPointsForThread, NULL); + } + } + tl->ResumeAll(); +} + +void Runtime::UninstrumentQuickAllocEntryPoints() { + ThreadList* tl = thread_list_; + Thread* self = Thread::Current(); + tl->SuspendAll(); + { + MutexLock mu(self, *Locks::runtime_shutdown_lock_); + MutexLock mu2(self, *Locks::thread_list_lock_); + DCHECK_LT(quick_alloc_entry_points_instrumentation_counter_, 0); + int new_counter = --quick_alloc_entry_points_instrumentation_counter_; + if (new_counter == 0) { + // Disable it if the counter becomes zero. + SetQuickAllocEntryPointsInstrumented(false); + tl->ForEach(ResetQuickAllocEntryPointsForThread, NULL); + } + } + tl->ResumeAll(); +} + } // namespace art diff --git a/runtime/runtime.h b/runtime/runtime.h index d73940b53b9..552cfdf0092 100644 --- a/runtime/runtime.h +++ b/runtime/runtime.h @@ -398,6 +398,9 @@ class Runtime { const std::vector& GetCompileTimeClassPath(jobject class_loader); void SetCompileTimeClassPath(jobject class_loader, std::vector& class_path); + void InstrumentQuickAllocEntryPoints(); + void UninstrumentQuickAllocEntryPoints(); + private: static void InitPlatformSignalHandlers(); @@ -514,6 +517,8 @@ class Runtime { jobject main_thread_group_; jobject system_thread_group_; + int quick_alloc_entry_points_instrumentation_counter_; + DISALLOW_COPY_AND_ASSIGN(Runtime); }; diff --git a/runtime/thread.cc b/runtime/thread.cc index 7e79ce17f22..67a614f65db 100644 --- a/runtime/thread.cc +++ b/runtime/thread.cc @@ -108,6 +108,12 @@ void Thread::InitTlsEntryPoints() { &quick_entrypoints_); } +void ResetQuickAllocEntryPoints(QuickEntryPoints* qpoints); + +void Thread::ResetQuickAllocEntryPointsForThread() { + ResetQuickAllocEntryPoints(&quick_entrypoints_); +} + void Thread::SetDeoptimizationShadowFrame(ShadowFrame* sf) { deoptimization_shadow_frame_ = sf; } diff --git a/runtime/thread.h b/runtime/thread.h index dbf973619fa..2d9e0097d00 100644 --- a/runtime/thread.h +++ b/runtime/thread.h @@ -576,6 +576,8 @@ class PACKED(4) Thread { void AtomicClearFlag(ThreadFlag flag); + void ResetQuickAllocEntryPointsForThread(); + private: // We have no control over the size of 'bool', but want our boolean fields // to be 4-byte quantities. From 07d447bebe6cc358e05ef041db84a5095a69e4f1 Mon Sep 17 00:00:00 2001 From: Mathieu Chartier Date: Thu, 26 Sep 2013 11:57:43 -0700 Subject: [PATCH 0057/2402] Make artQuickResolutionTrampoline fix up references after GC points. Required for compaction. Bug: 8981901 Change-Id: I53a5f942af2a4428fe3548ba1856967632c2df9f --- .../quick/quick_trampoline_entrypoints.cc | 21 ++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc index cb486d58275..12291c39a99 100644 --- a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc +++ b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc @@ -423,13 +423,23 @@ class RememberFoGcArgumentVisitor : public QuickArgumentVisitor { virtual void Visit() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { if (IsParamAReference()) { - soa_->AddLocalReference(*reinterpret_cast(GetParamAddress())); + mirror::Object** param_address = reinterpret_cast(GetParamAddress()); + jobject reference = + soa_->AddLocalReference(*param_address); + references_.push_back(std::make_pair(reference, param_address)); + } + } + + void FixupReferences() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + // Fixup any references which may have changed. + for (std::pair& it : references_) { + *it.second = soa_->Decode(it.first); } } private: ScopedObjectAccessUnchecked* soa_; - + std::vector > references_; DISALLOW_COPY_AND_ASSIGN(RememberFoGcArgumentVisitor); }; @@ -556,11 +566,8 @@ extern "C" const void* artQuickResolutionTrampoline(mirror::ArtMethod* called, } } CHECK_EQ(code == NULL, thread->IsExceptionPending()); -#ifdef MOVING_GARBAGE_COLLECTOR - // TODO: locally saved objects may have moved during a GC during resolution. Need to update the - // registers so that the stale objects aren't passed to the method we've resolved. - UNIMPLEMENTED(WARNING); -#endif + // Fixup any locally saved objects may have moved during a GC. + visitor.FixupReferences(); // Place called method in callee-save frame to be placed as first argument to quick method. *sp = called; return code; From 7287601984059902aebde00ea4e077ead42fb149 Mon Sep 17 00:00:00 2001 From: Hiroshi Yamauchi Date: Thu, 26 Sep 2013 15:14:49 -0700 Subject: [PATCH 0058/2402] Fix a portable build breakage http://android-build/builds/git_dalvik-dev-linux-mysid-userdebug_portable/848398/logs/build.log due to https://googleplex-android.googlesource.com/platform/art/+/3b4c18933c24b8a33f38573c2ebcdb9aa16efeb5 Change-Id: I89ef5f77dfc640efbe3378549e22bb699da3149f --- runtime/gc/allocator/dlmalloc.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/runtime/gc/allocator/dlmalloc.h b/runtime/gc/allocator/dlmalloc.h index 07ebd1c0e3b..19159b1353b 100644 --- a/runtime/gc/allocator/dlmalloc.h +++ b/runtime/gc/allocator/dlmalloc.h @@ -18,6 +18,8 @@ #define ART_RUNTIME_GC_ALLOCATOR_DLMALLOC_H_ // Configure dlmalloc for mspaces. +// Avoid a collision with one used in llvm. +#undef HAVE_MMAP #define HAVE_MMAP 0 #define HAVE_MREMAP 0 #define HAVE_MORECORE 1 From d91d6d6a80748f277fd938a412211e5af28913b1 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Wed, 25 Sep 2013 20:26:14 -0700 Subject: [PATCH 0059/2402] Introduce Signature type to avoid string comparisons. Method resolution currently creates strings to then compare with strings formed from methods in other dex files. The temporary strings are purely created for the sake of comparisons. This change creates a new Signature type that represents a method signature but not as a string. This type supports comparisons and so can be used when searching for methods in resolution. With this change malloc is no longer the hottest method during dex2oat (now its memset) and allocations during verification have been reduced. The verifier is commonly what is populating the dex cache for methods and fields not declared in the dex file itself. Change-Id: I5ef0542823fbcae868aaa4a2457e8da7df0e9dae --- compiler/dex/quick/codegen_util.cc | 12 +-- compiler/dex/quick/mir_to_lir.h | 4 +- compiler/driver/compiler_driver.cc | 5 +- runtime/class_linker.cc | 22 ++--- runtime/class_linker_test.cc | 21 ++-- runtime/common_throws.cc | 2 +- runtime/common_throws.h | 3 +- runtime/debugger.cc | 2 +- runtime/dex_file-inl.h | 4 + runtime/dex_file.cc | 66 +++++++------ runtime/dex_file.h | 95 +++++++++++++++++-- runtime/dex_file_test.cc | 27 ++---- runtime/jni_internal.cc | 1 + runtime/mirror/class.cc | 64 ++++++++++++- runtime/mirror/class.h | 35 +++++-- runtime/object_utils.h | 41 +------- runtime/trace.cc | 2 +- runtime/utils.cc | 22 +++-- runtime/verifier/method_verifier.cc | 4 +- test/Android.mk | 2 +- .../GetMethodSignature.java} | 4 +- 21 files changed, 283 insertions(+), 155 deletions(-) rename test/{CreateMethodSignature/CreateMethodSignature.java => GetMethodSignature/GetMethodSignature.java} (86%) diff --git a/compiler/dex/quick/codegen_util.cc b/compiler/dex/quick/codegen_util.cc index 4ce752fb398..6e49f0bc54a 100644 --- a/compiler/dex/quick/codegen_util.cc +++ b/compiler/dex/quick/codegen_util.cc @@ -248,12 +248,12 @@ void Mir2Lir::DumpPromotionMap() { } /* Dump a mapping table */ -void Mir2Lir::DumpMappingTable(const char* table_name, const std::string& descriptor, - const std::string& name, const std::string& signature, +void Mir2Lir::DumpMappingTable(const char* table_name, const char* descriptor, + const char* name, const Signature& signature, const std::vector& v) { if (v.size() > 0) { std::string line(StringPrintf("\n %s %s%s_%s_table[%zu] = {", table_name, - descriptor.c_str(), name.c_str(), signature.c_str(), v.size())); + descriptor, name, signature.ToString().c_str(), v.size())); std::replace(line.begin(), line.end(), ';', '_'); LOG(INFO) << line; for (uint32_t i = 0; i < v.size(); i+=2) { @@ -293,9 +293,9 @@ void Mir2Lir::CodegenDump() { const DexFile::MethodId& method_id = cu_->dex_file->GetMethodId(cu_->method_idx); - std::string signature(cu_->dex_file->GetMethodSignature(method_id)); - std::string name(cu_->dex_file->GetMethodName(method_id)); - std::string descriptor(cu_->dex_file->GetMethodDeclaringClassDescriptor(method_id)); + const Signature signature = cu_->dex_file->GetMethodSignature(method_id); + const char* name = cu_->dex_file->GetMethodName(method_id); + const char* descriptor(cu_->dex_file->GetMethodDeclaringClassDescriptor(method_id)); // Dump mapping tables DumpMappingTable("PC2Dex_MappingTable", descriptor, name, signature, pc2dex_mapping_table_); diff --git a/compiler/dex/quick/mir_to_lir.h b/compiler/dex/quick/mir_to_lir.h index 401e3d5f93e..7d6f968da5c 100644 --- a/compiler/dex/quick/mir_to_lir.h +++ b/compiler/dex/quick/mir_to_lir.h @@ -283,8 +283,8 @@ class Mir2Lir : public Backend { bool EvaluateBranch(Instruction::Code opcode, int src1, int src2); bool IsInexpensiveConstant(RegLocation rl_src); ConditionCode FlipComparisonOrder(ConditionCode before); - void DumpMappingTable(const char* table_name, const std::string& descriptor, - const std::string& name, const std::string& signature, + void DumpMappingTable(const char* table_name, const char* descriptor, + const char* name, const Signature& signature, const std::vector& v); void InstallLiteralPools(); void InstallSwitchTables(); diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc index e2277156053..056be1fb049 100644 --- a/compiler/driver/compiler_driver.cc +++ b/compiler/driver/compiler_driver.cc @@ -1227,8 +1227,9 @@ bool CompilerDriver::ComputeInvokeInfo(const DexCompilationUnit* mUnit, const ui if (name != NULL) { uint16_t return_type_idx; std::vector param_type_idxs; - bool success = dexfile->CreateTypeList(&return_type_idx, ¶m_type_idxs, - cm_dexfile->GetMethodSignature(cm_method_id)); + bool success = + dexfile->CreateTypeList(cm_dexfile->GetMethodSignature(cm_method_id).ToString(), + &return_type_idx, ¶m_type_idxs); if (success) { const DexFile::ProtoId* sig = dexfile->FindProtoId(return_type_idx, param_type_idxs); diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index 6aae63ee2c6..17a179f2d91 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -2823,12 +2823,12 @@ static void CheckProxyConstructor(mirror::ArtMethod* constructor) CHECK(constructor->IsConstructor()); MethodHelper mh(constructor); CHECK_STREQ(mh.GetName(), ""); - CHECK_EQ(mh.GetSignature(), std::string("(Ljava/lang/reflect/InvocationHandler;)V")); + CHECK_STREQ(mh.GetSignature().ToString().c_str(), "(Ljava/lang/reflect/InvocationHandler;)V"); DCHECK(constructor->IsPublic()); } mirror::ArtMethod* ClassLinker::CreateProxyMethod(Thread* self, SirtRef& klass, - SirtRef& prototype) { + SirtRef& prototype) { // Ensure prototype is in dex cache so that we can use the dex cache to look up the overridden // prototype method prototype->GetDeclaringClass()->GetDexCache()->SetResolvedMethod(prototype->GetDexMethodIndex(), @@ -2892,7 +2892,7 @@ static bool CanWeInitializeClass(mirror::Class* klass, bool can_init_statics, } if (!can_init_statics) { // Check if there's a class initializer. - mirror::ArtMethod* clinit = klass->FindDeclaredDirectMethod("", "()V"); + mirror::ArtMethod* clinit = klass->FindClassInitializer(); if (clinit != NULL) { return false; } @@ -3039,7 +3039,7 @@ bool ClassLinker::InitializeClass(mirror::Class* klass, bool can_init_statics, } } - mirror::ArtMethod* clinit = klass->FindDeclaredDirectMethod("", "()V"); + mirror::ArtMethod* clinit = klass->FindClassInitializer(); if (clinit != NULL) { CHECK(can_init_statics); if (LIKELY(Runtime::Current()->IsStarted())) { @@ -3992,11 +3992,11 @@ mirror::Class* ClassLinker::ResolveType(const DexFile& dex_file, } mirror::ArtMethod* ClassLinker::ResolveMethod(const DexFile& dex_file, - uint32_t method_idx, - mirror::DexCache* dex_cache, - mirror::ClassLoader* class_loader, - const mirror::ArtMethod* referrer, - InvokeType type) { + uint32_t method_idx, + mirror::DexCache* dex_cache, + mirror::ClassLoader* class_loader, + const mirror::ArtMethod* referrer, + InvokeType type) { DCHECK(dex_cache != NULL); // Check for hit in the dex cache. mirror::ArtMethod* resolved = dex_cache->GetResolvedMethod(method_idx); @@ -4031,7 +4031,7 @@ mirror::ArtMethod* ClassLinker::ResolveMethod(const DexFile& dex_file, if (resolved == NULL) { // Search by name, which works across dex files. const char* name = dex_file.StringDataByIdx(method_id.name_idx_); - std::string signature(dex_file.CreateMethodSignature(method_id.proto_idx_, NULL)); + const Signature signature = dex_file.GetMethodSignature(method_id); switch (type) { case kDirect: // Fall-through. case kStatic: @@ -4061,7 +4061,7 @@ mirror::ArtMethod* ClassLinker::ResolveMethod(const DexFile& dex_file, // We failed to find the method which means either an access error, an incompatible class // change, or no such method. First try to find the method among direct and virtual methods. const char* name = dex_file.StringDataByIdx(method_id.name_idx_); - std::string signature(dex_file.CreateMethodSignature(method_id.proto_idx_, NULL)); + const Signature signature = dex_file.GetMethodSignature(method_id); switch (type) { case kDirect: case kStatic: diff --git a/runtime/class_linker_test.cc b/runtime/class_linker_test.cc index bea1139679b..ad9347fee19 100644 --- a/runtime/class_linker_test.cc +++ b/runtime/class_linker_test.cc @@ -152,7 +152,7 @@ class ClassLinkerTest : public CommonTest { EXPECT_TRUE(method != NULL); EXPECT_TRUE(method->GetClass() != NULL); EXPECT_TRUE(mh.GetName() != NULL); - EXPECT_TRUE(mh.GetSignature() != NULL); + EXPECT_TRUE(mh.GetSignature() != Signature::NoSignature()); EXPECT_TRUE(method->GetDexCacheStrings() != NULL); EXPECT_TRUE(method->GetDexCacheResolvedMethods() != NULL); @@ -942,15 +942,16 @@ TEST_F(ClassLinkerTest, Interfaces) { EXPECT_TRUE(K->IsAssignableFrom(B)); EXPECT_TRUE(J->IsAssignableFrom(B)); - mirror::ArtMethod* Ii = I->FindVirtualMethod("i", "()V"); - mirror::ArtMethod* Jj1 = J->FindVirtualMethod("j1", "()V"); - mirror::ArtMethod* Jj2 = J->FindVirtualMethod("j2", "()V"); - mirror::ArtMethod* Kj1 = K->FindInterfaceMethod("j1", "()V"); - mirror::ArtMethod* Kj2 = K->FindInterfaceMethod("j2", "()V"); - mirror::ArtMethod* Kk = K->FindInterfaceMethod("k", "()V"); - mirror::ArtMethod* Ai = A->FindVirtualMethod("i", "()V"); - mirror::ArtMethod* Aj1 = A->FindVirtualMethod("j1", "()V"); - mirror::ArtMethod* Aj2 = A->FindVirtualMethod("j2", "()V"); + const Signature void_sig = I->GetDexCache()->GetDexFile()->CreateSignature("()V"); + mirror::ArtMethod* Ii = I->FindVirtualMethod("i", void_sig); + mirror::ArtMethod* Jj1 = J->FindVirtualMethod("j1", void_sig); + mirror::ArtMethod* Jj2 = J->FindVirtualMethod("j2", void_sig); + mirror::ArtMethod* Kj1 = K->FindInterfaceMethod("j1", void_sig); + mirror::ArtMethod* Kj2 = K->FindInterfaceMethod("j2", void_sig); + mirror::ArtMethod* Kk = K->FindInterfaceMethod("k", void_sig); + mirror::ArtMethod* Ai = A->FindVirtualMethod("i", void_sig); + mirror::ArtMethod* Aj1 = A->FindVirtualMethod("j1", void_sig); + mirror::ArtMethod* Aj2 = A->FindVirtualMethod("j2", void_sig); ASSERT_TRUE(Ii != NULL); ASSERT_TRUE(Jj1 != NULL); ASSERT_TRUE(Jj2 != NULL); diff --git a/runtime/common_throws.cc b/runtime/common_throws.cc index 26ce5be1ec1..189e3edc0f6 100644 --- a/runtime/common_throws.cc +++ b/runtime/common_throws.cc @@ -265,7 +265,7 @@ void ThrowNoSuchFieldError(const StringPiece& scope, mirror::Class* c, // NoSuchMethodError void ThrowNoSuchMethodError(InvokeType type, mirror::Class* c, const StringPiece& name, - const StringPiece& signature) { + const Signature& signature) { std::ostringstream msg; ClassHelper kh(c); msg << "No " << type << " method " << name << signature diff --git a/runtime/common_throws.h b/runtime/common_throws.h index 99c6343cdd0..1d77e2d6250 100644 --- a/runtime/common_throws.h +++ b/runtime/common_throws.h @@ -27,6 +27,7 @@ class ArtMethod; class Class; class Object; } // namespace mirror +class Signature; class StringPiece; class ThrowLocation; @@ -140,7 +141,7 @@ void ThrowNoSuchFieldError(const StringPiece& scope, mirror::Class* c, // NoSuchMethodError void ThrowNoSuchMethodError(InvokeType type, mirror::Class* c, const StringPiece& name, - const StringPiece& signature) + const Signature& signature) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); void ThrowNoSuchMethodError(uint32_t method_idx) diff --git a/runtime/debugger.cc b/runtime/debugger.cc index e57137fb963..ae57aa34ec3 100644 --- a/runtime/debugger.cc +++ b/runtime/debugger.cc @@ -1287,7 +1287,7 @@ JDWP::JdwpError Dbg::OutputDeclaredMethods(JDWP::RefTypeId class_id, bool with_g MethodHelper mh(m); expandBufAddMethodId(pReply, ToMethodId(m)); expandBufAddUtf8String(pReply, mh.GetName()); - expandBufAddUtf8String(pReply, mh.GetSignature()); + expandBufAddUtf8String(pReply, mh.GetSignature().ToString()); if (with_generic) { static const char genericSignature[1] = ""; expandBufAddUtf8String(pReply, genericSignature); diff --git a/runtime/dex_file-inl.h b/runtime/dex_file-inl.h index 2ee9244bf23..c57a1e7582b 100644 --- a/runtime/dex_file-inl.h +++ b/runtime/dex_file-inl.h @@ -47,6 +47,10 @@ inline StringPiece DexFile::StringDataAsStringPieceByIdx(uint32_t idx) const { return StringPiece(data, static_cast(length)); } +inline const Signature DexFile::GetMethodSignature(const MethodId& method_id) const { + return Signature(this, GetProtoId(method_id.proto_idx_)); +} + inline const DexFile::TryItem* DexFile::GetTryItems(const CodeItem& code_item, uint32_t offset) { const uint16_t* insns_end_ = &code_item.insns_[code_item.insns_size_in_code_units_]; return reinterpret_cast diff --git a/runtime/dex_file.cc b/runtime/dex_file.cc index 098ab674f4f..275dcc5a03c 100644 --- a/runtime/dex_file.cc +++ b/runtime/dex_file.cc @@ -503,8 +503,8 @@ const DexFile::ProtoId* DexFile::FindProtoId(uint16_t return_type_idx, } // Given a signature place the type ids into the given vector -bool DexFile::CreateTypeList(uint16_t* return_type_idx, std::vector* param_type_idxs, - const std::string& signature) const { +bool DexFile::CreateTypeList(const StringPiece& signature, uint16_t* return_type_idx, + std::vector* param_type_idxs) const { if (signature[0] != '(') { return false; } @@ -518,6 +518,7 @@ bool DexFile::CreateTypeList(uint16_t* return_type_idx, std::vector* p process_return = true; continue; } + // TODO: avoid building a string. std::string descriptor; descriptor += c; while (c == '[') { // process array prefix @@ -557,35 +558,18 @@ bool DexFile::CreateTypeList(uint16_t* return_type_idx, std::vector* p return false; // failed to correctly parse return type } -// Materializes the method descriptor for a method prototype. Method -// descriptors are not stored directly in the dex file. Instead, one -// must assemble the descriptor from references in the prototype. -std::string DexFile::CreateMethodSignature(uint32_t proto_idx, int32_t* unicode_length) const { - const ProtoId& proto_id = GetProtoId(proto_idx); - std::string descriptor; - descriptor.push_back('('); - const TypeList* type_list = GetProtoParameters(proto_id); - size_t parameter_length = 0; - if (type_list != NULL) { - // A non-zero number of arguments. Append the type names. - for (size_t i = 0; i < type_list->Size(); ++i) { - const TypeItem& type_item = type_list->GetTypeItem(i); - uint32_t type_idx = type_item.type_idx_; - uint32_t type_length; - const char* name = StringByTypeIdx(type_idx, &type_length); - parameter_length += type_length; - descriptor.append(name); - } +const Signature DexFile::CreateSignature(const StringPiece& signature) const { + uint16_t return_type_idx; + std::vector param_type_indices; + bool success = CreateTypeList(signature, &return_type_idx, ¶m_type_indices); + if (!success) { + return Signature::NoSignature(); } - descriptor.push_back(')'); - uint32_t return_type_idx = proto_id.return_type_idx_; - uint32_t return_type_length; - const char* name = StringByTypeIdx(return_type_idx, &return_type_length); - descriptor.append(name); - if (unicode_length != NULL) { - *unicode_length = parameter_length + return_type_length + 2; // 2 for ( and ) + const ProtoId* proto_id = FindProtoId(return_type_idx, param_type_indices); + if (proto_id == NULL) { + return Signature::NoSignature(); } - return descriptor; + return Signature(this, *proto_id); } int32_t DexFile::GetLineNumFromPC(const mirror::ArtMethod* method, uint32_t rel_pc) const { @@ -831,6 +815,30 @@ bool DexFile::LineNumForPcCb(void* raw_context, uint32_t address, uint32_t line_ } } +std::string Signature::ToString() const { + if (dex_file_ == nullptr) { + CHECK(proto_id_ == nullptr); + return ""; + } + const DexFile::TypeList* params = dex_file_->GetProtoParameters(*proto_id_); + std::string result; + if (params == nullptr) { + result += "()"; + } else { + result += "("; + for (uint32_t i = 0; i < params->Size(); ++i) { + result += dex_file_->StringByTypeIdx(params->GetTypeItem(i).type_idx_); + } + result += ")"; + } + result += dex_file_->StringByTypeIdx(proto_id_->return_type_idx_); + return result; +} + +std::ostream& operator<<(std::ostream& os, const Signature& sig) { + return os << sig.ToString(); +} + // Decodes the header section from the class data bytes. void ClassDataItemIterator::ReadClassDataHeader() { CHECK(ptr_pos_ != NULL); diff --git a/runtime/dex_file.h b/runtime/dex_file.h index 4534b41e92d..40e4c72772e 100644 --- a/runtime/dex_file.h +++ b/runtime/dex_file.h @@ -40,6 +40,7 @@ namespace mirror { class DexCache; } // namespace mirror class ClassLinker; +class Signature; class StringPiece; class ZipArchive; @@ -559,10 +560,8 @@ class DexFile { return GetProtoId(method_id.proto_idx_); } - // Returns the signature of a method id. - const std::string GetMethodSignature(const MethodId& method_id) const { - return CreateMethodSignature(method_id.proto_idx_, NULL); - } + // Returns a representation of the signature of a method id. + const Signature GetMethodSignature(const MethodId& method_id) const; // Returns the name of a method id. const char* GetMethodName(const MethodId& method_id) const { @@ -656,15 +655,16 @@ class DexFile { } // Looks up a proto id for a given return type and signature type list - const ProtoId* FindProtoId(uint16_t return_type_id, + const ProtoId* FindProtoId(uint16_t return_type_idx, const std::vector& signature_type_idxs_) const; // Given a signature place the type ids into the given vector, returns true on success - bool CreateTypeList(uint16_t* return_type_idx, std::vector* param_type_idxs, - const std::string& signature) const; + bool CreateTypeList(const StringPiece& signature, uint16_t* return_type_idx, + std::vector* param_type_idxs) const; - // Given a proto_idx decode the type list and return type into a method signature - std::string CreateMethodSignature(uint32_t proto_idx, int32_t* unicode_length) const; + // Create a Signature from the given string signature or return Signature::NoSignature if not + // possible. + const Signature CreateSignature(const StringPiece& signature) const; // Returns the short form method descriptor for the given prototype. const char* GetShorty(uint32_t proto_idx) const { @@ -942,6 +942,83 @@ class DexFileParameterIterator { DISALLOW_IMPLICIT_CONSTRUCTORS(DexFileParameterIterator); }; +// Abstract the signature of a method. +class Signature { + public: + std::string ToString() const; + + static Signature NoSignature() { + return Signature(); + } + + bool operator==(const Signature& rhs) const { + if (dex_file_ == nullptr) { + return rhs.dex_file_ == nullptr; + } + if (rhs.dex_file_ == nullptr) { + return false; + } + if (dex_file_ == rhs.dex_file_) { + return proto_id_ == rhs.proto_id_; + } + StringPiece shorty(dex_file_->StringDataAsStringPieceByIdx(proto_id_->shorty_idx_)); + if (shorty != rhs.dex_file_->StringDataAsStringPieceByIdx(rhs.proto_id_->shorty_idx_)) { + return false; // Shorty mismatch. + } + if (shorty[0] == 'L') { + const DexFile::TypeId& return_type_id = dex_file_->GetTypeId(proto_id_->return_type_idx_); + const DexFile::TypeId& rhs_return_type_id = + rhs.dex_file_->GetTypeId(rhs.proto_id_->return_type_idx_); + if (dex_file_->StringDataAsStringPieceByIdx(return_type_id.descriptor_idx_) != + rhs.dex_file_->StringDataAsStringPieceByIdx(rhs_return_type_id.descriptor_idx_)) { + return false; // Return type mismatch. + } + } + if (shorty.find('L', 1) != StringPiece::npos) { + const DexFile::TypeList* params = dex_file_->GetProtoParameters(*proto_id_); + const DexFile::TypeList* rhs_params = rhs.dex_file_->GetProtoParameters(*rhs.proto_id_); + // Both lists are empty or have contents, or else shorty is broken. + DCHECK_EQ(params == nullptr, rhs_params == nullptr); + if (params != nullptr) { + uint32_t params_size = params->Size(); + DCHECK_EQ(params_size, rhs_params->Size()); // Parameter list size must match. + for (uint32_t i = 0; i < params_size; ++i) { + const DexFile::TypeId& param_id = dex_file_->GetTypeId(params->GetTypeItem(i).type_idx_); + const DexFile::TypeId& rhs_param_id = + rhs.dex_file_->GetTypeId(rhs_params->GetTypeItem(i).type_idx_); + if (dex_file_->StringDataAsStringPieceByIdx(param_id.descriptor_idx_) != + rhs.dex_file_->StringDataAsStringPieceByIdx(rhs_param_id.descriptor_idx_)) { + return false; // Parameter type mismatch. + } + } + } + } + return true; + } + + bool operator!=(const Signature& rhs) const { + return !(*this == rhs); + } + + bool operator==(const StringPiece& rhs) const { + // TODO: Avoid temporary string allocation. + return ToString() == rhs; + } + + private: + Signature(const DexFile* dex, const DexFile::ProtoId& proto) : dex_file_(dex), proto_id_(&proto) { + } + + Signature() : dex_file_(nullptr), proto_id_(nullptr) { + } + + friend class DexFile; + + const DexFile* const dex_file_; + const DexFile::ProtoId* const proto_id_; +}; +std::ostream& operator<<(std::ostream& os, const Signature& sig); + // Iterate and decode class_data_item class ClassDataItemIterator { public: diff --git a/runtime/dex_file_test.cc b/runtime/dex_file_test.cc index 32a8354d017..1b40529a089 100644 --- a/runtime/dex_file_test.cc +++ b/runtime/dex_file_test.cc @@ -137,14 +137,14 @@ TEST_F(DexFileTest, ClassDefs) { EXPECT_STREQ("LNested;", raw->GetClassDescriptor(c1)); } -TEST_F(DexFileTest, CreateMethodSignature) { +TEST_F(DexFileTest, GetMethodSignature) { ScopedObjectAccess soa(Thread::Current()); - const DexFile* raw(OpenTestDexFile("CreateMethodSignature")); + const DexFile* raw(OpenTestDexFile("GetMethodSignature")); ASSERT_TRUE(raw != NULL); EXPECT_EQ(1U, raw->NumClassDefs()); const DexFile::ClassDef& class_def = raw->GetClassDef(0); - ASSERT_STREQ("LCreateMethodSignature;", raw->GetClassDescriptor(class_def)); + ASSERT_STREQ("LGetMethodSignature;", raw->GetClassDescriptor(class_def)); const byte* class_data = raw->GetClassData(class_def); ASSERT_TRUE(class_data != NULL); @@ -156,11 +156,9 @@ TEST_F(DexFileTest, CreateMethodSignature) { { ASSERT_EQ(1U, it.NumDirectMethods()); const DexFile::MethodId& method_id = raw->GetMethodId(it.GetMemberIndex()); - uint32_t proto_idx = method_id.proto_idx_; const char* name = raw->StringDataByIdx(method_id.name_idx_); ASSERT_STREQ("", name); - int32_t length; - std::string signature(raw->CreateMethodSignature(proto_idx, &length)); + std::string signature(raw->GetMethodSignature(method_id).ToString()); ASSERT_EQ("()V", signature); } @@ -173,9 +171,7 @@ TEST_F(DexFileTest, CreateMethodSignature) { const char* name = raw->StringDataByIdx(method_id.name_idx_); ASSERT_STREQ("m1", name); - uint32_t proto_idx = method_id.proto_idx_; - int32_t length; - std::string signature(raw->CreateMethodSignature(proto_idx, &length)); + std::string signature(raw->GetMethodSignature(method_id).ToString()); ASSERT_EQ("(IDJLjava/lang/Object;)Ljava/lang/Float;", signature); } @@ -186,20 +182,18 @@ TEST_F(DexFileTest, CreateMethodSignature) { const char* name = raw->StringDataByIdx(method_id.name_idx_); ASSERT_STREQ("m2", name); - uint32_t proto_idx = method_id.proto_idx_; - int32_t length; - std::string signature(raw->CreateMethodSignature(proto_idx, &length)); - ASSERT_EQ("(ZSC)LCreateMethodSignature;", signature); + std::string signature(raw->GetMethodSignature(method_id).ToString()); + ASSERT_EQ("(ZSC)LGetMethodSignature;", signature); } } TEST_F(DexFileTest, FindStringId) { ScopedObjectAccess soa(Thread::Current()); - const DexFile* raw(OpenTestDexFile("CreateMethodSignature")); + const DexFile* raw(OpenTestDexFile("GetMethodSignature")); ASSERT_TRUE(raw != NULL); EXPECT_EQ(1U, raw->NumClassDefs()); - const char* strings[] = { "LCreateMethodSignature;", "Ljava/lang/Float;", "Ljava/lang/Object;", + const char* strings[] = { "LGetMethodSignature;", "Ljava/lang/Float;", "Ljava/lang/Object;", "D", "I", "J", NULL }; for (size_t i = 0; strings[i] != NULL; i++) { const char* str = strings[i]; @@ -245,11 +239,10 @@ TEST_F(DexFileTest, FindMethodId) { const DexFile::StringId& name = java_lang_dex_file_->GetStringId(to_find.name_idx_); const DexFile::ProtoId& signature = java_lang_dex_file_->GetProtoId(to_find.proto_idx_); const DexFile::MethodId* found = java_lang_dex_file_->FindMethodId(klass, name, signature); - int32_t length; ASSERT_TRUE(found != NULL) << "Didn't find method " << i << ": " << java_lang_dex_file_->StringByTypeIdx(to_find.class_idx_) << "." << java_lang_dex_file_->GetStringData(name) - << java_lang_dex_file_->CreateMethodSignature(to_find.proto_idx_, &length); + << java_lang_dex_file_->GetMethodSignature(to_find); EXPECT_EQ(java_lang_dex_file_->GetIndexForMethodId(*found), i); } } diff --git a/runtime/jni_internal.cc b/runtime/jni_internal.cc index 60fad6ee481..8be9b21cdf2 100644 --- a/runtime/jni_internal.cc +++ b/runtime/jni_internal.cc @@ -228,6 +228,7 @@ static jmethodID FindMethodID(ScopedObjectAccess& soa, jclass jni_class, const char* name, const char* sig, bool is_static) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { Class* c = soa.Decode(jni_class); + DCHECK(c != nullptr); if (!Runtime::Current()->GetClassLinker()->EnsureInitialized(c, true, true)) { return NULL; } diff --git a/runtime/mirror/class.cc b/runtime/mirror/class.cc index 287e8b0959f..c6db5b9a612 100644 --- a/runtime/mirror/class.cc +++ b/runtime/mirror/class.cc @@ -325,7 +325,7 @@ void Class::SetClassLoader(ClassLoader* new_class_loader) { SetFieldObject(OFFSET_OF_OBJECT_MEMBER(Class, class_loader_), new_class_loader, false); } -ArtMethod* Class::FindInterfaceMethod(const StringPiece& name, const StringPiece& signature) const { +ArtMethod* Class::FindInterfaceMethod(const StringPiece& name, const Signature& signature) const { // Check the current class before checking the interfaces. ArtMethod* method = FindDeclaredVirtualMethod(name, signature); if (method != NULL) { @@ -361,8 +361,19 @@ ArtMethod* Class::FindInterfaceMethod(const DexCache* dex_cache, uint32_t dex_me return NULL; } - ArtMethod* Class::FindDeclaredDirectMethod(const StringPiece& name, const StringPiece& signature) const { + MethodHelper mh; + for (size_t i = 0; i < NumDirectMethods(); ++i) { + ArtMethod* method = GetDirectMethod(i); + mh.ChangeMethod(method); + if (name == mh.GetNameAsStringPiece() && mh.GetSignature() == signature) { + return method; + } + } + return NULL; +} + +ArtMethod* Class::FindDeclaredDirectMethod(const StringPiece& name, const Signature& signature) const { MethodHelper mh; for (size_t i = 0; i < NumDirectMethods(); ++i) { ArtMethod* method = GetDirectMethod(i); @@ -396,6 +407,16 @@ ArtMethod* Class::FindDirectMethod(const StringPiece& name, const StringPiece& s return NULL; } +ArtMethod* Class::FindDirectMethod(const StringPiece& name, const Signature& signature) const { + for (const Class* klass = this; klass != NULL; klass = klass->GetSuperClass()) { + ArtMethod* method = klass->FindDeclaredDirectMethod(name, signature); + if (method != NULL) { + return method; + } + } + return NULL; +} + ArtMethod* Class::FindDirectMethod(const DexCache* dex_cache, uint32_t dex_method_idx) const { for (const Class* klass = this; klass != NULL; klass = klass->GetSuperClass()) { ArtMethod* method = klass->FindDeclaredDirectMethod(dex_cache, dex_method_idx); @@ -406,8 +427,20 @@ ArtMethod* Class::FindDirectMethod(const DexCache* dex_cache, uint32_t dex_metho return NULL; } +ArtMethod* Class::FindDeclaredVirtualMethod(const StringPiece& name, const StringPiece& signature) const { + MethodHelper mh; + for (size_t i = 0; i < NumVirtualMethods(); ++i) { + ArtMethod* method = GetVirtualMethod(i); + mh.ChangeMethod(method); + if (name == mh.GetNameAsStringPiece() && mh.GetSignature() == signature) { + return method; + } + } + return NULL; +} + ArtMethod* Class::FindDeclaredVirtualMethod(const StringPiece& name, - const StringPiece& signature) const { + const Signature& signature) const { MethodHelper mh; for (size_t i = 0; i < NumVirtualMethods(); ++i) { ArtMethod* method = GetVirtualMethod(i); @@ -441,6 +474,16 @@ ArtMethod* Class::FindVirtualMethod(const StringPiece& name, const StringPiece& return NULL; } +ArtMethod* Class::FindVirtualMethod(const StringPiece& name, const Signature& signature) const { + for (const Class* klass = this; klass != NULL; klass = klass->GetSuperClass()) { + ArtMethod* method = klass->FindDeclaredVirtualMethod(name, signature); + if (method != NULL) { + return method; + } + } + return NULL; +} + ArtMethod* Class::FindVirtualMethod(const DexCache* dex_cache, uint32_t dex_method_idx) const { for (const Class* klass = this; klass != NULL; klass = klass->GetSuperClass()) { ArtMethod* method = klass->FindDeclaredVirtualMethod(dex_cache, dex_method_idx); @@ -451,6 +494,21 @@ ArtMethod* Class::FindVirtualMethod(const DexCache* dex_cache, uint32_t dex_meth return NULL; } +ArtMethod* Class::FindClassInitializer() const { + for (size_t i = 0; i < NumDirectMethods(); ++i) { + ArtMethod* method = GetDirectMethod(i); + if (method->IsConstructor() && method->IsStatic()) { + if (kIsDebugBuild) { + MethodHelper mh(method); + CHECK_STREQ(mh.GetName(), ""); + CHECK_STREQ(mh.GetSignature().ToString().c_str(), "()V"); + } + return method; + } + } + return NULL; +} + ArtField* Class::FindDeclaredInstanceField(const StringPiece& name, const StringPiece& type) { // Is the field in this class? // Interfaces are not relevant because they can't contain instance fields. diff --git a/runtime/mirror/class.h b/runtime/mirror/class.h index 4f8ab7d90a0..586151de454 100644 --- a/runtime/mirror/class.h +++ b/runtime/mirror/class.h @@ -59,6 +59,7 @@ namespace art { struct ClassClassOffsets; struct ClassOffsets; +class Signature; class StringPiece; namespace mirror { @@ -565,39 +566,53 @@ class MANAGED Class : public StaticStorageBase { ArtMethod* FindVirtualMethodForInterface(ArtMethod* method) const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) ALWAYS_INLINE; - ArtMethod* FindInterfaceMethod(const StringPiece& name, const StringPiece& descriptor) const + ArtMethod* FindVirtualMethodForVirtualOrInterface(ArtMethod* method) const + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + ArtMethod* FindInterfaceMethod(const StringPiece& name, const Signature& signature) const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); ArtMethod* FindInterfaceMethod(const DexCache* dex_cache, uint32_t dex_method_idx) const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - ArtMethod* FindVirtualMethodForVirtualOrInterface(ArtMethod* method) const + ArtMethod* FindDeclaredDirectMethod(const StringPiece& name, const StringPiece& signature) const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - ArtMethod* FindDeclaredVirtualMethod(const StringPiece& name, const StringPiece& signature) const + ArtMethod* FindDeclaredDirectMethod(const StringPiece& name, const Signature& signature) const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - ArtMethod* FindDeclaredVirtualMethod(const DexCache* dex_cache, uint32_t dex_method_idx) const + ArtMethod* FindDeclaredDirectMethod(const DexCache* dex_cache, uint32_t dex_method_idx) const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - ArtMethod* FindVirtualMethod(const StringPiece& name, const StringPiece& descriptor) const + ArtMethod* FindDirectMethod(const StringPiece& name, const StringPiece& signature) const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - ArtMethod* FindVirtualMethod(const DexCache* dex_cache, uint32_t dex_method_idx) const + ArtMethod* FindDirectMethod(const StringPiece& name, const Signature& signature) const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - ArtMethod* FindDeclaredDirectMethod(const StringPiece& name, const StringPiece& signature) const + ArtMethod* FindDirectMethod(const DexCache* dex_cache, uint32_t dex_method_idx) const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - ArtMethod* FindDeclaredDirectMethod(const DexCache* dex_cache, uint32_t dex_method_idx) const + ArtMethod* FindDeclaredVirtualMethod(const StringPiece& name, const StringPiece& signature) const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - ArtMethod* FindDirectMethod(const StringPiece& name, const StringPiece& signature) const + ArtMethod* FindDeclaredVirtualMethod(const StringPiece& name, const Signature& signature) const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - ArtMethod* FindDirectMethod(const DexCache* dex_cache, uint32_t dex_method_idx) const + ArtMethod* FindDeclaredVirtualMethod(const DexCache* dex_cache, uint32_t dex_method_idx) const + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + ArtMethod* FindVirtualMethod(const StringPiece& name, const StringPiece& signature) const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + ArtMethod* FindVirtualMethod(const StringPiece& name, const Signature& signature) const + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + ArtMethod* FindVirtualMethod(const DexCache* dex_cache, uint32_t dex_method_idx) const + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + ArtMethod* FindClassInitializer() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + int32_t GetIfTableCount() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); IfTable* GetIfTable() const; diff --git a/runtime/object_utils.h b/runtime/object_utils.h index 9e107a46d5f..f83db903ff5 100644 --- a/runtime/object_utils.h +++ b/runtime/object_utils.h @@ -504,13 +504,13 @@ class MethodHelper { return shorty_len_; } - const std::string GetSignature() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + const Signature GetSignature() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { const DexFile& dex_file = GetDexFile(); uint32_t dex_method_idx = method_->GetDexMethodIndex(); if (dex_method_idx != DexFile::kDexNoIndex) { return dex_file.GetMethodSignature(dex_file.GetMethodId(dex_method_idx)); } else { - return ""; + return Signature::NoSignature(); } } @@ -638,42 +638,7 @@ class MethodHelper { other_dex_file.StringDataAsStringPieceByIdx(other_mid.name_idx_)) { return false; // Name mismatch. } - const DexFile::ProtoId& proto_id = dex_file.GetMethodPrototype(mid); - const DexFile::ProtoId& other_proto_id = other_dex_file.GetMethodPrototype(other_mid); - if (dex_file.StringDataAsStringPieceByIdx(proto_id.shorty_idx_) != - other_dex_file.StringDataAsStringPieceByIdx(other_proto_id.shorty_idx_)) { - return false; // Shorty mismatch. - } - const DexFile::TypeId& return_type_id = dex_file.GetTypeId(proto_id.return_type_idx_); - const DexFile::TypeId& other_return_type_id = - other_dex_file.GetTypeId(other_proto_id.return_type_idx_); - if (dex_file.StringDataAsStringPieceByIdx(return_type_id.descriptor_idx_) != - other_dex_file.StringDataAsStringPieceByIdx(other_return_type_id.descriptor_idx_)) { - return false; // Return type mismatch. - } - const DexFile::TypeList* params = dex_file.GetProtoParameters(proto_id); - const DexFile::TypeList* other_params = other_dex_file.GetProtoParameters(other_proto_id); - if (params == nullptr) { - return other_params == nullptr; // Check both lists are empty. - } - if (other_params == nullptr) { - return false; // Parameter list size mismatch. - } - uint32_t params_size = params->Size(); - uint32_t other_params_size = other_params->Size(); - if (params_size != other_params_size) { - return false; // Parameter list size mismatch. - } - for (uint32_t i = 0; i < params_size; ++i) { - const DexFile::TypeId& param_id = dex_file.GetTypeId(params->GetTypeItem(i).type_idx_); - const DexFile::TypeId& other_param_id = - other_dex_file.GetTypeId(other_params->GetTypeItem(i).type_idx_); - if (dex_file.StringDataAsStringPieceByIdx(param_id.descriptor_idx_) != - other_dex_file.StringDataAsStringPieceByIdx(other_param_id.descriptor_idx_)) { - return false; // Parameter type mismatch. - } - } - return true; + return dex_file.GetMethodSignature(mid) == other_dex_file.GetMethodSignature(other_mid); } const DexFile::CodeItem* GetCodeItem() diff --git a/runtime/trace.cc b/runtime/trace.cc index 7b25306177c..ec95a87146c 100644 --- a/runtime/trace.cc +++ b/runtime/trace.cc @@ -667,7 +667,7 @@ void Trace::DumpMethodList(std::ostream& os, const std::set& mh.ChangeMethod(method); os << StringPrintf("%p\t%s\t%s\t%s\t%s\n", method, PrettyDescriptor(mh.GetDeclaringClassDescriptor()).c_str(), mh.GetName(), - mh.GetSignature().c_str(), mh.GetDeclaringClassSourceFile()); + mh.GetSignature().ToString().c_str(), mh.GetDeclaringClassSourceFile()); } } diff --git a/runtime/utils.cc b/runtime/utils.cc index 23dcde30658..b97239fd18a 100644 --- a/runtime/utils.cc +++ b/runtime/utils.cc @@ -367,11 +367,13 @@ std::string PrettyMethod(const mirror::ArtMethod* m, bool with_signature) { result += '.'; result += mh.GetName(); if (with_signature) { - std::string signature(mh.GetSignature()); - if (signature == "") { - return result + signature; + const Signature signature = mh.GetSignature(); + std::string sig_as_string(signature.ToString()); + if (signature == Signature::NoSignature()) { + return result + sig_as_string; } - result = PrettyReturnType(signature.c_str()) + " " + result + PrettyArguments(signature.c_str()); + result = PrettyReturnType(sig_as_string.c_str()) + " " + result + + PrettyArguments(sig_as_string.c_str()); } return result; } @@ -385,11 +387,13 @@ std::string PrettyMethod(uint32_t method_idx, const DexFile& dex_file, bool with result += '.'; result += dex_file.GetMethodName(method_id); if (with_signature) { - std::string signature(dex_file.GetMethodSignature(method_id)); - if (signature == "") { - return result + signature; + const Signature signature = dex_file.GetMethodSignature(method_id); + std::string sig_as_string(signature.ToString()); + if (signature == Signature::NoSignature()) { + return result + sig_as_string; } - result = PrettyReturnType(signature.c_str()) + " " + result + PrettyArguments(signature.c_str()); + result = PrettyReturnType(sig_as_string.c_str()) + " " + result + + PrettyArguments(sig_as_string.c_str()); } return result; } @@ -641,7 +645,7 @@ std::string JniLongName(const mirror::ArtMethod* m) { long_name += JniShortName(m); long_name += "__"; - std::string signature(MethodHelper(m).GetSignature()); + std::string signature(MethodHelper(m).GetSignature().ToString()); signature.erase(0, 1); signature.erase(signature.begin() + signature.find(')'), signature.end()); diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc index 6b13517fdc8..36b409d1425 100644 --- a/runtime/verifier/method_verifier.cc +++ b/runtime/verifier/method_verifier.cc @@ -2910,7 +2910,7 @@ const RegType& MethodVerifier::GetCaughtExceptionType() { } mirror::ArtMethod* MethodVerifier::ResolveMethodAndCheckAccess(uint32_t dex_method_idx, - MethodType method_type) { + MethodType method_type) { const DexFile::MethodId& method_id = dex_file_->GetMethodId(dex_method_idx); const RegType& klass_type = ResolveClassAndCheckAccess(method_id.class_idx_); if (klass_type.IsConflict()) { @@ -2927,7 +2927,7 @@ mirror::ArtMethod* MethodVerifier::ResolveMethodAndCheckAccess(uint32_t dex_meth mirror::ArtMethod* res_method = dex_cache_->GetResolvedMethod(dex_method_idx); if (res_method == NULL) { const char* name = dex_file_->GetMethodName(method_id); - std::string signature(dex_file_->CreateMethodSignature(method_id.proto_idx_, NULL)); + const Signature signature = dex_file_->GetMethodSignature(method_id); if (method_type == METHOD_DIRECT || method_type == METHOD_STATIC) { res_method = klass->FindDirectMethod(name, signature); diff --git a/test/Android.mk b/test/Android.mk index 6f498e8c02f..08ec03a01aa 100644 --- a/test/Android.mk +++ b/test/Android.mk @@ -23,8 +23,8 @@ include art/build/Android.common.mk TEST_DEX_DIRECTORIES := \ AbstractMethod \ AllFields \ - CreateMethodSignature \ ExceptionHandle \ + GetMethodSignature \ Interfaces \ Main \ MyClass \ diff --git a/test/CreateMethodSignature/CreateMethodSignature.java b/test/GetMethodSignature/GetMethodSignature.java similarity index 86% rename from test/CreateMethodSignature/CreateMethodSignature.java rename to test/GetMethodSignature/GetMethodSignature.java index f6cd6ae6fd6..c2ba948d601 100644 --- a/test/CreateMethodSignature/CreateMethodSignature.java +++ b/test/GetMethodSignature/GetMethodSignature.java @@ -14,7 +14,7 @@ * limitations under the License. */ -class CreateMethodSignature { +class GetMethodSignature { Float m1(int a, double b, long c, Object d) { return null; } - CreateMethodSignature m2(boolean x, short y, char z) { return null; } + GetMethodSignature m2(boolean x, short y, char z) { return null; } } From 65d1b22d0b02fb0111f69013163c8170e68392f1 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Fri, 27 Sep 2013 10:59:41 -0700 Subject: [PATCH 0060/2402] MIPS fix. Bug 10094803. Resolution stub needs to set $t9 to ensure the global pointer is calculated correctly. Fix copy-paste error in resolution stub in function being invoked. Change-Id: I330b3052f87f47c10cbde2f7e6b271664185d7b0 --- runtime/arch/mips/quick_entrypoints_mips.S | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/runtime/arch/mips/quick_entrypoints_mips.S b/runtime/arch/mips/quick_entrypoints_mips.S index cb8260669f5..f9b703f1134 100644 --- a/runtime/arch/mips/quick_entrypoints_mips.S +++ b/runtime/arch/mips/quick_entrypoints_mips.S @@ -953,7 +953,7 @@ ENTRY art_quick_resolution_trampoline GENERATE_GLOBAL_POINTER SETUP_REF_AND_ARGS_CALLEE_SAVE_FRAME move $a2, rSELF # pass Thread::Current - jal artQuickProxyInvokeHandler # (Method* called, receiver, Thread*, SP) + jal artQuickResolutionTrampoline # (Method* called, receiver, Thread*, SP) move $a3, $sp # pass $sp lw $gp, 52($sp) # restore $gp lw $ra, 60($sp) # restore $ra @@ -962,6 +962,7 @@ ENTRY art_quick_resolution_trampoline lw $a1, 4($sp) # restore non-callee save $a1 lw $a2, 8($sp) # restore non-callee save $a2 lw $a3, 12($sp) # restore non-callee save $a3 + move $t9, $v0 # code pointer must be in $t9 to generate the global pointer jr $v0 # tail call to method 1: addiu $sp, $sp, 64 # pop frame From c61124bdeaae94f977ffc36ac69535e792c226f2 Mon Sep 17 00:00:00 2001 From: Sebastien Hertz Date: Tue, 10 Sep 2013 11:44:19 +0200 Subject: [PATCH 0061/2402] Cleanup invoke in interpreter. Some cleanup in invocation stuff: - Get the number of invoke arguments from instruction (vA) rather than get it from its code item. This benefits to native invoke since we no longer need to parse the method's shorty. Also pass the low 16 bits of instructions to avoid fetching it twice when reading vA. - Remove "is_static" tests by taking advantage of invoke type template argument rather than testing method's access flags. - Ensure Instruction::GetArgs is inlined. - Check exception when initializing method's class when transitioning from interpreter to compiled code (artInterpreterToCompiledCodeBridge). - Move UnstartedRuntimeInvoke function to interpreter_common.cc and make it static as it's only used by DoInvoke and DoInvokeVirtualQuick functions. - Avoid duplicating code in ShadowFrame::Create. Performance remains the same according to benchmarks. Hopefully, this should be addressed in next CLs, especially by improving new shadow frame initialization. Bug: 10668955 Change-Id: I514b8f098d0ef3e35921ceb770383aac1a9c7902 --- runtime/dex_instruction-inl.h | 13 +- runtime/dex_instruction.h | 5 +- .../interpreter/interpreter_entrypoints.cc | 12 +- runtime/interpreter/interpreter.cc | 18 +- runtime/interpreter/interpreter_common.cc | 195 ++++-------------- runtime/interpreter/interpreter_common.h | 79 +++++-- .../interpreter_goto_table_impl.cc | 25 ++- .../interpreter/interpreter_switch_impl.cc | 24 +-- runtime/stack.h | 3 +- 9 files changed, 165 insertions(+), 209 deletions(-) diff --git a/runtime/dex_instruction-inl.h b/runtime/dex_instruction-inl.h index 4d390240135..207b0b67956 100644 --- a/runtime/dex_instruction-inl.h +++ b/runtime/dex_instruction-inl.h @@ -281,7 +281,7 @@ inline uint16_t Instruction::VRegC_3rc() const { return Fetch16(2); } -inline void Instruction::GetArgs(uint32_t arg[5]) const { +inline void Instruction::GetArgs(uint32_t arg[5], uint16_t inst_data) const { DCHECK_EQ(FormatOf(Opcode()), k35c); /* @@ -295,7 +295,8 @@ inline void Instruction::GetArgs(uint32_t arg[5]) const { * method constant (or equivalent) is always in vB. */ uint16_t regList = Fetch16(2); - uint4_t count = InstB(); // This is labeled A in the spec. + uint4_t count = InstB(inst_data); // This is labeled A in the spec. + DCHECK_LE(count, 5U) << "Invalid arg count in 35c (" << count << ")"; /* * Copy the argument registers into the arg[] array, and @@ -305,15 +306,13 @@ inline void Instruction::GetArgs(uint32_t arg[5]) const { * copies of those.) Note that cases 5..2 fall through. */ switch (count) { - case 5: arg[4] = InstA(); + case 5: arg[4] = InstA(inst_data); case 4: arg[3] = (regList >> 12) & 0x0f; case 3: arg[2] = (regList >> 8) & 0x0f; case 2: arg[1] = (regList >> 4) & 0x0f; case 1: arg[0] = regList & 0x0f; break; - case 0: break; // Valid, but no need to do anything. - default: - LOG(ERROR) << "Invalid arg count in 35c (" << count << ")"; - return; + default: // case 0 + break; // Valid, but no need to do anything. } } diff --git a/runtime/dex_instruction.h b/runtime/dex_instruction.h index e8db3bcf4f6..c434cdd938e 100644 --- a/runtime/dex_instruction.h +++ b/runtime/dex_instruction.h @@ -365,7 +365,10 @@ class Instruction { uint16_t VRegC_3rc() const; // Fills the given array with the 'arg' array of the instruction. - void GetArgs(uint32_t args[5]) const; + void GetArgs(uint32_t args[5], uint16_t inst_data) const; + void GetArgs(uint32_t args[5]) const { + return GetArgs(args, Fetch16(0)); + } // Returns the opcode field of the instruction. The given "inst_data" parameter must be the first // 16 bits of instruction. diff --git a/runtime/entrypoints/interpreter/interpreter_entrypoints.cc b/runtime/entrypoints/interpreter/interpreter_entrypoints.cc index ecf98bc722c..05c02f22fa0 100644 --- a/runtime/entrypoints/interpreter/interpreter_entrypoints.cc +++ b/runtime/entrypoints/interpreter/interpreter_entrypoints.cc @@ -31,7 +31,15 @@ extern "C" void artInterpreterToCompiledCodeBridge(Thread* self, MethodHelper& m mirror::ArtMethod* method = shadow_frame->GetMethod(); // Ensure static methods are initialized. if (method->IsStatic()) { - Runtime::Current()->GetClassLinker()->EnsureInitialized(method->GetDeclaringClass(), true, true); + mirror::Class* declaringClass = method->GetDeclaringClass(); + if (UNLIKELY(!declaringClass->IsInitializing())) { + if (UNLIKELY(!Runtime::Current()->GetClassLinker()->EnsureInitialized(declaringClass, + true, true))) { + DCHECK(Thread::Current()->IsExceptionPending()); + return; + } + CHECK(declaringClass->IsInitializing()); + } } uint16_t arg_offset = (code_item == NULL) ? 0 : code_item->registers_size_ - code_item->ins_size_; #if defined(ART_USE_PORTABLE_COMPILER) @@ -40,7 +48,7 @@ extern "C" void artInterpreterToCompiledCodeBridge(Thread* self, MethodHelper& m method->Invoke(self, arg_array.GetArray(), arg_array.GetNumBytes(), result, mh.GetShorty()[0]); #else method->Invoke(self, shadow_frame->GetVRegArgs(arg_offset), - (shadow_frame->NumberOfVRegs() - arg_offset) * 4, + (shadow_frame->NumberOfVRegs() - arg_offset) * sizeof(uint32_t), result, mh.GetShorty()[0]); #endif } diff --git a/runtime/interpreter/interpreter.cc b/runtime/interpreter/interpreter.cc index fd92e06fb3a..8aa6fa21048 100644 --- a/runtime/interpreter/interpreter.cc +++ b/runtime/interpreter/interpreter.cc @@ -422,15 +422,18 @@ extern "C" void artInterpreterToInterpreterBridge(Thread* self, MethodHelper& mh } ArtMethod* method = shadow_frame->GetMethod(); - if (method->IsStatic() && !method->GetDeclaringClass()->IsInitializing()) { - if (!Runtime::Current()->GetClassLinker()->EnsureInitialized(method->GetDeclaringClass(), - true, true)) { - DCHECK(Thread::Current()->IsExceptionPending()); - return; + // Ensure static methods are initialized. + if (method->IsStatic()) { + Class* declaringClass = method->GetDeclaringClass(); + if (UNLIKELY(!declaringClass->IsInitializing())) { + if (UNLIKELY(!Runtime::Current()->GetClassLinker()->EnsureInitialized(declaringClass, + true, true))) { + DCHECK(Thread::Current()->IsExceptionPending()); + return; + } + CHECK(declaringClass->IsInitializing()); } - CHECK(method->GetDeclaringClass()->IsInitializing()); } - self->PushShadowFrame(shadow_frame); if (LIKELY(!method->IsNative())) { @@ -445,7 +448,6 @@ extern "C" void artInterpreterToInterpreterBridge(Thread* self, MethodHelper& mh } self->PopShadowFrame(); - return; } } // namespace interpreter diff --git a/runtime/interpreter/interpreter_common.cc b/runtime/interpreter/interpreter_common.cc index 36b250ca425..4992031f254 100644 --- a/runtime/interpreter/interpreter_common.cc +++ b/runtime/interpreter/interpreter_common.cc @@ -19,62 +19,59 @@ namespace art { namespace interpreter { -template -bool DoInvoke(Thread* self, ShadowFrame& shadow_frame, - const Instruction* inst, JValue* result) { - bool do_assignability_check = do_access_check; - uint32_t method_idx = (is_range) ? inst->VRegB_3rc() : inst->VRegB_35c(); - uint32_t vregC = (is_range) ? inst->VRegC_3rc() : inst->VRegC_35c(); - Object* receiver = (type == kStatic) ? NULL : shadow_frame.GetVRegReference(vregC); - ArtMethod* method = FindMethodFromCode(method_idx, receiver, shadow_frame.GetMethod(), self, - do_access_check, type); - if (UNLIKELY(method == NULL)) { - CHECK(self->IsExceptionPending()); - result->SetJ(0); - return false; - } else if (UNLIKELY(method->IsAbstract())) { - ThrowAbstractMethodError(method); - result->SetJ(0); - return false; - } +static void UnstartedRuntimeInvoke(Thread* self, MethodHelper& mh, + const DexFile::CodeItem* code_item, ShadowFrame* shadow_frame, + JValue* result, size_t arg_offset) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); +template +bool DoCall(ArtMethod* method, Object* receiver, Thread* self, ShadowFrame& shadow_frame, + const Instruction* inst, uint16_t inst_data, JValue* result) { + // Compute method information. MethodHelper mh(method); const DexFile::CodeItem* code_item = mh.GetCodeItem(); + const uint16_t num_ins = (is_range) ? inst->VRegA_3rc(inst_data) : inst->VRegA_35c(inst_data); uint16_t num_regs; - uint16_t num_ins; if (LIKELY(code_item != NULL)) { num_regs = code_item->registers_size_; - num_ins = code_item->ins_size_; + DCHECK_EQ(num_ins, code_item->ins_size_); } else { DCHECK(method->IsNative() || method->IsProxyMethod()); - num_regs = num_ins = ArtMethod::NumArgRegisters(mh.GetShorty()); - if (!method->IsStatic()) { - num_regs++; - num_ins++; - } + num_regs = num_ins; } + // Allocate shadow frame on the stack. void* memory = alloca(ShadowFrame::ComputeSize(num_regs)); ShadowFrame* new_shadow_frame(ShadowFrame::Create(num_regs, &shadow_frame, method, 0, memory)); + + // Initialize new shadow frame. size_t cur_reg = num_regs - num_ins; + size_t arg_offset = 0; if (receiver != NULL) { + DCHECK(!method->IsStatic()); new_shadow_frame->SetVRegReference(cur_reg, receiver); ++cur_reg; + ++arg_offset; + } else { + DCHECK(method->IsStatic()); } const DexFile::TypeList* params; if (do_assignability_check) { params = mh.GetParameterTypeList(); } - size_t arg_offset = (receiver == NULL) ? 0 : 1; const char* shorty = mh.GetShorty(); - uint32_t arg[5]; - if (!is_range) { - inst->GetArgs(arg); + // TODO: find a cleaner way to separate non-range and range information. + uint32_t arg[5]; // only used in invoke-XXX. + uint32_t vregC; // only used in invoke-XXX-range. + if (is_range) { + vregC = inst->VRegC_3rc(); + } else { + inst->GetArgs(arg, inst_data); } - for (size_t shorty_pos = 0; cur_reg < num_regs; ++shorty_pos, cur_reg++, arg_offset++) { + for (size_t shorty_pos = 0; cur_reg < num_regs; ++shorty_pos, ++cur_reg, ++arg_offset) { DCHECK_LT(shorty_pos + 1, mh.GetShortyLength()); - size_t arg_pos = is_range ? vregC + arg_offset : arg[arg_offset]; + size_t arg_pos = (is_range) ? vregC + arg_offset : arg[arg_offset]; switch (shorty[shorty_pos + 1]) { case 'L': { Object* o = shadow_frame.GetVRegReference(arg_pos); @@ -102,94 +99,8 @@ bool DoInvoke(Thread* self, ShadowFrame& shadow_frame, uint64_t wide_value = (static_cast(shadow_frame.GetVReg(arg_pos + 1)) << 32) | static_cast(shadow_frame.GetVReg(arg_pos)); new_shadow_frame->SetVRegLong(cur_reg, wide_value); - cur_reg++; - arg_offset++; - break; - } - default: - new_shadow_frame->SetVReg(cur_reg, shadow_frame.GetVReg(arg_pos)); - break; - } - } - - if (LIKELY(Runtime::Current()->IsStarted())) { - (method->GetEntryPointFromInterpreter())(self, mh, code_item, new_shadow_frame, result); - } else { - UnstartedRuntimeInvoke(self, mh, code_item, new_shadow_frame, result, num_regs - num_ins); - } - return !self->IsExceptionPending(); -} - -template -bool DoInvokeVirtualQuick(Thread* self, ShadowFrame& shadow_frame, - const Instruction* inst, JValue* result) { - uint32_t vregC = (is_range) ? inst->VRegC_3rc() : inst->VRegC_35c(); - Object* receiver = shadow_frame.GetVRegReference(vregC); - if (UNLIKELY(receiver == NULL)) { - // We lost the reference to the method index so we cannot get a more - // precised exception message. - ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow()); - return false; - } - uint32_t vtable_idx = (is_range) ? inst->VRegB_3rc() : inst->VRegB_35c(); - // TODO: use ObjectArray::GetWithoutChecks ? - ArtMethod* method = receiver->GetClass()->GetVTable()->Get(vtable_idx); - if (UNLIKELY(method == NULL)) { - CHECK(self->IsExceptionPending()); - result->SetJ(0); - return false; - } else if (UNLIKELY(method->IsAbstract())) { - ThrowAbstractMethodError(method); - result->SetJ(0); - return false; - } - - MethodHelper mh(method); - const DexFile::CodeItem* code_item = mh.GetCodeItem(); - uint16_t num_regs; - uint16_t num_ins; - if (code_item != NULL) { - num_regs = code_item->registers_size_; - num_ins = code_item->ins_size_; - } else { - DCHECK(method->IsNative() || method->IsProxyMethod()); - num_regs = num_ins = ArtMethod::NumArgRegisters(mh.GetShorty()); - if (!method->IsStatic()) { - num_regs++; - num_ins++; - } - } - - void* memory = alloca(ShadowFrame::ComputeSize(num_regs)); - ShadowFrame* new_shadow_frame(ShadowFrame::Create(num_regs, &shadow_frame, - method, 0, memory)); - size_t cur_reg = num_regs - num_ins; - if (receiver != NULL) { - new_shadow_frame->SetVRegReference(cur_reg, receiver); - ++cur_reg; - } - - size_t arg_offset = (receiver == NULL) ? 0 : 1; - const char* shorty = mh.GetShorty(); - uint32_t arg[5]; - if (!is_range) { - inst->GetArgs(arg); - } - for (size_t shorty_pos = 0; cur_reg < num_regs; ++shorty_pos, cur_reg++, arg_offset++) { - DCHECK_LT(shorty_pos + 1, mh.GetShortyLength()); - size_t arg_pos = is_range ? vregC + arg_offset : arg[arg_offset]; - switch (shorty[shorty_pos + 1]) { - case 'L': { - Object* o = shadow_frame.GetVRegReference(arg_pos); - new_shadow_frame->SetVRegReference(cur_reg, o); - break; - } - case 'J': case 'D': { - uint64_t wide_value = (static_cast(shadow_frame.GetVReg(arg_pos + 1)) << 32) | - static_cast(shadow_frame.GetVReg(arg_pos)); - new_shadow_frame->SetVRegLong(cur_reg, wide_value); - cur_reg++; - arg_offset++; + ++cur_reg; + ++arg_offset; break; } default: @@ -198,6 +109,7 @@ bool DoInvokeVirtualQuick(Thread* self, ShadowFrame& shadow_frame, } } + // Do the call now. if (LIKELY(Runtime::Current()->IsStarted())) { (method->GetEntryPointFromInterpreter())(self, mh, code_item, new_shadow_frame, result); } else { @@ -273,9 +185,9 @@ bool DoFilledNewArray(const Instruction* inst, const ShadowFrame& shadow_frame, return true; } -void UnstartedRuntimeInvoke(Thread* self, MethodHelper& mh, - const DexFile::CodeItem* code_item, ShadowFrame* shadow_frame, - JValue* result, size_t arg_offset) { +static void UnstartedRuntimeInvoke(Thread* self, MethodHelper& mh, + const DexFile::CodeItem* code_item, ShadowFrame* shadow_frame, + JValue* result, size_t arg_offset) { // In a runtime that's not started we intercept certain methods to avoid complicated dependency // problems in core libraries. std::string name(PrettyMethod(shadow_frame->GetMethod())); @@ -367,34 +279,17 @@ void UnstartedRuntimeInvoke(Thread* self, MethodHelper& mh, } } -// Explicit DoInvoke template function declarations. -#define EXPLICIT_DO_INVOKE_TEMPLATE_DECL(_type, _is_range_, _check) \ - template bool DoInvoke<_type, _is_range_, _check>(Thread* self, ShadowFrame& shadow_frame, \ - const Instruction* inst, JValue* result) - -#define EXPLICIT_DO_INVOKE_TEMPLATE_DECL_VARIANTS(_type) \ - EXPLICIT_DO_INVOKE_TEMPLATE_DECL(_type, false, false); \ - EXPLICIT_DO_INVOKE_TEMPLATE_DECL(_type, false, true); \ - EXPLICIT_DO_INVOKE_TEMPLATE_DECL(_type, true, false); \ - EXPLICIT_DO_INVOKE_TEMPLATE_DECL(_type, true, true) - -EXPLICIT_DO_INVOKE_TEMPLATE_DECL_VARIANTS(kStatic); -EXPLICIT_DO_INVOKE_TEMPLATE_DECL_VARIANTS(kDirect); -EXPLICIT_DO_INVOKE_TEMPLATE_DECL_VARIANTS(kVirtual); -EXPLICIT_DO_INVOKE_TEMPLATE_DECL_VARIANTS(kSuper); -EXPLICIT_DO_INVOKE_TEMPLATE_DECL_VARIANTS(kInterface); - -#undef EXPLICIT_DO_INVOKE_TEMPLATE_DECL_VARIANTS -#undef EXPLICIT_DO_INVOKE_TEMPLATE_DECL - -// Explicit DoInvokeVirtualQuick template function declarations. -#define EXPLICIT_DO_INVOKE_VIRTUAL_QUICK_TEMPLATE_DECL(_is_range) \ -template bool DoInvokeVirtualQuick<_is_range>(Thread* self, ShadowFrame& shadow_frame, \ - const Instruction* inst, JValue* result) - -EXPLICIT_DO_INVOKE_VIRTUAL_QUICK_TEMPLATE_DECL(false); -EXPLICIT_DO_INVOKE_VIRTUAL_QUICK_TEMPLATE_DECL(true); -#undef EXPLICIT_DO_INVOKE_VIRTUAL_QUICK_TEMPLATE_DECL +// Explicit DoCall template function declarations. +#define EXPLICIT_DO_CALL_TEMPLATE_DECL(_is_range, _do_assignability_check) \ +template bool DoCall<_is_range, _do_assignability_check>(ArtMethod* method, Object* receiver, \ + Thread* self, ShadowFrame& shadow_frame, \ + const Instruction* inst, \ + uint16_t inst_data, JValue* result) +EXPLICIT_DO_CALL_TEMPLATE_DECL(false, false); +EXPLICIT_DO_CALL_TEMPLATE_DECL(false, true); +EXPLICIT_DO_CALL_TEMPLATE_DECL(true, false); +EXPLICIT_DO_CALL_TEMPLATE_DECL(true, true); +#undef EXPLICIT_DO_CALL_TEMPLATE_DECL // Explicit DoFilledNewArray template function declarations. #define EXPLICIT_DO_FILLED_NEW_ARRAY_TEMPLATE_DECL(_is_range_, _check) \ diff --git a/runtime/interpreter/interpreter_common.h b/runtime/interpreter/interpreter_common.h index 3ad1935c942..80502b44f63 100644 --- a/runtime/interpreter/interpreter_common.h +++ b/runtime/interpreter/interpreter_common.h @@ -71,7 +71,7 @@ template extern JValue ExecuteSwitchImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* code_item, ShadowFrame& shadow_frame, JValue result_register) - NO_THREAD_SAFETY_ANALYSIS __attribute__((hot)); + NO_THREAD_SAFETY_ANALYSIS HOT_ATTR; // TODO: should be SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) which is failing due to template // specialization. @@ -79,12 +79,7 @@ template extern JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* code_item, ShadowFrame& shadow_frame, JValue result_register) - NO_THREAD_SAFETY_ANALYSIS __attribute__((hot)); - -void UnstartedRuntimeInvoke(Thread* self, MethodHelper& mh, - const DexFile::CodeItem* code_item, ShadowFrame* shadow_frame, - JValue* result, size_t arg_offset) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + NO_THREAD_SAFETY_ANALYSIS HOT_ATTR; static inline void DoMonitorEnter(Thread* self, Object* ref) NO_THREAD_SAFETY_ANALYSIS { ref->MonitorEnter(self); @@ -94,18 +89,74 @@ static inline void DoMonitorExit(Thread* self, Object* ref) NO_THREAD_SAFETY_ANA ref->MonitorExit(self); } +// TODO: should be SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) which is failing due to template +// specialization. +template +bool DoCall(ArtMethod* method, Object* receiver, Thread* self, ShadowFrame& shadow_frame, + const Instruction* inst, uint16_t inst_data, JValue* result) NO_THREAD_SAFETY_ANALYSIS; + // TODO: should be SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) which is failing due to template // specialization. template -bool DoInvoke(Thread* self, ShadowFrame& shadow_frame, - const Instruction* inst, JValue* result) NO_THREAD_SAFETY_ANALYSIS; +static bool DoInvoke(Thread* self, ShadowFrame& shadow_frame, const Instruction* inst, + uint16_t inst_data, JValue* result) NO_THREAD_SAFETY_ANALYSIS ALWAYS_INLINE; + +template +static inline bool DoInvoke(Thread* self, ShadowFrame& shadow_frame, const Instruction* inst, + uint16_t inst_data, JValue* result) { + const uint32_t method_idx = (is_range) ? inst->VRegB_3rc() : inst->VRegB_35c(); + const uint32_t vregC = (is_range) ? inst->VRegC_3rc() : inst->VRegC_35c(); + Object* const receiver = (type == kStatic) ? NULL : shadow_frame.GetVRegReference(vregC); + ArtMethod* const method = FindMethodFromCode(method_idx, receiver, shadow_frame.GetMethod(), self, + do_access_check, type); + if (UNLIKELY(method == NULL)) { + CHECK(self->IsExceptionPending()); + result->SetJ(0); + return false; + } else if (UNLIKELY(method->IsAbstract())) { + ThrowAbstractMethodError(method); + result->SetJ(0); + return false; + } else { + return DoCall(method, receiver, self, shadow_frame, inst, + inst_data, result); + } +} // TODO: should be SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) which is failing due to template // specialization. template -bool DoInvokeVirtualQuick(Thread* self, ShadowFrame& shadow_frame, - const Instruction* inst, JValue* result) - NO_THREAD_SAFETY_ANALYSIS; +static bool DoInvokeVirtualQuick(Thread* self, ShadowFrame& shadow_frame, const Instruction* inst, + uint16_t inst_data, JValue* result) + NO_THREAD_SAFETY_ANALYSIS ALWAYS_INLINE; + +template +static inline bool DoInvokeVirtualQuick(Thread* self, ShadowFrame& shadow_frame, + const Instruction* inst, uint16_t inst_data, + JValue* result) { + const uint32_t vregC = (is_range) ? inst->VRegC_3rc() : inst->VRegC_35c(); + Object* const receiver = shadow_frame.GetVRegReference(vregC); + if (UNLIKELY(receiver == NULL)) { + // We lost the reference to the method index so we cannot get a more + // precised exception message. + ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow()); + return false; + } + const uint32_t vtable_idx = (is_range) ? inst->VRegB_3rc() : inst->VRegB_35c(); + ArtMethod* const method = receiver->GetClass()->GetVTable()->GetWithoutChecks(vtable_idx); + if (UNLIKELY(method == NULL)) { + CHECK(self->IsExceptionPending()); + result->SetJ(0); + return false; + } else if (UNLIKELY(method->IsAbstract())) { + ThrowAbstractMethodError(method); + result->SetJ(0); + return false; + } else { + // No need to check since we've been quickened. + return DoCall(method, receiver, self, shadow_frame, inst, inst_data, result); + } +} // We use template functions to optimize compiler inlining process. Otherwise, // some parts of the code (like a switch statement) which depend on a constant @@ -283,11 +334,11 @@ static inline bool DoFieldPut(Thread* self, const ShadowFrame& shadow_frame, // TODO: should be SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) which is failing due to template // specialization. template -static bool DoIPutQuick(ShadowFrame& shadow_frame, const Instruction* inst, uint16_t inst_data) +static bool DoIPutQuick(const ShadowFrame& shadow_frame, const Instruction* inst, uint16_t inst_data) NO_THREAD_SAFETY_ANALYSIS ALWAYS_INLINE; template -static inline bool DoIPutQuick(ShadowFrame& shadow_frame, const Instruction* inst, uint16_t inst_data) { +static inline bool DoIPutQuick(const ShadowFrame& shadow_frame, const Instruction* inst, uint16_t inst_data) { Object* obj = shadow_frame.GetVRegReference(inst->VRegB_22c(inst_data)); if (UNLIKELY(obj == NULL)) { // We lost the reference to the field index so we cannot get a more diff --git a/runtime/interpreter/interpreter_goto_table_impl.cc b/runtime/interpreter/interpreter_goto_table_impl.cc index 018add30070..5a008319f8b 100644 --- a/runtime/interpreter/interpreter_goto_table_impl.cc +++ b/runtime/interpreter/interpreter_goto_table_impl.cc @@ -29,7 +29,6 @@ namespace interpreter { // - "currentHandlersTable": the current table of pointer to each instruction handler. // Advance to the next instruction and updates interpreter state. -// TODO: move check suspend to backward branch, return and exception handling. #define ADVANCE(_offset) \ do { \ int32_t disp = static_cast(_offset); \ @@ -1339,84 +1338,84 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(INVOKE_VIRTUAL) { - bool success = DoInvoke(self, shadow_frame, inst, &result_register); + bool success = DoInvoke(self, shadow_frame, inst, inst_data, &result_register); UPDATE_HANDLER_TABLE(); POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 3); } HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(INVOKE_VIRTUAL_RANGE) { - bool success = DoInvoke(self, shadow_frame, inst, &result_register); + bool success = DoInvoke(self, shadow_frame, inst, inst_data, &result_register); UPDATE_HANDLER_TABLE(); POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 3); } HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(INVOKE_SUPER) { - bool success = DoInvoke(self, shadow_frame, inst, &result_register); + bool success = DoInvoke(self, shadow_frame, inst, inst_data, &result_register); UPDATE_HANDLER_TABLE(); POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 3); } HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(INVOKE_SUPER_RANGE) { - bool success = DoInvoke(self, shadow_frame, inst, &result_register); + bool success = DoInvoke(self, shadow_frame, inst, inst_data, &result_register); UPDATE_HANDLER_TABLE(); POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 3); } HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(INVOKE_DIRECT) { - bool success = DoInvoke(self, shadow_frame, inst, &result_register); + bool success = DoInvoke(self, shadow_frame, inst, inst_data, &result_register); UPDATE_HANDLER_TABLE(); POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 3); } HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(INVOKE_DIRECT_RANGE) { - bool success = DoInvoke(self, shadow_frame, inst, &result_register); + bool success = DoInvoke(self, shadow_frame, inst, inst_data, &result_register); UPDATE_HANDLER_TABLE(); POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 3); } HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(INVOKE_INTERFACE) { - bool success = DoInvoke(self, shadow_frame, inst, &result_register); + bool success = DoInvoke(self, shadow_frame, inst, inst_data, &result_register); UPDATE_HANDLER_TABLE(); POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 3); } HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(INVOKE_INTERFACE_RANGE) { - bool success = DoInvoke(self, shadow_frame, inst, &result_register); + bool success = DoInvoke(self, shadow_frame, inst, inst_data, &result_register); UPDATE_HANDLER_TABLE(); POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 3); } HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(INVOKE_STATIC) { - bool success = DoInvoke(self, shadow_frame, inst, &result_register); + bool success = DoInvoke(self, shadow_frame, inst, inst_data, &result_register); UPDATE_HANDLER_TABLE(); POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 3); } HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(INVOKE_STATIC_RANGE) { - bool success = DoInvoke(self, shadow_frame, inst, &result_register); + bool success = DoInvoke(self, shadow_frame, inst, inst_data, &result_register); UPDATE_HANDLER_TABLE(); POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 3); } HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(INVOKE_VIRTUAL_QUICK) { - bool success = DoInvokeVirtualQuick(self, shadow_frame, inst, &result_register); + bool success = DoInvokeVirtualQuick(self, shadow_frame, inst, inst_data, &result_register); UPDATE_HANDLER_TABLE(); POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 3); } HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(INVOKE_VIRTUAL_RANGE_QUICK) { - bool success = DoInvokeVirtualQuick(self, shadow_frame, inst, &result_register); + bool success = DoInvokeVirtualQuick(self, shadow_frame, inst, inst_data, &result_register); UPDATE_HANDLER_TABLE(); POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 3); } diff --git a/runtime/interpreter/interpreter_switch_impl.cc b/runtime/interpreter/interpreter_switch_impl.cc index 2d658b3784f..82f216aa41c 100644 --- a/runtime/interpreter/interpreter_switch_impl.cc +++ b/runtime/interpreter/interpreter_switch_impl.cc @@ -1283,73 +1283,73 @@ static JValue ExecuteSwitchImpl(Thread* self, MethodHelper& mh, const DexFile::C } case Instruction::INVOKE_VIRTUAL: { PREAMBLE(); - bool success = DoInvoke(self, shadow_frame, inst, &result_register); + bool success = DoInvoke(self, shadow_frame, inst, inst_data, &result_register); POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_3xx); break; } case Instruction::INVOKE_VIRTUAL_RANGE: { PREAMBLE(); - bool success = DoInvoke(self, shadow_frame, inst, &result_register); + bool success = DoInvoke(self, shadow_frame, inst, inst_data, &result_register); POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_3xx); break; } case Instruction::INVOKE_SUPER: { PREAMBLE(); - bool success = DoInvoke(self, shadow_frame, inst, &result_register); + bool success = DoInvoke(self, shadow_frame, inst, inst_data, &result_register); POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_3xx); break; } case Instruction::INVOKE_SUPER_RANGE: { PREAMBLE(); - bool success = DoInvoke(self, shadow_frame, inst, &result_register); + bool success = DoInvoke(self, shadow_frame, inst, inst_data, &result_register); POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_3xx); break; } case Instruction::INVOKE_DIRECT: { PREAMBLE(); - bool success = DoInvoke(self, shadow_frame, inst, &result_register); + bool success = DoInvoke(self, shadow_frame, inst, inst_data, &result_register); POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_3xx); break; } case Instruction::INVOKE_DIRECT_RANGE: { PREAMBLE(); - bool success = DoInvoke(self, shadow_frame, inst, &result_register); + bool success = DoInvoke(self, shadow_frame, inst, inst_data, &result_register); POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_3xx); break; } case Instruction::INVOKE_INTERFACE: { PREAMBLE(); - bool success = DoInvoke(self, shadow_frame, inst, &result_register); + bool success = DoInvoke(self, shadow_frame, inst, inst_data, &result_register); POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_3xx); break; } case Instruction::INVOKE_INTERFACE_RANGE: { PREAMBLE(); - bool success = DoInvoke(self, shadow_frame, inst, &result_register); + bool success = DoInvoke(self, shadow_frame, inst, inst_data, &result_register); POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_3xx); break; } case Instruction::INVOKE_STATIC: { PREAMBLE(); - bool success = DoInvoke(self, shadow_frame, inst, &result_register); + bool success = DoInvoke(self, shadow_frame, inst, inst_data, &result_register); POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_3xx); break; } case Instruction::INVOKE_STATIC_RANGE: { PREAMBLE(); - bool success = DoInvoke(self, shadow_frame, inst, &result_register); + bool success = DoInvoke(self, shadow_frame, inst, inst_data, &result_register); POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_3xx); break; } case Instruction::INVOKE_VIRTUAL_QUICK: { PREAMBLE(); - bool success = DoInvokeVirtualQuick(self, shadow_frame, inst, &result_register); + bool success = DoInvokeVirtualQuick(self, shadow_frame, inst, inst_data, &result_register); POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_3xx); break; } case Instruction::INVOKE_VIRTUAL_RANGE_QUICK: { PREAMBLE(); - bool success = DoInvokeVirtualQuick(self, shadow_frame, inst, &result_register); + bool success = DoInvokeVirtualQuick(self, shadow_frame, inst, inst_data, &result_register); POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_3xx); break; } diff --git a/runtime/stack.h b/runtime/stack.h index 2bf3340c995..700b7f19603 100644 --- a/runtime/stack.h +++ b/runtime/stack.h @@ -68,8 +68,7 @@ class ShadowFrame { static ShadowFrame* Create(uint32_t num_vregs, ShadowFrame* link, mirror::ArtMethod* method, uint32_t dex_pc) { uint8_t* memory = new uint8_t[ComputeSize(num_vregs)]; - ShadowFrame* sf = new (memory) ShadowFrame(num_vregs, link, method, dex_pc, true); - return sf; + return Create(num_vregs, link, method, dex_pc, memory); } // Create ShadowFrame for interpreter using provided memory. From 9ace87b47a756d519e8897d6c1f0f4664ed82f86 Mon Sep 17 00:00:00 2001 From: Sebastien Hertz Date: Fri, 27 Sep 2013 11:48:09 +0200 Subject: [PATCH 0062/2402] Optimize shadow frame initialization during invoke. This CL improves the initialization of the new shadow frame (callee) from the current shadow frame (caller) following invoke format (non-range or range). This also removes the use of Instruction::GetArgs for non-range invoke when we can go the fast route (no use of shorty). This avoids decoding arguments into the stack from the instruction and then read these arguments from the stack. We now do both at once. Note the slow path (doing runtime checks) is not modified since it should be rarely used. Performance improved by 3% in average on our benchmarks. Good news it is more important on benchmark doing lots of invokes: - Dhrystone: +11% - DeltaBlue: +7% - CaffeineMark Method: +16% Bug: 10668955 Change-Id: I5c230777b2388a78dca7b3a88189e007c1c2bb7a --- runtime/interpreter/interpreter_common.cc | 150 ++++++++++++++-------- 1 file changed, 93 insertions(+), 57 deletions(-) diff --git a/runtime/interpreter/interpreter_common.cc b/runtime/interpreter/interpreter_common.cc index 4992031f254..6f87a8f35ba 100644 --- a/runtime/interpreter/interpreter_common.cc +++ b/runtime/interpreter/interpreter_common.cc @@ -24,6 +24,19 @@ static void UnstartedRuntimeInvoke(Thread* self, MethodHelper& mh, JValue* result, size_t arg_offset) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); +// Assign register 'src_reg' from shadow_frame to register 'dest_reg' into new_shadow_frame. +static inline void AssignRegister(ShadowFrame& new_shadow_frame, const ShadowFrame& shadow_frame, + size_t dest_reg, size_t src_reg) { + // If both register locations contains the same value, the register probably holds a reference. + int32_t src_value = shadow_frame.GetVReg(src_reg); + mirror::Object* o = shadow_frame.GetVRegReference(src_reg); + if (src_value == reinterpret_cast(o)) { + new_shadow_frame.SetVRegReference(dest_reg, o); + } else { + new_shadow_frame.SetVReg(dest_reg, src_value); + } +} + template bool DoCall(ArtMethod* method, Object* receiver, Thread* self, ShadowFrame& shadow_frame, const Instruction* inst, uint16_t inst_data, JValue* result) { @@ -45,67 +58,90 @@ bool DoCall(ArtMethod* method, Object* receiver, Thread* self, ShadowFrame& shad ShadowFrame* new_shadow_frame(ShadowFrame::Create(num_regs, &shadow_frame, method, 0, memory)); // Initialize new shadow frame. - size_t cur_reg = num_regs - num_ins; - size_t arg_offset = 0; - if (receiver != NULL) { - DCHECK(!method->IsStatic()); - new_shadow_frame->SetVRegReference(cur_reg, receiver); - ++cur_reg; - ++arg_offset; - } else { - DCHECK(method->IsStatic()); - } - - const DexFile::TypeList* params; + const size_t first_dest_reg = num_regs - num_ins; if (do_assignability_check) { - params = mh.GetParameterTypeList(); - } - const char* shorty = mh.GetShorty(); - // TODO: find a cleaner way to separate non-range and range information. - uint32_t arg[5]; // only used in invoke-XXX. - uint32_t vregC; // only used in invoke-XXX-range. - if (is_range) { - vregC = inst->VRegC_3rc(); - } else { - inst->GetArgs(arg, inst_data); - } - for (size_t shorty_pos = 0; cur_reg < num_regs; ++shorty_pos, ++cur_reg, ++arg_offset) { - DCHECK_LT(shorty_pos + 1, mh.GetShortyLength()); - size_t arg_pos = (is_range) ? vregC + arg_offset : arg[arg_offset]; - switch (shorty[shorty_pos + 1]) { - case 'L': { - Object* o = shadow_frame.GetVRegReference(arg_pos); - if (do_assignability_check && o != NULL) { - Class* arg_type = mh.GetClassFromTypeIdx(params->GetTypeItem(shorty_pos).type_idx_); - if (arg_type == NULL) { - CHECK(self->IsExceptionPending()); - return false; - } - if (!o->VerifierInstanceOf(arg_type)) { - // This should never happen. - self->ThrowNewExceptionF(self->GetCurrentLocationForThrow(), - "Ljava/lang/VirtualMachineError;", - "Invoking %s with bad arg %d, type '%s' not instance of '%s'", - mh.GetName(), shorty_pos, - ClassHelper(o->GetClass()).GetDescriptor(), - ClassHelper(arg_type).GetDescriptor()); - return false; + // Slow path: we need to do runtime check on reference assignment. We need to load the shorty + // to get the exact type of each reference argument. + const DexFile::TypeList* params = mh.GetParameterTypeList(); + const char* shorty = mh.GetShorty(); + + // Handle receiver apart since it's not part of the shorty. + size_t dest_reg = first_dest_reg; + size_t arg_offset = 0; + if (receiver != NULL) { + DCHECK(!method->IsStatic()); + new_shadow_frame->SetVRegReference(dest_reg, receiver); + ++dest_reg; + ++arg_offset; + } else { + DCHECK(method->IsStatic()); + } + // TODO: find a cleaner way to separate non-range and range information without duplicating code. + uint32_t arg[5]; // only used in invoke-XXX. + uint32_t vregC; // only used in invoke-XXX-range. + if (is_range) { + vregC = inst->VRegC_3rc(); + } else { + inst->GetArgs(arg, inst_data); + } + for (size_t shorty_pos = 0; dest_reg < num_regs; ++shorty_pos, ++dest_reg, ++arg_offset) { + DCHECK_LT(shorty_pos + 1, mh.GetShortyLength()); + const size_t src_reg = (is_range) ? vregC + arg_offset : arg[arg_offset]; + switch (shorty[shorty_pos + 1]) { + case 'L': { + Object* o = shadow_frame.GetVRegReference(src_reg); + if (do_assignability_check && o != NULL) { + Class* arg_type = mh.GetClassFromTypeIdx(params->GetTypeItem(shorty_pos).type_idx_); + if (arg_type == NULL) { + CHECK(self->IsExceptionPending()); + return false; + } + if (!o->VerifierInstanceOf(arg_type)) { + // This should never happen. + self->ThrowNewExceptionF(self->GetCurrentLocationForThrow(), + "Ljava/lang/VirtualMachineError;", + "Invoking %s with bad arg %d, type '%s' not instance of '%s'", + mh.GetName(), shorty_pos, + ClassHelper(o->GetClass()).GetDescriptor(), + ClassHelper(arg_type).GetDescriptor()); + return false; + } } + new_shadow_frame->SetVRegReference(dest_reg, o); + break; } - new_shadow_frame->SetVRegReference(cur_reg, o); - break; + case 'J': case 'D': { + uint64_t wide_value = (static_cast(shadow_frame.GetVReg(src_reg + 1)) << 32) | + static_cast(shadow_frame.GetVReg(src_reg)); + new_shadow_frame->SetVRegLong(dest_reg, wide_value); + ++dest_reg; + ++arg_offset; + break; + } + default: + new_shadow_frame->SetVReg(dest_reg, shadow_frame.GetVReg(src_reg)); + break; } - case 'J': case 'D': { - uint64_t wide_value = (static_cast(shadow_frame.GetVReg(arg_pos + 1)) << 32) | - static_cast(shadow_frame.GetVReg(arg_pos)); - new_shadow_frame->SetVRegLong(cur_reg, wide_value); - ++cur_reg; - ++arg_offset; - break; + } + } else { + // Fast path: no extra checks. + if (is_range) { + const uint16_t first_src_reg = inst->VRegC_3rc(); + for (size_t src_reg = first_src_reg, dest_reg = first_dest_reg; dest_reg < num_regs; + ++dest_reg, ++src_reg) { + AssignRegister(*new_shadow_frame, shadow_frame, dest_reg, src_reg); + } + } else { + DCHECK_LE(num_ins, 5U); + uint16_t regList = inst->Fetch16(2); + uint16_t count = num_ins; + if (count == 5) { + AssignRegister(*new_shadow_frame, shadow_frame, first_dest_reg + 4U, (inst_data >> 8) & 0x0f); + --count; + } + for (size_t arg_index = 0; arg_index < count; ++arg_index, regList >>= 4) { + AssignRegister(*new_shadow_frame, shadow_frame, first_dest_reg + arg_index, regList & 0x0f); } - default: - new_shadow_frame->SetVReg(cur_reg, shadow_frame.GetVReg(arg_pos)); - break; } } @@ -113,7 +149,7 @@ bool DoCall(ArtMethod* method, Object* receiver, Thread* self, ShadowFrame& shad if (LIKELY(Runtime::Current()->IsStarted())) { (method->GetEntryPointFromInterpreter())(self, mh, code_item, new_shadow_frame, result); } else { - UnstartedRuntimeInvoke(self, mh, code_item, new_shadow_frame, result, num_regs - num_ins); + UnstartedRuntimeInvoke(self, mh, code_item, new_shadow_frame, result, first_dest_reg); } return !self->IsExceptionPending(); } From c67148594b1580c278ae71e3ce5c6fd59bfa6bd3 Mon Sep 17 00:00:00 2001 From: Sebastien Hertz Date: Mon, 30 Sep 2013 16:42:32 +0200 Subject: [PATCH 0063/2402] Enable thread analysis on template functions. All template functions using thread analysis must be explicitly instantiated with the thread analysis attributes to enable thread analysis. We use macros to do this since there are many variants of a same function depending the number of template arguments. Also add documentation of these functions. Change-Id: I3c79acc2f0a6a8dfb5c42924439145292dd68812 --- runtime/interpreter/interpreter_common.cc | 14 +- runtime/interpreter/interpreter_common.h | 208 +++++++++++++----- .../interpreter_goto_table_impl.cc | 14 +- .../interpreter/interpreter_switch_impl.cc | 16 +- 4 files changed, 175 insertions(+), 77 deletions(-) diff --git a/runtime/interpreter/interpreter_common.cc b/runtime/interpreter/interpreter_common.cc index 6f87a8f35ba..f0f10bb0f5f 100644 --- a/runtime/interpreter/interpreter_common.cc +++ b/runtime/interpreter/interpreter_common.cc @@ -316,11 +316,12 @@ static void UnstartedRuntimeInvoke(Thread* self, MethodHelper& mh, } // Explicit DoCall template function declarations. -#define EXPLICIT_DO_CALL_TEMPLATE_DECL(_is_range, _do_assignability_check) \ -template bool DoCall<_is_range, _do_assignability_check>(ArtMethod* method, Object* receiver, \ - Thread* self, ShadowFrame& shadow_frame, \ - const Instruction* inst, \ - uint16_t inst_data, JValue* result) +#define EXPLICIT_DO_CALL_TEMPLATE_DECL(_is_range, _do_assignability_check) \ + template SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) \ + bool DoCall<_is_range, _do_assignability_check>(ArtMethod* method, Object* receiver, \ + Thread* self, ShadowFrame& shadow_frame, \ + const Instruction* inst, uint16_t inst_data, \ + JValue* result) EXPLICIT_DO_CALL_TEMPLATE_DECL(false, false); EXPLICIT_DO_CALL_TEMPLATE_DECL(false, true); EXPLICIT_DO_CALL_TEMPLATE_DECL(true, false); @@ -329,7 +330,8 @@ EXPLICIT_DO_CALL_TEMPLATE_DECL(true, true); // Explicit DoFilledNewArray template function declarations. #define EXPLICIT_DO_FILLED_NEW_ARRAY_TEMPLATE_DECL(_is_range_, _check) \ - template bool DoFilledNewArray<_is_range_, _check>(const Instruction* inst, \ + template SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) \ + bool DoFilledNewArray<_is_range_, _check>(const Instruction* inst, \ const ShadowFrame& shadow_frame, \ Thread* self, JValue* result) EXPLICIT_DO_FILLED_NEW_ARRAY_TEMPLATE_DECL(false, false); diff --git a/runtime/interpreter/interpreter_common.h b/runtime/interpreter/interpreter_common.h index 80502b44f63..481d6cc00cb 100644 --- a/runtime/interpreter/interpreter_common.h +++ b/runtime/interpreter/interpreter_common.h @@ -65,21 +65,15 @@ namespace interpreter { // External references to both interpreter implementations. -// TODO: should be SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) which is failing due to template -// specialization. template extern JValue ExecuteSwitchImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* code_item, - ShadowFrame& shadow_frame, JValue result_register) - NO_THREAD_SAFETY_ANALYSIS HOT_ATTR; + ShadowFrame& shadow_frame, JValue result_register); -// TODO: should be SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) which is failing due to template -// specialization. template extern JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* code_item, - ShadowFrame& shadow_frame, JValue result_register) - NO_THREAD_SAFETY_ANALYSIS HOT_ATTR; + ShadowFrame& shadow_frame, JValue result_register); static inline void DoMonitorEnter(Thread* self, Object* ref) NO_THREAD_SAFETY_ANALYSIS { ref->MonitorEnter(self); @@ -89,18 +83,16 @@ static inline void DoMonitorExit(Thread* self, Object* ref) NO_THREAD_SAFETY_ANA ref->MonitorExit(self); } -// TODO: should be SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) which is failing due to template -// specialization. +// Invokes the given method. This is part of the invocation support and is used by DoInvoke and +// DoInvokeVirtualQuick functions. +// Returns true on success, otherwise throws an exception and returns false. template bool DoCall(ArtMethod* method, Object* receiver, Thread* self, ShadowFrame& shadow_frame, - const Instruction* inst, uint16_t inst_data, JValue* result) NO_THREAD_SAFETY_ANALYSIS; + const Instruction* inst, uint16_t inst_data, JValue* result); -// TODO: should be SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) which is failing due to template -// specialization. -template -static bool DoInvoke(Thread* self, ShadowFrame& shadow_frame, const Instruction* inst, - uint16_t inst_data, JValue* result) NO_THREAD_SAFETY_ANALYSIS ALWAYS_INLINE; +// Handles invoke-XXX/range instructions. +// Returns true on success, otherwise throws an exception and returns false. template static inline bool DoInvoke(Thread* self, ShadowFrame& shadow_frame, const Instruction* inst, uint16_t inst_data, JValue* result) { @@ -123,13 +115,8 @@ static inline bool DoInvoke(Thread* self, ShadowFrame& shadow_frame, const Instr } } -// TODO: should be SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) which is failing due to template -// specialization. -template -static bool DoInvokeVirtualQuick(Thread* self, ShadowFrame& shadow_frame, const Instruction* inst, - uint16_t inst_data, JValue* result) - NO_THREAD_SAFETY_ANALYSIS ALWAYS_INLINE; - +// Handles invoke-virtual-quick and invoke-virtual-quick-range instructions. +// Returns true on success, otherwise throws an exception and returns false. template static inline bool DoInvokeVirtualQuick(Thread* self, ShadowFrame& shadow_frame, const Instruction* inst, uint16_t inst_data, @@ -158,19 +145,8 @@ static inline bool DoInvokeVirtualQuick(Thread* self, ShadowFrame& shadow_frame, } } -// We use template functions to optimize compiler inlining process. Otherwise, -// some parts of the code (like a switch statement) which depend on a constant -// parameter would not be inlined while it should be. These constant parameters -// are now part of the template arguments. -// Note these template functions are static and inlined so they should not be -// part of the final object file. -// TODO: should be SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) which is failing due to template -// specialization. -template -static bool DoFieldGet(Thread* self, ShadowFrame& shadow_frame, - const Instruction* inst, uint16_t inst_data) - NO_THREAD_SAFETY_ANALYSIS ALWAYS_INLINE; - +// Handles iget-XXX and sget-XXX instructions. +// Returns true on success, otherwise throws an exception and returns false. template static inline bool DoFieldGet(Thread* self, ShadowFrame& shadow_frame, const Instruction* inst, uint16_t inst_data) { @@ -222,12 +198,8 @@ static inline bool DoFieldGet(Thread* self, ShadowFrame& shadow_frame, return true; } -// TODO: should be SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) which is failing due to template -// specialization. -template -static bool DoIGetQuick(ShadowFrame& shadow_frame, const Instruction* inst, uint16_t inst_data) - NO_THREAD_SAFETY_ANALYSIS ALWAYS_INLINE; - +// Handles iget-quick, iget-wide-quick and iget-object-quick instructions. +// Returns true on success, otherwise throws an exception and returns false. template static inline bool DoIGetQuick(ShadowFrame& shadow_frame, const Instruction* inst, uint16_t inst_data) { Object* obj = shadow_frame.GetVRegReference(inst->VRegB_22c(inst_data)); @@ -256,13 +228,8 @@ static inline bool DoIGetQuick(ShadowFrame& shadow_frame, const Instruction* ins return true; } -// TODO: should be SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) which is failing due to template -// specialization. -template -static bool DoFieldPut(Thread* self, const ShadowFrame& shadow_frame, - const Instruction* inst, uint16_t inst_data) - NO_THREAD_SAFETY_ANALYSIS ALWAYS_INLINE; - +// Handles iput-XXX and sput-XXX instructions. +// Returns true on success, otherwise throws an exception and returns false. template static inline bool DoFieldPut(Thread* self, const ShadowFrame& shadow_frame, const Instruction* inst, uint16_t inst_data) { @@ -331,12 +298,8 @@ static inline bool DoFieldPut(Thread* self, const ShadowFrame& shadow_frame, return true; } -// TODO: should be SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) which is failing due to template -// specialization. -template -static bool DoIPutQuick(const ShadowFrame& shadow_frame, const Instruction* inst, uint16_t inst_data) - NO_THREAD_SAFETY_ANALYSIS ALWAYS_INLINE; - +// Handles iput-quick, iput-wide-quick and iput-object-quick instructions. +// Returns true on success, otherwise throws an exception and returns false. template static inline bool DoIPutQuick(const ShadowFrame& shadow_frame, const Instruction* inst, uint16_t inst_data) { Object* obj = shadow_frame.GetVRegReference(inst->VRegB_22c(inst_data)); @@ -365,6 +328,8 @@ static inline bool DoIPutQuick(const ShadowFrame& shadow_frame, const Instructio return true; } +// Handles string resolution for const-string and const-string-jumbo instructions. Also ensures the +// java.lang.String class is initialized. static inline String* ResolveString(Thread* self, MethodHelper& mh, uint32_t string_idx) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { Class* java_lang_string_class = String::GetJavaLangString(); @@ -379,6 +344,8 @@ static inline String* ResolveString(Thread* self, MethodHelper& mh, uint32_t str return mh.ResolveString(string_idx); } +// Handles div-int, div-int/2addr, div-int/li16 and div-int/lit8 instructions. +// Returns true on success, otherwise throws a java.lang.ArithmeticException and return false. static inline bool DoIntDivide(ShadowFrame& shadow_frame, size_t result_reg, int32_t dividend, int32_t divisor) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { @@ -395,6 +362,8 @@ static inline bool DoIntDivide(ShadowFrame& shadow_frame, size_t result_reg, return true; } +// Handles rem-int, rem-int/2addr, rem-int/li16 and rem-int/lit8 instructions. +// Returns true on success, otherwise throws a java.lang.ArithmeticException and return false. static inline bool DoIntRemainder(ShadowFrame& shadow_frame, size_t result_reg, int32_t dividend, int32_t divisor) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { @@ -411,6 +380,8 @@ static inline bool DoIntRemainder(ShadowFrame& shadow_frame, size_t result_reg, return true; } +// Handles div-long and div-long-2addr instructions. +// Returns true on success, otherwise throws a java.lang.ArithmeticException and return false. static inline bool DoLongDivide(ShadowFrame& shadow_frame, size_t result_reg, int64_t dividend, int64_t divisor) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { @@ -427,6 +398,8 @@ static inline bool DoLongDivide(ShadowFrame& shadow_frame, size_t result_reg, return true; } +// Handles rem-long and rem-long-2addr instructions. +// Returns true on success, otherwise throws a java.lang.ArithmeticException and return false. static inline bool DoLongRemainder(ShadowFrame& shadow_frame, size_t result_reg, int64_t dividend, int64_t divisor) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { @@ -443,13 +416,14 @@ static inline bool DoLongRemainder(ShadowFrame& shadow_frame, size_t result_reg, return true; } -// TODO: should be SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) which is failing due to template -// specialization. +// Handles filled-new-array and filled-new-array-range instructions. // Returns true on success, otherwise throws an exception and returns false. template bool DoFilledNewArray(const Instruction* inst, const ShadowFrame& shadow_frame, - Thread* self, JValue* result) NO_THREAD_SAFETY_ANALYSIS; + Thread* self, JValue* result); +// Handles packed-switch instruction. +// Returns the branch offset to the next instruction to execute. static inline int32_t DoPackedSwitch(const Instruction* inst, const ShadowFrame& shadow_frame, uint16_t inst_data) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { @@ -473,6 +447,8 @@ static inline int32_t DoPackedSwitch(const Instruction* inst, const ShadowFrame& } } +// Handles sparse-switch instruction. +// Returns the branch offset to the next instruction to execute. static inline int32_t DoSparseSwitch(const Instruction* inst, const ShadowFrame& shadow_frame, uint16_t inst_data) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { @@ -576,6 +552,122 @@ static inline bool IsBackwardBranch(int32_t branch_offset) { return branch_offset <= 0; } +// Explicitly instantiate all DoInvoke functions. +#define EXPLICIT_DO_INVOKE_TEMPLATE_DECL(_type, _is_range, _do_check) \ + template SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) ALWAYS_INLINE \ + static bool DoInvoke<_type, _is_range, _do_check>(Thread* self, ShadowFrame& shadow_frame, \ + const Instruction* inst, uint16_t inst_data, \ + JValue* result) + +#define EXPLICIT_DO_INVOKE_ALL_TEMPLATE_DECL(_type) \ + EXPLICIT_DO_INVOKE_TEMPLATE_DECL(_type, false, false); \ + EXPLICIT_DO_INVOKE_TEMPLATE_DECL(_type, false, true); \ + EXPLICIT_DO_INVOKE_TEMPLATE_DECL(_type, true, false); \ + EXPLICIT_DO_INVOKE_TEMPLATE_DECL(_type, true, true); + +EXPLICIT_DO_INVOKE_ALL_TEMPLATE_DECL(kStatic); // invoke-static/range. +EXPLICIT_DO_INVOKE_ALL_TEMPLATE_DECL(kDirect); // invoke-direct/range. +EXPLICIT_DO_INVOKE_ALL_TEMPLATE_DECL(kVirtual); // invoke-virtual/range. +EXPLICIT_DO_INVOKE_ALL_TEMPLATE_DECL(kSuper); // invoke-super/range. +EXPLICIT_DO_INVOKE_ALL_TEMPLATE_DECL(kInterface); // invoke-interface/range. +#undef EXPLICIT_DO_INVOKE_ALL_TEMPLATE_DECL +#undef EXPLICIT_DO_INVOKE_TEMPLATE_DECL + +// Explicitly instantiate all DoFieldGet functions. +#define EXPLICIT_DO_FIELD_GET_TEMPLATE_DECL(_find_type, _field_type, _do_check) \ + template SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) ALWAYS_INLINE \ + static bool DoFieldGet<_find_type, _field_type, _do_check>(Thread* self, ShadowFrame& shadow_frame, \ + const Instruction* inst, uint16_t inst_data) + +#define EXPLICIT_DO_FIELD_GET_ALL_TEMPLATE_DECL(_find_type, _field_type) \ + EXPLICIT_DO_FIELD_GET_TEMPLATE_DECL(_find_type, _field_type, false); \ + EXPLICIT_DO_FIELD_GET_TEMPLATE_DECL(_find_type, _field_type, true); + +// iget-XXX +EXPLICIT_DO_FIELD_GET_ALL_TEMPLATE_DECL(InstancePrimitiveRead, Primitive::kPrimBoolean); +EXPLICIT_DO_FIELD_GET_ALL_TEMPLATE_DECL(InstancePrimitiveRead, Primitive::kPrimByte); +EXPLICIT_DO_FIELD_GET_ALL_TEMPLATE_DECL(InstancePrimitiveRead, Primitive::kPrimChar); +EXPLICIT_DO_FIELD_GET_ALL_TEMPLATE_DECL(InstancePrimitiveRead, Primitive::kPrimShort); +EXPLICIT_DO_FIELD_GET_ALL_TEMPLATE_DECL(InstancePrimitiveRead, Primitive::kPrimInt); +EXPLICIT_DO_FIELD_GET_ALL_TEMPLATE_DECL(InstancePrimitiveRead, Primitive::kPrimLong); +EXPLICIT_DO_FIELD_GET_ALL_TEMPLATE_DECL(InstanceObjectRead, Primitive::kPrimNot); + +// sget-XXX +EXPLICIT_DO_FIELD_GET_ALL_TEMPLATE_DECL(StaticPrimitiveRead, Primitive::kPrimBoolean); +EXPLICIT_DO_FIELD_GET_ALL_TEMPLATE_DECL(StaticPrimitiveRead, Primitive::kPrimByte); +EXPLICIT_DO_FIELD_GET_ALL_TEMPLATE_DECL(StaticPrimitiveRead, Primitive::kPrimChar); +EXPLICIT_DO_FIELD_GET_ALL_TEMPLATE_DECL(StaticPrimitiveRead, Primitive::kPrimShort); +EXPLICIT_DO_FIELD_GET_ALL_TEMPLATE_DECL(StaticPrimitiveRead, Primitive::kPrimInt); +EXPLICIT_DO_FIELD_GET_ALL_TEMPLATE_DECL(StaticPrimitiveRead, Primitive::kPrimLong); +EXPLICIT_DO_FIELD_GET_ALL_TEMPLATE_DECL(StaticObjectRead, Primitive::kPrimNot); + +#undef EXPLICIT_DO_FIELD_GET_ALL_TEMPLATE_DECL +#undef EXPLICIT_DO_FIELD_GET_TEMPLATE_DECL + +// Explicitly instantiate all DoFieldPut functions. +#define EXPLICIT_DO_FIELD_PUT_TEMPLATE_DECL(_find_type, _field_type, _do_check) \ + template SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) ALWAYS_INLINE \ + static bool DoFieldPut<_find_type, _field_type, _do_check>(Thread* self, const ShadowFrame& shadow_frame, \ + const Instruction* inst, uint16_t inst_data) + +#define EXPLICIT_DO_FIELD_PUT_ALL_TEMPLATE_DECL(_find_type, _field_type) \ + EXPLICIT_DO_FIELD_PUT_TEMPLATE_DECL(_find_type, _field_type, false); \ + EXPLICIT_DO_FIELD_PUT_TEMPLATE_DECL(_find_type, _field_type, true); + +// iput-XXX +EXPLICIT_DO_FIELD_PUT_ALL_TEMPLATE_DECL(InstancePrimitiveWrite, Primitive::kPrimBoolean); +EXPLICIT_DO_FIELD_PUT_ALL_TEMPLATE_DECL(InstancePrimitiveWrite, Primitive::kPrimByte); +EXPLICIT_DO_FIELD_PUT_ALL_TEMPLATE_DECL(InstancePrimitiveWrite, Primitive::kPrimChar); +EXPLICIT_DO_FIELD_PUT_ALL_TEMPLATE_DECL(InstancePrimitiveWrite, Primitive::kPrimShort); +EXPLICIT_DO_FIELD_PUT_ALL_TEMPLATE_DECL(InstancePrimitiveWrite, Primitive::kPrimInt); +EXPLICIT_DO_FIELD_PUT_ALL_TEMPLATE_DECL(InstancePrimitiveWrite, Primitive::kPrimLong); +EXPLICIT_DO_FIELD_PUT_ALL_TEMPLATE_DECL(InstanceObjectWrite, Primitive::kPrimNot); + +// sput-XXX +EXPLICIT_DO_FIELD_PUT_ALL_TEMPLATE_DECL(StaticPrimitiveWrite, Primitive::kPrimBoolean); +EXPLICIT_DO_FIELD_PUT_ALL_TEMPLATE_DECL(StaticPrimitiveWrite, Primitive::kPrimByte); +EXPLICIT_DO_FIELD_PUT_ALL_TEMPLATE_DECL(StaticPrimitiveWrite, Primitive::kPrimChar); +EXPLICIT_DO_FIELD_PUT_ALL_TEMPLATE_DECL(StaticPrimitiveWrite, Primitive::kPrimShort); +EXPLICIT_DO_FIELD_PUT_ALL_TEMPLATE_DECL(StaticPrimitiveWrite, Primitive::kPrimInt); +EXPLICIT_DO_FIELD_PUT_ALL_TEMPLATE_DECL(StaticPrimitiveWrite, Primitive::kPrimLong); +EXPLICIT_DO_FIELD_PUT_ALL_TEMPLATE_DECL(StaticObjectWrite, Primitive::kPrimNot); + +#undef EXPLICIT_DO_FIELD_PUT_ALL_TEMPLATE_DECL +#undef EXPLICIT_DO_FIELD_PUT_TEMPLATE_DECL + +// Explicitly instantiate all DoInvokeVirtualQuick functions. +#define EXPLICIT_DO_INVOKE_VIRTUAL_QUICK_TEMPLATE_DECL(_is_range) \ + template SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) ALWAYS_INLINE \ + static bool DoInvokeVirtualQuick<_is_range>(Thread* self, ShadowFrame& shadow_frame, \ + const Instruction* inst, uint16_t inst_data, \ + JValue* result) + +EXPLICIT_DO_INVOKE_VIRTUAL_QUICK_TEMPLATE_DECL(false); // invoke-virtual-quick. +EXPLICIT_DO_INVOKE_VIRTUAL_QUICK_TEMPLATE_DECL(true); // invoke-virtual-quick-range. +#undef EXPLICIT_INSTANTIATION_DO_INVOKE_VIRTUAL_QUICK + +// Explicitly instantiate all DoIGetQuick functions. +#define EXPLICIT_DO_IGET_QUICK_TEMPLATE_DECL(_field_type) \ + template SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) ALWAYS_INLINE \ + static bool DoIGetQuick<_field_type>(ShadowFrame& shadow_frame, const Instruction* inst, \ + uint16_t inst_data) + +EXPLICIT_DO_IGET_QUICK_TEMPLATE_DECL(Primitive::kPrimInt); // iget-quick. +EXPLICIT_DO_IGET_QUICK_TEMPLATE_DECL(Primitive::kPrimLong); // iget-wide-quick. +EXPLICIT_DO_IGET_QUICK_TEMPLATE_DECL(Primitive::kPrimNot); // iget-object-quick. +#undef EXPLICIT_DO_IGET_QUICK_TEMPLATE_DECL + +// Explicitly instantiate all DoIPutQuick functions. +#define EXPLICIT_DO_IPUT_QUICK_TEMPLATE_DECL(_field_type) \ + template SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) ALWAYS_INLINE \ + static bool DoIPutQuick<_field_type>(const ShadowFrame& shadow_frame, const Instruction* inst, \ + uint16_t inst_data) + +EXPLICIT_DO_IPUT_QUICK_TEMPLATE_DECL(Primitive::kPrimInt); // iget-quick. +EXPLICIT_DO_IPUT_QUICK_TEMPLATE_DECL(Primitive::kPrimLong); // iget-wide-quick. +EXPLICIT_DO_IPUT_QUICK_TEMPLATE_DECL(Primitive::kPrimNot); // iget-object-quick. +#undef EXPLICIT_DO_IPUT_QUICK_TEMPLATE_DECL + } // namespace interpreter } // namespace art diff --git a/runtime/interpreter/interpreter_goto_table_impl.cc b/runtime/interpreter/interpreter_goto_table_impl.cc index 5a008319f8b..869729123dc 100644 --- a/runtime/interpreter/interpreter_goto_table_impl.cc +++ b/runtime/interpreter/interpreter_goto_table_impl.cc @@ -2333,12 +2333,14 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* } // NOLINT(readability/fn_size) // Explicit definitions of ExecuteGotoImpl. -template JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, - const DexFile::CodeItem* code_item, - ShadowFrame& shadow_frame, JValue result_register); -template JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, - const DexFile::CodeItem* code_item, - ShadowFrame& shadow_frame, JValue result_register); +template SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) HOT_ATTR +JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, + const DexFile::CodeItem* code_item, + ShadowFrame& shadow_frame, JValue result_register); +template SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) HOT_ATTR +JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, + const DexFile::CodeItem* code_item, + ShadowFrame& shadow_frame, JValue result_register); } // namespace interpreter } // namespace art diff --git a/runtime/interpreter/interpreter_switch_impl.cc b/runtime/interpreter/interpreter_switch_impl.cc index 82f216aa41c..dcc86e81aa8 100644 --- a/runtime/interpreter/interpreter_switch_impl.cc +++ b/runtime/interpreter/interpreter_switch_impl.cc @@ -51,7 +51,7 @@ namespace interpreter { #define PREAMBLE() template -static JValue ExecuteSwitchImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* code_item, +JValue ExecuteSwitchImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* code_item, ShadowFrame& shadow_frame, JValue result_register) { bool do_assignability_check = do_access_check; if (UNLIKELY(!shadow_frame.HasReferenceArray())) { @@ -2134,12 +2134,14 @@ static JValue ExecuteSwitchImpl(Thread* self, MethodHelper& mh, const DexFile::C } // NOLINT(readability/fn_size) // Explicit definitions of ExecuteSwitchImpl. -template JValue ExecuteSwitchImpl(Thread* self, MethodHelper& mh, - const DexFile::CodeItem* code_item, - ShadowFrame& shadow_frame, JValue result_register); -template JValue ExecuteSwitchImpl(Thread* self, MethodHelper& mh, - const DexFile::CodeItem* code_item, - ShadowFrame& shadow_frame, JValue result_register); +template SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) HOT_ATTR +JValue ExecuteSwitchImpl(Thread* self, MethodHelper& mh, + const DexFile::CodeItem* code_item, + ShadowFrame& shadow_frame, JValue result_register); +template SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) HOT_ATTR +JValue ExecuteSwitchImpl(Thread* self, MethodHelper& mh, + const DexFile::CodeItem* code_item, + ShadowFrame& shadow_frame, JValue result_register); } // namespace interpreter } // namespace art From 1c82982f4eab8f0e84829fec25e3f899da44e7ce Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Mon, 30 Sep 2013 18:18:50 -0700 Subject: [PATCH 0064/2402] Early exit for static fixup if no direct methods. Change-Id: I401746e48259a98fb0c80144ff5310380889b154 --- runtime/class_linker.cc | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index 17a179f2d91..17f86259c26 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -1620,21 +1620,22 @@ static bool NeedsInterpreter(const mirror::ArtMethod* method, const void* code) } void ClassLinker::FixupStaticTrampolines(mirror::Class* klass) { - ClassHelper kh(klass); - const DexFile::ClassDef* dex_class_def = kh.GetClassDef(); - CHECK(dex_class_def != NULL); - const DexFile& dex_file = kh.GetDexFile(); - const byte* class_data = dex_file.GetClassData(*dex_class_def); - if (class_data == NULL) { - return; // no fields or methods - for example a marker interface + if (klass->NumDirectMethods() == 0) { + return; // No direct methods => no static methods. } Runtime* runtime = Runtime::Current(); if (!runtime->IsStarted() || runtime->UseCompileTimeClassPath()) { - // OAT file unavailable - return; + return; // OAT file unavailable. } + ClassHelper kh(klass); + const DexFile& dex_file = kh.GetDexFile(); + const DexFile::ClassDef* dex_class_def = kh.GetClassDef(); + CHECK(dex_class_def != nullptr); + const byte* class_data = dex_file.GetClassData(*dex_class_def); + // There should always be class data if there were direct methods. + CHECK(class_data != nullptr) << PrettyDescriptor(klass); UniquePtr oat_class(GetOatClass(dex_file, klass->GetDexClassDefIndex())); - CHECK(oat_class.get() != NULL); + CHECK(oat_class.get() != nullptr); ClassDataItemIterator it(dex_file, class_data); // Skip fields while (it.HasNextStaticField()) { @@ -1643,7 +1644,7 @@ void ClassLinker::FixupStaticTrampolines(mirror::Class* klass) { while (it.HasNextInstanceField()) { it.Next(); } - // Link the code of methods skipped by LinkCode + // Link the code of methods skipped by LinkCode. for (size_t method_index = 0; it.HasNextDirectMethod(); ++method_index, it.Next()) { mirror::ArtMethod* method = klass->GetDirectMethod(method_index); if (!method->IsStatic()) { From b48819db07f9a0992a72173380c24249d7fc648a Mon Sep 17 00:00:00 2001 From: buzbee Date: Sat, 14 Sep 2013 16:15:25 -0700 Subject: [PATCH 0065/2402] Compile-time tuning: assembly phase Not as much compile-time gain from reworking the assembly phase as I'd hoped, but still worthwhile. Should see ~2% improvement thanks to the assembly rework. On the other hand, expect some huge gains for some application thanks to better detection of large machine-generated init methods. Thinkfree shows a 25% improvement. The major assembly change was to establish thread the LIR nodes that require fixup into a fixup chain. Only those are processed during the final assembly pass(es). This doesn't help for methods which only require a single pass to assemble, but does speed up the larger methods which required multiple assembly passes. Also replaced the block_map_ basic block lookup table (which contained space for a BasicBlock* for each dex instruction unit) with a block id map - cutting its space requirements by half in a 32-bit pointer environment. Changes: o Reduce size of LIR struct by 12.5% (one of the big memory users) o Repurpose the use/def portion of the LIR after optimization complete. o Encode instruction bits to LIR o Thread LIR nodes requiring pc fixup o Change follow-on assembly passes to only consider fixup LIRs o Switch on pc-rel fixup kind o Fast-path for small methods - single pass assembly o Avoid using cb[n]z for null checks (almost always exceed displacement) o Improve detection of large initialization methods. o Rework def/use flag setup. o Remove a sequential search from FindBlock using lookup table of 16-bit block ids rather than full block pointers. o Eliminate pcRelFixup and use fixup kind instead. o Add check for 16-bit overflow on dex offset. Change-Id: I4c6615f83fed46f84629ad6cfe4237205a9562b4 --- compiler/dex/compiler_enums.h | 21 + compiler/dex/frontend.cc | 7 +- compiler/dex/mir_analysis.cc | 8 + compiler/dex/mir_graph.cc | 78 +- compiler/dex/mir_graph.h | 27 +- compiler/dex/quick/arm/arm_lir.h | 4 +- compiler/dex/quick/arm/assemble_arm.cc | 1404 ++++++++++++--------- compiler/dex/quick/arm/codegen_arm.h | 10 +- compiler/dex/quick/arm/int_arm.cc | 15 +- compiler/dex/quick/arm/target_arm.cc | 117 +- compiler/dex/quick/arm/utility_arm.cc | 2 - compiler/dex/quick/codegen_util.cc | 138 +- compiler/dex/quick/gen_common.cc | 5 +- compiler/dex/quick/gen_invoke.cc | 4 +- compiler/dex/quick/local_optimizations.cc | 38 +- compiler/dex/quick/mips/assemble_mips.cc | 95 +- compiler/dex/quick/mips/codegen_mips.h | 5 +- compiler/dex/quick/mips/target_mips.cc | 15 +- compiler/dex/quick/mir_to_lir-inl.h | 45 +- compiler/dex/quick/mir_to_lir.cc | 4 +- compiler/dex/quick/mir_to_lir.h | 45 +- compiler/dex/quick/x86/assemble_x86.cc | 95 +- compiler/dex/quick/x86/codegen_x86.h | 5 +- compiler/dex/quick/x86/target_x86.cc | 23 +- 24 files changed, 1352 insertions(+), 858 deletions(-) diff --git a/compiler/dex/compiler_enums.h b/compiler/dex/compiler_enums.h index 97a682f2aa9..17b5bb5b197 100644 --- a/compiler/dex/compiler_enums.h +++ b/compiler/dex/compiler_enums.h @@ -412,6 +412,27 @@ enum OatBitMapKind { std::ostream& operator<<(std::ostream& os, const OatBitMapKind& kind); +// LIR fixup kinds for Arm +enum FixupKind { + kFixupNone, + kFixupLabel, // For labels we just adjust the offset. + kFixupLoad, // Mostly for imediates. + kFixupVLoad, // FP load which *may* be pc-relative. + kFixupCBxZ, // Cbz, Cbnz. + kFixupPushPop, // Not really pc relative, but changes size based on args. + kFixupCondBranch, // Conditional branch + kFixupT1Branch, // Thumb1 Unconditional branch + kFixupT2Branch, // Thumb2 Unconditional branch + kFixupBlx1, // Blx1 (start of Blx1/Blx2 pair). + kFixupBl1, // Bl1 (start of Bl1/Bl2 pair). + kFixupAdr, // Adr. + kFixupMovImmLST, // kThumb2MovImm16LST. + kFixupMovImmHST, // kThumb2MovImm16HST. + kFixupAlign4, // Align to 4-byte boundary. +}; + +std::ostream& operator<<(std::ostream& os, const FixupKind& kind); + } // namespace art #endif // ART_COMPILER_DEX_COMPILER_ENUMS_H_ diff --git a/compiler/dex/frontend.cc b/compiler/dex/frontend.cc index fefcab9e87c..2952570436a 100644 --- a/compiler/dex/frontend.cc +++ b/compiler/dex/frontend.cc @@ -117,6 +117,11 @@ static CompiledMethod* CompileMethod(CompilerDriver& compiler, #endif ) { VLOG(compiler) << "Compiling " << PrettyMethod(method_idx, dex_file) << "..."; + if (code_item->insns_size_in_code_units_ >= 0x10000) { + LOG(INFO) << "Method size exceeds compiler limits: " << code_item->insns_size_in_code_units_ + << " in " << PrettyMethod(method_idx, dex_file); + return NULL; + } ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); CompilationUnit cu(&compiler.GetArenaPool()); @@ -151,7 +156,7 @@ static CompiledMethod* CompileMethod(CompilerDriver& compiler, */ if (compiler_backend == kPortable) { - // Fused long branches not currently usseful in bitcode. + // Fused long branches not currently useful in bitcode. cu.disable_opt |= (1 << kBranchFusing); } diff --git a/compiler/dex/mir_analysis.cc b/compiler/dex/mir_analysis.cc index 8472a3c011b..8597172881d 100644 --- a/compiler/dex/mir_analysis.cc +++ b/compiler/dex/mir_analysis.cc @@ -1032,6 +1032,14 @@ bool MIRGraph::SkipCompilation(Runtime::CompilerFilter compiler_filter) { */ if (GetNumDalvikInsns() > Runtime::Current()->GetHugeMethodThreshold()) { skip_compilation = true; + // If we're got a huge number of basic blocks, don't bother with further analysis. + if (static_cast(num_blocks_) > (Runtime::Current()->GetHugeMethodThreshold() / 2)) { + return true; + } + } else if (GetNumDalvikInsns() > Runtime::Current()->GetLargeMethodThreshold() && + /* If it's large and contains no branches, it's likely to be machine generated initialization */ + (GetBranchCount() == 0)) { + return true; } else if (compiler_filter == Runtime::kSpeed) { // If not huge, compile. return false; diff --git a/compiler/dex/mir_graph.cc b/compiler/dex/mir_graph.cc index c234298a88e..fb306de0c13 100644 --- a/compiler/dex/mir_graph.cc +++ b/compiler/dex/mir_graph.cc @@ -96,10 +96,9 @@ MIRGraph::MIRGraph(CompilationUnit* cu, ArenaAllocator* arena) try_block_addr_(NULL), entry_block_(NULL), exit_block_(NULL), - cur_block_(NULL), num_blocks_(0), current_code_item_(NULL), - block_map_(arena, 0, kGrowableArrayMisc), + dex_pc_to_block_map_(arena, 0, kGrowableArrayMisc), current_method_(kInvalidEntry), current_offset_(kInvalidEntry), def_count_(0), @@ -109,7 +108,9 @@ MIRGraph::MIRGraph(CompilationUnit* cu, ArenaAllocator* arena) attributes_(METHOD_IS_LEAF), // Start with leaf assumption, change on encountering invoke. checkstats_(NULL), special_case_(kNoHandler), - arena_(arena) { + arena_(arena), + backward_branches_(0), + forward_branches_(0) { try_block_addr_ = new (arena_) ArenaBitVector(arena_, 0, true /* expandable */); } @@ -151,7 +152,7 @@ BasicBlock* MIRGraph::SplitBlock(unsigned int code_offset, orig_block->terminated_by_return = false; /* Add it to the quick lookup cache */ - block_map_.Put(bottom_block->start_offset, bottom_block); + dex_pc_to_block_map_.Put(bottom_block->start_offset, bottom_block->id); /* Handle the taken path */ bottom_block->taken = orig_block->taken; @@ -196,6 +197,23 @@ BasicBlock* MIRGraph::SplitBlock(unsigned int code_offset, DCHECK_EQ(*immed_pred_block_p, orig_block); *immed_pred_block_p = bottom_block; } + + // Associate dex instructions in the bottom block with the new container. + MIR* p = bottom_block->first_mir_insn; + while (p != NULL) { + int opcode = p->dalvikInsn.opcode; + /* + * Some messiness here to ensure that we only enter real opcodes and only the + * first half of a potentially throwing instruction that has been split into + * CHECK and work portions. The 2nd half of a split operation will have a non-null + * throw_insn pointer that refers to the 1st half. + */ + if ((opcode == kMirOpCheck) || (!IsPseudoMirOp(opcode) && (p->meta.throw_insn == NULL))) { + dex_pc_to_block_map_.Put(p->offset, bottom_block->id); + } + p = (p == bottom_block->last_mir_insn) ? NULL : p->next; + } + return bottom_block; } @@ -209,39 +227,37 @@ BasicBlock* MIRGraph::SplitBlock(unsigned int code_offset, */ BasicBlock* MIRGraph::FindBlock(unsigned int code_offset, bool split, bool create, BasicBlock** immed_pred_block_p) { - BasicBlock* bb; - unsigned int i; - if (code_offset >= cu_->code_item->insns_size_in_code_units_) { return NULL; } - bb = block_map_.Get(code_offset); - if ((bb != NULL) || !create) { + + int block_id = dex_pc_to_block_map_.Get(code_offset); + BasicBlock* bb = (block_id == 0) ? NULL : block_list_.Get(block_id); + + if ((bb != NULL) && (bb->start_offset == code_offset)) { + // Does this containing block start with the desired instruction? return bb; } - if (split) { - for (i = block_list_.Size(); i > 0; i--) { - bb = block_list_.Get(i - 1); - if (bb->block_type != kDalvikByteCode) continue; - /* Check if a branch jumps into the middle of an existing block */ - if ((code_offset > bb->start_offset) && (bb->last_mir_insn != NULL) && - (code_offset <= bb->last_mir_insn->offset)) { - BasicBlock *new_bb = SplitBlock(code_offset, bb, bb == *immed_pred_block_p ? - immed_pred_block_p : NULL); - return new_bb; - } - } + // No direct hit. + if (!create) { + return NULL; + } + + if (bb != NULL) { + // The target exists somewhere in an existing block. + return SplitBlock(code_offset, bb, bb == *immed_pred_block_p ? immed_pred_block_p : NULL); } - /* Create a new one */ + // Create a new block. bb = NewMemBB(kDalvikByteCode, num_blocks_++); block_list_.Insert(bb); bb->start_offset = code_offset; - block_map_.Put(bb->start_offset, bb); + dex_pc_to_block_map_.Put(bb->start_offset, bb->id); return bb; } + /* Identify code range in try blocks and set up the empty catch blocks */ void MIRGraph::ProcessTryCatchBlocks() { int tries_size = current_code_item_->tries_size_; @@ -307,6 +323,7 @@ BasicBlock* MIRGraph::ProcessCanBranch(BasicBlock* cur_block, MIR* insn, int cur default: LOG(FATAL) << "Unexpected opcode(" << insn->dalvikInsn.opcode << ") with kBranch set"; } + CountBranch(target); BasicBlock *taken_block = FindBlock(target, /* split */ true, /* create */ true, /* immed_pred_block_p */ &cur_block); cur_block->taken = taken_block; @@ -485,6 +502,9 @@ BasicBlock* MIRGraph::ProcessCanThrow(BasicBlock* cur_block, MIR* insn, int cur_ * pseudo exception edge MIR. Note also that this new block is * not automatically terminated after the work portion, and may * contain following instructions. + * + * Note also that the dex_pc_to_block_map_ entry for the potentially + * throwing instruction will refer to the original basic block. */ BasicBlock *new_block = NewMemBB(kDalvikByteCode, num_blocks_++); block_list_.Insert(new_block); @@ -518,8 +538,9 @@ void MIRGraph::InlineMethod(const DexFile::CodeItem* code_item, uint32_t access_ current_code_item_->insns_ + current_code_item_->insns_size_in_code_units_; // TODO: need to rework expansion of block list & try_block_addr when inlining activated. + // TUNING: use better estimate of basic blocks for following resize. block_list_.Resize(block_list_.Size() + current_code_item_->insns_size_in_code_units_); - block_map_.SetSize(block_map_.Size() + current_code_item_->insns_size_in_code_units_); + dex_pc_to_block_map_.SetSize(dex_pc_to_block_map_.Size() + current_code_item_->insns_size_in_code_units_); // TODO: replace with explicit resize routine. Using automatic extension side effect for now. try_block_addr_->SetBit(current_code_item_->insns_size_in_code_units_); @@ -560,10 +581,7 @@ void MIRGraph::InlineMethod(const DexFile::CodeItem* code_item, uint32_t access_ DCHECK_EQ(current_offset_, 0); cur_block->start_offset = current_offset_; block_list_.Insert(cur_block); - /* Add first block to the fast lookup cache */ -// FIXME: block map needs association with offset/method pair rather than just offset - block_map_.Put(cur_block->start_offset, cur_block); -// FIXME: this needs to insert at the insert point rather than entry block. + // FIXME: this needs to insert at the insert point rather than entry block. entry_block_->fall_through = cur_block; cur_block->predecessors->Insert(entry_block_); @@ -589,7 +607,6 @@ void MIRGraph::InlineMethod(const DexFile::CodeItem* code_item, uint32_t access_ opcode_count_[static_cast(opcode)]++; } - /* Possible simple method? */ if (live_pattern) { live_pattern = false; @@ -640,6 +657,9 @@ void MIRGraph::InlineMethod(const DexFile::CodeItem* code_item, uint32_t access_ AppendMIR(cur_block, insn); } + // Associate the starting dex_pc for this opcode with its containing basic block. + dex_pc_to_block_map_.Put(insn->offset, cur_block->id); + code_ptr += width; if (flags & Instruction::kBranch) { diff --git a/compiler/dex/mir_graph.h b/compiler/dex/mir_graph.h index 9d4ab98f67c..5d014894c1a 100644 --- a/compiler/dex/mir_graph.h +++ b/compiler/dex/mir_graph.h @@ -569,6 +569,26 @@ class MIRGraph { return IsBackedge(branch_bb, branch_bb->taken) || IsBackedge(branch_bb, branch_bb->fall_through); } + void CountBranch(int target_offset) { + if (target_offset <= current_offset_) { + backward_branches_++; + } else { + forward_branches_++; + } + } + + int GetBranchCount() { + return backward_branches_ + forward_branches_; + } + + bool IsPseudoMirOp(Instruction::Code opcode) { + return static_cast(opcode) >= static_cast(kMirOpFirst); + } + + bool IsPseudoMirOp(int opcode) { + return opcode >= static_cast(kMirOpFirst); + } + void BasicBlockCombine(); void CodeLayout(); void DumpCheckStats(); @@ -725,15 +745,14 @@ class MIRGraph { ArenaBitVector* try_block_addr_; BasicBlock* entry_block_; BasicBlock* exit_block_; - BasicBlock* cur_block_; int num_blocks_; const DexFile::CodeItem* current_code_item_; - GrowableArray block_map_; // FindBlock lookup cache. + GrowableArray dex_pc_to_block_map_; // FindBlock lookup cache. std::vector m_units_; // List of methods included in this graph typedef std::pair MIRLocation; // Insert point, (m_unit_ index, offset) std::vector method_stack_; // Include stack int current_method_; - int current_offset_; + int current_offset_; // Dex offset in code units int def_count_; // Used to estimate size of ssa name storage. int* opcode_count_; // Dex opcode coverage stats. int num_ssa_regs_; // Number of names following SSA transformation. @@ -743,6 +762,8 @@ class MIRGraph { Checkstats* checkstats_; SpecialCaseHandler special_case_; ArenaAllocator* arena_; + int backward_branches_; + int forward_branches_; }; } // namespace art diff --git a/compiler/dex/quick/arm/arm_lir.h b/compiler/dex/quick/arm/arm_lir.h index 2f54190ae7e..d18467304f9 100644 --- a/compiler/dex/quick/arm/arm_lir.h +++ b/compiler/dex/quick/arm/arm_lir.h @@ -462,7 +462,7 @@ enum ArmOpDmbOptions { // Instruction assembly field_loc kind. enum ArmEncodingKind { - kFmtUnused, + kFmtUnused, // Unused field and marks end of formats. kFmtBitBlt, // Bit string using end/start. kFmtDfp, // Double FP reg. kFmtSfp, // Single FP reg. @@ -477,6 +477,7 @@ enum ArmEncodingKind { kFmtBrOffset, // Signed extended [26,11,13,21-16,10-0]:0. kFmtFPImm, // Encoded floating point immediate. kFmtOff24, // 24-bit Thumb2 unconditional branch encoding. + kFmtSkip, // Unused field, but continue to next. }; // Struct used to define the snippet positions for each Thumb opcode. @@ -492,6 +493,7 @@ struct ArmEncodingMap { const char* name; const char* fmt; int size; // Note: size is in bytes. + FixupKind fixup; }; } // namespace art diff --git a/compiler/dex/quick/arm/assemble_arm.cc b/compiler/dex/quick/arm/assemble_arm.cc index 2d69d935ca2..dac3a2159c1 100644 --- a/compiler/dex/quick/arm/assemble_arm.cc +++ b/compiler/dex/quick/arm/assemble_arm.cc @@ -37,9 +37,9 @@ namespace art { * fmt: for pretty-printing */ #define ENCODING_MAP(opcode, skeleton, k0, ds, de, k1, s1s, s1e, k2, s2s, s2e, \ - k3, k3s, k3e, flags, name, fmt, size) \ + k3, k3s, k3e, flags, name, fmt, size, fixup) \ {skeleton, {{k0, ds, de}, {k1, s1s, s1e}, {k2, s2s, s2e}, \ - {k3, k3s, k3e}}, opcode, flags, name, fmt, size} + {k3, k3s, k3e}}, opcode, flags, name, fmt, size, fixup} /* Instruction dump string format keys: !pf, where "!" is the start * of the key, "p" is which numeric operand to use and "f" is the @@ -79,916 +79,938 @@ namespace art { const ArmEncodingMap ArmMir2Lir::EncodingMap[kArmLast] = { ENCODING_MAP(kArm16BitData, 0x0000, kFmtBitBlt, 15, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1, - kFmtUnused, -1, -1, IS_UNARY_OP, "data", "0x!0h(!0d)", 2), + kFmtUnused, -1, -1, IS_UNARY_OP, "data", "0x!0h(!0d)", 2, kFixupNone), ENCODING_MAP(kThumbAdcRR, 0x4140, kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1, kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE01 | SETS_CCODES | USES_CCODES, - "adcs", "!0C, !1C", 2), + "adcs", "!0C, !1C", 2, kFixupNone), ENCODING_MAP(kThumbAddRRI3, 0x1c00, kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtBitBlt, 8, 6, kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1 | SETS_CCODES, - "adds", "!0C, !1C, #!2d", 2), + "adds", "!0C, !1C, #!2d", 2, kFixupNone), ENCODING_MAP(kThumbAddRI8, 0x3000, kFmtBitBlt, 10, 8, kFmtBitBlt, 7, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE0 | SETS_CCODES, - "adds", "!0C, !0C, #!1d", 2), + "adds", "!0C, !0C, #!1d", 2, kFixupNone), ENCODING_MAP(kThumbAddRRR, 0x1800, kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtBitBlt, 8, 6, kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12 | SETS_CCODES, - "adds", "!0C, !1C, !2C", 2), + "adds", "!0C, !1C, !2C", 2, kFixupNone), ENCODING_MAP(kThumbAddRRLH, 0x4440, kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1, kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE01, - "add", "!0C, !1C", 2), + "add", "!0C, !1C", 2, kFixupNone), ENCODING_MAP(kThumbAddRRHL, 0x4480, kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1, kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE01, - "add", "!0C, !1C", 2), + "add", "!0C, !1C", 2, kFixupNone), ENCODING_MAP(kThumbAddRRHH, 0x44c0, kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1, kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE01, - "add", "!0C, !1C", 2), + "add", "!0C, !1C", 2, kFixupNone), ENCODING_MAP(kThumbAddPcRel, 0xa000, kFmtBitBlt, 10, 8, kFmtBitBlt, 7, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1, IS_TERTIARY_OP | IS_BRANCH | NEEDS_FIXUP, - "add", "!0C, pc, #!1E", 2), + "add", "!0C, pc, #!1E", 2, kFixupLoad), ENCODING_MAP(kThumbAddSpRel, 0xa800, - kFmtBitBlt, 10, 8, kFmtUnused, -1, -1, kFmtBitBlt, 7, 0, + kFmtBitBlt, 10, 8, kFmtSkip, -1, -1, kFmtBitBlt, 7, 0, kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF_SP | REG_USE_SP, - "add", "!0C, sp, #!2E", 2), + "add", "!0C, sp, #!2E", 2, kFixupNone), ENCODING_MAP(kThumbAddSpI7, 0xb000, kFmtBitBlt, 6, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1, kFmtUnused, -1, -1, IS_UNARY_OP | REG_DEF_SP | REG_USE_SP, - "add", "sp, #!0d*4", 2), + "add", "sp, #!0d*4", 2, kFixupNone), ENCODING_MAP(kThumbAndRR, 0x4000, kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1, kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE01 | SETS_CCODES, - "ands", "!0C, !1C", 2), + "ands", "!0C, !1C", 2, kFixupNone), ENCODING_MAP(kThumbAsrRRI5, 0x1000, kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtBitBlt, 10, 6, kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1 | SETS_CCODES, - "asrs", "!0C, !1C, #!2d", 2), + "asrs", "!0C, !1C, #!2d", 2, kFixupNone), ENCODING_MAP(kThumbAsrRR, 0x4100, kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1, kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE01 | SETS_CCODES, - "asrs", "!0C, !1C", 2), + "asrs", "!0C, !1C", 2, kFixupNone), ENCODING_MAP(kThumbBCond, 0xd000, kFmtBitBlt, 7, 0, kFmtBitBlt, 11, 8, kFmtUnused, -1, -1, kFmtUnused, -1, -1, IS_BINARY_OP | IS_BRANCH | USES_CCODES | - NEEDS_FIXUP, "b!1c", "!0t", 2), + NEEDS_FIXUP, "b!1c", "!0t", 2, kFixupCondBranch), ENCODING_MAP(kThumbBUncond, 0xe000, kFmtBitBlt, 10, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1, kFmtUnused, -1, -1, IS_UNARY_OP | IS_BRANCH | NEEDS_FIXUP, - "b", "!0t", 2), + "b", "!0t", 2, kFixupT1Branch), ENCODING_MAP(kThumbBicRR, 0x4380, kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1, kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE01 | SETS_CCODES, - "bics", "!0C, !1C", 2), + "bics", "!0C, !1C", 2, kFixupNone), ENCODING_MAP(kThumbBkpt, 0xbe00, kFmtBitBlt, 7, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1, kFmtUnused, -1, -1, IS_UNARY_OP | IS_BRANCH, - "bkpt", "!0d", 2), + "bkpt", "!0d", 2, kFixupNone), ENCODING_MAP(kThumbBlx1, 0xf000, kFmtBitBlt, 10, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1, kFmtUnused, -1, -1, IS_BINARY_OP | IS_BRANCH | REG_DEF_LR | - NEEDS_FIXUP, "blx_1", "!0u", 2), + NEEDS_FIXUP, "blx_1", "!0u", 2, kFixupBlx1), ENCODING_MAP(kThumbBlx2, 0xe800, kFmtBitBlt, 10, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1, kFmtUnused, -1, -1, IS_BINARY_OP | IS_BRANCH | REG_DEF_LR | - NEEDS_FIXUP, "blx_2", "!0v", 2), + NEEDS_FIXUP, "blx_2", "!0v", 2, kFixupLabel), ENCODING_MAP(kThumbBl1, 0xf000, kFmtBitBlt, 10, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1, - kFmtUnused, -1, -1, IS_UNARY_OP | IS_BRANCH | REG_DEF_LR, - "bl_1", "!0u", 2), + kFmtUnused, -1, -1, IS_UNARY_OP | IS_BRANCH | REG_DEF_LR | NEEDS_FIXUP, + "bl_1", "!0u", 2, kFixupBl1), ENCODING_MAP(kThumbBl2, 0xf800, kFmtBitBlt, 10, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1, - kFmtUnused, -1, -1, IS_UNARY_OP | IS_BRANCH | REG_DEF_LR, - "bl_2", "!0v", 2), + kFmtUnused, -1, -1, IS_UNARY_OP | IS_BRANCH | REG_DEF_LR | NEEDS_FIXUP, + "bl_2", "!0v", 2, kFixupLabel), ENCODING_MAP(kThumbBlxR, 0x4780, kFmtBitBlt, 6, 3, kFmtUnused, -1, -1, kFmtUnused, -1, -1, kFmtUnused, -1, -1, IS_UNARY_OP | REG_USE0 | IS_BRANCH | REG_DEF_LR, - "blx", "!0C", 2), + "blx", "!0C", 2, kFixupNone), ENCODING_MAP(kThumbBx, 0x4700, kFmtBitBlt, 6, 3, kFmtUnused, -1, -1, kFmtUnused, -1, -1, kFmtUnused, -1, -1, IS_UNARY_OP | IS_BRANCH, - "bx", "!0C", 2), + "bx", "!0C", 2, kFixupNone), ENCODING_MAP(kThumbCmnRR, 0x42c0, kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1, kFmtUnused, -1, -1, IS_BINARY_OP | REG_USE01 | SETS_CCODES, - "cmn", "!0C, !1C", 2), + "cmn", "!0C, !1C", 2, kFixupNone), ENCODING_MAP(kThumbCmpRI8, 0x2800, kFmtBitBlt, 10, 8, kFmtBitBlt, 7, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1, IS_BINARY_OP | REG_USE0 | SETS_CCODES, - "cmp", "!0C, #!1d", 2), + "cmp", "!0C, #!1d", 2, kFixupNone), ENCODING_MAP(kThumbCmpRR, 0x4280, kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1, kFmtUnused, -1, -1, IS_BINARY_OP | REG_USE01 | SETS_CCODES, - "cmp", "!0C, !1C", 2), + "cmp", "!0C, !1C", 2, kFixupNone), ENCODING_MAP(kThumbCmpLH, 0x4540, kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1, kFmtUnused, -1, -1, IS_BINARY_OP | REG_USE01 | SETS_CCODES, - "cmp", "!0C, !1C", 2), + "cmp", "!0C, !1C", 2, kFixupNone), ENCODING_MAP(kThumbCmpHL, 0x4580, kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1, kFmtUnused, -1, -1, IS_BINARY_OP | REG_USE01 | SETS_CCODES, - "cmp", "!0C, !1C", 2), + "cmp", "!0C, !1C", 2, kFixupNone), ENCODING_MAP(kThumbCmpHH, 0x45c0, kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1, kFmtUnused, -1, -1, IS_BINARY_OP | REG_USE01 | SETS_CCODES, - "cmp", "!0C, !1C", 2), + "cmp", "!0C, !1C", 2, kFixupNone), ENCODING_MAP(kThumbEorRR, 0x4040, kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1, kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE01 | SETS_CCODES, - "eors", "!0C, !1C", 2), + "eors", "!0C, !1C", 2, kFixupNone), ENCODING_MAP(kThumbLdmia, 0xc800, kFmtBitBlt, 10, 8, kFmtBitBlt, 7, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE0 | REG_DEF_LIST1 | IS_LOAD, - "ldmia", "!0C!!, ", 2), + "ldmia", "!0C!!, ", 2, kFixupNone), ENCODING_MAP(kThumbLdrRRI5, 0x6800, kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtBitBlt, 10, 6, kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1 | IS_LOAD, - "ldr", "!0C, [!1C, #!2E]", 2), + "ldr", "!0C, [!1C, #!2E]", 2, kFixupNone), ENCODING_MAP(kThumbLdrRRR, 0x5800, kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtBitBlt, 8, 6, kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12 | IS_LOAD, - "ldr", "!0C, [!1C, !2C]", 2), + "ldr", "!0C, [!1C, !2C]", 2, kFixupNone), ENCODING_MAP(kThumbLdrPcRel, 0x4800, kFmtBitBlt, 10, 8, kFmtBitBlt, 7, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0 | REG_USE_PC - | IS_LOAD | NEEDS_FIXUP, "ldr", "!0C, [pc, #!1E]", 2), + | IS_LOAD | NEEDS_FIXUP, "ldr", "!0C, [pc, #!1E]", 2, kFixupLoad), ENCODING_MAP(kThumbLdrSpRel, 0x9800, - kFmtBitBlt, 10, 8, kFmtUnused, -1, -1, kFmtBitBlt, 7, 0, + kFmtBitBlt, 10, 8, kFmtSkip, -1, -1, kFmtBitBlt, 7, 0, kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0 | REG_USE_SP - | IS_LOAD, "ldr", "!0C, [sp, #!2E]", 2), + | IS_LOAD, "ldr", "!0C, [sp, #!2E]", 2, kFixupNone), ENCODING_MAP(kThumbLdrbRRI5, 0x7800, kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtBitBlt, 10, 6, kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1 | IS_LOAD, - "ldrb", "!0C, [!1C, #2d]", 2), + "ldrb", "!0C, [!1C, #2d]", 2, kFixupNone), ENCODING_MAP(kThumbLdrbRRR, 0x5c00, kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtBitBlt, 8, 6, kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12 | IS_LOAD, - "ldrb", "!0C, [!1C, !2C]", 2), + "ldrb", "!0C, [!1C, !2C]", 2, kFixupNone), ENCODING_MAP(kThumbLdrhRRI5, 0x8800, kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtBitBlt, 10, 6, kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1 | IS_LOAD, - "ldrh", "!0C, [!1C, #!2F]", 2), + "ldrh", "!0C, [!1C, #!2F]", 2, kFixupNone), ENCODING_MAP(kThumbLdrhRRR, 0x5a00, kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtBitBlt, 8, 6, kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12 | IS_LOAD, - "ldrh", "!0C, [!1C, !2C]", 2), + "ldrh", "!0C, [!1C, !2C]", 2, kFixupNone), ENCODING_MAP(kThumbLdrsbRRR, 0x5600, kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtBitBlt, 8, 6, kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12 | IS_LOAD, - "ldrsb", "!0C, [!1C, !2C]", 2), + "ldrsb", "!0C, [!1C, !2C]", 2, kFixupNone), ENCODING_MAP(kThumbLdrshRRR, 0x5e00, kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtBitBlt, 8, 6, kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12 | IS_LOAD, - "ldrsh", "!0C, [!1C, !2C]", 2), + "ldrsh", "!0C, [!1C, !2C]", 2, kFixupNone), ENCODING_MAP(kThumbLslRRI5, 0x0000, kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtBitBlt, 10, 6, kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1 | SETS_CCODES, - "lsls", "!0C, !1C, #!2d", 2), + "lsls", "!0C, !1C, #!2d", 2, kFixupNone), ENCODING_MAP(kThumbLslRR, 0x4080, kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1, kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE01 | SETS_CCODES, - "lsls", "!0C, !1C", 2), + "lsls", "!0C, !1C", 2, kFixupNone), ENCODING_MAP(kThumbLsrRRI5, 0x0800, kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtBitBlt, 10, 6, kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1 | SETS_CCODES, - "lsrs", "!0C, !1C, #!2d", 2), + "lsrs", "!0C, !1C, #!2d", 2, kFixupNone), ENCODING_MAP(kThumbLsrRR, 0x40c0, kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1, kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE01 | SETS_CCODES, - "lsrs", "!0C, !1C", 2), + "lsrs", "!0C, !1C", 2, kFixupNone), ENCODING_MAP(kThumbMovImm, 0x2000, kFmtBitBlt, 10, 8, kFmtBitBlt, 7, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0 | SETS_CCODES, - "movs", "!0C, #!1d", 2), + "movs", "!0C, #!1d", 2, kFixupNone), ENCODING_MAP(kThumbMovRR, 0x1c00, kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1, kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1 | SETS_CCODES, - "movs", "!0C, !1C", 2), + "movs", "!0C, !1C", 2, kFixupNone), ENCODING_MAP(kThumbMovRR_H2H, 0x46c0, kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1, kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1, - "mov", "!0C, !1C", 2), + "mov", "!0C, !1C", 2, kFixupNone), ENCODING_MAP(kThumbMovRR_H2L, 0x4640, kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1, kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1, - "mov", "!0C, !1C", 2), + "mov", "!0C, !1C", 2, kFixupNone), ENCODING_MAP(kThumbMovRR_L2H, 0x4680, kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1, kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1, - "mov", "!0C, !1C", 2), + "mov", "!0C, !1C", 2, kFixupNone), ENCODING_MAP(kThumbMul, 0x4340, kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1, kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE01 | SETS_CCODES, - "muls", "!0C, !1C", 2), + "muls", "!0C, !1C", 2, kFixupNone), ENCODING_MAP(kThumbMvn, 0x43c0, kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1, kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1 | SETS_CCODES, - "mvns", "!0C, !1C", 2), + "mvns", "!0C, !1C", 2, kFixupNone), ENCODING_MAP(kThumbNeg, 0x4240, kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1, kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1 | SETS_CCODES, - "negs", "!0C, !1C", 2), + "negs", "!0C, !1C", 2, kFixupNone), ENCODING_MAP(kThumbOrr, 0x4300, kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1, kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE01 | SETS_CCODES, - "orrs", "!0C, !1C", 2), + "orrs", "!0C, !1C", 2, kFixupNone), ENCODING_MAP(kThumbPop, 0xbc00, kFmtBitBlt, 8, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1, kFmtUnused, -1, -1, IS_UNARY_OP | REG_DEF_SP | REG_USE_SP | REG_DEF_LIST0 - | IS_LOAD, "pop", "", 2), + | IS_LOAD, "pop", "", 2, kFixupNone), ENCODING_MAP(kThumbPush, 0xb400, kFmtBitBlt, 8, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1, kFmtUnused, -1, -1, IS_UNARY_OP | REG_DEF_SP | REG_USE_SP | REG_USE_LIST0 - | IS_STORE, "push", "", 2), + | IS_STORE, "push", "", 2, kFixupNone), ENCODING_MAP(kThumbRorRR, 0x41c0, kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1, kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE01 | SETS_CCODES, - "rors", "!0C, !1C", 2), + "rors", "!0C, !1C", 2, kFixupNone), ENCODING_MAP(kThumbSbc, 0x4180, kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1, kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE01 | USES_CCODES | SETS_CCODES, - "sbcs", "!0C, !1C", 2), + "sbcs", "!0C, !1C", 2, kFixupNone), ENCODING_MAP(kThumbStmia, 0xc000, kFmtBitBlt, 10, 8, kFmtBitBlt, 7, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0 | REG_USE0 | REG_USE_LIST1 | IS_STORE, - "stmia", "!0C!!, ", 2), + "stmia", "!0C!!, ", 2, kFixupNone), ENCODING_MAP(kThumbStrRRI5, 0x6000, kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtBitBlt, 10, 6, kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_USE01 | IS_STORE, - "str", "!0C, [!1C, #!2E]", 2), + "str", "!0C, [!1C, #!2E]", 2, kFixupNone), ENCODING_MAP(kThumbStrRRR, 0x5000, kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtBitBlt, 8, 6, kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_USE012 | IS_STORE, - "str", "!0C, [!1C, !2C]", 2), + "str", "!0C, [!1C, !2C]", 2, kFixupNone), ENCODING_MAP(kThumbStrSpRel, 0x9000, - kFmtBitBlt, 10, 8, kFmtUnused, -1, -1, kFmtBitBlt, 7, 0, + kFmtBitBlt, 10, 8, kFmtSkip, -1, -1, kFmtBitBlt, 7, 0, kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_USE0 | REG_USE_SP - | IS_STORE, "str", "!0C, [sp, #!2E]", 2), + | IS_STORE, "str", "!0C, [sp, #!2E]", 2, kFixupNone), ENCODING_MAP(kThumbStrbRRI5, 0x7000, kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtBitBlt, 10, 6, kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_USE01 | IS_STORE, - "strb", "!0C, [!1C, #!2d]", 2), + "strb", "!0C, [!1C, #!2d]", 2, kFixupNone), ENCODING_MAP(kThumbStrbRRR, 0x5400, kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtBitBlt, 8, 6, kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_USE012 | IS_STORE, - "strb", "!0C, [!1C, !2C]", 2), + "strb", "!0C, [!1C, !2C]", 2, kFixupNone), ENCODING_MAP(kThumbStrhRRI5, 0x8000, kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtBitBlt, 10, 6, kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_USE01 | IS_STORE, - "strh", "!0C, [!1C, #!2F]", 2), + "strh", "!0C, [!1C, #!2F]", 2, kFixupNone), ENCODING_MAP(kThumbStrhRRR, 0x5200, kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtBitBlt, 8, 6, kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_USE012 | IS_STORE, - "strh", "!0C, [!1C, !2C]", 2), + "strh", "!0C, [!1C, !2C]", 2, kFixupNone), ENCODING_MAP(kThumbSubRRI3, 0x1e00, kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtBitBlt, 8, 6, kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1 | SETS_CCODES, - "subs", "!0C, !1C, #!2d", 2), + "subs", "!0C, !1C, #!2d", 2, kFixupNone), ENCODING_MAP(kThumbSubRI8, 0x3800, kFmtBitBlt, 10, 8, kFmtBitBlt, 7, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE0 | SETS_CCODES, - "subs", "!0C, #!1d", 2), + "subs", "!0C, #!1d", 2, kFixupNone), ENCODING_MAP(kThumbSubRRR, 0x1a00, kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtBitBlt, 8, 6, kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12 | SETS_CCODES, - "subs", "!0C, !1C, !2C", 2), + "subs", "!0C, !1C, !2C", 2, kFixupNone), ENCODING_MAP(kThumbSubSpI7, 0xb080, kFmtBitBlt, 6, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1, kFmtUnused, -1, -1, IS_UNARY_OP | REG_DEF_SP | REG_USE_SP, - "sub", "sp, #!0d*4", 2), + "sub", "sp, #!0d*4", 2, kFixupNone), ENCODING_MAP(kThumbSwi, 0xdf00, kFmtBitBlt, 7, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1, kFmtUnused, -1, -1, IS_UNARY_OP | IS_BRANCH, - "swi", "!0d", 2), + "swi", "!0d", 2, kFixupNone), ENCODING_MAP(kThumbTst, 0x4200, kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1, kFmtUnused, -1, -1, IS_UNARY_OP | REG_USE01 | SETS_CCODES, - "tst", "!0C, !1C", 2), + "tst", "!0C, !1C", 2, kFixupNone), ENCODING_MAP(kThumb2Vldrs, 0xed900a00, kFmtSfp, 22, 12, kFmtBitBlt, 19, 16, kFmtBitBlt, 7, 0, kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1 | IS_LOAD | - REG_DEF_LR | NEEDS_FIXUP, "vldr", "!0s, [!1C, #!2E]", 4), + REG_DEF_LR | NEEDS_FIXUP, "vldr", "!0s, [!1C, #!2E]", 4, kFixupVLoad), ENCODING_MAP(kThumb2Vldrd, 0xed900b00, kFmtDfp, 22, 12, kFmtBitBlt, 19, 16, kFmtBitBlt, 7, 0, kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1 | IS_LOAD | - REG_DEF_LR | NEEDS_FIXUP, "vldr", "!0S, [!1C, #!2E]", 4), + REG_DEF_LR | NEEDS_FIXUP, "vldr", "!0S, [!1C, #!2E]", 4, kFixupVLoad), ENCODING_MAP(kThumb2Vmuls, 0xee200a00, kFmtSfp, 22, 12, kFmtSfp, 7, 16, kFmtSfp, 5, 0, kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12, - "vmuls", "!0s, !1s, !2s", 4), + "vmuls", "!0s, !1s, !2s", 4, kFixupNone), ENCODING_MAP(kThumb2Vmuld, 0xee200b00, kFmtDfp, 22, 12, kFmtDfp, 7, 16, kFmtDfp, 5, 0, kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12, - "vmuld", "!0S, !1S, !2S", 4), + "vmuld", "!0S, !1S, !2S", 4, kFixupNone), ENCODING_MAP(kThumb2Vstrs, 0xed800a00, kFmtSfp, 22, 12, kFmtBitBlt, 19, 16, kFmtBitBlt, 7, 0, kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_USE01 | IS_STORE, - "vstr", "!0s, [!1C, #!2E]", 4), + "vstr", "!0s, [!1C, #!2E]", 4, kFixupNone), ENCODING_MAP(kThumb2Vstrd, 0xed800b00, kFmtDfp, 22, 12, kFmtBitBlt, 19, 16, kFmtBitBlt, 7, 0, kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_USE01 | IS_STORE, - "vstr", "!0S, [!1C, #!2E]", 4), + "vstr", "!0S, [!1C, #!2E]", 4, kFixupNone), ENCODING_MAP(kThumb2Vsubs, 0xee300a40, kFmtSfp, 22, 12, kFmtSfp, 7, 16, kFmtSfp, 5, 0, kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12, - "vsub", "!0s, !1s, !2s", 4), + "vsub", "!0s, !1s, !2s", 4, kFixupNone), ENCODING_MAP(kThumb2Vsubd, 0xee300b40, kFmtDfp, 22, 12, kFmtDfp, 7, 16, kFmtDfp, 5, 0, kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12, - "vsub", "!0S, !1S, !2S", 4), + "vsub", "!0S, !1S, !2S", 4, kFixupNone), ENCODING_MAP(kThumb2Vadds, 0xee300a00, kFmtSfp, 22, 12, kFmtSfp, 7, 16, kFmtSfp, 5, 0, kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12, - "vadd", "!0s, !1s, !2s", 4), + "vadd", "!0s, !1s, !2s", 4, kFixupNone), ENCODING_MAP(kThumb2Vaddd, 0xee300b00, kFmtDfp, 22, 12, kFmtDfp, 7, 16, kFmtDfp, 5, 0, kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12, - "vadd", "!0S, !1S, !2S", 4), + "vadd", "!0S, !1S, !2S", 4, kFixupNone), ENCODING_MAP(kThumb2Vdivs, 0xee800a00, kFmtSfp, 22, 12, kFmtSfp, 7, 16, kFmtSfp, 5, 0, kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12, - "vdivs", "!0s, !1s, !2s", 4), + "vdivs", "!0s, !1s, !2s", 4, kFixupNone), ENCODING_MAP(kThumb2Vdivd, 0xee800b00, kFmtDfp, 22, 12, kFmtDfp, 7, 16, kFmtDfp, 5, 0, kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12, - "vdivd", "!0S, !1S, !2S", 4), + "vdivd", "!0S, !1S, !2S", 4, kFixupNone), ENCODING_MAP(kThumb2VcvtIF, 0xeeb80ac0, kFmtSfp, 22, 12, kFmtSfp, 5, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1, - "vcvt.f32", "!0s, !1s", 4), + "vcvt.f32", "!0s, !1s", 4, kFixupNone), ENCODING_MAP(kThumb2VcvtID, 0xeeb80bc0, kFmtDfp, 22, 12, kFmtSfp, 5, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1, - "vcvt.f64", "!0S, !1s", 4), + "vcvt.f64", "!0S, !1s", 4, kFixupNone), ENCODING_MAP(kThumb2VcvtFI, 0xeebd0ac0, kFmtSfp, 22, 12, kFmtSfp, 5, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1, - "vcvt.s32.f32 ", "!0s, !1s", 4), + "vcvt.s32.f32 ", "!0s, !1s", 4, kFixupNone), ENCODING_MAP(kThumb2VcvtDI, 0xeebd0bc0, kFmtSfp, 22, 12, kFmtDfp, 5, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1, - "vcvt.s32.f64 ", "!0s, !1S", 4), + "vcvt.s32.f64 ", "!0s, !1S", 4, kFixupNone), ENCODING_MAP(kThumb2VcvtFd, 0xeeb70ac0, kFmtDfp, 22, 12, kFmtSfp, 5, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1, - "vcvt.f64.f32 ", "!0S, !1s", 4), + "vcvt.f64.f32 ", "!0S, !1s", 4, kFixupNone), ENCODING_MAP(kThumb2VcvtDF, 0xeeb70bc0, kFmtSfp, 22, 12, kFmtDfp, 5, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1, - "vcvt.f32.f64 ", "!0s, !1S", 4), + "vcvt.f32.f64 ", "!0s, !1S", 4, kFixupNone), ENCODING_MAP(kThumb2Vsqrts, 0xeeb10ac0, kFmtSfp, 22, 12, kFmtSfp, 5, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1, - "vsqrt.f32 ", "!0s, !1s", 4), + "vsqrt.f32 ", "!0s, !1s", 4, kFixupNone), ENCODING_MAP(kThumb2Vsqrtd, 0xeeb10bc0, kFmtDfp, 22, 12, kFmtDfp, 5, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1, - "vsqrt.f64 ", "!0S, !1S", 4), + "vsqrt.f64 ", "!0S, !1S", 4, kFixupNone), ENCODING_MAP(kThumb2MovImmShift, 0xf04f0000, /* no setflags encoding */ kFmtBitBlt, 11, 8, kFmtModImm, -1, -1, kFmtUnused, -1, -1, kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0, - "mov", "!0C, #!1m", 4), + "mov", "!0C, #!1m", 4, kFixupNone), ENCODING_MAP(kThumb2MovImm16, 0xf2400000, kFmtBitBlt, 11, 8, kFmtImm16, -1, -1, kFmtUnused, -1, -1, kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0, - "mov", "!0C, #!1M", 4), + "mov", "!0C, #!1M", 4, kFixupNone), ENCODING_MAP(kThumb2StrRRI12, 0xf8c00000, kFmtBitBlt, 15, 12, kFmtBitBlt, 19, 16, kFmtBitBlt, 11, 0, kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_USE01 | IS_STORE, - "str", "!0C, [!1C, #!2d]", 4), + "str", "!0C, [!1C, #!2d]", 4, kFixupNone), ENCODING_MAP(kThumb2LdrRRI12, 0xf8d00000, kFmtBitBlt, 15, 12, kFmtBitBlt, 19, 16, kFmtBitBlt, 11, 0, kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1 | IS_LOAD, - "ldr", "!0C, [!1C, #!2d]", 4), + "ldr", "!0C, [!1C, #!2d]", 4, kFixupNone), ENCODING_MAP(kThumb2StrRRI8Predec, 0xf8400c00, kFmtBitBlt, 15, 12, kFmtBitBlt, 19, 16, kFmtBitBlt, 8, 0, kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_USE01 | IS_STORE, - "str", "!0C, [!1C, #-!2d]", 4), + "str", "!0C, [!1C, #-!2d]", 4, kFixupNone), ENCODING_MAP(kThumb2LdrRRI8Predec, 0xf8500c00, kFmtBitBlt, 15, 12, kFmtBitBlt, 19, 16, kFmtBitBlt, 8, 0, kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1 | IS_LOAD, - "ldr", "!0C, [!1C, #-!2d]", 4), + "ldr", "!0C, [!1C, #-!2d]", 4, kFixupNone), ENCODING_MAP(kThumb2Cbnz, 0xb900, /* Note: does not affect flags */ kFmtBitBlt, 2, 0, kFmtImm6, -1, -1, kFmtUnused, -1, -1, kFmtUnused, -1, -1, IS_BINARY_OP | REG_USE0 | IS_BRANCH | - NEEDS_FIXUP, "cbnz", "!0C,!1t", 2), + NEEDS_FIXUP, "cbnz", "!0C,!1t", 2, kFixupCBxZ), ENCODING_MAP(kThumb2Cbz, 0xb100, /* Note: does not affect flags */ kFmtBitBlt, 2, 0, kFmtImm6, -1, -1, kFmtUnused, -1, -1, kFmtUnused, -1, -1, IS_BINARY_OP | REG_USE0 | IS_BRANCH | - NEEDS_FIXUP, "cbz", "!0C,!1t", 2), + NEEDS_FIXUP, "cbz", "!0C,!1t", 2, kFixupCBxZ), ENCODING_MAP(kThumb2AddRRI12, 0xf2000000, kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtImm12, -1, -1, kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1,/* Note: doesn't affect flags */ - "add", "!0C,!1C,#!2d", 4), + "add", "!0C,!1C,#!2d", 4, kFixupNone), ENCODING_MAP(kThumb2MovRR, 0xea4f0000, /* no setflags encoding */ kFmtBitBlt, 11, 8, kFmtBitBlt, 3, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1, - "mov", "!0C, !1C", 4), + "mov", "!0C, !1C", 4, kFixupNone), ENCODING_MAP(kThumb2Vmovs, 0xeeb00a40, kFmtSfp, 22, 12, kFmtSfp, 5, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1, - "vmov.f32 ", " !0s, !1s", 4), + "vmov.f32 ", " !0s, !1s", 4, kFixupNone), ENCODING_MAP(kThumb2Vmovd, 0xeeb00b40, kFmtDfp, 22, 12, kFmtDfp, 5, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1, - "vmov.f64 ", " !0S, !1S", 4), + "vmov.f64 ", " !0S, !1S", 4, kFixupNone), ENCODING_MAP(kThumb2Ldmia, 0xe8900000, kFmtBitBlt, 19, 16, kFmtBitBlt, 15, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE0 | REG_DEF_LIST1 | IS_LOAD, - "ldmia", "!0C!!, ", 4), + "ldmia", "!0C!!, ", 4, kFixupNone), ENCODING_MAP(kThumb2Stmia, 0xe8800000, kFmtBitBlt, 19, 16, kFmtBitBlt, 15, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE0 | REG_USE_LIST1 | IS_STORE, - "stmia", "!0C!!, ", 4), + "stmia", "!0C!!, ", 4, kFixupNone), ENCODING_MAP(kThumb2AddRRR, 0xeb100000, /* setflags encoding */ kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0, kFmtShift, -1, -1, IS_QUAD_OP | REG_DEF0_USE12 | SETS_CCODES, - "adds", "!0C, !1C, !2C!3H", 4), + "adds", "!0C, !1C, !2C!3H", 4, kFixupNone), ENCODING_MAP(kThumb2SubRRR, 0xebb00000, /* setflags enconding */ kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0, kFmtShift, -1, -1, IS_QUAD_OP | REG_DEF0_USE12 | SETS_CCODES, - "subs", "!0C, !1C, !2C!3H", 4), + "subs", "!0C, !1C, !2C!3H", 4, kFixupNone), ENCODING_MAP(kThumb2SbcRRR, 0xeb700000, /* setflags encoding */ kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0, kFmtShift, -1, -1, IS_QUAD_OP | REG_DEF0_USE12 | USES_CCODES | SETS_CCODES, - "sbcs", "!0C, !1C, !2C!3H", 4), + "sbcs", "!0C, !1C, !2C!3H", 4, kFixupNone), ENCODING_MAP(kThumb2CmpRR, 0xebb00f00, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0, kFmtShift, -1, -1, kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_USE01 | SETS_CCODES, - "cmp", "!0C, !1C", 4), + "cmp", "!0C, !1C", 4, kFixupNone), ENCODING_MAP(kThumb2SubRRI12, 0xf2a00000, kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtImm12, -1, -1, kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1,/* Note: doesn't affect flags */ - "sub", "!0C,!1C,#!2d", 4), + "sub", "!0C,!1C,#!2d", 4, kFixupNone), ENCODING_MAP(kThumb2MvnImm12, 0xf06f0000, /* no setflags encoding */ kFmtBitBlt, 11, 8, kFmtImm12, -1, -1, kFmtUnused, -1, -1, kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0, - "mvn", "!0C, #!1n", 4), + "mvn", "!0C, #!1n", 4, kFixupNone), ENCODING_MAP(kThumb2Sel, 0xfaa0f080, kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0, kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12 | USES_CCODES, - "sel", "!0C, !1C, !2C", 4), + "sel", "!0C, !1C, !2C", 4, kFixupNone), ENCODING_MAP(kThumb2Ubfx, 0xf3c00000, kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtLsb, -1, -1, kFmtBWidth, 4, 0, IS_QUAD_OP | REG_DEF0_USE1, - "ubfx", "!0C, !1C, #!2d, #!3d", 4), + "ubfx", "!0C, !1C, #!2d, #!3d", 4, kFixupNone), ENCODING_MAP(kThumb2Sbfx, 0xf3400000, kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtLsb, -1, -1, kFmtBWidth, 4, 0, IS_QUAD_OP | REG_DEF0_USE1, - "sbfx", "!0C, !1C, #!2d, #!3d", 4), + "sbfx", "!0C, !1C, #!2d, #!3d", 4, kFixupNone), ENCODING_MAP(kThumb2LdrRRR, 0xf8500000, kFmtBitBlt, 15, 12, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0, kFmtBitBlt, 5, 4, IS_QUAD_OP | REG_DEF0_USE12 | IS_LOAD, - "ldr", "!0C, [!1C, !2C, LSL #!3d]", 4), + "ldr", "!0C, [!1C, !2C, LSL #!3d]", 4, kFixupNone), ENCODING_MAP(kThumb2LdrhRRR, 0xf8300000, kFmtBitBlt, 15, 12, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0, kFmtBitBlt, 5, 4, IS_QUAD_OP | REG_DEF0_USE12 | IS_LOAD, - "ldrh", "!0C, [!1C, !2C, LSL #!3d]", 4), + "ldrh", "!0C, [!1C, !2C, LSL #!3d]", 4, kFixupNone), ENCODING_MAP(kThumb2LdrshRRR, 0xf9300000, kFmtBitBlt, 15, 12, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0, kFmtBitBlt, 5, 4, IS_QUAD_OP | REG_DEF0_USE12 | IS_LOAD, - "ldrsh", "!0C, [!1C, !2C, LSL #!3d]", 4), + "ldrsh", "!0C, [!1C, !2C, LSL #!3d]", 4, kFixupNone), ENCODING_MAP(kThumb2LdrbRRR, 0xf8100000, kFmtBitBlt, 15, 12, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0, kFmtBitBlt, 5, 4, IS_QUAD_OP | REG_DEF0_USE12 | IS_LOAD, - "ldrb", "!0C, [!1C, !2C, LSL #!3d]", 4), + "ldrb", "!0C, [!1C, !2C, LSL #!3d]", 4, kFixupNone), ENCODING_MAP(kThumb2LdrsbRRR, 0xf9100000, kFmtBitBlt, 15, 12, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0, kFmtBitBlt, 5, 4, IS_QUAD_OP | REG_DEF0_USE12 | IS_LOAD, - "ldrsb", "!0C, [!1C, !2C, LSL #!3d]", 4), + "ldrsb", "!0C, [!1C, !2C, LSL #!3d]", 4, kFixupNone), ENCODING_MAP(kThumb2StrRRR, 0xf8400000, kFmtBitBlt, 15, 12, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0, kFmtBitBlt, 5, 4, IS_QUAD_OP | REG_USE012 | IS_STORE, - "str", "!0C, [!1C, !2C, LSL #!3d]", 4), + "str", "!0C, [!1C, !2C, LSL #!3d]", 4, kFixupNone), ENCODING_MAP(kThumb2StrhRRR, 0xf8200000, kFmtBitBlt, 15, 12, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0, kFmtBitBlt, 5, 4, IS_QUAD_OP | REG_USE012 | IS_STORE, - "strh", "!0C, [!1C, !2C, LSL #!3d]", 4), + "strh", "!0C, [!1C, !2C, LSL #!3d]", 4, kFixupNone), ENCODING_MAP(kThumb2StrbRRR, 0xf8000000, kFmtBitBlt, 15, 12, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0, kFmtBitBlt, 5, 4, IS_QUAD_OP | REG_USE012 | IS_STORE, - "strb", "!0C, [!1C, !2C, LSL #!3d]", 4), + "strb", "!0C, [!1C, !2C, LSL #!3d]", 4, kFixupNone), ENCODING_MAP(kThumb2LdrhRRI12, 0xf8b00000, kFmtBitBlt, 15, 12, kFmtBitBlt, 19, 16, kFmtBitBlt, 11, 0, kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1 | IS_LOAD, - "ldrh", "!0C, [!1C, #!2d]", 4), + "ldrh", "!0C, [!1C, #!2d]", 4, kFixupNone), ENCODING_MAP(kThumb2LdrshRRI12, 0xf9b00000, kFmtBitBlt, 15, 12, kFmtBitBlt, 19, 16, kFmtBitBlt, 11, 0, kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1 | IS_LOAD, - "ldrsh", "!0C, [!1C, #!2d]", 4), + "ldrsh", "!0C, [!1C, #!2d]", 4, kFixupNone), ENCODING_MAP(kThumb2LdrbRRI12, 0xf8900000, kFmtBitBlt, 15, 12, kFmtBitBlt, 19, 16, kFmtBitBlt, 11, 0, kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1 | IS_LOAD, - "ldrb", "!0C, [!1C, #!2d]", 4), + "ldrb", "!0C, [!1C, #!2d]", 4, kFixupNone), ENCODING_MAP(kThumb2LdrsbRRI12, 0xf9900000, kFmtBitBlt, 15, 12, kFmtBitBlt, 19, 16, kFmtBitBlt, 11, 0, kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1 | IS_LOAD, - "ldrsb", "!0C, [!1C, #!2d]", 4), + "ldrsb", "!0C, [!1C, #!2d]", 4, kFixupNone), ENCODING_MAP(kThumb2StrhRRI12, 0xf8a00000, kFmtBitBlt, 15, 12, kFmtBitBlt, 19, 16, kFmtBitBlt, 11, 0, kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_USE01 | IS_STORE, - "strh", "!0C, [!1C, #!2d]", 4), + "strh", "!0C, [!1C, #!2d]", 4, kFixupNone), ENCODING_MAP(kThumb2StrbRRI12, 0xf8800000, kFmtBitBlt, 15, 12, kFmtBitBlt, 19, 16, kFmtBitBlt, 11, 0, kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_USE01 | IS_STORE, - "strb", "!0C, [!1C, #!2d]", 4), + "strb", "!0C, [!1C, #!2d]", 4, kFixupNone), ENCODING_MAP(kThumb2Pop, 0xe8bd0000, kFmtBitBlt, 15, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1, kFmtUnused, -1, -1, IS_UNARY_OP | REG_DEF_SP | REG_USE_SP | REG_DEF_LIST0 - | IS_LOAD | NEEDS_FIXUP, "pop", "", 4), + | IS_LOAD | NEEDS_FIXUP, "pop", "", 4, kFixupPushPop), ENCODING_MAP(kThumb2Push, 0xe92d0000, kFmtBitBlt, 15, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1, kFmtUnused, -1, -1, IS_UNARY_OP | REG_DEF_SP | REG_USE_SP | REG_USE_LIST0 - | IS_STORE | NEEDS_FIXUP, "push", "", 4), + | IS_STORE | NEEDS_FIXUP, "push", "", 4, kFixupPushPop), ENCODING_MAP(kThumb2CmpRI12, 0xf1b00f00, kFmtBitBlt, 19, 16, kFmtModImm, -1, -1, kFmtUnused, -1, -1, kFmtUnused, -1, -1, IS_BINARY_OP | REG_USE0 | SETS_CCODES, - "cmp", "!0C, #!1m", 4), + "cmp", "!0C, #!1m", 4, kFixupNone), ENCODING_MAP(kThumb2AdcRRR, 0xeb500000, /* setflags encoding */ kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0, kFmtShift, -1, -1, IS_QUAD_OP | REG_DEF0_USE12 | SETS_CCODES, - "adcs", "!0C, !1C, !2C!3H", 4), + "adcs", "!0C, !1C, !2C!3H", 4, kFixupNone), ENCODING_MAP(kThumb2AndRRR, 0xea000000, kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0, kFmtShift, -1, -1, IS_QUAD_OP | REG_DEF0_USE12, - "and", "!0C, !1C, !2C!3H", 4), + "and", "!0C, !1C, !2C!3H", 4, kFixupNone), ENCODING_MAP(kThumb2BicRRR, 0xea200000, kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0, kFmtShift, -1, -1, IS_QUAD_OP | REG_DEF0_USE12, - "bic", "!0C, !1C, !2C!3H", 4), + "bic", "!0C, !1C, !2C!3H", 4, kFixupNone), ENCODING_MAP(kThumb2CmnRR, 0xeb000000, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0, kFmtShift, -1, -1, kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1 | SETS_CCODES, - "cmn", "!0C, !1C, shift !2d", 4), + "cmn", "!0C, !1C, shift !2d", 4, kFixupNone), ENCODING_MAP(kThumb2EorRRR, 0xea800000, kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0, kFmtShift, -1, -1, IS_QUAD_OP | REG_DEF0_USE12, - "eor", "!0C, !1C, !2C!3H", 4), + "eor", "!0C, !1C, !2C!3H", 4, kFixupNone), ENCODING_MAP(kThumb2MulRRR, 0xfb00f000, kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0, kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12, - "mul", "!0C, !1C, !2C", 4), + "mul", "!0C, !1C, !2C", 4, kFixupNone), ENCODING_MAP(kThumb2MnvRR, 0xea6f0000, kFmtBitBlt, 11, 8, kFmtBitBlt, 3, 0, kFmtShift, -1, -1, kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1, - "mvn", "!0C, !1C, shift !2d", 4), + "mvn", "!0C, !1C, shift !2d", 4, kFixupNone), ENCODING_MAP(kThumb2RsubRRI8, 0xf1d00000, kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtModImm, -1, -1, kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1 | SETS_CCODES, - "rsb", "!0C,!1C,#!2m", 4), + "rsb", "!0C,!1C,#!2m", 4, kFixupNone), ENCODING_MAP(kThumb2NegRR, 0xf1d00000, /* instance of rsub */ kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtUnused, -1, -1, kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1 | SETS_CCODES, - "neg", "!0C,!1C", 4), + "neg", "!0C,!1C", 4, kFixupNone), ENCODING_MAP(kThumb2OrrRRR, 0xea400000, kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0, kFmtShift, -1, -1, IS_QUAD_OP | REG_DEF0_USE12, - "orr", "!0C, !1C, !2C!3H", 4), + "orr", "!0C, !1C, !2C!3H", 4, kFixupNone), ENCODING_MAP(kThumb2TstRR, 0xea100f00, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0, kFmtShift, -1, -1, kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_USE01 | SETS_CCODES, - "tst", "!0C, !1C, shift !2d", 4), + "tst", "!0C, !1C, shift !2d", 4, kFixupNone), ENCODING_MAP(kThumb2LslRRR, 0xfa00f000, kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0, kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12, - "lsl", "!0C, !1C, !2C", 4), + "lsl", "!0C, !1C, !2C", 4, kFixupNone), ENCODING_MAP(kThumb2LsrRRR, 0xfa20f000, kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0, kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12, - "lsr", "!0C, !1C, !2C", 4), + "lsr", "!0C, !1C, !2C", 4, kFixupNone), ENCODING_MAP(kThumb2AsrRRR, 0xfa40f000, kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0, kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12, - "asr", "!0C, !1C, !2C", 4), + "asr", "!0C, !1C, !2C", 4, kFixupNone), ENCODING_MAP(kThumb2RorRRR, 0xfa60f000, kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0, kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12, - "ror", "!0C, !1C, !2C", 4), + "ror", "!0C, !1C, !2C", 4, kFixupNone), ENCODING_MAP(kThumb2LslRRI5, 0xea4f0000, kFmtBitBlt, 11, 8, kFmtBitBlt, 3, 0, kFmtShift5, -1, -1, kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1, - "lsl", "!0C, !1C, #!2d", 4), + "lsl", "!0C, !1C, #!2d", 4, kFixupNone), ENCODING_MAP(kThumb2LsrRRI5, 0xea4f0010, kFmtBitBlt, 11, 8, kFmtBitBlt, 3, 0, kFmtShift5, -1, -1, kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1, - "lsr", "!0C, !1C, #!2d", 4), + "lsr", "!0C, !1C, #!2d", 4, kFixupNone), ENCODING_MAP(kThumb2AsrRRI5, 0xea4f0020, kFmtBitBlt, 11, 8, kFmtBitBlt, 3, 0, kFmtShift5, -1, -1, kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1, - "asr", "!0C, !1C, #!2d", 4), + "asr", "!0C, !1C, #!2d", 4, kFixupNone), ENCODING_MAP(kThumb2RorRRI5, 0xea4f0030, kFmtBitBlt, 11, 8, kFmtBitBlt, 3, 0, kFmtShift5, -1, -1, kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1, - "ror", "!0C, !1C, #!2d", 4), + "ror", "!0C, !1C, #!2d", 4, kFixupNone), ENCODING_MAP(kThumb2BicRRI8, 0xf0200000, kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtModImm, -1, -1, kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1, - "bic", "!0C, !1C, #!2m", 4), + "bic", "!0C, !1C, #!2m", 4, kFixupNone), ENCODING_MAP(kThumb2AndRRI8, 0xf0000000, kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtModImm, -1, -1, kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1, - "and", "!0C, !1C, #!2m", 4), + "and", "!0C, !1C, #!2m", 4, kFixupNone), ENCODING_MAP(kThumb2OrrRRI8, 0xf0400000, kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtModImm, -1, -1, kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1, - "orr", "!0C, !1C, #!2m", 4), + "orr", "!0C, !1C, #!2m", 4, kFixupNone), ENCODING_MAP(kThumb2EorRRI8, 0xf0800000, kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtModImm, -1, -1, kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1, - "eor", "!0C, !1C, #!2m", 4), + "eor", "!0C, !1C, #!2m", 4, kFixupNone), ENCODING_MAP(kThumb2AddRRI8, 0xf1100000, kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtModImm, -1, -1, kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1 | SETS_CCODES, - "adds", "!0C, !1C, #!2m", 4), + "adds", "!0C, !1C, #!2m", 4, kFixupNone), ENCODING_MAP(kThumb2AdcRRI8, 0xf1500000, kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtModImm, -1, -1, kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1 | SETS_CCODES | USES_CCODES, - "adcs", "!0C, !1C, #!2m", 4), + "adcs", "!0C, !1C, #!2m", 4, kFixupNone), ENCODING_MAP(kThumb2SubRRI8, 0xf1b00000, kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtModImm, -1, -1, kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1 | SETS_CCODES, - "subs", "!0C, !1C, #!2m", 4), + "subs", "!0C, !1C, #!2m", 4, kFixupNone), ENCODING_MAP(kThumb2SbcRRI8, 0xf1700000, kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtModImm, -1, -1, kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1 | SETS_CCODES | USES_CCODES, - "sbcs", "!0C, !1C, #!2m", 4), + "sbcs", "!0C, !1C, #!2m", 4, kFixupNone), ENCODING_MAP(kThumb2It, 0xbf00, kFmtBitBlt, 7, 4, kFmtBitBlt, 3, 0, kFmtModImm, -1, -1, kFmtUnused, -1, -1, IS_BINARY_OP | IS_IT | USES_CCODES, - "it:!1b", "!0c", 2), + "it:!1b", "!0c", 2, kFixupNone), ENCODING_MAP(kThumb2Fmstat, 0xeef1fa10, kFmtUnused, -1, -1, kFmtUnused, -1, -1, kFmtUnused, -1, -1, kFmtUnused, -1, -1, NO_OPERAND | SETS_CCODES, - "fmstat", "", 4), + "fmstat", "", 4, kFixupNone), ENCODING_MAP(kThumb2Vcmpd, 0xeeb40b40, kFmtDfp, 22, 12, kFmtDfp, 5, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1, IS_BINARY_OP | REG_USE01, - "vcmp.f64", "!0S, !1S", 4), + "vcmp.f64", "!0S, !1S", 4, kFixupNone), ENCODING_MAP(kThumb2Vcmps, 0xeeb40a40, kFmtSfp, 22, 12, kFmtSfp, 5, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1, IS_BINARY_OP | REG_USE01, - "vcmp.f32", "!0s, !1s", 4), + "vcmp.f32", "!0s, !1s", 4, kFixupNone), ENCODING_MAP(kThumb2LdrPcRel12, 0xf8df0000, kFmtBitBlt, 15, 12, kFmtBitBlt, 11, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0 | REG_USE_PC | IS_LOAD | NEEDS_FIXUP, - "ldr", "!0C, [r15pc, #!1d]", 4), + "ldr", "!0C, [r15pc, #!1d]", 4, kFixupLoad), ENCODING_MAP(kThumb2BCond, 0xf0008000, kFmtBrOffset, -1, -1, kFmtBitBlt, 25, 22, kFmtUnused, -1, -1, kFmtUnused, -1, -1, IS_BINARY_OP | IS_BRANCH | USES_CCODES | NEEDS_FIXUP, - "b!1c", "!0t", 4), + "b!1c", "!0t", 4, kFixupCondBranch), ENCODING_MAP(kThumb2Vmovd_RR, 0xeeb00b40, kFmtDfp, 22, 12, kFmtDfp, 5, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1, - "vmov.f64", "!0S, !1S", 4), + "vmov.f64", "!0S, !1S", 4, kFixupNone), ENCODING_MAP(kThumb2Vmovs_RR, 0xeeb00a40, kFmtSfp, 22, 12, kFmtSfp, 5, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1, - "vmov.f32", "!0s, !1s", 4), + "vmov.f32", "!0s, !1s", 4, kFixupNone), ENCODING_MAP(kThumb2Fmrs, 0xee100a10, kFmtBitBlt, 15, 12, kFmtSfp, 7, 16, kFmtUnused, -1, -1, kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1, - "fmrs", "!0C, !1s", 4), + "fmrs", "!0C, !1s", 4, kFixupNone), ENCODING_MAP(kThumb2Fmsr, 0xee000a10, kFmtSfp, 7, 16, kFmtBitBlt, 15, 12, kFmtUnused, -1, -1, kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1, - "fmsr", "!0s, !1C", 4), + "fmsr", "!0s, !1C", 4, kFixupNone), ENCODING_MAP(kThumb2Fmrrd, 0xec500b10, kFmtBitBlt, 15, 12, kFmtBitBlt, 19, 16, kFmtDfp, 5, 0, kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF01_USE2, - "fmrrd", "!0C, !1C, !2S", 4), + "fmrrd", "!0C, !1C, !2S", 4, kFixupNone), ENCODING_MAP(kThumb2Fmdrr, 0xec400b10, kFmtDfp, 5, 0, kFmtBitBlt, 15, 12, kFmtBitBlt, 19, 16, kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12, - "fmdrr", "!0S, !1C, !2C", 4), + "fmdrr", "!0S, !1C, !2C", 4, kFixupNone), ENCODING_MAP(kThumb2Vabsd, 0xeeb00bc0, kFmtDfp, 22, 12, kFmtDfp, 5, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1, - "vabs.f64", "!0S, !1S", 4), + "vabs.f64", "!0S, !1S", 4, kFixupNone), ENCODING_MAP(kThumb2Vabss, 0xeeb00ac0, kFmtSfp, 22, 12, kFmtSfp, 5, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1, - "vabs.f32", "!0s, !1s", 4), + "vabs.f32", "!0s, !1s", 4, kFixupNone), ENCODING_MAP(kThumb2Vnegd, 0xeeb10b40, kFmtDfp, 22, 12, kFmtDfp, 5, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1, - "vneg.f64", "!0S, !1S", 4), + "vneg.f64", "!0S, !1S", 4, kFixupNone), ENCODING_MAP(kThumb2Vnegs, 0xeeb10a40, kFmtSfp, 22, 12, kFmtSfp, 5, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1, - "vneg.f32", "!0s, !1s", 4), + "vneg.f32", "!0s, !1s", 4, kFixupNone), ENCODING_MAP(kThumb2Vmovs_IMM8, 0xeeb00a00, kFmtSfp, 22, 12, kFmtFPImm, 16, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0, - "vmov.f32", "!0s, #0x!1h", 4), + "vmov.f32", "!0s, #0x!1h", 4, kFixupNone), ENCODING_MAP(kThumb2Vmovd_IMM8, 0xeeb00b00, kFmtDfp, 22, 12, kFmtFPImm, 16, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0, - "vmov.f64", "!0S, #0x!1h", 4), + "vmov.f64", "!0S, #0x!1h", 4, kFixupNone), ENCODING_MAP(kThumb2Mla, 0xfb000000, kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0, kFmtBitBlt, 15, 12, IS_QUAD_OP | REG_DEF0 | REG_USE1 | REG_USE2 | REG_USE3, - "mla", "!0C, !1C, !2C, !3C", 4), + "mla", "!0C, !1C, !2C, !3C", 4, kFixupNone), ENCODING_MAP(kThumb2Umull, 0xfba00000, kFmtBitBlt, 15, 12, kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0, IS_QUAD_OP | REG_DEF0 | REG_DEF1 | REG_USE2 | REG_USE3, - "umull", "!0C, !1C, !2C, !3C", 4), + "umull", "!0C, !1C, !2C, !3C", 4, kFixupNone), ENCODING_MAP(kThumb2Ldrex, 0xe8500f00, kFmtBitBlt, 15, 12, kFmtBitBlt, 19, 16, kFmtBitBlt, 7, 0, kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1 | IS_LOAD, - "ldrex", "!0C, [!1C, #!2E]", 4), + "ldrex", "!0C, [!1C, #!2E]", 4, kFixupNone), ENCODING_MAP(kThumb2Strex, 0xe8400000, kFmtBitBlt, 11, 8, kFmtBitBlt, 15, 12, kFmtBitBlt, 19, 16, kFmtBitBlt, 7, 0, IS_QUAD_OP | REG_DEF0_USE12 | IS_STORE, - "strex", "!0C,!1C, [!2C, #!2E]", 4), + "strex", "!0C,!1C, [!2C, #!2E]", 4, kFixupNone), ENCODING_MAP(kThumb2Clrex, 0xf3bf8f2f, kFmtUnused, -1, -1, kFmtUnused, -1, -1, kFmtUnused, -1, -1, kFmtUnused, -1, -1, NO_OPERAND, - "clrex", "", 4), + "clrex", "", 4, kFixupNone), ENCODING_MAP(kThumb2Bfi, 0xf3600000, kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtShift5, -1, -1, kFmtBitBlt, 4, 0, IS_QUAD_OP | REG_DEF0_USE1, - "bfi", "!0C,!1C,#!2d,#!3d", 4), + "bfi", "!0C,!1C,#!2d,#!3d", 4, kFixupNone), ENCODING_MAP(kThumb2Bfc, 0xf36f0000, kFmtBitBlt, 11, 8, kFmtShift5, -1, -1, kFmtBitBlt, 4, 0, kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0, - "bfc", "!0C,#!1d,#!2d", 4), + "bfc", "!0C,#!1d,#!2d", 4, kFixupNone), ENCODING_MAP(kThumb2Dmb, 0xf3bf8f50, kFmtBitBlt, 3, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1, kFmtUnused, -1, -1, IS_UNARY_OP, - "dmb", "#!0B", 4), + "dmb", "#!0B", 4, kFixupNone), ENCODING_MAP(kThumb2LdrPcReln12, 0xf85f0000, kFmtBitBlt, 15, 12, kFmtBitBlt, 11, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0 | REG_USE_PC | IS_LOAD, - "ldr", "!0C, [r15pc, -#!1d]", 4), + "ldr", "!0C, [r15pc, -#!1d]", 4, kFixupNone), ENCODING_MAP(kThumb2Stm, 0xe9000000, kFmtBitBlt, 19, 16, kFmtBitBlt, 12, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1, IS_BINARY_OP | REG_USE0 | REG_USE_LIST1 | IS_STORE, - "stm", "!0C, ", 4), + "stm", "!0C, ", 4, kFixupNone), ENCODING_MAP(kThumbUndefined, 0xde00, kFmtUnused, -1, -1, kFmtUnused, -1, -1, kFmtUnused, -1, -1, kFmtUnused, -1, -1, NO_OPERAND, - "undefined", "", 2), + "undefined", "", 2, kFixupNone), // NOTE: vpop, vpush hard-encoded for s16+ reg list ENCODING_MAP(kThumb2VPopCS, 0xecbd8a00, kFmtBitBlt, 7, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1, kFmtUnused, -1, -1, IS_UNARY_OP | REG_DEF_SP | REG_USE_SP | REG_DEF_FPCS_LIST0 - | IS_LOAD, "vpop", "", 4), + | IS_LOAD, "vpop", "", 4, kFixupNone), ENCODING_MAP(kThumb2VPushCS, 0xed2d8a00, kFmtBitBlt, 7, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1, kFmtUnused, -1, -1, IS_UNARY_OP | REG_DEF_SP | REG_USE_SP | REG_USE_FPCS_LIST0 - | IS_STORE, "vpush", "", 4), + | IS_STORE, "vpush", "", 4, kFixupNone), ENCODING_MAP(kThumb2Vldms, 0xec900a00, kFmtBitBlt, 19, 16, kFmtSfp, 22, 12, kFmtBitBlt, 7, 0, kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_USE0 | REG_DEF_FPCS_LIST2 - | IS_LOAD, "vldms", "!0C, ", 4), + | IS_LOAD, "vldms", "!0C, ", 4, kFixupNone), ENCODING_MAP(kThumb2Vstms, 0xec800a00, kFmtBitBlt, 19, 16, kFmtSfp, 22, 12, kFmtBitBlt, 7, 0, kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_USE0 | REG_USE_FPCS_LIST2 - | IS_STORE, "vstms", "!0C, ", 4), + | IS_STORE, "vstms", "!0C, ", 4, kFixupNone), ENCODING_MAP(kThumb2BUncond, 0xf0009000, kFmtOff24, -1, -1, kFmtUnused, -1, -1, kFmtUnused, -1, -1, kFmtUnused, -1, -1, NO_OPERAND | IS_BRANCH, - "b", "!0t", 4), + "b", "!0t", 4, kFixupT2Branch), ENCODING_MAP(kThumb2MovImm16H, 0xf2c00000, kFmtBitBlt, 11, 8, kFmtImm16, -1, -1, kFmtUnused, -1, -1, kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0 | REG_USE0, - "movt", "!0C, #!1M", 4), + "movt", "!0C, #!1M", 4, kFixupNone), ENCODING_MAP(kThumb2AddPCR, 0x4487, kFmtBitBlt, 6, 3, kFmtUnused, -1, -1, kFmtUnused, -1, -1, kFmtUnused, -1, -1, - IS_UNARY_OP | REG_USE0 | IS_BRANCH, - "add", "rPC, !0C", 2), + IS_UNARY_OP | REG_USE0 | IS_BRANCH | NEEDS_FIXUP, + "add", "rPC, !0C", 2, kFixupLabel), ENCODING_MAP(kThumb2Adr, 0xf20f0000, kFmtBitBlt, 11, 8, kFmtImm12, -1, -1, kFmtUnused, -1, -1, kFmtUnused, -1, -1, /* Note: doesn't affect flags */ IS_TERTIARY_OP | REG_DEF0 | NEEDS_FIXUP, - "adr", "!0C,#!1d", 4), + "adr", "!0C,#!1d", 4, kFixupAdr), ENCODING_MAP(kThumb2MovImm16LST, 0xf2400000, kFmtBitBlt, 11, 8, kFmtImm16, -1, -1, kFmtUnused, -1, -1, kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0 | NEEDS_FIXUP, - "mov", "!0C, #!1M", 4), + "mov", "!0C, #!1M", 4, kFixupMovImmLST), ENCODING_MAP(kThumb2MovImm16HST, 0xf2c00000, kFmtBitBlt, 11, 8, kFmtImm16, -1, -1, kFmtUnused, -1, -1, kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0 | REG_USE0 | NEEDS_FIXUP, - "movt", "!0C, #!1M", 4), + "movt", "!0C, #!1M", 4, kFixupMovImmHST), ENCODING_MAP(kThumb2LdmiaWB, 0xe8b00000, kFmtBitBlt, 19, 16, kFmtBitBlt, 15, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE0 | REG_DEF_LIST1 | IS_LOAD, - "ldmia", "!0C!!, ", 4), + "ldmia", "!0C!!, ", 4, kFixupNone), ENCODING_MAP(kThumb2SubsRRI12, 0xf1b00000, kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtImm12, -1, -1, kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1 | SETS_CCODES, - "subs", "!0C,!1C,#!2d", 4), + "subs", "!0C,!1C,#!2d", 4, kFixupNone), ENCODING_MAP(kThumb2OrrRRRs, 0xea500000, kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0, kFmtShift, -1, -1, IS_QUAD_OP | REG_DEF0_USE12 | SETS_CCODES, - "orrs", "!0C, !1C, !2C!3H", 4), + "orrs", "!0C, !1C, !2C!3H", 4, kFixupNone), ENCODING_MAP(kThumb2Push1, 0xf84d0d04, kFmtBitBlt, 15, 12, kFmtUnused, -1, -1, kFmtUnused, -1, -1, kFmtUnused, -1, -1, IS_UNARY_OP | REG_DEF_SP | REG_USE_SP | REG_USE0 - | IS_STORE, "push1", "!0C", 4), + | IS_STORE, "push1", "!0C", 4, kFixupNone), ENCODING_MAP(kThumb2Pop1, 0xf85d0b04, kFmtBitBlt, 15, 12, kFmtUnused, -1, -1, kFmtUnused, -1, -1, kFmtUnused, -1, -1, IS_UNARY_OP | REG_DEF_SP | REG_USE_SP | REG_DEF0 - | IS_LOAD, "pop1", "!0C", 4), + | IS_LOAD, "pop1", "!0C", 4, kFixupNone), ENCODING_MAP(kThumb2RsubRRR, 0xebd00000, /* setflags encoding */ kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0, kFmtShift, -1, -1, IS_QUAD_OP | REG_DEF0_USE12 | SETS_CCODES, - "rsbs", "!0C, !1C, !2C!3H", 4), + "rsbs", "!0C, !1C, !2C!3H", 4, kFixupNone), ENCODING_MAP(kThumb2Smull, 0xfb800000, kFmtBitBlt, 15, 12, kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0, IS_QUAD_OP | REG_DEF0 | REG_DEF1 | REG_USE2 | REG_USE3, - "smull", "!0C, !1C, !2C, !3C", 4), + "smull", "!0C, !1C, !2C, !3C", 4, kFixupNone), ENCODING_MAP(kThumb2LdrdPcRel8, 0xe9df0000, kFmtBitBlt, 15, 12, kFmtBitBlt, 11, 8, kFmtBitBlt, 7, 0, kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0 | REG_DEF1 | REG_USE_PC | IS_LOAD | NEEDS_FIXUP, - "ldrd", "!0C, !1C, [pc, #!2E]", 4), + "ldrd", "!0C, !1C, [pc, #!2E]", 4, kFixupLoad), ENCODING_MAP(kThumb2LdrdI8, 0xe9d00000, kFmtBitBlt, 15, 12, kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtBitBlt, 7, 0, IS_QUAD_OP | REG_DEF0 | REG_DEF1 | REG_USE2 | IS_LOAD, - "ldrd", "!0C, !1C, [!2C, #!3E]", 4), + "ldrd", "!0C, !1C, [!2C, #!3E]", 4, kFixupNone), ENCODING_MAP(kThumb2StrdI8, 0xe9c00000, kFmtBitBlt, 15, 12, kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtBitBlt, 7, 0, IS_QUAD_OP | REG_USE0 | REG_USE1 | REG_USE2 | IS_STORE, - "strd", "!0C, !1C, [!2C, #!3E]", 4), + "strd", "!0C, !1C, [!2C, #!3E]", 4, kFixupNone), }; +// new_lir replaces orig_lir in the pcrel_fixup list. +void ArmMir2Lir::ReplaceFixup(LIR* prev_lir, LIR* orig_lir, LIR* new_lir) { + new_lir->u.a.pcrel_next = orig_lir->u.a.pcrel_next; + if (UNLIKELY(prev_lir == NULL)) { + first_fixup_ = new_lir; + } else { + prev_lir->u.a.pcrel_next = new_lir; + } + orig_lir->flags.fixup = kFixupNone; +} + +// new_lir is inserted before orig_lir in the pcrel_fixup list. +void ArmMir2Lir::InsertFixupBefore(LIR* prev_lir, LIR* orig_lir, LIR* new_lir) { + new_lir->u.a.pcrel_next = orig_lir; + if (UNLIKELY(prev_lir == NULL)) { + first_fixup_ = new_lir; + } else { + DCHECK(prev_lir->u.a.pcrel_next == orig_lir); + prev_lir->u.a.pcrel_next = new_lir; + } +} + /* * The fake NOP of moving r0 to r0 actually will incur data stalls if r0 is * not ready. Since r5FP is not updated often, it is less likely to @@ -997,404 +1019,640 @@ const ArmEncodingMap ArmMir2Lir::EncodingMap[kArmLast] = { */ #define PADDING_MOV_R5_R5 0x1C2D -/* - * Assemble the LIR into binary instruction format. Note that we may - * discover that pc-relative displacements may not fit the selected - * instruction. - */ -AssemblerStatus ArmMir2Lir::AssembleInstructions(uintptr_t start_addr) { - LIR* lir; - AssemblerStatus res = kSuccess; // Assume success - - for (lir = first_lir_insn_; lir != NULL; lir = NEXT_LIR(lir)) { - if (lir->opcode < 0) { - /* 1 means padding is needed */ - if ((lir->opcode == kPseudoPseudoAlign4) && (lir->operands[0] == 1)) { - code_buffer_.push_back(PADDING_MOV_R5_R5 & 0xFF); - code_buffer_.push_back((PADDING_MOV_R5_R5 >> 8) & 0xFF); +void ArmMir2Lir::EncodeLIR(LIR* lir) { + int opcode = lir->opcode; + if (opcode < 0) { + if (UNLIKELY(opcode == kPseudoPseudoAlign4)) { + // Note: size for this opcode will be either 0 or 2 depending on final alignment. + lir->u.a.bytes[0] = (PADDING_MOV_R5_R5 & 0xff); + lir->u.a.bytes[1] = ((PADDING_MOV_R5_R5 >> 8) & 0xff); + lir->flags.size = (lir->offset & 0x2); + } + } else if (LIKELY(!lir->flags.is_nop)) { + const ArmEncodingMap *encoder = &EncodingMap[lir->opcode]; + uint32_t bits = encoder->skeleton; + int i; + for (i = 0; i < 4; i++) { + uint32_t operand; + uint32_t value; + operand = lir->operands[i]; + ArmEncodingKind kind = encoder->field_loc[i].kind; + if (LIKELY(kind == kFmtBitBlt)) { + value = (operand << encoder->field_loc[i].start) & + ((1 << (encoder->field_loc[i].end + 1)) - 1); + bits |= value; + } else { + switch (encoder->field_loc[i].kind) { + case kFmtSkip: + break; // Nothing to do, but continue to next. + case kFmtUnused: + i = 4; // Done, break out of the enclosing loop. + break; + case kFmtFPImm: + value = ((operand & 0xF0) >> 4) << encoder->field_loc[i].end; + value |= (operand & 0x0F) << encoder->field_loc[i].start; + bits |= value; + break; + case kFmtBrOffset: + value = ((operand & 0x80000) >> 19) << 26; + value |= ((operand & 0x40000) >> 18) << 11; + value |= ((operand & 0x20000) >> 17) << 13; + value |= ((operand & 0x1f800) >> 11) << 16; + value |= (operand & 0x007ff); + bits |= value; + break; + case kFmtShift5: + value = ((operand & 0x1c) >> 2) << 12; + value |= (operand & 0x03) << 6; + bits |= value; + break; + case kFmtShift: + value = ((operand & 0x70) >> 4) << 12; + value |= (operand & 0x0f) << 4; + bits |= value; + break; + case kFmtBWidth: + value = operand - 1; + bits |= value; + break; + case kFmtLsb: + value = ((operand & 0x1c) >> 2) << 12; + value |= (operand & 0x03) << 6; + bits |= value; + break; + case kFmtImm6: + value = ((operand & 0x20) >> 5) << 9; + value |= (operand & 0x1f) << 3; + bits |= value; + break; + case kFmtDfp: { + DCHECK(ARM_DOUBLEREG(operand)); + DCHECK_EQ((operand & 0x1), 0U); + int reg_name = (operand & ARM_FP_REG_MASK) >> 1; + /* Snag the 1-bit slice and position it */ + value = ((reg_name & 0x10) >> 4) << encoder->field_loc[i].end; + /* Extract and position the 4-bit slice */ + value |= (reg_name & 0x0f) << encoder->field_loc[i].start; + bits |= value; + break; + } + case kFmtSfp: + DCHECK(ARM_SINGLEREG(operand)); + /* Snag the 1-bit slice and position it */ + value = (operand & 0x1) << encoder->field_loc[i].end; + /* Extract and position the 4-bit slice */ + value |= ((operand & 0x1e) >> 1) << encoder->field_loc[i].start; + bits |= value; + break; + case kFmtImm12: + case kFmtModImm: + value = ((operand & 0x800) >> 11) << 26; + value |= ((operand & 0x700) >> 8) << 12; + value |= operand & 0x0ff; + bits |= value; + break; + case kFmtImm16: + value = ((operand & 0x0800) >> 11) << 26; + value |= ((operand & 0xf000) >> 12) << 16; + value |= ((operand & 0x0700) >> 8) << 12; + value |= operand & 0x0ff; + bits |= value; + break; + case kFmtOff24: { + uint32_t signbit = (operand >> 31) & 0x1; + uint32_t i1 = (operand >> 22) & 0x1; + uint32_t i2 = (operand >> 21) & 0x1; + uint32_t imm10 = (operand >> 11) & 0x03ff; + uint32_t imm11 = operand & 0x07ff; + uint32_t j1 = (i1 ^ signbit) ? 0 : 1; + uint32_t j2 = (i2 ^ signbit) ? 0 : 1; + value = (signbit << 26) | (j1 << 13) | (j2 << 11) | (imm10 << 16) | + imm11; + bits |= value; + } + break; + default: + LOG(FATAL) << "Bad fmt:" << encoder->field_loc[i].kind; + } } - continue; } - - if (lir->flags.is_nop) { - continue; + if (encoder->size == 4) { + lir->u.a.bytes[0] = ((bits >> 16) & 0xff); + lir->u.a.bytes[1] = ((bits >> 24) & 0xff); + lir->u.a.bytes[2] = (bits & 0xff); + lir->u.a.bytes[3] = ((bits >> 8) & 0xff); + } else { + DCHECK_EQ(encoder->size, 2); + lir->u.a.bytes[0] = (bits & 0xff); + lir->u.a.bytes[1] = ((bits >> 8) & 0xff); } + lir->flags.size = encoder->size; + } +} - /* - * For PC-relative displacements we won't know if the - * selected instruction will work until late (i.e. - now). - * If something doesn't fit, we must replace the short-form - * operation with a longer-form one. Note, though, that this - * can change code we've already processed, so we'll need to - * re-calculate offsets and restart. To limit the number of - * restarts, the entire list will be scanned and patched. - * Of course, the patching itself may cause new overflows so this - * is an iterative process. - */ - if (lir->flags.pcRelFixup) { - if (lir->opcode == kThumbLdrPcRel || - lir->opcode == kThumb2LdrPcRel12 || - lir->opcode == kThumbAddPcRel || - lir->opcode == kThumb2LdrdPcRel8 || - ((lir->opcode == kThumb2Vldrd) && (lir->operands[1] == r15pc)) || - ((lir->opcode == kThumb2Vldrs) && (lir->operands[1] == r15pc))) { - /* - * PC-relative loads are mostly used to load immediates - * that are too large to materialize directly in one shot. - * However, if the load displacement exceeds the limit, - * we revert to a multiple-instruction materialization sequence. - */ - LIR *lir_target = lir->target; - uintptr_t pc = (lir->offset + 4) & ~3; - uintptr_t target = lir_target->offset; - int delta = target - pc; - if (delta & 0x3) { - LOG(FATAL) << "PC-rel offset not multiple of 4: " << delta; - } - // First, a sanity check for cases we shouldn't see now - if (((lir->opcode == kThumbAddPcRel) && (delta > 1020)) || - ((lir->opcode == kThumbLdrPcRel) && (delta > 1020))) { - // Shouldn't happen in current codegen. - LOG(FATAL) << "Unexpected pc-rel offset " << delta; - } - // Now, check for the difficult cases - if (((lir->opcode == kThumb2LdrPcRel12) && (delta > 4091)) || - ((lir->opcode == kThumb2LdrdPcRel8) && (delta > 1020)) || - ((lir->opcode == kThumb2Vldrs) && (delta > 1020)) || - ((lir->opcode == kThumb2Vldrd) && (delta > 1020))) { +// Assemble the LIR into binary instruction format. +void ArmMir2Lir::AssembleLIR() { + LIR* lir; + LIR* prev_lir; + int assembler_retries = 0; + int starting_offset = EncodeRange(first_lir_insn_, last_lir_insn_, 0); + data_offset_ = (starting_offset + 0x3) & ~0x3; + int offset_adjustment; + AssignDataOffsets(); + + /* + * Note: generation must be 1 on first pass (to distinguish from initialized state of 0 for non-visited nodes). + * Start at zero here, and bit will be flipped to 1 on entry to the loop. + */ + int generation = 0; + while (true) { + offset_adjustment = 0; + AssemblerStatus res = kSuccess; // Assume success + generation ^= 1; + // Note: nodes requring possible fixup linked in ascending order. + lir = first_fixup_; + prev_lir = NULL; + while (lir != NULL) { + /* + * NOTE: the lir being considered here will be encoded following the switch (so long as + * we're not in a retry situation). However, any new non-pc_rel instructions inserted + * due to retry must be explicitly encoded at the time of insertion. Note that + * inserted instructions don't need use/def flags, but do need size and pc-rel status + * properly updated. + */ + lir->offset += offset_adjustment; + // During pass, allows us to tell whether a node has been updated with offset_adjustment yet. + lir->flags.generation = generation; + switch (static_cast(lir->flags.fixup)) { + case kFixupLabel: + case kFixupNone: + break; + case kFixupVLoad: + if (lir->operands[1] != r15pc) { + break; + } + // NOTE: intentional fallthrough. + case kFixupLoad: { /* - * Note: because rARM_LR may be used to fix up out-of-range - * vldrs/vldrd we include REG_DEF_LR in the resource - * masks for these instructions. + * PC-relative loads are mostly used to load immediates + * that are too large to materialize directly in one shot. + * However, if the load displacement exceeds the limit, + * we revert to a multiple-instruction materialization sequence. */ - int base_reg = ((lir->opcode == kThumb2LdrdPcRel8) || (lir->opcode == kThumb2LdrPcRel12)) - ? lir->operands[0] : rARM_LR; + LIR *lir_target = lir->target; + uintptr_t pc = (lir->offset + 4) & ~3; + uintptr_t target = lir_target->offset + + ((lir_target->flags.generation == lir->flags.generation) ? 0 : offset_adjustment); + int delta = target - pc; + if (res != kSuccess) { + /* + * In this case, we're just estimating and will do it again for real. Ensure offset + * is legal. + */ + delta &= ~0x3; + } + DCHECK_EQ((delta & 0x3), 0); + // First, a sanity check for cases we shouldn't see now + if (kIsDebugBuild && (((lir->opcode == kThumbAddPcRel) && (delta > 1020)) || + ((lir->opcode == kThumbLdrPcRel) && (delta > 1020)))) { + // Shouldn't happen in current codegen. + LOG(FATAL) << "Unexpected pc-rel offset " << delta; + } + // Now, check for the difficult cases + if (((lir->opcode == kThumb2LdrPcRel12) && (delta > 4091)) || + ((lir->opcode == kThumb2LdrdPcRel8) && (delta > 1020)) || + ((lir->opcode == kThumb2Vldrs) && (delta > 1020)) || + ((lir->opcode == kThumb2Vldrd) && (delta > 1020))) { + /* + * Note: The reason vldrs/vldrd include rARM_LR in their use/def masks is that we + * sometimes have to use it to fix up out-of-range accesses. This is where that + * happens. + */ + int base_reg = ((lir->opcode == kThumb2LdrdPcRel8) || + (lir->opcode == kThumb2LdrPcRel12)) ? lir->operands[0] : rARM_LR; - // Add new Adr to generate the address. - LIR* new_adr = RawLIR(lir->dalvik_offset, kThumb2Adr, - base_reg, 0, 0, 0, 0, lir->target); - InsertLIRBefore(lir, new_adr); + // Add new Adr to generate the address. + LIR* new_adr = RawLIR(lir->dalvik_offset, kThumb2Adr, + base_reg, 0, 0, 0, 0, lir->target); + new_adr->offset = lir->offset; + new_adr->flags.fixup = kFixupAdr; + new_adr->flags.size = EncodingMap[kThumb2Adr].size; + InsertLIRBefore(lir, new_adr); + lir->offset += new_adr->flags.size; + offset_adjustment += new_adr->flags.size; - // Convert to normal load. - if (lir->opcode == kThumb2LdrPcRel12) { - lir->opcode = kThumb2LdrRRI12; - } else if (lir->opcode == kThumb2LdrdPcRel8) { - lir->opcode = kThumb2LdrdI8; - } - // Change the load to be relative to the new Adr base. - if (lir->opcode == kThumb2LdrdI8) { - lir->operands[3] = 0; - lir->operands[2] = base_reg; + // lir no longer pcrel, unlink and link in new_adr. + ReplaceFixup(prev_lir, lir, new_adr); + + // Convert to normal load. + offset_adjustment -= lir->flags.size; + if (lir->opcode == kThumb2LdrPcRel12) { + lir->opcode = kThumb2LdrRRI12; + } else if (lir->opcode == kThumb2LdrdPcRel8) { + lir->opcode = kThumb2LdrdI8; + } + lir->flags.size = EncodingMap[lir->opcode].size; + offset_adjustment += lir->flags.size; + // Change the load to be relative to the new Adr base. + if (lir->opcode == kThumb2LdrdI8) { + lir->operands[3] = 0; + lir->operands[2] = base_reg; + } else { + lir->operands[2] = 0; + lir->operands[1] = base_reg; + } + // Must redo encoding here - won't ever revisit this node. + EncodeLIR(lir); + prev_lir = new_adr; // Continue scan with new_adr; + lir = new_adr->u.a.pcrel_next; + res = kRetryAll; + continue; } else { - lir->operands[2] = 0; - lir->operands[1] = base_reg; + if ((lir->opcode == kThumb2Vldrs) || + (lir->opcode == kThumb2Vldrd) || + (lir->opcode == kThumb2LdrdPcRel8)) { + lir->operands[2] = delta >> 2; + } else { + lir->operands[1] = (lir->opcode == kThumb2LdrPcRel12) ? delta : + delta >> 2; + } } - SetupResourceMasks(lir); - res = kRetryAll; - } else { - if ((lir->opcode == kThumb2Vldrs) || - (lir->opcode == kThumb2Vldrd) || - (lir->opcode == kThumb2LdrdPcRel8)) { - lir->operands[2] = delta >> 2; + break; + } + case kFixupCBxZ: { + LIR *target_lir = lir->target; + uintptr_t pc = lir->offset + 4; + uintptr_t target = target_lir->offset + + ((target_lir->flags.generation == lir->flags.generation) ? 0 : offset_adjustment); + int delta = target - pc; + if (delta > 126 || delta < 0) { + /* + * Convert to cmp rx,#0 / b[eq/ne] tgt pair + * Make new branch instruction and insert after + */ + LIR* new_inst = + RawLIR(lir->dalvik_offset, kThumbBCond, 0, + (lir->opcode == kThumb2Cbz) ? kArmCondEq : kArmCondNe, + 0, 0, 0, lir->target); + InsertLIRAfter(lir, new_inst); + + /* Convert the cb[n]z to a cmp rx, #0 ] */ + // Subtract the old size. + offset_adjustment -= lir->flags.size; + lir->opcode = kThumbCmpRI8; + /* operand[0] is src1 in both cb[n]z & CmpRI8 */ + lir->operands[1] = 0; + lir->target = 0; + EncodeLIR(lir); // NOTE: sets flags.size. + // Add back the new size. + DCHECK_EQ(lir->flags.size, static_cast(EncodingMap[lir->opcode].size)); + offset_adjustment += lir->flags.size; + // Set up the new following inst. + new_inst->offset = lir->offset + lir->flags.size; + new_inst->flags.fixup = kFixupCondBranch; + new_inst->flags.size = EncodingMap[new_inst->opcode].size; + offset_adjustment += new_inst->flags.size; + + // lir no longer pcrel, unlink and link in new_inst. + ReplaceFixup(prev_lir, lir, new_inst); + prev_lir = new_inst; // Continue with the new instruction. + lir = new_inst->u.a.pcrel_next; + res = kRetryAll; + continue; } else { - lir->operands[1] = (lir->opcode == kThumb2LdrPcRel12) ? delta : - delta >> 2; + lir->operands[1] = delta >> 1; } + break; } - } else if (lir->opcode == kThumb2Cbnz || lir->opcode == kThumb2Cbz) { - LIR *target_lir = lir->target; - uintptr_t pc = lir->offset + 4; - uintptr_t target = target_lir->offset; - int delta = target - pc; - if (delta > 126 || delta < 0) { - /* - * Convert to cmp rx,#0 / b[eq/ne] tgt pair - * Make new branch instruction and insert after - */ - LIR* new_inst = - RawLIR(lir->dalvik_offset, kThumbBCond, 0, - (lir->opcode == kThumb2Cbz) ? kArmCondEq : kArmCondNe, - 0, 0, 0, lir->target); - InsertLIRAfter(lir, new_inst); - /* Convert the cb[n]z to a cmp rx, #0 ] */ - lir->opcode = kThumbCmpRI8; - /* operand[0] is src1 in both cb[n]z & CmpRI8 */ - lir->operands[1] = 0; - lir->target = 0; - SetupResourceMasks(lir); - /* - * Because we just added this new instruction after the current one, - * advance lir so that this new instruction won't be checked for displacement - * overflow until the next pass (when its base offset will be properly established). - */ - lir = new_inst; - res = kRetryAll; - } else { - lir->operands[1] = delta >> 1; - } - } else if (lir->opcode == kThumb2Push || lir->opcode == kThumb2Pop) { - if (__builtin_popcount(lir->operands[0]) == 1) { - /* - * The standard push/pop multiple instruction - * requires at least two registers in the list. - * If we've got just one, switch to the single-reg - * encoding. - */ - lir->opcode = (lir->opcode == kThumb2Push) ? kThumb2Push1 : - kThumb2Pop1; - int reg = 0; - while (lir->operands[0]) { - if (lir->operands[0] & 0x1) { - break; - } else { - reg++; - lir->operands[0] >>= 1; + case kFixupPushPop: { + if (__builtin_popcount(lir->operands[0]) == 1) { + /* + * The standard push/pop multiple instruction + * requires at least two registers in the list. + * If we've got just one, switch to the single-reg + * encoding. + */ + lir->opcode = (lir->opcode == kThumb2Push) ? kThumb2Push1 : + kThumb2Pop1; + int reg = 0; + while (lir->operands[0]) { + if (lir->operands[0] & 0x1) { + break; + } else { + reg++; + lir->operands[0] >>= 1; + } } + lir->operands[0] = reg; + // This won't change again, don't bother unlinking, just reset fixup kind + lir->flags.fixup = kFixupNone; } - lir->operands[0] = reg; - SetupResourceMasks(lir); - res = kRetryAll; - } - } else if (lir->opcode == kThumbBCond || lir->opcode == kThumb2BCond) { - LIR *target_lir = lir->target; - int delta = 0; - DCHECK(target_lir); - uintptr_t pc = lir->offset + 4; - uintptr_t target = target_lir->offset; - delta = target - pc; - if ((lir->opcode == kThumbBCond) && (delta > 254 || delta < -256)) { - lir->opcode = kThumb2BCond; - SetupResourceMasks(lir); - res = kRetryAll; + break; } - lir->operands[0] = delta >> 1; - } else if (lir->opcode == kThumb2BUncond) { - LIR *target_lir = lir->target; - uintptr_t pc = lir->offset + 4; - uintptr_t target = target_lir->offset; - int delta = target - pc; - lir->operands[0] = delta >> 1; - if (!(cu_->disable_opt & (1 << kSafeOptimizations)) && - lir->operands[0] == 0) { // Useless branch - NopLIR(lir); - res = kRetryAll; + case kFixupCondBranch: { + LIR *target_lir = lir->target; + int delta = 0; + DCHECK(target_lir); + uintptr_t pc = lir->offset + 4; + uintptr_t target = target_lir->offset + + ((target_lir->flags.generation == lir->flags.generation) ? 0 : offset_adjustment); + delta = target - pc; + if ((lir->opcode == kThumbBCond) && (delta > 254 || delta < -256)) { + offset_adjustment -= lir->flags.size; + lir->opcode = kThumb2BCond; + lir->flags.size = EncodingMap[lir->opcode].size; + // Fixup kind remains the same. + offset_adjustment += lir->flags.size; + res = kRetryAll; + } + lir->operands[0] = delta >> 1; + break; } - } else if (lir->opcode == kThumbBUncond) { - LIR *target_lir = lir->target; - uintptr_t pc = lir->offset + 4; - uintptr_t target = target_lir->offset; - int delta = target - pc; - if (delta > 2046 || delta < -2048) { - // Convert to Thumb2BCond w/ kArmCondAl - lir->opcode = kThumb2BUncond; - lir->operands[0] = 0; - SetupResourceMasks(lir); - res = kRetryAll; - } else { + case kFixupT2Branch: { + LIR *target_lir = lir->target; + uintptr_t pc = lir->offset + 4; + uintptr_t target = target_lir->offset + + ((target_lir->flags.generation == lir->flags.generation) ? 0 : offset_adjustment); + int delta = target - pc; lir->operands[0] = delta >> 1; - if (!(cu_->disable_opt & (1 << kSafeOptimizations)) && - lir->operands[0] == -1) { // Useless branch - NopLIR(lir); + if (!(cu_->disable_opt & (1 << kSafeOptimizations)) && lir->operands[0] == 0) { + // Useless branch + offset_adjustment -= lir->flags.size; + lir->flags.is_nop = true; + // Don't unlink - just set to do-nothing. + lir->flags.fixup = kFixupNone; res = kRetryAll; } + break; } - } else if (lir->opcode == kThumbBlx1) { - DCHECK(NEXT_LIR(lir)->opcode == kThumbBlx2); - /* cur_pc is Thumb */ - uintptr_t cur_pc = (start_addr + lir->offset + 4) & ~3; - uintptr_t target = lir->operands[1]; - - /* Match bit[1] in target with base */ - if (cur_pc & 0x2) { - target |= 0x2; + case kFixupT1Branch: { + LIR *target_lir = lir->target; + uintptr_t pc = lir->offset + 4; + uintptr_t target = target_lir->offset + + ((target_lir->flags.generation == lir->flags.generation) ? 0 : offset_adjustment); + int delta = target - pc; + if (delta > 2046 || delta < -2048) { + // Convert to Thumb2BCond w/ kArmCondAl + offset_adjustment -= lir->flags.size; + lir->opcode = kThumb2BUncond; + lir->operands[0] = 0; + lir->flags.size = EncodingMap[lir->opcode].size; + lir->flags.fixup = kFixupT2Branch; + offset_adjustment += lir->flags.size; + res = kRetryAll; + } else { + lir->operands[0] = delta >> 1; + if (!(cu_->disable_opt & (1 << kSafeOptimizations)) && lir->operands[0] == -1) { + // Useless branch + offset_adjustment -= lir->flags.size; + lir->flags.is_nop = true; + // Don't unlink - just set to do-nothing. + lir->flags.fixup = kFixupNone; + res = kRetryAll; + } + } + break; } - int delta = target - cur_pc; - DCHECK((delta >= -(1<<22)) && (delta <= ((1<<22)-2))); + case kFixupBlx1: { + DCHECK(NEXT_LIR(lir)->opcode == kThumbBlx2); + /* cur_pc is Thumb */ + uintptr_t cur_pc = (lir->offset + 4) & ~3; + uintptr_t target = lir->operands[1]; - lir->operands[0] = (delta >> 12) & 0x7ff; - NEXT_LIR(lir)->operands[0] = (delta>> 1) & 0x7ff; - } else if (lir->opcode == kThumbBl1) { - DCHECK(NEXT_LIR(lir)->opcode == kThumbBl2); - /* Both cur_pc and target are Thumb */ - uintptr_t cur_pc = start_addr + lir->offset + 4; - uintptr_t target = lir->operands[1]; + /* Match bit[1] in target with base */ + if (cur_pc & 0x2) { + target |= 0x2; + } + int delta = target - cur_pc; + DCHECK((delta >= -(1<<22)) && (delta <= ((1<<22)-2))); - int delta = target - cur_pc; - DCHECK((delta >= -(1<<22)) && (delta <= ((1<<22)-2))); + lir->operands[0] = (delta >> 12) & 0x7ff; + NEXT_LIR(lir)->operands[0] = (delta>> 1) & 0x7ff; + break; + } + case kFixupBl1: { + DCHECK(NEXT_LIR(lir)->opcode == kThumbBl2); + /* Both cur_pc and target are Thumb */ + uintptr_t cur_pc = lir->offset + 4; + uintptr_t target = lir->operands[1]; - lir->operands[0] = (delta >> 12) & 0x7ff; - NEXT_LIR(lir)->operands[0] = (delta>> 1) & 0x7ff; - } else if (lir->opcode == kThumb2Adr) { - SwitchTable *tab_rec = reinterpret_cast(lir->operands[2]); - LIR* target = lir->target; - int target_disp = tab_rec ? tab_rec->offset - : target->offset; - int disp = target_disp - ((lir->offset + 4) & ~3); - if (disp < 4096) { - lir->operands[1] = disp; - } else { - // convert to ldimm16l, ldimm16h, add tgt, pc, operands[0] - // TUNING: if this case fires often, it can be improved. Not expected to be common. - LIR *new_mov16L = - RawLIR(lir->dalvik_offset, kThumb2MovImm16LST, - lir->operands[0], 0, reinterpret_cast(lir), - reinterpret_cast(tab_rec), 0, lir->target); - InsertLIRBefore(lir, new_mov16L); - LIR *new_mov16H = - RawLIR(lir->dalvik_offset, kThumb2MovImm16HST, - lir->operands[0], 0, reinterpret_cast(lir), - reinterpret_cast(tab_rec), 0, lir->target); - InsertLIRBefore(lir, new_mov16H); - if (ARM_LOWREG(lir->operands[0])) { - lir->opcode = kThumbAddRRLH; + int delta = target - cur_pc; + DCHECK((delta >= -(1<<22)) && (delta <= ((1<<22)-2))); + + lir->operands[0] = (delta >> 12) & 0x7ff; + NEXT_LIR(lir)->operands[0] = (delta>> 1) & 0x7ff; + break; + } + case kFixupAdr: { + SwitchTable *tab_rec = reinterpret_cast(lir->operands[2]); + LIR* target = lir->target; + int target_disp = (tab_rec != NULL) ? tab_rec->offset + offset_adjustment + : target->offset + ((target->flags.generation == lir->flags.generation) ? 0 : offset_adjustment); + int disp = target_disp - ((lir->offset + 4) & ~3); + if (disp < 4096) { + lir->operands[1] = disp; } else { - lir->opcode = kThumbAddRRHH; + // convert to ldimm16l, ldimm16h, add tgt, pc, operands[0] + // TUNING: if this case fires often, it can be improved. Not expected to be common. + LIR *new_mov16L = + RawLIR(lir->dalvik_offset, kThumb2MovImm16LST, + lir->operands[0], 0, reinterpret_cast(lir), + reinterpret_cast(tab_rec), 0, lir->target); + new_mov16L->flags.size = EncodingMap[new_mov16L->opcode].size; + new_mov16L->flags.fixup = kFixupMovImmLST; + new_mov16L->offset = lir->offset; + // Link the new instruction, retaining lir. + InsertLIRBefore(lir, new_mov16L); + lir->offset += new_mov16L->flags.size; + offset_adjustment += new_mov16L->flags.size; + InsertFixupBefore(prev_lir, lir, new_mov16L); + prev_lir = new_mov16L; // Now we've got a new prev. + + LIR *new_mov16H = + RawLIR(lir->dalvik_offset, kThumb2MovImm16HST, + lir->operands[0], 0, reinterpret_cast(lir), + reinterpret_cast(tab_rec), 0, lir->target); + new_mov16H->flags.size = EncodingMap[new_mov16H->opcode].size; + new_mov16H->flags.fixup = kFixupMovImmHST; + new_mov16H->offset = lir->offset; + // Link the new instruction, retaining lir. + InsertLIRBefore(lir, new_mov16H); + lir->offset += new_mov16H->flags.size; + offset_adjustment += new_mov16H->flags.size; + InsertFixupBefore(prev_lir, lir, new_mov16H); + prev_lir = new_mov16H; // Now we've got a new prev. + + offset_adjustment -= lir->flags.size; + if (ARM_LOWREG(lir->operands[0])) { + lir->opcode = kThumbAddRRLH; + } else { + lir->opcode = kThumbAddRRHH; + } + lir->operands[1] = rARM_PC; + lir->flags.size = EncodingMap[lir->opcode].size; + offset_adjustment += lir->flags.size; + // Must stay in fixup list and have offset updated; will be used by LST/HSP pair. + lir->flags.fixup = kFixupNone; + res = kRetryAll; } - lir->operands[1] = rARM_PC; - SetupResourceMasks(lir); - res = kRetryAll; - } - } else if (lir->opcode == kThumb2MovImm16LST) { - // operands[1] should hold disp, [2] has add, [3] has tab_rec - LIR *addPCInst = reinterpret_cast(lir->operands[2]); - SwitchTable *tab_rec = reinterpret_cast(lir->operands[3]); - // If tab_rec is null, this is a literal load. Use target - LIR* target = lir->target; - int target_disp = tab_rec ? tab_rec->offset : target->offset; - lir->operands[1] = (target_disp - (addPCInst->offset + 4)) & 0xffff; - } else if (lir->opcode == kThumb2MovImm16HST) { - // operands[1] should hold disp, [2] has add, [3] has tab_rec - LIR *addPCInst = reinterpret_cast(lir->operands[2]); - SwitchTable *tab_rec = reinterpret_cast(lir->operands[3]); - // If tab_rec is null, this is a literal load. Use target - LIR* target = lir->target; - int target_disp = tab_rec ? tab_rec->offset : target->offset; - lir->operands[1] = - ((target_disp - (addPCInst->offset + 4)) >> 16) & 0xffff; - } - } - /* - * If one of the pc-relative instructions expanded we'll have - * to make another pass. Don't bother to fully assemble the - * instruction. - */ - if (res != kSuccess) { - continue; - } - const ArmEncodingMap *encoder = &EncodingMap[lir->opcode]; - uint32_t bits = encoder->skeleton; - int i; - for (i = 0; i < 4; i++) { - uint32_t operand; - uint32_t value; - operand = lir->operands[i]; - switch (encoder->field_loc[i].kind) { - case kFmtUnused: - break; - case kFmtFPImm: - value = ((operand & 0xF0) >> 4) << encoder->field_loc[i].end; - value |= (operand & 0x0F) << encoder->field_loc[i].start; - bits |= value; - break; - case kFmtBrOffset: - value = ((operand & 0x80000) >> 19) << 26; - value |= ((operand & 0x40000) >> 18) << 11; - value |= ((operand & 0x20000) >> 17) << 13; - value |= ((operand & 0x1f800) >> 11) << 16; - value |= (operand & 0x007ff); - bits |= value; - break; - case kFmtShift5: - value = ((operand & 0x1c) >> 2) << 12; - value |= (operand & 0x03) << 6; - bits |= value; - break; - case kFmtShift: - value = ((operand & 0x70) >> 4) << 12; - value |= (operand & 0x0f) << 4; - bits |= value; - break; - case kFmtBWidth: - value = operand - 1; - bits |= value; - break; - case kFmtLsb: - value = ((operand & 0x1c) >> 2) << 12; - value |= (operand & 0x03) << 6; - bits |= value; - break; - case kFmtImm6: - value = ((operand & 0x20) >> 5) << 9; - value |= (operand & 0x1f) << 3; - bits |= value; - break; - case kFmtBitBlt: - value = (operand << encoder->field_loc[i].start) & - ((1 << (encoder->field_loc[i].end + 1)) - 1); - bits |= value; - break; - case kFmtDfp: { - DCHECK(ARM_DOUBLEREG(operand)); - DCHECK_EQ((operand & 0x1), 0U); - int reg_name = (operand & ARM_FP_REG_MASK) >> 1; - /* Snag the 1-bit slice and position it */ - value = ((reg_name & 0x10) >> 4) << encoder->field_loc[i].end; - /* Extract and position the 4-bit slice */ - value |= (reg_name & 0x0f) << encoder->field_loc[i].start; - bits |= value; break; } - case kFmtSfp: - DCHECK(ARM_SINGLEREG(operand)); - /* Snag the 1-bit slice and position it */ - value = (operand & 0x1) << encoder->field_loc[i].end; - /* Extract and position the 4-bit slice */ - value |= ((operand & 0x1e) >> 1) << encoder->field_loc[i].start; - bits |= value; - break; - case kFmtImm12: - case kFmtModImm: - value = ((operand & 0x800) >> 11) << 26; - value |= ((operand & 0x700) >> 8) << 12; - value |= operand & 0x0ff; - bits |= value; + case kFixupMovImmLST: { + // operands[1] should hold disp, [2] has add, [3] has tab_rec + LIR *addPCInst = reinterpret_cast(lir->operands[2]); + SwitchTable *tab_rec = reinterpret_cast(lir->operands[3]); + // If tab_rec is null, this is a literal load. Use target + LIR* target = lir->target; + int target_disp = tab_rec ? tab_rec->offset : target->offset; + lir->operands[1] = (target_disp - (addPCInst->offset + 4)) & 0xffff; break; - case kFmtImm16: - value = ((operand & 0x0800) >> 11) << 26; - value |= ((operand & 0xf000) >> 12) << 16; - value |= ((operand & 0x0700) >> 8) << 12; - value |= operand & 0x0ff; - bits |= value; + } + case kFixupMovImmHST: { + // operands[1] should hold disp, [2] has add, [3] has tab_rec + LIR *addPCInst = reinterpret_cast(lir->operands[2]); + SwitchTable *tab_rec = reinterpret_cast(lir->operands[3]); + // If tab_rec is null, this is a literal load. Use target + LIR* target = lir->target; + int target_disp = tab_rec ? tab_rec->offset : target->offset; + lir->operands[1] = + ((target_disp - (addPCInst->offset + 4)) >> 16) & 0xffff; break; - case kFmtOff24: { - uint32_t signbit = (operand >> 31) & 0x1; - uint32_t i1 = (operand >> 22) & 0x1; - uint32_t i2 = (operand >> 21) & 0x1; - uint32_t imm10 = (operand >> 11) & 0x03ff; - uint32_t imm11 = operand & 0x07ff; - uint32_t j1 = (i1 ^ signbit) ? 0 : 1; - uint32_t j2 = (i2 ^ signbit) ? 0 : 1; - value = (signbit << 26) | (j1 << 13) | (j2 << 11) | (imm10 << 16) | - imm11; - bits |= value; + } + case kFixupAlign4: { + int required_size = lir->offset & 0x2; + if (lir->flags.size != required_size) { + offset_adjustment += required_size - lir->flags.size; + lir->flags.size = required_size; + res = kRetryAll; } break; + } default: - LOG(FATAL) << "Bad fmt:" << encoder->field_loc[i].kind; + LOG(FATAL) << "Unexpected case " << lir->flags.fixup; + } + /* + * If one of the pc-relative instructions expanded we'll have + * to make another pass. Don't bother to fully assemble the + * instruction. + */ + if (res == kSuccess) { + EncodeLIR(lir); + if (assembler_retries == 0) { + // Go ahead and fix up the code buffer image. + for (int i = 0; i < lir->flags.size; i++) { + code_buffer_[lir->offset + i] = lir->u.a.bytes[i]; + } + } } + prev_lir = lir; + lir = lir->u.a.pcrel_next; } - if (encoder->size == 4) { - code_buffer_.push_back((bits >> 16) & 0xff); - code_buffer_.push_back((bits >> 24) & 0xff); + + if (res == kSuccess) { + break; + } else { + assembler_retries++; + if (assembler_retries > MAX_ASSEMBLER_RETRIES) { + CodegenDump(); + LOG(FATAL) << "Assembler error - too many retries"; + } + starting_offset += offset_adjustment; + data_offset_ = (starting_offset + 0x3) & ~0x3; + AssignDataOffsets(); + } + } + + // Rebuild the CodeBuffer if we had to retry; otherwise it should be good as-is. + if (assembler_retries != 0) { + code_buffer_.clear(); + for (LIR* lir = first_lir_insn_; lir != NULL; lir = NEXT_LIR(lir)) { + if (lir->flags.is_nop) { + continue; + } else { + for (int i = 0; i < lir->flags.size; i++) { + code_buffer_.push_back(lir->u.a.bytes[i]); + } + } } - code_buffer_.push_back(bits & 0xff); - code_buffer_.push_back((bits >> 8) & 0xff); } - return res; + + data_offset_ = (code_buffer_.size() + 0x3) & ~0x3; + + // Install literals + InstallLiteralPools(); + + // Install switch tables + InstallSwitchTables(); + + // Install fill array data + InstallFillArrayData(); + + // Create the mapping table and native offset to reference map. + CreateMappingTables(); + + CreateNativeGcMap(); } int ArmMir2Lir::GetInsnSize(LIR* lir) { return EncodingMap[lir->opcode].size; } +// Encode instruction bit pattern and assign offsets. +uint32_t ArmMir2Lir::EncodeRange(LIR* head_lir, LIR* tail_lir, uint32_t offset) { + LIR* end_lir = tail_lir->next; + + /* + * A significant percentage of methods can be assembled in a single pass. We'll + * go ahead and build the code image here, leaving holes for pc-relative fixup + * codes. If the code size changes during that pass, we'll have to throw away + * this work - but if not, we're ready to go. + */ + code_buffer_.reserve(estimated_native_code_size_ + 256); // Add a little slop. + LIR* last_fixup = NULL; + for (LIR* lir = head_lir; lir != end_lir; lir = NEXT_LIR(lir)) { + lir->offset = offset; + if (!lir->flags.is_nop) { + if (lir->flags.fixup != kFixupNone) { + if (lir->opcode >= 0) { + lir->flags.size = EncodingMap[lir->opcode].size; + lir->flags.fixup = EncodingMap[lir->opcode].fixup; + } else if (UNLIKELY(lir->opcode == kPseudoPseudoAlign4)) { + lir->flags.size = (offset & 0x2); + lir->flags.fixup = kFixupAlign4; + } else { + lir->flags.size = 0; + lir->flags.fixup = kFixupLabel; + } + // Link into the fixup chain. + lir->flags.use_def_invalid = true; + lir->u.a.pcrel_next = NULL; + if (first_fixup_ == NULL) { + first_fixup_ = lir; + } else { + last_fixup->u.a.pcrel_next = lir; + } + last_fixup = lir; + } else { + EncodeLIR(lir); + } + for (int i = 0; i < lir->flags.size; i++) { + code_buffer_.push_back(lir->u.a.bytes[i]); + } + offset += lir->flags.size; + } + } + return offset; +} + +void ArmMir2Lir::AssignDataOffsets() { + /* Set up offsets for literals */ + int offset = data_offset_; + + offset = AssignLiteralOffset(offset); + + offset = AssignSwitchTablesOffset(offset); + + total_size_ = AssignFillArrayDataOffset(offset); +} + } // namespace art diff --git a/compiler/dex/quick/arm/codegen_arm.h b/compiler/dex/quick/arm/codegen_arm.h index 1954fbac51d..b75661cdfd3 100644 --- a/compiler/dex/quick/arm/codegen_arm.h +++ b/compiler/dex/quick/arm/codegen_arm.h @@ -70,9 +70,14 @@ class ArmMir2Lir : public Mir2Lir { void CompilerInitializeRegAlloc(); // Required for target - miscellaneous. + void AssembleLIR(); + uint32_t EncodeRange(LIR* head_lir, LIR* tail_lir, uint32_t starting_offset); + int AssignInsnOffsets(); + void AssignOffsets(); AssemblerStatus AssembleInstructions(uintptr_t start_addr); + void EncodeLIR(LIR* lir); void DumpResourceMask(LIR* lir, uint64_t mask, const char* prefix); - void SetupTargetResourceMasks(LIR* lir); + void SetupTargetResourceMasks(LIR* lir, uint64_t flags); const char* GetTargetInstFmt(int opcode); const char* GetTargetInstName(int opcode); std::string BuildInsnString(const char* fmt, LIR* lir, unsigned char* base_addr); @@ -187,6 +192,9 @@ class ArmMir2Lir : public Mir2Lir { MIR* SpecialIdentity(MIR* mir); LIR* LoadFPConstantValue(int r_dest, int value); bool BadOverlap(RegLocation rl_src, RegLocation rl_dest); + void ReplaceFixup(LIR* prev_lir, LIR* orig_lir, LIR* new_lir); + void InsertFixupBefore(LIR* prev_lir, LIR* orig_lir, LIR* new_lir); + void AssignDataOffsets(); }; } // namespace art diff --git a/compiler/dex/quick/arm/int_arm.cc b/compiler/dex/quick/arm/int_arm.cc index 07782d957f9..9b0fa62b329 100644 --- a/compiler/dex/quick/arm/int_arm.cc +++ b/compiler/dex/quick/arm/int_arm.cc @@ -319,7 +319,18 @@ LIR* ArmMir2Lir::OpCmpImmBranch(ConditionCode cond, int reg, int check_value, LIR* branch; int mod_imm; ArmConditionCode arm_cond = ArmConditionEncoding(cond); - if ((ARM_LOWREG(reg)) && (check_value == 0) && + /* + * A common use of OpCmpImmBranch is for null checks, and using the Thumb 16-bit + * compare-and-branch if zero is ideal if it will reach. However, because null checks + * branch forward to a launch pad, they will frequently not reach - and thus have to + * be converted to a long form during assembly (which will trigger another assembly + * pass). Here we estimate the branch distance for checks, and if large directly + * generate the long form in an attempt to avoid an extra assembly pass. + * TODO: consider interspersing launchpads in code following unconditional branches. + */ + bool skip = ((target != NULL) && (target->opcode == kPseudoThrowTarget)); + skip &= ((cu_->code_item->insns_size_in_code_units_ - current_dalvik_offset_) > 64); + if (!skip && (ARM_LOWREG(reg)) && (check_value == 0) && ((arm_cond == kArmCondEq) || (arm_cond == kArmCondNe))) { branch = NewLIR2((arm_cond == kArmCondEq) ? kThumb2Cbz : kThumb2Cbnz, reg, 0); @@ -624,7 +635,7 @@ void ArmMir2Lir::GenMemBarrier(MemBarrierKind barrier_kind) { break; } LIR* dmb = NewLIR1(kThumb2Dmb, dmb_flavor); - dmb->def_mask = ENCODE_ALL; + dmb->u.m.def_mask = ENCODE_ALL; #endif } diff --git a/compiler/dex/quick/arm/target_arm.cc b/compiler/dex/quick/arm/target_arm.cc index 203a8cc55df..a4ea10b799a 100644 --- a/compiler/dex/quick/arm/target_arm.cc +++ b/compiler/dex/quick/arm/target_arm.cc @@ -118,78 +118,83 @@ uint64_t ArmMir2Lir::GetPCUseDefEncoding() { return ENCODE_ARM_REG_PC; } -void ArmMir2Lir::SetupTargetResourceMasks(LIR* lir) { +// Thumb2 specific setup. TODO: inline?: +void ArmMir2Lir::SetupTargetResourceMasks(LIR* lir, uint64_t flags) { DCHECK_EQ(cu_->instruction_set, kThumb2); + DCHECK(!lir->flags.use_def_invalid); - // Thumb2 specific setup - uint64_t flags = ArmMir2Lir::EncodingMap[lir->opcode].flags; int opcode = lir->opcode; - if (flags & REG_DEF_SP) { - lir->def_mask |= ENCODE_ARM_REG_SP; - } + // These flags are somewhat uncommon - bypass if we can. + if ((flags & (REG_DEF_SP | REG_USE_SP | REG_DEF_LIST0 | REG_DEF_LIST1 | + REG_DEF_FPCS_LIST0 | REG_DEF_FPCS_LIST2 | REG_USE_PC | IS_IT | REG_USE_LIST0 | + REG_USE_LIST1 | REG_USE_FPCS_LIST0 | REG_USE_FPCS_LIST2 | REG_DEF_LR)) != 0) { + if (flags & REG_DEF_SP) { + lir->u.m.def_mask |= ENCODE_ARM_REG_SP; + } - if (flags & REG_USE_SP) { - lir->use_mask |= ENCODE_ARM_REG_SP; - } + if (flags & REG_USE_SP) { + lir->u.m.use_mask |= ENCODE_ARM_REG_SP; + } - if (flags & REG_DEF_LIST0) { - lir->def_mask |= ENCODE_ARM_REG_LIST(lir->operands[0]); - } + if (flags & REG_DEF_LIST0) { + lir->u.m.def_mask |= ENCODE_ARM_REG_LIST(lir->operands[0]); + } - if (flags & REG_DEF_LIST1) { - lir->def_mask |= ENCODE_ARM_REG_LIST(lir->operands[1]); - } + if (flags & REG_DEF_LIST1) { + lir->u.m.def_mask |= ENCODE_ARM_REG_LIST(lir->operands[1]); + } - if (flags & REG_DEF_FPCS_LIST0) { - lir->def_mask |= ENCODE_ARM_REG_FPCS_LIST(lir->operands[0]); - } + if (flags & REG_DEF_FPCS_LIST0) { + lir->u.m.def_mask |= ENCODE_ARM_REG_FPCS_LIST(lir->operands[0]); + } - if (flags & REG_DEF_FPCS_LIST2) { - for (int i = 0; i < lir->operands[2]; i++) { - SetupRegMask(&lir->def_mask, lir->operands[1] + i); + if (flags & REG_DEF_FPCS_LIST2) { + for (int i = 0; i < lir->operands[2]; i++) { + SetupRegMask(&lir->u.m.def_mask, lir->operands[1] + i); + } } - } - if (flags & REG_USE_PC) { - lir->use_mask |= ENCODE_ARM_REG_PC; - } + if (flags & REG_USE_PC) { + lir->u.m.use_mask |= ENCODE_ARM_REG_PC; + } - /* Conservatively treat the IT block */ - if (flags & IS_IT) { - lir->def_mask = ENCODE_ALL; - } + /* Conservatively treat the IT block */ + if (flags & IS_IT) { + lir->u.m.def_mask = ENCODE_ALL; + } - if (flags & REG_USE_LIST0) { - lir->use_mask |= ENCODE_ARM_REG_LIST(lir->operands[0]); - } + if (flags & REG_USE_LIST0) { + lir->u.m.use_mask |= ENCODE_ARM_REG_LIST(lir->operands[0]); + } - if (flags & REG_USE_LIST1) { - lir->use_mask |= ENCODE_ARM_REG_LIST(lir->operands[1]); - } + if (flags & REG_USE_LIST1) { + lir->u.m.use_mask |= ENCODE_ARM_REG_LIST(lir->operands[1]); + } - if (flags & REG_USE_FPCS_LIST0) { - lir->use_mask |= ENCODE_ARM_REG_FPCS_LIST(lir->operands[0]); - } + if (flags & REG_USE_FPCS_LIST0) { + lir->u.m.use_mask |= ENCODE_ARM_REG_FPCS_LIST(lir->operands[0]); + } - if (flags & REG_USE_FPCS_LIST2) { - for (int i = 0; i < lir->operands[2]; i++) { - SetupRegMask(&lir->use_mask, lir->operands[1] + i); + if (flags & REG_USE_FPCS_LIST2) { + for (int i = 0; i < lir->operands[2]; i++) { + SetupRegMask(&lir->u.m.use_mask, lir->operands[1] + i); + } } - } - /* Fixup for kThumbPush/lr and kThumbPop/pc */ - if (opcode == kThumbPush || opcode == kThumbPop) { - uint64_t r8Mask = GetRegMaskCommon(r8); - if ((opcode == kThumbPush) && (lir->use_mask & r8Mask)) { - lir->use_mask &= ~r8Mask; - lir->use_mask |= ENCODE_ARM_REG_LR; - } else if ((opcode == kThumbPop) && (lir->def_mask & r8Mask)) { - lir->def_mask &= ~r8Mask; - lir->def_mask |= ENCODE_ARM_REG_PC; + /* Fixup for kThumbPush/lr and kThumbPop/pc */ + if (opcode == kThumbPush || opcode == kThumbPop) { + uint64_t r8Mask = GetRegMaskCommon(r8); + if ((opcode == kThumbPush) && (lir->u.m.use_mask & r8Mask)) { + lir->u.m.use_mask &= ~r8Mask; + lir->u.m.use_mask |= ENCODE_ARM_REG_LR; + } else if ((opcode == kThumbPop) && (lir->u.m.def_mask & r8Mask)) { + lir->u.m.def_mask &= ~r8Mask; + lir->u.m.def_mask |= ENCODE_ARM_REG_PC; + } + } + if (flags & REG_DEF_LR) { + lir->u.m.def_mask |= ENCODE_ARM_REG_LR; } - } - if (flags & REG_DEF_LR) { - lir->def_mask |= ENCODE_ARM_REG_LR; } } @@ -466,8 +471,8 @@ void ArmMir2Lir::DumpResourceMask(LIR* arm_lir, uint64_t mask, const char* prefi /* Memory bits */ if (arm_lir && (mask & ENCODE_DALVIK_REG)) { - sprintf(buf + strlen(buf), "dr%d%s", arm_lir->alias_info & 0xffff, - (arm_lir->alias_info & 0x80000000) ? "(+1)" : ""); + sprintf(buf + strlen(buf), "dr%d%s", DECODE_ALIAS_INFO_REG(arm_lir->flags.alias_info), + DECODE_ALIAS_INFO_WIDE(arm_lir->flags.alias_info) ? "(+1)" : ""); } if (mask & ENCODE_LITERAL) { strcat(buf, "lit "); diff --git a/compiler/dex/quick/arm/utility_arm.cc b/compiler/dex/quick/arm/utility_arm.cc index c63de692841..a7b8dfecf01 100644 --- a/compiler/dex/quick/arm/utility_arm.cc +++ b/compiler/dex/quick/arm/utility_arm.cc @@ -90,7 +90,6 @@ LIR* ArmMir2Lir::LoadFPConstantValue(int r_dest, int value) { LIR* load_pc_rel = RawLIR(current_dalvik_offset_, kThumb2Vldrs, r_dest, r15pc, 0, 0, 0, data_target); SetMemRefType(load_pc_rel, true, kLiteral); - load_pc_rel->alias_info = reinterpret_cast(data_target); AppendLIR(load_pc_rel); return load_pc_rel; } @@ -626,7 +625,6 @@ LIR* ArmMir2Lir::LoadConstantWide(int r_dest_lo, int r_dest_hi, int64_t value) { r_dest_lo, r_dest_hi, r15pc, 0, 0, data_target); } SetMemRefType(res, true, kLiteral); - res->alias_info = reinterpret_cast(data_target); AppendLIR(res); } return res; diff --git a/compiler/dex/quick/codegen_util.cc b/compiler/dex/quick/codegen_util.cc index 6e49f0bc54a..617f35707c1 100644 --- a/compiler/dex/quick/codegen_util.cc +++ b/compiler/dex/quick/codegen_util.cc @@ -45,9 +45,10 @@ bool Mir2Lir::IsInexpensiveConstant(RegLocation rl_src) { } void Mir2Lir::MarkSafepointPC(LIR* inst) { - inst->def_mask = ENCODE_ALL; + DCHECK(!inst->flags.use_def_invalid); + inst->u.m.def_mask = ENCODE_ALL; LIR* safepoint_pc = NewLIR0(kPseudoSafepointPC); - DCHECK_EQ(safepoint_pc->def_mask, ENCODE_ALL); + DCHECK_EQ(safepoint_pc->u.m.def_mask, ENCODE_ALL); } bool Mir2Lir::FastInstance(uint32_t field_idx, bool is_put, int* field_offset, bool* is_volatile) { @@ -87,10 +88,11 @@ void Mir2Lir::SetMemRefType(LIR* lir, bool is_load, int mem_type) { uint64_t *mask_ptr; uint64_t mask = ENCODE_MEM; DCHECK(GetTargetInstFlags(lir->opcode) & (IS_LOAD | IS_STORE)); + DCHECK(!lir->flags.use_def_invalid); if (is_load) { - mask_ptr = &lir->use_mask; + mask_ptr = &lir->u.m.use_mask; } else { - mask_ptr = &lir->def_mask; + mask_ptr = &lir->u.m.def_mask; } /* Clear out the memref flags */ *mask_ptr &= ~mask; @@ -127,7 +129,7 @@ void Mir2Lir::AnnotateDalvikRegAccess(LIR* lir, int reg_id, bool is_load, * Store the Dalvik register id in alias_info. Mark the MSB if it is a 64-bit * access. */ - lir->alias_info = ENCODE_ALIAS_INFO(reg_id, is64bit); + lir->flags.alias_info = ENCODE_ALIAS_INFO(reg_id, is64bit); } /* @@ -213,11 +215,11 @@ void Mir2Lir::DumpLIRInsn(LIR* lir, unsigned char* base_addr) { break; } - if (lir->use_mask && (!lir->flags.is_nop || dump_nop)) { - DUMP_RESOURCE_MASK(DumpResourceMask(lir, lir->use_mask, "use")); + if (lir->u.m.use_mask && (!lir->flags.is_nop || dump_nop)) { + DUMP_RESOURCE_MASK(DumpResourceMask(lir, lir->u.m.use_mask, "use")); } - if (lir->def_mask && (!lir->flags.is_nop || dump_nop)) { - DUMP_RESOURCE_MASK(DumpResourceMask(lir, lir->def_mask, "def")); + if (lir->u.m.def_mask && (!lir->flags.is_nop || dump_nop)) { + DUMP_RESOURCE_MASK(DumpResourceMask(lir, lir->u.m.def_mask, "def")); } } @@ -348,6 +350,7 @@ LIR* Mir2Lir::AddWordData(LIR* *constant_list_p, int value) { new_value->operands[0] = value; new_value->next = *constant_list_p; *constant_list_p = new_value; + estimated_native_code_size_ += sizeof(value); return new_value; } return NULL; @@ -431,6 +434,7 @@ void Mir2Lir::InstallSwitchTables() { int bx_offset = INVALID_OFFSET; switch (cu_->instruction_set) { case kThumb2: + DCHECK(tab_rec->anchor->flags.fixup != kFixupNone); bx_offset = tab_rec->anchor->offset + 4; break; case kX86: @@ -714,111 +718,29 @@ int Mir2Lir::AssignFillArrayDataOffset(int offset) { return offset; } -// LIR offset assignment. -int Mir2Lir::AssignInsnOffsets() { - LIR* lir; - int offset = 0; - - for (lir = first_lir_insn_; lir != NULL; lir = NEXT_LIR(lir)) { - lir->offset = offset; - if (LIKELY(lir->opcode >= 0)) { - if (!lir->flags.is_nop) { - offset += lir->flags.size; - } - } else if (UNLIKELY(lir->opcode == kPseudoPseudoAlign4)) { - if (offset & 0x2) { - offset += 2; - lir->operands[0] = 1; - } else { - lir->operands[0] = 0; - } - } - /* Pseudo opcodes don't consume space */ - } - return offset; -} - -/* - * Walk the compilation unit and assign offsets to instructions - * and literals and compute the total size of the compiled unit. - */ -void Mir2Lir::AssignOffsets() { - int offset = AssignInsnOffsets(); - - /* Const values have to be word aligned */ - offset = (offset + 3) & ~3; - - /* Set up offsets for literals */ - data_offset_ = offset; - - offset = AssignLiteralOffset(offset); - - offset = AssignSwitchTablesOffset(offset); - - offset = AssignFillArrayDataOffset(offset); - - total_size_ = offset; -} - -/* - * Go over each instruction in the list and calculate the offset from the top - * before sending them off to the assembler. If out-of-range branch distance is - * seen rearrange the instructions a bit to correct it. - */ -void Mir2Lir::AssembleLIR() { - AssignOffsets(); - int assembler_retries = 0; - /* - * Assemble here. Note that we generate code with optimistic assumptions - * and if found now to work, we'll have to redo the sequence and retry. - */ - - while (true) { - AssemblerStatus res = AssembleInstructions(0); - if (res == kSuccess) { - break; - } else { - assembler_retries++; - if (assembler_retries > MAX_ASSEMBLER_RETRIES) { - CodegenDump(); - LOG(FATAL) << "Assembler error - too many retries"; - } - // Redo offsets and try again - AssignOffsets(); - code_buffer_.clear(); - } - } - - // Install literals - InstallLiteralPools(); - - // Install switch tables - InstallSwitchTables(); - - // Install fill array data - InstallFillArrayData(); - - // Create the mapping table and native offset to reference map. - CreateMappingTables(); - - CreateNativeGcMap(); -} - /* * Insert a kPseudoCaseLabel at the beginning of the Dalvik - * offset vaddr. This label will be used to fix up the case + * offset vaddr if pretty-printing, otherise use the standard block + * label. The selected label will be used to fix up the case * branch table during the assembly phase. All resource flags * are set to prevent code motion. KeyVal is just there for debugging. */ LIR* Mir2Lir::InsertCaseLabel(int vaddr, int keyVal) { LIR* boundary_lir = &block_label_list_[mir_graph_->FindBlock(vaddr)->id]; - LIR* new_label = static_cast(arena_->Alloc(sizeof(LIR), ArenaAllocator::kAllocLIR)); - new_label->dalvik_offset = vaddr; - new_label->opcode = kPseudoCaseLabel; - new_label->operands[0] = keyVal; - new_label->def_mask = ENCODE_ALL; - InsertLIRAfter(boundary_lir, new_label); - return new_label; + LIR* res = boundary_lir; + if (cu_->verbose) { + // Only pay the expense if we're pretty-printing. + LIR* new_label = static_cast(arena_->Alloc(sizeof(LIR), ArenaAllocator::kAllocLIR)); + new_label->dalvik_offset = vaddr; + new_label->opcode = kPseudoCaseLabel; + new_label->operands[0] = keyVal; + new_label->flags.fixup = kFixupLabel; + DCHECK(!new_label->flags.use_def_invalid); + new_label->u.m.def_mask = ENCODE_ALL; + InsertLIRAfter(boundary_lir, new_label); + res = new_label; + } + return res; } void Mir2Lir::MarkPackedCaseLabels(Mir2Lir::SwitchTable *tab_rec) { @@ -951,6 +873,7 @@ Mir2Lir::Mir2Lir(CompilationUnit* cu, MIRGraph* mir_graph, ArenaAllocator* arena literal_list_(NULL), method_literal_list_(NULL), code_literal_list_(NULL), + first_fixup_(NULL), cu_(cu), mir_graph_(mir_graph), switch_tables_(arena, 4, kGrowableArraySwitchTables), @@ -964,6 +887,7 @@ Mir2Lir::Mir2Lir(CompilationUnit* cu, MIRGraph* mir_graph, ArenaAllocator* arena total_size_(0), block_label_list_(NULL), current_dalvik_offset_(0), + estimated_native_code_size_(0), reg_pool_(NULL), live_sreg_(0), num_core_spills_(0), diff --git a/compiler/dex/quick/gen_common.cc b/compiler/dex/quick/gen_common.cc index 4dd55d763a1..9b9fc070a01 100644 --- a/compiler/dex/quick/gen_common.cc +++ b/compiler/dex/quick/gen_common.cc @@ -30,13 +30,14 @@ namespace art { */ /* - * Generate an kPseudoBarrier marker to indicate the boundary of special + * Generate a kPseudoBarrier marker to indicate the boundary of special * blocks. */ void Mir2Lir::GenBarrier() { LIR* barrier = NewLIR0(kPseudoBarrier); /* Mark all resources as being clobbered */ - barrier->def_mask = -1; + DCHECK(!barrier->flags.use_def_invalid); + barrier->u.m.def_mask = ENCODE_ALL; } // FIXME: need to do some work to split out targets with diff --git a/compiler/dex/quick/gen_invoke.cc b/compiler/dex/quick/gen_invoke.cc index ed838637338..8270e017ad1 100644 --- a/compiler/dex/quick/gen_invoke.cc +++ b/compiler/dex/quick/gen_invoke.cc @@ -810,7 +810,7 @@ int Mir2Lir::GenDalvikArgsRange(CallInfo* info, int call_state, OpRegRegImm(kOpAdd, TargetReg(kArg3), TargetReg(kSp), start_offset); LIR* ld = OpVldm(TargetReg(kArg3), regs_left); // TUNING: loosen barrier - ld->def_mask = ENCODE_ALL; + ld->u.m.def_mask = ENCODE_ALL; SetMemRefType(ld, true /* is_load */, kDalvikReg); call_state = next_call_insn(cu_, info, call_state, target_method, vtable_idx, direct_code, direct_method, type); @@ -819,7 +819,7 @@ int Mir2Lir::GenDalvikArgsRange(CallInfo* info, int call_state, direct_code, direct_method, type); LIR* st = OpVstm(TargetReg(kArg3), regs_left); SetMemRefType(st, false /* is_load */, kDalvikReg); - st->def_mask = ENCODE_ALL; + st->u.m.def_mask = ENCODE_ALL; call_state = next_call_insn(cu_, info, call_state, target_method, vtable_idx, direct_code, direct_method, type); } diff --git a/compiler/dex/quick/local_optimizations.cc b/compiler/dex/quick/local_optimizations.cc index cb7694de687..f915779e757 100644 --- a/compiler/dex/quick/local_optimizations.cc +++ b/compiler/dex/quick/local_optimizations.cc @@ -21,8 +21,8 @@ namespace art { #define DEBUG_OPT(X) /* Check RAW, WAR, and RAW dependency on the register operands */ -#define CHECK_REG_DEP(use, def, check) ((def & check->use_mask) || \ - ((use | def) & check->def_mask)) +#define CHECK_REG_DEP(use, def, check) ((def & check->u.m.use_mask) || \ + ((use | def) & check->u.m.def_mask)) /* Scheduler heuristics */ #define MAX_HOIST_DISTANCE 20 @@ -30,10 +30,10 @@ namespace art { #define LD_LATENCY 2 static bool IsDalvikRegisterClobbered(LIR* lir1, LIR* lir2) { - int reg1Lo = DECODE_ALIAS_INFO_REG(lir1->alias_info); - int reg1Hi = reg1Lo + DECODE_ALIAS_INFO_WIDE(lir1->alias_info); - int reg2Lo = DECODE_ALIAS_INFO_REG(lir2->alias_info); - int reg2Hi = reg2Lo + DECODE_ALIAS_INFO_WIDE(lir2->alias_info); + int reg1Lo = DECODE_ALIAS_INFO_REG(lir1->flags.alias_info); + int reg1Hi = reg1Lo + DECODE_ALIAS_INFO_WIDE(lir1->flags.alias_info); + int reg2Lo = DECODE_ALIAS_INFO_REG(lir2->flags.alias_info); + int reg2Hi = reg2Lo + DECODE_ALIAS_INFO_WIDE(lir2->flags.alias_info); return (reg1Lo == reg2Lo) || (reg1Lo == reg2Hi) || (reg1Hi == reg2Lo); } @@ -106,7 +106,7 @@ void Mir2Lir::ApplyLoadStoreElimination(LIR* head_lir, LIR* tail_lir) { bool is_this_lir_load = target_flags & IS_LOAD; LIR* check_lir; /* Use the mem mask to determine the rough memory location */ - uint64_t this_mem_mask = (this_lir->use_mask | this_lir->def_mask) & ENCODE_MEM; + uint64_t this_mem_mask = (this_lir->u.m.use_mask | this_lir->u.m.def_mask) & ENCODE_MEM; /* * Currently only eliminate redundant ld/st for constant and Dalvik @@ -116,10 +116,10 @@ void Mir2Lir::ApplyLoadStoreElimination(LIR* head_lir, LIR* tail_lir) { continue; } - uint64_t stop_def_reg_mask = this_lir->def_mask & ~ENCODE_MEM; + uint64_t stop_def_reg_mask = this_lir->u.m.def_mask & ~ENCODE_MEM; uint64_t stop_use_reg_mask; if (cu_->instruction_set == kX86) { - stop_use_reg_mask = (IS_BRANCH | this_lir->use_mask) & ~ENCODE_MEM; + stop_use_reg_mask = (IS_BRANCH | this_lir->u.m.use_mask) & ~ENCODE_MEM; } else { /* * Add pc to the resource mask to prevent this instruction @@ -127,7 +127,7 @@ void Mir2Lir::ApplyLoadStoreElimination(LIR* head_lir, LIR* tail_lir) { * region bits since stop_mask is used to check data/control * dependencies. */ - stop_use_reg_mask = (GetPCUseDefEncoding() | this_lir->use_mask) & ~ENCODE_MEM; + stop_use_reg_mask = (GetPCUseDefEncoding() | this_lir->u.m.use_mask) & ~ENCODE_MEM; } for (check_lir = NEXT_LIR(this_lir); check_lir != tail_lir; check_lir = NEXT_LIR(check_lir)) { @@ -139,7 +139,7 @@ void Mir2Lir::ApplyLoadStoreElimination(LIR* head_lir, LIR* tail_lir) { continue; } - uint64_t check_mem_mask = (check_lir->use_mask | check_lir->def_mask) & ENCODE_MEM; + uint64_t check_mem_mask = (check_lir->u.m.use_mask | check_lir->u.m.def_mask) & ENCODE_MEM; uint64_t alias_condition = this_mem_mask & check_mem_mask; bool stop_here = false; @@ -159,7 +159,7 @@ void Mir2Lir::ApplyLoadStoreElimination(LIR* head_lir, LIR* tail_lir) { */ DCHECK(!(check_flags & IS_STORE)); /* Same value && same register type */ - if (check_lir->alias_info == this_lir->alias_info && + if (check_lir->flags.alias_info == this_lir->flags.alias_info && SameRegType(check_lir->operands[0], native_reg_id)) { /* * Different destination register - insert @@ -172,7 +172,7 @@ void Mir2Lir::ApplyLoadStoreElimination(LIR* head_lir, LIR* tail_lir) { } } else if (alias_condition == ENCODE_DALVIK_REG) { /* Must alias */ - if (check_lir->alias_info == this_lir->alias_info) { + if (check_lir->flags.alias_info == this_lir->flags.alias_info) { /* Only optimize compatible registers */ bool reg_compatible = SameRegType(check_lir->operands[0], native_reg_id); if ((is_this_lir_load && is_check_lir_load) || @@ -297,7 +297,7 @@ void Mir2Lir::ApplyLoadHoisting(LIR* head_lir, LIR* tail_lir) { continue; } - uint64_t stop_use_all_mask = this_lir->use_mask; + uint64_t stop_use_all_mask = this_lir->u.m.use_mask; if (cu_->instruction_set != kX86) { /* @@ -313,7 +313,7 @@ void Mir2Lir::ApplyLoadHoisting(LIR* head_lir, LIR* tail_lir) { /* Similar as above, but just check for pure register dependency */ uint64_t stop_use_reg_mask = stop_use_all_mask & ~ENCODE_MEM; - uint64_t stop_def_reg_mask = this_lir->def_mask & ~ENCODE_MEM; + uint64_t stop_def_reg_mask = this_lir->u.m.def_mask & ~ENCODE_MEM; int next_slot = 0; bool stop_here = false; @@ -328,7 +328,7 @@ void Mir2Lir::ApplyLoadHoisting(LIR* head_lir, LIR* tail_lir) { continue; } - uint64_t check_mem_mask = check_lir->def_mask & ENCODE_MEM; + uint64_t check_mem_mask = check_lir->u.m.def_mask & ENCODE_MEM; uint64_t alias_condition = stop_use_all_mask & check_mem_mask; stop_here = false; @@ -337,7 +337,7 @@ void Mir2Lir::ApplyLoadHoisting(LIR* head_lir, LIR* tail_lir) { /* We can fully disambiguate Dalvik references */ if (alias_condition == ENCODE_DALVIK_REG) { /* Must alias or partually overlap */ - if ((check_lir->alias_info == this_lir->alias_info) || + if ((check_lir->flags.alias_info == this_lir->flags.alias_info) || IsDalvikRegisterClobbered(this_lir, check_lir)) { stop_here = true; } @@ -406,7 +406,7 @@ void Mir2Lir::ApplyLoadHoisting(LIR* head_lir, LIR* tail_lir) { LIR* prev_lir = prev_inst_list[slot+1]; /* Check the highest instruction */ - if (prev_lir->def_mask == ENCODE_ALL) { + if (prev_lir->u.m.def_mask == ENCODE_ALL) { /* * If the first instruction is a load, don't hoist anything * above it since it is unlikely to be beneficial. @@ -436,7 +436,7 @@ void Mir2Lir::ApplyLoadHoisting(LIR* head_lir, LIR* tail_lir) { */ bool prev_is_load = is_pseudo_opcode(prev_lir->opcode) ? false : (GetTargetInstFlags(prev_lir->opcode) & IS_LOAD); - if (((cur_lir->use_mask & prev_lir->def_mask) && prev_is_load) || (slot < LD_LATENCY)) { + if (((cur_lir->u.m.use_mask & prev_lir->u.m.def_mask) && prev_is_load) || (slot < LD_LATENCY)) { break; } } diff --git a/compiler/dex/quick/mips/assemble_mips.cc b/compiler/dex/quick/mips/assemble_mips.cc index dbd668b3307..3a6207cfb59 100644 --- a/compiler/dex/quick/mips/assemble_mips.cc +++ b/compiler/dex/quick/mips/assemble_mips.cc @@ -526,7 +526,7 @@ AssemblerStatus MipsMir2Lir::AssembleInstructions(uintptr_t start_addr) { continue; } - if (lir->flags.pcRelFixup) { + if (lir->flags.fixup != kFixupNone) { if (lir->opcode == kMipsDelta) { /* * The "Delta" pseudo-ops load the difference between @@ -710,4 +710,97 @@ int MipsMir2Lir::GetInsnSize(LIR* lir) { return EncodingMap[lir->opcode].size; } +// LIR offset assignment. +// TODO: consolidate w/ Arm assembly mechanism. +int MipsMir2Lir::AssignInsnOffsets() { + LIR* lir; + int offset = 0; + + for (lir = first_lir_insn_; lir != NULL; lir = NEXT_LIR(lir)) { + lir->offset = offset; + if (LIKELY(lir->opcode >= 0)) { + if (!lir->flags.is_nop) { + offset += lir->flags.size; + } + } else if (UNLIKELY(lir->opcode == kPseudoPseudoAlign4)) { + if (offset & 0x2) { + offset += 2; + lir->operands[0] = 1; + } else { + lir->operands[0] = 0; + } + } + /* Pseudo opcodes don't consume space */ + } + return offset; +} + +/* + * Walk the compilation unit and assign offsets to instructions + * and literals and compute the total size of the compiled unit. + * TODO: consolidate w/ Arm assembly mechanism. + */ +void MipsMir2Lir::AssignOffsets() { + int offset = AssignInsnOffsets(); + + /* Const values have to be word aligned */ + offset = (offset + 3) & ~3; + + /* Set up offsets for literals */ + data_offset_ = offset; + + offset = AssignLiteralOffset(offset); + + offset = AssignSwitchTablesOffset(offset); + + offset = AssignFillArrayDataOffset(offset); + + total_size_ = offset; +} + +/* + * Go over each instruction in the list and calculate the offset from the top + * before sending them off to the assembler. If out-of-range branch distance is + * seen rearrange the instructions a bit to correct it. + * TODO: consolidate w/ Arm assembly mechanism. + */ +void MipsMir2Lir::AssembleLIR() { + AssignOffsets(); + int assembler_retries = 0; + /* + * Assemble here. Note that we generate code with optimistic assumptions + * and if found now to work, we'll have to redo the sequence and retry. + */ + + while (true) { + AssemblerStatus res = AssembleInstructions(0); + if (res == kSuccess) { + break; + } else { + assembler_retries++; + if (assembler_retries > MAX_ASSEMBLER_RETRIES) { + CodegenDump(); + LOG(FATAL) << "Assembler error - too many retries"; + } + // Redo offsets and try again + AssignOffsets(); + code_buffer_.clear(); + } + } + + // Install literals + InstallLiteralPools(); + + // Install switch tables + InstallSwitchTables(); + + // Install fill array data + InstallFillArrayData(); + + // Create the mapping table and native offset to reference map. + CreateMappingTables(); + + CreateNativeGcMap(); +} + } // namespace art diff --git a/compiler/dex/quick/mips/codegen_mips.h b/compiler/dex/quick/mips/codegen_mips.h index 8d0b347a34c..9671a78497a 100644 --- a/compiler/dex/quick/mips/codegen_mips.h +++ b/compiler/dex/quick/mips/codegen_mips.h @@ -71,9 +71,12 @@ class MipsMir2Lir : public Mir2Lir { void CompilerInitializeRegAlloc(); // Required for target - miscellaneous. + void AssembleLIR(); + int AssignInsnOffsets(); + void AssignOffsets(); AssemblerStatus AssembleInstructions(uintptr_t start_addr); void DumpResourceMask(LIR* lir, uint64_t mask, const char* prefix); - void SetupTargetResourceMasks(LIR* lir); + void SetupTargetResourceMasks(LIR* lir, uint64_t flags); const char* GetTargetInstFmt(int opcode); const char* GetTargetInstName(int opcode); std::string BuildInsnString(const char* fmt, LIR* lir, unsigned char* base_addr); diff --git a/compiler/dex/quick/mips/target_mips.cc b/compiler/dex/quick/mips/target_mips.cc index 8e768dcf182..f9d432ec753 100644 --- a/compiler/dex/quick/mips/target_mips.cc +++ b/compiler/dex/quick/mips/target_mips.cc @@ -120,22 +120,21 @@ uint64_t MipsMir2Lir::GetPCUseDefEncoding() { } -void MipsMir2Lir::SetupTargetResourceMasks(LIR* lir) { +void MipsMir2Lir::SetupTargetResourceMasks(LIR* lir, uint64_t flags) { DCHECK_EQ(cu_->instruction_set, kMips); + DCHECK(!lir->flags.use_def_invalid); // Mips-specific resource map setup here. - uint64_t flags = MipsMir2Lir::EncodingMap[lir->opcode].flags; - if (flags & REG_DEF_SP) { - lir->def_mask |= ENCODE_MIPS_REG_SP; + lir->u.m.def_mask |= ENCODE_MIPS_REG_SP; } if (flags & REG_USE_SP) { - lir->use_mask |= ENCODE_MIPS_REG_SP; + lir->u.m.use_mask |= ENCODE_MIPS_REG_SP; } if (flags & REG_DEF_LR) { - lir->def_mask |= ENCODE_MIPS_REG_LR; + lir->u.m.def_mask |= ENCODE_MIPS_REG_LR; } } @@ -269,8 +268,8 @@ void MipsMir2Lir::DumpResourceMask(LIR *mips_lir, uint64_t mask, const char *pre } /* Memory bits */ if (mips_lir && (mask & ENCODE_DALVIK_REG)) { - sprintf(buf + strlen(buf), "dr%d%s", mips_lir->alias_info & 0xffff, - (mips_lir->alias_info & 0x80000000) ? "(+1)" : ""); + sprintf(buf + strlen(buf), "dr%d%s", DECODE_ALIAS_INFO_REG(mips_lir->flags.alias_info), + DECODE_ALIAS_INFO_WIDE(mips_lir->flags.alias_info) ? "(+1)" : ""); } if (mask & ENCODE_LITERAL) { strcat(buf, "lit "); diff --git a/compiler/dex/quick/mir_to_lir-inl.h b/compiler/dex/quick/mir_to_lir-inl.h index 0ca8d8de112..314c57e02c0 100644 --- a/compiler/dex/quick/mir_to_lir-inl.h +++ b/compiler/dex/quick/mir_to_lir-inl.h @@ -58,7 +58,8 @@ inline LIR* Mir2Lir::RawLIR(int dalvik_offset, int opcode, int op0, if ((opcode == kPseudoTargetLabel) || (opcode == kPseudoSafepointPC) || (opcode == kPseudoExportedPC)) { // Always make labels scheduling barriers - insn->use_mask = insn->def_mask = ENCODE_ALL; + DCHECK(!insn->flags.use_def_invalid); + insn->u.m.use_mask = insn->u.m.def_mask = ENCODE_ALL; } return insn; } @@ -141,20 +142,21 @@ inline void Mir2Lir::SetupRegMask(uint64_t* mask, int reg) { inline void Mir2Lir::SetupResourceMasks(LIR* lir) { int opcode = lir->opcode; - if (opcode <= 0) { - lir->use_mask = lir->def_mask = 0; + if ((opcode < 0) && (opcode != kPseudoBarrier)) { + lir->flags.fixup = kFixupLabel; return; } uint64_t flags = GetTargetInstFlags(opcode); if (flags & NEEDS_FIXUP) { - lir->flags.pcRelFixup = true; + // Note: target-specific setup may specialize the fixup kind. + lir->flags.fixup = kFixupLabel; } /* Get the starting size of the instruction's template */ lir->flags.size = GetInsnSize(lir); - + estimated_native_code_size_ += lir->flags.size; /* Set up the mask for resources that are updated */ if (flags & (IS_LOAD | IS_STORE)) { /* Default to heap - will catch specialized classes later */ @@ -166,39 +168,44 @@ inline void Mir2Lir::SetupResourceMasks(LIR* lir) { * turn will trash everything. */ if (flags & IS_BRANCH) { - lir->def_mask = lir->use_mask = ENCODE_ALL; + lir->u.m.def_mask = lir->u.m.use_mask = ENCODE_ALL; return; } if (flags & REG_DEF0) { - SetupRegMask(&lir->def_mask, lir->operands[0]); + SetupRegMask(&lir->u.m.def_mask, lir->operands[0]); } if (flags & REG_DEF1) { - SetupRegMask(&lir->def_mask, lir->operands[1]); + SetupRegMask(&lir->u.m.def_mask, lir->operands[1]); } + if (flags & REG_USE0) { + SetupRegMask(&lir->u.m.use_mask, lir->operands[0]); + } - if (flags & SETS_CCODES) { - lir->def_mask |= ENCODE_CCODE; + if (flags & REG_USE1) { + SetupRegMask(&lir->u.m.use_mask, lir->operands[1]); + } + + if (flags & REG_USE2) { + SetupRegMask(&lir->u.m.use_mask, lir->operands[2]); } - if (flags & (REG_USE0 | REG_USE1 | REG_USE2 | REG_USE3)) { - int i; + if (flags & REG_USE3) { + SetupRegMask(&lir->u.m.use_mask, lir->operands[3]); + } - for (i = 0; i < 4; i++) { - if (flags & (1 << (kRegUse0 + i))) { - SetupRegMask(&lir->use_mask, lir->operands[i]); - } - } + if (flags & SETS_CCODES) { + lir->u.m.def_mask |= ENCODE_CCODE; } if (flags & USES_CCODES) { - lir->use_mask |= ENCODE_CCODE; + lir->u.m.use_mask |= ENCODE_CCODE; } // Handle target-specific actions - SetupTargetResourceMasks(lir); + SetupTargetResourceMasks(lir, flags); } inline art::Mir2Lir::RegisterInfo* Mir2Lir::GetRegInfo(int reg) { diff --git a/compiler/dex/quick/mir_to_lir.cc b/compiler/dex/quick/mir_to_lir.cc index 6f398696dd0..66ece2c2307 100644 --- a/compiler/dex/quick/mir_to_lir.cc +++ b/compiler/dex/quick/mir_to_lir.cc @@ -697,6 +697,7 @@ bool Mir2Lir::MethodBlockCodeGen(BasicBlock* bb) { // Insert the block label. block_label_list_[block_id].opcode = kPseudoNormalBlockLabel; + block_label_list_[block_id].flags.fixup = kFixupLabel; AppendLIR(&block_label_list_[block_id]); LIR* head_lir = NULL; @@ -746,7 +747,8 @@ bool Mir2Lir::MethodBlockCodeGen(BasicBlock* bb) { if (head_lir == NULL) { head_lir = &block_label_list_[bb->id]; // Set the first label as a scheduling barrier. - head_lir->def_mask = ENCODE_ALL; + DCHECK(!head_lir->flags.use_def_invalid); + head_lir->u.m.def_mask = ENCODE_ALL; } if (opcode == kMirOpCheck) { diff --git a/compiler/dex/quick/mir_to_lir.h b/compiler/dex/quick/mir_to_lir.h index 7d6f968da5c..e48cdbc7f8b 100644 --- a/compiler/dex/quick/mir_to_lir.h +++ b/compiler/dex/quick/mir_to_lir.h @@ -95,6 +95,7 @@ struct BasicBlock; struct CallInfo; struct CompilationUnit; struct MIR; +struct LIR; struct RegLocation; struct RegisterInfo; class MIRGraph; @@ -107,24 +108,36 @@ typedef int (*NextCallInsn)(CompilationUnit*, CallInfo*, int, typedef std::vector CodeBuffer; +struct UseDefMasks { + uint64_t use_mask; // Resource mask for use. + uint64_t def_mask; // Resource mask for def. +}; + +struct AssemblyInfo { + LIR* pcrel_next; // Chain of LIR nodes needing pc relative fixups. + uint8_t bytes[16]; // Encoded instruction bytes. +}; struct LIR { int offset; // Offset of this instruction. - int dalvik_offset; // Offset of Dalvik opcode. + uint16_t dalvik_offset; // Offset of Dalvik opcode in code units (16-bit words). + int16_t opcode; LIR* next; LIR* prev; LIR* target; - int opcode; - int operands[5]; // [0..4] = [dest, src1, src2, extra, extra2]. struct { - bool is_nop:1; // LIR is optimized away. - bool pcRelFixup:1; // May need pc-relative fixup. - unsigned int size:5; // Note: size is in bytes. - unsigned int unused:25; + unsigned int alias_info:17; // For Dalvik register disambiguation. + bool is_nop:1; // LIR is optimized away. + unsigned int size:4; // Note: size of encoded instruction is in bytes. + bool use_def_invalid:1; // If true, masks should not be used. + unsigned int generation:1; // Used to track visitation state during fixup pass. + unsigned int fixup:8; // Fixup kind. } flags; - int alias_info; // For Dalvik register & litpool disambiguation. - uint64_t use_mask; // Resource mask for use. - uint64_t def_mask; // Resource mask for def. + union { + UseDefMasks m; // Use & Def masks used during optimization. + AssemblyInfo a; // Instruction encoding used during assembly phase. + } u; + int operands[5]; // [0..4] = [dest, src1, src2, extra, extra2]. }; // Target-specific initialization. @@ -141,7 +154,7 @@ Mir2Lir* X86CodeGenerator(CompilationUnit* const cu, MIRGraph* const mir_graph, // Defines for alias_info (tracks Dalvik register references). #define DECODE_ALIAS_INFO_REG(X) (X & 0xffff) -#define DECODE_ALIAS_INFO_WIDE_FLAG (0x80000000) +#define DECODE_ALIAS_INFO_WIDE_FLAG (0x10000) #define DECODE_ALIAS_INFO_WIDE(X) ((X & DECODE_ALIAS_INFO_WIDE_FLAG) ? 1 : 0) #define ENCODE_ALIAS_INFO(REG, ISWIDE) (REG | (ISWIDE ? DECODE_ALIAS_INFO_WIDE_FLAG : 0)) @@ -255,7 +268,6 @@ class Mir2Lir : public Backend { void MarkSafepointPC(LIR* inst); bool FastInstance(uint32_t field_idx, bool is_put, int* field_offset, bool* is_volatile); void SetupResourceMasks(LIR* lir); - void AssembleLIR(); void SetMemRefType(LIR* lir, bool is_load, int mem_type); void AnnotateDalvikRegAccess(LIR* lir, int reg_id, bool is_load, bool is64bit); void SetupRegMask(uint64_t* mask, int reg); @@ -295,8 +307,6 @@ class Mir2Lir : public Backend { int AssignLiteralOffset(int offset); int AssignSwitchTablesOffset(int offset); int AssignFillArrayDataOffset(int offset); - int AssignInsnOffsets(); - void AssignOffsets(); LIR* InsertCaseLabel(int vaddr, int keyVal); void MarkPackedCaseLabels(Mir2Lir::SwitchTable *tab_rec); void MarkSparseCaseLabels(Mir2Lir::SwitchTable *tab_rec); @@ -571,9 +581,9 @@ class Mir2Lir : public Backend { virtual void CompilerInitializeRegAlloc() = 0; // Required for target - miscellaneous. - virtual AssemblerStatus AssembleInstructions(uintptr_t start_addr) = 0; + virtual void AssembleLIR() = 0; virtual void DumpResourceMask(LIR* lir, uint64_t mask, const char* prefix) = 0; - virtual void SetupTargetResourceMasks(LIR* lir) = 0; + virtual void SetupTargetResourceMasks(LIR* lir, uint64_t flags) = 0; virtual const char* GetTargetInstFmt(int opcode) = 0; virtual const char* GetTargetInstName(int opcode) = 0; virtual std::string BuildInsnString(const char* fmt, LIR* lir, unsigned char* base_addr) = 0; @@ -719,6 +729,7 @@ class Mir2Lir : public Backend { LIR* literal_list_; // Constants. LIR* method_literal_list_; // Method literals requiring patching. LIR* code_literal_list_; // Code literals requiring patching. + LIR* first_fixup_; // Doubly-linked list of LIR nodes requiring fixups. protected: CompilationUnit* const cu_; @@ -741,6 +752,7 @@ class Mir2Lir : public Backend { * immediately preceed the instruction. */ std::vector dex2pc_mapping_table_; + int current_code_offset_; // Working byte offset of machine instructons. int data_offset_; // starting offset of literal pool. int total_size_; // header + code size. LIR* block_label_list_; @@ -755,6 +767,7 @@ class Mir2Lir : public Backend { * The low-level LIR creation utilites will pull it from here. Rework this. */ int current_dalvik_offset_; + int estimated_native_code_size_; // Just an estimate; used to reserve code_buffer_ size. RegisterPool* reg_pool_; /* * Sanity checking for the register temp tracking. The same ssa diff --git a/compiler/dex/quick/x86/assemble_x86.cc b/compiler/dex/quick/x86/assemble_x86.cc index 3e768837ff1..b1634da4d9c 100644 --- a/compiler/dex/quick/x86/assemble_x86.cc +++ b/compiler/dex/quick/x86/assemble_x86.cc @@ -1174,7 +1174,7 @@ AssemblerStatus X86Mir2Lir::AssembleInstructions(uintptr_t start_addr) { continue; } - if (lir->flags.pcRelFixup) { + if (lir->flags.fixup != kFixupNone) { switch (lir->opcode) { case kX86Jcc8: { LIR *target_lir = lir->target; @@ -1385,4 +1385,97 @@ AssemblerStatus X86Mir2Lir::AssembleInstructions(uintptr_t start_addr) { return res; } +// LIR offset assignment. +// TODO: consolidate w/ Arm assembly mechanism. +int X86Mir2Lir::AssignInsnOffsets() { + LIR* lir; + int offset = 0; + + for (lir = first_lir_insn_; lir != NULL; lir = NEXT_LIR(lir)) { + lir->offset = offset; + if (LIKELY(lir->opcode >= 0)) { + if (!lir->flags.is_nop) { + offset += lir->flags.size; + } + } else if (UNLIKELY(lir->opcode == kPseudoPseudoAlign4)) { + if (offset & 0x2) { + offset += 2; + lir->operands[0] = 1; + } else { + lir->operands[0] = 0; + } + } + /* Pseudo opcodes don't consume space */ + } + return offset; +} + +/* + * Walk the compilation unit and assign offsets to instructions + * and literals and compute the total size of the compiled unit. + * TODO: consolidate w/ Arm assembly mechanism. + */ +void X86Mir2Lir::AssignOffsets() { + int offset = AssignInsnOffsets(); + + /* Const values have to be word aligned */ + offset = (offset + 3) & ~3; + + /* Set up offsets for literals */ + data_offset_ = offset; + + offset = AssignLiteralOffset(offset); + + offset = AssignSwitchTablesOffset(offset); + + offset = AssignFillArrayDataOffset(offset); + + total_size_ = offset; +} + +/* + * Go over each instruction in the list and calculate the offset from the top + * before sending them off to the assembler. If out-of-range branch distance is + * seen rearrange the instructions a bit to correct it. + * TODO: consolidate w/ Arm assembly mechanism. + */ +void X86Mir2Lir::AssembleLIR() { + AssignOffsets(); + int assembler_retries = 0; + /* + * Assemble here. Note that we generate code with optimistic assumptions + * and if found now to work, we'll have to redo the sequence and retry. + */ + + while (true) { + AssemblerStatus res = AssembleInstructions(0); + if (res == kSuccess) { + break; + } else { + assembler_retries++; + if (assembler_retries > MAX_ASSEMBLER_RETRIES) { + CodegenDump(); + LOG(FATAL) << "Assembler error - too many retries"; + } + // Redo offsets and try again + AssignOffsets(); + code_buffer_.clear(); + } + } + + // Install literals + InstallLiteralPools(); + + // Install switch tables + InstallSwitchTables(); + + // Install fill array data + InstallFillArrayData(); + + // Create the mapping table and native offset to reference map. + CreateMappingTables(); + + CreateNativeGcMap(); +} + } // namespace art diff --git a/compiler/dex/quick/x86/codegen_x86.h b/compiler/dex/quick/x86/codegen_x86.h index 0f281106b27..e9eafc66f09 100644 --- a/compiler/dex/quick/x86/codegen_x86.h +++ b/compiler/dex/quick/x86/codegen_x86.h @@ -71,9 +71,12 @@ class X86Mir2Lir : public Mir2Lir { void CompilerInitializeRegAlloc(); // Required for target - miscellaneous. + void AssembleLIR(); + int AssignInsnOffsets(); + void AssignOffsets(); AssemblerStatus AssembleInstructions(uintptr_t start_addr); void DumpResourceMask(LIR* lir, uint64_t mask, const char* prefix); - void SetupTargetResourceMasks(LIR* lir); + void SetupTargetResourceMasks(LIR* lir, uint64_t flags); const char* GetTargetInstFmt(int opcode); const char* GetTargetInstName(int opcode); std::string BuildInsnString(const char* fmt, LIR* lir, unsigned char* base_addr); diff --git a/compiler/dex/quick/x86/target_x86.cc b/compiler/dex/quick/x86/target_x86.cc index 94dd759e91a..f0808301a3a 100644 --- a/compiler/dex/quick/x86/target_x86.cc +++ b/compiler/dex/quick/x86/target_x86.cc @@ -132,37 +132,36 @@ uint64_t X86Mir2Lir::GetPCUseDefEncoding() { return 0ULL; } -void X86Mir2Lir::SetupTargetResourceMasks(LIR* lir) { +void X86Mir2Lir::SetupTargetResourceMasks(LIR* lir, uint64_t flags) { DCHECK_EQ(cu_->instruction_set, kX86); + DCHECK(!lir->flags.use_def_invalid); // X86-specific resource map setup here. - uint64_t flags = X86Mir2Lir::EncodingMap[lir->opcode].flags; - if (flags & REG_USE_SP) { - lir->use_mask |= ENCODE_X86_REG_SP; + lir->u.m.use_mask |= ENCODE_X86_REG_SP; } if (flags & REG_DEF_SP) { - lir->def_mask |= ENCODE_X86_REG_SP; + lir->u.m.def_mask |= ENCODE_X86_REG_SP; } if (flags & REG_DEFA) { - SetupRegMask(&lir->def_mask, rAX); + SetupRegMask(&lir->u.m.def_mask, rAX); } if (flags & REG_DEFD) { - SetupRegMask(&lir->def_mask, rDX); + SetupRegMask(&lir->u.m.def_mask, rDX); } if (flags & REG_USEA) { - SetupRegMask(&lir->use_mask, rAX); + SetupRegMask(&lir->u.m.use_mask, rAX); } if (flags & REG_USEC) { - SetupRegMask(&lir->use_mask, rCX); + SetupRegMask(&lir->u.m.use_mask, rCX); } if (flags & REG_USED) { - SetupRegMask(&lir->use_mask, rDX); + SetupRegMask(&lir->u.m.use_mask, rDX); } } @@ -275,8 +274,8 @@ void X86Mir2Lir::DumpResourceMask(LIR *x86LIR, uint64_t mask, const char *prefix } /* Memory bits */ if (x86LIR && (mask & ENCODE_DALVIK_REG)) { - sprintf(buf + strlen(buf), "dr%d%s", x86LIR->alias_info & 0xffff, - (x86LIR->alias_info & 0x80000000) ? "(+1)" : ""); + sprintf(buf + strlen(buf), "dr%d%s", DECODE_ALIAS_INFO_REG(x86LIR->flags.alias_info), + (DECODE_ALIAS_INFO_WIDE(x86LIR->flags.alias_info)) ? "(+1)" : ""); } if (mask & ENCODE_LITERAL) { strcat(buf, "lit "); From d9c4fc94fa618617f94e1de9af5f034549100753 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Tue, 1 Oct 2013 19:45:43 -0700 Subject: [PATCH 0066/2402] Inflate contended lock word by suspending owner. Bug 6961405. Don't inflate monitors for Notify and NotifyAll. Tidy lock word, handle recursive lock case alongside unlocked case and move assembly out of line (except for ARM quick). Also handle null in out-of-line assembly as the test is quick and the enter/exit code is already a safepoint. To gain ownership of a monitor on behalf of another thread, monitor contenders must not hold the monitor_lock_, so they wait on a condition variable. Reduce size of per mutex contention log. Be consistent in calling thin lock thread ids just thread ids. Fix potential thread death races caused by the use of FindThreadByThreadId, make it invariant that returned threads are either self or suspended now. Code size reduction on ARM boot.oat 0.2%. Old nexus 7 speedup 0.25%, new nexus 7 speedup 1.4%, nexus 10 speedup 2.24%, nexus 4 speedup 2.09% on DeltaBlue. Change-Id: Id52558b914f160d9c8578fdd7fc8199a9598576a --- compiler/Android.mk | 1 - compiler/dex/quick/arm/call_arm.cc | 168 ++-- compiler/dex/quick/arm/int_arm.cc | 3 +- compiler/dex/quick/gen_common.cc | 12 + compiler/dex/quick/mips/call_mips.cc | 31 +- compiler/dex/quick/mips/codegen_mips.h | 2 - compiler/dex/quick/mir_to_lir.h | 6 +- compiler/dex/quick/x86/call_x86.cc | 37 - compiler/dex/quick/x86/codegen_x86.h | 2 - compiler/image_test.cc | 4 +- compiler/llvm/llvm_compilation_unit.cc | 5 +- compiler/llvm/runtime_support_builder.cc | 76 -- compiler/llvm/runtime_support_builder.h | 4 +- compiler/llvm/runtime_support_builder_arm.cc | 19 - compiler/llvm/runtime_support_builder_arm.h | 4 - .../llvm/runtime_support_builder_thumb2.cc | 90 --- .../llvm/runtime_support_builder_thumb2.h | 37 - dalvikvm/Android.mk | 2 +- runtime/Android.mk | 1 + runtime/arch/arm/asm_support_arm.h | 2 + runtime/arch/arm/quick_entrypoints_arm.S | 49 +- runtime/arch/arm/thread_arm.cc | 1 + runtime/arch/x86/asm_support_x86.h | 2 + runtime/arch/x86/quick_entrypoints_x86.S | 79 +- runtime/arch/x86/thread_x86.cc | 1 + runtime/asm_support.h | 3 + runtime/base/mutex.cc | 17 +- runtime/base/mutex.h | 2 +- runtime/debugger.cc | 24 +- runtime/entrypoints/entrypoint_utils.h | 1 - .../quick/quick_lock_entrypoints.cc | 42 +- runtime/jni_internal.cc | 4 +- runtime/lock_word-inl.h | 50 ++ runtime/lock_word.h | 128 +++ runtime/mirror/class.cc | 2 +- runtime/mirror/object-inl.h | 25 +- runtime/mirror/object.h | 15 +- runtime/monitor.cc | 727 ++++++++---------- runtime/monitor.h | 106 +-- runtime/monitor_android.cc | 2 +- runtime/native/dalvik_system_VMStack.cc | 4 +- runtime/native/java_lang_DexCache.cc | 2 +- runtime/native/java_lang_Thread.cc | 4 +- ...pache_harmony_dalvik_ddmc_DdmVmInternal.cc | 20 +- runtime/object_utils.h | 3 +- runtime/runtime.cc | 10 +- runtime/runtime.h | 7 + runtime/signal_catcher.cc | 1 - runtime/thread-inl.h | 9 +- runtime/thread.cc | 114 +-- runtime/thread.h | 43 +- runtime/thread_list.cc | 201 ++++- runtime/thread_list.h | 31 +- 53 files changed, 1192 insertions(+), 1043 deletions(-) delete mode 100644 compiler/llvm/runtime_support_builder_thumb2.cc delete mode 100644 compiler/llvm/runtime_support_builder_thumb2.h create mode 100644 runtime/lock_word-inl.h create mode 100644 runtime/lock_word.h diff --git a/compiler/Android.mk b/compiler/Android.mk index 66ff46163ba..fc2f02b59e7 100644 --- a/compiler/Android.mk +++ b/compiler/Android.mk @@ -74,7 +74,6 @@ LIBART_COMPILER_SRC_FILES := \ llvm/md_builder.cc \ llvm/runtime_support_builder.cc \ llvm/runtime_support_builder_arm.cc \ - llvm/runtime_support_builder_thumb2.cc \ llvm/runtime_support_builder_x86.cc \ trampolines/trampoline_compiler.cc \ utils/arm/assembler_arm.cc \ diff --git a/compiler/dex/quick/arm/call_arm.cc b/compiler/dex/quick/arm/call_arm.cc index bba2ec5c4e7..401da2abd48 100644 --- a/compiler/dex/quick/arm/call_arm.cc +++ b/compiler/dex/quick/arm/call_arm.cc @@ -440,88 +440,120 @@ void ArmMir2Lir::GenFillArrayData(uint32_t table_offset, RegLocation rl_src) { } /* - * Handle simple case (thin lock) inline. If it's complicated, bail - * out to the heavyweight lock/unlock routines. We'll use dedicated - * registers here in order to be in the right position in case we - * to bail to oat[Lock/Unlock]Object(self, object) - * - * r0 -> self pointer [arg0 for oat[Lock/Unlock]Object - * r1 -> object [arg1 for oat[Lock/Unlock]Object - * r2 -> intial contents of object->lock, later result of strex - * r3 -> self->thread_id - * r12 -> allow to be used by utilities as general temp - * - * The result of the strex is 0 if we acquire the lock. - * - * See comments in monitor.cc for the layout of the lock word. - * Of particular interest to this code is the test for the - * simple case - which we handle inline. For monitor enter, the - * simple case is thin lock, held by no-one. For monitor exit, - * the simple case is thin lock, held by the unlocking thread with - * a recurse count of 0. - * - * A minor complication is that there is a field in the lock word - * unrelated to locking: the hash state. This field must be ignored, but - * preserved. - * + * Handle unlocked -> thin locked transition inline or else call out to quick entrypoint. For more + * details see monitor.cc. */ void ArmMir2Lir::GenMonitorEnter(int opt_flags, RegLocation rl_src) { FlushAllRegs(); - DCHECK_EQ(LW_SHAPE_THIN, 0); LoadValueDirectFixed(rl_src, r0); // Get obj LockCallTemps(); // Prepare for explicit register usage - GenNullCheck(rl_src.s_reg_low, r0, opt_flags); - LoadWordDisp(rARM_SELF, Thread::ThinLockIdOffset().Int32Value(), r2); - NewLIR3(kThumb2Ldrex, r1, r0, - mirror::Object::MonitorOffset().Int32Value() >> 2); // Get object->lock - // Align owner - OpRegImm(kOpLsl, r2, LW_LOCK_OWNER_SHIFT); - // Is lock unheld on lock or held by us (==thread_id) on unlock? - NewLIR4(kThumb2Bfi, r2, r1, 0, LW_LOCK_OWNER_SHIFT - 1); - NewLIR3(kThumb2Bfc, r1, LW_HASH_STATE_SHIFT, LW_LOCK_OWNER_SHIFT - 1); - OpRegImm(kOpCmp, r1, 0); - OpIT(kCondEq, ""); - NewLIR4(kThumb2Strex, r1, r2, r0, - mirror::Object::MonitorOffset().Int32Value() >> 2); - OpRegImm(kOpCmp, r1, 0); - OpIT(kCondNe, "T"); - // Go expensive route - artLockObjectFromCode(self, obj); - LoadWordDisp(rARM_SELF, QUICK_ENTRYPOINT_OFFSET(pLockObject).Int32Value(), rARM_LR); - ClobberCalleeSave(); - LIR* call_inst = OpReg(kOpBlx, rARM_LR); - MarkSafepointPC(call_inst); - GenMemBarrier(kLoadLoad); + constexpr bool kArchVariantHasGoodBranchPredictor = false; // TODO: true if cortex-A15. + if (kArchVariantHasGoodBranchPredictor) { + LIR* null_check_branch; + if ((opt_flags & MIR_IGNORE_NULL_CHECK) && !(cu_->disable_opt & (1 << kNullCheckElimination))) { + null_check_branch = nullptr; // No null check. + } else { + // If the null-check fails its handled by the slow-path to reduce exception related meta-data. + null_check_branch = OpCmpImmBranch(kCondEq, r0, 0, NULL); + } + LoadWordDisp(rARM_SELF, Thread::ThinLockIdOffset().Int32Value(), r2); + NewLIR3(kThumb2Ldrex, r1, r0, mirror::Object::MonitorOffset().Int32Value() >> 2); + LIR* not_unlocked_branch = OpCmpImmBranch(kCondNe, r1, 0, NULL); + NewLIR4(kThumb2Strex, r1, r2, r0, mirror::Object::MonitorOffset().Int32Value() >> 2); + LIR* lock_success_branch = OpCmpImmBranch(kCondEq, r1, 0, NULL); + + + LIR* slow_path_target = NewLIR0(kPseudoTargetLabel); + not_unlocked_branch->target = slow_path_target; + if (null_check_branch != nullptr) { + null_check_branch->target = slow_path_target; + } + // TODO: move to a slow path. + // Go expensive route - artLockObjectFromCode(obj); + LoadWordDisp(rARM_SELF, QUICK_ENTRYPOINT_OFFSET(pLockObject).Int32Value(), rARM_LR); + ClobberCalleeSave(); + LIR* call_inst = OpReg(kOpBlx, rARM_LR); + MarkSafepointPC(call_inst); + + LIR* success_target = NewLIR0(kPseudoTargetLabel); + lock_success_branch->target = success_target; + GenMemBarrier(kLoadLoad); + } else { + // Explicit null-check as slow-path is entered using an IT. + GenNullCheck(rl_src.s_reg_low, r0, opt_flags); + LoadWordDisp(rARM_SELF, Thread::ThinLockIdOffset().Int32Value(), r2); + NewLIR3(kThumb2Ldrex, r1, r0, mirror::Object::MonitorOffset().Int32Value() >> 2); + OpRegImm(kOpCmp, r1, 0); + OpIT(kCondEq, ""); + NewLIR4(kThumb2Strex/*eq*/, r1, r2, r0, mirror::Object::MonitorOffset().Int32Value() >> 2); + OpRegImm(kOpCmp, r1, 0); + OpIT(kCondNe, "T"); + // Go expensive route - artLockObjectFromCode(self, obj); + LoadWordDisp/*ne*/(rARM_SELF, QUICK_ENTRYPOINT_OFFSET(pLockObject).Int32Value(), rARM_LR); + ClobberCalleeSave(); + LIR* call_inst = OpReg(kOpBlx/*ne*/, rARM_LR); + MarkSafepointPC(call_inst); + GenMemBarrier(kLoadLoad); + } } /* - * For monitor unlock, we don't have to use ldrex/strex. Once - * we've determined that the lock is thin and that we own it with - * a zero recursion count, it's safe to punch it back to the - * initial, unlock thin state with a store word. + * Handle thin locked -> unlocked transition inline or else call out to quick entrypoint. For more + * details see monitor.cc. Note the code below doesn't use ldrex/strex as the code holds the lock + * and can only give away ownership if its suspended. */ void ArmMir2Lir::GenMonitorExit(int opt_flags, RegLocation rl_src) { - DCHECK_EQ(LW_SHAPE_THIN, 0); FlushAllRegs(); LoadValueDirectFixed(rl_src, r0); // Get obj LockCallTemps(); // Prepare for explicit register usage - GenNullCheck(rl_src.s_reg_low, r0, opt_flags); - LoadWordDisp(r0, mirror::Object::MonitorOffset().Int32Value(), r1); // Get lock + LIR* null_check_branch; LoadWordDisp(rARM_SELF, Thread::ThinLockIdOffset().Int32Value(), r2); - // Is lock unheld on lock or held by us (==thread_id) on unlock? - OpRegRegImm(kOpAnd, r3, r1, - (LW_HASH_STATE_MASK << LW_HASH_STATE_SHIFT)); - // Align owner - OpRegImm(kOpLsl, r2, LW_LOCK_OWNER_SHIFT); - NewLIR3(kThumb2Bfc, r1, LW_HASH_STATE_SHIFT, LW_LOCK_OWNER_SHIFT - 1); - OpRegReg(kOpSub, r1, r2); - OpIT(kCondEq, "EE"); - StoreWordDisp(r0, mirror::Object::MonitorOffset().Int32Value(), r3); - // Go expensive route - UnlockObjectFromCode(obj); - LoadWordDisp(rARM_SELF, QUICK_ENTRYPOINT_OFFSET(pUnlockObject).Int32Value(), rARM_LR); - ClobberCalleeSave(); - LIR* call_inst = OpReg(kOpBlx, rARM_LR); - MarkSafepointPC(call_inst); - GenMemBarrier(kStoreLoad); + constexpr bool kArchVariantHasGoodBranchPredictor = false; // TODO: true if cortex-A15. + if (kArchVariantHasGoodBranchPredictor) { + if ((opt_flags & MIR_IGNORE_NULL_CHECK) && !(cu_->disable_opt & (1 << kNullCheckElimination))) { + null_check_branch = nullptr; // No null check. + } else { + // If the null-check fails its handled by the slow-path to reduce exception related meta-data. + null_check_branch = OpCmpImmBranch(kCondEq, r0, 0, NULL); + } + LoadWordDisp(r0, mirror::Object::MonitorOffset().Int32Value(), r1); + LoadConstantNoClobber(r3, 0); + LIR* slow_unlock_branch = OpCmpBranch(kCondNe, r1, r2, NULL); + StoreWordDisp(r0, mirror::Object::MonitorOffset().Int32Value(), r3); + LIR* unlock_success_branch = OpUnconditionalBranch(NULL); + + LIR* slow_path_target = NewLIR0(kPseudoTargetLabel); + slow_unlock_branch->target = slow_path_target; + if (null_check_branch != nullptr) { + null_check_branch->target = slow_path_target; + } + // TODO: move to a slow path. + // Go expensive route - artUnlockObjectFromCode(obj); + LoadWordDisp(rARM_SELF, QUICK_ENTRYPOINT_OFFSET(pUnlockObject).Int32Value(), rARM_LR); + ClobberCalleeSave(); + LIR* call_inst = OpReg(kOpBlx, rARM_LR); + MarkSafepointPC(call_inst); + + LIR* success_target = NewLIR0(kPseudoTargetLabel); + unlock_success_branch->target = success_target; + GenMemBarrier(kStoreLoad); + } else { + // Explicit null-check as slow-path is entered using an IT. + GenNullCheck(rl_src.s_reg_low, r0, opt_flags); + LoadWordDisp(r0, mirror::Object::MonitorOffset().Int32Value(), r1); // Get lock + LoadWordDisp(rARM_SELF, Thread::ThinLockIdOffset().Int32Value(), r2); + LoadConstantNoClobber(r3, 0); + // Is lock unheld on lock or held by us (==thread_id) on unlock? + OpRegReg(kOpCmp, r1, r2); + OpIT(kCondEq, "EE"); + StoreWordDisp/*eq*/(r0, mirror::Object::MonitorOffset().Int32Value(), r3); + // Go expensive route - UnlockObjectFromCode(obj); + LoadWordDisp/*ne*/(rARM_SELF, QUICK_ENTRYPOINT_OFFSET(pUnlockObject).Int32Value(), rARM_LR); + ClobberCalleeSave(); + LIR* call_inst = OpReg(kOpBlx/*ne*/, rARM_LR); + MarkSafepointPC(call_inst); + GenMemBarrier(kStoreLoad); + } } void ArmMir2Lir::GenMoveException(RegLocation rl_dest) { diff --git a/compiler/dex/quick/arm/int_arm.cc b/compiler/dex/quick/arm/int_arm.cc index 07782d957f9..4fa038763c5 100644 --- a/compiler/dex/quick/arm/int_arm.cc +++ b/compiler/dex/quick/arm/int_arm.cc @@ -24,8 +24,7 @@ namespace art { -LIR* ArmMir2Lir::OpCmpBranch(ConditionCode cond, int src1, - int src2, LIR* target) { +LIR* ArmMir2Lir::OpCmpBranch(ConditionCode cond, int src1, int src2, LIR* target) { OpRegReg(kOpCmp, src1, src2); return OpCondBranch(cond, target); } diff --git a/compiler/dex/quick/gen_common.cc b/compiler/dex/quick/gen_common.cc index 4dd55d763a1..f38225a934b 100644 --- a/compiler/dex/quick/gen_common.cc +++ b/compiler/dex/quick/gen_common.cc @@ -1761,4 +1761,16 @@ void Mir2Lir::GenSuspendTestAndBranch(int opt_flags, LIR* target) { suspend_launchpads_.Insert(launch_pad); } +/* Call out to helper assembly routine that will null check obj and then lock it. */ +void Mir2Lir::GenMonitorEnter(int opt_flags, RegLocation rl_src) { + FlushAllRegs(); + CallRuntimeHelperRegLocation(QUICK_ENTRYPOINT_OFFSET(pLockObject), rl_src, true); +} + +/* Call out to helper assembly routine that will null check obj and then unlock it. */ +void Mir2Lir::GenMonitorExit(int opt_flags, RegLocation rl_src) { + FlushAllRegs(); + CallRuntimeHelperRegLocation(QUICK_ENTRYPOINT_OFFSET(pUnlockObject), rl_src, true); +} + } // namespace art diff --git a/compiler/dex/quick/mips/call_mips.cc b/compiler/dex/quick/mips/call_mips.cc index d53c0124661..9a5ca2ce67f 100644 --- a/compiler/dex/quick/mips/call_mips.cc +++ b/compiler/dex/quick/mips/call_mips.cc @@ -261,36 +261,6 @@ void MipsMir2Lir::GenFillArrayData(uint32_t table_offset, RegLocation rl_src) { MarkSafepointPC(call_inst); } -/* - * TODO: implement fast path to short-circuit thin-lock case - */ -void MipsMir2Lir::GenMonitorEnter(int opt_flags, RegLocation rl_src) { - FlushAllRegs(); - LoadValueDirectFixed(rl_src, rMIPS_ARG0); // Get obj - LockCallTemps(); // Prepare for explicit register usage - GenNullCheck(rl_src.s_reg_low, rMIPS_ARG0, opt_flags); - // Go expensive route - artLockObjectFromCode(self, obj); - int r_tgt = LoadHelper(QUICK_ENTRYPOINT_OFFSET(pLockObject)); - ClobberCalleeSave(); - LIR* call_inst = OpReg(kOpBlx, r_tgt); - MarkSafepointPC(call_inst); -} - -/* - * TODO: implement fast path to short-circuit thin-lock case - */ -void MipsMir2Lir::GenMonitorExit(int opt_flags, RegLocation rl_src) { - FlushAllRegs(); - LoadValueDirectFixed(rl_src, rMIPS_ARG0); // Get obj - LockCallTemps(); // Prepare for explicit register usage - GenNullCheck(rl_src.s_reg_low, rMIPS_ARG0, opt_flags); - // Go expensive route - UnlockObjectFromCode(obj); - int r_tgt = LoadHelper(QUICK_ENTRYPOINT_OFFSET(pUnlockObject)); - ClobberCalleeSave(); - LIR* call_inst = OpReg(kOpBlx, r_tgt); - MarkSafepointPC(call_inst); -} - void MipsMir2Lir::GenMoveException(RegLocation rl_dest) { int ex_offset = Thread::ExceptionOffset().Int32Value(); RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true); @@ -318,6 +288,7 @@ void MipsMir2Lir::MarkGCCard(int val_reg, int tgt_addr_reg) { FreeTemp(reg_card_base); FreeTemp(reg_card_no); } + void MipsMir2Lir::GenEntrySequence(RegLocation* ArgLocs, RegLocation rl_method) { int spill_count = num_core_spills_ + num_fp_spills_; /* diff --git a/compiler/dex/quick/mips/codegen_mips.h b/compiler/dex/quick/mips/codegen_mips.h index 8d0b347a34c..95b2e77dd9f 100644 --- a/compiler/dex/quick/mips/codegen_mips.h +++ b/compiler/dex/quick/mips/codegen_mips.h @@ -123,8 +123,6 @@ class MipsMir2Lir : public Mir2Lir { void GenFusedLongCmpBranch(BasicBlock* bb, MIR* mir); void GenSelect(BasicBlock* bb, MIR* mir); void GenMemBarrier(MemBarrierKind barrier_kind); - void GenMonitorEnter(int opt_flags, RegLocation rl_src); - void GenMonitorExit(int opt_flags, RegLocation rl_src); void GenMoveException(RegLocation rl_dest); void GenMultiplyByTwoBitMultiplier(RegLocation rl_src, RegLocation rl_result, int lit, int first_bit, int second_bit); diff --git a/compiler/dex/quick/mir_to_lir.h b/compiler/dex/quick/mir_to_lir.h index 7d6f968da5c..71b68fe2585 100644 --- a/compiler/dex/quick/mir_to_lir.h +++ b/compiler/dex/quick/mir_to_lir.h @@ -629,8 +629,6 @@ class Mir2Lir : public Backend { virtual void GenFusedLongCmpBranch(BasicBlock* bb, MIR* mir) = 0; virtual void GenSelect(BasicBlock* bb, MIR* mir) = 0; virtual void GenMemBarrier(MemBarrierKind barrier_kind) = 0; - virtual void GenMonitorEnter(int opt_flags, RegLocation rl_src) = 0; - virtual void GenMonitorExit(int opt_flags, RegLocation rl_src) = 0; virtual void GenMoveException(RegLocation rl_dest) = 0; virtual void GenMultiplyByTwoBitMultiplier(RegLocation rl_src, RegLocation rl_result, int lit, int first_bit, @@ -689,6 +687,10 @@ class Mir2Lir : public Backend { virtual bool InexpensiveConstantLong(int64_t value) = 0; virtual bool InexpensiveConstantDouble(int64_t value) = 0; + // May be optimized by targets. + virtual void GenMonitorEnter(int opt_flags, RegLocation rl_src); + virtual void GenMonitorExit(int opt_flags, RegLocation rl_src); + // Temp workaround void Workaround7250540(RegLocation rl_dest, int value); diff --git a/compiler/dex/quick/x86/call_x86.cc b/compiler/dex/quick/x86/call_x86.cc index 2be2aa9a0ec..7fad6f07d1d 100644 --- a/compiler/dex/quick/x86/call_x86.cc +++ b/compiler/dex/quick/x86/call_x86.cc @@ -150,43 +150,6 @@ void X86Mir2Lir::GenFillArrayData(uint32_t table_offset, RegLocation rl_src) { rX86_ARG1, true); } -void X86Mir2Lir::GenMonitorEnter(int opt_flags, RegLocation rl_src) { - FlushAllRegs(); - LoadValueDirectFixed(rl_src, rCX); // Get obj - LockCallTemps(); // Prepare for explicit register usage - GenNullCheck(rl_src.s_reg_low, rCX, opt_flags); - // If lock is unheld, try to grab it quickly with compare and exchange - // TODO: copy and clear hash state? - NewLIR2(kX86Mov32RT, rDX, Thread::ThinLockIdOffset().Int32Value()); - NewLIR2(kX86Sal32RI, rDX, LW_LOCK_OWNER_SHIFT); - NewLIR2(kX86Xor32RR, rAX, rAX); - NewLIR3(kX86LockCmpxchgMR, rCX, mirror::Object::MonitorOffset().Int32Value(), rDX); - LIR* branch = NewLIR2(kX86Jcc8, 0, kX86CondEq); - // If lock is held, go the expensive route - artLockObjectFromCode(self, obj); - CallRuntimeHelperReg(QUICK_ENTRYPOINT_OFFSET(pLockObject), rCX, true); - branch->target = NewLIR0(kPseudoTargetLabel); -} - -void X86Mir2Lir::GenMonitorExit(int opt_flags, RegLocation rl_src) { - FlushAllRegs(); - LoadValueDirectFixed(rl_src, rAX); // Get obj - LockCallTemps(); // Prepare for explicit register usage - GenNullCheck(rl_src.s_reg_low, rAX, opt_flags); - // If lock is held by the current thread, clear it to quickly release it - // TODO: clear hash state? - NewLIR2(kX86Mov32RT, rDX, Thread::ThinLockIdOffset().Int32Value()); - NewLIR2(kX86Sal32RI, rDX, LW_LOCK_OWNER_SHIFT); - NewLIR3(kX86Mov32RM, rCX, rAX, mirror::Object::MonitorOffset().Int32Value()); - OpRegReg(kOpSub, rCX, rDX); - LIR* branch = NewLIR2(kX86Jcc8, 0, kX86CondNe); - NewLIR3(kX86Mov32MR, rAX, mirror::Object::MonitorOffset().Int32Value(), rCX); - LIR* branch2 = NewLIR1(kX86Jmp8, 0); - branch->target = NewLIR0(kPseudoTargetLabel); - // Otherwise, go the expensive route - UnlockObjectFromCode(obj); - CallRuntimeHelperReg(QUICK_ENTRYPOINT_OFFSET(pUnlockObject), rAX, true); - branch2->target = NewLIR0(kPseudoTargetLabel); -} - void X86Mir2Lir::GenMoveException(RegLocation rl_dest) { int ex_offset = Thread::ExceptionOffset().Int32Value(); RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true); diff --git a/compiler/dex/quick/x86/codegen_x86.h b/compiler/dex/quick/x86/codegen_x86.h index 0f281106b27..29fb3a519f1 100644 --- a/compiler/dex/quick/x86/codegen_x86.h +++ b/compiler/dex/quick/x86/codegen_x86.h @@ -123,8 +123,6 @@ class X86Mir2Lir : public Mir2Lir { void GenFusedLongCmpBranch(BasicBlock* bb, MIR* mir); void GenSelect(BasicBlock* bb, MIR* mir); void GenMemBarrier(MemBarrierKind barrier_kind); - void GenMonitorEnter(int opt_flags, RegLocation rl_src); - void GenMonitorExit(int opt_flags, RegLocation rl_src); void GenMoveException(RegLocation rl_dest); void GenMultiplyByTwoBitMultiplier(RegLocation rl_src, RegLocation rl_result, int lit, int first_bit, int second_bit); diff --git a/compiler/image_test.cc b/compiler/image_test.cc index 6464a4c78e2..d4be7c0cdc8 100644 --- a/compiler/image_test.cc +++ b/compiler/image_test.cc @@ -23,6 +23,8 @@ #include "compiler/oat_writer.h" #include "gc/space/image_space.h" #include "image.h" +#include "lock_word.h" +#include "mirror/object-inl.h" #include "signal_catcher.h" #include "UniquePtr.h" #include "utils.h" @@ -158,7 +160,7 @@ TEST_F(ImageTest, WriteRead) { // non image classes should be in a space after the image. EXPECT_GT(reinterpret_cast(klass), image_end) << descriptor; } - EXPECT_TRUE(Monitor::IsValidLockWord(*klass->GetRawLockWordAddress())); + EXPECT_TRUE(Monitor::IsValidLockWord(klass->GetLockWord())); } } diff --git a/compiler/llvm/llvm_compilation_unit.cc b/compiler/llvm/llvm_compilation_unit.cc index aa439ccbaed..feb495e35fe 100644 --- a/compiler/llvm/llvm_compilation_unit.cc +++ b/compiler/llvm/llvm_compilation_unit.cc @@ -82,7 +82,6 @@ #include "ir_builder.h" #include "os.h" #include "runtime_support_builder_arm.h" -#include "runtime_support_builder_thumb2.h" #include "runtime_support_builder_x86.h" #include "utils_llvm.h" @@ -118,12 +117,10 @@ LlvmCompilationUnit::LlvmCompilationUnit(const CompilerLLVM* compiler_llvm, size default: runtime_support_.reset(new RuntimeSupportBuilder(*context_, *module_, *irb_)); break; + case kThumb2: case kArm: runtime_support_.reset(new RuntimeSupportBuilderARM(*context_, *module_, *irb_)); break; - case kThumb2: - runtime_support_.reset(new RuntimeSupportBuilderThumb2(*context_, *module_, *irb_)); - break; case kX86: runtime_support_.reset(new RuntimeSupportBuilderX86(*context_, *module_, *irb_)); break; diff --git a/compiler/llvm/runtime_support_builder.cc b/compiler/llvm/runtime_support_builder.cc index 24e283d3097..c825fbf190a 100644 --- a/compiler/llvm/runtime_support_builder.cc +++ b/compiler/llvm/runtime_support_builder.cc @@ -164,89 +164,13 @@ void RuntimeSupportBuilder::EmitTestSuspend() { /* Monitor */ void RuntimeSupportBuilder::EmitLockObject(::llvm::Value* object) { - Value* monitor = - irb_.LoadFromObjectOffset(object, - mirror::Object::MonitorOffset().Int32Value(), - irb_.getJIntTy(), - kTBAARuntimeInfo); - - Value* real_monitor = - irb_.CreateAnd(monitor, ~(LW_HASH_STATE_MASK << LW_HASH_STATE_SHIFT)); - - // Is thin lock, unheld and not recursively acquired. - Value* unheld = irb_.CreateICmpEQ(real_monitor, irb_.getInt32(0)); - - Function* parent_func = irb_.GetInsertBlock()->getParent(); - BasicBlock* bb_fast = BasicBlock::Create(context_, "lock_fast", parent_func); - BasicBlock* bb_slow = BasicBlock::Create(context_, "lock_slow", parent_func); - BasicBlock* bb_cont = BasicBlock::Create(context_, "lock_cont", parent_func); - irb_.CreateCondBr(unheld, bb_fast, bb_slow, kLikely); - - irb_.SetInsertPoint(bb_fast); - - // Calculate new monitor: new = old | (lock_id << LW_LOCK_OWNER_SHIFT) - Value* lock_id = - EmitLoadFromThreadOffset(Thread::ThinLockIdOffset().Int32Value(), - irb_.getInt32Ty(), kTBAARuntimeInfo); - - Value* owner = irb_.CreateShl(lock_id, LW_LOCK_OWNER_SHIFT); - Value* new_monitor = irb_.CreateOr(monitor, owner); - - // Atomically update monitor. - Value* old_monitor = - irb_.CompareExchangeObjectOffset(object, - mirror::Object::MonitorOffset().Int32Value(), - monitor, new_monitor, kTBAARuntimeInfo); - - Value* retry_slow_path = irb_.CreateICmpEQ(old_monitor, monitor); - irb_.CreateCondBr(retry_slow_path, bb_cont, bb_slow, kLikely); - - irb_.SetInsertPoint(bb_slow); Function* slow_func = GetRuntimeSupportFunction(runtime_support::LockObject); irb_.CreateCall2(slow_func, object, EmitGetCurrentThread()); - irb_.CreateBr(bb_cont); - - irb_.SetInsertPoint(bb_cont); } void RuntimeSupportBuilder::EmitUnlockObject(::llvm::Value* object) { - Value* lock_id = - EmitLoadFromThreadOffset(Thread::ThinLockIdOffset().Int32Value(), - irb_.getJIntTy(), - kTBAARuntimeInfo); - Value* monitor = - irb_.LoadFromObjectOffset(object, - mirror::Object::MonitorOffset().Int32Value(), - irb_.getJIntTy(), - kTBAARuntimeInfo); - - Value* my_monitor = irb_.CreateShl(lock_id, LW_LOCK_OWNER_SHIFT); - Value* hash_state = irb_.CreateAnd(monitor, (LW_HASH_STATE_MASK << LW_HASH_STATE_SHIFT)); - Value* real_monitor = irb_.CreateAnd(monitor, ~(LW_HASH_STATE_MASK << LW_HASH_STATE_SHIFT)); - - // Is thin lock, held by us and not recursively acquired - Value* is_fast_path = irb_.CreateICmpEQ(real_monitor, my_monitor); - - Function* parent_func = irb_.GetInsertBlock()->getParent(); - BasicBlock* bb_fast = BasicBlock::Create(context_, "unlock_fast", parent_func); - BasicBlock* bb_slow = BasicBlock::Create(context_, "unlock_slow", parent_func); - BasicBlock* bb_cont = BasicBlock::Create(context_, "unlock_cont", parent_func); - irb_.CreateCondBr(is_fast_path, bb_fast, bb_slow, kLikely); - - irb_.SetInsertPoint(bb_fast); - // Set all bits to zero (except hash state) - irb_.StoreToObjectOffset(object, - mirror::Object::MonitorOffset().Int32Value(), - hash_state, - kTBAARuntimeInfo); - irb_.CreateBr(bb_cont); - - irb_.SetInsertPoint(bb_slow); Function* slow_func = GetRuntimeSupportFunction(runtime_support::UnlockObject); irb_.CreateCall2(slow_func, object, EmitGetCurrentThread()); - irb_.CreateBr(bb_cont); - - irb_.SetInsertPoint(bb_cont); } diff --git a/compiler/llvm/runtime_support_builder.h b/compiler/llvm/runtime_support_builder.h index e92ac0a9089..898611af758 100644 --- a/compiler/llvm/runtime_support_builder.h +++ b/compiler/llvm/runtime_support_builder.h @@ -64,8 +64,8 @@ class RuntimeSupportBuilder { virtual void EmitTestSuspend(); /* Monitor */ - virtual void EmitLockObject(::llvm::Value* object); - virtual void EmitUnlockObject(::llvm::Value* object); + void EmitLockObject(::llvm::Value* object); + void EmitUnlockObject(::llvm::Value* object); /* MarkGCCard */ virtual void EmitMarkGCCard(::llvm::Value* value, ::llvm::Value* target_addr); diff --git a/compiler/llvm/runtime_support_builder_arm.cc b/compiler/llvm/runtime_support_builder_arm.cc index 569d825272f..cad46247fdd 100644 --- a/compiler/llvm/runtime_support_builder_arm.cc +++ b/compiler/llvm/runtime_support_builder_arm.cc @@ -116,24 +116,5 @@ Value* RuntimeSupportBuilderARM::EmitSetCurrentThread(Value* thread) { return old_thread_register; } - -/* Monitor */ - -void RuntimeSupportBuilderARM::EmitLockObject(Value* object) { - RuntimeSupportBuilder::EmitLockObject(object); - FunctionType* func_ty = FunctionType::get(/*Result=*/Type::getVoidTy(context_), - /*isVarArg=*/false); - InlineAsm* func = InlineAsm::get(func_ty, "dmb sy", "", true); - irb_.CreateCall(func); -} - -void RuntimeSupportBuilderARM::EmitUnlockObject(Value* object) { - RuntimeSupportBuilder::EmitUnlockObject(object); - FunctionType* func_ty = FunctionType::get(/*Result=*/Type::getVoidTy(context_), - /*isVarArg=*/false); - InlineAsm* func = InlineAsm::get(func_ty, "dmb sy", "", true); - irb_.CreateCall(func); -} - } // namespace llvm } // namespace art diff --git a/compiler/llvm/runtime_support_builder_arm.h b/compiler/llvm/runtime_support_builder_arm.h index 5a353d7f30f..0d01509be0a 100644 --- a/compiler/llvm/runtime_support_builder_arm.h +++ b/compiler/llvm/runtime_support_builder_arm.h @@ -34,10 +34,6 @@ class RuntimeSupportBuilderARM : public RuntimeSupportBuilder { virtual void EmitStoreToThreadOffset(int64_t offset, ::llvm::Value* value, TBAASpecialType s_ty); virtual ::llvm::Value* EmitSetCurrentThread(::llvm::Value* thread); - - /* Monitor */ - virtual void EmitLockObject(::llvm::Value* object); - virtual void EmitUnlockObject(::llvm::Value* object); }; } // namespace llvm diff --git a/compiler/llvm/runtime_support_builder_thumb2.cc b/compiler/llvm/runtime_support_builder_thumb2.cc deleted file mode 100644 index eff29c8b042..00000000000 --- a/compiler/llvm/runtime_support_builder_thumb2.cc +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Copyright (C) 2012 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 "runtime_support_builder_thumb2.h" - -#include "ir_builder.h" -#include "mirror/object.h" -#include "monitor.h" -#include "thread.h" -#include "utils_llvm.h" - -#include -#include -#include -#include -#include - -#include -#include - -using ::llvm::BasicBlock; -using ::llvm::Function; -using ::llvm::FunctionType; -using ::llvm::InlineAsm; -using ::llvm::Type; -using ::llvm::Value; - -namespace art { -namespace llvm { - - -void RuntimeSupportBuilderThumb2::EmitLockObject(Value* object) { - FunctionType* func_ty = FunctionType::get(/*Result=*/irb_.getInt32Ty(), - /*Params=*/irb_.getJObjectTy(), - /*isVarArg=*/false); - // $0: result - // $1: object - // $2: temp - // $3: temp - std::string asms; - StringAppendF(&asms, "add $3, $1, #%" PRId32 "\n", mirror::Object::MonitorOffset().Int32Value()); - StringAppendF(&asms, "ldr $2, [r9, #%" PRId32 "]\n", Thread::ThinLockIdOffset().Int32Value()); - StringAppendF(&asms, "ldrex $0, [$3]\n"); - StringAppendF(&asms, "lsl $2, $2, %d\n", LW_LOCK_OWNER_SHIFT); - StringAppendF(&asms, "bfi $2, $0, #0, #%d\n", LW_LOCK_OWNER_SHIFT - 1); - StringAppendF(&asms, "bfc $0, #%d, #%d\n", LW_HASH_STATE_SHIFT, LW_LOCK_OWNER_SHIFT - 1); - StringAppendF(&asms, "cmp $0, #0\n"); - StringAppendF(&asms, "it eq\n"); - StringAppendF(&asms, "strexeq $0, $2, [$3]\n"); - - InlineAsm* func = InlineAsm::get(func_ty, asms, "=&l,l,~l,~l", true); - - Value* retry_slow_path = irb_.CreateCall(func, object); - retry_slow_path = irb_.CreateICmpNE(retry_slow_path, irb_.getJInt(0)); - - Function* parent_func = irb_.GetInsertBlock()->getParent(); - BasicBlock* basic_block_lock = BasicBlock::Create(context_, "lock", parent_func); - BasicBlock* basic_block_cont = BasicBlock::Create(context_, "lock_cont", parent_func); - irb_.CreateCondBr(retry_slow_path, basic_block_lock, basic_block_cont, kUnlikely); - - irb_.SetInsertPoint(basic_block_lock); - Function* slow_func = GetRuntimeSupportFunction(runtime_support::LockObject); - irb_.CreateCall2(slow_func, object, EmitGetCurrentThread()); - irb_.CreateBr(basic_block_cont); - - irb_.SetInsertPoint(basic_block_cont); - { // Memory barrier - FunctionType* asm_ty = FunctionType::get(/*Result=*/Type::getVoidTy(context_), - /*isVarArg=*/false); - InlineAsm* func = InlineAsm::get(asm_ty, "dmb sy", "", true); - irb_.CreateCall(func); - } -} - - -} // namespace llvm -} // namespace art diff --git a/compiler/llvm/runtime_support_builder_thumb2.h b/compiler/llvm/runtime_support_builder_thumb2.h deleted file mode 100644 index c47a2744ef1..00000000000 --- a/compiler/llvm/runtime_support_builder_thumb2.h +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (C) 2012 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef ART_COMPILER_LLVM_RUNTIME_SUPPORT_BUILDER_THUMB2_H_ -#define ART_COMPILER_LLVM_RUNTIME_SUPPORT_BUILDER_THUMB2_H_ - -#include "runtime_support_builder_arm.h" - -namespace art { -namespace llvm { - -class RuntimeSupportBuilderThumb2 : public RuntimeSupportBuilderARM { - public: - RuntimeSupportBuilderThumb2(::llvm::LLVMContext& context, ::llvm::Module& module, IRBuilder& irb) - : RuntimeSupportBuilderARM(context, module, irb) {} - - /* Monitor */ - virtual void EmitLockObject(::llvm::Value* object); -}; - -} // namespace llvm -} // namespace art - -#endif // ART_COMPILER_LLVM_RUNTIME_SUPPORT_BUILDER_THUMB2_H_ diff --git a/dalvikvm/Android.mk b/dalvikvm/Android.mk index 52584cf5e66..a0463918856 100644 --- a/dalvikvm/Android.mk +++ b/dalvikvm/Android.mk @@ -38,7 +38,7 @@ LOCAL_CPP_EXTENSION := cc LOCAL_SRC_FILES := dalvikvm.cc LOCAL_CFLAGS := $(dalvikvm_cflags) LOCAL_SHARED_LIBRARIES := libnativehelper -LOCAL_LDFLAGS := -ldl +LOCAL_LDFLAGS := -ldl -lpthread LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk include $(BUILD_HOST_EXECUTABLE) ART_HOST_EXECUTABLES += $(HOST_OUT_EXECUTABLES)/$(LOCAL_MODULE) diff --git a/runtime/Android.mk b/runtime/Android.mk index 5edf7592d94..d8abbf1a2a7 100644 --- a/runtime/Android.mk +++ b/runtime/Android.mk @@ -243,6 +243,7 @@ LIBART_ENUM_OPERATOR_OUT_HEADER_FILES := \ jdwp/jdwp.h \ jdwp/jdwp_constants.h \ locks.h \ + lock_word.h \ mirror/class.h \ thread.h \ thread_state.h \ diff --git a/runtime/arch/arm/asm_support_arm.h b/runtime/arch/arm/asm_support_arm.h index ed3d476b242..69fb9c3f528 100644 --- a/runtime/arch/arm/asm_support_arm.h +++ b/runtime/arch/arm/asm_support_arm.h @@ -27,5 +27,7 @@ #define THREAD_FLAGS_OFFSET 0 // Offset of field Thread::exception_ verified in InitCpu #define THREAD_EXCEPTION_OFFSET 12 +// Offset of field Thread::thin_lock_thread_id_ verified in InitCpu +#define THREAD_ID_OFFSET 60 #endif // ART_RUNTIME_ARCH_ARM_ASM_SUPPORT_ARM_H_ diff --git a/runtime/arch/arm/quick_entrypoints_arm.S b/runtime/arch/arm/quick_entrypoints_arm.S index 5b2dd6c7333..cb61698751d 100644 --- a/runtime/arch/arm/quick_entrypoints_arm.S +++ b/runtime/arch/arm/quick_entrypoints_arm.S @@ -318,22 +318,67 @@ ENTRY art_quick_handle_fill_data END art_quick_handle_fill_data /* - * Entry from managed code that calls artLockObjectFromCode, may block for GC. + * Entry from managed code that calls artLockObjectFromCode, may block for GC. r0 holds the + * possibly null object to lock. */ .extern artLockObjectFromCode ENTRY art_quick_lock_object + cbz r0, slow_lock +retry_lock: + ldrex r1, [r0, #LOCK_WORD_OFFSET] + ldrt r2, [r9, #THREAD_ID_OFFSET] + cmp r1, #0 + bmi slow_lock @ lock word contains a monitor + bne already_thin + @ unlocked case - r2 holds thread id with count of 0 + strex r3, r2, [r0, #LOCK_WORD_OFFSET] + cbnz r3, strex_fail @ store failed, retry + bx lr +strex_fail: + b retry_lock @ unlikely forward branch, need to reload and recheck r1/r2 +already_thin: + eor r2, r1, r2 @ lock_word.ThreadId() ^ self->ThreadId() + uxth r2, r2 @ zero top 16 bits + cbnz r2, slow_lock @ lock word and self thread id's match -> recursive lock + @ else contention, go to slow path + adds r2, r1, #65536 @ increment count in lock word placing in r2 for storing + bmi slow_lock @ if we overflow the count go slow + str r2, [r0, #LOCK_WORD_OFFSET] @ no need for strex as we hold the lock + bx lr +slow_lock: SETUP_REF_ONLY_CALLEE_SAVE_FRAME @ save callee saves in case we block mov r1, r9 @ pass Thread::Current mov r2, sp @ pass SP bl artLockObjectFromCode @ (Object* obj, Thread*, SP) - RESTORE_REF_ONLY_CALLEE_SAVE_FRAME_AND_RETURN + RESTORE_REF_ONLY_CALLEE_SAVE_FRAME + RETURN_IF_RESULT_IS_ZERO + DELIVER_PENDING_EXCEPTION END art_quick_lock_object /* * Entry from managed code that calls artUnlockObjectFromCode and delivers exception on failure. + * r0 holds the possibly null object to lock. */ .extern artUnlockObjectFromCode ENTRY art_quick_unlock_object + cbz r0, slow_unlock + ldr r1, [r0, #LOCK_WORD_OFFSET] + ldr r2, [r9, #THREAD_ID_OFFSET] + cmp r1, #0 + bmi slow_unlock @ lock word contains a monitor + eor r3, r1, r2 @ lock_word.ThreadId() ^ self->ThreadId() + uxth r3, r3 @ zero top 16 bits + cbnz r3, slow_unlock @ do lock word and self thread id's match? + cmp r1, #65536 + bpl recursive_thin_unlock + @ transition to unlocked, r3 holds 0 + str r3, [r0, #LOCK_WORD_OFFSET] + bx lr +recursive_thin_unlock: + sub r1, r1, #65536 + str r1, [r0, #LOCK_WORD_OFFSET] + bx lr +slow_unlock: SETUP_REF_ONLY_CALLEE_SAVE_FRAME @ save callee saves in case exception allocation triggers GC mov r1, r9 @ pass Thread::Current mov r2, sp @ pass SP diff --git a/runtime/arch/arm/thread_arm.cc b/runtime/arch/arm/thread_arm.cc index ea908be22ca..75eef60de24 100644 --- a/runtime/arch/arm/thread_arm.cc +++ b/runtime/arch/arm/thread_arm.cc @@ -24,6 +24,7 @@ namespace art { void Thread::InitCpu() { CHECK_EQ(THREAD_FLAGS_OFFSET, OFFSETOF_MEMBER(Thread, state_and_flags_)); CHECK_EQ(THREAD_EXCEPTION_OFFSET, OFFSETOF_MEMBER(Thread, exception_)); + CHECK_EQ(THREAD_ID_OFFSET, OFFSETOF_MEMBER(Thread, thin_lock_thread_id_)); } } // namespace art diff --git a/runtime/arch/x86/asm_support_x86.h b/runtime/arch/x86/asm_support_x86.h index 1092910d789..d4e092753f0 100644 --- a/runtime/arch/x86/asm_support_x86.h +++ b/runtime/arch/x86/asm_support_x86.h @@ -23,5 +23,7 @@ #define THREAD_SELF_OFFSET 40 // Offset of field Thread::exception_ verified in InitCpu #define THREAD_EXCEPTION_OFFSET 12 +// Offset of field Thread::thin_lock_thread_id_ verified in InitCpu +#define THREAD_ID_OFFSET 60 #endif // ART_RUNTIME_ARCH_X86_ASM_SUPPORT_X86_H_ diff --git a/runtime/arch/x86/quick_entrypoints_x86.S b/runtime/arch/x86/quick_entrypoints_x86.S index 06b220391f2..6be73d1aa16 100644 --- a/runtime/arch/x86/quick_entrypoints_x86.S +++ b/runtime/arch/x86/quick_entrypoints_x86.S @@ -401,14 +401,85 @@ TWO_ARG_DOWNCALL art_quick_initialize_static_storage, artInitializeStaticStorage TWO_ARG_DOWNCALL art_quick_initialize_type, artInitializeTypeFromCode, RETURN_IF_EAX_NOT_ZERO TWO_ARG_DOWNCALL art_quick_initialize_type_and_verify_access, artInitializeTypeAndVerifyAccessFromCode, RETURN_IF_EAX_NOT_ZERO -ONE_ARG_DOWNCALL art_quick_lock_object, artLockObjectFromCode, ret -ONE_ARG_DOWNCALL art_quick_unlock_object, artUnlockObjectFromCode, RETURN_IF_EAX_ZERO - TWO_ARG_DOWNCALL art_quick_handle_fill_data, artHandleFillArrayDataFromCode, RETURN_IF_EAX_ZERO +DEFINE_FUNCTION art_quick_lock_object + testl %eax, %eax // null check object/eax + jz slow_lock +retry_lock: + movl LOCK_WORD_OFFSET(%eax), %ecx // ecx := lock word + movl %fs:THREAD_ID_OFFSET, %edx // edx := thread id + test %ecx, %ecx + jb slow_lock // lock word contains a monitor + jnz already_thin // lock word contains a thin lock + // unlocked case - %edx holds thread id with count of 0 + movl %eax, %ecx // remember object in case of retry + xor %eax, %eax // eax == 0 for comparison with lock word in cmpxchg + lock cmpxchg %edx, LOCK_WORD_OFFSET(%ecx) + jnz cmpxchg_fail // cmpxchg failed retry + ret +cmpxchg_fail: + movl %ecx, %eax // restore eax + jmp retry_lock +already_thin: + cmpw %ax, %dx // do we hold the lock already? + jne slow_lock + addl LITERAL(65536), %eax // increment recursion count + jb slow_lock // count overflowed so go slow + movl %eax, LOCK_WORD_OFFSET(%ecx) // update lockword, cmpxchg not necessary as we hold lock + ret +slow_lock: + SETUP_REF_ONLY_CALLEE_SAVE_FRAME // save ref containing registers for GC + mov %esp, %edx // remember SP + // Outgoing argument set up + PUSH eax // push padding + PUSH edx // pass SP + pushl %fs:THREAD_SELF_OFFSET // pass Thread::Current() + .cfi_adjust_cfa_offset 4 + PUSH eax // pass object + call artLockObjectFromCode // artLockObjectFromCode(object, Thread*, SP) + addl MACRO_LITERAL(16), %esp // pop arguments + .cfi_adjust_cfa_offset -16 + RESTORE_REF_ONLY_CALLEE_SAVE_FRAME // restore frame up to return address + RETURN_IF_EAX_ZERO +END_FUNCTION art_quick_lock_object + +DEFINE_FUNCTION art_quick_unlock_object + testl %eax, %eax // null check object/eax + jz slow_unlock + movl LOCK_WORD_OFFSET(%eax), %ecx // ecx := lock word + movl %fs:THREAD_ID_OFFSET, %edx // edx := thread id + test %ecx, %ecx + jb slow_unlock // lock word contains a monitor + cmpw %cx, %dx // does the thread id match? + jne slow_unlock + cmpl LITERAL(65536), %ecx + jae recursive_thin_unlock + movl LITERAL(0), LOCK_WORD_OFFSET(%eax) + ret +recursive_thin_unlock: + subl LITERAL(65536), %ecx + mov %ecx, LOCK_WORD_OFFSET(%eax) + ret +slow_unlock: + SETUP_REF_ONLY_CALLEE_SAVE_FRAME // save ref containing registers for GC + mov %esp, %edx // remember SP + // Outgoing argument set up + PUSH eax // push padding + PUSH edx // pass SP + pushl %fs:THREAD_SELF_OFFSET // pass Thread::Current() + .cfi_adjust_cfa_offset 4 + PUSH eax // pass object + call artUnlockObjectFromCode // artUnlockObjectFromCode(object, Thread*, SP) + addl MACRO_LITERAL(16), %esp // pop arguments + .cfi_adjust_cfa_offset -16 + RESTORE_REF_ONLY_CALLEE_SAVE_FRAME // restore frame up to return address + RETURN_IF_EAX_ZERO +END_FUNCTION art_quick_unlock_object + DEFINE_FUNCTION art_quick_is_assignable PUSH eax // alignment padding - PUSH ecx // pass arg2 + PUSH ecx // pass arg2 PUSH eax // pass arg1 call SYMBOL(artIsAssignableFromCode) // (Class* a, Class* b, Thread*, SP) addl LITERAL(12), %esp // pop arguments diff --git a/runtime/arch/x86/thread_x86.cc b/runtime/arch/x86/thread_x86.cc index dd3e7dd1375..7e0aee086c0 100644 --- a/runtime/arch/x86/thread_x86.cc +++ b/runtime/arch/x86/thread_x86.cc @@ -134,6 +134,7 @@ void Thread::InitCpu() { // Sanity check other offsets. CHECK_EQ(THREAD_EXCEPTION_OFFSET, OFFSETOF_MEMBER(Thread, exception_)); + CHECK_EQ(THREAD_ID_OFFSET, OFFSETOF_MEMBER(Thread, thin_lock_thread_id_)); } } // namespace art diff --git a/runtime/asm_support.h b/runtime/asm_support.h index aca93a55526..d2eaf8e153c 100644 --- a/runtime/asm_support.h +++ b/runtime/asm_support.h @@ -21,6 +21,9 @@ // check. #define SUSPEND_CHECK_INTERVAL (1000) +// Offsets within java.lang.Object. +#define LOCK_WORD_OFFSET 4 + // Offsets within java.lang.String. #define STRING_VALUE_OFFSET 8 #define STRING_COUNT_OFFSET 12 diff --git a/runtime/base/mutex.cc b/runtime/base/mutex.cc index b048bbb1ec5..249f031df0a 100644 --- a/runtime/base/mutex.cc +++ b/runtime/base/mutex.cc @@ -54,17 +54,17 @@ struct AllMutexData { std::set* all_mutexes; AllMutexData() : all_mutexes(NULL) {} }; -static struct AllMutexData all_mutex_data[kAllMutexDataSize]; +static struct AllMutexData gAllMutexData[kAllMutexDataSize]; class ScopedAllMutexesLock { public: explicit ScopedAllMutexesLock(const BaseMutex* mutex) : mutex_(mutex) { - while (!all_mutex_data->all_mutexes_guard.compare_and_swap(0, reinterpret_cast(mutex))) { + while (!gAllMutexData->all_mutexes_guard.compare_and_swap(0, reinterpret_cast(mutex))) { NanoSleep(100); } } ~ScopedAllMutexesLock() { - while (!all_mutex_data->all_mutexes_guard.compare_and_swap(reinterpret_cast(mutex_), 0)) { + while (!gAllMutexData->all_mutexes_guard.compare_and_swap(reinterpret_cast(mutex_), 0)) { NanoSleep(100); } } @@ -75,7 +75,7 @@ class ScopedAllMutexesLock { BaseMutex::BaseMutex(const char* name, LockLevel level) : level_(level), name_(name) { if (kLogLockContentions) { ScopedAllMutexesLock mu(this); - std::set** all_mutexes_ptr = &all_mutex_data->all_mutexes; + std::set** all_mutexes_ptr = &gAllMutexData->all_mutexes; if (*all_mutexes_ptr == NULL) { // We leak the global set of all mutexes to avoid ordering issues in global variable // construction/destruction. @@ -88,7 +88,7 @@ BaseMutex::BaseMutex(const char* name, LockLevel level) : level_(level), name_(n BaseMutex::~BaseMutex() { if (kLogLockContentions) { ScopedAllMutexesLock mu(this); - all_mutex_data->all_mutexes->erase(this); + gAllMutexData->all_mutexes->erase(this); } } @@ -96,13 +96,13 @@ void BaseMutex::DumpAll(std::ostream& os) { if (kLogLockContentions) { os << "Mutex logging:\n"; ScopedAllMutexesLock mu(reinterpret_cast(-1)); - std::set* all_mutexes = all_mutex_data->all_mutexes; + std::set* all_mutexes = gAllMutexData->all_mutexes; if (all_mutexes == NULL) { // No mutexes have been created yet during at startup. return; } typedef std::set::const_iterator It; - os << "(Contented)\n"; + os << "(Contended)\n"; for (It it = all_mutexes->begin(); it != all_mutexes->end(); ++it) { BaseMutex* mutex = *it; if (mutex->HasEverContended()) { @@ -127,7 +127,8 @@ void BaseMutex::CheckSafeToWait(Thread* self) { return; } if (kDebugLocking) { - CHECK(self->GetHeldMutex(level_) == this) << "Waiting on unacquired mutex: " << name_; + CHECK(self->GetHeldMutex(level_) == this || level_ == kMonitorLock) + << "Waiting on unacquired mutex: " << name_; bool bad_mutexes_held = false; for (int i = kLockLevelCount - 1; i >= 0; --i) { if (i != level_) { diff --git a/runtime/base/mutex.h b/runtime/base/mutex.h index ee37388f3b8..feb8a6c6c1b 100644 --- a/runtime/base/mutex.h +++ b/runtime/base/mutex.h @@ -58,7 +58,7 @@ const bool kLogLockContentions = false; // futex. const bool kLogLockContentions = false; #endif -const size_t kContentionLogSize = 64; +const size_t kContentionLogSize = 4; const size_t kContentionLogDataSize = kLogLockContentions ? 1 : 0; const size_t kAllMutexDataSize = kLogLockContentions ? 1 : 0; diff --git a/runtime/debugger.cc b/runtime/debugger.cc index ae57aa34ec3..57bd57e61f9 100644 --- a/runtime/debugger.cc +++ b/runtime/debugger.cc @@ -674,15 +674,15 @@ JDWP::JdwpError Dbg::GetMonitorInfo(JDWP::ObjectId object_id, JDWP::ExpandBuf* r Locks::mutator_lock_->ExclusiveUnlock(self); Locks::mutator_lock_->SharedLock(self); - if (monitor_info.owner != NULL) { - expandBufAddObjectId(reply, gRegistry->Add(monitor_info.owner->GetPeer())); + if (monitor_info.owner_ != NULL) { + expandBufAddObjectId(reply, gRegistry->Add(monitor_info.owner_->GetPeer())); } else { expandBufAddObjectId(reply, gRegistry->Add(NULL)); } - expandBufAdd4BE(reply, monitor_info.entry_count); - expandBufAdd4BE(reply, monitor_info.waiters.size()); - for (size_t i = 0; i < monitor_info.waiters.size(); ++i) { - expandBufAddObjectId(reply, gRegistry->Add(monitor_info.waiters[i]->GetPeer())); + expandBufAdd4BE(reply, monitor_info.entry_count_); + expandBufAdd4BE(reply, monitor_info.waiters_.size()); + for (size_t i = 0; i < monitor_info.waiters_.size(); ++i) { + expandBufAddObjectId(reply, gRegistry->Add(monitor_info.waiters_[i]->GetPeer())); } return JDWP::ERR_NONE; } @@ -1935,7 +1935,8 @@ JDWP::JdwpError Dbg::SuspendThread(JDWP::ObjectId thread_id, bool request_suspen } // Suspend thread to build stack trace. bool timed_out; - Thread* thread = Thread::SuspendForDebugger(peer.get(), request_suspension, &timed_out); + Thread* thread = ThreadList::SuspendThreadByPeer(peer.get(), request_suspension, true, + &timed_out); if (thread != NULL) { return JDWP::ERR_NONE; } else if (timed_out) { @@ -2412,7 +2413,8 @@ class ScopedThreadSuspension { soa.Self()->TransitionFromRunnableToSuspended(kWaitingForDebuggerSuspension); jobject thread_peer = gRegistry->GetJObject(thread_id); bool timed_out; - Thread* suspended_thread = Thread::SuspendForDebugger(thread_peer, true, &timed_out); + Thread* suspended_thread = ThreadList::SuspendThreadByPeer(thread_peer, true, true, + &timed_out); CHECK_EQ(soa.Self()->TransitionFromSuspendedToRunnable(), kWaitingForDebuggerSuspension); if (suspended_thread == NULL) { // Thread terminated from under us while suspending. @@ -3012,7 +3014,7 @@ void Dbg::DdmSendThreadNotification(Thread* t, uint32_t type) { if (type == CHUNK_TYPE("THDE")) { uint8_t buf[4]; - JDWP::Set4BE(&buf[0], t->GetThinLockId()); + JDWP::Set4BE(&buf[0], t->GetThreadId()); Dbg::DdmSendChunk(CHUNK_TYPE("THDE"), 4, buf); } else { CHECK(type == CHUNK_TYPE("THCR") || type == CHUNK_TYPE("THNM")) << type; @@ -3022,7 +3024,7 @@ void Dbg::DdmSendThreadNotification(Thread* t, uint32_t type) { const jchar* chars = (name.get() != NULL) ? name->GetCharArray()->GetData() : NULL; std::vector bytes; - JDWP::Append4BE(bytes, t->GetThinLockId()); + JDWP::Append4BE(bytes, t->GetThreadId()); JDWP::AppendUtf16BE(bytes, chars, char_count); CHECK_EQ(bytes.size(), char_count*2 + sizeof(uint32_t)*2); Dbg::DdmSendChunk(type, bytes); @@ -3545,7 +3547,7 @@ void Dbg::RecordAllocation(mirror::Class* type, size_t byte_count) { AllocRecord* record = &recent_allocation_records_[gAllocRecordHead]; record->type = type; record->byte_count = byte_count; - record->thin_lock_id = self->GetThinLockId(); + record->thin_lock_id = self->GetThreadId(); // Fill in the stack trace. AllocRecordStackVisitor visitor(self, record); diff --git a/runtime/entrypoints/entrypoint_utils.h b/runtime/entrypoints/entrypoint_utils.h index e87dc96c975..e9e6c5ad4d1 100644 --- a/runtime/entrypoints/entrypoint_utils.h +++ b/runtime/entrypoints/entrypoint_utils.h @@ -378,7 +378,6 @@ static inline void CheckSuspend(Thread* thread) SHARED_LOCKS_REQUIRED(Locks::mut for (;;) { if (thread->ReadFlag(kCheckpointRequest)) { thread->RunCheckpointFunction(); - thread->AtomicClearFlag(kCheckpointRequest); } else if (thread->ReadFlag(kSuspendRequest)) { thread->FullSuspendCheck(); } else { diff --git a/runtime/entrypoints/quick/quick_lock_entrypoints.cc b/runtime/entrypoints/quick/quick_lock_entrypoints.cc index 36ca6044a21..2102ab1beae 100644 --- a/runtime/entrypoints/quick/quick_lock_entrypoints.cc +++ b/runtime/entrypoints/quick/quick_lock_entrypoints.cc @@ -15,28 +15,40 @@ */ #include "callee_save_frame.h" +#include "common_throws.h" #include "mirror/object-inl.h" namespace art { -extern "C" int artUnlockObjectFromCode(mirror::Object* obj, Thread* self, - mirror::ArtMethod** sp) - UNLOCK_FUNCTION(monitor_lock_) { +extern "C" int artLockObjectFromCode(mirror::Object* obj, Thread* self, mirror::ArtMethod** sp) + EXCLUSIVE_LOCK_FUNCTION(monitor_lock_) { FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly); - DCHECK(obj != NULL); // Assumed to have been checked before entry - // MonitorExit may throw exception - return obj->MonitorExit(self) ? 0 /* Success */ : -1 /* Failure */; + if (UNLIKELY(obj == NULL)) { + ThrowLocation throw_location(self->GetCurrentLocationForThrow()); + ThrowNullPointerException(&throw_location, + "Null reference used for synchronization (monitor-enter)"); + return -1; // Failure. + } else { + obj->MonitorEnter(self); // May block + DCHECK(self->HoldsLock(obj)); + DCHECK(!self->IsExceptionPending()); + return 0; // Success. + // Only possible exception is NPE and is handled before entry + } } -extern "C" void artLockObjectFromCode(mirror::Object* obj, Thread* thread, - mirror::ArtMethod** sp) - EXCLUSIVE_LOCK_FUNCTION(monitor_lock_) { - FinishCalleeSaveFrameSetup(thread, sp, Runtime::kRefsOnly); - DCHECK(obj != NULL); // Assumed to have been checked before entry - obj->MonitorEnter(thread); // May block - DCHECK(thread->HoldsLock(obj)); - // Only possible exception is NPE and is handled before entry - DCHECK(!thread->IsExceptionPending()); +extern "C" int artUnlockObjectFromCode(mirror::Object* obj, Thread* self, mirror::ArtMethod** sp) + UNLOCK_FUNCTION(monitor_lock_) { + FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly); + if (UNLIKELY(obj == NULL)) { + ThrowLocation throw_location(self->GetCurrentLocationForThrow()); + ThrowNullPointerException(&throw_location, + "Null reference used for synchronization (monitor-exit)"); + return -1; // Failure. + } else { + // MonitorExit may throw exception. + return obj->MonitorExit(self) ? 0 /* Success */ : -1 /* Failure */; + } } } // namespace art diff --git a/runtime/jni_internal.cc b/runtime/jni_internal.cc index 8be9b21cdf2..b1b664d481b 100644 --- a/runtime/jni_internal.cc +++ b/runtime/jni_internal.cc @@ -434,7 +434,7 @@ class SharedLibrary { class_loader_(class_loader), jni_on_load_lock_("JNI_OnLoad lock"), jni_on_load_cond_("JNI_OnLoad condition variable", jni_on_load_lock_), - jni_on_load_thread_id_(Thread::Current()->GetThinLockId()), + jni_on_load_thread_id_(Thread::Current()->GetThreadId()), jni_on_load_result_(kPending) { } @@ -459,7 +459,7 @@ class SharedLibrary { { MutexLock mu(self, jni_on_load_lock_); - if (jni_on_load_thread_id_ == self->GetThinLockId()) { + if (jni_on_load_thread_id_ == self->GetThreadId()) { // Check this so we don't end up waiting for ourselves. We need to return "true" so the // caller can continue. LOG(INFO) << *self << " recursive attempt to load library " << "\"" << path_ << "\""; diff --git a/runtime/lock_word-inl.h b/runtime/lock_word-inl.h new file mode 100644 index 00000000000..30bf9bbaa65 --- /dev/null +++ b/runtime/lock_word-inl.h @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ART_RUNTIME_LOCK_WORD_INL_H_ +#define ART_RUNTIME_LOCK_WORD_INL_H_ + +#include "lock_word.h" + +namespace art { + +inline uint32_t LockWord::ThinLockOwner() const { + DCHECK_EQ(GetState(), kThinLocked); + return (value_ >> kThinLockOwnerShift) & kThinLockOwnerMask; +} + +inline uint32_t LockWord::ThinLockCount() const { + DCHECK_EQ(GetState(), kThinLocked); + return (value_ >> kThinLockCountShift) & kThinLockCountMask; +} + +inline Monitor* LockWord::FatLockMonitor() const { + DCHECK_EQ(GetState(), kFatLocked); + return reinterpret_cast(value_ << 1); +} + +inline LockWord::LockWord() : value_(0) { + DCHECK_EQ(GetState(), kUnlocked); +} + +inline LockWord::LockWord(Monitor* mon) + : value_((reinterpret_cast(mon) >> 1) | (kStateFat << kStateShift)) { + DCHECK_EQ(FatLockMonitor(), mon); +} + +} // namespace art + +#endif // ART_RUNTIME_LOCK_WORD_INL_H_ diff --git a/runtime/lock_word.h b/runtime/lock_word.h new file mode 100644 index 00000000000..cd4bfbb9010 --- /dev/null +++ b/runtime/lock_word.h @@ -0,0 +1,128 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ART_RUNTIME_LOCK_WORD_H_ +#define ART_RUNTIME_LOCK_WORD_H_ + +#include +#include + +#include "base/logging.h" + +namespace art { +namespace mirror { + class Object; +} // namespace mirror + +class Monitor; + +/* The lock value itself as stored in mirror::Object::monitor_. The MSB of the lock encodes its + * state. When cleared, the lock is in the "thin" state and its bits are formatted as follows: + * + * |3|32222222222111|11111110000000000| + * |1|09876543210987|65432109876543210| + * |0| lock count | thread id | + * + * When set, the lock is in the "fat" state and its bits are formatted as follows: + * + * |3|3222222222211111111110000000000| + * |1|0987654321098765432109876543210| + * |1| Monitor* >> 1 | + */ +class LockWord { + public: + enum { + // Number of bits to encode the state, currently just fat or thin/unlocked. + kStateSize = 1, + // Number of bits to encode the thin lock owner. + kThinLockOwnerSize = 16, + // Remaining bits are the recursive lock count. + kThinLockCountSize = 32 - kThinLockOwnerSize - kStateSize, + + // Thin lock bits. Owner in lowest bits. + kThinLockOwnerShift = 0, + kThinLockOwnerMask = (1 << kThinLockOwnerSize) - 1, + // Count in higher bits. + kThinLockCountShift = kThinLockOwnerSize + kThinLockOwnerShift, + kThinLockCountMask = (1 << kThinLockCountShift) - 1, + kThinLockMaxCount = kThinLockCountMask, + + // State in the highest bits. + kStateShift = kThinLockCountSize + kThinLockCountShift, + kStateMask = (1 << kStateSize) - 1, + kStateThinOrUnlocked = 0, + kStateFat = 1, + }; + + static LockWord FromThinLockId(uint32_t thread_id, uint32_t count) { + CHECK_LE(thread_id, static_cast(kThinLockOwnerMask)); + return LockWord((thread_id << kThinLockOwnerShift) | (count << kThinLockCountShift)); + } + + enum LockState { + kUnlocked, // No lock owners. + kThinLocked, // Single uncontended owner. + kFatLocked // See associated monitor. + }; + + LockState GetState() const { + if (value_ == 0) { + return kUnlocked; + } else if (((value_ >> kStateShift) & kStateMask) == kStateThinOrUnlocked) { + return kThinLocked; + } else { + return kFatLocked; + } + } + + // Return the owner thin lock thread id. + uint32_t ThinLockOwner() const; + + // Return the number of times a lock value has been locked. + uint32_t ThinLockCount() const; + + // Return the Monitor encoded in a fat lock. + Monitor* FatLockMonitor() const; + + // Default constructor with no lock ownership. + LockWord(); + + // Constructor a lock word for inflation to use a Monitor. + explicit LockWord(Monitor* mon); + + bool operator==(const LockWord& rhs) { + return GetValue() == rhs.GetValue(); + } + + private: + explicit LockWord(uint32_t val) : value_(val) {} + + uint32_t GetValue() const { + return value_; + } + + // Only Object should be converting LockWords to/from uints. + friend class mirror::Object; + + // The encoded value holding all the state. + uint32_t value_; +}; +std::ostream& operator<<(std::ostream& os, const LockWord::LockState& code); + +} // namespace art + + +#endif // ART_RUNTIME_LOCK_WORD_H_ diff --git a/runtime/mirror/class.cc b/runtime/mirror/class.cc index c6db5b9a612..b16c2f71bb9 100644 --- a/runtime/mirror/class.cc +++ b/runtime/mirror/class.cc @@ -60,7 +60,7 @@ void Class::SetStatus(Status new_status, Thread* self) { } if (new_status >= kStatusResolved || old_status >= kStatusResolved) { // When classes are being resolved the resolution code should hold the lock. - CHECK_EQ(GetThinLockId(), self->GetThinLockId()) + CHECK_EQ(GetLockOwnerThreadId(), self->GetThreadId()) << "Attempt to change status of class while not holding its lock: " << PrettyClass(this) << " " << old_status << " -> " << new_status; } diff --git a/runtime/mirror/object-inl.h b/runtime/mirror/object-inl.h index 5ed3db342ca..e6591088ef2 100644 --- a/runtime/mirror/object-inl.h +++ b/runtime/mirror/object-inl.h @@ -24,6 +24,7 @@ #include "atomic.h" #include "array-inl.h" #include "class.h" +#include "lock_word-inl.h" #include "monitor.h" #include "runtime.h" #include "throwable.h" @@ -43,8 +44,21 @@ inline void Object::SetClass(Class* new_klass) { SetFieldPtr(OFFSET_OF_OBJECT_MEMBER(Object, klass_), new_klass, false, false); } -inline uint32_t Object::GetThinLockId() { - return Monitor::GetThinLockId(monitor_); +inline LockWord Object::GetLockWord() { + return LockWord(GetField32(OFFSET_OF_OBJECT_MEMBER(Object, monitor_), true)); +} + +inline void Object::SetLockWord(LockWord new_val) { + SetField32(OFFSET_OF_OBJECT_MEMBER(Object, monitor_), new_val.GetValue(), true); +} + +inline bool Object::CasLockWord(LockWord old_val, LockWord new_val) { + return CasField32(OFFSET_OF_OBJECT_MEMBER(Object, monitor_), old_val.GetValue(), + new_val.GetValue()); +} + +inline uint32_t Object::GetLockOwnerThreadId() { + return Monitor::GetLockOwnerThreadId(this); } inline void Object::MonitorEnter(Thread* self) { @@ -238,6 +252,13 @@ inline size_t Object::SizeOf() const { return result; } +inline bool Object::CasField32(MemberOffset field_offset, uint32_t old_value, uint32_t new_value) { + VerifyObject(this); + byte* raw_addr = reinterpret_cast(this) + field_offset.Int32Value(); + int32_t* addr = reinterpret_cast(raw_addr); + return android_atomic_release_cas(old_value, new_value, addr) == 0; +} + inline uint64_t Object::GetField64(MemberOffset field_offset, bool is_volatile) const { VerifyObject(this); const byte* raw_addr = reinterpret_cast(this) + field_offset.Int32Value(); diff --git a/runtime/mirror/object.h b/runtime/mirror/object.h index 003581a1c83..e3f5c101c1a 100644 --- a/runtime/mirror/object.h +++ b/runtime/mirror/object.h @@ -26,6 +26,7 @@ namespace art { class ImageWriter; +class LockWord; struct ObjectOffsets; class Thread; @@ -95,14 +96,10 @@ class MANAGED Object { return OFFSET_OF_OBJECT_MEMBER(Object, monitor_); } - volatile int32_t* GetRawLockWordAddress() { - byte* raw_addr = reinterpret_cast(this) + - OFFSET_OF_OBJECT_MEMBER(Object, monitor_).Int32Value(); - int32_t* word_addr = reinterpret_cast(raw_addr); - return const_cast(word_addr); - } - - uint32_t GetThinLockId(); + LockWord GetLockWord(); + void SetLockWord(LockWord new_val); + bool CasLockWord(LockWord old_val, LockWord new_val); + uint32_t GetLockOwnerThreadId(); void MonitorEnter(Thread* self) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) EXCLUSIVE_LOCK_FUNCTION(monitor_lock_); @@ -226,6 +223,8 @@ class MANAGED Object { } } + bool CasField32(MemberOffset field_offset, uint32_t old_value, uint32_t new_value); + uint64_t GetField64(MemberOffset field_offset, bool is_volatile) const; void SetField64(MemberOffset field_offset, uint64_t new_value, bool is_volatile); diff --git a/runtime/monitor.cc b/runtime/monitor.cc index e7ab2d49e03..1ceaa5dd99b 100644 --- a/runtime/monitor.cc +++ b/runtime/monitor.cc @@ -23,6 +23,7 @@ #include "class_linker.h" #include "dex_file-inl.h" #include "dex_instruction.h" +#include "lock_word-inl.h" #include "mirror/art_method-inl.h" #include "mirror/class-inl.h" #include "mirror/object-inl.h" @@ -37,36 +38,20 @@ namespace art { /* - * Every Object has a monitor associated with it, but not every Object is - * actually locked. Even the ones that are locked do not need a - * full-fledged monitor until a) there is actual contention or b) wait() - * is called on the Object. + * Every Object has a monitor associated with it, but not every Object is actually locked. Even + * the ones that are locked do not need a full-fledged monitor until a) there is actual contention + * or b) wait() is called on the Object. * - * For Android, we have implemented a scheme similar to the one described - * in Bacon et al.'s "Thin locks: featherweight synchronization for Java" - * (ACM 1998). Things are even easier for us, though, because we have - * a full 32 bits to work with. + * For Android, we have implemented a scheme similar to the one described in Bacon et al.'s + * "Thin locks: featherweight synchronization for Java" (ACM 1998). Things are even easier for us, + * though, because we have a full 32 bits to work with. * - * The two states of an Object's lock are referred to as "thin" and - * "fat". A lock may transition from the "thin" state to the "fat" - * state and this transition is referred to as inflation. Once a lock - * has been inflated it remains in the "fat" state indefinitely. + * The two states of an Object's lock are referred to as "thin" and "fat". A lock may transition + * from the "thin" state to the "fat" state and this transition is referred to as inflation. Once + * a lock has been inflated it remains in the "fat" state indefinitely. * - * The lock value itself is stored in Object.lock. The LSB of the - * lock encodes its state. When cleared, the lock is in the "thin" - * state and its bits are formatted as follows: - * - * [31 ---- 19] [18 ---- 3] [2 ---- 1] [0] - * lock count thread id hash state 0 - * - * When set, the lock is in the "fat" state and its bits are formatted - * as follows: - * - * [31 ---- 3] [2 ---- 1] [0] - * pointer hash state 1 - * - * For an in-depth description of the mechanics of thin-vs-fat locking, - * read the paper referred to above. + * The lock value itself is stored in mirror::Object::monitor_ and the representation is described + * in the LockWord value type. * * Monitors provide: * - mutually exclusive access to resources @@ -74,32 +59,11 @@ namespace art { * * In effect, they fill the role of both mutexes and condition variables. * - * Only one thread can own the monitor at any time. There may be several - * threads waiting on it (the wait call unlocks it). One or more waiting - * threads may be getting interrupted or notified at any given time. - * - * TODO: the various members of monitor are not SMP-safe. + * Only one thread can own the monitor at any time. There may be several threads waiting on it + * (the wait call unlocks it). One or more waiting threads may be getting interrupted or notified + * at any given time. */ -// The shape is the bottom bit; either LW_SHAPE_THIN or LW_SHAPE_FAT. -#define LW_SHAPE_MASK 0x1 -#define LW_SHAPE(x) static_cast((x) & LW_SHAPE_MASK) - -/* - * Monitor accessor. Extracts a monitor structure pointer from a fat - * lock. Performs no error checking. - */ -#define LW_MONITOR(x) \ - (reinterpret_cast((x) & ~((LW_HASH_STATE_MASK << LW_HASH_STATE_SHIFT) | LW_SHAPE_MASK))) - -/* - * Lock recursion count field. Contains a count of the number of times - * a lock has been recursively acquired. - */ -#define LW_LOCK_COUNT_MASK 0x1fff -#define LW_LOCK_COUNT_SHIFT 19 -#define LW_LOCK_COUNT(x) (((x) >> LW_LOCK_COUNT_SHIFT) & LW_LOCK_COUNT_MASK) - bool (*Monitor::is_sensitive_thread_hook_)() = NULL; uint32_t Monitor::lock_profiling_threshold_ = 0; @@ -117,29 +81,43 @@ void Monitor::Init(uint32_t lock_profiling_threshold, bool (*is_sensitive_thread Monitor::Monitor(Thread* owner, mirror::Object* obj) : monitor_lock_("a monitor lock", kMonitorLock), + monitor_contenders_("monitor contenders", monitor_lock_), owner_(owner), lock_count_(0), obj_(obj), wait_set_(NULL), locking_method_(NULL), locking_dex_pc_(0) { - monitor_lock_.Lock(owner); + // We should only inflate a lock if the owner is ourselves or suspended. This avoids a race + // with the owner unlocking the thin-lock. + CHECK(owner == Thread::Current() || owner->IsSuspended()); +} + +bool Monitor::Install(Thread* self) { + MutexLock mu(self, monitor_lock_); // Uncontended mutex acquisition as monitor isn't yet public. + CHECK(owner_ == self || owner_->IsSuspended()); // Propagate the lock state. - uint32_t thin = *obj->GetRawLockWordAddress(); - lock_count_ = LW_LOCK_COUNT(thin); - thin &= LW_HASH_STATE_MASK << LW_HASH_STATE_SHIFT; - thin |= reinterpret_cast(this) | LW_SHAPE_FAT; - // Publish the updated lock word. - android_atomic_release_store(thin, obj->GetRawLockWordAddress()); + LockWord thin(obj_->GetLockWord()); + if (thin.GetState() != LockWord::kThinLocked) { + // The owner_ is suspended but another thread beat us to install a monitor. + CHECK_EQ(thin.GetState(), LockWord::kFatLocked); + return false; + } + CHECK_EQ(owner_->GetThreadId(), thin.ThinLockOwner()); + lock_count_ = thin.ThinLockCount(); + LockWord fat(this); + // Publish the updated lock word, which may race with other threads. + bool success = obj_->CasLockWord(thin, fat); // Lock profiling. - if (lock_profiling_threshold_ != 0) { - locking_method_ = owner->GetCurrentMethod(&locking_dex_pc_); + if (success && lock_profiling_threshold_ != 0) { + locking_method_ = owner_->GetCurrentMethod(&locking_dex_pc_); } + return success; } Monitor::~Monitor() { - DCHECK(obj_ != NULL); - DCHECK_EQ(LW_SHAPE(*obj_->GetRawLockWordAddress()), LW_SHAPE_FAT); + CHECK(obj_ != NULL); + CHECK_EQ(obj_->GetLockWord().GetState(), LockWord::kFatLocked); } /* @@ -190,64 +168,56 @@ void Monitor::RemoveFromWaitSet(Thread *thread) { } } -mirror::Object* Monitor::GetObject() { - return obj_; -} - void Monitor::SetObject(mirror::Object* object) { obj_ = object; } void Monitor::Lock(Thread* self) { - if (owner_ == self) { - lock_count_++; - return; - } - - if (!monitor_lock_.TryLock(self)) { - uint64_t waitStart = 0; - uint64_t waitEnd = 0; - uint32_t wait_threshold = lock_profiling_threshold_; - const mirror::ArtMethod* current_locking_method = NULL; - uint32_t current_locking_dex_pc = 0; - { - ScopedThreadStateChange tsc(self, kBlocked); - if (wait_threshold != 0) { - waitStart = NanoTime() / 1000; - } - current_locking_method = locking_method_; - current_locking_dex_pc = locking_dex_pc_; - - monitor_lock_.Lock(self); - if (wait_threshold != 0) { - waitEnd = NanoTime() / 1000; + MutexLock mu(self, monitor_lock_); + while (true) { + if (owner_ == NULL) { // Unowned. + owner_ = self; + CHECK_EQ(lock_count_, 0); + // When debugging, save the current monitor holder for future + // acquisition failures to use in sampled logging. + if (lock_profiling_threshold_ != 0) { + locking_method_ = self->GetCurrentMethod(&locking_dex_pc_); } + return; + } else if (owner_ == self) { // Recursive. + lock_count_++; + return; } - - if (wait_threshold != 0) { - uint64_t wait_ms = (waitEnd - waitStart) / 1000; - uint32_t sample_percent; - if (wait_ms >= wait_threshold) { - sample_percent = 100; - } else { - sample_percent = 100 * wait_ms / wait_threshold; - } - if (sample_percent != 0 && (static_cast(rand() % 100) < sample_percent)) { - const char* current_locking_filename; - uint32_t current_locking_line_number; - TranslateLocation(current_locking_method, current_locking_dex_pc, - current_locking_filename, current_locking_line_number); - LogContentionEvent(self, wait_ms, sample_percent, current_locking_filename, current_locking_line_number); + // Contended. + const bool log_contention = (lock_profiling_threshold_ != 0); + uint64_t wait_start_ms = log_contention ? 0 : MilliTime(); + const mirror::ArtMethod* owners_method = locking_method_; + uint32_t owners_dex_pc = locking_dex_pc_; + monitor_lock_.Unlock(self); // Let go of locks in order. + { + ScopedThreadStateChange tsc(self, kBlocked); // Change to blocked and give up mutator_lock_. + MutexLock mu2(self, monitor_lock_); // Reacquire monitor_lock_ without mutator_lock_ for Wait. + if (owner_ != NULL) { // Did the owner_ give the lock up? + monitor_contenders_.Wait(self); // Still contended so wait. + // Woken from contention. + if (log_contention) { + uint64_t wait_ms = MilliTime() - wait_start_ms; + uint32_t sample_percent; + if (wait_ms >= lock_profiling_threshold_) { + sample_percent = 100; + } else { + sample_percent = 100 * wait_ms / lock_profiling_threshold_; + } + if (sample_percent != 0 && (static_cast(rand() % 100) < sample_percent)) { + const char* owners_filename; + uint32_t owners_line_number; + TranslateLocation(owners_method, owners_dex_pc, &owners_filename, &owners_line_number); + LogContentionEvent(self, wait_ms, sample_percent, owners_filename, owners_line_number); + } + } } } - } - owner_ = self; - DCHECK_EQ(lock_count_, 0); - - // When debugging, save the current monitor holder for future - // acquisition failures to use in sampled logging. - if (lock_profiling_threshold_ != 0) { - locking_method_ = self->GetCurrentMethod(&locking_dex_pc_); + monitor_lock_.Lock(self); // Reacquire locks in order. } } @@ -261,10 +231,11 @@ static void ThrowIllegalMonitorStateExceptionF(const char* fmt, ...) Thread* self = Thread::Current(); ThrowLocation throw_location = self->GetCurrentLocationForThrow(); self->ThrowNewExceptionV(throw_location, "Ljava/lang/IllegalMonitorStateException;", fmt, args); - if (!Runtime::Current()->IsStarted()) { + if (!Runtime::Current()->IsStarted() || VLOG_IS_ON(monitor)) { std::ostringstream ss; self->Dump(ss); - LOG(ERROR) << self->GetException(NULL)->Dump() << "\n" << ss.str(); + LOG(Runtime::Current()->IsStarted() ? INFO : ERROR) + << self->GetException(NULL)->Dump() << "\n" << ss.str(); } va_end(args); } @@ -290,7 +261,7 @@ void Monitor::FailedUnlock(mirror::Object* o, Thread* expected_owner, Thread* fo // Acquire thread list lock so threads won't disappear from under us. MutexLock mu(Thread::Current(), *Locks::thread_list_lock_); // Re-read owner now that we hold lock. - current_owner = (monitor != NULL) ? monitor->owner_ : NULL; + current_owner = (monitor != NULL) ? monitor->GetOwner() : NULL; // Get short descriptions of the threads involved. current_owner_string = ThreadToString(current_owner); expected_owner_string = ThreadToString(expected_owner); @@ -338,8 +309,9 @@ void Monitor::FailedUnlock(mirror::Object* o, Thread* expected_owner, Thread* fo } } -bool Monitor::Unlock(Thread* self, bool for_wait) { +bool Monitor::Unlock(Thread* self) { DCHECK(self != NULL); + MutexLock mu(self, monitor_lock_); Thread* owner = owner_; if (owner == self) { // We own the monitor, so nobody else can be in here. @@ -347,17 +319,11 @@ bool Monitor::Unlock(Thread* self, bool for_wait) { owner_ = NULL; locking_method_ = NULL; locking_dex_pc_ = 0; - monitor_lock_.Unlock(self); + // Wake a contender. + monitor_contenders_.Signal(self); } else { --lock_count_; } - } else if (for_wait) { - // Wait should have already cleared the fields. - DCHECK_EQ(lock_count_, 0); - DCHECK(owner == NULL); - DCHECK(locking_method_ == NULL); - DCHECK_EQ(locking_dex_pc_, 0u); - monitor_lock_.Unlock(self); } else { // We don't own this, so we're not allowed to unlock it. // The JNI spec says that we should throw IllegalMonitorStateException @@ -396,12 +362,14 @@ void Monitor::Wait(Thread* self, int64_t ms, int32_t ns, DCHECK(self != NULL); DCHECK(why == kTimedWaiting || why == kWaiting || why == kSleeping); + monitor_lock_.Lock(self); + // Make sure that we hold the lock. if (owner_ != self) { ThrowIllegalMonitorStateExceptionF("object not locked by thread before wait()"); + monitor_lock_.Unlock(self); return; } - monitor_lock_.AssertHeld(self); // We need to turn a zero-length timed wait into a regular wait because // Object.wait(0, 0) is defined as Object.wait(0), which is defined as Object.wait(). @@ -409,16 +377,12 @@ void Monitor::Wait(Thread* self, int64_t ms, int32_t ns, why = kWaiting; } - WaitWithLock(self, ms, ns, interruptShouldThrow, why); -} - -void Monitor::WaitWithLock(Thread* self, int64_t ms, int32_t ns, - bool interruptShouldThrow, ThreadState why) { // Enforce the timeout range. if (ms < 0 || ns < 0 || ns > 999999) { ThrowLocation throw_location = self->GetCurrentLocationForThrow(); self->ThrowNewExceptionF(throw_location, "Ljava/lang/IllegalArgumentException;", "timeout arguments out of range: ms=%lld ns=%d", ms, ns); + monitor_lock_.Unlock(self); return; } @@ -460,7 +424,8 @@ void Monitor::WaitWithLock(Thread* self, int64_t ms, int32_t ns, self->wait_monitor_ = this; // Release the monitor lock. - Unlock(self, true); + monitor_contenders_.Signal(self); + monitor_lock_.Unlock(self); // Handle the case where the thread was interrupted before we called wait(). if (self->interrupted_) { @@ -493,9 +458,9 @@ void Monitor::WaitWithLock(Thread* self, int64_t ms, int32_t ns, self->wait_monitor_ = NULL; } - // Re-acquire the monitor lock. + // Re-acquire the monitor and lock. Lock(self); - + monitor_lock_.Lock(self); self->wait_mutex_->AssertNotHeld(self); /* @@ -527,20 +492,17 @@ void Monitor::WaitWithLock(Thread* self, int64_t ms, int32_t ns, self->ThrowNewException(throw_location, "Ljava/lang/InterruptedException;", NULL); } } + monitor_lock_.Unlock(self); } void Monitor::Notify(Thread* self) { DCHECK(self != NULL); + MutexLock mu(self, monitor_lock_); // Make sure that we hold the lock. if (owner_ != self) { ThrowIllegalMonitorStateExceptionF("object not locked by thread before notify()"); return; } - monitor_lock_.AssertHeld(self); - NotifyWithLock(self); -} - -void Monitor::NotifyWithLock(Thread* self) { // Signal the first waiting thread in the wait set. while (wait_set_ != NULL) { Thread* thread = wait_set_; @@ -558,16 +520,12 @@ void Monitor::NotifyWithLock(Thread* self) { void Monitor::NotifyAll(Thread* self) { DCHECK(self != NULL); + MutexLock mu(self, monitor_lock_); // Make sure that we hold the lock. if (owner_ != self) { ThrowIllegalMonitorStateExceptionF("object not locked by thread before notifyAll()"); return; } - monitor_lock_.AssertHeld(self); - NotifyAllWithLock(); -} - -void Monitor::NotifyAllWithLock() { // Signal all threads in the wait set. while (wait_set_ != NULL) { Thread* thread = wait_set_; @@ -578,182 +536,130 @@ void Monitor::NotifyAllWithLock() { } /* - * Changes the shape of a monitor from thin to fat, preserving the - * internal lock state. The calling thread must own the lock. + * Changes the shape of a monitor from thin to fat, preserving the internal lock state. The calling + * thread must own the lock or the owner must be suspended. There's a race with other threads + * inflating the lock and so the caller should read the monitor following the call. */ -void Monitor::Inflate(Thread* self, mirror::Object* obj) { +void Monitor::Inflate(Thread* self, Thread* owner, mirror::Object* obj) { DCHECK(self != NULL); + DCHECK(owner != NULL); DCHECK(obj != NULL); - DCHECK_EQ(LW_SHAPE(*obj->GetRawLockWordAddress()), LW_SHAPE_THIN); - DCHECK_EQ(LW_LOCK_OWNER(*obj->GetRawLockWordAddress()), static_cast(self->GetThinLockId())); // Allocate and acquire a new monitor. - Monitor* m = new Monitor(self, obj); - VLOG(monitor) << "monitor: thread " << self->GetThinLockId() - << " created monitor " << m << " for object " << obj; - Runtime::Current()->GetMonitorList()->Add(m); + UniquePtr m(new Monitor(owner, obj)); + if (m->Install(self)) { + VLOG(monitor) << "monitor: thread " << owner->GetThreadId() + << " created monitor " << m.get() << " for object " << obj; + Runtime::Current()->GetMonitorList()->Add(m.release()); + } + CHECK_EQ(obj->GetLockWord().GetState(), LockWord::kFatLocked); } void Monitor::MonitorEnter(Thread* self, mirror::Object* obj) { - volatile int32_t* thinp = obj->GetRawLockWordAddress(); - uint32_t sleepDelayNs; - uint32_t minSleepDelayNs = 1000000; /* 1 millisecond */ - uint32_t maxSleepDelayNs = 1000000000; /* 1 second */ - uint32_t thin, newThin; - DCHECK(self != NULL); DCHECK(obj != NULL); - uint32_t threadId = self->GetThinLockId(); - retry: - thin = *thinp; - if (LW_SHAPE(thin) == LW_SHAPE_THIN) { - /* - * The lock is a thin lock. The owner field is used to - * determine the acquire method, ordered by cost. - */ - if (LW_LOCK_OWNER(thin) == threadId) { - /* - * The calling thread owns the lock. Increment the - * value of the recursion count field. - */ - *thinp += 1 << LW_LOCK_COUNT_SHIFT; - if (LW_LOCK_COUNT(*thinp) == LW_LOCK_COUNT_MASK) { - /* - * The reacquisition limit has been reached. Inflate - * the lock so the next acquire will not overflow the - * recursion count field. - */ - Inflate(self, obj); - } - } else if (LW_LOCK_OWNER(thin) == 0) { - // The lock is unowned. Install the thread id of the calling thread into the owner field. - // This is the common case: compiled code will have tried this before calling back into - // the runtime. - newThin = thin | (threadId << LW_LOCK_OWNER_SHIFT); - if (android_atomic_acquire_cas(thin, newThin, thinp) != 0) { - // The acquire failed. Try again. - goto retry; + uint32_t thread_id = self->GetThreadId(); + size_t contention_count = 0; + + while (true) { + LockWord lock_word = obj->GetLockWord(); + switch (lock_word.GetState()) { + case LockWord::kUnlocked: { + LockWord thin_locked(LockWord::FromThinLockId(thread_id, 0)); + if (obj->CasLockWord(lock_word, thin_locked)) { + return; // Success! + } + continue; // Go again. } - } else { - VLOG(monitor) << StringPrintf("monitor: thread %d spin on lock %p (a %s) owned by %d", - threadId, thinp, PrettyTypeOf(obj).c_str(), LW_LOCK_OWNER(thin)); - // The lock is owned by another thread. Notify the runtime that we are about to wait. - self->monitor_enter_object_ = obj; - self->TransitionFromRunnableToSuspended(kBlocked); - // Spin until the thin lock is released or inflated. - sleepDelayNs = 0; - for (;;) { - thin = *thinp; - // Check the shape of the lock word. Another thread - // may have inflated the lock while we were waiting. - if (LW_SHAPE(thin) == LW_SHAPE_THIN) { - if (LW_LOCK_OWNER(thin) == 0) { - // The lock has been released. Install the thread id of the - // calling thread into the owner field. - newThin = thin | (threadId << LW_LOCK_OWNER_SHIFT); - if (android_atomic_acquire_cas(thin, newThin, thinp) == 0) { - // The acquire succeed. Break out of the loop and proceed to inflate the lock. - break; - } + case LockWord::kThinLocked: { + uint32_t owner_thread_id = lock_word.ThinLockOwner(); + if (owner_thread_id == thread_id) { + // We own the lock, increase the recursion count. + uint32_t new_count = lock_word.ThinLockCount() + 1; + if (LIKELY(new_count <= LockWord::kThinLockMaxCount)) { + LockWord thin_locked(LockWord::FromThinLockId(thread_id, new_count)); + obj->SetLockWord(thin_locked); + return; // Success! + } else { + // We'd overflow the recursion count, so inflate the monitor. + Inflate(self, self, obj); + } + } else { + // Contention. + contention_count++; + if (contention_count <= Runtime::Current()->GetMaxSpinsBeforeThinkLockInflation()) { + NanoSleep(1000); // Sleep for 1us and re-attempt. } else { - // The lock has not been released. Yield so the owning thread can run. - if (sleepDelayNs == 0) { - sched_yield(); - sleepDelayNs = minSleepDelayNs; - } else { - NanoSleep(sleepDelayNs); - // Prepare the next delay value. Wrap to avoid once a second polls for eternity. - if (sleepDelayNs < maxSleepDelayNs / 2) { - sleepDelayNs *= 2; - } else { - sleepDelayNs = minSleepDelayNs; + contention_count = 0; + // Suspend the owner, inflate. First change to blocked and give up mutator_lock_. + ScopedThreadStateChange tsc(self, kBlocked); + bool timed_out; + ThreadList* thread_list = Runtime::Current()->GetThreadList(); + if (lock_word == obj->GetLockWord()) { // If lock word hasn't changed. + Thread* owner = thread_list->SuspendThreadByThreadId(lock_word.ThinLockOwner(), false, + &timed_out); + if (owner != NULL) { + // We succeeded in suspending the thread, check the lock's status didn't change. + lock_word = obj->GetLockWord(); + if (lock_word.GetState() == LockWord::kThinLocked && + lock_word.ThinLockOwner() == owner_thread_id) { + // Go ahead and inflate the lock. + Inflate(self, owner, obj); + } + thread_list->Resume(owner, false); } } } - } else { - // The thin lock was inflated by another thread. Let the runtime know we are no longer - // waiting and try again. - VLOG(monitor) << StringPrintf("monitor: thread %d found lock %p surprise-fattened by another thread", threadId, thinp); - self->monitor_enter_object_ = NULL; - self->TransitionFromSuspendedToRunnable(); - goto retry; } + continue; // Start from the beginning. + } + case LockWord::kFatLocked: { + Monitor* mon = lock_word.FatLockMonitor(); + mon->Lock(self); + return; // Success! } - VLOG(monitor) << StringPrintf("monitor: thread %d spin on lock %p done", threadId, thinp); - // We have acquired the thin lock. Let the runtime know that we are no longer waiting. - self->monitor_enter_object_ = NULL; - self->TransitionFromSuspendedToRunnable(); - // Fatten the lock. - Inflate(self, obj); - VLOG(monitor) << StringPrintf("monitor: thread %d fattened lock %p", threadId, thinp); } - } else { - // The lock is a fat lock. - VLOG(monitor) << StringPrintf("monitor: thread %d locking fat lock %p (%p) %p on a %s", - threadId, thinp, LW_MONITOR(*thinp), - reinterpret_cast(*thinp), PrettyTypeOf(obj).c_str()); - DCHECK(LW_MONITOR(*thinp) != NULL); - LW_MONITOR(*thinp)->Lock(self); } } bool Monitor::MonitorExit(Thread* self, mirror::Object* obj) { - volatile int32_t* thinp = obj->GetRawLockWordAddress(); - DCHECK(self != NULL); - // DCHECK_EQ(self->GetState(), kRunnable); DCHECK(obj != NULL); - /* - * Cache the lock word as its value can change while we are - * examining its state. - */ - uint32_t thin = *thinp; - if (LW_SHAPE(thin) == LW_SHAPE_THIN) { - /* - * The lock is thin. We must ensure that the lock is owned - * by the given thread before unlocking it. - */ - if (LW_LOCK_OWNER(thin) == self->GetThinLockId()) { - /* - * We are the lock owner. It is safe to update the lock - * without CAS as lock ownership guards the lock itself. - */ - if (LW_LOCK_COUNT(thin) == 0) { - /* - * The lock was not recursively acquired, the common - * case. Unlock by clearing all bits except for the - * hash state. - */ - thin &= (LW_HASH_STATE_MASK << LW_HASH_STATE_SHIFT); - android_atomic_release_store(thin, thinp); + LockWord lock_word = obj->GetLockWord(); + switch (lock_word.GetState()) { + case LockWord::kUnlocked: + FailedUnlock(obj, self, NULL, NULL); + return false; // Failure. + case LockWord::kThinLocked: { + uint32_t thread_id = self->GetThreadId(); + uint32_t owner_thread_id = lock_word.ThinLockOwner(); + if (owner_thread_id != thread_id) { + // TODO: there's a race here with the owner dying while we unlock. + Thread* owner = + Runtime::Current()->GetThreadList()->FindThreadByThreadId(lock_word.ThinLockOwner()); + FailedUnlock(obj, self, owner, NULL); + return false; // Failure. } else { - /* - * The object was recursively acquired. Decrement the - * lock recursion count field. - */ - *thinp -= 1 << LW_LOCK_COUNT_SHIFT; + // We own the lock, decrease the recursion count. + if (lock_word.ThinLockCount() != 0) { + uint32_t new_count = lock_word.ThinLockCount() - 1; + LockWord thin_locked(LockWord::FromThinLockId(thread_id, new_count)); + obj->SetLockWord(thin_locked); + } else { + obj->SetLockWord(LockWord()); + } + return true; // Success! } - } else { - /* - * We do not own the lock. The JVM spec requires that we - * throw an exception in this case. - */ - FailedUnlock(obj, self, NULL, NULL); - return false; } - } else { - /* - * The lock is fat. We must check to see if Unlock has - * raised any exceptions before continuing. - */ - DCHECK(LW_MONITOR(*thinp) != NULL); - if (!LW_MONITOR(*thinp)->Unlock(self, false)) { - // An exception has been raised. Do not fall through. - return false; + case LockWord::kFatLocked: { + Monitor* mon = lock_word.FatLockMonitor(); + return mon->Unlock(self); } + default: + LOG(FATAL) << "Unreachable"; + return false; } - return true; } /* @@ -761,84 +667,91 @@ bool Monitor::MonitorExit(Thread* self, mirror::Object* obj) { */ void Monitor::Wait(Thread* self, mirror::Object *obj, int64_t ms, int32_t ns, bool interruptShouldThrow, ThreadState why) { - volatile int32_t* thinp = obj->GetRawLockWordAddress(); + DCHECK(self != NULL); + DCHECK(obj != NULL); - // If the lock is still thin, we need to fatten it. - uint32_t thin = *thinp; - if (LW_SHAPE(thin) == LW_SHAPE_THIN) { - // Make sure that 'self' holds the lock. - if (LW_LOCK_OWNER(thin) != self->GetThinLockId()) { + LockWord lock_word = obj->GetLockWord(); + switch (lock_word.GetState()) { + case LockWord::kUnlocked: ThrowIllegalMonitorStateExceptionF("object not locked by thread before wait()"); - return; + return; // Failure. + case LockWord::kThinLocked: { + uint32_t thread_id = self->GetThreadId(); + uint32_t owner_thread_id = lock_word.ThinLockOwner(); + if (owner_thread_id != thread_id) { + ThrowIllegalMonitorStateExceptionF("object not locked by thread before wait()"); + return; // Failure. + } else { + // We own the lock, inflate to enqueue ourself on the Monitor. + Inflate(self, self, obj); + lock_word = obj->GetLockWord(); + } + break; } - - /* This thread holds the lock. We need to fatten the lock - * so 'self' can block on it. Don't update the object lock - * field yet, because 'self' needs to acquire the lock before - * any other thread gets a chance. - */ - Inflate(self, obj); - VLOG(monitor) << StringPrintf("monitor: thread %d fattened lock %p by wait()", self->GetThinLockId(), thinp); + case LockWord::kFatLocked: + break; // Already set for a wait. } - LW_MONITOR(*thinp)->Wait(self, ms, ns, interruptShouldThrow, why); + Monitor* mon = lock_word.FatLockMonitor(); + mon->Wait(self, ms, ns, interruptShouldThrow, why); } -void Monitor::Notify(Thread* self, mirror::Object *obj) { - uint32_t thin = *obj->GetRawLockWordAddress(); +void Monitor::InflateAndNotify(Thread* self, mirror::Object* obj, bool notify_all) { + DCHECK(self != NULL); + DCHECK(obj != NULL); - // If the lock is still thin, there aren't any waiters; - // waiting on an object forces lock fattening. - if (LW_SHAPE(thin) == LW_SHAPE_THIN) { - // Make sure that 'self' holds the lock. - if (LW_LOCK_OWNER(thin) != self->GetThinLockId()) { + LockWord lock_word = obj->GetLockWord(); + switch (lock_word.GetState()) { + case LockWord::kUnlocked: ThrowIllegalMonitorStateExceptionF("object not locked by thread before notify()"); - return; + return; // Failure. + case LockWord::kThinLocked: { + uint32_t thread_id = self->GetThreadId(); + uint32_t owner_thread_id = lock_word.ThinLockOwner(); + if (owner_thread_id != thread_id) { + ThrowIllegalMonitorStateExceptionF("object not locked by thread before notify()"); + return; // Failure. + } else { + // We own the lock but there's no Monitor and therefore no waiters. + return; // Success. + } + } + case LockWord::kFatLocked: { + Monitor* mon = lock_word.FatLockMonitor(); + if (notify_all) { + mon->NotifyAll(self); + } else { + mon->Notify(self); + } + return; // Success. } - // no-op; there are no waiters to notify. - // We inflate here in case the Notify is in a tight loop. Without inflation here the waiter - // will struggle to get in. Bug 6961405. - Inflate(self, obj); - } else { - // It's a fat lock. - LW_MONITOR(thin)->Notify(self); } } -void Monitor::NotifyAll(Thread* self, mirror::Object *obj) { - uint32_t thin = *obj->GetRawLockWordAddress(); +uint32_t Monitor::GetLockOwnerThreadId(mirror::Object* obj) { + DCHECK(obj != NULL); - // If the lock is still thin, there aren't any waiters; - // waiting on an object forces lock fattening. - if (LW_SHAPE(thin) == LW_SHAPE_THIN) { - // Make sure that 'self' holds the lock. - if (LW_LOCK_OWNER(thin) != self->GetThinLockId()) { - ThrowIllegalMonitorStateExceptionF("object not locked by thread before notifyAll()"); - return; + LockWord lock_word = obj->GetLockWord(); + switch (lock_word.GetState()) { + case LockWord::kUnlocked: + return ThreadList::kInvalidThreadId; + case LockWord::kThinLocked: + return lock_word.ThinLockOwner(); + case LockWord::kFatLocked: { + Monitor* mon = lock_word.FatLockMonitor(); + return mon->GetOwnerThreadId(); } - // no-op; there are no waiters to notify. - // We inflate here in case the NotifyAll is in a tight loop. Without inflation here the waiter - // will struggle to get in. Bug 6961405. - Inflate(self, obj); - } else { - // It's a fat lock. - LW_MONITOR(thin)->NotifyAll(self); - } -} - -uint32_t Monitor::GetThinLockId(uint32_t raw_lock_word) { - if (LW_SHAPE(raw_lock_word) == LW_SHAPE_THIN) { - return LW_LOCK_OWNER(raw_lock_word); - } else { - Thread* owner = LW_MONITOR(raw_lock_word)->owner_; - return owner ? owner->GetThinLockId() : 0; + default: + LOG(FATAL) << "Unreachable"; + return ThreadList::kInvalidThreadId; } } void Monitor::DescribeWait(std::ostream& os, const Thread* thread) { ThreadState state = thread->GetState(); - mirror::Object* object = NULL; - uint32_t lock_owner = ThreadList::kInvalidId; + int32_t object_identity_hashcode = 0; + uint32_t lock_owner = ThreadList::kInvalidThreadId; + std::string pretty_type; if (state == kWaiting || state == kTimedWaiting || state == kSleeping) { if (state == kSleeping) { os << " - sleeping on "; @@ -850,14 +763,18 @@ void Monitor::DescribeWait(std::ostream& os, const Thread* thread) { MutexLock mu(self, *thread->wait_mutex_); Monitor* monitor = thread->wait_monitor_; if (monitor != NULL) { - object = monitor->obj_; + mirror::Object* object = monitor->obj_; + object_identity_hashcode = object->IdentityHashCode(); + pretty_type = PrettyTypeOf(object); } } } else if (state == kBlocked) { os << " - waiting to lock "; - object = thread->monitor_enter_object_; + mirror::Object* object = thread->monitor_enter_object_; if (object != NULL) { - lock_owner = object->GetThinLockId(); + object_identity_hashcode = object->IdentityHashCode(); + lock_owner = object->GetLockOwnerThreadId(); + pretty_type = PrettyTypeOf(object); } } else { // We're not waiting on anything. @@ -865,10 +782,10 @@ void Monitor::DescribeWait(std::ostream& os, const Thread* thread) { } // - waiting on <0x6008c468> (a java.lang.Class) - os << "<" << object << "> (a " << PrettyTypeOf(object) << ")"; + os << StringPrintf("<0x%08x> (a %s)", object_identity_hashcode, pretty_type.c_str()); // - waiting to lock <0x613f83d8> (a java.lang.Object) held by thread 5 - if (lock_owner != ThreadList::kInvalidId) { + if (lock_owner != ThreadList::kInvalidThreadId) { os << " held by thread " << lock_owner; } @@ -879,18 +796,15 @@ mirror::Object* Monitor::GetContendedMonitor(Thread* thread) { // This is used to implement JDWP's ThreadReference.CurrentContendedMonitor, and has a bizarre // definition of contended that includes a monitor a thread is trying to enter... mirror::Object* result = thread->monitor_enter_object_; - if (result != NULL) { - return result; - } - // ...but also a monitor that the thread is waiting on. - { + if (result == NULL) { + // ...but also a monitor that the thread is waiting on. MutexLock mu(Thread::Current(), *thread->wait_mutex_); Monitor* monitor = thread->wait_monitor_; if (monitor != NULL) { - return monitor->obj_; + result = monitor->GetObject(); } } - return NULL; + return result; } void Monitor::VisitLocks(StackVisitor* stack_visitor, void (*callback)(mirror::Object*, void*), @@ -955,41 +869,56 @@ void Monitor::VisitLocks(StackVisitor* stack_visitor, void (*callback)(mirror::O } } -bool Monitor::IsValidLockWord(int32_t lock_word) { - if (lock_word == 0) { - return true; - } else if (LW_SHAPE(lock_word) == LW_SHAPE_FAT) { - Monitor* mon = LW_MONITOR(lock_word); - MonitorList* list = Runtime::Current()->GetMonitorList(); - MutexLock mu(Thread::Current(), list->monitor_list_lock_); - bool found = false; - for (Monitor* list_mon : list->list_) { - if (mon == list_mon) { - found = true; - break; +bool Monitor::IsValidLockWord(LockWord lock_word) { + switch (lock_word.GetState()) { + case LockWord::kUnlocked: + // Nothing to check. + return true; + case LockWord::kThinLocked: + // Basic sanity check of owner. + return lock_word.ThinLockOwner() != ThreadList::kInvalidThreadId; + case LockWord::kFatLocked: { + // Check the monitor appears in the monitor list. + Monitor* mon = lock_word.FatLockMonitor(); + MonitorList* list = Runtime::Current()->GetMonitorList(); + MutexLock mu(Thread::Current(), list->monitor_list_lock_); + for (Monitor* list_mon : list->list_) { + if (mon == list_mon) { + return true; // Found our monitor. + } } + return false; // Fail - unowned monitor in an object. } - return found; - } else { - // TODO: thin lock validity checking. - return LW_SHAPE(lock_word) == LW_SHAPE_THIN; + default: + LOG(FATAL) << "Unreachable"; + return false; } } void Monitor::TranslateLocation(const mirror::ArtMethod* method, uint32_t dex_pc, - const char*& source_file, uint32_t& line_number) const { + const char** source_file, uint32_t* line_number) const { // If method is null, location is unknown if (method == NULL) { - source_file = ""; - line_number = 0; + *source_file = ""; + *line_number = 0; return; } MethodHelper mh(method); - source_file = mh.GetDeclaringClassSourceFile(); - if (source_file == NULL) { - source_file = ""; + *source_file = mh.GetDeclaringClassSourceFile(); + if (*source_file == NULL) { + *source_file = ""; + } + *line_number = mh.GetLineNumFromDexPC(dex_pc); +} + +uint32_t Monitor::GetOwnerThreadId() { + MutexLock mu(Thread::Current(), monitor_lock_); + Thread* owner = owner_; + if (owner != NULL) { + return owner->GetThreadId(); + } else { + return ThreadList::kInvalidThreadId; } - line_number = mh.GetLineNumFromDexPC(dex_pc); } MonitorList::MonitorList() @@ -1041,22 +970,26 @@ void MonitorList::SweepMonitorList(RootVisitor visitor, void* arg) { } } -MonitorInfo::MonitorInfo(mirror::Object* o) : owner(NULL), entry_count(0) { - uint32_t lock_word = *o->GetRawLockWordAddress(); - if (LW_SHAPE(lock_word) == LW_SHAPE_THIN) { - uint32_t owner_thin_lock_id = LW_LOCK_OWNER(lock_word); - if (owner_thin_lock_id != 0) { - owner = Runtime::Current()->GetThreadList()->FindThreadByThinLockId(owner_thin_lock_id); - entry_count = 1 + LW_LOCK_COUNT(lock_word); - } - // Thin locks have no waiters. - } else { - CHECK_EQ(LW_SHAPE(lock_word), LW_SHAPE_FAT); - Monitor* monitor = LW_MONITOR(lock_word); - owner = monitor->owner_; - entry_count = 1 + monitor->lock_count_; - for (Thread* waiter = monitor->wait_set_; waiter != NULL; waiter = waiter->wait_next_) { - waiters.push_back(waiter); +MonitorInfo::MonitorInfo(mirror::Object* obj) : owner_(NULL), entry_count_(0) { + DCHECK(obj != NULL); + + LockWord lock_word = obj->GetLockWord(); + switch (lock_word.GetState()) { + case LockWord::kUnlocked: + break; + case LockWord::kThinLocked: + owner_ = Runtime::Current()->GetThreadList()->FindThreadByThreadId(lock_word.ThinLockOwner()); + entry_count_ = 1 + lock_word.ThinLockCount(); + // Thin locks have no waiters. + break; + case LockWord::kFatLocked: { + Monitor* mon = lock_word.FatLockMonitor(); + owner_ = mon->owner_; + entry_count_ = 1 + mon->lock_count_; + for (Thread* waiter = mon->wait_set_; waiter != NULL; waiter = waiter->wait_next_) { + waiters_.push_back(waiter); + } + break; } } } diff --git a/runtime/monitor.h b/runtime/monitor.h index 71fe71671ff..044f76e2b67 100644 --- a/runtime/monitor.h +++ b/runtime/monitor.h @@ -30,47 +30,28 @@ namespace art { -/* - * Monitor shape field. Used to distinguish thin locks from fat locks. - */ -#define LW_SHAPE_THIN 0 -#define LW_SHAPE_FAT 1 - -/* - * Hash state field. Used to signify that an object has had its - * identity hash code exposed or relocated. - */ -#define LW_HASH_STATE_UNHASHED 0 -#define LW_HASH_STATE_HASHED 1 -#define LW_HASH_STATE_HASHED_AND_MOVED 3 -#define LW_HASH_STATE_MASK 0x3 -#define LW_HASH_STATE_SHIFT 1 -#define LW_HASH_STATE(x) (((x) >> LW_HASH_STATE_SHIFT) & LW_HASH_STATE_MASK) - -/* - * Lock owner field. Contains the thread id of the thread currently - * holding the lock. - */ -#define LW_LOCK_OWNER_MASK 0xffff -#define LW_LOCK_OWNER_SHIFT 3 -#define LW_LOCK_OWNER(x) (((x) >> LW_LOCK_OWNER_SHIFT) & LW_LOCK_OWNER_MASK) - namespace mirror { class ArtMethod; class Object; } // namespace mirror +class LockWord; class Thread; class StackVisitor; class Monitor { public: + // The default number of spins that are done before thread suspension is used to forcibly inflate + // a lock word. See Runtime::max_spins_before_thin_lock_inflation_. + constexpr static size_t kDefaultMaxSpinsBeforeThinLockInflation = 50; + ~Monitor(); static bool IsSensitiveThread(); static void Init(uint32_t lock_profiling_threshold, bool (*is_sensitive_thread_hook)()); - static uint32_t GetThinLockId(uint32_t raw_lock_word) - NO_THREAD_SAFETY_ANALYSIS; // Reading lock owner without holding lock is racy. + // Return the thread id of the lock owner or 0 when there is no owner. + static uint32_t GetLockOwnerThreadId(mirror::Object* obj) + NO_THREAD_SAFETY_ANALYSIS; // TODO: Reading lock owner without holding lock is racy. static void MonitorEnter(Thread* thread, mirror::Object* obj) EXCLUSIVE_LOCK_FUNCTION(monitor_lock_) @@ -80,9 +61,13 @@ class Monitor { UNLOCK_FUNCTION(monitor_lock_); static void Notify(Thread* self, mirror::Object* obj) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + InflateAndNotify(self, obj, false); + } static void NotifyAll(Thread* self, mirror::Object* obj) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + InflateAndNotify(self, obj, true); + } static void Wait(Thread* self, mirror::Object* obj, int64_t ms, int32_t ns, bool interruptShouldThrow, ThreadState why) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); @@ -92,7 +77,8 @@ class Monitor { SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); // Used to implement JDWP's ThreadReference.CurrentContendedMonitor. - static mirror::Object* GetContendedMonitor(Thread* thread); + static mirror::Object* GetContendedMonitor(Thread* thread) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); // Calls 'callback' once for each lock held in the single stack frame represented by // the current state of 'stack_visitor'. @@ -100,19 +86,33 @@ class Monitor { void* callback_context) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - static bool IsValidLockWord(int32_t lock_word); + static bool IsValidLockWord(LockWord lock_word); + + // TODO: SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) + mirror::Object* GetObject() const { + return obj_; + } - mirror::Object* GetObject(); void SetObject(mirror::Object* object); + Thread* GetOwner() const NO_THREAD_SAFETY_ANALYSIS { + return owner_; + } + private: explicit Monitor(Thread* owner, mirror::Object* obj) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + // Install the monitor into its object, may fail if another thread installs a different monitor + // first. + bool Install(Thread* self) + LOCKS_EXCLUDED(monitor_lock_) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + void AppendToWaitSet(Thread* thread) EXCLUSIVE_LOCKS_REQUIRED(monitor_lock_); void RemoveFromWaitSet(Thread* thread) EXCLUSIVE_LOCKS_REQUIRED(monitor_lock_); - static void Inflate(Thread* self, mirror::Object* obj) + static void Inflate(Thread* self, Thread* owner, mirror::Object* obj) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); void LogContentionEvent(Thread* self, uint32_t wait_ms, uint32_t sample_percent, @@ -123,43 +123,49 @@ class Monitor { LOCKS_EXCLUDED(Locks::thread_list_lock_) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - void Lock(Thread* self) EXCLUSIVE_LOCK_FUNCTION(monitor_lock_); - bool Unlock(Thread* thread, bool for_wait) UNLOCK_FUNCTION(monitor_lock_); + void Lock(Thread* self) + LOCKS_EXCLUDED(monitor_lock_) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + bool Unlock(Thread* thread) + LOCKS_EXCLUDED(monitor_lock_) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - void Notify(Thread* self) NO_THREAD_SAFETY_ANALYSIS; - void NotifyWithLock(Thread* self) - EXCLUSIVE_LOCKS_REQUIRED(monitor_lock_) + static void InflateAndNotify(Thread* self, mirror::Object* obj, bool notify_all) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - void NotifyAll(Thread* self) NO_THREAD_SAFETY_ANALYSIS; - void NotifyAllWithLock() - EXCLUSIVE_LOCKS_REQUIRED(monitor_lock_) + void Notify(Thread* self) + LOCKS_EXCLUDED(monitor_lock_) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + void NotifyAll(Thread* self) + LOCKS_EXCLUDED(monitor_lock_) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); void Wait(Thread* self, int64_t msec, int32_t nsec, bool interruptShouldThrow, ThreadState why) - NO_THREAD_SAFETY_ANALYSIS; - void WaitWithLock(Thread* self, int64_t ms, int32_t ns, bool interruptShouldThrow, ThreadState why) - EXCLUSIVE_LOCKS_REQUIRED(monitor_lock_) + LOCKS_EXCLUDED(monitor_lock_) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); // Translates the provided method and pc into its declaring class' source file and line number. void TranslateLocation(const mirror::ArtMethod* method, uint32_t pc, - const char*& source_file, uint32_t& line_number) const + const char** source_file, uint32_t* line_number) const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + uint32_t GetOwnerThreadId(); + static bool (*is_sensitive_thread_hook_)(); static uint32_t lock_profiling_threshold_; Mutex monitor_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER; + ConditionVariable monitor_contenders_ GUARDED_BY(monitor_lock_); // Which thread currently owns the lock? - Thread* volatile owner_; + Thread* volatile owner_ GUARDED_BY(monitor_lock_); // Owner's recursive lock depth. int lock_count_ GUARDED_BY(monitor_lock_); - // What object are we part of (for debugging). + // What object are we part of. mirror::Object* obj_; // Threads currently waiting on this monitor. @@ -205,9 +211,9 @@ class MonitorInfo { public: explicit MonitorInfo(mirror::Object* o) EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_); - Thread* owner; - size_t entry_count; - std::vector waiters; + Thread* owner_; + size_t entry_count_; + std::vector waiters_; private: DISALLOW_COPY_AND_ASSIGN(MonitorInfo); diff --git a/runtime/monitor_android.cc b/runtime/monitor_android.cc index 8efa0721e85..d89290b836f 100644 --- a/runtime/monitor_android.cc +++ b/runtime/monitor_android.cc @@ -81,7 +81,7 @@ void Monitor::LogContentionEvent(Thread* self, uint32_t wait_ms, uint32_t sample mirror::ArtMethod* m = self->GetCurrentMethod(&pc); const char* filename; uint32_t line_number; - TranslateLocation(m, pc, filename, line_number); + TranslateLocation(m, pc, &filename, &line_number); cp = EventLogWriteString(cp, filename, strlen(filename)); // Emit the source code line number, 5 bytes. diff --git a/runtime/native/dalvik_system_VMStack.cc b/runtime/native/dalvik_system_VMStack.cc index eaf67b8f026..5508270bc22 100644 --- a/runtime/native/dalvik_system_VMStack.cc +++ b/runtime/native/dalvik_system_VMStack.cc @@ -34,7 +34,7 @@ static jobject GetThreadStack(JNIEnv* env, jobject peer) { } // Suspend thread to build stack trace. bool timed_out; - Thread* thread = Thread::SuspendForDebugger(peer, true, &timed_out); + Thread* thread = ThreadList::SuspendThreadByPeer(peer, true, false, &timed_out); if (thread != NULL) { jobject trace; { @@ -42,7 +42,7 @@ static jobject GetThreadStack(JNIEnv* env, jobject peer) { trace = thread->CreateInternalStackTrace(soa); } // Restart suspended thread. - Runtime::Current()->GetThreadList()->Resume(thread, true); + Runtime::Current()->GetThreadList()->Resume(thread, false); return trace; } else { if (timed_out) { diff --git a/runtime/native/java_lang_DexCache.cc b/runtime/native/java_lang_DexCache.cc index f8eeb2906ec..9b83206c37a 100644 --- a/runtime/native/java_lang_DexCache.cc +++ b/runtime/native/java_lang_DexCache.cc @@ -26,7 +26,7 @@ static jobject DexCache_getDexNative(JNIEnv* env, jobject javaDexCache) { ScopedObjectAccess soa(env); mirror::DexCache* dex_cache = soa.Decode(javaDexCache); // Should only be called while holding the lock on the dex cache. - DCHECK_EQ(dex_cache->GetThinLockId(), soa.Self()->GetThinLockId()); + DCHECK_EQ(dex_cache->GetLockOwnerThreadId(), soa.Self()->GetThreadId()); const DexFile* dex_file = dex_cache->GetDexFile(); if (dex_file == NULL) { return NULL; diff --git a/runtime/native/java_lang_Thread.cc b/runtime/native/java_lang_Thread.cc index e85ef09a131..a9de0867851 100644 --- a/runtime/native/java_lang_Thread.cc +++ b/runtime/native/java_lang_Thread.cc @@ -122,13 +122,13 @@ static void Thread_nativeSetName(JNIEnv* env, jobject peer, jstring java_name) { // thread list lock to avoid this, as setting the thread name causes mutator to lock/unlock // in the DDMS send code. bool timed_out; - Thread* thread = Thread::SuspendForDebugger(peer, true, &timed_out); + Thread* thread = ThreadList::SuspendThreadByPeer(peer, true, false, &timed_out); if (thread != NULL) { { ScopedObjectAccess soa(env); thread->SetThreadName(name.c_str()); } - Runtime::Current()->GetThreadList()->Resume(thread, true); + Runtime::Current()->GetThreadList()->Resume(thread, false); } else if (timed_out) { LOG(ERROR) << "Trying to set thread name to '" << name.c_str() << "' failed as the thread " "failed to suspend within a generous timeout."; diff --git a/runtime/native/org_apache_harmony_dalvik_ddmc_DdmVmInternal.cc b/runtime/native/org_apache_harmony_dalvik_ddmc_DdmVmInternal.cc index 06769687bf5..4f81a0b95a9 100644 --- a/runtime/native/org_apache_harmony_dalvik_ddmc_DdmVmInternal.cc +++ b/runtime/native/org_apache_harmony_dalvik_ddmc_DdmVmInternal.cc @@ -44,22 +44,10 @@ static jboolean DdmVmInternal_getRecentAllocationStatus(JNIEnv*, jclass) { * NULL on failure, e.g. if the threadId couldn't be found. */ static jobjectArray DdmVmInternal_getStackTraceById(JNIEnv* env, jclass, jint thin_lock_id) { - ScopedLocalRef peer(env, NULL); - { - Thread* t = Runtime::Current()->GetThreadList()->FindThreadByThinLockId(thin_lock_id); - if (t == NULL) { - return NULL; - } - ScopedObjectAccess soa(env); - peer.reset(soa.AddLocalReference(t->GetPeer())); - } - if (peer.get() == NULL) { - return NULL; - } - // Suspend thread to build stack trace. + ThreadList* thread_list = Runtime::Current()->GetThreadList(); bool timed_out; - Thread* thread = Thread::SuspendForDebugger(peer.get(), true, &timed_out); + Thread* thread = thread_list->SuspendThreadByThreadId(thin_lock_id, false, &timed_out); if (thread != NULL) { jobject trace; { @@ -67,7 +55,7 @@ static jobjectArray DdmVmInternal_getStackTraceById(JNIEnv* env, jclass, jint th trace = thread->CreateInternalStackTrace(soa); } // Restart suspended thread. - Runtime::Current()->GetThreadList()->Resume(thread, true); + thread_list->Resume(thread, false); return Thread::InternalStackTraceToStackTraceElementArray(env, trace); } else { if (timed_out) { @@ -115,7 +103,7 @@ static void ThreadStatsGetterCallback(Thread* t, void* context) { GetTaskStats(t->GetTid(), &native_thread_state, &utime, &stime, &task_cpu); std::vector& bytes = *reinterpret_cast*>(context); - JDWP::Append4BE(bytes, t->GetThinLockId()); + JDWP::Append4BE(bytes, t->GetThreadId()); JDWP::Append1BE(bytes, Dbg::ToJdwpThreadStatus(t->GetState())); JDWP::Append4BE(bytes, t->GetTid()); JDWP::Append4BE(bytes, utime); diff --git a/runtime/object_utils.h b/runtime/object_utils.h index f83db903ff5..692ceccc6ce 100644 --- a/runtime/object_utils.h +++ b/runtime/object_utils.h @@ -36,7 +36,8 @@ namespace art { class ObjectLock { public: - explicit ObjectLock(Thread* self, mirror::Object* object) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) + explicit ObjectLock(Thread* self, mirror::Object* object) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) : self_(self), obj_(object) { CHECK(object != NULL); obj_->MonitorEnter(self_); diff --git a/runtime/runtime.cc b/runtime/runtime.cc index b4ce37fe1cf..8a20bbcac47 100644 --- a/runtime/runtime.cc +++ b/runtime/runtime.cc @@ -75,6 +75,7 @@ Runtime::Runtime() is_explicit_gc_disabled_(false), default_stack_size_(0), heap_(NULL), + max_spins_before_thin_lock_inflation_(Monitor::kDefaultMaxSpinsBeforeThinLockInflation), monitor_list_(NULL), thread_list_(NULL), intern_table_(NULL), @@ -350,6 +351,7 @@ Runtime::ParsedOptions* Runtime::ParsedOptions::Create(const Options& options, b // Only the main GC thread, no workers. parsed->conc_gc_threads_ = 0; parsed->stack_size_ = 0; // 0 means default. + parsed->max_spins_before_thin_lock_inflation_ = Monitor::kDefaultMaxSpinsBeforeThinLockInflation; parsed->low_memory_mode_ = false; parsed->is_compiler_ = false; @@ -510,6 +512,10 @@ Runtime::ParsedOptions* Runtime::ParsedOptions::Create(const Options& options, b return NULL; } parsed->stack_size_ = size; + } else if (StartsWith(option, "-XX:MaxSpinsBeforeThinLockInflation=")) { + parsed->max_spins_before_thin_lock_inflation_ = + strtoul(option.substr(strlen("-XX:MaxSpinsBeforeThinLockInflation=")).c_str(), + nullptr, 10); } else if (option == "-XX:LongPauseLogThreshold") { parsed->long_pause_log_threshold_ = ParseMemoryOption(option.substr(strlen("-XX:LongPauseLogThreshold=")).c_str(), 1024); @@ -866,6 +872,8 @@ bool Runtime::Init(const Options& raw_options, bool ignore_unrecognized) { default_stack_size_ = options->stack_size_; stack_trace_file_ = options->stack_trace_file_; + max_spins_before_thin_lock_inflation_ = options->max_spins_before_thin_lock_inflation_; + monitor_list_ = new MonitorList; thread_list_ = new ThreadList; intern_table_ = new InternTable; @@ -901,7 +909,7 @@ bool Runtime::Init(const Options& raw_options, bool ignore_unrecognized) { // objects. We can't supply a thread group yet; it will be fixed later. Since we are the main // thread, we do not get a java peer. Thread* self = Thread::Attach("main", false, NULL, false); - CHECK_EQ(self->thin_lock_id_, ThreadList::kMainId); + CHECK_EQ(self->thin_lock_thread_id_, ThreadList::kMainThreadId); CHECK(self != NULL); // Set us to runnable so tools using a runtime can allocate and GC by default diff --git a/runtime/runtime.h b/runtime/runtime.h index 552cfdf0092..36b0bd6dd69 100644 --- a/runtime/runtime.h +++ b/runtime/runtime.h @@ -112,6 +112,7 @@ class Runtime { size_t parallel_gc_threads_; size_t conc_gc_threads_; size_t stack_size_; + size_t max_spins_before_thin_lock_inflation_; bool low_memory_mode_; size_t lock_profiling_threshold_; std::string stack_trace_file_; @@ -283,6 +284,10 @@ class Runtime { return java_vm_; } + size_t GetMaxSpinsBeforeThinkLockInflation() const { + return max_spins_before_thin_lock_inflation_; + } + MonitorList* GetMonitorList() const { return monitor_list_; } @@ -455,6 +460,8 @@ class Runtime { gc::Heap* heap_; + // The number of spins that are done before thread suspension is used to forcibly inflate. + size_t max_spins_before_thin_lock_inflation_; MonitorList* monitor_list_; ThreadList* thread_list_; diff --git a/runtime/signal_catcher.cc b/runtime/signal_catcher.cc index 15eb27deb21..fe62e254430 100644 --- a/runtime/signal_catcher.cc +++ b/runtime/signal_catcher.cc @@ -147,7 +147,6 @@ void SignalCatcher::HandleSigQuit() { CHECK_EQ(self->SetStateUnsafe(old_state), kRunnable); if (self->ReadFlag(kCheckpointRequest)) { self->RunCheckpointFunction(); - self->AtomicClearFlag(kCheckpointRequest); } self->EndAssertNoThreadSuspension(old_cause); thread_list->ResumeAll(); diff --git a/runtime/thread-inl.h b/runtime/thread-inl.h index 4552062319b..7d28785f584 100644 --- a/runtime/thread-inl.h +++ b/runtime/thread-inl.h @@ -80,17 +80,16 @@ inline void Thread::TransitionFromRunnableToSuspended(ThreadState new_state) { union StateAndFlags new_state_and_flags; do { old_state_and_flags = state_and_flags_; + if (UNLIKELY((old_state_and_flags.as_struct.flags & kCheckpointRequest) != 0)) { + RunCheckpointFunction(); + continue; + } // Copy over flags and try to clear the checkpoint bit if it is set. new_state_and_flags.as_struct.flags = old_state_and_flags.as_struct.flags & ~kCheckpointRequest; new_state_and_flags.as_struct.state = new_state; // CAS the value without a memory barrier, that will occur in the unlock below. } while (UNLIKELY(android_atomic_cas(old_state_and_flags.as_int, new_state_and_flags.as_int, &state_and_flags_.as_int) != 0)); - // If we toggled the checkpoint flag we must have cleared it. - uint16_t flag_change = new_state_and_flags.as_struct.flags ^ old_state_and_flags.as_struct.flags; - if (UNLIKELY((flag_change & kCheckpointRequest) != 0)) { - RunCheckpointFunction(); - } // Release share on mutator_lock_. Locks::mutator_lock_->SharedUnlock(this); } diff --git a/runtime/thread.cc b/runtime/thread.cc index 7040337f045..de14dbb0c0a 100644 --- a/runtime/thread.cc +++ b/runtime/thread.cc @@ -311,7 +311,7 @@ void Thread::Init(ThreadList* thread_list, JavaVMExt* java_vm) { CHECK_PTHREAD_CALL(pthread_setspecific, (Thread::pthread_key_self_, this), "attach self"); DCHECK_EQ(Thread::Current(), this); - thin_lock_id_ = thread_list->AllocThreadId(this); + thin_lock_thread_id_ = thread_list->AllocThreadId(this); InitStackHwm(); jni_env_ = new JNIEnvExt(this, java_vm); @@ -476,9 +476,9 @@ void Thread::InitStackHwm() { void Thread::ShortDump(std::ostream& os) const { os << "Thread["; - if (GetThinLockId() != 0) { + if (GetThreadId() != 0) { // If we're in kStarting, we won't have a thin lock id or tid yet. - os << GetThinLockId() + os << GetThreadId() << ",tid=" << GetTid() << ','; } os << GetState() @@ -574,18 +574,32 @@ void Thread::RunCheckpointFunction() { ATRACE_BEGIN("Checkpoint function"); checkpoint_function_->Run(this); ATRACE_END(); + checkpoint_function_ = NULL; + AtomicClearFlag(kCheckpointRequest); } bool Thread::RequestCheckpoint(Closure* function) { - CHECK(!ReadFlag(kCheckpointRequest)) << "Already have a pending checkpoint request"; - checkpoint_function_ = function; union StateAndFlags old_state_and_flags = state_and_flags_; + if (old_state_and_flags.as_struct.state != kRunnable) { + return false; // Fail, thread is suspended and so can't run a checkpoint. + } + if ((old_state_and_flags.as_struct.flags & kCheckpointRequest) != 0) { + return false; // Fail, already a checkpoint pending. + } + CHECK(checkpoint_function_ == NULL); + checkpoint_function_ = function; + // Checkpoint function installed now install flag bit. // We must be runnable to request a checkpoint. old_state_and_flags.as_struct.state = kRunnable; union StateAndFlags new_state_and_flags = old_state_and_flags; new_state_and_flags.as_struct.flags |= kCheckpointRequest; int succeeded = android_atomic_cmpxchg(old_state_and_flags.as_int, new_state_and_flags.as_int, &state_and_flags_.as_int); + if (UNLIKELY(succeeded != 0)) { + // The thread changed state before the checkpoint was installed. + CHECK(checkpoint_function_ == function); + checkpoint_function_ = NULL; + } return succeeded == 0; } @@ -600,88 +614,6 @@ void Thread::FullSuspendCheck() { VLOG(threads) << this << " self-reviving"; } -Thread* Thread::SuspendForDebugger(jobject peer, bool request_suspension, bool* timed_out) { - static const useconds_t kTimeoutUs = 30 * 1000000; // 30s. - useconds_t total_delay_us = 0; - useconds_t delay_us = 0; - bool did_suspend_request = false; - *timed_out = false; - while (true) { - Thread* thread; - { - ScopedObjectAccess soa(Thread::Current()); - Thread* self = soa.Self(); - MutexLock mu(self, *Locks::thread_list_lock_); - thread = Thread::FromManagedThread(soa, peer); - if (thread == NULL) { - JNIEnv* env = self->GetJniEnv(); - ScopedLocalRef scoped_name_string(env, - (jstring)env->GetObjectField(peer, - WellKnownClasses::java_lang_Thread_name)); - ScopedUtfChars scoped_name_chars(env, scoped_name_string.get()); - if (scoped_name_chars.c_str() == NULL) { - LOG(WARNING) << "No such thread for suspend: " << peer; - env->ExceptionClear(); - } else { - LOG(WARNING) << "No such thread for suspend: " << peer << ":" << scoped_name_chars.c_str(); - } - - return NULL; - } - { - MutexLock mu(soa.Self(), *Locks::thread_suspend_count_lock_); - if (request_suspension) { - thread->ModifySuspendCount(soa.Self(), +1, true /* for_debugger */); - request_suspension = false; - did_suspend_request = true; - } - // IsSuspended on the current thread will fail as the current thread is changed into - // Runnable above. As the suspend count is now raised if this is the current thread - // it will self suspend on transition to Runnable, making it hard to work with. It's simpler - // to just explicitly handle the current thread in the callers to this code. - CHECK_NE(thread, soa.Self()) << "Attempt to suspend the current thread for the debugger"; - // If thread is suspended (perhaps it was already not Runnable but didn't have a suspend - // count, or else we've waited and it has self suspended) or is the current thread, we're - // done. - if (thread->IsSuspended()) { - return thread; - } - if (total_delay_us >= kTimeoutUs) { - LOG(ERROR) << "Thread suspension timed out: " << peer; - if (did_suspend_request) { - thread->ModifySuspendCount(soa.Self(), -1, true /* for_debugger */); - } - *timed_out = true; - return NULL; - } - } - // Release locks and come out of runnable state. - } - for (int i = kLockLevelCount - 1; i >= 0; --i) { - BaseMutex* held_mutex = Thread::Current()->GetHeldMutex(static_cast(i)); - if (held_mutex != NULL) { - LOG(FATAL) << "Holding " << held_mutex->GetName() - << " while sleeping for thread suspension"; - } - } - { - useconds_t new_delay_us = delay_us * 2; - CHECK_GE(new_delay_us, delay_us); - if (new_delay_us < 500000) { // Don't allow sleeping to be more than 0.5s. - delay_us = new_delay_us; - } - } - if (delay_us == 0) { - sched_yield(); - // Default to 1 milliseconds (note that this gets multiplied by 2 before the first sleep). - delay_us = 500; - } else { - usleep(delay_us); - total_delay_us += delay_us; - } - } -} - void Thread::DumpState(std::ostream& os, const Thread* thread, pid_t tid) { std::string group_name; int priority; @@ -718,7 +650,7 @@ void Thread::DumpState(std::ostream& os, const Thread* thread, pid_t tid) { os << " daemon"; } os << " prio=" << priority - << " tid=" << thread->GetThinLockId() + << " tid=" << thread->GetThreadId() << " " << thread->GetState(); if (thread->IsStillStarting()) { os << " (still starting up)"; @@ -968,9 +900,9 @@ Thread::Thread(bool daemon) jpeer_(NULL), stack_begin_(NULL), stack_size_(0), + thin_lock_thread_id_(0), stack_trace_sample_(NULL), trace_clock_base_(0), - thin_lock_id_(0), tid_(0), wait_mutex_(new Mutex("a thread wait mutex")), wait_cond_(new ConditionVariable("a thread wait condition variable", *wait_mutex_)), @@ -1718,7 +1650,7 @@ void Thread::DumpThreadOffset(std::ostream& os, uint32_t offset, size_t size_of_ DO_THREAD_OFFSET(self_); DO_THREAD_OFFSET(stack_end_); DO_THREAD_OFFSET(suspend_count_); - DO_THREAD_OFFSET(thin_lock_id_); + DO_THREAD_OFFSET(thin_lock_thread_id_); // DO_THREAD_OFFSET(top_of_managed_stack_); // DO_THREAD_OFFSET(top_of_managed_stack_pc_); DO_THREAD_OFFSET(top_sirt_); @@ -2001,7 +1933,7 @@ bool Thread::HoldsLock(mirror::Object* object) { if (object == NULL) { return false; } - return object->GetThinLockId() == thin_lock_id_; + return object->GetLockOwnerThreadId() == thin_lock_thread_id_; } // RootVisitor parameters are: (const Object* obj, size_t vreg, const StackVisitor* visitor). diff --git a/runtime/thread.h b/runtime/thread.h index 2d9e0097d00..3aa137375ee 100644 --- a/runtime/thread.h +++ b/runtime/thread.h @@ -154,7 +154,8 @@ class PACKED(4) Thread { void ModifySuspendCount(Thread* self, int delta, bool for_debugger) EXCLUSIVE_LOCKS_REQUIRED(Locks::thread_suspend_count_lock_); - bool RequestCheckpoint(Closure* function); + bool RequestCheckpoint(Closure* function) + EXCLUSIVE_LOCKS_REQUIRED(Locks::thread_suspend_count_lock_); // Called when thread detected that the thread_suspend_count_ was non-zero. Gives up share of // mutator_lock_ and waits until it is resumed and thread_suspend_count_ is zero. @@ -175,14 +176,6 @@ class PACKED(4) Thread { UNLOCK_FUNCTION(Locks::mutator_lock_) ALWAYS_INLINE; - // Wait for a debugger suspension on the thread associated with the given peer. Returns the - // thread on success, else NULL. If the thread should be suspended then request_suspension should - // be true on entry. If the suspension times out then *timeout is set to true. - static Thread* SuspendForDebugger(jobject peer, bool request_suspension, bool* timed_out) - LOCKS_EXCLUDED(Locks::mutator_lock_, - Locks::thread_list_lock_, - Locks::thread_suspend_count_lock_); - // Once called thread suspension will cause an assertion failure. #ifndef NDEBUG const char* StartAssertNoThreadSuspension(const char* cause) { @@ -219,7 +212,7 @@ class PACKED(4) Thread { return daemon_; } - bool HoldsLock(mirror::Object*); + bool HoldsLock(mirror::Object*) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); /* * Changes the priority of this thread to match that of the java.lang.Thread object. @@ -237,8 +230,8 @@ class PACKED(4) Thread { */ static int GetNativePriority(); - uint32_t GetThinLockId() const { - return thin_lock_id_; + uint32_t GetThreadId() const { + return thin_lock_thread_id_; } pid_t GetTid() const { @@ -414,7 +407,7 @@ class PACKED(4) Thread { } static ThreadOffset ThinLockIdOffset() { - return ThreadOffset(OFFSETOF_MEMBER(Thread, thin_lock_id_)); + return ThreadOffset(OFFSETOF_MEMBER(Thread, thin_lock_thread_id_)); } static ThreadOffset CardTableOffset() { @@ -702,18 +695,18 @@ class PACKED(4) Thread { // Size of the stack size_t stack_size_; - // Pointer to previous stack trace captured by sampling profiler. - std::vector* stack_trace_sample_; - - // The clock base used for tracing. - uint64_t trace_clock_base_; - // Thin lock thread id. This is a small integer used by the thin lock implementation. // This is not to be confused with the native thread's tid, nor is it the value returned // by java.lang.Thread.getId --- this is a distinct value, used only for locking. One // important difference between this id and the ids visible to managed code is that these // ones get reused (to ensure that they fit in the number of bits available). - uint32_t thin_lock_id_; + uint32_t thin_lock_thread_id_; + + // Pointer to previous stack trace captured by sampling profiler. + std::vector* stack_trace_sample_; + + // The clock base used for tracing. + uint64_t trace_clock_base_; // System thread id. pid_t tid_; @@ -722,13 +715,16 @@ class PACKED(4) Thread { // Guards the 'interrupted_' and 'wait_monitor_' members. mutable Mutex* wait_mutex_ DEFAULT_MUTEX_ACQUIRED_AFTER; + // Condition variable waited upon during a wait. ConditionVariable* wait_cond_ GUARDED_BY(wait_mutex_); - // Pointer to the monitor lock we're currently waiting on (or NULL). + // Pointer to the monitor lock we're currently waiting on or NULL if not waiting. Monitor* wait_monitor_ GUARDED_BY(wait_mutex_); // Thread "interrupted" status; stays raised until queried or thrown. bool32_t interrupted_ GUARDED_BY(wait_mutex_); - // The next thread in the wait set this thread is part of. + // The next thread in the wait set this thread is part of or NULL if not waiting. Thread* wait_next_; + + // If we're blocked in MonitorEnter, this is the object we're trying to lock. mirror::Object* monitor_enter_object_; @@ -785,7 +781,8 @@ class PACKED(4) Thread { // Cause for last suspension. const char* last_no_thread_suspension_cause_; - // Pending checkpoint functions. + // Pending checkpoint function or NULL if non-pending. Installation guarding by + // Locks::thread_suspend_count_lock_. Closure* checkpoint_function_; public: diff --git a/runtime/thread_list.cc b/runtime/thread_list.cc index 44cf8101782..ff1ed2a4d27 100644 --- a/runtime/thread_list.cc +++ b/runtime/thread_list.cc @@ -17,6 +17,8 @@ #include "thread_list.h" #include +#include +#include #include #include @@ -24,8 +26,13 @@ #include "base/mutex-inl.h" #include "base/timing_logger.h" #include "debugger.h" +#include "jni_internal.h" +#include "lock_word.h" +#include "monitor.h" +#include "scoped_thread_state_change.h" #include "thread.h" #include "utils.h" +#include "well_known_classes.h" namespace art { @@ -33,6 +40,7 @@ ThreadList::ThreadList() : allocated_ids_lock_("allocated thread ids lock"), suspend_all_count_(0), debug_suspend_all_count_(0), thread_exit_cond_("thread exit condition variable", *Locks::thread_list_lock_) { + CHECK(Monitor::IsValidLockWord(LockWord::FromThinLockId(kMaxThreadId, 1))); } ThreadList::~ThreadList() { @@ -160,18 +168,19 @@ size_t ThreadList::RunCheckpoint(Closure* checkpoint_function) { // Call a checkpoint function for each thread, threads which are suspend get their checkpoint // manually called. MutexLock mu(self, *Locks::thread_list_lock_); + MutexLock mu2(self, *Locks::thread_suspend_count_lock_); for (const auto& thread : list_) { if (thread != self) { - for (;;) { + while (true) { if (thread->RequestCheckpoint(checkpoint_function)) { // This thread will run it's checkpoint some time in the near future. count++; break; } else { // We are probably suspended, try to make sure that we stay suspended. - MutexLock mu2(self, *Locks::thread_suspend_count_lock_); // The thread switched back to runnable. if (thread->GetState() == kRunnable) { + // Spurious fail, try again. continue; } thread->ModifySuspendCount(self, +1, false); @@ -204,7 +213,7 @@ size_t ThreadList::RunCheckpoint(Closure* checkpoint_function) { } } // We know for sure that the thread is suspended at this point. - thread->RunCheckpointFunction(); + checkpoint_function->Run(thread); { MutexLock mu2(self, *Locks::thread_suspend_count_lock_); thread->ModifySuspendCount(self, -1, false); @@ -322,6 +331,178 @@ void ThreadList::Resume(Thread* thread, bool for_debugger) { VLOG(threads) << "Resume(" << *thread << ") complete"; } +static void ThreadSuspendByPeerWarning(Thread* self, int level, const char* message, jobject peer) { + JNIEnvExt* env = self->GetJniEnv(); + ScopedLocalRef + scoped_name_string(env, (jstring)env->GetObjectField(peer, + WellKnownClasses::java_lang_Thread_name)); + ScopedUtfChars scoped_name_chars(env, scoped_name_string.get()); + if (scoped_name_chars.c_str() == NULL) { + LOG(level) << message << ": " << peer; + env->ExceptionClear(); + } else { + LOG(level) << message << ": " << peer << ":" << scoped_name_chars.c_str(); + } +} + +// Unlike suspending all threads where we can wait to acquire the mutator_lock_, suspending an +// individual thread requires polling. delay_us is the requested sleep and total_delay_us +// accumulates the total time spent sleeping for timeouts. The first sleep is just a yield, +// subsequently sleeps increase delay_us from 1ms to 500ms by doubling. +static void ThreadSuspendSleep(Thread* self, useconds_t* delay_us, useconds_t* total_delay_us) { + for (int i = kLockLevelCount - 1; i >= 0; --i) { + BaseMutex* held_mutex = self->GetHeldMutex(static_cast(i)); + if (held_mutex != NULL) { + LOG(FATAL) << "Holding " << held_mutex->GetName() << " while sleeping for thread suspension"; + } + } + { + useconds_t new_delay_us = (*delay_us) * 2; + CHECK_GE(new_delay_us, *delay_us); + if (new_delay_us < 500000) { // Don't allow sleeping to be more than 0.5s. + *delay_us = new_delay_us; + } + } + if ((*delay_us) == 0) { + sched_yield(); + // Default to 1 milliseconds (note that this gets multiplied by 2 before the first sleep). + (*delay_us) = 500; + } else { + usleep(*delay_us); + (*total_delay_us) += (*delay_us); + } +} + +Thread* ThreadList::SuspendThreadByPeer(jobject peer, bool request_suspension, + bool debug_suspension, bool* timed_out) { + static const useconds_t kTimeoutUs = 30 * 1000000; // 30s. + useconds_t total_delay_us = 0; + useconds_t delay_us = 0; + bool did_suspend_request = false; + *timed_out = false; + Thread* self = Thread::Current(); + while (true) { + Thread* thread; + { + ScopedObjectAccess soa(self); + MutexLock mu(self, *Locks::thread_list_lock_); + thread = Thread::FromManagedThread(soa, peer); + if (thread == NULL) { + ThreadSuspendByPeerWarning(self, WARNING, "No such thread for suspend", peer); + return NULL; + } + { + MutexLock mu(self, *Locks::thread_suspend_count_lock_); + if (request_suspension) { + thread->ModifySuspendCount(self, +1, debug_suspension); + request_suspension = false; + did_suspend_request = true; + } else { + // If the caller isn't requesting suspension, a suspension should have already occurred. + CHECK_GT(thread->GetSuspendCount(), 0); + } + // IsSuspended on the current thread will fail as the current thread is changed into + // Runnable above. As the suspend count is now raised if this is the current thread + // it will self suspend on transition to Runnable, making it hard to work with. It's simpler + // to just explicitly handle the current thread in the callers to this code. + CHECK_NE(thread, self) << "Attempt to suspend the current thread for the debugger"; + // If thread is suspended (perhaps it was already not Runnable but didn't have a suspend + // count, or else we've waited and it has self suspended) or is the current thread, we're + // done. + if (thread->IsSuspended()) { + return thread; + } + if (total_delay_us >= kTimeoutUs) { + ThreadSuspendByPeerWarning(self, ERROR, "Thread suspension timed out", peer); + if (did_suspend_request) { + thread->ModifySuspendCount(soa.Self(), -1, debug_suspension); + } + *timed_out = true; + return NULL; + } + } + // Release locks and come out of runnable state. + } + ThreadSuspendSleep(self, &delay_us, &total_delay_us); + } +} + +static void ThreadSuspendByThreadIdWarning(int level, const char* message, uint32_t thread_id) { + LOG(level) << StringPrintf("%s: %d", message, thread_id); +} + +Thread* ThreadList::SuspendThreadByThreadId(uint32_t thread_id, bool debug_suspension, + bool* timed_out) { + static const useconds_t kTimeoutUs = 30 * 1000000; // 30s. + useconds_t total_delay_us = 0; + useconds_t delay_us = 0; + bool did_suspend_request = false; + *timed_out = false; + Thread* self = Thread::Current(); + CHECK_NE(thread_id, kInvalidThreadId); + while (true) { + Thread* thread = NULL; + { + ScopedObjectAccess soa(self); + MutexLock mu(self, *Locks::thread_list_lock_); + for (const auto& it : list_) { + if (it->GetThreadId() == thread_id) { + thread = it; + break; + } + } + if (thread == NULL) { + // There's a race in inflating a lock and the owner giving up ownership and then dying. + ThreadSuspendByThreadIdWarning(WARNING, "No such thread id for suspend", thread_id); + return NULL; + } + { + MutexLock mu(self, *Locks::thread_suspend_count_lock_); + if (!did_suspend_request) { + thread->ModifySuspendCount(self, +1, debug_suspension); + did_suspend_request = true; + } else { + // If the caller isn't requesting suspension, a suspension should have already occurred. + CHECK_GT(thread->GetSuspendCount(), 0); + } + // IsSuspended on the current thread will fail as the current thread is changed into + // Runnable above. As the suspend count is now raised if this is the current thread + // it will self suspend on transition to Runnable, making it hard to work with. It's simpler + // to just explicitly handle the current thread in the callers to this code. + CHECK_NE(thread, self) << "Attempt to suspend the current thread for the debugger"; + // If thread is suspended (perhaps it was already not Runnable but didn't have a suspend + // count, or else we've waited and it has self suspended) or is the current thread, we're + // done. + if (thread->IsSuspended()) { + return thread; + } + if (total_delay_us >= kTimeoutUs) { + ThreadSuspendByThreadIdWarning(ERROR, "Thread suspension timed out", thread_id); + if (did_suspend_request) { + thread->ModifySuspendCount(soa.Self(), -1, debug_suspension); + } + *timed_out = true; + return NULL; + } + } + // Release locks and come out of runnable state. + } + ThreadSuspendSleep(self, &delay_us, &total_delay_us); + } +} + +Thread* ThreadList::FindThreadByThreadId(uint32_t thin_lock_id) { + Thread* self = Thread::Current(); + MutexLock mu(self, *Locks::thread_list_lock_); + for (const auto& thread : list_) { + if (thread->GetThreadId() == thin_lock_id) { + CHECK(thread == self || thread->IsSuspended()); + return thread; + } + } + return NULL; +} + void ThreadList::SuspendAllForDebugger() { Thread* self = Thread::Current(); Thread* debug_thread = Dbg::GetDebugThread(); @@ -528,8 +709,8 @@ void ThreadList::Unregister(Thread* self) { // suspend and so on, must happen at this point, and not in ~Thread. self->Destroy(); - uint32_t thin_lock_id = self->thin_lock_id_; - self->thin_lock_id_ = 0; + uint32_t thin_lock_id = self->thin_lock_thread_id_; + self->thin_lock_thread_id_ = 0; ReleaseThreadId(self, thin_lock_id); while (self != NULL) { // Remove and delete the Thread* while holding the thread_list_lock_ and @@ -609,14 +790,4 @@ void ThreadList::ReleaseThreadId(Thread* self, uint32_t id) { allocated_ids_.reset(id); } -Thread* ThreadList::FindThreadByThinLockId(uint32_t thin_lock_id) { - MutexLock mu(Thread::Current(), *Locks::thread_list_lock_); - for (const auto& thread : list_) { - if (thread->GetThinLockId() == thin_lock_id) { - return thread; - } - } - return NULL; -} - } // namespace art diff --git a/runtime/thread_list.h b/runtime/thread_list.h index 3df3e2c8419..b1b3e888605 100644 --- a/runtime/thread_list.h +++ b/runtime/thread_list.h @@ -18,6 +18,7 @@ #define ART_RUNTIME_THREAD_LIST_H_ #include "base/mutex.h" +#include "jni.h" #include "root_visitor.h" #include @@ -31,8 +32,8 @@ class TimingLogger; class ThreadList { public: static const uint32_t kMaxThreadId = 0xFFFF; - static const uint32_t kInvalidId = 0; - static const uint32_t kMainId = 1; + static const uint32_t kInvalidThreadId = 0; + static const uint32_t kMainThreadId = 1; explicit ThreadList(); ~ThreadList(); @@ -59,6 +60,30 @@ class ThreadList { LOCKS_EXCLUDED(Locks::thread_list_lock_, Locks::thread_suspend_count_lock_); + + // Suspend a thread using a peer, typically used by the debugger. Returns the thread on success, + // else NULL. The peer is used to identify the thread to avoid races with the thread terminating. + // If the thread should be suspended then value of request_suspension should be true otherwise + // the routine will wait for a previous suspend request. If the suspension times out then *timeout + // is set to true. + static Thread* SuspendThreadByPeer(jobject peer, bool request_suspension, bool debug_suspension, + bool* timed_out) + LOCKS_EXCLUDED(Locks::mutator_lock_, + Locks::thread_list_lock_, + Locks::thread_suspend_count_lock_); + + // Suspend a thread using its thread id, typically used by lock/monitor inflation. Returns the + // thread on success else NULL. The thread id is used to identify the thread to avoid races with + // the thread terminating. Note that as thread ids are recycled this may not suspend the expected + // thread, that may be terminating. If the suspension times out then *timeout is set to true. + Thread* SuspendThreadByThreadId(uint32_t thread_id, bool debug_suspension, bool* timed_out) + LOCKS_EXCLUDED(Locks::mutator_lock_, + Locks::thread_list_lock_, + Locks::thread_suspend_count_lock_); + + // Find an already suspended thread (or self) by its id. + Thread* FindThreadByThreadId(uint32_t thin_lock_id); + // Run a checkpoint on threads, running threads are not suspended but run the checkpoint inside // of the suspend check. Returns how many checkpoints we should expect to run. size_t RunCheckpoint(Closure* checkpoint_function); @@ -99,8 +124,6 @@ class ThreadList { return list_; } - Thread* FindThreadByThinLockId(uint32_t thin_lock_id); - private: uint32_t AllocThreadId(Thread* self); void ReleaseThreadId(Thread* self, uint32_t id) LOCKS_EXCLUDED(allocated_ids_lock_); From 253ea073ec03a0be1e22df98957257594e316a39 Mon Sep 17 00:00:00 2001 From: Hiroshi Yamauchi Date: Wed, 2 Oct 2013 12:44:17 -0700 Subject: [PATCH 0067/2402] Fix an Art debug build boot failure. (The original change is 3b4c18933c24b8a33f38573c2ebcdb9aa16efeb5.) Bug: 11003273 Bug: 9986565 Change-Id: Iaaf6395f171eb6e6b8c99386c20c4970c53ee00d --- runtime/interpreter/interpreter_goto_table_impl.cc | 8 ++++---- runtime/interpreter/interpreter_switch_impl.cc | 8 ++++---- runtime/runtime.cc | 4 ++-- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/runtime/interpreter/interpreter_goto_table_impl.cc b/runtime/interpreter/interpreter_goto_table_impl.cc index 869729123dc..daf6f93f1cd 100644 --- a/runtime/interpreter/interpreter_goto_table_impl.cc +++ b/runtime/interpreter/interpreter_goto_table_impl.cc @@ -461,8 +461,8 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(NEW_INSTANCE) { - Object* obj = AllocObjectFromCode(inst->VRegB_21c(), shadow_frame.GetMethod(), - self, do_access_check); + Object* obj = AllocObjectFromCodeInstrumented(inst->VRegB_21c(), shadow_frame.GetMethod(), + self, do_access_check); if (UNLIKELY(obj == NULL)) { HANDLE_PENDING_EXCEPTION(); } else { @@ -474,8 +474,8 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* HANDLE_INSTRUCTION_START(NEW_ARRAY) { int32_t length = shadow_frame.GetVReg(inst->VRegB_22c(inst_data)); - Object* obj = AllocArrayFromCode(inst->VRegC_22c(), shadow_frame.GetMethod(), - length, self, do_access_check); + Object* obj = AllocArrayFromCodeInstrumented(inst->VRegC_22c(), shadow_frame.GetMethod(), + length, self, do_access_check); if (UNLIKELY(obj == NULL)) { HANDLE_PENDING_EXCEPTION(); } else { diff --git a/runtime/interpreter/interpreter_switch_impl.cc b/runtime/interpreter/interpreter_switch_impl.cc index dcc86e81aa8..c3977c6a3da 100644 --- a/runtime/interpreter/interpreter_switch_impl.cc +++ b/runtime/interpreter/interpreter_switch_impl.cc @@ -422,8 +422,8 @@ JValue ExecuteSwitchImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem } case Instruction::NEW_INSTANCE: { PREAMBLE(); - Object* obj = AllocObjectFromCode(inst->VRegB_21c(), shadow_frame.GetMethod(), - self, do_access_check); + Object* obj = AllocObjectFromCodeInstrumented(inst->VRegB_21c(), shadow_frame.GetMethod(), + self, do_access_check); if (UNLIKELY(obj == NULL)) { HANDLE_PENDING_EXCEPTION(); } else { @@ -435,8 +435,8 @@ JValue ExecuteSwitchImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem case Instruction::NEW_ARRAY: { PREAMBLE(); int32_t length = shadow_frame.GetVReg(inst->VRegB_22c(inst_data)); - Object* obj = AllocArrayFromCode(inst->VRegC_22c(), shadow_frame.GetMethod(), - length, self, do_access_check); + Object* obj = AllocArrayFromCodeInstrumented(inst->VRegC_22c(), shadow_frame.GetMethod(), + length, self, do_access_check); if (UNLIKELY(obj == NULL)) { HANDLE_PENDING_EXCEPTION(); } else { diff --git a/runtime/runtime.cc b/runtime/runtime.cc index b4ce37fe1cf..17455dcdc3c 100644 --- a/runtime/runtime.cc +++ b/runtime/runtime.cc @@ -1299,7 +1299,7 @@ void Runtime::InstrumentQuickAllocEntryPoints() { { MutexLock mu(self, *Locks::runtime_shutdown_lock_); MutexLock mu2(self, *Locks::thread_list_lock_); - DCHECK_LE(quick_alloc_entry_points_instrumentation_counter_, 0); + DCHECK_GE(quick_alloc_entry_points_instrumentation_counter_, 0); int old_counter = quick_alloc_entry_points_instrumentation_counter_++; if (old_counter == 0) { // If it was disabled, enable it. @@ -1317,7 +1317,7 @@ void Runtime::UninstrumentQuickAllocEntryPoints() { { MutexLock mu(self, *Locks::runtime_shutdown_lock_); MutexLock mu2(self, *Locks::thread_list_lock_); - DCHECK_LT(quick_alloc_entry_points_instrumentation_counter_, 0); + DCHECK_GT(quick_alloc_entry_points_instrumentation_counter_, 0); int new_counter = --quick_alloc_entry_points_instrumentation_counter_; if (new_counter == 0) { // Disable it if the counter becomes zero. From a9650dd5e7195aec987a69a6ebbdaf33f73a6b00 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Fri, 4 Oct 2013 08:23:32 -0700 Subject: [PATCH 0068/2402] Implement thumb expansion of immediates. Change-Id: Ie50c17f82cbf97a16b58350b378914030cc0499f --- disassembler/disassembler_arm.cc | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/disassembler/disassembler_arm.cc b/disassembler/disassembler_arm.cc index 879d3ac71cc..782c1f32168 100644 --- a/disassembler/disassembler_arm.cc +++ b/disassembler/disassembler_arm.cc @@ -278,6 +278,26 @@ void DisassemblerArm::DumpArm(std::ostream& os, const uint8_t* instr_ptr) { os << StringPrintf("%p: %08x\t%-7s ", instr_ptr, instruction, opcode.c_str()) << args.str() << '\n'; } +int32_t ThumbExpand(int32_t imm12) { + if ((imm12 & 0xC00) == 0) { + switch ((imm12 >> 8) & 3) { + case 0: + return imm12 & 0xFF; + case 1: + return ((imm12 & 0xFF) << 16) | (imm12 & 0xFF); + case 2: + return ((imm12 & 0xFF) << 24) | ((imm12 & 0xFF) << 8); + default: // 3 + return ((imm12 & 0xFF) << 24) | ((imm12 & 0xFF) << 16) | ((imm12 & 0xFF) << 8) | + (imm12 & 0xFF); + } + } else { + uint32_t val = 0x80 | (imm12 & 0x7F); + int32_t rotate = (imm12 >> 7) & 0x1F; + return (val >> rotate) | (val << (32 - rotate)); + } +} + size_t DisassemblerArm::DumpThumb32(std::ostream& os, const uint8_t* instr_ptr) { uint32_t instr = (ReadU16(instr_ptr) << 16) | ReadU16(instr_ptr + 2); // |111|1 1|1000000|0000|1111110000000000| @@ -628,7 +648,7 @@ size_t DisassemblerArm::DumpThumb32(std::ostream& os, const uint8_t* instr_ptr) opcode << "s"; } } - args << Rd << ", ThumbExpand(" << imm32 << ")"; + args << Rd << ", #" << ThumbExpand(imm32); } else if (Rd.r == 0xF && S == 1 && (op3 == 0x0 || op3 == 0x4 || op3 == 0x8 || op3 == 0xD)) { if (op3 == 0x0) { @@ -640,7 +660,7 @@ size_t DisassemblerArm::DumpThumb32(std::ostream& os, const uint8_t* instr_ptr) } else { opcode << "cmp.w"; } - args << Rn << ", ThumbExpand(" << imm32 << ")"; + args << Rn << ", #" << ThumbExpand(imm32); } else { switch (op3) { case 0x0: opcode << "and"; break; @@ -658,7 +678,7 @@ size_t DisassemblerArm::DumpThumb32(std::ostream& os, const uint8_t* instr_ptr) if (S == 1) { opcode << "s"; } - args << Rd << ", " << Rn << ", ThumbExpand(" << imm32 << ")"; + args << Rd << ", " << Rn << ", #" << ThumbExpand(imm32); } } else if ((instr & 0x8000) == 0 && (op2 & 0x20) != 0) { // Data-processing (plain binary immediate) From a9a8254c920ce8e22210abfc16c9842ce0aea28f Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Fri, 4 Oct 2013 11:17:26 -0700 Subject: [PATCH 0069/2402] Improve quick codegen for aput-object. 1) don't type check known null. 2) if we know types in verify don't check at runtime. 3) if we're runtime checking then move all the code out-of-line. Also, don't set up a callee-save frame for check-cast, do an instance-of test then throw an exception if that fails. Tidy quick entry point of Ldivmod to Lmod which it is on x86 and mips. Fix monitor-enter/exit NPE for MIPS. Fix benign bug in mirror::Class::CannotBeAssignedFromOtherTypes, a byte[] cannot be assigned to from other types. Change-Id: I9cb3859ec70cca71ed79331ec8df5bec969d6745 --- compiler/dex/quick/arm/codegen_arm.h | 6 +- compiler/dex/quick/arm/int_arm.cc | 70 +--------- compiler/dex/quick/gen_common.cc | 21 ++- compiler/dex/quick/gen_invoke.cc | 16 +++ compiler/dex/quick/mips/codegen_mips.h | 6 +- compiler/dex/quick/mips/int_mips.cc | 63 +-------- compiler/dex/quick/mir_to_lir.cc | 29 ++-- compiler/dex/quick/mir_to_lir.h | 12 +- compiler/dex/quick/x86/codegen_x86.h | 6 +- compiler/dex/quick/x86/int_x86.cc | 60 +------- runtime/arch/arm/asm_support_arm.h | 2 + runtime/arch/arm/entrypoints_init_arm.cc | 14 +- runtime/arch/arm/quick_entrypoints_arm.S | 107 +++++++++++--- runtime/arch/arm/thread_arm.cc | 1 + runtime/arch/mips/asm_support_mips.h | 2 + runtime/arch/mips/entrypoints_init_mips.cc | 24 ++-- runtime/arch/mips/quick_entrypoints_mips.S | 125 ++++++++++++++--- runtime/arch/mips/thread_mips.cc | 1 + runtime/arch/x86/asm_support_x86.h | 2 + runtime/arch/x86/entrypoints_init_x86.cc | 16 ++- runtime/arch/x86/quick_entrypoints_x86.S | 130 +++++++++++++++--- runtime/arch/x86/thread_x86.cc | 1 + runtime/asm_support.h | 8 ++ .../quick/quick_cast_entrypoints.cc | 37 ----- runtime/entrypoints/quick/quick_entrypoints.h | 8 +- .../quick/quick_math_entrypoints.cc | 6 +- .../quick/quick_throw_entrypoints.cc | 20 ++- runtime/mirror/class.h | 10 +- runtime/mirror/object_test.cc | 18 ++- runtime/thread.cc | 6 +- runtime/verifier/method_verifier.cc | 66 ++++++--- runtime/verifier/method_verifier.h | 6 +- runtime/verifier/reg_type_cache.cc | 5 +- 33 files changed, 544 insertions(+), 360 deletions(-) diff --git a/compiler/dex/quick/arm/codegen_arm.h b/compiler/dex/quick/arm/codegen_arm.h index b75661cdfd3..aa5782b20cc 100644 --- a/compiler/dex/quick/arm/codegen_arm.h +++ b/compiler/dex/quick/arm/codegen_arm.h @@ -89,12 +89,10 @@ class ArmMir2Lir : public Mir2Lir { // Required for target - Dalvik-level generators. void GenArithImmOpLong(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2); - void GenArrayObjPut(int opt_flags, RegLocation rl_array, RegLocation rl_index, - RegLocation rl_src, int scale); void GenArrayGet(int opt_flags, OpSize size, RegLocation rl_array, RegLocation rl_index, RegLocation rl_dest, int scale); - void GenArrayPut(int opt_flags, OpSize size, RegLocation rl_array, - RegLocation rl_index, RegLocation rl_src, int scale); + void GenArrayPut(int opt_flags, OpSize size, RegLocation rl_array, RegLocation rl_index, + RegLocation rl_src, int scale, bool card_mark); void GenShiftImmOpLong(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_shift); void GenMulLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2); diff --git a/compiler/dex/quick/arm/int_arm.cc b/compiler/dex/quick/arm/int_arm.cc index 6d11b03d5e1..b1772fd81f8 100644 --- a/compiler/dex/quick/arm/int_arm.cc +++ b/compiler/dex/quick/arm/int_arm.cc @@ -771,7 +771,7 @@ void ArmMir2Lir::GenXorLong(RegLocation rl_dest, RegLocation rl_src1, * Generate array load */ void ArmMir2Lir::GenArrayGet(int opt_flags, OpSize size, RegLocation rl_array, - RegLocation rl_index, RegLocation rl_dest, int scale) { + RegLocation rl_index, RegLocation rl_dest, int scale) { RegisterClass reg_class = oat_reg_class_by_size(size); int len_offset = mirror::Array::LengthOffset().Int32Value(); int data_offset; @@ -861,13 +861,13 @@ void ArmMir2Lir::GenArrayGet(int opt_flags, OpSize size, RegLocation rl_array, * */ void ArmMir2Lir::GenArrayPut(int opt_flags, OpSize size, RegLocation rl_array, - RegLocation rl_index, RegLocation rl_src, int scale) { + RegLocation rl_index, RegLocation rl_src, int scale, bool card_mark) { RegisterClass reg_class = oat_reg_class_by_size(size); int len_offset = mirror::Array::LengthOffset().Int32Value(); - int data_offset; bool constant_index = rl_index.is_const; - if (rl_src.wide) { + int data_offset; + if (size == kLong || size == kDouble) { data_offset = mirror::Array::DataOffset(sizeof(int64_t)).Int32Value(); } else { data_offset = mirror::Array::DataOffset(sizeof(int32_t)).Int32Value(); @@ -943,68 +943,12 @@ void ArmMir2Lir::GenArrayPut(int opt_flags, OpSize size, RegLocation rl_array, if (!constant_index) { FreeTemp(reg_ptr); } -} - -/* - * Generate array store - * - */ -void ArmMir2Lir::GenArrayObjPut(int opt_flags, RegLocation rl_array, - RegLocation rl_index, RegLocation rl_src, int scale) { - int len_offset = mirror::Array::LengthOffset().Int32Value(); - int data_offset = mirror::Array::DataOffset(sizeof(mirror::Object*)).Int32Value(); - - FlushAllRegs(); // Use explicit registers - LockCallTemps(); - - int r_value = TargetReg(kArg0); // Register holding value - int r_array_class = TargetReg(kArg1); // Register holding array's Class - int r_array = TargetReg(kArg2); // Register holding array - int r_index = TargetReg(kArg3); // Register holding index into array - - LoadValueDirectFixed(rl_array, r_array); // Grab array - LoadValueDirectFixed(rl_src, r_value); // Grab value - LoadValueDirectFixed(rl_index, r_index); // Grab index - - GenNullCheck(rl_array.s_reg_low, r_array, opt_flags); // NPE? - - // Store of null? - LIR* null_value_check = OpCmpImmBranch(kCondEq, r_value, 0, NULL); - - // Get the array's class. - LoadWordDisp(r_array, mirror::Object::ClassOffset().Int32Value(), r_array_class); - CallRuntimeHelperRegReg(QUICK_ENTRYPOINT_OFFSET(pCanPutArrayElement), r_value, - r_array_class, true); - // Redo LoadValues in case they didn't survive the call. - LoadValueDirectFixed(rl_array, r_array); // Reload array - LoadValueDirectFixed(rl_index, r_index); // Reload index - LoadValueDirectFixed(rl_src, r_value); // Reload value - r_array_class = INVALID_REG; - - // Branch here if value to be stored == null - LIR* target = NewLIR0(kPseudoTargetLabel); - null_value_check->target = target; - - bool needs_range_check = (!(opt_flags & MIR_IGNORE_RANGE_CHECK)); - int reg_len = INVALID_REG; - if (needs_range_check) { - reg_len = TargetReg(kArg1); - LoadWordDisp(r_array, len_offset, reg_len); // Get len - } - /* r_ptr -> array data */ - int r_ptr = AllocTemp(); - OpRegRegImm(kOpAdd, r_ptr, r_array, data_offset); - if (needs_range_check) { - GenRegRegCheck(kCondCs, r_index, reg_len, kThrowArrayBounds); - } - StoreBaseIndexed(r_ptr, r_index, r_value, scale, kWord); - FreeTemp(r_ptr); - FreeTemp(r_index); - if (!mir_graph_->IsConstantNullRef(rl_src)) { - MarkGCCard(r_value, r_array); + if (card_mark) { + MarkGCCard(rl_src.low_reg, rl_array.low_reg); } } + void ArmMir2Lir::GenShiftImmOpLong(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src, RegLocation rl_shift) { rl_src = LoadValueWide(rl_src, kCoreReg); diff --git a/compiler/dex/quick/gen_common.cc b/compiler/dex/quick/gen_common.cc index 9e717491f08..2670c23a0dd 100644 --- a/compiler/dex/quick/gen_common.cc +++ b/compiler/dex/quick/gen_common.cc @@ -66,8 +66,7 @@ LIR* Mir2Lir::GenImmedCheck(ConditionCode c_code, int reg, int imm_val, ThrowKin /* Perform null-check on a register. */ LIR* Mir2Lir::GenNullCheck(int s_reg, int m_reg, int opt_flags) { - if (!(cu_->disable_opt & (1 << kNullCheckElimination)) && - opt_flags & MIR_IGNORE_NULL_CHECK) { + if (!(cu_->disable_opt & (1 << kNullCheckElimination)) && (opt_flags & MIR_IGNORE_NULL_CHECK)) { return NULL; } return GenImmedCheck(kCondEq, m_reg, 0, kThrowNullPointer); @@ -727,6 +726,18 @@ void Mir2Lir::GenIPut(uint32_t field_idx, int opt_flags, OpSize size, } } +void Mir2Lir::GenArrayObjPut(int opt_flags, RegLocation rl_array, RegLocation rl_index, + RegLocation rl_src) { + bool needs_range_check = !(opt_flags & MIR_IGNORE_RANGE_CHECK); + bool needs_null_check = !((cu_->disable_opt & (1 << kNullCheckElimination)) && + (opt_flags & MIR_IGNORE_NULL_CHECK)); + ThreadOffset helper = needs_range_check + ? (needs_null_check ? QUICK_ENTRYPOINT_OFFSET(pAputObjectWithNullAndBoundCheck) + : QUICK_ENTRYPOINT_OFFSET(pAputObjectWithBoundCheck)) + : QUICK_ENTRYPOINT_OFFSET(pAputObject); + CallRuntimeHelperRegLocationRegLocationRegLocation(helper, rl_array, rl_index, rl_src, true); +} + void Mir2Lir::GenConstClass(uint32_t type_idx, RegLocation rl_dest) { RegLocation rl_method = LoadCurrMethod(); int res_reg = AllocTemp(); @@ -1110,8 +1121,8 @@ void Mir2Lir::GenCheckCast(uint32_t insn_idx, uint32_t type_idx, RegLocation rl_ if (!type_known_abstract) { branch2 = OpCmpBranch(kCondEq, TargetReg(kArg1), class_reg, NULL); } - CallRuntimeHelperRegReg(QUICK_ENTRYPOINT_OFFSET(pCheckCast), TargetReg(kArg1), - TargetReg(kArg2), true); + CallRuntimeHelperRegReg(QUICK_ENTRYPOINT_OFFSET(pCheckCast), TargetReg(kArg2), + TargetReg(kArg1), true); /* branch target here */ LIR* target = NewLIR0(kPseudoTargetLabel); branch1->target = target; @@ -1648,7 +1659,7 @@ void Mir2Lir::GenArithOpLong(Instruction::Code opcode, RegLocation rl_dest, case Instruction::REM_LONG_2ADDR: call_out = true; check_zero = true; - func_offset = QUICK_ENTRYPOINT_OFFSET(pLdivmod); + func_offset = QUICK_ENTRYPOINT_OFFSET(pLmod); /* NOTE - for Arm, result is in kArg2/kArg3 instead of kRet0/kRet1 */ ret_reg = (cu_->instruction_set == kThumb2) ? TargetReg(kArg2) : TargetReg(kRet0); break; diff --git a/compiler/dex/quick/gen_invoke.cc b/compiler/dex/quick/gen_invoke.cc index 8270e017ad1..0a0cc17ab27 100644 --- a/compiler/dex/quick/gen_invoke.cc +++ b/compiler/dex/quick/gen_invoke.cc @@ -214,6 +214,7 @@ void Mir2Lir::CallRuntimeHelperImmRegLocationRegLocation(ThreadOffset helper_off int arg0, RegLocation arg1, RegLocation arg2, bool safepoint_pc) { int r_tgt = CallHelperSetup(helper_offset); + DCHECK_EQ(arg1.wide, 0U); LoadValueDirectFixed(arg1, TargetReg(kArg1)); if (arg2.wide == 0) { LoadValueDirectFixed(arg2, TargetReg(kArg2)); @@ -225,6 +226,21 @@ void Mir2Lir::CallRuntimeHelperImmRegLocationRegLocation(ThreadOffset helper_off CallHelper(r_tgt, helper_offset, safepoint_pc); } +void Mir2Lir::CallRuntimeHelperRegLocationRegLocationRegLocation(ThreadOffset helper_offset, + RegLocation arg0, RegLocation arg1, + RegLocation arg2, + bool safepoint_pc) { + int r_tgt = CallHelperSetup(helper_offset); + DCHECK_EQ(arg0.wide, 0U); + LoadValueDirectFixed(arg0, TargetReg(kArg0)); + DCHECK_EQ(arg1.wide, 0U); + LoadValueDirectFixed(arg1, TargetReg(kArg1)); + DCHECK_EQ(arg1.wide, 0U); + LoadValueDirectFixed(arg2, TargetReg(kArg2)); + ClobberCalleeSave(); + CallHelper(r_tgt, helper_offset, safepoint_pc); +} + /* * If there are any ins passed in registers that have not been promoted * to a callee-save register, flush them to the frame. Perform intial diff --git a/compiler/dex/quick/mips/codegen_mips.h b/compiler/dex/quick/mips/codegen_mips.h index 892af091730..387fef35b82 100644 --- a/compiler/dex/quick/mips/codegen_mips.h +++ b/compiler/dex/quick/mips/codegen_mips.h @@ -88,12 +88,10 @@ class MipsMir2Lir : public Mir2Lir { // Required for target - Dalvik-level generators. void GenArithImmOpLong(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2); - void GenArrayObjPut(int opt_flags, RegLocation rl_array, RegLocation rl_index, - RegLocation rl_src, int scale); void GenArrayGet(int opt_flags, OpSize size, RegLocation rl_array, - RegLocation rl_index, RegLocation rl_dest, int scale); + RegLocation rl_index, RegLocation rl_dest, int scale); void GenArrayPut(int opt_flags, OpSize size, RegLocation rl_array, - RegLocation rl_index, RegLocation rl_src, int scale); + RegLocation rl_index, RegLocation rl_src, int scale, bool card_mark); void GenShiftImmOpLong(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_shift); void GenMulLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2); diff --git a/compiler/dex/quick/mips/int_mips.cc b/compiler/dex/quick/mips/int_mips.cc index 6ce5750a5fc..218ed489b46 100644 --- a/compiler/dex/quick/mips/int_mips.cc +++ b/compiler/dex/quick/mips/int_mips.cc @@ -484,7 +484,7 @@ void MipsMir2Lir::GenArrayGet(int opt_flags, OpSize size, RegLocation rl_array, * */ void MipsMir2Lir::GenArrayPut(int opt_flags, OpSize size, RegLocation rl_array, - RegLocation rl_index, RegLocation rl_src, int scale) { + RegLocation rl_index, RegLocation rl_src, int scale, bool card_mark) { RegisterClass reg_class = oat_reg_class_by_size(size); int len_offset = mirror::Array::LengthOffset().Int32Value(); int data_offset; @@ -549,65 +549,8 @@ void MipsMir2Lir::GenArrayPut(int opt_flags, OpSize size, RegLocation rl_array, StoreBaseIndexed(reg_ptr, rl_index.low_reg, rl_src.low_reg, scale, size); } -} - -/* - * Generate array store - * - */ -void MipsMir2Lir::GenArrayObjPut(int opt_flags, RegLocation rl_array, - RegLocation rl_index, RegLocation rl_src, int scale) { - int len_offset = mirror::Array::LengthOffset().Int32Value(); - int data_offset = mirror::Array::DataOffset(sizeof(mirror::Object*)).Int32Value(); - - FlushAllRegs(); // Use explicit registers - LockCallTemps(); - - int r_value = TargetReg(kArg0); // Register holding value - int r_array_class = TargetReg(kArg1); // Register holding array's Class - int r_array = TargetReg(kArg2); // Register holding array - int r_index = TargetReg(kArg3); // Register holding index into array - - LoadValueDirectFixed(rl_array, r_array); // Grab array - LoadValueDirectFixed(rl_src, r_value); // Grab value - LoadValueDirectFixed(rl_index, r_index); // Grab index - - GenNullCheck(rl_array.s_reg_low, r_array, opt_flags); // NPE? - - // Store of null? - LIR* null_value_check = OpCmpImmBranch(kCondEq, r_value, 0, NULL); - - // Get the array's class. - LoadWordDisp(r_array, mirror::Object::ClassOffset().Int32Value(), r_array_class); - CallRuntimeHelperRegReg(QUICK_ENTRYPOINT_OFFSET(pCanPutArrayElement), r_value, - r_array_class, true); - // Redo LoadValues in case they didn't survive the call. - LoadValueDirectFixed(rl_array, r_array); // Reload array - LoadValueDirectFixed(rl_index, r_index); // Reload index - LoadValueDirectFixed(rl_src, r_value); // Reload value - r_array_class = INVALID_REG; - - // Branch here if value to be stored == null - LIR* target = NewLIR0(kPseudoTargetLabel); - null_value_check->target = target; - - bool needs_range_check = (!(opt_flags & MIR_IGNORE_RANGE_CHECK)); - int reg_len = INVALID_REG; - if (needs_range_check) { - reg_len = TargetReg(kArg1); - LoadWordDisp(r_array, len_offset, reg_len); // Get len - } - /* r_ptr -> array data */ - int r_ptr = AllocTemp(); - OpRegRegImm(kOpAdd, r_ptr, r_array, data_offset); - if (needs_range_check) { - GenRegRegCheck(kCondCs, r_index, reg_len, kThrowArrayBounds); - } - StoreBaseIndexed(r_ptr, r_index, r_value, scale, kWord); - FreeTemp(r_ptr); - FreeTemp(r_index); - if (!mir_graph_->IsConstantNullRef(rl_src)) { - MarkGCCard(r_value, r_array); + if (card_mark) { + MarkGCCard(rl_src.low_reg, rl_array.low_reg); } } diff --git a/compiler/dex/quick/mir_to_lir.cc b/compiler/dex/quick/mir_to_lir.cc index 66ece2c2307..2b26c3de807 100644 --- a/compiler/dex/quick/mir_to_lir.cc +++ b/compiler/dex/quick/mir_to_lir.cc @@ -338,22 +338,35 @@ void Mir2Lir::CompileDalvikInstruction(MIR* mir, BasicBlock* bb, LIR* label_list GenArrayGet(opt_flags, kSignedHalf, rl_src[0], rl_src[1], rl_dest, 1); break; case Instruction::APUT_WIDE: - GenArrayPut(opt_flags, kLong, rl_src[1], rl_src[2], rl_src[0], 3); + GenArrayPut(opt_flags, kLong, rl_src[1], rl_src[2], rl_src[0], 3, false); break; case Instruction::APUT: - GenArrayPut(opt_flags, kWord, rl_src[1], rl_src[2], rl_src[0], 2); - break; - case Instruction::APUT_OBJECT: - GenArrayObjPut(opt_flags, rl_src[1], rl_src[2], rl_src[0], 2); + GenArrayPut(opt_flags, kWord, rl_src[1], rl_src[2], rl_src[0], 2, false); + break; + case Instruction::APUT_OBJECT: { + bool is_null = mir_graph_->IsConstantNullRef(rl_src[0]); + bool is_safe = is_null; // Always safe to store null. + if (!is_safe) { + // Check safety from verifier type information. + const MethodReference mr(cu_->dex_file, cu_->method_idx); + is_safe = cu_->compiler_driver->IsSafeCast(mr, mir->offset); + } + if (is_null || is_safe) { + // Store of constant null doesn't require an assignability test and can be generated inline + // without fixed register usage or a card mark. + GenArrayPut(opt_flags, kWord, rl_src[1], rl_src[2], rl_src[0], 2, !is_null); + } else { + GenArrayObjPut(opt_flags, rl_src[1], rl_src[2], rl_src[0]); + } break; + } case Instruction::APUT_SHORT: case Instruction::APUT_CHAR: - GenArrayPut(opt_flags, kUnsignedHalf, rl_src[1], rl_src[2], rl_src[0], 1); + GenArrayPut(opt_flags, kUnsignedHalf, rl_src[1], rl_src[2], rl_src[0], 1, false); break; case Instruction::APUT_BYTE: case Instruction::APUT_BOOLEAN: - GenArrayPut(opt_flags, kUnsignedByte, rl_src[1], rl_src[2], - rl_src[0], 0); + GenArrayPut(opt_flags, kUnsignedByte, rl_src[1], rl_src[2], rl_src[0], 0, false); break; case Instruction::IGET_OBJECT: diff --git a/compiler/dex/quick/mir_to_lir.h b/compiler/dex/quick/mir_to_lir.h index 61f448458c2..21711e567f1 100644 --- a/compiler/dex/quick/mir_to_lir.h +++ b/compiler/dex/quick/mir_to_lir.h @@ -419,6 +419,9 @@ class Mir2Lir : public Backend { RegLocation rl_dest, RegLocation rl_obj, bool is_long_or_double, bool is_object); void GenIPut(uint32_t field_idx, int opt_flags, OpSize size, RegLocation rl_src, RegLocation rl_obj, bool is_long_or_double, bool is_object); + void GenArrayObjPut(int opt_flags, RegLocation rl_array, RegLocation rl_index, + RegLocation rl_src); + void GenConstClass(uint32_t type_idx, RegLocation rl_dest); void GenConstString(uint32_t string_idx, RegLocation rl_dest); void GenNewInstance(uint32_t type_idx, RegLocation rl_dest); @@ -475,6 +478,10 @@ class Mir2Lir : public Backend { void CallRuntimeHelperImmRegLocationRegLocation(ThreadOffset helper_offset, int arg0, RegLocation arg1, RegLocation arg2, bool safepoint_pc); + void CallRuntimeHelperRegLocationRegLocationRegLocation(ThreadOffset helper_offset, + RegLocation arg0, RegLocation arg1, + RegLocation arg2, + bool safepoint_pc); void GenInvoke(CallInfo* info); void FlushIns(RegLocation* ArgLocs, RegLocation rl_method); int GenDalvikArgsNoRange(CallInfo* info, int call_state, LIR** pcrLabel, @@ -651,12 +658,11 @@ class Mir2Lir : public Backend { RegLocation rl_src) = 0; virtual void GenSpecialCase(BasicBlock* bb, MIR* mir, SpecialCaseHandler special_case) = 0; - virtual void GenArrayObjPut(int opt_flags, RegLocation rl_array, - RegLocation rl_index, RegLocation rl_src, int scale) = 0; virtual void GenArrayGet(int opt_flags, OpSize size, RegLocation rl_array, RegLocation rl_index, RegLocation rl_dest, int scale) = 0; virtual void GenArrayPut(int opt_flags, OpSize size, RegLocation rl_array, - RegLocation rl_index, RegLocation rl_src, int scale) = 0; + RegLocation rl_index, RegLocation rl_src, int scale, + bool card_mark) = 0; virtual void GenShiftImmOpLong(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_shift) = 0; diff --git a/compiler/dex/quick/x86/codegen_x86.h b/compiler/dex/quick/x86/codegen_x86.h index d5c21e5fdc9..c266e39bbef 100644 --- a/compiler/dex/quick/x86/codegen_x86.h +++ b/compiler/dex/quick/x86/codegen_x86.h @@ -88,14 +88,12 @@ class X86Mir2Lir : public Mir2Lir { // Required for target - Dalvik-level generators. void GenArithImmOpLong(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2); - void GenArrayObjPut(int opt_flags, RegLocation rl_array, - RegLocation rl_index, RegLocation rl_src, int scale); void GenArrayGet(int opt_flags, OpSize size, RegLocation rl_array, RegLocation rl_index, RegLocation rl_dest, int scale); void GenArrayPut(int opt_flags, OpSize size, RegLocation rl_array, - RegLocation rl_index, RegLocation rl_src, int scale); + RegLocation rl_index, RegLocation rl_src, int scale, bool card_mark); void GenShiftImmOpLong(Instruction::Code opcode, RegLocation rl_dest, - RegLocation rl_src1, RegLocation rl_shift); + RegLocation rl_src1, RegLocation rl_shift); void GenMulLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2); void GenAddLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2); void GenAndLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2); diff --git a/compiler/dex/quick/x86/int_x86.cc b/compiler/dex/quick/x86/int_x86.cc index 14be7dde907..14f5348c125 100644 --- a/compiler/dex/quick/x86/int_x86.cc +++ b/compiler/dex/quick/x86/int_x86.cc @@ -419,7 +419,7 @@ void X86Mir2Lir::OpRegThreadMem(OpKind op, int r_dest, ThreadOffset thread_offse * Generate array load */ void X86Mir2Lir::GenArrayGet(int opt_flags, OpSize size, RegLocation rl_array, - RegLocation rl_index, RegLocation rl_dest, int scale) { + RegLocation rl_index, RegLocation rl_dest, int scale) { RegisterClass reg_class = oat_reg_class_by_size(size); int len_offset = mirror::Array::LengthOffset().Int32Value(); int data_offset; @@ -466,7 +466,7 @@ void X86Mir2Lir::GenArrayGet(int opt_flags, OpSize size, RegLocation rl_array, * */ void X86Mir2Lir::GenArrayPut(int opt_flags, OpSize size, RegLocation rl_array, - RegLocation rl_index, RegLocation rl_src, int scale) { + RegLocation rl_index, RegLocation rl_src, int scale, bool card_mark) { RegisterClass reg_class = oat_reg_class_by_size(size); int len_offset = mirror::Array::LengthOffset().Int32Value(); int data_offset; @@ -502,59 +502,9 @@ void X86Mir2Lir::GenArrayPut(int opt_flags, OpSize size, RegLocation rl_array, StoreBaseIndexedDisp(rl_array.low_reg, rl_index.low_reg, scale, data_offset, rl_src.low_reg, rl_src.high_reg, size, INVALID_SREG); } -} - -/* - * Generate array store - * - */ -void X86Mir2Lir::GenArrayObjPut(int opt_flags, RegLocation rl_array, - RegLocation rl_index, RegLocation rl_src, int scale) { - int len_offset = mirror::Array::LengthOffset().Int32Value(); - int data_offset = mirror::Array::DataOffset(sizeof(mirror::Object*)).Int32Value(); - - FlushAllRegs(); // Use explicit registers - LockCallTemps(); - - int r_value = TargetReg(kArg0); // Register holding value - int r_array_class = TargetReg(kArg1); // Register holding array's Class - int r_array = TargetReg(kArg2); // Register holding array - int r_index = TargetReg(kArg3); // Register holding index into array - - LoadValueDirectFixed(rl_array, r_array); // Grab array - LoadValueDirectFixed(rl_src, r_value); // Grab value - LoadValueDirectFixed(rl_index, r_index); // Grab index - - GenNullCheck(rl_array.s_reg_low, r_array, opt_flags); // NPE? - - // Store of null? - LIR* null_value_check = OpCmpImmBranch(kCondEq, r_value, 0, NULL); - - // Get the array's class. - LoadWordDisp(r_array, mirror::Object::ClassOffset().Int32Value(), r_array_class); - CallRuntimeHelperRegReg(QUICK_ENTRYPOINT_OFFSET(pCanPutArrayElement), r_value, - r_array_class, true); - // Redo LoadValues in case they didn't survive the call. - LoadValueDirectFixed(rl_array, r_array); // Reload array - LoadValueDirectFixed(rl_index, r_index); // Reload index - LoadValueDirectFixed(rl_src, r_value); // Reload value - r_array_class = INVALID_REG; - - // Branch here if value to be stored == null - LIR* target = NewLIR0(kPseudoTargetLabel); - null_value_check->target = target; - - // make an extra temp available for card mark below - FreeTemp(TargetReg(kArg1)); - if (!(opt_flags & MIR_IGNORE_RANGE_CHECK)) { - /* if (rl_index >= [rl_array + len_offset]) goto kThrowArrayBounds */ - GenRegMemCheck(kCondUge, r_index, r_array, len_offset, kThrowArrayBounds); - } - StoreBaseIndexedDisp(r_array, r_index, scale, - data_offset, r_value, INVALID_REG, kWord, INVALID_SREG); - FreeTemp(r_index); - if (!mir_graph_->IsConstantNullRef(rl_src)) { - MarkGCCard(r_value, r_array); + if (card_mark) { + FreeTemp(rl_index.low_reg); // Ensure there are 2 free regs for card mark. + MarkGCCard(rl_src.low_reg, rl_array.low_reg); } } diff --git a/runtime/arch/arm/asm_support_arm.h b/runtime/arch/arm/asm_support_arm.h index 69fb9c3f528..cfffbea2c50 100644 --- a/runtime/arch/arm/asm_support_arm.h +++ b/runtime/arch/arm/asm_support_arm.h @@ -25,6 +25,8 @@ #define rSELF r9 // Offset of field Thread::suspend_count_ verified in InitCpu #define THREAD_FLAGS_OFFSET 0 +// Offset of field Thread::card_table_ verified in InitCpu +#define THREAD_CARD_TABLE_OFFSET 8 // Offset of field Thread::exception_ verified in InitCpu #define THREAD_EXCEPTION_OFFSET 12 // Offset of field Thread::thin_lock_thread_id_ verified in InitCpu diff --git a/runtime/arch/arm/entrypoints_init_arm.cc b/runtime/arch/arm/entrypoints_init_arm.cc index e6e13be0b28..352982fc3a5 100644 --- a/runtime/arch/arm/entrypoints_init_arm.cc +++ b/runtime/arch/arm/entrypoints_init_arm.cc @@ -52,7 +52,6 @@ extern "C" void* art_quick_check_and_alloc_array_with_access_check_instrumented( // Cast entrypoints. extern "C" uint32_t artIsAssignableFromCode(const mirror::Class* klass, const mirror::Class* ref_class); -extern "C" void art_quick_can_put_array_element(void*, void*); extern "C" void art_quick_check_cast(void*, void*); // DexCache entrypoints. @@ -78,7 +77,10 @@ extern "C" int64_t art_quick_get64_static(uint32_t); extern "C" void* art_quick_get_obj_instance(uint32_t, void*); extern "C" void* art_quick_get_obj_static(uint32_t); -// FillArray entrypoint. +// Array entrypoints. +extern "C" void art_quick_aput_obj_with_null_and_bound_check(void*, uint32_t, void*); +extern "C" void art_quick_aput_obj_with_bound_check(void*, uint32_t, void*); +extern "C" void art_quick_aput_obj(void*, uint32_t, void*); extern "C" void art_quick_handle_fill_data(void*, void*); // Lock entrypoints. @@ -182,7 +184,6 @@ void InitEntryPoints(InterpreterEntryPoints* ipoints, JniEntryPoints* jpoints, // Cast qpoints->pInstanceofNonTrivial = artIsAssignableFromCode; - qpoints->pCanPutArrayElement = art_quick_can_put_array_element; qpoints->pCheckCast = art_quick_check_cast; // DexCache @@ -205,7 +206,10 @@ void InitEntryPoints(InterpreterEntryPoints* ipoints, JniEntryPoints* jpoints, qpoints->pGet64Static = art_quick_get64_static; qpoints->pGetObjStatic = art_quick_get_obj_static; - // FillArray + // Array + qpoints->pAputObjectWithNullAndBoundCheck = art_quick_aput_obj_with_null_and_bound_check; + qpoints->pAputObjectWithBoundCheck = art_quick_aput_obj_with_bound_check; + qpoints->pAputObject = art_quick_aput_obj; qpoints->pHandleFillArrayData = art_quick_handle_fill_data; // JNI @@ -236,7 +240,7 @@ void InitEntryPoints(InterpreterEntryPoints* ipoints, JniEntryPoints* jpoints, qpoints->pD2l = art_d2l; qpoints->pF2l = art_f2l; qpoints->pLdiv = __aeabi_ldivmod; - qpoints->pLdivmod = __aeabi_ldivmod; // result returned in r2:r3 + qpoints->pLmod = __aeabi_ldivmod; // result returned in r2:r3 qpoints->pLmul = art_quick_mul_long; qpoints->pShlLong = art_quick_shl_long; qpoints->pShrLong = art_quick_shr_long; diff --git a/runtime/arch/arm/quick_entrypoints_arm.S b/runtime/arch/arm/quick_entrypoints_arm.S index cb61698751d..d0731770a89 100644 --- a/runtime/arch/arm/quick_entrypoints_arm.S +++ b/runtime/arch/arm/quick_entrypoints_arm.S @@ -152,6 +152,7 @@ ENTRY \c_name mov r1, r9 @ pass Thread::Current mov r2, sp @ pass SP b \cxx_name @ \cxx_name(Thread*, SP) + bkpt END \c_name .endm @@ -162,6 +163,7 @@ ENTRY \c_name mov r2, r9 @ pass Thread::Current mov r3, sp @ pass SP b \cxx_name @ \cxx_name(Thread*, SP) + bkpt END \c_name .endm @@ -389,33 +391,96 @@ slow_unlock: END art_quick_unlock_object /* - * Entry from managed code that calls artCheckCastFromCode and delivers exception on failure. + * Entry from managed code that calls artIsAssignableFromCode and on failure calls + * artThrowClassCastException. */ - .extern artCheckCastFromCode + .extern artThrowClassCastException ENTRY art_quick_check_cast - SETUP_REF_ONLY_CALLEE_SAVE_FRAME @ save callee saves in case exception allocation triggers GC - mov r2, r9 @ pass Thread::Current - mov r3, sp @ pass SP - bl artCheckCastFromCode @ (Class* a, Class* b, Thread*, SP) - RESTORE_REF_ONLY_CALLEE_SAVE_FRAME - RETURN_IF_RESULT_IS_ZERO - DELIVER_PENDING_EXCEPTION + push {r0-r1, lr} @ save arguments, link register and pad + .save {r0-r1, lr} + .cfi_adjust_cfa_offset 12 + .cfi_rel_offset r0, 0 + .cfi_rel_offset r1, 4 + .cfi_rel_offset lr, 8 + sub sp, #4 + .pad #4 + .cfi_adjust_cfa_offset 4 + bl artIsAssignableFromCode + cbz r0, throw_class_cast_exception + add sp, #4 + .cfi_adjust_cfa_offset -4 + pop {r0-r1, pc} +throw_class_cast_exception: + add sp, #4 + .cfi_adjust_cfa_offset -4 + pop {r0-r1, lr} + SETUP_SAVE_ALL_CALLEE_SAVE_FRAME // save all registers as basis for long jump context + mov r2, r9 @ pass Thread::Current + mov r3, sp @ pass SP + b artThrowClassCastException @ (Class*, Class*, Thread*, SP) + bkpt END art_quick_check_cast /* - * Entry from managed code that calls artCanPutArrayElementFromCode and delivers exception on - * failure. + * Entry from managed code for array put operations of objects where the value being stored + * needs to be checked for compatibility. + * r0 = array, r1 = index, r2 = value */ - .extern artCanPutArrayElementFromCode -ENTRY art_quick_can_put_array_element - SETUP_REF_ONLY_CALLEE_SAVE_FRAME @ save callee saves in case exception allocation triggers GC - mov r2, r9 @ pass Thread::Current - mov r3, sp @ pass SP - bl artCanPutArrayElementFromCode @ (Object* element, Class* array_class, Thread*, SP) - RESTORE_REF_ONLY_CALLEE_SAVE_FRAME - RETURN_IF_RESULT_IS_ZERO - DELIVER_PENDING_EXCEPTION -END art_quick_can_put_array_element +ENTRY art_quick_aput_obj_with_null_and_bound_check + tst r0, r0 + bne art_quick_aput_obj_with_bound_check + b art_quick_throw_null_pointer_exception +END art_quick_aput_obj_with_null_and_bound_check + +ENTRY art_quick_aput_obj_with_bound_check + ldr r3, [r0, #ARRAY_LENGTH_OFFSET] + cmp r3, r1 + bhi art_quick_aput_obj + mov r0, r1 + mov r1, r3 + b art_quick_throw_array_bounds +END art_quick_aput_obj_with_bound_check + +ENTRY art_quick_aput_obj + cbz r2, do_aput_null + ldr r3, [r0, #CLASS_OFFSET] + ldr ip, [r2, #CLASS_OFFSET] + ldr r3, [r3, #CLASS_COMPONENT_TYPE_OFFSET] + cmp r3, ip @ value's type == array's component type - trivial assignability + bne check_assignability +do_aput: + add r3, r0, #OBJECT_ARRAY_DATA_OFFSET + str r2, [r3, r1, lsl #2] + ldr r3, [r9, #THREAD_CARD_TABLE_OFFSET] + lsr r0, r0, #7 + strb r3, [r3, r0] + blx lr +do_aput_null: + add r3, r0, #OBJECT_ARRAY_DATA_OFFSET + str r2, [r3, r1, lsl #2] + blx lr +check_assignability: + push {r0-r2, lr} @ save arguments + mov r1, ip + mov r0, r3 + bl artIsAssignableFromCode + cbz r0, throw_array_store_exception + pop {r0-r2, lr} + add r3, r0, #OBJECT_ARRAY_DATA_OFFSET + str r2, [r3, r1, lsl #2] + ldr r3, [r9, #THREAD_CARD_TABLE_OFFSET] + lsr r0, r0, #7 + strb r3, [r3, r0] + blx lr +throw_array_store_exception: + pop {r0-r2, lr} + SETUP_SAVE_ALL_CALLEE_SAVE_FRAME + mov r1, r2 + mov r2, r9 @ pass Thread::Current + mov r3, sp @ pass SP + b artThrowArrayStoreException @ (Class*, Class*, Thread*, SP) + bkpt @ unreached +END art_quick_aput_obj /* * Entry from managed code when uninitialized static storage, this stub will run the class diff --git a/runtime/arch/arm/thread_arm.cc b/runtime/arch/arm/thread_arm.cc index 75eef60de24..8c1efeb3d0d 100644 --- a/runtime/arch/arm/thread_arm.cc +++ b/runtime/arch/arm/thread_arm.cc @@ -23,6 +23,7 @@ namespace art { void Thread::InitCpu() { CHECK_EQ(THREAD_FLAGS_OFFSET, OFFSETOF_MEMBER(Thread, state_and_flags_)); + CHECK_EQ(THREAD_CARD_TABLE_OFFSET, OFFSETOF_MEMBER(Thread, card_table_)); CHECK_EQ(THREAD_EXCEPTION_OFFSET, OFFSETOF_MEMBER(Thread, exception_)); CHECK_EQ(THREAD_ID_OFFSET, OFFSETOF_MEMBER(Thread, thin_lock_thread_id_)); } diff --git a/runtime/arch/mips/asm_support_mips.h b/runtime/arch/mips/asm_support_mips.h index 9a66352ad18..530799789a8 100644 --- a/runtime/arch/mips/asm_support_mips.h +++ b/runtime/arch/mips/asm_support_mips.h @@ -25,6 +25,8 @@ #define rSELF $s1 // Offset of field Thread::suspend_count_ verified in InitCpu #define THREAD_FLAGS_OFFSET 0 +// Offset of field Thread::card_table_ verified in InitCpu +#define THREAD_CARD_TABLE_OFFSET 8 // Offset of field Thread::exception_ verified in InitCpu #define THREAD_EXCEPTION_OFFSET 12 diff --git a/runtime/arch/mips/entrypoints_init_mips.cc b/runtime/arch/mips/entrypoints_init_mips.cc index 3d08298151d..cc975d75a31 100644 --- a/runtime/arch/mips/entrypoints_init_mips.cc +++ b/runtime/arch/mips/entrypoints_init_mips.cc @@ -51,7 +51,6 @@ extern "C" void* art_quick_check_and_alloc_array_with_access_check_instrumented( // Cast entrypoints. extern "C" uint32_t artIsAssignableFromCode(const mirror::Class* klass, const mirror::Class* ref_class); -extern "C" void art_quick_can_put_array_element(void*, void*); extern "C" void art_quick_check_cast(void*, void*); // DexCache entrypoints. @@ -77,7 +76,10 @@ extern "C" int64_t art_quick_get64_static(uint32_t); extern "C" void* art_quick_get_obj_instance(uint32_t, void*); extern "C" void* art_quick_get_obj_static(uint32_t); -// FillArray entrypoint. +// Array entrypoints. +extern "C" void art_quick_aput_obj_with_null_and_bound_check(void*, uint32_t, void*); +extern "C" void art_quick_aput_obj_with_bound_check(void*, uint32_t, void*); +extern "C" void art_quick_aput_obj(void*, uint32_t, void*); extern "C" void art_quick_handle_fill_data(void*, void*); // Lock entrypoints. @@ -89,9 +91,9 @@ extern int32_t CmpgDouble(double a, double b); extern int32_t CmplDouble(double a, double b); extern int32_t CmpgFloat(float a, float b); extern int32_t CmplFloat(float a, float b); -extern "C" int64_t artLmulFromCode(int64_t a, int64_t b); -extern "C" int64_t artLdivFromCode(int64_t a, int64_t b); -extern "C" int64_t artLdivmodFromCode(int64_t a, int64_t b); +extern "C" int64_t artLmul(int64_t a, int64_t b); +extern "C" int64_t artLdiv(int64_t a, int64_t b); +extern "C" int64_t artLmod(int64_t a, int64_t b); // Math conversions. extern "C" int32_t __fixsfsi(float op1); // FLOAT_TO_INT @@ -183,7 +185,6 @@ void InitEntryPoints(InterpreterEntryPoints* ipoints, JniEntryPoints* jpoints, // Cast qpoints->pInstanceofNonTrivial = artIsAssignableFromCode; - qpoints->pCanPutArrayElement = art_quick_can_put_array_element; qpoints->pCheckCast = art_quick_check_cast; // DexCache @@ -206,7 +207,10 @@ void InitEntryPoints(InterpreterEntryPoints* ipoints, JniEntryPoints* jpoints, qpoints->pGet64Static = art_quick_get64_static; qpoints->pGetObjStatic = art_quick_get_obj_static; - // FillArray + // Array + qpoints->pAputObjectWithNullAndBoundCheck = art_quick_aput_obj_with_null_and_bound_check; + qpoints->pAputObjectWithBoundCheck = art_quick_aput_obj_with_bound_check; + qpoints->pAputObject = art_quick_aput_obj; qpoints->pHandleFillArrayData = art_quick_handle_fill_data; // JNI @@ -235,9 +239,9 @@ void InitEntryPoints(InterpreterEntryPoints* ipoints, JniEntryPoints* jpoints, qpoints->pIdivmod = NULL; qpoints->pD2l = art_d2l; qpoints->pF2l = art_f2l; - qpoints->pLdiv = artLdivFromCode; - qpoints->pLdivmod = artLdivmodFromCode; - qpoints->pLmul = artLmulFromCode; + qpoints->pLdiv = artLdiv; + qpoints->pLmod = artLmod; + qpoints->pLmul = artLmul; qpoints->pShlLong = art_quick_shl_long; qpoints->pShrLong = art_quick_shr_long; qpoints->pUshrLong = art_quick_ushr_long; diff --git a/runtime/arch/mips/quick_entrypoints_mips.S b/runtime/arch/mips/quick_entrypoints_mips.S index f9b703f1134..e9c66986d52 100644 --- a/runtime/arch/mips/quick_entrypoints_mips.S +++ b/runtime/arch/mips/quick_entrypoints_mips.S @@ -283,6 +283,7 @@ END art_quick_deliver_exception .extern artThrowNullPointerExceptionFromCode ENTRY art_quick_throw_null_pointer_exception GENERATE_GLOBAL_POINTER +art_quick_throw_null_pointer_exception_gp_set: SETUP_SAVE_ALL_CALLEE_SAVE_FRAME move $a0, rSELF # pass Thread::Current la $t9, artThrowNullPointerExceptionFromCode @@ -309,6 +310,7 @@ END art_quick_throw_div_zero .extern artThrowArrayBoundsFromCode ENTRY art_quick_throw_array_bounds GENERATE_GLOBAL_POINTER +art_quick_throw_array_bounds_gp_set: SETUP_SAVE_ALL_CALLEE_SAVE_FRAME move $a2, rSELF # pass Thread::Current la $t9, artThrowArrayBoundsFromCode @@ -481,6 +483,8 @@ END art_quick_handle_fill_data .extern artLockObjectFromCode ENTRY art_quick_lock_object GENERATE_GLOBAL_POINTER + beqz $a0, art_quick_throw_null_pointer_exception_gp_set + nop SETUP_REF_ONLY_CALLEE_SAVE_FRAME # save callee saves in case we block move $a1, rSELF # pass Thread::Current jal artLockObjectFromCode # (Object* obj, Thread*, $sp) @@ -494,6 +498,8 @@ END art_quick_lock_object .extern artUnlockObjectFromCode ENTRY art_quick_unlock_object GENERATE_GLOBAL_POINTER + beqz $a0, art_quick_throw_null_pointer_exception_gp_set + nop SETUP_REF_ONLY_CALLEE_SAVE_FRAME # save callee saves in case exception allocation triggers GC move $a1, rSELF # pass Thread::Current jal artUnlockObjectFromCode # (Object* obj, Thread*, $sp) @@ -504,29 +510,116 @@ END art_quick_unlock_object /* * Entry from managed code that calls artCheckCastFromCode and delivers exception on failure. */ - .extern artCheckCastFromCode + .extern artThrowClassCastException ENTRY art_quick_check_cast GENERATE_GLOBAL_POINTER - SETUP_REF_ONLY_CALLEE_SAVE_FRAME # save callee saves in case exception allocation triggers GC - move $a2, rSELF # pass Thread::Current - jal artCheckCastFromCode # (Class* a, Class* b, Thread*, $sp) - move $a3, $sp # pass $sp - RETURN_IF_ZERO + addiu $sp, $sp, -16 + .cfi_adjust_cfa_offset 16 + sw $ra, 12($sp) + .cfi_rel_offset 31, 12 + sw $t9, 8($sp) + sw $a1, 4($sp) + sw $a0, 0($sp) + jal artIsAssignableFromCode + nop + beqz $v0, throw_class_cast_exception + lw $ra, 12($sp) + jr $ra + addiu $sp, $sp, 16 + .cfi_adjust_cfa_offset -16 +throw_class_cast_exception: + lw $t9, 8($sp) + lw $a1, 4($sp) + lw $a0, 0($sp) + addiu $sp, $sp, 16 + .cfi_adjust_cfa_offset -16 + SETUP_SAVE_ALL_CALLEE_SAVE_FRAME + move $a2, rSELF # pass Thread::Current + la $t9, artThrowClassCastException + jr $t9 # artThrowClassCastException (Class*, Class*, Thread*, SP) + move $a3, $sp # pass $sp END art_quick_check_cast /* - * Entry from managed code that calls artCanPutArrayElementFromCode and delivers exception on - * failure. + * Entry from managed code for array put operations of objects where the value being stored + * needs to be checked for compatibility. + * a0 = array, a1 = index, a2 = value */ - .extern artCanPutArrayElementFromCode -ENTRY art_quick_can_put_array_element +ENTRY art_quick_aput_obj_with_null_and_bound_check GENERATE_GLOBAL_POINTER - SETUP_REF_ONLY_CALLEE_SAVE_FRAME # save callee saves in case exception allocation triggers GC - move $a2, rSELF # pass Thread::Current - jal artCanPutArrayElementFromCode # (Object* element, Class* array_class, Thread*, $sp) - move $a3, $sp # pass $sp - RETURN_IF_ZERO -END art_quick_can_put_array_element + bnez $a0, art_quick_aput_obj_with_bound_check_gp_set + nop + b art_quick_throw_null_pointer_exception_gp_set + nop +END art_quick_aput_obj_with_null_and_bound_check + +ENTRY art_quick_aput_obj_with_bound_check + GENERATE_GLOBAL_POINTER +art_quick_aput_obj_with_bound_check_gp_set: + lw $t0, ARRAY_LENGTH_OFFSET($a0) + sltu $t1, $a1, $t0 + bnez $t1, art_quick_aput_obj_gp_set + nop + move $a0, $a1 + b art_quick_throw_array_bounds_gp_set + move $a1, $t0 +END art_quick_aput_obj_with_bound_check + +ENTRY art_quick_aput_obj + GENERATE_GLOBAL_POINTER +art_quick_aput_obj_gp_set: + beqz $a2, do_aput_null + nop + lw $t0, CLASS_OFFSET($a0) + lw $t1, CLASS_OFFSET($a2) + lw $t0, CLASS_COMPONENT_TYPE_OFFSET($t0) + bne $t1, $t0, check_assignability # value's type == array's component type - trivial assignability + nop +do_aput: + sll $a1, $a1, 2 + add $t0, $a0, $a1 + sw $a2, OBJECT_ARRAY_DATA_OFFSET($t0) + lw $t0, THREAD_CARD_TABLE_OFFSET(rSELF) + srl $t1, $a0, 7 + add $t1, $t1, $t0 + sb $t0, ($t1) + jr $ra + nop +do_aput_null: + sll $a1, $a1, 2 + add $t0, $a0, $a1 + sw $a2, OBJECT_ARRAY_DATA_OFFSET($t0) + jr $ra + nop +check_assignability: + addiu $sp, $sp, -32 + .cfi_adjust_cfa_offset 32 + sw $ra, 28($sp) + .cfi_rel_offset 31, 28 + sw $t9, 12($sp) + sw $a2, 8($sp) + sw $a1, 4($sp) + sw $a0, 0($sp) + move $a1, $t1 + move $a0, $t0 + jal artIsAssignableFromCode # (Class*, Class*) + nop + lw $ra, 28($sp) + lw $t9, 12($sp) + lw $a2, 8($sp) + lw $a1, 4($sp) + lw $a0, 0($sp) + add $sp, 32 + .cfi_adjust_cfa_offset -32 + bnez $v0, do_aput + nop + SETUP_SAVE_ALL_CALLEE_SAVE_FRAME + move $a1, $a2 + move $a2, rSELF # pass Thread::Current + la $t9, artThrowArrayStoreException + jr $t9 # artThrowArrayStoreException(Class*, Class*, Thread*, SP) + move $a3, $sp # pass $sp +END art_quick_aput_obj /* * Entry from managed code when uninitialized static storage, this stub will run the class diff --git a/runtime/arch/mips/thread_mips.cc b/runtime/arch/mips/thread_mips.cc index 7364de067e6..bd545497fc2 100644 --- a/runtime/arch/mips/thread_mips.cc +++ b/runtime/arch/mips/thread_mips.cc @@ -23,6 +23,7 @@ namespace art { void Thread::InitCpu() { CHECK_EQ(THREAD_FLAGS_OFFSET, OFFSETOF_MEMBER(Thread, state_and_flags_)); + CHECK_EQ(THREAD_CARD_TABLE_OFFSET, OFFSETOF_MEMBER(Thread, card_table_)); CHECK_EQ(THREAD_EXCEPTION_OFFSET, OFFSETOF_MEMBER(Thread, exception_)); } diff --git a/runtime/arch/x86/asm_support_x86.h b/runtime/arch/x86/asm_support_x86.h index d4e092753f0..e817ff78e66 100644 --- a/runtime/arch/x86/asm_support_x86.h +++ b/runtime/arch/x86/asm_support_x86.h @@ -21,6 +21,8 @@ // Offset of field Thread::self_ verified in InitCpu #define THREAD_SELF_OFFSET 40 +// Offset of field Thread::card_table_ verified in InitCpu +#define THREAD_CARD_TABLE_OFFSET 8 // Offset of field Thread::exception_ verified in InitCpu #define THREAD_EXCEPTION_OFFSET 12 // Offset of field Thread::thin_lock_thread_id_ verified in InitCpu diff --git a/runtime/arch/x86/entrypoints_init_x86.cc b/runtime/arch/x86/entrypoints_init_x86.cc index 4c87e076080..89dd1b8f8ca 100644 --- a/runtime/arch/x86/entrypoints_init_x86.cc +++ b/runtime/arch/x86/entrypoints_init_x86.cc @@ -50,7 +50,6 @@ extern "C" void* art_quick_check_and_alloc_array_with_access_check_instrumented( // Cast entrypoints. extern "C" uint32_t art_quick_is_assignable(const mirror::Class* klass, const mirror::Class* ref_class); -extern "C" void art_quick_can_put_array_element(void*, void*); extern "C" void art_quick_check_cast(void*, void*); // DexCache entrypoints. @@ -73,7 +72,10 @@ extern "C" int64_t art_quick_get64_static(uint32_t); extern "C" void* art_quick_get_obj_instance(uint32_t, void*); extern "C" void* art_quick_get_obj_static(uint32_t); -// FillArray entrypoint. +// Array entrypoints. +extern "C" void art_quick_aput_obj_with_null_and_bound_check(void*, uint32_t, void*); +extern "C" void art_quick_aput_obj_with_bound_check(void*, uint32_t, void*); +extern "C" void art_quick_aput_obj(void*, uint32_t, void*); extern "C" void art_quick_handle_fill_data(void*, void*); // Lock entrypoints. @@ -89,7 +91,7 @@ extern "C" int64_t art_quick_d2l(double); extern "C" int64_t art_quick_f2l(float); extern "C" int32_t art_quick_idivmod(int32_t, int32_t); extern "C" int64_t art_quick_ldiv(int64_t, int64_t); -extern "C" int64_t art_quick_ldivmod(int64_t, int64_t); +extern "C" int64_t art_quick_lmod(int64_t, int64_t); extern "C" int64_t art_quick_lmul(int64_t, int64_t); extern "C" uint64_t art_quick_lshl(uint64_t, uint32_t); extern "C" uint64_t art_quick_lshr(uint64_t, uint32_t); @@ -165,7 +167,6 @@ void InitEntryPoints(InterpreterEntryPoints* ipoints, JniEntryPoints* jpoints, // Cast qpoints->pInstanceofNonTrivial = art_quick_is_assignable; - qpoints->pCanPutArrayElement = art_quick_can_put_array_element; qpoints->pCheckCast = art_quick_check_cast; // DexCache @@ -188,7 +189,10 @@ void InitEntryPoints(InterpreterEntryPoints* ipoints, JniEntryPoints* jpoints, qpoints->pGet64Static = art_quick_get64_static; qpoints->pGetObjStatic = art_quick_get_obj_static; - // FillArray + // Array + qpoints->pAputObjectWithNullAndBoundCheck = art_quick_aput_obj_with_null_and_bound_check; + qpoints->pAputObjectWithBoundCheck = art_quick_aput_obj_with_bound_check; + qpoints->pAputObject = art_quick_aput_obj; qpoints->pHandleFillArrayData = art_quick_handle_fill_data; // JNI @@ -218,7 +222,7 @@ void InitEntryPoints(InterpreterEntryPoints* ipoints, JniEntryPoints* jpoints, qpoints->pD2l = art_quick_d2l; qpoints->pF2l = art_quick_f2l; qpoints->pLdiv = art_quick_ldiv; - qpoints->pLdivmod = art_quick_ldivmod; + qpoints->pLmod = art_quick_lmod; qpoints->pLmul = art_quick_lmul; qpoints->pShlLong = art_quick_lshl; qpoints->pShrLong = art_quick_lshr; diff --git a/runtime/arch/x86/quick_entrypoints_x86.S b/runtime/arch/x86/quick_entrypoints_x86.S index 6be73d1aa16..9fce72f7802 100644 --- a/runtime/arch/x86/quick_entrypoints_x86.S +++ b/runtime/arch/x86/quick_entrypoints_x86.S @@ -479,14 +479,115 @@ END_FUNCTION art_quick_unlock_object DEFINE_FUNCTION art_quick_is_assignable PUSH eax // alignment padding - PUSH ecx // pass arg2 - PUSH eax // pass arg1 - call SYMBOL(artIsAssignableFromCode) // (Class* a, Class* b, Thread*, SP) + PUSH ecx // pass arg2 - obj->klass + PUSH eax // pass arg1 - checked class + call SYMBOL(artIsAssignableFromCode) // (Class* klass, Class* ref_klass) addl LITERAL(12), %esp // pop arguments .cfi_adjust_cfa_offset -12 ret END_FUNCTION art_quick_is_assignable +DEFINE_FUNCTION art_quick_check_cast + PUSH eax // alignment padding + PUSH ecx // pass arg2 - obj->klass + PUSH eax // pass arg1 - checked class + call SYMBOL(artIsAssignableFromCode) // (Class* klass, Class* ref_klass) + testl %eax, %eax + jz 1f // jump forward if not assignable + addl LITERAL(12), %esp // pop arguments + .cfi_adjust_cfa_offset -12 + ret +1: + POP eax // pop arguments + POP ecx + addl LITERAL(4), %esp + .cfi_adjust_cfa_offset -12 + SETUP_SAVE_ALL_CALLEE_SAVE_FRAME // save all registers as basis for long jump context + mov %esp, %edx + // Outgoing argument set up + PUSH edx // pass SP + pushl %fs:THREAD_SELF_OFFSET // pass Thread::Current() + .cfi_adjust_cfa_offset 4 + PUSH ecx // pass arg2 + PUSH eax // pass arg1 + call SYMBOL(artThrowClassCastException) // (Class* a, Class* b, Thread*, SP) + int3 // unreached +END_FUNCTION art_quick_check_cast + + /* + * Entry from managed code for array put operations of objects where the value being stored + * needs to be checked for compatibility. + * eax = array, ecx = index, edx = value + */ +DEFINE_FUNCTION art_quick_aput_obj_with_null_and_bound_check + testl %eax, %eax + jnz art_quick_aput_obj_with_bound_check + jmp art_quick_throw_null_pointer_exception +END_FUNCTION art_quick_aput_obj_with_null_and_bound_check + +DEFINE_FUNCTION art_quick_aput_obj_with_bound_check + movl ARRAY_LENGTH_OFFSET(%eax), %ebx + cmpl %ebx, %ecx + jb art_quick_aput_obj + mov %ecx, %eax + mov %ebx, %ecx + jmp art_quick_throw_array_bounds +END_FUNCTION art_quick_aput_obj_with_bound_check + +DEFINE_FUNCTION art_quick_aput_obj + test %edx, %edx // store of null + jz do_aput_null + movl CLASS_OFFSET(%eax), %ebx + movl CLASS_COMPONENT_TYPE_OFFSET(%ebx), %ebx + cmpl CLASS_OFFSET(%edx), %ebx // value's type == array's component type - trivial assignability + jne check_assignability +do_aput: + movl %edx, OBJECT_ARRAY_DATA_OFFSET(%eax, %ecx, 4) + movl %fs:THREAD_CARD_TABLE_OFFSET, %edx + shrl LITERAL(7), %eax + movb %dl, (%edx, %eax) + ret +do_aput_null: + movl %edx, OBJECT_ARRAY_DATA_OFFSET(%eax, %ecx, 4) + ret +check_assignability: + PUSH eax // save arguments + PUSH ecx + PUSH edx + subl LITERAL(8), %esp // alignment padding + .cfi_adjust_cfa_offset 8 + pushl CLASS_OFFSET(%edx) // pass arg2 - type of the value to be stored + .cfi_adjust_cfa_offset 4 + PUSH ebx // pass arg1 - component type of the array + call SYMBOL(artIsAssignableFromCode) // (Class* a, Class* b) + addl LITERAL(16), %esp // pop arguments + .cfi_adjust_cfa_offset -16 + testl %eax, %eax + jz throw_array_store_exception + POP edx + POP ecx + POP eax + movl %edx, OBJECT_ARRAY_DATA_OFFSET(%eax, %ecx, 4) // do the aput + movl %fs:THREAD_CARD_TABLE_OFFSET, %edx + shrl LITERAL(7), %eax + movb %dl, (%edx, %eax) + ret +throw_array_store_exception: + POP edx + POP ecx + POP eax + SETUP_SAVE_ALL_CALLEE_SAVE_FRAME // save all registers as basis for long jump context + mov %esp, %ecx + // Outgoing argument set up + PUSH ecx // pass SP + pushl %fs:THREAD_SELF_OFFSET // pass Thread::Current() + .cfi_adjust_cfa_offset 4 + PUSH edx // pass arg2 - value + PUSH eax // pass arg1 - array + call SYMBOL(artThrowArrayStoreException) // (array, value, Thread*, SP) + int3 // unreached +END_FUNCTION art_quick_aput_obj + DEFINE_FUNCTION art_quick_memcpy PUSH edx // pass arg3 PUSH ecx // pass arg2 @@ -497,9 +598,6 @@ DEFINE_FUNCTION art_quick_memcpy ret END_FUNCTION art_quick_memcpy -TWO_ARG_DOWNCALL art_quick_check_cast, artCheckCastFromCode, RETURN_IF_EAX_ZERO -TWO_ARG_DOWNCALL art_quick_can_put_array_element, artCanPutArrayElementFromCode, RETURN_IF_EAX_ZERO - NO_ARG_DOWNCALL art_quick_test_suspend, artTestSuspendFromCode, ret DEFINE_FUNCTION art_quick_fmod @@ -586,30 +684,30 @@ check_arg2: END_FUNCTION art_quick_idivmod DEFINE_FUNCTION art_quick_ldiv - subl LITERAL(12), %esp // alignment padding + subl LITERAL(12), %esp // alignment padding .cfi_adjust_cfa_offset 12 PUSH ebx // pass arg4 b.hi PUSH edx // pass arg3 b.lo PUSH ecx // pass arg2 a.hi - PUSH eax // pass arg1 a.lo - call SYMBOL(artLdivFromCode) // (jlong a, jlong b) - addl LITERAL(28), %esp // pop arguments + PUSH eax // pass arg1 a.lo + call SYMBOL(artLdiv) // (jlong a, jlong b) + addl LITERAL(28), %esp // pop arguments .cfi_adjust_cfa_offset -28 ret END_FUNCTION art_quick_ldiv -DEFINE_FUNCTION art_quick_ldivmod - subl LITERAL(12), %esp // alignment padding +DEFINE_FUNCTION art_quick_lmod + subl LITERAL(12), %esp // alignment padding .cfi_adjust_cfa_offset 12 PUSH ebx // pass arg4 b.hi PUSH edx // pass arg3 b.lo PUSH ecx // pass arg2 a.hi - PUSH eax // pass arg1 a.lo - call SYMBOL(artLdivmodFromCode) // (jlong a, jlong b) - addl LITERAL(28), %esp // pop arguments + PUSH eax // pass arg1 a.lo + call SYMBOL(artLmod) // (jlong a, jlong b) + addl LITERAL(28), %esp // pop arguments .cfi_adjust_cfa_offset -28 ret -END_FUNCTION art_quick_ldivmod +END_FUNCTION art_quick_lmod DEFINE_FUNCTION art_quick_lmul imul %eax, %ebx // ebx = a.lo(eax) * b.hi(ebx) diff --git a/runtime/arch/x86/thread_x86.cc b/runtime/arch/x86/thread_x86.cc index 7e0aee086c0..42789cb1cd2 100644 --- a/runtime/arch/x86/thread_x86.cc +++ b/runtime/arch/x86/thread_x86.cc @@ -134,6 +134,7 @@ void Thread::InitCpu() { // Sanity check other offsets. CHECK_EQ(THREAD_EXCEPTION_OFFSET, OFFSETOF_MEMBER(Thread, exception_)); + CHECK_EQ(THREAD_CARD_TABLE_OFFSET, OFFSETOF_MEMBER(Thread, card_table_)); CHECK_EQ(THREAD_ID_OFFSET, OFFSETOF_MEMBER(Thread, thin_lock_thread_id_)); } diff --git a/runtime/asm_support.h b/runtime/asm_support.h index d2eaf8e153c..a6700bc2e4b 100644 --- a/runtime/asm_support.h +++ b/runtime/asm_support.h @@ -22,8 +22,16 @@ #define SUSPEND_CHECK_INTERVAL (1000) // Offsets within java.lang.Object. +#define CLASS_OFFSET 0 #define LOCK_WORD_OFFSET 4 +// Offsets within java.lang.Class. +#define CLASS_COMPONENT_TYPE_OFFSET 12 + +// Array offsets. +#define ARRAY_LENGTH_OFFSET 8 +#define OBJECT_ARRAY_DATA_OFFSET 12 + // Offsets within java.lang.String. #define STRING_VALUE_OFFSET 8 #define STRING_COUNT_OFFSET 12 diff --git a/runtime/entrypoints/quick/quick_cast_entrypoints.cc b/runtime/entrypoints/quick/quick_cast_entrypoints.cc index 9ffa736614f..ae53d6c7bee 100644 --- a/runtime/entrypoints/quick/quick_cast_entrypoints.cc +++ b/runtime/entrypoints/quick/quick_cast_entrypoints.cc @@ -14,11 +14,8 @@ * limitations under the License. */ -#include "callee_save_frame.h" -#include "entrypoints/entrypoint_utils.h" #include "mirror/class-inl.h" #include "mirror/object-inl.h" -#include "mirror/object_array-inl.h" namespace art { @@ -31,38 +28,4 @@ extern "C" uint32_t artIsAssignableFromCode(const mirror::Class* klass, return klass->IsAssignableFrom(ref_class) ? 1 : 0; } -// Check whether it is safe to cast one class to the other, throw exception and return -1 on failure -extern "C" int artCheckCastFromCode(mirror::Class* src_type, mirror::Class* dest_type, - Thread* self, mirror::ArtMethod** sp) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - DCHECK(src_type->IsClass()) << PrettyClass(src_type); - DCHECK(dest_type->IsClass()) << PrettyClass(dest_type); - if (LIKELY(dest_type->IsAssignableFrom(src_type))) { - return 0; // Success - } else { - FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly); - ThrowClassCastException(dest_type, src_type); - return -1; // Failure - } -} - -// Tests whether 'element' can be assigned into an array of type 'array_class'. -// Returns 0 on success and -1 if an exception is pending. -extern "C" int artCanPutArrayElementFromCode(const mirror::Object* element, - const mirror::Class* array_class, - Thread* self, mirror::ArtMethod** sp) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - DCHECK(array_class != NULL); - // element can't be NULL as we catch this is screened in runtime_support - mirror::Class* element_class = element->GetClass(); - mirror::Class* component_type = array_class->GetComponentType(); - if (LIKELY(component_type->IsAssignableFrom(element_class))) { - return 0; // Success - } else { - FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly); - ThrowArrayStoreException(element_class, array_class); - return -1; // Failure - } -} - } // namespace art diff --git a/runtime/entrypoints/quick/quick_entrypoints.h b/runtime/entrypoints/quick/quick_entrypoints.h index 9d3b8ef27cd..e9964ad48c4 100644 --- a/runtime/entrypoints/quick/quick_entrypoints.h +++ b/runtime/entrypoints/quick/quick_entrypoints.h @@ -48,7 +48,6 @@ struct PACKED(4) QuickEntryPoints { // Cast uint32_t (*pInstanceofNonTrivial)(const mirror::Class*, const mirror::Class*); - void (*pCanPutArrayElement)(void*, void*); void (*pCheckCast)(void*, void*); // DexCache @@ -71,7 +70,10 @@ struct PACKED(4) QuickEntryPoints { void* (*pGetObjInstance)(uint32_t, void*); void* (*pGetObjStatic)(uint32_t); - // FillArray + // Array + void (*pAputObjectWithNullAndBoundCheck)(void*, uint32_t, void*); // array, index, src + void (*pAputObjectWithBoundCheck)(void*, uint32_t, void*); // array, index, src + void (*pAputObject)(void*, uint32_t, void*); // array, index, src void (*pHandleFillArrayData)(void*, void*); // JNI @@ -103,7 +105,7 @@ struct PACKED(4) QuickEntryPoints { int64_t (*pD2l)(double); int64_t (*pF2l)(float); int64_t (*pLdiv)(int64_t, int64_t); - int64_t (*pLdivmod)(int64_t, int64_t); + int64_t (*pLmod)(int64_t, int64_t); int64_t (*pLmul)(int64_t, int64_t); uint64_t (*pShlLong)(uint64_t, uint32_t); uint64_t (*pShrLong)(uint64_t, uint32_t); diff --git a/runtime/entrypoints/quick/quick_math_entrypoints.cc b/runtime/entrypoints/quick/quick_math_entrypoints.cc index 0bfe59dc2f9..014aad3b902 100644 --- a/runtime/entrypoints/quick/quick_math_entrypoints.cc +++ b/runtime/entrypoints/quick/quick_math_entrypoints.cc @@ -62,15 +62,15 @@ int CmplDouble(double a, double b) { return -1; } -extern "C" int64_t artLmulFromCode(int64_t a, int64_t b) { +extern "C" int64_t artLmul(int64_t a, int64_t b) { return a * b; } -extern "C" int64_t artLdivFromCode(int64_t a, int64_t b) { +extern "C" int64_t artLdiv(int64_t a, int64_t b) { return a / b; } -extern "C" int64_t artLdivmodFromCode(int64_t a, int64_t b) { +extern "C" int64_t artLmod(int64_t a, int64_t b) { return a % b; } diff --git a/runtime/entrypoints/quick/quick_throw_entrypoints.cc b/runtime/entrypoints/quick/quick_throw_entrypoints.cc index f67b2fc4ab0..31eacac114d 100644 --- a/runtime/entrypoints/quick/quick_throw_entrypoints.cc +++ b/runtime/entrypoints/quick/quick_throw_entrypoints.cc @@ -15,8 +15,9 @@ */ #include "callee_save_frame.h" +#include "common_throws.h" #include "entrypoints/entrypoint_utils.h" -#include "mirror/object.h" +#include "mirror/object-inl.h" #include "object_utils.h" #include "thread.h" #include "well_known_classes.h" @@ -95,4 +96,21 @@ extern "C" void artThrowNoSuchMethodFromCode(int32_t method_idx, Thread* self, self->QuickDeliverException(); } +extern "C" void artThrowClassCastException(mirror::Class* dest_type, mirror::Class* src_type, + Thread* self, mirror::ArtMethod** sp) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + FinishCalleeSaveFrameSetup(self, sp, Runtime::kSaveAll); + CHECK(!dest_type->IsAssignableFrom(src_type)); + ThrowClassCastException(dest_type, src_type); + self->QuickDeliverException(); +} + +extern "C" void artThrowArrayStoreException(mirror::Object* array, mirror::Object* value, + Thread* self, mirror::ArtMethod** sp) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + FinishCalleeSaveFrameSetup(self, sp, Runtime::kSaveAll); + ThrowArrayStoreException(value->GetClass(), array->GetClass()); + self->QuickDeliverException(); +} + } // namespace art diff --git a/runtime/mirror/class.h b/runtime/mirror/class.h index 586151de454..dbc6f575e75 100644 --- a/runtime/mirror/class.h +++ b/runtime/mirror/class.h @@ -247,7 +247,7 @@ class MANAGED Class : public StaticStorageBase { } else { Class* component = GetComponentType(); if (component->IsPrimitive()) { - return false; + return true; } else { return component->CannotBeAssignedFromOtherTypes(); } @@ -346,14 +346,18 @@ class MANAGED Class : public StaticStorageBase { bool IsArtMethodClass() const; + static MemberOffset ComponentTypeOffset() { + return OFFSET_OF_OBJECT_MEMBER(Class, component_type_); + } + Class* GetComponentType() const { - return GetFieldObject(OFFSET_OF_OBJECT_MEMBER(Class, component_type_), false); + return GetFieldObject(ComponentTypeOffset(), false); } void SetComponentType(Class* new_component_type) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { DCHECK(GetComponentType() == NULL); DCHECK(new_component_type != NULL); - SetFieldObject(OFFSET_OF_OBJECT_MEMBER(Class, component_type_), new_component_type, false); + SetFieldObject(ComponentTypeOffset(), new_component_type, false); } size_t GetComponentSize() const { diff --git a/runtime/mirror/object_test.cc b/runtime/mirror/object_test.cc index b8765afadc1..1e610f27841 100644 --- a/runtime/mirror/object_test.cc +++ b/runtime/mirror/object_test.cc @@ -71,12 +71,20 @@ class ObjectTest : public CommonTest { // Keep the assembly code in sync TEST_F(ObjectTest, AsmConstants) { - ASSERT_EQ(STRING_VALUE_OFFSET, String::ValueOffset().Int32Value()); - ASSERT_EQ(STRING_COUNT_OFFSET, String::CountOffset().Int32Value()); - ASSERT_EQ(STRING_OFFSET_OFFSET, String::OffsetOffset().Int32Value()); - ASSERT_EQ(STRING_DATA_OFFSET, Array::DataOffset(sizeof(uint16_t)).Int32Value()); + EXPECT_EQ(CLASS_OFFSET, Object::ClassOffset().Int32Value()); + EXPECT_EQ(LOCK_WORD_OFFSET, Object::MonitorOffset().Int32Value()); - ASSERT_EQ(METHOD_CODE_OFFSET, ArtMethod::EntryPointFromCompiledCodeOffset().Int32Value()); + EXPECT_EQ(CLASS_COMPONENT_TYPE_OFFSET, Class::ComponentTypeOffset().Int32Value()); + + EXPECT_EQ(ARRAY_LENGTH_OFFSET, Array::LengthOffset().Int32Value()); + EXPECT_EQ(OBJECT_ARRAY_DATA_OFFSET, Array::DataOffset(sizeof(Object*)).Int32Value()); + + EXPECT_EQ(STRING_VALUE_OFFSET, String::ValueOffset().Int32Value()); + EXPECT_EQ(STRING_COUNT_OFFSET, String::CountOffset().Int32Value()); + EXPECT_EQ(STRING_OFFSET_OFFSET, String::OffsetOffset().Int32Value()); + EXPECT_EQ(STRING_DATA_OFFSET, Array::DataOffset(sizeof(uint16_t)).Int32Value()); + + EXPECT_EQ(METHOD_CODE_OFFSET, ArtMethod::EntryPointFromCompiledCodeOffset().Int32Value()); } TEST_F(ObjectTest, IsInSamePackage) { diff --git a/runtime/thread.cc b/runtime/thread.cc index de14dbb0c0a..30636589cd3 100644 --- a/runtime/thread.cc +++ b/runtime/thread.cc @@ -1564,7 +1564,6 @@ static const EntryPointInfo gThreadEntryPointInfo[] = { QUICK_ENTRY_POINT_INFO(pCheckAndAllocArray), QUICK_ENTRY_POINT_INFO(pCheckAndAllocArrayWithAccessCheck), QUICK_ENTRY_POINT_INFO(pInstanceofNonTrivial), - QUICK_ENTRY_POINT_INFO(pCanPutArrayElement), QUICK_ENTRY_POINT_INFO(pCheckCast), QUICK_ENTRY_POINT_INFO(pInitializeStaticStorage), QUICK_ENTRY_POINT_INFO(pInitializeTypeAndVerifyAccess), @@ -1582,6 +1581,9 @@ static const EntryPointInfo gThreadEntryPointInfo[] = { QUICK_ENTRY_POINT_INFO(pGet64Static), QUICK_ENTRY_POINT_INFO(pGetObjInstance), QUICK_ENTRY_POINT_INFO(pGetObjStatic), + QUICK_ENTRY_POINT_INFO(pAputObjectWithNullAndBoundCheck), + QUICK_ENTRY_POINT_INFO(pAputObjectWithBoundCheck), + QUICK_ENTRY_POINT_INFO(pAputObject), QUICK_ENTRY_POINT_INFO(pHandleFillArrayData), QUICK_ENTRY_POINT_INFO(pJniMethodStart), QUICK_ENTRY_POINT_INFO(pJniMethodStartSynchronized), @@ -1606,7 +1608,7 @@ static const EntryPointInfo gThreadEntryPointInfo[] = { QUICK_ENTRY_POINT_INFO(pD2l), QUICK_ENTRY_POINT_INFO(pF2l), QUICK_ENTRY_POINT_INFO(pLdiv), - QUICK_ENTRY_POINT_INFO(pLdivmod), + QUICK_ENTRY_POINT_INFO(pLmod), QUICK_ENTRY_POINT_INFO(pLmul), QUICK_ENTRY_POINT_INFO(pShlLong), QUICK_ENTRY_POINT_INFO(pShrLong), diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc index 6b1ff7775cf..7d2ee19572e 100644 --- a/runtime/verifier/method_verifier.cc +++ b/runtime/verifier/method_verifier.cc @@ -506,17 +506,25 @@ bool MethodVerifier::ComputeWidthsAndCountOps() { while (dex_pc < insns_size) { Instruction::Code opcode = inst->Opcode(); - if (opcode == Instruction::NEW_INSTANCE) { - new_instance_count++; - } else if (opcode == Instruction::MONITOR_ENTER) { - monitor_enter_count++; - } else if (opcode == Instruction::CHECK_CAST) { - has_check_casts_ = true; - } else if ((inst->Opcode() == Instruction::INVOKE_VIRTUAL) || - (inst->Opcode() == Instruction::INVOKE_VIRTUAL_RANGE) || - (inst->Opcode() == Instruction::INVOKE_INTERFACE) || - (inst->Opcode() == Instruction::INVOKE_INTERFACE_RANGE)) { - has_virtual_or_interface_invokes_ = true; + switch (opcode) { + case Instruction::APUT_OBJECT: + case Instruction::CHECK_CAST: + has_check_casts_ = true; + break; + case Instruction::INVOKE_VIRTUAL: + case Instruction::INVOKE_VIRTUAL_RANGE: + case Instruction::INVOKE_INTERFACE: + case Instruction::INVOKE_INTERFACE_RANGE: + has_virtual_or_interface_invokes_ = true; + break; + case Instruction::MONITOR_ENTER: + monitor_enter_count++; + break; + case Instruction::NEW_INSTANCE: + new_instance_count++; + break; + default: + break; } size_t inst_size = inst->SizeInCodeUnits(); insn_flags_[dex_pc].SetLengthInCodeUnits(inst_size); @@ -3940,18 +3948,32 @@ MethodVerifier::MethodSafeCastSet* MethodVerifier::GenerateSafeCastSet() { code_item_->insns_size_in_code_units_); for (; inst < end; inst = inst->Next()) { - if (Instruction::CHECK_CAST != inst->Opcode()) { - continue; - } - uint32_t dex_pc = inst->GetDexPc(code_item_->insns_); - RegisterLine* line = reg_table_.GetLine(dex_pc); - const RegType& reg_type(line->GetRegisterType(inst->VRegA_21c())); - const RegType& cast_type = ResolveClassAndCheckAccess(inst->VRegB_21c()); - if (cast_type.IsStrictlyAssignableFrom(reg_type)) { - if (mscs.get() == NULL) { - mscs.reset(new MethodSafeCastSet()); + Instruction::Code code = inst->Opcode(); + if ((code == Instruction::CHECK_CAST) || (code == Instruction::APUT_OBJECT)) { + uint32_t dex_pc = inst->GetDexPc(code_item_->insns_); + RegisterLine* line = reg_table_.GetLine(dex_pc); + bool is_safe_cast = false; + if (code == Instruction::CHECK_CAST) { + const RegType& reg_type(line->GetRegisterType(inst->VRegA_21c())); + const RegType& cast_type = ResolveClassAndCheckAccess(inst->VRegB_21c()); + is_safe_cast = cast_type.IsStrictlyAssignableFrom(reg_type); + } else { + const RegType& array_type(line->GetRegisterType(inst->VRegB_23x())); + // We only know its safe to assign to an array if the array type is precise. For example, + // an Object[] can have any type of object stored in it, but it may also be assigned a + // String[] in which case the stores need to be of Strings. + if (array_type.IsPreciseReference()) { + const RegType& value_type(line->GetRegisterType(inst->VRegA_23x())); + const RegType& component_type(reg_types_.GetComponentType(array_type, class_loader_)); + is_safe_cast = component_type.IsStrictlyAssignableFrom(value_type); + } + } + if (is_safe_cast) { + if (mscs.get() == NULL) { + mscs.reset(new MethodSafeCastSet()); + } + mscs->insert(dex_pc); } - mscs->insert(dex_pc); } } return mscs.release(); diff --git a/runtime/verifier/method_verifier.h b/runtime/verifier/method_verifier.h index 7f337419a4d..57fde1d5d58 100644 --- a/runtime/verifier/method_verifier.h +++ b/runtime/verifier/method_verifier.h @@ -724,10 +724,12 @@ class MethodVerifier { // running and the verifier is called from the class linker. const bool allow_soft_failures_; - // Indicates if the method being verified contains at least one check-cast instruction. + // Indicates the method being verified contains at least one check-cast or aput-object + // instruction. Aput-object operations implicitly check for array-store exceptions, similar to + // check-cast. bool has_check_casts_; - // Indicates if the method being verified contains at least one invoke-virtual/range + // Indicates the method being verified contains at least one invoke-virtual/range // or invoke-interface/range. bool has_virtual_or_interface_invokes_; }; diff --git a/runtime/verifier/reg_type_cache.cc b/runtime/verifier/reg_type_cache.cc index fd7030011d0..446dd0080c5 100644 --- a/runtime/verifier/reg_type_cache.cc +++ b/runtime/verifier/reg_type_cache.cc @@ -531,8 +531,9 @@ const ConstantType& RegTypeCache::FromCat2ConstHi(int32_t value, bool precise) { } const RegType& RegTypeCache::GetComponentType(const RegType& array, mirror::ClassLoader* loader) { - CHECK(array.IsArrayTypes()); - if (array.IsUnresolvedTypes()) { + if (!array.IsArrayTypes()) { + return Conflict(); + } else if (array.IsUnresolvedTypes()) { const std::string& descriptor(array.GetDescriptor()); const std::string component(descriptor.substr(1, descriptor.size() - 1)); return FromDescriptor(loader, component.c_str(), false); From 6bcd163d322b867578fbcfe60e4e3b247c42974b Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Tue, 8 Oct 2013 18:50:47 -0700 Subject: [PATCH 0070/2402] Fix exception throwing in monitor-enter for MIPS. Need to deliver pending exception. Change-Id: I1bceb1b0d866acc7b3190e7c661993ad43fc1769 --- runtime/arch/mips/quick_entrypoints_mips.S | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/arch/mips/quick_entrypoints_mips.S b/runtime/arch/mips/quick_entrypoints_mips.S index e9c66986d52..7780bb3a25e 100644 --- a/runtime/arch/mips/quick_entrypoints_mips.S +++ b/runtime/arch/mips/quick_entrypoints_mips.S @@ -489,7 +489,7 @@ ENTRY art_quick_lock_object move $a1, rSELF # pass Thread::Current jal artLockObjectFromCode # (Object* obj, Thread*, $sp) move $a2, $sp # pass $sp - RESTORE_REF_ONLY_CALLEE_SAVE_FRAME_AND_RETURN + RETURN_IF_ZERO END art_quick_lock_object /* From 74e256b8e442417d4ba2054c771c1e4f41062768 Mon Sep 17 00:00:00 2001 From: Sebastien Hertz Date: Fri, 4 Oct 2013 10:40:37 +0200 Subject: [PATCH 0071/2402] Add missing references. This mainly avoids implicit copies when extracting an object from a container. Change-Id: If4d0e6153e8c2b48345fde5bb546b4c65649fcf3 --- compiler/dex/ssa_transformation.cc | 2 +- compiler/oat_writer.cc | 2 +- runtime/stack.cc | 4 ++-- runtime/stack.h | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/compiler/dex/ssa_transformation.cc b/compiler/dex/ssa_transformation.cc index 366d7f26be0..0ca5fd4aa3e 100644 --- a/compiler/dex/ssa_transformation.cc +++ b/compiler/dex/ssa_transformation.cc @@ -179,7 +179,7 @@ void MIRGraph::ComputeDomPostOrderTraversal(BasicBlock* bb) { bb->visited = true; work_stack.push_back(std::make_pair(bb, new (arena_) ArenaBitVector::Iterator(bb->i_dominated))); while (!work_stack.empty()) { - std::pair curr = work_stack.back(); + const std::pair& curr = work_stack.back(); BasicBlock* curr_bb = curr.first; ArenaBitVector::Iterator* curr_idom_iter = curr.second; int bb_idx = curr_idom_iter->Next(); diff --git a/compiler/oat_writer.cc b/compiler/oat_writer.cc index f9d6e4192dd..f23b72b466e 100644 --- a/compiler/oat_writer.cc +++ b/compiler/oat_writer.cc @@ -667,7 +667,7 @@ size_t OatWriter::WriteCodeMethod(OutputStream& out, const size_t file_offset, const CompiledMethod* compiled_method = compiler_driver_->GetCompiledMethod(MethodReference(&dex_file, method_idx)); - OatMethodOffsets method_offsets = + const OatMethodOffsets& method_offsets = oat_classes_[oat_class_index]->method_offsets_[class_def_method_index]; diff --git a/runtime/stack.cc b/runtime/stack.cc index 17156647ebd..5d3a9a5234c 100644 --- a/runtime/stack.cc +++ b/runtime/stack.cc @@ -253,7 +253,7 @@ std::string StackVisitor::DescribeLocation() const { return result; } -instrumentation::InstrumentationStackFrame StackVisitor::GetInstrumentationStackFrame(uint32_t depth) const { +instrumentation::InstrumentationStackFrame& StackVisitor::GetInstrumentationStackFrame(uint32_t depth) const { return thread_->GetInstrumentationStack()->at(depth); } @@ -309,7 +309,7 @@ void StackVisitor::WalkStack(bool include_transitions) { // While profiling, the return pc is restored from the side stack, except when walking // the stack for an exception where the side stack will be unwound in VisitFrame. if (GetQuickInstrumentationExitPc() == return_pc) { - instrumentation::InstrumentationStackFrame instrumentation_frame = + const instrumentation::InstrumentationStackFrame& instrumentation_frame = GetInstrumentationStackFrame(instrumentation_stack_depth); instrumentation_stack_depth++; if (GetMethod() == Runtime::Current()->GetCalleeSaveMethod(Runtime::kSaveAll)) { diff --git a/runtime/stack.h b/runtime/stack.h index 700b7f19603..a4b93bc4071 100644 --- a/runtime/stack.h +++ b/runtime/stack.h @@ -567,7 +567,7 @@ class StackVisitor { static void DescribeStack(Thread* thread) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); private: - instrumentation::InstrumentationStackFrame GetInstrumentationStackFrame(uint32_t depth) const; + instrumentation::InstrumentationStackFrame& GetInstrumentationStackFrame(uint32_t depth) const; void SanityCheckFrame() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); From e861ebd5d9490cc86200f3859f3d36fadad4588c Mon Sep 17 00:00:00 2001 From: Mathieu Chartier Date: Wed, 9 Oct 2013 15:01:21 -0700 Subject: [PATCH 0072/2402] Fix interpreter bugs. These showed up in compaction work. Change-Id: Iac8eb0a1395c25aabba9f2e0ff6b01fc6180bdca --- runtime/interpreter/interpreter.cc | 12 ++++++---- runtime/interpreter/interpreter_common.cc | 4 ++++ runtime/interpreter/interpreter_common.h | 24 ++++++++++++------- .../interpreter_goto_table_impl.cc | 6 ++--- .../interpreter/interpreter_switch_impl.cc | 6 ++--- 5 files changed, 33 insertions(+), 19 deletions(-) diff --git a/runtime/interpreter/interpreter.cc b/runtime/interpreter/interpreter.cc index 8aa6fa21048..48c00148b46 100644 --- a/runtime/interpreter/interpreter.cc +++ b/runtime/interpreter/interpreter.cc @@ -309,6 +309,7 @@ void EnterInterpreterFromInvoke(Thread* self, ArtMethod* method, Object* receive return; } + const char* old_cause = self->StartAssertNoThreadSuspension("EnterInterpreterFromInvoke"); MethodHelper mh(method); const DexFile::CodeItem* code_item = mh.GetCodeItem(); uint16_t num_regs; @@ -317,6 +318,7 @@ void EnterInterpreterFromInvoke(Thread* self, ArtMethod* method, Object* receive num_regs = code_item->registers_size_; num_ins = code_item->ins_size_; } else if (method->IsAbstract()) { + self->EndAssertNoThreadSuspension(old_cause); ThrowAbstractMethodError(method); return; } else { @@ -332,6 +334,8 @@ void EnterInterpreterFromInvoke(Thread* self, ArtMethod* method, Object* receive void* memory = alloca(ShadowFrame::ComputeSize(num_regs)); ShadowFrame* shadow_frame(ShadowFrame::Create(num_regs, last_shadow_frame, method, 0, memory)); self->PushShadowFrame(shadow_frame); + self->EndAssertNoThreadSuspension(old_cause); + size_t cur_reg = num_regs - num_ins; if (!method->IsStatic()) { CHECK(receiver != NULL); @@ -339,8 +343,7 @@ void EnterInterpreterFromInvoke(Thread* self, ArtMethod* method, Object* receive ++cur_reg; } else if (UNLIKELY(!method->GetDeclaringClass()->IsInitializing())) { ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); - if (UNLIKELY(!class_linker->EnsureInitialized(method->GetDeclaringClass(), - true, true))) { + if (UNLIKELY(!class_linker->EnsureInitialized(method->GetDeclaringClass(), true, true))) { CHECK(self->IsExceptionPending()); self->PopShadowFrame(); return; @@ -421,6 +424,7 @@ extern "C" void artInterpreterToInterpreterBridge(Thread* self, MethodHelper& mh return; } + self->PushShadowFrame(shadow_frame); ArtMethod* method = shadow_frame->GetMethod(); // Ensure static methods are initialized. if (method->IsStatic()) { @@ -429,12 +433,12 @@ extern "C" void artInterpreterToInterpreterBridge(Thread* self, MethodHelper& mh if (UNLIKELY(!Runtime::Current()->GetClassLinker()->EnsureInitialized(declaringClass, true, true))) { DCHECK(Thread::Current()->IsExceptionPending()); + self->PopShadowFrame(); return; } CHECK(declaringClass->IsInitializing()); } } - self->PushShadowFrame(shadow_frame); if (LIKELY(!method->IsNative())) { result->SetJ(Execute(self, mh, code_item, *shadow_frame, JValue()).GetJ()); @@ -442,7 +446,7 @@ extern "C" void artInterpreterToInterpreterBridge(Thread* self, MethodHelper& mh // We don't expect to be asked to interpret native code (which is entered via a JNI compiler // generated stub) except during testing and image writing. CHECK(!Runtime::Current()->IsStarted()); - Object* receiver = method->IsStatic() ? NULL : shadow_frame->GetVRegReference(0); + Object* receiver = method->IsStatic() ? nullptr : shadow_frame->GetVRegReference(0); uint32_t* args = shadow_frame->GetVRegArgs(method->IsStatic() ? 0 : 1); UnstartedRuntimeJni(self, method, receiver, args, result); } diff --git a/runtime/interpreter/interpreter_common.cc b/runtime/interpreter/interpreter_common.cc index f0f10bb0f5f..19f55d2f973 100644 --- a/runtime/interpreter/interpreter_common.cc +++ b/runtime/interpreter/interpreter_common.cc @@ -54,6 +54,7 @@ bool DoCall(ArtMethod* method, Object* receiver, Thread* self, ShadowFrame& shad } // Allocate shadow frame on the stack. + const char* old_cause = self->StartAssertNoThreadSuspension("DoCall"); void* memory = alloca(ShadowFrame::ComputeSize(num_regs)); ShadowFrame* new_shadow_frame(ShadowFrame::Create(num_regs, &shadow_frame, method, 0, memory)); @@ -94,9 +95,11 @@ bool DoCall(ArtMethod* method, Object* receiver, Thread* self, ShadowFrame& shad Class* arg_type = mh.GetClassFromTypeIdx(params->GetTypeItem(shorty_pos).type_idx_); if (arg_type == NULL) { CHECK(self->IsExceptionPending()); + self->EndAssertNoThreadSuspension(old_cause); return false; } if (!o->VerifierInstanceOf(arg_type)) { + self->EndAssertNoThreadSuspension(old_cause); // This should never happen. self->ThrowNewExceptionF(self->GetCurrentLocationForThrow(), "Ljava/lang/VirtualMachineError;", @@ -144,6 +147,7 @@ bool DoCall(ArtMethod* method, Object* receiver, Thread* self, ShadowFrame& shad } } } + self->EndAssertNoThreadSuspension(old_cause); // Do the call now. if (LIKELY(Runtime::Current()->IsStarted())) { diff --git a/runtime/interpreter/interpreter_common.h b/runtime/interpreter/interpreter_common.h index 481d6cc00cb..29b00d2e995 100644 --- a/runtime/interpreter/interpreter_common.h +++ b/runtime/interpreter/interpreter_common.h @@ -98,9 +98,14 @@ static inline bool DoInvoke(Thread* self, ShadowFrame& shadow_frame, const Instr uint16_t inst_data, JValue* result) { const uint32_t method_idx = (is_range) ? inst->VRegB_3rc() : inst->VRegB_35c(); const uint32_t vregC = (is_range) ? inst->VRegC_3rc() : inst->VRegC_35c(); - Object* const receiver = (type == kStatic) ? NULL : shadow_frame.GetVRegReference(vregC); + Object* receiver = (type == kStatic) ? nullptr : shadow_frame.GetVRegReference(vregC); ArtMethod* const method = FindMethodFromCode(method_idx, receiver, shadow_frame.GetMethod(), self, do_access_check, type); + if (type != kStatic) { + // Reload the vreg since the GC may have moved the object. + receiver = shadow_frame.GetVRegReference(vregC); + } + if (UNLIKELY(method == NULL)) { CHECK(self->IsExceptionPending()); result->SetJ(0); @@ -524,26 +529,27 @@ static void UnexpectedOpcode(const Instruction* inst, MethodHelper& mh) static inline void TraceExecution(const ShadowFrame& shadow_frame, const Instruction* inst, const uint32_t dex_pc, MethodHelper& mh) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - const bool kTracing = false; + constexpr bool kTracing = false; if (kTracing) { #define TRACE_LOG std::cerr - TRACE_LOG << PrettyMethod(shadow_frame.GetMethod()) - << StringPrintf("\n0x%x: ", dex_pc) - << inst->DumpString(&mh.GetDexFile()) << "\n"; + std::ostringstream oss; + oss << PrettyMethod(shadow_frame.GetMethod()) + << StringPrintf("\n0x%x: ", dex_pc) + << inst->DumpString(&mh.GetDexFile()) << "\n"; for (size_t i = 0; i < shadow_frame.NumberOfVRegs(); ++i) { uint32_t raw_value = shadow_frame.GetVReg(i); Object* ref_value = shadow_frame.GetVRegReference(i); - TRACE_LOG << StringPrintf(" vreg%d=0x%08X", i, raw_value); + oss << StringPrintf(" vreg%d=0x%08X", i, raw_value); if (ref_value != NULL) { if (ref_value->GetClass()->IsStringClass() && ref_value->AsString()->GetCharArray() != NULL) { - TRACE_LOG << "/java.lang.String \"" << ref_value->AsString()->ToModifiedUtf8() << "\""; + oss << "/java.lang.String \"" << ref_value->AsString()->ToModifiedUtf8() << "\""; } else { - TRACE_LOG << "/" << PrettyTypeOf(ref_value); + oss << "/" << PrettyTypeOf(ref_value); } } } - TRACE_LOG << "\n"; + TRACE_LOG << oss.str() << "\n"; #undef TRACE_LOG } } diff --git a/runtime/interpreter/interpreter_goto_table_impl.cc b/runtime/interpreter/interpreter_goto_table_impl.cc index daf6f93f1cd..3a91b8c3a63 100644 --- a/runtime/interpreter/interpreter_goto_table_impl.cc +++ b/runtime/interpreter/interpreter_goto_table_impl.cc @@ -264,12 +264,12 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* HANDLE_INSTRUCTION_START(RETURN_OBJECT) { JValue result; - Object* obj_result = shadow_frame.GetVRegReference(inst->VRegA_11x(inst_data)); - result.SetJ(0); - result.SetL(obj_result); if (UNLIKELY(self->TestAllFlags())) { CheckSuspend(self); } + Object* obj_result = shadow_frame.GetVRegReference(inst->VRegA_11x(inst_data)); + result.SetJ(0); + result.SetL(obj_result); if (do_assignability_check && obj_result != NULL) { Class* return_type = MethodHelper(shadow_frame.GetMethod()).GetReturnType(); if (return_type == NULL) { diff --git a/runtime/interpreter/interpreter_switch_impl.cc b/runtime/interpreter/interpreter_switch_impl.cc index c3977c6a3da..bd0d87eefc1 100644 --- a/runtime/interpreter/interpreter_switch_impl.cc +++ b/runtime/interpreter/interpreter_switch_impl.cc @@ -227,12 +227,12 @@ JValue ExecuteSwitchImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem case Instruction::RETURN_OBJECT: { PREAMBLE(); JValue result; - Object* obj_result = shadow_frame.GetVRegReference(inst->VRegA_11x(inst_data)); - result.SetJ(0); - result.SetL(obj_result); if (UNLIKELY(self->TestAllFlags())) { CheckSuspend(self); } + Object* obj_result = shadow_frame.GetVRegReference(inst->VRegA_11x(inst_data)); + result.SetJ(0); + result.SetL(obj_result); if (do_assignability_check && obj_result != NULL) { Class* return_type = MethodHelper(shadow_frame.GetMethod()).GetReturnType(); if (return_type == NULL) { From e732ef1c0192acd71925bd0ff1ab09640d45531d Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Wed, 9 Oct 2013 15:22:24 -0700 Subject: [PATCH 0073/2402] Make compiler stat dumping easier to use. Previous needed -verbose:compiler + a debug build. Now specify --dump-stats to dex2oat. Change-Id: Ifde379e7cd06bbb0fe20149ce89c3b3789221eac --- compiler/driver/compiler_driver.cc | 2 +- dex2oat/dex2oat.cc | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc index 056be1fb049..7c4a6ce7851 100644 --- a/compiler/driver/compiler_driver.cc +++ b/compiler/driver/compiler_driver.cc @@ -64,7 +64,7 @@ static void DumpStat(size_t x, size_t y, const char* str) { if (x == 0 && y == 0) { return; } - VLOG(compiler) << Percentage(x, y) << "% of " << str << " for " << (x + y) << " cases"; + LOG(INFO) << Percentage(x, y) << "% of " << str << " for " << (x + y) << " cases"; } class AOTCompilationStats { diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc index c4cce2ff732..d5d1303f554 100644 --- a/dex2oat/dex2oat.cc +++ b/dex2oat/dex2oat.cc @@ -604,7 +604,7 @@ static int dex2oat(int argc, char** argv) { #error "Unsupported architecture" #endif bool is_host = false; - bool dump_stats = kIsDebugBuild; + bool dump_stats = false; bool dump_timing = false; bool dump_slow_timing = kIsDebugBuild; bool watch_dog_enabled = !kIsTargetBuild; @@ -696,6 +696,8 @@ static int dex2oat(int argc, char** argv) { runtime_args.push_back(argv[i]); } else if (option == "--dump-timing") { dump_timing = true; + } else if (option == "--dump-stats") { + dump_stats = true; } else { Usage("Unknown argument %s", option.data()); } From be031fff278799984166ec866c2dd202447e0f23 Mon Sep 17 00:00:00 2001 From: Hiroshi Yamauchi Date: Tue, 8 Oct 2013 16:42:37 -0700 Subject: [PATCH 0074/2402] Remove four counter increments out of the allocation path. - This change removes the four allocation counters (the number of bytes allocated, the number of objects allocated, the number of bytes allocated ever, the number of objects allocated ever) from the dlmalloc space allocation path. Now those counter values are computed on the fly based on a combination of the two new counters that are incremented by the GC side (the number of bytes freed ever and the number of objects freed ever) and mspace_inspect_all() calls. - This results in a 1-2% speedup (though with some noise) in Ritz MemAllocTest on Nexus 4. Bug: 9986565 Change-Id: Id9a8e05a745ac1e5ea7a2b5fd9319814a9d4af13 --- runtime/gc/allocator/dlmalloc.cc | 16 ++++++++++++++ runtime/gc/allocator/dlmalloc.h | 6 +++++ runtime/gc/heap.cc | 6 +++-- runtime/gc/space/dlmalloc_space-inl.h | 4 ---- runtime/gc/space/dlmalloc_space.cc | 25 ++++++++++++++++----- runtime/gc/space/dlmalloc_space.h | 27 ++++++++--------------- runtime/gc/space/large_object_space.h | 8 +++---- runtime/gc/space/space.h | 8 +++---- runtime/native/dalvik_system_VMRuntime.cc | 5 +---- 9 files changed, 63 insertions(+), 42 deletions(-) diff --git a/runtime/gc/allocator/dlmalloc.cc b/runtime/gc/allocator/dlmalloc.cc index 3cc64e9da5c..a6a3ee7a579 100644 --- a/runtime/gc/allocator/dlmalloc.cc +++ b/runtime/gc/allocator/dlmalloc.cc @@ -69,3 +69,19 @@ extern "C" void DlmallocMadviseCallback(void* start, void* end, size_t used_byte *reclaimed += length; } } + +extern "C" void DlmallocBytesAllocatedCallback(void* start, void* end, size_t used_bytes, void* arg) { + if (used_bytes == 0) { + return; + } + size_t* bytes_allocated = reinterpret_cast(arg); + *bytes_allocated += used_bytes + sizeof(size_t); +} + +extern "C" void DlmallocObjectsAllocatedCallback(void* start, void* end, size_t used_bytes, void* arg) { + if (used_bytes == 0) { + return; + } + size_t* objects_allocated = reinterpret_cast(arg); + ++(*objects_allocated); +} diff --git a/runtime/gc/allocator/dlmalloc.h b/runtime/gc/allocator/dlmalloc.h index 19159b1353b..c820b192b91 100644 --- a/runtime/gc/allocator/dlmalloc.h +++ b/runtime/gc/allocator/dlmalloc.h @@ -39,4 +39,10 @@ extern "C" int dlmalloc_trim(size_t); // pages back to the kernel. extern "C" void DlmallocMadviseCallback(void* start, void* end, size_t used_bytes, void* /*arg*/); +// Callbacks for dlmalloc_inspect_all or mspace_inspect_all that will +// count the number of bytes allocated and objects allocated, +// respectively. +extern "C" void DlmallocBytesAllocatedCallback(void* start, void* end, size_t used_bytes, void* arg); +extern "C" void DlmallocObjectsAllocatedCallback(void* start, void* end, size_t used_bytes, void* arg); + #endif // ART_RUNTIME_GC_ALLOCATOR_DLMALLOC_H_ diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc index c0e46ac1658..ed902420af0 100644 --- a/runtime/gc/heap.cc +++ b/runtime/gc/heap.cc @@ -1999,8 +1999,10 @@ void Heap::RequestHeapTrim() { // We could try mincore(2) but that's only a measure of how many pages we haven't given away, // not how much use we're making of those pages. uint64_t ms_time = MilliTime(); - float utilization = - static_cast(alloc_space_->GetBytesAllocated()) / alloc_space_->Size(); + // Note the large object space's bytes allocated is equal to its capacity. + uint64_t los_bytes_allocated = large_object_space_->GetBytesAllocated(); + float utilization = static_cast(GetBytesAllocated() - los_bytes_allocated) / + (GetTotalMemory() - los_bytes_allocated); if ((utilization > 0.75f && !IsLowMemoryMode()) || ((ms_time - last_trim_time_ms_) < 2 * 1000)) { // Don't bother trimming the alloc space if it's more than 75% utilized and low memory mode is // not enabled, or if a heap trim occurred in the last two seconds. diff --git a/runtime/gc/space/dlmalloc_space-inl.h b/runtime/gc/space/dlmalloc_space-inl.h index 242ef6886e9..fb2c66b2fb7 100644 --- a/runtime/gc/space/dlmalloc_space-inl.h +++ b/runtime/gc/space/dlmalloc_space-inl.h @@ -47,10 +47,6 @@ inline mirror::Object* DlMallocSpace::AllocWithoutGrowthLocked(size_t num_bytes, size_t allocation_size = AllocationSizeNonvirtual(result); DCHECK(bytes_allocated != NULL); *bytes_allocated = allocation_size; - num_bytes_allocated_ += allocation_size; - total_bytes_allocated_ += allocation_size; - ++total_objects_allocated_; - ++num_objects_allocated_; } return result; } diff --git a/runtime/gc/space/dlmalloc_space.cc b/runtime/gc/space/dlmalloc_space.cc index 1b6fca7cbbe..468d1d23311 100644 --- a/runtime/gc/space/dlmalloc_space.cc +++ b/runtime/gc/space/dlmalloc_space.cc @@ -119,8 +119,7 @@ size_t DlMallocSpace::bitmap_index_ = 0; DlMallocSpace::DlMallocSpace(const std::string& name, MemMap* mem_map, void* mspace, byte* begin, byte* end, size_t growth_limit) : MemMapSpace(name, mem_map, end - begin, kGcRetentionPolicyAlwaysCollect), - recent_free_pos_(0), num_bytes_allocated_(0), num_objects_allocated_(0), - total_bytes_allocated_(0), total_objects_allocated_(0), + recent_free_pos_(0), total_bytes_freed_(0), total_objects_freed_(0), lock_("allocation space lock", kAllocSpaceLock), mspace_(mspace), growth_limit_(growth_limit) { CHECK(mspace != NULL); @@ -353,8 +352,8 @@ size_t DlMallocSpace::Free(Thread* self, mirror::Object* ptr) { CHECK(Contains(ptr)) << "Free (" << ptr << ") not in bounds of heap " << *this; } const size_t bytes_freed = InternalAllocationSize(ptr); - num_bytes_allocated_ -= bytes_freed; - --num_objects_allocated_; + total_bytes_freed_ += bytes_freed; + ++total_objects_freed_; if (kRecentFreeCount > 0) { RegisterRecentFree(ptr); } @@ -400,8 +399,8 @@ size_t DlMallocSpace::FreeList(Thread* self, size_t num_ptrs, mirror::Object** p { MutexLock mu(self, lock_); - num_bytes_allocated_ -= bytes_freed; - num_objects_allocated_ -= num_ptrs; + total_bytes_freed_ += bytes_freed; + total_objects_freed_ += num_ptrs; mspace_bulk_free(mspace_, reinterpret_cast(ptrs), num_ptrs); return bytes_freed; } @@ -501,6 +500,20 @@ void DlMallocSpace::Dump(std::ostream& os) const { << ",name=\"" << GetName() << "\"]"; } +uint64_t DlMallocSpace::GetBytesAllocated() { + MutexLock mu(Thread::Current(), lock_); + size_t bytes_allocated = 0; + mspace_inspect_all(mspace_, DlmallocBytesAllocatedCallback, &bytes_allocated); + return bytes_allocated; +} + +uint64_t DlMallocSpace::GetObjectsAllocated() { + MutexLock mu(Thread::Current(), lock_); + size_t objects_allocated = 0; + mspace_inspect_all(mspace_, DlmallocObjectsAllocatedCallback, &objects_allocated); + return objects_allocated; +} + } // namespace space } // namespace gc } // namespace art diff --git a/runtime/gc/space/dlmalloc_space.h b/runtime/gc/space/dlmalloc_space.h index b08da0146b0..522535e3c03 100644 --- a/runtime/gc/space/dlmalloc_space.h +++ b/runtime/gc/space/dlmalloc_space.h @@ -127,20 +127,13 @@ class DlMallocSpace : public MemMapSpace, public AllocSpace { // Turn ourself into a zygote space and return a new alloc space which has our unused memory. DlMallocSpace* CreateZygoteSpace(const char* alloc_space_name); - uint64_t GetBytesAllocated() const { - return num_bytes_allocated_; + uint64_t GetBytesAllocated(); + uint64_t GetObjectsAllocated(); + uint64_t GetTotalBytesAllocated() { + return GetBytesAllocated() + total_bytes_freed_; } - - uint64_t GetObjectsAllocated() const { - return num_objects_allocated_; - } - - uint64_t GetTotalBytesAllocated() const { - return total_bytes_allocated_; - } - - uint64_t GetTotalObjectsAllocated() const { - return total_objects_allocated_; + uint64_t GetTotalObjectsAllocated() { + return GetObjectsAllocated() + total_objects_freed_; } // Returns the class of a recently freed object. @@ -168,11 +161,9 @@ class DlMallocSpace : public MemMapSpace, public AllocSpace { std::pair recent_freed_objects_[kRecentFreeCount]; size_t recent_free_pos_; - // Approximate number of bytes which have been allocated into the space. - size_t num_bytes_allocated_; - size_t num_objects_allocated_; - size_t total_bytes_allocated_; - size_t total_objects_allocated_; + // Approximate number of bytes and objects which have been deallocated in the space. + size_t total_bytes_freed_; + size_t total_objects_freed_; static size_t bitmap_index_; diff --git a/runtime/gc/space/large_object_space.h b/runtime/gc/space/large_object_space.h index a703e86ea16..3f2e848c703 100644 --- a/runtime/gc/space/large_object_space.h +++ b/runtime/gc/space/large_object_space.h @@ -41,19 +41,19 @@ class LargeObjectSpace : public DiscontinuousSpace, public AllocSpace { virtual void Walk(DlMallocSpace::WalkCallback, void* arg) = 0; virtual ~LargeObjectSpace() {} - uint64_t GetBytesAllocated() const { + uint64_t GetBytesAllocated() { return num_bytes_allocated_; } - uint64_t GetObjectsAllocated() const { + uint64_t GetObjectsAllocated() { return num_objects_allocated_; } - uint64_t GetTotalBytesAllocated() const { + uint64_t GetTotalBytesAllocated() { return total_bytes_allocated_; } - uint64_t GetTotalObjectsAllocated() const { + uint64_t GetTotalObjectsAllocated() { return total_objects_allocated_; } diff --git a/runtime/gc/space/space.h b/runtime/gc/space/space.h index 68df56333fe..6dd795227d0 100644 --- a/runtime/gc/space/space.h +++ b/runtime/gc/space/space.h @@ -146,13 +146,13 @@ std::ostream& operator<<(std::ostream& os, const Space& space); class AllocSpace { public: // Number of bytes currently allocated. - virtual uint64_t GetBytesAllocated() const = 0; + virtual uint64_t GetBytesAllocated() = 0; // Number of objects currently allocated. - virtual uint64_t GetObjectsAllocated() const = 0; + virtual uint64_t GetObjectsAllocated() = 0; // Number of bytes allocated since the space was created. - virtual uint64_t GetTotalBytesAllocated() const = 0; + virtual uint64_t GetTotalBytesAllocated() = 0; // Number of objects allocated since the space was created. - virtual uint64_t GetTotalObjectsAllocated() const = 0; + virtual uint64_t GetTotalObjectsAllocated() = 0; // Allocate num_bytes without allowing growth. If the allocation // succeeds, the output parameter bytes_allocated will be set to the diff --git a/runtime/native/dalvik_system_VMRuntime.cc b/runtime/native/dalvik_system_VMRuntime.cc index 5fc8bd5a48c..dad6eff3548 100644 --- a/runtime/native/dalvik_system_VMRuntime.cc +++ b/runtime/native/dalvik_system_VMRuntime.cc @@ -169,10 +169,7 @@ static void VMRuntime_trimHeap(JNIEnv*, jobject) { // Trim the managed heap. gc::Heap* heap = Runtime::Current()->GetHeap(); - gc::space::DlMallocSpace* alloc_space = heap->GetAllocSpace(); - size_t alloc_space_size = alloc_space->Size(); - float managed_utilization = - static_cast(alloc_space->GetBytesAllocated()) / alloc_space_size; + float managed_utilization = static_cast(heap->GetBytesAllocated()) / heap->GetTotalMemory(); size_t managed_reclaimed = heap->Trim(); uint64_t gc_heap_end_ns = NanoTime(); From 241b5de2d3cf06868ac31f1153aa0b32ddb07b20 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Wed, 9 Oct 2013 17:58:57 -0700 Subject: [PATCH 0075/2402] Clinits may not have the kAccConstructor flag. Bug: 11157540 Set the clinit access flag when we load the method and warn about badly formed access flags. Change-Id: I515c692095051f84f98510722ab764591185918e --- runtime/class_linker.cc | 75 ++++++++++++++------------ runtime/class_linker_test.cc | 2 +- runtime/entrypoints/entrypoint_utils.h | 2 +- runtime/mirror/class.cc | 1 + runtime/mirror/object_test.cc | 2 +- runtime/object_utils.h | 2 +- 6 files changed, 46 insertions(+), 38 deletions(-) diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index f0a7202408e..28c9a56aba5 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -1866,47 +1866,54 @@ mirror::ArtMethod* ClassLinker::LoadMethod(Thread* self, const DexFile& dex_file const char* old_cause = self->StartAssertNoThreadSuspension("LoadMethod"); dst->SetDexMethodIndex(dex_method_idx); dst->SetDeclaringClass(klass.get()); - - if (method_name == "finalize") { - // Create the prototype for a signature of "()V" - const DexFile::StringId* void_string_id = dex_file.FindStringId("V"); - if (void_string_id != NULL) { - const DexFile::TypeId* void_type_id = - dex_file.FindTypeId(dex_file.GetIndexForStringId(*void_string_id)); - if (void_type_id != NULL) { - std::vector no_args; - const DexFile::ProtoId* finalizer_proto = - dex_file.FindProtoId(dex_file.GetIndexForTypeId(*void_type_id), no_args); - if (finalizer_proto != NULL) { - // We have the prototype in the dex file - if (klass->GetClassLoader() != NULL) { // All non-boot finalizer methods are flagged - klass->SetFinalizable(); - } else { - ClassHelper kh(klass.get()); - StringPiece klass_descriptor(kh.GetDescriptorAsStringPiece()); - // The Enum class declares a "final" finalize() method to prevent subclasses from - // introducing a finalizer. We don't want to set the finalizable flag for Enum or its - // subclasses, so we exclude it here. - // We also want to avoid setting the flag on Object, where we know that finalize() is - // empty. - if (klass_descriptor != "Ljava/lang/Object;" && - klass_descriptor != "Ljava/lang/Enum;") { - klass->SetFinalizable(); - } - } - } - } - } - } dst->SetCodeItemOffset(it.GetMethodCodeItemOffset()); - dst->SetAccessFlags(it.GetMemberAccessFlags()); dst->SetDexCacheStrings(klass->GetDexCache()->GetStrings()); dst->SetDexCacheResolvedMethods(klass->GetDexCache()->GetResolvedMethods()); dst->SetDexCacheResolvedTypes(klass->GetDexCache()->GetResolvedTypes()); dst->SetDexCacheInitializedStaticStorage(klass->GetDexCache()->GetInitializedStaticStorage()); - CHECK(dst->IsArtMethod()); + uint32_t access_flags = it.GetMemberAccessFlags(); + + if (UNLIKELY(method_name == "finalize")) { + // Set finalizable flag on declaring class. + const DexFile::ProtoId& proto = dex_file.GetProtoId(method_id.proto_idx_); + if (dex_file.GetProtoParameters(proto) == NULL) { // No arguments + const DexFile::TypeId& return_type = dex_file.GetTypeId(proto.return_type_idx_); + if (dex_file.StringDataAsStringPieceByIdx(return_type.descriptor_idx_) == "V") { + // Void return type. + if (klass->GetClassLoader() != NULL) { // All non-boot finalizer methods are flagged + klass->SetFinalizable(); + } else { + ClassHelper kh(klass.get()); + StringPiece klass_descriptor(kh.GetDescriptorAsStringPiece()); + // The Enum class declares a "final" finalize() method to prevent subclasses from + // introducing a finalizer. We don't want to set the finalizable flag for Enum or its + // subclasses, so we exclude it here. + // We also want to avoid setting the flag on Object, where we know that finalize() is + // empty. + if (klass_descriptor != "Ljava/lang/Object;" && + klass_descriptor != "Ljava/lang/Enum;") { + klass->SetFinalizable(); + } + } + } + } + } else if (method_name[0] == '<') { + // Fix broken access flags for initializers. Bug 11157540. + bool is_init = (method_name == ""); + bool is_clinit = !is_init && (method_name == ""); + if (UNLIKELY(!is_init && !is_clinit)) { + LOG(WARNING) << "Unexpected '<' at start of method name " << method_name; + } else { + if (UNLIKELY((access_flags & kAccConstructor) == 0)) { + LOG(WARNING) << method_name << " didn't have expected constructor access flag in class " + << PrettyDescriptor(klass.get()) << " in dex file " << dex_file.GetLocation(); + access_flags |= kAccConstructor; + } + } + } + dst->SetAccessFlags(access_flags); self->EndAssertNoThreadSuspension(old_cause); return dst; diff --git a/runtime/class_linker_test.cc b/runtime/class_linker_test.cc index ad9347fee19..029b73e35f1 100644 --- a/runtime/class_linker_test.cc +++ b/runtime/class_linker_test.cc @@ -996,7 +996,7 @@ TEST_F(ClassLinkerTest, ResolveVerifyAndClinit) { CHECK(dex_file != NULL); mirror::Class* klass = class_linker_->FindClass("LStaticsFromCode;", class_loader.get()); - mirror::ArtMethod* clinit = klass->FindDirectMethod("", "()V"); + mirror::ArtMethod* clinit = klass->FindClassInitializer(); mirror::ArtMethod* getS0 = klass->FindDirectMethod("getS0", "()Ljava/lang/Object;"); const DexFile::StringId* string_id = dex_file->FindStringId("LStaticsFromCode;"); ASSERT_TRUE(string_id != NULL); diff --git a/runtime/entrypoints/entrypoint_utils.h b/runtime/entrypoints/entrypoint_utils.h index e9e6c5ad4d1..c32a661effe 100644 --- a/runtime/entrypoints/entrypoint_utils.h +++ b/runtime/entrypoints/entrypoint_utils.h @@ -312,7 +312,7 @@ static inline mirror::Class* ResolveVerifyAndClinit(uint32_t type_idx, // // Do not set the DexCache InitializedStaticStorage, since that implies has finished // running. - if (klass == referring_class && MethodHelper(referrer).IsClassInitializer()) { + if (klass == referring_class && referrer->IsConstructor() && referrer->IsStatic()) { return klass; } if (!class_linker->EnsureInitialized(klass, true, true)) { diff --git a/runtime/mirror/class.cc b/runtime/mirror/class.cc index b16c2f71bb9..2b0b1e1755e 100644 --- a/runtime/mirror/class.cc +++ b/runtime/mirror/class.cc @@ -500,6 +500,7 @@ ArtMethod* Class::FindClassInitializer() const { if (method->IsConstructor() && method->IsStatic()) { if (kIsDebugBuild) { MethodHelper mh(method); + CHECK(mh.IsClassInitializer()); CHECK_STREQ(mh.GetName(), ""); CHECK_STREQ(mh.GetSignature().ToString().c_str(), "()V"); } diff --git a/runtime/mirror/object_test.cc b/runtime/mirror/object_test.cc index 1e610f27841..4c5f90c41da 100644 --- a/runtime/mirror/object_test.cc +++ b/runtime/mirror/object_test.cc @@ -270,7 +270,7 @@ TEST_F(ObjectTest, StaticFieldFromCode) { Class* klass = class_linker_->FindClass("LStaticsFromCode;", soa.Decode(class_loader)); - ArtMethod* clinit = klass->FindDirectMethod("", "()V"); + ArtMethod* clinit = klass->FindClassInitializer(); const DexFile::StringId* klass_string_id = dex_file->FindStringId("LStaticsFromCode;"); ASSERT_TRUE(klass_string_id != NULL); const DexFile::TypeId* klass_type_id = dex_file->FindTypeId( diff --git a/runtime/object_utils.h b/runtime/object_utils.h index 692ceccc6ce..8062a890c26 100644 --- a/runtime/object_utils.h +++ b/runtime/object_utils.h @@ -592,7 +592,7 @@ class MethodHelper { } bool IsClassInitializer() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - return IsStatic() && GetNameAsStringPiece() == ""; + return method_->IsConstructor() && IsStatic(); } size_t NumArgs() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { From 13c479e7e9b62115fe0409e5273c1e976a1c09f9 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Fri, 11 Oct 2013 07:59:01 -0700 Subject: [PATCH 0076/2402] Notify doesn't inflate, rename helper. Change-Id: I85c93b522478b3116a66630856a2370ec0fda490 --- runtime/monitor.cc | 2 +- runtime/monitor.h | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/runtime/monitor.cc b/runtime/monitor.cc index 1ceaa5dd99b..a5605ff3299 100644 --- a/runtime/monitor.cc +++ b/runtime/monitor.cc @@ -695,7 +695,7 @@ void Monitor::Wait(Thread* self, mirror::Object *obj, int64_t ms, int32_t ns, mon->Wait(self, ms, ns, interruptShouldThrow, why); } -void Monitor::InflateAndNotify(Thread* self, mirror::Object* obj, bool notify_all) { +void Monitor::DoNotify(Thread* self, mirror::Object* obj, bool notify_all) { DCHECK(self != NULL); DCHECK(obj != NULL); diff --git a/runtime/monitor.h b/runtime/monitor.h index 044f76e2b67..27124a2eb0b 100644 --- a/runtime/monitor.h +++ b/runtime/monitor.h @@ -62,11 +62,11 @@ class Monitor { static void Notify(Thread* self, mirror::Object* obj) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - InflateAndNotify(self, obj, false); + DoNotify(self, obj, false); } static void NotifyAll(Thread* self, mirror::Object* obj) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - InflateAndNotify(self, obj, true); + DoNotify(self, obj, true); } static void Wait(Thread* self, mirror::Object* obj, int64_t ms, int32_t ns, bool interruptShouldThrow, ThreadState why) @@ -130,7 +130,7 @@ class Monitor { LOCKS_EXCLUDED(monitor_lock_) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - static void InflateAndNotify(Thread* self, mirror::Object* obj, bool notify_all) + static void DoNotify(Thread* self, mirror::Object* obj, bool notify_all) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); void Notify(Thread* self) From 41cdd43bd6968a06b1344efdd57ccf302f997a0e Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Fri, 11 Oct 2013 08:44:13 -0700 Subject: [PATCH 0077/2402] Disable select instruction generation on ARM. Change-Id: I114547d44605b06b2fed396b2fbad03935f66ebc --- compiler/dex/mir_optimization.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/compiler/dex/mir_optimization.cc b/compiler/dex/mir_optimization.cc index 05e428e1788..0b4f04100b6 100644 --- a/compiler/dex/mir_optimization.cc +++ b/compiler/dex/mir_optimization.cc @@ -325,7 +325,8 @@ bool MIRGraph::BasicBlockOpt(BasicBlock* bb) { // Is this the select pattern? // TODO: flesh out support for Mips and X86. NOTE: llvm's select op doesn't quite work here. // TUNING: expand to support IF_xx compare & branches - if (!(cu_->compiler_backend == kPortable) && (cu_->instruction_set == kThumb2) && + if (false && + !(cu_->compiler_backend == kPortable) && (cu_->instruction_set == kThumb2) && ((mir->dalvikInsn.opcode == Instruction::IF_EQZ) || (mir->dalvikInsn.opcode == Instruction::IF_NEZ))) { BasicBlock* ft = bb->fall_through; From 409fe94ad529d9334587be80b9f6a3d166805508 Mon Sep 17 00:00:00 2001 From: buzbee Date: Fri, 11 Oct 2013 10:49:56 -0700 Subject: [PATCH 0078/2402] Quick assembler fix This CL re-instates the select pattern optimization disabled by CL 374310, and fixes the underlying problem: improper handling of the kPseudoBarrier LIR opcode. The bug was introduced in the recent assembler restructuring. In short, LIR pseudo opcodes (which have values < 0), should always have size 0 - and thus cause no bits to be emitted during assembly. In this case, bad logic caused us to set the size of a kPseudoBarrier opcode via lookup through the EncodingMap. Because all pseudo ops are < 0, this meant we did an array underflow load, picking up whatever garbage was located before the EncodingMap. This explains why this error showed up recently - we'd previuosly just gotten a lucky layout. This CL corrects the faulty logic, and adds DCHECKs to uses of the EncodingMap to ensure that we don't try to access w/ a pseudo op. Additionally, the existing is_pseudo_op() macro is replaced with IsPseudoLirOp(), named similar to the existing IsPseudoMirOp(). Change-Id: I46761a0275a923d85b545664cadf052e1ab120dc --- compiler/dex/mir_optimization.cc | 3 +-- compiler/dex/quick/arm/assemble_arm.cc | 5 +++-- compiler/dex/quick/arm/target_arm.cc | 3 +++ compiler/dex/quick/arm/utility_arm.cc | 4 ++-- compiler/dex/quick/local_optimizations.cc | 12 ++++++------ compiler/dex/quick/mips/assemble_mips.cc | 3 +++ compiler/dex/quick/mips/target_mips.cc | 3 +++ compiler/dex/quick/mir_to_lir-inl.h | 18 ++++++++++-------- compiler/dex/quick/mir_to_lir.h | 5 ++++- compiler/dex/quick/x86/assemble_x86.cc | 5 +++-- compiler/dex/quick/x86/target_x86.cc | 3 +++ 11 files changed, 41 insertions(+), 23 deletions(-) diff --git a/compiler/dex/mir_optimization.cc b/compiler/dex/mir_optimization.cc index 0b4f04100b6..05e428e1788 100644 --- a/compiler/dex/mir_optimization.cc +++ b/compiler/dex/mir_optimization.cc @@ -325,8 +325,7 @@ bool MIRGraph::BasicBlockOpt(BasicBlock* bb) { // Is this the select pattern? // TODO: flesh out support for Mips and X86. NOTE: llvm's select op doesn't quite work here. // TUNING: expand to support IF_xx compare & branches - if (false && - !(cu_->compiler_backend == kPortable) && (cu_->instruction_set == kThumb2) && + if (!(cu_->compiler_backend == kPortable) && (cu_->instruction_set == kThumb2) && ((mir->dalvikInsn.opcode == Instruction::IF_EQZ) || (mir->dalvikInsn.opcode == Instruction::IF_NEZ))) { BasicBlock* ft = bb->fall_through; diff --git a/compiler/dex/quick/arm/assemble_arm.cc b/compiler/dex/quick/arm/assemble_arm.cc index dac3a2159c1..3c646c40b6b 100644 --- a/compiler/dex/quick/arm/assemble_arm.cc +++ b/compiler/dex/quick/arm/assemble_arm.cc @@ -1021,7 +1021,7 @@ void ArmMir2Lir::InsertFixupBefore(LIR* prev_lir, LIR* orig_lir, LIR* new_lir) { void ArmMir2Lir::EncodeLIR(LIR* lir) { int opcode = lir->opcode; - if (opcode < 0) { + if (IsPseudoLirOp(opcode)) { if (UNLIKELY(opcode == kPseudoPseudoAlign4)) { // Note: size for this opcode will be either 0 or 2 depending on final alignment. lir->u.a.bytes[0] = (PADDING_MOV_R5_R5 & 0xff); @@ -1594,6 +1594,7 @@ void ArmMir2Lir::AssembleLIR() { } int ArmMir2Lir::GetInsnSize(LIR* lir) { + DCHECK(!IsPseudoLirOp(lir->opcode)); return EncodingMap[lir->opcode].size; } @@ -1613,7 +1614,7 @@ uint32_t ArmMir2Lir::EncodeRange(LIR* head_lir, LIR* tail_lir, uint32_t offset) lir->offset = offset; if (!lir->flags.is_nop) { if (lir->flags.fixup != kFixupNone) { - if (lir->opcode >= 0) { + if (!IsPseudoLirOp(lir->opcode)) { lir->flags.size = EncodingMap[lir->opcode].size; lir->flags.fixup = EncodingMap[lir->opcode].fixup; } else if (UNLIKELY(lir->opcode == kPseudoPseudoAlign4)) { diff --git a/compiler/dex/quick/arm/target_arm.cc b/compiler/dex/quick/arm/target_arm.cc index a4ea10b799a..933c1a3b917 100644 --- a/compiler/dex/quick/arm/target_arm.cc +++ b/compiler/dex/quick/arm/target_arm.cc @@ -718,14 +718,17 @@ int ArmMir2Lir::LoadHelper(ThreadOffset offset) { } uint64_t ArmMir2Lir::GetTargetInstFlags(int opcode) { + DCHECK(!IsPseudoLirOp(opcode)); return ArmMir2Lir::EncodingMap[opcode].flags; } const char* ArmMir2Lir::GetTargetInstName(int opcode) { + DCHECK(!IsPseudoLirOp(opcode)); return ArmMir2Lir::EncodingMap[opcode].name; } const char* ArmMir2Lir::GetTargetInstFmt(int opcode) { + DCHECK(!IsPseudoLirOp(opcode)); return ArmMir2Lir::EncodingMap[opcode].fmt; } diff --git a/compiler/dex/quick/arm/utility_arm.cc b/compiler/dex/quick/arm/utility_arm.cc index a7b8dfecf01..00de8de70a7 100644 --- a/compiler/dex/quick/arm/utility_arm.cc +++ b/compiler/dex/quick/arm/utility_arm.cc @@ -327,7 +327,7 @@ LIR* ArmMir2Lir::OpRegRegShift(OpKind op, int r_dest_src1, int r_src2, LOG(FATAL) << "Bad opcode: " << op; break; } - DCHECK_GE(static_cast(opcode), 0); + DCHECK(!IsPseudoLirOp(opcode)); if (EncodingMap[opcode].flags & IS_BINARY_OP) { return NewLIR2(opcode, r_dest_src1, r_src2); } else if (EncodingMap[opcode].flags & IS_TERTIARY_OP) { @@ -405,7 +405,7 @@ LIR* ArmMir2Lir::OpRegRegRegShift(OpKind op, int r_dest, int r_src1, LOG(FATAL) << "Bad opcode: " << op; break; } - DCHECK_GE(static_cast(opcode), 0); + DCHECK(!IsPseudoLirOp(opcode)); if (EncodingMap[opcode].flags & IS_QUAD_OP) { return NewLIR4(opcode, r_dest, r_src1, r_src2, shift); } else { diff --git a/compiler/dex/quick/local_optimizations.cc b/compiler/dex/quick/local_optimizations.cc index f915779e757..0f29578c4ec 100644 --- a/compiler/dex/quick/local_optimizations.cc +++ b/compiler/dex/quick/local_optimizations.cc @@ -78,7 +78,7 @@ void Mir2Lir::ApplyLoadStoreElimination(LIR* head_lir, LIR* tail_lir) { } for (this_lir = PREV_LIR(tail_lir); this_lir != head_lir; this_lir = PREV_LIR(this_lir)) { - if (is_pseudo_opcode(this_lir->opcode)) { + if (IsPseudoLirOp(this_lir->opcode)) { continue; } @@ -135,7 +135,7 @@ void Mir2Lir::ApplyLoadStoreElimination(LIR* head_lir, LIR* tail_lir) { * Skip already dead instructions (whose dataflow information is * outdated and misleading). */ - if (check_lir->flags.is_nop || is_pseudo_opcode(check_lir->opcode)) { + if (check_lir->flags.is_nop || IsPseudoLirOp(check_lir->opcode)) { continue; } @@ -285,7 +285,7 @@ void Mir2Lir::ApplyLoadHoisting(LIR* head_lir, LIR* tail_lir) { /* Start from the second instruction */ for (this_lir = NEXT_LIR(head_lir); this_lir != tail_lir; this_lir = NEXT_LIR(this_lir)) { - if (is_pseudo_opcode(this_lir->opcode)) { + if (IsPseudoLirOp(this_lir->opcode)) { continue; } @@ -362,7 +362,7 @@ void Mir2Lir::ApplyLoadHoisting(LIR* head_lir, LIR* tail_lir) { * Store the dependent or non-pseudo/indepedent instruction to the * list. */ - if (stop_here || !is_pseudo_opcode(check_lir->opcode)) { + if (stop_here || !IsPseudoLirOp(check_lir->opcode)) { prev_inst_list[next_slot++] = check_lir; if (next_slot == MAX_HOIST_DISTANCE) { break; @@ -393,7 +393,7 @@ void Mir2Lir::ApplyLoadHoisting(LIR* head_lir, LIR* tail_lir) { int slot; LIR* dep_lir = prev_inst_list[next_slot-1]; /* If there is ld-ld dependency, wait LDLD_DISTANCE cycles */ - if (!is_pseudo_opcode(dep_lir->opcode) && + if (!IsPseudoLirOp(dep_lir->opcode) && (GetTargetInstFlags(dep_lir->opcode) & IS_LOAD)) { first_slot -= LDLD_DISTANCE; } @@ -434,7 +434,7 @@ void Mir2Lir::ApplyLoadHoisting(LIR* head_lir, LIR* tail_lir) { * Try to find two instructions with load/use dependency until * the remaining instructions are less than LD_LATENCY. */ - bool prev_is_load = is_pseudo_opcode(prev_lir->opcode) ? false : + bool prev_is_load = IsPseudoLirOp(prev_lir->opcode) ? false : (GetTargetInstFlags(prev_lir->opcode) & IS_LOAD); if (((cur_lir->u.m.use_mask & prev_lir->u.m.def_mask) && prev_is_load) || (slot < LD_LATENCY)) { break; diff --git a/compiler/dex/quick/mips/assemble_mips.cc b/compiler/dex/quick/mips/assemble_mips.cc index 3a6207cfb59..6bfccfd575f 100644 --- a/compiler/dex/quick/mips/assemble_mips.cc +++ b/compiler/dex/quick/mips/assemble_mips.cc @@ -646,6 +646,7 @@ AssemblerStatus MipsMir2Lir::AssembleInstructions(uintptr_t start_addr) { if (res != kSuccess) { continue; } + DCHECK(!IsPseudoLirOp(lir->opcode)); const MipsEncodingMap *encoder = &EncodingMap[lir->opcode]; uint32_t bits = encoder->skeleton; int i; @@ -695,6 +696,7 @@ AssemblerStatus MipsMir2Lir::AssembleInstructions(uintptr_t start_addr) { code_buffer_.push_back((bits >> 24) & 0xff); // TUNING: replace with proper delay slot handling if (encoder->size == 8) { + DCHECK(!IsPseudoLirOp(lir->opcode)); const MipsEncodingMap *encoder = &EncodingMap[kMipsNop]; uint32_t bits = encoder->skeleton; code_buffer_.push_back(bits & 0xff); @@ -707,6 +709,7 @@ AssemblerStatus MipsMir2Lir::AssembleInstructions(uintptr_t start_addr) { } int MipsMir2Lir::GetInsnSize(LIR* lir) { + DCHECK(!IsPseudoLirOp(lir->opcode)); return EncodingMap[lir->opcode].size; } diff --git a/compiler/dex/quick/mips/target_mips.cc b/compiler/dex/quick/mips/target_mips.cc index f9d432ec753..0ee32d4d21c 100644 --- a/compiler/dex/quick/mips/target_mips.cc +++ b/compiler/dex/quick/mips/target_mips.cc @@ -553,14 +553,17 @@ Mir2Lir* MipsCodeGenerator(CompilationUnit* const cu, MIRGraph* const mir_graph, } uint64_t MipsMir2Lir::GetTargetInstFlags(int opcode) { + DCHECK(!IsPseudoLirOp(opcode)); return MipsMir2Lir::EncodingMap[opcode].flags; } const char* MipsMir2Lir::GetTargetInstName(int opcode) { + DCHECK(!IsPseudoLirOp(opcode)); return MipsMir2Lir::EncodingMap[opcode].name; } const char* MipsMir2Lir::GetTargetInstFmt(int opcode) { + DCHECK(!IsPseudoLirOp(opcode)); return MipsMir2Lir::EncodingMap[opcode].fmt; } diff --git a/compiler/dex/quick/mir_to_lir-inl.h b/compiler/dex/quick/mir_to_lir-inl.h index 314c57e02c0..f293700bb99 100644 --- a/compiler/dex/quick/mir_to_lir-inl.h +++ b/compiler/dex/quick/mir_to_lir-inl.h @@ -69,7 +69,7 @@ inline LIR* Mir2Lir::RawLIR(int dalvik_offset, int opcode, int op0, * operands. */ inline LIR* Mir2Lir::NewLIR0(int opcode) { - DCHECK(is_pseudo_opcode(opcode) || (GetTargetInstFlags(opcode) & NO_OPERAND)) + DCHECK(IsPseudoLirOp(opcode) || (GetTargetInstFlags(opcode) & NO_OPERAND)) << GetTargetInstName(opcode) << " " << opcode << " " << PrettyMethod(cu_->method_idx, *cu_->dex_file) << " " << current_dalvik_offset_; @@ -79,7 +79,7 @@ inline LIR* Mir2Lir::NewLIR0(int opcode) { } inline LIR* Mir2Lir::NewLIR1(int opcode, int dest) { - DCHECK(is_pseudo_opcode(opcode) || (GetTargetInstFlags(opcode) & IS_UNARY_OP)) + DCHECK(IsPseudoLirOp(opcode) || (GetTargetInstFlags(opcode) & IS_UNARY_OP)) << GetTargetInstName(opcode) << " " << opcode << " " << PrettyMethod(cu_->method_idx, *cu_->dex_file) << " " << current_dalvik_offset_; @@ -89,7 +89,7 @@ inline LIR* Mir2Lir::NewLIR1(int opcode, int dest) { } inline LIR* Mir2Lir::NewLIR2(int opcode, int dest, int src1) { - DCHECK(is_pseudo_opcode(opcode) || (GetTargetInstFlags(opcode) & IS_BINARY_OP)) + DCHECK(IsPseudoLirOp(opcode) || (GetTargetInstFlags(opcode) & IS_BINARY_OP)) << GetTargetInstName(opcode) << " " << opcode << " " << PrettyMethod(cu_->method_idx, *cu_->dex_file) << " " << current_dalvik_offset_; @@ -99,7 +99,7 @@ inline LIR* Mir2Lir::NewLIR2(int opcode, int dest, int src1) { } inline LIR* Mir2Lir::NewLIR3(int opcode, int dest, int src1, int src2) { - DCHECK(is_pseudo_opcode(opcode) || (GetTargetInstFlags(opcode) & IS_TERTIARY_OP)) + DCHECK(IsPseudoLirOp(opcode) || (GetTargetInstFlags(opcode) & IS_TERTIARY_OP)) << GetTargetInstName(opcode) << " " << opcode << " " << PrettyMethod(cu_->method_idx, *cu_->dex_file) << " " << current_dalvik_offset_; @@ -109,7 +109,7 @@ inline LIR* Mir2Lir::NewLIR3(int opcode, int dest, int src1, int src2) { } inline LIR* Mir2Lir::NewLIR4(int opcode, int dest, int src1, int src2, int info) { - DCHECK(is_pseudo_opcode(opcode) || (GetTargetInstFlags(opcode) & IS_QUAD_OP)) + DCHECK(IsPseudoLirOp(opcode) || (GetTargetInstFlags(opcode) & IS_QUAD_OP)) << GetTargetInstName(opcode) << " " << opcode << " " << PrettyMethod(cu_->method_idx, *cu_->dex_file) << " " << current_dalvik_offset_; @@ -120,7 +120,7 @@ inline LIR* Mir2Lir::NewLIR4(int opcode, int dest, int src1, int src2, int info) inline LIR* Mir2Lir::NewLIR5(int opcode, int dest, int src1, int src2, int info1, int info2) { - DCHECK(is_pseudo_opcode(opcode) || (GetTargetInstFlags(opcode) & IS_QUIN_OP)) + DCHECK(IsPseudoLirOp(opcode) || (GetTargetInstFlags(opcode) & IS_QUIN_OP)) << GetTargetInstName(opcode) << " " << opcode << " " << PrettyMethod(cu_->method_idx, *cu_->dex_file) << " " << current_dalvik_offset_; @@ -142,8 +142,10 @@ inline void Mir2Lir::SetupRegMask(uint64_t* mask, int reg) { inline void Mir2Lir::SetupResourceMasks(LIR* lir) { int opcode = lir->opcode; - if ((opcode < 0) && (opcode != kPseudoBarrier)) { - lir->flags.fixup = kFixupLabel; + if (IsPseudoLirOp(opcode)) { + if (opcode != kPseudoBarrier) { + lir->flags.fixup = kFixupLabel; + } return; } diff --git a/compiler/dex/quick/mir_to_lir.h b/compiler/dex/quick/mir_to_lir.h index 21711e567f1..5df2672fe2b 100644 --- a/compiler/dex/quick/mir_to_lir.h +++ b/compiler/dex/quick/mir_to_lir.h @@ -181,7 +181,6 @@ Mir2Lir* X86CodeGenerator(CompilationUnit* const cu, MIRGraph* const mir_graph, #define SLOW_STRING_PATH (cu_->enable_debug & (1 << kDebugSlowStringPath)) #define SLOW_TYPE_PATH (cu_->enable_debug & (1 << kDebugSlowTypePath)) #define EXERCISE_SLOWEST_STRING_PATH (cu_->enable_debug & (1 << kDebugSlowestStringPath)) -#define is_pseudo_opcode(opcode) (static_cast(opcode) < 0) class Mir2Lir : public Backend { public: @@ -257,6 +256,10 @@ class Mir2Lir : public Backend { return code_buffer_.size() / sizeof(code_buffer_[0]); } + bool IsPseudoLirOp(int opcode) { + return (opcode < 0); + } + // Shared by all targets - implemented in codegen_util.cc void AppendLIR(LIR* lir); void InsertLIRBefore(LIR* current_lir, LIR* new_lir); diff --git a/compiler/dex/quick/x86/assemble_x86.cc b/compiler/dex/quick/x86/assemble_x86.cc index b1634da4d9c..064ff31bcd7 100644 --- a/compiler/dex/quick/x86/assemble_x86.cc +++ b/compiler/dex/quick/x86/assemble_x86.cc @@ -362,6 +362,7 @@ static size_t ComputeSize(const X86EncodingMap* entry, int base, int displacemen } int X86Mir2Lir::GetInsnSize(LIR* lir) { + DCHECK(!IsPseudoLirOp(lir->opcode)); const X86EncodingMap* entry = &X86Mir2Lir::EncodingMap[lir->opcode]; switch (entry->kind) { case kData: @@ -1166,7 +1167,7 @@ AssemblerStatus X86Mir2Lir::AssembleInstructions(uintptr_t start_addr) { const bool kVerbosePcFixup = false; for (lir = first_lir_insn_; lir != NULL; lir = NEXT_LIR(lir)) { - if (lir->opcode < 0) { + if (IsPseudoLirOp(lir->opcode)) { continue; } @@ -1393,7 +1394,7 @@ int X86Mir2Lir::AssignInsnOffsets() { for (lir = first_lir_insn_; lir != NULL; lir = NEXT_LIR(lir)) { lir->offset = offset; - if (LIKELY(lir->opcode >= 0)) { + if (LIKELY(!IsPseudoLirOp(lir->opcode))) { if (!lir->flags.is_nop) { offset += lir->flags.size; } diff --git a/compiler/dex/quick/x86/target_x86.cc b/compiler/dex/quick/x86/target_x86.cc index f0808301a3a..0f005dab26f 100644 --- a/compiler/dex/quick/x86/target_x86.cc +++ b/compiler/dex/quick/x86/target_x86.cc @@ -524,14 +524,17 @@ int X86Mir2Lir::LoadHelper(ThreadOffset offset) { } uint64_t X86Mir2Lir::GetTargetInstFlags(int opcode) { + DCHECK(!IsPseudoLirOp(opcode)); return X86Mir2Lir::EncodingMap[opcode].flags; } const char* X86Mir2Lir::GetTargetInstName(int opcode) { + DCHECK(!IsPseudoLirOp(opcode)); return X86Mir2Lir::EncodingMap[opcode].name; } const char* X86Mir2Lir::GetTargetInstFmt(int opcode) { + DCHECK(!IsPseudoLirOp(opcode)); return X86Mir2Lir::EncodingMap[opcode].fmt; } From 773aab1e8992b2834153eb23c976a4eb0da51a71 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Mon, 14 Oct 2013 13:50:10 -0700 Subject: [PATCH 0079/2402] Correct free-ing of temp register. Bug 11199874. The card mark was potentially using a register freed just before. Make the free-ing of temps strongly correspond to their allocation. Change-Id: I3d1e8c923b7fd8b3666e841d3ff9a46e6eb58318 --- compiler/dex/quick/arm/int_arm.cc | 4 +++- compiler/dex/quick/mips/int_mips.cc | 7 +++++-- compiler/dex/quick/x86/int_x86.cc | 3 ++- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/compiler/dex/quick/arm/int_arm.cc b/compiler/dex/quick/arm/int_arm.cc index b1772fd81f8..75d3738c6ad 100644 --- a/compiler/dex/quick/arm/int_arm.cc +++ b/compiler/dex/quick/arm/int_arm.cc @@ -884,12 +884,14 @@ void ArmMir2Lir::GenArrayPut(int opt_flags, OpSize size, RegLocation rl_array, } int reg_ptr; + bool allocated_reg_ptr_temp = false; if (constant_index) { reg_ptr = rl_array.low_reg; } else if (IsTemp(rl_array.low_reg)) { Clobber(rl_array.low_reg); reg_ptr = rl_array.low_reg; } else { + allocated_reg_ptr_temp = true; reg_ptr = AllocTemp(); } @@ -940,7 +942,7 @@ void ArmMir2Lir::GenArrayPut(int opt_flags, OpSize size, RegLocation rl_array, StoreBaseIndexed(reg_ptr, rl_index.low_reg, rl_src.low_reg, scale, size); } - if (!constant_index) { + if (allocated_reg_ptr_temp) { FreeTemp(reg_ptr); } if (card_mark) { diff --git a/compiler/dex/quick/mips/int_mips.cc b/compiler/dex/quick/mips/int_mips.cc index 218ed489b46..7fefd33591b 100644 --- a/compiler/dex/quick/mips/int_mips.cc +++ b/compiler/dex/quick/mips/int_mips.cc @@ -498,12 +498,14 @@ void MipsMir2Lir::GenArrayPut(int opt_flags, OpSize size, RegLocation rl_array, rl_array = LoadValue(rl_array, kCoreReg); rl_index = LoadValue(rl_index, kCoreReg); int reg_ptr = INVALID_REG; + bool allocated_reg_ptr_temp = false; if (IsTemp(rl_array.low_reg)) { Clobber(rl_array.low_reg); reg_ptr = rl_array.low_reg; } else { reg_ptr = AllocTemp(); OpRegCopy(reg_ptr, rl_array.low_reg); + allocated_reg_ptr_temp = true; } /* null object? */ @@ -538,8 +540,6 @@ void MipsMir2Lir::GenArrayPut(int opt_flags, OpSize size, RegLocation rl_array, } StoreBaseDispWide(reg_ptr, 0, rl_src.low_reg, rl_src.high_reg); - - FreeTemp(reg_ptr); } else { rl_src = LoadValue(rl_src, reg_class); if (needs_range_check) { @@ -549,6 +549,9 @@ void MipsMir2Lir::GenArrayPut(int opt_flags, OpSize size, RegLocation rl_array, StoreBaseIndexed(reg_ptr, rl_index.low_reg, rl_src.low_reg, scale, size); } + if (allocated_reg_ptr_temp) { + FreeTemp(reg_ptr); + } if (card_mark) { MarkGCCard(rl_src.low_reg, rl_array.low_reg); } diff --git a/compiler/dex/quick/x86/int_x86.cc b/compiler/dex/quick/x86/int_x86.cc index 14f5348c125..a9f2c59a22a 100644 --- a/compiler/dex/quick/x86/int_x86.cc +++ b/compiler/dex/quick/x86/int_x86.cc @@ -503,7 +503,8 @@ void X86Mir2Lir::GenArrayPut(int opt_flags, OpSize size, RegLocation rl_array, rl_src.high_reg, size, INVALID_SREG); } if (card_mark) { - FreeTemp(rl_index.low_reg); // Ensure there are 2 free regs for card mark. + // Free rl_index if its a temp. Ensures there are 2 free regs for card mark. + FreeTemp(rl_index.low_reg); MarkGCCard(rl_src.low_reg, rl_array.low_reg); } } From 379067c3970fb225332cca25301743f5010d3ef9 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Tue, 15 Oct 2013 15:06:58 -0700 Subject: [PATCH 0080/2402] Don't clobber array reg if its needed for card marking Change-Id: I4377717a2431ffd7e8fafc2e2cca7c1285b38668 --- compiler/dex/quick/arm/int_arm.cc | 2 +- compiler/dex/quick/mips/int_mips.cc | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/dex/quick/arm/int_arm.cc b/compiler/dex/quick/arm/int_arm.cc index 75d3738c6ad..a89d1611546 100644 --- a/compiler/dex/quick/arm/int_arm.cc +++ b/compiler/dex/quick/arm/int_arm.cc @@ -887,7 +887,7 @@ void ArmMir2Lir::GenArrayPut(int opt_flags, OpSize size, RegLocation rl_array, bool allocated_reg_ptr_temp = false; if (constant_index) { reg_ptr = rl_array.low_reg; - } else if (IsTemp(rl_array.low_reg)) { + } else if (IsTemp(rl_array.low_reg) && !card_mark) { Clobber(rl_array.low_reg); reg_ptr = rl_array.low_reg; } else { diff --git a/compiler/dex/quick/mips/int_mips.cc b/compiler/dex/quick/mips/int_mips.cc index 7fefd33591b..02ab04ef829 100644 --- a/compiler/dex/quick/mips/int_mips.cc +++ b/compiler/dex/quick/mips/int_mips.cc @@ -499,7 +499,7 @@ void MipsMir2Lir::GenArrayPut(int opt_flags, OpSize size, RegLocation rl_array, rl_index = LoadValue(rl_index, kCoreReg); int reg_ptr = INVALID_REG; bool allocated_reg_ptr_temp = false; - if (IsTemp(rl_array.low_reg)) { + if (IsTemp(rl_array.low_reg) && !card_mark) { Clobber(rl_array.low_reg); reg_ptr = rl_array.low_reg; } else { From 2a6c7b7c21adcd4493542604305585b852ccf554 Mon Sep 17 00:00:00 2001 From: Mathieu Chartier Date: Wed, 16 Oct 2013 11:16:33 -0700 Subject: [PATCH 0081/2402] Restore callee save registers. The callee saves weren't all being restored as an optimiation, but this breaks compaction if register promotion is enabled. The reason for this is that these registers may contain references which the GC will update. Change-Id: I810f56b4ed1f92c632155e30c0838269cb95f3c5 --- runtime/arch/arm/quick_entrypoints_arm.S | 16 +++++----- runtime/arch/mips/quick_entrypoints_mips.S | 35 +++++++++++++++++----- runtime/arch/x86/quick_entrypoints_x86.S | 5 +++- 3 files changed, 39 insertions(+), 17 deletions(-) diff --git a/runtime/arch/arm/quick_entrypoints_arm.S b/runtime/arch/arm/quick_entrypoints_arm.S index d0731770a89..c98b7644240 100644 --- a/runtime/arch/arm/quick_entrypoints_arm.S +++ b/runtime/arch/arm/quick_entrypoints_arm.S @@ -67,16 +67,16 @@ .endm .macro RESTORE_REF_ONLY_CALLEE_SAVE_FRAME - ldr lr, [sp, #28] @ restore lr for return - add sp, #32 @ unwind stack + add sp, #4 @ bottom word holds Method* + pop {r5-r8, r10-r11, lr} @ 7 words of callee saves .cfi_adjust_cfa_offset -32 .endm .macro RESTORE_REF_ONLY_CALLEE_SAVE_FRAME_AND_RETURN - ldr lr, [sp, #28] @ restore lr for return - add sp, #32 @ unwind stack + add sp, #4 @ bottom word holds Method* + pop {r5-r8, r10-r11, lr} @ 7 words of callee saves .cfi_adjust_cfa_offset -32 - bx lr @ return + bx lr @ return .endm /* @@ -103,10 +103,8 @@ .endm .macro RESTORE_REF_AND_ARGS_CALLEE_SAVE_FRAME - ldr r1, [sp, #8] @ restore non-callee save r1 - ldrd r2, [sp, #12] @ restore non-callee saves r2-r3 - ldr lr, [sp, #44] @ restore lr - add sp, #48 @ rewind sp + add sp, #8 @ rewind sp + pop {r1-r3, r5-r8, r10-r11, lr} @ 10 words of callee saves .cfi_adjust_cfa_offset -48 .endm diff --git a/runtime/arch/mips/quick_entrypoints_mips.S b/runtime/arch/mips/quick_entrypoints_mips.S index 7780bb3a25e..897aaf40361 100644 --- a/runtime/arch/mips/quick_entrypoints_mips.S +++ b/runtime/arch/mips/quick_entrypoints_mips.S @@ -88,15 +88,29 @@ .endm .macro RESTORE_REF_ONLY_CALLEE_SAVE_FRAME - lw $gp, 52($sp) lw $ra, 60($sp) + lw $s8, 56($sp) + lw $gp, 52($sp) + lw $s7, 48($sp) + lw $s6, 44($sp) + lw $s5, 40($sp) + lw $s4, 36($sp) + lw $s3, 32($sp) + lw $s2, 28($sp) addiu $sp, $sp, 64 .cfi_adjust_cfa_offset -64 .endm .macro RESTORE_REF_ONLY_CALLEE_SAVE_FRAME_AND_RETURN - lw $gp, 52($sp) lw $ra, 60($sp) + lw $s8, 56($sp) + lw $gp, 52($sp) + lw $s7, 48($sp) + lw $s6, 44($sp) + lw $s5, 40($sp) + lw $s4, 36($sp) + lw $s3, 32($sp) + lw $s2, 28($sp) jr $ra addiu $sp, $sp, 64 .cfi_adjust_cfa_offset -64 @@ -138,11 +152,18 @@ .endm .macro RESTORE_REF_AND_ARGS_CALLEE_SAVE_FRAME - lw $ra, 60($sp) # restore $ra - lw $gp, 52($sp) # restore $gp - lw $a1, 4($sp) # restore non-callee save $a1 - lw $a2, 8($sp) # restore non-callee save $a2 - lw $a3, 12($sp) # restore non-callee save $a3 + lw $ra, 60($sp) + lw $s8, 56($sp) + lw $gp, 52($sp) + lw $s7, 48($sp) + lw $s6, 44($sp) + lw $s5, 40($sp) + lw $s4, 36($sp) + lw $s3, 32($sp) + lw $s2, 28($sp) + lw $a3, 12($sp) + lw $a2, 8($sp) + lw $a1, 4($sp) addiu $sp, $sp, 64 # pop frame .cfi_adjust_cfa_offset -64 .endm diff --git a/runtime/arch/x86/quick_entrypoints_x86.S b/runtime/arch/x86/quick_entrypoints_x86.S index 9fce72f7802..d7e1be81700 100644 --- a/runtime/arch/x86/quick_entrypoints_x86.S +++ b/runtime/arch/x86/quick_entrypoints_x86.S @@ -41,7 +41,10 @@ MACRO0(SETUP_REF_ONLY_CALLEE_SAVE_FRAME) END_MACRO MACRO0(RESTORE_REF_ONLY_CALLEE_SAVE_FRAME) - addl MACRO_LITERAL(28), %esp // Unwind stack up to return address + addl MACRO_LITERAL(16), %esp // Unwind stack up to return address + POP ebp // Restore callee saves (ebx is saved/restored by the upcall) + POP esi + POP edi .cfi_adjust_cfa_offset -28 END_MACRO From 8770e5c77868dd3c6c832ba9ea68140f70e73088 Mon Sep 17 00:00:00 2001 From: Mathieu Chartier Date: Wed, 16 Oct 2013 14:49:01 -0700 Subject: [PATCH 0082/2402] Restore callee save registers in JNI compiler. The GC may update these if objects move. Change-Id: Idb7df85c614081abf02f41ba9ee033b0fb7ba2b2 --- compiler/jni/quick/jni_compiler.cc | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/compiler/jni/quick/jni_compiler.cc b/compiler/jni/quick/jni_compiler.cc index b6b15f94ebd..1c9aed83c32 100644 --- a/compiler/jni/quick/jni_compiler.cc +++ b/compiler/jni/quick/jni_compiler.cc @@ -81,7 +81,6 @@ CompiledMethod* ArtJniCompileMethodInternal(CompilerDriver& compiler, UniquePtr end_jni_conv( JniCallingConvention::Create(is_static, is_synchronized, jni_end_shorty, instruction_set)); - // Assembler that holds generated instructions UniquePtr jni_asm(Assembler::Create(instruction_set)); @@ -354,9 +353,9 @@ CompiledMethod* ArtJniCompileMethodInternal(CompilerDriver& compiler, // 15. Process pending exceptions from JNI call or monitor exit. __ ExceptionPoll(main_jni_conv->InterproceduralScratchRegister(), 0); - // 16. Remove activation - no need to restore callee save registers because we didn't clobber + // 16. Remove activation - need to restore callee save registers since the GC may have changed // them. - __ RemoveFrame(frame_size, std::vector()); + __ RemoveFrame(frame_size, callee_save_regs); // 17. Finalize code generation __ EmitSlowPaths(); From 19a083603128c2cded84f0e5ad59db2044b1d6b6 Mon Sep 17 00:00:00 2001 From: Brian Carlstrom Date: Wed, 16 Oct 2013 14:37:22 -0700 Subject: [PATCH 0083/2402] Fix dumpsys meminfo for art Bug: 10112253 (cherry picked from commit fb2f70c7678d33c2027cdd0285d8b5421876e6aa) Change-Id: I149c30242f3c9ef1f67b95e26731c2505e840171 --- runtime/utils.cc | 2 +- runtime/utils_test.cc | 14 ++++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/runtime/utils.cc b/runtime/utils.cc index ac5cae2fce1..01441a2f27b 100644 --- a/runtime/utils.cc +++ b/runtime/utils.cc @@ -1196,7 +1196,7 @@ std::string GetDalvikCacheFilenameOrDie(const std::string& location) { LOG(FATAL) << "Expected path in location to be absolute: "<< location; } std::string cache_file(location, 1); // skip leading slash - if (!EndsWith(location, ".dex") || !EndsWith(location, ".art")) { + if (!EndsWith(location, ".dex") && !EndsWith(location, ".art")) { cache_file += "/"; cache_file += DexFile::kClassesDex; } diff --git a/runtime/utils_test.cc b/runtime/utils_test.cc index 2633964b571..b43177b4fd7 100644 --- a/runtime/utils_test.cc +++ b/runtime/utils_test.cc @@ -335,4 +335,18 @@ TEST_F(UtilsTest, EndsWith) { EXPECT_FALSE(EndsWith("oo", "foo")); } +void CheckGetDalvikCacheFilenameOrDie(const char* in, const char* out) { + std::string expected(getenv("ANDROID_DATA")); + expected += "/dalvik-cache/"; + expected += out; + EXPECT_STREQ(expected.c_str(), GetDalvikCacheFilenameOrDie(in).c_str()); +} + +TEST_F(UtilsTest, GetDalvikCacheFilenameOrDie) { + CheckGetDalvikCacheFilenameOrDie("/system/app/Foo.apk", "system@app@Foo.apk@classes.dex"); + CheckGetDalvikCacheFilenameOrDie("/data/app/foo-1.apk", "data@app@foo-1.apk@classes.dex"); + CheckGetDalvikCacheFilenameOrDie("/system/framework/core.jar", "system@framework@core.jar@classes.dex"); + CheckGetDalvikCacheFilenameOrDie("/system/framework/boot.art", "system@framework@boot.art"); +} + } // namespace art From 67fe2b41a6afccf6ab1a58879eae3e0e8f3d6c7a Mon Sep 17 00:00:00 2001 From: Brian Carlstrom Date: Tue, 15 Oct 2013 18:51:42 -0700 Subject: [PATCH 0084/2402] Fix backwards check in CheckStaticMethod Bug: 11243757 Change-Id: I559d1163ce72ab7831bd328c621519acb72975e0 --- runtime/check_jni.cc | 2 +- test/JniTest/JniTest.java | 21 +++++++++++++++++++++ test/JniTest/jni_test.cc | 16 ++++++++++++++++ 3 files changed, 38 insertions(+), 1 deletion(-) diff --git a/runtime/check_jni.cc b/runtime/check_jni.cc index 54f314391b4..3b1bda41bcc 100644 --- a/runtime/check_jni.cc +++ b/runtime/check_jni.cc @@ -335,7 +335,7 @@ class ScopedCheck { return; } mirror::Class* c = soa_.Decode(java_class); - if (!c->IsAssignableFrom(m->GetDeclaringClass())) { + if (!m->GetDeclaringClass()->IsAssignableFrom(c)) { JniAbortF(function_name_, "can't call static %s on class %s", PrettyMethod(m).c_str(), PrettyClass(c).c_str()); } diff --git a/test/JniTest/JniTest.java b/test/JniTest/JniTest.java index 431056ae32f..7014ef93342 100644 --- a/test/JniTest/JniTest.java +++ b/test/JniTest/JniTest.java @@ -18,7 +18,28 @@ class JniTest { public static void main(String[] args) { System.loadLibrary("arttest"); testFindClassOnAttachedNativeThread(); + testCallStaticVoidMethodOnSubClass(); } private static native void testFindClassOnAttachedNativeThread(); + + private static void testCallStaticVoidMethodOnSubClass() { + testCallStaticVoidMethodOnSubClassNative(); + if (!testCallStaticVoidMethodOnSubClass_SuperClass.executed) { + throw new AssertionError(); + } + } + + private static native void testCallStaticVoidMethodOnSubClassNative(); + + private static class testCallStaticVoidMethodOnSubClass_SuperClass { + private static boolean executed = false; + private static void execute() { + executed = true; + } + } + + private static class testCallStaticVoidMethodOnSubClass_SubClass + extends testCallStaticVoidMethodOnSubClass_SuperClass { + } } diff --git a/test/JniTest/jni_test.cc b/test/JniTest/jni_test.cc index ed69d39d274..72a3309d9dd 100644 --- a/test/JniTest/jni_test.cc +++ b/test/JniTest/jni_test.cc @@ -54,6 +54,7 @@ static void* testFindClassOnAttachedNativeThread(void*) { return NULL; } +// http://b/10994325 extern "C" JNIEXPORT void JNICALL Java_JniTest_testFindClassOnAttachedNativeThread(JNIEnv*, jclass) { pthread_t pthread; @@ -65,3 +66,18 @@ extern "C" JNIEXPORT void JNICALL Java_JniTest_testFindClassOnAttachedNativeThre int pthread_join_result = pthread_join(pthread, NULL); assert(pthread_join_result == 0); } + +// http://b/11243757 +extern "C" JNIEXPORT void JNICALL Java_JniTest_testCallStaticVoidMethodOnSubClassNative(JNIEnv* env, + jclass) { + jclass super_class = env->FindClass("JniTest$testCallStaticVoidMethodOnSubClass_SuperClass"); + assert(super_class != NULL); + + jmethodID execute = env->GetStaticMethodID(super_class, "execute", "()V"); + assert(execute != NULL); + + jclass sub_class = env->FindClass("JniTest$testCallStaticVoidMethodOnSubClass_SubClass"); + assert(sub_class != NULL); + + env->CallStaticVoidMethod(sub_class, execute); +} From ee1997a3b83334985e757f369c09e111b121661b Mon Sep 17 00:00:00 2001 From: Sebastien Hertz Date: Thu, 19 Sep 2013 14:47:09 +0200 Subject: [PATCH 0085/2402] Improve interpreter handler table management. We still have two handlers table: - the "main" table which holds execution handlers of each instruction, - the "alternative" table which holds handlers supporting instrumentation before jumping to the corresponding instruction handler from the "main" table. Instrumentation holds the index of the handler table the interpreter must use. This index is represented by the InterpreterHandlerTable enum and is stored in the Instrumentation::interpreter_handler_table_ field. Interpreter's current handler table update happens: - on backward branch - after invoke - when throwing exception. In the case of the backward branch and exception, we only update the table if any thread's flags is set. This allows to only do one test for handling thread suspension and handler table update. This CL also removes the local variable "instrumentation". Every handler which needs it will get it from Runtime::Current()->GetInstrumentation(). Change-Id: Id886ea7ebf3dac1285f0ca701c098aee7ebaab8d --- runtime/instrumentation.cc | 2 + runtime/instrumentation.h | 27 ++- .../interpreter_goto_table_impl.cc | 162 ++++++++++++------ 3 files changed, 136 insertions(+), 55 deletions(-) diff --git a/runtime/instrumentation.cc b/runtime/instrumentation.cc index 481cbad3b84..8316bc56b3b 100644 --- a/runtime/instrumentation.cc +++ b/runtime/instrumentation.cc @@ -293,6 +293,7 @@ void Instrumentation::AddListener(InstrumentationListener* listener, uint32_t ev have_exception_caught_listeners_ = true; } ConfigureStubs(require_entry_exit_stubs, require_interpreter); + UpdateInterpreterHandlerTable(); } void Instrumentation::RemoveListener(InstrumentationListener* listener, uint32_t events) { @@ -341,6 +342,7 @@ void Instrumentation::RemoveListener(InstrumentationListener* listener, uint32_t have_exception_caught_listeners_ = exception_caught_listeners_.size() > 0; } ConfigureStubs(require_entry_exit_stubs, require_interpreter); + UpdateInterpreterHandlerTable(); } void Instrumentation::ConfigureStubs(bool require_entry_exit_stubs, bool require_interpreter) { diff --git a/runtime/instrumentation.h b/runtime/instrumentation.h index 28f95553f8e..7a0aaf7858c 100644 --- a/runtime/instrumentation.h +++ b/runtime/instrumentation.h @@ -38,6 +38,14 @@ namespace instrumentation { const bool kVerboseInstrumentation = false; +// Interpreter handler tables. +enum InterpreterHandlerTable { + kMainHandlerTable = 0, // Main handler table: no suspend check, no instrumentation. + kAlternativeHandlerTable = 1, // Alternative handler table: suspend check and/or instrumentation + // enabled. + kNumHandlerTables +}; + // Instrumentation event listener API. Registered listeners will get the appropriate call back for // the events they are listening for. The call backs supply the thread, method and dex_pc the event // occurred upon. The thread may or may not be Thread::Current(). @@ -95,7 +103,8 @@ class Instrumentation { interpret_only_(false), forced_interpret_only_(false), have_method_entry_listeners_(false), have_method_exit_listeners_(false), have_method_unwind_listeners_(false), have_dex_pc_listeners_(false), - have_exception_caught_listeners_(false) {} + have_exception_caught_listeners_(false), + interpreter_handler_table_(kMainHandlerTable) {} // Add a listener to be notified of the masked together sent of instrumentation events. This // suspend the runtime to install stubs. You are expected to hold the mutator lock as a proxy @@ -110,6 +119,10 @@ class Instrumentation { EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_) LOCKS_EXCLUDED(Locks::thread_list_lock_, Locks::classlinker_classes_lock_); + InterpreterHandlerTable GetInterpreterHandlerTable() const { + return interpreter_handler_table_; + } + // Update the code of a method respecting any installed stubs. void UpdateMethodsCode(mirror::ArtMethod* method, const void* code) const; @@ -149,6 +162,11 @@ class Instrumentation { return have_dex_pc_listeners_; } + bool IsActive() const { + return have_dex_pc_listeners_ || have_method_entry_listeners_ || have_method_exit_listeners_ || + have_exception_caught_listeners_ || have_method_unwind_listeners_; + } + // Inform listeners that a method has been entered. A dex PC is provided as we may install // listeners into executing code and get method enter events for methods already on the stack. void MethodEnterEvent(Thread* thread, mirror::Object* this_object, @@ -215,6 +233,10 @@ class Instrumentation { EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_) LOCKS_EXCLUDED(Locks::thread_list_lock_, Locks::classlinker_classes_lock_); + void UpdateInterpreterHandlerTable() { + interpreter_handler_table_ = IsActive() ? kAlternativeHandlerTable : kMainHandlerTable; + } + void MethodEnterEventImpl(Thread* thread, mirror::Object* this_object, const mirror::ArtMethod* method, uint32_t dex_pc) const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); @@ -267,6 +289,9 @@ class Instrumentation { std::list dex_pc_listeners_ GUARDED_BY(Locks::mutator_lock_); std::list exception_caught_listeners_ GUARDED_BY(Locks::mutator_lock_); + // Current interpreter handler table. This is updated each time the thread state flags are modified. + InterpreterHandlerTable interpreter_handler_table_; + DISALLOW_COPY_AND_ASSIGN(Instrumentation); }; diff --git a/runtime/interpreter/interpreter_goto_table_impl.cc b/runtime/interpreter/interpreter_goto_table_impl.cc index daf6f93f1cd..608cd41b4f8 100644 --- a/runtime/interpreter/interpreter_goto_table_impl.cc +++ b/runtime/interpreter/interpreter_goto_table_impl.cc @@ -51,14 +51,8 @@ namespace interpreter { } \ } while (false) -#define UPDATE_HANDLER_TABLE() \ - do { \ - if (UNLIKELY(instrumentation->HasDexPcListeners())) { \ - currentHandlersTable = instrumentationHandlersTable; \ - } else { \ - currentHandlersTable = handlersTable; \ - } \ - } while (false); +#define UPDATE_HANDLER_TABLE() \ + currentHandlersTable = handlersTable[Runtime::Current()->GetInstrumentation()->GetInterpreterHandlerTable()] #define UNREACHABLE_CODE_CHECK() \ do { \ @@ -70,10 +64,77 @@ namespace interpreter { #define HANDLE_INSTRUCTION_START(opcode) op_##opcode: // NOLINT(whitespace/labels) #define HANDLE_INSTRUCTION_END() UNREACHABLE_CODE_CHECK() +/** + * Interpreter based on computed goto tables. + * + * Each instruction is associated to a handler. This handler is responsible for executing the + * instruction and jump to the next instruction's handler. + * In order to limit the cost of instrumentation, we have two handler tables: + * - the "main" handler table: it contains handlers for normal execution of each instruction without + * handling of instrumentation. + * - the "alternative" handler table: it contains alternative handlers which first handle + * instrumentation before jumping to the corresponding "normal" instruction's handler. + * + * When instrumentation is active, the interpreter uses the "alternative" handler table. Otherwise + * it uses the "main" handler table. + * + * The current handler table is the handler table being used by the interpreter. It is updated: + * - on backward branch (goto, if and switch instructions) + * - after invoke + * - when an exception is thrown. + * This allows to support an attaching debugger to an already running application for instance. + * + * For a fast handler table update, handler tables are stored in an array of handler tables. Each + * handler table is represented by the InterpreterHandlerTable enum which allows to associate it + * to an index in this array of handler tables ((see Instrumentation::GetInterpreterHandlerTable). + * + * Here's the current layout of this array of handler tables: + * + * ---------------------+---------------+ + * | NOP | (handler for NOP instruction) + * +---------------+ + * "main" | MOVE | (handler for MOVE instruction) + * handler table +---------------+ + * | ... | + * +---------------+ + * | UNUSED_FF | (handler for UNUSED_FF instruction) + * ---------------------+---------------+ + * | NOP | (alternative handler for NOP instruction) + * +---------------+ + * "alternative" | MOVE | (alternative handler for MOVE instruction) + * handler table +---------------+ + * | ... | + * +---------------+ + * | UNUSED_FF | (alternative handler for UNUSED_FF instruction) + * ---------------------+---------------+ + * + */ template JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* code_item, ShadowFrame& shadow_frame, JValue result_register) { - bool do_assignability_check = do_access_check; + // Define handler tables: + // - The main handler table contains execution handlers for each instruction. + // - The alternative handler table contains prelude handlers which check for thread suspend and + // manage instrumentation before jumping to the execution handler. + static const void* const handlersTable[instrumentation::kNumHandlerTables][kNumPackedOpcodes] = { + { + // Main handler table. +#define INSTRUCTION_HANDLER(o, code, n, f, r, i, a, v) &&op_##code, +#include "dex_instruction_list.h" + DEX_INSTRUCTION_LIST(INSTRUCTION_HANDLER) +#undef DEX_INSTRUCTION_LIST +#undef INSTRUCTION_HANDLER + }, { + // Alternative handler table. +#define INSTRUCTION_HANDLER(o, code, n, f, r, i, a, v) &&alt_op_##code, +#include "dex_instruction_list.h" + DEX_INSTRUCTION_LIST(INSTRUCTION_HANDLER) +#undef DEX_INSTRUCTION_LIST +#undef INSTRUCTION_HANDLER + } + }; + + const bool do_assignability_check = do_access_check; if (UNLIKELY(!shadow_frame.HasReferenceArray())) { LOG(FATAL) << "Invalid shadow frame for interpreter use"; return JValue(); @@ -81,35 +142,17 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* self->VerifyStack(); uint32_t dex_pc = shadow_frame.GetDexPC(); - const instrumentation::Instrumentation* const instrumentation = Runtime::Current()->GetInstrumentation(); + const Instruction* inst = Instruction::At(code_item->insns_ + dex_pc); + uint16_t inst_data; + const void* const* currentHandlersTable; + UPDATE_HANDLER_TABLE(); if (LIKELY(dex_pc == 0)) { // We are entering the method as opposed to deoptimizing.. + instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation(); if (UNLIKELY(instrumentation->HasMethodEntryListeners())) { instrumentation->MethodEnterEvent(self, shadow_frame.GetThisObject(code_item->ins_size_), shadow_frame.GetMethod(), 0); } } - const Instruction* inst = Instruction::At(code_item->insns_ + dex_pc); - uint16_t inst_data; - - // Define handlers table. - static const void* handlersTable[kNumPackedOpcodes] = { -#define INSTRUCTION_HANDLER(o, code, n, f, r, i, a, v) &&op_##code, -#include "dex_instruction_list.h" - DEX_INSTRUCTION_LIST(INSTRUCTION_HANDLER) -#undef DEX_INSTRUCTION_LIST -#undef INSTRUCTION_HANDLER - }; - - static const void* instrumentationHandlersTable[kNumPackedOpcodes] = { -#define INSTRUCTION_HANDLER(o, code, n, f, r, i, a, v) &&instrumentation_op_##code, -#include "dex_instruction_list.h" - DEX_INSTRUCTION_LIST(INSTRUCTION_HANDLER) -#undef DEX_INSTRUCTION_LIST -#undef INSTRUCTION_HANDLER - }; - - const void** currentHandlersTable; - UPDATE_HANDLER_TABLE(); // Jump to first instruction. ADVANCE(0); @@ -207,6 +250,7 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* if (UNLIKELY(self->TestAllFlags())) { CheckSuspend(self); } + instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation(); if (UNLIKELY(instrumentation->HasMethodExitListeners())) { instrumentation->MethodExitEvent(self, shadow_frame.GetThisObject(code_item->ins_size_), shadow_frame.GetMethod(), dex_pc, @@ -222,6 +266,7 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* if (UNLIKELY(self->TestAllFlags())) { CheckSuspend(self); } + instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation(); if (UNLIKELY(instrumentation->HasMethodExitListeners())) { instrumentation->MethodExitEvent(self, shadow_frame.GetThisObject(code_item->ins_size_), shadow_frame.GetMethod(), dex_pc, @@ -238,6 +283,7 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* if (UNLIKELY(self->TestAllFlags())) { CheckSuspend(self); } + instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation(); if (UNLIKELY(instrumentation->HasMethodExitListeners())) { instrumentation->MethodExitEvent(self, shadow_frame.GetThisObject(code_item->ins_size_), shadow_frame.GetMethod(), dex_pc, @@ -253,6 +299,7 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* if (UNLIKELY(self->TestAllFlags())) { CheckSuspend(self); } + instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation(); if (UNLIKELY(instrumentation->HasMethodExitListeners())) { instrumentation->MethodExitEvent(self, shadow_frame.GetThisObject(code_item->ins_size_), shadow_frame.GetMethod(), dex_pc, @@ -286,6 +333,7 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* HANDLE_PENDING_EXCEPTION(); } } + instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation(); if (UNLIKELY(instrumentation->HasMethodExitListeners())) { instrumentation->MethodExitEvent(self, shadow_frame.GetThisObject(code_item->ins_size_), shadow_frame.GetMethod(), dex_pc, @@ -547,8 +595,8 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* if (IsBackwardBranch(offset)) { if (UNLIKELY(self->TestAllFlags())) { CheckSuspend(self); + UPDATE_HANDLER_TABLE(); } - UPDATE_HANDLER_TABLE(); } ADVANCE(offset); } @@ -559,8 +607,8 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* if (IsBackwardBranch(offset)) { if (UNLIKELY(self->TestAllFlags())) { CheckSuspend(self); + UPDATE_HANDLER_TABLE(); } - UPDATE_HANDLER_TABLE(); } ADVANCE(offset); } @@ -571,8 +619,8 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* if (IsBackwardBranch(offset)) { if (UNLIKELY(self->TestAllFlags())) { CheckSuspend(self); + UPDATE_HANDLER_TABLE(); } - UPDATE_HANDLER_TABLE(); } ADVANCE(offset); } @@ -583,8 +631,8 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* if (IsBackwardBranch(offset)) { if (UNLIKELY(self->TestAllFlags())) { CheckSuspend(self); + UPDATE_HANDLER_TABLE(); } - UPDATE_HANDLER_TABLE(); } ADVANCE(offset); } @@ -595,8 +643,8 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* if (IsBackwardBranch(offset)) { if (UNLIKELY(self->TestAllFlags())) { CheckSuspend(self); + UPDATE_HANDLER_TABLE(); } - UPDATE_HANDLER_TABLE(); } ADVANCE(offset); } @@ -688,8 +736,8 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* if (IsBackwardBranch(offset)) { if (UNLIKELY(self->TestAllFlags())) { CheckSuspend(self); + UPDATE_HANDLER_TABLE(); } - UPDATE_HANDLER_TABLE(); } ADVANCE(offset); } else { @@ -704,8 +752,8 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* if (IsBackwardBranch(offset)) { if (UNLIKELY(self->TestAllFlags())) { CheckSuspend(self); + UPDATE_HANDLER_TABLE(); } - UPDATE_HANDLER_TABLE(); } ADVANCE(offset); } else { @@ -720,8 +768,8 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* if (IsBackwardBranch(offset)) { if (UNLIKELY(self->TestAllFlags())) { CheckSuspend(self); + UPDATE_HANDLER_TABLE(); } - UPDATE_HANDLER_TABLE(); } ADVANCE(offset); } else { @@ -736,8 +784,8 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* if (IsBackwardBranch(offset)) { if (UNLIKELY(self->TestAllFlags())) { CheckSuspend(self); + UPDATE_HANDLER_TABLE(); } - UPDATE_HANDLER_TABLE(); } ADVANCE(offset); } else { @@ -752,8 +800,8 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* if (IsBackwardBranch(offset)) { if (UNLIKELY(self->TestAllFlags())) { CheckSuspend(self); + UPDATE_HANDLER_TABLE(); } - UPDATE_HANDLER_TABLE(); } ADVANCE(offset); } else { @@ -768,8 +816,8 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* if (IsBackwardBranch(offset)) { if (UNLIKELY(self->TestAllFlags())) { CheckSuspend(self); + UPDATE_HANDLER_TABLE(); } - UPDATE_HANDLER_TABLE(); } ADVANCE(offset); } else { @@ -784,8 +832,8 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* if (IsBackwardBranch(offset)) { if (UNLIKELY(self->TestAllFlags())) { CheckSuspend(self); + UPDATE_HANDLER_TABLE(); } - UPDATE_HANDLER_TABLE(); } ADVANCE(offset); } else { @@ -800,8 +848,8 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* if (IsBackwardBranch(offset)) { if (UNLIKELY(self->TestAllFlags())) { CheckSuspend(self); + UPDATE_HANDLER_TABLE(); } - UPDATE_HANDLER_TABLE(); } ADVANCE(offset); } else { @@ -816,8 +864,8 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* if (IsBackwardBranch(offset)) { if (UNLIKELY(self->TestAllFlags())) { CheckSuspend(self); + UPDATE_HANDLER_TABLE(); } - UPDATE_HANDLER_TABLE(); } ADVANCE(offset); } else { @@ -832,8 +880,8 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* if (IsBackwardBranch(offset)) { if (UNLIKELY(self->TestAllFlags())) { CheckSuspend(self); + UPDATE_HANDLER_TABLE(); } - UPDATE_HANDLER_TABLE(); } ADVANCE(offset); } else { @@ -848,8 +896,8 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* if (IsBackwardBranch(offset)) { if (UNLIKELY(self->TestAllFlags())) { CheckSuspend(self); + UPDATE_HANDLER_TABLE(); } - UPDATE_HANDLER_TABLE(); } ADVANCE(offset); } else { @@ -864,8 +912,8 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* if (IsBackwardBranch(offset)) { if (UNLIKELY(self->TestAllFlags())) { CheckSuspend(self); + UPDATE_HANDLER_TABLE(); } - UPDATE_HANDLER_TABLE(); } ADVANCE(offset); } else { @@ -2306,8 +2354,10 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* CHECK(self->IsExceptionPending()); if (UNLIKELY(self->TestAllFlags())) { CheckSuspend(self); + UPDATE_HANDLER_TABLE(); } Object* this_object = shadow_frame.GetThisObject(code_item->ins_size_); + instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation(); uint32_t found_dex_pc = FindNextInstructionFollowingException(self, shadow_frame, dex_pc, this_object, instrumentation); @@ -2320,11 +2370,15 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* } // Create alternative instruction handlers dedicated to instrumentation. -#define INSTRUMENTATION_INSTRUCTION_HANDLER(o, code, n, f, r, i, a, v) \ - instrumentation_op_##code: { \ - instrumentation->DexPcMovedEvent(self, shadow_frame.GetThisObject(code_item->ins_size_), \ - shadow_frame.GetMethod(), dex_pc); \ - goto *handlersTable[Instruction::code]; \ +#define INSTRUMENTATION_INSTRUCTION_HANDLER(o, code, n, f, r, i, a, v) \ + alt_op_##code: { \ + instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation(); \ + if (UNLIKELY(instrumentation->HasDexPcListeners())) { \ + instrumentation->DexPcMovedEvent(self, shadow_frame.GetThisObject(code_item->ins_size_), \ + shadow_frame.GetMethod(), dex_pc); \ + } \ + UPDATE_HANDLER_TABLE(); \ + goto *handlersTable[instrumentation::kMainHandlerTable][Instruction::code]; \ } #include "dex_instruction_list.h" DEX_INSTRUCTION_LIST(INSTRUMENTATION_INSTRUCTION_HANDLER) From 2c88b38ec22278c325c2917fdc60f0674e8b7976 Mon Sep 17 00:00:00 2001 From: Sebastien Hertz Date: Fri, 18 Oct 2013 10:35:39 +0200 Subject: [PATCH 0086/2402] Make goto-based interpreter the default interpreter. Change-Id: Id21b57893864a72b77a78ef7df874c2c06209dc9 --- runtime/interpreter/interpreter.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/interpreter/interpreter.cc b/runtime/interpreter/interpreter.cc index 48c00148b46..d7555ddb6a6 100644 --- a/runtime/interpreter/interpreter.cc +++ b/runtime/interpreter/interpreter.cc @@ -269,7 +269,7 @@ enum InterpreterImplKind { kComputedGotoImplKind // computed-goto-based interpreter implementation. }; -static const InterpreterImplKind kInterpreterImplKind = kSwitchImpl; +static const InterpreterImplKind kInterpreterImplKind = kComputedGotoImplKind; static JValue Execute(Thread* self, MethodHelper& mh, const DexFile::CodeItem* code_item, ShadowFrame& shadow_frame, JValue result_register) From b917ea1a62aa0ab8eca3f689ef64b5be34e11abb Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Fri, 18 Oct 2013 08:12:48 -0700 Subject: [PATCH 0087/2402] Fix intrinsification of floatToRawIntBits. Accidentially lost in quick refactoring in commit fa57c47f1b72916371a9c2d5c1389219bce655b4. Change-Id: Ic83cf257306efa66a236f2034ffbff3b53579090 --- compiler/dex/quick/gen_invoke.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/dex/quick/gen_invoke.cc b/compiler/dex/quick/gen_invoke.cc index 0a0cc17ab27..f0623b01add 100644 --- a/compiler/dex/quick/gen_invoke.cc +++ b/compiler/dex/quick/gen_invoke.cc @@ -1249,7 +1249,7 @@ bool Mir2Lir::GenIntrinsic(CallInfo* info) { } } else if (tgt_methods_declaring_class.starts_with("Ljava/lang/Float;")) { std::string tgt_method(PrettyMethod(info->index, *cu_->dex_file)); - if (tgt_method == "int java.lang.Float.float_to_raw_int_bits(float)") { + if (tgt_method == "int java.lang.Float.floatToRawIntBits(float)") { return GenInlinedFloatCvt(info); } if (tgt_method == "float java.lang.Float.intBitsToFloat(int)") { From 77129ff50fc0234b62684e556d2c0bcb86123e80 Mon Sep 17 00:00:00 2001 From: Mathieu Chartier Date: Fri, 18 Oct 2013 11:36:46 -0700 Subject: [PATCH 0088/2402] Remove kNoCopyMagic. Was needed by jniGetNonMovableArrayElements which is now removed. Change-Id: Ie71eda5c5c3643c12db07d249597dbb2df41e88e --- runtime/check_jni.cc | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/runtime/check_jni.cc b/runtime/check_jni.cc index 3b1bda41bcc..54cbfe6ea57 100644 --- a/runtime/check_jni.cc +++ b/runtime/check_jni.cc @@ -90,12 +90,6 @@ static bool IsSirtLocalRef(JNIEnv* env, jobject localRef) { reinterpret_cast(env)->self->SirtContains(localRef); } -// Hack to allow forcecopy to work with jniGetNonMovableArrayElements. -// The code deliberately uses an invalid sequence of operations, so we -// need to pass it through unmodified. Review that code before making -// any changes here. -#define kNoCopyMagic 0xd5aab57f - // Flags passed into ScopedCheck. #define kFlag_Default 0x0000 @@ -1098,10 +1092,6 @@ static void* CreateGuardedPACopy(JNIEnv* env, const jarray java_array, jboolean* * back into the managed heap, and may or may not release the underlying storage. */ static void ReleaseGuardedPACopy(JNIEnv* env, jarray java_array, void* dataBuf, int mode) { - if (reinterpret_cast(dataBuf) == kNoCopyMagic) { - return; - } - ScopedObjectAccess soa(env); mirror::Array* a = soa.Decode(java_array); @@ -1596,9 +1586,7 @@ struct ForceCopyGetChecker { template ResultT Check(JNIEnv* env, jarray array, jboolean* isCopy, ResultT result) { if (force_copy && result != NULL) { - if (no_copy != kNoCopyMagic) { - result = reinterpret_cast(CreateGuardedPACopy(env, array, isCopy)); - } + result = reinterpret_cast(CreateGuardedPACopy(env, array, isCopy)); } return result; } From 1eb512d33f94d1dd7ea38263307ba0f7a0dfa653 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Fri, 18 Oct 2013 15:42:20 -0700 Subject: [PATCH 0089/2402] Fast JNI support. Use a modifier to signal a native method is a fast JNI method. If the modifier is set then don't perform runnable transitions. Change-Id: I7835b4d837bfdd1cb8e2d54b919c0d5e6cf90499 --- compiler/jni/jni_compiler_test.cc | 4 +- runtime/entrypoints/jni/jni_entrypoints.cc | 4 +- runtime/entrypoints/quick/quick_entrypoints.h | 13 ++-- .../quick/quick_jni_entrypoints.cc | 30 ++++++-- runtime/gc/heap.cc | 22 +++--- runtime/gc/heap.h | 5 +- runtime/jni_internal.cc | 20 ++--- runtime/jni_internal.h | 2 +- runtime/mirror/art_method.cc | 10 ++- runtime/mirror/art_method.h | 6 +- runtime/mirror/class.cc | 4 +- runtime/modifiers.h | 1 + runtime/native/dalvik_system_DexFile.cc | 26 +++---- runtime/native/dalvik_system_VMRuntime.cc | 23 +++--- runtime/native/dalvik_system_VMStack.cc | 13 ++-- runtime/native/java_lang_Class.cc | 11 +-- runtime/native/java_lang_DexCache.cc | 6 +- runtime/native/java_lang_Object.cc | 22 +++--- runtime/native/java_lang_Runtime.cc | 14 ++-- runtime/native/java_lang_String.cc | 13 ++-- runtime/native/java_lang_System.cc | 14 ++-- runtime/native/java_lang_Thread.cc | 9 ++- runtime/native/java_lang_reflect_Array.cc | 10 +-- .../native/java_lang_reflect_Constructor.cc | 3 +- runtime/native/java_lang_reflect_Field.cc | 54 +++++++------- ...rg_apache_harmony_dalvik_ddmc_DdmServer.cc | 6 +- runtime/native/sun_misc_Unsafe.cc | 74 +++++++++---------- runtime/runtime.cc | 2 +- runtime/scoped_thread_state_change.h | 13 +--- runtime/thread-inl.h | 8 ++ 30 files changed, 238 insertions(+), 204 deletions(-) diff --git a/compiler/jni/jni_compiler_test.cc b/compiler/jni/jni_compiler_test.cc index a653ab42a99..667b9130394 100644 --- a/compiler/jni/jni_compiler_test.cc +++ b/compiler/jni/jni_compiler_test.cc @@ -152,7 +152,7 @@ TEST_F(JniCompilerTest, CompileAndRunIntMethodThroughStub) { std::string reason; ASSERT_TRUE( Runtime::Current()->GetJavaVM()->LoadNativeLibrary("", soa.Decode(class_loader_), - reason)) << reason; + &reason)) << reason; jint result = env_->CallNonvirtualIntMethod(jobj_, jklass_, jmethod_, 24); EXPECT_EQ(25, result); @@ -167,7 +167,7 @@ TEST_F(JniCompilerTest, CompileAndRunStaticIntMethodThroughStub) { std::string reason; ASSERT_TRUE( Runtime::Current()->GetJavaVM()->LoadNativeLibrary("", soa.Decode(class_loader_), - reason)) << reason; + &reason)) << reason; jint result = env_->CallStaticIntMethod(jklass_, jmethod_, 42); EXPECT_EQ(43, result); diff --git a/runtime/entrypoints/jni/jni_entrypoints.cc b/runtime/entrypoints/jni/jni_entrypoints.cc index 83d3a584c55..16364fcdb70 100644 --- a/runtime/entrypoints/jni/jni_entrypoints.cc +++ b/runtime/entrypoints/jni/jni_entrypoints.cc @@ -41,7 +41,7 @@ extern "C" void* artFindNativeMethod() { return NULL; } else { // Register so that future calls don't come here - method->RegisterNative(self, native_code); + method->RegisterNative(self, native_code, false); return native_code; } } @@ -115,7 +115,7 @@ extern "C" const void* artWorkAroundAppJniBugs(Thread* self, intptr_t* sp) const void* code = reinterpret_cast(jni_method->GetNativeGcMap()); if (UNLIKELY(code == NULL)) { code = GetJniDlsymLookupStub(); - jni_method->RegisterNative(self, code); + jni_method->RegisterNative(self, code, false); } return code; } diff --git a/runtime/entrypoints/quick/quick_entrypoints.h b/runtime/entrypoints/quick/quick_entrypoints.h index e9964ad48c4..c8a85a0fe35 100644 --- a/runtime/entrypoints/quick/quick_entrypoints.h +++ b/runtime/entrypoints/quick/quick_entrypoints.h @@ -142,22 +142,23 @@ struct PACKED(4) QuickEntryPoints { // JNI entrypoints. -extern uint32_t JniMethodStart(Thread* self) UNLOCK_FUNCTION(Locks::mutator_lock_) HOT_ATTR; +// TODO: NO_THREAD_SAFETY_ANALYSIS due to different control paths depending on fast JNI. +extern uint32_t JniMethodStart(Thread* self) NO_THREAD_SAFETY_ANALYSIS HOT_ATTR; extern uint32_t JniMethodStartSynchronized(jobject to_lock, Thread* self) - UNLOCK_FUNCTION(Locks::mutator_lock_) HOT_ATTR; + NO_THREAD_SAFETY_ANALYSIS HOT_ATTR; extern void JniMethodEnd(uint32_t saved_local_ref_cookie, Thread* self) - SHARED_LOCK_FUNCTION(Locks::mutator_lock_) HOT_ATTR; + NO_THREAD_SAFETY_ANALYSIS HOT_ATTR; extern void JniMethodEndSynchronized(uint32_t saved_local_ref_cookie, jobject locked, Thread* self) - SHARED_LOCK_FUNCTION(Locks::mutator_lock_) HOT_ATTR; + NO_THREAD_SAFETY_ANALYSIS HOT_ATTR; extern mirror::Object* JniMethodEndWithReference(jobject result, uint32_t saved_local_ref_cookie, Thread* self) - SHARED_LOCK_FUNCTION(Locks::mutator_lock_) HOT_ATTR; + NO_THREAD_SAFETY_ANALYSIS HOT_ATTR; extern mirror::Object* JniMethodEndWithReferenceSynchronized(jobject result, uint32_t saved_local_ref_cookie, jobject locked, Thread* self) - SHARED_LOCK_FUNCTION(Locks::mutator_lock_) HOT_ATTR; + NO_THREAD_SAFETY_ANALYSIS HOT_ATTR; } // namespace art diff --git a/runtime/entrypoints/quick/quick_jni_entrypoints.cc b/runtime/entrypoints/quick/quick_jni_entrypoints.cc index 27ae59b9b0c..59da7a05cbe 100644 --- a/runtime/entrypoints/quick/quick_jni_entrypoints.cc +++ b/runtime/entrypoints/quick/quick_jni_entrypoints.cc @@ -30,10 +30,14 @@ namespace art { // Called on entry to JNI, transition out of Runnable and release share of mutator_lock_. extern uint32_t JniMethodStart(Thread* self) { JNIEnvExt* env = self->GetJniEnv(); - DCHECK(env != NULL); + DCHECK(env != nullptr); uint32_t saved_local_ref_cookie = env->local_ref_cookie; env->local_ref_cookie = env->locals.GetSegmentState(); - self->TransitionFromRunnableToSuspended(kNative); + mirror::ArtMethod* native_method = *self->GetManagedStack()->GetTopQuickFrame(); + if (!native_method->IsFastNative()) { + // When not fast JNI we transition out of runnable. + self->TransitionFromRunnableToSuspended(kNative); + } return saved_local_ref_cookie; } @@ -42,6 +46,20 @@ extern uint32_t JniMethodStartSynchronized(jobject to_lock, Thread* self) { return JniMethodStart(self); } +// TODO: NO_THREAD_SAFETY_ANALYSIS due to different control paths depending on fast JNI. +static void GoToRunnable(Thread* self) NO_THREAD_SAFETY_ANALYSIS { + mirror::ArtMethod* native_method = *self->GetManagedStack()->GetTopQuickFrame(); + bool is_fast = native_method->IsFastNative(); + if (!is_fast) { + self->TransitionFromSuspendedToRunnable(); + } else if (UNLIKELY(self->TestAllFlags())) { + // In fast JNI mode we never transitioned out of runnable. Perform a suspend check if there + // is a flag raised. + DCHECK(Locks::mutator_lock_->IsSharedHeld(self)); + CheckSuspend(self); + } +} + static void PopLocalReferences(uint32_t saved_local_ref_cookie, Thread* self) { JNIEnvExt* env = self->GetJniEnv(); env->locals.SetSegmentState(env->local_ref_cookie); @@ -50,21 +68,21 @@ static void PopLocalReferences(uint32_t saved_local_ref_cookie, Thread* self) { } extern void JniMethodEnd(uint32_t saved_local_ref_cookie, Thread* self) { - self->TransitionFromSuspendedToRunnable(); + GoToRunnable(self); PopLocalReferences(saved_local_ref_cookie, self); } extern void JniMethodEndSynchronized(uint32_t saved_local_ref_cookie, jobject locked, Thread* self) { - self->TransitionFromSuspendedToRunnable(); + GoToRunnable(self); UnlockJniSynchronizedMethod(locked, self); // Must decode before pop. PopLocalReferences(saved_local_ref_cookie, self); } extern mirror::Object* JniMethodEndWithReference(jobject result, uint32_t saved_local_ref_cookie, Thread* self) { - self->TransitionFromSuspendedToRunnable(); + GoToRunnable(self); mirror::Object* o = self->DecodeJObject(result); // Must decode before pop. PopLocalReferences(saved_local_ref_cookie, self); // Process result. @@ -80,7 +98,7 @@ extern mirror::Object* JniMethodEndWithReference(jobject result, uint32_t saved_ extern mirror::Object* JniMethodEndWithReferenceSynchronized(jobject result, uint32_t saved_local_ref_cookie, jobject locked, Thread* self) { - self->TransitionFromSuspendedToRunnable(); + GoToRunnable(self); UnlockJniSynchronizedMethod(locked, self); // Must decode before pop. mirror::Object* o = self->DecodeJObject(result); PopLocalReferences(saved_local_ref_cookie, self); diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc index ed902420af0..d26e28cb472 100644 --- a/runtime/gc/heap.cc +++ b/runtime/gc/heap.cc @@ -2044,24 +2044,22 @@ bool Heap::IsGCRequestPending() const { return concurrent_start_bytes_ != std::numeric_limits::max(); } -void Heap::RegisterNativeAllocation(int bytes) { +void Heap::RegisterNativeAllocation(JNIEnv* env, int bytes) { // Total number of native bytes allocated. native_bytes_allocated_.fetch_add(bytes); - Thread* self = Thread::Current(); if (static_cast(native_bytes_allocated_) > native_footprint_gc_watermark_) { // The second watermark is higher than the gc watermark. If you hit this it means you are // allocating native objects faster than the GC can keep up with. if (static_cast(native_bytes_allocated_) > native_footprint_limit_) { - JNIEnv* env = self->GetJniEnv(); // Can't do this in WellKnownClasses::Init since System is not properly set up at that // point. - if (WellKnownClasses::java_lang_System_runFinalization == NULL) { + if (UNLIKELY(WellKnownClasses::java_lang_System_runFinalization == NULL)) { DCHECK(WellKnownClasses::java_lang_System != NULL); WellKnownClasses::java_lang_System_runFinalization = CacheMethod(env, WellKnownClasses::java_lang_System, true, "runFinalization", "()V"); - assert(WellKnownClasses::java_lang_System_runFinalization != NULL); + CHECK(WellKnownClasses::java_lang_System_runFinalization != NULL); } - if (WaitForConcurrentGcToComplete(self) != collector::kGcTypeNone) { + if (WaitForConcurrentGcToComplete(ThreadForEnv(env)) != collector::kGcTypeNone) { // Just finished a GC, attempt to run finalizers. env->CallStaticVoidMethod(WellKnownClasses::java_lang_System, WellKnownClasses::java_lang_System_runFinalization); @@ -2080,20 +2078,22 @@ void Heap::RegisterNativeAllocation(int bytes) { UpdateMaxNativeFootprint(); } else { if (!IsGCRequestPending()) { - RequestConcurrentGC(self); + RequestConcurrentGC(ThreadForEnv(env)); } } } } -void Heap::RegisterNativeFree(int bytes) { +void Heap::RegisterNativeFree(JNIEnv* env, int bytes) { int expected_size, new_size; do { expected_size = native_bytes_allocated_.load(); new_size = expected_size - bytes; - if (new_size < 0) { - ThrowRuntimeException("attempted to free %d native bytes with only %d native bytes registered as allocated", - bytes, expected_size); + if (UNLIKELY(new_size < 0)) { + ScopedObjectAccess soa(env); + env->ThrowNew(WellKnownClasses::java_lang_RuntimeException, + StringPrintf("Attempted to free %d native bytes with only %d native bytes " + "registered as allocated", bytes, expected_size).c_str()); break; } } while (!native_bytes_allocated_.compare_and_swap(expected_size, new_size)); diff --git a/runtime/gc/heap.h b/runtime/gc/heap.h index ffd3034674b..1c2b7efc225 100644 --- a/runtime/gc/heap.h +++ b/runtime/gc/heap.h @@ -146,9 +146,8 @@ class Heap { SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); void ThrowOutOfMemoryError(size_t byte_count, bool large_object_allocation); - void RegisterNativeAllocation(int bytes) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - void RegisterNativeFree(int bytes) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + void RegisterNativeAllocation(JNIEnv* env, int bytes); + void RegisterNativeFree(JNIEnv* env, int bytes); // The given reference is believed to be to an object in the Java heap, check the soundness of it. void VerifyObjectImpl(const mirror::Object* o); diff --git a/runtime/jni_internal.cc b/runtime/jni_internal.cc index 29fc7a40c89..ec717c1bee5 100644 --- a/runtime/jni_internal.cc +++ b/runtime/jni_internal.cc @@ -2357,9 +2357,9 @@ class JNI { for (jint i = 0; i < method_count; ++i) { const char* name = methods[i].name; const char* sig = methods[i].signature; - + bool is_fast = false; if (*sig == '!') { - // TODO: fast jni. it's too noisy to log all these. + is_fast = true; ++sig; } @@ -2382,7 +2382,7 @@ class JNI { VLOG(jni) << "[Registering JNI native method " << PrettyMethod(m) << "]"; - m->RegisterNative(soa.Self(), methods[i].fnPtr); + m->RegisterNative(soa.Self(), methods[i].fnPtr, is_fast); } return JNI_OK; } @@ -3107,8 +3107,8 @@ void JavaVMExt::DumpReferenceTables(std::ostream& os) { } bool JavaVMExt::LoadNativeLibrary(const std::string& path, ClassLoader* class_loader, - std::string& detail) { - detail.clear(); + std::string* detail) { + detail->clear(); // See if we've already loaded this library. If we have, and the class loader // matches, return successfully without doing anything. @@ -3126,7 +3126,7 @@ bool JavaVMExt::LoadNativeLibrary(const std::string& path, ClassLoader* class_lo // The library will be associated with class_loader. The JNI // spec says we can't load the same library into more than one // class loader. - StringAppendF(&detail, "Shared library \"%s\" already opened by " + StringAppendF(detail, "Shared library \"%s\" already opened by " "ClassLoader %p; can't open in ClassLoader %p", path.c_str(), library->GetClassLoader(), class_loader); LOG(WARNING) << detail; @@ -3135,7 +3135,7 @@ bool JavaVMExt::LoadNativeLibrary(const std::string& path, ClassLoader* class_lo VLOG(jni) << "[Shared library \"" << path << "\" already loaded in " << "ClassLoader " << class_loader << "]"; if (!library->CheckOnLoadResult()) { - StringAppendF(&detail, "JNI_OnLoad failed on a previous attempt " + StringAppendF(detail, "JNI_OnLoad failed on a previous attempt " "to load \"%s\"", path.c_str()); return false; } @@ -3162,7 +3162,7 @@ bool JavaVMExt::LoadNativeLibrary(const std::string& path, ClassLoader* class_lo VLOG(jni) << "[Call to dlopen(\"" << path << "\", RTLD_LAZY) returned " << handle << "]"; if (handle == NULL) { - detail = dlerror(); + *detail = dlerror(); LOG(ERROR) << "dlopen(\"" << path << "\", RTLD_LAZY) failed: " << detail; return false; } @@ -3212,9 +3212,9 @@ bool JavaVMExt::LoadNativeLibrary(const std::string& path, ClassLoader* class_lo self->SetClassLoaderOverride(old_class_loader); if (version == JNI_ERR) { - StringAppendF(&detail, "JNI_ERR returned from JNI_OnLoad in \"%s\"", path.c_str()); + StringAppendF(detail, "JNI_ERR returned from JNI_OnLoad in \"%s\"", path.c_str()); } else if (IsBadJniVersion(version)) { - StringAppendF(&detail, "Bad JNI version returned from JNI_OnLoad in \"%s\": %d", + StringAppendF(detail, "Bad JNI version returned from JNI_OnLoad in \"%s\": %d", path.c_str(), version); // It's unwise to call dlclose() here, but we can mark it // as bad and ensure that future load attempts will fail. diff --git a/runtime/jni_internal.h b/runtime/jni_internal.h index c73ed48014c..888d5e5458a 100644 --- a/runtime/jni_internal.h +++ b/runtime/jni_internal.h @@ -73,7 +73,7 @@ class JavaVMExt : public JavaVM { * human-readable description of the error. */ bool LoadNativeLibrary(const std::string& path, mirror::ClassLoader* class_loader, - std::string& detail) + std::string* detail) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); /** diff --git a/runtime/mirror/art_method.cc b/runtime/mirror/art_method.cc index cd05f41cc24..f5c0e9f2166 100644 --- a/runtime/mirror/art_method.cc +++ b/runtime/mirror/art_method.cc @@ -306,11 +306,15 @@ bool ArtMethod::IsRegistered() const { } extern "C" void art_work_around_app_jni_bugs(JNIEnv*, jobject); -void ArtMethod::RegisterNative(Thread* self, const void* native_method) { +void ArtMethod::RegisterNative(Thread* self, const void* native_method, bool is_fast) { DCHECK(Thread::Current() == self); CHECK(IsNative()) << PrettyMethod(this); + CHECK(!IsFastNative()) << PrettyMethod(this); CHECK(native_method != NULL) << PrettyMethod(this); if (!self->GetJniEnv()->vm->work_around_app_jni_bugs) { + if (is_fast) { + SetAccessFlags(GetAccessFlags() | kAccFastNative); + } SetNativeMethod(native_method); } else { // We've been asked to associate this method with the given native method but are working @@ -328,9 +332,9 @@ void ArtMethod::RegisterNative(Thread* self, const void* native_method) { } void ArtMethod::UnregisterNative(Thread* self) { - CHECK(IsNative()) << PrettyMethod(this); + CHECK(IsNative() && !IsFastNative()) << PrettyMethod(this); // restore stub to lookup native pointer via dlsym - RegisterNative(self, GetJniDlsymLookupStub()); + RegisterNative(self, GetJniDlsymLookupStub(), false); } void ArtMethod::SetNativeMethod(const void* native_method) { diff --git a/runtime/mirror/art_method.h b/runtime/mirror/art_method.h index 5d4a6ea0d5a..052089373dd 100644 --- a/runtime/mirror/art_method.h +++ b/runtime/mirror/art_method.h @@ -112,6 +112,10 @@ class MANAGED ArtMethod : public Object { return (GetAccessFlags() & kAccNative) != 0; } + bool IsFastNative() const { + return (GetAccessFlags() & kAccFastNative) != 0; + } + bool IsAbstract() const { return (GetAccessFlags() & kAccAbstract) != 0; } @@ -307,7 +311,7 @@ class MANAGED ArtMethod : public Object { bool IsRegistered() const; - void RegisterNative(Thread* self, const void* native_method) + void RegisterNative(Thread* self, const void* native_method, bool is_fast) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); void UnregisterNative(Thread* self) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); diff --git a/runtime/mirror/class.cc b/runtime/mirror/class.cc index 2b0b1e1755e..319ca4a5f93 100644 --- a/runtime/mirror/class.cc +++ b/runtime/mirror/class.cc @@ -661,7 +661,9 @@ static void SetPreverifiedFlagOnMethods(mirror::ObjectArray* for (int32_t index = 0, end = methods->GetLength(); index < end; ++index) { mirror::ArtMethod* method = methods->GetWithoutChecks(index); DCHECK(method != NULL); - method->SetPreverified(); + if (!method->IsNative() && !method->IsAbstract()) { + method->SetPreverified(); + } } } } diff --git a/runtime/modifiers.h b/runtime/modifiers.h index 34f4af845b7..4e365be8e0d 100644 --- a/runtime/modifiers.h +++ b/runtime/modifiers.h @@ -46,6 +46,7 @@ static const uint32_t kAccConstructor = 0x00010000; // method (dex only) static const uint32_t kAccDeclaredSynchronized = 0x00020000; // method (dex only) static const uint32_t kAccClassIsProxy = 0x00040000; // class (dex only) static const uint32_t kAccPreverified = 0x00080000; // method (dex only) +static const uint32_t kAccFastNative = 0x0080000; // method (dex only) // Special runtime-only flags. // Note: if only kAccClassIsReference is set, we have a soft reference. diff --git a/runtime/native/dalvik_system_DexFile.cc b/runtime/native/dalvik_system_DexFile.cc index 823013a9509..4e17b795a53 100644 --- a/runtime/native/dalvik_system_DexFile.cc +++ b/runtime/native/dalvik_system_DexFile.cc @@ -95,7 +95,6 @@ static jint DexFile_openDexFileNative(JNIEnv* env, jclass, jstring javaSourceNam return 0; } ScopedObjectAccess soa(env); - uint32_t dex_location_checksum; if (!DexFile::GetChecksum(dex_location, &dex_location_checksum)) { LOG(WARNING) << "Failed to compute checksum: " << dex_location; @@ -123,9 +122,10 @@ static jint DexFile_openDexFileNative(JNIEnv* env, jclass, jstring javaSourceNam return static_cast(reinterpret_cast(dex_file)); } -static const DexFile* toDexFile(int dex_file_address) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { +static const DexFile* toDexFile(int dex_file_address, JNIEnv* env) { const DexFile* dex_file = reinterpret_cast(static_cast(dex_file_address)); - if (dex_file == NULL) { + if (UNLIKELY(dex_file == nullptr)) { + ScopedObjectAccess soa(env); ThrowNullPointerException(NULL, "dex_file == null"); } return dex_file; @@ -133,11 +133,8 @@ static const DexFile* toDexFile(int dex_file_address) SHARED_LOCKS_REQUIRED(Lock static void DexFile_closeDexFile(JNIEnv* env, jclass, jint cookie) { const DexFile* dex_file; - { - ScopedObjectAccess soa(env); - dex_file = toDexFile(cookie); - } - if (dex_file == NULL) { + dex_file = toDexFile(cookie, env); + if (dex_file == nullptr) { return; } if (Runtime::Current()->GetClassLinker()->IsDexFileRegistered(*dex_file)) { @@ -148,8 +145,7 @@ static void DexFile_closeDexFile(JNIEnv* env, jclass, jint cookie) { static jclass DexFile_defineClassNative(JNIEnv* env, jclass, jstring javaName, jobject javaLoader, jint cookie) { - ScopedObjectAccess soa(env); - const DexFile* dex_file = toDexFile(cookie); + const DexFile* dex_file = toDexFile(cookie, env); if (dex_file == NULL) { VLOG(class_linker) << "Failed to find dex_file"; return NULL; @@ -165,6 +161,7 @@ static jclass DexFile_defineClassNative(JNIEnv* env, jclass, jstring javaName, j VLOG(class_linker) << "Failed to find dex_class_def"; return NULL; } + ScopedObjectAccess soa(env); ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); class_linker->RegisterDexFile(*dex_file); mirror::ClassLoader* class_loader = soa.Decode(javaLoader); @@ -176,12 +173,9 @@ static jclass DexFile_defineClassNative(JNIEnv* env, jclass, jstring javaName, j static jobjectArray DexFile_getClassNameList(JNIEnv* env, jclass, jint cookie) { const DexFile* dex_file; - { - ScopedObjectAccess soa(env); - dex_file = toDexFile(cookie); - } - if (dex_file == NULL) { - return NULL; + dex_file = toDexFile(cookie, env); + if (dex_file == nullptr) { + return nullptr; } std::vector class_names; diff --git a/runtime/native/dalvik_system_VMRuntime.cc b/runtime/native/dalvik_system_VMRuntime.cc index dad6eff3548..9ad2585bc52 100644 --- a/runtime/native/dalvik_system_VMRuntime.cc +++ b/runtime/native/dalvik_system_VMRuntime.cc @@ -28,6 +28,7 @@ #include "mirror/object.h" #include "mirror/object-inl.h" #include "object_utils.h" +#include "scoped_fast_native_object_access.h" #include "scoped_thread_state_change.h" #include "thread.h" #include "thread_list.h" @@ -50,7 +51,7 @@ static void VMRuntime_disableJitCompilation(JNIEnv*, jobject) { } static jobject VMRuntime_newNonMovableArray(JNIEnv* env, jobject, jclass javaElementClass, jint length) { - ScopedObjectAccess soa(env); + ScopedFastNativeObjectAccess soa(env); #ifdef MOVING_GARBAGE_COLLECTOR // TODO: right now, we don't have a copying collector, so there's no need // to do anything special here, but we ought to pass the non-movability @@ -81,7 +82,7 @@ static jlong VMRuntime_addressOf(JNIEnv* env, jobject, jobject javaArray) { if (javaArray == NULL) { // Most likely allocation failed return 0; } - ScopedObjectAccess soa(env); + ScopedFastNativeObjectAccess soa(env); mirror::Array* array = soa.Decode(javaArray); if (!array->IsArrayInstance()) { ThrowIllegalArgumentException(NULL, "not an array"); @@ -147,21 +148,21 @@ static void VMRuntime_setTargetSdkVersion(JNIEnv* env, jobject, jint targetSdkVe } static void VMRuntime_registerNativeAllocation(JNIEnv* env, jobject, jint bytes) { - ScopedObjectAccess soa(env); - if (bytes < 0) { + if (UNLIKELY(bytes < 0)) { + ScopedObjectAccess soa(env); ThrowRuntimeException("allocation size negative %d", bytes); return; } - Runtime::Current()->GetHeap()->RegisterNativeAllocation(bytes); + Runtime::Current()->GetHeap()->RegisterNativeAllocation(env, bytes); } static void VMRuntime_registerNativeFree(JNIEnv* env, jobject, jint bytes) { - ScopedObjectAccess soa(env); - if (bytes < 0) { + if (UNLIKELY(bytes < 0)) { + ScopedObjectAccess soa(env); ThrowRuntimeException("allocation size negative %d", bytes); return; } - Runtime::Current()->GetHeap()->RegisterNativeFree(bytes); + Runtime::Current()->GetHeap()->RegisterNativeFree(env, bytes); } static void VMRuntime_trimHeap(JNIEnv*, jobject) { @@ -189,12 +190,12 @@ static void VMRuntime_trimHeap(JNIEnv*, jobject) { } static void VMRuntime_concurrentGC(JNIEnv* env, jobject) { - Thread* self = static_cast(env)->self; + Thread* self = ThreadForEnv(env); Runtime::Current()->GetHeap()->ConcurrentGC(self); } static JNINativeMethod gMethods[] = { - NATIVE_METHOD(VMRuntime, addressOf, "(Ljava/lang/Object;)J"), + NATIVE_METHOD(VMRuntime, addressOf, "!(Ljava/lang/Object;)J"), NATIVE_METHOD(VMRuntime, bootClassPath, "()Ljava/lang/String;"), NATIVE_METHOD(VMRuntime, classPath, "()Ljava/lang/String;"), NATIVE_METHOD(VMRuntime, clearGrowthLimit, "()V"), @@ -203,7 +204,7 @@ static JNINativeMethod gMethods[] = { NATIVE_METHOD(VMRuntime, getTargetHeapUtilization, "()F"), NATIVE_METHOD(VMRuntime, isDebuggerActive, "()Z"), NATIVE_METHOD(VMRuntime, nativeSetTargetHeapUtilization, "(F)V"), - NATIVE_METHOD(VMRuntime, newNonMovableArray, "(Ljava/lang/Class;I)Ljava/lang/Object;"), + NATIVE_METHOD(VMRuntime, newNonMovableArray, "!(Ljava/lang/Class;I)Ljava/lang/Object;"), NATIVE_METHOD(VMRuntime, properties, "()[Ljava/lang/String;"), NATIVE_METHOD(VMRuntime, setTargetSdkVersion, "(I)V"), NATIVE_METHOD(VMRuntime, registerNativeAllocation, "(I)V"), diff --git a/runtime/native/dalvik_system_VMStack.cc b/runtime/native/dalvik_system_VMStack.cc index 5508270bc22..f91536544a0 100644 --- a/runtime/native/dalvik_system_VMStack.cc +++ b/runtime/native/dalvik_system_VMStack.cc @@ -20,6 +20,7 @@ #include "mirror/class-inl.h" #include "mirror/class_loader.h" #include "mirror/object-inl.h" +#include "scoped_fast_native_object_access.h" #include "scoped_thread_state_change.h" #include "thread_list.h" @@ -66,7 +67,7 @@ static jint VMStack_fillStackTraceElements(JNIEnv* env, jclass, jobject javaThre // Returns the defining class loader of the caller's caller. static jobject VMStack_getCallingClassLoader(JNIEnv* env, jclass) { - ScopedObjectAccess soa(env); + ScopedFastNativeObjectAccess soa(env); NthCallerVisitor visitor(soa.Self(), 2); visitor.WalkStack(); return soa.AddLocalReference(visitor.caller->GetDeclaringClass()->GetClassLoader()); @@ -93,7 +94,7 @@ static jobject VMStack_getClosestUserClassLoader(JNIEnv* env, jclass, jobject ja mirror::Object* system; mirror::Object* class_loader; }; - ScopedObjectAccess soa(env); + ScopedFastNativeObjectAccess soa(env); mirror::Object* bootstrap = soa.Decode(javaBootstrap); mirror::Object* system = soa.Decode(javaSystem); ClosestUserClassLoaderVisitor visitor(soa.Self(), bootstrap, system); @@ -103,7 +104,7 @@ static jobject VMStack_getClosestUserClassLoader(JNIEnv* env, jclass, jobject ja // Returns the class of the caller's caller's caller. static jclass VMStack_getStackClass2(JNIEnv* env, jclass) { - ScopedObjectAccess soa(env); + ScopedFastNativeObjectAccess soa(env); NthCallerVisitor visitor(soa.Self(), 3); visitor.WalkStack(); return soa.AddLocalReference(visitor.caller->GetDeclaringClass()); @@ -119,9 +120,9 @@ static jobjectArray VMStack_getThreadStackTrace(JNIEnv* env, jclass, jobject jav static JNINativeMethod gMethods[] = { NATIVE_METHOD(VMStack, fillStackTraceElements, "(Ljava/lang/Thread;[Ljava/lang/StackTraceElement;)I"), - NATIVE_METHOD(VMStack, getCallingClassLoader, "()Ljava/lang/ClassLoader;"), - NATIVE_METHOD(VMStack, getClosestUserClassLoader, "(Ljava/lang/ClassLoader;Ljava/lang/ClassLoader;)Ljava/lang/ClassLoader;"), - NATIVE_METHOD(VMStack, getStackClass2, "()Ljava/lang/Class;"), + NATIVE_METHOD(VMStack, getCallingClassLoader, "!()Ljava/lang/ClassLoader;"), + NATIVE_METHOD(VMStack, getClosestUserClassLoader, "!(Ljava/lang/ClassLoader;Ljava/lang/ClassLoader;)Ljava/lang/ClassLoader;"), + NATIVE_METHOD(VMStack, getStackClass2, "!()Ljava/lang/Class;"), NATIVE_METHOD(VMStack, getThreadStackTrace, "(Ljava/lang/Thread;)[Ljava/lang/StackTraceElement;"), }; diff --git a/runtime/native/java_lang_Class.cc b/runtime/native/java_lang_Class.cc index d3011cb0132..3591611185d 100644 --- a/runtime/native/java_lang_Class.cc +++ b/runtime/native/java_lang_Class.cc @@ -24,13 +24,14 @@ #include "mirror/proxy.h" #include "object_utils.h" #include "scoped_thread_state_change.h" +#include "scoped_fast_native_object_access.h" #include "ScopedLocalRef.h" #include "ScopedUtfChars.h" #include "well_known_classes.h" namespace art { -static mirror::Class* DecodeClass(const ScopedObjectAccess& soa, jobject java_class) +static mirror::Class* DecodeClass(const ScopedFastNativeObjectAccess& soa, jobject java_class) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { mirror::Class* c = soa.Decode(java_class); DCHECK(c != NULL); @@ -79,13 +80,13 @@ static jclass Class_classForName(JNIEnv* env, jclass, jstring javaName, jboolean } static jstring Class_getNameNative(JNIEnv* env, jobject javaThis) { - ScopedObjectAccess soa(env); + ScopedFastNativeObjectAccess soa(env); mirror::Class* c = DecodeClass(soa, javaThis); return soa.AddLocalReference(c->ComputeName()); } static jobjectArray Class_getProxyInterfaces(JNIEnv* env, jobject javaThis) { - ScopedObjectAccess soa(env); + ScopedFastNativeObjectAccess soa(env); mirror::SynthesizedProxyClass* c = down_cast(DecodeClass(soa, javaThis)); return soa.AddLocalReference(c->GetInterfaces()->Clone(soa.Self())); @@ -93,8 +94,8 @@ static jobjectArray Class_getProxyInterfaces(JNIEnv* env, jobject javaThis) { static JNINativeMethod gMethods[] = { NATIVE_METHOD(Class, classForName, "(Ljava/lang/String;ZLjava/lang/ClassLoader;)Ljava/lang/Class;"), - NATIVE_METHOD(Class, getNameNative, "()Ljava/lang/String;"), - NATIVE_METHOD(Class, getProxyInterfaces, "()[Ljava/lang/Class;"), + NATIVE_METHOD(Class, getNameNative, "!()Ljava/lang/String;"), + NATIVE_METHOD(Class, getProxyInterfaces, "!()[Ljava/lang/Class;"), }; void register_java_lang_Class(JNIEnv* env) { diff --git a/runtime/native/java_lang_DexCache.cc b/runtime/native/java_lang_DexCache.cc index 9b83206c37a..51cd5b80d57 100644 --- a/runtime/native/java_lang_DexCache.cc +++ b/runtime/native/java_lang_DexCache.cc @@ -17,13 +17,13 @@ #include "dex_file.h" #include "mirror/dex_cache.h" #include "mirror/object-inl.h" -#include "scoped_thread_state_change.h" +#include "scoped_fast_native_object_access.h" #include "well_known_classes.h" namespace art { static jobject DexCache_getDexNative(JNIEnv* env, jobject javaDexCache) { - ScopedObjectAccess soa(env); + ScopedFastNativeObjectAccess soa(env); mirror::DexCache* dex_cache = soa.Decode(javaDexCache); // Should only be called while holding the lock on the dex cache. DCHECK_EQ(dex_cache->GetLockOwnerThreadId(), soa.Self()->GetThreadId()); @@ -46,7 +46,7 @@ static jobject DexCache_getDexNative(JNIEnv* env, jobject javaDexCache) { } static JNINativeMethod gMethods[] = { - NATIVE_METHOD(DexCache, getDexNative, "()Lcom/android/dex/Dex;"), + NATIVE_METHOD(DexCache, getDexNative, "!()Lcom/android/dex/Dex;"), }; void register_java_lang_DexCache(JNIEnv* env) { diff --git a/runtime/native/java_lang_Object.cc b/runtime/native/java_lang_Object.cc index 5db7a330a8d..4768f48d9c8 100644 --- a/runtime/native/java_lang_Object.cc +++ b/runtime/native/java_lang_Object.cc @@ -16,7 +16,7 @@ #include "jni_internal.h" #include "mirror/object-inl.h" -#include "scoped_thread_state_change.h" +#include "scoped_fast_native_object_access.h" // TODO: better support for overloading. #undef NATIVE_METHOD @@ -26,41 +26,41 @@ namespace art { static jobject Object_internalClone(JNIEnv* env, jobject java_this) { - ScopedObjectAccess soa(env); + ScopedFastNativeObjectAccess soa(env); mirror::Object* o = soa.Decode(java_this); return soa.AddLocalReference(o->Clone(soa.Self())); } static void Object_notify(JNIEnv* env, jobject java_this) { - ScopedObjectAccess soa(env); + ScopedFastNativeObjectAccess soa(env); mirror::Object* o = soa.Decode(java_this); o->Notify(soa.Self()); } static void Object_notifyAll(JNIEnv* env, jobject java_this) { - ScopedObjectAccess soa(env); + ScopedFastNativeObjectAccess soa(env); mirror::Object* o = soa.Decode(java_this); o->NotifyAll(soa.Self()); } static void Object_wait(JNIEnv* env, jobject java_this) { - ScopedObjectAccess soa(env); + ScopedFastNativeObjectAccess soa(env); mirror::Object* o = soa.Decode(java_this); o->Wait(soa.Self()); } static void Object_waitJI(JNIEnv* env, jobject java_this, jlong ms, jint ns) { - ScopedObjectAccess soa(env); + ScopedFastNativeObjectAccess soa(env); mirror::Object* o = soa.Decode(java_this); o->Wait(soa.Self(), ms, ns); } static JNINativeMethod gMethods[] = { - NATIVE_METHOD(Object, internalClone, "()Ljava/lang/Object;", internalClone), - NATIVE_METHOD(Object, notify, "()V", notify), - NATIVE_METHOD(Object, notifyAll, "()V", notifyAll), - NATIVE_METHOD(Object, wait, "()V", wait), - NATIVE_METHOD(Object, wait, "(JI)V", waitJI), + NATIVE_METHOD(Object, internalClone, "!()Ljava/lang/Object;", internalClone), + NATIVE_METHOD(Object, notify, "!()V", notify), + NATIVE_METHOD(Object, notifyAll, "!()V", notifyAll), + NATIVE_METHOD(Object, wait, "!()V", wait), + NATIVE_METHOD(Object, wait, "!(JI)V", waitJI), }; void register_java_lang_Object(JNIEnv* env) { diff --git a/runtime/native/java_lang_Runtime.cc b/runtime/native/java_lang_Runtime.cc index 55575cf5a24..e969fcf503e 100644 --- a/runtime/native/java_lang_Runtime.cc +++ b/runtime/native/java_lang_Runtime.cc @@ -41,7 +41,6 @@ static void Runtime_nativeExit(JNIEnv*, jclass, jint status) { } static jstring Runtime_nativeLoad(JNIEnv* env, jclass, jstring javaFilename, jobject javaLoader, jstring javaLdLibraryPath) { - ScopedObjectAccess soa(env); ScopedUtfChars filename(env, javaFilename); if (filename.c_str() == NULL) { return NULL; @@ -62,12 +61,15 @@ static jstring Runtime_nativeLoad(JNIEnv* env, jclass, jstring javaFilename, job } } - mirror::ClassLoader* classLoader = soa.Decode(javaLoader); std::string detail; - JavaVMExt* vm = Runtime::Current()->GetJavaVM(); - bool success = vm->LoadNativeLibrary(filename.c_str(), classLoader, detail); - if (success) { - return NULL; + { + ScopedObjectAccess soa(env); + mirror::ClassLoader* classLoader = soa.Decode(javaLoader); + JavaVMExt* vm = Runtime::Current()->GetJavaVM(); + bool success = vm->LoadNativeLibrary(filename.c_str(), classLoader, &detail); + if (success) { + return nullptr; + } } // Don't let a pending exception from JNI_OnLoad cause a CheckJNI issue with NewStringUTF. diff --git a/runtime/native/java_lang_String.cc b/runtime/native/java_lang_String.cc index 3e9c3f36faf..c401d502ffb 100644 --- a/runtime/native/java_lang_String.cc +++ b/runtime/native/java_lang_String.cc @@ -17,13 +17,14 @@ #include "common_throws.h" #include "jni_internal.h" #include "mirror/string.h" +#include "scoped_fast_native_object_access.h" #include "scoped_thread_state_change.h" #include "ScopedLocalRef.h" namespace art { static jint String_compareTo(JNIEnv* env, jobject javaThis, jobject javaRhs) { - ScopedObjectAccess soa(env); + ScopedFastNativeObjectAccess soa(env); if (UNLIKELY(javaRhs == NULL)) { ThrowNullPointerException(NULL, "rhs == null"); return -1; @@ -33,7 +34,7 @@ static jint String_compareTo(JNIEnv* env, jobject javaThis, jobject javaRhs) { } static jint String_fastIndexOf(JNIEnv* env, jobject java_this, jint ch, jint start) { - ScopedObjectAccess soa(env); + ScopedFastNativeObjectAccess soa(env); // This method does not handle supplementary characters. They're dealt with in managed code. DCHECK_LE(ch, 0xffff); @@ -42,16 +43,16 @@ static jint String_fastIndexOf(JNIEnv* env, jobject java_this, jint ch, jint sta } static jstring String_intern(JNIEnv* env, jobject javaThis) { - ScopedObjectAccess soa(env); + ScopedFastNativeObjectAccess soa(env); mirror::String* s = soa.Decode(javaThis); mirror::String* result = s->Intern(); return soa.AddLocalReference(result); } static JNINativeMethod gMethods[] = { - NATIVE_METHOD(String, compareTo, "(Ljava/lang/String;)I"), - NATIVE_METHOD(String, fastIndexOf, "(II)I"), - NATIVE_METHOD(String, intern, "()Ljava/lang/String;"), + NATIVE_METHOD(String, compareTo, "!(Ljava/lang/String;)I"), + NATIVE_METHOD(String, fastIndexOf, "!(II)I"), + NATIVE_METHOD(String, intern, "!()Ljava/lang/String;"), }; void register_java_lang_String(JNIEnv* env) { diff --git a/runtime/native/java_lang_System.cc b/runtime/native/java_lang_System.cc index 100f5a9b186..6674db2403e 100644 --- a/runtime/native/java_lang_System.cc +++ b/runtime/native/java_lang_System.cc @@ -22,7 +22,7 @@ #include "mirror/class-inl.h" #include "mirror/object-inl.h" #include "mirror/object_array-inl.h" -#include "scoped_thread_state_change.h" +#include "scoped_fast_native_object_access.h" /* * We make guarantees about the atomicity of accesses to primitive @@ -179,7 +179,7 @@ static void ThrowArrayStoreException_NotAnArray(const char* identifier, mirror:: } static void System_arraycopy(JNIEnv* env, jclass, jobject javaSrc, jint srcPos, jobject javaDst, jint dstPos, jint length) { - ScopedObjectAccess soa(env); + ScopedFastNativeObjectAccess soa(env); // Null pointer checks. if (UNLIKELY(javaSrc == NULL)) { @@ -317,7 +317,7 @@ static void System_arraycopy(JNIEnv* env, jclass, jobject javaSrc, jint srcPos, } static void System_arraycopyCharUnchecked(JNIEnv* env, jclass, jobject javaSrc, jint srcPos, jobject javaDst, jint dstPos, jint length) { - ScopedObjectAccess soa(env); + ScopedFastNativeObjectAccess soa(env); DCHECK(javaSrc != NULL); DCHECK(javaDst != NULL); mirror::Object* srcObject = soa.Decode(javaSrc); @@ -339,15 +339,15 @@ static void System_arraycopyCharUnchecked(JNIEnv* env, jclass, jobject javaSrc, } static jint System_identityHashCode(JNIEnv* env, jclass, jobject javaObject) { - ScopedObjectAccess soa(env); + ScopedFastNativeObjectAccess soa(env); mirror::Object* o = soa.Decode(javaObject); return static_cast(o->IdentityHashCode()); } static JNINativeMethod gMethods[] = { - NATIVE_METHOD(System, arraycopy, "(Ljava/lang/Object;ILjava/lang/Object;II)V"), - NATIVE_METHOD(System, arraycopyCharUnchecked, "([CI[CII)V"), - NATIVE_METHOD(System, identityHashCode, "(Ljava/lang/Object;)I"), + NATIVE_METHOD(System, arraycopy, "!(Ljava/lang/Object;ILjava/lang/Object;II)V"), + NATIVE_METHOD(System, arraycopyCharUnchecked, "!([CI[CII)V"), + NATIVE_METHOD(System, identityHashCode, "!(Ljava/lang/Object;)I"), }; void register_java_lang_System(JNIEnv* env) { diff --git a/runtime/native/java_lang_Thread.cc b/runtime/native/java_lang_Thread.cc index a9de0867851..5b34cfb224c 100644 --- a/runtime/native/java_lang_Thread.cc +++ b/runtime/native/java_lang_Thread.cc @@ -19,6 +19,7 @@ #include "jni_internal.h" #include "monitor.h" #include "mirror/object.h" +#include "scoped_fast_native_object_access.h" #include "scoped_thread_state_change.h" #include "ScopedUtfChars.h" #include "thread.h" @@ -27,7 +28,7 @@ namespace art { static jobject Thread_currentThread(JNIEnv* env, jclass) { - ScopedObjectAccess soa(env); + ScopedFastNativeObjectAccess soa(env); return soa.AddLocalReference(soa.Self()->GetPeer()); } @@ -150,7 +151,7 @@ static void Thread_nativeSetPriority(JNIEnv* env, jobject java_thread, jint new_ } static void Thread_sleep(JNIEnv* env, jclass, jobject java_lock, jlong ms, jint ns) { - ScopedObjectAccess soa(env); + ScopedFastNativeObjectAccess soa(env); mirror::Object* lock = soa.Decode(java_lock); Monitor::Wait(Thread::Current(), lock, ms, ns, true, kSleeping); } @@ -166,7 +167,7 @@ static void Thread_yield(JNIEnv*, jobject) { } static JNINativeMethod gMethods[] = { - NATIVE_METHOD(Thread, currentThread, "()Ljava/lang/Thread;"), + NATIVE_METHOD(Thread, currentThread, "!()Ljava/lang/Thread;"), NATIVE_METHOD(Thread, interrupted, "()Z"), NATIVE_METHOD(Thread, isInterrupted, "()Z"), NATIVE_METHOD(Thread, nativeCreate, "(Ljava/lang/Thread;JZ)V"), @@ -175,7 +176,7 @@ static JNINativeMethod gMethods[] = { NATIVE_METHOD(Thread, nativeInterrupt, "()V"), NATIVE_METHOD(Thread, nativeSetName, "(Ljava/lang/String;)V"), NATIVE_METHOD(Thread, nativeSetPriority, "(I)V"), - NATIVE_METHOD(Thread, sleep, "(Ljava/lang/Object;JI)V"), + NATIVE_METHOD(Thread, sleep, "!(Ljava/lang/Object;JI)V"), NATIVE_METHOD(Thread, yield, "()V"), }; diff --git a/runtime/native/java_lang_reflect_Array.cc b/runtime/native/java_lang_reflect_Array.cc index 45ec0ad5a25..a2d6b180267 100644 --- a/runtime/native/java_lang_reflect_Array.cc +++ b/runtime/native/java_lang_reflect_Array.cc @@ -21,13 +21,13 @@ #include "mirror/class-inl.h" #include "mirror/object-inl.h" #include "object_utils.h" -#include "scoped_thread_state_change.h" +#include "scoped_fast_native_object_access.h" #include "sirt_ref.h" namespace art { static jobject Array_createMultiArray(JNIEnv* env, jclass, jclass javaElementClass, jobject javaDimArray) { - ScopedObjectAccess soa(env); + ScopedFastNativeObjectAccess soa(env); DCHECK(javaElementClass != NULL); mirror::Class* element_class = soa.Decode(javaElementClass); DCHECK(element_class->IsClass()); @@ -41,7 +41,7 @@ static jobject Array_createMultiArray(JNIEnv* env, jclass, jclass javaElementCla } static jobject Array_createObjectArray(JNIEnv* env, jclass, jclass javaElementClass, jint length) { - ScopedObjectAccess soa(env); + ScopedFastNativeObjectAccess soa(env); DCHECK(javaElementClass != NULL); mirror::Class* element_class = soa.Decode(javaElementClass); if (UNLIKELY(length < 0)) { @@ -63,8 +63,8 @@ static jobject Array_createObjectArray(JNIEnv* env, jclass, jclass javaElementCl } static JNINativeMethod gMethods[] = { - NATIVE_METHOD(Array, createMultiArray, "(Ljava/lang/Class;[I)Ljava/lang/Object;"), - NATIVE_METHOD(Array, createObjectArray, "(Ljava/lang/Class;I)Ljava/lang/Object;"), + NATIVE_METHOD(Array, createMultiArray, "!(Ljava/lang/Class;[I)Ljava/lang/Object;"), + NATIVE_METHOD(Array, createObjectArray, "!(Ljava/lang/Class;I)Ljava/lang/Object;"), }; void register_java_lang_reflect_Array(JNIEnv* env) { diff --git a/runtime/native/java_lang_reflect_Constructor.cc b/runtime/native/java_lang_reflect_Constructor.cc index 85556ac16e5..aa72755c9da 100644 --- a/runtime/native/java_lang_reflect_Constructor.cc +++ b/runtime/native/java_lang_reflect_Constructor.cc @@ -35,6 +35,7 @@ namespace art { * with an interface, array, or primitive class. */ static jobject Constructor_newInstance(JNIEnv* env, jobject javaMethod, jobjectArray javaArgs) { + // TODO: ScopedFastNativeObjectAccess ScopedObjectAccess soa(env); jobject art_method = soa.Env()->GetObjectField( javaMethod, WellKnownClasses::java_lang_reflect_AbstractMethod_artMethod); @@ -68,7 +69,7 @@ static jobject Constructor_newInstance(JNIEnv* env, jobject javaMethod, jobjectA } static JNINativeMethod gMethods[] = { - NATIVE_METHOD(Constructor, newInstance, "([Ljava/lang/Object;)Ljava/lang/Object;"), + NATIVE_METHOD(Constructor, newInstance, "!([Ljava/lang/Object;)Ljava/lang/Object;"), }; void register_java_lang_reflect_Constructor(JNIEnv* env) { diff --git a/runtime/native/java_lang_reflect_Field.cc b/runtime/native/java_lang_reflect_Field.cc index 00f89b65eaa..4d69a688ac8 100644 --- a/runtime/native/java_lang_reflect_Field.cc +++ b/runtime/native/java_lang_reflect_Field.cc @@ -23,12 +23,12 @@ #include "mirror/class-inl.h" #include "object_utils.h" #include "reflection.h" -#include "scoped_thread_state_change.h" +#include "scoped_fast_native_object_access.h" namespace art { -static bool GetFieldValue(const ScopedObjectAccess& soa, mirror::Object* o, mirror::ArtField* f, - JValue& value, bool allow_references) +static bool GetFieldValue(const ScopedFastNativeObjectAccess& soa, mirror::Object* o, + mirror::ArtField* f, JValue& value, bool allow_references) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { DCHECK_EQ(value.GetJ(), 0LL); if (!Runtime::Current()->GetClassLinker()->EnsureInitialized(f->GetDeclaringClass(), @@ -77,8 +77,8 @@ static bool GetFieldValue(const ScopedObjectAccess& soa, mirror::Object* o, mirr return false; } -static bool CheckReceiver(const ScopedObjectAccess& soa, jobject j_rcvr, mirror::ArtField* f, - mirror::Object*& class_or_rcvr) +static bool CheckReceiver(const ScopedFastNativeObjectAccess& soa, jobject j_rcvr, + mirror::ArtField* f, mirror::Object*& class_or_rcvr) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { if (f->IsStatic()) { class_or_rcvr = f->GetDeclaringClass(); @@ -94,7 +94,7 @@ static bool CheckReceiver(const ScopedObjectAccess& soa, jobject j_rcvr, mirror: } static jobject Field_get(JNIEnv* env, jobject javaField, jobject javaObj) { - ScopedObjectAccess soa(env); + ScopedFastNativeObjectAccess soa(env); mirror::ArtField* f = soa.DecodeField(env->FromReflectedField(javaField)); mirror::Object* o = NULL; if (!CheckReceiver(soa, javaObj, f, o)) { @@ -112,7 +112,7 @@ static jobject Field_get(JNIEnv* env, jobject javaField, jobject javaObj) { static JValue GetPrimitiveField(JNIEnv* env, jobject javaField, jobject javaObj, char dst_descriptor) { - ScopedObjectAccess soa(env); + ScopedFastNativeObjectAccess soa(env); mirror::ArtField* f = soa.DecodeField(env->FromReflectedField(javaField)); mirror::Object* o = NULL; if (!CheckReceiver(soa, javaObj, f, o)) { @@ -221,7 +221,7 @@ static void SetFieldValue(mirror::Object* o, mirror::ArtField* f, const JValue& } static void Field_set(JNIEnv* env, jobject javaField, jobject javaObj, jobject javaValue) { - ScopedObjectAccess soa(env); + ScopedFastNativeObjectAccess soa(env); mirror::ArtField* f = soa.DecodeField(env->FromReflectedField(javaField)); // Unbox the value, if necessary. @@ -242,7 +242,7 @@ static void Field_set(JNIEnv* env, jobject javaField, jobject javaObj, jobject j static void SetPrimitiveField(JNIEnv* env, jobject javaField, jobject javaObj, char src_descriptor, const JValue& new_value) { - ScopedObjectAccess soa(env); + ScopedFastNativeObjectAccess soa(env); mirror::ArtField* f = soa.DecodeField(env->FromReflectedField(javaField)); mirror::Object* o = NULL; if (!CheckReceiver(soa, javaObj, f, o)) { @@ -316,24 +316,24 @@ static void Field_setShort(JNIEnv* env, jobject javaField, jobject javaObj, jsho } static JNINativeMethod gMethods[] = { - NATIVE_METHOD(Field, get, "(Ljava/lang/Object;)Ljava/lang/Object;"), - NATIVE_METHOD(Field, getBoolean, "(Ljava/lang/Object;)Z"), - NATIVE_METHOD(Field, getByte, "(Ljava/lang/Object;)B"), - NATIVE_METHOD(Field, getChar, "(Ljava/lang/Object;)C"), - NATIVE_METHOD(Field, getDouble, "(Ljava/lang/Object;)D"), - NATIVE_METHOD(Field, getFloat, "(Ljava/lang/Object;)F"), - NATIVE_METHOD(Field, getInt, "(Ljava/lang/Object;)I"), - NATIVE_METHOD(Field, getLong, "(Ljava/lang/Object;)J"), - NATIVE_METHOD(Field, getShort, "(Ljava/lang/Object;)S"), - NATIVE_METHOD(Field, set, "(Ljava/lang/Object;Ljava/lang/Object;)V"), - NATIVE_METHOD(Field, setBoolean, "(Ljava/lang/Object;Z)V"), - NATIVE_METHOD(Field, setByte, "(Ljava/lang/Object;B)V"), - NATIVE_METHOD(Field, setChar, "(Ljava/lang/Object;C)V"), - NATIVE_METHOD(Field, setDouble, "(Ljava/lang/Object;D)V"), - NATIVE_METHOD(Field, setFloat, "(Ljava/lang/Object;F)V"), - NATIVE_METHOD(Field, setInt, "(Ljava/lang/Object;I)V"), - NATIVE_METHOD(Field, setLong, "(Ljava/lang/Object;J)V"), - NATIVE_METHOD(Field, setShort, "(Ljava/lang/Object;S)V"), + NATIVE_METHOD(Field, get, "!(Ljava/lang/Object;)Ljava/lang/Object;"), + NATIVE_METHOD(Field, getBoolean, "!(Ljava/lang/Object;)Z"), + NATIVE_METHOD(Field, getByte, "!(Ljava/lang/Object;)B"), + NATIVE_METHOD(Field, getChar, "!(Ljava/lang/Object;)C"), + NATIVE_METHOD(Field, getDouble, "!(Ljava/lang/Object;)D"), + NATIVE_METHOD(Field, getFloat, "!(Ljava/lang/Object;)F"), + NATIVE_METHOD(Field, getInt, "!(Ljava/lang/Object;)I"), + NATIVE_METHOD(Field, getLong, "!(Ljava/lang/Object;)J"), + NATIVE_METHOD(Field, getShort, "!(Ljava/lang/Object;)S"), + NATIVE_METHOD(Field, set, "!(Ljava/lang/Object;Ljava/lang/Object;)V"), + NATIVE_METHOD(Field, setBoolean, "!(Ljava/lang/Object;Z)V"), + NATIVE_METHOD(Field, setByte, "!(Ljava/lang/Object;B)V"), + NATIVE_METHOD(Field, setChar, "!(Ljava/lang/Object;C)V"), + NATIVE_METHOD(Field, setDouble, "!(Ljava/lang/Object;D)V"), + NATIVE_METHOD(Field, setFloat, "!(Ljava/lang/Object;F)V"), + NATIVE_METHOD(Field, setInt, "!(Ljava/lang/Object;I)V"), + NATIVE_METHOD(Field, setLong, "!(Ljava/lang/Object;J)V"), + NATIVE_METHOD(Field, setShort, "!(Ljava/lang/Object;S)V"), }; void register_java_lang_reflect_Field(JNIEnv* env) { diff --git a/runtime/native/org_apache_harmony_dalvik_ddmc_DdmServer.cc b/runtime/native/org_apache_harmony_dalvik_ddmc_DdmServer.cc index d7cd18dc9c9..163ae20628a 100644 --- a/runtime/native/org_apache_harmony_dalvik_ddmc_DdmServer.cc +++ b/runtime/native/org_apache_harmony_dalvik_ddmc_DdmServer.cc @@ -16,21 +16,21 @@ #include "base/logging.h" #include "debugger.h" -#include "scoped_thread_state_change.h" +#include "scoped_fast_native_object_access.h" #include "ScopedPrimitiveArray.h" namespace art { static void DdmServer_nativeSendChunk(JNIEnv* env, jclass, jint type, jbyteArray javaData, jint offset, jint length) { - ScopedObjectAccess soa(env); + ScopedFastNativeObjectAccess soa(env); ScopedByteArrayRO data(env, javaData); DCHECK_LE(offset + length, static_cast(data.size())); Dbg::DdmSendChunk(type, length, reinterpret_cast(&data[offset])); } static JNINativeMethod gMethods[] = { - NATIVE_METHOD(DdmServer, nativeSendChunk, "(I[BII)V"), + NATIVE_METHOD(DdmServer, nativeSendChunk, "!(I[BII)V"), }; void register_org_apache_harmony_dalvik_ddmc_DdmServer(JNIEnv* env) { diff --git a/runtime/native/sun_misc_Unsafe.cc b/runtime/native/sun_misc_Unsafe.cc index eece81a9e82..2c6d2810b1e 100644 --- a/runtime/native/sun_misc_Unsafe.cc +++ b/runtime/native/sun_misc_Unsafe.cc @@ -19,12 +19,12 @@ #include "jni_internal.h" #include "mirror/object.h" #include "mirror/object-inl.h" -#include "scoped_thread_state_change.h" +#include "scoped_fast_native_object_access.h" namespace art { static jboolean Unsafe_compareAndSwapInt(JNIEnv* env, jobject, jobject javaObj, jlong offset, jint expectedValue, jint newValue) { - ScopedObjectAccess soa(env); + ScopedFastNativeObjectAccess soa(env); mirror::Object* obj = soa.Decode(javaObj); byte* raw_addr = reinterpret_cast(obj) + offset; volatile int32_t* address = reinterpret_cast(raw_addr); @@ -34,7 +34,7 @@ static jboolean Unsafe_compareAndSwapInt(JNIEnv* env, jobject, jobject javaObj, } static jboolean Unsafe_compareAndSwapLong(JNIEnv* env, jobject, jobject javaObj, jlong offset, jlong expectedValue, jlong newValue) { - ScopedObjectAccess soa(env); + ScopedFastNativeObjectAccess soa(env); mirror::Object* obj = soa.Decode(javaObj); byte* raw_addr = reinterpret_cast(obj) + offset; volatile int64_t* address = reinterpret_cast(raw_addr); @@ -44,7 +44,7 @@ static jboolean Unsafe_compareAndSwapLong(JNIEnv* env, jobject, jobject javaObj, } static jboolean Unsafe_compareAndSwapObject(JNIEnv* env, jobject, jobject javaObj, jlong offset, jobject javaExpectedValue, jobject javaNewValue) { - ScopedObjectAccess soa(env); + ScopedFastNativeObjectAccess soa(env); mirror::Object* obj = soa.Decode(javaObj); mirror::Object* expectedValue = soa.Decode(javaExpectedValue); mirror::Object* newValue = soa.Decode(javaNewValue); @@ -60,97 +60,97 @@ static jboolean Unsafe_compareAndSwapObject(JNIEnv* env, jobject, jobject javaOb } static jint Unsafe_getInt(JNIEnv* env, jobject, jobject javaObj, jlong offset) { - ScopedObjectAccess soa(env); + ScopedFastNativeObjectAccess soa(env); mirror::Object* obj = soa.Decode(javaObj); return obj->GetField32(MemberOffset(offset), false); } static jint Unsafe_getIntVolatile(JNIEnv* env, jobject, jobject javaObj, jlong offset) { - ScopedObjectAccess soa(env); + ScopedFastNativeObjectAccess soa(env); mirror::Object* obj = soa.Decode(javaObj); return obj->GetField32(MemberOffset(offset), true); } static void Unsafe_putInt(JNIEnv* env, jobject, jobject javaObj, jlong offset, jint newValue) { - ScopedObjectAccess soa(env); + ScopedFastNativeObjectAccess soa(env); mirror::Object* obj = soa.Decode(javaObj); obj->SetField32(MemberOffset(offset), newValue, false); } static void Unsafe_putIntVolatile(JNIEnv* env, jobject, jobject javaObj, jlong offset, jint newValue) { - ScopedObjectAccess soa(env); + ScopedFastNativeObjectAccess soa(env); mirror::Object* obj = soa.Decode(javaObj); obj->SetField32(MemberOffset(offset), newValue, true); } static void Unsafe_putOrderedInt(JNIEnv* env, jobject, jobject javaObj, jlong offset, jint newValue) { - ScopedObjectAccess soa(env); + ScopedFastNativeObjectAccess soa(env); mirror::Object* obj = soa.Decode(javaObj); ANDROID_MEMBAR_STORE(); obj->SetField32(MemberOffset(offset), newValue, false); } static jlong Unsafe_getLong(JNIEnv* env, jobject, jobject javaObj, jlong offset) { - ScopedObjectAccess soa(env); + ScopedFastNativeObjectAccess soa(env); mirror::Object* obj = soa.Decode(javaObj); return obj->GetField64(MemberOffset(offset), false); } static jlong Unsafe_getLongVolatile(JNIEnv* env, jobject, jobject javaObj, jlong offset) { - ScopedObjectAccess soa(env); + ScopedFastNativeObjectAccess soa(env); mirror::Object* obj = soa.Decode(javaObj); return obj->GetField64(MemberOffset(offset), true); } static void Unsafe_putLong(JNIEnv* env, jobject, jobject javaObj, jlong offset, jlong newValue) { - ScopedObjectAccess soa(env); + ScopedFastNativeObjectAccess soa(env); mirror::Object* obj = soa.Decode(javaObj); obj->SetField64(MemberOffset(offset), newValue, false); } static void Unsafe_putLongVolatile(JNIEnv* env, jobject, jobject javaObj, jlong offset, jlong newValue) { - ScopedObjectAccess soa(env); + ScopedFastNativeObjectAccess soa(env); mirror::Object* obj = soa.Decode(javaObj); obj->SetField64(MemberOffset(offset), newValue, true); } static void Unsafe_putOrderedLong(JNIEnv* env, jobject, jobject javaObj, jlong offset, jlong newValue) { - ScopedObjectAccess soa(env); + ScopedFastNativeObjectAccess soa(env); mirror::Object* obj = soa.Decode(javaObj); ANDROID_MEMBAR_STORE(); obj->SetField64(MemberOffset(offset), newValue, false); } static jobject Unsafe_getObjectVolatile(JNIEnv* env, jobject, jobject javaObj, jlong offset) { - ScopedObjectAccess soa(env); + ScopedFastNativeObjectAccess soa(env); mirror::Object* obj = soa.Decode(javaObj); mirror::Object* value = obj->GetFieldObject(MemberOffset(offset), true); return soa.AddLocalReference(value); } static jobject Unsafe_getObject(JNIEnv* env, jobject, jobject javaObj, jlong offset) { - ScopedObjectAccess soa(env); + ScopedFastNativeObjectAccess soa(env); mirror::Object* obj = soa.Decode(javaObj); mirror::Object* value = obj->GetFieldObject(MemberOffset(offset), false); return soa.AddLocalReference(value); } static void Unsafe_putObject(JNIEnv* env, jobject, jobject javaObj, jlong offset, jobject javaNewValue) { - ScopedObjectAccess soa(env); + ScopedFastNativeObjectAccess soa(env); mirror::Object* obj = soa.Decode(javaObj); mirror::Object* newValue = soa.Decode(javaNewValue); obj->SetFieldObject(MemberOffset(offset), newValue, false); } static void Unsafe_putObjectVolatile(JNIEnv* env, jobject, jobject javaObj, jlong offset, jobject javaNewValue) { - ScopedObjectAccess soa(env); + ScopedFastNativeObjectAccess soa(env); mirror::Object* obj = soa.Decode(javaObj); mirror::Object* newValue = soa.Decode(javaNewValue); obj->SetFieldObject(MemberOffset(offset), newValue, true); } static void Unsafe_putOrderedObject(JNIEnv* env, jobject, jobject javaObj, jlong offset, jobject javaNewValue) { - ScopedObjectAccess soa(env); + ScopedFastNativeObjectAccess soa(env); mirror::Object* obj = soa.Decode(javaObj); mirror::Object* newValue = soa.Decode(javaNewValue); ANDROID_MEMBAR_STORE(); @@ -158,24 +158,24 @@ static void Unsafe_putOrderedObject(JNIEnv* env, jobject, jobject javaObj, jlong } static JNINativeMethod gMethods[] = { - NATIVE_METHOD(Unsafe, compareAndSwapInt, "(Ljava/lang/Object;JII)Z"), - NATIVE_METHOD(Unsafe, compareAndSwapLong, "(Ljava/lang/Object;JJJ)Z"), - NATIVE_METHOD(Unsafe, compareAndSwapObject, "(Ljava/lang/Object;JLjava/lang/Object;Ljava/lang/Object;)Z"), - NATIVE_METHOD(Unsafe, getIntVolatile, "(Ljava/lang/Object;J)I"), - NATIVE_METHOD(Unsafe, putIntVolatile, "(Ljava/lang/Object;JI)V"), - NATIVE_METHOD(Unsafe, getLongVolatile, "(Ljava/lang/Object;J)J"), - NATIVE_METHOD(Unsafe, putLongVolatile, "(Ljava/lang/Object;JJ)V"), - NATIVE_METHOD(Unsafe, getObjectVolatile, "(Ljava/lang/Object;J)Ljava/lang/Object;"), - NATIVE_METHOD(Unsafe, putObjectVolatile, "(Ljava/lang/Object;JLjava/lang/Object;)V"), - NATIVE_METHOD(Unsafe, getInt, "(Ljava/lang/Object;J)I"), - NATIVE_METHOD(Unsafe, putInt, "(Ljava/lang/Object;JI)V"), - NATIVE_METHOD(Unsafe, putOrderedInt, "(Ljava/lang/Object;JI)V"), - NATIVE_METHOD(Unsafe, getLong, "(Ljava/lang/Object;J)J"), - NATIVE_METHOD(Unsafe, putLong, "(Ljava/lang/Object;JJ)V"), - NATIVE_METHOD(Unsafe, putOrderedLong, "(Ljava/lang/Object;JJ)V"), - NATIVE_METHOD(Unsafe, getObject, "(Ljava/lang/Object;J)Ljava/lang/Object;"), - NATIVE_METHOD(Unsafe, putObject, "(Ljava/lang/Object;JLjava/lang/Object;)V"), - NATIVE_METHOD(Unsafe, putOrderedObject, "(Ljava/lang/Object;JLjava/lang/Object;)V"), + NATIVE_METHOD(Unsafe, compareAndSwapInt, "!(Ljava/lang/Object;JII)Z"), + NATIVE_METHOD(Unsafe, compareAndSwapLong, "!(Ljava/lang/Object;JJJ)Z"), + NATIVE_METHOD(Unsafe, compareAndSwapObject, "!(Ljava/lang/Object;JLjava/lang/Object;Ljava/lang/Object;)Z"), + NATIVE_METHOD(Unsafe, getIntVolatile, "!(Ljava/lang/Object;J)I"), + NATIVE_METHOD(Unsafe, putIntVolatile, "!(Ljava/lang/Object;JI)V"), + NATIVE_METHOD(Unsafe, getLongVolatile, "!(Ljava/lang/Object;J)J"), + NATIVE_METHOD(Unsafe, putLongVolatile, "!(Ljava/lang/Object;JJ)V"), + NATIVE_METHOD(Unsafe, getObjectVolatile, "!(Ljava/lang/Object;J)Ljava/lang/Object;"), + NATIVE_METHOD(Unsafe, putObjectVolatile, "!(Ljava/lang/Object;JLjava/lang/Object;)V"), + NATIVE_METHOD(Unsafe, getInt, "!(Ljava/lang/Object;J)I"), + NATIVE_METHOD(Unsafe, putInt, "!(Ljava/lang/Object;JI)V"), + NATIVE_METHOD(Unsafe, putOrderedInt, "!(Ljava/lang/Object;JI)V"), + NATIVE_METHOD(Unsafe, getLong, "!(Ljava/lang/Object;J)J"), + NATIVE_METHOD(Unsafe, putLong, "!(Ljava/lang/Object;JJ)V"), + NATIVE_METHOD(Unsafe, putOrderedLong, "!(Ljava/lang/Object;JJ)V"), + NATIVE_METHOD(Unsafe, getObject, "!(Ljava/lang/Object;J)Ljava/lang/Object;"), + NATIVE_METHOD(Unsafe, putObject, "!(Ljava/lang/Object;JLjava/lang/Object;)V"), + NATIVE_METHOD(Unsafe, putOrderedObject, "!(Ljava/lang/Object;JLjava/lang/Object;)V"), }; void register_sun_misc_Unsafe(JNIEnv* env) { diff --git a/runtime/runtime.cc b/runtime/runtime.cc index bdedef4cab6..f46b7943873 100644 --- a/runtime/runtime.cc +++ b/runtime/runtime.cc @@ -977,7 +977,7 @@ void Runtime::InitNativeMethods() { std::string mapped_name(StringPrintf(OS_SHARED_LIB_FORMAT_STR, "javacore")); std::string reason; self->TransitionFromSuspendedToRunnable(); - if (!instance_->java_vm_->LoadNativeLibrary(mapped_name, NULL, reason)) { + if (!instance_->java_vm_->LoadNativeLibrary(mapped_name, NULL, &reason)) { LOG(FATAL) << "LoadNativeLibrary failed for \"" << mapped_name << "\": " << reason; } self->TransitionFromRunnableToSuspended(kNative); diff --git a/runtime/scoped_thread_state_change.h b/runtime/scoped_thread_state_change.h index d3f3a88d661..c39cdb26792 100644 --- a/runtime/scoped_thread_state_change.h +++ b/runtime/scoped_thread_state_change.h @@ -18,7 +18,6 @@ #define ART_RUNTIME_SCOPED_THREAD_STATE_CHANGE_H_ #include "base/casts.h" -#include "jni_internal.h" #include "thread-inl.h" namespace art { @@ -122,14 +121,14 @@ class ScopedObjectAccessUnchecked : public ScopedThreadStateChange { explicit ScopedObjectAccessUnchecked(JNIEnv* env) LOCKS_EXCLUDED(Locks::thread_suspend_count_lock_) ALWAYS_INLINE : ScopedThreadStateChange(ThreadForEnv(env), kRunnable), - env_(reinterpret_cast(env)), vm_(env_->vm) { + env_(down_cast(env)), vm_(env_->vm) { self_->VerifyStack(); } explicit ScopedObjectAccessUnchecked(Thread* self) LOCKS_EXCLUDED(Locks::thread_suspend_count_lock_) : ScopedThreadStateChange(self, kRunnable), - env_(reinterpret_cast(self->GetJniEnv())), + env_(down_cast(self->GetJniEnv())), vm_(env_ != NULL ? env_->vm : NULL) { self_->VerifyStack(); } @@ -137,7 +136,7 @@ class ScopedObjectAccessUnchecked : public ScopedThreadStateChange { // Used when we want a scoped JNI thread state but have no thread/JNIEnv. Consequently doesn't // change into Runnable or acquire a share on the mutator_lock_. explicit ScopedObjectAccessUnchecked(JavaVM* vm) - : ScopedThreadStateChange(), env_(NULL), vm_(reinterpret_cast(vm)) {} + : ScopedThreadStateChange(), env_(NULL), vm_(down_cast(vm)) {} // Here purely to force inlining. ~ScopedObjectAccessUnchecked() ALWAYS_INLINE { @@ -162,6 +161,7 @@ class ScopedObjectAccessUnchecked : public ScopedThreadStateChange { */ template T AddLocalReference(mirror::Object* obj) const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + Locks::mutator_lock_->AssertSharedHeld(Self()); DCHECK_EQ(thread_state_, kRunnable); // Don't work with raw objects in non-runnable states. if (obj == NULL) { return NULL; @@ -245,11 +245,6 @@ class ScopedObjectAccessUnchecked : public ScopedThreadStateChange { } private: - static Thread* ThreadForEnv(JNIEnv* env) { - JNIEnvExt* full_env(reinterpret_cast(env)); - return full_env->self; - } - // The full JNIEnv. JNIEnvExt* const env_; // The full JavaVM. diff --git a/runtime/thread-inl.h b/runtime/thread-inl.h index 7d28785f584..84496072b11 100644 --- a/runtime/thread-inl.h +++ b/runtime/thread-inl.h @@ -21,11 +21,19 @@ #include +#include "base/casts.h" #include "base/mutex-inl.h" #include "cutils/atomic-inline.h" +#include "jni_internal.h" namespace art { +// Quickly access the current thread from a JNIEnv. +static inline Thread* ThreadForEnv(JNIEnv* env) { + JNIEnvExt* full_env(down_cast(env)); + return full_env->self; +} + inline Thread* Thread::Current() { // We rely on Thread::Current returning NULL for a detached thread, so it's not obvious // that we can replace this with a direct %fs access on x86. From c765c9c7c1a64854ac026a71b7ae8b2f4790f21e Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Sun, 20 Oct 2013 18:56:20 -0700 Subject: [PATCH 0090/2402] Build fix. Add file missed in commit 1eb512d33f94d1dd7ea38263307ba0f7a0dfa653. Change-Id: Id3e13d8f302502400543cd47e8452fcfdd98ea4d --- .../native/scoped_fast_native_object_access.h | 103 ++++++++++++++++++ 1 file changed, 103 insertions(+) create mode 100644 runtime/native/scoped_fast_native_object_access.h diff --git a/runtime/native/scoped_fast_native_object_access.h b/runtime/native/scoped_fast_native_object_access.h new file mode 100644 index 00000000000..d941ec31f00 --- /dev/null +++ b/runtime/native/scoped_fast_native_object_access.h @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ART_RUNTIME_NATIVE_SCOPED_FAST_NATIVE_OBJECT_ACCESS_H_ +#define ART_RUNTIME_NATIVE_SCOPED_FAST_NATIVE_OBJECT_ACCESS_H_ + +#include "base/casts.h" +#include "jni_internal.h" +#include "thread-inl.h" +#include "mirror/art_method.h" + +namespace art { + +// Variant of ScopedObjectAccess that does no runnable transitions. Should only be used by "fast" +// JNI methods. +class ScopedFastNativeObjectAccess { + public: + explicit ScopedFastNativeObjectAccess(JNIEnv* env) + LOCKS_EXCLUDED(Locks::thread_suspend_count_lock_) + SHARED_LOCK_FUNCTION(Locks::mutator_lock_) ALWAYS_INLINE + : env_(down_cast(env)), self_(ThreadForEnv(env)) { + Locks::mutator_lock_->AssertSharedHeld(Self()); + DCHECK((*Self()->GetManagedStack()->GetTopQuickFrame())->IsFastNative()); + // Don't work with raw objects in non-runnable states. + DCHECK_EQ(Self()->GetState(), kRunnable); + } + + ~ScopedFastNativeObjectAccess() UNLOCK_FUNCTION(Locks::mutator_lock_) ALWAYS_INLINE { + } + + Thread* Self() const { + return self_; + } + + JNIEnvExt* Env() const { + return env_; + } + + template + T Decode(jobject obj) const + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + Locks::mutator_lock_->AssertSharedHeld(Self()); + // Don't work with raw objects in non-runnable states. + DCHECK_EQ(Self()->GetState(), kRunnable); + return down_cast(Self()->DecodeJObject(obj)); + } + + mirror::ArtField* DecodeField(jfieldID fid) const + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + Locks::mutator_lock_->AssertSharedHeld(Self()); + // Don't work with raw objects in non-runnable states. + DCHECK_EQ(Self()->GetState(), kRunnable); +#ifdef MOVING_GARBAGE_COLLECTOR + // TODO: we should make these unique weak globals if Field instances can ever move. + UNIMPLEMENTED(WARNING); +#endif + return reinterpret_cast(fid); + } + + /* + * Variant of ScopedObjectAccessUnched::AddLocalReference that without JNI work arounds + * or check JNI that should be being used by fast native methods. + */ + template + T AddLocalReference(mirror::Object* obj) const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + Locks::mutator_lock_->AssertSharedHeld(Self()); + // Don't work with raw objects in non-runnable states. + DCHECK_EQ(Self()->GetState(), kRunnable); + if (obj == NULL) { + return NULL; + } + + DCHECK_NE((reinterpret_cast(obj) & 0xffff0000), 0xebad0000); + + IndirectReferenceTable& locals = Env()->locals; + + uint32_t cookie = Env()->local_ref_cookie; + IndirectRef ref = locals.Add(cookie, obj); + + return reinterpret_cast(ref); + } + + private: + JNIEnvExt* const env_; + Thread* const self_; +}; + +} // namespace art + +#endif // ART_RUNTIME_NATIVE_SCOPED_FAST_NATIVE_OBJECT_ACCESS_H_ From e810452722ac83b294d1f7aa80bdd88e547d5af0 Mon Sep 17 00:00:00 2001 From: Brian Carlstrom Date: Tue, 15 Oct 2013 21:56:36 -0700 Subject: [PATCH 0091/2402] Preload DexCaches Bug: 11045348 Change-Id: I6f9c0d11613b6b4933a04ae23dbf4bc7879cea65 --- runtime/class_linker.cc | 24 +- runtime/class_linker.h | 4 - runtime/dex_file.h | 2 + runtime/native/dalvik_system_VMRuntime.cc | 316 +++++++++++++++++++++- runtime/runtime.h | 1 + 5 files changed, 329 insertions(+), 18 deletions(-) diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index f07282040ce..db4cc00a8bf 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -4002,11 +4002,11 @@ mirror::Class* ClassLinker::ResolveType(const DexFile& dex_file, } mirror::ArtMethod* ClassLinker::ResolveMethod(const DexFile& dex_file, - uint32_t method_idx, - mirror::DexCache* dex_cache, - mirror::ClassLoader* class_loader, - const mirror::ArtMethod* referrer, - InvokeType type) { + uint32_t method_idx, + mirror::DexCache* dex_cache, + mirror::ClassLoader* class_loader, + const mirror::ArtMethod* referrer, + InvokeType type) { DCHECK(dex_cache != NULL); // Check for hit in the dex cache. mirror::ArtMethod* resolved = dex_cache->GetResolvedMethod(method_idx); @@ -4149,10 +4149,10 @@ mirror::ArtMethod* ClassLinker::ResolveMethod(const DexFile& dex_file, } mirror::ArtField* ClassLinker::ResolveField(const DexFile& dex_file, - uint32_t field_idx, - mirror::DexCache* dex_cache, - mirror::ClassLoader* class_loader, - bool is_static) { + uint32_t field_idx, + mirror::DexCache* dex_cache, + mirror::ClassLoader* class_loader, + bool is_static) { DCHECK(dex_cache != NULL); mirror::ArtField* resolved = dex_cache->GetResolvedField(field_idx); if (resolved != NULL) { @@ -4189,9 +4189,9 @@ mirror::ArtField* ClassLinker::ResolveField(const DexFile& dex_file, } mirror::ArtField* ClassLinker::ResolveFieldJLS(const DexFile& dex_file, - uint32_t field_idx, - mirror::DexCache* dex_cache, - mirror::ClassLoader* class_loader) { + uint32_t field_idx, + mirror::DexCache* dex_cache, + mirror::ClassLoader* class_loader) { DCHECK(dex_cache != NULL); mirror::ArtField* resolved = dex_cache->GetResolvedField(field_idx); if (resolved != NULL) { diff --git a/runtime/class_linker.h b/runtime/class_linker.h index baeec66f79f..11ba78b36a0 100644 --- a/runtime/class_linker.h +++ b/runtime/class_linker.h @@ -346,10 +346,6 @@ class ClassLinker { return quick_resolution_trampoline_; } - InternTable* GetInternTable() const { - return intern_table_; - } - // Attempts to insert a class into a class table. Returns NULL if // the class was inserted, otherwise returns an existing class with // the same descriptor and ClassLoader. diff --git a/runtime/dex_file.h b/runtime/dex_file.h index 346154cc894..cef4ce4b878 100644 --- a/runtime/dex_file.h +++ b/runtime/dex_file.h @@ -33,6 +33,8 @@ namespace art { +// TODO: remove dependencies on mirror classes, primarily by moving +// EncodedStaticFieldValueIterator to its own file. namespace mirror { class ArtField; class ArtMethod; diff --git a/runtime/native/dalvik_system_VMRuntime.cc b/runtime/native/dalvik_system_VMRuntime.cc index 5fc8bd5a48c..cb7828b9a4d 100644 --- a/runtime/native/dalvik_system_VMRuntime.cc +++ b/runtime/native/dalvik_system_VMRuntime.cc @@ -20,12 +20,15 @@ #include "common_throws.h" #include "debugger.h" #include "dex_file-inl.h" +#include "gc/accounting/card_table-inl.h" #include "gc/allocator/dlmalloc.h" #include "gc/heap.h" #include "gc/space/dlmalloc_space.h" +#include "intern_table.h" #include "jni_internal.h" +#include "mirror/art_method-inl.h" #include "mirror/class-inl.h" -#include "mirror/object.h" +#include "mirror/dex_cache-inl.h" #include "mirror/object-inl.h" #include "object_utils.h" #include "scoped_thread_state_change.h" @@ -49,7 +52,10 @@ static void VMRuntime_startJitCompilation(JNIEnv*, jobject) { static void VMRuntime_disableJitCompilation(JNIEnv*, jobject) { } -static jobject VMRuntime_newNonMovableArray(JNIEnv* env, jobject, jclass javaElementClass, jint length) { +static jobject VMRuntime_newNonMovableArray(JNIEnv* env, + jobject, + jclass javaElementClass, + jint length) { ScopedObjectAccess soa(env); #ifdef MOVING_GARBAGE_COLLECTOR // TODO: right now, we don't have a copying collector, so there's no need @@ -196,6 +202,311 @@ static void VMRuntime_concurrentGC(JNIEnv* env, jobject) { Runtime::Current()->GetHeap()->ConcurrentGC(self); } +typedef std::map StringTable; + +static void PreloadDexCachesStringsVisitor(const mirror::Object* root, void* arg) { + StringTable& table = *reinterpret_cast(arg); + mirror::String* string = const_cast(root)->AsString(); + // LOG(INFO) << "VMRuntime.preloadDexCaches interned=" << string->ToModifiedUtf8(); + table[string->ToModifiedUtf8()] = string; +} + +// Based on ClassLinker::ResolveString. +static void PreloadDexCachesResolveString(mirror::DexCache* dex_cache, + uint32_t string_idx, + StringTable& strings) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + mirror::String* string = dex_cache->GetResolvedString(string_idx); + if (string != NULL) { + return; + } + const DexFile* dex_file = dex_cache->GetDexFile(); + uint32_t utf16Size; + const char* utf8 = dex_file->StringDataAndLengthByIdx(string_idx, &utf16Size); + string = strings[utf8]; + if (string == NULL) { + return; + } + // LOG(INFO) << "VMRuntime.preloadDexCaches resolved string=" << utf8; + dex_cache->SetResolvedString(string_idx, string); +} + +// Based on ClassLinker::ResolveType. +static void PreloadDexCachesResolveType(mirror::DexCache* dex_cache, uint32_t type_idx) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + mirror::Class* klass = dex_cache->GetResolvedType(type_idx); + if (klass != NULL) { + return; + } + const DexFile* dex_file = dex_cache->GetDexFile(); + const char* class_name = dex_file->StringByTypeIdx(type_idx); + ClassLinker* linker = Runtime::Current()->GetClassLinker(); + if (class_name[1] == '\0') { + klass = linker->FindPrimitiveClass(class_name[0]); + } else { + klass = linker->LookupClass(class_name, NULL); + } + if (klass == NULL) { + return; + } + // LOG(INFO) << "VMRuntime.preloadDexCaches resolved klass=" << class_name; + dex_cache->SetResolvedType(type_idx, klass); + // Skip uninitialized classes because filled static storage entry implies it is initialized. + if (!klass->IsInitialized()) { + // LOG(INFO) << "VMRuntime.preloadDexCaches uninitialized klass=" << class_name; + return; + } + // LOG(INFO) << "VMRuntime.preloadDexCaches static storage klass=" << class_name; + dex_cache->GetInitializedStaticStorage()->Set(type_idx, klass); +} + +// Based on ClassLinker::ResolveField. +static void PreloadDexCachesResolveField(mirror::DexCache* dex_cache, + uint32_t field_idx, + bool is_static) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + mirror::ArtField* field = dex_cache->GetResolvedField(field_idx); + if (field != NULL) { + return; + } + const DexFile* dex_file = dex_cache->GetDexFile(); + const DexFile::FieldId& field_id = dex_file->GetFieldId(field_idx); + mirror::Class* klass = dex_cache->GetResolvedType(field_id.class_idx_); + if (klass == NULL) { + return; + } + if (is_static) { + field = klass->FindStaticField(dex_cache, field_idx); + } else { + field = klass->FindInstanceField(dex_cache, field_idx); + } + if (field == NULL) { + return; + } + // LOG(INFO) << "VMRuntime.preloadDexCaches resolved field " << PrettyField(field); + dex_cache->SetResolvedField(field_idx, field); +} + +// Based on ClassLinker::ResolveMethod. +static void PreloadDexCachesResolveMethod(mirror::DexCache* dex_cache, + uint32_t method_idx, + InvokeType invoke_type) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + mirror::ArtMethod* method = dex_cache->GetResolvedMethod(method_idx); + if (method != NULL) { + return; + } + const DexFile* dex_file = dex_cache->GetDexFile(); + const DexFile::MethodId& method_id = dex_file->GetMethodId(method_idx); + mirror::Class* klass = dex_cache->GetResolvedType(method_id.class_idx_); + if (klass == NULL) { + return; + } + switch (invoke_type) { + case kDirect: + case kStatic: + method = klass->FindDirectMethod(dex_cache, method_idx); + break; + case kInterface: + method = klass->FindInterfaceMethod(dex_cache, method_idx); + break; + case kSuper: + case kVirtual: + method = klass->FindVirtualMethod(dex_cache, method_idx); + break; + default: + LOG(FATAL) << "Unreachable - invocation type: " << invoke_type; + } + if (method == NULL) { + return; + } + // LOG(INFO) << "VMRuntime.preloadDexCaches resolved method " << PrettyMethod(method); + dex_cache->SetResolvedMethod(method_idx, method); +} + +struct DexCacheStats { + uint32_t num_strings; + uint32_t num_types; + uint32_t num_fields; + uint32_t num_methods; + uint32_t num_static_storage; + DexCacheStats() : num_strings(0), + num_types(0), + num_fields(0), + num_methods(0), + num_static_storage(0) {} +}; + +static const bool kPreloadDexCachesEnabled = true; + +// Disabled because it takes a long time (extra half second) but +// gives almost no benefit in terms of saving private dirty pages. +static const bool kPreloadDexCachesStrings = false; + +static const bool kPreloadDexCachesTypes = true; +static const bool kPreloadDexCachesFieldsAndMethods = true; + +static const bool kPreloadDexCachesCollectStats = true; + +static void PreloadDexCachesStatsTotal(DexCacheStats* total) { + if (!kPreloadDexCachesCollectStats) { + return; + } + + ClassLinker* linker = Runtime::Current()->GetClassLinker(); + const std::vector& boot_class_path = linker->GetBootClassPath(); + for (size_t i = 0; i< boot_class_path.size(); i++) { + const DexFile* dex_file = boot_class_path[i]; + CHECK(dex_file != NULL); + total->num_strings += dex_file->NumStringIds(); + total->num_fields += dex_file->NumFieldIds(); + total->num_methods += dex_file->NumMethodIds(); + total->num_types += dex_file->NumTypeIds(); + total->num_static_storage += dex_file->NumTypeIds(); + } +} + +static void PreloadDexCachesStatsFilled(DexCacheStats* filled) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + if (!kPreloadDexCachesCollectStats) { + return; + } + ClassLinker* linker = Runtime::Current()->GetClassLinker(); + const std::vector& boot_class_path = linker->GetBootClassPath(); + for (size_t i = 0; i< boot_class_path.size(); i++) { + const DexFile* dex_file = boot_class_path[i]; + CHECK(dex_file != NULL); + mirror::DexCache* dex_cache = linker->FindDexCache(*dex_file); + for (size_t i = 0; i < dex_cache->NumStrings(); i++) { + mirror::String* string = dex_cache->GetResolvedString(i); + if (string != NULL) { + filled->num_strings++; + } + } + for (size_t i = 0; i < dex_cache->NumResolvedTypes(); i++) { + mirror::Class* klass = dex_cache->GetResolvedType(i); + if (klass != NULL) { + filled->num_types++; + } + } + for (size_t i = 0; i < dex_cache->NumResolvedFields(); i++) { + mirror::ArtField* field = dex_cache->GetResolvedField(i); + if (field != NULL) { + filled->num_fields++; + } + } + for (size_t i = 0; i < dex_cache->NumResolvedMethods(); i++) { + mirror::ArtMethod* method = dex_cache->GetResolvedMethod(i); + if (method != NULL) { + filled->num_methods++; + } + } + for (size_t i = 0; i < dex_cache->NumInitializedStaticStorage(); i++) { + mirror::StaticStorageBase* klass = dex_cache->GetInitializedStaticStorage()->Get(i); + if (klass != NULL) { + filled->num_static_storage++; + } + } + } +} + +// TODO: http://b/11309598 This code was ported over based on the +// Dalvik version. However, ART has similar code in other places such +// as the CompilerDriver. This code could probably be refactored to +// serve both uses. +static void VMRuntime_preloadDexCaches(JNIEnv* env, jobject) { + if (!kPreloadDexCachesEnabled) { + return; + } + + ScopedObjectAccess soa(env); + + DexCacheStats total; + DexCacheStats before; + if (kPreloadDexCachesCollectStats) { + LOG(INFO) << "VMRuntime.preloadDexCaches starting"; + PreloadDexCachesStatsTotal(&total); + PreloadDexCachesStatsFilled(&before); + } + + Runtime* runtime = Runtime::Current(); + ClassLinker* linker = runtime->GetClassLinker(); + + // We use a std::map to avoid heap allocating StringObjects to lookup in gDvm.literalStrings + StringTable strings; + if (kPreloadDexCachesStrings) { + runtime->GetInternTable()->VisitRoots(PreloadDexCachesStringsVisitor, &strings, false, false); + } + + const std::vector& boot_class_path = linker->GetBootClassPath(); + for (size_t i = 0; i< boot_class_path.size(); i++) { + const DexFile* dex_file = boot_class_path[i]; + CHECK(dex_file != NULL); + mirror::DexCache* dex_cache = linker->FindDexCache(*dex_file); + + if (kPreloadDexCachesStrings) { + for (size_t i = 0; i < dex_cache->NumStrings(); i++) { + PreloadDexCachesResolveString(dex_cache, i, strings); + } + } + + if (kPreloadDexCachesTypes) { + for (size_t i = 0; i < dex_cache->NumResolvedTypes(); i++) { + PreloadDexCachesResolveType(dex_cache, i); + } + } + + if (kPreloadDexCachesFieldsAndMethods) { + for (size_t class_def_index = 0; + class_def_index < dex_file->NumClassDefs(); + class_def_index++) { + const DexFile::ClassDef& class_def = dex_file->GetClassDef(class_def_index); + const byte* class_data = dex_file->GetClassData(class_def); + if (class_data == NULL) { + continue; + } + ClassDataItemIterator it(*dex_file, class_data); + for (; it.HasNextStaticField(); it.Next()) { + uint32_t field_idx = it.GetMemberIndex(); + PreloadDexCachesResolveField(dex_cache, field_idx, true); + } + for (; it.HasNextInstanceField(); it.Next()) { + uint32_t field_idx = it.GetMemberIndex(); + PreloadDexCachesResolveField(dex_cache, field_idx, false); + } + for (; it.HasNextDirectMethod(); it.Next()) { + uint32_t method_idx = it.GetMemberIndex(); + InvokeType invoke_type = it.GetMethodInvokeType(class_def); + PreloadDexCachesResolveMethod(dex_cache, method_idx, invoke_type); + } + for (; it.HasNextVirtualMethod(); it.Next()) { + uint32_t method_idx = it.GetMemberIndex(); + InvokeType invoke_type = it.GetMethodInvokeType(class_def); + PreloadDexCachesResolveMethod(dex_cache, method_idx, invoke_type); + } + } + } + } + + if (kPreloadDexCachesCollectStats) { + DexCacheStats after; + PreloadDexCachesStatsFilled(&after); + LOG(INFO) << StringPrintf("VMRuntime.preloadDexCaches strings total=%d before=%d after=%d", + total.num_strings, before.num_strings, after.num_strings); + LOG(INFO) << StringPrintf("VMRuntime.preloadDexCaches types total=%d before=%d after=%d", + total.num_types, before.num_types, after.num_types); + LOG(INFO) << StringPrintf("VMRuntime.preloadDexCaches fields total=%d before=%d after=%d", + total.num_fields, before.num_fields, after.num_fields); + LOG(INFO) << StringPrintf("VMRuntime.preloadDexCaches methods total=%d before=%d after=%d", + total.num_methods, before.num_methods, after.num_methods); + LOG(INFO) << StringPrintf("VMRuntime.preloadDexCaches storage total=%d before=%d after=%d", + total.num_static_storage, + before.num_static_storage, + after.num_static_storage); + LOG(INFO) << StringPrintf("VMRuntime.preloadDexCaches finished"); + } +} + static JNINativeMethod gMethods[] = { NATIVE_METHOD(VMRuntime, addressOf, "(Ljava/lang/Object;)J"), NATIVE_METHOD(VMRuntime, bootClassPath, "()Ljava/lang/String;"), @@ -215,6 +526,7 @@ static JNINativeMethod gMethods[] = { NATIVE_METHOD(VMRuntime, trimHeap, "()V"), NATIVE_METHOD(VMRuntime, vmVersion, "()Ljava/lang/String;"), NATIVE_METHOD(VMRuntime, vmLibrary, "()Ljava/lang/String;"), + NATIVE_METHOD(VMRuntime, preloadDexCaches, "()V"), }; void register_dalvik_system_VMRuntime(JNIEnv* env) { diff --git a/runtime/runtime.h b/runtime/runtime.h index bc5c8b00a83..7c2847949a6 100644 --- a/runtime/runtime.h +++ b/runtime/runtime.h @@ -279,6 +279,7 @@ class Runtime { } InternTable* GetInternTable() const { + DCHECK(intern_table_ != NULL); return intern_table_; } From 57e6d8a99058e5c74d5244b68a5f4d53526fa108 Mon Sep 17 00:00:00 2001 From: Brian Carlstrom Date: Sun, 20 Oct 2013 22:53:26 -0700 Subject: [PATCH 0092/2402] Tracking change to InternTable::VisitRoots Change-Id: I260e8f07ae4c4dde048a1ad4bb6208624af965e9 --- runtime/native/dalvik_system_VMRuntime.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/runtime/native/dalvik_system_VMRuntime.cc b/runtime/native/dalvik_system_VMRuntime.cc index 09204a39cfd..486328cbbaf 100644 --- a/runtime/native/dalvik_system_VMRuntime.cc +++ b/runtime/native/dalvik_system_VMRuntime.cc @@ -203,11 +203,12 @@ static void VMRuntime_concurrentGC(JNIEnv* env, jobject) { typedef std::map StringTable; -static void PreloadDexCachesStringsVisitor(const mirror::Object* root, void* arg) { +static mirror::Object* PreloadDexCachesStringsVisitor(mirror::Object* root, void* arg) { StringTable& table = *reinterpret_cast(arg); mirror::String* string = const_cast(root)->AsString(); // LOG(INFO) << "VMRuntime.preloadDexCaches interned=" << string->ToModifiedUtf8(); table[string->ToModifiedUtf8()] = string; + return root; } // Based on ClassLinker::ResolveString. From 0d82948094d9a198e01aa95f64012bdedd5b6fc9 Mon Sep 17 00:00:00 2001 From: buzbee Date: Fri, 11 Oct 2013 15:24:55 -0700 Subject: [PATCH 0093/2402] 64-bit prep Preparation for 64-bit roll. o Eliminated storing pointers in 32-bit int slots in LIR. o General size reductions of common structures to reduce impact of doubled pointer sizes: - BasicBlock struct was 72 bytes, now is 48. - MIR struct was 72 bytes, now is 64. - RegLocation was 12 bytes, now is 8. o Generally replaced uses of BasicBlock* pointers with 16-bit Ids. o Replaced several doubly-linked lists with singly-linked to save one stored pointer per node. o We had quite a few uses of uintptr_t's that were a holdover from the JIT (which used pointers to mapped dex & actual code cache addresses rather than trace-relative offsets). Replaced those with uint32_t's. o Clean up handling of embedded data for switch tables and array data. o Miscellaneous cleanup. I anticipate one or two additional CLs to reduce the size of MIR and LIR structs. Change-Id: I58e426d3f8e5efe64c1146b2823453da99451230 --- compiler/dex/arena_bit_vector.h | 14 +- compiler/dex/compiler_enums.h | 1 + compiler/dex/compiler_ir.h | 10 +- compiler/dex/dataflow_iterator-inl.h | 8 +- compiler/dex/dataflow_iterator.h | 10 +- compiler/dex/mir_analysis.cc | 11 +- compiler/dex/mir_dataflow.cc | 16 +- compiler/dex/mir_graph.cc | 220 ++++++++++++----------- compiler/dex/mir_graph.h | 144 ++++++++------- compiler/dex/mir_optimization.cc | 84 ++++----- compiler/dex/portable/mir_to_gbc.cc | 60 +++---- compiler/dex/quick/arm/assemble_arm.cc | 84 +++++---- compiler/dex/quick/arm/call_arm.cc | 20 +-- compiler/dex/quick/arm/codegen_arm.h | 7 +- compiler/dex/quick/arm/fp_arm.cc | 2 +- compiler/dex/quick/arm/int_arm.cc | 25 +-- compiler/dex/quick/arm/target_arm.cc | 4 +- compiler/dex/quick/arm/utility_arm.cc | 48 ++--- compiler/dex/quick/codegen_util.cc | 90 ++++++---- compiler/dex/quick/gen_common.cc | 18 +- compiler/dex/quick/gen_invoke.cc | 10 +- compiler/dex/quick/mips/assemble_mips.cc | 42 ++--- compiler/dex/quick/mips/call_mips.cc | 23 ++- compiler/dex/quick/mips/codegen_mips.h | 2 +- compiler/dex/quick/mips/utility_mips.cc | 2 +- compiler/dex/quick/mir_to_lir-inl.h | 2 +- compiler/dex/quick/mir_to_lir.cc | 32 ++-- compiler/dex/quick/mir_to_lir.h | 123 ++++++++----- compiler/dex/quick/ralloc_util.cc | 11 +- compiler/dex/quick/x86/assemble_x86.cc | 24 +-- compiler/dex/quick/x86/call_x86.cc | 21 ++- compiler/dex/quick/x86/codegen_x86.h | 8 +- compiler/dex/quick/x86/fp_x86.cc | 4 +- compiler/dex/quick/x86/int_x86.cc | 2 +- compiler/dex/quick/x86/target_x86.cc | 4 +- compiler/dex/ssa_transformation.cc | 141 +++++++-------- 36 files changed, 695 insertions(+), 632 deletions(-) diff --git a/compiler/dex/arena_bit_vector.h b/compiler/dex/arena_bit_vector.h index 24a7ce96015..53e6eb65124 100644 --- a/compiler/dex/arena_bit_vector.h +++ b/compiler/dex/arena_bit_vector.h @@ -39,7 +39,7 @@ class ArenaBitVector { bit_size_(p_bits_->storage_size_ * sizeof(uint32_t) * 8) {} // Return the position of the next set bit. -1 means end-of-element reached. - int Next() { + int32_t Next() { // Did anything obviously change since we started? DCHECK_EQ(bit_size_, p_bits_->GetStorageSize() * sizeof(uint32_t) * 8); DCHECK_EQ(bit_storage_, p_bits_->GetRawStorage()); @@ -79,7 +79,7 @@ class ArenaBitVector { const uint32_t bit_size_; // Size of vector in bits. }; - ArenaBitVector(ArenaAllocator* arena, unsigned int start_bits, bool expandable, + ArenaBitVector(ArenaAllocator* arena, uint32_t start_bits, bool expandable, OatBitMapKind kind = kBitMapMisc); ~ArenaBitVector() {} @@ -88,13 +88,13 @@ class ArenaBitVector { } static void operator delete(void* p) {} // Nop. - void SetBit(unsigned int num); - void ClearBit(unsigned int num); + void SetBit(uint32_t num); + void ClearBit(uint32_t num); void MarkAllBits(bool set); void DebugBitVector(char* msg, int length); - bool IsBitSet(unsigned int num); + bool IsBitSet(uint32_t num); void ClearAllBits(); - void SetInitialBits(unsigned int num_bits); + void SetInitialBits(uint32_t num_bits); void Copy(ArenaBitVector* src) { memcpy(storage_, src->GetRawStorage(), sizeof(uint32_t) * storage_size_); } @@ -106,7 +106,7 @@ class ArenaBitVector { (expandable_ == src->IsExpandable()) && (memcmp(storage_, src->GetRawStorage(), storage_size_ * 4) == 0); } - int NumSetBits(); + int32_t NumSetBits(); uint32_t GetStorageSize() const { return storage_size_; } bool IsExpandable() const { return expandable_; } diff --git a/compiler/dex/compiler_enums.h b/compiler/dex/compiler_enums.h index 17b5bb5b197..05ca1b565e5 100644 --- a/compiler/dex/compiler_enums.h +++ b/compiler/dex/compiler_enums.h @@ -55,6 +55,7 @@ enum RegLocationType { }; enum BBType { + kNullBlock, kEntryBlock, kDalvikByteCode, kExitBlock, diff --git a/compiler/dex/compiler_ir.h b/compiler/dex/compiler_ir.h index 6607562b134..bdc31547cbc 100644 --- a/compiler/dex/compiler_ir.h +++ b/compiler/dex/compiler_ir.h @@ -90,14 +90,14 @@ struct CompilationUnit { InstructionSet instruction_set; // TODO: much of this info available elsewhere. Go to the original source? - int num_dalvik_registers; // method->registers_size. + uint16_t num_dalvik_registers; // method->registers_size. const uint16_t* insns; - int num_ins; - int num_outs; - int num_regs; // Unlike num_dalvik_registers, does not include ins. + uint16_t num_ins; + uint16_t num_outs; + uint16_t num_regs; // Unlike num_dalvik_registers, does not include ins. // TODO: may want to move this to MIRGraph. - int num_compiler_temps; + uint16_t num_compiler_temps; // If non-empty, apply optimizer/debug flags only to matching methods. std::string compiler_method_match; diff --git a/compiler/dex/dataflow_iterator-inl.h b/compiler/dex/dataflow_iterator-inl.h index 236c6f49401..74f36ddd810 100644 --- a/compiler/dex/dataflow_iterator-inl.h +++ b/compiler/dex/dataflow_iterator-inl.h @@ -25,7 +25,7 @@ namespace art { inline BasicBlock* DataflowIterator::ForwardSingleNext() { BasicBlock* res = NULL; if (idx_ < end_idx_) { - int bb_id = block_id_list_->Get(idx_++); + BasicBlockId bb_id = block_id_list_->Get(idx_++); res = mir_graph_->GetBasicBlock(bb_id); } return res; @@ -40,7 +40,7 @@ inline BasicBlock* DataflowIterator::ForwardRepeatNext(bool had_change) { changed_ = false; } if (idx_ < end_idx_) { - int bb_id = block_id_list_->Get(idx_++); + BasicBlockId bb_id = block_id_list_->Get(idx_++); res = mir_graph_->GetBasicBlock(bb_id); } return res; @@ -50,7 +50,7 @@ inline BasicBlock* DataflowIterator::ForwardRepeatNext(bool had_change) { inline BasicBlock* DataflowIterator::ReverseSingleNext() { BasicBlock* res = NULL; if (idx_ >= 0) { - int bb_id = block_id_list_->Get(idx_--); + BasicBlockId bb_id = block_id_list_->Get(idx_--); res = mir_graph_->GetBasicBlock(bb_id); } return res; @@ -65,7 +65,7 @@ inline BasicBlock* DataflowIterator::ReverseRepeatNext(bool had_change) { changed_ = false; } if (idx_ >= 0) { - int bb_id = block_id_list_->Get(idx_--); + BasicBlockId bb_id = block_id_list_->Get(idx_--); res = mir_graph_->GetBasicBlock(bb_id); } return res; diff --git a/compiler/dex/dataflow_iterator.h b/compiler/dex/dataflow_iterator.h index 1dab54ea728..26e36653bef 100644 --- a/compiler/dex/dataflow_iterator.h +++ b/compiler/dex/dataflow_iterator.h @@ -39,7 +39,7 @@ namespace art { virtual ~DataflowIterator() {} protected: - DataflowIterator(MIRGraph* mir_graph, int start_idx, int end_idx) + DataflowIterator(MIRGraph* mir_graph, int32_t start_idx, int32_t end_idx) : mir_graph_(mir_graph), start_idx_(start_idx), end_idx_(end_idx), @@ -53,10 +53,10 @@ namespace art { virtual BasicBlock* ReverseRepeatNext(bool had_change) ALWAYS_INLINE; MIRGraph* const mir_graph_; - const int start_idx_; - const int end_idx_; - GrowableArray* block_id_list_; - int idx_; + const int32_t start_idx_; + const int32_t end_idx_; + GrowableArray* block_id_list_; + int32_t idx_; bool changed_; }; // DataflowIterator diff --git a/compiler/dex/mir_analysis.cc b/compiler/dex/mir_analysis.cc index 8597172881d..89af06e085e 100644 --- a/compiler/dex/mir_analysis.cc +++ b/compiler/dex/mir_analysis.cc @@ -864,7 +864,7 @@ void MIRGraph::AnalyzeBlock(BasicBlock* bb, MethodStats* stats) { if (ending_bb->last_mir_insn != NULL) { uint32_t ending_flags = analysis_attributes_[ending_bb->last_mir_insn->dalvikInsn.opcode]; while ((ending_flags & AN_BRANCH) == 0) { - ending_bb = ending_bb->fall_through; + ending_bb = GetBasicBlock(ending_bb->fall_through); ending_flags = analysis_attributes_[ending_bb->last_mir_insn->dalvikInsn.opcode]; } } @@ -876,13 +876,14 @@ void MIRGraph::AnalyzeBlock(BasicBlock* bb, MethodStats* stats) { */ int loop_scale_factor = 1; // Simple for and while loops - if ((ending_bb->taken != NULL) && (ending_bb->fall_through == NULL)) { - if ((ending_bb->taken->taken == bb) || (ending_bb->taken->fall_through == bb)) { + if ((ending_bb->taken != NullBasicBlockId) && (ending_bb->fall_through == NullBasicBlockId)) { + if ((GetBasicBlock(ending_bb->taken)->taken == bb->id) || + (GetBasicBlock(ending_bb->taken)->fall_through == bb->id)) { loop_scale_factor = 25; } } // Simple do-while loop - if ((ending_bb->taken != NULL) && (ending_bb->taken == bb)) { + if ((ending_bb->taken != NullBasicBlockId) && (ending_bb->taken == bb->id)) { loop_scale_factor = 25; } @@ -922,7 +923,7 @@ void MIRGraph::AnalyzeBlock(BasicBlock* bb, MethodStats* stats) { if (tbb == ending_bb) { done = true; } else { - tbb = tbb->fall_through; + tbb = GetBasicBlock(tbb->fall_through); } } if (has_math && computational_block && (loop_scale_factor > 1)) { diff --git a/compiler/dex/mir_dataflow.cc b/compiler/dex/mir_dataflow.cc index 3d29908e9f2..9c8ce23ca56 100644 --- a/compiler/dex/mir_dataflow.cc +++ b/compiler/dex/mir_dataflow.cc @@ -1295,23 +1295,23 @@ void MIRGraph::MethodUseCount() { /* Verify if all the successor is connected with all the claimed predecessors */ bool MIRGraph::VerifyPredInfo(BasicBlock* bb) { - GrowableArray::Iterator iter(bb->predecessors); + GrowableArray::Iterator iter(bb->predecessors); while (true) { - BasicBlock *pred_bb = iter.Next(); + BasicBlock *pred_bb = GetBasicBlock(iter.Next()); if (!pred_bb) break; bool found = false; - if (pred_bb->taken == bb) { + if (pred_bb->taken == bb->id) { found = true; - } else if (pred_bb->fall_through == bb) { + } else if (pred_bb->fall_through == bb->id) { found = true; - } else if (pred_bb->successor_block_list.block_list_type != kNotUsed) { - GrowableArray::Iterator iterator(pred_bb->successor_block_list.blocks); + } else if (pred_bb->successor_block_list_type != kNotUsed) { + GrowableArray::Iterator iterator(pred_bb->successor_blocks); while (true) { SuccessorBlockInfo *successor_block_info = iterator.Next(); if (successor_block_info == NULL) break; - BasicBlock *succ_bb = successor_block_info->block; - if (succ_bb == bb) { + BasicBlockId succ_bb = successor_block_info->block; + if (succ_bb == bb->id) { found = true; break; } diff --git a/compiler/dex/mir_graph.cc b/compiler/dex/mir_graph.cc index fb306de0c13..cf758fc5dab 100644 --- a/compiler/dex/mir_graph.cc +++ b/compiler/dex/mir_graph.cc @@ -130,11 +130,14 @@ int MIRGraph::ParseInsn(const uint16_t* code_ptr, DecodedInstruction* decoded_in /* Split an existing block from the specified code offset into two */ -BasicBlock* MIRGraph::SplitBlock(unsigned int code_offset, +BasicBlock* MIRGraph::SplitBlock(DexOffset code_offset, BasicBlock* orig_block, BasicBlock** immed_pred_block_p) { + DCHECK_GT(code_offset, orig_block->start_offset); MIR* insn = orig_block->first_mir_insn; + MIR* prev = NULL; while (insn) { if (insn->offset == code_offset) break; + prev = insn; insn = insn->next; } if (insn == NULL) { @@ -156,39 +159,42 @@ BasicBlock* MIRGraph::SplitBlock(unsigned int code_offset, /* Handle the taken path */ bottom_block->taken = orig_block->taken; - if (bottom_block->taken) { - orig_block->taken = NULL; - bottom_block->taken->predecessors->Delete(orig_block); - bottom_block->taken->predecessors->Insert(bottom_block); + if (bottom_block->taken != NullBasicBlockId) { + orig_block->taken = NullBasicBlockId; + BasicBlock* bb_taken = GetBasicBlock(bottom_block->taken); + bb_taken->predecessors->Delete(orig_block->id); + bb_taken->predecessors->Insert(bottom_block->id); } /* Handle the fallthrough path */ bottom_block->fall_through = orig_block->fall_through; - orig_block->fall_through = bottom_block; - bottom_block->predecessors->Insert(orig_block); - if (bottom_block->fall_through) { - bottom_block->fall_through->predecessors->Delete(orig_block); - bottom_block->fall_through->predecessors->Insert(bottom_block); + orig_block->fall_through = bottom_block->id; + bottom_block->predecessors->Insert(orig_block->id); + if (bottom_block->fall_through != NullBasicBlockId) { + BasicBlock* bb_fall_through = GetBasicBlock(bottom_block->fall_through); + bb_fall_through->predecessors->Delete(orig_block->id); + bb_fall_through->predecessors->Insert(bottom_block->id); } /* Handle the successor list */ - if (orig_block->successor_block_list.block_list_type != kNotUsed) { - bottom_block->successor_block_list = orig_block->successor_block_list; - orig_block->successor_block_list.block_list_type = kNotUsed; - GrowableArray::Iterator iterator(bottom_block->successor_block_list.blocks); + if (orig_block->successor_block_list_type != kNotUsed) { + bottom_block->successor_block_list_type = orig_block->successor_block_list_type; + bottom_block->successor_blocks = orig_block->successor_blocks; + orig_block->successor_block_list_type = kNotUsed; + orig_block->successor_blocks = NULL; + GrowableArray::Iterator iterator(bottom_block->successor_blocks); while (true) { SuccessorBlockInfo *successor_block_info = iterator.Next(); if (successor_block_info == NULL) break; - BasicBlock *bb = successor_block_info->block; - bb->predecessors->Delete(orig_block); - bb->predecessors->Insert(bottom_block); + BasicBlock *bb = GetBasicBlock(successor_block_info->block); + bb->predecessors->Delete(orig_block->id); + bb->predecessors->Insert(bottom_block->id); } } - orig_block->last_mir_insn = insn->prev; + orig_block->last_mir_insn = prev; + prev->next = NULL; - insn->prev->next = NULL; - insn->prev = NULL; /* * Update the immediate predecessor block pointer so that outgoing edges * can be applied to the proper block. @@ -225,7 +231,7 @@ BasicBlock* MIRGraph::SplitBlock(unsigned int code_offset, * (by the caller) * Utilizes a map for fast lookup of the typical cases. */ -BasicBlock* MIRGraph::FindBlock(unsigned int code_offset, bool split, bool create, +BasicBlock* MIRGraph::FindBlock(DexOffset code_offset, bool split, bool create, BasicBlock** immed_pred_block_p) { if (code_offset >= cu_->code_item->insns_size_in_code_units_) { return NULL; @@ -261,7 +267,7 @@ BasicBlock* MIRGraph::FindBlock(unsigned int code_offset, bool split, bool creat /* Identify code range in try blocks and set up the empty catch blocks */ void MIRGraph::ProcessTryCatchBlocks() { int tries_size = current_code_item_->tries_size_; - int offset; + DexOffset offset; if (tries_size == 0) { return; @@ -270,8 +276,8 @@ void MIRGraph::ProcessTryCatchBlocks() { for (int i = 0; i < tries_size; i++) { const DexFile::TryItem* pTry = DexFile::GetTryItems(*current_code_item_, i); - int start_offset = pTry->start_addr_; - int end_offset = start_offset + pTry->insn_count_; + DexOffset start_offset = pTry->start_addr_; + DexOffset end_offset = start_offset + pTry->insn_count_; for (offset = start_offset; offset < end_offset; offset++) { try_block_addr_->SetBit(offset); } @@ -292,10 +298,10 @@ void MIRGraph::ProcessTryCatchBlocks() { } /* Process instructions with the kBranch flag */ -BasicBlock* MIRGraph::ProcessCanBranch(BasicBlock* cur_block, MIR* insn, int cur_offset, int width, - int flags, const uint16_t* code_ptr, +BasicBlock* MIRGraph::ProcessCanBranch(BasicBlock* cur_block, MIR* insn, DexOffset cur_offset, + int width, int flags, const uint16_t* code_ptr, const uint16_t* code_end) { - int target = cur_offset; + DexOffset target = cur_offset; switch (insn->dalvikInsn.opcode) { case Instruction::GOTO: case Instruction::GOTO_16: @@ -326,8 +332,8 @@ BasicBlock* MIRGraph::ProcessCanBranch(BasicBlock* cur_block, MIR* insn, int cur CountBranch(target); BasicBlock *taken_block = FindBlock(target, /* split */ true, /* create */ true, /* immed_pred_block_p */ &cur_block); - cur_block->taken = taken_block; - taken_block->predecessors->Insert(cur_block); + cur_block->taken = taken_block->id; + taken_block->predecessors->Insert(cur_block->id); /* Always terminate the current block for conditional branches */ if (flags & Instruction::kContinue) { @@ -349,8 +355,8 @@ BasicBlock* MIRGraph::ProcessCanBranch(BasicBlock* cur_block, MIR* insn, int cur true, /* immed_pred_block_p */ &cur_block); - cur_block->fall_through = fallthrough_block; - fallthrough_block->predecessors->Insert(cur_block); + cur_block->fall_through = fallthrough_block->id; + fallthrough_block->predecessors->Insert(cur_block->id); } else if (code_ptr < code_end) { FindBlock(cur_offset + width, /* split */ false, /* create */ true, /* immed_pred_block_p */ NULL); @@ -359,7 +365,7 @@ BasicBlock* MIRGraph::ProcessCanBranch(BasicBlock* cur_block, MIR* insn, int cur } /* Process instructions with the kSwitch flag */ -void MIRGraph::ProcessCanSwitch(BasicBlock* cur_block, MIR* insn, int cur_offset, int width, +void MIRGraph::ProcessCanSwitch(BasicBlock* cur_block, MIR* insn, DexOffset cur_offset, int width, int flags) { const uint16_t* switch_data = reinterpret_cast(GetCurrentInsns() + cur_offset + insn->dalvikInsn.vB); @@ -403,14 +409,13 @@ void MIRGraph::ProcessCanSwitch(BasicBlock* cur_block, MIR* insn, int cur_offset first_key = 0; // To make the compiler happy } - if (cur_block->successor_block_list.block_list_type != kNotUsed) { + if (cur_block->successor_block_list_type != kNotUsed) { LOG(FATAL) << "Successor block list already in use: " - << static_cast(cur_block->successor_block_list.block_list_type); + << static_cast(cur_block->successor_block_list_type); } - cur_block->successor_block_list.block_list_type = - (insn->dalvikInsn.opcode == Instruction::PACKED_SWITCH) ? - kPackedSwitch : kSparseSwitch; - cur_block->successor_block_list.blocks = + cur_block->successor_block_list_type = + (insn->dalvikInsn.opcode == Instruction::PACKED_SWITCH) ? kPackedSwitch : kSparseSwitch; + cur_block->successor_blocks = new (arena_) GrowableArray(arena_, size, kGrowableArraySuccessorBlocks); for (i = 0; i < size; i++) { @@ -419,24 +424,24 @@ void MIRGraph::ProcessCanSwitch(BasicBlock* cur_block, MIR* insn, int cur_offset SuccessorBlockInfo *successor_block_info = static_cast(arena_->Alloc(sizeof(SuccessorBlockInfo), ArenaAllocator::kAllocSuccessor)); - successor_block_info->block = case_block; + successor_block_info->block = case_block->id; successor_block_info->key = (insn->dalvikInsn.opcode == Instruction::PACKED_SWITCH) ? first_key + i : keyTable[i]; - cur_block->successor_block_list.blocks->Insert(successor_block_info); - case_block->predecessors->Insert(cur_block); + cur_block->successor_blocks->Insert(successor_block_info); + case_block->predecessors->Insert(cur_block->id); } /* Fall-through case */ BasicBlock* fallthrough_block = FindBlock(cur_offset + width, /* split */ false, /* create */ true, /* immed_pred_block_p */ NULL); - cur_block->fall_through = fallthrough_block; - fallthrough_block->predecessors->Insert(cur_block); + cur_block->fall_through = fallthrough_block->id; + fallthrough_block->predecessors->Insert(cur_block->id); } /* Process instructions with the kThrow flag */ -BasicBlock* MIRGraph::ProcessCanThrow(BasicBlock* cur_block, MIR* insn, int cur_offset, int width, - int flags, ArenaBitVector* try_block_addr, +BasicBlock* MIRGraph::ProcessCanThrow(BasicBlock* cur_block, MIR* insn, DexOffset cur_offset, + int width, int flags, ArenaBitVector* try_block_addr, const uint16_t* code_ptr, const uint16_t* code_end) { bool in_try_block = try_block_addr->IsBitSet(cur_offset); @@ -444,14 +449,14 @@ BasicBlock* MIRGraph::ProcessCanThrow(BasicBlock* cur_block, MIR* insn, int cur_ if (in_try_block) { CatchHandlerIterator iterator(*current_code_item_, cur_offset); - if (cur_block->successor_block_list.block_list_type != kNotUsed) { + if (cur_block->successor_block_list_type != kNotUsed) { LOG(INFO) << PrettyMethod(cu_->method_idx, *cu_->dex_file); LOG(FATAL) << "Successor block list already in use: " - << static_cast(cur_block->successor_block_list.block_list_type); + << static_cast(cur_block->successor_block_list_type); } - cur_block->successor_block_list.block_list_type = kCatch; - cur_block->successor_block_list.blocks = + cur_block->successor_block_list_type = kCatch; + cur_block->successor_blocks = new (arena_) GrowableArray(arena_, 2, kGrowableArraySuccessorBlocks); for (; iterator.HasNext(); iterator.Next()) { @@ -463,17 +468,17 @@ BasicBlock* MIRGraph::ProcessCanThrow(BasicBlock* cur_block, MIR* insn, int cur_ } SuccessorBlockInfo *successor_block_info = reinterpret_cast (arena_->Alloc(sizeof(SuccessorBlockInfo), ArenaAllocator::kAllocSuccessor)); - successor_block_info->block = catch_block; + successor_block_info->block = catch_block->id; successor_block_info->key = iterator.GetHandlerTypeIndex(); - cur_block->successor_block_list.blocks->Insert(successor_block_info); - catch_block->predecessors->Insert(cur_block); + cur_block->successor_blocks->Insert(successor_block_info); + catch_block->predecessors->Insert(cur_block->id); } } else { BasicBlock *eh_block = NewMemBB(kExceptionHandling, num_blocks_++); - cur_block->taken = eh_block; + cur_block->taken = eh_block->id; block_list_.Insert(eh_block); eh_block->start_offset = cur_offset; - eh_block->predecessors->Insert(cur_block); + eh_block->predecessors->Insert(cur_block->id); } if (insn->dalvikInsn.opcode == Instruction::THROW) { @@ -509,8 +514,8 @@ BasicBlock* MIRGraph::ProcessCanThrow(BasicBlock* cur_block, MIR* insn, int cur_ BasicBlock *new_block = NewMemBB(kDalvikByteCode, num_blocks_++); block_list_.Insert(new_block); new_block->start_offset = insn->offset; - cur_block->fall_through = new_block; - new_block->predecessors->Insert(cur_block); + cur_block->fall_through = new_block->id; + new_block->predecessors->Insert(cur_block->id); MIR* new_insn = static_cast(arena_->Alloc(sizeof(MIR), ArenaAllocator::kAllocMIR)); *new_insn = *insn; insn->dalvikInsn.opcode = @@ -551,9 +556,14 @@ void MIRGraph::InlineMethod(const DexFile::CodeItem* code_item, uint32_t access_ DCHECK(entry_block_ == NULL); DCHECK(exit_block_ == NULL); DCHECK_EQ(num_blocks_, 0); + // Use id 0 to represent a null block. + BasicBlock* null_block = NewMemBB(kNullBlock, num_blocks_++); + DCHECK_EQ(null_block->id, NullBasicBlockId); + null_block->hidden = true; + block_list_.Insert(null_block); entry_block_ = NewMemBB(kEntryBlock, num_blocks_++); - exit_block_ = NewMemBB(kExitBlock, num_blocks_++); block_list_.Insert(entry_block_); + exit_block_ = NewMemBB(kExitBlock, num_blocks_++); block_list_.Insert(exit_block_); // TODO: deprecate all "cu->" fields; move what's left to wherever CompilationUnit is allocated. cu_->dex_file = &dex_file; @@ -578,12 +588,12 @@ void MIRGraph::InlineMethod(const DexFile::CodeItem* code_item, uint32_t access_ /* Current block to record parsed instructions */ BasicBlock *cur_block = NewMemBB(kDalvikByteCode, num_blocks_++); - DCHECK_EQ(current_offset_, 0); + DCHECK_EQ(current_offset_, 0U); cur_block->start_offset = current_offset_; block_list_.Insert(cur_block); - // FIXME: this needs to insert at the insert point rather than entry block. - entry_block_->fall_through = cur_block; - cur_block->predecessors->Insert(entry_block_); + // TODO: for inlining support, insert at the insert point rather than entry block. + entry_block_->fall_through = cur_block->id; + cur_block->predecessors->Insert(entry_block_->id); /* Identify code range in try blocks and set up the empty catch blocks */ ProcessTryCatchBlocks(); @@ -648,8 +658,8 @@ void MIRGraph::InlineMethod(const DexFile::CodeItem* code_item, uint32_t access_ // It is a simple nop - treat normally. AppendMIR(cur_block, insn); } else { - DCHECK(cur_block->fall_through == NULL); - DCHECK(cur_block->taken == NULL); + DCHECK(cur_block->fall_through == NullBasicBlockId); + DCHECK(cur_block->taken == NullBasicBlockId); // Unreachable instruction, mark for no continuation. flags &= ~Instruction::kContinue; } @@ -667,8 +677,8 @@ void MIRGraph::InlineMethod(const DexFile::CodeItem* code_item, uint32_t access_ width, flags, code_ptr, code_end); } else if (flags & Instruction::kReturn) { cur_block->terminated_by_return = true; - cur_block->fall_through = exit_block_; - exit_block_->predecessors->Insert(cur_block); + cur_block->fall_through = exit_block_->id; + exit_block_->predecessors->Insert(cur_block->id); /* * Terminate the current block if there are instructions * afterwards. @@ -697,13 +707,13 @@ void MIRGraph::InlineMethod(const DexFile::CodeItem* code_item, uint32_t access_ * instruction is not an unconditional branch, connect them through * the fall-through link. */ - DCHECK(cur_block->fall_through == NULL || - cur_block->fall_through == next_block || - cur_block->fall_through == exit_block_); + DCHECK(cur_block->fall_through == NullBasicBlockId || + GetBasicBlock(cur_block->fall_through) == next_block || + GetBasicBlock(cur_block->fall_through) == exit_block_); - if ((cur_block->fall_through == NULL) && (flags & Instruction::kContinue)) { - cur_block->fall_through = next_block; - next_block->predecessors->Insert(cur_block); + if ((cur_block->fall_through == NullBasicBlockId) && (flags & Instruction::kContinue)) { + cur_block->fall_through = next_block->id; + next_block->predecessors->Insert(cur_block->id); } cur_block = next_block; } @@ -735,7 +745,7 @@ void MIRGraph::DumpCFG(const char* dir_prefix, bool all_blocks) { std::string fname(PrettyMethod(cu_->method_idx, *cu_->dex_file)); ReplaceSpecialChars(fname); fname = StringPrintf("%s%s%x.dot", dir_prefix, fname.c_str(), - GetEntryBlock()->fall_through->start_offset); + GetBasicBlock(GetEntryBlock()->fall_through)->start_offset); file = fopen(fname.c_str(), "w"); if (file == NULL) { return; @@ -782,31 +792,30 @@ void MIRGraph::DumpCFG(const char* dir_prefix, bool all_blocks) { char block_name1[BLOCK_NAME_LEN], block_name2[BLOCK_NAME_LEN]; - if (bb->taken) { + if (bb->taken != NullBasicBlockId) { GetBlockName(bb, block_name1); - GetBlockName(bb->taken, block_name2); + GetBlockName(GetBasicBlock(bb->taken), block_name2); fprintf(file, " %s:s -> %s:n [style=dotted]\n", block_name1, block_name2); } - if (bb->fall_through) { + if (bb->fall_through != NullBasicBlockId) { GetBlockName(bb, block_name1); - GetBlockName(bb->fall_through, block_name2); + GetBlockName(GetBasicBlock(bb->fall_through), block_name2); fprintf(file, " %s:s -> %s:n\n", block_name1, block_name2); } - if (bb->successor_block_list.block_list_type != kNotUsed) { + if (bb->successor_block_list_type != kNotUsed) { fprintf(file, " succ%04x_%d [shape=%s,label = \"{ \\\n", bb->start_offset, bb->id, - (bb->successor_block_list.block_list_type == kCatch) ? - "Mrecord" : "record"); - GrowableArray::Iterator iterator(bb->successor_block_list.blocks); + (bb->successor_block_list_type == kCatch) ? "Mrecord" : "record"); + GrowableArray::Iterator iterator(bb->successor_blocks); SuccessorBlockInfo *successor_block_info = iterator.Next(); int succ_id = 0; while (true) { if (successor_block_info == NULL) break; - BasicBlock *dest_block = successor_block_info->block; + BasicBlock *dest_block = GetBasicBlock(successor_block_info->block); SuccessorBlockInfo *next_successor_block_info = iterator.Next(); fprintf(file, " { %04x: %04x\\l}%s\\\n", @@ -823,16 +832,16 @@ void MIRGraph::DumpCFG(const char* dir_prefix, bool all_blocks) { fprintf(file, " %s:s -> succ%04x_%d:n [style=dashed]\n", block_name1, bb->start_offset, bb->id); - if (bb->successor_block_list.block_list_type == kPackedSwitch || - bb->successor_block_list.block_list_type == kSparseSwitch) { - GrowableArray::Iterator iter(bb->successor_block_list.blocks); + if (bb->successor_block_list_type == kPackedSwitch || + bb->successor_block_list_type == kSparseSwitch) { + GrowableArray::Iterator iter(bb->successor_blocks); succ_id = 0; while (true) { SuccessorBlockInfo *successor_block_info = iter.Next(); if (successor_block_info == NULL) break; - BasicBlock *dest_block = successor_block_info->block; + BasicBlock* dest_block = GetBasicBlock(successor_block_info->block); GetBlockName(dest_block, block_name2); fprintf(file, " succ%04x_%d:f%d:e -> %s:n\n", bb->start_offset, @@ -848,7 +857,7 @@ void MIRGraph::DumpCFG(const char* dir_prefix, bool all_blocks) { fprintf(file, " cfg%s [label=\"%s\", shape=none];\n", block_name1, block_name1); if (bb->i_dom) { - GetBlockName(bb->i_dom, block_name2); + GetBlockName(GetBasicBlock(bb->i_dom), block_name2); fprintf(file, " cfg%s:s -> cfg%s:n\n\n", block_name2, block_name1); } } @@ -862,10 +871,9 @@ void MIRGraph::AppendMIR(BasicBlock* bb, MIR* mir) { if (bb->first_mir_insn == NULL) { DCHECK(bb->last_mir_insn == NULL); bb->last_mir_insn = bb->first_mir_insn = mir; - mir->prev = mir->next = NULL; + mir->next = NULL; } else { bb->last_mir_insn->next = mir; - mir->prev = bb->last_mir_insn; mir->next = NULL; bb->last_mir_insn = mir; } @@ -876,25 +884,19 @@ void MIRGraph::PrependMIR(BasicBlock* bb, MIR* mir) { if (bb->first_mir_insn == NULL) { DCHECK(bb->last_mir_insn == NULL); bb->last_mir_insn = bb->first_mir_insn = mir; - mir->prev = mir->next = NULL; + mir->next = NULL; } else { - bb->first_mir_insn->prev = mir; mir->next = bb->first_mir_insn; - mir->prev = NULL; bb->first_mir_insn = mir; } } /* Insert a MIR instruction after the specified MIR */ void MIRGraph::InsertMIRAfter(BasicBlock* bb, MIR* current_mir, MIR* new_mir) { - new_mir->prev = current_mir; new_mir->next = current_mir->next; current_mir->next = new_mir; - if (new_mir->next) { - /* Is not the last MIR in the block */ - new_mir->next->prev = new_mir; - } else { + if (bb->last_mir_insn == current_mir) { /* Is the last MIR in the block */ bb->last_mir_insn = new_mir; } @@ -924,8 +926,9 @@ char* MIRGraph::GetDalvikDisassembly(const MIR* mir) { opcode = insn.opcode; } else if (opcode == kMirOpNop) { str.append("["); - insn.opcode = mir->meta.original_opcode; - opcode = mir->meta.original_opcode; + // Recover original opcode. + insn.opcode = Instruction::At(current_code_item_->insns_ + mir->offset)->Opcode(); + opcode = insn.opcode; nop = true; } @@ -938,7 +941,7 @@ char* MIRGraph::GetDalvikDisassembly(const MIR* mir) { } if (opcode == kMirOpPhi) { - int* incoming = reinterpret_cast(insn.vB); + BasicBlockId* incoming = mir->meta.phi_incoming; str.append(StringPrintf(" %s = (%s", GetSSANameWithConst(ssa_rep->defs[0], true).c_str(), GetSSANameWithConst(ssa_rep->uses[0], true).c_str())); @@ -1088,7 +1091,7 @@ void MIRGraph::GetBlockName(BasicBlock* bb, char* name) { } const char* MIRGraph::GetShortyFromTargetIdx(int target_idx) { - // FIXME: use current code unit for inline support. + // TODO: for inlining support, use current code unit. const DexFile::MethodId& method_id = cu_->dex_file->GetMethodId(target_idx); return cu_->dex_file->GetShorty(method_id.proto_idx_); } @@ -1118,13 +1121,13 @@ void MIRGraph::DumpMIRGraph() { bb->start_offset, bb->last_mir_insn ? bb->last_mir_insn->offset : bb->start_offset, bb->last_mir_insn ? "" : " empty"); - if (bb->taken) { - LOG(INFO) << " Taken branch: block " << bb->taken->id - << "(0x" << std::hex << bb->taken->start_offset << ")"; + if (bb->taken != NullBasicBlockId) { + LOG(INFO) << " Taken branch: block " << bb->taken + << "(0x" << std::hex << GetBasicBlock(bb->taken)->start_offset << ")"; } - if (bb->fall_through) { - LOG(INFO) << " Fallthrough : block " << bb->fall_through->id - << " (0x" << std::hex << bb->fall_through->start_offset << ")"; + if (bb->fall_through != NullBasicBlockId) { + LOG(INFO) << " Fallthrough : block " << bb->fall_through + << " (0x" << std::hex << GetBasicBlock(bb->fall_through)->start_offset << ")"; } } } @@ -1144,7 +1147,6 @@ CallInfo* MIRGraph::NewMemCallInfo(BasicBlock* bb, MIR* mir, InvokeType type, info->result.location = kLocInvalid; } else { info->result = GetRawDest(move_result_mir); - move_result_mir->meta.original_opcode = move_result_mir->dalvikInsn.opcode; move_result_mir->dalvikInsn.opcode = static_cast(kMirOpNop); } info->num_arg_words = mir->ssa_rep->num_uses; @@ -1168,10 +1170,10 @@ BasicBlock* MIRGraph::NewMemBB(BBType block_type, int block_id) { bb->block_type = block_type; bb->id = block_id; // TUNING: better estimate of the exit block predecessors? - bb->predecessors = new (arena_) GrowableArray(arena_, + bb->predecessors = new (arena_) GrowableArray(arena_, (block_type == kExitBlock) ? 2048 : 2, kGrowableArrayPredecessors); - bb->successor_block_list.block_list_type = kNotUsed; + bb->successor_block_list_type = kNotUsed; block_id_map_.Put(block_id, block_id); return bb; } diff --git a/compiler/dex/mir_graph.h b/compiler/dex/mir_graph.h index 5d014894c1a..8dda7c4c7ea 100644 --- a/compiler/dex/mir_graph.h +++ b/compiler/dex/mir_graph.h @@ -183,6 +183,9 @@ enum OatMethodAttributes { #define BLOCK_NAME_LEN 80 +typedef uint16_t BasicBlockId; +static const BasicBlockId NullBasicBlockId = 0; + /* * In general, vreg/sreg describe Dalvik registers that originated with dx. However, * it is useful to have compiler-generated temporary registers and have them treated @@ -190,15 +193,15 @@ enum OatMethodAttributes { * name of compiler-introduced temporaries. */ struct CompilerTemp { - int s_reg; + int32_t s_reg; }; // When debug option enabled, records effectiveness of null and range check elimination. struct Checkstats { - int null_checks; - int null_checks_eliminated; - int range_checks; - int range_checks_eliminated; + int32_t null_checks; + int32_t null_checks_eliminated; + int32_t range_checks; + int32_t range_checks_eliminated; }; // Dataflow attributes of a basic block. @@ -207,7 +210,7 @@ struct BasicBlockDataFlow { ArenaBitVector* def_v; ArenaBitVector* live_in_v; ArenaBitVector* phi_v; - int* vreg_to_ssa_map; + int32_t* vreg_to_ssa_map; ArenaBitVector* ending_null_check_v; }; @@ -220,11 +223,11 @@ struct BasicBlockDataFlow { * we may want to revisit in the future. */ struct SSARepresentation { - int num_uses; - int* uses; + int16_t num_uses; + int16_t num_defs; + int32_t* uses; bool* fp_use; - int num_defs; - int* defs; + int32_t* defs; bool* fp_def; }; @@ -233,51 +236,53 @@ struct SSARepresentation { * wrapper around a Dalvik byte code. */ struct MIR { + /* + * TODO: remove embedded DecodedInstruction to save space, keeping only opcode. Recover + * additional fields on as-needed basis. Question: how to support MIR Pseudo-ops; probably + * need to carry aux data pointer. + */ DecodedInstruction dalvikInsn; - uint32_t width; // NOTE: only need 16 bits for width. - unsigned int offset; - int m_unit_index; // From which method was this MIR included - MIR* prev; + uint16_t width; // Note: width can include switch table or fill array data. + NarrowDexOffset offset; // Offset of the instruction in code units. + uint16_t optimization_flags; + int16_t m_unit_index; // From which method was this MIR included MIR* next; SSARepresentation* ssa_rep; - int optimization_flags; union { + // Incoming edges for phi node. + BasicBlockId* phi_incoming; // Establish link between two halves of throwing instructions. MIR* throw_insn; - // Saved opcode for NOP'd MIRs - Instruction::Code original_opcode; } meta; }; struct SuccessorBlockInfo; struct BasicBlock { - int id; - int dfs_id; - bool visited; - bool hidden; - bool catch_entry; - bool explicit_throw; - bool conditional_branch; - bool terminated_by_return; // Block ends with a Dalvik return opcode. - bool dominates_return; // Is a member of return extended basic block. - uint16_t start_offset; + BasicBlockId id; + BasicBlockId dfs_id; + NarrowDexOffset start_offset; // Offset in code units. + BasicBlockId fall_through; + BasicBlockId taken; + BasicBlockId i_dom; // Immediate dominator. uint16_t nesting_depth; - BBType block_type; + BBType block_type:4; + BlockListType successor_block_list_type:4; + bool visited:1; + bool hidden:1; + bool catch_entry:1; + bool explicit_throw:1; + bool conditional_branch:1; + bool terminated_by_return:1; // Block ends with a Dalvik return opcode. + bool dominates_return:1; // Is a member of return extended basic block. MIR* first_mir_insn; MIR* last_mir_insn; - BasicBlock* fall_through; - BasicBlock* taken; - BasicBlock* i_dom; // Immediate dominator. BasicBlockDataFlow* data_flow_info; - GrowableArray* predecessors; ArenaBitVector* dominators; ArenaBitVector* i_dominated; // Set nodes being immediately dominated. ArenaBitVector* dom_frontier; // Dominance frontier. - struct { // For one-to-many successors like. - BlockListType block_list_type; // switch and exception handling. - GrowableArray* blocks; - } successor_block_list; + GrowableArray* predecessors; + GrowableArray* successor_blocks; }; /* @@ -285,9 +290,8 @@ struct BasicBlock { * "SuccessorBlockInfo". For catch blocks, key is type index for the exception. For swtich * blocks, key is the case value. */ -// TODO: make class with placement new. struct SuccessorBlockInfo { - BasicBlock* block; + BasicBlockId block; int key; }; @@ -296,6 +300,15 @@ struct SuccessorBlockInfo { * the type of an SSA name (and, can also be used by code generators to record where the * value is located (i.e. - physical register, frame, spill, etc.). For each SSA name (SReg) * there is a RegLocation. + * A note on SSA names: + * o SSA names for Dalvik vRegs v0..vN will be assigned 0..N. These represent the "vN_0" + * names. Negative SSA names represent special values not present in the Dalvik byte code. + * For example, SSA name -1 represents an invalid SSA name, and SSA name -2 represents the + * the Method pointer. SSA names < -2 are reserved for future use. + * o The vN_0 names for non-argument Dalvik should in practice never be used (as they would + * represent the read of an undefined local variable). The first definition of the + * underlying Dalvik vReg will result in a vN_1 name. + * * FIXME: The orig_sreg field was added as a workaround for llvm bitcode generation. With * the latest restructuring, we should be able to remove it and rely on s_reg_low throughout. */ @@ -311,9 +324,9 @@ struct RegLocation { unsigned home:1; // Does this represent the home location? uint8_t low_reg; // First physical register. uint8_t high_reg; // 2nd physical register (if wide). - int32_t s_reg_low; // SSA name for low Dalvik word. - int32_t orig_sreg; // TODO: remove after Bitcode gen complete - // and consolodate usage w/ s_reg_low. + int16_t s_reg_low; // SSA name for low Dalvik word. + int16_t orig_sreg; // TODO: remove after Bitcode gen complete + // and consolidate usage w/ s_reg_low. }; /* @@ -334,7 +347,7 @@ struct CallInfo { RegLocation target; // Target of following move_result. bool skip_this; bool is_range; - int offset; // Dalvik offset. + DexOffset offset; // Offset in code units. }; @@ -361,7 +374,7 @@ class MIRGraph { uint32_t method_idx, jobject class_loader, const DexFile& dex_file); /* Find existing block */ - BasicBlock* FindBlock(unsigned int code_offset) { + BasicBlock* FindBlock(DexOffset code_offset) { return FindBlock(code_offset, false, false, NULL); } @@ -394,7 +407,7 @@ class MIRGraph { } BasicBlock* GetBasicBlock(int block_id) const { - return block_list_.Get(block_id); + return (block_id == NullBasicBlockId) ? NULL : block_list_.Get(block_id); } size_t GetBasicBlockListCount() const { @@ -405,15 +418,15 @@ class MIRGraph { return &block_list_; } - GrowableArray* GetDfsOrder() { + GrowableArray* GetDfsOrder() { return dfs_order_; } - GrowableArray* GetDfsPostOrder() { + GrowableArray* GetDfsPostOrder() { return dfs_post_order_; } - GrowableArray* GetDomPostOrder() { + GrowableArray* GetDomPostOrder() { return dom_post_order_traversal_; } @@ -477,6 +490,12 @@ class MIRGraph { } void SetNumSSARegs(int new_num) { + /* + * TODO: It's theoretically possible to exceed 32767, though any cases which did + * would be filtered out with current settings. When orig_sreg field is removed + * from RegLocation, expand s_reg_low to handle all possible cases and remove DCHECK(). + */ + DCHECK_EQ(new_num, static_cast(new_num)); num_ssa_regs_ = new_num; } @@ -561,15 +580,16 @@ class MIRGraph { return special_case_; } - bool IsBackedge(BasicBlock* branch_bb, BasicBlock* target_bb) { - return ((target_bb != NULL) && (target_bb->start_offset <= branch_bb->start_offset)); + bool IsBackedge(BasicBlock* branch_bb, BasicBlockId target_bb_id) { + return ((target_bb_id != NullBasicBlockId) && + (GetBasicBlock(target_bb_id)->start_offset <= branch_bb->start_offset)); } bool IsBackwardsBranch(BasicBlock* branch_bb) { return IsBackedge(branch_bb, branch_bb->taken) || IsBackedge(branch_bb, branch_bb->fall_through); } - void CountBranch(int target_offset) { + void CountBranch(DexOffset target_offset) { if (target_offset <= current_offset_) { backward_branches_++; } else { @@ -640,6 +660,9 @@ class MIRGraph { void DumpMIRGraph(); CallInfo* NewMemCallInfo(BasicBlock* bb, MIR* mir, InvokeType type, bool is_range); BasicBlock* NewMemBB(BBType block_type, int block_id); + MIR* AdvanceMIR(BasicBlock** p_bb, MIR* mir); + BasicBlock* NextDominatedBlock(BasicBlock* bb); + bool LayoutBlocks(BasicBlock* bb); /* * IsDebugBuild sanity check: keep track of the Dex PCs for catch entries so that later on @@ -668,15 +691,16 @@ class MIRGraph { bool InvokeUsesMethodStar(MIR* mir); int ParseInsn(const uint16_t* code_ptr, DecodedInstruction* decoded_instruction); bool ContentIsInsn(const uint16_t* code_ptr); - BasicBlock* SplitBlock(unsigned int code_offset, BasicBlock* orig_block, + BasicBlock* SplitBlock(DexOffset code_offset, BasicBlock* orig_block, BasicBlock** immed_pred_block_p); - BasicBlock* FindBlock(unsigned int code_offset, bool split, bool create, + BasicBlock* FindBlock(DexOffset code_offset, bool split, bool create, BasicBlock** immed_pred_block_p); void ProcessTryCatchBlocks(); - BasicBlock* ProcessCanBranch(BasicBlock* cur_block, MIR* insn, int cur_offset, int width, + BasicBlock* ProcessCanBranch(BasicBlock* cur_block, MIR* insn, DexOffset cur_offset, int width, int flags, const uint16_t* code_ptr, const uint16_t* code_end); - void ProcessCanSwitch(BasicBlock* cur_block, MIR* insn, int cur_offset, int width, int flags); - BasicBlock* ProcessCanThrow(BasicBlock* cur_block, MIR* insn, int cur_offset, int width, + void ProcessCanSwitch(BasicBlock* cur_block, MIR* insn, DexOffset cur_offset, int width, + int flags); + BasicBlock* ProcessCanThrow(BasicBlock* cur_block, MIR* insn, DexOffset cur_offset, int width, int flags, ArenaBitVector* try_block_addr, const uint16_t* code_ptr, const uint16_t* code_end); int AddNewSReg(int v_reg); @@ -732,9 +756,9 @@ class MIRGraph { GrowableArray use_counts_; // Weighted by nesting depth GrowableArray raw_use_counts_; // Not weighted unsigned int num_reachable_blocks_; - GrowableArray* dfs_order_; - GrowableArray* dfs_post_order_; - GrowableArray* dom_post_order_traversal_; + GrowableArray* dfs_order_; + GrowableArray* dfs_post_order_; + GrowableArray* dom_post_order_traversal_; int* i_dom_list_; ArenaBitVector** def_block_matrix_; // num_dalvik_register x num_blocks. ArenaBitVector* temp_block_v_; @@ -752,11 +776,11 @@ class MIRGraph { typedef std::pair MIRLocation; // Insert point, (m_unit_ index, offset) std::vector method_stack_; // Include stack int current_method_; - int current_offset_; // Dex offset in code units + DexOffset current_offset_; // Offset in code units int def_count_; // Used to estimate size of ssa name storage. int* opcode_count_; // Dex opcode coverage stats. int num_ssa_regs_; // Number of names following SSA transformation. - std::vector extended_basic_blocks_; // Heads of block "traces". + std::vector extended_basic_blocks_; // Heads of block "traces". int method_sreg_; unsigned int attributes_; Checkstats* checkstats_; diff --git a/compiler/dex/mir_optimization.cc b/compiler/dex/mir_optimization.cc index 05e428e1788..3cd158ffc06 100644 --- a/compiler/dex/mir_optimization.cc +++ b/compiler/dex/mir_optimization.cc @@ -103,12 +103,12 @@ void MIRGraph::PropagateConstants() { } /* Advance to next strictly dominated MIR node in an extended basic block */ -static MIR* AdvanceMIR(BasicBlock** p_bb, MIR* mir) { +MIR* MIRGraph::AdvanceMIR(BasicBlock** p_bb, MIR* mir) { BasicBlock* bb = *p_bb; if (mir != NULL) { mir = mir->next; if (mir == NULL) { - bb = bb->fall_through; + bb = GetBasicBlock(bb->fall_through); if ((bb == NULL) || Predecessors(bb) != 1) { mir = NULL; } else { @@ -147,19 +147,21 @@ MIR* MIRGraph::FindMoveResult(BasicBlock* bb, MIR* mir) { return mir; } -static BasicBlock* NextDominatedBlock(BasicBlock* bb) { +BasicBlock* MIRGraph::NextDominatedBlock(BasicBlock* bb) { if (bb->block_type == kDead) { return NULL; } DCHECK((bb->block_type == kEntryBlock) || (bb->block_type == kDalvikByteCode) || (bb->block_type == kExitBlock)); - if (((bb->taken != NULL) && (bb->fall_through == NULL)) && - ((bb->taken->block_type == kDalvikByteCode) || (bb->taken->block_type == kExitBlock))) { + BasicBlock* bb_taken = GetBasicBlock(bb->taken); + BasicBlock* bb_fall_through = GetBasicBlock(bb->fall_through); + if (((bb_taken != NULL) && (bb_fall_through == NULL)) && + ((bb_taken->block_type == kDalvikByteCode) || (bb_taken->block_type == kExitBlock))) { // Follow simple unconditional branches. - bb = bb->taken; + bb = bb_taken; } else { // Follow simple fallthrough - bb = (bb->taken != NULL) ? NULL : bb->fall_through; + bb = (bb_taken != NULL) ? NULL : bb_fall_through; } if (bb == NULL || (Predecessors(bb) != 1)) { return NULL; @@ -311,11 +313,13 @@ bool MIRGraph::BasicBlockOpt(BasicBlock* bb) { case Instruction::IF_GTZ: case Instruction::IF_LEZ: // If we've got a backwards branch to return, no need to suspend check. - if ((IsBackedge(bb, bb->taken) && bb->taken->dominates_return) || - (IsBackedge(bb, bb->fall_through) && bb->fall_through->dominates_return)) { + if ((IsBackedge(bb, bb->taken) && GetBasicBlock(bb->taken)->dominates_return) || + (IsBackedge(bb, bb->fall_through) && + GetBasicBlock(bb->fall_through)->dominates_return)) { mir->optimization_flags |= MIR_IGNORE_SUSPEND_CHECK; if (cu_->verbose) { - LOG(INFO) << "Suppressed suspend check on branch to return at 0x" << std::hex << mir->offset; + LOG(INFO) << "Suppressed suspend check on branch to return at 0x" << std::hex + << mir->offset; } } break; @@ -328,15 +332,15 @@ bool MIRGraph::BasicBlockOpt(BasicBlock* bb) { if (!(cu_->compiler_backend == kPortable) && (cu_->instruction_set == kThumb2) && ((mir->dalvikInsn.opcode == Instruction::IF_EQZ) || (mir->dalvikInsn.opcode == Instruction::IF_NEZ))) { - BasicBlock* ft = bb->fall_through; + BasicBlock* ft = GetBasicBlock(bb->fall_through); DCHECK(ft != NULL); - BasicBlock* ft_ft = ft->fall_through; - BasicBlock* ft_tk = ft->taken; + BasicBlock* ft_ft = GetBasicBlock(ft->fall_through); + BasicBlock* ft_tk = GetBasicBlock(ft->taken); - BasicBlock* tk = bb->taken; + BasicBlock* tk = GetBasicBlock(bb->taken); DCHECK(tk != NULL); - BasicBlock* tk_ft = tk->fall_through; - BasicBlock* tk_tk = tk->taken; + BasicBlock* tk_ft = GetBasicBlock(tk->fall_through); + BasicBlock* tk_tk = GetBasicBlock(tk->taken); /* * In the select pattern, the taken edge goes to a block that unconditionally @@ -434,7 +438,7 @@ bool MIRGraph::BasicBlockOpt(BasicBlock* bb) { int dead_def = if_false->ssa_rep->defs[0]; int live_def = if_true->ssa_rep->defs[0]; mir->ssa_rep->defs[0] = live_def; - int* incoming = reinterpret_cast(phi->dalvikInsn.vB); + BasicBlockId* incoming = phi->meta.phi_incoming; for (int i = 0; i < phi->ssa_rep->num_uses; i++) { if (phi->ssa_rep->uses[i] == live_def) { incoming[i] = bb->id; @@ -449,7 +453,7 @@ bool MIRGraph::BasicBlockOpt(BasicBlock* bb) { } } phi->ssa_rep->num_uses--; - bb->taken = NULL; + bb->taken = NullBasicBlockId; tk->block_type = kDead; for (MIR* tmir = ft->first_mir_insn; tmir != NULL; tmir = tmir->next) { tmir->dalvikInsn.opcode = static_cast(kMirOpNop); @@ -500,7 +504,7 @@ void MIRGraph::CountChecks(struct BasicBlock* bb) { } /* Try to make common case the fallthrough path */ -static bool LayoutBlocks(struct BasicBlock* bb) { +bool MIRGraph::LayoutBlocks(BasicBlock* bb) { // TODO: For now, just looking for direct throws. Consider generalizing for profile feedback if (!bb->explicit_throw) { return false; @@ -511,13 +515,13 @@ static bool LayoutBlocks(struct BasicBlock* bb) { if ((walker->block_type == kEntryBlock) || (Predecessors(walker) != 1)) { break; } - BasicBlock* prev = walker->predecessors->Get(0); + BasicBlock* prev = GetBasicBlock(walker->predecessors->Get(0)); if (prev->conditional_branch) { - if (prev->fall_through == walker) { + if (GetBasicBlock(prev->fall_through) == walker) { // Already done - return break; } - DCHECK_EQ(walker, prev->taken); + DCHECK_EQ(walker, GetBasicBlock(prev->taken)); // Got one. Flip it and exit Instruction::Code opcode = prev->last_mir_insn->dalvikInsn.opcode; switch (opcode) { @@ -536,7 +540,7 @@ static bool LayoutBlocks(struct BasicBlock* bb) { default: LOG(FATAL) << "Unexpected opcode " << opcode; } prev->last_mir_insn->dalvikInsn.opcode = opcode; - BasicBlock* t_bb = prev->taken; + BasicBlockId t_bb = prev->taken; prev->taken = prev->fall_through; prev->fall_through = t_bb; break; @@ -556,8 +560,9 @@ bool MIRGraph::CombineBlocks(struct BasicBlock* bb) { || (bb->block_type == kExceptionHandling) || (bb->block_type == kExitBlock) || (bb->block_type == kDead) - || ((bb->taken == NULL) || (bb->taken->block_type != kExceptionHandling)) - || (bb->successor_block_list.block_list_type != kNotUsed) + || (bb->taken == NullBasicBlockId) + || (GetBasicBlock(bb->taken)->block_type != kExceptionHandling) + || (bb->successor_block_list_type != kNotUsed) || (static_cast(bb->last_mir_insn->dalvikInsn.opcode) != kMirOpCheck)) { break; } @@ -578,19 +583,18 @@ bool MIRGraph::CombineBlocks(struct BasicBlock* bb) { break; } // OK - got one. Combine - BasicBlock* bb_next = bb->fall_through; + BasicBlock* bb_next = GetBasicBlock(bb->fall_through); DCHECK(!bb_next->catch_entry); DCHECK_EQ(Predecessors(bb_next), 1U); - MIR* t_mir = bb->last_mir_insn->prev; // Overwrite the kOpCheck insn with the paired opcode DCHECK_EQ(bb_next->first_mir_insn, throw_insn); *bb->last_mir_insn = *throw_insn; - bb->last_mir_insn->prev = t_mir; // Use the successor info from the next block - bb->successor_block_list = bb_next->successor_block_list; + bb->successor_block_list_type = bb_next->successor_block_list_type; + bb->successor_blocks = bb_next->successor_blocks; // Use the ending block linkage from the next block bb->fall_through = bb_next->fall_through; - bb->taken->block_type = kDead; // Kill the unused exception block + GetBasicBlock(bb->taken)->block_type = kDead; // Kill the unused exception block bb->taken = bb_next->taken; // Include the rest of the instructions bb->last_mir_insn = bb_next->last_mir_insn; @@ -631,20 +635,20 @@ bool MIRGraph::EliminateNullChecks(struct BasicBlock* bb) { temp_ssa_register_v_->SetBit(this_reg); } } else if (bb->predecessors->Size() == 1) { - BasicBlock* pred_bb = bb->predecessors->Get(0); + BasicBlock* pred_bb = GetBasicBlock(bb->predecessors->Get(0)); temp_ssa_register_v_->Copy(pred_bb->data_flow_info->ending_null_check_v); if (pred_bb->block_type == kDalvikByteCode) { // Check to see if predecessor had an explicit null-check. MIR* last_insn = pred_bb->last_mir_insn; Instruction::Code last_opcode = last_insn->dalvikInsn.opcode; if (last_opcode == Instruction::IF_EQZ) { - if (pred_bb->fall_through == bb) { + if (pred_bb->fall_through == bb->id) { // The fall-through of a block following a IF_EQZ, set the vA of the IF_EQZ to show that // it can't be null. temp_ssa_register_v_->SetBit(last_insn->ssa_rep->uses[0]); } } else if (last_opcode == Instruction::IF_NEZ) { - if (pred_bb->taken == bb) { + if (pred_bb->taken == bb->id) { // The taken block following a IF_NEZ, set the vA of the IF_NEZ to show that it can't be // null. temp_ssa_register_v_->SetBit(last_insn->ssa_rep->uses[0]); @@ -653,12 +657,12 @@ bool MIRGraph::EliminateNullChecks(struct BasicBlock* bb) { } } else { // Starting state is intersection of all incoming arcs - GrowableArray::Iterator iter(bb->predecessors); - BasicBlock* pred_bb = iter.Next(); + GrowableArray::Iterator iter(bb->predecessors); + BasicBlock* pred_bb = GetBasicBlock(iter.Next()); DCHECK(pred_bb != NULL); temp_ssa_register_v_->Copy(pred_bb->data_flow_info->ending_null_check_v); while (true) { - pred_bb = iter.Next(); + pred_bb = GetBasicBlock(iter.Next()); if (!pred_bb) break; if ((pred_bb->data_flow_info == NULL) || (pred_bb->data_flow_info->ending_null_check_v == NULL)) { @@ -691,9 +695,9 @@ bool MIRGraph::EliminateNullChecks(struct BasicBlock* bb) { } else { if (next_mir) { LOG(WARNING) << "Unexpected opcode following new: " << next_mir->dalvikInsn.opcode; - } else if (bb->fall_through) { + } else if (bb->fall_through != NullBasicBlockId) { // Look in next basic block - struct BasicBlock* next_bb = bb->fall_through; + struct BasicBlock* next_bb = GetBasicBlock(bb->fall_through); for (MIR* tmir = next_bb->first_mir_insn; tmir != NULL; tmir =tmir->next) { if (static_cast(tmir->dalvikInsn.opcode) >= static_cast(kMirOpFirst)) { @@ -834,7 +838,7 @@ bool MIRGraph::BuildExtendedBBList(struct BasicBlock* bb) { } // Must be head of extended basic block. BasicBlock* start_bb = bb; - extended_basic_blocks_.push_back(bb); + extended_basic_blocks_.push_back(bb->id); bool terminated_by_return = false; // Visit blocks strictly dominated by this head. while (bb != NULL) { @@ -864,7 +868,7 @@ void MIRGraph::BasicBlockOptimization() { } // Perform extended basic block optimizations. for (unsigned int i = 0; i < extended_basic_blocks_.size(); i++) { - BasicBlockOpt(extended_basic_blocks_[i]); + BasicBlockOpt(GetBasicBlock(extended_basic_blocks_[i])); } } if (cu_->enable_debug & (1 << kDebugDumpCFG)) { diff --git a/compiler/dex/portable/mir_to_gbc.cc b/compiler/dex/portable/mir_to_gbc.cc index df10f7eda0d..963cbeb1d15 100644 --- a/compiler/dex/portable/mir_to_gbc.cc +++ b/compiler/dex/portable/mir_to_gbc.cc @@ -132,7 +132,7 @@ void MirConverter::ConvertPackedSwitch(BasicBlock* bb, ::llvm::Value* value = GetLLVMValue(rl_src.orig_sreg); ::llvm::SwitchInst* sw = - irb_->CreateSwitch(value, GetLLVMBlock(bb->fall_through->id), + irb_->CreateSwitch(value, GetLLVMBlock(bb->fall_through), payload->case_count); for (uint16_t i = 0; i < payload->case_count; ++i) { @@ -143,8 +143,8 @@ void MirConverter::ConvertPackedSwitch(BasicBlock* bb, ::llvm::MDNode* switch_node = ::llvm::MDNode::get(*context_, irb_->getInt32(table_offset)); sw->setMetadata("SwitchTable", switch_node); - bb->taken = NULL; - bb->fall_through = NULL; + bb->taken = NullBasicBlockId; + bb->fall_through = NullBasicBlockId; } void MirConverter::ConvertSparseSwitch(BasicBlock* bb, @@ -159,7 +159,7 @@ void MirConverter::ConvertSparseSwitch(BasicBlock* bb, ::llvm::Value* value = GetLLVMValue(rl_src.orig_sreg); ::llvm::SwitchInst* sw = - irb_->CreateSwitch(value, GetLLVMBlock(bb->fall_through->id), + irb_->CreateSwitch(value, GetLLVMBlock(bb->fall_through), payload->case_count); for (size_t i = 0; i < payload->case_count; ++i) { @@ -170,8 +170,8 @@ void MirConverter::ConvertSparseSwitch(BasicBlock* bb, ::llvm::MDNode* switch_node = ::llvm::MDNode::get(*context_, irb_->getInt32(table_offset)); sw->setMetadata("SwitchTable", switch_node); - bb->taken = NULL; - bb->fall_through = NULL; + bb->taken = NullBasicBlockId; + bb->fall_through = NullBasicBlockId; } void MirConverter::ConvertSget(int32_t field_index, @@ -311,22 +311,22 @@ ::llvm::Value* MirConverter::ConvertCompare(ConditionCode cc, void MirConverter::ConvertCompareAndBranch(BasicBlock* bb, MIR* mir, ConditionCode cc, RegLocation rl_src1, RegLocation rl_src2) { - if (bb->taken->start_offset <= mir->offset) { + if (mir_graph_->GetBasicBlock(bb->taken)->start_offset <= mir->offset) { EmitSuspendCheck(); } ::llvm::Value* src1 = GetLLVMValue(rl_src1.orig_sreg); ::llvm::Value* src2 = GetLLVMValue(rl_src2.orig_sreg); ::llvm::Value* cond_value = ConvertCompare(cc, src1, src2); cond_value->setName(StringPrintf("t%d", temp_name_++)); - irb_->CreateCondBr(cond_value, GetLLVMBlock(bb->taken->id), - GetLLVMBlock(bb->fall_through->id)); + irb_->CreateCondBr(cond_value, GetLLVMBlock(bb->taken), + GetLLVMBlock(bb->fall_through)); // Don't redo the fallthrough branch in the BB driver - bb->fall_through = NULL; + bb->fall_through = NullBasicBlockId; } void MirConverter::ConvertCompareZeroAndBranch(BasicBlock* bb, MIR* mir, ConditionCode cc, RegLocation rl_src1) { - if (bb->taken->start_offset <= mir->offset) { + if (mir_graph_->GetBasicBlock(bb->taken)->start_offset <= mir->offset) { EmitSuspendCheck(); } ::llvm::Value* src1 = GetLLVMValue(rl_src1.orig_sreg); @@ -337,10 +337,10 @@ void MirConverter::ConvertCompareZeroAndBranch(BasicBlock* bb, src2 = irb_->getInt32(0); } ::llvm::Value* cond_value = ConvertCompare(cc, src1, src2); - irb_->CreateCondBr(cond_value, GetLLVMBlock(bb->taken->id), - GetLLVMBlock(bb->fall_through->id)); + irb_->CreateCondBr(cond_value, GetLLVMBlock(bb->taken), + GetLLVMBlock(bb->fall_through)); // Don't redo the fallthrough branch in the BB driver - bb->fall_through = NULL; + bb->fall_through = NullBasicBlockId; } ::llvm::Value* MirConverter::GenDivModOp(bool is_div, bool is_long, @@ -941,10 +941,10 @@ bool MirConverter::ConvertMIRNode(MIR* mir, BasicBlock* bb, case Instruction::GOTO: case Instruction::GOTO_16: case Instruction::GOTO_32: { - if (bb->taken->start_offset <= bb->start_offset) { + if (mir_graph_->GetBasicBlock(bb->taken)->start_offset <= bb->start_offset) { EmitSuspendCheck(); } - irb_->CreateBr(GetLLVMBlock(bb->taken->id)); + irb_->CreateBr(GetLLVMBlock(bb->taken)); } break; @@ -1190,11 +1190,11 @@ bool MirConverter::ConvertMIRNode(MIR* mir, BasicBlock* bb, * If it might rethrow, force termination * of the following block. */ - if (bb->fall_through == NULL) { + if (bb->fall_through == NullBasicBlockId) { irb_->CreateUnreachable(); } else { - bb->fall_through->fall_through = NULL; - bb->fall_through->taken = NULL; + mir_graph_->GetBasicBlock(bb->fall_through)->fall_through = NullBasicBlockId; + mir_graph_->GetBasicBlock(bb->fall_through)->taken = NullBasicBlockId; } break; @@ -1552,7 +1552,7 @@ void MirConverter::HandlePhiNodes(BasicBlock* bb, ::llvm::BasicBlock* llvm_bb) { if (rl_dest.high_word) { continue; // No Phi node - handled via low word } - int* incoming = reinterpret_cast(mir->dalvikInsn.vB); + BasicBlockId* incoming = mir->meta.phi_incoming; ::llvm::Type* phi_type = LlvmTypeFromLocRec(rl_dest); ::llvm::PHINode* phi = irb_->CreatePHI(phi_type, mir->ssa_rep->num_uses); @@ -1597,8 +1597,8 @@ void MirConverter::ConvertExtendedMIR(BasicBlock* bb, MIR* mir, break; } case kMirOpNop: - if ((mir == bb->last_mir_insn) && (bb->taken == NULL) && - (bb->fall_through == NULL)) { + if ((mir == bb->last_mir_insn) && (bb->taken == NullBasicBlockId) && + (bb->fall_through == NullBasicBlockId)) { irb_->CreateUnreachable(); } break; @@ -1718,25 +1718,23 @@ bool MirConverter::BlockBitcodeConversion(BasicBlock* bb) { SSARepresentation* ssa_rep = work_half->ssa_rep; work_half->ssa_rep = mir->ssa_rep; mir->ssa_rep = ssa_rep; - work_half->meta.original_opcode = work_half->dalvikInsn.opcode; work_half->dalvikInsn.opcode = static_cast(kMirOpNop); - if (bb->successor_block_list.block_list_type == kCatch) { + if (bb->successor_block_list_type == kCatch) { ::llvm::Function* intr = intrinsic_helper_->GetIntrinsicFunction( art::llvm::IntrinsicHelper::CatchTargets); ::llvm::Value* switch_key = irb_->CreateCall(intr, irb_->getInt32(mir->offset)); - GrowableArray::Iterator iter(bb->successor_block_list.blocks); + GrowableArray::Iterator iter(bb->successor_blocks); // New basic block to use for work half ::llvm::BasicBlock* work_bb = ::llvm::BasicBlock::Create(*context_, "", func_); ::llvm::SwitchInst* sw = - irb_->CreateSwitch(switch_key, work_bb, - bb->successor_block_list.blocks->Size()); + irb_->CreateSwitch(switch_key, work_bb, bb->successor_blocks->Size()); while (true) { SuccessorBlockInfo *successor_block_info = iter.Next(); if (successor_block_info == NULL) break; ::llvm::BasicBlock *target = - GetLLVMBlock(successor_block_info->block->id); + GetLLVMBlock(successor_block_info->block); int type_index = successor_block_info->key; sw->addCase(irb_->getInt32(type_index), target); } @@ -1761,9 +1759,9 @@ bool MirConverter::BlockBitcodeConversion(BasicBlock* bb) { } if (bb->block_type == kEntryBlock) { - entry_target_bb_ = GetLLVMBlock(bb->fall_through->id); - } else if ((bb->fall_through != NULL) && !bb->terminated_by_return) { - irb_->CreateBr(GetLLVMBlock(bb->fall_through->id)); + entry_target_bb_ = GetLLVMBlock(bb->fall_through); + } else if ((bb->fall_through != NullBasicBlockId) && !bb->terminated_by_return) { + irb_->CreateBr(GetLLVMBlock(bb->fall_through)); } return false; diff --git a/compiler/dex/quick/arm/assemble_arm.cc b/compiler/dex/quick/arm/assemble_arm.cc index 3c646c40b6b..cc40e99c524 100644 --- a/compiler/dex/quick/arm/assemble_arm.cc +++ b/compiler/dex/quick/arm/assemble_arm.cc @@ -1031,8 +1031,7 @@ void ArmMir2Lir::EncodeLIR(LIR* lir) { } else if (LIKELY(!lir->flags.is_nop)) { const ArmEncodingMap *encoder = &EncodingMap[lir->opcode]; uint32_t bits = encoder->skeleton; - int i; - for (i = 0; i < 4; i++) { + for (int i = 0; i < 4; i++) { uint32_t operand; uint32_t value; operand = lir->operands[i]; @@ -1088,7 +1087,7 @@ void ArmMir2Lir::EncodeLIR(LIR* lir) { case kFmtDfp: { DCHECK(ARM_DOUBLEREG(operand)); DCHECK_EQ((operand & 0x1), 0U); - int reg_name = (operand & ARM_FP_REG_MASK) >> 1; + uint32_t reg_name = (operand & ARM_FP_REG_MASK) >> 1; /* Snag the 1-bit slice and position it */ value = ((reg_name & 0x10) >> 4) << encoder->field_loc[i].end; /* Extract and position the 4-bit slice */ @@ -1155,9 +1154,9 @@ void ArmMir2Lir::AssembleLIR() { LIR* lir; LIR* prev_lir; int assembler_retries = 0; - int starting_offset = EncodeRange(first_lir_insn_, last_lir_insn_, 0); + CodeOffset starting_offset = EncodeRange(first_lir_insn_, last_lir_insn_, 0); data_offset_ = (starting_offset + 0x3) & ~0x3; - int offset_adjustment; + int32_t offset_adjustment; AssignDataOffsets(); /* @@ -1200,10 +1199,10 @@ void ArmMir2Lir::AssembleLIR() { * we revert to a multiple-instruction materialization sequence. */ LIR *lir_target = lir->target; - uintptr_t pc = (lir->offset + 4) & ~3; - uintptr_t target = lir_target->offset + + CodeOffset pc = (lir->offset + 4) & ~3; + CodeOffset target = lir_target->offset + ((lir_target->flags.generation == lir->flags.generation) ? 0 : offset_adjustment); - int delta = target - pc; + int32_t delta = target - pc; if (res != kSuccess) { /* * In this case, we're just estimating and will do it again for real. Ensure offset @@ -1281,10 +1280,10 @@ void ArmMir2Lir::AssembleLIR() { } case kFixupCBxZ: { LIR *target_lir = lir->target; - uintptr_t pc = lir->offset + 4; - uintptr_t target = target_lir->offset + + CodeOffset pc = lir->offset + 4; + CodeOffset target = target_lir->offset + ((target_lir->flags.generation == lir->flags.generation) ? 0 : offset_adjustment); - int delta = target - pc; + int32_t delta = target - pc; if (delta > 126 || delta < 0) { /* * Convert to cmp rx,#0 / b[eq/ne] tgt pair @@ -1351,10 +1350,10 @@ void ArmMir2Lir::AssembleLIR() { } case kFixupCondBranch: { LIR *target_lir = lir->target; - int delta = 0; + int32_t delta = 0; DCHECK(target_lir); - uintptr_t pc = lir->offset + 4; - uintptr_t target = target_lir->offset + + CodeOffset pc = lir->offset + 4; + CodeOffset target = target_lir->offset + ((target_lir->flags.generation == lir->flags.generation) ? 0 : offset_adjustment); delta = target - pc; if ((lir->opcode == kThumbBCond) && (delta > 254 || delta < -256)) { @@ -1370,10 +1369,10 @@ void ArmMir2Lir::AssembleLIR() { } case kFixupT2Branch: { LIR *target_lir = lir->target; - uintptr_t pc = lir->offset + 4; - uintptr_t target = target_lir->offset + + CodeOffset pc = lir->offset + 4; + CodeOffset target = target_lir->offset + ((target_lir->flags.generation == lir->flags.generation) ? 0 : offset_adjustment); - int delta = target - pc; + int32_t delta = target - pc; lir->operands[0] = delta >> 1; if (!(cu_->disable_opt & (1 << kSafeOptimizations)) && lir->operands[0] == 0) { // Useless branch @@ -1387,10 +1386,10 @@ void ArmMir2Lir::AssembleLIR() { } case kFixupT1Branch: { LIR *target_lir = lir->target; - uintptr_t pc = lir->offset + 4; - uintptr_t target = target_lir->offset + + CodeOffset pc = lir->offset + 4; + CodeOffset target = target_lir->offset + ((target_lir->flags.generation == lir->flags.generation) ? 0 : offset_adjustment); - int delta = target - pc; + int32_t delta = target - pc; if (delta > 2046 || delta < -2048) { // Convert to Thumb2BCond w/ kArmCondAl offset_adjustment -= lir->flags.size; @@ -1416,14 +1415,14 @@ void ArmMir2Lir::AssembleLIR() { case kFixupBlx1: { DCHECK(NEXT_LIR(lir)->opcode == kThumbBlx2); /* cur_pc is Thumb */ - uintptr_t cur_pc = (lir->offset + 4) & ~3; - uintptr_t target = lir->operands[1]; + CodeOffset cur_pc = (lir->offset + 4) & ~3; + CodeOffset target = lir->operands[1]; /* Match bit[1] in target with base */ if (cur_pc & 0x2) { target |= 0x2; } - int delta = target - cur_pc; + int32_t delta = target - cur_pc; DCHECK((delta >= -(1<<22)) && (delta <= ((1<<22)-2))); lir->operands[0] = (delta >> 12) & 0x7ff; @@ -1433,10 +1432,10 @@ void ArmMir2Lir::AssembleLIR() { case kFixupBl1: { DCHECK(NEXT_LIR(lir)->opcode == kThumbBl2); /* Both cur_pc and target are Thumb */ - uintptr_t cur_pc = lir->offset + 4; - uintptr_t target = lir->operands[1]; + CodeOffset cur_pc = lir->offset + 4; + CodeOffset target = lir->operands[1]; - int delta = target - cur_pc; + int32_t delta = target - cur_pc; DCHECK((delta >= -(1<<22)) && (delta <= ((1<<22)-2))); lir->operands[0] = (delta >> 12) & 0x7ff; @@ -1444,20 +1443,19 @@ void ArmMir2Lir::AssembleLIR() { break; } case kFixupAdr: { - SwitchTable *tab_rec = reinterpret_cast(lir->operands[2]); + EmbeddedData *tab_rec = reinterpret_cast(UnwrapPointer(lir->operands[2])); LIR* target = lir->target; - int target_disp = (tab_rec != NULL) ? tab_rec->offset + offset_adjustment + int32_t target_disp = (tab_rec != NULL) ? tab_rec->offset + offset_adjustment : target->offset + ((target->flags.generation == lir->flags.generation) ? 0 : offset_adjustment); - int disp = target_disp - ((lir->offset + 4) & ~3); + int32_t disp = target_disp - ((lir->offset + 4) & ~3); if (disp < 4096) { lir->operands[1] = disp; } else { // convert to ldimm16l, ldimm16h, add tgt, pc, operands[0] // TUNING: if this case fires often, it can be improved. Not expected to be common. LIR *new_mov16L = - RawLIR(lir->dalvik_offset, kThumb2MovImm16LST, - lir->operands[0], 0, reinterpret_cast(lir), - reinterpret_cast(tab_rec), 0, lir->target); + RawLIR(lir->dalvik_offset, kThumb2MovImm16LST, lir->operands[0], 0, + WrapPointer(lir), WrapPointer(tab_rec), 0, lir->target); new_mov16L->flags.size = EncodingMap[new_mov16L->opcode].size; new_mov16L->flags.fixup = kFixupMovImmLST; new_mov16L->offset = lir->offset; @@ -1467,11 +1465,9 @@ void ArmMir2Lir::AssembleLIR() { offset_adjustment += new_mov16L->flags.size; InsertFixupBefore(prev_lir, lir, new_mov16L); prev_lir = new_mov16L; // Now we've got a new prev. - LIR *new_mov16H = - RawLIR(lir->dalvik_offset, kThumb2MovImm16HST, - lir->operands[0], 0, reinterpret_cast(lir), - reinterpret_cast(tab_rec), 0, lir->target); + RawLIR(lir->dalvik_offset, kThumb2MovImm16HST, lir->operands[0], 0, + WrapPointer(lir), WrapPointer(tab_rec), 0, lir->target); new_mov16H->flags.size = EncodingMap[new_mov16H->opcode].size; new_mov16H->flags.fixup = kFixupMovImmHST; new_mov16H->offset = lir->offset; @@ -1499,27 +1495,27 @@ void ArmMir2Lir::AssembleLIR() { } case kFixupMovImmLST: { // operands[1] should hold disp, [2] has add, [3] has tab_rec - LIR *addPCInst = reinterpret_cast(lir->operands[2]); - SwitchTable *tab_rec = reinterpret_cast(lir->operands[3]); + LIR *addPCInst = reinterpret_cast(UnwrapPointer(lir->operands[2])); + EmbeddedData *tab_rec = reinterpret_cast(UnwrapPointer(lir->operands[3])); // If tab_rec is null, this is a literal load. Use target LIR* target = lir->target; - int target_disp = tab_rec ? tab_rec->offset : target->offset; + int32_t target_disp = tab_rec ? tab_rec->offset : target->offset; lir->operands[1] = (target_disp - (addPCInst->offset + 4)) & 0xffff; break; } case kFixupMovImmHST: { // operands[1] should hold disp, [2] has add, [3] has tab_rec - LIR *addPCInst = reinterpret_cast(lir->operands[2]); - SwitchTable *tab_rec = reinterpret_cast(lir->operands[3]); + LIR *addPCInst = reinterpret_cast(UnwrapPointer(lir->operands[2])); + EmbeddedData *tab_rec = reinterpret_cast(UnwrapPointer(lir->operands[3])); // If tab_rec is null, this is a literal load. Use target LIR* target = lir->target; - int target_disp = tab_rec ? tab_rec->offset : target->offset; + int32_t target_disp = tab_rec ? tab_rec->offset : target->offset; lir->operands[1] = ((target_disp - (addPCInst->offset + 4)) >> 16) & 0xffff; break; } case kFixupAlign4: { - int required_size = lir->offset & 0x2; + int32_t required_size = lir->offset & 0x2; if (lir->flags.size != required_size) { offset_adjustment += required_size - lir->flags.size; lir->flags.size = required_size; @@ -1647,7 +1643,7 @@ uint32_t ArmMir2Lir::EncodeRange(LIR* head_lir, LIR* tail_lir, uint32_t offset) void ArmMir2Lir::AssignDataOffsets() { /* Set up offsets for literals */ - int offset = data_offset_; + CodeOffset offset = data_offset_; offset = AssignLiteralOffset(offset); diff --git a/compiler/dex/quick/arm/call_arm.cc b/compiler/dex/quick/arm/call_arm.cc index 401da2abd48..51aca8540c8 100644 --- a/compiler/dex/quick/arm/call_arm.cc +++ b/compiler/dex/quick/arm/call_arm.cc @@ -92,7 +92,7 @@ void ArmMir2Lir::LockLiveArgs(MIR* mir) { } /* Find the next MIR, which may be in a following basic block */ -// TODO: should this be a utility in mir_graph? +// TODO: make this a utility in mir_graph. MIR* ArmMir2Lir::GetNextMir(BasicBlock** p_bb, MIR* mir) { BasicBlock* bb = *p_bb; MIR* orig_mir = mir; @@ -103,7 +103,7 @@ MIR* ArmMir2Lir::GetNextMir(BasicBlock** p_bb, MIR* mir) { if (mir != NULL) { return mir; } else { - bb = bb->fall_through; + bb = mir_graph_->GetBasicBlock(bb->fall_through); *p_bb = bb; if (bb) { mir = bb->first_mir_insn; @@ -128,7 +128,7 @@ void ArmMir2Lir::GenPrintLabel(MIR* mir) { MIR* ArmMir2Lir::SpecialIGet(BasicBlock** bb, MIR* mir, OpSize size, bool long_or_double, bool is_object) { - int field_offset; + int32_t field_offset; bool is_volatile; uint32_t field_idx = mir->dalvikInsn.vC; bool fast_path = FastInstance(field_idx, false, &field_offset, &is_volatile); @@ -153,7 +153,7 @@ MIR* ArmMir2Lir::SpecialIGet(BasicBlock** bb, MIR* mir, MIR* ArmMir2Lir::SpecialIPut(BasicBlock** bb, MIR* mir, OpSize size, bool long_or_double, bool is_object) { - int field_offset; + int32_t field_offset; bool is_volatile; uint32_t field_idx = mir->dalvikInsn.vC; bool fast_path = FastInstance(field_idx, false, &field_offset, &is_volatile); @@ -320,9 +320,9 @@ void ArmMir2Lir::GenSparseSwitch(MIR* mir, uint32_t table_offset, static_cast(arena_->Alloc(sizeof(SwitchTable), ArenaAllocator::kAllocData)); tab_rec->table = table; tab_rec->vaddr = current_dalvik_offset_; - int size = table[1]; + uint32_t size = table[1]; tab_rec->targets = static_cast(arena_->Alloc(size * sizeof(LIR*), - ArenaAllocator::kAllocLIR)); + ArenaAllocator::kAllocLIR)); switch_tables_.Insert(tab_rec); // Get the switch value @@ -338,7 +338,7 @@ void ArmMir2Lir::GenSparseSwitch(MIR* mir, uint32_t table_offset, r_key = tmp; } // Materialize a pointer to the switch table - NewLIR3(kThumb2Adr, rBase, 0, reinterpret_cast(tab_rec)); + NewLIR3(kThumb2Adr, rBase, 0, WrapPointer(tab_rec)); // Set up r_idx int r_idx = AllocTemp(); LoadConstant(r_idx, size); @@ -368,7 +368,7 @@ void ArmMir2Lir::GenPackedSwitch(MIR* mir, uint32_t table_offset, static_cast(arena_->Alloc(sizeof(SwitchTable), ArenaAllocator::kAllocData)); tab_rec->table = table; tab_rec->vaddr = current_dalvik_offset_; - int size = table[1]; + uint32_t size = table[1]; tab_rec->targets = static_cast(arena_->Alloc(size * sizeof(LIR*), ArenaAllocator::kAllocLIR)); switch_tables_.Insert(tab_rec); @@ -377,7 +377,7 @@ void ArmMir2Lir::GenPackedSwitch(MIR* mir, uint32_t table_offset, rl_src = LoadValue(rl_src, kCoreReg); int table_base = AllocTemp(); // Materialize a pointer to the switch table - NewLIR3(kThumb2Adr, table_base, 0, reinterpret_cast(tab_rec)); + NewLIR3(kThumb2Adr, table_base, 0, WrapPointer(tab_rec)); int low_key = s4FromSwitchData(&table[2]); int keyReg; // Remove the bias, if necessary @@ -433,7 +433,7 @@ void ArmMir2Lir::GenFillArrayData(uint32_t table_offset, RegLocation rl_src) { LoadWordDisp(rARM_SELF, QUICK_ENTRYPOINT_OFFSET(pHandleFillArrayData).Int32Value(), rARM_LR); // Materialize a pointer to the fill data image - NewLIR3(kThumb2Adr, r1, 0, reinterpret_cast(tab_rec)); + NewLIR3(kThumb2Adr, r1, 0, WrapPointer(tab_rec)); ClobberCalleeSave(); LIR* call_inst = OpReg(kOpBlx, rARM_LR); MarkSafepointPC(call_inst); diff --git a/compiler/dex/quick/arm/codegen_arm.h b/compiler/dex/quick/arm/codegen_arm.h index aa5782b20cc..0a3bfc10ee2 100644 --- a/compiler/dex/quick/arm/codegen_arm.h +++ b/compiler/dex/quick/arm/codegen_arm.h @@ -74,7 +74,6 @@ class ArmMir2Lir : public Mir2Lir { uint32_t EncodeRange(LIR* head_lir, LIR* tail_lir, uint32_t starting_offset); int AssignInsnOffsets(); void AssignOffsets(); - AssemblerStatus AssembleInstructions(uintptr_t start_addr); void EncodeLIR(LIR* lir); void DumpResourceMask(LIR* lir, uint64_t mask, const char* prefix); void SetupTargetResourceMasks(LIR* lir, uint64_t flags); @@ -120,7 +119,7 @@ class ArmMir2Lir : public Mir2Lir { void GenDivZeroCheck(int reg_lo, int reg_hi); void GenEntrySequence(RegLocation* ArgLocs, RegLocation rl_method); void GenExitSequence(); - void GenFillArrayData(uint32_t table_offset, RegLocation rl_src); + void GenFillArrayData(DexOffset table_offset, RegLocation rl_src); void GenFusedFPCmpBranch(BasicBlock* bb, MIR* mir, bool gt_bias, bool is_double); void GenFusedLongCmpBranch(BasicBlock* bb, MIR* mir); void GenSelect(BasicBlock* bb, MIR* mir); @@ -132,8 +131,8 @@ class ArmMir2Lir : public Mir2Lir { int first_bit, int second_bit); void GenNegDouble(RegLocation rl_dest, RegLocation rl_src); void GenNegFloat(RegLocation rl_dest, RegLocation rl_src); - void GenPackedSwitch(MIR* mir, uint32_t table_offset, RegLocation rl_src); - void GenSparseSwitch(MIR* mir, uint32_t table_offset, RegLocation rl_src); + void GenPackedSwitch(MIR* mir, DexOffset table_offset, RegLocation rl_src); + void GenSparseSwitch(MIR* mir, DexOffset table_offset, RegLocation rl_src); void GenSpecialCase(BasicBlock* bb, MIR* mir, SpecialCaseHandler special_case); // Required for target - single operation generators. diff --git a/compiler/dex/quick/arm/fp_arm.cc b/compiler/dex/quick/arm/fp_arm.cc index 08d67781295..480e0218d54 100644 --- a/compiler/dex/quick/arm/fp_arm.cc +++ b/compiler/dex/quick/arm/fp_arm.cc @@ -176,7 +176,7 @@ void ArmMir2Lir::GenConversion(Instruction::Code opcode, void ArmMir2Lir::GenFusedFPCmpBranch(BasicBlock* bb, MIR* mir, bool gt_bias, bool is_double) { - LIR* target = &block_label_list_[bb->taken->id]; + LIR* target = &block_label_list_[bb->taken]; RegLocation rl_src1; RegLocation rl_src2; if (is_double) { diff --git a/compiler/dex/quick/arm/int_arm.cc b/compiler/dex/quick/arm/int_arm.cc index b1772fd81f8..69ea4e9ca35 100644 --- a/compiler/dex/quick/arm/int_arm.cc +++ b/compiler/dex/quick/arm/int_arm.cc @@ -122,8 +122,8 @@ void ArmMir2Lir::GenFusedLongCmpImmBranch(BasicBlock* bb, RegLocation rl_src1, int32_t val_hi = High32Bits(val); DCHECK_GE(ModifiedImmediate(val_lo), 0); DCHECK_GE(ModifiedImmediate(val_hi), 0); - LIR* taken = &block_label_list_[bb->taken->id]; - LIR* not_taken = &block_label_list_[bb->fall_through->id]; + LIR* taken = &block_label_list_[bb->taken]; + LIR* not_taken = &block_label_list_[bb->fall_through]; rl_src1 = LoadValueWide(rl_src1, kCoreReg); int32_t low_reg = rl_src1.low_reg; int32_t high_reg = rl_src1.high_reg; @@ -178,23 +178,6 @@ void ArmMir2Lir::GenFusedLongCmpImmBranch(BasicBlock* bb, RegLocation rl_src1, void ArmMir2Lir::GenSelect(BasicBlock* bb, MIR* mir) { RegLocation rl_result; RegLocation rl_src = mir_graph_->GetSrc(mir, 0); - // Temporary debugging code - int dest_sreg = mir->ssa_rep->defs[0]; - if ((dest_sreg < 0) || (dest_sreg >= mir_graph_->GetNumSSARegs())) { - LOG(INFO) << "Bad target sreg: " << dest_sreg << ", in " - << PrettyMethod(cu_->method_idx, *cu_->dex_file); - LOG(INFO) << "at dex offset 0x" << std::hex << mir->offset; - LOG(INFO) << "vreg = " << mir_graph_->SRegToVReg(dest_sreg); - LOG(INFO) << "num uses = " << mir->ssa_rep->num_uses; - if (mir->ssa_rep->num_uses == 1) { - LOG(INFO) << "CONST case, vals = " << mir->dalvikInsn.vB << ", " << mir->dalvikInsn.vC; - } else { - LOG(INFO) << "MOVE case, operands = " << mir->ssa_rep->uses[1] << ", " - << mir->ssa_rep->uses[2]; - } - CHECK(false) << "Invalid target sreg on Select."; - } - // End temporary debugging code RegLocation rl_dest = mir_graph_->GetDest(mir); rl_src = LoadValue(rl_src, kCoreReg); if (mir->ssa_rep->num_uses == 1) { @@ -270,8 +253,8 @@ void ArmMir2Lir::GenFusedLongCmpBranch(BasicBlock* bb, MIR* mir) { return; } } - LIR* taken = &block_label_list_[bb->taken->id]; - LIR* not_taken = &block_label_list_[bb->fall_through->id]; + LIR* taken = &block_label_list_[bb->taken]; + LIR* not_taken = &block_label_list_[bb->fall_through]; rl_src1 = LoadValueWide(rl_src1, kCoreReg); rl_src2 = LoadValueWide(rl_src2, kCoreReg); OpRegReg(kOpCmp, rl_src1.high_reg, rl_src2.high_reg); diff --git a/compiler/dex/quick/arm/target_arm.cc b/compiler/dex/quick/arm/target_arm.cc index 933c1a3b917..3395ae7a443 100644 --- a/compiler/dex/quick/arm/target_arm.cc +++ b/compiler/dex/quick/arm/target_arm.cc @@ -282,8 +282,8 @@ static char* DecodeFPCSRegList(int count, int base, char* buf) { return buf; } -static int ExpandImmediate(int value) { - int mode = (value & 0xf00) >> 8; +static int32_t ExpandImmediate(int value) { + int32_t mode = (value & 0xf00) >> 8; uint32_t bits = value & 0xff; switch (mode) { case 0: diff --git a/compiler/dex/quick/arm/utility_arm.cc b/compiler/dex/quick/arm/utility_arm.cc index 00de8de70a7..a2ac6ef53bc 100644 --- a/compiler/dex/quick/arm/utility_arm.cc +++ b/compiler/dex/quick/arm/utility_arm.cc @@ -22,14 +22,14 @@ namespace art { /* This file contains codegen for the Thumb ISA. */ -static int EncodeImmSingle(int value) { - int res; - int bit_a = (value & 0x80000000) >> 31; - int not_bit_b = (value & 0x40000000) >> 30; - int bit_b = (value & 0x20000000) >> 29; - int b_smear = (value & 0x3e000000) >> 25; - int slice = (value & 0x01f80000) >> 19; - int zeroes = (value & 0x0007ffff); +static int32_t EncodeImmSingle(int32_t value) { + int32_t res; + int32_t bit_a = (value & 0x80000000) >> 31; + int32_t not_bit_b = (value & 0x40000000) >> 30; + int32_t bit_b = (value & 0x20000000) >> 29; + int32_t b_smear = (value & 0x3e000000) >> 25; + int32_t slice = (value & 0x01f80000) >> 19; + int32_t zeroes = (value & 0x0007ffff); if (zeroes != 0) return -1; if (bit_b) { @@ -47,15 +47,15 @@ static int EncodeImmSingle(int value) { * Determine whether value can be encoded as a Thumb2 floating point * immediate. If not, return -1. If so return encoded 8-bit value. */ -static int EncodeImmDouble(int64_t value) { - int res; - int bit_a = (value & 0x8000000000000000ll) >> 63; - int not_bit_b = (value & 0x4000000000000000ll) >> 62; - int bit_b = (value & 0x2000000000000000ll) >> 61; - int b_smear = (value & 0x3fc0000000000000ll) >> 54; - int slice = (value & 0x003f000000000000ll) >> 48; +static int32_t EncodeImmDouble(int64_t value) { + int32_t res; + int32_t bit_a = (value & 0x8000000000000000ll) >> 63; + int32_t not_bit_b = (value & 0x4000000000000000ll) >> 62; + int32_t bit_b = (value & 0x2000000000000000ll) >> 61; + int32_t b_smear = (value & 0x3fc0000000000000ll) >> 54; + int32_t slice = (value & 0x003f000000000000ll) >> 48; uint64_t zeroes = (value & 0x0000ffffffffffffll); - if (zeroes != 0) + if (zeroes != 0ull) return -1; if (bit_b) { if ((not_bit_b != 0) || (b_smear != 0xff)) @@ -96,8 +96,8 @@ LIR* ArmMir2Lir::LoadFPConstantValue(int r_dest, int value) { static int LeadingZeros(uint32_t val) { uint32_t alt; - int n; - int count; + int32_t n; + int32_t count; count = 16; n = 32; @@ -117,8 +117,8 @@ static int LeadingZeros(uint32_t val) { * immediate. If not, return -1. If so, return i:imm3:a:bcdefgh form. */ int ArmMir2Lir::ModifiedImmediate(uint32_t value) { - int z_leading; - int z_trailing; + int32_t z_leading; + int32_t z_trailing; uint32_t b0 = value & 0xff; /* Note: case of value==0 must use 0:000:0:0000000 encoding */ @@ -421,12 +421,12 @@ LIR* ArmMir2Lir::OpRegRegReg(OpKind op, int r_dest, int r_src1, int r_src2) { LIR* ArmMir2Lir::OpRegRegImm(OpKind op, int r_dest, int r_src1, int value) { LIR* res; bool neg = (value < 0); - int abs_value = (neg) ? -value : value; + int32_t abs_value = (neg) ? -value : value; ArmOpcode opcode = kThumbBkpt; ArmOpcode alt_opcode = kThumbBkpt; bool all_low_regs = (ARM_LOWREG(r_dest) && ARM_LOWREG(r_src1)); - int mod_imm = ModifiedImmediate(value); - int mod_imm_neg = ModifiedImmediate(-value); + int32_t mod_imm = ModifiedImmediate(value); + int32_t mod_imm_neg = ModifiedImmediate(-value); switch (op) { case kOpLsl: @@ -544,7 +544,7 @@ LIR* ArmMir2Lir::OpRegRegImm(OpKind op, int r_dest, int r_src1, int value) { /* Handle Thumb-only variants here - otherwise punt to OpRegRegImm */ LIR* ArmMir2Lir::OpRegImm(OpKind op, int r_dest_src1, int value) { bool neg = (value < 0); - int abs_value = (neg) ? -value : value; + int32_t abs_value = (neg) ? -value : value; bool short_form = (((abs_value & 0xff) == abs_value) && ARM_LOWREG(r_dest_src1)); ArmOpcode opcode = kThumbBkpt; switch (op) { diff --git a/compiler/dex/quick/codegen_util.cc b/compiler/dex/quick/codegen_util.cc index 617f35707c1..2ce8f581b4b 100644 --- a/compiler/dex/quick/codegen_util.cc +++ b/compiler/dex/quick/codegen_util.cc @@ -160,7 +160,8 @@ void Mir2Lir::DumpLIRInsn(LIR* lir, unsigned char* base_addr) { break; case kPseudoDalvikByteCodeBoundary: if (lir->operands[0] == 0) { - lir->operands[0] = reinterpret_cast("No instruction string"); + // NOTE: only used for debug listings. + lir->operands[0] = WrapPointer(ArenaStrdup("No instruction string")); } LOG(INFO) << "-------- dalvik offset: 0x" << std::hex << lir->dalvik_offset << " @ " << reinterpret_cast(lir->operands[0]); @@ -369,6 +370,17 @@ static void PushWord(std::vector&buf, int data) { buf.push_back((data >> 24) & 0xff); } +// Push 8 bytes on 64-bit systems; 4 on 32-bit systems. +static void PushPointer(std::vector&buf, void const* pointer) { + uintptr_t data = reinterpret_cast(pointer); + if (sizeof(void*) == sizeof(uint64_t)) { + PushWord(buf, (data >> (sizeof(void*) * 4)) & 0xFFFFFFFF); + PushWord(buf, data & 0xFFFFFFFF); + } else { + PushWord(buf, data); + } +} + static void AlignBuffer(std::vector&buf, size_t offset) { while (buf.size() < offset) { buf.push_back(0); @@ -395,9 +407,8 @@ void Mir2Lir::InstallLiteralPools() { static_cast(data_lir->operands[1]), code_buffer_.size()); const DexFile::MethodId& id = cu_->dex_file->GetMethodId(target); - // unique based on target to ensure code deduplication works - uint32_t unique_patch_value = reinterpret_cast(&id); - PushWord(code_buffer_, unique_patch_value); + // unique value based on target to ensure code deduplication works + PushPointer(code_buffer_, &id); data_lir = NEXT_LIR(data_lir); } data_lir = method_literal_list_; @@ -411,9 +422,8 @@ void Mir2Lir::InstallLiteralPools() { static_cast(data_lir->operands[1]), code_buffer_.size()); const DexFile::MethodId& id = cu_->dex_file->GetMethodId(target); - // unique based on target to ensure code deduplication works - uint32_t unique_patch_value = reinterpret_cast(&id); - PushWord(code_buffer_, unique_patch_value); + // unique value based on target to ensure code deduplication works + PushPointer(code_buffer_, &id); data_lir = NEXT_LIR(data_lir); } } @@ -449,7 +459,7 @@ void Mir2Lir::InstallSwitchTables() { LOG(INFO) << "Switch table for offset 0x" << std::hex << bx_offset; } if (tab_rec->table[0] == Instruction::kSparseSwitchSignature) { - const int* keys = reinterpret_cast(&(tab_rec->table[2])); + const int32_t* keys = reinterpret_cast(&(tab_rec->table[2])); for (int elems = 0; elems < tab_rec->table[1]; elems++) { int disp = tab_rec->targets[elems]->offset - bx_offset; if (cu_->verbose) { @@ -490,7 +500,7 @@ void Mir2Lir::InstallFillArrayData() { } } -static int AssignLiteralOffsetCommon(LIR* lir, int offset) { +static int AssignLiteralOffsetCommon(LIR* lir, CodeOffset offset) { for (; lir != NULL; lir = lir->next) { lir->offset = offset; offset += 4; @@ -498,6 +508,17 @@ static int AssignLiteralOffsetCommon(LIR* lir, int offset) { return offset; } +static int AssignLiteralPointerOffsetCommon(LIR* lir, CodeOffset offset) { + unsigned int element_size = sizeof(void*); + // Align to natural pointer size. + offset = (offset + (element_size - 1)) & ~(element_size - 1); + for (; lir != NULL; lir = lir->next) { + lir->offset = offset; + offset += element_size; + } + return offset; +} + // Make sure we have a code address for every declared catch entry bool Mir2Lir::VerifyCatchEntries() { bool success = true; @@ -607,8 +628,8 @@ class NativePcToReferenceMapBuilder { table_index = (table_index + 1) % entries_; } in_use_[table_index] = true; - SetNativeOffset(table_index, native_offset); - DCHECK_EQ(native_offset, GetNativeOffset(table_index)); + SetCodeOffset(table_index, native_offset); + DCHECK_EQ(native_offset, GetCodeOffset(table_index)); SetReferences(table_index, references); } @@ -617,7 +638,7 @@ class NativePcToReferenceMapBuilder { return NativePcOffsetToReferenceMap::Hash(native_offset) % entries_; } - uint32_t GetNativeOffset(size_t table_index) { + uint32_t GetCodeOffset(size_t table_index) { uint32_t native_offset = 0; size_t table_offset = (table_index * EntryWidth()) + sizeof(uint32_t); for (size_t i = 0; i < native_offset_width_; i++) { @@ -626,7 +647,7 @@ class NativePcToReferenceMapBuilder { return native_offset; } - void SetNativeOffset(size_t table_index, uint32_t native_offset) { + void SetCodeOffset(size_t table_index, uint32_t native_offset) { size_t table_offset = (table_index * EntryWidth()) + sizeof(uint32_t); for (size_t i = 0; i < native_offset_width_; i++) { (*table_)[table_offset + i] = (native_offset >> (i * 8)) & 0xFF; @@ -681,17 +702,17 @@ void Mir2Lir::CreateNativeGcMap() { } /* Determine the offset of each literal field */ -int Mir2Lir::AssignLiteralOffset(int offset) { +int Mir2Lir::AssignLiteralOffset(CodeOffset offset) { offset = AssignLiteralOffsetCommon(literal_list_, offset); - offset = AssignLiteralOffsetCommon(code_literal_list_, offset); - offset = AssignLiteralOffsetCommon(method_literal_list_, offset); + offset = AssignLiteralPointerOffsetCommon(code_literal_list_, offset); + offset = AssignLiteralPointerOffsetCommon(method_literal_list_, offset); return offset; } -int Mir2Lir::AssignSwitchTablesOffset(int offset) { +int Mir2Lir::AssignSwitchTablesOffset(CodeOffset offset) { GrowableArray::Iterator iterator(&switch_tables_); while (true) { - Mir2Lir::SwitchTable *tab_rec = iterator.Next(); + Mir2Lir::SwitchTable* tab_rec = iterator.Next(); if (tab_rec == NULL) break; tab_rec->offset = offset; if (tab_rec->table[0] == Instruction::kSparseSwitchSignature) { @@ -705,7 +726,7 @@ int Mir2Lir::AssignSwitchTablesOffset(int offset) { return offset; } -int Mir2Lir::AssignFillArrayDataOffset(int offset) { +int Mir2Lir::AssignFillArrayDataOffset(CodeOffset offset) { GrowableArray::Iterator iterator(&fill_array_data_); while (true) { Mir2Lir::FillArrayData *tab_rec = iterator.Next(); @@ -725,7 +746,7 @@ int Mir2Lir::AssignFillArrayDataOffset(int offset) { * branch table during the assembly phase. All resource flags * are set to prevent code motion. KeyVal is just there for debugging. */ -LIR* Mir2Lir::InsertCaseLabel(int vaddr, int keyVal) { +LIR* Mir2Lir::InsertCaseLabel(DexOffset vaddr, int keyVal) { LIR* boundary_lir = &block_label_list_[mir_graph_->FindBlock(vaddr)->id]; LIR* res = boundary_lir; if (cu_->verbose) { @@ -743,10 +764,10 @@ LIR* Mir2Lir::InsertCaseLabel(int vaddr, int keyVal) { return res; } -void Mir2Lir::MarkPackedCaseLabels(Mir2Lir::SwitchTable *tab_rec) { +void Mir2Lir::MarkPackedCaseLabels(Mir2Lir::SwitchTable* tab_rec) { const uint16_t* table = tab_rec->table; - int base_vaddr = tab_rec->vaddr; - const int *targets = reinterpret_cast(&table[4]); + DexOffset base_vaddr = tab_rec->vaddr; + const int32_t *targets = reinterpret_cast(&table[4]); int entries = table[1]; int low_key = s4FromSwitchData(&table[2]); for (int i = 0; i < entries; i++) { @@ -754,12 +775,12 @@ void Mir2Lir::MarkPackedCaseLabels(Mir2Lir::SwitchTable *tab_rec) { } } -void Mir2Lir::MarkSparseCaseLabels(Mir2Lir::SwitchTable *tab_rec) { +void Mir2Lir::MarkSparseCaseLabels(Mir2Lir::SwitchTable* tab_rec) { const uint16_t* table = tab_rec->table; - int base_vaddr = tab_rec->vaddr; + DexOffset base_vaddr = tab_rec->vaddr; int entries = table[1]; - const int* keys = reinterpret_cast(&table[2]); - const int* targets = &keys[entries]; + const int32_t* keys = reinterpret_cast(&table[2]); + const int32_t* targets = &keys[entries]; for (int i = 0; i < entries; i++) { tab_rec->targets[i] = InsertCaseLabel(base_vaddr + targets[i], keys[i]); } @@ -792,8 +813,8 @@ void Mir2Lir::DumpSparseSwitchTable(const uint16_t* table) { */ uint16_t ident = table[0]; int entries = table[1]; - const int* keys = reinterpret_cast(&table[2]); - const int* targets = &keys[entries]; + const int32_t* keys = reinterpret_cast(&table[2]); + const int32_t* targets = &keys[entries]; LOG(INFO) << "Sparse switch table - ident:0x" << std::hex << ident << ", entries: " << std::dec << entries; for (int i = 0; i < entries; i++) { @@ -812,7 +833,7 @@ void Mir2Lir::DumpPackedSwitchTable(const uint16_t* table) { * Total size is (4+size*2) 16-bit code units. */ uint16_t ident = table[0]; - const int* targets = reinterpret_cast(&table[4]); + const int32_t* targets = reinterpret_cast(&table[4]); int entries = table[1]; int low_key = s4FromSwitchData(&table[2]); LOG(INFO) << "Packed switch table - ident:0x" << std::hex << ident @@ -824,8 +845,9 @@ void Mir2Lir::DumpPackedSwitchTable(const uint16_t* table) { } /* Set up special LIR to mark a Dalvik byte-code instruction start for pretty printing */ -void Mir2Lir::MarkBoundary(int offset, const char* inst_str) { - NewLIR1(kPseudoDalvikByteCodeBoundary, reinterpret_cast(inst_str)); +void Mir2Lir::MarkBoundary(DexOffset offset, const char* inst_str) { + // NOTE: only used for debug listings. + NewLIR1(kPseudoDalvikByteCodeBoundary, WrapPointer(ArenaStrdup(inst_str))); } bool Mir2Lir::EvaluateBranch(Instruction::Code opcode, int32_t src1, int32_t src2) { @@ -883,6 +905,7 @@ Mir2Lir::Mir2Lir(CompilationUnit* cu, MIRGraph* mir_graph, ArenaAllocator* arena intrinsic_launchpads_(arena, 2048, kGrowableArrayMisc), tempreg_info_(arena, 20, kGrowableArrayMisc), reginfo_map_(arena, 64, kGrowableArrayMisc), + pointer_storage_(arena, 128, kGrowableArrayMisc), data_offset_(0), total_size_(0), block_label_list_(NULL), @@ -900,6 +923,9 @@ Mir2Lir::Mir2Lir(CompilationUnit* cu, MIRGraph* mir_graph, ArenaAllocator* arena promotion_map_ = static_cast (arena_->Alloc((cu_->num_dalvik_registers + cu_->num_compiler_temps + 1) * sizeof(promotion_map_[0]), ArenaAllocator::kAllocRegAlloc)); + // Reserve pointer id 0 for NULL. + size_t null_idx = WrapPointer(NULL); + DCHECK_EQ(null_idx, 0U); } void Mir2Lir::Materialize() { diff --git a/compiler/dex/quick/gen_common.cc b/compiler/dex/quick/gen_common.cc index 2670c23a0dd..2b3404a9c75 100644 --- a/compiler/dex/quick/gen_common.cc +++ b/compiler/dex/quick/gen_common.cc @@ -40,7 +40,7 @@ void Mir2Lir::GenBarrier() { barrier->u.m.def_mask = ENCODE_ALL; } -// FIXME: need to do some work to split out targets with +// TODO: need to do some work to split out targets with // condition codes and those without LIR* Mir2Lir::GenCheck(ConditionCode c_code, ThrowKind kind) { DCHECK_NE(cu_->instruction_set, kMips); @@ -503,7 +503,7 @@ void Mir2Lir::HandleSuspendLaunchPads() { ResetRegPool(); ResetDefTracking(); LIR* lab = suspend_launchpads_.Get(i); - LIR* resume_lab = reinterpret_cast(lab->operands[0]); + LIR* resume_lab = reinterpret_cast(UnwrapPointer(lab->operands[0])); current_dalvik_offset_ = lab->operands[1]; AppendLIR(lab); int r_tgt = CallHelperSetup(helper_offset); @@ -518,12 +518,12 @@ void Mir2Lir::HandleIntrinsicLaunchPads() { ResetRegPool(); ResetDefTracking(); LIR* lab = intrinsic_launchpads_.Get(i); - CallInfo* info = reinterpret_cast(lab->operands[0]); + CallInfo* info = reinterpret_cast(UnwrapPointer(lab->operands[0])); current_dalvik_offset_ = info->offset; AppendLIR(lab); // NOTE: GenInvoke handles MarkSafepointPC GenInvoke(info); - LIR* resume_lab = reinterpret_cast(lab->operands[2]); + LIR* resume_lab = reinterpret_cast(UnwrapPointer(lab->operands[2])); if (resume_lab != NULL) { OpUnconditionalBranch(resume_lab); } @@ -1351,7 +1351,7 @@ static bool IsPopCountLE2(unsigned int x) { } // Returns the index of the lowest set bit in 'x'. -static int LowestSetBit(unsigned int x) { +static int32_t LowestSetBit(uint32_t x) { int bit_posn = 0; while ((x & 0xf) == 0) { bit_posn += 4; @@ -1752,8 +1752,8 @@ void Mir2Lir::GenSuspendTest(int opt_flags) { FlushAllRegs(); LIR* branch = OpTestSuspend(NULL); LIR* ret_lab = NewLIR0(kPseudoTargetLabel); - LIR* target = RawLIR(current_dalvik_offset_, kPseudoSuspendTarget, - reinterpret_cast(ret_lab), current_dalvik_offset_); + LIR* target = RawLIR(current_dalvik_offset_, kPseudoSuspendTarget, WrapPointer(ret_lab), + current_dalvik_offset_); branch->target = target; suspend_launchpads_.Insert(target); } @@ -1766,8 +1766,8 @@ void Mir2Lir::GenSuspendTestAndBranch(int opt_flags, LIR* target) { } OpTestSuspend(target); LIR* launch_pad = - RawLIR(current_dalvik_offset_, kPseudoSuspendTarget, - reinterpret_cast(target), current_dalvik_offset_); + RawLIR(current_dalvik_offset_, kPseudoSuspendTarget, WrapPointer(target), + current_dalvik_offset_); FlushAllRegs(); OpUnconditionalBranch(launch_pad); suspend_launchpads_.Insert(launch_pad); diff --git a/compiler/dex/quick/gen_invoke.cc b/compiler/dex/quick/gen_invoke.cc index 0a0cc17ab27..3def7f54040 100644 --- a/compiler/dex/quick/gen_invoke.cc +++ b/compiler/dex/quick/gen_invoke.cc @@ -908,7 +908,7 @@ bool Mir2Lir::GenInlinedCharAt(CallInfo* info) { LoadWordDisp(rl_obj.low_reg, value_offset, reg_ptr); if (range_check) { // Set up a launch pad to allow retry in case of bounds violation */ - launch_pad = RawLIR(0, kPseudoIntrinsicRetry, reinterpret_cast(info)); + launch_pad = RawLIR(0, kPseudoIntrinsicRetry, WrapPointer(info)); intrinsic_launchpads_.Insert(launch_pad); OpRegReg(kOpCmp, rl_idx.low_reg, reg_max); FreeTemp(reg_max); @@ -919,7 +919,7 @@ bool Mir2Lir::GenInlinedCharAt(CallInfo* info) { reg_max = AllocTemp(); LoadWordDisp(rl_obj.low_reg, count_offset, reg_max); // Set up a launch pad to allow retry in case of bounds violation */ - launch_pad = RawLIR(0, kPseudoIntrinsicRetry, reinterpret_cast(info)); + launch_pad = RawLIR(0, kPseudoIntrinsicRetry, WrapPointer(info)); intrinsic_launchpads_.Insert(launch_pad); OpRegReg(kOpCmp, rl_idx.low_reg, reg_max); FreeTemp(reg_max); @@ -1085,7 +1085,7 @@ bool Mir2Lir::GenInlinedIndexOf(CallInfo* info, bool zero_based) { } int r_tgt = (cu_->instruction_set != kX86) ? LoadHelper(QUICK_ENTRYPOINT_OFFSET(pIndexOf)) : 0; GenNullCheck(rl_obj.s_reg_low, reg_ptr, info->opt_flags); - LIR* launch_pad = RawLIR(0, kPseudoIntrinsicRetry, reinterpret_cast(info)); + LIR* launch_pad = RawLIR(0, kPseudoIntrinsicRetry, WrapPointer(info)); intrinsic_launchpads_.Insert(launch_pad); OpCmpImmBranch(kCondGt, reg_char, 0xFFFF, launch_pad); // NOTE: not a safepoint @@ -1095,7 +1095,7 @@ bool Mir2Lir::GenInlinedIndexOf(CallInfo* info, bool zero_based) { OpThreadMem(kOpBlx, QUICK_ENTRYPOINT_OFFSET(pIndexOf)); } LIR* resume_tgt = NewLIR0(kPseudoTargetLabel); - launch_pad->operands[2] = reinterpret_cast(resume_tgt); + launch_pad->operands[2] = WrapPointer(resume_tgt); // Record that we've already inlined & null checked info->opt_flags |= (MIR_INLINED | MIR_IGNORE_NULL_CHECK); RegLocation rl_return = GetReturn(false); @@ -1123,7 +1123,7 @@ bool Mir2Lir::GenInlinedStringCompareTo(CallInfo* info) { LoadHelper(QUICK_ENTRYPOINT_OFFSET(pStringCompareTo)) : 0; GenNullCheck(rl_this.s_reg_low, reg_this, info->opt_flags); // TUNING: check if rl_cmp.s_reg_low is already null checked - LIR* launch_pad = RawLIR(0, kPseudoIntrinsicRetry, reinterpret_cast(info)); + LIR* launch_pad = RawLIR(0, kPseudoIntrinsicRetry, WrapPointer(info)); intrinsic_launchpads_.Insert(launch_pad); OpCmpImmBranch(kCondEq, reg_cmp, 0, launch_pad); // NOTE: not a safepoint diff --git a/compiler/dex/quick/mips/assemble_mips.cc b/compiler/dex/quick/mips/assemble_mips.cc index 6bfccfd575f..ea8b7a64c19 100644 --- a/compiler/dex/quick/mips/assemble_mips.cc +++ b/compiler/dex/quick/mips/assemble_mips.cc @@ -489,12 +489,12 @@ void MipsMir2Lir::ConvertShortToLongBranch(LIR* lir) { LIR* curr_pc = RawLIR(dalvik_offset, kMipsCurrPC); InsertLIRBefore(lir, curr_pc); LIR* anchor = RawLIR(dalvik_offset, kPseudoTargetLabel); - LIR* delta_hi = RawLIR(dalvik_offset, kMipsDeltaHi, r_AT, 0, - reinterpret_cast(anchor), 0, 0, lir->target); + LIR* delta_hi = RawLIR(dalvik_offset, kMipsDeltaHi, r_AT, 0, WrapPointer(anchor), 0, 0, + lir->target); InsertLIRBefore(lir, delta_hi); InsertLIRBefore(lir, anchor); - LIR* delta_lo = RawLIR(dalvik_offset, kMipsDeltaLo, r_AT, 0, - reinterpret_cast(anchor), 0, 0, lir->target); + LIR* delta_lo = RawLIR(dalvik_offset, kMipsDeltaLo, r_AT, 0, WrapPointer(anchor), 0, 0, + lir->target); InsertLIRBefore(lir, delta_lo); LIR* addu = RawLIR(dalvik_offset, kMipsAddu, r_AT, r_AT, r_RA); InsertLIRBefore(lir, addu); @@ -512,7 +512,7 @@ void MipsMir2Lir::ConvertShortToLongBranch(LIR* lir) { * instruction. In those cases we will try to substitute a new code * sequence or request that the trace be shortened and retried. */ -AssemblerStatus MipsMir2Lir::AssembleInstructions(uintptr_t start_addr) { +AssemblerStatus MipsMir2Lir::AssembleInstructions(CodeOffset start_addr) { LIR *lir; AssemblerStatus res = kSuccess; // Assume success @@ -538,8 +538,8 @@ AssemblerStatus MipsMir2Lir::AssembleInstructions(uintptr_t start_addr) { * and is found in lir->target. If operands[3] is non-NULL, * then it is a Switch/Data table. */ - int offset1 = (reinterpret_cast(lir->operands[2]))->offset; - SwitchTable *tab_rec = reinterpret_cast(lir->operands[3]); + int offset1 = (reinterpret_cast(UnwrapPointer(lir->operands[2])))->offset; + EmbeddedData *tab_rec = reinterpret_cast(UnwrapPointer(lir->operands[3])); int offset2 = tab_rec ? tab_rec->offset : lir->target->offset; int delta = offset2 - offset1; if ((delta & 0xffff) == delta && ((delta & 0x8000) == 0)) { @@ -565,21 +565,21 @@ AssemblerStatus MipsMir2Lir::AssembleInstructions(uintptr_t start_addr) { res = kRetryAll; } } else if (lir->opcode == kMipsDeltaLo) { - int offset1 = (reinterpret_cast(lir->operands[2]))->offset; - SwitchTable *tab_rec = reinterpret_cast(lir->operands[3]); + int offset1 = (reinterpret_cast(UnwrapPointer(lir->operands[2])))->offset; + EmbeddedData *tab_rec = reinterpret_cast(UnwrapPointer(lir->operands[3])); int offset2 = tab_rec ? tab_rec->offset : lir->target->offset; int delta = offset2 - offset1; lir->operands[1] = delta & 0xffff; } else if (lir->opcode == kMipsDeltaHi) { - int offset1 = (reinterpret_cast(lir->operands[2]))->offset; - SwitchTable *tab_rec = reinterpret_cast(lir->operands[3]); + int offset1 = (reinterpret_cast(UnwrapPointer(lir->operands[2])))->offset; + EmbeddedData *tab_rec = reinterpret_cast(UnwrapPointer(lir->operands[3])); int offset2 = tab_rec ? tab_rec->offset : lir->target->offset; int delta = offset2 - offset1; lir->operands[1] = (delta >> 16) & 0xffff; } else if (lir->opcode == kMipsB || lir->opcode == kMipsBal) { LIR *target_lir = lir->target; - uintptr_t pc = lir->offset + 4; - uintptr_t target = target_lir->offset; + CodeOffset pc = lir->offset + 4; + CodeOffset target = target_lir->offset; int delta = target - pc; if (delta & 0x3) { LOG(FATAL) << "PC-rel offset not multiple of 4: " << delta; @@ -592,8 +592,8 @@ AssemblerStatus MipsMir2Lir::AssembleInstructions(uintptr_t start_addr) { } } else if (lir->opcode >= kMipsBeqz && lir->opcode <= kMipsBnez) { LIR *target_lir = lir->target; - uintptr_t pc = lir->offset + 4; - uintptr_t target = target_lir->offset; + CodeOffset pc = lir->offset + 4; + CodeOffset target = target_lir->offset; int delta = target - pc; if (delta & 0x3) { LOG(FATAL) << "PC-rel offset not multiple of 4: " << delta; @@ -606,8 +606,8 @@ AssemblerStatus MipsMir2Lir::AssembleInstructions(uintptr_t start_addr) { } } else if (lir->opcode == kMipsBeq || lir->opcode == kMipsBne) { LIR *target_lir = lir->target; - uintptr_t pc = lir->offset + 4; - uintptr_t target = target_lir->offset; + CodeOffset pc = lir->offset + 4; + CodeOffset target = target_lir->offset; int delta = target - pc; if (delta & 0x3) { LOG(FATAL) << "PC-rel offset not multiple of 4: " << delta; @@ -619,8 +619,8 @@ AssemblerStatus MipsMir2Lir::AssembleInstructions(uintptr_t start_addr) { lir->operands[2] = delta >> 2; } } else if (lir->opcode == kMipsJal) { - uintptr_t cur_pc = (start_addr + lir->offset + 4) & ~3; - uintptr_t target = lir->operands[0]; + CodeOffset cur_pc = (start_addr + lir->offset + 4) & ~3; + CodeOffset target = lir->operands[0]; /* ensure PC-region branch can be used */ DCHECK_EQ((cur_pc & 0xF0000000), (target & 0xF0000000)); if (target & 0x3) { @@ -629,11 +629,11 @@ AssemblerStatus MipsMir2Lir::AssembleInstructions(uintptr_t start_addr) { lir->operands[0] = target >> 2; } else if (lir->opcode == kMipsLahi) { /* ld address hi (via lui) */ LIR *target_lir = lir->target; - uintptr_t target = start_addr + target_lir->offset; + CodeOffset target = start_addr + target_lir->offset; lir->operands[1] = target >> 16; } else if (lir->opcode == kMipsLalo) { /* ld address lo (via ori) */ LIR *target_lir = lir->target; - uintptr_t target = start_addr + target_lir->offset; + CodeOffset target = start_addr + target_lir->offset; lir->operands[2] = lir->operands[2] + target; } } diff --git a/compiler/dex/quick/mips/call_mips.cc b/compiler/dex/quick/mips/call_mips.cc index 9a5ca2ce67f..18c8cf87f2c 100644 --- a/compiler/dex/quick/mips/call_mips.cc +++ b/compiler/dex/quick/mips/call_mips.cc @@ -59,14 +59,14 @@ void MipsMir2Lir::GenSpecialCase(BasicBlock* bb, MIR* mir, * done: * */ -void MipsMir2Lir::GenSparseSwitch(MIR* mir, uint32_t table_offset, +void MipsMir2Lir::GenSparseSwitch(MIR* mir, DexOffset table_offset, RegLocation rl_src) { const uint16_t* table = cu_->insns + current_dalvik_offset_ + table_offset; if (cu_->verbose) { DumpSparseSwitchTable(table); } // Add the table to the list - we'll process it later - SwitchTable *tab_rec = + SwitchTable* tab_rec = static_cast(arena_->Alloc(sizeof(SwitchTable), ArenaAllocator::kAllocData)); tab_rec->table = table; tab_rec->vaddr = current_dalvik_offset_; @@ -101,8 +101,7 @@ void MipsMir2Lir::GenSparseSwitch(MIR* mir, uint32_t table_offset, // Remember base label so offsets can be computed later tab_rec->anchor = base_label; int rBase = AllocTemp(); - NewLIR4(kMipsDelta, rBase, 0, reinterpret_cast(base_label), - reinterpret_cast(tab_rec)); + NewLIR4(kMipsDelta, rBase, 0, WrapPointer(base_label), WrapPointer(tab_rec)); OpRegRegReg(kOpAdd, rEnd, rEnd, rBase); // Grab switch test value @@ -138,20 +137,20 @@ void MipsMir2Lir::GenSparseSwitch(MIR* mir, uint32_t table_offset, * jr r_RA * done: */ -void MipsMir2Lir::GenPackedSwitch(MIR* mir, uint32_t table_offset, +void MipsMir2Lir::GenPackedSwitch(MIR* mir, DexOffset table_offset, RegLocation rl_src) { const uint16_t* table = cu_->insns + current_dalvik_offset_ + table_offset; if (cu_->verbose) { DumpPackedSwitchTable(table); } // Add the table to the list - we'll process it later - SwitchTable *tab_rec = + SwitchTable* tab_rec = static_cast(arena_->Alloc(sizeof(SwitchTable), ArenaAllocator::kAllocData)); tab_rec->table = table; tab_rec->vaddr = current_dalvik_offset_; int size = table[1]; tab_rec->targets = static_cast(arena_->Alloc(size * sizeof(LIR*), - ArenaAllocator::kAllocLIR)); + ArenaAllocator::kAllocLIR)); switch_tables_.Insert(tab_rec); // Get the switch value @@ -196,8 +195,7 @@ void MipsMir2Lir::GenPackedSwitch(MIR* mir, uint32_t table_offset, // Materialize the table base pointer int rBase = AllocTemp(); - NewLIR4(kMipsDelta, rBase, 0, reinterpret_cast(base_label), - reinterpret_cast(tab_rec)); + NewLIR4(kMipsDelta, rBase, 0, WrapPointer(base_label), WrapPointer(tab_rec)); // Load the displacement from the switch table int r_disp = AllocTemp(); @@ -222,10 +220,10 @@ void MipsMir2Lir::GenPackedSwitch(MIR* mir, uint32_t table_offset, * * Total size is 4+(width * size + 1)/2 16-bit code units. */ -void MipsMir2Lir::GenFillArrayData(uint32_t table_offset, RegLocation rl_src) { +void MipsMir2Lir::GenFillArrayData(DexOffset table_offset, RegLocation rl_src) { const uint16_t* table = cu_->insns + current_dalvik_offset_ + table_offset; // Add the table to the list - we'll process it later - FillArrayData *tab_rec = + FillArrayData* tab_rec = reinterpret_cast(arena_->Alloc(sizeof(FillArrayData), ArenaAllocator::kAllocData)); tab_rec->table = table; @@ -252,8 +250,7 @@ void MipsMir2Lir::GenFillArrayData(uint32_t table_offset, RegLocation rl_src) { LIR* base_label = NewLIR0(kPseudoTargetLabel); // Materialize a pointer to the fill data image - NewLIR4(kMipsDelta, rMIPS_ARG1, 0, reinterpret_cast(base_label), - reinterpret_cast(tab_rec)); + NewLIR4(kMipsDelta, rMIPS_ARG1, 0, WrapPointer(base_label), WrapPointer(tab_rec)); // And go... ClobberCalleeSave(); diff --git a/compiler/dex/quick/mips/codegen_mips.h b/compiler/dex/quick/mips/codegen_mips.h index 387fef35b82..0be20e8ea5a 100644 --- a/compiler/dex/quick/mips/codegen_mips.h +++ b/compiler/dex/quick/mips/codegen_mips.h @@ -74,7 +74,7 @@ class MipsMir2Lir : public Mir2Lir { void AssembleLIR(); int AssignInsnOffsets(); void AssignOffsets(); - AssemblerStatus AssembleInstructions(uintptr_t start_addr); + AssemblerStatus AssembleInstructions(CodeOffset start_addr); void DumpResourceMask(LIR* lir, uint64_t mask, const char* prefix); void SetupTargetResourceMasks(LIR* lir, uint64_t flags); const char* GetTargetInstFmt(int opcode); diff --git a/compiler/dex/quick/mips/utility_mips.cc b/compiler/dex/quick/mips/utility_mips.cc index 5d9ae339216..2ba2c8487dd 100644 --- a/compiler/dex/quick/mips/utility_mips.cc +++ b/compiler/dex/quick/mips/utility_mips.cc @@ -93,7 +93,7 @@ LIR* MipsMir2Lir::LoadConstantNoClobber(int r_dest, int value) { } else if ((value < 0) && (value >= -32768)) { res = NewLIR3(kMipsAddiu, r_dest, r_ZERO, value); } else { - res = NewLIR2(kMipsLui, r_dest, value>>16); + res = NewLIR2(kMipsLui, r_dest, value >> 16); if (value & 0xffff) NewLIR3(kMipsOri, r_dest, r_dest, value); } diff --git a/compiler/dex/quick/mir_to_lir-inl.h b/compiler/dex/quick/mir_to_lir-inl.h index f293700bb99..1a30b7aef07 100644 --- a/compiler/dex/quick/mir_to_lir-inl.h +++ b/compiler/dex/quick/mir_to_lir-inl.h @@ -43,7 +43,7 @@ inline void Mir2Lir::ClobberBody(RegisterInfo* p) { } } -inline LIR* Mir2Lir::RawLIR(int dalvik_offset, int opcode, int op0, +inline LIR* Mir2Lir::RawLIR(DexOffset dalvik_offset, int opcode, int op0, int op1, int op2, int op3, int op4, LIR* target) { LIR* insn = static_cast(arena_->Alloc(sizeof(LIR), ArenaAllocator::kAllocLIR)); insn->dalvik_offset = dalvik_offset; diff --git a/compiler/dex/quick/mir_to_lir.cc b/compiler/dex/quick/mir_to_lir.cc index 2b26c3de807..197e200fc3b 100644 --- a/compiler/dex/quick/mir_to_lir.cc +++ b/compiler/dex/quick/mir_to_lir.cc @@ -241,9 +241,9 @@ void Mir2Lir::CompileDalvikInstruction(MIR* mir, BasicBlock* bb, LIR* label_list case Instruction::GOTO_16: case Instruction::GOTO_32: if (mir_graph_->IsBackedge(bb, bb->taken)) { - GenSuspendTestAndBranch(opt_flags, &label_list[bb->taken->id]); + GenSuspendTestAndBranch(opt_flags, &label_list[bb->taken]); } else { - OpUnconditionalBranch(&label_list[bb->taken->id]); + OpUnconditionalBranch(&label_list[bb->taken]); } break; @@ -272,23 +272,22 @@ void Mir2Lir::CompileDalvikInstruction(MIR* mir, BasicBlock* bb, LIR* label_list case Instruction::IF_GE: case Instruction::IF_GT: case Instruction::IF_LE: { - LIR* taken = &label_list[bb->taken->id]; - LIR* fall_through = &label_list[bb->fall_through->id]; + LIR* taken = &label_list[bb->taken]; + LIR* fall_through = &label_list[bb->fall_through]; // Result known at compile time? if (rl_src[0].is_const && rl_src[1].is_const) { bool is_taken = EvaluateBranch(opcode, mir_graph_->ConstantValue(rl_src[0].orig_sreg), mir_graph_->ConstantValue(rl_src[1].orig_sreg)); - BasicBlock* target = is_taken ? bb->taken : bb->fall_through; - if (mir_graph_->IsBackedge(bb, target)) { + BasicBlockId target_id = is_taken ? bb->taken : bb->fall_through; + if (mir_graph_->IsBackedge(bb, target_id)) { GenSuspendTest(opt_flags); } - OpUnconditionalBranch(&label_list[target->id]); + OpUnconditionalBranch(&label_list[target_id]); } else { if (mir_graph_->IsBackwardsBranch(bb)) { GenSuspendTest(opt_flags); } - GenCompareAndBranch(opcode, rl_src[0], rl_src[1], taken, - fall_through); + GenCompareAndBranch(opcode, rl_src[0], rl_src[1], taken, fall_through); } break; } @@ -299,16 +298,16 @@ void Mir2Lir::CompileDalvikInstruction(MIR* mir, BasicBlock* bb, LIR* label_list case Instruction::IF_GEZ: case Instruction::IF_GTZ: case Instruction::IF_LEZ: { - LIR* taken = &label_list[bb->taken->id]; - LIR* fall_through = &label_list[bb->fall_through->id]; + LIR* taken = &label_list[bb->taken]; + LIR* fall_through = &label_list[bb->fall_through]; // Result known at compile time? if (rl_src[0].is_const) { bool is_taken = EvaluateBranch(opcode, mir_graph_->ConstantValue(rl_src[0].orig_sreg), 0); - BasicBlock* target = is_taken ? bb->taken : bb->fall_through; - if (mir_graph_->IsBackedge(bb, target)) { + BasicBlockId target_id = is_taken ? bb->taken : bb->fall_through; + if (mir_graph_->IsBackedge(bb, target_id)) { GenSuspendTest(opt_flags); } - OpUnconditionalBranch(&label_list[target->id]); + OpUnconditionalBranch(&label_list[target_id]); } else { if (mir_graph_->IsBackwardsBranch(bb)) { GenSuspendTest(opt_flags); @@ -831,8 +830,9 @@ void Mir2Lir::MethodMIR2LIR() { while (curr_bb != NULL) { MethodBlockCodeGen(curr_bb); // If the fall_through block is no longer laid out consecutively, drop in a branch. - if ((curr_bb->fall_through != NULL) && (curr_bb->fall_through != next_bb)) { - OpUnconditionalBranch(&block_label_list_[curr_bb->fall_through->id]); + BasicBlock* curr_bb_fall_through = mir_graph_->GetBasicBlock(curr_bb->fall_through); + if ((curr_bb_fall_through != NULL) && (curr_bb_fall_through != next_bb)) { + OpUnconditionalBranch(&block_label_list_[curr_bb->fall_through]); } curr_bb = next_bb; do { diff --git a/compiler/dex/quick/mir_to_lir.h b/compiler/dex/quick/mir_to_lir.h index 5df2672fe2b..d629b44ddbd 100644 --- a/compiler/dex/quick/mir_to_lir.h +++ b/compiler/dex/quick/mir_to_lir.h @@ -30,6 +30,14 @@ namespace art { +/* + * TODO: refactoring pass to move these (and other) typdefs towards usage style of runtime to + * add type safety (see runtime/offsets.h). + */ +typedef uint32_t DexOffset; // Dex offset in code units. +typedef uint16_t NarrowDexOffset; // For use in structs, Dex offsets range from 0 .. 0xffff. +typedef uint32_t CodeOffset; // Native code offset in bytes. + // Set to 1 to measure cost of suspend check. #define NO_SUSPEND 0 @@ -119,8 +127,8 @@ struct AssemblyInfo { }; struct LIR { - int offset; // Offset of this instruction. - uint16_t dalvik_offset; // Offset of Dalvik opcode in code units (16-bit words). + CodeOffset offset; // Offset of this instruction. + NarrowDexOffset dalvik_offset; // Offset of Dalvik opcode in code units (16-bit words). int16_t opcode; LIR* next; LIR* prev; @@ -134,10 +142,10 @@ struct LIR { unsigned int fixup:8; // Fixup kind. } flags; union { - UseDefMasks m; // Use & Def masks used during optimization. - AssemblyInfo a; // Instruction encoding used during assembly phase. + UseDefMasks m; // Use & Def masks used during optimization. + AssemblyInfo a; // Instruction encoding used during assembly phase. } u; - int operands[5]; // [0..4] = [dest, src1, src2, extra, extra2]. + int32_t operands[5]; // [0..4] = [dest, src1, src2, extra, extra2]. }; // Target-specific initialization. @@ -184,19 +192,23 @@ Mir2Lir* X86CodeGenerator(CompilationUnit* const cu, MIRGraph* const mir_graph, class Mir2Lir : public Backend { public: - struct SwitchTable { - int offset; - const uint16_t* table; // Original dex table. - int vaddr; // Dalvik offset of switch opcode. - LIR* anchor; // Reference instruction for relative offsets. - LIR** targets; // Array of case targets. + /* + * Auxiliary information describing the location of data embedded in the Dalvik + * byte code stream. + */ + struct EmbeddedData { + CodeOffset offset; // Code offset of data block. + const uint16_t* table; // Original dex data. + DexOffset vaddr; // Dalvik offset of parent opcode. }; - struct FillArrayData { - int offset; - const uint16_t* table; // Original dex table. - int size; - int vaddr; // Dalvik offset of FILL_ARRAY_DATA opcode. + struct FillArrayData : EmbeddedData { + int32_t size; + }; + + struct SwitchTable : EmbeddedData { + LIR* anchor; // Reference instruction for relative offsets. + LIR** targets; // Array of case targets. }; /* Static register use counts */ @@ -260,6 +272,34 @@ class Mir2Lir : public Backend { return (opcode < 0); } + /* + * LIR operands are 32-bit integers. Sometimes, (especially for managing + * instructions which require PC-relative fixups), we need the operands to carry + * pointers. To do this, we assign these pointers an index in pointer_storage_, and + * hold that index in the operand array. + * TUNING: If use of these utilities becomes more common on 32-bit builds, it + * may be worth conditionally-compiling a set of identity functions here. + */ + uint32_t WrapPointer(void* pointer) { + uint32_t res = pointer_storage_.Size(); + pointer_storage_.Insert(pointer); + return res; + } + + void* UnwrapPointer(size_t index) { + return pointer_storage_.Get(index); + } + + // strdup(), but allocates from the arena. + char* ArenaStrdup(const char* str) { + size_t len = strlen(str) + 1; + char* res = reinterpret_cast(arena_->Alloc(len, ArenaAllocator::kAllocMisc)); + if (res != NULL) { + strncpy(res, str, len); + } + return res; + } + // Shared by all targets - implemented in codegen_util.cc void AppendLIR(LIR* lir); void InsertLIRBefore(LIR* current_lir, LIR* new_lir); @@ -277,7 +317,7 @@ class Mir2Lir : public Backend { void DumpLIRInsn(LIR* arg, unsigned char* base_addr); void DumpPromotionMap(); void CodegenDump(); - LIR* RawLIR(int dalvik_offset, int opcode, int op0 = 0, int op1 = 0, + LIR* RawLIR(DexOffset dalvik_offset, int opcode, int op0 = 0, int op1 = 0, int op2 = 0, int op3 = 0, int op4 = 0, LIR* target = NULL); LIR* NewLIR0(int opcode); LIR* NewLIR1(int opcode, int dest); @@ -292,7 +332,7 @@ class Mir2Lir : public Backend { void ProcessSwitchTables(); void DumpSparseSwitchTable(const uint16_t* table); void DumpPackedSwitchTable(const uint16_t* table); - void MarkBoundary(int offset, const char* inst_str); + void MarkBoundary(DexOffset offset, const char* inst_str); void NopLIR(LIR* lir); void UnlinkLIR(LIR* lir); bool EvaluateBranch(Instruction::Code opcode, int src1, int src2); @@ -307,12 +347,12 @@ class Mir2Lir : public Backend { bool VerifyCatchEntries(); void CreateMappingTables(); void CreateNativeGcMap(); - int AssignLiteralOffset(int offset); - int AssignSwitchTablesOffset(int offset); - int AssignFillArrayDataOffset(int offset); - LIR* InsertCaseLabel(int vaddr, int keyVal); - void MarkPackedCaseLabels(Mir2Lir::SwitchTable *tab_rec); - void MarkSparseCaseLabels(Mir2Lir::SwitchTable *tab_rec); + int AssignLiteralOffset(CodeOffset offset); + int AssignSwitchTablesOffset(CodeOffset offset); + int AssignFillArrayDataOffset(CodeOffset offset); + LIR* InsertCaseLabel(DexOffset vaddr, int keyVal); + void MarkPackedCaseLabels(Mir2Lir::SwitchTable* tab_rec); + void MarkSparseCaseLabels(Mir2Lir::SwitchTable* tab_rec); // Shared by all targets - implemented in local_optimizations.cc void ConvertMemOpIntoMove(LIR* orig_lir, int dest, int src); @@ -642,7 +682,7 @@ class Mir2Lir : public Backend { virtual void GenEntrySequence(RegLocation* ArgLocs, RegLocation rl_method) = 0; virtual void GenExitSequence() = 0; - virtual void GenFillArrayData(uint32_t table_offset, + virtual void GenFillArrayData(DexOffset table_offset, RegLocation rl_src) = 0; virtual void GenFusedFPCmpBranch(BasicBlock* bb, MIR* mir, bool gt_bias, bool is_double) = 0; @@ -655,9 +695,9 @@ class Mir2Lir : public Backend { int second_bit) = 0; virtual void GenNegDouble(RegLocation rl_dest, RegLocation rl_src) = 0; virtual void GenNegFloat(RegLocation rl_dest, RegLocation rl_src) = 0; - virtual void GenPackedSwitch(MIR* mir, uint32_t table_offset, + virtual void GenPackedSwitch(MIR* mir, DexOffset table_offset, RegLocation rl_src) = 0; - virtual void GenSparseSwitch(MIR* mir, uint32_t table_offset, + virtual void GenSparseSwitch(MIR* mir, DexOffset table_offset, RegLocation rl_src) = 0; virtual void GenSpecialCase(BasicBlock* bb, MIR* mir, SpecialCaseHandler special_case) = 0; @@ -672,13 +712,10 @@ class Mir2Lir : public Backend { // Required for target - single operation generators. virtual LIR* OpUnconditionalBranch(LIR* target) = 0; - virtual LIR* OpCmpBranch(ConditionCode cond, int src1, int src2, - LIR* target) = 0; - virtual LIR* OpCmpImmBranch(ConditionCode cond, int reg, int check_value, - LIR* target) = 0; + virtual LIR* OpCmpBranch(ConditionCode cond, int src1, int src2, LIR* target) = 0; + virtual LIR* OpCmpImmBranch(ConditionCode cond, int reg, int check_value, LIR* target) = 0; virtual LIR* OpCondBranch(ConditionCode cc, LIR* target) = 0; - virtual LIR* OpDecAndBranch(ConditionCode c_code, int reg, - LIR* target) = 0; + virtual LIR* OpDecAndBranch(ConditionCode c_code, int reg, LIR* target) = 0; virtual LIR* OpFpRegCopy(int r_dest, int r_src) = 0; virtual LIR* OpIT(ConditionCode cond, const char* guide) = 0; virtual LIR* OpMem(OpKind op, int rBase, int disp) = 0; @@ -690,16 +727,13 @@ class Mir2Lir : public Backend { virtual LIR* OpRegMem(OpKind op, int r_dest, int rBase, int offset) = 0; virtual LIR* OpRegReg(OpKind op, int r_dest_src1, int r_src2) = 0; virtual LIR* OpRegRegImm(OpKind op, int r_dest, int r_src1, int value) = 0; - virtual LIR* OpRegRegReg(OpKind op, int r_dest, int r_src1, - int r_src2) = 0; + virtual LIR* OpRegRegReg(OpKind op, int r_dest, int r_src1, int r_src2) = 0; virtual LIR* OpTestSuspend(LIR* target) = 0; virtual LIR* OpThreadMem(OpKind op, ThreadOffset thread_offset) = 0; virtual LIR* OpVldm(int rBase, int count) = 0; virtual LIR* OpVstm(int rBase, int count) = 0; - virtual void OpLea(int rBase, int reg1, int reg2, int scale, - int offset) = 0; - virtual void OpRegCopyWide(int dest_lo, int dest_hi, int src_lo, - int src_hi) = 0; + virtual void OpLea(int rBase, int reg1, int reg2, int scale, int offset) = 0; + virtual void OpRegCopyWide(int dest_lo, int dest_hi, int src_lo, int src_hi) = 0; virtual void OpTlsCmp(ThreadOffset offset, int val) = 0; virtual bool InexpensiveConstantInt(int32_t value) = 0; virtual bool InexpensiveConstantFloat(int32_t value) = 0; @@ -752,6 +786,7 @@ class Mir2Lir : public Backend { GrowableArray intrinsic_launchpads_; GrowableArray tempreg_info_; GrowableArray reginfo_map_; + GrowableArray pointer_storage_; /* * Holds mapping from native PC to dex PC for safepoints where we may deoptimize. * Native PC is on the return address of the safepointed operation. Dex PC is for @@ -763,9 +798,9 @@ class Mir2Lir : public Backend { * immediately preceed the instruction. */ std::vector dex2pc_mapping_table_; - int current_code_offset_; // Working byte offset of machine instructons. - int data_offset_; // starting offset of literal pool. - int total_size_; // header + code size. + CodeOffset current_code_offset_; // Working byte offset of machine instructons. + CodeOffset data_offset_; // starting offset of literal pool. + size_t total_size_; // header + code size. LIR* block_label_list_; PromotionMap* promotion_map_; /* @@ -777,8 +812,8 @@ class Mir2Lir : public Backend { * in the CompilationUnit struct before codegen for each instruction. * The low-level LIR creation utilites will pull it from here. Rework this. */ - int current_dalvik_offset_; - int estimated_native_code_size_; // Just an estimate; used to reserve code_buffer_ size. + DexOffset current_dalvik_offset_; + size_t estimated_native_code_size_; // Just an estimate; used to reserve code_buffer_ size. RegisterPool* reg_pool_; /* * Sanity checking for the register temp tracking. The same ssa diff --git a/compiler/dex/quick/ralloc_util.cc b/compiler/dex/quick/ralloc_util.cc index 7927ff9864f..41a57afca19 100644 --- a/compiler/dex/quick/ralloc_util.cc +++ b/compiler/dex/quick/ralloc_util.cc @@ -66,10 +66,9 @@ void Mir2Lir::DumpRegPool(RegisterInfo* p, int num_regs) { LOG(INFO) << "================================================"; for (int i = 0; i < num_regs; i++) { LOG(INFO) << StringPrintf( - "R[%d]: T:%d, U:%d, P:%d, p:%d, LV:%d, D:%d, SR:%d, ST:%x, EN:%x", + "R[%d]: T:%d, U:%d, P:%d, p:%d, LV:%d, D:%d, SR:%d", p[i].reg, p[i].is_temp, p[i].in_use, p[i].pair, p[i].partner, - p[i].live, p[i].dirty, p[i].s_reg, reinterpret_cast(p[i].def_start), - reinterpret_cast(p[i].def_end)); + p[i].live, p[i].dirty, p[i].s_reg); } LOG(INFO) << "================================================"; } @@ -769,9 +768,9 @@ RegLocation Mir2Lir::UpdateRawLoc(RegLocation loc) { RegLocation Mir2Lir::EvalLocWide(RegLocation loc, int reg_class, bool update) { DCHECK(loc.wide); - int new_regs; - int low_reg; - int high_reg; + int32_t new_regs; + int32_t low_reg; + int32_t high_reg; loc = UpdateLocWide(loc); diff --git a/compiler/dex/quick/x86/assemble_x86.cc b/compiler/dex/quick/x86/assemble_x86.cc index 064ff31bcd7..fb8e75fc1b3 100644 --- a/compiler/dex/quick/x86/assemble_x86.cc +++ b/compiler/dex/quick/x86/assemble_x86.cc @@ -1090,11 +1090,13 @@ void X86Mir2Lir::EmitPcRel(const X86EncodingMap* entry, uint8_t reg, int base_or_table, uint8_t index, int scale, int table_or_disp) { int disp; if (entry->opcode == kX86PcRelLoadRA) { - Mir2Lir::SwitchTable *tab_rec = reinterpret_cast(table_or_disp); + Mir2Lir::EmbeddedData *tab_rec = + reinterpret_cast(UnwrapPointer(table_or_disp)); disp = tab_rec->offset; } else { DCHECK(entry->opcode == kX86PcRelAdr); - Mir2Lir::FillArrayData *tab_rec = reinterpret_cast(base_or_table); + Mir2Lir::EmbeddedData *tab_rec = + reinterpret_cast(UnwrapPointer(base_or_table)); disp = tab_rec->offset; } if (entry->skeleton.prefix1 != 0) { @@ -1161,7 +1163,7 @@ void X86Mir2Lir::EmitUnimplemented(const X86EncodingMap* entry, LIR* lir) { * instruction. In those cases we will try to substitute a new code * sequence or request that the trace be shortened and retried. */ -AssemblerStatus X86Mir2Lir::AssembleInstructions(uintptr_t start_addr) { +AssemblerStatus X86Mir2Lir::AssembleInstructions(CodeOffset start_addr) { LIR *lir; AssemblerStatus res = kSuccess; // Assume success @@ -1181,13 +1183,13 @@ AssemblerStatus X86Mir2Lir::AssembleInstructions(uintptr_t start_addr) { LIR *target_lir = lir->target; DCHECK(target_lir != NULL); int delta = 0; - uintptr_t pc; + CodeOffset pc; if (IS_SIMM8(lir->operands[0])) { pc = lir->offset + 2 /* opcode + rel8 */; } else { pc = lir->offset + 6 /* 2 byte opcode + rel32 */; } - uintptr_t target = target_lir->offset; + CodeOffset target = target_lir->offset; delta = target - pc; if (IS_SIMM8(delta) != IS_SIMM8(lir->operands[0])) { if (kVerbosePcFixup) { @@ -1211,8 +1213,8 @@ AssemblerStatus X86Mir2Lir::AssembleInstructions(uintptr_t start_addr) { case kX86Jcc32: { LIR *target_lir = lir->target; DCHECK(target_lir != NULL); - uintptr_t pc = lir->offset + 6 /* 2 byte opcode + rel32 */; - uintptr_t target = target_lir->offset; + CodeOffset pc = lir->offset + 6 /* 2 byte opcode + rel32 */; + CodeOffset target = target_lir->offset; int delta = target - pc; if (kVerbosePcFixup) { LOG(INFO) << "Source:"; @@ -1228,13 +1230,13 @@ AssemblerStatus X86Mir2Lir::AssembleInstructions(uintptr_t start_addr) { LIR *target_lir = lir->target; DCHECK(target_lir != NULL); int delta = 0; - uintptr_t pc; + CodeOffset pc; if (IS_SIMM8(lir->operands[0])) { pc = lir->offset + 2 /* opcode + rel8 */; } else { pc = lir->offset + 5 /* opcode + rel32 */; } - uintptr_t target = target_lir->offset; + CodeOffset target = target_lir->offset; delta = target - pc; if (!(cu_->disable_opt & (1 << kSafeOptimizations)) && delta == 0) { // Useless branch @@ -1257,8 +1259,8 @@ AssemblerStatus X86Mir2Lir::AssembleInstructions(uintptr_t start_addr) { case kX86Jmp32: { LIR *target_lir = lir->target; DCHECK(target_lir != NULL); - uintptr_t pc = lir->offset + 5 /* opcode + rel32 */; - uintptr_t target = target_lir->offset; + CodeOffset pc = lir->offset + 5 /* opcode + rel32 */; + CodeOffset target = target_lir->offset; int delta = target - pc; lir->operands[0] = delta; break; diff --git a/compiler/dex/quick/x86/call_x86.cc b/compiler/dex/quick/x86/call_x86.cc index 7fad6f07d1d..17924b0f080 100644 --- a/compiler/dex/quick/x86/call_x86.cc +++ b/compiler/dex/quick/x86/call_x86.cc @@ -31,15 +31,15 @@ void X86Mir2Lir::GenSpecialCase(BasicBlock* bb, MIR* mir, * The sparse table in the literal pool is an array of * pairs. */ -void X86Mir2Lir::GenSparseSwitch(MIR* mir, uint32_t table_offset, +void X86Mir2Lir::GenSparseSwitch(MIR* mir, DexOffset table_offset, RegLocation rl_src) { const uint16_t* table = cu_->insns + current_dalvik_offset_ + table_offset; if (cu_->verbose) { DumpSparseSwitchTable(table); } int entries = table[1]; - const int* keys = reinterpret_cast(&table[2]); - const int* targets = &keys[entries]; + const int32_t* keys = reinterpret_cast(&table[2]); + const int32_t* targets = &keys[entries]; rl_src = LoadValue(rl_src, kCoreReg); for (int i = 0; i < entries; i++) { int key = keys[i]; @@ -66,15 +66,15 @@ void X86Mir2Lir::GenSparseSwitch(MIR* mir, uint32_t table_offset, * jmp r_start_of_method * done: */ -void X86Mir2Lir::GenPackedSwitch(MIR* mir, uint32_t table_offset, +void X86Mir2Lir::GenPackedSwitch(MIR* mir, DexOffset table_offset, RegLocation rl_src) { const uint16_t* table = cu_->insns + current_dalvik_offset_ + table_offset; if (cu_->verbose) { DumpPackedSwitchTable(table); } // Add the table to the list - we'll process it later - SwitchTable *tab_rec = - static_cast(arena_->Alloc(sizeof(SwitchTable), ArenaAllocator::kAllocData)); + SwitchTable* tab_rec = + static_cast(arena_->Alloc(sizeof(SwitchTable), ArenaAllocator::kAllocData)); tab_rec->table = table; tab_rec->vaddr = current_dalvik_offset_; int size = table[1]; @@ -103,8 +103,7 @@ void X86Mir2Lir::GenPackedSwitch(MIR* mir, uint32_t table_offset, // Load the displacement from the switch table int disp_reg = AllocTemp(); - NewLIR5(kX86PcRelLoadRA, disp_reg, start_of_method_reg, keyReg, 2, - reinterpret_cast(tab_rec)); + NewLIR5(kX86PcRelLoadRA, disp_reg, start_of_method_reg, keyReg, 2, WrapPointer(tab_rec)); // Add displacement to start of method OpRegReg(kOpAdd, start_of_method_reg, disp_reg); // ..and go! @@ -126,10 +125,10 @@ void X86Mir2Lir::GenPackedSwitch(MIR* mir, uint32_t table_offset, * * Total size is 4+(width * size + 1)/2 16-bit code units. */ -void X86Mir2Lir::GenFillArrayData(uint32_t table_offset, RegLocation rl_src) { +void X86Mir2Lir::GenFillArrayData(DexOffset table_offset, RegLocation rl_src) { const uint16_t* table = cu_->insns + current_dalvik_offset_ + table_offset; // Add the table to the list - we'll process it later - FillArrayData *tab_rec = + FillArrayData* tab_rec = static_cast(arena_->Alloc(sizeof(FillArrayData), ArenaAllocator::kAllocData)); tab_rec->table = table; tab_rec->vaddr = current_dalvik_offset_; @@ -144,7 +143,7 @@ void X86Mir2Lir::GenFillArrayData(uint32_t table_offset, RegLocation rl_src) { LoadValueDirectFixed(rl_src, rX86_ARG0); // Materialize a pointer to the fill data image NewLIR1(kX86StartOfMethod, rX86_ARG2); - NewLIR2(kX86PcRelAdr, rX86_ARG1, reinterpret_cast(tab_rec)); + NewLIR2(kX86PcRelAdr, rX86_ARG1, WrapPointer(tab_rec)); NewLIR2(kX86Add32RR, rX86_ARG1, rX86_ARG2); CallRuntimeHelperRegReg(QUICK_ENTRYPOINT_OFFSET(pHandleFillArrayData), rX86_ARG0, rX86_ARG1, true); diff --git a/compiler/dex/quick/x86/codegen_x86.h b/compiler/dex/quick/x86/codegen_x86.h index c266e39bbef..b1d95ffcdfd 100644 --- a/compiler/dex/quick/x86/codegen_x86.h +++ b/compiler/dex/quick/x86/codegen_x86.h @@ -74,7 +74,7 @@ class X86Mir2Lir : public Mir2Lir { void AssembleLIR(); int AssignInsnOffsets(); void AssignOffsets(); - AssemblerStatus AssembleInstructions(uintptr_t start_addr); + AssemblerStatus AssembleInstructions(CodeOffset start_addr); void DumpResourceMask(LIR* lir, uint64_t mask, const char* prefix); void SetupTargetResourceMasks(LIR* lir, uint64_t flags); const char* GetTargetInstFmt(int opcode); @@ -119,7 +119,7 @@ class X86Mir2Lir : public Mir2Lir { void GenDivZeroCheck(int reg_lo, int reg_hi); void GenEntrySequence(RegLocation* ArgLocs, RegLocation rl_method); void GenExitSequence(); - void GenFillArrayData(uint32_t table_offset, RegLocation rl_src); + void GenFillArrayData(DexOffset table_offset, RegLocation rl_src); void GenFusedFPCmpBranch(BasicBlock* bb, MIR* mir, bool gt_bias, bool is_double); void GenFusedLongCmpBranch(BasicBlock* bb, MIR* mir); void GenSelect(BasicBlock* bb, MIR* mir); @@ -129,8 +129,8 @@ class X86Mir2Lir : public Mir2Lir { int lit, int first_bit, int second_bit); void GenNegDouble(RegLocation rl_dest, RegLocation rl_src); void GenNegFloat(RegLocation rl_dest, RegLocation rl_src); - void GenPackedSwitch(MIR* mir, uint32_t table_offset, RegLocation rl_src); - void GenSparseSwitch(MIR* mir, uint32_t table_offset, RegLocation rl_src); + void GenPackedSwitch(MIR* mir, DexOffset table_offset, RegLocation rl_src); + void GenSparseSwitch(MIR* mir, DexOffset table_offset, RegLocation rl_src); void GenSpecialCase(BasicBlock* bb, MIR* mir, SpecialCaseHandler special_case); // Single operation generators. diff --git a/compiler/dex/quick/x86/fp_x86.cc b/compiler/dex/quick/x86/fp_x86.cc index f736b5e28fc..c9d6bfc8cce 100644 --- a/compiler/dex/quick/x86/fp_x86.cc +++ b/compiler/dex/quick/x86/fp_x86.cc @@ -284,8 +284,8 @@ void X86Mir2Lir::GenCmpFP(Instruction::Code code, RegLocation rl_dest, void X86Mir2Lir::GenFusedFPCmpBranch(BasicBlock* bb, MIR* mir, bool gt_bias, bool is_double) { - LIR* taken = &block_label_list_[bb->taken->id]; - LIR* not_taken = &block_label_list_[bb->fall_through->id]; + LIR* taken = &block_label_list_[bb->taken]; + LIR* not_taken = &block_label_list_[bb->fall_through]; LIR* branch = NULL; RegLocation rl_src1; RegLocation rl_src2; diff --git a/compiler/dex/quick/x86/int_x86.cc b/compiler/dex/quick/x86/int_x86.cc index 14f5348c125..324d975fc84 100644 --- a/compiler/dex/quick/x86/int_x86.cc +++ b/compiler/dex/quick/x86/int_x86.cc @@ -166,7 +166,7 @@ void X86Mir2Lir::GenSelect(BasicBlock* bb, MIR* mir) { } void X86Mir2Lir::GenFusedLongCmpBranch(BasicBlock* bb, MIR* mir) { - LIR* taken = &block_label_list_[bb->taken->id]; + LIR* taken = &block_label_list_[bb->taken]; RegLocation rl_src1 = mir_graph_->GetSrcWide(mir, 0); RegLocation rl_src2 = mir_graph_->GetSrcWide(mir, 2); FlushAllRegs(); diff --git a/compiler/dex/quick/x86/target_x86.cc b/compiler/dex/quick/x86/target_x86.cc index 0f005dab26f..901ac9e69d9 100644 --- a/compiler/dex/quick/x86/target_x86.cc +++ b/compiler/dex/quick/x86/target_x86.cc @@ -223,7 +223,7 @@ std::string X86Mir2Lir::BuildInsnString(const char *fmt, LIR *lir, unsigned char buf += StringPrintf("%d", operand); break; case 'p': { - SwitchTable *tab_rec = reinterpret_cast(operand); + EmbeddedData *tab_rec = reinterpret_cast(UnwrapPointer(operand)); buf += StringPrintf("0x%08x", tab_rec->offset); break; } @@ -238,7 +238,7 @@ std::string X86Mir2Lir::BuildInsnString(const char *fmt, LIR *lir, unsigned char break; case 't': buf += StringPrintf("0x%08x (L%p)", - reinterpret_cast(base_addr) + reinterpret_cast(base_addr) + lir->offset + operand, lir->target); break; default: diff --git a/compiler/dex/ssa_transformation.cc b/compiler/dex/ssa_transformation.cc index 0ca5fd4aa3e..eb0d412bae3 100644 --- a/compiler/dex/ssa_transformation.cc +++ b/compiler/dex/ssa_transformation.cc @@ -38,18 +38,18 @@ BasicBlock* MIRGraph::NeedsVisit(BasicBlock* bb) { } BasicBlock* MIRGraph::NextUnvisitedSuccessor(BasicBlock* bb) { - BasicBlock* res = NeedsVisit(bb->fall_through); + BasicBlock* res = NeedsVisit(GetBasicBlock(bb->fall_through)); if (res == NULL) { - res = NeedsVisit(bb->taken); + res = NeedsVisit(GetBasicBlock(bb->taken)); if (res == NULL) { - if (bb->successor_block_list.block_list_type != kNotUsed) { - GrowableArray::Iterator iterator(bb->successor_block_list.blocks); + if (bb->successor_block_list_type != kNotUsed) { + GrowableArray::Iterator iterator(bb->successor_blocks); while (true) { SuccessorBlockInfo *sbi = iterator.Next(); if (sbi == NULL) { break; } - res = NeedsVisit(sbi->block); + res = NeedsVisit(GetBasicBlock(sbi->block)); if (res != NULL) { break; } @@ -63,7 +63,9 @@ BasicBlock* MIRGraph::NextUnvisitedSuccessor(BasicBlock* bb) { void MIRGraph::MarkPreOrder(BasicBlock* block) { block->visited = true; /* Enqueue the pre_order block id */ - dfs_order_->Insert(block->id); + if (block->id != NullBasicBlockId) { + dfs_order_->Insert(block->id); + } } void MIRGraph::RecordDFSOrders(BasicBlock* block) { @@ -79,7 +81,9 @@ void MIRGraph::RecordDFSOrders(BasicBlock* block) { continue; } curr->dfs_id = dfs_post_order_->Size(); - dfs_post_order_->Insert(curr->id); + if (curr->id != NullBasicBlockId) { + dfs_post_order_->Insert(curr->id); + } succ.pop_back(); } } @@ -88,7 +92,8 @@ void MIRGraph::RecordDFSOrders(BasicBlock* block) { void MIRGraph::ComputeDFSOrders() { /* Initialize or reset the DFS pre_order list */ if (dfs_order_ == NULL) { - dfs_order_ = new (arena_) GrowableArray(arena_, GetNumBlocks(), kGrowableArrayDfsOrder); + dfs_order_ = new (arena_) GrowableArray(arena_, GetNumBlocks(), + kGrowableArrayDfsOrder); } else { /* Just reset the used length on the counter */ dfs_order_->Reset(); @@ -96,7 +101,8 @@ void MIRGraph::ComputeDFSOrders() { /* Initialize or reset the DFS post_order list */ if (dfs_post_order_ == NULL) { - dfs_post_order_ = new (arena_) GrowableArray(arena_, GetNumBlocks(), kGrowableArrayDfsPostOrder); + dfs_post_order_ = new (arena_) GrowableArray(arena_, GetNumBlocks(), + kGrowableArrayDfsPostOrder); } else { /* Just reset the used length on the counter */ dfs_post_order_->Reset(); @@ -169,7 +175,7 @@ void MIRGraph::ComputeDomPostOrderTraversal(BasicBlock* bb) { if (dom_post_order_traversal_ == NULL) { // First time - create the array. dom_post_order_traversal_ = - new (arena_) GrowableArray(arena_, num_reachable_blocks_, + new (arena_) GrowableArray(arena_, num_reachable_blocks_, kGrowableArrayDomPostOrderTraversal); } else { dom_post_order_traversal_->Reset(); @@ -193,11 +199,13 @@ void MIRGraph::ComputeDomPostOrderTraversal(BasicBlock* bb) { std::make_pair(new_bb, new (arena_) ArenaBitVector::Iterator(new_bb->i_dominated))); } else { // no successor/next - dom_post_order_traversal_->Insert(curr_bb->id); + if (curr_bb->id != NullBasicBlockId) { + dom_post_order_traversal_->Insert(curr_bb->id); + } work_stack.pop_back(); /* hacky loop detection */ - if (curr_bb->taken && curr_bb->dominators->IsBitSet(curr_bb->taken->id)) { + if ((curr_bb->taken != NullBasicBlockId) && curr_bb->dominators->IsBitSet(curr_bb->taken)) { attributes_ |= METHOD_HAS_LOOP; } } @@ -210,7 +218,7 @@ void MIRGraph::CheckForDominanceFrontier(BasicBlock* dom_bb, * TODO - evaluate whether phi will ever need to be inserted into exit * blocks. */ - if (succ_bb->i_dom != dom_bb && + if (succ_bb->i_dom != dom_bb->id && succ_bb->block_type == kDalvikByteCode && succ_bb->hidden == false) { dom_bb->dom_frontier->SetBit(succ_bb->id); @@ -220,20 +228,20 @@ void MIRGraph::CheckForDominanceFrontier(BasicBlock* dom_bb, /* Worker function to compute the dominance frontier */ bool MIRGraph::ComputeDominanceFrontier(BasicBlock* bb) { /* Calculate DF_local */ - if (bb->taken) { - CheckForDominanceFrontier(bb, bb->taken); + if (bb->taken != NullBasicBlockId) { + CheckForDominanceFrontier(bb, GetBasicBlock(bb->taken)); } - if (bb->fall_through) { - CheckForDominanceFrontier(bb, bb->fall_through); + if (bb->fall_through != NullBasicBlockId) { + CheckForDominanceFrontier(bb, GetBasicBlock(bb->fall_through)); } - if (bb->successor_block_list.block_list_type != kNotUsed) { - GrowableArray::Iterator iterator(bb->successor_block_list.blocks); + if (bb->successor_block_list_type != kNotUsed) { + GrowableArray::Iterator iterator(bb->successor_blocks); while (true) { SuccessorBlockInfo *successor_block_info = iterator.Next(); if (successor_block_info == NULL) { break; } - BasicBlock* succ_bb = successor_block_info->block; + BasicBlock* succ_bb = GetBasicBlock(successor_block_info->block); CheckForDominanceFrontier(bb, succ_bb); } } @@ -306,17 +314,17 @@ int MIRGraph::FindCommonParent(int block1, int block2) { /* Worker function to compute each block's immediate dominator */ bool MIRGraph::ComputeblockIDom(BasicBlock* bb) { /* Special-case entry block */ - if (bb == GetEntryBlock()) { + if ((bb->id == NullBasicBlockId) || (bb == GetEntryBlock())) { return false; } /* Iterate through the predecessors */ - GrowableArray::Iterator iter(bb->predecessors); + GrowableArray::Iterator iter(bb->predecessors); /* Find the first processed predecessor */ int idom = -1; while (true) { - BasicBlock* pred_bb = iter.Next(); + BasicBlock* pred_bb = GetBasicBlock(iter.Next()); CHECK(pred_bb != NULL); if (i_dom_list_[pred_bb->dfs_id] != NOTVISITED) { idom = pred_bb->dfs_id; @@ -326,7 +334,7 @@ bool MIRGraph::ComputeblockIDom(BasicBlock* bb) { /* Scan the rest of the predecessors */ while (true) { - BasicBlock* pred_bb = iter.Next(); + BasicBlock* pred_bb = GetBasicBlock(iter.Next()); if (!pred_bb) { break; } @@ -352,7 +360,7 @@ bool MIRGraph::ComputeBlockDominators(BasicBlock* bb) { if (bb == GetEntryBlock()) { bb->dominators->ClearAllBits(); } else { - bb->dominators->Copy(bb->i_dom->dominators); + bb->dominators->Copy(GetBasicBlock(bb->i_dom)->dominators); } bb->dominators->SetBit(bb->id); return false; @@ -364,7 +372,7 @@ bool MIRGraph::SetDominators(BasicBlock* bb) { DCHECK_NE(idom_dfs_idx, NOTVISITED); int i_dom_idx = dfs_post_order_->Get(idom_dfs_idx); BasicBlock* i_dom = GetBasicBlock(i_dom_idx); - bb->i_dom = i_dom; + bb->i_dom = i_dom->id; /* Add bb to the i_dominated set of the immediate dominator block */ i_dom->i_dominated->SetBit(bb->id); } @@ -412,7 +420,7 @@ void MIRGraph::ComputeDominators() { } else { temp_block_v_->ClearAllBits(); } - GetEntryBlock()->i_dom = NULL; + GetEntryBlock()->i_dom = 0; PreOrderDfsIterator iter3(this); for (BasicBlock* bb = iter3.Next(); bb != NULL; bb = iter3.Next()) { @@ -463,20 +471,22 @@ bool MIRGraph::ComputeBlockLiveIns(BasicBlock* bb) { return false; } temp_dalvik_register_v->Copy(bb->data_flow_info->live_in_v); - if (bb->taken && bb->taken->data_flow_info) - ComputeSuccLineIn(temp_dalvik_register_v, bb->taken->data_flow_info->live_in_v, + BasicBlock* bb_taken = GetBasicBlock(bb->taken); + BasicBlock* bb_fall_through = GetBasicBlock(bb->fall_through); + if (bb_taken && bb_taken->data_flow_info) + ComputeSuccLineIn(temp_dalvik_register_v, bb_taken->data_flow_info->live_in_v, bb->data_flow_info->def_v); - if (bb->fall_through && bb->fall_through->data_flow_info) - ComputeSuccLineIn(temp_dalvik_register_v, bb->fall_through->data_flow_info->live_in_v, + if (bb_fall_through && bb_fall_through->data_flow_info) + ComputeSuccLineIn(temp_dalvik_register_v, bb_fall_through->data_flow_info->live_in_v, bb->data_flow_info->def_v); - if (bb->successor_block_list.block_list_type != kNotUsed) { - GrowableArray::Iterator iterator(bb->successor_block_list.blocks); + if (bb->successor_block_list_type != kNotUsed) { + GrowableArray::Iterator iterator(bb->successor_blocks); while (true) { SuccessorBlockInfo *successor_block_info = iterator.Next(); if (successor_block_info == NULL) { break; } - BasicBlock* succ_bb = successor_block_info->block; + BasicBlock* succ_bb = GetBasicBlock(successor_block_info->block); if (succ_bb->data_flow_info) { ComputeSuccLineIn(temp_dalvik_register_v, succ_bb->data_flow_info->live_in_v, bb->data_flow_info->def_v); @@ -579,50 +589,37 @@ void MIRGraph::InsertPhiNodes() { * predecessor blocks */ bool MIRGraph::InsertPhiNodeOperands(BasicBlock* bb) { - MIR *mir; - std::vector uses; - std::vector incoming_arc; - /* Phi nodes are at the beginning of each block */ - for (mir = bb->first_mir_insn; mir != NULL; mir = mir->next) { + for (MIR* mir = bb->first_mir_insn; mir != NULL; mir = mir->next) { if (mir->dalvikInsn.opcode != static_cast(kMirOpPhi)) return true; int ssa_reg = mir->ssa_rep->defs[0]; DCHECK_GE(ssa_reg, 0); // Shouldn't see compiler temps here int v_reg = SRegToVReg(ssa_reg); - uses.clear(); - incoming_arc.clear(); - /* Iterate through the predecessors */ - GrowableArray::Iterator iter(bb->predecessors); + GrowableArray::Iterator iter(bb->predecessors); + size_t num_uses = bb->predecessors->Size(); + mir->ssa_rep->num_uses = num_uses; + int* uses = static_cast(arena_->Alloc(sizeof(int) * num_uses, + ArenaAllocator::kAllocDFInfo)); + mir->ssa_rep->uses = uses; + mir->ssa_rep->fp_use = + static_cast(arena_->Alloc(sizeof(bool) * num_uses, ArenaAllocator::kAllocDFInfo)); + BasicBlockId* incoming = + static_cast(arena_->Alloc(sizeof(BasicBlockId) * num_uses, + ArenaAllocator::kAllocDFInfo)); + mir->meta.phi_incoming = incoming; + int idx = 0; while (true) { - BasicBlock* pred_bb = iter.Next(); + BasicBlock* pred_bb = GetBasicBlock(iter.Next()); if (!pred_bb) { break; } int ssa_reg = pred_bb->data_flow_info->vreg_to_ssa_map[v_reg]; - uses.push_back(ssa_reg); - incoming_arc.push_back(pred_bb->id); - } - - /* Count the number of SSA registers for a Dalvik register */ - int num_uses = uses.size(); - mir->ssa_rep->num_uses = num_uses; - mir->ssa_rep->uses = - static_cast(arena_->Alloc(sizeof(int) * num_uses, ArenaAllocator::kAllocDFInfo)); - mir->ssa_rep->fp_use = - static_cast(arena_->Alloc(sizeof(bool) * num_uses, ArenaAllocator::kAllocDFInfo)); - int* incoming = - static_cast(arena_->Alloc(sizeof(int) * num_uses, ArenaAllocator::kAllocDFInfo)); - // TODO: Ugly, rework (but don't burden each MIR/LIR for Phi-only needs) - mir->dalvikInsn.vB = reinterpret_cast(incoming); - - /* Set the uses array for the phi node */ - int *use_ptr = mir->ssa_rep->uses; - for (int i = 0; i < num_uses; i++) { - *use_ptr++ = uses[i]; - *incoming++ = incoming_arc[i]; + uses[idx] = ssa_reg; + incoming[idx] = pred_bb->id; + idx++; } } @@ -644,24 +641,24 @@ void MIRGraph::DoDFSPreOrderSSARename(BasicBlock* block) { static_cast(arena_->Alloc(map_size, ArenaAllocator::kAllocDalvikToSSAMap)); memcpy(saved_ssa_map, vreg_to_ssa_map_, map_size); - if (block->fall_through) { - DoDFSPreOrderSSARename(block->fall_through); + if (block->fall_through != NullBasicBlockId) { + DoDFSPreOrderSSARename(GetBasicBlock(block->fall_through)); /* Restore SSA map snapshot */ memcpy(vreg_to_ssa_map_, saved_ssa_map, map_size); } - if (block->taken) { - DoDFSPreOrderSSARename(block->taken); + if (block->taken != NullBasicBlockId) { + DoDFSPreOrderSSARename(GetBasicBlock(block->taken)); /* Restore SSA map snapshot */ memcpy(vreg_to_ssa_map_, saved_ssa_map, map_size); } - if (block->successor_block_list.block_list_type != kNotUsed) { - GrowableArray::Iterator iterator(block->successor_block_list.blocks); + if (block->successor_block_list_type != kNotUsed) { + GrowableArray::Iterator iterator(block->successor_blocks); while (true) { SuccessorBlockInfo *successor_block_info = iterator.Next(); if (successor_block_info == NULL) { break; } - BasicBlock* succ_bb = successor_block_info->block; + BasicBlock* succ_bb = GetBasicBlock(successor_block_info->block); DoDFSPreOrderSSARename(succ_bb); /* Restore SSA map snapshot */ memcpy(vreg_to_ssa_map_, saved_ssa_map, map_size); From 8d31bbd3d6536de12bc20e3d29cfe03fe848f9da Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Sun, 13 Oct 2013 10:44:14 -0700 Subject: [PATCH 0094/2402] Throw IOException at source of failing to open a dex file. Before is: java.lang.ClassNotFoundException: Didn't find class "GCBench" on path: DexPathList[[zip file "/disk2/dalvik-dev/out/host/linux-x86/framework/GCBench.jar"],nativeLibraryDirectories=[/disk2/dalvik-dev/out/host/linux-x86/lib]] at dalvik.system.BaseDexClassLoader.findClass(BaseDexClassLoader.java:56) at java.lang.ClassLoader.loadClass(ClassLoader.java:511) at java.lang.ClassLoader.loadClass(ClassLoader.java:469) Suppressed: java.lang.ClassNotFoundException: GCBench at java.lang.Class.classForName(Native Method) at java.lang.BootClassLoader.findClass(ClassLoader.java:781) at java.lang.BootClassLoader.loadClass(ClassLoader.java:841) at java.lang.ClassLoader.loadClass(ClassLoader.java:504) ... 1 more Caused by: java.lang.NoClassDefFoundError: Class "LGCBench;" not found ... 5 more And after is: java.lang.ClassNotFoundException: Didn't find class "GCBench" on path: DexPathList[[zip file "/disk2/dalvik-dev/out/host/linux-x86/framework/GCBench.jar"],nativeLibraryDirectories=[/disk2/dalvik-dev/out/host/linux-x86/lib]] at dalvik.system.BaseDexClassLoader.findClass(BaseDexClassLoader.java:56) at java.lang.ClassLoader.loadClass(ClassLoader.java:511) at java.lang.ClassLoader.loadClass(ClassLoader.java:469) Suppressed: java.io.IOException: Zip archive '/disk2/dalvik-dev/out/host/linux-x86/framework/GCBench.jar' doesn't contain classes.dex at dalvik.system.DexFile.openDexFile(Native Method) at dalvik.system.DexFile.(DexFile.java:80) at dalvik.system.DexFile.(DexFile.java:59) at dalvik.system.DexPathList.loadDexFile(DexPathList.java:268) at dalvik.system.DexPathList.makeDexElements(DexPathList.java:235) at dalvik.system.DexPathList.(DexPathList.java:113) at dalvik.system.BaseDexClassLoader.(BaseDexClassLoader.java:48) at dalvik.system.PathClassLoader.(PathClassLoader.java:38) at java.lang.ClassLoader.createSystemClassLoader(ClassLoader.java:128) at java.lang.ClassLoader.access$000(ClassLoader.java:65) at java.lang.ClassLoader$SystemClassLoader.(ClassLoader.java:81) at java.lang.ClassLoader.getSystemClassLoader(ClassLoader.java:137) Suppressed: java.lang.ClassNotFoundException: GCBench at java.lang.Class.classForName(Native Method) at java.lang.BootClassLoader.findClass(ClassLoader.java:781) at java.lang.BootClassLoader.loadClass(ClassLoader.java:841) at java.lang.ClassLoader.loadClass(ClassLoader.java:504) ... 1 more Caused by: java.lang.NoClassDefFoundError: Class "LGCBench;" not found ... 5 more Also, move dex file verifier messages out of logs. In the process the ClassLinker::dex_lock_ needed tidying to cover a smaller scope. Bug 11301553. Change-Id: I80058652e11e7ea63457cc01a0cb48afe1c15543 --- compiler/dex/arena_allocator.cc | 4 +- compiler/elf_fixup.cc | 5 +- compiler/elf_stripper.cc | 11 +- compiler/elf_stripper.h | 4 +- compiler/elf_writer.cc | 5 +- compiler/elf_writer_test.cc | 17 +- compiler/image_test.cc | 7 +- compiler/image_writer.cc | 17 +- compiler/oat_test.cc | 9 +- dex2oat/dex2oat.cc | 49 +- oatdump/oatdump.cc | 34 +- runtime/base/macros.h | 2 + runtime/base/unix_file/fd_file.cc | 4 - runtime/base/unix_file/fd_file.h | 4 +- runtime/class_linker.cc | 373 ++++++++------- runtime/class_linker.h | 66 ++- runtime/common_test.h | 16 +- runtime/common_throws.cc | 9 + runtime/common_throws.h | 78 ++-- runtime/dex_file.cc | 157 ++++--- runtime/dex_file.h | 30 +- runtime/dex_file_test.cc | 15 +- runtime/dex_file_verifier.cc | 546 +++++++++++----------- runtime/dex_file_verifier.h | 42 +- runtime/dex_method_iterator_test.cc | 23 +- runtime/elf_file.cc | 63 +-- runtime/elf_file.h | 6 +- runtime/gc/accounting/atomic_stack.h | 6 +- runtime/gc/accounting/card_table.cc | 6 +- runtime/gc/accounting/gc_allocator.cc | 18 +- runtime/gc/accounting/gc_allocator.h | 83 ++-- runtime/gc/accounting/heap_bitmap.h | 4 +- runtime/gc/accounting/mod_union_table.h | 4 +- runtime/gc/accounting/space_bitmap.cc | 8 +- runtime/gc/accounting/space_bitmap.h | 2 +- runtime/gc/heap.cc | 5 +- runtime/gc/space/dlmalloc_space.cc | 10 +- runtime/gc/space/image_space.cc | 130 ++++-- runtime/gc/space/image_space.h | 11 +- runtime/gc/space/large_object_space.cc | 14 +- runtime/gc/space/large_object_space.h | 6 +- runtime/mem_map.cc | 28 +- runtime/mem_map.h | 13 +- runtime/mem_map_test.cc | 7 +- runtime/native/dalvik_system_DexFile.cc | 140 +++--- runtime/native/java_lang_VMClassLoader.cc | 6 +- runtime/oat_file.cc | 183 ++++---- runtime/oat_file.h | 29 +- runtime/utils.cc | 10 +- runtime/utils.h | 2 +- runtime/zip_archive.cc | 120 ++--- runtime/zip_archive.h | 22 +- runtime/zip_archive_test.cc | 11 +- 53 files changed, 1358 insertions(+), 1116 deletions(-) diff --git a/compiler/dex/arena_allocator.cc b/compiler/dex/arena_allocator.cc index 2da806437c7..95e44b3e0dc 100644 --- a/compiler/dex/arena_allocator.cc +++ b/compiler/dex/arena_allocator.cc @@ -50,7 +50,9 @@ Arena::Arena(size_t size) map_(nullptr), next_(nullptr) { if (kUseMemMap) { - map_ = MemMap::MapAnonymous("dalvik-arena", NULL, size, PROT_READ | PROT_WRITE); + std::string error_msg; + map_ = MemMap::MapAnonymous("dalvik-arena", NULL, size, PROT_READ | PROT_WRITE, &error_msg); + CHECK(map_ != nullptr) << error_msg; memory_ = map_->Begin(); size_ = map_->Size(); } else { diff --git a/compiler/elf_fixup.cc b/compiler/elf_fixup.cc index 359c4936a6e..c5712880c19 100644 --- a/compiler/elf_fixup.cc +++ b/compiler/elf_fixup.cc @@ -27,8 +27,9 @@ namespace art { static const bool DEBUG_FIXUP = false; bool ElfFixup::Fixup(File* file, uintptr_t oat_data_begin) { - UniquePtr elf_file(ElfFile::Open(file, true, false)); - CHECK(elf_file.get() != NULL); + std::string error_msg; + UniquePtr elf_file(ElfFile::Open(file, true, false, &error_msg)); + CHECK(elf_file.get() != nullptr) << error_msg; // Lookup "oatdata" symbol address. ::llvm::ELF::Elf32_Addr oatdata_address = ElfWriter::GetOatDataAddress(elf_file.get()); diff --git a/compiler/elf_stripper.cc b/compiler/elf_stripper.cc index 7fc662ca1d2..7ee8d3cae1b 100644 --- a/compiler/elf_stripper.cc +++ b/compiler/elf_stripper.cc @@ -27,9 +27,11 @@ namespace art { -bool ElfStripper::Strip(File* file) { - UniquePtr elf_file(ElfFile::Open(file, true, false)); - CHECK(elf_file.get() != NULL); +bool ElfStripper::Strip(File* file, std::string* error_msg) { + UniquePtr elf_file(ElfFile::Open(file, true, false, error_msg)); + if (elf_file.get() == nullptr) { + return false; + } // ELF files produced by MCLinker look roughly like this // @@ -120,7 +122,8 @@ bool ElfStripper::Strip(File* file) { elf_file->GetHeader().e_shoff = shoff; int result = ftruncate(file->Fd(), offset); if (result != 0) { - PLOG(ERROR) << "Failed to truncate while stripping ELF file: " << file->GetPath(); + *error_msg = StringPrintf("Failed to truncate while stripping ELF file: '%s': %s", + file->GetPath().c_str(), strerror(errno)); return false; } return true; diff --git a/compiler/elf_stripper.h b/compiler/elf_stripper.h index 6015b30cb2a..f1a1d4605de 100644 --- a/compiler/elf_stripper.h +++ b/compiler/elf_stripper.h @@ -17,6 +17,8 @@ #ifndef ART_COMPILER_ELF_STRIPPER_H_ #define ART_COMPILER_ELF_STRIPPER_H_ +#include + #include "base/macros.h" #include "os.h" @@ -26,7 +28,7 @@ class ElfStripper { public: // Strip an ELF file of unneeded debugging information. // Returns true on success, false on failure. - static bool Strip(File* file); + static bool Strip(File* file, std::string* error_msg); private: DISALLOW_IMPLICIT_CONSTRUCTORS(ElfStripper); diff --git a/compiler/elf_writer.cc b/compiler/elf_writer.cc index d3c13dd791d..0bfe4a424c5 100644 --- a/compiler/elf_writer.cc +++ b/compiler/elf_writer.cc @@ -47,8 +47,9 @@ llvm::ELF::Elf32_Addr ElfWriter::GetOatDataAddress(ElfFile* elf_file) { void ElfWriter::GetOatElfInformation(File* file, size_t& oat_loaded_size, size_t& oat_data_offset) { - UniquePtr elf_file(ElfFile::Open(file, false, false)); - CHECK(elf_file.get() != NULL); + std::string error_msg; + UniquePtr elf_file(ElfFile::Open(file, false, false, &error_msg)); + CHECK(elf_file.get() != NULL) << error_msg; oat_loaded_size = elf_file->GetLoadedSize(); CHECK_NE(0U, oat_loaded_size); diff --git a/compiler/elf_writer_test.cc b/compiler/elf_writer_test.cc index ffe1f729260..eca67a8a6e6 100644 --- a/compiler/elf_writer_test.cc +++ b/compiler/elf_writer_test.cc @@ -65,23 +65,26 @@ TEST_F(ElfWriterTest, dlsym) { UniquePtr file(OS::OpenFileForReading(elf_filename.c_str())); ASSERT_TRUE(file.get() != NULL); { - UniquePtr ef(ElfFile::Open(file.get(), false, false)); - CHECK(ef.get() != NULL); + std::string error_msg; + UniquePtr ef(ElfFile::Open(file.get(), false, false, &error_msg)); + CHECK(ef.get() != nullptr) << error_msg; EXPECT_ELF_FILE_ADDRESS(ef, dl_oatdata, "oatdata", false); EXPECT_ELF_FILE_ADDRESS(ef, dl_oatexec, "oatexec", false); EXPECT_ELF_FILE_ADDRESS(ef, dl_oatlastword, "oatlastword", false); } { - UniquePtr ef(ElfFile::Open(file.get(), false, false)); - CHECK(ef.get() != NULL); + std::string error_msg; + UniquePtr ef(ElfFile::Open(file.get(), false, false, &error_msg)); + CHECK(ef.get() != nullptr) << error_msg; EXPECT_ELF_FILE_ADDRESS(ef, dl_oatdata, "oatdata", true); EXPECT_ELF_FILE_ADDRESS(ef, dl_oatexec, "oatexec", true); EXPECT_ELF_FILE_ADDRESS(ef, dl_oatlastword, "oatlastword", true); } { - UniquePtr ef(ElfFile::Open(file.get(), false, true)); - CHECK(ef.get() != NULL); - ef->Load(false); + std::string error_msg; + UniquePtr ef(ElfFile::Open(file.get(), false, true, &error_msg)); + CHECK(ef.get() != nullptr) << error_msg; + CHECK(ef->Load(false, &error_msg)) << error_msg; EXPECT_EQ(dl_oatdata, ef->FindDynamicSymbolAddress("oatdata")); EXPECT_EQ(dl_oatexec, ef->FindDynamicSymbolAddress("oatexec")); EXPECT_EQ(dl_oatlastword, ef->FindDynamicSymbolAddress("oatlastword")); diff --git a/compiler/image_test.cc b/compiler/image_test.cc index d4be7c0cdc8..a8b7c881f48 100644 --- a/compiler/image_test.cc +++ b/compiler/image_test.cc @@ -112,8 +112,11 @@ TEST_F(ImageTest, WriteRead) { runtime_.reset(); java_lang_dex_file_ = NULL; - UniquePtr dex(DexFile::Open(GetLibCoreDexFileName(), GetLibCoreDexFileName())); - ASSERT_TRUE(dex.get() != NULL); + std::string error_msg; + UniquePtr dex(DexFile::Open(GetLibCoreDexFileName().c_str(), + GetLibCoreDexFileName().c_str(), + &error_msg)); + ASSERT_TRUE(dex.get() != nullptr) << error_msg; // Remove the reservation of the memory for use to load the image. UnreserveImageSpace(); diff --git a/compiler/image_writer.cc b/compiler/image_writer.cc index bcdc1c15c94..871cfd5c41b 100644 --- a/compiler/image_writer.cc +++ b/compiler/image_writer.cc @@ -82,12 +82,14 @@ bool ImageWriter::Write(const std::string& image_filename, LOG(ERROR) << "Failed to open oat file " << oat_filename << " for " << oat_location; return false; } - oat_file_ = OatFile::OpenWritable(oat_file.get(), oat_location); - if (oat_file_ == NULL) { - LOG(ERROR) << "Failed to open writable oat file " << oat_filename << " for " << oat_location; + std::string error_msg; + oat_file_ = OatFile::OpenWritable(oat_file.get(), oat_location, &error_msg); + if (oat_file_ == nullptr) { + LOG(ERROR) << "Failed to open writable oat file " << oat_filename << " for " << oat_location + << ": " << error_msg; return false; } - class_linker->RegisterOatFile(*oat_file_); + CHECK_EQ(class_linker->RegisterOatFile(oat_file_), oat_file_); interpreter_to_interpreter_bridge_offset_ = oat_file_->GetOatHeader().GetInterpreterToInterpreterBridgeOffset(); @@ -192,9 +194,10 @@ bool ImageWriter::AllocMemory() { int prot = PROT_READ | PROT_WRITE; size_t length = RoundUp(size, kPageSize); - image_.reset(MemMap::MapAnonymous("image writer image", NULL, length, prot)); - if (image_.get() == NULL) { - LOG(ERROR) << "Failed to allocate memory for image file generation"; + std::string error_msg; + image_.reset(MemMap::MapAnonymous("image writer image", NULL, length, prot, &error_msg)); + if (UNLIKELY(image_.get() == nullptr)) { + LOG(ERROR) << "Failed to allocate memory for image file generation: " << error_msg; return false; } return true; diff --git a/compiler/oat_test.cc b/compiler/oat_test.cc index 6ac5d6a16e6..634a160a974 100644 --- a/compiler/oat_test.cc +++ b/compiler/oat_test.cc @@ -100,8 +100,10 @@ TEST_F(OatTest, WriteRead) { base::TimingLogger timings("CommonTest::WriteRead", false, false); compiler_driver_->CompileAll(class_loader, class_linker->GetBootClassPath(), timings); } - UniquePtr oat_file(OatFile::Open(tmp.GetFilename(), tmp.GetFilename(), NULL, false)); - ASSERT_TRUE(oat_file.get() != NULL); + std::string error_msg; + UniquePtr oat_file(OatFile::Open(tmp.GetFilename(), tmp.GetFilename(), NULL, false, + &error_msg)); + ASSERT_TRUE(oat_file.get() != nullptr) << error_msg; const OatHeader& oat_header = oat_file->GetOatHeader(); ASSERT_TRUE(oat_header.IsValid()); ASSERT_EQ(1U, oat_header.GetDexFileCount()); // core @@ -111,8 +113,9 @@ TEST_F(OatTest, WriteRead) { const DexFile* dex_file = java_lang_dex_file_; uint32_t dex_file_checksum = dex_file->GetLocationChecksum(); - const OatFile::OatDexFile* oat_dex_file = oat_file->GetOatDexFile(dex_file->GetLocation(), + const OatFile::OatDexFile* oat_dex_file = oat_file->GetOatDexFile(dex_file->GetLocation().c_str(), &dex_file_checksum); + ASSERT_TRUE(oat_dex_file != nullptr); CHECK_EQ(dex_file->GetLocationChecksum(), oat_dex_file->GetDexFileLocationChecksum()); for (size_t i = 0; i < dex_file->NumClassDefs(); i++) { const DexFile::ClassDef& class_def = dex_file->GetClassDef(i); diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc index d5d1303f554..d8112ea7082 100644 --- a/dex2oat/dex2oat.cc +++ b/dex2oat/dex2oat.cc @@ -200,21 +200,24 @@ class Dex2Oat { } // Reads the class names (java.lang.Object) and returns a set of descriptors (Ljava/lang/Object;) - CompilerDriver::DescriptorSet* ReadImageClassesFromZip(const std::string& zip_filename, - const char* image_classes_filename) { - UniquePtr zip_archive(ZipArchive::Open(zip_filename)); + CompilerDriver::DescriptorSet* ReadImageClassesFromZip(const char* zip_filename, + const char* image_classes_filename, + std::string* error_msg) { + UniquePtr zip_archive(ZipArchive::Open(zip_filename, error_msg)); if (zip_archive.get() == NULL) { - LOG(ERROR) << "Failed to open zip file " << zip_filename; return NULL; } UniquePtr zip_entry(zip_archive->Find(image_classes_filename)); if (zip_entry.get() == NULL) { - LOG(ERROR) << "Failed to find " << image_classes_filename << " within " << zip_filename; + *error_msg = StringPrintf("Failed to find '%s' within '%s': %s", image_classes_filename, + zip_filename, error_msg->c_str()); return NULL; } - UniquePtr image_classes_file(zip_entry->ExtractToMemMap(image_classes_filename)); + UniquePtr image_classes_file(zip_entry->ExtractToMemMap(image_classes_filename, + error_msg)); if (image_classes_file.get() == NULL) { - LOG(ERROR) << "Failed to extract " << image_classes_filename << " from " << zip_filename; + *error_msg = StringPrintf("Failed to extract '%s' from '%s': %s", image_classes_filename, + zip_filename, error_msg->c_str()); return NULL; } const std::string image_classes_string(reinterpret_cast(image_classes_file->Begin()), @@ -368,9 +371,10 @@ class Dex2Oat { if (DexFilesContains(dex_files, parsed[i])) { continue; } - const DexFile* dex_file = DexFile::Open(parsed[i], parsed[i]); + std::string error_msg; + const DexFile* dex_file = DexFile::Open(parsed[i].c_str(), parsed[i].c_str(), &error_msg); if (dex_file == NULL) { - LOG(WARNING) << "Failed to open dex file " << parsed[i]; + LOG(WARNING) << "Failed to open dex file '" << parsed[i] << "': " << error_msg; } else { dex_files.push_back(dex_file); } @@ -416,9 +420,10 @@ static size_t OpenDexFiles(const std::vector& dex_filenames, for (size_t i = 0; i < dex_filenames.size(); i++) { const char* dex_filename = dex_filenames[i]; const char* dex_location = dex_locations[i]; - const DexFile* dex_file = DexFile::Open(dex_filename, dex_location); + std::string error_msg; + const DexFile* dex_file = DexFile::Open(dex_filename, dex_location, &error_msg); if (dex_file == NULL) { - LOG(WARNING) << "Failed to open .dex from file '" << dex_filename << "'\n"; + LOG(WARNING) << "Failed to open .dex from file '" << dex_filename << "': " << error_msg; ++failure_count; } else { dex_files.push_back(dex_file); @@ -887,14 +892,17 @@ static int dex2oat(int argc, char** argv) { // If --image-classes was specified, calculate the full list of classes to include in the image UniquePtr image_classes(NULL); if (image_classes_filename != NULL) { + std::string error_msg; if (image_classes_zip_filename != NULL) { image_classes.reset(dex2oat->ReadImageClassesFromZip(image_classes_zip_filename, - image_classes_filename)); + image_classes_filename, + &error_msg)); } else { image_classes.reset(dex2oat->ReadImageClassesFromFile(image_classes_filename)); } if (image_classes.get() == NULL) { - LOG(ERROR) << "Failed to create list of image classes from " << image_classes_filename; + LOG(ERROR) << "Failed to create list of image classes from '" << image_classes_filename << + "': " << error_msg; return EXIT_FAILURE; } } @@ -904,14 +912,18 @@ static int dex2oat(int argc, char** argv) { dex_files = Runtime::Current()->GetClassLinker()->GetBootClassPath(); } else { if (dex_filenames.empty()) { - UniquePtr zip_archive(ZipArchive::OpenFromFd(zip_fd)); + std::string error_msg; + UniquePtr zip_archive(ZipArchive::OpenFromFd(zip_fd, zip_location.c_str(), + &error_msg)); if (zip_archive.get() == NULL) { - LOG(ERROR) << "Failed to open zip from file descriptor for " << zip_location; + LOG(ERROR) << "Failed to open zip from file descriptor for '" << zip_location << "': " + << error_msg; return EXIT_FAILURE; } - const DexFile* dex_file = DexFile::Open(*zip_archive.get(), zip_location); + const DexFile* dex_file = DexFile::Open(*zip_archive.get(), zip_location, &error_msg); if (dex_file == NULL) { - LOG(ERROR) << "Failed to open dex from file descriptor for zip file: " << zip_location; + LOG(ERROR) << "Failed to open dex from file descriptor for zip file '" << zip_location + << "': " << error_msg; return EXIT_FAILURE; } dex_files.push_back(dex_file); @@ -1063,7 +1075,8 @@ static int dex2oat(int argc, char** argv) { // Strip unneeded sections for target off_t seek_actual = lseek(oat_file->Fd(), 0, SEEK_SET); CHECK_EQ(0, seek_actual); - ElfStripper::Strip(oat_file.get()); + std::string error_msg; + CHECK(ElfStripper::Strip(oat_file.get(), &error_msg)) << error_msg; // We wrote the oat file successfully, and want to keep it. diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc index 6db5813a1b7..ea06b02d4f6 100644 --- a/oatdump/oatdump.cc +++ b/oatdump/oatdump.cc @@ -173,9 +173,13 @@ class OatDumper { MethodHelper mh(m); for (size_t i = 0; i < oat_dex_files_.size(); i++) { const OatFile::OatDexFile* oat_dex_file = oat_dex_files_[i]; - CHECK(oat_dex_file != NULL); - UniquePtr dex_file(oat_dex_file->OpenDexFile()); - if (dex_file.get() != NULL) { + CHECK(oat_dex_file != nullptr); + std::string error_msg; + UniquePtr dex_file(oat_dex_file->OpenDexFile(&error_msg)); + if (dex_file.get() == nullptr) { + LOG(WARNING) << "Failed to open dex file '" << oat_dex_file->GetDexFileLocation() + << "': " << error_msg; + } else { const DexFile::ClassDef* class_def = dex_file->FindClassDef(mh.GetDeclaringClassDescriptor()); if (class_def != NULL) { @@ -199,8 +203,11 @@ class OatDumper { for (size_t i = 0; i < oat_dex_files_.size(); i++) { const OatFile::OatDexFile* oat_dex_file = oat_dex_files_[i]; CHECK(oat_dex_file != NULL); - UniquePtr dex_file(oat_dex_file->OpenDexFile()); - if (dex_file.get() == NULL) { + std::string error_msg; + UniquePtr dex_file(oat_dex_file->OpenDexFile(&error_msg)); + if (dex_file.get() == nullptr) { + LOG(WARNING) << "Failed to open dex file '" << oat_dex_file->GetDexFileLocation() + << "': " << error_msg; continue; } offsets_.insert(reinterpret_cast(&dex_file->GetHeader())); @@ -245,9 +252,10 @@ class OatDumper { os << "OAT DEX FILE:\n"; os << StringPrintf("location: %s\n", oat_dex_file.GetDexFileLocation().c_str()); os << StringPrintf("checksum: 0x%08x\n", oat_dex_file.GetDexFileLocationChecksum()); - UniquePtr dex_file(oat_dex_file.OpenDexFile()); + std::string error_msg; + UniquePtr dex_file(oat_dex_file.OpenDexFile(&error_msg)); if (dex_file.get() == NULL) { - os << "NOT FOUND\n\n"; + os << "NOT FOUND: " << error_msg << "\n\n"; return; } for (size_t class_def_index = 0; class_def_index < dex_file->NumClassDefs(); class_def_index++) { @@ -727,9 +735,10 @@ class ImageDumper { os << " (" << oat_location << ")"; } os << "\n"; - const OatFile* oat_file = class_linker->FindOatFileFromOatLocation(oat_location); + std::string error_msg; + const OatFile* oat_file = class_linker->FindOatFileFromOatLocation(oat_location, &error_msg); if (oat_file == NULL) { - os << "NOT FOUND\n"; + os << "NOT FOUND: " << error_msg << "\n"; return; } os << "\n"; @@ -775,7 +784,7 @@ class ImageDumper { os << "STATS:\n" << std::flush; UniquePtr file(OS::OpenFileForReading(image_filename_.c_str())); if (file.get() == NULL) { - std::string cache_location(GetDalvikCacheFilenameOrDie(image_filename_)); + std::string cache_location(GetDalvikCacheFilenameOrDie(image_filename_.c_str())); file.reset(OS::OpenFileForReading(cache_location.c_str())); if (file.get() == NULL) { LOG(WARNING) << "Failed to find image in " << image_filename_ @@ -1412,10 +1421,11 @@ static int oatdump(int argc, char** argv) { } if (oat_filename != NULL) { + std::string error_msg; OatFile* oat_file = - OatFile::Open(oat_filename, oat_filename, NULL, false); + OatFile::Open(oat_filename, oat_filename, NULL, false, &error_msg); if (oat_file == NULL) { - fprintf(stderr, "Failed to open oat file from %s\n", oat_filename); + fprintf(stderr, "Failed to open oat file from '%s': %s\n", oat_filename, error_msg.c_str()); return EXIT_FAILURE; } OatDumper oat_dumper(*host_prefix.get(), *oat_file); diff --git a/runtime/base/macros.h b/runtime/base/macros.h index 6531858f9d0..d00c64a4abe 100644 --- a/runtime/base/macros.h +++ b/runtime/base/macros.h @@ -138,8 +138,10 @@ char (&ArraySizeHelper(T (&array)[N]))[N]; #if defined (__APPLE__) #define HOT_ATTR +#define COLD_ATTR #else #define HOT_ATTR __attribute__ ((hot)) +#define COLD_ATTR __attribute__ ((cold)) #endif #define PURE __attribute__ ((__pure__)) diff --git a/runtime/base/unix_file/fd_file.cc b/runtime/base/unix_file/fd_file.cc index 36f8ba7fc65..f48c76db23b 100644 --- a/runtime/base/unix_file/fd_file.cc +++ b/runtime/base/unix_file/fd_file.cc @@ -102,10 +102,6 @@ bool FdFile::IsOpened() const { return fd_ >= 0; } -std::string FdFile::GetPath() const { - return file_path_; -} - bool FdFile::ReadFully(void* buffer, int64_t byte_count) { char* ptr = static_cast(buffer); while (byte_count > 0) { diff --git a/runtime/base/unix_file/fd_file.h b/runtime/base/unix_file/fd_file.h index 79a0db9edae..19e3511eb4a 100644 --- a/runtime/base/unix_file/fd_file.h +++ b/runtime/base/unix_file/fd_file.h @@ -57,7 +57,9 @@ class FdFile : public RandomAccessFile { // Bonus API. int Fd() const; bool IsOpened() const; - std::string GetPath() const; + const std::string& GetPath() const { + return file_path_; + } void DisableAutoClose(); bool ReadFully(void* buffer, int64_t byte_count); bool WriteFully(const void* buffer, int64_t byte_count); diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index aa5f2bf21ed..eb42e0ac44a 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -540,9 +540,10 @@ void ClassLinker::RunRootClinits() { } } -bool ClassLinker::GenerateOatFile(const std::string& dex_filename, +bool ClassLinker::GenerateOatFile(const char* dex_filename, int oat_fd, - const std::string& oat_cache_filename) { + const char* oat_cache_filename) { + Locks::mutator_lock_->AssertNotHeld(Thread::Current()); // Avoid starving GC. std::string dex2oat_string(GetAndroidRoot()); dex2oat_string += (kIsDebugBuild ? "/bin/dex2oatd" : "/bin/dex2oat"); const char* dex2oat = dex2oat_string.c_str(); @@ -567,7 +568,8 @@ bool ClassLinker::GenerateOatFile(const std::string& dex_filename, const char* oat_location_option = oat_location_option_string.c_str(); std::string oat_compiler_filter_string("-compiler-filter:"); - switch (Runtime::Current()->GetCompilerFilter()) { + Runtime::CompilerFilter filter = Runtime::Current()->GetCompilerFilter(); + switch (filter) { case Runtime::kInterpretOnly: oat_compiler_filter_string += "interpret-only"; break; @@ -584,7 +586,7 @@ bool ClassLinker::GenerateOatFile(const std::string& dex_filename, oat_compiler_filter_string += "everything"; break; default: - LOG(FATAL) << "Unexpected case."; + LOG(FATAL) << "Unexpected case: " << filter; } const char* oat_compiler_filter_option = oat_compiler_filter_string.c_str(); @@ -633,49 +635,55 @@ bool ClassLinker::GenerateOatFile(const std::string& dex_filename, int status; pid_t got_pid = TEMP_FAILURE_RETRY(waitpid(pid, &status, 0)); if (got_pid != pid) { - PLOG(ERROR) << "waitpid failed: wanted " << pid << ", got " << got_pid; + ScopedObjectAccess soa(Thread::Current()); + ThrowIOException("Failed to create oat file. Waitpid failed: wanted %d, got %d", pid, + got_pid); return false; } if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) { - LOG(ERROR) << dex2oat << " failed with dex-file=" << dex_filename; + ScopedObjectAccess soa(Thread::Current()); + ThrowIOException("Failed to create oat file. %s failed with dex-file '%s'", dex2oat, + dex_filename); return false; } } return true; } -void ClassLinker::RegisterOatFile(const OatFile& oat_file) { +const OatFile* ClassLinker::RegisterOatFile(const OatFile* oat_file) { WriterMutexLock mu(Thread::Current(), dex_lock_); - RegisterOatFileLocked(oat_file); -} - -void ClassLinker::RegisterOatFileLocked(const OatFile& oat_file) { - dex_lock_.AssertExclusiveHeld(Thread::Current()); - if (kIsDebugBuild) { - for (size_t i = 0; i < oat_files_.size(); ++i) { - CHECK_NE(&oat_file, oat_files_[i]) << oat_file.GetLocation(); + for (size_t i = 0; i < oat_files_.size(); ++i) { + if (UNLIKELY(oat_file->GetLocation() == oat_files_[i]->GetLocation())) { + VLOG(class_linker) << "Attempt to register oat file that's already registered: " + << oat_file->GetLocation(); + for (size_t j = i; j < oat_files_.size(); ++j) { + CHECK_NE(oat_file, oat_files_[j]) << "Attempt to re-register dex file."; + } + delete oat_file; + return oat_files_[i]; } } - VLOG(class_linker) << "Registering " << oat_file.GetLocation(); - oat_files_.push_back(&oat_file); + VLOG(class_linker) << "Registering " << oat_file->GetLocation(); + oat_files_.push_back(oat_file); + return oat_file; } OatFile& ClassLinker::GetImageOatFile(gc::space::ImageSpace* space) { VLOG(startup) << "ClassLinker::GetImageOatFile entering"; - OatFile& oat_file = space->ReleaseOatFile(); - WriterMutexLock mu(Thread::Current(), dex_lock_); - RegisterOatFileLocked(oat_file); + OatFile* oat_file = space->ReleaseOatFile(); + CHECK_EQ(RegisterOatFile(oat_file), oat_file); VLOG(startup) << "ClassLinker::GetImageOatFile exiting"; - return oat_file; + return *oat_file; } const OatFile* ClassLinker::FindOpenedOatFileForDexFile(const DexFile& dex_file) { - ReaderMutexLock mu(Thread::Current(), dex_lock_); - return FindOpenedOatFileFromDexLocation(dex_file.GetLocation(), dex_file.GetLocationChecksum()); + return FindOpenedOatFileFromDexLocation(dex_file.GetLocation().c_str(), + dex_file.GetLocationChecksum()); } -const OatFile* ClassLinker::FindOpenedOatFileFromDexLocation(const std::string& dex_location, +const OatFile* ClassLinker::FindOpenedOatFileFromDexLocation(const char* dex_location, uint32_t dex_location_checksum) { + ReaderMutexLock mu(Thread::Current(), dex_lock_); for (size_t i = 0; i < oat_files_.size(); i++) { const OatFile* oat_file = oat_files_[i]; DCHECK(oat_file != NULL); @@ -689,82 +697,83 @@ const OatFile* ClassLinker::FindOpenedOatFileFromDexLocation(const std::string& return NULL; } -const DexFile* ClassLinker::FindDexFileInOatLocation(const std::string& dex_location, +const DexFile* ClassLinker::FindDexFileInOatLocation(const char* dex_location, uint32_t dex_location_checksum, - const std::string& oat_location) { + const char* oat_location, + std::string* error_msg) { UniquePtr oat_file(OatFile::Open(oat_location, oat_location, NULL, - !Runtime::Current()->IsCompiler())); - if (oat_file.get() == NULL) { - VLOG(class_linker) << "Failed to find existing oat file at " << oat_location; - return NULL; + !Runtime::Current()->IsCompiler(), + error_msg)); + if (oat_file.get() == nullptr) { + *error_msg = StringPrintf("Failed to find existing oat file at %s: %s", oat_location, + error_msg->c_str()); + return nullptr; } Runtime* runtime = Runtime::Current(); const ImageHeader& image_header = runtime->GetHeap()->GetImageSpace()->GetImageHeader(); uint32_t expected_image_oat_checksum = image_header.GetOatChecksum(); uint32_t actual_image_oat_checksum = oat_file->GetOatHeader().GetImageFileLocationOatChecksum(); if (expected_image_oat_checksum != actual_image_oat_checksum) { - VLOG(class_linker) << "Failed to find oat file at " << oat_location - << " with expected image oat checksum of " << expected_image_oat_checksum - << ", found " << actual_image_oat_checksum; - return NULL; + *error_msg = StringPrintf("Failed to find oat file at '%s' with expected image oat checksum of " + "0x%x, found 0x%x", oat_location, expected_image_oat_checksum, + actual_image_oat_checksum); + return nullptr; } uint32_t expected_image_oat_offset = reinterpret_cast(image_header.GetOatDataBegin()); uint32_t actual_image_oat_offset = oat_file->GetOatHeader().GetImageFileLocationOatDataBegin(); if (expected_image_oat_offset != actual_image_oat_offset) { - VLOG(class_linker) << "Failed to find oat file at " << oat_location - << " with expected image oat offset " << expected_image_oat_offset - << ", found " << actual_image_oat_offset; - return NULL; - } - const OatFile::OatDexFile* oat_dex_file = oat_file->GetOatDexFile(dex_location, &dex_location_checksum); - if (oat_dex_file == NULL) { - VLOG(class_linker) << "Failed to find oat file at " << oat_location << " containing " << dex_location; - return NULL; + *error_msg = StringPrintf("Failed to find oat file at '%s' with expected image oat offset %ud, " + "found %ud", oat_location, expected_image_oat_offset, + actual_image_oat_offset); + return nullptr; + } + // TODO: this registers the oat file now as we may use the oat_dex_file later and we want the + // intern behavior of RegisterOatFile. However, if we take an early return we could remove + // the oat file. + const OatFile* opened_oat_file = RegisterOatFile(oat_file.release()); + const OatFile::OatDexFile* oat_dex_file = opened_oat_file->GetOatDexFile(dex_location, + &dex_location_checksum); + if (oat_dex_file == nullptr) { + *error_msg = StringPrintf("Failed to find oat file at '%s' containing '%s'", oat_location, + dex_location); + return nullptr; } uint32_t expected_dex_checksum = dex_location_checksum; uint32_t actual_dex_checksum = oat_dex_file->GetDexFileLocationChecksum(); if (expected_dex_checksum != actual_dex_checksum) { - VLOG(class_linker) << "Failed to find oat file at " << oat_location - << " with expected dex checksum of " << expected_dex_checksum - << ", found " << actual_dex_checksum; - return NULL; + *error_msg = StringPrintf("Failed to find oat file at '%s' with expected dex checksum of 0x%x, " + "found 0x%x", oat_location, expected_dex_checksum, + actual_dex_checksum); + return nullptr; } - RegisterOatFileLocked(*oat_file.release()); - return oat_dex_file->OpenDexFile(); -} - -const DexFile* ClassLinker::FindOrCreateOatFileForDexLocation(const std::string& dex_location, - uint32_t dex_location_checksum, - const std::string& oat_location) { - WriterMutexLock mu(Thread::Current(), dex_lock_); - return FindOrCreateOatFileForDexLocationLocked(dex_location, dex_location_checksum, oat_location); + return oat_dex_file->OpenDexFile(error_msg); } class ScopedFlock { public: ScopedFlock() {} - bool Init(const std::string& filename) { + bool Init(const char* filename, std::string* error_msg) { while (true) { - file_.reset(OS::OpenFileWithFlags(filename.c_str(), O_CREAT | O_RDWR)); + file_.reset(OS::OpenFileWithFlags(filename, O_CREAT | O_RDWR)); if (file_.get() == NULL) { - LOG(ERROR) << "Failed to open file: " << filename; + *error_msg = StringPrintf("Failed to open file '%s'", filename); return false; } int flock_result = TEMP_FAILURE_RETRY(flock(file_->Fd(), LOCK_EX)); if (flock_result != 0) { - PLOG(ERROR) << "Failed to lock file: " << filename; + *error_msg = StringPrintf("Failed to lock file '%s': %s", filename, strerror(errno)); return false; } struct stat fstat_stat; int fstat_result = TEMP_FAILURE_RETRY(fstat(file_->Fd(), &fstat_stat)); if (fstat_result != 0) { - PLOG(ERROR) << "Failed to fstat: " << filename; + *error_msg = StringPrintf("Failed to fstat file '%s': %s", filename, strerror(errno)); return false; } struct stat stat_stat; - int stat_result = TEMP_FAILURE_RETRY(stat(filename.c_str(), &stat_stat)); + int stat_result = TEMP_FAILURE_RETRY(stat(filename, &stat_stat)); if (stat_result != 0) { PLOG(WARNING) << "Failed to stat, will retry: " << filename; // ENOENT can happen if someone racing with us unlinks the file we created so just retry. @@ -795,49 +804,54 @@ class ScopedFlock { DISALLOW_COPY_AND_ASSIGN(ScopedFlock); }; -const DexFile* ClassLinker::FindOrCreateOatFileForDexLocationLocked(const std::string& dex_location, - uint32_t dex_location_checksum, - const std::string& oat_location) { +const DexFile* ClassLinker::FindOrCreateOatFileForDexLocation(const char* dex_location, + uint32_t dex_location_checksum, + const char* oat_location, + std::string* error_msg) { // We play a locking game here so that if two different processes // race to generate (or worse, one tries to open a partial generated // file) we will be okay. This is actually common with apps that use // DexClassLoader to work around the dex method reference limit and // that have a background service running in a separate process. ScopedFlock scoped_flock; - if (!scoped_flock.Init(oat_location)) { - LOG(ERROR) << "Failed to open locked oat file: " << oat_location; - return NULL; + if (!scoped_flock.Init(oat_location, error_msg)) { + return nullptr; } // Check if we already have an up-to-date output file - const DexFile* dex_file = FindDexFileInOatLocation(dex_location, - dex_location_checksum, - oat_location); - if (dex_file != NULL) { + const DexFile* dex_file = FindDexFileInOatLocation(dex_location, dex_location_checksum, + oat_location, error_msg); + if (dex_file != nullptr) { return dex_file; } + VLOG(class_linker) << "Failed to find dex file '" << dex_location << "' in oat location '" + << oat_location << "': " << *error_msg; + error_msg->clear(); // Generate the output oat file for the dex file VLOG(class_linker) << "Generating oat file " << oat_location << " for " << dex_location; if (!GenerateOatFile(dex_location, scoped_flock.GetFile().Fd(), oat_location)) { - LOG(ERROR) << "Failed to generate oat file: " << oat_location; - return NULL; + CHECK(Thread::Current()->IsExceptionPending()); + return nullptr; } const OatFile* oat_file = OatFile::Open(oat_location, oat_location, NULL, - !Runtime::Current()->IsCompiler()); - if (oat_file == NULL) { - LOG(ERROR) << "Failed to open generated oat file: " << oat_location; - return NULL; - } - RegisterOatFileLocked(*oat_file); - const OatFile::OatDexFile* oat_dex_file = oat_file->GetOatDexFile(dex_location, &dex_location_checksum); - if (oat_dex_file == NULL) { - LOG(ERROR) << "Failed to find dex file " << dex_location - << " (checksum " << dex_location_checksum - << ") in generated oat file: " << oat_location; - return NULL; + !Runtime::Current()->IsCompiler(), + error_msg); + if (oat_file == nullptr) { + *error_msg = StringPrintf("Failed to open generated oat file '%s': %s", + oat_location, error_msg->c_str()); + return nullptr; + } + oat_file = RegisterOatFile(oat_file); + const OatFile::OatDexFile* oat_dex_file = oat_file->GetOatDexFile(dex_location, + &dex_location_checksum); + if (oat_dex_file == nullptr) { + *error_msg = StringPrintf("Failed to find dex file '%s' (checksum 0x%x) in generated out file " + "'%s'", dex_location, dex_location_checksum, oat_location); + return nullptr; } - const DexFile* result = oat_dex_file->OpenDexFile(); + const DexFile* result = oat_dex_file->OpenDexFile(error_msg); + CHECK(result != nullptr) << *error_msg; CHECK_EQ(dex_location_checksum, result->GetLocationChecksum()) << "dex_location=" << dex_location << " oat_location=" << oat_location << std::hex << " dex_location_checksum=" << dex_location_checksum @@ -846,8 +860,9 @@ const DexFile* ClassLinker::FindOrCreateOatFileForDexLocationLocked(const std::s } bool ClassLinker::VerifyOatFileChecksums(const OatFile* oat_file, - const std::string& dex_location, - uint32_t dex_location_checksum) { + const char* dex_location, + uint32_t dex_location_checksum, + std::string* error_msg) { Runtime* runtime = Runtime::Current(); const ImageHeader& image_header = runtime->GetHeap()->GetImageSpace()->GetImageHeader(); uint32_t image_oat_checksum = image_header.GetOatChecksum(); @@ -857,14 +872,14 @@ bool ClassLinker::VerifyOatFileChecksums(const OatFile* oat_file, const OatFile::OatDexFile* oat_dex_file = oat_file->GetOatDexFile(dex_location, &dex_location_checksum); if (oat_dex_file == NULL) { - LOG(ERROR) << "oat file " << oat_file->GetLocation() - << " does not contain contents for " << dex_location - << " with checksum " << dex_location_checksum; + *error_msg = StringPrintf("oat file '%s' does not contain contents for '%s' with checksum 0x%x", + oat_file->GetLocation().c_str(), dex_location, dex_location_checksum); std::vector oat_dex_files = oat_file->GetOatDexFiles(); for (size_t i = 0; i < oat_dex_files.size(); i++) { const OatFile::OatDexFile* oat_dex_file = oat_dex_files[i]; - LOG(ERROR) << "oat file " << oat_file->GetLocation() - << " contains contents for " << oat_dex_file->GetDexFileLocation(); + *error_msg += StringPrintf("\noat file '%s' contains contents for '%s'", + oat_file->GetLocation().c_str(), + oat_dex_file->GetDexFileLocation().c_str()); } return false; } @@ -875,116 +890,123 @@ bool ClassLinker::VerifyOatFileChecksums(const OatFile* oat_file, } if (!image_check) { - std::string image_file(image_header.GetImageRoot( - ImageHeader::kOatLocation)->AsString()->ToModifiedUtf8()); - LOG(WARNING) << "oat file " << oat_file->GetLocation() - << " mismatch (" << std::hex << oat_file->GetOatHeader().GetImageFileLocationOatChecksum() - << ", " << oat_file->GetOatHeader().GetImageFileLocationOatDataBegin() - << ") with " << image_file - << " (" << image_oat_checksum << ", " << std::hex << image_oat_data_begin << ")"; + ScopedObjectAccess soa(Thread::Current()); + mirror::String* oat_location = image_header.GetImageRoot(ImageHeader::kOatLocation)->AsString(); + std::string image_file(oat_location->ToModifiedUtf8()); + *error_msg = StringPrintf("oat file '%s' mismatch (0x%x, %d) with '%s' (0x%x, %d)", + oat_file->GetLocation().c_str(), + oat_file->GetOatHeader().GetImageFileLocationOatChecksum(), + oat_file->GetOatHeader().GetImageFileLocationOatDataBegin(), + image_file.c_str(), image_oat_checksum, image_oat_data_begin); } if (!dex_check) { - LOG(WARNING) << "oat file " << oat_file->GetLocation() - << " mismatch (" << std::hex << oat_dex_file->GetDexFileLocationChecksum() - << ") with " << dex_location - << " (" << std::hex << dex_location_checksum << ")"; + *error_msg = StringPrintf("oat file '%s' mismatch (0x%x) with '%s' (0x%x)", + oat_file->GetLocation().c_str(), + oat_dex_file->GetDexFileLocationChecksum(), + dex_location, dex_location_checksum); } return false; } -const DexFile* ClassLinker::VerifyAndOpenDexFileFromOatFile(const OatFile* oat_file, - const std::string& dex_location, - uint32_t dex_location_checksum) { - bool verified = VerifyOatFileChecksums(oat_file, dex_location, dex_location_checksum); +const DexFile* ClassLinker::VerifyAndOpenDexFileFromOatFile(const std::string& oat_file_location, + const char* dex_location, + std::string* error_msg, + bool* open_failed) { + UniquePtr oat_file(FindOatFileFromOatLocation(oat_file_location, error_msg)); + if (oat_file.get() == nullptr) { + *open_failed = true; + return nullptr; + } + *open_failed = false; + uint32_t dex_location_checksum; + if (!DexFile::GetChecksum(dex_location, &dex_location_checksum, error_msg)) { + // If no classes.dex found in dex_location, it has been stripped or is corrupt, assume oat is + // up-to-date. This is the common case in user builds for jar's and apk's in the /system + // directory. + const OatFile* opened_oat_file = oat_file.release(); + opened_oat_file = RegisterOatFile(opened_oat_file); + const OatFile::OatDexFile* oat_dex_file = opened_oat_file->GetOatDexFile(dex_location, NULL); + if (oat_dex_file == nullptr) { + *error_msg = StringPrintf("Dex checksum mismatch for location '%s' and failed to find oat " + "dex file '%s': %s", oat_file_location.c_str(), dex_location, + error_msg->c_str()); + return nullptr; + } + return oat_dex_file->OpenDexFile(error_msg); + } + + bool verified = VerifyOatFileChecksums(oat_file.get(), dex_location, dex_location_checksum, + error_msg); if (!verified) { - delete oat_file; - return NULL; + return nullptr; } - RegisterOatFileLocked(*oat_file); - return oat_file->GetOatDexFile(dex_location, &dex_location_checksum)->OpenDexFile(); + const OatFile* opened_oat_file = oat_file.release(); + opened_oat_file = RegisterOatFile(opened_oat_file); + return opened_oat_file->GetOatDexFile(dex_location, + &dex_location_checksum)->OpenDexFile(error_msg); } -const DexFile* ClassLinker::FindDexFileInOatFileFromDexLocation(const std::string& dex_location, - uint32_t dex_location_checksum) { - WriterMutexLock mu(Thread::Current(), dex_lock_); - +const DexFile* ClassLinker::FindDexFileInOatFileFromDexLocation(const char* dex_location, + uint32_t dex_location_checksum, + std::string* error_msg) { const OatFile* open_oat_file = FindOpenedOatFileFromDexLocation(dex_location, dex_location_checksum); - if (open_oat_file != NULL) { - return open_oat_file->GetOatDexFile(dex_location, &dex_location_checksum)->OpenDexFile(); + if (open_oat_file != nullptr) { + const OatFile::OatDexFile* oat_dex_file = open_oat_file->GetOatDexFile(dex_location, + &dex_location_checksum); + return oat_dex_file->OpenDexFile(error_msg); } // Look for an existing file next to dex. for example, for // /foo/bar/baz.jar, look for /foo/bar/baz.odex. std::string odex_filename(OatFile::DexFilenameToOdexFilename(dex_location)); - UniquePtr oat_file(FindOatFileFromOatLocationLocked(odex_filename)); - if (oat_file.get() != NULL) { - uint32_t dex_location_checksum; - if (!DexFile::GetChecksum(dex_location, &dex_location_checksum)) { - // If no classes.dex found in dex_location, it has been stripped, assume oat is up-to-date. - // This is the common case in user builds for jar's and apk's in the /system directory. - const OatFile::OatDexFile* oat_dex_file = oat_file->GetOatDexFile(dex_location, NULL); - CHECK(oat_dex_file != NULL) << odex_filename << " " << dex_location; - RegisterOatFileLocked(*oat_file); - return oat_dex_file->OpenDexFile(); - } - const DexFile* dex_file = VerifyAndOpenDexFileFromOatFile(oat_file.release(), - dex_location, - dex_location_checksum); - if (dex_file != NULL) { - return dex_file; - } - } - // Look for an existing file in the dalvik-cache, validating the result if found - // not found in /foo/bar/baz.odex? try /data/dalvik-cache/foo@bar@baz.jar@classes.dex + bool open_failed; + const DexFile* dex_file = VerifyAndOpenDexFileFromOatFile(odex_filename, dex_location, + error_msg, &open_failed); + if (dex_file != nullptr) { + return dex_file; + } + std::string cache_error_msg; std::string cache_location(GetDalvikCacheFilenameOrDie(dex_location)); - oat_file.reset(FindOatFileFromOatLocationLocked(cache_location)); - if (oat_file.get() != NULL) { - uint32_t dex_location_checksum; - if (!DexFile::GetChecksum(dex_location, &dex_location_checksum)) { - LOG(WARNING) << "Failed to compute checksum: " << dex_location; - return NULL; - } - const DexFile* dex_file = VerifyAndOpenDexFileFromOatFile(oat_file.release(), - dex_location, - dex_location_checksum); - if (dex_file != NULL) { - return dex_file; - } - if (TEMP_FAILURE_RETRY(unlink(cache_location.c_str())) != 0) { - PLOG(FATAL) << "Failed to remove obsolete oat file from " << cache_location; - } + dex_file = VerifyAndOpenDexFileFromOatFile(cache_location, dex_location, &cache_error_msg, + &open_failed); + if (dex_file != nullptr) { + return dex_file; + } + if (!open_failed && TEMP_FAILURE_RETRY(unlink(cache_location.c_str())) != 0) { + PLOG(FATAL) << "Failed to remove obsolete oat file from " << cache_location; } - LOG(INFO) << "Failed to open oat file from " << odex_filename << " or " << cache_location << "."; + VLOG(class_linker) << "Failed to open oat file from " << odex_filename + << " (error '" << *error_msg << "') or " << cache_location + << " (error '" << cache_error_msg << "')."; // Try to generate oat file if it wasn't found or was obsolete. - std::string oat_cache_filename(GetDalvikCacheFilenameOrDie(dex_location)); - return FindOrCreateOatFileForDexLocationLocked(dex_location, dex_location_checksum, oat_cache_filename); + error_msg->clear(); + return FindOrCreateOatFileForDexLocation(dex_location, dex_location_checksum, + cache_location.c_str(), error_msg); } const OatFile* ClassLinker::FindOpenedOatFileFromOatLocation(const std::string& oat_location) { + ReaderMutexLock mu(Thread::Current(), dex_lock_); for (size_t i = 0; i < oat_files_.size(); i++) { const OatFile* oat_file = oat_files_[i]; - DCHECK(oat_file != NULL); + DCHECK(oat_file != nullptr); if (oat_file->GetLocation() == oat_location) { return oat_file; } } - return NULL; -} - -const OatFile* ClassLinker::FindOatFileFromOatLocation(const std::string& oat_location) { - ReaderMutexLock mu(Thread::Current(), dex_lock_); - return FindOatFileFromOatLocationLocked(oat_location); + return nullptr; } -const OatFile* ClassLinker::FindOatFileFromOatLocationLocked(const std::string& oat_location) { +const OatFile* ClassLinker::FindOatFileFromOatLocation(const std::string& oat_location, + std::string* error_msg) { const OatFile* oat_file = FindOpenedOatFileFromOatLocation(oat_location); - if (oat_file != NULL) { + if (oat_file != nullptr) { return oat_file; } - oat_file = OatFile::Open(oat_location, oat_location, NULL, !Runtime::Current()->IsCompiler()); + oat_file = OatFile::Open(oat_location, oat_location, NULL, !Runtime::Current()->IsCompiler(), + error_msg); if (oat_file == NULL) { return NULL; } @@ -1041,12 +1063,15 @@ void ClassLinker::InitFromImage() { for (int32_t i = 0; i < dex_caches->GetLength(); i++) { SirtRef dex_cache(self, dex_caches->Get(i)); const std::string& dex_file_location(dex_cache->GetLocation()->ToModifiedUtf8()); - const OatFile::OatDexFile* oat_dex_file = oat_file.GetOatDexFile(dex_file_location, NULL); + const OatFile::OatDexFile* oat_dex_file = oat_file.GetOatDexFile(dex_file_location.c_str(), + nullptr); CHECK(oat_dex_file != NULL) << oat_file.GetLocation() << " " << dex_file_location; - const DexFile* dex_file = oat_dex_file->OpenDexFile(); + std::string error_msg; + const DexFile* dex_file = oat_dex_file->OpenDexFile(&error_msg); if (dex_file == NULL) { LOG(FATAL) << "Failed to open dex file " << dex_file_location - << " from within oat file " << oat_file.GetLocation(); + << " from within oat file " << oat_file.GetLocation() + << " error '" << error_msg << "'"; } CHECK_EQ(dex_file->GetLocationChecksum(), oat_dex_file->GetDexFileLocationChecksum()); @@ -1510,7 +1535,7 @@ const OatFile::OatClass* ClassLinker::GetOatClass(const DexFile& dex_file, uint1 const OatFile* oat_file = FindOpenedOatFileForDexFile(dex_file); CHECK(oat_file != NULL) << dex_file.GetLocation(); uint dex_location_checksum = dex_file.GetLocationChecksum(); - const OatFile::OatDexFile* oat_dex_file = oat_file->GetOatDexFile(dex_file.GetLocation(), + const OatFile::OatDexFile* oat_dex_file = oat_file->GetOatDexFile(dex_file.GetLocation().c_str(), &dex_location_checksum); CHECK(oat_dex_file != NULL) << dex_file.GetLocation(); const OatFile::OatClass* oat_class = oat_dex_file->GetOatClass(class_def_idx); @@ -2559,7 +2584,7 @@ bool ClassLinker::VerifyClassUsingOatFile(const DexFile& dex_file, mirror::Class CHECK(oat_file != NULL) << dex_file.GetLocation() << " " << PrettyClass(klass); uint dex_location_checksum = dex_file.GetLocationChecksum(); - const OatFile::OatDexFile* oat_dex_file = oat_file->GetOatDexFile(dex_file.GetLocation(), + const OatFile::OatDexFile* oat_dex_file = oat_file->GetOatDexFile(dex_file.GetLocation().c_str(), &dex_location_checksum); CHECK(oat_dex_file != NULL) << dex_file.GetLocation() << " " << PrettyClass(klass); uint16_t class_def_index = klass->GetDexClassDefIndex(); @@ -4025,7 +4050,7 @@ mirror::ArtMethod* ClassLinker::ResolveMethod(const DexFile& dex_file, DCHECK(dex_cache != NULL); // Check for hit in the dex cache. mirror::ArtMethod* resolved = dex_cache->GetResolvedMethod(method_idx); - if (resolved != NULL) { + if (resolved != NULL && !resolved->IsRuntimeMethod()) { return resolved; } // Fail, get the declaring class. diff --git a/runtime/class_linker.h b/runtime/class_linker.h index 11ba78b36a0..0bc1b5f4440 100644 --- a/runtime/class_linker.h +++ b/runtime/class_linker.h @@ -215,7 +215,7 @@ class ClassLinker { LOCKS_EXCLUDED(dex_lock_) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - void RegisterOatFile(const OatFile& oat_file) + const OatFile* RegisterOatFile(const OatFile* oat_file) LOCKS_EXCLUDED(dex_lock_); const std::vector& GetBootClassPath() { @@ -244,43 +244,37 @@ class ClassLinker { SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); // Generate an oat file from a dex file - bool GenerateOatFile(const std::string& dex_filename, + bool GenerateOatFile(const char* dex_filename, int oat_fd, - const std::string& oat_cache_filename); + const char* oat_cache_filename); + LOCKS_EXCLUDED(Locks::mutator_lock_); - const OatFile* FindOatFileFromOatLocation(const std::string& location) + const OatFile* FindOatFileFromOatLocation(const std::string& location, + std::string* error_msg) LOCKS_EXCLUDED(dex_lock_); - const OatFile* FindOatFileFromOatLocationLocked(const std::string& location) - SHARED_LOCKS_REQUIRED(dex_lock_); - // Finds the oat file for a dex location, generating the oat file if // it is missing or out of date. Returns the DexFile from within the // created oat file. - const DexFile* FindOrCreateOatFileForDexLocation(const std::string& dex_location, + const DexFile* FindOrCreateOatFileForDexLocation(const char* dex_location, uint32_t dex_location_checksum, - const std::string& oat_location) - LOCKS_EXCLUDED(dex_lock_) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - const DexFile* FindOrCreateOatFileForDexLocationLocked(const std::string& dex_location, - uint32_t dex_location_checksum, - const std::string& oat_location) - EXCLUSIVE_LOCKS_REQUIRED(dex_lock_) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + const char* oat_location, + std::string* error_msg) + LOCKS_EXCLUDED(dex_lock_, Locks::mutator_lock_); // Find a DexFile within an OatFile given a DexFile location. Note // that this returns null if the location checksum of the DexFile // does not match the OatFile. - const DexFile* FindDexFileInOatFileFromDexLocation(const std::string& location, - uint32_t location_checksum) - LOCKS_EXCLUDED(dex_lock_) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + const DexFile* FindDexFileInOatFileFromDexLocation(const char* location, + uint32_t location_checksum, + std::string* error_msg) + LOCKS_EXCLUDED(dex_lock_, Locks::mutator_lock_); // Returns true if oat file contains the dex file with the given location and checksum. static bool VerifyOatFileChecksums(const OatFile* oat_file, - const std::string& dex_location, - uint32_t dex_location_checksum) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + const char* dex_location, + uint32_t dex_location_checksum, + std::string* error_msg); // TODO: replace this with multiple methods that allocate the correct managed type. template @@ -430,8 +424,6 @@ class ClassLinker { EXCLUSIVE_LOCKS_REQUIRED(dex_lock_) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); bool IsDexFileRegisteredLocked(const DexFile& dex_file) const SHARED_LOCKS_REQUIRED(dex_lock_); - void RegisterOatFileLocked(const OatFile& oat_file) EXCLUSIVE_LOCKS_REQUIRED(dex_lock_) - EXCLUSIVE_LOCKS_REQUIRED(dex_lock_); bool InitializeClass(mirror::Class* klass, bool can_run_clinit, bool can_init_parents) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); @@ -493,22 +485,22 @@ class ClassLinker { const OatFile* FindOpenedOatFileForDexFile(const DexFile& dex_file) LOCKS_EXCLUDED(dex_lock_) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - const OatFile* FindOpenedOatFileFromDexLocation(const std::string& dex_location, + const OatFile* FindOpenedOatFileFromDexLocation(const char* dex_location, uint32_t dex_location_checksum) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_, dex_lock_); + LOCKS_EXCLUDED(dex_lock); const OatFile* FindOpenedOatFileFromOatLocation(const std::string& oat_location) - SHARED_LOCKS_REQUIRED(dex_lock_); - const DexFile* FindDexFileInOatLocation(const std::string& dex_location, + LOCKS_EXCLUDED(dex_lock_); + const DexFile* FindDexFileInOatLocation(const char* dex_location, uint32_t dex_location_checksum, - const std::string& oat_location) - EXCLUSIVE_LOCKS_REQUIRED(dex_lock_) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + const char* oat_location, + std::string* error_msg) + LOCKS_EXCLUDED(dex_lock_); - const DexFile* VerifyAndOpenDexFileFromOatFile(const OatFile* oat_file, - const std::string& dex_location, - uint32_t dex_location_checksum) - EXCLUSIVE_LOCKS_REQUIRED(dex_lock_) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + const DexFile* VerifyAndOpenDexFileFromOatFile(const std::string& oat_file_location, + const char* dex_location, + std::string* error_msg, + bool* open_failed) + LOCKS_EXCLUDED(dex_lock_); mirror::ArtMethod* CreateProxyConstructor(Thread* self, SirtRef& klass, mirror::Class* proxy_class) diff --git a/runtime/common_test.h b/runtime/common_test.h index fe54d0341d7..899eab1f63d 100644 --- a/runtime/common_test.h +++ b/runtime/common_test.h @@ -282,9 +282,12 @@ class CommonTest : public testing::Test { int mkdir_result = mkdir(dalvik_cache_.c_str(), 0700); ASSERT_EQ(mkdir_result, 0); - java_lang_dex_file_ = DexFile::Open(GetLibCoreDexFileName(), GetLibCoreDexFileName()); + std::string error_msg; + java_lang_dex_file_ = DexFile::Open(GetLibCoreDexFileName().c_str(), + GetLibCoreDexFileName().c_str(), &error_msg); if (java_lang_dex_file_ == NULL) { - LOG(FATAL) << "Could not open .dex file '" << GetLibCoreDexFileName() << "'\n"; + LOG(FATAL) << "Could not open .dex file '" << GetLibCoreDexFileName() << "': " + << error_msg << "\n"; } boot_class_path_.push_back(java_lang_dex_file_); @@ -423,8 +426,9 @@ class CommonTest : public testing::Test { filename += "art-test-dex-"; filename += name; filename += ".jar"; - const DexFile* dex_file = DexFile::Open(filename, filename); - CHECK(dex_file != NULL) << "Failed to open " << filename; + std::string error_msg; + const DexFile* dex_file = DexFile::Open(filename.c_str(), filename.c_str(), &error_msg); + CHECK(dex_file != NULL) << "Failed to open '" << filename << "': " << error_msg; CHECK_EQ(PROT_READ, dex_file->GetPermissions()); CHECK(dex_file->IsReadOnly()); opened_dex_files_.push_back(dex_file); @@ -498,10 +502,12 @@ class CommonTest : public testing::Test { void ReserveImageSpace() { // Reserve where the image will be loaded up front so that other parts of test set up don't // accidentally end up colliding with the fixed memory address when we need to load the image. + std::string error_msg; image_reservation_.reset(MemMap::MapAnonymous("image reservation", reinterpret_cast(ART_BASE_ADDRESS), (size_t)100 * 1024 * 1024, // 100MB - PROT_NONE)); + PROT_NONE, &error_msg)); + CHECK(image_reservation_.get() != nullptr) << error_msg; } void UnreserveImageSpace() { diff --git a/runtime/common_throws.cc b/runtime/common_throws.cc index 189e3edc0f6..0419dab0f88 100644 --- a/runtime/common_throws.cc +++ b/runtime/common_throws.cc @@ -230,6 +230,15 @@ void ThrowIncompatibleClassChangeError(const mirror::Class* referrer, const char va_end(args); } +// IOException + +void ThrowIOException(const char* fmt, ...) { + va_list args; + va_start(args, fmt); + ThrowException(NULL, "Ljava/io/IOException;", NULL, fmt, &args); + va_end(args); +} + // LinkageError void ThrowLinkageError(const mirror::Class* referrer, const char* fmt, ...) { diff --git a/runtime/common_throws.h b/runtime/common_throws.h index 1d77e2d6250..3164f308c12 100644 --- a/runtime/common_throws.h +++ b/runtime/common_throws.h @@ -22,10 +22,10 @@ namespace art { namespace mirror { -class ArtField; -class ArtMethod; -class Class; -class Object; + class ArtField; + class ArtMethod; + class Class; + class Object; } // namespace mirror class Signature; class StringPiece; @@ -34,102 +34,110 @@ class ThrowLocation; // AbstractMethodError void ThrowAbstractMethodError(const mirror::ArtMethod* method) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) COLD_ATTR; // ArithmeticException -void ThrowArithmeticExceptionDivideByZero() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); +void ThrowArithmeticExceptionDivideByZero() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) COLD_ATTR; // ArrayIndexOutOfBoundsException void ThrowArrayIndexOutOfBoundsException(int index, int length) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) COLD_ATTR; // ArrayStoreException void ThrowArrayStoreException(const mirror::Class* element_class, const mirror::Class* array_class) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) COLD_ATTR; // ClassCircularityError -void ThrowClassCircularityError(mirror::Class* c) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); +void ThrowClassCircularityError(mirror::Class* c) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) COLD_ATTR; // ClassCastException void ThrowClassCastException(const mirror::Class* dest_type, const mirror::Class* src_type) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) COLD_ATTR; void ThrowClassCastException(const ThrowLocation* throw_location, const char* msg) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) COLD_ATTR; // ClassFormatError void ThrowClassFormatError(const mirror::Class* referrer, const char* fmt, ...) __attribute__((__format__(__printf__, 2, 3))) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) COLD_ATTR; // IllegalAccessError void ThrowIllegalAccessErrorClass(mirror::Class* referrer, mirror::Class* accessed) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) COLD_ATTR; void ThrowIllegalAccessErrorClassForMethodDispatch(mirror::Class* referrer, mirror::Class* accessed, const mirror::ArtMethod* caller, const mirror::ArtMethod* called, InvokeType type) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) COLD_ATTR; void ThrowIllegalAccessErrorMethod(mirror::Class* referrer, mirror::ArtMethod* accessed) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) COLD_ATTR; void ThrowIllegalAccessErrorField(mirror::Class* referrer, mirror::ArtField* accessed) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) COLD_ATTR; void ThrowIllegalAccessErrorFinalField(const mirror::ArtMethod* referrer, mirror::ArtField* accessed) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) COLD_ATTR; void ThrowIllegalAccessError(mirror::Class* referrer, const char* fmt, ...) __attribute__((__format__(__printf__, 2, 3))) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) COLD_ATTR; // IllegalArgumentException void ThrowIllegalArgumentException(const ThrowLocation* throw_location, const char* msg) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) COLD_ATTR; // IncompatibleClassChangeError void ThrowIncompatibleClassChangeError(InvokeType expected_type, InvokeType found_type, mirror::ArtMethod* method, const mirror::ArtMethod* referrer) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) COLD_ATTR; void ThrowIncompatibleClassChangeErrorClassForInterfaceDispatch(const mirror::ArtMethod* interface_method, mirror::Object* this_object, const mirror::ArtMethod* referrer) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) COLD_ATTR; void ThrowIncompatibleClassChangeErrorField(const mirror::ArtField* resolved_field, bool is_static, const mirror::ArtMethod* referrer) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) COLD_ATTR; void ThrowIncompatibleClassChangeError(const mirror::Class* referrer, const char* fmt, ...) __attribute__((__format__(__printf__, 2, 3))) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) COLD_ATTR; + +// IOException + +void ThrowIOException(const char* fmt, ...) __attribute__((__format__(__printf__, 1, 2))) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) COLD_ATTR; // LinkageError void ThrowLinkageError(const mirror::Class* referrer, const char* fmt, ...) __attribute__((__format__(__printf__, 2, 3))) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) COLD_ATTR; // NegativeArraySizeException -void ThrowNegativeArraySizeException(int size) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); +void ThrowNegativeArraySizeException(int size) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) COLD_ATTR; -void ThrowNegativeArraySizeException(const char* msg) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); +void ThrowNegativeArraySizeException(const char* msg) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) COLD_ATTR; // NoSuchFieldError @@ -142,45 +150,45 @@ void ThrowNoSuchFieldError(const StringPiece& scope, mirror::Class* c, void ThrowNoSuchMethodError(InvokeType type, mirror::Class* c, const StringPiece& name, const Signature& signature) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) COLD_ATTR; void ThrowNoSuchMethodError(uint32_t method_idx) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) COLD_ATTR; // NullPointerException void ThrowNullPointerExceptionForFieldAccess(const ThrowLocation& throw_location, mirror::ArtField* field, bool is_read) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) COLD_ATTR; void ThrowNullPointerExceptionForMethodAccess(const ThrowLocation& throw_location, uint32_t method_idx, InvokeType type) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) COLD_ATTR; void ThrowNullPointerExceptionForMethodAccess(const ThrowLocation& throw_location, mirror::ArtMethod* method, InvokeType type) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) COLD_ATTR; void ThrowNullPointerExceptionFromDexPC(const ThrowLocation& throw_location) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) COLD_ATTR; void ThrowNullPointerException(const ThrowLocation* throw_location, const char* msg) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) COLD_ATTR; // RuntimeException void ThrowRuntimeException(const char* fmt, ...) __attribute__((__format__(__printf__, 1, 2))) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) COLD_ATTR; // VerifyError void ThrowVerifyError(const mirror::Class* referrer, const char* fmt, ...) __attribute__((__format__(__printf__, 2, 3))) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) COLD_ATTR; } // namespace art diff --git a/runtime/dex_file.cc b/runtime/dex_file.cc index ac133a3cc13..a0f5601fff2 100644 --- a/runtime/dex_file.cc +++ b/runtime/dex_file.cc @@ -62,72 +62,77 @@ DexFile::ClassPathEntry DexFile::FindInClassPath(const char* descriptor, reinterpret_cast(NULL)); } -int OpenAndReadMagic(const std::string& filename, uint32_t* magic) { +static int OpenAndReadMagic(const char* filename, uint32_t* magic, std::string* error_msg) { CHECK(magic != NULL); - int fd = open(filename.c_str(), O_RDONLY, 0); + int fd = open(filename, O_RDONLY, 0); if (fd == -1) { - PLOG(WARNING) << "Unable to open '" << filename << "'"; + *error_msg = StringPrintf("Unable to open '%s' : %s", filename, strerror(errno)); return -1; } int n = TEMP_FAILURE_RETRY(read(fd, magic, sizeof(*magic))); if (n != sizeof(*magic)) { - PLOG(ERROR) << "Failed to find magic in '" << filename << "'"; + *error_msg = StringPrintf("Failed to find magic in '%s'", filename); return -1; } if (lseek(fd, 0, SEEK_SET) != 0) { - PLOG(ERROR) << "Failed to seek to beginning of file '" << filename << "'"; + *error_msg = StringPrintf("Failed to seek to beginning of file '%s' : %s", filename, + strerror(errno)); return -1; } return fd; } -bool DexFile::GetChecksum(const std::string& filename, uint32_t* checksum) { +bool DexFile::GetChecksum(const char* filename, uint32_t* checksum, std::string* error_msg) { CHECK(checksum != NULL); uint32_t magic; - int fd = OpenAndReadMagic(filename, &magic); + int fd = OpenAndReadMagic(filename, &magic, error_msg); if (fd == -1) { + DCHECK(!error_msg->empty()); return false; } if (IsZipMagic(magic)) { - UniquePtr zip_archive(ZipArchive::OpenFromFd(fd)); + UniquePtr zip_archive(ZipArchive::OpenFromFd(fd, filename, error_msg)); if (zip_archive.get() == NULL) { + *error_msg = StringPrintf("Failed to open zip archive '%s'", filename); return false; } UniquePtr zip_entry(zip_archive->Find(kClassesDex)); if (zip_entry.get() == NULL) { - LOG(ERROR) << "Zip archive '" << filename << "' doesn't contain " << kClassesDex; + *error_msg = StringPrintf("Zip archive '%s' doesn\'t contain %s", filename, kClassesDex); return false; } *checksum = zip_entry->GetCrc32(); return true; } if (IsDexMagic(magic)) { - UniquePtr dex_file(DexFile::OpenFile(fd, filename, false)); + UniquePtr dex_file(DexFile::OpenFile(fd, filename, false, error_msg)); if (dex_file.get() == NULL) { return false; } *checksum = dex_file->GetHeader().checksum_; return true; } - LOG(ERROR) << "Expected valid zip or dex file: " << filename; + *error_msg = StringPrintf("Expected valid zip or dex file: '%s'", filename); return false; } -const DexFile* DexFile::Open(const std::string& filename, - const std::string& location) { +const DexFile* DexFile::Open(const char* filename, + const char* location, + std::string* error_msg) { uint32_t magic; - int fd = OpenAndReadMagic(filename, &magic); + int fd = OpenAndReadMagic(filename, &magic, error_msg); if (fd == -1) { + DCHECK(!error_msg->empty()); return NULL; } if (IsZipMagic(magic)) { - return DexFile::OpenZip(fd, location); + return DexFile::OpenZip(fd, location, error_msg); } if (IsDexMagic(magic)) { - return DexFile::OpenFile(fd, location, true); + return DexFile::OpenFile(fd, location, true, error_msg); } - LOG(ERROR) << "Expected valid zip or dex file: " << filename; - return NULL; + *error_msg = StringPrintf("Expected valid zip or dex file: '%s'", filename); + return nullptr; } int DexFile::GetPermissions() const { @@ -160,46 +165,48 @@ bool DexFile::DisableWrite() const { } } -const DexFile* DexFile::OpenFile(int fd, - const std::string& location, - bool verify) { - CHECK(!location.empty()); +const DexFile* DexFile::OpenFile(int fd, const char* location, bool verify, + std::string* error_msg) { + CHECK(location != nullptr); struct stat sbuf; memset(&sbuf, 0, sizeof(sbuf)); if (fstat(fd, &sbuf) == -1) { - PLOG(ERROR) << "fstat \"" << location << "\" failed"; + *error_msg = StringPrintf("DexFile: fstat \'%s\' failed: %s", location, strerror(errno)); close(fd); - return NULL; + return nullptr; } if (S_ISDIR(sbuf.st_mode)) { - LOG(ERROR) << "attempt to mmap directory \"" << location << "\""; - return NULL; + *error_msg = StringPrintf("Attempt to mmap directory '%s'", location); + return nullptr; } size_t length = sbuf.st_size; - UniquePtr map(MemMap::MapFile(length, PROT_READ, MAP_PRIVATE, fd, 0)); - if (map.get() == NULL) { - LOG(ERROR) << "mmap \"" << location << "\" failed"; + UniquePtr map(MemMap::MapFile(length, PROT_READ, MAP_PRIVATE, fd, 0, location, + error_msg)); + if (map.get() == nullptr) { + DCHECK(!error_msg->empty()); close(fd); - return NULL; + return nullptr; } close(fd); if (map->Size() < sizeof(DexFile::Header)) { - LOG(ERROR) << "Failed to open dex file '" << location << "' that is too short to have a header"; - return NULL; + *error_msg = StringPrintf( + "DexFile: failed to open dex file \'%s\' that is too short to have a header", location); + return nullptr; } const Header* dex_header = reinterpret_cast(map->Begin()); - const DexFile* dex_file = OpenMemory(location, dex_header->checksum_, map.release()); - if (dex_file == NULL) { - LOG(ERROR) << "Failed to open dex file '" << location << "' from memory"; - return NULL; + const DexFile* dex_file = OpenMemory(location, dex_header->checksum_, map.release(), error_msg); + if (dex_file == nullptr) { + *error_msg = StringPrintf("Failed to open dex file '%s' from memory: %s", location, + error_msg->c_str()); + return nullptr; } - if (verify && !DexFileVerifier::Verify(dex_file, dex_file->Begin(), dex_file->Size())) { - LOG(ERROR) << "Failed to verify dex file '" << location << "'"; - return NULL; + if (verify && !DexFileVerifier::Verify(dex_file, dex_file->Begin(), dex_file->Size(), location, + error_msg)) { + return nullptr; } return dex_file; @@ -207,49 +214,55 @@ const DexFile* DexFile::OpenFile(int fd, const char* DexFile::kClassesDex = "classes.dex"; -const DexFile* DexFile::OpenZip(int fd, const std::string& location) { - UniquePtr zip_archive(ZipArchive::OpenFromFd(fd)); - if (zip_archive.get() == NULL) { - LOG(ERROR) << "Failed to open " << location << " when looking for classes.dex"; - return NULL; +const DexFile* DexFile::OpenZip(int fd, const std::string& location, std::string* error_msg) { + UniquePtr zip_archive(ZipArchive::OpenFromFd(fd, location.c_str(), error_msg)); + if (zip_archive.get() == nullptr) { + DCHECK(!error_msg->empty()); + return nullptr; } - return DexFile::Open(*zip_archive.get(), location); + return DexFile::Open(*zip_archive.get(), location, error_msg); } const DexFile* DexFile::OpenMemory(const std::string& location, uint32_t location_checksum, - MemMap* mem_map) { + MemMap* mem_map, + std::string* error_msg) { return OpenMemory(mem_map->Begin(), mem_map->Size(), location, location_checksum, - mem_map); + mem_map, + error_msg); } -const DexFile* DexFile::Open(const ZipArchive& zip_archive, const std::string& location) { +const DexFile* DexFile::Open(const ZipArchive& zip_archive, const std::string& location, + std::string* error_msg) { CHECK(!location.empty()); UniquePtr zip_entry(zip_archive.Find(kClassesDex)); if (zip_entry.get() == NULL) { - LOG(ERROR) << "Failed to find classes.dex within '" << location << "'"; - return NULL; + *error_msg = StringPrintf("Failed to find classes.dex within '%s'", location.c_str()); + return nullptr; } - UniquePtr map(zip_entry->ExtractToMemMap(kClassesDex)); + UniquePtr map(zip_entry->ExtractToMemMap(kClassesDex, error_msg)); if (map.get() == NULL) { - LOG(ERROR) << "Failed to extract '" << kClassesDex << "' from '" << location << "'"; - return NULL; + *error_msg = StringPrintf("Failed to extract '%s' from '%s': %s", kClassesDex, location.c_str(), + error_msg->c_str()); + return nullptr; } - UniquePtr dex_file(OpenMemory(location, zip_entry->GetCrc32(), map.release())); - if (dex_file.get() == NULL) { - LOG(ERROR) << "Failed to open dex file '" << location << "' from memory"; - return NULL; + UniquePtr dex_file(OpenMemory(location, zip_entry->GetCrc32(), map.release(), + error_msg)); + if (dex_file.get() == nullptr) { + *error_msg = StringPrintf("Failed to open dex file '%s' from memory: %s", location.c_str(), + error_msg->c_str()); + return nullptr; } - if (!DexFileVerifier::Verify(dex_file.get(), dex_file->Begin(), dex_file->Size())) { - LOG(ERROR) << "Failed to verify dex file '" << location << "'"; - return NULL; + if (!DexFileVerifier::Verify(dex_file.get(), dex_file->Begin(), dex_file->Size(), + location.c_str(), error_msg)) { + return nullptr; } if (!dex_file->DisableWrite()) { - LOG(ERROR) << "Failed to make dex file read only '" << location << "'"; - return NULL; + *error_msg = StringPrintf("Failed to make dex file '%s' read only", location.c_str()); + return nullptr; } CHECK(dex_file->IsReadOnly()) << location; return dex_file.release(); @@ -259,11 +272,11 @@ const DexFile* DexFile::OpenMemory(const byte* base, size_t size, const std::string& location, uint32_t location_checksum, - MemMap* mem_map) { + MemMap* mem_map, std::string* error_msg) { CHECK_ALIGNED(base, 4); // various dex file structures must be word aligned UniquePtr dex_file(new DexFile(base, size, location, location_checksum, mem_map)); - if (!dex_file->Init()) { - return NULL; + if (!dex_file->Init(error_msg)) { + return nullptr; } else { return dex_file.release(); } @@ -276,9 +289,9 @@ DexFile::~DexFile() { // the global reference table is otherwise empty! } -bool DexFile::Init() { +bool DexFile::Init(std::string* error_msg) { InitMembers(); - if (!CheckMagicAndVersion()) { + if (!CheckMagicAndVersion(error_msg)) { return false; } return true; @@ -296,22 +309,26 @@ void DexFile::InitMembers() { class_defs_ = reinterpret_cast(b + h->class_defs_off_); } -bool DexFile::CheckMagicAndVersion() const { +bool DexFile::CheckMagicAndVersion(std::string* error_msg) const { CHECK(header_->magic_ != NULL) << GetLocation(); if (!IsMagicValid(header_->magic_)) { - LOG(ERROR) << "Unrecognized magic number in " << GetLocation() << ":" + std::ostringstream oss; + oss << "Unrecognized magic number in " << GetLocation() << ":" << " " << header_->magic_[0] << " " << header_->magic_[1] << " " << header_->magic_[2] << " " << header_->magic_[3]; + *error_msg = oss.str(); return false; } if (!IsVersionValid(header_->magic_)) { - LOG(ERROR) << "Unrecognized version number in " << GetLocation() << ":" + std::ostringstream oss; + oss << "Unrecognized version number in " << GetLocation() << ":" << " " << header_->magic_[4] << " " << header_->magic_[5] << " " << header_->magic_[6] << " " << header_->magic_[7]; + *error_msg = oss.str(); return false; } return true; diff --git a/runtime/dex_file.h b/runtime/dex_file.h index 12e8440d2a5..035a6910250 100644 --- a/runtime/dex_file.h +++ b/runtime/dex_file.h @@ -350,22 +350,22 @@ class DexFile { // For .dex files, this is the header checksum. // For zip files, this is the classes.dex zip entry CRC32 checksum. // Return true if the checksum could be found, false otherwise. - static bool GetChecksum(const std::string& filename, uint32_t* checksum) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + static bool GetChecksum(const char* filename, uint32_t* checksum, std::string* error_msg); // Opens .dex file, guessing the container format based on file extension - static const DexFile* Open(const std::string& filename, - const std::string& location); + static const DexFile* Open(const char* filename, const char* location, std::string* error_msg); // Opens .dex file, backed by existing memory static const DexFile* Open(const uint8_t* base, size_t size, const std::string& location, - uint32_t location_checksum) { - return OpenMemory(base, size, location, location_checksum, NULL); + uint32_t location_checksum, + std::string* error_msg) { + return OpenMemory(base, size, location, location_checksum, NULL, error_msg); } // Opens .dex file from the classes.dex in a zip archive - static const DexFile* Open(const ZipArchive& zip_archive, const std::string& location); + static const DexFile* Open(const ZipArchive& zip_archive, const std::string& location, + std::string* error_msg); // Closes a .dex file. virtual ~DexFile(); @@ -820,24 +820,24 @@ class DexFile { private: // Opens a .dex file - static const DexFile* OpenFile(int fd, - const std::string& location, - bool verify); + static const DexFile* OpenFile(int fd, const char* location, bool verify, std::string* error_msg); // Opens a dex file from within a .jar, .zip, or .apk file - static const DexFile* OpenZip(int fd, const std::string& location); + static const DexFile* OpenZip(int fd, const std::string& location, std::string* error_msg); // Opens a .dex file at the given address backed by a MemMap static const DexFile* OpenMemory(const std::string& location, uint32_t location_checksum, - MemMap* mem_map); + MemMap* mem_map, + std::string* error_msg); // Opens a .dex file at the given address, optionally backed by a MemMap static const DexFile* OpenMemory(const byte* dex_file, size_t size, const std::string& location, uint32_t location_checksum, - MemMap* mem_map); + MemMap* mem_map, + std::string* error_msg); DexFile(const byte* base, size_t size, const std::string& location, @@ -861,13 +861,13 @@ class DexFile { } // Top-level initializer that calls other Init methods. - bool Init(); + bool Init(std::string* error_msg); // Caches pointers into to the various file sections. void InitMembers(); // Returns true if the header magic and version numbers are of the expected values. - bool CheckMagicAndVersion() const; + bool CheckMagicAndVersion(std::string* error_msg) const; void DecodeDebugInfo0(const CodeItem* code_item, bool is_static, uint32_t method_idx, DexDebugNewPositionCb position_cb, DexDebugNewLocalCb local_cb, diff --git a/runtime/dex_file_test.cc b/runtime/dex_file_test.cc index 7575d4a43c6..543a7b00312 100644 --- a/runtime/dex_file_test.cc +++ b/runtime/dex_file_test.cc @@ -56,7 +56,7 @@ static const char kRawDex[] = "AAMgAAACAAAAiAIAAAQgAAADAAAAlAIAAAAgAAACAAAAqwIAAAAQAAABAAAAxAIAAA=="; static const DexFile* OpenDexFileBase64(const char* base64, - const std::string& location) { + const char* location) { // decode base64 CHECK(base64 != NULL); size_t length; @@ -64,7 +64,7 @@ static const DexFile* OpenDexFileBase64(const char* base64, CHECK(dex_bytes.get() != NULL); // write to provided file - UniquePtr file(OS::CreateEmptyFile(location.c_str())); + UniquePtr file(OS::CreateEmptyFile(location)); CHECK(file.get() != NULL); if (!file->WriteFully(dex_bytes.get(), length)) { PLOG(FATAL) << "Failed to write base64 as dex file"; @@ -73,8 +73,9 @@ static const DexFile* OpenDexFileBase64(const char* base64, // read dex file ScopedObjectAccess soa(Thread::Current()); - const DexFile* dex_file = DexFile::Open(location, location); - CHECK(dex_file != NULL); + std::string error_msg; + const DexFile* dex_file = DexFile::Open(location, location, &error_msg); + CHECK(dex_file != nullptr) << error_msg; EXPECT_EQ(PROT_READ, dex_file->GetPermissions()); EXPECT_TRUE(dex_file->IsReadOnly()); return dex_file; @@ -82,7 +83,7 @@ static const DexFile* OpenDexFileBase64(const char* base64, TEST_F(DexFileTest, Header) { ScratchFile tmp; - UniquePtr raw(OpenDexFileBase64(kRawDex, tmp.GetFilename())); + UniquePtr raw(OpenDexFileBase64(kRawDex, tmp.GetFilename().c_str())); ASSERT_TRUE(raw.get() != NULL); const DexFile::Header& header = raw->GetHeader(); @@ -120,7 +121,9 @@ TEST_F(DexFileTest, GetLocationChecksum) { TEST_F(DexFileTest, GetChecksum) { uint32_t checksum; ScopedObjectAccess soa(Thread::Current()); - EXPECT_TRUE(DexFile::GetChecksum(GetLibCoreDexFileName(), &checksum)); + std::string error_msg; + EXPECT_TRUE(DexFile::GetChecksum(GetLibCoreDexFileName().c_str(), &checksum, &error_msg)) + << error_msg; EXPECT_EQ(java_lang_dex_file_->GetLocationChecksum(), checksum); } diff --git a/runtime/dex_file_verifier.cc b/runtime/dex_file_verifier.cc index 7dc2b3172e0..56bf21d2cb8 100644 --- a/runtime/dex_file_verifier.cc +++ b/runtime/dex_file_verifier.cc @@ -65,12 +65,22 @@ static bool IsDataSectionType(uint32_t map_type) { return true; } -static bool CheckShortyDescriptorMatch(char shorty_char, const char* descriptor, - bool is_return_type) { +bool DexFileVerifier::Verify(const DexFile* dex_file, const byte* begin, size_t size, + const char* location, std::string* error_msg) { + UniquePtr verifier(new DexFileVerifier(dex_file, begin, size, location)); + if (!verifier->Verify()) { + *error_msg = verifier->FailureReason(); + return false; + } + return true; +} + +bool DexFileVerifier::CheckShortyDescriptorMatch(char shorty_char, const char* descriptor, + bool is_return_type) { switch (shorty_char) { case 'V': - if (!is_return_type) { - LOG(ERROR) << "Invalid use of void"; + if (UNLIKELY(!is_return_type)) { + ErrorStringPrintf("Invalid use of void"); return false; } // Intentional fallthrough. @@ -82,62 +92,58 @@ static bool CheckShortyDescriptorMatch(char shorty_char, const char* descriptor, case 'J': case 'S': case 'Z': - if ((descriptor[0] != shorty_char) || (descriptor[1] != '\0')) { - LOG(ERROR) << StringPrintf("Shorty vs. primitive type mismatch: '%c', '%s'", shorty_char, descriptor); + if (UNLIKELY((descriptor[0] != shorty_char) || (descriptor[1] != '\0'))) { + ErrorStringPrintf("Shorty vs. primitive type mismatch: '%c', '%s'", + shorty_char, descriptor); return false; } break; case 'L': - if ((descriptor[0] != 'L') && (descriptor[0] != '[')) { - LOG(ERROR) << StringPrintf("Shorty vs. type mismatch: '%c', '%s'", shorty_char, descriptor); + if (UNLIKELY((descriptor[0] != 'L') && (descriptor[0] != '['))) { + ErrorStringPrintf("Shorty vs. type mismatch: '%c', '%s'", shorty_char, descriptor); return false; } break; default: - LOG(ERROR) << "Bad shorty character: '" << shorty_char << "'"; + ErrorStringPrintf("Bad shorty character: '%c'", shorty_char); return false; } return true; } -bool DexFileVerifier::Verify(const DexFile* dex_file, const byte* begin, size_t size) { - UniquePtr verifier(new DexFileVerifier(dex_file, begin, size)); - return verifier->Verify(); -} - -bool DexFileVerifier::CheckPointerRange(const void* start, const void* end, const char* label) const { +bool DexFileVerifier::CheckPointerRange(const void* start, const void* end, const char* label) { uint32_t range_start = reinterpret_cast(start); uint32_t range_end = reinterpret_cast(end); uint32_t file_start = reinterpret_cast(begin_); uint32_t file_end = file_start + size_; - if ((range_start < file_start) || (range_start > file_end) || - (range_end < file_start) || (range_end > file_end)) { - LOG(ERROR) << StringPrintf("Bad range for %s: %x to %x", label, - range_start - file_start, range_end - file_start); + if (UNLIKELY((range_start < file_start) || (range_start > file_end) || + (range_end < file_start) || (range_end > file_end))) { + ErrorStringPrintf("Bad range for %s: %x to %x", label, + range_start - file_start, range_end - file_start); return false; } return true; } bool DexFileVerifier::CheckListSize(const void* start, uint32_t count, - uint32_t element_size, const char* label) const { + uint32_t element_size, const char* label) { const byte* list_start = reinterpret_cast(start); return CheckPointerRange(list_start, list_start + (count * element_size), label); } -bool DexFileVerifier::CheckIndex(uint32_t field, uint32_t limit, const char* label) const { - if (field >= limit) { - LOG(ERROR) << StringPrintf("Bad index for %s: %x >= %x", label, field, limit); +bool DexFileVerifier::CheckIndex(uint32_t field, uint32_t limit, const char* label) { + if (UNLIKELY(field >= limit)) { + ErrorStringPrintf("Bad index for %s: %x >= %x", label, field, limit); return false; } return true; } -bool DexFileVerifier::CheckHeader() const { +bool DexFileVerifier::CheckHeader() { // Check file size from the header. uint32_t expected_size = header_->file_size_; if (size_ != expected_size) { - LOG(ERROR) << "Bad file size (" << size_ << ", expected " << expected_size << ")"; + ErrorStringPrintf("Bad file size (%zd, expected %ud)", size_, expected_size); return false; } @@ -147,25 +153,25 @@ bool DexFileVerifier::CheckHeader() const { const byte* non_sum_ptr = reinterpret_cast(header_) + non_sum; adler_checksum = adler32(adler_checksum, non_sum_ptr, expected_size - non_sum); if (adler_checksum != header_->checksum_) { - LOG(ERROR) << StringPrintf("Bad checksum (%08x, expected %08x)", adler_checksum, header_->checksum_); + ErrorStringPrintf("Bad checksum (%08x, expected %08x)", adler_checksum, header_->checksum_); return false; } // Check the contents of the header. if (header_->endian_tag_ != DexFile::kDexEndianConstant) { - LOG(ERROR) << StringPrintf("Unexpected endian_tag: %x", header_->endian_tag_); + ErrorStringPrintf("Unexpected endian_tag: %x", header_->endian_tag_); return false; } if (header_->header_size_ != sizeof(DexFile::Header)) { - LOG(ERROR) << "Bad header size: " << header_->header_size_; + ErrorStringPrintf("Bad header size: %ud", header_->header_size_); return false; } return true; } -bool DexFileVerifier::CheckMap() const { +bool DexFileVerifier::CheckMap() { const DexFile::MapList* map = reinterpret_cast(begin_ + header_->map_off_); const DexFile::MapItem* item = map->list_; @@ -182,19 +188,20 @@ bool DexFileVerifier::CheckMap() const { // Check the items listed in the map. for (uint32_t i = 0; i < count; i++) { - if (last_offset >= item->offset_ && i != 0) { - LOG(ERROR) << StringPrintf("Out of order map item: %x then %x", last_offset, item->offset_); + if (UNLIKELY(last_offset >= item->offset_ && i != 0)) { + ErrorStringPrintf("Out of order map item: %x then %x", last_offset, item->offset_); return false; } - if (item->offset_ >= header_->file_size_) { - LOG(ERROR) << StringPrintf("Map item after end of file: %x, size %x", item->offset_, header_->file_size_); + if (UNLIKELY(item->offset_ >= header_->file_size_)) { + ErrorStringPrintf("Map item after end of file: %x, size %x", + item->offset_, header_->file_size_); return false; } if (IsDataSectionType(item->type_)) { uint32_t icount = item->size_; - if (icount > data_items_left) { - LOG(ERROR) << "Too many items in data section: " << data_item_count + icount; + if (UNLIKELY(icount > data_items_left)) { + ErrorStringPrintf("Too many items in data section: %ud", data_item_count + icount); return false; } data_items_left -= icount; @@ -203,13 +210,13 @@ bool DexFileVerifier::CheckMap() const { uint32_t bit = MapTypeToBitMask(item->type_); - if (bit == 0) { - LOG(ERROR) << StringPrintf("Unknown map section type %x", item->type_); + if (UNLIKELY(bit == 0)) { + ErrorStringPrintf("Unknown map section type %x", item->type_); return false; } - if ((used_bits & bit) != 0) { - LOG(ERROR) << StringPrintf("Duplicate map section of type %x", item->type_); + if (UNLIKELY((used_bits & bit) != 0)) { + ErrorStringPrintf("Duplicate map section of type %x", item->type_); return false; } @@ -219,63 +226,59 @@ bool DexFileVerifier::CheckMap() const { } // Check for missing sections in the map. - if ((used_bits & MapTypeToBitMask(DexFile::kDexTypeHeaderItem)) == 0) { - LOG(ERROR) << "Map is missing header entry"; + if (UNLIKELY((used_bits & MapTypeToBitMask(DexFile::kDexTypeHeaderItem)) == 0)) { + ErrorStringPrintf("Map is missing header entry"); return false; } - if ((used_bits & MapTypeToBitMask(DexFile::kDexTypeMapList)) == 0) { - LOG(ERROR) << "Map is missing map_list entry"; + if (UNLIKELY((used_bits & MapTypeToBitMask(DexFile::kDexTypeMapList)) == 0)) { + ErrorStringPrintf("Map is missing map_list entry"); return false; } - if ((used_bits & MapTypeToBitMask(DexFile::kDexTypeStringIdItem)) == 0 && - ((header_->string_ids_off_ != 0) || (header_->string_ids_size_ != 0))) { - LOG(ERROR) << "Map is missing string_ids entry"; + if (UNLIKELY((used_bits & MapTypeToBitMask(DexFile::kDexTypeStringIdItem)) == 0 && + ((header_->string_ids_off_ != 0) || (header_->string_ids_size_ != 0)))) { + ErrorStringPrintf("Map is missing string_ids entry"); return false; } - if ((used_bits & MapTypeToBitMask(DexFile::kDexTypeTypeIdItem)) == 0 && - ((header_->type_ids_off_ != 0) || (header_->type_ids_size_ != 0))) { - LOG(ERROR) << "Map is missing type_ids entry"; + if (UNLIKELY((used_bits & MapTypeToBitMask(DexFile::kDexTypeTypeIdItem)) == 0 && + ((header_->type_ids_off_ != 0) || (header_->type_ids_size_ != 0)))) { + ErrorStringPrintf("Map is missing type_ids entry"); return false; } - if ((used_bits & MapTypeToBitMask(DexFile::kDexTypeProtoIdItem)) == 0 && - ((header_->proto_ids_off_ != 0) || (header_->proto_ids_size_ != 0))) { - LOG(ERROR) << "Map is missing proto_ids entry"; + if (UNLIKELY((used_bits & MapTypeToBitMask(DexFile::kDexTypeProtoIdItem)) == 0 && + ((header_->proto_ids_off_ != 0) || (header_->proto_ids_size_ != 0)))) { + ErrorStringPrintf("Map is missing proto_ids entry"); return false; } - if ((used_bits & MapTypeToBitMask(DexFile::kDexTypeFieldIdItem)) == 0 && - ((header_->field_ids_off_ != 0) || (header_->field_ids_size_ != 0))) { - LOG(ERROR) << "Map is missing field_ids entry"; + if (UNLIKELY((used_bits & MapTypeToBitMask(DexFile::kDexTypeFieldIdItem)) == 0 && + ((header_->field_ids_off_ != 0) || (header_->field_ids_size_ != 0)))) { + ErrorStringPrintf("Map is missing field_ids entry"); return false; } - if ((used_bits & MapTypeToBitMask(DexFile::kDexTypeMethodIdItem)) == 0 && - ((header_->method_ids_off_ != 0) || (header_->method_ids_size_ != 0))) { - LOG(ERROR) << "Map is missing method_ids entry"; + if (UNLIKELY((used_bits & MapTypeToBitMask(DexFile::kDexTypeMethodIdItem)) == 0 && + ((header_->method_ids_off_ != 0) || (header_->method_ids_size_ != 0)))) { + ErrorStringPrintf("Map is missing method_ids entry"); return false; } - if ((used_bits & MapTypeToBitMask(DexFile::kDexTypeClassDefItem)) == 0 && - ((header_->class_defs_off_ != 0) || (header_->class_defs_size_ != 0))) { - LOG(ERROR) << "Map is missing class_defs entry"; + if (UNLIKELY((used_bits & MapTypeToBitMask(DexFile::kDexTypeClassDefItem)) == 0 && + ((header_->class_defs_off_ != 0) || (header_->class_defs_size_ != 0)))) { + ErrorStringPrintf("Map is missing class_defs entry"); return false; } - return true; } uint32_t DexFileVerifier::ReadUnsignedLittleEndian(uint32_t size) { uint32_t result = 0; - if (!CheckPointerRange(ptr_, ptr_ + size, "encoded_value")) { - return 0; - } - - for (uint32_t i = 0; i < size; i++) { - result |= ((uint32_t) *(ptr_++)) << (i * 8); + if (LIKELY(CheckPointerRange(ptr_, ptr_ + size, "encoded_value"))) { + for (uint32_t i = 0; i < size; i++) { + result |= ((uint32_t) *(ptr_++)) << (i * 8); + } } - return result; } bool DexFileVerifier::CheckAndGetHandlerOffsets(const DexFile::CodeItem* code_item, - uint32_t* handler_offsets, uint32_t handlers_size) { + uint32_t* handler_offsets, uint32_t handlers_size) { const byte* handlers_base = DexFile::GetCatchHandlerData(*code_item, 0); for (uint32_t i = 0; i < handlers_size; i++) { @@ -283,8 +286,8 @@ bool DexFileVerifier::CheckAndGetHandlerOffsets(const DexFile::CodeItem* code_it uint32_t offset = reinterpret_cast(ptr_) - reinterpret_cast(handlers_base); int32_t size = DecodeSignedLeb128(&ptr_); - if ((size < -65536) || (size > 65536)) { - LOG(ERROR) << "Invalid exception handler size: " << size; + if (UNLIKELY((size < -65536) || (size > 65536))) { + ErrorStringPrintf("Invalid exception handler size: %d", size); return false; } @@ -304,16 +307,16 @@ bool DexFileVerifier::CheckAndGetHandlerOffsets(const DexFile::CodeItem* code_it } uint32_t addr = DecodeUnsignedLeb128(&ptr_); - if (addr >= code_item->insns_size_in_code_units_) { - LOG(ERROR) << StringPrintf("Invalid handler addr: %x", addr); + if (UNLIKELY(addr >= code_item->insns_size_in_code_units_)) { + ErrorStringPrintf("Invalid handler addr: %x", addr); return false; } } if (catch_all) { uint32_t addr = DecodeUnsignedLeb128(&ptr_); - if (addr >= code_item->insns_size_in_code_units_) { - LOG(ERROR) << StringPrintf("Invalid handler catch_all_addr: %x", addr); + if (UNLIKELY(addr >= code_item->insns_size_in_code_units_)) { + ErrorStringPrintf("Invalid handler catch_all_addr: %x", addr); return false; } } @@ -323,21 +326,21 @@ bool DexFileVerifier::CheckAndGetHandlerOffsets(const DexFile::CodeItem* code_it } bool DexFileVerifier::CheckClassDataItemField(uint32_t idx, uint32_t access_flags, - bool expect_static) const { + bool expect_static) { if (!CheckIndex(idx, header_->field_ids_size_, "class_data_item field_idx")) { return false; } bool is_static = (access_flags & kAccStatic) != 0; - if (is_static != expect_static) { - LOG(ERROR) << "Static/instance field not in expected list"; + if (UNLIKELY(is_static != expect_static)) { + ErrorStringPrintf("Static/instance field not in expected list"); return false; } uint32_t access_field_mask = kAccPublic | kAccPrivate | kAccProtected | kAccStatic | kAccFinal | kAccVolatile | kAccTransient | kAccSynthetic | kAccEnum; - if ((access_flags & ~access_field_mask) != 0) { - LOG(ERROR) << StringPrintf("Bad class_data_item field access_flags %x", access_flags); + if (UNLIKELY((access_flags & ~access_field_mask) != 0)) { + ErrorStringPrintf("Bad class_data_item field access_flags %x", access_flags); return false; } @@ -345,7 +348,7 @@ bool DexFileVerifier::CheckClassDataItemField(uint32_t idx, uint32_t access_flag } bool DexFileVerifier::CheckClassDataItemMethod(uint32_t idx, uint32_t access_flags, - uint32_t code_offset, bool expect_direct) const { + uint32_t code_offset, bool expect_direct) { if (!CheckIndex(idx, header_->method_ids_size_, "class_data_item method_idx")) { return false; } @@ -355,26 +358,27 @@ bool DexFileVerifier::CheckClassDataItemMethod(uint32_t idx, uint32_t access_fla bool is_synchronized = (access_flags & kAccSynchronized) != 0; bool allow_synchronized = (access_flags & kAccNative) != 0; - if (is_direct != expect_direct) { - LOG(ERROR) << "Direct/virtual method not in expected list"; + if (UNLIKELY(is_direct != expect_direct)) { + ErrorStringPrintf("Direct/virtual method not in expected list"); return false; } uint32_t access_method_mask = kAccPublic | kAccPrivate | kAccProtected | kAccStatic | kAccFinal | kAccSynchronized | kAccBridge | kAccVarargs | kAccNative | kAccAbstract | kAccStrict | kAccSynthetic | kAccConstructor | kAccDeclaredSynchronized; - if (((access_flags & ~access_method_mask) != 0) || (is_synchronized && !allow_synchronized)) { - LOG(ERROR) << StringPrintf("Bad class_data_item method access_flags %x", access_flags); + if (UNLIKELY(((access_flags & ~access_method_mask) != 0) || + (is_synchronized && !allow_synchronized))) { + ErrorStringPrintf("Bad class_data_item method access_flags %x", access_flags); return false; } - if (expect_code && code_offset == 0) { - LOG(ERROR)<< StringPrintf("Unexpected zero value for class_data_item method code_off" - " with access flags %x", access_flags); + if (UNLIKELY(expect_code && (code_offset == 0))) { + ErrorStringPrintf("Unexpected zero value for class_data_item method code_off with access " + "flags %x", access_flags); return false; - } else if (!expect_code && code_offset != 0) { - LOG(ERROR) << StringPrintf("Unexpected non-zero value %x for class_data_item method code_off" - " with access flags %x", code_offset, access_flags); + } else if (UNLIKELY(!expect_code && (code_offset != 0))) { + ErrorStringPrintf("Unexpected non-zero value %x for class_data_item method code_off" + " with access flags %x", code_offset, access_flags); return false; } @@ -387,8 +391,8 @@ bool DexFileVerifier::CheckPadding(uint32_t offset, uint32_t aligned_offset) { return false; } while (offset < aligned_offset) { - if (*ptr_ != '\0') { - LOG(ERROR) << StringPrintf("Non-zero padding %x before section start at %x", *ptr_, offset); + if (UNLIKELY(*ptr_ != '\0')) { + ErrorStringPrintf("Non-zero padding %x before section start at %x", *ptr_, offset); return false; } ptr_++; @@ -409,24 +413,24 @@ bool DexFileVerifier::CheckEncodedValue() { switch (value_type) { case DexFile::kDexAnnotationByte: - if (value_arg != 0) { - LOG(ERROR) << StringPrintf("Bad encoded_value byte size %x", value_arg); + if (UNLIKELY(value_arg != 0)) { + ErrorStringPrintf("Bad encoded_value byte size %x", value_arg); return false; } ptr_++; break; case DexFile::kDexAnnotationShort: case DexFile::kDexAnnotationChar: - if (value_arg > 1) { - LOG(ERROR) << StringPrintf("Bad encoded_value char/short size %x", value_arg); + if (UNLIKELY(value_arg > 1)) { + ErrorStringPrintf("Bad encoded_value char/short size %x", value_arg); return false; } ptr_ += value_arg + 1; break; case DexFile::kDexAnnotationInt: case DexFile::kDexAnnotationFloat: - if (value_arg > 3) { - LOG(ERROR) << StringPrintf("Bad encoded_value int/float size %x", value_arg); + if (UNLIKELY(value_arg > 3)) { + ErrorStringPrintf("Bad encoded_value int/float size %x", value_arg); return false; } ptr_ += value_arg + 1; @@ -436,8 +440,8 @@ bool DexFileVerifier::CheckEncodedValue() { ptr_ += value_arg + 1; break; case DexFile::kDexAnnotationString: { - if (value_arg > 3) { - LOG(ERROR) << StringPrintf("Bad encoded_value string size %x", value_arg); + if (UNLIKELY(value_arg > 3)) { + ErrorStringPrintf("Bad encoded_value string size %x", value_arg); return false; } uint32_t idx = ReadUnsignedLittleEndian(value_arg + 1); @@ -447,8 +451,8 @@ bool DexFileVerifier::CheckEncodedValue() { break; } case DexFile::kDexAnnotationType: { - if (value_arg > 3) { - LOG(ERROR) << StringPrintf("Bad encoded_value type size %x", value_arg); + if (UNLIKELY(value_arg > 3)) { + ErrorStringPrintf("Bad encoded_value type size %x", value_arg); return false; } uint32_t idx = ReadUnsignedLittleEndian(value_arg + 1); @@ -459,8 +463,8 @@ bool DexFileVerifier::CheckEncodedValue() { } case DexFile::kDexAnnotationField: case DexFile::kDexAnnotationEnum: { - if (value_arg > 3) { - LOG(ERROR) << StringPrintf("Bad encoded_value field/enum size %x", value_arg); + if (UNLIKELY(value_arg > 3)) { + ErrorStringPrintf("Bad encoded_value field/enum size %x", value_arg); return false; } uint32_t idx = ReadUnsignedLittleEndian(value_arg + 1); @@ -470,8 +474,8 @@ bool DexFileVerifier::CheckEncodedValue() { break; } case DexFile::kDexAnnotationMethod: { - if (value_arg > 3) { - LOG(ERROR) << StringPrintf("Bad encoded_value method size %x", value_arg); + if (UNLIKELY(value_arg > 3)) { + ErrorStringPrintf("Bad encoded_value method size %x", value_arg); return false; } uint32_t idx = ReadUnsignedLittleEndian(value_arg + 1); @@ -481,8 +485,8 @@ bool DexFileVerifier::CheckEncodedValue() { break; } case DexFile::kDexAnnotationArray: - if (value_arg != 0) { - LOG(ERROR) << StringPrintf("Bad encoded_value array value_arg %x", value_arg); + if (UNLIKELY(value_arg != 0)) { + ErrorStringPrintf("Bad encoded_value array value_arg %x", value_arg); return false; } if (!CheckEncodedArray()) { @@ -490,8 +494,8 @@ bool DexFileVerifier::CheckEncodedValue() { } break; case DexFile::kDexAnnotationAnnotation: - if (value_arg != 0) { - LOG(ERROR) << StringPrintf("Bad encoded_value annotation value_arg %x", value_arg); + if (UNLIKELY(value_arg != 0)) { + ErrorStringPrintf("Bad encoded_value annotation value_arg %x", value_arg); return false; } if (!CheckEncodedAnnotation()) { @@ -499,19 +503,19 @@ bool DexFileVerifier::CheckEncodedValue() { } break; case DexFile::kDexAnnotationNull: - if (value_arg != 0) { - LOG(ERROR) << StringPrintf("Bad encoded_value null value_arg %x", value_arg); + if (UNLIKELY(value_arg != 0)) { + ErrorStringPrintf("Bad encoded_value null value_arg %x", value_arg); return false; } break; case DexFile::kDexAnnotationBoolean: - if (value_arg > 1) { - LOG(ERROR) << StringPrintf("Bad encoded_value boolean size %x", value_arg); + if (UNLIKELY(value_arg > 1)) { + ErrorStringPrintf("Bad encoded_value boolean size %x", value_arg); return false; } break; default: - LOG(ERROR) << StringPrintf("Bogus encoded_value value_type %x", value_type); + ErrorStringPrintf("Bogus encoded_value value_type %x", value_type); return false; } @@ -523,7 +527,7 @@ bool DexFileVerifier::CheckEncodedArray() { while (size--) { if (!CheckEncodedValue()) { - LOG(ERROR) << "Bad encoded_array value"; + failure_reason_ = StringPrintf("Bad encoded_array value: %s", failure_reason_.c_str()); return false; } } @@ -545,9 +549,9 @@ bool DexFileVerifier::CheckEncodedAnnotation() { return false; } - if (last_idx >= idx && i != 0) { - LOG(ERROR) << StringPrintf("Out-of-order annotation_element name_idx: %x then %x", - last_idx, idx); + if (UNLIKELY(last_idx >= idx && i != 0)) { + ErrorStringPrintf("Out-of-order annotation_element name_idx: %x then %x", + last_idx, idx); return false; } @@ -596,21 +600,22 @@ bool DexFileVerifier::CheckIntraCodeItem() { return false; } - if (code_item->ins_size_ > code_item->registers_size_) { - LOG(ERROR) << "ins_size (" << code_item->ins_size_ << ") > registers_size (" - << code_item->registers_size_ << ")"; + if (UNLIKELY(code_item->ins_size_ > code_item->registers_size_)) { + ErrorStringPrintf("ins_size (%ud) > registers_size (%ud)", + code_item->ins_size_, code_item->registers_size_); return false; } - if ((code_item->outs_size_ > 5) && (code_item->outs_size_ > code_item->registers_size_)) { + if (UNLIKELY((code_item->outs_size_ > 5) && + (code_item->outs_size_ > code_item->registers_size_))) { /* * outs_size can be up to 5, even if registers_size is smaller, since the * short forms of method invocation allow repetitions of a register multiple * times within a single parameter list. However, longer parameter lists * need to be represented in-order in the register file. */ - LOG(ERROR) << "outs_size (" << code_item->outs_size_ << ") > registers_size (" - << code_item->registers_size_ << ")"; + ErrorStringPrintf("outs_size (%ud) > registers_size (%ud)", + code_item->outs_size_, code_item->registers_size_); return false; } @@ -629,7 +634,7 @@ bool DexFileVerifier::CheckIntraCodeItem() { // try_items are 4-byte aligned. Verify the spacer is 0. if ((((uint32_t) &insns[insns_size] & 3) != 0) && (insns[insns_size] != 0)) { - LOG(ERROR) << StringPrintf("Non-zero padding: %x", insns[insns_size]); + ErrorStringPrintf("Non-zero padding: %x", insns[insns_size]); return false; } @@ -641,8 +646,8 @@ bool DexFileVerifier::CheckIntraCodeItem() { return false; } - if ((handlers_size == 0) || (handlers_size >= 65536)) { - LOG(ERROR) << "Invalid handlers_size: " << handlers_size; + if (UNLIKELY((handlers_size == 0) || (handlers_size >= 65536))) { + ErrorStringPrintf("Invalid handlers_size: %ud", handlers_size); return false; } @@ -653,14 +658,13 @@ bool DexFileVerifier::CheckIntraCodeItem() { uint32_t last_addr = 0; while (try_items_size--) { - if (try_items->start_addr_ < last_addr) { - LOG(ERROR) << StringPrintf("Out-of_order try_item with start_addr: %x", - try_items->start_addr_); + if (UNLIKELY(try_items->start_addr_ < last_addr)) { + ErrorStringPrintf("Out-of_order try_item with start_addr: %x", try_items->start_addr_); return false; } - if (try_items->start_addr_ >= insns_size) { - LOG(ERROR) << StringPrintf("Invalid try_item start_addr: %x", try_items->start_addr_); + if (UNLIKELY(try_items->start_addr_ >= insns_size)) { + ErrorStringPrintf("Invalid try_item start_addr: %x", try_items->start_addr_); return false; } @@ -671,14 +675,14 @@ bool DexFileVerifier::CheckIntraCodeItem() { } } - if (i == handlers_size) { - LOG(ERROR) << StringPrintf("Bogus handler offset: %x", try_items->handler_off_); + if (UNLIKELY(i == handlers_size)) { + ErrorStringPrintf("Bogus handler offset: %x", try_items->handler_off_); return false; } last_addr = try_items->start_addr_ + try_items->insn_count_; - if (last_addr > insns_size) { - LOG(ERROR) << StringPrintf("Invalid try_item insn_count: %x", try_items->insn_count_); + if (UNLIKELY(last_addr > insns_size)) { + ErrorStringPrintf("Invalid try_item insn_count: %x", try_items->insn_count_); return false; } @@ -693,8 +697,8 @@ bool DexFileVerifier::CheckIntraStringDataItem() { const byte* file_end = begin_ + size_; for (uint32_t i = 0; i < size; i++) { - if (ptr_ >= file_end) { - LOG(ERROR) << "String data would go beyond end-of-file"; + if (UNLIKELY(ptr_ >= file_end)) { + ErrorStringPrintf("String data would go beyond end-of-file"); return false; } @@ -704,8 +708,8 @@ bool DexFileVerifier::CheckIntraStringDataItem() { switch (byte >> 4) { case 0x00: // Special case of bit pattern 0xxx. - if (byte == 0) { - LOG(ERROR) << StringPrintf("String data shorter than indicated utf16_size %x", size); + if (UNLIKELY(byte == 0)) { + ErrorStringPrintf("String data shorter than indicated utf16_size %x", size); return false; } break; @@ -725,19 +729,19 @@ bool DexFileVerifier::CheckIntraStringDataItem() { case 0x0f: // Illegal bit patterns 10xx or 1111. // Note: 1111 is valid for normal UTF-8, but not here. - LOG(ERROR) << StringPrintf("Illegal start byte %x in string data", byte); + ErrorStringPrintf("Illegal start byte %x in string data", byte); return false; case 0x0c: case 0x0d: { // Bit pattern 110x has an additional byte. uint8_t byte2 = *(ptr_++); - if ((byte2 & 0xc0) != 0x80) { - LOG(ERROR) << StringPrintf("Illegal continuation byte %x in string data", byte2); + if (UNLIKELY((byte2 & 0xc0) != 0x80)) { + ErrorStringPrintf("Illegal continuation byte %x in string data", byte2); return false; } uint16_t value = ((byte & 0x1f) << 6) | (byte2 & 0x3f); - if ((value != 0) && (value < 0x80)) { - LOG(ERROR) << StringPrintf("Illegal representation for value %x in string data", value); + if (UNLIKELY((value != 0) && (value < 0x80))) { + ErrorStringPrintf("Illegal representation for value %x in string data", value); return false; } break; @@ -745,18 +749,18 @@ bool DexFileVerifier::CheckIntraStringDataItem() { case 0x0e: { // Bit pattern 1110 has 2 additional bytes. uint8_t byte2 = *(ptr_++); - if ((byte2 & 0xc0) != 0x80) { - LOG(ERROR) << StringPrintf("Illegal continuation byte %x in string data", byte2); + if (UNLIKELY((byte2 & 0xc0) != 0x80)) { + ErrorStringPrintf("Illegal continuation byte %x in string data", byte2); return false; } uint8_t byte3 = *(ptr_++); - if ((byte3 & 0xc0) != 0x80) { - LOG(ERROR) << StringPrintf("Illegal continuation byte %x in string data", byte3); + if (UNLIKELY((byte3 & 0xc0) != 0x80)) { + ErrorStringPrintf("Illegal continuation byte %x in string data", byte3); return false; } uint16_t value = ((byte & 0x0f) << 12) | ((byte2 & 0x3f) << 6) | (byte3 & 0x3f); - if (value < 0x800) { - LOG(ERROR) << StringPrintf("Illegal representation for value %x in string data", value); + if (UNLIKELY(value < 0x800)) { + ErrorStringPrintf("Illegal representation for value %x in string data", value); return false; } break; @@ -764,8 +768,8 @@ bool DexFileVerifier::CheckIntraStringDataItem() { } } - if (*(ptr_++) != '\0') { - LOG(ERROR) << StringPrintf("String longer than indicated size %x", size); + if (UNLIKELY(*(ptr_++) != '\0')) { + ErrorStringPrintf("String longer than indicated size %x", size); return false; } @@ -775,8 +779,8 @@ bool DexFileVerifier::CheckIntraStringDataItem() { bool DexFileVerifier::CheckIntraDebugInfoItem() { DecodeUnsignedLeb128(&ptr_); uint32_t parameters_size = DecodeUnsignedLeb128(&ptr_); - if (parameters_size > 65536) { - LOG(ERROR) << StringPrintf("Invalid parameters_size: %x", parameters_size); + if (UNLIKELY(parameters_size > 65536)) { + ErrorStringPrintf("Invalid parameters_size: %x", parameters_size); return false; } @@ -806,8 +810,8 @@ bool DexFileVerifier::CheckIntraDebugInfoItem() { } case DexFile::DBG_START_LOCAL: { uint32_t reg_num = DecodeUnsignedLeb128(&ptr_); - if (reg_num >= 65536) { - LOG(ERROR) << StringPrintf("Bad reg_num for opcode %x", opcode); + if (UNLIKELY(reg_num >= 65536)) { + ErrorStringPrintf("Bad reg_num for opcode %x", opcode); return false; } uint32_t name_idx = DecodeUnsignedLeb128(&ptr_); @@ -829,16 +833,16 @@ bool DexFileVerifier::CheckIntraDebugInfoItem() { case DexFile::DBG_END_LOCAL: case DexFile::DBG_RESTART_LOCAL: { uint32_t reg_num = DecodeUnsignedLeb128(&ptr_); - if (reg_num >= 65536) { - LOG(ERROR) << StringPrintf("Bad reg_num for opcode %x", opcode); + if (UNLIKELY(reg_num >= 65536)) { + ErrorStringPrintf("Bad reg_num for opcode %x", opcode); return false; } break; } case DexFile::DBG_START_LOCAL_EXTENDED: { uint32_t reg_num = DecodeUnsignedLeb128(&ptr_); - if (reg_num >= 65536) { - LOG(ERROR) << StringPrintf("Bad reg_num for opcode %x", opcode); + if (UNLIKELY(reg_num >= 65536)) { + ErrorStringPrintf("Bad reg_num for opcode %x", opcode); return false; } uint32_t name_idx = DecodeUnsignedLeb128(&ptr_); @@ -890,7 +894,7 @@ bool DexFileVerifier::CheckIntraAnnotationItem() { case DexFile::kDexVisibilitySystem: break; default: - LOG(ERROR) << StringPrintf("Bad annotation visibility: %x", *ptr_); + ErrorStringPrintf("Bad annotation visibility: %x", *ptr_); return false; } @@ -918,8 +922,8 @@ bool DexFileVerifier::CheckIntraAnnotationsDirectoryItem() { uint32_t last_idx = 0; for (uint32_t i = 0; i < field_count; i++) { - if (last_idx >= field_item->field_idx_ && i != 0) { - LOG(ERROR) << StringPrintf("Out-of-order field_idx for annotation: %x then %x", last_idx, field_item->field_idx_); + if (UNLIKELY(last_idx >= field_item->field_idx_ && i != 0)) { + ErrorStringPrintf("Out-of-order field_idx for annotation: %x then %x", last_idx, field_item->field_idx_); return false; } last_idx = field_item->field_idx_; @@ -936,9 +940,9 @@ bool DexFileVerifier::CheckIntraAnnotationsDirectoryItem() { last_idx = 0; for (uint32_t i = 0; i < method_count; i++) { - if (last_idx >= method_item->method_idx_ && i != 0) { - LOG(ERROR) << StringPrintf("Out-of-order method_idx for annotation: %x then %x", - last_idx, method_item->method_idx_); + if (UNLIKELY(last_idx >= method_item->method_idx_ && i != 0)) { + ErrorStringPrintf("Out-of-order method_idx for annotation: %x then %x", + last_idx, method_item->method_idx_); return false; } last_idx = method_item->method_idx_; @@ -950,15 +954,15 @@ bool DexFileVerifier::CheckIntraAnnotationsDirectoryItem() { reinterpret_cast(method_item); uint32_t parameter_count = item->parameters_size_; if (!CheckListSize(parameter_item, parameter_count, sizeof(DexFile::ParameterAnnotationsItem), - "parameter_annotations list")) { + "parameter_annotations list")) { return false; } last_idx = 0; for (uint32_t i = 0; i < parameter_count; i++) { - if (last_idx >= parameter_item->method_idx_ && i != 0) { - LOG(ERROR) << StringPrintf("Out-of-order method_idx for annotation: %x then %x", - last_idx, parameter_item->method_idx_); + if (UNLIKELY(last_idx >= parameter_item->method_idx_ && i != 0)) { + ErrorStringPrintf("Out-of-order method_idx for annotation: %x then %x", + last_idx, parameter_item->method_idx_); return false; } last_idx = parameter_item->method_idx_; @@ -1059,7 +1063,7 @@ bool DexFileVerifier::CheckIntraSectionIterate(uint32_t offset, uint32_t count, if (!CheckPointerRange(list, list + 1, "annotation_set_ref_list") || !CheckListSize(item, count, sizeof(DexFile::AnnotationSetRefItem), - "annotation_set_ref_list size")) { + "annotation_set_ref_list size")) { return false; } ptr_ = reinterpret_cast(item + count); @@ -1121,7 +1125,7 @@ bool DexFileVerifier::CheckIntraSectionIterate(uint32_t offset, uint32_t count, break; } default: - LOG(ERROR) << StringPrintf("Unknown map item type %x", type); + ErrorStringPrintf("Unknown map item type %x", type); return false; } @@ -1130,8 +1134,8 @@ bool DexFileVerifier::CheckIntraSectionIterate(uint32_t offset, uint32_t count, } aligned_offset = reinterpret_cast(ptr_) - reinterpret_cast(begin_); - if (aligned_offset > size_) { - LOG(ERROR) << StringPrintf("Item %d at ends out of bounds", i); + if (UNLIKELY(aligned_offset > size_)) { + ErrorStringPrintf("Item %d at ends out of bounds", i); return false; } @@ -1172,17 +1176,17 @@ bool DexFileVerifier::CheckIntraIdSection(uint32_t offset, uint32_t count, uint1 expected_size = header_->class_defs_size_; break; default: - LOG(ERROR) << StringPrintf("Bad type for id section: %x", type); + ErrorStringPrintf("Bad type for id section: %x", type); return false; } // Check that the offset and size are what were expected from the header. - if (offset != expected_offset) { - LOG(ERROR) << StringPrintf("Bad offset for section: got %x, expected %x", offset, expected_offset); + if (UNLIKELY(offset != expected_offset)) { + ErrorStringPrintf("Bad offset for section: got %x, expected %x", offset, expected_offset); return false; } - if (count != expected_size) { - LOG(ERROR) << StringPrintf("Bad size for section: got %x, expected %x", count, expected_size); + if (UNLIKELY(count != expected_size)) { + ErrorStringPrintf("Bad size for section: got %x, expected %x", count, expected_size); return false; } @@ -1194,8 +1198,8 @@ bool DexFileVerifier::CheckIntraDataSection(uint32_t offset, uint32_t count, uin uint32_t data_end = data_start + header_->data_size_; // Sanity check the offset of the section. - if ((offset < data_start) || (offset > data_end)) { - LOG(ERROR) << StringPrintf("Bad offset for data subsection: %x", offset); + if (UNLIKELY((offset < data_start) || (offset > data_end))) { + ErrorStringPrintf("Bad offset for data subsection: %x", offset); return false; } @@ -1205,7 +1209,7 @@ bool DexFileVerifier::CheckIntraDataSection(uint32_t offset, uint32_t count, uin uint32_t next_offset = reinterpret_cast(ptr_) - reinterpret_cast(begin_); if (next_offset > data_end) { - LOG(ERROR) << StringPrintf("Out-of-bounds end of data subsection: %x", next_offset); + ErrorStringPrintf("Out-of-bounds end of data subsection: %x", next_offset); return false; } @@ -1229,20 +1233,20 @@ bool DexFileVerifier::CheckIntraSection() { // Check for padding and overlap between items. if (!CheckPadding(offset, section_offset)) { return false; - } else if (offset > section_offset) { - LOG(ERROR) << StringPrintf("Section overlap or out-of-order map: %x, %x", offset, section_offset); + } else if (UNLIKELY(offset > section_offset)) { + ErrorStringPrintf("Section overlap or out-of-order map: %x, %x", offset, section_offset); return false; } // Check each item based on its type. switch (type) { case DexFile::kDexTypeHeaderItem: - if (section_count != 1) { - LOG(ERROR) << "Multiple header items"; + if (UNLIKELY(section_count != 1)) { + ErrorStringPrintf("Multiple header items"); return false; } - if (section_offset != 0) { - LOG(ERROR) << StringPrintf("Header at %x, not at start of file", section_offset); + if (UNLIKELY(section_offset != 0)) { + ErrorStringPrintf("Header at %x, not at start of file", section_offset); return false; } ptr_ = begin_ + header_->header_size_; @@ -1260,13 +1264,13 @@ bool DexFileVerifier::CheckIntraSection() { offset = reinterpret_cast(ptr_) - reinterpret_cast(begin_); break; case DexFile::kDexTypeMapList: - if (section_count != 1) { - LOG(ERROR) << "Multiple map list items"; + if (UNLIKELY(section_count != 1)) { + ErrorStringPrintf("Multiple map list items"); return false; } - if (section_offset != header_->map_off_) { - LOG(ERROR) << StringPrintf("Map not at header-defined offset: %x, expected %x", - section_offset, header_->map_off_); + if (UNLIKELY(section_offset != header_->map_off_)) { + ErrorStringPrintf("Map not at header-defined offset: %x, expected %x", + section_offset, header_->map_off_); return false; } ptr_ += sizeof(uint32_t) + (map->size_ * sizeof(DexFile::MapItem)); @@ -1288,7 +1292,7 @@ bool DexFileVerifier::CheckIntraSection() { offset = reinterpret_cast(ptr_) - reinterpret_cast(begin_); break; default: - LOG(ERROR) << StringPrintf("Unknown map item type %x", type); + ErrorStringPrintf("Unknown map item type %x", type); return false; } @@ -1300,13 +1304,13 @@ bool DexFileVerifier::CheckIntraSection() { bool DexFileVerifier::CheckOffsetToTypeMap(uint32_t offset, uint16_t type) { auto it = offset_to_type_map_.find(offset); - if (it == offset_to_type_map_.end()) { - LOG(ERROR) << StringPrintf("No data map entry found @ %x; expected %x", offset, type); + if (UNLIKELY(it == offset_to_type_map_.end())) { + ErrorStringPrintf("No data map entry found @ %x; expected %x", offset, type); return false; } - if (it->second != type) { - LOG(ERROR) << StringPrintf("Unexpected data map entry @ %x; expected %x, found %x", - offset, type, it->second); + if (UNLIKELY(it->second != type)) { + ErrorStringPrintf("Unexpected data map entry @ %x; expected %x, found %x", + offset, type, it->second); return false; } return true; @@ -1365,8 +1369,8 @@ bool DexFileVerifier::CheckInterStringIdItem() { const DexFile::StringId* prev_item = reinterpret_cast(previous_item_); const char* prev_str = dex_file_->GetStringData(*prev_item); const char* str = dex_file_->GetStringData(*item); - if (CompareModifiedUtf8ToModifiedUtf8AsUtf16CodePointValues(prev_str, str) >= 0) { - LOG(ERROR) << StringPrintf("Out-of-order string_ids: '%s' then '%s'", prev_str, str); + if (UNLIKELY(CompareModifiedUtf8ToModifiedUtf8AsUtf16CodePointValues(prev_str, str) >= 0)) { + ErrorStringPrintf("Out-of-order string_ids: '%s' then '%s'", prev_str, str); return false; } } @@ -1380,17 +1384,17 @@ bool DexFileVerifier::CheckInterTypeIdItem() { const char* descriptor = dex_file_->StringDataByIdx(item->descriptor_idx_); // Check that the descriptor is a valid type. - if (!IsValidDescriptor(descriptor)) { - LOG(ERROR) << StringPrintf("Invalid type descriptor: '%s'", descriptor); + if (UNLIKELY(!IsValidDescriptor(descriptor))) { + ErrorStringPrintf("Invalid type descriptor: '%s'", descriptor); return false; } // Check ordering between items. if (previous_item_ != NULL) { const DexFile::TypeId* prev_item = reinterpret_cast(previous_item_); - if (prev_item->descriptor_idx_ >= item->descriptor_idx_) { - LOG(ERROR) << StringPrintf("Out-of-order type_ids: %x then %x", - prev_item->descriptor_idx_, item->descriptor_idx_); + if (UNLIKELY(prev_item->descriptor_idx_ >= item->descriptor_idx_)) { + ErrorStringPrintf("Out-of-order type_ids: %x then %x", + prev_item->descriptor_idx_, item->descriptor_idx_); return false; } } @@ -1422,16 +1426,16 @@ bool DexFileVerifier::CheckInterProtoIdItem() { it.Next(); shorty++; } - if (it.HasNext() || *shorty != '\0') { - LOG(ERROR) << "Mismatched length for parameters and shorty"; + if (UNLIKELY(it.HasNext() || *shorty != '\0')) { + ErrorStringPrintf("Mismatched length for parameters and shorty"); return false; } // Check ordering between items. This relies on type_ids being in order. if (previous_item_ != NULL) { const DexFile::ProtoId* prev = reinterpret_cast(previous_item_); - if (prev->return_type_idx_ > item->return_type_idx_) { - LOG(ERROR) << "Out-of-order proto_id return types"; + if (UNLIKELY(prev->return_type_idx_ > item->return_type_idx_)) { + ErrorStringPrintf("Out-of-order proto_id return types"); return false; } else if (prev->return_type_idx_ == item->return_type_idx_) { DexFileParameterIterator curr_it(*dex_file_, *item); @@ -1443,15 +1447,15 @@ bool DexFileVerifier::CheckInterProtoIdItem() { if (prev_idx == DexFile::kDexNoIndex16) { break; } - if (curr_idx == DexFile::kDexNoIndex16) { - LOG(ERROR) << "Out-of-order proto_id arguments"; + if (UNLIKELY(curr_idx == DexFile::kDexNoIndex16)) { + ErrorStringPrintf("Out-of-order proto_id arguments"); return false; } if (prev_idx < curr_idx) { break; - } else if (prev_idx > curr_idx) { - LOG(ERROR) << "Out-of-order proto_id arguments"; + } else if (UNLIKELY(prev_idx > curr_idx)) { + ErrorStringPrintf("Out-of-order proto_id arguments"); return false; } @@ -1470,38 +1474,38 @@ bool DexFileVerifier::CheckInterFieldIdItem() { // Check that the class descriptor is valid. const char* descriptor = dex_file_->StringByTypeIdx(item->class_idx_); - if (!IsValidDescriptor(descriptor) || descriptor[0] != 'L') { - LOG(ERROR) << "Invalid descriptor for class_idx: '" << descriptor << '"'; + if (UNLIKELY(!IsValidDescriptor(descriptor) || descriptor[0] != 'L')) { + ErrorStringPrintf("Invalid descriptor for class_idx: '%s'", descriptor); return false; } // Check that the type descriptor is a valid field name. descriptor = dex_file_->StringByTypeIdx(item->type_idx_); - if (!IsValidDescriptor(descriptor) || descriptor[0] == 'V') { - LOG(ERROR) << "Invalid descriptor for type_idx: '" << descriptor << '"'; + if (UNLIKELY(!IsValidDescriptor(descriptor) || descriptor[0] == 'V')) { + ErrorStringPrintf("Invalid descriptor for type_idx: '%s'", descriptor); return false; } // Check that the name is valid. descriptor = dex_file_->StringDataByIdx(item->name_idx_); - if (!IsValidMemberName(descriptor)) { - LOG(ERROR) << "Invalid field name: '" << descriptor << '"'; + if (UNLIKELY(!IsValidMemberName(descriptor))) { + ErrorStringPrintf("Invalid field name: '%s'", descriptor); return false; } // Check ordering between items. This relies on the other sections being in order. if (previous_item_ != NULL) { const DexFile::FieldId* prev_item = reinterpret_cast(previous_item_); - if (prev_item->class_idx_ > item->class_idx_) { - LOG(ERROR) << "Out-of-order field_ids"; + if (UNLIKELY(prev_item->class_idx_ > item->class_idx_)) { + ErrorStringPrintf("Out-of-order field_ids"); return false; } else if (prev_item->class_idx_ == item->class_idx_) { - if (prev_item->name_idx_ > item->name_idx_) { - LOG(ERROR) << "Out-of-order field_ids"; + if (UNLIKELY(prev_item->name_idx_ > item->name_idx_)) { + ErrorStringPrintf("Out-of-order field_ids"); return false; } else if (prev_item->name_idx_ == item->name_idx_) { - if (prev_item->type_idx_ >= item->type_idx_) { - LOG(ERROR) << "Out-of-order field_ids"; + if (UNLIKELY(prev_item->type_idx_ >= item->type_idx_)) { + ErrorStringPrintf("Out-of-order field_ids"); return false; } } @@ -1517,31 +1521,31 @@ bool DexFileVerifier::CheckInterMethodIdItem() { // Check that the class descriptor is a valid reference name. const char* descriptor = dex_file_->StringByTypeIdx(item->class_idx_); - if (!IsValidDescriptor(descriptor) || (descriptor[0] != 'L' && descriptor[0] != '[')) { - LOG(ERROR) << "Invalid descriptor for class_idx: '" << descriptor << '"'; + if (UNLIKELY(!IsValidDescriptor(descriptor) || (descriptor[0] != 'L' && descriptor[0] != '['))) { + ErrorStringPrintf("Invalid descriptor for class_idx: '%s'", descriptor); return false; } // Check that the name is valid. descriptor = dex_file_->StringDataByIdx(item->name_idx_); - if (!IsValidMemberName(descriptor)) { - LOG(ERROR) << "Invalid method name: '" << descriptor << '"'; + if (UNLIKELY(!IsValidMemberName(descriptor))) { + ErrorStringPrintf("Invalid method name: '%s'", descriptor); return false; } // Check ordering between items. This relies on the other sections being in order. if (previous_item_ != NULL) { const DexFile::MethodId* prev_item = reinterpret_cast(previous_item_); - if (prev_item->class_idx_ > item->class_idx_) { - LOG(ERROR) << "Out-of-order method_ids"; + if (UNLIKELY(prev_item->class_idx_ > item->class_idx_)) { + ErrorStringPrintf("Out-of-order method_ids"); return false; } else if (prev_item->class_idx_ == item->class_idx_) { - if (prev_item->name_idx_ > item->name_idx_) { - LOG(ERROR) << "Out-of-order method_ids"; + if (UNLIKELY(prev_item->name_idx_ > item->name_idx_)) { + ErrorStringPrintf("Out-of-order method_ids"); return false; } else if (prev_item->name_idx_ == item->name_idx_) { - if (prev_item->proto_idx_ >= item->proto_idx_) { - LOG(ERROR) << "Out-of-order method_ids"; + if (UNLIKELY(prev_item->proto_idx_ >= item->proto_idx_)) { + ErrorStringPrintf("Out-of-order method_ids"); return false; } } @@ -1557,8 +1561,8 @@ bool DexFileVerifier::CheckInterClassDefItem() { uint32_t class_idx = item->class_idx_; const char* descriptor = dex_file_->StringByTypeIdx(class_idx); - if (!IsValidDescriptor(descriptor) || descriptor[0] != 'L') { - LOG(ERROR) << "Invalid class descriptor: '" << descriptor << "'"; + if (UNLIKELY(!IsValidDescriptor(descriptor) || descriptor[0] != 'L')) { + ErrorStringPrintf("Invalid class descriptor: '%s'", descriptor); return false; } @@ -1581,8 +1585,8 @@ bool DexFileVerifier::CheckInterClassDefItem() { if (item->superclass_idx_ != DexFile::kDexNoIndex16) { descriptor = dex_file_->StringByTypeIdx(item->superclass_idx_); - if (!IsValidDescriptor(descriptor) || descriptor[0] != 'L') { - LOG(ERROR) << "Invalid superclass: '" << descriptor << "'"; + if (UNLIKELY(!IsValidDescriptor(descriptor) || descriptor[0] != 'L')) { + ErrorStringPrintf("Invalid superclass: '%s'", descriptor); return false; } } @@ -1594,8 +1598,8 @@ bool DexFileVerifier::CheckInterClassDefItem() { // Ensure that all interfaces refer to classes (not arrays or primitives). for (uint32_t i = 0; i < size; i++) { descriptor = dex_file_->StringByTypeIdx(interfaces->GetTypeItem(i).type_idx_); - if (!IsValidDescriptor(descriptor) || descriptor[0] != 'L') { - LOG(ERROR) << "Invalid interface: '" << descriptor << "'"; + if (UNLIKELY(!IsValidDescriptor(descriptor) || descriptor[0] != 'L')) { + ErrorStringPrintf("Invalid interface: '%s'", descriptor); return false; } } @@ -1608,8 +1612,8 @@ bool DexFileVerifier::CheckInterClassDefItem() { uint32_t idx1 = interfaces->GetTypeItem(i).type_idx_; for (uint32_t j =0; j < i; j++) { uint32_t idx2 = interfaces->GetTypeItem(j).type_idx_; - if (idx1 == idx2) { - LOG(ERROR) << "Duplicate interface: '" << dex_file_->StringByTypeIdx(idx1) << "'"; + if (UNLIKELY(idx1 == idx2)) { + ErrorStringPrintf("Duplicate interface: '%s'", dex_file_->StringByTypeIdx(idx1)); return false; } } @@ -1620,8 +1624,8 @@ bool DexFileVerifier::CheckInterClassDefItem() { if (item->class_data_off_ != 0) { const byte* data = begin_ + item->class_data_off_; uint16_t data_definer = FindFirstClassDataDefiner(data); - if ((data_definer != item->class_idx_) && (data_definer != DexFile::kDexNoIndex16)) { - LOG(ERROR) << "Invalid class_data_item"; + if (UNLIKELY((data_definer != item->class_idx_) && (data_definer != DexFile::kDexNoIndex16))) { + ErrorStringPrintf("Invalid class_data_item"); return false; } } @@ -1630,8 +1634,9 @@ bool DexFileVerifier::CheckInterClassDefItem() { if (item->annotations_off_ != 0) { const byte* data = begin_ + item->annotations_off_; uint16_t annotations_definer = FindFirstAnnotationsDirectoryDefiner(data); - if ((annotations_definer != item->class_idx_) && (annotations_definer != DexFile::kDexNoIndex16)) { - LOG(ERROR) << "Invalid annotations_directory_item"; + if (UNLIKELY((annotations_definer != item->class_idx_) && + (annotations_definer != DexFile::kDexNoIndex16))) { + ErrorStringPrintf("Invalid annotations_directory_item"); return false; } } @@ -1675,8 +1680,8 @@ bool DexFileVerifier::CheckInterAnnotationSetItem() { const uint8_t* data = annotation->annotation_; uint32_t idx = DecodeUnsignedLeb128(&data); - if (last_idx >= idx && i != 0) { - LOG(ERROR) << StringPrintf("Out-of-order entry types: %x then %x", last_idx, idx); + if (UNLIKELY(last_idx >= idx && i != 0)) { + ErrorStringPrintf("Out-of-order entry types: %x then %x", last_idx, idx); return false; } @@ -1694,8 +1699,8 @@ bool DexFileVerifier::CheckInterClassDataItem() { for (; it.HasNextStaticField() || it.HasNextInstanceField(); it.Next()) { const DexFile::FieldId& field = dex_file_->GetFieldId(it.GetMemberIndex()); - if (field.class_idx_ != defining_class) { - LOG(ERROR) << "Mismatched defining class for class_data_item field"; + if (UNLIKELY(field.class_idx_ != defining_class)) { + ErrorStringPrintf("Mismatched defining class for class_data_item field"); return false; } } @@ -1705,8 +1710,8 @@ bool DexFileVerifier::CheckInterClassDataItem() { return false; } const DexFile::MethodId& method = dex_file_->GetMethodId(it.GetMemberIndex()); - if (method.class_idx_ != defining_class) { - LOG(ERROR) << "Mismatched defining class for class_data_item method"; + if (UNLIKELY(method.class_idx_ != defining_class)) { + ErrorStringPrintf("Mismatched defining class for class_data_item method"); return false; } } @@ -1731,8 +1736,8 @@ bool DexFileVerifier::CheckInterAnnotationsDirectoryItem() { uint32_t field_count = item->fields_size_; for (uint32_t i = 0; i < field_count; i++) { const DexFile::FieldId& field = dex_file_->GetFieldId(field_item->field_idx_); - if (field.class_idx_ != defining_class) { - LOG(ERROR) << "Mismatched defining class for field_annotation"; + if (UNLIKELY(field.class_idx_ != defining_class)) { + ErrorStringPrintf("Mismatched defining class for field_annotation"); return false; } if (!CheckOffsetToTypeMap(field_item->annotations_off_, DexFile::kDexTypeAnnotationSetItem)) { @@ -1747,8 +1752,8 @@ bool DexFileVerifier::CheckInterAnnotationsDirectoryItem() { uint32_t method_count = item->methods_size_; for (uint32_t i = 0; i < method_count; i++) { const DexFile::MethodId& method = dex_file_->GetMethodId(method_item->method_idx_); - if (method.class_idx_ != defining_class) { - LOG(ERROR) << "Mismatched defining class for method_annotation"; + if (UNLIKELY(method.class_idx_ != defining_class)) { + ErrorStringPrintf("Mismatched defining class for method_annotation"); return false; } if (!CheckOffsetToTypeMap(method_item->annotations_off_, DexFile::kDexTypeAnnotationSetItem)) { @@ -1763,8 +1768,8 @@ bool DexFileVerifier::CheckInterAnnotationsDirectoryItem() { uint32_t parameter_count = item->parameters_size_; for (uint32_t i = 0; i < parameter_count; i++) { const DexFile::MethodId& parameter_method = dex_file_->GetMethodId(parameter_item->method_idx_); - if (parameter_method.class_idx_ != defining_class) { - LOG(ERROR) << "Mismatched defining class for parameter_annotation"; + if (UNLIKELY(parameter_method.class_idx_ != defining_class)) { + ErrorStringPrintf("Mismatched defining class for parameter_annotation"); return false; } if (!CheckOffsetToTypeMap(parameter_item->annotations_off_, @@ -1860,7 +1865,7 @@ bool DexFileVerifier::CheckInterSectionIterate(uint32_t offset, uint32_t count, break; } default: - LOG(ERROR) << StringPrintf("Unknown map item type %x", type); + ErrorStringPrintf("Unknown map item type %x", type); return false; } @@ -1908,7 +1913,7 @@ bool DexFileVerifier::CheckInterSection() { break; } default: - LOG(ERROR) << StringPrintf("Unknown map item type %x", type); + ErrorStringPrintf("Unknown map item type %x", type); return false; } @@ -1942,4 +1947,13 @@ bool DexFileVerifier::Verify() { return true; } +void DexFileVerifier::ErrorStringPrintf(const char* fmt, ...) { + va_list ap; + va_start(ap, fmt); + DCHECK(failure_reason_.empty()) << failure_reason_; + failure_reason_ = StringPrintf("Failure to verify dex file '%s': ", location_); + StringAppendV(&failure_reason_, fmt, ap); + va_end(ap); +} + } // namespace art diff --git a/runtime/dex_file_verifier.h b/runtime/dex_file_verifier.h index 3797dc77e50..4b8b80ae7bd 100644 --- a/runtime/dex_file_verifier.h +++ b/runtime/dex_file_verifier.h @@ -24,29 +24,35 @@ namespace art { class DexFileVerifier { public: - static bool Verify(const DexFile* dex_file, const byte* begin, size_t size); + static bool Verify(const DexFile* dex_file, const byte* begin, size_t size, + const char* location, std::string* error_msg); + + const std::string& FailureReason() const { + return failure_reason_; + } private: - DexFileVerifier(const DexFile* dex_file, const byte* begin, size_t size) - : dex_file_(dex_file), begin_(begin), size_(size), + DexFileVerifier(const DexFile* dex_file, const byte* begin, size_t size, const char* location) + : dex_file_(dex_file), begin_(begin), size_(size), location_(location), header_(&dex_file->GetHeader()), ptr_(NULL), previous_item_(NULL) { } bool Verify(); - bool CheckPointerRange(const void* start, const void* end, const char* label) const; - bool CheckListSize(const void* start, uint32_t count, uint32_t element_size, const char* label) const; - bool CheckIndex(uint32_t field, uint32_t limit, const char* label) const; + bool CheckShortyDescriptorMatch(char shorty_char, const char* descriptor, bool is_return_type); + bool CheckPointerRange(const void* start, const void* end, const char* label); + bool CheckListSize(const void* start, uint32_t count, uint32_t element_size, const char* label); + bool CheckIndex(uint32_t field, uint32_t limit, const char* label); - bool CheckHeader() const; - bool CheckMap() const; + bool CheckHeader(); + bool CheckMap(); uint32_t ReadUnsignedLittleEndian(uint32_t size); bool CheckAndGetHandlerOffsets(const DexFile::CodeItem* code_item, - uint32_t* handler_offsets, uint32_t handlers_size); - bool CheckClassDataItemField(uint32_t idx, uint32_t access_flags, bool expect_static) const; + uint32_t* handler_offsets, uint32_t handlers_size); + bool CheckClassDataItemField(uint32_t idx, uint32_t access_flags, bool expect_static); bool CheckClassDataItemMethod(uint32_t idx, uint32_t access_flags, uint32_t code_offset, - bool expect_direct) const; + bool expect_direct); bool CheckPadding(uint32_t offset, uint32_t aligned_offset); bool CheckEncodedValue(); bool CheckEncodedArray(); @@ -82,14 +88,20 @@ class DexFileVerifier { bool CheckInterSectionIterate(uint32_t offset, uint32_t count, uint16_t type); bool CheckInterSection(); - const DexFile* dex_file_; - const byte* begin_; - size_t size_; - const DexFile::Header* header_; + void ErrorStringPrintf(const char* fmt, ...) + __attribute__((__format__(__printf__, 2, 3))) COLD_ATTR; + + const DexFile* const dex_file_; + const byte* const begin_; + const size_t size_; + const char* const location_; + const DexFile::Header* const header_; SafeMap offset_to_type_map_; const byte* ptr_; const void* previous_item_; + + std::string failure_reason_; }; } // namespace art diff --git a/runtime/dex_method_iterator_test.cc b/runtime/dex_method_iterator_test.cc index 64c645e81a9..9961df9a558 100644 --- a/runtime/dex_method_iterator_test.cc +++ b/runtime/dex_method_iterator_test.cc @@ -20,16 +20,27 @@ namespace art { -class DexMethodIteratorTest : public CommonTest {}; +class DexMethodIteratorTest : public CommonTest { + public: + const DexFile* OpenDexFile(const std::string& partial_filename) { + std::string dfn = GetDexFileName(partial_filename); + std::string error_msg; + const DexFile* dexfile = DexFile::Open(dfn.c_str(), dfn.c_str(), &error_msg); + if (dexfile == nullptr) { + LG << "Failed to open '" << dfn << "': " << error_msg; + } + return dexfile; + } +}; TEST_F(DexMethodIteratorTest, Basic) { ScopedObjectAccess soa(Thread::Current()); std::vector dex_files; - dex_files.push_back(DexFile::Open(GetDexFileName("core"), GetDexFileName("core"))); - dex_files.push_back(DexFile::Open(GetDexFileName("conscrypt"), GetDexFileName("conscrypt"))); - dex_files.push_back(DexFile::Open(GetDexFileName("okhttp"), GetDexFileName("okhttp"))); - dex_files.push_back(DexFile::Open(GetDexFileName("core-junit"), GetDexFileName("core-junit"))); - dex_files.push_back(DexFile::Open(GetDexFileName("bouncycastle"), GetDexFileName("bouncycastle"))); + dex_files.push_back(OpenDexFile("core")); + dex_files.push_back(OpenDexFile("conscrypt")); + dex_files.push_back(OpenDexFile("okhttp")); + dex_files.push_back(OpenDexFile("core-junit")); + dex_files.push_back(OpenDexFile("bouncycastle")); DexMethodIterator it(dex_files); while (it.HasNext()) { const DexFile& dex_file = it.GetDexFile(); diff --git a/runtime/elf_file.cc b/runtime/elf_file.cc index dfb6819a444..15c95f2206d 100644 --- a/runtime/elf_file.cc +++ b/runtime/elf_file.cc @@ -40,15 +40,16 @@ ElfFile::ElfFile() symtab_symbol_table_(NULL), dynsym_symbol_table_(NULL) {} -ElfFile* ElfFile::Open(File* file, bool writable, bool program_header_only) { +ElfFile* ElfFile::Open(File* file, bool writable, bool program_header_only, + std::string* error_msg) { UniquePtr elf_file(new ElfFile()); - if (!elf_file->Setup(file, writable, program_header_only)) { - return NULL; + if (!elf_file->Setup(file, writable, program_header_only, error_msg)) { + return nullptr; } return elf_file.release(); } -bool ElfFile::Setup(File* file, bool writable, bool program_header_only) { +bool ElfFile::Setup(File* file, bool writable, bool program_header_only, std::string* error_msg) { CHECK(file != NULL); file_ = file; writable_ = writable; @@ -66,40 +67,42 @@ bool ElfFile::Setup(File* file, bool writable, bool program_header_only) { int64_t file_length = file_->GetLength(); if (file_length < 0) { errno = -file_length; - PLOG(WARNING) << "Failed to get length of file: " << file_->GetPath() << " fd=" << file_->Fd(); + *error_msg = StringPrintf("Failed to get length of file: '%s' fd=%d: %s", + file_->GetPath().c_str(), file_->Fd(), strerror(errno)); return false; } if (file_length < sizeof(llvm::ELF::Elf32_Ehdr)) { - if (writable) { - LOG(WARNING) << "File size of " << file_length - << " bytes not large enough to contain ELF header of " - << sizeof(llvm::ELF::Elf32_Ehdr) << " bytes: " << file_->GetPath(); - } + *error_msg = StringPrintf("File size of %lld bytes not large enough to contain ELF header of " + "%zd bytes: '%s'", file_length, sizeof(llvm::ELF::Elf32_Ehdr), + file_->GetPath().c_str()); return false; } if (program_header_only) { // first just map ELF header to get program header size information size_t elf_header_size = sizeof(llvm::ELF::Elf32_Ehdr); - if (!SetMap(MemMap::MapFile(elf_header_size, prot, flags, file_->Fd(), 0))) { + if (!SetMap(MemMap::MapFile(elf_header_size, prot, flags, file_->Fd(), 0, + file_->GetPath().c_str(), error_msg))) { return false; } // then remap to cover program header size_t program_header_size = header_->e_phoff + (header_->e_phentsize * header_->e_phnum); if (file_length < program_header_size) { - LOG(WARNING) << "File size of " << file_length - << " bytes not large enough to contain ELF program header of " - << program_header_size << " bytes: " << file_->GetPath(); + *error_msg = StringPrintf("File size of %lld bytes not large enough to contain ELF program " + "header of %zd bytes: '%s'", file_length, + sizeof(llvm::ELF::Elf32_Ehdr), file_->GetPath().c_str()); return false; } - if (!SetMap(MemMap::MapFile(program_header_size, prot, flags, file_->Fd(), 0))) { - LOG(WARNING) << "Failed to map ELF program headers: " << file_->GetPath(); + if (!SetMap(MemMap::MapFile(program_header_size, prot, flags, file_->Fd(), 0, + file_->GetPath().c_str(), error_msg))) { + *error_msg = StringPrintf("Failed to map ELF program headers: %s", error_msg->c_str()); return false; } } else { // otherwise map entire file - if (!SetMap(MemMap::MapFile(file_->GetLength(), prot, flags, file_->Fd(), 0))) { - LOG(WARNING) << "Failed to map ELF file: " << file_->GetPath(); + if (!SetMap(MemMap::MapFile(file_->GetLength(), prot, flags, file_->Fd(), 0, + file_->GetPath().c_str(), error_msg))) { + *error_msg = StringPrintf("Failed to map ELF file: %s", error_msg->c_str()); return false; } } @@ -114,7 +117,8 @@ bool ElfFile::Setup(File* file, bool writable, bool program_header_only) { // Find .dynamic section info from program header dynamic_program_header_ = FindProgamHeaderByType(llvm::ELF::PT_DYNAMIC); if (dynamic_program_header_ == NULL) { - LOG(WARNING) << "Failed to find PT_DYNAMIC program header in ELF file: " << file_->GetPath(); + *error_msg = StringPrintf("Failed to find PT_DYNAMIC program header in ELF file: '%s'", + file_->GetPath().c_str()); return false; } @@ -596,7 +600,7 @@ size_t ElfFile::GetLoadedSize() { return loaded_size; } -bool ElfFile::Load(bool executable) { +bool ElfFile::Load(bool executable, std::string* error_msg) { // TODO: actually return false error CHECK(program_header_only_) << file_->GetPath(); for (llvm::ELF::Elf32_Word i = 0; i < GetProgramHeaderNum(); i++) { @@ -628,9 +632,10 @@ bool ElfFile::Load(bool executable) { if (program_header.p_vaddr == 0) { std::string reservation_name("ElfFile reservation for "); reservation_name += file_->GetPath(); + std::string error_msg; UniquePtr reserve(MemMap::MapAnonymous(reservation_name.c_str(), - NULL, GetLoadedSize(), PROT_NONE)); - CHECK(reserve.get() != NULL) << file_->GetPath(); + NULL, GetLoadedSize(), PROT_NONE, &error_msg)); + CHECK(reserve.get() != NULL) << file_->GetPath() << ": " << error_msg; base_address_ = reserve->Begin(); segments_.push_back(reserve.release()); } @@ -657,18 +662,20 @@ bool ElfFile::Load(bool executable) { flags |= MAP_PRIVATE; } if (file_length < (program_header.p_offset + program_header.p_memsz)) { - LOG(WARNING) << "File size of " << file_length - << " bytes not large enough to contain ELF segment " << i - << " of " << (program_header.p_offset + program_header.p_memsz) - << " bytes: " << file_->GetPath(); + *error_msg = StringPrintf("File size of %lld bytes not large enough to contain ELF segment " + "%d of %d bytes: '%s'", file_length, i, + program_header.p_offset + program_header.p_memsz, + file_->GetPath().c_str()); return false; } UniquePtr segment(MemMap::MapFileAtAddress(p_vaddr, program_header.p_memsz, prot, flags, file_->Fd(), program_header.p_offset, - true)); - CHECK(segment.get() != NULL) << file_->GetPath(); + true, + file_->GetPath().c_str(), + error_msg)); + CHECK(segment.get() != nullptr) << *error_msg; CHECK_EQ(segment->Begin(), p_vaddr) << file_->GetPath(); segments_.push_back(segment.release()); } diff --git a/runtime/elf_file.h b/runtime/elf_file.h index 33b5fc3f9bf..b0251370d22 100644 --- a/runtime/elf_file.h +++ b/runtime/elf_file.h @@ -35,7 +35,7 @@ namespace art { // ELFObjectFile. class ElfFile { public: - static ElfFile* Open(File* file, bool writable, bool program_header_only); + static ElfFile* Open(File* file, bool writable, bool program_header_only, std::string* error_msg); ~ElfFile(); // Load segments into memory based on PT_LOAD program headers @@ -115,12 +115,12 @@ class ElfFile { // Load segments into memory based on PT_LOAD program headers. // executable is true at run time, false at compile time. - bool Load(bool executable); + bool Load(bool executable, std::string* error_msg); private: ElfFile(); - bool Setup(File* file, bool writable, bool program_header_only); + bool Setup(File* file, bool writable, bool program_header_only, std::string* error_msg); bool SetMap(MemMap* map); diff --git a/runtime/gc/accounting/atomic_stack.h b/runtime/gc/accounting/atomic_stack.h index 997d7251fe2..8fa5b86f9a8 100644 --- a/runtime/gc/accounting/atomic_stack.h +++ b/runtime/gc/accounting/atomic_stack.h @@ -163,8 +163,10 @@ class AtomicStack { // Size in number of elements. void Init() { - mem_map_.reset(MemMap::MapAnonymous(name_.c_str(), NULL, capacity_ * sizeof(T), PROT_READ | PROT_WRITE)); - CHECK(mem_map_.get() != NULL) << "couldn't allocate mark stack"; + std::string error_msg; + mem_map_.reset(MemMap::MapAnonymous(name_.c_str(), NULL, capacity_ * sizeof(T), + PROT_READ | PROT_WRITE, &error_msg)); + CHECK(mem_map_.get() != NULL) << "couldn't allocate mark stack.\n" << error_msg; byte* addr = mem_map_->Begin(); CHECK(addr != NULL); debug_is_sorted_ = true; diff --git a/runtime/gc/accounting/card_table.cc b/runtime/gc/accounting/card_table.cc index 85034a0f27c..7818bc8ba61 100644 --- a/runtime/gc/accounting/card_table.cc +++ b/runtime/gc/accounting/card_table.cc @@ -54,9 +54,11 @@ CardTable* CardTable::Create(const byte* heap_begin, size_t heap_capacity) { /* Set up the card table */ size_t capacity = heap_capacity / kCardSize; /* Allocate an extra 256 bytes to allow fixed low-byte of base */ + std::string error_msg; UniquePtr mem_map(MemMap::MapAnonymous("card table", NULL, - capacity + 256, PROT_READ | PROT_WRITE)); - CHECK(mem_map.get() != NULL) << "couldn't allocate card table"; + capacity + 256, PROT_READ | PROT_WRITE, + &error_msg)); + CHECK(mem_map.get() != NULL) << "couldn't allocate card table: " << error_msg; // All zeros is the correct initial value; all clean. Anonymous mmaps are initialized to zero, we // don't clear the card table to avoid unnecessary pages being allocated COMPILE_ASSERT(kCardClean == 0, card_clean_must_be_0); diff --git a/runtime/gc/accounting/gc_allocator.cc b/runtime/gc/accounting/gc_allocator.cc index 11d0e679b15..49d84faf3c8 100644 --- a/runtime/gc/accounting/gc_allocator.cc +++ b/runtime/gc/accounting/gc_allocator.cc @@ -22,15 +22,17 @@ namespace art { namespace gc { namespace accounting { - void* RegisterGCAllocation(size_t bytes) { - Runtime::Current()->GetHeap()->RegisterGCAllocation(bytes); - return malloc(bytes); - } - void RegisterGCDeAllocation(void* p, size_t bytes) { - Runtime::Current()->GetHeap()->RegisterGCDeAllocation(bytes); - free(p); - } +void* RegisterGcAllocation(size_t bytes) { + Runtime::Current()->GetHeap()->RegisterGCAllocation(bytes); + return malloc(bytes); +} + +void RegisterGcDeallocation(void* p, size_t bytes) { + Runtime::Current()->GetHeap()->RegisterGCDeAllocation(bytes); + free(p); +} + } // namespace accounting } // namespace gc } // namespace art diff --git a/runtime/gc/accounting/gc_allocator.h b/runtime/gc/accounting/gc_allocator.h index 1fba8588821..4fe936735fe 100644 --- a/runtime/gc/accounting/gc_allocator.h +++ b/runtime/gc/accounting/gc_allocator.h @@ -26,55 +26,56 @@ namespace art { namespace gc { namespace accounting { - void* RegisterGCAllocation(size_t bytes); - void RegisterGCDeAllocation(void* p, size_t bytes); - static const bool kMeasureGCMemoryOverhead = false; +void* RegisterGcAllocation(size_t bytes); +void RegisterGcDeallocation(void* p, size_t bytes); - template - class GCAllocatorImpl : public std::allocator { - public: - typedef typename std::allocator::value_type value_type; - typedef typename std::allocator::size_type size_type; - typedef typename std::allocator::difference_type difference_type; - typedef typename std::allocator::pointer pointer; - typedef typename std::allocator::const_pointer const_pointer; - typedef typename std::allocator::reference reference; - typedef typename std::allocator::const_reference const_reference; +static const bool kMeasureGcMemoryOverhead = false; - // Used internally by STL data structures. - template - GCAllocatorImpl(const GCAllocatorImpl& alloc) throw() { - } +template +class GcAllocatorImpl : public std::allocator { + public: + typedef typename std::allocator::value_type value_type; + typedef typename std::allocator::size_type size_type; + typedef typename std::allocator::difference_type difference_type; + typedef typename std::allocator::pointer pointer; + typedef typename std::allocator::const_pointer const_pointer; + typedef typename std::allocator::reference reference; + typedef typename std::allocator::const_reference const_reference; - // Used internally by STL data structures. - GCAllocatorImpl() throw() { - } + // Used internally by STL data structures. + template + GcAllocatorImpl(const GcAllocatorImpl& alloc) throw() { + } - // Enables an allocator for objects of one type to allocate storage for objects of another type. - // Used internally by STL data structures. - template - struct rebind { - typedef GCAllocatorImpl other; - }; + // Used internally by STL data structures. + GcAllocatorImpl() throw() { + } - pointer allocate(size_type n, const_pointer hint = 0) { - return reinterpret_cast(RegisterGCAllocation(n * sizeof(T))); - } - - template - void deallocate(PT p, size_type n) { - RegisterGCDeAllocation(p, n * sizeof(T)); - } + // Enables an allocator for objects of one type to allocate storage for objects of another type. + // Used internally by STL data structures. + template + struct rebind { + typedef GcAllocatorImpl other; }; - // C++ doesn't allow template typedefs. This is a workaround template typedef which is - // GCAllocatorImpl if kMeasureGCMemoryOverhead is true, std::allocator otherwise. - template - class GCAllocator : public TypeStaticIf, - std::allocator >::value { - }; + pointer allocate(size_type n, const_pointer hint = 0) { + return reinterpret_cast(RegisterGcAllocation(n * sizeof(T))); + } + + template + void deallocate(PT p, size_type n) { + RegisterGcDeallocation(p, n * sizeof(T)); + } +}; + +// C++ doesn't allow template typedefs. This is a workaround template typedef which is +// GCAllocatorImpl if kMeasureGCMemoryOverhead is true, std::allocator otherwise. +template +class GcAllocator : public TypeStaticIf, + std::allocator >::value { +}; + } // namespace accounting } // namespace gc } // namespace art diff --git a/runtime/gc/accounting/heap_bitmap.h b/runtime/gc/accounting/heap_bitmap.h index 2ca8c4a82eb..24ebbaa1b54 100644 --- a/runtime/gc/accounting/heap_bitmap.h +++ b/runtime/gc/accounting/heap_bitmap.h @@ -31,8 +31,8 @@ namespace accounting { class HeapBitmap { public: - typedef std::vector > SpaceBitmapVector; - typedef std::vector > SpaceSetMapVector; + typedef std::vector > SpaceBitmapVector; + typedef std::vector > SpaceSetMapVector; bool Test(const mirror::Object* obj) SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_) { SpaceBitmap* bitmap = GetContinuousSpaceBitmap(obj); diff --git a/runtime/gc/accounting/mod_union_table.h b/runtime/gc/accounting/mod_union_table.h index d874c6080c1..5a99f1bb41f 100644 --- a/runtime/gc/accounting/mod_union_table.h +++ b/runtime/gc/accounting/mod_union_table.h @@ -51,7 +51,7 @@ class HeapBitmap; // cleared between GC phases, reducing the number of dirty cards that need to be scanned. class ModUnionTable { public: - typedef std::set, GCAllocator > CardSet; + typedef std::set, GcAllocator > CardSet; explicit ModUnionTable(const std::string& name, Heap* heap, space::ContinuousSpace* space) : name_(name), @@ -125,7 +125,7 @@ class ModUnionTableReferenceCache : public ModUnionTable { // Maps from dirty cards to their corresponding alloc space references. SafeMap, std::less, - GCAllocator > > > references_; + GcAllocator > > > references_; }; // Card caching implementation. Keeps track of which cards we cleared and only this information. diff --git a/runtime/gc/accounting/space_bitmap.cc b/runtime/gc/accounting/space_bitmap.cc index 63b24ffb4e2..52c02f77282 100644 --- a/runtime/gc/accounting/space_bitmap.cc +++ b/runtime/gc/accounting/space_bitmap.cc @@ -62,9 +62,11 @@ SpaceBitmap* SpaceBitmap::Create(const std::string& name, byte* heap_begin, size CHECK(heap_begin != NULL); // Round up since heap_capacity is not necessarily a multiple of kAlignment * kBitsPerWord. size_t bitmap_size = OffsetToIndex(RoundUp(heap_capacity, kAlignment * kBitsPerWord)) * kWordSize; - UniquePtr mem_map(MemMap::MapAnonymous(name.c_str(), NULL, bitmap_size, PROT_READ | PROT_WRITE)); - if (mem_map.get() == NULL) { - LOG(ERROR) << "Failed to allocate bitmap " << name; + std::string error_msg; + UniquePtr mem_map(MemMap::MapAnonymous(name.c_str(), NULL, bitmap_size, + PROT_READ | PROT_WRITE, &error_msg)); + if (UNLIKELY(mem_map.get() == nullptr)) { + LOG(ERROR) << "Failed to allocate bitmap " << name << ": " << error_msg; return NULL; } return CreateFromMemMap(name, mem_map.release(), heap_begin, heap_capacity); diff --git a/runtime/gc/accounting/space_bitmap.h b/runtime/gc/accounting/space_bitmap.h index 4cf88724b92..21709ad3b73 100644 --- a/runtime/gc/accounting/space_bitmap.h +++ b/runtime/gc/accounting/space_bitmap.h @@ -212,7 +212,7 @@ class SpaceSetMap { public: typedef std::set< const mirror::Object*, std::less, - GCAllocator > Objects; + GcAllocator > Objects; bool IsEmpty() const { return contained_.empty(); diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc index d26e28cb472..804c66932e3 100644 --- a/runtime/gc/heap.cc +++ b/runtime/gc/heap.cc @@ -66,7 +66,7 @@ static constexpr bool kDumpGcPerformanceOnShutdown = false; static constexpr size_t kMinConcurrentRemainingBytes = 128 * KB; Heap::Heap(size_t initial_size, size_t growth_limit, size_t min_free, size_t max_free, - double target_utilization, size_t capacity, const std::string& original_image_file_name, + double target_utilization, size_t capacity, const std::string& image_file_name, bool concurrent_gc, size_t parallel_gc_threads, size_t conc_gc_threads, bool low_memory_mode, size_t long_pause_log_threshold, size_t long_gc_log_threshold, bool ignore_max_footprint) @@ -144,9 +144,8 @@ Heap::Heap(size_t initial_size, size_t growth_limit, size_t min_free, size_t max // Requested begin for the alloc space, to follow the mapped image and oat files byte* requested_alloc_space_begin = NULL; - std::string image_file_name(original_image_file_name); if (!image_file_name.empty()) { - space::ImageSpace* image_space = space::ImageSpace::Create(image_file_name); + space::ImageSpace* image_space = space::ImageSpace::Create(image_file_name.c_str()); CHECK(image_space != NULL) << "Failed to create space for " << image_file_name; AddContinuousSpace(image_space); // Oat files referenced by image files immediately follow them in memory, ensure alloc space diff --git a/runtime/gc/space/dlmalloc_space.cc b/runtime/gc/space/dlmalloc_space.cc index 468d1d23311..8c13d79be6e 100644 --- a/runtime/gc/space/dlmalloc_space.cc +++ b/runtime/gc/space/dlmalloc_space.cc @@ -183,11 +183,12 @@ DlMallocSpace* DlMallocSpace::Create(const std::string& name, size_t initial_siz growth_limit = RoundUp(growth_limit, kPageSize); capacity = RoundUp(capacity, kPageSize); + std::string error_msg; UniquePtr mem_map(MemMap::MapAnonymous(name.c_str(), requested_begin, capacity, - PROT_READ | PROT_WRITE)); + PROT_READ | PROT_WRITE, &error_msg)); if (mem_map.get() == NULL) { LOG(ERROR) << "Failed to allocate pages for alloc space (" << name << ") of size " - << PrettySize(capacity); + << PrettySize(capacity) << ": " << error_msg; return NULL; } @@ -307,7 +308,10 @@ DlMallocSpace* DlMallocSpace::CreateZygoteSpace(const char* alloc_space_name) { VLOG(heap) << "Size " << GetMemMap()->Size(); VLOG(heap) << "GrowthLimit " << PrettySize(growth_limit); VLOG(heap) << "Capacity " << PrettySize(capacity); - UniquePtr mem_map(MemMap::MapAnonymous(alloc_space_name, End(), capacity, PROT_READ | PROT_WRITE)); + std::string error_msg; + UniquePtr mem_map(MemMap::MapAnonymous(alloc_space_name, End(), capacity, + PROT_READ | PROT_WRITE, &error_msg)); + CHECK(mem_map.get() != nullptr) << error_msg; void* mspace = CreateMallocSpace(end_, starting_size, initial_size); // Protect memory beyond the initial size. byte* end = mem_map->Begin() + starting_size; diff --git a/runtime/gc/space/image_space.cc b/runtime/gc/space/image_space.cc index 1cd33ee8403..fa28642c3e5 100644 --- a/runtime/gc/space/image_space.cc +++ b/runtime/gc/space/image_space.cc @@ -44,12 +44,13 @@ ImageSpace::ImageSpace(const std::string& name, MemMap* mem_map, live_bitmap_.reset(live_bitmap); } -static bool GenerateImage(const std::string& image_file_name) { +static bool GenerateImage(const std::string& image_file_name, std::string* error_msg) { const std::string boot_class_path_string(Runtime::Current()->GetBootClassPathString()); std::vector boot_class_path; Split(boot_class_path_string, ':', boot_class_path); if (boot_class_path.empty()) { - LOG(FATAL) << "Failed to generate image because no boot class path specified"; + *error_msg = "Failed to generate image because no boot class path specified"; + return false; } std::vector arg_vector; @@ -112,41 +113,57 @@ static bool GenerateImage(const std::string& image_file_name) { return false; } else { if (pid == -1) { - PLOG(ERROR) << "fork failed"; + *error_msg = StringPrintf("Failed to generate image '%s' because fork failed: %s", + image_file_name.c_str(), strerror(errno)); + return false; } // wait for dex2oat to finish int status; pid_t got_pid = TEMP_FAILURE_RETRY(waitpid(pid, &status, 0)); if (got_pid != pid) { - PLOG(ERROR) << "waitpid failed: wanted " << pid << ", got " << got_pid; + *error_msg = StringPrintf("Failed to generate image '%s' because waitpid failed: " + "wanted %d, got %d: %s", + image_file_name.c_str(), pid, got_pid, strerror(errno)); return false; } if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) { - LOG(ERROR) << dex2oat << " failed: " << command_line; + *error_msg = StringPrintf("Failed to generate image '%s' because dex2oat failed: %s", + image_file_name.c_str(), command_line.c_str()); return false; } } return true; } -ImageSpace* ImageSpace::Create(const std::string& original_image_file_name) { - if (OS::FileExists(original_image_file_name.c_str())) { +ImageSpace* ImageSpace::Create(const char* original_image_file_name) { + if (OS::FileExists(original_image_file_name)) { // If the /system file exists, it should be up-to-date, don't try to generate - return space::ImageSpace::Init(original_image_file_name, false); + std::string error_msg; + ImageSpace* space = ImageSpace::Init(original_image_file_name, false, &error_msg); + if (space == nullptr) { + LOG(FATAL) << "Failed to load image '" << original_image_file_name << "': " << error_msg; + } + return space; } // If the /system file didn't exist, we need to use one from the dalvik-cache. // If the cache file exists, try to open, but if it fails, regenerate. // If it does not exist, generate. std::string image_file_name(GetDalvikCacheFilenameOrDie(original_image_file_name)); + std::string error_msg; if (OS::FileExists(image_file_name.c_str())) { - space::ImageSpace* image_space = space::ImageSpace::Init(image_file_name, true); - if (image_space != NULL) { + space::ImageSpace* image_space = ImageSpace::Init(image_file_name.c_str(), true, &error_msg); + if (image_space != nullptr) { return image_space; } } - CHECK(GenerateImage(image_file_name)) << "Failed to generate image: " << image_file_name; - return space::ImageSpace::Init(image_file_name, true); + CHECK(GenerateImage(image_file_name, &error_msg)) + << "Failed to generate image '" << image_file_name << "': " << error_msg; + ImageSpace* space = ImageSpace::Init(image_file_name.c_str(), true, &error_msg); + if (space == nullptr) { + LOG(FATAL) << "Failed to load image '" << original_image_file_name << "': " << error_msg; + } + return space; } void ImageSpace::VerifyImageAllocations() { @@ -160,8 +177,9 @@ void ImageSpace::VerifyImageAllocations() { } } -ImageSpace* ImageSpace::Init(const std::string& image_file_name, bool validate_oat_file) { - CHECK(!image_file_name.empty()); +ImageSpace* ImageSpace::Init(const char* image_file_name, bool validate_oat_file, + std::string* error_msg) { + CHECK(image_file_name != nullptr); uint64_t start_time = 0; if (VLOG_IS_ON(heap) || VLOG_IS_ON(startup)) { @@ -169,16 +187,16 @@ ImageSpace* ImageSpace::Init(const std::string& image_file_name, bool validate_o LOG(INFO) << "ImageSpace::Init entering image_file_name=" << image_file_name; } - UniquePtr file(OS::OpenFileForReading(image_file_name.c_str())); + UniquePtr file(OS::OpenFileForReading(image_file_name)); if (file.get() == NULL) { - LOG(ERROR) << "Failed to open " << image_file_name; - return NULL; + *error_msg = StringPrintf("Failed to open '%s'", image_file_name); + return nullptr; } ImageHeader image_header; bool success = file->ReadFully(&image_header, sizeof(image_header)); if (!success || !image_header.IsValid()) { - LOG(ERROR) << "Invalid image header " << image_file_name; - return NULL; + *error_msg = StringPrintf("Invalid image header in '%s'", image_file_name); + return nullptr; } // Note: The image header is part of the image due to mmap page alignment required of offset. @@ -188,10 +206,12 @@ ImageSpace* ImageSpace::Init(const std::string& image_file_name, bool validate_o MAP_PRIVATE | MAP_FIXED, file->Fd(), 0, - false)); + false, + image_file_name, + error_msg)); if (map.get() == NULL) { - LOG(ERROR) << "Failed to map " << image_file_name; - return NULL; + DCHECK(!error_msg->empty()); + return nullptr; } CHECK_EQ(image_header.GetImageBegin(), map->Begin()); DCHECK_EQ(0, memcmp(&image_header, map->Begin(), sizeof(ImageHeader))); @@ -199,16 +219,24 @@ ImageSpace* ImageSpace::Init(const std::string& image_file_name, bool validate_o UniquePtr image_map(MemMap::MapFileAtAddress(nullptr, image_header.GetImageBitmapSize(), PROT_READ, MAP_PRIVATE, file->Fd(), image_header.GetBitmapOffset(), - false)); - CHECK(image_map.get() != nullptr) << "failed to map image bitmap"; + false, + image_file_name, + error_msg)); + if (image_map.get() == nullptr) { + *error_msg = StringPrintf("Failed to map image bitmap: %s", error_msg->c_str()); + return nullptr; + } size_t bitmap_index = bitmap_index_.fetch_add(1); - std::string bitmap_name(StringPrintf("imagespace %s live-bitmap %u", image_file_name.c_str(), + std::string bitmap_name(StringPrintf("imagespace %s live-bitmap %u", image_file_name, bitmap_index)); UniquePtr bitmap( accounting::SpaceBitmap::CreateFromMemMap(bitmap_name, image_map.release(), reinterpret_cast(map->Begin()), map->Size())); - CHECK(bitmap.get() != nullptr) << "could not create " << bitmap_name; + if (bitmap.get() == nullptr) { + *error_msg = StringPrintf("Could not create bitmap '%s'", bitmap_name.c_str()); + return nullptr; + } Runtime* runtime = Runtime::Current(); mirror::Object* resolution_method = image_header.GetImageRoot(ImageHeader::kResolutionMethod); @@ -226,15 +254,15 @@ ImageSpace* ImageSpace::Init(const std::string& image_file_name, bool validate_o space->VerifyImageAllocations(); } - space->oat_file_.reset(space->OpenOatFile()); - if (space->oat_file_.get() == NULL) { - LOG(ERROR) << "Failed to open oat file for image: " << image_file_name; - return NULL; + space->oat_file_.reset(space->OpenOatFile(error_msg)); + if (space->oat_file_.get() == nullptr) { + DCHECK(!error_msg->empty()); + return nullptr; } - if (validate_oat_file && !space->ValidateOatFile()) { - LOG(WARNING) << "Failed to validate oat file for image: " << image_file_name; - return NULL; + if (validate_oat_file && !space->ValidateOatFile(error_msg)) { + DCHECK(!error_msg->empty()); + return nullptr; } if (VLOG_IS_ON(heap) || VLOG_IS_ON(startup)) { @@ -244,7 +272,7 @@ ImageSpace* ImageSpace::Init(const std::string& image_file_name, bool validate_o return space.release(); } -OatFile* ImageSpace::OpenOatFile() const { +OatFile* ImageSpace::OpenOatFile(std::string* error_msg) const { const Runtime* runtime = Runtime::Current(); const ImageHeader& image_header = GetImageHeader(); // Grab location but don't use Object::AsString as we haven't yet initialized the roots to @@ -255,45 +283,47 @@ OatFile* ImageSpace::OpenOatFile() const { oat_filename += runtime->GetHostPrefix(); oat_filename += oat_location->ToModifiedUtf8(); OatFile* oat_file = OatFile::Open(oat_filename, oat_filename, image_header.GetOatDataBegin(), - !Runtime::Current()->IsCompiler()); + !Runtime::Current()->IsCompiler(), error_msg); if (oat_file == NULL) { - LOG(ERROR) << "Failed to open oat file " << oat_filename << " referenced from image."; - return NULL; + *error_msg = StringPrintf("Failed to open oat file '%s' referenced from image %s: %s", + oat_filename.c_str(), GetName(), error_msg->c_str()); + return nullptr; } uint32_t oat_checksum = oat_file->GetOatHeader().GetChecksum(); uint32_t image_oat_checksum = image_header.GetOatChecksum(); if (oat_checksum != image_oat_checksum) { - LOG(ERROR) << "Failed to match oat file checksum " << std::hex << oat_checksum - << " to expected oat checksum " << std::hex << image_oat_checksum - << " in image"; - return NULL; + *error_msg = StringPrintf("Failed to match oat file checksum 0x%x to expected oat checksum 0x%x" + " in image %s", oat_checksum, image_oat_checksum, GetName()); + return nullptr; } return oat_file; } -bool ImageSpace::ValidateOatFile() const { +bool ImageSpace::ValidateOatFile(std::string* error_msg) const { CHECK(oat_file_.get() != NULL); for (const OatFile::OatDexFile* oat_dex_file : oat_file_->GetOatDexFiles()) { const std::string& dex_file_location = oat_dex_file->GetDexFileLocation(); uint32_t dex_file_location_checksum; - if (!DexFile::GetChecksum(dex_file_location.c_str(), &dex_file_location_checksum)) { - LOG(WARNING) << "ValidateOatFile could not find checksum for " << dex_file_location; + if (!DexFile::GetChecksum(dex_file_location.c_str(), &dex_file_location_checksum, error_msg)) { + *error_msg = StringPrintf("Failed to get checksum of dex file '%s' referenced by image %s: " + "%s", dex_file_location.c_str(), GetName(), error_msg->c_str()); return false; } if (dex_file_location_checksum != oat_dex_file->GetDexFileLocationChecksum()) { - LOG(WARNING) << "ValidateOatFile found checksum mismatch between oat file " - << oat_file_->GetLocation() << " and dex file " << dex_file_location - << " (" << oat_dex_file->GetDexFileLocationChecksum() << " != " - << dex_file_location_checksum << ")"; + *error_msg = StringPrintf("ValidateOatFile found checksum mismatch between oat file '%s' and " + "dex file '%s' (0x%x != 0x%x)", + oat_file_->GetLocation().c_str(), dex_file_location.c_str(), + oat_dex_file->GetDexFileLocationChecksum(), + dex_file_location_checksum); return false; } } return true; } -OatFile& ImageSpace::ReleaseOatFile() { +OatFile* ImageSpace::ReleaseOatFile() { CHECK(oat_file_.get() != NULL); - return *oat_file_.release(); + return oat_file_.release(); } void ImageSpace::Dump(std::ostream& os) const { diff --git a/runtime/gc/space/image_space.h b/runtime/gc/space/image_space.h index 381a98e3860..78a83c92cd1 100644 --- a/runtime/gc/space/image_space.h +++ b/runtime/gc/space/image_space.h @@ -45,12 +45,11 @@ class ImageSpace : public MemMapSpace { // creation of the alloc space. The ReleaseOatFile will later be // used to transfer ownership of the OatFile to the ClassLinker when // it is initialized. - static ImageSpace* Create(const std::string& image) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + static ImageSpace* Create(const char* image) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); // Releases the OatFile from the ImageSpace so it can be transfer to // the caller, presumably the ClassLinker. - OatFile& ReleaseOatFile() + OatFile* ReleaseOatFile() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); void VerifyImageAllocations() @@ -84,13 +83,13 @@ class ImageSpace : public MemMapSpace { // image's OatFile is up-to-date relative to its DexFile // inputs. Otherwise (for /data), validate the inputs and generate // the OatFile in /data/dalvik-cache if necessary. - static ImageSpace* Init(const std::string& image, bool validate_oat_file) + static ImageSpace* Init(const char* image, bool validate_oat_file, std::string* error_msg) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - OatFile* OpenOatFile() const + OatFile* OpenOatFile(std::string* error_msg) const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - bool ValidateOatFile() const + bool ValidateOatFile(std::string* error_msg) const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); friend class Space; diff --git a/runtime/gc/space/large_object_space.cc b/runtime/gc/space/large_object_space.cc index c6d028eed50..1321b193aa0 100644 --- a/runtime/gc/space/large_object_space.cc +++ b/runtime/gc/space/large_object_space.cc @@ -56,10 +56,13 @@ LargeObjectMapSpace* LargeObjectMapSpace::Create(const std::string& name) { return new LargeObjectMapSpace(name); } -mirror::Object* LargeObjectMapSpace::Alloc(Thread* self, size_t num_bytes, size_t* bytes_allocated) { +mirror::Object* LargeObjectMapSpace::Alloc(Thread* self, size_t num_bytes, + size_t* bytes_allocated) { + std::string error_msg; MemMap* mem_map = MemMap::MapAnonymous("large object space allocation", NULL, num_bytes, - PROT_READ | PROT_WRITE); - if (mem_map == NULL) { + PROT_READ | PROT_WRITE, &error_msg); + if (UNLIKELY(mem_map == NULL)) { + LOG(WARNING) << "Large object allocation failed: " << error_msg; return NULL; } MutexLock mu(self, lock_); @@ -129,9 +132,10 @@ bool LargeObjectMapSpace::Contains(const mirror::Object* obj) const { FreeListSpace* FreeListSpace::Create(const std::string& name, byte* requested_begin, size_t size) { CHECK_EQ(size % kAlignment, 0U); + std::string error_msg; MemMap* mem_map = MemMap::MapAnonymous(name.c_str(), requested_begin, size, - PROT_READ | PROT_WRITE); - CHECK(mem_map != NULL) << "Failed to allocate large object space mem map"; + PROT_READ | PROT_WRITE, &error_msg); + CHECK(mem_map != NULL) << "Failed to allocate large object space mem map: " << error_msg; return new FreeListSpace(name, mem_map, mem_map->Begin(), mem_map->End()); } diff --git a/runtime/gc/space/large_object_space.h b/runtime/gc/space/large_object_space.h index 3f2e848c703..ef889d42c2b 100644 --- a/runtime/gc/space/large_object_space.h +++ b/runtime/gc/space/large_object_space.h @@ -96,9 +96,9 @@ class LargeObjectMapSpace : public LargeObjectSpace { // Used to ensure mutual exclusion when the allocation spaces data structures are being modified. mutable Mutex lock_ DEFAULT_MUTEX_ACQUIRED_AFTER; std::vector > large_objects_ GUARDED_BY(lock_); + accounting::GcAllocator > large_objects_ GUARDED_BY(lock_); typedef SafeMap, - accounting::GCAllocator > > MemMaps; + accounting::GcAllocator > > MemMaps; MemMaps mem_maps_ GUARDED_BY(lock_); }; @@ -217,7 +217,7 @@ class FreeListSpace : public LargeObjectSpace { AllocationHeader* GetAllocationHeader(const mirror::Object* obj); typedef std::set > FreeBlocks; + accounting::GcAllocator > FreeBlocks; byte* const begin_; byte* const end_; diff --git a/runtime/mem_map.cc b/runtime/mem_map.cc index 6451d5c51ae..00316f7a211 100644 --- a/runtime/mem_map.cc +++ b/runtime/mem_map.cc @@ -67,7 +67,8 @@ static void CheckMapRequest(byte* addr, size_t byte_count) { static void CheckMapRequest(byte*, size_t) { } #endif -MemMap* MemMap::MapAnonymous(const char* name, byte* addr, size_t byte_count, int prot) { +MemMap* MemMap::MapAnonymous(const char* name, byte* addr, size_t byte_count, int prot, + std::string* error_msg) { if (byte_count == 0) { return new MemMap(name, NULL, 0, NULL, 0, prot); } @@ -82,8 +83,8 @@ MemMap* MemMap::MapAnonymous(const char* name, byte* addr, size_t byte_count, in ScopedFd fd(ashmem_create_region(debug_friendly_name.c_str(), page_aligned_byte_count)); int flags = MAP_PRIVATE; if (fd.get() == -1) { - PLOG(ERROR) << "ashmem_create_region failed (" << name << ")"; - return NULL; + *error_msg = StringPrintf("ashmem_create_region failed for '%s': %s", name, strerror(errno)); + return nullptr; } #else ScopedFd fd(-1); @@ -94,16 +95,17 @@ MemMap* MemMap::MapAnonymous(const char* name, byte* addr, size_t byte_count, in if (actual == MAP_FAILED) { std::string maps; ReadFileToString("/proc/self/maps", &maps); - PLOG(ERROR) << "mmap(" << reinterpret_cast(addr) << ", " << page_aligned_byte_count - << ", " << prot << ", " << flags << ", " << fd.get() << ", 0) failed for " << name - << "\n" << maps; - return NULL; + *error_msg = StringPrintf("anonymous mmap(%p, %zd, %x, %x, %d, 0) failed\n%s", + addr, page_aligned_byte_count, prot, flags, fd.get(), + maps.c_str()); + return nullptr; } return new MemMap(name, actual, byte_count, actual, page_aligned_byte_count, prot); } -MemMap* MemMap::MapFileAtAddress(byte* addr, size_t byte_count, - int prot, int flags, int fd, off_t start, bool reuse) { +MemMap* MemMap::MapFileAtAddress(byte* addr, size_t byte_count, int prot, int flags, int fd, + off_t start, bool reuse, const char* filename, + std::string* error_msg) { CHECK_NE(0, prot); CHECK_NE(0, flags & (MAP_SHARED | MAP_PRIVATE)); if (byte_count == 0) { @@ -133,10 +135,10 @@ MemMap* MemMap::MapFileAtAddress(byte* addr, size_t byte_count, if (actual == MAP_FAILED) { std::string maps; ReadFileToString("/proc/self/maps", &maps); - PLOG(ERROR) << "mmap(" << reinterpret_cast(page_aligned_addr) - << ", " << page_aligned_byte_count - << ", " << prot << ", " << flags << ", " << fd << ", " << page_aligned_offset - << ") failed\n" << maps; + *error_msg = StringPrintf("mmap(%p, %zd, %x, %x, %d, %lld) of file '%s' failed\n%s", + page_aligned_addr, page_aligned_byte_count, prot, flags, fd, + static_cast(page_aligned_offset), + filename, maps.c_str()); return NULL; } return new MemMap("file", actual + page_offset, byte_count, actual, page_aligned_byte_count, diff --git a/runtime/mem_map.h b/runtime/mem_map.h index e294824d913..919463c4c62 100644 --- a/runtime/mem_map.h +++ b/runtime/mem_map.h @@ -38,14 +38,16 @@ class MemMap { // a name. // // On success, returns returns a MemMap instance. On failure, returns a NULL; - static MemMap* MapAnonymous(const char* ashmem_name, byte* addr, size_t byte_count, int prot); + static MemMap* MapAnonymous(const char* ashmem_name, byte* addr, size_t byte_count, int prot, + std::string* error_msg); // Map part of a file, taking care of non-page aligned offsets. The // "start" offset is absolute, not relative. // // On success, returns returns a MemMap instance. On failure, returns a NULL; - static MemMap* MapFile(size_t byte_count, int prot, int flags, int fd, off_t start) { - return MapFileAtAddress(NULL, byte_count, prot, flags, fd, start, false); + static MemMap* MapFile(size_t byte_count, int prot, int flags, int fd, off_t start, + const char* filename, std::string* error_msg) { + return MapFileAtAddress(NULL, byte_count, prot, flags, fd, start, false, filename, error_msg); } // Map part of a file, taking care of non-page aligned offsets. The @@ -53,8 +55,9 @@ class MemMap { // requesting a specific address for the base of the mapping. // // On success, returns returns a MemMap instance. On failure, returns a NULL; - static MemMap* MapFileAtAddress( - byte* addr, size_t byte_count, int prot, int flags, int fd, off_t start, bool reuse); + static MemMap* MapFileAtAddress(byte* addr, size_t byte_count, int prot, int flags, int fd, + off_t start, bool reuse, const char* filename, + std::string* error_msg); // Releases the memory mapping ~MemMap(); diff --git a/runtime/mem_map_test.cc b/runtime/mem_map_test.cc index dade01b23fc..09de320a5e2 100644 --- a/runtime/mem_map_test.cc +++ b/runtime/mem_map_test.cc @@ -24,11 +24,14 @@ namespace art { class MemMapTest : public testing::Test {}; TEST_F(MemMapTest, MapAnonymousEmpty) { + std::string error_msg; UniquePtr map(MemMap::MapAnonymous("MapAnonymousEmpty", NULL, 0, - PROT_READ)); - ASSERT_TRUE(map.get() != NULL); + PROT_READ, + &error_msg)); + ASSERT_TRUE(map.get() != NULL) << error_msg; + ASSERT_TRUE(error_msg.empty()); } } // namespace art diff --git a/runtime/native/dalvik_system_DexFile.cc b/runtime/native/dalvik_system_DexFile.cc index 4e17b795a53..af09a1c57dd 100644 --- a/runtime/native/dalvik_system_DexFile.cc +++ b/runtime/native/dalvik_system_DexFile.cc @@ -89,34 +89,32 @@ static jint DexFile_openDexFileNative(JNIEnv* env, jclass, jstring javaSourceNam if (sourceName.c_str() == NULL) { return 0; } - std::string dex_location(sourceName.c_str()); NullableScopedUtfChars outputName(env, javaOutputName); if (env->ExceptionCheck()) { return 0; } - ScopedObjectAccess soa(env); + uint32_t dex_location_checksum; - if (!DexFile::GetChecksum(dex_location, &dex_location_checksum)) { - LOG(WARNING) << "Failed to compute checksum: " << dex_location; - ThrowLocation throw_location = soa.Self()->GetCurrentLocationForThrow(); - soa.Self()->ThrowNewExceptionF(throw_location, "Ljava/io/IOException;", - "Unable to get checksum of dex file: %s", dex_location.c_str()); + std::string error_msg; + if (!DexFile::GetChecksum(sourceName.c_str(), &dex_location_checksum, &error_msg)) { + ScopedObjectAccess soa(env); + DCHECK(!error_msg.empty()); + ThrowIOException("%s", error_msg.c_str()); return 0; } ClassLinker* linker = Runtime::Current()->GetClassLinker(); const DexFile* dex_file; - if (outputName.c_str() == NULL) { - dex_file = linker->FindDexFileInOatFileFromDexLocation(dex_location, dex_location_checksum); + if (outputName.c_str() == nullptr) { + dex_file = linker->FindDexFileInOatFileFromDexLocation(sourceName.c_str(), + dex_location_checksum, &error_msg); } else { - std::string oat_location(outputName.c_str()); - dex_file = linker->FindOrCreateOatFileForDexLocation(dex_location, dex_location_checksum, oat_location); + dex_file = linker->FindOrCreateOatFileForDexLocation(sourceName.c_str(), dex_location_checksum, + outputName.c_str(), &error_msg); } - if (dex_file == NULL) { - LOG(WARNING) << "Failed to open dex file: " << dex_location; - ThrowLocation throw_location = soa.Self()->GetCurrentLocationForThrow(); - soa.Self()->ThrowNewExceptionF(throw_location, "Ljava/io/IOException;", - "Unable to open dex file: %s", dex_location.c_str()); + if (dex_file == nullptr) { + ScopedObjectAccess soa(env); + ThrowIOException("%s", error_msg.c_str()); return 0; } return static_cast(reinterpret_cast(dex_file)); @@ -188,21 +186,17 @@ static jobjectArray DexFile_getClassNameList(JNIEnv* env, jclass, jint cookie) { } static jboolean DexFile_isDexOptNeeded(JNIEnv* env, jclass, jstring javaFilename) { - bool debug_logging = false; + const bool kVerboseLogging = false; // Spammy logging. + const bool kDebugLogging = true; // Logging useful for debugging. ScopedUtfChars filename(env, javaFilename); - if (filename.c_str() == NULL) { - LOG(ERROR) << "DexFile_isDexOptNeeded null filename"; - return JNI_TRUE; - } - if (!OS::FileExists(filename.c_str())) { + if ((filename.c_str() == nullptr) || !OS::FileExists(filename.c_str())) { LOG(ERROR) << "DexFile_isDexOptNeeded file '" << filename.c_str() << "' does not exist"; - ScopedObjectAccess soa(env); - ThrowLocation throw_location = soa.Self()->GetCurrentLocationForThrow(); - soa.Self()->ThrowNewExceptionF(throw_location, "Ljava/io/FileNotFoundException;", - "%s", filename.c_str()); - return JNI_TRUE; + ScopedLocalRef fnfe(env, env->FindClass("java/io/FileNotFoundException")); + const char* message = (filename.c_str() == nullptr) ? "" : filename.c_str(); + env->ThrowNew(fnfe.get(), message); + return JNI_FALSE; } // Always treat elements of the bootclasspath as up-to-date. The @@ -212,7 +206,7 @@ static jboolean DexFile_isDexOptNeeded(JNIEnv* env, jclass, jstring javaFilename const std::vector& boot_class_path = class_linker->GetBootClassPath(); for (size_t i = 0; i < boot_class_path.size(); i++) { if (boot_class_path[i]->GetLocation() == filename.c_str()) { - if (debug_logging) { + if (kVerboseLogging) { LOG(INFO) << "DexFile_isDexOptNeeded ignoring boot class path file: " << filename.c_str(); } return JNI_FALSE; @@ -221,26 +215,32 @@ static jboolean DexFile_isDexOptNeeded(JNIEnv* env, jclass, jstring javaFilename // Check if we have an odex file next to the dex file. std::string odex_filename(OatFile::DexFilenameToOdexFilename(filename.c_str())); - UniquePtr oat_file(OatFile::Open(odex_filename, odex_filename, NULL, false)); - if (oat_file.get() != NULL) { - ScopedObjectAccess soa(env); - const art::OatFile::OatDexFile* oat_dex_file = oat_file->GetOatDexFile(filename.c_str(), NULL); - if (oat_dex_file == NULL) { - if (debug_logging) { - LOG(INFO) << "DexFile_isDexOptNeeded GetOatDexFile failed"; - } - } else { + std::string error_msg; + UniquePtr oat_file(OatFile::Open(odex_filename, odex_filename, NULL, false, + &error_msg)); + if (oat_file.get() == nullptr) { + if (kVerboseLogging) { + LOG(INFO) << "DexFile_isDexOptNeeded failed to open oat file '" << filename.c_str() + << "': " << error_msg; + } + error_msg.clear(); + } else { + const art::OatFile::OatDexFile* oat_dex_file = oat_file->GetOatDexFile(filename.c_str(), NULL, + kDebugLogging); + if (oat_dex_file != nullptr) { uint32_t location_checksum; - // If we have no classes.dex checksum such as in a user build, assume up-to-date. - if (!DexFile::GetChecksum(filename.c_str(), &location_checksum)) { - if (debug_logging) { + // If its not possible to read the classes.dex assume up-to-date as we won't be able to + // compile it anyway. + if (!DexFile::GetChecksum(filename.c_str(), &location_checksum, &error_msg)) { + if (kVerboseLogging) { LOG(INFO) << "DexFile_isDexOptNeeded ignoring precompiled stripped file: " - << filename.c_str(); + << filename.c_str() << ": " << error_msg; } return JNI_FALSE; } - if (ClassLinker::VerifyOatFileChecksums(oat_file.get(), filename.c_str(), location_checksum)) { - if (debug_logging) { + if (ClassLinker::VerifyOatFileChecksums(oat_file.get(), filename.c_str(), location_checksum, + &error_msg)) { + if (kVerboseLogging) { LOG(INFO) << "DexFile_isDexOptNeeded precompiled file " << odex_filename << " is up-to-date checksum compared to " << filename.c_str(); } @@ -251,10 +251,12 @@ static jboolean DexFile_isDexOptNeeded(JNIEnv* env, jclass, jstring javaFilename // Check if we have an oat file in the cache std::string cache_location(GetDalvikCacheFilenameOrDie(filename.c_str())); - oat_file.reset(OatFile::Open(cache_location, filename.c_str(), NULL, false)); - if (oat_file.get() == NULL) { - LOG(INFO) << "DexFile_isDexOptNeeded cache file " << cache_location - << " does not exist for " << filename.c_str(); + oat_file.reset(OatFile::Open(cache_location, filename.c_str(), NULL, false, &error_msg)); + if (oat_file.get() == nullptr) { + if (kDebugLogging) { + LOG(INFO) << "DexFile_isDexOptNeeded cache file " << cache_location + << " does not exist for " << filename.c_str() << ": " << error_msg; + } return JNI_TRUE; } @@ -262,41 +264,53 @@ static jboolean DexFile_isDexOptNeeded(JNIEnv* env, jclass, jstring javaFilename if (space->IsImageSpace()) { // TODO: Ensure this works with multiple image spaces. const ImageHeader& image_header = space->AsImageSpace()->GetImageHeader(); - if (oat_file->GetOatHeader().GetImageFileLocationOatChecksum() != image_header.GetOatChecksum()) { - ScopedObjectAccess soa(env); - LOG(INFO) << "DexFile_isDexOptNeeded cache file " << cache_location - << " has out-of-date oat checksum compared to " - << image_header.GetImageRoot(ImageHeader::kOatLocation)->AsString()->ToModifiedUtf8(); + if (oat_file->GetOatHeader().GetImageFileLocationOatChecksum() != + image_header.GetOatChecksum()) { + if (kDebugLogging) { + ScopedObjectAccess soa(env); + LOG(INFO) << "DexFile_isDexOptNeeded cache file " << cache_location + << " has out-of-date oat checksum compared to " + << image_header.GetImageRoot(ImageHeader::kOatLocation)->AsString()->ToModifiedUtf8(); + } return JNI_TRUE; } if (oat_file->GetOatHeader().GetImageFileLocationOatDataBegin() != reinterpret_cast(image_header.GetOatDataBegin())) { - ScopedObjectAccess soa(env); - LOG(INFO) << "DexFile_isDexOptNeeded cache file " << cache_location - << " has out-of-date oat begin compared to " - << image_header.GetImageRoot(ImageHeader::kOatLocation)->AsString()->ToModifiedUtf8(); + if (kDebugLogging) { + ScopedObjectAccess soa(env); + LOG(INFO) << "DexFile_isDexOptNeeded cache file " << cache_location + << " has out-of-date oat begin compared to " + << image_header.GetImageRoot(ImageHeader::kOatLocation)->AsString()->ToModifiedUtf8(); + } return JNI_TRUE; } } } - ScopedObjectAccess soa(env); uint32_t location_checksum; - if (!DexFile::GetChecksum(filename.c_str(), &location_checksum)) { - LOG(ERROR) << "DexFile_isDexOptNeeded failed to compute checksum of " << filename.c_str(); + if (!DexFile::GetChecksum(filename.c_str(), &location_checksum, &error_msg)) { + if (kDebugLogging) { + LOG(ERROR) << "DexFile_isDexOptNeeded failed to compute checksum of " << filename.c_str() + << " (error " << error_msg << ")"; + } return JNI_TRUE; } - if (!ClassLinker::VerifyOatFileChecksums(oat_file.get(), filename.c_str(), location_checksum)) { - LOG(INFO) << "DexFile_isDexOptNeeded cache file " << cache_location - << " has out-of-date checksum compared to " << filename.c_str(); + if (!ClassLinker::VerifyOatFileChecksums(oat_file.get(), filename.c_str(), location_checksum, + &error_msg)) { + if (kDebugLogging) { + LOG(INFO) << "DexFile_isDexOptNeeded cache file " << cache_location + << " has out-of-date checksum compared to " << filename.c_str() + << " (error " << error_msg << ")"; + } return JNI_TRUE; } - if (debug_logging) { + if (kVerboseLogging) { LOG(INFO) << "DexFile_isDexOptNeeded cache file " << cache_location << " is up-to-date for " << filename.c_str(); } + CHECK(error_msg.empty()) << error_msg; return JNI_FALSE; } diff --git a/runtime/native/java_lang_VMClassLoader.cc b/runtime/native/java_lang_VMClassLoader.cc index c23b08ca667..af1b548aa3d 100644 --- a/runtime/native/java_lang_VMClassLoader.cc +++ b/runtime/native/java_lang_VMClassLoader.cc @@ -72,8 +72,10 @@ static jstring VMClassLoader_getBootClassPathResource(JNIEnv* env, jclass, jstri } const DexFile* dex_file = path[index]; const std::string& location(dex_file->GetLocation()); - UniquePtr zip_archive(ZipArchive::Open(location)); - if (zip_archive.get() == NULL) { + std::string error_msg; + UniquePtr zip_archive(ZipArchive::Open(location.c_str(), &error_msg)); + if (zip_archive.get() == nullptr) { + LOG(WARNING) << "Failed to open zip archive '" << location << "': " << error_msg; return NULL; } UniquePtr zip_entry(zip_archive->Find(name.c_str())); diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc index 7ecaf012ae3..7553dcca341 100644 --- a/runtime/oat_file.cc +++ b/runtime/oat_file.cc @@ -48,19 +48,21 @@ void OatFile::CheckLocation(const std::string& location) { } OatFile* OatFile::OpenMemory(std::vector& oat_contents, - const std::string& location) { + const std::string& location, + std::string* error_msg) { CHECK(!oat_contents.empty()) << location; CheckLocation(location); UniquePtr oat_file(new OatFile(location)); oat_file->begin_ = &oat_contents[0]; oat_file->end_ = &oat_contents[oat_contents.size()]; - return oat_file->Setup() ? oat_file.release() : NULL; + return oat_file->Setup(error_msg) ? oat_file.release() : nullptr; } OatFile* OatFile::Open(const std::string& filename, const std::string& location, byte* requested_base, - bool executable) { + bool executable, + std::string* error_msg) { CHECK(!filename.empty()) << location; CheckLocation(filename); #ifdef ART_USE_PORTABLE_COMPILER @@ -70,7 +72,7 @@ OatFile* OatFile::Open(const std::string& filename, // open a generated dex file by name, remove the file, then open // another generated dex file with the same name. http://b/10614658 if (executable) { - return OpenDlopen(filename, location, requested_base); + return OpenDlopen(filename, location, requested_base, error_msg); } #endif // If we aren't trying to execute, we just use our own ElfFile loader for a couple reasons: @@ -83,21 +85,22 @@ OatFile* OatFile::Open(const std::string& filename, if (file.get() == NULL) { return NULL; } - return OpenElfFile(file.get(), location, requested_base, false, executable); + return OpenElfFile(file.get(), location, requested_base, false, executable, error_msg); } -OatFile* OatFile::OpenWritable(File* file, const std::string& location) { +OatFile* OatFile::OpenWritable(File* file, const std::string& location, std::string* error_msg) { CheckLocation(location); - return OpenElfFile(file, location, NULL, true, false); + return OpenElfFile(file, location, NULL, true, false, error_msg); } OatFile* OatFile::OpenDlopen(const std::string& elf_filename, const std::string& location, - byte* requested_base) { + byte* requested_base, + std::string* error_msg) { UniquePtr oat_file(new OatFile(location)); - bool success = oat_file->Dlopen(elf_filename, requested_base); + bool success = oat_file->Dlopen(elf_filename, requested_base, error_msg); if (!success) { - return NULL; + return nullptr; } return oat_file.release(); } @@ -106,11 +109,13 @@ OatFile* OatFile::OpenElfFile(File* file, const std::string& location, byte* requested_base, bool writable, - bool executable) { + bool executable, + std::string* error_msg) { UniquePtr oat_file(new OatFile(location)); - bool success = oat_file->ElfFileOpen(file, requested_base, writable, executable); + bool success = oat_file->ElfFileOpen(file, requested_base, writable, executable, error_msg); if (!success) { - return NULL; + CHECK(!error_msg->empty()); + return nullptr; } return oat_file.release(); } @@ -127,120 +132,117 @@ OatFile::~OatFile() { } } -bool OatFile::Dlopen(const std::string& elf_filename, byte* requested_base) { +bool OatFile::Dlopen(const std::string& elf_filename, byte* requested_base, + std::string* error_msg) { char* absolute_path = realpath(elf_filename.c_str(), NULL); if (absolute_path == NULL) { - VLOG(class_linker) << "Failed to find absolute path for " << elf_filename; + *error_msg = StringPrintf("Failed to find absolute path for '%s'", elf_filename.c_str()); return false; } dlopen_handle_ = dlopen(absolute_path, RTLD_NOW); free(absolute_path); if (dlopen_handle_ == NULL) { - VLOG(class_linker) << "Failed to dlopen " << elf_filename << ": " << dlerror(); + *error_msg = StringPrintf("Failed to dlopen '%s': %s", elf_filename.c_str(), dlerror()); return false; } begin_ = reinterpret_cast(dlsym(dlopen_handle_, "oatdata")); if (begin_ == NULL) { - LOG(WARNING) << "Failed to find oatdata symbol in " << elf_filename << ": " << dlerror(); + *error_msg = StringPrintf("Failed to find oatdata symbol in '%s': %s", elf_filename.c_str(), + dlerror()); return false; } if (requested_base != NULL && begin_ != requested_base) { - std::string maps; - ReadFileToString("/proc/self/maps", &maps); - LOG(WARNING) << "Failed to find oatdata symbol at expected address: oatdata=" - << reinterpret_cast(begin_) << " != expected=" - << reinterpret_cast(requested_base) - << " /proc/self/maps:\n" << maps; + *error_msg = StringPrintf("Failed to find oatdata symbol at expected address: " + "oatdata=%p != expected=%p /proc/self/maps:\n", + begin_, requested_base); + ReadFileToString("/proc/self/maps", error_msg); return false; } end_ = reinterpret_cast(dlsym(dlopen_handle_, "oatlastword")); if (end_ == NULL) { - LOG(WARNING) << "Failed to find oatlastword symbol in " << elf_filename << ": " << dlerror(); + *error_msg = StringPrintf("Failed to find oatlastword symbol in '%s': %s", elf_filename.c_str(), + dlerror()); return false; } // Readjust to be non-inclusive upper bound. end_ += sizeof(uint32_t); - return Setup(); + return Setup(error_msg); } -bool OatFile::ElfFileOpen(File* file, byte* requested_base, bool writable, bool executable) { - elf_file_.reset(ElfFile::Open(file, writable, true)); - if (elf_file_.get() == NULL) { - if (writable) { - PLOG(WARNING) << "Failed to open ELF file for " << file->GetPath(); - } +bool OatFile::ElfFileOpen(File* file, byte* requested_base, bool writable, bool executable, + std::string* error_msg) { + elf_file_.reset(ElfFile::Open(file, writable, true, error_msg)); + if (elf_file_.get() == nullptr) { + DCHECK(!error_msg->empty()); return false; } - bool loaded = elf_file_->Load(executable); + bool loaded = elf_file_->Load(executable, error_msg); if (!loaded) { - LOG(WARNING) << "Failed to load ELF file " << file->GetPath(); + DCHECK(!error_msg->empty()); return false; } begin_ = elf_file_->FindDynamicSymbolAddress("oatdata"); if (begin_ == NULL) { - LOG(WARNING) << "Failed to find oatdata symbol in " << file->GetPath(); + *error_msg = StringPrintf("Failed to find oatdata symbol in '%s'", file->GetPath().c_str()); return false; } if (requested_base != NULL && begin_ != requested_base) { - std::string maps; - ReadFileToString("/proc/self/maps", &maps); - LOG(WARNING) << "Failed to find oatdata symbol at expected address: oatdata=" - << reinterpret_cast(begin_) << " != expected=" - << reinterpret_cast(requested_base) - << " /proc/self/maps:\n" << maps; + *error_msg = StringPrintf("Failed to find oatdata symbol at expected address: " + "oatdata=%p != expected=%p /proc/self/maps:\n", + begin_, requested_base); + ReadFileToString("/proc/self/maps", error_msg); return false; } end_ = elf_file_->FindDynamicSymbolAddress("oatlastword"); if (end_ == NULL) { - LOG(WARNING) << "Failed to find oatlastword symbol in " << file->GetPath(); + *error_msg = StringPrintf("Failed to find oatlastword symbol in '%s'", file->GetPath().c_str()); return false; } // Readjust to be non-inclusive upper bound. end_ += sizeof(uint32_t); - return Setup(); + return Setup(error_msg); } -bool OatFile::Setup() { +bool OatFile::Setup(std::string* error_msg) { if (!GetOatHeader().IsValid()) { - LOG(WARNING) << "Invalid oat magic for " << GetLocation(); + *error_msg = StringPrintf("Invalid oat magic for '%s'", GetLocation().c_str()); return false; } const byte* oat = Begin(); oat += sizeof(OatHeader); if (oat > End()) { - LOG(ERROR) << "In oat file " << GetLocation() << " found truncated OatHeader"; + *error_msg = StringPrintf("In oat file '%s' found truncated OatHeader", GetLocation().c_str()); return false; } oat += GetOatHeader().GetImageFileLocationSize(); if (oat > End()) { - LOG(ERROR) << "In oat file " << GetLocation() << " found truncated image file location: " - << reinterpret_cast(Begin()) - << "+" << sizeof(OatHeader) - << "+" << GetOatHeader().GetImageFileLocationSize() - << "<=" << reinterpret_cast(End()); + *error_msg = StringPrintf("In oat file '%s' found truncated image file location: " + "%p + %zd + %ud <= %p", GetLocation().c_str(), + Begin(), sizeof(OatHeader), GetOatHeader().GetImageFileLocationSize(), + End()); return false; } for (size_t i = 0; i < GetOatHeader().GetDexFileCount(); i++) { size_t dex_file_location_size = *reinterpret_cast(oat); - if (dex_file_location_size == 0U) { - LOG(ERROR) << "In oat file " << GetLocation() << " found OatDexFile # " << i - << " with empty location name"; + if (UNLIKELY(dex_file_location_size == 0U)) { + *error_msg = StringPrintf("In oat file '%s' found OatDexFile #%zd with empty location name", + GetLocation().c_str(), i); return false; } oat += sizeof(dex_file_location_size); - if (oat > End()) { - LOG(ERROR) << "In oat file " << GetLocation() << " found OatDexFile # " << i - << " truncated after dex file location size"; + if (UNLIKELY(oat > End())) { + *error_msg = StringPrintf("In oat file '%s' found OatDexFile #%zd truncated after dex file " + "location size", GetLocation().c_str(), i); return false; } const char* dex_file_location_data = reinterpret_cast(oat); oat += dex_file_location_size; - if (oat > End()) { - LOG(ERROR) << "In oat file " << GetLocation() << " found OatDexFile # " << i - << " with truncated dex file location"; + if (UNLIKELY(oat > End())) { + *error_msg = StringPrintf("In oat file '%s' found OatDexFile #%zd with truncated dex file " + "location", GetLocation().c_str(), i); return false; } @@ -248,55 +250,54 @@ bool OatFile::Setup() { uint32_t dex_file_checksum = *reinterpret_cast(oat); oat += sizeof(dex_file_checksum); - if (oat > End()) { - LOG(ERROR) << "In oat file " << GetLocation() << " found OatDexFile # " << i - << " for "<< dex_file_location - << " truncated after dex file checksum"; + if (UNLIKELY(oat > End())) { + *error_msg = StringPrintf("In oat file '%s' found OatDexFile #%zd for '%s' truncated after " + "dex file checksum", GetLocation().c_str(), i, + dex_file_location.c_str()); return false; } uint32_t dex_file_offset = *reinterpret_cast(oat); - if (dex_file_offset == 0U) { - LOG(ERROR) << "In oat file " << GetLocation() << " found OatDexFile # " << i - << " for "<< dex_file_location - << " with zero dex file offset"; + if (UNLIKELY(dex_file_offset == 0U)) { + *error_msg = StringPrintf("In oat file '%s' found OatDexFile #%zd for '%s' with zero dex " + "file offset", GetLocation().c_str(), i, dex_file_location.c_str()); return false; } - if (dex_file_offset > Size()) { - LOG(ERROR) << "In oat file " << GetLocation() << " found OatDexFile # " << i - << " for "<< dex_file_location - << " with dex file offset" << dex_file_offset << " > " << Size(); + if (UNLIKELY(dex_file_offset > Size())) { + *error_msg = StringPrintf("In oat file '%s' found OatDexFile #%zd for '%s' with dex file " + "offset %ud > %zd", GetLocation().c_str(), i, + dex_file_location.c_str(), dex_file_offset, Size()); return false; } oat += sizeof(dex_file_offset); - if (oat > End()) { - LOG(ERROR) << "In oat file " << GetLocation() << " found OatDexFile # " << i - << " for "<< dex_file_location - << " truncated after dex file offset"; + if (UNLIKELY(oat > End())) { + *error_msg = StringPrintf("In oat file '%s' found OatDexFile #%zd for '%s' truncated " + " after dex file offsets", GetLocation().c_str(), i, + dex_file_location.c_str()); return false; } const uint8_t* dex_file_pointer = Begin() + dex_file_offset; - if (!DexFile::IsMagicValid(dex_file_pointer)) { - LOG(ERROR) << "In oat file " << GetLocation() << " found OatDexFile # " << i - << " for "<< dex_file_location - << " with invalid dex file magic: " << dex_file_pointer; + if (UNLIKELY(!DexFile::IsMagicValid(dex_file_pointer))) { + *error_msg = StringPrintf("In oat file '%s' found OatDexFile #%zd for '%s' with invalid " + " dex file magic '%s'", GetLocation().c_str(), i, + dex_file_location.c_str(), dex_file_pointer); return false; } - if (!DexFile::IsVersionValid(dex_file_pointer)) { - LOG(ERROR) << "In oat file " << GetLocation() << " found OatDexFile # " << i - << " for "<< dex_file_location - << " with invalid dex file version: " << dex_file_pointer; + if (UNLIKELY(!DexFile::IsVersionValid(dex_file_pointer))) { + *error_msg = StringPrintf("In oat file '%s' found OatDexFile #%zd for '%s' with invalid " + " dex file version '%s'", GetLocation().c_str(), i, + dex_file_location.c_str(), dex_file_pointer); return false; } const DexFile::Header* header = reinterpret_cast(dex_file_pointer); const uint32_t* methods_offsets_pointer = reinterpret_cast(oat); oat += (sizeof(*methods_offsets_pointer) * header->class_defs_size_); - if (oat > End()) { - LOG(ERROR) << "In oat file " << GetLocation() << " found OatDexFile # " << i - << " for "<< dex_file_location - << " with truncated method offsets"; + if (UNLIKELY(oat > End())) { + *error_msg = StringPrintf("In oat file '%s' found OatDexFile #%zd for '%s' with truncated " + " method offsets", GetLocation().c_str(), i, + dex_file_location.c_str()); return false; } @@ -323,8 +324,8 @@ const byte* OatFile::End() const { return end_; } -const OatFile::OatDexFile* OatFile::GetOatDexFile(const std::string& dex_location, - const uint32_t* const dex_location_checksum, +const OatFile::OatDexFile* OatFile::GetOatDexFile(const char* dex_location, + const uint32_t* dex_location_checksum, bool warn_if_not_found) const { Table::const_iterator it = oat_dex_files_.find(dex_location); if (it != oat_dex_files_.end()) { @@ -373,9 +374,9 @@ size_t OatFile::OatDexFile::FileSize() const { return reinterpret_cast(dex_file_pointer_)->file_size_; } -const DexFile* OatFile::OatDexFile::OpenDexFile() const { +const DexFile* OatFile::OatDexFile::OpenDexFile(std::string* error_msg) const { return DexFile::Open(dex_file_pointer_, FileSize(), dex_file_location_, - dex_file_location_checksum_); + dex_file_location_checksum_, error_msg); } const OatFile::OatClass* OatFile::OatDexFile::GetOatClass(uint16_t class_def_index) const { diff --git a/runtime/oat_file.h b/runtime/oat_file.h index 270976f030c..af1476088d2 100644 --- a/runtime/oat_file.h +++ b/runtime/oat_file.h @@ -45,18 +45,20 @@ class OatFile { static OatFile* Open(const std::string& filename, const std::string& location, byte* requested_base, - bool executable); + bool executable, + std::string* error_msg); // Open an oat file from an already opened File. // Does not use dlopen underneath so cannot be used for runtime use // where relocations may be required. Currently used from // ImageWriter which wants to open a writable version from an existing // file descriptor for patching. - static OatFile* OpenWritable(File* file, const std::string& location); + static OatFile* OpenWritable(File* file, const std::string& location, std::string* error_msg); // Open an oat file backed by a std::vector with the given location. static OatFile* OpenMemory(std::vector& oat_contents, - const std::string& location); + const std::string& location, + std::string* error_msg); ~OatFile(); @@ -167,7 +169,7 @@ class OatFile { class OatDexFile { public: // Opens the DexFile referred to by this OatDexFile from within the containing OatFile. - const DexFile* OpenDexFile() const; + const DexFile* OpenDexFile(std::string* error_msg) const; // Returns the size of the DexFile refered to by this OatDexFile. size_t FileSize() const; @@ -204,10 +206,10 @@ class OatFile { DISALLOW_COPY_AND_ASSIGN(OatDexFile); }; - const OatDexFile* GetOatDexFile(const std::string& dex_location, + const OatDexFile* GetOatDexFile(const char* dex_location, const uint32_t* const dex_location_checksum, - bool exception_if_not_found = true) const - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + bool exception_if_not_found = true) const; + std::vector GetOatDexFiles() const; size_t Size() const { @@ -219,18 +221,21 @@ class OatFile { static OatFile* OpenDlopen(const std::string& elf_filename, const std::string& location, - byte* requested_base); + byte* requested_base, + std::string* error_msg); static OatFile* OpenElfFile(File* file, const std::string& location, byte* requested_base, bool writable, - bool executable); + bool executable, + std::string* error_msg); explicit OatFile(const std::string& filename); - bool Dlopen(const std::string& elf_filename, byte* requested_base); - bool ElfFileOpen(File* file, byte* requested_base, bool writable, bool executable); - bool Setup(); + bool Dlopen(const std::string& elf_filename, byte* requested_base, std::string* error_msg); + bool ElfFileOpen(File* file, byte* requested_base, bool writable, bool executable, + std::string* error_msg); + bool Setup(std::string* error_msg); const byte* Begin() const; const byte* End() const; diff --git a/runtime/utils.cc b/runtime/utils.cc index e2852a6cfab..f9e4ebe14d8 100644 --- a/runtime/utils.cc +++ b/runtime/utils.cc @@ -718,9 +718,9 @@ bool IsValidPartOfMemberNameUtf8Slow(const char** pUtf8Ptr) { * this function returns false, then the given pointer may only have * been partially advanced. */ -bool IsValidPartOfMemberNameUtf8(const char** pUtf8Ptr) { +static bool IsValidPartOfMemberNameUtf8(const char** pUtf8Ptr) { uint8_t c = (uint8_t) **pUtf8Ptr; - if (c <= 0x7f) { + if (LIKELY(c <= 0x7f)) { // It's low-ascii, so check the table. uint32_t wordIdx = c >> 5; uint32_t bitIdx = c & 0x1f; @@ -761,7 +761,7 @@ bool IsValidMemberName(const char* s) { } enum ClassNameType { kName, kDescriptor }; -bool IsValidClassName(const char* s, ClassNameType type, char separator) { +static bool IsValidClassName(const char* s, ClassNameType type, char separator) { int arrayCount = 0; while (*s == '[') { arrayCount++; @@ -1194,12 +1194,12 @@ std::string GetDalvikCacheOrDie(const char* android_data) { return dalvik_cache; } -std::string GetDalvikCacheFilenameOrDie(const std::string& location) { +std::string GetDalvikCacheFilenameOrDie(const char* location) { std::string dalvik_cache(GetDalvikCacheOrDie(GetAndroidData())); if (location[0] != '/') { LOG(FATAL) << "Expected path in location to be absolute: "<< location; } - std::string cache_file(location, 1); // skip leading slash + std::string cache_file(&location[1]); // skip leading slash if (!EndsWith(location, ".dex") && !EndsWith(location, ".art")) { cache_file += "/"; cache_file += DexFile::kClassesDex; diff --git a/runtime/utils.h b/runtime/utils.h index 975f08b8389..0174b37dcc1 100644 --- a/runtime/utils.h +++ b/runtime/utils.h @@ -351,7 +351,7 @@ const char* GetAndroidData(); std::string GetDalvikCacheOrDie(const char* android_data); // Returns the dalvik-cache location for a DexFile or OatFile, or dies trying. -std::string GetDalvikCacheFilenameOrDie(const std::string& location); +std::string GetDalvikCacheFilenameOrDie(const char* location); // Check whether the given magic matches a known file type. bool IsZipMagic(uint32_t magic); diff --git a/runtime/zip_archive.cc b/runtime/zip_archive.cc index 8e09e783edc..db273ec7bf7 100644 --- a/runtime/zip_archive.cc +++ b/runtime/zip_archive.cc @@ -19,10 +19,12 @@ #include #include +#include #include #include #include +#include "base/stringprintf.h" #include "base/unix_file/fd_file.h" #include "UniquePtr.h" @@ -247,35 +249,38 @@ static bool InflateToMemory(uint8_t* begin, size_t size, return true; } -bool ZipEntry::ExtractToFile(File& file) { +bool ZipEntry::ExtractToFile(File& file, std::string* error_msg) { uint32_t length = GetUncompressedLength(); int result = TEMP_FAILURE_RETRY(ftruncate(file.Fd(), length)); if (result == -1) { - PLOG(WARNING) << "Zip: failed to ftruncate " << file.GetPath() << " to length " << length; + *error_msg = StringPrintf("Zip: failed to ftruncate '%s' to length %ud", file.GetPath().c_str(), + length); return false; } - UniquePtr map(MemMap::MapFile(length, PROT_READ | PROT_WRITE, MAP_SHARED, file.Fd(), 0)); + UniquePtr map(MemMap::MapFile(length, PROT_READ | PROT_WRITE, MAP_SHARED, file.Fd(), 0, + file.GetPath().c_str(), error_msg)); if (map.get() == NULL) { - LOG(WARNING) << "Zip: failed to mmap space for " << file.GetPath(); + *error_msg = StringPrintf("Zip: failed to mmap space for '%s': %s", file.GetPath().c_str(), + error_msg->c_str()); return false; } - return ExtractToMemory(map->Begin(), map->Size()); + return ExtractToMemory(map->Begin(), map->Size(), error_msg); } -bool ZipEntry::ExtractToMemory(uint8_t* begin, size_t size) { +bool ZipEntry::ExtractToMemory(uint8_t* begin, size_t size, std::string* error_msg) { // If size is zero, data offset will be meaningless, so bail out early. if (size == 0) { return true; } off64_t data_offset = GetDataOffset(); if (data_offset == -1) { - LOG(WARNING) << "Zip: data_offset=" << data_offset; + *error_msg = StringPrintf("Zip: data_offset=%lld", data_offset); return false; } if (lseek64(zip_archive_->fd_, data_offset, SEEK_SET) != data_offset) { - PLOG(WARNING) << "Zip: lseek to data at " << data_offset << " failed"; + *error_msg = StringPrintf("Zip: lseek to data at %lld failed", data_offset); return false; } @@ -288,25 +293,25 @@ bool ZipEntry::ExtractToMemory(uint8_t* begin, size_t size) { return InflateToMemory(begin, size, zip_archive_->fd_, GetUncompressedLength(), GetCompressedLength()); default: - LOG(WARNING) << "Zip: unknown compression method " << std::hex << GetCompressionMethod(); + *error_msg = StringPrintf("Zip: unknown compression method 0x%x", GetCompressionMethod()); return false; } } -MemMap* ZipEntry::ExtractToMemMap(const char* entry_filename) { +MemMap* ZipEntry::ExtractToMemMap(const char* entry_filename, std::string* error_msg) { std::string name(entry_filename); name += " extracted in memory from "; name += entry_filename; UniquePtr map(MemMap::MapAnonymous(name.c_str(), NULL, GetUncompressedLength(), - PROT_READ | PROT_WRITE)); - if (map.get() == NULL) { - LOG(ERROR) << "Zip: mmap for '" << entry_filename << "' failed"; + PROT_READ | PROT_WRITE, error_msg)); + if (map.get() == nullptr) { + DCHECK(!error_msg->empty()); return NULL; } - bool success = ExtractToMemory(map->Begin(), map->Size()); + bool success = ExtractToMemory(map->Begin(), map->Size(), error_msg); if (!success) { LOG(ERROR) << "Zip: Failed to extract '" << entry_filename << "' to memory"; return NULL; @@ -329,27 +334,25 @@ static void SetCloseOnExec(int fd) { } } -ZipArchive* ZipArchive::Open(const std::string& filename) { - DCHECK(!filename.empty()); - int fd = open(filename.c_str(), O_RDONLY, 0); +ZipArchive* ZipArchive::Open(const char* filename, std::string* error_msg) { + DCHECK(filename != nullptr); + int fd = open(filename, O_RDONLY, 0); if (fd == -1) { - PLOG(WARNING) << "Unable to open '" << filename << "'"; + *error_msg = StringPrintf("Zip: unable to open '%s': %s", filename, strerror(errno)); return NULL; } - return OpenFromFd(fd); + return OpenFromFd(fd, filename, error_msg); } -ZipArchive* ZipArchive::OpenFromFd(int fd) { +ZipArchive* ZipArchive::OpenFromFd(int fd, const char* filename, std::string* error_msg) { SetCloseOnExec(fd); - UniquePtr zip_archive(new ZipArchive(fd)); - if (zip_archive.get() == NULL) { - return NULL; - } - if (!zip_archive->MapCentralDirectory()) { + UniquePtr zip_archive(new ZipArchive(fd, filename)); + CHECK(zip_archive.get() != nullptr); + if (!zip_archive->MapCentralDirectory(error_msg)) { zip_archive->Close(); return NULL; } - if (!zip_archive->Parse()) { + if (!zip_archive->Parse(error_msg)) { zip_archive->Close(); return NULL; } @@ -374,19 +377,28 @@ void ZipArchive::Close() { dir_offset_ = 0; } +std::string ZipArchive::ErrorStringPrintf(const char* fmt, ...) { + va_list ap; + va_start(ap, fmt); + std::string result(StringPrintf("Zip '%s' : ", filename_.c_str())); + StringAppendV(&result, fmt, ap); + va_end(ap); + return result; +} + // Find the zip Central Directory and memory-map it. // // On success, returns true after populating fields from the EOCD area: // num_entries_ // dir_offset_ // dir_map_ -bool ZipArchive::MapCentralDirectory() { +bool ZipArchive::MapCentralDirectory(std::string* error_msg) { /* * Get and test file length. */ off64_t file_length = lseek64(fd_, 0, SEEK_END); if (file_length < kEOCDLen) { - LOG(WARNING) << "Zip: length " << file_length << " is too small to be zip"; + *error_msg = ErrorStringPrintf("length %lld is too small to be zip", file_length); return false; } @@ -396,27 +408,26 @@ bool ZipArchive::MapCentralDirectory() { } UniquePtr scan_buf(new uint8_t[read_amount]); - if (scan_buf.get() == NULL) { - return false; - } + CHECK(scan_buf.get() != nullptr); /* * Make sure this is a Zip archive. */ if (lseek64(fd_, 0, SEEK_SET) != 0) { - PLOG(WARNING) << "seek to start failed: "; + *error_msg = ErrorStringPrintf("seek to start failed: %s", strerror(errno)); return false; } ssize_t actual = TEMP_FAILURE_RETRY(read(fd_, scan_buf.get(), sizeof(int32_t))); if (actual != static_cast(sizeof(int32_t))) { - PLOG(INFO) << "couldn't read first signature from zip archive: "; + *error_msg = ErrorStringPrintf("couldn\'t read first signature from zip archive: %s", + strerror(errno)); return false; } unsigned int header = Le32ToHost(scan_buf.get()); if (header != kLFHSignature) { - LOG(VERBOSE) << "Not a Zip archive (found " << std::hex << header << ")"; + *error_msg = ErrorStringPrintf("not a zip archive (found 0x%x)", header); return false; } @@ -433,12 +444,13 @@ bool ZipArchive::MapCentralDirectory() { off64_t search_start = file_length - read_amount; if (lseek64(fd_, search_start, SEEK_SET) != search_start) { - PLOG(WARNING) << "Zip: seek " << search_start << " failed"; + *error_msg = ErrorStringPrintf("seek %lld failed: %s", search_start, strerror(errno)); return false; } actual = TEMP_FAILURE_RETRY(read(fd_, scan_buf.get(), read_amount)); if (actual != static_cast(read_amount)) { - PLOG(WARNING) << "Zip: read " << actual << ", expected " << read_amount << ". failed"; + *error_msg = ErrorStringPrintf("read %lld, expected %zd. %s", search_start, read_amount, + strerror(errno)); return false; } @@ -454,14 +466,14 @@ bool ZipArchive::MapCentralDirectory() { } } if (i < 0) { - LOG(WARNING) << "Zip: EOCD not found, not a zip file"; + *error_msg = ErrorStringPrintf("EOCD not found, not a zip file"); return false; } off64_t eocd_offset = search_start + i; const byte* eocd_ptr = scan_buf.get() + i; - DCHECK(eocd_offset < file_length); + CHECK(eocd_offset < file_length); // Grab the CD offset and size, and the number of entries in the // archive. Verify that they look reasonable. @@ -474,29 +486,28 @@ bool ZipArchive::MapCentralDirectory() { uint16_t comment_size = Le16ToHost(eocd_ptr + kEOCDCommentSize); if ((uint64_t) dir_offset + (uint64_t) dir_size > (uint64_t) eocd_offset) { - LOG(WARNING) << "Zip: bad offsets (" - << "dir=" << dir_offset << ", " - << "size=" << dir_size << ", " - << "eocd=" << eocd_offset << ")"; + *error_msg = ErrorStringPrintf("bad offsets (dir=%ud, size=%ud, eocd=%lld)", + dir_offset, dir_size, eocd_offset); return false; } if (num_entries == 0) { - LOG(WARNING) << "Zip: empty archive?"; + *error_msg = ErrorStringPrintf("empty archive?"); return false; } else if (num_entries != total_num_entries || disk_number != 0 || disk_with_central_dir != 0) { - LOG(WARNING) << "spanned archives not supported"; + *error_msg = ErrorStringPrintf("spanned archives not supported"); return false; } // Check to see if comment is a sane size if ((comment_size > (file_length - kEOCDLen)) || (eocd_offset > (file_length - kEOCDLen) - comment_size)) { - LOG(WARNING) << "comment size runs off end of file"; + *error_msg = ErrorStringPrintf("comment size runs off end of file"); return false; } // It all looks good. Create a mapping for the CD. - dir_map_.reset(MemMap::MapFile(dir_size, PROT_READ, MAP_SHARED, fd_, dir_offset)); + dir_map_.reset(MemMap::MapFile(dir_size, PROT_READ, MAP_SHARED, fd_, dir_offset, + filename_.c_str(), error_msg)); if (dir_map_.get() == NULL) { return false; } @@ -506,7 +517,7 @@ bool ZipArchive::MapCentralDirectory() { return true; } -bool ZipArchive::Parse() { +bool ZipArchive::Parse(std::string* error_msg) { const byte* cd_ptr = dir_map_->Begin(); size_t cd_length = dir_map_->Size(); @@ -515,23 +526,23 @@ bool ZipArchive::Parse() { const byte* ptr = cd_ptr; for (int i = 0; i < num_entries_; i++) { if (Le32ToHost(ptr) != kCDESignature) { - LOG(WARNING) << "Zip: missed a central dir sig (at " << i << ")"; + *error_msg = ErrorStringPrintf("missed a central dir sig (at %d)", i); return false; } if (ptr + kCDELen > cd_ptr + cd_length) { - LOG(WARNING) << "Zip: ran off the end (at " << i << ")"; + *error_msg = ErrorStringPrintf("ran off the end (at %d)", i); return false; } int64_t local_hdr_offset = Le32ToHost(ptr + kCDELocalOffset); if (local_hdr_offset >= dir_offset_) { - LOG(WARNING) << "Zip: bad LFH offset " << local_hdr_offset << " at entry " << i; + *error_msg = ErrorStringPrintf("bad LFH offset %lld at entry %d", local_hdr_offset, i); return false; } uint16_t gpbf = Le16ToHost(ptr + kCDEGPBFlags); if ((gpbf & kGPFUnsupportedMask) != 0) { - LOG(WARNING) << "Invalid General Purpose Bit Flag: " << gpbf; + *error_msg = ErrorStringPrintf("invalid general purpose bit flag %x", gpbf); return false; } @@ -544,16 +555,15 @@ bool ZipArchive::Parse() { // Check name for NULL characters if (memchr(name, 0, name_len) != NULL) { - LOG(WARNING) << "Filename contains NUL byte"; + *error_msg = ErrorStringPrintf("filename contains NUL byte"); return false; } dir_entries_.Put(StringPiece(name, name_len), ptr); ptr += kCDELen + name_len + extra_len + comment_len; if (ptr > cd_ptr + cd_length) { - LOG(WARNING) << "Zip: bad CD advance " - << "(" << ptr << " vs " << (cd_ptr + cd_length) << ") " - << "at entry " << i; + *error_msg = ErrorStringPrintf("bad CD advance (%p vs %p) at entry %d", + ptr, cd_ptr + cd_length, i); return false; } } diff --git a/runtime/zip_archive.h b/runtime/zip_archive.h index d9ccba2cceb..8ff952baab2 100644 --- a/runtime/zip_archive.h +++ b/runtime/zip_archive.h @@ -19,6 +19,7 @@ #include #include +#include #include "base/logging.h" #include "base/stringpiece.h" @@ -36,9 +37,9 @@ class MemMap; class ZipEntry { public: - bool ExtractToFile(File& file); - bool ExtractToMemory(uint8_t* begin, size_t size); - MemMap* ExtractToMemMap(const char* entry_filename); + bool ExtractToFile(File& file, std::string* error_msg); + bool ExtractToMemory(uint8_t* begin, size_t size, std::string* error_msg); + MemMap* ExtractToMemMap(const char* entry_filename, std::string* error_msg); uint32_t GetUncompressedLength(); uint32_t GetCrc32(); @@ -109,8 +110,8 @@ class ZipArchive { static const int32_t kGPFUnsupportedMask = (kGPFEncryptedFlag); // return new ZipArchive instance on success, NULL on error. - static ZipArchive* Open(const std::string& filename); - static ZipArchive* OpenFromFd(int fd); + static ZipArchive* Open(const char* filename, std::string* error_msg); + static ZipArchive* OpenFromFd(int fd, const char* filename, std::string* error_msg); ZipEntry* Find(const char* name) const; @@ -119,11 +120,14 @@ class ZipArchive { } private: - explicit ZipArchive(int fd) : fd_(fd), num_entries_(0), dir_offset_(0) {} + explicit ZipArchive(int fd, const char* filename) + : fd_(fd), num_entries_(0), dir_offset_(0), filename_(filename) {} - bool MapCentralDirectory(); - bool Parse(); + bool MapCentralDirectory(std::string* error_msg); + bool Parse(std::string* error_msg); void Close(); + std::string ErrorStringPrintf(const char* fmt, ...) + __attribute__((__format__(__printf__, 2, 3))) COLD_ATTR; int fd_; uint16_t num_entries_; @@ -131,6 +135,8 @@ class ZipArchive { UniquePtr dir_map_; typedef SafeMap DirEntries; DirEntries dir_entries_; + // Containing file for error reporting. + const std::string filename_; friend class ZipEntry; diff --git a/runtime/zip_archive_test.cc b/runtime/zip_archive_test.cc index 9bdc24ba03f..622dc89587c 100644 --- a/runtime/zip_archive_test.cc +++ b/runtime/zip_archive_test.cc @@ -29,8 +29,10 @@ namespace art { class ZipArchiveTest : public CommonTest {}; TEST_F(ZipArchiveTest, FindAndExtract) { - UniquePtr zip_archive(ZipArchive::Open(GetLibCoreDexFileName())); - ASSERT_TRUE(zip_archive.get() != false); + std::string error_msg; + UniquePtr zip_archive(ZipArchive::Open(GetLibCoreDexFileName().c_str(), &error_msg)); + ASSERT_TRUE(zip_archive.get() != false) << error_msg; + ASSERT_TRUE(error_msg.empty()); UniquePtr zip_entry(zip_archive->Find("classes.dex")); ASSERT_TRUE(zip_entry.get() != false); @@ -38,8 +40,9 @@ TEST_F(ZipArchiveTest, FindAndExtract) { ASSERT_NE(-1, tmp.GetFd()); UniquePtr file(new File(tmp.GetFd(), tmp.GetFilename())); ASSERT_TRUE(file.get() != NULL); - bool success = zip_entry->ExtractToFile(*file); - ASSERT_TRUE(success); + bool success = zip_entry->ExtractToFile(*file, &error_msg); + ASSERT_TRUE(success) << error_msg; + ASSERT_TRUE(error_msg.empty()); file.reset(NULL); uint32_t computed_crc = crc32(0L, Z_NULL, 0); From 1984152ac92aad244ae15184d12f9ceade686b7b Mon Sep 17 00:00:00 2001 From: Mathieu Chartier Date: Tue, 22 Oct 2013 11:29:00 -0700 Subject: [PATCH 0095/2402] Add missing callee save restore to quick entrypoints. Added callee save restoration to art_quick_proxy_invoke_handler, art_quick_resolution_trampoline, art_quick_to_interpreter_bridge. Bug: 8981901 Change-Id: Iaa5bf862834b49cc48bce4f4ffa34a6797024e6d --- runtime/arch/arm/quick_entrypoints_arm.S | 24 ++++++++++------------ runtime/arch/mips/quick_entrypoints_mips.S | 22 +++++++------------- runtime/arch/x86/quick_entrypoints_x86.S | 5 +++-- 3 files changed, 21 insertions(+), 30 deletions(-) diff --git a/runtime/arch/arm/quick_entrypoints_arm.S b/runtime/arch/arm/quick_entrypoints_arm.S index c98b7644240..736ce2fb24a 100644 --- a/runtime/arch/arm/quick_entrypoints_arm.S +++ b/runtime/arch/arm/quick_entrypoints_arm.S @@ -48,7 +48,7 @@ /* * Macro that sets up the callee save frame to conform with - * Runtime::CreateCalleeSaveMethod(kRefsOnly). Restoration assumes non-moving GC. + * Runtime::CreateCalleeSaveMethod(kRefsOnly). */ .macro SETUP_REF_ONLY_CALLEE_SAVE_FRAME push {r5-r8, r10-r11, lr} @ 7 words of callee saves @@ -81,7 +81,7 @@ /* * Macro that sets up the callee save frame to conform with - * Runtime::CreateCalleeSaveMethod(kRefsAndArgs). Restoration assumes non-moving GC. + * Runtime::CreateCalleeSaveMethod(kRefsAndArgs). */ .macro SETUP_REF_AND_ARGS_CALLEE_SAVE_FRAME push {r1-r3, r5-r8, r10-r11, lr} @ 10 words of callee saves @@ -1030,12 +1030,13 @@ ENTRY art_quick_proxy_invoke_handler mov r3, sp @ pass SP blx artQuickProxyInvokeHandler @ (Method* proxy method, receiver, Thread*, SP) ldr r2, [r9, #THREAD_EXCEPTION_OFFSET] @ load Thread::Current()->exception_ - ldr lr, [sp, #44] @ restore lr - add sp, #48 @ pop frame - .cfi_adjust_cfa_offset -48 + add sp, #16 @ skip r1-r3, 4 bytes padding. + .cfi_adjust_cfa_offset -16 cbnz r2, 1f @ success if no exception is pending + RESTORE_REF_ONLY_CALLEE_SAVE_FRAME bx lr @ return on success 1: + RESTORE_REF_ONLY_CALLEE_SAVE_FRAME DELIVER_PENDING_EXCEPTION END art_quick_proxy_invoke_handler @@ -1048,11 +1049,7 @@ ENTRY art_quick_resolution_trampoline cbz r0, 1f @ is code pointer null? goto exception mov r12, r0 ldr r0, [sp, #0] @ load resolved method in r0 - ldr r1, [sp, #8] @ restore non-callee save r1 - ldrd r2, [sp, #12] @ restore non-callee saves r2-r3 - ldr lr, [sp, #44] @ restore lr - add sp, #48 @ rewind sp - .cfi_adjust_cfa_offset -48 + RESTORE_REF_AND_ARGS_CALLEE_SAVE_FRAME bx r12 @ tail-call into actual code 1: RESTORE_REF_AND_ARGS_CALLEE_SAVE_FRAME @@ -1066,12 +1063,13 @@ ENTRY art_quick_to_interpreter_bridge mov r2, sp @ pass SP blx artQuickToInterpreterBridge @ (Method* method, Thread*, SP) ldr r2, [r9, #THREAD_EXCEPTION_OFFSET] @ load Thread::Current()->exception_ - ldr lr, [sp, #44] @ restore lr - add sp, #48 @ pop frame - .cfi_adjust_cfa_offset -48 + add sp, #16 @ skip r1-r3, 4 bytes padding. + .cfi_adjust_cfa_offset -16 cbnz r2, 1f @ success if no exception is pending + RESTORE_REF_ONLY_CALLEE_SAVE_FRAME bx lr @ return on success 1: + RESTORE_REF_ONLY_CALLEE_SAVE_FRAME DELIVER_PENDING_EXCEPTION END art_quick_to_interpreter_bridge diff --git a/runtime/arch/mips/quick_entrypoints_mips.S b/runtime/arch/mips/quick_entrypoints_mips.S index 897aaf40361..031d13a6b09 100644 --- a/runtime/arch/mips/quick_entrypoints_mips.S +++ b/runtime/arch/mips/quick_entrypoints_mips.S @@ -1051,11 +1051,9 @@ ENTRY art_quick_proxy_invoke_handler jal artQuickProxyInvokeHandler # (Method* proxy method, receiver, Thread*, SP) move $a3, $sp # pass $sp lw $t0, THREAD_EXCEPTION_OFFSET(rSELF) # load Thread::Current()->exception_ - lw $gp, 52($sp) # restore $gp - lw $ra, 60($sp) # restore $ra + RESTORE_REF_ONLY_CALLEE_SAVE_FRAME bnez $t0, 1f - addiu $sp, $sp, 64 # pop frame - .cfi_adjust_cfa_offset -64 + nop jr $ra nop 1: @@ -1069,18 +1067,14 @@ ENTRY art_quick_resolution_trampoline move $a2, rSELF # pass Thread::Current jal artQuickResolutionTrampoline # (Method* called, receiver, Thread*, SP) move $a3, $sp # pass $sp - lw $gp, 52($sp) # restore $gp - lw $ra, 60($sp) # restore $ra beqz $v0, 1f lw $a0, 0($sp) # load resolved method to $a0 - lw $a1, 4($sp) # restore non-callee save $a1 - lw $a2, 8($sp) # restore non-callee save $a2 - lw $a3, 12($sp) # restore non-callee save $a3 + RESTORE_REF_AND_ARGS_CALLEE_SAVE_FRAME move $t9, $v0 # code pointer must be in $t9 to generate the global pointer jr $v0 # tail call to method + nop 1: - addiu $sp, $sp, 64 # pop frame - .cfi_adjust_cfa_offset -64 + RESTORE_REF_AND_ARGS_CALLEE_SAVE_FRAME DELIVER_PENDING_EXCEPTION END art_quick_resolution_trampoline @@ -1092,11 +1086,9 @@ ENTRY art_quick_to_interpreter_bridge jal artQuickToInterpreterBridge # (Method* method, Thread*, SP) move $a2, $sp # pass $sp lw $t0, THREAD_EXCEPTION_OFFSET(rSELF) # load Thread::Current()->exception_ - lw $gp, 52($sp) # restore $gp - lw $ra, 60($sp) # restore $ra + RESTORE_REF_ONLY_CALLEE_SAVE_FRAME bnez $t0, 1f - addiu $sp, $sp, 64 # pop frame - .cfi_adjust_cfa_offset -64 + nop jr $ra nop 1: diff --git a/runtime/arch/x86/quick_entrypoints_x86.S b/runtime/arch/x86/quick_entrypoints_x86.S index d7e1be81700..805f6f4bd19 100644 --- a/runtime/arch/x86/quick_entrypoints_x86.S +++ b/runtime/arch/x86/quick_entrypoints_x86.S @@ -1034,8 +1034,9 @@ DEFINE_FUNCTION art_quick_to_interpreter_bridge movd %eax, %xmm0 // place return value also into floating point return value movd %edx, %xmm1 punpckldq %xmm1, %xmm0 - addl LITERAL(44), %esp // pop arguments - .cfi_adjust_cfa_offset -44 + addl LITERAL(16), %esp // pop arguments + .cfi_adjust_cfa_offset -16 + RESTORE_REF_ONLY_CALLEE_SAVE_FRAME RETURN_OR_DELIVER_PENDING_EXCEPTION // return or deliver exception END_FUNCTION art_quick_to_interpreter_bridge From 79b4f38dd35b83206e8166aaafb94bd75c3318b3 Mon Sep 17 00:00:00 2001 From: Mathieu Chartier Date: Wed, 23 Oct 2013 15:21:37 -0700 Subject: [PATCH 0096/2402] Fix incorrect initial dex cache size. We were previously setting it to be sizeof(DexCacheClass) instead of sizeof(DexCache). This was causing some problems with compaction when I relied on the object sizes being accurate to visit objects in the bump pointer space. Bug: 8981901 Change-Id: Iede04763aced041986b1b239368fc867143ad70d --- runtime/class_linker.cc | 7 ++++++- runtime/mirror/class-inl.h | 4 +--- runtime/mirror/object-inl.h | 1 + 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index aa5f2bf21ed..663e8b78c85 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -283,7 +283,7 @@ void ClassLinker::InitFromCompiler(const std::vector& boot_class SirtRef java_lang_DexCache(self, AllocClass(self, java_lang_Class.get(), sizeof(mirror::DexCacheClass))); SetClassRoot(kJavaLangDexCache, java_lang_DexCache.get()); - java_lang_DexCache->SetObjectSize(sizeof(mirror::DexCacheClass)); + java_lang_DexCache->SetObjectSize(sizeof(mirror::DexCache)); java_lang_DexCache->SetStatus(mirror::Class::kStatusResolved, self); // Constructor, Field, Method, and AbstractMethod are necessary so that FindClass can link members. @@ -3911,6 +3911,11 @@ bool ClassLinker::LinkFields(SirtRef& klass, bool is_static) { klass->SetNumReferenceInstanceFields(num_reference_fields); if (!klass->IsVariableSize()) { DCHECK_GE(size, sizeof(mirror::Object)) << ClassHelper(klass.get(), this).GetDescriptor(); + size_t previous_size = klass->GetObjectSize(); + if (previous_size != 0) { + // Make sure that we didn't originally have an incorrect size. + CHECK_EQ(previous_size, size); + } klass->SetObjectSize(size); } } diff --git a/runtime/mirror/class-inl.h b/runtime/mirror/class-inl.h index 88cffb77fc5..cd5e865c7c8 100644 --- a/runtime/mirror/class-inl.h +++ b/runtime/mirror/class-inl.h @@ -35,9 +35,7 @@ namespace mirror { inline size_t Class::GetObjectSize() const { DCHECK(!IsVariableSize()) << " class=" << PrettyTypeOf(this); DCHECK_EQ(sizeof(size_t), sizeof(int32_t)); - size_t result = GetField32(OFFSET_OF_OBJECT_MEMBER(Class, object_size_), false); - DCHECK_GE(result, sizeof(Object)) << " class=" << PrettyTypeOf(this); - return result; + return GetField32(OFFSET_OF_OBJECT_MEMBER(Class, object_size_), false); } inline Class* Class::GetSuperClass() const { diff --git a/runtime/mirror/object-inl.h b/runtime/mirror/object-inl.h index e6591088ef2..e460a8d1cca 100644 --- a/runtime/mirror/object-inl.h +++ b/runtime/mirror/object-inl.h @@ -247,6 +247,7 @@ inline size_t Object::SizeOf() const { } else { result = GetClass()->GetObjectSize(); } + DCHECK_GE(result, sizeof(Object)) << " class=" << PrettyTypeOf(GetClass()); DCHECK(!IsArtField() || result == sizeof(ArtField)); DCHECK(!IsArtMethod() || result == sizeof(ArtMethod)); return result; From 59fe2a68b94daa4d215d69facec1b3079846df9e Mon Sep 17 00:00:00 2001 From: Nick Kralevich Date: Wed, 23 Oct 2013 17:21:05 -0700 Subject: [PATCH 0097/2402] Add -Wl,--no-fatal-warnings to x86 build libart.so has text relocations. When -Wl,--fatal-warnings is added to the global LDFLAGS, these text relocations will become a compile error. This is a short term workaround until the text relocations can be fixed properly. Bug: 11358761 Bug: 11353056 Change-Id: I886d668a1bd6ed326f6552693d8c38c5d5a6b43d --- runtime/Android.mk | 3 +++ 1 file changed, 3 insertions(+) diff --git a/runtime/Android.mk b/runtime/Android.mk index a0ae4bffbc9..459ca0e8a74 100644 --- a/runtime/Android.mk +++ b/runtime/Android.mk @@ -178,6 +178,7 @@ LIBART_TARGET_SRC_FILES := \ runtime_android.cc \ thread_android.cc +LIBART_LDFLAGS := ifeq ($(TARGET_ARCH),arm) LIBART_TARGET_SRC_FILES += \ arch/arm/context_arm.cc.arm \ @@ -195,6 +196,7 @@ LIBART_TARGET_SRC_FILES += \ arch/x86/portable_entrypoints_x86.S \ arch/x86/quick_entrypoints_x86.S \ arch/x86/thread_x86.cc +LIBART_LDFLAGS += -Wl,--no-fatal-warnings else # TARGET_ARCH != x86 ifeq ($(TARGET_ARCH),mips) LIBART_TARGET_SRC_FILES += \ @@ -304,6 +306,7 @@ $$(ENUM_OPERATOR_OUT_GEN): $$(GENERATED_SRC_DIR)/%_operator_out.cc : $(LOCAL_PAT LOCAL_GENERATED_SOURCES += $$(ENUM_OPERATOR_OUT_GEN) LOCAL_CFLAGS := $(LIBART_CFLAGS) + LOCAL_LDFLAGS := $(LIBART_LDFLAGS) ifeq ($$(art_target_or_host),target) LOCAL_CLANG := $(ART_TARGET_CLANG) LOCAL_CFLAGS += $(ART_TARGET_CFLAGS) From e6ed00ba91da535fbe1d0b5a5705e99da149d82e Mon Sep 17 00:00:00 2001 From: Vladimir Marko Date: Thu, 24 Oct 2013 14:52:37 +0100 Subject: [PATCH 0098/2402] Fix x86 code generation for 0x0F 0x3A 0x?? instructions. Change-Id: I9b2b2190787d1e5674818159aa96e513d6325b54 --- compiler/dex/quick/x86/assemble_x86.cc | 22 +++++++++++----------- compiler/dex/quick/x86/x86_lir.h | 2 +- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/compiler/dex/quick/x86/assemble_x86.cc b/compiler/dex/quick/x86/assemble_x86.cc index fb8e75fc1b3..9167b1c8f2a 100644 --- a/compiler/dex/quick/x86/assemble_x86.cc +++ b/compiler/dex/quick/x86/assemble_x86.cc @@ -526,7 +526,7 @@ void X86Mir2Lir::EmitOpReg(const X86EncodingMap* entry, uint8_t reg) { code_buffer_.push_back(entry->skeleton.opcode); if (entry->skeleton.opcode == 0x0F) { code_buffer_.push_back(entry->skeleton.extra_opcode1); - if (entry->skeleton.extra_opcode1 == 0x38 || entry->skeleton.extra_opcode2 == 0x3A) { + if (entry->skeleton.extra_opcode1 == 0x38 || entry->skeleton.extra_opcode1 == 0x3A) { code_buffer_.push_back(entry->skeleton.extra_opcode2); } else { DCHECK_EQ(0, entry->skeleton.extra_opcode2); @@ -583,7 +583,7 @@ void X86Mir2Lir::EmitMemReg(const X86EncodingMap* entry, code_buffer_.push_back(entry->skeleton.opcode); if (entry->skeleton.opcode == 0x0F) { code_buffer_.push_back(entry->skeleton.extra_opcode1); - if (entry->skeleton.extra_opcode1 == 0x38 || entry->skeleton.extra_opcode2 == 0x3A) { + if (entry->skeleton.extra_opcode1 == 0x38 || entry->skeleton.extra_opcode1 == 0x3A) { code_buffer_.push_back(entry->skeleton.extra_opcode2); } else { DCHECK_EQ(0, entry->skeleton.extra_opcode2); @@ -632,7 +632,7 @@ void X86Mir2Lir::EmitRegArray(const X86EncodingMap* entry, uint8_t reg, uint8_t code_buffer_.push_back(entry->skeleton.opcode); if (entry->skeleton.opcode == 0x0F) { code_buffer_.push_back(entry->skeleton.extra_opcode1); - if (entry->skeleton.extra_opcode1 == 0x38 || entry->skeleton.extra_opcode2 == 0x3A) { + if (entry->skeleton.extra_opcode1 == 0x38 || entry->skeleton.extra_opcode1 == 0x3A) { code_buffer_.push_back(entry->skeleton.extra_opcode2); } else { DCHECK_EQ(0, entry->skeleton.extra_opcode2); @@ -673,7 +673,7 @@ void X86Mir2Lir::EmitRegThread(const X86EncodingMap* entry, uint8_t reg, int dis code_buffer_.push_back(entry->skeleton.opcode); if (entry->skeleton.opcode == 0x0F) { code_buffer_.push_back(entry->skeleton.extra_opcode1); - if (entry->skeleton.extra_opcode1 == 0x38 || entry->skeleton.extra_opcode2 == 0x3A) { + if (entry->skeleton.extra_opcode1 == 0x38 || entry->skeleton.extra_opcode1 == 0x3A) { code_buffer_.push_back(entry->skeleton.extra_opcode2); } else { DCHECK_EQ(0, entry->skeleton.extra_opcode2); @@ -713,7 +713,7 @@ void X86Mir2Lir::EmitRegReg(const X86EncodingMap* entry, uint8_t reg1, uint8_t r code_buffer_.push_back(entry->skeleton.opcode); if (entry->skeleton.opcode == 0x0F) { code_buffer_.push_back(entry->skeleton.extra_opcode1); - if (entry->skeleton.extra_opcode1 == 0x38 || entry->skeleton.extra_opcode2 == 0x3A) { + if (entry->skeleton.extra_opcode1 == 0x38 || entry->skeleton.extra_opcode1 == 0x3A) { code_buffer_.push_back(entry->skeleton.extra_opcode2); } else { DCHECK_EQ(0, entry->skeleton.extra_opcode2); @@ -750,7 +750,7 @@ void X86Mir2Lir::EmitRegRegImm(const X86EncodingMap* entry, code_buffer_.push_back(entry->skeleton.opcode); if (entry->skeleton.opcode == 0x0F) { code_buffer_.push_back(entry->skeleton.extra_opcode1); - if (entry->skeleton.extra_opcode1 == 0x38 || entry->skeleton.extra_opcode2 == 0x3A) { + if (entry->skeleton.extra_opcode1 == 0x38 || entry->skeleton.extra_opcode1 == 0x3A) { code_buffer_.push_back(entry->skeleton.extra_opcode2); } else { DCHECK_EQ(0, entry->skeleton.extra_opcode2); @@ -809,7 +809,7 @@ void X86Mir2Lir::EmitRegImm(const X86EncodingMap* entry, uint8_t reg, int imm) { code_buffer_.push_back(entry->skeleton.opcode); if (entry->skeleton.opcode == 0x0F) { code_buffer_.push_back(entry->skeleton.extra_opcode1); - if (entry->skeleton.extra_opcode1 == 0x38 || entry->skeleton.extra_opcode2 == 0x3A) { + if (entry->skeleton.extra_opcode1 == 0x38 || entry->skeleton.extra_opcode1 == 0x3A) { code_buffer_.push_back(entry->skeleton.extra_opcode2); } else { DCHECK_EQ(0, entry->skeleton.extra_opcode2); @@ -859,7 +859,7 @@ void X86Mir2Lir::EmitThreadImm(const X86EncodingMap* entry, int disp, int imm) { code_buffer_.push_back(entry->skeleton.opcode); if (entry->skeleton.opcode == 0x0F) { code_buffer_.push_back(entry->skeleton.extra_opcode1); - if (entry->skeleton.extra_opcode1 == 0x38 || entry->skeleton.extra_opcode2 == 0x3A) { + if (entry->skeleton.extra_opcode1 == 0x38 || entry->skeleton.extra_opcode1 == 0x3A) { code_buffer_.push_back(entry->skeleton.extra_opcode2); } else { DCHECK_EQ(0, entry->skeleton.extra_opcode2); @@ -924,7 +924,7 @@ void X86Mir2Lir::EmitShiftRegImm(const X86EncodingMap* entry, uint8_t reg, int i } if (entry->skeleton.opcode == 0x0F) { code_buffer_.push_back(entry->skeleton.extra_opcode1); - if (entry->skeleton.extra_opcode1 == 0x38 || entry->skeleton.extra_opcode2 == 0x3A) { + if (entry->skeleton.extra_opcode1 == 0x38 || entry->skeleton.extra_opcode1 == 0x3A) { code_buffer_.push_back(entry->skeleton.extra_opcode2); } else { DCHECK_EQ(0, entry->skeleton.extra_opcode2); @@ -1038,7 +1038,7 @@ void X86Mir2Lir::EmitCallMem(const X86EncodingMap* entry, uint8_t base, int disp code_buffer_.push_back(entry->skeleton.opcode); if (entry->skeleton.opcode == 0x0F) { code_buffer_.push_back(entry->skeleton.extra_opcode1); - if (entry->skeleton.extra_opcode1 == 0x38 || entry->skeleton.extra_opcode2 == 0x3A) { + if (entry->skeleton.extra_opcode1 == 0x38 || entry->skeleton.extra_opcode1 == 0x3A) { code_buffer_.push_back(entry->skeleton.extra_opcode2); } else { DCHECK_EQ(0, entry->skeleton.extra_opcode2); @@ -1067,7 +1067,7 @@ void X86Mir2Lir::EmitCallThread(const X86EncodingMap* entry, int disp) { code_buffer_.push_back(entry->skeleton.opcode); if (entry->skeleton.opcode == 0x0F) { code_buffer_.push_back(entry->skeleton.extra_opcode1); - if (entry->skeleton.extra_opcode1 == 0x38 || entry->skeleton.extra_opcode2 == 0x3A) { + if (entry->skeleton.extra_opcode1 == 0x38 || entry->skeleton.extra_opcode1 == 0x3A) { code_buffer_.push_back(entry->skeleton.extra_opcode2); } else { DCHECK_EQ(0, entry->skeleton.extra_opcode2); diff --git a/compiler/dex/quick/x86/x86_lir.h b/compiler/dex/quick/x86/x86_lir.h index 643a3d5b8f4..f1b91ca7fd2 100644 --- a/compiler/dex/quick/x86/x86_lir.h +++ b/compiler/dex/quick/x86/x86_lir.h @@ -243,7 +243,7 @@ enum X86OpCode { // - lir operands - 0: base, 1: disp, 2: immediate // AI - Array Immediate - opcode [base + index * scale + disp], #immediate // - lir operands - 0: base, 1: index, 2: scale, 3: disp 4: immediate - // TI - Thread Register - opcode fs:[disp], imm - where fs: is equal to Thread::Current() + // TI - Thread Immediate - opcode fs:[disp], imm - where fs: is equal to Thread::Current() // - lir operands - 0: disp, 1: imm #define BinaryOpCode(opcode) \ opcode ## 8MR, opcode ## 8AR, opcode ## 8TR, \ From 8584a68279efc0f9a409a3555ae5ebf3ec2cc4ac Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Thu, 24 Oct 2013 11:48:02 -0700 Subject: [PATCH 0099/2402] Add error message to ELF writer mc linker. Portable build fix. Change-Id: Ia7270984693d4ce8139595d78870c3522dfce36a --- compiler/elf_writer_mclinker.cc | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/compiler/elf_writer_mclinker.cc b/compiler/elf_writer_mclinker.cc index e496ace27ab..8e19ef61950 100644 --- a/compiler/elf_writer_mclinker.cc +++ b/compiler/elf_writer_mclinker.cc @@ -153,8 +153,9 @@ void ElfWriterMclinker::Init() { void ElfWriterMclinker::AddOatInput(std::vector& oat_contents) { // Add an artificial memory input. Based on LinkerTest. - UniquePtr oat_file(OatFile::OpenMemory(oat_contents, elf_file_->GetPath())); - CHECK(oat_file.get() != NULL) << elf_file_->GetPath(); + std::string error_msg; + UniquePtr oat_file(OatFile::OpenMemory(oat_contents, elf_file_->GetPath(), &error_msg)); + CHECK(oat_file.get() != NULL) << elf_file_->GetPath() << ": " << error_msg; const char* oat_data_start = reinterpret_cast(&oat_file->GetOatHeader()); const size_t oat_data_length = oat_file->GetOatHeader().GetExecutableOffset(); @@ -344,8 +345,9 @@ bool ElfWriterMclinker::Link() { #if defined(ART_USE_PORTABLE_COMPILER) void ElfWriterMclinker::FixupOatMethodOffsets(const std::vector& dex_files) { - UniquePtr elf_file(ElfFile::Open(elf_file_, true, false)); - CHECK(elf_file.get() != NULL) << elf_file_->GetPath(); + std::string error_msg; + UniquePtr elf_file(ElfFile::Open(elf_file_, true, false, &error_msg)); + CHECK(elf_file.get() != NULL) << elf_file_->GetPath() << ": " << error_msg; llvm::ELF::Elf32_Addr oatdata_address = GetOatDataAddress(elf_file.get()); DexMethodIterator it(dex_files); From 4db179d1821a9e78819d5adc8057a72f49e2aed8 Mon Sep 17 00:00:00 2001 From: buzbee Date: Wed, 23 Oct 2013 12:16:39 -0700 Subject: [PATCH 0100/2402] Null check elimination improvement See b/10862777 Improves the null check elimination pass by tracking visibility of object definitions, rather than successful uses of object dereferences. For boot class path, increases static null check elimination success rate from 98.4% to 98.6%. Reduces size of boot.oat by ~300K bytes. Fixes loop nesting depth computation, which is used by register promotion, and tweaked the heuristics. Fixes a bug in verbose listing output in which a basic block id is directly dereferenced, rather than first being converted to a pointer. Change-Id: Id01c20b533cdb12ea8fc4be576438407d0a34cec --- compiler/dex/mir_dataflow.cc | 3 +- compiler/dex/mir_optimization.cc | 86 ++++++++++++++++++------------ compiler/dex/quick/codegen_util.cc | 3 +- compiler/dex/ssa_transformation.cc | 1 + 4 files changed, 58 insertions(+), 35 deletions(-) diff --git a/compiler/dex/mir_dataflow.cc b/compiler/dex/mir_dataflow.cc index 9c8ce23ca56..11e19dc43f1 100644 --- a/compiler/dex/mir_dataflow.cc +++ b/compiler/dex/mir_dataflow.cc @@ -1243,7 +1243,8 @@ bool MIRGraph::CountUses(struct BasicBlock* bb) { if (mir->ssa_rep == NULL) { continue; } - uint32_t weight = std::min(16U, static_cast(bb->nesting_depth)); + // Each level of nesting adds *16 to count, up to 3 levels deep. + uint32_t weight = std::min(3U, static_cast(bb->nesting_depth) * 4); for (int i = 0; i < mir->ssa_rep->num_uses; i++) { int s_reg = mir->ssa_rep->uses[i]; raw_use_counts_.Increment(s_reg); diff --git a/compiler/dex/mir_optimization.cc b/compiler/dex/mir_optimization.cc index 3cd158ffc06..0b453b142fb 100644 --- a/compiler/dex/mir_optimization.cc +++ b/compiler/dex/mir_optimization.cc @@ -629,10 +629,15 @@ bool MIRGraph::EliminateNullChecks(struct BasicBlock* bb) { */ if ((bb->block_type == kEntryBlock) | bb->catch_entry) { temp_ssa_register_v_->ClearAllBits(); + // Assume all ins are objects. + for (uint16_t in_reg = cu_->num_dalvik_registers - cu_->num_ins; + in_reg < cu_->num_dalvik_registers; in_reg++) { + temp_ssa_register_v_->SetBit(in_reg); + } if ((cu_->access_flags & kAccStatic) == 0) { // If non-static method, mark "this" as non-null int this_reg = cu_->num_dalvik_registers - cu_->num_ins; - temp_ssa_register_v_->SetBit(this_reg); + temp_ssa_register_v_->ClearBit(this_reg); } } else if (bb->predecessors->Size() == 1) { BasicBlock* pred_bb = GetBasicBlock(bb->predecessors->Get(0)); @@ -645,18 +650,18 @@ bool MIRGraph::EliminateNullChecks(struct BasicBlock* bb) { if (pred_bb->fall_through == bb->id) { // The fall-through of a block following a IF_EQZ, set the vA of the IF_EQZ to show that // it can't be null. - temp_ssa_register_v_->SetBit(last_insn->ssa_rep->uses[0]); + temp_ssa_register_v_->ClearBit(last_insn->ssa_rep->uses[0]); } } else if (last_opcode == Instruction::IF_NEZ) { if (pred_bb->taken == bb->id) { // The taken block following a IF_NEZ, set the vA of the IF_NEZ to show that it can't be // null. - temp_ssa_register_v_->SetBit(last_insn->ssa_rep->uses[0]); + temp_ssa_register_v_->ClearBit(last_insn->ssa_rep->uses[0]); } } } } else { - // Starting state is intersection of all incoming arcs + // Starting state is union of all incoming arcs GrowableArray::Iterator iter(bb->predecessors); BasicBlock* pred_bb = GetBasicBlock(iter.Next()); DCHECK(pred_bb != NULL); @@ -668,10 +673,13 @@ bool MIRGraph::EliminateNullChecks(struct BasicBlock* bb) { (pred_bb->data_flow_info->ending_null_check_v == NULL)) { continue; } - temp_ssa_register_v_->Intersect(pred_bb->data_flow_info->ending_null_check_v); + temp_ssa_register_v_->Union(pred_bb->data_flow_info->ending_null_check_v); } } + // At this point, temp_ssa_register_v_ shows which sregs have an object definition with + // no intervening uses. + // Walk through the instruction in the block, updating as necessary for (MIR* mir = bb->first_mir_insn; mir != NULL; mir = mir->next) { if (mir->ssa_rep == NULL) { @@ -679,11 +687,41 @@ bool MIRGraph::EliminateNullChecks(struct BasicBlock* bb) { } int df_attributes = oat_data_flow_attributes_[mir->dalvikInsn.opcode]; - // Mark target of NEW* as non-null - if (df_attributes & DF_NON_NULL_DST) { + // Already nullchecked? + if ((df_attributes & DF_HAS_NULL_CHKS) && !(mir->optimization_flags & MIR_IGNORE_NULL_CHECK)) { + int src_idx; + if (df_attributes & DF_NULL_CHK_1) { + src_idx = 1; + } else if (df_attributes & DF_NULL_CHK_2) { + src_idx = 2; + } else { + src_idx = 0; + } + int src_sreg = mir->ssa_rep->uses[src_idx]; + if (!temp_ssa_register_v_->IsBitSet(src_sreg)) { + // Eliminate the null check + mir->optimization_flags |= MIR_IGNORE_NULL_CHECK; + } else { + // Mark s_reg as null-checked + temp_ssa_register_v_->ClearBit(src_sreg); + } + } + + if ((df_attributes & (DF_REF_A | DF_NULL_TRANSFER_0 | DF_NULL_TRANSFER_N)) == 0) { + continue; + } + + // First, mark all object definitions as requiring null check. + if ((df_attributes & (DF_DA | DF_REF_A)) == (DF_DA | DF_REF_A)) { temp_ssa_register_v_->SetBit(mir->ssa_rep->defs[0]); } + // Now, remove mark from all object definitions we know are non-null. + if (df_attributes & DF_NON_NULL_DST) { + // Mark target of NEW* as non-null + temp_ssa_register_v_->ClearBit(mir->ssa_rep->defs[0]); + } + // Mark non-null returns from invoke-style NEW* if (df_attributes & DF_NON_NULL_RET) { MIR* next_mir = mir->next; @@ -691,7 +729,7 @@ bool MIRGraph::EliminateNullChecks(struct BasicBlock* bb) { if (next_mir && next_mir->dalvikInsn.opcode == Instruction::MOVE_RESULT_OBJECT) { // Mark as null checked - temp_ssa_register_v_->SetBit(next_mir->ssa_rep->defs[0]); + temp_ssa_register_v_->ClearBit(next_mir->ssa_rep->defs[0]); } else { if (next_mir) { LOG(WARNING) << "Unexpected opcode following new: " << next_mir->dalvikInsn.opcode; @@ -706,7 +744,7 @@ bool MIRGraph::EliminateNullChecks(struct BasicBlock* bb) { // First non-pseudo should be MOVE_RESULT_OBJECT if (tmir->dalvikInsn.opcode == Instruction::MOVE_RESULT_OBJECT) { // Mark as null checked - temp_ssa_register_v_->SetBit(tmir->ssa_rep->defs[0]); + temp_ssa_register_v_->ClearBit(tmir->ssa_rep->defs[0]); } else { LOG(WARNING) << "Unexpected op after new: " << tmir->dalvikInsn.opcode; } @@ -719,40 +757,22 @@ bool MIRGraph::EliminateNullChecks(struct BasicBlock* bb) { /* * Propagate nullcheck state on register copies (including * Phi pseudo copies. For the latter, nullcheck state is - * the "and" of all the Phi's operands. + * the "or" of all the Phi's operands. */ if (df_attributes & (DF_NULL_TRANSFER_0 | DF_NULL_TRANSFER_N)) { int tgt_sreg = mir->ssa_rep->defs[0]; int operands = (df_attributes & DF_NULL_TRANSFER_0) ? 1 : mir->ssa_rep->num_uses; - bool null_checked = true; + bool needs_null_check = false; for (int i = 0; i < operands; i++) { - null_checked &= temp_ssa_register_v_->IsBitSet(mir->ssa_rep->uses[i]); + needs_null_check |= temp_ssa_register_v_->IsBitSet(mir->ssa_rep->uses[i]); } - if (null_checked) { + if (needs_null_check) { temp_ssa_register_v_->SetBit(tgt_sreg); - } - } - - // Already nullchecked? - if ((df_attributes & DF_HAS_NULL_CHKS) && !(mir->optimization_flags & MIR_IGNORE_NULL_CHECK)) { - int src_idx; - if (df_attributes & DF_NULL_CHK_1) { - src_idx = 1; - } else if (df_attributes & DF_NULL_CHK_2) { - src_idx = 2; } else { - src_idx = 0; + temp_ssa_register_v_->ClearBit(tgt_sreg); } - int src_sreg = mir->ssa_rep->uses[src_idx]; - if (temp_ssa_register_v_->IsBitSet(src_sreg)) { - // Eliminate the null check - mir->optimization_flags |= MIR_IGNORE_NULL_CHECK; - } else { - // Mark s_reg as null-checked - temp_ssa_register_v_->SetBit(src_sreg); - } - } + } } // Did anything change? diff --git a/compiler/dex/quick/codegen_util.cc b/compiler/dex/quick/codegen_util.cc index 2ce8f581b4b..cc185f54eeb 100644 --- a/compiler/dex/quick/codegen_util.cc +++ b/compiler/dex/quick/codegen_util.cc @@ -164,7 +164,8 @@ void Mir2Lir::DumpLIRInsn(LIR* lir, unsigned char* base_addr) { lir->operands[0] = WrapPointer(ArenaStrdup("No instruction string")); } LOG(INFO) << "-------- dalvik offset: 0x" << std::hex - << lir->dalvik_offset << " @ " << reinterpret_cast(lir->operands[0]); + << lir->dalvik_offset << " @ " + << reinterpret_cast(UnwrapPointer(lir->operands[0])); break; case kPseudoExitBlock: LOG(INFO) << "-------- exit offset: 0x" << std::hex << dest; diff --git a/compiler/dex/ssa_transformation.cc b/compiler/dex/ssa_transformation.cc index eb0d412bae3..c86a788470a 100644 --- a/compiler/dex/ssa_transformation.cc +++ b/compiler/dex/ssa_transformation.cc @@ -206,6 +206,7 @@ void MIRGraph::ComputeDomPostOrderTraversal(BasicBlock* bb) { /* hacky loop detection */ if ((curr_bb->taken != NullBasicBlockId) && curr_bb->dominators->IsBitSet(curr_bb->taken)) { + curr_bb->nesting_depth++; attributes_ |= METHOD_HAS_LOOP; } } From 413e89f277ec6ba1bdf2040f5b5611f29a27a447 Mon Sep 17 00:00:00 2001 From: Brian Carlstrom Date: Mon, 21 Oct 2013 23:53:49 -0700 Subject: [PATCH 0101/2402] Refactor ArenaBitVector to create more general BitVector Change-Id: Ib26f2884de9ce7d620048bdf5ed6dec639622e41 --- build/Android.gtest.mk | 1 + compiler/Android.mk | 2 + compiler/dex/arena_bit_vector.cc | 124 ++++------------------- compiler/dex/arena_bit_vector.h | 99 ++---------------- compiler/dex/ssa_transformation.cc | 4 +- compiler/utils/allocator.cc | 74 ++++++++++++++ compiler/utils/allocator.h | 41 ++++++++ compiler/utils/bit_vector.cc | 155 +++++++++++++++++++++++++++++ compiler/utils/bit_vector.h | 134 +++++++++++++++++++++++++ compiler/utils/bit_vector_test.cc | 96 ++++++++++++++++++ compiler/utils/dedupe_set_test.cc | 6 +- 11 files changed, 532 insertions(+), 204 deletions(-) create mode 100644 compiler/utils/allocator.cc create mode 100644 compiler/utils/allocator.h create mode 100644 compiler/utils/bit_vector.cc create mode 100644 compiler/utils/bit_vector.h create mode 100644 compiler/utils/bit_vector_test.cc diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk index 4c658a2bb79..22abba170bd 100644 --- a/build/Android.gtest.mk +++ b/build/Android.gtest.mk @@ -23,6 +23,7 @@ TEST_COMMON_SRC_FILES := \ compiler/jni/jni_compiler_test.cc \ compiler/oat_test.cc \ compiler/output_stream_test.cc \ + compiler/utils/bit_vector_test.cc \ compiler/utils/dedupe_set_test.cc \ compiler/utils/arm/managed_register_arm_test.cc \ compiler/utils/x86/managed_register_x86_test.cc \ diff --git a/compiler/Android.mk b/compiler/Android.mk index fc2f02b59e7..0d3acbdb716 100644 --- a/compiler/Android.mk +++ b/compiler/Android.mk @@ -79,6 +79,8 @@ LIBART_COMPILER_SRC_FILES := \ utils/arm/assembler_arm.cc \ utils/arm/managed_register_arm.cc \ utils/assembler.cc \ + utils/allocator.cc \ + utils/bit_vector.cc \ utils/mips/assembler_mips.cc \ utils/mips/managed_register_mips.cc \ utils/x86/assembler_x86.cc \ diff --git a/compiler/dex/arena_bit_vector.cc b/compiler/dex/arena_bit_vector.cc index b921f615b6e..b567ae8d8a0 100644 --- a/compiler/dex/arena_bit_vector.cc +++ b/compiler/dex/arena_bit_vector.cc @@ -19,119 +19,29 @@ namespace art { -// TODO: profile to make sure this is still a win relative to just using shifted masks. -static uint32_t check_masks[32] = { - 0x00000001, 0x00000002, 0x00000004, 0x00000008, 0x00000010, - 0x00000020, 0x00000040, 0x00000080, 0x00000100, 0x00000200, - 0x00000400, 0x00000800, 0x00001000, 0x00002000, 0x00004000, - 0x00008000, 0x00010000, 0x00020000, 0x00040000, 0x00080000, - 0x00100000, 0x00200000, 0x00400000, 0x00800000, 0x01000000, - 0x02000000, 0x04000000, 0x08000000, 0x10000000, 0x20000000, - 0x40000000, 0x80000000 }; +class ArenaBitVectorAllocator : public Allocator { + public: + explicit ArenaBitVectorAllocator(ArenaAllocator* arena) : arena_(arena) {} + ~ArenaBitVectorAllocator() {} -ArenaBitVector::ArenaBitVector(ArenaAllocator* arena, unsigned int start_bits, - bool expandable, OatBitMapKind kind) - : arena_(arena), - expandable_(expandable), - kind_(kind), - storage_size_((start_bits + 31) >> 5), - storage_(static_cast(arena_->Alloc(storage_size_ * sizeof(uint32_t), - ArenaAllocator::kAllocGrowableBitMap))) { - DCHECK_EQ(sizeof(storage_[0]), 4U); // Assuming 32-bit units. -} - -/* - * Determine whether or not the specified bit is set. - */ -bool ArenaBitVector::IsBitSet(unsigned int num) { - DCHECK_LT(num, storage_size_ * sizeof(uint32_t) * 8); - - unsigned int val = storage_[num >> 5] & check_masks[num & 0x1f]; - return (val != 0); -} - -// Mark all bits bit as "clear". -void ArenaBitVector::ClearAllBits() { - memset(storage_, 0, storage_size_ * sizeof(uint32_t)); -} - -// Mark the specified bit as "set". -/* - * TUNING: this could have pathologically bad growth/expand behavior. Make sure we're - * not using it badly or change resize mechanism. - */ -void ArenaBitVector::SetBit(unsigned int num) { - if (num >= storage_size_ * sizeof(uint32_t) * 8) { - DCHECK(expandable_) << "Attempted to expand a non-expandable bitmap to position " << num; - - /* Round up to word boundaries for "num+1" bits */ - unsigned int new_size = (num + 1 + 31) >> 5; - DCHECK_GT(new_size, storage_size_); - uint32_t *new_storage = - static_cast(arena_->Alloc(new_size * sizeof(uint32_t), - ArenaAllocator::kAllocGrowableBitMap)); - memcpy(new_storage, storage_, storage_size_ * sizeof(uint32_t)); - // Zero out the new storage words. - memset(&new_storage[storage_size_], 0, (new_size - storage_size_) * sizeof(uint32_t)); - // TOTO: collect stats on space wasted because of resize. - storage_ = new_storage; - storage_size_ = new_size; + virtual void* Alloc(size_t size) { + return arena_->Alloc(size, ArenaAllocator::kAllocGrowableBitMap); } - storage_[num >> 5] |= check_masks[num & 0x1f]; -} - -// Mark the specified bit as "unset". -void ArenaBitVector::ClearBit(unsigned int num) { - DCHECK_LT(num, storage_size_ * sizeof(uint32_t) * 8); - storage_[num >> 5] &= ~check_masks[num & 0x1f]; -} + virtual void Free(void*) {} // Nop. -// Intersect with another bit vector. Sizes and expandability must be the same. -void ArenaBitVector::Intersect(const ArenaBitVector* src) { - DCHECK_EQ(storage_size_, src->GetStorageSize()); - DCHECK_EQ(expandable_, src->IsExpandable()); - for (unsigned int idx = 0; idx < storage_size_; idx++) { - storage_[idx] &= src->GetRawStorageWord(idx); + static void* operator new(size_t size, ArenaAllocator* arena) { + return arena->Alloc(sizeof(ArenaBitVectorAllocator), ArenaAllocator::kAllocGrowableBitMap); } -} + static void operator delete(void* p) {} // Nop. -/* - * Union with another bit vector. Sizes and expandability must be the same. - */ -void ArenaBitVector::Union(const ArenaBitVector* src) { - DCHECK_EQ(storage_size_, src->GetStorageSize()); - DCHECK_EQ(expandable_, src->IsExpandable()); - for (unsigned int idx = 0; idx < storage_size_; idx++) { - storage_[idx] |= src->GetRawStorageWord(idx); - } -} - -// Count the number of bits that are set. -int ArenaBitVector::NumSetBits() { - unsigned int count = 0; - - for (unsigned int word = 0; word < storage_size_; word++) { - count += __builtin_popcount(storage_[word]); - } - return count; -} + private: + ArenaAllocator* arena_; + DISALLOW_COPY_AND_ASSIGN(ArenaBitVectorAllocator); +}; -/* - * Mark specified number of bits as "set". Cannot set all bits like ClearAll - * since there might be unused bits - setting those to one will confuse the - * iterator. - */ -void ArenaBitVector::SetInitialBits(unsigned int num_bits) { - DCHECK_LE(((num_bits + 31) >> 5), storage_size_); - unsigned int idx; - for (idx = 0; idx < (num_bits >> 5); idx++) { - storage_[idx] = -1; - } - unsigned int rem_num_bits = num_bits & 0x1f; - if (rem_num_bits) { - storage_[idx] = (1 << rem_num_bits) - 1; - } -} +ArenaBitVector::ArenaBitVector(ArenaAllocator* arena, unsigned int start_bits, + bool expandable, OatBitMapKind kind) + : BitVector(start_bits, expandable, new (arena) ArenaBitVectorAllocator(arena)), kind_(kind) {} } // namespace art diff --git a/compiler/dex/arena_bit_vector.h b/compiler/dex/arena_bit_vector.h index 53e6eb65124..7d2f3ffa5ed 100644 --- a/compiler/dex/arena_bit_vector.h +++ b/compiler/dex/arena_bit_vector.h @@ -17,109 +17,28 @@ #ifndef ART_COMPILER_DEX_ARENA_BIT_VECTOR_H_ #define ART_COMPILER_DEX_ARENA_BIT_VECTOR_H_ -#include -#include -#include "compiler_enums.h" #include "arena_allocator.h" +#include "compiler_enums.h" +#include "utils/bit_vector.h" namespace art { /* - * Expanding bitmap, used for tracking resources. Bits are numbered starting - * from zero. All operations on a BitVector are unsynchronized. + * A BitVector implementation that uses Arena allocation. */ -class ArenaBitVector { +class ArenaBitVector : public BitVector { public: - class Iterator { - public: - explicit Iterator(ArenaBitVector* bit_vector) - : p_bits_(bit_vector), - bit_storage_(bit_vector->GetRawStorage()), - bit_index_(0), - bit_size_(p_bits_->storage_size_ * sizeof(uint32_t) * 8) {} - - // Return the position of the next set bit. -1 means end-of-element reached. - int32_t Next() { - // Did anything obviously change since we started? - DCHECK_EQ(bit_size_, p_bits_->GetStorageSize() * sizeof(uint32_t) * 8); - DCHECK_EQ(bit_storage_, p_bits_->GetRawStorage()); - - if (UNLIKELY(bit_index_ >= bit_size_)) return -1; - - uint32_t word_index = bit_index_ / 32; - uint32_t word = bit_storage_[word_index]; - // Mask out any bits in the first word we've already considered. - word >>= bit_index_ & 0x1f; - if (word == 0) { - bit_index_ &= ~0x1f; - do { - word_index++; - if (UNLIKELY((word_index * 32) >= bit_size_)) { - bit_index_ = bit_size_; - return -1; - } - word = bit_storage_[word_index]; - bit_index_ += 32; - } while (word == 0); - } - bit_index_ += CTZ(word) + 1; - return bit_index_ - 1; - } - - static void* operator new(size_t size, ArenaAllocator* arena) { - return arena->Alloc(sizeof(ArenaBitVector::Iterator), - ArenaAllocator::kAllocGrowableBitMap); - }; - static void operator delete(void* p) {} // Nop. - - private: - ArenaBitVector* const p_bits_; - uint32_t* const bit_storage_; - uint32_t bit_index_; // Current index (size in bits). - const uint32_t bit_size_; // Size of vector in bits. - }; - ArenaBitVector(ArenaAllocator* arena, uint32_t start_bits, bool expandable, OatBitMapKind kind = kBitMapMisc); ~ArenaBitVector() {} - static void* operator new(size_t size, ArenaAllocator* arena) { - return arena->Alloc(sizeof(ArenaBitVector), ArenaAllocator::kAllocGrowableBitMap); - } - static void operator delete(void* p) {} // Nop. - - void SetBit(uint32_t num); - void ClearBit(uint32_t num); - void MarkAllBits(bool set); - void DebugBitVector(char* msg, int length); - bool IsBitSet(uint32_t num); - void ClearAllBits(); - void SetInitialBits(uint32_t num_bits); - void Copy(ArenaBitVector* src) { - memcpy(storage_, src->GetRawStorage(), sizeof(uint32_t) * storage_size_); - } - void Intersect(const ArenaBitVector* src2); - void Union(const ArenaBitVector* src); - // Are we equal to another bit vector? Note: expandability attributes must also match. - bool Equal(const ArenaBitVector* src) { - return (storage_size_ == src->GetStorageSize()) && - (expandable_ == src->IsExpandable()) && - (memcmp(storage_, src->GetRawStorage(), storage_size_ * 4) == 0); - } - int32_t NumSetBits(); - - uint32_t GetStorageSize() const { return storage_size_; } - bool IsExpandable() const { return expandable_; } - uint32_t GetRawStorageWord(size_t idx) const { return storage_[idx]; } - uint32_t* GetRawStorage() { return storage_; } - const uint32_t* GetRawStorage() const { return storage_; } + static void* operator new(size_t size, ArenaAllocator* arena) { + return arena->Alloc(sizeof(ArenaBitVector), ArenaAllocator::kAllocGrowableBitMap); + } + static void operator delete(void* p) {} // Nop. private: - ArenaAllocator* const arena_; - const bool expandable_; // expand bitmap if we run out? - const OatBitMapKind kind_; // for memory use tuning. - uint32_t storage_size_; // current size, in 32-bit words. - uint32_t* storage_; + const OatBitMapKind kind_; // for memory use tuning. TODO: currently unused. }; diff --git a/compiler/dex/ssa_transformation.cc b/compiler/dex/ssa_transformation.cc index eb0d412bae3..b6c892212cd 100644 --- a/compiler/dex/ssa_transformation.cc +++ b/compiler/dex/ssa_transformation.cc @@ -183,7 +183,7 @@ void MIRGraph::ComputeDomPostOrderTraversal(BasicBlock* bb) { ClearAllVisitedFlags(); std::vector > work_stack; bb->visited = true; - work_stack.push_back(std::make_pair(bb, new (arena_) ArenaBitVector::Iterator(bb->i_dominated))); + work_stack.push_back(std::make_pair(bb, bb->i_dominated->GetIterator())); while (!work_stack.empty()) { const std::pair& curr = work_stack.back(); BasicBlock* curr_bb = curr.first; @@ -196,7 +196,7 @@ void MIRGraph::ComputeDomPostOrderTraversal(BasicBlock* bb) { BasicBlock* new_bb = GetBasicBlock(bb_idx); new_bb->visited = true; work_stack.push_back( - std::make_pair(new_bb, new (arena_) ArenaBitVector::Iterator(new_bb->i_dominated))); + std::make_pair(new_bb, new_bb->i_dominated->GetIterator())); } else { // no successor/next if (curr_bb->id != NullBasicBlockId) { diff --git a/compiler/utils/allocator.cc b/compiler/utils/allocator.cc new file mode 100644 index 00000000000..4f7753d4761 --- /dev/null +++ b/compiler/utils/allocator.cc @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2013 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 "allocator.h" + +#include +#include + +#include "base/logging.h" + +namespace art { + +class MallocAllocator : public Allocator { + public: + explicit MallocAllocator() {} + ~MallocAllocator() {} + + virtual void* Alloc(size_t size) { + return calloc(sizeof(uint8_t), size); + } + + virtual void Free(void* p) { + free(p); + } + + private: + DISALLOW_COPY_AND_ASSIGN(MallocAllocator); +}; + +MallocAllocator g_malloc_allocator; + +class NoopAllocator : public Allocator { + public: + explicit NoopAllocator() {} + ~NoopAllocator() {} + + virtual void* Alloc(size_t size) { + LOG(FATAL) << "NoopAllocator::Alloc should not be called"; + return NULL; + } + + virtual void Free(void* p) { + // Noop. + } + + private: + DISALLOW_COPY_AND_ASSIGN(NoopAllocator); +}; + +NoopAllocator g_noop_allocator; + +Allocator* Allocator::GetMallocAllocator() { + return &g_malloc_allocator; +} + +Allocator* Allocator::GetNoopAllocator() { + return &g_noop_allocator; +} + + +} // namespace art diff --git a/compiler/utils/allocator.h b/compiler/utils/allocator.h new file mode 100644 index 00000000000..3482a7928ea --- /dev/null +++ b/compiler/utils/allocator.h @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ART_COMPILER_UTILS_ALLOCATOR_H_ +#define ART_COMPILER_UTILS_ALLOCATOR_H_ + +#include "base/macros.h" + +namespace art { + +class Allocator { + public: + static Allocator* GetMallocAllocator(); + static Allocator* GetNoopAllocator(); + + Allocator() {} + virtual ~Allocator() {} + + virtual void* Alloc(size_t) = 0; + virtual void Free(void*) = 0; + + private: + DISALLOW_COPY_AND_ASSIGN(Allocator); +}; + +} // namespace art + +#endif // ART_COMPILER_UTILS_ALLOCATOR_H_ diff --git a/compiler/utils/bit_vector.cc b/compiler/utils/bit_vector.cc new file mode 100644 index 00000000000..81a639a0501 --- /dev/null +++ b/compiler/utils/bit_vector.cc @@ -0,0 +1,155 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "bit_vector.h" + +namespace art { + +// TODO: profile to make sure this is still a win relative to just using shifted masks. +static uint32_t check_masks[32] = { + 0x00000001, 0x00000002, 0x00000004, 0x00000008, 0x00000010, + 0x00000020, 0x00000040, 0x00000080, 0x00000100, 0x00000200, + 0x00000400, 0x00000800, 0x00001000, 0x00002000, 0x00004000, + 0x00008000, 0x00010000, 0x00020000, 0x00040000, 0x00080000, + 0x00100000, 0x00200000, 0x00400000, 0x00800000, 0x01000000, + 0x02000000, 0x04000000, 0x08000000, 0x10000000, 0x20000000, + 0x40000000, 0x80000000 }; + +static inline uint32_t BitsToWords(unsigned int bits) { + return (bits + 31) >> 5; +} + +// TODO: replace excessive argument defaulting when we are at gcc 4.7 +// or later on host with delegating constructor support. Specifically, +// starts_bits and storage_size/storage are mutually exclusive. +BitVector::BitVector(unsigned int start_bits, + bool expandable, + Allocator* allocator, + uint32_t storage_size, + uint32_t* storage) + : allocator_(allocator), + expandable_(expandable), + storage_size_(storage_size), + storage_(storage) { + DCHECK_EQ(sizeof(storage_[0]), 4U); // Assuming 32-bit units. + if (storage_ == NULL) { + storage_size_ = BitsToWords(start_bits); + storage_ = static_cast(allocator_->Alloc(storage_size_ * sizeof(uint32_t))); + } +} + +BitVector::~BitVector() { + allocator_->Free(storage_); +} + +/* + * Determine whether or not the specified bit is set. + */ +bool BitVector::IsBitSet(unsigned int num) { + DCHECK_LT(num, storage_size_ * sizeof(uint32_t) * 8); + + unsigned int val = storage_[num >> 5] & check_masks[num & 0x1f]; + return (val != 0); +} + +// Mark all bits bit as "clear". +void BitVector::ClearAllBits() { + memset(storage_, 0, storage_size_ * sizeof(uint32_t)); +} + +// Mark the specified bit as "set". +/* + * TUNING: this could have pathologically bad growth/expand behavior. Make sure we're + * not using it badly or change resize mechanism. + */ +void BitVector::SetBit(unsigned int num) { + if (num >= storage_size_ * sizeof(uint32_t) * 8) { + DCHECK(expandable_) << "Attempted to expand a non-expandable bitmap to position " << num; + + /* Round up to word boundaries for "num+1" bits */ + unsigned int new_size = BitsToWords(num + 1); + DCHECK_GT(new_size, storage_size_); + uint32_t *new_storage = + static_cast(allocator_->Alloc(new_size * sizeof(uint32_t))); + memcpy(new_storage, storage_, storage_size_ * sizeof(uint32_t)); + // Zero out the new storage words. + memset(&new_storage[storage_size_], 0, (new_size - storage_size_) * sizeof(uint32_t)); + // TOTO: collect stats on space wasted because of resize. + storage_ = new_storage; + storage_size_ = new_size; + } + + storage_[num >> 5] |= check_masks[num & 0x1f]; +} + +// Mark the specified bit as "unset". +void BitVector::ClearBit(unsigned int num) { + DCHECK_LT(num, storage_size_ * sizeof(uint32_t) * 8); + storage_[num >> 5] &= ~check_masks[num & 0x1f]; +} + +// Intersect with another bit vector. Sizes and expandability must be the same. +void BitVector::Intersect(const BitVector* src) { + DCHECK_EQ(storage_size_, src->GetStorageSize()); + DCHECK_EQ(expandable_, src->IsExpandable()); + for (unsigned int idx = 0; idx < storage_size_; idx++) { + storage_[idx] &= src->GetRawStorageWord(idx); + } +} + +/* + * Union with another bit vector. Sizes and expandability must be the same. + */ +void BitVector::Union(const BitVector* src) { + DCHECK_EQ(storage_size_, src->GetStorageSize()); + DCHECK_EQ(expandable_, src->IsExpandable()); + for (unsigned int idx = 0; idx < storage_size_; idx++) { + storage_[idx] |= src->GetRawStorageWord(idx); + } +} + +// Count the number of bits that are set. +int BitVector::NumSetBits() { + unsigned int count = 0; + + for (unsigned int word = 0; word < storage_size_; word++) { + count += __builtin_popcount(storage_[word]); + } + return count; +} + +BitVector::Iterator* BitVector::GetIterator() { + return new (allocator_) Iterator(this); +} + +/* + * Mark specified number of bits as "set". Cannot set all bits like ClearAll + * since there might be unused bits - setting those to one will confuse the + * iterator. + */ +void BitVector::SetInitialBits(unsigned int num_bits) { + DCHECK_LE(BitsToWords(num_bits), storage_size_); + unsigned int idx; + for (idx = 0; idx < (num_bits >> 5); idx++) { + storage_[idx] = -1; + } + unsigned int rem_num_bits = num_bits & 0x1f; + if (rem_num_bits) { + storage_[idx] = (1 << rem_num_bits) - 1; + } +} + +} // namespace art diff --git a/compiler/utils/bit_vector.h b/compiler/utils/bit_vector.h new file mode 100644 index 00000000000..bf0f7c32e1f --- /dev/null +++ b/compiler/utils/bit_vector.h @@ -0,0 +1,134 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ART_COMPILER_UTILS_BIT_VECTOR_H_ +#define ART_COMPILER_UTILS_BIT_VECTOR_H_ + +#include +#include + +#include "allocator.h" +#include "base/logging.h" +#include "utils.h" + +namespace art { + +/* + * Expanding bitmap, used for tracking resources. Bits are numbered starting + * from zero. All operations on a BitVector are unsynchronized. + */ +class BitVector { + public: + class Iterator { + public: + explicit Iterator(BitVector* bit_vector) + : p_bits_(bit_vector), + bit_storage_(bit_vector->GetRawStorage()), + bit_index_(0), + bit_size_(p_bits_->storage_size_ * sizeof(uint32_t) * 8) {} + + // Return the position of the next set bit. -1 means end-of-element reached. + int32_t Next() { + // Did anything obviously change since we started? + DCHECK_EQ(bit_size_, p_bits_->GetStorageSize() * sizeof(uint32_t) * 8); + DCHECK_EQ(bit_storage_, p_bits_->GetRawStorage()); + + if (UNLIKELY(bit_index_ >= bit_size_)) return -1; + + uint32_t word_index = bit_index_ / 32; + uint32_t word = bit_storage_[word_index]; + // Mask out any bits in the first word we've already considered. + word >>= bit_index_ & 0x1f; + if (word == 0) { + bit_index_ &= ~0x1f; + do { + word_index++; + if (UNLIKELY((word_index * 32) >= bit_size_)) { + bit_index_ = bit_size_; + return -1; + } + word = bit_storage_[word_index]; + bit_index_ += 32; + } while (word == 0); + } + bit_index_ += CTZ(word) + 1; + return bit_index_ - 1; + } + + static void* operator new(size_t size, Allocator* allocator) { + return allocator->Alloc(sizeof(BitVector::Iterator)); + }; + static void operator delete(void* p) { + Iterator* it = reinterpret_cast(p); + it->p_bits_->allocator_->Free(p); + } + + private: + BitVector* const p_bits_; + uint32_t* const bit_storage_; + uint32_t bit_index_; // Current index (size in bits). + const uint32_t bit_size_; // Size of vector in bits. + + friend class BitVector; + }; + + BitVector(uint32_t start_bits, + bool expandable, + Allocator* allocator, + uint32_t storage_size = 0, + uint32_t* storage = NULL); + + virtual ~BitVector(); + + void SetBit(uint32_t num); + void ClearBit(uint32_t num); + void MarkAllBits(bool set); + void DebugBitVector(char* msg, int length); + bool IsBitSet(uint32_t num); + void ClearAllBits(); + void SetInitialBits(uint32_t num_bits); + void Copy(BitVector* src) { + memcpy(storage_, src->GetRawStorage(), sizeof(uint32_t) * storage_size_); + } + void Intersect(const BitVector* src2); + void Union(const BitVector* src); + // Are we equal to another bit vector? Note: expandability attributes must also match. + bool Equal(const BitVector* src) { + return (storage_size_ == src->GetStorageSize()) && + (expandable_ == src->IsExpandable()) && + (memcmp(storage_, src->GetRawStorage(), storage_size_ * sizeof(uint32_t)) == 0); + } + int32_t NumSetBits(); + + Iterator* GetIterator(); + + uint32_t GetStorageSize() const { return storage_size_; } + bool IsExpandable() const { return expandable_; } + uint32_t GetRawStorageWord(size_t idx) const { return storage_[idx]; } + uint32_t* GetRawStorage() { return storage_; } + const uint32_t* GetRawStorage() const { return storage_; } + + private: + Allocator* const allocator_; + const bool expandable_; // expand bitmap if we run out? + uint32_t storage_size_; // current size, in 32-bit words. + uint32_t* storage_; +}; + + +} // namespace art + +#endif // ART_COMPILER_UTILS_BIT_VECTOR_H_ diff --git a/compiler/utils/bit_vector_test.cc b/compiler/utils/bit_vector_test.cc new file mode 100644 index 00000000000..5c18ec53d3e --- /dev/null +++ b/compiler/utils/bit_vector_test.cc @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2013 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 "common_test.h" +#include "bit_vector.h" +#include "UniquePtr.h" + +namespace art { + +TEST(BitVector, Test) { + const size_t kBits = 32; + + BitVector bv(kBits, false, Allocator::GetMallocAllocator()); + EXPECT_EQ(1U, bv.GetStorageSize()); + EXPECT_FALSE(bv.IsExpandable()); + + EXPECT_EQ(0, bv.NumSetBits()); + for (size_t i = 0; i < kBits; i++) { + EXPECT_FALSE(bv.IsBitSet(i)); + } + EXPECT_EQ(0U, bv.GetRawStorageWord(0)); + EXPECT_EQ(0U, *bv.GetRawStorage()); + + BitVector::Iterator empty_iterator(&bv); + EXPECT_EQ(-1, empty_iterator.Next()); + + UniquePtr empty_iterator_on_heap(bv.GetIterator()); + EXPECT_EQ(-1, empty_iterator_on_heap->Next()); + + bv.SetBit(0); + bv.SetBit(kBits - 1); + EXPECT_EQ(2, bv.NumSetBits()); + EXPECT_TRUE(bv.IsBitSet(0)); + for (size_t i = 1; i < kBits - 1; i++) { + EXPECT_FALSE(bv.IsBitSet(i)); + } + EXPECT_TRUE(bv.IsBitSet(kBits - 1)); + EXPECT_EQ(0x80000001U, bv.GetRawStorageWord(0)); + EXPECT_EQ(0x80000001U, *bv.GetRawStorage()); + + BitVector::Iterator iterator(&bv); + EXPECT_EQ(0, iterator.Next()); + EXPECT_EQ(static_cast(kBits - 1), iterator.Next()); + EXPECT_EQ(-1, iterator.Next()); +} + +TEST(BitVector, NoopAllocator) { + const uint32_t kWords = 2; + + uint32_t bits[kWords]; + memset(bits, 0, sizeof(bits)); + + BitVector bv(0U, false, Allocator::GetNoopAllocator(), kWords, bits); + EXPECT_EQ(kWords, bv.GetStorageSize()); + EXPECT_EQ(bits, bv.GetRawStorage()); + EXPECT_EQ(0, bv.NumSetBits()); + + bv.SetBit(8); + EXPECT_EQ(1, bv.NumSetBits()); + EXPECT_EQ(0x00000100U, bv.GetRawStorageWord(0)); + EXPECT_EQ(0x00000000U, bv.GetRawStorageWord(1)); + EXPECT_EQ(1, bv.NumSetBits()); + + bv.SetBit(16); + EXPECT_EQ(2, bv.NumSetBits()); + EXPECT_EQ(0x00010100U, bv.GetRawStorageWord(0)); + EXPECT_EQ(0x00000000U, bv.GetRawStorageWord(1)); + EXPECT_EQ(2, bv.NumSetBits()); + + bv.SetBit(32); + EXPECT_EQ(3, bv.NumSetBits()); + EXPECT_EQ(0x00010100U, bv.GetRawStorageWord(0)); + EXPECT_EQ(0x00000001U, bv.GetRawStorageWord(1)); + EXPECT_EQ(3, bv.NumSetBits()); + + bv.SetBit(48); + EXPECT_EQ(4, bv.NumSetBits()); + EXPECT_EQ(0x00010100U, bv.GetRawStorageWord(0)); + EXPECT_EQ(0x00010001U, bv.GetRawStorageWord(1)); + EXPECT_EQ(4, bv.NumSetBits()); +} + +} // namespace art diff --git a/compiler/utils/dedupe_set_test.cc b/compiler/utils/dedupe_set_test.cc index 03d8b961fa5..2c6787b8a6e 100644 --- a/compiler/utils/dedupe_set_test.cc +++ b/compiler/utils/dedupe_set_test.cc @@ -19,10 +19,6 @@ namespace art { -class DedupeSetTest : public testing::Test { - public: -}; - class DedupeHashFunc { public: size_t operator()(const std::vector& array) const { @@ -35,7 +31,7 @@ class DedupeHashFunc { return hash; } }; -TEST_F(DedupeSetTest, Test) { +TEST(DedupeSetTest, Test) { Thread* self = Thread::Current(); typedef std::vector ByteArray; DedupeSet deduplicator("test"); From e3a21740ec7056d52a90620d988bbb72eca0997f Mon Sep 17 00:00:00 2001 From: Nick Kralevich Date: Wed, 23 Oct 2013 17:21:05 -0700 Subject: [PATCH 0102/2402] Add -Wl,--no-fatal-warnings to x86 build libart.so has text relocations. When -Wl,--fatal-warnings is added to the global LDFLAGS, these text relocations will become a compile error. This is a short term workaround until the text relocations can be fixed properly. Bug: 11358761 Bug: 11353056 (cherry picked from commit 59fe2a68b94daa4d215d69facec1b3079846df9e) Change-Id: Id604bdec5d4fdf2606b9970cf65b6a358b9a673a --- runtime/Android.mk | 3 +++ 1 file changed, 3 insertions(+) diff --git a/runtime/Android.mk b/runtime/Android.mk index a0ae4bffbc9..459ca0e8a74 100644 --- a/runtime/Android.mk +++ b/runtime/Android.mk @@ -178,6 +178,7 @@ LIBART_TARGET_SRC_FILES := \ runtime_android.cc \ thread_android.cc +LIBART_LDFLAGS := ifeq ($(TARGET_ARCH),arm) LIBART_TARGET_SRC_FILES += \ arch/arm/context_arm.cc.arm \ @@ -195,6 +196,7 @@ LIBART_TARGET_SRC_FILES += \ arch/x86/portable_entrypoints_x86.S \ arch/x86/quick_entrypoints_x86.S \ arch/x86/thread_x86.cc +LIBART_LDFLAGS += -Wl,--no-fatal-warnings else # TARGET_ARCH != x86 ifeq ($(TARGET_ARCH),mips) LIBART_TARGET_SRC_FILES += \ @@ -304,6 +306,7 @@ $$(ENUM_OPERATOR_OUT_GEN): $$(GENERATED_SRC_DIR)/%_operator_out.cc : $(LOCAL_PAT LOCAL_GENERATED_SOURCES += $$(ENUM_OPERATOR_OUT_GEN) LOCAL_CFLAGS := $(LIBART_CFLAGS) + LOCAL_LDFLAGS := $(LIBART_LDFLAGS) ifeq ($$(art_target_or_host),target) LOCAL_CLANG := $(ART_TARGET_CLANG) LOCAL_CFLAGS += $(ART_TARGET_CFLAGS) From 881392f55e566a7c6407e7d321a31836d2d8865f Mon Sep 17 00:00:00 2001 From: Alex Klyubin Date: Thu, 24 Oct 2013 16:31:56 -0700 Subject: [PATCH 0103/2402] Adjust the name of setTargetSdkVersion native method. Change-Id: I60f1febc1f8ddf2a97925a48ddf232508d5c44f9 --- runtime/native/dalvik_system_VMRuntime.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/runtime/native/dalvik_system_VMRuntime.cc b/runtime/native/dalvik_system_VMRuntime.cc index cb7828b9a4d..83d3acb3397 100644 --- a/runtime/native/dalvik_system_VMRuntime.cc +++ b/runtime/native/dalvik_system_VMRuntime.cc @@ -134,7 +134,7 @@ static jstring VMRuntime_vmLibrary(JNIEnv* env, jobject) { return env->NewStringUTF(kIsDebugBuild ? "libartd.so" : "libart.so"); } -static void VMRuntime_setTargetSdkVersion(JNIEnv* env, jobject, jint targetSdkVersion) { +static void VMRuntime_setTargetSdkVersionNative(JNIEnv* env, jobject, jint targetSdkVersion) { // This is the target SDK version of the app we're about to run. // Note that targetSdkVersion may be CUR_DEVELOPMENT (10000). // Note that targetSdkVersion may be 0, meaning "current". @@ -519,7 +519,7 @@ static JNINativeMethod gMethods[] = { NATIVE_METHOD(VMRuntime, nativeSetTargetHeapUtilization, "(F)V"), NATIVE_METHOD(VMRuntime, newNonMovableArray, "(Ljava/lang/Class;I)Ljava/lang/Object;"), NATIVE_METHOD(VMRuntime, properties, "()[Ljava/lang/String;"), - NATIVE_METHOD(VMRuntime, setTargetSdkVersion, "(I)V"), + NATIVE_METHOD(VMRuntime, setTargetSdkVersionNative, "(I)V"), NATIVE_METHOD(VMRuntime, registerNativeAllocation, "(I)V"), NATIVE_METHOD(VMRuntime, registerNativeFree, "(I)V"), NATIVE_METHOD(VMRuntime, startJitCompilation, "()V"), From a61f49539a59b610e557b5513695295639496750 Mon Sep 17 00:00:00 2001 From: buzbee Date: Fri, 23 Aug 2013 14:27:06 -0700 Subject: [PATCH 0104/2402] Add timing logger to Quick compiler Current Quick compiler breakdown for compiling the boot class path: MIR2LIR: 29.674% MIROpt:SSATransform: 17.656% MIROpt:BBOpt: 11.508% BuildMIRGraph: 7.815% Assemble: 6.898% MIROpt:ConstantProp: 5.151% Cleanup: 4.916% MIROpt:NullCheckElimination: 4.085% RegisterAllocation: 3.972% GcMap: 2.359% Launchpads: 2.147% PcMappingTable: 2.145% MIROpt:CodeLayout: 0.697% LiteralData: 0.654% SpecialMIR2LIR: 0.323% Change-Id: I9f77e825faf79e6f6b214bb42edcc4b36f55d291 --- compiler/dex/compiler_ir.h | 11 +++++++- compiler/dex/frontend.cc | 32 ++++++++++++++++++++++++ compiler/dex/frontend.h | 1 + compiler/dex/quick/arm/assemble_arm.cc | 4 +++ compiler/dex/quick/codegen_util.cc | 2 ++ compiler/dex/quick/mips/assemble_mips.cc | 4 +++ compiler/dex/quick/mir_to_lir.cc | 4 ++- compiler/dex/quick/x86/assemble_x86.cc | 4 +++ 8 files changed, 60 insertions(+), 2 deletions(-) diff --git a/compiler/dex/compiler_ir.h b/compiler/dex/compiler_ir.h index bdc31547cbc..0d7209e438d 100644 --- a/compiler/dex/compiler_ir.h +++ b/compiler/dex/compiler_ir.h @@ -29,6 +29,7 @@ #include "llvm/intrinsic_helper.h" #include "llvm/ir_builder.h" #include "safe_map.h" +#include "base/timing_logger.h" namespace art { @@ -68,7 +69,14 @@ struct CompilationUnit { compiler_flip_match(false), arena(pool), mir_graph(NULL), - cg(NULL) {} + cg(NULL), + timings("QuickCompiler", true, false) { + } + + void StartTimingSplit(const char* label); + void NewTimingSplit(const char* label); + void EndTiming(); + /* * Fields needed/generated by common frontend and generally used throughout * the compiler. @@ -109,6 +117,7 @@ struct CompilationUnit { UniquePtr mir_graph; // MIR container. UniquePtr cg; // Target-specific codegen. + base::TimingLogger timings; }; } // namespace art diff --git a/compiler/dex/frontend.cc b/compiler/dex/frontend.cc index 2952570436a..2f8521f7880 100644 --- a/compiler/dex/frontend.cc +++ b/compiler/dex/frontend.cc @@ -24,6 +24,7 @@ #include "runtime.h" #include "backend.h" #include "base/logging.h" +#include "base/timing_logger.h" #if defined(ART_USE_PORTABLE_COMPILER) #include "dex/portable/mir_to_gbc.h" @@ -104,8 +105,30 @@ static uint32_t kCompilerDebugFlags = 0 | // Enable debug/testing modes // (1 << kDebugVerifyBitcode) | // (1 << kDebugShowSummaryMemoryUsage) | // (1 << kDebugShowFilterStats) | + // (1 << kDebugTimings) | 0; +// TODO: Add a cumulative version of logging, and combine with dex2oat --dump-timing +void CompilationUnit::StartTimingSplit(const char* label) { + if (enable_debug & (1 << kDebugTimings)) { + timings.StartSplit(label); + } +} + +void CompilationUnit::NewTimingSplit(const char* label) { + if (enable_debug & (1 << kDebugTimings)) { + timings.NewSplit(label); + } +} + +void CompilationUnit::EndTiming() { + if (enable_debug & (1 << kDebugTimings)) { + timings.EndSplit(); + LOG(INFO) << "TIMINGS " << PrettyMethod(method_idx, *dex_file); + LOG(INFO) << Dumpable(timings); + } +} + static CompiledMethod* CompileMethod(CompilerDriver& compiler, const CompilerBackend compiler_backend, const DexFile::CodeItem* code_item, @@ -175,6 +198,7 @@ static CompiledMethod* CompileMethod(CompilerDriver& compiler, (1 << kPromoteCompilerTemps)); } + cu.StartTimingSplit("BuildMIRGraph"); cu.mir_graph.reset(new MIRGraph(&cu, &cu.arena)); /* Gathering opcode stats? */ @@ -192,22 +216,28 @@ static CompiledMethod* CompileMethod(CompilerDriver& compiler, } #endif + cu.NewTimingSplit("MIROpt:CodeLayout"); + /* Do a code layout pass */ cu.mir_graph->CodeLayout(); /* Perform SSA transformation for the whole method */ + cu.NewTimingSplit("MIROpt:SSATransform"); cu.mir_graph->SSATransformation(); /* Do constant propagation */ + cu.NewTimingSplit("MIROpt:ConstantProp"); cu.mir_graph->PropagateConstants(); /* Count uses */ cu.mir_graph->MethodUseCount(); /* Perform null check elimination */ + cu.NewTimingSplit("MIROpt:NullCheckElimination"); cu.mir_graph->NullCheckElimination(); /* Combine basic blocks where possible */ + cu.NewTimingSplit("MIROpt:BBOpt"); cu.mir_graph->BasicBlockCombine(); /* Do some basic block optimizations */ @@ -250,6 +280,7 @@ static CompiledMethod* CompileMethod(CompilerDriver& compiler, cu.cg->Materialize(); + cu.NewTimingSplit("Cleanup"); result = cu.cg->GetCompiledMethod(); if (result) { @@ -270,6 +301,7 @@ static CompiledMethod* CompileMethod(CompilerDriver& compiler, << " " << PrettyMethod(method_idx, dex_file); } + cu.EndTiming(); return result; } diff --git a/compiler/dex/frontend.h b/compiler/dex/frontend.h index 6c33d109e31..43f68554b57 100644 --- a/compiler/dex/frontend.h +++ b/compiler/dex/frontend.h @@ -78,6 +78,7 @@ enum debugControlVector { kDebugVerifyBitcode, kDebugShowSummaryMemoryUsage, kDebugShowFilterStats, + kDebugTimings }; class LLVMInfo { diff --git a/compiler/dex/quick/arm/assemble_arm.cc b/compiler/dex/quick/arm/assemble_arm.cc index cc40e99c524..2a6e656901d 100644 --- a/compiler/dex/quick/arm/assemble_arm.cc +++ b/compiler/dex/quick/arm/assemble_arm.cc @@ -1153,6 +1153,7 @@ void ArmMir2Lir::EncodeLIR(LIR* lir) { void ArmMir2Lir::AssembleLIR() { LIR* lir; LIR* prev_lir; + cu_->NewTimingSplit("Assemble"); int assembler_retries = 0; CodeOffset starting_offset = EncodeRange(first_lir_insn_, last_lir_insn_, 0); data_offset_ = (starting_offset + 0x3) & ~0x3; @@ -1574,6 +1575,7 @@ void ArmMir2Lir::AssembleLIR() { data_offset_ = (code_buffer_.size() + 0x3) & ~0x3; + cu_->NewTimingSplit("LiteralData"); // Install literals InstallLiteralPools(); @@ -1584,8 +1586,10 @@ void ArmMir2Lir::AssembleLIR() { InstallFillArrayData(); // Create the mapping table and native offset to reference map. + cu_->NewTimingSplit("PcMappingTable"); CreateMappingTables(); + cu_->NewTimingSplit("GcMap"); CreateNativeGcMap(); } diff --git a/compiler/dex/quick/codegen_util.cc b/compiler/dex/quick/codegen_util.cc index 2ce8f581b4b..a6653fab198 100644 --- a/compiler/dex/quick/codegen_util.cc +++ b/compiler/dex/quick/codegen_util.cc @@ -929,6 +929,7 @@ Mir2Lir::Mir2Lir(CompilationUnit* cu, MIRGraph* mir_graph, ArenaAllocator* arena } void Mir2Lir::Materialize() { + cu_->NewTimingSplit("RegisterAllocation"); CompilerInitializeRegAlloc(); // Needs to happen after SSA naming /* Allocate Registers using simple local allocation scheme */ @@ -940,6 +941,7 @@ void Mir2Lir::Materialize() { * special codegen doesn't succeed, first_lir_insn_ will * set to NULL; */ + cu_->NewTimingSplit("SpecialMIR2LIR"); SpecialMIR2LIR(mir_graph_->GetSpecialCase()); } diff --git a/compiler/dex/quick/mips/assemble_mips.cc b/compiler/dex/quick/mips/assemble_mips.cc index ea8b7a64c19..5f5e5e44ac3 100644 --- a/compiler/dex/quick/mips/assemble_mips.cc +++ b/compiler/dex/quick/mips/assemble_mips.cc @@ -768,6 +768,7 @@ void MipsMir2Lir::AssignOffsets() { * TODO: consolidate w/ Arm assembly mechanism. */ void MipsMir2Lir::AssembleLIR() { + cu_->NewTimingSplit("Assemble"); AssignOffsets(); int assembler_retries = 0; /* @@ -792,6 +793,7 @@ void MipsMir2Lir::AssembleLIR() { } // Install literals + cu_->NewTimingSplit("LiteralData"); InstallLiteralPools(); // Install switch tables @@ -801,8 +803,10 @@ void MipsMir2Lir::AssembleLIR() { InstallFillArrayData(); // Create the mapping table and native offset to reference map. + cu_->NewTimingSplit("PcMappingTable"); CreateMappingTables(); + cu_->NewTimingSplit("GcMap"); CreateNativeGcMap(); } diff --git a/compiler/dex/quick/mir_to_lir.cc b/compiler/dex/quick/mir_to_lir.cc index 197e200fc3b..fa9a3ad566b 100644 --- a/compiler/dex/quick/mir_to_lir.cc +++ b/compiler/dex/quick/mir_to_lir.cc @@ -819,6 +819,8 @@ void Mir2Lir::SpecialMIR2LIR(SpecialCaseHandler special_case) { } void Mir2Lir::MethodMIR2LIR() { + cu_->NewTimingSplit("MIR2LIR"); + // Hold the labels of each block. block_label_list_ = static_cast(arena_->Alloc(sizeof(LIR) * mir_graph_->GetNumBlocks(), @@ -839,7 +841,7 @@ void Mir2Lir::MethodMIR2LIR() { next_bb = iter.Next(); } while ((next_bb != NULL) && (next_bb->block_type == kDead)); } - + cu_->NewTimingSplit("Launchpads"); HandleSuspendLaunchPads(); HandleThrowLaunchPads(); diff --git a/compiler/dex/quick/x86/assemble_x86.cc b/compiler/dex/quick/x86/assemble_x86.cc index fb8e75fc1b3..0e302608d82 100644 --- a/compiler/dex/quick/x86/assemble_x86.cc +++ b/compiler/dex/quick/x86/assemble_x86.cc @@ -1443,6 +1443,7 @@ void X86Mir2Lir::AssignOffsets() { * TODO: consolidate w/ Arm assembly mechanism. */ void X86Mir2Lir::AssembleLIR() { + cu_->NewTimingSplit("Assemble"); AssignOffsets(); int assembler_retries = 0; /* @@ -1466,6 +1467,7 @@ void X86Mir2Lir::AssembleLIR() { } } + cu_->NewTimingSplit("LiteralData"); // Install literals InstallLiteralPools(); @@ -1476,8 +1478,10 @@ void X86Mir2Lir::AssembleLIR() { InstallFillArrayData(); // Create the mapping table and native offset to reference map. + cu_->NewTimingSplit("PcMappingTable"); CreateMappingTables(); + cu_->NewTimingSplit("GcMap"); CreateNativeGcMap(); } From fd7e7f1253927c8d7f17e7cbc259daaf51868bd3 Mon Sep 17 00:00:00 2001 From: Hiroshi Yamauchi Date: Tue, 22 Oct 2013 14:17:48 -0700 Subject: [PATCH 0105/2402] Fix a double unmap issue in MemMap::UnMapAtEnd(). MemMap::UnMapAtEnd() unmaps the unused tail of the alloc space during a zygote fork. But it can cause the same tail region of the memory to be unmapped twice (once in UnMapAtEnd() and once more in ~MemMap() during a shutdown.) I encountered a crash because of this issue in SpaceTest.ZygoteTest (which happens to happen only on a device in a branch with the rosalloc change probably due to some randomness in mmap address choice, etc.) Here's what happens: 1) CreateZygoteSpace() will call UnMapAtEnd() and unmap the unused tail of the alloc space. 2) In the same function, after UnMapAtEnd(), several libc new/malloc allocations, including a new DlMallocSpace object, happen. This happens to cause libc to map a new memory region that overlaps with the memory region that has just been unmapped in 1) and use it to allocate those allocations (that is, the new DlMallocSpace object is allocated in that memory region.) This is a second DlMallocSpace that becomes the new alloc space after zygote fork. The first DlMallocSpace becomes the zygote space. Note that that libc maps that memory region before the underlying memory of the second DlMallocSpace is mapped. 3) During a Runtime shutdown (which happens once for a normal VM shutdown or at the end of each test run) all the spaces get destructed including the the two DlMallocSpaces one by one. When the first DlMallocSpace gets destructed (note the space list is sorted by address,) its super destructor ~MemMap() unmaps the original memory region that's already partially unmapped in 2). Now this memory region includes the libc memory region that includes the second DlMallocSpace object. 4) When the second DlMallocSpace object gets attempted to be destructed, the memory in which the object resides is already unmapped in 3) and causes a SIGSEGV. This change replaces UnMapAtEnd() with a new function RemapAtEnd() which combines the unmapping of the tail region and remapping of it to achieve the following two things: 1) Fixes this double unmap issue by updating the base_size_ member variable to exclude the already-unmapped tail region so that ~MemMap() will not unmap the tail region again. 2) Improves on the non-atomicity issue in the unmap/map sequence in CreateZygoteSpace(). That is, once the unused tail portion of the memory region of the origina alloc space is unmapped, something like libc could come along and take that memory region, before the memory region is mapped again for the new alloc space. This, as a result, would make a hole between the old alloc (new zygote) space and the new alloc space and cause the two spaces to be non-contiguous. RemapAtEnd() eliminates new/malloc allocations between the unmap and the map calls. But note this still isn't perfect as other threads could in theory take the memory region between the munmap and the mmap calls. Added tests. Change-Id: I43bc3a33a2cbfc7a092890312e34aa5285384589 --- runtime/gc/space/dlmalloc_space.cc | 7 ++- runtime/mem_map.cc | 69 ++++++++++++++++++++++++++++-- runtime/mem_map.h | 9 ++-- runtime/mem_map_test.cc | 63 ++++++++++++++++++++++++++- 4 files changed, 136 insertions(+), 12 deletions(-) diff --git a/runtime/gc/space/dlmalloc_space.cc b/runtime/gc/space/dlmalloc_space.cc index 8c13d79be6e..9ebc16a4a3d 100644 --- a/runtime/gc/space/dlmalloc_space.cc +++ b/runtime/gc/space/dlmalloc_space.cc @@ -287,8 +287,6 @@ DlMallocSpace* DlMallocSpace::CreateZygoteSpace(const char* alloc_space_name) { size_t size = RoundUp(Size(), kPageSize); // Trim the heap so that we minimize the size of the Zygote space. Trim(); - // Trim our mem-map to free unused pages. - GetMemMap()->UnMapAtEnd(end_); // TODO: Not hardcode these in? const size_t starting_size = kPageSize; const size_t initial_size = 2 * MB; @@ -308,9 +306,10 @@ DlMallocSpace* DlMallocSpace::CreateZygoteSpace(const char* alloc_space_name) { VLOG(heap) << "Size " << GetMemMap()->Size(); VLOG(heap) << "GrowthLimit " << PrettySize(growth_limit); VLOG(heap) << "Capacity " << PrettySize(capacity); + // Remap the tail. std::string error_msg; - UniquePtr mem_map(MemMap::MapAnonymous(alloc_space_name, End(), capacity, - PROT_READ | PROT_WRITE, &error_msg)); + UniquePtr mem_map(GetMemMap()->RemapAtEnd(end_, alloc_space_name, + PROT_READ | PROT_WRITE, &error_msg)); CHECK(mem_map.get() != nullptr) << error_msg; void* mspace = CreateMallocSpace(end_, starting_size, initial_size); // Protect memory beyond the initial size. diff --git a/runtime/mem_map.cc b/runtime/mem_map.cc index 00316f7a211..70d3457c7fe 100644 --- a/runtime/mem_map.cc +++ b/runtime/mem_map.cc @@ -170,12 +170,73 @@ MemMap::MemMap(const std::string& name, byte* begin, size_t size, void* base_beg } }; -void MemMap::UnMapAtEnd(byte* new_end) { +MemMap* MemMap::RemapAtEnd(byte* new_end, const char* tail_name, int tail_prot, + std::string* error_msg) { DCHECK_GE(new_end, Begin()); DCHECK_LE(new_end, End()); - size_t unmap_size = End() - new_end; - munmap(new_end, unmap_size); - size_ -= unmap_size; + DCHECK_LE(begin_ + size_, reinterpret_cast(base_begin_) + base_size_); + DCHECK(IsAligned(begin_)); + DCHECK(IsAligned(base_begin_)); + DCHECK(IsAligned(reinterpret_cast(base_begin_) + base_size_)); + DCHECK(IsAligned(new_end)); + byte* old_end = begin_ + size_; + byte* old_base_end = reinterpret_cast(base_begin_) + base_size_; + byte* new_base_end = new_end; + DCHECK_LE(new_base_end, old_base_end); + if (new_base_end == old_base_end) { + return new MemMap(tail_name, NULL, 0, NULL, 0, tail_prot); + } + size_ = new_end - reinterpret_cast(begin_); + base_size_ = new_base_end - reinterpret_cast(base_begin_); + DCHECK_LE(begin_ + size_, reinterpret_cast(base_begin_) + base_size_); + size_t tail_size = old_end - new_end; + byte* tail_base_begin = new_base_end; + size_t tail_base_size = old_base_end - new_base_end; + DCHECK_EQ(tail_base_begin + tail_base_size, old_base_end); + DCHECK(IsAligned(tail_base_size)); + +#ifdef USE_ASHMEM + // android_os_Debug.cpp read_mapinfo assumes all ashmem regions associated with the VM are + // prefixed "dalvik-". + std::string debug_friendly_name("dalvik-"); + debug_friendly_name += tail_name; + ScopedFd fd(ashmem_create_region(debug_friendly_name.c_str(), tail_base_size)); + int flags = MAP_PRIVATE; + if (fd.get() == -1) { + *error_msg = StringPrintf("ashmem_create_region failed for '%s': %s", + tail_name, strerror(errno)); + return nullptr; + } +#else + ScopedFd fd(-1); + int flags = MAP_PRIVATE | MAP_ANONYMOUS; +#endif + + // Unmap/map the tail region. + int result = munmap(tail_base_begin, tail_base_size); + if (result == -1) { + std::string maps; + ReadFileToString("/proc/self/maps", &maps); + *error_msg = StringPrintf("munmap(%p, %zd) failed for '%s'\n%s", + tail_base_begin, tail_base_size, name_.c_str(), + maps.c_str()); + return nullptr; + } + // Don't cause memory allocation between the munmap and the mmap + // calls. Otherwise, libc (or something else) might take this memory + // region. Note this isn't perfect as there's no way to prevent + // other threads to try to take this memory region here. + byte* actual = reinterpret_cast(mmap(tail_base_begin, tail_base_size, tail_prot, + flags, fd.get(), 0)); + if (actual == MAP_FAILED) { + std::string maps; + ReadFileToString("/proc/self/maps", &maps); + *error_msg = StringPrintf("anonymous mmap(%p, %zd, %x, %x, %d, 0) failed\n%s", + tail_base_begin, tail_base_size, tail_prot, flags, fd.get(), + maps.c_str()); + return nullptr; + } + return new MemMap(tail_name, actual, tail_size, actual, tail_base_size, tail_prot); } bool MemMap::Protect(int prot) { diff --git a/runtime/mem_map.h b/runtime/mem_map.h index 919463c4c62..2c658334a5c 100644 --- a/runtime/mem_map.h +++ b/runtime/mem_map.h @@ -84,8 +84,9 @@ class MemMap { return Begin() <= addr && addr < End(); } - // Trim by unmapping pages at the end of the map. - void UnMapAtEnd(byte* new_end); + // Unmap the pages at end and remap them to create another memory map. + MemMap* RemapAtEnd(byte* new_end, const char* tail_name, int tail_prot, + std::string* error_msg); private: MemMap(const std::string& name, byte* begin, size_t size, void* base_begin, size_t base_size, @@ -96,8 +97,10 @@ class MemMap { size_t size_; // Length of data. void* const base_begin_; // Page-aligned base address. - const size_t base_size_; // Length of mapping. + size_t base_size_; // Length of mapping. May be changed by RemapAtEnd (ie Zygote). int prot_; // Protection of the map. + + friend class MemMapTest; // To allow access to base_begin_ and base_size_. }; } // namespace art diff --git a/runtime/mem_map_test.cc b/runtime/mem_map_test.cc index 09de320a5e2..cf2c9d028c3 100644 --- a/runtime/mem_map_test.cc +++ b/runtime/mem_map_test.cc @@ -21,7 +21,15 @@ namespace art { -class MemMapTest : public testing::Test {}; +class MemMapTest : public testing::Test { + public: + byte* BaseBegin(MemMap* mem_map) { + return reinterpret_cast(mem_map->base_begin_); + } + size_t BaseSize(MemMap* mem_map) { + return mem_map->base_size_; + } +}; TEST_F(MemMapTest, MapAnonymousEmpty) { std::string error_msg; @@ -34,4 +42,57 @@ TEST_F(MemMapTest, MapAnonymousEmpty) { ASSERT_TRUE(error_msg.empty()); } +TEST_F(MemMapTest, RemapAtEnd) { + std::string error_msg; + // Cast the page size to size_t. + const size_t page_size = static_cast(kPageSize); + // Map a two-page memory region. + MemMap* m0 = MemMap::MapAnonymous("MemMapTest_RemapAtEndTest_map0", + NULL, + 2 * page_size, + PROT_READ | PROT_WRITE, + &error_msg); + // Check its state and write to it. + byte* base0 = m0->Begin(); + ASSERT_TRUE(base0 != NULL) << error_msg; + size_t size0 = m0->Size(); + EXPECT_EQ(m0->Size(), 2 * page_size); + EXPECT_EQ(BaseBegin(m0), base0); + EXPECT_EQ(BaseSize(m0), size0); + memset(base0, 42, 2 * page_size); + // Remap the latter half into a second MemMap. + MemMap* m1 = m0->RemapAtEnd(base0 + page_size, + "MemMapTest_RemapAtEndTest_map1", + PROT_READ | PROT_WRITE, + &error_msg); + // Check the states of the two maps. + EXPECT_EQ(m0->Begin(), base0) << error_msg; + EXPECT_EQ(m0->Size(), page_size); + EXPECT_EQ(BaseBegin(m0), base0); + EXPECT_EQ(BaseSize(m0), page_size); + byte* base1 = m1->Begin(); + size_t size1 = m1->Size(); + EXPECT_EQ(base1, base0 + page_size); + EXPECT_EQ(size1, page_size); + EXPECT_EQ(BaseBegin(m1), base1); + EXPECT_EQ(BaseSize(m1), size1); + // Write to the second region. + memset(base1, 43, page_size); + // Check the contents of the two regions. + for (size_t i = 0; i < page_size; ++i) { + EXPECT_EQ(base0[i], 42); + } + for (size_t i = 0; i < page_size; ++i) { + EXPECT_EQ(base1[i], 43); + } + // Unmap the first region. + delete m0; + // Make sure the second region is still accessible after the first + // region is unmapped. + for (size_t i = 0; i < page_size; ++i) { + EXPECT_EQ(base1[i], 43); + } + delete m1; +} + } // namespace art From 83883d7fddf30fdb8b6903560fa1337ab991e74c Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Mon, 21 Oct 2013 21:07:24 -0700 Subject: [PATCH 0106/2402] Populate dex cache for sharpened calls. We ensured the resolved method was in the dex cache, but for a sharpened call this is abstract. Ensure that the concrete method is also resolved. Limit the use of direct dex cache based dispatch to cases where we know how to patch the dex cache. Bug 11389002 Change-Id: I08252686a53b5948650632837c74bcd5cbf8a862 --- compiler/dex/quick/gen_invoke.cc | 14 +- compiler/driver/compiler_driver.cc | 210 +++++++++--------- compiler/driver/compiler_driver.h | 4 +- .../quick/quick_trampoline_entrypoints.cc | 16 ++ runtime/object_utils.h | 40 ++++ 5 files changed, 168 insertions(+), 116 deletions(-) diff --git a/compiler/dex/quick/gen_invoke.cc b/compiler/dex/quick/gen_invoke.cc index 64938f3a734..62feadedccf 100644 --- a/compiler/dex/quick/gen_invoke.cc +++ b/compiler/dex/quick/gen_invoke.cc @@ -350,16 +350,13 @@ static int NextSDCallInsn(CompilationUnit* cu, CallInfo* info, uintptr_t direct_code, uintptr_t direct_method, InvokeType type) { Mir2Lir* cg = static_cast(cu->cg.get()); - if (cu->instruction_set != kThumb2) { - // Disable sharpening - direct_code = 0; - direct_method = 0; - } if (direct_code != 0 && direct_method != 0) { switch (state) { case 0: // Get the current Method* [sets kArg0] if (direct_code != static_cast(-1)) { - cg->LoadConstant(cg->TargetReg(kInvokeTgt), direct_code); + if (cu->instruction_set != kX86) { + cg->LoadConstant(cg->TargetReg(kInvokeTgt), direct_code); + } } else { CHECK_EQ(cu->dex_file, target_method.dex_file); LIR* data_target = cg->ScanLiteralPool(cg->code_literal_list_, @@ -405,6 +402,7 @@ static int NextSDCallInsn(CompilationUnit* cu, CallInfo* info, cg->LoadConstant(cg->TargetReg(kInvokeTgt), direct_code); } else { CHECK_EQ(cu->dex_file, target_method.dex_file); + CHECK_LT(target_method.dex_method_index, target_method.dex_file->NumMethodIds()); LIR* data_target = cg->ScanLiteralPool(cg->code_literal_list_, target_method.dex_method_index, 0); if (data_target == NULL) { @@ -501,10 +499,6 @@ static int NextInterfaceCallInsn(CompilationUnit* cu, CallInfo* info, int state, uint32_t unused, uintptr_t unused2, uintptr_t direct_method, InvokeType unused4) { Mir2Lir* cg = static_cast(cu->cg.get()); - if (cu->instruction_set != kThumb2) { - // Disable sharpening - direct_method = 0; - } ThreadOffset trampoline = QUICK_ENTRYPOINT_OFFSET(pInvokeInterfaceTrampoline); if (direct_method != 0) { diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc index 7c4a6ce7851..e618307e727 100644 --- a/compiler/driver/compiler_driver.cc +++ b/compiler/driver/compiler_driver.cc @@ -355,7 +355,7 @@ CompilerDriver::CompilerDriver(CompilerBackend compiler_backend, InstructionSet jni_compiler_(NULL), compiler_enable_auto_elf_loading_(NULL), compiler_get_method_code_addr_(NULL), - support_boot_image_fixup_(true), + support_boot_image_fixup_(instruction_set == kThumb2), dedupe_code_("dedupe code"), dedupe_mapping_table_("dedupe mapping table"), dedupe_vmap_table_("dedupe vmap table"), @@ -1058,10 +1058,12 @@ bool CompilerDriver::ComputeStaticFieldInfo(uint32_t field_idx, const DexCompila return false; // Incomplete knowledge needs slow path. } -void CompilerDriver::GetCodeAndMethodForDirectCall(InvokeType type, InvokeType sharp_type, +void CompilerDriver::GetCodeAndMethodForDirectCall(InvokeType* type, InvokeType sharp_type, + bool no_guarantee_of_dex_cache_entry, mirror::Class* referrer_class, mirror::ArtMethod* method, bool update_stats, + MethodReference* target_method, uintptr_t* direct_code, uintptr_t* direct_method) { // For direct and static methods compute possible direct_code and direct_method values, ie @@ -1070,46 +1072,103 @@ void CompilerDriver::GetCodeAndMethodForDirectCall(InvokeType type, InvokeType s // invoked, so this can be passed to the out-of-line runtime support code. *direct_code = 0; *direct_method = 0; + bool use_dex_cache = false; + bool compiling_boot = Runtime::Current()->GetHeap()->GetContinuousSpaces().size() == 1; if (compiler_backend_ == kPortable) { if (sharp_type != kStatic && sharp_type != kDirect) { return; } + use_dex_cache = true; } else { if (sharp_type != kStatic && sharp_type != kDirect && sharp_type != kInterface) { return; } + // TODO: support patching on all architectures. + use_dex_cache = compiling_boot && !support_boot_image_fixup_; } - bool method_code_in_boot = method->GetDeclaringClass()->GetClassLoader() == NULL; - if (!method_code_in_boot) { - return; + bool method_code_in_boot = (method->GetDeclaringClass()->GetClassLoader() == nullptr); + if (!use_dex_cache) { + if (!method_code_in_boot) { + use_dex_cache = true; + } else { + bool has_clinit_trampoline = + method->IsStatic() && !method->GetDeclaringClass()->IsInitialized(); + if (has_clinit_trampoline && (method->GetDeclaringClass() != referrer_class)) { + // Ensure we run the clinit trampoline unless we are invoking a static method in the same + // class. + use_dex_cache = true; + } + } } - bool has_clinit_trampoline = method->IsStatic() && !method->GetDeclaringClass()->IsInitialized(); - if (has_clinit_trampoline && (method->GetDeclaringClass() != referrer_class)) { - // Ensure we run the clinit trampoline unless we are invoking a static method in the same class. - return; + if (update_stats && method_code_in_boot) { + if (sharp_type != kInterface) { // Interfaces always go via a trampoline until we get IMTs. + stats_->DirectCallsToBoot(*type); + } + stats_->DirectMethodsToBoot(*type); } - if (update_stats) { - if (sharp_type != kInterface) { // Interfaces always go via a trampoline. - stats_->DirectCallsToBoot(type); + if (!use_dex_cache && compiling_boot) { + MethodHelper mh(method); + if (!IsImageClass(mh.GetDeclaringClassDescriptorAsStringPiece())) { + // We can only branch directly to Methods that are resolved in the DexCache. + // Otherwise we won't invoke the resolution trampoline. + use_dex_cache = true; } - stats_->DirectMethodsToBoot(type); } - bool compiling_boot = Runtime::Current()->GetHeap()->GetContinuousSpaces().size() == 1; - if (compiling_boot) { - if (support_boot_image_fixup_) { - MethodHelper mh(method); - if (IsImageClass(mh.GetDeclaringClassDescriptorAsStringPiece())) { - // We can only branch directly to Methods that are resolved in the DexCache. - // Otherwise we won't invoke the resolution trampoline. - *direct_method = -1; - *direct_code = -1; + // The method is defined not within this dex file. We need a dex cache slot within the current + // dex file or direct pointers. + bool must_use_direct_pointers = false; + if (target_method->dex_file == method->GetDeclaringClass()->GetDexCache()->GetDexFile()) { + target_method->dex_method_index = method->GetDexMethodIndex(); + } else { + // TODO: support patching from one dex file to another in the boot image. + use_dex_cache = use_dex_cache || compiling_boot; + if (no_guarantee_of_dex_cache_entry) { + // See if the method is also declared in this dex cache. + uint32_t dex_method_idx = MethodHelper(method).FindDexMethodIndexInOtherDexFile( + *referrer_class->GetDexCache()->GetDexFile()); + if (dex_method_idx != DexFile::kDexNoIndex) { + target_method->dex_method_index = dex_method_idx; + } else { + must_use_direct_pointers = true; } } + } + if (use_dex_cache) { + if (must_use_direct_pointers) { + // Fail. Test above showed the only safe dispatch was via the dex cache, however, the direct + // pointers are required as the dex cache lacks an appropriate entry. + VLOG(compiler) << "Dex cache devirtualization failed for: " << PrettyMethod(method); + } else { + *type = sharp_type; + } } else { - if (Runtime::Current()->GetHeap()->FindSpaceFromObject(method, false)->IsImageSpace()) { - *direct_method = reinterpret_cast(method); + if (compiling_boot) { + *type = sharp_type; + *direct_method = -1; + if (sharp_type != kInterface) { + *direct_code = -1; + } + } else { + bool method_in_image = + Runtime::Current()->GetHeap()->FindSpaceFromObject(method, false)->IsImageSpace(); + if (method_in_image) { + CHECK_EQ(method->IsAbstract(), sharp_type == kInterface); + *type = sharp_type; + *direct_method = reinterpret_cast(method); + if (*type != kInterface) { + *direct_code = reinterpret_cast(method->GetEntryPointFromCompiledCode()); + } + target_method->dex_file = method->GetDeclaringClass()->GetDexCache()->GetDexFile(); + target_method->dex_method_index = method->GetDexMethodIndex(); + } else if (!must_use_direct_pointers) { + // Set the code and rely on the dex cache for the method. + *type = sharp_type; + *direct_code = reinterpret_cast(method->GetEntryPointFromCompiledCode()); + } else { + // Direct pointers were required but none were available. + VLOG(compiler) << "Dex cache devirtualization failed for: " << PrettyMethod(method); + } } - *direct_code = reinterpret_cast(method->GetEntryPointFromCompiledCode()); } } @@ -1126,6 +1185,9 @@ bool CompilerDriver::ComputeInvokeInfo(const DexCompilationUnit* mUnit, const ui ComputeMethodReferencedFromCompilingMethod(soa, mUnit, target_method->dex_method_index, *invoke_type); if (resolved_method != NULL) { + if (*invoke_type == kVirtual || *invoke_type == kSuper) { + *vtable_idx = resolved_method->GetMethodIndex(); + } // Don't try to fast-path if we don't understand the caller's class or this appears to be an // Incompatible Class Change Error. mirror::Class* referrer_class = @@ -1166,13 +1228,14 @@ bool CompilerDriver::ComputeInvokeInfo(const DexCompilationUnit* mUnit, const ui // dex cache, check that this resolved method is where we expect it. CHECK(referrer_class->GetDexCache()->GetResolvedMethod(target_method->dex_method_index) == resolved_method) << PrettyMethod(resolved_method); - if (update_stats) { - stats_->ResolvedMethod(*invoke_type); - stats_->VirtualMadeDirect(*invoke_type); + InvokeType orig_invoke_type = *invoke_type; + GetCodeAndMethodForDirectCall(invoke_type, kDirect, false, referrer_class, resolved_method, + update_stats, target_method, direct_code, direct_method); + if (update_stats && (*invoke_type == kDirect)) { + stats_->ResolvedMethod(orig_invoke_type); + stats_->VirtualMadeDirect(orig_invoke_type); } - GetCodeAndMethodForDirectCall(*invoke_type, kDirect, referrer_class, resolved_method, - update_stats, direct_code, direct_method); - *invoke_type = kDirect; + DCHECK_NE(*invoke_type, kSuper) << PrettyMethod(resolved_method); return true; } const bool enableVerifierBasedSharpening = enable_devirtualization; @@ -1194,76 +1257,16 @@ bool CompilerDriver::ComputeInvokeInfo(const DexCompilationUnit* mUnit, const ui kVirtual); CHECK(called_method != NULL); CHECK(!called_method->IsAbstract()); - GetCodeAndMethodForDirectCall(*invoke_type, kDirect, referrer_class, called_method, - update_stats, direct_code, direct_method); - bool compiler_needs_dex_cache = - (GetCompilerBackend() == kPortable) || - (GetCompilerBackend() == kQuick && instruction_set_ != kThumb2) || - (*direct_code == 0) || (*direct_code == static_cast(-1)) || - (*direct_method == 0) || (*direct_method == static_cast(-1)); - if ((devirt_map_target->dex_file != target_method->dex_file) && - compiler_needs_dex_cache) { - // We need to use the dex cache to find either the method or code, and the dex file - // containing the method isn't the one expected for the target method. Try to find - // the method within the expected target dex file. - // TODO: the -1 could be handled as direct code if the patching new the target dex - // file. - // TODO: quick only supports direct pointers with Thumb2. - // TODO: the following should be factored into a common helper routine to find - // one dex file's method within another. - const DexFile* dexfile = target_method->dex_file; - const DexFile* cm_dexfile = - called_method->GetDeclaringClass()->GetDexCache()->GetDexFile(); - const DexFile::MethodId& cm_method_id = - cm_dexfile->GetMethodId(called_method->GetDexMethodIndex()); - const char* cm_descriptor = cm_dexfile->StringByTypeIdx(cm_method_id.class_idx_); - const DexFile::StringId* descriptor = dexfile->FindStringId(cm_descriptor); - if (descriptor != NULL) { - const DexFile::TypeId* type_id = - dexfile->FindTypeId(dexfile->GetIndexForStringId(*descriptor)); - if (type_id != NULL) { - const char* cm_name = cm_dexfile->GetMethodName(cm_method_id); - const DexFile::StringId* name = dexfile->FindStringId(cm_name); - if (name != NULL) { - uint16_t return_type_idx; - std::vector param_type_idxs; - bool success = - dexfile->CreateTypeList(cm_dexfile->GetMethodSignature(cm_method_id).ToString(), - &return_type_idx, ¶m_type_idxs); - if (success) { - const DexFile::ProtoId* sig = - dexfile->FindProtoId(return_type_idx, param_type_idxs); - if (sig != NULL) { - const DexFile::MethodId* method_id = dexfile->FindMethodId(*type_id, - *name, *sig); - if (method_id != NULL) { - if (update_stats) { - stats_->ResolvedMethod(*invoke_type); - stats_->VirtualMadeDirect(*invoke_type); - stats_->PreciseTypeDevirtualization(); - } - target_method->dex_method_index = - dexfile->GetIndexForMethodId(*method_id); - *invoke_type = kDirect; - return true; - } - } - } - } - } - } - // TODO: the stats for direct code and method are off as we failed to find the direct - // method in the referring method's dex cache/file. - } else { - if (update_stats) { - stats_->ResolvedMethod(*invoke_type); - stats_->VirtualMadeDirect(*invoke_type); - stats_->PreciseTypeDevirtualization(); - } - *target_method = *devirt_map_target; - *invoke_type = kDirect; - return true; + InvokeType orig_invoke_type = *invoke_type; + GetCodeAndMethodForDirectCall(invoke_type, kDirect, true, referrer_class, called_method, + update_stats, target_method, direct_code, direct_method); + if (update_stats && (*invoke_type == kDirect)) { + stats_->ResolvedMethod(orig_invoke_type); + stats_->VirtualMadeDirect(orig_invoke_type); + stats_->PreciseTypeDevirtualization(); } + DCHECK_NE(*invoke_type, kSuper); + return true; } } if (*invoke_type == kSuper) { @@ -1273,11 +1276,8 @@ bool CompilerDriver::ComputeInvokeInfo(const DexCompilationUnit* mUnit, const ui if (update_stats) { stats_->ResolvedMethod(*invoke_type); } - if (*invoke_type == kVirtual || *invoke_type == kSuper) { - *vtable_idx = resolved_method->GetMethodIndex(); - } - GetCodeAndMethodForDirectCall(*invoke_type, *invoke_type, referrer_class, resolved_method, - update_stats, direct_code, direct_method); + GetCodeAndMethodForDirectCall(invoke_type, *invoke_type, false, referrer_class, resolved_method, + update_stats, target_method, direct_code, direct_method); return true; } } diff --git a/compiler/driver/compiler_driver.h b/compiler/driver/compiler_driver.h index 7657af5cee3..971021f9034 100644 --- a/compiler/driver/compiler_driver.h +++ b/compiler/driver/compiler_driver.h @@ -321,10 +321,12 @@ class CompilerDriver { private: // Compute constant code and method pointers when possible - void GetCodeAndMethodForDirectCall(InvokeType type, InvokeType sharp_type, + void GetCodeAndMethodForDirectCall(InvokeType* type, InvokeType sharp_type, + bool no_guarantee_of_dex_cache_entry, mirror::Class* referrer_class, mirror::ArtMethod* method, bool update_stats, + MethodReference* target_method, uintptr_t* direct_code, uintptr_t* direct_method) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); diff --git a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc index 12291c39a99..01d35499857 100644 --- a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc +++ b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc @@ -19,6 +19,7 @@ #include "dex_file-inl.h" #include "dex_instruction-inl.h" #include "entrypoints/entrypoint_utils.h" +#include "gc/accounting/card_table-inl.h" #include "interpreter/interpreter.h" #include "invoke_arg_array_builder.h" #include "mirror/art_method-inl.h" @@ -547,6 +548,21 @@ extern "C" const void* artQuickResolutionTrampoline(mirror::ArtMethod* called, } else if (invoke_type == kInterface) { called = receiver->GetClass()->FindVirtualMethodForInterface(called); } + if ((invoke_type == kVirtual) || (invoke_type == kInterface)) { + // We came here because of sharpening. Ensure the dex cache is up-to-date on the method index + // of the sharpened method. + if (called->GetDexCacheResolvedMethods() == caller->GetDexCacheResolvedMethods()) { + caller->GetDexCacheResolvedMethods()->Set(called->GetDexMethodIndex(), called); + } else { + // Calling from one dex file to another, need to compute the method index appropriate to + // the caller's dex file. + uint32_t method_index = + MethodHelper(called).FindDexMethodIndexInOtherDexFile(MethodHelper(caller).GetDexFile()); + if (method_index != DexFile::kDexNoIndex) { + caller->GetDexCacheResolvedMethods()->Set(method_index, called); + } + } + } // Ensure that the called method's class is initialized. mirror::Class* called_class = called->GetDeclaringClass(); linker->EnsureInitialized(called_class, true, true); diff --git a/runtime/object_utils.h b/runtime/object_utils.h index 8062a890c26..3ca3c0bcf33 100644 --- a/runtime/object_utils.h +++ b/runtime/object_utils.h @@ -700,6 +700,46 @@ class MethodHelper { return s; } + uint32_t FindDexMethodIndexInOtherDexFile(const DexFile& other_dexfile) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + const DexFile& dexfile = GetDexFile(); + if (&dexfile == &other_dexfile) { + return method_->GetDexMethodIndex(); + } + const DexFile::MethodId& mid = dexfile.GetMethodId(method_->GetDexMethodIndex()); + const char* mid_declaring_class_descriptor = dexfile.StringByTypeIdx(mid.class_idx_); + const DexFile::StringId* other_descriptor = + other_dexfile.FindStringId(mid_declaring_class_descriptor); + if (other_descriptor != nullptr) { + const DexFile::TypeId* other_type_id = + other_dexfile.FindTypeId(other_dexfile.GetIndexForStringId(*other_descriptor)); + if (other_type_id != nullptr) { + const char* mid_name = dexfile.GetMethodName(mid); + const DexFile::StringId* other_name = other_dexfile.FindStringId(mid_name); + if (other_name != nullptr) { + uint16_t other_return_type_idx; + std::vector other_param_type_idxs; + bool success = other_dexfile.CreateTypeList(dexfile.GetMethodSignature(mid).ToString(), + &other_return_type_idx, + &other_param_type_idxs); + if (success) { + const DexFile::ProtoId* other_sig = + other_dexfile.FindProtoId(other_return_type_idx, other_param_type_idxs); + if (other_sig != nullptr) { + const DexFile::MethodId* other_mid = other_dexfile.FindMethodId(*other_type_id, + *other_name, + *other_sig); + if (other_mid != nullptr) { + return other_dexfile.GetIndexForMethodId(*other_mid); + } + } + } + } + } + } + return DexFile::kDexNoIndex; + } + private: // Set the method_ field, for proxy methods looking up the interface method via the resolved // methods table. From 31aa97cfec5ee76b2f2496464e1b6f9e11d21a29 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Fri, 25 Oct 2013 23:07:29 +0000 Subject: [PATCH 0107/2402] Revert "Null check elimination improvement" This reverts commit 4db179d1821a9e78819d5adc8057a72f49e2aed8. Change-Id: I059c15c85860c6c9f235b5dabaaef2edebaf1de2 --- compiler/dex/mir_dataflow.cc | 3 +- compiler/dex/mir_optimization.cc | 86 ++++++++++++------------------ compiler/dex/quick/codegen_util.cc | 3 +- compiler/dex/ssa_transformation.cc | 1 - 4 files changed, 35 insertions(+), 58 deletions(-) diff --git a/compiler/dex/mir_dataflow.cc b/compiler/dex/mir_dataflow.cc index 11e19dc43f1..9c8ce23ca56 100644 --- a/compiler/dex/mir_dataflow.cc +++ b/compiler/dex/mir_dataflow.cc @@ -1243,8 +1243,7 @@ bool MIRGraph::CountUses(struct BasicBlock* bb) { if (mir->ssa_rep == NULL) { continue; } - // Each level of nesting adds *16 to count, up to 3 levels deep. - uint32_t weight = std::min(3U, static_cast(bb->nesting_depth) * 4); + uint32_t weight = std::min(16U, static_cast(bb->nesting_depth)); for (int i = 0; i < mir->ssa_rep->num_uses; i++) { int s_reg = mir->ssa_rep->uses[i]; raw_use_counts_.Increment(s_reg); diff --git a/compiler/dex/mir_optimization.cc b/compiler/dex/mir_optimization.cc index 0b453b142fb..3cd158ffc06 100644 --- a/compiler/dex/mir_optimization.cc +++ b/compiler/dex/mir_optimization.cc @@ -629,15 +629,10 @@ bool MIRGraph::EliminateNullChecks(struct BasicBlock* bb) { */ if ((bb->block_type == kEntryBlock) | bb->catch_entry) { temp_ssa_register_v_->ClearAllBits(); - // Assume all ins are objects. - for (uint16_t in_reg = cu_->num_dalvik_registers - cu_->num_ins; - in_reg < cu_->num_dalvik_registers; in_reg++) { - temp_ssa_register_v_->SetBit(in_reg); - } if ((cu_->access_flags & kAccStatic) == 0) { // If non-static method, mark "this" as non-null int this_reg = cu_->num_dalvik_registers - cu_->num_ins; - temp_ssa_register_v_->ClearBit(this_reg); + temp_ssa_register_v_->SetBit(this_reg); } } else if (bb->predecessors->Size() == 1) { BasicBlock* pred_bb = GetBasicBlock(bb->predecessors->Get(0)); @@ -650,18 +645,18 @@ bool MIRGraph::EliminateNullChecks(struct BasicBlock* bb) { if (pred_bb->fall_through == bb->id) { // The fall-through of a block following a IF_EQZ, set the vA of the IF_EQZ to show that // it can't be null. - temp_ssa_register_v_->ClearBit(last_insn->ssa_rep->uses[0]); + temp_ssa_register_v_->SetBit(last_insn->ssa_rep->uses[0]); } } else if (last_opcode == Instruction::IF_NEZ) { if (pred_bb->taken == bb->id) { // The taken block following a IF_NEZ, set the vA of the IF_NEZ to show that it can't be // null. - temp_ssa_register_v_->ClearBit(last_insn->ssa_rep->uses[0]); + temp_ssa_register_v_->SetBit(last_insn->ssa_rep->uses[0]); } } } } else { - // Starting state is union of all incoming arcs + // Starting state is intersection of all incoming arcs GrowableArray::Iterator iter(bb->predecessors); BasicBlock* pred_bb = GetBasicBlock(iter.Next()); DCHECK(pred_bb != NULL); @@ -673,13 +668,10 @@ bool MIRGraph::EliminateNullChecks(struct BasicBlock* bb) { (pred_bb->data_flow_info->ending_null_check_v == NULL)) { continue; } - temp_ssa_register_v_->Union(pred_bb->data_flow_info->ending_null_check_v); + temp_ssa_register_v_->Intersect(pred_bb->data_flow_info->ending_null_check_v); } } - // At this point, temp_ssa_register_v_ shows which sregs have an object definition with - // no intervening uses. - // Walk through the instruction in the block, updating as necessary for (MIR* mir = bb->first_mir_insn; mir != NULL; mir = mir->next) { if (mir->ssa_rep == NULL) { @@ -687,39 +679,9 @@ bool MIRGraph::EliminateNullChecks(struct BasicBlock* bb) { } int df_attributes = oat_data_flow_attributes_[mir->dalvikInsn.opcode]; - // Already nullchecked? - if ((df_attributes & DF_HAS_NULL_CHKS) && !(mir->optimization_flags & MIR_IGNORE_NULL_CHECK)) { - int src_idx; - if (df_attributes & DF_NULL_CHK_1) { - src_idx = 1; - } else if (df_attributes & DF_NULL_CHK_2) { - src_idx = 2; - } else { - src_idx = 0; - } - int src_sreg = mir->ssa_rep->uses[src_idx]; - if (!temp_ssa_register_v_->IsBitSet(src_sreg)) { - // Eliminate the null check - mir->optimization_flags |= MIR_IGNORE_NULL_CHECK; - } else { - // Mark s_reg as null-checked - temp_ssa_register_v_->ClearBit(src_sreg); - } - } - - if ((df_attributes & (DF_REF_A | DF_NULL_TRANSFER_0 | DF_NULL_TRANSFER_N)) == 0) { - continue; - } - - // First, mark all object definitions as requiring null check. - if ((df_attributes & (DF_DA | DF_REF_A)) == (DF_DA | DF_REF_A)) { - temp_ssa_register_v_->SetBit(mir->ssa_rep->defs[0]); - } - - // Now, remove mark from all object definitions we know are non-null. + // Mark target of NEW* as non-null if (df_attributes & DF_NON_NULL_DST) { - // Mark target of NEW* as non-null - temp_ssa_register_v_->ClearBit(mir->ssa_rep->defs[0]); + temp_ssa_register_v_->SetBit(mir->ssa_rep->defs[0]); } // Mark non-null returns from invoke-style NEW* @@ -729,7 +691,7 @@ bool MIRGraph::EliminateNullChecks(struct BasicBlock* bb) { if (next_mir && next_mir->dalvikInsn.opcode == Instruction::MOVE_RESULT_OBJECT) { // Mark as null checked - temp_ssa_register_v_->ClearBit(next_mir->ssa_rep->defs[0]); + temp_ssa_register_v_->SetBit(next_mir->ssa_rep->defs[0]); } else { if (next_mir) { LOG(WARNING) << "Unexpected opcode following new: " << next_mir->dalvikInsn.opcode; @@ -744,7 +706,7 @@ bool MIRGraph::EliminateNullChecks(struct BasicBlock* bb) { // First non-pseudo should be MOVE_RESULT_OBJECT if (tmir->dalvikInsn.opcode == Instruction::MOVE_RESULT_OBJECT) { // Mark as null checked - temp_ssa_register_v_->ClearBit(tmir->ssa_rep->defs[0]); + temp_ssa_register_v_->SetBit(tmir->ssa_rep->defs[0]); } else { LOG(WARNING) << "Unexpected op after new: " << tmir->dalvikInsn.opcode; } @@ -757,22 +719,40 @@ bool MIRGraph::EliminateNullChecks(struct BasicBlock* bb) { /* * Propagate nullcheck state on register copies (including * Phi pseudo copies. For the latter, nullcheck state is - * the "or" of all the Phi's operands. + * the "and" of all the Phi's operands. */ if (df_attributes & (DF_NULL_TRANSFER_0 | DF_NULL_TRANSFER_N)) { int tgt_sreg = mir->ssa_rep->defs[0]; int operands = (df_attributes & DF_NULL_TRANSFER_0) ? 1 : mir->ssa_rep->num_uses; - bool needs_null_check = false; + bool null_checked = true; for (int i = 0; i < operands; i++) { - needs_null_check |= temp_ssa_register_v_->IsBitSet(mir->ssa_rep->uses[i]); + null_checked &= temp_ssa_register_v_->IsBitSet(mir->ssa_rep->uses[i]); } - if (needs_null_check) { + if (null_checked) { temp_ssa_register_v_->SetBit(tgt_sreg); - } else { - temp_ssa_register_v_->ClearBit(tgt_sreg); } } + + // Already nullchecked? + if ((df_attributes & DF_HAS_NULL_CHKS) && !(mir->optimization_flags & MIR_IGNORE_NULL_CHECK)) { + int src_idx; + if (df_attributes & DF_NULL_CHK_1) { + src_idx = 1; + } else if (df_attributes & DF_NULL_CHK_2) { + src_idx = 2; + } else { + src_idx = 0; + } + int src_sreg = mir->ssa_rep->uses[src_idx]; + if (temp_ssa_register_v_->IsBitSet(src_sreg)) { + // Eliminate the null check + mir->optimization_flags |= MIR_IGNORE_NULL_CHECK; + } else { + // Mark s_reg as null-checked + temp_ssa_register_v_->SetBit(src_sreg); + } + } } // Did anything change? diff --git a/compiler/dex/quick/codegen_util.cc b/compiler/dex/quick/codegen_util.cc index cc185f54eeb..2ce8f581b4b 100644 --- a/compiler/dex/quick/codegen_util.cc +++ b/compiler/dex/quick/codegen_util.cc @@ -164,8 +164,7 @@ void Mir2Lir::DumpLIRInsn(LIR* lir, unsigned char* base_addr) { lir->operands[0] = WrapPointer(ArenaStrdup("No instruction string")); } LOG(INFO) << "-------- dalvik offset: 0x" << std::hex - << lir->dalvik_offset << " @ " - << reinterpret_cast(UnwrapPointer(lir->operands[0])); + << lir->dalvik_offset << " @ " << reinterpret_cast(lir->operands[0]); break; case kPseudoExitBlock: LOG(INFO) << "-------- exit offset: 0x" << std::hex << dest; diff --git a/compiler/dex/ssa_transformation.cc b/compiler/dex/ssa_transformation.cc index c86a788470a..eb0d412bae3 100644 --- a/compiler/dex/ssa_transformation.cc +++ b/compiler/dex/ssa_transformation.cc @@ -206,7 +206,6 @@ void MIRGraph::ComputeDomPostOrderTraversal(BasicBlock* bb) { /* hacky loop detection */ if ((curr_bb->taken != NullBasicBlockId) && curr_bb->dominators->IsBitSet(curr_bb->taken)) { - curr_bb->nesting_depth++; attributes_ |= METHOD_HAS_LOOP; } } From ba150c37d582eeeb8c11ba5245edc281cf31793c Mon Sep 17 00:00:00 2001 From: Brian Carlstrom Date: Tue, 27 Aug 2013 17:31:03 -0700 Subject: [PATCH 0108/2402] Omit OatMethodOffsets for classes without compiled code Change-Id: If0d290f4aebc778ff12d8fed017c270ad2ac3220 --- build/Android.gtest.mk | 3 +- compiler/Android.mk | 2 - compiler/dex/arena_allocator_test.cc | 33 +++ compiler/dex/arena_bit_vector.h | 2 +- compiler/oat_test.cc | 17 +- compiler/oat_writer.cc | 239 ++++++++++++++---- compiler/oat_writer.h | 60 ++++- compiler/utils/dedupe_set.h | 1 + compiler/utils/dedupe_set_test.cc | 3 +- oatdump/oatdump.cc | 6 +- runtime/Android.mk | 3 + {compiler/utils => runtime/base}/allocator.cc | 0 {compiler/utils => runtime/base}/allocator.h | 6 +- .../utils => runtime/base}/bit_vector.cc | 54 ++-- {compiler/utils => runtime/base}/bit_vector.h | 22 +- .../utils => runtime/base}/bit_vector_test.cc | 52 +++- runtime/exception_test.cc | 2 +- runtime/globals.h | 20 +- runtime/mirror/class.h | 1 + runtime/oat.cc | 2 +- runtime/oat.h | 13 + runtime/oat_file.cc | 80 +++++- runtime/oat_file.h | 20 +- 23 files changed, 500 insertions(+), 141 deletions(-) create mode 100644 compiler/dex/arena_allocator_test.cc rename {compiler/utils => runtime/base}/allocator.cc (100%) rename {compiler/utils => runtime/base}/allocator.h (88%) rename {compiler/utils => runtime/base}/bit_vector.cc (70%) rename {compiler/utils => runtime/base}/bit_vector.h (89%) rename {compiler/utils => runtime/base}/bit_vector_test.cc (68%) diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk index 22abba170bd..655c7dd01f0 100644 --- a/build/Android.gtest.mk +++ b/build/Android.gtest.mk @@ -17,17 +17,18 @@ LOCAL_PATH := art TEST_COMMON_SRC_FILES := \ + compiler/dex/arena_allocator_test.cc \ compiler/driver/compiler_driver_test.cc \ compiler/elf_writer_test.cc \ compiler/image_test.cc \ compiler/jni/jni_compiler_test.cc \ compiler/oat_test.cc \ compiler/output_stream_test.cc \ - compiler/utils/bit_vector_test.cc \ compiler/utils/dedupe_set_test.cc \ compiler/utils/arm/managed_register_arm_test.cc \ compiler/utils/x86/managed_register_x86_test.cc \ runtime/barrier_test.cc \ + runtime/base/bit_vector_test.cc \ runtime/base/histogram_test.cc \ runtime/base/mutex_test.cc \ runtime/base/timing_logger_test.cc \ diff --git a/compiler/Android.mk b/compiler/Android.mk index 0d3acbdb716..fc2f02b59e7 100644 --- a/compiler/Android.mk +++ b/compiler/Android.mk @@ -79,8 +79,6 @@ LIBART_COMPILER_SRC_FILES := \ utils/arm/assembler_arm.cc \ utils/arm/managed_register_arm.cc \ utils/assembler.cc \ - utils/allocator.cc \ - utils/bit_vector.cc \ utils/mips/assembler_mips.cc \ utils/mips/managed_register_mips.cc \ utils/x86/assembler_x86.cc \ diff --git a/compiler/dex/arena_allocator_test.cc b/compiler/dex/arena_allocator_test.cc new file mode 100644 index 00000000000..63dc6159ebf --- /dev/null +++ b/compiler/dex/arena_allocator_test.cc @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2013 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 "arena_allocator.h" +#include "arena_bit_vector.h" +#include "gtest/gtest.h" + +namespace art { + +TEST(ArenaAllocator, Test) { + ArenaPool pool; + ArenaAllocator arena(&pool); + ArenaBitVector bv(&arena, 10, true); + bv.SetBit(5); + EXPECT_EQ(1U, bv.GetStorageSize()); + bv.SetBit(35); + EXPECT_EQ(2U, bv.GetStorageSize()); +} + +} // namespace art diff --git a/compiler/dex/arena_bit_vector.h b/compiler/dex/arena_bit_vector.h index 7d2f3ffa5ed..4b2193a3f1c 100644 --- a/compiler/dex/arena_bit_vector.h +++ b/compiler/dex/arena_bit_vector.h @@ -18,8 +18,8 @@ #define ART_COMPILER_DEX_ARENA_BIT_VECTOR_H_ #include "arena_allocator.h" +#include "base/bit_vector.h" #include "compiler_enums.h" -#include "utils/bit_vector.h" namespace art { diff --git a/compiler/oat_test.cc b/compiler/oat_test.cc index 634a160a974..af867430140 100644 --- a/compiler/oat_test.cc +++ b/compiler/oat_test.cc @@ -28,6 +28,8 @@ namespace art { class OatTest : public CommonTest { protected: + static const bool kCompile = false; // DISABLED_ due to the time to compile libcore + void CheckMethod(mirror::ArtMethod* method, const OatFile::OatMethod& oat_method, const DexFile* dex_file) @@ -40,7 +42,7 @@ class OatTest : public CommonTest { EXPECT_TRUE(oat_method.GetCode() == NULL) << PrettyMethod(method) << " " << oat_method.GetCode(); #if !defined(ART_USE_PORTABLE_COMPILER) - EXPECT_EQ(oat_method.GetFrameSizeInBytes(), static_cast(kStackAlignment)); + EXPECT_EQ(oat_method.GetFrameSizeInBytes(), kCompile ? kStackAlignment : 0); EXPECT_EQ(oat_method.GetCoreSpillMask(), 0U); EXPECT_EQ(oat_method.GetFpSpillMask(), 0U); #endif @@ -65,7 +67,6 @@ class OatTest : public CommonTest { }; TEST_F(OatTest, WriteRead) { - const bool compile = false; // DISABLED_ due to the time to compile libcore ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); // TODO: make selectable @@ -77,7 +78,7 @@ TEST_F(OatTest, WriteRead) { InstructionSet insn_set = kIsTargetBuild ? kThumb2 : kX86; compiler_driver_.reset(new CompilerDriver(compiler_backend, insn_set, false, NULL, 2, true)); jobject class_loader = NULL; - if (compile) { + if (kCompile) { base::TimingLogger timings("OatTest::WriteRead", false, false); compiler_driver_->CompileAll(class_loader, class_linker->GetBootClassPath(), timings); } @@ -96,7 +97,7 @@ TEST_F(OatTest, WriteRead) { tmp.GetFile()); ASSERT_TRUE(success); - if (compile) { // OatWriter strips the code, regenerate to compare + if (kCompile) { // OatWriter strips the code, regenerate to compare base::TimingLogger timings("CommonTest::WriteRead", false, false); compiler_driver_->CompileAll(class_loader, class_linker->GetBootClassPath(), timings); } @@ -120,16 +121,18 @@ TEST_F(OatTest, WriteRead) { for (size_t i = 0; i < dex_file->NumClassDefs(); i++) { const DexFile::ClassDef& class_def = dex_file->GetClassDef(i); const byte* class_data = dex_file->GetClassData(class_def); - size_t num_virtual_methods =0; + size_t num_virtual_methods = 0; if (class_data != NULL) { ClassDataItemIterator it(*dex_file, class_data); num_virtual_methods = it.NumVirtualMethods(); } const char* descriptor = dex_file->GetClassDescriptor(class_def); + mirror::Class* klass = class_linker->FindClass(descriptor, NULL); UniquePtr oat_class(oat_dex_file->GetOatClass(i)); - - mirror::Class* klass = class_linker->FindClass(descriptor, NULL); + CHECK_EQ(mirror::Class::Status::kStatusNotReady, oat_class->GetStatus()) << descriptor; + CHECK_EQ(kCompile ? OatClassType::kOatClassAllCompiled : OatClassType::kOatClassNoneCompiled, + oat_class->GetType()) << descriptor; size_t method_index = 0; for (size_t i = 0; i < klass->NumDirectMethods(); i++, method_index++) { diff --git a/compiler/oat_writer.cc b/compiler/oat_writer.cc index f23b72b466e..f681d7da6fc 100644 --- a/compiler/oat_writer.cc +++ b/compiler/oat_writer.cc @@ -18,6 +18,7 @@ #include +#include "base/bit_vector.h" #include "base/stl_util.h" #include "base/unix_file/fd_file.h" #include "class_linker.h" @@ -70,7 +71,9 @@ OatWriter::OatWriter(const std::vector& dex_files, size_oat_dex_file_location_checksum_(0), size_oat_dex_file_offset_(0), size_oat_dex_file_methods_offsets_(0), + size_oat_class_type_(0), size_oat_class_status_(0), + size_oat_class_method_bitmaps_(0), size_oat_class_method_offsets_(0) { size_t offset = InitOatHeader(); offset = InitOatDexFiles(offset); @@ -142,12 +145,48 @@ size_t OatWriter::InitOatClasses(size_t offset) { oat_dex_files_[i]->methods_offsets_[class_def_index] = offset; const DexFile::ClassDef& class_def = dex_file->GetClassDef(class_def_index); const byte* class_data = dex_file->GetClassData(class_def); - uint32_t num_methods = 0; + uint32_t num_non_null_compiled_methods = 0; + UniquePtr > compiled_methods(new std::vector()); if (class_data != NULL) { // ie not an empty class, such as a marker interface ClassDataItemIterator it(*dex_file, class_data); size_t num_direct_methods = it.NumDirectMethods(); size_t num_virtual_methods = it.NumVirtualMethods(); - num_methods = num_direct_methods + num_virtual_methods; + size_t num_methods = num_direct_methods + num_virtual_methods; + + // Fill in the compiled_methods_ array for methods that have a + // CompiledMethod. We track the number of non-null entries in + // num_non_null_compiled_methods since we only want to allocate + // OatMethodOffsets for the compiled methods. + compiled_methods->reserve(num_methods); + while (it.HasNextStaticField()) { + it.Next(); + } + while (it.HasNextInstanceField()) { + it.Next(); + } + size_t class_def_method_index = 0; + while (it.HasNextDirectMethod()) { + uint32_t method_idx = it.GetMemberIndex(); + CompiledMethod* compiled_method = + compiler_driver_->GetCompiledMethod(MethodReference(dex_file, method_idx)); + compiled_methods->push_back(compiled_method); + if (compiled_method != NULL) { + num_non_null_compiled_methods++; + } + class_def_method_index++; + it.Next(); + } + while (it.HasNextVirtualMethod()) { + uint32_t method_idx = it.GetMemberIndex(); + CompiledMethod* compiled_method = + compiler_driver_->GetCompiledMethod(MethodReference(dex_file, method_idx)); + compiled_methods->push_back(compiled_method); + if (compiled_method != NULL) { + num_non_null_compiled_methods++; + } + class_def_method_index++; + it.Next(); + } } ClassReference class_ref(dex_file, class_def_index); @@ -161,7 +200,8 @@ size_t OatWriter::InitOatClasses(size_t offset) { status = mirror::Class::kStatusNotReady; } - OatClass* oat_class = new OatClass(offset, status, num_methods); + OatClass* oat_class = new OatClass(offset, compiled_methods.release(), + num_non_null_compiled_methods, status); oat_classes_.push_back(oat_class); offset += oat_class->SizeOf(); } @@ -212,20 +252,20 @@ size_t OatWriter::InitOatCodeDexFiles(size_t offset) { for (size_t i = 0; i != dex_files_->size(); ++i) { const DexFile* dex_file = (*dex_files_)[i]; CHECK(dex_file != NULL); - offset = InitOatCodeDexFile(offset, oat_class_index, *dex_file); + offset = InitOatCodeDexFile(offset, &oat_class_index, *dex_file); } return offset; } size_t OatWriter::InitOatCodeDexFile(size_t offset, - size_t& oat_class_index, + size_t* oat_class_index, const DexFile& dex_file) { for (size_t class_def_index = 0; class_def_index < dex_file.NumClassDefs(); - class_def_index++, oat_class_index++) { + class_def_index++, (*oat_class_index)++) { const DexFile::ClassDef& class_def = dex_file.GetClassDef(class_def_index); - offset = InitOatCodeClassDef(offset, oat_class_index, class_def_index, dex_file, class_def); - oat_classes_[oat_class_index]->UpdateChecksum(*oat_header_); + offset = InitOatCodeClassDef(offset, *oat_class_index, class_def_index, dex_file, class_def); + oat_classes_[*oat_class_index]->UpdateChecksum(*oat_header_); } return offset; } @@ -240,7 +280,7 @@ size_t OatWriter::InitOatCodeClassDef(size_t offset, return offset; } ClassDataItemIterator it(dex_file, class_data); - CHECK_EQ(oat_classes_[oat_class_index]->method_offsets_.size(), + CHECK_LE(oat_classes_[oat_class_index]->method_offsets_.size(), it.NumDirectMethods() + it.NumVirtualMethods()); // Skip fields while (it.HasNextStaticField()) { @@ -251,32 +291,35 @@ size_t OatWriter::InitOatCodeClassDef(size_t offset, } // Process methods size_t class_def_method_index = 0; + size_t method_offsets_index = 0; while (it.HasNextDirectMethod()) { bool is_native = (it.GetMemberAccessFlags() & kAccNative) != 0; offset = InitOatCodeMethod(offset, oat_class_index, class_def_index, class_def_method_index, - is_native, it.GetMethodInvokeType(class_def), it.GetMemberIndex(), - &dex_file); + &method_offsets_index, is_native, + it.GetMethodInvokeType(class_def), it.GetMemberIndex(), dex_file); class_def_method_index++; it.Next(); } while (it.HasNextVirtualMethod()) { bool is_native = (it.GetMemberAccessFlags() & kAccNative) != 0; offset = InitOatCodeMethod(offset, oat_class_index, class_def_index, class_def_method_index, - is_native, it.GetMethodInvokeType(class_def), it.GetMemberIndex(), - &dex_file); + &method_offsets_index, is_native, + it.GetMethodInvokeType(class_def), it.GetMemberIndex(), dex_file); class_def_method_index++; it.Next(); } DCHECK(!it.HasNext()); + CHECK_LE(method_offsets_index, class_def_method_index); return offset; } size_t OatWriter::InitOatCodeMethod(size_t offset, size_t oat_class_index, size_t __attribute__((unused)) class_def_index, size_t class_def_method_index, + size_t* method_offsets_index, bool __attribute__((unused)) is_native, InvokeType invoke_type, - uint32_t method_idx, const DexFile* dex_file) { + uint32_t method_idx, const DexFile& dex_file) { // derived from CompiledMethod if available uint32_t code_offset = 0; uint32_t frame_size_in_bytes = kStackAlignment; @@ -292,8 +335,7 @@ size_t OatWriter::InitOatCodeMethod(size_t offset, size_t oat_class_index, oat_class->GetOatMethodOffsetsOffsetFromOatHeader(class_def_method_index); #endif - CompiledMethod* compiled_method = - compiler_driver_->GetCompiledMethod(MethodReference(dex_file, method_idx)); + CompiledMethod* compiled_method = oat_class->GetCompiledMethod(class_def_method_index); if (compiled_method != NULL) { #if defined(ART_USE_PORTABLE_COMPILER) compiled_method->AddOatdataOffsetToCompliledCodeOffset( @@ -358,7 +400,7 @@ size_t OatWriter::InitOatCodeMethod(size_t offset, size_t oat_class_index, #if !defined(NDEBUG) // We expect GC maps except when the class hasn't been verified or the method is native - ClassReference class_ref(dex_file, class_def_index); + ClassReference class_ref(&dex_file, class_def_index); CompiledClass* compiled_class = compiler_driver_->GetCompiledClass(class_ref); mirror::Class::Status status; if (compiled_class != NULL) { @@ -371,7 +413,7 @@ size_t OatWriter::InitOatCodeMethod(size_t offset, size_t oat_class_index, CHECK(gc_map_size != 0 || is_native || status < mirror::Class::kStatusVerified) << &gc_map << " " << gc_map_size << " " << (is_native ? "true" : "false") << " " << (status < mirror::Class::kStatusVerified) << " " << status << " " - << PrettyMethod(method_idx, *dex_file); + << PrettyMethod(method_idx, dex_file); #endif // Deduplicate GC maps @@ -384,24 +426,26 @@ size_t OatWriter::InitOatCodeMethod(size_t offset, size_t oat_class_index, offset += gc_map_size; oat_header_->UpdateChecksum(&gc_map[0], gc_map_size); } + + oat_class->method_offsets_[*method_offsets_index] = + OatMethodOffsets(code_offset, + frame_size_in_bytes, + core_spill_mask, + fp_spill_mask, + mapping_table_offset, + vmap_table_offset, + gc_map_offset); + (*method_offsets_index)++; } - oat_class->method_offsets_[class_def_method_index] = - OatMethodOffsets(code_offset, - frame_size_in_bytes, - core_spill_mask, - fp_spill_mask, - mapping_table_offset, - vmap_table_offset, - gc_map_offset); if (compiler_driver_->IsImage()) { ClassLinker* linker = Runtime::Current()->GetClassLinker(); - mirror::DexCache* dex_cache = linker->FindDexCache(*dex_file); + mirror::DexCache* dex_cache = linker->FindDexCache(dex_file); // Unchecked as we hold mutator_lock_ on entry. ScopedObjectAccessUnchecked soa(Thread::Current()); - mirror::ArtMethod* method = linker->ResolveMethod(*dex_file, method_idx, dex_cache, - NULL, NULL, invoke_type); + mirror::ArtMethod* method = linker->ResolveMethod(dex_file, method_idx, dex_cache, + NULL, NULL, invoke_type); CHECK(method != NULL); method->SetFrameSizeInBytes(frame_size_in_bytes); method->SetCoreSpillMask(core_spill_mask); @@ -491,7 +535,9 @@ bool OatWriter::Write(OutputStream& out) { DO_STAT(size_oat_dex_file_location_checksum_); DO_STAT(size_oat_dex_file_offset_); DO_STAT(size_oat_dex_file_methods_offsets_); + DO_STAT(size_oat_class_type_); DO_STAT(size_oat_class_status_); + DO_STAT(size_oat_class_method_bitmaps_); DO_STAT(size_oat_class_method_offsets_); #undef DO_STAT @@ -586,7 +632,7 @@ size_t OatWriter::WriteCodeDexFiles(OutputStream& out, for (size_t i = 0; i != oat_dex_files_.size(); ++i) { const DexFile* dex_file = (*dex_files_)[i]; CHECK(dex_file != NULL); - relative_offset = WriteCodeDexFile(out, file_offset, relative_offset, oat_class_index, + relative_offset = WriteCodeDexFile(out, file_offset, relative_offset, &oat_class_index, *dex_file); if (relative_offset == 0) { return 0; @@ -596,12 +642,12 @@ size_t OatWriter::WriteCodeDexFiles(OutputStream& out, } size_t OatWriter::WriteCodeDexFile(OutputStream& out, const size_t file_offset, - size_t relative_offset, size_t& oat_class_index, + size_t relative_offset, size_t* oat_class_index, const DexFile& dex_file) { for (size_t class_def_index = 0; class_def_index < dex_file.NumClassDefs(); - class_def_index++, oat_class_index++) { + class_def_index++, (*oat_class_index)++) { const DexFile::ClassDef& class_def = dex_file.GetClassDef(class_def_index); - relative_offset = WriteCodeClassDef(out, file_offset, relative_offset, oat_class_index, + relative_offset = WriteCodeClassDef(out, file_offset, relative_offset, *oat_class_index, dex_file, class_def); if (relative_offset == 0) { return 0; @@ -637,11 +683,12 @@ size_t OatWriter::WriteCodeClassDef(OutputStream& out, } // Process methods size_t class_def_method_index = 0; + size_t method_offsets_index = 0; while (it.HasNextDirectMethod()) { bool is_static = (it.GetMemberAccessFlags() & kAccStatic) != 0; relative_offset = WriteCodeMethod(out, file_offset, relative_offset, oat_class_index, - class_def_method_index, is_static, it.GetMemberIndex(), - dex_file); + class_def_method_index, &method_offsets_index, is_static, + it.GetMemberIndex(), dex_file); if (relative_offset == 0) { return 0; } @@ -650,28 +697,30 @@ size_t OatWriter::WriteCodeClassDef(OutputStream& out, } while (it.HasNextVirtualMethod()) { relative_offset = WriteCodeMethod(out, file_offset, relative_offset, oat_class_index, - class_def_method_index, false, it.GetMemberIndex(), dex_file); + class_def_method_index, &method_offsets_index, false, + it.GetMemberIndex(), dex_file); if (relative_offset == 0) { return 0; } class_def_method_index++; it.Next(); } + DCHECK(!it.HasNext()); + CHECK_LE(method_offsets_index, class_def_method_index); return relative_offset; } size_t OatWriter::WriteCodeMethod(OutputStream& out, const size_t file_offset, size_t relative_offset, size_t oat_class_index, - size_t class_def_method_index, bool is_static, - uint32_t method_idx, const DexFile& dex_file) { - const CompiledMethod* compiled_method = - compiler_driver_->GetCompiledMethod(MethodReference(&dex_file, method_idx)); - - const OatMethodOffsets& method_offsets = - oat_classes_[oat_class_index]->method_offsets_[class_def_method_index]; - + size_t class_def_method_index, size_t* method_offsets_index, + bool is_static, uint32_t method_idx, const DexFile& dex_file) { + OatClass* oat_class = oat_classes_[oat_class_index]; + const CompiledMethod* compiled_method = oat_class->GetCompiledMethod(class_def_method_index); if (compiled_method != NULL) { // ie. not an abstract method + const OatMethodOffsets method_offsets = oat_class->method_offsets_[*method_offsets_index]; + (*method_offsets_index)++; + #if !defined(ART_USE_PORTABLE_COMPILER) uint32_t aligned_offset = compiled_method->AlignCode(relative_offset); uint32_t aligned_code_delta = aligned_offset - relative_offset; @@ -854,29 +903,96 @@ bool OatWriter::OatDexFile::Write(OatWriter* oat_writer, return true; } -OatWriter::OatClass::OatClass(size_t offset, mirror::Class::Status status, uint32_t methods_count) { +OatWriter::OatClass::OatClass(size_t offset, + std::vector* compiled_methods, + uint32_t num_non_null_compiled_methods, + mirror::Class::Status status) { + CHECK(compiled_methods != NULL); + uint32_t num_methods = compiled_methods->size(); + CHECK_LE(num_non_null_compiled_methods, num_methods); + offset_ = offset; + compiled_methods_ = compiled_methods; + oat_method_offsets_offsets_from_oat_class_.resize(num_methods); + + // Since both kOatClassNoneCompiled and kOatClassAllCompiled could + // apply when there are 0 methods, we just arbitrarily say that 0 + // methods means kOatClassNoneCompiled and that we won't use + // kOatClassAllCompiled unless there is at least one compiled + // method. This means in an interpretter only system, we can assert + // that all classes are kOatClassNoneCompiled. + if (num_non_null_compiled_methods == 0) { + type_ = kOatClassNoneCompiled; + } else if (num_non_null_compiled_methods == num_methods) { + type_ = kOatClassAllCompiled; + } else { + type_ = kOatClassSomeCompiled; + } + status_ = status; - method_offsets_.resize(methods_count); + method_offsets_.resize(num_non_null_compiled_methods); + + uint32_t oat_method_offsets_offset_from_oat_class = sizeof(type_) + sizeof(status_); + if (type_ == kOatClassSomeCompiled) { + method_bitmap_ = new BitVector(num_methods, false, Allocator::GetMallocAllocator()); + method_bitmap_size_ = method_bitmap_->GetSizeOf(); + oat_method_offsets_offset_from_oat_class += sizeof(method_bitmap_size_); + oat_method_offsets_offset_from_oat_class += method_bitmap_size_; + } else { + method_bitmap_ = NULL; + method_bitmap_size_ = 0; + } + + for (size_t i = 0; i < num_methods; i++) { + CompiledMethod* compiled_method = (*compiled_methods_)[i]; + if (compiled_method == NULL) { + oat_method_offsets_offsets_from_oat_class_[i] = 0; + } else { + oat_method_offsets_offsets_from_oat_class_[i] = oat_method_offsets_offset_from_oat_class; + oat_method_offsets_offset_from_oat_class += sizeof(OatMethodOffsets); + if (type_ == kOatClassSomeCompiled) { + method_bitmap_->SetBit(i); + } + } + } } +OatWriter::OatClass::~OatClass() { + delete compiled_methods_; +} + +#if defined(ART_USE_PORTABLE_COMPILER) size_t OatWriter::OatClass::GetOatMethodOffsetsOffsetFromOatHeader( size_t class_def_method_index_) const { - return offset_ + GetOatMethodOffsetsOffsetFromOatClass(class_def_method_index_); + uint32_t method_offset = GetOatMethodOffsetsOffsetFromOatClass(class_def_method_index_); + if (method_offset == 0) { + return 0; + } + return offset_ + method_offset; } size_t OatWriter::OatClass::GetOatMethodOffsetsOffsetFromOatClass( size_t class_def_method_index_) const { - return sizeof(status_) - + (sizeof(method_offsets_[0]) * class_def_method_index_); + return oat_method_offsets_offsets_from_oat_class_[class_def_method_index_]; } +#endif size_t OatWriter::OatClass::SizeOf() const { - return GetOatMethodOffsetsOffsetFromOatClass(method_offsets_.size()); + return sizeof(status_) + + sizeof(type_) + + ((method_bitmap_size_ == 0) ? 0 : sizeof(method_bitmap_size_)) + + method_bitmap_size_ + + (sizeof(method_offsets_[0]) * method_offsets_.size()); } void OatWriter::OatClass::UpdateChecksum(OatHeader& oat_header) const { oat_header.UpdateChecksum(&status_, sizeof(status_)); + oat_header.UpdateChecksum(&type_, sizeof(type_)); + if (method_bitmap_size_ != 0) { + CHECK_EQ(kOatClassSomeCompiled, type_); + oat_header.UpdateChecksum(&method_bitmap_size_, sizeof(method_bitmap_size_)); + oat_header.UpdateChecksum(method_bitmap_->GetRawStorage(), method_bitmap_size_); + } oat_header.UpdateChecksum(&method_offsets_[0], sizeof(method_offsets_[0]) * method_offsets_.size()); } @@ -890,17 +1006,30 @@ bool OatWriter::OatClass::Write(OatWriter* oat_writer, return false; } oat_writer->size_oat_class_status_ += sizeof(status_); - DCHECK_EQ(static_cast(file_offset + GetOatMethodOffsetsOffsetFromOatHeader(0)), - out.Seek(0, kSeekCurrent)); + if (!out.WriteFully(&type_, sizeof(type_))) { + PLOG(ERROR) << "Failed to write oat class type to " << out.GetLocation(); + return false; + } + oat_writer->size_oat_class_type_ += sizeof(type_); + if (method_bitmap_size_ != 0) { + CHECK_EQ(kOatClassSomeCompiled, type_); + if (!out.WriteFully(&method_bitmap_size_, sizeof(method_bitmap_size_))) { + PLOG(ERROR) << "Failed to write method bitmap size to " << out.GetLocation(); + return false; + } + oat_writer->size_oat_class_method_bitmaps_ += sizeof(method_bitmap_size_); + if (!out.WriteFully(method_bitmap_->GetRawStorage(), method_bitmap_size_)) { + PLOG(ERROR) << "Failed to write method bitmap to " << out.GetLocation(); + return false; + } + oat_writer->size_oat_class_method_bitmaps_ += method_bitmap_size_; + } if (!out.WriteFully(&method_offsets_[0], sizeof(method_offsets_[0]) * method_offsets_.size())) { PLOG(ERROR) << "Failed to write method offsets to " << out.GetLocation(); return false; } oat_writer->size_oat_class_method_offsets_ += sizeof(method_offsets_[0]) * method_offsets_.size(); - DCHECK_EQ(static_cast(file_offset + - GetOatMethodOffsetsOffsetFromOatHeader(method_offsets_.size())), - out.Seek(0, kSeekCurrent)); return true; } diff --git a/compiler/oat_writer.h b/compiler/oat_writer.h index d5f7e21a1a9..e3cb0a86ed6 100644 --- a/compiler/oat_writer.h +++ b/compiler/oat_writer.h @@ -30,6 +30,7 @@ namespace art { +class BitVector; class OutputStream; // OatHeader variable length with count of D OatDexFiles @@ -90,7 +91,7 @@ class OatWriter { size_t InitOatCodeDexFiles(size_t offset) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); size_t InitOatCodeDexFile(size_t offset, - size_t& oat_class_index, + size_t* oat_class_index, const DexFile& dex_file) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); size_t InitOatCodeClassDef(size_t offset, @@ -99,21 +100,22 @@ class OatWriter { const DexFile::ClassDef& class_def) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); size_t InitOatCodeMethod(size_t offset, size_t oat_class_index, size_t class_def_index, - size_t class_def_method_index, bool is_native, InvokeType type, - uint32_t method_idx, const DexFile*) + size_t class_def_method_index, size_t* method_offsets_index, + bool is_native, InvokeType type, uint32_t method_idx, const DexFile&) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); bool WriteTables(OutputStream& out, const size_t file_offset); size_t WriteCode(OutputStream& out, const size_t file_offset); size_t WriteCodeDexFiles(OutputStream& out, const size_t file_offset, size_t relative_offset); size_t WriteCodeDexFile(OutputStream& out, const size_t file_offset, size_t relative_offset, - size_t& oat_class_index, const DexFile& dex_file); + size_t* oat_class_index, const DexFile& dex_file); size_t WriteCodeClassDef(OutputStream& out, const size_t file_offset, size_t relative_offset, size_t oat_class_index, const DexFile& dex_file, const DexFile::ClassDef& class_def); size_t WriteCodeMethod(OutputStream& out, const size_t file_offset, size_t relative_offset, - size_t oat_class_index, size_t class_def_method_index, bool is_static, - uint32_t method_idx, const DexFile& dex_file); + size_t oat_class_index, size_t class_def_method_index, + size_t* method_offsets_index, bool is_static, uint32_t method_idx, + const DexFile& dex_file); void ReportWriteFailure(const char* what, uint32_t method_idx, const DexFile& dex_file, OutputStream& out) const; @@ -142,13 +144,24 @@ class OatWriter { class OatClass { public: - explicit OatClass(size_t offset, mirror::Class::Status status, uint32_t methods_count); + explicit OatClass(size_t offset, + std::vector* compiled_methods, + uint32_t num_non_null_compiled_methods, + mirror::Class::Status status); + ~OatClass(); +#if defined(ART_USE_PORTABLE_COMPILER) size_t GetOatMethodOffsetsOffsetFromOatHeader(size_t class_def_method_index_) const; size_t GetOatMethodOffsetsOffsetFromOatClass(size_t class_def_method_index_) const; +#endif size_t SizeOf() const; void UpdateChecksum(OatHeader& oat_header) const; bool Write(OatWriter* oat_writer, OutputStream& out, const size_t file_offset) const; + CompiledMethod* GetCompiledMethod(size_t class_def_method_index) const { + DCHECK(compiled_methods_ != NULL); + return (*compiled_methods_)[class_def_method_index]; + } + // Offset of start of OatClass from beginning of OatHeader. It is // used to validate file position when writing. For Portable, it // is also used to calculate the position of the OatMethodOffsets @@ -156,8 +169,37 @@ class OatWriter { // patched to point to code in the Portable .o ELF objects. size_t offset_; + // CompiledMethods for each class_def_method_index, or NULL if no method is available. + std::vector* compiled_methods_; + + // Offset from OatClass::offset_ to the OatMethodOffsets for the + // class_def_method_index. If 0, it means the corresponding + // CompiledMethod entry in OatClass::compiled_methods_ should be + // NULL and that the OatClass::type_ should be kOatClassBitmap. + std::vector oat_method_offsets_offsets_from_oat_class_; + // data to write - mirror::Class::Status status_; + + COMPILE_ASSERT(mirror::Class::Status::kStatusMax < (2 ^ 16), class_status_wont_fit_in_16bits); + int16_t status_; + + COMPILE_ASSERT(OatClassType::kOatClassMax < (2 ^ 16), oat_class_type_wont_fit_in_16bits); + uint16_t type_; + + uint32_t method_bitmap_size_; + + // bit vector indexed by ClassDef method index. When + // OatClassType::type_ is kOatClassBitmap, a set bit indicates the + // method has an OatMethodOffsets in methods_offsets_, otherwise + // the entry was ommited to save space. If OatClassType::type_ is + // not is kOatClassBitmap, the bitmap will be NULL. + BitVector* method_bitmap_; + + // OatMethodOffsets for each CompiledMethod present in the + // OatClass. Note that some may be missing if + // OatClass::compiled_methods_ contains NULL values (and + // oat_method_offsets_offsets_from_oat_class_ should contain 0 + // values in this case). std::vector method_offsets_; private: @@ -214,7 +256,9 @@ class OatWriter { uint32_t size_oat_dex_file_location_checksum_; uint32_t size_oat_dex_file_offset_; uint32_t size_oat_dex_file_methods_offsets_; + uint32_t size_oat_class_type_; uint32_t size_oat_class_status_; + uint32_t size_oat_class_method_bitmaps_; uint32_t size_oat_class_method_offsets_; // Code mappings for deduplication. Deduplication is already done on a pointer basis by the diff --git a/compiler/utils/dedupe_set.h b/compiler/utils/dedupe_set.h index 53c1afa6983..638e0ec457f 100644 --- a/compiler/utils/dedupe_set.h +++ b/compiler/utils/dedupe_set.h @@ -22,6 +22,7 @@ #include "base/mutex.h" #include "base/stl_util.h" +#include "base/stringprintf.h" namespace art { diff --git a/compiler/utils/dedupe_set_test.cc b/compiler/utils/dedupe_set_test.cc index 2c6787b8a6e..8abe6debc12 100644 --- a/compiler/utils/dedupe_set_test.cc +++ b/compiler/utils/dedupe_set_test.cc @@ -14,8 +14,9 @@ * limitations under the License. */ -#include "common_test.h" #include "dedupe_set.h" +#include "gtest/gtest.h" +#include "thread-inl.h" namespace art { diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc index ea06b02d4f6..fdeeaecd93d 100644 --- a/oatdump/oatdump.cc +++ b/oatdump/oatdump.cc @@ -263,8 +263,10 @@ class OatDumper { const char* descriptor = dex_file->GetClassDescriptor(class_def); UniquePtr oat_class(oat_dex_file.GetOatClass(class_def_index)); CHECK(oat_class.get() != NULL); - os << StringPrintf("%zd: %s (type_idx=%d) (", class_def_index, descriptor, class_def.class_idx_) - << oat_class->GetStatus() << ")\n"; + os << StringPrintf("%zd: %s (type_idx=%d)", class_def_index, descriptor, class_def.class_idx_) + << " (" << oat_class->GetStatus() << ")" + << " (" << oat_class->GetType() << ")\n"; + // TODO: include bitmap here if type is kOatClassBitmap? Indenter indent_filter(os.rdbuf(), kIndentChar, kIndentBy1Count); std::ostream indented_os(&indent_filter); DumpOatClass(indented_os, *oat_class.get(), *(dex_file.get()), class_def); diff --git a/runtime/Android.mk b/runtime/Android.mk index 8579222ed6b..e4b7e47fe38 100644 --- a/runtime/Android.mk +++ b/runtime/Android.mk @@ -21,6 +21,8 @@ include art/build/Android.common.mk LIBART_COMMON_SRC_FILES := \ atomic.cc.arm \ barrier.cc \ + base/allocator.cc \ + base/bit_vector.cc \ base/logging.cc \ base/mutex.cc \ base/stringpiece.cc \ @@ -247,6 +249,7 @@ LIBART_ENUM_OPERATOR_OUT_HEADER_FILES := \ locks.h \ lock_word.h \ mirror/class.h \ + oat.h \ thread.h \ thread_state.h \ verifier/method_verifier.h diff --git a/compiler/utils/allocator.cc b/runtime/base/allocator.cc similarity index 100% rename from compiler/utils/allocator.cc rename to runtime/base/allocator.cc diff --git a/compiler/utils/allocator.h b/runtime/base/allocator.h similarity index 88% rename from compiler/utils/allocator.h rename to runtime/base/allocator.h index 3482a7928ea..917bf0b6e3d 100644 --- a/compiler/utils/allocator.h +++ b/runtime/base/allocator.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef ART_COMPILER_UTILS_ALLOCATOR_H_ -#define ART_COMPILER_UTILS_ALLOCATOR_H_ +#ifndef ART_RUNTIME_BASE_ALLOCATOR_H_ +#define ART_RUNTIME_BASE_ALLOCATOR_H_ #include "base/macros.h" @@ -38,4 +38,4 @@ class Allocator { } // namespace art -#endif // ART_COMPILER_UTILS_ALLOCATOR_H_ +#endif // ART_RUNTIME_BASE_ALLOCATOR_H_ diff --git a/compiler/utils/bit_vector.cc b/runtime/base/bit_vector.cc similarity index 70% rename from compiler/utils/bit_vector.cc rename to runtime/base/bit_vector.cc index 81a639a0501..3b826514981 100644 --- a/compiler/utils/bit_vector.cc +++ b/runtime/base/bit_vector.cc @@ -28,14 +28,14 @@ static uint32_t check_masks[32] = { 0x02000000, 0x04000000, 0x08000000, 0x10000000, 0x20000000, 0x40000000, 0x80000000 }; -static inline uint32_t BitsToWords(unsigned int bits) { +static inline uint32_t BitsToWords(uint32_t bits) { return (bits + 31) >> 5; } // TODO: replace excessive argument defaulting when we are at gcc 4.7 // or later on host with delegating constructor support. Specifically, // starts_bits and storage_size/storage are mutually exclusive. -BitVector::BitVector(unsigned int start_bits, +BitVector::BitVector(uint32_t start_bits, bool expandable, Allocator* allocator, uint32_t storage_size, @@ -58,10 +58,10 @@ BitVector::~BitVector() { /* * Determine whether or not the specified bit is set. */ -bool BitVector::IsBitSet(unsigned int num) { +bool BitVector::IsBitSet(uint32_t num) const { DCHECK_LT(num, storage_size_ * sizeof(uint32_t) * 8); - unsigned int val = storage_[num >> 5] & check_masks[num & 0x1f]; + uint32_t val = storage_[num >> 5] & check_masks[num & 0x1f]; return (val != 0); } @@ -75,12 +75,12 @@ void BitVector::ClearAllBits() { * TUNING: this could have pathologically bad growth/expand behavior. Make sure we're * not using it badly or change resize mechanism. */ -void BitVector::SetBit(unsigned int num) { +void BitVector::SetBit(uint32_t num) { if (num >= storage_size_ * sizeof(uint32_t) * 8) { DCHECK(expandable_) << "Attempted to expand a non-expandable bitmap to position " << num; /* Round up to word boundaries for "num+1" bits */ - unsigned int new_size = BitsToWords(num + 1); + uint32_t new_size = BitsToWords(num + 1); DCHECK_GT(new_size, storage_size_); uint32_t *new_storage = static_cast(allocator_->Alloc(new_size * sizeof(uint32_t))); @@ -96,7 +96,7 @@ void BitVector::SetBit(unsigned int num) { } // Mark the specified bit as "unset". -void BitVector::ClearBit(unsigned int num) { +void BitVector::ClearBit(uint32_t num) { DCHECK_LT(num, storage_size_ * sizeof(uint32_t) * 8); storage_[num >> 5] &= ~check_masks[num & 0x1f]; } @@ -105,7 +105,7 @@ void BitVector::ClearBit(unsigned int num) { void BitVector::Intersect(const BitVector* src) { DCHECK_EQ(storage_size_, src->GetStorageSize()); DCHECK_EQ(expandable_, src->IsExpandable()); - for (unsigned int idx = 0; idx < storage_size_; idx++) { + for (uint32_t idx = 0; idx < storage_size_; idx++) { storage_[idx] &= src->GetRawStorageWord(idx); } } @@ -116,22 +116,44 @@ void BitVector::Intersect(const BitVector* src) { void BitVector::Union(const BitVector* src) { DCHECK_EQ(storage_size_, src->GetStorageSize()); DCHECK_EQ(expandable_, src->IsExpandable()); - for (unsigned int idx = 0; idx < storage_size_; idx++) { + for (uint32_t idx = 0; idx < storage_size_; idx++) { storage_[idx] |= src->GetRawStorageWord(idx); } } // Count the number of bits that are set. -int BitVector::NumSetBits() { - unsigned int count = 0; +uint32_t BitVector::NumSetBits() const { + uint32_t count = 0; + for (uint32_t word = 0; word < storage_size_; word++) { + count += __builtin_popcount(storage_[word]); + } + return count; +} - for (unsigned int word = 0; word < storage_size_; word++) { +// Count the number of bits that are set up through and including num. +uint32_t BitVector::NumSetBits(uint32_t num) const { + DCHECK_LT(num, storage_size_ * sizeof(uint32_t) * 8); + uint32_t last_word = num >> 5; + uint32_t partial_word_bits = num & 0x1f; + + // partial_word_bits | # | | | partial_word_mask + // 00000 | 0 | 0xffffffff >> (31 - 0) | (1 << (0 + 1)) - 1 | 0x00000001 + // 00001 | 1 | 0xffffffff >> (31 - 1) | (1 << (1 + 1)) - 1 | 0x00000003 + // 00010 | 2 | 0xffffffff >> (31 - 2) | (1 << (2 + 1)) - 1 | 0x00000007 + // ..... | + // 11110 | 30 | 0xffffffff >> (31 - 30) | (1 << (30 + 1)) - 1 | 0x7fffffff + // 11111 | 31 | 0xffffffff >> (31 - 31) | last_full_word++ | 0xffffffff + uint32_t partial_word_mask = 0xffffffff >> (0x1f - partial_word_bits); + + uint32_t count = 0; + for (uint32_t word = 0; word < last_word; word++) { count += __builtin_popcount(storage_[word]); } + count += __builtin_popcount(storage_[last_word] & partial_word_mask); return count; } -BitVector::Iterator* BitVector::GetIterator() { +BitVector::Iterator* BitVector::GetIterator() const { return new (allocator_) Iterator(this); } @@ -140,13 +162,13 @@ BitVector::Iterator* BitVector::GetIterator() { * since there might be unused bits - setting those to one will confuse the * iterator. */ -void BitVector::SetInitialBits(unsigned int num_bits) { +void BitVector::SetInitialBits(uint32_t num_bits) { DCHECK_LE(BitsToWords(num_bits), storage_size_); - unsigned int idx; + uint32_t idx; for (idx = 0; idx < (num_bits >> 5); idx++) { storage_[idx] = -1; } - unsigned int rem_num_bits = num_bits & 0x1f; + uint32_t rem_num_bits = num_bits & 0x1f; if (rem_num_bits) { storage_[idx] = (1 << rem_num_bits) - 1; } diff --git a/compiler/utils/bit_vector.h b/runtime/base/bit_vector.h similarity index 89% rename from compiler/utils/bit_vector.h rename to runtime/base/bit_vector.h index bf0f7c32e1f..74bec087a33 100644 --- a/compiler/utils/bit_vector.h +++ b/runtime/base/bit_vector.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef ART_COMPILER_UTILS_BIT_VECTOR_H_ -#define ART_COMPILER_UTILS_BIT_VECTOR_H_ +#ifndef ART_RUNTIME_BASE_BIT_VECTOR_H_ +#define ART_RUNTIME_BASE_BIT_VECTOR_H_ #include #include @@ -34,7 +34,7 @@ class BitVector { public: class Iterator { public: - explicit Iterator(BitVector* bit_vector) + explicit Iterator(const BitVector* bit_vector) : p_bits_(bit_vector), bit_storage_(bit_vector->GetRawStorage()), bit_index_(0), @@ -77,8 +77,8 @@ class BitVector { } private: - BitVector* const p_bits_; - uint32_t* const bit_storage_; + const BitVector* const p_bits_; + const uint32_t* const bit_storage_; uint32_t bit_index_; // Current index (size in bits). const uint32_t bit_size_; // Size of vector in bits. @@ -95,9 +95,7 @@ class BitVector { void SetBit(uint32_t num); void ClearBit(uint32_t num); - void MarkAllBits(bool set); - void DebugBitVector(char* msg, int length); - bool IsBitSet(uint32_t num); + bool IsBitSet(uint32_t num) const; void ClearAllBits(); void SetInitialBits(uint32_t num_bits); void Copy(BitVector* src) { @@ -111,15 +109,17 @@ class BitVector { (expandable_ == src->IsExpandable()) && (memcmp(storage_, src->GetRawStorage(), storage_size_ * sizeof(uint32_t)) == 0); } - int32_t NumSetBits(); + uint32_t NumSetBits() const; + uint32_t NumSetBits(uint32_t num) const; - Iterator* GetIterator(); + Iterator* GetIterator() const; uint32_t GetStorageSize() const { return storage_size_; } bool IsExpandable() const { return expandable_; } uint32_t GetRawStorageWord(size_t idx) const { return storage_[idx]; } uint32_t* GetRawStorage() { return storage_; } const uint32_t* GetRawStorage() const { return storage_; } + size_t GetSizeOf() const { return storage_size_ * sizeof(uint32_t); } private: Allocator* const allocator_; @@ -131,4 +131,4 @@ class BitVector { } // namespace art -#endif // ART_COMPILER_UTILS_BIT_VECTOR_H_ +#endif // ART_RUNTIME_BASE_BIT_VECTOR_H_ diff --git a/compiler/utils/bit_vector_test.cc b/runtime/base/bit_vector_test.cc similarity index 68% rename from compiler/utils/bit_vector_test.cc rename to runtime/base/bit_vector_test.cc index 5c18ec53d3e..d99d059e0fc 100644 --- a/compiler/utils/bit_vector_test.cc +++ b/runtime/base/bit_vector_test.cc @@ -14,9 +14,9 @@ * limitations under the License. */ -#include "common_test.h" -#include "bit_vector.h" #include "UniquePtr.h" +#include "bit_vector.h" +#include "gtest/gtest.h" namespace art { @@ -25,9 +25,12 @@ TEST(BitVector, Test) { BitVector bv(kBits, false, Allocator::GetMallocAllocator()); EXPECT_EQ(1U, bv.GetStorageSize()); + EXPECT_EQ(kWordSize, bv.GetSizeOf()); EXPECT_FALSE(bv.IsExpandable()); - EXPECT_EQ(0, bv.NumSetBits()); + EXPECT_EQ(0U, bv.NumSetBits()); + EXPECT_EQ(0U, bv.NumSetBits(0)); + EXPECT_EQ(0U, bv.NumSetBits(kBits - 1)); for (size_t i = 0; i < kBits; i++) { EXPECT_FALSE(bv.IsBitSet(i)); } @@ -42,7 +45,9 @@ TEST(BitVector, Test) { bv.SetBit(0); bv.SetBit(kBits - 1); - EXPECT_EQ(2, bv.NumSetBits()); + EXPECT_EQ(2U, bv.NumSetBits()); + EXPECT_EQ(1U, bv.NumSetBits(0)); + EXPECT_EQ(2U, bv.NumSetBits(kBits - 1)); EXPECT_TRUE(bv.IsBitSet(0)); for (size_t i = 1; i < kBits - 1; i++) { EXPECT_FALSE(bv.IsBitSet(i)); @@ -65,32 +70,53 @@ TEST(BitVector, NoopAllocator) { BitVector bv(0U, false, Allocator::GetNoopAllocator(), kWords, bits); EXPECT_EQ(kWords, bv.GetStorageSize()); + EXPECT_EQ(kWords * kWordSize, bv.GetSizeOf()); EXPECT_EQ(bits, bv.GetRawStorage()); - EXPECT_EQ(0, bv.NumSetBits()); + EXPECT_EQ(0U, bv.NumSetBits()); bv.SetBit(8); - EXPECT_EQ(1, bv.NumSetBits()); + EXPECT_EQ(1U, bv.NumSetBits()); EXPECT_EQ(0x00000100U, bv.GetRawStorageWord(0)); EXPECT_EQ(0x00000000U, bv.GetRawStorageWord(1)); - EXPECT_EQ(1, bv.NumSetBits()); + EXPECT_EQ(1U, bv.NumSetBits()); bv.SetBit(16); - EXPECT_EQ(2, bv.NumSetBits()); + EXPECT_EQ(2U, bv.NumSetBits()); EXPECT_EQ(0x00010100U, bv.GetRawStorageWord(0)); EXPECT_EQ(0x00000000U, bv.GetRawStorageWord(1)); - EXPECT_EQ(2, bv.NumSetBits()); + EXPECT_EQ(2U, bv.NumSetBits()); bv.SetBit(32); - EXPECT_EQ(3, bv.NumSetBits()); + EXPECT_EQ(3U, bv.NumSetBits()); EXPECT_EQ(0x00010100U, bv.GetRawStorageWord(0)); EXPECT_EQ(0x00000001U, bv.GetRawStorageWord(1)); - EXPECT_EQ(3, bv.NumSetBits()); + EXPECT_EQ(3U, bv.NumSetBits()); bv.SetBit(48); - EXPECT_EQ(4, bv.NumSetBits()); + EXPECT_EQ(4U, bv.NumSetBits()); EXPECT_EQ(0x00010100U, bv.GetRawStorageWord(0)); EXPECT_EQ(0x00010001U, bv.GetRawStorageWord(1)); - EXPECT_EQ(4, bv.NumSetBits()); + EXPECT_EQ(4U, bv.NumSetBits()); + + EXPECT_EQ(0U, bv.NumSetBits(0)); + + EXPECT_EQ(0U, bv.NumSetBits(7)); + EXPECT_EQ(1U, bv.NumSetBits(8)); + EXPECT_EQ(1U, bv.NumSetBits(9)); + + EXPECT_EQ(1U, bv.NumSetBits(15)); + EXPECT_EQ(2U, bv.NumSetBits(16)); + EXPECT_EQ(2U, bv.NumSetBits(17)); + + EXPECT_EQ(2U, bv.NumSetBits(31)); + EXPECT_EQ(3U, bv.NumSetBits(32)); + EXPECT_EQ(3U, bv.NumSetBits(33)); + + EXPECT_EQ(3U, bv.NumSetBits(47)); + EXPECT_EQ(4U, bv.NumSetBits(48)); + EXPECT_EQ(4U, bv.NumSetBits(49)); + + EXPECT_EQ(4U, bv.NumSetBits(63)); } } // namespace art diff --git a/runtime/exception_test.cc b/runtime/exception_test.cc index 2e6b0a8f564..a5f999784d3 100644 --- a/runtime/exception_test.cc +++ b/runtime/exception_test.cc @@ -146,7 +146,7 @@ TEST_F(ExceptionTest, StackTraceElement) { ScopedObjectAccess soa(env); std::vector fake_stack; - ASSERT_EQ(kStackAlignment, 16); + ASSERT_EQ(kStackAlignment, 16U); ASSERT_EQ(sizeof(uintptr_t), sizeof(uint32_t)); #if !defined(ART_USE_PORTABLE_COMPILER) diff --git a/runtime/globals.h b/runtime/globals.h index c3974943cd6..31574ff72d9 100644 --- a/runtime/globals.h +++ b/runtime/globals.h @@ -30,30 +30,30 @@ const size_t KB = 1024; const size_t MB = KB * KB; const size_t GB = KB * KB * KB; -const int kWordSize = sizeof(word); -const int kPointerSize = sizeof(void*); +const size_t kWordSize = sizeof(word); +const size_t kPointerSize = sizeof(void*); -const int kBitsPerByte = 8; -const int kBitsPerByteLog2 = 3; +const size_t kBitsPerByte = 8; +const size_t kBitsPerByteLog2 = 3; const int kBitsPerWord = kWordSize * kBitsPerByte; -const int kWordHighBitMask = 1 << (kBitsPerWord - 1); +const size_t kWordHighBitMask = 1 << (kBitsPerWord - 1); // Required stack alignment -const int kStackAlignment = 16; +const size_t kStackAlignment = 16; // Required object alignment -const int kObjectAlignment = 8; +const size_t kObjectAlignment = 8; // ARM instruction alignment. ARM processors require code to be 4-byte aligned, // but ARM ELF requires 8.. -const int kArmAlignment = 8; +const size_t kArmAlignment = 8; // MIPS instruction alignment. MIPS processors require code to be 4-byte aligned. // TODO: Can this be 4? -const int kMipsAlignment = 8; +const size_t kMipsAlignment = 8; // X86 instruction alignment. This is the recommended alignment for maximum performance. -const int kX86Alignment = 16; +const size_t kX86Alignment = 16; // System page size. We check this against sysconf(_SC_PAGE_SIZE) at runtime, but use a simple // compile-time constant so the compiler can generate better code. diff --git a/runtime/mirror/class.h b/runtime/mirror/class.h index dbc6f575e75..d15f3375fdb 100644 --- a/runtime/mirror/class.h +++ b/runtime/mirror/class.h @@ -122,6 +122,7 @@ class MANAGED Class : public StaticStorageBase { kStatusVerified = 7, // Logically part of linking; done pre-init. kStatusInitializing = 8, // Class init in progress. kStatusInitialized = 9, // Ready to go. + kStatusMax = 10, }; Status GetStatus() const { diff --git a/runtime/oat.cc b/runtime/oat.cc index c01f77c3645..6fe5d1097b6 100644 --- a/runtime/oat.cc +++ b/runtime/oat.cc @@ -22,7 +22,7 @@ namespace art { const uint8_t OatHeader::kOatMagic[] = { 'o', 'a', 't', '\n' }; -const uint8_t OatHeader::kOatVersion[] = { '0', '0', '7', '\0' }; +const uint8_t OatHeader::kOatVersion[] = { '0', '0', '8', '\0' }; OatHeader::OatHeader() { memset(this, 0, sizeof(*this)); diff --git a/runtime/oat.h b/runtime/oat.h index a653cf8d90c..a9dc540c032 100644 --- a/runtime/oat.h +++ b/runtime/oat.h @@ -104,6 +104,19 @@ class PACKED(4) OatHeader { DISALLOW_COPY_AND_ASSIGN(OatHeader); }; +// OatMethodOffsets are currently 7x32-bits=224-bits long, so if we can +// save even one OatMethodOffsets struct, the more complicated encoding +// using a bitmap pays for itself since few classes will have 224 +// methods. +enum OatClassType { + kOatClassAllCompiled = 0, // OatClass is followed by an OatMethodOffsets for each method. + kOatClassSomeCompiled = 1, // A bitmap of which OatMethodOffsets are present follows the OatClass. + kOatClassNoneCompiled = 2, // All methods are interpretted so no OatMethodOffsets are necessary. + kOatClassMax = 3, +}; + +std::ostream& operator<<(std::ostream& os, const OatClassType& rhs); + class PACKED(4) OatMethodOffsets { public: OatMethodOffsets(); diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc index 7553dcca341..fa2b4858227 100644 --- a/runtime/oat_file.cc +++ b/runtime/oat_file.cc @@ -18,6 +18,7 @@ #include +#include "base/bit_vector.h" #include "base/stl_util.h" #include "base/unix_file/fd_file.h" #include "elf_file.h" @@ -384,29 +385,92 @@ const OatFile::OatClass* OatFile::OatDexFile::GetOatClass(uint16_t class_def_ind const byte* oat_class_pointer = oat_file_->Begin() + oat_class_offset; CHECK_LT(oat_class_pointer, oat_file_->End()) << oat_file_->GetLocation(); - mirror::Class::Status status = *reinterpret_cast(oat_class_pointer); - const byte* methods_pointer = oat_class_pointer + sizeof(status); + const byte* status_pointer = oat_class_pointer; + CHECK_LT(status_pointer, oat_file_->End()) << oat_file_->GetLocation(); + mirror::Class::Status status = + static_cast(*reinterpret_cast(status_pointer)); + CHECK_LT(status, mirror::Class::kStatusMax); + + const byte* type_pointer = status_pointer + sizeof(uint16_t); + CHECK_LT(type_pointer, oat_file_->End()) << oat_file_->GetLocation(); + OatClassType type = static_cast(*reinterpret_cast(type_pointer)); + CHECK_LT(type, kOatClassMax); + + const byte* bitmap_pointer = type_pointer + sizeof(int16_t); + CHECK_LT(bitmap_pointer, oat_file_->End()) << oat_file_->GetLocation(); + uint32_t bitmap_size = 0; + if (type == kOatClassSomeCompiled) { + bitmap_size = static_cast(*reinterpret_cast(bitmap_pointer)); + bitmap_pointer += sizeof(bitmap_size); + CHECK_LT(bitmap_pointer, oat_file_->End()) << oat_file_->GetLocation(); + } + + const byte* methods_pointer = bitmap_pointer + bitmap_size; CHECK_LT(methods_pointer, oat_file_->End()) << oat_file_->GetLocation(); return new OatClass(oat_file_, status, + type, + bitmap_size, + reinterpret_cast(bitmap_pointer), reinterpret_cast(methods_pointer)); } OatFile::OatClass::OatClass(const OatFile* oat_file, mirror::Class::Status status, + OatClassType type, + uint32_t bitmap_size, + const uint32_t* bitmap_pointer, const OatMethodOffsets* methods_pointer) - : oat_file_(oat_file), status_(status), methods_pointer_(methods_pointer) {} - -OatFile::OatClass::~OatClass() {} + : oat_file_(oat_file), status_(status), type_(type), + bitmap_(NULL), methods_pointer_(methods_pointer) { + switch (type_) { + case kOatClassAllCompiled: { + CHECK_EQ(0U, bitmap_size); + break; + } + case kOatClassSomeCompiled: { + CHECK_NE(0U, bitmap_size); + bitmap_ = new BitVector(0, false, Allocator::GetNoopAllocator(), bitmap_size, + const_cast(bitmap_pointer)); + break; + } + case kOatClassNoneCompiled: { + CHECK_EQ(0U, bitmap_size); + methods_pointer_ = NULL; + break; + } + case kOatClassMax: { + LOG(FATAL) << "Invalid OatClassType " << type_; + break; + } + } +} -mirror::Class::Status OatFile::OatClass::GetStatus() const { - return status_; +OatFile::OatClass::~OatClass() { + delete bitmap_; } const OatFile::OatMethod OatFile::OatClass::GetOatMethod(uint32_t method_index) const { - const OatMethodOffsets& oat_method_offsets = methods_pointer_[method_index]; + if (methods_pointer_ == NULL) { + CHECK_EQ(kOatClassNoneCompiled, type_); + return OatMethod(NULL, 0, 0, 0, 0, 0, 0, 0); + } + size_t methods_pointer_index; + if (bitmap_ == NULL) { + CHECK_EQ(kOatClassAllCompiled, type_); + methods_pointer_index = method_index; + } else { + CHECK_EQ(kOatClassSomeCompiled, type_); + if (!bitmap_->IsBitSet(method_index)) { + return OatMethod(NULL, 0, 0, 0, 0, 0, 0, 0); + } + size_t num_set_bits = bitmap_->NumSetBits(method_index); + CHECK_NE(0U, num_set_bits); + methods_pointer_index = num_set_bits - 1; + } + const OatMethodOffsets& oat_method_offsets = methods_pointer_[methods_pointer_index]; return OatMethod( oat_file_->Begin(), oat_method_offsets.code_offset_, diff --git a/runtime/oat_file.h b/runtime/oat_file.h index af1476088d2..887a9d1b63e 100644 --- a/runtime/oat_file.h +++ b/runtime/oat_file.h @@ -29,6 +29,7 @@ namespace art { +class BitVector; class ElfFile; class MemMap; class OatMethodOffsets; @@ -145,7 +146,13 @@ class OatFile { class OatClass { public: - mirror::Class::Status GetStatus() const; + mirror::Class::Status GetStatus() const { + return status_; + } + + OatClassType GetType() const { + return type_; + } // get the OatMethod entry based on its index into the class // defintion. direct methods come first, followed by virtual @@ -157,10 +164,21 @@ class OatFile { private: OatClass(const OatFile* oat_file, mirror::Class::Status status, + OatClassType type, + uint32_t bitmap_size, + const uint32_t* bitmap_pointer, const OatMethodOffsets* methods_pointer); const OatFile* oat_file_; + const mirror::Class::Status status_; + COMPILE_ASSERT(mirror::Class::Status::kStatusMax < (2 ^ 16), class_status_wont_fit_in_16bits); + + OatClassType type_; + COMPILE_ASSERT(OatClassType::kOatClassMax < (2 ^ 16), oat_class_type_wont_fit_in_16bits); + + const BitVector* bitmap_; + const OatMethodOffsets* methods_pointer_; friend class OatDexFile; From 17088bbded68e35da8050a40206dfd3cbba9e6d2 Mon Sep 17 00:00:00 2001 From: Vladimir Marko Date: Mon, 28 Oct 2013 13:55:34 +0000 Subject: [PATCH 0109/2402] Fix invalid DCHECK for movzx/movsx. k86Movzx8RM and kMovsx8RM don't have to use eax/ecx/edx/ebx. The incorrect check could fail for LoadBaseDisp() with kUnsignedByte or kSignedByte. Change-Id: I777f14cf372c7b211ad8c595d4a8a47533bdd0fc --- compiler/dex/quick/x86/assemble_x86.cc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/compiler/dex/quick/x86/assemble_x86.cc b/compiler/dex/quick/x86/assemble_x86.cc index d66862bb9aa..92d58d5f3c7 100644 --- a/compiler/dex/quick/x86/assemble_x86.cc +++ b/compiler/dex/quick/x86/assemble_x86.cc @@ -596,7 +596,9 @@ void X86Mir2Lir::EmitMemReg(const X86EncodingMap* entry, reg = reg & X86_FP_REG_MASK; } if (reg >= 4) { - DCHECK(strchr(entry->name, '8') == NULL) << entry->name << " " << static_cast(reg) + DCHECK(strchr(entry->name, '8') == NULL || + entry->opcode == kX86Movzx8RM || entry->opcode == kX86Movsx8RM) + << entry->name << " " << static_cast(reg) << " in " << PrettyMethod(cu_->method_idx, *cu_->dex_file); } DCHECK_LT(reg, 8); From d4beb6bc2b42b176c6d04fdd91d6c758e542c7c2 Mon Sep 17 00:00:00 2001 From: Sebastien Hertz Date: Wed, 2 Oct 2013 17:07:20 +0200 Subject: [PATCH 0110/2402] Inline field and method resolution. According to profiling results, field and method resolutions are hot points during interpreter execution. This CL attempts to speed up these resolutions. Forces aggressive inlining of FindFieldFromCode and FindMethodFromCode. This allows to reduce the overhead of access check code when the interpreter runs without these checks. Templatize these functions to optimize inlining and their callers. Also spread the use of C++11 "nullptr" in place of "NULL" in field access and invoke helpers. Change-Id: Ic1a69834d8975b2cddcddaae32f08a7de146a951 --- runtime/entrypoints/entrypoint_utils.cc | 195 -------------- runtime/entrypoints/entrypoint_utils.h | 239 +++++++++++++++++- .../portable/portable_field_entrypoints.cc | 56 ++-- .../portable/portable_invoke_entrypoints.cc | 52 ++-- .../quick/quick_field_entrypoints.cc | 39 +-- .../quick/quick_invoke_entrypoints.cc | 42 ++- runtime/interpreter/interpreter_common.h | 41 ++- runtime/mirror/object_test.cc | 4 +- 8 files changed, 356 insertions(+), 312 deletions(-) diff --git a/runtime/entrypoints/entrypoint_utils.cc b/runtime/entrypoints/entrypoint_utils.cc index d9c9e3141ab..24ab1ce9dab 100644 --- a/runtime/entrypoints/entrypoint_utils.cc +++ b/runtime/entrypoints/entrypoint_utils.cc @@ -96,201 +96,6 @@ mirror::Array* CheckAndAllocArrayFromCodeInstrumented(uint32_t type_idx, mirror: return mirror::Array::AllocInstrumented(self, klass, component_count); } -mirror::ArtField* FindFieldFromCode(uint32_t field_idx, const mirror::ArtMethod* referrer, - Thread* self, FindFieldType type, size_t expected_size, - bool access_check) { - bool is_primitive; - bool is_set; - bool is_static; - switch (type) { - case InstanceObjectRead: is_primitive = false; is_set = false; is_static = false; break; - case InstanceObjectWrite: is_primitive = false; is_set = true; is_static = false; break; - case InstancePrimitiveRead: is_primitive = true; is_set = false; is_static = false; break; - case InstancePrimitiveWrite: is_primitive = true; is_set = true; is_static = false; break; - case StaticObjectRead: is_primitive = false; is_set = false; is_static = true; break; - case StaticObjectWrite: is_primitive = false; is_set = true; is_static = true; break; - case StaticPrimitiveRead: is_primitive = true; is_set = false; is_static = true; break; - case StaticPrimitiveWrite: // Keep GCC happy by having a default handler, fall-through. - default: is_primitive = true; is_set = true; is_static = true; break; - } - ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); - mirror::ArtField* resolved_field = class_linker->ResolveField(field_idx, referrer, is_static); - if (UNLIKELY(resolved_field == NULL)) { - DCHECK(self->IsExceptionPending()); // Throw exception and unwind. - return NULL; // Failure. - } - mirror::Class* fields_class = resolved_field->GetDeclaringClass(); - if (access_check) { - if (UNLIKELY(resolved_field->IsStatic() != is_static)) { - ThrowIncompatibleClassChangeErrorField(resolved_field, is_static, referrer); - return NULL; - } - mirror::Class* referring_class = referrer->GetDeclaringClass(); - if (UNLIKELY(!referring_class->CanAccess(fields_class) || - !referring_class->CanAccessMember(fields_class, - resolved_field->GetAccessFlags()))) { - // The referring class can't access the resolved field, this may occur as a result of a - // protected field being made public by a sub-class. Resort to the dex file to determine - // the correct class for the access check. - const DexFile& dex_file = *referring_class->GetDexCache()->GetDexFile(); - fields_class = class_linker->ResolveType(dex_file, - dex_file.GetFieldId(field_idx).class_idx_, - referring_class); - if (UNLIKELY(!referring_class->CanAccess(fields_class))) { - ThrowIllegalAccessErrorClass(referring_class, fields_class); - return NULL; // failure - } else if (UNLIKELY(!referring_class->CanAccessMember(fields_class, - resolved_field->GetAccessFlags()))) { - ThrowIllegalAccessErrorField(referring_class, resolved_field); - return NULL; // failure - } - } - if (UNLIKELY(is_set && resolved_field->IsFinal() && (fields_class != referring_class))) { - ThrowIllegalAccessErrorFinalField(referrer, resolved_field); - return NULL; // failure - } else { - FieldHelper fh(resolved_field); - if (UNLIKELY(fh.IsPrimitiveType() != is_primitive || - fh.FieldSize() != expected_size)) { - ThrowLocation throw_location = self->GetCurrentLocationForThrow(); - DCHECK(throw_location.GetMethod() == referrer); - self->ThrowNewExceptionF(throw_location, "Ljava/lang/NoSuchFieldError;", - "Attempted read of %zd-bit %s on field '%s'", - expected_size * (32 / sizeof(int32_t)), - is_primitive ? "primitive" : "non-primitive", - PrettyField(resolved_field, true).c_str()); - return NULL; // failure - } - } - } - if (!is_static) { - // instance fields must be being accessed on an initialized class - return resolved_field; - } else { - // If the class is initialized we're done. - if (fields_class->IsInitialized()) { - return resolved_field; - } else if (Runtime::Current()->GetClassLinker()->EnsureInitialized(fields_class, true, true)) { - // Otherwise let's ensure the class is initialized before resolving the field. - return resolved_field; - } else { - DCHECK(self->IsExceptionPending()); // Throw exception and unwind - return NULL; // failure - } - } -} - -// Slow path method resolution -mirror::ArtMethod* FindMethodFromCode(uint32_t method_idx, mirror::Object* this_object, - mirror::ArtMethod* referrer, - Thread* self, bool access_check, InvokeType type) { - ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); - bool is_direct = type == kStatic || type == kDirect; - mirror::ArtMethod* resolved_method = class_linker->ResolveMethod(method_idx, referrer, type); - if (UNLIKELY(resolved_method == NULL)) { - DCHECK(self->IsExceptionPending()); // Throw exception and unwind. - return NULL; // Failure. - } else if (UNLIKELY(this_object == NULL && type != kStatic)) { - // Maintain interpreter-like semantics where NullPointerException is thrown - // after potential NoSuchMethodError from class linker. - ThrowLocation throw_location = self->GetCurrentLocationForThrow(); - DCHECK(referrer == throw_location.GetMethod()); - ThrowNullPointerExceptionForMethodAccess(throw_location, method_idx, type); - return NULL; // Failure. - } else { - if (!access_check) { - if (is_direct) { - return resolved_method; - } else if (type == kInterface) { - mirror::ArtMethod* interface_method = - this_object->GetClass()->FindVirtualMethodForInterface(resolved_method); - if (UNLIKELY(interface_method == NULL)) { - ThrowIncompatibleClassChangeErrorClassForInterfaceDispatch(resolved_method, this_object, - referrer); - return NULL; // Failure. - } else { - return interface_method; - } - } else { - mirror::ObjectArray* vtable; - uint16_t vtable_index = resolved_method->GetMethodIndex(); - if (type == kSuper) { - vtable = referrer->GetDeclaringClass()->GetSuperClass()->GetVTable(); - } else { - vtable = this_object->GetClass()->GetVTable(); - } - // TODO: eliminate bounds check? - return vtable->Get(vtable_index); - } - } else { - // Incompatible class change should have been handled in resolve method. - if (UNLIKELY(resolved_method->CheckIncompatibleClassChange(type))) { - ThrowIncompatibleClassChangeError(type, resolved_method->GetInvokeType(), resolved_method, - referrer); - return NULL; // Failure. - } - mirror::Class* methods_class = resolved_method->GetDeclaringClass(); - mirror::Class* referring_class = referrer->GetDeclaringClass(); - if (UNLIKELY(!referring_class->CanAccess(methods_class) || - !referring_class->CanAccessMember(methods_class, - resolved_method->GetAccessFlags()))) { - // The referring class can't access the resolved method, this may occur as a result of a - // protected method being made public by implementing an interface that re-declares the - // method public. Resort to the dex file to determine the correct class for the access check - const DexFile& dex_file = *referring_class->GetDexCache()->GetDexFile(); - methods_class = class_linker->ResolveType(dex_file, - dex_file.GetMethodId(method_idx).class_idx_, - referring_class); - if (UNLIKELY(!referring_class->CanAccess(methods_class))) { - ThrowIllegalAccessErrorClassForMethodDispatch(referring_class, methods_class, - referrer, resolved_method, type); - return NULL; // Failure. - } else if (UNLIKELY(!referring_class->CanAccessMember(methods_class, - resolved_method->GetAccessFlags()))) { - ThrowIllegalAccessErrorMethod(referring_class, resolved_method); - return NULL; // Failure. - } - } - if (is_direct) { - return resolved_method; - } else if (type == kInterface) { - mirror::ArtMethod* interface_method = - this_object->GetClass()->FindVirtualMethodForInterface(resolved_method); - if (UNLIKELY(interface_method == NULL)) { - ThrowIncompatibleClassChangeErrorClassForInterfaceDispatch(resolved_method, this_object, - referrer); - return NULL; // Failure. - } else { - return interface_method; - } - } else { - mirror::ObjectArray* vtable; - uint16_t vtable_index = resolved_method->GetMethodIndex(); - if (type == kSuper) { - mirror::Class* super_class = referring_class->GetSuperClass(); - if (LIKELY(super_class != NULL)) { - vtable = referring_class->GetSuperClass()->GetVTable(); - } else { - vtable = NULL; - } - } else { - vtable = this_object->GetClass()->GetVTable(); - } - if (LIKELY(vtable != NULL && - vtable_index < static_cast(vtable->GetLength()))) { - return vtable->GetWithoutChecks(vtable_index); - } else { - // Behavior to agree with that of the verifier. - MethodHelper mh(resolved_method); - ThrowNoSuchMethodError(type, resolved_method->GetDeclaringClass(), mh.GetName(), - mh.GetSignature()); - return NULL; // Failure. - } - } - } - } -} - void ThrowStackOverflowError(Thread* self) { if (self->IsHandlingStackOverflow()) { LOG(ERROR) << "Recursive stack overflow."; diff --git a/runtime/entrypoints/entrypoint_utils.h b/runtime/entrypoints/entrypoint_utils.h index c32a661effe..200860470db 100644 --- a/runtime/entrypoints/entrypoint_utils.h +++ b/runtime/entrypoints/entrypoint_utils.h @@ -178,11 +178,235 @@ enum FindFieldType { StaticPrimitiveWrite, }; -// Slow field find that can initialize classes and may throw exceptions. -extern mirror::ArtField* FindFieldFromCode(uint32_t field_idx, const mirror::ArtMethod* referrer, - Thread* self, FindFieldType type, size_t expected_size, - bool access_check) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); +template +static inline mirror::ArtField* FindFieldFromCode(uint32_t field_idx, const mirror::ArtMethod* referrer, + Thread* self, size_t expected_size) { + bool is_primitive; + bool is_set; + bool is_static; + switch (type) { + case InstanceObjectRead: is_primitive = false; is_set = false; is_static = false; break; + case InstanceObjectWrite: is_primitive = false; is_set = true; is_static = false; break; + case InstancePrimitiveRead: is_primitive = true; is_set = false; is_static = false; break; + case InstancePrimitiveWrite: is_primitive = true; is_set = true; is_static = false; break; + case StaticObjectRead: is_primitive = false; is_set = false; is_static = true; break; + case StaticObjectWrite: is_primitive = false; is_set = true; is_static = true; break; + case StaticPrimitiveRead: is_primitive = true; is_set = false; is_static = true; break; + case StaticPrimitiveWrite: // Keep GCC happy by having a default handler, fall-through. + default: is_primitive = true; is_set = true; is_static = true; break; + } + ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); + mirror::ArtField* resolved_field = class_linker->ResolveField(field_idx, referrer, is_static); + if (UNLIKELY(resolved_field == nullptr)) { + DCHECK(self->IsExceptionPending()); // Throw exception and unwind. + return nullptr; // Failure. + } + mirror::Class* fields_class = resolved_field->GetDeclaringClass(); + if (access_check) { + if (UNLIKELY(resolved_field->IsStatic() != is_static)) { + ThrowIncompatibleClassChangeErrorField(resolved_field, is_static, referrer); + return nullptr; + } + mirror::Class* referring_class = referrer->GetDeclaringClass(); + if (UNLIKELY(!referring_class->CanAccess(fields_class) || + !referring_class->CanAccessMember(fields_class, + resolved_field->GetAccessFlags()))) { + // The referring class can't access the resolved field, this may occur as a result of a + // protected field being made public by a sub-class. Resort to the dex file to determine + // the correct class for the access check. + const DexFile& dex_file = *referring_class->GetDexCache()->GetDexFile(); + fields_class = class_linker->ResolveType(dex_file, + dex_file.GetFieldId(field_idx).class_idx_, + referring_class); + if (UNLIKELY(!referring_class->CanAccess(fields_class))) { + ThrowIllegalAccessErrorClass(referring_class, fields_class); + return nullptr; // failure + } else if (UNLIKELY(!referring_class->CanAccessMember(fields_class, + resolved_field->GetAccessFlags()))) { + ThrowIllegalAccessErrorField(referring_class, resolved_field); + return nullptr; // failure + } + } + if (UNLIKELY(is_set && resolved_field->IsFinal() && (fields_class != referring_class))) { + ThrowIllegalAccessErrorFinalField(referrer, resolved_field); + return nullptr; // failure + } else { + FieldHelper fh(resolved_field); + if (UNLIKELY(fh.IsPrimitiveType() != is_primitive || + fh.FieldSize() != expected_size)) { + ThrowLocation throw_location = self->GetCurrentLocationForThrow(); + DCHECK(throw_location.GetMethod() == referrer); + self->ThrowNewExceptionF(throw_location, "Ljava/lang/NoSuchFieldError;", + "Attempted read of %zd-bit %s on field '%s'", + expected_size * (32 / sizeof(int32_t)), + is_primitive ? "primitive" : "non-primitive", + PrettyField(resolved_field, true).c_str()); + return nullptr; // failure + } + } + } + if (!is_static) { + // instance fields must be being accessed on an initialized class + return resolved_field; + } else { + // If the class is initialized we're done. + if (LIKELY(fields_class->IsInitialized())) { + return resolved_field; + } else if (LIKELY(class_linker->EnsureInitialized(fields_class, true, true))) { + // Otherwise let's ensure the class is initialized before resolving the field. + return resolved_field; + } else { + DCHECK(self->IsExceptionPending()); // Throw exception and unwind + return nullptr; // failure + } + } +} + +// Explicit template declarations of FindFieldFromCode for all field access types. +#define EXPLICIT_FIND_FIELD_FROM_CODE_TEMPLATE_DECL(_type, _access_check) \ +template SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) ALWAYS_INLINE \ +static mirror::ArtField* FindFieldFromCode<_type, _access_check>(uint32_t field_idx, \ + const mirror::ArtMethod* referrer, \ + Thread* self, size_t expected_size) \ + +#define EXPLICIT_FIND_FIELD_FROM_CODE_TYPED_TEMPLATE_DECL(_type) \ + EXPLICIT_FIND_FIELD_FROM_CODE_TEMPLATE_DECL(_type, false); \ + EXPLICIT_FIND_FIELD_FROM_CODE_TEMPLATE_DECL(_type, true) + +EXPLICIT_FIND_FIELD_FROM_CODE_TYPED_TEMPLATE_DECL(InstanceObjectRead); +EXPLICIT_FIND_FIELD_FROM_CODE_TYPED_TEMPLATE_DECL(InstanceObjectWrite); +EXPLICIT_FIND_FIELD_FROM_CODE_TYPED_TEMPLATE_DECL(InstancePrimitiveRead); +EXPLICIT_FIND_FIELD_FROM_CODE_TYPED_TEMPLATE_DECL(InstancePrimitiveWrite); +EXPLICIT_FIND_FIELD_FROM_CODE_TYPED_TEMPLATE_DECL(StaticObjectRead); +EXPLICIT_FIND_FIELD_FROM_CODE_TYPED_TEMPLATE_DECL(StaticObjectWrite); +EXPLICIT_FIND_FIELD_FROM_CODE_TYPED_TEMPLATE_DECL(StaticPrimitiveRead); +EXPLICIT_FIND_FIELD_FROM_CODE_TYPED_TEMPLATE_DECL(StaticPrimitiveWrite); + +#undef EXPLICIT_FIND_FIELD_FROM_CODE_TYPED_TEMPLATE_DECL +#undef EXPLICIT_FIND_FIELD_FROM_CODE_TEMPLATE_DECL + +template +static inline mirror::ArtMethod* FindMethodFromCode(uint32_t method_idx, mirror::Object* this_object, + mirror::ArtMethod* referrer, Thread* self) { + ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); + mirror::ArtMethod* resolved_method = class_linker->ResolveMethod(method_idx, referrer, type); + if (UNLIKELY(resolved_method == nullptr)) { + DCHECK(self->IsExceptionPending()); // Throw exception and unwind. + return nullptr; // Failure. + } else if (UNLIKELY(this_object == nullptr && type != kStatic)) { + // Maintain interpreter-like semantics where NullPointerException is thrown + // after potential NoSuchMethodError from class linker. + ThrowLocation throw_location = self->GetCurrentLocationForThrow(); + DCHECK(referrer == throw_location.GetMethod()); + ThrowNullPointerExceptionForMethodAccess(throw_location, method_idx, type); + return nullptr; // Failure. + } else if (access_check) { + // Incompatible class change should have been handled in resolve method. + if (UNLIKELY(resolved_method->CheckIncompatibleClassChange(type))) { + ThrowIncompatibleClassChangeError(type, resolved_method->GetInvokeType(), resolved_method, + referrer); + return nullptr; // Failure. + } + mirror::Class* methods_class = resolved_method->GetDeclaringClass(); + mirror::Class* referring_class = referrer->GetDeclaringClass(); + if (UNLIKELY(!referring_class->CanAccess(methods_class) || + !referring_class->CanAccessMember(methods_class, + resolved_method->GetAccessFlags()))) { + // The referring class can't access the resolved method, this may occur as a result of a + // protected method being made public by implementing an interface that re-declares the + // method public. Resort to the dex file to determine the correct class for the access check + const DexFile& dex_file = *referring_class->GetDexCache()->GetDexFile(); + ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); + methods_class = class_linker->ResolveType(dex_file, + dex_file.GetMethodId(method_idx).class_idx_, + referring_class); + if (UNLIKELY(!referring_class->CanAccess(methods_class))) { + ThrowIllegalAccessErrorClassForMethodDispatch(referring_class, methods_class, + referrer, resolved_method, type); + return nullptr; // Failure. + } else if (UNLIKELY(!referring_class->CanAccessMember(methods_class, + resolved_method->GetAccessFlags()))) { + ThrowIllegalAccessErrorMethod(referring_class, resolved_method); + return nullptr; // Failure. + } + } + } + switch (type) { + case kStatic: + case kDirect: + return resolved_method; + case kVirtual: { + mirror::ObjectArray* vtable = this_object->GetClass()->GetVTable(); + uint16_t vtable_index = resolved_method->GetMethodIndex(); + if (access_check && + (vtable == nullptr || vtable_index >= static_cast(vtable->GetLength()))) { + // Behavior to agree with that of the verifier. + MethodHelper mh(resolved_method); + ThrowNoSuchMethodError(type, resolved_method->GetDeclaringClass(), mh.GetName(), + mh.GetSignature()); + return nullptr; // Failure. + } + DCHECK(vtable != nullptr); + return vtable->GetWithoutChecks(vtable_index); + } + case kSuper: { + mirror::Class* super_class = referrer->GetDeclaringClass()->GetSuperClass(); + uint16_t vtable_index = resolved_method->GetMethodIndex(); + mirror::ObjectArray* vtable; + if (access_check) { + // Check existence of super class. + vtable = (super_class != nullptr) ? super_class->GetVTable() : nullptr; + if (vtable == nullptr || vtable_index >= static_cast(vtable->GetLength())) { + // Behavior to agree with that of the verifier. + MethodHelper mh(resolved_method); + ThrowNoSuchMethodError(type, resolved_method->GetDeclaringClass(), mh.GetName(), + mh.GetSignature()); + return nullptr; // Failure. + } + } else { + // Super class must exist. + DCHECK(super_class != nullptr); + vtable = super_class->GetVTable(); + } + DCHECK(vtable != nullptr); + return vtable->GetWithoutChecks(vtable_index); + } + case kInterface: { + mirror::ArtMethod* interface_method = + this_object->GetClass()->FindVirtualMethodForInterface(resolved_method); + if (UNLIKELY(interface_method == nullptr)) { + ThrowIncompatibleClassChangeErrorClassForInterfaceDispatch(resolved_method, this_object, + referrer); + return nullptr; // Failure. + } else { + return interface_method; + } + } + default: + LOG(FATAL) << "Unknown invoke type " << type; + return nullptr; // Failure. + } +} + +// Explicit template declarations of FindMethodFromCode for all invoke types. +#define EXPLICIT_FIND_METHOD_FROM_CODE_TEMPLATE_DECL(_type, _access_check) \ + template SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) ALWAYS_INLINE \ + static mirror::ArtMethod* FindMethodFromCode<_type, _access_check>(uint32_t method_idx, \ + mirror::Object* this_object, \ + mirror::ArtMethod* referrer, \ + Thread* self) +#define EXPLICIT_FIND_METHOD_FROM_CODE_TYPED_TEMPLATE_DECL(_type) \ + EXPLICIT_FIND_METHOD_FROM_CODE_TEMPLATE_DECL(_type, false); \ + EXPLICIT_FIND_METHOD_FROM_CODE_TEMPLATE_DECL(_type, true) + +EXPLICIT_FIND_METHOD_FROM_CODE_TYPED_TEMPLATE_DECL(kStatic); +EXPLICIT_FIND_METHOD_FROM_CODE_TYPED_TEMPLATE_DECL(kDirect); +EXPLICIT_FIND_METHOD_FROM_CODE_TYPED_TEMPLATE_DECL(kVirtual); +EXPLICIT_FIND_METHOD_FROM_CODE_TYPED_TEMPLATE_DECL(kSuper); +EXPLICIT_FIND_METHOD_FROM_CODE_TYPED_TEMPLATE_DECL(kInterface); + +#undef EXPLICIT_FIND_METHOD_FROM_CODE_TYPED_TEMPLATE_DECL +#undef EXPLICIT_FIND_METHOD_FROM_CODE_TEMPLATE_DECL // Fast path field resolution that can't initialize classes or throw exceptions. static inline mirror::ArtField* FindFieldFast(uint32_t field_idx, @@ -282,11 +506,6 @@ static inline mirror::ArtMethod* FindMethodFast(uint32_t method_idx, } } -extern mirror::ArtMethod* FindMethodFromCode(uint32_t method_idx, mirror::Object* this_object, - mirror::ArtMethod* referrer, - Thread* self, bool access_check, InvokeType type) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - static inline mirror::Class* ResolveVerifyAndClinit(uint32_t type_idx, const mirror::ArtMethod* referrer, Thread* self, bool can_run_clinit, diff --git a/runtime/entrypoints/portable/portable_field_entrypoints.cc b/runtime/entrypoints/portable/portable_field_entrypoints.cc index bd6f79584b2..095e99ef347 100644 --- a/runtime/entrypoints/portable/portable_field_entrypoints.cc +++ b/runtime/entrypoints/portable/portable_field_entrypoints.cc @@ -33,12 +33,8 @@ extern "C" int32_t art_portable_set32_static_from_code(uint32_t field_idx, field->Set32(field->GetDeclaringClass(), new_value); return 0; } - field = FindFieldFromCode(field_idx, - referrer, - Thread::Current(), - StaticPrimitiveWrite, - sizeof(uint32_t), - true); + field = FindFieldFromCode(field_idx, referrer, Thread::Current(), + sizeof(uint32_t)); if (LIKELY(field != NULL)) { field->Set32(field->GetDeclaringClass(), new_value); return 0; @@ -55,12 +51,8 @@ extern "C" int32_t art_portable_set64_static_from_code(uint32_t field_idx, field->Set64(field->GetDeclaringClass(), new_value); return 0; } - field = FindFieldFromCode(field_idx, - referrer, - Thread::Current(), - StaticPrimitiveWrite, - sizeof(uint64_t), - true); + field = FindFieldFromCode(field_idx, referrer, Thread::Current(), + sizeof(uint64_t)); if (LIKELY(field != NULL)) { field->Set64(field->GetDeclaringClass(), new_value); return 0; @@ -78,8 +70,8 @@ extern "C" int32_t art_portable_set_obj_static_from_code(uint32_t field_idx, field->SetObj(field->GetDeclaringClass(), new_value); return 0; } - field = FindFieldFromCode(field_idx, referrer, Thread::Current(), - StaticObjectWrite, sizeof(mirror::Object*), true); + field = FindFieldFromCode(field_idx, referrer, Thread::Current(), + sizeof(mirror::Object*)); if (LIKELY(field != NULL)) { field->SetObj(field->GetDeclaringClass(), new_value); return 0; @@ -94,8 +86,8 @@ extern "C" int32_t art_portable_get32_static_from_code(uint32_t field_idx, if (LIKELY(field != NULL)) { return field->Get32(field->GetDeclaringClass()); } - field = FindFieldFromCode(field_idx, referrer, Thread::Current(), - StaticPrimitiveRead, sizeof(uint32_t), true); + field = FindFieldFromCode(field_idx, referrer, Thread::Current(), + sizeof(uint32_t)); if (LIKELY(field != NULL)) { return field->Get32(field->GetDeclaringClass()); } @@ -109,8 +101,8 @@ extern "C" int64_t art_portable_get64_static_from_code(uint32_t field_idx, if (LIKELY(field != NULL)) { return field->Get64(field->GetDeclaringClass()); } - field = FindFieldFromCode(field_idx, referrer, Thread::Current(), - StaticPrimitiveRead, sizeof(uint64_t), true); + field = FindFieldFromCode(field_idx, referrer, Thread::Current(), + sizeof(uint64_t)); if (LIKELY(field != NULL)) { return field->Get64(field->GetDeclaringClass()); } @@ -125,8 +117,8 @@ extern "C" mirror::Object* art_portable_get_obj_static_from_code(uint32_t field_ if (LIKELY(field != NULL)) { return field->GetObj(field->GetDeclaringClass()); } - field = FindFieldFromCode(field_idx, referrer, Thread::Current(), - StaticObjectRead, sizeof(mirror::Object*), true); + field = FindFieldFromCode(field_idx, referrer, Thread::Current(), + sizeof(mirror::Object*)); if (LIKELY(field != NULL)) { return field->GetObj(field->GetDeclaringClass()); } @@ -142,8 +134,8 @@ extern "C" int32_t art_portable_set32_instance_from_code(uint32_t field_idx, field->Set32(obj, new_value); return 0; } - field = FindFieldFromCode(field_idx, referrer, Thread::Current(), - InstancePrimitiveWrite, sizeof(uint32_t), true); + field = FindFieldFromCode(field_idx, referrer, Thread::Current(), + sizeof(uint32_t)); if (LIKELY(field != NULL)) { field->Set32(obj, new_value); return 0; @@ -160,8 +152,8 @@ extern "C" int32_t art_portable_set64_instance_from_code(uint32_t field_idx, field->Set64(obj, new_value); return 0; } - field = FindFieldFromCode(field_idx, referrer, Thread::Current(), - InstancePrimitiveWrite, sizeof(uint64_t), true); + field = FindFieldFromCode(field_idx, referrer, Thread::Current(), + sizeof(uint64_t)); if (LIKELY(field != NULL)) { field->Set64(obj, new_value); return 0; @@ -180,8 +172,8 @@ extern "C" int32_t art_portable_set_obj_instance_from_code(uint32_t field_idx, field->SetObj(obj, new_value); return 0; } - field = FindFieldFromCode(field_idx, referrer, Thread::Current(), - InstanceObjectWrite, sizeof(mirror::Object*), true); + field = FindFieldFromCode(field_idx, referrer, Thread::Current(), + sizeof(mirror::Object*)); if (LIKELY(field != NULL)) { field->SetObj(obj, new_value); return 0; @@ -197,8 +189,8 @@ extern "C" int32_t art_portable_get32_instance_from_code(uint32_t field_idx, if (LIKELY(field != NULL)) { return field->Get32(obj); } - field = FindFieldFromCode(field_idx, referrer, Thread::Current(), - InstancePrimitiveRead, sizeof(uint32_t), true); + field = FindFieldFromCode(field_idx, referrer, Thread::Current(), + sizeof(uint32_t)); if (LIKELY(field != NULL)) { return field->Get32(obj); } @@ -213,8 +205,8 @@ extern "C" int64_t art_portable_get64_instance_from_code(uint32_t field_idx, if (LIKELY(field != NULL)) { return field->Get64(obj); } - field = FindFieldFromCode(field_idx, referrer, Thread::Current(), - InstancePrimitiveRead, sizeof(uint64_t), true); + field = FindFieldFromCode(field_idx, referrer, Thread::Current(), + sizeof(uint64_t)); if (LIKELY(field != NULL)) { return field->Get64(obj); } @@ -230,8 +222,8 @@ extern "C" mirror::Object* art_portable_get_obj_instance_from_code(uint32_t fiel if (LIKELY(field != NULL)) { return field->GetObj(obj); } - field = FindFieldFromCode(field_idx, referrer, Thread::Current(), - InstanceObjectRead, sizeof(mirror::Object*), true); + field = FindFieldFromCode(field_idx, referrer, Thread::Current(), + sizeof(mirror::Object*)); if (LIKELY(field != NULL)) { return field->GetObj(obj); } diff --git a/runtime/entrypoints/portable/portable_invoke_entrypoints.cc b/runtime/entrypoints/portable/portable_invoke_entrypoints.cc index 14cbd841864..e2a0cc20081 100644 --- a/runtime/entrypoints/portable/portable_invoke_entrypoints.cc +++ b/runtime/entrypoints/portable/portable_invoke_entrypoints.cc @@ -21,21 +21,13 @@ namespace art { -static mirror::ArtMethod* FindMethodHelper(uint32_t method_idx, - mirror::Object* this_object, - mirror::ArtMethod* caller_method, - bool access_check, - InvokeType type, - Thread* thread) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - mirror::ArtMethod* method = FindMethodFast(method_idx, - this_object, - caller_method, - access_check, - type); +template +static mirror::ArtMethod* FindMethodHelper(uint32_t method_idx, mirror::Object* this_object, + mirror::ArtMethod* caller_method, Thread* thread) { + mirror::ArtMethod* method = FindMethodFast(method_idx, this_object, caller_method, + access_check, type); if (UNLIKELY(method == NULL)) { - method = FindMethodFromCode(method_idx, this_object, caller_method, - thread, access_check, type); + method = FindMethodFromCode(method_idx, this_object, caller_method, thread); if (UNLIKELY(method == NULL)) { CHECK(thread->IsExceptionPending()); return 0; // failure @@ -53,12 +45,32 @@ static mirror::ArtMethod* FindMethodHelper(uint32_t method_idx, return method; } +// Explicit template declarations of FindMethodHelper for all invoke types. +#define EXPLICIT_FIND_METHOD_HELPER_TEMPLATE_DECL(_type, _access_check) \ + template SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) \ + static mirror::ArtMethod* FindMethodHelper<_type, _access_check>(uint32_t method_idx, \ + mirror::Object* this_object, \ + mirror::ArtMethod* caller_method, \ + Thread* thread) +#define EXPLICIT_FIND_METHOD_HELPER_TYPED_TEMPLATE_DECL(_type) \ + EXPLICIT_FIND_METHOD_HELPER_TEMPLATE_DECL(_type, false); \ + EXPLICIT_FIND_METHOD_HELPER_TEMPLATE_DECL(_type, true) + +EXPLICIT_FIND_METHOD_HELPER_TYPED_TEMPLATE_DECL(kStatic); +EXPLICIT_FIND_METHOD_HELPER_TYPED_TEMPLATE_DECL(kDirect); +EXPLICIT_FIND_METHOD_HELPER_TYPED_TEMPLATE_DECL(kVirtual); +EXPLICIT_FIND_METHOD_HELPER_TYPED_TEMPLATE_DECL(kSuper); +EXPLICIT_FIND_METHOD_HELPER_TYPED_TEMPLATE_DECL(kInterface); + +#undef EXPLICIT_FIND_METHOD_HELPER_TYPED_TEMPLATE_DECL +#undef EXPLICIT_FIND_METHOD_HELPER_TEMPLATE_DECL + extern "C" mirror::Object* art_portable_find_static_method_from_code_with_access_check(uint32_t method_idx, mirror::Object* this_object, mirror::ArtMethod* referrer, Thread* thread) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - return FindMethodHelper(method_idx, this_object, referrer, true, kStatic, thread); + return FindMethodHelper(method_idx, this_object, referrer, thread); } extern "C" mirror::Object* art_portable_find_direct_method_from_code_with_access_check(uint32_t method_idx, @@ -66,7 +78,7 @@ extern "C" mirror::Object* art_portable_find_direct_method_from_code_with_access mirror::ArtMethod* referrer, Thread* thread) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - return FindMethodHelper(method_idx, this_object, referrer, true, kDirect, thread); + return FindMethodHelper(method_idx, this_object, referrer, thread); } extern "C" mirror::Object* art_portable_find_virtual_method_from_code_with_access_check(uint32_t method_idx, @@ -74,7 +86,7 @@ extern "C" mirror::Object* art_portable_find_virtual_method_from_code_with_acces mirror::ArtMethod* referrer, Thread* thread) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - return FindMethodHelper(method_idx, this_object, referrer, true, kVirtual, thread); + return FindMethodHelper(method_idx, this_object, referrer, thread); } extern "C" mirror::Object* art_portable_find_super_method_from_code_with_access_check(uint32_t method_idx, @@ -82,7 +94,7 @@ extern "C" mirror::Object* art_portable_find_super_method_from_code_with_access_ mirror::ArtMethod* referrer, Thread* thread) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - return FindMethodHelper(method_idx, this_object, referrer, true, kSuper, thread); + return FindMethodHelper(method_idx, this_object, referrer, thread); } extern "C" mirror::Object* art_portable_find_interface_method_from_code_with_access_check(uint32_t method_idx, @@ -90,7 +102,7 @@ extern "C" mirror::Object* art_portable_find_interface_method_from_code_with_acc mirror::ArtMethod* referrer, Thread* thread) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - return FindMethodHelper(method_idx, this_object, referrer, true, kInterface, thread); + return FindMethodHelper(method_idx, this_object, referrer, thread); } extern "C" mirror::Object* art_portable_find_interface_method_from_code(uint32_t method_idx, @@ -98,7 +110,7 @@ extern "C" mirror::Object* art_portable_find_interface_method_from_code(uint32_t mirror::ArtMethod* referrer, Thread* thread) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - return FindMethodHelper(method_idx, this_object, referrer, false, kInterface, thread); + return FindMethodHelper(method_idx, this_object, referrer, thread); } } // namespace art diff --git a/runtime/entrypoints/quick/quick_field_entrypoints.cc b/runtime/entrypoints/quick/quick_field_entrypoints.cc index 0ec1eb7920b..0a533bdfbca 100644 --- a/runtime/entrypoints/quick/quick_field_entrypoints.cc +++ b/runtime/entrypoints/quick/quick_field_entrypoints.cc @@ -35,7 +35,7 @@ extern "C" uint32_t artGet32StaticFromCode(uint32_t field_idx, return field->Get32(field->GetDeclaringClass()); } FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly); - field = FindFieldFromCode(field_idx, referrer, self, StaticPrimitiveRead, sizeof(int32_t), true); + field = FindFieldFromCode(field_idx, referrer, self, sizeof(int32_t)); if (LIKELY(field != NULL)) { return field->Get32(field->GetDeclaringClass()); } @@ -52,7 +52,7 @@ extern "C" uint64_t artGet64StaticFromCode(uint32_t field_idx, return field->Get64(field->GetDeclaringClass()); } FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly); - field = FindFieldFromCode(field_idx, referrer, self, StaticPrimitiveRead, sizeof(int64_t), true); + field = FindFieldFromCode(field_idx, referrer, self, sizeof(int64_t)); if (LIKELY(field != NULL)) { return field->Get64(field->GetDeclaringClass()); } @@ -69,8 +69,8 @@ extern "C" mirror::Object* artGetObjStaticFromCode(uint32_t field_idx, return field->GetObj(field->GetDeclaringClass()); } FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly); - field = FindFieldFromCode(field_idx, referrer, self, StaticObjectRead, sizeof(mirror::Object*), - true); + field = FindFieldFromCode(field_idx, referrer, self, + sizeof(mirror::Object*)); if (LIKELY(field != NULL)) { return field->GetObj(field->GetDeclaringClass()); } @@ -87,8 +87,8 @@ extern "C" uint32_t artGet32InstanceFromCode(uint32_t field_idx, mirror::Object* return field->Get32(obj); } FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly); - field = FindFieldFromCode(field_idx, referrer, self, InstancePrimitiveRead, sizeof(int32_t), - true); + field = FindFieldFromCode(field_idx, referrer, self, + sizeof(int32_t)); if (LIKELY(field != NULL)) { if (UNLIKELY(obj == NULL)) { ThrowLocation throw_location = self->GetCurrentLocationForThrow(); @@ -110,8 +110,8 @@ extern "C" uint64_t artGet64InstanceFromCode(uint32_t field_idx, mirror::Object* return field->Get64(obj); } FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly); - field = FindFieldFromCode(field_idx, referrer, self, InstancePrimitiveRead, sizeof(int64_t), - true); + field = FindFieldFromCode(field_idx, referrer, self, + sizeof(int64_t)); if (LIKELY(field != NULL)) { if (UNLIKELY(obj == NULL)) { ThrowLocation throw_location = self->GetCurrentLocationForThrow(); @@ -134,8 +134,8 @@ extern "C" mirror::Object* artGetObjInstanceFromCode(uint32_t field_idx, mirror: return field->GetObj(obj); } FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly); - field = FindFieldFromCode(field_idx, referrer, self, InstanceObjectRead, sizeof(mirror::Object*), - true); + field = FindFieldFromCode(field_idx, referrer, self, + sizeof(mirror::Object*)); if (LIKELY(field != NULL)) { if (UNLIKELY(obj == NULL)) { ThrowLocation throw_location = self->GetCurrentLocationForThrow(); @@ -158,7 +158,7 @@ extern "C" int artSet32StaticFromCode(uint32_t field_idx, uint32_t new_value, return 0; // success } FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly); - field = FindFieldFromCode(field_idx, referrer, self, StaticPrimitiveWrite, sizeof(int32_t), true); + field = FindFieldFromCode(field_idx, referrer, self, sizeof(int32_t)); if (LIKELY(field != NULL)) { field->Set32(field->GetDeclaringClass(), new_value); return 0; // success @@ -176,7 +176,7 @@ extern "C" int artSet64StaticFromCode(uint32_t field_idx, const mirror::ArtMetho return 0; // success } FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly); - field = FindFieldFromCode(field_idx, referrer, self, StaticPrimitiveWrite, sizeof(int64_t), true); + field = FindFieldFromCode(field_idx, referrer, self, sizeof(int64_t)); if (LIKELY(field != NULL)) { field->Set64(field->GetDeclaringClass(), new_value); return 0; // success @@ -197,7 +197,8 @@ extern "C" int artSetObjStaticFromCode(uint32_t field_idx, mirror::Object* new_v } } FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly); - field = FindFieldFromCode(field_idx, referrer, self, StaticObjectWrite, sizeof(mirror::Object*), true); + field = FindFieldFromCode(field_idx, referrer, self, + sizeof(mirror::Object*)); if (LIKELY(field != NULL)) { field->SetObj(field->GetDeclaringClass(), new_value); return 0; // success @@ -216,8 +217,8 @@ extern "C" int artSet32InstanceFromCode(uint32_t field_idx, mirror::Object* obj, return 0; // success } FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly); - field = FindFieldFromCode(field_idx, referrer, self, InstancePrimitiveWrite, sizeof(int32_t), - true); + field = FindFieldFromCode(field_idx, referrer, self, + sizeof(int32_t)); if (LIKELY(field != NULL)) { if (UNLIKELY(obj == NULL)) { ThrowLocation throw_location = self->GetCurrentLocationForThrow(); @@ -244,8 +245,8 @@ extern "C" int artSet64InstanceFromCode(uint32_t field_idx, mirror::Object* obj, } *sp = callee_save; self->SetTopOfStack(sp, 0); - field = FindFieldFromCode(field_idx, referrer, self, InstancePrimitiveWrite, sizeof(int64_t), - true); + field = FindFieldFromCode(field_idx, referrer, self, + sizeof(int64_t)); if (LIKELY(field != NULL)) { if (UNLIKELY(obj == NULL)) { ThrowLocation throw_location = self->GetCurrentLocationForThrow(); @@ -270,8 +271,8 @@ extern "C" int artSetObjInstanceFromCode(uint32_t field_idx, mirror::Object* obj return 0; // success } FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly); - field = FindFieldFromCode(field_idx, referrer, self, InstanceObjectWrite, - sizeof(mirror::Object*), true); + field = FindFieldFromCode(field_idx, referrer, self, + sizeof(mirror::Object*)); if (LIKELY(field != NULL)) { if (UNLIKELY(obj == NULL)) { ThrowLocation throw_location = self->GetCurrentLocationForThrow(); diff --git a/runtime/entrypoints/quick/quick_invoke_entrypoints.cc b/runtime/entrypoints/quick/quick_invoke_entrypoints.cc index 07c1c015aa5..b852a3292ef 100644 --- a/runtime/entrypoints/quick/quick_invoke_entrypoints.cc +++ b/runtime/entrypoints/quick/quick_invoke_entrypoints.cc @@ -118,8 +118,7 @@ extern "C" uint64_t artInvokeInterfaceTrampoline(mirror::ArtMethod* interface_me DCHECK_EQ(instr_code, Instruction::INVOKE_INTERFACE_RANGE); dex_method_idx = instr->VRegB_3rc(); } - method = FindMethodFromCode(dex_method_idx, this_object, caller_method, self, - false, kInterface); + method = FindMethodFromCode(dex_method_idx, this_object, caller_method, self); if (UNLIKELY(method == NULL)) { CHECK(self->IsExceptionPending()); return 0; // Failure. @@ -142,17 +141,15 @@ extern "C" uint64_t artInvokeInterfaceTrampoline(mirror::ArtMethod* interface_me return result; } - +template static uint64_t artInvokeCommon(uint32_t method_idx, mirror::Object* this_object, mirror::ArtMethod* caller_method, - Thread* self, mirror::ArtMethod** sp, bool access_check, - InvokeType type) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + Thread* self, mirror::ArtMethod** sp) { mirror::ArtMethod* method = FindMethodFast(method_idx, this_object, caller_method, access_check, type); if (UNLIKELY(method == NULL)) { FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsAndArgs); - method = FindMethodFromCode(method_idx, this_object, caller_method, self, access_check, type); + method = FindMethodFromCode(method_idx, this_object, caller_method, self); if (UNLIKELY(method == NULL)) { CHECK(self->IsExceptionPending()); return 0; // failure @@ -176,6 +173,27 @@ static uint64_t artInvokeCommon(uint32_t method_idx, mirror::Object* this_object return result; } +// Explicit template declarations of artInvokeCommon for all invoke types. +#define EXPLICIT_ART_INVOKE_COMMON_TEMPLATE_DECL(_type, _access_check) \ + template SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) \ + static uint64_t artInvokeCommon<_type, _access_check>(uint32_t method_idx, \ + mirror::Object* this_object, \ + mirror::ArtMethod* caller_method, \ + Thread* self, mirror::ArtMethod** sp) + +#define EXPLICIT_ART_INVOKE_COMMON_TYPED_TEMPLATE_DECL(_type) \ + EXPLICIT_ART_INVOKE_COMMON_TEMPLATE_DECL(_type, false); \ + EXPLICIT_ART_INVOKE_COMMON_TEMPLATE_DECL(_type, true) + +EXPLICIT_ART_INVOKE_COMMON_TYPED_TEMPLATE_DECL(kStatic); +EXPLICIT_ART_INVOKE_COMMON_TYPED_TEMPLATE_DECL(kDirect); +EXPLICIT_ART_INVOKE_COMMON_TYPED_TEMPLATE_DECL(kVirtual); +EXPLICIT_ART_INVOKE_COMMON_TYPED_TEMPLATE_DECL(kSuper); +EXPLICIT_ART_INVOKE_COMMON_TYPED_TEMPLATE_DECL(kInterface); + +#undef EXPLICIT_ART_INVOKE_COMMON_TYPED_TEMPLATE_DECL +#undef EXPLICIT_ART_INVOKE_COMMON_TEMPLATE_DECL + // See comments in runtime_support_asm.S extern "C" uint64_t artInvokeInterfaceTrampolineWithAccessCheck(uint32_t method_idx, mirror::Object* this_object, @@ -183,7 +201,7 @@ extern "C" uint64_t artInvokeInterfaceTrampolineWithAccessCheck(uint32_t method_ Thread* self, mirror::ArtMethod** sp) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - return artInvokeCommon(method_idx, this_object, caller_method, self, sp, true, kInterface); + return artInvokeCommon(method_idx, this_object, caller_method, self, sp); } @@ -193,7 +211,7 @@ extern "C" uint64_t artInvokeDirectTrampolineWithAccessCheck(uint32_t method_idx Thread* self, mirror::ArtMethod** sp) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - return artInvokeCommon(method_idx, this_object, caller_method, self, sp, true, kDirect); + return artInvokeCommon(method_idx, this_object, caller_method, self, sp); } extern "C" uint64_t artInvokeStaticTrampolineWithAccessCheck(uint32_t method_idx, @@ -202,7 +220,7 @@ extern "C" uint64_t artInvokeStaticTrampolineWithAccessCheck(uint32_t method_idx Thread* self, mirror::ArtMethod** sp) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - return artInvokeCommon(method_idx, this_object, caller_method, self, sp, true, kStatic); + return artInvokeCommon(method_idx, this_object, caller_method, self, sp); } extern "C" uint64_t artInvokeSuperTrampolineWithAccessCheck(uint32_t method_idx, @@ -211,7 +229,7 @@ extern "C" uint64_t artInvokeSuperTrampolineWithAccessCheck(uint32_t method_idx, Thread* self, mirror::ArtMethod** sp) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - return artInvokeCommon(method_idx, this_object, caller_method, self, sp, true, kSuper); + return artInvokeCommon(method_idx, this_object, caller_method, self, sp); } extern "C" uint64_t artInvokeVirtualTrampolineWithAccessCheck(uint32_t method_idx, @@ -220,7 +238,7 @@ extern "C" uint64_t artInvokeVirtualTrampolineWithAccessCheck(uint32_t method_id Thread* self, mirror::ArtMethod** sp) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - return artInvokeCommon(method_idx, this_object, caller_method, self, sp, true, kVirtual); + return artInvokeCommon(method_idx, this_object, caller_method, self, sp); } } // namespace art diff --git a/runtime/interpreter/interpreter_common.h b/runtime/interpreter/interpreter_common.h index 29b00d2e995..0bc834ccb1a 100644 --- a/runtime/interpreter/interpreter_common.h +++ b/runtime/interpreter/interpreter_common.h @@ -90,7 +90,6 @@ template bool DoCall(ArtMethod* method, Object* receiver, Thread* self, ShadowFrame& shadow_frame, const Instruction* inst, uint16_t inst_data, JValue* result); - // Handles invoke-XXX/range instructions. // Returns true on success, otherwise throws an exception and returns false. template @@ -99,14 +98,14 @@ static inline bool DoInvoke(Thread* self, ShadowFrame& shadow_frame, const Instr const uint32_t method_idx = (is_range) ? inst->VRegB_3rc() : inst->VRegB_35c(); const uint32_t vregC = (is_range) ? inst->VRegC_3rc() : inst->VRegC_35c(); Object* receiver = (type == kStatic) ? nullptr : shadow_frame.GetVRegReference(vregC); - ArtMethod* const method = FindMethodFromCode(method_idx, receiver, shadow_frame.GetMethod(), self, - do_access_check, type); + ArtMethod* const method = FindMethodFromCode(method_idx, receiver, + shadow_frame.GetMethod(), + self); if (type != kStatic) { // Reload the vreg since the GC may have moved the object. receiver = shadow_frame.GetVRegReference(vregC); } - - if (UNLIKELY(method == NULL)) { + if (UNLIKELY(method == nullptr)) { CHECK(self->IsExceptionPending()); result->SetJ(0); return false; @@ -128,7 +127,7 @@ static inline bool DoInvokeVirtualQuick(Thread* self, ShadowFrame& shadow_frame, JValue* result) { const uint32_t vregC = (is_range) ? inst->VRegC_3rc() : inst->VRegC_35c(); Object* const receiver = shadow_frame.GetVRegReference(vregC); - if (UNLIKELY(receiver == NULL)) { + if (UNLIKELY(receiver == nullptr)) { // We lost the reference to the method index so we cannot get a more // precised exception message. ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow()); @@ -136,7 +135,7 @@ static inline bool DoInvokeVirtualQuick(Thread* self, ShadowFrame& shadow_frame, } const uint32_t vtable_idx = (is_range) ? inst->VRegB_3rc() : inst->VRegB_35c(); ArtMethod* const method = receiver->GetClass()->GetVTable()->GetWithoutChecks(vtable_idx); - if (UNLIKELY(method == NULL)) { + if (UNLIKELY(method == nullptr)) { CHECK(self->IsExceptionPending()); result->SetJ(0); return false; @@ -155,12 +154,11 @@ static inline bool DoInvokeVirtualQuick(Thread* self, ShadowFrame& shadow_frame, template static inline bool DoFieldGet(Thread* self, ShadowFrame& shadow_frame, const Instruction* inst, uint16_t inst_data) { - bool is_static = (find_type == StaticObjectRead) || (find_type == StaticPrimitiveRead); - uint32_t field_idx = is_static ? inst->VRegB_21c() : inst->VRegC_22c(); - ArtField* f = FindFieldFromCode(field_idx, shadow_frame.GetMethod(), self, - find_type, Primitive::FieldSize(field_type), - do_access_check); - if (UNLIKELY(f == NULL)) { + const bool is_static = (find_type == StaticObjectRead) || (find_type == StaticPrimitiveRead); + const uint32_t field_idx = is_static ? inst->VRegB_21c() : inst->VRegC_22c(); + ArtField* f = FindFieldFromCode(field_idx, shadow_frame.GetMethod(), self, + Primitive::FieldSize(field_type)); + if (UNLIKELY(f == nullptr)) { CHECK(self->IsExceptionPending()); return false; } @@ -169,7 +167,7 @@ static inline bool DoFieldGet(Thread* self, ShadowFrame& shadow_frame, obj = f->GetDeclaringClass(); } else { obj = shadow_frame.GetVRegReference(inst->VRegB_22c(inst_data)); - if (UNLIKELY(obj == NULL)) { + if (UNLIKELY(obj == nullptr)) { ThrowNullPointerExceptionForFieldAccess(shadow_frame.GetCurrentLocationForThrow(), f, true); return false; } @@ -208,7 +206,7 @@ static inline bool DoFieldGet(Thread* self, ShadowFrame& shadow_frame, template static inline bool DoIGetQuick(ShadowFrame& shadow_frame, const Instruction* inst, uint16_t inst_data) { Object* obj = shadow_frame.GetVRegReference(inst->VRegB_22c(inst_data)); - if (UNLIKELY(obj == NULL)) { + if (UNLIKELY(obj == nullptr)) { // We lost the reference to the field index so we cannot get a more // precised exception message. ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow()); @@ -241,10 +239,9 @@ static inline bool DoFieldPut(Thread* self, const ShadowFrame& shadow_frame, bool do_assignability_check = do_access_check; bool is_static = (find_type == StaticObjectWrite) || (find_type == StaticPrimitiveWrite); uint32_t field_idx = is_static ? inst->VRegB_21c() : inst->VRegC_22c(); - ArtField* f = FindFieldFromCode(field_idx, shadow_frame.GetMethod(), self, - find_type, Primitive::FieldSize(field_type), - do_access_check); - if (UNLIKELY(f == NULL)) { + ArtField* f = FindFieldFromCode(field_idx, shadow_frame.GetMethod(), self, + Primitive::FieldSize(field_type)); + if (UNLIKELY(f == nullptr)) { CHECK(self->IsExceptionPending()); return false; } @@ -253,7 +250,7 @@ static inline bool DoFieldPut(Thread* self, const ShadowFrame& shadow_frame, obj = f->GetDeclaringClass(); } else { obj = shadow_frame.GetVRegReference(inst->VRegB_22c(inst_data)); - if (UNLIKELY(obj == NULL)) { + if (UNLIKELY(obj == nullptr)) { ThrowNullPointerExceptionForFieldAccess(shadow_frame.GetCurrentLocationForThrow(), f, false); return false; @@ -281,7 +278,7 @@ static inline bool DoFieldPut(Thread* self, const ShadowFrame& shadow_frame, break; case Primitive::kPrimNot: { Object* reg = shadow_frame.GetVRegReference(vregA); - if (do_assignability_check && reg != NULL) { + if (do_assignability_check && reg != nullptr) { Class* field_class = FieldHelper(f).GetType(); if (!reg->VerifierInstanceOf(field_class)) { // This should never happen. @@ -308,7 +305,7 @@ static inline bool DoFieldPut(Thread* self, const ShadowFrame& shadow_frame, template static inline bool DoIPutQuick(const ShadowFrame& shadow_frame, const Instruction* inst, uint16_t inst_data) { Object* obj = shadow_frame.GetVRegReference(inst->VRegB_22c(inst_data)); - if (UNLIKELY(obj == NULL)) { + if (UNLIKELY(obj == nullptr)) { // We lost the reference to the field index so we cannot get a more // precised exception message. ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow()); diff --git a/runtime/mirror/object_test.cc b/runtime/mirror/object_test.cc index 4c5f90c41da..6c6d488f4a9 100644 --- a/runtime/mirror/object_test.cc +++ b/runtime/mirror/object_test.cc @@ -291,8 +291,8 @@ TEST_F(ObjectTest, StaticFieldFromCode) { ASSERT_TRUE(field_id != NULL); uint32_t field_idx = dex_file->GetIndexForFieldId(*field_id); - ArtField* field = FindFieldFromCode(field_idx, clinit, Thread::Current(), StaticObjectRead, - sizeof(Object*), true); + ArtField* field = FindFieldFromCode(field_idx, clinit, Thread::Current(), + sizeof(Object*)); Object* s0 = field->GetObj(klass); EXPECT_TRUE(s0 != NULL); From a8b4caf7526b6b66a8ae0826bd52c39c66e3c714 Mon Sep 17 00:00:00 2001 From: Vladimir Marko Date: Thu, 24 Oct 2013 15:08:57 +0100 Subject: [PATCH 0111/2402] Add byte swap instructions for ARM and x86. Change-Id: I03fdd61ffc811ae521141f532b3e04dda566c77d --- compiler/dex/compiler_enums.h | 2 ++ compiler/dex/quick/arm/arm_lir.h | 4 +++ compiler/dex/quick/arm/assemble_arm.cc | 20 +++++++++++++ compiler/dex/quick/arm/utility_arm.cc | 16 +++++++++++ compiler/dex/quick/x86/assemble_x86.cc | 34 ++++++++++++++++++++++ compiler/dex/quick/x86/codegen_x86.h | 1 + compiler/dex/quick/x86/utility_x86.cc | 8 ++++++ compiler/dex/quick/x86/x86_lir.h | 2 ++ disassembler/disassembler_arm.cc | 39 ++++++++++++++++++++++++++ disassembler/disassembler_x86.cc | 4 +++ 10 files changed, 130 insertions(+) diff --git a/compiler/dex/compiler_enums.h b/compiler/dex/compiler_enums.h index 05ca1b565e5..6ea21fc5865 100644 --- a/compiler/dex/compiler_enums.h +++ b/compiler/dex/compiler_enums.h @@ -181,6 +181,8 @@ enum OpKind { kOpBic, kOpCmn, kOpTst, + kOpRev, + kOpRevsh, kOpBkpt, kOpBlx, kOpPush, diff --git a/compiler/dex/quick/arm/arm_lir.h b/compiler/dex/quick/arm/arm_lir.h index d18467304f9..2ff7f1ca6f1 100644 --- a/compiler/dex/quick/arm/arm_lir.h +++ b/compiler/dex/quick/arm/arm_lir.h @@ -296,6 +296,8 @@ enum ArmOpcode { kThumbOrr, // orr [0100001100] rm[5..3] rd[2..0]. kThumbPop, // pop [1011110] r[8..8] rl[7..0]. kThumbPush, // push [1011010] r[8..8] rl[7..0]. + kThumbRev, // rev [1011101000] rm[5..3] rd[2..0] + kThumbRevsh, // revsh [1011101011] rm[5..3] rd[2..0] kThumbRorRR, // ror [0100000111] rs[5..3] rd[2..0]. kThumbSbc, // sbc [0100000110] rm[5..3] rd[2..0]. kThumbStmia, // stmia [11000] rn[10..8] reglist [7.. 0]. @@ -399,6 +401,8 @@ enum ArmOpcode { kThumb2AdcRRI8, // adc [111100010101] rn[19..16] [0] imm3 rd[11..8] imm8. kThumb2SubRRI8, // sub [111100011011] rn[19..16] [0] imm3 rd[11..8] imm8. kThumb2SbcRRI8, // sbc [111100010111] rn[19..16] [0] imm3 rd[11..8] imm8. + kThumb2RevRR, // rev [111110101001] rm[19..16] [1111] rd[11..8] 1000 rm[3..0] + kThumb2RevshRR, // rev [111110101001] rm[19..16] [1111] rd[11..8] 1011 rm[3..0] kThumb2It, // it [10111111] firstcond[7-4] mask[3-0]. kThumb2Fmstat, // fmstat [11101110111100011111101000010000]. kThumb2Vcmpd, // vcmp [111011101] D [11011] rd[15-12] [1011] E [1] M [0] rm[3-0]. diff --git a/compiler/dex/quick/arm/assemble_arm.cc b/compiler/dex/quick/arm/assemble_arm.cc index 2a6e656901d..e8c188c4c9e 100644 --- a/compiler/dex/quick/arm/assemble_arm.cc +++ b/compiler/dex/quick/arm/assemble_arm.cc @@ -327,6 +327,16 @@ const ArmEncodingMap ArmMir2Lir::EncodingMap[kArmLast] = { kFmtUnused, -1, -1, IS_UNARY_OP | REG_DEF_SP | REG_USE_SP | REG_USE_LIST0 | IS_STORE, "push", "", 2, kFixupNone), + ENCODING_MAP(kThumbRev, 0xba00, + kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1, + kFmtUnused, -1, -1, + IS_BINARY_OP | REG_DEF0_USE1, + "rev", "!0C, !1C", 2, kFixupNone), + ENCODING_MAP(kThumbRevsh, 0xbac0, + kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1, + kFmtUnused, -1, -1, + IS_BINARY_OP | REG_DEF0_USE1, + "rev", "!0C, !1C", 2, kFixupNone), ENCODING_MAP(kThumbRorRR, 0x41c0, kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1, kFmtUnused, -1, -1, @@ -768,6 +778,16 @@ const ArmEncodingMap ArmMir2Lir::EncodingMap[kArmLast] = { kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1 | SETS_CCODES | USES_CCODES, "sbcs", "!0C, !1C, #!2m", 4, kFixupNone), + ENCODING_MAP(kThumb2RevRR, 0xfa90f080, + kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0, + kFmtUnused, -1, -1, + IS_TERTIARY_OP | REG_DEF0_USE12, // Binary, but rm is stored twice. + "rev", "!0C, !1C", 4, kFixupNone), + ENCODING_MAP(kThumb2RevshRR, 0xfa90f0b0, + kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0, + kFmtUnused, -1, -1, + IS_TERTIARY_OP | REG_DEF0_USE12, // Binary, but rm is stored twice. + "revsh", "!0C, !1C", 4, kFixupNone), ENCODING_MAP(kThumb2It, 0xbf00, kFmtBitBlt, 7, 4, kFmtBitBlt, 3, 0, kFmtModImm, -1, -1, kFmtUnused, -1, -1, IS_BINARY_OP | IS_IT | USES_CCODES, diff --git a/compiler/dex/quick/arm/utility_arm.cc b/compiler/dex/quick/arm/utility_arm.cc index a2ac6ef53bc..3ceeacf5b15 100644 --- a/compiler/dex/quick/arm/utility_arm.cc +++ b/compiler/dex/quick/arm/utility_arm.cc @@ -314,6 +314,22 @@ LIR* ArmMir2Lir::OpRegRegShift(OpKind op, int r_dest_src1, int r_src2, case kOpSub: opcode = (thumb_form) ? kThumbSubRRR : kThumb2SubRRR; break; + case kOpRev: + DCHECK_EQ(shift, 0); + if (!thumb_form) { + // Binary, but rm is encoded twice. + return NewLIR3(kThumb2RevRR, r_dest_src1, r_src2, r_src2); + } + opcode = kThumbRev; + break; + case kOpRevsh: + DCHECK_EQ(shift, 0); + if (!thumb_form) { + // Binary, but rm is encoded twice. + return NewLIR3(kThumb2RevshRR, r_dest_src1, r_src2, r_src2); + } + opcode = kThumbRevsh; + break; case kOp2Byte: DCHECK_EQ(shift, 0); return NewLIR4(kThumb2Sbfx, r_dest_src1, r_src2, 0, 8); diff --git a/compiler/dex/quick/x86/assemble_x86.cc b/compiler/dex/quick/x86/assemble_x86.cc index 92d58d5f3c7..2047f307658 100644 --- a/compiler/dex/quick/x86/assemble_x86.cc +++ b/compiler/dex/quick/x86/assemble_x86.cc @@ -246,6 +246,8 @@ ENCODING_MAP(Cmp, IS_LOAD, 0, 0, UNARY_ENCODING_MAP(Idivmod, 0x7, 0, SETS_CCODES, DaR, kRegRegReg, IS_UNARY_OP | REG_USE0, DaM, kRegRegMem, IS_BINARY_OP | REG_USE0, DaA, kRegRegArray, IS_QUAD_OP | REG_USE01, 0, REG_DEFA_USEA, REG_DEFAD_USEAD, REG_DEFAD_USEAD, "ah:al,ax,", "dx:ax,dx:ax,", "edx:eax,edx:eax,"), #undef UNARY_ENCODING_MAP + { kX86Bswap32R, kRegOpcode, IS_UNARY_OP | REG_DEF0_USE0, { 0, 0, 0x0F, 0xC8, 0, 0, 0, 0 }, "Bswap32R", "!0r" }, + #define EXT_0F_ENCODING_MAP(opname, prefix, opcode, reg_def) \ { kX86 ## opname ## RR, kRegReg, IS_BINARY_OP | reg_def | REG_USE01, { prefix, 0, 0x0F, opcode, 0, 0, 0, 0 }, #opname "RR", "!0r,!1r" }, \ { kX86 ## opname ## RM, kRegMem, IS_LOAD | IS_TERTIARY_OP | reg_def | REG_USE01, { prefix, 0, 0x0F, opcode, 0, 0, 0, 0 }, #opname "RM", "!0r,[!1r+!2d]" }, \ @@ -371,6 +373,8 @@ int X86Mir2Lir::GetInsnSize(LIR* lir) { return lir->operands[0]; // length of nop is sole operand case kNullary: return 1; // 1 byte of opcode + case kRegOpcode: // lir operands - 0: reg + return ComputeSize(entry, 0, 0, false) - 1; // substract 1 for modrm case kReg: // lir operands - 0: reg return ComputeSize(entry, 0, 0, false); case kMem: // lir operands - 0: base, 1: disp @@ -514,6 +518,33 @@ void X86Mir2Lir::EmitDisp(int base, int disp) { } } +void X86Mir2Lir::EmitOpRegOpcode(const X86EncodingMap* entry, uint8_t reg) { + if (entry->skeleton.prefix1 != 0) { + code_buffer_.push_back(entry->skeleton.prefix1); + if (entry->skeleton.prefix2 != 0) { + code_buffer_.push_back(entry->skeleton.prefix2); + } + } else { + DCHECK_EQ(0, entry->skeleton.prefix2); + } + code_buffer_.push_back(entry->skeleton.opcode); + if (entry->skeleton.opcode == 0x0F) { + code_buffer_.push_back(entry->skeleton.extra_opcode1); + // There's no 3-byte instruction with +rd + DCHECK_NE(0x38, entry->skeleton.extra_opcode1); + DCHECK_NE(0x3A, entry->skeleton.extra_opcode1); + DCHECK_EQ(0, entry->skeleton.extra_opcode2); + } else { + DCHECK_EQ(0, entry->skeleton.extra_opcode1); + DCHECK_EQ(0, entry->skeleton.extra_opcode2); + } + DCHECK(!X86_FPREG(reg)); + DCHECK_LT(reg, 8); + code_buffer_.back() += reg; + DCHECK_EQ(0, entry->skeleton.ax_opcode); + DCHECK_EQ(0, entry->skeleton.immediate_bytes); +} + void X86Mir2Lir::EmitOpReg(const X86EncodingMap* entry, uint8_t reg) { if (entry->skeleton.prefix1 != 0) { code_buffer_.push_back(entry->skeleton.prefix1); @@ -1303,6 +1334,9 @@ AssemblerStatus X86Mir2Lir::AssembleInstructions(CodeOffset start_addr) { DCHECK_EQ(0, entry->skeleton.ax_opcode); DCHECK_EQ(0, entry->skeleton.immediate_bytes); break; + case kRegOpcode: // lir operands - 0: reg + EmitOpRegOpcode(entry, lir->operands[0]); + break; case kReg: // lir operands - 0: reg EmitOpReg(entry, lir->operands[0]); break; diff --git a/compiler/dex/quick/x86/codegen_x86.h b/compiler/dex/quick/x86/codegen_x86.h index b1d95ffcdfd..b28d7ef2f6a 100644 --- a/compiler/dex/quick/x86/codegen_x86.h +++ b/compiler/dex/quick/x86/codegen_x86.h @@ -170,6 +170,7 @@ class X86Mir2Lir : public Mir2Lir { private: void EmitDisp(int base, int disp); + void EmitOpRegOpcode(const X86EncodingMap* entry, uint8_t reg); void EmitOpReg(const X86EncodingMap* entry, uint8_t reg); void EmitOpMem(const X86EncodingMap* entry, uint8_t base, int disp); void EmitMemReg(const X86EncodingMap* entry, uint8_t base, int disp, uint8_t reg); diff --git a/compiler/dex/quick/x86/utility_x86.cc b/compiler/dex/quick/x86/utility_x86.cc index c519bfec449..6ec7ebb91ae 100644 --- a/compiler/dex/quick/x86/utility_x86.cc +++ b/compiler/dex/quick/x86/utility_x86.cc @@ -117,6 +117,7 @@ LIR* X86Mir2Lir::OpReg(OpKind op, int r_dest_src) { switch (op) { case kOpNeg: opcode = kX86Neg32R; break; case kOpNot: opcode = kX86Not32R; break; + case kOpRev: opcode = kX86Bswap32R; break; case kOpBlx: opcode = kX86CallR; break; default: LOG(FATAL) << "Bad case in OpReg " << op; @@ -161,6 +162,13 @@ LIR* X86Mir2Lir::OpRegReg(OpKind op, int r_dest_src1, int r_src2) { case kOpNeg: OpRegCopy(r_dest_src1, r_src2); return OpReg(kOpNeg, r_dest_src1); + case kOpRev: + OpRegCopy(r_dest_src1, r_src2); + return OpReg(kOpRev, r_dest_src1); + case kOpRevsh: + OpRegCopy(r_dest_src1, r_src2); + OpReg(kOpRev, r_dest_src1); + return OpRegImm(kOpAsr, r_dest_src1, 16); // X86 binary opcodes case kOpSub: opcode = kX86Sub32RR; break; case kOpSbc: opcode = kX86Sbb32RR; break; diff --git a/compiler/dex/quick/x86/x86_lir.h b/compiler/dex/quick/x86/x86_lir.h index f1b91ca7fd2..3518131cfe3 100644 --- a/compiler/dex/quick/x86/x86_lir.h +++ b/compiler/dex/quick/x86/x86_lir.h @@ -313,6 +313,7 @@ enum X86OpCode { UnaryOpcode(kX86Imul, DaR, DaM, DaA), UnaryOpcode(kX86Divmod, DaR, DaM, DaA), UnaryOpcode(kX86Idivmod, DaR, DaM, DaA), + kX86Bswap32R, #undef UnaryOpcode #define Binary0fOpCode(opcode) \ opcode ## RR, opcode ## RM, opcode ## RA @@ -381,6 +382,7 @@ enum X86EncodingKind { kData, // Special case for raw data. kNop, // Special case for variable length nop. kNullary, // Opcode that takes no arguments. + kRegOpcode, // Shorter form of R instruction kind (opcode+rd) kReg, kMem, kArray, // R, M and A instruction kinds. kMemReg, kArrayReg, kThreadReg, // MR, AR and TR instruction kinds. kRegReg, kRegMem, kRegArray, kRegThread, // RR, RM, RA and RT instruction kinds. diff --git a/disassembler/disassembler_arm.cc b/disassembler/disassembler_arm.cc index 782c1f32168..e2622161a56 100644 --- a/disassembler/disassembler_arm.cc +++ b/disassembler/disassembler_arm.cc @@ -103,6 +103,10 @@ static const char* kThumbDataProcessingOperations[] = { "tst", "rsb", "cmp", "cmn", "orr", "mul", "bic", "mvn", }; +static const char* kThumbReverseOperations[] = { + "rev", "rev16", "rbit", "revsh" +}; + struct ArmRegister { explicit ArmRegister(uint32_t r) : r(r) { CHECK_LE(r, 15U); } ArmRegister(uint32_t instruction, uint32_t at_bit) : r((instruction >> at_bit) & 0xf) { CHECK_LE(r, 15U); } @@ -995,6 +999,31 @@ size_t DisassemblerArm::DumpThumb32(std::ostream& os, const uint8_t* instr_ptr) } break; } + case 0x29: { // 0101001 + // |111|11|1000000|0000|1111|1100|00|0 0|0000| + // |5 3|21|0 4|3 0|5 2|1 8|76|5 4|3 0| + // |---|--|-------|----|----|----|--|---|----| + // |332|22|2222222|1111|1111|1100|00|0 0|0000| + // |1 9|87|6 0|9 6|5 2|1 8|76|5 4|3 0| + // |---|--|-------|----|----|----|--|---|----| + // |111|11|0101001| Rm |1111| Rd |11|op3| Rm | + // REV - 111 11 0101001 mmmm 1111 dddd 1000 mmmm + // REV16 - 111 11 0101001 mmmm 1111 dddd 1001 mmmm + // RBIT - 111 11 0101001 mmmm 1111 dddd 1010 mmmm + // REVSH - 111 11 0101001 mmmm 1111 dddd 1011 mmmm + if ((instr & 0xf0c0) == 0xf080) { + uint32_t op3 = (instr >> 4) & 3; + opcode << kThumbReverseOperations[op3]; + ArmRegister Rm(instr, 0); + ArmRegister Rd(instr, 8); + args << Rd << ", " << Rm; + ArmRegister Rm2(instr, 16); + if (Rm.r != Rm2.r || Rm.r == 13 || Rm.r == 15 || Rd.r == 13 || Rd.r == 15) { + args << " (UNPREDICTABLE)"; + } + } // else unknown instruction + break; + } case 0x05: case 0x0D: case 0x15: case 0x1D: { // 00xx101 // Load word // |111|11|10|0 0|00|0|0000|1111|110000|000000| @@ -1285,6 +1314,16 @@ size_t DisassemblerArm::DumpThumb16(std::ostream& os, const uint8_t* instr_ptr) DumpBranchTarget(args, instr_ptr + 4, imm32); break; } + case 0x50: case 0x51: // 101000x + case 0x52: case 0x53: // 101001x + case 0x56: case 0x57: { // 101011x + uint16_t op = (instr >> 6) & 3; + opcode << kThumbReverseOperations[op]; + ThumbRegister Rm(instr, 3); + ThumbRegister Rd(instr, 0); + args << Rd << ", " << Rm; + break; + } case 0x78: case 0x79: case 0x7A: case 0x7B: // 1111xxx case 0x7C: case 0x7D: case 0x7E: case 0x7F: { // If-Then, and hints diff --git a/disassembler/disassembler_x86.cc b/disassembler/disassembler_x86.cc index e5cdb7b297b..9ed65cd80a5 100644 --- a/disassembler/disassembler_x86.cc +++ b/disassembler/disassembler_x86.cc @@ -520,6 +520,10 @@ DISASSEMBLER_ENTRY(cmp, case 0xB7: opcode << "movzxw"; has_modrm = true; load = true; break; case 0xBE: opcode << "movsxb"; has_modrm = true; load = true; break; case 0xBF: opcode << "movsxw"; has_modrm = true; load = true; break; + case 0xC8: case 0xC9: case 0xCA: case 0xCB: case 0xCC: case 0xCD: case 0xCE: case 0xCF: + opcode << "bswap"; + reg_in_opcode = true; + break; default: opcode << StringPrintf("unknown opcode '0F %02X'", *instr); break; From 1f6754dc482f0175acb05275a82b9950c3d268ee Mon Sep 17 00:00:00 2001 From: Vladimir Marko Date: Mon, 28 Oct 2013 20:27:17 +0000 Subject: [PATCH 0112/2402] Fix whitespace-sensitive build. Change-Id: I82c6c49e253275543831dbaf288cb63d759ea20a --- disassembler/disassembler_arm.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/disassembler/disassembler_arm.cc b/disassembler/disassembler_arm.cc index e2622161a56..6239e9abb71 100644 --- a/disassembler/disassembler_arm.cc +++ b/disassembler/disassembler_arm.cc @@ -1021,7 +1021,7 @@ size_t DisassemblerArm::DumpThumb32(std::ostream& os, const uint8_t* instr_ptr) if (Rm.r != Rm2.r || Rm.r == 13 || Rm.r == 15 || Rd.r == 13 || Rd.r == 15) { args << " (UNPREDICTABLE)"; } - } // else unknown instruction + } // else unknown instruction break; } case 0x05: case 0x0D: case 0x15: case 0x1D: { // 00xx101 From 0d3bbff796454aca1495131ab4a866ebfef2e857 Mon Sep 17 00:00:00 2001 From: Brian Carlstrom Date: Mon, 28 Oct 2013 15:21:32 -0700 Subject: [PATCH 0113/2402] Fix DexFile_isDexOptNeeded to clear an error_msg in a recoverable case Change-Id: Id4a6d0ab8fb259fa0edcc18ce768b33e95afc28a --- runtime/native/dalvik_system_DexFile.cc | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/runtime/native/dalvik_system_DexFile.cc b/runtime/native/dalvik_system_DexFile.cc index af09a1c57dd..55a56d6c7a2 100644 --- a/runtime/native/dalvik_system_DexFile.cc +++ b/runtime/native/dalvik_system_DexFile.cc @@ -242,9 +242,16 @@ static jboolean DexFile_isDexOptNeeded(JNIEnv* env, jclass, jstring javaFilename &error_msg)) { if (kVerboseLogging) { LOG(INFO) << "DexFile_isDexOptNeeded precompiled file " << odex_filename - << " is up-to-date checksum compared to " << filename.c_str(); + << " has an up-to-date checksum compared to " << filename.c_str(); } return JNI_FALSE; + } else { + if (kVerboseLogging) { + LOG(INFO) << "DexFile_isDexOptNeeded found precompiled file " << odex_filename + << " with an out-of-date checksum compared to " << filename.c_str() + << ": " << error_msg; + } + error_msg.clear(); } } } From dc959ea822b100740424fc2eee4037512bde82c2 Mon Sep 17 00:00:00 2001 From: Brian Carlstrom Date: Mon, 28 Oct 2013 00:44:49 -0700 Subject: [PATCH 0114/2402] Make ART's run-test support Dalvik so we can remove dalvik/tests Change-Id: Iba4ddf39169346573c97b443d5d65c6b8963fd4b --- build/Android.oat.mk | 10 ++++++++ test/055-enum-performance/run | 4 ++-- test/etc/host-run-test-jar | 25 +++++++++++++------ test/etc/push-and-run-test-jar | 19 +++++++++++---- test/run-test | 44 ++++++++++++++++++++++++++++++++-- 5 files changed, 86 insertions(+), 16 deletions(-) diff --git a/build/Android.oat.mk b/build/Android.oat.mk index ea7b0b08ca9..e8438a213d2 100644 --- a/build/Android.oat.mk +++ b/build/Android.oat.mk @@ -81,6 +81,16 @@ LOCAL_ADDITIONAL_DEPENDENCIES += $(HOST_CORE_IMG_OUT) include $(BUILD_PHONY_PACKAGE) endif +ifeq ($(ART_BUILD_TARGET),true) +include $(CLEAR_VARS) +LOCAL_MODULE := core.art +LOCAL_MODULE_TAGS := optional +LOCAL_ADDITIONAL_DEPENDENCIES := art/build/Android.common.mk +LOCAL_ADDITIONAL_DEPENDENCIES += art/build/Android.oat.mk +LOCAL_ADDITIONAL_DEPENDENCIES += $(TARGET_CORE_IMG_OUT) +include $(BUILD_PHONY_PACKAGE) +endif + ######################################################################## # The full system boot classpath TARGET_BOOT_JARS := $(subst :, ,$(DEXPREOPT_BOOT_JARS)) diff --git a/test/055-enum-performance/run b/test/055-enum-performance/run index 1436ce23265..e27a622f40e 100755 --- a/test/055-enum-performance/run +++ b/test/055-enum-performance/run @@ -14,5 +14,5 @@ # See the License for the specific language governing permissions and # limitations under the License. -# As this is a performance test we always run -O -exec ${RUN} -O "$@" +# As this is a performance test we always use the non-debug build. +exec ${RUN} "${@/#libartd.so/libart.so}" diff --git a/test/etc/host-run-test-jar b/test/etc/host-run-test-jar index 357fb5adab4..da745324c97 100755 --- a/test/etc/host-run-test-jar +++ b/test/etc/host-run-test-jar @@ -9,7 +9,6 @@ msg() { fi } -LIB="libartd.so" DEBUGGER="n" GDB="n" INTERPRETER="n" @@ -23,11 +22,17 @@ while true; do if [ "x$1" = "x--quiet" ]; then QUIET="y" shift - elif [ "x$1" = "x-lib" ]; then + elif [ "x$1" = "x--lib" ]; then shift + if [ "x$1" = "x" ]; then + echo "$0 missing argument to --lib" 1>&2 + exit 1 + fi LIB="$1" - elif [ "x$1" = "x-O" ]; then - LIB="libart.so" + shift + elif [ "x$1" = "x--boot" ]; then + shift + BOOT_OPT="$1" shift elif [ "x$1" = "x--debug" ]; then DEBUGGER="y" @@ -38,6 +43,10 @@ while true; do shift elif [ "x$1" = "x--invoke-with" ]; then shift + if [ "x$1" = "x" ]; then + echo "$0 missing argument to --invoke-with" 1>&2 + exit 1 + fi if [ "x$INVOKE_WITH" = "x" ]; then INVOKE_WITH="$1" else @@ -106,7 +115,9 @@ fi JNI_OPTS="-Xjnigreflimit:512 -Xcheck:jni" +if [ "$DEV_MODE" = "y" ]; then + echo $cmdline "$@" +fi + cd $ANDROID_BUILD_TOP -$INVOKE_WITH $gdb $exe $gdbargs -XXlib:$LIB -Ximage:$ANDROID_ROOT/framework/core.art \ - $JNI_OPTS $INT_OPTS $DEBUGGER_OPTS \ - -cp $DEX_LOCATION/$TEST_NAME.jar Main "$@" +$INVOKE_WITH $gdb $exe $gdbargs -XXlib:$LIB $JNI_OPTS $INT_OPTS $DEBUGGER_OPTS $BOOT_OPT -cp $DEX_LOCATION/$TEST_NAME.jar Main "$@" diff --git a/test/etc/push-and-run-test-jar b/test/etc/push-and-run-test-jar index cc285920dfd..ff75d32d1b2 100755 --- a/test/etc/push-and-run-test-jar +++ b/test/etc/push-and-run-test-jar @@ -9,7 +9,6 @@ msg() { fi } -LIB="libartd.so" GDB="n" DEBUGGER="n" INTERPRETER="n" @@ -24,11 +23,17 @@ while true; do if [ "x$1" = "x--quiet" ]; then QUIET="y" shift - elif [ "x$1" = "x-lib" ]; then + elif [ "x$1" = "x--lib" ]; then shift + if [ "x$1" = "x" ]; then + echo "$0 missing argument to --lib" 1>&2 + exit 1 + fi LIB="$1" - elif [ "x$1" = "x-O" ]; then - LIB="libart.so" + shift + elif [ "x$1" = "x--boot" ]; then + shift + BOOT_OPT="$1" shift elif [ "x$1" = "x--debug" ]; then DEBUGGER="y" @@ -49,6 +54,10 @@ while true; do shift elif [ "x$1" = "x--invoke-with" ]; then shift + if [ "x$1" = "x" ]; then + echo "$0 missing argument to --invoke-with" 1>&2 + exit 1 + fi if [ "x$INVOKE_WITH" = "x" ]; then INVOKE_WITH="$1" else @@ -132,7 +141,7 @@ fi JNI_OPTS="-Xjnigreflimit:512 -Xcheck:jni" cmdline="cd $DEX_LOCATION && mkdir dalvik-cache && export ANDROID_DATA=$DEX_LOCATION && export DEX_LOCATION=$DEX_LOCATION && \ - $INVOKE_WITH $gdb dalvikvm $gdbargs -XXlib:$LIB $ZYGOTE $JNI_OPTS $INT_OPTS $DEBUGGER_OPTS -Ximage:/data/art-test/core.art -cp $DEX_LOCATION/$TEST_NAME.jar Main" + $INVOKE_WITH $gdb dalvikvm $gdbargs -XXlib:$LIB $ZYGOTE $JNI_OPTS $INT_OPTS $DEBUGGER_OPTS $BOOT_OPT -cp $DEX_LOCATION/$TEST_NAME.jar Main" if [ "$DEV_MODE" = "y" ]; then echo $cmdline "$@" fi diff --git a/test/run-test b/test/run-test index 11dcfc57def..a34cc72b1e6 100755 --- a/test/run-test +++ b/test/run-test @@ -58,12 +58,14 @@ run="run" expected="expected.txt" output="output.txt" build_output="build-output.txt" +lib="libartd.so" run_args="--quiet" target_mode="yes" dev_mode="no" update_mode="no" debug_mode="no" +dalvik_mode="no" usage="no" build_only="no" @@ -79,7 +81,16 @@ while true; do NEED_DEX="false" shift elif [ "x$1" = "x-O" ]; then - run_args="${run_args} -O" + lib="libart.so" + shift + elif [ "x$1" = "x--dalvik" ]; then + lib="libdvm.so" + dalvik_mode="yes" + shift + elif [ "x$1" = "x--image" ]; then + shift + image="$1" + run_args="${run_args} --image $image" shift elif [ "x$1" = "x--debug" ]; then run_args="${run_args} --debug" @@ -106,6 +117,11 @@ while true; do elif [ "x$1" = "x--invoke-with" ]; then shift what="$1" + if [ "x$what" = "x" ]; then + echo "$0 missing argument to --invoke-with" 1>&2 + usage="yes" + break + fi run_args="${run_args} --invoke-with ${what}" shift elif [ "x$1" = "x--dev" ]; then @@ -118,6 +134,11 @@ while true; do elif [ "x$1" = "x--output-path" ]; then shift tmp_dir=$1 + if [ "x$tmp_dir" = "x" ]; then + echo "$0 missing argument to --output-path" 1>&2 + usage="yes" + break + fi shift elif [ "x$1" = "x--update" ]; then update_mode="yes" @@ -134,6 +155,24 @@ while true; do fi done +run_args="${run_args} --lib $lib" + +if [ "$dalvik_mode" = "no" ]; then + if [ "$target_mode" = "no" ]; then + run_args="${run_args} --boot -Ximage:${ANDROID_HOST_OUT}/framework/core.art" + else + run_args="${run_args} --boot -Ximage:/data/art-test/core.art" + fi +else + if [ "$target_mode" = "no" ]; then + framework="${OUT}/system/framework" + bpath="${framework}/core.jar:${framework}/conscrypt.jar:${framework}/okhttp.jar:${framework}/core-junit.jar:${framework}/bouncycastle.jar:${framework}/ext.jar" + run_args="${run_args} --boot -Xbootclasspath:${bpath}" + else + true # defaults to using target BOOTCLASSPATH + fi +fi + if [ "$dev_mode" = "yes" -a "$update_mode" = "yes" ]; then echo "--dev and --update are mutually exclusive" 1>&2 usage="yes" @@ -185,6 +224,7 @@ if [ "$usage" = "yes" ]; then echo " other runtime options are ignored." echo " --host Use the host-mode virtual machine." echo " --invoke-with Pass --invoke-with option to runtime." + echo " --dalvik Use Dalvik (off by default)." echo " --jvm Use a host-local RI virtual machine." echo " --output-path [path] Location where to store the build" \ "files." @@ -237,7 +277,7 @@ if [ "$dev_mode" = "yes" ]; then if [ "$build_exit" = '0' ]; then echo "${test_dir}: running..." 1>&2 "./${run}" $run_args "$@" 2>&1 - run_exit="$?" + run_exit="$?" echo "run exit status: $run_exit" 1>&2 if [ "$run_exit" = "0" ]; then good="yes" From ad3d996316dd90b84b4b29ccdfc4aeeb1ec890ee Mon Sep 17 00:00:00 2001 From: Sebastien Hertz Date: Tue, 29 Oct 2013 14:20:14 +0100 Subject: [PATCH 0115/2402] Fix test failure in SMART mode. Update test 082-inline-execute to avoid a verifier issue with quickening. An invoke-virtual is quickened into an invoke-virtual-quick. However it is made on a "null" instance. During verification, we can't infer the method being invoked from the vtable index since we have no access to the class. This leads to fail on a check and abort. Bug: 11427954 Change-Id: I740d67c72abb617db67a2c35d9be8346358f78ce --- test/082-inline-execute/src/Main.java | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/test/082-inline-execute/src/Main.java b/test/082-inline-execute/src/Main.java index f4d2dd114a9..b881b348021 100644 --- a/test/082-inline-execute/src/Main.java +++ b/test/082-inline-execute/src/Main.java @@ -37,6 +37,10 @@ public static void main(String args[]) { test_String_length(); } + private static String nullString() { + return null; + } + public static void test_String_length() { String str0 = ""; String str1 = "x"; @@ -46,7 +50,7 @@ public static void test_String_length() { Assert.assertEquals(str1.length(), 1); Assert.assertEquals(str80.length(), 80); - String strNull = null; + String strNull = nullString(); try { strNull.length(); Assert.fail(); @@ -61,7 +65,7 @@ public static void test_String_isEmpty() { Assert.assertTrue(str0.isEmpty()); Assert.assertFalse(str1.isEmpty()); - String strNull = null; + String strNull = nullString(); try { strNull.isEmpty(); Assert.fail(); @@ -88,7 +92,7 @@ public static void test_String_charAt() { } catch (StringIndexOutOfBoundsException expected) { } - String strNull = null; + String strNull = nullString(); try { strNull.charAt(0); Assert.fail(); @@ -133,7 +137,7 @@ public static void test_String_indexOf() { Assert.assertEquals(str40.indexOf('a',10), 10); Assert.assertEquals(str40.indexOf('b',40), -1); - String strNull = null; + String strNull = nullString(); try { strNull.indexOf('a'); Assert.fail(); From 8249b425ba81d804c222c746e31bfcac9516e759 Mon Sep 17 00:00:00 2001 From: Sebastien Hertz Date: Tue, 29 Oct 2013 17:50:55 +0100 Subject: [PATCH 0116/2402] Avoid verifier crash for quickened invoke on null. When verifying an invoke-virtual-quick on a "null" instance, we can't infer the class of the method being invoked. This CL handles this case and avoid a crash due to a failed check in RegType::GetClass. Also revert changes made to test 082-inline-execute since it succeeds with this CL now. Bug: 11427954 Change-Id: I4b2c1deaa43b144684539acea471543716f36fb3 --- runtime/verifier/method_verifier.cc | 2 ++ test/082-inline-execute/src/Main.java | 12 ++++-------- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc index 7d2ee19572e..663268137b0 100644 --- a/runtime/verifier/method_verifier.cc +++ b/runtime/verifier/method_verifier.cc @@ -3140,6 +3140,8 @@ mirror::ArtMethod* MethodVerifier::GetQuickInvokedMethod(const Instruction* inst const RegType& actual_arg_type = reg_line->GetInvocationThis(inst, is_range); if (actual_arg_type.IsConflict()) { // GetInvocationThis failed. return NULL; + } else if (actual_arg_type.IsZero()) { // Invoke on "null" instance: we can't go further. + return NULL; } mirror::Class* this_class = NULL; if (!actual_arg_type.IsUnresolvedTypes()) { diff --git a/test/082-inline-execute/src/Main.java b/test/082-inline-execute/src/Main.java index b881b348021..f4d2dd114a9 100644 --- a/test/082-inline-execute/src/Main.java +++ b/test/082-inline-execute/src/Main.java @@ -37,10 +37,6 @@ public static void main(String args[]) { test_String_length(); } - private static String nullString() { - return null; - } - public static void test_String_length() { String str0 = ""; String str1 = "x"; @@ -50,7 +46,7 @@ public static void test_String_length() { Assert.assertEquals(str1.length(), 1); Assert.assertEquals(str80.length(), 80); - String strNull = nullString(); + String strNull = null; try { strNull.length(); Assert.fail(); @@ -65,7 +61,7 @@ public static void test_String_isEmpty() { Assert.assertTrue(str0.isEmpty()); Assert.assertFalse(str1.isEmpty()); - String strNull = nullString(); + String strNull = null; try { strNull.isEmpty(); Assert.fail(); @@ -92,7 +88,7 @@ public static void test_String_charAt() { } catch (StringIndexOutOfBoundsException expected) { } - String strNull = nullString(); + String strNull = null; try { strNull.charAt(0); Assert.fail(); @@ -137,7 +133,7 @@ public static void test_String_indexOf() { Assert.assertEquals(str40.indexOf('a',10), 10); Assert.assertEquals(str40.indexOf('b',40), -1); - String strNull = nullString(); + String strNull = null; try { strNull.indexOf('a'); Assert.fail(); From 6bdf1fff5f841f3997d4b488f00647f7aa2cdaa3 Mon Sep 17 00:00:00 2001 From: Vladimir Marko Date: Tue, 29 Oct 2013 17:40:46 +0000 Subject: [PATCH 0117/2402] Add intrinsics for {Short,Int,Long}.reverseBytes(). Change-Id: I34a2ec642f59fc4ff18aed59769a9e8d7e361098 --- compiler/dex/quick/gen_invoke.cc | 38 ++++++++++++++++++++++++++++++++ compiler/dex/quick/mir_to_lir.h | 1 + 2 files changed, 39 insertions(+) diff --git a/compiler/dex/quick/gen_invoke.cc b/compiler/dex/quick/gen_invoke.cc index 62feadedccf..267495414af 100644 --- a/compiler/dex/quick/gen_invoke.cc +++ b/compiler/dex/quick/gen_invoke.cc @@ -971,6 +971,29 @@ bool Mir2Lir::GenInlinedStringIsEmptyOrLength(CallInfo* info, bool is_empty) { return true; } +bool Mir2Lir::GenInlinedReverseBytes(CallInfo* info, OpSize size) { + if (cu_->instruction_set == kMips) { + // TODO - add Mips implementation + return false; + } + RegLocation rl_src_i = info->args[0]; + RegLocation rl_dest = InlineTarget(info); // result reg + RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true); + if (size == kLong) { + RegLocation rl_i = LoadValueWide(rl_src_i, kCoreReg); + OpRegReg(kOpRev, rl_result.low_reg, rl_i.high_reg); + OpRegReg(kOpRev, rl_result.high_reg, rl_i.low_reg); + StoreValueWide(rl_dest, rl_result); + } else { + DCHECK(size == kWord || size == kSignedHalf); + OpKind op = (size == kWord) ? kOpRev : kOpRevsh; + RegLocation rl_i = LoadValue(rl_src_i, kCoreReg); + OpRegReg(op, rl_result.low_reg, rl_i.low_reg); + StoreValue(rl_dest, rl_result); + } + return true; +} + bool Mir2Lir::GenInlinedAbsInt(CallInfo* info) { if (cu_->instruction_set == kMips) { // TODO - add Mips implementation @@ -1249,6 +1272,16 @@ bool Mir2Lir::GenIntrinsic(CallInfo* info) { if (tgt_method == "float java.lang.Float.intBitsToFloat(int)") { return GenInlinedFloatCvt(info); } + } else if (tgt_methods_declaring_class.starts_with("Ljava/lang/Integer;")) { + std::string tgt_method(PrettyMethod(info->index, *cu_->dex_file)); + if (tgt_method == "int java.lang.Integer.reverseBytes(int)") { + return GenInlinedReverseBytes(info, kWord); + } + } else if (tgt_methods_declaring_class.starts_with("Ljava/lang/Long;")) { + std::string tgt_method(PrettyMethod(info->index, *cu_->dex_file)); + if (tgt_method == "long java.lang.Long.reverseBytes(long)") { + return GenInlinedReverseBytes(info, kLong); + } } else if (tgt_methods_declaring_class.starts_with("Ljava/lang/Math;") || tgt_methods_declaring_class.starts_with("Ljava/lang/StrictMath;")) { std::string tgt_method(PrettyMethod(info->index, *cu_->dex_file)); @@ -1272,6 +1305,11 @@ bool Mir2Lir::GenIntrinsic(CallInfo* info) { tgt_method == "double java.lang.StrictMath.sqrt(double)") { return GenInlinedSqrt(info); } + } else if (tgt_methods_declaring_class.starts_with("Ljava/lang/Short;")) { + std::string tgt_method(PrettyMethod(info->index, *cu_->dex_file)); + if (tgt_method == "short java.lang.Short.reverseBytes(short)") { + return GenInlinedReverseBytes(info, kSignedHalf); + } } else if (tgt_methods_declaring_class.starts_with("Ljava/lang/String;")) { std::string tgt_method(PrettyMethod(info->index, *cu_->dex_file)); if (tgt_method == "char java.lang.String.charAt(int)") { diff --git a/compiler/dex/quick/mir_to_lir.h b/compiler/dex/quick/mir_to_lir.h index d629b44ddbd..7e9848d58b3 100644 --- a/compiler/dex/quick/mir_to_lir.h +++ b/compiler/dex/quick/mir_to_lir.h @@ -544,6 +544,7 @@ class Mir2Lir : public Backend { bool GenInlinedCharAt(CallInfo* info); bool GenInlinedStringIsEmptyOrLength(CallInfo* info, bool is_empty); + bool GenInlinedReverseBytes(CallInfo* info, OpSize size); bool GenInlinedAbsInt(CallInfo* info); bool GenInlinedAbsLong(CallInfo* info); bool GenInlinedFloatCvt(CallInfo* info); From 88474b416eb257078e590bf9bc7957cee604a186 Mon Sep 17 00:00:00 2001 From: Jeff Hao Date: Wed, 23 Oct 2013 16:24:40 -0700 Subject: [PATCH 0118/2402] Implement Interface Method Tables (IMT). Change-Id: Idf7fe85e1293453a8ad862ff2380dcd5db4e3a39 --- compiler/dex/compiler_enums.h | 2 + compiler/dex/quick/arm/target_arm.cc | 2 + compiler/dex/quick/gen_invoke.cc | 101 ++++++++---------- compiler/dex/quick/mips/target_mips.cc | 2 + compiler/dex/quick/x86/target_x86.cc | 2 + compiler/driver/compiler_driver.cc | 28 +++-- compiler/driver/compiler_driver.h | 4 + compiler/image_writer.cc | 12 +++ compiler/image_writer.h | 5 +- compiler/oat_test.cc | 2 +- compiler/oat_writer.cc | 10 ++ compiler/oat_writer.h | 4 + dex2oat/dex2oat.cc | 4 - oatdump/oatdump.cc | 5 +- runtime/arch/arm/entrypoints_init_arm.cc | 4 +- runtime/arch/arm/quick_entrypoints_arm.S | 12 +++ runtime/arch/mips/entrypoints_init_mips.cc | 4 +- runtime/arch/mips/quick_entrypoints_mips.S | 15 +++ runtime/arch/x86/entrypoints_init_x86.cc | 4 +- runtime/arch/x86/quick_entrypoints_x86.S | 14 +++ runtime/asm_support.h | 3 +- runtime/class_linker.cc | 39 ++++++- runtime/class_linker.h | 19 ++++ runtime/class_linker_test.cc | 7 +- runtime/common_test.h | 3 - runtime/entrypoints/entrypoint_utils.h | 38 +++++-- .../portable/portable_entrypoints.h | 1 + runtime/entrypoints/quick/quick_entrypoints.h | 2 +- runtime/gc/space/image_space.cc | 4 + runtime/image.h | 2 + runtime/mirror/art_method-inl.h | 7 ++ runtime/mirror/art_method.h | 2 + runtime/mirror/class-inl.h | 8 ++ runtime/mirror/class.h | 13 +++ runtime/mirror/object_test.cc | 1 + runtime/mirror/string.h | 2 +- runtime/oat.cc | 50 ++++++++- runtime/oat.h | 8 ++ runtime/object_utils.h | 2 + runtime/runtime.cc | 31 ++++++ runtime/runtime.h | 38 +++++++ runtime/thread.cc | 3 +- test/100-reflect2/expected.txt | 2 +- test/Android.mk | 1 + test/InterfaceTest/InterfaceTest.java | 53 +++++++++ 45 files changed, 468 insertions(+), 107 deletions(-) create mode 100644 test/InterfaceTest/InterfaceTest.java diff --git a/compiler/dex/compiler_enums.h b/compiler/dex/compiler_enums.h index 6ea21fc5865..56facfd8892 100644 --- a/compiler/dex/compiler_enums.h +++ b/compiler/dex/compiler_enums.h @@ -44,6 +44,8 @@ enum SpecialTargetRegister { kRet0, kRet1, kInvokeTgt, + kHiddenArg, + kHiddenFpArg, kCount }; diff --git a/compiler/dex/quick/arm/target_arm.cc b/compiler/dex/quick/arm/target_arm.cc index 3395ae7a443..52aba9b4dfd 100644 --- a/compiler/dex/quick/arm/target_arm.cc +++ b/compiler/dex/quick/arm/target_arm.cc @@ -74,6 +74,8 @@ int ArmMir2Lir::TargetReg(SpecialTargetRegister reg) { case kRet0: res = rARM_RET0; break; case kRet1: res = rARM_RET1; break; case kInvokeTgt: res = rARM_INVOKE_TGT; break; + case kHiddenArg: res = r12; break; + case kHiddenFpArg: res = INVALID_REG; break; case kCount: res = rARM_COUNT; break; } return res; diff --git a/compiler/dex/quick/gen_invoke.cc b/compiler/dex/quick/gen_invoke.cc index 62feadedccf..0a5fbb1f179 100644 --- a/compiler/dex/quick/gen_invoke.cc +++ b/compiler/dex/quick/gen_invoke.cc @@ -491,69 +491,56 @@ static int NextVCallInsn(CompilationUnit* cu, CallInfo* info, } /* - * All invoke-interface calls bounce off of art_quick_invoke_interface_trampoline, - * which will locate the target and continue on via a tail call. + * Emit the next instruction in an invoke interface sequence. This will do a lookup in the + * class's IMT, calling either the actual method or art_quick_imt_conflict_trampoline if + * more than one interface method map to the same index. Note also that we'll load the first + * argument ("this") into kArg1 here rather than the standard LoadArgRegs. */ static int NextInterfaceCallInsn(CompilationUnit* cu, CallInfo* info, int state, const MethodReference& target_method, - uint32_t unused, uintptr_t unused2, - uintptr_t direct_method, InvokeType unused4) { + uint32_t method_idx, uintptr_t unused, + uintptr_t direct_method, InvokeType unused2) { Mir2Lir* cg = static_cast(cu->cg.get()); - ThreadOffset trampoline = QUICK_ENTRYPOINT_OFFSET(pInvokeInterfaceTrampoline); - if (direct_method != 0) { - switch (state) { - case 0: // Load the trampoline target [sets kInvokeTgt]. - if (cu->instruction_set != kX86) { - cg->LoadWordDisp(cg->TargetReg(kSelf), trampoline.Int32Value(), - cg->TargetReg(kInvokeTgt)); - } - // Get the interface Method* [sets kArg0] - if (direct_method != static_cast(-1)) { - cg->LoadConstant(cg->TargetReg(kArg0), direct_method); - } else { - CHECK_EQ(cu->dex_file, target_method.dex_file); - LIR* data_target = cg->ScanLiteralPool(cg->method_literal_list_, - target_method.dex_method_index, 0); - if (data_target == NULL) { - data_target = cg->AddWordData(&cg->method_literal_list_, - target_method.dex_method_index); - data_target->operands[1] = kInterface; - } - LIR* load_pc_rel = cg->OpPcRelLoad(cg->TargetReg(kArg0), data_target); - cg->AppendLIR(load_pc_rel); - DCHECK_EQ(cu->instruction_set, kThumb2) << reinterpret_cast(data_target); - } - break; - default: - return -1; + switch (state) { + case 0: // Set target method index in case of conflict [set kHiddenArg, kHiddenFpArg (x86)] + CHECK_EQ(cu->dex_file, target_method.dex_file); + CHECK_LT(target_method.dex_method_index, target_method.dex_file->NumMethodIds()); + cg->LoadConstant(cg->TargetReg(kHiddenArg), target_method.dex_method_index); + if (cu->instruction_set == kX86) { + cg->OpRegCopy(cg->TargetReg(kHiddenFpArg), cg->TargetReg(kHiddenArg)); + } + break; + case 1: { // Get "this" [set kArg1] + RegLocation rl_arg = info->args[0]; + cg->LoadValueDirectFixed(rl_arg, cg->TargetReg(kArg1)); + break; } - } else { - switch (state) { - case 0: - // Get the current Method* [sets kArg0] - TUNING: remove copy of method if it is promoted. - cg->LoadCurrMethodDirect(cg->TargetReg(kArg0)); - // Load the trampoline target [sets kInvokeTgt]. - if (cu->instruction_set != kX86) { - cg->LoadWordDisp(cg->TargetReg(kSelf), trampoline.Int32Value(), - cg->TargetReg(kInvokeTgt)); - } - break; - case 1: // Get method->dex_cache_resolved_methods_ [set/use kArg0] - cg->LoadWordDisp(cg->TargetReg(kArg0), - mirror::ArtMethod::DexCacheResolvedMethodsOffset().Int32Value(), - cg->TargetReg(kArg0)); + case 2: // Is "this" null? [use kArg1] + cg->GenNullCheck(info->args[0].s_reg_low, cg->TargetReg(kArg1), info->opt_flags); + // Get this->klass_ [use kArg1, set kInvokeTgt] + cg->LoadWordDisp(cg->TargetReg(kArg1), mirror::Object::ClassOffset().Int32Value(), + cg->TargetReg(kInvokeTgt)); break; - case 2: // Grab target method* [set/use kArg0] - CHECK_EQ(cu->dex_file, target_method.dex_file); - cg->LoadWordDisp(cg->TargetReg(kArg0), - mirror::Array::DataOffset(sizeof(mirror::Object*)).Int32Value() + - (target_method.dex_method_index * 4), + case 3: // Get this->klass_->imtable [use kInvokeTgt, set kInvokeTgt] + cg->LoadWordDisp(cg->TargetReg(kInvokeTgt), mirror::Class::ImTableOffset().Int32Value(), + cg->TargetReg(kInvokeTgt)); + break; + case 4: // Get target method [use kInvokeTgt, set kArg0] + cg->LoadWordDisp(cg->TargetReg(kInvokeTgt), ((method_idx % ClassLinker::kImtSize) * 4) + + mirror::Array::DataOffset(sizeof(mirror::Object*)).Int32Value(), cg->TargetReg(kArg0)); break; + case 5: // Get the compiled code address [use kArg0, set kInvokeTgt] + if (cu->instruction_set != kX86) { + cg->LoadWordDisp(cg->TargetReg(kArg0), + mirror::ArtMethod::GetEntryPointFromCompiledCodeOffset().Int32Value(), + cg->TargetReg(kInvokeTgt)); + break; + } + // Intentional fallthrough for X86 default: return -1; - } } return state + 1; } @@ -1390,11 +1377,8 @@ void Mir2Lir::GenInvoke(CallInfo* info) { &vtable_idx, &direct_code, &direct_method) && !SLOW_INVOKE_PATH; if (info->type == kInterface) { - if (fast_path) { - p_null_ck = &null_ck; - } next_call_insn = fast_path ? NextInterfaceCallInsn : NextInterfaceCallInsnWithAccessCheck; - skip_this = false; + skip_this = fast_path; } else if (info->type == kDirect) { if (fast_path) { p_null_ck = &null_ck; @@ -1434,15 +1418,14 @@ void Mir2Lir::GenInvoke(CallInfo* info) { if (cu_->instruction_set != kX86) { call_inst = OpReg(kOpBlx, TargetReg(kInvokeTgt)); } else { - if (fast_path && info->type != kInterface) { + if (fast_path) { call_inst = OpMem(kOpBlx, TargetReg(kArg0), mirror::ArtMethod::GetEntryPointFromCompiledCodeOffset().Int32Value()); } else { ThreadOffset trampoline(-1); switch (info->type) { case kInterface: - trampoline = fast_path ? QUICK_ENTRYPOINT_OFFSET(pInvokeInterfaceTrampoline) - : QUICK_ENTRYPOINT_OFFSET(pInvokeInterfaceTrampolineWithAccessCheck); + trampoline = QUICK_ENTRYPOINT_OFFSET(pInvokeInterfaceTrampolineWithAccessCheck); break; case kDirect: trampoline = QUICK_ENTRYPOINT_OFFSET(pInvokeDirectTrampolineWithAccessCheck); diff --git a/compiler/dex/quick/mips/target_mips.cc b/compiler/dex/quick/mips/target_mips.cc index 0ee32d4d21c..9c598e6bee0 100644 --- a/compiler/dex/quick/mips/target_mips.cc +++ b/compiler/dex/quick/mips/target_mips.cc @@ -76,6 +76,8 @@ int MipsMir2Lir::TargetReg(SpecialTargetRegister reg) { case kRet0: res = rMIPS_RET0; break; case kRet1: res = rMIPS_RET1; break; case kInvokeTgt: res = rMIPS_INVOKE_TGT; break; + case kHiddenArg: res = r_T0; break; + case kHiddenFpArg: res = INVALID_REG; break; case kCount: res = rMIPS_COUNT; break; } return res; diff --git a/compiler/dex/quick/x86/target_x86.cc b/compiler/dex/quick/x86/target_x86.cc index 901ac9e69d9..878fa769b6b 100644 --- a/compiler/dex/quick/x86/target_x86.cc +++ b/compiler/dex/quick/x86/target_x86.cc @@ -85,6 +85,8 @@ int X86Mir2Lir::TargetReg(SpecialTargetRegister reg) { case kRet0: res = rX86_RET0; break; case kRet1: res = rX86_RET1; break; case kInvokeTgt: res = rX86_INVOKE_TGT; break; + case kHiddenArg: res = rAX; break; + case kHiddenFpArg: res = fr0; break; case kCount: res = rX86_COUNT; break; } return res; diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc index 91b0188b7ba..053ea16fe87 100644 --- a/compiler/driver/compiler_driver.cc +++ b/compiler/driver/compiler_driver.cc @@ -469,6 +469,11 @@ const std::vector* CompilerDriver::CreateJniDlsymLookup() const { return CreateTrampoline(instruction_set_, kJniAbi, JNI_ENTRYPOINT_OFFSET(pDlsymLookup)); } +const std::vector* CompilerDriver::CreatePortableImtConflictTrampoline() const { + return CreateTrampoline(instruction_set_, kPortableAbi, + PORTABLE_ENTRYPOINT_OFFSET(pPortableImtConflictTrampoline)); +} + const std::vector* CompilerDriver::CreatePortableResolutionTrampoline() const { return CreateTrampoline(instruction_set_, kPortableAbi, PORTABLE_ENTRYPOINT_OFFSET(pPortableResolutionTrampoline)); @@ -479,6 +484,11 @@ const std::vector* CompilerDriver::CreatePortableToInterpreterBridge() PORTABLE_ENTRYPOINT_OFFSET(pPortableToInterpreterBridge)); } +const std::vector* CompilerDriver::CreateQuickImtConflictTrampoline() const { + return CreateTrampoline(instruction_set_, kQuickAbi, + QUICK_ENTRYPOINT_OFFSET(pQuickImtConflictTrampoline)); +} + const std::vector* CompilerDriver::CreateQuickResolutionTrampoline() const { return CreateTrampoline(instruction_set_, kQuickAbi, QUICK_ENTRYPOINT_OFFSET(pQuickResolutionTrampoline)); @@ -1080,7 +1090,7 @@ void CompilerDriver::GetCodeAndMethodForDirectCall(InvokeType* type, InvokeType } use_dex_cache = true; } else { - if (sharp_type != kStatic && sharp_type != kDirect && sharp_type != kInterface) { + if (sharp_type != kStatic && sharp_type != kDirect) { return; } // TODO: support patching on all architectures. @@ -1101,9 +1111,7 @@ void CompilerDriver::GetCodeAndMethodForDirectCall(InvokeType* type, InvokeType } } if (update_stats && method_code_in_boot) { - if (sharp_type != kInterface) { // Interfaces always go via a trampoline until we get IMTs. - stats_->DirectCallsToBoot(*type); - } + stats_->DirectCallsToBoot(*type); stats_->DirectMethodsToBoot(*type); } if (!use_dex_cache && compiling_boot) { @@ -1145,19 +1153,15 @@ void CompilerDriver::GetCodeAndMethodForDirectCall(InvokeType* type, InvokeType if (compiling_boot) { *type = sharp_type; *direct_method = -1; - if (sharp_type != kInterface) { - *direct_code = -1; - } + *direct_code = -1; } else { bool method_in_image = Runtime::Current()->GetHeap()->FindSpaceFromObject(method, false)->IsImageSpace(); if (method_in_image) { - CHECK_EQ(method->IsAbstract(), sharp_type == kInterface); + CHECK(!method->IsAbstract()); *type = sharp_type; *direct_method = reinterpret_cast(method); - if (*type != kInterface) { - *direct_code = reinterpret_cast(method->GetEntryPointFromCompiledCode()); - } + *direct_code = reinterpret_cast(method->GetEntryPointFromCompiledCode()); target_method->dex_file = method->GetDeclaringClass()->GetDexCache()->GetDexFile(); target_method->dex_method_index = method->GetDexMethodIndex(); } else if (!must_use_direct_pointers) { @@ -1187,6 +1191,8 @@ bool CompilerDriver::ComputeInvokeInfo(const DexCompilationUnit* mUnit, const ui if (resolved_method != NULL) { if (*invoke_type == kVirtual || *invoke_type == kSuper) { *vtable_idx = resolved_method->GetMethodIndex(); + } else if (*invoke_type == kInterface) { + *vtable_idx = resolved_method->GetDexMethodIndex(); } // Don't try to fast-path if we don't understand the caller's class or this appears to be an // Incompatible Class Change Error. diff --git a/compiler/driver/compiler_driver.h b/compiler/driver/compiler_driver.h index 971021f9034..c79175394a7 100644 --- a/compiler/driver/compiler_driver.h +++ b/compiler/driver/compiler_driver.h @@ -130,10 +130,14 @@ class CompilerDriver { SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); const std::vector* CreateJniDlsymLookup() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + const std::vector* CreatePortableImtConflictTrampoline() const + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); const std::vector* CreatePortableResolutionTrampoline() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); const std::vector* CreatePortableToInterpreterBridge() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + const std::vector* CreateQuickImtConflictTrampoline() const + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); const std::vector* CreateQuickResolutionTrampoline() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); const std::vector* CreateQuickToInterpreterBridge() const diff --git a/compiler/image_writer.cc b/compiler/image_writer.cc index 871cfd5c41b..af61bcc79a1 100644 --- a/compiler/image_writer.cc +++ b/compiler/image_writer.cc @@ -98,11 +98,15 @@ bool ImageWriter::Write(const std::string& image_filename, jni_dlsym_lookup_offset_ = oat_file_->GetOatHeader().GetJniDlsymLookupOffset(); + portable_imt_conflict_trampoline_offset_ = + oat_file_->GetOatHeader().GetPortableImtConflictTrampolineOffset(); portable_resolution_trampoline_offset_ = oat_file_->GetOatHeader().GetPortableResolutionTrampolineOffset(); portable_to_interpreter_bridge_offset_ = oat_file_->GetOatHeader().GetPortableToInterpreterBridgeOffset(); + quick_imt_conflict_trampoline_offset_ = + oat_file_->GetOatHeader().GetQuickImtConflictTrampolineOffset(); quick_resolution_trampoline_offset_ = oat_file_->GetOatHeader().GetQuickResolutionTrampolineOffset(); quick_to_interpreter_bridge_offset_ = @@ -390,6 +394,8 @@ ObjectArray* ImageWriter::CreateImageRoots() const { ObjectArray::Alloc(self, object_array_class, ImageHeader::kImageRootsMax)); image_roots->Set(ImageHeader::kResolutionMethod, runtime->GetResolutionMethod()); + image_roots->Set(ImageHeader::kImtConflictMethod, runtime->GetImtConflictMethod()); + image_roots->Set(ImageHeader::kDefaultImt, runtime->GetDefaultImt()); image_roots->Set(ImageHeader::kCalleeSaveMethod, runtime->GetCalleeSaveMethod(Runtime::kSaveAll)); image_roots->Set(ImageHeader::kRefsOnlySaveMethod, @@ -526,6 +532,12 @@ void ImageWriter::FixupMethod(const ArtMethod* orig, ArtMethod* copy) { copy->SetEntryPointFromCompiledCode(GetOatAddress(portable_resolution_trampoline_offset_)); #else copy->SetEntryPointFromCompiledCode(GetOatAddress(quick_resolution_trampoline_offset_)); +#endif + } else if (UNLIKELY(orig == Runtime::Current()->GetImtConflictMethod())) { +#if defined(ART_USE_PORTABLE_COMPILER) + copy->SetEntryPointFromCompiledCode(GetOatAddress(portable_imt_conflict_trampoline_offset_)); +#else + copy->SetEntryPointFromCompiledCode(GetOatAddress(quick_imt_conflict_trampoline_offset_)); #endif } else { // We assume all methods have code. If they don't currently then we set them to the use the diff --git a/compiler/image_writer.h b/compiler/image_writer.h index 0d85f36a5b4..0b408e85cc7 100644 --- a/compiler/image_writer.h +++ b/compiler/image_writer.h @@ -40,7 +40,8 @@ class ImageWriter { explicit ImageWriter(const CompilerDriver& compiler_driver) : compiler_driver_(compiler_driver), oat_file_(NULL), image_end_(0), image_begin_(NULL), oat_data_begin_(NULL), interpreter_to_interpreter_bridge_offset_(0), - interpreter_to_compiled_code_bridge_offset_(0), portable_resolution_trampoline_offset_(0), + interpreter_to_compiled_code_bridge_offset_(0), portable_imt_conflict_trampoline_offset_(0), + portable_resolution_trampoline_offset_(0), quick_imt_conflict_trampoline_offset_(0), quick_resolution_trampoline_offset_(0) {} ~ImageWriter() {} @@ -204,8 +205,10 @@ class ImageWriter { uint32_t interpreter_to_interpreter_bridge_offset_; uint32_t interpreter_to_compiled_code_bridge_offset_; uint32_t jni_dlsym_lookup_offset_; + uint32_t portable_imt_conflict_trampoline_offset_; uint32_t portable_resolution_trampoline_offset_; uint32_t portable_to_interpreter_bridge_offset_; + uint32_t quick_imt_conflict_trampoline_offset_; uint32_t quick_resolution_trampoline_offset_; uint32_t quick_to_interpreter_bridge_offset_; diff --git a/compiler/oat_test.cc b/compiler/oat_test.cc index af867430140..815bca5c5a7 100644 --- a/compiler/oat_test.cc +++ b/compiler/oat_test.cc @@ -149,7 +149,7 @@ TEST_F(OatTest, WriteRead) { TEST_F(OatTest, OatHeaderSizeCheck) { // If this test is failing and you have to update these constants, // it is time to update OatHeader::kOatVersion - EXPECT_EQ(64U, sizeof(OatHeader)); + EXPECT_EQ(72U, sizeof(OatHeader)); EXPECT_EQ(28U, sizeof(OatMethodOffsets)); } diff --git a/compiler/oat_writer.cc b/compiler/oat_writer.cc index f681d7da6fc..28355bfc746 100644 --- a/compiler/oat_writer.cc +++ b/compiler/oat_writer.cc @@ -55,8 +55,10 @@ OatWriter::OatWriter(const std::vector& dex_files, size_interpreter_to_interpreter_bridge_(0), size_interpreter_to_compiled_code_bridge_(0), size_jni_dlsym_lookup_(0), + size_portable_imt_conflict_trampoline_(0), size_portable_resolution_trampoline_(0), size_portable_to_interpreter_bridge_(0), + size_quick_imt_conflict_trampoline_(0), size_quick_resolution_trampoline_(0), size_quick_to_interpreter_bridge_(0), size_trampoline_alignment_(0), @@ -229,8 +231,10 @@ size_t OatWriter::InitOatCode(size_t offset) { DO_TRAMPOLINE(interpreter_to_interpreter_bridge_, InterpreterToInterpreterBridge); DO_TRAMPOLINE(interpreter_to_compiled_code_bridge_, InterpreterToCompiledCodeBridge); DO_TRAMPOLINE(jni_dlsym_lookup_, JniDlsymLookup); + DO_TRAMPOLINE(portable_imt_conflict_trampoline_, PortableImtConflictTrampoline); DO_TRAMPOLINE(portable_resolution_trampoline_, PortableResolutionTrampoline); DO_TRAMPOLINE(portable_to_interpreter_bridge_, PortableToInterpreterBridge); + DO_TRAMPOLINE(quick_imt_conflict_trampoline_, QuickImtConflictTrampoline); DO_TRAMPOLINE(quick_resolution_trampoline_, QuickResolutionTrampoline); DO_TRAMPOLINE(quick_to_interpreter_bridge_, QuickToInterpreterBridge); @@ -239,8 +243,10 @@ size_t OatWriter::InitOatCode(size_t offset) { oat_header_->SetInterpreterToInterpreterBridgeOffset(0); oat_header_->SetInterpreterToCompiledCodeBridgeOffset(0); oat_header_->SetJniDlsymLookupOffset(0); + oat_header_->SetPortableImtConflictTrampolineOffset(0); oat_header_->SetPortableResolutionTrampolineOffset(0); oat_header_->SetPortableToInterpreterBridgeOffset(0); + oat_header_->SetQuickImtConflictTrampolineOffset(0); oat_header_->SetQuickResolutionTrampolineOffset(0); oat_header_->SetQuickToInterpreterBridgeOffset(0); } @@ -519,8 +525,10 @@ bool OatWriter::Write(OutputStream& out) { DO_STAT(size_interpreter_to_interpreter_bridge_); DO_STAT(size_interpreter_to_compiled_code_bridge_); DO_STAT(size_jni_dlsym_lookup_); + DO_STAT(size_portable_imt_conflict_trampoline_); DO_STAT(size_portable_resolution_trampoline_); DO_STAT(size_portable_to_interpreter_bridge_); + DO_STAT(size_quick_imt_conflict_trampoline_); DO_STAT(size_quick_resolution_trampoline_); DO_STAT(size_quick_to_interpreter_bridge_); DO_STAT(size_trampoline_alignment_); @@ -616,8 +624,10 @@ size_t OatWriter::WriteCode(OutputStream& out, const size_t file_offset) { DO_TRAMPOLINE(interpreter_to_interpreter_bridge_); DO_TRAMPOLINE(interpreter_to_compiled_code_bridge_); DO_TRAMPOLINE(jni_dlsym_lookup_); + DO_TRAMPOLINE(portable_imt_conflict_trampoline_); DO_TRAMPOLINE(portable_resolution_trampoline_); DO_TRAMPOLINE(portable_to_interpreter_bridge_); + DO_TRAMPOLINE(quick_imt_conflict_trampoline_); DO_TRAMPOLINE(quick_resolution_trampoline_); DO_TRAMPOLINE(quick_to_interpreter_bridge_); #undef DO_TRAMPOLINE diff --git a/compiler/oat_writer.h b/compiler/oat_writer.h index e3cb0a86ed6..5d947cfaeaa 100644 --- a/compiler/oat_writer.h +++ b/compiler/oat_writer.h @@ -226,8 +226,10 @@ class OatWriter { UniquePtr > interpreter_to_interpreter_bridge_; UniquePtr > interpreter_to_compiled_code_bridge_; UniquePtr > jni_dlsym_lookup_; + UniquePtr > portable_imt_conflict_trampoline_; UniquePtr > portable_resolution_trampoline_; UniquePtr > portable_to_interpreter_bridge_; + UniquePtr > quick_imt_conflict_trampoline_; UniquePtr > quick_resolution_trampoline_; UniquePtr > quick_to_interpreter_bridge_; @@ -240,8 +242,10 @@ class OatWriter { uint32_t size_interpreter_to_interpreter_bridge_; uint32_t size_interpreter_to_compiled_code_bridge_; uint32_t size_jni_dlsym_lookup_; + uint32_t size_portable_imt_conflict_trampoline_; uint32_t size_portable_resolution_trampoline_; uint32_t size_portable_to_interpreter_bridge_; + uint32_t size_quick_imt_conflict_trampoline_; uint32_t size_quick_resolution_trampoline_; uint32_t size_quick_to_interpreter_bridge_; uint32_t size_trampoline_alignment_; diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc index d8112ea7082..1beb8622674 100644 --- a/dex2oat/dex2oat.cc +++ b/dex2oat/dex2oat.cc @@ -345,10 +345,6 @@ class Dex2Oat { return false; } Runtime* runtime = Runtime::Current(); - // if we loaded an existing image, we will reuse values from the image roots. - if (!runtime->HasResolutionMethod()) { - runtime->SetResolutionMethod(runtime->CreateResolutionMethod()); - } for (int i = 0; i < Runtime::kLastCalleeSaveType; i++) { Runtime::CalleeSaveType type = Runtime::CalleeSaveType(i); if (!runtime->HasCalleeSaveMethod(type)) { diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc index fdeeaecd93d..3a32ff14bda 100644 --- a/oatdump/oatdump.cc +++ b/oatdump/oatdump.cc @@ -86,6 +86,8 @@ static void usage() { const char* image_roots_descriptions_[] = { "kResolutionMethod", + "kImtConflictMethod", + "kDefaultImt", "kCalleeSaveMethod", "kRefsOnlySaveMethod", "kRefsAndArgsSaveMethod", @@ -1005,7 +1007,8 @@ class ImageDumper { indent_os << StringPrintf("OAT CODE: %p\n", oat_code); } } else if (method->IsAbstract() || method->IsCalleeSaveMethod() || - method->IsResolutionMethod() || MethodHelper(method).IsClassInitializer()) { + method->IsResolutionMethod() || method->IsImtConflictMethod() || + MethodHelper(method).IsClassInitializer()) { DCHECK(method->GetNativeGcMap() == NULL) << PrettyMethod(method); DCHECK(method->GetMappingTable() == NULL) << PrettyMethod(method); } else { diff --git a/runtime/arch/arm/entrypoints_init_arm.cc b/runtime/arch/arm/entrypoints_init_arm.cc index 352982fc3a5..3dac636df95 100644 --- a/runtime/arch/arm/entrypoints_init_arm.cc +++ b/runtime/arch/arm/entrypoints_init_arm.cc @@ -121,10 +121,10 @@ extern "C" int32_t art_quick_indexof(void*, uint32_t, uint32_t, uint32_t); extern "C" int32_t art_quick_string_compareto(void*, void*); // Invoke entrypoints. +extern "C" void art_quick_imt_conflict_trampoline(mirror::ArtMethod*); extern "C" void art_quick_resolution_trampoline(mirror::ArtMethod*); extern "C" void art_quick_to_interpreter_bridge(mirror::ArtMethod*); extern "C" void art_quick_invoke_direct_trampoline_with_access_check(uint32_t, void*); -extern "C" void art_quick_invoke_interface_trampoline(uint32_t, void*); extern "C" void art_quick_invoke_interface_trampoline_with_access_check(uint32_t, void*); extern "C" void art_quick_invoke_static_trampoline_with_access_check(uint32_t, void*); extern "C" void art_quick_invoke_super_trampoline_with_access_check(uint32_t, void*); @@ -253,10 +253,10 @@ void InitEntryPoints(InterpreterEntryPoints* ipoints, JniEntryPoints* jpoints, qpoints->pMemcpy = memcpy; // Invocation + qpoints->pQuickImtConflictTrampoline = art_quick_imt_conflict_trampoline; qpoints->pQuickResolutionTrampoline = art_quick_resolution_trampoline; qpoints->pQuickToInterpreterBridge = art_quick_to_interpreter_bridge; qpoints->pInvokeDirectTrampolineWithAccessCheck = art_quick_invoke_direct_trampoline_with_access_check; - qpoints->pInvokeInterfaceTrampoline = art_quick_invoke_interface_trampoline; qpoints->pInvokeInterfaceTrampolineWithAccessCheck = art_quick_invoke_interface_trampoline_with_access_check; qpoints->pInvokeStaticTrampolineWithAccessCheck = art_quick_invoke_static_trampoline_with_access_check; qpoints->pInvokeSuperTrampolineWithAccessCheck = art_quick_invoke_super_trampoline_with_access_check; diff --git a/runtime/arch/arm/quick_entrypoints_arm.S b/runtime/arch/arm/quick_entrypoints_arm.S index 736ce2fb24a..808ff5b34f6 100644 --- a/runtime/arch/arm/quick_entrypoints_arm.S +++ b/runtime/arch/arm/quick_entrypoints_arm.S @@ -1040,6 +1040,18 @@ ENTRY art_quick_proxy_invoke_handler DELIVER_PENDING_EXCEPTION END art_quick_proxy_invoke_handler + /* + * Called to resolve an imt conflict. r12 is a hidden argument that holds the target method's + * dex method index. + */ +ENTRY art_quick_imt_conflict_trampoline + ldr r0, [sp, #0] @ load caller Method* + ldr r0, [r0, #METHOD_DEX_CACHE_METHODS_OFFSET] @ load dex_cache_resolved_methods + add r0, #OBJECT_ARRAY_DATA_OFFSET @ get starting address of data + ldr r0, [r0, r12, lsl 2] @ load the target method + b art_quick_invoke_interface_trampoline +END art_quick_imt_conflict_trampoline + .extern artQuickResolutionTrampoline ENTRY art_quick_resolution_trampoline SETUP_REF_AND_ARGS_CALLEE_SAVE_FRAME diff --git a/runtime/arch/mips/entrypoints_init_mips.cc b/runtime/arch/mips/entrypoints_init_mips.cc index cc975d75a31..331a4613549 100644 --- a/runtime/arch/mips/entrypoints_init_mips.cc +++ b/runtime/arch/mips/entrypoints_init_mips.cc @@ -122,10 +122,10 @@ extern "C" int32_t art_quick_indexof(void*, uint32_t, uint32_t, uint32_t); extern "C" int32_t art_quick_string_compareto(void*, void*); // Invoke entrypoints. +extern "C" void art_quick_imt_conflict_trampoline(mirror::ArtMethod*); extern "C" void art_quick_resolution_trampoline(mirror::ArtMethod*); extern "C" void art_quick_to_interpreter_bridge(mirror::ArtMethod*); extern "C" void art_quick_invoke_direct_trampoline_with_access_check(uint32_t, void*); -extern "C" void art_quick_invoke_interface_trampoline(uint32_t, void*); extern "C" void art_quick_invoke_interface_trampoline_with_access_check(uint32_t, void*); extern "C" void art_quick_invoke_static_trampoline_with_access_check(uint32_t, void*); extern "C" void art_quick_invoke_super_trampoline_with_access_check(uint32_t, void*); @@ -253,10 +253,10 @@ void InitEntryPoints(InterpreterEntryPoints* ipoints, JniEntryPoints* jpoints, qpoints->pMemcpy = memcpy; // Invocation + qpoints->pQuickImtConflictTrampoline = art_quick_imt_conflict_trampoline; qpoints->pQuickResolutionTrampoline = art_quick_resolution_trampoline; qpoints->pQuickToInterpreterBridge = art_quick_to_interpreter_bridge; qpoints->pInvokeDirectTrampolineWithAccessCheck = art_quick_invoke_direct_trampoline_with_access_check; - qpoints->pInvokeInterfaceTrampoline = art_quick_invoke_interface_trampoline; qpoints->pInvokeInterfaceTrampolineWithAccessCheck = art_quick_invoke_interface_trampoline_with_access_check; qpoints->pInvokeStaticTrampolineWithAccessCheck = art_quick_invoke_static_trampoline_with_access_check; qpoints->pInvokeSuperTrampolineWithAccessCheck = art_quick_invoke_super_trampoline_with_access_check; diff --git a/runtime/arch/mips/quick_entrypoints_mips.S b/runtime/arch/mips/quick_entrypoints_mips.S index 031d13a6b09..451b1bb30f4 100644 --- a/runtime/arch/mips/quick_entrypoints_mips.S +++ b/runtime/arch/mips/quick_entrypoints_mips.S @@ -1060,6 +1060,21 @@ ENTRY art_quick_proxy_invoke_handler DELIVER_PENDING_EXCEPTION END art_quick_proxy_invoke_handler + /* + * Called to resolve an imt conflict. t0 is a hidden argument that holds the target method's + * dex method index. + */ +ENTRY art_quick_imt_conflict_trampoline + GENERATE_GLOBAL_POINTER + lw $a0, 0($sp) # load caller Method* + lw $a0, METHOD_DEX_CACHE_METHODS_OFFSET($a0) # load dex_cache_resolved_methods + sll $t0, 2 # convert target method offset to bytes + add $a0, $t0 # get address of target method + lw $a0, OBJECT_ARRAY_DATA_OFFSET($a0) # load the target method + la $t9, art_quick_invoke_interface_trampoline + jr $t9 +END art_quick_imt_conflict_trampoline + .extern artQuickResolutionTrampoline ENTRY art_quick_resolution_trampoline GENERATE_GLOBAL_POINTER diff --git a/runtime/arch/x86/entrypoints_init_x86.cc b/runtime/arch/x86/entrypoints_init_x86.cc index 89dd1b8f8ca..99b0dd548c4 100644 --- a/runtime/arch/x86/entrypoints_init_x86.cc +++ b/runtime/arch/x86/entrypoints_init_x86.cc @@ -104,10 +104,10 @@ extern "C" int32_t art_quick_string_compareto(void*, void*); extern "C" void* art_quick_memcpy(void*, const void*, size_t); // Invoke entrypoints. +extern "C" void art_quick_imt_conflict_trampoline(mirror::ArtMethod*); extern "C" void art_quick_resolution_trampoline(mirror::ArtMethod*); extern "C" void art_quick_to_interpreter_bridge(mirror::ArtMethod*); extern "C" void art_quick_invoke_direct_trampoline_with_access_check(uint32_t, void*); -extern "C" void art_quick_invoke_interface_trampoline(uint32_t, void*); extern "C" void art_quick_invoke_interface_trampoline_with_access_check(uint32_t, void*); extern "C" void art_quick_invoke_static_trampoline_with_access_check(uint32_t, void*); extern "C" void art_quick_invoke_super_trampoline_with_access_check(uint32_t, void*); @@ -235,10 +235,10 @@ void InitEntryPoints(InterpreterEntryPoints* ipoints, JniEntryPoints* jpoints, qpoints->pMemcpy = art_quick_memcpy; // Invocation + qpoints->pQuickImtConflictTrampoline = art_quick_imt_conflict_trampoline; qpoints->pQuickResolutionTrampoline = art_quick_resolution_trampoline; qpoints->pQuickToInterpreterBridge = art_quick_to_interpreter_bridge; qpoints->pInvokeDirectTrampolineWithAccessCheck = art_quick_invoke_direct_trampoline_with_access_check; - qpoints->pInvokeInterfaceTrampoline = art_quick_invoke_interface_trampoline; qpoints->pInvokeInterfaceTrampolineWithAccessCheck = art_quick_invoke_interface_trampoline_with_access_check; qpoints->pInvokeStaticTrampolineWithAccessCheck = art_quick_invoke_static_trampoline_with_access_check; qpoints->pInvokeSuperTrampolineWithAccessCheck = art_quick_invoke_super_trampoline_with_access_check; diff --git a/runtime/arch/x86/quick_entrypoints_x86.S b/runtime/arch/x86/quick_entrypoints_x86.S index 805f6f4bd19..6a6891b3314 100644 --- a/runtime/arch/x86/quick_entrypoints_x86.S +++ b/runtime/arch/x86/quick_entrypoints_x86.S @@ -997,6 +997,20 @@ DEFINE_FUNCTION art_quick_proxy_invoke_handler RETURN_OR_DELIVER_PENDING_EXCEPTION // return or deliver exception END_FUNCTION art_quick_proxy_invoke_handler + /* + * Called to resolve an imt conflict. xmm0 is a hidden argument that holds the target method's + * dex method index. + */ +DEFINE_FUNCTION art_quick_imt_conflict_trampoline + PUSH ecx + movl 8(%esp), %eax // load caller Method* + movl METHOD_DEX_CACHE_METHODS_OFFSET(%eax), %eax // load dex_cache_resolved_methods + movd %xmm0, %ecx // get target method index stored in xmm0 + movl OBJECT_ARRAY_DATA_OFFSET(%eax, %ecx, 4), %eax // load the target method + POP ecx + jmp art_quick_invoke_interface_trampoline +END_FUNCTION art_quick_imt_conflict_trampoline + DEFINE_FUNCTION art_quick_resolution_trampoline SETUP_REF_AND_ARGS_CALLEE_SAVE_FRAME PUSH esp // pass SP diff --git a/runtime/asm_support.h b/runtime/asm_support.h index a6700bc2e4b..e9bbf917614 100644 --- a/runtime/asm_support.h +++ b/runtime/asm_support.h @@ -38,7 +38,8 @@ #define STRING_OFFSET_OFFSET 20 #define STRING_DATA_OFFSET 12 -// Offset of field Method::entry_point_from_compiled_code_ +// Offsets within java.lang.Method. +#define METHOD_DEX_CACHE_METHODS_OFFSET 16 #define METHOD_CODE_OFFSET 40 #endif // ART_RUNTIME_ASM_SUPPORT_H_ diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index 03f2c9df420..2fc564fbd5a 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -196,7 +196,9 @@ ClassLinker::ClassLinker(InternTable* intern_table) class_table_dirty_(false), intern_table_(intern_table), portable_resolution_trampoline_(NULL), - quick_resolution_trampoline_(NULL) { + quick_resolution_trampoline_(NULL), + portable_imt_conflict_trampoline_(NULL), + quick_imt_conflict_trampoline_(NULL) { CHECK_EQ(arraysize(class_roots_descriptors_), size_t(kClassRootsMax)); } @@ -336,6 +338,12 @@ void ClassLinker::InitFromCompiler(const std::vector& boot_class InitializePrimitiveClass(char_class.get(), Primitive::kPrimChar); SetClassRoot(kPrimitiveChar, char_class.get()); // needs descriptor + // Create runtime resolution and imt conflict methods. Also setup the default imt. + Runtime* runtime = Runtime::Current(); + runtime->SetResolutionMethod(runtime->CreateResolutionMethod()); + runtime->SetImtConflictMethod(runtime->CreateImtConflictMethod()); + runtime->SetDefaultImt(runtime->CreateDefaultImt(this)); + // Object, String and DexCache need to be rerun through FindSystemClass to finish init java_lang_Object->SetStatus(mirror::Class::kStatusNotReady, self); mirror::Class* Object_class = FindSystemClass("Ljava/lang/Object;"); @@ -1045,6 +1053,8 @@ void ClassLinker::InitFromImage() { CHECK(oat_file.GetOatHeader().GetImageFileLocation().empty()); portable_resolution_trampoline_ = oat_file.GetOatHeader().GetPortableResolutionTrampoline(); quick_resolution_trampoline_ = oat_file.GetOatHeader().GetQuickResolutionTrampoline(); + portable_imt_conflict_trampoline_ = oat_file.GetOatHeader().GetPortableImtConflictTrampoline(); + quick_imt_conflict_trampoline_ = oat_file.GetOatHeader().GetQuickImtConflictTrampoline(); mirror::Object* dex_caches_object = space->GetImageHeader().GetImageRoot(ImageHeader::kDexCaches); mirror::ObjectArray* dex_caches = dex_caches_object->AsObjectArray(); @@ -3518,6 +3528,8 @@ bool ClassLinker::LinkVirtualMethods(SirtRef& klass) { bool ClassLinker::LinkInterfaceMethods(SirtRef& klass, mirror::ObjectArray* interfaces) { + // Set the imt table to be all conflicts by default. + klass->SetImTable(Runtime::Current()->GetDefaultImt()); size_t super_ifcount; if (klass->HasSuperClass()) { super_ifcount = klass->GetSuperClass()->GetIfTableCount(); @@ -3625,6 +3637,13 @@ bool ClassLinker::LinkInterfaceMethods(SirtRef& klass, if (klass->IsInterface()) { return true; } + // Allocate imtable + bool imtable_changed = false; + SirtRef > imtable(self, AllocArtMethodArray(self, kImtSize)); + if (UNLIKELY(imtable.get() == NULL)) { + CHECK(self->IsExceptionPending()); // OOME. + return false; + } std::vector miranda_list; MethodHelper vtable_mh(NULL, this); MethodHelper interface_mh(NULL, this); @@ -3664,6 +3683,14 @@ bool ClassLinker::LinkInterfaceMethods(SirtRef& klass, return false; } method_array->Set(j, vtable_method); + // Place method in imt if entry is empty, place conflict otherwise. + uint32_t imt_index = interface_method->GetDexMethodIndex() % kImtSize; + if (imtable->Get(imt_index) == NULL) { + imtable->Set(imt_index, vtable_method); + imtable_changed = true; + } else { + imtable->Set(imt_index, Runtime::Current()->GetImtConflictMethod()); + } break; } } @@ -3695,6 +3722,16 @@ bool ClassLinker::LinkInterfaceMethods(SirtRef& klass, } } } + if (imtable_changed) { + // Fill in empty entries in interface method table with conflict. + mirror::ArtMethod* imt_conflict_method = Runtime::Current()->GetImtConflictMethod(); + for (size_t i = 0; i < kImtSize; i++) { + if (imtable->Get(i) == NULL) { + imtable->Set(i, imt_conflict_method); + } + } + klass->SetImTable(imtable.get()); + } if (!miranda_list.empty()) { int old_method_count = klass->NumVirtualMethods(); int new_method_count = old_method_count + miranda_list.size(); diff --git a/runtime/class_linker.h b/runtime/class_linker.h index 0bc1b5f4440..473370d90f0 100644 --- a/runtime/class_linker.h +++ b/runtime/class_linker.h @@ -51,6 +51,11 @@ typedef bool (ClassVisitor)(mirror::Class* c, void* arg); class ClassLinker { public: + // Interface method table size. Increasing this value reduces the chance of two interface methods + // colliding in the interface method table but increases the size of classes that implement + // (non-marker) interfaces. + static constexpr size_t kImtSize = 64; + // Creates the class linker by bootstrapping from dex files. static ClassLinker* CreateFromCompiler(const std::vector& boot_class_path, InternTable* intern_table) @@ -340,6 +345,18 @@ class ClassLinker { return quick_resolution_trampoline_; } + const void* GetPortableImtConflictTrampoline() const { + return portable_imt_conflict_trampoline_; + } + + const void* GetQuickImtConflictTrampoline() const { + return quick_imt_conflict_trampoline_; + } + + InternTable* GetInternTable() const { + return intern_table_; + } + // Attempts to insert a class into a class table. Returns NULL if // the class was inserted, otherwise returns an existing class with // the same descriptor and ClassLoader. @@ -608,6 +625,8 @@ class ClassLinker { const void* portable_resolution_trampoline_; const void* quick_resolution_trampoline_; + const void* portable_imt_conflict_trampoline_; + const void* quick_imt_conflict_trampoline_; friend class ImageWriter; // for GetClassRoots FRIEND_TEST(ClassLinkerTest, ClassRootDescriptors); diff --git a/runtime/class_linker_test.cc b/runtime/class_linker_test.cc index 029b73e35f1..a52b6802602 100644 --- a/runtime/class_linker_test.cc +++ b/runtime/class_linker_test.cc @@ -497,6 +497,7 @@ struct ClassOffsets : public CheckOffsets { offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::Class, direct_methods_), "directMethods")); offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::Class, ifields_), "iFields")); offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::Class, iftable_), "ifTable")); + offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::Class, imtable_), "imTable")); offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::Class, name_), "name")); offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::Class, sfields_), "sFields")); offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::Class, super_class_), "superClass")); @@ -582,11 +583,11 @@ struct StringClassOffsets : public CheckOffsets { offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::StringClass, ASCII_), "ASCII")); offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::StringClass, CASE_INSENSITIVE_ORDER_), "CASE_INSENSITIVE_ORDER")); - // alphabetical 64-bit - offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::StringClass, serialVersionUID_), "serialVersionUID")); - // alphabetical 32-bit offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::StringClass, REPLACEMENT_CHAR_), "REPLACEMENT_CHAR")); + + // alphabetical 64-bit + offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::StringClass, serialVersionUID_), "serialVersionUID")); }; }; diff --git a/runtime/common_test.h b/runtime/common_test.h index 899eab1f63d..673a03b355b 100644 --- a/runtime/common_test.h +++ b/runtime/common_test.h @@ -329,9 +329,6 @@ class CommonTest : public testing::Test { CompilerBackend compiler_backend = kQuick; #endif - if (!runtime_->HasResolutionMethod()) { - runtime_->SetResolutionMethod(runtime_->CreateResolutionMethod()); - } for (int i = 0; i < Runtime::kLastCalleeSaveType; i++) { Runtime::CalleeSaveType type = Runtime::CalleeSaveType(i); if (!runtime_->HasCalleeSaveMethod(type)) { diff --git a/runtime/entrypoints/entrypoint_utils.h b/runtime/entrypoints/entrypoint_utils.h index 200860470db..7ce50c5dfbe 100644 --- a/runtime/entrypoints/entrypoint_utils.h +++ b/runtime/entrypoints/entrypoint_utils.h @@ -372,14 +372,21 @@ static inline mirror::ArtMethod* FindMethodFromCode(uint32_t method_idx, mirror: return vtable->GetWithoutChecks(vtable_index); } case kInterface: { - mirror::ArtMethod* interface_method = - this_object->GetClass()->FindVirtualMethodForInterface(resolved_method); - if (UNLIKELY(interface_method == nullptr)) { - ThrowIncompatibleClassChangeErrorClassForInterfaceDispatch(resolved_method, this_object, - referrer); - return nullptr; // Failure. + uint32_t imt_index = resolved_method->GetDexMethodIndex() % ClassLinker::kImtSize; + mirror::ObjectArray* imt_table = this_object->GetClass()->GetImTable(); + mirror::ArtMethod* imt_method = imt_table->Get(imt_index); + if (!imt_method->IsImtConflictMethod()) { + return imt_method; } else { - return interface_method; + mirror::ArtMethod* interface_method = + this_object->GetClass()->FindVirtualMethodForInterface(resolved_method); + if (UNLIKELY(interface_method == nullptr)) { + ThrowIncompatibleClassChangeErrorClassForInterfaceDispatch(resolved_method, this_object, + referrer); + return nullptr; // Failure. + } else { + return interface_method; + } } } default: @@ -665,6 +672,23 @@ static inline const void* GetResolutionTrampoline(ClassLinker* class_linker) { #endif } +static inline const void* GetPortableImtConflictTrampoline(ClassLinker* class_linker) { + return class_linker->GetPortableImtConflictTrampoline(); +} + +static inline const void* GetQuickImtConflictTrampoline(ClassLinker* class_linker) { + return class_linker->GetQuickImtConflictTrampoline(); +} + +// Return address of imt conflict trampoline stub for defined compiler. +static inline const void* GetImtConflictTrampoline(ClassLinker* class_linker) { +#if defined(ART_USE_PORTABLE_COMPILER) + return GetPortableImtConflictTrampoline(class_linker); +#else + return GetQuickImtConflictTrampoline(class_linker); +#endif +} + extern "C" void art_portable_proxy_invoke_handler(); static inline const void* GetPortableProxyInvokeHandler() { return reinterpret_cast(art_portable_proxy_invoke_handler); diff --git a/runtime/entrypoints/portable/portable_entrypoints.h b/runtime/entrypoints/portable/portable_entrypoints.h index d4564471ac6..dbea70735f1 100644 --- a/runtime/entrypoints/portable/portable_entrypoints.h +++ b/runtime/entrypoints/portable/portable_entrypoints.h @@ -35,6 +35,7 @@ class Thread; // compiler ABI. struct PACKED(4) PortableEntryPoints { // Invocation + void (*pPortableImtConflictTrampoline)(mirror::ArtMethod*); void (*pPortableResolutionTrampoline)(mirror::ArtMethod*); void (*pPortableToInterpreterBridge)(mirror::ArtMethod*); }; diff --git a/runtime/entrypoints/quick/quick_entrypoints.h b/runtime/entrypoints/quick/quick_entrypoints.h index c8a85a0fe35..1ba206629e4 100644 --- a/runtime/entrypoints/quick/quick_entrypoints.h +++ b/runtime/entrypoints/quick/quick_entrypoints.h @@ -118,10 +118,10 @@ struct PACKED(4) QuickEntryPoints { void* (*pMemcpy)(void*, const void*, size_t); // Invocation + void (*pQuickImtConflictTrampoline)(mirror::ArtMethod*); void (*pQuickResolutionTrampoline)(mirror::ArtMethod*); void (*pQuickToInterpreterBridge)(mirror::ArtMethod*); void (*pInvokeDirectTrampolineWithAccessCheck)(uint32_t, void*); - void (*pInvokeInterfaceTrampoline)(uint32_t, void*); void (*pInvokeInterfaceTrampolineWithAccessCheck)(uint32_t, void*); void (*pInvokeStaticTrampolineWithAccessCheck)(uint32_t, void*); void (*pInvokeSuperTrampolineWithAccessCheck)(uint32_t, void*); diff --git a/runtime/gc/space/image_space.cc b/runtime/gc/space/image_space.cc index fa28642c3e5..e12ee063c0e 100644 --- a/runtime/gc/space/image_space.cc +++ b/runtime/gc/space/image_space.cc @@ -241,6 +241,10 @@ ImageSpace* ImageSpace::Init(const char* image_file_name, bool validate_oat_file Runtime* runtime = Runtime::Current(); mirror::Object* resolution_method = image_header.GetImageRoot(ImageHeader::kResolutionMethod); runtime->SetResolutionMethod(down_cast(resolution_method)); + mirror::Object* imt_conflict_method = image_header.GetImageRoot(ImageHeader::kImtConflictMethod); + runtime->SetImtConflictMethod(down_cast(imt_conflict_method)); + mirror::Object* default_imt = image_header.GetImageRoot(ImageHeader::kDefaultImt); + runtime->SetDefaultImt(down_cast*>(default_imt)); mirror::Object* callee_save_method = image_header.GetImageRoot(ImageHeader::kCalleeSaveMethod); runtime->SetCalleeSaveMethod(down_cast(callee_save_method), Runtime::kSaveAll); diff --git a/runtime/image.h b/runtime/image.h index 2cb468fed19..246f1063828 100644 --- a/runtime/image.h +++ b/runtime/image.h @@ -90,6 +90,8 @@ class PACKED(4) ImageHeader { enum ImageRoot { kResolutionMethod, + kImtConflictMethod, + kDefaultImt, kCalleeSaveMethod, kRefsOnlySaveMethod, kRefsAndArgsSaveMethod, diff --git a/runtime/mirror/art_method-inl.h b/runtime/mirror/art_method-inl.h index ccf3e59f189..c9bf1609a04 100644 --- a/runtime/mirror/art_method-inl.h +++ b/runtime/mirror/art_method-inl.h @@ -202,6 +202,13 @@ inline bool ArtMethod::IsResolutionMethod() const { DCHECK(!result || IsRuntimeMethod()); return result; } + +inline bool ArtMethod::IsImtConflictMethod() const { + bool result = this == Runtime::Current()->GetImtConflictMethod(); + // Check that if we do think it is phony it looks like the imt conflict method. + DCHECK(!result || IsRuntimeMethod()); + return result; +} } // namespace mirror } // namespace art diff --git a/runtime/mirror/art_method.h b/runtime/mirror/art_method.h index 052089373dd..f396fbe4362 100644 --- a/runtime/mirror/art_method.h +++ b/runtime/mirror/art_method.h @@ -357,6 +357,8 @@ class MANAGED ArtMethod : public Object { bool IsResolutionMethod() const; + bool IsImtConflictMethod() const; + uintptr_t NativePcOffset(const uintptr_t pc) const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); // Converts a native PC to a dex PC. diff --git a/runtime/mirror/class-inl.h b/runtime/mirror/class-inl.h index cd5e865c7c8..7f3a302768e 100644 --- a/runtime/mirror/class-inl.h +++ b/runtime/mirror/class-inl.h @@ -139,6 +139,14 @@ inline void Class::SetVTable(ObjectArray* new_vtable) SetFieldObject(OFFSET_OF_OBJECT_MEMBER(Class, vtable_), new_vtable, false); } +inline ObjectArray* Class::GetImTable() const { + return GetFieldObject*>(OFFSET_OF_OBJECT_MEMBER(Class, imtable_), false); +} + +inline void Class::SetImTable(ObjectArray* new_imtable) { + SetFieldObject(OFFSET_OF_OBJECT_MEMBER(Class, imtable_), new_imtable, false); +} + inline bool Class::Implements(const Class* klass) const { DCHECK(klass != NULL); DCHECK(klass->IsInterface()) << PrettyClass(this); diff --git a/runtime/mirror/class.h b/runtime/mirror/class.h index d15f3375fdb..ed1aad39d21 100644 --- a/runtime/mirror/class.h +++ b/runtime/mirror/class.h @@ -555,6 +555,15 @@ class MANAGED Class : public StaticStorageBase { return OFFSET_OF_OBJECT_MEMBER(Class, vtable_); } + ObjectArray* GetImTable() const; + + void SetImTable(ObjectArray* new_imtable) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + static MemberOffset ImTableOffset() { + return OFFSET_OF_OBJECT_MEMBER(Class, imtable_); + } + // Given a method implemented by this class but potentially from a super class, return the // specific implementation method for this class. ArtMethod* FindVirtualMethodForVirtual(ArtMethod* method) const @@ -830,6 +839,9 @@ class MANAGED Class : public StaticStorageBase { // methods for the methods in the interface. IfTable* iftable_; + // Interface method table (imt), for quick "invoke-interface". + ObjectArray* imtable_; + // descriptor for the class such as "java.lang.Class" or "[C". Lazily initialized by ComputeName String* name_; @@ -912,6 +924,7 @@ std::ostream& operator<<(std::ostream& os, const Class::Status& rhs); class MANAGED ClassClass : public Class { private: + int32_t pad_; int64_t serialVersionUID_; friend struct art::ClassClassOffsets; // for verifying offset information DISALLOW_IMPLICIT_CONSTRUCTORS(ClassClass); diff --git a/runtime/mirror/object_test.cc b/runtime/mirror/object_test.cc index 6c6d488f4a9..d0d1ee4a462 100644 --- a/runtime/mirror/object_test.cc +++ b/runtime/mirror/object_test.cc @@ -84,6 +84,7 @@ TEST_F(ObjectTest, AsmConstants) { EXPECT_EQ(STRING_OFFSET_OFFSET, String::OffsetOffset().Int32Value()); EXPECT_EQ(STRING_DATA_OFFSET, Array::DataOffset(sizeof(uint16_t)).Int32Value()); + EXPECT_EQ(METHOD_DEX_CACHE_METHODS_OFFSET, ArtMethod::DexCacheResolvedMethodsOffset().Int32Value()); EXPECT_EQ(METHOD_CODE_OFFSET, ArtMethod::EntryPointFromCompiledCodeOffset().Int32Value()); } diff --git a/runtime/mirror/string.h b/runtime/mirror/string.h index 1879f04befe..01d8f318ffc 100644 --- a/runtime/mirror/string.h +++ b/runtime/mirror/string.h @@ -156,8 +156,8 @@ class MANAGED StringClass : public Class { private: CharArray* ASCII_; Object* CASE_INSENSITIVE_ORDER_; - int64_t serialVersionUID_; uint32_t REPLACEMENT_CHAR_; + int64_t serialVersionUID_; friend struct art::StringClassOffsets; // for verifying offset information DISALLOW_IMPLICIT_CONSTRUCTORS(StringClass); }; diff --git a/runtime/oat.cc b/runtime/oat.cc index 6fe5d1097b6..defda6bb1a5 100644 --- a/runtime/oat.cc +++ b/runtime/oat.cc @@ -22,7 +22,7 @@ namespace art { const uint8_t OatHeader::kOatMagic[] = { 'o', 'a', 't', '\n' }; -const uint8_t OatHeader::kOatVersion[] = { '0', '0', '8', '\0' }; +const uint8_t OatHeader::kOatVersion[] = { '0', '0', '9', '\0' }; OatHeader::OatHeader() { memset(this, 0, sizeof(*this)); @@ -60,8 +60,10 @@ OatHeader::OatHeader(InstructionSet instruction_set, interpreter_to_interpreter_bridge_offset_ = 0; interpreter_to_compiled_code_bridge_offset_ = 0; jni_dlsym_lookup_offset_ = 0; + portable_imt_conflict_trampoline_offset_ = 0; portable_resolution_trampoline_offset_ = 0; portable_to_interpreter_bridge_offset_ = 0; + quick_imt_conflict_trampoline_offset_ = 0; quick_resolution_trampoline_offset_ = 0; quick_to_interpreter_bridge_offset_ = 0; } @@ -171,18 +173,37 @@ void OatHeader::SetJniDlsymLookupOffset(uint32_t offset) { UpdateChecksum(&jni_dlsym_lookup_offset_, sizeof(offset)); } +const void* OatHeader::GetPortableImtConflictTrampoline() const { + return reinterpret_cast(this) + GetPortableImtConflictTrampolineOffset(); +} + +uint32_t OatHeader::GetPortableImtConflictTrampolineOffset() const { + DCHECK(IsValid()); + CHECK_GE(portable_imt_conflict_trampoline_offset_, jni_dlsym_lookup_offset_); + return portable_imt_conflict_trampoline_offset_; +} + +void OatHeader::SetPortableImtConflictTrampolineOffset(uint32_t offset) { + CHECK(offset == 0 || offset >= jni_dlsym_lookup_offset_); + DCHECK(IsValid()); + DCHECK_EQ(portable_imt_conflict_trampoline_offset_, 0U) << offset; + + portable_imt_conflict_trampoline_offset_ = offset; + UpdateChecksum(&portable_imt_conflict_trampoline_offset_, sizeof(offset)); +} + const void* OatHeader::GetPortableResolutionTrampoline() const { return reinterpret_cast(this) + GetPortableResolutionTrampolineOffset(); } uint32_t OatHeader::GetPortableResolutionTrampolineOffset() const { DCHECK(IsValid()); - CHECK_GE(portable_resolution_trampoline_offset_, jni_dlsym_lookup_offset_); + CHECK_GE(portable_resolution_trampoline_offset_, portable_imt_conflict_trampoline_offset_); return portable_resolution_trampoline_offset_; } void OatHeader::SetPortableResolutionTrampolineOffset(uint32_t offset) { - CHECK(offset == 0 || offset >= jni_dlsym_lookup_offset_); + CHECK(offset == 0 || offset >= portable_imt_conflict_trampoline_offset_); DCHECK(IsValid()); DCHECK_EQ(portable_resolution_trampoline_offset_, 0U) << offset; @@ -209,18 +230,37 @@ void OatHeader::SetPortableToInterpreterBridgeOffset(uint32_t offset) { UpdateChecksum(&portable_to_interpreter_bridge_offset_, sizeof(offset)); } +const void* OatHeader::GetQuickImtConflictTrampoline() const { + return reinterpret_cast(this) + GetQuickImtConflictTrampolineOffset(); +} + +uint32_t OatHeader::GetQuickImtConflictTrampolineOffset() const { + DCHECK(IsValid()); + CHECK_GE(quick_imt_conflict_trampoline_offset_, portable_to_interpreter_bridge_offset_); + return quick_imt_conflict_trampoline_offset_; +} + +void OatHeader::SetQuickImtConflictTrampolineOffset(uint32_t offset) { + CHECK(offset == 0 || offset >= portable_to_interpreter_bridge_offset_); + DCHECK(IsValid()); + DCHECK_EQ(quick_imt_conflict_trampoline_offset_, 0U) << offset; + + quick_imt_conflict_trampoline_offset_ = offset; + UpdateChecksum(&quick_imt_conflict_trampoline_offset_, sizeof(offset)); +} + const void* OatHeader::GetQuickResolutionTrampoline() const { return reinterpret_cast(this) + GetQuickResolutionTrampolineOffset(); } uint32_t OatHeader::GetQuickResolutionTrampolineOffset() const { DCHECK(IsValid()); - CHECK_GE(quick_resolution_trampoline_offset_, portable_to_interpreter_bridge_offset_); + CHECK_GE(quick_resolution_trampoline_offset_, quick_imt_conflict_trampoline_offset_); return quick_resolution_trampoline_offset_; } void OatHeader::SetQuickResolutionTrampolineOffset(uint32_t offset) { - CHECK(offset == 0 || offset >= portable_to_interpreter_bridge_offset_); + CHECK(offset == 0 || offset >= quick_imt_conflict_trampoline_offset_); DCHECK(IsValid()); DCHECK_EQ(quick_resolution_trampoline_offset_, 0U) << offset; diff --git a/runtime/oat.h b/runtime/oat.h index a9dc540c032..c864c2cc525 100644 --- a/runtime/oat.h +++ b/runtime/oat.h @@ -62,6 +62,9 @@ class PACKED(4) OatHeader { const void* GetPortableResolutionTrampoline() const; uint32_t GetPortableResolutionTrampolineOffset() const; void SetPortableResolutionTrampolineOffset(uint32_t offset); + const void* GetPortableImtConflictTrampoline() const; + uint32_t GetPortableImtConflictTrampolineOffset() const; + void SetPortableImtConflictTrampolineOffset(uint32_t offset); const void* GetPortableToInterpreterBridge() const; uint32_t GetPortableToInterpreterBridgeOffset() const; void SetPortableToInterpreterBridgeOffset(uint32_t offset); @@ -69,6 +72,9 @@ class PACKED(4) OatHeader { const void* GetQuickResolutionTrampoline() const; uint32_t GetQuickResolutionTrampolineOffset() const; void SetQuickResolutionTrampolineOffset(uint32_t offset); + const void* GetQuickImtConflictTrampoline() const; + uint32_t GetQuickImtConflictTrampolineOffset() const; + void SetQuickImtConflictTrampolineOffset(uint32_t offset); const void* GetQuickToInterpreterBridge() const; uint32_t GetQuickToInterpreterBridgeOffset() const; void SetQuickToInterpreterBridgeOffset(uint32_t offset); @@ -91,8 +97,10 @@ class PACKED(4) OatHeader { uint32_t interpreter_to_interpreter_bridge_offset_; uint32_t interpreter_to_compiled_code_bridge_offset_; uint32_t jni_dlsym_lookup_offset_; + uint32_t portable_imt_conflict_trampoline_offset_; uint32_t portable_resolution_trampoline_offset_; uint32_t portable_to_interpreter_bridge_offset_; + uint32_t quick_imt_conflict_trampoline_offset_; uint32_t quick_resolution_trampoline_offset_; uint32_t quick_to_interpreter_bridge_offset_; diff --git a/runtime/object_utils.h b/runtime/object_utils.h index 3ca3c0bcf33..bf25b810a90 100644 --- a/runtime/object_utils.h +++ b/runtime/object_utils.h @@ -458,6 +458,8 @@ class MethodHelper { Runtime* runtime = Runtime::Current(); if (method_ == runtime->GetResolutionMethod()) { return ""; + } else if (method_ == runtime->GetImtConflictMethod()) { + return ""; } else if (method_ == runtime->GetCalleeSaveMethod(Runtime::kSaveAll)) { return ""; } else if (method_ == runtime->GetCalleeSaveMethod(Runtime::kRefsOnly)) { diff --git a/runtime/runtime.cc b/runtime/runtime.cc index f46b7943873..34cf45b4c7d 100644 --- a/runtime/runtime.cc +++ b/runtime/runtime.cc @@ -84,6 +84,8 @@ Runtime::Runtime() java_vm_(NULL), pre_allocated_OutOfMemoryError_(NULL), resolution_method_(NULL), + imt_conflict_method_(NULL), + default_imt_(NULL), threads_being_born_(0), shutdown_cond_(new ConditionVariable("Runtime shutdown", *Locks::runtime_shutdown_lock_)), shutting_down_(false), @@ -1175,6 +1177,10 @@ void Runtime::VisitNonThreadRoots(RootVisitor* visitor, void* arg) { } resolution_method_ = reinterpret_cast(visitor(resolution_method_, arg)); DCHECK(resolution_method_ != nullptr); + imt_conflict_method_ = reinterpret_cast(visitor(imt_conflict_method_, arg)); + DCHECK(imt_conflict_method_ != nullptr); + default_imt_ = reinterpret_cast*>(visitor(default_imt_, arg)); + DCHECK(default_imt_ != nullptr); for (int i = 0; i < Runtime::kLastCalleeSaveType; i++) { callee_save_methods_[i] = reinterpret_cast( visitor(callee_save_methods_[i], arg)); @@ -1192,6 +1198,31 @@ void Runtime::VisitRoots(RootVisitor* visitor, void* arg, bool only_dirty, bool VisitNonConcurrentRoots(visitor, arg); } +mirror::ObjectArray* Runtime::CreateDefaultImt(ClassLinker* cl) { + Thread* self = Thread::Current(); + SirtRef > imtable(self, cl->AllocArtMethodArray(self, 64)); + mirror::ArtMethod* imt_conflict_method = Runtime::Current()->GetImtConflictMethod(); + for (size_t i = 0; i < 64; i++) { + imtable->Set(i, imt_conflict_method); + } + return imtable.get(); +} + +mirror::ArtMethod* Runtime::CreateImtConflictMethod() { + mirror::Class* method_class = mirror::ArtMethod::GetJavaLangReflectArtMethod(); + Thread* self = Thread::Current(); + SirtRef + method(self, down_cast(method_class->AllocObject(self))); + method->SetDeclaringClass(method_class); + // TODO: use a special method for imt conflict method saves + method->SetDexMethodIndex(DexFile::kDexNoIndex); + // When compiling, the code pointer will get set later when the image is loaded. + Runtime* r = Runtime::Current(); + ClassLinker* cl = r->GetClassLinker(); + method->SetEntryPointFromCompiledCode(r->IsCompiler() ? NULL : GetImtConflictTrampoline(cl)); + return method.get(); +} + mirror::ArtMethod* Runtime::CreateResolutionMethod() { mirror::Class* method_class = mirror::ArtMethod::GetJavaLangReflectArtMethod(); Thread* self = Thread::Current(); diff --git a/runtime/runtime.h b/runtime/runtime.h index b6429b646d0..51d9074d033 100644 --- a/runtime/runtime.h +++ b/runtime/runtime.h @@ -45,6 +45,7 @@ namespace gc { namespace mirror { class ArtMethod; class ClassLoader; + template class ObjectArray; template class PrimitiveArray; typedef PrimitiveArray ByteArray; class String; @@ -349,6 +350,39 @@ class Runtime { mirror::ArtMethod* CreateResolutionMethod() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + // Returns a special method that calls into a trampoline for runtime imt conflicts + mirror::ArtMethod* GetImtConflictMethod() const { + CHECK(HasImtConflictMethod()); + return imt_conflict_method_; + } + + bool HasImtConflictMethod() const { + return imt_conflict_method_ != NULL; + } + + void SetImtConflictMethod(mirror::ArtMethod* method) { + imt_conflict_method_ = method; + } + + mirror::ArtMethod* CreateImtConflictMethod() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + // Returns an imt with every entry set to conflict, used as default imt for all classes. + mirror::ObjectArray* GetDefaultImt() const { + CHECK(HasDefaultImt()); + return default_imt_; + } + + bool HasDefaultImt() const { + return default_imt_ != NULL; + } + + void SetDefaultImt(mirror::ObjectArray* imt) { + default_imt_ = imt; + } + + mirror::ObjectArray* CreateDefaultImt(ClassLinker* cl) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + // Returns a special method that describes all callee saves being spilled to the stack. enum CalleeSaveType { kSaveAll, @@ -485,6 +519,10 @@ class Runtime { mirror::ArtMethod* resolution_method_; + mirror::ArtMethod* imt_conflict_method_; + + mirror::ObjectArray* default_imt_; + // A non-zero value indicates that a thread has been created but not yet initialized. Guarded by // the shutdown lock so that threads aren't born while we're shutting down. size_t threads_being_born_ GUARDED_BY(Locks::runtime_shutdown_lock_); diff --git a/runtime/thread.cc b/runtime/thread.cc index 30636589cd3..97510762353 100644 --- a/runtime/thread.cc +++ b/runtime/thread.cc @@ -1555,6 +1555,7 @@ static const EntryPointInfo gThreadEntryPointInfo[] = { INTERPRETER_ENTRY_POINT_INFO(pInterpreterToInterpreterBridge), INTERPRETER_ENTRY_POINT_INFO(pInterpreterToCompiledCodeBridge), JNI_ENTRY_POINT_INFO(pDlsymLookup), + PORTABLE_ENTRY_POINT_INFO(pPortableImtConflictTrampoline), PORTABLE_ENTRY_POINT_INFO(pPortableResolutionTrampoline), PORTABLE_ENTRY_POINT_INFO(pPortableToInterpreterBridge), QUICK_ENTRY_POINT_INFO(pAllocArray), @@ -1617,10 +1618,10 @@ static const EntryPointInfo gThreadEntryPointInfo[] = { QUICK_ENTRY_POINT_INFO(pMemcmp16), QUICK_ENTRY_POINT_INFO(pStringCompareTo), QUICK_ENTRY_POINT_INFO(pMemcpy), + QUICK_ENTRY_POINT_INFO(pQuickImtConflictTrampoline), QUICK_ENTRY_POINT_INFO(pQuickResolutionTrampoline), QUICK_ENTRY_POINT_INFO(pQuickToInterpreterBridge), QUICK_ENTRY_POINT_INFO(pInvokeDirectTrampolineWithAccessCheck), - QUICK_ENTRY_POINT_INFO(pInvokeInterfaceTrampoline), QUICK_ENTRY_POINT_INFO(pInvokeInterfaceTrampolineWithAccessCheck), QUICK_ENTRY_POINT_INFO(pInvokeStaticTrampolineWithAccessCheck), QUICK_ENTRY_POINT_INFO(pInvokeSuperTrampolineWithAccessCheck), diff --git a/test/100-reflect2/expected.txt b/test/100-reflect2/expected.txt index 967f167f37c..3d87ebc5596 100644 --- a/test/100-reflect2/expected.txt +++ b/test/100-reflect2/expected.txt @@ -35,7 +35,7 @@ z (class java.lang.Character) 62 (class java.lang.Long) 14 (class java.lang.Short) [public java.lang.String(), java.lang.String(int,int,char[]), public java.lang.String(java.lang.String), public java.lang.String(java.lang.StringBuffer), public java.lang.String(java.lang.StringBuilder), public java.lang.String(byte[]), public java.lang.String(byte[],int), public java.lang.String(byte[],int,int), public java.lang.String(byte[],int,int,int), public java.lang.String(byte[],int,int,java.lang.String) throws java.io.UnsupportedEncodingException, public java.lang.String(byte[],int,int,java.nio.charset.Charset), public java.lang.String(byte[],java.lang.String) throws java.io.UnsupportedEncodingException, public java.lang.String(byte[],java.nio.charset.Charset), public java.lang.String(char[]), public java.lang.String(char[],int,int), public java.lang.String(int[],int,int)] -[private final char[] java.lang.String.value, private final int java.lang.String.count, private int java.lang.String.hashCode, private final int java.lang.String.offset, private static final char[] java.lang.String.ASCII, public static final java.util.Comparator java.lang.String.CASE_INSENSITIVE_ORDER, private static final long java.lang.String.serialVersionUID, private static final char java.lang.String.REPLACEMENT_CHAR] +[private final char[] java.lang.String.value, private final int java.lang.String.count, private int java.lang.String.hashCode, private final int java.lang.String.offset, private static final char[] java.lang.String.ASCII, public static final java.util.Comparator java.lang.String.CASE_INSENSITIVE_ORDER, private static final char java.lang.String.REPLACEMENT_CHAR, private static final long java.lang.String.serialVersionUID] [void java.lang.String._getChars(int,int,char[],int), public char java.lang.String.charAt(int), public int java.lang.String.codePointAt(int), public int java.lang.String.codePointBefore(int), public int java.lang.String.codePointCount(int,int), public volatile int java.lang.String.compareTo(java.lang.Object), public native int java.lang.String.compareTo(java.lang.String), public int java.lang.String.compareToIgnoreCase(java.lang.String), public java.lang.String java.lang.String.concat(java.lang.String), public boolean java.lang.String.contains(java.lang.CharSequence), public boolean java.lang.String.contentEquals(java.lang.CharSequence), public boolean java.lang.String.contentEquals(java.lang.StringBuffer), public boolean java.lang.String.endsWith(java.lang.String), public boolean java.lang.String.equals(java.lang.Object), public boolean java.lang.String.equalsIgnoreCase(java.lang.String), public void java.lang.String.getBytes(int,int,byte[],int), public [B java.lang.String.getBytes(), public [B java.lang.String.getBytes(java.lang.String) throws java.io.UnsupportedEncodingException, public [B java.lang.String.getBytes(java.nio.charset.Charset), public void java.lang.String.getChars(int,int,char[],int), public int java.lang.String.hashCode(), public int java.lang.String.indexOf(int), public int java.lang.String.indexOf(int,int), public int java.lang.String.indexOf(java.lang.String), public int java.lang.String.indexOf(java.lang.String,int), public native java.lang.String java.lang.String.intern(), public boolean java.lang.String.isEmpty(), public int java.lang.String.lastIndexOf(int), public int java.lang.String.lastIndexOf(int,int), public int java.lang.String.lastIndexOf(java.lang.String), public int java.lang.String.lastIndexOf(java.lang.String,int), public int java.lang.String.length(), public boolean java.lang.String.matches(java.lang.String), public int java.lang.String.offsetByCodePoints(int,int), public boolean java.lang.String.regionMatches(int,java.lang.String,int,int), public boolean java.lang.String.regionMatches(boolean,int,java.lang.String,int,int), public java.lang.String java.lang.String.replace(char,char), public java.lang.String java.lang.String.replace(java.lang.CharSequence,java.lang.CharSequence), public java.lang.String java.lang.String.replaceAll(java.lang.String,java.lang.String), public java.lang.String java.lang.String.replaceFirst(java.lang.String,java.lang.String), public [Ljava.lang.String; java.lang.String.split(java.lang.String), public [Ljava.lang.String; java.lang.String.split(java.lang.String,int), public boolean java.lang.String.startsWith(java.lang.String), public boolean java.lang.String.startsWith(java.lang.String,int), public java.lang.CharSequence java.lang.String.subSequence(int,int), public java.lang.String java.lang.String.substring(int), public java.lang.String java.lang.String.substring(int,int), public [C java.lang.String.toCharArray(), public java.lang.String java.lang.String.toLowerCase(), public java.lang.String java.lang.String.toLowerCase(java.util.Locale), public java.lang.String java.lang.String.toString(), public java.lang.String java.lang.String.toUpperCase(), public java.lang.String java.lang.String.toUpperCase(java.util.Locale), public java.lang.String java.lang.String.trim(), public static java.lang.String java.lang.String.copyValueOf(char[]), public static java.lang.String java.lang.String.copyValueOf(char[],int,int), private java.lang.StringIndexOutOfBoundsException java.lang.String.failedBoundsCheck(int,int,int), private native int java.lang.String.fastIndexOf(int,int), private char java.lang.String.foldCase(char), public static transient java.lang.String java.lang.String.format(java.lang.String,java.lang.Object[]), public static transient java.lang.String java.lang.String.format(java.util.Locale,java.lang.String,java.lang.Object[]), private java.lang.StringIndexOutOfBoundsException java.lang.String.indexAndLength(int), private static int java.lang.String.indexOf(java.lang.String,java.lang.String,int,int,char), private int java.lang.String.indexOfSupplementary(int,int), private int java.lang.String.lastIndexOfSupplementary(int,int), private java.lang.StringIndexOutOfBoundsException java.lang.String.startEndAndLength(int,int), public static java.lang.String java.lang.String.valueOf(char), public static java.lang.String java.lang.String.valueOf(double), public static java.lang.String java.lang.String.valueOf(float), public static java.lang.String java.lang.String.valueOf(int), public static java.lang.String java.lang.String.valueOf(long), public static java.lang.String java.lang.String.valueOf(java.lang.Object), public static java.lang.String java.lang.String.valueOf(boolean), public static java.lang.String java.lang.String.valueOf(char[]), public static java.lang.String java.lang.String.valueOf(char[],int,int)] [] [interface java.io.Serializable, interface java.lang.Comparable, interface java.lang.CharSequence] diff --git a/test/Android.mk b/test/Android.mk index cdd61f0eeff..6d3a84a25fb 100644 --- a/test/Android.mk +++ b/test/Android.mk @@ -44,6 +44,7 @@ TEST_OAT_DIRECTORIES := \ Main \ HelloWorld \ \ + InterfaceTest \ JniTest \ NativeAllocations \ ParallelGC \ diff --git a/test/InterfaceTest/InterfaceTest.java b/test/InterfaceTest/InterfaceTest.java new file mode 100644 index 00000000000..ed18eb3dc78 --- /dev/null +++ b/test/InterfaceTest/InterfaceTest.java @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import java.util.Map; +import java.util.HashMap; + +class InterfaceTest { + + public static long test_virtual(HashMap map) { + Integer intobj = new Integer(0); + String s = "asdf"; + long start = System.currentTimeMillis(); + for (int i = 0; i < 1000000; i++) { + map.put(intobj, s); + } + long end = System.currentTimeMillis(); + return (end - start); + } + + public static long test_interface(Map map) { + Integer intobj = new Integer(0); + String s = "asdf"; + long start = System.currentTimeMillis(); + for (int i = 0; i < 1000000; i++) { + map.put(intobj, s); + } + long end = System.currentTimeMillis(); + return (end - start); + } + + public static void main(String[] args) { + HashMap hashmap = new HashMap(); + long elapsed = test_virtual(hashmap); + System.logI("virtual map put: " + elapsed); + hashmap.clear(); + + elapsed = test_interface(hashmap); + System.logI("interface map put: " + elapsed); + } +} From ad2541a59c00c2c69e8973088891a2b5257c9780 Mon Sep 17 00:00:00 2001 From: Mathieu Chartier Date: Fri, 25 Oct 2013 10:05:23 -0700 Subject: [PATCH 0119/2402] Fix object identity hash. The object identity hash is now stored in the monitor word after being computed. Hashes are computed by a pseudo random number generator. When we write the image, we eagerly compute object hashes to prevent pages getting dirtied. Bug: 8981901 Change-Id: Ic8edacbacb0afc7055fd740a52444929f88ed564 --- compiler/image_writer.cc | 26 ++++- runtime/arch/arm/quick_entrypoints_arm.S | 19 ++-- runtime/arch/x86/quick_entrypoints_x86.S | 6 +- runtime/gc/collector/mark_sweep.h | 6 +- runtime/gc/heap.h | 3 +- runtime/lock_word-inl.h | 9 +- runtime/lock_word.h | 63 ++++++++---- runtime/locks.h | 2 +- runtime/mirror/object-inl.h | 2 +- runtime/mirror/object.cc | 48 +++++++++ runtime/mirror/object.h | 16 ++- runtime/monitor.cc | 122 ++++++++++++++++------- runtime/monitor.h | 21 +++- runtime/native/java_lang_System.cc | 3 + runtime/runtime.h | 3 +- 15 files changed, 258 insertions(+), 91 deletions(-) diff --git a/compiler/image_writer.cc b/compiler/image_writer.cc index 871cfd5c41b..d60b544479a 100644 --- a/compiler/image_writer.cc +++ b/compiler/image_writer.cc @@ -36,6 +36,7 @@ #include "globals.h" #include "image.h" #include "intern_table.h" +#include "lock_word.h" #include "mirror/art_field-inl.h" #include "mirror/art_method-inl.h" #include "mirror/array-inl.h" @@ -489,7 +490,30 @@ void ImageWriter::CopyAndFixupObjectsCallback(Object* object, void* arg) { DCHECK_LT(offset + n, image_writer->image_->Size()); memcpy(dst, src, n); Object* copy = reinterpret_cast(dst); - copy->SetField32(Object::MonitorOffset(), 0, false); // We may have inflated the lock during compilation. + // Write in a hash code of objects which have inflated monitors or a hash code in their monitor + // word. + LockWord lw(copy->GetLockWord()); + switch (lw.GetState()) { + case LockWord::kFatLocked: { + Monitor* monitor = lw.FatLockMonitor(); + CHECK(monitor != nullptr); + CHECK(!monitor->IsLocked()); + copy->SetLockWord(LockWord::FromHashCode(monitor->GetHashCode())); + break; + } + case LockWord::kThinLocked: { + LOG(FATAL) << "Thin locked object " << obj << " found during object copy"; + break; + } + case LockWord::kUnlocked: + // Fall-through. + case LockWord::kHashCode: + // Do nothing since we can just keep the same hash code. + break; + default: + LOG(FATAL) << "Unreachable."; + break; + } image_writer->FixupObject(obj, copy); } diff --git a/runtime/arch/arm/quick_entrypoints_arm.S b/runtime/arch/arm/quick_entrypoints_arm.S index 736ce2fb24a..c1134980205 100644 --- a/runtime/arch/arm/quick_entrypoints_arm.S +++ b/runtime/arch/arm/quick_entrypoints_arm.S @@ -325,24 +325,25 @@ END art_quick_handle_fill_data ENTRY art_quick_lock_object cbz r0, slow_lock retry_lock: - ldrex r1, [r0, #LOCK_WORD_OFFSET] ldrt r2, [r9, #THREAD_ID_OFFSET] - cmp r1, #0 - bmi slow_lock @ lock word contains a monitor - bne already_thin + ldrex r1, [r0, #LOCK_WORD_OFFSET] + cbnz r1, not_unlocked @ already thin locked @ unlocked case - r2 holds thread id with count of 0 strex r3, r2, [r0, #LOCK_WORD_OFFSET] cbnz r3, strex_fail @ store failed, retry bx lr strex_fail: b retry_lock @ unlikely forward branch, need to reload and recheck r1/r2 -already_thin: +not_unlocked: + lsr r3, r1, 30 + cbnz r3, slow_lock @ if either of the top two bits are set, go slow path eor r2, r1, r2 @ lock_word.ThreadId() ^ self->ThreadId() uxth r2, r2 @ zero top 16 bits cbnz r2, slow_lock @ lock word and self thread id's match -> recursive lock @ else contention, go to slow path - adds r2, r1, #65536 @ increment count in lock word placing in r2 for storing - bmi slow_lock @ if we overflow the count go slow + add r2, r1, #65536 @ increment count in lock word placing in r2 for storing + lsr r1, r2, 30 @ if either of the top two bits are set, we overflowed. + cbnz r1, slow_lock @ if we overflow the count go slow path str r2, [r0, #LOCK_WORD_OFFSET] @ no need for strex as we hold the lock bx lr slow_lock: @@ -363,9 +364,9 @@ END art_quick_lock_object ENTRY art_quick_unlock_object cbz r0, slow_unlock ldr r1, [r0, #LOCK_WORD_OFFSET] + lsr r2, r1, 30 + cbnz r2, slow_unlock @ if either of the top two bits are set, go slow path ldr r2, [r9, #THREAD_ID_OFFSET] - cmp r1, #0 - bmi slow_unlock @ lock word contains a monitor eor r3, r1, r2 @ lock_word.ThreadId() ^ self->ThreadId() uxth r3, r3 @ zero top 16 bits cbnz r3, slow_unlock @ do lock word and self thread id's match? diff --git a/runtime/arch/x86/quick_entrypoints_x86.S b/runtime/arch/x86/quick_entrypoints_x86.S index 805f6f4bd19..4e797701b89 100644 --- a/runtime/arch/x86/quick_entrypoints_x86.S +++ b/runtime/arch/x86/quick_entrypoints_x86.S @@ -411,9 +411,10 @@ DEFINE_FUNCTION art_quick_lock_object jz slow_lock retry_lock: movl LOCK_WORD_OFFSET(%eax), %ecx // ecx := lock word + test LITERAL(0xC0000000), %ecx // test the 2 high bits. + jne slow_lock // slow path if either of the two high bits are set. movl %fs:THREAD_ID_OFFSET, %edx // edx := thread id test %ecx, %ecx - jb slow_lock // lock word contains a monitor jnz already_thin // lock word contains a thin lock // unlocked case - %edx holds thread id with count of 0 movl %eax, %ecx // remember object in case of retry @@ -428,7 +429,8 @@ already_thin: cmpw %ax, %dx // do we hold the lock already? jne slow_lock addl LITERAL(65536), %eax // increment recursion count - jb slow_lock // count overflowed so go slow + test LITERAL(0xC0000000), %eax // overflowed if either of top two bits are set + jne slow_lock // count overflowed so go slow movl %eax, LOCK_WORD_OFFSET(%ecx) // update lockword, cmpxchg not necessary as we hold lock ret slow_lock: diff --git a/runtime/gc/collector/mark_sweep.h b/runtime/gc/collector/mark_sweep.h index 19df2da0b67..7e051369f04 100644 --- a/runtime/gc/collector/mark_sweep.h +++ b/runtime/gc/collector/mark_sweep.h @@ -69,7 +69,7 @@ class MarkSweep : public GarbageCollector { virtual bool HandleDirtyObjectsPhase() EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_); virtual void MarkingPhase() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); virtual void ReclaimPhase() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - virtual void FinishPhase(); + virtual void FinishPhase() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); virtual void MarkReachableObjects() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_); @@ -208,13 +208,13 @@ class MarkSweep : public GarbageCollector { void SetImmuneRange(mirror::Object* begin, mirror::Object* end); void SweepSystemWeaks() - SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_); + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_, Locks::heap_bitmap_lock_); static mirror::Object* VerifySystemWeakIsLiveCallback(mirror::Object* obj, void* arg) SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_); void VerifySystemWeaks() - SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_); + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_, Locks::heap_bitmap_lock_); // Verify that an object is live, either in a live bitmap or in the allocation stack. void VerifyIsLive(const mirror::Object* obj) diff --git a/runtime/gc/heap.h b/runtime/gc/heap.h index 1c2b7efc225..7d2441baa16 100644 --- a/runtime/gc/heap.h +++ b/runtime/gc/heap.h @@ -518,8 +518,9 @@ class Heap { void PreGcVerification(collector::GarbageCollector* gc); void PreSweepingGcVerification(collector::GarbageCollector* gc) + EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_); + void PostGcVerification(collector::GarbageCollector* gc) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - void PostGcVerification(collector::GarbageCollector* gc); // Update the watermark for the native allocated bytes based on the current number of native // bytes allocated and the target utilization ratio. diff --git a/runtime/lock_word-inl.h b/runtime/lock_word-inl.h index 30bf9bbaa65..59947f56946 100644 --- a/runtime/lock_word-inl.h +++ b/runtime/lock_word-inl.h @@ -33,7 +33,7 @@ inline uint32_t LockWord::ThinLockCount() const { inline Monitor* LockWord::FatLockMonitor() const { DCHECK_EQ(GetState(), kFatLocked); - return reinterpret_cast(value_ << 1); + return reinterpret_cast(value_ << kStateSize); } inline LockWord::LockWord() : value_(0) { @@ -41,10 +41,15 @@ inline LockWord::LockWord() : value_(0) { } inline LockWord::LockWord(Monitor* mon) - : value_((reinterpret_cast(mon) >> 1) | (kStateFat << kStateShift)) { + : value_((reinterpret_cast(mon) >> kStateSize) | (kStateFat << kStateShift)) { DCHECK_EQ(FatLockMonitor(), mon); } +inline uint32_t LockWord::GetHashCode() const { + DCHECK_EQ(GetState(), kHashCode); + return (value_ >> kHashShift) & kHashMask; +} + } // namespace art #endif // ART_RUNTIME_LOCK_WORD_INL_H_ diff --git a/runtime/lock_word.h b/runtime/lock_word.h index cd4bfbb9010..9b6c64a1835 100644 --- a/runtime/lock_word.h +++ b/runtime/lock_word.h @@ -29,30 +29,37 @@ namespace mirror { class Monitor; -/* The lock value itself as stored in mirror::Object::monitor_. The MSB of the lock encodes its - * state. When cleared, the lock is in the "thin" state and its bits are formatted as follows: +/* The lock value itself as stored in mirror::Object::monitor_. The two most significant bits of + * the state. The three possible states are fat locked, thin/unlocked, and hash code. + * When the lock word is in the "thin" state and its bits are formatted as follows: * - * |3|32222222222111|11111110000000000| - * |1|09876543210987|65432109876543210| - * |0| lock count | thread id | + * |33|22222222221111|1111110000000000| + * |10|98765432109876|5432109876543210| + * |00| lock count |thread id owner | * - * When set, the lock is in the "fat" state and its bits are formatted as follows: + * When the lock word is in the "fat" state and its bits are formatted as follows: * - * |3|3222222222211111111110000000000| - * |1|0987654321098765432109876543210| - * |1| Monitor* >> 1 | + * |33|222222222211111111110000000000| + * |10|987654321098765432109876543210| + * |01| Monitor* >> kStateSize | + * + * When the lock word is in hash state and its bits are formatted as follows: + * + * |33|222222222211111111110000000000| + * |10|987654321098765432109876543210| + * |10| HashCode | */ class LockWord { public: enum { - // Number of bits to encode the state, currently just fat or thin/unlocked. - kStateSize = 1, + // Number of bits to encode the state, currently just fat or thin/unlocked or hash code. + kStateSize = 2, // Number of bits to encode the thin lock owner. kThinLockOwnerSize = 16, // Remaining bits are the recursive lock count. kThinLockCountSize = 32 - kThinLockOwnerSize - kStateSize, - // Thin lock bits. Owner in lowest bits. + kThinLockOwnerShift = 0, kThinLockOwnerMask = (1 << kThinLockOwnerSize) - 1, // Count in higher bits. @@ -65,25 +72,42 @@ class LockWord { kStateMask = (1 << kStateSize) - 1, kStateThinOrUnlocked = 0, kStateFat = 1, + kStateHash = 2, + + // When the state is kHashCode, the non-state bits hold the hashcode. + kHashShift = 0, + kHashSize = 32 - kStateSize, + kHashMask = (1 << kHashSize) - 1, }; static LockWord FromThinLockId(uint32_t thread_id, uint32_t count) { CHECK_LE(thread_id, static_cast(kThinLockOwnerMask)); - return LockWord((thread_id << kThinLockOwnerShift) | (count << kThinLockCountShift)); + return LockWord((thread_id << kThinLockOwnerShift) | (count << kThinLockCountShift) | + (kStateThinOrUnlocked << kStateShift)); + } + + static LockWord FromHashCode(uint32_t hash_code) { + CHECK_LE(hash_code, static_cast(kHashMask)); + return LockWord((hash_code << kHashShift) | (kStateHash << kStateShift)); } enum LockState { kUnlocked, // No lock owners. kThinLocked, // Single uncontended owner. - kFatLocked // See associated monitor. + kFatLocked, // See associated monitor. + kHashCode, // Lock word contains an identity hash. }; LockState GetState() const { + uint32_t internal_state = (value_ >> kStateShift) & kStateMask; if (value_ == 0) { return kUnlocked; - } else if (((value_ >> kStateShift) & kStateMask) == kStateThinOrUnlocked) { + } else if (internal_state == kStateThinOrUnlocked) { return kThinLocked; + } else if (internal_state == kStateHash) { + return kHashCode; } else { + DCHECK_EQ(internal_state, static_cast(kStateFat)); return kFatLocked; } } @@ -103,17 +127,20 @@ class LockWord { // Constructor a lock word for inflation to use a Monitor. explicit LockWord(Monitor* mon); - bool operator==(const LockWord& rhs) { + bool operator==(const LockWord& rhs) const { return GetValue() == rhs.GetValue(); } - private: - explicit LockWord(uint32_t val) : value_(val) {} + // Return the hash code stored in the lock word, must be kHashCode state. + uint32_t GetHashCode() const; uint32_t GetValue() const { return value_; } + private: + explicit LockWord(uint32_t val) : value_(val) {} + // Only Object should be converting LockWords to/from uints. friend class mirror::Object; diff --git a/runtime/locks.h b/runtime/locks.h index f63e2b17209..226221822bf 100644 --- a/runtime/locks.h +++ b/runtime/locks.h @@ -53,8 +53,8 @@ enum LockLevel { kJdwpAttachLock, kJdwpStartLock, kRuntimeShutdownLock, - kHeapBitmapLock, kMonitorLock, + kHeapBitmapLock, kMutatorLock, kZygoteCreationLock, diff --git a/runtime/mirror/object-inl.h b/runtime/mirror/object-inl.h index e460a8d1cca..7ac2c8c083a 100644 --- a/runtime/mirror/object-inl.h +++ b/runtime/mirror/object-inl.h @@ -44,7 +44,7 @@ inline void Object::SetClass(Class* new_klass) { SetFieldPtr(OFFSET_OF_OBJECT_MEMBER(Object, klass_), new_klass, false, false); } -inline LockWord Object::GetLockWord() { +inline LockWord Object::GetLockWord() const { return LockWord(GetField32(OFFSET_OF_OBJECT_MEMBER(Object, monitor_), true)); } diff --git a/runtime/mirror/object.cc b/runtime/mirror/object.cc index 92c05b26ca3..49bad4ccb8e 100644 --- a/runtime/mirror/object.cc +++ b/runtime/mirror/object.cc @@ -14,6 +14,8 @@ * limitations under the License. */ +#include + #include "object.h" #include "art_field.h" @@ -82,6 +84,52 @@ Object* Object::Clone(Thread* self) { return copy.get(); } +uint32_t Object::GenerateIdentityHashCode() { + static AtomicInteger seed(987654321 + std::time(nullptr)); + uint32_t expected_value, new_value; + do { + expected_value = static_cast(seed.load()); + new_value = expected_value * 1103515245 + 12345; + } while (!seed.compare_and_swap(static_cast(expected_value), + static_cast(new_value))); + return expected_value & LockWord::kHashMask; +} + +int32_t Object::IdentityHashCode() const { + while (true) { + LockWord lw = GetLockWord(); + switch (lw.GetState()) { + case LockWord::kUnlocked: { + // Try to compare and swap in a new hash, if we succeed we will return the hash on the next + // loop iteration. + LockWord hash_word(LockWord::FromHashCode(GenerateIdentityHashCode())); + DCHECK_EQ(hash_word.GetState(), LockWord::kHashCode); + if (const_cast(this)->CasLockWord(lw, hash_word)) { + return hash_word.GetHashCode(); + } + break; + } + case LockWord::kThinLocked: { + // Inflate the thin lock to a monitor and stick the hash code inside of the monitor. + Thread* self = Thread::Current(); + Monitor::InflateThinLocked(self, const_cast(this), lw, GenerateIdentityHashCode()); + break; + } + case LockWord::kFatLocked: { + // Already inflated, return the has stored in the monitor. + Monitor* monitor = lw.FatLockMonitor(); + DCHECK(monitor != nullptr); + return monitor->GetHashCode(); + } + case LockWord::kHashCode: { + return lw.GetHashCode(); + } + } + } + LOG(FATAL) << "Unreachable"; + return 0; +} + void Object::CheckFieldAssignmentImpl(MemberOffset field_offset, const Object* new_value) { const Class* c = GetClass(); if (Runtime::Current()->GetClassLinker() == NULL || diff --git a/runtime/mirror/object.h b/runtime/mirror/object.h index e3f5c101c1a..11473cd3991 100644 --- a/runtime/mirror/object.h +++ b/runtime/mirror/object.h @@ -27,6 +27,7 @@ namespace art { class ImageWriter; class LockWord; +class Monitor; struct ObjectOffsets; class Thread; @@ -84,19 +85,13 @@ class MANAGED Object { Object* Clone(Thread* self) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - int32_t IdentityHashCode() const { -#ifdef MOVING_GARBAGE_COLLECTOR - // TODO: we'll need to use the Object's internal concept of identity - UNIMPLEMENTED(FATAL); -#endif - return reinterpret_cast(this); - } + int32_t IdentityHashCode() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); static MemberOffset MonitorOffset() { return OFFSET_OF_OBJECT_MEMBER(Object, monitor_); } - LockWord GetLockWord(); + LockWord GetLockWord() const; void SetLockWord(LockWord new_val); bool CasLockWord(LockWord old_val, LockWord new_val); uint32_t GetLockOwnerThreadId(); @@ -243,7 +238,6 @@ class MANAGED Object { private: static void VerifyObject(const Object* obj) ALWAYS_INLINE; - // Verify the type correctness of stores to fields. void CheckFieldAssignmentImpl(MemberOffset field_offset, const Object* new_value) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); @@ -254,6 +248,9 @@ class MANAGED Object { } } + // Generate an identity hash code. + static uint32_t GenerateIdentityHashCode(); + // Write barrier called post update to a reference bearing field. static void WriteBarrierField(const Object* dst, MemberOffset offset, const Object* new_value); @@ -262,6 +259,7 @@ class MANAGED Object { uint32_t monitor_; friend class art::ImageWriter; + friend class art::Monitor; friend struct art::ObjectOffsets; // for verifying offset information DISALLOW_IMPLICIT_CONSTRUCTORS(Object); }; diff --git a/runtime/monitor.cc b/runtime/monitor.cc index a5605ff3299..b1bf84f4983 100644 --- a/runtime/monitor.cc +++ b/runtime/monitor.cc @@ -79,35 +79,49 @@ void Monitor::Init(uint32_t lock_profiling_threshold, bool (*is_sensitive_thread is_sensitive_thread_hook_ = is_sensitive_thread_hook; } -Monitor::Monitor(Thread* owner, mirror::Object* obj) +Monitor::Monitor(Thread* owner, mirror::Object* obj, uint32_t hash_code) : monitor_lock_("a monitor lock", kMonitorLock), monitor_contenders_("monitor contenders", monitor_lock_), owner_(owner), lock_count_(0), obj_(obj), wait_set_(NULL), + hash_code_(hash_code), locking_method_(NULL), locking_dex_pc_(0) { // We should only inflate a lock if the owner is ourselves or suspended. This avoids a race // with the owner unlocking the thin-lock. - CHECK(owner == Thread::Current() || owner->IsSuspended()); + CHECK(owner == nullptr || owner == Thread::Current() || owner->IsSuspended()); + // The identity hash code is set for the life time of the monitor. } bool Monitor::Install(Thread* self) { MutexLock mu(self, monitor_lock_); // Uncontended mutex acquisition as monitor isn't yet public. - CHECK(owner_ == self || owner_->IsSuspended()); + CHECK(owner_ == nullptr || owner_ == self || owner_->IsSuspended()); // Propagate the lock state. - LockWord thin(obj_->GetLockWord()); - if (thin.GetState() != LockWord::kThinLocked) { - // The owner_ is suspended but another thread beat us to install a monitor. - CHECK_EQ(thin.GetState(), LockWord::kFatLocked); - return false; + LockWord lw(obj_->GetLockWord()); + switch (lw.GetState()) { + case LockWord::kThinLocked: { + CHECK_EQ(owner_->GetThreadId(), lw.ThinLockOwner()); + lock_count_ = lw.ThinLockCount(); + break; + } + case LockWord::kHashCode: { + CHECK_EQ(hash_code_, lw.GetHashCode()); + break; + } + case LockWord::kFatLocked: { + // The owner_ is suspended but another thread beat us to install a monitor. + return false; + } + case LockWord::kUnlocked: { + LOG(FATAL) << "Inflating unlocked lock word"; + break; + } } - CHECK_EQ(owner_->GetThreadId(), thin.ThinLockOwner()); - lock_count_ = thin.ThinLockCount(); LockWord fat(this); // Publish the updated lock word, which may race with other threads. - bool success = obj_->CasLockWord(thin, fat); + bool success = obj_->CasLockWord(lw, fat); // Lock profiling. if (success && lock_profiling_threshold_ != 0) { locking_method_ = owner_->GetCurrentMethod(&locking_dex_pc_); @@ -540,19 +554,46 @@ void Monitor::NotifyAll(Thread* self) { * thread must own the lock or the owner must be suspended. There's a race with other threads * inflating the lock and so the caller should read the monitor following the call. */ -void Monitor::Inflate(Thread* self, Thread* owner, mirror::Object* obj) { +void Monitor::Inflate(Thread* self, Thread* owner, mirror::Object* obj, int32_t hash_code) { DCHECK(self != NULL); - DCHECK(owner != NULL); DCHECK(obj != NULL); - // Allocate and acquire a new monitor. - UniquePtr m(new Monitor(owner, obj)); + UniquePtr m(new Monitor(owner, obj, hash_code)); if (m->Install(self)) { VLOG(monitor) << "monitor: thread " << owner->GetThreadId() << " created monitor " << m.get() << " for object " << obj; Runtime::Current()->GetMonitorList()->Add(m.release()); + CHECK_EQ(obj->GetLockWord().GetState(), LockWord::kFatLocked); + } +} + +void Monitor::InflateThinLocked(Thread* self, mirror::Object* obj, LockWord lock_word, + uint32_t hash_code) { + DCHECK_EQ(lock_word.GetState(), LockWord::kThinLocked); + uint32_t owner_thread_id = lock_word.ThinLockOwner(); + if (owner_thread_id == self->GetThreadId()) { + // We own the monitor, we can easily inflate it. + Inflate(self, self, obj, hash_code); + } else { + ThreadList* thread_list = Runtime::Current()->GetThreadList(); + // Suspend the owner, inflate. First change to blocked and give up mutator_lock_. + ScopedThreadStateChange tsc(self, kBlocked); + if (lock_word == obj->GetLockWord()) { // If lock word hasn't changed. + bool timed_out; + Thread* owner = thread_list->SuspendThreadByThreadId(lock_word.ThinLockOwner(), false, + &timed_out); + if (owner != nullptr) { + // We succeeded in suspending the thread, check the lock's status didn't change. + lock_word = obj->GetLockWord(); + if (lock_word.GetState() == LockWord::kThinLocked && + lock_word.ThinLockOwner() == owner_thread_id) { + // Go ahead and inflate the lock. + Inflate(self, owner, obj, hash_code); + } + thread_list->Resume(owner, false); + } + } } - CHECK_EQ(obj->GetLockWord().GetState(), LockWord::kFatLocked); } void Monitor::MonitorEnter(Thread* self, mirror::Object* obj) { @@ -560,7 +601,6 @@ void Monitor::MonitorEnter(Thread* self, mirror::Object* obj) { DCHECK(obj != NULL); uint32_t thread_id = self->GetThreadId(); size_t contention_count = 0; - while (true) { LockWord lock_word = obj->GetLockWord(); switch (lock_word.GetState()) { @@ -582,33 +622,17 @@ void Monitor::MonitorEnter(Thread* self, mirror::Object* obj) { return; // Success! } else { // We'd overflow the recursion count, so inflate the monitor. - Inflate(self, self, obj); + InflateThinLocked(self, obj, lock_word, mirror::Object::GenerateIdentityHashCode()); } } else { // Contention. contention_count++; - if (contention_count <= Runtime::Current()->GetMaxSpinsBeforeThinkLockInflation()) { + Runtime* runtime = Runtime::Current(); + if (contention_count <= runtime->GetMaxSpinsBeforeThinkLockInflation()) { NanoSleep(1000); // Sleep for 1us and re-attempt. } else { contention_count = 0; - // Suspend the owner, inflate. First change to blocked and give up mutator_lock_. - ScopedThreadStateChange tsc(self, kBlocked); - bool timed_out; - ThreadList* thread_list = Runtime::Current()->GetThreadList(); - if (lock_word == obj->GetLockWord()) { // If lock word hasn't changed. - Thread* owner = thread_list->SuspendThreadByThreadId(lock_word.ThinLockOwner(), false, - &timed_out); - if (owner != NULL) { - // We succeeded in suspending the thread, check the lock's status didn't change. - lock_word = obj->GetLockWord(); - if (lock_word.GetState() == LockWord::kThinLocked && - lock_word.ThinLockOwner() == owner_thread_id) { - // Go ahead and inflate the lock. - Inflate(self, owner, obj); - } - thread_list->Resume(owner, false); - } - } + InflateThinLocked(self, obj, lock_word, mirror::Object::GenerateIdentityHashCode()); } } continue; // Start from the beginning. @@ -618,6 +642,11 @@ void Monitor::MonitorEnter(Thread* self, mirror::Object* obj) { mon->Lock(self); return; // Success! } + case LockWord::kHashCode: { + // Inflate with the existing hashcode. + Inflate(self, nullptr, obj, lock_word.GetHashCode()); + break; + } } } } @@ -628,6 +657,8 @@ bool Monitor::MonitorExit(Thread* self, mirror::Object* obj) { LockWord lock_word = obj->GetLockWord(); switch (lock_word.GetState()) { + case LockWord::kHashCode: + // Fall-through. case LockWord::kUnlocked: FailedUnlock(obj, self, NULL, NULL); return false; // Failure. @@ -672,6 +703,8 @@ void Monitor::Wait(Thread* self, mirror::Object *obj, int64_t ms, int32_t ns, LockWord lock_word = obj->GetLockWord(); switch (lock_word.GetState()) { + case LockWord::kHashCode: + // Fall-through. case LockWord::kUnlocked: ThrowIllegalMonitorStateExceptionF("object not locked by thread before wait()"); return; // Failure. @@ -683,7 +716,7 @@ void Monitor::Wait(Thread* self, mirror::Object *obj, int64_t ms, int32_t ns, return; // Failure. } else { // We own the lock, inflate to enqueue ourself on the Monitor. - Inflate(self, self, obj); + Inflate(self, self, obj, mirror::Object::GenerateIdentityHashCode()); lock_word = obj->GetLockWord(); } break; @@ -701,6 +734,8 @@ void Monitor::DoNotify(Thread* self, mirror::Object* obj, bool notify_all) { LockWord lock_word = obj->GetLockWord(); switch (lock_word.GetState()) { + case LockWord::kHashCode: + // Fall-through. case LockWord::kUnlocked: ThrowIllegalMonitorStateExceptionF("object not locked by thread before notify()"); return; // Failure. @@ -732,6 +767,8 @@ uint32_t Monitor::GetLockOwnerThreadId(mirror::Object* obj) { LockWord lock_word = obj->GetLockWord(); switch (lock_word.GetState()) { + case LockWord::kHashCode: + // Fall-through. case LockWord::kUnlocked: return ThreadList::kInvalidThreadId; case LockWord::kThinLocked: @@ -889,12 +926,19 @@ bool Monitor::IsValidLockWord(LockWord lock_word) { } return false; // Fail - unowned monitor in an object. } + case LockWord::kHashCode: + return true; default: LOG(FATAL) << "Unreachable"; return false; } } +bool Monitor::IsLocked() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + MutexLock mu(Thread::Current(), monitor_lock_); + return owner_ != nullptr; +} + void Monitor::TranslateLocation(const mirror::ArtMethod* method, uint32_t dex_pc, const char** source_file, uint32_t* line_number) const { // If method is null, location is unknown @@ -976,6 +1020,8 @@ MonitorInfo::MonitorInfo(mirror::Object* obj) : owner_(NULL), entry_count_(0) { LockWord lock_word = obj->GetLockWord(); switch (lock_word.GetState()) { case LockWord::kUnlocked: + // Fall-through. + case LockWord::kHashCode: break; case LockWord::kThinLocked: owner_ = Runtime::Current()->GetThreadList()->FindThreadByThreadId(lock_word.ThinLockOwner()); diff --git a/runtime/monitor.h b/runtime/monitor.h index 27124a2eb0b..c464400f467 100644 --- a/runtime/monitor.h +++ b/runtime/monitor.h @@ -88,8 +88,7 @@ class Monitor { static bool IsValidLockWord(LockWord lock_word); - // TODO: SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) - mirror::Object* GetObject() const { + mirror::Object* GetObject() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { return obj_; } @@ -99,8 +98,17 @@ class Monitor { return owner_; } + int32_t GetHashCode() const { + return hash_code_; + } + + bool IsLocked() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + static void InflateThinLocked(Thread* self, mirror::Object* obj, LockWord lock_word, + uint32_t hash_code) NO_THREAD_SAFETY_ANALYSIS; + private: - explicit Monitor(Thread* owner, mirror::Object* obj) + explicit Monitor(Thread* owner, mirror::Object* obj, uint32_t hash_code) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); // Install the monitor into its object, may fail if another thread installs a different monitor @@ -112,7 +120,7 @@ class Monitor { void AppendToWaitSet(Thread* thread) EXCLUSIVE_LOCKS_REQUIRED(monitor_lock_); void RemoveFromWaitSet(Thread* thread) EXCLUSIVE_LOCKS_REQUIRED(monitor_lock_); - static void Inflate(Thread* self, Thread* owner, mirror::Object* obj) + static void Inflate(Thread* self, Thread* owner, mirror::Object* obj, int32_t hash_code) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); void LogContentionEvent(Thread* self, uint32_t wait_ms, uint32_t sample_percent, @@ -171,6 +179,9 @@ class Monitor { // Threads currently waiting on this monitor. Thread* wait_set_ GUARDED_BY(monitor_lock_); + // Stored object hash code, always generated. + const uint32_t hash_code_; + // Method and dex pc where the lock owner acquired the lock, used when lock // sampling is enabled. locking_method_ may be null if the lock is currently // unlocked, or if the lock is acquired by the system when the stack is empty. @@ -190,7 +201,7 @@ class MonitorList { void Add(Monitor* m); - void SweepMonitorList(RootVisitor visitor, void* arg); + void SweepMonitorList(RootVisitor visitor, void* arg) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); void DisallowNewMonitors(); void AllowNewMonitors(); diff --git a/runtime/native/java_lang_System.cc b/runtime/native/java_lang_System.cc index 6674db2403e..ea78e04702e 100644 --- a/runtime/native/java_lang_System.cc +++ b/runtime/native/java_lang_System.cc @@ -339,6 +339,9 @@ static void System_arraycopyCharUnchecked(JNIEnv* env, jclass, jobject javaSrc, } static jint System_identityHashCode(JNIEnv* env, jclass, jobject javaObject) { + if (javaObject == nullptr) { + return 0; + } ScopedFastNativeObjectAccess soa(env); mirror::Object* o = soa.Decode(javaObject); return static_cast(o->IdentityHashCode()); diff --git a/runtime/runtime.h b/runtime/runtime.h index b6429b646d0..77da098d3c5 100644 --- a/runtime/runtime.h +++ b/runtime/runtime.h @@ -331,7 +331,8 @@ class Runtime { // Sweep system weaks, the system weak is deleted if the visitor return nullptr. Otherwise, the // system weak is updated to be the visitor's returned value. - void SweepSystemWeaks(RootVisitor* visitor, void* arg); + void SweepSystemWeaks(RootVisitor* visitor, void* arg) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); // Returns a special method that calls into a trampoline for runtime method resolution mirror::ArtMethod* GetResolutionMethod() const { From 2b07096c1de5461339d9ea97869d33b5166544ae Mon Sep 17 00:00:00 2001 From: Brian Carlstrom Date: Tue, 29 Oct 2013 13:35:25 -0700 Subject: [PATCH 0120/2402] Fix Mac build Change-Id: I2125cd9555c48ef69627f0824ae60759fc6ac853 --- build/Android.oat.mk | 3 +++ 1 file changed, 3 insertions(+) diff --git a/build/Android.oat.mk b/build/Android.oat.mk index e8438a213d2..b680b820b70 100644 --- a/build/Android.oat.mk +++ b/build/Android.oat.mk @@ -81,6 +81,8 @@ LOCAL_ADDITIONAL_DEPENDENCIES += $(HOST_CORE_IMG_OUT) include $(BUILD_PHONY_PACKAGE) endif +# If we aren't building the host toolchain, skip building the target core.art. +ifeq ($(WITH_HOST_DALVIK),true) ifeq ($(ART_BUILD_TARGET),true) include $(CLEAR_VARS) LOCAL_MODULE := core.art @@ -90,6 +92,7 @@ LOCAL_ADDITIONAL_DEPENDENCIES += art/build/Android.oat.mk LOCAL_ADDITIONAL_DEPENDENCIES += $(TARGET_CORE_IMG_OUT) include $(BUILD_PHONY_PACKAGE) endif +endif ######################################################################## # The full system boot classpath From dfb325e0ddd746cd8f7c2e3723b3a573eb7cc111 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Wed, 30 Oct 2013 01:00:44 -0700 Subject: [PATCH 0121/2402] Don't use UTF16 length as length for MUTF8. Bug 11367555. Change-Id: Ia0b07072a1a49d435c3b71ed9a668b316b7ff5d8 --- compiler/dex/quick/gen_invoke.cc | 2 +- compiler/driver/compiler_driver.cc | 20 +++--- compiler/driver/compiler_driver.h | 2 +- compiler/image_writer.cc | 4 +- runtime/class_linker.cc | 67 +++++++++--------- runtime/debugger.cc | 6 +- runtime/dex_file-inl.h | 83 +++++++++++++++++++---- runtime/dex_file.cc | 4 +- runtime/dex_file.h | 72 ++++---------------- runtime/gc/heap-inl.h | 2 +- runtime/mirror/class.cc | 18 ++--- runtime/native/dalvik_system_VMRuntime.cc | 2 +- runtime/object_utils.h | 62 ++--------------- runtime/reflection.cc | 2 +- runtime/verifier/method_verifier.cc | 4 +- 15 files changed, 152 insertions(+), 198 deletions(-) diff --git a/compiler/dex/quick/gen_invoke.cc b/compiler/dex/quick/gen_invoke.cc index 62feadedccf..5a1d3d1a529 100644 --- a/compiler/dex/quick/gen_invoke.cc +++ b/compiler/dex/quick/gen_invoke.cc @@ -1232,7 +1232,7 @@ bool Mir2Lir::GenIntrinsic(CallInfo* info) { const DexFile::MethodId& target_mid = cu_->dex_file->GetMethodId(info->index); const DexFile::TypeId& declaring_type = cu_->dex_file->GetTypeId(target_mid.class_idx_); StringPiece tgt_methods_declaring_class( - cu_->dex_file->StringDataAsStringPieceByIdx(declaring_type.descriptor_idx_)); + cu_->dex_file->StringDataByIdx(declaring_type.descriptor_idx_)); if (tgt_methods_declaring_class.starts_with("Ljava/lang/Double;")) { std::string tgt_method(PrettyMethod(info->index, *cu_->dex_file)); if (tgt_method == "long java.lang.Double.doubleToRawLongBits(double)") { diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc index 91b0188b7ba..7653d16352f 100644 --- a/compiler/driver/compiler_driver.cc +++ b/compiler/driver/compiler_driver.cc @@ -600,11 +600,11 @@ void CompilerDriver::PreCompile(jobject class_loader, const std::vectorfind(descriptor.data()) != image_classes_->end(); + return image_classes_->find(descriptor) != image_classes_->end(); } } @@ -780,7 +780,7 @@ void CompilerDriver::UpdateImageClasses(base::TimingLogger& timings) { bool CompilerDriver::CanAssumeTypeIsPresentInDexCache(const DexFile& dex_file, uint32_t type_idx) { if (IsImage() && - IsImageClass(dex_file.StringDataAsStringPieceByIdx(dex_file.GetTypeId(type_idx).descriptor_idx_))) { + IsImageClass(dex_file.StringDataByIdx(dex_file.GetTypeId(type_idx).descriptor_idx_))) { if (kIsDebugBuild) { ScopedObjectAccess soa(Thread::Current()); mirror::DexCache* dex_cache = Runtime::Current()->GetClassLinker()->FindDexCache(dex_file); @@ -1108,7 +1108,7 @@ void CompilerDriver::GetCodeAndMethodForDirectCall(InvokeType* type, InvokeType } if (!use_dex_cache && compiling_boot) { MethodHelper mh(method); - if (!IsImageClass(mh.GetDeclaringClassDescriptorAsStringPiece())) { + if (!IsImageClass(mh.GetDeclaringClassDescriptor())) { // We can only branch directly to Methods that are resolved in the DexCache. // Otherwise we won't invoke the resolution trampoline. use_dex_cache = true; @@ -1573,8 +1573,8 @@ static void ResolveType(const ParallelCompilationManager* manager, size_t type_i CHECK(soa.Self()->IsExceptionPending()); mirror::Throwable* exception = soa.Self()->GetException(NULL); VLOG(compiler) << "Exception during type resolution: " << exception->Dump(); - if (ClassHelper(exception->GetClass()).GetDescriptorAsStringPiece() == - "Ljava/lang/OutOfMemoryError;") { + if (strcmp("Ljava/lang/OutOfMemoryError;", + ClassHelper(exception->GetClass()).GetDescriptor()) == 0) { // There's little point continuing compilation if the heap is exhausted. LOG(FATAL) << "Out of memory during type resolution for compilation"; } @@ -2089,11 +2089,11 @@ static void InitializeClass(const ParallelCompilationManager* manager, size_t cl const DexFile* dex_file = manager->GetDexFile(); const DexFile::ClassDef& class_def = dex_file->GetClassDef(class_def_index); const DexFile::TypeId& class_type_id = dex_file->GetTypeId(class_def.class_idx_); - StringPiece descriptor(dex_file->StringDataAsStringPieceByIdx(class_type_id.descriptor_idx_)); + const char* descriptor = dex_file->StringDataByIdx(class_type_id.descriptor_idx_); ScopedObjectAccess soa(Thread::Current()); mirror::ClassLoader* class_loader = soa.Decode(manager->GetClassLoader()); - mirror::Class* klass = manager->GetClassLinker()->FindClass(descriptor.data(), class_loader); + mirror::Class* klass = manager->GetClassLinker()->FindClass(descriptor, class_loader); if (klass != NULL) { // Only try to initialize classes that were successfully verified. if (klass->IsVerified()) { @@ -2123,7 +2123,7 @@ static void InitializeClass(const ParallelCompilationManager* manager, size_t cl bool is_black_listed = StringPiece(descriptor).ends_with("$NoPreloadHolder;"); if (!is_black_listed) { for (size_t i = 0; i < arraysize(class_initializer_black_list); ++i) { - if (descriptor == class_initializer_black_list[i]) { + if (strcmp(descriptor, class_initializer_black_list[i]) == 0) { is_black_listed = true; break; } @@ -2131,7 +2131,7 @@ static void InitializeClass(const ParallelCompilationManager* manager, size_t cl } if (!is_black_listed) { VLOG(compiler) << "Initializing: " << descriptor; - if (descriptor == "Ljava/lang/Void;") { + if (strcmp("Ljava/lang/Void;", descriptor) == 0) { // Hand initialize j.l.Void to avoid Dex file operations in un-started runtime. ObjectLock lock(soa.Self(), klass); mirror::ObjectArray* fields = klass->GetSFields(); diff --git a/compiler/driver/compiler_driver.h b/compiler/driver/compiler_driver.h index 971021f9034..fe21eff842f 100644 --- a/compiler/driver/compiler_driver.h +++ b/compiler/driver/compiler_driver.h @@ -309,7 +309,7 @@ class CompilerDriver { } // Checks if class specified by type_idx is one of the image_classes_ - bool IsImageClass(const StringPiece& descriptor) const; + bool IsImageClass(const char* descriptor) const; void RecordClassStatus(ClassReference ref, mirror::Class::Status status) LOCKS_EXCLUDED(compiled_classes_lock_); diff --git a/compiler/image_writer.cc b/compiler/image_writer.cc index 871cfd5c41b..e66e214248f 100644 --- a/compiler/image_writer.cc +++ b/compiler/image_writer.cc @@ -244,7 +244,7 @@ void ImageWriter::ComputeEagerResolvedStrings() } bool ImageWriter::IsImageClass(const Class* klass) { - return compiler_driver_.IsImageClass(ClassHelper(klass).GetDescriptorAsStringPiece()); + return compiler_driver_.IsImageClass(ClassHelper(klass).GetDescriptor()); } struct NonImageClasses { @@ -299,7 +299,7 @@ void ImageWriter::PruneNonImageClasses() { bool ImageWriter::NonImageClassesVisitor(Class* klass, void* arg) { NonImageClasses* context = reinterpret_cast(arg); if (!context->image_writer->IsImageClass(klass)) { - context->non_image_classes->insert(ClassHelper(klass).GetDescriptorAsStringPiece().as_string()); + context->non_image_classes->insert(ClassHelper(klass).GetDescriptor()); } return true; } diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index 03f2c9df420..55bbf7cef13 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -1878,7 +1878,7 @@ mirror::ArtMethod* ClassLinker::LoadMethod(Thread* self, const DexFile& dex_file SirtRef& klass) { uint32_t dex_method_idx = it.GetMemberIndex(); const DexFile::MethodId& method_id = dex_file.GetMethodId(dex_method_idx); - StringPiece method_name(dex_file.StringDataAsStringPieceByIdx(method_id.name_idx_)); + const char* method_name = dex_file.StringDataByIdx(method_id.name_idx_); mirror::ArtMethod* dst = AllocArtMethod(self); if (UNLIKELY(dst == NULL)) { @@ -1899,34 +1899,30 @@ mirror::ArtMethod* ClassLinker::LoadMethod(Thread* self, const DexFile& dex_file uint32_t access_flags = it.GetMemberAccessFlags(); - if (UNLIKELY(method_name == "finalize")) { + if (UNLIKELY(strcmp("finalize", method_name) == 0)) { // Set finalizable flag on declaring class. - const DexFile::ProtoId& proto = dex_file.GetProtoId(method_id.proto_idx_); - if (dex_file.GetProtoParameters(proto) == NULL) { // No arguments - const DexFile::TypeId& return_type = dex_file.GetTypeId(proto.return_type_idx_); - if (dex_file.StringDataAsStringPieceByIdx(return_type.descriptor_idx_) == "V") { - // Void return type. - if (klass->GetClassLoader() != NULL) { // All non-boot finalizer methods are flagged + if (strcmp("V", dex_file.GetShorty(method_id.proto_idx_)) == 0) { + // Void return type. + if (klass->GetClassLoader() != NULL) { // All non-boot finalizer methods are flagged + klass->SetFinalizable(); + } else { + ClassHelper kh(klass.get()); + const char* klass_descriptor = kh.GetDescriptor(); + // The Enum class declares a "final" finalize() method to prevent subclasses from + // introducing a finalizer. We don't want to set the finalizable flag for Enum or its + // subclasses, so we exclude it here. + // We also want to avoid setting the flag on Object, where we know that finalize() is + // empty. + if ((strcmp("Ljava/lang/Object;", klass_descriptor) != 0) && + (strcmp("Ljava/lang/Enum;", klass_descriptor) != 0)) { klass->SetFinalizable(); - } else { - ClassHelper kh(klass.get()); - StringPiece klass_descriptor(kh.GetDescriptorAsStringPiece()); - // The Enum class declares a "final" finalize() method to prevent subclasses from - // introducing a finalizer. We don't want to set the finalizable flag for Enum or its - // subclasses, so we exclude it here. - // We also want to avoid setting the flag on Object, where we know that finalize() is - // empty. - if (klass_descriptor != "Ljava/lang/Object;" && - klass_descriptor != "Ljava/lang/Enum;") { - klass->SetFinalizable(); - } } } } } else if (method_name[0] == '<') { // Fix broken access flags for initializers. Bug 11157540. - bool is_init = (method_name == ""); - bool is_clinit = !is_init && (method_name == ""); + bool is_init = (strcmp("", method_name) == 0); + bool is_clinit = !is_init && (strcmp("", method_name) == 0); if (UNLIKELY(!is_init && !is_clinit)) { LOG(WARNING) << "Unexpected '<' at start of method name " << method_name; } else { @@ -2265,7 +2261,8 @@ bool ClassLinker::RemoveClass(const char* descriptor, const mirror::ClassLoader* ++it) { mirror::Class* klass = it->second; kh.ChangeClass(klass); - if (kh.GetDescriptorAsStringPiece() == descriptor && klass->GetClassLoader() == class_loader) { + if ((klass->GetClassLoader() == class_loader) && + (strcmp(descriptor, kh.GetDescriptor()) == 0)) { class_table_.erase(it); return true; } @@ -2311,14 +2308,15 @@ mirror::Class* ClassLinker::LookupClassFromTableLocked(const char* descriptor, for (auto it = class_table_.lower_bound(hash); it != end && it->first == hash; ++it) { mirror::Class* klass = it->second; kh.ChangeClass(klass); - if (klass->GetClassLoader() == class_loader && kh.GetDescriptorAsStringPiece() == descriptor) { + if ((klass->GetClassLoader() == class_loader) && + (strcmp(descriptor, kh.GetDescriptor()) == 0)) { if (kIsDebugBuild) { // Check for duplicates in the table. for (++it; it != end && it->first == hash; ++it) { mirror::Class* klass2 = it->second; kh.ChangeClass(klass2); - CHECK(!(kh.GetDescriptorAsStringPiece() == descriptor && - klass2->GetClassLoader() == class_loader)) + CHECK(!((klass2->GetClassLoader() == class_loader) && + (strcmp(descriptor, kh.GetDescriptor()) == 0))) << PrettyClass(klass) << " " << klass << " " << klass->GetClassLoader() << " " << PrettyClass(klass2) << " " << klass2 << " " << klass2->GetClassLoader(); } @@ -2426,7 +2424,7 @@ void ClassLinker::LookupClasses(const char* descriptor, std::vectorfirst == hash; ++it) { mirror::Class* klass = it->second; kh.ChangeClass(klass); - if (kh.GetDescriptorAsStringPiece() == descriptor) { + if (strcmp(descriptor, kh.GetDescriptor()) == 0) { result.push_back(klass); } } @@ -3885,7 +3883,7 @@ bool ClassLinker::LinkFields(SirtRef& klass, bool is_static) { // We lie to the GC about the java.lang.ref.Reference.referent field, so it doesn't scan it. if (!is_static && - (ClassHelper(klass.get(), this).GetDescriptorAsStringPiece() == "Ljava/lang/ref/Reference;")) { + (strcmp("Ljava/lang/ref/Reference;", ClassHelper(klass.get(), this).GetDescriptor()) == 0)) { // We know there are no non-reference fields in the Reference classes, and we know // that 'referent' is alphabetically last, so this is easy... CHECK_EQ(num_reference_fields, num_fields); @@ -3910,8 +3908,8 @@ bool ClassLinker::LinkFields(SirtRef& klass, bool is_static) { fh.ChangeField(field); Primitive::Type type = fh.GetTypeAsPrimitiveType(); bool is_primitive = type != Primitive::kPrimNot; - if (ClassHelper(klass.get(), this).GetDescriptorAsStringPiece() == "Ljava/lang/ref/Reference;" && - fh.GetNameAsStringPiece() == "referent") { + if ((strcmp("Ljava/lang/ref/Reference;", ClassHelper(klass.get(), this).GetDescriptor()) == 0) + && (strcmp("referent", fh.GetName()) == 0)) { is_primitive = true; // We lied above, so we have to expect a lie here. } if (is_primitive) { @@ -4006,9 +4004,8 @@ mirror::String* ClassLinker::ResolveString(const DexFile& dex_file, if (resolved != NULL) { return resolved; } - const DexFile::StringId& string_id = dex_file.GetStringId(string_idx); - int32_t utf16_length = dex_file.GetStringLength(string_id); - const char* utf8_data = dex_file.GetStringData(string_id); + uint32_t utf16_length; + const char* utf8_data = dex_file.StringDataAndUtf16LengthByIdx(string_idx, &utf16_length); mirror::String* string = intern_table_->InternStrong(utf16_length, utf8_data); dex_cache->SetResolvedString(string_idx, string); return string; @@ -4249,8 +4246,8 @@ mirror::ArtField* ClassLinker::ResolveFieldJLS(const DexFile& dex_file, return NULL; } - StringPiece name(dex_file.StringDataAsStringPieceByIdx(field_id.name_idx_)); - StringPiece type(dex_file.StringDataAsStringPieceByIdx( + StringPiece name(dex_file.StringDataByIdx(field_id.name_idx_)); + StringPiece type(dex_file.StringDataByIdx( dex_file.GetTypeId(field_id.type_idx_).descriptor_idx_)); resolved = klass->FindField(name, type); if (resolved != NULL) { diff --git a/runtime/debugger.cc b/runtime/debugger.cc index 57bd57e61f9..4b30a15dbfc 100644 --- a/runtime/debugger.cc +++ b/runtime/debugger.cc @@ -891,7 +891,7 @@ JDWP::JdwpError Dbg::GetClassInfo(JDWP::RefTypeId class_id, JDWP::JdwpTypeTag* p } if (pDescriptor != NULL) { - *pDescriptor = ClassHelper(c).GetDescriptorAsStringPiece().as_string(); + *pDescriptor = ClassHelper(c).GetDescriptor(); } return JDWP::ERR_NONE; } @@ -934,7 +934,7 @@ JDWP::JdwpError Dbg::GetSignature(JDWP::RefTypeId class_id, std::string* signatu if (c == NULL) { return status; } - *signature = ClassHelper(c).GetDescriptorAsStringPiece().as_string(); + *signature = ClassHelper(c).GetDescriptor(); return JDWP::ERR_NONE; } @@ -2289,7 +2289,7 @@ void Dbg::PostClassPrepare(mirror::Class* c) { int state = JDWP::CS_VERIFIED | JDWP::CS_PREPARED; JDWP::JdwpTypeTag tag = c->IsInterface() ? JDWP::TT_INTERFACE : JDWP::TT_CLASS; gJdwpState->PostClassPrepare(tag, gRegistry->Add(c), - ClassHelper(c).GetDescriptorAsStringPiece().as_string(), state); + ClassHelper(c).GetDescriptor(), state); } void Dbg::UpdateDebugger(Thread* thread, mirror::Object* this_object, diff --git a/runtime/dex_file-inl.h b/runtime/dex_file-inl.h index c57a1e7582b..3b2135c2cda 100644 --- a/runtime/dex_file-inl.h +++ b/runtime/dex_file-inl.h @@ -18,7 +18,6 @@ #define ART_RUNTIME_DEX_FILE_INL_H_ #include "base/logging.h" -#include "base/stringpiece.h" #include "dex_file.h" #include "leb128.h" #include "utils.h" @@ -30,23 +29,14 @@ inline int32_t DexFile::GetStringLength(const StringId& string_id) const { return DecodeUnsignedLeb128(&ptr); } -inline const char* DexFile::GetStringDataAndLength(const StringId& string_id, uint32_t* length) const { - DCHECK(length != NULL) << GetLocation(); +inline const char* DexFile::GetStringDataAndUtf16Length(const StringId& string_id, + uint32_t* utf16_length) const { + DCHECK(utf16_length != NULL) << GetLocation(); const byte* ptr = begin_ + string_id.string_data_off_; - *length = DecodeUnsignedLeb128(&ptr); + *utf16_length = DecodeUnsignedLeb128(&ptr); return reinterpret_cast(ptr); } -inline StringPiece DexFile::StringDataAsStringPieceByIdx(uint32_t idx) const { - if (idx == kDexNoIndex) { - return StringPiece(); - } - const StringId& string_id = GetStringId(idx); - uint32_t length; - const char* data = GetStringDataAndLength(string_id, &length); - return StringPiece(data, static_cast(length)); -} - inline const Signature DexFile::GetMethodSignature(const MethodId& method_id) const { return Signature(this, GetProtoId(method_id.proto_idx_)); } @@ -57,6 +47,71 @@ inline const DexFile::TryItem* DexFile::GetTryItems(const CodeItem& code_item, u (RoundUp(reinterpret_cast(insns_end_), 4)) + offset; } +static inline bool DexFileStringEquals(const DexFile* df1, uint32_t sidx1, + const DexFile* df2, uint32_t sidx2) { + uint32_t s1_len; // Note: utf16 length != mutf8 length. + const char* s1_data = df1->StringDataAndUtf16LengthByIdx(sidx1, &s1_len); + uint32_t s2_len; + const char* s2_data = df2->StringDataAndUtf16LengthByIdx(sidx2, &s2_len); + return (s1_len == s2_len) && (strcmp(s1_data, s2_data) == 0); +} + +inline bool Signature::operator==(const Signature& rhs) const { + if (dex_file_ == nullptr) { + return rhs.dex_file_ == nullptr; + } + if (rhs.dex_file_ == nullptr) { + return false; + } + if (dex_file_ == rhs.dex_file_) { + return proto_id_ == rhs.proto_id_; + } + uint32_t lhs_shorty_len; // For a shorty utf16 length == mutf8 length. + const char* lhs_shorty_data = dex_file_->StringDataAndUtf16LengthByIdx(proto_id_->shorty_idx_, + &lhs_shorty_len); + StringPiece lhs_shorty(lhs_shorty_data, lhs_shorty_len); + { + uint32_t rhs_shorty_len; + const char* rhs_shorty_data = + rhs.dex_file_->StringDataAndUtf16LengthByIdx(rhs.proto_id_->shorty_idx_, + &rhs_shorty_len); + StringPiece rhs_shorty(rhs_shorty_data, rhs_shorty_len); + if (lhs_shorty != rhs_shorty) { + return false; // Shorty mismatch. + } + } + if (lhs_shorty[0] == 'L') { + const DexFile::TypeId& return_type_id = dex_file_->GetTypeId(proto_id_->return_type_idx_); + const DexFile::TypeId& rhs_return_type_id = + rhs.dex_file_->GetTypeId(rhs.proto_id_->return_type_idx_); + if (!DexFileStringEquals(dex_file_, return_type_id.descriptor_idx_, + rhs.dex_file_, rhs_return_type_id.descriptor_idx_)) { + return false; // Return type mismatch. + } + } + if (lhs_shorty.find('L', 1) != StringPiece::npos) { + const DexFile::TypeList* params = dex_file_->GetProtoParameters(*proto_id_); + const DexFile::TypeList* rhs_params = rhs.dex_file_->GetProtoParameters(*rhs.proto_id_); + // Both lists are empty or have contents, or else shorty is broken. + DCHECK_EQ(params == nullptr, rhs_params == nullptr); + if (params != nullptr) { + uint32_t params_size = params->Size(); + DCHECK_EQ(params_size, rhs_params->Size()); // Parameter list size must match. + for (uint32_t i = 0; i < params_size; ++i) { + const DexFile::TypeId& param_id = dex_file_->GetTypeId(params->GetTypeItem(i).type_idx_); + const DexFile::TypeId& rhs_param_id = + rhs.dex_file_->GetTypeId(rhs_params->GetTypeItem(i).type_idx_); + if (!DexFileStringEquals(dex_file_, param_id.descriptor_idx_, + rhs.dex_file_, rhs_param_id.descriptor_idx_)) { + return false; // Parameter type mismatch. + } + } + } + } + return true; +} + + } // namespace art #endif // ART_RUNTIME_DEX_FILE_INL_H_ diff --git a/runtime/dex_file.cc b/runtime/dex_file.cc index a0f5601fff2..d3bb483b740 100644 --- a/runtime/dex_file.cc +++ b/runtime/dex_file.cc @@ -459,7 +459,7 @@ const DexFile::StringId* DexFile::FindStringId(const char* string) const { int32_t mid = (hi + lo) / 2; uint32_t length; const DexFile::StringId& str_id = GetStringId(mid); - const char* str = GetStringDataAndLength(str_id, &length); + const char* str = GetStringDataAndUtf16Length(str_id, &length); int compare = CompareModifiedUtf8ToModifiedUtf8AsUtf16CodePointValues(string, str); if (compare > 0) { lo = mid + 1; @@ -479,7 +479,7 @@ const DexFile::StringId* DexFile::FindStringId(const uint16_t* string) const { int32_t mid = (hi + lo) / 2; uint32_t length; const DexFile::StringId& str_id = GetStringId(mid); - const char* str = GetStringDataAndLength(str_id, &length); + const char* str = GetStringDataAndUtf16Length(str_id, &length); int compare = CompareModifiedUtf8ToUtf16AsCodePointValues(str, string); if (compare > 0) { lo = mid + 1; diff --git a/runtime/dex_file.h b/runtime/dex_file.h index 035a6910250..7901ea74473 100644 --- a/runtime/dex_file.h +++ b/runtime/dex_file.h @@ -418,29 +418,29 @@ class DexFile { int32_t GetStringLength(const StringId& string_id) const; - // Returns a pointer to the UTF-8 string data referred to by the given string_id. - const char* GetStringDataAndLength(const StringId& string_id, uint32_t* length) const; + // Returns a pointer to the UTF-8 string data referred to by the given string_id as well as the + // length of the string when decoded as a UTF-16 string. Note the UTF-16 length is not the same + // as the string length of the string data. + const char* GetStringDataAndUtf16Length(const StringId& string_id, uint32_t* utf16_length) const; const char* GetStringData(const StringId& string_id) const { - uint32_t length; - return GetStringDataAndLength(string_id, &length); + uint32_t ignored; + return GetStringDataAndUtf16Length(string_id, &ignored); } - // return the UTF-8 encoded string with the specified string_id index - const char* StringDataAndLengthByIdx(uint32_t idx, uint32_t* unicode_length) const { + // Index version of GetStringDataAndUtf16Length. + const char* StringDataAndUtf16LengthByIdx(uint32_t idx, uint32_t* utf16_length) const { if (idx == kDexNoIndex) { - *unicode_length = 0; + *utf16_length = 0; return NULL; } const StringId& string_id = GetStringId(idx); - return GetStringDataAndLength(string_id, unicode_length); + return GetStringDataAndUtf16Length(string_id, utf16_length); } - StringPiece StringDataAsStringPieceByIdx(uint32_t idx) const; - const char* StringDataByIdx(uint32_t idx) const { uint32_t unicode_length; - return StringDataAndLengthByIdx(idx, &unicode_length); + return StringDataAndUtf16LengthByIdx(idx, &unicode_length); } // Looks up a string id for a given modified utf8 string. @@ -472,7 +472,7 @@ class DexFile { // Get the descriptor string associated with a given type index. const char* StringByTypeIdx(uint32_t idx, uint32_t* unicode_length) const { const TypeId& type_id = GetTypeId(idx); - return StringDataAndLengthByIdx(type_id.descriptor_idx_, unicode_length); + return StringDataAndUtf16LengthByIdx(type_id.descriptor_idx_, unicode_length); } const char* StringByTypeIdx(uint32_t idx) const { @@ -575,7 +575,7 @@ class DexFile { return StringDataByIdx(GetProtoId(method_id.proto_idx_).shorty_idx_); } const char* GetMethodShorty(const MethodId& method_id, uint32_t* length) const { - return StringDataAndLengthByIdx(GetProtoId(method_id.proto_idx_).shorty_idx_, length); + return StringDataAndUtf16LengthByIdx(GetProtoId(method_id.proto_idx_).shorty_idx_, length); } // Returns the number of class definitions in the .dex file. size_t NumClassDefs() const { @@ -952,51 +952,7 @@ class Signature { return Signature(); } - bool operator==(const Signature& rhs) const { - if (dex_file_ == nullptr) { - return rhs.dex_file_ == nullptr; - } - if (rhs.dex_file_ == nullptr) { - return false; - } - if (dex_file_ == rhs.dex_file_) { - return proto_id_ == rhs.proto_id_; - } - StringPiece shorty(dex_file_->StringDataAsStringPieceByIdx(proto_id_->shorty_idx_)); - if (shorty != rhs.dex_file_->StringDataAsStringPieceByIdx(rhs.proto_id_->shorty_idx_)) { - return false; // Shorty mismatch. - } - if (shorty[0] == 'L') { - const DexFile::TypeId& return_type_id = dex_file_->GetTypeId(proto_id_->return_type_idx_); - const DexFile::TypeId& rhs_return_type_id = - rhs.dex_file_->GetTypeId(rhs.proto_id_->return_type_idx_); - if (dex_file_->StringDataAsStringPieceByIdx(return_type_id.descriptor_idx_) != - rhs.dex_file_->StringDataAsStringPieceByIdx(rhs_return_type_id.descriptor_idx_)) { - return false; // Return type mismatch. - } - } - if (shorty.find('L', 1) != StringPiece::npos) { - const DexFile::TypeList* params = dex_file_->GetProtoParameters(*proto_id_); - const DexFile::TypeList* rhs_params = rhs.dex_file_->GetProtoParameters(*rhs.proto_id_); - // Both lists are empty or have contents, or else shorty is broken. - DCHECK_EQ(params == nullptr, rhs_params == nullptr); - if (params != nullptr) { - uint32_t params_size = params->Size(); - DCHECK_EQ(params_size, rhs_params->Size()); // Parameter list size must match. - for (uint32_t i = 0; i < params_size; ++i) { - const DexFile::TypeId& param_id = dex_file_->GetTypeId(params->GetTypeItem(i).type_idx_); - const DexFile::TypeId& rhs_param_id = - rhs.dex_file_->GetTypeId(rhs_params->GetTypeItem(i).type_idx_); - if (dex_file_->StringDataAsStringPieceByIdx(param_id.descriptor_idx_) != - rhs.dex_file_->StringDataAsStringPieceByIdx(rhs_param_id.descriptor_idx_)) { - return false; // Parameter type mismatch. - } - } - } - } - return true; - } - + bool operator==(const Signature& rhs) const; bool operator!=(const Signature& rhs) const { return !(*this == rhs); } diff --git a/runtime/gc/heap-inl.h b/runtime/gc/heap-inl.h index b7ef77c35a2..873eadc46ae 100644 --- a/runtime/gc/heap-inl.h +++ b/runtime/gc/heap-inl.h @@ -127,7 +127,7 @@ inline bool Heap::TryAllocLargeObjectUninstrumented(Thread* self, mirror::Class* inline void Heap::DebugCheckPreconditionsForAllobObject(mirror::Class* c, size_t byte_count) { DCHECK(c == NULL || (c->IsClassClass() && byte_count >= sizeof(mirror::Class)) || (c->IsVariableSize() || c->GetObjectSize() == byte_count) || - ClassHelper(c).GetDescriptorAsStringPiece().length() == 0); + strlen(ClassHelper(c).GetDescriptor()) == 0); DCHECK_GE(byte_count, sizeof(mirror::Object)); } diff --git a/runtime/mirror/class.cc b/runtime/mirror/class.cc index 319ca4a5f93..f3cb54aa150 100644 --- a/runtime/mirror/class.cc +++ b/runtime/mirror/class.cc @@ -135,7 +135,7 @@ String* Class::ComputeName() { if (name != NULL) { return name; } - std::string descriptor(ClassHelper(this).GetDescriptorAsStringPiece().as_string()); + std::string descriptor(ClassHelper(this).GetDescriptor()); if ((descriptor[0] != 'L') && (descriptor[0] != '[')) { // The descriptor indicates that this is the class for // a primitive type; special-case the return value. @@ -294,8 +294,8 @@ bool Class::IsInSamePackage(const Class* that) const { return true; } // Compare the package part of the descriptor string. - return IsInSamePackage(ClassHelper(klass1).GetDescriptorAsStringPiece(), - ClassHelper(klass2).GetDescriptorAsStringPiece()); + return IsInSamePackage(ClassHelper(klass1).GetDescriptor(), + ClassHelper(klass2).GetDescriptor()); } bool Class::IsClassClass() const { @@ -366,7 +366,7 @@ ArtMethod* Class::FindDeclaredDirectMethod(const StringPiece& name, const String for (size_t i = 0; i < NumDirectMethods(); ++i) { ArtMethod* method = GetDirectMethod(i); mh.ChangeMethod(method); - if (name == mh.GetNameAsStringPiece() && mh.GetSignature() == signature) { + if (name == mh.GetName() && mh.GetSignature() == signature) { return method; } } @@ -378,7 +378,7 @@ ArtMethod* Class::FindDeclaredDirectMethod(const StringPiece& name, const Signat for (size_t i = 0; i < NumDirectMethods(); ++i) { ArtMethod* method = GetDirectMethod(i); mh.ChangeMethod(method); - if (name == mh.GetNameAsStringPiece() && signature == mh.GetSignature()) { + if (name == mh.GetName() && signature == mh.GetSignature()) { return method; } } @@ -432,7 +432,7 @@ ArtMethod* Class::FindDeclaredVirtualMethod(const StringPiece& name, const Strin for (size_t i = 0; i < NumVirtualMethods(); ++i) { ArtMethod* method = GetVirtualMethod(i); mh.ChangeMethod(method); - if (name == mh.GetNameAsStringPiece() && mh.GetSignature() == signature) { + if (name == mh.GetName() && mh.GetSignature() == signature) { return method; } } @@ -445,7 +445,7 @@ ArtMethod* Class::FindDeclaredVirtualMethod(const StringPiece& name, for (size_t i = 0; i < NumVirtualMethods(); ++i) { ArtMethod* method = GetVirtualMethod(i); mh.ChangeMethod(method); - if (name == mh.GetNameAsStringPiece() && signature == mh.GetSignature()) { + if (name == mh.GetName() && signature == mh.GetSignature()) { return method; } } @@ -517,7 +517,7 @@ ArtField* Class::FindDeclaredInstanceField(const StringPiece& name, const String for (size_t i = 0; i < NumInstanceFields(); ++i) { ArtField* f = GetInstanceField(i); fh.ChangeField(f); - if (name == fh.GetNameAsStringPiece() && type == fh.GetTypeDescriptorAsStringPiece()) { + if (name == fh.GetName() && type == fh.GetTypeDescriptor()) { return f; } } @@ -566,7 +566,7 @@ ArtField* Class::FindDeclaredStaticField(const StringPiece& name, const StringPi for (size_t i = 0; i < NumStaticFields(); ++i) { ArtField* f = GetStaticField(i); fh.ChangeField(f); - if (name == fh.GetNameAsStringPiece() && type == fh.GetTypeDescriptorAsStringPiece()) { + if (name == fh.GetName() && type == fh.GetTypeDescriptor()) { return f; } } diff --git a/runtime/native/dalvik_system_VMRuntime.cc b/runtime/native/dalvik_system_VMRuntime.cc index 4629dbd0aa8..71ed95c4e21 100644 --- a/runtime/native/dalvik_system_VMRuntime.cc +++ b/runtime/native/dalvik_system_VMRuntime.cc @@ -222,7 +222,7 @@ static void PreloadDexCachesResolveString(mirror::DexCache* dex_cache, } const DexFile* dex_file = dex_cache->GetDexFile(); uint32_t utf16Size; - const char* utf8 = dex_file->StringDataAndLengthByIdx(string_idx, &utf16Size); + const char* utf8 = dex_file->StringDataAndUtf16LengthByIdx(string_idx, &utf16Size); string = strings[utf8]; if (string == NULL) { return; diff --git a/runtime/object_utils.h b/runtime/object_utils.h index 3ca3c0bcf33..b0a4ce56fea 100644 --- a/runtime/object_utils.h +++ b/runtime/object_utils.h @@ -112,17 +112,6 @@ class ClassHelper { } } - StringPiece GetDescriptorAsStringPiece() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - CHECK(klass_ != NULL); - if (UNLIKELY(klass_->IsArrayClass() || klass_->IsPrimitive() || klass_->IsProxyClass())) { - return StringPiece(GetDescriptor()); - } else { - const DexFile& dex_file = GetDexFile(); - const DexFile::TypeId& type_id = dex_file.GetTypeId(GetClassDef()->class_idx_); - return dex_file.StringDataAsStringPieceByIdx(type_id.descriptor_idx_); - } - } - const char* GetArrayDescriptor() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { std::string result("["); const mirror::Class* saved_klass = klass_; @@ -194,7 +183,7 @@ class ClassHelper { } const char* GetSourceFile() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - std::string descriptor(GetDescriptorAsStringPiece().as_string()); + std::string descriptor(GetDescriptor()); const DexFile& dex_file = GetDexFile(); const DexFile::ClassDef* dex_class_def = GetClassDef(); CHECK(dex_class_def != NULL); @@ -291,16 +280,6 @@ class FieldHelper { return dex_file.GetFieldName(dex_file.GetFieldId(field_index)); } - StringPiece GetNameAsStringPiece() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - uint32_t field_index = field_->GetDexFieldIndex(); - if (UNLIKELY(field_->GetDeclaringClass()->IsProxyClass())) { - return StringPiece(GetName()); - } - const DexFile& dex_file = GetDexFile(); - const DexFile::FieldId& field_id = dex_file.GetFieldId(field_index); - return dex_file.StringDataAsStringPieceByIdx(field_id.name_idx_); - } - mirror::Class* GetType(bool resolve = true) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { uint32_t field_index = field_->GetDexFieldIndex(); if (UNLIKELY(field_->GetDeclaringClass()->IsProxyClass())) { @@ -329,17 +308,6 @@ class FieldHelper { return dex_file.GetFieldTypeDescriptor(field_id); } - StringPiece GetTypeDescriptorAsStringPiece() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - uint32_t field_index = field_->GetDexFieldIndex(); - if (UNLIKELY(field_->GetDeclaringClass()->IsProxyClass())) { - return StringPiece(GetTypeDescriptor()); - } - const DexFile& dex_file = GetDexFile(); - const DexFile::FieldId& field_id = dex_file.GetFieldId(field_index); - const DexFile::TypeId& type_id = dex_file.GetTypeId(field_id.type_idx_); - return dex_file.StringDataAsStringPieceByIdx(type_id.descriptor_idx_); - } - Primitive::Type GetTypeAsPrimitiveType() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { return Primitive::GetType(GetTypeDescriptor()[0]); @@ -365,7 +333,7 @@ class FieldHelper { DCHECK_LT(field_index, 2U); // 0 == Class[] interfaces; 1 == Class[][] throws; ClassHelper kh(field_->GetDeclaringClass()); - declaring_class_descriptor_ = kh.GetDescriptorAsStringPiece().as_string(); + declaring_class_descriptor_ = kh.GetDescriptor(); return declaring_class_descriptor_.c_str(); } const DexFile& dex_file = GetDexFile(); @@ -470,16 +438,6 @@ class MethodHelper { } } - StringPiece GetNameAsStringPiece() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - const DexFile& dex_file = GetDexFile(); - uint32_t dex_method_idx = method_->GetDexMethodIndex(); - if (UNLIKELY(dex_method_idx == DexFile::kDexNoIndex)) { - return StringPiece(GetName()); - } - const DexFile::MethodId& method_id = dex_file.GetMethodId(dex_method_idx); - return dex_file.StringDataAsStringPieceByIdx(method_id.name_idx_); - } - mirror::String* GetNameAsString() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { const DexFile& dex_file = GetDexFile(); uint32_t dex_method_idx = method_->GetDexMethodIndex(); @@ -559,18 +517,6 @@ class MethodHelper { return dex_file.GetMethodDeclaringClassDescriptor(dex_file.GetMethodId(dex_method_idx)); } - StringPiece GetDeclaringClassDescriptorAsStringPiece() - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - const DexFile& dex_file = GetDexFile(); - uint32_t dex_method_idx = method_->GetDexMethodIndex(); - if (UNLIKELY(dex_method_idx == DexFile::kDexNoIndex)) { - return StringPiece(""); - } - const DexFile::MethodId& mid = dex_file.GetMethodId(dex_method_idx); - const DexFile::TypeId& type_id = dex_file.GetTypeId(mid.class_idx_); - return dex_file.StringDataAsStringPieceByIdx(type_id.descriptor_idx_); - } - const char* GetDeclaringClassSourceFile() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { return ClassHelper(method_->GetDeclaringClass()).GetSourceFile(); } @@ -635,8 +581,8 @@ class MethodHelper { const DexFile& other_dex_file = other->GetDexFile(); const DexFile::MethodId& other_mid = other_dex_file.GetMethodId(other->method_->GetDexMethodIndex()); - if (dex_file.StringDataAsStringPieceByIdx(mid.name_idx_) != - other_dex_file.StringDataAsStringPieceByIdx(other_mid.name_idx_)) { + if (!DexFileStringEquals(&dex_file, mid.name_idx_, + &other_dex_file, other_mid.name_idx_)) { return false; // Name mismatch. } return dex_file.GetMethodSignature(mid) == other_dex_file.GetMethodSignature(other_mid); diff --git a/runtime/reflection.cc b/runtime/reflection.cc index 4ff73498336..80e16aa6c06 100644 --- a/runtime/reflection.cc +++ b/runtime/reflection.cc @@ -323,7 +323,7 @@ static bool UnboxPrimitive(const ThrowLocation* throw_location, mirror::Object* } JValue boxed_value; - const StringPiece src_descriptor(ClassHelper(o->GetClass()).GetDescriptorAsStringPiece()); + const StringPiece src_descriptor(ClassHelper(o->GetClass()).GetDescriptor()); mirror::Class* src_class = NULL; ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); mirror::ArtField* primitive_field = o->GetClass()->GetIFields()->Get(0); diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc index 7d2ee19572e..563c8438e1a 100644 --- a/runtime/verifier/method_verifier.cc +++ b/runtime/verifier/method_verifier.cc @@ -90,7 +90,7 @@ MethodVerifier::FailureKind MethodVerifier::VerifyClass(const mirror::Class* kla return kNoFailure; } mirror::Class* super = klass->GetSuperClass(); - if (super == NULL && ClassHelper(klass).GetDescriptorAsStringPiece() != "Ljava/lang/Object;") { + if (super == NULL && strcmp("Ljava/lang/Object;", ClassHelper(klass).GetDescriptor()) != 0) { *error = "Verifier rejected class "; *error += PrettyDescriptor(klass); *error += " that has no super class"; @@ -2188,7 +2188,7 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { if (called_method == NULL) { uint32_t method_idx = (is_range) ? inst->VRegB_3rc() : inst->VRegB_35c(); const DexFile::MethodId& method_id = dex_file_->GetMethodId(method_idx); - is_constructor = dex_file_->StringDataAsStringPieceByIdx(method_id.name_idx_) == ""; + is_constructor = strcmp("", dex_file_->StringDataByIdx(method_id.name_idx_)) == 0; uint32_t return_type_idx = dex_file_->GetProtoId(method_id.proto_idx_).return_type_idx_; return_type_descriptor = dex_file_->StringByTypeIdx(return_type_idx); } else { From 9728f91a63016136261231ff5213bde703bd27b6 Mon Sep 17 00:00:00 2001 From: Mathieu Chartier Date: Wed, 30 Oct 2013 09:45:13 -0700 Subject: [PATCH 0122/2402] Add missing null check in monitor install. Fixes broken OTA. Change-Id: I3a89e4755dc6b23d214ec542df3d167e788b904d --- runtime/monitor.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/monitor.cc b/runtime/monitor.cc index b1bf84f4983..aa47bdac5a3 100644 --- a/runtime/monitor.cc +++ b/runtime/monitor.cc @@ -123,7 +123,7 @@ bool Monitor::Install(Thread* self) { // Publish the updated lock word, which may race with other threads. bool success = obj_->CasLockWord(lw, fat); // Lock profiling. - if (success && lock_profiling_threshold_ != 0) { + if (success && owner_ != nullptr && lock_profiling_threshold_ != 0) { locking_method_ = owner_->GetCurrentMethod(&locking_dex_pc_); } return success; From a52454455048d04d12e4da637a103412a55e579b Mon Sep 17 00:00:00 2001 From: Jeff Hao Date: Wed, 30 Oct 2013 14:12:12 -0700 Subject: [PATCH 0123/2402] Update compiler blacklist to include java.net.NetworkInterface. Change-Id: Ib93d2f75024b3def54eadc11547990150deccf4d --- compiler/driver/compiler_driver.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc index 9f06755307d..783c3227a69 100644 --- a/compiler/driver/compiler_driver.cc +++ b/compiler/driver/compiler_driver.cc @@ -1987,6 +1987,7 @@ static const char* class_initializer_black_list[] = { "Ljava/net/Inet4Address;", // Sub-class of InetAddress. "Ljava/net/Inet6Address;", // Sub-class of InetAddress. "Ljava/net/InetUnixAddress;", // Sub-class of InetAddress. + "Ljava/net/NetworkInterface;", // Calls to Random. -> System.currentTimeMillis -> OsConstants.initConstants. "Ljava/nio/charset/Charset;", // Calls Charset.getDefaultCharset -> System.getProperty -> OsConstants.initConstants. "Ljava/nio/charset/CharsetICU;", // Sub-class of Charset. "Ljava/nio/charset/Charsets;", // Calls Charset.forName. From 4cad324424124998aee164b7bd7e6965c39a5c00 Mon Sep 17 00:00:00 2001 From: Jeff Hao Date: Wed, 30 Oct 2013 14:12:12 -0700 Subject: [PATCH 0124/2402] Update compiler blacklist to include java.net.NetworkInterface. (cherry picked from commit a52454455048d04d12e4da637a103412a55e579b) Change-Id: I65771b8379bccee93d689f6e1d94509e15ec13cb --- compiler/driver/compiler_driver.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc index bea94a74dfa..b876724f213 100644 --- a/compiler/driver/compiler_driver.cc +++ b/compiler/driver/compiler_driver.cc @@ -1980,6 +1980,7 @@ static const char* class_initializer_black_list[] = { "Ljava/net/Inet4Address;", // Sub-class of InetAddress. "Ljava/net/Inet6Address;", // Sub-class of InetAddress. "Ljava/net/InetUnixAddress;", // Sub-class of InetAddress. + "Ljava/net/NetworkInterface;", // Calls to Random. -> System.currentTimeMillis -> OsConstants.initConstants. "Ljava/nio/charset/Charset;", // Calls Charset.getDefaultCharset -> System.getProperty -> OsConstants.initConstants. "Ljava/nio/charset/CharsetICU;", // Sub-class of Charset. "Ljava/nio/charset/Charsets;", // Calls Charset.forName. From 0b1191cfece83f6f8d4101575a06555a2d13387a Mon Sep 17 00:00:00 2001 From: Bill Buzbee Date: Mon, 28 Oct 2013 22:11:59 +0000 Subject: [PATCH 0125/2402] Revert "Revert "Null check elimination improvement"" This reverts commit 31aa97cfec5ee76b2f2496464e1b6f9e11d21a29. ..and thereby brings back change 380165, which was reverted because it was buggy. Three problems with the original CL: 1. The author ran the pre-submit tests, but used -j24 and failed to search the output for fail messages. 2. The new null check analysis pass uses an interative approach to identify whether a null check is needed. It is possible that the null-check-required state may oscillate, and a logic error caused it to stick in the "no check needed" state. 3. Our old nemesis Dalvik untyped constants, in which 0 values can be used both as object reference and non-object references. This CL conservatively treats all CONST definitions as potential object definitions for the purposes of null check elimination. Change-Id: I3c1744e44318276e42989502a314585e56ac57a0 --- compiler/dex/mir_dataflow.cc | 3 +- compiler/dex/mir_graph.h | 2 +- compiler/dex/mir_optimization.cc | 94 +++++++++++++++++++----------- compiler/dex/quick/codegen_util.cc | 3 +- compiler/dex/ssa_transformation.cc | 1 + 5 files changed, 67 insertions(+), 36 deletions(-) diff --git a/compiler/dex/mir_dataflow.cc b/compiler/dex/mir_dataflow.cc index 9c8ce23ca56..11e19dc43f1 100644 --- a/compiler/dex/mir_dataflow.cc +++ b/compiler/dex/mir_dataflow.cc @@ -1243,7 +1243,8 @@ bool MIRGraph::CountUses(struct BasicBlock* bb) { if (mir->ssa_rep == NULL) { continue; } - uint32_t weight = std::min(16U, static_cast(bb->nesting_depth)); + // Each level of nesting adds *16 to count, up to 3 levels deep. + uint32_t weight = std::min(3U, static_cast(bb->nesting_depth) * 4); for (int i = 0; i < mir->ssa_rep->num_uses; i++) { int s_reg = mir->ssa_rep->uses[i]; raw_use_counts_.Increment(s_reg); diff --git a/compiler/dex/mir_graph.h b/compiler/dex/mir_graph.h index 8dda7c4c7ea..a69dde0da38 100644 --- a/compiler/dex/mir_graph.h +++ b/compiler/dex/mir_graph.h @@ -149,7 +149,7 @@ enum DataFlowAttributePos { #define DF_C_IS_REG (DF_UC) #define DF_IS_GETTER_OR_SETTER (DF_IS_GETTER | DF_IS_SETTER) #define DF_USES_FP (DF_FP_A | DF_FP_B | DF_FP_C) - +#define DF_NULL_TRANSFER (DF_NULL_TRANSFER_0 | DF_NULL_TRANSFER_N) enum OatMethodAttributes { kIsLeaf, // Method is leaf. kHasLoop, // Method contains simple loop. diff --git a/compiler/dex/mir_optimization.cc b/compiler/dex/mir_optimization.cc index 3cd158ffc06..f5913a5ad4c 100644 --- a/compiler/dex/mir_optimization.cc +++ b/compiler/dex/mir_optimization.cc @@ -629,10 +629,15 @@ bool MIRGraph::EliminateNullChecks(struct BasicBlock* bb) { */ if ((bb->block_type == kEntryBlock) | bb->catch_entry) { temp_ssa_register_v_->ClearAllBits(); + // Assume all ins are objects. + for (uint16_t in_reg = cu_->num_dalvik_registers - cu_->num_ins; + in_reg < cu_->num_dalvik_registers; in_reg++) { + temp_ssa_register_v_->SetBit(in_reg); + } if ((cu_->access_flags & kAccStatic) == 0) { // If non-static method, mark "this" as non-null int this_reg = cu_->num_dalvik_registers - cu_->num_ins; - temp_ssa_register_v_->SetBit(this_reg); + temp_ssa_register_v_->ClearBit(this_reg); } } else if (bb->predecessors->Size() == 1) { BasicBlock* pred_bb = GetBasicBlock(bb->predecessors->Get(0)); @@ -645,18 +650,18 @@ bool MIRGraph::EliminateNullChecks(struct BasicBlock* bb) { if (pred_bb->fall_through == bb->id) { // The fall-through of a block following a IF_EQZ, set the vA of the IF_EQZ to show that // it can't be null. - temp_ssa_register_v_->SetBit(last_insn->ssa_rep->uses[0]); + temp_ssa_register_v_->ClearBit(last_insn->ssa_rep->uses[0]); } } else if (last_opcode == Instruction::IF_NEZ) { if (pred_bb->taken == bb->id) { // The taken block following a IF_NEZ, set the vA of the IF_NEZ to show that it can't be // null. - temp_ssa_register_v_->SetBit(last_insn->ssa_rep->uses[0]); + temp_ssa_register_v_->ClearBit(last_insn->ssa_rep->uses[0]); } } } } else { - // Starting state is intersection of all incoming arcs + // Starting state is union of all incoming arcs GrowableArray::Iterator iter(bb->predecessors); BasicBlock* pred_bb = GetBasicBlock(iter.Next()); DCHECK(pred_bb != NULL); @@ -668,10 +673,13 @@ bool MIRGraph::EliminateNullChecks(struct BasicBlock* bb) { (pred_bb->data_flow_info->ending_null_check_v == NULL)) { continue; } - temp_ssa_register_v_->Intersect(pred_bb->data_flow_info->ending_null_check_v); + temp_ssa_register_v_->Union(pred_bb->data_flow_info->ending_null_check_v); } } + // At this point, temp_ssa_register_v_ shows which sregs have an object definition with + // no intervening uses. + // Walk through the instruction in the block, updating as necessary for (MIR* mir = bb->first_mir_insn; mir != NULL; mir = mir->next) { if (mir->ssa_rep == NULL) { @@ -679,11 +687,49 @@ bool MIRGraph::EliminateNullChecks(struct BasicBlock* bb) { } int df_attributes = oat_data_flow_attributes_[mir->dalvikInsn.opcode]; - // Mark target of NEW* as non-null - if (df_attributes & DF_NON_NULL_DST) { + // Might need a null check? + if (df_attributes & DF_HAS_NULL_CHKS) { + int src_idx; + if (df_attributes & DF_NULL_CHK_1) { + src_idx = 1; + } else if (df_attributes & DF_NULL_CHK_2) { + src_idx = 2; + } else { + src_idx = 0; + } + int src_sreg = mir->ssa_rep->uses[src_idx]; + if (!temp_ssa_register_v_->IsBitSet(src_sreg)) { + // Eliminate the null check. + mir->optimization_flags |= MIR_IGNORE_NULL_CHECK; + } else { + // Do the null check. + mir->optimization_flags &= ~MIR_IGNORE_NULL_CHECK; + // Mark s_reg as null-checked + temp_ssa_register_v_->ClearBit(src_sreg); + } + } + + if ((df_attributes & DF_A_WIDE) || + (df_attributes & (DF_REF_A | DF_SETS_CONST | DF_NULL_TRANSFER)) == 0) { + continue; + } + + /* + * First, mark all object definitions as requiring null check. + * Note: we can't tell if a CONST definition might be used as an object, so treat + * them all as object definitions. + */ + if (((df_attributes & (DF_DA | DF_REF_A)) == (DF_DA | DF_REF_A)) || + (df_attributes & DF_SETS_CONST)) { temp_ssa_register_v_->SetBit(mir->ssa_rep->defs[0]); } + // Now, remove mark from all object definitions we know are non-null. + if (df_attributes & DF_NON_NULL_DST) { + // Mark target of NEW* as non-null + temp_ssa_register_v_->ClearBit(mir->ssa_rep->defs[0]); + } + // Mark non-null returns from invoke-style NEW* if (df_attributes & DF_NON_NULL_RET) { MIR* next_mir = mir->next; @@ -691,7 +737,7 @@ bool MIRGraph::EliminateNullChecks(struct BasicBlock* bb) { if (next_mir && next_mir->dalvikInsn.opcode == Instruction::MOVE_RESULT_OBJECT) { // Mark as null checked - temp_ssa_register_v_->SetBit(next_mir->ssa_rep->defs[0]); + temp_ssa_register_v_->ClearBit(next_mir->ssa_rep->defs[0]); } else { if (next_mir) { LOG(WARNING) << "Unexpected opcode following new: " << next_mir->dalvikInsn.opcode; @@ -706,7 +752,7 @@ bool MIRGraph::EliminateNullChecks(struct BasicBlock* bb) { // First non-pseudo should be MOVE_RESULT_OBJECT if (tmir->dalvikInsn.opcode == Instruction::MOVE_RESULT_OBJECT) { // Mark as null checked - temp_ssa_register_v_->SetBit(tmir->ssa_rep->defs[0]); + temp_ssa_register_v_->ClearBit(tmir->ssa_rep->defs[0]); } else { LOG(WARNING) << "Unexpected op after new: " << tmir->dalvikInsn.opcode; } @@ -719,40 +765,22 @@ bool MIRGraph::EliminateNullChecks(struct BasicBlock* bb) { /* * Propagate nullcheck state on register copies (including * Phi pseudo copies. For the latter, nullcheck state is - * the "and" of all the Phi's operands. + * the "or" of all the Phi's operands. */ if (df_attributes & (DF_NULL_TRANSFER_0 | DF_NULL_TRANSFER_N)) { int tgt_sreg = mir->ssa_rep->defs[0]; int operands = (df_attributes & DF_NULL_TRANSFER_0) ? 1 : mir->ssa_rep->num_uses; - bool null_checked = true; + bool needs_null_check = false; for (int i = 0; i < operands; i++) { - null_checked &= temp_ssa_register_v_->IsBitSet(mir->ssa_rep->uses[i]); + needs_null_check |= temp_ssa_register_v_->IsBitSet(mir->ssa_rep->uses[i]); } - if (null_checked) { + if (needs_null_check) { temp_ssa_register_v_->SetBit(tgt_sreg); - } - } - - // Already nullchecked? - if ((df_attributes & DF_HAS_NULL_CHKS) && !(mir->optimization_flags & MIR_IGNORE_NULL_CHECK)) { - int src_idx; - if (df_attributes & DF_NULL_CHK_1) { - src_idx = 1; - } else if (df_attributes & DF_NULL_CHK_2) { - src_idx = 2; } else { - src_idx = 0; + temp_ssa_register_v_->ClearBit(tgt_sreg); } - int src_sreg = mir->ssa_rep->uses[src_idx]; - if (temp_ssa_register_v_->IsBitSet(src_sreg)) { - // Eliminate the null check - mir->optimization_flags |= MIR_IGNORE_NULL_CHECK; - } else { - // Mark s_reg as null-checked - temp_ssa_register_v_->SetBit(src_sreg); - } - } + } } // Did anything change? diff --git a/compiler/dex/quick/codegen_util.cc b/compiler/dex/quick/codegen_util.cc index a6653fab198..dfbc8872990 100644 --- a/compiler/dex/quick/codegen_util.cc +++ b/compiler/dex/quick/codegen_util.cc @@ -164,7 +164,8 @@ void Mir2Lir::DumpLIRInsn(LIR* lir, unsigned char* base_addr) { lir->operands[0] = WrapPointer(ArenaStrdup("No instruction string")); } LOG(INFO) << "-------- dalvik offset: 0x" << std::hex - << lir->dalvik_offset << " @ " << reinterpret_cast(lir->operands[0]); + << lir->dalvik_offset << " @ " + << reinterpret_cast(UnwrapPointer(lir->operands[0])); break; case kPseudoExitBlock: LOG(INFO) << "-------- exit offset: 0x" << std::hex << dest; diff --git a/compiler/dex/ssa_transformation.cc b/compiler/dex/ssa_transformation.cc index b6c892212cd..0d8bd07f409 100644 --- a/compiler/dex/ssa_transformation.cc +++ b/compiler/dex/ssa_transformation.cc @@ -206,6 +206,7 @@ void MIRGraph::ComputeDomPostOrderTraversal(BasicBlock* bb) { /* hacky loop detection */ if ((curr_bb->taken != NullBasicBlockId) && curr_bb->dominators->IsBitSet(curr_bb->taken)) { + curr_bb->nesting_depth++; attributes_ |= METHOD_HAS_LOOP; } } From 2ec3f71bee23d97ec35c80222c1d073b87b42a0f Mon Sep 17 00:00:00 2001 From: Jeff Hao Date: Wed, 30 Oct 2013 15:13:17 -0700 Subject: [PATCH 0126/2402] Fix openDexFileNative to throw pending exception when it fails. Bug: 11391006 Change-Id: I2331d73a2ab8f70d46b1afb2649550c296e7393a --- runtime/native/dalvik_system_DexFile.cc | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/runtime/native/dalvik_system_DexFile.cc b/runtime/native/dalvik_system_DexFile.cc index 55a56d6c7a2..ab5eab39556 100644 --- a/runtime/native/dalvik_system_DexFile.cc +++ b/runtime/native/dalvik_system_DexFile.cc @@ -113,8 +113,7 @@ static jint DexFile_openDexFileNative(JNIEnv* env, jclass, jstring javaSourceNam outputName.c_str(), &error_msg); } if (dex_file == nullptr) { - ScopedObjectAccess soa(env); - ThrowIOException("%s", error_msg.c_str()); + CHECK_EQ(env->ExceptionCheck(), JNI_TRUE); return 0; } return static_cast(reinterpret_cast(dex_file)); From 635733da0f67df1c979cc9764409d2a9dcbb20aa Mon Sep 17 00:00:00 2001 From: Brian Carlstrom Date: Wed, 30 Oct 2013 23:19:31 -0700 Subject: [PATCH 0127/2402] Fix --compiler-backend usage example Change-Id: I2ebefe57e94d6a8103c71e86f7c6a302b7107fc4 --- dex2oat/dex2oat.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc index 1beb8622674..98c62cec663 100644 --- a/dex2oat/dex2oat.cc +++ b/dex2oat/dex2oat.cc @@ -133,7 +133,7 @@ static void Usage(const char* fmt, ...) { UsageError(""); UsageError(" --compiler-backend=(Quick|QuickGBC|Portable): select compiler backend"); UsageError(" set."); - UsageError(" Example: --instruction-set=Portable"); + UsageError(" Example: --compiler-backend=Portable"); UsageError(" Default: Quick"); UsageError(""); UsageError(" --host: used with Portable backend to link against host runtime libraries"); From 1eeba46ba66e462b58640b604b035a481d65d898 Mon Sep 17 00:00:00 2001 From: Baligh Uddin Date: Thu, 31 Oct 2013 18:16:26 +0000 Subject: [PATCH 0128/2402] Initial empty repository From cf5077ac14f0922b6104a8a03fd66d97a490a3dd Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Thu, 31 Oct 2013 12:37:54 -0700 Subject: [PATCH 0129/2402] Remove unused length from DexFile GetString calls. Address extra review comments from commit dfb325e0ddd746cd8f7c2e3723b3a573eb7cc111. Change-Id: If76e81e7af5870431901de0bf561e0f827435fe3 --- runtime/dex_file.cc | 6 ++---- runtime/dex_file.h | 1 + runtime/native/dalvik_system_VMRuntime.cc | 3 +-- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/runtime/dex_file.cc b/runtime/dex_file.cc index d3bb483b740..7e09a489be5 100644 --- a/runtime/dex_file.cc +++ b/runtime/dex_file.cc @@ -457,9 +457,8 @@ const DexFile::StringId* DexFile::FindStringId(const char* string) const { int32_t hi = NumStringIds() - 1; while (hi >= lo) { int32_t mid = (hi + lo) / 2; - uint32_t length; const DexFile::StringId& str_id = GetStringId(mid); - const char* str = GetStringDataAndUtf16Length(str_id, &length); + const char* str = GetStringData(str_id); int compare = CompareModifiedUtf8ToModifiedUtf8AsUtf16CodePointValues(string, str); if (compare > 0) { lo = mid + 1; @@ -477,9 +476,8 @@ const DexFile::StringId* DexFile::FindStringId(const uint16_t* string) const { int32_t hi = NumStringIds() - 1; while (hi >= lo) { int32_t mid = (hi + lo) / 2; - uint32_t length; const DexFile::StringId& str_id = GetStringId(mid); - const char* str = GetStringDataAndUtf16Length(str_id, &length); + const char* str = GetStringData(str_id); int compare = CompareModifiedUtf8ToUtf16AsCodePointValues(str, string); if (compare > 0) { lo = mid + 1; diff --git a/runtime/dex_file.h b/runtime/dex_file.h index 7901ea74473..a9c24e66c1e 100644 --- a/runtime/dex_file.h +++ b/runtime/dex_file.h @@ -575,6 +575,7 @@ class DexFile { return StringDataByIdx(GetProtoId(method_id.proto_idx_).shorty_idx_); } const char* GetMethodShorty(const MethodId& method_id, uint32_t* length) const { + // Using the UTF16 length is safe here as shorties are guaranteed to be ASCII characters. return StringDataAndUtf16LengthByIdx(GetProtoId(method_id.proto_idx_).shorty_idx_, length); } // Returns the number of class definitions in the .dex file. diff --git a/runtime/native/dalvik_system_VMRuntime.cc b/runtime/native/dalvik_system_VMRuntime.cc index 71ed95c4e21..aef000cf10d 100644 --- a/runtime/native/dalvik_system_VMRuntime.cc +++ b/runtime/native/dalvik_system_VMRuntime.cc @@ -221,8 +221,7 @@ static void PreloadDexCachesResolveString(mirror::DexCache* dex_cache, return; } const DexFile* dex_file = dex_cache->GetDexFile(); - uint32_t utf16Size; - const char* utf8 = dex_file->StringDataAndUtf16LengthByIdx(string_idx, &utf16Size); + const char* utf8 = dex_file->StringDataByIdx(string_idx); string = strings[utf8]; if (string == NULL) { return; From 4e6a31eb97f22f4480827474b30b9e64f396eace Mon Sep 17 00:00:00 2001 From: Mathieu Chartier Date: Thu, 31 Oct 2013 10:35:05 -0700 Subject: [PATCH 0130/2402] Lazily compute object identity hash codes. Before, we computed identity hashcodes whenever we inflated a monitor. This caused issues since it meant that we would have all of these hash codes in the image, causing locks to excessively inflate during application run time. This change makes it so that we lazily compute hash codes. When a thin lock gets inflated, we assign a hash code of 0 assigned to it. This value signifies no hash code. When we try to get the identity hash code of an object with an inflated monitor, it gets computed if it is 0. Change-Id: Iae6acd1960515a36e74644e5b1323ff336731806 --- compiler/image_writer.cc | 9 +++++++-- runtime/arch/arm/quick_entrypoints_arm.S | 2 +- runtime/lock_word-inl.h | 2 +- runtime/lock_word.h | 2 +- runtime/mirror/object.cc | 8 ++++---- runtime/mirror/object.h | 2 +- runtime/monitor.cc | 20 +++++++++++++++----- runtime/monitor.h | 15 +++++++++------ 8 files changed, 39 insertions(+), 21 deletions(-) diff --git a/compiler/image_writer.cc b/compiler/image_writer.cc index af60a3878a8..75be2c9c439 100644 --- a/compiler/image_writer.cc +++ b/compiler/image_writer.cc @@ -504,7 +504,11 @@ void ImageWriter::CopyAndFixupObjectsCallback(Object* object, void* arg) { Monitor* monitor = lw.FatLockMonitor(); CHECK(monitor != nullptr); CHECK(!monitor->IsLocked()); - copy->SetLockWord(LockWord::FromHashCode(monitor->GetHashCode())); + if (monitor->HasHashCode()) { + copy->SetLockWord(LockWord::FromHashCode(monitor->GetHashCode())); + } else { + copy->SetLockWord(LockWord()); + } break; } case LockWord::kThinLocked: { @@ -512,9 +516,10 @@ void ImageWriter::CopyAndFixupObjectsCallback(Object* object, void* arg) { break; } case LockWord::kUnlocked: - // Fall-through. + break; case LockWord::kHashCode: // Do nothing since we can just keep the same hash code. + CHECK_NE(lw.GetHashCode(), 0); break; default: LOG(FATAL) << "Unreachable."; diff --git a/runtime/arch/arm/quick_entrypoints_arm.S b/runtime/arch/arm/quick_entrypoints_arm.S index 50a51760aea..9a853d07aba 100644 --- a/runtime/arch/arm/quick_entrypoints_arm.S +++ b/runtime/arch/arm/quick_entrypoints_arm.S @@ -325,7 +325,7 @@ END art_quick_handle_fill_data ENTRY art_quick_lock_object cbz r0, slow_lock retry_lock: - ldrt r2, [r9, #THREAD_ID_OFFSET] + ldr r2, [r9, #THREAD_ID_OFFSET] ldrex r1, [r0, #LOCK_WORD_OFFSET] cbnz r1, not_unlocked @ already thin locked @ unlocked case - r2 holds thread id with count of 0 diff --git a/runtime/lock_word-inl.h b/runtime/lock_word-inl.h index 59947f56946..efd3d9d25e9 100644 --- a/runtime/lock_word-inl.h +++ b/runtime/lock_word-inl.h @@ -45,7 +45,7 @@ inline LockWord::LockWord(Monitor* mon) DCHECK_EQ(FatLockMonitor(), mon); } -inline uint32_t LockWord::GetHashCode() const { +inline int32_t LockWord::GetHashCode() const { DCHECK_EQ(GetState(), kHashCode); return (value_ >> kHashShift) & kHashMask; } diff --git a/runtime/lock_word.h b/runtime/lock_word.h index 9b6c64a1835..1882ae6504a 100644 --- a/runtime/lock_word.h +++ b/runtime/lock_word.h @@ -132,7 +132,7 @@ class LockWord { } // Return the hash code stored in the lock word, must be kHashCode state. - uint32_t GetHashCode() const; + int32_t GetHashCode() const; uint32_t GetValue() const { return value_; diff --git a/runtime/mirror/object.cc b/runtime/mirror/object.cc index 49bad4ccb8e..bd187c1835e 100644 --- a/runtime/mirror/object.cc +++ b/runtime/mirror/object.cc @@ -84,14 +84,14 @@ Object* Object::Clone(Thread* self) { return copy.get(); } -uint32_t Object::GenerateIdentityHashCode() { +int32_t Object::GenerateIdentityHashCode() { static AtomicInteger seed(987654321 + std::time(nullptr)); - uint32_t expected_value, new_value; + int32_t expected_value, new_value; do { expected_value = static_cast(seed.load()); new_value = expected_value * 1103515245 + 12345; - } while (!seed.compare_and_swap(static_cast(expected_value), - static_cast(new_value))); + } while ((expected_value & LockWord::kHashMask) == 0 || + !seed.compare_and_swap(expected_value, new_value)); return expected_value & LockWord::kHashMask; } diff --git a/runtime/mirror/object.h b/runtime/mirror/object.h index 11473cd3991..e8ea3f23759 100644 --- a/runtime/mirror/object.h +++ b/runtime/mirror/object.h @@ -249,7 +249,7 @@ class MANAGED Object { } // Generate an identity hash code. - static uint32_t GenerateIdentityHashCode(); + static int32_t GenerateIdentityHashCode(); // Write barrier called post update to a reference bearing field. static void WriteBarrierField(const Object* dst, MemberOffset offset, const Object* new_value); diff --git a/runtime/monitor.cc b/runtime/monitor.cc index aa47bdac5a3..2abfd3df417 100644 --- a/runtime/monitor.cc +++ b/runtime/monitor.cc @@ -79,7 +79,7 @@ void Monitor::Init(uint32_t lock_profiling_threshold, bool (*is_sensitive_thread is_sensitive_thread_hook_ = is_sensitive_thread_hook; } -Monitor::Monitor(Thread* owner, mirror::Object* obj, uint32_t hash_code) +Monitor::Monitor(Thread* owner, mirror::Object* obj, int32_t hash_code) : monitor_lock_("a monitor lock", kMonitorLock), monitor_contenders_("monitor contenders", monitor_lock_), owner_(owner), @@ -95,6 +95,16 @@ Monitor::Monitor(Thread* owner, mirror::Object* obj, uint32_t hash_code) // The identity hash code is set for the life time of the monitor. } +int32_t Monitor::GetHashCode() { + while (!HasHashCode()) { + if (hash_code_.compare_and_swap(0, mirror::Object::GenerateIdentityHashCode())) { + break; + } + } + DCHECK(HasHashCode()); + return hash_code_.load(); +} + bool Monitor::Install(Thread* self) { MutexLock mu(self, monitor_lock_); // Uncontended mutex acquisition as monitor isn't yet public. CHECK(owner_ == nullptr || owner_ == self || owner_->IsSuspended()); @@ -107,7 +117,7 @@ bool Monitor::Install(Thread* self) { break; } case LockWord::kHashCode: { - CHECK_EQ(hash_code_, lw.GetHashCode()); + CHECK_EQ(hash_code_, static_cast(lw.GetHashCode())); break; } case LockWord::kFatLocked: { @@ -622,7 +632,7 @@ void Monitor::MonitorEnter(Thread* self, mirror::Object* obj) { return; // Success! } else { // We'd overflow the recursion count, so inflate the monitor. - InflateThinLocked(self, obj, lock_word, mirror::Object::GenerateIdentityHashCode()); + InflateThinLocked(self, obj, lock_word, 0); } } else { // Contention. @@ -632,7 +642,7 @@ void Monitor::MonitorEnter(Thread* self, mirror::Object* obj) { NanoSleep(1000); // Sleep for 1us and re-attempt. } else { contention_count = 0; - InflateThinLocked(self, obj, lock_word, mirror::Object::GenerateIdentityHashCode()); + InflateThinLocked(self, obj, lock_word, 0); } } continue; // Start from the beginning. @@ -716,7 +726,7 @@ void Monitor::Wait(Thread* self, mirror::Object *obj, int64_t ms, int32_t ns, return; // Failure. } else { // We own the lock, inflate to enqueue ourself on the Monitor. - Inflate(self, self, obj, mirror::Object::GenerateIdentityHashCode()); + Inflate(self, self, obj, 0); lock_word = obj->GetLockWord(); } break; diff --git a/runtime/monitor.h b/runtime/monitor.h index c464400f467..09cfafa042e 100644 --- a/runtime/monitor.h +++ b/runtime/monitor.h @@ -24,6 +24,7 @@ #include #include +#include "atomic_integer.h" #include "base/mutex.h" #include "root_visitor.h" #include "thread_state.h" @@ -98,17 +99,19 @@ class Monitor { return owner_; } - int32_t GetHashCode() const { - return hash_code_; - } + int32_t GetHashCode(); bool IsLocked() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + bool HasHashCode() const { + return hash_code_.load() != 0; + } + static void InflateThinLocked(Thread* self, mirror::Object* obj, LockWord lock_word, uint32_t hash_code) NO_THREAD_SAFETY_ANALYSIS; private: - explicit Monitor(Thread* owner, mirror::Object* obj, uint32_t hash_code) + explicit Monitor(Thread* owner, mirror::Object* obj, int32_t hash_code) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); // Install the monitor into its object, may fail if another thread installs a different monitor @@ -179,8 +182,8 @@ class Monitor { // Threads currently waiting on this monitor. Thread* wait_set_ GUARDED_BY(monitor_lock_); - // Stored object hash code, always generated. - const uint32_t hash_code_; + // Stored object hash code, generated lazily by GetHashCode. + AtomicInteger hash_code_; // Method and dex pc where the lock owner acquired the lock, used when lock // sampling is enabled. locking_method_ may be null if the lock is currently From cdb4b715e77f427838b97da9bdb6654f561296ab Mon Sep 17 00:00:00 2001 From: Vladimir Marko Date: Fri, 1 Nov 2013 09:42:05 +0000 Subject: [PATCH 0131/2402] Fix intrinsic Long.reverseBytes(). Change-Id: I6cfab7e072f406439f0bde73f192149f0a6e58f7 --- compiler/dex/quick/gen_invoke.cc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/compiler/dex/quick/gen_invoke.cc b/compiler/dex/quick/gen_invoke.cc index b366fdd55d1..c7d0014b808 100644 --- a/compiler/dex/quick/gen_invoke.cc +++ b/compiler/dex/quick/gen_invoke.cc @@ -968,8 +968,10 @@ bool Mir2Lir::GenInlinedReverseBytes(CallInfo* info, OpSize size) { RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true); if (size == kLong) { RegLocation rl_i = LoadValueWide(rl_src_i, kCoreReg); + int reg_tmp = AllocTemp(); + OpRegCopy(reg_tmp, rl_result.low_reg); OpRegReg(kOpRev, rl_result.low_reg, rl_i.high_reg); - OpRegReg(kOpRev, rl_result.high_reg, rl_i.low_reg); + OpRegReg(kOpRev, rl_result.high_reg, reg_tmp); StoreValueWide(rl_dest, rl_result); } else { DCHECK(size == kWord || size == kSignedHalf); From cc8522f6b89e55a1c9abee181874d3ab29441466 Mon Sep 17 00:00:00 2001 From: Narayan Kamath Date: Mon, 28 Oct 2013 13:24:56 +0000 Subject: [PATCH 0132/2402] Remove usage of LOCAL_BUILD_HOST_DEX Change-Id: Id6745578e7ecc4899a52df5de2e81a915cdbb5e0 --- test/Android.mk | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/test/Android.mk b/test/Android.mk index da469d75d7c..881d664bb2a 100644 --- a/test/Android.mk +++ b/test/Android.mk @@ -84,10 +84,9 @@ define build-art-test-dex LOCAL_JAVA_LIBRARIES := $(HOST_CORE_JARS) LOCAL_NO_STANDARD_LIBRARIES := true LOCAL_DEX_PREOPT_IMAGE := $(HOST_CORE_IMG_OUT) - LOCAL_BUILD_HOST_DEX := true LOCAL_ADDITIONAL_DEPENDENCIES := art/build/Android.common.mk LOCAL_ADDITIONAL_DEPENDENCIES += $(LOCAL_PATH)/Android.mk - include $(BUILD_HOST_JAVA_LIBRARY) + include $(BUILD_DALVIK_HOST_JAVA_LIBRARY) ART_TEST_HOST_DEX_FILES += $$(LOCAL_MODULE_PATH)/$$(LOCAL_MODULE).jar endif endef From 65636e5de2375839e29e3e19ee7a7db737901cf0 Mon Sep 17 00:00:00 2001 From: Vladimir Marko Date: Thu, 24 Oct 2013 15:08:57 +0100 Subject: [PATCH 0133/2402] Add intrinsics for Memory peek/poke. Add intrinsics for single memory access (non-array) peek/poke methods in libcore.io.Memory. Change-Id: I5d66a5b14ea89875d8afb8252eb293f7d637b83f --- compiler/dex/quick/gen_invoke.cc | 59 ++++++++++++++++++++++++++++++++ compiler/dex/quick/mir_to_lir.h | 2 ++ 2 files changed, 61 insertions(+) diff --git a/compiler/dex/quick/gen_invoke.cc b/compiler/dex/quick/gen_invoke.cc index b366fdd55d1..abc8120d19e 100644 --- a/compiler/dex/quick/gen_invoke.cc +++ b/compiler/dex/quick/gen_invoke.cc @@ -1159,6 +1159,39 @@ bool Mir2Lir::GenInlinedCurrentThread(CallInfo* info) { return true; } +bool Mir2Lir::GenInlinedPeek(CallInfo* info, OpSize size) { + RegLocation rl_src_address = info->args[0]; // long address + rl_src_address.wide = 0; // ignore high half in info->args[1] + RegLocation rl_dest = InlineTarget(info); + RegLocation rl_address = LoadValue(rl_src_address, kCoreReg); + RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true); + if (size == kLong) { + LoadBaseDispWide(rl_address.low_reg, 0, rl_result.low_reg, rl_result.high_reg, INVALID_SREG); + StoreValueWide(rl_dest, rl_result); + } else { + DCHECK(size == kSignedByte || size == kSignedHalf || size == kWord); + LoadBaseDisp(rl_address.low_reg, 0, rl_result.low_reg, size, INVALID_SREG); + StoreValue(rl_dest, rl_result); + } + return true; +} + +bool Mir2Lir::GenInlinedPoke(CallInfo* info, OpSize size) { + RegLocation rl_src_address = info->args[0]; // long address + rl_src_address.wide = 0; // ignore high half in info->args[1] + RegLocation rl_src_value = info->args[2]; // [size] value + RegLocation rl_address = LoadValue(rl_src_address, kCoreReg); + if (size == kLong) { + RegLocation rl_value = LoadValueWide(rl_src_value, kCoreReg); + StoreBaseDispWide(rl_address.low_reg, 0, rl_value.low_reg, rl_value.high_reg); + } else { + DCHECK(size == kSignedByte || size == kSignedHalf || size == kWord); + RegLocation rl_value = LoadValue(rl_src_value, kCoreReg); + StoreBaseDisp(rl_address.low_reg, 0, rl_value.low_reg, size); + } + return true; +} + bool Mir2Lir::GenInlinedUnsafeGet(CallInfo* info, bool is_long, bool is_volatile) { if (cu_->instruction_set == kMips) { @@ -1322,6 +1355,32 @@ bool Mir2Lir::GenIntrinsic(CallInfo* info) { if (tgt_method == "java.lang.Thread java.lang.Thread.currentThread()") { return GenInlinedCurrentThread(info); } + } else if (tgt_methods_declaring_class.starts_with("Llibcore/io/Memory;")) { + std::string tgt_method(PrettyMethod(info->index, *cu_->dex_file)); + if (tgt_method == "byte libcore.io.Memory.peekByte(long)") { + return GenInlinedPeek(info, kSignedByte); + } + if (tgt_method == "int libcore.io.Memory.peekIntNative(long)") { + return GenInlinedPeek(info, kWord); + } + if (tgt_method == "long libcore.io.Memory.peekLongNative(long)") { + return GenInlinedPeek(info, kLong); + } + if (tgt_method == "short libcore.io.Memory.peekShortNative(long)") { + return GenInlinedPeek(info, kSignedHalf); + } + if (tgt_method == "void libcore.io.Memory.pokeByte(long, byte)") { + return GenInlinedPoke(info, kSignedByte); + } + if (tgt_method == "void libcore.io.Memory.pokeIntNative(long, int)") { + return GenInlinedPoke(info, kWord); + } + if (tgt_method == "void libcore.io.Memory.pokeLongNative(long, long)") { + return GenInlinedPoke(info, kLong); + } + if (tgt_method == "void libcore.io.Memory.pokeShortNative(long, short)") { + return GenInlinedPoke(info, kSignedHalf); + } } else if (tgt_methods_declaring_class.starts_with("Lsun/misc/Unsafe;")) { std::string tgt_method(PrettyMethod(info->index, *cu_->dex_file)); if (tgt_method == "boolean sun.misc.Unsafe.compareAndSwapInt(java.lang.Object, long, int, int)") { diff --git a/compiler/dex/quick/mir_to_lir.h b/compiler/dex/quick/mir_to_lir.h index 7e9848d58b3..0c2a70c649d 100644 --- a/compiler/dex/quick/mir_to_lir.h +++ b/compiler/dex/quick/mir_to_lir.h @@ -552,6 +552,8 @@ class Mir2Lir : public Backend { bool GenInlinedIndexOf(CallInfo* info, bool zero_based); bool GenInlinedStringCompareTo(CallInfo* info); bool GenInlinedCurrentThread(CallInfo* info); + bool GenInlinedPeek(CallInfo* info, OpSize size); + bool GenInlinedPoke(CallInfo* info, OpSize size); bool GenInlinedUnsafeGet(CallInfo* info, bool is_long, bool is_volatile); bool GenInlinedUnsafePut(CallInfo* info, bool is_long, bool is_object, bool is_volatile, bool is_ordered); From e508a2090b19fe705fbc6b99d76474037a74bbfb Mon Sep 17 00:00:00 2001 From: Vladimir Marko Date: Mon, 4 Nov 2013 15:24:22 +0000 Subject: [PATCH 0134/2402] Fix unaligned Memory peek/poke intrinsics. Change-Id: Id454464d0b28aa37f5239f1c6589ceb0b3bbbdea --- compiler/dex/quick/arm/codegen_arm.h | 2 ++ compiler/dex/quick/arm/int_arm.cc | 44 ++++++++++++++++++++++++++ compiler/dex/quick/gen_invoke.cc | 33 ------------------- compiler/dex/quick/mips/codegen_mips.h | 2 ++ compiler/dex/quick/mips/int_mips.cc | 31 ++++++++++++++++++ compiler/dex/quick/mir_to_lir.h | 4 +-- compiler/dex/quick/x86/codegen_x86.h | 2 ++ compiler/dex/quick/x86/int_x86.cc | 37 ++++++++++++++++++++++ 8 files changed, 120 insertions(+), 35 deletions(-) diff --git a/compiler/dex/quick/arm/codegen_arm.h b/compiler/dex/quick/arm/codegen_arm.h index 0a3bfc10ee2..15355be9d7c 100644 --- a/compiler/dex/quick/arm/codegen_arm.h +++ b/compiler/dex/quick/arm/codegen_arm.h @@ -107,6 +107,8 @@ class ArmMir2Lir : public Mir2Lir { bool GenInlinedCas32(CallInfo* info, bool need_write_barrier); bool GenInlinedMinMaxInt(CallInfo* info, bool is_min); bool GenInlinedSqrt(CallInfo* info); + bool GenInlinedPeek(CallInfo* info, OpSize size); + bool GenInlinedPoke(CallInfo* info, OpSize size); void GenNegLong(RegLocation rl_dest, RegLocation rl_src); void GenOrLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2); void GenSubLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2); diff --git a/compiler/dex/quick/arm/int_arm.cc b/compiler/dex/quick/arm/int_arm.cc index c3140a5cac9..0a8cbf9cc0a 100644 --- a/compiler/dex/quick/arm/int_arm.cc +++ b/compiler/dex/quick/arm/int_arm.cc @@ -493,6 +493,50 @@ bool ArmMir2Lir::GenInlinedMinMaxInt(CallInfo* info, bool is_min) { return true; } +bool ArmMir2Lir::GenInlinedPeek(CallInfo* info, OpSize size) { + RegLocation rl_src_address = info->args[0]; // long address + rl_src_address.wide = 0; // ignore high half in info->args[1] + RegLocation rl_dest = InlineTarget(info); + RegLocation rl_address = LoadValue(rl_src_address, kCoreReg); + RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true); + if (size == kLong) { + // Fake unaligned LDRD by two unaligned LDR instructions on ARMv7 with SCTLR.A set to 0. + if (rl_address.low_reg != rl_result.low_reg) { + LoadBaseDisp(rl_address.low_reg, 0, rl_result.low_reg, kWord, INVALID_SREG); + LoadBaseDisp(rl_address.low_reg, 4, rl_result.high_reg, kWord, INVALID_SREG); + } else { + LoadBaseDisp(rl_address.low_reg, 4, rl_result.high_reg, kWord, INVALID_SREG); + LoadBaseDisp(rl_address.low_reg, 0, rl_result.low_reg, kWord, INVALID_SREG); + } + StoreValueWide(rl_dest, rl_result); + } else { + DCHECK(size == kSignedByte || size == kSignedHalf || size == kWord); + // Unaligned load with LDR and LDRSH is allowed on ARMv7 with SCTLR.A set to 0. + LoadBaseDisp(rl_address.low_reg, 0, rl_result.low_reg, size, INVALID_SREG); + StoreValue(rl_dest, rl_result); + } + return true; +} + +bool ArmMir2Lir::GenInlinedPoke(CallInfo* info, OpSize size) { + RegLocation rl_src_address = info->args[0]; // long address + rl_src_address.wide = 0; // ignore high half in info->args[1] + RegLocation rl_src_value = info->args[2]; // [size] value + RegLocation rl_address = LoadValue(rl_src_address, kCoreReg); + if (size == kLong) { + // Fake unaligned STRD by two unaligned STR instructions on ARMv7 with SCTLR.A set to 0. + RegLocation rl_value = LoadValueWide(rl_src_value, kCoreReg); + StoreBaseDisp(rl_address.low_reg, 0, rl_value.low_reg, kWord); + StoreBaseDisp(rl_address.low_reg, 4, rl_value.high_reg, kWord); + } else { + DCHECK(size == kSignedByte || size == kSignedHalf || size == kWord); + // Unaligned store with STR and STRSH is allowed on ARMv7 with SCTLR.A set to 0. + RegLocation rl_value = LoadValue(rl_src_value, kCoreReg); + StoreBaseDisp(rl_address.low_reg, 0, rl_value.low_reg, size); + } + return true; +} + void ArmMir2Lir::OpLea(int rBase, int reg1, int reg2, int scale, int offset) { LOG(FATAL) << "Unexpected use of OpLea for Arm"; } diff --git a/compiler/dex/quick/gen_invoke.cc b/compiler/dex/quick/gen_invoke.cc index e7bbb04eae0..72252626477 100644 --- a/compiler/dex/quick/gen_invoke.cc +++ b/compiler/dex/quick/gen_invoke.cc @@ -1161,39 +1161,6 @@ bool Mir2Lir::GenInlinedCurrentThread(CallInfo* info) { return true; } -bool Mir2Lir::GenInlinedPeek(CallInfo* info, OpSize size) { - RegLocation rl_src_address = info->args[0]; // long address - rl_src_address.wide = 0; // ignore high half in info->args[1] - RegLocation rl_dest = InlineTarget(info); - RegLocation rl_address = LoadValue(rl_src_address, kCoreReg); - RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true); - if (size == kLong) { - LoadBaseDispWide(rl_address.low_reg, 0, rl_result.low_reg, rl_result.high_reg, INVALID_SREG); - StoreValueWide(rl_dest, rl_result); - } else { - DCHECK(size == kSignedByte || size == kSignedHalf || size == kWord); - LoadBaseDisp(rl_address.low_reg, 0, rl_result.low_reg, size, INVALID_SREG); - StoreValue(rl_dest, rl_result); - } - return true; -} - -bool Mir2Lir::GenInlinedPoke(CallInfo* info, OpSize size) { - RegLocation rl_src_address = info->args[0]; // long address - rl_src_address.wide = 0; // ignore high half in info->args[1] - RegLocation rl_src_value = info->args[2]; // [size] value - RegLocation rl_address = LoadValue(rl_src_address, kCoreReg); - if (size == kLong) { - RegLocation rl_value = LoadValueWide(rl_src_value, kCoreReg); - StoreBaseDispWide(rl_address.low_reg, 0, rl_value.low_reg, rl_value.high_reg); - } else { - DCHECK(size == kSignedByte || size == kSignedHalf || size == kWord); - RegLocation rl_value = LoadValue(rl_src_value, kCoreReg); - StoreBaseDisp(rl_address.low_reg, 0, rl_value.low_reg, size); - } - return true; -} - bool Mir2Lir::GenInlinedUnsafeGet(CallInfo* info, bool is_long, bool is_volatile) { if (cu_->instruction_set == kMips) { diff --git a/compiler/dex/quick/mips/codegen_mips.h b/compiler/dex/quick/mips/codegen_mips.h index 0be20e8ea5a..88b244ba909 100644 --- a/compiler/dex/quick/mips/codegen_mips.h +++ b/compiler/dex/quick/mips/codegen_mips.h @@ -107,6 +107,8 @@ class MipsMir2Lir : public Mir2Lir { bool GenInlinedCas32(CallInfo* info, bool need_write_barrier); bool GenInlinedMinMaxInt(CallInfo* info, bool is_min); bool GenInlinedSqrt(CallInfo* info); + bool GenInlinedPeek(CallInfo* info, OpSize size); + bool GenInlinedPoke(CallInfo* info, OpSize size); void GenNegLong(RegLocation rl_dest, RegLocation rl_src); void GenOrLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2); void GenSubLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2); diff --git a/compiler/dex/quick/mips/int_mips.cc b/compiler/dex/quick/mips/int_mips.cc index 02ab04ef829..52294290c93 100644 --- a/compiler/dex/quick/mips/int_mips.cc +++ b/compiler/dex/quick/mips/int_mips.cc @@ -268,6 +268,37 @@ bool MipsMir2Lir::GenInlinedSqrt(CallInfo* info) { return false; } +bool MipsMir2Lir::GenInlinedPeek(CallInfo* info, OpSize size) { + if (size != kSignedByte) { + // MIPS supports only aligned access. Defer unaligned access to JNI implementation. + return false; + } + RegLocation rl_src_address = info->args[0]; // long address + rl_src_address.wide = 0; // ignore high half in info->args[1] + RegLocation rl_dest = InlineTarget(info); + RegLocation rl_address = LoadValue(rl_src_address, kCoreReg); + RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true); + DCHECK(size == kSignedByte); + LoadBaseDisp(rl_address.low_reg, 0, rl_result.low_reg, size, INVALID_SREG); + StoreValue(rl_dest, rl_result); + return true; +} + +bool MipsMir2Lir::GenInlinedPoke(CallInfo* info, OpSize size) { + if (size != kSignedByte) { + // MIPS supports only aligned access. Defer unaligned access to JNI implementation. + return false; + } + RegLocation rl_src_address = info->args[0]; // long address + rl_src_address.wide = 0; // ignore high half in info->args[1] + RegLocation rl_src_value = info->args[2]; // [size] value + RegLocation rl_address = LoadValue(rl_src_address, kCoreReg); + DCHECK(size == kSignedByte); + RegLocation rl_value = LoadValue(rl_src_value, kCoreReg); + StoreBaseDisp(rl_address.low_reg, 0, rl_value.low_reg, size); + return true; +} + LIR* MipsMir2Lir::OpPcRelLoad(int reg, LIR* target) { LOG(FATAL) << "Unexpected use of OpPcRelLoad for Mips"; return NULL; diff --git a/compiler/dex/quick/mir_to_lir.h b/compiler/dex/quick/mir_to_lir.h index 0c2a70c649d..4c56b74dc4a 100644 --- a/compiler/dex/quick/mir_to_lir.h +++ b/compiler/dex/quick/mir_to_lir.h @@ -552,8 +552,6 @@ class Mir2Lir : public Backend { bool GenInlinedIndexOf(CallInfo* info, bool zero_based); bool GenInlinedStringCompareTo(CallInfo* info); bool GenInlinedCurrentThread(CallInfo* info); - bool GenInlinedPeek(CallInfo* info, OpSize size); - bool GenInlinedPoke(CallInfo* info, OpSize size); bool GenInlinedUnsafeGet(CallInfo* info, bool is_long, bool is_volatile); bool GenInlinedUnsafePut(CallInfo* info, bool is_long, bool is_object, bool is_volatile, bool is_ordered); @@ -666,6 +664,8 @@ class Mir2Lir : public Backend { virtual bool GenInlinedCas32(CallInfo* info, bool need_write_barrier) = 0; virtual bool GenInlinedMinMaxInt(CallInfo* info, bool is_min) = 0; virtual bool GenInlinedSqrt(CallInfo* info) = 0; + virtual bool GenInlinedPeek(CallInfo* info, OpSize size) = 0; + virtual bool GenInlinedPoke(CallInfo* info, OpSize size) = 0; virtual void GenNegLong(RegLocation rl_dest, RegLocation rl_src) = 0; virtual void GenOrLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2) = 0; diff --git a/compiler/dex/quick/x86/codegen_x86.h b/compiler/dex/quick/x86/codegen_x86.h index b28d7ef2f6a..1d6509eea5c 100644 --- a/compiler/dex/quick/x86/codegen_x86.h +++ b/compiler/dex/quick/x86/codegen_x86.h @@ -107,6 +107,8 @@ class X86Mir2Lir : public Mir2Lir { bool GenInlinedCas32(CallInfo* info, bool need_write_barrier); bool GenInlinedMinMaxInt(CallInfo* info, bool is_min); bool GenInlinedSqrt(CallInfo* info); + bool GenInlinedPeek(CallInfo* info, OpSize size); + bool GenInlinedPoke(CallInfo* info, OpSize size); void GenNegLong(RegLocation rl_dest, RegLocation rl_src); void GenOrLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2); void GenSubLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2); diff --git a/compiler/dex/quick/x86/int_x86.cc b/compiler/dex/quick/x86/int_x86.cc index 3fbc7634bee..499547bb377 100644 --- a/compiler/dex/quick/x86/int_x86.cc +++ b/compiler/dex/quick/x86/int_x86.cc @@ -236,6 +236,43 @@ bool X86Mir2Lir::GenInlinedMinMaxInt(CallInfo* info, bool is_min) { return true; } +bool X86Mir2Lir::GenInlinedPeek(CallInfo* info, OpSize size) { + RegLocation rl_src_address = info->args[0]; // long address + rl_src_address.wide = 0; // ignore high half in info->args[1] + RegLocation rl_dest = InlineTarget(info); + RegLocation rl_address = LoadValue(rl_src_address, kCoreReg); + RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true); + if (size == kLong) { + // Unaligned access is allowed on x86. + LoadBaseDispWide(rl_address.low_reg, 0, rl_result.low_reg, rl_result.high_reg, INVALID_SREG); + StoreValueWide(rl_dest, rl_result); + } else { + DCHECK(size == kSignedByte || size == kSignedHalf || size == kWord); + // Unaligned access is allowed on x86. + LoadBaseDisp(rl_address.low_reg, 0, rl_result.low_reg, size, INVALID_SREG); + StoreValue(rl_dest, rl_result); + } + return true; +} + +bool X86Mir2Lir::GenInlinedPoke(CallInfo* info, OpSize size) { + RegLocation rl_src_address = info->args[0]; // long address + rl_src_address.wide = 0; // ignore high half in info->args[1] + RegLocation rl_src_value = info->args[2]; // [size] value + RegLocation rl_address = LoadValue(rl_src_address, kCoreReg); + if (size == kLong) { + // Unaligned access is allowed on x86. + RegLocation rl_value = LoadValueWide(rl_src_value, kCoreReg); + StoreBaseDispWide(rl_address.low_reg, 0, rl_value.low_reg, rl_value.high_reg); + } else { + DCHECK(size == kSignedByte || size == kSignedHalf || size == kWord); + // Unaligned access is allowed on x86. + RegLocation rl_value = LoadValue(rl_src_value, kCoreReg); + StoreBaseDisp(rl_address.low_reg, 0, rl_value.low_reg, size); + } + return true; +} + void X86Mir2Lir::OpLea(int rBase, int reg1, int reg2, int scale, int offset) { NewLIR5(kX86Lea32RA, rBase, reg1, reg2, scale, offset); } From 7b5f0cf08f74ff36760a813888779d28a175982d Mon Sep 17 00:00:00 2001 From: Christopher Ferris Date: Fri, 1 Nov 2013 15:18:45 -0700 Subject: [PATCH 0135/2402] Use libbacktrace instead of libcorkscrew. Also, removed the ignore frames of 2, this was causing threads to chop the lower two frames. The original code assumed that the calls to decode the frame were in the unwind trace, but that's not the case. Change-Id: Ifc0da0227f9114a5b462ef88e038439d58f951e9 --- runtime/Android.mk | 2 +- runtime/mem_map.cc | 12 +++--- runtime/utils.cc | 95 ++++++++++------------------------------------ 3 files changed, 27 insertions(+), 82 deletions(-) diff --git a/runtime/Android.mk b/runtime/Android.mk index 459ca0e8a74..8df03cf1afe 100644 --- a/runtime/Android.mk +++ b/runtime/Android.mk @@ -331,7 +331,7 @@ $$(ENUM_OPERATOR_OUT_GEN): $$(GENERATED_SRC_DIR)/%_operator_out.cc : $(LOCAL_PAT endif LOCAL_C_INCLUDES += $(ART_C_INCLUDES) LOCAL_SHARED_LIBRARIES += liblog libnativehelper - LOCAL_SHARED_LIBRARIES += libcorkscrew # native stack trace support + LOCAL_SHARED_LIBRARIES += libbacktrace # native stack trace support ifeq ($$(art_target_or_host),target) LOCAL_SHARED_LIBRARIES += libcutils libz libdl libselinux else # host diff --git a/runtime/mem_map.cc b/runtime/mem_map.cc index 6451d5c51ae..7802acc88b6 100644 --- a/runtime/mem_map.cc +++ b/runtime/mem_map.cc @@ -16,7 +16,7 @@ #include "mem_map.h" -#include +#include #include "base/stringprintf.h" #include "ScopedFd.h" @@ -32,8 +32,8 @@ namespace art { #if !defined(NDEBUG) -static std::ostream& operator<<(std::ostream& os, map_info_t* rhs) { - for (map_info_t* m = rhs; m != NULL; m = m->next) { +static std::ostream& operator<<(std::ostream& os, backtrace_map_info_t* rhs) { + for (backtrace_map_info_t* m = rhs; m != NULL; m = m->next) { os << StringPrintf("0x%08x-0x%08x %c%c %s\n", static_cast(m->start), static_cast(m->end), @@ -50,8 +50,8 @@ static void CheckMapRequest(byte* addr, size_t byte_count) { uint32_t base = reinterpret_cast(addr); uint32_t limit = base + byte_count; - map_info_t* map_info_list = load_map_info_list(getpid()); - for (map_info_t* m = map_info_list; m != NULL; m = m->next) { + backtrace_map_info_t* map_info_list = backtrace_create_map_info_list(getpid()); + for (backtrace_map_info_t* m = map_info_list; m != NULL; m = m->next) { CHECK(!(base >= m->start && base < m->end) // start of new within old && !(limit > m->start && limit < m->end) // end of new within old && !(base <= m->start && limit > m->end)) // start/end of new includes all of old @@ -60,7 +60,7 @@ static void CheckMapRequest(byte* addr, size_t byte_count) { static_cast(m->start), static_cast(m->end), m->name) << map_info_list; } - free_map_info_list(map_info_list); + backtrace_destroy_map_info_list(map_info_list); } #else diff --git a/runtime/utils.cc b/runtime/utils.cc index 01441a2f27b..7fa06a394f9 100644 --- a/runtime/utils.cc +++ b/runtime/utils.cc @@ -49,8 +49,7 @@ #include #endif -#include // For DumpNativeStack. -#include // For DumpNativeStack. +#include // For DumpNativeStack. #if defined(__linux__) #include @@ -997,10 +996,9 @@ std::string GetSchedulerGroupName(pid_t tid) { return ""; } -static const char* CleanMapName(const backtrace_symbol_t* symbol) { - const char* map_name = symbol->map_name; +static const char* CleanMapName(const char* map_name) { if (map_name == NULL) { - map_name = "???"; + return "???"; } // Turn "/usr/local/google/home/enh/clean-dalvik-dev/out/host/linux-x86/lib/libartd.so" // into "libartd.so". @@ -1011,89 +1009,36 @@ static const char* CleanMapName(const backtrace_symbol_t* symbol) { return map_name; } -static void FindSymbolInElf(const backtrace_frame_t* frame, const backtrace_symbol_t* symbol, - std::string& symbol_name, uint32_t& pc_offset) { - symbol_table_t* symbol_table = NULL; - if (symbol->map_name != NULL) { - symbol_table = load_symbol_table(symbol->map_name); - } - const symbol_t* elf_symbol = NULL; - bool was_relative = true; - if (symbol_table != NULL) { - elf_symbol = find_symbol(symbol_table, symbol->relative_pc); - if (elf_symbol == NULL) { - elf_symbol = find_symbol(symbol_table, frame->absolute_pc); - was_relative = false; - } - } - if (elf_symbol != NULL) { - const char* demangled_symbol_name = demangle_symbol_name(elf_symbol->name); - if (demangled_symbol_name != NULL) { - symbol_name = demangled_symbol_name; - } else { - symbol_name = elf_symbol->name; - } - - // TODO: is it a libcorkscrew bug that we have to do this? - pc_offset = (was_relative ? symbol->relative_pc : frame->absolute_pc) - elf_symbol->start; - } else { - symbol_name = "???"; - } - free_symbol_table(symbol_table); -} - void DumpNativeStack(std::ostream& os, pid_t tid, const char* prefix, bool include_count) { - // Ensure libcorkscrew doesn't use a stale cache of /proc/self/maps. - flush_my_map_info_list(); - - const size_t MAX_DEPTH = 32; - UniquePtr frames(new backtrace_frame_t[MAX_DEPTH]); - size_t ignore_count = 2; // Don't include unwind_backtrace_thread or DumpNativeStack. - ssize_t frame_count = unwind_backtrace_thread(tid, frames.get(), ignore_count, MAX_DEPTH); - if (frame_count == -1) { - os << prefix << "(unwind_backtrace_thread failed for thread " << tid << ")\n"; + UniquePtr backtrace(Backtrace::Create(-1, tid)); + if (!backtrace->Unwind(0)) { + os << prefix << "(backtrace::Unwind failed for thread " << tid << ")\n"; return; - } else if (frame_count == 0) { + } else if (backtrace->NumFrames() == 0) { os << prefix << "(no native stack frames for thread " << tid << ")\n"; return; } - UniquePtr backtrace_symbols(new backtrace_symbol_t[frame_count]); - get_backtrace_symbols(frames.get(), frame_count, backtrace_symbols.get()); - - for (size_t i = 0; i < static_cast(frame_count); ++i) { - const backtrace_frame_t* frame = &frames[i]; - const backtrace_symbol_t* symbol = &backtrace_symbols[i]; - + for (size_t i = 0; i < backtrace->NumFrames(); ++i) { // We produce output like this: - // ] #00 unwind_backtrace_thread+536 [0x55d75bb8] (libcorkscrew.so) - - std::string symbol_name; - uint32_t pc_offset = 0; - if (symbol->demangled_name != NULL) { - symbol_name = symbol->demangled_name; - pc_offset = symbol->relative_pc - symbol->relative_symbol_addr; - } else if (symbol->symbol_name != NULL) { - symbol_name = symbol->symbol_name; - pc_offset = symbol->relative_pc - symbol->relative_symbol_addr; - } else { - // dladdr(3) didn't find a symbol; maybe it's static? Look in the ELF file... - FindSymbolInElf(frame, symbol, symbol_name, pc_offset); - } + // ] #00 unwind_backtrace_thread+536 [0x55d75bb8] (libbacktrace.so) + const backtrace_frame_data_t* frame = backtrace->GetFrame(i); os << prefix; if (include_count) { - os << StringPrintf("#%02zd ", i); + os << StringPrintf("#%02zu ", i); + } + if (frame->func_name) { + os << frame->func_name; + } else { + os << "???"; } - os << symbol_name; - if (pc_offset != 0) { - os << "+" << pc_offset; + if (frame->func_offset != 0) { + os << "+" << frame->func_offset; } - os << StringPrintf(" [%p] (%s)\n", - reinterpret_cast(frame->absolute_pc), CleanMapName(symbol)); + os << StringPrintf(" [%p]", reinterpret_cast(frame->pc)); + os << " (" << CleanMapName(frame->map_name) << ")\n"; } - - free_backtrace_symbols(backtrace_symbols.get(), frame_count); } #if defined(__APPLE__) From c26a56cb596c3c8efd519c4014fc2ebb3e48b221 Mon Sep 17 00:00:00 2001 From: Jeff Hao Date: Mon, 4 Nov 2013 12:00:47 -0800 Subject: [PATCH 0136/2402] Verifier uses exception type instead of conflict if unresolved. Fixes OneMedical app installation issues. Bug: 11335470 Change-Id: I10ef8c84ef5bf5587283413b8cea89202407fe2b --- runtime/verifier/method_verifier.cc | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc index 5a99167b6f1..9f980610a43 100644 --- a/runtime/verifier/method_verifier.cc +++ b/runtime/verifier/method_verifier.cc @@ -2892,11 +2892,14 @@ const RegType& MethodVerifier::GetCaughtExceptionType() { // as that is caught at runtime common_super = &exception; } else if (!reg_types_.JavaLangThrowable(false).IsAssignableFrom(exception)) { - // We don't know enough about the type and the common path merge will result in - // Conflict. Fail here knowing the correct thing can be done at runtime. - Fail(exception.IsUnresolvedTypes() ? VERIFY_ERROR_NO_CLASS : - VERIFY_ERROR_BAD_CLASS_SOFT) << "unexpected non-exception class " << exception; - return reg_types_.Conflict(); + if (exception.IsUnresolvedTypes()) { + // We don't know enough about the type. Fail here and let runtime handle it. + Fail(VERIFY_ERROR_NO_CLASS) << "unresolved exception class " << exception; + return exception; + } else { + Fail(VERIFY_ERROR_BAD_CLASS_SOFT) << "unexpected non-exception class " << exception; + return reg_types_.Conflict(); + } } else if (common_super->Equals(exception)) { // odd case, but nothing to do } else { From 610e49f5adc8b5e4a37696aa20fc029dab6b1e40 Mon Sep 17 00:00:00 2001 From: Brian Carlstrom Date: Mon, 4 Nov 2013 17:07:22 -0800 Subject: [PATCH 0137/2402] Fix typo in duplicate condition Bug: https://code.google.com/p/android/issues/detail?id=61768 Change-Id: I65b85de1d942c5bd0dfd6a8f7b67e157c066b9f6 --- runtime/class_linker.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index ac8a87c4076..184e5d4be9d 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -2945,7 +2945,7 @@ static void CheckProxyMethod(mirror::ArtMethod* method, static bool CanWeInitializeClass(mirror::Class* klass, bool can_init_statics, bool can_init_parents) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - if (can_init_statics && can_init_statics) { + if (can_init_statics && can_init_parents) { return true; } if (!can_init_statics) { @@ -2969,7 +2969,7 @@ static bool CanWeInitializeClass(mirror::Class* klass, bool can_init_statics, if (!can_init_parents && !super_class->IsInitialized()) { return false; } else { - if (!CanWeInitializeClass(super_class, can_init_statics, true)) { + if (!CanWeInitializeClass(super_class, can_init_statics, can_init_parents)) { return false; } } From 0941b0423537a6a5d7c1df6dd23e9864ea8f319c Mon Sep 17 00:00:00 2001 From: Hiroshi Yamauchi Date: Tue, 5 Nov 2013 11:34:03 -0800 Subject: [PATCH 0138/2402] Fix a DCHECK failure due to unmatching numbers of cards scanned. - See the bug for details of the failure. - After a discussion, we decided to get rid of the DCHECK as a simple solution would not detect corner failure cases and a full solution would add undesired complexity, and left a comment that explains what situation had caused a DCHECK failure. - Fix a potential error of failing to scan the last card that the end of the image space falls on as a result of the image end being not necessarily aligned by the card size. - Remove dead/unused MarkSweep::ScanRoot(). - Add AlignUp and AlignDown for aligning pointers. Bug: 11465268 Change-Id: Iee3018a42c48a159feb0e9cf77b1a6b303f5d245 --- runtime/gc/collector/mark_sweep.cc | 27 ++++++++------------------- runtime/gc/collector/mark_sweep.h | 6 ------ runtime/utils.h | 13 +++++++++++++ 3 files changed, 21 insertions(+), 25 deletions(-) diff --git a/runtime/gc/collector/mark_sweep.cc b/runtime/gc/collector/mark_sweep.cc index a5e66d20df0..2c69c77187f 100644 --- a/runtime/gc/collector/mark_sweep.cc +++ b/runtime/gc/collector/mark_sweep.cc @@ -776,7 +776,6 @@ class CardScanTask : public MarkStackTask { ScanObjectParallelVisitor visitor(this); accounting::CardTable* card_table = mark_sweep_->GetHeap()->GetCardTable(); size_t cards_scanned = card_table->Scan(bitmap_, begin_, end_, visitor, minimum_age_); - mark_sweep_->cards_scanned_.fetch_add(cards_scanned); VLOG(heap) << "Parallel scanning cards " << reinterpret_cast(begin_) << " - " << reinterpret_cast(end_) << " = " << cards_scanned; // Finish by emptying our local mark stack. @@ -814,11 +813,14 @@ void MarkSweep::ScanGrayObjects(bool paused, byte minimum_age) { DCHECK_NE(mark_stack_tasks, 0U); const size_t mark_stack_delta = std::min(CardScanTask::kMaxSize / 2, mark_stack_size / mark_stack_tasks + 1); - size_t ref_card_count = 0; - cards_scanned_ = 0; for (const auto& space : GetHeap()->GetContinuousSpaces()) { byte* card_begin = space->Begin(); byte* card_end = space->End(); + // Align up the end address. For example, the image space's end + // may not be card-size-aligned. + card_end = AlignUp(card_end, accounting::CardTable::kCardSize); + DCHECK(IsAligned(card_begin)); + DCHECK(IsAligned(card_end)); // Calculate how many bytes of heap we will scan, const size_t address_range = card_end - card_begin; // Calculate how much address range each task gets. @@ -842,24 +844,15 @@ void MarkSweep::ScanGrayObjects(bool paused, byte minimum_age) { thread_pool->AddTask(self, task); card_begin += card_increment; } - - if (paused && kIsDebugBuild) { - // Make sure we don't miss scanning any cards. - size_t scanned_cards = card_table->Scan(space->GetMarkBitmap(), space->Begin(), - space->End(), VoidFunctor(), minimum_age); - VLOG(heap) << "Scanning space cards " << reinterpret_cast(space->Begin()) << " - " - << reinterpret_cast(space->End()) << " = " << scanned_cards; - ref_card_count += scanned_cards; - } } + // Note: the card scan below may dirty new cards (and scan them) + // as a side effect when a Reference object is encountered and + // queued during the marking. See b/11465268. thread_pool->SetMaxActiveWorkers(thread_count - 1); thread_pool->StartWorkers(self); thread_pool->Wait(self, true, true); thread_pool->StopWorkers(self); - if (paused) { - DCHECK_EQ(ref_card_count, static_cast(cards_scanned_.load())); - } timings_.EndSplit(); } else { for (const auto& space : GetHeap()->GetContinuousSpaces()) { @@ -1356,10 +1349,6 @@ void MarkSweep::DelayReferenceReferent(mirror::Class* klass, Object* obj) { } } -void MarkSweep::ScanRoot(const Object* obj) { - ScanObject(obj); -} - class MarkObjectVisitor { public: explicit MarkObjectVisitor(MarkSweep* const mark_sweep) ALWAYS_INLINE : mark_sweep_(mark_sweep) {} diff --git a/runtime/gc/collector/mark_sweep.h b/runtime/gc/collector/mark_sweep.h index 19df2da0b67..637428bd481 100644 --- a/runtime/gc/collector/mark_sweep.h +++ b/runtime/gc/collector/mark_sweep.h @@ -157,11 +157,6 @@ class MarkSweep : public GarbageCollector { return cleared_reference_list_; } - // Proxy for external access to ScanObject. - void ScanRoot(const mirror::Object* obj) - EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - // Blackens an object. void ScanObject(const mirror::Object* obj) EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_) @@ -438,7 +433,6 @@ class MarkSweep : public GarbageCollector { AtomicInteger work_chunks_created_; AtomicInteger work_chunks_deleted_; AtomicInteger reference_count_; - AtomicInteger cards_scanned_; // Verification. size_t live_stack_freeze_size_; diff --git a/runtime/utils.h b/runtime/utils.h index 0174b37dcc1..51035b697fe 100644 --- a/runtime/utils.h +++ b/runtime/utils.h @@ -119,6 +119,7 @@ struct TypeStaticIf { typedef B value; }; +// For rounding integers. template static inline T RoundDown(T x, int n) { CHECK(IsPowerOfTwo(n)); @@ -130,6 +131,18 @@ static inline T RoundUp(T x, int n) { return RoundDown(x + n - 1, n); } +// For aligning pointers. +template +static inline T* AlignDown(T* x, int n) { + CHECK(IsPowerOfTwo(n)); + return reinterpret_cast(reinterpret_cast(x) & -static_cast(n)); +} + +template +static inline T* AlignUp(T* x, int n) { + return AlignDown(reinterpret_cast(reinterpret_cast(x) + static_cast(n - 1)), n); +} + // Implementation is from "Hacker's Delight" by Henry S. Warren, Jr., // figure 3-3, page 48, where the function is called clp2. static inline uint32_t RoundUpToPowerOfTwo(uint32_t x) { From 94b400d56bb246d54a1606a3ca3f0b2157acc1fa Mon Sep 17 00:00:00 2001 From: Vladimir Marko Date: Tue, 5 Nov 2013 19:19:02 +0000 Subject: [PATCH 0139/2402] Improve Mir2Lir::GenIntrinsic() performance. GenIntrinsic() spends most time just comparing the method's declaring class name to the class names we're looking for without hiting anything. Reduce the number of comparisons by splitting the path for java.lang.* and other calls. Change-Id: Ic0d42ee2bde9e86b602b421c11fb2315de774a29 --- compiler/dex/quick/gen_invoke.cc | 159 ++++++++++++++++--------------- 1 file changed, 81 insertions(+), 78 deletions(-) diff --git a/compiler/dex/quick/gen_invoke.cc b/compiler/dex/quick/gen_invoke.cc index 72252626477..d1a9a132bc7 100644 --- a/compiler/dex/quick/gen_invoke.cc +++ b/compiler/dex/quick/gen_invoke.cc @@ -1245,84 +1245,87 @@ bool Mir2Lir::GenIntrinsic(CallInfo* info) { const DexFile::TypeId& declaring_type = cu_->dex_file->GetTypeId(target_mid.class_idx_); StringPiece tgt_methods_declaring_class( cu_->dex_file->StringDataByIdx(declaring_type.descriptor_idx_)); - if (tgt_methods_declaring_class.starts_with("Ljava/lang/Double;")) { - std::string tgt_method(PrettyMethod(info->index, *cu_->dex_file)); - if (tgt_method == "long java.lang.Double.doubleToRawLongBits(double)") { - return GenInlinedDoubleCvt(info); - } - if (tgt_method == "double java.lang.Double.longBitsToDouble(long)") { - return GenInlinedDoubleCvt(info); - } - } else if (tgt_methods_declaring_class.starts_with("Ljava/lang/Float;")) { - std::string tgt_method(PrettyMethod(info->index, *cu_->dex_file)); - if (tgt_method == "int java.lang.Float.floatToRawIntBits(float)") { - return GenInlinedFloatCvt(info); - } - if (tgt_method == "float java.lang.Float.intBitsToFloat(int)") { - return GenInlinedFloatCvt(info); - } - } else if (tgt_methods_declaring_class.starts_with("Ljava/lang/Integer;")) { - std::string tgt_method(PrettyMethod(info->index, *cu_->dex_file)); - if (tgt_method == "int java.lang.Integer.reverseBytes(int)") { - return GenInlinedReverseBytes(info, kWord); - } - } else if (tgt_methods_declaring_class.starts_with("Ljava/lang/Long;")) { - std::string tgt_method(PrettyMethod(info->index, *cu_->dex_file)); - if (tgt_method == "long java.lang.Long.reverseBytes(long)") { - return GenInlinedReverseBytes(info, kLong); - } - } else if (tgt_methods_declaring_class.starts_with("Ljava/lang/Math;") || - tgt_methods_declaring_class.starts_with("Ljava/lang/StrictMath;")) { - std::string tgt_method(PrettyMethod(info->index, *cu_->dex_file)); - if (tgt_method == "int java.lang.Math.abs(int)" || - tgt_method == "int java.lang.StrictMath.abs(int)") { - return GenInlinedAbsInt(info); - } - if (tgt_method == "long java.lang.Math.abs(long)" || - tgt_method == "long java.lang.StrictMath.abs(long)") { - return GenInlinedAbsLong(info); - } - if (tgt_method == "int java.lang.Math.max(int, int)" || - tgt_method == "int java.lang.StrictMath.max(int, int)") { - return GenInlinedMinMaxInt(info, false /* is_min */); - } - if (tgt_method == "int java.lang.Math.min(int, int)" || - tgt_method == "int java.lang.StrictMath.min(int, int)") { - return GenInlinedMinMaxInt(info, true /* is_min */); - } - if (tgt_method == "double java.lang.Math.sqrt(double)" || - tgt_method == "double java.lang.StrictMath.sqrt(double)") { - return GenInlinedSqrt(info); - } - } else if (tgt_methods_declaring_class.starts_with("Ljava/lang/Short;")) { - std::string tgt_method(PrettyMethod(info->index, *cu_->dex_file)); - if (tgt_method == "short java.lang.Short.reverseBytes(short)") { - return GenInlinedReverseBytes(info, kSignedHalf); - } - } else if (tgt_methods_declaring_class.starts_with("Ljava/lang/String;")) { - std::string tgt_method(PrettyMethod(info->index, *cu_->dex_file)); - if (tgt_method == "char java.lang.String.charAt(int)") { - return GenInlinedCharAt(info); - } - if (tgt_method == "int java.lang.String.compareTo(java.lang.String)") { - return GenInlinedStringCompareTo(info); - } - if (tgt_method == "boolean java.lang.String.is_empty()") { - return GenInlinedStringIsEmptyOrLength(info, true /* is_empty */); - } - if (tgt_method == "int java.lang.String.index_of(int, int)") { - return GenInlinedIndexOf(info, false /* base 0 */); - } - if (tgt_method == "int java.lang.String.index_of(int)") { - return GenInlinedIndexOf(info, true /* base 0 */); - } - if (tgt_method == "int java.lang.String.length()") { - return GenInlinedStringIsEmptyOrLength(info, false /* is_empty */); - } - } else if (tgt_methods_declaring_class.starts_with("Ljava/lang/Thread;")) { - std::string tgt_method(PrettyMethod(info->index, *cu_->dex_file)); - if (tgt_method == "java.lang.Thread java.lang.Thread.currentThread()") { - return GenInlinedCurrentThread(info); + if (tgt_methods_declaring_class.starts_with("Ljava/lang/")) { + tgt_methods_declaring_class.remove_prefix(sizeof("Ljava/lang/") - 1); + if (tgt_methods_declaring_class.starts_with("Double;")) { + std::string tgt_method(PrettyMethod(info->index, *cu_->dex_file)); + if (tgt_method == "long java.lang.Double.doubleToRawLongBits(double)") { + return GenInlinedDoubleCvt(info); + } + if (tgt_method == "double java.lang.Double.longBitsToDouble(long)") { + return GenInlinedDoubleCvt(info); + } + } else if (tgt_methods_declaring_class.starts_with("Float;")) { + std::string tgt_method(PrettyMethod(info->index, *cu_->dex_file)); + if (tgt_method == "int java.lang.Float.floatToRawIntBits(float)") { + return GenInlinedFloatCvt(info); + } + if (tgt_method == "float java.lang.Float.intBitsToFloat(int)") { + return GenInlinedFloatCvt(info); + } + } else if (tgt_methods_declaring_class.starts_with("Integer;")) { + std::string tgt_method(PrettyMethod(info->index, *cu_->dex_file)); + if (tgt_method == "int java.lang.Integer.reverseBytes(int)") { + return GenInlinedReverseBytes(info, kWord); + } + } else if (tgt_methods_declaring_class.starts_with("Long;")) { + std::string tgt_method(PrettyMethod(info->index, *cu_->dex_file)); + if (tgt_method == "long java.lang.Long.reverseBytes(long)") { + return GenInlinedReverseBytes(info, kLong); + } + } else if (tgt_methods_declaring_class.starts_with("Math;") || + tgt_methods_declaring_class.starts_with("StrictMath;")) { + std::string tgt_method(PrettyMethod(info->index, *cu_->dex_file)); + if (tgt_method == "int java.lang.Math.abs(int)" || + tgt_method == "int java.lang.StrictMath.abs(int)") { + return GenInlinedAbsInt(info); + } + if (tgt_method == "long java.lang.Math.abs(long)" || + tgt_method == "long java.lang.StrictMath.abs(long)") { + return GenInlinedAbsLong(info); + } + if (tgt_method == "int java.lang.Math.max(int, int)" || + tgt_method == "int java.lang.StrictMath.max(int, int)") { + return GenInlinedMinMaxInt(info, false /* is_min */); + } + if (tgt_method == "int java.lang.Math.min(int, int)" || + tgt_method == "int java.lang.StrictMath.min(int, int)") { + return GenInlinedMinMaxInt(info, true /* is_min */); + } + if (tgt_method == "double java.lang.Math.sqrt(double)" || + tgt_method == "double java.lang.StrictMath.sqrt(double)") { + return GenInlinedSqrt(info); + } + } else if (tgt_methods_declaring_class.starts_with("Short;")) { + std::string tgt_method(PrettyMethod(info->index, *cu_->dex_file)); + if (tgt_method == "short java.lang.Short.reverseBytes(short)") { + return GenInlinedReverseBytes(info, kSignedHalf); + } + } else if (tgt_methods_declaring_class.starts_with("String;")) { + std::string tgt_method(PrettyMethod(info->index, *cu_->dex_file)); + if (tgt_method == "char java.lang.String.charAt(int)") { + return GenInlinedCharAt(info); + } + if (tgt_method == "int java.lang.String.compareTo(java.lang.String)") { + return GenInlinedStringCompareTo(info); + } + if (tgt_method == "boolean java.lang.String.is_empty()") { + return GenInlinedStringIsEmptyOrLength(info, true /* is_empty */); + } + if (tgt_method == "int java.lang.String.index_of(int, int)") { + return GenInlinedIndexOf(info, false /* base 0 */); + } + if (tgt_method == "int java.lang.String.index_of(int)") { + return GenInlinedIndexOf(info, true /* base 0 */); + } + if (tgt_method == "int java.lang.String.length()") { + return GenInlinedStringIsEmptyOrLength(info, false /* is_empty */); + } + } else if (tgt_methods_declaring_class.starts_with("Thread;")) { + std::string tgt_method(PrettyMethod(info->index, *cu_->dex_file)); + if (tgt_method == "java.lang.Thread java.lang.Thread.currentThread()") { + return GenInlinedCurrentThread(info); + } } } else if (tgt_methods_declaring_class.starts_with("Llibcore/io/Memory;")) { std::string tgt_method(PrettyMethod(info->index, *cu_->dex_file)); From 7020278bce98a0735dc6abcbd33bdf1ed2634f1d Mon Sep 17 00:00:00 2001 From: Dave Allison Date: Tue, 22 Oct 2013 17:52:19 -0700 Subject: [PATCH 0140/2402] Support hardware divide instruction Bug: 11299025 Uses sdiv for division and a combo of sdiv, mul and sub for modulus. Only does this on processors that are capable of the sdiv instruction, as determined by the build system. Also provides a command line arg --instruction-set-features= to allow cross compilation. Makefile adds the --instruction-set-features= arg to build-time dex2oat runs and defaults it to something obtained from the target architecture. Provides a GetInstructionSetFeatures() function on CompilerDriver that can be queried for various features. The only feature supported right now is hasDivideInstruction(). Also adds a few more instructions to the ARM disassembler b/11535253 is an addition to this CL to be done later. Change-Id: Ia8aaf801fd94bc71e476902749cf20f74eba9f68 --- Android.mk | 2 +- build/Android.executable.mk | 4 + build/Android.gtest.mk | 1 + build/Android.oat.mk | 22 ++- compiler/dex/compiler_ir.h | 3 + compiler/dex/quick/arm/arm_lir.h | 2 + compiler/dex/quick/arm/assemble_arm.cc | 8 + compiler/dex/quick/arm/int_arm.cc | 33 +++- compiler/dex/quick/arm/utility_arm.cc | 4 + compiler/dex/quick/gen_common.cc | 40 ++++- compiler/driver/compiler_driver.cc | 2 + compiler/driver/compiler_driver.h | 10 +- compiler/oat_test.cc | 9 +- compiler/oat_writer.cc | 1 + dex2oat/dex2oat.cc | 57 +++++- disassembler/disassembler_arm.cc | 232 ++++++++++++++++++++++--- oatdump/oatdump.cc | 3 + runtime/Android.mk | 1 + runtime/arch/arm/arm_sdiv.S | 24 +++ runtime/base/macros.h | 4 + runtime/common_test.h | 124 ++++++++++++- runtime/instruction_set.h | 50 ++++++ runtime/oat.cc | 11 +- runtime/oat.h | 3 + runtime/utils.cc | 29 ++++ runtime/utils.h | 9 +- 26 files changed, 636 insertions(+), 52 deletions(-) create mode 100644 runtime/arch/arm/arm_sdiv.S diff --git a/Android.mk b/Android.mk index 0b4b2316fd8..3112ab02881 100644 --- a/Android.mk +++ b/Android.mk @@ -270,7 +270,7 @@ oat-target-$(1): $$(OUT_OAT_FILE) $$(OUT_OAT_FILE): $(PRODUCT_OUT)/$(1) $(TARGET_BOOT_IMG_OUT) $(DEX2OAT_DEPENDENCY) @mkdir -p $$(dir $$@) - $(DEX2OAT) $(PARALLEL_ART_COMPILE_JOBS) --runtime-arg -Xms64m --runtime-arg -Xmx64m --boot-image=$(TARGET_BOOT_IMG_OUT) --dex-file=$(PRODUCT_OUT)/$(1) --dex-location=/$(1) --oat-file=$$@ --host-prefix=$(PRODUCT_OUT) --instruction-set=$(TARGET_ARCH) --android-root=$(PRODUCT_OUT)/system + $(DEX2OAT) $(PARALLEL_ART_COMPILE_JOBS) --runtime-arg -Xms64m --runtime-arg -Xmx64m --boot-image=$(TARGET_BOOT_IMG_OUT) --dex-file=$(PRODUCT_OUT)/$(1) --dex-location=/$(1) --oat-file=$$@ --host-prefix=$(PRODUCT_OUT) --instruction-set=$(TARGET_ARCH) --instruction-set-features=$(TARGET_INSTRUCTION_SET_FEATURES) --android-root=$(PRODUCT_OUT)/system endif diff --git a/build/Android.executable.mk b/build/Android.executable.mk index 5cf15be1c17..b317d92999a 100644 --- a/build/Android.executable.mk +++ b/build/Android.executable.mk @@ -24,6 +24,10 @@ ifeq ($(ART_USE_PORTABLE_COMPILER),true) ART_EXECUTABLES_CFLAGS += -DART_USE_PORTABLE_COMPILER=1 endif +# add the default instruction set features +ART_EXECUTABLES_CFLAGS += \ + -DART_DEFAULT_INSTRUCTION_SET_FEATURES=$(DEX2OAT_TARGET_INSTRUCTION_SET_FEATURES) + # $(1): executable ("d" will be appended for debug version) # $(2): source # $(3): extra shared libraries diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk index 655c7dd01f0..0d759ce085b 100644 --- a/build/Android.gtest.mk +++ b/build/Android.gtest.mk @@ -86,6 +86,7 @@ ART_TEST_CFLAGS := ifeq ($(ART_USE_PORTABLE_COMPILER),true) ART_TEST_CFLAGS += -DART_USE_PORTABLE_COMPILER=1 endif +ART_TEST_CFLAGS += -DART_DEFAULT_INSTRUCTION_SET_FEATURES=$(DEX2OAT_TARGET_INSTRUCTION_SET_FEATURES) # $(1): target or host # $(2): file name diff --git a/build/Android.oat.mk b/build/Android.oat.mk index b680b820b70..5d355a6d86b 100644 --- a/build/Android.oat.mk +++ b/build/Android.oat.mk @@ -26,7 +26,7 @@ LIBART_COMPILER := $(LIBARTD_COMPILER) # By default, do not run rerun dex2oat if the tool changes. # Comment out the | to force dex2oat to rerun on after all changes. -DEX2OAT_DEPENDENCY := | +DEX2OAT_DEPENDENCY := #| DEX2OAT_DEPENDENCY += $(DEX2OAT) DEX2OAT_DEPENDENCY += $(LIBART_COMPILER) @@ -57,15 +57,26 @@ TARGET_CORE_OAT_OUT := $(ART_TEST_OUT)/core.oat HOST_CORE_IMG_OUT := $(HOST_OUT_JAVA_LIBRARIES)/core.art TARGET_CORE_IMG_OUT := $(ART_TEST_OUT)/core.art +# DEX2OAT_TARGET_INSTRUCTION_SET_FEATURES is set in ../build/core/dex_preopt.mk based on +# the TARGET_CPU_VARIANT + +TARGET_INSTRUCTION_SET_FEATURES := $(DEX2OAT_TARGET_INSTRUCTION_SET_FEATURES) + $(HOST_CORE_IMG_OUT): $(HOST_CORE_DEX_FILES) $(DEX2OAT_DEPENDENCY) @echo "host dex2oat: $@ ($?)" @mkdir -p $(dir $@) - $(hide) $(DEX2OAT) $(PARALLEL_ART_COMPILE_JOBS) --runtime-arg -Xms16m --runtime-arg -Xmx16m --image-classes=$(PRELOADED_CLASSES) $(addprefix --dex-file=,$(HOST_CORE_DEX_FILES)) $(addprefix --dex-location=,$(HOST_CORE_DEX_LOCATIONS)) --oat-file=$(HOST_CORE_OAT_OUT) --oat-location=$(HOST_CORE_OAT) --image=$(HOST_CORE_IMG_OUT) --base=$(IMG_HOST_BASE_ADDRESS) --instruction-set=$(HOST_ARCH) --host --android-root=$(HOST_OUT) + $(hide) $(DEX2OAT) $(PARALLEL_ART_COMPILE_JOBS) --runtime-arg -Xms16m --runtime-arg -Xmx16m --image-classes=$(PRELOADED_CLASSES) $(addprefix \ + --dex-file=,$(HOST_CORE_DEX_FILES)) $(addprefix --dex-location=,$(HOST_CORE_DEX_LOCATIONS)) --oat-file=$(HOST_CORE_OAT_OUT) \ + --oat-location=$(HOST_CORE_OAT) --image=$(HOST_CORE_IMG_OUT) --base=$(IMG_HOST_BASE_ADDRESS) \ + --instruction-set=$(HOST_ARCH) --host --android-root=$(HOST_OUT) $(TARGET_CORE_IMG_OUT): $(TARGET_CORE_DEX_FILES) $(DEX2OAT_DEPENDENCY) @echo "target dex2oat: $@ ($?)" @mkdir -p $(dir $@) - $(hide) $(DEX2OAT) $(PARALLEL_ART_COMPILE_JOBS) --runtime-arg -Xms16m --runtime-arg -Xmx16m --image-classes=$(PRELOADED_CLASSES) $(addprefix --dex-file=,$(TARGET_CORE_DEX_FILES)) $(addprefix --dex-location=,$(TARGET_CORE_DEX_LOCATIONS)) --oat-file=$(TARGET_CORE_OAT_OUT) --oat-location=$(TARGET_CORE_OAT) --image=$(TARGET_CORE_IMG_OUT) --base=$(IMG_TARGET_BASE_ADDRESS) --instruction-set=$(TARGET_ARCH) --host-prefix=$(PRODUCT_OUT) --android-root=$(PRODUCT_OUT)/system + $(hide) $(DEX2OAT) $(PARALLEL_ART_COMPILE_JOBS) --runtime-arg -Xms16m --runtime-arg -Xmx16m --image-classes=$(PRELOADED_CLASSES) $(addprefix \ + --dex-file=,$(TARGET_CORE_DEX_FILES)) $(addprefix --dex-location=,$(TARGET_CORE_DEX_LOCATIONS)) --oat-file=$(TARGET_CORE_OAT_OUT) \ + --oat-location=$(TARGET_CORE_OAT) --image=$(TARGET_CORE_IMG_OUT) --base=$(IMG_TARGET_BASE_ADDRESS) \ + --instruction-set=$(TARGET_ARCH) --instruction-set-features=$(TARGET_INSTRUCTION_SET_FEATURES) --host-prefix=$(PRODUCT_OUT) --android-root=$(PRODUCT_OUT)/system $(HOST_CORE_OAT_OUT): $(HOST_CORE_IMG_OUT) @@ -110,7 +121,10 @@ $(TARGET_BOOT_IMG_OUT): $(TARGET_BOOT_DEX_FILES) $(DEX2OAT_DEPENDENCY) @echo "target dex2oat: $@ ($?)" @mkdir -p $(dir $@) @mkdir -p $(dir $(TARGET_BOOT_OAT_UNSTRIPPED_OUT)) - $(hide) $(DEX2OAT) $(PARALLEL_ART_COMPILE_JOBS) --runtime-arg -Xms256m --runtime-arg -Xmx256m --image-classes=$(PRELOADED_CLASSES) $(addprefix --dex-file=,$(TARGET_BOOT_DEX_FILES)) $(addprefix --dex-location=,$(TARGET_BOOT_DEX_LOCATIONS)) --oat-symbols=$(TARGET_BOOT_OAT_UNSTRIPPED_OUT) --oat-file=$(TARGET_BOOT_OAT_OUT) --oat-location=$(TARGET_BOOT_OAT) --image=$(TARGET_BOOT_IMG_OUT) --base=$(IMG_TARGET_BASE_ADDRESS) --instruction-set=$(TARGET_ARCH) --host-prefix=$(PRODUCT_OUT) --android-root=$(PRODUCT_OUT)/system + $(hide) $(DEX2OAT) $(PARALLEL_ART_COMPILE_JOBS) --runtime-arg -Xms256m --runtime-arg -Xmx256m --image-classes=$(PRELOADED_CLASSES) $(addprefix --dex-file=,$(TARGET_BOOT_DEX_FILES)) $(addprefix --dex-location=,$(TARGET_BOOT_DEX_LOCATIONS)) \ + --oat-symbols=$(TARGET_BOOT_OAT_UNSTRIPPED_OUT) --oat-file=$(TARGET_BOOT_OAT_OUT) \ + --oat-location=$(TARGET_BOOT_OAT) --image=$(TARGET_BOOT_IMG_OUT) --base=$(IMG_TARGET_BASE_ADDRESS) \ + --instruction-set=$(TARGET_ARCH) --instruction-set-features=$(TARGET_INSTRUCTION_SET_FEATURES) --host-prefix=$(PRODUCT_OUT) --android-root=$(PRODUCT_OUT)/system $(TARGET_BOOT_OAT_UNSTRIPPED_OUT): $(TARGET_BOOT_IMG_OUT) diff --git a/compiler/dex/compiler_ir.h b/compiler/dex/compiler_ir.h index 0d7209e438d..fd46975b9a8 100644 --- a/compiler/dex/compiler_ir.h +++ b/compiler/dex/compiler_ir.h @@ -97,6 +97,9 @@ struct CompilationUnit { CompilerBackend compiler_backend; InstructionSet instruction_set; + const InstructionSetFeatures& GetInstructionSetFeatures() { + return compiler_driver->GetInstructionSetFeatures(); + } // TODO: much of this info available elsewhere. Go to the original source? uint16_t num_dalvik_registers; // method->registers_size. const uint16_t* insns; diff --git a/compiler/dex/quick/arm/arm_lir.h b/compiler/dex/quick/arm/arm_lir.h index 2ff7f1ca6f1..ffaaf84503e 100644 --- a/compiler/dex/quick/arm/arm_lir.h +++ b/compiler/dex/quick/arm/arm_lir.h @@ -380,6 +380,8 @@ enum ArmOpcode { kThumb2CmnRR, // cmn [111010110001] rn[19..16] [0000] [1111] [0000] rm[3..0]. kThumb2EorRRR, // eor [111010101000] rn[19..16] [0000] rd[11..8] [0000] rm[3..0]. kThumb2MulRRR, // mul [111110110000] rn[19..16] [1111] rd[11..8] [0000] rm[3..0]. + kThumb2SdivRRR, // sdiv [111110111001] rn[19..16] [1111] rd[11..8] [1111] rm[3..0]. + kThumb2UdivRRR, // udiv [111110111011] rn[19..16] [1111] rd[11..8] [1111] rm[3..0]. kThumb2MnvRR, // mvn [11101010011011110] rd[11-8] [0000] rm[3..0]. kThumb2RsubRRI8, // rsub [111100011100] rn[19..16] [0000] rd[11..8] imm8[7..0]. kThumb2NegRR, // actually rsub rd, rn, #0. diff --git a/compiler/dex/quick/arm/assemble_arm.cc b/compiler/dex/quick/arm/assemble_arm.cc index e8c188c4c9e..3d0f263fad3 100644 --- a/compiler/dex/quick/arm/assemble_arm.cc +++ b/compiler/dex/quick/arm/assemble_arm.cc @@ -687,6 +687,14 @@ const ArmEncodingMap ArmMir2Lir::EncodingMap[kArmLast] = { kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0, kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12, "mul", "!0C, !1C, !2C", 4, kFixupNone), + ENCODING_MAP(kThumb2SdivRRR, 0xfb90f0f0, + kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0, + kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12, + "sdiv", "!0C, !1C, !2C", 4, kFixupNone), + ENCODING_MAP(kThumb2UdivRRR, 0xfbb0f0f0, + kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0, + kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12, + "udiv", "!0C, !1C, !2C", 4, kFixupNone), ENCODING_MAP(kThumb2MnvRR, 0xea6f0000, kFmtBitBlt, 11, 8, kFmtBitBlt, 3, 0, kFmtShift, -1, -1, kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1, diff --git a/compiler/dex/quick/arm/int_arm.cc b/compiler/dex/quick/arm/int_arm.cc index 0a8cbf9cc0a..42bf3d4d008 100644 --- a/compiler/dex/quick/arm/int_arm.cc +++ b/compiler/dex/quick/arm/int_arm.cc @@ -466,14 +466,39 @@ LIR* ArmMir2Lir::GenRegMemCheck(ConditionCode c_code, RegLocation ArmMir2Lir::GenDivRemLit(RegLocation rl_dest, int reg1, int lit, bool is_div) { - LOG(FATAL) << "Unexpected use of GenDivRemLit for Arm"; - return rl_dest; + RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true); + + // Put the literal in a temp. + int lit_temp = AllocTemp(); + LoadConstant(lit_temp, lit); + // Use the generic case for div/rem with arg2 in a register. + // TODO: The literal temp can be freed earlier during a modulus to reduce reg pressure. + rl_result = GenDivRem(rl_result, reg1, lit_temp, is_div); + FreeTemp(lit_temp); + + return rl_result; } RegLocation ArmMir2Lir::GenDivRem(RegLocation rl_dest, int reg1, int reg2, bool is_div) { - LOG(FATAL) << "Unexpected use of GenDivRem for Arm"; - return rl_dest; + RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true); + if (is_div) { + // Simple case, use sdiv instruction. + OpRegRegReg(kOpDiv, rl_result.low_reg, reg1, reg2); + } else { + // Remainder case, use the following code: + // temp = reg1 / reg2 - integer division + // temp = temp * reg2 + // dest = reg1 - temp + + int temp = AllocTemp(); + OpRegRegReg(kOpDiv, temp, reg1, reg2); + OpRegReg(kOpMul, temp, reg2); + OpRegRegReg(kOpSub, rl_result.low_reg, reg1, temp); + FreeTemp(temp); + } + + return rl_result; } bool ArmMir2Lir::GenInlinedMinMaxInt(CallInfo* info, bool is_min) { diff --git a/compiler/dex/quick/arm/utility_arm.cc b/compiler/dex/quick/arm/utility_arm.cc index 3ceeacf5b15..d631cf70478 100644 --- a/compiler/dex/quick/arm/utility_arm.cc +++ b/compiler/dex/quick/arm/utility_arm.cc @@ -395,6 +395,10 @@ LIR* ArmMir2Lir::OpRegRegRegShift(OpKind op, int r_dest, int r_src1, DCHECK_EQ(shift, 0); opcode = kThumb2MulRRR; break; + case kOpDiv: + DCHECK_EQ(shift, 0); + opcode = kThumb2SdivRRR; + break; case kOpOr: opcode = kThumb2OrrRRR; break; diff --git a/compiler/dex/quick/gen_common.cc b/compiler/dex/quick/gen_common.cc index 2b3404a9c75..df6493dc779 100644 --- a/compiler/dex/quick/gen_common.cc +++ b/compiler/dex/quick/gen_common.cc @@ -1307,6 +1307,7 @@ void Mir2Lir::GenArithOpInt(Instruction::Code opcode, RegLocation rl_dest, } StoreValue(rl_dest, rl_result); } else { + bool done = false; // Set to true if we happen to find a way to use a real instruction. if (cu_->instruction_set == kMips) { rl_src1 = LoadValue(rl_src1, kCoreReg); rl_src2 = LoadValue(rl_src2, kCoreReg); @@ -1314,7 +1315,23 @@ void Mir2Lir::GenArithOpInt(Instruction::Code opcode, RegLocation rl_dest, GenImmedCheck(kCondEq, rl_src2.low_reg, 0, kThrowDivZero); } rl_result = GenDivRem(rl_dest, rl_src1.low_reg, rl_src2.low_reg, op == kOpDiv); - } else { + done = true; + } else if (cu_->instruction_set == kThumb2) { + if (cu_->GetInstructionSetFeatures().HasDivideInstruction()) { + // Use ARM SDIV instruction for division. For remainder we also need to + // calculate using a MUL and subtract. + rl_src1 = LoadValue(rl_src1, kCoreReg); + rl_src2 = LoadValue(rl_src2, kCoreReg); + if (check_zero) { + GenImmedCheck(kCondEq, rl_src2.low_reg, 0, kThrowDivZero); + } + rl_result = GenDivRem(rl_dest, rl_src1.low_reg, rl_src2.low_reg, op == kOpDiv); + done = true; + } + } + + // If we haven't already generated the code use the callout function. + if (!done) { ThreadOffset func_offset = QUICK_ENTRYPOINT_OFFSET(pIdivmod); FlushAllRegs(); /* Send everything to home location */ LoadValueDirectFixed(rl_src2, TargetReg(kArg1)); @@ -1323,7 +1340,7 @@ void Mir2Lir::GenArithOpInt(Instruction::Code opcode, RegLocation rl_dest, if (check_zero) { GenImmedCheck(kCondEq, TargetReg(kArg1), 0, kThrowDivZero); } - // NOTE: callout here is not a safepoint + // NOTE: callout here is not a safepoint. CallHelper(r_tgt, func_offset, false /* not a safepoint */); if (op == kOpDiv) rl_result = GetReturn(false); @@ -1561,11 +1578,24 @@ void Mir2Lir::GenArithOpIntLit(Instruction::Code opcode, RegLocation rl_dest, Re if (HandleEasyDivRem(opcode, is_div, rl_src, rl_dest, lit)) { return; } + + bool done = false; if (cu_->instruction_set == kMips) { rl_src = LoadValue(rl_src, kCoreReg); rl_result = GenDivRemLit(rl_dest, rl_src.low_reg, lit, is_div); - } else { - FlushAllRegs(); /* Everything to home location */ + done = true; + } else if (cu_->instruction_set == kThumb2) { + if (cu_->GetInstructionSetFeatures().HasDivideInstruction()) { + // Use ARM SDIV instruction for division. For remainder we also need to + // calculate using a MUL and subtract. + rl_src = LoadValue(rl_src, kCoreReg); + rl_result = GenDivRemLit(rl_dest, rl_src.low_reg, lit, is_div); + done = true; + } + } + + if (!done) { + FlushAllRegs(); /* Everything to home location. */ LoadValueDirectFixed(rl_src, TargetReg(kArg0)); Clobber(TargetReg(kArg0)); ThreadOffset func_offset = QUICK_ENTRYPOINT_OFFSET(pIdivmod); @@ -1583,7 +1613,7 @@ void Mir2Lir::GenArithOpIntLit(Instruction::Code opcode, RegLocation rl_dest, Re } rl_src = LoadValue(rl_src, kCoreReg); rl_result = EvalLoc(rl_dest, kCoreReg, true); - // Avoid shifts by literal 0 - no support in Thumb. Change to copy + // Avoid shifts by literal 0 - no support in Thumb. Change to copy. if (shift_op && (lit == 0)) { OpRegCopy(rl_result.low_reg, rl_src.low_reg); } else { diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc index 783c3227a69..4871e162a37 100644 --- a/compiler/driver/compiler_driver.cc +++ b/compiler/driver/compiler_driver.cc @@ -336,10 +336,12 @@ extern "C" void compilerLLVMSetBitcodeFileName(art::CompilerDriver& driver, std::string const& filename); CompilerDriver::CompilerDriver(CompilerBackend compiler_backend, InstructionSet instruction_set, + InstructionSetFeatures instruction_set_features, bool image, DescriptorSet* image_classes, size_t thread_count, bool dump_stats) : compiler_backend_(compiler_backend), instruction_set_(instruction_set), + instruction_set_features_(instruction_set_features), freezing_constructor_lock_("freezing constructor lock"), compiled_classes_lock_("compiled classes lock"), compiled_methods_lock_("compiled method lock"), diff --git a/compiler/driver/compiler_driver.h b/compiler/driver/compiler_driver.h index 9b9a8843399..9321f065263 100644 --- a/compiler/driver/compiler_driver.h +++ b/compiler/driver/compiler_driver.h @@ -91,6 +91,7 @@ class CompilerDriver { // can assume will be in the image, with NULL implying all available // classes. explicit CompilerDriver(CompilerBackend compiler_backend, InstructionSet instruction_set, + InstructionSetFeatures instruction_set_features, bool image, DescriptorSet* image_classes, size_t thread_count, bool dump_stats); @@ -104,10 +105,14 @@ class CompilerDriver { void CompileOne(const mirror::ArtMethod* method, base::TimingLogger& timings) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - InstructionSet GetInstructionSet() const { + const InstructionSet& GetInstructionSet() const { return instruction_set_; } + const InstructionSetFeatures& GetInstructionSetFeatures() const { + return instruction_set_features_; + } + CompilerBackend GetCompilerBackend() const { return compiler_backend_; } @@ -386,7 +391,8 @@ class CompilerDriver { CompilerBackend compiler_backend_; - InstructionSet instruction_set_; + const InstructionSet instruction_set_; + const InstructionSetFeatures instruction_set_features_; // All class references that require mutable ReaderWriterMutex freezing_constructor_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER; diff --git a/compiler/oat_test.cc b/compiler/oat_test.cc index 815bca5c5a7..6213b45c410 100644 --- a/compiler/oat_test.cc +++ b/compiler/oat_test.cc @@ -76,7 +76,10 @@ TEST_F(OatTest, WriteRead) { CompilerBackend compiler_backend = kQuick; #endif InstructionSet insn_set = kIsTargetBuild ? kThumb2 : kX86; - compiler_driver_.reset(new CompilerDriver(compiler_backend, insn_set, false, NULL, 2, true)); + + InstructionSetFeatures insn_features; + compiler_driver_.reset(new CompilerDriver(compiler_backend, insn_set, + insn_features, false, NULL, 2, true)); jobject class_loader = NULL; if (kCompile) { base::TimingLogger timings("OatTest::WriteRead", false, false); @@ -149,17 +152,19 @@ TEST_F(OatTest, WriteRead) { TEST_F(OatTest, OatHeaderSizeCheck) { // If this test is failing and you have to update these constants, // it is time to update OatHeader::kOatVersion - EXPECT_EQ(72U, sizeof(OatHeader)); + EXPECT_EQ(76U, sizeof(OatHeader)); EXPECT_EQ(28U, sizeof(OatMethodOffsets)); } TEST_F(OatTest, OatHeaderIsValid) { InstructionSet instruction_set = kX86; + InstructionSetFeatures instruction_set_features; std::vector dex_files; uint32_t image_file_location_oat_checksum = 0; uint32_t image_file_location_oat_begin = 0; const std::string image_file_location; OatHeader oat_header(instruction_set, + instruction_set_features, &dex_files, image_file_location_oat_checksum, image_file_location_oat_begin, diff --git a/compiler/oat_writer.cc b/compiler/oat_writer.cc index 28355bfc746..f3bb11272ec 100644 --- a/compiler/oat_writer.cc +++ b/compiler/oat_writer.cc @@ -98,6 +98,7 @@ OatWriter::~OatWriter() { size_t OatWriter::InitOatHeader() { // create the OatHeader oat_header_ = new OatHeader(compiler_driver_->GetInstructionSet(), + compiler_driver_->GetInstructionSetFeatures(), dex_files_, image_file_location_oat_checksum_, image_file_location_oat_begin_, diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc index 98c62cec663..14723376152 100644 --- a/dex2oat/dex2oat.cc +++ b/dex2oat/dex2oat.cc @@ -131,6 +131,10 @@ static void Usage(const char* fmt, ...) { UsageError(" Example: --instruction-set=x86"); UsageError(" Default: arm"); UsageError(""); + UsageError(" --instruction-set-features=...,: Specify instruction set features"); + UsageError(" Example: --instruction-set-features=div"); + UsageError(" Default: default"); + UsageError(""); UsageError(" --compiler-backend=(Quick|QuickGBC|Portable): select compiler backend"); UsageError(" set."); UsageError(" Example: --compiler-backend=Portable"); @@ -155,13 +159,15 @@ class Dex2Oat { Runtime::Options& options, CompilerBackend compiler_backend, InstructionSet instruction_set, + InstructionSetFeatures instruction_set_features, size_t thread_count) SHARED_TRYLOCK_FUNCTION(true, Locks::mutator_lock_) { if (!CreateRuntime(options, instruction_set)) { *p_dex2oat = NULL; return false; } - *p_dex2oat = new Dex2Oat(Runtime::Current(), compiler_backend, instruction_set, thread_count); + *p_dex2oat = new Dex2Oat(Runtime::Current(), compiler_backend, instruction_set, + instruction_set_features, thread_count); return true; } @@ -257,6 +263,7 @@ class Dex2Oat { UniquePtr driver(new CompilerDriver(compiler_backend_, instruction_set_, + instruction_set_features_, image, image_classes.release(), thread_count_, @@ -330,9 +337,11 @@ class Dex2Oat { explicit Dex2Oat(Runtime* runtime, CompilerBackend compiler_backend, InstructionSet instruction_set, + InstructionSetFeatures instruction_set_features, size_t thread_count) : compiler_backend_(compiler_backend), instruction_set_(instruction_set), + instruction_set_features_(instruction_set_features), runtime_(runtime), thread_count_(thread_count), start_ns_(NanoTime()) { @@ -391,6 +400,7 @@ class Dex2Oat { const CompilerBackend compiler_backend_; const InstructionSet instruction_set_; + const InstructionSetFeatures instruction_set_features_; Runtime* runtime_; size_t thread_count_; @@ -559,6 +569,32 @@ class WatchDog { const unsigned int WatchDog::kWatchDogWarningSeconds; const unsigned int WatchDog::kWatchDogTimeoutSeconds; +// Given a set of instruction features from the build, parse it. The +// input 'str' is a comma separated list of feature names. Parse it and +// return the InstructionSetFeatures object. +static InstructionSetFeatures ParseFeatureList(std::string str) { + InstructionSetFeatures result; + typedef std::vector FeatureList; + FeatureList features; + Split(str, ',', features); + for (FeatureList::iterator i = features.begin(); i != features.end(); i++) { + std::string feature = Trim(*i); + if (feature == "default") { + // Nothing to do. + } else if (feature == "div") { + // Supports divide instruction. + result.SetHasDivideInstruction(true); + } else if (feature == "nodiv") { + // Turn off support for divide instruction. + result.SetHasDivideInstruction(false); + } else { + Usage("Unknown instruction set feature: '%s'", feature.c_str()); + } + } + // others... + return result; +} + static int dex2oat(int argc, char** argv) { base::TimingLogger timings("compiler", false, false); @@ -595,6 +631,15 @@ static int dex2oat(int argc, char** argv) { #else CompilerBackend compiler_backend = kQuick; #endif + + // Take the default set of instruction features from the build if present. + InstructionSetFeatures instruction_set_features = +#ifdef ART_DEFAULT_INSTRUCTION_SET_FEATURES + ParseFeatureList(STRINGIFY(ART_DEFAULT_INSTRUCTION_SET_FEATURES)); +#else + ParseFeatureList("default"); +#endif + #if defined(__arm__) InstructionSet instruction_set = kThumb2; #elif defined(__i386__) @@ -604,6 +649,8 @@ static int dex2oat(int argc, char** argv) { #else #error "Unsupported architecture" #endif + + bool is_host = false; bool dump_stats = false; bool dump_timing = false; @@ -678,6 +725,9 @@ static int dex2oat(int argc, char** argv) { } else if (instruction_set_str == "x86") { instruction_set = kX86; } + } else if (option.starts_with("--instruction-set-features=")) { + StringPiece str = option.substr(strlen("--instruction-set-features=")).data(); + instruction_set_features = ParseFeatureList(str.as_string()); } else if (option.starts_with("--compiler-backend=")) { StringPiece backend_str = option.substr(strlen("--compiler-backend=")).data(); if (backend_str == "Quick") { @@ -870,7 +920,8 @@ static int dex2oat(int argc, char** argv) { #endif Dex2Oat* p_dex2oat; - if (!Dex2Oat::Create(&p_dex2oat, options, compiler_backend, instruction_set, thread_count)) { + if (!Dex2Oat::Create(&p_dex2oat, options, compiler_backend, instruction_set, + instruction_set_features, thread_count)) { LOG(ERROR) << "Failed to create dex2oat"; return EXIT_FAILURE; } @@ -1093,8 +1144,6 @@ static int dex2oat(int argc, char** argv) { return EXIT_SUCCESS; } - - } // namespace art int main(int argc, char** argv) { diff --git a/disassembler/disassembler_arm.cc b/disassembler/disassembler_arm.cc index 6239e9abb71..8d4f3ce2630 100644 --- a/disassembler/disassembler_arm.cc +++ b/disassembler/disassembler_arm.cc @@ -374,7 +374,102 @@ size_t DisassemblerArm::DumpThumb32(std::ostream& os, const uint8_t* instr_ptr) // uint32_t op5 = (instr >> 4) & 0xF; ArmRegister Rn(instr, 16); ArmRegister Rt(instr, 12); + ArmRegister Rd(instr, 8); uint32_t imm8 = instr & 0xFF; + if ((op3 & 2) == 2) { // 1x + int W = (instr >> 21) & 1; + int U = (instr >> 23) & 1; + int P = (instr >> 24) & 1; + + if ((op4 & 1) == 1) { + opcode << "ldrd"; + } else { + opcode << "strd"; + } + args << Rt << "," << Rd << ", [" << Rn; + const char *sign = U ? "+" : "-"; + if (P == 0 && W == 1) { + args << "], #" << sign << imm8; + } else { + args << ", #" << sign << imm8 << "]"; + if (W == 1) { + args << "!"; + } + } + } else { // 0x + switch (op4) { + case 0: + if (op3 == 0) { // op3 is 00, op4 is 00 + opcode << "strex"; + args << Rd << ", " << Rt << ", [" << Rn << ", #" << (imm8 << 2) << "]"; + } else { // op3 is 01, op4 is 00 + // this is one of strexb, strexh or strexd + int op5 = (instr >> 4) & 0xf; + switch (op5) { + case 4: + opcode << "strexb"; + break; + case 5: + opcode << "strexh"; + break; + case 7: + opcode << "strexd"; + break; + } + } + break; + case 1: + if (op3 == 0) { // op3 is 00, op4 is 01 + opcode << "ldrex"; + args << Rt << ", [" << Rn << ", #" << (imm8 << 2) << "]"; + } else { // op3 is 01, op4 is 01 + // this is one of strexb, strexh or strexd + int op5 = (instr >> 4) & 0xf; + switch (op5) { + case 0: + opcode << "tbb"; + break; + case 1: + opcode << "tbh"; + break; + case 4: + opcode << "ldrexb"; + break; + case 5: + opcode << "ldrexh"; + break; + case 7: + opcode << "ldrexd"; + break; + } + } + break; + case 2: // op3 is 0x, op4 is 10 + case 3: // op3 is 0x, op4 is 11 + if (op4 == 2) { + opcode << "strd"; + } else { + opcode << "ldrd"; + } + int W = (instr >> 21) & 1; + int U = (instr >> 23) & 1; + int P = (instr >> 24) & 1; + + args << Rt << "," << Rd << ", [" << Rn; + const char *sign = U ? "+" : "-"; + if (P == 0 && W == 1) { + args << "], #" << sign << imm8; + } else { + args << ", #" << sign << imm8 << "]"; + if (W == 1) { + args << "!"; + } + } + break; + } + } + + if (op3 == 0 && op4 == 0) { // STREX ArmRegister Rd(instr, 8); opcode << "strex"; @@ -519,19 +614,11 @@ size_t DisassemblerArm::DumpThumb32(std::ostream& os, const uint8_t* instr_ptr) uint32_t op3 = (instr >> 20) & 0x3F; uint32_t coproc = (instr >> 8) & 0xF; uint32_t op4 = (instr >> 4) & 0x1; - if ((op3 == 2 || op3 == 2 || op3 == 6 || op3 == 7) || // 00x1x - (op3 >= 8 && op3 <= 15) || (op3 >= 16 && op3 <= 31)) { // 001xxx, 01xxxx - // Extension register load/store instructions - // |111|1|110|00000|0000|1111|110|000000000| - // |5 3|2|109|87654|3 0|54 2|10 |87 54 0| - // |---|-|---|-----|----|----|---|---------| - // |332|2|222|22222|1111|1111|110|000000000| - // |1 9|8|765|43210|9 6|54 2|10 |87 54 0| - // |---|-|---|-----|----|----|---|---------| - // |111|T|110| op3 | Rn | |101| | - // 111 0 110 01001 0011 0000 101 000000011 - ec930a03 - if (op3 == 9 || op3 == 0xD) { // VLDM - // 1110 110 PUDW1 nnnn dddd 101S iiii iiii + + if (coproc == 10 || coproc == 11) { // 101x + if (op3 < 0x20 && (op3 >> 1) != 2) { // 0xxxxx and not 00010x + // extension load/store instructions + int op = op3 & 0x1f; uint32_t P = (instr >> 24) & 1; uint32_t U = (instr >> 23) & 1; uint32_t D = (instr >> 22) & 1; @@ -541,20 +628,49 @@ size_t DisassemblerArm::DumpThumb32(std::ostream& os, const uint8_t* instr_ptr) uint32_t Vd = (instr >> 12) & 0xF; uint32_t imm8 = instr & 0xFF; uint32_t d = (S == 0 ? ((Vd << 1) | D) : (Vd | (D << 4))); - if (P == 0 && U == 0 && W == 0) { - // TODO: 64bit transfers between ARM core and extension registers. - } else if (P == 0 && U == 1 && Rn.r == 13) { // VPOP - opcode << "vpop" << (S == 0 ? ".f64" : ".f32"); - args << d << " .. " << (d + imm8); - } else if (P == 1 && W == 0) { // VLDR - opcode << "vldr" << (S == 0 ? ".f64" : ".f32"); - args << d << ", [" << Rn << ", #" << imm8 << "]"; - } else { // VLDM - opcode << "vldm" << (S == 0 ? ".f64" : ".f32"); - args << Rn << ", " << d << " .. " << (d + imm8); + ArmRegister Rd(d, 0); + + if (op == 8 || op == 12 || op == 10 || op == 14 || + op == 18 || op == 22) { // 01x00 or 01x10 + // vector store multiple or vpush + if (P == 1 && U == 0 && W == 1 && Rn.r == 13) { + opcode << "vpush" << (S == 0 ? ".f64" : ".f32"); + args << Rd << " .. " << (Rd.r + imm8); + } else { + opcode << "vstm" << (S == 0 ? ".f64" : ".f32"); + args << Rn << ", " << Rd << " .. " << (Rd.r + imm8); + } + } else if (op == 16 || op == 20 || op == 24 || op == 28) { + // 1xx00 + // vector store register + opcode << "vstr" << (S == 0 ? ".f64" : ".f32"); + args << Rd << ", [" << Rn << ", #" << imm8 << "]"; + } else if (op == 17 || op == 21 || op == 25 || op == 29) { + // 1xx01 + // vector load register + opcode << "vldr" << (S == 0 ? ".f64" : ".f32"); + args << Rd << ", [" << Rn << ", #" << imm8 << "]"; + } else if (op == 9 || op == 13 || op == 11 || op == 15 || + op == 19 || op == 23 ) { // 01x11 10x11 + // vldm or vpop + if (P == 1 && U == 0 && W == 1 && Rn.r == 13) { + opcode << "vpop" << (S == 0 ? ".f64" : ".f32"); + args << Rd << " .. " << (Rd.r + imm8); + } else { + opcode << "vldm" << (S == 0 ? ".f64" : ".f32"); + args << Rn << ", " << Rd << " .. " << (Rd.r + imm8); + } } + } else if ((op3 >> 1) == 2) { // 00010x + // 64 bit transfers + } else if ((op3 >> 4) == 2 && op4 == 0) { // 10xxxx, op = 0 + // fp data processing + } else if ((op3 >> 4) == 2 && op4 == 1) { // 10xxxx, op = 1 + // 8,16,32 bit transfers } - } else if ((op3 & 0x30) == 0x20 && op4 == 0) { // 10 xxxx ... 0 + } + + if ((op3 & 0x30) == 0x20 && op4 == 0) { // 10 xxxx ... 0 if ((coproc & 0xE) == 0xA) { // VFP data-processing instructions // |111|1|1100|0000|0000|1111|110|0|00 |0|0|0000| @@ -1070,6 +1186,72 @@ size_t DisassemblerArm::DumpThumb32(std::ostream& os, const uint8_t* instr_ptr) } break; } + default: // more formats + if ((op2 >> 4) == 2) { // 010xxxx + // data processing (register) + } else if ((op2 >> 3) == 6) { // 0110xxx + // Multiply, multiply accumulate, and absolute difference + op1 = (instr >> 20) & 0x7; + op2 = (instr >> 4) & 0x2; + ArmRegister Ra(instr, 12); + ArmRegister Rn(instr, 16); + ArmRegister Rm(instr, 0); + ArmRegister Rd(instr, 8); + switch (op1) { + case 0: + if (op2 == 0) { + if (Ra.r == 0xf) { + opcode << "mul"; + args << Rd << ", " << Rn << ", " << Rm; + } else { + opcode << "mla"; + args << Rd << ", " << Rn << ", " << Rm << ", " << Ra; + } + } else { + opcode << "mls"; + args << Rd << ", " << Rn << ", " << Rm << ", " << Ra; + } + break; + case 1: + case 2: + case 3: + case 4: + case 5: + case 6: + break; // do these sometime + } + } else if ((op2 >> 3) == 7) { // 0111xxx + // Long multiply, long multiply accumulate, and divide + op1 = (instr >> 20) & 0x7; + op2 = (instr >> 4) & 0xf; + ArmRegister Rn(instr, 16); + ArmRegister Rm(instr, 0); + ArmRegister Rd(instr, 8); + ArmRegister RdHi(instr, 8); + ArmRegister RdLo(instr, 12); + switch (op1) { + case 0: + opcode << "smull"; + args << RdLo << ", " << RdHi << ", " << Rn << ", " << Rm; + break; + case 1: + opcode << "sdiv"; + args << Rd << ", " << Rn << ", " << Rm; + break; + case 2: + opcode << "umull"; + args << RdLo << ", " << RdHi << ", " << Rn << ", " << Rm; + break; + case 3: + opcode << "udiv"; + args << Rd << ", " << Rn << ", " << Rm; + break; + case 4: + case 5: + case 6: + break; // TODO: when we generate these... + } + } } default: break; diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc index 3a32ff14bda..b9716d556f5 100644 --- a/oatdump/oatdump.cc +++ b/oatdump/oatdump.cc @@ -118,6 +118,9 @@ class OatDumper { os << "INSTRUCTION SET:\n"; os << oat_header.GetInstructionSet() << "\n\n"; + os << "INSTRUCTION SET FEATURES:\n"; + os << oat_header.GetInstructionSetFeatures().GetFeatureString() << "\n\n"; + os << "DEX FILE COUNT:\n"; os << oat_header.GetDexFileCount() << "\n\n"; diff --git a/runtime/Android.mk b/runtime/Android.mk index 3d275e63c09..bef4381c2b1 100644 --- a/runtime/Android.mk +++ b/runtime/Android.mk @@ -187,6 +187,7 @@ LIBART_TARGET_SRC_FILES += \ arch/arm/jni_entrypoints_arm.S \ arch/arm/portable_entrypoints_arm.S \ arch/arm/quick_entrypoints_arm.S \ + arch/arm/arm_sdiv.S \ arch/arm/thread_arm.cc else # TARGET_ARCH != arm ifeq ($(TARGET_ARCH),x86) diff --git a/runtime/arch/arm/arm_sdiv.S b/runtime/arch/arm/arm_sdiv.S new file mode 100644 index 00000000000..925e4284443 --- /dev/null +++ b/runtime/arch/arm/arm_sdiv.S @@ -0,0 +1,24 @@ +// This function is used to check for the CPU's support for the sdiv +// instruction at runtime. It will either return the value 1 or +// will cause an invalid instruction trap (SIGILL signal). The +// caller must arrange for the signal handler to set the r0 +// register to 0 and move the pc forward by 4 bytes (to skip +// the invalid instruction). + + +#include "asm_support_arm.S" + +.section .text +ENTRY CheckForARMSDIVInstruction + mov r1,#1 + // depending on the architecture, the assembler will not allow an + // sdiv instruction, so we will have to output the bytes directly. + + // sdiv r0,r1,r1 is two words: 0xfb91 0xf1f0. We need little endian. + .byte 0x91,0xfb,0xf1,0xf0 + + // if the divide worked, r0 will have the value #1 (result of sdiv). + // It will have 0 otherwise (set by the signal handler) + // the value is just returned from this function. + bx lr + END CheckForARMSDIVInstruction diff --git a/runtime/base/macros.h b/runtime/base/macros.h index d00c64a4abe..00a530a206f 100644 --- a/runtime/base/macros.h +++ b/runtime/base/macros.h @@ -130,6 +130,10 @@ char (&ArraySizeHelper(T (&array)[N]))[N]; #define LIKELY(x) __builtin_expect((x), true) #define UNLIKELY(x) __builtin_expect((x), false) +// Stringify the argument. +#define QUOTE(x) #x +#define STRINGIFY(x) QUOTE(x) + #ifndef NDEBUG #define ALWAYS_INLINE #else diff --git a/runtime/common_test.h b/runtime/common_test.h index 673a03b355b..79fa6803925 100644 --- a/runtime/common_test.h +++ b/runtime/common_test.h @@ -22,6 +22,7 @@ #include #include #include +#include #include "../../external/icu4c/common/unicode/uvernum.h" #include "base/macros.h" @@ -152,6 +153,113 @@ class ScratchFile { UniquePtr file_; }; +#if defined(__arm__) + + +#include +#include +#include + + +// A signal handler called when have an illegal instruction. We record the fact in +// a global boolean and then increment the PC in the signal context to return to +// the next instruction. We know the instruction is an sdiv (4 bytes long). +static void baddivideinst(int signo, siginfo *si, void *data) { + (void)signo; + (void)si; + struct ucontext *uc = (struct ucontext *)data; + struct sigcontext *sc = &uc->uc_mcontext; + sc->arm_r0 = 0; // set R0 to #0 to signal error + sc->arm_pc += 4; // skip offending instruction +} + +// This is in arch/arm/arm_sdiv.S. It does the following: +// mov r1,#1 +// sdiv r0,r1,r1 +// bx lr +// +// the result will be the value 1 if sdiv is supported. If it is not supported +// a SIGILL signal will be raised and the signal handler (baddivideinst) called. +// The signal handler sets r0 to #0 and then increments pc beyond the failed instruction. +// Thus if the instruction is not supported, the result of this function will be #0 + +extern "C" bool CheckForARMSDIVInstruction(); + +static InstructionSetFeatures GuessInstructionFeatures() { + InstructionSetFeatures f; + + // Uncomment this for processing of /proc/cpuinfo. + if (false) { + // Look in /proc/cpuinfo for features we need. Only use this when we can guarantee that + // the kernel puts the appropriate feature flags in here. Sometimes it doesn't. + std::ifstream in("/proc/cpuinfo"); + if (in) { + while (!in.eof()) { + std::string line; + std::getline(in, line); + if (!in.eof()) { + if (line.find("Features") != std::string::npos) { + if (line.find("idivt") != std::string::npos) { + f.SetHasDivideInstruction(true); + } + } + } + in.close(); + } + } else { + LOG(INFO) << "Failed to open /proc/cpuinfo"; + } + } + + // See if have a sdiv instruction. Register a signal handler and try to execute + // an sdiv instruction. If we get a SIGILL then it's not supported. We can't use + // the /proc/cpuinfo method for this because Krait devices don't always put the idivt + // feature in the list. + struct sigaction sa, osa; + sa.sa_flags = SA_ONSTACK | SA_RESTART | SA_SIGINFO; + sa.sa_sigaction = baddivideinst; + sigaction(SIGILL, &sa, &osa); + + if (CheckForARMSDIVInstruction()) { + f.SetHasDivideInstruction(true); + } + + // Restore the signal handler. + sigaction(SIGILL, &osa, NULL); + + // Other feature guesses in here. + return f; +} + +#endif + +// Given a set of instruction features from the build, parse it. The +// input 'str' is a comma separated list of feature names. Parse it and +// return the InstructionSetFeatures object. +static InstructionSetFeatures ParseFeatureList(std::string str) { + LOG(INFO) << "Parsing features " << str; + InstructionSetFeatures result; + typedef std::vector FeatureList; + FeatureList features; + Split(str, ',', features); + for (FeatureList::iterator i = features.begin(); i != features.end(); i++) { + std::string feature = Trim(*i); + if (feature == "default") { + // Nothing to do. + } else if (feature == "div") { + // Supports divide instruction. + result.SetHasDivideInstruction(true); + } else if (feature == "nodiv") { + // Turn off support for divide instruction. + result.SetHasDivideInstruction(false); + } else { + LOG(FATAL) << "Unknown instruction set feature: '" << feature << "'"; + } + } + // Others... + return result; +} + class CommonTest : public testing::Test { public: static void MakeExecutable(const mirror::ByteArray* code_array) { @@ -314,8 +422,22 @@ class CommonTest : public testing::Test { class_linker_ = runtime_->GetClassLinker(); InstructionSet instruction_set = kNone; + + // take the default set of instruction features from the build if present + InstructionSetFeatures instruction_set_features = +#ifdef ART_DEFAULT_INSTRUCTION_SET_FEATURES + ParseFeatureList(STRINGIFY(ART_DEFAULT_INSTRUCTION_SET_FEATURES)); +#else + ParseFeatureList("default"); +#endif + #if defined(__arm__) instruction_set = kThumb2; + InstructionSetFeatures runtime_features = GuessInstructionFeatures(); + + // for ARM, do a runtime check to make sure that the features we are passed from + // the build match the features we actually determine at runtime. + ASSERT_EQ(instruction_set_features, runtime_features); #elif defined(__mips__) instruction_set = kMips; #elif defined(__i386__) @@ -338,6 +460,7 @@ class CommonTest : public testing::Test { } class_linker_->FixupDexCaches(runtime_->GetResolutionMethod()); compiler_driver_.reset(new CompilerDriver(compiler_backend, instruction_set, + instruction_set_features, true, new CompilerDriver::DescriptorSet, 2, true)); } @@ -568,7 +691,6 @@ class CheckJniAbortCatcher { #else #define TEST_DISABLED_FOR_PORTABLE() #endif - } // namespace art namespace std { diff --git a/runtime/instruction_set.h b/runtime/instruction_set.h index 2217f7f76e5..aee7447fafb 100644 --- a/runtime/instruction_set.h +++ b/runtime/instruction_set.h @@ -18,6 +18,9 @@ #define ART_RUNTIME_INSTRUCTION_SET_H_ #include +#include + +#include "base/macros.h" namespace art { @@ -29,6 +32,53 @@ enum InstructionSet { kMips }; +enum InstructionFeatures { + kHwDiv = 1 // Supports hardware divide. +}; + +// This is a bitmask of supported features per architecture. +class PACKED(4) InstructionSetFeatures { + public: + InstructionSetFeatures() : mask_(0) {} + explicit InstructionSetFeatures(uint32_t mask) : mask_(mask) {} + + bool HasDivideInstruction() const { + return (mask_ & kHwDiv) != 0; + } + + void SetHasDivideInstruction(bool v) { + mask_ = (mask_ & ~kHwDiv) | (v ? kHwDiv : 0); + } + + std::string GetFeatureString() const { + std::string result; + if ((mask_ & kHwDiv) != 0) { + result += "div"; + } + if (result.size() == 0) { + result = "none"; + } + return result; + } + + uint32_t get_mask() const { + return mask_; + } + + // Other features in here. + + bool operator==(const InstructionSetFeatures &peer) const { + return mask_ == peer.mask_; + } + + bool operator!=(const InstructionSetFeatures &peer) const { + return mask_ != peer.mask_; + } + + private: + uint32_t mask_; +}; + std::ostream& operator<<(std::ostream& os, const InstructionSet& rhs); } // namespace art diff --git a/runtime/oat.cc b/runtime/oat.cc index defda6bb1a5..94897950e0e 100644 --- a/runtime/oat.cc +++ b/runtime/oat.cc @@ -22,13 +22,14 @@ namespace art { const uint8_t OatHeader::kOatMagic[] = { 'o', 'a', 't', '\n' }; -const uint8_t OatHeader::kOatVersion[] = { '0', '0', '9', '\0' }; +const uint8_t OatHeader::kOatVersion[] = { '0', '1', '0', '\0' }; OatHeader::OatHeader() { memset(this, 0, sizeof(*this)); } OatHeader::OatHeader(InstructionSet instruction_set, + const InstructionSetFeatures& instruction_set_features, const std::vector* dex_files, uint32_t image_file_location_oat_checksum, uint32_t image_file_location_oat_data_begin, @@ -42,6 +43,9 @@ OatHeader::OatHeader(InstructionSet instruction_set, instruction_set_ = instruction_set; UpdateChecksum(&instruction_set_, sizeof(instruction_set_)); + instruction_set_features_ = instruction_set_features; + UpdateChecksum(&instruction_set_features_, sizeof(instruction_set_features_)); + dex_file_count_ = dex_files->size(); UpdateChecksum(&dex_file_count_, sizeof(dex_file_count_)); @@ -99,6 +103,11 @@ InstructionSet OatHeader::GetInstructionSet() const { return instruction_set_; } +const InstructionSetFeatures& OatHeader::GetInstructionSetFeatures() const { + CHECK(IsValid()); + return instruction_set_features_; +} + uint32_t OatHeader::GetExecutableOffset() const { DCHECK(IsValid()); DCHECK_ALIGNED(executable_offset_, kPageSize); diff --git a/runtime/oat.h b/runtime/oat.h index c864c2cc525..de840b58700 100644 --- a/runtime/oat.h +++ b/runtime/oat.h @@ -32,6 +32,7 @@ class PACKED(4) OatHeader { OatHeader(); OatHeader(InstructionSet instruction_set, + const InstructionSetFeatures& instruction_set_features, const std::vector* dex_files, uint32_t image_file_location_oat_checksum, uint32_t image_file_location_oat_data_begin, @@ -80,6 +81,7 @@ class PACKED(4) OatHeader { void SetQuickToInterpreterBridgeOffset(uint32_t offset); InstructionSet GetInstructionSet() const; + const InstructionSetFeatures& GetInstructionSetFeatures() const; uint32_t GetImageFileLocationOatChecksum() const; uint32_t GetImageFileLocationOatDataBegin() const; uint32_t GetImageFileLocationSize() const; @@ -92,6 +94,7 @@ class PACKED(4) OatHeader { uint32_t adler32_checksum_; InstructionSet instruction_set_; + InstructionSetFeatures instruction_set_features_; uint32_t dex_file_count_; uint32_t executable_offset_; uint32_t interpreter_to_interpreter_bridge_offset_; diff --git a/runtime/utils.cc b/runtime/utils.cc index 9796b996354..e039581b05b 100644 --- a/runtime/utils.cc +++ b/runtime/utils.cc @@ -889,6 +889,35 @@ void Split(const std::string& s, char separator, std::vector& resul } } +std::string Trim(std::string s) { + std::string result; + unsigned int start_index = 0; + unsigned int end_index = s.size() - 1; + + // Skip initial whitespace. + while (start_index < s.size()) { + if (!isspace(s[start_index])) { + break; + } + start_index++; + } + + // Skip terminating whitespace. + while (end_index >= start_index) { + if (!isspace(s[end_index])) { + break; + } + end_index--; + } + + // All spaces, no beef. + if (end_index < start_index) { + return ""; + } + // Start_index is the first non-space, end_index is the last one. + return s.substr(start_index, end_index - start_index + 1); +} + template std::string Join(std::vector& strings, char separator) { if (strings.empty()) { diff --git a/runtime/utils.h b/runtime/utils.h index 51035b697fe..6850e8b0259 100644 --- a/runtime/utils.h +++ b/runtime/utils.h @@ -252,7 +252,7 @@ std::string FormatDuration(uint64_t nano_duration, TimeUnit time_unit); // Get the appropriate unit for a nanosecond duration. TimeUnit GetAppropriateTimeUnit(uint64_t nano_duration); -// Get the divisor to convert from a nanoseconds to a time unit +// Get the divisor to convert from a nanoseconds to a time unit. uint64_t GetNsToTimeUnitDivisor(TimeUnit time_unit); // Performs JNI name mangling as described in section 11.3 "Linking Native Methods" @@ -326,6 +326,9 @@ void InitTimeSpec(bool absolute, int clock, int64_t ms, int32_t ns, timespec* ts // strings. Empty strings will be omitted. void Split(const std::string& s, char separator, std::vector& result); +// Trims whitespace off both ends of the given string. +std::string Trim(std::string s); + // Joins a vector of strings into a single string, using the given separator. template std::string Join(std::vector& strings, char separator); @@ -354,10 +357,10 @@ void DumpNativeStack(std::ostream& os, pid_t tid, const char* prefix = "", bool // Dumps the kernel stack for thread 'tid' to 'os'. Note that this is only available on linux-x86. void DumpKernelStack(std::ostream& os, pid_t tid, const char* prefix = "", bool include_count = true); -// Find $ANDROID_ROOT, /system, or abort +// Find $ANDROID_ROOT, /system, or abort. const char* GetAndroidRoot(); -// Find $ANDROID_DATA, /data, or abort +// Find $ANDROID_DATA, /data, or abort. const char* GetAndroidData(); // Returns the dalvik-cache location, or dies trying. From f7ee11632e3dfda29d553d8962be9747d5ce6dfd Mon Sep 17 00:00:00 2001 From: Stephen Hines Date: Fri, 9 Aug 2013 02:14:36 -0700 Subject: [PATCH 0141/2402] Update ART for LLVM merge up to r187914. Removed NoFramePointerElimNonLeaf because this is now only specified via a function attribute (and thus covered by existing cases). Switch to llvm::sys::fs::F_* enums. Remove unused DisableSimplifyLibCalls(). (cherry picked from commit 1961a2716cf02f597f06c27a0850daa2dc917586) Change-Id: Idc2e3fb73d798a62b6ebf6f55aff5c715b2c62f7 --- compiler/dex/portable/mir_to_gbc.cc | 2 +- compiler/llvm/llvm_compilation_unit.cc | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/compiler/dex/portable/mir_to_gbc.cc b/compiler/dex/portable/mir_to_gbc.cc index 963cbeb1d15..07bd2aa979e 100644 --- a/compiler/dex/portable/mir_to_gbc.cc +++ b/compiler/dex/portable/mir_to_gbc.cc @@ -1970,7 +1970,7 @@ void MirConverter::MethodMIR2Bitcode() { ::llvm::OwningPtr< ::llvm::tool_output_file> out_file( new ::llvm::tool_output_file(fname.c_str(), errmsg, - ::llvm::raw_fd_ostream::F_Binary)); + ::llvm::sys::fs::F_Binary)); if (!errmsg.empty()) { LOG(ERROR) << "Failed to create bitcode output file: " << errmsg; diff --git a/compiler/llvm/llvm_compilation_unit.cc b/compiler/llvm/llvm_compilation_unit.cc index feb495e35fe..038f5dc4eb6 100644 --- a/compiler/llvm/llvm_compilation_unit.cc +++ b/compiler/llvm/llvm_compilation_unit.cc @@ -211,7 +211,6 @@ bool LlvmCompilationUnit::MaterializeToRawOStream(::llvm::raw_ostream& out_strea ::llvm::TargetOptions target_options; target_options.FloatABIType = ::llvm::FloatABI::Soft; target_options.NoFramePointerElim = true; - target_options.NoFramePointerElimNonLeaf = true; target_options.UseSoftFloat = false; target_options.EnableFastISel = false; @@ -255,7 +254,7 @@ bool LlvmCompilationUnit::MaterializeToRawOStream(::llvm::raw_ostream& out_strea ::llvm::OwningPtr< ::llvm::tool_output_file> out_file( new ::llvm::tool_output_file(bitcode_filename_.c_str(), errmsg, - ::llvm::raw_fd_ostream::F_Binary)); + ::llvm::sys::fs::F_Binary)); if (!errmsg.empty()) { @@ -275,7 +274,6 @@ bool LlvmCompilationUnit::MaterializeToRawOStream(::llvm::raw_ostream& out_strea // pm_builder.Inliner = ::llvm::createAlwaysInlinerPass(); // pm_builder.Inliner = ::llvm::createPartialInliningPass(); pm_builder.OptLevel = 3; - pm_builder.DisableSimplifyLibCalls = 1; pm_builder.DisableUnitAtATime = 1; pm_builder.populateFunctionPassManager(fpm); pm_builder.populateModulePassManager(pm); From a3d2718d1fac53210b2a311b1728409d6c8e7b9d Mon Sep 17 00:00:00 2001 From: Brian Carlstrom Date: Tue, 5 Nov 2013 23:22:27 -0800 Subject: [PATCH 0142/2402] Change thread.h to thread-inl.h to pick up missing Thread::Currnet for debug build in master Change-Id: I56a4dd18ec1c212f9dbb73b14c0c0623b23c87bd --- runtime/base/logging.cc | 2 +- runtime/base/timing_logger.cc | 2 +- runtime/gc/collector/garbage_collector.cc | 2 +- runtime/gc/collector/partial_mark_sweep.cc | 2 +- runtime/gc/collector/sticky_mark_sweep.cc | 2 +- runtime/gc/space/large_object_space.cc | 2 +- runtime/native/dalvik_system_Zygote.cc | 2 +- runtime/thread_pool.cc | 2 +- runtime/well_known_classes.cc | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/runtime/base/logging.cc b/runtime/base/logging.cc index 7d54bafd9ca..3d842a06459 100644 --- a/runtime/base/logging.cc +++ b/runtime/base/logging.cc @@ -18,7 +18,7 @@ #include "base/mutex.h" #include "runtime.h" -#include "thread.h" +#include "thread-inl.h" #include "utils.h" namespace art { diff --git a/runtime/base/timing_logger.cc b/runtime/base/timing_logger.cc index 11dc54271eb..6df1126e0a7 100644 --- a/runtime/base/timing_logger.cc +++ b/runtime/base/timing_logger.cc @@ -22,7 +22,7 @@ #include "timing_logger.h" #include "base/logging.h" -#include "thread.h" +#include "thread-inl.h" #include "base/stl_util.h" #include "base/histogram-inl.h" diff --git a/runtime/gc/collector/garbage_collector.cc b/runtime/gc/collector/garbage_collector.cc index edfea9489f9..6691cadbd4c 100644 --- a/runtime/gc/collector/garbage_collector.cc +++ b/runtime/gc/collector/garbage_collector.cc @@ -26,7 +26,7 @@ #include "gc/accounting/heap_bitmap.h" #include "gc/space/large_object_space.h" #include "gc/space/space-inl.h" -#include "thread.h" +#include "thread-inl.h" #include "thread_list.h" namespace art { diff --git a/runtime/gc/collector/partial_mark_sweep.cc b/runtime/gc/collector/partial_mark_sweep.cc index cc3cfe544d9..29367ce0bff 100644 --- a/runtime/gc/collector/partial_mark_sweep.cc +++ b/runtime/gc/collector/partial_mark_sweep.cc @@ -19,7 +19,7 @@ #include "gc/heap.h" #include "gc/space/space.h" #include "partial_mark_sweep.h" -#include "thread.h" +#include "thread-inl.h" namespace art { namespace gc { diff --git a/runtime/gc/collector/sticky_mark_sweep.cc b/runtime/gc/collector/sticky_mark_sweep.cc index 19cbe6bae6f..9f0bf333875 100644 --- a/runtime/gc/collector/sticky_mark_sweep.cc +++ b/runtime/gc/collector/sticky_mark_sweep.cc @@ -18,7 +18,7 @@ #include "gc/space/large_object_space.h" #include "gc/space/space.h" #include "sticky_mark_sweep.h" -#include "thread.h" +#include "thread-inl.h" namespace art { namespace gc { diff --git a/runtime/gc/space/large_object_space.cc b/runtime/gc/space/large_object_space.cc index 1321b193aa0..ab6e42bb2a6 100644 --- a/runtime/gc/space/large_object_space.cc +++ b/runtime/gc/space/large_object_space.cc @@ -22,7 +22,7 @@ #include "UniquePtr.h" #include "image.h" #include "os.h" -#include "thread.h" +#include "thread-inl.h" #include "utils.h" namespace art { diff --git a/runtime/native/dalvik_system_Zygote.cc b/runtime/native/dalvik_system_Zygote.cc index 2acacc99594..d065ee44830 100644 --- a/runtime/native/dalvik_system_Zygote.cc +++ b/runtime/native/dalvik_system_Zygote.cc @@ -35,7 +35,7 @@ #include "ScopedLocalRef.h" #include "ScopedPrimitiveArray.h" #include "ScopedUtfChars.h" -#include "thread.h" +#include "thread-inl.h" #if defined(HAVE_PRCTL) #include diff --git a/runtime/thread_pool.cc b/runtime/thread_pool.cc index 674ab9d9ae0..bb6c47538ec 100644 --- a/runtime/thread_pool.cc +++ b/runtime/thread_pool.cc @@ -19,7 +19,7 @@ #include "base/casts.h" #include "base/stl_util.h" #include "runtime.h" -#include "thread.h" +#include "thread-inl.h" namespace art { diff --git a/runtime/well_known_classes.cc b/runtime/well_known_classes.cc index 8de020a9759..e3946f72092 100644 --- a/runtime/well_known_classes.cc +++ b/runtime/well_known_classes.cc @@ -21,7 +21,7 @@ #include "base/logging.h" #include "mirror/class.h" #include "ScopedLocalRef.h" -#include "thread.h" +#include "thread-inl.h" namespace art { From 1bd2ceb3a8c68ae6ea1f9627b588a7bc7a74487f Mon Sep 17 00:00:00 2001 From: Brian Carlstrom Date: Wed, 6 Nov 2013 00:29:48 -0800 Subject: [PATCH 0143/2402] Make missing DEX2OAT_TARGET_INSTRUCTION_SET_FEATURES an error Change-Id: I3ca23e4db80c8ab8a86da6408cf38daccd4cfaf1 --- build/Android.common.mk | 18 +++++++++++++----- build/Android.executable.mk | 4 ---- build/Android.gtest.mk | 1 - build/Android.oat.mk | 3 --- dex2oat/dex2oat.cc | 8 ++------ runtime/common_test.h | 8 ++------ 6 files changed, 17 insertions(+), 25 deletions(-) diff --git a/build/Android.common.mk b/build/Android.common.mk index 0871884b006..a2c2e1ae5ee 100644 --- a/build/Android.common.mk +++ b/build/Android.common.mk @@ -149,11 +149,8 @@ else IMG_TARGET_BASE_ADDRESS := 0x60000000 endif -ART_HOST_CFLAGS := $(art_cflags) -DANDROID_SMP=1 -DART_BASE_ADDRESS=$(IMG_HOST_BASE_ADDRESS) - -ifeq ($(TARGET_ARCH),x86) -ART_TARGET_CFLAGS += -msse2 -endif +ART_HOST_CFLAGS := $(art_cflags) -DANDROID_SMP=1 -DART_BASE_ADDRESS=$(IMG_HOST_BASE_ADDRESS) +ART_HOST_CFLAGS += -DART_DEFAULT_INSTRUCTION_SET_FEATURES=default ART_TARGET_CFLAGS := $(art_cflags) -DART_TARGET -DART_BASE_ADDRESS=$(IMG_TARGET_BASE_ADDRESS) ifeq ($(TARGET_CPU_SMP),true) @@ -162,6 +159,17 @@ else ART_TARGET_CFLAGS += -DANDROID_SMP=0 endif +# DEX2OAT_TARGET_INSTRUCTION_SET_FEATURES is set in ../build/core/dex_preopt.mk based on +# the TARGET_CPU_VARIANT +ifeq ($(DEX2OAT_TARGET_INSTRUCTION_SET_FEATURES),) +$(error Required DEX2OAT_TARGET_INSTRUCTION_SET_FEATURES is not set) +endif +ART_TARGET_CFLAGS += -DART_DEFAULT_INSTRUCTION_SET_FEATURES=$(DEX2OAT_TARGET_INSTRUCTION_SET_FEATURES) + +ifeq ($(TARGET_ARCH),x86) +ART_TARGET_CFLAGS += -msse2 +endif + # Enable thread-safety for GCC 4.6 on the target but not for GCC 4.7 where this feature was removed. ifneq ($(filter 4.6 4.6.%, $(TARGET_GCC_VERSION)),) ART_TARGET_CFLAGS += -Wthread-safety diff --git a/build/Android.executable.mk b/build/Android.executable.mk index b317d92999a..5cf15be1c17 100644 --- a/build/Android.executable.mk +++ b/build/Android.executable.mk @@ -24,10 +24,6 @@ ifeq ($(ART_USE_PORTABLE_COMPILER),true) ART_EXECUTABLES_CFLAGS += -DART_USE_PORTABLE_COMPILER=1 endif -# add the default instruction set features -ART_EXECUTABLES_CFLAGS += \ - -DART_DEFAULT_INSTRUCTION_SET_FEATURES=$(DEX2OAT_TARGET_INSTRUCTION_SET_FEATURES) - # $(1): executable ("d" will be appended for debug version) # $(2): source # $(3): extra shared libraries diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk index 0d759ce085b..655c7dd01f0 100644 --- a/build/Android.gtest.mk +++ b/build/Android.gtest.mk @@ -86,7 +86,6 @@ ART_TEST_CFLAGS := ifeq ($(ART_USE_PORTABLE_COMPILER),true) ART_TEST_CFLAGS += -DART_USE_PORTABLE_COMPILER=1 endif -ART_TEST_CFLAGS += -DART_DEFAULT_INSTRUCTION_SET_FEATURES=$(DEX2OAT_TARGET_INSTRUCTION_SET_FEATURES) # $(1): target or host # $(2): file name diff --git a/build/Android.oat.mk b/build/Android.oat.mk index 5d355a6d86b..f964346dbd6 100644 --- a/build/Android.oat.mk +++ b/build/Android.oat.mk @@ -57,9 +57,6 @@ TARGET_CORE_OAT_OUT := $(ART_TEST_OUT)/core.oat HOST_CORE_IMG_OUT := $(HOST_OUT_JAVA_LIBRARIES)/core.art TARGET_CORE_IMG_OUT := $(ART_TEST_OUT)/core.art -# DEX2OAT_TARGET_INSTRUCTION_SET_FEATURES is set in ../build/core/dex_preopt.mk based on -# the TARGET_CPU_VARIANT - TARGET_INSTRUCTION_SET_FEATURES := $(DEX2OAT_TARGET_INSTRUCTION_SET_FEATURES) $(HOST_CORE_IMG_OUT): $(HOST_CORE_DEX_FILES) $(DEX2OAT_DEPENDENCY) diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc index 14723376152..5b0653e00f5 100644 --- a/dex2oat/dex2oat.cc +++ b/dex2oat/dex2oat.cc @@ -632,13 +632,9 @@ static int dex2oat(int argc, char** argv) { CompilerBackend compiler_backend = kQuick; #endif - // Take the default set of instruction features from the build if present. + // Take the default set of instruction features from the build. InstructionSetFeatures instruction_set_features = -#ifdef ART_DEFAULT_INSTRUCTION_SET_FEATURES - ParseFeatureList(STRINGIFY(ART_DEFAULT_INSTRUCTION_SET_FEATURES)); -#else - ParseFeatureList("default"); -#endif + ParseFeatureList(STRINGIFY(ART_DEFAULT_INSTRUCTION_SET_FEATURES)); #if defined(__arm__) InstructionSet instruction_set = kThumb2; diff --git a/runtime/common_test.h b/runtime/common_test.h index 79fa6803925..643ed1d89d8 100644 --- a/runtime/common_test.h +++ b/runtime/common_test.h @@ -423,13 +423,9 @@ class CommonTest : public testing::Test { InstructionSet instruction_set = kNone; - // take the default set of instruction features from the build if present + // Take the default set of instruction features from the build. InstructionSetFeatures instruction_set_features = -#ifdef ART_DEFAULT_INSTRUCTION_SET_FEATURES - ParseFeatureList(STRINGIFY(ART_DEFAULT_INSTRUCTION_SET_FEATURES)); -#else - ParseFeatureList("default"); -#endif + ParseFeatureList(STRINGIFY(ART_DEFAULT_INSTRUCTION_SET_FEATURES)); #if defined(__arm__) instruction_set = kThumb2; From fd99576d0fe25e327ca6daf44e219268a1d4779f Mon Sep 17 00:00:00 2001 From: Vladimir Marko Date: Wed, 6 Nov 2013 16:36:36 +0000 Subject: [PATCH 0144/2402] Fix DexFile error handling to close fd properly. Change-Id: I20bf4ec6fdd6e8ced432d12886670537a2952eee --- runtime/dex_file.cc | 67 +++++++++++++++++++++++---------------------- 1 file changed, 34 insertions(+), 33 deletions(-) diff --git a/runtime/dex_file.cc b/runtime/dex_file.cc index 7e09a489be5..a897cce2e0a 100644 --- a/runtime/dex_file.cc +++ b/runtime/dex_file.cc @@ -36,6 +36,7 @@ #include "mirror/string.h" #include "os.h" #include "safe_map.h" +#include "ScopedFd.h" #include "thread.h" #include "UniquePtr.h" #include "utf-inl.h" @@ -64,34 +65,34 @@ DexFile::ClassPathEntry DexFile::FindInClassPath(const char* descriptor, static int OpenAndReadMagic(const char* filename, uint32_t* magic, std::string* error_msg) { CHECK(magic != NULL); - int fd = open(filename, O_RDONLY, 0); - if (fd == -1) { + ScopedFd fd(open(filename, O_RDONLY, 0)); + if (fd.get() == -1) { *error_msg = StringPrintf("Unable to open '%s' : %s", filename, strerror(errno)); return -1; } - int n = TEMP_FAILURE_RETRY(read(fd, magic, sizeof(*magic))); + int n = TEMP_FAILURE_RETRY(read(fd.get(), magic, sizeof(*magic))); if (n != sizeof(*magic)) { *error_msg = StringPrintf("Failed to find magic in '%s'", filename); return -1; } - if (lseek(fd, 0, SEEK_SET) != 0) { + if (lseek(fd.get(), 0, SEEK_SET) != 0) { *error_msg = StringPrintf("Failed to seek to beginning of file '%s' : %s", filename, strerror(errno)); return -1; } - return fd; + return fd.release(); } bool DexFile::GetChecksum(const char* filename, uint32_t* checksum, std::string* error_msg) { CHECK(checksum != NULL); uint32_t magic; - int fd = OpenAndReadMagic(filename, &magic, error_msg); - if (fd == -1) { + ScopedFd fd(OpenAndReadMagic(filename, &magic, error_msg)); + if (fd.get() == -1) { DCHECK(!error_msg->empty()); return false; } if (IsZipMagic(magic)) { - UniquePtr zip_archive(ZipArchive::OpenFromFd(fd, filename, error_msg)); + UniquePtr zip_archive(ZipArchive::OpenFromFd(fd.release(), filename, error_msg)); if (zip_archive.get() == NULL) { *error_msg = StringPrintf("Failed to open zip archive '%s'", filename); return false; @@ -105,7 +106,7 @@ bool DexFile::GetChecksum(const char* filename, uint32_t* checksum, std::string* return true; } if (IsDexMagic(magic)) { - UniquePtr dex_file(DexFile::OpenFile(fd, filename, false, error_msg)); + UniquePtr dex_file(DexFile::OpenFile(fd.release(), filename, false, error_msg)); if (dex_file.get() == NULL) { return false; } @@ -120,16 +121,16 @@ const DexFile* DexFile::Open(const char* filename, const char* location, std::string* error_msg) { uint32_t magic; - int fd = OpenAndReadMagic(filename, &magic, error_msg); - if (fd == -1) { + ScopedFd fd(OpenAndReadMagic(filename, &magic, error_msg)); + if (fd.get() == -1) { DCHECK(!error_msg->empty()); return NULL; } if (IsZipMagic(magic)) { - return DexFile::OpenZip(fd, location, error_msg); + return DexFile::OpenZip(fd.release(), location, error_msg); } if (IsDexMagic(magic)) { - return DexFile::OpenFile(fd, location, true, error_msg); + return DexFile::OpenFile(fd.release(), location, true, error_msg); } *error_msg = StringPrintf("Expected valid zip or dex file: '%s'", filename); return nullptr; @@ -168,26 +169,26 @@ bool DexFile::DisableWrite() const { const DexFile* DexFile::OpenFile(int fd, const char* location, bool verify, std::string* error_msg) { CHECK(location != nullptr); - struct stat sbuf; - memset(&sbuf, 0, sizeof(sbuf)); - if (fstat(fd, &sbuf) == -1) { - *error_msg = StringPrintf("DexFile: fstat \'%s\' failed: %s", location, strerror(errno)); - close(fd); - return nullptr; - } - if (S_ISDIR(sbuf.st_mode)) { - *error_msg = StringPrintf("Attempt to mmap directory '%s'", location); - return nullptr; - } - size_t length = sbuf.st_size; - UniquePtr map(MemMap::MapFile(length, PROT_READ, MAP_PRIVATE, fd, 0, location, - error_msg)); - if (map.get() == nullptr) { - DCHECK(!error_msg->empty()); - close(fd); - return nullptr; + UniquePtr map; + { + ScopedFd delayed_close(fd); + struct stat sbuf; + memset(&sbuf, 0, sizeof(sbuf)); + if (fstat(fd, &sbuf) == -1) { + *error_msg = StringPrintf("DexFile: fstat \'%s\' failed: %s", location, strerror(errno)); + return nullptr; + } + if (S_ISDIR(sbuf.st_mode)) { + *error_msg = StringPrintf("Attempt to mmap directory '%s'", location); + return nullptr; + } + size_t length = sbuf.st_size; + map.reset(MemMap::MapFile(length, PROT_READ, MAP_PRIVATE, fd, 0, location, error_msg)); + if (map.get() == nullptr) { + DCHECK(!error_msg->empty()); + return nullptr; + } } - close(fd); if (map->Size() < sizeof(DexFile::Header)) { *error_msg = StringPrintf( @@ -220,7 +221,7 @@ const DexFile* DexFile::OpenZip(int fd, const std::string& location, std::string DCHECK(!error_msg->empty()); return nullptr; } - return DexFile::Open(*zip_archive.get(), location, error_msg); + return DexFile::Open(*zip_archive, location, error_msg); } const DexFile* DexFile::OpenMemory(const std::string& location, From 0732d59d42523b8c2d807de3a380d2fbfa781364 Mon Sep 17 00:00:00 2001 From: Mathieu Chartier Date: Wed, 6 Nov 2013 11:02:50 -0800 Subject: [PATCH 0145/2402] Improve object clone performance and make compaction proof. Bug: 11551604 Bug: 8981901 Change-Id: I60646d838dbb51e125303d1a8fe869191aa63e78 --- runtime/gc/heap.h | 4 ++++ runtime/mirror/object.cc | 37 ++++++++++++++----------------------- 2 files changed, 18 insertions(+), 23 deletions(-) diff --git a/runtime/gc/heap.h b/runtime/gc/heap.h index 7d2441baa16..91909e4f07a 100644 --- a/runtime/gc/heap.h +++ b/runtime/gc/heap.h @@ -300,6 +300,10 @@ class Heap { card_table_->MarkCard(dst); } + void WriteBarrierEveryFieldOf(const mirror::Object* obj) { + card_table_->MarkCard(obj); + } + accounting::CardTable* GetCardTable() const { return card_table_.get(); } diff --git a/runtime/mirror/object.cc b/runtime/mirror/object.cc index bd187c1835e..87d02c9469b 100644 --- a/runtime/mirror/object.cc +++ b/runtime/mirror/object.cc @@ -40,48 +40,39 @@ namespace art { namespace mirror { Object* Object::Clone(Thread* self) { - Class* c = GetClass(); + mirror::Class* c = GetClass(); DCHECK(!c->IsClassClass()); - // Object::SizeOf gets the right size even if we're an array. // Using c->AllocObject() here would be wrong. size_t num_bytes = SizeOf(); gc::Heap* heap = Runtime::Current()->GetHeap(); - SirtRef copy(self, heap->AllocObject(self, c, num_bytes)); - if (copy.get() == NULL) { - return NULL; + SirtRef sirt_this(self, this); + Object* copy = heap->AllocObject(self, c, num_bytes); + if (UNLIKELY(copy == nullptr)) { + return nullptr; } - // Copy instance data. We assume memcpy copies by words. // TODO: expose and use move32. - byte* src_bytes = reinterpret_cast(this); - byte* dst_bytes = reinterpret_cast(copy.get()); + byte* src_bytes = reinterpret_cast(sirt_this.get()); + byte* dst_bytes = reinterpret_cast(copy); size_t offset = sizeof(Object); memcpy(dst_bytes + offset, src_bytes + offset, num_bytes - offset); - // Perform write barriers on copied object references. + c = copy->GetClass(); // Re-read Class in case it moved. if (c->IsArrayClass()) { if (!c->GetComponentType()->IsPrimitive()) { const ObjectArray* array = copy->AsObjectArray(); - heap->WriteBarrierArray(copy.get(), 0, array->GetLength()); + heap->WriteBarrierArray(copy, 0, array->GetLength()); } } else { - for (const Class* klass = c; klass != NULL; klass = klass->GetSuperClass()) { - size_t num_reference_fields = klass->NumReferenceInstanceFields(); - for (size_t i = 0; i < num_reference_fields; ++i) { - ArtField* field = klass->GetInstanceField(i); - MemberOffset field_offset = field->GetOffset(); - const Object* ref = copy->GetFieldObject(field_offset, false); - heap->WriteBarrierField(copy.get(), field_offset, ref); - } - } + heap->WriteBarrierEveryFieldOf(copy); } - if (c->IsFinalizable()) { - heap->AddFinalizerReference(Thread::Current(), copy.get()); + SirtRef sirt_copy(self, copy); + heap->AddFinalizerReference(self, copy); + return sirt_copy.get(); } - - return copy.get(); + return copy; } int32_t Object::GenerateIdentityHashCode() { From fa82427c68b09f4aedbee319dc71579afbfc66f5 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Tue, 5 Nov 2013 16:12:57 -0800 Subject: [PATCH 0146/2402] Fix oatdump and valgrind. Bug: 11531382 Move allocation instrumentation out of runtime into instrumentation. Don't attempt to suspend threads in unstarted runtimes. Make indentation support sputc returning eof, on which it will sync and try again. A further failure likely means the disk is full. Move the dump-oat output directory to be art as now there's too much output to fit all the dump-oat data in our standard /tmp. Change-Id: I8ea848ace318552c180e2efa46570288ff1ca62c --- Android.mk | 18 +++++++------ oatdump/oatdump.cc | 19 +++++++++++--- runtime/debugger.cc | 4 +-- runtime/gc/heap.cc | 2 +- runtime/indenter.h | 27 +++++++++++++++----- runtime/instrumentation.cc | 52 ++++++++++++++++++++++++++++++++++++++ runtime/instrumentation.h | 13 ++++++++-- runtime/runtime.cc | 49 +++-------------------------------- runtime/runtime.h | 5 ---- 9 files changed, 114 insertions(+), 75 deletions(-) diff --git a/Android.mk b/Android.mk index 0b4b2316fd8..774353f419d 100644 --- a/Android.mk +++ b/Android.mk @@ -305,6 +305,8 @@ build-art-target: $(ART_TARGET_EXECUTABLES) $(ART_TARGET_TEST_EXECUTABLES) $(TAR ######################################################################## # oatdump targets +ART_DUMP_OAT_PATH ?= $(OUT_DIR) + .PHONY: dump-oat dump-oat: dump-oat-core dump-oat-boot @@ -314,29 +316,29 @@ dump-oat-core: dump-oat-core-host dump-oat-core-target .PHONY: dump-oat-core-host ifeq ($(ART_BUILD_HOST),true) dump-oat-core-host: $(HOST_CORE_IMG_OUT) $(OATDUMP) - $(OATDUMP) --image=$(HOST_CORE_IMG_OUT) --output=/tmp/core.host.oatdump.txt --host-prefix="" - @echo Output in /tmp/core.host.oatdump.txt + $(OATDUMP) --image=$(HOST_CORE_IMG_OUT) --output=$(ART_DUMP_OAT_PATH)/core.host.oatdump.txt --host-prefix="" + @echo Output in $(ART_DUMP_OAT_PATH)/core.host.oatdump.txt endif .PHONY: dump-oat-core-target ifeq ($(ART_BUILD_TARGET),true) dump-oat-core-target: $(TARGET_CORE_IMG_OUT) $(OATDUMP) - $(OATDUMP) --image=$(TARGET_CORE_IMG_OUT) --output=/tmp/core.target.oatdump.txt - @echo Output in /tmp/core.target.oatdump.txt + $(OATDUMP) --image=$(TARGET_CORE_IMG_OUT) --output=$(ART_DUMP_OAT_PATH)/core.target.oatdump.txt + @echo Output in $(ART_DUMP_OAT_PATH)/core.target.oatdump.txt endif .PHONY: dump-oat-boot ifeq ($(ART_BUILD_TARGET_NDEBUG),true) dump-oat-boot: $(TARGET_BOOT_IMG_OUT) $(OATDUMP) - $(OATDUMP) --image=$(TARGET_BOOT_IMG_OUT) --output=/tmp/boot.oatdump.txt - @echo Output in /tmp/boot.oatdump.txt + $(OATDUMP) --image=$(TARGET_BOOT_IMG_OUT) --output=$(ART_DUMP_OAT_PATH)/boot.oatdump.txt + @echo Output in $(ART_DUMP_OAT_PATH)/boot.oatdump.txt endif .PHONY: dump-oat-Calculator ifeq ($(ART_BUILD_TARGET_NDEBUG),true) dump-oat-Calculator: $(TARGET_OUT_APPS)/Calculator.odex $(TARGET_BOOT_IMG_OUT) $(OATDUMP) - $(OATDUMP) --oat-file=$< --output=/tmp/Calculator.oatdump.txt - @echo Output in /tmp/Calculator.oatdump.txt + $(OATDUMP) --oat-file=$< --output=$(ART_DUMP_OAT_PATH)/Calculator.oatdump.txt + @echo Output in $(ART_DUMP_OAT_PATH)/Calculator.oatdump.txt endif ######################################################################## diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc index 3a32ff14bda..047d9857964 100644 --- a/oatdump/oatdump.cc +++ b/oatdump/oatdump.cc @@ -712,14 +712,25 @@ class ImageDumper { if (image_root_object->IsObjectArray()) { Indenter indent2_filter(indent1_os.rdbuf(), kIndentChar, kIndentBy1Count); std::ostream indent2_os(&indent2_filter); - // TODO: replace down_cast with AsObjectArray (g++ currently has a problem with this) mirror::ObjectArray* image_root_object_array - = down_cast*>(image_root_object); - // = image_root_object->AsObjectArray(); + = image_root_object->AsObjectArray(); for (int i = 0; i < image_root_object_array->GetLength(); i++) { mirror::Object* value = image_root_object_array->Get(i); + size_t run = 0; + for (int32_t j = i + 1; j < image_root_object_array->GetLength(); j++) { + if (value == image_root_object_array->Get(j)) { + run++; + } else { + break; + } + } + if (run == 0) { + indent2_os << StringPrintf("%d: ", i); + } else { + indent2_os << StringPrintf("%d to %zd: ", i, i + run); + i = i + run; + } if (value != NULL) { - indent2_os << i << ": "; PrettyObjectValue(indent2_os, value->GetClass(), value); } else { indent2_os << i << ": null\n"; diff --git a/runtime/debugger.cc b/runtime/debugger.cc index bdcf6ac6aba..0eecd288311 100644 --- a/runtime/debugger.cc +++ b/runtime/debugger.cc @@ -3489,9 +3489,9 @@ void Dbg::SetAllocTrackingEnabled(bool enabled) { recent_allocation_records_ = new AllocRecord[gAllocRecordMax]; CHECK(recent_allocation_records_ != NULL); } - Runtime::Current()->InstrumentQuickAllocEntryPoints(); + Runtime::Current()->GetInstrumentation()->InstrumentQuickAllocEntryPoints(); } else { - Runtime::Current()->UninstrumentQuickAllocEntryPoints(); + Runtime::Current()->GetInstrumentation()->UninstrumentQuickAllocEntryPoints(); delete[] recent_allocation_records_; recent_allocation_records_ = NULL; } diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc index 804c66932e3..de3ab0eb9d2 100644 --- a/runtime/gc/heap.cc +++ b/runtime/gc/heap.cc @@ -237,7 +237,7 @@ Heap::Heap(size_t initial_size, size_t growth_limit, size_t min_free, size_t max CHECK_NE(max_allowed_footprint_, 0U); if (running_on_valgrind_) { - Runtime::Current()->InstrumentQuickAllocEntryPoints(); + Runtime::Current()->GetInstrumentation()->InstrumentQuickAllocEntryPoints(); } if (VLOG_IS_ON(heap) || VLOG_IS_ON(startup)) { diff --git a/runtime/indenter.h b/runtime/indenter.h index c432e1ba8dd..d055d4e3f43 100644 --- a/runtime/indenter.h +++ b/runtime/indenter.h @@ -17,6 +17,7 @@ #ifndef ART_RUNTIME_INDENTER_H_ #define ART_RUNTIME_INDENTER_H_ +#include "base/logging.h" #include "base/macros.h" #include @@ -30,16 +31,28 @@ class Indenter : public std::streambuf { private: int_type overflow(int_type c) { - if (c != std::char_traits::eof()) { - if (indent_next_) { - for (size_t i = 0; i < count_; ++i) { - out_sbuf_->sputc(text_); + if (UNLIKELY(c == std::char_traits::eof())) { + out_sbuf_->pubsync(); + return c; + } + if (indent_next_) { + for (size_t i = 0; i < count_; ++i) { + int_type r = out_sbuf_->sputc(text_); + if (UNLIKELY(r != text_)) { + out_sbuf_->pubsync(); + r = out_sbuf_->sputc(text_); + CHECK_EQ(r, text_) << "Error writing to buffer. Disk full?"; } } - out_sbuf_->sputc(c); - indent_next_ = (c == '\n'); } - return std::char_traits::not_eof(c); + indent_next_ = (c == '\n'); + int_type r = out_sbuf_->sputc(c); + if (UNLIKELY(r != c)) { + out_sbuf_->pubsync(); + r = out_sbuf_->sputc(c); + CHECK_EQ(r, c) << "Error writing to buffer. Disk full?"; + } + return r; } int sync() { diff --git a/runtime/instrumentation.cc b/runtime/instrumentation.cc index 8316bc56b3b..4070324fafb 100644 --- a/runtime/instrumentation.cc +++ b/runtime/instrumentation.cc @@ -39,6 +39,9 @@ #include "thread_list.h" namespace art { + +extern void SetQuickAllocEntryPointsInstrumented(bool instrumented); + namespace instrumentation { // Do we want to deoptimize for method entry and exit listeners or just try to intercept @@ -391,6 +394,55 @@ void Instrumentation::ConfigureStubs(bool require_entry_exit_stubs, bool require } } +static void ResetQuickAllocEntryPointsForThread(Thread* thread, void* arg) { + thread->ResetQuickAllocEntryPointsForThread(); +} + +void Instrumentation::InstrumentQuickAllocEntryPoints() { + // TODO: the read of quick_alloc_entry_points_instrumentation_counter_ is racey and this code + // should be guarded by a lock. + DCHECK_GE(quick_alloc_entry_points_instrumentation_counter_, 0U); + bool enable_instrumentation = (quick_alloc_entry_points_instrumentation_counter_ == 0); + quick_alloc_entry_points_instrumentation_counter_++; + if (enable_instrumentation) { + // Instrumentation wasn't enabled so enable it. + SetQuickAllocEntryPointsInstrumented(true); + Runtime* runtime = Runtime::Current(); + if (runtime->IsStarted()) { + ThreadList* tl = runtime->GetThreadList(); + Thread* self = Thread::Current(); + tl->SuspendAll(); + { + MutexLock mu(self, *Locks::thread_list_lock_); + tl->ForEach(ResetQuickAllocEntryPointsForThread, NULL); + } + tl->ResumeAll(); + } + } +} + +void Instrumentation::UninstrumentQuickAllocEntryPoints() { + // TODO: the read of quick_alloc_entry_points_instrumentation_counter_ is racey and this code + // should be guarded by a lock. + DCHECK_GT(quick_alloc_entry_points_instrumentation_counter_, 0U); + quick_alloc_entry_points_instrumentation_counter_--; + bool disable_instrumentation = (quick_alloc_entry_points_instrumentation_counter_ == 0); + if (disable_instrumentation) { + SetQuickAllocEntryPointsInstrumented(false); + Runtime* runtime = Runtime::Current(); + if (runtime->IsStarted()) { + ThreadList* tl = Runtime::Current()->GetThreadList(); + Thread* self = Thread::Current(); + tl->SuspendAll(); + { + MutexLock mu(self, *Locks::thread_list_lock_); + tl->ForEach(ResetQuickAllocEntryPointsForThread, NULL); + } + tl->ResumeAll(); + } + } +} + void Instrumentation::UpdateMethodsCode(mirror::ArtMethod* method, const void* code) const { if (LIKELY(!instrumentation_stubs_installed_)) { method->SetEntryPointFromCompiledCode(code); diff --git a/runtime/instrumentation.h b/runtime/instrumentation.h index 7a0aaf7858c..25a4eec976a 100644 --- a/runtime/instrumentation.h +++ b/runtime/instrumentation.h @@ -104,7 +104,8 @@ class Instrumentation { have_method_entry_listeners_(false), have_method_exit_listeners_(false), have_method_unwind_listeners_(false), have_dex_pc_listeners_(false), have_exception_caught_listeners_(false), - interpreter_handler_table_(kMainHandlerTable) {} + interpreter_handler_table_(kMainHandlerTable), + quick_alloc_entry_points_instrumentation_counter_(0) {} // Add a listener to be notified of the masked together sent of instrumentation events. This // suspend the runtime to install stubs. You are expected to hold the mutator lock as a proxy @@ -123,6 +124,9 @@ class Instrumentation { return interpreter_handler_table_; } + void InstrumentQuickAllocEntryPoints() LOCKS_EXCLUDED(Locks::thread_list_lock_); + void UninstrumentQuickAllocEntryPoints() LOCKS_EXCLUDED(Locks::thread_list_lock_); + // Update the code of a method respecting any installed stubs. void UpdateMethodsCode(mirror::ArtMethod* method, const void* code) const; @@ -289,9 +293,14 @@ class Instrumentation { std::list dex_pc_listeners_ GUARDED_BY(Locks::mutator_lock_); std::list exception_caught_listeners_ GUARDED_BY(Locks::mutator_lock_); - // Current interpreter handler table. This is updated each time the thread state flags are modified. + // Current interpreter handler table. This is updated each time the thread state flags are + // modified. InterpreterHandlerTable interpreter_handler_table_; + // Greater than 0 if quick alloc entry points instrumented. + // TODO: The access and changes to this is racy and should be guarded by a lock. + size_t quick_alloc_entry_points_instrumentation_counter_; + DISALLOW_COPY_AND_ASSIGN(Instrumentation); }; diff --git a/runtime/runtime.cc b/runtime/runtime.cc index 34cf45b4c7d..53c9b2efbb4 100644 --- a/runtime/runtime.cc +++ b/runtime/runtime.cc @@ -102,8 +102,7 @@ Runtime::Runtime() use_compile_time_class_path_(false), main_thread_group_(NULL), system_thread_group_(NULL), - system_class_loader_(NULL), - quick_alloc_entry_points_instrumentation_counter_(0) { + system_class_loader_(NULL) { for (int i = 0; i < Runtime::kLastCalleeSaveType; i++) { callee_save_methods_[i] = NULL; } @@ -1077,9 +1076,9 @@ void Runtime::SetStatsEnabled(bool new_state) { GetStats()->Clear(~0); // TODO: wouldn't it make more sense to clear _all_ threads' stats? Thread::Current()->GetStats()->Clear(~0); - InstrumentQuickAllocEntryPoints(); + GetInstrumentation()->InstrumentQuickAllocEntryPoints(); } else { - UninstrumentQuickAllocEntryPoints(); + GetInstrumentation()->UninstrumentQuickAllocEntryPoints(); } stats_enabled_ = new_state; } @@ -1336,46 +1335,4 @@ void Runtime::SetCompileTimeClassPath(jobject class_loader, std::vectorResetQuickAllocEntryPointsForThread(); -} - -void SetQuickAllocEntryPointsInstrumented(bool instrumented); - -void Runtime::InstrumentQuickAllocEntryPoints() { - ThreadList* tl = thread_list_; - Thread* self = Thread::Current(); - tl->SuspendAll(); - { - MutexLock mu(self, *Locks::runtime_shutdown_lock_); - MutexLock mu2(self, *Locks::thread_list_lock_); - DCHECK_GE(quick_alloc_entry_points_instrumentation_counter_, 0); - int old_counter = quick_alloc_entry_points_instrumentation_counter_++; - if (old_counter == 0) { - // If it was disabled, enable it. - SetQuickAllocEntryPointsInstrumented(true); - tl->ForEach(ResetQuickAllocEntryPointsForThread, NULL); - } - } - tl->ResumeAll(); -} - -void Runtime::UninstrumentQuickAllocEntryPoints() { - ThreadList* tl = thread_list_; - Thread* self = Thread::Current(); - tl->SuspendAll(); - { - MutexLock mu(self, *Locks::runtime_shutdown_lock_); - MutexLock mu2(self, *Locks::thread_list_lock_); - DCHECK_GT(quick_alloc_entry_points_instrumentation_counter_, 0); - int new_counter = --quick_alloc_entry_points_instrumentation_counter_; - if (new_counter == 0) { - // Disable it if the counter becomes zero. - SetQuickAllocEntryPointsInstrumented(false); - tl->ForEach(ResetQuickAllocEntryPointsForThread, NULL); - } - } - tl->ResumeAll(); -} - } // namespace art diff --git a/runtime/runtime.h b/runtime/runtime.h index 0ce2642c98b..24b4c876667 100644 --- a/runtime/runtime.h +++ b/runtime/runtime.h @@ -442,9 +442,6 @@ class Runtime { const std::vector& GetCompileTimeClassPath(jobject class_loader); void SetCompileTimeClassPath(jobject class_loader, std::vector& class_path); - void InstrumentQuickAllocEntryPoints(); - void UninstrumentQuickAllocEntryPoints(); - private: static void InitPlatformSignalHandlers(); @@ -567,8 +564,6 @@ class Runtime { // As returned by ClassLoader.getSystemClassLoader(). jobject system_class_loader_; - int quick_alloc_entry_points_instrumentation_counter_; - DISALLOW_COPY_AND_ASSIGN(Runtime); }; From ce90ec44861977372aeac550ed6c878c908bd774 Mon Sep 17 00:00:00 2001 From: Dave Allison Date: Wed, 6 Nov 2013 11:29:51 -0800 Subject: [PATCH 0147/2402] Don't force dex2oat run on dex2oat changes This was accidentally checked in on a previous CL. Bug: 11299025 Change-Id: I6c695dc891db9102fabd7805ee9480dfc41d0408 --- build/Android.oat.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/Android.oat.mk b/build/Android.oat.mk index 5d355a6d86b..e505a86ee15 100644 --- a/build/Android.oat.mk +++ b/build/Android.oat.mk @@ -26,7 +26,7 @@ LIBART_COMPILER := $(LIBARTD_COMPILER) # By default, do not run rerun dex2oat if the tool changes. # Comment out the | to force dex2oat to rerun on after all changes. -DEX2OAT_DEPENDENCY := #| +DEX2OAT_DEPENDENCY := | DEX2OAT_DEPENDENCY += $(DEX2OAT) DEX2OAT_DEPENDENCY += $(LIBART_COMPILER) From bbb32c277b800a8ee378c16a3645ab6d4d19aef1 Mon Sep 17 00:00:00 2001 From: Dave Allison Date: Tue, 5 Nov 2013 18:25:18 -0800 Subject: [PATCH 0148/2402] Add .cfi_restore for assembly functions Bug: 11256318 This adds .cfi_restore directives to the assembly language functions for ARM, MIPS and x86. Change-Id: Ic5a80e190aa6cf2d2fcfaecec14de03221ff386b --- runtime/arch/arm/quick_entrypoints_arm.S | 35 +++++++++++++- runtime/arch/mips/quick_entrypoints_mips.S | 34 ++++++++++++++ runtime/arch/x86/quick_entrypoints_x86.S | 53 ++++++++++++++++++++-- 3 files changed, 117 insertions(+), 5 deletions(-) diff --git a/runtime/arch/arm/quick_entrypoints_arm.S b/runtime/arch/arm/quick_entrypoints_arm.S index 9a853d07aba..1a058ea61e6 100644 --- a/runtime/arch/arm/quick_entrypoints_arm.S +++ b/runtime/arch/arm/quick_entrypoints_arm.S @@ -69,12 +69,24 @@ .macro RESTORE_REF_ONLY_CALLEE_SAVE_FRAME add sp, #4 @ bottom word holds Method* pop {r5-r8, r10-r11, lr} @ 7 words of callee saves + .cfi_restore r5 + .cfi_restore r6 + .cfi_restore r7 + .cfi_restore r8 + .cfi_restore r10 + .cfi_restore r11 .cfi_adjust_cfa_offset -32 .endm .macro RESTORE_REF_ONLY_CALLEE_SAVE_FRAME_AND_RETURN add sp, #4 @ bottom word holds Method* pop {r5-r8, r10-r11, lr} @ 7 words of callee saves + .cfi_restore r5 + .cfi_restore r6 + .cfi_restore r7 + .cfi_restore r8 + .cfi_restore r10 + .cfi_restore r11 .cfi_adjust_cfa_offset -32 bx lr @ return .endm @@ -86,7 +98,6 @@ .macro SETUP_REF_AND_ARGS_CALLEE_SAVE_FRAME push {r1-r3, r5-r8, r10-r11, lr} @ 10 words of callee saves .save {r1-r3, r5-r8, r10-r11, lr} - .cfi_adjust_cfa_offset 40 .cfi_rel_offset r1, 0 .cfi_rel_offset r2, 4 .cfi_rel_offset r3, 8 @@ -97,6 +108,7 @@ .cfi_rel_offset r10, 28 .cfi_rel_offset r11, 32 .cfi_rel_offset lr, 36 + .cfi_adjust_cfa_offset 40 sub sp, #8 @ 2 words of space, bottom word will hold Method* .pad #8 .cfi_adjust_cfa_offset 8 @@ -105,6 +117,15 @@ .macro RESTORE_REF_AND_ARGS_CALLEE_SAVE_FRAME add sp, #8 @ rewind sp pop {r1-r3, r5-r8, r10-r11, lr} @ 10 words of callee saves + .cfi_restore r1 + .cfi_restore r2 + .cfi_restore r3 + .cfi_restore r5 + .cfi_restore r6 + .cfi_restore r7 + .cfi_restore r8 + .cfi_restore r10 + .cfi_restore r11 .cfi_adjust_cfa_offset -48 .endm @@ -285,6 +306,11 @@ ENTRY art_quick_invoke_stub ldr ip, [sp, #24] @ load the result pointer strd r0, [ip] @ store r0/r1 into result pointer pop {r0, r4, r5, r9, r11, lr} @ restore spill regs + .cfi_restore r0 + .cfi_restore r4 + .cfi_restore r5 + .cfi_restore r9 + .cfi_restore lr .cfi_adjust_cfa_offset -24 bx lr END art_quick_invoke_stub @@ -413,6 +439,8 @@ throw_class_cast_exception: add sp, #4 .cfi_adjust_cfa_offset -4 pop {r0-r1, lr} + .cfi_restore r0 + .cfi_restore r1 SETUP_SAVE_ALL_CALLEE_SAVE_FRAME // save all registers as basis for long jump context mov r2, r9 @ pass Thread::Current mov r3, sp @ pass SP @@ -689,6 +717,7 @@ ENTRY art_quick_set64_static .cfi_rel_offset r9, 0 bl artSet64StaticFromCode @ (field_idx, referrer, new_val, Thread*, SP) add sp, #16 @ release out args + .cfi_adjust_cfa_offset -16 RESTORE_REF_ONLY_CALLEE_SAVE_FRAME @ TODO: we can clearly save an add here RETURN_IF_RESULT_IS_ZERO DELIVER_PENDING_EXCEPTION @@ -1137,6 +1166,8 @@ art_quick_instrumentation_exit: mov r2, r0 @ link register saved by instrumentation mov lr, r1 @ r1 is holding link register if we're to bounce to deoptimize pop {r0, r1} @ restore return value + .cfi_restore r0 + .cfi_restore r1 add sp, #32 @ remove callee save frame .cfi_adjust_cfa_offset -32 bx r2 @ return @@ -1187,6 +1218,8 @@ ENTRY art_quick_mul_long mov r1,r10 pop {r9 - r10} .cfi_adjust_cfa_offset -8 + .cfi_restore r9 + .cfi_restore r10 bx lr END art_quick_mul_long diff --git a/runtime/arch/mips/quick_entrypoints_mips.S b/runtime/arch/mips/quick_entrypoints_mips.S index 451b1bb30f4..886271166c8 100644 --- a/runtime/arch/mips/quick_entrypoints_mips.S +++ b/runtime/arch/mips/quick_entrypoints_mips.S @@ -89,28 +89,46 @@ .macro RESTORE_REF_ONLY_CALLEE_SAVE_FRAME lw $ra, 60($sp) + .cfi_restore 31 lw $s8, 56($sp) + .cfi_restore 30 lw $gp, 52($sp) + .cfi_restore 28 lw $s7, 48($sp) + .cfi_restore 23 lw $s6, 44($sp) + .cfi_restore 22 lw $s5, 40($sp) + .cfi_restore 21 lw $s4, 36($sp) + .cfi_restore 20 lw $s3, 32($sp) + .cfi_restore 19 lw $s2, 28($sp) + .cfi_restore 18 addiu $sp, $sp, 64 .cfi_adjust_cfa_offset -64 .endm .macro RESTORE_REF_ONLY_CALLEE_SAVE_FRAME_AND_RETURN lw $ra, 60($sp) + .cfi_restore 31 lw $s8, 56($sp) + .cfi_restore 30 lw $gp, 52($sp) + .cfi_restore 28 lw $s7, 48($sp) + .cfi_restore 23 lw $s6, 44($sp) + .cfi_restore 22 lw $s5, 40($sp) + .cfi_restore 21 lw $s4, 36($sp) + .cfi_restore 20 lw $s3, 32($sp) + .cfi_restore 19 lw $s2, 28($sp) + .cfi_restore 18 jr $ra addiu $sp, $sp, 64 .cfi_adjust_cfa_offset -64 @@ -153,17 +171,29 @@ .macro RESTORE_REF_AND_ARGS_CALLEE_SAVE_FRAME lw $ra, 60($sp) + .cfi_restore 31 lw $s8, 56($sp) + .cfi_restore 30 lw $gp, 52($sp) + .cfi_restore 28 lw $s7, 48($sp) + .cfi_restore 23 lw $s6, 44($sp) + .cfi_restore 22 lw $s5, 40($sp) + .cfi_restore 21 lw $s4, 36($sp) + .cfi_restore 20 lw $s3, 32($sp) + .cfi_restore 19 lw $s2, 28($sp) + .cfi_restore 18 lw $a3, 12($sp) + .cfi_restore 7 lw $a2, 8($sp) + .cfi_restore 6 lw $a1, 4($sp) + .cfi_restore 5 addiu $sp, $sp, 64 # pop frame .cfi_adjust_cfa_offset -64 .endm @@ -463,9 +493,13 @@ ENTRY art_quick_invoke_stub sw $zero, 0($sp) # store NULL for method* at bottom of frame move $sp, $fp # restore the stack lw $s0, 0($sp) + .cfi_restore 16 lw $s1, 4($sp) + .cfi_restore 17 lw $fp, 8($sp) + .cfi_restore 30 lw $ra, 12($sp) + .cfi_restore 31 addiu $sp, $sp, 16 .cfi_adjust_cfa_offset -16 lw $t0, 16($sp) # get result pointer diff --git a/runtime/arch/x86/quick_entrypoints_x86.S b/runtime/arch/x86/quick_entrypoints_x86.S index 6fe499374aa..ee78d457935 100644 --- a/runtime/arch/x86/quick_entrypoints_x86.S +++ b/runtime/arch/x86/quick_entrypoints_x86.S @@ -16,14 +16,19 @@ #include "asm_support_x86.S" +// For x86, the CFA is esp+4, the address above the pushed return address on the stack. + /* * Macro that sets up the callee save frame to conform with * Runtime::CreateCalleeSaveMethod(kSaveAll) */ MACRO0(SETUP_SAVE_ALL_CALLEE_SAVE_FRAME) PUSH edi // Save callee saves (ebx is saved/restored by the upcall) + .cfi_rel_offset edi, -8 PUSH esi + .cfi_rel_offset esi, -12 PUSH ebp + .cfi_rel_offset ebp, -16 subl MACRO_LITERAL(16), %esp // Grow stack by 4 words, bottom word will hold Method* .cfi_adjust_cfa_offset 16 END_MACRO @@ -34,8 +39,11 @@ END_MACRO */ MACRO0(SETUP_REF_ONLY_CALLEE_SAVE_FRAME) PUSH edi // Save callee saves (ebx is saved/restored by the upcall) + .cfi_rel_offset edi, -8 PUSH esi + .cfi_rel_offset esi, -12 PUSH ebp + .cfi_rel_offset ebp, -16 subl MACRO_LITERAL(16), %esp // Grow stack by 4 words, bottom word will hold Method* .cfi_adjust_cfa_offset 16 END_MACRO @@ -43,8 +51,11 @@ END_MACRO MACRO0(RESTORE_REF_ONLY_CALLEE_SAVE_FRAME) addl MACRO_LITERAL(16), %esp // Unwind stack up to return address POP ebp // Restore callee saves (ebx is saved/restored by the upcall) + .cfi_restore ebp POP esi + .cfi_restore esi POP edi + .cfi_restore edi .cfi_adjust_cfa_offset -28 END_MACRO @@ -54,23 +65,36 @@ END_MACRO */ MACRO0(SETUP_REF_AND_ARGS_CALLEE_SAVE_FRAME) PUSH edi // Save callee saves + .cfi_rel_offset edi, -8 PUSH esi + .cfi_rel_offset esi, -12 PUSH ebp + .cfi_rel_offset ebp, -16 PUSH ebx // Save args + .cfi_rel_offset ebx, -20 PUSH edx + .cfi_rel_offset edx, -24 PUSH ecx + .cfi_rel_offset ecx, -28 PUSH eax // Align stack, eax will be clobbered by Method* + .cfi_rel_offset eax, -28 END_MACRO MACRO0(RESTORE_REF_AND_ARGS_CALLEE_SAVE_FRAME) addl MACRO_LITERAL(4), %esp // Remove padding .cfi_adjust_cfa_offset -4 POP ecx // Restore args except eax + .cfi_restore ecx POP edx + .cfi_restore edx POP ebx + .cfi_restore ebx POP ebp // Restore callee saves + .cfi_restore ebp POP esi + .cfi_restore esi POP edi + .cfi_restore edi END_MACRO /* @@ -188,12 +212,19 @@ MACRO2(INVOKE_TRAMPOLINE, c_name, cxx_name) // Set up the callee save frame to conform with Runtime::CreateCalleeSaveMethod(kRefsAndArgs) // return address PUSH edi + .cfi_rel_offset edi, -8 PUSH esi + .cfi_rel_offset esi, -12 PUSH ebp - PUSH ebx + .cfi_rel_offset ebp, -16 + PUSH ebx // Save args + .cfi_rel_offset ebx, -20 PUSH edx + .cfi_rel_offset edx, -24 PUSH ecx - PUSH eax // <-- callee save Method* to go here + .cfi_rel_offset ecx, -28 + PUSH eax // <-- callee save Method* to go here + .cfi_rel_offset eax, -32 movl %esp, %edx // remember SP // Outgoing argument set up subl MACRO_LITERAL(12), %esp // alignment padding @@ -209,11 +240,16 @@ MACRO2(INVOKE_TRAMPOLINE, c_name, cxx_name) movl %edx, %edi // save code pointer in EDI addl MACRO_LITERAL(36), %esp // Pop arguments skip eax .cfi_adjust_cfa_offset -36 - POP ecx // Restore args + POP ecx // Restore args except eax + .cfi_restore ecx POP edx + .cfi_restore edx POP ebx - POP ebp // Restore callee saves. + .cfi_restore ebx + POP ebp // Restore callee saves + .cfi_restore ebp POP esi + .cfi_restore esi // Swap EDI callee save with code pointer. xchgl %edi, (%esp) testl %eax, %eax // Branch forward if exception pending. @@ -248,7 +284,9 @@ INVOKE_TRAMPOLINE art_quick_invoke_virtual_trampoline_with_access_check, artInvo */ DEFINE_FUNCTION art_quick_invoke_stub PUSH ebp // save ebp + .cfi_rel_offset ebp, -8 PUSH ebx // save ebx + .cfi_rel_offset ebx, -12 mov %esp, %ebp // copy value of stack pointer into base pointer .cfi_def_cfa_register ebp mov 20(%ebp), %ebx // get arg array size @@ -269,8 +307,11 @@ DEFINE_FUNCTION art_quick_invoke_stub mov 12(%esp), %ebx // copy arg3 into ebx call *METHOD_CODE_OFFSET(%eax) // call the method mov %ebp, %esp // restore stack pointer + .cfi_def_cfa_register esp POP ebx // pop ebx + .cfi_restore ebx POP ebp // pop ebp + .cfi_restore ebp mov 20(%esp), %ecx // get result pointer cmpl LITERAL(68), 24(%esp) // test if result type char == 'D' je return_double_quick @@ -495,7 +536,9 @@ END_FUNCTION art_quick_is_assignable DEFINE_FUNCTION art_quick_check_cast PUSH eax // alignment padding PUSH ecx // pass arg2 - obj->klass + .cfi_rel_offset ecx, -12 PUSH eax // pass arg1 - checked class + .cfi_rel_offset eax, -16 call SYMBOL(artIsAssignableFromCode) // (Class* klass, Class* ref_klass) testl %eax, %eax jz 1f // jump forward if not assignable @@ -504,7 +547,9 @@ DEFINE_FUNCTION art_quick_check_cast ret 1: POP eax // pop arguments + .cfi_restore eax POP ecx + .cfi_restore ecx addl LITERAL(4), %esp .cfi_adjust_cfa_offset -12 SETUP_SAVE_ALL_CALLEE_SAVE_FRAME // save all registers as basis for long jump context From d0c09dc2177d132099a05af8537bdb9a8225af8c Mon Sep 17 00:00:00 2001 From: Brian Carlstrom Date: Wed, 6 Nov 2013 18:25:35 -0800 Subject: [PATCH 0149/2402] Add missing error message propagation to ElfFile::SetMap Change-Id: I085e23cd4728e10a7efca3586270c6cffed9e8d4 --- runtime/elf_file.cc | 26 +++++++++++++++----------- runtime/elf_file.h | 2 +- 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/runtime/elf_file.cc b/runtime/elf_file.cc index 15c95f2206d..b3b24baf800 100644 --- a/runtime/elf_file.cc +++ b/runtime/elf_file.cc @@ -82,7 +82,8 @@ bool ElfFile::Setup(File* file, bool writable, bool program_header_only, std::st // first just map ELF header to get program header size information size_t elf_header_size = sizeof(llvm::ELF::Elf32_Ehdr); if (!SetMap(MemMap::MapFile(elf_header_size, prot, flags, file_->Fd(), 0, - file_->GetPath().c_str(), error_msg))) { + file_->GetPath().c_str(), error_msg), + error_msg)) { return false; } // then remap to cover program header @@ -94,14 +95,16 @@ bool ElfFile::Setup(File* file, bool writable, bool program_header_only, std::st return false; } if (!SetMap(MemMap::MapFile(program_header_size, prot, flags, file_->Fd(), 0, - file_->GetPath().c_str(), error_msg))) { + file_->GetPath().c_str(), error_msg), + error_msg)) { *error_msg = StringPrintf("Failed to map ELF program headers: %s", error_msg->c_str()); return false; } } else { // otherwise map entire file if (!SetMap(MemMap::MapFile(file_->GetLength(), prot, flags, file_->Fd(), 0, - file_->GetPath().c_str(), error_msg))) { + file_->GetPath().c_str(), error_msg), + error_msg)) { *error_msg = StringPrintf("Failed to map ELF file: %s", error_msg->c_str()); return false; } @@ -173,9 +176,10 @@ ElfFile::~ElfFile() { delete dynsym_symbol_table_; } -bool ElfFile::SetMap(MemMap* map) { +bool ElfFile::SetMap(MemMap* map, std::string* error_msg) { if (map == NULL) { - // MemMap::Open should have already logged + // MemMap::Open should have already set an error. + DCHECK(!error_msg->empty()); return false; } map_.reset(map); @@ -187,12 +191,12 @@ bool ElfFile::SetMap(MemMap* map) { || (llvm::ELF::ElfMagic[1] != header_->e_ident[llvm::ELF::EI_MAG1]) || (llvm::ELF::ElfMagic[2] != header_->e_ident[llvm::ELF::EI_MAG2]) || (llvm::ELF::ElfMagic[3] != header_->e_ident[llvm::ELF::EI_MAG3])) { - LOG(WARNING) << "Failed to find ELF magic in " << file_->GetPath() - << ": " << std::hex - << static_cast(header_->e_ident[llvm::ELF::EI_MAG0]) - << static_cast(header_->e_ident[llvm::ELF::EI_MAG1]) - << static_cast(header_->e_ident[llvm::ELF::EI_MAG2]) - << static_cast(header_->e_ident[llvm::ELF::EI_MAG3]); + *error_msg = StringPrintf("Failed to find ELF magic in %s: %c%c%c%c", + file_->GetPath().c_str(), + header_->e_ident[llvm::ELF::EI_MAG0], + header_->e_ident[llvm::ELF::EI_MAG1], + header_->e_ident[llvm::ELF::EI_MAG2], + header_->e_ident[llvm::ELF::EI_MAG3]); return false; } diff --git a/runtime/elf_file.h b/runtime/elf_file.h index b0251370d22..f8c235d191a 100644 --- a/runtime/elf_file.h +++ b/runtime/elf_file.h @@ -122,7 +122,7 @@ class ElfFile { bool Setup(File* file, bool writable, bool program_header_only, std::string* error_msg); - bool SetMap(MemMap* map); + bool SetMap(MemMap* map, std::string* error_msg); byte* GetProgramHeadersStart(); byte* GetSectionHeadersStart(); From e21a40730ae51d8dfd0633dc021765a7839f70dc Mon Sep 17 00:00:00 2001 From: Brian Carlstrom Date: Thu, 7 Nov 2013 11:53:15 -0800 Subject: [PATCH 0150/2402] Revert "Update ART for LLVM merge up to r187914." This reverts commit f7ee11632e3dfda29d553d8962be9747d5ce6dfd. --- compiler/dex/portable/mir_to_gbc.cc | 2 +- compiler/llvm/llvm_compilation_unit.cc | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/compiler/dex/portable/mir_to_gbc.cc b/compiler/dex/portable/mir_to_gbc.cc index 07bd2aa979e..963cbeb1d15 100644 --- a/compiler/dex/portable/mir_to_gbc.cc +++ b/compiler/dex/portable/mir_to_gbc.cc @@ -1970,7 +1970,7 @@ void MirConverter::MethodMIR2Bitcode() { ::llvm::OwningPtr< ::llvm::tool_output_file> out_file( new ::llvm::tool_output_file(fname.c_str(), errmsg, - ::llvm::sys::fs::F_Binary)); + ::llvm::raw_fd_ostream::F_Binary)); if (!errmsg.empty()) { LOG(ERROR) << "Failed to create bitcode output file: " << errmsg; diff --git a/compiler/llvm/llvm_compilation_unit.cc b/compiler/llvm/llvm_compilation_unit.cc index 038f5dc4eb6..feb495e35fe 100644 --- a/compiler/llvm/llvm_compilation_unit.cc +++ b/compiler/llvm/llvm_compilation_unit.cc @@ -211,6 +211,7 @@ bool LlvmCompilationUnit::MaterializeToRawOStream(::llvm::raw_ostream& out_strea ::llvm::TargetOptions target_options; target_options.FloatABIType = ::llvm::FloatABI::Soft; target_options.NoFramePointerElim = true; + target_options.NoFramePointerElimNonLeaf = true; target_options.UseSoftFloat = false; target_options.EnableFastISel = false; @@ -254,7 +255,7 @@ bool LlvmCompilationUnit::MaterializeToRawOStream(::llvm::raw_ostream& out_strea ::llvm::OwningPtr< ::llvm::tool_output_file> out_file( new ::llvm::tool_output_file(bitcode_filename_.c_str(), errmsg, - ::llvm::sys::fs::F_Binary)); + ::llvm::raw_fd_ostream::F_Binary)); if (!errmsg.empty()) { @@ -274,6 +275,7 @@ bool LlvmCompilationUnit::MaterializeToRawOStream(::llvm::raw_ostream& out_strea // pm_builder.Inliner = ::llvm::createAlwaysInlinerPass(); // pm_builder.Inliner = ::llvm::createPartialInliningPass(); pm_builder.OptLevel = 3; + pm_builder.DisableSimplifyLibCalls = 1; pm_builder.DisableUnitAtATime = 1; pm_builder.populateFunctionPassManager(fpm); pm_builder.populateModulePassManager(pm); From dd577a3c9849105429fe7afb3559773d59aaafb6 Mon Sep 17 00:00:00 2001 From: Vladimir Marko Date: Thu, 7 Nov 2013 19:25:24 +0000 Subject: [PATCH 0151/2402] Disassemble Thumb2 vstm/vldm/vstr/vldr/vpush/vpop/vmov/vmrs. Not all versions of vmov are disassembled. Change-Id: I876199f7536d2a9429106deab821016fe8972469 --- disassembler/disassembler_arm.cc | 216 +++++++++++++++++++++---------- 1 file changed, 147 insertions(+), 69 deletions(-) diff --git a/disassembler/disassembler_arm.cc b/disassembler/disassembler_arm.cc index 8d4f3ce2630..65f23839048 100644 --- a/disassembler/disassembler_arm.cc +++ b/disassembler/disassembler_arm.cc @@ -180,6 +180,44 @@ std::ostream& operator<<(std::ostream& os, const RegisterList& rhs) { return os; } +struct FpRegister { + explicit FpRegister(uint32_t instr, uint16_t at_bit, uint16_t extra_at_bit) { + size = (instr >> 8) & 1; + uint32_t Vn = (instr >> at_bit) & 0xF; + uint32_t N = (instr >> extra_at_bit) & 1; + r = (size != 0 ? ((N << 4) | Vn) : ((Vn << 1) | N)); + } + FpRegister(const FpRegister& other, uint32_t offset) + : size(other.size), r(other.r + offset) {} + + uint32_t size; // 0 = f32, 1 = f64 + uint32_t r; +}; +std::ostream& operator<<(std::ostream& os, const FpRegister& rhs) { + return os << ((rhs.size != 0) ? "d" : "s") << rhs.r; +} + +struct FpRegisterRange { + explicit FpRegisterRange(uint32_t instr) + : first(instr, 12, 22), imm8(instr & 0xFF) {} + FpRegister first; + uint32_t imm8; +}; +std::ostream& operator<<(std::ostream& os, const FpRegisterRange& rhs) { + os << "{" << rhs.first; + int count = (rhs.first.size != 0 ? ((rhs.imm8 + 1u) >> 1) : rhs.imm8); + if (count > 1) { + os << "-" << FpRegister(rhs.first, count - 1); + } + if (rhs.imm8 == 0) { + os << " (EMPTY)"; + } else if (rhs.first.size != 0 && (rhs.imm8 & 1) != 0) { + os << rhs.first << " (HALF)"; + } + os << "}"; + return os; +} + void DisassemblerArm::DumpArm(std::ostream& os, const uint8_t* instr_ptr) { uint32_t instruction = ReadU32(instr_ptr); uint32_t cond = (instruction >> 28) & 0xf; @@ -616,57 +654,115 @@ size_t DisassemblerArm::DumpThumb32(std::ostream& os, const uint8_t* instr_ptr) uint32_t op4 = (instr >> 4) & 0x1; if (coproc == 10 || coproc == 11) { // 101x - if (op3 < 0x20 && (op3 >> 1) != 2) { // 0xxxxx and not 00010x - // extension load/store instructions - int op = op3 & 0x1f; + if (op3 < 0x20 && (op3 & ~5) != 0) { // 0xxxxx and not 000x0x + // Extension register load/store instructions + // |1111|110|00000|0000|1111|110|0|00000000| + // |5 2|1 9|87654|3 0|5 2|1 9|8|7 0| + // |----|---|-----|----|----|---|-|--------| + // |3322|222|22222|1111|1111|110|0|00000000| + // |1 8|7 5|4 0|9 6|5 2|1 9|8|7 0| + // |----|---|-----|----|----|---|-|--------| + // |1110|110|PUDWL| Rn | Vd |101|S| imm8 | uint32_t P = (instr >> 24) & 1; uint32_t U = (instr >> 23) & 1; - uint32_t D = (instr >> 22) & 1; uint32_t W = (instr >> 21) & 1; - uint32_t S = (instr >> 8) & 1; - ArmRegister Rn(instr, 16); - uint32_t Vd = (instr >> 12) & 0xF; - uint32_t imm8 = instr & 0xFF; - uint32_t d = (S == 0 ? ((Vd << 1) | D) : (Vd | (D << 4))); - ArmRegister Rd(d, 0); - - if (op == 8 || op == 12 || op == 10 || op == 14 || - op == 18 || op == 22) { // 01x00 or 01x10 - // vector store multiple or vpush - if (P == 1 && U == 0 && W == 1 && Rn.r == 13) { - opcode << "vpush" << (S == 0 ? ".f64" : ".f32"); - args << Rd << " .. " << (Rd.r + imm8); - } else { - opcode << "vstm" << (S == 0 ? ".f64" : ".f32"); - args << Rn << ", " << Rd << " .. " << (Rd.r + imm8); + if (P == U && W == 1) { + opcode << "UNDEFINED"; + } else { + uint32_t L = (instr >> 20) & 1; + uint32_t S = (instr >> 8) & 1; + ArmRegister Rn(instr, 16); + if (P == 1 && W == 0) { // VLDR + FpRegister d(instr, 12, 22); + uint32_t imm8 = instr & 0xFF; + opcode << (L == 1 ? "vldr" : "vstr"); + args << d << ", [" << Rn << ", #" << ((U == 1) ? "" : "-") + << (imm8 << 2) << "]"; + } else if (Rn.r == 13 && W == 1 && U == L) { // VPUSH/VPOP + opcode << (L == 1 ? "vpop" : "vpush"); + args << FpRegisterRange(instr); + } else { // VLDM + opcode << (L == 1 ? "vldm" : "vstm"); + args << Rn << ((W == 1) ? "!" : "") << ", " + << FpRegisterRange(instr); } - } else if (op == 16 || op == 20 || op == 24 || op == 28) { - // 1xx00 - // vector store register - opcode << "vstr" << (S == 0 ? ".f64" : ".f32"); - args << Rd << ", [" << Rn << ", #" << imm8 << "]"; - } else if (op == 17 || op == 21 || op == 25 || op == 29) { - // 1xx01 - // vector load register - opcode << "vldr" << (S == 0 ? ".f64" : ".f32"); - args << Rd << ", [" << Rn << ", #" << imm8 << "]"; - } else if (op == 9 || op == 13 || op == 11 || op == 15 || - op == 19 || op == 23 ) { // 01x11 10x11 - // vldm or vpop - if (P == 1 && U == 0 && W == 1 && Rn.r == 13) { - opcode << "vpop" << (S == 0 ? ".f64" : ".f32"); - args << Rd << " .. " << (Rd.r + imm8); + opcode << (S == 1 ? ".f64" : ".f32"); + } + } else if ((op3 >> 1) == 2) { // 00010x + if ((instr & 0xD0) == 0x10) { + // 64bit transfers between ARM core and extension registers. + uint32_t L = (instr >> 20) & 1; + uint32_t S = (instr >> 8) & 1; + ArmRegister Rt2(instr, 16); + ArmRegister Rt(instr, 12); + FpRegister m(instr, 0, 5); + opcode << "vmov" << (S ? ".f64" : ".f32"); + if (L == 1) { + args << Rt << ", " << Rt2 << ", "; + } + if (S) { + args << m; } else { - opcode << "vldm" << (S == 0 ? ".f64" : ".f32"); - args << Rn << ", " << Rd << " .. " << (Rd.r + imm8); + args << m << ", " << FpRegister(m, 1); + } + if (L == 0) { + args << ", " << Rt << ", " << Rt2; + } + if (Rt.r == 15 || Rt.r == 13 || Rt2.r == 15 || Rt2.r == 13 || + (S == 0 && m.r == 31) || (L == 1 && Rt.r == Rt2.r)) { + args << " (UNPREDICTABLE)"; } } - } else if ((op3 >> 1) == 2) { // 00010x - // 64 bit transfers } else if ((op3 >> 4) == 2 && op4 == 0) { // 10xxxx, op = 0 // fp data processing } else if ((op3 >> 4) == 2 && op4 == 1) { // 10xxxx, op = 1 - // 8,16,32 bit transfers + if (coproc == 10 && (op3 & 0xE) == 0) { + // VMOV (between ARM core register and single-precision register) + // |1111|1100|000|0 |0000|1111|1100|0|00|0|0000| + // |5 |1 8|7 5|4 |3 0|5 2|1 8|7|65|4|3 0| + // |----|----|---|- |----|----|----|-|--|-|----| + // |3322|2222|222|2 |1111|1111|1100|0|00|0|0000| + // |1 8|7 4|3 1|0 |9 6|5 2|1 8|7|65|4|3 0| + // |----|----|---|- |----|----|----|-|--|-|----| + // |1110|1110|000|op| Vn | Rt |1010|N|00|1|0000| + uint32_t op = op3 & 1; + ArmRegister Rt(instr, 12); + FpRegister n(instr, 16, 7); + opcode << "vmov.f32"; + if (op) { + args << Rt << ", " << n; + } else { + args << n << ", " << Rt; + } + if (Rt.r == 13 || Rt.r == 15 || (instr & 0x6F) != 0) { + args << " (UNPREDICTABLE)"; + } + } else if (coproc == 10 && op3 == 0x2F) { + // VMRS + // |1111|11000000|0000|1111|1100|000|0|0000| + // |5 |1 4|3 0|5 2|1 8|7 5|4|3 0| + // |----|--------|----|----|----|---|-|----| + // |3322|22222222|1111|1111|1100|000|0|0000| + // |1 8|7 0|9 6|5 2|1 8|7 5|4|3 0| + // |----|--------|----|----|----|---|-|----| + // |1110|11101111|reg | Rt |1010|000|1|0000| - last 7 0s are (0) + uint32_t spec_reg = (instr >> 16) & 0xF; + ArmRegister Rt(instr, 12); + opcode << "vmrs"; + if (spec_reg == 1) { + if (Rt.r == 15) { + args << "APSR_nzcv, FPSCR"; + } else if (Rt.r == 13) { + args << Rt << ", FPSCR (UNPREDICTABLE)"; + } else { + args << Rt << ", FPSCR"; + } + } else { + args << "(PRIVILEGED)"; + } + } else if (coproc == 11 && (op3 & 0x9) != 8) { + // VMOV (ARM core register to scalar or vice versa; 8/16/32-bit) + } } } @@ -686,30 +782,19 @@ size_t DisassemblerArm::DumpThumb32(std::ostream& os, const uint8_t* instr_ptr) uint32_t opc3 = (instr >> 6) & 0x3; if ((opc1 & 0xB) == 0xB) { // 1x11 // Other VFP data-processing instructions. - uint32_t D = (instr >> 22) & 0x1; - uint32_t Vd = (instr >> 12) & 0xF; uint32_t sz = (instr >> 8) & 1; - uint32_t M = (instr >> 5) & 1; - uint32_t Vm = instr & 0xF; - bool dp_operation = sz == 1; + FpRegister d(instr, 12, 22); + FpRegister m(instr, 0, 5); switch (opc2) { case 0x1: // Vneg/Vsqrt // 1110 11101 D 11 0001 dddd 101s o1M0 mmmm - opcode << (opc3 == 1 ? "vneg" : "vsqrt") << (dp_operation ? ".f64" : ".f32"); - if (dp_operation) { - args << "f" << ((D << 4) | Vd) << ", " << "f" << ((M << 4) | Vm); - } else { - args << "f" << ((Vd << 1) | D) << ", " << "f" << ((Vm << 1) | M); - } + opcode << (opc3 == 1 ? "vneg" : "vsqrt") << (sz == 1 ? ".f64" : ".f32"); + args << d << ", " << m; break; case 0x4: case 0x5: { // Vector compare // 1110 11101 D 11 0100 dddd 101 sE1M0 mmmm - opcode << (opc3 == 1 ? "vcmp" : "vcmpe") << (dp_operation ? ".f64" : ".f32"); - if (dp_operation) { - args << "f" << ((D << 4) | Vd) << ", " << "f" << ((M << 4) | Vm); - } else { - args << "f" << ((Vd << 1) | D) << ", " << "f" << ((Vm << 1) | M); - } + opcode << (opc3 == 1 ? "vcmp" : "vcmpe") << (sz == 1 ? ".f64" : ".f32"); + args << d << ", " << m; break; } } @@ -720,18 +805,11 @@ size_t DisassemblerArm::DumpThumb32(std::ostream& os, const uint8_t* instr_ptr) if ((instr & 0xFFBF0ED0) == 0xeeb10ac0) { // Vsqrt // 1110 11101 D 11 0001 dddd 101S 11M0 mmmm // 1110 11101 0 11 0001 1101 1011 1100 1000 - eeb1dbc8 - uint32_t D = (instr >> 22) & 1; - uint32_t Vd = (instr >> 12) & 0xF; uint32_t sz = (instr >> 8) & 1; - uint32_t M = (instr >> 5) & 1; - uint32_t Vm = instr & 0xF; - bool dp_operation = sz == 1; - opcode << "vsqrt" << (dp_operation ? ".f64" : ".f32"); - if (dp_operation) { - args << "f" << ((D << 4) | Vd) << ", " << "f" << ((M << 4) | Vm); - } else { - args << "f" << ((Vd << 1) | D) << ", " << "f" << ((Vm << 1) | M); - } + FpRegister d(instr, 12, 22); + FpRegister m(instr, 0, 5); + opcode << "vsqrt" << (sz == 1 ? ".f64" : ".f32"); + args << d << ", " << m; } } } From 8b31a36f846b63b1d375b31097773365ac8d7cb1 Mon Sep 17 00:00:00 2001 From: Brian Carlstrom Date: Thu, 7 Nov 2013 14:58:15 -0800 Subject: [PATCH 0152/2402] Change thread.h to thread-inl.h for missing Thread::Current for x86 Change-Id: I83ff019626b284edc8cf7232594f28472d8d9e5b --- runtime/arch/x86/thread_x86.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/arch/x86/thread_x86.cc b/runtime/arch/x86/thread_x86.cc index 42789cb1cd2..39bad586d2c 100644 --- a/runtime/arch/x86/thread_x86.cc +++ b/runtime/arch/x86/thread_x86.cc @@ -21,7 +21,7 @@ #include "asm_support_x86.h" #include "base/macros.h" -#include "thread.h" +#include "thread-inl.h" #include "thread_list.h" #if defined(__APPLE__) From 784639608387602e0c9e448e88db7809577f1667 Mon Sep 17 00:00:00 2001 From: Brian Carlstrom Date: Thu, 7 Nov 2013 15:48:35 -0800 Subject: [PATCH 0153/2402] Change thread.h to thread-inl.h for missing Thread::Current for mips Change-Id: Ic1c7c1870c1a49ea470d7f86a9565fea9bb85508 --- runtime/atomic.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/atomic.cc b/runtime/atomic.cc index c91db793ba8..47cee6aa2b8 100644 --- a/runtime/atomic.cc +++ b/runtime/atomic.cc @@ -23,7 +23,7 @@ #include "base/mutex.h" #include "base/stl_util.h" #include "base/stringprintf.h" -#include "thread.h" +#include "thread-inl.h" #endif namespace art { From 2b130681f724a6b306f17bab63fe3e20565a9af9 Mon Sep 17 00:00:00 2001 From: Brian Carlstrom Date: Fri, 8 Nov 2013 16:42:41 -0800 Subject: [PATCH 0154/2402] Fix BUILD_DALVIK_HOST_JAVA_LIBRARY to BUILD_HOST_DALVIK_JAVA_LIBRARY Change-Id: I69f74e7207b19caae376746dc2dce102cbfde186 --- test/Android.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/Android.mk b/test/Android.mk index 6b992127e07..1bf6074c207 100644 --- a/test/Android.mk +++ b/test/Android.mk @@ -87,7 +87,7 @@ define build-art-test-dex LOCAL_DEX_PREOPT_IMAGE := $(HOST_CORE_IMG_OUT) LOCAL_ADDITIONAL_DEPENDENCIES := art/build/Android.common.mk LOCAL_ADDITIONAL_DEPENDENCIES += $(LOCAL_PATH)/Android.mk - include $(BUILD_DALVIK_HOST_JAVA_LIBRARY) + include $(BUILD_HOST_DALVIK_JAVA_LIBRARY) ART_TEST_HOST_DEX_FILES += $$(LOCAL_MODULE_PATH)/$$(LOCAL_MODULE).jar endif endef From d7997ebc2ae418d593fab83afe484f161f52b455 Mon Sep 17 00:00:00 2001 From: Brian Carlstrom Date: Fri, 8 Nov 2013 16:42:41 -0800 Subject: [PATCH 0155/2402] Fix BUILD_DALVIK_HOST_JAVA_LIBRARY to BUILD_HOST_DALVIK_JAVA_LIBRARY Change-Id: I69f74e7207b19caae376746dc2dce102cbfde186 --- test/Android.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/Android.mk b/test/Android.mk index 6b992127e07..1bf6074c207 100644 --- a/test/Android.mk +++ b/test/Android.mk @@ -87,7 +87,7 @@ define build-art-test-dex LOCAL_DEX_PREOPT_IMAGE := $(HOST_CORE_IMG_OUT) LOCAL_ADDITIONAL_DEPENDENCIES := art/build/Android.common.mk LOCAL_ADDITIONAL_DEPENDENCIES += $(LOCAL_PATH)/Android.mk - include $(BUILD_DALVIK_HOST_JAVA_LIBRARY) + include $(BUILD_HOST_DALVIK_JAVA_LIBRARY) ART_TEST_HOST_DEX_FILES += $$(LOCAL_MODULE_PATH)/$$(LOCAL_MODULE).jar endif endef From e1467659568500cefd84abc4cdb399d8db7e214d Mon Sep 17 00:00:00 2001 From: Elliott Hughes Date: Mon, 11 Nov 2013 11:15:43 -0800 Subject: [PATCH 0156/2402] Fix capability setting for the top 32 bits. Bug: 11508244 Change-Id: I83523dfef9946466e1628c461915dac00769c03d --- runtime/native/dalvik_system_Zygote.cc | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/runtime/native/dalvik_system_Zygote.cc b/runtime/native/dalvik_system_Zygote.cc index d065ee44830..dac2c894ee0 100644 --- a/runtime/native/dalvik_system_Zygote.cc +++ b/runtime/native/dalvik_system_Zygote.cc @@ -230,18 +230,18 @@ static void DropCapabilitiesBoundingSet() { static void SetCapabilities(int64_t permitted, int64_t effective) { __user_cap_header_struct capheader; - __user_cap_data_struct capdata; - memset(&capheader, 0, sizeof(capheader)); - memset(&capdata, 0, sizeof(capdata)); - - capheader.version = _LINUX_CAPABILITY_VERSION; + capheader.version = _LINUX_CAPABILITY_VERSION_3; capheader.pid = 0; - capdata.effective = effective; - capdata.permitted = permitted; + __user_cap_data_struct capdata[2]; + memset(&capdata, 0, sizeof(capdata)); + capdata[0].effective = effective; + capdata[1].effective = effective >> 32; + capdata[0].permitted = permitted; + capdata[1].permitted = permitted >> 32; - if (capset(&capheader, &capdata) != 0) { + if (capset(&capheader, &capdata[0]) == -1) { PLOG(FATAL) << "capset(" << permitted << ", " << effective << ") failed"; } } From 1e7d1d5084e730cc800de782b4f8b00c62bda40c Mon Sep 17 00:00:00 2001 From: William Roberts Date: Mon, 11 Nov 2013 07:28:37 -0800 Subject: [PATCH 0157/2402] Set processes' comm names to their package names. When debugging audit logs, it is often helpful to have the package's name. Bear in mind that the package name is the right most chars up to TASK_COMM_LEN. Although a possibility of truncation exists, it can be useful for narrowing down the possibilities. (cherry-pick of 5ae4516eb30c7cf674025ab01b097e62c7901b98 from AOSP.) Change-Id: I27dfd986ebe86af4a5b8c702af955d5065daa12f --- runtime/native/dalvik_system_Zygote.cc | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/runtime/native/dalvik_system_Zygote.cc b/runtime/native/dalvik_system_Zygote.cc index dac2c894ee0..2e3d6a6ab4a 100644 --- a/runtime/native/dalvik_system_Zygote.cc +++ b/runtime/native/dalvik_system_Zygote.cc @@ -36,6 +36,7 @@ #include "ScopedPrimitiveArray.h" #include "ScopedUtfChars.h" #include "thread-inl.h" +#include "utils.h" #if defined(HAVE_PRCTL) #include @@ -496,6 +497,15 @@ static pid_t ForkAndSpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArra << (is_system_server ? "true" : "false") << ", " << "\"" << se_info_c_str << "\", \"" << se_name_c_str << "\") failed"; } + + // Make it easier to debug audit logs by setting the main thread's name to the + // nice name rather than "app_process". + if (se_info_c_str == NULL && is_system_server) { + se_name_c_str = "system_server"; + } + if (se_info_c_str != NULL) { + SetThreadName(se_name_c_str); + } } #else UNUSED(is_system_server); From 590fee9e8972f872301c2d16a575d579ee564bee Mon Sep 17 00:00:00 2001 From: Mathieu Chartier Date: Fri, 13 Sep 2013 13:46:47 -0700 Subject: [PATCH 0158/2402] Compacting collector. The compacting collector is currently similar to semispace. It works by copying objects back and forth between two bump pointer spaces. There are types of objects which are "non-movable" due to current runtime limitations. These are Classes, Methods, and Fields. Bump pointer spaces are a new type of continuous alloc space which have no lock in the allocation code path. When you allocate from these it uses atomic operations to increase an index. Traversing the objects in the bump pointer space relies on Object::SizeOf matching the allocated size exactly. Runtime changes: JNI::GetArrayElements returns copies objects if you attempt to get the backing data of a movable array. For GetArrayElementsCritical, we return direct backing storage for any types of arrays, but temporarily disable the GC until the critical region is completed. Added a new runtime call called VisitObjects, this is used in place of the old pattern which was flushing the allocation stack and walking the bitmaps. Changed image writer to be compaction safe and use object monitor word for forwarding addresses. Added a bunch of added SIRTs to ClassLinker, MethodLinker, etc.. TODO: Enable switching allocators, compacting on background, etc.. Bug: 8981901 Change-Id: I3c886fd322a6eef2b99388d19a765042ec26ab99 --- compiler/driver/compiler_driver.cc | 97 +- compiler/driver/compiler_driver.h | 3 +- compiler/driver/compiler_driver_test.cc | 9 +- compiler/image_test.cc | 11 +- compiler/image_writer.cc | 346 ++++--- compiler/image_writer.h | 48 +- compiler/jni/jni_compiler_test.cc | 4 +- compiler/oat_test.cc | 3 +- compiler/oat_writer.cc | 37 +- oatdump/oatdump.cc | 57 +- runtime/Android.mk | 2 + runtime/arch/alloc_entrypoints.S | 36 + runtime/arch/arm/quick_entrypoints_arm.S | 203 +--- runtime/arch/x86/quick_entrypoints_x86.S | 26 +- runtime/base/mutex-inl.h | 2 +- runtime/base/mutex.cc | 6 +- runtime/base/mutex.h | 5 + runtime/base/timing_logger.cc | 5 + runtime/base/timing_logger.h | 1 + runtime/check_jni.cc | 17 +- runtime/class_linker-inl.h | 34 +- runtime/class_linker.cc | 356 ++++--- runtime/class_linker.h | 80 +- runtime/class_linker_test.cc | 48 +- runtime/common_test.h | 15 +- runtime/debugger.cc | 32 +- runtime/dex_file.cc | 14 +- runtime/dex_file.h | 10 +- runtime/entrypoints/entrypoint_utils.cc | 4 +- runtime/entrypoints/entrypoint_utils.h | 8 +- .../quick/quick_lock_entrypoints.cc | 12 +- .../quick/quick_trampoline_entrypoints.cc | 10 +- runtime/exception_test.cc | 2 +- runtime/gc/accounting/mod_union_table.cc | 8 +- runtime/gc/collector/garbage_collector.cc | 10 +- runtime/gc/collector/garbage_collector.h | 38 +- runtime/gc/collector/mark_sweep-inl.h | 16 +- runtime/gc/collector/mark_sweep.cc | 105 +- runtime/gc/collector/mark_sweep.h | 53 +- runtime/gc/collector/partial_mark_sweep.cc | 2 +- runtime/gc/collector/semi_space-inl.h | 37 + runtime/gc/collector/semi_space.cc | 799 +++++++++++++++ runtime/gc/collector/semi_space.h | 289 ++++++ runtime/gc/collector/sticky_mark_sweep.cc | 5 +- runtime/gc/collector/sticky_mark_sweep.h | 8 +- runtime/gc/heap-inl.h | 51 +- runtime/gc/heap.cc | 909 +++++++++++------- runtime/gc/heap.h | 158 ++- runtime/gc/heap_test.cc | 8 +- runtime/gc/space/bump_pointer_space-inl.h | 52 + runtime/gc/space/bump_pointer_space.cc | 88 ++ runtime/gc/space/bump_pointer_space.h | 149 +++ runtime/gc/space/dlmalloc_space.cc | 75 +- runtime/gc/space/dlmalloc_space.h | 19 +- runtime/gc/space/image_space.cc | 7 +- runtime/gc/space/large_object_space.h | 8 + runtime/gc/space/space-inl.h | 18 +- runtime/gc/space/space.cc | 1 - runtime/gc/space/space.h | 124 ++- runtime/gc/space/space_test.cc | 14 +- runtime/globals.h | 9 + runtime/intern_table.cc | 8 +- runtime/interpreter/interpreter.cc | 4 +- runtime/interpreter/interpreter_common.cc | 7 +- runtime/jni_internal.cc | 142 ++- runtime/jni_internal.h | 3 + runtime/jni_internal_test.cc | 22 +- runtime/lock_word-inl.h | 5 + runtime/lock_word.h | 32 +- runtime/mirror/array-inl.h | 40 +- runtime/mirror/array.cc | 15 +- runtime/mirror/array.h | 23 +- runtime/mirror/class-inl.h | 23 +- runtime/mirror/class.cc | 12 +- runtime/mirror/class.h | 7 +- runtime/mirror/object.cc | 64 +- runtime/mirror/object.h | 1 + runtime/mirror/object_array-inl.h | 9 +- runtime/mirror/object_test.cc | 32 +- runtime/mirror/stack_trace_element.cc | 12 +- runtime/mirror/stack_trace_element.h | 7 +- runtime/mirror/string.cc | 27 +- runtime/mirror/string.h | 6 +- runtime/monitor.cc | 97 +- runtime/monitor.h | 6 +- runtime/native/dalvik_system_DexFile.cc | 2 +- runtime/native/dalvik_system_VMRuntime.cc | 66 +- runtime/native/java_lang_Class.cc | 3 +- runtime/native/java_lang_reflect_Array.cc | 6 +- runtime/native/java_lang_reflect_Proxy.cc | 16 +- .../native/scoped_fast_native_object_access.h | 8 +- runtime/object_utils.h | 138 +-- runtime/reference_table.cc | 2 +- runtime/root_visitor.h | 2 + runtime/runtime.cc | 67 +- runtime/runtime.h | 7 +- runtime/scoped_thread_state_change.h | 29 +- runtime/sirt_ref.h | 1 + runtime/stack.cc | 5 + runtime/stack.h | 38 +- runtime/thread.cc | 49 +- runtime/thread.h | 41 +- runtime/thread_list.cc | 15 +- runtime/thread_list.h | 3 + runtime/utils.h | 2 +- runtime/verifier/method_verifier.cc | 176 ++-- runtime/verifier/method_verifier.h | 27 +- runtime/verifier/reg_type.cc | 3 +- runtime/verifier/reg_type_cache.cc | 3 +- 109 files changed, 3920 insertions(+), 1996 deletions(-) create mode 100644 runtime/arch/alloc_entrypoints.S create mode 100644 runtime/gc/collector/semi_space-inl.h create mode 100644 runtime/gc/collector/semi_space.cc create mode 100644 runtime/gc/collector/semi_space.h create mode 100644 runtime/gc/space/bump_pointer_space-inl.h create mode 100644 runtime/gc/space/bump_pointer_space.cc create mode 100644 runtime/gc/space/bump_pointer_space.h diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc index 9cc94e8c0df..4af492bf6e8 100644 --- a/compiler/driver/compiler_driver.cc +++ b/compiler/driver/compiler_driver.cc @@ -513,10 +513,9 @@ void CompilerDriver::CompileAll(jobject class_loader, } } -static DexToDexCompilationLevel GetDexToDexCompilationlevel(mirror::ClassLoader* class_loader, - const DexFile& dex_file, - const DexFile::ClassDef& class_def) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { +static DexToDexCompilationLevel GetDexToDexCompilationlevel( + SirtRef& class_loader, const DexFile& dex_file, + const DexFile::ClassDef& class_def) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { const char* descriptor = dex_file.GetClassDescriptor(class_def); ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); mirror::Class* klass = class_linker->FindClass(descriptor, class_loader); @@ -531,7 +530,7 @@ static DexToDexCompilationLevel GetDexToDexCompilationlevel(mirror::ClassLoader* // function). Since image classes can be verified again while compiling an application, // we must prevent the DEX-to-DEX compiler from introducing them. // TODO: find a way to enable "quick" instructions for image classes and remove this check. - bool compiling_image_classes = (class_loader == NULL); + bool compiling_image_classes = class_loader.get() == nullptr; if (compiling_image_classes) { return kRequired; } else if (klass->IsVerified()) { @@ -579,7 +578,8 @@ void CompilerDriver::CompileOne(const mirror::ArtMethod* method, base::TimingLog { ScopedObjectAccess soa(Thread::Current()); const DexFile::ClassDef& class_def = dex_file->GetClassDef(class_def_idx); - mirror::ClassLoader* class_loader = soa.Decode(jclass_loader); + SirtRef class_loader(soa.Self(), + soa.Decode(jclass_loader)); dex_to_dex_compilation_level = GetDexToDexCompilationlevel(class_loader, *dex_file, class_def); } CompileMethod(code_item, method->GetAccessFlags(), method->GetInvokeType(), @@ -721,8 +721,8 @@ void CompilerDriver::LoadImageClasses(base::TimingLogger& timings) for (const std::pair& exception_type : unresolved_exception_types) { uint16_t exception_type_idx = exception_type.first; const DexFile* dex_file = exception_type.second; - mirror::DexCache* dex_cache = class_linker->FindDexCache(*dex_file); - mirror:: ClassLoader* class_loader = NULL; + SirtRef dex_cache(self, class_linker->FindDexCache(*dex_file)); + SirtRef class_loader(self, nullptr); SirtRef klass(self, class_linker->ResolveType(*dex_file, exception_type_idx, dex_cache, class_loader)); if (klass.get() == NULL) { @@ -782,15 +782,14 @@ void CompilerDriver::UpdateImageClasses(base::TimingLogger& timings) { const char* old_cause = self->StartAssertNoThreadSuspension("ImageWriter"); gc::Heap* heap = Runtime::Current()->GetHeap(); // TODO: Image spaces only? + ScopedObjectAccess soa(Thread::Current()); WriterMutexLock mu(self, *Locks::heap_bitmap_lock_); - heap->FlushAllocStack(); - heap->GetLiveBitmap()->Walk(FindClinitImageClassesCallback, this); + heap->VisitObjects(FindClinitImageClassesCallback, this); self->EndAssertNoThreadSuspension(old_cause); } } -bool CompilerDriver::CanAssumeTypeIsPresentInDexCache(const DexFile& dex_file, - uint32_t type_idx) { +bool CompilerDriver::CanAssumeTypeIsPresentInDexCache(const DexFile& dex_file, uint32_t type_idx) { if (IsImage() && IsImageClass(dex_file.StringDataByIdx(dex_file.GetTypeId(type_idx).descriptor_idx_))) { if (kIsDebugBuild) { @@ -815,7 +814,7 @@ bool CompilerDriver::CanAssumeStringIsPresentInDexCache(const DexFile& dex_file, if (IsImage()) { // We resolve all const-string strings when building for the image. ScopedObjectAccess soa(Thread::Current()); - mirror::DexCache* dex_cache = Runtime::Current()->GetClassLinker()->FindDexCache(dex_file); + SirtRef dex_cache(soa.Self(), Runtime::Current()->GetClassLinker()->FindDexCache(dex_file)); Runtime::Current()->GetClassLinker()->ResolveString(dex_file, string_idx, dex_cache); result = true; } @@ -903,26 +902,27 @@ bool CompilerDriver::CanAccessInstantiableTypeWithoutChecks(uint32_t referrer_id } static mirror::Class* ComputeCompilingMethodsClass(ScopedObjectAccess& soa, - mirror::DexCache* dex_cache, + SirtRef& dex_cache, const DexCompilationUnit* mUnit) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { // The passed dex_cache is a hint, sanity check before asking the class linker that will take a // lock. if (dex_cache->GetDexFile() != mUnit->GetDexFile()) { - dex_cache = mUnit->GetClassLinker()->FindDexCache(*mUnit->GetDexFile()); + dex_cache.reset(mUnit->GetClassLinker()->FindDexCache(*mUnit->GetDexFile())); } - mirror::ClassLoader* class_loader = soa.Decode(mUnit->GetClassLoader()); - const DexFile::MethodId& referrer_method_id = mUnit->GetDexFile()->GetMethodId(mUnit->GetDexMethodIndex()); + SirtRef + class_loader(soa.Self(), soa.Decode(mUnit->GetClassLoader())); + const DexFile::MethodId& referrer_method_id = + mUnit->GetDexFile()->GetMethodId(mUnit->GetDexMethodIndex()); return mUnit->GetClassLinker()->ResolveType(*mUnit->GetDexFile(), referrer_method_id.class_idx_, dex_cache, class_loader); } -static mirror::ArtField* ComputeFieldReferencedFromCompilingMethod(ScopedObjectAccess& soa, - const DexCompilationUnit* mUnit, - uint32_t field_idx) +static mirror::ArtField* ComputeFieldReferencedFromCompilingMethod( + ScopedObjectAccess& soa, const DexCompilationUnit* mUnit, uint32_t field_idx) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - mirror::DexCache* dex_cache = mUnit->GetClassLinker()->FindDexCache(*mUnit->GetDexFile()); - mirror::ClassLoader* class_loader = soa.Decode(mUnit->GetClassLoader()); + SirtRef dex_cache(soa.Self(), mUnit->GetClassLinker()->FindDexCache(*mUnit->GetDexFile())); + SirtRef class_loader(soa.Self(), soa.Decode(mUnit->GetClassLoader())); return mUnit->GetClassLinker()->ResolveField(*mUnit->GetDexFile(), field_idx, dex_cache, class_loader, false); } @@ -932,8 +932,8 @@ static mirror::ArtMethod* ComputeMethodReferencedFromCompilingMethod(ScopedObjec uint32_t method_idx, InvokeType type) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - mirror::DexCache* dex_cache = mUnit->GetClassLinker()->FindDexCache(*mUnit->GetDexFile()); - mirror::ClassLoader* class_loader = soa.Decode(mUnit->GetClassLoader()); + SirtRef dex_cache(soa.Self(), mUnit->GetClassLinker()->FindDexCache(*mUnit->GetDexFile())); + SirtRef class_loader(soa.Self(), soa.Decode(mUnit->GetClassLoader())); return mUnit->GetClassLinker()->ResolveMethod(*mUnit->GetDexFile(), method_idx, dex_cache, class_loader, NULL, type); } @@ -947,9 +947,10 @@ bool CompilerDriver::ComputeInstanceFieldInfo(uint32_t field_idx, const DexCompi // Try to resolve field and ignore if an Incompatible Class Change Error (ie is static). mirror::ArtField* resolved_field = ComputeFieldReferencedFromCompilingMethod(soa, mUnit, field_idx); if (resolved_field != NULL && !resolved_field->IsStatic()) { + SirtRef dex_cache(soa.Self(), + resolved_field->GetDeclaringClass()->GetDexCache()); mirror::Class* referrer_class = - ComputeCompilingMethodsClass(soa, resolved_field->GetDeclaringClass()->GetDexCache(), - mUnit); + ComputeCompilingMethodsClass(soa, dex_cache, mUnit); if (referrer_class != NULL) { mirror::Class* fields_class = resolved_field->GetDeclaringClass(); bool access_ok = referrer_class->CanAccess(fields_class) && @@ -997,9 +998,9 @@ bool CompilerDriver::ComputeStaticFieldInfo(uint32_t field_idx, const DexCompila // Try to resolve field and ignore if an Incompatible Class Change Error (ie isn't static). mirror::ArtField* resolved_field = ComputeFieldReferencedFromCompilingMethod(soa, mUnit, field_idx); if (resolved_field != NULL && resolved_field->IsStatic()) { + SirtRef dex_cache(soa.Self(), resolved_field->GetDeclaringClass()->GetDexCache()); mirror::Class* referrer_class = - ComputeCompilingMethodsClass(soa, resolved_field->GetDeclaringClass()->GetDexCache(), - mUnit); + ComputeCompilingMethodsClass(soa, dex_cache, mUnit); if (referrer_class != NULL) { mirror::Class* fields_class = resolved_field->GetDeclaringClass(); if (fields_class == referrer_class) { @@ -1085,7 +1086,7 @@ void CompilerDriver::GetCodeAndMethodForDirectCall(InvokeType* type, InvokeType *direct_code = 0; *direct_method = 0; bool use_dex_cache = false; - bool compiling_boot = Runtime::Current()->GetHeap()->GetContinuousSpaces().size() == 1; + const bool compiling_boot = Runtime::Current()->GetHeap()->IsCompilingBoot(); if (compiler_backend_ == kPortable) { if (sharp_type != kStatic && sharp_type != kDirect) { return; @@ -1198,9 +1199,9 @@ bool CompilerDriver::ComputeInvokeInfo(const DexCompilationUnit* mUnit, const ui } // Don't try to fast-path if we don't understand the caller's class or this appears to be an // Incompatible Class Change Error. + SirtRef dex_cache(soa.Self(), resolved_method->GetDeclaringClass()->GetDexCache()); mirror::Class* referrer_class = - ComputeCompilingMethodsClass(soa, resolved_method->GetDeclaringClass()->GetDexCache(), - mUnit); + ComputeCompilingMethodsClass(soa, dex_cache, mUnit); bool icce = resolved_method->CheckIncompatibleClassChange(*invoke_type); if (referrer_class != NULL && !icce) { mirror::Class* methods_class = resolved_method->GetDeclaringClass(); @@ -1254,10 +1255,8 @@ bool CompilerDriver::ComputeInvokeInfo(const DexCompilationUnit* mUnit, const ui const MethodReference* devirt_map_target = verifier::MethodVerifier::GetDevirtMap(caller_method, dex_pc); if (devirt_map_target != NULL) { - mirror::DexCache* target_dex_cache = - mUnit->GetClassLinker()->FindDexCache(*devirt_map_target->dex_file); - mirror::ClassLoader* class_loader = - soa.Decode(mUnit->GetClassLoader()); + SirtRef target_dex_cache(soa.Self(), mUnit->GetClassLinker()->FindDexCache(*devirt_map_target->dex_file)); + SirtRef class_loader(soa.Self(), soa.Decode(mUnit->GetClassLoader())); mirror::ArtMethod* called_method = mUnit->GetClassLinker()->ResolveMethod(*devirt_map_target->dex_file, devirt_map_target->dex_method_index, @@ -1509,13 +1508,11 @@ static void ResolveClassFieldsAndMethods(const ParallelCompilationManager* manag const DexFile::ClassDef& class_def = dex_file.GetClassDef(class_def_index); if (!SkipClass(class_linker, jclass_loader, dex_file, class_def)) { ScopedObjectAccess soa(self); - mirror::ClassLoader* class_loader = soa.Decode(jclass_loader); - mirror::DexCache* dex_cache = class_linker->FindDexCache(dex_file); - + SirtRef class_loader(soa.Self(), soa.Decode(jclass_loader)); + SirtRef dex_cache(soa.Self(), class_linker->FindDexCache(dex_file)); // Resolve the class. mirror::Class* klass = class_linker->ResolveType(dex_file, class_def.class_idx_, dex_cache, class_loader); - bool resolve_fields_and_methods; if (klass == NULL) { // Class couldn't be resolved, for example, super-class is in a different dex file. Don't @@ -1598,8 +1595,8 @@ static void ResolveType(const ParallelCompilationManager* manager, size_t type_i ScopedObjectAccess soa(Thread::Current()); ClassLinker* class_linker = manager->GetClassLinker(); const DexFile& dex_file = *manager->GetDexFile(); - mirror::DexCache* dex_cache = class_linker->FindDexCache(dex_file); - mirror::ClassLoader* class_loader = soa.Decode(manager->GetClassLoader()); + SirtRef dex_cache(soa.Self(), class_linker->FindDexCache(dex_file)); + SirtRef class_loader(soa.Self(), soa.Decode(manager->GetClassLoader())); mirror::Class* klass = class_linker->ResolveType(dex_file, type_idx, dex_cache, class_loader); if (klass == NULL) { @@ -1652,8 +1649,9 @@ static void VerifyClass(const ParallelCompilationManager* manager, size_t class_ const char* descriptor = dex_file.GetClassDescriptor(class_def); ClassLinker* class_linker = manager->GetClassLinker(); jobject jclass_loader = manager->GetClassLoader(); - mirror::Class* klass = class_linker->FindClass(descriptor, - soa.Decode(jclass_loader)); + SirtRef class_loader( + soa.Self(), soa.Decode(jclass_loader)); + mirror::Class* klass = class_linker->FindClass(descriptor, class_loader); if (klass == NULL) { CHECK(soa.Self()->IsExceptionPending()); soa.Self()->ClearException(); @@ -1663,11 +1661,10 @@ static void VerifyClass(const ParallelCompilationManager* manager, size_t class_ * This is to ensure the class is structurally sound for compilation. An unsound class * will be rejected by the verifier and later skipped during compilation in the compiler. */ - mirror::DexCache* dex_cache = class_linker->FindDexCache(dex_file); + SirtRef dex_cache(soa.Self(), class_linker->FindDexCache(dex_file)); std::string error_msg; - if (verifier::MethodVerifier::VerifyClass(&dex_file, dex_cache, - soa.Decode(jclass_loader), - &class_def, true, &error_msg) == + if (verifier::MethodVerifier::VerifyClass(&dex_file, dex_cache, class_loader, &class_def, true, + &error_msg) == verifier::MethodVerifier::kHardFailure) { LOG(ERROR) << "Verification failed on class " << PrettyDescriptor(descriptor) << " because: " << error_msg; @@ -2124,7 +2121,8 @@ static void InitializeClass(const ParallelCompilationManager* manager, size_t cl const char* descriptor = dex_file.StringDataByIdx(class_type_id.descriptor_idx_); ScopedObjectAccess soa(Thread::Current()); - mirror::ClassLoader* class_loader = soa.Decode(jclass_loader); + SirtRef class_loader(soa.Self(), + soa.Decode(jclass_loader)); mirror::Class* klass = manager->GetClassLinker()->FindClass(descriptor, class_loader); if (klass != NULL && !SkipClass(jclass_loader, dex_file, klass)) { @@ -2253,7 +2251,8 @@ void CompilerDriver::CompileClass(const ParallelCompilationManager* manager, siz DexToDexCompilationLevel dex_to_dex_compilation_level = kDontDexToDexCompile; { ScopedObjectAccess soa(Thread::Current()); - mirror::ClassLoader* class_loader = soa.Decode(jclass_loader); + SirtRef class_loader(soa.Self(), + soa.Decode(jclass_loader)); dex_to_dex_compilation_level = GetDexToDexCompilationlevel(class_loader, dex_file, class_def); } ClassDataItemIterator it(dex_file, class_data); diff --git a/compiler/driver/compiler_driver.h b/compiler/driver/compiler_driver.h index 9321f065263..9bfea6ff0ad 100644 --- a/compiler/driver/compiler_driver.h +++ b/compiler/driver/compiler_driver.h @@ -368,7 +368,8 @@ class CompilerDriver { ThreadPool& thread_pool, base::TimingLogger& timings) LOCKS_EXCLUDED(Locks::mutator_lock_, compiled_classes_lock_); - void UpdateImageClasses(base::TimingLogger& timings); + void UpdateImageClasses(base::TimingLogger& timings) + LOCKS_EXCLUDED(Locks::mutator_lock_); static void FindClinitImageClassesCallback(mirror::Object* object, void* arg) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); diff --git a/compiler/driver/compiler_driver_test.cc b/compiler/driver/compiler_driver_test.cc index c6687bb4aaf..bfc93b3c8f9 100644 --- a/compiler/driver/compiler_driver_test.cc +++ b/compiler/driver/compiler_driver_test.cc @@ -78,7 +78,9 @@ class CompilerDriverTest : public CommonTest { const DexFile::ClassDef& class_def = dex_file.GetClassDef(i); const char* descriptor = dex_file.GetClassDescriptor(class_def); ScopedObjectAccess soa(Thread::Current()); - mirror::Class* c = class_linker->FindClass(descriptor, soa.Decode(class_loader)); + Thread* self = Thread::Current(); + SirtRef loader(self, soa.Decode(class_loader)); + mirror::Class* c = class_linker->FindClass(descriptor, loader); CHECK(c != NULL); for (size_t i = 0; i < c->NumDirectMethods(); i++) { MakeExecutable(c->GetDirectMethod(i)); @@ -142,8 +144,9 @@ TEST_F(CompilerDriverTest, AbstractMethodErrorStub) { jobject class_loader; { ScopedObjectAccess soa(Thread::Current()); - CompileVirtualMethod(NULL, "java.lang.Class", "isFinalizable", "()Z"); - CompileDirectMethod(NULL, "java.lang.Object", "", "()V"); + SirtRef null_loader(soa.Self(), nullptr); + CompileVirtualMethod(null_loader, "java.lang.Class", "isFinalizable", "()Z"); + CompileDirectMethod(null_loader, "java.lang.Object", "", "()V"); class_loader = LoadDex("AbstractMethod"); } ASSERT_TRUE(class_loader != NULL); diff --git a/compiler/image_test.cc b/compiler/image_test.cc index a8b7c881f48..9d9c06401e7 100644 --- a/compiler/image_test.cc +++ b/compiler/image_test.cc @@ -94,8 +94,8 @@ TEST_F(ImageTest, WriteRead) { ASSERT_NE(0U, image_header.GetImageBitmapSize()); gc::Heap* heap = Runtime::Current()->GetHeap(); - ASSERT_EQ(1U, heap->GetContinuousSpaces().size()); - gc::space::ContinuousSpace* space = heap->GetContinuousSpaces().front(); + ASSERT_TRUE(!heap->GetContinuousSpaces().empty()); + gc::space::ContinuousSpace* space = heap->GetNonMovingSpace(); ASSERT_FALSE(space->IsImageSpace()); ASSERT_TRUE(space != NULL); ASSERT_TRUE(space->IsDlMallocSpace()); @@ -139,11 +139,8 @@ TEST_F(ImageTest, WriteRead) { class_linker_ = runtime_->GetClassLinker(); gc::Heap* heap = Runtime::Current()->GetHeap(); - ASSERT_EQ(2U, heap->GetContinuousSpaces().size()); - ASSERT_TRUE(heap->GetContinuousSpaces()[0]->IsImageSpace()); - ASSERT_FALSE(heap->GetContinuousSpaces()[0]->IsDlMallocSpace()); - ASSERT_FALSE(heap->GetContinuousSpaces()[1]->IsImageSpace()); - ASSERT_TRUE(heap->GetContinuousSpaces()[1]->IsDlMallocSpace()); + ASSERT_TRUE(heap->HasImageSpace()); + ASSERT_TRUE(heap->GetNonMovingSpace()->IsDlMallocSpace()); gc::space::ImageSpace* image_space = heap->GetImageSpace(); image_space->VerifyImageAllocations(); diff --git a/compiler/image_writer.cc b/compiler/image_writer.cc index 75be2c9c439..c22f8d6c012 100644 --- a/compiler/image_writer.cc +++ b/compiler/image_writer.cc @@ -75,8 +75,6 @@ bool ImageWriter::Write(const std::string& image_filename, image_begin_ = reinterpret_cast(image_begin); ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); - const std::vector& all_dex_caches = class_linker->GetDexCaches(); - dex_caches_.insert(all_dex_caches.begin(), all_dex_caches.end()); UniquePtr oat_file(OS::OpenFileReadWrite(oat_filename.c_str())); if (oat_file.get() == NULL) { @@ -121,22 +119,16 @@ bool ImageWriter::Write(const std::string& image_filename, } gc::Heap* heap = Runtime::Current()->GetHeap(); heap->CollectGarbage(false); // Remove garbage. - // Trim size of alloc spaces. - for (const auto& space : heap->GetContinuousSpaces()) { - if (space->IsDlMallocSpace()) { - space->AsDlMallocSpace()->Trim(); - } - } if (!AllocMemory()) { return false; } -#ifndef NDEBUG - { // NOLINT(whitespace/braces) + + if (kIsDebugBuild) { ScopedObjectAccess soa(Thread::Current()); CheckNonImageClassesRemoved(); } -#endif + Thread::Current()->TransitionFromSuspendedToRunnable(); size_t oat_loaded_size = 0; size_t oat_data_offset = 0; @@ -144,8 +136,6 @@ bool ImageWriter::Write(const std::string& image_filename, CalculateNewObjectOffsets(oat_loaded_size, oat_data_offset); CopyAndFixupObjects(); PatchOatCodeAndMethods(); - // Record allocations into the image bitmap. - RecordImageAllocations(); Thread::Current()->TransitionFromRunnableToSuspended(kNative); UniquePtr image_file(OS::CreateEmptyFile(image_filename.c_str())); @@ -178,39 +168,82 @@ bool ImageWriter::Write(const std::string& image_filename, return true; } -void ImageWriter::RecordImageAllocations() { - uint64_t start_time = NanoTime(); - CHECK(image_bitmap_.get() != nullptr); - for (const auto& it : offsets_) { - mirror::Object* obj = reinterpret_cast(image_->Begin() + it.second); - DCHECK_ALIGNED(obj, kObjectAlignment); - image_bitmap_->Set(obj); +void ImageWriter::SetImageOffset(mirror::Object* object, size_t offset) { + DCHECK(object != nullptr); + DCHECK_NE(offset, 0U); + DCHECK(!IsImageOffsetAssigned(object)); + mirror::Object* obj = reinterpret_cast(image_->Begin() + offset); + DCHECK_ALIGNED(obj, kObjectAlignment); + image_bitmap_->Set(obj); + // Before we stomp over the lock word, save the hash code for later. + Monitor::Deflate(Thread::Current(), object);; + LockWord lw(object->GetLockWord()); + switch (lw.GetState()) { + case LockWord::kFatLocked: { + LOG(FATAL) << "Fat locked object " << obj << " found during object copy"; + break; + } + case LockWord::kThinLocked: { + LOG(FATAL) << "Thin locked object " << obj << " found during object copy"; + break; + } + case LockWord::kUnlocked: + // No hash, don't need to save it. + break; + case LockWord::kHashCode: + saved_hashes_.push_back(std::make_pair(obj, lw.GetHashCode())); + break; + default: + LOG(FATAL) << "Unreachable."; + break; } - LOG(INFO) << "RecordImageAllocations took " << PrettyDuration(NanoTime() - start_time); + object->SetLockWord(LockWord::FromForwardingAddress(offset)); + DCHECK(IsImageOffsetAssigned(object)); } -bool ImageWriter::AllocMemory() { - size_t size = 0; - for (const auto& space : Runtime::Current()->GetHeap()->GetContinuousSpaces()) { - if (space->IsDlMallocSpace()) { - size += space->Size(); - } - } +void ImageWriter::AssignImageOffset(mirror::Object* object) { + DCHECK(object != nullptr); + SetImageOffset(object, image_end_); + image_end_ += RoundUp(object->SizeOf(), 8); // 64-bit alignment + DCHECK_LT(image_end_, image_->Size()); +} - int prot = PROT_READ | PROT_WRITE; - size_t length = RoundUp(size, kPageSize); +bool ImageWriter::IsImageOffsetAssigned(const mirror::Object* object) const { + DCHECK(object != nullptr); + return object->GetLockWord().GetState() == LockWord::kForwardingAddress; +} + +size_t ImageWriter::GetImageOffset(const mirror::Object* object) const { + DCHECK(object != nullptr); + DCHECK(IsImageOffsetAssigned(object)); + LockWord lock_word = object->GetLockWord(); + size_t offset = lock_word.ForwardingAddress(); + DCHECK_LT(offset, image_end_); + return offset; +} + +bool ImageWriter::AllocMemory() { + size_t length = RoundUp(Runtime::Current()->GetHeap()->GetTotalMemory(), kPageSize); std::string error_msg; - image_.reset(MemMap::MapAnonymous("image writer image", NULL, length, prot, &error_msg)); + image_.reset(MemMap::MapAnonymous("image writer image", NULL, length, PROT_READ | PROT_WRITE, + &error_msg)); if (UNLIKELY(image_.get() == nullptr)) { LOG(ERROR) << "Failed to allocate memory for image file generation: " << error_msg; return false; } + + // Create the image bitmap. + image_bitmap_.reset(gc::accounting::SpaceBitmap::Create("image bitmap", image_->Begin(), + length)); + if (image_bitmap_.get() == nullptr) { + LOG(ERROR) << "Failed to allocate memory for image bitmap"; + return false; + } return true; } void ImageWriter::ComputeLazyFieldsForImageClasses() { - Runtime* runtime = Runtime::Current(); - ClassLinker* class_linker = runtime->GetClassLinker(); + ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); class_linker->VisitClassesWithoutClassesLock(ComputeLazyFieldsForClassesVisitor, NULL); } @@ -223,13 +256,12 @@ void ImageWriter::ComputeEagerResolvedStringsCallback(Object* obj, void* arg) { if (!obj->GetClass()->IsStringClass()) { return; } - String* string = obj->AsString(); + mirror::String* string = obj->AsString(); const uint16_t* utf16_string = string->GetCharArray()->GetData() + string->GetOffset(); - ImageWriter* writer = reinterpret_cast(arg); - for (DexCache* dex_cache : writer->dex_caches_) { + for (DexCache* dex_cache : Runtime::Current()->GetClassLinker()->GetDexCaches()) { const DexFile& dex_file = *dex_cache->GetDexFile(); const DexFile::StringId* string_id = dex_file.FindStringId(utf16_string); - if (string_id != NULL) { + if (string_id != nullptr) { // This string occurs in this dex file, assign the dex cache entry. uint32_t string_idx = dex_file.GetIndexForStringId(*string_id); if (dex_cache->GetResolvedString(string_idx) == NULL) { @@ -239,13 +271,9 @@ void ImageWriter::ComputeEagerResolvedStringsCallback(Object* obj, void* arg) { } } -void ImageWriter::ComputeEagerResolvedStrings() - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - // TODO: Check image spaces only? - gc::Heap* heap = Runtime::Current()->GetHeap(); - WriterMutexLock mu(Thread::Current(), *Locks::heap_bitmap_lock_); - heap->FlushAllocStack(); - heap->GetLiveBitmap()->Walk(ComputeEagerResolvedStringsCallback, this); +void ImageWriter::ComputeEagerResolvedStrings() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + ReaderMutexLock mu(Thread::Current(), *Locks::heap_bitmap_lock_); + Runtime::Current()->GetHeap()->VisitObjects(ComputeEagerResolvedStringsCallback, this); } bool ImageWriter::IsImageClass(const Class* klass) { @@ -278,7 +306,7 @@ void ImageWriter::PruneNonImageClasses() { // Clear references to removed classes from the DexCaches. ArtMethod* resolution_method = runtime->GetResolutionMethod(); - for (DexCache* dex_cache : dex_caches_) { + for (DexCache* dex_cache : class_linker->GetDexCaches()) { for (size_t i = 0; i < dex_cache->NumResolvedTypes(); i++) { Class* klass = dex_cache->GetResolvedType(i); if (klass != NULL && !IsImageClass(klass)) { @@ -311,31 +339,22 @@ bool ImageWriter::NonImageClassesVisitor(Class* klass, void* arg) { void ImageWriter::CheckNonImageClassesRemoved() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - if (compiler_driver_.GetImageClasses() == NULL) { - return; - } - - gc::Heap* heap = Runtime::Current()->GetHeap(); - Thread* self = Thread::Current(); - { - WriterMutexLock mu(self, *Locks::heap_bitmap_lock_); - heap->FlushAllocStack(); + if (compiler_driver_.GetImageClasses() != nullptr) { + gc::Heap* heap = Runtime::Current()->GetHeap(); + ReaderMutexLock mu(Thread::Current(), *Locks::heap_bitmap_lock_); + heap->VisitObjects(CheckNonImageClassesRemovedCallback, this); } - - ReaderMutexLock mu(self, *Locks::heap_bitmap_lock_); - heap->GetLiveBitmap()->Walk(CheckNonImageClassesRemovedCallback, this); } void ImageWriter::CheckNonImageClassesRemovedCallback(Object* obj, void* arg) { ImageWriter* image_writer = reinterpret_cast(arg); - if (!obj->IsClass()) { - return; - } - Class* klass = obj->AsClass(); - if (!image_writer->IsImageClass(klass)) { - image_writer->DumpImageClasses(); - CHECK(image_writer->IsImageClass(klass)) << ClassHelper(klass).GetDescriptor() - << " " << PrettyDescriptor(klass); + if (obj->IsClass()) { + Class* klass = obj->AsClass(); + if (!image_writer->IsImageClass(klass)) { + image_writer->DumpImageClasses(); + CHECK(image_writer->IsImageClass(klass)) << ClassHelper(klass).GetDescriptor() + << " " << PrettyDescriptor(klass); + } } } @@ -347,53 +366,50 @@ void ImageWriter::DumpImageClasses() { } } -void ImageWriter::CalculateNewObjectOffsetsCallback(Object* obj, void* arg) { +void ImageWriter::CalculateObjectOffsets(Object* obj) { DCHECK(obj != NULL); - DCHECK(arg != NULL); - ImageWriter* image_writer = reinterpret_cast(arg); - // if it is a string, we want to intern it if its not interned. if (obj->GetClass()->IsStringClass()) { // we must be an interned string that was forward referenced and already assigned - if (image_writer->IsImageOffsetAssigned(obj)) { + if (IsImageOffsetAssigned(obj)) { DCHECK_EQ(obj, obj->AsString()->Intern()); return; } - SirtRef interned(Thread::Current(), obj->AsString()->Intern()); - if (obj != interned.get()) { - if (!image_writer->IsImageOffsetAssigned(interned.get())) { + Thread* self = Thread::Current(); + SirtRef sirt_obj(self, obj); + mirror::String* interned = obj->AsString()->Intern(); + if (sirt_obj.get() != interned) { + if (!IsImageOffsetAssigned(interned)) { // interned obj is after us, allocate its location early - image_writer->AssignImageOffset(interned.get()); + AssignImageOffset(interned); } // point those looking for this object to the interned version. - image_writer->SetImageOffset(obj, image_writer->GetImageOffset(interned.get())); + SetImageOffset(sirt_obj.get(), GetImageOffset(interned)); return; } // else (obj == interned), nothing to do but fall through to the normal case } - image_writer->AssignImageOffset(obj); + AssignImageOffset(obj); } ObjectArray* ImageWriter::CreateImageRoots() const { Runtime* runtime = Runtime::Current(); ClassLinker* class_linker = runtime->GetClassLinker(); - Class* object_array_class = class_linker->FindSystemClass("[Ljava/lang/Object;"); Thread* self = Thread::Current(); + SirtRef object_array_class(self, class_linker->FindSystemClass("[Ljava/lang/Object;")); // build an Object[] of all the DexCaches used in the source_space_ - ObjectArray* dex_caches = ObjectArray::Alloc(self, object_array_class, - dex_caches_.size()); + ObjectArray* dex_caches = ObjectArray::Alloc(self, object_array_class.get(), + class_linker->GetDexCaches().size()); int i = 0; - for (DexCache* dex_cache : dex_caches_) { + for (DexCache* dex_cache : class_linker->GetDexCaches()) { dex_caches->Set(i++, dex_cache); } // build an Object[] of the roots needed to restore the runtime - SirtRef > - image_roots(self, - ObjectArray::Alloc(self, object_array_class, - ImageHeader::kImageRootsMax)); + SirtRef > image_roots( + self, ObjectArray::Alloc(self, object_array_class.get(), ImageHeader::kImageRootsMax)); image_roots->Set(ImageHeader::kResolutionMethod, runtime->GetResolutionMethod()); image_roots->Set(ImageHeader::kImtConflictMethod, runtime->GetImtConflictMethod()); image_roots->Set(ImageHeader::kDefaultImt, runtime->GetDefaultImt()); @@ -405,24 +421,82 @@ ObjectArray* ImageWriter::CreateImageRoots() const { runtime->GetCalleeSaveMethod(Runtime::kRefsAndArgs)); image_roots->Set(ImageHeader::kOatLocation, String::AllocFromModifiedUtf8(self, oat_file_->GetLocation().c_str())); - image_roots->Set(ImageHeader::kDexCaches, - dex_caches); - image_roots->Set(ImageHeader::kClassRoots, - class_linker->GetClassRoots()); + image_roots->Set(ImageHeader::kDexCaches, dex_caches); + image_roots->Set(ImageHeader::kClassRoots, class_linker->GetClassRoots()); for (int i = 0; i < ImageHeader::kImageRootsMax; i++) { CHECK(image_roots->Get(i) != NULL); } return image_roots.get(); } +// Walk instance fields of the given Class. Separate function to allow recursion on the super +// class. +void ImageWriter::WalkInstanceFields(mirror::Object* obj, mirror::Class* klass) { + // Visit fields of parent classes first. + SirtRef sirt_class(Thread::Current(), klass); + mirror::Class* super = sirt_class->GetSuperClass(); + if (super != nullptr) { + WalkInstanceFields(obj, super); + } + // + size_t num_reference_fields = sirt_class->NumReferenceInstanceFields(); + for (size_t i = 0; i < num_reference_fields; ++i) { + mirror::ArtField* field = sirt_class->GetInstanceField(i); + MemberOffset field_offset = field->GetOffset(); + mirror::Object* value = obj->GetFieldObject(field_offset, false); + if (value != nullptr) { + WalkFieldsInOrder(value); + } + } +} + +// For an unvisited object, visit it then all its children found via fields. +void ImageWriter::WalkFieldsInOrder(mirror::Object* obj) { + if (!IsImageOffsetAssigned(obj)) { + // Walk instance fields of all objects + Thread* self = Thread::Current(); + SirtRef sirt_obj(self, obj); + SirtRef klass(self, obj->GetClass()); + // visit the object itself. + CalculateObjectOffsets(sirt_obj.get()); + WalkInstanceFields(sirt_obj.get(), klass.get()); + // Walk static fields of a Class. + if (sirt_obj->IsClass()) { + size_t num_static_fields = klass->NumReferenceStaticFields(); + for (size_t i = 0; i < num_static_fields; ++i) { + mirror::ArtField* field = klass->GetStaticField(i); + MemberOffset field_offset = field->GetOffset(); + mirror::Object* value = sirt_obj->GetFieldObject(field_offset, false); + if (value != nullptr) { + WalkFieldsInOrder(value); + } + } + } else if (sirt_obj->IsObjectArray()) { + // Walk elements of an object array. + int32_t length = sirt_obj->AsObjectArray()->GetLength(); + for (int32_t i = 0; i < length; i++) { + mirror::ObjectArray* obj_array = sirt_obj->AsObjectArray(); + mirror::Object* value = obj_array->Get(i); + if (value != nullptr) { + WalkFieldsInOrder(value); + } + } + } + } +} + +void ImageWriter::WalkFieldsCallback(mirror::Object* obj, void* arg) { + ImageWriter* writer = reinterpret_cast(arg); + DCHECK(writer != nullptr); + writer->WalkFieldsInOrder(obj); +} + void ImageWriter::CalculateNewObjectOffsets(size_t oat_loaded_size, size_t oat_data_offset) { CHECK_NE(0U, oat_loaded_size); Thread* self = Thread::Current(); SirtRef > image_roots(self, CreateImageRoots()); gc::Heap* heap = Runtime::Current()->GetHeap(); - const auto& spaces = heap->GetContinuousSpaces(); - DCHECK(!spaces.empty()); DCHECK_EQ(0U, image_end_); // Leave space for the header, but do not write it yet, we need to @@ -431,21 +505,14 @@ void ImageWriter::CalculateNewObjectOffsets(size_t oat_loaded_size, size_t oat_d { WriterMutexLock mu(self, *Locks::heap_bitmap_lock_); - heap->FlushAllocStack(); // TODO: Image spaces only? - // TODO: Add InOrderWalk to heap bitmap. const char* old = self->StartAssertNoThreadSuspension("ImageWriter"); - DCHECK(heap->GetLargeObjectsSpace()->GetLiveObjects()->IsEmpty()); - for (const auto& space : spaces) { - space->GetLiveBitmap()->InOrderWalk(CalculateNewObjectOffsetsCallback, this); - DCHECK_LT(image_end_, image_->Size()); - } + DCHECK_LT(image_end_, image_->Size()); + // Clear any pre-existing monitors which may have been in the monitor words. + heap->VisitObjects(WalkFieldsCallback, this); self->EndAssertNoThreadSuspension(old); } - // Create the image bitmap. - image_bitmap_.reset(gc::accounting::SpaceBitmap::Create("image bitmap", image_->Begin(), - image_end_)); const byte* oat_file_begin = image_begin_ + RoundUp(image_end_, kPageSize); const byte* oat_file_end = oat_file_begin + oat_loaded_size; oat_data_begin_ = oat_file_begin + oat_data_offset; @@ -456,7 +523,8 @@ void ImageWriter::CalculateNewObjectOffsets(size_t oat_loaded_size, size_t oat_d ImageHeader image_header(reinterpret_cast(image_begin_), static_cast(image_end_), RoundUp(image_end_, kPageSize), - image_bitmap_->Size(), + RoundUp(image_end_ / gc::accounting::SpaceBitmap::kAlignment, + sizeof(size_t)), reinterpret_cast(GetImageAddress(image_roots.get())), oat_file_->GetOatHeader().GetChecksum(), reinterpret_cast(oat_file_begin), @@ -477,17 +545,19 @@ void ImageWriter::CopyAndFixupObjects() heap->DisableObjectValidation(); // TODO: Image spaces only? WriterMutexLock mu(self, *Locks::heap_bitmap_lock_); - heap->FlushAllocStack(); - heap->GetLiveBitmap()->Walk(CopyAndFixupObjectsCallback, this); + heap->VisitObjects(CopyAndFixupObjectsCallback, this); + // Fix up the object previously had hash codes. + for (const std::pair& hash_pair : saved_hashes_) { + hash_pair.first->SetLockWord(LockWord::FromHashCode(hash_pair.second)); + } + saved_hashes_.clear(); self->EndAssertNoThreadSuspension(old_cause); } -void ImageWriter::CopyAndFixupObjectsCallback(Object* object, void* arg) { - DCHECK(object != NULL); +void ImageWriter::CopyAndFixupObjectsCallback(Object* obj, void* arg) { + DCHECK(obj != NULL); DCHECK(arg != NULL); - const Object* obj = object; ImageWriter* image_writer = reinterpret_cast(arg); - // see GetLocalAddress for similar computation size_t offset = image_writer->GetImageOffset(obj); byte* dst = image_writer->image_->Begin() + offset; @@ -498,33 +568,7 @@ void ImageWriter::CopyAndFixupObjectsCallback(Object* object, void* arg) { Object* copy = reinterpret_cast(dst); // Write in a hash code of objects which have inflated monitors or a hash code in their monitor // word. - LockWord lw(copy->GetLockWord()); - switch (lw.GetState()) { - case LockWord::kFatLocked: { - Monitor* monitor = lw.FatLockMonitor(); - CHECK(monitor != nullptr); - CHECK(!monitor->IsLocked()); - if (monitor->HasHashCode()) { - copy->SetLockWord(LockWord::FromHashCode(monitor->GetHashCode())); - } else { - copy->SetLockWord(LockWord()); - } - break; - } - case LockWord::kThinLocked: { - LOG(FATAL) << "Thin locked object " << obj << " found during object copy"; - break; - } - case LockWord::kUnlocked: - break; - case LockWord::kHashCode: - // Do nothing since we can just keep the same hash code. - CHECK_NE(lw.GetHashCode(), 0); - break; - default: - LOG(FATAL) << "Unreachable."; - break; - } + copy->SetLockWord(LockWord()); image_writer->FixupObject(obj, copy); } @@ -629,19 +673,13 @@ void ImageWriter::FixupInstanceFields(const Object* orig, Object* copy) { DCHECK(copy != NULL); Class* klass = orig->GetClass(); DCHECK(klass != NULL); - FixupFields(orig, - copy, - klass->GetReferenceInstanceOffsets(), - false); + FixupFields(orig, copy, klass->GetReferenceInstanceOffsets(), false); } void ImageWriter::FixupStaticFields(const Class* orig, Class* copy) { DCHECK(orig != NULL); DCHECK(copy != NULL); - FixupFields(orig, - copy, - orig->GetReferenceStaticOffsets(), - true); + FixupFields(orig, copy, orig->GetReferenceStaticOffsets(), true); } void ImageWriter::FixupFields(const Object* orig, @@ -693,11 +731,13 @@ void ImageWriter::FixupFields(const Object* orig, static ArtMethod* GetTargetMethod(const CompilerDriver::PatchInformation* patch) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); - DexCache* dex_cache = class_linker->FindDexCache(patch->GetDexFile()); + Thread* self = Thread::Current(); + SirtRef dex_cache(self, class_linker->FindDexCache(patch->GetDexFile())); + SirtRef class_loader(self, nullptr); ArtMethod* method = class_linker->ResolveMethod(patch->GetDexFile(), patch->GetTargetMethodIdx(), dex_cache, - NULL, + class_loader, NULL, patch->GetTargetInvokeType()); CHECK(method != NULL) @@ -749,15 +789,15 @@ void ImageWriter::SetPatchLocation(const CompilerDriver::PatchInformation* patch // TODO: make this Thumb2 specific uint8_t* base = reinterpret_cast(reinterpret_cast(oat_code) & ~0x1); uint32_t* patch_location = reinterpret_cast(base + patch->GetLiteralOffset()); -#ifndef NDEBUG - const DexFile::MethodId& id = patch->GetDexFile().GetMethodId(patch->GetTargetMethodIdx()); - uint32_t expected = reinterpret_cast(&id); - uint32_t actual = *patch_location; - CHECK(actual == expected || actual == value) << std::hex - << "actual=" << actual - << "expected=" << expected - << "value=" << value; -#endif + if (kIsDebugBuild) { + const DexFile::MethodId& id = patch->GetDexFile().GetMethodId(patch->GetTargetMethodIdx()); + uint32_t expected = reinterpret_cast(&id); + uint32_t actual = *patch_location; + CHECK(actual == expected || actual == value) << std::hex + << "actual=" << actual + << "expected=" << expected + << "value=" << value; + } *patch_location = value; oat_header.UpdateChecksum(patch_location, sizeof(value)); } diff --git a/compiler/image_writer.h b/compiler/image_writer.h index 0b408e85cc7..695f59b40e1 100644 --- a/compiler/image_writer.h +++ b/compiler/image_writer.h @@ -63,31 +63,11 @@ class ImageWriter { void RecordImageAllocations() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); // We use the lock word to store the offset of the object in the image. - void AssignImageOffset(mirror::Object* object) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - DCHECK(object != NULL); - SetImageOffset(object, image_end_); - image_end_ += RoundUp(object->SizeOf(), 8); // 64-bit alignment - DCHECK_LT(image_end_, image_->Size()); - } - - void SetImageOffset(mirror::Object* object, size_t offset) { - DCHECK(object != NULL); - DCHECK_NE(offset, 0U); - DCHECK(!IsImageOffsetAssigned(object)); - offsets_.Put(object, offset); - } - - size_t IsImageOffsetAssigned(const mirror::Object* object) const { - DCHECK(object != NULL); - return offsets_.find(object) != offsets_.end(); - } - - size_t GetImageOffset(const mirror::Object* object) const { - DCHECK(object != NULL); - DCHECK(IsImageOffsetAssigned(object)); - return offsets_.find(object)->second; - } + void AssignImageOffset(mirror::Object* object) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + void SetImageOffset(mirror::Object* object, size_t offset) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + bool IsImageOffsetAssigned(const mirror::Object* object) const; + size_t GetImageOffset(const mirror::Object* object) const; mirror::Object* GetImageAddress(const mirror::Object* object) const { if (object == NULL) { @@ -147,7 +127,14 @@ class ImageWriter { SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); mirror::ObjectArray* CreateImageRoots() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - static void CalculateNewObjectOffsetsCallback(mirror::Object* obj, void* arg) + void CalculateObjectOffsets(mirror::Object* obj) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + void WalkInstanceFields(mirror::Object* obj, mirror::Class* klass) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + void WalkFieldsInOrder(mirror::Object* obj) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + static void WalkFieldsCallback(mirror::Object* obj, void* arg) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); // Creates the contiguous image in memory and adjusts pointers. @@ -180,9 +167,6 @@ class ImageWriter { const CompilerDriver& compiler_driver_; - // Map of Object to where it will be at runtime. - SafeMap offsets_; - // oat file with code for this image OatFile* oat_file_; @@ -195,6 +179,9 @@ class ImageWriter { // Beginning target image address for the output image. byte* image_begin_; + // Saved hashes (objects are inside of the image so that they don't move). + std::vector > saved_hashes_; + // Beginning target oat address for the pointers from the output image to its oat file. const byte* oat_data_begin_; @@ -211,9 +198,6 @@ class ImageWriter { uint32_t quick_imt_conflict_trampoline_offset_; uint32_t quick_resolution_trampoline_offset_; uint32_t quick_to_interpreter_bridge_offset_; - - // DexCaches seen while scanning for fixing up CodeAndDirectMethods - std::set dex_caches_; }; } // namespace art diff --git a/compiler/jni/jni_compiler_test.cc b/compiler/jni/jni_compiler_test.cc index 667b9130394..21dd11ef201 100644 --- a/compiler/jni/jni_compiler_test.cc +++ b/compiler/jni/jni_compiler_test.cc @@ -48,9 +48,9 @@ class JniCompilerTest : public CommonTest { void CompileForTest(jobject class_loader, bool direct, const char* method_name, const char* method_sig) { ScopedObjectAccess soa(Thread::Current()); + SirtRef loader(soa.Self(), soa.Decode(class_loader)); // Compile the native method before starting the runtime - mirror::Class* c = class_linker_->FindClass("LMyClassNatives;", - soa.Decode(class_loader)); + mirror::Class* c = class_linker_->FindClass("LMyClassNatives;", loader); mirror::ArtMethod* method; if (direct) { method = c->FindDirectMethod(method_name, method_sig); diff --git a/compiler/oat_test.cc b/compiler/oat_test.cc index 6213b45c410..c423f34f7fb 100644 --- a/compiler/oat_test.cc +++ b/compiler/oat_test.cc @@ -130,7 +130,8 @@ TEST_F(OatTest, WriteRead) { num_virtual_methods = it.NumVirtualMethods(); } const char* descriptor = dex_file->GetClassDescriptor(class_def); - mirror::Class* klass = class_linker->FindClass(descriptor, NULL); + SirtRef loader(Thread::Current(), nullptr); + mirror::Class* klass = class_linker->FindClass(descriptor, loader); UniquePtr oat_class(oat_dex_file->GetOatClass(i)); CHECK_EQ(mirror::Class::Status::kStatusNotReady, oat_class->GetStatus()) << descriptor; diff --git a/compiler/oat_writer.cc b/compiler/oat_writer.cc index f3bb11272ec..28fb1479d71 100644 --- a/compiler/oat_writer.cc +++ b/compiler/oat_writer.cc @@ -405,23 +405,23 @@ size_t OatWriter::InitOatCodeMethod(size_t offset, size_t oat_class_index, size_t gc_map_size = gc_map.size() * sizeof(gc_map[0]); gc_map_offset = (gc_map_size == 0) ? 0 : offset; -#if !defined(NDEBUG) - // We expect GC maps except when the class hasn't been verified or the method is native - ClassReference class_ref(&dex_file, class_def_index); - CompiledClass* compiled_class = compiler_driver_->GetCompiledClass(class_ref); - mirror::Class::Status status; - if (compiled_class != NULL) { - status = compiled_class->GetStatus(); - } else if (verifier::MethodVerifier::IsClassRejected(class_ref)) { - status = mirror::Class::kStatusError; - } else { - status = mirror::Class::kStatusNotReady; + if (kIsDebugBuild) { + // We expect GC maps except when the class hasn't been verified or the method is native + ClassReference class_ref(&dex_file, class_def_index); + CompiledClass* compiled_class = compiler_driver_->GetCompiledClass(class_ref); + mirror::Class::Status status; + if (compiled_class != NULL) { + status = compiled_class->GetStatus(); + } else if (verifier::MethodVerifier::IsClassRejected(class_ref)) { + status = mirror::Class::kStatusError; + } else { + status = mirror::Class::kStatusNotReady; + } + CHECK(gc_map_size != 0 || is_native || status < mirror::Class::kStatusVerified) + << &gc_map << " " << gc_map_size << " " << (is_native ? "true" : "false") << " " + << (status < mirror::Class::kStatusVerified) << " " << status << " " + << PrettyMethod(method_idx, dex_file); } - CHECK(gc_map_size != 0 || is_native || status < mirror::Class::kStatusVerified) - << &gc_map << " " << gc_map_size << " " << (is_native ? "true" : "false") << " " - << (status < mirror::Class::kStatusVerified) << " " << status << " " - << PrettyMethod(method_idx, dex_file); -#endif // Deduplicate GC maps SafeMap*, uint32_t>::iterator gc_map_iter = @@ -448,11 +448,12 @@ size_t OatWriter::InitOatCodeMethod(size_t offset, size_t oat_class_index, if (compiler_driver_->IsImage()) { ClassLinker* linker = Runtime::Current()->GetClassLinker(); - mirror::DexCache* dex_cache = linker->FindDexCache(dex_file); // Unchecked as we hold mutator_lock_ on entry. ScopedObjectAccessUnchecked soa(Thread::Current()); + SirtRef dex_cache(soa.Self(), linker->FindDexCache(dex_file)); + SirtRef class_loader(soa.Self(), nullptr); mirror::ArtMethod* method = linker->ResolveMethod(dex_file, method_idx, dex_cache, - NULL, NULL, invoke_type); + class_loader, nullptr, invoke_type); CHECK(method != NULL); method->SetFrameSizeInBytes(frame_size_in_bytes); method->SetCoreSpillMask(core_spill_mask); diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc index 90276c26786..e219dd33a2a 100644 --- a/oatdump/oatdump.cc +++ b/oatdump/oatdump.cc @@ -257,6 +257,9 @@ class OatDumper { os << "OAT DEX FILE:\n"; os << StringPrintf("location: %s\n", oat_dex_file.GetDexFileLocation().c_str()); os << StringPrintf("checksum: 0x%08x\n", oat_dex_file.GetDexFileLocationChecksum()); + + // Create the verifier early. + std::string error_msg; UniquePtr dex_file(oat_dex_file.OpenDexFile(&error_msg)); if (dex_file.get() == NULL) { @@ -377,8 +380,20 @@ class OatDumper { oat_method.GetCode() != NULL ? "..." : ""); Indenter indent2_filter(indent1_os.rdbuf(), kIndentChar, kIndentBy1Count); std::ostream indent2_os(&indent2_filter); - DumpCode(indent2_os, oat_method, dex_method_idx, &dex_file, class_def, code_item, - method_access_flags); + + Runtime* runtime = Runtime::Current(); + if (runtime != nullptr) { + ScopedObjectAccess soa(Thread::Current()); + SirtRef dex_cache( + soa.Self(), runtime->GetClassLinker()->FindDexCache(dex_file)); + SirtRef class_loader(soa.Self(), nullptr); + verifier::MethodVerifier verifier(&dex_file, &dex_cache, &class_loader, &class_def, code_item, + dex_method_idx, nullptr, method_access_flags, true, true); + verifier.Verify(); + DumpCode(indent2_os, &verifier, oat_method, code_item); + } else { + DumpCode(indent2_os, nullptr, oat_method, code_item); + } } } @@ -566,24 +581,10 @@ class OatDumper { } } - void DumpVRegsAtDexPc(std::ostream& os, const OatFile::OatMethod& oat_method, - uint32_t dex_method_idx, const DexFile* dex_file, - const DexFile::ClassDef& class_def, const DexFile::CodeItem* code_item, - uint32_t method_access_flags, uint32_t dex_pc) { - static UniquePtr verifier; - static const DexFile* verified_dex_file = NULL; - static uint32_t verified_dex_method_idx = DexFile::kDexNoIndex; - if (dex_file != verified_dex_file || verified_dex_method_idx != dex_method_idx) { - ScopedObjectAccess soa(Thread::Current()); - mirror::DexCache* dex_cache = Runtime::Current()->GetClassLinker()->FindDexCache(*dex_file); - mirror::ClassLoader* class_loader = NULL; - verifier.reset(new verifier::MethodVerifier(dex_file, dex_cache, class_loader, &class_def, - code_item, dex_method_idx, NULL, - method_access_flags, true, true)); - verifier->Verify(); - verified_dex_file = dex_file; - verified_dex_method_idx = dex_method_idx; - } + void DumpVRegsAtDexPc(std::ostream& os, verifier::MethodVerifier* verifier, + const OatFile::OatMethod& oat_method, + const DexFile::CodeItem* code_item, uint32_t dex_pc) { + DCHECK(verifier != nullptr); std::vector kinds = verifier->DescribeVRegs(dex_pc); bool first = true; for (size_t reg = 0; reg < code_item->registers_size_; reg++) { @@ -633,18 +634,16 @@ class OatDumper { uint32_t method_access_flags) { if ((method_access_flags & kAccNative) == 0) { ScopedObjectAccess soa(Thread::Current()); - mirror::DexCache* dex_cache = Runtime::Current()->GetClassLinker()->FindDexCache(*dex_file); - mirror::ClassLoader* class_loader = NULL; + SirtRef dex_cache(soa.Self(), Runtime::Current()->GetClassLinker()->FindDexCache(*dex_file)); + SirtRef class_loader(soa.Self(), nullptr); verifier::MethodVerifier::VerifyMethodAndDump(os, dex_method_idx, dex_file, dex_cache, class_loader, &class_def, code_item, NULL, method_access_flags); } } - void DumpCode(std::ostream& os, const OatFile::OatMethod& oat_method, - uint32_t dex_method_idx, const DexFile* dex_file, - const DexFile::ClassDef& class_def, const DexFile::CodeItem* code_item, - uint32_t method_access_flags) { + void DumpCode(std::ostream& os, verifier::MethodVerifier* verifier, + const OatFile::OatMethod& oat_method, const DexFile::CodeItem* code_item) { const void* code = oat_method.GetCode(); size_t code_size = oat_method.GetCodeSize(); if (code == NULL || code_size == 0) { @@ -653,16 +652,14 @@ class OatDumper { } const uint8_t* native_pc = reinterpret_cast(code); size_t offset = 0; - const bool kDumpVRegs = (Runtime::Current() != NULL); while (offset < code_size) { DumpMappingAtOffset(os, oat_method, offset, false); offset += disassembler_->Dump(os, native_pc + offset); uint32_t dex_pc = DumpMappingAtOffset(os, oat_method, offset, true); if (dex_pc != DexFile::kDexNoIndex) { DumpGcMapAtNativePcOffset(os, oat_method, code_item, offset); - if (kDumpVRegs) { - DumpVRegsAtDexPc(os, oat_method, dex_method_idx, dex_file, class_def, code_item, - method_access_flags, dex_pc); + if (verifier != nullptr) { + DumpVRegsAtDexPc(os, verifier, oat_method, code_item, dex_pc); } } } diff --git a/runtime/Android.mk b/runtime/Android.mk index bef4381c2b1..97cbdd9ab5b 100644 --- a/runtime/Android.mk +++ b/runtime/Android.mk @@ -50,8 +50,10 @@ LIBART_COMMON_SRC_FILES := \ gc/collector/garbage_collector.cc \ gc/collector/mark_sweep.cc \ gc/collector/partial_mark_sweep.cc \ + gc/collector/semi_space.cc \ gc/collector/sticky_mark_sweep.cc \ gc/heap.cc \ + gc/space/bump_pointer_space.cc \ gc/space/dlmalloc_space.cc \ gc/space/image_space.cc \ gc/space/large_object_space.cc \ diff --git a/runtime/arch/alloc_entrypoints.S b/runtime/arch/alloc_entrypoints.S new file mode 100644 index 00000000000..840f3c6197b --- /dev/null +++ b/runtime/arch/alloc_entrypoints.S @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2013 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. + */ + +/* Called by managed code to allocate an object */ +TWO_ARG_DOWNCALL art_quick_alloc_object, artAllocObjectFromCode, RETURN_IF_RESULT_IS_NON_ZERO +TWO_ARG_DOWNCALL art_quick_alloc_object_instrumented, artAllocObjectFromCodeInstrumented, RETURN_IF_RESULT_IS_NON_ZERO +/* Called by managed code to allocate an object when the caller doesn't know whether it has access + * to the created type. */ +TWO_ARG_DOWNCALL art_quick_alloc_object_with_access_check, artAllocObjectFromCodeWithAccessCheck, RETURN_IF_RESULT_IS_NON_ZERO +TWO_ARG_DOWNCALL art_quick_alloc_object_with_access_check_instrumented, artAllocObjectFromCodeWithAccessCheckInstrumented, RETURN_IF_RESULT_IS_NON_ZERO +/* Called by managed code to allocate an array. */ +THREE_ARG_DOWNCALL art_quick_alloc_array, artAllocArrayFromCode, RETURN_IF_RESULT_IS_NON_ZERO +THREE_ARG_DOWNCALL art_quick_alloc_array_instrumented, artAllocArrayFromCodeInstrumented, RETURN_IF_RESULT_IS_NON_ZERO +/* Called by managed code to allocate an array when the caller doesn't know whether it has access + * to the created type. */ +THREE_ARG_DOWNCALL art_quick_alloc_array_with_access_check, artAllocArrayFromCodeWithAccessCheck, RETURN_IF_RESULT_IS_NON_ZERO +THREE_ARG_DOWNCALL art_quick_alloc_array_with_access_check_instrumented, artAllocArrayFromCodeWithAccessCheckInstrumented, RETURN_IF_RESULT_IS_NON_ZERO +/* Called by managed code to allocate an array in a special case for FILLED_NEW_ARRAY. */ +THREE_ARG_DOWNCALL art_quick_check_and_alloc_array, artCheckAndAllocArrayFromCode, RETURN_IF_RESULT_IS_NON_ZERO +THREE_ARG_DOWNCALL art_quick_check_and_alloc_array_instrumented, artCheckAndAllocArrayFromCodeInstrumented, RETURN_IF_RESULT_IS_NON_ZERO +/* Called by managed code to allocate an array in a special case for FILLED_NEW_ARRAY. */ +THREE_ARG_DOWNCALL art_quick_check_and_alloc_array_with_access_check, artCheckAndAllocArrayFromCodeWithAccessCheck +THREE_ARG_DOWNCALL art_quick_check_and_alloc_array_with_access_check_instrumented, artCheckAndAllocArrayFromCodeWithAccessCheckInstrumented diff --git a/runtime/arch/arm/quick_entrypoints_arm.S b/runtime/arch/arm/quick_entrypoints_arm.S index 1a058ea61e6..dbfb93a8463 100644 --- a/runtime/arch/arm/quick_entrypoints_arm.S +++ b/runtime/arch/arm/quick_entrypoints_arm.S @@ -830,205 +830,41 @@ ENTRY art_quick_resolve_string DELIVER_PENDING_EXCEPTION END art_quick_resolve_string - /* - * Called by managed code to allocate an object - */ - .extern artAllocObjectFromCode -ENTRY art_quick_alloc_object - SETUP_REF_ONLY_CALLEE_SAVE_FRAME @ save callee saves in case of GC - mov r2, r9 @ pass Thread::Current - mov r3, sp @ pass SP - bl artAllocObjectFromCode @ (uint32_t type_idx, Method* method, Thread*, SP) - RESTORE_REF_ONLY_CALLEE_SAVE_FRAME - RETURN_IF_RESULT_IS_NON_ZERO - DELIVER_PENDING_EXCEPTION -END art_quick_alloc_object - - .extern artAllocObjectFromCodeInstrumented -ENTRY art_quick_alloc_object_instrumented +// Macro to facilitate adding new allocation entrypoints. +.macro TWO_ARG_DOWNCALL name, entrypoint, return + .extern \entrypoint +ENTRY \name SETUP_REF_ONLY_CALLEE_SAVE_FRAME @ save callee saves in case of GC mov r2, r9 @ pass Thread::Current mov r3, sp @ pass SP - bl artAllocObjectFromCodeInstrumented @ (uint32_t type_idx, Method* method, Thread*, SP) - RESTORE_REF_ONLY_CALLEE_SAVE_FRAME - RETURN_IF_RESULT_IS_NON_ZERO - DELIVER_PENDING_EXCEPTION -END art_quick_alloc_object_instrumented - - /* - * Called by managed code to allocate an object when the caller doesn't know whether it has - * access to the created type. - */ - .extern artAllocObjectFromCodeWithAccessCheck -ENTRY art_quick_alloc_object_with_access_check - SETUP_REF_ONLY_CALLEE_SAVE_FRAME @ save callee saves in case of GC - mov r2, r9 @ pass Thread::Current - mov r3, sp @ pass SP - bl artAllocObjectFromCodeWithAccessCheck @ (uint32_t type_idx, Method* method, Thread*, SP) - RESTORE_REF_ONLY_CALLEE_SAVE_FRAME - RETURN_IF_RESULT_IS_NON_ZERO - DELIVER_PENDING_EXCEPTION -END art_quick_alloc_object_with_access_check - - .extern artAllocObjectFromCodeWithAccessCheckInstrumented -ENTRY art_quick_alloc_object_with_access_check_instrumented - SETUP_REF_ONLY_CALLEE_SAVE_FRAME @ save callee saves in case of GC - mov r2, r9 @ pass Thread::Current - mov r3, sp @ pass SP - bl artAllocObjectFromCodeWithAccessCheckInstrumented @ (uint32_t type_idx, Method* method, Thread*, SP) - RESTORE_REF_ONLY_CALLEE_SAVE_FRAME - RETURN_IF_RESULT_IS_NON_ZERO - DELIVER_PENDING_EXCEPTION -END art_quick_alloc_object_with_access_check_instrumented - - /* - * Called by managed code to allocate an array. - */ - .extern artAllocArrayFromCode -ENTRY art_quick_alloc_array - SETUP_REF_ONLY_CALLEE_SAVE_FRAME @ save callee saves in case of GC - mov r3, r9 @ pass Thread::Current - mov r12, sp - str r12, [sp, #-16]! @ expand the frame and pass SP - .pad #16 - .cfi_adjust_cfa_offset 16 - @ artAllocArrayFromCode(uint32_t type_idx, Method* method, int32_t component_count, Thread*, SP) - bl artAllocArrayFromCode - add sp, #16 @ strip the extra frame - .cfi_adjust_cfa_offset -16 + bl \entrypoint @ (uint32_t type_idx, Method* method, Thread*, SP) RESTORE_REF_ONLY_CALLEE_SAVE_FRAME - RETURN_IF_RESULT_IS_NON_ZERO + \return DELIVER_PENDING_EXCEPTION -END art_quick_alloc_array - - .extern artAllocArrayFromCodeInstrumented -ENTRY art_quick_alloc_array_instrumented - SETUP_REF_ONLY_CALLEE_SAVE_FRAME @ save callee saves in case of GC - mov r3, r9 @ pass Thread::Current - mov r12, sp - str r12, [sp, #-16]! @ expand the frame and pass SP - .pad #16 - .cfi_adjust_cfa_offset 16 - @ artAllocArrayFromCodeInstrumented(uint32_t type_idx, Method* method, int32_t component_count, Thread*, SP) - bl artAllocArrayFromCodeInstrumented - add sp, #16 @ strip the extra frame - .cfi_adjust_cfa_offset -16 - RESTORE_REF_ONLY_CALLEE_SAVE_FRAME - RETURN_IF_RESULT_IS_NON_ZERO - DELIVER_PENDING_EXCEPTION -END art_quick_alloc_array_instrumented - - /* - * Called by managed code to allocate an array when the caller doesn't know whether it has - * access to the created type. - */ - .extern artAllocArrayFromCodeWithAccessCheck -ENTRY art_quick_alloc_array_with_access_check - SETUP_REF_ONLY_CALLEE_SAVE_FRAME @ save callee saves in case of GC - mov r3, r9 @ pass Thread::Current - mov r12, sp - str r12, [sp, #-16]! @ expand the frame and pass SP - .pad #16 - .cfi_adjust_cfa_offset 16 - @ artAllocArrayFromCodeWithAccessCheck(type_idx, method, component_count, Thread*, SP) - bl artAllocArrayFromCodeWithAccessCheck - add sp, #16 @ strip the extra frame - .cfi_adjust_cfa_offset -16 - RESTORE_REF_ONLY_CALLEE_SAVE_FRAME - RETURN_IF_RESULT_IS_NON_ZERO - DELIVER_PENDING_EXCEPTION -END art_quick_alloc_array_with_access_check - - .extern artAllocArrayFromCodeWithAccessCheckInstrumented -ENTRY art_quick_alloc_array_with_access_check_instrumented - SETUP_REF_ONLY_CALLEE_SAVE_FRAME @ save callee saves in case of GC - mov r3, r9 @ pass Thread::Current - mov r12, sp - str r12, [sp, #-16]! @ expand the frame and pass SP - .pad #16 - .cfi_adjust_cfa_offset 16 - @ artAllocArrayFromCodeWithAccessCheckInstrumented(type_idx, method, component_count, Thread*, SP) - bl artAllocArrayFromCodeWithAccessCheckInstrumented - add sp, #16 @ strip the extra frame - .cfi_adjust_cfa_offset -16 - RESTORE_REF_ONLY_CALLEE_SAVE_FRAME - RETURN_IF_RESULT_IS_NON_ZERO - DELIVER_PENDING_EXCEPTION -END art_quick_alloc_array_with_access_check_instrumented - - /* - * Called by managed code to allocate an array in a special case for FILLED_NEW_ARRAY. - */ - .extern artCheckAndAllocArrayFromCode -ENTRY art_quick_check_and_alloc_array - SETUP_REF_ONLY_CALLEE_SAVE_FRAME @ save callee saves in case of GC - mov r3, r9 @ pass Thread::Current - mov r12, sp - str r12, [sp, #-16]! @ expand the frame and pass SP - .pad #16 - .cfi_adjust_cfa_offset 16 - @ artCheckAndAllocArrayFromCode(uint32_t type_idx, Method* method, int32_t count, Thread* , SP) - bl artCheckAndAllocArrayFromCode - add sp, #16 @ strip the extra frame - .cfi_adjust_cfa_offset -16 - RESTORE_REF_ONLY_CALLEE_SAVE_FRAME - RETURN_IF_RESULT_IS_NON_ZERO - DELIVER_PENDING_EXCEPTION -END art_quick_check_and_alloc_array - - .extern artCheckAndAllocArrayFromCodeInstrumented -ENTRY art_quick_check_and_alloc_array_instrumented - SETUP_REF_ONLY_CALLEE_SAVE_FRAME @ save callee saves in case of GC - mov r3, r9 @ pass Thread::Current - mov r12, sp - str r12, [sp, #-16]! @ expand the frame and pass SP - .pad #16 - .cfi_adjust_cfa_offset 16 - @ artCheckAndAllocArrayFromCodeInstrumented(uint32_t type_idx, Method* method, int32_t count, Thread* , SP) - bl artCheckAndAllocArrayFromCodeInstrumented - add sp, #16 @ strip the extra frame - .cfi_adjust_cfa_offset -16 - RESTORE_REF_ONLY_CALLEE_SAVE_FRAME - RETURN_IF_RESULT_IS_NON_ZERO - DELIVER_PENDING_EXCEPTION -END art_quick_check_and_alloc_array_instrumented +END \name +.endm - /* - * Called by managed code to allocate an array in a special case for FILLED_NEW_ARRAY. - */ - .extern artCheckAndAllocArrayFromCodeWithAccessCheck -ENTRY art_quick_check_and_alloc_array_with_access_check +// Macro to facilitate adding new array allocation entrypoints. +.macro THREE_ARG_DOWNCALL name, entrypoint, return + .extern \entrypoint +ENTRY \name SETUP_REF_ONLY_CALLEE_SAVE_FRAME @ save callee saves in case of GC mov r3, r9 @ pass Thread::Current mov r12, sp str r12, [sp, #-16]! @ expand the frame and pass SP .pad #16 .cfi_adjust_cfa_offset 16 - @ artCheckAndAllocArrayFromCodeWithAccessCheck(type_idx, method, count, Thread* , SP) - bl artCheckAndAllocArrayFromCodeWithAccessCheck + @ (uint32_t type_idx, Method* method, int32_t component_count, Thread*, SP) + bl \entrypoint add sp, #16 @ strip the extra frame .cfi_adjust_cfa_offset -16 RESTORE_REF_ONLY_CALLEE_SAVE_FRAME - RETURN_IF_RESULT_IS_NON_ZERO + \return DELIVER_PENDING_EXCEPTION -END art_quick_check_and_alloc_array_with_access_check +END \name +.endm - .extern artCheckAndAllocArrayFromCodeWithAccessCheckInstrumented -ENTRY art_quick_check_and_alloc_array_with_access_check_instrumented - SETUP_REF_ONLY_CALLEE_SAVE_FRAME @ save callee saves in case of GC - mov r3, r9 @ pass Thread::Current - mov r12, sp - str r12, [sp, #-16]! @ expand the frame and pass SP - .pad #16 - .cfi_adjust_cfa_offset 16 - @ artCheckAndAllocArrayFromCodeWithAccessCheckInstrumented(type_idx, method, count, Thread* , SP) - bl artCheckAndAllocArrayFromCodeWithAccessCheckInstrumented - add sp, #16 @ strip the extra frame - .cfi_adjust_cfa_offset -16 - RESTORE_REF_ONLY_CALLEE_SAVE_FRAME - RETURN_IF_RESULT_IS_NON_ZERO - DELIVER_PENDING_EXCEPTION -END art_quick_check_and_alloc_array_with_access_check_instrumented +#include "arch/alloc_entrypoints.S" /* * Called by managed code when the value in rSUSPEND has been decremented to 0. @@ -1107,11 +943,10 @@ ENTRY art_quick_to_interpreter_bridge ldr r2, [r9, #THREAD_EXCEPTION_OFFSET] @ load Thread::Current()->exception_ add sp, #16 @ skip r1-r3, 4 bytes padding. .cfi_adjust_cfa_offset -16 - cbnz r2, 1f @ success if no exception is pending RESTORE_REF_ONLY_CALLEE_SAVE_FRAME + cbnz r2, 1f @ success if no exception is pending bx lr @ return on success 1: - RESTORE_REF_ONLY_CALLEE_SAVE_FRAME DELIVER_PENDING_EXCEPTION END art_quick_to_interpreter_bridge diff --git a/runtime/arch/x86/quick_entrypoints_x86.S b/runtime/arch/x86/quick_entrypoints_x86.S index ee78d457935..decdb500b2f 100644 --- a/runtime/arch/x86/quick_entrypoints_x86.S +++ b/runtime/arch/x86/quick_entrypoints_x86.S @@ -401,7 +401,7 @@ MACRO3(THREE_ARG_DOWNCALL, c_name, cxx_name, return_macro) END_FUNCTION VAR(c_name, 0) END_MACRO -MACRO0(RETURN_IF_EAX_NOT_ZERO) +MACRO0(RETURN_IF_RESULT_IS_NON_ZERO) testl %eax, %eax // eax == 0 ? jz 1f // if eax == 0 goto 1 ret // return @@ -426,24 +426,12 @@ MACRO0(RETURN_OR_DELIVER_PENDING_EXCEPTION) DELIVER_PENDING_EXCEPTION END_MACRO -TWO_ARG_DOWNCALL art_quick_alloc_object, artAllocObjectFromCode, RETURN_IF_EAX_NOT_ZERO -TWO_ARG_DOWNCALL art_quick_alloc_object_with_access_check, artAllocObjectFromCodeWithAccessCheck, RETURN_IF_EAX_NOT_ZERO -THREE_ARG_DOWNCALL art_quick_alloc_array, artAllocArrayFromCode, RETURN_IF_EAX_NOT_ZERO -THREE_ARG_DOWNCALL art_quick_alloc_array_with_access_check, artAllocArrayFromCodeWithAccessCheck, RETURN_IF_EAX_NOT_ZERO -THREE_ARG_DOWNCALL art_quick_check_and_alloc_array, artCheckAndAllocArrayFromCode, RETURN_IF_EAX_NOT_ZERO -THREE_ARG_DOWNCALL art_quick_check_and_alloc_array_with_access_check, artCheckAndAllocArrayFromCodeWithAccessCheck, RETURN_IF_EAX_NOT_ZERO - -TWO_ARG_DOWNCALL art_quick_alloc_object_instrumented, artAllocObjectFromCodeInstrumented, RETURN_IF_EAX_NOT_ZERO -TWO_ARG_DOWNCALL art_quick_alloc_object_with_access_check_instrumented, artAllocObjectFromCodeWithAccessCheckInstrumented, RETURN_IF_EAX_NOT_ZERO -THREE_ARG_DOWNCALL art_quick_alloc_array_instrumented, artAllocArrayFromCodeInstrumented, RETURN_IF_EAX_NOT_ZERO -THREE_ARG_DOWNCALL art_quick_alloc_array_with_access_check_instrumented, artAllocArrayFromCodeWithAccessCheckInstrumented, RETURN_IF_EAX_NOT_ZERO -THREE_ARG_DOWNCALL art_quick_check_and_alloc_array_instrumented, artCheckAndAllocArrayFromCodeInstrumented, RETURN_IF_EAX_NOT_ZERO -THREE_ARG_DOWNCALL art_quick_check_and_alloc_array_with_access_check_instrumented, artCheckAndAllocArrayFromCodeWithAccessCheckInstrumented, RETURN_IF_EAX_NOT_ZERO - -TWO_ARG_DOWNCALL art_quick_resolve_string, artResolveStringFromCode, RETURN_IF_EAX_NOT_ZERO -TWO_ARG_DOWNCALL art_quick_initialize_static_storage, artInitializeStaticStorageFromCode, RETURN_IF_EAX_NOT_ZERO -TWO_ARG_DOWNCALL art_quick_initialize_type, artInitializeTypeFromCode, RETURN_IF_EAX_NOT_ZERO -TWO_ARG_DOWNCALL art_quick_initialize_type_and_verify_access, artInitializeTypeAndVerifyAccessFromCode, RETURN_IF_EAX_NOT_ZERO +#include "arch/alloc_entrypoints.S" + +TWO_ARG_DOWNCALL art_quick_resolve_string, artResolveStringFromCode, RETURN_IF_RESULT_IS_NON_ZERO +TWO_ARG_DOWNCALL art_quick_initialize_static_storage, artInitializeStaticStorageFromCode, RETURN_IF_RESULT_IS_NON_ZERO +TWO_ARG_DOWNCALL art_quick_initialize_type, artInitializeTypeFromCode, RETURN_IF_RESULT_IS_NON_ZERO +TWO_ARG_DOWNCALL art_quick_initialize_type_and_verify_access, artInitializeTypeAndVerifyAccessFromCode, RETURN_IF_RESULT_IS_NON_ZERO TWO_ARG_DOWNCALL art_quick_handle_fill_data, artHandleFillArrayDataFromCode, RETURN_IF_EAX_ZERO diff --git a/runtime/base/mutex-inl.h b/runtime/base/mutex-inl.h index c0cfee2463d..29b39817a86 100644 --- a/runtime/base/mutex-inl.h +++ b/runtime/base/mutex-inl.h @@ -130,7 +130,7 @@ static inline void CheckUnattachedThread(LockLevel level) NO_THREAD_SAFETY_ANALY // TODO: tighten this check. if (kDebugLocking) { Runtime* runtime = Runtime::Current(); - CHECK(runtime == NULL || !runtime->IsStarted() || runtime->IsShuttingDown() || + CHECK(runtime == NULL || !runtime->IsStarted() || runtime->IsShuttingDownLocked() || level == kDefaultMutexLevel || level == kRuntimeShutdownLock || level == kThreadListLock || level == kLoggingLock || level == kAbortLock); } diff --git a/runtime/base/mutex.cc b/runtime/base/mutex.cc index 249f031df0a..1c7d7449458 100644 --- a/runtime/base/mutex.cc +++ b/runtime/base/mutex.cc @@ -266,9 +266,8 @@ Mutex::Mutex(const char* name, LockLevel level, bool recursive) Mutex::~Mutex() { #if ART_USE_FUTEXES if (state_ != 0) { - MutexLock mu(Thread::Current(), *Locks::runtime_shutdown_lock_); Runtime* runtime = Runtime::Current(); - bool shutting_down = (runtime == NULL) || runtime->IsShuttingDown(); + bool shutting_down = runtime == nullptr || runtime->IsShuttingDown(Thread::Current()); LOG(shutting_down ? WARNING : FATAL) << "destroying mutex with owner: " << exclusive_owner_; } else { CHECK_EQ(exclusive_owner_, 0U) << "unexpectedly found an owner on unlocked mutex " << name_; @@ -641,9 +640,8 @@ ConditionVariable::ConditionVariable(const char* name, Mutex& guard) ConditionVariable::~ConditionVariable() { #if ART_USE_FUTEXES if (num_waiters_!= 0) { - MutexLock mu(Thread::Current(), *Locks::runtime_shutdown_lock_); Runtime* runtime = Runtime::Current(); - bool shutting_down = (runtime == NULL) || runtime->IsShuttingDown(); + bool shutting_down = runtime == nullptr || runtime->IsShuttingDown(Thread::Current()); LOG(shutting_down ? WARNING : FATAL) << "ConditionVariable::~ConditionVariable for " << name_ << " called with " << num_waiters_ << " waiters."; } diff --git a/runtime/base/mutex.h b/runtime/base/mutex.h index feb8a6c6c1b..a8750177c59 100644 --- a/runtime/base/mutex.h +++ b/runtime/base/mutex.h @@ -329,6 +329,11 @@ class ConditionVariable { // TODO: remove this. void WaitHoldingLocks(Thread* self) NO_THREAD_SAFETY_ANALYSIS; + // Return the number of people that are waiting on this condition. + int32_t GetNumWaiters() const NO_THREAD_SAFETY_ANALYSIS { + return num_waiters_; + } + private: const char* const name_; // The Mutex being used by waiters. It is an error to mix condition variables between different diff --git a/runtime/base/timing_logger.cc b/runtime/base/timing_logger.cc index 6df1126e0a7..45a546f37e1 100644 --- a/runtime/base/timing_logger.cc +++ b/runtime/base/timing_logger.cc @@ -86,6 +86,11 @@ void CumulativeLogger::AddLogger(const base::TimingLogger &logger) { } } +size_t CumulativeLogger::GetIterations() const { + MutexLock mu(Thread::Current(), lock_); + return iterations_; +} + void CumulativeLogger::Dump(std::ostream &os) { MutexLock mu(Thread::Current(), lock_); DumpHistogram(os); diff --git a/runtime/base/timing_logger.h b/runtime/base/timing_logger.h index 07d1ee00e0e..501d2d7fd20 100644 --- a/runtime/base/timing_logger.h +++ b/runtime/base/timing_logger.h @@ -45,6 +45,7 @@ class CumulativeLogger { // parent class that is unable to determine the "name" of a sub-class. void SetName(const std::string& name); void AddLogger(const base::TimingLogger& logger) LOCKS_EXCLUDED(lock_); + size_t GetIterations() const; private: typedef std::map *> Histograms; diff --git a/runtime/check_jni.cc b/runtime/check_jni.cc index 54cbfe6ea57..a84e18acc80 100644 --- a/runtime/check_jni.cc +++ b/runtime/check_jni.cc @@ -205,7 +205,7 @@ class ScopedCheck { // If java_object is a weak global ref whose referent has been cleared, // obj will be NULL. Otherwise, obj should always be non-NULL // and valid. - if (!Runtime::Current()->GetHeap()->IsHeapAddress(obj)) { + if (!Runtime::Current()->GetHeap()->IsValidObjectAddress(obj)) { Runtime::Current()->GetHeap()->DumpSpaces(); JniAbortF(function_name_, "field operation on invalid %s: %p", ToStr(GetIndirectRefKind(java_object)).c_str(), java_object); @@ -242,7 +242,7 @@ class ScopedCheck { void CheckInstanceFieldID(jobject java_object, jfieldID fid) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { mirror::Object* o = soa_.Decode(java_object); - if (o == NULL || !Runtime::Current()->GetHeap()->IsHeapAddress(o)) { + if (o == NULL || !Runtime::Current()->GetHeap()->IsValidObjectAddress(o)) { Runtime::Current()->GetHeap()->DumpSpaces(); JniAbortF(function_name_, "field operation on invalid %s: %p", ToStr(GetIndirectRefKind(java_object)).c_str(), java_object); @@ -455,7 +455,8 @@ class ScopedCheck { mirror::Class* c = reinterpret_cast(Thread::Current()->DecodeJObject(jc)); if (c == NULL) { msg += "NULL"; - } else if (c == kInvalidIndirectRefObject || !Runtime::Current()->GetHeap()->IsHeapAddress(c)) { + } else if (c == kInvalidIndirectRefObject || + !Runtime::Current()->GetHeap()->IsValidObjectAddress(c)) { StringAppendF(&msg, "INVALID POINTER:%p", jc); } else if (!c->IsClass()) { msg += "INVALID NON-CLASS OBJECT OF TYPE:" + PrettyTypeOf(c); @@ -621,7 +622,7 @@ class ScopedCheck { } mirror::Object* obj = soa_.Decode(java_object); - if (!Runtime::Current()->GetHeap()->IsHeapAddress(obj)) { + if (!Runtime::Current()->GetHeap()->IsValidObjectAddress(obj)) { Runtime::Current()->GetHeap()->DumpSpaces(); JniAbortF(function_name_, "%s is an invalid %s: %p (%p)", what, ToStr(GetIndirectRefKind(java_object)).c_str(), java_object, obj); @@ -675,7 +676,7 @@ class ScopedCheck { } mirror::Array* a = soa_.Decode(java_array); - if (!Runtime::Current()->GetHeap()->IsHeapAddress(a)) { + if (!Runtime::Current()->GetHeap()->IsValidObjectAddress(a)) { Runtime::Current()->GetHeap()->DumpSpaces(); JniAbortF(function_name_, "jarray is an invalid %s: %p (%p)", ToStr(GetIndirectRefKind(java_array)).c_str(), java_array, a); @@ -696,7 +697,7 @@ class ScopedCheck { return NULL; } mirror::ArtField* f = soa_.DecodeField(fid); - if (!Runtime::Current()->GetHeap()->IsHeapAddress(f) || !f->IsArtField()) { + if (!Runtime::Current()->GetHeap()->IsValidObjectAddress(f) || !f->IsArtField()) { Runtime::Current()->GetHeap()->DumpSpaces(); JniAbortF(function_name_, "invalid jfieldID: %p", fid); return NULL; @@ -710,7 +711,7 @@ class ScopedCheck { return NULL; } mirror::ArtMethod* m = soa_.DecodeMethod(mid); - if (!Runtime::Current()->GetHeap()->IsHeapAddress(m) || !m->IsArtMethod()) { + if (!Runtime::Current()->GetHeap()->IsValidObjectAddress(m) || !m->IsArtMethod()) { Runtime::Current()->GetHeap()->DumpSpaces(); JniAbortF(function_name_, "invalid jmethodID: %p", mid); return NULL; @@ -731,7 +732,7 @@ class ScopedCheck { } mirror::Object* o = soa_.Decode(java_object); - if (!Runtime::Current()->GetHeap()->IsHeapAddress(o)) { + if (!Runtime::Current()->GetHeap()->IsValidObjectAddress(o)) { Runtime::Current()->GetHeap()->DumpSpaces(); // TODO: when we remove work_around_app_jni_bugs, this should be impossible. JniAbortF(function_name_, "native code passing in reference to invalid %s: %p", diff --git a/runtime/class_linker-inl.h b/runtime/class_linker-inl.h index ad568b1cdb6..0436435e650 100644 --- a/runtime/class_linker-inl.h +++ b/runtime/class_linker-inl.h @@ -18,20 +18,21 @@ #define ART_RUNTIME_CLASS_LINKER_INL_H_ #include "class_linker.h" - #include "mirror/art_field.h" +#include "mirror/class_loader.h" #include "mirror/dex_cache.h" #include "mirror/iftable.h" #include "mirror/object_array.h" +#include "sirt_ref.h" namespace art { inline mirror::String* ClassLinker::ResolveString(uint32_t string_idx, - const mirror::ArtMethod* referrer) { + const mirror::ArtMethod* referrer) { mirror::String* resolved_string = referrer->GetDexCacheStrings()->Get(string_idx); if (UNLIKELY(resolved_string == NULL)) { mirror::Class* declaring_class = referrer->GetDeclaringClass(); - mirror::DexCache* dex_cache = declaring_class->GetDexCache(); + SirtRef dex_cache(Thread::Current(), declaring_class->GetDexCache()); const DexFile& dex_file = *dex_cache->GetDexFile(); resolved_string = ResolveString(dex_file, string_idx, dex_cache); } @@ -43,8 +44,9 @@ inline mirror::Class* ClassLinker::ResolveType(uint16_t type_idx, mirror::Class* resolved_type = referrer->GetDexCacheResolvedTypes()->Get(type_idx); if (UNLIKELY(resolved_type == NULL)) { mirror::Class* declaring_class = referrer->GetDeclaringClass(); - mirror::DexCache* dex_cache = declaring_class->GetDexCache(); - mirror::ClassLoader* class_loader = declaring_class->GetClassLoader(); + Thread* self = Thread::Current(); + SirtRef dex_cache(self, declaring_class->GetDexCache()); + SirtRef class_loader(self, declaring_class->GetClassLoader()); const DexFile& dex_file = *dex_cache->GetDexFile(); resolved_type = ResolveType(dex_file, type_idx, dex_cache, class_loader); } @@ -53,10 +55,12 @@ inline mirror::Class* ClassLinker::ResolveType(uint16_t type_idx, inline mirror::Class* ClassLinker::ResolveType(uint16_t type_idx, const mirror::ArtField* referrer) { mirror::Class* declaring_class = referrer->GetDeclaringClass(); - mirror::DexCache* dex_cache = declaring_class->GetDexCache(); - mirror::Class* resolved_type = dex_cache->GetResolvedType(type_idx); + mirror::DexCache* dex_cache_ptr = declaring_class->GetDexCache(); + mirror::Class* resolved_type = dex_cache_ptr->GetResolvedType(type_idx); if (UNLIKELY(resolved_type == NULL)) { - mirror::ClassLoader* class_loader = declaring_class->GetClassLoader(); + Thread* self = Thread::Current(); + SirtRef dex_cache(self, dex_cache_ptr); + SirtRef class_loader(self, declaring_class->GetClassLoader()); const DexFile& dex_file = *dex_cache->GetDexFile(); resolved_type = ResolveType(dex_file, type_idx, dex_cache, class_loader); } @@ -70,8 +74,9 @@ inline mirror::ArtMethod* ClassLinker::ResolveMethod(uint32_t method_idx, referrer->GetDexCacheResolvedMethods()->Get(method_idx); if (UNLIKELY(resolved_method == NULL || resolved_method->IsRuntimeMethod())) { mirror::Class* declaring_class = referrer->GetDeclaringClass(); - mirror::DexCache* dex_cache = declaring_class->GetDexCache(); - mirror::ClassLoader* class_loader = declaring_class->GetClassLoader(); + Thread* self = Thread::Current(); + SirtRef dex_cache(self, declaring_class->GetDexCache()); + SirtRef class_loader(self, declaring_class->GetClassLoader()); const DexFile& dex_file = *dex_cache->GetDexFile(); resolved_method = ResolveMethod(dex_file, method_idx, dex_cache, class_loader, referrer, type); } @@ -81,12 +86,13 @@ inline mirror::ArtMethod* ClassLinker::ResolveMethod(uint32_t method_idx, inline mirror::ArtField* ClassLinker::ResolveField(uint32_t field_idx, const mirror::ArtMethod* referrer, bool is_static) { + mirror::Class* declaring_class = referrer->GetDeclaringClass(); mirror::ArtField* resolved_field = - referrer->GetDeclaringClass()->GetDexCache()->GetResolvedField(field_idx); + declaring_class->GetDexCache()->GetResolvedField(field_idx); if (UNLIKELY(resolved_field == NULL)) { - mirror::Class* declaring_class = referrer->GetDeclaringClass(); - mirror::DexCache* dex_cache = declaring_class->GetDexCache(); - mirror::ClassLoader* class_loader = declaring_class->GetClassLoader(); + Thread* self = Thread::Current(); + SirtRef dex_cache(self, declaring_class->GetDexCache()); + SirtRef class_loader(self, declaring_class->GetClassLoader()); const DexFile& dex_file = *dex_cache->GetDexFile(); resolved_field = ResolveField(dex_file, field_idx, dex_cache, class_loader, is_static); } diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index 184e5d4be9d..cfe3bf4c0fd 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -170,20 +170,6 @@ const char* ClassLinker::class_roots_descriptors_[] = { "[Ljava/lang/StackTraceElement;", }; -ClassLinker* ClassLinker::CreateFromCompiler(const std::vector& boot_class_path, - InternTable* intern_table) { - CHECK_NE(boot_class_path.size(), 0U); - UniquePtr class_linker(new ClassLinker(intern_table)); - class_linker->InitFromCompiler(boot_class_path); - return class_linker.release(); -} - -ClassLinker* ClassLinker::CreateFromImage(InternTable* intern_table) { - UniquePtr class_linker(new ClassLinker(intern_table)); - class_linker->InitFromImage(); - return class_linker.release(); -} - ClassLinker::ClassLinker(InternTable* intern_table) // dex_lock_ is recursive as it may be used in stack dumping. : dex_lock_("ClassLinker dex lock", kDefaultMutexLevel), @@ -211,14 +197,16 @@ void ClassLinker::InitFromCompiler(const std::vector& boot_class // java_lang_Class comes first, it's needed for AllocClass Thread* self = Thread::Current(); gc::Heap* heap = Runtime::Current()->GetHeap(); - SirtRef - java_lang_Class(self, - down_cast(heap->AllocObject(self, NULL, - sizeof(mirror::ClassClass)))); + // The GC can't handle an object with a null class since we can't get the size of this object. + heap->IncrementDisableGC(self); + SirtRef java_lang_Class( + self, down_cast( + heap->AllocNonMovableObject(self, NULL, sizeof(mirror::ClassClass)))); CHECK(java_lang_Class.get() != NULL); mirror::Class::SetClassClass(java_lang_Class.get()); java_lang_Class->SetClass(java_lang_Class.get()); java_lang_Class->SetClassSize(sizeof(mirror::ClassClass)); + heap->DecrementDisableGC(self); // AllocClass(mirror::Class*) can now be used // Class[] is used for reflection support. @@ -401,7 +389,7 @@ void ClassLinker::InitFromCompiler(const std::vector& boot_class array_iftable_->SetInterface(1, java_io_Serializable); // Sanity check Class[] and Object[]'s interfaces. - ClassHelper kh(class_array_class.get(), this); + ClassHelper kh(class_array_class.get()); CHECK_EQ(java_lang_Cloneable, kh.GetDirectInterface(0)); CHECK_EQ(java_io_Serializable, kh.GetDirectInterface(1)); kh.ChangeClass(object_array_class.get()); @@ -487,7 +475,7 @@ void ClassLinker::FinishInit() { FindSystemClass("Ljava/lang/ref/FinalizerReference;"); mirror::ArtField* pendingNext = java_lang_ref_Reference->GetInstanceField(0); - FieldHelper fh(pendingNext, this); + FieldHelper fh(pendingNext); CHECK_STREQ(fh.GetName(), "pendingNext"); CHECK_STREQ(fh.GetTypeDescriptor(), "Ljava/lang/ref/Reference;"); @@ -1043,6 +1031,7 @@ void ClassLinker::InitFromImage() { VLOG(startup) << "ClassLinker::InitFromImage entering"; CHECK(!init_done_); + Thread* self = Thread::Current(); gc::Heap* heap = Runtime::Current()->GetHeap(); gc::space::ImageSpace* space = heap->GetImageSpace(); dex_cache_image_class_lookup_required_ = true; @@ -1059,9 +1048,10 @@ void ClassLinker::InitFromImage() { mirror::ObjectArray* dex_caches = dex_caches_object->AsObjectArray(); - mirror::ObjectArray* class_roots = - space->GetImageHeader().GetImageRoot(ImageHeader::kClassRoots)->AsObjectArray(); - class_roots_ = class_roots; + SirtRef > class_roots( + self, + space->GetImageHeader().GetImageRoot(ImageHeader::kClassRoots)->AsObjectArray()); + class_roots_ = class_roots.get(); // Special case of setting up the String class early so that we can test arbitrary objects // as being Strings or not @@ -1069,7 +1059,6 @@ void ClassLinker::InitFromImage() { CHECK_EQ(oat_file.GetOatHeader().GetDexFileCount(), static_cast(dex_caches->GetLength())); - Thread* self = Thread::Current(); for (int32_t i = 0; i < dex_caches->GetLength(); i++) { SirtRef dex_cache(self, dex_caches->Get(i)); const std::string& dex_file_location(dex_cache->GetLocation()->ToModifiedUtf8()); @@ -1096,13 +1085,12 @@ void ClassLinker::InitFromImage() { // Set entry point to interpreter if in InterpretOnly mode. if (Runtime::Current()->GetInstrumentation()->InterpretOnly()) { ReaderMutexLock mu(self, *Locks::heap_bitmap_lock_); - heap->FlushAllocStack(); - heap->GetLiveBitmap()->Walk(InitFromImageInterpretOnlyCallback, this); + heap->VisitObjects(InitFromImageInterpretOnlyCallback, this); } // reinit class_roots_ mirror::Class::SetClassClass(class_roots->Get(kJavaLangClass)); - class_roots_ = class_roots; + class_roots_ = class_roots.get(); // reinit array_iftable_ from any array class instance, they should be == array_iftable_ = GetClassRoot(kObjectArrayClass)->GetIfTable(); @@ -1192,7 +1180,6 @@ void ClassLinker::VisitClassesWithoutClassesLock(ClassVisitor* visitor, void* ar } } - ClassLinker::~ClassLinker() { mirror::Class::ResetClass(); mirror::String::ResetClass(); @@ -1214,10 +1201,10 @@ ClassLinker::~ClassLinker() { mirror::DexCache* ClassLinker::AllocDexCache(Thread* self, const DexFile& dex_file) { gc::Heap* heap = Runtime::Current()->GetHeap(); - mirror::Class* dex_cache_class = GetClassRoot(kJavaLangDexCache); - SirtRef dex_cache(self, - down_cast(heap->AllocObject(self, dex_cache_class, - dex_cache_class->GetObjectSize()))); + SirtRef dex_cache_class(self, GetClassRoot(kJavaLangDexCache)); + SirtRef dex_cache( + self, down_cast( + heap->AllocObject(self, dex_cache_class.get(), dex_cache_class->GetObjectSize()))); if (dex_cache.get() == NULL) { return NULL; } @@ -1253,13 +1240,8 @@ mirror::DexCache* ClassLinker::AllocDexCache(Thread* self, const DexFile& dex_fi return NULL; } - dex_cache->Init(&dex_file, - location.get(), - strings.get(), - types.get(), - methods.get(), - fields.get(), - initialized_static_storage.get()); + dex_cache->Init(&dex_file, location.get(), strings.get(), types.get(), methods.get(), + fields.get(), initialized_static_storage.get()); return dex_cache.get(); } @@ -1267,7 +1249,7 @@ mirror::Class* ClassLinker::AllocClass(Thread* self, mirror::Class* java_lang_Cl size_t class_size) { DCHECK_GE(class_size, sizeof(mirror::Class)); gc::Heap* heap = Runtime::Current()->GetHeap(); - mirror::Object* k = heap->AllocObject(self, java_lang_Class, class_size); + mirror::Object* k = heap->AllocNonMovableObject(self, java_lang_Class, class_size); if (UNLIKELY(k == NULL)) { CHECK(self->IsExceptionPending()); // OOME. return NULL; @@ -1285,18 +1267,19 @@ mirror::Class* ClassLinker::AllocClass(Thread* self, size_t class_size) { } mirror::ArtField* ClassLinker::AllocArtField(Thread* self) { - return down_cast(GetClassRoot(kJavaLangReflectArtField)->AllocObject(self)); + return down_cast( + GetClassRoot(kJavaLangReflectArtField)->Alloc(self)); } mirror::ArtMethod* ClassLinker::AllocArtMethod(Thread* self) { - return down_cast(GetClassRoot(kJavaLangReflectArtMethod)->AllocObject(self)); + return down_cast( + GetClassRoot(kJavaLangReflectArtMethod)->Alloc(self)); } -mirror::ObjectArray* ClassLinker::AllocStackTraceElementArray(Thread* self, - size_t length) { - return mirror::ObjectArray::Alloc(self, - GetClassRoot(kJavaLangStackTraceElementArrayClass), - length); +mirror::ObjectArray* ClassLinker::AllocStackTraceElementArray( + Thread* self, size_t length) { + return mirror::ObjectArray::Alloc( + self, GetClassRoot(kJavaLangStackTraceElementArrayClass), length); } static mirror::Class* EnsureResolved(Thread* self, mirror::Class* klass) @@ -1332,10 +1315,12 @@ bool ClassLinker::IsInBootClassPath(const char* descriptor) { } mirror::Class* ClassLinker::FindSystemClass(const char* descriptor) { - return FindClass(descriptor, NULL); + SirtRef class_loader(Thread::Current(), nullptr); + return FindClass(descriptor, class_loader); } -mirror::Class* ClassLinker::FindClass(const char* descriptor, mirror::ClassLoader* class_loader) { +mirror::Class* ClassLinker::FindClass(const char* descriptor, + SirtRef& class_loader) { DCHECK_NE(*descriptor, '\0') << "descriptor is empty string"; Thread* self = Thread::Current(); DCHECK(self != NULL); @@ -1346,20 +1331,19 @@ mirror::Class* ClassLinker::FindClass(const char* descriptor, mirror::ClassLoade return FindPrimitiveClass(descriptor[0]); } // Find the class in the loaded classes table. - mirror::Class* klass = LookupClass(descriptor, class_loader); + mirror::Class* klass = LookupClass(descriptor, class_loader.get()); if (klass != NULL) { return EnsureResolved(self, klass); } // Class is not yet loaded. if (descriptor[0] == '[') { return CreateArrayClass(descriptor, class_loader); - - } else if (class_loader == NULL) { + } else if (class_loader.get() == nullptr) { DexFile::ClassPathEntry pair = DexFile::FindInClassPath(descriptor, boot_class_path_); if (pair.second != NULL) { - return DefineClass(descriptor, NULL, *pair.first, *pair.second); + SirtRef class_loader(self, nullptr); + return DefineClass(descriptor, class_loader, *pair.first, *pair.second); } - } else if (Runtime::Current()->UseCompileTimeClassPath()) { // First try the boot class path, we check the descriptor first to avoid an unnecessary // throw of a NoClassDefFoundError. @@ -1372,7 +1356,8 @@ mirror::Class* ClassLinker::FindClass(const char* descriptor, mirror::ClassLoade const std::vector* class_path; { ScopedObjectAccessUnchecked soa(self); - ScopedLocalRef jclass_loader(soa.Env(), soa.AddLocalReference(class_loader)); + ScopedLocalRef jclass_loader(soa.Env(), + soa.AddLocalReference(class_loader.get())); class_path = &Runtime::Current()->GetCompileTimeClassPath(jclass_loader.get()); } @@ -1384,7 +1369,7 @@ mirror::Class* ClassLinker::FindClass(const char* descriptor, mirror::ClassLoade } else { ScopedObjectAccessUnchecked soa(self->GetJniEnv()); ScopedLocalRef class_loader_object(soa.Env(), - soa.AddLocalReference(class_loader)); + soa.AddLocalReference(class_loader.get())); std::string class_name_string(DescriptorToDot(descriptor)); ScopedLocalRef result(soa.Env(), NULL); { @@ -1418,7 +1403,7 @@ mirror::Class* ClassLinker::FindClass(const char* descriptor, mirror::ClassLoade } mirror::Class* ClassLinker::DefineClass(const char* descriptor, - mirror::ClassLoader* class_loader, + SirtRef& class_loader, const DexFile& dex_file, const DexFile::ClassDef& dex_class_def) { Thread* self = Thread::Current(); @@ -1449,7 +1434,7 @@ mirror::Class* ClassLinker::DefineClass(const char* descriptor, return NULL; } klass->SetDexCache(FindDexCache(dex_file)); - LoadClass(dex_file, dex_class_def, klass, class_loader); + LoadClass(dex_file, dex_class_def, klass, class_loader.get()); // Check for a pending exception during load if (self->IsExceptionPending()) { klass->SetStatus(mirror::Class::kStatusError, self); @@ -1457,14 +1442,12 @@ mirror::Class* ClassLinker::DefineClass(const char* descriptor, } ObjectLock lock(self, klass.get()); klass->SetClinitThreadId(self->GetTid()); - { - // Add the newly loaded class to the loaded classes table. - mirror::Class* existing = InsertClass(descriptor, klass.get(), Hash(descriptor)); - if (existing != NULL) { - // We failed to insert because we raced with another thread. Calling EnsureResolved may cause - // this thread to block. - return EnsureResolved(self, existing); - } + // Add the newly loaded class to the loaded classes table. + mirror::Class* existing = InsertClass(descriptor, klass.get(), Hash(descriptor)); + if (existing != NULL) { + // We failed to insert because we raced with another thread. Calling EnsureResolved may cause + // this thread to block. + return EnsureResolved(self, existing); } // Finish loading (if necessary) by finding parents CHECK(!klass->IsLoaded()); @@ -1476,7 +1459,9 @@ mirror::Class* ClassLinker::DefineClass(const char* descriptor, CHECK(klass->IsLoaded()); // Link the class (if necessary) CHECK(!klass->IsResolved()); - if (!LinkClass(klass, NULL, self)) { + // TODO: Use fast jobjects? + SirtRef > interfaces(self, nullptr); + if (!LinkClass(self, klass, interfaces)) { // Linking failed. klass->SetStatus(mirror::Class::kStatusError, self); return NULL; @@ -2083,7 +2068,7 @@ mirror::Class* ClassLinker::InitializePrimitiveClass(mirror::Class* primitive_cl // // Returns NULL with an exception raised on failure. mirror::Class* ClassLinker::CreateArrayClass(const char* descriptor, - mirror::ClassLoader* class_loader) { + SirtRef& class_loader) { // Identify the underlying component type CHECK_EQ('[', descriptor[0]); mirror::Class* component_type = FindClass(descriptor + 1, class_loader); @@ -2109,7 +2094,7 @@ mirror::Class* ClassLinker::CreateArrayClass(const char* descriptor, // because we effectively do this lookup again when we add the new // class to the hash table --- necessary because of possible races with // other threads.) - if (class_loader != component_type->GetClassLoader()) { + if (class_loader.get() != component_type->GetClassLoader()) { mirror::Class* new_class = LookupClass(descriptor, component_type->GetClassLoader()); if (new_class != NULL) { return new_class; @@ -2266,11 +2251,10 @@ mirror::Class* ClassLinker::InsertClass(const char* descriptor, mirror::Class* k bool ClassLinker::RemoveClass(const char* descriptor, const mirror::ClassLoader* class_loader) { size_t hash = Hash(descriptor); WriterMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_); - ClassHelper kh; for (auto it = class_table_.lower_bound(hash), end = class_table_.end(); it != end && it->first == hash; ++it) { mirror::Class* klass = it->second; - kh.ChangeClass(klass); + ClassHelper kh(klass); if ((klass->GetClassLoader() == class_loader) && (strcmp(descriptor, kh.GetDescriptor()) == 0)) { class_table_.erase(it); @@ -2313,18 +2297,17 @@ mirror::Class* ClassLinker::LookupClass(const char* descriptor, mirror::Class* ClassLinker::LookupClassFromTableLocked(const char* descriptor, const mirror::ClassLoader* class_loader, size_t hash) { - ClassHelper kh(NULL, this); auto end = class_table_.end(); for (auto it = class_table_.lower_bound(hash); it != end && it->first == hash; ++it) { mirror::Class* klass = it->second; - kh.ChangeClass(klass); + ClassHelper kh(klass); if ((klass->GetClassLoader() == class_loader) && (strcmp(descriptor, kh.GetDescriptor()) == 0)) { if (kIsDebugBuild) { // Check for duplicates in the table. for (++it; it != end && it->first == hash; ++it) { mirror::Class* klass2 = it->second; - kh.ChangeClass(klass2); + ClassHelper kh(klass2); CHECK(!((klass2->GetClassLoader() == class_loader) && (strcmp(descriptor, kh.GetDescriptor()) == 0))) << PrettyClass(klass) << " " << klass << " " << klass->GetClassLoader() << " " @@ -2354,14 +2337,13 @@ void ClassLinker::MoveImageClassesToClassTable() { const char* old_no_suspend_cause = self->StartAssertNoThreadSuspension("Moving image classes to class table"); mirror::ObjectArray* dex_caches = GetImageDexCaches(); - ClassHelper kh(NULL, this); for (int32_t i = 0; i < dex_caches->GetLength(); i++) { mirror::DexCache* dex_cache = dex_caches->Get(i); mirror::ObjectArray* types = dex_cache->GetResolvedTypes(); for (int32_t j = 0; j < types->GetLength(); j++) { mirror::Class* klass = types->Get(j); if (klass != NULL) { - kh.ChangeClass(klass); + ClassHelper kh(klass); DCHECK(klass->GetClassLoader() == NULL); const char* descriptor = kh.GetDescriptor(); size_t hash = Hash(descriptor); @@ -2429,11 +2411,10 @@ void ClassLinker::LookupClasses(const char* descriptor, std::vectorfirst == hash; ++it) { mirror::Class* klass = it->second; - kh.ChangeClass(klass); + ClassHelper kh(klass); if (strcmp(descriptor, kh.GetDescriptor()) == 0) { result.push_back(klass); } @@ -2687,12 +2668,10 @@ static void CheckProxyConstructor(mirror::ArtMethod* constructor); static void CheckProxyMethod(mirror::ArtMethod* method, SirtRef& prototype); -mirror::Class* ClassLinker::CreateProxyClass(mirror::String* name, - mirror::ObjectArray* interfaces, - mirror::ClassLoader* loader, - mirror::ObjectArray* methods, - mirror::ObjectArray >* throws) { - Thread* self = Thread::Current(); +mirror::Class* ClassLinker::CreateProxyClass(ScopedObjectAccess& soa, jstring name, + jobjectArray interfaces, jobject loader, + jobjectArray methods, jobjectArray throws) { + Thread* self = soa.Self(); SirtRef klass(self, AllocClass(self, GetClassRoot(kJavaLangClass), sizeof(mirror::SynthesizedProxyClass))); if (klass.get() == NULL) { @@ -2702,9 +2681,9 @@ mirror::Class* ClassLinker::CreateProxyClass(mirror::String* name, DCHECK(klass->GetClass() != NULL); klass->SetObjectSize(sizeof(mirror::Proxy)); klass->SetAccessFlags(kAccClassIsProxy | kAccPublic | kAccFinal); - klass->SetClassLoader(loader); + klass->SetClassLoader(soa.Decode(loader)); DCHECK_EQ(klass->GetPrimitiveType(), Primitive::kPrimNot); - klass->SetName(name); + klass->SetName(soa.Decode(name)); mirror::Class* proxy_class = GetClassRoot(kJavaLangReflectProxy); klass->SetDexCache(proxy_class->GetDexCache()); klass->SetStatus(mirror::Class::kStatusIdx, self); @@ -2742,8 +2721,7 @@ mirror::Class* ClassLinker::CreateProxyClass(mirror::String* name, // Proxies have 1 direct method, the constructor { - mirror::ObjectArray* directs = - AllocArtMethodArray(self, 1); + mirror::ObjectArray* directs = AllocArtMethodArray(self, 1); if (UNLIKELY(directs == NULL)) { CHECK(self->IsExceptionPending()); // OOME. return NULL; @@ -2757,11 +2735,11 @@ mirror::Class* ClassLinker::CreateProxyClass(mirror::String* name, klass->SetDirectMethod(0, constructor); } - // Create virtual method using specified prototypes - size_t num_virtual_methods = methods->GetLength(); + // Create virtual method using specified prototypes. + size_t num_virtual_methods = + soa.Decode*>(methods)->GetLength(); { - mirror::ObjectArray* virtuals = - AllocArtMethodArray(self, num_virtual_methods); + mirror::ObjectArray* virtuals = AllocArtMethodArray(self, num_virtual_methods); if (UNLIKELY(virtuals == NULL)) { CHECK(self->IsExceptionPending()); // OOME. return NULL; @@ -2769,7 +2747,9 @@ mirror::Class* ClassLinker::CreateProxyClass(mirror::String* name, klass->SetVirtualMethods(virtuals); } for (size_t i = 0; i < num_virtual_methods; ++i) { - SirtRef prototype(self, methods->Get(i)); + mirror::ObjectArray* decoded_methods = + soa.Decode*>(methods); + SirtRef prototype(self, decoded_methods->Get(i)); mirror::ArtMethod* clone = CreateProxyMethod(self, klass, prototype); if (UNLIKELY(clone == NULL)) { CHECK(self->IsExceptionPending()); // OOME. @@ -2785,13 +2765,15 @@ mirror::Class* ClassLinker::CreateProxyClass(mirror::String* name, { ObjectLock lock(self, klass.get()); // Must hold lock on object when resolved. // Link the fields and virtual methods, creating vtable and iftables - if (!LinkClass(klass, interfaces, self)) { + SirtRef > sirt_interfaces( + self, soa.Decode*>(interfaces)); + if (!LinkClass(self, klass, sirt_interfaces)) { klass->SetStatus(mirror::Class::kStatusError, self); return NULL; } - interfaces_sfield->SetObject(klass.get(), interfaces); - throws_sfield->SetObject(klass.get(), throws); + interfaces_sfield->SetObject(klass.get(), soa.Decode*>(interfaces)); + throws_sfield->SetObject(klass.get(), soa.Decode >*>(throws)); klass->SetStatus(mirror::Class::kStatusInitialized, self); } @@ -2800,22 +2782,25 @@ mirror::Class* ClassLinker::CreateProxyClass(mirror::String* name, CHECK(klass->GetIFields() == NULL); CheckProxyConstructor(klass->GetDirectMethod(0)); for (size_t i = 0; i < num_virtual_methods; ++i) { - SirtRef prototype(self, methods->Get(i)); + mirror::ObjectArray* decoded_methods = + soa.Decode*>(methods); + SirtRef prototype(self, decoded_methods->Get(i)); CheckProxyMethod(klass->GetVirtualMethod(i), prototype); } + mirror::String* decoded_name = soa.Decode(name); std::string interfaces_field_name(StringPrintf("java.lang.Class[] %s.interfaces", - name->ToModifiedUtf8().c_str())); + decoded_name->ToModifiedUtf8().c_str())); CHECK_EQ(PrettyField(klass->GetStaticField(0)), interfaces_field_name); std::string throws_field_name(StringPrintf("java.lang.Class[][] %s.throws", - name->ToModifiedUtf8().c_str())); + decoded_name->ToModifiedUtf8().c_str())); CHECK_EQ(PrettyField(klass->GetStaticField(1)), throws_field_name); mirror::SynthesizedProxyClass* synth_proxy_class = down_cast(klass.get()); - CHECK_EQ(synth_proxy_class->GetInterfaces(), interfaces); - CHECK_EQ(synth_proxy_class->GetThrows(), throws); + CHECK_EQ(synth_proxy_class->GetInterfaces(), soa.Decode*>(interfaces)); + CHECK_EQ(synth_proxy_class->GetThrows(), soa.Decode >*>(throws)); } std::string descriptor(GetDescriptorForProxy(klass.get())); mirror::Class* existing = InsertClass(descriptor.c_str(), klass.get(), Hash(descriptor.c_str())); @@ -2977,6 +2962,10 @@ static bool CanWeInitializeClass(mirror::Class* klass, bool can_init_statics, return true; } +bool ClassLinker::IsInitialized() const { + return init_done_; +} + bool ClassLinker::InitializeClass(mirror::Class* klass, bool can_init_statics, bool can_init_parents) { // see JLS 3rd edition, 12.4.2 "Detailed Initialization Procedure" for the locking protocol @@ -3084,7 +3073,9 @@ bool ClassLinker::InitializeClass(mirror::Class* klass, bool can_init_statics, const DexFile::ClassDef* dex_class_def = kh.GetClassDef(); CHECK(dex_class_def != NULL); const DexFile& dex_file = kh.GetDexFile(); - EncodedStaticFieldValueIterator it(dex_file, kh.GetDexCache(), klass->GetClassLoader(), + SirtRef class_loader(self, klass->GetClassLoader()); + SirtRef dex_cache(self, kh.GetDexCache()); + EncodedStaticFieldValueIterator it(dex_file, &dex_cache, &class_loader, this, *dex_class_def); if (it.HasNext()) { CHECK(can_init_statics); @@ -3196,12 +3187,11 @@ bool ClassLinker::ValidateSuperClassDescriptors(const mirror::Class* klass) { } } } - mirror::IfTable* iftable = klass->GetIfTable(); for (int32_t i = 0; i < klass->GetIfTableCount(); ++i) { - mirror::Class* interface = iftable->GetInterface(i); + mirror::Class* interface = klass->GetIfTable()->GetInterface(i); if (klass->GetClassLoader() != interface->GetClassLoader()) { for (size_t j = 0; j < interface->NumVirtualMethods(); ++j) { - const mirror::ArtMethod* method = iftable->GetMethodArray(i)->Get(j); + const mirror::ArtMethod* method = klass->GetIfTable()->GetMethodArray(i)->Get(j); if (!IsSameMethodSignatureInDifferentClassContexts(method, interface, method->GetDeclaringClass())) { ThrowLinkageError(klass, "Class %s method %s resolves differently in interface %s", @@ -3259,11 +3249,14 @@ bool ClassLinker::IsSameDescriptorInDifferentClassContexts(const char* descripto if (klass1 == klass2) { return true; } - mirror::Class* found1 = FindClass(descriptor, klass1->GetClassLoader()); + Thread* self = Thread::Current(); + SirtRef class_loader1(self, klass1->GetClassLoader()); + mirror::Class* found1 = FindClass(descriptor, class_loader1); if (found1 == NULL) { Thread::Current()->ClearException(); } - mirror::Class* found2 = FindClass(descriptor, klass2->GetClassLoader()); + SirtRef class_loader2(self, klass2->GetClassLoader()); + mirror::Class* found2 = FindClass(descriptor, class_loader2); if (found2 == NULL) { Thread::Current()->ClearException(); } @@ -3285,17 +3278,20 @@ bool ClassLinker::EnsureInitialized(mirror::Class* c, bool can_init_fields, bool } void ClassLinker::ConstructFieldMap(const DexFile& dex_file, const DexFile::ClassDef& dex_class_def, - mirror::Class* c, SafeMap& field_map) { - mirror::ClassLoader* cl = c->GetClassLoader(); + mirror::Class* c, + SafeMap& field_map) { const byte* class_data = dex_file.GetClassData(dex_class_def); ClassDataItemIterator it(dex_file, class_data); + Thread* self = Thread::Current(); + SirtRef dex_cache(self, c->GetDexCache()); + SirtRef class_loader(self, c->GetClassLoader()); for (size_t i = 0; it.HasNextStaticField(); i++, it.Next()) { - field_map.Put(i, ResolveField(dex_file, it.GetMemberIndex(), c->GetDexCache(), cl, true)); + field_map.Put(i, ResolveField(dex_file, it.GetMemberIndex(), dex_cache, class_loader, true)); } } -bool ClassLinker::LinkClass(SirtRef& klass, - mirror::ObjectArray* interfaces, Thread* self) { +bool ClassLinker::LinkClass(Thread* self, SirtRef& klass, + SirtRef >& interfaces) { CHECK_EQ(mirror::Class::kStatusLoaded, klass->GetStatus()); if (!LinkSuperClass(klass)) { return false; @@ -3419,7 +3415,7 @@ bool ClassLinker::LinkSuperClass(SirtRef& klass) { // Populate the class vtable and itable. Compute return type indices. bool ClassLinker::LinkMethods(SirtRef& klass, - mirror::ObjectArray* interfaces) { + SirtRef >& interfaces) { if (klass->IsInterface()) { // No vtable. size_t count = klass->NumVirtualMethods(); @@ -3453,15 +3449,13 @@ bool ClassLinker::LinkVirtualMethods(SirtRef& klass) { return false; } // See if any of our virtual methods override the superclass. - MethodHelper local_mh(NULL, this); - MethodHelper super_mh(NULL, this); for (size_t i = 0; i < klass->NumVirtualMethods(); ++i) { mirror::ArtMethod* local_method = klass->GetVirtualMethodDuringLinking(i); - local_mh.ChangeMethod(local_method); + MethodHelper local_mh(local_method); size_t j = 0; for (; j < actual_count; ++j) { mirror::ArtMethod* super_method = vtable->Get(j); - super_mh.ChangeMethod(super_method); + MethodHelper super_mh(super_method); if (local_mh.HasSameNameAndSignature(&super_mh)) { if (klass->CanAccessMember(super_method->GetDeclaringClass(), super_method->GetAccessFlags())) { if (super_method->IsFinal()) { @@ -3525,7 +3519,7 @@ bool ClassLinker::LinkVirtualMethods(SirtRef& klass) { } bool ClassLinker::LinkInterfaceMethods(SirtRef& klass, - mirror::ObjectArray* interfaces) { + SirtRef >& interfaces) { // Set the imt table to be all conflicts by default. klass->SetImTable(Runtime::Current()->GetDefaultImt()); size_t super_ifcount; @@ -3535,11 +3529,13 @@ bool ClassLinker::LinkInterfaceMethods(SirtRef& klass, super_ifcount = 0; } size_t ifcount = super_ifcount; - ClassHelper kh(klass.get(), this); - uint32_t num_interfaces = interfaces == NULL ? kh.NumDirectInterfaces() : interfaces->GetLength(); + ClassHelper kh(klass.get()); + uint32_t num_interfaces = + interfaces.get() == nullptr ? kh.NumDirectInterfaces() : interfaces->GetLength(); ifcount += num_interfaces; for (size_t i = 0; i < num_interfaces; i++) { - mirror::Class* interface = interfaces == NULL ? kh.GetDirectInterface(i) : interfaces->Get(i); + mirror::Class* interface = + interfaces.get() == nullptr ? kh.GetDirectInterface(i) : interfaces->Get(i); ifcount += interface->GetIfTableCount(); } if (ifcount == 0) { @@ -3580,7 +3576,8 @@ bool ClassLinker::LinkInterfaceMethods(SirtRef& klass, // Flatten the interface inheritance hierarchy. size_t idx = super_ifcount; for (size_t i = 0; i < num_interfaces; i++) { - mirror::Class* interface = interfaces == NULL ? kh.GetDirectInterface(i) : interfaces->Get(i); + mirror::Class* interface = + interfaces.get() == nullptr ? kh.GetDirectInterface(i) : interfaces->Get(i); DCHECK(interface != NULL); if (!interface->IsInterface()) { ClassHelper ih(interface); @@ -3643,20 +3640,21 @@ bool ClassLinker::LinkInterfaceMethods(SirtRef& klass, return false; } std::vector miranda_list; - MethodHelper vtable_mh(NULL, this); - MethodHelper interface_mh(NULL, this); + MethodHelper vtable_mh(NULL); + MethodHelper interface_mh(NULL); for (size_t i = 0; i < ifcount; ++i) { mirror::Class* interface = iftable->GetInterface(i); size_t num_methods = interface->NumVirtualMethods(); if (num_methods > 0) { - mirror::ObjectArray* method_array = - AllocArtMethodArray(self, num_methods); - if (UNLIKELY(method_array == NULL)) { + SirtRef > + method_array(self, AllocArtMethodArray(self, num_methods)); + if (UNLIKELY(method_array.get() == nullptr)) { CHECK(self->IsExceptionPending()); // OOME. return false; } - iftable->SetMethodArray(i, method_array); - mirror::ObjectArray* vtable = klass->GetVTableDuringLinking(); + iftable->SetMethodArray(i, method_array.get()); + SirtRef > vtable(self, + klass->GetVTableDuringLinking()); for (size_t j = 0; j < num_methods; ++j) { mirror::ArtMethod* interface_method = interface->GetVirtualMethod(j); interface_mh.ChangeMethod(interface_method); @@ -3709,10 +3707,7 @@ bool ClassLinker::LinkInterfaceMethods(SirtRef& klass, CHECK(self->IsExceptionPending()); // OOME. return false; } -#ifdef MOVING_GARBAGE_COLLECTOR // TODO: If a methods move then the miranda_list may hold stale references. - UNIMPLEMENTED(FATAL); -#endif miranda_list.push_back(miranda_method.get()); } method_array->Set(j, miranda_method.get()); @@ -3791,17 +3786,16 @@ bool ClassLinker::LinkStaticFields(SirtRef& klass) { } struct LinkFieldsComparator { - explicit LinkFieldsComparator(FieldHelper* fh) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) - : fh_(fh) {} + explicit LinkFieldsComparator() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + } // No thread safety analysis as will be called from STL. Checked lock held in constructor. bool operator()(const mirror::ArtField* field1, const mirror::ArtField* field2) NO_THREAD_SAFETY_ANALYSIS { // First come reference fields, then 64-bit, and finally 32-bit - fh_->ChangeField(field1); - Primitive::Type type1 = fh_->GetTypeAsPrimitiveType(); - fh_->ChangeField(field2); - Primitive::Type type2 = fh_->GetTypeAsPrimitiveType(); + FieldHelper fh1(field1); + Primitive::Type type1 = fh1.GetTypeAsPrimitiveType(); + FieldHelper fh2(field2); + Primitive::Type type2 = fh2.GetTypeAsPrimitiveType(); bool isPrimitive1 = type1 != Primitive::kPrimNot; bool isPrimitive2 = type2 != Primitive::kPrimNot; bool is64bit1 = isPrimitive1 && (type1 == Primitive::kPrimLong || type1 == Primitive::kPrimDouble); @@ -3813,14 +3807,10 @@ struct LinkFieldsComparator { } // same basic group? then sort by string. - fh_->ChangeField(field1); - const char* name1 = fh_->GetName(); - fh_->ChangeField(field2); - const char* name2 = fh_->GetName(); + const char* name1 = fh1.GetName(); + const char* name2 = fh2.GetName(); return strcmp(name1, name2) < 0; } - - FieldHelper* fh_; }; bool ClassLinker::LinkFields(SirtRef& klass, bool is_static) { @@ -3855,17 +3845,15 @@ bool ClassLinker::LinkFields(SirtRef& klass, bool is_static) { CHECK(f != NULL); grouped_and_sorted_fields.push_back(f); } - FieldHelper fh(NULL, this); - std::sort(grouped_and_sorted_fields.begin(), - grouped_and_sorted_fields.end(), - LinkFieldsComparator(&fh)); + std::sort(grouped_and_sorted_fields.begin(), grouped_and_sorted_fields.end(), + LinkFieldsComparator()); // References should be at the front. size_t current_field = 0; size_t num_reference_fields = 0; for (; current_field < num_fields; current_field++) { mirror::ArtField* field = grouped_and_sorted_fields.front(); - fh.ChangeField(field); + FieldHelper fh(field); Primitive::Type type = fh.GetTypeAsPrimitiveType(); bool isPrimitive = type != Primitive::kPrimNot; if (isPrimitive) { @@ -3884,7 +3872,7 @@ bool ClassLinker::LinkFields(SirtRef& klass, bool is_static) { if (current_field != num_fields && !IsAligned<8>(field_offset.Uint32Value())) { for (size_t i = 0; i < grouped_and_sorted_fields.size(); i++) { mirror::ArtField* field = grouped_and_sorted_fields[i]; - fh.ChangeField(field); + FieldHelper fh(field); Primitive::Type type = fh.GetTypeAsPrimitiveType(); CHECK(type != Primitive::kPrimNot); // should only be working on primitive types if (type == Primitive::kPrimLong || type == Primitive::kPrimDouble) { @@ -3906,7 +3894,7 @@ bool ClassLinker::LinkFields(SirtRef& klass, bool is_static) { while (!grouped_and_sorted_fields.empty()) { mirror::ArtField* field = grouped_and_sorted_fields.front(); grouped_and_sorted_fields.pop_front(); - fh.ChangeField(field); + FieldHelper fh(field); Primitive::Type type = fh.GetTypeAsPrimitiveType(); CHECK(type != Primitive::kPrimNot); // should only be working on primitive types fields->Set(current_field, field); @@ -3920,11 +3908,11 @@ bool ClassLinker::LinkFields(SirtRef& klass, bool is_static) { // We lie to the GC about the java.lang.ref.Reference.referent field, so it doesn't scan it. if (!is_static && - (strcmp("Ljava/lang/ref/Reference;", ClassHelper(klass.get(), this).GetDescriptor()) == 0)) { + (strcmp("Ljava/lang/ref/Reference;", ClassHelper(klass.get()).GetDescriptor()) == 0)) { // We know there are no non-reference fields in the Reference classes, and we know // that 'referent' is alphabetically last, so this is easy... CHECK_EQ(num_reference_fields, num_fields); - fh.ChangeField(fields->Get(num_fields - 1)); + FieldHelper fh(fields->Get(num_fields - 1)); CHECK_STREQ(fh.GetName(), "referent"); --num_reference_fields; } @@ -3942,10 +3930,10 @@ bool ClassLinker::LinkFields(SirtRef& klass, bool is_static) { << " offset=" << field->GetField32(MemberOffset(mirror::ArtField::OffsetOffset()), false); } - fh.ChangeField(field); + FieldHelper fh(field); Primitive::Type type = fh.GetTypeAsPrimitiveType(); bool is_primitive = type != Primitive::kPrimNot; - if ((strcmp("Ljava/lang/ref/Reference;", ClassHelper(klass.get(), this).GetDescriptor()) == 0) + if ((strcmp("Ljava/lang/ref/Reference;", ClassHelper(klass.get()).GetDescriptor()) == 0) && (strcmp("referent", fh.GetName()) == 0)) { is_primitive = true; // We lied above, so we have to expect a lie here. } @@ -3970,7 +3958,7 @@ bool ClassLinker::LinkFields(SirtRef& klass, bool is_static) { } else { klass->SetNumReferenceInstanceFields(num_reference_fields); if (!klass->IsVariableSize()) { - DCHECK_GE(size, sizeof(mirror::Object)) << ClassHelper(klass.get(), this).GetDescriptor(); + DCHECK_GE(size, sizeof(mirror::Object)) << ClassHelper(klass.get()).GetDescriptor(); size_t previous_size = klass->GetObjectSize(); if (previous_size != 0) { // Make sure that we didn't originally have an incorrect size. @@ -4034,9 +4022,9 @@ void ClassLinker::CreateReferenceOffsets(SirtRef& klass, bool is_ } } -mirror::String* ClassLinker::ResolveString(const DexFile& dex_file, - uint32_t string_idx, mirror::DexCache* dex_cache) { - DCHECK(dex_cache != NULL); +mirror::String* ClassLinker::ResolveString(const DexFile& dex_file, uint32_t string_idx, + SirtRef& dex_cache) { + DCHECK(dex_cache.get() != nullptr); mirror::String* resolved = dex_cache->GetResolvedString(string_idx); if (resolved != NULL) { return resolved; @@ -4048,11 +4036,18 @@ mirror::String* ClassLinker::ResolveString(const DexFile& dex_file, return string; } -mirror::Class* ClassLinker::ResolveType(const DexFile& dex_file, - uint16_t type_idx, - mirror::DexCache* dex_cache, - mirror::ClassLoader* class_loader) { - DCHECK(dex_cache != NULL); +mirror::Class* ClassLinker::ResolveType(const DexFile& dex_file, uint16_t type_idx, + const mirror::Class* referrer) { + Thread* self = Thread::Current(); + SirtRef dex_cache(self, referrer->GetDexCache()); + SirtRef class_loader(self, referrer->GetClassLoader()); + return ResolveType(dex_file, type_idx, dex_cache, class_loader); +} + +mirror::Class* ClassLinker::ResolveType(const DexFile& dex_file, uint16_t type_idx, + SirtRef& dex_cache, + SirtRef& class_loader) { + DCHECK(dex_cache.get() != NULL); mirror::Class* resolved = dex_cache->GetResolvedType(type_idx); if (resolved == NULL) { const char* descriptor = dex_file.StringByTypeIdx(type_idx); @@ -4082,11 +4077,11 @@ mirror::Class* ClassLinker::ResolveType(const DexFile& dex_file, mirror::ArtMethod* ClassLinker::ResolveMethod(const DexFile& dex_file, uint32_t method_idx, - mirror::DexCache* dex_cache, - mirror::ClassLoader* class_loader, + SirtRef& dex_cache, + SirtRef& class_loader, const mirror::ArtMethod* referrer, InvokeType type) { - DCHECK(dex_cache != NULL); + DCHECK(dex_cache.get() != NULL); // Check for hit in the dex cache. mirror::ArtMethod* resolved = dex_cache->GetResolvedMethod(method_idx); if (resolved != NULL && !resolved->IsRuntimeMethod()) { @@ -4104,15 +4099,15 @@ mirror::ArtMethod* ClassLinker::ResolveMethod(const DexFile& dex_file, switch (type) { case kDirect: // Fall-through. case kStatic: - resolved = klass->FindDirectMethod(dex_cache, method_idx); + resolved = klass->FindDirectMethod(dex_cache.get(), method_idx); break; case kInterface: - resolved = klass->FindInterfaceMethod(dex_cache, method_idx); + resolved = klass->FindInterfaceMethod(dex_cache.get(), method_idx); DCHECK(resolved == NULL || resolved->GetDeclaringClass()->IsInterface()); break; case kSuper: // Fall-through. case kVirtual: - resolved = klass->FindVirtualMethod(dex_cache, method_idx); + resolved = klass->FindVirtualMethod(dex_cache.get(), method_idx); break; default: LOG(FATAL) << "Unreachable - invocation type: " << type; @@ -4227,12 +4222,11 @@ mirror::ArtMethod* ClassLinker::ResolveMethod(const DexFile& dex_file, } } -mirror::ArtField* ClassLinker::ResolveField(const DexFile& dex_file, - uint32_t field_idx, - mirror::DexCache* dex_cache, - mirror::ClassLoader* class_loader, +mirror::ArtField* ClassLinker::ResolveField(const DexFile& dex_file, uint32_t field_idx, + SirtRef& dex_cache, + SirtRef& class_loader, bool is_static) { - DCHECK(dex_cache != NULL); + DCHECK(dex_cache.get() != nullptr); mirror::ArtField* resolved = dex_cache->GetResolvedField(field_idx); if (resolved != NULL) { return resolved; @@ -4245,9 +4239,9 @@ mirror::ArtField* ClassLinker::ResolveField(const DexFile& dex_file, } if (is_static) { - resolved = klass->FindStaticField(dex_cache, field_idx); + resolved = klass->FindStaticField(dex_cache.get(), field_idx); } else { - resolved = klass->FindInstanceField(dex_cache, field_idx); + resolved = klass->FindInstanceField(dex_cache.get(), field_idx); } if (resolved == NULL) { @@ -4269,9 +4263,9 @@ mirror::ArtField* ClassLinker::ResolveField(const DexFile& dex_file, mirror::ArtField* ClassLinker::ResolveFieldJLS(const DexFile& dex_file, uint32_t field_idx, - mirror::DexCache* dex_cache, - mirror::ClassLoader* class_loader) { - DCHECK(dex_cache != NULL); + SirtRef& dex_cache, + SirtRef& class_loader) { + DCHECK(dex_cache.get() != nullptr); mirror::ArtField* resolved = dex_cache->GetResolvedField(field_idx); if (resolved != NULL) { return resolved; diff --git a/runtime/class_linker.h b/runtime/class_linker.h index 473370d90f0..4e2cc063b34 100644 --- a/runtime/class_linker.h +++ b/runtime/class_linker.h @@ -25,6 +25,7 @@ #include "base/mutex.h" #include "dex_file.h" #include "gtest/gtest.h" +#include "jni.h" #include "root_visitor.h" #include "oat_file.h" @@ -45,6 +46,7 @@ namespace mirror { class InternTable; class ObjectLock; +class ScopedObjectAccess; template class SirtRef; typedef bool (ClassVisitor)(mirror::Class* c, void* arg); @@ -56,29 +58,31 @@ class ClassLinker { // (non-marker) interfaces. static constexpr size_t kImtSize = 64; - // Creates the class linker by bootstrapping from dex files. - static ClassLinker* CreateFromCompiler(const std::vector& boot_class_path, - InternTable* intern_table) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + explicit ClassLinker(InternTable* intern_table); + ~ClassLinker(); - // Creates the class linker from an image. - static ClassLinker* CreateFromImage(InternTable* intern_table) + // Initialize class linker by bootstraping from dex files + void InitFromCompiler(const std::vector& boot_class_path) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - ~ClassLinker(); + // Initialize class linker from one or more images. + void InitFromImage() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); bool IsInBootClassPath(const char* descriptor); // Finds a class by its descriptor, loading it if necessary. // If class_loader is null, searches boot_class_path_. - mirror::Class* FindClass(const char* descriptor, mirror::ClassLoader* class_loader) + mirror::Class* FindClass(const char* descriptor, SirtRef& class_loader) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); mirror::Class* FindSystemClass(const char* descriptor) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + // Reutrns true if the class linker is initialized. + bool IsInitialized() const; + // Define a new a class based on a ClassDef from a DexFile - mirror::Class* DefineClass(const char* descriptor, mirror::ClassLoader* class_loader, + mirror::Class* DefineClass(const char* descriptor, SirtRef& class_loader, const DexFile& dex_file, const DexFile::ClassDef& dex_class_def) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); @@ -122,7 +126,7 @@ class ClassLinker { // Resolve a String with the given index from the DexFile, storing the // result in the DexCache. mirror::String* ResolveString(const DexFile& dex_file, uint32_t string_idx, - mirror::DexCache* dex_cache) + SirtRef& dex_cache) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); // Resolve a Type with the given index from the DexFile, storing the @@ -130,12 +134,7 @@ class ClassLinker { // target DexCache and ClassLoader to use for resolution. mirror::Class* ResolveType(const DexFile& dex_file, uint16_t type_idx, const mirror::Class* referrer) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - return ResolveType(dex_file, - type_idx, - referrer->GetDexCache(), - referrer->GetClassLoader()); - } + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); // Resolve a Type with the given index from the DexFile, storing the // result in the DexCache. The referrer is used to identify the @@ -150,10 +149,9 @@ class ClassLinker { // result in DexCache. The ClassLoader is used to search for the // type, since it may be referenced from but not contained within // the given DexFile. - mirror::Class* ResolveType(const DexFile& dex_file, - uint16_t type_idx, - mirror::DexCache* dex_cache, - mirror::ClassLoader* class_loader) + mirror::Class* ResolveType(const DexFile& dex_file, uint16_t type_idx, + SirtRef& dex_cache, + SirtRef& class_loader) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); // Resolve a method with a given ID from the DexFile, storing the @@ -163,8 +161,8 @@ class ClassLinker { // virtual method. mirror::ArtMethod* ResolveMethod(const DexFile& dex_file, uint32_t method_idx, - mirror::DexCache* dex_cache, - mirror::ClassLoader* class_loader, + SirtRef& dex_cache, + SirtRef& class_loader, const mirror::ArtMethod* referrer, InvokeType type) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); @@ -184,8 +182,8 @@ class ClassLinker { // field. mirror::ArtField* ResolveField(const DexFile& dex_file, uint32_t field_idx, - mirror::DexCache* dex_cache, - mirror::ClassLoader* class_loader, + SirtRef& dex_cache, + SirtRef& class_loader, bool is_static) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); @@ -195,8 +193,8 @@ class ClassLinker { // field resolution semantics are followed. mirror::ArtField* ResolveFieldJLS(const DexFile& dex_file, uint32_t field_idx, - mirror::DexCache* dex_cache, - mirror::ClassLoader* class_loader) + SirtRef& dex_cache, + SirtRef& class_loader) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); // Get shorty from method index without resolution. Used to do handlerization. @@ -314,10 +312,8 @@ class ClassLinker { void ResolveMethodExceptionHandlerTypes(const DexFile& dex_file, mirror::ArtMethod* klass) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - mirror::Class* CreateProxyClass(mirror::String* name, mirror::ObjectArray* interfaces, - mirror::ClassLoader* loader, - mirror::ObjectArray* methods, - mirror::ObjectArray >* throws) + mirror::Class* CreateProxyClass(ScopedObjectAccess& soa, jstring name, jobjectArray interfaces, + jobject loader, jobjectArray methods, jobjectArray throws) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); std::string GetDescriptorForProxy(const mirror::Class* proxy_class) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); @@ -364,18 +360,13 @@ class ClassLinker { LOCKS_EXCLUDED(Locks::classlinker_classes_lock_) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - private: - explicit ClassLinker(InternTable*); + // Special code to allocate an art method, use this instead of class->AllocObject. + mirror::ArtMethod* AllocArtMethod(Thread* self) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + private: const OatFile::OatMethod GetOatMethodFor(const mirror::ArtMethod* method) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - // Initialize class linker by bootstraping from dex files - void InitFromCompiler(const std::vector& boot_class_path) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - - // Initialize class linker from one or more images. - void InitFromImage() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); OatFile& GetImageOatFile(gc::space::ImageSpace* space) LOCKS_EXCLUDED(dex_lock_) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); @@ -393,7 +384,6 @@ class ClassLinker { mirror::DexCache* AllocDexCache(Thread* self, const DexFile& dex_file) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); mirror::ArtField* AllocArtField(Thread* self) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - mirror::ArtMethod* AllocArtMethod(Thread* self) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); mirror::Class* CreatePrimitiveClass(Thread* self, Primitive::Type type) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); @@ -401,7 +391,8 @@ class ClassLinker { SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - mirror::Class* CreateArrayClass(const char* descriptor, mirror::ClassLoader* class_loader) + mirror::Class* CreateArrayClass(const char* descriptor, + SirtRef& class_loader) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); void AppendToBootClassPath(const DexFile& dex_file) @@ -458,8 +449,8 @@ class ClassLinker { const mirror::Class* klass2) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - bool LinkClass(SirtRef& klass, mirror::ObjectArray* interfaces, - Thread* self) + bool LinkClass(Thread* self, SirtRef& klass, + SirtRef >& interfaces) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); bool LinkSuperClass(SirtRef& klass) @@ -468,14 +459,15 @@ class ClassLinker { bool LoadSuperAndInterfaces(SirtRef& klass, const DexFile& dex_file) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - bool LinkMethods(SirtRef& klass, mirror::ObjectArray* interfaces) + bool LinkMethods(SirtRef& klass, + SirtRef >& interfaces) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); bool LinkVirtualMethods(SirtRef& klass) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); bool LinkInterfaceMethods(SirtRef& klass, - mirror::ObjectArray* interfaces) + SirtRef >& interfaces) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); bool LinkStaticFields(SirtRef& klass) diff --git a/runtime/class_linker_test.cc b/runtime/class_linker_test.cc index a52b6802602..b8bc474b107 100644 --- a/runtime/class_linker_test.cc +++ b/runtime/class_linker_test.cc @@ -95,7 +95,8 @@ class ClassLinkerTest : public CommonTest { const std::string& component_type, mirror::ClassLoader* class_loader) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - mirror::Class* array = class_linker_->FindClass(array_descriptor.c_str(), class_loader); + SirtRef loader(Thread::Current(), class_loader); + mirror::Class* array = class_linker_->FindClass(array_descriptor.c_str(), loader); ClassHelper array_component_ch(array->GetComponentType()); EXPECT_STREQ(component_type.c_str(), array_component_ch.GetDescriptor()); EXPECT_EQ(class_loader, array->GetClassLoader()); @@ -647,12 +648,12 @@ TEST_F(ClassLinkerTest, FindClassNested) { ScopedObjectAccess soa(Thread::Current()); SirtRef class_loader(soa.Self(), soa.Decode(LoadDex("Nested"))); - mirror::Class* outer = class_linker_->FindClass("LNested;", class_loader.get()); + mirror::Class* outer = class_linker_->FindClass("LNested;", class_loader); ASSERT_TRUE(outer != NULL); EXPECT_EQ(0U, outer->NumVirtualMethods()); EXPECT_EQ(1U, outer->NumDirectMethods()); - mirror::Class* inner = class_linker_->FindClass("LNested$Inner;", class_loader.get()); + mirror::Class* inner = class_linker_->FindClass("LNested$Inner;", class_loader); ASSERT_TRUE(inner != NULL); EXPECT_EQ(0U, inner->NumVirtualMethods()); EXPECT_EQ(1U, inner->NumDirectMethods()); @@ -711,7 +712,7 @@ TEST_F(ClassLinkerTest, FindClass) { SirtRef class_loader(soa.Self(), soa.Decode(LoadDex("MyClass"))); AssertNonExistentClass("LMyClass;"); - mirror::Class* MyClass = class_linker_->FindClass("LMyClass;", class_loader.get()); + mirror::Class* MyClass = class_linker_->FindClass("LMyClass;", class_loader); kh.ChangeClass(MyClass); ASSERT_TRUE(MyClass != NULL); ASSERT_TRUE(MyClass->GetClass() != NULL); @@ -809,29 +810,30 @@ TEST_F(ClassLinkerTest, ValidateBoxedTypes) { // Validate that the "value" field is always the 0th field in each of java.lang's box classes. // This lets UnboxPrimitive avoid searching for the field by name at runtime. ScopedObjectAccess soa(Thread::Current()); + SirtRef class_loader(soa.Self(), nullptr); mirror::Class* c; - c = class_linker_->FindClass("Ljava/lang/Boolean;", NULL); + c = class_linker_->FindClass("Ljava/lang/Boolean;", class_loader); FieldHelper fh(c->GetIFields()->Get(0)); EXPECT_STREQ("value", fh.GetName()); - c = class_linker_->FindClass("Ljava/lang/Byte;", NULL); + c = class_linker_->FindClass("Ljava/lang/Byte;", class_loader); fh.ChangeField(c->GetIFields()->Get(0)); EXPECT_STREQ("value", fh.GetName()); - c = class_linker_->FindClass("Ljava/lang/Character;", NULL); + c = class_linker_->FindClass("Ljava/lang/Character;", class_loader); fh.ChangeField(c->GetIFields()->Get(0)); EXPECT_STREQ("value", fh.GetName()); - c = class_linker_->FindClass("Ljava/lang/Double;", NULL); + c = class_linker_->FindClass("Ljava/lang/Double;", class_loader); fh.ChangeField(c->GetIFields()->Get(0)); EXPECT_STREQ("value", fh.GetName()); - c = class_linker_->FindClass("Ljava/lang/Float;", NULL); + c = class_linker_->FindClass("Ljava/lang/Float;", class_loader); fh.ChangeField(c->GetIFields()->Get(0)); EXPECT_STREQ("value", fh.GetName()); - c = class_linker_->FindClass("Ljava/lang/Integer;", NULL); + c = class_linker_->FindClass("Ljava/lang/Integer;", class_loader); fh.ChangeField(c->GetIFields()->Get(0)); EXPECT_STREQ("value", fh.GetName()); - c = class_linker_->FindClass("Ljava/lang/Long;", NULL); + c = class_linker_->FindClass("Ljava/lang/Long;", class_loader); fh.ChangeField(c->GetIFields()->Get(0)); EXPECT_STREQ("value", fh.GetName()); - c = class_linker_->FindClass("Ljava/lang/Short;", NULL); + c = class_linker_->FindClass("Ljava/lang/Short;", class_loader); fh.ChangeField(c->GetIFields()->Get(0)); EXPECT_STREQ("value", fh.GetName()); } @@ -840,8 +842,8 @@ TEST_F(ClassLinkerTest, TwoClassLoadersOneClass) { ScopedObjectAccess soa(Thread::Current()); SirtRef class_loader_1(soa.Self(), soa.Decode(LoadDex("MyClass"))); SirtRef class_loader_2(soa.Self(), soa.Decode(LoadDex("MyClass"))); - mirror::Class* MyClass_1 = class_linker_->FindClass("LMyClass;", class_loader_1.get()); - mirror::Class* MyClass_2 = class_linker_->FindClass("LMyClass;", class_loader_2.get()); + mirror::Class* MyClass_1 = class_linker_->FindClass("LMyClass;", class_loader_1); + mirror::Class* MyClass_2 = class_linker_->FindClass("LMyClass;", class_loader_2); EXPECT_TRUE(MyClass_1 != NULL); EXPECT_TRUE(MyClass_2 != NULL); EXPECT_NE(MyClass_1, MyClass_2); @@ -850,7 +852,7 @@ TEST_F(ClassLinkerTest, TwoClassLoadersOneClass) { TEST_F(ClassLinkerTest, StaticFields) { ScopedObjectAccess soa(Thread::Current()); SirtRef class_loader(soa.Self(), soa.Decode(LoadDex("Statics"))); - mirror::Class* statics = class_linker_->FindClass("LStatics;", class_loader.get()); + mirror::Class* statics = class_linker_->FindClass("LStatics;", class_loader); class_linker_->EnsureInitialized(statics, true, true); // Static final primitives that are initialized by a compile-time constant @@ -932,11 +934,11 @@ TEST_F(ClassLinkerTest, StaticFields) { TEST_F(ClassLinkerTest, Interfaces) { ScopedObjectAccess soa(Thread::Current()); SirtRef class_loader(soa.Self(), soa.Decode(LoadDex("Interfaces"))); - mirror::Class* I = class_linker_->FindClass("LInterfaces$I;", class_loader.get()); - mirror::Class* J = class_linker_->FindClass("LInterfaces$J;", class_loader.get()); - mirror::Class* K = class_linker_->FindClass("LInterfaces$K;", class_loader.get()); - mirror::Class* A = class_linker_->FindClass("LInterfaces$A;", class_loader.get()); - mirror::Class* B = class_linker_->FindClass("LInterfaces$B;", class_loader.get()); + mirror::Class* I = class_linker_->FindClass("LInterfaces$I;", class_loader); + mirror::Class* J = class_linker_->FindClass("LInterfaces$J;", class_loader); + mirror::Class* K = class_linker_->FindClass("LInterfaces$K;", class_loader); + mirror::Class* A = class_linker_->FindClass("LInterfaces$A;", class_loader); + mirror::Class* B = class_linker_->FindClass("LInterfaces$B;", class_loader); EXPECT_TRUE(I->IsAssignableFrom(A)); EXPECT_TRUE(J->IsAssignableFrom(A)); EXPECT_TRUE(J->IsAssignableFrom(K)); @@ -995,8 +997,7 @@ TEST_F(ClassLinkerTest, ResolveVerifyAndClinit) { SirtRef class_loader(soa.Self(), soa.Decode(jclass_loader)); const DexFile* dex_file = Runtime::Current()->GetCompileTimeClassPath(jclass_loader)[0]; CHECK(dex_file != NULL); - - mirror::Class* klass = class_linker_->FindClass("LStaticsFromCode;", class_loader.get()); + mirror::Class* klass = class_linker_->FindClass("LStaticsFromCode;", class_loader); mirror::ArtMethod* clinit = klass->FindClassInitializer(); mirror::ArtMethod* getS0 = klass->FindDirectMethod("getS0", "()Ljava/lang/Object;"); const DexFile::StringId* string_id = dex_file->FindStringId("LStaticsFromCode;"); @@ -1049,10 +1050,9 @@ TEST_F(ClassLinkerTest, FinalizableBit) { TEST_F(ClassLinkerTest, ClassRootDescriptors) { ScopedObjectAccess soa(Thread::Current()); - ClassHelper kh; for (int i = 0; i < ClassLinker::kClassRootsMax; i++) { mirror::Class* klass = class_linker_->GetClassRoot(ClassLinker::ClassRoot(i)); - kh.ChangeClass(klass); + ClassHelper kh(klass); EXPECT_TRUE(kh.GetDescriptor() != NULL); EXPECT_STREQ(kh.GetDescriptor(), class_linker_->GetClassRootDescriptor(ClassLinker::ClassRoot(i))) << " i = " << i; diff --git a/runtime/common_test.h b/runtime/common_test.h index 643ed1d89d8..7cc29a1e58b 100644 --- a/runtime/common_test.h +++ b/runtime/common_test.h @@ -569,7 +569,8 @@ class CommonTest : public testing::Test { void CompileClass(mirror::ClassLoader* class_loader, const char* class_name) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { std::string class_descriptor(DotToDescriptor(class_name)); - mirror::Class* klass = class_linker_->FindClass(class_descriptor.c_str(), class_loader); + SirtRef loader(Thread::Current(), class_loader); + mirror::Class* klass = class_linker_->FindClass(class_descriptor.c_str(), loader); CHECK(klass != NULL) << "Class not found " << class_name; for (size_t i = 0; i < klass->NumDirectMethods(); i++) { CompileMethod(klass->GetDirectMethod(i)); @@ -587,10 +588,8 @@ class CommonTest : public testing::Test { MakeExecutable(method); } - void CompileDirectMethod(mirror::ClassLoader* class_loader, - const char* class_name, - const char* method_name, - const char* signature) + void CompileDirectMethod(SirtRef& class_loader, const char* class_name, + const char* method_name, const char* signature) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { std::string class_descriptor(DotToDescriptor(class_name)); mirror::Class* klass = class_linker_->FindClass(class_descriptor.c_str(), class_loader); @@ -601,10 +600,8 @@ class CommonTest : public testing::Test { CompileMethod(method); } - void CompileVirtualMethod(mirror::ClassLoader* class_loader, - const char* class_name, - const char* method_name, - const char* signature) + void CompileVirtualMethod(SirtRef& class_loader, const char* class_name, + const char* method_name, const char* signature) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { std::string class_descriptor(DotToDescriptor(class_name)); mirror::Class* klass = class_linker_->FindClass(class_descriptor.c_str(), class_loader); diff --git a/runtime/debugger.cc b/runtime/debugger.cc index 0eecd288311..f5377092618 100644 --- a/runtime/debugger.cc +++ b/runtime/debugger.cc @@ -1118,7 +1118,8 @@ JDWP::JdwpError Dbg::CreateArrayObject(JDWP::RefTypeId array_class_id, uint32_t if (c == NULL) { return status; } - new_array = gRegistry->Add(mirror::Array::Alloc(Thread::Current(), c, length)); + new_array = gRegistry->Add( + mirror::Array::Alloc(Thread::Current(), c, length)); return JDWP::ERR_NONE; } @@ -1133,38 +1134,26 @@ bool Dbg::MatchType(JDWP::RefTypeId instance_class_id, JDWP::RefTypeId class_id) static JDWP::FieldId ToFieldId(const mirror::ArtField* f) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { -#ifdef MOVING_GARBAGE_COLLECTOR - UNIMPLEMENTED(FATAL); -#else + CHECK(!kMovingFields); return static_cast(reinterpret_cast(f)); -#endif } static JDWP::MethodId ToMethodId(const mirror::ArtMethod* m) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { -#ifdef MOVING_GARBAGE_COLLECTOR - UNIMPLEMENTED(FATAL); -#else + CHECK(!kMovingMethods); return static_cast(reinterpret_cast(m)); -#endif } static mirror::ArtField* FromFieldId(JDWP::FieldId fid) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { -#ifdef MOVING_GARBAGE_COLLECTOR - UNIMPLEMENTED(FATAL); -#else + CHECK(!kMovingFields); return reinterpret_cast(static_cast(fid)); -#endif } static mirror::ArtMethod* FromMethodId(JDWP::MethodId mid) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { -#ifdef MOVING_GARBAGE_COLLECTOR - UNIMPLEMENTED(FATAL); -#else + CHECK(!kMovingMethods); return reinterpret_cast(static_cast(mid)); -#endif } static void SetLocation(JDWP::JdwpLocation& location, mirror::ArtMethod* m, uint32_t dex_pc) @@ -2079,7 +2068,7 @@ void Dbg::GetLocalValue(JDWP::ObjectId thread_id, JDWP::FrameId frame_id, int sl CHECK_EQ(width_, sizeof(JDWP::ObjectId)); mirror::Object* o = reinterpret_cast(GetVReg(m, reg, kReferenceVReg)); VLOG(jdwp) << "get array local " << reg << " = " << o; - if (!Runtime::Current()->GetHeap()->IsHeapAddress(o)) { + if (!Runtime::Current()->GetHeap()->IsValidObjectAddress(o)) { LOG(FATAL) << "Register " << reg << " expected to hold array: " << o; } JDWP::SetObjectId(buf_+1, gRegistry->Add(o)); @@ -2095,7 +2084,7 @@ void Dbg::GetLocalValue(JDWP::ObjectId thread_id, JDWP::FrameId frame_id, int sl CHECK_EQ(width_, sizeof(JDWP::ObjectId)); mirror::Object* o = reinterpret_cast(GetVReg(m, reg, kReferenceVReg)); VLOG(jdwp) << "get object local " << reg << " = " << o; - if (!Runtime::Current()->GetHeap()->IsHeapAddress(o)) { + if (!Runtime::Current()->GetHeap()->IsValidObjectAddress(o)) { LOG(FATAL) << "Register " << reg << " expected to hold object: " << o; } tag_ = TagFromObject(o); @@ -3372,7 +3361,7 @@ class HeapChunkContext { return HPSG_STATE(SOLIDITY_HARD, KIND_OBJECT); } - if (!Runtime::Current()->GetHeap()->IsHeapAddress(c)) { + if (!Runtime::Current()->GetHeap()->IsValidObjectAddress(c)) { LOG(ERROR) << "Invalid class for managed heap object: " << o << " " << c; return HPSG_STATE(SOLIDITY_HARD, KIND_UNKNOWN); } @@ -3752,7 +3741,6 @@ jbyteArray Dbg::GetRecentAllocations() { count = gAllocRecordCount; idx = HeadIndex(); - ClassHelper kh; while (count--) { // For each entry: // (4b) total allocation size @@ -3761,7 +3749,7 @@ jbyteArray Dbg::GetRecentAllocations() { // (1b) stack depth AllocRecord* record = &recent_allocation_records_[idx]; size_t stack_depth = record->GetDepth(); - kh.ChangeClass(record->type); + ClassHelper kh(record->type); size_t allocated_object_class_name_index = class_names.IndexOf(kh.GetDescriptor()); JDWP::Append4BE(bytes, record->byte_count); JDWP::Append2BE(bytes, record->thin_lock_id); diff --git a/runtime/dex_file.cc b/runtime/dex_file.cc index a897cce2e0a..a02823eb90f 100644 --- a/runtime/dex_file.cc +++ b/runtime/dex_file.cc @@ -37,6 +37,7 @@ #include "os.h" #include "safe_map.h" #include "ScopedFd.h" +#include "sirt_ref.h" #include "thread.h" #include "UniquePtr.h" #include "utf-inl.h" @@ -963,12 +964,14 @@ static uint64_t ReadUnsignedLong(const byte* ptr, int zwidth, bool fill_on_right } EncodedStaticFieldValueIterator::EncodedStaticFieldValueIterator(const DexFile& dex_file, - mirror::DexCache* dex_cache, - mirror::ClassLoader* class_loader, + SirtRef* dex_cache, + SirtRef* class_loader, ClassLinker* linker, const DexFile::ClassDef& class_def) : dex_file_(dex_file), dex_cache_(dex_cache), class_loader_(class_loader), linker_(linker), array_size_(), pos_(-1), type_(kByte) { + DCHECK(dex_cache != nullptr); + DCHECK(class_loader != nullptr); ptr_ = dex_file.GetEncodedStaticFieldValuesArray(class_def); if (ptr_ == NULL) { array_size_ = 0; @@ -1051,12 +1054,15 @@ void EncodedStaticFieldValueIterator::ReadValueToField(mirror::ArtField* field) case kDouble: field->SetDouble(field->GetDeclaringClass(), jval_.d); break; case kNull: field->SetObject(field->GetDeclaringClass(), NULL); break; case kString: { - mirror::String* resolved = linker_->ResolveString(dex_file_, jval_.i, dex_cache_); + CHECK(!kMovingFields); + mirror::String* resolved = linker_->ResolveString(dex_file_, jval_.i, *dex_cache_); field->SetObject(field->GetDeclaringClass(), resolved); break; } case kType: { - mirror::Class* resolved = linker_->ResolveType(dex_file_, jval_.i, dex_cache_, class_loader_); + CHECK(!kMovingFields); + mirror::Class* resolved = linker_->ResolveType(dex_file_, jval_.i, *dex_cache_, + *class_loader_); field->SetObject(field->GetDeclaringClass(), resolved); break; } diff --git a/runtime/dex_file.h b/runtime/dex_file.h index a9c24e66c1e..51ab8d81a88 100644 --- a/runtime/dex_file.h +++ b/runtime/dex_file.h @@ -43,6 +43,8 @@ namespace mirror { } // namespace mirror class ClassLinker; class Signature; +template +class SirtRef; class StringPiece; class ZipArchive; @@ -1152,8 +1154,8 @@ class ClassDataItemIterator { class EncodedStaticFieldValueIterator { public: - EncodedStaticFieldValueIterator(const DexFile& dex_file, mirror::DexCache* dex_cache, - mirror::ClassLoader* class_loader, + EncodedStaticFieldValueIterator(const DexFile& dex_file, SirtRef* dex_cache, + SirtRef* class_loader, ClassLinker* linker, const DexFile::ClassDef& class_def) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); @@ -1187,8 +1189,8 @@ class EncodedStaticFieldValueIterator { static const byte kEncodedValueArgShift = 5; const DexFile& dex_file_; - mirror::DexCache* dex_cache_; // Dex cache to resolve literal objects. - mirror::ClassLoader* class_loader_; // ClassLoader to resolve types. + SirtRef* const dex_cache_; // Dex cache to resolve literal objects. + SirtRef* const class_loader_; // ClassLoader to resolve types. ClassLinker* linker_; // Linker to resolve literal objects. size_t array_size_; // Size of array. size_t pos_; // Current position. diff --git a/runtime/entrypoints/entrypoint_utils.cc b/runtime/entrypoints/entrypoint_utils.cc index 24ab1ce9dab..d7bbe6475a9 100644 --- a/runtime/entrypoints/entrypoint_utils.cc +++ b/runtime/entrypoints/entrypoint_utils.cc @@ -82,7 +82,7 @@ mirror::Array* CheckAndAllocArrayFromCode(uint32_t type_idx, mirror::ArtMethod* if (UNLIKELY(!CheckFilledNewArrayAlloc(type_idx, referrer, component_count, self, access_check, &klass))) { return NULL; } - return mirror::Array::AllocUninstrumented(self, klass, component_count); + return mirror::Array::Alloc(self, klass, component_count); } // Helper function to allocate array for FILLED_NEW_ARRAY. @@ -93,7 +93,7 @@ mirror::Array* CheckAndAllocArrayFromCodeInstrumented(uint32_t type_idx, mirror: if (UNLIKELY(!CheckFilledNewArrayAlloc(type_idx, referrer, component_count, self, access_check, &klass))) { return NULL; } - return mirror::Array::AllocInstrumented(self, klass, component_count); + return mirror::Array::Alloc(self, klass, component_count); } void ThrowStackOverflowError(Thread* self) { diff --git a/runtime/entrypoints/entrypoint_utils.h b/runtime/entrypoints/entrypoint_utils.h index 7ce50c5dfbe..3b58a8da300 100644 --- a/runtime/entrypoints/entrypoint_utils.h +++ b/runtime/entrypoints/entrypoint_utils.h @@ -88,7 +88,7 @@ static inline mirror::Object* AllocObjectFromCode(uint32_t type_idx, mirror::Art if (UNLIKELY(!CheckObjectAlloc(type_idx, method, self, access_check, &klass))) { return NULL; } - return klass->AllocObjectUninstrumented(self); + return klass->Alloc(self); } static inline mirror::Object* AllocObjectFromCodeInstrumented(uint32_t type_idx, mirror::ArtMethod* method, @@ -99,7 +99,7 @@ static inline mirror::Object* AllocObjectFromCodeInstrumented(uint32_t type_idx, if (UNLIKELY(!CheckObjectAlloc(type_idx, method, self, access_check, &klass))) { return NULL; } - return klass->AllocObjectInstrumented(self); + return klass->Alloc(self); } static inline bool CheckArrayAlloc(uint32_t type_idx, mirror::ArtMethod* method, @@ -142,7 +142,7 @@ static inline mirror::Array* AllocArrayFromCode(uint32_t type_idx, mirror::ArtMe if (UNLIKELY(!CheckArrayAlloc(type_idx, method, component_count, access_check, &klass))) { return NULL; } - return mirror::Array::AllocUninstrumented(self, klass, component_count); + return mirror::Array::Alloc(self, klass, component_count); } static inline mirror::Array* AllocArrayFromCodeInstrumented(uint32_t type_idx, mirror::ArtMethod* method, @@ -153,7 +153,7 @@ static inline mirror::Array* AllocArrayFromCodeInstrumented(uint32_t type_idx, m if (UNLIKELY(!CheckArrayAlloc(type_idx, method, component_count, access_check, &klass))) { return NULL; } - return mirror::Array::AllocInstrumented(self, klass, component_count); + return mirror::Array::Alloc(self, klass, component_count); } extern mirror::Array* CheckAndAllocArrayFromCode(uint32_t type_idx, mirror::ArtMethod* method, diff --git a/runtime/entrypoints/quick/quick_lock_entrypoints.cc b/runtime/entrypoints/quick/quick_lock_entrypoints.cc index 2102ab1beae..540abb3ef9b 100644 --- a/runtime/entrypoints/quick/quick_lock_entrypoints.cc +++ b/runtime/entrypoints/quick/quick_lock_entrypoints.cc @@ -29,9 +29,15 @@ extern "C" int artLockObjectFromCode(mirror::Object* obj, Thread* self, mirror:: "Null reference used for synchronization (monitor-enter)"); return -1; // Failure. } else { - obj->MonitorEnter(self); // May block - DCHECK(self->HoldsLock(obj)); - DCHECK(!self->IsExceptionPending()); + if (kIsDebugBuild) { + // GC may move the obj, need Sirt for the following DCHECKs. + SirtRef sirt_obj(self, obj); + obj->MonitorEnter(self); // May block + CHECK(self->HoldsLock(sirt_obj.get())); + CHECK(!self->IsExceptionPending()); + } else { + obj->MonitorEnter(self); // May block + } return 0; // Success. // Only possible exception is NPE and is handled before entry } diff --git a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc index 01d35499857..8ba08ee6046 100644 --- a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc +++ b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc @@ -416,10 +416,10 @@ extern "C" uint64_t artQuickProxyInvokeHandler(mirror::ArtMethod* proxy_method, // Read object references held in arguments from quick frames and place in a JNI local references, // so they don't get garbage collected. -class RememberFoGcArgumentVisitor : public QuickArgumentVisitor { +class RememberForGcArgumentVisitor : public QuickArgumentVisitor { public: - RememberFoGcArgumentVisitor(mirror::ArtMethod** sp, bool is_static, const char* shorty, - uint32_t shorty_len, ScopedObjectAccessUnchecked* soa) : + RememberForGcArgumentVisitor(mirror::ArtMethod** sp, bool is_static, const char* shorty, + uint32_t shorty_len, ScopedObjectAccessUnchecked* soa) : QuickArgumentVisitor(sp, is_static, shorty, shorty_len), soa_(soa) {} virtual void Visit() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { @@ -441,7 +441,7 @@ class RememberFoGcArgumentVisitor : public QuickArgumentVisitor { private: ScopedObjectAccessUnchecked* soa_; std::vector > references_; - DISALLOW_COPY_AND_ASSIGN(RememberFoGcArgumentVisitor); + DISALLOW_COPY_AND_ASSIGN(RememberForGcArgumentVisitor); }; // Lazily resolve a method for quick. Called by stub code. @@ -531,7 +531,7 @@ extern "C" const void* artQuickResolutionTrampoline(mirror::ArtMethod* called, uint32_t shorty_len; const char* shorty = dex_file->GetMethodShorty(dex_file->GetMethodId(dex_method_idx), &shorty_len); - RememberFoGcArgumentVisitor visitor(sp, invoke_type == kStatic, shorty, shorty_len, &soa); + RememberForGcArgumentVisitor visitor(sp, invoke_type == kStatic, shorty, shorty_len, &soa); visitor.VisitArguments(); thread->EndAssertNoThreadSuspension(old_cause); // Resolve method filling in dex cache. diff --git a/runtime/exception_test.cc b/runtime/exception_test.cc index a5f999784d3..e9a6e4fa491 100644 --- a/runtime/exception_test.cc +++ b/runtime/exception_test.cc @@ -39,7 +39,7 @@ class ExceptionTest : public CommonTest { ScopedObjectAccess soa(Thread::Current()); SirtRef class_loader(soa.Self(), soa.Decode(LoadDex("ExceptionHandle"))); - my_klass_ = class_linker_->FindClass("LExceptionHandle;", class_loader.get()); + my_klass_ = class_linker_->FindClass("LExceptionHandle;", class_loader); ASSERT_TRUE(my_klass_ != NULL); class_linker_->EnsureInitialized(my_klass_, true, true); diff --git a/runtime/gc/accounting/mod_union_table.cc b/runtime/gc/accounting/mod_union_table.cc index 7cbe94d3d2b..faa198a3700 100644 --- a/runtime/gc/accounting/mod_union_table.cc +++ b/runtime/gc/accounting/mod_union_table.cc @@ -82,7 +82,7 @@ class ModUnionUpdateObjectReferencesVisitor { if (ref != nullptr) { Object* new_ref = visitor_(ref, arg_); if (new_ref != ref) { - obj->SetFieldObject(offset, ref, false, true); + obj->SetFieldObject(offset, new_ref, true); } } } @@ -154,7 +154,7 @@ class ModUnionReferenceVisitor { // We don't have an early exit since we use the visitor pattern, an early // exit should significantly speed this up. AddToReferenceArrayVisitor visitor(mod_union_table_, references_); - collector::MarkSweep::VisitObjectReferences(obj, visitor); + collector::MarkSweep::VisitObjectReferences(obj, visitor, true); } private: ModUnionTableReferenceCache* const mod_union_table_; @@ -206,7 +206,7 @@ class ModUnionCheckReferences { Locks::heap_bitmap_lock_->AssertSharedHeld(Thread::Current()); DCHECK(obj != NULL); CheckReferenceVisitor visitor(mod_union_table_, references_); - collector::MarkSweep::VisitObjectReferences(obj, visitor); + collector::MarkSweep::VisitObjectReferences(obj, visitor, true); } private: @@ -334,7 +334,7 @@ void ModUnionTableCardCache::Dump(std::ostream& os) { for (const byte* card_addr : cleared_cards_) { auto start = reinterpret_cast(card_table->AddrFromCard(card_addr)); auto end = start + CardTable::kCardSize; - os << reinterpret_cast(start) << "-" << reinterpret_cast(end) << ","; + os << reinterpret_cast(start) << "-" << reinterpret_cast(end) << "\n"; } os << "]"; } diff --git a/runtime/gc/collector/garbage_collector.cc b/runtime/gc/collector/garbage_collector.cc index 6691cadbd4c..178910347c6 100644 --- a/runtime/gc/collector/garbage_collector.cc +++ b/runtime/gc/collector/garbage_collector.cc @@ -36,6 +36,7 @@ namespace collector { GarbageCollector::GarbageCollector(Heap* heap, const std::string& name) : heap_(heap), name_(name), + clear_soft_references_(false), verbose_(VLOG_IS_ON(heap)), duration_ns_(0), timings_(name_.c_str(), true, verbose_), @@ -60,11 +61,18 @@ void GarbageCollector::ResetCumulativeStatistics() { total_freed_bytes_ = 0; } -void GarbageCollector::Run() { +void GarbageCollector::Run(bool clear_soft_references) { ThreadList* thread_list = Runtime::Current()->GetThreadList(); uint64_t start_time = NanoTime(); pause_times_.clear(); duration_ns_ = 0; + clear_soft_references_ = clear_soft_references; + + // Reset stats. + freed_bytes_ = 0; + freed_large_object_bytes_ = 0; + freed_objects_ = 0; + freed_large_objects_ = 0; InitializePhase(); diff --git a/runtime/gc/collector/garbage_collector.h b/runtime/gc/collector/garbage_collector.h index 0f566c954be..6111c2fbf23 100644 --- a/runtime/gc/collector/garbage_collector.h +++ b/runtime/gc/collector/garbage_collector.h @@ -46,7 +46,7 @@ class GarbageCollector { virtual GcType GetGcType() const = 0; // Run the garbage collector. - void Run(); + void Run(bool clear_soft_references); Heap* GetHeap() const { return heap_; @@ -78,6 +78,34 @@ class GarbageCollector { // this is the allocation space, for full GC then we swap the zygote bitmaps too. void SwapBitmaps() EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_); + size_t GetFreedBytes() const { + return freed_bytes_; + } + + size_t GetFreedLargeObjectBytes() const { + return freed_large_object_bytes_; + } + + size_t GetFreedObjects() const { + return freed_objects_; + } + + size_t GetFreedLargeObjects() const { + return freed_large_objects_; + } + + uint64_t GetTotalPausedTimeNs() const { + return total_paused_time_ns_; + } + + uint64_t GetTotalFreedBytes() const { + return total_freed_bytes_; + } + + uint64_t GetTotalFreedObjects() const { + return total_freed_objects_; + } + protected: // The initial phase. Done without mutators paused. virtual void InitializePhase() = 0; @@ -98,6 +126,8 @@ class GarbageCollector { std::string name_; + bool clear_soft_references_; + const bool verbose_; uint64_t duration_ns_; @@ -109,6 +139,12 @@ class GarbageCollector { uint64_t total_freed_objects_; uint64_t total_freed_bytes_; + // Single GC statitstics. + AtomicInteger freed_bytes_; + AtomicInteger freed_large_object_bytes_; + AtomicInteger freed_objects_; + AtomicInteger freed_large_objects_; + CumulativeLogger cumulative_timings_; std::vector pause_times_; diff --git a/runtime/gc/collector/mark_sweep-inl.h b/runtime/gc/collector/mark_sweep-inl.h index 270c9efde97..7a515539929 100644 --- a/runtime/gc/collector/mark_sweep-inl.h +++ b/runtime/gc/collector/mark_sweep-inl.h @@ -44,8 +44,7 @@ inline void MarkSweep::ScanObjectVisit(mirror::Object* obj, const MarkVisitor& v if (klass->IsObjectArrayClass()) { VisitObjectArrayReferences(obj->AsObjectArray(), visitor); } - } else if (UNLIKELY(klass == java_lang_Class_)) { - DCHECK_EQ(klass->GetClass(), java_lang_Class_); + } else if (UNLIKELY(klass == mirror::Class::GetJavaLangClass())) { if (kCountScannedTypes) { ++class_count_; } @@ -56,7 +55,7 @@ inline void MarkSweep::ScanObjectVisit(mirror::Object* obj, const MarkVisitor& v } VisitOtherReferences(klass, obj, visitor); if (UNLIKELY(klass->IsReferenceClass())) { - DelayReferenceReferent(klass, const_cast(obj)); + DelayReferenceReferent(klass, obj); } } } @@ -68,11 +67,10 @@ inline void MarkSweep::VisitObjectReferences(mirror::Object* obj, const Visitor& Locks::mutator_lock_) { DCHECK(obj != NULL); DCHECK(obj->GetClass() != NULL); - mirror::Class* klass = obj->GetClass(); DCHECK(klass != NULL); if (visit_class) { - visitor(obj, klass, MemberOffset(0), false); + visitor(obj, klass, mirror::Object::ClassOffset(), false); } if (klass == mirror::Class::GetJavaLangClass()) { DCHECK_EQ(klass->GetClass(), mirror::Class::GetJavaLangClass()); @@ -90,8 +88,7 @@ inline void MarkSweep::VisitObjectReferences(mirror::Object* obj, const Visitor& } template -inline void MarkSweep::VisitInstanceFieldsReferences(mirror::Class* klass, - mirror::Object* obj, +inline void MarkSweep::VisitInstanceFieldsReferences(mirror::Class* klass, mirror::Object* obj, const Visitor& visitor) SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_, Locks::mutator_lock_) { DCHECK(obj != NULL); @@ -119,11 +116,6 @@ inline void MarkSweep::VisitFieldsReferences(mirror::Object* obj, uint32_t ref_o bool is_static, const Visitor& visitor) { if (LIKELY(ref_offsets != CLASS_WALK_SUPER)) { // Found a reference offset bitmap. Mark the specified offsets. -#ifndef MOVING_COLLECTOR - // Clear the class bit since we mark the class as part of marking the classlinker roots. - DCHECK_EQ(mirror::Object::ClassOffset().Uint32Value(), 0U); - ref_offsets &= (1U << (sizeof(ref_offsets) * 8 - 1)) - 1; -#endif while (ref_offsets != 0) { size_t right_shift = CLZ(ref_offsets); MemberOffset field_offset = CLASS_OFFSET_FROM_CLZ(right_shift); diff --git a/runtime/gc/collector/mark_sweep.cc b/runtime/gc/collector/mark_sweep.cc index 2c69c77187f..11e911cf46a 100644 --- a/runtime/gc/collector/mark_sweep.cc +++ b/runtime/gc/collector/mark_sweep.cc @@ -93,6 +93,8 @@ void MarkSweep::ImmuneSpace(space::ContinuousSpace* space) { } // Add the space to the immune region. + // TODO: Use space limits instead of current end_ since the end_ can be changed by dlmalloc + // callbacks. if (immune_begin_ == NULL) { DCHECK(immune_end_ == NULL); SetImmuneRange(reinterpret_cast(space->Begin()), @@ -108,14 +110,14 @@ void MarkSweep::ImmuneSpace(space::ContinuousSpace* space) { } // If previous space was immune, then extend the immune region. Relies on continuous spaces // being sorted by Heap::AddContinuousSpace. - if (prev_space != NULL && IsImmuneSpace(prev_space)) { + if (prev_space != nullptr && IsImmuneSpace(prev_space)) { immune_begin_ = std::min(reinterpret_cast(space->Begin()), immune_begin_); immune_end_ = std::max(reinterpret_cast(space->End()), immune_end_); } } } -bool MarkSweep::IsImmuneSpace(const space::ContinuousSpace* space) { +bool MarkSweep::IsImmuneSpace(const space::ContinuousSpace* space) const { return immune_begin_ <= reinterpret_cast(space->Begin()) && immune_end_ >= reinterpret_cast(space->End()); @@ -135,10 +137,9 @@ void MarkSweep::BindBitmaps() { MarkSweep::MarkSweep(Heap* heap, bool is_concurrent, const std::string& name_prefix) : GarbageCollector(heap, - name_prefix + (name_prefix.empty() ? "" : " ") + + name_prefix + (is_concurrent ? "concurrent mark sweep": "mark sweep")), current_mark_bitmap_(NULL), - java_lang_Class_(NULL), mark_stack_(NULL), immune_begin_(NULL), immune_end_(NULL), @@ -150,8 +151,7 @@ MarkSweep::MarkSweep(Heap* heap, bool is_concurrent, const std::string& name_pre gc_barrier_(new Barrier(0)), large_object_lock_("mark sweep large object lock", kMarkSweepLargeObjectLock), mark_stack_lock_("mark sweep mark stack lock", kMarkSweepMarkStackLock), - is_concurrent_(is_concurrent), - clear_soft_references_(false) { + is_concurrent_(is_concurrent) { } void MarkSweep::InitializePhase() { @@ -165,10 +165,6 @@ void MarkSweep::InitializePhase() { finalizer_reference_list_ = nullptr; phantom_reference_list_ = nullptr; cleared_reference_list_ = nullptr; - freed_bytes_ = 0; - freed_large_object_bytes_ = 0; - freed_objects_ = 0; - freed_large_objects_ = 0; class_count_ = 0; array_count_ = 0; other_count_ = 0; @@ -179,8 +175,6 @@ void MarkSweep::InitializePhase() { work_chunks_created_ = 0; work_chunks_deleted_ = 0; reference_count_ = 0; - java_lang_Class_ = Class::GetJavaLangClass(); - CHECK(java_lang_Class_ != nullptr); FindDefaultMarkBitmap(); @@ -294,8 +288,7 @@ void MarkSweep::MarkReachableObjects() { // knowing that new allocations won't be marked as live. timings_.StartSplit("MarkStackAsLive"); accounting::ObjectStack* live_stack = heap_->GetLiveStack(); - heap_->MarkAllocStack(heap_->alloc_space_->GetLiveBitmap(), - heap_->large_object_space_->GetLiveObjects(), live_stack); + heap_->MarkAllocStackAsLive(live_stack); live_stack->Reset(); timings_.EndSplit(); // Recursively mark all the non-image bits set in the mark bitmap. @@ -371,8 +364,10 @@ void MarkSweep::SetImmuneRange(Object* begin, Object* end) { void MarkSweep::FindDefaultMarkBitmap() { base::TimingLogger::ScopedSplit split("FindDefaultMarkBitmap", &timings_); for (const auto& space : GetHeap()->GetContinuousSpaces()) { - if (space->GetGcRetentionPolicy() == space::kGcRetentionPolicyAlwaysCollect) { - current_mark_bitmap_ = space->GetMarkBitmap(); + accounting::SpaceBitmap* bitmap = space->GetMarkBitmap(); + if (bitmap != nullptr && + space->GetGcRetentionPolicy() == space::kGcRetentionPolicyAlwaysCollect) { + current_mark_bitmap_ = bitmap; CHECK(current_mark_bitmap_ != NULL); return; } @@ -613,10 +608,8 @@ void MarkSweep::BindLiveToMarkBitmap(space::ContinuousSpace* space) { CHECK(space->IsDlMallocSpace()); space::DlMallocSpace* alloc_space = space->AsDlMallocSpace(); accounting::SpaceBitmap* live_bitmap = space->GetLiveBitmap(); - accounting::SpaceBitmap* mark_bitmap = alloc_space->mark_bitmap_.release(); + accounting::SpaceBitmap* mark_bitmap = alloc_space->BindLiveToMarkBitmap(); GetHeap()->GetMarkBitmap()->ReplaceBitmap(mark_bitmap, live_bitmap); - alloc_space->temp_bitmap_.reset(mark_bitmap); - alloc_space->mark_bitmap_.reset(live_bitmap); } class ScanObjectVisitor { @@ -625,7 +618,7 @@ class ScanObjectVisitor { : mark_sweep_(mark_sweep) {} // TODO: Fixme when anotatalysis works with visitors. - void operator()(const Object* obj) const ALWAYS_INLINE NO_THREAD_SAFETY_ANALYSIS { + void operator()(Object* obj) const ALWAYS_INLINE NO_THREAD_SAFETY_ANALYSIS { if (kCheckLocks) { Locks::mutator_lock_->AssertSharedHeld(Thread::Current()); Locks::heap_bitmap_lock_->AssertExclusiveHeld(Thread::Current()); @@ -814,6 +807,9 @@ void MarkSweep::ScanGrayObjects(bool paused, byte minimum_age) { const size_t mark_stack_delta = std::min(CardScanTask::kMaxSize / 2, mark_stack_size / mark_stack_tasks + 1); for (const auto& space : GetHeap()->GetContinuousSpaces()) { + if (space->GetMarkBitmap() == nullptr) { + continue; + } byte* card_begin = space->Begin(); byte* card_end = space->End(); // Align up the end address. For example, the image space's end @@ -856,24 +852,26 @@ void MarkSweep::ScanGrayObjects(bool paused, byte minimum_age) { timings_.EndSplit(); } else { for (const auto& space : GetHeap()->GetContinuousSpaces()) { - // Image spaces are handled properly since live == marked for them. - switch (space->GetGcRetentionPolicy()) { - case space::kGcRetentionPolicyNeverCollect: - timings_.StartSplit(paused ? "(Paused)ScanGrayImageSpaceObjects" : - "ScanGrayImageSpaceObjects"); - break; - case space::kGcRetentionPolicyFullCollect: - timings_.StartSplit(paused ? "(Paused)ScanGrayZygoteSpaceObjects" : - "ScanGrayZygoteSpaceObjects"); - break; - case space::kGcRetentionPolicyAlwaysCollect: - timings_.StartSplit(paused ? "(Paused)ScanGrayAllocSpaceObjects" : - "ScanGrayAllocSpaceObjects"); - break; - } - ScanObjectVisitor visitor(this); - card_table->Scan(space->GetMarkBitmap(), space->Begin(), space->End(), visitor, minimum_age); - timings_.EndSplit(); + if (space->GetMarkBitmap() != nullptr) { + // Image spaces are handled properly since live == marked for them. + switch (space->GetGcRetentionPolicy()) { + case space::kGcRetentionPolicyNeverCollect: + timings_.StartSplit(paused ? "(Paused)ScanGrayImageSpaceObjects" : + "ScanGrayImageSpaceObjects"); + break; + case space::kGcRetentionPolicyFullCollect: + timings_.StartSplit(paused ? "(Paused)ScanGrayZygoteSpaceObjects" : + "ScanGrayZygoteSpaceObjects"); + break; + case space::kGcRetentionPolicyAlwaysCollect: + timings_.StartSplit(paused ? "(Paused)ScanGrayAllocSpaceObjects" : + "ScanGrayAllocSpaceObjects"); + break; + } + ScanObjectVisitor visitor(this); + card_table->Scan(space->GetMarkBitmap(), space->Begin(), space->End(), visitor, minimum_age); + timings_.EndSplit(); + } } } } @@ -954,9 +952,8 @@ void MarkSweep::RecursiveMark() { if ((space->GetGcRetentionPolicy() == space::kGcRetentionPolicyAlwaysCollect) || (!partial && space->GetGcRetentionPolicy() == space::kGcRetentionPolicyFullCollect)) { current_mark_bitmap_ = space->GetMarkBitmap(); - if (current_mark_bitmap_ == NULL) { - GetHeap()->DumpSpaces(); - LOG(FATAL) << "invalid bitmap"; + if (current_mark_bitmap_ == nullptr) { + continue; } if (parallel) { // We will use the mark stack the future. @@ -1121,7 +1118,7 @@ void MarkSweep::ZygoteSweepCallback(size_t num_ptrs, Object** ptrs, void* arg) { } void MarkSweep::SweepArray(accounting::ObjectStack* allocations, bool swap_bitmaps) { - space::DlMallocSpace* space = heap_->GetAllocSpace(); + space::DlMallocSpace* space = heap_->GetNonMovingSpace(); timings_.StartSplit("SweepArray"); // Newly allocated objects MUST be in the alloc space and those are the only objects which we are // going to free. @@ -1207,8 +1204,11 @@ void MarkSweep::Sweep(bool swap_bitmaps) { scc.mark_sweep = this; scc.self = Thread::Current(); for (const auto& space : GetHeap()->GetContinuousSpaces()) { + if (!space->IsDlMallocSpace()) { + continue; + } // We always sweep always collect spaces. - bool sweep_space = (space->GetGcRetentionPolicy() == space::kGcRetentionPolicyAlwaysCollect); + bool sweep_space = space->GetGcRetentionPolicy() == space::kGcRetentionPolicyAlwaysCollect; if (!partial && !sweep_space) { // We sweep full collect spaces when the GC isn't a partial GC (ie its full). sweep_space = (space->GetGcRetentionPolicy() == space::kGcRetentionPolicyFullCollect); @@ -1370,9 +1370,9 @@ class MarkObjectVisitor { // Scans an object reference. Determines the type of the reference // and dispatches to a specialized scanning routine. -void MarkSweep::ScanObject(const Object* obj) { +void MarkSweep::ScanObject(Object* obj) { MarkObjectVisitor visitor(this); - ScanObjectVisit(const_cast(obj), visitor); + ScanObjectVisit(obj, visitor); } void MarkSweep::ProcessMarkStackParallel(size_t thread_count) { @@ -1406,12 +1406,12 @@ void MarkSweep::ProcessMarkStack(bool paused) { } else { // TODO: Tune this. static const size_t kFifoSize = 4; - BoundedFifoPowerOfTwo prefetch_fifo; + BoundedFifoPowerOfTwo prefetch_fifo; for (;;) { - const Object* obj = NULL; + Object* obj = NULL; if (kUseMarkStackPrefetch) { while (!mark_stack_->IsEmpty() && prefetch_fifo.size() < kFifoSize) { - const Object* obj = mark_stack_->PopBack(); + Object* obj = mark_stack_->PopBack(); DCHECK(obj != NULL); __builtin_prefetch(obj); prefetch_fifo.push_back(obj); @@ -1603,9 +1603,6 @@ void MarkSweep::FinishPhase() { timings_.NewSplit("PostGcVerification"); heap->PostGcVerification(this); - timings_.NewSplit("GrowForUtilization"); - heap->GrowForUtilization(GetGcType(), GetDurationNs()); - timings_.NewSplit("RequestHeapTrim"); heap->RequestHeapTrim(); @@ -1651,8 +1648,10 @@ void MarkSweep::FinishPhase() { // Clear all of the spaces' mark bitmaps. for (const auto& space : GetHeap()->GetContinuousSpaces()) { - if (space->GetGcRetentionPolicy() != space::kGcRetentionPolicyNeverCollect) { - space->GetMarkBitmap()->Clear(); + accounting::SpaceBitmap* bitmap = space->GetMarkBitmap(); + if (bitmap != nullptr && + space->GetGcRetentionPolicy() != space::kGcRetentionPolicyNeverCollect) { + bitmap->Clear(); } } mark_stack_->Reset(); diff --git a/runtime/gc/collector/mark_sweep.h b/runtime/gc/collector/mark_sweep.h index 3bc014aa1e7..cc5841244d8 100644 --- a/runtime/gc/collector/mark_sweep.h +++ b/runtime/gc/collector/mark_sweep.h @@ -114,7 +114,7 @@ class MarkSweep : public GarbageCollector { EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - bool IsImmuneSpace(const space::ContinuousSpace* space) + bool IsImmuneSpace(const space::ContinuousSpace* space) const; SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); // Bind the live bits to the mark bits of bitmaps for spaces that are never collected, ie @@ -140,6 +140,7 @@ class MarkSweep : public GarbageCollector { void ProcessReferences(Thread* self) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + // Update and mark references from immune spaces. virtual void UpdateAndMarkModUnion() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); @@ -158,7 +159,7 @@ class MarkSweep : public GarbageCollector { } // Blackens an object. - void ScanObject(const mirror::Object* obj) + void ScanObject(mirror::Object* obj) EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); @@ -167,38 +168,6 @@ class MarkSweep : public GarbageCollector { void ScanObjectVisit(mirror::Object* obj, const MarkVisitor& visitor) NO_THREAD_SAFETY_ANALYSIS; - size_t GetFreedBytes() const { - return freed_bytes_; - } - - size_t GetFreedLargeObjectBytes() const { - return freed_large_object_bytes_; - } - - size_t GetFreedObjects() const { - return freed_objects_; - } - - size_t GetFreedLargeObjects() const { - return freed_large_objects_; - } - - uint64_t GetTotalTimeNs() const { - return total_time_ns_; - } - - uint64_t GetTotalPausedTimeNs() const { - return total_paused_time_ns_; - } - - uint64_t GetTotalFreedObjects() const { - return total_freed_objects_; - } - - uint64_t GetTotalFreedBytes() const { - return total_freed_bytes_; - } - // Everything inside the immune range is assumed to be marked. void SetImmuneRange(mirror::Object* begin, mirror::Object* end); @@ -216,8 +185,7 @@ class MarkSweep : public GarbageCollector { SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_); template - static void VisitObjectReferences(mirror::Object* obj, const Visitor& visitor, - bool visit_class = false) + static void VisitObjectReferences(mirror::Object* obj, const Visitor& visitor, bool visit_class) SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_, Locks::mutator_lock_); @@ -395,9 +363,6 @@ class MarkSweep : public GarbageCollector { // object. accounting::SpaceBitmap* current_mark_bitmap_; - // Cache java.lang.Class for optimization. - mirror::Class* java_lang_Class_; - accounting::ObjectStack* mark_stack_; // Immune range, every object inside the immune range is assumed to be marked. @@ -412,14 +377,6 @@ class MarkSweep : public GarbageCollector { // Parallel finger. AtomicInteger atomic_finger_; - // Number of non large object bytes freed in this collection. - AtomicInteger freed_bytes_; - // Number of large object bytes freed. - AtomicInteger freed_large_object_bytes_; - // Number of objects freed in this collection. - AtomicInteger freed_objects_; - // Number of freed large objects. - AtomicInteger freed_large_objects_; // Number of classes scanned, if kCountScannedTypes. AtomicInteger class_count_; // Number of arrays scanned, if kCountScannedTypes. @@ -443,8 +400,6 @@ class MarkSweep : public GarbageCollector { const bool is_concurrent_; - bool clear_soft_references_; - private: friend class AddIfReachesAllocSpaceVisitor; // Used by mod-union table. friend class CardScanTask; diff --git a/runtime/gc/collector/partial_mark_sweep.cc b/runtime/gc/collector/partial_mark_sweep.cc index 29367ce0bff..8ec28f31744 100644 --- a/runtime/gc/collector/partial_mark_sweep.cc +++ b/runtime/gc/collector/partial_mark_sweep.cc @@ -26,7 +26,7 @@ namespace gc { namespace collector { PartialMarkSweep::PartialMarkSweep(Heap* heap, bool is_concurrent, const std::string& name_prefix) - : MarkSweep(heap, is_concurrent, name_prefix + (name_prefix.empty() ? "" : " ") + "partial") { + : MarkSweep(heap, is_concurrent, name_prefix.empty() ? "partial " : name_prefix) { cumulative_timings_.SetName(GetName()); } diff --git a/runtime/gc/collector/semi_space-inl.h b/runtime/gc/collector/semi_space-inl.h new file mode 100644 index 00000000000..3b8f7c395d9 --- /dev/null +++ b/runtime/gc/collector/semi_space-inl.h @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ART_RUNTIME_GC_COLLECTOR_SEMI_SPACE_INL_H_ +#define ART_RUNTIME_GC_COLLECTOR_SEMI_SPACE_INL_H_ + +namespace art { +namespace gc { +namespace collector { + +inline mirror::Object* SemiSpace::GetForwardingAddressInFromSpace(mirror::Object* obj) const { + DCHECK(from_space_->HasAddress(obj)); + LockWord lock_word = obj->GetLockWord(); + if (lock_word.GetState() != LockWord::kForwardingAddress) { + return nullptr; + } + return reinterpret_cast(lock_word.ForwardingAddress()); +} + +} // namespace collector +} // namespace gc +} // namespace art + +#endif // ART_RUNTIME_GC_COLLECTOR_SEMI_SPACE_INL_H_ diff --git a/runtime/gc/collector/semi_space.cc b/runtime/gc/collector/semi_space.cc new file mode 100644 index 00000000000..d833631da91 --- /dev/null +++ b/runtime/gc/collector/semi_space.cc @@ -0,0 +1,799 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "semi_space.h" + +#include +#include +#include +#include + +#include "base/logging.h" +#include "base/macros.h" +#include "base/mutex-inl.h" +#include "base/timing_logger.h" +#include "gc/accounting/heap_bitmap.h" +#include "gc/accounting/mod_union_table.h" +#include "gc/accounting/space_bitmap-inl.h" +#include "gc/heap.h" +#include "gc/space/bump_pointer_space.h" +#include "gc/space/bump_pointer_space-inl.h" +#include "gc/space/image_space.h" +#include "gc/space/large_object_space.h" +#include "gc/space/space-inl.h" +#include "indirect_reference_table.h" +#include "intern_table.h" +#include "jni_internal.h" +#include "mark_sweep-inl.h" +#include "monitor.h" +#include "mirror/art_field.h" +#include "mirror/art_field-inl.h" +#include "mirror/class-inl.h" +#include "mirror/class_loader.h" +#include "mirror/dex_cache.h" +#include "mirror/object-inl.h" +#include "mirror/object_array.h" +#include "mirror/object_array-inl.h" +#include "runtime.h" +#include "semi_space-inl.h" +#include "thread-inl.h" +#include "thread_list.h" +#include "verifier/method_verifier.h" + +using ::art::mirror::Class; +using ::art::mirror::Object; + +namespace art { +namespace gc { +namespace collector { + +static constexpr bool kProtectFromSpace = true; +static constexpr bool kResetFromSpace = true; + +// TODO: Unduplicate logic. +void SemiSpace::ImmuneSpace(space::ContinuousSpace* space) { + // Bind live to mark bitmap if necessary. + if (space->GetLiveBitmap() != space->GetMarkBitmap()) { + BindLiveToMarkBitmap(space); + } + // Add the space to the immune region. + if (immune_begin_ == nullptr) { + DCHECK(immune_end_ == nullptr); + immune_begin_ = reinterpret_cast(space->Begin()); + immune_end_ = reinterpret_cast(space->End()); + } else { + const space::ContinuousSpace* prev_space = nullptr; + // Find out if the previous space is immune. + for (space::ContinuousSpace* cur_space : GetHeap()->GetContinuousSpaces()) { + if (cur_space == space) { + break; + } + prev_space = cur_space; + } + // If previous space was immune, then extend the immune region. Relies on continuous spaces + // being sorted by Heap::AddContinuousSpace. + if (prev_space != nullptr && IsImmuneSpace(prev_space)) { + immune_begin_ = std::min(reinterpret_cast(space->Begin()), immune_begin_); + immune_end_ = std::max(reinterpret_cast(space->End()), immune_end_); + } + } +} + +void SemiSpace::BindBitmaps() { + timings_.StartSplit("BindBitmaps"); + WriterMutexLock mu(Thread::Current(), *Locks::heap_bitmap_lock_); + // Mark all of the spaces we never collect as immune. + for (const auto& space : GetHeap()->GetContinuousSpaces()) { + if (space->GetGcRetentionPolicy() == space::kGcRetentionPolicyNeverCollect + || space->GetGcRetentionPolicy() == space::kGcRetentionPolicyFullCollect) { + ImmuneSpace(space); + } + } + timings_.EndSplit(); +} + +SemiSpace::SemiSpace(Heap* heap, const std::string& name_prefix) + : GarbageCollector(heap, + name_prefix + (name_prefix.empty() ? "" : " ") + "marksweep + semispace"), + mark_stack_(nullptr), + immune_begin_(nullptr), + immune_end_(nullptr), + to_space_(nullptr), + from_space_(nullptr), + soft_reference_list_(nullptr), + weak_reference_list_(nullptr), + finalizer_reference_list_(nullptr), + phantom_reference_list_(nullptr), + cleared_reference_list_(nullptr), + self_(nullptr) { +} + +void SemiSpace::InitializePhase() { + timings_.Reset(); + base::TimingLogger::ScopedSplit split("InitializePhase", &timings_); + mark_stack_ = heap_->mark_stack_.get(); + DCHECK(mark_stack_ != nullptr); + immune_begin_ = nullptr; + immune_end_ = nullptr; + soft_reference_list_ = nullptr; + weak_reference_list_ = nullptr; + finalizer_reference_list_ = nullptr; + phantom_reference_list_ = nullptr; + cleared_reference_list_ = nullptr; + self_ = Thread::Current(); + // Do any pre GC verification. + timings_.NewSplit("PreGcVerification"); + heap_->PreGcVerification(this); +} + +void SemiSpace::ProcessReferences(Thread* self) { + base::TimingLogger::ScopedSplit split("ProcessReferences", &timings_); + WriterMutexLock mu(self, *Locks::heap_bitmap_lock_); + ProcessReferences(&soft_reference_list_, clear_soft_references_, &weak_reference_list_, + &finalizer_reference_list_, &phantom_reference_list_); +} + +void SemiSpace::MarkingPhase() { + Thread* self = Thread::Current(); + Locks::mutator_lock_->AssertExclusiveHeld(self); + base::TimingLogger::ScopedSplit split("MarkingPhase", &timings_); + // Need to do this with mutators paused so that somebody doesn't accidentally allocate into the + // wrong space. + heap_->SwapSemiSpaces(); + // Assume the cleared space is already empty. + BindBitmaps(); + // Process dirty cards and add dirty cards to mod-union tables. + heap_->ProcessCards(timings_); + // Need to do this before the checkpoint since we don't want any threads to add references to + // the live stack during the recursive mark. + timings_.NewSplit("SwapStacks"); + heap_->SwapStacks(); + WriterMutexLock mu(self, *Locks::heap_bitmap_lock_); + MarkRoots(); + // Mark roots of immune spaces. + UpdateAndMarkModUnion(); + // Recursively mark remaining objects. + MarkReachableObjects(); +} + +bool SemiSpace::IsImmuneSpace(const space::ContinuousSpace* space) const { + return + immune_begin_ <= reinterpret_cast(space->Begin()) && + immune_end_ >= reinterpret_cast(space->End()); +} + +void SemiSpace::UpdateAndMarkModUnion() { + for (auto& space : heap_->GetContinuousSpaces()) { + // If the space is immune then we need to mark the references to other spaces. + if (IsImmuneSpace(space)) { + accounting::ModUnionTable* table = heap_->FindModUnionTableFromSpace(space); + CHECK(table != nullptr); + // TODO: Improve naming. + base::TimingLogger::ScopedSplit split( + space->IsZygoteSpace() ? "UpdateAndMarkZygoteModUnionTable" : + "UpdateAndMarkImageModUnionTable", + &timings_); + table->UpdateAndMarkReferences(MarkRootCallback, this); + } + } +} + +void SemiSpace::MarkReachableObjects() { + timings_.StartSplit("MarkStackAsLive"); + accounting::ObjectStack* live_stack = heap_->GetLiveStack(); + heap_->MarkAllocStackAsLive(live_stack); + live_stack->Reset(); + timings_.EndSplit(); + // Recursively process the mark stack. + ProcessMarkStack(true); +} + +void SemiSpace::ReclaimPhase() { + base::TimingLogger::ScopedSplit split("ReclaimPhase", &timings_); + Thread* self = Thread::Current(); + ProcessReferences(self); + { + ReaderMutexLock mu(self, *Locks::heap_bitmap_lock_); + SweepSystemWeaks(); + } + // Record freed memory. + int from_bytes = from_space_->GetBytesAllocated(); + int to_bytes = to_space_->GetBytesAllocated(); + int from_objects = from_space_->GetObjectsAllocated(); + int to_objects = to_space_->GetObjectsAllocated(); + int freed_bytes = from_bytes - to_bytes; + int freed_objects = from_objects - to_objects; + CHECK_GE(freed_bytes, 0); + freed_bytes_.fetch_add(freed_bytes); + freed_objects_.fetch_add(freed_objects); + heap_->RecordFree(static_cast(freed_objects), static_cast(freed_bytes)); + + timings_.StartSplit("PreSweepingGcVerification"); + heap_->PreSweepingGcVerification(this); + timings_.EndSplit(); + + { + WriterMutexLock mu(self, *Locks::heap_bitmap_lock_); + // Reclaim unmarked objects. + Sweep(false); + // Swap the live and mark bitmaps for each space which we modified space. This is an + // optimization that enables us to not clear live bits inside of the sweep. Only swaps unbound + // bitmaps. + timings_.StartSplit("SwapBitmaps"); + SwapBitmaps(); + timings_.EndSplit(); + // Unbind the live and mark bitmaps. + UnBindBitmaps(); + } + // Release the memory used by the from space. + if (kResetFromSpace) { + // Clearing from space. + from_space_->Clear(); + } + // Protect the from space. + VLOG(heap) + << "mprotect region " << reinterpret_cast(from_space_->Begin()) << " - " + << reinterpret_cast(from_space_->Limit()); + if (kProtectFromSpace) { + mprotect(from_space_->Begin(), from_space_->Capacity(), PROT_NONE); + } else { + mprotect(from_space_->Begin(), from_space_->Capacity(), PROT_READ); + } +} + +void SemiSpace::ResizeMarkStack(size_t new_size) { + std::vector temp(mark_stack_->Begin(), mark_stack_->End()); + CHECK_LE(mark_stack_->Size(), new_size); + mark_stack_->Resize(new_size); + for (const auto& obj : temp) { + mark_stack_->PushBack(obj); + } +} + +inline void SemiSpace::MarkStackPush(Object* obj) { + if (UNLIKELY(mark_stack_->Size() >= mark_stack_->Capacity())) { + ResizeMarkStack(mark_stack_->Capacity() * 2); + } + // The object must be pushed on to the mark stack. + mark_stack_->PushBack(obj); +} + +// Rare case, probably not worth inlining since it will increase instruction cache miss rate. +bool SemiSpace::MarkLargeObject(const Object* obj) { + // TODO: support >1 discontinuous space. + space::LargeObjectSpace* large_object_space = GetHeap()->GetLargeObjectsSpace(); + accounting::SpaceSetMap* large_objects = large_object_space->GetMarkObjects(); + if (UNLIKELY(!large_objects->Test(obj))) { + large_objects->Set(obj); + return true; + } + return false; +} + +// Used to mark and copy objects. Any newly-marked objects who are in the from space get moved to +// the to-space and have their forward address updated. Objects which have been newly marked are +// pushed on the mark stack. +Object* SemiSpace::MarkObject(Object* obj) { + Object* ret = obj; + if (obj != nullptr && !IsImmune(obj)) { + if (from_space_->HasAddress(obj)) { + mirror::Object* forward_address = GetForwardingAddressInFromSpace(obj); + // If the object has already been moved, return the new forward address. + if (!to_space_->HasAddress(forward_address)) { + // Otherwise, we need to move the object and add it to the markstack for processing. + size_t object_size = obj->SizeOf(); + size_t dummy = 0; + forward_address = to_space_->Alloc(self_, object_size, &dummy); + // Copy over the object and add it to the mark stack since we still need to update it's + // references. + memcpy(reinterpret_cast(forward_address), obj, object_size); + // Make sure to only update the forwarding address AFTER you copy the object so that the + // monitor word doesn't get stomped over. + COMPILE_ASSERT(sizeof(uint32_t) == sizeof(mirror::Object*), + monitor_size_must_be_same_as_object); + obj->SetLockWord(LockWord::FromForwardingAddress(reinterpret_cast(forward_address))); + MarkStackPush(forward_address); + } + ret = forward_address; + // TODO: Do we need this if in the else statement? + } else { + accounting::SpaceBitmap* object_bitmap = heap_->GetMarkBitmap()->GetContinuousSpaceBitmap(obj); + if (LIKELY(object_bitmap != nullptr)) { + // This object was not previously marked. + if (!object_bitmap->Test(obj)) { + object_bitmap->Set(obj); + MarkStackPush(obj); + } + } else { + DCHECK(!to_space_->HasAddress(obj)) << "Marking object in to_space_"; + if (MarkLargeObject(obj)) { + MarkStackPush(obj); + } + } + } + } + return ret; +} + +Object* SemiSpace::MarkRootCallback(Object* root, void* arg) { + DCHECK(root != nullptr); + DCHECK(arg != nullptr); + return reinterpret_cast(arg)->MarkObject(root); +} + +// Marks all objects in the root set. +void SemiSpace::MarkRoots() { + timings_.StartSplit("MarkRoots"); + // TODO: Visit up image roots as well? + Runtime::Current()->VisitRoots(MarkRootCallback, this, false, true); + timings_.EndSplit(); +} + +void SemiSpace::BindLiveToMarkBitmap(space::ContinuousSpace* space) { + CHECK(space->IsDlMallocSpace()); + space::DlMallocSpace* alloc_space = space->AsDlMallocSpace(); + accounting::SpaceBitmap* live_bitmap = space->GetLiveBitmap(); + accounting::SpaceBitmap* mark_bitmap = alloc_space->BindLiveToMarkBitmap(); + GetHeap()->GetMarkBitmap()->ReplaceBitmap(mark_bitmap, live_bitmap); +} + +mirror::Object* SemiSpace::GetForwardingAddress(mirror::Object* obj) { + if (from_space_->HasAddress(obj)) { + LOG(FATAL) << "Shouldn't happen!"; + return GetForwardingAddressInFromSpace(obj); + } + return obj; +} + +mirror::Object* SemiSpace::SystemWeakIsMarkedCallback(Object* object, void* arg) { + return reinterpret_cast(arg)->GetMarkedForwardAddress(object); +} + +void SemiSpace::SweepSystemWeaks() { + timings_.StartSplit("SweepSystemWeaks"); + Runtime::Current()->SweepSystemWeaks(SystemWeakIsMarkedCallback, this); + timings_.EndSplit(); +} + +struct SweepCallbackContext { + SemiSpace* mark_sweep; + space::AllocSpace* space; + Thread* self; +}; + +void SemiSpace::SweepCallback(size_t num_ptrs, Object** ptrs, void* arg) { + SweepCallbackContext* context = static_cast(arg); + SemiSpace* gc = context->mark_sweep; + Heap* heap = gc->GetHeap(); + space::AllocSpace* space = context->space; + Thread* self = context->self; + Locks::heap_bitmap_lock_->AssertExclusiveHeld(self); + size_t freed_bytes = space->FreeList(self, num_ptrs, ptrs); + heap->RecordFree(num_ptrs, freed_bytes); + gc->freed_objects_.fetch_add(num_ptrs); + gc->freed_bytes_.fetch_add(freed_bytes); +} + +void SemiSpace::ZygoteSweepCallback(size_t num_ptrs, Object** ptrs, void* arg) { + SweepCallbackContext* context = static_cast(arg); + Locks::heap_bitmap_lock_->AssertExclusiveHeld(context->self); + Heap* heap = context->mark_sweep->GetHeap(); + // We don't free any actual memory to avoid dirtying the shared zygote pages. + for (size_t i = 0; i < num_ptrs; ++i) { + Object* obj = static_cast(ptrs[i]); + heap->GetLiveBitmap()->Clear(obj); + heap->GetCardTable()->MarkCard(obj); + } +} + +void SemiSpace::Sweep(bool swap_bitmaps) { + DCHECK(mark_stack_->IsEmpty()); + base::TimingLogger::ScopedSplit("Sweep", &timings_); + + const bool partial = (GetGcType() == kGcTypePartial); + SweepCallbackContext scc; + scc.mark_sweep = this; + scc.self = Thread::Current(); + for (const auto& space : GetHeap()->GetContinuousSpaces()) { + if (!space->IsDlMallocSpace()) { + continue; + } + // We always sweep always collect spaces. + bool sweep_space = (space->GetGcRetentionPolicy() == space::kGcRetentionPolicyAlwaysCollect); + if (!partial && !sweep_space) { + // We sweep full collect spaces when the GC isn't a partial GC (ie its full). + sweep_space = (space->GetGcRetentionPolicy() == space::kGcRetentionPolicyFullCollect); + } + if (sweep_space && space->IsDlMallocSpace()) { + uintptr_t begin = reinterpret_cast(space->Begin()); + uintptr_t end = reinterpret_cast(space->End()); + scc.space = space->AsDlMallocSpace(); + accounting::SpaceBitmap* live_bitmap = space->GetLiveBitmap(); + accounting::SpaceBitmap* mark_bitmap = space->GetMarkBitmap(); + if (swap_bitmaps) { + std::swap(live_bitmap, mark_bitmap); + } + if (!space->IsZygoteSpace()) { + base::TimingLogger::ScopedSplit split("SweepAllocSpace", &timings_); + // Bitmaps are pre-swapped for optimization which enables sweeping with the heap unlocked. + accounting::SpaceBitmap::SweepWalk(*live_bitmap, *mark_bitmap, begin, end, + &SweepCallback, reinterpret_cast(&scc)); + } else { + base::TimingLogger::ScopedSplit split("SweepZygote", &timings_); + // Zygote sweep takes care of dirtying cards and clearing live bits, does not free actual + // memory. + accounting::SpaceBitmap::SweepWalk(*live_bitmap, *mark_bitmap, begin, end, + &ZygoteSweepCallback, reinterpret_cast(&scc)); + } + } + } + + SweepLargeObjects(swap_bitmaps); +} + +void SemiSpace::SweepLargeObjects(bool swap_bitmaps) { + base::TimingLogger::ScopedSplit("SweepLargeObjects", &timings_); + // Sweep large objects + space::LargeObjectSpace* large_object_space = GetHeap()->GetLargeObjectsSpace(); + accounting::SpaceSetMap* large_live_objects = large_object_space->GetLiveObjects(); + accounting::SpaceSetMap* large_mark_objects = large_object_space->GetMarkObjects(); + if (swap_bitmaps) { + std::swap(large_live_objects, large_mark_objects); + } + // O(n*log(n)) but hopefully there are not too many large objects. + size_t freed_objects = 0; + size_t freed_bytes = 0; + Thread* self = Thread::Current(); + for (const Object* obj : large_live_objects->GetObjects()) { + if (!large_mark_objects->Test(obj)) { + freed_bytes += large_object_space->Free(self, const_cast(obj)); + ++freed_objects; + } + } + freed_large_objects_.fetch_add(freed_objects); + freed_large_object_bytes_.fetch_add(freed_bytes); + GetHeap()->RecordFree(freed_objects, freed_bytes); +} + +// Process the "referent" field in a java.lang.ref.Reference. If the referent has not yet been +// marked, put it on the appropriate list in the heap for later processing. +void SemiSpace::DelayReferenceReferent(mirror::Class* klass, Object* obj) { + DCHECK(klass != nullptr); + DCHECK(klass->IsReferenceClass()); + DCHECK(obj != nullptr); + Object* referent = heap_->GetReferenceReferent(obj); + if (referent != nullptr) { + Object* forward_address = GetMarkedForwardAddress(referent); + if (forward_address == nullptr) { + Thread* self = Thread::Current(); + // TODO: Remove these locks, and use atomic stacks for storing references? + // We need to check that the references haven't already been enqueued since we can end up + // scanning the same reference multiple times due to dirty cards. + if (klass->IsSoftReferenceClass()) { + MutexLock mu(self, *heap_->GetSoftRefQueueLock()); + if (!heap_->IsEnqueued(obj)) { + heap_->EnqueuePendingReference(obj, &soft_reference_list_); + } + } else if (klass->IsWeakReferenceClass()) { + MutexLock mu(self, *heap_->GetWeakRefQueueLock()); + if (!heap_->IsEnqueued(obj)) { + heap_->EnqueuePendingReference(obj, &weak_reference_list_); + } + } else if (klass->IsFinalizerReferenceClass()) { + MutexLock mu(self, *heap_->GetFinalizerRefQueueLock()); + if (!heap_->IsEnqueued(obj)) { + heap_->EnqueuePendingReference(obj, &finalizer_reference_list_); + } + } else if (klass->IsPhantomReferenceClass()) { + MutexLock mu(self, *heap_->GetPhantomRefQueueLock()); + if (!heap_->IsEnqueued(obj)) { + heap_->EnqueuePendingReference(obj, &phantom_reference_list_); + } + } else { + LOG(FATAL) << "Invalid reference type " << PrettyClass(klass) << " " << std::hex + << klass->GetAccessFlags(); + } + } else if (referent != forward_address) { + heap_->SetReferenceReferent(obj, forward_address); + } + } +} + +// Visit all of the references of an object and update. +void SemiSpace::ScanObject(Object* obj) { + DCHECK(obj != NULL); + DCHECK(!from_space_->HasAddress(obj)) << "Scanning object " << obj << " in from space"; + MarkSweep::VisitObjectReferences(obj, [this](Object* obj, Object* ref, const MemberOffset& offset, + bool /* is_static */) ALWAYS_INLINE NO_THREAD_SAFETY_ANALYSIS { + mirror::Object* new_address = MarkObject(ref); + if (new_address != ref) { + DCHECK(new_address != nullptr); + obj->SetFieldObject(offset, new_address, false); + } + }, kMovingClasses); + mirror::Class* klass = obj->GetClass(); + if (UNLIKELY(klass->IsReferenceClass())) { + DelayReferenceReferent(klass, obj); + } +} + +// Scan anything that's on the mark stack. +void SemiSpace::ProcessMarkStack(bool paused) { + timings_.StartSplit(paused ? "(paused)ProcessMarkStack" : "ProcessMarkStack"); + while (!mark_stack_->IsEmpty()) { + ScanObject(mark_stack_->PopBack()); + } + timings_.EndSplit(); +} + +// Walks the reference list marking any references subject to the +// reference clearing policy. References with a black referent are +// removed from the list. References with white referents biased +// toward saving are blackened and also removed from the list. +void SemiSpace::PreserveSomeSoftReferences(Object** list) { + DCHECK(list != NULL); + Object* clear = NULL; + size_t counter = 0; + DCHECK(mark_stack_->IsEmpty()); + timings_.StartSplit("PreserveSomeSoftReferences"); + while (*list != NULL) { + Object* ref = heap_->DequeuePendingReference(list); + Object* referent = heap_->GetReferenceReferent(ref); + if (referent == NULL) { + // Referent was cleared by the user during marking. + continue; + } + Object* forward_address = GetMarkedForwardAddress(referent); + bool is_marked = forward_address != nullptr; + if (!is_marked && ((++counter) & 1)) { + // Referent is white and biased toward saving, mark it. + forward_address = MarkObject(referent); + if (referent != forward_address) { + // Update the referent if we moved it. + heap_->SetReferenceReferent(ref, forward_address); + } + } else { + if (!is_marked) { + // Referent is white, queue it for clearing. + heap_->EnqueuePendingReference(ref, &clear); + } else if (referent != forward_address) { + CHECK(forward_address != nullptr); + heap_->SetReferenceReferent(ref, forward_address); + } + } + } + *list = clear; + timings_.EndSplit(); + // Restart the mark with the newly black references added to the root set. + ProcessMarkStack(true); +} + +inline Object* SemiSpace::GetMarkedForwardAddress(mirror::Object* obj) const + SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_) { + // All immune objects are assumed marked. + if (IsImmune(obj)) { + return obj; + } + if (from_space_->HasAddress(obj)) { + mirror::Object* forwarding_address = GetForwardingAddressInFromSpace(const_cast(obj)); + // If the object is forwarded then it MUST be marked. + if (to_space_->HasAddress(forwarding_address)) { + return forwarding_address; + } + // Must not be marked, return nullptr; + return nullptr; + } else if (to_space_->HasAddress(obj)) { + // Already forwarded, must be marked. + return obj; + } + return heap_->GetMarkBitmap()->Test(obj) ? obj : nullptr; +} + +// Unlink the reference list clearing references objects with white +// referents. Cleared references registered to a reference queue are +// scheduled for appending by the heap worker thread. +void SemiSpace::ClearWhiteReferences(Object** list) { + DCHECK(list != NULL); + while (*list != NULL) { + Object* ref = heap_->DequeuePendingReference(list); + Object* referent = heap_->GetReferenceReferent(ref); + if (referent != nullptr) { + Object* forward_address = GetMarkedForwardAddress(referent); + if (forward_address == nullptr) { + // Referent is white, clear it. + heap_->ClearReferenceReferent(ref); + if (heap_->IsEnqueuable(ref)) { + heap_->EnqueueReference(ref, &cleared_reference_list_); + } + } else if (referent != forward_address) { + heap_->SetReferenceReferent(ref, forward_address); + } + } + } + DCHECK(*list == NULL); +} + +// Enqueues finalizer references with white referents. White +// referents are blackened, moved to the zombie field, and the +// referent field is cleared. +void SemiSpace::EnqueueFinalizerReferences(Object** list) { + // *list = NULL; + // return; + DCHECK(list != NULL); + timings_.StartSplit("EnqueueFinalizerReferences"); + MemberOffset zombie_offset = heap_->GetFinalizerReferenceZombieOffset(); + bool has_enqueued = false; + while (*list != NULL) { + Object* ref = heap_->DequeuePendingReference(list); + Object* referent = heap_->GetReferenceReferent(ref); + if (referent != nullptr) { + Object* forward_address = GetMarkedForwardAddress(referent); + // Not marked. + if (forward_address == nullptr) { + forward_address = MarkObject(referent); + // If the referent is non-null the reference must queuable. + DCHECK(heap_->IsEnqueuable(ref)); + // Move the referent to the zombie field. + ref->SetFieldObject(zombie_offset, forward_address, false); + heap_->ClearReferenceReferent(ref); + heap_->EnqueueReference(ref, &cleared_reference_list_); + has_enqueued = true; + } else if (referent != forward_address) { + heap_->SetReferenceReferent(ref, forward_address); + } + } + } + timings_.EndSplit(); + if (has_enqueued) { + ProcessMarkStack(true); + } + DCHECK(*list == NULL); +} + +// Process reference class instances and schedule finalizations. +void SemiSpace::ProcessReferences(Object** soft_references, bool clear_soft, + Object** weak_references, + Object** finalizer_references, + Object** phantom_references) { + CHECK(soft_references != NULL); + CHECK(weak_references != NULL); + CHECK(finalizer_references != NULL); + CHECK(phantom_references != NULL); + CHECK(mark_stack_->IsEmpty()); + + // Unless we are in the zygote or required to clear soft references + // with white references, preserve some white referents. + if (!clear_soft && !Runtime::Current()->IsZygote()) { + PreserveSomeSoftReferences(soft_references); + } + + timings_.StartSplit("ProcessReferences"); + // Clear all remaining soft and weak references with white + // referents. + ClearWhiteReferences(soft_references); + ClearWhiteReferences(weak_references); + timings_.EndSplit(); + + // Preserve all white objects with finalize methods and schedule + // them for finalization. + EnqueueFinalizerReferences(finalizer_references); + + timings_.StartSplit("ProcessReferences"); + // Clear all f-reachable soft and weak references with white + // referents. + ClearWhiteReferences(soft_references); + ClearWhiteReferences(weak_references); + + // Clear all phantom references with white referents. + ClearWhiteReferences(phantom_references); + + // At this point all reference lists should be empty. + DCHECK(*soft_references == NULL); + DCHECK(*weak_references == NULL); + DCHECK(*finalizer_references == NULL); + DCHECK(*phantom_references == NULL); + timings_.EndSplit(); +} + +void SemiSpace::UnBindBitmaps() { + base::TimingLogger::ScopedSplit split("UnBindBitmaps", &timings_); + for (const auto& space : GetHeap()->GetContinuousSpaces()) { + if (space->IsDlMallocSpace()) { + space::DlMallocSpace* alloc_space = space->AsDlMallocSpace(); + if (alloc_space->HasBoundBitmaps()) { + alloc_space->UnBindBitmaps(); + heap_->GetMarkBitmap()->ReplaceBitmap(alloc_space->GetLiveBitmap(), + alloc_space->GetMarkBitmap()); + } + } + } +} + +void SemiSpace::SetToSpace(space::ContinuousMemMapAllocSpace* to_space) { + DCHECK(to_space != nullptr); + to_space_ = to_space; +} + +void SemiSpace::SetFromSpace(space::ContinuousMemMapAllocSpace* from_space) { + DCHECK(from_space != nullptr); + from_space_ = from_space; +} + +void SemiSpace::FinishPhase() { + base::TimingLogger::ScopedSplit split("FinishPhase", &timings_); + // Can't enqueue references if we hold the mutator lock. + Object* cleared_references = GetClearedReferences(); + Heap* heap = GetHeap(); + timings_.NewSplit("EnqueueClearedReferences"); + heap->EnqueueClearedReferences(&cleared_references); + + timings_.NewSplit("PostGcVerification"); + heap->PostGcVerification(this); + + // Null the "to" and "from" spaces since compacting from one to the other isn't valid until + // further action is done by the heap. + to_space_ = nullptr; + from_space_ = nullptr; + + // Update the cumulative statistics + total_time_ns_ += GetDurationNs(); + total_paused_time_ns_ += std::accumulate(GetPauseTimes().begin(), GetPauseTimes().end(), 0, + std::plus()); + total_freed_objects_ += GetFreedObjects() + GetFreedLargeObjects(); + total_freed_bytes_ += GetFreedBytes() + GetFreedLargeObjectBytes(); + + // Ensure that the mark stack is empty. + CHECK(mark_stack_->IsEmpty()); + + // Update the cumulative loggers. + cumulative_timings_.Start(); + cumulative_timings_.AddLogger(timings_); + cumulative_timings_.End(); + + // Clear all of the spaces' mark bitmaps. + for (const auto& space : GetHeap()->GetContinuousSpaces()) { + accounting::SpaceBitmap* bitmap = space->GetMarkBitmap(); + if (bitmap != nullptr && + space->GetGcRetentionPolicy() != space::kGcRetentionPolicyNeverCollect) { + bitmap->Clear(); + } + } + mark_stack_->Reset(); + + // Reset the marked large objects. + space::LargeObjectSpace* large_objects = GetHeap()->GetLargeObjectsSpace(); + large_objects->GetMarkObjects()->Clear(); +} + +} // namespace collector +} // namespace gc +} // namespace art diff --git a/runtime/gc/collector/semi_space.h b/runtime/gc/collector/semi_space.h new file mode 100644 index 00000000000..13d519559aa --- /dev/null +++ b/runtime/gc/collector/semi_space.h @@ -0,0 +1,289 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ART_RUNTIME_GC_COLLECTOR_SEMI_SPACE_H_ +#define ART_RUNTIME_GC_COLLECTOR_SEMI_SPACE_H_ + +#include "atomic_integer.h" +#include "barrier.h" +#include "base/macros.h" +#include "base/mutex.h" +#include "garbage_collector.h" +#include "offsets.h" +#include "root_visitor.h" +#include "UniquePtr.h" + +namespace art { + +namespace mirror { + class Class; + class Object; + template class ObjectArray; +} // namespace mirror + +class StackVisitor; +class Thread; + +namespace gc { + +namespace accounting { + template class AtomicStack; + class MarkIfReachesAllocspaceVisitor; + class ModUnionClearCardVisitor; + class ModUnionVisitor; + class ModUnionTableBitmap; + class MarkStackChunk; + typedef AtomicStack ObjectStack; + class SpaceBitmap; +} // namespace accounting + +namespace space { + class BumpPointerSpace; + class ContinuousMemMapAllocSpace; + class ContinuousSpace; +} // namespace space + +class Heap; + +namespace collector { + +class SemiSpace : public GarbageCollector { + public: + explicit SemiSpace(Heap* heap, const std::string& name_prefix = ""); + + ~SemiSpace() {} + + virtual void InitializePhase(); + virtual bool IsConcurrent() const { + return false; + } + virtual void MarkingPhase() EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_); + virtual void ReclaimPhase() EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_); + virtual void FinishPhase() EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_); + virtual void MarkReachableObjects() + EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_, Locks::heap_bitmap_lock_); + virtual GcType GetGcType() const { + return kGcTypePartial; + } + + // Sets which space we will be copying objects to. + void SetToSpace(space::ContinuousMemMapAllocSpace* to_space); + + // Set the space where we copy objects from. + void SetFromSpace(space::ContinuousMemMapAllocSpace* from_space); + + // Initializes internal structures. + void Init(); + + // Find the default mark bitmap. + void FindDefaultMarkBitmap(); + + // Returns the new address of the object. + mirror::Object* MarkObject(mirror::Object* object) + EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_, Locks::mutator_lock_); + + void ScanObject(mirror::Object* obj) + EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_, Locks::mutator_lock_); + + // Marks the root set at the start of a garbage collection. + void MarkRoots() + EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_, Locks::mutator_lock_); + + // Make a space immune, immune spaces have all live objects marked - that is the mark and + // live bitmaps are bound together. + void ImmuneSpace(space::ContinuousSpace* space) + EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + // Bind the live bits to the mark bits of bitmaps for spaces that are never collected, ie + // the image. Mark that portion of the heap as immune. + virtual void BindBitmaps() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + void BindLiveToMarkBitmap(space::ContinuousSpace* space) + EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_); + + void UnBindBitmaps() + EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_); + + void ProcessReferences(Thread* self) + EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_); + + // Sweeps unmarked objects to complete the garbage collection. + virtual void Sweep(bool swap_bitmaps) EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_); + + // Sweeps unmarked objects to complete the garbage collection. + void SweepLargeObjects(bool swap_bitmaps) EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_); + + // Sweep only pointers within an array. WARNING: Trashes objects. + void SweepArray(accounting::ObjectStack* allocation_stack_, bool swap_bitmaps) + EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_); + + mirror::Object* GetClearedReferences() { + return cleared_reference_list_; + } + + // TODO: enable thread safety analysis when in use by multiple worker threads. + template + void ScanObjectVisit(const mirror::Object* obj, const MarkVisitor& visitor) + NO_THREAD_SAFETY_ANALYSIS; + + void SweepSystemWeaks() + SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_, Locks::mutator_lock_); + + template + static void VisitObjectReferencesAndClass(mirror::Object* obj, const Visitor& visitor) + EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_, Locks::mutator_lock_); + + static mirror::Object* MarkRootCallback(mirror::Object* root, void* arg) + EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_, Locks::mutator_lock_); + + protected: + // Returns null if the object is not marked, otherwise returns the forwarding address (same as + // object for non movable things). + mirror::Object* GetMarkedForwardAddress(mirror::Object* object) const; + + static mirror::Object* SystemWeakIsMarkedCallback(mirror::Object* object, void* arg) + SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_); + + // Marks or unmarks a large object based on whether or not set is true. If set is true, then we + // mark, otherwise we unmark. + bool MarkLargeObject(const mirror::Object* obj) + EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_); + + static void SweepCallback(size_t num_ptrs, mirror::Object** ptrs, void* arg) + EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_); + + // Special sweep for zygote that just marks objects / dirties cards. + static void ZygoteSweepCallback(size_t num_ptrs, mirror::Object** ptrs, void* arg) + EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_); + + // Expand mark stack to 2x its current size. + void ResizeMarkStack(size_t new_size); + + // Returns how many threads we should use for the current GC phase based on if we are paused, + // whether or not we care about pauses. + size_t GetThreadCount(bool paused) const; + + // Returns true if an object is inside of the immune region (assumed to be marked). + bool IsImmune(const mirror::Object* obj) const ALWAYS_INLINE { + return obj >= immune_begin_ && obj < immune_end_; + } + + bool IsImmuneSpace(const space::ContinuousSpace* space) const; + + static void VerifyRootCallback(const mirror::Object* root, void* arg, size_t vreg, + const StackVisitor *visitor); + + void VerifyRoot(const mirror::Object* root, size_t vreg, const StackVisitor* visitor) + NO_THREAD_SAFETY_ANALYSIS; + + template + static void VisitInstanceFieldsReferences(const mirror::Class* klass, const mirror::Object* obj, + const Visitor& visitor) + SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_, Locks::mutator_lock_); + + // Visit the header, static field references, and interface pointers of a class object. + template + static void VisitClassReferences(const mirror::Class* klass, const mirror::Object* obj, + const Visitor& visitor) + SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_, Locks::mutator_lock_); + + template + static void VisitStaticFieldsReferences(const mirror::Class* klass, const Visitor& visitor) + SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_, Locks::mutator_lock_); + + template + static void VisitFieldsReferences(const mirror::Object* obj, uint32_t ref_offsets, bool is_static, + const Visitor& visitor) + SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_, Locks::mutator_lock_); + + // Visit all of the references in an object array. + template + static void VisitObjectArrayReferences(const mirror::ObjectArray* array, + const Visitor& visitor) + SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_, Locks::mutator_lock_); + + // Visits the header and field references of a data object. + template + static void VisitOtherReferences(const mirror::Class* klass, const mirror::Object* obj, + const Visitor& visitor) + SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_, Locks::mutator_lock_) { + return VisitInstanceFieldsReferences(klass, obj, visitor); + } + + // Push an object onto the mark stack. + inline void MarkStackPush(mirror::Object* obj); + + void UpdateAndMarkModUnion() + EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + // Schedules an unmarked object for reference processing. + void DelayReferenceReferent(mirror::Class* klass, mirror::Object* reference) + SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_, Locks::mutator_lock_); + + // Recursively blackens objects on the mark stack. + void ProcessMarkStack(bool paused) + EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_, Locks::heap_bitmap_lock_); + + void EnqueueFinalizerReferences(mirror::Object** ref) + EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_, Locks::heap_bitmap_lock_); + + void PreserveSomeSoftReferences(mirror::Object** ref) + EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_, Locks::heap_bitmap_lock_); + + void ClearWhiteReferences(mirror::Object** list) + EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_, Locks::heap_bitmap_lock_); + + void ProcessReferences(mirror::Object** soft_references, bool clear_soft_references, + mirror::Object** weak_references, + mirror::Object** finalizer_references, + mirror::Object** phantom_references) + EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_, Locks::heap_bitmap_lock_); + + inline mirror::Object* GetForwardingAddressInFromSpace(mirror::Object* obj) const; + + mirror::Object* GetForwardingAddress(mirror::Object* obj); + + // Current space, we check this space first to avoid searching for the appropriate space for an + // object. + accounting::ObjectStack* mark_stack_; + + // Immune range, every object inside the immune range is assumed to be marked. + mirror::Object* immune_begin_; + mirror::Object* immune_end_; + + // Destination and source spaces. + space::ContinuousMemMapAllocSpace* to_space_; + space::ContinuousMemMapAllocSpace* from_space_; + + mirror::Object* soft_reference_list_; + mirror::Object* weak_reference_list_; + mirror::Object* finalizer_reference_list_; + mirror::Object* phantom_reference_list_; + mirror::Object* cleared_reference_list_; + + Thread* self_; + + private: + DISALLOW_COPY_AND_ASSIGN(SemiSpace); +}; + +} // namespace collector +} // namespace gc +} // namespace art + +#endif // ART_RUNTIME_GC_COLLECTOR_SEMI_SPACE_H_ diff --git a/runtime/gc/collector/sticky_mark_sweep.cc b/runtime/gc/collector/sticky_mark_sweep.cc index 9f0bf333875..b27b8dfb46e 100644 --- a/runtime/gc/collector/sticky_mark_sweep.cc +++ b/runtime/gc/collector/sticky_mark_sweep.cc @@ -26,7 +26,7 @@ namespace collector { StickyMarkSweep::StickyMarkSweep(Heap* heap, bool is_concurrent, const std::string& name_prefix) : PartialMarkSweep(heap, is_concurrent, - name_prefix + (name_prefix.empty() ? "" : " ") + "sticky") { + name_prefix.empty() ? "sticky " : name_prefix) { cumulative_timings_.SetName(GetName()); } @@ -38,7 +38,8 @@ void StickyMarkSweep::BindBitmaps() { // know what was allocated since the last GC. A side-effect of binding the allocation space mark // and live bitmap is that marking the objects will place them in the live bitmap. for (const auto& space : GetHeap()->GetContinuousSpaces()) { - if (space->GetGcRetentionPolicy() == space::kGcRetentionPolicyAlwaysCollect) { + if (space->IsDlMallocSpace() && + space->GetGcRetentionPolicy() == space::kGcRetentionPolicyAlwaysCollect) { BindLiveToMarkBitmap(space); } } diff --git a/runtime/gc/collector/sticky_mark_sweep.h b/runtime/gc/collector/sticky_mark_sweep.h index 8bee00f0b8f..b6758777af9 100644 --- a/runtime/gc/collector/sticky_mark_sweep.h +++ b/runtime/gc/collector/sticky_mark_sweep.h @@ -31,10 +31,6 @@ class StickyMarkSweep : public PartialMarkSweep { return kGcTypeSticky; } - // Don't need to do anything special here since we scan all the cards which may have references - // to the newly allocated objects. - virtual void UpdateAndMarkModUnion() { } - explicit StickyMarkSweep(Heap* heap, bool is_concurrent, const std::string& name_prefix = ""); ~StickyMarkSweep() {} @@ -53,6 +49,10 @@ class StickyMarkSweep : public PartialMarkSweep { void Sweep(bool swap_bitmaps) EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_); + // Don't need to do anything special here since we scan all the cards which may have references + // to the newly allocated objects. + virtual void UpdateAndMarkModUnion() { } + private: DISALLOW_COPY_AND_ASSIGN(StickyMarkSweep); }; diff --git a/runtime/gc/heap-inl.h b/runtime/gc/heap-inl.h index 873eadc46ae..1d3c0d87772 100644 --- a/runtime/gc/heap-inl.h +++ b/runtime/gc/heap-inl.h @@ -20,6 +20,7 @@ #include "heap.h" #include "debugger.h" +#include "gc/space/bump_pointer_space-inl.h" #include "gc/space/dlmalloc_space-inl.h" #include "gc/space/large_object_space.h" #include "object_utils.h" @@ -30,8 +31,9 @@ namespace art { namespace gc { -inline mirror::Object* Heap::AllocObjectUninstrumented(Thread* self, mirror::Class* c, size_t byte_count) { - DebugCheckPreconditionsForAllobObject(c, byte_count); +inline mirror::Object* Heap::AllocNonMovableObjectUninstrumented(Thread* self, mirror::Class* c, + size_t byte_count) { + DebugCheckPreconditionsForAllocObject(c, byte_count); mirror::Object* obj; size_t bytes_allocated; AllocationTimer alloc_timer(this, &obj); @@ -39,7 +41,7 @@ inline mirror::Object* Heap::AllocObjectUninstrumented(Thread* self, mirror::Cla &obj, &bytes_allocated); if (LIKELY(!large_object_allocation)) { // Non-large object allocation. - obj = AllocateUninstrumented(self, alloc_space_, byte_count, &bytes_allocated); + obj = AllocateUninstrumented(self, non_moving_space_, byte_count, &bytes_allocated); // Ensure that we did not allocate into a zygote space. DCHECK(obj == NULL || !have_zygote_space_ || !FindSpaceFromObject(obj, false)->IsZygoteSpace()); } @@ -53,10 +55,45 @@ inline mirror::Object* Heap::AllocObjectUninstrumented(Thread* self, mirror::Cla if (kDesiredHeapVerification > kNoHeapVerification) { VerifyObject(obj); } - return obj; + } else { + ThrowOutOfMemoryError(self, byte_count, large_object_allocation); } - ThrowOutOfMemoryError(self, byte_count, large_object_allocation); - return NULL; + if (kIsDebugBuild) { + self->VerifyStack(); + } + return obj; +} + +inline mirror::Object* Heap::AllocMovableObjectUninstrumented(Thread* self, mirror::Class* c, + size_t byte_count) { + DebugCheckPreconditionsForAllocObject(c, byte_count); + mirror::Object* obj; + AllocationTimer alloc_timer(this, &obj); + byte_count = (byte_count + 7) & ~7; + if (UNLIKELY(IsOutOfMemoryOnAllocation(byte_count, false))) { + CollectGarbageInternal(collector::kGcTypeFull, kGcCauseForAlloc, false); + if (UNLIKELY(IsOutOfMemoryOnAllocation(byte_count, true))) { + CollectGarbageInternal(collector::kGcTypeFull, kGcCauseForAlloc, true); + } + } + obj = bump_pointer_space_->AllocNonvirtual(byte_count); + if (LIKELY(obj != NULL)) { + obj->SetClass(c); + DCHECK(!obj->IsClass()); + // Record allocation after since we want to use the atomic add for the atomic fence to guard + // the SetClass since we do not want the class to appear NULL in another thread. + num_bytes_allocated_.fetch_add(byte_count); + DCHECK(!Dbg::IsAllocTrackingEnabled()); + if (kDesiredHeapVerification > kNoHeapVerification) { + VerifyObject(obj); + } + } else { + ThrowOutOfMemoryError(self, byte_count, false); + } + if (kIsDebugBuild) { + self->VerifyStack(); + } + return obj; } inline size_t Heap::RecordAllocationUninstrumented(size_t size, mirror::Object* obj) { @@ -124,7 +161,7 @@ inline bool Heap::TryAllocLargeObjectUninstrumented(Thread* self, mirror::Class* return large_object_allocation; } -inline void Heap::DebugCheckPreconditionsForAllobObject(mirror::Class* c, size_t byte_count) { +inline void Heap::DebugCheckPreconditionsForAllocObject(mirror::Class* c, size_t byte_count) { DCHECK(c == NULL || (c->IsClassClass() && byte_count >= sizeof(mirror::Class)) || (c->IsVariableSize() || c->GetObjectSize() == byte_count) || strlen(ClassHelper(c).GetDescriptor()) == 0); diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc index de3ab0eb9d2..69ca6202f94 100644 --- a/runtime/gc/heap.cc +++ b/runtime/gc/heap.cc @@ -30,11 +30,14 @@ #include "gc/accounting/atomic_stack.h" #include "gc/accounting/card_table-inl.h" #include "gc/accounting/heap_bitmap-inl.h" +#include "gc/accounting/mod_union_table.h" #include "gc/accounting/mod_union_table-inl.h" #include "gc/accounting/space_bitmap-inl.h" #include "gc/collector/mark_sweep-inl.h" #include "gc/collector/partial_mark_sweep.h" +#include "gc/collector/semi_space.h" #include "gc/collector/sticky_mark_sweep.h" +#include "gc/space/bump_pointer_space.h" #include "gc/space/dlmalloc_space-inl.h" #include "gc/space/image_space.h" #include "gc/space/large_object_space.h" @@ -70,9 +73,8 @@ Heap::Heap(size_t initial_size, size_t growth_limit, size_t min_free, size_t max bool concurrent_gc, size_t parallel_gc_threads, size_t conc_gc_threads, bool low_memory_mode, size_t long_pause_log_threshold, size_t long_gc_log_threshold, bool ignore_max_footprint) - : alloc_space_(NULL), - card_table_(NULL), - concurrent_gc_(concurrent_gc), + : non_moving_space_(NULL), + concurrent_gc_(!kMovingCollector && concurrent_gc), parallel_gc_threads_(parallel_gc_threads), conc_gc_threads_(conc_gc_threads), low_memory_mode_(low_memory_mode), @@ -92,6 +94,7 @@ Heap::Heap(size_t initial_size, size_t growth_limit, size_t min_free, size_t max max_allowed_footprint_(initial_size), native_footprint_gc_watermark_(initial_size), native_footprint_limit_(2 * initial_size), + native_need_to_run_finalization_(false), activity_thread_class_(NULL), application_thread_class_(NULL), activity_thread_(NULL), @@ -122,7 +125,9 @@ Heap::Heap(size_t initial_size, size_t growth_limit, size_t min_free, size_t max * searching. */ max_allocation_stack_size_(kGCALotMode ? kGcAlotInterval - : (kDesiredHeapVerification > kNoHeapVerification) ? KB : MB), + : (kDesiredHeapVerification > kVerifyAllFast) ? KB : MB), + bump_pointer_space_(nullptr), + temp_space_(nullptr), reference_referent_offset_(0), reference_queue_offset_(0), reference_queueNext_offset_(0), @@ -134,6 +139,7 @@ Heap::Heap(size_t initial_size, size_t growth_limit, size_t min_free, size_t max total_wait_time_(0), total_allocation_time_(0), verify_object_mode_(kHeapVerificationNotPermitted), + gc_disable_count_(0), running_on_valgrind_(RUNNING_ON_VALGRIND) { if (VLOG_IS_ON(heap) || VLOG_IS_ON(startup)) { LOG(INFO) << "Heap() entering"; @@ -147,7 +153,7 @@ Heap::Heap(size_t initial_size, size_t growth_limit, size_t min_free, size_t max if (!image_file_name.empty()) { space::ImageSpace* image_space = space::ImageSpace::Create(image_file_name.c_str()); CHECK(image_space != NULL) << "Failed to create space for " << image_file_name; - AddContinuousSpace(image_space); + AddSpace(image_space); // Oat files referenced by image files immediately follow them in memory, ensure alloc space // isn't going to get in the middle byte* oat_file_end_addr = image_space->GetImageHeader().GetOatFileEnd(); @@ -159,13 +165,28 @@ Heap::Heap(size_t initial_size, size_t growth_limit, size_t min_free, size_t max } } - alloc_space_ = space::DlMallocSpace::Create(Runtime::Current()->IsZygote() ? "zygote space" : "alloc space", - initial_size, - growth_limit, capacity, - requested_alloc_space_begin); - CHECK(alloc_space_ != NULL) << "Failed to create alloc space"; - alloc_space_->SetFootprintLimit(alloc_space_->Capacity()); - AddContinuousSpace(alloc_space_); + const char* name = Runtime::Current()->IsZygote() ? "zygote space" : "alloc space"; + non_moving_space_ = space::DlMallocSpace::Create(name, initial_size, growth_limit, capacity, + requested_alloc_space_begin); + + if (kMovingCollector) { + // TODO: Place bump-pointer spaces somewhere to minimize size of card table. + // TODO: Having 3+ spaces as big as the large heap size can cause virtual memory fragmentation + // issues. + const size_t bump_pointer_space_size = std::min(non_moving_space_->Capacity(), 128 * MB); + bump_pointer_space_ = space::BumpPointerSpace::Create("Bump pointer space", + bump_pointer_space_size, nullptr); + CHECK(bump_pointer_space_ != nullptr) << "Failed to create bump pointer space"; + AddSpace(bump_pointer_space_); + temp_space_ = space::BumpPointerSpace::Create("Bump pointer space 2", bump_pointer_space_size, + nullptr); + CHECK(temp_space_ != nullptr) << "Failed to create bump pointer space"; + AddSpace(temp_space_); + } + + CHECK(non_moving_space_ != NULL) << "Failed to create non-moving space"; + non_moving_space_->SetFootprintLimit(non_moving_space_->Capacity()); + AddSpace(non_moving_space_); // Allocate the large object space. const bool kUseFreeListSpaceForLOS = false; @@ -175,22 +196,23 @@ Heap::Heap(size_t initial_size, size_t growth_limit, size_t min_free, size_t max large_object_space_ = space::LargeObjectMapSpace::Create("large object space"); } CHECK(large_object_space_ != NULL) << "Failed to create large object space"; - AddDiscontinuousSpace(large_object_space_); + AddSpace(large_object_space_); // Compute heap capacity. Continuous spaces are sorted in order of Begin(). + CHECK(!continuous_spaces_.empty()); + // Relies on the spaces being sorted. byte* heap_begin = continuous_spaces_.front()->Begin(); - size_t heap_capacity = continuous_spaces_.back()->End() - continuous_spaces_.front()->Begin(); - if (continuous_spaces_.back()->IsDlMallocSpace()) { - heap_capacity += continuous_spaces_.back()->AsDlMallocSpace()->NonGrowthLimitCapacity(); - } + byte* heap_end = continuous_spaces_.back()->Limit(); + size_t heap_capacity = heap_end - heap_begin; // Allocate the card table. card_table_.reset(accounting::CardTable::Create(heap_begin, heap_capacity)); CHECK(card_table_.get() != NULL) << "Failed to create card table"; + // Card cache for now since it makes it easier for us to update the references to the copying + // spaces. accounting::ModUnionTable* mod_union_table = - new accounting::ModUnionTableToZygoteAllocspace("Image mod-union table", this, - GetImageSpace()); + new accounting::ModUnionTableCardCache("Image mod-union table", this, GetImageSpace()); CHECK(mod_union_table != nullptr) << "Failed to create image mod-union table"; AddModUnionTable(mod_union_table); @@ -223,19 +245,23 @@ Heap::Heap(size_t initial_size, size_t growth_limit, size_t min_free, size_t max if (ignore_max_footprint_) { SetIdealFootprint(std::numeric_limits::max()); - concurrent_start_bytes_ = max_allowed_footprint_; + concurrent_start_bytes_ = std::numeric_limits::max(); } + CHECK_NE(max_allowed_footprint_, 0U); // Create our garbage collectors. - for (size_t i = 0; i < 2; ++i) { - const bool concurrent = i != 0; - mark_sweep_collectors_.push_back(new collector::MarkSweep(this, concurrent)); - mark_sweep_collectors_.push_back(new collector::PartialMarkSweep(this, concurrent)); - mark_sweep_collectors_.push_back(new collector::StickyMarkSweep(this, concurrent)); + if (!kMovingCollector) { + for (size_t i = 0; i < 2; ++i) { + const bool concurrent = i != 0; + garbage_collectors_.push_back(new collector::MarkSweep(this, concurrent)); + garbage_collectors_.push_back(new collector::PartialMarkSweep(this, concurrent)); + garbage_collectors_.push_back(new collector::StickyMarkSweep(this, concurrent)); + } + } else { + semi_space_collector_ = new collector::SemiSpace(this); + garbage_collectors_.push_back(semi_space_collector_); } - CHECK_NE(max_allowed_footprint_, 0U); - if (running_on_valgrind_) { Runtime::Current()->GetInstrumentation()->InstrumentQuickAllocEntryPoints(); } @@ -245,6 +271,41 @@ Heap::Heap(size_t initial_size, size_t growth_limit, size_t min_free, size_t max } } +bool Heap::IsCompilingBoot() const { + for (const auto& space : continuous_spaces_) { + if (space->IsImageSpace()) { + return false; + } else if (space->IsZygoteSpace()) { + return false; + } + } + return true; +} + +bool Heap::HasImageSpace() const { + for (const auto& space : continuous_spaces_) { + if (space->IsImageSpace()) { + return true; + } + } + return false; +} + +void Heap::IncrementDisableGC(Thread* self) { + // Need to do this holding the lock to prevent races where the GC is about to run / running when + // we attempt to disable it. + ScopedThreadStateChange tsc(self, kWaitingForGcToComplete); + MutexLock mu(self, *gc_complete_lock_); + WaitForGcToCompleteLocked(self); + ++gc_disable_count_; +} + +void Heap::DecrementDisableGC(Thread* self) { + MutexLock mu(self, *gc_complete_lock_); + CHECK_GE(gc_disable_count_, 0U); + --gc_disable_count_; +} + void Heap::CreateThreadPool() { const size_t num_threads = std::max(parallel_gc_threads_, conc_gc_threads_); if (num_threads != 0) { @@ -252,12 +313,49 @@ void Heap::CreateThreadPool() { } } +void Heap::VisitObjects(ObjectVisitorCallback callback, void* arg) { + // Visit objects in bump pointer space. + Thread* self = Thread::Current(); + // TODO: Use reference block. + std::vector*> saved_refs; + if (bump_pointer_space_ != nullptr) { + // Need to put all these in sirts since the callback may trigger a GC. TODO: Use a better data + // structure. + mirror::Object* obj = reinterpret_cast(bump_pointer_space_->Begin()); + const mirror::Object* end = reinterpret_cast( + bump_pointer_space_->End()); + while (obj < end) { + saved_refs.push_back(new SirtRef(self, obj)); + obj = space::BumpPointerSpace::GetNextObject(obj); + } + } + // TODO: Switch to standard begin and end to use ranged a based loop. + for (mirror::Object** it = allocation_stack_->Begin(), **end = allocation_stack_->End(); + it < end; ++it) { + mirror::Object* obj = *it; + // Objects in the allocation stack might be in a movable space. + saved_refs.push_back(new SirtRef(self, obj)); + } + GetLiveBitmap()->Walk(callback, arg); + for (const auto& ref : saved_refs) { + callback(ref->get(), arg); + } + // Need to free the sirts in reverse order they were allocated. + for (size_t i = saved_refs.size(); i != 0; --i) { + delete saved_refs[i - 1]; + } +} + +void Heap::MarkAllocStackAsLive(accounting::ObjectStack* stack) { + MarkAllocStack(non_moving_space_->GetLiveBitmap(), large_object_space_->GetLiveObjects(), stack); +} + void Heap::DeleteThreadPool() { thread_pool_.reset(nullptr); } static bool ReadStaticInt(JNIEnvExt* env, jclass clz, const char* name, int* out_value) { - CHECK(out_value != NULL); + DCHECK(out_value != NULL); jfieldID field = env->GetStaticFieldID(clz, name, "I"); if (field == NULL) { env->ExceptionClear(); @@ -374,62 +472,71 @@ void Heap::ListenForProcessStateChange() { } } -void Heap::AddContinuousSpace(space::ContinuousSpace* space) { - WriterMutexLock mu(Thread::Current(), *Locks::heap_bitmap_lock_); +void Heap::AddSpace(space::Space* space) { DCHECK(space != NULL); - DCHECK(space->GetLiveBitmap() != NULL); - live_bitmap_->AddContinuousSpaceBitmap(space->GetLiveBitmap()); - DCHECK(space->GetMarkBitmap() != NULL); - mark_bitmap_->AddContinuousSpaceBitmap(space->GetMarkBitmap()); - continuous_spaces_.push_back(space); - if (space->IsDlMallocSpace() && !space->IsLargeObjectSpace()) { - alloc_space_ = space->AsDlMallocSpace(); - } - - // Ensure that spaces remain sorted in increasing order of start address (required for CMS finger) - std::sort(continuous_spaces_.begin(), continuous_spaces_.end(), - [](const space::ContinuousSpace* a, const space::ContinuousSpace* b) { - return a->Begin() < b->Begin(); - }); - - // Ensure that ImageSpaces < ZygoteSpaces < AllocSpaces so that we can do address based checks to - // avoid redundant marking. - bool seen_zygote = false, seen_alloc = false; - for (const auto& space : continuous_spaces_) { - if (space->IsImageSpace()) { - DCHECK(!seen_zygote); - DCHECK(!seen_alloc); - } else if (space->IsZygoteSpace()) { - DCHECK(!seen_alloc); - seen_zygote = true; - } else if (space->IsDlMallocSpace()) { - seen_alloc = true; + WriterMutexLock mu(Thread::Current(), *Locks::heap_bitmap_lock_); + if (space->IsContinuousSpace()) { + DCHECK(!space->IsDiscontinuousSpace()); + space::ContinuousSpace* continuous_space = space->AsContinuousSpace(); + // Continuous spaces don't necessarily have bitmaps. + accounting::SpaceBitmap* live_bitmap = continuous_space->GetLiveBitmap(); + accounting::SpaceBitmap* mark_bitmap = continuous_space->GetMarkBitmap(); + if (live_bitmap != nullptr) { + DCHECK(mark_bitmap != nullptr); + live_bitmap_->AddContinuousSpaceBitmap(live_bitmap); + mark_bitmap_->AddContinuousSpaceBitmap(mark_bitmap); + } + + continuous_spaces_.push_back(continuous_space); + if (continuous_space->IsDlMallocSpace()) { + non_moving_space_ = continuous_space->AsDlMallocSpace(); + } + + // Ensure that spaces remain sorted in increasing order of start address. + std::sort(continuous_spaces_.begin(), continuous_spaces_.end(), + [](const space::ContinuousSpace* a, const space::ContinuousSpace* b) { + return a->Begin() < b->Begin(); + }); + // Ensure that ImageSpaces < ZygoteSpaces < AllocSpaces so that we can do address based checks to + // avoid redundant marking. + bool seen_zygote = false, seen_alloc = false; + for (const auto& space : continuous_spaces_) { + if (space->IsImageSpace()) { + CHECK(!seen_zygote); + CHECK(!seen_alloc); + } else if (space->IsZygoteSpace()) { + CHECK(!seen_alloc); + seen_zygote = true; + } else if (space->IsDlMallocSpace()) { + seen_alloc = true; + } } + } else { + DCHECK(space->IsDiscontinuousSpace()); + space::DiscontinuousSpace* discontinuous_space = space->AsDiscontinuousSpace(); + DCHECK(discontinuous_space->GetLiveObjects() != nullptr); + live_bitmap_->AddDiscontinuousObjectSet(discontinuous_space->GetLiveObjects()); + DCHECK(discontinuous_space->GetMarkObjects() != nullptr); + mark_bitmap_->AddDiscontinuousObjectSet(discontinuous_space->GetMarkObjects()); + discontinuous_spaces_.push_back(discontinuous_space); + } + if (space->IsAllocSpace()) { + alloc_spaces_.push_back(space->AsAllocSpace()); } } void Heap::RegisterGCAllocation(size_t bytes) { - if (this != NULL) { + if (this != nullptr) { gc_memory_overhead_.fetch_add(bytes); } } void Heap::RegisterGCDeAllocation(size_t bytes) { - if (this != NULL) { + if (this != nullptr) { gc_memory_overhead_.fetch_sub(bytes); } } -void Heap::AddDiscontinuousSpace(space::DiscontinuousSpace* space) { - WriterMutexLock mu(Thread::Current(), *Locks::heap_bitmap_lock_); - DCHECK(space != NULL); - DCHECK(space->GetLiveObjects() != NULL); - live_bitmap_->AddDiscontinuousObjectSet(space->GetLiveObjects()); - DCHECK(space->GetMarkObjects() != NULL); - mark_bitmap_->AddDiscontinuousObjectSet(space->GetMarkObjects()); - discontinuous_spaces_.push_back(space); -} - void Heap::DumpGcPerformanceInfo(std::ostream& os) { // Dump cumulative timings. os << "Dumping cumulative Gc timings\n"; @@ -437,7 +544,7 @@ void Heap::DumpGcPerformanceInfo(std::ostream& os) { // Dump cumulative loggers for each GC type. uint64_t total_paused_time = 0; - for (const auto& collector : mark_sweep_collectors_) { + for (const auto& collector : garbage_collectors_) { CumulativeLogger& logger = collector->GetCumulativeTimings(); if (logger.GetTotalNs() != 0) { os << Dumpable(logger); @@ -480,17 +587,14 @@ void Heap::DumpGcPerformanceInfo(std::ostream& os) { } Heap::~Heap() { + VLOG(heap) << "Starting ~Heap()"; if (kDumpGcPerformanceOnShutdown) { DumpGcPerformanceInfo(LOG(INFO)); } - - STLDeleteElements(&mark_sweep_collectors_); - - // If we don't reset then the mark stack complains in it's destructor. + STLDeleteElements(&garbage_collectors_); + // If we don't reset then the mark stack complains in its destructor. allocation_stack_->Reset(); live_stack_->Reset(); - - VLOG(heap) << "~Heap()"; STLDeleteValues(&mod_union_tables_); STLDeleteElements(&continuous_spaces_); STLDeleteElements(&discontinuous_spaces_); @@ -499,6 +603,7 @@ Heap::~Heap() { delete weak_ref_queue_lock_; delete finalizer_ref_queue_lock_; delete phantom_ref_queue_lock_; + VLOG(heap) << "Finished ~Heap()"; } space::ContinuousSpace* Heap::FindContinuousSpaceFromObject(const mirror::Object* obj, @@ -579,7 +684,7 @@ inline bool Heap::TryAllocLargeObjectInstrumented(Thread* self, mirror::Class* c mirror::Object* obj = AllocateInstrumented(self, large_object_space_, byte_count, bytes_allocated); // Make sure that our large object didn't get placed anywhere within the space interval or else // it breaks the immune range. - DCHECK(obj == NULL || + DCHECK(obj == nullptr || reinterpret_cast(obj) < continuous_spaces_.front()->Begin() || reinterpret_cast(obj) >= continuous_spaces_.back()->End()); *obj_ptr = obj; @@ -587,16 +692,59 @@ inline bool Heap::TryAllocLargeObjectInstrumented(Thread* self, mirror::Class* c return large_object_allocation; } -mirror::Object* Heap::AllocObjectInstrumented(Thread* self, mirror::Class* c, size_t byte_count) { - DebugCheckPreconditionsForAllobObject(c, byte_count); +mirror::Object* Heap::AllocMovableObjectInstrumented(Thread* self, mirror::Class* c, + size_t byte_count) { + DebugCheckPreconditionsForAllocObject(c, byte_count); + mirror::Object* obj; + AllocationTimer alloc_timer(this, &obj); + byte_count = RoundUp(byte_count, 8); + if (UNLIKELY(IsOutOfMemoryOnAllocation(byte_count, false))) { + CollectGarbageInternal(collector::kGcTypeFull, kGcCauseForAlloc, false); + if (UNLIKELY(IsOutOfMemoryOnAllocation(byte_count, true))) { + CollectGarbageInternal(collector::kGcTypeFull, kGcCauseForAlloc, true); + } + } + obj = bump_pointer_space_->AllocNonvirtual(byte_count); + if (LIKELY(obj != NULL)) { + obj->SetClass(c); + DCHECK(!obj->IsClass()); + // Record allocation after since we want to use the atomic add for the atomic fence to guard + // the SetClass since we do not want the class to appear NULL in another thread. + num_bytes_allocated_.fetch_add(byte_count); + if (Runtime::Current()->HasStatsEnabled()) { + RuntimeStats* thread_stats = Thread::Current()->GetStats(); + ++thread_stats->allocated_objects; + thread_stats->allocated_bytes += byte_count; + RuntimeStats* global_stats = Runtime::Current()->GetStats(); + ++global_stats->allocated_objects; + global_stats->allocated_bytes += byte_count; + } + if (Dbg::IsAllocTrackingEnabled()) { + Dbg::RecordAllocation(c, byte_count); + } + if (kDesiredHeapVerification > kNoHeapVerification) { + VerifyObject(obj); + } + } else { + ThrowOutOfMemoryError(self, byte_count, false); + } + if (kIsDebugBuild) { + self->VerifyStack(); + } + return obj; +} + +mirror::Object* Heap::AllocNonMovableObjectInstrumented(Thread* self, mirror::Class* c, + size_t byte_count) { + DebugCheckPreconditionsForAllocObject(c, byte_count); mirror::Object* obj; size_t bytes_allocated; AllocationTimer alloc_timer(this, &obj); - bool large_object_allocation = TryAllocLargeObjectInstrumented(self, c, byte_count, - &obj, &bytes_allocated); + bool large_object_allocation = TryAllocLargeObjectInstrumented(self, c, byte_count, &obj, + &bytes_allocated); if (LIKELY(!large_object_allocation)) { // Non-large object allocation. - obj = AllocateInstrumented(self, alloc_space_, byte_count, &bytes_allocated); + obj = AllocateInstrumented(self, non_moving_space_, byte_count, &bytes_allocated); // Ensure that we did not allocate into a zygote space. DCHECK(obj == NULL || !have_zygote_space_ || !FindSpaceFromObject(obj, false)->IsZygoteSpace()); } @@ -612,28 +760,66 @@ mirror::Object* Heap::AllocObjectInstrumented(Thread* self, mirror::Class* c, si if (kDesiredHeapVerification > kNoHeapVerification) { VerifyObject(obj); } - return obj; + } else { + ThrowOutOfMemoryError(self, byte_count, large_object_allocation); } - ThrowOutOfMemoryError(self, byte_count, large_object_allocation); - return NULL; + if (kIsDebugBuild) { + self->VerifyStack(); + } + return obj; } -bool Heap::IsHeapAddress(const mirror::Object* obj) { - // Note: we deliberately don't take the lock here, and mustn't test anything that would - // require taking the lock. - if (obj == NULL) { +void Heap::Trim() { + uint64_t start_ns = NanoTime(); + // Trim the managed spaces. + uint64_t total_alloc_space_allocated = 0; + uint64_t total_alloc_space_size = 0; + uint64_t managed_reclaimed = 0; + for (const auto& space : continuous_spaces_) { + if (space->IsDlMallocSpace() && !space->IsZygoteSpace()) { + gc::space::DlMallocSpace* alloc_space = space->AsDlMallocSpace(); + total_alloc_space_size += alloc_space->Size(); + managed_reclaimed += alloc_space->Trim(); + } + } + total_alloc_space_allocated = GetBytesAllocated() - large_object_space_->GetBytesAllocated() - + bump_pointer_space_->GetBytesAllocated(); + const float managed_utilization = static_cast(total_alloc_space_allocated) / + static_cast(total_alloc_space_size); + uint64_t gc_heap_end_ns = NanoTime(); + // Trim the native heap. + dlmalloc_trim(0); + size_t native_reclaimed = 0; + dlmalloc_inspect_all(DlmallocMadviseCallback, &native_reclaimed); + uint64_t end_ns = NanoTime(); + VLOG(heap) << "Heap trim of managed (duration=" << PrettyDuration(gc_heap_end_ns - start_ns) + << ", advised=" << PrettySize(managed_reclaimed) << ") and native (duration=" + << PrettyDuration(end_ns - gc_heap_end_ns) << ", advised=" << PrettySize(native_reclaimed) + << ") heaps. Managed heap utilization of " << static_cast(100 * managed_utilization) + << "%."; +} + +bool Heap::IsValidObjectAddress(const mirror::Object* obj) const { + // Note: we deliberately don't take the lock here, and mustn't test anything that would require + // taking the lock. + if (obj == nullptr) { return true; } - if (UNLIKELY(!IsAligned(obj))) { - return false; + return IsAligned(obj) && IsHeapAddress(obj); +} + +bool Heap::IsHeapAddress(const mirror::Object* obj) const { + if (kMovingCollector && bump_pointer_space_->HasAddress(obj)) { + return true; } - return FindSpaceFromObject(obj, true) != NULL; + // TODO: This probably doesn't work for large objects. + return FindSpaceFromObject(obj, true) != nullptr; } bool Heap::IsLiveObjectLocked(const mirror::Object* obj, bool search_allocation_stack, bool search_live_stack, bool sorted) { // Locks::heap_bitmap_lock_->AssertReaderHeld(Thread::Current()); - if (obj == NULL || UNLIKELY(!IsAligned(obj))) { + if (obj == nullptr || UNLIKELY(!IsAligned(obj))) { return false; } space::ContinuousSpace* c_space = FindContinuousSpaceFromObject(obj, true); @@ -642,6 +828,8 @@ bool Heap::IsLiveObjectLocked(const mirror::Object* obj, bool search_allocation_ if (c_space->GetLiveBitmap()->Test(obj)) { return true; } + } else if (bump_pointer_space_->Contains(obj) || temp_space_->Contains(obj)) { + return true; } else { d_space = FindDiscontinuousSpaceFromObject(obj, true); if (d_space != NULL) { @@ -699,16 +887,20 @@ void Heap::VerifyObjectImpl(const mirror::Object* obj) { VerifyObjectBody(obj); } -void Heap::DumpSpaces() { +void Heap::DumpSpaces(std::ostream& stream) { for (const auto& space : continuous_spaces_) { accounting::SpaceBitmap* live_bitmap = space->GetLiveBitmap(); accounting::SpaceBitmap* mark_bitmap = space->GetMarkBitmap(); - LOG(INFO) << space << " " << *space << "\n" - << live_bitmap << " " << *live_bitmap << "\n" - << mark_bitmap << " " << *mark_bitmap; + stream << space << " " << *space << "\n"; + if (live_bitmap != nullptr) { + stream << live_bitmap << " " << *live_bitmap << "\n"; + } + if (mark_bitmap != nullptr) { + stream << mark_bitmap << " " << *mark_bitmap << "\n"; + } } for (const auto& space : discontinuous_spaces_) { - LOG(INFO) << space << " " << *space << "\n"; + stream << space << " " << *space << "\n"; } } @@ -735,7 +927,7 @@ void Heap::VerifyObjectBody(const mirror::Object* obj) { const mirror::Class* c_c_c = *reinterpret_cast(raw_addr); CHECK_EQ(c_c, c_c_c); - if (verify_object_mode_ != kVerifyAllFast) { + if (verify_object_mode_ > kVerifyAllFast) { // TODO: the bitmap tests below are racy if VerifyObjectBody is called without the // heap_bitmap_lock_. if (!IsLiveObjectLocked(obj)) { @@ -811,7 +1003,7 @@ inline mirror::Object* Heap::TryToAllocateInstrumented(Thread* self, space::Allo inline mirror::Object* Heap::TryToAllocateInstrumented(Thread* self, space::DlMallocSpace* space, size_t alloc_size, bool grow, size_t* bytes_allocated) { if (UNLIKELY(IsOutOfMemoryOnAllocation(alloc_size, grow))) { - return NULL; + return nullptr; } if (LIKELY(!running_on_valgrind_)) { return space->AllocNonvirtual(self, alloc_size, bytes_allocated); @@ -841,7 +1033,7 @@ mirror::Object* Heap::AllocateInternalWithGc(Thread* self, space::AllocSpace* sp // The allocation failed. If the GC is running, block until it completes, and then retry the // allocation. - collector::GcType last_gc = WaitForConcurrentGcToComplete(self); + collector::GcType last_gc = WaitForGcToComplete(self); if (last_gc != collector::kGcTypeNone) { // A GC was in progress and we blocked, retry allocation now that memory has been freed. ptr = TryToAllocateInstrumented(self, space, alloc_size, false, bytes_allocated); @@ -857,9 +1049,10 @@ mirror::Object* Heap::AllocateInternalWithGc(Thread* self, space::AllocSpace* sp collector::GcType gc_type = static_cast(i); switch (gc_type) { case collector::kGcTypeSticky: { - const size_t alloc_space_size = alloc_space_->Size(); + const size_t alloc_space_size = non_moving_space_->Size(); run_gc = alloc_space_size > min_alloc_space_size_for_sticky_gc_ && - alloc_space_->Capacity() - alloc_space_size >= min_remaining_space_for_sticky_gc_; + non_moving_space_->Capacity() - alloc_space_size >= + min_remaining_space_for_sticky_gc_; break; } case collector::kGcTypePartial: @@ -869,7 +1062,7 @@ mirror::Object* Heap::AllocateInternalWithGc(Thread* self, space::AllocSpace* sp run_gc = true; break; default: - break; + LOG(FATAL) << "Invalid GC type"; } if (run_gc) { @@ -897,11 +1090,11 @@ mirror::Object* Heap::AllocateInternalWithGc(Thread* self, space::AllocSpace* sp // or the requested size is really big. Do another GC, collecting SoftReferences this time. The // VM spec requires that all SoftReferences have been collected and cleared before throwing OOME. - // OLD-TODO: wait for the finalizers from the previous GC to finish + // TODO: Run finalization, but this can cause more allocations to occur. VLOG(gc) << "Forcing collection of SoftReferences for " << PrettySize(alloc_size) << " allocation"; - // We don't need a WaitForConcurrentGcToComplete here either. + // We don't need a WaitForGcToComplete here either. CollectGarbageInternal(collector::kGcTypeFull, kGcCauseForAlloc, true); return TryToAllocateInstrumented(self, space, alloc_size, true, bytes_allocated); } @@ -914,51 +1107,24 @@ void Heap::SetTargetHeapUtilization(float target) { size_t Heap::GetObjectsAllocated() const { size_t total = 0; - typedef std::vector::const_iterator It; - for (It it = continuous_spaces_.begin(), end = continuous_spaces_.end(); it != end; ++it) { - space::ContinuousSpace* space = *it; - if (space->IsDlMallocSpace()) { - total += space->AsDlMallocSpace()->GetObjectsAllocated(); - } - } - typedef std::vector::const_iterator It2; - for (It2 it = discontinuous_spaces_.begin(), end = discontinuous_spaces_.end(); it != end; ++it) { - space::DiscontinuousSpace* space = *it; - total += space->AsLargeObjectSpace()->GetObjectsAllocated(); + for (space::AllocSpace* space : alloc_spaces_) { + total += space->GetObjectsAllocated(); } return total; } size_t Heap::GetObjectsAllocatedEver() const { size_t total = 0; - typedef std::vector::const_iterator It; - for (It it = continuous_spaces_.begin(), end = continuous_spaces_.end(); it != end; ++it) { - space::ContinuousSpace* space = *it; - if (space->IsDlMallocSpace()) { - total += space->AsDlMallocSpace()->GetTotalObjectsAllocated(); - } - } - typedef std::vector::const_iterator It2; - for (It2 it = discontinuous_spaces_.begin(), end = discontinuous_spaces_.end(); it != end; ++it) { - space::DiscontinuousSpace* space = *it; - total += space->AsLargeObjectSpace()->GetTotalObjectsAllocated(); + for (space::AllocSpace* space : alloc_spaces_) { + total += space->GetTotalObjectsAllocated(); } return total; } size_t Heap::GetBytesAllocatedEver() const { size_t total = 0; - typedef std::vector::const_iterator It; - for (It it = continuous_spaces_.begin(), end = continuous_spaces_.end(); it != end; ++it) { - space::ContinuousSpace* space = *it; - if (space->IsDlMallocSpace()) { - total += space->AsDlMallocSpace()->GetTotalBytesAllocated(); - } - } - typedef std::vector::const_iterator It2; - for (It2 it = discontinuous_spaces_.begin(), end = discontinuous_spaces_.end(); it != end; ++it) { - space::DiscontinuousSpace* space = *it; - total += space->AsLargeObjectSpace()->GetTotalBytesAllocated(); + for (space::AllocSpace* space : alloc_spaces_) { + total += space->GetTotalBytesAllocated(); } return total; } @@ -1056,8 +1222,8 @@ class ReferringObjectsFinder { // For bitmap Visit. // TODO: Fix lock analysis to not use NO_THREAD_SAFETY_ANALYSIS, requires support for // annotalysis on visitors. - void operator()(mirror::Object* obj) const NO_THREAD_SAFETY_ANALYSIS { - collector::MarkSweep::VisitObjectReferences(obj, *this, true); + void operator()(const mirror::Object* o) const NO_THREAD_SAFETY_ANALYSIS { + collector::MarkSweep::VisitObjectReferences(const_cast(o), *this, true); } // For MarkSweep::VisitObjectReferences. @@ -1093,56 +1259,69 @@ void Heap::GetReferringObjects(mirror::Object* o, int32_t max_count, void Heap::CollectGarbage(bool clear_soft_references) { // Even if we waited for a GC we still need to do another GC since weaks allocated during the // last GC will not have necessarily been cleared. - Thread* self = Thread::Current(); - WaitForConcurrentGcToComplete(self); CollectGarbageInternal(collector::kGcTypeFull, kGcCauseExplicit, clear_soft_references); } void Heap::PreZygoteFork() { static Mutex zygote_creation_lock_("zygote creation lock", kZygoteCreationLock); - // Do this before acquiring the zygote creation lock so that we don't get lock order violations. - CollectGarbage(false); Thread* self = Thread::Current(); MutexLock mu(self, zygote_creation_lock_); - // Try to see if we have any Zygote spaces. if (have_zygote_space_) { return; } - - VLOG(heap) << "Starting PreZygoteFork with alloc space size " << PrettySize(alloc_space_->Size()); - - { - // Flush the alloc stack. - WriterMutexLock mu(self, *Locks::heap_bitmap_lock_); - FlushAllocStack(); + VLOG(heap) << "Starting PreZygoteFork"; + // Do this before acquiring the zygote creation lock so that we don't get lock order violations. + CollectGarbageInternal(collector::kGcTypeFull, kGcCauseBackground, false); + // Trim the pages at the end of the non moving space. + non_moving_space_->Trim(); + non_moving_space_->GetMemMap()->Protect(PROT_READ | PROT_WRITE); + // Create a new bump pointer space which we will compact into. + if (semi_space_collector_ != nullptr) { + space::BumpPointerSpace target_space("zygote bump space", non_moving_space_->End(), + non_moving_space_->Limit()); + // Compact the bump pointer space to a new zygote bump pointer space. + temp_space_->GetMemMap()->Protect(PROT_READ | PROT_WRITE); + Compact(&target_space, bump_pointer_space_); + CHECK_EQ(temp_space_->GetBytesAllocated(), 0U); + total_objects_freed_ever_ += semi_space_collector_->GetFreedObjects(); + total_bytes_freed_ever_ += semi_space_collector_->GetFreedBytes(); + // Update the end and write out image. + non_moving_space_->SetEnd(target_space.End()); + non_moving_space_->SetLimit(target_space.Limit()); + accounting::SpaceBitmap* bitmap = non_moving_space_->GetLiveBitmap(); + // Record the allocations in the bitmap. + VLOG(heap) << "Recording zygote allocations"; + mirror::Object* obj = reinterpret_cast(target_space.Begin()); + const mirror::Object* end = reinterpret_cast(target_space.End()); + while (obj < end) { + bitmap->Set(obj); + obj = space::BumpPointerSpace::GetNextObject(obj); + } } - - // Turns the current alloc space into a Zygote space and obtain the new alloc space composed - // of the remaining available heap memory. - space::DlMallocSpace* zygote_space = alloc_space_; - alloc_space_ = zygote_space->CreateZygoteSpace("alloc space"); - alloc_space_->SetFootprintLimit(alloc_space_->Capacity()); - + // Turn the current alloc space into a zygote space and obtain the new alloc space composed of + // the remaining available heap memory. + space::DlMallocSpace* zygote_space = non_moving_space_; + non_moving_space_ = zygote_space->CreateZygoteSpace("alloc space"); + non_moving_space_->SetFootprintLimit(non_moving_space_->Capacity()); // Change the GC retention policy of the zygote space to only collect when full. zygote_space->SetGcRetentionPolicy(space::kGcRetentionPolicyFullCollect); - AddContinuousSpace(alloc_space_); + AddSpace(non_moving_space_); have_zygote_space_ = true; - + zygote_space->InvalidateMSpace(); // Create the zygote space mod union table. accounting::ModUnionTable* mod_union_table = new accounting::ModUnionTableCardCache("zygote space mod-union table", this, zygote_space); CHECK(mod_union_table != nullptr) << "Failed to create zygote space mod-union table"; AddModUnionTable(mod_union_table); - // Reset the cumulative loggers since we now have a few additional timing phases. - for (const auto& collector : mark_sweep_collectors_) { + for (const auto& collector : garbage_collectors_) { collector->ResetCumulativeStatistics(); } } void Heap::FlushAllocStack() { - MarkAllocStack(alloc_space_->GetLiveBitmap(), large_object_space_->GetLiveObjects(), + MarkAllocStack(non_moving_space_->GetLiveBitmap(), large_object_space_->GetLiveObjects(), allocation_stack_.get()); allocation_stack_->Reset(); } @@ -1161,86 +1340,111 @@ void Heap::MarkAllocStack(accounting::SpaceBitmap* bitmap, accounting::SpaceSetM } } +const char* PrettyCause(GcCause cause) { + switch (cause) { + case kGcCauseForAlloc: return "Alloc"; + case kGcCauseBackground: return "Background"; + case kGcCauseExplicit: return "Explicit"; + default: + LOG(FATAL) << "Unreachable"; + } + return ""; +} + +void Heap::SwapSemiSpaces() { + // Swap the spaces so we allocate into the space which we just evacuated. + std::swap(bump_pointer_space_, temp_space_); +} -const char* gc_cause_and_type_strings[3][4] = { - {"", "GC Alloc Sticky", "GC Alloc Partial", "GC Alloc Full"}, - {"", "GC Background Sticky", "GC Background Partial", "GC Background Full"}, - {"", "GC Explicit Sticky", "GC Explicit Partial", "GC Explicit Full"}}; +void Heap::Compact(space::ContinuousMemMapAllocSpace* target_space, + space::ContinuousMemMapAllocSpace* source_space) { + CHECK(kMovingCollector); + CHECK_NE(target_space, source_space) << "In-place compaction unsupported"; + if (target_space != source_space) { + semi_space_collector_->SetFromSpace(source_space); + semi_space_collector_->SetToSpace(target_space); + semi_space_collector_->Run(false); + } +} collector::GcType Heap::CollectGarbageInternal(collector::GcType gc_type, GcCause gc_cause, bool clear_soft_references) { Thread* self = Thread::Current(); - + Runtime* runtime = Runtime::Current(); ScopedThreadStateChange tsc(self, kWaitingPerformingGc); Locks::mutator_lock_->AssertNotHeld(self); - if (self->IsHandlingStackOverflow()) { LOG(WARNING) << "Performing GC on a thread that is handling a stack overflow."; } - - // Ensure there is only one GC at a time. - bool start_collect = false; - while (!start_collect) { - { - MutexLock mu(self, *gc_complete_lock_); - if (!is_gc_running_) { - is_gc_running_ = true; - start_collect = true; - } - } - if (!start_collect) { - // TODO: timinglog this. - WaitForConcurrentGcToComplete(self); - - // TODO: if another thread beat this one to do the GC, perhaps we should just return here? - // Not doing at the moment to ensure soft references are cleared. + { + gc_complete_lock_->AssertNotHeld(self); + MutexLock mu(self, *gc_complete_lock_); + // Ensure there is only one GC at a time. + WaitForGcToCompleteLocked(self); + // TODO: if another thread beat this one to do the GC, perhaps we should just return here? + // Not doing at the moment to ensure soft references are cleared. + // GC can be disabled if someone has a used GetPrimitiveArrayCritical. + if (gc_disable_count_ != 0) { + LOG(WARNING) << "Skipping GC due to disable count " << gc_disable_count_; + return collector::kGcTypeNone; } + is_gc_running_ = true; } - gc_complete_lock_->AssertNotHeld(self); - if (gc_cause == kGcCauseForAlloc && Runtime::Current()->HasStatsEnabled()) { - ++Runtime::Current()->GetStats()->gc_for_alloc_count; - ++Thread::Current()->GetStats()->gc_for_alloc_count; + if (gc_cause == kGcCauseForAlloc && runtime->HasStatsEnabled()) { + ++runtime->GetStats()->gc_for_alloc_count; + ++self->GetStats()->gc_for_alloc_count; } uint64_t gc_start_time_ns = NanoTime(); uint64_t gc_start_size = GetBytesAllocated(); // Approximate allocation rate in bytes / second. - if (UNLIKELY(gc_start_time_ns == last_gc_time_ns_)) { - LOG(WARNING) << "Timers are broken (gc_start_time == last_gc_time_)."; - } uint64_t ms_delta = NsToMs(gc_start_time_ns - last_gc_time_ns_); - if (ms_delta != 0) { + // Back to back GCs can cause 0 ms of wait time in between GC invocations. + if (LIKELY(ms_delta != 0)) { allocation_rate_ = ((gc_start_size - last_gc_size_) * 1000) / ms_delta; VLOG(heap) << "Allocation rate: " << PrettySize(allocation_rate_) << "/s"; } if (gc_type == collector::kGcTypeSticky && - alloc_space_->Size() < min_alloc_space_size_for_sticky_gc_) { + non_moving_space_->Size() < min_alloc_space_size_for_sticky_gc_) { gc_type = collector::kGcTypePartial; } DCHECK_LT(gc_type, collector::kGcTypeMax); DCHECK_NE(gc_type, collector::kGcTypeNone); - DCHECK_LE(gc_cause, kGcCauseExplicit); - ATRACE_BEGIN(gc_cause_and_type_strings[gc_cause][gc_type]); - - collector::MarkSweep* collector = NULL; - for (const auto& cur_collector : mark_sweep_collectors_) { - if (cur_collector->IsConcurrent() == concurrent_gc_ && cur_collector->GetGcType() == gc_type) { + collector::GarbageCollector* collector = nullptr; + if (kMovingCollector) { + gc_type = semi_space_collector_->GetGcType(); + CHECK_EQ(temp_space_->GetObjectsAllocated(), 0U); + semi_space_collector_->SetFromSpace(bump_pointer_space_); + semi_space_collector_->SetToSpace(temp_space_); + mprotect(temp_space_->Begin(), temp_space_->Capacity(), PROT_READ | PROT_WRITE); + } + for (const auto& cur_collector : garbage_collectors_) { + if (cur_collector->IsConcurrent() == concurrent_gc_ && + cur_collector->GetGcType() == gc_type) { collector = cur_collector; break; } } + if (kMovingCollector) { + gc_type = collector::kGcTypeFull; + } CHECK(collector != NULL) << "Could not find garbage collector with concurrent=" << concurrent_gc_ << " and type=" << gc_type; - collector->clear_soft_references_ = clear_soft_references; - collector->Run(); + ATRACE_BEGIN(StringPrintf("%s %s GC", PrettyCause(gc_cause), collector->GetName()).c_str()); + + collector->Run(clear_soft_references); total_objects_freed_ever_ += collector->GetFreedObjects(); total_bytes_freed_ever_ += collector->GetFreedBytes(); + + // Grow the heap so that we know when to perform the next GC. + GrowForUtilization(gc_type, collector->GetDurationNs()); + if (care_about_pause_times_) { const size_t duration = collector->GetDurationNs(); std::vector pauses = collector->GetPauseTimes(); @@ -1252,7 +1456,6 @@ collector::GcType Heap::CollectGarbageInternal(collector::GcType gc_type, GcCaus was_slow = was_slow || pause > long_pause_log_threshold_; } } - if (was_slow) { const size_t percent_free = GetPercentFree(); const size_t current_heap_size = GetBytesAllocated(); @@ -1327,7 +1530,6 @@ class VerifyReferenceVisitor { accounting::CardTable* card_table = heap_->GetCardTable(); accounting::ObjectStack* alloc_stack = heap_->allocation_stack_.get(); accounting::ObjectStack* live_stack = heap_->live_stack_.get(); - if (!failed_) { // Print message on only on first failure to prevent spam. LOG(ERROR) << "!!!!!!!!!!!!!!Heap corruption detected!!!!!!!!!!!!!!!!!!!"; @@ -1337,7 +1539,7 @@ class VerifyReferenceVisitor { byte* card_addr = card_table->CardFromAddr(obj); LOG(ERROR) << "Object " << obj << " references dead object " << ref << " at offset " << offset << "\n card value = " << static_cast(*card_addr); - if (heap_->IsHeapAddress(obj->GetClass())) { + if (heap_->IsValidObjectAddress(obj->GetClass())) { LOG(ERROR) << "Obj type " << PrettyTypeOf(obj); } else { LOG(ERROR) << "Object " << obj << " class(" << obj->GetClass() << ") not a heap address"; @@ -1345,7 +1547,7 @@ class VerifyReferenceVisitor { // Attmept to find the class inside of the recently freed objects. space::ContinuousSpace* ref_space = heap_->FindContinuousSpaceFromObject(ref, true); - if (ref_space->IsDlMallocSpace()) { + if (ref_space != nullptr && ref_space->IsDlMallocSpace()) { space::DlMallocSpace* space = ref_space->AsDlMallocSpace(); mirror::Class* ref_class = space->FindRecentFreedObject(ref); if (ref_class != nullptr) { @@ -1356,7 +1558,7 @@ class VerifyReferenceVisitor { } } - if (ref->GetClass() != nullptr && heap_->IsHeapAddress(ref->GetClass()) && + if (ref->GetClass() != nullptr && heap_->IsValidObjectAddress(ref->GetClass()) && ref->GetClass()->IsClass()) { LOG(ERROR) << "Ref type " << PrettyTypeOf(ref); } else { @@ -1427,17 +1629,25 @@ class VerifyObjectVisitor { public: explicit VerifyObjectVisitor(Heap* heap) : heap_(heap), failed_(false) {} - void operator()(const mirror::Object* obj) const + void operator()(mirror::Object* obj) const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_, Locks::heap_bitmap_lock_) { // Note: we are verifying the references in obj but not obj itself, this is because obj must // be live or else how did we find it in the live bitmap? VerifyReferenceVisitor visitor(heap_); // The class doesn't count as a reference but we should verify it anyways. - visitor(obj, obj->GetClass(), MemberOffset(0), false); - collector::MarkSweep::VisitObjectReferences(const_cast(obj), visitor, true); + collector::MarkSweep::VisitObjectReferences(obj, visitor, true); + if (obj->GetClass()->IsReferenceClass()) { + visitor(obj, heap_->GetReferenceReferent(obj), MemberOffset(0), false); + } failed_ = failed_ || visitor.Failed(); } + static void VisitCallback(mirror::Object* obj, void* arg) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_, Locks::heap_bitmap_lock_) { + VerifyObjectVisitor* visitor = reinterpret_cast(arg); + visitor->operator()(obj); + } + bool Failed() const { return failed_; } @@ -1453,18 +1663,15 @@ bool Heap::VerifyHeapReferences() { // Lets sort our allocation stacks so that we can efficiently binary search them. allocation_stack_->Sort(); live_stack_->Sort(); - // Perform the verification. VerifyObjectVisitor visitor(this); - Runtime::Current()->VisitRoots(VerifyReferenceVisitor::VerifyRoots, &visitor, false, false); - GetLiveBitmap()->Visit(visitor); // Verify objects in the allocation stack since these will be objects which were: // 1. Allocated prior to the GC (pre GC verification). // 2. Allocated during the GC (pre sweep GC verification). - for (mirror::Object** it = allocation_stack_->Begin(); it != allocation_stack_->End(); ++it) { - visitor(*it); - } // We don't want to verify the objects in the live stack since they themselves may be // pointing to dead objects if they are not reachable. + VisitObjects(VerifyObjectVisitor::VisitCallback, &visitor); + // Verify the roots: + Runtime::Current()->VisitRoots(VerifyReferenceVisitor::VerifyRoots, &visitor, false, false); if (visitor.Failed()) { // Dump mod-union tables. for (const auto& table_pair : mod_union_tables_) { @@ -1557,7 +1764,7 @@ class VerifyLiveStackReferences { void operator()(mirror::Object* obj) const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_, Locks::heap_bitmap_lock_) { VerifyReferenceCardVisitor visitor(heap_, const_cast(&failed_)); - collector::MarkSweep::VisitObjectReferences(obj, visitor, true); + collector::MarkSweep::VisitObjectReferences(const_cast(obj), visitor, true); } bool Failed() const { @@ -1610,10 +1817,14 @@ void Heap::ProcessCards(base::TimingLogger& timings) { "ImageModUnionClearCards"; base::TimingLogger::ScopedSplit split(name, &timings); table->ClearCards(); - } else { + } else if (space->GetType() != space::kSpaceTypeBumpPointerSpace) { base::TimingLogger::ScopedSplit split("AllocSpaceClearCards", &timings); // No mod union table for the AllocSpace. Age the cards so that the GC knows that these cards // were dirty before the GC started. + // TODO: Don't need to use atomic. + // The races are we either end up with: Aged card, unaged card. Since we have the checkpoint + // roots and then we scan / update mod union tables after. We will always scan either card.// + // If we end up with the non aged card, we scan it it in the pause. card_table_->ModifyCardsAtomic(space->Begin(), space->End(), AgeCardVisitor(), VoidFunctor()); } } @@ -1692,36 +1903,27 @@ void Heap::PostGcVerification(collector::GarbageCollector* gc) { } } -collector::GcType Heap::WaitForConcurrentGcToComplete(Thread* self) { +collector::GcType Heap::WaitForGcToComplete(Thread* self) { + ScopedThreadStateChange tsc(self, kWaitingForGcToComplete); + MutexLock mu(self, *gc_complete_lock_); + return WaitForGcToCompleteLocked(self); +} + +collector::GcType Heap::WaitForGcToCompleteLocked(Thread* self) { collector::GcType last_gc_type = collector::kGcTypeNone; - if (concurrent_gc_) { - ATRACE_BEGIN("GC: Wait For Concurrent"); - bool do_wait; - uint64_t wait_start = NanoTime(); - { - // Check if GC is running holding gc_complete_lock_. - MutexLock mu(self, *gc_complete_lock_); - do_wait = is_gc_running_; - } - if (do_wait) { - uint64_t wait_time; - // We must wait, change thread state then sleep on gc_complete_cond_; - ScopedThreadStateChange tsc(Thread::Current(), kWaitingForGcToComplete); - { - MutexLock mu(self, *gc_complete_lock_); - while (is_gc_running_) { - gc_complete_cond_->Wait(self); - } - last_gc_type = last_gc_type_; - wait_time = NanoTime() - wait_start; - total_wait_time_ += wait_time; - } - if (wait_time > long_pause_log_threshold_) { - LOG(INFO) << "WaitForConcurrentGcToComplete blocked for " << PrettyDuration(wait_time); - } - } + uint64_t wait_start = NanoTime(); + while (is_gc_running_) { + ATRACE_BEGIN("GC: Wait For Completion"); + // We must wait, change thread state then sleep on gc_complete_cond_; + gc_complete_cond_->Wait(self); + last_gc_type = last_gc_type_; ATRACE_END(); } + uint64_t wait_time = NanoTime() - wait_start; + total_wait_time_ += wait_time; + if (wait_time > long_pause_log_threshold_) { + LOG(INFO) << "WaitForGcToComplete blocked for " << PrettyDuration(wait_time); + } return last_gc_type; } @@ -1744,6 +1946,23 @@ void Heap::SetIdealFootprint(size_t max_allowed_footprint) { max_allowed_footprint_ = max_allowed_footprint; } +bool Heap::IsMovableObject(const mirror::Object* obj) const { + if (kMovingCollector) { + DCHECK(!IsInTempSpace(obj)); + if (bump_pointer_space_->HasAddress(obj)) { + return true; + } + } + return false; +} + +bool Heap::IsInTempSpace(const mirror::Object* obj) const { + if (temp_space_->HasAddress(obj) && !temp_space_->Contains(obj)) { + return true; + } + return false; +} + void Heap::UpdateMaxNativeFootprint() { size_t native_size = native_bytes_allocated_; // TODO: Tune the native heap utilization to be a value other than the java heap utilization. @@ -1773,6 +1992,7 @@ void Heap::GrowForUtilization(collector::GcType gc_type, uint64_t gc_duration) { } else if (target_size < bytes_allocated + min_free_) { target_size = bytes_allocated + min_free_; } + native_need_to_run_finalization_ = true; next_gc_type_ = collector::kGcTypeSticky; } else { // Based on how close the current heap size is to the target size, decide @@ -1796,7 +2016,6 @@ void Heap::GrowForUtilization(collector::GcType gc_type, uint64_t gc_duration) { if (concurrent_gc_) { // Calculate when to perform the next ConcurrentGC. - // Calculate the estimated GC duration. double gc_duration_seconds = NsToMs(gc_duration) / 1000.0; // Estimate how many remaining bytes we will have when we need to start the next GC. @@ -1817,13 +2036,11 @@ void Heap::GrowForUtilization(collector::GcType gc_type, uint64_t gc_duration) { DCHECK_LE(max_allowed_footprint_, growth_limit_); } } - - UpdateMaxNativeFootprint(); } void Heap::ClearGrowthLimit() { growth_limit_ = capacity_; - alloc_space_->ClearGrowthLimit(); + non_moving_space_->ClearGrowthLimit(); } void Heap::SetReferenceOffsets(MemberOffset reference_referent_offset, @@ -1843,6 +2060,12 @@ void Heap::SetReferenceOffsets(MemberOffset reference_referent_offset, CHECK_NE(finalizer_reference_zombie_offset_.Uint32Value(), 0U); } +void Heap::SetReferenceReferent(mirror::Object* reference, mirror::Object* referent) { + DCHECK(reference != NULL); + DCHECK_NE(reference_referent_offset_.Uint32Value(), 0U); + reference->SetFieldObject(reference_referent_offset_, referent, true); +} + mirror::Object* Heap::GetReferenceReferent(mirror::Object* reference) { DCHECK(reference != NULL); DCHECK_NE(reference_referent_offset_.Uint32Value(), 0U); @@ -1852,7 +2075,7 @@ mirror::Object* Heap::GetReferenceReferent(mirror::Object* reference) { void Heap::ClearReferenceReferent(mirror::Object* reference) { DCHECK(reference != NULL); DCHECK_NE(reference_referent_offset_.Uint32Value(), 0U); - reference->SetFieldObject(reference_referent_offset_, NULL, true); + reference->SetFieldObject(reference_referent_offset_, nullptr, true); } // Returns true if the reference object has not yet been enqueued. @@ -1924,19 +2147,41 @@ void Heap::AddFinalizerReference(Thread* self, mirror::Object* object) { arg_array.GetArray(), arg_array.GetNumBytes(), &result, 'V'); } +void Heap::PrintReferenceQueue(std::ostream& os, mirror::Object** queue) { + os << "Refernece queue " << queue << "\n"; + if (queue != nullptr) { + mirror::Object* list = *queue; + if (list != nullptr) { + mirror::Object* cur = list; + do { + mirror::Object* pending_next = + cur->GetFieldObject(reference_pendingNext_offset_, false); + os << "PendingNext=" << pending_next; + if (cur->GetClass()->IsFinalizerReferenceClass()) { + os << " Zombie=" << + cur->GetFieldObject(finalizer_reference_zombie_offset_, false); + } + os << "\n"; + cur = pending_next; + } while (cur != list); + } + } +} + void Heap::EnqueueClearedReferences(mirror::Object** cleared) { - DCHECK(cleared != NULL); - if (*cleared != NULL) { + DCHECK(cleared != nullptr); + mirror::Object* list = *cleared; + if (list != nullptr) { // When a runtime isn't started there are no reference queues to care about so ignore. if (LIKELY(Runtime::Current()->IsStarted())) { ScopedObjectAccess soa(Thread::Current()); JValue result; ArgArray arg_array(NULL, 0); - arg_array.Append(reinterpret_cast(*cleared)); + arg_array.Append(reinterpret_cast(list)); soa.DecodeMethod(WellKnownClasses::java_lang_ref_ReferenceQueue_add)->Invoke(soa.Self(), arg_array.GetArray(), arg_array.GetNumBytes(), &result, 'V'); } - *cleared = NULL; + *cleared = nullptr; } } @@ -1944,42 +2189,27 @@ void Heap::RequestConcurrentGC(Thread* self) { // Make sure that we can do a concurrent GC. Runtime* runtime = Runtime::Current(); DCHECK(concurrent_gc_); - if (runtime == NULL || !runtime->IsFinishedStarting() || - !runtime->IsConcurrentGcEnabled()) { - return; - } - { - MutexLock mu(self, *Locks::runtime_shutdown_lock_); - if (runtime->IsShuttingDown()) { - return; - } - } - if (self->IsHandlingStackOverflow()) { + if (runtime == NULL || !runtime->IsFinishedStarting() || runtime->IsShuttingDown(self) || + self->IsHandlingStackOverflow()) { return; } - // We already have a request pending, no reason to start more until we update // concurrent_start_bytes_. concurrent_start_bytes_ = std::numeric_limits::max(); - JNIEnv* env = self->GetJniEnv(); - DCHECK(WellKnownClasses::java_lang_Daemons != NULL); - DCHECK(WellKnownClasses::java_lang_Daemons_requestGC != NULL); + DCHECK(WellKnownClasses::java_lang_Daemons != nullptr); + DCHECK(WellKnownClasses::java_lang_Daemons_requestGC != nullptr); env->CallStaticVoidMethod(WellKnownClasses::java_lang_Daemons, WellKnownClasses::java_lang_Daemons_requestGC); CHECK(!env->ExceptionCheck()); } void Heap::ConcurrentGC(Thread* self) { - { - MutexLock mu(self, *Locks::runtime_shutdown_lock_); - if (Runtime::Current()->IsShuttingDown()) { - return; - } + if (Runtime::Current()->IsShuttingDown(self)) { + return; } - // Wait for any GCs currently running to finish. - if (WaitForConcurrentGcToComplete(self) == collector::kGcTypeNone) { + if (WaitForGcToComplete(self) == collector::kGcTypeNone) { CollectGarbageInternal(next_gc_type_, kGcCauseBackground, false); } } @@ -1998,26 +2228,18 @@ void Heap::RequestHeapTrim() { // We could try mincore(2) but that's only a measure of how many pages we haven't given away, // not how much use we're making of those pages. uint64_t ms_time = MilliTime(); - // Note the large object space's bytes allocated is equal to its capacity. - uint64_t los_bytes_allocated = large_object_space_->GetBytesAllocated(); - float utilization = static_cast(GetBytesAllocated() - los_bytes_allocated) / - (GetTotalMemory() - los_bytes_allocated); - if ((utilization > 0.75f && !IsLowMemoryMode()) || ((ms_time - last_trim_time_ms_) < 2 * 1000)) { - // Don't bother trimming the alloc space if it's more than 75% utilized and low memory mode is - // not enabled, or if a heap trim occurred in the last two seconds. + // Don't bother trimming the alloc space if a heap trim occurred in the last two seconds. + if (ms_time - last_trim_time_ms_ < 2 * 1000) { return; } Thread* self = Thread::Current(); - { - MutexLock mu(self, *Locks::runtime_shutdown_lock_); - Runtime* runtime = Runtime::Current(); - if (runtime == NULL || !runtime->IsFinishedStarting() || runtime->IsShuttingDown()) { - // Heap trimming isn't supported without a Java runtime or Daemons (such as at dex2oat time) - // Also: we do not wish to start a heap trim if the runtime is shutting down (a racy check - // as we don't hold the lock while requesting the trim). - return; - } + Runtime* runtime = Runtime::Current(); + if (runtime == nullptr || !runtime->IsFinishedStarting() || runtime->IsShuttingDown(self)) { + // Heap trimming isn't supported without a Java runtime or Daemons (such as at dex2oat time) + // Also: we do not wish to start a heap trim if the runtime is shutting down (a racy check + // as we don't hold the lock while requesting the trim). + return; } last_trim_time_ms_ = ms_time; @@ -2034,50 +2256,55 @@ void Heap::RequestHeapTrim() { } } -size_t Heap::Trim() { - // Handle a requested heap trim on a thread outside of the main GC thread. - return alloc_space_->Trim(); -} - bool Heap::IsGCRequestPending() const { return concurrent_start_bytes_ != std::numeric_limits::max(); } +void Heap::RunFinalization(JNIEnv* env) { + // Can't do this in WellKnownClasses::Init since System is not properly set up at that point. + if (WellKnownClasses::java_lang_System_runFinalization == nullptr) { + CHECK(WellKnownClasses::java_lang_System != nullptr); + WellKnownClasses::java_lang_System_runFinalization = + CacheMethod(env, WellKnownClasses::java_lang_System, true, "runFinalization", "()V"); + CHECK(WellKnownClasses::java_lang_System_runFinalization != nullptr); + } + env->CallStaticVoidMethod(WellKnownClasses::java_lang_System, + WellKnownClasses::java_lang_System_runFinalization); +} + void Heap::RegisterNativeAllocation(JNIEnv* env, int bytes) { + Thread* self = ThreadForEnv(env); + if (native_need_to_run_finalization_) { + RunFinalization(env); + UpdateMaxNativeFootprint(); + native_need_to_run_finalization_ = false; + } // Total number of native bytes allocated. native_bytes_allocated_.fetch_add(bytes); if (static_cast(native_bytes_allocated_) > native_footprint_gc_watermark_) { // The second watermark is higher than the gc watermark. If you hit this it means you are // allocating native objects faster than the GC can keep up with. if (static_cast(native_bytes_allocated_) > native_footprint_limit_) { - // Can't do this in WellKnownClasses::Init since System is not properly set up at that - // point. - if (UNLIKELY(WellKnownClasses::java_lang_System_runFinalization == NULL)) { - DCHECK(WellKnownClasses::java_lang_System != NULL); - WellKnownClasses::java_lang_System_runFinalization = - CacheMethod(env, WellKnownClasses::java_lang_System, true, "runFinalization", "()V"); - CHECK(WellKnownClasses::java_lang_System_runFinalization != NULL); - } - if (WaitForConcurrentGcToComplete(ThreadForEnv(env)) != collector::kGcTypeNone) { - // Just finished a GC, attempt to run finalizers. - env->CallStaticVoidMethod(WellKnownClasses::java_lang_System, - WellKnownClasses::java_lang_System_runFinalization); - CHECK(!env->ExceptionCheck()); - } - - // If we still are over the watermark, attempt a GC for alloc and run finalizers. - if (static_cast(native_bytes_allocated_) > native_footprint_limit_) { - CollectGarbageInternal(collector::kGcTypePartial, kGcCauseForAlloc, false); - env->CallStaticVoidMethod(WellKnownClasses::java_lang_System, - WellKnownClasses::java_lang_System_runFinalization); - CHECK(!env->ExceptionCheck()); - } - // We have just run finalizers, update the native watermark since it is very likely that - // finalizers released native managed allocations. - UpdateMaxNativeFootprint(); - } else { - if (!IsGCRequestPending()) { - RequestConcurrentGC(ThreadForEnv(env)); + if (WaitForGcToComplete(self) != collector::kGcTypeNone) { + // Just finished a GC, attempt to run finalizers. + RunFinalization(env); + CHECK(!env->ExceptionCheck()); + } + // If we still are over the watermark, attempt a GC for alloc and run finalizers. + if (static_cast(native_bytes_allocated_) > native_footprint_limit_) { + CollectGarbageInternal(collector::kGcTypePartial, kGcCauseForAlloc, false); + RunFinalization(env); + native_need_to_run_finalization_ = false; + CHECK(!env->ExceptionCheck()); + } + // We have just run finalizers, update the native watermark since it is very likely that + // finalizers released native managed allocations. + UpdateMaxNativeFootprint(); + } else if (!IsGCRequestPending()) { + if (concurrent_gc_) { + RequestConcurrentGC(self); + } else { + CollectGarbageInternal(collector::kGcTypePartial, kGcCauseForAlloc, false); } } } @@ -2086,26 +2313,24 @@ void Heap::RegisterNativeAllocation(JNIEnv* env, int bytes) { void Heap::RegisterNativeFree(JNIEnv* env, int bytes) { int expected_size, new_size; do { - expected_size = native_bytes_allocated_.load(); - new_size = expected_size - bytes; - if (UNLIKELY(new_size < 0)) { - ScopedObjectAccess soa(env); - env->ThrowNew(WellKnownClasses::java_lang_RuntimeException, - StringPrintf("Attempted to free %d native bytes with only %d native bytes " - "registered as allocated", bytes, expected_size).c_str()); - break; - } + expected_size = native_bytes_allocated_.load(); + new_size = expected_size - bytes; + if (UNLIKELY(new_size < 0)) { + ScopedObjectAccess soa(env); + env->ThrowNew(WellKnownClasses::java_lang_RuntimeException, + StringPrintf("Attempted to free %d native bytes with only %d native bytes " + "registered as allocated", bytes, expected_size).c_str()); + break; + } } while (!native_bytes_allocated_.compare_and_swap(expected_size, new_size)); } int64_t Heap::GetTotalMemory() const { int64_t ret = 0; for (const auto& space : continuous_spaces_) { - if (space->IsImageSpace()) { - // Currently don't include the image space. - } else if (space->IsDlMallocSpace()) { - // Zygote or alloc space - ret += space->AsDlMallocSpace()->GetFootprint(); + // Currently don't include the image space. + if (!space->IsImageSpace()) { + ret += space->Size(); } } for (const auto& space : discontinuous_spaces_) { diff --git a/runtime/gc/heap.h b/runtime/gc/heap.h index 91909e4f07a..0fa000f18d9 100644 --- a/runtime/gc/heap.h +++ b/runtime/gc/heap.h @@ -31,6 +31,7 @@ #include "jni.h" #include "locks.h" #include "offsets.h" +#include "root_visitor.h" #include "safe_map.h" #include "thread_pool.h" @@ -57,16 +58,19 @@ namespace accounting { namespace collector { class GarbageCollector; class MarkSweep; + class SemiSpace; } // namespace collector namespace space { class AllocSpace; + class BumpPointerSpace; class DiscontinuousSpace; class DlMallocSpace; class ImageSpace; class LargeObjectSpace; class Space; class SpaceTest; + class ContinuousMemMapAllocSpace; } // namespace space class AgeCardVisitor { @@ -101,13 +105,13 @@ enum HeapVerificationMode { }; static constexpr HeapVerificationMode kDesiredHeapVerification = kNoHeapVerification; -// If true, measure the total allocation time. -static constexpr bool kMeasureAllocationTime = false; -// Primitive arrays larger than this size are put in the large object space. -static constexpr size_t kLargeObjectThreshold = 3 * kPageSize; - class Heap { public: + // If true, measure the total allocation time. + static constexpr bool kMeasureAllocationTime = false; + // Primitive arrays larger than this size are put in the large object space. + static constexpr size_t kLargeObjectThreshold = 3 * kPageSize; + static constexpr size_t kDefaultInitialSize = 2 * MB; static constexpr size_t kDefaultMaximumSize = 32 * MB; static constexpr size_t kDefaultMaxFree = 2 * MB; @@ -135,14 +139,47 @@ class Heap { // Allocates and initializes storage for an object instance. mirror::Object* AllocObject(Thread* self, mirror::Class* klass, size_t num_bytes) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + CHECK(!kMovingClasses); return AllocObjectInstrumented(self, klass, num_bytes); } + // Allocates and initializes storage for an object instance. + mirror::Object* AllocNonMovableObject(Thread* self, mirror::Class* klass, size_t num_bytes) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + CHECK(!kMovingClasses); + return AllocNonMovableObjectInstrumented(self, klass, num_bytes); + } mirror::Object* AllocObjectInstrumented(Thread* self, mirror::Class* klass, size_t num_bytes) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + CHECK(!kMovingClasses); + if (kMovingCollector) { + return AllocMovableObjectInstrumented(self, klass, num_bytes); + } else { + return AllocNonMovableObjectInstrumented(self, klass, num_bytes); + } + } mirror::Object* AllocObjectUninstrumented(Thread* self, mirror::Class* klass, size_t num_bytes) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + CHECK(!kMovingClasses); + if (kMovingCollector) { + return AllocMovableObjectUninstrumented(self, klass, num_bytes); + } else { + return AllocNonMovableObjectUninstrumented(self, klass, num_bytes); + } + } + mirror::Object* AllocNonMovableObjectInstrumented(Thread* self, mirror::Class* klass, + size_t num_bytes) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + mirror::Object* AllocNonMovableObjectUninstrumented(Thread* self, mirror::Class* klass, + size_t num_bytes) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - void DebugCheckPreconditionsForAllobObject(mirror::Class* c, size_t byte_count) + // Visit all of the live objects in the heap. + void VisitObjects(ObjectVisitorCallback callback, void* arg) + SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_, Locks::mutator_lock_); + + void SwapSemiSpaces() EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_); + + void DebugCheckPreconditionsForAllocObject(mirror::Class* c, size_t byte_count) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); void ThrowOutOfMemoryError(size_t byte_count, bool large_object_allocation); @@ -152,7 +189,7 @@ class Heap { // The given reference is believed to be to an object in the Java heap, check the soundness of it. void VerifyObjectImpl(const mirror::Object* o); void VerifyObject(const mirror::Object* o) { - if (o != NULL && this != NULL && verify_object_mode_ > kNoHeapVerification) { + if (o != nullptr && this != nullptr && verify_object_mode_ > kNoHeapVerification) { VerifyObjectImpl(o); } } @@ -169,7 +206,10 @@ class Heap { // A weaker test than IsLiveObject or VerifyObject that doesn't require the heap lock, // and doesn't abort on error, allowing the caller to report more // meaningful diagnostics. - bool IsHeapAddress(const mirror::Object* obj); + bool IsValidObjectAddress(const mirror::Object* obj) const; + + // Returns true if the address passed in is a heap address, doesn't need to be aligned. + bool IsHeapAddress(const mirror::Object* obj) const; // Returns true if 'obj' is a live heap object, false otherwise (including for invalid addresses). // Requires the heap lock to be held. @@ -177,6 +217,17 @@ class Heap { bool search_live_stack = true, bool sorted = false) SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_); + // Returns true if there is any chance that the object (obj) will move. + bool IsMovableObject(const mirror::Object* obj) const; + + // Returns true if an object is in the temp space, if this happens its usually indicative of + // compaction related errors. + bool IsInTempSpace(const mirror::Object* obj) const; + + // Enables us to prevent GC until objects are released. + void IncrementDisableGC(Thread* self); + void DecrementDisableGC(Thread* self); + // Initiates an explicit garbage collection. void CollectGarbage(bool clear_soft_references) LOCKS_EXCLUDED(Locks::mutator_lock_); @@ -221,9 +272,9 @@ class Heap { // from the system. Doesn't allow the space to exceed its growth limit. void SetIdealFootprint(size_t max_allowed_footprint); - // Blocks the caller until the garbage collector becomes idle and returns - // true if we waited for the GC to complete. - collector::GcType WaitForConcurrentGcToComplete(Thread* self) LOCKS_EXCLUDED(gc_complete_lock_); + // Blocks the caller until the garbage collector becomes idle and returns the type of GC we + // waited for. + collector::GcType WaitForGcToComplete(Thread* self) LOCKS_EXCLUDED(gc_complete_lock_); const std::vector& GetContinuousSpaces() const { return continuous_spaces_; @@ -239,7 +290,10 @@ class Heap { MemberOffset reference_pendingNext_offset, MemberOffset finalizer_reference_zombie_offset); - mirror::Object* GetReferenceReferent(mirror::Object* reference); + void SetReferenceReferent(mirror::Object* reference, mirror::Object* referent) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + mirror::Object* GetReferenceReferent(mirror::Object* reference) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); void ClearReferenceReferent(mirror::Object* reference) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); // Returns true if the reference object has not yet been enqueued. @@ -316,7 +370,7 @@ class Heap { } // Returns the number of objects currently allocated. - size_t GetObjectsAllocated() const; + size_t GetObjectsAllocated() const LOCKS_EXCLUDED(Locks::heap_bitmap_lock_); // Returns the total number of objects allocated since the heap was created. size_t GetObjectsAllocatedEver() const; @@ -361,7 +415,8 @@ class Heap { void DumpForSigQuit(std::ostream& os); - size_t Trim(); + // Trim the managed and native heaps by releasing unused memory back to the OS. + void Trim(); accounting::HeapBitmap* GetLiveBitmap() SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_) { return live_bitmap_.get(); @@ -375,7 +430,7 @@ class Heap { return live_stack_.get(); } - void PreZygoteFork() LOCKS_EXCLUDED(Locks::heap_bitmap_lock_); + void PreZygoteFork() NO_THREAD_SAFETY_ANALYSIS; // Mark and empty stack. void FlushAllocStack() @@ -386,6 +441,10 @@ class Heap { accounting::ObjectStack* stack) EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_); + // Mark the specified allocation stack as live. + void MarkAllocStackAsLive(accounting::ObjectStack* stack) + EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_); + // Gets called when we get notified by ActivityThread that the process state has changed. void ListenForProcessStateChange(); @@ -393,8 +452,8 @@ class Heap { // Assumes there is only one image space. space::ImageSpace* GetImageSpace() const; - space::DlMallocSpace* GetAllocSpace() const { - return alloc_space_; + space::DlMallocSpace* GetNonMovingSpace() const { + return non_moving_space_; } space::LargeObjectSpace* GetLargeObjectsSpace() const { @@ -417,7 +476,7 @@ class Heap { return phantom_ref_queue_lock_; } - void DumpSpaces(); + void DumpSpaces(std::ostream& stream = LOG(INFO)); // GC performance measuring void DumpGcPerformanceInfo(std::ostream& os); @@ -442,7 +501,20 @@ class Heap { accounting::ModUnionTable* FindModUnionTableFromSpace(space::Space* space); void AddModUnionTable(accounting::ModUnionTable* mod_union_table); + mirror::Object* AllocMovableObjectInstrumented(Thread* self, mirror::Class* klass, + size_t num_bytes) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + mirror::Object* AllocMovableObjectUninstrumented(Thread* self, mirror::Class* klass, + size_t num_bytes) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + bool IsCompilingBoot() const; + bool HasImageSpace() const; + private: + void Compact(space::ContinuousMemMapAllocSpace* target_space, + space::ContinuousMemMapAllocSpace* source_space); + bool TryAllocLargeObjectInstrumented(Thread* self, mirror::Class* c, size_t byte_count, mirror::Object** obj_ptr, size_t* bytes_allocated) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); @@ -471,6 +543,11 @@ class Heap { LOCKS_EXCLUDED(Locks::thread_suspend_count_lock_) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + // Allocate into a specific space. + mirror::Object* AllocateInto(Thread* self, space::AllocSpace* space, mirror::Class* c, + size_t bytes) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + // Try to allocate a number of bytes, this function never does any GCs. mirror::Object* TryToAllocateInstrumented(Thread* self, space::AllocSpace* space, size_t alloc_size, bool grow, size_t* bytes_allocated) @@ -500,6 +577,17 @@ class Heap { // Pushes a list of cleared references out to the managed heap. void EnqueueClearedReferences(mirror::Object** cleared_references); + // Print a reference queue. + void PrintReferenceQueue(std::ostream& os, mirror::Object** queue); + + // Run the finalizers. + void RunFinalization(JNIEnv* env); + + // Blocks the caller until the garbage collector becomes idle and returns the type of GC we + // waited for. + collector::GcType WaitForGcToCompleteLocked(Thread* self) + EXCLUSIVE_LOCKS_REQUIRED(gc_complete_lock_); + void RequestHeapTrim() LOCKS_EXCLUDED(Locks::runtime_shutdown_lock_); void RequestConcurrentGC(Thread* self) LOCKS_EXCLUDED(Locks::runtime_shutdown_lock_); bool IsGCRequestPending() const; @@ -537,9 +625,7 @@ class Heap { size_t GetPercentFree(); - void AddContinuousSpace(space::ContinuousSpace* space) LOCKS_EXCLUDED(Locks::heap_bitmap_lock_); - void AddDiscontinuousSpace(space::DiscontinuousSpace* space) - LOCKS_EXCLUDED(Locks::heap_bitmap_lock_); + void AddSpace(space::Space* space) LOCKS_EXCLUDED(Locks::heap_bitmap_lock_); // No thread saftey analysis since we call this everywhere and it is impossible to find a proper // lock ordering for it. @@ -560,8 +646,12 @@ class Heap { // All-known discontinuous spaces, where objects may be placed throughout virtual memory. std::vector discontinuous_spaces_; - // The allocation space we are currently allocating into. - space::DlMallocSpace* alloc_space_; + // All-known alloc spaces, where objects may be or have been allocated. + std::vector alloc_spaces_; + + // A space where non-movable objects are allocated, when compaction is enabled it contains + // Classes, ArtMethods, ArtFields, and non moving objects. + space::DlMallocSpace* non_moving_space_; // The large object space we are currently allocating into. space::LargeObjectSpace* large_object_space_; @@ -599,6 +689,11 @@ class Heap { // If we have a zygote space. bool have_zygote_space_; + // Number of pinned primitive arrays in the movable space. + // Block all GC until this hits zero, or we hit the timeout! + size_t number_gc_blockers_; + static constexpr size_t KGCBlockTimeout = 30000; + // Guards access to the state of GC, associated conditional variable is used to signal when a GC // completes. Mutex* gc_complete_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER; @@ -635,6 +730,9 @@ class Heap { // The watermark at which a GC is performed inside of registerNativeAllocation. size_t native_footprint_limit_; + // Whether or not we need to run finalizers in the next native allocation. + bool native_need_to_run_finalization_; + // Activity manager members. jclass activity_thread_class_; jclass application_thread_class_; @@ -714,6 +812,11 @@ class Heap { // Second allocation stack so that we can process allocation with the heap unlocked. UniquePtr live_stack_; + // Bump pointer spaces. + space::BumpPointerSpace* bump_pointer_space_; + // Temp space is the space which the semispace collector copies to. + space::BumpPointerSpace* temp_space_; + // offset of java.lang.ref.Reference.referent MemberOffset reference_referent_offset_; @@ -748,11 +851,16 @@ class Heap { // The current state of heap verification, may be enabled or disabled. HeapVerificationMode verify_object_mode_; - std::vector mark_sweep_collectors_; + // GC disable count, error on GC if > 0. + size_t gc_disable_count_ GUARDED_BY(gc_complete_lock_); + + std::vector garbage_collectors_; + collector::SemiSpace* semi_space_collector_; const bool running_on_valgrind_; friend class collector::MarkSweep; + friend class collector::SemiSpace; friend class VerifyReferenceCardVisitor; friend class VerifyReferenceVisitor; friend class VerifyObjectVisitor; diff --git a/runtime/gc/heap_test.cc b/runtime/gc/heap_test.cc index 02708e83417..8af2725e1dc 100644 --- a/runtime/gc/heap_test.cc +++ b/runtime/gc/heap_test.cc @@ -43,12 +43,14 @@ TEST_F(HeapTest, GarbageCollectClassLinkerInit) { ScopedObjectAccess soa(Thread::Current()); // garbage is created during ClassLinker::Init - mirror::Class* c = class_linker_->FindSystemClass("[Ljava/lang/Object;"); + SirtRef c(soa.Self(), class_linker_->FindSystemClass("[Ljava/lang/Object;")); for (size_t i = 0; i < 1024; ++i) { SirtRef > array(soa.Self(), - mirror::ObjectArray::Alloc(soa.Self(), c, 2048)); + mirror::ObjectArray::Alloc(soa.Self(), c.get(), 2048)); for (size_t j = 0; j < 2048; ++j) { - array->Set(j, mirror::String::AllocFromModifiedUtf8(soa.Self(), "hello, world!")); + mirror::String* string = mirror::String::AllocFromModifiedUtf8(soa.Self(), "hello, world!"); + // SIRT operator -> deferences the SIRT before running the method. + array->Set(j, string); } } } diff --git a/runtime/gc/space/bump_pointer_space-inl.h b/runtime/gc/space/bump_pointer_space-inl.h new file mode 100644 index 00000000000..85ef2f432f2 --- /dev/null +++ b/runtime/gc/space/bump_pointer_space-inl.h @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ART_RUNTIME_GC_SPACE_BUMP_POINTER_SPACE_INL_H_ +#define ART_RUNTIME_GC_SPACE_BUMP_POINTER_SPACE_INL_H_ + +#include "bump_pointer_space.h" + +namespace art { +namespace gc { +namespace space { + +inline mirror::Object* BumpPointerSpace::AllocNonvirtual(size_t num_bytes) { + num_bytes = RoundUp(num_bytes, kAlignment); + byte* old_end; + byte* new_end; + do { + old_end = end_; + new_end = old_end + num_bytes; + // If there is no more room in the region, we are out of memory. + if (UNLIKELY(new_end > growth_end_)) { + return nullptr; + } + // TODO: Use a cas which always equals the size of pointers. + } while (android_atomic_cas(reinterpret_cast(old_end), + reinterpret_cast(new_end), + reinterpret_cast(&end_)) != 0); + // TODO: Less statistics? + total_bytes_allocated_.fetch_add(num_bytes); + num_objects_allocated_.fetch_add(1); + total_objects_allocated_.fetch_add(1); + return reinterpret_cast(old_end); +} + +} // namespace space +} // namespace gc +} // namespace art + +#endif // ART_RUNTIME_GC_SPACE_BUMP_POINTER_SPACE_INL_H_ diff --git a/runtime/gc/space/bump_pointer_space.cc b/runtime/gc/space/bump_pointer_space.cc new file mode 100644 index 00000000000..06ba57e03ae --- /dev/null +++ b/runtime/gc/space/bump_pointer_space.cc @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2013 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 "bump_pointer_space.h" +#include "bump_pointer_space-inl.h" +#include "mirror/object-inl.h" +#include "mirror/class-inl.h" + +namespace art { +namespace gc { +namespace space { + +BumpPointerSpace* BumpPointerSpace::Create(const std::string& name, size_t capacity, + byte* requested_begin) { + capacity = RoundUp(capacity, kPageSize); + std::string error_msg; + UniquePtr mem_map(MemMap::MapAnonymous(name.c_str(), requested_begin, capacity, + PROT_READ | PROT_WRITE, &error_msg)); + if (mem_map.get() == nullptr) { + LOG(ERROR) << "Failed to allocate pages for alloc space (" << name << ") of size " + << PrettySize(capacity) << " with message " << error_msg; + return nullptr; + } + return new BumpPointerSpace(name, mem_map.release()); +} + +BumpPointerSpace::BumpPointerSpace(const std::string& name, byte* begin, byte* limit) + : ContinuousMemMapAllocSpace(name, nullptr, begin, begin, limit, + kGcRetentionPolicyAlwaysCollect), + num_objects_allocated_(0), total_bytes_allocated_(0), total_objects_allocated_(0), + growth_end_(limit) { +} + +BumpPointerSpace::BumpPointerSpace(const std::string& name, MemMap* mem_map) + : ContinuousMemMapAllocSpace(name, mem_map, mem_map->Begin(), mem_map->Begin(), mem_map->End(), + kGcRetentionPolicyAlwaysCollect), + num_objects_allocated_(0), total_bytes_allocated_(0), total_objects_allocated_(0), + growth_end_(mem_map->End()) { +} + +mirror::Object* BumpPointerSpace::Alloc(Thread*, size_t num_bytes, size_t* bytes_allocated) { + mirror::Object* ret = AllocNonvirtual(num_bytes); + if (LIKELY(ret != nullptr)) { + *bytes_allocated = num_bytes; + } + return ret; +} + +size_t BumpPointerSpace::AllocationSize(const mirror::Object* obj) { + return AllocationSizeNonvirtual(obj); +} + +void BumpPointerSpace::Clear() { + // Release the pages back to the operating system. + CHECK_NE(madvise(Begin(), Limit() - Begin(), MADV_DONTNEED), -1) << "madvise failed"; + // Reset the end of the space back to the beginning, we move the end forward as we allocate + // objects. + SetEnd(Begin()); + growth_end_ = Limit(); + num_objects_allocated_ = 0; +} + +void BumpPointerSpace::Dump(std::ostream& os) const { + os << reinterpret_cast(Begin()) << "-" << reinterpret_cast(End()) << " - " + << reinterpret_cast(Limit()); +} + +mirror::Object* BumpPointerSpace::GetNextObject(mirror::Object* obj) { + const uintptr_t position = reinterpret_cast(obj) + obj->SizeOf(); + return reinterpret_cast(RoundUp(position, kAlignment)); +} + +} // namespace space +} // namespace gc +} // namespace art diff --git a/runtime/gc/space/bump_pointer_space.h b/runtime/gc/space/bump_pointer_space.h new file mode 100644 index 00000000000..0faac0ce46a --- /dev/null +++ b/runtime/gc/space/bump_pointer_space.h @@ -0,0 +1,149 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ART_RUNTIME_GC_SPACE_BUMP_POINTER_SPACE_H_ +#define ART_RUNTIME_GC_SPACE_BUMP_POINTER_SPACE_H_ + +#include "space.h" + +namespace art { +namespace gc { + +namespace collector { + class MarkSweep; +} // namespace collector + +namespace space { + +// A bump pointer space is a space where objects may be allocated and garbage collected. +class BumpPointerSpace : public ContinuousMemMapAllocSpace { + public: + typedef void(*WalkCallback)(void *start, void *end, size_t num_bytes, void* callback_arg); + + SpaceType GetType() const { + return kSpaceTypeBumpPointerSpace; + } + + // Create a bump pointer space with the requested sizes. The requested base address is not + // guaranteed to be granted, if it is required, the caller should call Begin on the returned + // space to confirm the request was granted. + static BumpPointerSpace* Create(const std::string& name, size_t capacity, byte* requested_begin); + + // Allocate num_bytes, returns nullptr if the space is full. + virtual mirror::Object* Alloc(Thread* self, size_t num_bytes, size_t* bytes_allocated); + mirror::Object* AllocNonvirtual(size_t num_bytes); + + // Return the storage space required by obj. + virtual size_t AllocationSize(const mirror::Object* obj) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + // Nos unless we support free lists. + virtual size_t Free(Thread*, mirror::Object*) { + return 0; + } + virtual size_t FreeList(Thread*, size_t, mirror::Object**) { + return 0; + } + + size_t AllocationSizeNonvirtual(const mirror::Object* obj) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + return obj->SizeOf(); + } + + // Removes the fork time growth limit on capacity, allowing the application to allocate up to the + // maximum reserved size of the heap. + void ClearGrowthLimit() { + growth_end_ = Limit(); + } + + // Override capacity so that we only return the possibly limited capacity + size_t Capacity() const { + return growth_end_ - begin_; + } + + // The total amount of memory reserved for the space. + size_t NonGrowthLimitCapacity() const { + return GetMemMap()->Size(); + } + + accounting::SpaceBitmap* GetLiveBitmap() const { + return nullptr; + } + + accounting::SpaceBitmap* GetMarkBitmap() const { + return nullptr; + } + + // Clear the memory and reset the pointer to the start of the space. + void Clear(); + + void Dump(std::ostream& os) const; + + uint64_t GetBytesAllocated() { + return Size(); + } + + uint64_t GetObjectsAllocated() { + return num_objects_allocated_; + } + + uint64_t GetTotalBytesAllocated() { + return total_bytes_allocated_; + } + + uint64_t GetTotalObjectsAllocated() { + return total_objects_allocated_; + } + + bool Contains(const mirror::Object* obj) const { + const byte* byte_obj = reinterpret_cast(obj); + return byte_obj >= Begin() && byte_obj < End(); + } + + // TODO: Change this? Mainly used for compacting to a particular region of memory. + BumpPointerSpace(const std::string& name, byte* begin, byte* limit); + + // Return the object which comes after obj, while ensuring alignment. + static mirror::Object* GetNextObject(mirror::Object* obj) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + protected: + BumpPointerSpace(const std::string& name, MemMap* mem_map); + + size_t InternalAllocationSize(const mirror::Object* obj); + mirror::Object* AllocWithoutGrowthLocked(size_t num_bytes, size_t* bytes_allocated) + EXCLUSIVE_LOCKS_REQUIRED(lock_); + + // Approximate number of bytes which have been allocated into the space. + AtomicInteger num_objects_allocated_; + AtomicInteger total_bytes_allocated_; + AtomicInteger total_objects_allocated_; + + // Alignment. + static constexpr size_t kAlignment = 8; + + byte* growth_end_; + + private: + friend class collector::MarkSweep; + DISALLOW_COPY_AND_ASSIGN(BumpPointerSpace); +}; + +} // namespace space +} // namespace gc +} // namespace art + +#endif // ART_RUNTIME_GC_SPACE_BUMP_POINTER_SPACE_H_ diff --git a/runtime/gc/space/dlmalloc_space.cc b/runtime/gc/space/dlmalloc_space.cc index 9ebc16a4a3d..8a5e33a4031 100644 --- a/runtime/gc/space/dlmalloc_space.cc +++ b/runtime/gc/space/dlmalloc_space.cc @@ -102,8 +102,8 @@ class ValgrindDlMallocSpace : public DlMallocSpace { } ValgrindDlMallocSpace(const std::string& name, MemMap* mem_map, void* mspace, byte* begin, - byte* end, size_t growth_limit, size_t initial_size) : - DlMallocSpace(name, mem_map, mspace, begin, end, growth_limit) { + byte* end, byte* limit, size_t growth_limit, size_t initial_size) : + DlMallocSpace(name, mem_map, mspace, begin, end, limit, growth_limit) { VALGRIND_MAKE_MEM_UNDEFINED(mem_map->Begin() + initial_size, mem_map->Size() - initial_size); } @@ -117,15 +117,13 @@ class ValgrindDlMallocSpace : public DlMallocSpace { size_t DlMallocSpace::bitmap_index_ = 0; DlMallocSpace::DlMallocSpace(const std::string& name, MemMap* mem_map, void* mspace, byte* begin, - byte* end, size_t growth_limit) - : MemMapSpace(name, mem_map, end - begin, kGcRetentionPolicyAlwaysCollect), + byte* end, byte* limit, size_t growth_limit) + : ContinuousMemMapAllocSpace(name, mem_map, begin, end, limit, kGcRetentionPolicyAlwaysCollect), recent_free_pos_(0), total_bytes_freed_(0), total_objects_freed_(0), lock_("allocation space lock", kAllocSpaceLock), mspace_(mspace), growth_limit_(growth_limit) { CHECK(mspace != NULL); - size_t bitmap_index = bitmap_index_++; - static const uintptr_t kGcCardSize = static_cast(accounting::CardTable::kCardSize); CHECK(IsAligned(reinterpret_cast(mem_map->Begin()))); CHECK(IsAligned(reinterpret_cast(mem_map->End()))); @@ -133,12 +131,10 @@ DlMallocSpace::DlMallocSpace(const std::string& name, MemMap* mem_map, void* msp StringPrintf("allocspace %s live-bitmap %d", name.c_str(), static_cast(bitmap_index)), Begin(), Capacity())); DCHECK(live_bitmap_.get() != NULL) << "could not create allocspace live bitmap #" << bitmap_index; - mark_bitmap_.reset(accounting::SpaceBitmap::Create( StringPrintf("allocspace %s mark-bitmap %d", name.c_str(), static_cast(bitmap_index)), Begin(), Capacity())); DCHECK(live_bitmap_.get() != NULL) << "could not create allocspace mark bitmap #" << bitmap_index; - for (auto& freed : recent_freed_objects_) { freed.first = nullptr; freed.second = nullptr; @@ -207,12 +203,14 @@ DlMallocSpace* DlMallocSpace::Create(const std::string& name, size_t initial_siz // Everything is set so record in immutable structure and leave MemMap* mem_map_ptr = mem_map.release(); DlMallocSpace* space; + byte* begin = mem_map_ptr->Begin(); if (RUNNING_ON_VALGRIND > 0) { - space = new ValgrindDlMallocSpace(name, mem_map_ptr, mspace, mem_map_ptr->Begin(), end, + space = new ValgrindDlMallocSpace(name, mem_map_ptr, mspace, begin, end, begin + capacity, growth_limit, initial_size); } else { - space = new DlMallocSpace(name, mem_map_ptr, mspace, mem_map_ptr->Begin(), end, growth_limit); + space = new DlMallocSpace(name, mem_map_ptr, mspace, begin, end, begin + capacity, growth_limit); } + // We start out with only the initial size possibly containing objects. if (VLOG_IS_ON(heap) || VLOG_IS_ON(startup)) { LOG(INFO) << "Space::CreateAllocSpace exiting (" << PrettyDuration(NanoTime() - start_time) << " ) " << *space; @@ -318,7 +316,8 @@ DlMallocSpace* DlMallocSpace::CreateZygoteSpace(const char* alloc_space_name) { CHECK_MEMORY_CALL(mprotect, (end, capacity - initial_size, PROT_NONE), alloc_space_name); } DlMallocSpace* alloc_space = - new DlMallocSpace(alloc_space_name, mem_map.release(), mspace, end_, end, growth_limit); + new DlMallocSpace(alloc_space_name, mem_map.release(), mspace, end_, end, limit_, + growth_limit); live_bitmap_->SetHeapLimit(reinterpret_cast(End())); CHECK_EQ(live_bitmap_->HeapLimit(), reinterpret_cast(End())); mark_bitmap_->SetHeapLimit(reinterpret_cast(End())); @@ -343,8 +342,7 @@ mirror::Class* DlMallocSpace::FindRecentFreedObject(const mirror::Object* obj) { } void DlMallocSpace::RegisterRecentFree(mirror::Object* ptr) { - recent_freed_objects_[recent_free_pos_].first = ptr; - recent_freed_objects_[recent_free_pos_].second = ptr->GetClass(); + recent_freed_objects_[recent_free_pos_] = std::make_pair(ptr, ptr->GetClass()); recent_free_pos_ = (recent_free_pos_ + 1) & kRecentFreeMask; } @@ -412,8 +410,8 @@ size_t DlMallocSpace::FreeList(Thread* self, size_t num_ptrs, mirror::Object** p // Callback from dlmalloc when it needs to increase the footprint extern "C" void* art_heap_morecore(void* mspace, intptr_t increment) { Heap* heap = Runtime::Current()->GetHeap(); - DCHECK_EQ(heap->GetAllocSpace()->GetMspace(), mspace); - return heap->GetAllocSpace()->MoreCore(increment); + DCHECK_EQ(heap->GetNonMovingSpace()->GetMspace(), mspace); + return heap->GetNonMovingSpace()->MoreCore(increment); } void* DlMallocSpace::MoreCore(intptr_t increment) { @@ -482,6 +480,29 @@ size_t DlMallocSpace::GetFootprintLimit() { return mspace_footprint_limit(mspace_); } +// Returns the old mark bitmap. +accounting::SpaceBitmap* DlMallocSpace::BindLiveToMarkBitmap() { + accounting::SpaceBitmap* live_bitmap = GetLiveBitmap(); + accounting::SpaceBitmap* mark_bitmap = mark_bitmap_.release(); + temp_bitmap_.reset(mark_bitmap); + mark_bitmap_.reset(live_bitmap); + return mark_bitmap; +} + +bool DlMallocSpace::HasBoundBitmaps() const { + return temp_bitmap_.get() != nullptr; +} + +void DlMallocSpace::UnBindBitmaps() { + CHECK(HasBoundBitmaps()); + // At this point, the temp_bitmap holds our old mark bitmap. + accounting::SpaceBitmap* new_bitmap = temp_bitmap_.release(); + CHECK_EQ(mark_bitmap_.release(), live_bitmap_.get()); + mark_bitmap_.reset(new_bitmap); + DCHECK(temp_bitmap_.get() == NULL); +} + + void DlMallocSpace::SetFootprintLimit(size_t new_size) { MutexLock mu(Thread::Current(), lock_); VLOG(heap) << "DLMallocSpace::SetFootprintLimit " << PrettySize(new_size); @@ -504,17 +525,25 @@ void DlMallocSpace::Dump(std::ostream& os) const { } uint64_t DlMallocSpace::GetBytesAllocated() { - MutexLock mu(Thread::Current(), lock_); - size_t bytes_allocated = 0; - mspace_inspect_all(mspace_, DlmallocBytesAllocatedCallback, &bytes_allocated); - return bytes_allocated; + if (mspace_ != nullptr) { + MutexLock mu(Thread::Current(), lock_); + size_t bytes_allocated = 0; + mspace_inspect_all(mspace_, DlmallocBytesAllocatedCallback, &bytes_allocated); + return bytes_allocated; + } else { + return Size(); + } } uint64_t DlMallocSpace::GetObjectsAllocated() { - MutexLock mu(Thread::Current(), lock_); - size_t objects_allocated = 0; - mspace_inspect_all(mspace_, DlmallocObjectsAllocatedCallback, &objects_allocated); - return objects_allocated; + if (mspace_ != nullptr) { + MutexLock mu(Thread::Current(), lock_); + size_t objects_allocated = 0; + mspace_inspect_all(mspace_, DlmallocObjectsAllocatedCallback, &objects_allocated); + return objects_allocated; + } else { + return 0; + } } } // namespace space diff --git a/runtime/gc/space/dlmalloc_space.h b/runtime/gc/space/dlmalloc_space.h index 522535e3c03..59dafe3f2a2 100644 --- a/runtime/gc/space/dlmalloc_space.h +++ b/runtime/gc/space/dlmalloc_space.h @@ -30,7 +30,7 @@ namespace collector { namespace space { // An alloc space is a space where objects may be allocated and garbage collected. -class DlMallocSpace : public MemMapSpace, public AllocSpace { +class DlMallocSpace : public ContinuousMemMapAllocSpace { public: typedef void(*WalkCallback)(void *start, void *end, size_t num_bytes, void* callback_arg); @@ -136,19 +136,30 @@ class DlMallocSpace : public MemMapSpace, public AllocSpace { return GetObjectsAllocated() + total_objects_freed_; } + // Returns the old mark bitmap. + accounting::SpaceBitmap* BindLiveToMarkBitmap(); + bool HasBoundBitmaps() const; + void UnBindBitmaps(); + // Returns the class of a recently freed object. mirror::Class* FindRecentFreedObject(const mirror::Object* obj); + // Used to ensure that failure happens when you free / allocate into an invalidated space. If we + // don't do this we may get heap corruption instead of a segfault at null. + void InvalidateMSpace() { + mspace_ = nullptr; + } + protected: DlMallocSpace(const std::string& name, MemMap* mem_map, void* mspace, byte* begin, byte* end, - size_t growth_limit); + byte* limit, size_t growth_limit); private: size_t InternalAllocationSize(const mirror::Object* obj); mirror::Object* AllocWithoutGrowthLocked(size_t num_bytes, size_t* bytes_allocated) EXCLUSIVE_LOCKS_REQUIRED(lock_); bool Init(size_t initial_size, size_t maximum_size, size_t growth_size, byte* requested_base); - void RegisterRecentFree(mirror::Object* ptr); + void RegisterRecentFree(mirror::Object* ptr) EXCLUSIVE_LOCKS_REQUIRED(lock_); static void* CreateMallocSpace(void* base, size_t morecore_start, size_t initial_size); UniquePtr live_bitmap_; @@ -174,7 +185,7 @@ class DlMallocSpace : public MemMapSpace, public AllocSpace { Mutex lock_ DEFAULT_MUTEX_ACQUIRED_AFTER; // Underlying malloc space - void* const mspace_; + void* mspace_; // The capacity of the alloc space until such time that ClearGrowthLimit is called. // The underlying mem_map_ controls the maximum size we allow the heap to grow to. The growth diff --git a/runtime/gc/space/image_space.cc b/runtime/gc/space/image_space.cc index e12ee063c0e..c6177bd01d1 100644 --- a/runtime/gc/space/image_space.cc +++ b/runtime/gc/space/image_space.cc @@ -39,8 +39,9 @@ AtomicInteger ImageSpace::bitmap_index_(0); ImageSpace::ImageSpace(const std::string& name, MemMap* mem_map, accounting::SpaceBitmap* live_bitmap) - : MemMapSpace(name, mem_map, mem_map->Size(), kGcRetentionPolicyNeverCollect) { - DCHECK(live_bitmap != NULL); + : MemMapSpace(name, mem_map, mem_map->Begin(), mem_map->End(), mem_map->End(), + kGcRetentionPolicyNeverCollect) { + DCHECK(live_bitmap != nullptr); live_bitmap_.reset(live_bitmap); } @@ -332,7 +333,7 @@ OatFile* ImageSpace::ReleaseOatFile() { void ImageSpace::Dump(std::ostream& os) const { os << GetType() - << "begin=" << reinterpret_cast(Begin()) + << " begin=" << reinterpret_cast(Begin()) << ",end=" << reinterpret_cast(End()) << ",size=" << PrettySize(Size()) << ",name=\"" << GetName() << "\"]"; diff --git a/runtime/gc/space/large_object_space.h b/runtime/gc/space/large_object_space.h index ef889d42c2b..07fb288576a 100644 --- a/runtime/gc/space/large_object_space.h +++ b/runtime/gc/space/large_object_space.h @@ -59,6 +59,14 @@ class LargeObjectSpace : public DiscontinuousSpace, public AllocSpace { size_t FreeList(Thread* self, size_t num_ptrs, mirror::Object** ptrs); + virtual bool IsAllocSpace() const { + return true; + } + + virtual AllocSpace* AsAllocSpace() { + return this; + } + protected: explicit LargeObjectSpace(const std::string& name); diff --git a/runtime/gc/space/space-inl.h b/runtime/gc/space/space-inl.h index 2c3b93c60d4..f1031ff8d4e 100644 --- a/runtime/gc/space/space-inl.h +++ b/runtime/gc/space/space-inl.h @@ -27,18 +27,28 @@ namespace gc { namespace space { inline ImageSpace* Space::AsImageSpace() { - DCHECK_EQ(GetType(), kSpaceTypeImageSpace); + DCHECK(IsImageSpace()); return down_cast(down_cast(this)); } inline DlMallocSpace* Space::AsDlMallocSpace() { - DCHECK(GetType() == kSpaceTypeAllocSpace || GetType() == kSpaceTypeZygoteSpace); + DCHECK(IsDlMallocSpace()); return down_cast(down_cast(this)); } inline LargeObjectSpace* Space::AsLargeObjectSpace() { - DCHECK_EQ(GetType(), kSpaceTypeLargeObjectSpace); - return reinterpret_cast(this); + DCHECK(IsLargeObjectSpace()); + return down_cast(this); +} + +inline ContinuousSpace* Space::AsContinuousSpace() { + DCHECK(IsContinuousSpace()); + return down_cast(this); +} + +inline DiscontinuousSpace* Space::AsDiscontinuousSpace() { + DCHECK(IsDiscontinuousSpace()); + return down_cast(this); } } // namespace space diff --git a/runtime/gc/space/space.cc b/runtime/gc/space/space.cc index de48b743f56..8eb17e0c1ea 100644 --- a/runtime/gc/space/space.cc +++ b/runtime/gc/space/space.cc @@ -34,7 +34,6 @@ std::ostream& operator<<(std::ostream& os, const Space& space) { return os; } - DiscontinuousSpace::DiscontinuousSpace(const std::string& name, GcRetentionPolicy gc_retention_policy) : Space(name, gc_retention_policy), diff --git a/runtime/gc/space/space.h b/runtime/gc/space/space.h index 6dd795227d0..4c05ddef58a 100644 --- a/runtime/gc/space/space.h +++ b/runtime/gc/space/space.h @@ -42,7 +42,10 @@ class Heap; namespace space { +class AllocSpace; +class ContinuousSpace; class DlMallocSpace; +class DiscontinuousSpace; class ImageSpace; class LargeObjectSpace; @@ -64,6 +67,7 @@ enum SpaceType { kSpaceTypeImageSpace, kSpaceTypeAllocSpace, kSpaceTypeZygoteSpace, + kSpaceTypeBumpPointerSpace, kSpaceTypeLargeObjectSpace, }; std::ostream& operator<<(std::ostream& os, const SpaceType& space_type); @@ -113,12 +117,35 @@ class Space { return GetType() == kSpaceTypeZygoteSpace; } + // Is this space a bump pointer space? + bool IsBumpPointerSpace() const { + return GetType() == kSpaceTypeBumpPointerSpace; + } + // Does this space hold large objects and implement the large object space abstraction? bool IsLargeObjectSpace() const { return GetType() == kSpaceTypeLargeObjectSpace; } LargeObjectSpace* AsLargeObjectSpace(); + virtual bool IsContinuousSpace() const { + return false; + } + ContinuousSpace* AsContinuousSpace(); + + virtual bool IsDiscontinuousSpace() const { + return false; + } + DiscontinuousSpace* AsDiscontinuousSpace(); + + virtual bool IsAllocSpace() const { + return false; + } + virtual AllocSpace* AsAllocSpace() { + LOG(FATAL) << "Unimplemented"; + return nullptr; + } + virtual ~Space() {} protected: @@ -131,13 +158,13 @@ class Space { // Name of the space that may vary due to the Zygote fork. std::string name_; - private: + protected: // When should objects within this space be reclaimed? Not constant as we vary it in the case // of Zygote forking. GcRetentionPolicy gc_retention_policy_; + private: friend class art::gc::Heap; - DISALLOW_COPY_AND_ASSIGN(Space); }; std::ostream& operator<<(std::ostream& os, const Space& space); @@ -180,16 +207,31 @@ class AllocSpace { // continuous spaces can be marked in the card table. class ContinuousSpace : public Space { public: - // Address at which the space begins + // Address at which the space begins. byte* Begin() const { return begin_; } - // Address at which the space ends, which may vary as the space is filled. + // Current address at which the space ends, which may vary as the space is filled. byte* End() const { return end_; } + // The end of the address range covered by the space. + byte* Limit() const { + return limit_; + } + + // Change the end of the space. Be careful with use since changing the end of a space to an + // invalid value may break the GC. + void SetEnd(byte* end) { + end_ = end; + } + + void SetLimit(byte* limit) { + limit_ = limit; + } + // Current size of space size_t Size() const { return End() - Begin(); @@ -198,31 +240,42 @@ class ContinuousSpace : public Space { virtual accounting::SpaceBitmap* GetLiveBitmap() const = 0; virtual accounting::SpaceBitmap* GetMarkBitmap() const = 0; + // Maximum which the mapped space can grow to. + virtual size_t Capacity() const { + return Limit() - Begin(); + } + // Is object within this space? We check to see if the pointer is beyond the end first as // continuous spaces are iterated over from low to high. bool HasAddress(const mirror::Object* obj) const { const byte* byte_ptr = reinterpret_cast(obj); - return byte_ptr < End() && byte_ptr >= Begin(); + return byte_ptr >= Begin() && byte_ptr < Limit(); } bool Contains(const mirror::Object* obj) const { return HasAddress(obj); } + virtual bool IsContinuousSpace() const { + return true; + } + virtual ~ContinuousSpace() {} protected: ContinuousSpace(const std::string& name, GcRetentionPolicy gc_retention_policy, - byte* begin, byte* end) : - Space(name, gc_retention_policy), begin_(begin), end_(end) { + byte* begin, byte* end, byte* limit) : + Space(name, gc_retention_policy), begin_(begin), end_(end), limit_(limit) { } - // The beginning of the storage for fast access. - byte* const begin_; + byte* begin_; // Current end of the space. - byte* end_; + byte* volatile end_; + + // Limit of the space. + byte* limit_; private: DISALLOW_COPY_AND_ASSIGN(ContinuousSpace); @@ -241,6 +294,10 @@ class DiscontinuousSpace : public Space { return mark_objects_.get(); } + virtual bool IsDiscontinuousSpace() const { + return true; + } + virtual ~DiscontinuousSpace() {} protected: @@ -255,25 +312,12 @@ class DiscontinuousSpace : public Space { class MemMapSpace : public ContinuousSpace { public: - // Maximum which the mapped space can grow to. - virtual size_t Capacity() const { - return mem_map_->Size(); - } - // Size of the space without a limit on its growth. By default this is just the Capacity, but // for the allocation space we support starting with a small heap and then extending it. virtual size_t NonGrowthLimitCapacity() const { return Capacity(); } - protected: - MemMapSpace(const std::string& name, MemMap* mem_map, size_t initial_size, - GcRetentionPolicy gc_retention_policy) - : ContinuousSpace(name, gc_retention_policy, - mem_map->Begin(), mem_map->Begin() + initial_size), - mem_map_(mem_map) { - } - MemMap* GetMemMap() { return mem_map_.get(); } @@ -282,13 +326,45 @@ class MemMapSpace : public ContinuousSpace { return mem_map_.get(); } - private: + protected: + MemMapSpace(const std::string& name, MemMap* mem_map, byte* begin, byte* end, byte* limit, + GcRetentionPolicy gc_retention_policy) + : ContinuousSpace(name, gc_retention_policy, begin, end, limit), + mem_map_(mem_map) { + } + // Underlying storage of the space UniquePtr mem_map_; + private: DISALLOW_COPY_AND_ASSIGN(MemMapSpace); }; +// Used by the heap compaction interface to enable copying from one type of alloc space to another. +class ContinuousMemMapAllocSpace : public MemMapSpace, public AllocSpace { + public: + virtual bool IsAllocSpace() const { + return true; + } + + virtual AllocSpace* AsAllocSpace() { + return this; + } + + virtual void Clear() { + LOG(FATAL) << "Unimplemented"; + } + + protected: + ContinuousMemMapAllocSpace(const std::string& name, MemMap* mem_map, byte* begin, + byte* end, byte* limit, GcRetentionPolicy gc_retention_policy) + : MemMapSpace(name, mem_map, begin, end, limit, gc_retention_policy) { + } + + private: + DISALLOW_COPY_AND_ASSIGN(ContinuousMemMapAllocSpace); +}; + } // namespace space } // namespace gc } // namespace art diff --git a/runtime/gc/space/space_test.cc b/runtime/gc/space/space_test.cc index 455168c90f8..383714bb049 100644 --- a/runtime/gc/space/space_test.cc +++ b/runtime/gc/space/space_test.cc @@ -33,8 +33,8 @@ class SpaceTest : public CommonTest { int round, size_t growth_limit); void SizeFootPrintGrowthLimitAndTrimDriver(size_t object_size); - void AddContinuousSpace(ContinuousSpace* space) { - Runtime::Current()->GetHeap()->AddContinuousSpace(space); + void AddSpace(ContinuousSpace* space) { + Runtime::Current()->GetHeap()->AddSpace(space); } }; @@ -91,7 +91,7 @@ TEST_F(SpaceTest, ZygoteSpace) { ASSERT_TRUE(space != NULL); // Make space findable to the heap, will also delete space when runtime is cleaned up - AddContinuousSpace(space); + AddSpace(space); Thread* self = Thread::Current(); // Succeeds, fits without adjusting the footprint limit. @@ -136,7 +136,7 @@ TEST_F(SpaceTest, ZygoteSpace) { space = space->CreateZygoteSpace("alloc space"); // Make space findable to the heap, will also delete space when runtime is cleaned up - AddContinuousSpace(space); + AddSpace(space); // Succeeds, fits without adjusting the footprint limit. ptr1 = space->Alloc(self, 1 * MB, &dummy); @@ -164,7 +164,7 @@ TEST_F(SpaceTest, AllocAndFree) { Thread* self = Thread::Current(); // Make space findable to the heap, will also delete space when runtime is cleaned up - AddContinuousSpace(space); + AddSpace(space); // Succeeds, fits without adjusting the footprint limit. mirror::Object* ptr1 = space->Alloc(self, 1 * MB, &dummy); @@ -270,7 +270,7 @@ TEST_F(SpaceTest, AllocAndFreeList) { ASSERT_TRUE(space != NULL); // Make space findable to the heap, will also delete space when runtime is cleaned up - AddContinuousSpace(space); + AddSpace(space); Thread* self = Thread::Current(); // Succeeds, fits without adjusting the max allowed footprint. @@ -467,7 +467,7 @@ void SpaceTest::SizeFootPrintGrowthLimitAndTrimDriver(size_t object_size) { EXPECT_EQ(space->NonGrowthLimitCapacity(), capacity); // Make space findable to the heap, will also delete space when runtime is cleaned up - AddContinuousSpace(space); + AddSpace(space); // In this round we don't allocate with growth and therefore can't grow past the initial size. // This effectively makes the growth_limit the initial_size, so assert this. diff --git a/runtime/globals.h b/runtime/globals.h index 31574ff72d9..10426b0fe2a 100644 --- a/runtime/globals.h +++ b/runtime/globals.h @@ -73,6 +73,15 @@ const bool kIsTargetBuild = true; const bool kIsTargetBuild = false; #endif +// Garbage collector constants. +static constexpr bool kMovingCollector = false; +// True if we allow moving classes. +static constexpr bool kMovingClasses = false; +// True if we allow moving fields. +static constexpr bool kMovingFields = false; +// True if we allow moving methods. +static constexpr bool kMovingMethods = false; + } // namespace art #endif // ART_RUNTIME_GLOBALS_H_ diff --git a/runtime/intern_table.cc b/runtime/intern_table.cc index 8f9e072093d..a829e97a232 100644 --- a/runtime/intern_table.cc +++ b/runtime/intern_table.cc @@ -48,7 +48,7 @@ void InternTable::VisitRoots(RootVisitor* visitor, void* arg, MutexLock mu(Thread::Current(), intern_table_lock_); if (!only_dirty || is_dirty_) { for (auto& strong_intern : strong_interns_) { - strong_intern.second = reinterpret_cast(visitor(strong_intern.second, arg)); + strong_intern.second = down_cast(visitor(strong_intern.second, arg)); DCHECK(strong_intern.second != nullptr); } @@ -59,8 +59,7 @@ void InternTable::VisitRoots(RootVisitor* visitor, void* arg, // Note: we deliberately don't visit the weak_interns_ table and the immutable image roots. } -mirror::String* InternTable::Lookup(Table& table, mirror::String* s, - uint32_t hash_code) { +mirror::String* InternTable::Lookup(Table& table, mirror::String* s, uint32_t hash_code) { intern_table_lock_.AssertHeld(Thread::Current()); for (auto it = table.find(hash_code), end = table.end(); it != end; ++it) { mirror::String* existing_string = it->second; @@ -71,8 +70,7 @@ mirror::String* InternTable::Lookup(Table& table, mirror::String* s, return NULL; } -mirror::String* InternTable::Insert(Table& table, mirror::String* s, - uint32_t hash_code) { +mirror::String* InternTable::Insert(Table& table, mirror::String* s, uint32_t hash_code) { intern_table_lock_.AssertHeld(Thread::Current()); table.insert(std::make_pair(hash_code, s)); return s; diff --git a/runtime/interpreter/interpreter.cc b/runtime/interpreter/interpreter.cc index d7555ddb6a6..9938478b2c1 100644 --- a/runtime/interpreter/interpreter.cc +++ b/runtime/interpreter/interpreter.cc @@ -430,8 +430,8 @@ extern "C" void artInterpreterToInterpreterBridge(Thread* self, MethodHelper& mh if (method->IsStatic()) { Class* declaringClass = method->GetDeclaringClass(); if (UNLIKELY(!declaringClass->IsInitializing())) { - if (UNLIKELY(!Runtime::Current()->GetClassLinker()->EnsureInitialized(declaringClass, - true, true))) { + if (UNLIKELY(!Runtime::Current()->GetClassLinker()->EnsureInitialized(declaringClass, true, + true))) { DCHECK(Thread::Current()->IsExceptionPending()); self->PopShadowFrame(); return; diff --git a/runtime/interpreter/interpreter_common.cc b/runtime/interpreter/interpreter_common.cc index 19f55d2f973..08221b723d9 100644 --- a/runtime/interpreter/interpreter_common.cc +++ b/runtime/interpreter/interpreter_common.cc @@ -29,7 +29,7 @@ static inline void AssignRegister(ShadowFrame& new_shadow_frame, const ShadowFra size_t dest_reg, size_t src_reg) { // If both register locations contains the same value, the register probably holds a reference. int32_t src_value = shadow_frame.GetVReg(src_reg); - mirror::Object* o = shadow_frame.GetVRegReference(src_reg); + mirror::Object* o = shadow_frame.GetVRegReference(src_reg); if (src_value == reinterpret_cast(o)) { new_shadow_frame.SetVRegReference(dest_reg, o); } else { @@ -193,7 +193,7 @@ bool DoFilledNewArray(const Instruction* inst, const ShadowFrame& shadow_frame, } return false; } - Object* newArray = Array::Alloc(self, arrayClass, length); + Object* newArray = Array::Alloc(self, arrayClass, length); if (UNLIKELY(newArray == NULL)) { DCHECK(self->IsExceptionPending()); return false; @@ -233,7 +233,8 @@ static void UnstartedRuntimeInvoke(Thread* self, MethodHelper& mh, std::string name(PrettyMethod(shadow_frame->GetMethod())); if (name == "java.lang.Class java.lang.Class.forName(java.lang.String)") { std::string descriptor(DotToDescriptor(shadow_frame->GetVRegReference(arg_offset)->AsString()->ToModifiedUtf8().c_str())); - ClassLoader* class_loader = NULL; // shadow_frame.GetMethod()->GetDeclaringClass()->GetClassLoader(); + + SirtRef class_loader(self, nullptr); // shadow_frame.GetMethod()->GetDeclaringClass()->GetClassLoader(); Class* found = Runtime::Current()->GetClassLinker()->FindClass(descriptor.c_str(), class_loader); CHECK(found != NULL) << "Class.forName failed in un-started runtime for class: " diff --git a/runtime/jni_internal.cc b/runtime/jni_internal.cc index ec717c1bee5..466edebf591 100644 --- a/runtime/jni_internal.cc +++ b/runtime/jni_internal.cc @@ -22,6 +22,7 @@ #include #include +#include "atomic_integer.h" #include "base/logging.h" #include "base/mutex.h" #include "base/stl_util.h" @@ -292,8 +293,8 @@ static jfieldID FindFieldID(const ScopedObjectAccess& soa, jclass jni_class, con Class* field_type; ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); if (sig[1] != '\0') { - ClassLoader* cl = GetClassLoader(soa); - field_type = class_linker->FindClass(sig, cl); + SirtRef class_loader(soa.Self(), GetClassLoader(soa)); + field_type = class_linker->FindClass(sig, class_loader); } else { field_type = class_linker->FindPrimitiveClass(*sig); } @@ -646,8 +647,8 @@ class JNI { ScopedObjectAccess soa(env); Class* c = NULL; if (runtime->IsStarted()) { - ClassLoader* cl = GetClassLoader(soa); - c = class_linker->FindClass(descriptor.c_str(), cl); + SirtRef class_loader(soa.Self(), GetClassLoader(soa)); + c = class_linker->FindClass(descriptor.c_str(), class_loader); } else { c = class_linker->FindSystemClass(descriptor.c_str()); } @@ -2002,14 +2003,22 @@ class JNI { String* s = soa.Decode(java_string); CharArray* chars = s->GetCharArray(); PinPrimitiveArray(soa, chars); - if (is_copy != NULL) { - *is_copy = JNI_FALSE; + if (is_copy != nullptr) { + *is_copy = JNI_TRUE; + } + int32_t char_count = s->GetLength(); + int32_t offset = s->GetOffset(); + jchar* bytes = new jchar[char_count + 1]; + for (int32_t i = 0; i < char_count; i++) { + bytes[i] = chars->Get(i + offset); } - return chars->GetData() + s->GetOffset(); + bytes[char_count] = '\0'; + return bytes; } - static void ReleaseStringChars(JNIEnv* env, jstring java_string, const jchar*) { + static void ReleaseStringChars(JNIEnv* env, jstring java_string, const jchar* chars) { CHECK_NON_NULL_ARGUMENT(GetStringUTFRegion, java_string); + delete[] chars; ScopedObjectAccess soa(env); UnpinPrimitiveArray(soa, soa.Decode(java_string)->GetCharArray()); } @@ -2120,8 +2129,8 @@ class JNI { // Find the class. ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); - Class* array_class = class_linker->FindClass(descriptor.c_str(), - element_class->GetClassLoader()); + SirtRef class_loader(soa.Self(), element_class->GetClassLoader()); + Class* array_class = class_linker->FindClass(descriptor.c_str(), class_loader); if (array_class == NULL) { return NULL; } @@ -2146,16 +2155,23 @@ class JNI { CHECK_NON_NULL_ARGUMENT(GetPrimitiveArrayCritical, java_array); ScopedObjectAccess soa(env); Array* array = soa.Decode(java_array); + gc::Heap* heap = Runtime::Current()->GetHeap(); + if (heap->IsMovableObject(array)) { + heap->IncrementDisableGC(soa.Self()); + // Re-decode in case the object moved since IncrementDisableGC waits for GC to complete. + array = soa.Decode(java_array); + } PinPrimitiveArray(soa, array); - if (is_copy != NULL) { + if (is_copy != nullptr) { *is_copy = JNI_FALSE; } - return array->GetRawData(array->GetClass()->GetComponentSize()); + void* address = array->GetRawData(array->GetClass()->GetComponentSize());; + return address; } - static void ReleasePrimitiveArrayCritical(JNIEnv* env, jarray array, void*, jint mode) { + static void ReleasePrimitiveArrayCritical(JNIEnv* env, jarray array, void* elements, jint mode) { CHECK_NON_NULL_ARGUMENT(ReleasePrimitiveArrayCritical, array); - ReleasePrimitiveArray(env, array, mode); + ReleasePrimitiveArray(env, array, elements, mode); } static jboolean* GetBooleanArrayElements(JNIEnv* env, jbooleanArray array, jboolean* is_copy) { @@ -2206,36 +2222,40 @@ class JNI { return GetPrimitiveArray(soa, array, is_copy); } - static void ReleaseBooleanArrayElements(JNIEnv* env, jbooleanArray array, jboolean*, jint mode) { - ReleasePrimitiveArray(env, array, mode); + static void ReleaseBooleanArrayElements(JNIEnv* env, jbooleanArray array, jboolean* elements, + jint mode) { + ReleasePrimitiveArray(env, array, elements, mode); } - static void ReleaseByteArrayElements(JNIEnv* env, jbyteArray array, jbyte*, jint mode) { - ReleasePrimitiveArray(env, array, mode); + static void ReleaseByteArrayElements(JNIEnv* env, jbyteArray array, jbyte* elements, jint mode) { + ReleasePrimitiveArray(env, array, elements, mode); } - static void ReleaseCharArrayElements(JNIEnv* env, jcharArray array, jchar*, jint mode) { - ReleasePrimitiveArray(env, array, mode); + static void ReleaseCharArrayElements(JNIEnv* env, jcharArray array, jchar* elements, jint mode) { + ReleasePrimitiveArray(env, array, elements, mode); } - static void ReleaseDoubleArrayElements(JNIEnv* env, jdoubleArray array, jdouble*, jint mode) { - ReleasePrimitiveArray(env, array, mode); + static void ReleaseDoubleArrayElements(JNIEnv* env, jdoubleArray array, jdouble* elements, + jint mode) { + ReleasePrimitiveArray(env, array, elements, mode); } - static void ReleaseFloatArrayElements(JNIEnv* env, jfloatArray array, jfloat*, jint mode) { - ReleasePrimitiveArray(env, array, mode); + static void ReleaseFloatArrayElements(JNIEnv* env, jfloatArray array, jfloat* elements, + jint mode) { + ReleasePrimitiveArray(env, array, elements, mode); } - static void ReleaseIntArrayElements(JNIEnv* env, jintArray array, jint*, jint mode) { - ReleasePrimitiveArray(env, array, mode); + static void ReleaseIntArrayElements(JNIEnv* env, jintArray array, jint* elements, jint mode) { + ReleasePrimitiveArray(env, array, elements, mode); } - static void ReleaseLongArrayElements(JNIEnv* env, jlongArray array, jlong*, jint mode) { - ReleasePrimitiveArray(env, array, mode); + static void ReleaseLongArrayElements(JNIEnv* env, jlongArray array, jlong* elements, jint mode) { + ReleasePrimitiveArray(env, array, elements, mode); } - static void ReleaseShortArrayElements(JNIEnv* env, jshortArray array, jshort*, jint mode) { - ReleasePrimitiveArray(env, array, mode); + static void ReleaseShortArrayElements(JNIEnv* env, jshortArray array, jshort* elements, + jint mode) { + ReleasePrimitiveArray(env, array, elements, mode); } static void GetBooleanArrayRegion(JNIEnv* env, jbooleanArray array, jsize start, jsize length, @@ -2551,19 +2571,49 @@ class JNI { SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { ArtArrayT* array = soa.Decode(java_array); PinPrimitiveArray(soa, array); - if (is_copy != NULL) { - *is_copy = JNI_FALSE; + // Only make a copy if necessary. + if (Runtime::Current()->GetHeap()->IsMovableObject(array)) { + if (is_copy != nullptr) { + *is_copy = JNI_TRUE; + } + static const size_t component_size = array->GetClass()->GetComponentSize(); + size_t size = array->GetLength() * component_size; + void* data = new uint64_t[RoundUp(size, 8) / 8]; + memcpy(data, array->GetData(), size); + return reinterpret_cast(data); + } else { + if (is_copy != nullptr) { + *is_copy = JNI_FALSE; + } + return reinterpret_cast(array->GetData()); } - return array->GetData(); } - template - static void ReleasePrimitiveArray(JNIEnv* env, ArrayT java_array, jint mode) { + template + static void ReleasePrimitiveArray(JNIEnv* env, ArrayT java_array, ElementT* elements, jint mode) { + ScopedObjectAccess soa(env); + Array* array = soa.Decode(java_array); + size_t component_size = array->GetClass()->GetComponentSize(); + void* array_data = array->GetRawData(component_size); + gc::Heap* heap = Runtime::Current()->GetHeap(); + bool is_copy = array_data != reinterpret_cast(elements); + size_t bytes = array->GetLength() * component_size; + VLOG(heap) << "Release primitive array " << env << " array_data " << array_data + << " elements " << reinterpret_cast(elements); + if (!is_copy && heap->IsMovableObject(array)) { + heap->DecrementDisableGC(soa.Self()); + } + // Don't need to copy if we had a direct pointer. + if (mode != JNI_ABORT && is_copy) { + memcpy(array_data, elements, bytes); + } if (mode != JNI_COMMIT) { - ScopedObjectAccess soa(env); - Array* array = soa.Decode(java_array); - UnpinPrimitiveArray(soa, array); + if (is_copy) { + delete[] reinterpret_cast(elements); + } } + // TODO: Do we always unpin primitive array? + UnpinPrimitiveArray(soa, array); } template @@ -2854,6 +2904,18 @@ JNIEnvExt::JNIEnvExt(Thread* self, JavaVMExt* vm) JNIEnvExt::~JNIEnvExt() { } +jobject JNIEnvExt::NewLocalRef(mirror::Object* obj) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + if (obj == nullptr) { + return nullptr; + } + return reinterpret_cast(locals.Add(local_ref_cookie, obj)); +} + +void JNIEnvExt::DeleteLocalRef(jobject obj) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + if (obj != nullptr) { + locals.Remove(local_ref_cookie, reinterpret_cast(obj)); + } +} void JNIEnvExt::SetCheckJniEnabled(bool enabled) { check_jni = enabled; functions = enabled ? GetCheckJniNativeInterface() : &gJniNativeInterface; @@ -3199,7 +3261,7 @@ bool JavaVMExt::LoadNativeLibrary(const std::string& path, ClassLoader* class_lo // the comments in the JNI FindClass function.) typedef int (*JNI_OnLoadFn)(JavaVM*, void*); JNI_OnLoadFn jni_on_load = reinterpret_cast(sym); - ClassLoader* old_class_loader = self->GetClassLoaderOverride(); + SirtRef old_class_loader(self, self->GetClassLoaderOverride()); self->SetClassLoaderOverride(class_loader); int version = 0; @@ -3209,7 +3271,7 @@ bool JavaVMExt::LoadNativeLibrary(const std::string& path, ClassLoader* class_lo version = (*jni_on_load)(this, NULL); } - self->SetClassLoaderOverride(old_class_loader); + self->SetClassLoaderOverride(old_class_loader.get()); if (version == JNI_ERR) { StringAppendF(detail, "JNI_ERR returned from JNI_OnLoad in \"%s\"", path.c_str()); diff --git a/runtime/jni_internal.h b/runtime/jni_internal.h index 888d5e5458a..96f7ae09757 100644 --- a/runtime/jni_internal.h +++ b/runtime/jni_internal.h @@ -162,6 +162,9 @@ struct JNIEnvExt : public JNIEnv { return Offset(OFFSETOF_MEMBER(JNIEnvExt, self)); } + jobject NewLocalRef(mirror::Object* obj) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + void DeleteLocalRef(jobject obj) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + Thread* const self; JavaVMExt* vm; diff --git a/runtime/jni_internal_test.cc b/runtime/jni_internal_test.cc index c389580ebfc..26b18364cf6 100644 --- a/runtime/jni_internal_test.cc +++ b/runtime/jni_internal_test.cc @@ -86,19 +86,19 @@ class JniInternalTest : public CommonTest { const char* class_name = is_static ? "StaticLeafMethods" : "NonStaticLeafMethods"; jobject jclass_loader(LoadDex(class_name)); Thread* self = Thread::Current(); + SirtRef null_class_loader(self, nullptr); SirtRef class_loader(self, ScopedObjectAccessUnchecked(self).Decode(jclass_loader)); if (is_static) { - CompileDirectMethod(class_loader.get(), class_name, method_name, method_signature); + CompileDirectMethod(class_loader, class_name, method_name, method_signature); } else { - CompileVirtualMethod(NULL, "java.lang.Class", "isFinalizable", "()Z"); - CompileDirectMethod(NULL, "java.lang.Object", "", "()V"); - CompileVirtualMethod(class_loader.get(), class_name, method_name, method_signature); + CompileVirtualMethod(null_class_loader, "java.lang.Class", "isFinalizable", "()Z"); + CompileDirectMethod(null_class_loader, "java.lang.Object", "", "()V"); + CompileVirtualMethod(class_loader, class_name, method_name, method_signature); } - mirror::Class* c = class_linker_->FindClass(DotToDescriptor(class_name).c_str(), - class_loader.get()); + mirror::Class* c = class_linker_->FindClass(DotToDescriptor(class_name).c_str(), class_loader); CHECK(c != NULL); method = is_static ? c->FindDirectMethod(method_name, method_signature) @@ -1081,7 +1081,6 @@ TEST_F(JniInternalTest, RegisterNatives) { EXPECT_EQ(memcmp(&src_buf[0], xs, size * sizeof(scalar_type)), 0) \ << # get_elements_fn " not equal"; \ env_->release_elements_fn(a, xs, 0); \ - EXPECT_EQ(reinterpret_cast(v), reinterpret_cast(xs)) TEST_F(JniInternalTest, BooleanArrays) { EXPECT_PRIMITIVE_ARRAY(NewBooleanArray, GetBooleanArrayRegion, SetBooleanArrayRegion, @@ -1337,7 +1336,7 @@ TEST_F(JniInternalTest, GetStringChars_ReleaseStringChars) { jboolean is_copy = JNI_FALSE; chars = env_->GetStringChars(s, &is_copy); - EXPECT_EQ(JNI_FALSE, is_copy); + EXPECT_EQ(JNI_TRUE, is_copy); EXPECT_EQ(expected[0], chars[0]); EXPECT_EQ(expected[1], chars[1]); EXPECT_EQ(expected[2], chars[2]); @@ -1361,7 +1360,8 @@ TEST_F(JniInternalTest, GetStringCritical_ReleaseStringCritical) { jboolean is_copy = JNI_FALSE; chars = env_->GetStringCritical(s, &is_copy); - EXPECT_EQ(JNI_FALSE, is_copy); + // TODO: Fix GetStringCritical to use the same mechanism as GetPrimitiveArrayElementsCritical. + EXPECT_EQ(JNI_TRUE, is_copy); EXPECT_EQ(expected[0], chars[0]); EXPECT_EQ(expected[1], chars[1]); EXPECT_EQ(expected[2], chars[2]); @@ -1669,9 +1669,9 @@ TEST_F(JniInternalTest, StaticMainMethod) { jobject jclass_loader = LoadDex("Main"); SirtRef class_loader(soa.Self(), soa.Decode(jclass_loader)); - CompileDirectMethod(class_loader.get(), "Main", "main", "([Ljava/lang/String;)V"); + CompileDirectMethod(class_loader, "Main", "main", "([Ljava/lang/String;)V"); - mirror::Class* klass = class_linker_->FindClass("LMain;", class_loader.get()); + mirror::Class* klass = class_linker_->FindClass("LMain;", class_loader); ASSERT_TRUE(klass != NULL); mirror::ArtMethod* method = klass->FindDirectMethod("main", "([Ljava/lang/String;)V"); diff --git a/runtime/lock_word-inl.h b/runtime/lock_word-inl.h index efd3d9d25e9..aea10c20f84 100644 --- a/runtime/lock_word-inl.h +++ b/runtime/lock_word-inl.h @@ -36,6 +36,11 @@ inline Monitor* LockWord::FatLockMonitor() const { return reinterpret_cast(value_ << kStateSize); } +inline size_t LockWord::ForwardingAddress() const { + DCHECK_EQ(GetState(), kForwardingAddress); + return static_cast(value_ << kStateSize); +} + inline LockWord::LockWord() : value_(0) { DCHECK_EQ(GetState(), kUnlocked); } diff --git a/runtime/lock_word.h b/runtime/lock_word.h index 1882ae6504a..d24a3bbecc8 100644 --- a/runtime/lock_word.h +++ b/runtime/lock_word.h @@ -21,6 +21,7 @@ #include #include "base/logging.h" +#include "utils.h" namespace art { namespace mirror { @@ -73,6 +74,7 @@ class LockWord { kStateThinOrUnlocked = 0, kStateFat = 1, kStateHash = 2, + kStateForwardingAddress = 3, // When the state is kHashCode, the non-state bits hold the hashcode. kHashShift = 0, @@ -86,6 +88,11 @@ class LockWord { (kStateThinOrUnlocked << kStateShift)); } + static LockWord FromForwardingAddress(size_t target) { + DCHECK(IsAligned < 1 << kStateSize>(target)); + return LockWord((target >> kStateSize) | (kStateForwardingAddress << kStateShift)); + } + static LockWord FromHashCode(uint32_t hash_code) { CHECK_LE(hash_code, static_cast(kHashMask)); return LockWord((hash_code << kHashShift) | (kStateHash << kStateShift)); @@ -96,19 +103,25 @@ class LockWord { kThinLocked, // Single uncontended owner. kFatLocked, // See associated monitor. kHashCode, // Lock word contains an identity hash. + kForwardingAddress, // Lock word contains the forwarding address of an object. }; LockState GetState() const { - uint32_t internal_state = (value_ >> kStateShift) & kStateMask; - if (value_ == 0) { + if (UNLIKELY(value_ == 0)) { return kUnlocked; - } else if (internal_state == kStateThinOrUnlocked) { - return kThinLocked; - } else if (internal_state == kStateHash) { - return kHashCode; } else { - DCHECK_EQ(internal_state, static_cast(kStateFat)); - return kFatLocked; + uint32_t internal_state = (value_ >> kStateShift) & kStateMask; + switch (internal_state) { + case kStateThinOrUnlocked: + return kThinLocked; + case kStateHash: + return kHashCode; + case kStateForwardingAddress: + return kForwardingAddress; + default: + DCHECK_EQ(internal_state, static_cast(kStateFat)); + return kFatLocked; + } } } @@ -121,6 +134,9 @@ class LockWord { // Return the Monitor encoded in a fat lock. Monitor* FatLockMonitor() const; + // Return the forwarding address stored in the monitor. + size_t ForwardingAddress() const; + // Default constructor with no lock ownership. LockWord(); diff --git a/runtime/mirror/array-inl.h b/runtime/mirror/array-inl.h index c60e714d442..ef73e4df47a 100644 --- a/runtime/mirror/array-inl.h +++ b/runtime/mirror/array-inl.h @@ -66,36 +66,36 @@ static inline Array* SetArrayLength(Array* array, size_t length) { return array; } -inline Array* Array::AllocInstrumented(Thread* self, Class* array_class, int32_t component_count, - size_t component_size) { +template +inline Array* Array::Alloc(Thread* self, Class* array_class, int32_t component_count, + size_t component_size) { size_t size = ComputeArraySize(self, array_class, component_count, component_size); if (UNLIKELY(size == 0)) { return NULL; } gc::Heap* heap = Runtime::Current()->GetHeap(); - Array* array = down_cast(heap->AllocObjectInstrumented(self, array_class, size)); - return SetArrayLength(array, component_count); -} - -inline Array* Array::AllocUninstrumented(Thread* self, Class* array_class, int32_t component_count, - size_t component_size) { - size_t size = ComputeArraySize(self, array_class, component_count, component_size); - if (UNLIKELY(size == 0)) { - return NULL; + Array* array = nullptr; + if (kIsMovable) { + if (kIsInstrumented) { + array = down_cast(heap->AllocMovableObjectInstrumented(self, array_class, size)); + } else { + array = down_cast(heap->AllocMovableObjectUninstrumented(self, array_class, size)); + } + } else { + if (kIsInstrumented) { + array = down_cast(heap->AllocNonMovableObjectInstrumented(self, array_class, size)); + } else { + array = down_cast(heap->AllocNonMovableObjectUninstrumented(self, array_class, size)); + } } - gc::Heap* heap = Runtime::Current()->GetHeap(); - Array* array = down_cast(heap->AllocObjectUninstrumented(self, array_class, size)); return SetArrayLength(array, component_count); } -inline Array* Array::AllocInstrumented(Thread* self, Class* array_class, int32_t component_count) { - DCHECK(array_class->IsArrayClass()); - return AllocInstrumented(self, array_class, component_count, array_class->GetComponentSize()); -} - -inline Array* Array::AllocUninstrumented(Thread* self, Class* array_class, int32_t component_count) { +template +inline Array* Array::Alloc(Thread* self, Class* array_class, int32_t component_count) { DCHECK(array_class->IsArrayClass()); - return AllocUninstrumented(self, array_class, component_count, array_class->GetComponentSize()); + return Alloc(self, array_class, component_count, + array_class->GetComponentSize()); } } // namespace mirror diff --git a/runtime/mirror/array.cc b/runtime/mirror/array.cc index 020085dbf03..f8a283224c6 100644 --- a/runtime/mirror/array.cc +++ b/runtime/mirror/array.cc @@ -41,15 +41,16 @@ namespace mirror { // Recursively create an array with multiple dimensions. Elements may be // Objects or primitive types. static Array* RecursiveCreateMultiArray(Thread* self, Class* array_class, int current_dimension, - IntArray* dimensions) + SirtRef& dimensions) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { int32_t array_length = dimensions->Get(current_dimension); - SirtRef new_array(self, Array::Alloc(self, array_class, array_length)); + SirtRef new_array(self, Array::Alloc(self, array_class, + array_length)); if (UNLIKELY(new_array.get() == NULL)) { CHECK(self->IsExceptionPending()); return NULL; } - if ((current_dimension + 1) < dimensions->GetLength()) { + if (current_dimension + 1 < dimensions->GetLength()) { // Create a new sub-array in every element of the array. for (int32_t i = 0; i < array_length; i++) { Array* sub_array = RecursiveCreateMultiArray(self, array_class->GetComponentType(), @@ -87,13 +88,15 @@ Array* Array::CreateMultiArray(Thread* self, Class* element_class, IntArray* dim // Find/generate the array class. ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); - Class* array_class = class_linker->FindClass(descriptor.c_str(), element_class->GetClassLoader()); + SirtRef class_loader(self, element_class->GetClassLoader()); + Class* array_class = class_linker->FindClass(descriptor.c_str(), class_loader); if (UNLIKELY(array_class == NULL)) { CHECK(self->IsExceptionPending()); return NULL; } // create the array - Array* new_array = RecursiveCreateMultiArray(self, array_class, 0, dimensions); + SirtRef sirt_dimensions(self, dimensions); + Array* new_array = RecursiveCreateMultiArray(self, array_class, 0, sirt_dimensions); if (UNLIKELY(new_array == NULL)) { CHECK(self->IsExceptionPending()); return NULL; @@ -112,7 +115,7 @@ void Array::ThrowArrayStoreException(Object* object) const { template PrimitiveArray* PrimitiveArray::Alloc(Thread* self, size_t length) { DCHECK(array_class_ != NULL); - Array* raw_array = Array::Alloc(self, array_class_, length, sizeof(T)); + Array* raw_array = Array::Alloc(self, array_class_, length, sizeof(T)); return down_cast*>(raw_array); } diff --git a/runtime/mirror/array.h b/runtime/mirror/array.h index 570dcaa2926..584a4c095bb 100644 --- a/runtime/mirror/array.h +++ b/runtime/mirror/array.h @@ -24,28 +24,15 @@ namespace mirror { class MANAGED Array : public Object { public: - // A convenience for code that doesn't know the component size, - // and doesn't want to have to work it out itself. + // A convenience for code that doesn't know the component size, and doesn't want to have to work + // it out itself. + template static Array* Alloc(Thread* self, Class* array_class, int32_t component_count) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - return AllocInstrumented(self, array_class, component_count); - } - static Array* AllocUninstrumented(Thread* self, Class* array_class, int32_t component_count) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - static Array* AllocInstrumented(Thread* self, Class* array_class, int32_t component_count) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + template static Array* Alloc(Thread* self, Class* array_class, int32_t component_count, - size_t component_size) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - return AllocInstrumented(self, array_class, component_count, component_size); - } - static Array* AllocUninstrumented(Thread* self, Class* array_class, int32_t component_count, - size_t component_size) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - static Array* AllocInstrumented(Thread* self, Class* array_class, int32_t component_count, - size_t component_size) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + size_t component_size) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); static Array* CreateMultiArray(Thread* self, Class* element_class, IntArray* dimensions) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); diff --git a/runtime/mirror/class-inl.h b/runtime/mirror/class-inl.h index 7f3a302768e..406ab1bbb3a 100644 --- a/runtime/mirror/class-inl.h +++ b/runtime/mirror/class-inl.h @@ -357,14 +357,23 @@ inline void Class::CheckObjectAlloc() { DCHECK_GE(this->object_size_, sizeof(Object)); } -inline Object* Class::AllocObjectInstrumented(Thread* self) { +template +inline Object* Class::Alloc(Thread* self) { CheckObjectAlloc(); - return Runtime::Current()->GetHeap()->AllocObjectInstrumented(self, this, this->object_size_); -} - -inline Object* Class::AllocObjectUninstrumented(Thread* self) { - CheckObjectAlloc(); - return Runtime::Current()->GetHeap()->AllocObjectUninstrumented(self, this, this->object_size_); + gc::Heap* heap = Runtime::Current()->GetHeap(); + if (kIsMovable) { + if (kIsInstrumented) { + return heap->AllocMovableObjectInstrumented(self, this, this->object_size_); + } else { + return heap->AllocMovableObjectUninstrumented(self, this, this->object_size_); + } + } else { + if (kIsInstrumented) { + return heap->AllocNonMovableObjectInstrumented(self, this, this->object_size_); + } else { + return heap->AllocNonMovableObjectUninstrumented(self, this, this->object_size_); + } + } } } // namespace mirror diff --git a/runtime/mirror/class.cc b/runtime/mirror/class.cc index f3cb54aa150..cdc5ab2b399 100644 --- a/runtime/mirror/class.cc +++ b/runtime/mirror/class.cc @@ -52,7 +52,8 @@ void Class::ResetClass() { void Class::SetStatus(Status new_status, Thread* self) { Status old_status = GetStatus(); - bool class_linker_initialized = Runtime::Current()->GetClassLinker() != nullptr; + ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); + bool class_linker_initialized = class_linker != nullptr && class_linker->IsInitialized(); if (LIKELY(class_linker_initialized)) { if (UNLIKELY(new_status <= old_status && new_status != kStatusError)) { LOG(FATAL) << "Unexpected change back of class status for " << PrettyClass(this) << " " @@ -588,7 +589,6 @@ ArtField* Class::FindDeclaredStaticField(const DexCache* dex_cache, uint32_t dex ArtField* Class::FindStaticField(const StringPiece& name, const StringPiece& type) { // Is the field in this class (or its interfaces), or any of its // superclasses (or their interfaces)? - ClassHelper kh; for (Class* k = this; k != NULL; k = k->GetSuperClass()) { // Is the field in this class? ArtField* f = k->FindDeclaredStaticField(name, type); @@ -596,7 +596,7 @@ ArtField* Class::FindStaticField(const StringPiece& name, const StringPiece& typ return f; } // Is this field in any of this class' interfaces? - kh.ChangeClass(k); + ClassHelper kh(k); for (uint32_t i = 0; i < kh.NumDirectInterfaces(); ++i) { Class* interface = kh.GetDirectInterface(i); f = interface->FindStaticField(name, type); @@ -609,7 +609,6 @@ ArtField* Class::FindStaticField(const StringPiece& name, const StringPiece& typ } ArtField* Class::FindStaticField(const DexCache* dex_cache, uint32_t dex_field_idx) { - ClassHelper kh; for (Class* k = this; k != NULL; k = k->GetSuperClass()) { // Is the field in this class? ArtField* f = k->FindDeclaredStaticField(dex_cache, dex_field_idx); @@ -617,7 +616,7 @@ ArtField* Class::FindStaticField(const DexCache* dex_cache, uint32_t dex_field_i return f; } // Is this field in any of this class' interfaces? - kh.ChangeClass(k); + ClassHelper kh(k); for (uint32_t i = 0; i < kh.NumDirectInterfaces(); ++i) { Class* interface = kh.GetDirectInterface(i); f = interface->FindStaticField(dex_cache, dex_field_idx); @@ -631,7 +630,6 @@ ArtField* Class::FindStaticField(const DexCache* dex_cache, uint32_t dex_field_i ArtField* Class::FindField(const StringPiece& name, const StringPiece& type) { // Find a field using the JLS field resolution order - ClassHelper kh; for (Class* k = this; k != NULL; k = k->GetSuperClass()) { // Is the field in this class? ArtField* f = k->FindDeclaredInstanceField(name, type); @@ -643,7 +641,7 @@ ArtField* Class::FindField(const StringPiece& name, const StringPiece& type) { return f; } // Is this field in any of this class' interfaces? - kh.ChangeClass(k); + ClassHelper kh(k); for (uint32_t i = 0; i < kh.NumDirectInterfaces(); ++i) { Class* interface = kh.GetDirectInterface(i); f = interface->FindStaticField(name, type); diff --git a/runtime/mirror/class.h b/runtime/mirror/class.h index ed1aad39d21..82077dc52aa 100644 --- a/runtime/mirror/class.h +++ b/runtime/mirror/class.h @@ -378,11 +378,12 @@ class MANAGED Class : public StaticStorageBase { // Creates a raw object instance but does not invoke the default constructor. Object* AllocObject(Thread* self) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - return AllocObjectInstrumented(self); + return Alloc(self); } - Object* AllocObjectUninstrumented(Thread* self) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - Object* AllocObjectInstrumented(Thread* self) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + // Creates a raw object instance but does not invoke the default constructor. + template + Object* Alloc(Thread* self) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); bool IsVariableSize() const { // Classes and arrays vary in size, and so the object_size_ field cannot diff --git a/runtime/mirror/object.cc b/runtime/mirror/object.cc index 87d02c9469b..385ef5ff892 100644 --- a/runtime/mirror/object.cc +++ b/runtime/mirror/object.cc @@ -39,38 +39,48 @@ namespace art { namespace mirror { -Object* Object::Clone(Thread* self) { - mirror::Class* c = GetClass(); - DCHECK(!c->IsClassClass()); - // Object::SizeOf gets the right size even if we're an array. - // Using c->AllocObject() here would be wrong. - size_t num_bytes = SizeOf(); - gc::Heap* heap = Runtime::Current()->GetHeap(); - SirtRef sirt_this(self, this); - Object* copy = heap->AllocObject(self, c, num_bytes); - if (UNLIKELY(copy == nullptr)) { - return nullptr; - } +static Object* CopyObject(Thread* self, mirror::Object* dest, mirror::Object* src, size_t num_bytes) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { // Copy instance data. We assume memcpy copies by words. // TODO: expose and use move32. - byte* src_bytes = reinterpret_cast(sirt_this.get()); - byte* dst_bytes = reinterpret_cast(copy); + byte* src_bytes = reinterpret_cast(src); + byte* dst_bytes = reinterpret_cast(dest); size_t offset = sizeof(Object); memcpy(dst_bytes + offset, src_bytes + offset, num_bytes - offset); + gc::Heap* heap = Runtime::Current()->GetHeap(); // Perform write barriers on copied object references. - c = copy->GetClass(); // Re-read Class in case it moved. + Class* c = src->GetClass(); if (c->IsArrayClass()) { if (!c->GetComponentType()->IsPrimitive()) { - const ObjectArray* array = copy->AsObjectArray(); - heap->WriteBarrierArray(copy, 0, array->GetLength()); + const ObjectArray* array = dest->AsObjectArray(); + heap->WriteBarrierArray(dest, 0, array->GetLength()); } } else { - heap->WriteBarrierEveryFieldOf(copy); + heap->WriteBarrierEveryFieldOf(dest); } if (c->IsFinalizable()) { - SirtRef sirt_copy(self, copy); - heap->AddFinalizerReference(self, copy); - return sirt_copy.get(); + SirtRef sirt_dest(self, dest); + heap->AddFinalizerReference(self, dest); + return sirt_dest.get(); + } + return dest; +} + +Object* Object::Clone(Thread* self) { + CHECK(!IsClass()) << "Can't clone classes."; + // Object::SizeOf gets the right size even if we're an array. Using c->AllocObject() here would + // be wrong. + gc::Heap* heap = Runtime::Current()->GetHeap(); + size_t num_bytes = SizeOf(); + SirtRef this_object(self, this); + Object* copy; + if (heap->IsMovableObject(this)) { + copy = heap->AllocObject(self, GetClass(), num_bytes); + } else { + copy = heap->AllocNonMovableObject(self, GetClass(), num_bytes); + } + if (LIKELY(copy != nullptr)) { + return CopyObject(self, copy, this_object.get(), num_bytes); } return copy; } @@ -87,8 +97,9 @@ int32_t Object::GenerateIdentityHashCode() { } int32_t Object::IdentityHashCode() const { + mirror::Object* current_this = const_cast(this); while (true) { - LockWord lw = GetLockWord(); + LockWord lw = current_this->GetLockWord(); switch (lw.GetState()) { case LockWord::kUnlocked: { // Try to compare and swap in a new hash, if we succeed we will return the hash on the next @@ -103,7 +114,10 @@ int32_t Object::IdentityHashCode() const { case LockWord::kThinLocked: { // Inflate the thin lock to a monitor and stick the hash code inside of the monitor. Thread* self = Thread::Current(); - Monitor::InflateThinLocked(self, const_cast(this), lw, GenerateIdentityHashCode()); + SirtRef sirt_this(self, current_this); + Monitor::InflateThinLocked(self, sirt_this, lw, GenerateIdentityHashCode()); + // A GC may have occurred when we switched to kBlocked. + current_this = sirt_this.get(); break; } case LockWord::kFatLocked: { @@ -115,6 +129,10 @@ int32_t Object::IdentityHashCode() const { case LockWord::kHashCode: { return lw.GetHashCode(); } + default: { + LOG(FATAL) << "Invalid state during hashcode " << lw.GetState(); + break; + } } } LOG(FATAL) << "Unreachable"; diff --git a/runtime/mirror/object.h b/runtime/mirror/object.h index e8ea3f23759..0fb203917af 100644 --- a/runtime/mirror/object.h +++ b/runtime/mirror/object.h @@ -30,6 +30,7 @@ class LockWord; class Monitor; struct ObjectOffsets; class Thread; +template class SirtRef; namespace mirror { diff --git a/runtime/mirror/object_array-inl.h b/runtime/mirror/object_array-inl.h index abc88a3bf8d..478f4ec210d 100644 --- a/runtime/mirror/object_array-inl.h +++ b/runtime/mirror/object_array-inl.h @@ -23,6 +23,7 @@ #include "mirror/art_field.h" #include "mirror/class.h" #include "runtime.h" +#include "sirt_ref.h" #include "thread.h" namespace art { @@ -30,7 +31,7 @@ namespace mirror { template inline ObjectArray* ObjectArray::Alloc(Thread* self, Class* object_array_class, int32_t length) { - Array* array = Array::Alloc(self, object_array_class, length, sizeof(Object*)); + Array* array = Array::Alloc(self, object_array_class, length, sizeof(Object*)); if (UNLIKELY(array == NULL)) { return NULL; } else { @@ -134,9 +135,11 @@ inline void ObjectArray::Copy(const ObjectArray* src, int src_pos, template inline ObjectArray* ObjectArray::CopyOf(Thread* self, int32_t new_length) { + // We may get copied by a compacting GC. + SirtRef > sirt_this(self, this); ObjectArray* new_array = Alloc(self, GetClass(), new_length); - if (LIKELY(new_array != NULL)) { - Copy(this, 0, new_array, 0, std::min(GetLength(), new_length)); + if (LIKELY(new_array != nullptr)) { + Copy(sirt_this.get(), 0, new_array, 0, std::min(sirt_this->GetLength(), new_length)); } return new_array; } diff --git a/runtime/mirror/object_test.cc b/runtime/mirror/object_test.cc index d0d1ee4a462..853031701a3 100644 --- a/runtime/mirror/object_test.cc +++ b/runtime/mirror/object_test.cc @@ -144,15 +144,15 @@ TEST_F(ObjectTest, AllocObjectArray) { TEST_F(ObjectTest, AllocArray) { ScopedObjectAccess soa(Thread::Current()); Class* c = class_linker_->FindSystemClass("[I"); - SirtRef a(soa.Self(), Array::Alloc(soa.Self(), c, 1)); + SirtRef a(soa.Self(), Array::Alloc(soa.Self(), c, 1)); ASSERT_TRUE(c == a->GetClass()); c = class_linker_->FindSystemClass("[Ljava/lang/Object;"); - a.reset(Array::Alloc(soa.Self(), c, 1)); + a.reset(Array::Alloc(soa.Self(), c, 1)); ASSERT_TRUE(c == a->GetClass()); c = class_linker_->FindSystemClass("[[Ljava/lang/Object;"); - a.reset(Array::Alloc(soa.Self(), c, 1)); + a.reset(Array::Alloc(soa.Self(), c, 1)); ASSERT_TRUE(c == a->GetClass()); } @@ -269,8 +269,9 @@ TEST_F(ObjectTest, StaticFieldFromCode) { const DexFile* dex_file = Runtime::Current()->GetCompileTimeClassPath(class_loader)[0]; CHECK(dex_file != NULL); + SirtRef loader(soa.Self(), soa.Decode(class_loader)); Class* klass = - class_linker_->FindClass("LStaticsFromCode;", soa.Decode(class_loader)); + class_linker_->FindClass("LStaticsFromCode;", loader); ArtMethod* clinit = klass->FindClassInitializer(); const DexFile::StringId* klass_string_id = dex_file->FindStringId("LStaticsFromCode;"); ASSERT_TRUE(klass_string_id != NULL); @@ -392,6 +393,7 @@ TEST_F(ObjectTest, StringLength) { } TEST_F(ObjectTest, DescriptorCompare) { + // Two classloaders conflicts in compile_time_class_paths_. ScopedObjectAccess soa(Thread::Current()); ClassLinker* linker = class_linker_; @@ -400,9 +402,9 @@ TEST_F(ObjectTest, DescriptorCompare) { SirtRef class_loader_1(soa.Self(), soa.Decode(jclass_loader_1)); SirtRef class_loader_2(soa.Self(), soa.Decode(jclass_loader_2)); - Class* klass1 = linker->FindClass("LProtoCompare;", class_loader_1.get()); + Class* klass1 = linker->FindClass("LProtoCompare;", class_loader_1); ASSERT_TRUE(klass1 != NULL); - Class* klass2 = linker->FindClass("LProtoCompare2;", class_loader_2.get()); + Class* klass2 = linker->FindClass("LProtoCompare2;", class_loader_2); ASSERT_TRUE(klass2 != NULL); ArtMethod* m1_1 = klass1->GetVirtualMethod(0); @@ -468,8 +470,8 @@ TEST_F(ObjectTest, InstanceOf) { jobject jclass_loader = LoadDex("XandY"); SirtRef class_loader(soa.Self(), soa.Decode(jclass_loader)); - Class* X = class_linker_->FindClass("LX;", class_loader.get()); - Class* Y = class_linker_->FindClass("LY;", class_loader.get()); + Class* X = class_linker_->FindClass("LX;", class_loader); + Class* Y = class_linker_->FindClass("LY;", class_loader); ASSERT_TRUE(X != NULL); ASSERT_TRUE(Y != NULL); @@ -501,8 +503,8 @@ TEST_F(ObjectTest, IsAssignableFrom) { ScopedObjectAccess soa(Thread::Current()); jobject jclass_loader = LoadDex("XandY"); SirtRef class_loader(soa.Self(), soa.Decode(jclass_loader)); - Class* X = class_linker_->FindClass("LX;", class_loader.get()); - Class* Y = class_linker_->FindClass("LY;", class_loader.get()); + Class* X = class_linker_->FindClass("LX;", class_loader); + Class* Y = class_linker_->FindClass("LY;", class_loader); EXPECT_TRUE(X->IsAssignableFrom(X)); EXPECT_TRUE(X->IsAssignableFrom(Y)); @@ -538,17 +540,17 @@ TEST_F(ObjectTest, IsAssignableFromArray) { ScopedObjectAccess soa(Thread::Current()); jobject jclass_loader = LoadDex("XandY"); SirtRef class_loader(soa.Self(), soa.Decode(jclass_loader)); - Class* X = class_linker_->FindClass("LX;", class_loader.get()); - Class* Y = class_linker_->FindClass("LY;", class_loader.get()); + Class* X = class_linker_->FindClass("LX;", class_loader); + Class* Y = class_linker_->FindClass("LY;", class_loader); ASSERT_TRUE(X != NULL); ASSERT_TRUE(Y != NULL); - Class* YA = class_linker_->FindClass("[LY;", class_loader.get()); - Class* YAA = class_linker_->FindClass("[[LY;", class_loader.get()); + Class* YA = class_linker_->FindClass("[LY;", class_loader); + Class* YAA = class_linker_->FindClass("[[LY;", class_loader); ASSERT_TRUE(YA != NULL); ASSERT_TRUE(YAA != NULL); - Class* XAA = class_linker_->FindClass("[[LX;", class_loader.get()); + Class* XAA = class_linker_->FindClass("[[LX;", class_loader); ASSERT_TRUE(XAA != NULL); Class* O = class_linker_->FindSystemClass("Ljava/lang/Object;"); diff --git a/runtime/mirror/stack_trace_element.cc b/runtime/mirror/stack_trace_element.cc index 9d76c6bc117..32a50fe470b 100644 --- a/runtime/mirror/stack_trace_element.cc +++ b/runtime/mirror/stack_trace_element.cc @@ -39,19 +39,19 @@ void StackTraceElement::ResetClass() { } StackTraceElement* StackTraceElement::Alloc(Thread* self, - String* declaring_class, - String* method_name, - String* file_name, + SirtRef& declaring_class, + SirtRef& method_name, + SirtRef& file_name, int32_t line_number) { StackTraceElement* trace = down_cast(GetStackTraceElement()->AllocObject(self)); if (LIKELY(trace != NULL)) { trace->SetFieldObject(OFFSET_OF_OBJECT_MEMBER(StackTraceElement, declaring_class_), - const_cast(declaring_class), false); + declaring_class.get(), false); trace->SetFieldObject(OFFSET_OF_OBJECT_MEMBER(StackTraceElement, method_name_), - const_cast(method_name), false); + method_name.get(), false); trace->SetFieldObject(OFFSET_OF_OBJECT_MEMBER(StackTraceElement, file_name_), - const_cast(file_name), false); + file_name.get(), false); trace->SetField32(OFFSET_OF_OBJECT_MEMBER(StackTraceElement, line_number_), line_number, false); } diff --git a/runtime/mirror/stack_trace_element.h b/runtime/mirror/stack_trace_element.h index a9751f9988e..2af512823e2 100644 --- a/runtime/mirror/stack_trace_element.h +++ b/runtime/mirror/stack_trace_element.h @@ -18,6 +18,7 @@ #define ART_RUNTIME_MIRROR_STACK_TRACE_ELEMENT_H_ #include "object.h" +#include "sirt_ref.h" namespace art { @@ -49,9 +50,9 @@ class MANAGED StackTraceElement : public Object { } static StackTraceElement* Alloc(Thread* self, - String* declaring_class, - String* method_name, - String* file_name, + SirtRef& declaring_class, + SirtRef& method_name, + SirtRef& file_name, int32_t line_number) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); diff --git a/runtime/mirror/string.cc b/runtime/mirror/string.cc index 9c93f17f8e3..b372fe7f340 100644 --- a/runtime/mirror/string.cc +++ b/runtime/mirror/string.cc @@ -123,8 +123,8 @@ String* String::AllocFromUtf16(Thread* self, int32_t hash_code) { CHECK(utf16_data_in != NULL || utf16_length == 0); String* string = Alloc(self, GetJavaLangString(), utf16_length); - if (string == NULL) { - return NULL; + if (UNLIKELY(string == nullptr)) { + return nullptr; } // TODO: use 16-bit wide memset variant CharArray* array = const_cast(string->GetCharArray()); @@ -143,8 +143,8 @@ String* String::AllocFromUtf16(Thread* self, } String* String::AllocFromModifiedUtf8(Thread* self, const char* utf) { - if (utf == NULL) { - return NULL; + if (UNLIKELY(utf == nullptr)) { + return nullptr; } size_t char_count = CountModifiedUtf8Chars(utf); return AllocFromModifiedUtf8(self, char_count, utf); @@ -153,8 +153,8 @@ String* String::AllocFromModifiedUtf8(Thread* self, const char* utf) { String* String::AllocFromModifiedUtf8(Thread* self, int32_t utf16_length, const char* utf8_data_in) { String* string = Alloc(self, GetJavaLangString(), utf16_length); - if (string == NULL) { - return NULL; + if (UNLIKELY(string == nullptr)) { + return nullptr; } uint16_t* utf16_data_out = const_cast(string->GetCharArray()->GetData()); @@ -164,22 +164,21 @@ String* String::AllocFromModifiedUtf8(Thread* self, int32_t utf16_length, } String* String::Alloc(Thread* self, Class* java_lang_String, int32_t utf16_length) { - SirtRef array(self, CharArray::Alloc(self, utf16_length)); - if (array.get() == NULL) { - return NULL; + CharArray* array = CharArray::Alloc(self, utf16_length); + if (UNLIKELY(array == nullptr)) { + return nullptr; } - return Alloc(self, java_lang_String, array.get()); + return Alloc(self, java_lang_String, array); } String* String::Alloc(Thread* self, Class* java_lang_String, CharArray* array) { // Hold reference in case AllocObject causes GC. SirtRef array_ref(self, array); String* string = down_cast(java_lang_String->AllocObject(self)); - if (string == NULL) { - return NULL; + if (LIKELY(string != nullptr)) { + string->SetArray(array_ref.get()); + string->SetCount(array_ref->GetLength()); } - string->SetArray(array); - string->SetCount(array->GetLength()); return string; } diff --git a/runtime/mirror/string.h b/runtime/mirror/string.h index 01d8f318ffc..7520c4d33c5 100644 --- a/runtime/mirror/string.h +++ b/runtime/mirror/string.h @@ -117,10 +117,8 @@ class MANAGED String : public Object { private: void SetHashCode(int32_t new_hash_code) { - DCHECK_EQ(0u, - GetField32(OFFSET_OF_OBJECT_MEMBER(String, hash_code_), false)); - SetField32(OFFSET_OF_OBJECT_MEMBER(String, hash_code_), - new_hash_code, false); + DCHECK_EQ(0u, GetField32(OFFSET_OF_OBJECT_MEMBER(String, hash_code_), false)); + SetField32(OFFSET_OF_OBJECT_MEMBER(String, hash_code_), new_hash_code, false); } void SetCount(int32_t new_count) { diff --git a/runtime/monitor.cc b/runtime/monitor.cc index 2abfd3df417..7fada9ef81a 100644 --- a/runtime/monitor.cc +++ b/runtime/monitor.cc @@ -128,6 +128,10 @@ bool Monitor::Install(Thread* self) { LOG(FATAL) << "Inflating unlocked lock word"; break; } + default: { + LOG(FATAL) << "Invalid monitor state " << lw.GetState(); + return false; + } } LockWord fat(this); // Publish the updated lock word, which may race with other threads. @@ -140,8 +144,7 @@ bool Monitor::Install(Thread* self) { } Monitor::~Monitor() { - CHECK(obj_ != NULL); - CHECK_EQ(obj_->GetLockWord().GetState(), LockWord::kFatLocked); + // Deflated monitors have a null object. } /* @@ -559,6 +562,43 @@ void Monitor::NotifyAll(Thread* self) { } } +bool Monitor::Deflate(Thread* self, mirror::Object* obj) { + DCHECK(obj != nullptr); + LockWord lw(obj->GetLockWord()); + // If the lock isn't an inflated monitor, then we don't need to deflate anything. + if (lw.GetState() == LockWord::kFatLocked) { + Monitor* monitor = lw.FatLockMonitor(); + CHECK(monitor != nullptr); + MutexLock mu(self, monitor->monitor_lock_); + Thread* owner = monitor->owner_; + if (owner != nullptr) { + // Can't deflate if we are locked and have a hash code. + if (monitor->HasHashCode()) { + return false; + } + // Can't deflate if our lock count is too high. + if (monitor->lock_count_ > LockWord::kThinLockMaxCount) { + return false; + } + // Can't deflate if we have anybody waiting on the CV. + if (monitor->monitor_contenders_.GetNumWaiters() > 0) { + return false; + } + // Deflate to a thin lock. + obj->SetLockWord(LockWord::FromThinLockId(owner->GetTid(), monitor->lock_count_)); + } else if (monitor->HasHashCode()) { + obj->SetLockWord(LockWord::FromHashCode(monitor->GetHashCode())); + } else { + // No lock and no hash, just put an empty lock word inside the object. + obj->SetLockWord(LockWord()); + } + // The monitor is deflated, mark the object as nullptr so that we know to delete it during the + // next GC. + monitor->obj_ = nullptr; + } + return true; +} + /* * Changes the shape of a monitor from thin to fat, preserving the internal lock state. The calling * thread must own the lock or the owner must be suspended. There's a race with other threads @@ -577,13 +617,13 @@ void Monitor::Inflate(Thread* self, Thread* owner, mirror::Object* obj, int32_t } } -void Monitor::InflateThinLocked(Thread* self, mirror::Object* obj, LockWord lock_word, +void Monitor::InflateThinLocked(Thread* self, SirtRef& obj, LockWord lock_word, uint32_t hash_code) { DCHECK_EQ(lock_word.GetState(), LockWord::kThinLocked); uint32_t owner_thread_id = lock_word.ThinLockOwner(); if (owner_thread_id == self->GetThreadId()) { // We own the monitor, we can easily inflate it. - Inflate(self, self, obj, hash_code); + Inflate(self, self, obj.get(), hash_code); } else { ThreadList* thread_list = Runtime::Current()->GetThreadList(); // Suspend the owner, inflate. First change to blocked and give up mutator_lock_. @@ -598,7 +638,7 @@ void Monitor::InflateThinLocked(Thread* self, mirror::Object* obj, LockWord lock if (lock_word.GetState() == LockWord::kThinLocked && lock_word.ThinLockOwner() == owner_thread_id) { // Go ahead and inflate the lock. - Inflate(self, owner, obj, hash_code); + Inflate(self, owner, obj.get(), hash_code); } thread_list->Resume(owner, false); } @@ -611,12 +651,13 @@ void Monitor::MonitorEnter(Thread* self, mirror::Object* obj) { DCHECK(obj != NULL); uint32_t thread_id = self->GetThreadId(); size_t contention_count = 0; + SirtRef sirt_obj(self, obj); while (true) { - LockWord lock_word = obj->GetLockWord(); + LockWord lock_word = sirt_obj->GetLockWord(); switch (lock_word.GetState()) { case LockWord::kUnlocked: { LockWord thin_locked(LockWord::FromThinLockId(thread_id, 0)); - if (obj->CasLockWord(lock_word, thin_locked)) { + if (sirt_obj->CasLockWord(lock_word, thin_locked)) { return; // Success! } continue; // Go again. @@ -628,11 +669,11 @@ void Monitor::MonitorEnter(Thread* self, mirror::Object* obj) { uint32_t new_count = lock_word.ThinLockCount() + 1; if (LIKELY(new_count <= LockWord::kThinLockMaxCount)) { LockWord thin_locked(LockWord::FromThinLockId(thread_id, new_count)); - obj->SetLockWord(thin_locked); + sirt_obj->SetLockWord(thin_locked); return; // Success! } else { // We'd overflow the recursion count, so inflate the monitor. - InflateThinLocked(self, obj, lock_word, 0); + InflateThinLocked(self, sirt_obj, lock_word, 0); } } else { // Contention. @@ -642,7 +683,7 @@ void Monitor::MonitorEnter(Thread* self, mirror::Object* obj) { NanoSleep(1000); // Sleep for 1us and re-attempt. } else { contention_count = 0; - InflateThinLocked(self, obj, lock_word, 0); + InflateThinLocked(self, sirt_obj, lock_word, 0); } } continue; // Start from the beginning. @@ -654,9 +695,13 @@ void Monitor::MonitorEnter(Thread* self, mirror::Object* obj) { } case LockWord::kHashCode: { // Inflate with the existing hashcode. - Inflate(self, nullptr, obj, lock_word.GetHashCode()); + Inflate(self, nullptr, sirt_obj.get(), lock_word.GetHashCode()); break; } + default: { + LOG(FATAL) << "Invalid monitor state " << lock_word.GetState(); + return; + } } } } @@ -666,11 +711,12 @@ bool Monitor::MonitorExit(Thread* self, mirror::Object* obj) { DCHECK(obj != NULL); LockWord lock_word = obj->GetLockWord(); + SirtRef sirt_obj(self, obj); switch (lock_word.GetState()) { case LockWord::kHashCode: // Fall-through. case LockWord::kUnlocked: - FailedUnlock(obj, self, NULL, NULL); + FailedUnlock(sirt_obj.get(), self, NULL, NULL); return false; // Failure. case LockWord::kThinLocked: { uint32_t thread_id = self->GetThreadId(); @@ -679,16 +725,16 @@ bool Monitor::MonitorExit(Thread* self, mirror::Object* obj) { // TODO: there's a race here with the owner dying while we unlock. Thread* owner = Runtime::Current()->GetThreadList()->FindThreadByThreadId(lock_word.ThinLockOwner()); - FailedUnlock(obj, self, owner, NULL); + FailedUnlock(sirt_obj.get(), self, owner, NULL); return false; // Failure. } else { // We own the lock, decrease the recursion count. if (lock_word.ThinLockCount() != 0) { uint32_t new_count = lock_word.ThinLockCount() - 1; LockWord thin_locked(LockWord::FromThinLockId(thread_id, new_count)); - obj->SetLockWord(thin_locked); + sirt_obj->SetLockWord(thin_locked); } else { - obj->SetLockWord(LockWord()); + sirt_obj->SetLockWord(LockWord()); } return true; // Success! } @@ -697,9 +743,10 @@ bool Monitor::MonitorExit(Thread* self, mirror::Object* obj) { Monitor* mon = lock_word.FatLockMonitor(); return mon->Unlock(self); } - default: - LOG(FATAL) << "Unreachable"; + default: { + LOG(FATAL) << "Invalid monitor state " << lock_word.GetState(); return false; + } } } @@ -733,6 +780,10 @@ void Monitor::Wait(Thread* self, mirror::Object *obj, int64_t ms, int32_t ns, } case LockWord::kFatLocked: break; // Already set for a wait. + default: { + LOG(FATAL) << "Invalid monitor state " << lock_word.GetState(); + return; + } } Monitor* mon = lock_word.FatLockMonitor(); mon->Wait(self, ms, ns, interruptShouldThrow, why); @@ -769,6 +820,10 @@ void Monitor::DoNotify(Thread* self, mirror::Object* obj, bool notify_all) { } return; // Success. } + default: { + LOG(FATAL) << "Invalid monitor state " << lock_word.GetState(); + return; + } } } @@ -787,9 +842,10 @@ uint32_t Monitor::GetLockOwnerThreadId(mirror::Object* obj) { Monitor* mon = lock_word.FatLockMonitor(); return mon->GetOwnerThreadId(); } - default: + default: { LOG(FATAL) << "Unreachable"; return ThreadList::kInvalidThreadId; + } } } @@ -1011,7 +1067,8 @@ void MonitorList::SweepMonitorList(RootVisitor visitor, void* arg) { for (auto it = list_.begin(); it != list_.end(); ) { Monitor* m = *it; mirror::Object* obj = m->GetObject(); - mirror::Object* new_obj = visitor(obj, arg); + // The object of a monitor can be null if we have deflated it. + mirror::Object* new_obj = obj != nullptr ? visitor(obj, arg) : nullptr; if (new_obj == nullptr) { VLOG(monitor) << "freeing monitor " << m << " belonging to unmarked object " << m->GetObject(); @@ -1031,6 +1088,8 @@ MonitorInfo::MonitorInfo(mirror::Object* obj) : owner_(NULL), entry_count_(0) { switch (lock_word.GetState()) { case LockWord::kUnlocked: // Fall-through. + case LockWord::kForwardingAddress: + // Fall-through. case LockWord::kHashCode: break; case LockWord::kThinLocked: diff --git a/runtime/monitor.h b/runtime/monitor.h index 09cfafa042e..d7de8a50cd4 100644 --- a/runtime/monitor.h +++ b/runtime/monitor.h @@ -27,6 +27,7 @@ #include "atomic_integer.h" #include "base/mutex.h" #include "root_visitor.h" +#include "sirt_ref.h" #include "thread_state.h" namespace art { @@ -107,9 +108,12 @@ class Monitor { return hash_code_.load() != 0; } - static void InflateThinLocked(Thread* self, mirror::Object* obj, LockWord lock_word, + static void InflateThinLocked(Thread* self, SirtRef& obj, LockWord lock_word, uint32_t hash_code) NO_THREAD_SAFETY_ANALYSIS; + static bool Deflate(Thread* self, mirror::Object* obj) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + private: explicit Monitor(Thread* owner, mirror::Object* obj, int32_t hash_code) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); diff --git a/runtime/native/dalvik_system_DexFile.cc b/runtime/native/dalvik_system_DexFile.cc index ab5eab39556..c9e0e83b275 100644 --- a/runtime/native/dalvik_system_DexFile.cc +++ b/runtime/native/dalvik_system_DexFile.cc @@ -161,7 +161,7 @@ static jclass DexFile_defineClassNative(JNIEnv* env, jclass, jstring javaName, j ScopedObjectAccess soa(env); ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); class_linker->RegisterDexFile(*dex_file); - mirror::ClassLoader* class_loader = soa.Decode(javaLoader); + SirtRef class_loader(soa.Self(), soa.Decode(javaLoader)); mirror::Class* result = class_linker->DefineClass(descriptor.c_str(), class_loader, *dex_file, *dex_class_def); VLOG(class_linker) << "DexFile_defineClassNative returning " << result; diff --git a/runtime/native/dalvik_system_VMRuntime.cc b/runtime/native/dalvik_system_VMRuntime.cc index aef000cf10d..f0efdc20742 100644 --- a/runtime/native/dalvik_system_VMRuntime.cc +++ b/runtime/native/dalvik_system_VMRuntime.cc @@ -53,18 +53,9 @@ static void VMRuntime_startJitCompilation(JNIEnv*, jobject) { static void VMRuntime_disableJitCompilation(JNIEnv*, jobject) { } -static jobject VMRuntime_newNonMovableArray(JNIEnv* env, - jobject, - jclass javaElementClass, +static jobject VMRuntime_newNonMovableArray(JNIEnv* env, jobject, jclass javaElementClass, jint length) { ScopedFastNativeObjectAccess soa(env); -#ifdef MOVING_GARBAGE_COLLECTOR - // TODO: right now, we don't have a copying collector, so there's no need - // to do anything special here, but we ought to pass the non-movability - // through to the allocator. - UNIMPLEMENTED(FATAL); -#endif - mirror::Class* element_class = soa.Decode(javaElementClass); if (element_class == NULL) { ThrowNullPointerException(NULL, "element class == null"); @@ -74,13 +65,13 @@ static jobject VMRuntime_newNonMovableArray(JNIEnv* env, ThrowNegativeArraySizeException(length); return NULL; } - ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); std::string descriptor; descriptor += "["; descriptor += ClassHelper(element_class).GetDescriptor(); - mirror::Class* array_class = class_linker->FindClass(descriptor.c_str(), NULL); - mirror::Array* result = mirror::Array::Alloc(soa.Self(), array_class, length); + SirtRef class_loader(soa.Self(), nullptr); + mirror::Class* array_class = class_linker->FindClass(descriptor.c_str(), class_loader); + mirror::Array* result = mirror::Array::Alloc(soa.Self(), array_class, length); return soa.AddLocalReference(result); } @@ -94,7 +85,10 @@ static jlong VMRuntime_addressOf(JNIEnv* env, jobject, jobject javaArray) { ThrowIllegalArgumentException(NULL, "not an array"); return 0; } - // TODO: we should also check that this is a non-movable array. + if (Runtime::Current()->GetHeap()->IsMovableObject(array)) { + ThrowRuntimeException("Trying to get address of movable array object"); + return 0; + } return reinterpret_cast(array->GetRawData(array->GetClass()->GetComponentSize())); } @@ -172,28 +166,7 @@ static void VMRuntime_registerNativeFree(JNIEnv* env, jobject, jint bytes) { } static void VMRuntime_trimHeap(JNIEnv*, jobject) { - uint64_t start_ns = NanoTime(); - - // Trim the managed heap. - gc::Heap* heap = Runtime::Current()->GetHeap(); - float managed_utilization = (static_cast(heap->GetBytesAllocated()) / - heap->GetTotalMemory()); - size_t managed_reclaimed = heap->Trim(); - - uint64_t gc_heap_end_ns = NanoTime(); - - // Trim the native heap. - dlmalloc_trim(0); - size_t native_reclaimed = 0; - dlmalloc_inspect_all(DlmallocMadviseCallback, &native_reclaimed); - - uint64_t end_ns = NanoTime(); - - LOG(INFO) << "Heap trim of managed (duration=" << PrettyDuration(gc_heap_end_ns - start_ns) - << ", advised=" << PrettySize(managed_reclaimed) << ") and native (duration=" - << PrettyDuration(end_ns - gc_heap_end_ns) << ", advised=" << PrettySize(native_reclaimed) - << ") heaps. Managed heap utilization of " << static_cast(100 * managed_utilization) - << "%."; + Runtime::Current()->GetHeap()->Trim(); } static void VMRuntime_concurrentGC(JNIEnv* env, jobject) { @@ -212,7 +185,7 @@ static mirror::Object* PreloadDexCachesStringsVisitor(mirror::Object* root, void } // Based on ClassLinker::ResolveString. -static void PreloadDexCachesResolveString(mirror::DexCache* dex_cache, +static void PreloadDexCachesResolveString(SirtRef& dex_cache, uint32_t string_idx, StringTable& strings) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { @@ -260,7 +233,7 @@ static void PreloadDexCachesResolveType(mirror::DexCache* dex_cache, uint32_t ty } // Based on ClassLinker::ResolveField. -static void PreloadDexCachesResolveField(mirror::DexCache* dex_cache, +static void PreloadDexCachesResolveField(SirtRef& dex_cache, uint32_t field_idx, bool is_static) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { @@ -275,9 +248,9 @@ static void PreloadDexCachesResolveField(mirror::DexCache* dex_cache, return; } if (is_static) { - field = klass->FindStaticField(dex_cache, field_idx); + field = klass->FindStaticField(dex_cache.get(), field_idx); } else { - field = klass->FindInstanceField(dex_cache, field_idx); + field = klass->FindInstanceField(dex_cache.get(), field_idx); } if (field == NULL) { return; @@ -287,7 +260,7 @@ static void PreloadDexCachesResolveField(mirror::DexCache* dex_cache, } // Based on ClassLinker::ResolveMethod. -static void PreloadDexCachesResolveMethod(mirror::DexCache* dex_cache, +static void PreloadDexCachesResolveMethod(SirtRef& dex_cache, uint32_t method_idx, InvokeType invoke_type) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { @@ -304,14 +277,14 @@ static void PreloadDexCachesResolveMethod(mirror::DexCache* dex_cache, switch (invoke_type) { case kDirect: case kStatic: - method = klass->FindDirectMethod(dex_cache, method_idx); + method = klass->FindDirectMethod(dex_cache.get(), method_idx); break; case kInterface: - method = klass->FindInterfaceMethod(dex_cache, method_idx); + method = klass->FindInterfaceMethod(dex_cache.get(), method_idx); break; case kSuper: case kVirtual: - method = klass->FindVirtualMethod(dex_cache, method_idx); + method = klass->FindVirtualMethod(dex_cache.get(), method_idx); break; default: LOG(FATAL) << "Unreachable - invocation type: " << invoke_type; @@ -430,6 +403,7 @@ static void VMRuntime_preloadDexCaches(JNIEnv* env, jobject) { Runtime* runtime = Runtime::Current(); ClassLinker* linker = runtime->GetClassLinker(); + Thread* self = ThreadForEnv(env); // We use a std::map to avoid heap allocating StringObjects to lookup in gDvm.literalStrings StringTable strings; @@ -441,7 +415,7 @@ static void VMRuntime_preloadDexCaches(JNIEnv* env, jobject) { for (size_t i = 0; i< boot_class_path.size(); i++) { const DexFile* dex_file = boot_class_path[i]; CHECK(dex_file != NULL); - mirror::DexCache* dex_cache = linker->FindDexCache(*dex_file); + SirtRef dex_cache(self, linker->FindDexCache(*dex_file)); if (kPreloadDexCachesStrings) { for (size_t i = 0; i < dex_cache->NumStrings(); i++) { @@ -451,7 +425,7 @@ static void VMRuntime_preloadDexCaches(JNIEnv* env, jobject) { if (kPreloadDexCachesTypes) { for (size_t i = 0; i < dex_cache->NumResolvedTypes(); i++) { - PreloadDexCachesResolveType(dex_cache, i); + PreloadDexCachesResolveType(dex_cache.get(), i); } } diff --git a/runtime/native/java_lang_Class.cc b/runtime/native/java_lang_Class.cc index 3591611185d..33891078f88 100644 --- a/runtime/native/java_lang_Class.cc +++ b/runtime/native/java_lang_Class.cc @@ -61,7 +61,8 @@ static jclass Class_classForName(JNIEnv* env, jclass, jstring javaName, jboolean } std::string descriptor(DotToDescriptor(name.c_str())); - mirror::ClassLoader* class_loader = soa.Decode(javaLoader); + SirtRef class_loader(soa.Self(), + soa.Decode(javaLoader)); ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); mirror::Class* c = class_linker->FindClass(descriptor.c_str(), class_loader); if (c == NULL) { diff --git a/runtime/native/java_lang_reflect_Array.cc b/runtime/native/java_lang_reflect_Array.cc index a2d6b180267..808c9170d90 100644 --- a/runtime/native/java_lang_reflect_Array.cc +++ b/runtime/native/java_lang_reflect_Array.cc @@ -52,13 +52,15 @@ static jobject Array_createObjectArray(JNIEnv* env, jclass, jclass javaElementCl descriptor += ClassHelper(element_class).GetDescriptor(); ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); - mirror::Class* array_class = class_linker->FindClass(descriptor.c_str(), element_class->GetClassLoader()); + SirtRef class_loader(soa.Self(), element_class->GetClassLoader()); + mirror::Class* array_class = class_linker->FindClass(descriptor.c_str(), class_loader); if (UNLIKELY(array_class == NULL)) { CHECK(soa.Self()->IsExceptionPending()); return NULL; } DCHECK(array_class->IsArrayClass()); - mirror::Array* new_array = mirror::Array::Alloc(soa.Self(), array_class, length); + mirror::Array* new_array = mirror::Array::Alloc( + soa.Self(), array_class, length); return soa.AddLocalReference(new_array); } diff --git a/runtime/native/java_lang_reflect_Proxy.cc b/runtime/native/java_lang_reflect_Proxy.cc index a92823a85d8..809369ae81b 100644 --- a/runtime/native/java_lang_reflect_Proxy.cc +++ b/runtime/native/java_lang_reflect_Proxy.cc @@ -23,20 +23,12 @@ namespace art { -static jclass Proxy_generateProxy(JNIEnv* env, jclass, jstring javaName, - jobjectArray javaInterfaces, jobject javaLoader, - jobjectArray javaMethods, jobjectArray javaThrows) { +static jclass Proxy_generateProxy(JNIEnv* env, jclass, jstring name, jobjectArray interfaces, + jobject loader, jobjectArray methods, jobjectArray throws) { ScopedObjectAccess soa(env); - mirror::String* name = soa.Decode(javaName); - mirror::ObjectArray* interfaces = - soa.Decode*>(javaInterfaces); - mirror::ClassLoader* loader = soa.Decode(javaLoader); - mirror::ObjectArray* methods = - soa.Decode*>(javaMethods); - mirror::ObjectArray >* throws = - soa.Decode >*>(javaThrows); ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); - mirror::Class* result = class_linker->CreateProxyClass(name, interfaces, loader, methods, throws); + mirror::Class* result = class_linker->CreateProxyClass(soa, name, interfaces, loader, methods, + throws); return soa.AddLocalReference(result); } diff --git a/runtime/native/scoped_fast_native_object_access.h b/runtime/native/scoped_fast_native_object_access.h index d941ec31f00..1658d96de50 100644 --- a/runtime/native/scoped_fast_native_object_access.h +++ b/runtime/native/scoped_fast_native_object_access.h @@ -63,10 +63,6 @@ class ScopedFastNativeObjectAccess { Locks::mutator_lock_->AssertSharedHeld(Self()); // Don't work with raw objects in non-runnable states. DCHECK_EQ(Self()->GetState(), kRunnable); -#ifdef MOVING_GARBAGE_COLLECTOR - // TODO: we should make these unique weak globals if Field instances can ever move. - UNIMPLEMENTED(WARNING); -#endif return reinterpret_cast(fid); } @@ -83,6 +79,10 @@ class ScopedFastNativeObjectAccess { return NULL; } + if (kIsDebugBuild) { + Runtime::Current()->GetHeap()->VerifyObject(obj); + } + DCHECK_NE((reinterpret_cast(obj) & 0xffff0000), 0xebad0000); IndirectReferenceTable& locals = Env()->locals; diff --git a/runtime/object_utils.h b/runtime/object_utils.h index f724776f2e6..e37510c228c 100644 --- a/runtime/object_utils.h +++ b/runtime/object_utils.h @@ -67,12 +67,9 @@ class ObjectLock { class ClassHelper { public: - ClassHelper(const mirror::Class* c = NULL, ClassLinker* l = NULL) + explicit ClassHelper(const mirror::Class* c ) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) - : class_linker_(l), - dex_cache_(NULL), - dex_file_(NULL), - interface_type_list_(NULL), + : interface_type_list_(NULL), klass_(NULL) { if (c != NULL) { ChangeClass(c); @@ -82,13 +79,9 @@ class ClassHelper { void ChangeClass(const mirror::Class* new_c) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { CHECK(new_c != NULL) << "klass_=" << klass_; // Log what we were changing from if any - CHECK(new_c->IsClass()) << "new_c=" << new_c; - if (dex_cache_ != NULL) { - mirror::DexCache* new_c_dex_cache = new_c->GetDexCache(); - if (new_c_dex_cache != dex_cache_) { - dex_cache_ = new_c_dex_cache; - dex_file_ = NULL; - } + if (!new_c->IsClass()) { + LOG(FATAL) << "new_c=" << new_c << " cc " << new_c->GetClass() << " ccc " + << ((new_c->GetClass() != nullptr) ? new_c->GetClass()->GetClass() : NULL); } klass_ = new_c; interface_type_list_ = NULL; @@ -201,20 +194,11 @@ class ClassHelper { } const DexFile& GetDexFile() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - if (dex_file_ == NULL) { - dex_file_ = GetDexCache()->GetDexFile(); - } - return *dex_file_; + return *GetDexCache()->GetDexFile(); } mirror::DexCache* GetDexCache() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - mirror::DexCache* result = dex_cache_; - if (result == NULL) { - DCHECK(klass_ != NULL); - result = klass_->GetDexCache(); - dex_cache_ = result; - } - return result; + return klass_->GetDexCache(); } private: @@ -231,18 +215,10 @@ class ClassHelper { return result; } - ClassLinker* GetClassLinker() { - ClassLinker* result = class_linker_; - if (result == NULL) { - result = Runtime::Current()->GetClassLinker(); - class_linker_ = result; - } - return result; + ClassLinker* GetClassLinker() ALWAYS_INLINE { + return Runtime::Current()->GetClassLinker(); } - ClassLinker* class_linker_; - mirror::DexCache* dex_cache_; - const DexFile* dex_file_; const DexFile::TypeList* interface_type_list_; const mirror::Class* klass_; std::string descriptor_; @@ -252,20 +228,11 @@ class ClassHelper { class FieldHelper { public: - FieldHelper() : class_linker_(NULL), dex_cache_(NULL), dex_file_(NULL), field_(NULL) {} - explicit FieldHelper(const mirror::ArtField* f) : class_linker_(NULL), dex_cache_(NULL), dex_file_(NULL), field_(f) {} - FieldHelper(const mirror::ArtField* f, ClassLinker* l) - : class_linker_(l), dex_cache_(NULL), dex_file_(NULL), field_(f) {} + FieldHelper() : field_(NULL) {} + explicit FieldHelper(const mirror::ArtField* f) : field_(f) {} void ChangeField(const mirror::ArtField* new_f) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { DCHECK(new_f != NULL); - if (dex_cache_ != NULL) { - mirror::DexCache* new_f_dex_cache = new_f->GetDeclaringClass()->GetDexCache(); - if (new_f_dex_cache != dex_cache_) { - dex_cache_ = new_f_dex_cache; - dex_file_ = NULL; - } - } field_ = new_f; } @@ -343,31 +310,14 @@ class FieldHelper { private: mirror::DexCache* GetDexCache() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - mirror::DexCache* result = dex_cache_; - if (result == NULL) { - result = field_->GetDeclaringClass()->GetDexCache(); - dex_cache_ = result; - } - return result; + return field_->GetDeclaringClass()->GetDexCache(); } - ClassLinker* GetClassLinker() { - ClassLinker* result = class_linker_; - if (result == NULL) { - result = Runtime::Current()->GetClassLinker(); - class_linker_ = result; - } - return result; + ClassLinker* GetClassLinker() ALWAYS_INLINE { + return Runtime::Current()->GetClassLinker(); } const DexFile& GetDexFile() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - if (dex_file_ == NULL) { - dex_file_ = GetDexCache()->GetDexFile(); - } - return *dex_file_; + return *GetDexCache()->GetDexFile(); } - - ClassLinker* class_linker_; - mirror::DexCache* dex_cache_; - const DexFile* dex_file_; const mirror::ArtField* field_; std::string declaring_class_descriptor_; @@ -377,38 +327,17 @@ class FieldHelper { class MethodHelper { public: MethodHelper() - : class_linker_(NULL), dex_cache_(NULL), dex_file_(NULL), method_(NULL), shorty_(NULL), + : method_(NULL), shorty_(NULL), shorty_len_(0) {} explicit MethodHelper(const mirror::ArtMethod* m) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) - : class_linker_(NULL), dex_cache_(NULL), dex_file_(NULL), method_(NULL), shorty_(NULL), - shorty_len_(0) { - SetMethod(m); - } - - MethodHelper(const mirror::ArtMethod* m, ClassLinker* l) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) - : class_linker_(l), dex_cache_(NULL), dex_file_(NULL), method_(NULL), shorty_(NULL), - shorty_len_(0) { + : method_(NULL), shorty_(NULL), shorty_len_(0) { SetMethod(m); } void ChangeMethod(mirror::ArtMethod* new_m) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { DCHECK(new_m != NULL); - if (dex_cache_ != NULL) { - mirror::Class* klass = new_m->GetDeclaringClass(); - if (klass->IsProxyClass()) { - dex_cache_ = NULL; - dex_file_ = NULL; - } else { - mirror::DexCache* new_m_dex_cache = klass->GetDexCache(); - if (new_m_dex_cache != dex_cache_) { - dex_cache_ = new_m_dex_cache; - dex_file_ = NULL; - } - } - } SetMethod(new_m); shorty_ = NULL; } @@ -444,7 +373,8 @@ class MethodHelper { const DexFile& dex_file = GetDexFile(); uint32_t dex_method_idx = method_->GetDexMethodIndex(); const DexFile::MethodId& method_id = dex_file.GetMethodId(dex_method_idx); - return GetClassLinker()->ResolveString(dex_file, method_id.name_idx_, GetDexCache()); + SirtRef dex_cache(Thread::Current(), GetDexCache()); + return GetClassLinker()->ResolveString(dex_file, method_id.name_idx_, dex_cache); } const char* GetShorty() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { @@ -622,28 +552,18 @@ class MethodHelper { } const DexFile& GetDexFile() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - const DexFile* result = dex_file_; - if (result == NULL) { - const mirror::DexCache* dex_cache = GetDexCache(); - result = dex_file_ = dex_cache->GetDexFile(); - } - return *result; + return *GetDexCache()->GetDexFile(); } mirror::DexCache* GetDexCache() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - mirror::DexCache* result = dex_cache_; - if (result == NULL) { - mirror::Class* klass = method_->GetDeclaringClass(); - result = klass->GetDexCache(); - dex_cache_ = result; - } - return result; + return method_->GetDeclaringClass()->GetDexCache(); } mirror::String* ResolveString(uint32_t string_idx) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { mirror::String* s = method_->GetDexCacheStrings()->Get(string_idx); if (UNLIKELY(s == NULL)) { - s = GetClassLinker()->ResolveString(GetDexFile(), string_idx, GetDexCache()); + SirtRef dex_cache(Thread::Current(), GetDexCache()); + s = GetClassLinker()->ResolveString(GetDexFile(), string_idx, dex_cache); } return s; } @@ -705,18 +625,10 @@ class MethodHelper { method_ = method; } - ClassLinker* GetClassLinker() { - ClassLinker* result = class_linker_; - if (result == NULL) { - result = Runtime::Current()->GetClassLinker(); - class_linker_ = result; - } - return result; + ClassLinker* GetClassLinker() ALWAYS_INLINE { + return Runtime::Current()->GetClassLinker(); } - ClassLinker* class_linker_; - mirror::DexCache* dex_cache_; - const DexFile* dex_file_; const mirror::ArtMethod* method_; const char* shorty_; uint32_t shorty_len_; diff --git a/runtime/reference_table.cc b/runtime/reference_table.cc index e95fdb92269..6f65bff8997 100644 --- a/runtime/reference_table.cc +++ b/runtime/reference_table.cc @@ -233,7 +233,7 @@ void ReferenceTable::Dump(std::ostream& os, const Table& entries) { void ReferenceTable::VisitRoots(RootVisitor* visitor, void* arg) { for (auto& ref : entries_) { - ref = visitor(const_cast(ref), arg); + ref = visitor(ref, arg); } } diff --git a/runtime/root_visitor.h b/runtime/root_visitor.h index a2d898b43cb..d52f3511519 100644 --- a/runtime/root_visitor.h +++ b/runtime/root_visitor.h @@ -23,11 +23,13 @@ class Object; } // namespace mirror class StackVisitor; +// Returns the new address of the object, returns root if it has not moved. typedef mirror::Object* (RootVisitor)(mirror::Object* root, void* arg) __attribute__((warn_unused_result)); typedef void (VerifyRootVisitor)(const mirror::Object* root, void* arg, size_t vreg, const StackVisitor* visitor); typedef bool (IsMarkedTester)(const mirror::Object* object, void* arg); +typedef void (ObjectVisitorCallback)(mirror::Object* obj, void* arg); } // namespace art diff --git a/runtime/runtime.cc b/runtime/runtime.cc index 53c9b2efbb4..8e39023cf5d 100644 --- a/runtime/runtime.cc +++ b/runtime/runtime.cc @@ -121,7 +121,7 @@ Runtime::~Runtime() { Trace::Shutdown(); // Make sure to let the GC complete if it is running. - heap_->WaitForConcurrentGcToComplete(self); + heap_->WaitForGcToComplete(self); heap_->DeleteThreadPool(); // Make sure our internal threads are dead before we start tearing down things they're using. @@ -821,6 +821,11 @@ void Runtime::StartSignalCatcher() { } } +bool Runtime::IsShuttingDown(Thread* self) { + MutexLock mu(self, *Locks::runtime_shutdown_lock_); + return IsShuttingDownLocked(); +} + void Runtime::StartDaemonThreads() { VLOG(startup) << "Runtime::StartDaemonThreads entering"; @@ -861,7 +866,6 @@ bool Runtime::Init(const Options& raw_options, bool ignore_unrecognized) { is_compiler_ = options->is_compiler_; is_zygote_ = options->is_zygote_; - is_concurrent_gc_enabled_ = options->is_concurrent_gc_enabled_; is_explicit_gc_disabled_ = options->is_explicit_gc_disabled_; compiler_filter_ = options->compiler_filter_; @@ -926,12 +930,13 @@ bool Runtime::Init(const Options& raw_options, bool ignore_unrecognized) { GetHeap()->EnableObjectValidation(); CHECK_GE(GetHeap()->GetContinuousSpaces().size(), 1U); - if (GetHeap()->GetContinuousSpaces()[0]->IsImageSpace()) { - class_linker_ = ClassLinker::CreateFromImage(intern_table_); + class_linker_ = new ClassLinker(intern_table_); + if (GetHeap()->HasImageSpace()) { + class_linker_->InitFromImage(); } else { CHECK(options->boot_class_path_ != NULL); CHECK_NE(options->boot_class_path_->size(), 0U); - class_linker_ = ClassLinker::CreateFromCompiler(*options->boot_class_path_, intern_table_); + class_linker_->InitFromCompiler(*options->boot_class_path_); } CHECK(class_linker_ != NULL); verifier::MethodVerifier::Init(); @@ -1174,16 +1179,20 @@ void Runtime::VisitNonThreadRoots(RootVisitor* visitor, void* arg) { visitor(pre_allocated_OutOfMemoryError_, arg)); DCHECK(pre_allocated_OutOfMemoryError_ != nullptr); } - resolution_method_ = reinterpret_cast(visitor(resolution_method_, arg)); + resolution_method_ = down_cast(visitor(resolution_method_, arg)); DCHECK(resolution_method_ != nullptr); - imt_conflict_method_ = reinterpret_cast(visitor(imt_conflict_method_, arg)); - DCHECK(imt_conflict_method_ != nullptr); - default_imt_ = reinterpret_cast*>(visitor(default_imt_, arg)); - DCHECK(default_imt_ != nullptr); + if (HasImtConflictMethod()) { + imt_conflict_method_ = down_cast(visitor(imt_conflict_method_, arg)); + } + if (HasDefaultImt()) { + default_imt_ = down_cast*>(visitor(default_imt_, arg)); + } + for (int i = 0; i < Runtime::kLastCalleeSaveType; i++) { - callee_save_methods_[i] = reinterpret_cast( - visitor(callee_save_methods_[i], arg)); - DCHECK(callee_save_methods_[i] != nullptr); + if (callee_save_methods_[i] != nullptr) { + callee_save_methods_[i] = down_cast( + visitor(callee_save_methods_[i], arg)); + } } } @@ -1201,49 +1210,45 @@ mirror::ObjectArray* Runtime::CreateDefaultImt(ClassLinker* c Thread* self = Thread::Current(); SirtRef > imtable(self, cl->AllocArtMethodArray(self, 64)); mirror::ArtMethod* imt_conflict_method = Runtime::Current()->GetImtConflictMethod(); - for (size_t i = 0; i < 64; i++) { + for (size_t i = 0; i < static_cast(imtable->GetLength()); i++) { imtable->Set(i, imt_conflict_method); } return imtable.get(); } mirror::ArtMethod* Runtime::CreateImtConflictMethod() { - mirror::Class* method_class = mirror::ArtMethod::GetJavaLangReflectArtMethod(); Thread* self = Thread::Current(); - SirtRef - method(self, down_cast(method_class->AllocObject(self))); - method->SetDeclaringClass(method_class); + Runtime* r = Runtime::Current(); + ClassLinker* cl = r->GetClassLinker(); + SirtRef method(self, cl->AllocArtMethod(self)); + method->SetDeclaringClass(mirror::ArtMethod::GetJavaLangReflectArtMethod()); // TODO: use a special method for imt conflict method saves method->SetDexMethodIndex(DexFile::kDexNoIndex); // When compiling, the code pointer will get set later when the image is loaded. - Runtime* r = Runtime::Current(); - ClassLinker* cl = r->GetClassLinker(); method->SetEntryPointFromCompiledCode(r->IsCompiler() ? NULL : GetImtConflictTrampoline(cl)); return method.get(); } mirror::ArtMethod* Runtime::CreateResolutionMethod() { - mirror::Class* method_class = mirror::ArtMethod::GetJavaLangReflectArtMethod(); Thread* self = Thread::Current(); - SirtRef - method(self, down_cast(method_class->AllocObject(self))); - method->SetDeclaringClass(method_class); + Runtime* r = Runtime::Current(); + ClassLinker* cl = r->GetClassLinker(); + SirtRef method(self, cl->AllocArtMethod(self)); + method->SetDeclaringClass(mirror::ArtMethod::GetJavaLangReflectArtMethod()); // TODO: use a special method for resolution method saves method->SetDexMethodIndex(DexFile::kDexNoIndex); // When compiling, the code pointer will get set later when the image is loaded. - Runtime* r = Runtime::Current(); - ClassLinker* cl = r->GetClassLinker(); method->SetEntryPointFromCompiledCode(r->IsCompiler() ? NULL : GetResolutionTrampoline(cl)); return method.get(); } mirror::ArtMethod* Runtime::CreateCalleeSaveMethod(InstructionSet instruction_set, - CalleeSaveType type) { - mirror::Class* method_class = mirror::ArtMethod::GetJavaLangReflectArtMethod(); + CalleeSaveType type) { Thread* self = Thread::Current(); - SirtRef - method(self, down_cast(method_class->AllocObject(self))); - method->SetDeclaringClass(method_class); + Runtime* r = Runtime::Current(); + ClassLinker* cl = r->GetClassLinker(); + SirtRef method(self, cl->AllocArtMethod(self)); + method->SetDeclaringClass(mirror::ArtMethod::GetJavaLangReflectArtMethod()); // TODO: use a special method for callee saves method->SetDexMethodIndex(DexFile::kDexNoIndex); method->SetEntryPointFromCompiledCode(NULL); diff --git a/runtime/runtime.h b/runtime/runtime.h index 24b4c876667..d025d47b75d 100644 --- a/runtime/runtime.h +++ b/runtime/runtime.h @@ -149,10 +149,6 @@ class Runtime { return is_zygote_; } - bool IsConcurrentGcEnabled() const { - return is_concurrent_gc_enabled_; - } - bool IsExplicitGcDisabled() const { return is_explicit_gc_disabled_; } @@ -203,7 +199,8 @@ class Runtime { // Starts a runtime, which may cause threads to be started and code to run. bool Start() UNLOCK_FUNCTION(Locks::mutator_lock_); - bool IsShuttingDown() const EXCLUSIVE_LOCKS_REQUIRED(Locks::runtime_shutdown_lock_) { + bool IsShuttingDown(Thread* self); + bool IsShuttingDownLocked() const EXCLUSIVE_LOCKS_REQUIRED(Locks::runtime_shutdown_lock_) { return shutting_down_; } diff --git a/runtime/scoped_thread_state_change.h b/runtime/scoped_thread_state_change.h index c39cdb26792..1ca6c4e4fae 100644 --- a/runtime/scoped_thread_state_change.h +++ b/runtime/scoped_thread_state_change.h @@ -34,9 +34,8 @@ class ScopedThreadStateChange { if (UNLIKELY(self_ == NULL)) { // Value chosen arbitrarily and won't be used in the destructor since thread_ == NULL. old_thread_state_ = kTerminated; - MutexLock mu(NULL, *Locks::runtime_shutdown_lock_); Runtime* runtime = Runtime::Current(); - CHECK(runtime == NULL || !runtime->IsStarted() || runtime->IsShuttingDown()); + CHECK(runtime == NULL || !runtime->IsStarted() || runtime->IsShuttingDown(self_)); } else { bool runnable_transition; DCHECK_EQ(self, Thread::Current()); @@ -63,9 +62,8 @@ class ScopedThreadStateChange { ~ScopedThreadStateChange() LOCKS_EXCLUDED(Locks::thread_suspend_count_lock_) ALWAYS_INLINE { if (UNLIKELY(self_ == NULL)) { if (!expected_has_no_thread_) { - MutexLock mu(NULL, *Locks::runtime_shutdown_lock_); Runtime* runtime = Runtime::Current(); - bool shutting_down = (runtime == NULL) || runtime->IsShuttingDown(); + bool shutting_down = (runtime == NULL) || runtime->IsShuttingDown(nullptr); CHECK(shutting_down); } } else { @@ -167,6 +165,10 @@ class ScopedObjectAccessUnchecked : public ScopedThreadStateChange { return NULL; } + if (kIsDebugBuild) { + Runtime::Current()->GetHeap()->VerifyObject(obj); + } + DCHECK_NE((reinterpret_cast(obj) & 0xffff0000), 0xebad0000); IndirectReferenceTable& locals = Env()->locals; @@ -185,7 +187,6 @@ class ScopedObjectAccessUnchecked : public ScopedThreadStateChange { } } #endif - if (Vm()->work_around_app_jni_bugs) { // Hand out direct pointers to support broken old apps. return reinterpret_cast(obj); @@ -206,10 +207,7 @@ class ScopedObjectAccessUnchecked : public ScopedThreadStateChange { SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { Locks::mutator_lock_->AssertSharedHeld(Self()); DCHECK_EQ(thread_state_, kRunnable); // Don't work with raw objects in non-runnable states. -#ifdef MOVING_GARBAGE_COLLECTOR - // TODO: we should make these unique weak globals if Field instances can ever move. - UNIMPLEMENTED(WARNING); -#endif + CHECK(!kMovingFields); return reinterpret_cast(fid); } @@ -217,9 +215,7 @@ class ScopedObjectAccessUnchecked : public ScopedThreadStateChange { SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { Locks::mutator_lock_->AssertSharedHeld(Self()); DCHECK_EQ(thread_state_, kRunnable); // Don't work with raw objects in non-runnable states. -#ifdef MOVING_GARBAGE_COLLECTOR - UNIMPLEMENTED(WARNING); -#endif + CHECK(!kMovingFields); return reinterpret_cast(field); } @@ -227,10 +223,7 @@ class ScopedObjectAccessUnchecked : public ScopedThreadStateChange { SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { Locks::mutator_lock_->AssertSharedHeld(Self()); DCHECK_EQ(thread_state_, kRunnable); // Don't work with raw objects in non-runnable states. -#ifdef MOVING_GARBAGE_COLLECTOR - // TODO: we should make these unique weak globals if Method instances can ever move. - UNIMPLEMENTED(WARNING); -#endif + CHECK(!kMovingMethods); return reinterpret_cast(mid); } @@ -238,9 +231,7 @@ class ScopedObjectAccessUnchecked : public ScopedThreadStateChange { SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { Locks::mutator_lock_->AssertSharedHeld(Self()); DCHECK_EQ(thread_state_, kRunnable); // Don't work with raw objects in non-runnable states. -#ifdef MOVING_GARBAGE_COLLECTOR - UNIMPLEMENTED(WARNING); -#endif + CHECK(!kMovingMethods); return reinterpret_cast(method); } diff --git a/runtime/sirt_ref.h b/runtime/sirt_ref.h index a1f8a6693f7..56d81ecacc8 100644 --- a/runtime/sirt_ref.h +++ b/runtime/sirt_ref.h @@ -17,6 +17,7 @@ #ifndef ART_RUNTIME_SIRT_REF_H_ #define ART_RUNTIME_SIRT_REF_H_ +#include "base/casts.h" #include "base/logging.h" #include "base/macros.h" #include "thread.h" diff --git a/runtime/stack.cc b/runtime/stack.cc index 5d3a9a5234c..a50538399c8 100644 --- a/runtime/stack.cc +++ b/runtime/stack.cc @@ -22,12 +22,17 @@ #include "mirror/object-inl.h" #include "mirror/object_array-inl.h" #include "object_utils.h" +#include "runtime.h" #include "thread_list.h" #include "throw_location.h" #include "vmap_table.h" namespace art { +bool ShadowFrame::VerifyReference(const mirror::Object* val) const { + return !Runtime::Current()->GetHeap()->IsInTempSpace(val); +} + mirror::Object* ShadowFrame::GetThisObject() const { mirror::ArtMethod* m = GetMethod(); if (m->IsStatic()) { diff --git a/runtime/stack.h b/runtime/stack.h index a4b93bc4071..3d6b06a32d7 100644 --- a/runtime/stack.h +++ b/runtime/stack.h @@ -150,10 +150,15 @@ class ShadowFrame { return *reinterpret_cast(vreg); } + template mirror::Object* GetVRegReference(size_t i) const { DCHECK_LT(i, NumberOfVRegs()); if (HasReferenceArray()) { mirror::Object* ref = References()[i]; + if (kChecked) { + CHECK(VerifyReference(ref)) << "VReg " << i << "(" << ref + << ") is in protected space, reference array " << true; + } // If the vreg reference is not equal to the vreg then the vreg reference is stale. if (reinterpret_cast(ref) != vregs_[i]) { return nullptr; @@ -161,7 +166,12 @@ class ShadowFrame { return ref; } else { const uint32_t* vreg = &vregs_[i]; - return *reinterpret_cast(vreg); + mirror::Object* ref = *reinterpret_cast(vreg); + if (kChecked) { + CHECK(VerifyReference(ref)) << "VReg " << i + << "(" << ref << ") is in protected space, reference array " << false; + } + return ref; } } @@ -174,12 +184,22 @@ class ShadowFrame { DCHECK_LT(i, NumberOfVRegs()); uint32_t* vreg = &vregs_[i]; *reinterpret_cast(vreg) = val; + // This is needed for moving collectors since these can update the vreg references if they + // happen to agree with references in the reference array. + if (kMovingCollector && HasReferenceArray()) { + References()[i] = nullptr; + } } void SetVRegFloat(size_t i, float val) { DCHECK_LT(i, NumberOfVRegs()); uint32_t* vreg = &vregs_[i]; *reinterpret_cast(vreg) = val; + // This is needed for moving collectors since these can update the vreg references if they + // happen to agree with references in the reference array. + if (kMovingCollector && HasReferenceArray()) { + References()[i] = nullptr; + } } void SetVRegLong(size_t i, int64_t val) { @@ -188,6 +208,12 @@ class ShadowFrame { // Alignment attribute required for GCC 4.8 typedef int64_t unaligned_int64 __attribute__ ((aligned (4))); *reinterpret_cast(vreg) = val; + // This is needed for moving collectors since these can update the vreg references if they + // happen to agree with references in the reference array. + if (kMovingCollector && HasReferenceArray()) { + References()[i] = nullptr; + References()[i + 1] = nullptr; + } } void SetVRegDouble(size_t i, double val) { @@ -196,10 +222,18 @@ class ShadowFrame { // Alignment attribute required for GCC 4.8 typedef double unaligned_double __attribute__ ((aligned (4))); *reinterpret_cast(vreg) = val; + // This is needed for moving collectors since these can update the vreg references if they + // happen to agree with references in the reference array. + if (kMovingCollector && HasReferenceArray()) { + References()[i] = nullptr; + References()[i + 1] = nullptr; + } } void SetVRegReference(size_t i, mirror::Object* val) { DCHECK_LT(i, NumberOfVRegs()); + DCHECK(!kMovingCollector || VerifyReference(val)) + << "VReg " << i << "(" << val << ") is in protected space"; uint32_t* vreg = &vregs_[i]; *reinterpret_cast(vreg) = val; if (HasReferenceArray()) { @@ -280,6 +314,8 @@ class ShadowFrame { return reinterpret_cast(vreg_end); } + bool VerifyReference(const mirror::Object* val) const; + mirror::Object** References() { return const_cast(const_cast(this)->References()); } diff --git a/runtime/thread.cc b/runtime/thread.cc index 97510762353..1f6dd69110d 100644 --- a/runtime/thread.cc +++ b/runtime/thread.cc @@ -152,7 +152,7 @@ void* Thread::CreateCallback(void* arg) { MutexLock mu(NULL, *Locks::runtime_shutdown_lock_); // Check that if we got here we cannot be shutting down (as shutdown should never have started // while threads are being born). - CHECK(!runtime->IsShuttingDown()); + CHECK(!runtime->IsShuttingDownLocked()); self->Init(runtime->GetThreadList(), runtime->GetJavaVM()); Runtime::Current()->EndThreadBirth(); } @@ -241,7 +241,7 @@ void Thread::CreateNativeThread(JNIEnv* env, jobject java_peer, size_t stack_siz bool thread_start_during_shutdown = false; { MutexLock mu(self, *Locks::runtime_shutdown_lock_); - if (runtime->IsShuttingDown()) { + if (runtime->IsShuttingDownLocked()) { thread_start_during_shutdown = true; } else { runtime->StartThreadBirth(); @@ -328,7 +328,7 @@ Thread* Thread::Attach(const char* thread_name, bool as_daemon, jobject thread_g } { MutexLock mu(NULL, *Locks::runtime_shutdown_lock_); - if (runtime->IsShuttingDown()) { + if (runtime->IsShuttingDownLocked()) { LOG(ERROR) << "Thread attaching while runtime is shutting down: " << thread_name; return NULL; } else { @@ -1352,13 +1352,12 @@ jobjectArray Thread::InternalStackTraceToStackTraceElementArray(JNIEnv* env, job *stack_depth = depth; } - MethodHelper mh; for (int32_t i = 0; i < depth; ++i) { mirror::ObjectArray* method_trace = soa.Decode*>(internal); // Prepare parameters for StackTraceElement(String cls, String method, String file, int line) mirror::ArtMethod* method = down_cast(method_trace->Get(i)); - mh.ChangeMethod(method); + MethodHelper mh(method); mirror::IntArray* pc_trace = down_cast(method_trace->Get(depth)); uint32_t dex_pc = pc_trace->Get(i); int32_t line_number = mh.GetLineNumFromDexPC(dex_pc); @@ -1385,11 +1384,8 @@ jobjectArray Thread::InternalStackTraceToStackTraceElementArray(JNIEnv* env, job SirtRef source_name_object(soa.Self(), mirror::String::AllocFromModifiedUtf8(soa.Self(), source_file)); - mirror::StackTraceElement* obj = mirror::StackTraceElement::Alloc(soa.Self(), - class_name_object.get(), - method_name_object.get(), - source_name_object.get(), - line_number); + mirror::StackTraceElement* obj = mirror::StackTraceElement::Alloc( + soa.Self(), class_name_object, method_name_object, source_name_object, line_number); if (obj == NULL) { return NULL; } @@ -1437,8 +1433,10 @@ void Thread::ThrowNewWrappedException(const ThrowLocation& throw_location, if (throw_location.GetMethod() != NULL) { cl = throw_location.GetMethod()->GetDeclaringClass()->GetClassLoader(); } + SirtRef class_loader(this, cl); SirtRef - exception_class(this, runtime->GetClassLinker()->FindClass(exception_class_descriptor, cl)); + exception_class(this, runtime->GetClassLinker()->FindClass(exception_class_descriptor, + class_loader)); if (UNLIKELY(exception_class.get() == NULL)) { CHECK(IsExceptionPending()); LOG(ERROR) << "No exception class " << PrettyDescriptor(exception_class_descriptor); @@ -1453,6 +1451,12 @@ void Thread::ThrowNewWrappedException(const ThrowLocation& throw_location, SirtRef exception(this, down_cast(exception_class->AllocObject(this))); + // If we couldn't allocate the exception, throw the pre-allocated out of memory exception. + if (exception.get() == nullptr) { + SetException(throw_location, Runtime::Current()->GetPreAllocatedOutOfMemoryError()); + return; + } + // Choose an appropriate constructor and set up the arguments. const char* signature; SirtRef msg_string(this, NULL); @@ -1741,18 +1745,21 @@ class CatchBlockStackVisitor : public StackVisitor { return true; // Continue stack walk. } - bool HandleDeoptimization(mirror::ArtMethod* m) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + bool HandleDeoptimization(mirror::ArtMethod* m) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { MethodHelper mh(m); const DexFile::CodeItem* code_item = mh.GetCodeItem(); CHECK(code_item != NULL); - uint16_t num_regs = code_item->registers_size_; + uint16_t num_regs = code_item->registers_size_; uint32_t dex_pc = GetDexPc(); const Instruction* inst = Instruction::At(code_item->insns_ + dex_pc); uint32_t new_dex_pc = dex_pc + inst->SizeInCodeUnits(); ShadowFrame* new_frame = ShadowFrame::Create(num_regs, NULL, m, new_dex_pc); - verifier::MethodVerifier verifier(&mh.GetDexFile(), mh.GetDexCache(), mh.GetClassLoader(), - &mh.GetClassDef(), code_item, - m->GetDexMethodIndex(), m, m->GetAccessFlags(), false, true); + SirtRef dex_cache(self_, mh.GetDexCache()); + SirtRef class_loader(self_, mh.GetClassLoader()); + verifier::MethodVerifier verifier(&mh.GetDexFile(), &dex_cache, &class_loader, + &mh.GetClassDef(), code_item, m->GetDexMethodIndex(), m, + m->GetAccessFlags(), false, true); verifier.Verify(); std::vector kinds = verifier.DescribeVRegs(dex_pc); for (uint16_t reg = 0; reg < num_regs; reg++) { @@ -2088,6 +2095,13 @@ class VerifyCallbackVisitor { void* const arg_; }; +void Thread::SetClassLoaderOverride(mirror::ClassLoader* class_loader_override) { + if (kIsDebugBuild) { + Runtime::Current()->GetHeap()->VerifyObject(class_loader_override); + } + class_loader_override_ = class_loader_override; +} + void Thread::VisitRoots(RootVisitor* visitor, void* arg) { if (opeer_ != nullptr) { opeer_ = visitor(opeer_, arg); @@ -2115,10 +2129,9 @@ void Thread::VisitRoots(RootVisitor* visitor, void* arg) { for (instrumentation::InstrumentationStackFrame& frame : *GetInstrumentationStack()) { if (frame.this_object_ != nullptr) { frame.this_object_ = visitor(frame.this_object_, arg); - DCHECK(frame.this_object_ != nullptr); } - frame.method_ = reinterpret_cast(visitor(frame.method_, arg)); DCHECK(frame.method_ != nullptr); + frame.method_ = reinterpret_cast(visitor(frame.method_, arg)); } } diff --git a/runtime/thread.h b/runtime/thread.h index 3aa137375ee..6bd3607922e 100644 --- a/runtime/thread.h +++ b/runtime/thread.h @@ -177,34 +177,27 @@ class PACKED(4) Thread { ALWAYS_INLINE; // Once called thread suspension will cause an assertion failure. -#ifndef NDEBUG const char* StartAssertNoThreadSuspension(const char* cause) { - CHECK(cause != NULL); - const char* previous_cause = last_no_thread_suspension_cause_; - no_thread_suspension_++; - last_no_thread_suspension_cause_ = cause; - return previous_cause; - } -#else - const char* StartAssertNoThreadSuspension(const char* cause) { - CHECK(cause != NULL); - return NULL; + if (kIsDebugBuild) { + CHECK(cause != NULL); + const char* previous_cause = last_no_thread_suspension_cause_; + no_thread_suspension_++; + last_no_thread_suspension_cause_ = cause; + return previous_cause; + } else { + return nullptr; + } } -#endif // End region where no thread suspension is expected. -#ifndef NDEBUG void EndAssertNoThreadSuspension(const char* old_cause) { - CHECK(old_cause != NULL || no_thread_suspension_ == 1); - CHECK_GT(no_thread_suspension_, 0U); - no_thread_suspension_--; - last_no_thread_suspension_cause_ = old_cause; - } -#else - void EndAssertNoThreadSuspension(const char*) { + if (kIsDebugBuild) { + CHECK(old_cause != NULL || no_thread_suspension_ == 1); + CHECK_GT(no_thread_suspension_, 0U); + no_thread_suspension_--; + last_no_thread_suspension_cause_ = old_cause; + } } -#endif - void AssertThreadSuspensionIsAllowable(bool check_locks = true) const; @@ -370,9 +363,7 @@ class PACKED(4) Thread { return class_loader_override_; } - void SetClassLoaderOverride(mirror::ClassLoader* class_loader_override) { - class_loader_override_ = class_loader_override; - } + void SetClassLoaderOverride(mirror::ClassLoader* class_loader_override); // Create the internal representation of a stack trace, that is more time // and space efficient to compute than the StackTraceElement[] diff --git a/runtime/thread_list.cc b/runtime/thread_list.cc index ff1ed2a4d27..dd3f11cb9e0 100644 --- a/runtime/thread_list.cc +++ b/runtime/thread_list.cc @@ -74,6 +74,15 @@ pid_t ThreadList::GetLockOwner() { return Locks::thread_list_lock_->GetExclusiveOwnerTid(); } +void ThreadList::DumpNativeStacks(std::ostream& os) { + MutexLock mu(Thread::Current(), *Locks::thread_list_lock_); + for (const auto& thread : list_) { + os << "DUMPING THREAD " << thread->tid_ << "\n"; + DumpNativeStack(os, thread->tid_, "\t", true); + os << "\n"; + } +} + void ThreadList::DumpForSigQuit(std::ostream& os) { { MutexLock mu(Thread::Current(), *Locks::thread_list_lock_); @@ -413,7 +422,7 @@ Thread* ThreadList::SuspendThreadByPeer(jobject peer, bool request_suspension, return thread; } if (total_delay_us >= kTimeoutUs) { - ThreadSuspendByPeerWarning(self, ERROR, "Thread suspension timed out", peer); + ThreadSuspendByPeerWarning(self, FATAL, "Thread suspension timed out", peer); if (did_suspend_request) { thread->ModifySuspendCount(soa.Self(), -1, debug_suspension); } @@ -477,7 +486,7 @@ Thread* ThreadList::SuspendThreadByThreadId(uint32_t thread_id, bool debug_suspe return thread; } if (total_delay_us >= kTimeoutUs) { - ThreadSuspendByThreadIdWarning(ERROR, "Thread suspension timed out", thread_id); + ThreadSuspendByThreadIdWarning(WARNING, "Thread suspension timed out", thread_id); if (did_suspend_request) { thread->ModifySuspendCount(soa.Self(), -1, debug_suspension); } @@ -626,7 +635,7 @@ void ThreadList::WaitForOtherNonDaemonThreadsToExit() { { // No more threads can be born after we start to shutdown. MutexLock mu(self, *Locks::runtime_shutdown_lock_); - CHECK(Runtime::Current()->IsShuttingDown()); + CHECK(Runtime::Current()->IsShuttingDownLocked()); CHECK_EQ(Runtime::Current()->NumberOfThreadsBeingBorn(), 0U); } all_threads_are_daemons = true; diff --git a/runtime/thread_list.h b/runtime/thread_list.h index b1b3e888605..45994ae9b7e 100644 --- a/runtime/thread_list.h +++ b/runtime/thread_list.h @@ -124,6 +124,9 @@ class ThreadList { return list_; } + void DumpNativeStacks(std::ostream& os) + LOCKS_EXCLUDED(Locks::thread_list_lock_); + private: uint32_t AllocThreadId(Thread* self); void ReleaseThreadId(Thread* self, uint32_t id) LOCKS_EXCLUDED(allocated_ids_lock_); diff --git a/runtime/utils.h b/runtime/utils.h index 6850e8b0259..4b39acd5cf8 100644 --- a/runtime/utils.h +++ b/runtime/utils.h @@ -122,7 +122,7 @@ struct TypeStaticIf { // For rounding integers. template static inline T RoundDown(T x, int n) { - CHECK(IsPowerOfTwo(n)); + DCHECK(IsPowerOfTwo(n)); return (x & -n); } diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc index 9f980610a43..9cd8f738d77 100644 --- a/runtime/verifier/method_verifier.cc +++ b/runtime/verifier/method_verifier.cc @@ -39,6 +39,7 @@ #include "object_utils.h" #include "register_line-inl.h" #include "runtime.h" +#include "scoped_thread_state_change.h" #include "verifier/dex_gc_map.h" namespace art { @@ -113,17 +114,15 @@ MethodVerifier::FailureKind MethodVerifier::VerifyClass(const mirror::Class* kla *error += dex_file.GetLocation(); return kHardFailure; } - return VerifyClass(&dex_file, - kh.GetDexCache(), - klass->GetClassLoader(), - class_def, - allow_soft_failures, - error); + Thread* self = Thread::Current(); + SirtRef dex_cache(self, kh.GetDexCache()); + SirtRef class_loader(self, klass->GetClassLoader()); + return VerifyClass(&dex_file, dex_cache, class_loader, class_def, allow_soft_failures, error); } MethodVerifier::FailureKind MethodVerifier::VerifyClass(const DexFile* dex_file, - mirror::DexCache* dex_cache, - mirror::ClassLoader* class_loader, + SirtRef& dex_cache, + SirtRef& class_loader, const DexFile::ClassDef* class_def, bool allow_soft_failures, std::string* error) { @@ -233,8 +232,8 @@ MethodVerifier::FailureKind MethodVerifier::VerifyClass(const DexFile* dex_file, MethodVerifier::FailureKind MethodVerifier::VerifyMethod(uint32_t method_idx, const DexFile* dex_file, - mirror::DexCache* dex_cache, - mirror::ClassLoader* class_loader, + SirtRef& dex_cache, + SirtRef& class_loader, const DexFile::ClassDef* class_def, const DexFile::CodeItem* code_item, mirror::ArtMethod* method, @@ -243,8 +242,8 @@ MethodVerifier::FailureKind MethodVerifier::VerifyMethod(uint32_t method_idx, MethodVerifier::FailureKind result = kNoFailure; uint64_t start_ns = NanoTime(); - MethodVerifier verifier_(dex_file, dex_cache, class_loader, class_def, code_item, method_idx, - method, method_access_flags, true, allow_soft_failures); + MethodVerifier verifier_(dex_file, &dex_cache, &class_loader, class_def, code_item, + method_idx, method, method_access_flags, true, allow_soft_failures); if (verifier_.Verify()) { // Verification completed, however failures may be pending that didn't cause the verification // to hard fail. @@ -277,13 +276,14 @@ MethodVerifier::FailureKind MethodVerifier::VerifyMethod(uint32_t method_idx, } void MethodVerifier::VerifyMethodAndDump(std::ostream& os, uint32_t dex_method_idx, - const DexFile* dex_file, mirror::DexCache* dex_cache, - mirror::ClassLoader* class_loader, + const DexFile* dex_file, + SirtRef& dex_cache, + SirtRef& class_loader, const DexFile::ClassDef* class_def, const DexFile::CodeItem* code_item, mirror::ArtMethod* method, uint32_t method_access_flags) { - MethodVerifier verifier(dex_file, dex_cache, class_loader, class_def, code_item, + MethodVerifier verifier(dex_file, &dex_cache, &class_loader, class_def, code_item, dex_method_idx, method, method_access_flags, true, true); verifier.Verify(); verifier.DumpFailures(os); @@ -291,13 +291,12 @@ void MethodVerifier::VerifyMethodAndDump(std::ostream& os, uint32_t dex_method_i verifier.Dump(os); } -MethodVerifier::MethodVerifier(const DexFile* dex_file, mirror::DexCache* dex_cache, - mirror::ClassLoader* class_loader, +MethodVerifier::MethodVerifier(const DexFile* dex_file, SirtRef* dex_cache, + SirtRef* class_loader, const DexFile::ClassDef* class_def, - const DexFile::CodeItem* code_item, - uint32_t dex_method_idx, mirror::ArtMethod* method, - uint32_t method_access_flags, bool can_load_classes, - bool allow_soft_failures) + const DexFile::CodeItem* code_item, uint32_t dex_method_idx, + mirror::ArtMethod* method, uint32_t method_access_flags, + bool can_load_classes, bool allow_soft_failures) : reg_types_(can_load_classes), work_insn_idx_(-1), dex_method_idx_(dex_method_idx), @@ -323,12 +322,19 @@ MethodVerifier::MethodVerifier(const DexFile* dex_file, mirror::DexCache* dex_ca DCHECK(class_def != nullptr); } +MethodVerifier::~MethodVerifier() { + STLDeleteElements(&failure_messages_); +} + void MethodVerifier::FindLocksAtDexPc(mirror::ArtMethod* m, uint32_t dex_pc, std::vector& monitor_enter_dex_pcs) { MethodHelper mh(m); - MethodVerifier verifier(&mh.GetDexFile(), mh.GetDexCache(), mh.GetClassLoader(), - &mh.GetClassDef(), mh.GetCodeItem(), m->GetDexMethodIndex(), - m, m->GetAccessFlags(), false, true); + Thread* self = Thread::Current(); + SirtRef dex_cache(self, mh.GetDexCache()); + SirtRef class_loader(self, mh.GetClassLoader()); + MethodVerifier verifier(&mh.GetDexFile(), &dex_cache, &class_loader, &mh.GetClassDef(), + mh.GetCodeItem(), m->GetDexMethodIndex(), m, m->GetAccessFlags(), false, + true); verifier.interesting_dex_pc_ = dex_pc; verifier.monitor_enter_dex_pcs_ = &monitor_enter_dex_pcs; verifier.FindLocksAtDexPc(); @@ -348,9 +354,12 @@ void MethodVerifier::FindLocksAtDexPc() { mirror::ArtField* MethodVerifier::FindAccessedFieldAtDexPc(mirror::ArtMethod* m, uint32_t dex_pc) { MethodHelper mh(m); - MethodVerifier verifier(&mh.GetDexFile(), mh.GetDexCache(), mh.GetClassLoader(), - &mh.GetClassDef(), mh.GetCodeItem(), m->GetDexMethodIndex(), - m, m->GetAccessFlags(), false, true); + Thread* self = Thread::Current(); + SirtRef dex_cache(self, mh.GetDexCache()); + SirtRef class_loader(self, mh.GetClassLoader()); + MethodVerifier verifier(&mh.GetDexFile(), &dex_cache, &class_loader, &mh.GetClassDef(), + mh.GetCodeItem(), m->GetDexMethodIndex(), m, m->GetAccessFlags(), false, + true); return verifier.FindAccessedFieldAtDexPc(dex_pc); } @@ -374,11 +383,14 @@ mirror::ArtField* MethodVerifier::FindAccessedFieldAtDexPc(uint32_t dex_pc) { } mirror::ArtMethod* MethodVerifier::FindInvokedMethodAtDexPc(mirror::ArtMethod* m, - uint32_t dex_pc) { + uint32_t dex_pc) { MethodHelper mh(m); - MethodVerifier verifier(&mh.GetDexFile(), mh.GetDexCache(), mh.GetClassLoader(), - &mh.GetClassDef(), mh.GetCodeItem(), m->GetDexMethodIndex(), - m, m->GetAccessFlags(), false, true); + Thread* self = Thread::Current(); + SirtRef dex_cache(self, mh.GetDexCache()); + SirtRef class_loader(self, mh.GetClassLoader()); + MethodVerifier verifier(&mh.GetDexFile(), &dex_cache, &class_loader, &mh.GetClassDef(), + mh.GetCodeItem(), m->GetDexMethodIndex(), m, m->GetAccessFlags(), false, + true); return verifier.FindInvokedMethodAtDexPc(dex_pc); } @@ -589,7 +601,7 @@ bool MethodVerifier::ScanTryCatchBlocks() { if (iterator.GetHandlerTypeIndex() != DexFile::kDexNoIndex16) { mirror::Class* exception_type = linker->ResolveType(*dex_file_, iterator.GetHandlerTypeIndex(), - dex_cache_, class_loader_); + *dex_cache_, *class_loader_); if (exception_type == NULL) { DCHECK(Thread::Current()->IsExceptionPending()); Thread::Current()->ClearException(); @@ -1211,7 +1223,8 @@ bool MethodVerifier::SetTypesFromSignature() { // it's effectively considered initialized the instant we reach here (in the sense that we // can return without doing anything or call virtual methods). { - const RegType& reg_type = reg_types_.FromDescriptor(class_loader_, descriptor, false); + const RegType& reg_type = reg_types_.FromDescriptor(class_loader_->get(), descriptor, + false); reg_line->SetRegisterType(arg_start + cur_arg, reg_type); } break; @@ -1853,7 +1866,8 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "invalid fill-array-data with array type " << array_type; } else { - const RegType& component_type = reg_types_.GetComponentType(array_type, class_loader_); + const RegType& component_type = reg_types_.GetComponentType(array_type, + class_loader_->get()); DCHECK(!component_type.IsConflict()); if (component_type.IsNonZeroReferenceTypes()) { Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "invalid fill-array-data with component type " @@ -2168,7 +2182,7 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { const DexFile::MethodId& method_id = dex_file_->GetMethodId(method_idx); uint32_t return_type_idx = dex_file_->GetProtoId(method_id.proto_idx_).return_type_idx_; const char* descriptor = dex_file_->StringByTypeIdx(return_type_idx); - return_type = ®_types_.FromDescriptor(class_loader_, descriptor, false); + return_type = ®_types_.FromDescriptor(class_loader_->get(), descriptor, false); } if (!return_type->IsLowHalf()) { work_line_->SetResultRegisterType(*return_type); @@ -2235,8 +2249,8 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { */ work_line_->MarkRefsAsInitialized(this_type); } - const RegType& return_type = reg_types_.FromDescriptor(class_loader_, return_type_descriptor, - false); + const RegType& return_type = reg_types_.FromDescriptor(class_loader_->get(), + return_type_descriptor, false); if (!return_type.IsLowHalf()) { work_line_->SetResultRegisterType(return_type); } else { @@ -2257,11 +2271,12 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { uint32_t method_idx = (is_range) ? inst->VRegB_3rc() : inst->VRegB_35c(); const DexFile::MethodId& method_id = dex_file_->GetMethodId(method_idx); uint32_t return_type_idx = dex_file_->GetProtoId(method_id.proto_idx_).return_type_idx_; - descriptor = dex_file_->StringByTypeIdx(return_type_idx); + descriptor = dex_file_->StringByTypeIdx(return_type_idx); } else { descriptor = MethodHelper(called_method).GetReturnTypeDescriptor(); } - const RegType& return_type = reg_types_.FromDescriptor(class_loader_, descriptor, false); + const RegType& return_type = reg_types_.FromDescriptor(class_loader_->get(), descriptor, + false); if (!return_type.IsLowHalf()) { work_line_->SetResultRegisterType(return_type); } else { @@ -2318,7 +2333,8 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { } else { descriptor = MethodHelper(abs_method).GetReturnTypeDescriptor(); } - const RegType& return_type = reg_types_.FromDescriptor(class_loader_, descriptor, false); + const RegType& return_type = reg_types_.FromDescriptor(class_loader_->get(), descriptor, + false); if (!return_type.IsLowHalf()) { work_line_->SetResultRegisterType(return_type); } else { @@ -2584,7 +2600,8 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { mirror::ArtMethod* called_method = VerifyInvokeVirtualQuickArgs(inst, is_range); if (called_method != NULL) { const char* descriptor = MethodHelper(called_method).GetReturnTypeDescriptor(); - const RegType& return_type = reg_types_.FromDescriptor(class_loader_, descriptor, false); + const RegType& return_type = reg_types_.FromDescriptor(class_loader_->get(), descriptor, + false); if (!return_type.IsLowHalf()) { work_line_->SetResultRegisterType(return_type); } else { @@ -2850,18 +2867,18 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { const RegType& MethodVerifier::ResolveClassAndCheckAccess(uint32_t class_idx) { const char* descriptor = dex_file_->StringByTypeIdx(class_idx); const RegType& referrer = GetDeclaringClass(); - mirror::Class* klass = dex_cache_->GetResolvedType(class_idx); + mirror::Class* klass = (*dex_cache_)->GetResolvedType(class_idx); const RegType& result = klass != NULL ? reg_types_.FromClass(descriptor, klass, klass->CannotBeAssignedFromOtherTypes()) - : reg_types_.FromDescriptor(class_loader_, descriptor, false); + : reg_types_.FromDescriptor(class_loader_->get(), descriptor, false); if (result.IsConflict()) { Fail(VERIFY_ERROR_BAD_CLASS_SOFT) << "accessing broken descriptor '" << descriptor << "' in " << referrer; return result; } if (klass == NULL && !result.IsUnresolvedTypes()) { - dex_cache_->SetResolvedType(class_idx, result.GetClass()); + (*dex_cache_)->SetResolvedType(class_idx, result.GetClass()); } // Check if access is allowed. Unresolved types use xxxWithAccessCheck to // check at runtime if access is allowed and so pass here. If result is @@ -2935,7 +2952,7 @@ mirror::ArtMethod* MethodVerifier::ResolveMethodAndCheckAccess(uint32_t dex_meth } mirror::Class* klass = klass_type.GetClass(); const RegType& referrer = GetDeclaringClass(); - mirror::ArtMethod* res_method = dex_cache_->GetResolvedMethod(dex_method_idx); + mirror::ArtMethod* res_method = (*dex_cache_)->GetResolvedMethod(dex_method_idx); if (res_method == NULL) { const char* name = dex_file_->GetMethodName(method_id); const Signature signature = dex_file_->GetMethodSignature(method_id); @@ -2948,7 +2965,7 @@ mirror::ArtMethod* MethodVerifier::ResolveMethodAndCheckAccess(uint32_t dex_meth res_method = klass->FindVirtualMethod(name, signature); } if (res_method != NULL) { - dex_cache_->SetResolvedMethod(dex_method_idx, res_method); + (*dex_cache_)->SetResolvedMethod(dex_method_idx, res_method); } else { // If a virtual or interface method wasn't found with the expected type, look in // the direct methods. This can happen when the wrong invoke type is used or when @@ -3112,7 +3129,7 @@ mirror::ArtMethod* MethodVerifier::VerifyInvocationArgs(const Instruction* inst, << " missing signature component"; return NULL; } - const RegType& reg_type = reg_types_.FromDescriptor(class_loader_, descriptor, false); + const RegType& reg_type = reg_types_.FromDescriptor(class_loader_->get(), descriptor, false); uint32_t get_reg = is_range ? inst->VRegC_3rc() + actual_args : arg[actual_args]; if (reg_type.IsIntegralTypes()) { const RegType& src_type = work_line_->GetRegisterType(get_reg); @@ -3136,8 +3153,7 @@ mirror::ArtMethod* MethodVerifier::VerifyInvocationArgs(const Instruction* inst, } mirror::ArtMethod* MethodVerifier::GetQuickInvokedMethod(const Instruction* inst, - RegisterLine* reg_line, - bool is_range) { + RegisterLine* reg_line, bool is_range) { DCHECK(inst->Opcode() == Instruction::INVOKE_VIRTUAL_QUICK || inst->Opcode() == Instruction::INVOKE_VIRTUAL_RANGE_QUICK); const RegType& actual_arg_type = reg_line->GetInvocationThis(inst, is_range); @@ -3152,11 +3168,13 @@ mirror::ArtMethod* MethodVerifier::GetQuickInvokedMethod(const Instruction* inst } else { const std::string& descriptor(actual_arg_type.GetDescriptor()); ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); - this_class = class_linker->FindClass(descriptor.c_str(), class_loader_); + this_class = class_linker->FindClass(descriptor.c_str(), *class_loader_); if (this_class == NULL) { - Thread::Current()->ClearException(); + Thread* self = Thread::Current(); + self->ClearException(); // Look for a system class - this_class = class_linker->FindClass(descriptor.c_str(), NULL); + SirtRef null_class_loader(self, nullptr); + this_class = class_linker->FindClass(descriptor.c_str(), null_class_loader); } } if (this_class == NULL) { @@ -3246,7 +3264,7 @@ mirror::ArtMethod* MethodVerifier::VerifyInvokeVirtualQuickArgs(const Instructio << " missing signature component"; return NULL; } - const RegType& reg_type = reg_types_.FromDescriptor(class_loader_, descriptor, false); + const RegType& reg_type = reg_types_.FromDescriptor(class_loader_->get(), descriptor, false); uint32_t get_reg = is_range ? inst->VRegC_3rc() + actual_args : arg[actual_args]; if (!work_line_->VerifyRegisterType(get_reg, reg_type)) { return res_method; @@ -3290,7 +3308,7 @@ void MethodVerifier::VerifyNewArray(const Instruction* inst, bool is_filled, boo } else { // Verify each register. If "arg_count" is bad, VerifyRegisterType() will run off the end of // the list and fail. It's legal, if silly, for arg_count to be zero. - const RegType& expected_type = reg_types_.GetComponentType(res_type, class_loader_); + const RegType& expected_type = reg_types_.GetComponentType(res_type, class_loader_->get()); uint32_t arg_count = (is_range) ? inst->VRegA_3rc() : inst->VRegA_35c(); uint32_t arg[5]; if (!is_range) { @@ -3332,7 +3350,7 @@ void MethodVerifier::VerifyAGet(const Instruction* inst, Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "not array type " << array_type << " with aget"; } else { /* verify the class */ - const RegType& component_type = reg_types_.GetComponentType(array_type, class_loader_); + const RegType& component_type = reg_types_.GetComponentType(array_type, class_loader_->get()); if (!component_type.IsReferenceTypes() && !is_primitive) { Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "primitive array type " << array_type << " source for aget-object"; @@ -3409,7 +3427,7 @@ void MethodVerifier::VerifyAPut(const Instruction* inst, } else if (!array_type.IsArrayTypes()) { Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "not array type " << array_type << " with aput"; } else { - const RegType& component_type = reg_types_.GetComponentType(array_type, class_loader_); + const RegType& component_type = reg_types_.GetComponentType(array_type, class_loader_->get()); const uint32_t vregA = inst->VRegA_23x(); if (is_primitive) { VerifyPrimitivePut(component_type, insn_type, vregA); @@ -3441,10 +3459,9 @@ mirror::ArtField* MethodVerifier::GetStaticField(int field_idx) { if (klass_type.IsUnresolvedTypes()) { return NULL; // Can't resolve Class so no more to do here, will do checking at runtime. } - mirror::ArtField* field = Runtime::Current()->GetClassLinker()->ResolveFieldJLS(*dex_file_, - field_idx, - dex_cache_, - class_loader_); + ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); + mirror::ArtField* field = class_linker->ResolveFieldJLS(*dex_file_, field_idx, *dex_cache_, + *class_loader_); if (field == NULL) { VLOG(verifier) << "Unable to resolve static field " << field_idx << " (" << dex_file_->GetFieldName(field_id) << ") in " @@ -3460,9 +3477,8 @@ mirror::ArtField* MethodVerifier::GetStaticField(int field_idx) { } else if (!field->IsStatic()) { Fail(VERIFY_ERROR_CLASS_CHANGE) << "expected field " << PrettyField(field) << " to be static"; return NULL; - } else { - return field; } + return field; } mirror::ArtField* MethodVerifier::GetInstanceField(const RegType& obj_type, int field_idx) { @@ -3478,10 +3494,9 @@ mirror::ArtField* MethodVerifier::GetInstanceField(const RegType& obj_type, int if (klass_type.IsUnresolvedTypes()) { return NULL; // Can't resolve Class so no more to do here } - mirror::ArtField* field = Runtime::Current()->GetClassLinker()->ResolveFieldJLS(*dex_file_, - field_idx, - dex_cache_, - class_loader_); + ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); + mirror::ArtField* field = class_linker->ResolveFieldJLS(*dex_file_, field_idx, *dex_cache_, + *class_loader_); if (field == NULL) { VLOG(verifier) << "Unable to resolve instance field " << field_idx << " (" << dex_file_->GetFieldName(field_id) << ") in " @@ -3550,8 +3565,7 @@ void MethodVerifier::VerifyISGet(const Instruction* inst, const RegType& insn_ty if (field_type == nullptr) { const DexFile::FieldId& field_id = dex_file_->GetFieldId(field_idx); const char* descriptor = dex_file_->GetFieldTypeDescriptor(field_id); - mirror::ClassLoader* loader = class_loader_; - field_type = ®_types_.FromDescriptor(loader, descriptor, false); + field_type = ®_types_.FromDescriptor(class_loader_->get(), descriptor, false); } const uint32_t vregA = (is_static) ? inst->VRegA_21c() : inst->VRegA_22c(); if (is_primitive) { @@ -3613,8 +3627,7 @@ void MethodVerifier::VerifyISPut(const Instruction* inst, const RegType& insn_ty if (field_type == nullptr) { const DexFile::FieldId& field_id = dex_file_->GetFieldId(field_idx); const char* descriptor = dex_file_->GetFieldTypeDescriptor(field_id); - mirror::ClassLoader* loader = class_loader_; - field_type = ®_types_.FromDescriptor(loader, descriptor, false); + field_type = ®_types_.FromDescriptor(class_loader_->get(), descriptor, false); } const uint32_t vregA = (is_static) ? inst->VRegA_21c() : inst->VRegA_22c(); if (is_primitive) { @@ -3671,11 +3684,13 @@ mirror::ArtField* MethodVerifier::GetQuickFieldAccess(const Instruction* inst, // We need to resolve the class from its descriptor. const std::string& descriptor(object_type.GetDescriptor()); ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); - object_class = class_linker->FindClass(descriptor.c_str(), class_loader_); + Thread* self = Thread::Current(); + object_class = class_linker->FindClass(descriptor.c_str(), *class_loader_); if (object_class == NULL) { - Thread::Current()->ClearException(); + self->ClearException(); // Look for a system class - object_class = class_linker->FindClass(descriptor.c_str(), NULL); + SirtRef null_class_loader(self, nullptr); + object_class = class_linker->FindClass(descriptor.c_str(), null_class_loader); } } if (object_class == NULL) { @@ -3881,8 +3896,8 @@ const RegType& MethodVerifier::GetMethodReturnType() { MethodHelper mh(mirror_method_); mirror::Class* return_type_class = mh.GetReturnType(); if (return_type_class != nullptr) { - return_type_ =®_types_.FromClass(mh.GetReturnTypeDescriptor(), return_type_class, - return_type_class->CannotBeAssignedFromOtherTypes()); + return_type_ = ®_types_.FromClass(mh.GetReturnTypeDescriptor(), return_type_class, + return_type_class->CannotBeAssignedFromOtherTypes()); } else { Thread* self = Thread::Current(); DCHECK(self->IsExceptionPending()); @@ -3894,7 +3909,7 @@ const RegType& MethodVerifier::GetMethodReturnType() { const DexFile::ProtoId& proto_id = dex_file_->GetMethodPrototype(method_id); uint16_t return_type_idx = proto_id.return_type_idx_; const char* descriptor = dex_file_->GetTypeDescriptor(dex_file_->GetTypeId(return_type_idx)); - return_type_ = ®_types_.FromDescriptor(class_loader_, descriptor, false); + return_type_ = ®_types_.FromDescriptor(class_loader_->get(), descriptor, false); } } return *return_type_; @@ -3910,7 +3925,7 @@ const RegType& MethodVerifier::GetDeclaringClass() { declaring_class_ = ®_types_.FromClass(descriptor, klass, klass->CannotBeAssignedFromOtherTypes()); } else { - declaring_class_ = ®_types_.FromDescriptor(class_loader_, descriptor, false); + declaring_class_ = ®_types_.FromDescriptor(class_loader_->get(), descriptor, false); } } return *declaring_class_; @@ -3969,7 +3984,8 @@ MethodVerifier::MethodSafeCastSet* MethodVerifier::GenerateSafeCastSet() { // String[] in which case the stores need to be of Strings. if (array_type.IsPreciseReference()) { const RegType& value_type(line->GetRegisterType(inst->VRegA_23x())); - const RegType& component_type(reg_types_.GetComponentType(array_type, class_loader_)); + const RegType& component_type(reg_types_.GetComponentType(array_type, + class_loader_->get())); is_safe_cast = component_type.IsStrictlyAssignableFrom(value_type); } } @@ -4026,8 +4042,8 @@ MethodVerifier::PcToConcreteMethodMap* MethodVerifier::GenerateDevirtMap() { // We can't devirtualize abstract classes except on arrays of abstract classes. continue; } - mirror::ArtMethod* abstract_method = - dex_cache_->GetResolvedMethod(is_range ? inst->VRegB_3rc() : inst->VRegB_35c()); + mirror::ArtMethod* abstract_method = (*dex_cache_)->GetResolvedMethod( + is_range ? inst->VRegB_3rc() : inst->VRegB_35c()); if (abstract_method == NULL) { // If the method is not found in the cache this means that it was never found // by ResolveMethodAndCheckAccess() called when verifying invoke_*. diff --git a/runtime/verifier/method_verifier.h b/runtime/verifier/method_verifier.h index 57fde1d5d58..8a663f9f0e4 100644 --- a/runtime/verifier/method_verifier.h +++ b/runtime/verifier/method_verifier.h @@ -33,6 +33,7 @@ #include "reg_type_cache-inl.h" #include "register_line.h" #include "safe_map.h" +#include "sirt_ref.h" #include "UniquePtr.h" namespace art { @@ -142,14 +143,15 @@ class MethodVerifier { static FailureKind VerifyClass(const mirror::Class* klass, bool allow_soft_failures, std::string* error) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - static FailureKind VerifyClass(const DexFile* dex_file, mirror::DexCache* dex_cache, - mirror::ClassLoader* class_loader, + static FailureKind VerifyClass(const DexFile* dex_file, SirtRef& dex_cache, + SirtRef& class_loader, const DexFile::ClassDef* class_def, bool allow_soft_failures, std::string* error) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); static void VerifyMethodAndDump(std::ostream& os, uint32_t method_idx, const DexFile* dex_file, - mirror::DexCache* dex_cache, mirror::ClassLoader* class_loader, + SirtRef& dex_cache, + SirtRef& class_loader, const DexFile::ClassDef* class_def, const DexFile::CodeItem* code_item, mirror::ArtMethod* method, uint32_t method_access_flags) @@ -217,16 +219,13 @@ class MethodVerifier { return can_load_classes_; } - MethodVerifier(const DexFile* dex_file, mirror::DexCache* dex_cache, - mirror::ClassLoader* class_loader, const DexFile::ClassDef* class_def, - const DexFile::CodeItem* code_item, - uint32_t method_idx, mirror::ArtMethod* method, + MethodVerifier(const DexFile* dex_file, SirtRef* dex_cache, + SirtRef* class_loader, const DexFile::ClassDef* class_def, + const DexFile::CodeItem* code_item, uint32_t method_idx, mirror::ArtMethod* method, uint32_t access_flags, bool can_load_classes, bool allow_soft_failures) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - ~MethodVerifier() { - STLDeleteElements(&failure_messages_); - } + ~MethodVerifier(); // Run verification on the method. Returns true if verification completes and false if the input // has an irrecoverable corruption. @@ -257,8 +256,8 @@ class MethodVerifier { * for code flow problems. */ static FailureKind VerifyMethod(uint32_t method_idx, const DexFile* dex_file, - mirror::DexCache* dex_cache, - mirror::ClassLoader* class_loader, + SirtRef& dex_cache, + SirtRef& class_loader, const DexFile::ClassDef* class_def_idx, const DexFile::CodeItem* code_item, mirror::ArtMethod* method, uint32_t method_access_flags, @@ -685,9 +684,9 @@ class MethodVerifier { const RegType* return_type_; // Lazily computed return type of the method. const DexFile* const dex_file_; // The dex file containing the method. // The dex_cache for the declaring class of the method. - mirror::DexCache* dex_cache_ GUARDED_BY(Locks::mutator_lock_); + SirtRef* dex_cache_ GUARDED_BY(Locks::mutator_lock_); // The class loader for the declaring class of the method. - mirror::ClassLoader* class_loader_ GUARDED_BY(Locks::mutator_lock_); + SirtRef* class_loader_ GUARDED_BY(Locks::mutator_lock_); const DexFile::ClassDef* const class_def_; // The class def of the declaring class of the method. const DexFile::CodeItem* const code_item_; // The code item containing the code for the method. const RegType* declaring_class_; // Lazily computed reg type of the method's declaring class. diff --git a/runtime/verifier/reg_type.cc b/runtime/verifier/reg_type.cc index 50d1583bbba..d82e75de07e 100644 --- a/runtime/verifier/reg_type.cc +++ b/runtime/verifier/reg_type.cc @@ -928,7 +928,8 @@ mirror::Class* RegType::ClassJoin(mirror::Class* s, mirror::Class* t) { } mirror::Class* common_elem = ClassJoin(s_ct, t_ct); ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); - mirror::ClassLoader* class_loader = s->GetClassLoader(); + Thread* self = Thread::Current(); + SirtRef class_loader(self, s->GetClassLoader()); std::string descriptor("["); descriptor += ClassHelper(common_elem).GetDescriptor(); mirror::Class* array_class = class_linker->FindClass(descriptor.c_str(), class_loader); diff --git a/runtime/verifier/reg_type_cache.cc b/runtime/verifier/reg_type_cache.cc index 446dd0080c5..a62e835b1cd 100644 --- a/runtime/verifier/reg_type_cache.cc +++ b/runtime/verifier/reg_type_cache.cc @@ -140,9 +140,10 @@ mirror::Class* RegTypeCache::ResolveClass(const char* descriptor, mirror::ClassL // Class was not found, must create new type. // Try resolving class ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); + SirtRef class_loader(Thread::Current(), loader); mirror::Class* klass = NULL; if (can_load_classes_) { - klass = class_linker->FindClass(descriptor, loader); + klass = class_linker->FindClass(descriptor, class_loader); } else { klass = class_linker->LookupClass(descriptor, loader); if (klass != NULL && !klass->IsLoaded()) { From 08f20c63e2bd0c41d53a4c3ddaa34fd637f5405d Mon Sep 17 00:00:00 2001 From: Jeff Hao Date: Tue, 5 Nov 2013 12:00:29 -0800 Subject: [PATCH 0159/2402] Change monitor checks to warnings in verifier. Allows Whatsapp to install properly. Bug: 11335470 Bug: 11476436 Public bug: https://code.google.com/p/android/issues/detail?id=61792 Change-Id: I2d9b9deaa0126d3dbd6bbfb2babfc15acceed01b (cherry picked from commit 2bd5c233bb95877eab87c897bf802ae67a35178b) --- runtime/verifier/register_line.cc | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/runtime/verifier/register_line.cc b/runtime/verifier/register_line.cc index 1a41657264b..31b0113d15f 100644 --- a/runtime/verifier/register_line.cc +++ b/runtime/verifier/register_line.cc @@ -467,8 +467,8 @@ bool RegisterLine::MergeRegisters(const RegisterLine* incoming_line) { } } if (monitors_.size() != incoming_line->monitors_.size()) { - verifier_->Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "mismatched stack depths (depth=" - << MonitorStackDepth() << ", incoming depth=" << incoming_line->MonitorStackDepth() << ")"; + LOG(WARNING) << "mismatched stack depths (depth=" << MonitorStackDepth() + << ", incoming depth=" << incoming_line->MonitorStackDepth() << ")"; } else if (reg_to_lock_depths_ != incoming_line->reg_to_lock_depths_) { for (uint32_t idx = 0; idx < num_regs_; idx++) { size_t depths = reg_to_lock_depths_.count(idx); @@ -477,8 +477,8 @@ bool RegisterLine::MergeRegisters(const RegisterLine* incoming_line) { if (depths == 0 || incoming_depths == 0) { reg_to_lock_depths_.erase(idx); } else { - verifier_->Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "mismatched stack depths for register v" << idx - << ": " << depths << " != " << incoming_depths; + LOG(WARNING) << "mismatched stack depths for register v" << idx + << ": " << depths << " != " << incoming_depths; break; } } From 3cf59d567072562e120b9524f47ad2bd373f8333 Mon Sep 17 00:00:00 2001 From: Brian Carlstrom Date: Sun, 10 Nov 2013 21:04:10 -0800 Subject: [PATCH 0160/2402] Revert "resolved conflicts for merge of ad93c622 to master" This reverts commit 4f6a77d529ae295630e22d4636c2acc2ba2fbb6d, reversing changes made to bcdbbfebc8f32566d4cb3f66405e89cdb7351992. --- dex2oat/dex2oat.cc | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc index 9631e2df67d..5b0653e00f5 100644 --- a/dex2oat/dex2oat.cc +++ b/dex2oat/dex2oat.cc @@ -419,9 +419,10 @@ static bool ParseInt(const char* in, int* out) { return true; } -static void OpenDexFiles(const std::vector& dex_filenames, - const std::vector& dex_locations, - std::vector& dex_files) { +static size_t OpenDexFiles(const std::vector& dex_filenames, + const std::vector& dex_locations, + std::vector& dex_files) { + size_t failure_count = 0; for (size_t i = 0; i < dex_filenames.size(); i++) { const char* dex_filename = dex_filenames[i]; const char* dex_location = dex_locations[i]; @@ -429,10 +430,12 @@ static void OpenDexFiles(const std::vector& dex_filenames, const DexFile* dex_file = DexFile::Open(dex_filename, dex_location, &error_msg); if (dex_file == NULL) { LOG(WARNING) << "Failed to open .dex from file '" << dex_filename << "': " << error_msg; + ++failure_count; } else { dex_files.push_back(dex_file); } } + return failure_count; } // The primary goal of the watchdog is to prevent stuck build servers @@ -892,7 +895,11 @@ static int dex2oat(int argc, char** argv) { options.push_back(std::make_pair("compiler", reinterpret_cast(NULL))); std::vector boot_class_path; if (boot_image_option.empty()) { - OpenDexFiles(dex_filenames, dex_locations, boot_class_path); + size_t failure_count = OpenDexFiles(dex_filenames, dex_locations, boot_class_path); + if (failure_count > 0) { + LOG(ERROR) << "Failed to open some dex files: " << failure_count; + return EXIT_FAILURE; + } options.push_back(std::make_pair("bootclasspath", &boot_class_path)); } else { options.push_back(std::make_pair(boot_image_option.c_str(), reinterpret_cast(NULL))); @@ -964,7 +971,11 @@ static int dex2oat(int argc, char** argv) { } dex_files.push_back(dex_file); } else { - OpenDexFiles(dex_filenames, dex_locations, dex_files); + size_t failure_count = OpenDexFiles(dex_filenames, dex_locations, dex_files); + if (failure_count > 0) { + LOG(ERROR) << "Failed to open some dex files: " << failure_count; + return EXIT_FAILURE; + } } // Ensure opened dex files are writable for dex-to-dex transformations. From d5aba599561d8d14bb8c19fd633bfcd05af3c264 Mon Sep 17 00:00:00 2001 From: Brian Carlstrom Date: Tue, 12 Nov 2013 01:52:44 -0800 Subject: [PATCH 0161/2402] Fix CTS failures caused by ignoring malformed dex files in dex2oat Bug: 11621003 Bug: 11599365 Change-Id: If1f62dff59346edcd60e4c4b5df58137609f7f19 --- dex2oat/dex2oat.cc | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc index 5b0653e00f5..37819219271 100644 --- a/dex2oat/dex2oat.cc +++ b/dex2oat/dex2oat.cc @@ -427,6 +427,10 @@ static size_t OpenDexFiles(const std::vector& dex_filenames, const char* dex_filename = dex_filenames[i]; const char* dex_location = dex_locations[i]; std::string error_msg; + if (!OS::FileExists(dex_filename)) { + LOG(WARNING) << "Skipping non-existent dex file '" << dex_filename << "'"; + continue; + } const DexFile* dex_file = DexFile::Open(dex_filename, dex_location, &error_msg); if (dex_file == NULL) { LOG(WARNING) << "Failed to open .dex from file '" << dex_filename << "': " << error_msg; From 25724efbf84cce5734d1869e636cf59bc3f95941 Mon Sep 17 00:00:00 2001 From: Vladimir Marko Date: Tue, 12 Nov 2013 15:09:20 +0000 Subject: [PATCH 0162/2402] Fix CompilationUnit's UniquePtr usage. CompilationUnit defined in compiler_ir.h uses UniquePtr with forward-declared MIRGraph, so the ctor/dtor must be defined in a compilation unit that actually includes MIRGraph's definition. Otherwise the build would depend on the order of includes. Change-Id: I53096c5f1c975843bad3c375d4ce72a9c0656264 --- compiler/dex/compiler_ir.h | 32 +++----------------------------- compiler/dex/frontend.cc | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 35 insertions(+), 29 deletions(-) diff --git a/compiler/dex/compiler_ir.h b/compiler/dex/compiler_ir.h index fd46975b9a8..546ce4aee78 100644 --- a/compiler/dex/compiler_ir.h +++ b/compiler/dex/compiler_ir.h @@ -20,7 +20,6 @@ #include #include #include "arena_allocator.h" -#include "backend.h" #include "compiler_enums.h" #include "dex/quick/mir_to_lir.h" #include "dex_instruction.h" @@ -39,39 +38,14 @@ class LlvmCompilationUnit; } // namespace llvm struct ArenaMemBlock; +class Backend; struct Memstats; class MIRGraph; class Mir2Lir; struct CompilationUnit { - explicit CompilationUnit(ArenaPool* pool) - : compiler_driver(NULL), - class_linker(NULL), - dex_file(NULL), - class_loader(NULL), - class_def_idx(0), - method_idx(0), - code_item(NULL), - access_flags(0), - invoke_type(kDirect), - shorty(NULL), - disable_opt(0), - enable_debug(0), - verbose(false), - compiler_backend(kNoBackend), - instruction_set(kNone), - num_dalvik_registers(0), - insns(NULL), - num_ins(0), - num_outs(0), - num_regs(0), - num_compiler_temps(0), - compiler_flip_match(false), - arena(pool), - mir_graph(NULL), - cg(NULL), - timings("QuickCompiler", true, false) { - } + explicit CompilationUnit(ArenaPool* pool); + ~CompilationUnit(); void StartTimingSplit(const char* label); void NewTimingSplit(const char* label); diff --git a/compiler/dex/frontend.cc b/compiler/dex/frontend.cc index 2f8521f7880..b8cd67e3e71 100644 --- a/compiler/dex/frontend.cc +++ b/compiler/dex/frontend.cc @@ -108,6 +108,38 @@ static uint32_t kCompilerDebugFlags = 0 | // Enable debug/testing modes // (1 << kDebugTimings) | 0; +CompilationUnit::CompilationUnit(ArenaPool* pool) + : compiler_driver(NULL), + class_linker(NULL), + dex_file(NULL), + class_loader(NULL), + class_def_idx(0), + method_idx(0), + code_item(NULL), + access_flags(0), + invoke_type(kDirect), + shorty(NULL), + disable_opt(0), + enable_debug(0), + verbose(false), + compiler_backend(kNoBackend), + instruction_set(kNone), + num_dalvik_registers(0), + insns(NULL), + num_ins(0), + num_outs(0), + num_regs(0), + num_compiler_temps(0), + compiler_flip_match(false), + arena(pool), + mir_graph(NULL), + cg(NULL), + timings("QuickCompiler", true, false) { +} + +CompilationUnit::~CompilationUnit() { +} + // TODO: Add a cumulative version of logging, and combine with dex2oat --dump-timing void CompilationUnit::StartTimingSplit(const char* label) { if (enable_debug & (1 << kDebugTimings)) { From 6e9aeb6633e42bc7d2265f73de28e521e3fc9c21 Mon Sep 17 00:00:00 2001 From: Christopher Ferris Date: Tue, 5 Nov 2013 16:23:10 -0800 Subject: [PATCH 0163/2402] Update Backtrace::Create to use new define. Change-Id: Ib345ef796c99d12fb3a4118f28edec7cbffa76d2 --- runtime/utils.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/utils.cc b/runtime/utils.cc index e039581b05b..233990383cd 100644 --- a/runtime/utils.cc +++ b/runtime/utils.cc @@ -1043,7 +1043,7 @@ static const char* CleanMapName(const char* map_name) { } void DumpNativeStack(std::ostream& os, pid_t tid, const char* prefix, bool include_count) { - UniquePtr backtrace(Backtrace::Create(-1, tid)); + UniquePtr backtrace(Backtrace::Create(BACKTRACE_CURRENT_PROCESS, tid)); if (!backtrace->Unwind(0)) { os << prefix << "(backtrace::Unwind failed for thread " << tid << ")\n"; return; From 46bc778f1feed02b20d25e3d03470c93ca2c0506 Mon Sep 17 00:00:00 2001 From: Mathieu Chartier Date: Tue, 12 Nov 2013 17:03:02 -0800 Subject: [PATCH 0164/2402] Fix portable + mips build. Change-Id: Ia200e582b04c84973281e12331777351feb8a401 --- compiler/elf_writer_mclinker.cc | 5 +++-- runtime/base/mutex.h | 5 ----- runtime/globals.h | 8 +++++++- runtime/monitor.cc | 5 ++++- runtime/monitor.h | 3 +++ 5 files changed, 17 insertions(+), 9 deletions(-) diff --git a/compiler/elf_writer_mclinker.cc b/compiler/elf_writer_mclinker.cc index 8e19ef61950..f3fef232471 100644 --- a/compiler/elf_writer_mclinker.cc +++ b/compiler/elf_writer_mclinker.cc @@ -358,10 +358,11 @@ void ElfWriterMclinker::FixupOatMethodOffsets(const std::vector& mirror::ArtMethod* method = NULL; if (compiler_driver_->IsImage()) { ClassLinker* linker = Runtime::Current()->GetClassLinker(); - mirror::DexCache* dex_cache = linker->FindDexCache(dex_file); // Unchecked as we hold mutator_lock_ on entry. ScopedObjectAccessUnchecked soa(Thread::Current()); - method = linker->ResolveMethod(dex_file, method_idx, dex_cache, NULL, NULL, invoke_type); + SirtRef dex_cache(soa.Self(), linker->FindDexCache(dex_file)); + SirtRef class_loader(soa.Self(), nullptr); + method = linker->ResolveMethod(dex_file, method_idx, dex_cache, class_loader, NULL, invoke_type); CHECK(method != NULL); } const CompiledMethod* compiled_method = diff --git a/runtime/base/mutex.h b/runtime/base/mutex.h index a8750177c59..feb8a6c6c1b 100644 --- a/runtime/base/mutex.h +++ b/runtime/base/mutex.h @@ -329,11 +329,6 @@ class ConditionVariable { // TODO: remove this. void WaitHoldingLocks(Thread* self) NO_THREAD_SAFETY_ANALYSIS; - // Return the number of people that are waiting on this condition. - int32_t GetNumWaiters() const NO_THREAD_SAFETY_ANALYSIS { - return num_waiters_; - } - private: const char* const name_; // The Mutex being used by waiters. It is an error to mix condition variables between different diff --git a/runtime/globals.h b/runtime/globals.h index 10426b0fe2a..1a25dfa4dda 100644 --- a/runtime/globals.h +++ b/runtime/globals.h @@ -73,8 +73,14 @@ const bool kIsTargetBuild = true; const bool kIsTargetBuild = false; #endif +#if defined(ART_USE_PORTABLE_COMPILER) +constexpr bool kUsePortableCompiler = true; +#else +constexpr bool kUsePortableCompiler = false; +#endif + // Garbage collector constants. -static constexpr bool kMovingCollector = false; +static constexpr bool kMovingCollector = false && !kUsePortableCompiler; // True if we allow moving classes. static constexpr bool kMovingClasses = false; // True if we allow moving fields. diff --git a/runtime/monitor.cc b/runtime/monitor.cc index 7fada9ef81a..af93a562647 100644 --- a/runtime/monitor.cc +++ b/runtime/monitor.cc @@ -82,6 +82,7 @@ void Monitor::Init(uint32_t lock_profiling_threshold, bool (*is_sensitive_thread Monitor::Monitor(Thread* owner, mirror::Object* obj, int32_t hash_code) : monitor_lock_("a monitor lock", kMonitorLock), monitor_contenders_("monitor contenders", monitor_lock_), + num_waiters_(0), owner_(owner), lock_count_(0), obj_(obj), @@ -225,7 +226,9 @@ void Monitor::Lock(Thread* self) { ScopedThreadStateChange tsc(self, kBlocked); // Change to blocked and give up mutator_lock_. MutexLock mu2(self, monitor_lock_); // Reacquire monitor_lock_ without mutator_lock_ for Wait. if (owner_ != NULL) { // Did the owner_ give the lock up? + ++num_waiters_; monitor_contenders_.Wait(self); // Still contended so wait. + --num_waiters_; // Woken from contention. if (log_contention) { uint64_t wait_ms = MilliTime() - wait_start_ms; @@ -581,7 +584,7 @@ bool Monitor::Deflate(Thread* self, mirror::Object* obj) { return false; } // Can't deflate if we have anybody waiting on the CV. - if (monitor->monitor_contenders_.GetNumWaiters() > 0) { + if (monitor->num_waiters_ > 0) { return false; } // Deflate to a thin lock. diff --git a/runtime/monitor.h b/runtime/monitor.h index d7de8a50cd4..bfd854572ae 100644 --- a/runtime/monitor.h +++ b/runtime/monitor.h @@ -174,6 +174,9 @@ class Monitor { Mutex monitor_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER; ConditionVariable monitor_contenders_ GUARDED_BY(monitor_lock_); + // Number of people waiting on the condition. + size_t num_waiters_ GUARDED_BY(monitor_lock_); + // Which thread currently owns the lock? Thread* volatile owner_ GUARDED_BY(monitor_lock_); From 34e82934546bd470283346907bd7b74990797c56 Mon Sep 17 00:00:00 2001 From: Mathieu Chartier Date: Tue, 12 Nov 2013 18:22:47 -0800 Subject: [PATCH 0165/2402] Fix remaining mips build issues. Missed a few things in mutex.cc. Change-Id: I1c3acfc5faa2511490170199c03ab74c1f23022a --- runtime/base/mutex.cc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/runtime/base/mutex.cc b/runtime/base/mutex.cc index 1c7d7449458..ec79c558d29 100644 --- a/runtime/base/mutex.cc +++ b/runtime/base/mutex.cc @@ -282,7 +282,7 @@ Mutex::~Mutex() { // TODO: should we just not log at all if shutting down? this could be the logging mutex! MutexLock mu(Thread::Current(), *Locks::runtime_shutdown_lock_); Runtime* runtime = Runtime::Current(); - bool shutting_down = (runtime == NULL) || runtime->IsShuttingDown(); + bool shutting_down = (runtime == NULL) || runtime->IsShuttingDownLocked(); PLOG(shutting_down ? WARNING : FATAL) << "pthread_mutex_destroy failed for " << name_; } #endif @@ -453,7 +453,7 @@ ReaderWriterMutex::~ReaderWriterMutex() { // TODO: should we just not log at all if shutting down? this could be the logging mutex! MutexLock mu(Thread::Current(), *Locks::runtime_shutdown_lock_); Runtime* runtime = Runtime::Current(); - bool shutting_down = runtime == NULL || runtime->IsShuttingDown(); + bool shutting_down = runtime == NULL || runtime->IsShuttingDownLocked(); PLOG(shutting_down ? WARNING : FATAL) << "pthread_rwlock_destroy failed for " << name_; } #endif @@ -653,7 +653,7 @@ ConditionVariable::~ConditionVariable() { errno = rc; MutexLock mu(Thread::Current(), *Locks::runtime_shutdown_lock_); Runtime* runtime = Runtime::Current(); - bool shutting_down = (runtime == NULL) || runtime->IsShuttingDown(); + bool shutting_down = (runtime == NULL) || runtime->IsShuttingDownLocked(); PLOG(shutting_down ? WARNING : FATAL) << "pthread_cond_destroy failed for " << name_; } #endif From 3b6f0fae76fddf81930a263a075dc87b6039b7fc Mon Sep 17 00:00:00 2001 From: Mathieu Chartier Date: Wed, 13 Nov 2013 13:26:07 -0800 Subject: [PATCH 0166/2402] Add more logging when mmap fails. We now print the reason that the mmap failed. Change-Id: Ie515e4bba117c9ea1f4297abb826d32172bea962 --- runtime/mem_map.cc | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/runtime/mem_map.cc b/runtime/mem_map.cc index 3afb6065b6d..39e838f4571 100644 --- a/runtime/mem_map.cc +++ b/runtime/mem_map.cc @@ -133,12 +133,13 @@ MemMap* MemMap::MapFileAtAddress(byte* addr, size_t byte_count, int prot, int fl fd, page_aligned_offset)); if (actual == MAP_FAILED) { + std::string strerr(strerror(errno)); std::string maps; ReadFileToString("/proc/self/maps", &maps); - *error_msg = StringPrintf("mmap(%p, %zd, %x, %x, %d, %lld) of file '%s' failed\n%s", + *error_msg = StringPrintf("mmap(%p, %zd, %x, %x, %d, %lld) of file '%s' failed: %s\n%s", page_aligned_addr, page_aligned_byte_count, prot, flags, fd, - static_cast(page_aligned_offset), - filename, maps.c_str()); + static_cast(page_aligned_offset), filename, strerr.c_str(), + maps.c_str()); return NULL; } return new MemMap("file", actual + page_offset, byte_count, actual, page_aligned_byte_count, From d912e5c36f952db8ff027d67979724fe0e56b35a Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Wed, 13 Nov 2013 12:40:11 -0800 Subject: [PATCH 0167/2402] Allow host valgrind gtests to be run individually. Change-Id: I80976600034046cb42e8565bfd38f75811d7a08e --- Android.mk | 7 +------ build/Android.gtest.mk | 7 +++++++ 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/Android.mk b/Android.mk index 8024a3d3575..bf2eb9a33a6 100644 --- a/Android.mk +++ b/Android.mk @@ -146,14 +146,9 @@ test-art-host-dependencies: $(ART_HOST_TEST_DEPENDENCIES) $(HOST_OUT_SHARED_LIBR test-art-host-gtest: $(ART_HOST_TEST_TARGETS) @echo test-art-host-gtest PASSED -define run-host-gtests-with - $(foreach file,$(sort $(ART_HOST_TEST_EXECUTABLES)),$(1) $(file) &&) true -endef - # "mm valgrind-test-art-host-gtest" to build and run the host gtests under valgrind. .PHONY: valgrind-test-art-host-gtest -valgrind-test-art-host-gtest: test-art-host-dependencies - $(call run-host-gtests-with,valgrind --leak-check=full) +valgrind-test-art-host-gtest: $(ART_HOST_VALGRIND_TEST_TARGETS) @echo valgrind-test-art-host-gtest PASSED .PHONY: test-art-host-oat-default diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk index 655c7dd01f0..8165ec7ffca 100644 --- a/build/Android.gtest.mk +++ b/build/Android.gtest.mk @@ -170,6 +170,13 @@ $$(art_gtest_target): $$(art_gtest_exe) test-art-host-dependencies @echo $$@ PASSED ART_HOST_TEST_TARGETS += $$(art_gtest_target) + +.PHONY: valgrind-$$(art_gtest_target) +valgrind-$$(art_gtest_target): $$(art_gtest_exe) test-art-host-dependencies + valgrind --leak-check=full $$< + @echo $$@ PASSED + +ART_HOST_VALGRIND_TEST_TARGETS += valgrind-$$(art_gtest_target) endif endef From bcd5e9daecad39f0dab3246808b4835caec29ea6 Mon Sep 17 00:00:00 2001 From: Mathieu Chartier Date: Wed, 13 Nov 2013 14:33:28 -0800 Subject: [PATCH 0168/2402] Manually manage thread pool stacks. We now allocate the thread pool worker stack using a MemMap. This enables us to name the maps so that we get more descriptive output for debugging leaks. Appears to fix the mips build 5/5 successful clean-oat and builds. This is probably since glibc caches up to 40 MB of thread stacks before releasing them. Change-Id: I1df2de50cb95838aa0d272a09807021404ba410c --- compiler/driver/compiler_driver.cc | 4 ++-- runtime/barrier_test.cc | 4 ++-- runtime/gc/heap.cc | 2 +- runtime/thread_pool.cc | 20 ++++++++++++-------- runtime/thread_pool.h | 11 +++++++---- runtime/thread_pool_test.cc | 6 +++--- 6 files changed, 27 insertions(+), 20 deletions(-) diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc index 4af492bf6e8..d74383e33b6 100644 --- a/compiler/driver/compiler_driver.cc +++ b/compiler/driver/compiler_driver.cc @@ -505,7 +505,7 @@ void CompilerDriver::CompileAll(jobject class_loader, const std::vector& dex_files, base::TimingLogger& timings) { DCHECK(!Runtime::Current()->IsStarted()); - UniquePtr thread_pool(new ThreadPool(thread_count_ - 1)); + UniquePtr thread_pool(new ThreadPool("Compiler driver thread pool", thread_count_ - 1)); PreCompile(class_loader, dex_files, *thread_pool.get(), timings); Compile(class_loader, dex_files, *thread_pool.get(), timings); if (dump_stats_) { @@ -568,7 +568,7 @@ void CompilerDriver::CompileOne(const mirror::ArtMethod* method, base::TimingLog std::vector dex_files; dex_files.push_back(dex_file); - UniquePtr thread_pool(new ThreadPool(0U)); + UniquePtr thread_pool(new ThreadPool("Compiler driver thread pool", 0U)); PreCompile(jclass_loader, dex_files, *thread_pool.get(), timings); uint32_t method_idx = method->GetDexMethodIndex(); diff --git a/runtime/barrier_test.cc b/runtime/barrier_test.cc index 298ae569fbb..91fc143dbea 100644 --- a/runtime/barrier_test.cc +++ b/runtime/barrier_test.cc @@ -66,7 +66,7 @@ int32_t BarrierTest::num_threads = 4; // Check that barrier wait and barrier increment work. TEST_F(BarrierTest, CheckWait) { Thread* self = Thread::Current(); - ThreadPool thread_pool(num_threads); + ThreadPool thread_pool("Barrier test thread pool", num_threads); Barrier barrier(0); AtomicInteger count1(0); AtomicInteger count2(0); @@ -121,7 +121,7 @@ class CheckPassTask : public Task { // Check that barrier pass through works. TEST_F(BarrierTest, CheckPass) { Thread* self = Thread::Current(); - ThreadPool thread_pool(num_threads); + ThreadPool thread_pool("Barrier test thread pool", num_threads); Barrier barrier(0); AtomicInteger count(0); const int32_t num_tasks = num_threads * 4; diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc index 69ca6202f94..70df3d3133d 100644 --- a/runtime/gc/heap.cc +++ b/runtime/gc/heap.cc @@ -309,7 +309,7 @@ void Heap::DecrementDisableGC(Thread* self) { void Heap::CreateThreadPool() { const size_t num_threads = std::max(parallel_gc_threads_, conc_gc_threads_); if (num_threads != 0) { - thread_pool_.reset(new ThreadPool(num_threads)); + thread_pool_.reset(new ThreadPool("Heap thread pool", num_threads)); } } diff --git a/runtime/thread_pool.cc b/runtime/thread_pool.cc index bb6c47538ec..aca0561a775 100644 --- a/runtime/thread_pool.cc +++ b/runtime/thread_pool.cc @@ -28,12 +28,15 @@ static constexpr bool kMeasureWaitTime = false; ThreadPoolWorker::ThreadPoolWorker(ThreadPool* thread_pool, const std::string& name, size_t stack_size) : thread_pool_(thread_pool), - name_(name), - stack_size_(stack_size) { + name_(name) { + std::string error_msg; + stack_.reset(MemMap::MapAnonymous(name.c_str(), nullptr, stack_size, PROT_READ | PROT_WRITE, + &error_msg)); + CHECK(stack_.get() != nullptr) << error_msg; const char* reason = "new thread pool worker thread"; pthread_attr_t attr; CHECK_PTHREAD_CALL(pthread_attr_init, (&attr), reason); - CHECK_PTHREAD_CALL(pthread_attr_setstacksize, (&attr, stack_size), reason); + CHECK_PTHREAD_CALL(pthread_attr_setstack, (&attr, stack_->Begin(), stack_->Size()), reason); CHECK_PTHREAD_CALL(pthread_create, (&pthread_, &attr, &Callback, this), reason); CHECK_PTHREAD_CALL(pthread_attr_destroy, (&attr), reason); } @@ -71,8 +74,9 @@ void ThreadPool::AddTask(Thread* self, Task* task) { } } -ThreadPool::ThreadPool(size_t num_threads) - : task_queue_lock_("task queue lock"), +ThreadPool::ThreadPool(const char* name, size_t num_threads) + : name_(name), + task_queue_lock_("task queue lock"), task_queue_condition_("task queue condition", task_queue_lock_), completion_condition_("task completion condition", task_queue_lock_), started_(false), @@ -85,7 +89,7 @@ ThreadPool::ThreadPool(size_t num_threads) max_active_workers_(num_threads) { Thread* self = Thread::Current(); while (GetThreadCount() < num_threads) { - const std::string name = StringPrintf("Thread pool worker %zu", GetThreadCount()); + const std::string name = StringPrintf("%s worker thread %zu", name_.c_str(), GetThreadCount()); threads_.push_back(new ThreadPoolWorker(this, name, ThreadPoolWorker::kDefaultStackSize)); } // Wait for all of the threads to attach. @@ -270,8 +274,8 @@ void WorkStealingWorker::Run() { WorkStealingWorker::~WorkStealingWorker() {} -WorkStealingThreadPool::WorkStealingThreadPool(size_t num_threads) - : ThreadPool(0), +WorkStealingThreadPool::WorkStealingThreadPool(const char* name, size_t num_threads) + : ThreadPool(name, 0), work_steal_lock_("work stealing lock"), steal_index_(0) { while (GetThreadCount() < num_threads) { diff --git a/runtime/thread_pool.h b/runtime/thread_pool.h index b9a97a1427e..e8f9afe62dc 100644 --- a/runtime/thread_pool.h +++ b/runtime/thread_pool.h @@ -24,6 +24,7 @@ #include "base/mutex.h" #include "closure.h" #include "locks.h" +#include "mem_map.h" namespace art { @@ -40,7 +41,8 @@ class ThreadPoolWorker { static const size_t kDefaultStackSize = 1 * MB; size_t GetStackSize() const { - return stack_size_; + DCHECK(stack_.get() != nullptr); + return stack_->Size(); } virtual ~ThreadPoolWorker(); @@ -52,7 +54,7 @@ class ThreadPoolWorker { ThreadPool* const thread_pool_; const std::string name_; - const size_t stack_size_; + UniquePtr stack_; pthread_t pthread_; private: @@ -77,7 +79,7 @@ class ThreadPool { // after running it, it is the caller's responsibility. void AddTask(Thread* self, Task* task); - explicit ThreadPool(size_t num_threads); + explicit ThreadPool(const char* name, size_t num_threads); virtual ~ThreadPool(); // Wait for all tasks currently on queue to get completed. @@ -107,6 +109,7 @@ class ThreadPool { return shutting_down_; } + const std::string name_; Mutex task_queue_lock_; ConditionVariable task_queue_condition_ GUARDED_BY(task_queue_lock_); ConditionVariable completion_condition_ GUARDED_BY(task_queue_lock_); @@ -167,7 +170,7 @@ class WorkStealingWorker : public ThreadPoolWorker { class WorkStealingThreadPool : public ThreadPool { public: - explicit WorkStealingThreadPool(size_t num_threads); + explicit WorkStealingThreadPool(const char* name, size_t num_threads); virtual ~WorkStealingThreadPool(); private: diff --git a/runtime/thread_pool_test.cc b/runtime/thread_pool_test.cc index 9b789d2f4c8..1b2236152fc 100644 --- a/runtime/thread_pool_test.cc +++ b/runtime/thread_pool_test.cc @@ -59,7 +59,7 @@ int32_t ThreadPoolTest::num_threads = 4; // Check that the thread pool actually runs tasks that you assign it. TEST_F(ThreadPoolTest, CheckRun) { Thread* self = Thread::Current(); - ThreadPool thread_pool(num_threads); + ThreadPool thread_pool("Thread pool test thread pool", num_threads); AtomicInteger count(0); static const int32_t num_tasks = num_threads * 4; for (int32_t i = 0; i < num_tasks; ++i) { @@ -74,7 +74,7 @@ TEST_F(ThreadPoolTest, CheckRun) { TEST_F(ThreadPoolTest, StopStart) { Thread* self = Thread::Current(); - ThreadPool thread_pool(num_threads); + ThreadPool thread_pool("Thread pool test thread pool", num_threads); AtomicInteger count(0); static const int32_t num_tasks = num_threads * 4; for (int32_t i = 0; i < num_tasks; ++i) { @@ -129,7 +129,7 @@ class TreeTask : public Task { // Test that adding new tasks from within a task works. TEST_F(ThreadPoolTest, RecursiveTest) { Thread* self = Thread::Current(); - ThreadPool thread_pool(num_threads); + ThreadPool thread_pool("Thread pool test thread pool", num_threads); AtomicInteger count(0); static const int depth = 8; thread_pool.AddTask(self, new TreeTask(&thread_pool, &count, depth)); From 12aeccd09a87be7a0b995182151d778cfeb50e73 Mon Sep 17 00:00:00 2001 From: Mathieu Chartier Date: Wed, 13 Nov 2013 15:52:06 -0800 Subject: [PATCH 0169/2402] Fix image writer bitmap size calculation. Wasn't properly dividing by 8 inside of the image header creation. This resulted in a bitmap size which was larger than it should be inside of the image. Change-Id: I344d1f3c1794a7cff3c9e22afc7fdabedf74413c --- compiler/image_writer.cc | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/compiler/image_writer.cc b/compiler/image_writer.cc index c22f8d6c012..d2d7c0afd43 100644 --- a/compiler/image_writer.cc +++ b/compiler/image_writer.cc @@ -520,11 +520,13 @@ void ImageWriter::CalculateNewObjectOffsets(size_t oat_loaded_size, size_t oat_d // Return to write header at start of image with future location of image_roots. At this point, // image_end_ is the size of the image (excluding bitmaps). + const size_t heap_bytes_per_bitmap_byte = 8 * gc::accounting::SpaceBitmap::kAlignment; + const size_t bitmap_bytes = RoundUp(image_end_, heap_bytes_per_bitmap_byte) / + heap_bytes_per_bitmap_byte; ImageHeader image_header(reinterpret_cast(image_begin_), static_cast(image_end_), RoundUp(image_end_, kPageSize), - RoundUp(image_end_ / gc::accounting::SpaceBitmap::kAlignment, - sizeof(size_t)), + RoundUp(bitmap_bytes, kPageSize), reinterpret_cast(GetImageAddress(image_roots.get())), oat_file_->GetOatHeader().GetChecksum(), reinterpret_cast(oat_file_begin), From 86aed2a0b7972bf5a5f564d710e7b270589bbddc Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Wed, 13 Nov 2013 15:56:44 -0800 Subject: [PATCH 0170/2402] Fix testing all valgrind tests and make errors abort. Change-Id: I6185e26cf89fde6fc8642fd4f7c5285a6f33f835 --- build/Android.gtest.mk | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk index 8165ec7ffca..b07753c4fec 100644 --- a/build/Android.gtest.mk +++ b/build/Android.gtest.mk @@ -80,6 +80,7 @@ TEST_HOST_SRC_FILES := \ ART_HOST_TEST_EXECUTABLES := ART_TARGET_TEST_EXECUTABLES := ART_HOST_TEST_TARGETS := +ART_HOST_VALGRIND_TEST_TARGETS := ART_TARGET_TEST_TARGETS := ART_TEST_CFLAGS := @@ -173,7 +174,7 @@ ART_HOST_TEST_TARGETS += $$(art_gtest_target) .PHONY: valgrind-$$(art_gtest_target) valgrind-$$(art_gtest_target): $$(art_gtest_exe) test-art-host-dependencies - valgrind --leak-check=full $$< + valgrind --leak-check=full --error-exitcode=1 $$< @echo $$@ PASSED ART_HOST_VALGRIND_TEST_TARGETS += valgrind-$$(art_gtest_target) From 1e363f96a8e5386282340bb37972dcc823b42e2e Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Wed, 13 Nov 2013 15:58:24 -0800 Subject: [PATCH 0171/2402] Use a UniquePtr to clean up global logging std::string*s. This makes valgrind happier. Bug: 11670287 Change-Id: I957b94cdc177665e66b069e4d2b2b8d0a4b589c8 --- runtime/base/logging.cc | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/runtime/base/logging.cc b/runtime/base/logging.cc index 3d842a06459..3aabc8db8cb 100644 --- a/runtime/base/logging.cc +++ b/runtime/base/logging.cc @@ -19,6 +19,7 @@ #include "base/mutex.h" #include "runtime.h" #include "thread-inl.h" +#include "UniquePtr.h" #include "utils.h" namespace art { @@ -28,20 +29,21 @@ LogVerbosity gLogVerbosity; unsigned int gAborting = 0; static LogSeverity gMinimumLogSeverity = INFO; -static std::string* gCmdLine = NULL; -static std::string* gProgramInvocationName = NULL; -static std::string* gProgramInvocationShortName = NULL; +static UniquePtr gCmdLine; +static UniquePtr gProgramInvocationName; +static UniquePtr gProgramInvocationShortName; const char* GetCmdLine() { - return (gCmdLine != NULL) ? gCmdLine->c_str() : NULL; + return (gCmdLine.get() != nullptr) ? gCmdLine->c_str() : nullptr; } const char* ProgramInvocationName() { - return (gProgramInvocationName != NULL) ? gProgramInvocationName->c_str() : "art"; + return (gProgramInvocationName.get() != nullptr) ? gProgramInvocationName->c_str() : "art"; } const char* ProgramInvocationShortName() { - return (gProgramInvocationShortName != NULL) ? gProgramInvocationShortName->c_str() : "art"; + return (gProgramInvocationShortName.get() != nullptr) ? gProgramInvocationShortName->c_str() + : "art"; } // Configure logging based on ANDROID_LOG_TAGS environment variable. @@ -53,7 +55,7 @@ const char* ProgramInvocationShortName() { // and a letter indicating the minimum priority level we're expected to log. // This can be used to reveal or conceal logs with specific tags. void InitLogging(char* argv[]) { - if (gCmdLine != NULL) { + if (gCmdLine.get() != nullptr) { return; } // TODO: Move this to a more obvious InitART... @@ -63,17 +65,18 @@ void InitLogging(char* argv[]) { // but we don't have that luxury on the Mac, and there are a couple of argv[0] variants that are // commonly used. if (argv != NULL) { - gCmdLine = new std::string(argv[0]); + gCmdLine.reset(new std::string(argv[0])); for (size_t i = 1; argv[i] != NULL; ++i) { gCmdLine->append(" "); gCmdLine->append(argv[i]); } - gProgramInvocationName = new std::string(argv[0]); + gProgramInvocationName.reset(new std::string(argv[0])); const char* last_slash = strrchr(argv[0], '/'); - gProgramInvocationShortName = new std::string((last_slash != NULL) ? last_slash + 1 : argv[0]); + gProgramInvocationShortName.reset(new std::string((last_slash != NULL) ? last_slash + 1 + : argv[0])); } else { // TODO: fall back to /proc/self/cmdline when argv is NULL on Linux - gCmdLine = new std::string(""); + gCmdLine.reset(new std::string("")); } const char* tags = getenv("ANDROID_LOG_TAGS"); if (tags == NULL) { From 1f8730b5569a20f6293b10b98b0bda12a927be99 Mon Sep 17 00:00:00 2001 From: Brian Carlstrom Date: Wed, 13 Nov 2013 17:36:23 -0800 Subject: [PATCH 0172/2402] Fix clean-oat Change-Id: I96ba59794e50f630aaa06d226b1048000b1b6188 --- Android.mk | 1 + 1 file changed, 1 insertion(+) diff --git a/Android.mk b/Android.mk index bf2eb9a33a6..76fb411ac6f 100644 --- a/Android.mk +++ b/Android.mk @@ -18,6 +18,7 @@ LOCAL_PATH := $(call my-dir) art_path := $(LOCAL_PATH) art_build_path := $(art_path)/build +include $(art_build_path)/Android.common.mk ######################################################################## # clean-oat targets From 5ceb974adf4720f67b7157ad754aceefd0f20ecc Mon Sep 17 00:00:00 2001 From: Hiroshi Yamauchi Date: Wed, 13 Nov 2013 18:40:52 -0800 Subject: [PATCH 0173/2402] Fix libart.do boot check failure (b/11679102). The zygote space's limit should be updated as ContinuousSpace::HasAddress() uses the limit, instead of the end, as the range end of the space https://googleplex-android-review.git.corp.google.com/365173. Bug: 11679102 Change-Id: Ie02a6b858145847e7ede76b6801ce0af5c71297d --- runtime/gc/space/dlmalloc_space.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/runtime/gc/space/dlmalloc_space.cc b/runtime/gc/space/dlmalloc_space.cc index 8a5e33a4031..1c7aa22b74a 100644 --- a/runtime/gc/space/dlmalloc_space.cc +++ b/runtime/gc/space/dlmalloc_space.cc @@ -318,6 +318,7 @@ DlMallocSpace* DlMallocSpace::CreateZygoteSpace(const char* alloc_space_name) { DlMallocSpace* alloc_space = new DlMallocSpace(alloc_space_name, mem_map.release(), mspace, end_, end, limit_, growth_limit); + SetLimit(End()); live_bitmap_->SetHeapLimit(reinterpret_cast(End())); CHECK_EQ(live_bitmap_->HeapLimit(), reinterpret_cast(End())); mark_bitmap_->SetHeapLimit(reinterpret_cast(End())); From 906457c326d505f511fae42fc693cade1656c19e Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Wed, 13 Nov 2013 23:28:08 -0800 Subject: [PATCH 0174/2402] Add missing field initialization in mark sweep. Change-Id: Ied7deb35c1f1d975ef44c7b425128e0349ee44b0 --- runtime/gc/collector/mark_sweep.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/runtime/gc/collector/mark_sweep.cc b/runtime/gc/collector/mark_sweep.cc index 11e911cf46a..56dc0e528a2 100644 --- a/runtime/gc/collector/mark_sweep.cc +++ b/runtime/gc/collector/mark_sweep.cc @@ -148,6 +148,7 @@ MarkSweep::MarkSweep(Heap* heap, bool is_concurrent, const std::string& name_pre finalizer_reference_list_(NULL), phantom_reference_list_(NULL), cleared_reference_list_(NULL), + live_stack_freeze_size_(0), gc_barrier_(new Barrier(0)), large_object_lock_("mark sweep large object lock", kMarkSweepLargeObjectLock), mark_stack_lock_("mark sweep mark stack lock", kMarkSweepMarkStackLock), From 20ab6c861d248ae2822b5f38d5c09dff7506fc3a Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Wed, 13 Nov 2013 23:31:08 -0800 Subject: [PATCH 0175/2402] Avoid reading off the end of empty mapping tables. Caught by valgrind. Bug: 11670287 Change-Id: Ia5feacd6780e8e32c7ed600b5908c0b7d7ed7343 --- runtime/mapping_table.h | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/runtime/mapping_table.h b/runtime/mapping_table.h index 21620080257..9955f30d401 100644 --- a/runtime/mapping_table.h +++ b/runtime/mapping_table.h @@ -68,8 +68,10 @@ class MappingTable { native_pc_offset_(0), dex_pc_(0) { if (element == 0) { encoded_table_ptr_ = table_->FirstDexToPcPtr(); - native_pc_offset_ = DecodeUnsignedLeb128(&encoded_table_ptr_); - dex_pc_ = DecodeUnsignedLeb128(&encoded_table_ptr_); + if (end_ > 0) { + native_pc_offset_ = DecodeUnsignedLeb128(&encoded_table_ptr_); + dex_pc_ = DecodeUnsignedLeb128(&encoded_table_ptr_); + } } else { DCHECK_EQ(table_->DexToPcSize(), element); } @@ -141,8 +143,10 @@ class MappingTable { native_pc_offset_(0), dex_pc_(0) { if (element == 0) { encoded_table_ptr_ = table_->FirstPcToDexPtr(); - native_pc_offset_ = DecodeUnsignedLeb128(&encoded_table_ptr_); - dex_pc_ = DecodeUnsignedLeb128(&encoded_table_ptr_); + if (end_ > 0) { + native_pc_offset_ = DecodeUnsignedLeb128(&encoded_table_ptr_); + dex_pc_ = DecodeUnsignedLeb128(&encoded_table_ptr_); + } } else { DCHECK_EQ(table_->PcToDexSize(), element); } From 24c534d740fc50d5ed03538ec725144f44b4c0f7 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Thu, 14 Nov 2013 00:15:00 -0800 Subject: [PATCH 0176/2402] Don't read characters from 0 length strings. Caught by valgrind. Bug: 11670287 Change-Id: I3acf4855c8662b804cf0c24680fc21c50c435bdb --- compiler/image_writer.cc | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/compiler/image_writer.cc b/compiler/image_writer.cc index d2d7c0afd43..740babd73ac 100644 --- a/compiler/image_writer.cc +++ b/compiler/image_writer.cc @@ -260,7 +260,12 @@ void ImageWriter::ComputeEagerResolvedStringsCallback(Object* obj, void* arg) { const uint16_t* utf16_string = string->GetCharArray()->GetData() + string->GetOffset(); for (DexCache* dex_cache : Runtime::Current()->GetClassLinker()->GetDexCaches()) { const DexFile& dex_file = *dex_cache->GetDexFile(); - const DexFile::StringId* string_id = dex_file.FindStringId(utf16_string); + const DexFile::StringId* string_id; + if (UNLIKELY(string->GetLength() == 0)) { + string_id = dex_file.FindStringId(""); + } else { + string_id = dex_file.FindStringId(utf16_string); + } if (string_id != nullptr) { // This string occurs in this dex file, assign the dex cache entry. uint32_t string_idx = dex_file.GetIndexForStringId(*string_id); From 17189ac098b2f156713db1821b49db7b2f018bbe Mon Sep 17 00:00:00 2001 From: buzbee Date: Fri, 8 Nov 2013 11:07:02 -0800 Subject: [PATCH 0177/2402] Quick compiler compile-time/memory use improvement This CL delivers a surprisingly large reduction in compile time, as well as a significant reduction in memory usage by conditionally removing a CFG construction feature introduced to support LLVM bitcode generation. In short, bitcode requires all potential exception edges to be explicitly present in the CFG. The Quick compiler (based on the old JIT), can ignore, at least for the purposes of dataflow analysis, potential throw points that do not have a corresponding catch block. To support LLVM, we create a target basic block for every potentially throwing instruction to give us a destination for the exception edge. Then, following the check elimination pass, we remove blocks whose edges have gone away. However, if we're not using LLVM, we can skip the creation of those things in the first place. The savings are significant. Single-threaded compilation time on the host looks to be reduced by something in the vicinity of 10%. We create roughly 60% fewer basic blocks (and, importantly, the creation of fewer basic block nodes has a multiplying effect on memory use reduction because it results in fewer dataflow bitmaps). Some basic block redution stats: boot: 2325802 before, 844846 after. Phonesky: 485472 before, 156014 after. PlusOne: 806232 before, 243156 after. Thinkfree: 864498 before, 264858 after. Another nice side effect of this change is giving the basic block optimization pass generally larger scope. For arena memusage in the boot class path (compiled on the host): Average Max Before: 50,863 88,017,820 After: 41,964 4,914,208 The huge reduction in max arena memory usage is due to the collapsing of a large initialization method. Specifically, with complete exception edges org.ccil.cowan.tagsoup.Scheme requires 13,802 basic blocks. With exception edges collapsed, it requires 4. This change also caused 2 latent bugs to surface. 1) The dex parsing code did not expect that the target of a switch statement could reside in the middle of the same basic block ended by that same switch statement. 2) The x86 backend introduced a 5-operand LIR instruction for indexed memops. However, there was no corresponding change to the use/def mask creation code. Thus, the 5th operand was never included in the use/def mask. This allowed the instruction scheduling code to incorrectly move a use above a definition. We didn't see this before because the affected x86 instructions were only used for aget/aput, and prior to this CL those Dalvik opcodes caused a basic block break because of the implied exception edge - which prevented the code motion. And finally, also included is some minor tuning of register use weighting. Change-Id: I3f2cab7136dba2bded71e9e33b452b95e8fffc0e --- compiler/dex/frontend.cc | 5 ++++- compiler/dex/frontend.h | 1 + compiler/dex/local_value_numbering.cc | 16 ++++++++++++---- compiler/dex/mir_dataflow.cc | 9 +++++---- compiler/dex/mir_graph.cc | 25 ++++++++++++++++++++----- compiler/dex/mir_graph.h | 4 ++-- compiler/dex/quick/mir_to_lir-inl.h | 4 ++++ 7 files changed, 48 insertions(+), 16 deletions(-) diff --git a/compiler/dex/frontend.cc b/compiler/dex/frontend.cc index b8cd67e3e71..057177b9984 100644 --- a/compiler/dex/frontend.cc +++ b/compiler/dex/frontend.cc @@ -84,6 +84,7 @@ static uint32_t kCompilerOptimizerDisableFlags = 0 | // Disable specific optimi // (1 << kBBOpt) | // (1 << kMatch) | // (1 << kPromoteCompilerTemps) | + // (1 << kSuppressExceptionEdges) | 0; static uint32_t kCompilerDebugFlags = 0 | // Enable debug/testing modes @@ -212,7 +213,9 @@ static CompiledMethod* CompileMethod(CompilerDriver& compiler, if (compiler_backend == kPortable) { // Fused long branches not currently useful in bitcode. - cu.disable_opt |= (1 << kBranchFusing); + cu.disable_opt |= + (1 << kBranchFusing) | + (1 << kSuppressExceptionEdges); } if (cu.instruction_set == kMips) { diff --git a/compiler/dex/frontend.h b/compiler/dex/frontend.h index 43f68554b57..b9b41788901 100644 --- a/compiler/dex/frontend.h +++ b/compiler/dex/frontend.h @@ -56,6 +56,7 @@ enum opt_control_vector { kMatch, kPromoteCompilerTemps, kBranchFusing, + kSuppressExceptionEdges, }; // Force code generation paths for testing. diff --git a/compiler/dex/local_value_numbering.cc b/compiler/dex/local_value_numbering.cc index 35d29235f2c..75883b7bd6a 100644 --- a/compiler/dex/local_value_numbering.cc +++ b/compiler/dex/local_value_numbering.cc @@ -380,7 +380,9 @@ uint16_t LocalValueNumbering::GetValueNumber(MIR* mir) { } mir->optimization_flags |= MIR_IGNORE_RANGE_CHECK; } - mir->meta.throw_insn->optimization_flags |= mir->optimization_flags; + if (mir->meta.throw_insn != NULL) { + mir->meta.throw_insn->optimization_flags |= mir->optimization_flags; + } // Use side effect to note range check completed. (void)LookupValue(ARRAY_REF, array, index, NO_VALUE); // Establish value number for loaded register. Note use of memory version. @@ -419,7 +421,9 @@ uint16_t LocalValueNumbering::GetValueNumber(MIR* mir) { } mir->optimization_flags |= MIR_IGNORE_RANGE_CHECK; } - mir->meta.throw_insn->optimization_flags |= mir->optimization_flags; + if (mir->meta.throw_insn != NULL) { + mir->meta.throw_insn->optimization_flags |= mir->optimization_flags; + } // Use side effect to note range check completed. (void)LookupValue(ARRAY_REF, array, index, NO_VALUE); // Rev the memory version @@ -443,7 +447,9 @@ uint16_t LocalValueNumbering::GetValueNumber(MIR* mir) { } else { null_checked_.insert(base); } - mir->meta.throw_insn->optimization_flags |= mir->optimization_flags; + if (mir->meta.throw_insn != NULL) { + mir->meta.throw_insn->optimization_flags |= mir->optimization_flags; + } uint16_t field_ref = mir->dalvikInsn.vC; uint16_t memory_version = GetMemoryVersion(base, field_ref); if (opcode == Instruction::IGET_WIDE) { @@ -473,7 +479,9 @@ uint16_t LocalValueNumbering::GetValueNumber(MIR* mir) { } else { null_checked_.insert(base); } - mir->meta.throw_insn->optimization_flags |= mir->optimization_flags; + if (mir->meta.throw_insn != NULL) { + mir->meta.throw_insn->optimization_flags |= mir->optimization_flags; + } uint16_t field_ref = mir->dalvikInsn.vC; AdvanceMemoryVersion(base, field_ref); } diff --git a/compiler/dex/mir_dataflow.cc b/compiler/dex/mir_dataflow.cc index 11e19dc43f1..d359ee2dfe8 100644 --- a/compiler/dex/mir_dataflow.cc +++ b/compiler/dex/mir_dataflow.cc @@ -1243,12 +1243,13 @@ bool MIRGraph::CountUses(struct BasicBlock* bb) { if (mir->ssa_rep == NULL) { continue; } - // Each level of nesting adds *16 to count, up to 3 levels deep. - uint32_t weight = std::min(3U, static_cast(bb->nesting_depth) * 4); + // Each level of nesting adds *100 to count, up to 3 levels deep. + uint32_t depth = std::min(3U, static_cast(bb->nesting_depth)); + uint32_t weight = std::max(1U, depth * 100); for (int i = 0; i < mir->ssa_rep->num_uses; i++) { int s_reg = mir->ssa_rep->uses[i]; raw_use_counts_.Increment(s_reg); - use_counts_.Put(s_reg, use_counts_.Get(s_reg) + (1 << weight)); + use_counts_.Put(s_reg, use_counts_.Get(s_reg) + weight); } if (!(cu_->disable_opt & (1 << kPromoteCompilerTemps))) { int df_attributes = oat_data_flow_attributes_[mir->dalvikInsn.opcode]; @@ -1267,7 +1268,7 @@ bool MIRGraph::CountUses(struct BasicBlock* bb) { } if (uses_method_star) { raw_use_counts_.Increment(method_sreg_); - use_counts_.Put(method_sreg_, use_counts_.Get(method_sreg_) + (1 << weight)); + use_counts_.Put(method_sreg_, use_counts_.Get(method_sreg_) + weight); } } } diff --git a/compiler/dex/mir_graph.cc b/compiler/dex/mir_graph.cc index cf758fc5dab..deaf2ffe804 100644 --- a/compiler/dex/mir_graph.cc +++ b/compiler/dex/mir_graph.cc @@ -365,8 +365,8 @@ BasicBlock* MIRGraph::ProcessCanBranch(BasicBlock* cur_block, MIR* insn, DexOffs } /* Process instructions with the kSwitch flag */ -void MIRGraph::ProcessCanSwitch(BasicBlock* cur_block, MIR* insn, DexOffset cur_offset, int width, - int flags) { +BasicBlock* MIRGraph::ProcessCanSwitch(BasicBlock* cur_block, MIR* insn, DexOffset cur_offset, + int width, int flags) { const uint16_t* switch_data = reinterpret_cast(GetCurrentInsns() + cur_offset + insn->dalvikInsn.vB); int size; @@ -437,6 +437,7 @@ void MIRGraph::ProcessCanSwitch(BasicBlock* cur_block, MIR* insn, DexOffset cur_ /* create */ true, /* immed_pred_block_p */ NULL); cur_block->fall_through = fallthrough_block->id; fallthrough_block->predecessors->Insert(cur_block->id); + return cur_block; } /* Process instructions with the kThrow flag */ @@ -444,6 +445,9 @@ BasicBlock* MIRGraph::ProcessCanThrow(BasicBlock* cur_block, MIR* insn, DexOffse int width, int flags, ArenaBitVector* try_block_addr, const uint16_t* code_ptr, const uint16_t* code_end) { bool in_try_block = try_block_addr->IsBitSet(cur_offset); + bool is_throw = (insn->dalvikInsn.opcode == Instruction::THROW); + bool build_all_edges = + (cu_->disable_opt & (1 << kSuppressExceptionEdges)) || is_throw || in_try_block; /* In try block */ if (in_try_block) { @@ -473,7 +477,7 @@ BasicBlock* MIRGraph::ProcessCanThrow(BasicBlock* cur_block, MIR* insn, DexOffse cur_block->successor_blocks->Insert(successor_block_info); catch_block->predecessors->Insert(cur_block->id); } - } else { + } else if (build_all_edges) { BasicBlock *eh_block = NewMemBB(kExceptionHandling, num_blocks_++); cur_block->taken = eh_block->id; block_list_.Insert(eh_block); @@ -481,7 +485,7 @@ BasicBlock* MIRGraph::ProcessCanThrow(BasicBlock* cur_block, MIR* insn, DexOffse eh_block->predecessors->Insert(cur_block->id); } - if (insn->dalvikInsn.opcode == Instruction::THROW) { + if (is_throw) { cur_block->explicit_throw = true; if (code_ptr < code_end) { // Force creation of new block following THROW via side-effect @@ -494,6 +498,16 @@ BasicBlock* MIRGraph::ProcessCanThrow(BasicBlock* cur_block, MIR* insn, DexOffse } } + if (!build_all_edges) { + /* + * Even though there is an exception edge here, control cannot return to this + * method. Thus, for the purposes of dataflow analysis and optimization, we can + * ignore the edge. Doing this reduces compile time, and increases the scope + * of the basic-block level optimization pass. + */ + return cur_block; + } + /* * Split the potentially-throwing instruction into two parts. * The first half will be a pseudo-op that captures the exception @@ -695,7 +709,7 @@ void MIRGraph::InlineMethod(const DexFile::CodeItem* code_item, uint32_t access_ cur_block = ProcessCanThrow(cur_block, insn, current_offset_, width, flags, try_block_addr_, code_ptr, code_end); } else if (flags & Instruction::kSwitch) { - ProcessCanSwitch(cur_block, insn, current_offset_, width, flags); + cur_block = ProcessCanSwitch(cur_block, insn, current_offset_, width, flags); } current_offset_ += width; BasicBlock *next_block = FindBlock(current_offset_, /* split */ false, /* create */ @@ -1100,6 +1114,7 @@ const char* MIRGraph::GetShortyFromTargetIdx(int target_idx) { void MIRGraph::DumpMIRGraph() { BasicBlock* bb; const char* block_type_names[] = { + "Null Block", "Entry Block", "Code Block", "Exit Block", diff --git a/compiler/dex/mir_graph.h b/compiler/dex/mir_graph.h index a69dde0da38..8c20728a518 100644 --- a/compiler/dex/mir_graph.h +++ b/compiler/dex/mir_graph.h @@ -698,8 +698,8 @@ class MIRGraph { void ProcessTryCatchBlocks(); BasicBlock* ProcessCanBranch(BasicBlock* cur_block, MIR* insn, DexOffset cur_offset, int width, int flags, const uint16_t* code_ptr, const uint16_t* code_end); - void ProcessCanSwitch(BasicBlock* cur_block, MIR* insn, DexOffset cur_offset, int width, - int flags); + BasicBlock* ProcessCanSwitch(BasicBlock* cur_block, MIR* insn, DexOffset cur_offset, int width, + int flags); BasicBlock* ProcessCanThrow(BasicBlock* cur_block, MIR* insn, DexOffset cur_offset, int width, int flags, ArenaBitVector* try_block_addr, const uint16_t* code_ptr, const uint16_t* code_end); diff --git a/compiler/dex/quick/mir_to_lir-inl.h b/compiler/dex/quick/mir_to_lir-inl.h index 1a30b7aef07..f567b5c6ddc 100644 --- a/compiler/dex/quick/mir_to_lir-inl.h +++ b/compiler/dex/quick/mir_to_lir-inl.h @@ -198,6 +198,10 @@ inline void Mir2Lir::SetupResourceMasks(LIR* lir) { SetupRegMask(&lir->u.m.use_mask, lir->operands[3]); } + if (flags & REG_USE4) { + SetupRegMask(&lir->u.m.use_mask, lir->operands[4]); + } + if (flags & SETS_CCODES) { lir->u.m.def_mask |= ENCODE_CCODE; } From dfe78a6e6b526d482298100a1f6392a8c7105522 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Thu, 14 Nov 2013 09:20:55 -0800 Subject: [PATCH 0178/2402] Fix memory leak of verifier small precise constants. Change-Id: Icaabb2061916daeaba66f1f012b6e79f7f3318e6 --- runtime/verifier/reg_type_cache.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/runtime/verifier/reg_type_cache.cc b/runtime/verifier/reg_type_cache.cc index a62e835b1cd..9c9673aafd0 100644 --- a/runtime/verifier/reg_type_cache.cc +++ b/runtime/verifier/reg_type_cache.cc @@ -262,11 +262,11 @@ void RegTypeCache::ShutDown() { FloatType::Destroy(); DoubleLoType::Destroy(); DoubleHiType::Destroy(); - for (uint16_t value = kMinSmallConstant; value <= kMaxSmallConstant; ++value) { + for (int32_t value = kMinSmallConstant; value <= kMaxSmallConstant; ++value) { PreciseConstType* type = small_precise_constants_[value - kMinSmallConstant]; delete type; + small_precise_constants_[value - kMinSmallConstant] = nullptr; } - RegTypeCache::primitive_initialized_ = false; RegTypeCache::primitive_count_ = 0; } From 159aa95e1e23cd585ac305b44b4cd65ae72048a4 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Thu, 14 Nov 2013 14:51:11 -0800 Subject: [PATCH 0179/2402] Make exceptions use of LEB decoder valgrind clean. Bug: 11670287 Change-Id: I0c11c710d03e08559e0032bf602cf7cf13c92da9 --- runtime/mapping_table.h | 34 ++++++++++++++++++---------------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/runtime/mapping_table.h b/runtime/mapping_table.h index 9955f30d401..c468c1efb4c 100644 --- a/runtime/mapping_table.h +++ b/runtime/mapping_table.h @@ -30,7 +30,7 @@ class MappingTable { uint32_t TotalSize() const PURE { const uint8_t* table = encoded_table_; - if (table == NULL) { + if (table == nullptr) { return 0; } else { return DecodeUnsignedLeb128(&table); @@ -39,7 +39,7 @@ class MappingTable { uint32_t DexToPcSize() const PURE { const uint8_t* table = encoded_table_; - if (table == NULL) { + if (table == nullptr) { return 0; } else { uint32_t total_size = DecodeUnsignedLeb128(&table); @@ -50,9 +50,11 @@ class MappingTable { const uint8_t* FirstDexToPcPtr() const { const uint8_t* table = encoded_table_; - if (table != NULL) { - DecodeUnsignedLeb128(&table); // Total_size, unused. + if (table != nullptr) { + uint32_t total_size = DecodeUnsignedLeb128(&table); uint32_t pc_to_dex_size = DecodeUnsignedLeb128(&table); + // We must have dex to pc entries or else the loop will go beyond the end of the table. + DCHECK_GT(total_size, pc_to_dex_size); for (uint32_t i = 0; i < pc_to_dex_size; ++i) { DecodeUnsignedLeb128(&table); // Move ptr past native PC. DecodeUnsignedLeb128(&table); // Move ptr past dex PC. @@ -64,15 +66,15 @@ class MappingTable { class DexToPcIterator { public: DexToPcIterator(const MappingTable* table, uint32_t element) : - table_(table), element_(element), end_(table_->DexToPcSize()), encoded_table_ptr_(NULL), + table_(table), element_(element), end_(table_->DexToPcSize()), encoded_table_ptr_(nullptr), native_pc_offset_(0), dex_pc_(0) { - if (element == 0) { - encoded_table_ptr_ = table_->FirstDexToPcPtr(); + if (element == 0) { // An iterator wanted from the start. if (end_ > 0) { + encoded_table_ptr_ = table_->FirstDexToPcPtr(); native_pc_offset_ = DecodeUnsignedLeb128(&encoded_table_ptr_); dex_pc_ = DecodeUnsignedLeb128(&encoded_table_ptr_); } - } else { + } else { // An iterator wanted from the end. DCHECK_EQ(table_->DexToPcSize(), element); } } @@ -102,7 +104,7 @@ class MappingTable { const MappingTable* const table_; // The original table. uint32_t element_; // A value in the range 0 to end_. const uint32_t end_; // Equal to table_->DexToPcSize(). - const uint8_t* encoded_table_ptr_; // Either NULL or points to encoded data after this entry. + const uint8_t* encoded_table_ptr_; // Either nullptr or points to encoded data after this entry. uint32_t native_pc_offset_; // The current value of native pc offset. uint32_t dex_pc_; // The current value of dex pc. }; @@ -118,7 +120,7 @@ class MappingTable { uint32_t PcToDexSize() const PURE { const uint8_t* table = encoded_table_; - if (table == NULL) { + if (table == nullptr) { return 0; } else { DecodeUnsignedLeb128(&table); // Total_size, unused. @@ -129,7 +131,7 @@ class MappingTable { const uint8_t* FirstPcToDexPtr() const { const uint8_t* table = encoded_table_; - if (table != NULL) { + if (table != nullptr) { DecodeUnsignedLeb128(&table); // Total_size, unused. DecodeUnsignedLeb128(&table); // PC to Dex size, unused. } @@ -139,15 +141,15 @@ class MappingTable { class PcToDexIterator { public: PcToDexIterator(const MappingTable* table, uint32_t element) : - table_(table), element_(element), end_(table_->PcToDexSize()), encoded_table_ptr_(NULL), + table_(table), element_(element), end_(table_->PcToDexSize()), encoded_table_ptr_(nullptr), native_pc_offset_(0), dex_pc_(0) { - if (element == 0) { - encoded_table_ptr_ = table_->FirstPcToDexPtr(); + if (element == 0) { // An iterator wanted from the start. if (end_ > 0) { + encoded_table_ptr_ = table_->FirstPcToDexPtr(); native_pc_offset_ = DecodeUnsignedLeb128(&encoded_table_ptr_); dex_pc_ = DecodeUnsignedLeb128(&encoded_table_ptr_); } - } else { + } else { // An iterator wanted from the end. DCHECK_EQ(table_->PcToDexSize(), element); } } @@ -177,7 +179,7 @@ class MappingTable { const MappingTable* const table_; // The original table. uint32_t element_; // A value in the range 0 to PcToDexSize. const uint32_t end_; // Equal to table_->PcToDexSize(). - const uint8_t* encoded_table_ptr_; // Either NULL or points to encoded data after this entry. + const uint8_t* encoded_table_ptr_; // Either null or points to encoded data after this entry. uint32_t native_pc_offset_; // The current value of native pc offset. uint32_t dex_pc_; // The current value of dex pc. }; From 5fe9af720048673e62ee29597a30bb9e54c903c5 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Thu, 14 Nov 2013 00:17:20 -0800 Subject: [PATCH 0180/2402] Fix memory leaks relating to timing logger. Bug: 11670287. We use pointers to uninitialized values for control-flow in the timing logger code, add TODO comments to clean this up later. Remove base namespace and other bits of tidying. Change-Id: I1e6600a1e92f974c8f58f3a405a4e4abb4d9f80f --- compiler/dex/compiler_ir.h | 2 +- compiler/dex/frontend.cc | 2 +- compiler/driver/compiler_driver.cc | 26 +++++------ compiler/driver/compiler_driver.h | 26 +++++------ compiler/driver/compiler_driver_test.cc | 3 +- compiler/image_test.cc | 3 +- compiler/oat_test.cc | 4 +- dex2oat/dex2oat.cc | 8 ++-- runtime/base/timing_logger.cc | 58 +++++++++++------------- runtime/base/timing_logger.h | 18 +++----- runtime/base/timing_logger_test.cc | 30 ++++++------ runtime/common_test.h | 3 +- runtime/gc/collector/garbage_collector.h | 4 +- runtime/gc/collector/mark_sweep.cc | 30 ++++++------ runtime/gc/collector/semi_space.cc | 22 ++++----- runtime/gc/heap.cc | 8 ++-- runtime/gc/heap.h | 2 +- 17 files changed, 121 insertions(+), 128 deletions(-) diff --git a/compiler/dex/compiler_ir.h b/compiler/dex/compiler_ir.h index 546ce4aee78..3798b459d1d 100644 --- a/compiler/dex/compiler_ir.h +++ b/compiler/dex/compiler_ir.h @@ -94,7 +94,7 @@ struct CompilationUnit { UniquePtr mir_graph; // MIR container. UniquePtr cg; // Target-specific codegen. - base::TimingLogger timings; + TimingLogger timings; }; } // namespace art diff --git a/compiler/dex/frontend.cc b/compiler/dex/frontend.cc index b8cd67e3e71..c643bf85d9c 100644 --- a/compiler/dex/frontend.cc +++ b/compiler/dex/frontend.cc @@ -157,7 +157,7 @@ void CompilationUnit::EndTiming() { if (enable_debug & (1 << kDebugTimings)) { timings.EndSplit(); LOG(INFO) << "TIMINGS " << PrettyMethod(method_idx, *dex_file); - LOG(INFO) << Dumpable(timings); + LOG(INFO) << Dumpable(timings); } } diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc index d74383e33b6..b9df1d6f48a 100644 --- a/compiler/driver/compiler_driver.cc +++ b/compiler/driver/compiler_driver.cc @@ -503,7 +503,7 @@ const std::vector* CompilerDriver::CreateQuickToInterpreterBridge() con void CompilerDriver::CompileAll(jobject class_loader, const std::vector& dex_files, - base::TimingLogger& timings) { + TimingLogger& timings) { DCHECK(!Runtime::Current()->IsStarted()); UniquePtr thread_pool(new ThreadPool("Compiler driver thread pool", thread_count_ - 1)); PreCompile(class_loader, dex_files, *thread_pool.get(), timings); @@ -546,7 +546,7 @@ static DexToDexCompilationLevel GetDexToDexCompilationlevel( } } -void CompilerDriver::CompileOne(const mirror::ArtMethod* method, base::TimingLogger& timings) { +void CompilerDriver::CompileOne(const mirror::ArtMethod* method, TimingLogger& timings) { DCHECK(!Runtime::Current()->IsStarted()); Thread* self = Thread::Current(); jobject jclass_loader; @@ -591,7 +591,7 @@ void CompilerDriver::CompileOne(const mirror::ArtMethod* method, base::TimingLog } void CompilerDriver::Resolve(jobject class_loader, const std::vector& dex_files, - ThreadPool& thread_pool, base::TimingLogger& timings) { + ThreadPool& thread_pool, TimingLogger& timings) { for (size_t i = 0; i != dex_files.size(); ++i) { const DexFile* dex_file = dex_files[i]; CHECK(dex_file != NULL); @@ -600,7 +600,7 @@ void CompilerDriver::Resolve(jobject class_loader, const std::vector& dex_files, - ThreadPool& thread_pool, base::TimingLogger& timings) { + ThreadPool& thread_pool, TimingLogger& timings) { LoadImageClasses(timings); Resolve(class_loader, dex_files, thread_pool, timings); @@ -685,7 +685,7 @@ static bool RecordImageClassesVisitor(mirror::Class* klass, void* arg) } // Make a list of descriptors for classes to include in the image -void CompilerDriver::LoadImageClasses(base::TimingLogger& timings) +void CompilerDriver::LoadImageClasses(TimingLogger& timings) LOCKS_EXCLUDED(Locks::mutator_lock_) { if (!IsImage()) { return; @@ -773,7 +773,7 @@ void CompilerDriver::FindClinitImageClassesCallback(mirror::Object* object, void MaybeAddToImageClasses(object->GetClass(), compiler_driver->image_classes_.get()); } -void CompilerDriver::UpdateImageClasses(base::TimingLogger& timings) { +void CompilerDriver::UpdateImageClasses(TimingLogger& timings) { if (IsImage()) { timings.NewSplit("UpdateImageClasses"); @@ -1613,7 +1613,7 @@ static void ResolveType(const ParallelCompilationManager* manager, size_t type_i } void CompilerDriver::ResolveDexFile(jobject class_loader, const DexFile& dex_file, - ThreadPool& thread_pool, base::TimingLogger& timings) { + ThreadPool& thread_pool, TimingLogger& timings) { ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); // TODO: we could resolve strings here, although the string table is largely filled with class @@ -1632,7 +1632,7 @@ void CompilerDriver::ResolveDexFile(jobject class_loader, const DexFile& dex_fil } void CompilerDriver::Verify(jobject class_loader, const std::vector& dex_files, - ThreadPool& thread_pool, base::TimingLogger& timings) { + ThreadPool& thread_pool, TimingLogger& timings) { for (size_t i = 0; i != dex_files.size(); ++i) { const DexFile* dex_file = dex_files[i]; CHECK(dex_file != NULL); @@ -1686,7 +1686,7 @@ static void VerifyClass(const ParallelCompilationManager* manager, size_t class_ } void CompilerDriver::VerifyDexFile(jobject class_loader, const DexFile& dex_file, - ThreadPool& thread_pool, base::TimingLogger& timings) { + ThreadPool& thread_pool, TimingLogger& timings) { timings.NewSplit("Verify Dex File"); ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); ParallelCompilationManager context(class_linker, class_loader, this, &dex_file, thread_pool); @@ -2192,7 +2192,7 @@ static void InitializeClass(const ParallelCompilationManager* manager, size_t cl } void CompilerDriver::InitializeClasses(jobject jni_class_loader, const DexFile& dex_file, - ThreadPool& thread_pool, base::TimingLogger& timings) { + ThreadPool& thread_pool, TimingLogger& timings) { timings.NewSplit("InitializeNoClinit"); #ifndef NDEBUG // Sanity check blacklist descriptors. @@ -2210,7 +2210,7 @@ void CompilerDriver::InitializeClasses(jobject jni_class_loader, const DexFile& void CompilerDriver::InitializeClasses(jobject class_loader, const std::vector& dex_files, - ThreadPool& thread_pool, base::TimingLogger& timings) { + ThreadPool& thread_pool, TimingLogger& timings) { for (size_t i = 0; i != dex_files.size(); ++i) { const DexFile* dex_file = dex_files[i]; CHECK(dex_file != NULL); @@ -2219,7 +2219,7 @@ void CompilerDriver::InitializeClasses(jobject class_loader, } void CompilerDriver::Compile(jobject class_loader, const std::vector& dex_files, - ThreadPool& thread_pool, base::TimingLogger& timings) { + ThreadPool& thread_pool, TimingLogger& timings) { for (size_t i = 0; i != dex_files.size(); ++i) { const DexFile* dex_file = dex_files[i]; CHECK(dex_file != NULL); @@ -2300,7 +2300,7 @@ void CompilerDriver::CompileClass(const ParallelCompilationManager* manager, siz } void CompilerDriver::CompileDexFile(jobject class_loader, const DexFile& dex_file, - ThreadPool& thread_pool, base::TimingLogger& timings) { + ThreadPool& thread_pool, TimingLogger& timings) { timings.NewSplit("Compile Dex File"); ParallelCompilationManager context(Runtime::Current()->GetClassLinker(), class_loader, this, &dex_file, thread_pool); diff --git a/compiler/driver/compiler_driver.h b/compiler/driver/compiler_driver.h index 9bfea6ff0ad..7e8184975c8 100644 --- a/compiler/driver/compiler_driver.h +++ b/compiler/driver/compiler_driver.h @@ -98,11 +98,11 @@ class CompilerDriver { ~CompilerDriver(); void CompileAll(jobject class_loader, const std::vector& dex_files, - base::TimingLogger& timings) + TimingLogger& timings) LOCKS_EXCLUDED(Locks::mutator_lock_); // Compile a single Method - void CompileOne(const mirror::ArtMethod* method, base::TimingLogger& timings) + void CompileOne(const mirror::ArtMethod* method, TimingLogger& timings) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); const InstructionSet& GetInstructionSet() const { @@ -340,43 +340,43 @@ class CompilerDriver { SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); void PreCompile(jobject class_loader, const std::vector& dex_files, - ThreadPool& thread_pool, base::TimingLogger& timings) + ThreadPool& thread_pool, TimingLogger& timings) LOCKS_EXCLUDED(Locks::mutator_lock_); - void LoadImageClasses(base::TimingLogger& timings); + void LoadImageClasses(TimingLogger& timings); // Attempt to resolve all type, methods, fields, and strings // referenced from code in the dex file following PathClassLoader // ordering semantics. void Resolve(jobject class_loader, const std::vector& dex_files, - ThreadPool& thread_pool, base::TimingLogger& timings) + ThreadPool& thread_pool, TimingLogger& timings) LOCKS_EXCLUDED(Locks::mutator_lock_); void ResolveDexFile(jobject class_loader, const DexFile& dex_file, - ThreadPool& thread_pool, base::TimingLogger& timings) + ThreadPool& thread_pool, TimingLogger& timings) LOCKS_EXCLUDED(Locks::mutator_lock_); void Verify(jobject class_loader, const std::vector& dex_files, - ThreadPool& thread_pool, base::TimingLogger& timings); + ThreadPool& thread_pool, TimingLogger& timings); void VerifyDexFile(jobject class_loader, const DexFile& dex_file, - ThreadPool& thread_pool, base::TimingLogger& timings) + ThreadPool& thread_pool, TimingLogger& timings) LOCKS_EXCLUDED(Locks::mutator_lock_); void InitializeClasses(jobject class_loader, const std::vector& dex_files, - ThreadPool& thread_pool, base::TimingLogger& timings) + ThreadPool& thread_pool, TimingLogger& timings) LOCKS_EXCLUDED(Locks::mutator_lock_); void InitializeClasses(jobject class_loader, const DexFile& dex_file, - ThreadPool& thread_pool, base::TimingLogger& timings) + ThreadPool& thread_pool, TimingLogger& timings) LOCKS_EXCLUDED(Locks::mutator_lock_, compiled_classes_lock_); - void UpdateImageClasses(base::TimingLogger& timings) + void UpdateImageClasses(TimingLogger& timings) LOCKS_EXCLUDED(Locks::mutator_lock_); static void FindClinitImageClassesCallback(mirror::Object* object, void* arg) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); void Compile(jobject class_loader, const std::vector& dex_files, - ThreadPool& thread_pool, base::TimingLogger& timings); + ThreadPool& thread_pool, TimingLogger& timings); void CompileDexFile(jobject class_loader, const DexFile& dex_file, - ThreadPool& thread_pool, base::TimingLogger& timings) + ThreadPool& thread_pool, TimingLogger& timings) LOCKS_EXCLUDED(Locks::mutator_lock_); void CompileMethod(const DexFile::CodeItem* code_item, uint32_t access_flags, InvokeType invoke_type, uint16_t class_def_idx, uint32_t method_idx, diff --git a/compiler/driver/compiler_driver_test.cc b/compiler/driver/compiler_driver_test.cc index bfc93b3c8f9..a5eb94f0e91 100644 --- a/compiler/driver/compiler_driver_test.cc +++ b/compiler/driver/compiler_driver_test.cc @@ -36,12 +36,13 @@ namespace art { class CompilerDriverTest : public CommonTest { protected: void CompileAll(jobject class_loader) LOCKS_EXCLUDED(Locks::mutator_lock_) { - base::TimingLogger timings("CompilerDriverTest::CompileAll", false, false); + TimingLogger timings("CompilerDriverTest::CompileAll", false, false); timings.StartSplit("CompileAll"); compiler_driver_->CompileAll(class_loader, Runtime::Current()->GetCompileTimeClassPath(class_loader), timings); MakeAllExecutable(class_loader); + timings.EndSplit(); } void EnsureCompiled(jobject class_loader, const char* class_name, const char* method, diff --git a/compiler/image_test.cc b/compiler/image_test.cc index 9d9c06401e7..e22e7028f6a 100644 --- a/compiler/image_test.cc +++ b/compiler/image_test.cc @@ -46,7 +46,7 @@ TEST_F(ImageTest, WriteRead) { { jobject class_loader = NULL; ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); - base::TimingLogger timings("ImageTest::WriteRead", false, false); + TimingLogger timings("ImageTest::WriteRead", false, false); timings.StartSplit("CompileAll"); #if defined(ART_USE_PORTABLE_COMPILER) // TODO: we disable this for portable so the test executes in a reasonable amount of time. @@ -67,6 +67,7 @@ TEST_F(ImageTest, WriteRead) { oat_writer, tmp_elf.GetFile()); ASSERT_TRUE(success); + timings.EndSplit(); } } // Workound bug that mcld::Linker::emit closes tmp_elf by reopening as tmp_oat. diff --git a/compiler/oat_test.cc b/compiler/oat_test.cc index c423f34f7fb..714ec4eee80 100644 --- a/compiler/oat_test.cc +++ b/compiler/oat_test.cc @@ -82,7 +82,7 @@ TEST_F(OatTest, WriteRead) { insn_features, false, NULL, 2, true)); jobject class_loader = NULL; if (kCompile) { - base::TimingLogger timings("OatTest::WriteRead", false, false); + TimingLogger timings("OatTest::WriteRead", false, false); compiler_driver_->CompileAll(class_loader, class_linker->GetBootClassPath(), timings); } @@ -101,7 +101,7 @@ TEST_F(OatTest, WriteRead) { ASSERT_TRUE(success); if (kCompile) { // OatWriter strips the code, regenerate to compare - base::TimingLogger timings("CommonTest::WriteRead", false, false); + TimingLogger timings("CommonTest::WriteRead", false, false); compiler_driver_->CompileAll(class_loader, class_linker->GetBootClassPath(), timings); } std::string error_msg; diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc index 37819219271..fada66a9526 100644 --- a/dex2oat/dex2oat.cc +++ b/dex2oat/dex2oat.cc @@ -242,7 +242,7 @@ class Dex2Oat { bool image, UniquePtr& image_classes, bool dump_stats, - base::TimingLogger& timings) { + TimingLogger& timings) { // SirtRef and ClassLoader creation needs to come after Runtime::Create jobject class_loader = NULL; Thread* self = Thread::Current(); @@ -600,7 +600,7 @@ static InstructionSetFeatures ParseFeatureList(std::string str) { } static int dex2oat(int argc, char** argv) { - base::TimingLogger timings("compiler", false, false); + TimingLogger timings("compiler", false, false); InitLogging(argv); @@ -1091,7 +1091,7 @@ static int dex2oat(int argc, char** argv) { if (is_host) { if (dump_timing || (dump_slow_timing && timings.GetTotalNs() > MsToNs(1000))) { - LOG(INFO) << Dumpable(timings); + LOG(INFO) << Dumpable(timings); } return EXIT_SUCCESS; } @@ -1133,7 +1133,7 @@ static int dex2oat(int argc, char** argv) { timings.EndSplit(); if (dump_timing || (dump_slow_timing && timings.GetTotalNs() > MsToNs(1000))) { - LOG(INFO) << Dumpable(timings); + LOG(INFO) << Dumpable(timings); } // Everything was successfully written, do an explicit exit here to avoid running Runtime diff --git a/runtime/base/timing_logger.cc b/runtime/base/timing_logger.cc index 45a546f37e1..dae82011171 100644 --- a/runtime/base/timing_logger.cc +++ b/runtime/base/timing_logger.cc @@ -74,12 +74,11 @@ uint64_t CumulativeLogger::GetTotalTime() const { return total; } -void CumulativeLogger::AddLogger(const base::TimingLogger &logger) { +void CumulativeLogger::AddLogger(const TimingLogger &logger) { MutexLock mu(Thread::Current(), lock_); - const base::TimingLogger::SplitTimings& splits = logger.GetSplits(); - for (base::TimingLogger::SplitTimingsIterator it = splits.begin(), end = splits.end(); - it != end; ++it) { - base::TimingLogger::SplitTiming split = *it; + const TimingLogger::SplitTimings& splits = logger.GetSplits(); + for (auto it = splits.begin(), end = splits.end(); it != end; ++it) { + TimingLogger::SplitTiming split = *it; uint64_t split_time = split.first; const char* split_name = split.second; AddPair(split_name, split_time); @@ -101,7 +100,8 @@ void CumulativeLogger::AddPair(const std::string &label, uint64_t delta_time) { delta_time /= kAdjust; if (histograms_.find(label) == histograms_.end()) { - // TODO: Shoud this be a defined constant so we we know out of which orifice 16 and 100 were picked? + // TODO: Should this be a defined constant so we we know out of which orifice 16 and 100 were + // picked? const size_t max_buckets = Runtime::Current()->GetHeap()->IsLowMemoryMode() ? 16 : 100; // TODO: Should this be a defined constant so we know 50 of WTF? histograms_[label] = new Histogram(label.c_str(), 50, max_buckets); @@ -123,9 +123,6 @@ void CumulativeLogger::DumpHistogram(std::ostream &os) { os << "Done Dumping histograms \n"; } - -namespace base { - TimingLogger::TimingLogger(const char* name, bool precise, bool verbose) : name_(name), precise_(precise), verbose_(verbose), current_split_(NULL) { } @@ -136,33 +133,35 @@ void TimingLogger::Reset() { } void TimingLogger::StartSplit(const char* new_split_label) { - DCHECK(new_split_label != NULL) << "Starting split (" << new_split_label << ") with null label."; - TimingLogger::ScopedSplit* explicit_scoped_split = new TimingLogger::ScopedSplit(new_split_label, this); + DCHECK(new_split_label != nullptr) << "Starting split with null label."; + TimingLogger::ScopedSplit* explicit_scoped_split = + new TimingLogger::ScopedSplit(new_split_label, this); explicit_scoped_split->explicit_ = true; } void TimingLogger::EndSplit() { - CHECK(current_split_ != NULL) << "Ending a non-existent split."; - DCHECK(current_split_->label_ != NULL); - DCHECK(current_split_->explicit_ == true) << "Explicitly ending scoped split: " << current_split_->label_; - + CHECK(current_split_ != nullptr) << "Ending a non-existent split."; + DCHECK(current_split_->label_ != nullptr); + DCHECK(current_split_->explicit_ == true) + << "Explicitly ending scoped split: " << current_split_->label_; delete current_split_; + // TODO: current_split_ = nullptr; } // Ends the current split and starts the one given by the label. void TimingLogger::NewSplit(const char* new_split_label) { - CHECK(current_split_ != NULL) << "Inserting a new split (" << new_split_label - << ") into a non-existent split."; - DCHECK(new_split_label != NULL) << "New split (" << new_split_label << ") with null label."; - - current_split_->TailInsertSplit(new_split_label); + if (current_split_ == nullptr) { + StartSplit(new_split_label); + } else { + DCHECK(new_split_label != nullptr) << "New split (" << new_split_label << ") with null label."; + current_split_->TailInsertSplit(new_split_label); + } } uint64_t TimingLogger::GetTotalNs() const { uint64_t total_ns = 0; - for (base::TimingLogger::SplitTimingsIterator it = splits_.begin(), end = splits_.end(); - it != end; ++it) { - base::TimingLogger::SplitTiming split = *it; + for (auto it = splits_.begin(), end = splits_.end(); it != end; ++it) { + TimingLogger::SplitTiming split = *it; total_ns += split.first; } return total_ns; @@ -171,9 +170,8 @@ uint64_t TimingLogger::GetTotalNs() const { void TimingLogger::Dump(std::ostream &os) const { uint64_t longest_split = 0; uint64_t total_ns = 0; - for (base::TimingLogger::SplitTimingsIterator it = splits_.begin(), end = splits_.end(); - it != end; ++it) { - base::TimingLogger::SplitTiming split = *it; + for (auto it = splits_.begin(), end = splits_.end(); it != end; ++it) { + TimingLogger::SplitTiming split = *it; uint64_t split_time = split.first; longest_split = std::max(longest_split, split_time); total_ns += split_time; @@ -182,9 +180,8 @@ void TimingLogger::Dump(std::ostream &os) const { TimeUnit tu = GetAppropriateTimeUnit(longest_split); uint64_t divisor = GetNsToTimeUnitDivisor(tu); // Print formatted splits. - for (base::TimingLogger::SplitTimingsIterator it = splits_.begin(), end = splits_.end(); - it != end; ++it) { - base::TimingLogger::SplitTiming split = *it; + for (auto it = splits_.begin(), end = splits_.end(); it != end; ++it) { + const TimingLogger::SplitTiming& split = *it; uint64_t split_time = split.first; if (!precise_ && divisor >= 1000) { // Make the fractional part 0. @@ -231,7 +228,7 @@ TimingLogger::ScopedSplit::~ScopedSplit() { LOG(INFO) << "End: " << label_ << " " << PrettyDuration(split_time); } - // If one or more enclosed explcitly started splits are not terminated we can + // If one or more enclosed explicitly started splits are not terminated we can // either fail or "unwind" the stack of splits in the timing logger to 'this' // (by deleting the intervening scoped splits). This implements the latter. TimingLogger::ScopedSplit* current = timing_logger_->current_split_; @@ -293,5 +290,4 @@ void TimingLogger::ScopedSplit::Resume() { ATRACE_BEGIN(label_); } -} // namespace base } // namespace art diff --git a/runtime/base/timing_logger.h b/runtime/base/timing_logger.h index 501d2d7fd20..f1f78557aae 100644 --- a/runtime/base/timing_logger.h +++ b/runtime/base/timing_logger.h @@ -26,10 +26,7 @@ #include namespace art { - -namespace base { - class TimingLogger; -} // namespace base +class TimingLogger; class CumulativeLogger { public: @@ -44,7 +41,7 @@ class CumulativeLogger { // Allow the name to be modified, particularly when the cumulative logger is a field within a // parent class that is unable to determine the "name" of a sub-class. void SetName(const std::string& name); - void AddLogger(const base::TimingLogger& logger) LOCKS_EXCLUDED(lock_); + void AddLogger(const TimingLogger& logger) LOCKS_EXCLUDED(lock_); size_t GetIterations() const; private: @@ -65,19 +62,17 @@ class CumulativeLogger { DISALLOW_COPY_AND_ASSIGN(CumulativeLogger); }; -namespace base { - - // A timing logger that knows when a split starts for the purposes of logging tools, like systrace. class TimingLogger { public: // Splits are nanosecond times and split names. typedef std::pair SplitTiming; typedef std::vector SplitTimings; - typedef std::vector::const_iterator SplitTimingsIterator; explicit TimingLogger(const char* name, bool precise, bool verbose); - + ~TimingLogger() { + // TODO: DCHECK(current_split_ == nullptr) << "Forgot to end split: " << current_split_->label_; + } // Clears current splits and labels. void Reset(); @@ -143,7 +138,7 @@ class TimingLogger { friend class ScopedSplit; protected: // The name of the timing logger. - const char* name_; + const char* const name_; // Do we want to print the exactly recorded split (true) or round down to the time unit being // used (false). @@ -162,7 +157,6 @@ class TimingLogger { DISALLOW_COPY_AND_ASSIGN(TimingLogger); }; -} // namespace base } // namespace art #endif // ART_RUNTIME_BASE_TIMING_LOGGER_H_ diff --git a/runtime/base/timing_logger_test.cc b/runtime/base/timing_logger_test.cc index 8f28e4809b3..03cc9cc5e43 100644 --- a/runtime/base/timing_logger_test.cc +++ b/runtime/base/timing_logger_test.cc @@ -26,13 +26,13 @@ class TimingLoggerTest : public CommonTest {}; TEST_F(TimingLoggerTest, StartEnd) { const char* split1name = "First Split"; - base::TimingLogger timings("StartEnd", true, false); + TimingLogger timings("StartEnd", true, false); timings.StartSplit(split1name); timings.EndSplit(); // Ends split1. - const base::TimingLogger::SplitTimings& splits = timings.GetSplits(); + const TimingLogger::SplitTimings& splits = timings.GetSplits(); EXPECT_EQ(1U, splits.size()); EXPECT_STREQ(splits[0].second, split1name); @@ -43,7 +43,7 @@ TEST_F(TimingLoggerTest, StartNewEnd) { const char* split1name = "First Split"; const char* split2name = "Second Split"; const char* split3name = "Third Split"; - base::TimingLogger timings("StartNewEnd", true, false); + TimingLogger timings("StartNewEnd", true, false); timings.StartSplit(split1name); @@ -53,7 +53,7 @@ TEST_F(TimingLoggerTest, StartNewEnd) { timings.EndSplit(); // Ends split3. - const base::TimingLogger::SplitTimings& splits = timings.GetSplits(); + const TimingLogger::SplitTimings& splits = timings.GetSplits(); EXPECT_EQ(3U, splits.size()); EXPECT_STREQ(splits[0].second, split1name); @@ -67,7 +67,7 @@ TEST_F(TimingLoggerTest, StartNewEndNested) { const char* split3name = "Third Split"; const char* split4name = "Fourth Split"; const char* split5name = "Fifth Split"; - base::TimingLogger timings("StartNewEndNested", true, false); + TimingLogger timings("StartNewEndNested", true, false); timings.StartSplit(split1name); @@ -85,7 +85,7 @@ TEST_F(TimingLoggerTest, StartNewEndNested) { timings.EndSplit(); // Ends split2. - const base::TimingLogger::SplitTimings& splits = timings.GetSplits(); + const TimingLogger::SplitTimings& splits = timings.GetSplits(); EXPECT_EQ(5U, splits.size()); EXPECT_STREQ(splits[0].second, split1name); @@ -101,25 +101,25 @@ TEST_F(TimingLoggerTest, Scoped) { const char* innersplit1 = "Inner Split 1"; const char* innerinnersplit1 = "Inner Inner Split 1"; const char* innersplit2 = "Inner Split 2"; - base::TimingLogger timings("Scoped", true, false); + TimingLogger timings("Scoped", true, false); { - base::TimingLogger::ScopedSplit outer(outersplit, &timings); + TimingLogger::ScopedSplit outer(outersplit, &timings); { - base::TimingLogger::ScopedSplit inner1(innersplit1, &timings); + TimingLogger::ScopedSplit inner1(innersplit1, &timings); { - base::TimingLogger::ScopedSplit innerinner1(innerinnersplit1, &timings); + TimingLogger::ScopedSplit innerinner1(innerinnersplit1, &timings); } // Ends innerinnersplit1. } // Ends innersplit1. { - base::TimingLogger::ScopedSplit inner2(innersplit2, &timings); + TimingLogger::ScopedSplit inner2(innersplit2, &timings); } // Ends innersplit2. } // Ends outersplit. - const base::TimingLogger::SplitTimings& splits = timings.GetSplits(); + const TimingLogger::SplitTimings& splits = timings.GetSplits(); EXPECT_EQ(4U, splits.size()); EXPECT_STREQ(splits[0].second, innerinnersplit1); @@ -134,12 +134,12 @@ TEST_F(TimingLoggerTest, ScopedAndExplicit) { const char* innersplit = "Inner Split"; const char* innerinnersplit1 = "Inner Inner Split 1"; const char* innerinnersplit2 = "Inner Inner Split 2"; - base::TimingLogger timings("Scoped", true, false); + TimingLogger timings("Scoped", true, false); timings.StartSplit(outersplit); { - base::TimingLogger::ScopedSplit inner(innersplit, &timings); + TimingLogger::ScopedSplit inner(innersplit, &timings); timings.StartSplit(innerinnersplit1); @@ -148,7 +148,7 @@ TEST_F(TimingLoggerTest, ScopedAndExplicit) { timings.EndSplit(); // Ends outersplit. - const base::TimingLogger::SplitTimings& splits = timings.GetSplits(); + const TimingLogger::SplitTimings& splits = timings.GetSplits(); EXPECT_EQ(4U, splits.size()); EXPECT_STREQ(splits[0].second, innerinnersplit1); diff --git a/runtime/common_test.h b/runtime/common_test.h index 7cc29a1e58b..d860b6c34a7 100644 --- a/runtime/common_test.h +++ b/runtime/common_test.h @@ -582,10 +582,11 @@ class CommonTest : public testing::Test { void CompileMethod(mirror::ArtMethod* method) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { CHECK(method != NULL); - base::TimingLogger timings("CommonTest::CompileMethod", false, false); + TimingLogger timings("CommonTest::CompileMethod", false, false); timings.StartSplit("CompileOne"); compiler_driver_->CompileOne(method, timings); MakeExecutable(method); + timings.EndSplit(); } void CompileDirectMethod(SirtRef& class_loader, const char* class_name, diff --git a/runtime/gc/collector/garbage_collector.h b/runtime/gc/collector/garbage_collector.h index 6111c2fbf23..a80f5935c25 100644 --- a/runtime/gc/collector/garbage_collector.h +++ b/runtime/gc/collector/garbage_collector.h @@ -64,7 +64,7 @@ class GarbageCollector { void RegisterPause(uint64_t nano_length); - base::TimingLogger& GetTimings() { + TimingLogger& GetTimings() { return timings_; } @@ -131,7 +131,7 @@ class GarbageCollector { const bool verbose_; uint64_t duration_ns_; - base::TimingLogger timings_; + TimingLogger timings_; // Cumulative statistics. uint64_t total_time_ns_; diff --git a/runtime/gc/collector/mark_sweep.cc b/runtime/gc/collector/mark_sweep.cc index 56dc0e528a2..fc41c7fcc50 100644 --- a/runtime/gc/collector/mark_sweep.cc +++ b/runtime/gc/collector/mark_sweep.cc @@ -157,7 +157,7 @@ MarkSweep::MarkSweep(Heap* heap, bool is_concurrent, const std::string& name_pre void MarkSweep::InitializePhase() { timings_.Reset(); - base::TimingLogger::ScopedSplit split("InitializePhase", &timings_); + TimingLogger::ScopedSplit split("InitializePhase", &timings_); mark_stack_ = heap_->mark_stack_.get(); DCHECK(mark_stack_ != nullptr); SetImmuneRange(nullptr, nullptr); @@ -185,14 +185,14 @@ void MarkSweep::InitializePhase() { } void MarkSweep::ProcessReferences(Thread* self) { - base::TimingLogger::ScopedSplit split("ProcessReferences", &timings_); + TimingLogger::ScopedSplit split("ProcessReferences", &timings_); WriterMutexLock mu(self, *Locks::heap_bitmap_lock_); ProcessReferences(&soft_reference_list_, clear_soft_references_, &weak_reference_list_, &finalizer_reference_list_, &phantom_reference_list_); } bool MarkSweep::HandleDirtyObjectsPhase() { - base::TimingLogger::ScopedSplit split("HandleDirtyObjectsPhase", &timings_); + TimingLogger::ScopedSplit split("HandleDirtyObjectsPhase", &timings_); Thread* self = Thread::Current(); Locks::mutator_lock_->AssertExclusiveHeld(self); @@ -238,7 +238,7 @@ bool MarkSweep::IsConcurrent() const { } void MarkSweep::MarkingPhase() { - base::TimingLogger::ScopedSplit split("MarkingPhase", &timings_); + TimingLogger::ScopedSplit split("MarkingPhase", &timings_); Thread* self = Thread::Current(); BindBitmaps(); @@ -272,7 +272,7 @@ void MarkSweep::UpdateAndMarkModUnion() { if (IsImmuneSpace(space)) { const char* name = space->IsZygoteSpace() ? "UpdateAndMarkZygoteModUnionTable" : "UpdateAndMarkImageModUnionTable"; - base::TimingLogger::ScopedSplit split(name, &timings_); + TimingLogger::ScopedSplit split(name, &timings_); accounting::ModUnionTable* mod_union_table = heap_->FindModUnionTableFromSpace(space); CHECK(mod_union_table != nullptr); mod_union_table->UpdateAndMarkReferences(MarkRootCallback, this); @@ -297,7 +297,7 @@ void MarkSweep::MarkReachableObjects() { } void MarkSweep::ReclaimPhase() { - base::TimingLogger::ScopedSplit split("ReclaimPhase", &timings_); + TimingLogger::ScopedSplit split("ReclaimPhase", &timings_); Thread* self = Thread::Current(); if (!IsConcurrent()) { @@ -312,7 +312,7 @@ void MarkSweep::ReclaimPhase() { if (IsConcurrent()) { Runtime::Current()->AllowNewSystemWeaks(); - base::TimingLogger::ScopedSplit split("UnMarkAllocStack", &timings_); + TimingLogger::ScopedSplit split("UnMarkAllocStack", &timings_); WriterMutexLock mu(self, *Locks::heap_bitmap_lock_); accounting::ObjectStack* allocation_stack = GetHeap()->allocation_stack_.get(); // The allocation stack contains things allocated since the start of the GC. These may have been @@ -363,7 +363,7 @@ void MarkSweep::SetImmuneRange(Object* begin, Object* end) { } void MarkSweep::FindDefaultMarkBitmap() { - base::TimingLogger::ScopedSplit split("FindDefaultMarkBitmap", &timings_); + TimingLogger::ScopedSplit split("FindDefaultMarkBitmap", &timings_); for (const auto& space : GetHeap()->GetContinuousSpaces()) { accounting::SpaceBitmap* bitmap = space->GetMarkBitmap(); if (bitmap != nullptr && @@ -932,7 +932,7 @@ class RecursiveMarkTask : public MarkStackTask { // Populates the mark stack based on the set of marked objects and // recursively marks until the mark stack is emptied. void MarkSweep::RecursiveMark() { - base::TimingLogger::ScopedSplit split("RecursiveMark", &timings_); + TimingLogger::ScopedSplit split("RecursiveMark", &timings_); // RecursiveMark will build the lists of known instances of the Reference classes. // See DelayReferenceReferent for details. CHECK(soft_reference_list_ == NULL); @@ -1198,7 +1198,7 @@ void MarkSweep::SweepArray(accounting::ObjectStack* allocations, bool swap_bitma void MarkSweep::Sweep(bool swap_bitmaps) { DCHECK(mark_stack_->IsEmpty()); - base::TimingLogger::ScopedSplit("Sweep", &timings_); + TimingLogger::ScopedSplit("Sweep", &timings_); const bool partial = (GetGcType() == kGcTypePartial); SweepCallbackContext scc; @@ -1224,12 +1224,12 @@ void MarkSweep::Sweep(bool swap_bitmaps) { std::swap(live_bitmap, mark_bitmap); } if (!space->IsZygoteSpace()) { - base::TimingLogger::ScopedSplit split("SweepAllocSpace", &timings_); + TimingLogger::ScopedSplit split("SweepAllocSpace", &timings_); // Bitmaps are pre-swapped for optimization which enables sweeping with the heap unlocked. accounting::SpaceBitmap::SweepWalk(*live_bitmap, *mark_bitmap, begin, end, &SweepCallback, reinterpret_cast(&scc)); } else { - base::TimingLogger::ScopedSplit split("SweepZygote", &timings_); + TimingLogger::ScopedSplit split("SweepZygote", &timings_); // Zygote sweep takes care of dirtying cards and clearing live bits, does not free actual // memory. accounting::SpaceBitmap::SweepWalk(*live_bitmap, *mark_bitmap, begin, end, @@ -1242,7 +1242,7 @@ void MarkSweep::Sweep(bool swap_bitmaps) { } void MarkSweep::SweepLargeObjects(bool swap_bitmaps) { - base::TimingLogger::ScopedSplit("SweepLargeObjects", &timings_); + TimingLogger::ScopedSplit("SweepLargeObjects", &timings_); // Sweep large objects space::LargeObjectSpace* large_object_space = GetHeap()->GetLargeObjectsSpace(); accounting::SpaceSetMap* large_live_objects = large_object_space->GetLiveObjects(); @@ -1577,7 +1577,7 @@ void MarkSweep::ProcessReferences(Object** soft_references, bool clear_soft, } void MarkSweep::UnBindBitmaps() { - base::TimingLogger::ScopedSplit split("UnBindBitmaps", &timings_); + TimingLogger::ScopedSplit split("UnBindBitmaps", &timings_); for (const auto& space : GetHeap()->GetContinuousSpaces()) { if (space->IsDlMallocSpace()) { space::DlMallocSpace* alloc_space = space->AsDlMallocSpace(); @@ -1594,7 +1594,7 @@ void MarkSweep::UnBindBitmaps() { } void MarkSweep::FinishPhase() { - base::TimingLogger::ScopedSplit split("FinishPhase", &timings_); + TimingLogger::ScopedSplit split("FinishPhase", &timings_); // Can't enqueue references if we hold the mutator lock. Object* cleared_references = GetClearedReferences(); Heap* heap = GetHeap(); diff --git a/runtime/gc/collector/semi_space.cc b/runtime/gc/collector/semi_space.cc index d833631da91..aafe7a91883 100644 --- a/runtime/gc/collector/semi_space.cc +++ b/runtime/gc/collector/semi_space.cc @@ -139,7 +139,7 @@ SemiSpace::SemiSpace(Heap* heap, const std::string& name_prefix) void SemiSpace::InitializePhase() { timings_.Reset(); - base::TimingLogger::ScopedSplit split("InitializePhase", &timings_); + TimingLogger::ScopedSplit split("InitializePhase", &timings_); mark_stack_ = heap_->mark_stack_.get(); DCHECK(mark_stack_ != nullptr); immune_begin_ = nullptr; @@ -156,7 +156,7 @@ void SemiSpace::InitializePhase() { } void SemiSpace::ProcessReferences(Thread* self) { - base::TimingLogger::ScopedSplit split("ProcessReferences", &timings_); + TimingLogger::ScopedSplit split("ProcessReferences", &timings_); WriterMutexLock mu(self, *Locks::heap_bitmap_lock_); ProcessReferences(&soft_reference_list_, clear_soft_references_, &weak_reference_list_, &finalizer_reference_list_, &phantom_reference_list_); @@ -165,7 +165,7 @@ void SemiSpace::ProcessReferences(Thread* self) { void SemiSpace::MarkingPhase() { Thread* self = Thread::Current(); Locks::mutator_lock_->AssertExclusiveHeld(self); - base::TimingLogger::ScopedSplit split("MarkingPhase", &timings_); + TimingLogger::ScopedSplit split("MarkingPhase", &timings_); // Need to do this with mutators paused so that somebody doesn't accidentally allocate into the // wrong space. heap_->SwapSemiSpaces(); @@ -198,7 +198,7 @@ void SemiSpace::UpdateAndMarkModUnion() { accounting::ModUnionTable* table = heap_->FindModUnionTableFromSpace(space); CHECK(table != nullptr); // TODO: Improve naming. - base::TimingLogger::ScopedSplit split( + TimingLogger::ScopedSplit split( space->IsZygoteSpace() ? "UpdateAndMarkZygoteModUnionTable" : "UpdateAndMarkImageModUnionTable", &timings_); @@ -218,7 +218,7 @@ void SemiSpace::MarkReachableObjects() { } void SemiSpace::ReclaimPhase() { - base::TimingLogger::ScopedSplit split("ReclaimPhase", &timings_); + TimingLogger::ScopedSplit split("ReclaimPhase", &timings_); Thread* self = Thread::Current(); ProcessReferences(self); { @@ -417,7 +417,7 @@ void SemiSpace::ZygoteSweepCallback(size_t num_ptrs, Object** ptrs, void* arg) { void SemiSpace::Sweep(bool swap_bitmaps) { DCHECK(mark_stack_->IsEmpty()); - base::TimingLogger::ScopedSplit("Sweep", &timings_); + TimingLogger::ScopedSplit("Sweep", &timings_); const bool partial = (GetGcType() == kGcTypePartial); SweepCallbackContext scc; @@ -443,12 +443,12 @@ void SemiSpace::Sweep(bool swap_bitmaps) { std::swap(live_bitmap, mark_bitmap); } if (!space->IsZygoteSpace()) { - base::TimingLogger::ScopedSplit split("SweepAllocSpace", &timings_); + TimingLogger::ScopedSplit split("SweepAllocSpace", &timings_); // Bitmaps are pre-swapped for optimization which enables sweeping with the heap unlocked. accounting::SpaceBitmap::SweepWalk(*live_bitmap, *mark_bitmap, begin, end, &SweepCallback, reinterpret_cast(&scc)); } else { - base::TimingLogger::ScopedSplit split("SweepZygote", &timings_); + TimingLogger::ScopedSplit split("SweepZygote", &timings_); // Zygote sweep takes care of dirtying cards and clearing live bits, does not free actual // memory. accounting::SpaceBitmap::SweepWalk(*live_bitmap, *mark_bitmap, begin, end, @@ -461,7 +461,7 @@ void SemiSpace::Sweep(bool swap_bitmaps) { } void SemiSpace::SweepLargeObjects(bool swap_bitmaps) { - base::TimingLogger::ScopedSplit("SweepLargeObjects", &timings_); + TimingLogger::ScopedSplit("SweepLargeObjects", &timings_); // Sweep large objects space::LargeObjectSpace* large_object_space = GetHeap()->GetLargeObjectsSpace(); accounting::SpaceSetMap* large_live_objects = large_object_space->GetLiveObjects(); @@ -725,7 +725,7 @@ void SemiSpace::ProcessReferences(Object** soft_references, bool clear_soft, } void SemiSpace::UnBindBitmaps() { - base::TimingLogger::ScopedSplit split("UnBindBitmaps", &timings_); + TimingLogger::ScopedSplit split("UnBindBitmaps", &timings_); for (const auto& space : GetHeap()->GetContinuousSpaces()) { if (space->IsDlMallocSpace()) { space::DlMallocSpace* alloc_space = space->AsDlMallocSpace(); @@ -749,7 +749,7 @@ void SemiSpace::SetFromSpace(space::ContinuousMemMapAllocSpace* from_space) { } void SemiSpace::FinishPhase() { - base::TimingLogger::ScopedSplit split("FinishPhase", &timings_); + TimingLogger::ScopedSplit split("FinishPhase", &timings_); // Can't enqueue references if we hold the mutator lock. Object* cleared_references = GetClearedReferences(); Heap* heap = GetHeap(); diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc index 70df3d3133d..c2094795b8f 100644 --- a/runtime/gc/heap.cc +++ b/runtime/gc/heap.cc @@ -1474,7 +1474,7 @@ collector::GcType Heap::CollectGarbageInternal(collector::GcType gc_type, GcCaus << PrettySize(total_memory) << ", " << "paused " << pause_string.str() << " total " << PrettyDuration((duration / 1000) * 1000); if (VLOG_IS_ON(heap)) { - LOG(INFO) << Dumpable(collector->GetTimings()); + LOG(INFO) << Dumpable(collector->GetTimings()); } } } @@ -1808,17 +1808,17 @@ accounting::ModUnionTable* Heap::FindModUnionTableFromSpace(space::Space* space) return it->second; } -void Heap::ProcessCards(base::TimingLogger& timings) { +void Heap::ProcessCards(TimingLogger& timings) { // Clear cards and keep track of cards cleared in the mod-union table. for (const auto& space : continuous_spaces_) { accounting::ModUnionTable* table = FindModUnionTableFromSpace(space); if (table != nullptr) { const char* name = space->IsZygoteSpace() ? "ZygoteModUnionClearCards" : "ImageModUnionClearCards"; - base::TimingLogger::ScopedSplit split(name, &timings); + TimingLogger::ScopedSplit split(name, &timings); table->ClearCards(); } else if (space->GetType() != space::kSpaceTypeBumpPointerSpace) { - base::TimingLogger::ScopedSplit split("AllocSpaceClearCards", &timings); + TimingLogger::ScopedSplit split("AllocSpaceClearCards", &timings); // No mod union table for the AllocSpace. Age the cards so that the GC knows that these cards // were dirty before the GC started. // TODO: Don't need to use atomic. diff --git a/runtime/gc/heap.h b/runtime/gc/heap.h index 0fa000f18d9..52343bcf79e 100644 --- a/runtime/gc/heap.h +++ b/runtime/gc/heap.h @@ -638,7 +638,7 @@ class Heap { void SwapStacks(); // Clear cards and update the mod union table. - void ProcessCards(base::TimingLogger& timings); + void ProcessCards(TimingLogger& timings); // All-known continuous spaces, where objects lie within fixed bounds. std::vector continuous_spaces_; From b7cefc7f5cac99a62fd4e662c1bdeec750434e28 Mon Sep 17 00:00:00 2001 From: Jeff Hao Date: Thu, 14 Nov 2013 14:51:09 -0800 Subject: [PATCH 0181/2402] Put arguments first in debugger variable table and fix name bug. Bug: 11569468 Change-Id: I63d45427ded0937c3ab2456fe5cec22da5558e53 --- runtime/debugger.cc | 59 ++++++++++++++++++++++----------------------- runtime/dex_file.cc | 6 ++--- 2 files changed, 32 insertions(+), 33 deletions(-) diff --git a/runtime/debugger.cc b/runtime/debugger.cc index f5377092618..3ef0a7fd811 100644 --- a/runtime/debugger.cc +++ b/runtime/debugger.cc @@ -1194,41 +1194,37 @@ static uint32_t MangleAccessFlags(uint32_t accessFlags) { return accessFlags; } -static const uint16_t kEclipseWorkaroundSlot = 1000; - /* - * Eclipse appears to expect that the "this" reference is in slot zero. - * If it's not, the "variables" display will show two copies of "this", - * possibly because it gets "this" from SF.ThisObject and then displays - * all locals with nonzero slot numbers. - * - * So, we remap the item in slot 0 to 1000, and remap "this" to zero. On - * SF.GetValues / SF.SetValues we map them back. - * - * TODO: jdb uses the value to determine whether a variable is a local or an argument, - * by checking whether it's less than the number of arguments. To make that work, we'd - * have to "mangle" all the arguments to come first, not just the implicit argument 'this'. + * Circularly shifts registers so that arguments come first. Debuggers + * expect slots to begin with arguments, but dex code places them at + * the end. */ -static uint16_t MangleSlot(uint16_t slot, const char* name) { - uint16_t newSlot = slot; - if (strcmp(name, "this") == 0) { - newSlot = 0; - } else if (slot == 0) { - newSlot = kEclipseWorkaroundSlot; +static uint16_t MangleSlot(uint16_t slot, mirror::ArtMethod* m) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + const DexFile::CodeItem* code_item = MethodHelper(m).GetCodeItem(); + uint16_t ins_size = code_item->ins_size_; + uint16_t locals_size = code_item->registers_size_ - ins_size; + if (slot >= locals_size) { + return slot - locals_size; + } else { + return slot + ins_size; } - return newSlot; } +/* + * Circularly shifts registers so that arguments come last. Reverts + * slots to dex style argument placement. + */ static uint16_t DemangleSlot(uint16_t slot, mirror::ArtMethod* m) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - if (slot == kEclipseWorkaroundSlot) { - return 0; - } else if (slot == 0) { - const DexFile::CodeItem* code_item = MethodHelper(m).GetCodeItem(); - CHECK(code_item != NULL) << PrettyMethod(m); - return code_item->registers_size_ - code_item->ins_size_; + const DexFile::CodeItem* code_item = MethodHelper(m).GetCodeItem(); + uint16_t ins_size = code_item->ins_size_; + uint16_t locals_size = code_item->registers_size_ - ins_size; + if (slot < ins_size) { + return slot + locals_size; + } else { + return slot - ins_size; } - return slot; } JDWP::JdwpError Dbg::OutputDeclaredFields(JDWP::RefTypeId class_id, bool with_generic, JDWP::ExpandBuf* pReply) { @@ -1347,16 +1343,18 @@ void Dbg::OutputLineTable(JDWP::RefTypeId, JDWP::MethodId method_id, JDWP::Expan void Dbg::OutputVariableTable(JDWP::RefTypeId, JDWP::MethodId method_id, bool with_generic, JDWP::ExpandBuf* pReply) { struct DebugCallbackContext { + mirror::ArtMethod* method; JDWP::ExpandBuf* pReply; size_t variable_count; bool with_generic; - static void Callback(void* context, uint16_t slot, uint32_t startAddress, uint32_t endAddress, const char* name, const char* descriptor, const char* signature) { + static void Callback(void* context, uint16_t slot, uint32_t startAddress, uint32_t endAddress, const char* name, const char* descriptor, const char* signature) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { DebugCallbackContext* pContext = reinterpret_cast(context); - VLOG(jdwp) << StringPrintf(" %2zd: %d(%d) '%s' '%s' '%s' actual slot=%d mangled slot=%d", pContext->variable_count, startAddress, endAddress - startAddress, name, descriptor, signature, slot, MangleSlot(slot, name)); + VLOG(jdwp) << StringPrintf(" %2zd: %d(%d) '%s' '%s' '%s' actual slot=%d mangled slot=%d", pContext->variable_count, startAddress, endAddress - startAddress, name, descriptor, signature, slot, MangleSlot(slot, pContext->method)); - slot = MangleSlot(slot, name); + slot = MangleSlot(slot, pContext->method); expandBufAdd8BE(pContext->pReply, startAddress); expandBufAddUtf8String(pContext->pReply, name); @@ -1384,6 +1382,7 @@ void Dbg::OutputVariableTable(JDWP::RefTypeId, JDWP::MethodId method_id, bool wi expandBufAdd4BE(pReply, 0); DebugCallbackContext context; + context.method = m; context.pReply = pReply; context.variable_count = 0; context.with_generic = with_generic; diff --git a/runtime/dex_file.cc b/runtime/dex_file.cc index a02823eb90f..4f332929292 100644 --- a/runtime/dex_file.cc +++ b/runtime/dex_file.cc @@ -721,9 +721,9 @@ void DexFile::DecodeDebugInfo0(const CodeItem* code_item, bool is_static, uint32 for (;;) { uint8_t opcode = *stream++; uint16_t reg; - uint16_t name_idx; - uint16_t descriptor_idx; - uint16_t signature_idx = 0; + uint32_t name_idx; + uint32_t descriptor_idx; + uint32_t signature_idx = 0; switch (opcode) { case DBG_END_SEQUENCE: From c01417898a6e4f8a5663d6c0556982488c133cdf Mon Sep 17 00:00:00 2001 From: Chris Dearman Date: Thu, 14 Nov 2013 17:29:21 -0800 Subject: [PATCH 0182/2402] [MIPS] Enable futex support Change-Id: I4095d889229fe1b96f7b276d7952b3578c8fcd6d --- runtime/base/mutex.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/base/mutex.h b/runtime/base/mutex.h index ee37388f3b8..3e05b109634 100644 --- a/runtime/base/mutex.h +++ b/runtime/base/mutex.h @@ -32,7 +32,7 @@ #if defined(__APPLE__) #define ART_USE_FUTEXES 0 #else -#define ART_USE_FUTEXES !defined(__mips__) +#define ART_USE_FUTEXES 1 #endif // Currently Darwin doesn't support locks with timeouts. From 500793f33b8af8bc7ccf5595a66b4b13bce766bc Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Thu, 14 Nov 2013 17:49:12 -0800 Subject: [PATCH 0183/2402] LEB128 tests and performance measurements. Change-Id: I1850a2ff9225cfab8f896619c212b1b55f03ae94 --- build/Android.gtest.mk | 1 + compiler/leb128_encoder_test.cc | 116 ++++++++++++++++++++++++++++++++ runtime/base/histogram-inl.h | 18 ++--- runtime/base/histogram.h | 2 +- runtime/base/histogram_test.cc | 22 +++--- runtime/base/timing_logger.cc | 2 +- 6 files changed, 139 insertions(+), 22 deletions(-) create mode 100644 compiler/leb128_encoder_test.cc diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk index b07753c4fec..bed48ba9992 100644 --- a/build/Android.gtest.mk +++ b/build/Android.gtest.mk @@ -22,6 +22,7 @@ TEST_COMMON_SRC_FILES := \ compiler/elf_writer_test.cc \ compiler/image_test.cc \ compiler/jni/jni_compiler_test.cc \ + compiler/leb128_encoder_test.cc \ compiler/oat_test.cc \ compiler/output_stream_test.cc \ compiler/utils/dedupe_set_test.cc \ diff --git a/compiler/leb128_encoder_test.cc b/compiler/leb128_encoder_test.cc new file mode 100644 index 00000000000..4fa80757c5f --- /dev/null +++ b/compiler/leb128_encoder_test.cc @@ -0,0 +1,116 @@ +/* + * Copyright (C) 2013 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 "base/histogram-inl.h" +#include "common_test.h" +#include "leb128.h" +#include "leb128_encoder.h" + +namespace art { + +class Leb128Test : public CommonTest {}; + +struct DecodeUnsignedLeb128TestCase { + uint32_t decoded; + uint8_t leb128_data[5]; +}; + +static DecodeUnsignedLeb128TestCase uleb128_tests[] = { + {0, {0, 0, 0, 0, 0}}, + {1, {1, 0, 0, 0, 0}}, + {0x7F, {0x7F, 0, 0, 0, 0}}, + {0x80, {0x80, 1, 0, 0, 0}}, + {0x81, {0x81, 1, 0, 0, 0}}, + {0xFF, {0xFF, 1, 0, 0, 0}}, + {0x4000, {0x80, 0x80, 1, 0, 0}}, + {0x4001, {0x81, 0x80, 1, 0, 0}}, + {0x4081, {0x81, 0x81, 1, 0, 0}}, + {0x0FFFFFFF, {0xFF, 0xFF, 0xFF, 0x7F, 0}}, + {0xFFFFFFFF, {0xFF, 0xFF, 0xFF, 0xFF, 0xF}}, +}; + +TEST_F(Leb128Test, Singles) { + // Test individual encodings. + for (size_t i = 0; i < arraysize(uleb128_tests); ++i) { + UnsignedLeb128EncodingVector builder; + builder.PushBack(uleb128_tests[i].decoded); + const uint8_t* data_ptr = &uleb128_tests[i].leb128_data[0]; + const uint8_t* encoded_data_ptr = &builder.GetData()[0]; + for (size_t j = 0; j < 5; ++j) { + if (j < builder.GetData().size()) { + EXPECT_EQ(data_ptr[j], encoded_data_ptr[j]) << " i = " << i << " j = " << j; + } else { + EXPECT_EQ(data_ptr[j], 0U) << " i = " << i << " j = " << j; + } + } + EXPECT_EQ(DecodeUnsignedLeb128(&data_ptr), uleb128_tests[i].decoded) << " i = " << i; + } +} + +TEST_F(Leb128Test, Stream) { + // Encode a number of entries. + UnsignedLeb128EncodingVector builder; + for (size_t i = 0; i < arraysize(uleb128_tests); ++i) { + builder.PushBack(uleb128_tests[i].decoded); + } + const uint8_t* encoded_data_ptr = &builder.GetData()[0]; + for (size_t i = 0; i < arraysize(uleb128_tests); ++i) { + const uint8_t* data_ptr = &uleb128_tests[i].leb128_data[0]; + for (size_t j = 0; j < 5; ++j) { + if (data_ptr[j] != 0) { + EXPECT_EQ(data_ptr[j], encoded_data_ptr[j]) << " i = " << i << " j = " << j; + } + } + EXPECT_EQ(DecodeUnsignedLeb128(&encoded_data_ptr), uleb128_tests[i].decoded) << " i = " << i; + } +} + +TEST_F(Leb128Test, Speed) { + UniquePtr > enc_hist(new Histogram("Leb128EncodeSpeedTest", 5)); + UniquePtr > dec_hist(new Histogram("Leb128DecodeSpeedTest", 5)); + UnsignedLeb128EncodingVector builder; + // Push back 1024 chunks of 1024 values measuring encoding speed. + uint64_t last_time = NanoTime(); + for (size_t i = 0; i < 1024; i++) { + for (size_t j = 0; j < 1024; j++) { + builder.PushBack((i * 1024) + j); + } + uint64_t cur_time = NanoTime(); + enc_hist->AddValue(cur_time - last_time); + last_time = cur_time; + } + // Verify encoding and measure decode speed. + const uint8_t* encoded_data_ptr = &builder.GetData()[0]; + last_time = NanoTime(); + for (size_t i = 0; i < 1024; i++) { + for (size_t j = 0; j < 1024; j++) { + EXPECT_EQ(DecodeUnsignedLeb128(&encoded_data_ptr), (i * 1024) + j); + } + uint64_t cur_time = NanoTime(); + dec_hist->AddValue(cur_time - last_time); + last_time = cur_time; + } + + Histogram::CumulativeData enc_data; + enc_hist->CreateHistogram(&enc_data); + enc_hist->PrintConfidenceIntervals(std::cout, 0.99, enc_data); + + Histogram::CumulativeData dec_data; + dec_hist->CreateHistogram(&dec_data); + dec_hist->PrintConfidenceIntervals(std::cout, 0.99, dec_data); +} + +} // namespace art diff --git a/runtime/base/histogram-inl.h b/runtime/base/histogram-inl.h index 0345266fbdc..9e08ae6febc 100644 --- a/runtime/base/histogram-inl.h +++ b/runtime/base/histogram-inl.h @@ -170,20 +170,20 @@ inline void Histogram::PrintConfidenceIntervals(std::ostream &os, double os << FormatDuration(Max() * kAdjust, unit) << "\n"; } -template inline void Histogram::CreateHistogram(CumulativeData& out_data) { +template inline void Histogram::CreateHistogram(CumulativeData* out_data) { DCHECK_GT(sample_size_, 0ull); - out_data.freq_.clear(); - out_data.perc_.clear(); + out_data->freq_.clear(); + out_data->perc_.clear(); uint64_t accumulated = 0; - out_data.freq_.push_back(accumulated); - out_data.perc_.push_back(0.0); + out_data->freq_.push_back(accumulated); + out_data->perc_.push_back(0.0); for (size_t idx = 0; idx < frequency_.size(); idx++) { accumulated += frequency_[idx]; - out_data.freq_.push_back(accumulated); - out_data.perc_.push_back(static_cast(accumulated) / static_cast(sample_size_)); + out_data->freq_.push_back(accumulated); + out_data->perc_.push_back(static_cast(accumulated) / static_cast(sample_size_)); } - DCHECK_EQ(out_data.freq_.back(), sample_size_); - DCHECK_LE(std::abs(out_data.perc_.back() - 1.0), 0.001); + DCHECK_EQ(out_data->freq_.back(), sample_size_); + DCHECK_LE(std::abs(out_data->perc_.back() - 1.0), 0.001); } template diff --git a/runtime/base/histogram.h b/runtime/base/histogram.h index 2a02cf42451..e22b6e17b27 100644 --- a/runtime/base/histogram.h +++ b/runtime/base/histogram.h @@ -47,7 +47,7 @@ template class Histogram { // cumulative_freq[i] = sum(frequency[j] : 0 < j < i ) // Accumulative summation of percentiles; which is the frequency / SampleSize // cumulative_perc[i] = sum(frequency[j] / SampleSize : 0 < j < i ) - void CreateHistogram(CumulativeData& data); + void CreateHistogram(CumulativeData* data); // Reset the cumulative values, next time CreateHistogram is called it will recreate the cache. void Reset(); double Mean() const; diff --git a/runtime/base/histogram_test.cc b/runtime/base/histogram_test.cc index 534440c64ff..9d371f57546 100644 --- a/runtime/base/histogram_test.cc +++ b/runtime/base/histogram_test.cc @@ -85,7 +85,7 @@ TEST(Histtest, Percentile) { hist->AddValue(145); hist->AddValue(155); - hist->CreateHistogram(data); + hist->CreateHistogram(&data); PerValue = hist->Percentile(0.50, data); EXPECT_EQ(875, static_cast(PerValue * 10)); } @@ -117,7 +117,7 @@ TEST(Histtest, UpdateRange) { hist->AddValue(200); hist->AddValue(205); hist->AddValue(212); - hist->CreateHistogram(data); + hist->CreateHistogram(&data); PerValue = hist->Percentile(0.50, data); std::string text; @@ -132,7 +132,6 @@ TEST(Histtest, UpdateRange) { TEST(Histtest, Reset) { UniquePtr > hist(new Histogram("Reset", 5)); - Histogram::CumulativeData data; double PerValue; hist->AddValue(0); @@ -160,7 +159,8 @@ TEST(Histtest, Reset) { hist->AddValue(200); hist->AddValue(205); hist->AddValue(212); - hist->CreateHistogram(data); + Histogram::CumulativeData data; + hist->CreateHistogram(&data); PerValue = hist->Percentile(0.50, data); std::string text; @@ -185,7 +185,7 @@ TEST(Histtest, MultipleCreateHist) { hist->AddValue(68); hist->AddValue(75); hist->AddValue(93); - hist->CreateHistogram(data); + hist->CreateHistogram(&data); hist->AddValue(110); hist->AddValue(121); hist->AddValue(132); @@ -194,14 +194,14 @@ TEST(Histtest, MultipleCreateHist) { hist->AddValue(155); hist->AddValue(163); hist->AddValue(168); - hist->CreateHistogram(data); + hist->CreateHistogram(&data); hist->AddValue(175); hist->AddValue(182); hist->AddValue(193); hist->AddValue(200); hist->AddValue(205); hist->AddValue(212); - hist->CreateHistogram(data); + hist->CreateHistogram(&data); PerValue = hist->Percentile(0.50, data); std::stringstream stream; std::string expected("MultipleCreateHist:\t99% C.I. 15us-212us Avg: 126.380us Max: 212us\n"); @@ -217,7 +217,7 @@ TEST(Histtest, SingleValue) { Histogram::CumulativeData data; hist->AddValue(1); - hist->CreateHistogram(data); + hist->CreateHistogram(&data); std::stringstream stream; std::string expected = "SingleValue:\t99% C.I. 1us-1us Avg: 1us Max: 1us\n"; hist->PrintConfidenceIntervals(stream, 0.99, data); @@ -234,7 +234,7 @@ TEST(Histtest, CappingPercentiles) { for (uint64_t idx = 0ull; idx < 150ull; idx++) { hist->AddValue(0); } - hist->CreateHistogram(data); + hist->CreateHistogram(&data); per_995 = hist->Percentile(0.995, data); EXPECT_EQ(per_995, 0); hist->Reset(); @@ -243,7 +243,7 @@ TEST(Histtest, CappingPercentiles) { hist->AddValue(val); } } - hist->CreateHistogram(data); + hist->CreateHistogram(&data); per_005 = hist->Percentile(0.005, data); per_995 = hist->Percentile(0.995, data); EXPECT_EQ(1, per_005); @@ -260,7 +260,7 @@ TEST(Histtest, SpikyValues) { } } hist->AddValue(10000); - hist->CreateHistogram(data); + hist->CreateHistogram(&data); std::stringstream stream; std::string expected = "SpikyValues:\t99% C.I. 0.089us-2541.825us Avg: 95.033us Max: 10000us\n"; hist->PrintConfidenceIntervals(stream, 0.99, data); diff --git a/runtime/base/timing_logger.cc b/runtime/base/timing_logger.cc index dae82011171..bebbd70b817 100644 --- a/runtime/base/timing_logger.cc +++ b/runtime/base/timing_logger.cc @@ -115,7 +115,7 @@ void CumulativeLogger::DumpHistogram(std::ostream &os) { for (CumulativeLogger::HistogramsIterator it = histograms_.begin(), end = histograms_.end(); it != end; ++it) { Histogram::CumulativeData cumulative_data; - it->second->CreateHistogram(cumulative_data); + it->second->CreateHistogram(&cumulative_data); it->second->PrintConfidenceIntervals(os, 0.99, cumulative_data); // Reset cumulative values to save memory. We don't expect DumpHistogram to be called often, so // it is not performance critical. From 5bfd5c9e75b31afab2cb3676b8dc71e58a7d90df Mon Sep 17 00:00:00 2001 From: Sebastien Hertz Date: Fri, 15 Nov 2013 11:36:07 +0100 Subject: [PATCH 0184/2402] Fix missing class initialization during instrumentation. Static methods (except the ) all point to the resolution trampoline to handle class initialization. When we enable instrumentation, we update the entry point to the instrumentation stub. But doing so makes us miss the call into the trampoline. This CL fixes this issue by leaving the resolution trampoline. Once a method's class is initialized, we update all its static methods' entry point. When instrumentation is enabled, this entry point becomes the instrumentation entry stub. This also allows to post method enter events in the right order during static invokes. First, we get into the trampoline which call the method's class method and post the corresponding "method enter" event. Then we get into the instrumentation entry stub of the static method being called and post the corresponding "method enter" event before getting into its code. Bug: 11686442 Change-Id: I202db921225c8be0b2191074d09b0ba40f9248b2 --- runtime/instrumentation.cc | 25 +++++++++++++++++++------ runtime/instrumentation.h | 2 -- 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/runtime/instrumentation.cc b/runtime/instrumentation.cc index 0f4fa4e577d..77ed7b88e34 100644 --- a/runtime/instrumentation.cc +++ b/runtime/instrumentation.cc @@ -44,6 +44,8 @@ extern void SetQuickAllocEntryPointsInstrumented(bool instrumented); namespace instrumentation { +const bool kVerboseInstrumentation = false; + // Do we want to deoptimize for method entry and exit listeners or just try to intercept // invocations? Deoptimization forces all code to run in the interpreter and considerably hurts the // application's performance. @@ -57,10 +59,7 @@ static bool InstallStubsClassVisitor(mirror::Class* klass, void* arg) bool Instrumentation::InstallStubsForClass(mirror::Class* klass) { bool uninstall = !entry_exit_stubs_installed_ && !interpreter_stubs_installed_; - ClassLinker* class_linker = NULL; - if (uninstall) { - class_linker = Runtime::Current()->GetClassLinker(); - } + ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); bool is_initialized = klass->IsInitialized(); for (size_t i = 0; i < klass->NumDirectMethods(); i++) { mirror::ArtMethod* method = klass->GetDirectMethod(i); @@ -76,7 +75,14 @@ bool Instrumentation::InstallStubsForClass(mirror::Class* klass) { } } else { // !uninstall if (!interpreter_stubs_installed_ || method->IsNative()) { - new_code = GetQuickInstrumentationEntryPoint(); + // Do not overwrite resolution trampoline. When the trampoline initializes the method's + // class, all its static methods' code will be set to the instrumentation entry point. + // For more details, see ClassLinker::FixupStaticTrampolines. + if (is_initialized || !method->IsStatic() || method->IsConstructor()) { + new_code = GetQuickInstrumentationEntryPoint(); + } else { + new_code = GetResolutionTrampoline(class_linker); + } } else { new_code = GetCompiledCodeToInterpreterBridge(); } @@ -448,7 +454,14 @@ void Instrumentation::UpdateMethodsCode(mirror::ArtMethod* method, const void* c method->SetEntryPointFromCompiledCode(code); } else { if (!interpreter_stubs_installed_ || method->IsNative()) { - method->SetEntryPointFromCompiledCode(GetQuickInstrumentationEntryPoint()); + // Do not overwrite resolution trampoline. When the trampoline initializes the method's + // class, all its static methods' code will be set to the instrumentation entry point. + // For more details, see ClassLinker::FixupStaticTrampolines. + if (code == GetResolutionTrampoline(Runtime::Current()->GetClassLinker())) { + method->SetEntryPointFromCompiledCode(code); + } else { + method->SetEntryPointFromCompiledCode(GetQuickInstrumentationEntryPoint()); + } } else { method->SetEntryPointFromCompiledCode(GetCompiledCodeToInterpreterBridge()); } diff --git a/runtime/instrumentation.h b/runtime/instrumentation.h index 25a4eec976a..5647c0e7246 100644 --- a/runtime/instrumentation.h +++ b/runtime/instrumentation.h @@ -36,8 +36,6 @@ class ThrowLocation; namespace instrumentation { -const bool kVerboseInstrumentation = false; - // Interpreter handler tables. enum InterpreterHandlerTable { kMainHandlerTable = 0, // Main handler table: no suspend check, no instrumentation. From ad435ebd9d011eef66ef77e96b065024220c10ad Mon Sep 17 00:00:00 2001 From: Vladimir Marko Date: Fri, 15 Nov 2013 15:21:25 +0000 Subject: [PATCH 0185/2402] Fix Thumb2 ldrd/strd disassembly. Change-Id: Ie75aeab5b970640e90e567621ac45ce1a3a7c377 --- disassembler/disassembler_arm.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/disassembler/disassembler_arm.cc b/disassembler/disassembler_arm.cc index 65f23839048..db51fabcc9a 100644 --- a/disassembler/disassembler_arm.cc +++ b/disassembler/disassembler_arm.cc @@ -427,9 +427,9 @@ size_t DisassemblerArm::DumpThumb32(std::ostream& os, const uint8_t* instr_ptr) args << Rt << "," << Rd << ", [" << Rn; const char *sign = U ? "+" : "-"; if (P == 0 && W == 1) { - args << "], #" << sign << imm8; + args << "], #" << sign << (imm8 << 2); } else { - args << ", #" << sign << imm8 << "]"; + args << ", #" << sign << (imm8 << 2) << "]"; if (W == 1) { args << "!"; } From 39e3261168e7761fea6d873494d7c5d191285791 Mon Sep 17 00:00:00 2001 From: Mathieu Chartier Date: Tue, 12 Nov 2013 16:28:05 -0800 Subject: [PATCH 0186/2402] Refactor reference queues. Refactored the reference queue processing to reside in the heap code. This removes significant code duplication in the semispace and marksweep garbage collectors. Changed the soft reference behaviour to preserve all soft references unless the GC requires them to be cleared to avoid an out of memory error. It may be worth investigating a better heuristic in the future to preserve soft references by LRU order. Change-Id: I1f3ff5bd4b3c5149271f4bb4fc94ba199e2f9bc2 --- runtime/Android.mk | 1 + runtime/gc/collector/mark_sweep.cc | 183 ++----------------------- runtime/gc/collector/mark_sweep.h | 16 +-- runtime/gc/collector/semi_space.cc | 209 ++--------------------------- runtime/gc/collector/semi_space.h | 5 +- runtime/gc/heap.cc | 200 +++++++++++++++------------ runtime/gc/heap.h | 88 ++++++------ runtime/gc/reference_queue.cc | 163 ++++++++++++++++++++++ runtime/gc/reference_queue.h | 96 +++++++++++++ 9 files changed, 445 insertions(+), 516 deletions(-) create mode 100644 runtime/gc/reference_queue.cc create mode 100644 runtime/gc/reference_queue.h diff --git a/runtime/Android.mk b/runtime/Android.mk index 97cbdd9ab5b..60683d0d527 100644 --- a/runtime/Android.mk +++ b/runtime/Android.mk @@ -53,6 +53,7 @@ LIBART_COMMON_SRC_FILES := \ gc/collector/semi_space.cc \ gc/collector/sticky_mark_sweep.cc \ gc/heap.cc \ + gc/reference_queue.cc \ gc/space/bump_pointer_space.cc \ gc/space/dlmalloc_space.cc \ gc/space/image_space.cc \ diff --git a/runtime/gc/collector/mark_sweep.cc b/runtime/gc/collector/mark_sweep.cc index fc41c7fcc50..61b3f09a4c9 100644 --- a/runtime/gc/collector/mark_sweep.cc +++ b/runtime/gc/collector/mark_sweep.cc @@ -187,8 +187,8 @@ void MarkSweep::InitializePhase() { void MarkSweep::ProcessReferences(Thread* self) { TimingLogger::ScopedSplit split("ProcessReferences", &timings_); WriterMutexLock mu(self, *Locks::heap_bitmap_lock_); - ProcessReferences(&soft_reference_list_, clear_soft_references_, &weak_reference_list_, - &finalizer_reference_list_, &phantom_reference_list_); + GetHeap()->ProcessReferences(timings_, clear_soft_references_, &IsMarkedCallback, + &RecursiveMarkObjectCallback, this); } bool MarkSweep::HandleDirtyObjectsPhase() { @@ -407,6 +407,13 @@ inline void MarkSweep::MarkObjectNonNullParallel(const Object* obj) { } } +mirror::Object* MarkSweep::RecursiveMarkObjectCallback(mirror::Object* obj, void* arg) { + MarkSweep* mark_sweep = reinterpret_cast(arg); + mark_sweep->MarkObject(obj); + mark_sweep->ProcessMarkStack(true); + return obj; +} + inline void MarkSweep::UnMarkObjectNonNull(const Object* obj) { DCHECK(!IsImmune(obj)); // Try to take advantage of locality of references within a space, failing this find the space @@ -992,7 +999,7 @@ void MarkSweep::RecursiveMark() { ProcessMarkStack(false); } -mirror::Object* MarkSweep::SystemWeakIsMarkedCallback(Object* object, void* arg) { +mirror::Object* MarkSweep::IsMarkedCallback(Object* object, void* arg) { if (reinterpret_cast(arg)->IsMarked(object)) { return object; } @@ -1013,7 +1020,7 @@ void MarkSweep::ReMarkRoots() { void MarkSweep::SweepSystemWeaks() { Runtime* runtime = Runtime::Current(); timings_.StartSplit("SweepSystemWeaks"); - runtime->SweepSystemWeaks(SystemWeakIsMarkedCallback, this); + runtime->SweepSystemWeaks(IsMarkedCallback, this); timings_.EndSplit(); } @@ -1314,40 +1321,7 @@ void MarkSweep::DelayReferenceReferent(mirror::Class* klass, Object* obj) { DCHECK(klass != nullptr); DCHECK(klass->IsReferenceClass()); DCHECK(obj != NULL); - Object* referent = heap_->GetReferenceReferent(obj); - if (referent != NULL && !IsMarked(referent)) { - if (kCountJavaLangRefs) { - ++reference_count_; - } - Thread* self = Thread::Current(); - // TODO: Remove these locks, and use atomic stacks for storing references? - // We need to check that the references haven't already been enqueued since we can end up - // scanning the same reference multiple times due to dirty cards. - if (klass->IsSoftReferenceClass()) { - MutexLock mu(self, *heap_->GetSoftRefQueueLock()); - if (!heap_->IsEnqueued(obj)) { - heap_->EnqueuePendingReference(obj, &soft_reference_list_); - } - } else if (klass->IsWeakReferenceClass()) { - MutexLock mu(self, *heap_->GetWeakRefQueueLock()); - if (!heap_->IsEnqueued(obj)) { - heap_->EnqueuePendingReference(obj, &weak_reference_list_); - } - } else if (klass->IsFinalizerReferenceClass()) { - MutexLock mu(self, *heap_->GetFinalizerRefQueueLock()); - if (!heap_->IsEnqueued(obj)) { - heap_->EnqueuePendingReference(obj, &finalizer_reference_list_); - } - } else if (klass->IsPhantomReferenceClass()) { - MutexLock mu(self, *heap_->GetPhantomRefQueueLock()); - if (!heap_->IsEnqueued(obj)) { - heap_->EnqueuePendingReference(obj, &phantom_reference_list_); - } - } else { - LOG(FATAL) << "Invalid reference type " << PrettyClass(klass) - << " " << std::hex << klass->GetAccessFlags(); - } - } + heap_->DelayReferenceReferent(klass, obj, IsMarkedCallback, this); } class MarkObjectVisitor { @@ -1435,43 +1409,6 @@ void MarkSweep::ProcessMarkStack(bool paused) { timings_.EndSplit(); } -// Walks the reference list marking any references subject to the -// reference clearing policy. References with a black referent are -// removed from the list. References with white referents biased -// toward saving are blackened and also removed from the list. -void MarkSweep::PreserveSomeSoftReferences(Object** list) { - DCHECK(list != NULL); - Object* clear = NULL; - size_t counter = 0; - - DCHECK(mark_stack_->IsEmpty()); - - timings_.StartSplit("PreserveSomeSoftReferences"); - while (*list != NULL) { - Object* ref = heap_->DequeuePendingReference(list); - Object* referent = heap_->GetReferenceReferent(ref); - if (referent == NULL) { - // Referent was cleared by the user during marking. - continue; - } - bool is_marked = IsMarked(referent); - if (!is_marked && ((++counter) & 1)) { - // Referent is white and biased toward saving, mark it. - MarkObject(referent); - is_marked = true; - } - if (!is_marked) { - // Referent is white, queue it for clearing. - heap_->EnqueuePendingReference(ref, &clear); - } - } - *list = clear; - timings_.EndSplit(); - - // Restart the mark with the newly black references added to the root set. - ProcessMarkStack(true); -} - inline bool MarkSweep::IsMarked(const Object* object) const SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_) { if (IsImmune(object)) { @@ -1484,98 +1421,6 @@ inline bool MarkSweep::IsMarked(const Object* object) const return heap_->GetMarkBitmap()->Test(object); } -// Unlink the reference list clearing references objects with white -// referents. Cleared references registered to a reference queue are -// scheduled for appending by the heap worker thread. -void MarkSweep::ClearWhiteReferences(Object** list) { - DCHECK(list != NULL); - while (*list != NULL) { - Object* ref = heap_->DequeuePendingReference(list); - Object* referent = heap_->GetReferenceReferent(ref); - if (referent != NULL && !IsMarked(referent)) { - // Referent is white, clear it. - heap_->ClearReferenceReferent(ref); - if (heap_->IsEnqueuable(ref)) { - heap_->EnqueueReference(ref, &cleared_reference_list_); - } - } - } - DCHECK(*list == NULL); -} - -// Enqueues finalizer references with white referents. White -// referents are blackened, moved to the zombie field, and the -// referent field is cleared. -void MarkSweep::EnqueueFinalizerReferences(Object** list) { - DCHECK(list != NULL); - timings_.StartSplit("EnqueueFinalizerReferences"); - MemberOffset zombie_offset = heap_->GetFinalizerReferenceZombieOffset(); - bool has_enqueued = false; - while (*list != NULL) { - Object* ref = heap_->DequeuePendingReference(list); - Object* referent = heap_->GetReferenceReferent(ref); - if (referent != NULL && !IsMarked(referent)) { - MarkObject(referent); - // If the referent is non-null the reference must queuable. - DCHECK(heap_->IsEnqueuable(ref)); - ref->SetFieldObject(zombie_offset, referent, false); - heap_->ClearReferenceReferent(ref); - heap_->EnqueueReference(ref, &cleared_reference_list_); - has_enqueued = true; - } - } - timings_.EndSplit(); - if (has_enqueued) { - ProcessMarkStack(true); - } - DCHECK(*list == NULL); -} - -// Process reference class instances and schedule finalizations. -void MarkSweep::ProcessReferences(Object** soft_references, bool clear_soft, - Object** weak_references, - Object** finalizer_references, - Object** phantom_references) { - CHECK(soft_references != NULL); - CHECK(weak_references != NULL); - CHECK(finalizer_references != NULL); - CHECK(phantom_references != NULL); - CHECK(mark_stack_->IsEmpty()); - - // Unless we are in the zygote or required to clear soft references - // with white references, preserve some white referents. - if (!clear_soft && !Runtime::Current()->IsZygote()) { - PreserveSomeSoftReferences(soft_references); - } - - timings_.StartSplit("ProcessReferences"); - // Clear all remaining soft and weak references with white - // referents. - ClearWhiteReferences(soft_references); - ClearWhiteReferences(weak_references); - timings_.EndSplit(); - - // Preserve all white objects with finalize methods and schedule - // them for finalization. - EnqueueFinalizerReferences(finalizer_references); - - timings_.StartSplit("ProcessReferences"); - // Clear all f-reachable soft and weak references with white - // referents. - ClearWhiteReferences(soft_references); - ClearWhiteReferences(weak_references); - - // Clear all phantom references with white referents. - ClearWhiteReferences(phantom_references); - - // At this point all reference lists should be empty. - DCHECK(*soft_references == NULL); - DCHECK(*weak_references == NULL); - DCHECK(*finalizer_references == NULL); - DCHECK(*phantom_references == NULL); - timings_.EndSplit(); -} - void MarkSweep::UnBindBitmaps() { TimingLogger::ScopedSplit split("UnBindBitmaps", &timings_); for (const auto& space : GetHeap()->GetContinuousSpaces()) { @@ -1596,11 +1441,7 @@ void MarkSweep::UnBindBitmaps() { void MarkSweep::FinishPhase() { TimingLogger::ScopedSplit split("FinishPhase", &timings_); // Can't enqueue references if we hold the mutator lock. - Object* cleared_references = GetClearedReferences(); Heap* heap = GetHeap(); - timings_.NewSplit("EnqueueClearedReferences"); - heap->EnqueueClearedReferences(&cleared_references); - timings_.NewSplit("PostGcVerification"); heap->PostGcVerification(this); diff --git a/runtime/gc/collector/mark_sweep.h b/runtime/gc/collector/mark_sweep.h index cc5841244d8..53d85b0d700 100644 --- a/runtime/gc/collector/mark_sweep.h +++ b/runtime/gc/collector/mark_sweep.h @@ -189,6 +189,10 @@ class MarkSweep : public GarbageCollector { SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_, Locks::mutator_lock_); + static mirror::Object* RecursiveMarkObjectCallback(mirror::Object* obj, void* arg) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) + EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_); + static mirror::Object* MarkRootCallback(mirror::Object* root, void* arg) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_); @@ -212,10 +216,7 @@ class MarkSweep : public GarbageCollector { // Returns true if the object has its bit set in the mark bitmap. bool IsMarked(const mirror::Object* object) const; - static mirror::Object* SystemWeakIsMarkedCallback(mirror::Object* object, void* arg) - SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_); - - static mirror::Object* SystemWeakIsMarkedArrayCallback(mirror::Object* object, void* arg) + static mirror::Object* IsMarkedCallback(mirror::Object* object, void* arg) SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_); static void VerifyImageRootVisitor(mirror::Object* root, void* arg) @@ -349,13 +350,6 @@ class MarkSweep : public GarbageCollector { void ClearWhiteReferences(mirror::Object** list) SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_, Locks::mutator_lock_); - void ProcessReferences(mirror::Object** soft_references, bool clear_soft_references, - mirror::Object** weak_references, - mirror::Object** finalizer_references, - mirror::Object** phantom_references) - EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - // Whether or not we count how many of each type of object were scanned. static const bool kCountScannedTypes = false; diff --git a/runtime/gc/collector/semi_space.cc b/runtime/gc/collector/semi_space.cc index aafe7a91883..ba98314f598 100644 --- a/runtime/gc/collector/semi_space.cc +++ b/runtime/gc/collector/semi_space.cc @@ -158,8 +158,8 @@ void SemiSpace::InitializePhase() { void SemiSpace::ProcessReferences(Thread* self) { TimingLogger::ScopedSplit split("ProcessReferences", &timings_); WriterMutexLock mu(self, *Locks::heap_bitmap_lock_); - ProcessReferences(&soft_reference_list_, clear_soft_references_, &weak_reference_list_, - &finalizer_reference_list_, &phantom_reference_list_); + GetHeap()->ProcessReferences(timings_, clear_soft_references_, &MarkedForwardingAddressCallback, + &RecursiveMarkObjectCallback, this); } void SemiSpace::MarkingPhase() { @@ -344,6 +344,15 @@ Object* SemiSpace::MarkObject(Object* obj) { return ret; } +Object* SemiSpace::RecursiveMarkObjectCallback(Object* root, void* arg) { + DCHECK(root != nullptr); + DCHECK(arg != nullptr); + SemiSpace* semi_space = reinterpret_cast(arg); + mirror::Object* ret = semi_space->MarkObject(root); + semi_space->ProcessMarkStack(true); + return ret; +} + Object* SemiSpace::MarkRootCallback(Object* root, void* arg) { DCHECK(root != nullptr); DCHECK(arg != nullptr); @@ -374,13 +383,13 @@ mirror::Object* SemiSpace::GetForwardingAddress(mirror::Object* obj) { return obj; } -mirror::Object* SemiSpace::SystemWeakIsMarkedCallback(Object* object, void* arg) { +mirror::Object* SemiSpace::MarkedForwardingAddressCallback(Object* object, void* arg) { return reinterpret_cast(arg)->GetMarkedForwardAddress(object); } void SemiSpace::SweepSystemWeaks() { timings_.StartSplit("SweepSystemWeaks"); - Runtime::Current()->SweepSystemWeaks(SystemWeakIsMarkedCallback, this); + Runtime::Current()->SweepSystemWeaks(MarkedForwardingAddressCallback, this); timings_.EndSplit(); } @@ -487,45 +496,7 @@ void SemiSpace::SweepLargeObjects(bool swap_bitmaps) { // Process the "referent" field in a java.lang.ref.Reference. If the referent has not yet been // marked, put it on the appropriate list in the heap for later processing. void SemiSpace::DelayReferenceReferent(mirror::Class* klass, Object* obj) { - DCHECK(klass != nullptr); - DCHECK(klass->IsReferenceClass()); - DCHECK(obj != nullptr); - Object* referent = heap_->GetReferenceReferent(obj); - if (referent != nullptr) { - Object* forward_address = GetMarkedForwardAddress(referent); - if (forward_address == nullptr) { - Thread* self = Thread::Current(); - // TODO: Remove these locks, and use atomic stacks for storing references? - // We need to check that the references haven't already been enqueued since we can end up - // scanning the same reference multiple times due to dirty cards. - if (klass->IsSoftReferenceClass()) { - MutexLock mu(self, *heap_->GetSoftRefQueueLock()); - if (!heap_->IsEnqueued(obj)) { - heap_->EnqueuePendingReference(obj, &soft_reference_list_); - } - } else if (klass->IsWeakReferenceClass()) { - MutexLock mu(self, *heap_->GetWeakRefQueueLock()); - if (!heap_->IsEnqueued(obj)) { - heap_->EnqueuePendingReference(obj, &weak_reference_list_); - } - } else if (klass->IsFinalizerReferenceClass()) { - MutexLock mu(self, *heap_->GetFinalizerRefQueueLock()); - if (!heap_->IsEnqueued(obj)) { - heap_->EnqueuePendingReference(obj, &finalizer_reference_list_); - } - } else if (klass->IsPhantomReferenceClass()) { - MutexLock mu(self, *heap_->GetPhantomRefQueueLock()); - if (!heap_->IsEnqueued(obj)) { - heap_->EnqueuePendingReference(obj, &phantom_reference_list_); - } - } else { - LOG(FATAL) << "Invalid reference type " << PrettyClass(klass) << " " << std::hex - << klass->GetAccessFlags(); - } - } else if (referent != forward_address) { - heap_->SetReferenceReferent(obj, forward_address); - } - } + heap_->DelayReferenceReferent(klass, obj, MarkedForwardingAddressCallback, this); } // Visit all of the references of an object and update. @@ -555,48 +526,6 @@ void SemiSpace::ProcessMarkStack(bool paused) { timings_.EndSplit(); } -// Walks the reference list marking any references subject to the -// reference clearing policy. References with a black referent are -// removed from the list. References with white referents biased -// toward saving are blackened and also removed from the list. -void SemiSpace::PreserveSomeSoftReferences(Object** list) { - DCHECK(list != NULL); - Object* clear = NULL; - size_t counter = 0; - DCHECK(mark_stack_->IsEmpty()); - timings_.StartSplit("PreserveSomeSoftReferences"); - while (*list != NULL) { - Object* ref = heap_->DequeuePendingReference(list); - Object* referent = heap_->GetReferenceReferent(ref); - if (referent == NULL) { - // Referent was cleared by the user during marking. - continue; - } - Object* forward_address = GetMarkedForwardAddress(referent); - bool is_marked = forward_address != nullptr; - if (!is_marked && ((++counter) & 1)) { - // Referent is white and biased toward saving, mark it. - forward_address = MarkObject(referent); - if (referent != forward_address) { - // Update the referent if we moved it. - heap_->SetReferenceReferent(ref, forward_address); - } - } else { - if (!is_marked) { - // Referent is white, queue it for clearing. - heap_->EnqueuePendingReference(ref, &clear); - } else if (referent != forward_address) { - CHECK(forward_address != nullptr); - heap_->SetReferenceReferent(ref, forward_address); - } - } - } - *list = clear; - timings_.EndSplit(); - // Restart the mark with the newly black references added to the root set. - ProcessMarkStack(true); -} - inline Object* SemiSpace::GetMarkedForwardAddress(mirror::Object* obj) const SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_) { // All immune objects are assumed marked. @@ -618,112 +547,6 @@ inline Object* SemiSpace::GetMarkedForwardAddress(mirror::Object* obj) const return heap_->GetMarkBitmap()->Test(obj) ? obj : nullptr; } -// Unlink the reference list clearing references objects with white -// referents. Cleared references registered to a reference queue are -// scheduled for appending by the heap worker thread. -void SemiSpace::ClearWhiteReferences(Object** list) { - DCHECK(list != NULL); - while (*list != NULL) { - Object* ref = heap_->DequeuePendingReference(list); - Object* referent = heap_->GetReferenceReferent(ref); - if (referent != nullptr) { - Object* forward_address = GetMarkedForwardAddress(referent); - if (forward_address == nullptr) { - // Referent is white, clear it. - heap_->ClearReferenceReferent(ref); - if (heap_->IsEnqueuable(ref)) { - heap_->EnqueueReference(ref, &cleared_reference_list_); - } - } else if (referent != forward_address) { - heap_->SetReferenceReferent(ref, forward_address); - } - } - } - DCHECK(*list == NULL); -} - -// Enqueues finalizer references with white referents. White -// referents are blackened, moved to the zombie field, and the -// referent field is cleared. -void SemiSpace::EnqueueFinalizerReferences(Object** list) { - // *list = NULL; - // return; - DCHECK(list != NULL); - timings_.StartSplit("EnqueueFinalizerReferences"); - MemberOffset zombie_offset = heap_->GetFinalizerReferenceZombieOffset(); - bool has_enqueued = false; - while (*list != NULL) { - Object* ref = heap_->DequeuePendingReference(list); - Object* referent = heap_->GetReferenceReferent(ref); - if (referent != nullptr) { - Object* forward_address = GetMarkedForwardAddress(referent); - // Not marked. - if (forward_address == nullptr) { - forward_address = MarkObject(referent); - // If the referent is non-null the reference must queuable. - DCHECK(heap_->IsEnqueuable(ref)); - // Move the referent to the zombie field. - ref->SetFieldObject(zombie_offset, forward_address, false); - heap_->ClearReferenceReferent(ref); - heap_->EnqueueReference(ref, &cleared_reference_list_); - has_enqueued = true; - } else if (referent != forward_address) { - heap_->SetReferenceReferent(ref, forward_address); - } - } - } - timings_.EndSplit(); - if (has_enqueued) { - ProcessMarkStack(true); - } - DCHECK(*list == NULL); -} - -// Process reference class instances and schedule finalizations. -void SemiSpace::ProcessReferences(Object** soft_references, bool clear_soft, - Object** weak_references, - Object** finalizer_references, - Object** phantom_references) { - CHECK(soft_references != NULL); - CHECK(weak_references != NULL); - CHECK(finalizer_references != NULL); - CHECK(phantom_references != NULL); - CHECK(mark_stack_->IsEmpty()); - - // Unless we are in the zygote or required to clear soft references - // with white references, preserve some white referents. - if (!clear_soft && !Runtime::Current()->IsZygote()) { - PreserveSomeSoftReferences(soft_references); - } - - timings_.StartSplit("ProcessReferences"); - // Clear all remaining soft and weak references with white - // referents. - ClearWhiteReferences(soft_references); - ClearWhiteReferences(weak_references); - timings_.EndSplit(); - - // Preserve all white objects with finalize methods and schedule - // them for finalization. - EnqueueFinalizerReferences(finalizer_references); - - timings_.StartSplit("ProcessReferences"); - // Clear all f-reachable soft and weak references with white - // referents. - ClearWhiteReferences(soft_references); - ClearWhiteReferences(weak_references); - - // Clear all phantom references with white referents. - ClearWhiteReferences(phantom_references); - - // At this point all reference lists should be empty. - DCHECK(*soft_references == NULL); - DCHECK(*weak_references == NULL); - DCHECK(*finalizer_references == NULL); - DCHECK(*phantom_references == NULL); - timings_.EndSplit(); -} - void SemiSpace::UnBindBitmaps() { TimingLogger::ScopedSplit split("UnBindBitmaps", &timings_); for (const auto& space : GetHeap()->GetContinuousSpaces()) { @@ -751,11 +574,7 @@ void SemiSpace::SetFromSpace(space::ContinuousMemMapAllocSpace* from_space) { void SemiSpace::FinishPhase() { TimingLogger::ScopedSplit split("FinishPhase", &timings_); // Can't enqueue references if we hold the mutator lock. - Object* cleared_references = GetClearedReferences(); Heap* heap = GetHeap(); - timings_.NewSplit("EnqueueClearedReferences"); - heap->EnqueueClearedReferences(&cleared_references); - timings_.NewSplit("PostGcVerification"); heap->PostGcVerification(this); diff --git a/runtime/gc/collector/semi_space.h b/runtime/gc/collector/semi_space.h index 13d519559aa..0f0cae19661 100644 --- a/runtime/gc/collector/semi_space.h +++ b/runtime/gc/collector/semi_space.h @@ -150,12 +150,15 @@ class SemiSpace : public GarbageCollector { static mirror::Object* MarkRootCallback(mirror::Object* root, void* arg) EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_, Locks::mutator_lock_); + static mirror::Object* RecursiveMarkObjectCallback(mirror::Object* root, void* arg) + EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_, Locks::mutator_lock_); + protected: // Returns null if the object is not marked, otherwise returns the forwarding address (same as // object for non movable things). mirror::Object* GetMarkedForwardAddress(mirror::Object* object) const; - static mirror::Object* SystemWeakIsMarkedCallback(mirror::Object* object, void* arg) + static mirror::Object* MarkedForwardingAddressCallback(mirror::Object* object, void* arg) SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_); // Marks or unmarks a large object based on whether or not set is true. If set is true, then we diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc index c2094795b8f..f446fcf541b 100644 --- a/runtime/gc/heap.cc +++ b/runtime/gc/heap.cc @@ -82,10 +82,11 @@ Heap::Heap(size_t initial_size, size_t growth_limit, size_t min_free, size_t max long_gc_log_threshold_(long_gc_log_threshold), ignore_max_footprint_(ignore_max_footprint), have_zygote_space_(false), - soft_ref_queue_lock_(NULL), - weak_ref_queue_lock_(NULL), - finalizer_ref_queue_lock_(NULL), - phantom_ref_queue_lock_(NULL), + soft_reference_queue_(this), + weak_reference_queue_(this), + finalizer_reference_queue_(this), + phantom_reference_queue_(this), + cleared_references_(this), is_gc_running_(false), last_gc_type_(collector::kGcTypeNone), next_gc_type_(collector::kGcTypePartial), @@ -233,13 +234,6 @@ Heap::Heap(size_t initial_size, size_t growth_limit, size_t min_free, size_t max gc_complete_lock_ = new Mutex("GC complete lock"); gc_complete_cond_.reset(new ConditionVariable("GC complete condition variable", *gc_complete_lock_)); - - // Create the reference queue locks, this is required so for parallel object scanning in the GC. - soft_ref_queue_lock_ = new Mutex("Soft reference queue lock"); - weak_ref_queue_lock_ = new Mutex("Weak reference queue lock"); - finalizer_ref_queue_lock_ = new Mutex("Finalizer reference queue lock"); - phantom_ref_queue_lock_ = new Mutex("Phantom reference queue lock"); - last_gc_time_ns_ = NanoTime(); last_gc_size_ = GetBytesAllocated(); @@ -599,10 +593,6 @@ Heap::~Heap() { STLDeleteElements(&continuous_spaces_); STLDeleteElements(&discontinuous_spaces_); delete gc_complete_lock_; - delete soft_ref_queue_lock_; - delete weak_ref_queue_lock_; - delete finalizer_ref_queue_lock_; - delete phantom_ref_queue_lock_; VLOG(heap) << "Finished ~Heap()"; } @@ -640,6 +630,106 @@ space::Space* Heap::FindSpaceFromObject(const mirror::Object* obj, bool fail_ok) return FindDiscontinuousSpaceFromObject(obj, true); } +struct SoftReferenceArgs { + RootVisitor* is_marked_callback_; + RootVisitor* recursive_mark_callback_; + void* arg_; +}; + +mirror::Object* Heap::PreserveSoftReferenceCallback(mirror::Object* obj, void* arg) { + SoftReferenceArgs* args = reinterpret_cast(arg); + // TODO: Not preserve all soft references. + return args->recursive_mark_callback_(obj, args->arg_); +} + +// Process reference class instances and schedule finalizations. +void Heap::ProcessReferences(TimingLogger& timings, bool clear_soft, + RootVisitor* is_marked_callback, + RootVisitor* recursive_mark_object_callback, void* arg) { + // Unless we are in the zygote or required to clear soft references with white references, + // preserve some white referents. + if (!clear_soft && !Runtime::Current()->IsZygote()) { + SoftReferenceArgs soft_reference_args; + soft_reference_args.is_marked_callback_ = is_marked_callback; + soft_reference_args.recursive_mark_callback_ = recursive_mark_object_callback; + soft_reference_args.arg_ = arg; + soft_reference_queue_.PreserveSomeSoftReferences(&PreserveSoftReferenceCallback, + &soft_reference_args); + } + timings.StartSplit("ProcessReferences"); + // Clear all remaining soft and weak references with white referents. + soft_reference_queue_.ClearWhiteReferences(cleared_references_, is_marked_callback, arg); + weak_reference_queue_.ClearWhiteReferences(cleared_references_, is_marked_callback, arg); + timings.EndSplit(); + // Preserve all white objects with finalize methods and schedule them for finalization. + timings.StartSplit("EnqueueFinalizerReferences"); + finalizer_reference_queue_.EnqueueFinalizerReferences(cleared_references_, is_marked_callback, + recursive_mark_object_callback, arg); + timings.EndSplit(); + timings.StartSplit("ProcessReferences"); + // Clear all f-reachable soft and weak references with white referents. + soft_reference_queue_.ClearWhiteReferences(cleared_references_, is_marked_callback, arg); + weak_reference_queue_.ClearWhiteReferences(cleared_references_, is_marked_callback, arg); + // Clear all phantom references with white referents. + phantom_reference_queue_.ClearWhiteReferences(cleared_references_, is_marked_callback, arg); + // At this point all reference queues other than the cleared references should be empty. + DCHECK(soft_reference_queue_.IsEmpty()); + DCHECK(weak_reference_queue_.IsEmpty()); + DCHECK(finalizer_reference_queue_.IsEmpty()); + DCHECK(phantom_reference_queue_.IsEmpty()); + timings.EndSplit(); +} + +bool Heap::IsEnqueued(mirror::Object* ref) const { + // Since the references are stored as cyclic lists it means that once enqueued, the pending next + // will always be non-null. + return ref->GetFieldObject(GetReferencePendingNextOffset(), false) != nullptr; +} + +bool Heap::IsEnqueuable(const mirror::Object* ref) const { + DCHECK(ref != nullptr); + const mirror::Object* queue = + ref->GetFieldObject(GetReferenceQueueOffset(), false); + const mirror::Object* queue_next = + ref->GetFieldObject(GetReferenceQueueNextOffset(), false); + return queue != nullptr && queue_next == nullptr; +} + +// Process the "referent" field in a java.lang.ref.Reference. If the referent has not yet been +// marked, put it on the appropriate list in the heap for later processing. +void Heap::DelayReferenceReferent(mirror::Class* klass, mirror::Object* obj, + RootVisitor mark_visitor, void* arg) { + DCHECK(klass != nullptr); + DCHECK(klass->IsReferenceClass()); + DCHECK(obj != nullptr); + mirror::Object* referent = GetReferenceReferent(obj); + if (referent != nullptr) { + mirror::Object* forward_address = mark_visitor(referent, arg); + // Null means that the object is not currently marked. + if (forward_address == nullptr) { + Thread* self = Thread::Current(); + // TODO: Remove these locks, and use atomic stacks for storing references? + // We need to check that the references haven't already been enqueued since we can end up + // scanning the same reference multiple times due to dirty cards. + if (klass->IsSoftReferenceClass()) { + soft_reference_queue_.AtomicEnqueueIfNotEnqueued(self, obj); + } else if (klass->IsWeakReferenceClass()) { + weak_reference_queue_.AtomicEnqueueIfNotEnqueued(self, obj); + } else if (klass->IsFinalizerReferenceClass()) { + finalizer_reference_queue_.AtomicEnqueueIfNotEnqueued(self, obj); + } else if (klass->IsPhantomReferenceClass()) { + phantom_reference_queue_.AtomicEnqueueIfNotEnqueued(self, obj); + } else { + LOG(FATAL) << "Invalid reference type " << PrettyClass(klass) << " " << std::hex + << klass->GetAccessFlags(); + } + } else if (referent != forward_address) { + // Referent is already marked and we need to update it. + SetReferenceReferent(obj, forward_address); + } + } +} + space::ImageSpace* Heap::GetImageSpace() const { for (const auto& space : continuous_spaces_) { if (space->IsImageSpace()) { @@ -843,7 +933,6 @@ bool Heap::IsLiveObjectLocked(const mirror::Object* obj, bool search_allocation_ if (i > 0) { NanoSleep(MsToNs(10)); } - if (search_allocation_stack) { if (sorted) { if (allocation_stack_->ContainsSorted(const_cast(obj))) { @@ -1442,6 +1531,9 @@ collector::GcType Heap::CollectGarbageInternal(collector::GcType gc_type, GcCaus total_objects_freed_ever_ += collector->GetFreedObjects(); total_bytes_freed_ever_ += collector->GetFreedBytes(); + // Enqueue cleared references. + EnqueueClearedReferences(); + // Grow the heap so that we know when to perform the next GC. GrowForUtilization(gc_type, collector->GetDurationNs()); @@ -2072,72 +2164,6 @@ mirror::Object* Heap::GetReferenceReferent(mirror::Object* reference) { return reference->GetFieldObject(reference_referent_offset_, true); } -void Heap::ClearReferenceReferent(mirror::Object* reference) { - DCHECK(reference != NULL); - DCHECK_NE(reference_referent_offset_.Uint32Value(), 0U); - reference->SetFieldObject(reference_referent_offset_, nullptr, true); -} - -// Returns true if the reference object has not yet been enqueued. -bool Heap::IsEnqueuable(const mirror::Object* ref) { - DCHECK(ref != NULL); - const mirror::Object* queue = - ref->GetFieldObject(reference_queue_offset_, false); - const mirror::Object* queue_next = - ref->GetFieldObject(reference_queueNext_offset_, false); - return (queue != NULL) && (queue_next == NULL); -} - -void Heap::EnqueueReference(mirror::Object* ref, mirror::Object** cleared_reference_list) { - DCHECK(ref != NULL); - CHECK(ref->GetFieldObject(reference_queue_offset_, false) != NULL); - CHECK(ref->GetFieldObject(reference_queueNext_offset_, false) == NULL); - EnqueuePendingReference(ref, cleared_reference_list); -} - -bool Heap::IsEnqueued(mirror::Object* ref) { - // Since the references are stored as cyclic lists it means that once enqueued, the pending next - // will always be non-null. - return ref->GetFieldObject(GetReferencePendingNextOffset(), false) != nullptr; -} - -void Heap::EnqueuePendingReference(mirror::Object* ref, mirror::Object** list) { - DCHECK(ref != NULL); - DCHECK(list != NULL); - if (*list == NULL) { - // 1 element cyclic queue, ie: Reference ref = ..; ref.pendingNext = ref; - ref->SetFieldObject(reference_pendingNext_offset_, ref, false); - *list = ref; - } else { - mirror::Object* head = - (*list)->GetFieldObject(reference_pendingNext_offset_, false); - ref->SetFieldObject(reference_pendingNext_offset_, head, false); - (*list)->SetFieldObject(reference_pendingNext_offset_, ref, false); - } -} - -mirror::Object* Heap::DequeuePendingReference(mirror::Object** list) { - DCHECK(list != NULL); - DCHECK(*list != NULL); - mirror::Object* head = (*list)->GetFieldObject(reference_pendingNext_offset_, - false); - mirror::Object* ref; - - // Note: the following code is thread-safe because it is only called from ProcessReferences which - // is single threaded. - if (*list == head) { - ref = *list; - *list = NULL; - } else { - mirror::Object* next = head->GetFieldObject(reference_pendingNext_offset_, - false); - (*list)->SetFieldObject(reference_pendingNext_offset_, next, false); - ref = head; - } - ref->SetFieldObject(reference_pendingNext_offset_, NULL, false); - return ref; -} - void Heap::AddFinalizerReference(Thread* self, mirror::Object* object) { ScopedObjectAccess soa(self); JValue result; @@ -2168,20 +2194,18 @@ void Heap::PrintReferenceQueue(std::ostream& os, mirror::Object** queue) { } } -void Heap::EnqueueClearedReferences(mirror::Object** cleared) { - DCHECK(cleared != nullptr); - mirror::Object* list = *cleared; - if (list != nullptr) { +void Heap::EnqueueClearedReferences() { + if (!cleared_references_.IsEmpty()) { // When a runtime isn't started there are no reference queues to care about so ignore. if (LIKELY(Runtime::Current()->IsStarted())) { ScopedObjectAccess soa(Thread::Current()); JValue result; ArgArray arg_array(NULL, 0); - arg_array.Append(reinterpret_cast(list)); + arg_array.Append(reinterpret_cast(cleared_references_.GetList())); soa.DecodeMethod(WellKnownClasses::java_lang_ref_ReferenceQueue_add)->Invoke(soa.Self(), arg_array.GetArray(), arg_array.GetNumBytes(), &result, 'V'); } - *cleared = nullptr; + cleared_references_.Clear(); } } diff --git a/runtime/gc/heap.h b/runtime/gc/heap.h index 52343bcf79e..08bec99ad90 100644 --- a/runtime/gc/heap.h +++ b/runtime/gc/heap.h @@ -31,6 +31,7 @@ #include "jni.h" #include "locks.h" #include "offsets.h" +#include "reference_queue.h" #include "root_visitor.h" #include "safe_map.h" #include "thread_pool.h" @@ -289,32 +290,26 @@ class Heap { MemberOffset reference_queueNext_offset, MemberOffset reference_pendingNext_offset, MemberOffset finalizer_reference_zombie_offset); - - void SetReferenceReferent(mirror::Object* reference, mirror::Object* referent) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - mirror::Object* GetReferenceReferent(mirror::Object* reference) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - void ClearReferenceReferent(mirror::Object* reference) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - - // Returns true if the reference object has not yet been enqueued. - bool IsEnqueuable(const mirror::Object* ref); - void EnqueueReference(mirror::Object* ref, mirror::Object** list) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - bool IsEnqueued(mirror::Object* ref) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - void EnqueuePendingReference(mirror::Object* ref, mirror::Object** list) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - mirror::Object* DequeuePendingReference(mirror::Object** list) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - - MemberOffset GetReferencePendingNextOffset() { - DCHECK_NE(reference_pendingNext_offset_.Uint32Value(), 0U); + MemberOffset GetReferenceReferentOffset() const { + return reference_referent_offset_; + } + MemberOffset GetReferenceQueueOffset() const { + return reference_queue_offset_; + } + MemberOffset GetReferenceQueueNextOffset() const { + return reference_queueNext_offset_; + } + MemberOffset GetReferencePendingNextOffset() const { return reference_pendingNext_offset_; } - - MemberOffset GetFinalizerReferenceZombieOffset() { - DCHECK_NE(finalizer_reference_zombie_offset_.Uint32Value(), 0U); + MemberOffset GetFinalizerReferenceZombieOffset() const { return finalizer_reference_zombie_offset_; } + static mirror::Object* PreserveSoftReferenceCallback(mirror::Object* obj, void* arg); + void ProcessReferences(TimingLogger& timings, bool clear_soft, RootVisitor* is_marked_callback, + RootVisitor* recursive_mark_object_callback, void* arg) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) + EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_); // Enable verification of object references when the runtime is sufficiently initialized. void EnableObjectValidation() { @@ -460,22 +455,6 @@ class Heap { return large_object_space_; } - Mutex* GetSoftRefQueueLock() { - return soft_ref_queue_lock_; - } - - Mutex* GetWeakRefQueueLock() { - return weak_ref_queue_lock_; - } - - Mutex* GetFinalizerRefQueueLock() { - return finalizer_ref_queue_lock_; - } - - Mutex* GetPhantomRefQueueLock() { - return phantom_ref_queue_lock_; - } - void DumpSpaces(std::ostream& stream = LOG(INFO)); // GC performance measuring @@ -575,8 +554,20 @@ class Heap { bool IsOutOfMemoryOnAllocation(size_t alloc_size, bool grow); // Pushes a list of cleared references out to the managed heap. - void EnqueueClearedReferences(mirror::Object** cleared_references); - + void SetReferenceReferent(mirror::Object* reference, mirror::Object* referent) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + mirror::Object* GetReferenceReferent(mirror::Object* reference) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + void ClearReferenceReferent(mirror::Object* reference) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + SetReferenceReferent(reference, nullptr); + } + void EnqueueClearedReferences(); + // Returns true if the reference object has not yet been enqueued. + bool IsEnqueuable(const mirror::Object* ref) const; + bool IsEnqueued(mirror::Object* ref) const; + void DelayReferenceReferent(mirror::Class* klass, mirror::Object* obj, RootVisitor mark_visitor, + void* arg) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); // Print a reference queue. void PrintReferenceQueue(std::ostream& os, mirror::Object** queue); @@ -699,12 +690,12 @@ class Heap { Mutex* gc_complete_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER; UniquePtr gc_complete_cond_ GUARDED_BY(gc_complete_lock_); - // Mutexes held when adding references to reference queues. - // TODO: move to a UniquePtr, currently annotalysis is confused that UniquePtr isn't lockable. - Mutex* soft_ref_queue_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER; - Mutex* weak_ref_queue_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER; - Mutex* finalizer_ref_queue_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER; - Mutex* phantom_ref_queue_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER; + // Reference queues. + ReferenceQueue soft_reference_queue_; + ReferenceQueue weak_reference_queue_; + ReferenceQueue finalizer_reference_queue_; + ReferenceQueue phantom_reference_queue_; + ReferenceQueue cleared_references_; // True while the garbage collector is running. volatile bool is_gc_running_ GUARDED_BY(gc_complete_lock_); @@ -819,16 +810,12 @@ class Heap { // offset of java.lang.ref.Reference.referent MemberOffset reference_referent_offset_; - // offset of java.lang.ref.Reference.queue MemberOffset reference_queue_offset_; - // offset of java.lang.ref.Reference.queueNext MemberOffset reference_queueNext_offset_; - // offset of java.lang.ref.Reference.pendingNext MemberOffset reference_pendingNext_offset_; - // offset of java.lang.ref.FinalizerReference.zombie MemberOffset finalizer_reference_zombie_offset_; @@ -861,6 +848,7 @@ class Heap { friend class collector::MarkSweep; friend class collector::SemiSpace; + friend class ReferenceQueue; friend class VerifyReferenceCardVisitor; friend class VerifyReferenceVisitor; friend class VerifyObjectVisitor; diff --git a/runtime/gc/reference_queue.cc b/runtime/gc/reference_queue.cc new file mode 100644 index 00000000000..d006349cbb4 --- /dev/null +++ b/runtime/gc/reference_queue.cc @@ -0,0 +1,163 @@ +/* + * Copyright (C) 2013 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 "reference_queue.h" + +#include "accounting/card_table-inl.h" +#include "heap.h" +#include "mirror/class-inl.h" +#include "mirror/object-inl.h" + +namespace art { +namespace gc { + +ReferenceQueue::ReferenceQueue(Heap* heap) + : lock_("reference queue lock"), + heap_(heap), + list_(nullptr) { +} + +void ReferenceQueue::AtomicEnqueueIfNotEnqueued(Thread* self, mirror::Object* ref) { + DCHECK(ref != NULL); + MutexLock mu(self, lock_); + if (!heap_->IsEnqueued(ref)) { + EnqueuePendingReference(ref); + } +} + +void ReferenceQueue::EnqueueReference(mirror::Object* ref) { + CHECK(heap_->IsEnqueuable(ref)); + EnqueuePendingReference(ref); +} + +void ReferenceQueue::EnqueuePendingReference(mirror::Object* ref) { + DCHECK(ref != NULL); + MemberOffset pending_next_offset = heap_->GetReferencePendingNextOffset(); + DCHECK_NE(pending_next_offset.Uint32Value(), 0U); + if (IsEmpty()) { + // 1 element cyclic queue, ie: Reference ref = ..; ref.pendingNext = ref; + ref->SetFieldObject(pending_next_offset, ref, false); + list_ = ref; + } else { + mirror::Object* head = + list_->GetFieldObject(pending_next_offset, false); + ref->SetFieldObject(pending_next_offset, head, false); + list_->SetFieldObject(pending_next_offset, ref, false); + } +} + +mirror::Object* ReferenceQueue::DequeuePendingReference() { + DCHECK(!IsEmpty()); + MemberOffset pending_next_offset = heap_->GetReferencePendingNextOffset(); + mirror::Object* head = list_->GetFieldObject(pending_next_offset, false); + DCHECK(head != nullptr); + mirror::Object* ref; + // Note: the following code is thread-safe because it is only called from ProcessReferences which + // is single threaded. + if (list_ == head) { + ref = list_; + list_ = nullptr; + } else { + mirror::Object* next = head->GetFieldObject(pending_next_offset, false); + list_->SetFieldObject(pending_next_offset, next, false); + ref = head; + } + ref->SetFieldObject(pending_next_offset, nullptr, false); + return ref; +} + +void ReferenceQueue::Dump(std::ostream& os) const { + mirror::Object* cur = list_; + os << "Reference starting at list_=" << list_ << "\n"; + while (cur != nullptr) { + mirror::Object* pending_next = + cur->GetFieldObject(heap_->GetReferencePendingNextOffset(), false); + os << "PendingNext=" << pending_next; + if (cur->GetClass()->IsFinalizerReferenceClass()) { + os << " Zombie=" << + cur->GetFieldObject(heap_->GetFinalizerReferenceZombieOffset(), false); + } + os << "\n"; + cur = pending_next; + } +} + +void ReferenceQueue::ClearWhiteReferences(ReferenceQueue& cleared_references, RootVisitor visitor, + void* arg) { + while (!IsEmpty()) { + mirror::Object* ref = DequeuePendingReference(); + mirror::Object* referent = heap_->GetReferenceReferent(ref); + if (referent != nullptr) { + mirror::Object* forward_address = visitor(referent, arg); + if (forward_address == nullptr) { + // Referent is white, clear it. + heap_->ClearReferenceReferent(ref); + if (heap_->IsEnqueuable(ref)) { + cleared_references.EnqueuePendingReference(ref); + } + } else if (referent != forward_address) { + // Object moved, need to updated the referrent. + heap_->SetReferenceReferent(ref, forward_address); + } + } + } +} + +void ReferenceQueue::EnqueueFinalizerReferences(ReferenceQueue& cleared_references, + RootVisitor is_marked_callback, + RootVisitor recursive_mark_callback, void* arg) { + while (!IsEmpty()) { + mirror::Object* ref = DequeuePendingReference(); + mirror::Object* referent = heap_->GetReferenceReferent(ref); + if (referent != nullptr) { + mirror::Object* forward_address = is_marked_callback(referent, arg); + // If the referent isn't marked, mark it and update the + if (forward_address == nullptr) { + forward_address = recursive_mark_callback(referent, arg); + // If the referent is non-null the reference must queuable. + DCHECK(heap_->IsEnqueuable(ref)); + // Move the updated referent to the zombie field. + ref->SetFieldObject(heap_->GetFinalizerReferenceZombieOffset(), forward_address, false); + heap_->ClearReferenceReferent(ref); + cleared_references.EnqueueReference(ref); + } else if (referent != forward_address) { + heap_->SetReferenceReferent(ref, forward_address); + } + } + } +} + +void ReferenceQueue::PreserveSomeSoftReferences(RootVisitor preserve_callback, void* arg) { + ReferenceQueue cleared(heap_); + while (!IsEmpty()) { + mirror::Object* ref = DequeuePendingReference(); + mirror::Object* referent = heap_->GetReferenceReferent(ref); + if (referent != nullptr) { + mirror::Object* forward_address = preserve_callback(referent, arg); + if (forward_address == nullptr) { + // Either the reference isn't marked or we don't wish to preserve it. + cleared.EnqueuePendingReference(ref); + } else { + heap_->SetReferenceReferent(ref, forward_address); + } + } + } + list_ = cleared.GetList(); +} + +} // namespace gc +} // namespace art + diff --git a/runtime/gc/reference_queue.h b/runtime/gc/reference_queue.h new file mode 100644 index 00000000000..89589c39f61 --- /dev/null +++ b/runtime/gc/reference_queue.h @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ART_RUNTIME_GC_REFERENCE_QUEUE_H_ +#define ART_RUNTIME_GC_REFERENCE_QUEUE_H_ + +#include +#include +#include + +#include "atomic_integer.h" +#include "base/timing_logger.h" +#include "globals.h" +#include "gtest/gtest.h" +#include "jni.h" +#include "locks.h" +#include "offsets.h" +#include "root_visitor.h" +#include "thread_pool.h" + +namespace art { +namespace gc { + +class Heap; + +// Used to temporarily store java.lang.ref.Reference(s) during GC and prior to queueing on the +// appropriate java.lang.ref.ReferenceQueue. The linked list is maintained in the +// java.lang.ref.Reference objects. +class ReferenceQueue { + public: + explicit ReferenceQueue(Heap* heap); + // Enqueue a reference if is not already enqueued. Thread safe to call from multiple threads + // since it uses a lock to avoid a race between checking for the references presence and adding + // it. + void AtomicEnqueueIfNotEnqueued(Thread* self, mirror::Object* ref) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) LOCKS_EXCLUDED(lock_); + // Enqueue a reference, unlike EnqueuePendingReference, enqueue reference checks that the + // reference IsEnqueueable. Not thread safe, used when mutators are paused to minimize lock + // overhead. + void EnqueueReference(mirror::Object* ref) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + void EnqueuePendingReference(mirror::Object* ref) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + mirror::Object* DequeuePendingReference() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + // Enqueues finalizer references with white referents. White referents are blackened, moved to the + // zombie field, and the referent field is cleared. + void EnqueueFinalizerReferences(ReferenceQueue& cleared_references, + RootVisitor is_marked_callback, + RootVisitor recursive_mark_callback, void* arg) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + // Walks the reference list marking any references subject to the reference clearing policy. + // References with a black referent are removed from the list. References with white referents + // biased toward saving are blackened and also removed from the list. + void PreserveSomeSoftReferences(RootVisitor preserve_callback, void* arg) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + // Unlink the reference list clearing references objects with white referents. Cleared references + // registered to a reference queue are scheduled for appending by the heap worker thread. + void ClearWhiteReferences(ReferenceQueue& cleared_references, RootVisitor visitor, void* arg) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + void Dump(std::ostream& os) const + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + bool IsEmpty() const { + return list_ == nullptr; + } + void Clear() { + list_ = nullptr; + } + mirror::Object* GetList() { + return list_; + } + + private: + // Lock, used for parallel GC reference enqueuing. It allows for multiple threads simultaneously + // calling AtomicEnqueueIfNotEnqueued. + Mutex lock_; + // The heap contains the reference offsets. + Heap* const heap_; + // The actual reference list. Not a root since it will be nullptr when the GC is not running. + mirror::Object* list_; +}; + +} // namespace gc +} // namespace art + +#endif // ART_RUNTIME_GC_REFERENCE_QUEUE_H_ From 8af3d2ba07bd3f0247880ef4725f9db19f11cf5f Mon Sep 17 00:00:00 2001 From: Mathieu Chartier Date: Fri, 15 Nov 2013 09:47:39 -0800 Subject: [PATCH 0187/2402] Temporary fix for target tests. Avoid a race in pthread_join which causes us to free the memmap stack before it is done being used. Bug: 11693195 Change-Id: Ic453359cebc7395e10e83319aa25528b8d1f3882 --- runtime/thread_pool.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/runtime/thread_pool.cc b/runtime/thread_pool.cc index aca0561a775..67fcd582207 100644 --- a/runtime/thread_pool.cc +++ b/runtime/thread_pool.cc @@ -43,6 +43,8 @@ ThreadPoolWorker::ThreadPoolWorker(ThreadPool* thread_pool, const std::string& n ThreadPoolWorker::~ThreadPoolWorker() { CHECK_PTHREAD_CALL(pthread_join, (pthread_, NULL), "thread pool worker shutdown"); + // TODO: Delete this when race condition in pthread_join is fixed. + usleep(500); } void ThreadPoolWorker::Run() { From ca368cb576cf6a436a32c357fca51fbb3082d7a9 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Fri, 15 Nov 2013 15:52:08 -0800 Subject: [PATCH 0188/2402] Break apart oat writing times. Change-Id: I11045e29dc58ed1efbd19070ec1dcafc1044c95a --- compiler/image_test.cc | 2 +- compiler/oat_test.cc | 5 +++-- compiler/oat_writer.cc | 34 +++++++++++++++++++++++++++------- compiler/oat_writer.h | 3 ++- dex2oat/dex2oat.cc | 5 ++++- 5 files changed, 37 insertions(+), 12 deletions(-) diff --git a/compiler/image_test.cc b/compiler/image_test.cc index e22e7028f6a..a8a9d2e4619 100644 --- a/compiler/image_test.cc +++ b/compiler/image_test.cc @@ -60,7 +60,7 @@ TEST_F(ImageTest, WriteRead) { ScopedObjectAccess soa(Thread::Current()); OatWriter oat_writer(class_linker->GetBootClassPath(), - 0, 0, "", compiler_driver_.get()); + 0, 0, "", compiler_driver_.get(), &timings); bool success = compiler_driver_->WriteElf(GetTestAndroidRoot(), !kIsTargetBuild, class_linker->GetBootClassPath(), diff --git a/compiler/oat_test.cc b/compiler/oat_test.cc index 714ec4eee80..fd0a69deea5 100644 --- a/compiler/oat_test.cc +++ b/compiler/oat_test.cc @@ -67,6 +67,7 @@ class OatTest : public CommonTest { }; TEST_F(OatTest, WriteRead) { + TimingLogger timings("CommonTest::WriteRead", false, false); ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); // TODO: make selectable @@ -92,7 +93,8 @@ TEST_F(OatTest, WriteRead) { 42U, 4096U, "lue.art", - compiler_driver_.get()); + compiler_driver_.get(), + &timings); bool success = compiler_driver_->WriteElf(GetTestAndroidRoot(), !kIsTargetBuild, class_linker->GetBootClassPath(), @@ -101,7 +103,6 @@ TEST_F(OatTest, WriteRead) { ASSERT_TRUE(success); if (kCompile) { // OatWriter strips the code, regenerate to compare - TimingLogger timings("CommonTest::WriteRead", false, false); compiler_driver_->CompileAll(class_loader, class_linker->GetBootClassPath(), timings); } std::string error_msg; diff --git a/compiler/oat_writer.cc b/compiler/oat_writer.cc index 28fb1479d71..83824694ae4 100644 --- a/compiler/oat_writer.cc +++ b/compiler/oat_writer.cc @@ -40,7 +40,8 @@ OatWriter::OatWriter(const std::vector& dex_files, uint32_t image_file_location_oat_checksum, uint32_t image_file_location_oat_begin, const std::string& image_file_location, - const CompilerDriver* compiler) + const CompilerDriver* compiler, + TimingLogger* timings) : compiler_driver_(compiler), dex_files_(&dex_files), image_file_location_oat_checksum_(image_file_location_oat_checksum), @@ -77,12 +78,31 @@ OatWriter::OatWriter(const std::vector& dex_files, size_oat_class_status_(0), size_oat_class_method_bitmaps_(0), size_oat_class_method_offsets_(0) { - size_t offset = InitOatHeader(); - offset = InitOatDexFiles(offset); - offset = InitDexFiles(offset); - offset = InitOatClasses(offset); - offset = InitOatCode(offset); - offset = InitOatCodeDexFiles(offset); + size_t offset; + { + TimingLogger::ScopedSplit split("InitOatHeader", timings); + offset = InitOatHeader(); + } + { + TimingLogger::ScopedSplit split("InitOatDexFiles", timings); + offset = InitOatDexFiles(offset); + } + { + TimingLogger::ScopedSplit split("InitDexFiles", timings); + offset = InitDexFiles(offset); + } + { + TimingLogger::ScopedSplit split("InitOatClasses", timings); + offset = InitOatClasses(offset); + } + { + TimingLogger::ScopedSplit split("InitOatCode", timings); + offset = InitOatCode(offset); + } + { + TimingLogger::ScopedSplit split("InitOatCodeDexFiles", timings); + offset = InitOatCodeDexFiles(offset); + } size_ = offset; CHECK_EQ(dex_files_->size(), oat_dex_files_.size()); diff --git a/compiler/oat_writer.h b/compiler/oat_writer.h index 5d947cfaeaa..64275e6bbb0 100644 --- a/compiler/oat_writer.h +++ b/compiler/oat_writer.h @@ -67,7 +67,8 @@ class OatWriter { uint32_t image_file_location_oat_checksum, uint32_t image_file_location_oat_begin, const std::string& image_file_location, - const CompilerDriver* compiler); + const CompilerDriver* compiler, + TimingLogger* timings); const OatHeader& GetOatHeader() const { return *oat_header_; diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc index fada66a9526..8b232700b08 100644 --- a/dex2oat/dex2oat.cc +++ b/dex2oat/dex2oat.cc @@ -280,6 +280,7 @@ class Dex2Oat { uint32_t image_file_location_oat_checksum = 0; uint32_t image_file_location_oat_data_begin = 0; if (!driver->IsImage()) { + TimingLogger::ScopedSplit split("Loading image checksum", &timings); gc::space::ImageSpace* image_space = Runtime::Current()->GetHeap()->GetImageSpace(); image_file_location_oat_checksum = image_space->GetImageHeader().GetOatChecksum(); image_file_location_oat_data_begin = @@ -294,8 +295,10 @@ class Dex2Oat { image_file_location_oat_checksum, image_file_location_oat_data_begin, image_file_location, - driver.get()); + driver.get(), + &timings); + TimingLogger::ScopedSplit split("Writing ELF", &timings); if (!driver->WriteElf(android_root, is_host, dex_files, oat_writer, oat_file)) { LOG(ERROR) << "Failed to write ELF file " << oat_file->GetPath(); return NULL; From cf58d4adf461eb9b8e84baa8019054c88cd8acc6 Mon Sep 17 00:00:00 2001 From: Hiroshi Yamauchi Date: Thu, 26 Sep 2013 14:21:22 -0700 Subject: [PATCH 0189/2402] A custom 'runs-of-slots' memory allocator. Bug: 9986565 Change-Id: I0eb73b9458752113f519483616536d219d5f798b --- compiler/image_test.cc | 4 +- runtime/Android.mk | 3 + runtime/debugger.cc | 17 +- runtime/gc/accounting/mod_union_table-inl.h | 2 +- runtime/gc/allocator/rosalloc.cc | 1630 +++++++++++++++++++ runtime/gc/allocator/rosalloc.h | 480 ++++++ runtime/gc/collector/garbage_collector.cc | 6 +- runtime/gc/collector/mark_sweep.cc | 16 +- runtime/gc/collector/semi_space.cc | 14 +- runtime/gc/collector/sticky_mark_sweep.cc | 2 +- runtime/gc/heap-inl.h | 21 +- runtime/gc/heap.cc | 71 +- runtime/gc/heap.h | 23 +- runtime/gc/space/dlmalloc_space-inl.h | 6 +- runtime/gc/space/dlmalloc_space.cc | 266 +-- runtime/gc/space/dlmalloc_space.h | 117 +- runtime/gc/space/large_object_space.h | 3 +- runtime/gc/space/malloc_space.cc | 243 +++ runtime/gc/space/malloc_space.h | 191 +++ runtime/gc/space/rosalloc_space-inl.h | 55 + runtime/gc/space/rosalloc_space.cc | 306 ++++ runtime/gc/space/rosalloc_space.h | 144 ++ runtime/gc/space/space-inl.h | 7 +- runtime/gc/space/space.h | 33 +- runtime/gc/space/space_test.cc | 80 +- runtime/locks.h | 3 + runtime/native/dalvik_system_VMDebug.cc | 12 +- runtime/runtime.cc | 5 + runtime/thread.cc | 3 + runtime/thread.h | 9 + 30 files changed, 3379 insertions(+), 393 deletions(-) create mode 100644 runtime/gc/allocator/rosalloc.cc create mode 100644 runtime/gc/allocator/rosalloc.h create mode 100644 runtime/gc/space/malloc_space.cc create mode 100644 runtime/gc/space/malloc_space.h create mode 100644 runtime/gc/space/rosalloc_space-inl.h create mode 100644 runtime/gc/space/rosalloc_space.cc create mode 100644 runtime/gc/space/rosalloc_space.h diff --git a/compiler/image_test.cc b/compiler/image_test.cc index e22e7028f6a..b9a87f1c68e 100644 --- a/compiler/image_test.cc +++ b/compiler/image_test.cc @@ -99,7 +99,7 @@ TEST_F(ImageTest, WriteRead) { gc::space::ContinuousSpace* space = heap->GetNonMovingSpace(); ASSERT_FALSE(space->IsImageSpace()); ASSERT_TRUE(space != NULL); - ASSERT_TRUE(space->IsDlMallocSpace()); + ASSERT_TRUE(space->IsMallocSpace()); ASSERT_GE(sizeof(image_header) + space->Size(), static_cast(file->GetLength())); } @@ -141,7 +141,7 @@ TEST_F(ImageTest, WriteRead) { gc::Heap* heap = Runtime::Current()->GetHeap(); ASSERT_TRUE(heap->HasImageSpace()); - ASSERT_TRUE(heap->GetNonMovingSpace()->IsDlMallocSpace()); + ASSERT_TRUE(heap->GetNonMovingSpace()->IsMallocSpace()); gc::space::ImageSpace* image_space = heap->GetImageSpace(); image_space->VerifyImageAllocations(); diff --git a/runtime/Android.mk b/runtime/Android.mk index 60683d0d527..2aa59d5f479 100644 --- a/runtime/Android.mk +++ b/runtime/Android.mk @@ -42,6 +42,7 @@ LIBART_COMMON_SRC_FILES := \ dex_instruction.cc \ elf_file.cc \ gc/allocator/dlmalloc.cc \ + gc/allocator/rosalloc.cc \ gc/accounting/card_table.cc \ gc/accounting/gc_allocator.cc \ gc/accounting/heap_bitmap.cc \ @@ -58,6 +59,8 @@ LIBART_COMMON_SRC_FILES := \ gc/space/dlmalloc_space.cc \ gc/space/image_space.cc \ gc/space/large_object_space.cc \ + gc/space/malloc_space.cc \ + gc/space/rosalloc_space.cc \ gc/space/space.cc \ hprof/hprof.cc \ image.cc \ diff --git a/runtime/debugger.cc b/runtime/debugger.cc index 3ef0a7fd811..4bc16fbf2c6 100644 --- a/runtime/debugger.cc +++ b/runtime/debugger.cc @@ -3418,6 +3418,14 @@ void Dbg::DdmSendHeapSegments(bool native) { JDWP::Set4BE(&heap_id[0], 1); // Heap id (bogus; we only have one heap). Dbg::DdmSendChunk(native ? CHUNK_TYPE("NHST") : CHUNK_TYPE("HPST"), sizeof(heap_id), heap_id); + Thread* self = Thread::Current(); + + // To allow the Walk/InspectAll() below to exclusively-lock the + // mutator lock, temporarily release the shared access to the + // mutator lock here by transitioning to the suspended state. + Locks::mutator_lock_->AssertSharedHeld(self); + self->TransitionFromRunnableToSuspended(kSuspended); + // Send a series of heap segment chunks. HeapChunkContext context((what == HPSG_WHAT_MERGED_OBJECTS), native); if (native) { @@ -3425,18 +3433,21 @@ void Dbg::DdmSendHeapSegments(bool native) { } else { gc::Heap* heap = Runtime::Current()->GetHeap(); const std::vector& spaces = heap->GetContinuousSpaces(); - Thread* self = Thread::Current(); ReaderMutexLock mu(self, *Locks::heap_bitmap_lock_); typedef std::vector::const_iterator It; for (It cur = spaces.begin(), end = spaces.end(); cur != end; ++cur) { - if ((*cur)->IsDlMallocSpace()) { - (*cur)->AsDlMallocSpace()->Walk(HeapChunkContext::HeapChunkCallback, &context); + if ((*cur)->IsMallocSpace()) { + (*cur)->AsMallocSpace()->Walk(HeapChunkContext::HeapChunkCallback, &context); } } // Walk the large objects, these are not in the AllocSpace. heap->GetLargeObjectsSpace()->Walk(HeapChunkContext::HeapChunkCallback, &context); } + // Shared-lock the mutator lock back. + self->TransitionFromSuspendedToRunnable(); + Locks::mutator_lock_->AssertSharedHeld(self); + // Finally, send a heap end chunk. Dbg::DdmSendChunk(native ? CHUNK_TYPE("NHEN") : CHUNK_TYPE("HPEN"), sizeof(heap_id), heap_id); } diff --git a/runtime/gc/accounting/mod_union_table-inl.h b/runtime/gc/accounting/mod_union_table-inl.h index fb425df78a4..19c67684127 100644 --- a/runtime/gc/accounting/mod_union_table-inl.h +++ b/runtime/gc/accounting/mod_union_table-inl.h @@ -37,7 +37,7 @@ class ModUnionTableToZygoteAllocspace : public ModUnionTableReferenceCache { typedef std::vector::const_iterator It; for (It it = spaces.begin(); it != spaces.end(); ++it) { if ((*it)->Contains(ref)) { - return (*it)->IsDlMallocSpace(); + return (*it)->IsMallocSpace(); } } // Assume it points to a large object. diff --git a/runtime/gc/allocator/rosalloc.cc b/runtime/gc/allocator/rosalloc.cc new file mode 100644 index 00000000000..9e658948952 --- /dev/null +++ b/runtime/gc/allocator/rosalloc.cc @@ -0,0 +1,1630 @@ +/* + * Copyright (C) 2013 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 "base/mutex-inl.h" +#include "thread.h" +#include "thread_list.h" +#include "rosalloc.h" + +#include +#include +#include + +namespace art { +namespace gc { +namespace allocator { + +// If true, log verbose details of operations. +static const bool kTraceRosAlloc = false; +// If true, check that the returned memory is actually zero. +static const bool kCheckZeroMemory = kIsDebugBuild; + +extern "C" void* art_heap_rosalloc_morecore(RosAlloc* rosalloc, intptr_t increment); + +size_t RosAlloc::bracketSizes[kNumOfSizeBrackets]; +size_t RosAlloc::numOfPages[kNumOfSizeBrackets]; +size_t RosAlloc::numOfSlots[kNumOfSizeBrackets]; +size_t RosAlloc::headerSizes[kNumOfSizeBrackets]; +size_t RosAlloc::bulkFreeBitMapOffsets[kNumOfSizeBrackets]; +size_t RosAlloc::threadLocalFreeBitMapOffsets[kNumOfSizeBrackets]; +bool RosAlloc::initialized_ = false; + +RosAlloc::RosAlloc(void* base, size_t capacity) + : base_(reinterpret_cast(base)), footprint_(capacity), + capacity_(capacity), + lock_("rosalloc global lock", kRosAllocGlobalLock), + bulk_free_lock_("rosalloc bulk free lock", kRosAllocBulkFreeLock) { + DCHECK(RoundUp(capacity, kPageSize) == capacity); + if (!initialized_) { + Initialize(); + } + VLOG(heap) << "RosAlloc base=" + << std::hex << (intptr_t)base_ << ", end=" + << std::hex << (intptr_t)(base_ + capacity_) + << ", capacity=" << std::dec << capacity_; + memset(current_runs_, 0, sizeof(current_runs_)); + for (size_t i = 0; i < kNumOfSizeBrackets; i++) { + size_bracket_locks_[i] = new Mutex("an rosalloc size bracket lock", + kRosAllocBracketLock); + } + size_t num_of_pages = capacity_ / kPageSize; + page_map_.resize(num_of_pages); + free_page_run_size_map_.resize(num_of_pages); + + FreePageRun* free_pages = reinterpret_cast(base_); + if (kIsDebugBuild) { + free_pages->magic_num_ = kMagicNumFree; + } + free_pages->SetByteSize(this, capacity_); + DCHECK_EQ(capacity_ % kPageSize, static_cast(0)); + free_pages->ReleasePages(this); + free_page_runs_.insert(free_pages); + if (kTraceRosAlloc) { + LOG(INFO) << "RosAlloc::RosAlloc() : Inserted run 0x" << std::hex + << reinterpret_cast(free_pages) + << " into free_page_runs_"; + } +} + +void* RosAlloc::AllocPages(Thread* self, size_t num_pages, byte page_map_type) { + lock_.AssertHeld(self); + DCHECK(page_map_type == kPageMapRun || page_map_type == kPageMapLargeObject); + FreePageRun* res = NULL; + size_t req_byte_size = num_pages * kPageSize; + // Find the lowest address free page run that's large enough. + for (auto it = free_page_runs_.begin(); it != free_page_runs_.end(); ) { + FreePageRun* fpr = *it; + DCHECK(fpr->IsFree()); + size_t fpr_byte_size = fpr->ByteSize(this); + DCHECK_EQ(fpr_byte_size % kPageSize, static_cast(0)); + if (req_byte_size <= fpr_byte_size) { + // Found one. + free_page_runs_.erase(it++); + if (kTraceRosAlloc) { + LOG(INFO) << "RosAlloc::AllocPages() : Erased run 0x" + << std::hex << reinterpret_cast(fpr) + << " from free_page_runs_"; + } + if (req_byte_size < fpr_byte_size) { + // Split. + FreePageRun* remainder = reinterpret_cast(reinterpret_cast(fpr) + req_byte_size); + if (kIsDebugBuild) { + remainder->magic_num_ = kMagicNumFree; + } + remainder->SetByteSize(this, fpr_byte_size - req_byte_size); + DCHECK_EQ(remainder->ByteSize(this) % kPageSize, static_cast(0)); + // Don't need to call madvise on remainder here. + free_page_runs_.insert(remainder); + if (kTraceRosAlloc) { + LOG(INFO) << "RosAlloc::AllocPages() : Inserted run 0x" << std::hex + << reinterpret_cast(remainder) + << " into free_page_runs_"; + } + fpr->SetByteSize(this, req_byte_size); + DCHECK_EQ(fpr->ByteSize(this) % kPageSize, static_cast(0)); + } + res = fpr; + break; + } else { + ++it; + } + } + + // Failed to allocate pages. Grow the footprint, if possible. + if (UNLIKELY(res == NULL && capacity_ > footprint_)) { + FreePageRun* last_free_page_run = NULL; + size_t last_free_page_run_size; + auto it = free_page_runs_.rbegin(); + if (it != free_page_runs_.rend() && (last_free_page_run = *it)->End(this) == base_ + footprint_) { + // There is a free page run at the end. + DCHECK(last_free_page_run->IsFree()); + DCHECK(page_map_[ToPageMapIndex(last_free_page_run)] == kPageMapEmpty); + last_free_page_run_size = last_free_page_run->ByteSize(this); + } else { + // There is no free page run at the end. + last_free_page_run_size = 0; + } + DCHECK_LT(last_free_page_run_size, req_byte_size); + if (capacity_ - footprint_ + last_free_page_run_size >= req_byte_size) { + // If we grow the heap, we can allocate it. + size_t increment = std::min(std::max(2 * MB, req_byte_size - last_free_page_run_size), + capacity_ - footprint_); + DCHECK_EQ(increment % kPageSize, static_cast(0)); + size_t new_footprint = footprint_ + increment; + size_t new_num_of_pages = new_footprint / kPageSize; + DCHECK_LT(page_map_.size(), new_num_of_pages); + DCHECK_LT(free_page_run_size_map_.size(), new_num_of_pages); + page_map_.resize(new_num_of_pages); + free_page_run_size_map_.resize(new_num_of_pages); + art_heap_rosalloc_morecore(this, increment); + if (last_free_page_run_size > 0) { + // There was a free page run at the end. Expand its size. + DCHECK_EQ(last_free_page_run_size, last_free_page_run->ByteSize(this)); + last_free_page_run->SetByteSize(this, last_free_page_run_size + increment); + DCHECK_EQ(last_free_page_run->ByteSize(this) % kPageSize, static_cast(0)); + DCHECK(last_free_page_run->End(this) == base_ + new_footprint); + } else { + // Otherwise, insert a new free page run at the end. + FreePageRun* new_free_page_run = reinterpret_cast(base_ + footprint_); + if (kIsDebugBuild) { + new_free_page_run->magic_num_ = kMagicNumFree; + } + new_free_page_run->SetByteSize(this, increment); + DCHECK_EQ(new_free_page_run->ByteSize(this) % kPageSize, static_cast(0)); + free_page_runs_.insert(new_free_page_run); + DCHECK(*free_page_runs_.rbegin() == new_free_page_run); + if (kTraceRosAlloc) { + LOG(INFO) << "RosAlloc::AlloPages() : Grew the heap by inserting run 0x" + << std::hex << reinterpret_cast(new_free_page_run) + << " into free_page_runs_"; + } + } + DCHECK_LE(footprint_ + increment, capacity_); + if (kTraceRosAlloc) { + LOG(INFO) << "RosAlloc::AllocPages() : increased the footprint from " + << footprint_ << " to " << new_footprint; + } + footprint_ = new_footprint; + + // And retry the last free page run. + it = free_page_runs_.rbegin(); + DCHECK(it != free_page_runs_.rend()); + FreePageRun* fpr = *it; + if (kIsDebugBuild && last_free_page_run_size > 0) { + DCHECK(last_free_page_run != NULL); + DCHECK_EQ(last_free_page_run, fpr); + } + size_t fpr_byte_size = fpr->ByteSize(this); + DCHECK_EQ(fpr_byte_size % kPageSize, static_cast(0)); + DCHECK_LE(req_byte_size, fpr_byte_size); + free_page_runs_.erase(fpr); + if (kTraceRosAlloc) { + LOG(INFO) << "RosAlloc::AllocPages() : Erased run 0x" << std::hex << reinterpret_cast(fpr) + << " from free_page_runs_"; + } + if (req_byte_size < fpr_byte_size) { + // Split if there's a remainder. + FreePageRun* remainder = reinterpret_cast(reinterpret_cast(fpr) + req_byte_size); + if (kIsDebugBuild) { + remainder->magic_num_ = kMagicNumFree; + } + remainder->SetByteSize(this, fpr_byte_size - req_byte_size); + DCHECK_EQ(remainder->ByteSize(this) % kPageSize, static_cast(0)); + free_page_runs_.insert(remainder); + if (kTraceRosAlloc) { + LOG(INFO) << "RosAlloc::AllocPages() : Inserted run 0x" << std::hex + << reinterpret_cast(remainder) + << " into free_page_runs_"; + } + fpr->SetByteSize(this, req_byte_size); + DCHECK_EQ(fpr->ByteSize(this) % kPageSize, static_cast(0)); + } + res = fpr; + } + } + if (LIKELY(res != NULL)) { + // Update the page map. + size_t page_map_idx = ToPageMapIndex(res); + for (size_t i = 0; i < num_pages; i++) { + DCHECK(page_map_[page_map_idx + i] == kPageMapEmpty); + } + switch (page_map_type) { + case kPageMapRun: + page_map_[page_map_idx] = kPageMapRun; + for (size_t i = 1; i < num_pages; i++) { + page_map_[page_map_idx + i] = kPageMapRunPart; + } + break; + case kPageMapLargeObject: + page_map_[page_map_idx] = kPageMapLargeObject; + for (size_t i = 1; i < num_pages; i++) { + page_map_[page_map_idx + i] = kPageMapLargeObjectPart; + } + break; + default: + LOG(FATAL) << "Unreachable - page map type: " << page_map_type; + break; + } + if (kIsDebugBuild) { + // Clear the first page which isn't madvised away in the debug + // build for the magic number. + memset(res, 0, kPageSize); + } + if (kTraceRosAlloc) { + LOG(INFO) << "RosAlloc::AllocPages() : 0x" << std::hex << reinterpret_cast(res) + << "-0x" << (reinterpret_cast(res) + num_pages * kPageSize) + << "(" << std::dec << (num_pages * kPageSize) << ")"; + } + return res; + } + + // Fail. + if (kTraceRosAlloc) { + LOG(INFO) << "RosAlloc::AllocPages() : NULL"; + } + return nullptr; +} + +void RosAlloc::FreePages(Thread* self, void* ptr) { + lock_.AssertHeld(self); + size_t pm_idx = ToPageMapIndex(ptr); + DCHECK(pm_idx < page_map_.size()); + byte pm_type = page_map_[pm_idx]; + DCHECK(pm_type == kPageMapRun || pm_type == kPageMapLargeObject); + byte pm_part_type; + switch (pm_type) { + case kPageMapRun: + pm_part_type = kPageMapRunPart; + break; + case kPageMapLargeObject: + pm_part_type = kPageMapLargeObjectPart; + break; + default: + pm_part_type = kPageMapEmpty; + LOG(FATAL) << "Unreachable - RosAlloc::FreePages() : " << "pm_idx=" << pm_idx << ", pm_type=" + << static_cast(pm_type) << ", ptr=" << std::hex + << reinterpret_cast(ptr); + return; + } + // Update the page map and count the number of pages. + size_t num_pages = 1; + page_map_[pm_idx] = kPageMapEmpty; + size_t idx = pm_idx + 1; + size_t end = page_map_.size(); + while (idx < end && page_map_[idx] == pm_part_type) { + page_map_[idx] = kPageMapEmpty; + num_pages++; + idx++; + } + + if (kTraceRosAlloc) { + LOG(INFO) << "RosAlloc::FreePages() : 0x" << std::hex << reinterpret_cast(ptr) + << "-0x" << (reinterpret_cast(ptr) + num_pages * kPageSize) + << "(" << std::dec << (num_pages * kPageSize) << ")"; + } + + // Turn it into a free run. + FreePageRun* fpr = reinterpret_cast(ptr); + if (kIsDebugBuild) { + fpr->magic_num_ = kMagicNumFree; + } + fpr->SetByteSize(this, num_pages * kPageSize); + DCHECK_EQ(fpr->ByteSize(this) % kPageSize, static_cast(0)); + + DCHECK(free_page_runs_.find(fpr) == free_page_runs_.end()); + if (!free_page_runs_.empty()) { + // Try to coalesce in the higher address direction. + if (kTraceRosAlloc) { + LOG(INFO) << "RosAlloc::FreePages() : trying to coalesce a free page run 0x" + << std::hex << reinterpret_cast(fpr) << " [" << std::dec << pm_idx << "] -0x" + << std::hex << reinterpret_cast(fpr->End(this)) << " [" << std::dec + << (fpr->End(this) == End() ? page_map_.size() : ToPageMapIndex(fpr->End(this))) << "]"; + } + auto higher_it = free_page_runs_.upper_bound(fpr); + if (higher_it != free_page_runs_.end()) { + for (auto it = higher_it; it != free_page_runs_.end(); ) { + FreePageRun* h = *it; + DCHECK_EQ(h->ByteSize(this) % kPageSize, static_cast(0)); + if (kTraceRosAlloc) { + LOG(INFO) << "RosAlloc::FreePages() : trying to coalesce with a higher free page run 0x" + << std::hex << reinterpret_cast(h) << " [" << std::dec << ToPageMapIndex(h) << "] -0x" + << std::hex << reinterpret_cast(h->End(this)) << " [" << std::dec + << (h->End(this) == End() ? page_map_.size() : ToPageMapIndex(h->End(this))) << "]"; + } + if (fpr->End(this) == h->Begin()) { + if (kTraceRosAlloc) { + LOG(INFO) << "Success"; + } + free_page_runs_.erase(it++); + if (kTraceRosAlloc) { + LOG(INFO) << "RosAlloc::FreePages() : (coalesce) Erased run 0x" << std::hex + << reinterpret_cast(h) + << " from free_page_runs_"; + } + fpr->SetByteSize(this, fpr->ByteSize(this) + h->ByteSize(this)); + DCHECK_EQ(fpr->ByteSize(this) % kPageSize, static_cast(0)); + } else { + // Not adjacent. Stop. + if (kTraceRosAlloc) { + LOG(INFO) << "Fail"; + } + break; + } + } + } + // Try to coalesce in the lower address direction. + auto lower_it = free_page_runs_.upper_bound(fpr); + if (lower_it != free_page_runs_.begin()) { + --lower_it; + for (auto it = lower_it; ; ) { + // We want to try to coalesce with the first element but + // there's no "<=" operator for the iterator. + bool to_exit_loop = it == free_page_runs_.begin(); + + FreePageRun* l = *it; + DCHECK_EQ(l->ByteSize(this) % kPageSize, static_cast(0)); + if (kTraceRosAlloc) { + LOG(INFO) << "RosAlloc::FreePages() : trying to coalesce with a lower free page run 0x" + << std::hex << reinterpret_cast(l) << " [" << std::dec << ToPageMapIndex(l) << "] -0x" + << std::hex << reinterpret_cast(l->End(this)) << " [" << std::dec + << (l->End(this) == End() ? page_map_.size() : ToPageMapIndex(l->End(this))) << "]"; + } + if (l->End(this) == fpr->Begin()) { + if (kTraceRosAlloc) { + LOG(INFO) << "Success"; + } + free_page_runs_.erase(it--); + if (kTraceRosAlloc) { + LOG(INFO) << "RosAlloc::FreePages() : (coalesce) Erased run 0x" << std::hex + << reinterpret_cast(l) + << " from free_page_runs_"; + } + l->SetByteSize(this, l->ByteSize(this) + fpr->ByteSize(this)); + DCHECK_EQ(l->ByteSize(this) % kPageSize, static_cast(0)); + fpr = l; + } else { + // Not adjacent. Stop. + if (kTraceRosAlloc) { + LOG(INFO) << "Fail"; + } + break; + } + if (to_exit_loop) { + break; + } + } + } + } + + // Insert it. + DCHECK_EQ(fpr->ByteSize(this) % kPageSize, static_cast(0)); + DCHECK(free_page_runs_.find(fpr) == free_page_runs_.end()); + fpr->ReleasePages(this); + free_page_runs_.insert(fpr); + DCHECK(free_page_runs_.find(fpr) != free_page_runs_.end()); + if (kTraceRosAlloc) { + LOG(INFO) << "RosAlloc::FreePages() : Inserted run 0x" << std::hex << reinterpret_cast(fpr) + << " into free_page_runs_"; + } +} + +void* RosAlloc::Alloc(Thread* self, size_t size, size_t* bytes_allocated) { + if (UNLIKELY(size > kLargeSizeThreshold)) { + size_t num_pages = RoundUp(size, kPageSize) / kPageSize; + void* r; + { + MutexLock mu(self, lock_); + r = AllocPages(self, num_pages, kPageMapLargeObject); + } + if (bytes_allocated != NULL) { + *bytes_allocated = num_pages * kPageSize; + } + if (kTraceRosAlloc) { + if (r != NULL) { + LOG(INFO) << "RosAlloc::Alloc() : (large obj) 0x" << std::hex << reinterpret_cast(r) + << "-0x" << (reinterpret_cast(r) + num_pages * kPageSize) + << "(" << std::dec << (num_pages * kPageSize) << ")"; + } else { + LOG(INFO) << "RosAlloc::Alloc() : (large obj) NULL"; + } + } + // Check if the returned memory is really all zero. + if (kCheckZeroMemory && r != NULL) { + byte* bytes = reinterpret_cast(r); + for (size_t i = 0; i < size; ++i) { + DCHECK_EQ(bytes[i], 0); + } + } + return r; + } + void* m = AllocFromRun(self, size, bytes_allocated); + // Check if the returned memory is really all zero. + if (kCheckZeroMemory && m != NULL) { + byte* bytes = reinterpret_cast(m); + for (size_t i = 0; i < size; ++i) { + DCHECK_EQ(bytes[i], 0); + } + } + return m; +} + +void RosAlloc::FreeInternal(Thread* self, void* ptr) { + DCHECK(base_ <= ptr && ptr < base_ + footprint_); + size_t pm_idx = RoundDownToPageMapIndex(ptr); + bool free_from_run = false; + Run* run = NULL; + { + MutexLock mu(self, lock_); + DCHECK(pm_idx < page_map_.size()); + byte page_map_entry = page_map_[pm_idx]; + if (kTraceRosAlloc) { + LOG(INFO) << "RosAlloc::FreeInternal() : " << std::hex << ptr << ", pm_idx=" << std::dec << pm_idx + << ", page_map_entry=" << static_cast(page_map_entry); + } + switch (page_map_[pm_idx]) { + case kPageMapEmpty: + LOG(FATAL) << "Unreachable - page map type: " << page_map_[pm_idx]; + return; + case kPageMapLargeObject: + FreePages(self, ptr); + return; + case kPageMapLargeObjectPart: + LOG(FATAL) << "Unreachable - page map type: " << page_map_[pm_idx]; + return; + case kPageMapRun: + case kPageMapRunPart: { + free_from_run = true; + size_t pi = pm_idx; + DCHECK(page_map_[pi] == kPageMapRun || page_map_[pi] == kPageMapRunPart); + // Find the beginning of the run. + while (page_map_[pi] != kPageMapRun) { + pi--; + DCHECK(pi < capacity_ / kPageSize); + } + DCHECK(page_map_[pi] == kPageMapRun); + run = reinterpret_cast(base_ + pi * kPageSize); + DCHECK(run->magic_num_ == kMagicNum); + break; + } + default: + LOG(FATAL) << "Unreachable - page map type: " << page_map_[pm_idx]; + return; + } + } + if (LIKELY(free_from_run)) { + DCHECK(run != NULL); + FreeFromRun(self, ptr, run); + } +} + +void RosAlloc::Free(Thread* self, void* ptr) { + ReaderMutexLock rmu(self, bulk_free_lock_); + FreeInternal(self, ptr); +} + +RosAlloc::Run* RosAlloc::RefillRun(Thread* self, size_t idx) { + Run* new_run; + size_t num_pages = numOfPages[idx]; + // Get the lowest address non-full run from the binary tree. + Run* temp = NULL; + std::set* bt = &non_full_runs_[idx]; + std::set::iterator found = bt->lower_bound(temp); + if (found != bt->end()) { + // If there's one, use it as the current run. + Run* non_full_run = *found; + DCHECK(non_full_run != NULL); + new_run = non_full_run; + DCHECK_EQ(new_run->is_thread_local_, 0); + bt->erase(found); + DCHECK_EQ(non_full_run->is_thread_local_, 0); + } else { + // If there's none, allocate a new run and use it as the + // current run. + { + MutexLock mu(self, lock_); + new_run = reinterpret_cast(AllocPages(self, num_pages, kPageMapRun)); + } + if (new_run == NULL) { + return NULL; + } + if (kIsDebugBuild) { + new_run->magic_num_ = kMagicNum; + } + new_run->size_bracket_idx_ = idx; + new_run->top_slot_idx_ = 0; + new_run->ClearBitMaps(); + new_run->to_be_bulk_freed_ = false; + } + return new_run; +} + +void* RosAlloc::AllocFromRun(Thread* self, size_t size, size_t* bytes_allocated) { + DCHECK(size <= kLargeSizeThreshold); + size_t bracket_size; + size_t idx = SizeToIndexAndBracketSize(size, &bracket_size); + DCHECK_EQ(idx, SizeToIndex(size)); + DCHECK_EQ(bracket_size, IndexToBracketSize(idx)); + DCHECK_EQ(bracket_size, bracketSizes[idx]); + DCHECK(size <= bracket_size); + DCHECK(size > 512 || bracket_size - size < 16); + + void* slot_addr; + + if (LIKELY(idx <= kMaxThreadLocalSizeBracketIdx)) { + // Use a thread-local run. + Run* thread_local_run = reinterpret_cast(self->rosalloc_runs_[idx]); + if (UNLIKELY(thread_local_run == NULL)) { + MutexLock mu(self, *size_bracket_locks_[idx]); + thread_local_run = RefillRun(self, idx); + if (UNLIKELY(thread_local_run == NULL)) { + return NULL; + } + DCHECK(non_full_runs_[idx].find(thread_local_run) == non_full_runs_[idx].end()); + DCHECK(full_runs_[idx].find(thread_local_run) == full_runs_[idx].end()); + thread_local_run->is_thread_local_ = 1; + self->rosalloc_runs_[idx] = thread_local_run; + DCHECK(!thread_local_run->IsFull()); + } + + DCHECK(thread_local_run != NULL); + DCHECK_NE(thread_local_run->is_thread_local_, 0); + slot_addr = thread_local_run->AllocSlot(); + + if (UNLIKELY(slot_addr == NULL)) { + // The run got full. Try to free slots. + DCHECK(thread_local_run->IsFull()); + MutexLock mu(self, *size_bracket_locks_[idx]); + bool is_all_free_after_merge; + if (thread_local_run->MergeThreadLocalFreeBitMapToAllocBitMap(&is_all_free_after_merge)) { + // Some slot got freed. Keep it. + DCHECK(!thread_local_run->IsFull()); + DCHECK_EQ(is_all_free_after_merge, thread_local_run->IsAllFree()); + if (is_all_free_after_merge) { + // Reinstate the bump index mode if it's all free. + DCHECK_EQ(thread_local_run->top_slot_idx_, numOfSlots[idx]); + thread_local_run->top_slot_idx_ = 0; + } + } else { + // No slots got freed. Try to refill the thread-local run. + DCHECK(thread_local_run->IsFull()); + self->rosalloc_runs_[idx] = NULL; + thread_local_run->is_thread_local_ = 0; + if (kIsDebugBuild) { + full_runs_[idx].insert(thread_local_run); + if (kTraceRosAlloc) { + LOG(INFO) << "RosAlloc::AllocFromRun() : Inserted run 0x" << std::hex + << reinterpret_cast(thread_local_run) + << " into full_runs_[" << std::dec << idx << "]"; + } + } + DCHECK(non_full_runs_[idx].find(thread_local_run) == non_full_runs_[idx].end()); + DCHECK(full_runs_[idx].find(thread_local_run) != full_runs_[idx].end()); + thread_local_run = RefillRun(self, idx); + if (UNLIKELY(thread_local_run == NULL)) { + return NULL; + } + DCHECK(non_full_runs_[idx].find(thread_local_run) == non_full_runs_[idx].end()); + DCHECK(full_runs_[idx].find(thread_local_run) == full_runs_[idx].end()); + thread_local_run->is_thread_local_ = 1; + self->rosalloc_runs_[idx] = thread_local_run; + DCHECK(!thread_local_run->IsFull()); + } + + DCHECK(thread_local_run != NULL); + DCHECK(!thread_local_run->IsFull()); + DCHECK_NE(thread_local_run->is_thread_local_, 0); + slot_addr = thread_local_run->AllocSlot(); + // Must succeed now with a new run. + DCHECK(slot_addr != NULL); + } + if (kTraceRosAlloc) { + LOG(INFO) << "RosAlloc::AllocFromRun() thread-local : 0x" << std::hex << reinterpret_cast(slot_addr) + << "-0x" << (reinterpret_cast(slot_addr) + bracket_size) + << "(" << std::dec << (bracket_size) << ")"; + } + } else { + // Use the (shared) current run. + MutexLock mu(self, *size_bracket_locks_[idx]); + Run* current_run = current_runs_[idx]; + if (UNLIKELY(current_run == NULL)) { + current_run = RefillRun(self, idx); + if (UNLIKELY(current_run == NULL)) { + return NULL; + } + DCHECK(non_full_runs_[idx].find(current_run) == non_full_runs_[idx].end()); + DCHECK(full_runs_[idx].find(current_run) == full_runs_[idx].end()); + current_run->is_thread_local_ = 0; + current_runs_[idx] = current_run; + DCHECK(!current_run->IsFull()); + } + DCHECK(current_run != NULL); + slot_addr = current_run->AllocSlot(); + if (UNLIKELY(slot_addr == NULL)) { + // The current run got full. Try to refill it. + DCHECK(current_run->IsFull()); + current_runs_[idx] = NULL; + if (kIsDebugBuild) { + // Insert it into full_runs and set the current run to NULL. + full_runs_[idx].insert(current_run); + if (kTraceRosAlloc) { + LOG(INFO) << "RosAlloc::AllocFromRun() : Inserted run 0x" << std::hex << reinterpret_cast(current_run) + << " into full_runs_[" << std::dec << idx << "]"; + } + } + DCHECK(non_full_runs_[idx].find(current_run) == non_full_runs_[idx].end()); + DCHECK(full_runs_[idx].find(current_run) != full_runs_[idx].end()); + current_run = RefillRun(self, idx); + if (UNLIKELY(current_run == NULL)) { + return NULL; + } + DCHECK(current_run != NULL); + DCHECK(non_full_runs_[idx].find(current_run) == non_full_runs_[idx].end()); + DCHECK(full_runs_[idx].find(current_run) == full_runs_[idx].end()); + current_run->is_thread_local_ = 0; + current_runs_[idx] = current_run; + DCHECK(!current_run->IsFull()); + slot_addr = current_run->AllocSlot(); + // Must succeed now with a new run. + DCHECK(slot_addr != NULL); + } + if (kTraceRosAlloc) { + LOG(INFO) << "RosAlloc::AllocFromRun() : 0x" << std::hex << reinterpret_cast(slot_addr) + << "-0x" << (reinterpret_cast(slot_addr) + bracket_size) + << "(" << std::dec << (bracket_size) << ")"; + } + } + if (LIKELY(bytes_allocated != NULL)) { + *bytes_allocated = bracket_size; + } + memset(slot_addr, 0, size); + return slot_addr; +} + +void RosAlloc::FreeFromRun(Thread* self, void* ptr, Run* run) { + DCHECK(run->magic_num_ == kMagicNum); + DCHECK(run < ptr && ptr < run->End()); + size_t idx = run->size_bracket_idx_; + MutexLock mu(self, *size_bracket_locks_[idx]); + bool run_was_full = false; + if (kIsDebugBuild) { + run_was_full = run->IsFull(); + } + if (kTraceRosAlloc) { + LOG(INFO) << "RosAlloc::FreeFromRun() : 0x" << std::hex << reinterpret_cast(ptr); + } + if (LIKELY(run->is_thread_local_ != 0)) { + // It's a thread-local run. Just mark the thread-local free bit map and return. + DCHECK_LE(run->size_bracket_idx_, kMaxThreadLocalSizeBracketIdx); + DCHECK(non_full_runs_[idx].find(run) == non_full_runs_[idx].end()); + DCHECK(full_runs_[idx].find(run) == full_runs_[idx].end()); + run->MarkThreadLocalFreeBitMap(ptr); + if (kTraceRosAlloc) { + LOG(INFO) << "RosAlloc::FreeFromRun() : Freed a slot in a thread local run 0x" << std::hex + << reinterpret_cast(run); + } + // A thread local run will be kept as a thread local even if it's become all free. + return; + } + // Free the slot in the run. + run->FreeSlot(ptr); + std::set* non_full_runs = &non_full_runs_[idx]; + if (run->IsAllFree()) { + // It has just become completely free. Free the pages of this run. + std::set::iterator pos = non_full_runs->find(run); + if (pos != non_full_runs->end()) { + non_full_runs->erase(pos); + if (kTraceRosAlloc) { + LOG(INFO) << "RosAlloc::FreeFromRun() : Erased run 0x" << std::hex + << reinterpret_cast(run) << " from non_full_runs_"; + } + } + if (run == current_runs_[idx]) { + current_runs_[idx] = NULL; + } + DCHECK(non_full_runs_[idx].find(run) == non_full_runs_[idx].end()); + DCHECK(full_runs_[idx].find(run) == full_runs_[idx].end()); + { + MutexLock mu(self, lock_); + FreePages(self, run); + } + } else { + // It is not completely free. If it wasn't the current run or + // already in the non-full run set (i.e., it was full) insert it + // into the non-full run set. + if (run != current_runs_[idx]) { + hash_set* full_runs = + kIsDebugBuild ? &full_runs_[idx] : NULL; + std::set::iterator pos = non_full_runs->find(run); + if (pos == non_full_runs->end()) { + DCHECK(run_was_full); + DCHECK(full_runs->find(run) != full_runs->end()); + if (kIsDebugBuild) { + full_runs->erase(run); + if (kTraceRosAlloc) { + LOG(INFO) << "RosAlloc::FreeFromRun() : Erased run 0x" << std::hex + << reinterpret_cast(run) << " from full_runs_"; + } + } + non_full_runs->insert(run); + DCHECK(!run->IsFull()); + if (kTraceRosAlloc) { + LOG(INFO) << "RosAlloc::FreeFromRun() : Inserted run 0x" << std::hex + << reinterpret_cast(run) + << " into non_full_runs_[" << std::dec << idx << "]"; + } + } + } + } +} + +void RosAlloc::Run::Dump() { + size_t idx = size_bracket_idx_; + size_t num_slots = numOfSlots[idx]; + size_t num_vec = RoundUp(num_slots, 32) / 32; + std::string bit_map_str; + for (size_t v = 0; v < num_vec; v++) { + uint32_t vec = alloc_bit_map_[v]; + if (v != num_vec - 1) { + bit_map_str.append(StringPrintf("%x-", vec)); + } else { + bit_map_str.append(StringPrintf("%x", vec)); + } + } + LOG(INFO) << "Run : " << std::hex << reinterpret_cast(this) + << std::dec << ", idx=" << idx << ", bit_map=" << bit_map_str; +} + +void* RosAlloc::Run::AllocSlot() { + size_t idx = size_bracket_idx_; + size_t num_slots = numOfSlots[idx]; + DCHECK_LE(top_slot_idx_, num_slots); + if (LIKELY(top_slot_idx_ < num_slots)) { + // If it's in bump index mode, grab the top slot and increment the top index. + size_t slot_idx = top_slot_idx_; + byte* slot_addr = reinterpret_cast(this) + headerSizes[idx] + slot_idx * bracketSizes[idx]; + if (kTraceRosAlloc) { + LOG(INFO) << "RosAlloc::Run::AllocSlot() : 0x" << std::hex << reinterpret_cast(slot_addr) + << ", bracket_size=" << std::dec << bracketSizes[idx] << ", slot_idx=" << slot_idx; + } + top_slot_idx_++; + size_t vec_idx = slot_idx / 32; + size_t vec_off = slot_idx % 32; + uint32_t* vec = &alloc_bit_map_[vec_idx]; + DCHECK_EQ((*vec & (1 << vec_off)), static_cast(0)); + *vec |= 1 << vec_off; + DCHECK_NE((*vec & (1 << vec_off)), static_cast(0)); + return slot_addr; + } + // Not in bump index mode. Search the alloc bit map for an empty slot. + size_t num_vec = RoundUp(num_slots, 32) / 32; + size_t slot_idx = 0; + bool found_slot = false; + for (size_t v = 0; v < num_vec; v++) { + uint32_t *vecp = &alloc_bit_map_[v]; + uint32_t ffz1 = __builtin_ffs(~*vecp); + uint32_t ffz; + // TODO: Use LIKELY or UNLIKELY here? + if (LIKELY(ffz1 > 0 && (ffz = ffz1 - 1) + v * 32 < num_slots)) { + // Found an empty slot. Set the bit. + DCHECK_EQ((*vecp & (1 << ffz)), static_cast(0)); + *vecp |= (1 << ffz); + DCHECK_NE((*vecp & (1 << ffz)), static_cast(0)); + slot_idx = ffz + v * 32; + found_slot = true; + break; + } + } + if (LIKELY(found_slot)) { + byte* slot_addr = reinterpret_cast(this) + headerSizes[idx] + slot_idx * bracketSizes[idx]; + if (kTraceRosAlloc) { + LOG(INFO) << "RosAlloc::Run::AllocSlot() : 0x" << std::hex << reinterpret_cast(slot_addr) + << ", bracket_size=" << std::dec << bracketSizes[idx] << ", slot_idx=" << slot_idx; + } + return slot_addr; + } + return NULL; +} + +inline void RosAlloc::Run::FreeSlot(void* ptr) { + DCHECK_EQ(is_thread_local_, 0); + byte idx = size_bracket_idx_; + size_t offset_from_slot_base = reinterpret_cast(ptr) + - (reinterpret_cast(this) + headerSizes[idx]); + DCHECK_EQ(offset_from_slot_base % bracketSizes[idx], static_cast(0)); + size_t slot_idx = offset_from_slot_base / bracketSizes[idx]; + DCHECK(slot_idx < numOfSlots[idx]); + size_t vec_idx = slot_idx / 32; + if (kIsDebugBuild) { + size_t num_vec = RoundUp(numOfSlots[idx], 32) / 32; + DCHECK(vec_idx < num_vec); + } + size_t vec_off = slot_idx % 32; + uint32_t* vec = &alloc_bit_map_[vec_idx]; + DCHECK_NE((*vec & (1 << vec_off)), static_cast(0)); + *vec &= ~(1 << vec_off); + DCHECK_EQ((*vec & (1 << vec_off)), static_cast(0)); + if (kTraceRosAlloc) { + LOG(INFO) << "RosAlloc::Run::FreeSlot() : 0x" << std::hex << reinterpret_cast(ptr) + << ", bracket_size=" << std::dec << bracketSizes[idx] << ", slot_idx=" << slot_idx; + } +} + +inline bool RosAlloc::Run::MergeThreadLocalFreeBitMapToAllocBitMap(bool* is_all_free_after_out) { + DCHECK_NE(is_thread_local_, 0); + // Free slots in the alloc bit map based on the thread local free bit map. + byte idx = size_bracket_idx_; + size_t num_slots = numOfSlots[idx]; + size_t num_vec = RoundUp(num_slots, 32) / 32; + bool changed = false; + uint32_t* vecp = &alloc_bit_map_[0]; + uint32_t* tl_free_vecp = &thread_local_free_bit_map()[0]; + bool is_all_free_after = true; + for (size_t v = 0; v < num_vec; v++, vecp++, tl_free_vecp++) { + uint32_t tl_free_vec = *tl_free_vecp; + uint32_t vec_before = *vecp; + uint32_t vec_after; + if (tl_free_vec != 0) { + vec_after = vec_before & ~tl_free_vec; + *vecp = vec_after; + changed = true; + *tl_free_vecp = 0; // clear the thread local free bit map. + } else { + vec_after = vec_before; + } + if (vec_after != 0) { + is_all_free_after = false; + } + DCHECK_EQ(*tl_free_vecp, static_cast(0)); + } + *is_all_free_after_out = is_all_free_after; + // Return true if there was at least a bit set in the thread-local + // free bit map and at least a bit in the alloc bit map changed. + return changed; +} + +inline void RosAlloc::Run::MergeBulkFreeBitMapIntoAllocBitMap() { + DCHECK_EQ(is_thread_local_, 0); + // Free slots in the alloc bit map based on the bulk free bit map. + byte idx = size_bracket_idx_; + size_t num_slots = numOfSlots[idx]; + size_t num_vec = RoundUp(num_slots, 32) / 32; + uint32_t* vecp = &alloc_bit_map_[0]; + uint32_t* free_vecp = &bulk_free_bit_map()[0]; + for (size_t v = 0; v < num_vec; v++, vecp++, free_vecp++) { + uint32_t free_vec = *free_vecp; + if (free_vec != 0) { + *vecp &= ~free_vec; + *free_vecp = 0; // clear the bulk free bit map. + } + DCHECK_EQ(*free_vecp, static_cast(0)); + } +} + +inline void RosAlloc::Run::UnionBulkFreeBitMapToThreadLocalFreeBitMap() { + DCHECK_NE(is_thread_local_, 0); + // Union the thread local bit map with the bulk free bit map. + byte idx = size_bracket_idx_; + size_t num_slots = numOfSlots[idx]; + size_t num_vec = RoundUp(num_slots, 32) / 32; + uint32_t* to_vecp = &thread_local_free_bit_map()[0]; + uint32_t* from_vecp = &bulk_free_bit_map()[0]; + for (size_t v = 0; v < num_vec; v++, to_vecp++, from_vecp++) { + uint32_t from_vec = *from_vecp; + if (from_vec != 0) { + *to_vecp |= from_vec; + *from_vecp = 0; // clear the from free bit map. + } + DCHECK_EQ(*from_vecp, static_cast(0)); + } +} + +inline void RosAlloc::Run::MarkThreadLocalFreeBitMap(void* ptr) { + DCHECK_NE(is_thread_local_, 0); + MarkFreeBitMapShared(ptr, thread_local_free_bit_map(), "MarkThreadLocalFreeBitMap"); +} + +inline void RosAlloc::Run::MarkBulkFreeBitMap(void* ptr) { + MarkFreeBitMapShared(ptr, bulk_free_bit_map(), "MarkFreeBitMap"); +} + +inline void RosAlloc::Run::MarkFreeBitMapShared(void* ptr, uint32_t* free_bit_map_base, + const char* caller_name) { + byte idx = size_bracket_idx_; + size_t offset_from_slot_base = reinterpret_cast(ptr) + - (reinterpret_cast(this) + headerSizes[idx]); + DCHECK_EQ(offset_from_slot_base % bracketSizes[idx], static_cast(0)); + size_t slot_idx = offset_from_slot_base / bracketSizes[idx]; + DCHECK(slot_idx < numOfSlots[idx]); + size_t vec_idx = slot_idx / 32; + if (kIsDebugBuild) { + size_t num_vec = RoundUp(numOfSlots[idx], 32) / 32; + DCHECK(vec_idx < num_vec); + } + size_t vec_off = slot_idx % 32; + uint32_t* vec = &free_bit_map_base[vec_idx]; + DCHECK_EQ((*vec & (1 << vec_off)), static_cast(0)); + *vec |= 1 << vec_off; + DCHECK_NE((*vec & (1 << vec_off)), static_cast(0)); + if (kTraceRosAlloc) { + LOG(INFO) << "RosAlloc::Run::" << caller_name << "() : 0x" << std::hex + << reinterpret_cast(ptr) + << ", bracket_size=" << std::dec << bracketSizes[idx] << ", slot_idx=" << slot_idx; + } +} + +inline bool RosAlloc::Run::IsAllFree() { + byte idx = size_bracket_idx_; + size_t num_slots = numOfSlots[idx]; + size_t num_vec = RoundUp(num_slots, 32) / 32; + for (size_t v = 0; v < num_vec; v++) { + uint32_t vec = alloc_bit_map_[v]; + if (vec != 0) { + return false; + } + } + return true; +} + +inline bool RosAlloc::Run::IsFull() { + byte idx = size_bracket_idx_; + size_t num_slots = numOfSlots[idx]; + size_t num_vec = RoundUp(num_slots, 32) / 32; + size_t slots = 0; + for (size_t v = 0; v < num_vec; v++, slots += 32) { + DCHECK(num_slots >= slots); + uint32_t vec = alloc_bit_map_[v]; + uint32_t mask = (num_slots - slots >= 32) ? static_cast(-1) + : (1 << (num_slots - slots)) - 1; + DCHECK(num_slots - slots >= 32 ? mask == static_cast(-1) : true); + if (vec != mask) { + return false; + } + } + return true; +} + +inline void RosAlloc::Run::ClearBitMaps() { + byte idx = size_bracket_idx_; + size_t num_slots = numOfSlots[idx]; + size_t num_vec = RoundUp(num_slots, 32) / 32; + memset(alloc_bit_map_, 0, sizeof(uint32_t) * num_vec * 3); +} + +void RosAlloc::Run::InspectAllSlots(void (*handler)(void* start, void* end, size_t used_bytes, void* callback_arg), + void* arg) { + size_t idx = size_bracket_idx_; + byte* slot_base = reinterpret_cast(this) + headerSizes[idx]; + size_t num_slots = numOfSlots[idx]; + size_t bracket_size = IndexToBracketSize(idx); + DCHECK_EQ(slot_base + num_slots * bracket_size, reinterpret_cast(this) + numOfPages[idx] * kPageSize); + size_t num_vec = RoundUp(num_slots, 32) / 32; + size_t slots = 0; + for (size_t v = 0; v < num_vec; v++, slots += 32) { + DCHECK(num_slots >= slots); + uint32_t vec = alloc_bit_map_[v]; + size_t end = std::min(num_slots - slots, static_cast(32)); + for (size_t i = 0; i < end; ++i) { + bool is_allocated = ((vec >> i) & 0x1) != 0; + byte* slot_addr = slot_base + (slots + i) * bracket_size; + if (is_allocated) { + handler(slot_addr, slot_addr + bracket_size, bracket_size, arg); + } else { + handler(slot_addr, slot_addr + bracket_size, 0, arg); + } + } + } +} + +void RosAlloc::BulkFree(Thread* self, void** ptrs, size_t num_ptrs) { + if (false) { + // Used only to test Free() as GC uses only BulkFree(). + for (size_t i = 0; i < num_ptrs; ++i) { + FreeInternal(self, ptrs[i]); + } + return; + } + + WriterMutexLock wmu(self, bulk_free_lock_); + + // First mark slots to free in the bulk free bit map without locking the + // size bracket locks. On host, hash_set is faster than vector + flag. +#ifdef HAVE_ANDROID_OS + std::vector runs; +#else + hash_set runs; +#endif + { + for (size_t i = 0; i < num_ptrs; i++) { + void* ptr = ptrs[i]; + ptrs[i] = NULL; + DCHECK(base_ <= ptr && ptr < base_ + footprint_); + size_t pm_idx = RoundDownToPageMapIndex(ptr); + bool free_from_run = false; + Run* run = NULL; + { + MutexLock mu(self, lock_); + DCHECK(pm_idx < page_map_.size()); + byte page_map_entry = page_map_[pm_idx]; + if (kTraceRosAlloc) { + LOG(INFO) << "RosAlloc::BulkFree() : " << std::hex << ptr << ", pm_idx=" + << std::dec << pm_idx + << ", page_map_entry=" << static_cast(page_map_entry); + } + if (LIKELY(page_map_entry == kPageMapRun)) { + free_from_run = true; + run = reinterpret_cast(base_ + pm_idx * kPageSize); + DCHECK(run->magic_num_ == kMagicNum); + } else if (LIKELY(page_map_entry == kPageMapRunPart)) { + free_from_run = true; + size_t pi = pm_idx; + DCHECK(page_map_[pi] == kPageMapRun || page_map_[pi] == kPageMapRunPart); + // Find the beginning of the run. + while (page_map_[pi] != kPageMapRun) { + pi--; + DCHECK(pi < capacity_ / kPageSize); + } + DCHECK(page_map_[pi] == kPageMapRun); + run = reinterpret_cast(base_ + pi * kPageSize); + DCHECK(run->magic_num_ == kMagicNum); + } else if (page_map_entry == kPageMapLargeObject) { + FreePages(self, ptr); + } else { + LOG(FATAL) << "Unreachable - page map type: " << page_map_entry; + } + } + if (LIKELY(free_from_run)) { + DCHECK(run != NULL); + // Set the bit in the bulk free bit map. + run->MarkBulkFreeBitMap(ptr); +#ifdef HAVE_ANDROID_OS + if (!run->to_be_bulk_freed_) { + run->to_be_bulk_freed_ = true; + runs.push_back(run); + } +#else + runs.insert(run); +#endif + } + } + } + + // Now, iterate over the affected runs and update the alloc bit map + // based on the bulk free bit map (for non-thread-local runs) and + // union the bulk free bit map into the thread-local free bit map + // (for thread-local runs.) +#ifdef HAVE_ANDROID_OS + typedef std::vector::iterator It; +#else + typedef hash_set::iterator It; +#endif + for (It it = runs.begin(); it != runs.end(); ++it) { + Run* run = *it; +#ifdef HAVE_ANDROID_OS + DCHECK(run->to_be_bulk_freed_); + run->to_be_bulk_freed_ = false; +#endif + size_t idx = run->size_bracket_idx_; + MutexLock mu(self, *size_bracket_locks_[idx]); + if (run->is_thread_local_ != 0) { + DCHECK_LE(run->size_bracket_idx_, kMaxThreadLocalSizeBracketIdx); + DCHECK(non_full_runs_[idx].find(run) == non_full_runs_[idx].end()); + DCHECK(full_runs_[idx].find(run) == full_runs_[idx].end()); + run->UnionBulkFreeBitMapToThreadLocalFreeBitMap(); + if (kTraceRosAlloc) { + LOG(INFO) << "RosAlloc::BulkFree() : Freed slot(s) in a thread local run 0x" + << std::hex << reinterpret_cast(run); + } + DCHECK_NE(run->is_thread_local_, 0); + // A thread local run will be kept as a thread local even if + // it's become all free. + } else { + bool run_was_full = run->IsFull(); + run->MergeBulkFreeBitMapIntoAllocBitMap(); + if (kTraceRosAlloc) { + LOG(INFO) << "RosAlloc::BulkFree() : Freed slot(s) in a run 0x" << std::hex + << reinterpret_cast(run); + } + // Check if the run should be moved to non_full_runs_ or + // free_page_runs_. + std::set* non_full_runs = &non_full_runs_[idx]; + hash_set* full_runs = + kIsDebugBuild ? &full_runs_[idx] : NULL; + if (run->IsAllFree()) { + // It has just become completely free. Free the pages of the + // run. + bool run_was_current = run == current_runs_[idx]; + if (run_was_current) { + DCHECK(full_runs->find(run) == full_runs->end()); + DCHECK(non_full_runs->find(run) == non_full_runs->end()); + // If it was a current run, reuse it. + } else if (run_was_full) { + // If it was full, remove it from the full run set (debug + // only.) + if (kIsDebugBuild) { + hash_set::iterator pos = full_runs->find(run); + DCHECK(pos != full_runs->end()); + full_runs->erase(pos); + if (kTraceRosAlloc) { + LOG(INFO) << "RosAlloc::BulkFree() : Erased run 0x" << std::hex + << reinterpret_cast(run) + << " from full_runs_"; + } + DCHECK(full_runs->find(run) == full_runs->end()); + } + } else { + // If it was in a non full run set, remove it from the set. + DCHECK(full_runs->find(run) == full_runs->end()); + DCHECK(non_full_runs->find(run) != non_full_runs->end()); + non_full_runs->erase(run); + if (kTraceRosAlloc) { + LOG(INFO) << "RosAlloc::BulkFree() : Erased run 0x" << std::hex + << reinterpret_cast(run) + << " from non_full_runs_"; + } + DCHECK(non_full_runs->find(run) == non_full_runs->end()); + } + if (!run_was_current) { + MutexLock mu(self, lock_); + FreePages(self, run); + } + } else { + // It is not completely free. If it wasn't the current run or + // already in the non-full run set (i.e., it was full) insert + // it into the non-full run set. + if (run == current_runs_[idx]) { + DCHECK(non_full_runs->find(run) == non_full_runs->end()); + DCHECK(full_runs->find(run) == full_runs->end()); + // If it was a current run, keep it. + } else if (run_was_full) { + // If it was full, remove it from the full run set (debug + // only) and insert into the non-full run set. + DCHECK(full_runs->find(run) != full_runs->end()); + DCHECK(non_full_runs->find(run) == non_full_runs->end()); + if (kIsDebugBuild) { + full_runs->erase(run); + if (kTraceRosAlloc) { + LOG(INFO) << "RosAlloc::BulkFree() : Erased run 0x" << std::hex + << reinterpret_cast(run) + << " from full_runs_"; + } + } + non_full_runs->insert(run); + if (kTraceRosAlloc) { + LOG(INFO) << "RosAlloc::BulkFree() : Inserted run 0x" << std::hex + << reinterpret_cast(run) + << " into non_full_runs_[" << std::dec << idx; + } + } else { + // If it was not full, so leave it in the non full run set. + DCHECK(full_runs->find(run) == full_runs->end()); + DCHECK(non_full_runs->find(run) != non_full_runs->end()); + } + } + } + } +} + +void RosAlloc::DumpPageMap(Thread* self) { + MutexLock mu(self, lock_); + size_t end = page_map_.size(); + FreePageRun* curr_fpr = NULL; + size_t curr_fpr_size = 0; + size_t remaining_curr_fpr_size = 0; + size_t num_running_empty_pages = 0; + for (size_t i = 0; i < end; ++i) { + byte pm = page_map_[i]; + switch (pm) { + case kPageMapEmpty: { + FreePageRun* fpr = reinterpret_cast(base_ + i * kPageSize); + if (free_page_runs_.find(fpr) != free_page_runs_.end()) { + // Encountered a fresh free page run. + DCHECK_EQ(remaining_curr_fpr_size, static_cast(0)); + DCHECK(fpr->IsFree()); + DCHECK(curr_fpr == NULL); + DCHECK_EQ(curr_fpr_size, static_cast(0)); + curr_fpr = fpr; + curr_fpr_size = fpr->ByteSize(this); + DCHECK_EQ(curr_fpr_size % kPageSize, static_cast(0)); + remaining_curr_fpr_size = curr_fpr_size - kPageSize; + LOG(INFO) << "[" << i << "]=Empty (FPR start)" + << " fpr_size=" << curr_fpr_size + << " remaining_fpr_size=" << remaining_curr_fpr_size; + if (remaining_curr_fpr_size == 0) { + // Reset at the end of the current free page run. + curr_fpr = NULL; + curr_fpr_size = 0; + } + LOG(INFO) << "curr_fpr=0x" << std::hex << reinterpret_cast(curr_fpr); + DCHECK_EQ(num_running_empty_pages, static_cast(0)); + } else { + // Still part of the current free page run. + DCHECK_NE(num_running_empty_pages, static_cast(0)); + DCHECK(curr_fpr != NULL && curr_fpr_size > 0 && remaining_curr_fpr_size > 0); + DCHECK_EQ(remaining_curr_fpr_size % kPageSize, static_cast(0)); + DCHECK_GE(remaining_curr_fpr_size, static_cast(kPageSize)); + remaining_curr_fpr_size -= kPageSize; + LOG(INFO) << "[" << i << "]=Empty (FPR part)" + << " remaining_fpr_size=" << remaining_curr_fpr_size; + if (remaining_curr_fpr_size == 0) { + // Reset at the end of the current free page run. + curr_fpr = NULL; + curr_fpr_size = 0; + } + } + num_running_empty_pages++; + break; + } + case kPageMapLargeObject: { + DCHECK_EQ(remaining_curr_fpr_size, static_cast(0)); + num_running_empty_pages = 0; + LOG(INFO) << "[" << i << "]=Large (start)"; + break; + } + case kPageMapLargeObjectPart: + DCHECK_EQ(remaining_curr_fpr_size, static_cast(0)); + num_running_empty_pages = 0; + LOG(INFO) << "[" << i << "]=Large (part)"; + break; + case kPageMapRun: { + DCHECK_EQ(remaining_curr_fpr_size, static_cast(0)); + num_running_empty_pages = 0; + Run* run = reinterpret_cast(base_ + i * kPageSize); + size_t idx = run->size_bracket_idx_; + LOG(INFO) << "[" << i << "]=Run (start)" + << " idx=" << idx + << " numOfPages=" << numOfPages[idx] + << " thread_local=" << static_cast(run->is_thread_local_) + << " is_all_free=" << (run->IsAllFree() ? 1 : 0); + break; + } + case kPageMapRunPart: + DCHECK_EQ(remaining_curr_fpr_size, static_cast(0)); + num_running_empty_pages = 0; + LOG(INFO) << "[" << i << "]=Run (part)"; + break; + default: + LOG(FATAL) << "Unreachable - page map type: " << pm; + break; + } + } +} + +size_t RosAlloc::UsableSize(void* ptr) { + DCHECK(base_ <= ptr && ptr < base_ + footprint_); + size_t pm_idx = RoundDownToPageMapIndex(ptr); + MutexLock mu(Thread::Current(), lock_); + switch (page_map_[pm_idx]) { + case kPageMapEmpty: + LOG(FATAL) << "Unreachable - RosAlloc::UsableSize(): pm_idx=" << pm_idx << ", ptr=" << std::hex + << reinterpret_cast(ptr); + break; + case kPageMapLargeObject: { + size_t num_pages = 1; + size_t idx = pm_idx + 1; + size_t end = page_map_.size(); + while (idx < end && page_map_[idx] == kPageMapLargeObjectPart) { + num_pages++; + idx++; + } + return num_pages * kPageSize; + } + case kPageMapLargeObjectPart: + LOG(FATAL) << "Unreachable - RosAlloc::UsableSize(): pm_idx=" << pm_idx << ", ptr=" << std::hex + << reinterpret_cast(ptr); + break; + case kPageMapRun: + case kPageMapRunPart: { + // Find the beginning of the run. + while (page_map_[pm_idx] != kPageMapRun) { + pm_idx--; + DCHECK(pm_idx < capacity_ / kPageSize); + } + DCHECK(page_map_[pm_idx] == kPageMapRun); + Run* run = reinterpret_cast(base_ + pm_idx * kPageSize); + DCHECK(run->magic_num_ == kMagicNum); + size_t idx = run->size_bracket_idx_; + size_t offset_from_slot_base = reinterpret_cast(ptr) + - (reinterpret_cast(run) + headerSizes[idx]); + DCHECK_EQ(offset_from_slot_base % bracketSizes[idx], static_cast(0)); + return IndexToBracketSize(idx); + } + default: + LOG(FATAL) << "Unreachable - page map type: " << page_map_[pm_idx]; + break; + } + return 0; +} + +bool RosAlloc::Trim() { + MutexLock mu(Thread::Current(), lock_); + FreePageRun* last_free_page_run; + DCHECK_EQ(footprint_ % kPageSize, static_cast(0)); + auto it = free_page_runs_.rbegin(); + if (it != free_page_runs_.rend() && (last_free_page_run = *it)->End(this) == base_ + footprint_) { + // Remove the last free page run, if any. + DCHECK(last_free_page_run->IsFree()); + DCHECK(page_map_[ToPageMapIndex(last_free_page_run)] == kPageMapEmpty); + DCHECK_EQ(last_free_page_run->ByteSize(this) % kPageSize, static_cast(0)); + DCHECK_EQ(last_free_page_run->End(this), base_ + footprint_); + free_page_runs_.erase(last_free_page_run); + size_t decrement = last_free_page_run->ByteSize(this); + size_t new_footprint = footprint_ - decrement; + DCHECK_EQ(new_footprint % kPageSize, static_cast(0)); + size_t new_num_of_pages = new_footprint / kPageSize; + DCHECK_GE(page_map_.size(), new_num_of_pages); + page_map_.resize(new_num_of_pages); + DCHECK_EQ(page_map_.size(), new_num_of_pages); + free_page_run_size_map_.resize(new_num_of_pages); + DCHECK_EQ(free_page_run_size_map_.size(), new_num_of_pages); + art_heap_rosalloc_morecore(this, -(static_cast(decrement))); + if (kTraceRosAlloc) { + LOG(INFO) << "RosAlloc::Trim() : decreased the footprint from " + << footprint_ << " to " << new_footprint; + } + DCHECK_LT(new_footprint, footprint_); + DCHECK_LT(new_footprint, capacity_); + footprint_ = new_footprint; + return true; + } + return false; +} + +void RosAlloc::InspectAll(void (*handler)(void* start, void* end, size_t used_bytes, void* callback_arg), + void* arg) { + // Note: no need to use this to release pages as we already do so in FreePages(). + if (handler == NULL) { + return; + } + MutexLock mu(Thread::Current(), lock_); + size_t pm_end = page_map_.size(); + size_t i = 0; + while (i < pm_end) { + byte pm = page_map_[i]; + switch (pm) { + case kPageMapEmpty: { + // The start of a free page run. + FreePageRun* fpr = reinterpret_cast(base_ + i * kPageSize); + DCHECK(free_page_runs_.find(fpr) != free_page_runs_.end()); + size_t fpr_size = fpr->ByteSize(this); + DCHECK(IsAligned(fpr_size)); + void* start = fpr; + void* end = reinterpret_cast(start) + fpr_size; + handler(start, end, 0, arg); + size_t num_pages = fpr_size / kPageSize; + if (kIsDebugBuild) { + for (size_t j = i + 1; j < i + num_pages; ++j) { + DCHECK_EQ(page_map_[j], kPageMapEmpty); + } + } + i += fpr_size / kPageSize; + DCHECK_LE(i, pm_end); + break; + } + case kPageMapLargeObject: { + // The start of a large object. + size_t num_pages = 1; + size_t idx = i + 1; + while (idx < pm_end && page_map_[idx] == kPageMapLargeObjectPart) { + num_pages++; + idx++; + } + void* start = base_ + i * kPageSize; + void* end = base_ + (i + num_pages) * kPageSize; + size_t used_bytes = num_pages * kPageSize; + handler(start, end, used_bytes, arg); + if (kIsDebugBuild) { + for (size_t j = i + 1; j < i + num_pages; ++j) { + DCHECK_EQ(page_map_[j], kPageMapLargeObjectPart); + } + } + i += num_pages; + DCHECK_LE(i, pm_end); + break; + } + case kPageMapLargeObjectPart: + LOG(FATAL) << "Unreachable - page map type: " << pm; + break; + case kPageMapRun: { + // The start of a run. + Run* run = reinterpret_cast(base_ + i * kPageSize); + DCHECK(run->magic_num_ == kMagicNum); + run->InspectAllSlots(handler, arg); + size_t num_pages = numOfPages[run->size_bracket_idx_]; + if (kIsDebugBuild) { + for (size_t j = i + 1; j < i + num_pages; ++j) { + DCHECK_EQ(page_map_[j], kPageMapRunPart); + } + } + i += num_pages; + DCHECK_LE(i, pm_end); + break; + } + case kPageMapRunPart: + LOG(FATAL) << "Unreachable - page map type: " << pm; + break; + default: + LOG(FATAL) << "Unreachable - page map type: " << pm; + break; + } + } +} + +size_t RosAlloc::Footprint() { + MutexLock mu(Thread::Current(), lock_); + return footprint_; +} + +size_t RosAlloc::FootprintLimit() { + MutexLock mu(Thread::Current(), lock_); + return capacity_; +} + +void RosAlloc::SetFootprintLimit(size_t new_capacity) { + MutexLock mu(Thread::Current(), lock_); + DCHECK_EQ(RoundUp(new_capacity, kPageSize), new_capacity); + // Only growing is supported here. But Trim() is supported. + if (capacity_ < new_capacity) { + capacity_ = new_capacity; + VLOG(heap) << "new capacity=" << capacity_; + } +} + +void RosAlloc::RevokeThreadLocalRuns(Thread* thread) { + Thread* self = Thread::Current(); + for (size_t idx = 0; idx < kNumOfSizeBrackets; idx++) { + MutexLock mu(self, *size_bracket_locks_[idx]); + Run* thread_local_run = reinterpret_cast(thread->rosalloc_runs_[idx]); + if (thread_local_run != NULL) { + DCHECK_EQ(thread_local_run->magic_num_, kMagicNum); + DCHECK_NE(thread_local_run->is_thread_local_, 0); + thread->rosalloc_runs_[idx] = NULL; + // Note the thread local run may not be full here. + bool dont_care; + thread_local_run->MergeThreadLocalFreeBitMapToAllocBitMap(&dont_care); + thread_local_run->is_thread_local_ = 0; + thread_local_run->MergeBulkFreeBitMapIntoAllocBitMap(); + DCHECK(non_full_runs_[idx].find(thread_local_run) == non_full_runs_[idx].end()); + DCHECK(full_runs_[idx].find(thread_local_run) == full_runs_[idx].end()); + if (thread_local_run->IsFull()) { + if (kIsDebugBuild) { + full_runs_[idx].insert(thread_local_run); + DCHECK(full_runs_[idx].find(thread_local_run) != full_runs_[idx].end()); + if (kTraceRosAlloc) { + LOG(INFO) << "RosAlloc::RevokeThreadLocalRuns() : Inserted run 0x" << std::hex + << reinterpret_cast(thread_local_run) + << " into full_runs_[" << std::dec << idx << "]"; + } + } + } else if (thread_local_run->IsAllFree()) { + MutexLock mu(self, lock_); + FreePages(self, thread_local_run); + } else { + non_full_runs_[idx].insert(thread_local_run); + DCHECK(non_full_runs_[idx].find(thread_local_run) != non_full_runs_[idx].end()); + if (kTraceRosAlloc) { + LOG(INFO) << "RosAlloc::RevokeThreadLocalRuns() : Inserted run 0x" << std::hex + << reinterpret_cast(thread_local_run) + << " into non_full_runs_[" << std::dec << idx << "]"; + } + } + } + } +} + +void RosAlloc::RevokeAllThreadLocalRuns() { + // This is called when a mutator thread won't allocate such as at + // the Zygote creation time or during the GC pause. + MutexLock mu(Thread::Current(), *Locks::thread_list_lock_); + std::list thread_list = Runtime::Current()->GetThreadList()->GetList(); + for (auto it = thread_list.begin(); it != thread_list.end(); ++it) { + Thread* t = *it; + RevokeThreadLocalRuns(t); + } +} + +void RosAlloc::Initialize() { + // Check the consistency of the number of size brackets. + DCHECK_EQ(Thread::kRosAllocNumOfSizeBrackets, kNumOfSizeBrackets); + // bracketSizes. + for (size_t i = 0; i < kNumOfSizeBrackets; i++) { + if (i < kNumOfSizeBrackets - 2) { + bracketSizes[i] = 16 * (i + 1); + } else if (i == kNumOfSizeBrackets - 2) { + bracketSizes[i] = 1 * KB; + } else { + DCHECK(i == kNumOfSizeBrackets - 1); + bracketSizes[i] = 2 * KB; + } + if (kTraceRosAlloc) { + LOG(INFO) << "bracketSizes[" << i << "]=" << bracketSizes[i]; + } + } + // numOfPages. + for (size_t i = 0; i < kNumOfSizeBrackets; i++) { + if (i < 4) { + numOfPages[i] = 1; + } else if (i < 8) { + numOfPages[i] = 2; + } else if (i < 16) { + numOfPages[i] = 4; + } else if (i < 32) { + numOfPages[i] = 8; + } else if (i == 32) { + DCHECK(i = kNumOfSizeBrackets - 2); + numOfPages[i] = 16; + } else { + DCHECK(i = kNumOfSizeBrackets - 1); + numOfPages[i] = 32; + } + if (kTraceRosAlloc) { + LOG(INFO) << "numOfPages[" << i << "]=" << numOfPages[i]; + } + } + // Compute numOfSlots and slotOffsets. + for (size_t i = 0; i < kNumOfSizeBrackets; i++) { + size_t bracket_size = bracketSizes[i]; + size_t run_size = kPageSize * numOfPages[i]; + size_t max_num_of_slots = run_size / bracket_size; + // Compute the actual number of slots by taking the header and + // alignment into account. + size_t fixed_header_size = RoundUp(Run::fixed_header_size(), sizeof(uint32_t)); + DCHECK_EQ(fixed_header_size, static_cast(8)); + size_t header_size = 0; + size_t bulk_free_bit_map_offset = 0; + size_t thread_local_free_bit_map_offset = 0; + size_t num_of_slots = 0; + // Search for the maximum number of slots that allows enough space + // for the header (including the bit maps.) + for (int s = max_num_of_slots; s >= 0; s--) { + size_t tmp_slots_size = bracket_size * s; + size_t tmp_bit_map_size = RoundUp(s, sizeof(uint32_t) * kBitsPerByte) / kBitsPerByte; + size_t tmp_bulk_free_bit_map_size = tmp_bit_map_size; + size_t tmp_bulk_free_bit_map_off = fixed_header_size + tmp_bit_map_size; + size_t tmp_thread_local_free_bit_map_size = tmp_bit_map_size; + size_t tmp_thread_local_free_bit_map_off = tmp_bulk_free_bit_map_off + tmp_bulk_free_bit_map_size; + size_t tmp_unaligned_header_size = tmp_thread_local_free_bit_map_off + tmp_thread_local_free_bit_map_size; + // Align up the unaligned header size. bracket_size may not be a power of two. + size_t tmp_header_size = (tmp_unaligned_header_size % bracket_size == 0) ? + tmp_unaligned_header_size : + tmp_unaligned_header_size + (bracket_size - tmp_unaligned_header_size % bracket_size); + DCHECK_EQ(tmp_header_size % bracket_size, static_cast(0)); + DCHECK_EQ(tmp_header_size % 8, static_cast(0)); + if (tmp_slots_size + tmp_header_size <= run_size) { + // Found the right number of slots, that is, there was enough + // space for the header (including the bit maps.) + num_of_slots = s; + header_size = tmp_header_size; + bulk_free_bit_map_offset = tmp_bulk_free_bit_map_off; + thread_local_free_bit_map_offset = tmp_thread_local_free_bit_map_off; + break; + } + } + DCHECK(num_of_slots > 0 && header_size > 0 && bulk_free_bit_map_offset > 0); + // Add the padding for the alignment remainder. + header_size += run_size % bracket_size; + DCHECK(header_size + num_of_slots * bracket_size == run_size); + numOfSlots[i] = num_of_slots; + headerSizes[i] = header_size; + bulkFreeBitMapOffsets[i] = bulk_free_bit_map_offset; + threadLocalFreeBitMapOffsets[i] = thread_local_free_bit_map_offset; + if (kTraceRosAlloc) { + LOG(INFO) << "numOfSlots[" << i << "]=" << numOfSlots[i] + << ", headerSizes[" << i << "]=" << headerSizes[i] + << ", bulkFreeBitMapOffsets[" << i << "]=" << bulkFreeBitMapOffsets[i] + << ", threadLocalFreeBitMapOffsets[" << i << "]=" << threadLocalFreeBitMapOffsets[i];; + } + } +} + +void RosAlloc::BytesAllocatedCallback(void* start, void* end, size_t used_bytes, void* arg) { + if (used_bytes == 0) { + return; + } + size_t* bytes_allocated = reinterpret_cast(arg); + *bytes_allocated += used_bytes; +} + +void RosAlloc::ObjectsAllocatedCallback(void* start, void* end, size_t used_bytes, void* arg) { + if (used_bytes == 0) { + return; + } + size_t* objects_allocated = reinterpret_cast(arg); + ++(*objects_allocated); +} + +} // namespace allocator +} // namespace gc +} // namespace art diff --git a/runtime/gc/allocator/rosalloc.h b/runtime/gc/allocator/rosalloc.h new file mode 100644 index 00000000000..5dda80c16eb --- /dev/null +++ b/runtime/gc/allocator/rosalloc.h @@ -0,0 +1,480 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ART_RUNTIME_GC_ALLOCATOR_ROSALLOC_H_ +#define ART_RUNTIME_GC_ALLOCATOR_ROSALLOC_H_ + +#include +#include +#include +#include +#include +#include + +#include "base/mutex.h" +#include "base/logging.h" +#include "globals.h" +#include "utils.h" + +// A boilerplate to use hash_map/hash_set both on host and device. +#ifdef HAVE_ANDROID_OS +#include +#include +using std::hash_map; +using std::hash_set; +#else // HAVE_ANDROID_OS +#ifdef __DEPRECATED +#define ROSALLOC_OLD__DEPRECATED __DEPRECATED +#undef __DEPRECATED +#endif +#include +#include +#ifdef ROSALLOC_OLD__DEPRECATED +#define __DEPRECATED ROSALLOC_OLD__DEPRECATED +#undef ROSALLOC_OLD__DEPRECATED +#endif +using __gnu_cxx::hash_map; +using __gnu_cxx::hash_set; +#endif // HAVE_ANDROID_OS + +namespace art { +namespace gc { +namespace allocator { + +// A Runs-of-slots memory allocator. +class RosAlloc { + private: + // Rerepresents a run of free pages. + class FreePageRun { + public: + byte magic_num_; // The magic number used for debugging only. + + bool IsFree() const { + if (kIsDebugBuild) { + return magic_num_ == kMagicNumFree; + } + return true; + } + size_t ByteSize(RosAlloc* rosalloc) const EXCLUSIVE_LOCKS_REQUIRED(rosalloc->lock_) { + const byte* fpr_base = reinterpret_cast(this); + size_t pm_idx = rosalloc->ToPageMapIndex(fpr_base); + size_t byte_size = rosalloc->free_page_run_size_map_[pm_idx]; + DCHECK_GE(byte_size, static_cast(0)); + DCHECK_EQ(byte_size % kPageSize, static_cast(0)); + return byte_size; + } + void SetByteSize(RosAlloc* rosalloc, size_t byte_size) + EXCLUSIVE_LOCKS_REQUIRED(rosalloc->lock_) { + DCHECK_EQ(byte_size % kPageSize, static_cast(0)); + byte* fpr_base = reinterpret_cast(this); + size_t pm_idx = rosalloc->ToPageMapIndex(fpr_base); + rosalloc->free_page_run_size_map_[pm_idx] = byte_size; + } + void* Begin() { + return reinterpret_cast(this); + } + void* End(RosAlloc* rosalloc) EXCLUSIVE_LOCKS_REQUIRED(rosalloc->lock_) { + byte* fpr_base = reinterpret_cast(this); + byte* end = fpr_base + ByteSize(rosalloc); + return end; + } + void ReleasePages(RosAlloc* rosalloc) EXCLUSIVE_LOCKS_REQUIRED(rosalloc->lock_) { + size_t byte_size = ByteSize(rosalloc); + DCHECK_EQ(byte_size % kPageSize, static_cast(0)); + if (kIsDebugBuild) { + // Exclude the first page that stores the magic number. + DCHECK_GE(byte_size, static_cast(kPageSize)); + byte_size -= kPageSize; + if (byte_size > 0) { + madvise(reinterpret_cast(this) + kPageSize, byte_size, MADV_DONTNEED); + } + } else { + madvise(this, byte_size, MADV_DONTNEED); + } + } + }; + + // Represents a run of memory slots of the same size. + // + // A run's memory layout: + // + // +-------------------+ + // | magic_num | + // +-------------------+ + // | size_bracket_idx | + // +-------------------+ + // | is_thread_local | + // +-------------------+ + // | to_be_bulk_freed | + // +-------------------+ + // | top_slot_idx | + // +-------------------+ + // | | + // | alloc bit map | + // | | + // +-------------------+ + // | | + // | bulk free bit map | + // | | + // +-------------------+ + // | | + // | thread-local free | + // | bit map | + // | | + // +-------------------+ + // | padding due to | + // | alignment | + // +-------------------+ + // | slot 0 | + // +-------------------+ + // | slot 1 | + // +-------------------+ + // | slot 2 | + // +-------------------+ + // ... + // +-------------------+ + // | last slot | + // +-------------------+ + // + class Run { + public: + byte magic_num_; // The magic number used for debugging. + byte size_bracket_idx_; // The index of the size bracket of this run. + byte is_thread_local_; // True if this run is used as a thread-local run. + byte to_be_bulk_freed_; // Used within BulkFree() to flag a run that's involved with a bulk free. + uint32_t top_slot_idx_; // The top slot index when this run is in bump index mode. + uint32_t alloc_bit_map_[0]; // The bit map that allocates if each slot is in use. + + // bulk_free_bit_map_[] : The bit map that is used for GC to + // temporarily mark the slots to free without using a lock. After + // all the slots to be freed in a run are marked, all those slots + // get freed in bulk with one locking per run, as opposed to one + // locking per slot to minimize the lock contention. This is used + // within BulkFree(). + + // thread_local_free_bit_map_[] : The bit map that is used for GC + // to temporarily mark the slots to free in a thread-local run + // without using a lock (without synchronizing the thread that + // owns the thread-local run.) When the thread-local run becomes + // full, the thread will check this bit map and update the + // allocation bit map of the run (that is, the slots get freed.) + + // Returns the byte size of the header except for the bit maps. + static size_t fixed_header_size() { + Run temp; + size_t size = reinterpret_cast(&temp.alloc_bit_map_) - reinterpret_cast(&temp); + DCHECK_EQ(size, static_cast(8)); + return size; + } + // Returns the base address of the free bit map. + uint32_t* bulk_free_bit_map() { + return reinterpret_cast(reinterpret_cast(this) + bulkFreeBitMapOffsets[size_bracket_idx_]); + } + // Returns the base address of the thread local free bit map. + uint32_t* thread_local_free_bit_map() { + return reinterpret_cast(reinterpret_cast(this) + threadLocalFreeBitMapOffsets[size_bracket_idx_]); + } + void* End() { + return reinterpret_cast(this) + kPageSize * numOfPages[size_bracket_idx_]; + } + // Frees slots in the allocation bit map with regard to the + // thread-local free bit map. Used when a thread-local run becomes + // full. + bool MergeThreadLocalFreeBitMapToAllocBitMap(bool* is_all_free_after_out); + // Frees slots in the allocation bit map with regard to the bulk + // free bit map. Used in a bulk free. + void MergeBulkFreeBitMapIntoAllocBitMap(); + // Unions the slots to be freed in the free bit map into the + // thread-local free bit map. In a bulk free, as a two-step + // process, GC will first record all the slots to free in a run in + // the free bit map where it can write without a lock, and later + // acquire a lock once per run to union the bits of the free bit + // map to the thread-local free bit map. + void UnionBulkFreeBitMapToThreadLocalFreeBitMap(); + // Allocates a slot in a run. + void* AllocSlot(); + // Frees a slot in a run. This is used in a non-bulk free. + void FreeSlot(void* ptr); + // Marks the slots to free in the bulk free bit map. + void MarkBulkFreeBitMap(void* ptr); + // Marks the slots to free in the thread-local free bit map. + void MarkThreadLocalFreeBitMap(void* ptr); + // Returns true if all the slots in the run are not in use. + bool IsAllFree(); + // Returns true if all the slots in the run are in use. + bool IsFull(); + // Clear all the bit maps. + void ClearBitMaps(); + // Iterate over all the slots and apply the given function. + void InspectAllSlots(void (*handler)(void* start, void* end, size_t used_bytes, void* callback_arg), void* arg); + // Dump the run metadata for debugging. + void Dump(); + + private: + // The common part of MarkFreeBitMap() and MarkThreadLocalFreeBitMap(). + void MarkFreeBitMapShared(void* ptr, uint32_t* free_bit_map_base, const char* caller_name); + }; + + // The magic number for a run. + static const byte kMagicNum = 42; + // The magic number for free pages. + static const byte kMagicNumFree = 43; + // The number of size brackets. Sync this with the length of Thread::rosalloc_runs_. + static const size_t kNumOfSizeBrackets = 34; + // The number of smaller size brackets that are 16 bytes apart. + static const size_t kNumOfQuantumSizeBrackets = 32; + // The sizes (the slot sizes, in bytes) of the size brackets. + static size_t bracketSizes[kNumOfSizeBrackets]; + // The numbers of pages that are used for runs for each size bracket. + static size_t numOfPages[kNumOfSizeBrackets]; + // The numbers of slots of the runs for each size bracket. + static size_t numOfSlots[kNumOfSizeBrackets]; + // The header sizes in bytes of the runs for each size bracket. + static size_t headerSizes[kNumOfSizeBrackets]; + // The byte offsets of the bulk free bit maps of the runs for each size bracket. + static size_t bulkFreeBitMapOffsets[kNumOfSizeBrackets]; + // The byte offsets of the thread-local free bit maps of the runs for each size bracket. + static size_t threadLocalFreeBitMapOffsets[kNumOfSizeBrackets]; + + // Initialize the run specs (the above arrays). + static void Initialize(); + static bool initialized_; + + // Returns the byte size of the bracket size from the index. + static size_t IndexToBracketSize(size_t idx) { + DCHECK(idx < kNumOfSizeBrackets); + return bracketSizes[idx]; + } + // Returns the index of the size bracket from the bracket size. + static size_t BracketSizeToIndex(size_t size) { + DCHECK(16 <= size && ((size < 1 * KB && size % 16 == 0) || size == 1 * KB || size == 2 * KB)); + size_t idx; + if (UNLIKELY(size == 1 * KB)) { + idx = kNumOfSizeBrackets - 2; + } else if (UNLIKELY(size == 2 * KB)) { + idx = kNumOfSizeBrackets - 1; + } else { + DCHECK(size < 1 * KB); + DCHECK_EQ(size % 16, static_cast(0)); + idx = size / 16 - 1; + } + DCHECK(bracketSizes[idx] == size); + return idx; + } + // Rounds up the size up the nearest bracket size. + static size_t RoundToBracketSize(size_t size) { + DCHECK(size <= kLargeSizeThreshold); + if (LIKELY(size <= 512)) { + return RoundUp(size, 16); + } else if (512 < size && size <= 1 * KB) { + return 1 * KB; + } else { + DCHECK(1 * KB < size && size <= 2 * KB); + return 2 * KB; + } + } + // Returns the size bracket index from the byte size with rounding. + static size_t SizeToIndex(size_t size) { + DCHECK(size <= kLargeSizeThreshold); + if (LIKELY(size <= 512)) { + return RoundUp(size, 16) / 16 - 1; + } else if (512 < size && size <= 1 * KB) { + return kNumOfSizeBrackets - 2; + } else { + DCHECK(1 * KB < size && size <= 2 * KB); + return kNumOfSizeBrackets - 1; + } + } + // A combination of SizeToIndex() and RoundToBracketSize(). + static size_t SizeToIndexAndBracketSize(size_t size, size_t* bracket_size_out) { + DCHECK(size <= kLargeSizeThreshold); + if (LIKELY(size <= 512)) { + size_t bracket_size = RoundUp(size, 16); + *bracket_size_out = bracket_size; + size_t idx = bracket_size / 16 - 1; + DCHECK_EQ(bracket_size, IndexToBracketSize(idx)); + return idx; + } else if (512 < size && size <= 1 * KB) { + size_t bracket_size = 1024; + *bracket_size_out = bracket_size; + size_t idx = kNumOfSizeBrackets - 2; + DCHECK_EQ(bracket_size, IndexToBracketSize(idx)); + return idx; + } else { + DCHECK(1 * KB < size && size <= 2 * KB); + size_t bracket_size = 2048; + *bracket_size_out = bracket_size; + size_t idx = kNumOfSizeBrackets - 1; + DCHECK_EQ(bracket_size, IndexToBracketSize(idx)); + return idx; + } + } + // Returns the page map index from an address. Requires that the + // address is page size aligned. + size_t ToPageMapIndex(const void* addr) const { + DCHECK(base_ <= addr && addr < base_ + capacity_); + size_t byte_offset = reinterpret_cast(addr) - base_; + DCHECK_EQ(byte_offset % static_cast(kPageSize), static_cast(0)); + return byte_offset / kPageSize; + } + // Returns the page map index from an address with rounding. + size_t RoundDownToPageMapIndex(void* addr) { + DCHECK(base_ <= addr && addr < reinterpret_cast(base_) + capacity_); + return (reinterpret_cast(addr) - reinterpret_cast(base_)) / kPageSize; + } + + // A memory allocation request larger than this size is treated as a large object and allocated + // at a page-granularity. + static const size_t kLargeSizeThreshold = 2048; + + // We use use thread-local runs for the size Brackets whose indexes + // are less than or equal to this index. We use shared (current) + // runs for the rest. + static const size_t kMaxThreadLocalSizeBracketIdx = 10; + + struct hash_run { + size_t operator()(const RosAlloc::Run* r) const { + return reinterpret_cast(r); + } + }; + + struct eq_run { + bool operator()(const RosAlloc::Run* r1, const RosAlloc::Run* r2) const { + return r1 == r2; + } + }; + + // The base address of the memory region that's managed by this allocator. + byte* base_; + + // The footprint in bytes of the currently allocated portion of the + // memory region. + size_t footprint_; + + // The maximum footprint. The address, base_ + capacity_, indicates + // the end of the memory region that's managed by this allocator. + size_t capacity_; + + // The run sets that hold the runs whose slots are not all + // full. non_full_runs_[i] is guarded by size_bracket_locks_[i]. + std::set non_full_runs_[kNumOfSizeBrackets]; + // The run sets that hold the runs whose slots are all full. This is + // debug only. full_runs_[i] is guarded by size_bracket_locks_[i]. + hash_set full_runs_[kNumOfSizeBrackets]; + // The set of free pages. + std::set free_page_runs_ GUARDED_BY(lock_); + // The free page run whose end address is the end of the memory + // region that's managed by this allocator, if any. + FreePageRun* last_free_page_run_; + // The current runs where the allocations are first attempted for + // the size brackes that do not use thread-local + // runs. current_runs_[i] is guarded by size_bracket_locks_[i]. + Run* current_runs_[kNumOfSizeBrackets]; + // The mutexes, one per size bracket. + Mutex* size_bracket_locks_[kNumOfSizeBrackets]; + // The types of page map entries. + enum { + kPageMapEmpty = 0, // Not allocated. + kPageMapRun = 1, // The beginning of a run. + kPageMapRunPart = 2, // The non-beginning part of a run. + kPageMapLargeObject = 3, // The beginning of a large object. + kPageMapLargeObjectPart = 4, // The non-beginning part of a large object. + }; + // The table that indicates what pages are currently used for. + std::vector page_map_ GUARDED_BY(lock_); + // The table that indicates the size of free page runs. These sizes + // are stored here to avoid storing in the free page header and + // release backing pages. + std::vector free_page_run_size_map_ GUARDED_BY(lock_); + // The global lock. Used to guard the page map, the free page set, + // and the footprint. + Mutex lock_ DEFAULT_MUTEX_ACQUIRED_AFTER; + // The reader-writer lock to allow one bulk free at a time while + // allowing multiple individual frees at the same time. + ReaderWriterMutex bulk_free_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER; + + // The base address of the memory region that's managed by this allocator. + byte* Begin() { return base_; } + // The end address of the memory region that's managed by this allocator. + byte* End() { return base_ + capacity_; } + + // Page-granularity alloc/free + void* AllocPages(Thread* self, size_t num_pages, byte page_map_type) + EXCLUSIVE_LOCKS_REQUIRED(lock_); + void FreePages(Thread* self, void* ptr) EXCLUSIVE_LOCKS_REQUIRED(lock_); + + // Allocate/free a run slot. + void* AllocFromRun(Thread* self, size_t size, size_t* bytes_allocated) + LOCKS_EXCLUDED(lock_); + void FreeFromRun(Thread* self, void* ptr, Run* run) + LOCKS_EXCLUDED(lock_); + + // Used to acquire a new/reused run for a size bracket. Used when a + // thread-local or current run gets full. + Run* RefillRun(Thread* self, size_t idx) LOCKS_EXCLUDED(lock_); + + // The internal of non-bulk Free(). + void FreeInternal(Thread* self, void* ptr) LOCKS_EXCLUDED(lock_); + + public: + RosAlloc(void* base, size_t capacity); + void* Alloc(Thread* self, size_t size, size_t* bytes_allocated) + LOCKS_EXCLUDED(lock_); + void Free(Thread* self, void* ptr) + LOCKS_EXCLUDED(bulk_free_lock_); + void BulkFree(Thread* self, void** ptrs, size_t num_ptrs) + LOCKS_EXCLUDED(bulk_free_lock_); + // Returns the size of the allocated slot for a given allocated memory chunk. + size_t UsableSize(void* ptr); + // Returns the size of the allocated slot for a given size. + size_t UsableSize(size_t bytes) { + if (UNLIKELY(bytes > kLargeSizeThreshold)) { + return RoundUp(bytes, kPageSize); + } else { + return RoundToBracketSize(bytes); + } + } + // Try to reduce the current footprint by releasing the free page + // run at the end of the memory region, if any. + bool Trim(); + // Iterates over all the memory slots and apply the given function. + void InspectAll(void (*handler)(void* start, void* end, size_t used_bytes, void* callback_arg), + void* arg) + LOCKS_EXCLUDED(lock_); + // Returns the current footprint. + size_t Footprint() LOCKS_EXCLUDED(lock_); + // Returns the current capacity, maximum footprint. + size_t FootprintLimit() LOCKS_EXCLUDED(lock_); + // Update the current capacity. + void SetFootprintLimit(size_t bytes) LOCKS_EXCLUDED(lock_); + // Releases the thread-local runs assigned to the given thread back to the common set of runs. + void RevokeThreadLocalRuns(Thread* thread); + // Releases the thread-local runs assigned to all the threads back to the common set of runs. + void RevokeAllThreadLocalRuns() LOCKS_EXCLUDED(Locks::thread_list_lock_); + // Dumps the page map for debugging. + void DumpPageMap(Thread* self); + + // Callbacks for InspectAll that will count the number of bytes + // allocated and objects allocated, respectively. + static void BytesAllocatedCallback(void* start, void* end, size_t used_bytes, void* arg); + static void ObjectsAllocatedCallback(void* start, void* end, size_t used_bytes, void* arg); +}; + +} // namespace allocator +} // namespace gc +} // namespace art + +#endif // ART_RUNTIME_GC_ALLOCATOR_ROSALLOC_H_ diff --git a/runtime/gc/collector/garbage_collector.cc b/runtime/gc/collector/garbage_collector.cc index 178910347c6..56d9ef4d4d0 100644 --- a/runtime/gc/collector/garbage_collector.cc +++ b/runtime/gc/collector/garbage_collector.cc @@ -83,6 +83,7 @@ void GarbageCollector::Run(bool clear_soft_references) { thread_list->SuspendAll(); MarkingPhase(); ReclaimPhase(); + GetHeap()->RevokeAllThreadLocalBuffers(); thread_list->ResumeAll(); ATRACE_END(); uint64_t pause_end = NanoTime(); @@ -101,6 +102,9 @@ void GarbageCollector::Run(bool clear_soft_references) { ATRACE_END(); ATRACE_BEGIN("All mutator threads suspended"); done = HandleDirtyObjectsPhase(); + if (done) { + GetHeap()->RevokeAllThreadLocalBuffers(); + } ATRACE_END(); uint64_t pause_end = NanoTime(); ATRACE_BEGIN("Resuming mutator threads"); @@ -135,7 +139,7 @@ void GarbageCollector::SwapBitmaps() { if (live_bitmap != mark_bitmap) { heap_->GetLiveBitmap()->ReplaceBitmap(live_bitmap, mark_bitmap); heap_->GetMarkBitmap()->ReplaceBitmap(mark_bitmap, live_bitmap); - space->AsDlMallocSpace()->SwapBitmaps(); + space->AsMallocSpace()->SwapBitmaps(); } } } diff --git a/runtime/gc/collector/mark_sweep.cc b/runtime/gc/collector/mark_sweep.cc index 61b3f09a4c9..58068b1bcd9 100644 --- a/runtime/gc/collector/mark_sweep.cc +++ b/runtime/gc/collector/mark_sweep.cc @@ -613,8 +613,8 @@ void MarkSweep::VerifyImageRootVisitor(Object* root, void* arg) { } void MarkSweep::BindLiveToMarkBitmap(space::ContinuousSpace* space) { - CHECK(space->IsDlMallocSpace()); - space::DlMallocSpace* alloc_space = space->AsDlMallocSpace(); + CHECK(space->IsMallocSpace()); + space::MallocSpace* alloc_space = space->AsMallocSpace(); accounting::SpaceBitmap* live_bitmap = space->GetLiveBitmap(); accounting::SpaceBitmap* mark_bitmap = alloc_space->BindLiveToMarkBitmap(); GetHeap()->GetMarkBitmap()->ReplaceBitmap(mark_bitmap, live_bitmap); @@ -1126,7 +1126,7 @@ void MarkSweep::ZygoteSweepCallback(size_t num_ptrs, Object** ptrs, void* arg) { } void MarkSweep::SweepArray(accounting::ObjectStack* allocations, bool swap_bitmaps) { - space::DlMallocSpace* space = heap_->GetNonMovingSpace(); + space::MallocSpace* space = heap_->GetNonMovingSpace(); timings_.StartSplit("SweepArray"); // Newly allocated objects MUST be in the alloc space and those are the only objects which we are // going to free. @@ -1212,7 +1212,7 @@ void MarkSweep::Sweep(bool swap_bitmaps) { scc.mark_sweep = this; scc.self = Thread::Current(); for (const auto& space : GetHeap()->GetContinuousSpaces()) { - if (!space->IsDlMallocSpace()) { + if (!space->IsMallocSpace()) { continue; } // We always sweep always collect spaces. @@ -1224,7 +1224,7 @@ void MarkSweep::Sweep(bool swap_bitmaps) { if (sweep_space) { uintptr_t begin = reinterpret_cast(space->Begin()); uintptr_t end = reinterpret_cast(space->End()); - scc.space = space->AsDlMallocSpace(); + scc.space = space->AsMallocSpace(); accounting::SpaceBitmap* live_bitmap = space->GetLiveBitmap(); accounting::SpaceBitmap* mark_bitmap = space->GetMarkBitmap(); if (swap_bitmaps) { @@ -1274,7 +1274,7 @@ void MarkSweep::SweepLargeObjects(bool swap_bitmaps) { void MarkSweep::CheckReference(const Object* obj, const Object* ref, MemberOffset offset, bool is_static) { for (const auto& space : GetHeap()->GetContinuousSpaces()) { - if (space->IsDlMallocSpace() && space->Contains(ref)) { + if (space->IsMallocSpace() && space->Contains(ref)) { DCHECK(IsMarked(obj)); bool is_marked = IsMarked(ref); @@ -1424,8 +1424,8 @@ inline bool MarkSweep::IsMarked(const Object* object) const void MarkSweep::UnBindBitmaps() { TimingLogger::ScopedSplit split("UnBindBitmaps", &timings_); for (const auto& space : GetHeap()->GetContinuousSpaces()) { - if (space->IsDlMallocSpace()) { - space::DlMallocSpace* alloc_space = space->AsDlMallocSpace(); + if (space->IsMallocSpace()) { + space::MallocSpace* alloc_space = space->AsMallocSpace(); if (alloc_space->temp_bitmap_.get() != NULL) { // At this point, the temp_bitmap holds our old mark bitmap. accounting::SpaceBitmap* new_bitmap = alloc_space->temp_bitmap_.release(); diff --git a/runtime/gc/collector/semi_space.cc b/runtime/gc/collector/semi_space.cc index ba98314f598..00794d69bcb 100644 --- a/runtime/gc/collector/semi_space.cc +++ b/runtime/gc/collector/semi_space.cc @@ -368,8 +368,8 @@ void SemiSpace::MarkRoots() { } void SemiSpace::BindLiveToMarkBitmap(space::ContinuousSpace* space) { - CHECK(space->IsDlMallocSpace()); - space::DlMallocSpace* alloc_space = space->AsDlMallocSpace(); + CHECK(space->IsMallocSpace()); + space::MallocSpace* alloc_space = space->AsMallocSpace(); accounting::SpaceBitmap* live_bitmap = space->GetLiveBitmap(); accounting::SpaceBitmap* mark_bitmap = alloc_space->BindLiveToMarkBitmap(); GetHeap()->GetMarkBitmap()->ReplaceBitmap(mark_bitmap, live_bitmap); @@ -433,7 +433,7 @@ void SemiSpace::Sweep(bool swap_bitmaps) { scc.mark_sweep = this; scc.self = Thread::Current(); for (const auto& space : GetHeap()->GetContinuousSpaces()) { - if (!space->IsDlMallocSpace()) { + if (!space->IsMallocSpace()) { continue; } // We always sweep always collect spaces. @@ -442,10 +442,10 @@ void SemiSpace::Sweep(bool swap_bitmaps) { // We sweep full collect spaces when the GC isn't a partial GC (ie its full). sweep_space = (space->GetGcRetentionPolicy() == space::kGcRetentionPolicyFullCollect); } - if (sweep_space && space->IsDlMallocSpace()) { + if (sweep_space && space->IsMallocSpace()) { uintptr_t begin = reinterpret_cast(space->Begin()); uintptr_t end = reinterpret_cast(space->End()); - scc.space = space->AsDlMallocSpace(); + scc.space = space->AsMallocSpace(); accounting::SpaceBitmap* live_bitmap = space->GetLiveBitmap(); accounting::SpaceBitmap* mark_bitmap = space->GetMarkBitmap(); if (swap_bitmaps) { @@ -550,8 +550,8 @@ inline Object* SemiSpace::GetMarkedForwardAddress(mirror::Object* obj) const void SemiSpace::UnBindBitmaps() { TimingLogger::ScopedSplit split("UnBindBitmaps", &timings_); for (const auto& space : GetHeap()->GetContinuousSpaces()) { - if (space->IsDlMallocSpace()) { - space::DlMallocSpace* alloc_space = space->AsDlMallocSpace(); + if (space->IsMallocSpace()) { + space::MallocSpace* alloc_space = space->AsMallocSpace(); if (alloc_space->HasBoundBitmaps()) { alloc_space->UnBindBitmaps(); heap_->GetMarkBitmap()->ReplaceBitmap(alloc_space->GetLiveBitmap(), diff --git a/runtime/gc/collector/sticky_mark_sweep.cc b/runtime/gc/collector/sticky_mark_sweep.cc index b27b8dfb46e..ee6077ad1d8 100644 --- a/runtime/gc/collector/sticky_mark_sweep.cc +++ b/runtime/gc/collector/sticky_mark_sweep.cc @@ -38,7 +38,7 @@ void StickyMarkSweep::BindBitmaps() { // know what was allocated since the last GC. A side-effect of binding the allocation space mark // and live bitmap is that marking the objects will place them in the live bitmap. for (const auto& space : GetHeap()->GetContinuousSpaces()) { - if (space->IsDlMallocSpace() && + if (space->IsMallocSpace() && space->GetGcRetentionPolicy() == space::kGcRetentionPolicyAlwaysCollect) { BindLiveToMarkBitmap(space); } diff --git a/runtime/gc/heap-inl.h b/runtime/gc/heap-inl.h index 1d3c0d87772..e6829e28045 100644 --- a/runtime/gc/heap-inl.h +++ b/runtime/gc/heap-inl.h @@ -23,6 +23,7 @@ #include "gc/space/bump_pointer_space-inl.h" #include "gc/space/dlmalloc_space-inl.h" #include "gc/space/large_object_space.h" +#include "gc/space/rosalloc_space-inl.h" #include "object_utils.h" #include "runtime.h" #include "thread.h" @@ -41,7 +42,15 @@ inline mirror::Object* Heap::AllocNonMovableObjectUninstrumented(Thread* self, m &obj, &bytes_allocated); if (LIKELY(!large_object_allocation)) { // Non-large object allocation. - obj = AllocateUninstrumented(self, non_moving_space_, byte_count, &bytes_allocated); + if (!kUseRosAlloc) { + DCHECK(non_moving_space_->IsDlMallocSpace()); + obj = AllocateUninstrumented(self, reinterpret_cast(non_moving_space_), + byte_count, &bytes_allocated); + } else { + DCHECK(non_moving_space_->IsRosAllocSpace()); + obj = AllocateUninstrumented(self, reinterpret_cast(non_moving_space_), + byte_count, &bytes_allocated); + } // Ensure that we did not allocate into a zygote space. DCHECK(obj == NULL || !have_zygote_space_ || !FindSpaceFromObject(obj, false)->IsZygoteSpace()); } @@ -131,6 +140,16 @@ inline mirror::Object* Heap::TryToAllocateUninstrumented(Thread* self, space::Dl return space->AllocNonvirtual(self, alloc_size, bytes_allocated); } +// RosAllocSpace-specific version. +inline mirror::Object* Heap::TryToAllocateUninstrumented(Thread* self, space::RosAllocSpace* space, size_t alloc_size, + bool grow, size_t* bytes_allocated) { + if (UNLIKELY(IsOutOfMemoryOnAllocation(alloc_size, grow))) { + return NULL; + } + DCHECK(!running_on_valgrind_); + return space->AllocNonvirtual(self, alloc_size, bytes_allocated); +} + template inline mirror::Object* Heap::AllocateUninstrumented(Thread* self, T* space, size_t alloc_size, size_t* bytes_allocated) { diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc index f446fcf541b..763bfe9cbd1 100644 --- a/runtime/gc/heap.cc +++ b/runtime/gc/heap.cc @@ -41,6 +41,7 @@ #include "gc/space/dlmalloc_space-inl.h" #include "gc/space/image_space.h" #include "gc/space/large_object_space.h" +#include "gc/space/rosalloc_space-inl.h" #include "gc/space/space-inl.h" #include "heap-inl.h" #include "image.h" @@ -167,9 +168,13 @@ Heap::Heap(size_t initial_size, size_t growth_limit, size_t min_free, size_t max } const char* name = Runtime::Current()->IsZygote() ? "zygote space" : "alloc space"; - non_moving_space_ = space::DlMallocSpace::Create(name, initial_size, growth_limit, capacity, - requested_alloc_space_begin); - + if (!kUseRosAlloc) { + non_moving_space_ = space::DlMallocSpace::Create(name, initial_size, growth_limit, capacity, + requested_alloc_space_begin); + } else { + non_moving_space_ = space::RosAllocSpace::Create(name, initial_size, growth_limit, capacity, + requested_alloc_space_begin); + } if (kMovingCollector) { // TODO: Place bump-pointer spaces somewhere to minimize size of card table. // TODO: Having 3+ spaces as big as the large heap size can cause virtual memory fragmentation @@ -482,8 +487,8 @@ void Heap::AddSpace(space::Space* space) { } continuous_spaces_.push_back(continuous_space); - if (continuous_space->IsDlMallocSpace()) { - non_moving_space_ = continuous_space->AsDlMallocSpace(); + if (continuous_space->IsMallocSpace()) { + non_moving_space_ = continuous_space->AsMallocSpace(); } // Ensure that spaces remain sorted in increasing order of start address. @@ -501,7 +506,7 @@ void Heap::AddSpace(space::Space* space) { } else if (space->IsZygoteSpace()) { CHECK(!seen_alloc); seen_zygote = true; - } else if (space->IsDlMallocSpace()) { + } else if (space->IsMallocSpace()) { seen_alloc = true; } } @@ -757,8 +762,15 @@ void Heap::ThrowOutOfMemoryError(Thread* self, size_t byte_count, bool large_obj if (!large_object_allocation && total_bytes_free >= byte_count) { size_t max_contiguous_allocation = 0; for (const auto& space : continuous_spaces_) { - if (space->IsDlMallocSpace()) { - space->AsDlMallocSpace()->Walk(MSpaceChunkCallback, &max_contiguous_allocation); + if (space->IsMallocSpace()) { + // To allow the Walk/InspectAll() to exclusively-lock the mutator + // lock, temporarily release the shared access to the mutator + // lock here by transitioning to the suspended state. + Locks::mutator_lock_->AssertSharedHeld(self); + self->TransitionFromRunnableToSuspended(kSuspended); + space->AsMallocSpace()->Walk(MSpaceChunkCallback, &max_contiguous_allocation); + self->TransitionFromSuspendedToRunnable(); + Locks::mutator_lock_->AssertSharedHeld(self); } } oss << "; failed due to fragmentation (largest possible contiguous allocation " @@ -834,7 +846,15 @@ mirror::Object* Heap::AllocNonMovableObjectInstrumented(Thread* self, mirror::Cl &bytes_allocated); if (LIKELY(!large_object_allocation)) { // Non-large object allocation. - obj = AllocateInstrumented(self, non_moving_space_, byte_count, &bytes_allocated); + if (!kUseRosAlloc) { + DCHECK(non_moving_space_->IsDlMallocSpace()); + obj = AllocateInstrumented(self, reinterpret_cast(non_moving_space_), + byte_count, &bytes_allocated); + } else { + DCHECK(non_moving_space_->IsRosAllocSpace()); + obj = AllocateInstrumented(self, reinterpret_cast(non_moving_space_), + byte_count, &bytes_allocated); + } // Ensure that we did not allocate into a zygote space. DCHECK(obj == NULL || !have_zygote_space_ || !FindSpaceFromObject(obj, false)->IsZygoteSpace()); } @@ -866,8 +886,8 @@ void Heap::Trim() { uint64_t total_alloc_space_size = 0; uint64_t managed_reclaimed = 0; for (const auto& space : continuous_spaces_) { - if (space->IsDlMallocSpace() && !space->IsZygoteSpace()) { - gc::space::DlMallocSpace* alloc_space = space->AsDlMallocSpace(); + if (space->IsMallocSpace() && !space->IsZygoteSpace()) { + gc::space::MallocSpace* alloc_space = space->AsMallocSpace(); total_alloc_space_size += alloc_space->Size(); managed_reclaimed += alloc_space->Trim(); } @@ -1101,6 +1121,19 @@ inline mirror::Object* Heap::TryToAllocateInstrumented(Thread* self, space::DlMa } } +// RosAllocSpace-specific version. +inline mirror::Object* Heap::TryToAllocateInstrumented(Thread* self, space::RosAllocSpace* space, size_t alloc_size, + bool grow, size_t* bytes_allocated) { + if (UNLIKELY(IsOutOfMemoryOnAllocation(alloc_size, grow))) { + return NULL; + } + if (LIKELY(!running_on_valgrind_)) { + return space->AllocNonvirtual(self, alloc_size, bytes_allocated); + } else { + return space->Alloc(self, alloc_size, bytes_allocated); + } +} + template inline mirror::Object* Heap::AllocateInstrumented(Thread* self, T* space, size_t alloc_size, size_t* bytes_allocated) { @@ -1390,14 +1423,14 @@ void Heap::PreZygoteFork() { } // Turn the current alloc space into a zygote space and obtain the new alloc space composed of // the remaining available heap memory. - space::DlMallocSpace* zygote_space = non_moving_space_; + space::MallocSpace* zygote_space = non_moving_space_; non_moving_space_ = zygote_space->CreateZygoteSpace("alloc space"); non_moving_space_->SetFootprintLimit(non_moving_space_->Capacity()); // Change the GC retention policy of the zygote space to only collect when full. zygote_space->SetGcRetentionPolicy(space::kGcRetentionPolicyFullCollect); AddSpace(non_moving_space_); have_zygote_space_ = true; - zygote_space->InvalidateMSpace(); + zygote_space->InvalidateAllocator(); // Create the zygote space mod union table. accounting::ModUnionTable* mod_union_table = new accounting::ModUnionTableCardCache("zygote space mod-union table", this, zygote_space); @@ -1639,8 +1672,8 @@ class VerifyReferenceVisitor { // Attmept to find the class inside of the recently freed objects. space::ContinuousSpace* ref_space = heap_->FindContinuousSpaceFromObject(ref, true); - if (ref_space != nullptr && ref_space->IsDlMallocSpace()) { - space::DlMallocSpace* space = ref_space->AsDlMallocSpace(); + if (ref_space != nullptr && ref_space->IsMallocSpace()) { + space::MallocSpace* space = ref_space->AsMallocSpace(); mirror::Class* ref_class = space->FindRecentFreedObject(ref); if (ref_class != nullptr) { LOG(ERROR) << "Reference " << ref << " found as a recently freed object with class " @@ -2280,6 +2313,14 @@ void Heap::RequestHeapTrim() { } } +void Heap::RevokeThreadLocalBuffers(Thread* thread) { + non_moving_space_->RevokeThreadLocalBuffers(thread); +} + +void Heap::RevokeAllThreadLocalBuffers() { + non_moving_space_->RevokeAllThreadLocalBuffers(); +} + bool Heap::IsGCRequestPending() const { return concurrent_start_bytes_ != std::numeric_limits::max(); } diff --git a/runtime/gc/heap.h b/runtime/gc/heap.h index 08bec99ad90..6e8890c4c65 100644 --- a/runtime/gc/heap.h +++ b/runtime/gc/heap.h @@ -69,6 +69,8 @@ namespace space { class DlMallocSpace; class ImageSpace; class LargeObjectSpace; + class MallocSpace; + class RosAllocSpace; class Space; class SpaceTest; class ContinuousMemMapAllocSpace; @@ -106,6 +108,9 @@ enum HeapVerificationMode { }; static constexpr HeapVerificationMode kDesiredHeapVerification = kNoHeapVerification; +// If true, use rosalloc/RosAllocSpace instead of dlmalloc/DlMallocSpace +static constexpr bool kUseRosAlloc = false; + class Heap { public: // If true, measure the total allocation time. @@ -413,6 +418,9 @@ class Heap { // Trim the managed and native heaps by releasing unused memory back to the OS. void Trim(); + void RevokeThreadLocalBuffers(Thread* thread); + void RevokeAllThreadLocalBuffers(); + accounting::HeapBitmap* GetLiveBitmap() SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_) { return live_bitmap_.get(); } @@ -447,7 +455,7 @@ class Heap { // Assumes there is only one image space. space::ImageSpace* GetImageSpace() const; - space::DlMallocSpace* GetNonMovingSpace() const { + space::MallocSpace* GetNonMovingSpace() const { return non_moving_space_; } @@ -539,6 +547,12 @@ class Heap { LOCKS_EXCLUDED(Locks::thread_suspend_count_lock_) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + // Try to allocate a number of bytes, this function never does any GCs. RosAllocSpace-specialized version. + mirror::Object* TryToAllocateInstrumented(Thread* self, space::RosAllocSpace* space, size_t alloc_size, + bool grow, size_t* bytes_allocated) + LOCKS_EXCLUDED(Locks::thread_suspend_count_lock_) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + mirror::Object* TryToAllocateUninstrumented(Thread* self, space::AllocSpace* space, size_t alloc_size, bool grow, size_t* bytes_allocated) LOCKS_EXCLUDED(Locks::thread_suspend_count_lock_) @@ -549,6 +563,11 @@ class Heap { LOCKS_EXCLUDED(Locks::thread_suspend_count_lock_) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + mirror::Object* TryToAllocateUninstrumented(Thread* self, space::RosAllocSpace* space, size_t alloc_size, + bool grow, size_t* bytes_allocated) + LOCKS_EXCLUDED(Locks::thread_suspend_count_lock_) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + void ThrowOutOfMemoryError(Thread* self, size_t byte_count, bool large_object_allocation) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); bool IsOutOfMemoryOnAllocation(size_t alloc_size, bool grow); @@ -642,7 +661,7 @@ class Heap { // A space where non-movable objects are allocated, when compaction is enabled it contains // Classes, ArtMethods, ArtFields, and non moving objects. - space::DlMallocSpace* non_moving_space_; + space::MallocSpace* non_moving_space_; // The large object space we are currently allocating into. space::LargeObjectSpace* large_object_space_; diff --git a/runtime/gc/space/dlmalloc_space-inl.h b/runtime/gc/space/dlmalloc_space-inl.h index fb2c66b2fb7..fbbba1faf28 100644 --- a/runtime/gc/space/dlmalloc_space-inl.h +++ b/runtime/gc/space/dlmalloc_space-inl.h @@ -18,6 +18,7 @@ #define ART_RUNTIME_GC_SPACE_DLMALLOC_SPACE_INL_H_ #include "dlmalloc_space.h" +#include "thread.h" namespace art { namespace gc { @@ -28,7 +29,7 @@ inline mirror::Object* DlMallocSpace::AllocNonvirtual(Thread* self, size_t num_b mirror::Object* obj; { MutexLock mu(self, lock_); - obj = AllocWithoutGrowthLocked(num_bytes, bytes_allocated); + obj = AllocWithoutGrowthLocked(self, num_bytes, bytes_allocated); } if (LIKELY(obj != NULL)) { // Zero freshly allocated memory, done while not holding the space's lock. @@ -37,7 +38,8 @@ inline mirror::Object* DlMallocSpace::AllocNonvirtual(Thread* self, size_t num_b return obj; } -inline mirror::Object* DlMallocSpace::AllocWithoutGrowthLocked(size_t num_bytes, size_t* bytes_allocated) { +inline mirror::Object* DlMallocSpace::AllocWithoutGrowthLocked(Thread* /*self*/, size_t num_bytes, + size_t* bytes_allocated) { mirror::Object* result = reinterpret_cast(mspace_malloc(mspace_, num_bytes)); if (LIKELY(result != NULL)) { if (kDebugSpaces) { diff --git a/runtime/gc/space/dlmalloc_space.cc b/runtime/gc/space/dlmalloc_space.cc index 1c7aa22b74a..fcac58895a2 100644 --- a/runtime/gc/space/dlmalloc_space.cc +++ b/runtime/gc/space/dlmalloc_space.cc @@ -13,13 +13,17 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + #include "dlmalloc_space.h" + #include "dlmalloc_space-inl.h" #include "gc/accounting/card_table.h" #include "gc/heap.h" +#include "mirror/class-inl.h" #include "mirror/object-inl.h" #include "runtime.h" #include "thread.h" +#include "thread_list.h" #include "utils.h" #include @@ -29,16 +33,6 @@ namespace art { namespace gc { namespace space { -// TODO: Remove define macro -#define CHECK_MEMORY_CALL(call, args, what) \ - do { \ - int rc = call args; \ - if (UNLIKELY(rc != 0)) { \ - errno = rc; \ - PLOG(FATAL) << # call << " failed for " << what; \ - } \ - } while (false) - static const bool kPrefetchDuringDlMallocFreeList = true; // Number of bytes to use as a red zone (rdz). A red zone of this size will be placed before and @@ -114,81 +108,38 @@ class ValgrindDlMallocSpace : public DlMallocSpace { DISALLOW_COPY_AND_ASSIGN(ValgrindDlMallocSpace); }; -size_t DlMallocSpace::bitmap_index_ = 0; - DlMallocSpace::DlMallocSpace(const std::string& name, MemMap* mem_map, void* mspace, byte* begin, - byte* end, byte* limit, size_t growth_limit) - : ContinuousMemMapAllocSpace(name, mem_map, begin, end, limit, kGcRetentionPolicyAlwaysCollect), - recent_free_pos_(0), total_bytes_freed_(0), total_objects_freed_(0), - lock_("allocation space lock", kAllocSpaceLock), mspace_(mspace), - growth_limit_(growth_limit) { + byte* end, byte* limit, size_t growth_limit) + : MallocSpace(name, mem_map, begin, end, limit, growth_limit), + total_bytes_freed_(0), total_objects_freed_(0), mspace_(mspace) { CHECK(mspace != NULL); - size_t bitmap_index = bitmap_index_++; - static const uintptr_t kGcCardSize = static_cast(accounting::CardTable::kCardSize); - CHECK(IsAligned(reinterpret_cast(mem_map->Begin()))); - CHECK(IsAligned(reinterpret_cast(mem_map->End()))); - live_bitmap_.reset(accounting::SpaceBitmap::Create( - StringPrintf("allocspace %s live-bitmap %d", name.c_str(), static_cast(bitmap_index)), - Begin(), Capacity())); - DCHECK(live_bitmap_.get() != NULL) << "could not create allocspace live bitmap #" << bitmap_index; - mark_bitmap_.reset(accounting::SpaceBitmap::Create( - StringPrintf("allocspace %s mark-bitmap %d", name.c_str(), static_cast(bitmap_index)), - Begin(), Capacity())); - DCHECK(live_bitmap_.get() != NULL) << "could not create allocspace mark bitmap #" << bitmap_index; - for (auto& freed : recent_freed_objects_) { - freed.first = nullptr; - freed.second = nullptr; - } } -DlMallocSpace* DlMallocSpace::Create(const std::string& name, size_t initial_size, size_t - growth_limit, size_t capacity, byte* requested_begin) { - // Memory we promise to dlmalloc before it asks for morecore. - // Note: making this value large means that large allocations are unlikely to succeed as dlmalloc - // will ask for this memory from sys_alloc which will fail as the footprint (this value plus the - // size of the large allocation) will be greater than the footprint limit. - size_t starting_size = kPageSize; +DlMallocSpace* DlMallocSpace::Create(const std::string& name, size_t initial_size, size_t growth_limit, + size_t capacity, byte* requested_begin) { uint64_t start_time = 0; if (VLOG_IS_ON(heap) || VLOG_IS_ON(startup)) { start_time = NanoTime(); - VLOG(startup) << "Space::CreateAllocSpace entering " << name + VLOG(startup) << "DlMallocSpace::Create entering " << name << " initial_size=" << PrettySize(initial_size) << " growth_limit=" << PrettySize(growth_limit) << " capacity=" << PrettySize(capacity) << " requested_begin=" << reinterpret_cast(requested_begin); } - // Sanity check arguments - if (starting_size > initial_size) { - initial_size = starting_size; - } - if (initial_size > growth_limit) { - LOG(ERROR) << "Failed to create alloc space (" << name << ") where the initial size (" - << PrettySize(initial_size) << ") is larger than its capacity (" - << PrettySize(growth_limit) << ")"; - return NULL; - } - if (growth_limit > capacity) { - LOG(ERROR) << "Failed to create alloc space (" << name << ") where the growth limit capacity (" - << PrettySize(growth_limit) << ") is larger than the capacity (" - << PrettySize(capacity) << ")"; - return NULL; - } - - // Page align growth limit and capacity which will be used to manage mmapped storage - growth_limit = RoundUp(growth_limit, kPageSize); - capacity = RoundUp(capacity, kPageSize); - - std::string error_msg; - UniquePtr mem_map(MemMap::MapAnonymous(name.c_str(), requested_begin, capacity, - PROT_READ | PROT_WRITE, &error_msg)); - if (mem_map.get() == NULL) { - LOG(ERROR) << "Failed to allocate pages for alloc space (" << name << ") of size " - << PrettySize(capacity) << ": " << error_msg; + // Memory we promise to dlmalloc before it asks for morecore. + // Note: making this value large means that large allocations are unlikely to succeed as dlmalloc + // will ask for this memory from sys_alloc which will fail as the footprint (this value plus the + // size of the large allocation) will be greater than the footprint limit. + size_t starting_size = kPageSize; + MemMap* mem_map = CreateMemMap(name, starting_size, &initial_size, &growth_limit, &capacity, + requested_begin); + if (mem_map == NULL) { + LOG(ERROR) << "Failed to create mem map for alloc space (" << name << ") of size " + << PrettySize(capacity); return NULL; } - - void* mspace = CreateMallocSpace(mem_map->Begin(), starting_size, initial_size); + void* mspace = CreateMspace(mem_map->Begin(), starting_size, initial_size); if (mspace == NULL) { LOG(ERROR) << "Failed to initialize mspace for alloc space (" << name << ")"; return NULL; @@ -201,24 +152,23 @@ DlMallocSpace* DlMallocSpace::Create(const std::string& name, size_t initial_siz } // Everything is set so record in immutable structure and leave - MemMap* mem_map_ptr = mem_map.release(); DlMallocSpace* space; - byte* begin = mem_map_ptr->Begin(); + byte* begin = mem_map->Begin(); if (RUNNING_ON_VALGRIND > 0) { - space = new ValgrindDlMallocSpace(name, mem_map_ptr, mspace, begin, end, begin + capacity, + space = new ValgrindDlMallocSpace(name, mem_map, mspace, begin, end, begin + capacity, growth_limit, initial_size); } else { - space = new DlMallocSpace(name, mem_map_ptr, mspace, begin, end, begin + capacity, growth_limit); + space = new DlMallocSpace(name, mem_map, mspace, begin, end, begin + capacity, growth_limit); } // We start out with only the initial size possibly containing objects. if (VLOG_IS_ON(heap) || VLOG_IS_ON(startup)) { - LOG(INFO) << "Space::CreateAllocSpace exiting (" << PrettyDuration(NanoTime() - start_time) + LOG(INFO) << "DlMallocSpace::Create exiting (" << PrettyDuration(NanoTime() - start_time) << " ) " << *space; } return space; } -void* DlMallocSpace::CreateMallocSpace(void* begin, size_t morecore_start, size_t initial_size) { +void* DlMallocSpace::CreateMspace(void* begin, size_t morecore_start, size_t initial_size) { // clear errno to allow PLOG on error errno = 0; // create mspace using our backing storage starting at begin and with a footprint of @@ -234,14 +184,6 @@ void* DlMallocSpace::CreateMallocSpace(void* begin, size_t morecore_start, size_ return msp; } -void DlMallocSpace::SwapBitmaps() { - live_bitmap_.swap(mark_bitmap_); - // Swap names to get more descriptive diagnostics. - std::string temp_name(live_bitmap_->GetName()); - live_bitmap_->SetName(mark_bitmap_->GetName()); - mark_bitmap_->SetName(temp_name); -} - mirror::Object* DlMallocSpace::Alloc(Thread* self, size_t num_bytes, size_t* bytes_allocated) { return AllocNonvirtual(self, num_bytes, bytes_allocated); } @@ -250,11 +192,11 @@ mirror::Object* DlMallocSpace::AllocWithGrowth(Thread* self, size_t num_bytes, s mirror::Object* result; { MutexLock mu(self, lock_); - // Grow as much as possible within the mspace. + // Grow as much as possible within the space. size_t max_allowed = Capacity(); mspace_set_footprint_limit(mspace_, max_allowed); // Try the allocation. - result = AllocWithoutGrowthLocked(num_bytes, bytes_allocated); + result = AllocWithoutGrowthLocked(self, num_bytes, bytes_allocated); // Shrink back down as small as possible. size_t footprint = mspace_footprint(mspace_); mspace_set_footprint_limit(mspace_, footprint); @@ -268,83 +210,9 @@ mirror::Object* DlMallocSpace::AllocWithGrowth(Thread* self, size_t num_bytes, s return result; } -void DlMallocSpace::SetGrowthLimit(size_t growth_limit) { - growth_limit = RoundUp(growth_limit, kPageSize); - growth_limit_ = growth_limit; - if (Size() > growth_limit_) { - end_ = begin_ + growth_limit; - } -} - -DlMallocSpace* DlMallocSpace::CreateZygoteSpace(const char* alloc_space_name) { - end_ = reinterpret_cast(RoundUp(reinterpret_cast(end_), kPageSize)); - DCHECK(IsAligned(begin_)); - DCHECK(IsAligned(end_)); - DCHECK(IsAligned(begin_)); - DCHECK(IsAligned(end_)); - size_t size = RoundUp(Size(), kPageSize); - // Trim the heap so that we minimize the size of the Zygote space. - Trim(); - // TODO: Not hardcode these in? - const size_t starting_size = kPageSize; - const size_t initial_size = 2 * MB; - // Remaining size is for the new alloc space. - const size_t growth_limit = growth_limit_ - size; - const size_t capacity = Capacity() - size; - VLOG(heap) << "Begin " << reinterpret_cast(begin_) << "\n" - << "End " << reinterpret_cast(end_) << "\n" - << "Size " << size << "\n" - << "GrowthLimit " << growth_limit_ << "\n" - << "Capacity " << Capacity(); - SetGrowthLimit(RoundUp(size, kPageSize)); - SetFootprintLimit(RoundUp(size, kPageSize)); - // FIXME: Do we need reference counted pointers here? - // Make the two spaces share the same mark bitmaps since the bitmaps span both of the spaces. - VLOG(heap) << "Creating new AllocSpace: "; - VLOG(heap) << "Size " << GetMemMap()->Size(); - VLOG(heap) << "GrowthLimit " << PrettySize(growth_limit); - VLOG(heap) << "Capacity " << PrettySize(capacity); - // Remap the tail. - std::string error_msg; - UniquePtr mem_map(GetMemMap()->RemapAtEnd(end_, alloc_space_name, - PROT_READ | PROT_WRITE, &error_msg)); - CHECK(mem_map.get() != nullptr) << error_msg; - void* mspace = CreateMallocSpace(end_, starting_size, initial_size); - // Protect memory beyond the initial size. - byte* end = mem_map->Begin() + starting_size; - if (capacity - initial_size > 0) { - CHECK_MEMORY_CALL(mprotect, (end, capacity - initial_size, PROT_NONE), alloc_space_name); - } - DlMallocSpace* alloc_space = - new DlMallocSpace(alloc_space_name, mem_map.release(), mspace, end_, end, limit_, - growth_limit); - SetLimit(End()); - live_bitmap_->SetHeapLimit(reinterpret_cast(End())); - CHECK_EQ(live_bitmap_->HeapLimit(), reinterpret_cast(End())); - mark_bitmap_->SetHeapLimit(reinterpret_cast(End())); - CHECK_EQ(mark_bitmap_->HeapLimit(), reinterpret_cast(End())); - VLOG(heap) << "zygote space creation done"; - return alloc_space; -} - -mirror::Class* DlMallocSpace::FindRecentFreedObject(const mirror::Object* obj) { - size_t pos = recent_free_pos_; - // Start at the most recently freed object and work our way back since there may be duplicates - // caused by dlmalloc reusing memory. - if (kRecentFreeCount > 0) { - for (size_t i = 0; i + 1 < kRecentFreeCount + 1; ++i) { - pos = pos != 0 ? pos - 1 : kRecentFreeMask; - if (recent_freed_objects_[pos].first == obj) { - return recent_freed_objects_[pos].second; - } - } - } - return nullptr; -} - -void DlMallocSpace::RegisterRecentFree(mirror::Object* ptr) { - recent_freed_objects_[recent_free_pos_] = std::make_pair(ptr, ptr->GetClass()); - recent_free_pos_ = (recent_free_pos_ + 1) & kRecentFreeMask; +MallocSpace* DlMallocSpace::CreateInstance(const std::string& name, MemMap* mem_map, void* allocator, byte* begin, byte* end, + byte* limit, size_t growth_limit) { + return new DlMallocSpace(name, mem_map, allocator, begin, end, limit, growth_limit); } size_t DlMallocSpace::Free(Thread* self, mirror::Object* ptr) { @@ -411,40 +279,11 @@ size_t DlMallocSpace::FreeList(Thread* self, size_t num_ptrs, mirror::Object** p // Callback from dlmalloc when it needs to increase the footprint extern "C" void* art_heap_morecore(void* mspace, intptr_t increment) { Heap* heap = Runtime::Current()->GetHeap(); - DCHECK_EQ(heap->GetNonMovingSpace()->GetMspace(), mspace); + DCHECK(heap->GetNonMovingSpace()->IsDlMallocSpace()); + DCHECK_EQ(heap->GetNonMovingSpace()->AsDlMallocSpace()->GetMspace(), mspace); return heap->GetNonMovingSpace()->MoreCore(increment); } -void* DlMallocSpace::MoreCore(intptr_t increment) { - lock_.AssertHeld(Thread::Current()); - byte* original_end = end_; - if (increment != 0) { - VLOG(heap) << "DlMallocSpace::MoreCore " << PrettySize(increment); - byte* new_end = original_end + increment; - if (increment > 0) { - // Should never be asked to increase the allocation beyond the capacity of the space. Enforced - // by mspace_set_footprint_limit. - CHECK_LE(new_end, Begin() + Capacity()); - CHECK_MEMORY_CALL(mprotect, (original_end, increment, PROT_READ | PROT_WRITE), GetName()); - } else { - // Should never be asked for negative footprint (ie before begin) - CHECK_GT(original_end + increment, Begin()); - // Advise we don't need the pages and protect them - // TODO: by removing permissions to the pages we may be causing TLB shoot-down which can be - // expensive (note the same isn't true for giving permissions to a page as the protected - // page shouldn't be in a TLB). We should investigate performance impact of just - // removing ignoring the memory protection change here and in Space::CreateAllocSpace. It's - // likely just a useful debug feature. - size_t size = -increment; - CHECK_MEMORY_CALL(madvise, (new_end, size, MADV_DONTNEED), GetName()); - CHECK_MEMORY_CALL(mprotect, (new_end, size, PROT_NONE), GetName()); - } - // Update end_ - end_ = new_end; - } - return original_end; -} - // Virtual functions can't get inlined. inline size_t DlMallocSpace::InternalAllocationSize(const mirror::Object* obj) { return AllocationSizeNonvirtual(obj); @@ -481,32 +320,9 @@ size_t DlMallocSpace::GetFootprintLimit() { return mspace_footprint_limit(mspace_); } -// Returns the old mark bitmap. -accounting::SpaceBitmap* DlMallocSpace::BindLiveToMarkBitmap() { - accounting::SpaceBitmap* live_bitmap = GetLiveBitmap(); - accounting::SpaceBitmap* mark_bitmap = mark_bitmap_.release(); - temp_bitmap_.reset(mark_bitmap); - mark_bitmap_.reset(live_bitmap); - return mark_bitmap; -} - -bool DlMallocSpace::HasBoundBitmaps() const { - return temp_bitmap_.get() != nullptr; -} - -void DlMallocSpace::UnBindBitmaps() { - CHECK(HasBoundBitmaps()); - // At this point, the temp_bitmap holds our old mark bitmap. - accounting::SpaceBitmap* new_bitmap = temp_bitmap_.release(); - CHECK_EQ(mark_bitmap_.release(), live_bitmap_.get()); - mark_bitmap_.reset(new_bitmap); - DCHECK(temp_bitmap_.get() == NULL); -} - - void DlMallocSpace::SetFootprintLimit(size_t new_size) { MutexLock mu(Thread::Current(), lock_); - VLOG(heap) << "DLMallocSpace::SetFootprintLimit " << PrettySize(new_size); + VLOG(heap) << "DlMallocSpace::SetFootprintLimit " << PrettySize(new_size); // Compare against the actual footprint, rather than the Size(), because the heap may not have // grown all the way to the allowed size yet. size_t current_space_size = mspace_footprint(mspace_); @@ -517,14 +333,6 @@ void DlMallocSpace::SetFootprintLimit(size_t new_size) { mspace_set_footprint_limit(mspace_, new_size); } -void DlMallocSpace::Dump(std::ostream& os) const { - os << GetType() - << " begin=" << reinterpret_cast(Begin()) - << ",end=" << reinterpret_cast(End()) - << ",size=" << PrettySize(Size()) << ",capacity=" << PrettySize(Capacity()) - << ",name=\"" << GetName() << "\"]"; -} - uint64_t DlMallocSpace::GetBytesAllocated() { if (mspace_ != nullptr) { MutexLock mu(Thread::Current(), lock_); @@ -547,6 +355,12 @@ uint64_t DlMallocSpace::GetObjectsAllocated() { } } +#ifndef NDEBUG +void DlMallocSpace::CheckMoreCoreForPrecondition() { + lock_.AssertHeld(Thread::Current()); +} +#endif + } // namespace space } // namespace gc } // namespace art diff --git a/runtime/gc/space/dlmalloc_space.h b/runtime/gc/space/dlmalloc_space.h index 59dafe3f2a2..63b1c216ceb 100644 --- a/runtime/gc/space/dlmalloc_space.h +++ b/runtime/gc/space/dlmalloc_space.h @@ -18,6 +18,7 @@ #define ART_RUNTIME_GC_SPACE_DLMALLOC_SPACE_H_ #include "gc/allocator/dlmalloc.h" +#include "malloc_space.h" #include "space.h" namespace art { @@ -30,33 +31,19 @@ namespace collector { namespace space { // An alloc space is a space where objects may be allocated and garbage collected. -class DlMallocSpace : public ContinuousMemMapAllocSpace { +class DlMallocSpace : public MallocSpace { public: - typedef void(*WalkCallback)(void *start, void *end, size_t num_bytes, void* callback_arg); - - SpaceType GetType() const { - if (GetGcRetentionPolicy() == kGcRetentionPolicyFullCollect) { - return kSpaceTypeZygoteSpace; - } else { - return kSpaceTypeAllocSpace; - } - } - // Create a AllocSpace with the requested sizes. The requested + // Create a DlMallocSpace with the requested sizes. The requested // base address is not guaranteed to be granted, if it is required, - // the caller should call Begin on the returned space to confirm - // the request was granted. + // the caller should call Begin on the returned space to confirm the + // request was granted. static DlMallocSpace* Create(const std::string& name, size_t initial_size, size_t growth_limit, size_t capacity, byte* requested_begin); - // Allocate num_bytes without allowing the underlying mspace to grow. virtual mirror::Object* AllocWithGrowth(Thread* self, size_t num_bytes, size_t* bytes_allocated) LOCKS_EXCLUDED(lock_); - - // Allocate num_bytes allowing the underlying mspace to grow. virtual mirror::Object* Alloc(Thread* self, size_t num_bytes, size_t* bytes_allocated); - - // Return the storage space required by obj. virtual size_t AllocationSize(const mirror::Object* obj); virtual size_t Free(Thread* self, mirror::Object* ptr); virtual size_t FreeList(Thread* self, size_t num_ptrs, mirror::Object** ptrs); @@ -64,17 +51,19 @@ class DlMallocSpace : public ContinuousMemMapAllocSpace { mirror::Object* AllocNonvirtual(Thread* self, size_t num_bytes, size_t* bytes_allocated); size_t AllocationSizeNonvirtual(const mirror::Object* obj) { - return mspace_usable_size(const_cast(reinterpret_cast(obj))) + - kChunkOverhead; + void* obj_ptr = const_cast(reinterpret_cast(obj)); + return mspace_usable_size(obj_ptr) + kChunkOverhead; } - void* MoreCore(intptr_t increment); +#ifndef NDEBUG + // Override only in the debug build. + void CheckMoreCoreForPrecondition(); +#endif void* GetMspace() const { return mspace_; } - // Hands unused pages back to the system. size_t Trim(); // Perform a mspace_inspect_all which calls back for each allocation chunk. The chunk may not be @@ -93,39 +82,8 @@ class DlMallocSpace : public ContinuousMemMapAllocSpace { // allocations fail we GC before increasing the footprint limit and allowing the mspace to grow. void SetFootprintLimit(size_t limit); - // Removes the fork time growth limit on capacity, allowing the application to allocate up to the - // maximum reserved size of the heap. - void ClearGrowthLimit() { - growth_limit_ = NonGrowthLimitCapacity(); - } - - // Override capacity so that we only return the possibly limited capacity - size_t Capacity() const { - return growth_limit_; - } - - // The total amount of memory reserved for the alloc space. - size_t NonGrowthLimitCapacity() const { - return GetMemMap()->Size(); - } - - accounting::SpaceBitmap* GetLiveBitmap() const { - return live_bitmap_.get(); - } - - accounting::SpaceBitmap* GetMarkBitmap() const { - return mark_bitmap_.get(); - } - - void Dump(std::ostream& os) const; - - void SetGrowthLimit(size_t growth_limit); - - // Swap the live and mark bitmaps of this space. This is used by the GC for concurrent sweeping. - void SwapBitmaps(); - - // Turn ourself into a zygote space and return a new alloc space which has our unused memory. - DlMallocSpace* CreateZygoteSpace(const char* alloc_space_name); + MallocSpace* CreateInstance(const std::string& name, MemMap* mem_map, void* allocator, + byte* begin, byte* end, byte* limit, size_t growth_limit); uint64_t GetBytesAllocated(); uint64_t GetObjectsAllocated(); @@ -136,66 +94,45 @@ class DlMallocSpace : public ContinuousMemMapAllocSpace { return GetObjectsAllocated() + total_objects_freed_; } - // Returns the old mark bitmap. - accounting::SpaceBitmap* BindLiveToMarkBitmap(); - bool HasBoundBitmaps() const; - void UnBindBitmaps(); - // Returns the class of a recently freed object. mirror::Class* FindRecentFreedObject(const mirror::Object* obj); - // Used to ensure that failure happens when you free / allocate into an invalidated space. If we - // don't do this we may get heap corruption instead of a segfault at null. - void InvalidateMSpace() { + virtual void InvalidateAllocator() { mspace_ = nullptr; } + virtual bool IsDlMallocSpace() const { + return true; + } + virtual DlMallocSpace* AsDlMallocSpace() { + return this; + } + protected: DlMallocSpace(const std::string& name, MemMap* mem_map, void* mspace, byte* begin, byte* end, byte* limit, size_t growth_limit); private: size_t InternalAllocationSize(const mirror::Object* obj); - mirror::Object* AllocWithoutGrowthLocked(size_t num_bytes, size_t* bytes_allocated) - EXCLUSIVE_LOCKS_REQUIRED(lock_); - bool Init(size_t initial_size, size_t maximum_size, size_t growth_size, byte* requested_base); - void RegisterRecentFree(mirror::Object* ptr) EXCLUSIVE_LOCKS_REQUIRED(lock_); - static void* CreateMallocSpace(void* base, size_t morecore_start, size_t initial_size); - UniquePtr live_bitmap_; - UniquePtr mark_bitmap_; - UniquePtr temp_bitmap_; + mirror::Object* AllocWithoutGrowthLocked(Thread* self, size_t num_bytes, size_t* bytes_allocated) + EXCLUSIVE_LOCKS_REQUIRED(lock_); - // Recent allocation buffer. - static constexpr size_t kRecentFreeCount = kDebugSpaces ? (1 << 16) : 0; - static constexpr size_t kRecentFreeMask = kRecentFreeCount - 1; - std::pair recent_freed_objects_[kRecentFreeCount]; - size_t recent_free_pos_; + void* CreateAllocator(void* base, size_t morecore_start, size_t initial_size) { + return CreateMspace(base, morecore_start, initial_size); + } + static void* CreateMspace(void* base, size_t morecore_start, size_t initial_size); // Approximate number of bytes and objects which have been deallocated in the space. size_t total_bytes_freed_; size_t total_objects_freed_; - static size_t bitmap_index_; - // The boundary tag overhead. static const size_t kChunkOverhead = kWordSize; - // Used to ensure mutual exclusion when the allocation spaces data structures are being modified. - Mutex lock_ DEFAULT_MUTEX_ACQUIRED_AFTER; - // Underlying malloc space void* mspace_; - // The capacity of the alloc space until such time that ClearGrowthLimit is called. - // The underlying mem_map_ controls the maximum size we allow the heap to grow to. The growth - // limit is a value <= to the mem_map_ capacity used for ergonomic reasons because of the zygote. - // Prior to forking the zygote the heap will have a maximally sized mem_map_ but the growth_limit_ - // will be set to a lower value. The growth_limit_ is used as the capacity of the alloc_space_, - // however, capacity normally can't vary. In the case of the growth_limit_ it can be cleared - // one time by a call to ClearGrowthLimit. - size_t growth_limit_; - friend class collector::MarkSweep; DISALLOW_COPY_AND_ASSIGN(DlMallocSpace); diff --git a/runtime/gc/space/large_object_space.h b/runtime/gc/space/large_object_space.h index 07fb288576a..d374ad3a82f 100644 --- a/runtime/gc/space/large_object_space.h +++ b/runtime/gc/space/large_object_space.h @@ -116,7 +116,8 @@ class FreeListSpace : public LargeObjectSpace { virtual ~FreeListSpace(); static FreeListSpace* Create(const std::string& name, byte* requested_begin, size_t capacity); - size_t AllocationSize(const mirror::Object* obj) EXCLUSIVE_LOCKS_REQUIRED(lock_); + size_t AllocationSize(const mirror::Object* obj) + EXCLUSIVE_LOCKS_REQUIRED(lock_); mirror::Object* Alloc(Thread* self, size_t num_bytes, size_t* bytes_allocated); size_t Free(Thread* self, mirror::Object* obj); bool Contains(const mirror::Object* obj) const; diff --git a/runtime/gc/space/malloc_space.cc b/runtime/gc/space/malloc_space.cc new file mode 100644 index 00000000000..785b5ed2760 --- /dev/null +++ b/runtime/gc/space/malloc_space.cc @@ -0,0 +1,243 @@ +/* + * Copyright (C) 2013 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 "malloc_space.h" + +#include "gc/accounting/card_table.h" +#include "gc/heap.h" +#include "mirror/class-inl.h" +#include "mirror/object-inl.h" +#include "runtime.h" +#include "thread.h" +#include "thread_list.h" +#include "utils.h" + +namespace art { +namespace gc { +namespace space { + +size_t MallocSpace::bitmap_index_ = 0; + +MallocSpace::MallocSpace(const std::string& name, MemMap* mem_map, + byte* begin, byte* end, byte* limit, size_t growth_limit) + : ContinuousMemMapAllocSpace(name, mem_map, begin, end, limit, kGcRetentionPolicyAlwaysCollect), + recent_free_pos_(0), lock_("allocation space lock", kAllocSpaceLock), + growth_limit_(growth_limit) { + size_t bitmap_index = bitmap_index_++; + static const uintptr_t kGcCardSize = static_cast(accounting::CardTable::kCardSize); + CHECK(IsAligned(reinterpret_cast(mem_map->Begin()))); + CHECK(IsAligned(reinterpret_cast(mem_map->End()))); + live_bitmap_.reset(accounting::SpaceBitmap::Create( + StringPrintf("allocspace %s live-bitmap %d", name.c_str(), static_cast(bitmap_index)), + Begin(), Capacity())); + DCHECK(live_bitmap_.get() != NULL) << "could not create allocspace live bitmap #" << bitmap_index; + mark_bitmap_.reset(accounting::SpaceBitmap::Create( + StringPrintf("allocspace %s mark-bitmap %d", name.c_str(), static_cast(bitmap_index)), + Begin(), Capacity())); + DCHECK(live_bitmap_.get() != NULL) << "could not create allocspace mark bitmap #" << bitmap_index; + for (auto& freed : recent_freed_objects_) { + freed.first = nullptr; + freed.second = nullptr; + } +} + +MemMap* MallocSpace::CreateMemMap(const std::string& name, size_t starting_size, size_t* initial_size, + size_t* growth_limit, size_t* capacity, byte* requested_begin) { + // Sanity check arguments + if (starting_size > *initial_size) { + *initial_size = starting_size; + } + if (*initial_size > *growth_limit) { + LOG(ERROR) << "Failed to create alloc space (" << name << ") where the initial size (" + << PrettySize(*initial_size) << ") is larger than its capacity (" + << PrettySize(*growth_limit) << ")"; + return NULL; + } + if (*growth_limit > *capacity) { + LOG(ERROR) << "Failed to create alloc space (" << name << ") where the growth limit capacity (" + << PrettySize(*growth_limit) << ") is larger than the capacity (" + << PrettySize(*capacity) << ")"; + return NULL; + } + + // Page align growth limit and capacity which will be used to manage mmapped storage + *growth_limit = RoundUp(*growth_limit, kPageSize); + *capacity = RoundUp(*capacity, kPageSize); + + std::string error_msg; + MemMap* mem_map = MemMap::MapAnonymous(name.c_str(), requested_begin, *capacity, + PROT_READ | PROT_WRITE, &error_msg); + if (mem_map == NULL) { + LOG(ERROR) << "Failed to allocate pages for alloc space (" << name << ") of size " + << PrettySize(*capacity) << ": " << error_msg; + return NULL; + } + return mem_map; +} + +void MallocSpace::SwapBitmaps() { + live_bitmap_.swap(mark_bitmap_); + // Swap names to get more descriptive diagnostics. + std::string temp_name(live_bitmap_->GetName()); + live_bitmap_->SetName(mark_bitmap_->GetName()); + mark_bitmap_->SetName(temp_name); +} + +mirror::Class* MallocSpace::FindRecentFreedObject(const mirror::Object* obj) { + size_t pos = recent_free_pos_; + // Start at the most recently freed object and work our way back since there may be duplicates + // caused by dlmalloc reusing memory. + if (kRecentFreeCount > 0) { + for (size_t i = 0; i + 1 < kRecentFreeCount + 1; ++i) { + pos = pos != 0 ? pos - 1 : kRecentFreeMask; + if (recent_freed_objects_[pos].first == obj) { + return recent_freed_objects_[pos].second; + } + } + } + return nullptr; +} + +void MallocSpace::RegisterRecentFree(mirror::Object* ptr) { + recent_freed_objects_[recent_free_pos_] = std::make_pair(ptr, ptr->GetClass()); + recent_free_pos_ = (recent_free_pos_ + 1) & kRecentFreeMask; +} + +void MallocSpace::SetGrowthLimit(size_t growth_limit) { + growth_limit = RoundUp(growth_limit, kPageSize); + growth_limit_ = growth_limit; + if (Size() > growth_limit_) { + end_ = begin_ + growth_limit; + } +} + +void* MallocSpace::MoreCore(intptr_t increment) { + CheckMoreCoreForPrecondition(); + byte* original_end = end_; + if (increment != 0) { + VLOG(heap) << "MallocSpace::MoreCore " << PrettySize(increment); + byte* new_end = original_end + increment; + if (increment > 0) { + // Should never be asked to increase the allocation beyond the capacity of the space. Enforced + // by mspace_set_footprint_limit. + CHECK_LE(new_end, Begin() + Capacity()); + CHECK_MEMORY_CALL(mprotect, (original_end, increment, PROT_READ | PROT_WRITE), GetName()); + } else { + // Should never be asked for negative footprint (ie before begin). Zero footprint is ok. + CHECK_GE(original_end + increment, Begin()); + // Advise we don't need the pages and protect them + // TODO: by removing permissions to the pages we may be causing TLB shoot-down which can be + // expensive (note the same isn't true for giving permissions to a page as the protected + // page shouldn't be in a TLB). We should investigate performance impact of just + // removing ignoring the memory protection change here and in Space::CreateAllocSpace. It's + // likely just a useful debug feature. + size_t size = -increment; + CHECK_MEMORY_CALL(madvise, (new_end, size, MADV_DONTNEED), GetName()); + CHECK_MEMORY_CALL(mprotect, (new_end, size, PROT_NONE), GetName()); + } + // Update end_ + end_ = new_end; + } + return original_end; +} + +// Returns the old mark bitmap. +accounting::SpaceBitmap* MallocSpace::BindLiveToMarkBitmap() { + accounting::SpaceBitmap* live_bitmap = GetLiveBitmap(); + accounting::SpaceBitmap* mark_bitmap = mark_bitmap_.release(); + temp_bitmap_.reset(mark_bitmap); + mark_bitmap_.reset(live_bitmap); + return mark_bitmap; +} + +bool MallocSpace::HasBoundBitmaps() const { + return temp_bitmap_.get() != nullptr; +} + +void MallocSpace::UnBindBitmaps() { + CHECK(HasBoundBitmaps()); + // At this point, the temp_bitmap holds our old mark bitmap. + accounting::SpaceBitmap* new_bitmap = temp_bitmap_.release(); + CHECK_EQ(mark_bitmap_.release(), live_bitmap_.get()); + mark_bitmap_.reset(new_bitmap); + DCHECK(temp_bitmap_.get() == NULL); +} + +MallocSpace* MallocSpace::CreateZygoteSpace(const char* alloc_space_name) { + // For RosAlloc, revoke thread local runs before creating a new + // alloc space so that we won't mix thread local runs from different + // alloc spaces. + RevokeAllThreadLocalBuffers(); + end_ = reinterpret_cast(RoundUp(reinterpret_cast(end_), kPageSize)); + DCHECK(IsAligned(begin_)); + DCHECK(IsAligned(end_)); + DCHECK(IsAligned(begin_)); + DCHECK(IsAligned(end_)); + size_t size = RoundUp(Size(), kPageSize); + // Trim the heap so that we minimize the size of the Zygote space. + Trim(); + // TODO: Not hardcode these in? + const size_t starting_size = kPageSize; + const size_t initial_size = 2 * MB; + // Remaining size is for the new alloc space. + const size_t growth_limit = growth_limit_ - size; + const size_t capacity = Capacity() - size; + VLOG(heap) << "Begin " << reinterpret_cast(begin_) << "\n" + << "End " << reinterpret_cast(end_) << "\n" + << "Size " << size << "\n" + << "GrowthLimit " << growth_limit_ << "\n" + << "Capacity " << Capacity(); + SetGrowthLimit(RoundUp(size, kPageSize)); + SetFootprintLimit(RoundUp(size, kPageSize)); + // FIXME: Do we need reference counted pointers here? + // Make the two spaces share the same mark bitmaps since the bitmaps span both of the spaces. + VLOG(heap) << "Creating new AllocSpace: "; + VLOG(heap) << "Size " << GetMemMap()->Size(); + VLOG(heap) << "GrowthLimit " << PrettySize(growth_limit); + VLOG(heap) << "Capacity " << PrettySize(capacity); + // Remap the tail. + std::string error_msg; + UniquePtr mem_map(GetMemMap()->RemapAtEnd(end_, alloc_space_name, + PROT_READ | PROT_WRITE, &error_msg)); + CHECK(mem_map.get() != nullptr) << error_msg; + void* allocator = CreateAllocator(end_, starting_size, initial_size); + // Protect memory beyond the initial size. + byte* end = mem_map->Begin() + starting_size; + if (capacity - initial_size > 0) { + CHECK_MEMORY_CALL(mprotect, (end, capacity - initial_size, PROT_NONE), alloc_space_name); + } + MallocSpace* alloc_space = CreateInstance(alloc_space_name, mem_map.release(), allocator, + end_, end, limit_, growth_limit); + SetLimit(End()); + live_bitmap_->SetHeapLimit(reinterpret_cast(End())); + CHECK_EQ(live_bitmap_->HeapLimit(), reinterpret_cast(End())); + mark_bitmap_->SetHeapLimit(reinterpret_cast(End())); + CHECK_EQ(mark_bitmap_->HeapLimit(), reinterpret_cast(End())); + VLOG(heap) << "zygote space creation done"; + return alloc_space; +} + +void MallocSpace::Dump(std::ostream& os) const { + os << GetType() + << " begin=" << reinterpret_cast(Begin()) + << ",end=" << reinterpret_cast(End()) + << ",size=" << PrettySize(Size()) << ",capacity=" << PrettySize(Capacity()) + << ",name=\"" << GetName() << "\"]"; +} + +} // namespace space +} // namespace gc +} // namespace art diff --git a/runtime/gc/space/malloc_space.h b/runtime/gc/space/malloc_space.h new file mode 100644 index 00000000000..bd21a4d557c --- /dev/null +++ b/runtime/gc/space/malloc_space.h @@ -0,0 +1,191 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ART_RUNTIME_GC_SPACE_MALLOC_SPACE_H_ +#define ART_RUNTIME_GC_SPACE_MALLOC_SPACE_H_ + +#include "space.h" + +namespace art { +namespace gc { + +namespace collector { + class MarkSweep; +} // namespace collector + +namespace space { + +// TODO: Remove define macro +#define CHECK_MEMORY_CALL(call, args, what) \ + do { \ + int rc = call args; \ + if (UNLIKELY(rc != 0)) { \ + errno = rc; \ + PLOG(FATAL) << # call << " failed for " << what; \ + } \ + } while (false) + +// const bool kUseRosAlloc = true; + +// A common parent of DlMallocSpace and RosAllocSpace. +class MallocSpace : public ContinuousMemMapAllocSpace { + public: + typedef void(*WalkCallback)(void *start, void *end, size_t num_bytes, void* callback_arg); + + SpaceType GetType() const { + if (GetGcRetentionPolicy() == kGcRetentionPolicyFullCollect) { + return kSpaceTypeZygoteSpace; + } else { + return kSpaceTypeAllocSpace; + } + } + + // Allocate num_bytes without allowing the underlying space to grow. + virtual mirror::Object* AllocWithGrowth(Thread* self, size_t num_bytes, + size_t* bytes_allocated) = 0; + // Allocate num_bytes allowing the underlying space to grow. + virtual mirror::Object* Alloc(Thread* self, size_t num_bytes, size_t* bytes_allocated) = 0; + // Return the storage space required by obj. + virtual size_t AllocationSize(const mirror::Object* obj) = 0; + virtual size_t Free(Thread* self, mirror::Object* ptr) = 0; + virtual size_t FreeList(Thread* self, size_t num_ptrs, mirror::Object** ptrs) = 0; + +#ifndef NDEBUG + virtual void CheckMoreCoreForPrecondition() {} // to be overridden in the debug build. +#else + void CheckMoreCoreForPrecondition() {} // no-op in the non-debug build. +#endif + + void* MoreCore(intptr_t increment); + + // Hands unused pages back to the system. + virtual size_t Trim() = 0; + + // Perform a mspace_inspect_all which calls back for each allocation chunk. The chunk may not be + // in use, indicated by num_bytes equaling zero. + virtual void Walk(WalkCallback callback, void* arg) = 0; + + // Returns the number of bytes that the space has currently obtained from the system. This is + // greater or equal to the amount of live data in the space. + virtual size_t GetFootprint() = 0; + + // Returns the number of bytes that the heap is allowed to obtain from the system via MoreCore. + virtual size_t GetFootprintLimit() = 0; + + // Set the maximum number of bytes that the heap is allowed to obtain from the system via + // MoreCore. Note this is used to stop the mspace growing beyond the limit to Capacity. When + // allocations fail we GC before increasing the footprint limit and allowing the mspace to grow. + virtual void SetFootprintLimit(size_t limit) = 0; + + // Removes the fork time growth limit on capacity, allowing the application to allocate up to the + // maximum reserved size of the heap. + void ClearGrowthLimit() { + growth_limit_ = NonGrowthLimitCapacity(); + } + + // Override capacity so that we only return the possibly limited capacity + size_t Capacity() const { + return growth_limit_; + } + + // The total amount of memory reserved for the alloc space. + size_t NonGrowthLimitCapacity() const { + return GetMemMap()->Size(); + } + + accounting::SpaceBitmap* GetLiveBitmap() const { + return live_bitmap_.get(); + } + + accounting::SpaceBitmap* GetMarkBitmap() const { + return mark_bitmap_.get(); + } + + void Dump(std::ostream& os) const; + + void SetGrowthLimit(size_t growth_limit); + + // Swap the live and mark bitmaps of this space. This is used by the GC for concurrent sweeping. + void SwapBitmaps(); + + virtual MallocSpace* CreateInstance(const std::string& name, MemMap* mem_map, void* allocator, + byte* begin, byte* end, byte* limit, size_t growth_limit) = 0; + + // Turn ourself into a zygote space and return a new alloc space which has our unused memory. + MallocSpace* CreateZygoteSpace(const char* alloc_space_name); + + virtual uint64_t GetBytesAllocated() = 0; + virtual uint64_t GetObjectsAllocated() = 0; + virtual uint64_t GetTotalBytesAllocated() = 0; + virtual uint64_t GetTotalObjectsAllocated() = 0; + + // Returns the old mark bitmap. + accounting::SpaceBitmap* BindLiveToMarkBitmap(); + bool HasBoundBitmaps() const; + void UnBindBitmaps(); + + // Returns the class of a recently freed object. + mirror::Class* FindRecentFreedObject(const mirror::Object* obj); + + // Used to ensure that failure happens when you free / allocate into an invalidated space. If we + // don't do this we may get heap corruption instead of a segfault at null. + virtual void InvalidateAllocator() = 0; + + protected: + MallocSpace(const std::string& name, MemMap* mem_map, byte* begin, byte* end, + byte* limit, size_t growth_limit); + + static MemMap* CreateMemMap(const std::string& name, size_t starting_size, size_t* initial_size, + size_t* growth_limit, size_t* capacity, byte* requested_begin); + + virtual void* CreateAllocator(void* base, size_t morecore_start, size_t initial_size) = 0; + + void RegisterRecentFree(mirror::Object* ptr) EXCLUSIVE_LOCKS_REQUIRED(lock_); + + UniquePtr live_bitmap_; + UniquePtr mark_bitmap_; + UniquePtr temp_bitmap_; + + // Recent allocation buffer. + static constexpr size_t kRecentFreeCount = kDebugSpaces ? (1 << 16) : 0; + static constexpr size_t kRecentFreeMask = kRecentFreeCount - 1; + std::pair recent_freed_objects_[kRecentFreeCount]; + size_t recent_free_pos_; + + static size_t bitmap_index_; + + // Used to ensure mutual exclusion when the allocation spaces data structures are being modified. + Mutex lock_ DEFAULT_MUTEX_ACQUIRED_AFTER; + + // The capacity of the alloc space until such time that ClearGrowthLimit is called. + // The underlying mem_map_ controls the maximum size we allow the heap to grow to. The growth + // limit is a value <= to the mem_map_ capacity used for ergonomic reasons because of the zygote. + // Prior to forking the zygote the heap will have a maximally sized mem_map_ but the growth_limit_ + // will be set to a lower value. The growth_limit_ is used as the capacity of the alloc_space_, + // however, capacity normally can't vary. In the case of the growth_limit_ it can be cleared + // one time by a call to ClearGrowthLimit. + size_t growth_limit_; + + friend class collector::MarkSweep; + + DISALLOW_COPY_AND_ASSIGN(MallocSpace); +}; + +} // namespace space +} // namespace gc +} // namespace art + +#endif // ART_RUNTIME_GC_SPACE_DLMALLOC_SPACE_H_ diff --git a/runtime/gc/space/rosalloc_space-inl.h b/runtime/gc/space/rosalloc_space-inl.h new file mode 100644 index 00000000000..92c69afa463 --- /dev/null +++ b/runtime/gc/space/rosalloc_space-inl.h @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ART_RUNTIME_GC_SPACE_ROSALLOC_SPACE_INL_H_ +#define ART_RUNTIME_GC_SPACE_ROSALLOC_SPACE_INL_H_ + +#include "rosalloc_space.h" +#include "thread.h" + +namespace art { +namespace gc { +namespace space { + +inline mirror::Object* RosAllocSpace::AllocNonvirtual(Thread* self, size_t num_bytes, + size_t* bytes_allocated) { + mirror::Object* obj; + obj = AllocWithoutGrowthLocked(self, num_bytes, bytes_allocated); + // RosAlloc zeroes memory internally. + return obj; +} + +inline mirror::Object* RosAllocSpace::AllocWithoutGrowthLocked(Thread* self, size_t num_bytes, + size_t* bytes_allocated) { + size_t rosalloc_size = 0; + mirror::Object* result = reinterpret_cast(rosalloc_->Alloc(self, num_bytes, + &rosalloc_size)); + if (LIKELY(result != NULL)) { + if (kDebugSpaces) { + CHECK(Contains(result)) << "Allocation (" << reinterpret_cast(result) + << ") not in bounds of allocation space " << *this; + } + DCHECK(bytes_allocated != NULL); + *bytes_allocated = rosalloc_size; + } + return result; +} + +} // namespace space +} // namespace gc +} // namespace art + +#endif // ART_RUNTIME_GC_SPACE_ROSALLOC_SPACE_INL_H_ diff --git a/runtime/gc/space/rosalloc_space.cc b/runtime/gc/space/rosalloc_space.cc new file mode 100644 index 00000000000..72692d6ca38 --- /dev/null +++ b/runtime/gc/space/rosalloc_space.cc @@ -0,0 +1,306 @@ +/* + * Copyright (C) 2013 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 "rosalloc_space.h" + +#include "rosalloc_space-inl.h" +#include "gc/accounting/card_table.h" +#include "gc/heap.h" +#include "mirror/class-inl.h" +#include "mirror/object-inl.h" +#include "runtime.h" +#include "thread.h" +#include "thread_list.h" +#include "utils.h" + +#include +#include + +namespace art { +namespace gc { +namespace space { + +static const bool kPrefetchDuringRosAllocFreeList = true; + +RosAllocSpace::RosAllocSpace(const std::string& name, MemMap* mem_map, + art::gc::allocator::RosAlloc* rosalloc, byte* begin, byte* end, + byte* limit, size_t growth_limit) + : MallocSpace(name, mem_map, begin, end, limit, growth_limit), rosalloc_(rosalloc) { + CHECK(rosalloc != NULL); +} + +RosAllocSpace* RosAllocSpace::Create(const std::string& name, size_t initial_size, size_t growth_limit, + size_t capacity, byte* requested_begin) { + uint64_t start_time = 0; + if (VLOG_IS_ON(heap) || VLOG_IS_ON(startup)) { + start_time = NanoTime(); + VLOG(startup) << "RosAllocSpace::Create entering " << name + << " initial_size=" << PrettySize(initial_size) + << " growth_limit=" << PrettySize(growth_limit) + << " capacity=" << PrettySize(capacity) + << " requested_begin=" << reinterpret_cast(requested_begin); + } + + // Memory we promise to rosalloc before it asks for morecore. + // Note: making this value large means that large allocations are unlikely to succeed as rosalloc + // will ask for this memory from sys_alloc which will fail as the footprint (this value plus the + // size of the large allocation) will be greater than the footprint limit. + size_t starting_size = kPageSize; + MemMap* mem_map = CreateMemMap(name, starting_size, &initial_size, &growth_limit, &capacity, + requested_begin); + if (mem_map == NULL) { + LOG(ERROR) << "Failed to create mem map for alloc space (" << name << ") of size " + << PrettySize(capacity); + return NULL; + } + allocator::RosAlloc* rosalloc = CreateRosAlloc(mem_map->Begin(), starting_size, initial_size); + if (rosalloc == NULL) { + LOG(ERROR) << "Failed to initialize rosalloc for alloc space (" << name << ")"; + return NULL; + } + + // Protect memory beyond the initial size. + byte* end = mem_map->Begin() + starting_size; + if (capacity - initial_size > 0) { + CHECK_MEMORY_CALL(mprotect, (end, capacity - initial_size, PROT_NONE), name); + } + + // Everything is set so record in immutable structure and leave + RosAllocSpace* space; + byte* begin = mem_map->Begin(); + if (RUNNING_ON_VALGRIND > 0) { + // TODO: support valgrind. + LOG(FATAL) << "Unimplemented"; + space = NULL; + } else { + space = new RosAllocSpace(name, mem_map, rosalloc, begin, end, begin + capacity, growth_limit); + } + // We start out with only the initial size possibly containing objects. + if (VLOG_IS_ON(heap) || VLOG_IS_ON(startup)) { + LOG(INFO) << "RosAllocSpace::Create exiting (" << PrettyDuration(NanoTime() - start_time) + << " ) " << *space; + } + return space; +} + +allocator::RosAlloc* RosAllocSpace::CreateRosAlloc(void* begin, size_t morecore_start, size_t initial_size) { + // clear errno to allow PLOG on error + errno = 0; + // create rosalloc using our backing storage starting at begin and + // with a footprint of morecore_start. When morecore_start bytes of + // memory is exhaused morecore will be called. + allocator::RosAlloc* rosalloc = new art::gc::allocator::RosAlloc(begin, morecore_start); + if (rosalloc != NULL) { + rosalloc->SetFootprintLimit(initial_size); + } else { + PLOG(ERROR) << "RosAlloc::Create failed"; + } + return rosalloc; +} + +mirror::Object* RosAllocSpace::Alloc(Thread* self, size_t num_bytes, size_t* bytes_allocated) { + return AllocNonvirtual(self, num_bytes, bytes_allocated); +} + +mirror::Object* RosAllocSpace::AllocWithGrowth(Thread* self, size_t num_bytes, size_t* bytes_allocated) { + mirror::Object* result; + { + MutexLock mu(self, lock_); + // Grow as much as possible within the space. + size_t max_allowed = Capacity(); + rosalloc_->SetFootprintLimit(max_allowed); + // Try the allocation. + result = AllocWithoutGrowthLocked(self, num_bytes, bytes_allocated); + // Shrink back down as small as possible. + size_t footprint = rosalloc_->Footprint(); + rosalloc_->SetFootprintLimit(footprint); + } + // Note RosAlloc zeroes memory internally. + // Return the new allocation or NULL. + CHECK(!kDebugSpaces || result == NULL || Contains(result)); + return result; +} + +MallocSpace* RosAllocSpace::CreateInstance(const std::string& name, MemMap* mem_map, void* allocator, + byte* begin, byte* end, byte* limit, size_t growth_limit) { + return new RosAllocSpace(name, mem_map, reinterpret_cast(allocator), + begin, end, limit, growth_limit); +} + +size_t RosAllocSpace::Free(Thread* self, mirror::Object* ptr) { + if (kDebugSpaces) { + CHECK(ptr != NULL); + CHECK(Contains(ptr)) << "Free (" << ptr << ") not in bounds of heap " << *this; + } + const size_t bytes_freed = InternalAllocationSize(ptr); + total_bytes_freed_atomic_.fetch_add(bytes_freed); + ++total_objects_freed_atomic_; + if (kRecentFreeCount > 0) { + MutexLock mu(self, lock_); + RegisterRecentFree(ptr); + } + rosalloc_->Free(self, ptr); + return bytes_freed; +} + +size_t RosAllocSpace::FreeList(Thread* self, size_t num_ptrs, mirror::Object** ptrs) { + DCHECK(ptrs != NULL); + + // Don't need the lock to calculate the size of the freed pointers. + size_t bytes_freed = 0; + for (size_t i = 0; i < num_ptrs; i++) { + mirror::Object* ptr = ptrs[i]; + const size_t look_ahead = 8; + if (kPrefetchDuringRosAllocFreeList && i + look_ahead < num_ptrs) { + __builtin_prefetch(reinterpret_cast(ptrs[i + look_ahead])); + } + bytes_freed += InternalAllocationSize(ptr); + } + + if (kRecentFreeCount > 0) { + MutexLock mu(self, lock_); + for (size_t i = 0; i < num_ptrs; i++) { + RegisterRecentFree(ptrs[i]); + } + } + + if (kDebugSpaces) { + size_t num_broken_ptrs = 0; + for (size_t i = 0; i < num_ptrs; i++) { + if (!Contains(ptrs[i])) { + num_broken_ptrs++; + LOG(ERROR) << "FreeList[" << i << "] (" << ptrs[i] << ") not in bounds of heap " << *this; + } else { + size_t size = rosalloc_->UsableSize(ptrs[i]); + memset(ptrs[i], 0xEF, size); + } + } + CHECK_EQ(num_broken_ptrs, 0u); + } + + rosalloc_->BulkFree(self, reinterpret_cast(ptrs), num_ptrs); + total_bytes_freed_atomic_.fetch_add(bytes_freed); + total_objects_freed_atomic_.fetch_add(num_ptrs); + return bytes_freed; +} + +// Callback from rosalloc when it needs to increase the footprint +extern "C" void* art_heap_rosalloc_morecore(allocator::RosAlloc* rosalloc, intptr_t increment) { + Heap* heap = Runtime::Current()->GetHeap(); + DCHECK(heap->GetNonMovingSpace()->IsRosAllocSpace()); + DCHECK_EQ(heap->GetNonMovingSpace()->AsRosAllocSpace()->GetRosAlloc(), rosalloc); + return heap->GetNonMovingSpace()->MoreCore(increment); +} + +// Virtual functions can't get inlined. +inline size_t RosAllocSpace::InternalAllocationSize(const mirror::Object* obj) { + return AllocationSizeNonvirtual(obj); +} + +size_t RosAllocSpace::AllocationSize(const mirror::Object* obj) { + return InternalAllocationSize(obj); +} + +size_t RosAllocSpace::Trim() { + MutexLock mu(Thread::Current(), lock_); + // Trim to release memory at the end of the space. + rosalloc_->Trim(); + // No inspect_all necessary here as trimming of pages is built-in. + return 0; +} + +void RosAllocSpace::Walk(void(*callback)(void *start, void *end, size_t num_bytes, void* callback_arg), + void* arg) { + InspectAllRosAlloc(callback, arg); + callback(NULL, NULL, 0, arg); // Indicate end of a space. +} + +size_t RosAllocSpace::GetFootprint() { + MutexLock mu(Thread::Current(), lock_); + return rosalloc_->Footprint(); +} + +size_t RosAllocSpace::GetFootprintLimit() { + MutexLock mu(Thread::Current(), lock_); + return rosalloc_->FootprintLimit(); +} + +void RosAllocSpace::SetFootprintLimit(size_t new_size) { + MutexLock mu(Thread::Current(), lock_); + VLOG(heap) << "RosAllocSpace::SetFootprintLimit " << PrettySize(new_size); + // Compare against the actual footprint, rather than the Size(), because the heap may not have + // grown all the way to the allowed size yet. + size_t current_space_size = rosalloc_->Footprint(); + if (new_size < current_space_size) { + // Don't let the space grow any more. + new_size = current_space_size; + } + rosalloc_->SetFootprintLimit(new_size); +} + +uint64_t RosAllocSpace::GetBytesAllocated() { + if (rosalloc_ != NULL) { + size_t bytes_allocated = 0; + InspectAllRosAlloc(art::gc::allocator::RosAlloc::BytesAllocatedCallback, &bytes_allocated); + return bytes_allocated; + } else { + return Size(); + } +} + +uint64_t RosAllocSpace::GetObjectsAllocated() { + if (rosalloc_ != NULL) { + size_t objects_allocated = 0; + InspectAllRosAlloc(art::gc::allocator::RosAlloc::ObjectsAllocatedCallback, &objects_allocated); + return objects_allocated; + } else { + return 0; + } +} + +void RosAllocSpace::InspectAllRosAlloc(void (*callback)(void *start, void *end, size_t num_bytes, void* callback_arg), + void* arg) NO_THREAD_SAFETY_ANALYSIS { + // TODO: NO_THREAD_SAFETY_ANALYSIS. + Thread* self = Thread::Current(); + if (Locks::mutator_lock_->IsExclusiveHeld(self)) { + // The mutators are already suspended. For example, a call path + // from SignalCatcher::HandleSigQuit(). + rosalloc_->InspectAll(callback, arg); + } else { + // The mutators are not suspended yet. + DCHECK(!Locks::mutator_lock_->IsSharedHeld(self)); + ThreadList* tl = Runtime::Current()->GetThreadList(); + tl->SuspendAll(); + { + MutexLock mu(self, *Locks::runtime_shutdown_lock_); + MutexLock mu2(self, *Locks::thread_list_lock_); + rosalloc_->InspectAll(callback, arg); + } + tl->ResumeAll(); + } +} + +void RosAllocSpace::RevokeThreadLocalBuffers(Thread* thread) { + rosalloc_->RevokeThreadLocalRuns(thread); +} + +void RosAllocSpace::RevokeAllThreadLocalBuffers() { + rosalloc_->RevokeAllThreadLocalRuns(); +} + +} // namespace space +} // namespace gc +} // namespace art diff --git a/runtime/gc/space/rosalloc_space.h b/runtime/gc/space/rosalloc_space.h new file mode 100644 index 00000000000..8191ffb7f95 --- /dev/null +++ b/runtime/gc/space/rosalloc_space.h @@ -0,0 +1,144 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ART_RUNTIME_GC_SPACE_ROSALLOC_SPACE_H_ +#define ART_RUNTIME_GC_SPACE_ROSALLOC_SPACE_H_ + +#include "gc/allocator/rosalloc.h" +#include "malloc_space.h" +#include "space.h" + +namespace art { +namespace gc { + +namespace collector { + class MarkSweep; +} // namespace collector + +namespace space { + +// An alloc space is a space where objects may be allocated and garbage collected. +class RosAllocSpace : public MallocSpace { + public: + // Create a RosAllocSpace with the requested sizes. The requested + // base address is not guaranteed to be granted, if it is required, + // the caller should call Begin on the returned space to confirm the + // request was granted. + static RosAllocSpace* Create(const std::string& name, size_t initial_size, size_t growth_limit, + size_t capacity, byte* requested_begin); + + virtual mirror::Object* AllocWithGrowth(Thread* self, size_t num_bytes, + size_t* bytes_allocated) LOCKS_EXCLUDED(lock_); + virtual mirror::Object* Alloc(Thread* self, size_t num_bytes, size_t* bytes_allocated); + virtual size_t AllocationSize(const mirror::Object* obj); + virtual size_t Free(Thread* self, mirror::Object* ptr); + virtual size_t FreeList(Thread* self, size_t num_ptrs, mirror::Object** ptrs); + + mirror::Object* AllocNonvirtual(Thread* self, size_t num_bytes, size_t* bytes_allocated); + + size_t AllocationSizeNonvirtual(const mirror::Object* obj) + NO_THREAD_SAFETY_ANALYSIS { + // TODO: NO_THREAD_SAFETY_ANALYSIS because SizeOf() requires that mutator_lock is held. + void* obj_ptr = const_cast(reinterpret_cast(obj)); + // obj is a valid object. Use its class in the header to get the size. + size_t size = obj->SizeOf(); + size_t size_by_size = rosalloc_->UsableSize(size); + if (kIsDebugBuild) { + size_t size_by_ptr = rosalloc_->UsableSize(obj_ptr); + if (size_by_size != size_by_ptr) { + LOG(INFO) << "Found a bad sized obj of size " << size + << " at " << std::hex << reinterpret_cast(obj_ptr) << std::dec + << " size_by_size=" << size_by_size << " size_by_ptr=" << size_by_ptr; + } + DCHECK_EQ(size_by_size, size_by_ptr); + } + return size_by_size; + } + + art::gc::allocator::RosAlloc* GetRosAlloc() { + return rosalloc_; + } + + size_t Trim(); + void Walk(WalkCallback callback, void* arg) LOCKS_EXCLUDED(lock_); + size_t GetFootprint(); + size_t GetFootprintLimit(); + void SetFootprintLimit(size_t limit); + + MallocSpace* CreateInstance(const std::string& name, MemMap* mem_map, void* allocator, + byte* begin, byte* end, byte* limit, size_t growth_limit); + + uint64_t GetBytesAllocated(); + uint64_t GetObjectsAllocated(); + uint64_t GetTotalBytesAllocated() { + return GetBytesAllocated() + total_bytes_freed_atomic_; + } + uint64_t GetTotalObjectsAllocated() { + return GetObjectsAllocated() + total_objects_freed_atomic_; + } + + void RevokeThreadLocalBuffers(Thread* thread); + void RevokeAllThreadLocalBuffers(); + + // Returns the class of a recently freed object. + mirror::Class* FindRecentFreedObject(const mirror::Object* obj); + + virtual void InvalidateAllocator() { + rosalloc_ = NULL; + } + + virtual bool IsRosAllocSpace() const { + return true; + } + virtual RosAllocSpace* AsRosAllocSpace() { + return this; + } + + protected: + RosAllocSpace(const std::string& name, MemMap* mem_map, allocator::RosAlloc* rosalloc, + byte* begin, byte* end, byte* limit, size_t growth_limit); + + private: + size_t InternalAllocationSize(const mirror::Object* obj); + mirror::Object* AllocWithoutGrowthLocked(Thread* self, size_t num_bytes, size_t* bytes_allocated); + + void* CreateAllocator(void* base, size_t morecore_start, size_t initial_size) { + return CreateRosAlloc(base, morecore_start, initial_size); + } + static allocator::RosAlloc* CreateRosAlloc(void* base, size_t morecore_start, size_t initial_size); + + + void InspectAllRosAlloc(void (*callback)(void *start, void *end, size_t num_bytes, void* callback_arg), + void* arg) + LOCKS_EXCLUDED(Locks::runtime_shutdown_lock_, Locks::thread_list_lock_); + + // Approximate number of bytes and objects which have been deallocated in the space. + AtomicInteger total_bytes_freed_atomic_; + AtomicInteger total_objects_freed_atomic_; + + // Underlying rosalloc. + art::gc::allocator::RosAlloc* rosalloc_; + + friend class collector::MarkSweep; + + DISALLOW_COPY_AND_ASSIGN(RosAllocSpace); +}; + +} // namespace space +} // namespace gc +} // namespace art + +#endif // ART_RUNTIME_GC_SPACE_ROSALLOC_SPACE_H_ diff --git a/runtime/gc/space/space-inl.h b/runtime/gc/space/space-inl.h index f1031ff8d4e..0c1d7a27698 100644 --- a/runtime/gc/space/space-inl.h +++ b/runtime/gc/space/space-inl.h @@ -31,9 +31,10 @@ inline ImageSpace* Space::AsImageSpace() { return down_cast(down_cast(this)); } -inline DlMallocSpace* Space::AsDlMallocSpace() { - DCHECK(IsDlMallocSpace()); - return down_cast(down_cast(this)); +inline MallocSpace* Space::AsMallocSpace() { + DCHECK(GetType() == kSpaceTypeAllocSpace || GetType() == kSpaceTypeZygoteSpace); + DCHECK(IsDlMallocSpace() || IsRosAllocSpace()); + return down_cast(down_cast(this)); } inline LargeObjectSpace* Space::AsLargeObjectSpace() { diff --git a/runtime/gc/space/space.h b/runtime/gc/space/space.h index 4c05ddef58a..38b602ead15 100644 --- a/runtime/gc/space/space.h +++ b/runtime/gc/space/space.h @@ -44,8 +44,10 @@ namespace space { class AllocSpace; class ContinuousSpace; -class DlMallocSpace; class DiscontinuousSpace; +class MallocSpace; +class DlMallocSpace; +class RosAllocSpace; class ImageSpace; class LargeObjectSpace; @@ -106,11 +108,26 @@ class Space { ImageSpace* AsImageSpace(); // Is this a dlmalloc backed allocation space? - bool IsDlMallocSpace() const { + bool IsMallocSpace() const { SpaceType type = GetType(); return type == kSpaceTypeAllocSpace || type == kSpaceTypeZygoteSpace; } - DlMallocSpace* AsDlMallocSpace(); + MallocSpace* AsMallocSpace(); + + virtual bool IsDlMallocSpace() const { + return false; + } + virtual DlMallocSpace* AsDlMallocSpace() { + LOG(FATAL) << "Unreachable"; + return NULL; + } + virtual bool IsRosAllocSpace() const { + return false; + } + virtual RosAllocSpace* AsRosAllocSpace() { + LOG(FATAL) << "Unreachable"; + return NULL; + } // Is this the space allocated into by the Zygote and no-longer in use? bool IsZygoteSpace() const { @@ -195,6 +212,16 @@ class AllocSpace { // Returns how many bytes were freed. virtual size_t FreeList(Thread* self, size_t num_ptrs, mirror::Object** ptrs) = 0; + // Revoke any sort of thread-local buffers that are used to speed up + // allocations for the given thread, if the alloc space + // implementation uses any. No-op by default. + virtual void RevokeThreadLocalBuffers(Thread* /*thread*/) {} + + // Revoke any sort of thread-local buffers that are used to speed up + // allocations for all the threads, if the alloc space + // implementation uses any. No-op by default. + virtual void RevokeAllThreadLocalBuffers() {} + protected: AllocSpace() {} virtual ~AllocSpace() {} diff --git a/runtime/gc/space/space_test.cc b/runtime/gc/space/space_test.cc index 383714bb049..6b597ae0936 100644 --- a/runtime/gc/space/space_test.cc +++ b/runtime/gc/space/space_test.cc @@ -20,6 +20,8 @@ #include "common_test.h" #include "globals.h" #include "UniquePtr.h" +#include "mirror/array-inl.h" +#include "mirror/object-inl.h" #include @@ -34,8 +36,25 @@ class SpaceTest : public CommonTest { void SizeFootPrintGrowthLimitAndTrimDriver(size_t object_size); void AddSpace(ContinuousSpace* space) { + // For RosAlloc, revoke the thread local runs before moving onto a + // new alloc space. + Runtime::Current()->GetHeap()->RevokeAllThreadLocalBuffers(); Runtime::Current()->GetHeap()->AddSpace(space); } + void InstallClass(mirror::Object* o, size_t size) NO_THREAD_SAFETY_ANALYSIS { + // Note the minimum size, which is the size of a zero-length byte array, is 12. + EXPECT_GE(size, static_cast(12)); + SirtRef null_loader(Thread::Current(), NULL); + mirror::Class* byte_array_class = Runtime::Current()->GetClassLinker()->FindClass("[B", null_loader); + EXPECT_TRUE(byte_array_class != NULL); + o->SetClass(byte_array_class); + mirror::Array* arr = o->AsArray(); + // size_t header_size = sizeof(mirror::Object) + 4; + size_t header_size = arr->DataOffset(1).Uint32Value(); + int32_t length = size - header_size; + arr->SetLength(length); + EXPECT_EQ(arr->SizeOf(), size); + } }; static size_t test_rand(size_t* seed) { @@ -87,7 +106,7 @@ TEST_F(SpaceTest, Init) { // the GC works with the ZygoteSpace. TEST_F(SpaceTest, ZygoteSpace) { size_t dummy = 0; - DlMallocSpace* space(DlMallocSpace::Create("test", 4 * MB, 16 * MB, 16 * MB, NULL)); + MallocSpace* space(DlMallocSpace::Create("test", 4 * MB, 16 * MB, 16 * MB, NULL)); ASSERT_TRUE(space != NULL); // Make space findable to the heap, will also delete space when runtime is cleaned up @@ -97,6 +116,7 @@ TEST_F(SpaceTest, ZygoteSpace) { // Succeeds, fits without adjusting the footprint limit. mirror::Object* ptr1 = space->Alloc(self, 1 * MB, &dummy); EXPECT_TRUE(ptr1 != NULL); + InstallClass(ptr1, 1 * MB); // Fails, requires a higher footprint limit. mirror::Object* ptr2 = space->Alloc(self, 8 * MB, &dummy); @@ -107,6 +127,7 @@ TEST_F(SpaceTest, ZygoteSpace) { mirror::Object* ptr3 = space->AllocWithGrowth(self, 8 * MB, &ptr3_bytes_allocated); EXPECT_TRUE(ptr3 != NULL); EXPECT_LE(8U * MB, ptr3_bytes_allocated); + InstallClass(ptr3, 8 * MB); // Fails, requires a higher footprint limit. mirror::Object* ptr4 = space->Alloc(self, 8 * MB, &dummy); @@ -123,8 +144,9 @@ TEST_F(SpaceTest, ZygoteSpace) { EXPECT_LE(8U * MB, free3); // Succeeds, now that memory has been freed. - void* ptr6 = space->AllocWithGrowth(self, 9 * MB, &dummy); + mirror::Object* ptr6 = space->AllocWithGrowth(self, 9 * MB, &dummy); EXPECT_TRUE(ptr6 != NULL); + InstallClass(ptr6, 9 * MB); // Final clean up. size_t free1 = space->AllocationSize(ptr1); @@ -141,6 +163,7 @@ TEST_F(SpaceTest, ZygoteSpace) { // Succeeds, fits without adjusting the footprint limit. ptr1 = space->Alloc(self, 1 * MB, &dummy); EXPECT_TRUE(ptr1 != NULL); + InstallClass(ptr1, 1 * MB); // Fails, requires a higher footprint limit. ptr2 = space->Alloc(self, 8 * MB, &dummy); @@ -149,6 +172,7 @@ TEST_F(SpaceTest, ZygoteSpace) { // Succeeds, adjusts the footprint. ptr3 = space->AllocWithGrowth(self, 2 * MB, &dummy); EXPECT_TRUE(ptr3 != NULL); + InstallClass(ptr3, 2 * MB); space->Free(self, ptr3); // Final clean up. @@ -169,6 +193,7 @@ TEST_F(SpaceTest, AllocAndFree) { // Succeeds, fits without adjusting the footprint limit. mirror::Object* ptr1 = space->Alloc(self, 1 * MB, &dummy); EXPECT_TRUE(ptr1 != NULL); + InstallClass(ptr1, 1 * MB); // Fails, requires a higher footprint limit. mirror::Object* ptr2 = space->Alloc(self, 8 * MB, &dummy); @@ -179,6 +204,7 @@ TEST_F(SpaceTest, AllocAndFree) { mirror::Object* ptr3 = space->AllocWithGrowth(self, 8 * MB, &ptr3_bytes_allocated); EXPECT_TRUE(ptr3 != NULL); EXPECT_LE(8U * MB, ptr3_bytes_allocated); + InstallClass(ptr3, 8 * MB); // Fails, requires a higher footprint limit. mirror::Object* ptr4 = space->Alloc(self, 8 * MB, &dummy); @@ -195,8 +221,9 @@ TEST_F(SpaceTest, AllocAndFree) { EXPECT_LE(8U * MB, free3); // Succeeds, now that memory has been freed. - void* ptr6 = space->AllocWithGrowth(self, 9 * MB, &dummy); + mirror::Object* ptr6 = space->AllocWithGrowth(self, 9 * MB, &dummy); EXPECT_TRUE(ptr6 != NULL); + InstallClass(ptr6, 9 * MB); // Final clean up. size_t free1 = space->AllocationSize(ptr1); @@ -278,8 +305,9 @@ TEST_F(SpaceTest, AllocAndFreeList) { for (size_t i = 0; i < arraysize(lots_of_objects); i++) { size_t allocation_size = 0; lots_of_objects[i] = space->Alloc(self, 16, &allocation_size); - EXPECT_EQ(allocation_size, space->AllocationSize(lots_of_objects[i])); EXPECT_TRUE(lots_of_objects[i] != NULL); + InstallClass(lots_of_objects[i], 16); + EXPECT_EQ(allocation_size, space->AllocationSize(lots_of_objects[i])); } // Release memory and check pointers are NULL @@ -292,8 +320,9 @@ TEST_F(SpaceTest, AllocAndFreeList) { for (size_t i = 0; i < arraysize(lots_of_objects); i++) { size_t allocation_size = 0; lots_of_objects[i] = space->AllocWithGrowth(self, 1024, &allocation_size); - EXPECT_EQ(allocation_size, space->AllocationSize(lots_of_objects[i])); EXPECT_TRUE(lots_of_objects[i] != NULL); + InstallClass(lots_of_objects[i], 1024); + EXPECT_EQ(allocation_size, space->AllocationSize(lots_of_objects[i])); } // Release memory and check pointers are NULL @@ -310,22 +339,20 @@ void SpaceTest::SizeFootPrintGrowthLimitAndTrimBody(DlMallocSpace* space, intptr // No allocation can succeed return; } - // Mspace for raw dlmalloc operations - void* mspace = space->GetMspace(); - // mspace's footprint equals amount of resources requested from system - size_t footprint = mspace_footprint(mspace); + // The space's footprint equals amount of resources requested from system + size_t footprint = space->GetFootprint(); - // mspace must at least have its book keeping allocated + // The space must at least have its book keeping allocated EXPECT_GT(footprint, 0u); - // mspace but it shouldn't exceed the initial size + // But it shouldn't exceed the initial size EXPECT_LE(footprint, growth_limit); // space's size shouldn't exceed the initial size EXPECT_LE(space->Size(), growth_limit); - // this invariant should always hold or else the mspace has grown to be larger than what the + // this invariant should always hold or else the space has grown to be larger than what the // space believes its size is (which will break invariants) EXPECT_GE(space->Size(), footprint); @@ -345,8 +372,9 @@ void SpaceTest::SizeFootPrintGrowthLimitAndTrimBody(DlMallocSpace* space, intptr alloc_size = object_size; } else { alloc_size = test_rand(&rand_seed) % static_cast(-object_size); - if (alloc_size < 8) { - alloc_size = 8; + // Note the minimum size, which is the size of a zero-length byte array, is 12. + if (alloc_size < 12) { + alloc_size = 12; } } mirror::Object* object; @@ -356,9 +384,10 @@ void SpaceTest::SizeFootPrintGrowthLimitAndTrimBody(DlMallocSpace* space, intptr } else { object = space->AllocWithGrowth(self, alloc_size, &bytes_allocated); } - footprint = mspace_footprint(mspace); + footprint = space->GetFootprint(); EXPECT_GE(space->Size(), footprint); // invariant if (object != NULL) { // allocation succeeded + InstallClass(object, alloc_size); lots_of_objects.get()[i] = object; size_t allocation_size = space->AllocationSize(object); EXPECT_EQ(bytes_allocated, allocation_size); @@ -395,7 +424,7 @@ void SpaceTest::SizeFootPrintGrowthLimitAndTrimBody(DlMallocSpace* space, intptr space->Trim(); // Bounds sanity - footprint = mspace_footprint(mspace); + footprint = space->GetFootprint(); EXPECT_LE(amount_allocated, growth_limit); EXPECT_GE(footprint, amount_allocated); EXPECT_LE(footprint, growth_limit); @@ -421,13 +450,21 @@ void SpaceTest::SizeFootPrintGrowthLimitAndTrimBody(DlMallocSpace* space, intptr space->Free(self, object); lots_of_objects.get()[i] = NULL; amount_allocated -= allocation_size; - footprint = mspace_footprint(mspace); + footprint = space->GetFootprint(); EXPECT_GE(space->Size(), footprint); // invariant } free_increment >>= 1; } + // The space has become empty here before allocating a large object + // below. For RosAlloc, revoke thread-local runs, which are kept + // even when empty for a performance reason, so that they won't + // cause the following large object allocation to fail due to + // potential fragmentation. Note they are normally revoked at each + // GC (but no GC here.) + space->RevokeAllThreadLocalBuffers(); + // All memory was released, try a large allocation to check freed memory is being coalesced mirror::Object* large_object; size_t three_quarters_space = (growth_limit / 2) + (growth_limit / 4); @@ -438,9 +475,10 @@ void SpaceTest::SizeFootPrintGrowthLimitAndTrimBody(DlMallocSpace* space, intptr large_object = space->AllocWithGrowth(self, three_quarters_space, &bytes_allocated); } EXPECT_TRUE(large_object != NULL); + InstallClass(large_object, three_quarters_space); // Sanity check footprint - footprint = mspace_footprint(mspace); + footprint = space->GetFootprint(); EXPECT_LE(footprint, growth_limit); EXPECT_GE(space->Size(), footprint); EXPECT_LE(space->Size(), growth_limit); @@ -449,7 +487,7 @@ void SpaceTest::SizeFootPrintGrowthLimitAndTrimBody(DlMallocSpace* space, intptr space->Free(self, large_object); // Sanity check footprint - footprint = mspace_footprint(mspace); + footprint = space->GetFootprint(); EXPECT_LE(footprint, growth_limit); EXPECT_GE(space->Size(), footprint); EXPECT_LE(space->Size(), growth_limit); @@ -488,8 +526,8 @@ void SpaceTest::SizeFootPrintGrowthLimitAndTrimDriver(size_t object_size) { } // Each size test is its own test so that we get a fresh heap each time -TEST_F(SpaceTest, SizeFootPrintGrowthLimitAndTrim_AllocationsOf_8B) { - SizeFootPrintGrowthLimitAndTrimDriver(8); +TEST_F(SpaceTest, SizeFootPrintGrowthLimitAndTrim_AllocationsOf_12B) { + SizeFootPrintGrowthLimitAndTrimDriver(12); } TEST_SizeFootPrintGrowthLimitAndTrim(16B, 16) TEST_SizeFootPrintGrowthLimitAndTrim(24B, 24) diff --git a/runtime/locks.h b/runtime/locks.h index 226221822bf..2308e951b04 100644 --- a/runtime/locks.h +++ b/runtime/locks.h @@ -37,6 +37,9 @@ enum LockLevel { kThreadSuspendCountLock, kAbortLock, kJdwpSocketLock, + kRosAllocGlobalLock, + kRosAllocBracketLock, + kRosAllocBulkFreeLock, kAllocSpaceLock, kMarkSweepMarkStackLock, kDefaultMutexLevel, diff --git a/runtime/native/dalvik_system_VMDebug.cc b/runtime/native/dalvik_system_VMDebug.cc index 96c3e78a43e..66fa100385c 100644 --- a/runtime/native/dalvik_system_VMDebug.cc +++ b/runtime/native/dalvik_system_VMDebug.cc @@ -267,14 +267,14 @@ static void VMDebug_getHeapSpaceStats(JNIEnv* env, jclass, jlongArray data) { if (space->IsImageSpace()) { // Currently don't include the image space. } else if (space->IsZygoteSpace()) { - gc::space::DlMallocSpace* dlmalloc_space = space->AsDlMallocSpace(); - zygoteSize += dlmalloc_space->GetFootprint(); - zygoteUsed += dlmalloc_space->GetBytesAllocated(); + gc::space::MallocSpace* malloc_space = space->AsMallocSpace(); + zygoteSize += malloc_space->GetFootprint(); + zygoteUsed += malloc_space->GetBytesAllocated(); } else { // This is the alloc space. - gc::space::DlMallocSpace* dlmalloc_space = space->AsDlMallocSpace(); - allocSize += dlmalloc_space->GetFootprint(); - allocUsed += dlmalloc_space->GetBytesAllocated(); + gc::space::MallocSpace* malloc_space = space->AsMallocSpace(); + allocSize += malloc_space->GetFootprint(); + allocUsed += malloc_space->GetBytesAllocated(); } } typedef std::vector::const_iterator It2; diff --git a/runtime/runtime.cc b/runtime/runtime.cc index 8e39023cf5d..08c0ba0e5ce 100644 --- a/runtime/runtime.cc +++ b/runtime/runtime.cc @@ -124,6 +124,11 @@ Runtime::~Runtime() { heap_->WaitForGcToComplete(self); heap_->DeleteThreadPool(); + // For RosAlloc, revoke thread local runs. Note that in tests + // (common_test.h) we repeat allocating and deleting Runtime + // objects. + heap_->RevokeAllThreadLocalBuffers(); + // Make sure our internal threads are dead before we start tearing down things they're using. Dbg::StopJdwp(); delete signal_catcher_; diff --git a/runtime/thread.cc b/runtime/thread.cc index 1f6dd69110d..e55c35fb42e 100644 --- a/runtime/thread.cc +++ b/runtime/thread.cc @@ -930,6 +930,7 @@ Thread::Thread(bool daemon) state_and_flags_.as_struct.flags = 0; state_and_flags_.as_struct.state = kNative; memset(&held_mutexes_[0], 0, sizeof(held_mutexes_)); + memset(rosalloc_runs_, 0, sizeof(rosalloc_runs_)); } bool Thread::IsStillStarting() const { @@ -1022,6 +1023,8 @@ Thread::~Thread() { delete name_; delete stack_trace_sample_; + Runtime::Current()->GetHeap()->RevokeThreadLocalBuffers(this); + TearDownAlternateSignalStack(); } diff --git a/runtime/thread.h b/runtime/thread.h index 6bd3607922e..f4b2ae5edfb 100644 --- a/runtime/thread.h +++ b/runtime/thread.h @@ -790,6 +790,15 @@ class PACKED(4) Thread { friend class ScopedThreadStateChange; + public: + // Thread-local rosalloc runs. There are 34 size brackets in rosalloc + // runs (RosAlloc::kNumOfSizeBrackets). We can't refer to the + // RosAlloc class due to a header file circular dependency issue. + // To compensate, we check that the two values match at RosAlloc + // initialization time. + static const size_t kRosAllocNumOfSizeBrackets = 34; + void* rosalloc_runs_[kRosAllocNumOfSizeBrackets]; + DISALLOW_COPY_AND_ASSIGN(Thread); }; From 5c96e6b4dc354a7439b211b93462fbe8edea5e57 Mon Sep 17 00:00:00 2001 From: Vladimir Marko Date: Thu, 14 Nov 2013 15:34:17 +0000 Subject: [PATCH 0190/2402] Rewrite intrinsics detection. Intrinsic methods should be treated as a special case of inline methods. They should be detected early and used to guide other optimizations. This CL rewrites the intrinsics detection so that it can be moved to any compilation phase. Change-Id: I4424a6a869bd98b9c478953c9e3bcaf1c6de2b33 --- compiler/Android.mk | 5 + compiler/dex/frontend.cc | 15 +- compiler/dex/frontend.h | 16 + .../quick/arm/arm_dex_file_method_inliner.cc | 103 ++++++ .../quick/arm/arm_dex_file_method_inliner.h | 37 ++ compiler/dex/quick/codegen_util.cc | 3 +- compiler/dex/quick/dex_file_method_inliner.cc | 346 ++++++++++++++++++ compiler/dex/quick/dex_file_method_inliner.h | 318 ++++++++++++++++ .../quick/dex_file_to_method_inliner_map.cc | 79 ++++ .../quick/dex_file_to_method_inliner_map.h | 53 +++ compiler/dex/quick/gen_invoke.cc | 204 +---------- .../mips/mips_dex_file_method_inliner.cc | 103 ++++++ .../quick/mips/mips_dex_file_method_inliner.h | 37 ++ compiler/dex/quick/mir_to_lir.h | 4 +- .../quick/x86/x86_dex_file_method_inliner.cc | 109 ++++++ .../quick/x86/x86_dex_file_method_inliner.h | 37 ++ runtime/dex_file.cc | 7 +- runtime/dex_file.h | 6 +- 18 files changed, 1280 insertions(+), 202 deletions(-) create mode 100644 compiler/dex/quick/arm/arm_dex_file_method_inliner.cc create mode 100644 compiler/dex/quick/arm/arm_dex_file_method_inliner.h create mode 100644 compiler/dex/quick/dex_file_method_inliner.cc create mode 100644 compiler/dex/quick/dex_file_method_inliner.h create mode 100644 compiler/dex/quick/dex_file_to_method_inliner_map.cc create mode 100644 compiler/dex/quick/dex_file_to_method_inliner_map.h create mode 100644 compiler/dex/quick/mips/mips_dex_file_method_inliner.cc create mode 100644 compiler/dex/quick/mips/mips_dex_file_method_inliner.h create mode 100644 compiler/dex/quick/x86/x86_dex_file_method_inliner.cc create mode 100644 compiler/dex/quick/x86/x86_dex_file_method_inliner.h diff --git a/compiler/Android.mk b/compiler/Android.mk index fc2f02b59e7..b7dc9f6c3b6 100644 --- a/compiler/Android.mk +++ b/compiler/Android.mk @@ -23,6 +23,7 @@ LIBART_COMPILER_SRC_FILES := \ dex/local_value_numbering.cc \ dex/arena_allocator.cc \ dex/arena_bit_vector.cc \ + dex/quick/arm/arm_dex_file_method_inliner.cc \ dex/quick/arm/assemble_arm.cc \ dex/quick/arm/call_arm.cc \ dex/quick/arm/fp_arm.cc \ @@ -30,6 +31,8 @@ LIBART_COMPILER_SRC_FILES := \ dex/quick/arm/target_arm.cc \ dex/quick/arm/utility_arm.cc \ dex/quick/codegen_util.cc \ + dex/quick/dex_file_method_inliner.cc \ + dex/quick/dex_file_to_method_inliner_map.cc \ dex/quick/gen_common.cc \ dex/quick/gen_invoke.cc \ dex/quick/gen_loadstore.cc \ @@ -38,6 +41,7 @@ LIBART_COMPILER_SRC_FILES := \ dex/quick/mips/call_mips.cc \ dex/quick/mips/fp_mips.cc \ dex/quick/mips/int_mips.cc \ + dex/quick/mips/mips_dex_file_method_inliner.cc \ dex/quick/mips/target_mips.cc \ dex/quick/mips/utility_mips.cc \ dex/quick/mir_to_lir.cc \ @@ -48,6 +52,7 @@ LIBART_COMPILER_SRC_FILES := \ dex/quick/x86/int_x86.cc \ dex/quick/x86/target_x86.cc \ dex/quick/x86/utility_x86.cc \ + dex/quick/x86/x86_dex_file_method_inliner.cc \ dex/portable/mir_to_gbc.cc \ dex/dex_to_dex_compiler.cc \ dex/mir_dataflow.cc \ diff --git a/compiler/dex/frontend.cc b/compiler/dex/frontend.cc index 3dc1914c30f..b6e062e413b 100644 --- a/compiler/dex/frontend.cc +++ b/compiler/dex/frontend.cc @@ -31,6 +31,8 @@ #include "llvm/llvm_compilation_unit.h" #endif +#include "dex/quick/dex_file_to_method_inliner_map.h" + namespace { #if !defined(ART_USE_PORTABLE_COMPILER) pthread_once_t llvm_multi_init = PTHREAD_ONCE_INIT; @@ -61,14 +63,21 @@ LLVMInfo::LLVMInfo() { LLVMInfo::~LLVMInfo() { } +QuickCompilerContext::QuickCompilerContext(CompilerDriver& compiler) + : inliner_map_(new DexFileToMethodInlinerMap(&compiler)) +{ +} + +QuickCompilerContext::~QuickCompilerContext() { +} + extern "C" void ArtInitQuickCompilerContext(art::CompilerDriver& compiler) { CHECK(compiler.GetCompilerContext() == NULL); - LLVMInfo* llvm_info = new LLVMInfo(); - compiler.SetCompilerContext(llvm_info); + compiler.SetCompilerContext(new QuickCompilerContext(compiler)); } extern "C" void ArtUnInitQuickCompilerContext(art::CompilerDriver& compiler) { - delete reinterpret_cast(compiler.GetCompilerContext()); + delete reinterpret_cast(compiler.GetCompilerContext()); compiler.SetCompilerContext(NULL); } diff --git a/compiler/dex/frontend.h b/compiler/dex/frontend.h index b9b41788901..b6e8577fdf1 100644 --- a/compiler/dex/frontend.h +++ b/compiler/dex/frontend.h @@ -82,6 +82,9 @@ enum debugControlVector { kDebugTimings }; +class DexFileToMethodInlinerMap; +class CompilerDriver; + class LLVMInfo { public: LLVMInfo(); @@ -110,6 +113,19 @@ class LLVMInfo { UniquePtr ir_builder_; }; +class QuickCompilerContext { + public: + QuickCompilerContext(CompilerDriver& compiler); + ~QuickCompilerContext(); + + DexFileToMethodInlinerMap* GetInlinerMap() { + return inliner_map_.get(); + } + + private: + UniquePtr inliner_map_; +}; + struct CompilationUnit; struct BasicBlock; diff --git a/compiler/dex/quick/arm/arm_dex_file_method_inliner.cc b/compiler/dex/quick/arm/arm_dex_file_method_inliner.cc new file mode 100644 index 00000000000..a8ae3cd72c9 --- /dev/null +++ b/compiler/dex/quick/arm/arm_dex_file_method_inliner.cc @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2013 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 "base/logging.h" +#include "base/macros.h" +#include "dex/compiler_enums.h" + +#include "arm_dex_file_method_inliner.h" + +namespace art { + +const DexFileMethodInliner::IntrinsicDef ArmDexFileMethodInliner::kIntrinsicMethods[] = { +#define INTRINSIC(c, n, p, o, d) \ + { { kClassCache ## c, kNameCache ## n, kProtoCache ## p }, { o, d } } + + INTRINSIC(JavaLangDouble, DoubleToRawLongBits, D_J, kIntrinsicDoubleCvt, 0), + INTRINSIC(JavaLangDouble, LongBitsToDouble, J_D, kIntrinsicDoubleCvt, 0), + INTRINSIC(JavaLangFloat, FloatToRawIntBits, F_I, kIntrinsicFloatCvt, 0), + INTRINSIC(JavaLangFloat, IntBitsToFloat, I_F, kIntrinsicFloatCvt, 0), + + INTRINSIC(JavaLangInteger, ReverseBytes, I_I, kIntrinsicReverseBytes, kWord), + INTRINSIC(JavaLangLong, ReverseBytes, J_J, kIntrinsicReverseBytes, kLong), + INTRINSIC(JavaLangShort, ReverseBytes, S_S, kIntrinsicReverseBytes, kSignedHalf), + + INTRINSIC(JavaLangMath, Abs, I_I, kIntrinsicAbsInt, 0), + INTRINSIC(JavaLangStrictMath, Abs, I_I, kIntrinsicAbsInt, 0), + INTRINSIC(JavaLangMath, Abs, J_J, kIntrinsicAbsLong, 0), + INTRINSIC(JavaLangStrictMath, Abs, J_J, kIntrinsicAbsLong, 0), + INTRINSIC(JavaLangMath, Min, II_I, kIntrinsicMinMaxInt, kIntrinsicFlagMin), + INTRINSIC(JavaLangStrictMath, Min, II_I, kIntrinsicMinMaxInt, kIntrinsicFlagMin), + INTRINSIC(JavaLangMath, Max, II_I, kIntrinsicMinMaxInt, kIntrinsicFlagMax), + INTRINSIC(JavaLangStrictMath, Max, II_I, kIntrinsicMinMaxInt, kIntrinsicFlagMax), + INTRINSIC(JavaLangMath, Sqrt, D_D, kIntrinsicSqrt, 0), + INTRINSIC(JavaLangStrictMath, Sqrt, D_D, kIntrinsicSqrt, 0), + + INTRINSIC(JavaLangString, CharAt, I_C, kIntrinsicCharAt, 0), + INTRINSIC(JavaLangString, CompareTo, String_I, kIntrinsicCompareTo, 0), + INTRINSIC(JavaLangString, IsEmpty, _Z, kIntrinsicIsEmptyOrLength, kIntrinsicFlagIsEmpty), + INTRINSIC(JavaLangString, IndexOf, II_I, kIntrinsicIndexOf, kIntrinsicFlagNone), + INTRINSIC(JavaLangString, IndexOf, I_I, kIntrinsicIndexOf, kIntrinsicFlagBase0), + INTRINSIC(JavaLangString, Length, _I, kIntrinsicIsEmptyOrLength, kIntrinsicFlagLength), + + INTRINSIC(JavaLangThread, CurrentThread, _Thread, kIntrinsicCurrentThread, 0), + + INTRINSIC(LibcoreIoMemory, PeekByte, J_B, kIntrinsicPeek, kSignedByte), + INTRINSIC(LibcoreIoMemory, PeekIntNative, J_I, kIntrinsicPeek, kWord), + INTRINSIC(LibcoreIoMemory, PeekLongNative, J_J, kIntrinsicPeek, kLong), + INTRINSIC(LibcoreIoMemory, PeekShortNative, J_S, kIntrinsicPeek, kSignedHalf), + INTRINSIC(LibcoreIoMemory, PokeByte, JB_V, kIntrinsicPoke, kSignedByte), + INTRINSIC(LibcoreIoMemory, PokeIntNative, JI_V, kIntrinsicPoke, kWord), + INTRINSIC(LibcoreIoMemory, PokeLongNative, JJ_V, kIntrinsicPoke, kLong), + INTRINSIC(LibcoreIoMemory, PokeShortNative, JS_V, kIntrinsicPoke, kSignedHalf), + + INTRINSIC(SunMiscUnsafe, CompareAndSwapInt, ObjectJII_Z, kIntrinsicCas32, + kIntrinsicFlagDontNeedWriteBarrier), + INTRINSIC(SunMiscUnsafe, CompareAndSwapObject, ObjectJObjectObject_Z, kIntrinsicCas32, + kIntrinsicFlagNeedWriteBarrier), + +#define UNSAFE_GET_PUT(type, code, type_flags) \ + INTRINSIC(SunMiscUnsafe, Get ## type, ObjectJ_ ## code, kIntrinsicUnsafeGet, \ + type_flags & ~kIntrinsicFlagIsObject), \ + INTRINSIC(SunMiscUnsafe, Get ## type ## Volatile, ObjectJ_ ## code, kIntrinsicUnsafeGet, \ + (type_flags | kIntrinsicFlagIsVolatile) & ~kIntrinsicFlagIsObject), \ + INTRINSIC(SunMiscUnsafe, Put ## type, ObjectJ ## code ## _V, kIntrinsicUnsafePut, \ + type_flags), \ + INTRINSIC(SunMiscUnsafe, Put ## type ## Volatile, ObjectJ ## code ## _V, kIntrinsicUnsafePut, \ + type_flags | kIntrinsicFlagIsVolatile), \ + INTRINSIC(SunMiscUnsafe, PutOrdered ## type, ObjectJ ## code ## _V, kIntrinsicUnsafePut, \ + type_flags | kIntrinsicFlagIsOrdered) + + UNSAFE_GET_PUT(Int, I, kIntrinsicFlagNone), + UNSAFE_GET_PUT(Long, J, kIntrinsicFlagIsLong), + UNSAFE_GET_PUT(Object, Object, kIntrinsicFlagIsObject), +#undef UNSAFE_GET_PUT + +#undef INTRINSIC +}; + +ArmDexFileMethodInliner::ArmDexFileMethodInliner() { +} + +ArmDexFileMethodInliner::~ArmDexFileMethodInliner() { +} + +void ArmDexFileMethodInliner::FindIntrinsics(const DexFile* dex_file) { + IndexCache cache; + DoFindIntrinsics(dex_file, &cache, kIntrinsicMethods, arraysize(kIntrinsicMethods)); +} + +} // namespace art diff --git a/compiler/dex/quick/arm/arm_dex_file_method_inliner.h b/compiler/dex/quick/arm/arm_dex_file_method_inliner.h new file mode 100644 index 00000000000..3428391624d --- /dev/null +++ b/compiler/dex/quick/arm/arm_dex_file_method_inliner.h @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ART_COMPILER_DEX_QUICK_ARM_ARM_DEX_FILE_METHOD_INLINER_H_ +#define ART_COMPILER_DEX_QUICK_ARM_ARM_DEX_FILE_METHOD_INLINER_H_ + +#include "dex/quick/dex_file_method_inliner.h" + +namespace art { + +class ArmDexFileMethodInliner : public DexFileMethodInliner { + public: + ArmDexFileMethodInliner(); + ~ArmDexFileMethodInliner(); + + void FindIntrinsics(const DexFile* dex_file); + + private: + static const IntrinsicDef kIntrinsicMethods[]; +}; + +} // namespace art + +#endif // ART_COMPILER_DEX_QUICK_ARM_ARM_DEX_FILE_METHOD_INLINER_H_ diff --git a/compiler/dex/quick/codegen_util.cc b/compiler/dex/quick/codegen_util.cc index dfbc8872990..4bc0b357af0 100644 --- a/compiler/dex/quick/codegen_util.cc +++ b/compiler/dex/quick/codegen_util.cc @@ -920,7 +920,8 @@ Mir2Lir::Mir2Lir(CompilationUnit* cu, MIRGraph* mir_graph, ArenaAllocator* arena core_spill_mask_(0), fp_spill_mask_(0), first_lir_insn_(NULL), - last_lir_insn_(NULL) { + last_lir_insn_(NULL), + inliner_(nullptr) { promotion_map_ = static_cast (arena_->Alloc((cu_->num_dalvik_registers + cu_->num_compiler_temps + 1) * sizeof(promotion_map_[0]), ArenaAllocator::kAllocRegAlloc)); diff --git a/compiler/dex/quick/dex_file_method_inliner.cc b/compiler/dex/quick/dex_file_method_inliner.cc new file mode 100644 index 00000000000..c962e9df936 --- /dev/null +++ b/compiler/dex/quick/dex_file_method_inliner.cc @@ -0,0 +1,346 @@ +/* + * Copyright (C) 2013 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 +#include "base/macros.h" +#include "dex/mir_graph.h" + +#include "dex_file_method_inliner.h" + +namespace art { + +const char* DexFileMethodInliner::kClassCacheNames[] = { + "Z", // kClassCacheBoolean + "B", // kClassCacheByte + "C", // kClassCacheChar + "S", // kClassCacheShort + "I", // kClassCacheInt + "J", // kClassCacheLong + "F", // kClassCacheFloat + "D", // kClassCacheDouble + "V", // kClassCacheVoid + "Ljava/lang/Object;", // kClassCacheJavaLangObject + "Ljava/lang/String;", // kClassCacheJavaLangString + "Ljava/lang/Double;", // kClassCacheJavaLangDouble + "Ljava/lang/Float;", // kClassCacheJavaLangFloat + "Ljava/lang/Integer;", // kClassCacheJavaLangInteger + "Ljava/lang/Long;", // kClassCacheJavaLangLong + "Ljava/lang/Short;", // kClassCacheJavaLangShort + "Ljava/lang/Math;", // kClassCacheJavaLangMath + "Ljava/lang/StrictMath;", // kClassCacheJavaLangStrictMath + "Ljava/lang/Thread;", // kClassCacheJavaLangThread + "Llibcore/io/Memory;", // kClassCacheLibcoreIoMemory + "Lsun/misc/Unsafe;", // kClassCacheSunMiscUnsafe +}; + +const char* DexFileMethodInliner::kNameCacheNames[] = { + "reverseBytes", // kNameCacheReverseBytes + "doubleToRawLongBits", // kNameCacheDoubleToRawLongBits + "longBitsToDouble", // kNameCacheLongBitsToDouble + "floatToRawIntBits", // kNameCacheFloatToRawIntBits + "intBitsToFloat", // kNameCacheIntBitsToFloat + "abs", // kNameCacheAbs + "max", // kNameCacheMax + "min", // kNameCacheMin + "sqrt", // kNameCacheSqrt + "charAt", // kNameCacheCharAt + "compareTo", // kNameCacheCompareTo + "is_empty", // kNameCacheIsEmpty + "index_of", // kNameCacheIndexOf + "length", // kNameCacheLength + "currentThread", // kNameCacheCurrentThread + "peekByte", // kNameCachePeekByte + "peekIntNative", // kNameCachePeekIntNative + "peekLongNative", // kNameCachePeekLongNative + "peekShortNative", // kNameCachePeekShortNative + "pokeByte", // kNameCachePokeByte + "pokeIntNative", // kNameCachePokeIntNative + "pokeLongNative", // kNameCachePokeLongNative + "pokeShortNative", // kNameCachePokeShortNative + "compareAndSwapInt", // kNameCacheCompareAndSwapInt + "compareAndSwapObject", // kNameCacheCompareAndSwapObject + "getInt", // kNameCacheGetInt + "getIntVolatile", // kNameCacheGetIntVolatile + "putInt", // kNameCachePutInt + "putIntVolatile", // kNameCachePutIntVolatile + "putOrderedInt", // kNameCachePutOrderedInt + "getLong", // kNameCacheGetLong + "getLongVolatile", // kNameCacheGetLongVolatile + "putLong", // kNameCachePutLong + "putLongVolatile", // kNameCachePutLongVolatile + "putOrderedLong", // kNameCachePutOrderedLong + "getObject", // kNameCacheGetObject + "getObjectVolatile", // kNameCacheGetObjectVolatile + "putObject", // kNameCachePutObject + "putObjectVolatile", // kNameCachePutObjectVolatile + "putOrderedObject", // kNameCachePutOrderedObject +}; + +const DexFileMethodInliner::ProtoDef DexFileMethodInliner::kProtoCacheDefs[] = { + // kProtoCacheI_I + { kClassCacheInt, 1, { kClassCacheInt } }, + // kProtoCacheJ_J + { kClassCacheLong, 1, { kClassCacheLong } }, + // kProtoCacheS_S + { kClassCacheShort, 1, { kClassCacheShort } }, + // kProtoCacheD_D + { kClassCacheDouble, 1, { kClassCacheDouble } }, + // kProtoCacheD_J + { kClassCacheLong, 1, { kClassCacheDouble } }, + // kProtoCacheJ_D + { kClassCacheDouble, 1, { kClassCacheLong } }, + // kProtoCacheF_I + { kClassCacheInt, 1, { kClassCacheFloat } }, + // kProtoCacheI_F + { kClassCacheFloat, 1, { kClassCacheInt } }, + // kProtoCacheII_I + { kClassCacheInt, 2, { kClassCacheInt, kClassCacheInt } }, + // kProtoCacheI_C + { kClassCacheChar, 1, { kClassCacheInt } }, + // kProtoCacheString_I + { kClassCacheInt, 1, { kClassCacheJavaLangString } }, + // kProtoCache_Z + { kClassCacheBoolean, 0, { } }, + // kProtoCache_I + { kClassCacheInt, 0, { } }, + // kProtoCache_Thread + { kClassCacheJavaLangThread, 0, { } }, + // kProtoCacheJ_B + { kClassCacheByte, 1, { kClassCacheLong } }, + // kProtoCacheJ_I + { kClassCacheInt, 1, { kClassCacheLong } }, + // kProtoCacheJ_S + { kClassCacheShort, 1, { kClassCacheLong } }, + // kProtoCacheJB_V + { kClassCacheVoid, 2, { kClassCacheLong, kClassCacheByte } }, + // kProtoCacheJI_V + { kClassCacheVoid, 2, { kClassCacheLong, kClassCacheInt } }, + // kProtoCacheJJ_V + { kClassCacheVoid, 2, { kClassCacheLong, kClassCacheLong } }, + // kProtoCacheJS_V + { kClassCacheVoid, 2, { kClassCacheLong, kClassCacheShort } }, + // kProtoCacheObjectJII_Z + { kClassCacheBoolean, 4, { kClassCacheJavaLangObject, kClassCacheLong, + kClassCacheInt, kClassCacheInt } }, + // kProtoCacheObjectJObjectObject_Z + { kClassCacheBoolean, 4, { kClassCacheJavaLangObject, kClassCacheLong, + kClassCacheJavaLangObject, kClassCacheJavaLangObject } }, + // kProtoCacheObjectJ_I + { kClassCacheInt, 2, { kClassCacheJavaLangObject, kClassCacheLong } }, + // kProtoCacheObjectJI_V + { kClassCacheVoid, 3, { kClassCacheJavaLangObject, kClassCacheLong, kClassCacheInt } }, + // kProtoCacheObjectJ_J + { kClassCacheLong, 2, { kClassCacheJavaLangObject, kClassCacheLong } }, + // kProtoCacheObjectJJ_V + { kClassCacheVoid, 3, { kClassCacheJavaLangObject, kClassCacheLong, kClassCacheLong } }, + // kProtoCacheObjectJ_Object + { kClassCacheJavaLangObject, 2, { kClassCacheJavaLangObject, kClassCacheLong } }, + // kProtoCacheObjectJObject_V + { kClassCacheVoid, 3, { kClassCacheJavaLangObject, kClassCacheLong, + kClassCacheJavaLangObject } }, +}; + +DexFileMethodInliner::~DexFileMethodInliner() { +} + +DexFileMethodInliner::DexFileMethodInliner() + : dex_file_(NULL) +{ + COMPILE_ASSERT(kClassCacheFirst == 0, kClassCacheFirst_not_0); + COMPILE_ASSERT(arraysize(kClassCacheNames) == kClassCacheLast, bad_arraysize_kClassCacheNames); + COMPILE_ASSERT(kNameCacheFirst == 0, kNameCacheFirst_not_0); + COMPILE_ASSERT(arraysize(kNameCacheNames) == kNameCacheLast, bad_arraysize_kNameCacheNames); + COMPILE_ASSERT(kProtoCacheFirst == 0, kProtoCacheFirst_not_0); + COMPILE_ASSERT(arraysize(kProtoCacheDefs) == kProtoCacheLast, bad_arraysize_kProtoCacheNames); +} + +bool DexFileMethodInliner::IsIntrinsic(uint32_t method_index) const { + return intrinsics_.find(method_index) != intrinsics_.end(); +} + +bool DexFileMethodInliner::GenIntrinsic(Mir2Lir* backend, CallInfo* info) const { + auto it = intrinsics_.find(info->index); + if (it == intrinsics_.end()) { + return false; + } + const Intrinsic& intrinsic = it->second; + switch (intrinsic.opcode) { + case kIntrinsicDoubleCvt: + return backend->GenInlinedDoubleCvt(info); + case kIntrinsicFloatCvt: + return backend->GenInlinedFloatCvt(info); + case kIntrinsicReverseBytes: + return backend->GenInlinedReverseBytes(info, static_cast(intrinsic.data)); + case kIntrinsicAbsInt: + return backend->GenInlinedAbsInt(info); + case kIntrinsicAbsLong: + return backend->GenInlinedAbsLong(info); + case kIntrinsicMinMaxInt: + return backend->GenInlinedMinMaxInt(info, intrinsic.data & kIntrinsicFlagMin); + case kIntrinsicSqrt: + return backend->GenInlinedSqrt(info); + case kIntrinsicCharAt: + return backend->GenInlinedCharAt(info); + case kIntrinsicCompareTo: + return backend->GenInlinedStringCompareTo(info); + case kIntrinsicIsEmptyOrLength: + return backend->GenInlinedStringIsEmptyOrLength(info, intrinsic.data & kIntrinsicFlagIsEmpty); + case kIntrinsicIndexOf: + return backend->GenInlinedIndexOf(info, intrinsic.data & kIntrinsicFlagBase0); + case kIntrinsicCurrentThread: + return backend->GenInlinedCurrentThread(info); + case kIntrinsicPeek: + return backend->GenInlinedPeek(info, static_cast(intrinsic.data)); + case kIntrinsicPoke: + return backend->GenInlinedPoke(info, static_cast(intrinsic.data)); + case kIntrinsicCas32: + return backend->GenInlinedCas32(info, intrinsic.data & kIntrinsicFlagNeedWriteBarrier); + case kIntrinsicUnsafeGet: + return backend->GenInlinedUnsafeGet(info, intrinsic.data & kIntrinsicFlagIsLong, + intrinsic.data & kIntrinsicFlagIsVolatile); + case kIntrinsicUnsafePut: + return backend->GenInlinedUnsafePut(info, intrinsic.data & kIntrinsicFlagIsLong, + intrinsic.data & kIntrinsicFlagIsObject, + intrinsic.data & kIntrinsicFlagIsVolatile, + intrinsic.data & kIntrinsicFlagIsOrdered); + default: + LOG(FATAL) << "Unexpected intrinsic opcode: " << intrinsic.opcode; + return false; // avoid warning "control reaches end of non-void function" + } +} + +uint32_t DexFileMethodInliner::FindClassIndex(const DexFile* dex_file, IndexCache* cache, + ClassCacheIndex index) { + uint32_t* class_index = &cache->class_indexes[index]; + if (*class_index != kIndexUnresolved) { + return *class_index; + } + + const DexFile::StringId* string_id = dex_file->FindStringId(kClassCacheNames[index]); + if (string_id == nullptr) { + *class_index = kIndexNotFound; + return *class_index; + } + uint32_t string_index = dex_file->GetIndexForStringId(*string_id); + + const DexFile::TypeId* type_id = dex_file->FindTypeId(string_index); + if (type_id == nullptr) { + *class_index = kIndexNotFound; + return *class_index; + } + *class_index = dex_file->GetIndexForTypeId(*type_id); + return *class_index; +} + +uint32_t DexFileMethodInliner::FindNameIndex(const DexFile* dex_file, IndexCache* cache, + NameCacheIndex index) { + uint32_t* name_index = &cache->name_indexes[index]; + if (*name_index != kIndexUnresolved) { + return *name_index; + } + + const DexFile::StringId* string_id = dex_file->FindStringId(kNameCacheNames[index]); + if (string_id == nullptr) { + *name_index = kIndexNotFound; + return *name_index; + } + *name_index = dex_file->GetIndexForStringId(*string_id); + return *name_index; +} + +uint32_t DexFileMethodInliner::FindProtoIndex(const DexFile* dex_file, IndexCache* cache, + ProtoCacheIndex index) { + uint32_t* proto_index = &cache->proto_indexes[index]; + if (*proto_index != kIndexUnresolved) { + return *proto_index; + } + + const ProtoDef& proto_def = kProtoCacheDefs[index]; + uint32_t return_index = FindClassIndex(dex_file, cache, proto_def.return_type); + if (return_index == kIndexNotFound) { + *proto_index = kIndexNotFound; + return *proto_index; + } + uint16_t return_type = static_cast(return_index); + DCHECK_EQ(static_cast(return_type), return_index); + + uint32_t signature_length = proto_def.param_count; + uint16_t signature_type_idxs[kProtoMaxParams]; + for (uint32_t i = 0; i != signature_length; ++i) { + uint32_t param_index = FindClassIndex(dex_file, cache, proto_def.params[i]); + if (param_index == kIndexNotFound) { + *proto_index = kIndexNotFound; + return *proto_index; + } + signature_type_idxs[i] = static_cast(param_index); + DCHECK_EQ(static_cast(signature_type_idxs[i]), param_index); + } + + const DexFile::ProtoId* proto_id = dex_file->FindProtoId(return_type, signature_type_idxs, + signature_length); + if (proto_id == nullptr) { + *proto_index = kIndexNotFound; + return *proto_index; + } + *proto_index = dex_file->GetIndexForProtoId(*proto_id); + return *proto_index; +} + +uint32_t DexFileMethodInliner::FindMethodIndex(const DexFile* dex_file, IndexCache* cache, + const MethodDef& method_def) { + uint32_t declaring_class_index = FindClassIndex(dex_file, cache, method_def.declaring_class); + if (declaring_class_index == kIndexNotFound) { + return kIndexNotFound; + } + uint32_t name_index = FindNameIndex(dex_file, cache, method_def.name); + if (name_index == kIndexNotFound) { + return kIndexNotFound; + } + uint32_t proto_index = FindProtoIndex(dex_file, cache, method_def.proto); + if (proto_index == kIndexNotFound) { + return kIndexNotFound; + } + const DexFile::MethodId* method_id = + dex_file->FindMethodId(dex_file->GetTypeId(declaring_class_index), + dex_file->GetStringId(name_index), + dex_file->GetProtoId(proto_index)); + if (method_id == nullptr) { + return kIndexNotFound; + } + return dex_file->GetIndexForMethodId(*method_id); +} + +DexFileMethodInliner::IndexCache::IndexCache() { + std::fill_n(class_indexes, arraysize(class_indexes), kIndexUnresolved); + std::fill_n(name_indexes, arraysize(name_indexes), kIndexUnresolved); + std::fill_n(proto_indexes, arraysize(proto_indexes), kIndexUnresolved); +} + +void DexFileMethodInliner::DoFindIntrinsics(const DexFile* dex_file, IndexCache* cache, + const IntrinsicDef* defs, uint32_t def_count) { + DCHECK(dex_file != nullptr); + DCHECK(dex_file_ == nullptr); + for (uint32_t i = 0u; i != def_count; ++i) { + uint32_t method_id = FindMethodIndex(dex_file, cache, defs[i].method_def); + if (method_id != kIndexNotFound) { + DCHECK(intrinsics_.find(method_id) == intrinsics_.end()); + intrinsics_[method_id] = defs[i].intrinsic; + } + } + dex_file_ = dex_file; +} + +} // namespace art diff --git a/compiler/dex/quick/dex_file_method_inliner.h b/compiler/dex/quick/dex_file_method_inliner.h new file mode 100644 index 00000000000..95b8dd3adfb --- /dev/null +++ b/compiler/dex/quick/dex_file_method_inliner.h @@ -0,0 +1,318 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ART_COMPILER_DEX_QUICK_DEX_FILE_METHOD_INLINER_H_ +#define ART_COMPILER_DEX_QUICK_DEX_FILE_METHOD_INLINER_H_ + +#include +#include + +namespace art { + +class CallInfo; +class DexFile; +class Mir2Lir; + +enum IntrinsicOpcode { + kIntrinsicDoubleCvt, + kIntrinsicFloatCvt, + kIntrinsicReverseBytes, + kIntrinsicAbsInt, + kIntrinsicAbsLong, + kIntrinsicMinMaxInt, + kIntrinsicSqrt, + kIntrinsicCharAt, + kIntrinsicCompareTo, + kIntrinsicIsEmptyOrLength, + kIntrinsicIndexOf, + kIntrinsicCurrentThread, + kIntrinsicPeek, + kIntrinsicPoke, + kIntrinsicCas32, + kIntrinsicUnsafeGet, + kIntrinsicUnsafePut, +}; + +enum IntrinsicFlags { + kIntrinsicFlagNone = 0, + + // kIntrinsicMinMaxInt + kIntrinsicFlagMax = kIntrinsicFlagNone, + kIntrinsicFlagMin = 1, + + // kIntrinsicIsEmptyOrLength + kIntrinsicFlagLength = kIntrinsicFlagNone, + kIntrinsicFlagIsEmpty = 1, + + // kIntrinsicIndexOf + kIntrinsicFlagBase0 = 1, + + // kIntrinsicUnsafeCas32 + kIntrinsicFlagDontNeedWriteBarrier = 0, + kIntrinsicFlagNeedWriteBarrier = 1, + + // kIntrinsicUnsafeGet, kIntrinsicUnsafePut + kIntrinsicFlagIsLong = 1, + kIntrinsicFlagIsVolatile = 2, + // kIntrinsicUnsafePut + kIntrinsicFlagIsObject = 4, + kIntrinsicFlagIsOrdered = 8, +}; + +struct Intrinsic { + IntrinsicOpcode opcode; + uint32_t data; +}; + +/** + * Handles inlining of methods from a particular DexFile. + * + * Intrinsics are a special case of inline methods. The DexFile indices for + * all the supported intrinsic methods are looked up once by the FindIntrinsics + * function and cached by this class for quick lookup by the method index. + * + * TODO: Detect short methods (at least getters, setters and empty functions) + * from the verifier and mark them for inlining. Inline these methods early + * during compilation to allow further optimizations. Similarly, provide + * additional information about intrinsics to the early phases of compilation. + */ +class DexFileMethodInliner { + public: + virtual ~DexFileMethodInliner(); + + /** + * Find all known intrinsic methods in the dex_file and cache their indices. + */ + virtual void FindIntrinsics(const DexFile* dex_file) = 0; + + /** + * Check whether a particular method index corresponds to an intrinsic function. + */ + bool IsIntrinsic(uint32_t method_index) const; + + /** + * Generate code for an intrinsic function invocation. + * + * TODO: This should be target-specific. For the time being, + * it's shared since it dispatches everything to backend. + */ + bool GenIntrinsic(Mir2Lir* backend, CallInfo* info) const; + + protected: + DexFileMethodInliner(); + + /** + * To avoid multiple lookups of a class by its descriptor, we cache its + * type index in the IndexCache. These are the indexes into the IndexCache + * class_indexes array. + */ + enum ClassCacheIndex : uint8_t { // unit8_t to save space, make larger if needed + kClassCacheFirst = 0, + kClassCacheBoolean = kClassCacheFirst, + kClassCacheByte, + kClassCacheChar, + kClassCacheShort, + kClassCacheInt, + kClassCacheLong, + kClassCacheFloat, + kClassCacheDouble, + kClassCacheVoid, + kClassCacheJavaLangObject, + kClassCacheJavaLangString, + kClassCacheJavaLangDouble, + kClassCacheJavaLangFloat, + kClassCacheJavaLangInteger, + kClassCacheJavaLangLong, + kClassCacheJavaLangShort, + kClassCacheJavaLangMath, + kClassCacheJavaLangStrictMath, + kClassCacheJavaLangThread, + kClassCacheLibcoreIoMemory, + kClassCacheSunMiscUnsafe, + kClassCacheLast + }; + + /** + * To avoid multiple lookups of a method name string, we cache its string + * index in the IndexCache. These are the indexes into the IndexCache + * name_indexes array. + */ + enum NameCacheIndex : uint8_t { // unit8_t to save space, make larger if needed + kNameCacheFirst = 0, + kNameCacheReverseBytes = kNameCacheFirst, + kNameCacheDoubleToRawLongBits, + kNameCacheLongBitsToDouble, + kNameCacheFloatToRawIntBits, + kNameCacheIntBitsToFloat, + kNameCacheAbs, + kNameCacheMax, + kNameCacheMin, + kNameCacheSqrt, + kNameCacheCharAt, + kNameCacheCompareTo, + kNameCacheIsEmpty, + kNameCacheIndexOf, + kNameCacheLength, + kNameCacheCurrentThread, + kNameCachePeekByte, + kNameCachePeekIntNative, + kNameCachePeekLongNative, + kNameCachePeekShortNative, + kNameCachePokeByte, + kNameCachePokeIntNative, + kNameCachePokeLongNative, + kNameCachePokeShortNative, + kNameCacheCompareAndSwapInt, + kNameCacheCompareAndSwapObject, + kNameCacheGetInt, + kNameCacheGetIntVolatile, + kNameCachePutInt, + kNameCachePutIntVolatile, + kNameCachePutOrderedInt, + kNameCacheGetLong, + kNameCacheGetLongVolatile, + kNameCachePutLong, + kNameCachePutLongVolatile, + kNameCachePutOrderedLong, + kNameCacheGetObject, + kNameCacheGetObjectVolatile, + kNameCachePutObject, + kNameCachePutObjectVolatile, + kNameCachePutOrderedObject, + kNameCacheLast + }; + + /** + * To avoid multiple lookups of a method signature, we cache its proto + * index in the IndexCache. These are the indexes into the IndexCache + * proto_indexes array. + */ + enum ProtoCacheIndex : uint8_t { // unit8_t to save space, make larger if needed + kProtoCacheFirst = 0, + kProtoCacheI_I = kProtoCacheFirst, + kProtoCacheJ_J, + kProtoCacheS_S, + kProtoCacheD_D, + kProtoCacheD_J, + kProtoCacheJ_D, + kProtoCacheF_I, + kProtoCacheI_F, + kProtoCacheII_I, + kProtoCacheI_C, + kProtoCacheString_I, + kProtoCache_Z, + kProtoCache_I, + kProtoCache_Thread, + kProtoCacheJ_B, + kProtoCacheJ_I, + kProtoCacheJ_S, + kProtoCacheJB_V, + kProtoCacheJI_V, + kProtoCacheJJ_V, + kProtoCacheJS_V, + kProtoCacheObjectJII_Z, + kProtoCacheObjectJObjectObject_Z, + kProtoCacheObjectJ_I, + kProtoCacheObjectJI_V, + kProtoCacheObjectJ_J, + kProtoCacheObjectJJ_V, + kProtoCacheObjectJ_Object, + kProtoCacheObjectJObject_V, + kProtoCacheLast + }; + + /** + * The maximum number of method parameters we support in the ProtoDef. + */ + static constexpr uint32_t kProtoMaxParams = 6; + + /** + * The method signature (proto) definition using cached class indexes. + * The return_type and params are used with the IndexCache to look up + * appropriate class indexes to be passed to DexFile::FindProtoId(). + */ + struct ProtoDef { + ClassCacheIndex return_type; + uint8_t param_count; + ClassCacheIndex params[kProtoMaxParams]; + }; + + /** + * The method definition using cached class, name and proto indexes. + * The class index, method name index and proto index are used with + * IndexCache to look up appropriate parameters for DexFile::FindMethodId(). + */ + struct MethodDef { + ClassCacheIndex declaring_class; + NameCacheIndex name; + ProtoCacheIndex proto; + }; + + /** + * The definition of an intrinsic function binds the method definition + * to an Intrinsic. + */ + struct IntrinsicDef { + MethodDef method_def; + Intrinsic intrinsic; + }; + + /** + * Cache for class, method name and method signature indexes used during + * intrinsic function lookup to avoid multiple lookups of the same items. + * + * Many classes have multiple intrinsics and/or they are used in multiple + * method signatures and we want to avoid repeated lookups since they are + * not exactly cheap. The method names and method signatures are sometimes + * reused and therefore cached as well. + */ + struct IndexCache { + IndexCache(); + + uint32_t class_indexes[kClassCacheLast - kClassCacheFirst]; + uint32_t name_indexes[kNameCacheLast - kNameCacheFirst]; + uint32_t proto_indexes[kProtoCacheLast - kProtoCacheFirst]; + }; + + static const char* kClassCacheNames[]; + static const char* kNameCacheNames[]; + static const ProtoDef kProtoCacheDefs[]; + + static const uint32_t kIndexNotFound = static_cast(-1); + static const uint32_t kIndexUnresolved = static_cast(-2); + + static uint32_t FindClassIndex(const DexFile* dex_file, IndexCache* cache, + ClassCacheIndex index); + static uint32_t FindNameIndex(const DexFile* dex_file, IndexCache* cache, + NameCacheIndex index); + static uint32_t FindProtoIndex(const DexFile* dex_file, IndexCache* cache, + ProtoCacheIndex index); + static uint32_t FindMethodIndex(const DexFile* dex_file, IndexCache* cache, + const MethodDef& method_def); + + void DoFindIntrinsics(const DexFile* dex_file, IndexCache* cache, + const IntrinsicDef* defs, uint32_t def_count); + + /* + * Maps method indexes (for the particular DexFile) to Intrinsic defintions. + */ + std::map intrinsics_; + const DexFile* dex_file_; +}; + +} // namespace art + +#endif // ART_COMPILER_DEX_QUICK_DEX_FILE_METHOD_INLINER_H_ diff --git a/compiler/dex/quick/dex_file_to_method_inliner_map.cc b/compiler/dex/quick/dex_file_to_method_inliner_map.cc new file mode 100644 index 00000000000..38022d2142e --- /dev/null +++ b/compiler/dex/quick/dex_file_to_method_inliner_map.cc @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2013 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 +#include +#include "thread.h" +#include "thread-inl.h" +#include "base/mutex.h" +#include "base/mutex-inl.h" +#include "base/logging.h" +#include "driver/compiler_driver.h" +#include "dex/quick/arm/arm_dex_file_method_inliner.h" +#include "dex/quick/mips/mips_dex_file_method_inliner.h" +#include "dex/quick/x86/x86_dex_file_method_inliner.h" + +#include "dex_file_to_method_inliner_map.h" + +namespace art { + +DexFileToMethodInlinerMap::DexFileToMethodInlinerMap(const CompilerDriver* compiler) + : compiler_(compiler), + mutex_("inline_helper_mutex") +{ +} + +DexFileToMethodInlinerMap::~DexFileToMethodInlinerMap() { + for (auto& entry : inliners_) { + delete entry.second; + } +} + +const DexFileMethodInliner& DexFileToMethodInlinerMap::GetMethodInliner(const DexFile* dex_file) { + Thread* self = Thread::Current(); + { + ReaderMutexLock lock(self, mutex_); + auto it = inliners_.find(dex_file); + if (it != inliners_.end()) { + return *it->second; + } + } + + WriterMutexLock lock(self, mutex_); + DexFileMethodInliner** inliner = &inliners_[dex_file]; // inserts new entry if not found + if (*inliner) { + return **inliner; + } + switch (compiler_->GetInstructionSet()) { + case kThumb2: + *inliner = new ArmDexFileMethodInliner; + break; + case kX86: + *inliner = new X86DexFileMethodInliner; + break; + case kMips: + *inliner = new MipsDexFileMethodInliner; + break; + default: + LOG(FATAL) << "Unexpected instruction set: " << compiler_->GetInstructionSet(); + } + DCHECK(*inliner != nullptr); + // TODO: per-dex file locking for the intrinsics container filling. + (*inliner)->FindIntrinsics(dex_file); + return **inliner; +} + +} // namespace art diff --git a/compiler/dex/quick/dex_file_to_method_inliner_map.h b/compiler/dex/quick/dex_file_to_method_inliner_map.h new file mode 100644 index 00000000000..e0b67e5a4b9 --- /dev/null +++ b/compiler/dex/quick/dex_file_to_method_inliner_map.h @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ART_COMPILER_DEX_QUICK_DEX_FILE_TO_METHOD_INLINER_MAP_H_ +#define ART_COMPILER_DEX_QUICK_DEX_FILE_TO_METHOD_INLINER_MAP_H_ + +#include +#include +#include "base/macros.h" +#include "base/mutex.h" + +#include "dex/quick/dex_file_method_inliner.h" + +namespace art { + +class CompilerDriver; +class DexFile; + +/** + * Map each DexFile to its DexFileMethodInliner. + * + * The method inliner is created and initialized the first time it's requested + * for a particular DexFile. + */ +class DexFileToMethodInlinerMap { + public: + DexFileToMethodInlinerMap(const CompilerDriver* compiler); + ~DexFileToMethodInlinerMap(); + + const DexFileMethodInliner& GetMethodInliner(const DexFile* dex_file) LOCKS_EXCLUDED(mutex_); + + private: + const CompilerDriver* const compiler_; + ReaderWriterMutex mutex_; + std::map inliners_ GUARDED_BY(mutex_); +}; + +} // namespace art + +#endif // ART_COMPILER_DEX_QUICK_DEX_FILE_TO_METHOD_INLINER_MAP_H_ diff --git a/compiler/dex/quick/gen_invoke.cc b/compiler/dex/quick/gen_invoke.cc index d1a9a132bc7..469c5770713 100644 --- a/compiler/dex/quick/gen_invoke.cc +++ b/compiler/dex/quick/gen_invoke.cc @@ -15,6 +15,9 @@ */ #include "dex/compiler_ir.h" +#include "dex/frontend.h" +#include "dex/quick/dex_file_method_inliner.h" +#include "dex/quick/dex_file_to_method_inliner_map.h" #include "dex_file-inl.h" #include "entrypoints/quick/quick_entrypoints.h" #include "invoke_type.h" @@ -1227,202 +1230,17 @@ bool Mir2Lir::GenInlinedUnsafePut(CallInfo* info, bool is_long, return true; } -bool Mir2Lir::GenIntrinsic(CallInfo* info) { - if (info->opt_flags & MIR_INLINED) { - return false; - } - /* - * TODO: move these to a target-specific structured constant array - * and use a generic match function. The list of intrinsics may be - * slightly different depending on target. - * TODO: Fold this into a matching function that runs during - * basic block building. This should be part of the action for - * small method inlining and recognition of the special object init - * method. By doing this during basic block construction, we can also - * take advantage of/generate new useful dataflow info. - */ - const DexFile::MethodId& target_mid = cu_->dex_file->GetMethodId(info->index); - const DexFile::TypeId& declaring_type = cu_->dex_file->GetTypeId(target_mid.class_idx_); - StringPiece tgt_methods_declaring_class( - cu_->dex_file->StringDataByIdx(declaring_type.descriptor_idx_)); - if (tgt_methods_declaring_class.starts_with("Ljava/lang/")) { - tgt_methods_declaring_class.remove_prefix(sizeof("Ljava/lang/") - 1); - if (tgt_methods_declaring_class.starts_with("Double;")) { - std::string tgt_method(PrettyMethod(info->index, *cu_->dex_file)); - if (tgt_method == "long java.lang.Double.doubleToRawLongBits(double)") { - return GenInlinedDoubleCvt(info); - } - if (tgt_method == "double java.lang.Double.longBitsToDouble(long)") { - return GenInlinedDoubleCvt(info); - } - } else if (tgt_methods_declaring_class.starts_with("Float;")) { - std::string tgt_method(PrettyMethod(info->index, *cu_->dex_file)); - if (tgt_method == "int java.lang.Float.floatToRawIntBits(float)") { - return GenInlinedFloatCvt(info); - } - if (tgt_method == "float java.lang.Float.intBitsToFloat(int)") { - return GenInlinedFloatCvt(info); - } - } else if (tgt_methods_declaring_class.starts_with("Integer;")) { - std::string tgt_method(PrettyMethod(info->index, *cu_->dex_file)); - if (tgt_method == "int java.lang.Integer.reverseBytes(int)") { - return GenInlinedReverseBytes(info, kWord); - } - } else if (tgt_methods_declaring_class.starts_with("Long;")) { - std::string tgt_method(PrettyMethod(info->index, *cu_->dex_file)); - if (tgt_method == "long java.lang.Long.reverseBytes(long)") { - return GenInlinedReverseBytes(info, kLong); - } - } else if (tgt_methods_declaring_class.starts_with("Math;") || - tgt_methods_declaring_class.starts_with("StrictMath;")) { - std::string tgt_method(PrettyMethod(info->index, *cu_->dex_file)); - if (tgt_method == "int java.lang.Math.abs(int)" || - tgt_method == "int java.lang.StrictMath.abs(int)") { - return GenInlinedAbsInt(info); - } - if (tgt_method == "long java.lang.Math.abs(long)" || - tgt_method == "long java.lang.StrictMath.abs(long)") { - return GenInlinedAbsLong(info); - } - if (tgt_method == "int java.lang.Math.max(int, int)" || - tgt_method == "int java.lang.StrictMath.max(int, int)") { - return GenInlinedMinMaxInt(info, false /* is_min */); - } - if (tgt_method == "int java.lang.Math.min(int, int)" || - tgt_method == "int java.lang.StrictMath.min(int, int)") { - return GenInlinedMinMaxInt(info, true /* is_min */); - } - if (tgt_method == "double java.lang.Math.sqrt(double)" || - tgt_method == "double java.lang.StrictMath.sqrt(double)") { - return GenInlinedSqrt(info); - } - } else if (tgt_methods_declaring_class.starts_with("Short;")) { - std::string tgt_method(PrettyMethod(info->index, *cu_->dex_file)); - if (tgt_method == "short java.lang.Short.reverseBytes(short)") { - return GenInlinedReverseBytes(info, kSignedHalf); - } - } else if (tgt_methods_declaring_class.starts_with("String;")) { - std::string tgt_method(PrettyMethod(info->index, *cu_->dex_file)); - if (tgt_method == "char java.lang.String.charAt(int)") { - return GenInlinedCharAt(info); - } - if (tgt_method == "int java.lang.String.compareTo(java.lang.String)") { - return GenInlinedStringCompareTo(info); - } - if (tgt_method == "boolean java.lang.String.is_empty()") { - return GenInlinedStringIsEmptyOrLength(info, true /* is_empty */); - } - if (tgt_method == "int java.lang.String.index_of(int, int)") { - return GenInlinedIndexOf(info, false /* base 0 */); - } - if (tgt_method == "int java.lang.String.index_of(int)") { - return GenInlinedIndexOf(info, true /* base 0 */); - } - if (tgt_method == "int java.lang.String.length()") { - return GenInlinedStringIsEmptyOrLength(info, false /* is_empty */); - } - } else if (tgt_methods_declaring_class.starts_with("Thread;")) { - std::string tgt_method(PrettyMethod(info->index, *cu_->dex_file)); - if (tgt_method == "java.lang.Thread java.lang.Thread.currentThread()") { - return GenInlinedCurrentThread(info); - } - } - } else if (tgt_methods_declaring_class.starts_with("Llibcore/io/Memory;")) { - std::string tgt_method(PrettyMethod(info->index, *cu_->dex_file)); - if (tgt_method == "byte libcore.io.Memory.peekByte(long)") { - return GenInlinedPeek(info, kSignedByte); - } - if (tgt_method == "int libcore.io.Memory.peekIntNative(long)") { - return GenInlinedPeek(info, kWord); - } - if (tgt_method == "long libcore.io.Memory.peekLongNative(long)") { - return GenInlinedPeek(info, kLong); - } - if (tgt_method == "short libcore.io.Memory.peekShortNative(long)") { - return GenInlinedPeek(info, kSignedHalf); - } - if (tgt_method == "void libcore.io.Memory.pokeByte(long, byte)") { - return GenInlinedPoke(info, kSignedByte); - } - if (tgt_method == "void libcore.io.Memory.pokeIntNative(long, int)") { - return GenInlinedPoke(info, kWord); - } - if (tgt_method == "void libcore.io.Memory.pokeLongNative(long, long)") { - return GenInlinedPoke(info, kLong); - } - if (tgt_method == "void libcore.io.Memory.pokeShortNative(long, short)") { - return GenInlinedPoke(info, kSignedHalf); - } - } else if (tgt_methods_declaring_class.starts_with("Lsun/misc/Unsafe;")) { - std::string tgt_method(PrettyMethod(info->index, *cu_->dex_file)); - if (tgt_method == "boolean sun.misc.Unsafe.compareAndSwapInt(java.lang.Object, long, int, int)") { - return GenInlinedCas32(info, false); - } - if (tgt_method == "boolean sun.misc.Unsafe.compareAndSwapObject(java.lang.Object, long, java.lang.Object, java.lang.Object)") { - return GenInlinedCas32(info, true); - } - if (tgt_method == "int sun.misc.Unsafe.getInt(java.lang.Object, long)") { - return GenInlinedUnsafeGet(info, false /* is_long */, false /* is_volatile */); - } - if (tgt_method == "int sun.misc.Unsafe.getIntVolatile(java.lang.Object, long)") { - return GenInlinedUnsafeGet(info, false /* is_long */, true /* is_volatile */); - } - if (tgt_method == "void sun.misc.Unsafe.putInt(java.lang.Object, long, int)") { - return GenInlinedUnsafePut(info, false /* is_long */, false /* is_object */, - false /* is_volatile */, false /* is_ordered */); - } - if (tgt_method == "void sun.misc.Unsafe.putIntVolatile(java.lang.Object, long, int)") { - return GenInlinedUnsafePut(info, false /* is_long */, false /* is_object */, - true /* is_volatile */, false /* is_ordered */); - } - if (tgt_method == "void sun.misc.Unsafe.putOrderedInt(java.lang.Object, long, int)") { - return GenInlinedUnsafePut(info, false /* is_long */, false /* is_object */, - false /* is_volatile */, true /* is_ordered */); - } - if (tgt_method == "long sun.misc.Unsafe.getLong(java.lang.Object, long)") { - return GenInlinedUnsafeGet(info, true /* is_long */, false /* is_volatile */); - } - if (tgt_method == "long sun.misc.Unsafe.getLongVolatile(java.lang.Object, long)") { - return GenInlinedUnsafeGet(info, true /* is_long */, true /* is_volatile */); - } - if (tgt_method == "void sun.misc.Unsafe.putLong(java.lang.Object, long, long)") { - return GenInlinedUnsafePut(info, true /* is_long */, false /* is_object */, - false /* is_volatile */, false /* is_ordered */); - } - if (tgt_method == "void sun.misc.Unsafe.putLongVolatile(java.lang.Object, long, long)") { - return GenInlinedUnsafePut(info, true /* is_long */, false /* is_object */, - true /* is_volatile */, false /* is_ordered */); - } - if (tgt_method == "void sun.misc.Unsafe.putOrderedLong(java.lang.Object, long, long)") { - return GenInlinedUnsafePut(info, true /* is_long */, false /* is_object */, - false /* is_volatile */, true /* is_ordered */); - } - if (tgt_method == "java.lang.Object sun.misc.Unsafe.getObject(java.lang.Object, long)") { - return GenInlinedUnsafeGet(info, false /* is_long */, false /* is_volatile */); - } - if (tgt_method == "java.lang.Object sun.misc.Unsafe.getObjectVolatile(java.lang.Object, long)") { - return GenInlinedUnsafeGet(info, false /* is_long */, true /* is_volatile */); - } - if (tgt_method == "void sun.misc.Unsafe.putObject(java.lang.Object, long, java.lang.Object)") { - return GenInlinedUnsafePut(info, false /* is_long */, true /* is_object */, - false /* is_volatile */, false /* is_ordered */); - } - if (tgt_method == "void sun.misc.Unsafe.putObjectVolatile(java.lang.Object, long, java.lang.Object)") { - return GenInlinedUnsafePut(info, false /* is_long */, true /* is_object */, - true /* is_volatile */, false /* is_ordered */); +void Mir2Lir::GenInvoke(CallInfo* info) { + if (!(info->opt_flags & MIR_INLINED)) { + if (inliner_ == nullptr) { + QuickCompilerContext* context = reinterpret_cast( + cu_->compiler_driver->GetCompilerContext()); + inliner_ = &context->GetInlinerMap()->GetMethodInliner(cu_->dex_file); } - if (tgt_method == "void sun.misc.Unsafe.putOrderedObject(java.lang.Object, long, java.lang.Object)") { - return GenInlinedUnsafePut(info, false /* is_long */, true /* is_object */, - false /* is_volatile */, true /* is_ordered */); + if (inliner_->GenIntrinsic(this, info)) { + return; } } - return false; -} - -void Mir2Lir::GenInvoke(CallInfo* info) { - if (GenIntrinsic(info)) { - return; - } InvokeType original_type = info->type; // avoiding mutation by ComputeInvokeInfo int call_state = 0; LIR* null_ck; diff --git a/compiler/dex/quick/mips/mips_dex_file_method_inliner.cc b/compiler/dex/quick/mips/mips_dex_file_method_inliner.cc new file mode 100644 index 00000000000..99eda82d5aa --- /dev/null +++ b/compiler/dex/quick/mips/mips_dex_file_method_inliner.cc @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2013 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 "base/logging.h" +#include "base/macros.h" +#include "dex/compiler_enums.h" + +#include "mips_dex_file_method_inliner.h" + +namespace art { + +const DexFileMethodInliner::IntrinsicDef MipsDexFileMethodInliner::kIntrinsicMethods[] = { +#define INTRINSIC(c, n, p, o, d) \ + { { kClassCache ## c, kNameCache ## n, kProtoCache ## p }, { o, d } } + + //INTRINSIC(JavaLangDouble, DoubleToRawLongBits, D_J, kIntrinsicDoubleCvt, 0), + //INTRINSIC(JavaLangDouble, LongBitsToDouble, J_D, kIntrinsicDoubleCvt, 0), + //INTRINSIC(JavaLangFloat, FloatToRawIntBits, F_I, kIntrinsicFloatCvt, 0), + //INTRINSIC(JavaLangFloat, IntBitsToFloat, I_F, kIntrinsicFloatCvt, 0), + + //INTRINSIC(JavaLangInteger, ReverseBytes, I_I, kIntrinsicReverseBytes, kWord), + //INTRINSIC(JavaLangLong, ReverseBytes, J_J, kIntrinsicReverseBytes, kLong), + //INTRINSIC(JavaLangShort, ReverseBytes, S_S, kIntrinsicReverseBytes, kSignedHalf), + + //INTRINSIC(JavaLangMath, Abs, I_I, kIntrinsicAbsInt, 0), + //INTRINSIC(JavaLangStrictMath, Abs, I_I, kIntrinsicAbsInt, 0), + //INTRINSIC(JavaLangMath, Abs, J_J, kIntrinsicAbsLong, 0), + //INTRINSIC(JavaLangStrictMath, Abs, J_J, kIntrinsicAbsLong, 0), + //INTRINSIC(JavaLangMath, Min, II_I, kIntrinsicMinMaxInt, kIntrinsicFlagMin), + //INTRINSIC(JavaLangStrictMath, Min, II_I, kIntrinsicMinMaxInt, kIntrinsicFlagMin), + //INTRINSIC(JavaLangMath, Max, II_I, kIntrinsicMinMaxInt, kIntrinsicFlagMax), + //INTRINSIC(JavaLangStrictMath, Max, II_I, kIntrinsicMinMaxInt, kIntrinsicFlagMax), + //INTRINSIC(JavaLangMath, Sqrt, D_D, kIntrinsicSqrt, 0), + //INTRINSIC(JavaLangStrictMath, Sqrt, D_D, kIntrinsicSqrt, 0), + + //INTRINSIC(JavaLangString, CharAt, I_C, kIntrinsicCharAt, 0), + //INTRINSIC(JavaLangString, CompareTo, String_I, kIntrinsicCompareTo, 0), + //INTRINSIC(JavaLangString, IsEmpty, _Z, kIntrinsicIsEmptyOrLength, kIntrinsicFlagIsEmpty), + //INTRINSIC(JavaLangString, IndexOf, II_I, kIntrinsicIndexOf, kIntrinsicFlagNone), + //INTRINSIC(JavaLangString, IndexOf, I_I, kIntrinsicIndexOf, kIntrinsicFlagBase0), + //INTRINSIC(JavaLangString, Length, _I, kIntrinsicIsEmptyOrLength, kIntrinsicFlagLength), + + INTRINSIC(JavaLangThread, CurrentThread, _Thread, kIntrinsicCurrentThread, 0), + + INTRINSIC(LibcoreIoMemory, PeekByte, J_B, kIntrinsicPeek, kSignedByte), + //INTRINSIC(LibcoreIoMemory, PeekIntNative, J_I, kIntrinsicPeek, kWord), + //INTRINSIC(LibcoreIoMemory, PeekLongNative, J_J, kIntrinsicPeek, kLong), + //INTRINSIC(LibcoreIoMemory, PeekShortNative, J_S, kIntrinsicPeek, kSignedHalf), + INTRINSIC(LibcoreIoMemory, PokeByte, JB_V, kIntrinsicPoke, kSignedByte), + //INTRINSIC(LibcoreIoMemory, PokeIntNative, JI_V, kIntrinsicPoke, kWord), + //INTRINSIC(LibcoreIoMemory, PokeLongNative, JJ_V, kIntrinsicPoke, kLong), + //INTRINSIC(LibcoreIoMemory, PokeShortNative, JS_V, kIntrinsicPoke, kSignedHalf), + + //INTRINSIC(SunMiscUnsafe, CompareAndSwapInt, ObjectJII_Z, kIntrinsicCas32, + // kIntrinsicFlagDontNeedWriteBarrier), + //INTRINSIC(SunMiscUnsafe, CompareAndSwapObject, ObjectJObjectObject_Z, kIntrinsicCas32, + // kIntrinsicFlagNeedWriteBarrier), + +#define UNSAFE_GET_PUT(type, code, type_flags) \ + INTRINSIC(SunMiscUnsafe, Get ## type, ObjectJ_ ## code, kIntrinsicUnsafeGet, \ + type_flags & ~kIntrinsicFlagIsObject), \ + INTRINSIC(SunMiscUnsafe, Get ## type ## Volatile, ObjectJ_ ## code, kIntrinsicUnsafeGet, \ + (type_flags | kIntrinsicFlagIsVolatile) & ~kIntrinsicFlagIsObject), \ + INTRINSIC(SunMiscUnsafe, Put ## type, ObjectJ ## code ## _V, kIntrinsicUnsafePut, \ + type_flags), \ + INTRINSIC(SunMiscUnsafe, Put ## type ## Volatile, ObjectJ ## code ## _V, kIntrinsicUnsafePut, \ + type_flags | kIntrinsicFlagIsVolatile), \ + INTRINSIC(SunMiscUnsafe, PutOrdered ## type, ObjectJ ## code ## _V, kIntrinsicUnsafePut, \ + type_flags | kIntrinsicFlagIsOrdered) + + //UNSAFE_GET_PUT(Int, I, kIntrinsicFlagNone), + //UNSAFE_GET_PUT(Long, J, kIntrinsicFlagIsLong), + //UNSAFE_GET_PUT(Object, Object, kIntrinsicFlagIsObject), +#undef UNSAFE_GET_PUT + +#undef INTRINSIC +}; + +MipsDexFileMethodInliner::MipsDexFileMethodInliner() { +} + +MipsDexFileMethodInliner::~MipsDexFileMethodInliner() { +} + +void MipsDexFileMethodInliner::FindIntrinsics(const DexFile* dex_file) { + IndexCache cache; + DoFindIntrinsics(dex_file, &cache, kIntrinsicMethods, arraysize(kIntrinsicMethods)); +} + +} // namespace art diff --git a/compiler/dex/quick/mips/mips_dex_file_method_inliner.h b/compiler/dex/quick/mips/mips_dex_file_method_inliner.h new file mode 100644 index 00000000000..8fe7ec771b1 --- /dev/null +++ b/compiler/dex/quick/mips/mips_dex_file_method_inliner.h @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ART_COMPILER_DEX_QUICK_MIPS_MIPS_DEX_FILE_METHOD_INLINER_H_ +#define ART_COMPILER_DEX_QUICK_MIPS_MIPS_DEX_FILE_METHOD_INLINER_H_ + +#include "dex/quick/dex_file_method_inliner.h" + +namespace art { + +class MipsDexFileMethodInliner : public DexFileMethodInliner { + public: + MipsDexFileMethodInliner(); + ~MipsDexFileMethodInliner(); + + void FindIntrinsics(const DexFile* dex_file); + + private: + static const IntrinsicDef kIntrinsicMethods[]; +}; + +} // namespace art + +#endif // ART_COMPILER_DEX_QUICK_MIPS_MIPS_DEX_FILE_METHOD_INLINER_H_ diff --git a/compiler/dex/quick/mir_to_lir.h b/compiler/dex/quick/mir_to_lir.h index 4c56b74dc4a..58a77c7a0a0 100644 --- a/compiler/dex/quick/mir_to_lir.h +++ b/compiler/dex/quick/mir_to_lir.h @@ -106,6 +106,7 @@ struct MIR; struct LIR; struct RegLocation; struct RegisterInfo; +class DexFileMethodInliner; class MIRGraph; class Mir2Lir; @@ -555,7 +556,6 @@ class Mir2Lir : public Backend { bool GenInlinedUnsafeGet(CallInfo* info, bool is_long, bool is_volatile); bool GenInlinedUnsafePut(CallInfo* info, bool is_long, bool is_object, bool is_volatile, bool is_ordered); - bool GenIntrinsic(CallInfo* info); int LoadArgRegs(CallInfo* info, int call_state, NextCallInsn next_call_insn, const MethodReference& target_method, @@ -837,6 +837,8 @@ class Mir2Lir : public Backend { unsigned int fp_spill_mask_; LIR* first_lir_insn_; LIR* last_lir_insn_; + // Lazily retrieved method inliner for intrinsics. + const DexFileMethodInliner* inliner_; }; // Class Mir2Lir } // namespace art diff --git a/compiler/dex/quick/x86/x86_dex_file_method_inliner.cc b/compiler/dex/quick/x86/x86_dex_file_method_inliner.cc new file mode 100644 index 00000000000..e604106161f --- /dev/null +++ b/compiler/dex/quick/x86/x86_dex_file_method_inliner.cc @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2013 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 "base/logging.h" +#include "base/macros.h" +#include "dex/compiler_enums.h" + +#include "x86_dex_file_method_inliner.h" + +namespace art { + +const DexFileMethodInliner::IntrinsicDef X86DexFileMethodInliner::kIntrinsicMethods[] = { +#define INTRINSIC(c, n, p, o, d) \ + { { kClassCache ## c, kNameCache ## n, kProtoCache ## p }, { o, d } } + + INTRINSIC(JavaLangDouble, DoubleToRawLongBits, D_J, kIntrinsicDoubleCvt, 0), + INTRINSIC(JavaLangDouble, LongBitsToDouble, J_D, kIntrinsicDoubleCvt, 0), + INTRINSIC(JavaLangFloat, FloatToRawIntBits, F_I, kIntrinsicFloatCvt, 0), + INTRINSIC(JavaLangFloat, IntBitsToFloat, I_F, kIntrinsicFloatCvt, 0), + + INTRINSIC(JavaLangInteger, ReverseBytes, I_I, kIntrinsicReverseBytes, kWord), + INTRINSIC(JavaLangLong, ReverseBytes, J_J, kIntrinsicReverseBytes, kLong), + INTRINSIC(JavaLangShort, ReverseBytes, S_S, kIntrinsicReverseBytes, kSignedHalf), + + INTRINSIC(JavaLangMath, Abs, I_I, kIntrinsicAbsInt, 0), + INTRINSIC(JavaLangStrictMath, Abs, I_I, kIntrinsicAbsInt, 0), + INTRINSIC(JavaLangMath, Abs, J_J, kIntrinsicAbsLong, 0), + INTRINSIC(JavaLangStrictMath, Abs, J_J, kIntrinsicAbsLong, 0), + INTRINSIC(JavaLangMath, Min, II_I, kIntrinsicMinMaxInt, kIntrinsicFlagMin), + INTRINSIC(JavaLangStrictMath, Min, II_I, kIntrinsicMinMaxInt, kIntrinsicFlagMin), + INTRINSIC(JavaLangMath, Max, II_I, kIntrinsicMinMaxInt, kIntrinsicFlagMax), + INTRINSIC(JavaLangStrictMath, Max, II_I, kIntrinsicMinMaxInt, kIntrinsicFlagMax), + //INTRINSIC(JavaLangMath, Sqrt, D_D, kIntrinsicSqrt, 0), + //INTRINSIC(JavaLangStrictMath, Sqrt, D_D, kIntrinsicSqrt, 0), + + INTRINSIC(JavaLangString, CharAt, I_C, kIntrinsicCharAt, 0), + INTRINSIC(JavaLangString, CompareTo, String_I, kIntrinsicCompareTo, 0), + INTRINSIC(JavaLangString, IsEmpty, _Z, kIntrinsicIsEmptyOrLength, kIntrinsicFlagIsEmpty), + INTRINSIC(JavaLangString, IndexOf, II_I, kIntrinsicIndexOf, kIntrinsicFlagNone), + INTRINSIC(JavaLangString, IndexOf, I_I, kIntrinsicIndexOf, kIntrinsicFlagBase0), + INTRINSIC(JavaLangString, Length, _I, kIntrinsicIsEmptyOrLength, kIntrinsicFlagLength), + + INTRINSIC(JavaLangThread, CurrentThread, _Thread, kIntrinsicCurrentThread, 0), + + INTRINSIC(LibcoreIoMemory, PeekByte, J_B, kIntrinsicPeek, kSignedByte), + INTRINSIC(LibcoreIoMemory, PeekIntNative, J_I, kIntrinsicPeek, kWord), + INTRINSIC(LibcoreIoMemory, PeekLongNative, J_J, kIntrinsicPeek, kLong), + INTRINSIC(LibcoreIoMemory, PeekShortNative, J_S, kIntrinsicPeek, kSignedHalf), + INTRINSIC(LibcoreIoMemory, PokeByte, JB_V, kIntrinsicPoke, kSignedByte), + INTRINSIC(LibcoreIoMemory, PokeIntNative, JI_V, kIntrinsicPoke, kWord), + INTRINSIC(LibcoreIoMemory, PokeLongNative, JJ_V, kIntrinsicPoke, kLong), + INTRINSIC(LibcoreIoMemory, PokeShortNative, JS_V, kIntrinsicPoke, kSignedHalf), + + //INTRINSIC(SunMiscUnsafe, CompareAndSwapInt, ObjectJII_Z, kIntrinsicCas32, + // kIntrinsicFlagDontNeedWriteBarrier), + //INTRINSIC(SunMiscUnsafe, CompareAndSwapObject, ObjectJObjectObject_Z, kIntrinsicCas32, + // kIntrinsicFlagNeedWriteBarrier), + +#define UNSAFE_GET_PUT(type, code, type_flags) \ + INTRINSIC(SunMiscUnsafe, Get ## type, ObjectJ_ ## code, kIntrinsicUnsafeGet, \ + type_flags & ~kIntrinsicFlagIsObject), \ + INTRINSIC(SunMiscUnsafe, Get ## type ## Volatile, ObjectJ_ ## code, kIntrinsicUnsafeGet, \ + (type_flags | kIntrinsicFlagIsVolatile) & ~kIntrinsicFlagIsObject), \ + INTRINSIC(SunMiscUnsafe, Put ## type, ObjectJ ## code ## _V, kIntrinsicUnsafePut, \ + type_flags), \ + INTRINSIC(SunMiscUnsafe, Put ## type ## Volatile, ObjectJ ## code ## _V, kIntrinsicUnsafePut, \ + type_flags | kIntrinsicFlagIsVolatile), \ + INTRINSIC(SunMiscUnsafe, PutOrdered ## type, ObjectJ ## code ## _V, kIntrinsicUnsafePut, \ + type_flags | kIntrinsicFlagIsOrdered) + + UNSAFE_GET_PUT(Int, I, kIntrinsicFlagNone), + UNSAFE_GET_PUT(Long, J, kIntrinsicFlagIsLong), + + //UNSAFE_GET_PUT(Object, Object, kIntrinsicFlagIsObject), + // PutObject: "TODO: fix X86, it exhausts registers for card marking." + INTRINSIC(SunMiscUnsafe, GetObject, ObjectJ_Object, kIntrinsicUnsafeGet, + kIntrinsicFlagNone), + INTRINSIC(SunMiscUnsafe, GetObjectVolatile, ObjectJ_Object, kIntrinsicUnsafeGet, + kIntrinsicFlagIsVolatile), +#undef UNSAFE_GET_PUT + +#undef INTRINSIC +}; + +X86DexFileMethodInliner::X86DexFileMethodInliner() { +} + +X86DexFileMethodInliner::~X86DexFileMethodInliner() { +} + +void X86DexFileMethodInliner::FindIntrinsics(const DexFile* dex_file) { + IndexCache cache; + DoFindIntrinsics(dex_file, &cache, kIntrinsicMethods, arraysize(kIntrinsicMethods)); +} + +} // namespace art diff --git a/compiler/dex/quick/x86/x86_dex_file_method_inliner.h b/compiler/dex/quick/x86/x86_dex_file_method_inliner.h new file mode 100644 index 00000000000..7813e444fe5 --- /dev/null +++ b/compiler/dex/quick/x86/x86_dex_file_method_inliner.h @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ART_COMPILER_DEX_QUICK_X86_X86_DEX_FILE_METHOD_INLINER_H_ +#define ART_COMPILER_DEX_QUICK_X86_X86_DEX_FILE_METHOD_INLINER_H_ + +#include "dex/quick/dex_file_method_inliner.h" + +namespace art { + +class X86DexFileMethodInliner : public DexFileMethodInliner { + public: + X86DexFileMethodInliner(); + ~X86DexFileMethodInliner(); + + void FindIntrinsics(const DexFile* dex_file); + + private: + static const IntrinsicDef kIntrinsicMethods[]; +}; + +} // namespace art + +#endif // ART_COMPILER_DEX_QUICK_X86_X86_DEX_FILE_METHOD_INLINER_H_ diff --git a/runtime/dex_file.cc b/runtime/dex_file.cc index 4f332929292..0ddcdf30c93 100644 --- a/runtime/dex_file.cc +++ b/runtime/dex_file.cc @@ -510,7 +510,8 @@ const DexFile::TypeId* DexFile::FindTypeId(uint32_t string_idx) const { } const DexFile::ProtoId* DexFile::FindProtoId(uint16_t return_type_idx, - const std::vector& signature_type_idxs) const { + const uint16_t* signature_type_idxs, + uint32_t signature_length) const { int32_t lo = 0; int32_t hi = NumProtoIds() - 1; while (hi >= lo) { @@ -520,7 +521,7 @@ const DexFile::ProtoId* DexFile::FindProtoId(uint16_t return_type_idx, if (compare == 0) { DexFileParameterIterator it(*this, proto); size_t i = 0; - while (it.HasNext() && i < signature_type_idxs.size() && compare == 0) { + while (it.HasNext() && i < signature_length && compare == 0) { compare = signature_type_idxs[i] - it.GetTypeIdx(); it.Next(); i++; @@ -528,7 +529,7 @@ const DexFile::ProtoId* DexFile::FindProtoId(uint16_t return_type_idx, if (compare == 0) { if (it.HasNext()) { compare = -1; - } else if (i < signature_type_idxs.size()) { + } else if (i < signature_length) { compare = 1; } } diff --git a/runtime/dex_file.h b/runtime/dex_file.h index 51ab8d81a88..84026a46e67 100644 --- a/runtime/dex_file.h +++ b/runtime/dex_file.h @@ -661,7 +661,11 @@ class DexFile { // Looks up a proto id for a given return type and signature type list const ProtoId* FindProtoId(uint16_t return_type_idx, - const std::vector& signature_type_idxs_) const; + const uint16_t* signature_type_idxs, uint32_t signature_length) const; + const ProtoId* FindProtoId(uint16_t return_type_idx, + const std::vector& signature_type_idxs) const { + return FindProtoId(return_type_idx, &signature_type_idxs[0], signature_type_idxs.size()); + } // Given a signature place the type ids into the given vector, returns true on success bool CreateTypeList(const StringPiece& signature, uint16_t* return_type_idx, From 332b7aa6220124dc638b9f7e59611c376473f128 Mon Sep 17 00:00:00 2001 From: Vladimir Marko Date: Mon, 18 Nov 2013 12:01:54 +0000 Subject: [PATCH 0191/2402] Improve Thumb2 instructions' use of constant operands. Rename instructions using modified immediate to use suffix I8M. Many were using I8 which may lead to confusion with Thumb I8 instructions and some were using other suffixes. Add and use CmnRI8M, increase constant range of AddRRI12 and SubRRI12 and use BicRRI8M for applicable kOpAnd constants. In particular, this should marginaly improve Math.abs(float) and Math.abs(double) by converting x & 0x7fffffff to BIC. Bug: 11579369 Change-Id: I0f17a9eb80752d2625730a60555152cdffed50ba --- compiler/dex/quick/arm/arm_lir.h | 31 +++++++-------- compiler/dex/quick/arm/assemble_arm.cc | 33 +++++++++------- compiler/dex/quick/arm/fp_arm.cc | 2 +- compiler/dex/quick/arm/int_arm.cc | 12 +++--- compiler/dex/quick/arm/utility_arm.cc | 53 ++++++++++++++++---------- 5 files changed, 74 insertions(+), 57 deletions(-) diff --git a/compiler/dex/quick/arm/arm_lir.h b/compiler/dex/quick/arm/arm_lir.h index ffaaf84503e..8cd7c9440f5 100644 --- a/compiler/dex/quick/arm/arm_lir.h +++ b/compiler/dex/quick/arm/arm_lir.h @@ -334,7 +334,7 @@ enum ArmOpcode { kThumb2VcvtDF, // vcvt.F32.F64 vd, vm [1110111010110111] vd[15..12] [10111100] vm[3..0]. kThumb2Vsqrts, // vsqrt.f32 vd, vm [1110111010110001] vd[15..12] [10101100] vm[3..0]. kThumb2Vsqrtd, // vsqrt.f64 vd, vm [1110111010110001] vd[15..12] [10111100] vm[3..0]. - kThumb2MovImmShift, // mov(T2) rd, # [11110] i [00001001111] imm3 rd[11..8] imm8. + kThumb2MovI8M, // mov(T2) rd, # [11110] i [00001001111] imm3 rd[11..8] imm8. kThumb2MovImm16, // mov(T3) rd, # [11110] i [0010100] imm4 [0] imm3 rd[11..8] imm8. kThumb2StrRRI12, // str(Imm,T3) rd,[rn,#imm12] [111110001100] rn[19..16] rt[15..12] imm12[11..0]. kThumb2LdrRRI12, // str(Imm,T3) rd,[rn,#imm12] [111110001100] rn[19..16] rt[15..12] imm12[11..0]. @@ -346,14 +346,14 @@ enum ArmOpcode { kThumb2MovRR, // mov rd, rm [11101010010011110000] rd[11..8] [0000] rm[3..0]. kThumb2Vmovs, // vmov.f32 vd, vm [111011101] D [110000] vd[15..12] 101001] M [0] vm[3..0]. kThumb2Vmovd, // vmov.f64 vd, vm [111011101] D [110000] vd[15..12] 101101] M [0] vm[3..0]. - kThumb2Ldmia, // ldmia [111010001001[ rn[19..16] mask[15..0]. - kThumb2Stmia, // stmia [111010001000[ rn[19..16] mask[15..0]. + kThumb2Ldmia, // ldmia [111010001001] rn[19..16] mask[15..0]. + kThumb2Stmia, // stmia [111010001000] rn[19..16] mask[15..0]. kThumb2AddRRR, // add [111010110000] rn[19..16] [0000] rd[11..8] [0000] rm[3..0]. kThumb2SubRRR, // sub [111010111010] rn[19..16] [0000] rd[11..8] [0000] rm[3..0]. kThumb2SbcRRR, // sbc [111010110110] rn[19..16] [0000] rd[11..8] [0000] rm[3..0]. kThumb2CmpRR, // cmp [111010111011] rn[19..16] [0000] [1111] [0000] rm[3..0]. - kThumb2SubRRI12, // sub rd, rn, #imm12 [11110] i [01010] rn[19..16] [0] imm3[14..12] rd[11..8] imm8[7..0]. - kThumb2MvnImm12, // mov(T2) rd, # [11110] i [00011011110] imm3 rd[11..8] imm8. + kThumb2SubRRI12, // sub rd, rn, #imm12 [11110] i [101010] rn[19..16] [0] imm3[14..12] rd[11..8] imm8[7..0]. + kThumb2MvnI8M, // mov(T2) rd, # [11110] i [00011011110] imm3 rd[11..8] imm8. kThumb2Sel, // sel rd, rn, rm [111110101010] rn[19-16] rd[11-8] rm[3-0]. kThumb2Ubfx, // ubfx rd,rn,#lsb,#width [111100111100] rn[19..16] [0] imm3[14-12] rd[11-8] w[4-0]. kThumb2Sbfx, // ubfx rd,rn,#lsb,#width [111100110100] rn[19..16] [0] imm3[14-12] rd[11-8] w[4-0]. @@ -373,7 +373,8 @@ enum ArmOpcode { kThumb2StrbRRI12, // strb rt,[rn,#imm12] [111110001000] rt[15..12] rn[19..16] imm12[11..0]. kThumb2Pop, // pop [1110100010111101] list[15-0]*/ kThumb2Push, // push [1110100100101101] list[15-0]*/ - kThumb2CmpRI12, // cmp rn, # [11110] i [011011] rn[19-16] [0] imm3 [1111] imm8[7..0]. + kThumb2CmpRI8M, // cmp rn, # [11110] i [011011] rn[19-16] [0] imm3 [1111] imm8[7..0]. + kThumb2CmnRI8M, // cmn rn, # [11110] i [010001] rn[19-16] [0] imm3 [1111] imm8[7..0]. kThumb2AdcRRR, // adc [111010110101] rn[19..16] [0000] rd[11..8] [0000] rm[3..0]. kThumb2AndRRR, // and [111010100000] rn[19..16] [0000] rd[11..8] [0000] rm[3..0]. kThumb2BicRRR, // bic [111010100010] rn[19..16] [0000] rd[11..8] [0000] rm[3..0]. @@ -383,7 +384,7 @@ enum ArmOpcode { kThumb2SdivRRR, // sdiv [111110111001] rn[19..16] [1111] rd[11..8] [1111] rm[3..0]. kThumb2UdivRRR, // udiv [111110111011] rn[19..16] [1111] rd[11..8] [1111] rm[3..0]. kThumb2MnvRR, // mvn [11101010011011110] rd[11-8] [0000] rm[3..0]. - kThumb2RsubRRI8, // rsub [111100011100] rn[19..16] [0000] rd[11..8] imm8[7..0]. + kThumb2RsubRRI8M, // rsb rd, rn, # [11110] i [011101] rn[19..16] [0] imm3[14..12] rd[11..8] imm8[7..0]. kThumb2NegRR, // actually rsub rd, rn, #0. kThumb2OrrRRR, // orr [111010100100] rn[19..16] [0000] rd[11..8] [0000] rm[3..0]. kThumb2TstRR, // tst [111010100001] rn[19..16] [0000] [1111] [0000] rm[3..0]. @@ -395,14 +396,14 @@ enum ArmOpcode { kThumb2LsrRRI5, // lsr [11101010010011110] imm[14.12] rd[11..8] [01] rm[3..0]. kThumb2AsrRRI5, // asr [11101010010011110] imm[14.12] rd[11..8] [10] rm[3..0]. kThumb2RorRRI5, // ror [11101010010011110] imm[14.12] rd[11..8] [11] rm[3..0]. - kThumb2BicRRI8, // bic [111100000010] rn[19..16] [0] imm3 rd[11..8] imm8. - kThumb2AndRRI8, // bic [111100000000] rn[19..16] [0] imm3 rd[11..8] imm8. - kThumb2OrrRRI8, // orr [111100000100] rn[19..16] [0] imm3 rd[11..8] imm8. - kThumb2EorRRI8, // eor [111100001000] rn[19..16] [0] imm3 rd[11..8] imm8. - kThumb2AddRRI8, // add [111100001000] rn[19..16] [0] imm3 rd[11..8] imm8. - kThumb2AdcRRI8, // adc [111100010101] rn[19..16] [0] imm3 rd[11..8] imm8. - kThumb2SubRRI8, // sub [111100011011] rn[19..16] [0] imm3 rd[11..8] imm8. - kThumb2SbcRRI8, // sbc [111100010111] rn[19..16] [0] imm3 rd[11..8] imm8. + kThumb2BicRRI8M, // bic rd, rn, # [11110] i [000010] rn[19..16] [0] imm3[14..12] rd[11..8] imm8[7..0]. + kThumb2AndRRI8M, // and rd, rn, # [11110] i [000000] rn[19..16] [0] imm3[14..12] rd[11..8] imm8[7..0]. + kThumb2OrrRRI8M, // orr rd, rn, # [11110] i [000100] rn[19..16] [0] imm3[14..12] rd[11..8] imm8[7..0]. + kThumb2EorRRI8M, // eor rd, rn, # [11110] i [001000] rn[19..16] [0] imm3[14..12] rd[11..8] imm8[7..0]. + kThumb2AddRRI8M, // add rd, rn, # [11110] i [010001] rn[19..16] [0] imm3[14..12] rd[11..8] imm8[7..0]. + kThumb2AdcRRI8M, // adc rd, rn, # [11110] i [010101] rn[19..16] [0] imm3[14..12] rd[11..8] imm8[7..0]. + kThumb2SubRRI8M, // sub rd, rn, # [11110] i [011011] rn[19..16] [0] imm3[14..12] rd[11..8] imm8[7..0]. + kThumb2SbcRRI8M, // sub rd, rn, # [11110] i [010111] rn[19..16] [0] imm3[14..12] rd[11..8] imm8[7..0]. kThumb2RevRR, // rev [111110101001] rm[19..16] [1111] rd[11..8] 1000 rm[3..0] kThumb2RevshRR, // rev [111110101001] rm[19..16] [1111] rd[11..8] 1011 rm[3..0] kThumb2It, // it [10111111] firstcond[7-4] mask[3-0]. diff --git a/compiler/dex/quick/arm/assemble_arm.cc b/compiler/dex/quick/arm/assemble_arm.cc index 3d0f263fad3..1c81a5aa0a0 100644 --- a/compiler/dex/quick/arm/assemble_arm.cc +++ b/compiler/dex/quick/arm/assemble_arm.cc @@ -489,7 +489,7 @@ const ArmEncodingMap ArmMir2Lir::EncodingMap[kArmLast] = { kFmtDfp, 22, 12, kFmtDfp, 5, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1, "vsqrt.f64 ", "!0S, !1S", 4, kFixupNone), - ENCODING_MAP(kThumb2MovImmShift, 0xf04f0000, /* no setflags encoding */ + ENCODING_MAP(kThumb2MovI8M, 0xf04f0000, /* no setflags encoding */ kFmtBitBlt, 11, 8, kFmtModImm, -1, -1, kFmtUnused, -1, -1, kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0, "mov", "!0C, #!1m", 4, kFixupNone), @@ -573,8 +573,8 @@ const ArmEncodingMap ArmMir2Lir::EncodingMap[kArmLast] = { kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1,/* Note: doesn't affect flags */ "sub", "!0C,!1C,#!2d", 4, kFixupNone), - ENCODING_MAP(kThumb2MvnImm12, 0xf06f0000, /* no setflags encoding */ - kFmtBitBlt, 11, 8, kFmtImm12, -1, -1, kFmtUnused, -1, -1, + ENCODING_MAP(kThumb2MvnI8M, 0xf06f0000, /* no setflags encoding */ + kFmtBitBlt, 11, 8, kFmtModImm, -1, -1, kFmtUnused, -1, -1, kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0, "mvn", "!0C, #!1n", 4, kFixupNone), ENCODING_MAP(kThumb2Sel, 0xfaa0f080, @@ -656,11 +656,16 @@ const ArmEncodingMap ArmMir2Lir::EncodingMap[kArmLast] = { kFmtUnused, -1, -1, IS_UNARY_OP | REG_DEF_SP | REG_USE_SP | REG_USE_LIST0 | IS_STORE | NEEDS_FIXUP, "push", "", 4, kFixupPushPop), - ENCODING_MAP(kThumb2CmpRI12, 0xf1b00f00, + ENCODING_MAP(kThumb2CmpRI8M, 0xf1b00f00, kFmtBitBlt, 19, 16, kFmtModImm, -1, -1, kFmtUnused, -1, -1, kFmtUnused, -1, -1, IS_BINARY_OP | REG_USE0 | SETS_CCODES, "cmp", "!0C, #!1m", 4, kFixupNone), + ENCODING_MAP(kThumb2CmnRI8M, 0xf1100f00, + kFmtBitBlt, 19, 16, kFmtModImm, -1, -1, kFmtUnused, -1, -1, + kFmtUnused, -1, -1, + IS_BINARY_OP | REG_USE0 | SETS_CCODES, + "cmn", "!0C, #!1m", 4, kFixupNone), ENCODING_MAP(kThumb2AdcRRR, 0xeb500000, /* setflags encoding */ kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0, kFmtShift, -1, -1, @@ -699,11 +704,11 @@ const ArmEncodingMap ArmMir2Lir::EncodingMap[kArmLast] = { kFmtBitBlt, 11, 8, kFmtBitBlt, 3, 0, kFmtShift, -1, -1, kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1, "mvn", "!0C, !1C, shift !2d", 4, kFixupNone), - ENCODING_MAP(kThumb2RsubRRI8, 0xf1d00000, + ENCODING_MAP(kThumb2RsubRRI8M, 0xf1d00000, kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtModImm, -1, -1, kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1 | SETS_CCODES, - "rsb", "!0C,!1C,#!2m", 4, kFixupNone), + "rsbs", "!0C,!1C,#!2m", 4, kFixupNone), ENCODING_MAP(kThumb2NegRR, 0xf1d00000, /* instance of rsub */ kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtUnused, -1, -1, kFmtUnused, -1, -1, @@ -750,38 +755,38 @@ const ArmEncodingMap ArmMir2Lir::EncodingMap[kArmLast] = { kFmtBitBlt, 11, 8, kFmtBitBlt, 3, 0, kFmtShift5, -1, -1, kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1, "ror", "!0C, !1C, #!2d", 4, kFixupNone), - ENCODING_MAP(kThumb2BicRRI8, 0xf0200000, + ENCODING_MAP(kThumb2BicRRI8M, 0xf0200000, kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtModImm, -1, -1, kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1, "bic", "!0C, !1C, #!2m", 4, kFixupNone), - ENCODING_MAP(kThumb2AndRRI8, 0xf0000000, + ENCODING_MAP(kThumb2AndRRI8M, 0xf0000000, kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtModImm, -1, -1, kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1, "and", "!0C, !1C, #!2m", 4, kFixupNone), - ENCODING_MAP(kThumb2OrrRRI8, 0xf0400000, + ENCODING_MAP(kThumb2OrrRRI8M, 0xf0400000, kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtModImm, -1, -1, kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1, "orr", "!0C, !1C, #!2m", 4, kFixupNone), - ENCODING_MAP(kThumb2EorRRI8, 0xf0800000, + ENCODING_MAP(kThumb2EorRRI8M, 0xf0800000, kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtModImm, -1, -1, kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1, "eor", "!0C, !1C, #!2m", 4, kFixupNone), - ENCODING_MAP(kThumb2AddRRI8, 0xf1100000, + ENCODING_MAP(kThumb2AddRRI8M, 0xf1100000, kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtModImm, -1, -1, kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1 | SETS_CCODES, "adds", "!0C, !1C, #!2m", 4, kFixupNone), - ENCODING_MAP(kThumb2AdcRRI8, 0xf1500000, + ENCODING_MAP(kThumb2AdcRRI8M, 0xf1500000, kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtModImm, -1, -1, kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1 | SETS_CCODES | USES_CCODES, "adcs", "!0C, !1C, #!2m", 4, kFixupNone), - ENCODING_MAP(kThumb2SubRRI8, 0xf1b00000, + ENCODING_MAP(kThumb2SubRRI8M, 0xf1b00000, kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtModImm, -1, -1, kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1 | SETS_CCODES, "subs", "!0C, !1C, #!2m", 4, kFixupNone), - ENCODING_MAP(kThumb2SbcRRI8, 0xf1700000, + ENCODING_MAP(kThumb2SbcRRI8M, 0xf1700000, kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtModImm, -1, -1, kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1 | SETS_CCODES | USES_CCODES, diff --git a/compiler/dex/quick/arm/fp_arm.cc b/compiler/dex/quick/arm/fp_arm.cc index 480e0218d54..1575ece532d 100644 --- a/compiler/dex/quick/arm/fp_arm.cc +++ b/compiler/dex/quick/arm/fp_arm.cc @@ -274,7 +274,7 @@ void ArmMir2Lir::GenCmpFP(Instruction::Code opcode, RegLocation rl_dest, NewLIR0(kThumb2Fmstat); OpIT((default_result == -1) ? kCondGt : kCondMi, ""); - NewLIR2(kThumb2MovImmShift, rl_result.low_reg, + NewLIR2(kThumb2MovI8M, rl_result.low_reg, ModifiedImmediate(-default_result)); // Must not alter ccodes GenBarrier(); diff --git a/compiler/dex/quick/arm/int_arm.cc b/compiler/dex/quick/arm/int_arm.cc index 42bf3d4d008..41d921385af 100644 --- a/compiler/dex/quick/arm/int_arm.cc +++ b/compiler/dex/quick/arm/int_arm.cc @@ -97,7 +97,7 @@ void ArmMir2Lir::GenCmpLong(RegLocation rl_dest, RegLocation rl_src1, LIR* branch3 = OpCondBranch(kCondEq, NULL); OpIT(kCondHi, "E"); - NewLIR2(kThumb2MovImmShift, t_reg, ModifiedImmediate(-1)); + NewLIR2(kThumb2MovI8M, t_reg, ModifiedImmediate(-1)); LoadConstant(t_reg, 1); GenBarrier(); @@ -321,7 +321,7 @@ LIR* ArmMir2Lir::OpCmpImmBranch(ConditionCode cond, int reg, int check_value, if (ARM_LOWREG(reg) && ((check_value & 0xff) == check_value)) { NewLIR2(kThumbCmpRI8, reg, check_value); } else if (mod_imm >= 0) { - NewLIR2(kThumb2CmpRI12, reg, mod_imm); + NewLIR2(kThumb2CmpRI8M, reg, mod_imm); } else { int t_reg = AllocTemp(); LoadConstant(t_reg, check_value); @@ -1124,8 +1124,8 @@ void ArmMir2Lir::GenArithImmOpLong(Instruction::Code opcode, switch (opcode) { case Instruction::ADD_LONG: case Instruction::ADD_LONG_2ADDR: - NewLIR3(kThumb2AddRRI8, rl_result.low_reg, rl_src1.low_reg, mod_imm_lo); - NewLIR3(kThumb2AdcRRI8, rl_result.high_reg, rl_src1.high_reg, mod_imm_hi); + NewLIR3(kThumb2AddRRI8M, rl_result.low_reg, rl_src1.low_reg, mod_imm_lo); + NewLIR3(kThumb2AdcRRI8M, rl_result.high_reg, rl_src1.high_reg, mod_imm_hi); break; case Instruction::OR_LONG: case Instruction::OR_LONG_2ADDR: @@ -1152,8 +1152,8 @@ void ArmMir2Lir::GenArithImmOpLong(Instruction::Code opcode, break; case Instruction::SUB_LONG_2ADDR: case Instruction::SUB_LONG: - NewLIR3(kThumb2SubRRI8, rl_result.low_reg, rl_src1.low_reg, mod_imm_lo); - NewLIR3(kThumb2SbcRRI8, rl_result.high_reg, rl_src1.high_reg, mod_imm_hi); + NewLIR3(kThumb2SubRRI8M, rl_result.low_reg, rl_src1.low_reg, mod_imm_lo); + NewLIR3(kThumb2SbcRRI8M, rl_result.high_reg, rl_src1.high_reg, mod_imm_hi); break; default: LOG(FATAL) << "Unexpected opcode " << opcode; diff --git a/compiler/dex/quick/arm/utility_arm.cc b/compiler/dex/quick/arm/utility_arm.cc index d631cf70478..4819bdfbe1d 100644 --- a/compiler/dex/quick/arm/utility_arm.cc +++ b/compiler/dex/quick/arm/utility_arm.cc @@ -184,12 +184,12 @@ LIR* ArmMir2Lir::LoadConstantNoClobber(int r_dest, int value) { /* Check Modified immediate special cases */ mod_imm = ModifiedImmediate(value); if (mod_imm >= 0) { - res = NewLIR2(kThumb2MovImmShift, r_dest, mod_imm); + res = NewLIR2(kThumb2MovI8M, r_dest, mod_imm); return res; } mod_imm = ModifiedImmediate(~value); if (mod_imm >= 0) { - res = NewLIR2(kThumb2MvnImm12, r_dest, mod_imm); + res = NewLIR2(kThumb2MvnI8M, r_dest, mod_imm); return res; } /* 16-bit immediate? */ @@ -446,7 +446,6 @@ LIR* ArmMir2Lir::OpRegRegImm(OpKind op, int r_dest, int r_src1, int value) { ArmOpcode alt_opcode = kThumbBkpt; bool all_low_regs = (ARM_LOWREG(r_dest) && ARM_LOWREG(r_src1)); int32_t mod_imm = ModifiedImmediate(value); - int32_t mod_imm_neg = ModifiedImmediate(-value); switch (op) { case kOpLsl: @@ -482,47 +481,55 @@ LIR* ArmMir2Lir::OpRegRegImm(OpKind op, int r_dest, int r_src1, int value) { else opcode = (neg) ? kThumbAddRRI3 : kThumbSubRRI3; return NewLIR3(opcode, r_dest, r_src1, abs_value); - } else if ((abs_value & 0xff) == abs_value) { + } else if ((abs_value & 0x3ff) == abs_value) { if (op == kOpAdd) opcode = (neg) ? kThumb2SubRRI12 : kThumb2AddRRI12; else opcode = (neg) ? kThumb2AddRRI12 : kThumb2SubRRI12; return NewLIR3(opcode, r_dest, r_src1, abs_value); } - if (mod_imm_neg >= 0) { - op = (op == kOpAdd) ? kOpSub : kOpAdd; - mod_imm = mod_imm_neg; + if (mod_imm < 0) { + mod_imm = ModifiedImmediate(-value); + if (mod_imm >= 0) { + op = (op == kOpAdd) ? kOpSub : kOpAdd; + } } if (op == kOpSub) { - opcode = kThumb2SubRRI8; + opcode = kThumb2SubRRI8M; alt_opcode = kThumb2SubRRR; } else { - opcode = kThumb2AddRRI8; + opcode = kThumb2AddRRI8M; alt_opcode = kThumb2AddRRR; } break; case kOpRsub: - opcode = kThumb2RsubRRI8; + opcode = kThumb2RsubRRI8M; alt_opcode = kThumb2RsubRRR; break; case kOpAdc: - opcode = kThumb2AdcRRI8; + opcode = kThumb2AdcRRI8M; alt_opcode = kThumb2AdcRRR; break; case kOpSbc: - opcode = kThumb2SbcRRI8; + opcode = kThumb2SbcRRI8M; alt_opcode = kThumb2SbcRRR; break; case kOpOr: - opcode = kThumb2OrrRRI8; + opcode = kThumb2OrrRRI8M; alt_opcode = kThumb2OrrRRR; break; case kOpAnd: - opcode = kThumb2AndRRI8; + if (mod_imm < 0) { + mod_imm = ModifiedImmediate(~value); + if (mod_imm >= 0) { + return NewLIR3(kThumb2BicRRI8M, r_dest, r_src1, mod_imm); + } + } + opcode = kThumb2AndRRI8M; alt_opcode = kThumb2AndRRR; break; case kOpXor: - opcode = kThumb2EorRRI8; + opcode = kThumb2EorRRI8M; alt_opcode = kThumb2EorRRR; break; case kOpMul: @@ -531,15 +538,19 @@ LIR* ArmMir2Lir::OpRegRegImm(OpKind op, int r_dest, int r_src1, int value) { alt_opcode = kThumb2MulRRR; break; case kOpCmp: { - int mod_imm = ModifiedImmediate(value); LIR* res; if (mod_imm >= 0) { - res = NewLIR2(kThumb2CmpRI12, r_src1, mod_imm); + res = NewLIR2(kThumb2CmpRI8M, r_src1, mod_imm); } else { - int r_tmp = AllocTemp(); - res = LoadConstant(r_tmp, value); - OpRegReg(kOpCmp, r_src1, r_tmp); - FreeTemp(r_tmp); + mod_imm = ModifiedImmediate(-value); + if (mod_imm >= 0) { + res = NewLIR2(kThumb2CmnRI8M, r_src1, mod_imm); + } else { + int r_tmp = AllocTemp(); + res = LoadConstant(r_tmp, value); + OpRegReg(kOpCmp, r_src1, r_tmp); + FreeTemp(r_tmp); + } } return res; } From 259b592d2f61aa8addd3f6d8aa04523773f1008a Mon Sep 17 00:00:00 2001 From: Vladimir Marko Date: Mon, 18 Nov 2013 17:32:21 +0000 Subject: [PATCH 0192/2402] Fix build: add required spaces after //. Change-Id: I332b3d514d02900ec9b485a46aa9e27c084f1bd6 --- .../mips/mips_dex_file_method_inliner.cc | 78 +++++++++---------- .../quick/x86/x86_dex_file_method_inliner.cc | 14 ++-- 2 files changed, 46 insertions(+), 46 deletions(-) diff --git a/compiler/dex/quick/mips/mips_dex_file_method_inliner.cc b/compiler/dex/quick/mips/mips_dex_file_method_inliner.cc index 99eda82d5aa..e5345ec1f59 100644 --- a/compiler/dex/quick/mips/mips_dex_file_method_inliner.cc +++ b/compiler/dex/quick/mips/mips_dex_file_method_inliner.cc @@ -26,48 +26,48 @@ const DexFileMethodInliner::IntrinsicDef MipsDexFileMethodInliner::kIntrinsicMet #define INTRINSIC(c, n, p, o, d) \ { { kClassCache ## c, kNameCache ## n, kProtoCache ## p }, { o, d } } - //INTRINSIC(JavaLangDouble, DoubleToRawLongBits, D_J, kIntrinsicDoubleCvt, 0), - //INTRINSIC(JavaLangDouble, LongBitsToDouble, J_D, kIntrinsicDoubleCvt, 0), - //INTRINSIC(JavaLangFloat, FloatToRawIntBits, F_I, kIntrinsicFloatCvt, 0), - //INTRINSIC(JavaLangFloat, IntBitsToFloat, I_F, kIntrinsicFloatCvt, 0), - - //INTRINSIC(JavaLangInteger, ReverseBytes, I_I, kIntrinsicReverseBytes, kWord), - //INTRINSIC(JavaLangLong, ReverseBytes, J_J, kIntrinsicReverseBytes, kLong), - //INTRINSIC(JavaLangShort, ReverseBytes, S_S, kIntrinsicReverseBytes, kSignedHalf), - - //INTRINSIC(JavaLangMath, Abs, I_I, kIntrinsicAbsInt, 0), - //INTRINSIC(JavaLangStrictMath, Abs, I_I, kIntrinsicAbsInt, 0), - //INTRINSIC(JavaLangMath, Abs, J_J, kIntrinsicAbsLong, 0), - //INTRINSIC(JavaLangStrictMath, Abs, J_J, kIntrinsicAbsLong, 0), - //INTRINSIC(JavaLangMath, Min, II_I, kIntrinsicMinMaxInt, kIntrinsicFlagMin), - //INTRINSIC(JavaLangStrictMath, Min, II_I, kIntrinsicMinMaxInt, kIntrinsicFlagMin), - //INTRINSIC(JavaLangMath, Max, II_I, kIntrinsicMinMaxInt, kIntrinsicFlagMax), - //INTRINSIC(JavaLangStrictMath, Max, II_I, kIntrinsicMinMaxInt, kIntrinsicFlagMax), - //INTRINSIC(JavaLangMath, Sqrt, D_D, kIntrinsicSqrt, 0), - //INTRINSIC(JavaLangStrictMath, Sqrt, D_D, kIntrinsicSqrt, 0), - - //INTRINSIC(JavaLangString, CharAt, I_C, kIntrinsicCharAt, 0), - //INTRINSIC(JavaLangString, CompareTo, String_I, kIntrinsicCompareTo, 0), - //INTRINSIC(JavaLangString, IsEmpty, _Z, kIntrinsicIsEmptyOrLength, kIntrinsicFlagIsEmpty), - //INTRINSIC(JavaLangString, IndexOf, II_I, kIntrinsicIndexOf, kIntrinsicFlagNone), - //INTRINSIC(JavaLangString, IndexOf, I_I, kIntrinsicIndexOf, kIntrinsicFlagBase0), - //INTRINSIC(JavaLangString, Length, _I, kIntrinsicIsEmptyOrLength, kIntrinsicFlagLength), + // INTRINSIC(JavaLangDouble, DoubleToRawLongBits, D_J, kIntrinsicDoubleCvt, 0), + // INTRINSIC(JavaLangDouble, LongBitsToDouble, J_D, kIntrinsicDoubleCvt, 0), + // INTRINSIC(JavaLangFloat, FloatToRawIntBits, F_I, kIntrinsicFloatCvt, 0), + // INTRINSIC(JavaLangFloat, IntBitsToFloat, I_F, kIntrinsicFloatCvt, 0), + + // INTRINSIC(JavaLangInteger, ReverseBytes, I_I, kIntrinsicReverseBytes, kWord), + // INTRINSIC(JavaLangLong, ReverseBytes, J_J, kIntrinsicReverseBytes, kLong), + // INTRINSIC(JavaLangShort, ReverseBytes, S_S, kIntrinsicReverseBytes, kSignedHalf), + + // INTRINSIC(JavaLangMath, Abs, I_I, kIntrinsicAbsInt, 0), + // INTRINSIC(JavaLangStrictMath, Abs, I_I, kIntrinsicAbsInt, 0), + // INTRINSIC(JavaLangMath, Abs, J_J, kIntrinsicAbsLong, 0), + // INTRINSIC(JavaLangStrictMath, Abs, J_J, kIntrinsicAbsLong, 0), + // INTRINSIC(JavaLangMath, Min, II_I, kIntrinsicMinMaxInt, kIntrinsicFlagMin), + // INTRINSIC(JavaLangStrictMath, Min, II_I, kIntrinsicMinMaxInt, kIntrinsicFlagMin), + // INTRINSIC(JavaLangMath, Max, II_I, kIntrinsicMinMaxInt, kIntrinsicFlagMax), + // INTRINSIC(JavaLangStrictMath, Max, II_I, kIntrinsicMinMaxInt, kIntrinsicFlagMax), + // INTRINSIC(JavaLangMath, Sqrt, D_D, kIntrinsicSqrt, 0), + // INTRINSIC(JavaLangStrictMath, Sqrt, D_D, kIntrinsicSqrt, 0), + + // INTRINSIC(JavaLangString, CharAt, I_C, kIntrinsicCharAt, 0), + // INTRINSIC(JavaLangString, CompareTo, String_I, kIntrinsicCompareTo, 0), + // INTRINSIC(JavaLangString, IsEmpty, _Z, kIntrinsicIsEmptyOrLength, kIntrinsicFlagIsEmpty), + // INTRINSIC(JavaLangString, IndexOf, II_I, kIntrinsicIndexOf, kIntrinsicFlagNone), + // INTRINSIC(JavaLangString, IndexOf, I_I, kIntrinsicIndexOf, kIntrinsicFlagBase0), + // INTRINSIC(JavaLangString, Length, _I, kIntrinsicIsEmptyOrLength, kIntrinsicFlagLength), INTRINSIC(JavaLangThread, CurrentThread, _Thread, kIntrinsicCurrentThread, 0), INTRINSIC(LibcoreIoMemory, PeekByte, J_B, kIntrinsicPeek, kSignedByte), - //INTRINSIC(LibcoreIoMemory, PeekIntNative, J_I, kIntrinsicPeek, kWord), - //INTRINSIC(LibcoreIoMemory, PeekLongNative, J_J, kIntrinsicPeek, kLong), - //INTRINSIC(LibcoreIoMemory, PeekShortNative, J_S, kIntrinsicPeek, kSignedHalf), + // INTRINSIC(LibcoreIoMemory, PeekIntNative, J_I, kIntrinsicPeek, kWord), + // INTRINSIC(LibcoreIoMemory, PeekLongNative, J_J, kIntrinsicPeek, kLong), + // INTRINSIC(LibcoreIoMemory, PeekShortNative, J_S, kIntrinsicPeek, kSignedHalf), INTRINSIC(LibcoreIoMemory, PokeByte, JB_V, kIntrinsicPoke, kSignedByte), - //INTRINSIC(LibcoreIoMemory, PokeIntNative, JI_V, kIntrinsicPoke, kWord), - //INTRINSIC(LibcoreIoMemory, PokeLongNative, JJ_V, kIntrinsicPoke, kLong), - //INTRINSIC(LibcoreIoMemory, PokeShortNative, JS_V, kIntrinsicPoke, kSignedHalf), + // INTRINSIC(LibcoreIoMemory, PokeIntNative, JI_V, kIntrinsicPoke, kWord), + // INTRINSIC(LibcoreIoMemory, PokeLongNative, JJ_V, kIntrinsicPoke, kLong), + // INTRINSIC(LibcoreIoMemory, PokeShortNative, JS_V, kIntrinsicPoke, kSignedHalf), - //INTRINSIC(SunMiscUnsafe, CompareAndSwapInt, ObjectJII_Z, kIntrinsicCas32, - // kIntrinsicFlagDontNeedWriteBarrier), - //INTRINSIC(SunMiscUnsafe, CompareAndSwapObject, ObjectJObjectObject_Z, kIntrinsicCas32, - // kIntrinsicFlagNeedWriteBarrier), + // INTRINSIC(SunMiscUnsafe, CompareAndSwapInt, ObjectJII_Z, kIntrinsicCas32, + // kIntrinsicFlagDontNeedWriteBarrier), + // INTRINSIC(SunMiscUnsafe, CompareAndSwapObject, ObjectJObjectObject_Z, kIntrinsicCas32, + // kIntrinsicFlagNeedWriteBarrier), #define UNSAFE_GET_PUT(type, code, type_flags) \ INTRINSIC(SunMiscUnsafe, Get ## type, ObjectJ_ ## code, kIntrinsicUnsafeGet, \ @@ -81,9 +81,9 @@ const DexFileMethodInliner::IntrinsicDef MipsDexFileMethodInliner::kIntrinsicMet INTRINSIC(SunMiscUnsafe, PutOrdered ## type, ObjectJ ## code ## _V, kIntrinsicUnsafePut, \ type_flags | kIntrinsicFlagIsOrdered) - //UNSAFE_GET_PUT(Int, I, kIntrinsicFlagNone), - //UNSAFE_GET_PUT(Long, J, kIntrinsicFlagIsLong), - //UNSAFE_GET_PUT(Object, Object, kIntrinsicFlagIsObject), + // UNSAFE_GET_PUT(Int, I, kIntrinsicFlagNone), + // UNSAFE_GET_PUT(Long, J, kIntrinsicFlagIsLong), + // UNSAFE_GET_PUT(Object, Object, kIntrinsicFlagIsObject), #undef UNSAFE_GET_PUT #undef INTRINSIC diff --git a/compiler/dex/quick/x86/x86_dex_file_method_inliner.cc b/compiler/dex/quick/x86/x86_dex_file_method_inliner.cc index e604106161f..87f881aa8b6 100644 --- a/compiler/dex/quick/x86/x86_dex_file_method_inliner.cc +++ b/compiler/dex/quick/x86/x86_dex_file_method_inliner.cc @@ -43,8 +43,8 @@ const DexFileMethodInliner::IntrinsicDef X86DexFileMethodInliner::kIntrinsicMeth INTRINSIC(JavaLangStrictMath, Min, II_I, kIntrinsicMinMaxInt, kIntrinsicFlagMin), INTRINSIC(JavaLangMath, Max, II_I, kIntrinsicMinMaxInt, kIntrinsicFlagMax), INTRINSIC(JavaLangStrictMath, Max, II_I, kIntrinsicMinMaxInt, kIntrinsicFlagMax), - //INTRINSIC(JavaLangMath, Sqrt, D_D, kIntrinsicSqrt, 0), - //INTRINSIC(JavaLangStrictMath, Sqrt, D_D, kIntrinsicSqrt, 0), + // INTRINSIC(JavaLangMath, Sqrt, D_D, kIntrinsicSqrt, 0), + // INTRINSIC(JavaLangStrictMath, Sqrt, D_D, kIntrinsicSqrt, 0), INTRINSIC(JavaLangString, CharAt, I_C, kIntrinsicCharAt, 0), INTRINSIC(JavaLangString, CompareTo, String_I, kIntrinsicCompareTo, 0), @@ -64,10 +64,10 @@ const DexFileMethodInliner::IntrinsicDef X86DexFileMethodInliner::kIntrinsicMeth INTRINSIC(LibcoreIoMemory, PokeLongNative, JJ_V, kIntrinsicPoke, kLong), INTRINSIC(LibcoreIoMemory, PokeShortNative, JS_V, kIntrinsicPoke, kSignedHalf), - //INTRINSIC(SunMiscUnsafe, CompareAndSwapInt, ObjectJII_Z, kIntrinsicCas32, - // kIntrinsicFlagDontNeedWriteBarrier), - //INTRINSIC(SunMiscUnsafe, CompareAndSwapObject, ObjectJObjectObject_Z, kIntrinsicCas32, - // kIntrinsicFlagNeedWriteBarrier), + // INTRINSIC(SunMiscUnsafe, CompareAndSwapInt, ObjectJII_Z, kIntrinsicCas32, + // kIntrinsicFlagDontNeedWriteBarrier), + // INTRINSIC(SunMiscUnsafe, CompareAndSwapObject, ObjectJObjectObject_Z, kIntrinsicCas32, + // kIntrinsicFlagNeedWriteBarrier), #define UNSAFE_GET_PUT(type, code, type_flags) \ INTRINSIC(SunMiscUnsafe, Get ## type, ObjectJ_ ## code, kIntrinsicUnsafeGet, \ @@ -84,7 +84,7 @@ const DexFileMethodInliner::IntrinsicDef X86DexFileMethodInliner::kIntrinsicMeth UNSAFE_GET_PUT(Int, I, kIntrinsicFlagNone), UNSAFE_GET_PUT(Long, J, kIntrinsicFlagIsLong), - //UNSAFE_GET_PUT(Object, Object, kIntrinsicFlagIsObject), + // UNSAFE_GET_PUT(Object, Object, kIntrinsicFlagIsObject), // PutObject: "TODO: fix X86, it exhausts registers for card marking." INTRINSIC(SunMiscUnsafe, GetObject, ObjectJ_Object, kIntrinsicUnsafeGet, kIntrinsicFlagNone), From e4a50ee34695a9d90cf03fbb1e8afd1e434f6ee1 Mon Sep 17 00:00:00 2001 From: Mathieu Chartier Date: Mon, 18 Nov 2013 09:49:33 -0800 Subject: [PATCH 0193/2402] Fix build. cpplint errors. Change-Id: I11141f6e19b56a3f53d6e52f3013f4d20f33f102 --- compiler/dex/frontend.cc | 3 +-- compiler/dex/frontend.h | 2 +- compiler/dex/quick/dex_file_method_inliner.cc | 3 +-- compiler/dex/quick/dex_file_to_method_inliner_map.cc | 3 +-- compiler/dex/quick/dex_file_to_method_inliner_map.h | 4 ++-- 5 files changed, 6 insertions(+), 9 deletions(-) diff --git a/compiler/dex/frontend.cc b/compiler/dex/frontend.cc index b6e062e413b..e53d63648b3 100644 --- a/compiler/dex/frontend.cc +++ b/compiler/dex/frontend.cc @@ -64,8 +64,7 @@ LLVMInfo::~LLVMInfo() { } QuickCompilerContext::QuickCompilerContext(CompilerDriver& compiler) - : inliner_map_(new DexFileToMethodInlinerMap(&compiler)) -{ + : inliner_map_(new DexFileToMethodInlinerMap(&compiler)) { } QuickCompilerContext::~QuickCompilerContext() { diff --git a/compiler/dex/frontend.h b/compiler/dex/frontend.h index b6e8577fdf1..4a863f5294c 100644 --- a/compiler/dex/frontend.h +++ b/compiler/dex/frontend.h @@ -115,7 +115,7 @@ class LLVMInfo { class QuickCompilerContext { public: - QuickCompilerContext(CompilerDriver& compiler); + explicit QuickCompilerContext(CompilerDriver& compiler); ~QuickCompilerContext(); DexFileToMethodInlinerMap* GetInlinerMap() { diff --git a/compiler/dex/quick/dex_file_method_inliner.cc b/compiler/dex/quick/dex_file_method_inliner.cc index c962e9df936..4d3e3da23df 100644 --- a/compiler/dex/quick/dex_file_method_inliner.cc +++ b/compiler/dex/quick/dex_file_method_inliner.cc @@ -157,8 +157,7 @@ DexFileMethodInliner::~DexFileMethodInliner() { } DexFileMethodInliner::DexFileMethodInliner() - : dex_file_(NULL) -{ + : dex_file_(NULL) { COMPILE_ASSERT(kClassCacheFirst == 0, kClassCacheFirst_not_0); COMPILE_ASSERT(arraysize(kClassCacheNames) == kClassCacheLast, bad_arraysize_kClassCacheNames); COMPILE_ASSERT(kNameCacheFirst == 0, kNameCacheFirst_not_0); diff --git a/compiler/dex/quick/dex_file_to_method_inliner_map.cc b/compiler/dex/quick/dex_file_to_method_inliner_map.cc index 38022d2142e..56a42bc3dbb 100644 --- a/compiler/dex/quick/dex_file_to_method_inliner_map.cc +++ b/compiler/dex/quick/dex_file_to_method_inliner_map.cc @@ -32,8 +32,7 @@ namespace art { DexFileToMethodInlinerMap::DexFileToMethodInlinerMap(const CompilerDriver* compiler) : compiler_(compiler), - mutex_("inline_helper_mutex") -{ + mutex_("inline_helper_mutex") { } DexFileToMethodInlinerMap::~DexFileToMethodInlinerMap() { diff --git a/compiler/dex/quick/dex_file_to_method_inliner_map.h b/compiler/dex/quick/dex_file_to_method_inliner_map.h index e0b67e5a4b9..77f2648ae66 100644 --- a/compiler/dex/quick/dex_file_to_method_inliner_map.h +++ b/compiler/dex/quick/dex_file_to_method_inliner_map.h @@ -37,7 +37,7 @@ class DexFile; */ class DexFileToMethodInlinerMap { public: - DexFileToMethodInlinerMap(const CompilerDriver* compiler); + explicit DexFileToMethodInlinerMap(const CompilerDriver* compiler); ~DexFileToMethodInlinerMap(); const DexFileMethodInliner& GetMethodInliner(const DexFile* dex_file) LOCKS_EXCLUDED(mutex_); @@ -50,4 +50,4 @@ class DexFileToMethodInlinerMap { } // namespace art -#endif // ART_COMPILER_DEX_QUICK_DEX_FILE_TO_METHOD_INLINER_MAP_H_ +#endif // ART_COMPILER_DEX_QUICK_DEX_FILE_TO_METHOD_INLINER_MAP_H_ From e5eedcb4a634246d1f912992853441f715d705cc Mon Sep 17 00:00:00 2001 From: Hiroshi Yamauchi Date: Mon, 18 Nov 2013 11:55:39 -0800 Subject: [PATCH 0194/2402] Fix cpplint errors. Change-Id: I21f7423ebe69a77d456b0d318de73448489d2df4 --- runtime/gc/allocator/rosalloc.h | 12 ++++++------ runtime/gc/space/dlmalloc_space.h | 1 - runtime/gc/space/malloc_space.h | 3 ++- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/runtime/gc/allocator/rosalloc.h b/runtime/gc/allocator/rosalloc.h index 5dda80c16eb..c81306ffd55 100644 --- a/runtime/gc/allocator/rosalloc.h +++ b/runtime/gc/allocator/rosalloc.h @@ -151,12 +151,12 @@ class RosAlloc { // class Run { public: - byte magic_num_; // The magic number used for debugging. - byte size_bracket_idx_; // The index of the size bracket of this run. - byte is_thread_local_; // True if this run is used as a thread-local run. - byte to_be_bulk_freed_; // Used within BulkFree() to flag a run that's involved with a bulk free. - uint32_t top_slot_idx_; // The top slot index when this run is in bump index mode. - uint32_t alloc_bit_map_[0]; // The bit map that allocates if each slot is in use. + byte magic_num_; // The magic number used for debugging. + byte size_bracket_idx_; // The index of the size bracket of this run. + byte is_thread_local_; // True if this run is used as a thread-local run. + byte to_be_bulk_freed_; // Used within BulkFree() to flag a run that's involved with a bulk free. + uint32_t top_slot_idx_; // The top slot index when this run is in bump index mode. + uint32_t alloc_bit_map_[0]; // The bit map that allocates if each slot is in use. // bulk_free_bit_map_[] : The bit map that is used for GC to // temporarily mark the slots to free without using a lock. After diff --git a/runtime/gc/space/dlmalloc_space.h b/runtime/gc/space/dlmalloc_space.h index 63b1c216ceb..1cfee7a9ebb 100644 --- a/runtime/gc/space/dlmalloc_space.h +++ b/runtime/gc/space/dlmalloc_space.h @@ -33,7 +33,6 @@ namespace space { // An alloc space is a space where objects may be allocated and garbage collected. class DlMallocSpace : public MallocSpace { public: - // Create a DlMallocSpace with the requested sizes. The requested // base address is not guaranteed to be granted, if it is required, // the caller should call Begin on the returned space to confirm the diff --git a/runtime/gc/space/malloc_space.h b/runtime/gc/space/malloc_space.h index bd21a4d557c..189f01cfabf 100644 --- a/runtime/gc/space/malloc_space.h +++ b/runtime/gc/space/malloc_space.h @@ -181,6 +181,7 @@ class MallocSpace : public ContinuousMemMapAllocSpace { friend class collector::MarkSweep; + private: DISALLOW_COPY_AND_ASSIGN(MallocSpace); }; @@ -188,4 +189,4 @@ class MallocSpace : public ContinuousMemMapAllocSpace { } // namespace gc } // namespace art -#endif // ART_RUNTIME_GC_SPACE_DLMALLOC_SPACE_H_ +#endif // ART_RUNTIME_GC_SPACE_MALLOC_SPACE_H_ From bb2f8042375e9e0f11e1629da58d5648ab1d45f5 Mon Sep 17 00:00:00 2001 From: Hiroshi Yamauchi Date: Mon, 18 Nov 2013 15:45:02 -0800 Subject: [PATCH 0195/2402] Remove a LOG(INFO) that's probably forgot to be removed. I see lots of log lines "Parsing features " in tests. I assume this isn't intended to be left there. Change-Id: Icfcb5421ed72e86160c24230b1418de19e230376 --- runtime/common_test.h | 1 - 1 file changed, 1 deletion(-) diff --git a/runtime/common_test.h b/runtime/common_test.h index d860b6c34a7..57cf71a50b7 100644 --- a/runtime/common_test.h +++ b/runtime/common_test.h @@ -237,7 +237,6 @@ static InstructionSetFeatures GuessInstructionFeatures() { // input 'str' is a comma separated list of feature names. Parse it and // return the InstructionSetFeatures object. static InstructionSetFeatures ParseFeatureList(std::string str) { - LOG(INFO) << "Parsing features " << str; InstructionSetFeatures result; typedef std::vector FeatureList; FeatureList features; From 4ce1f00cc74867188347e463f4a5ecb9fe55cde5 Mon Sep 17 00:00:00 2001 From: Hiroshi Yamauchi Date: Mon, 18 Nov 2013 14:49:09 -0800 Subject: [PATCH 0196/2402] Fix a per-process dumpsys meminfo crash. In the InvalidateAllocator() function, DlMallocSpace and RosAllocSpace both null out the underlying allocator pointer at the zygote fork time so that new allocations won't accidentally happen in the zygote space after a zygote fork. However, nulling out the only allocator pointer would cause crashes in non-allocation uses such as GetFootprint() in this particular crash. Fix this by creating a second pointer just for the allocation purpose that gets nulled upon a InvalidateAllocator() call while the original pointer keeps pointing to the allocator. Change-Id: Ie751d9380db39baace9e25712a3824eec9a9969a --- runtime/gc/space/dlmalloc_space-inl.h | 2 +- runtime/gc/space/dlmalloc_space.cc | 26 +++++++++----------------- runtime/gc/space/dlmalloc_space.h | 8 ++++++-- runtime/gc/space/rosalloc_space-inl.h | 5 +++-- runtime/gc/space/rosalloc_space.cc | 23 ++++++++--------------- runtime/gc/space/rosalloc_space.h | 8 ++++++-- 6 files changed, 33 insertions(+), 39 deletions(-) diff --git a/runtime/gc/space/dlmalloc_space-inl.h b/runtime/gc/space/dlmalloc_space-inl.h index fbbba1faf28..c14a4e1b3fd 100644 --- a/runtime/gc/space/dlmalloc_space-inl.h +++ b/runtime/gc/space/dlmalloc_space-inl.h @@ -40,7 +40,7 @@ inline mirror::Object* DlMallocSpace::AllocNonvirtual(Thread* self, size_t num_b inline mirror::Object* DlMallocSpace::AllocWithoutGrowthLocked(Thread* /*self*/, size_t num_bytes, size_t* bytes_allocated) { - mirror::Object* result = reinterpret_cast(mspace_malloc(mspace_, num_bytes)); + mirror::Object* result = reinterpret_cast(mspace_malloc(mspace_for_alloc_, num_bytes)); if (LIKELY(result != NULL)) { if (kDebugSpaces) { CHECK(Contains(result)) << "Allocation (" << reinterpret_cast(result) diff --git a/runtime/gc/space/dlmalloc_space.cc b/runtime/gc/space/dlmalloc_space.cc index fcac58895a2..b067bbc9cf7 100644 --- a/runtime/gc/space/dlmalloc_space.cc +++ b/runtime/gc/space/dlmalloc_space.cc @@ -111,7 +111,7 @@ class ValgrindDlMallocSpace : public DlMallocSpace { DlMallocSpace::DlMallocSpace(const std::string& name, MemMap* mem_map, void* mspace, byte* begin, byte* end, byte* limit, size_t growth_limit) : MallocSpace(name, mem_map, begin, end, limit, growth_limit), - total_bytes_freed_(0), total_objects_freed_(0), mspace_(mspace) { + total_bytes_freed_(0), total_objects_freed_(0), mspace_(mspace), mspace_for_alloc_(mspace) { CHECK(mspace != NULL); } @@ -334,25 +334,17 @@ void DlMallocSpace::SetFootprintLimit(size_t new_size) { } uint64_t DlMallocSpace::GetBytesAllocated() { - if (mspace_ != nullptr) { - MutexLock mu(Thread::Current(), lock_); - size_t bytes_allocated = 0; - mspace_inspect_all(mspace_, DlmallocBytesAllocatedCallback, &bytes_allocated); - return bytes_allocated; - } else { - return Size(); - } + MutexLock mu(Thread::Current(), lock_); + size_t bytes_allocated = 0; + mspace_inspect_all(mspace_, DlmallocBytesAllocatedCallback, &bytes_allocated); + return bytes_allocated; } uint64_t DlMallocSpace::GetObjectsAllocated() { - if (mspace_ != nullptr) { - MutexLock mu(Thread::Current(), lock_); - size_t objects_allocated = 0; - mspace_inspect_all(mspace_, DlmallocObjectsAllocatedCallback, &objects_allocated); - return objects_allocated; - } else { - return 0; - } + MutexLock mu(Thread::Current(), lock_); + size_t objects_allocated = 0; + mspace_inspect_all(mspace_, DlmallocObjectsAllocatedCallback, &objects_allocated); + return objects_allocated; } #ifndef NDEBUG diff --git a/runtime/gc/space/dlmalloc_space.h b/runtime/gc/space/dlmalloc_space.h index 1cfee7a9ebb..d18d4ad2a0e 100644 --- a/runtime/gc/space/dlmalloc_space.h +++ b/runtime/gc/space/dlmalloc_space.h @@ -97,7 +97,7 @@ class DlMallocSpace : public MallocSpace { mirror::Class* FindRecentFreedObject(const mirror::Object* obj); virtual void InvalidateAllocator() { - mspace_ = nullptr; + mspace_for_alloc_ = nullptr; } virtual bool IsDlMallocSpace() const { @@ -130,7 +130,11 @@ class DlMallocSpace : public MallocSpace { static const size_t kChunkOverhead = kWordSize; // Underlying malloc space - void* mspace_; + void* const mspace_; + + // A mspace pointer used for allocation. Equals to what mspace_ + // points to or nullptr after InvalidateAllocator() is called. + void* mspace_for_alloc_; friend class collector::MarkSweep; diff --git a/runtime/gc/space/rosalloc_space-inl.h b/runtime/gc/space/rosalloc_space-inl.h index 92c69afa463..0fe5dd7a95a 100644 --- a/runtime/gc/space/rosalloc_space-inl.h +++ b/runtime/gc/space/rosalloc_space-inl.h @@ -35,8 +35,9 @@ inline mirror::Object* RosAllocSpace::AllocNonvirtual(Thread* self, size_t num_b inline mirror::Object* RosAllocSpace::AllocWithoutGrowthLocked(Thread* self, size_t num_bytes, size_t* bytes_allocated) { size_t rosalloc_size = 0; - mirror::Object* result = reinterpret_cast(rosalloc_->Alloc(self, num_bytes, - &rosalloc_size)); + mirror::Object* result = reinterpret_cast( + rosalloc_for_alloc_->Alloc(self, num_bytes, + &rosalloc_size)); if (LIKELY(result != NULL)) { if (kDebugSpaces) { CHECK(Contains(result)) << "Allocation (" << reinterpret_cast(result) diff --git a/runtime/gc/space/rosalloc_space.cc b/runtime/gc/space/rosalloc_space.cc index 72692d6ca38..a3d2dfa20bc 100644 --- a/runtime/gc/space/rosalloc_space.cc +++ b/runtime/gc/space/rosalloc_space.cc @@ -38,7 +38,8 @@ static const bool kPrefetchDuringRosAllocFreeList = true; RosAllocSpace::RosAllocSpace(const std::string& name, MemMap* mem_map, art::gc::allocator::RosAlloc* rosalloc, byte* begin, byte* end, byte* limit, size_t growth_limit) - : MallocSpace(name, mem_map, begin, end, limit, growth_limit), rosalloc_(rosalloc) { + : MallocSpace(name, mem_map, begin, end, limit, growth_limit), rosalloc_(rosalloc), + rosalloc_for_alloc_(rosalloc) { CHECK(rosalloc != NULL); } @@ -252,23 +253,15 @@ void RosAllocSpace::SetFootprintLimit(size_t new_size) { } uint64_t RosAllocSpace::GetBytesAllocated() { - if (rosalloc_ != NULL) { - size_t bytes_allocated = 0; - InspectAllRosAlloc(art::gc::allocator::RosAlloc::BytesAllocatedCallback, &bytes_allocated); - return bytes_allocated; - } else { - return Size(); - } + size_t bytes_allocated = 0; + InspectAllRosAlloc(art::gc::allocator::RosAlloc::BytesAllocatedCallback, &bytes_allocated); + return bytes_allocated; } uint64_t RosAllocSpace::GetObjectsAllocated() { - if (rosalloc_ != NULL) { - size_t objects_allocated = 0; - InspectAllRosAlloc(art::gc::allocator::RosAlloc::ObjectsAllocatedCallback, &objects_allocated); - return objects_allocated; - } else { - return 0; - } + size_t objects_allocated = 0; + InspectAllRosAlloc(art::gc::allocator::RosAlloc::ObjectsAllocatedCallback, &objects_allocated); + return objects_allocated; } void RosAllocSpace::InspectAllRosAlloc(void (*callback)(void *start, void *end, size_t num_bytes, void* callback_arg), diff --git a/runtime/gc/space/rosalloc_space.h b/runtime/gc/space/rosalloc_space.h index 8191ffb7f95..6311580d146 100644 --- a/runtime/gc/space/rosalloc_space.h +++ b/runtime/gc/space/rosalloc_space.h @@ -97,7 +97,7 @@ class RosAllocSpace : public MallocSpace { mirror::Class* FindRecentFreedObject(const mirror::Object* obj); virtual void InvalidateAllocator() { - rosalloc_ = NULL; + rosalloc_for_alloc_ = NULL; } virtual bool IsRosAllocSpace() const { @@ -130,7 +130,11 @@ class RosAllocSpace : public MallocSpace { AtomicInteger total_objects_freed_atomic_; // Underlying rosalloc. - art::gc::allocator::RosAlloc* rosalloc_; + art::gc::allocator::RosAlloc* const rosalloc_; + + // A rosalloc pointer used for allocation. Equals to what rosalloc_ + // points to or nullptr after InvalidateAllocator() is called. + art::gc::allocator::RosAlloc* rosalloc_for_alloc_; friend class collector::MarkSweep; From 51db44a194bafc3810a41164a8b39614f10e79df Mon Sep 17 00:00:00 2001 From: Sebastien Hertz Date: Tue, 19 Nov 2013 10:00:29 +0100 Subject: [PATCH 0197/2402] Propagate 'this_object' for method unwind event. Propagates the 'this_object' to InstrumentationListener::MethodUnwind callback. Change-Id: I12561f1a611b8399b94e669f9b8a6eaaf1a58631 --- runtime/debugger.cc | 7 ++++--- runtime/instrumentation.cc | 2 +- runtime/instrumentation.h | 5 +++-- runtime/trace.cc | 3 ++- runtime/trace.h | 3 ++- 5 files changed, 12 insertions(+), 8 deletions(-) diff --git a/runtime/debugger.cc b/runtime/debugger.cc index f5377092618..a2de602a071 100644 --- a/runtime/debugger.cc +++ b/runtime/debugger.cc @@ -136,11 +136,12 @@ class DebugInstrumentationListener : public instrumentation::InstrumentationList Dbg::PostLocationEvent(method, dex_pc, this_object, Dbg::kMethodExit); } - virtual void MethodUnwind(Thread* thread, const mirror::ArtMethod* method, - uint32_t dex_pc) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + virtual void MethodUnwind(Thread* thread, mirror::Object* this_object, + const mirror::ArtMethod* method, uint32_t dex_pc) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { // We're not recorded to listen to this kind of event, so complain. LOG(ERROR) << "Unexpected method unwind event in debugger " << PrettyMethod(method) - << " " << dex_pc; + << " " << dex_pc; } virtual void DexPcMoved(Thread* thread, mirror::Object* this_object, diff --git a/runtime/instrumentation.cc b/runtime/instrumentation.cc index 0f4fa4e577d..2ff3c8dd78d 100644 --- a/runtime/instrumentation.cc +++ b/runtime/instrumentation.cc @@ -501,7 +501,7 @@ void Instrumentation::MethodUnwindEvent(Thread* thread, mirror::Object* this_obj uint32_t dex_pc) const { if (have_method_unwind_listeners_) { for (InstrumentationListener* listener : method_unwind_listeners_) { - listener->MethodUnwind(thread, method, dex_pc); + listener->MethodUnwind(thread, this_object, method, dex_pc); } } } diff --git a/runtime/instrumentation.h b/runtime/instrumentation.h index 25a4eec976a..6b290f674ff 100644 --- a/runtime/instrumentation.h +++ b/runtime/instrumentation.h @@ -68,8 +68,9 @@ struct InstrumentationListener { // Call-back for when a method is popped due to an exception throw. A method will either cause a // MethodExited call-back or a MethodUnwind call-back when its activation is removed. - virtual void MethodUnwind(Thread* thread, const mirror::ArtMethod* method, - uint32_t dex_pc) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) = 0; + virtual void MethodUnwind(Thread* thread, mirror::Object* this_object, + const mirror::ArtMethod* method, uint32_t dex_pc) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) = 0; // Call-back for when the dex pc moves in a method. virtual void DexPcMoved(Thread* thread, mirror::Object* this_object, diff --git a/runtime/trace.cc b/runtime/trace.cc index ec95a87146c..da2c80af882 100644 --- a/runtime/trace.cc +++ b/runtime/trace.cc @@ -570,7 +570,8 @@ void Trace::MethodExited(Thread* thread, mirror::Object* this_object, thread_clock_diff, wall_clock_diff); } -void Trace::MethodUnwind(Thread* thread, const mirror::ArtMethod* method, uint32_t dex_pc) { +void Trace::MethodUnwind(Thread* thread, mirror::Object* this_object, + const mirror::ArtMethod* method, uint32_t dex_pc) { uint32_t thread_clock_diff = 0; uint32_t wall_clock_diff = 0; ReadClocks(thread, &thread_clock_diff, &wall_clock_diff); diff --git a/runtime/trace.h b/runtime/trace.h index ffcb36d2941..9be015a13d5 100644 --- a/runtime/trace.h +++ b/runtime/trace.h @@ -79,7 +79,8 @@ class Trace : public instrumentation::InstrumentationListener { const mirror::ArtMethod* method, uint32_t dex_pc, const JValue& return_value) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - virtual void MethodUnwind(Thread* thread, const mirror::ArtMethod* method, uint32_t dex_pc) + virtual void MethodUnwind(Thread* thread, mirror::Object* this_object, + const mirror::ArtMethod* method, uint32_t dex_pc) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); virtual void DexPcMoved(Thread* thread, mirror::Object* this_object, const mirror::ArtMethod* method, uint32_t new_dex_pc) From ba9ece9c58de90b39c39b29dbdaee54b1654c066 Mon Sep 17 00:00:00 2001 From: Narayan Kamath Date: Tue, 19 Nov 2013 10:51:03 +0000 Subject: [PATCH 0198/2402] Fix inlining for String.indexOf / String.isEmpty. Before vs After: Equals URI 1544014.6 ============================== Equals URI 992997.6 ======================== bug: 11551453 Change-Id: I41045fda59b4c69a209d6b895dc7c2bdd1fc4e89 --- compiler/dex/quick/dex_file_method_inliner.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/dex/quick/dex_file_method_inliner.cc b/compiler/dex/quick/dex_file_method_inliner.cc index 4d3e3da23df..6a760fa2952 100644 --- a/compiler/dex/quick/dex_file_method_inliner.cc +++ b/compiler/dex/quick/dex_file_method_inliner.cc @@ -58,8 +58,8 @@ const char* DexFileMethodInliner::kNameCacheNames[] = { "sqrt", // kNameCacheSqrt "charAt", // kNameCacheCharAt "compareTo", // kNameCacheCompareTo - "is_empty", // kNameCacheIsEmpty - "index_of", // kNameCacheIndexOf + "isEmpty", // kNameCacheIsEmpty + "indexOf", // kNameCacheIndexOf "length", // kNameCacheLength "currentThread", // kNameCacheCurrentThread "peekByte", // kNameCachePeekByte From c255e9723c4ac6eff7778ade21296bb5f11ea7bf Mon Sep 17 00:00:00 2001 From: Vladimir Marko Date: Tue, 19 Nov 2013 11:21:24 +0000 Subject: [PATCH 0199/2402] Avoid unnecessary copy of dex_gc_map. Change-Id: I8a7209d92aeee853f6a4e9e9bb0e094c5acd5e05 --- runtime/verifier/method_verifier.cc | 52 +++++++++++------------------ runtime/verifier/method_verifier.h | 6 ++-- 2 files changed, 23 insertions(+), 35 deletions(-) diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc index 9cd8f738d77..c0e03706328 100644 --- a/runtime/verifier/method_verifier.cc +++ b/runtime/verifier/method_verifier.cc @@ -1029,26 +1029,6 @@ bool MethodVerifier::CheckVarArgRangeRegs(uint32_t vA, uint32_t vC) { return true; } -static const std::vector* CreateLengthPrefixedDexGcMap( - const std::vector& gc_map) { - std::vector* length_prefixed_gc_map = new std::vector; - length_prefixed_gc_map->reserve(gc_map.size() + 4); - length_prefixed_gc_map->push_back((gc_map.size() & 0xff000000) >> 24); - length_prefixed_gc_map->push_back((gc_map.size() & 0x00ff0000) >> 16); - length_prefixed_gc_map->push_back((gc_map.size() & 0x0000ff00) >> 8); - length_prefixed_gc_map->push_back((gc_map.size() & 0x000000ff) >> 0); - length_prefixed_gc_map->insert(length_prefixed_gc_map->end(), - gc_map.begin(), - gc_map.end()); - DCHECK_EQ(gc_map.size() + 4, length_prefixed_gc_map->size()); - DCHECK_EQ(gc_map.size(), - static_cast((length_prefixed_gc_map->at(0) << 24) | - (length_prefixed_gc_map->at(1) << 16) | - (length_prefixed_gc_map->at(2) << 8) | - (length_prefixed_gc_map->at(3) << 0))); - return length_prefixed_gc_map; -} - bool MethodVerifier::VerifyCodeFlow() { uint16_t registers_size = code_item_->registers_size_; uint32_t insns_size = code_item_->insns_size_in_code_units_; @@ -1088,16 +1068,15 @@ bool MethodVerifier::VerifyCodeFlow() { bool compile = IsCandidateForCompilation(ref, method_access_flags_); if (compile) { /* Generate a register map and add it to the method. */ - UniquePtr > map(GenerateGcMap()); - if (map.get() == NULL) { + const std::vector* dex_gc_map = GenerateLengthPrefixedGcMap(); + if (dex_gc_map == NULL) { DCHECK_NE(failures_.size(), 0U); return false; // Not a real failure, but a failure to encode } if (kIsDebugBuild) { - VerifyGcMap(*map); + VerifyLengthPrefixedGcMap(*dex_gc_map); } - const std::vector* dex_gc_map = CreateLengthPrefixedDexGcMap(*(map.get())); - verifier::MethodVerifier::SetDexGcMap(ref, *dex_gc_map); + verifier::MethodVerifier::SetDexGcMap(ref, dex_gc_map); } if (has_check_casts_) { @@ -4077,7 +4056,7 @@ MethodVerifier::PcToConcreteMethodMap* MethodVerifier::GenerateDevirtMap() { return pc_to_concrete_method_map.release(); } -const std::vector* MethodVerifier::GenerateGcMap() { +const std::vector* MethodVerifier::GenerateLengthPrefixedGcMap() { size_t num_entries, ref_bitmap_bits, pc_bits; ComputeGcMapSizes(&num_entries, &ref_bitmap_bits, &pc_bits); // There's a single byte to encode the size of each bitmap @@ -4115,7 +4094,12 @@ const std::vector* MethodVerifier::GenerateGcMap() { Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Failed to encode GC map (size=" << table_size << ")"; return NULL; } - table->reserve(table_size); + table->reserve(table_size + 4); // table_size plus the length prefix + // Write table size + table->push_back((table_size & 0xff000000) >> 24); + table->push_back((table_size & 0x00ff0000) >> 16); + table->push_back((table_size & 0x0000ff00) >> 8); + table->push_back((table_size & 0x000000ff) >> 0); // Write table header table->push_back(format | ((ref_bitmap_bytes >> DexPcToReferenceMap::kRegMapFormatShift) & ~DexPcToReferenceMap::kRegMapFormatMask)); @@ -4133,14 +4117,18 @@ const std::vector* MethodVerifier::GenerateGcMap() { line->WriteReferenceBitMap(*table, ref_bitmap_bytes); } } - DCHECK_EQ(table->size(), table_size); + DCHECK_EQ(table->size(), table_size + 4); // table_size plus the length prefix return table; } -void MethodVerifier::VerifyGcMap(const std::vector& data) { +void MethodVerifier::VerifyLengthPrefixedGcMap(const std::vector& data) { // Check that for every GC point there is a map entry, there aren't entries for non-GC points, // that the table data is well formed and all references are marked (or not) in the bitmap - DexPcToReferenceMap map(&data[0], data.size()); + DCHECK_GE(data.size(), 4u); + size_t table_size = data.size() - 4u; + DCHECK_EQ(table_size, static_cast((data[0] << 24) | (data[1] << 16) | + (data[2] << 8) | (data[3] << 0))); + DexPcToReferenceMap map(&data[4], table_size); size_t map_index = 0; for (size_t i = 0; i < code_item_->insns_size_in_code_units_; i++) { const uint8_t* reg_bitmap = map.FindBitMap(i, false); @@ -4166,7 +4154,7 @@ void MethodVerifier::VerifyGcMap(const std::vector& data) { } } -void MethodVerifier::SetDexGcMap(MethodReference ref, const std::vector& gc_map) { +void MethodVerifier::SetDexGcMap(MethodReference ref, const std::vector* gc_map) { DCHECK(Runtime::Current()->IsCompiler()); { WriterMutexLock mu(Thread::Current(), *dex_gc_maps_lock_); @@ -4175,7 +4163,7 @@ void MethodVerifier::SetDexGcMap(MethodReference ref, const std::vector delete it->second; dex_gc_maps_->erase(it); } - dex_gc_maps_->Put(ref, &gc_map); + dex_gc_maps_->Put(ref, gc_map); } DCHECK(GetDexGcMap(ref) != NULL); } diff --git a/runtime/verifier/method_verifier.h b/runtime/verifier/method_verifier.h index 8a663f9f0e4..f72898eee7a 100644 --- a/runtime/verifier/method_verifier.h +++ b/runtime/verifier/method_verifier.h @@ -614,10 +614,10 @@ class MethodVerifier { * encode it in some clever fashion. * Returns a pointer to a newly-allocated RegisterMap, or NULL on failure. */ - const std::vector* GenerateGcMap(); + const std::vector* GenerateLengthPrefixedGcMap(); // Verify that the GC map associated with method_ is well formed - void VerifyGcMap(const std::vector& data); + void VerifyLengthPrefixedGcMap(const std::vector& data); // Compute sizes for GC map data void ComputeGcMapSizes(size_t* gc_points, size_t* ref_bitmap_bits, size_t* log2_max_gc_pc); @@ -629,7 +629,7 @@ class MethodVerifier { MethodReferenceComparator> DexGcMapTable; static ReaderWriterMutex* dex_gc_maps_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER; static DexGcMapTable* dex_gc_maps_ GUARDED_BY(dex_gc_maps_lock_); - static void SetDexGcMap(MethodReference ref, const std::vector& dex_gc_map) + static void SetDexGcMap(MethodReference ref, const std::vector* dex_gc_map) LOCKS_EXCLUDED(dex_gc_maps_lock_); From 2247984899247b1402408d39731ff64048f0e274 Mon Sep 17 00:00:00 2001 From: Vladimir Marko Date: Tue, 19 Nov 2013 17:04:50 +0000 Subject: [PATCH 0200/2402] Clean up kOpCmp on ARM. kThumb2CmnRI8M is now used. Change-Id: I300299258ed99d86c300dee45c904c360dd44638 --- compiler/dex/quick/arm/int_arm.cc | 12 +----------- compiler/dex/quick/arm/utility_arm.cc | 7 ++----- disassembler/disassembler_arm.cc | 2 +- 3 files changed, 4 insertions(+), 17 deletions(-) diff --git a/compiler/dex/quick/arm/int_arm.cc b/compiler/dex/quick/arm/int_arm.cc index 41d921385af..9f84b0341f0 100644 --- a/compiler/dex/quick/arm/int_arm.cc +++ b/compiler/dex/quick/arm/int_arm.cc @@ -299,7 +299,6 @@ void ArmMir2Lir::GenFusedLongCmpBranch(BasicBlock* bb, MIR* mir) { LIR* ArmMir2Lir::OpCmpImmBranch(ConditionCode cond, int reg, int check_value, LIR* target) { LIR* branch; - int mod_imm; ArmConditionCode arm_cond = ArmConditionEncoding(cond); /* * A common use of OpCmpImmBranch is for null checks, and using the Thumb 16-bit @@ -317,16 +316,7 @@ LIR* ArmMir2Lir::OpCmpImmBranch(ConditionCode cond, int reg, int check_value, branch = NewLIR2((arm_cond == kArmCondEq) ? kThumb2Cbz : kThumb2Cbnz, reg, 0); } else { - mod_imm = ModifiedImmediate(check_value); - if (ARM_LOWREG(reg) && ((check_value & 0xff) == check_value)) { - NewLIR2(kThumbCmpRI8, reg, check_value); - } else if (mod_imm >= 0) { - NewLIR2(kThumb2CmpRI8M, reg, mod_imm); - } else { - int t_reg = AllocTemp(); - LoadConstant(t_reg, check_value); - OpRegReg(kOpCmp, reg, t_reg); - } + OpRegImm(kOpCmp, reg, check_value); branch = NewLIR2(kThumbBCond, 0, arm_cond); } branch->target = target; diff --git a/compiler/dex/quick/arm/utility_arm.cc b/compiler/dex/quick/arm/utility_arm.cc index 4819bdfbe1d..8a8b168ccc1 100644 --- a/compiler/dex/quick/arm/utility_arm.cc +++ b/compiler/dex/quick/arm/utility_arm.cc @@ -596,13 +596,10 @@ LIR* ArmMir2Lir::OpRegImm(OpKind op, int r_dest_src1, int value) { } break; case kOpCmp: - if (ARM_LOWREG(r_dest_src1) && short_form) { - opcode = (short_form) ? kThumbCmpRI8 : kThumbCmpRR; - } else if (ARM_LOWREG(r_dest_src1)) { - opcode = kThumbCmpRR; + if (!neg && short_form) { + opcode = kThumbCmpRI8; } else { short_form = false; - opcode = kThumbCmpHL; } break; default: diff --git a/disassembler/disassembler_arm.cc b/disassembler/disassembler_arm.cc index db51fabcc9a..936fb07728b 100644 --- a/disassembler/disassembler_arm.cc +++ b/disassembler/disassembler_arm.cc @@ -854,7 +854,7 @@ size_t DisassemblerArm::DumpThumb32(std::ostream& os, const uint8_t* instr_ptr) } else if (op3 == 0x4) { opcode << "teq"; } else if (op3 == 0x8) { - opcode << "cmw"; + opcode << "cmn.w"; } else { opcode << "cmp.w"; } From d31fb9718a6180304cd951619dc36be8e090a641 Mon Sep 17 00:00:00 2001 From: Hiroshi Yamauchi Date: Tue, 19 Nov 2013 11:08:27 -0800 Subject: [PATCH 0201/2402] Enable rosalloc by default. Bug: 9986565 Change-Id: I485341d30a21704ddbc45d8f531ef5593a358bb5 --- runtime/gc/heap.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/gc/heap.h b/runtime/gc/heap.h index 6e8890c4c65..3da3943aa7b 100644 --- a/runtime/gc/heap.h +++ b/runtime/gc/heap.h @@ -109,7 +109,7 @@ enum HeapVerificationMode { static constexpr HeapVerificationMode kDesiredHeapVerification = kNoHeapVerification; // If true, use rosalloc/RosAllocSpace instead of dlmalloc/DlMallocSpace -static constexpr bool kUseRosAlloc = false; +static constexpr bool kUseRosAlloc = true; class Heap { public: From 579b02416e05e32e535126e1ed61613a2cdb030e Mon Sep 17 00:00:00 2001 From: Jeff Hao Date: Mon, 18 Nov 2013 13:16:49 -0800 Subject: [PATCH 0202/2402] Add support for JDWP METHOD_EXIT_WITH_RETURN_VALUE events. Bug: 11569539 Change-Id: Ibc7a80df83470ffd726d73695a05f4938248f292 --- runtime/debugger.cc | 68 ++++++++++++++++++++++++-------------- runtime/debugger.h | 8 ++++- runtime/jdwp/jdwp.h | 6 +++- runtime/jdwp/jdwp_event.cc | 10 +++--- 4 files changed, 62 insertions(+), 30 deletions(-) diff --git a/runtime/debugger.cc b/runtime/debugger.cc index 3ef0a7fd811..af92b4b7e93 100644 --- a/runtime/debugger.cc +++ b/runtime/debugger.cc @@ -121,19 +121,18 @@ class DebugInstrumentationListener : public instrumentation::InstrumentationList // TODO: post location events is a suspension point and native method entry stubs aren't. return; } - Dbg::PostLocationEvent(method, 0, this_object, Dbg::kMethodEntry); + Dbg::PostLocationEvent(method, 0, this_object, Dbg::kMethodEntry, nullptr); } virtual void MethodExited(Thread* thread, mirror::Object* this_object, const mirror::ArtMethod* method, uint32_t dex_pc, const JValue& return_value) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - UNUSED(return_value); if (method->IsNative()) { // TODO: post location events is a suspension point and native method entry stubs aren't. return; } - Dbg::PostLocationEvent(method, dex_pc, this_object, Dbg::kMethodExit); + Dbg::PostLocationEvent(method, dex_pc, this_object, Dbg::kMethodExit, &return_value); } virtual void MethodUnwind(Thread* thread, const mirror::ArtMethod* method, @@ -1393,6 +1392,13 @@ void Dbg::OutputVariableTable(JDWP::RefTypeId, JDWP::MethodId method_id, bool wi JDWP::Set4BE(expandBufGetBuffer(pReply) + variable_count_offset, context.variable_count); } +void Dbg::OutputMethodReturnValue(JDWP::MethodId method_id, const JValue* return_value, + JDWP::ExpandBuf* pReply) { + mirror::ArtMethod* m = FromMethodId(method_id); + JDWP::JdwpTag tag = BasicTagFromDescriptor(MethodHelper(m).GetShorty()); + OutputJValue(tag, return_value, pReply); +} + JDWP::JdwpError Dbg::GetBytecodes(JDWP::RefTypeId, JDWP::MethodId method_id, std::vector& bytecodes) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { @@ -1461,25 +1467,18 @@ static JDWP::JdwpError GetFieldValueImpl(JDWP::RefTypeId ref_type_id, JDWP::Obje } JDWP::JdwpTag tag = BasicTagFromDescriptor(FieldHelper(f).GetTypeDescriptor()); - - if (IsPrimitiveTag(tag)) { - expandBufAdd1(pReply, tag); - if (tag == JDWP::JT_BOOLEAN || tag == JDWP::JT_BYTE) { - expandBufAdd1(pReply, f->Get32(o)); - } else if (tag == JDWP::JT_CHAR || tag == JDWP::JT_SHORT) { - expandBufAdd2BE(pReply, f->Get32(o)); - } else if (tag == JDWP::JT_FLOAT || tag == JDWP::JT_INT) { - expandBufAdd4BE(pReply, f->Get32(o)); - } else if (tag == JDWP::JT_DOUBLE || tag == JDWP::JT_LONG) { - expandBufAdd8BE(pReply, f->Get64(o)); - } else { - LOG(FATAL) << "Unknown tag: " << tag; - } + JValue field_value; + if (tag == JDWP::JT_VOID) { + LOG(FATAL) << "Unknown tag: " << tag; + } else if (!IsPrimitiveTag(tag)) { + field_value.SetL(f->GetObject(o)); + } else if (tag == JDWP::JT_DOUBLE || tag == JDWP::JT_LONG) { + field_value.SetJ(f->Get64(o)); } else { - mirror::Object* value = f->GetObject(o); - expandBufAdd1(pReply, TagFromObject(value)); - expandBufAddObjectId(pReply, gRegistry->Add(value)); + field_value.SetI(f->Get32(o)); } + Dbg::OutputJValue(tag, &field_value, pReply); + return JDWP::ERR_NONE; } @@ -1557,6 +1556,27 @@ std::string Dbg::StringToUtf8(JDWP::ObjectId string_id) { return s->ToModifiedUtf8(); } +void Dbg::OutputJValue(JDWP::JdwpTag tag, const JValue* return_value, JDWP::ExpandBuf* pReply) { + if (IsPrimitiveTag(tag)) { + expandBufAdd1(pReply, tag); + if (tag == JDWP::JT_BOOLEAN || tag == JDWP::JT_BYTE) { + expandBufAdd1(pReply, return_value->GetI()); + } else if (tag == JDWP::JT_CHAR || tag == JDWP::JT_SHORT) { + expandBufAdd2BE(pReply, return_value->GetI()); + } else if (tag == JDWP::JT_FLOAT || tag == JDWP::JT_INT) { + expandBufAdd4BE(pReply, return_value->GetI()); + } else if (tag == JDWP::JT_DOUBLE || tag == JDWP::JT_LONG) { + expandBufAdd8BE(pReply, return_value->GetJ()); + } else { + CHECK_EQ(tag, JDWP::JT_VOID); + } + } else { + mirror::Object* value = return_value->GetL(); + expandBufAdd1(pReply, TagFromObject(value)); + expandBufAddObjectId(pReply, gRegistry->Add(value)); + } +} + JDWP::JdwpError Dbg::GetThreadName(JDWP::ObjectId thread_id, std::string& name) { ScopedObjectAccessUnchecked soa(Thread::Current()); MutexLock mu(soa.Self(), *Locks::thread_list_lock_); @@ -2226,8 +2246,8 @@ void Dbg::SetLocalValue(JDWP::ObjectId thread_id, JDWP::FrameId frame_id, int sl visitor.WalkStack(); } -void Dbg::PostLocationEvent(const mirror::ArtMethod* m, int dex_pc, - mirror::Object* this_object, int event_flags) { +void Dbg::PostLocationEvent(const mirror::ArtMethod* m, int dex_pc, mirror::Object* this_object, + int event_flags, const JValue* return_value) { mirror::Class* c = m->GetDeclaringClass(); JDWP::JdwpLocation location; @@ -2242,7 +2262,7 @@ void Dbg::PostLocationEvent(const mirror::ArtMethod* m, int dex_pc, if (gRegistry->Contains(this_object)) { this_id = gRegistry->Add(this_object); } - gJdwpState->PostLocationEvent(&location, this_id, event_flags); + gJdwpState->PostLocationEvent(&location, this_id, event_flags, return_value); } void Dbg::PostException(Thread* thread, const ThrowLocation& throw_location, @@ -2356,7 +2376,7 @@ void Dbg::UpdateDebugger(Thread* thread, mirror::Object* this_object, // If there's something interesting going on, see if it matches one // of the debugger filters. if (event_flags != 0) { - Dbg::PostLocationEvent(m, dex_pc, this_object, event_flags); + Dbg::PostLocationEvent(m, dex_pc, this_object, event_flags, nullptr); } } diff --git a/runtime/debugger.h b/runtime/debugger.h index 8574a3308f2..d1936287bf4 100644 --- a/runtime/debugger.h +++ b/runtime/debugger.h @@ -230,6 +230,9 @@ class Dbg { static void OutputVariableTable(JDWP::RefTypeId ref_type_id, JDWP::MethodId id, bool with_generic, JDWP::ExpandBuf* pReply) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + static void OutputMethodReturnValue(JDWP::MethodId method_id, const JValue* return_value, + JDWP::ExpandBuf* pReply) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); static JDWP::JdwpError GetBytecodes(JDWP::RefTypeId class_id, JDWP::MethodId method_id, std::vector& bytecodes) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); @@ -254,6 +257,8 @@ class Dbg { static std::string StringToUtf8(JDWP::ObjectId string_id) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + static void OutputJValue(JDWP::JdwpTag tag, const JValue* return_value, JDWP::ExpandBuf* pReply) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); /* * Thread, ThreadGroup, Frame @@ -327,7 +332,8 @@ class Dbg { kMethodExit = 0x08, }; static void PostLocationEvent(const mirror::ArtMethod* method, int pcOffset, - mirror::Object* thisPtr, int eventFlags) + mirror::Object* thisPtr, int eventFlags, + const JValue* return_value) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); static void PostException(Thread* thread, const ThrowLocation& throw_location, mirror::ArtMethod* catch_method, diff --git a/runtime/jdwp/jdwp.h b/runtime/jdwp/jdwp.h index a1657d098bc..fd78bf2a62c 100644 --- a/runtime/jdwp/jdwp.h +++ b/runtime/jdwp/jdwp.h @@ -31,6 +31,7 @@ struct iovec; namespace art { + union JValue; namespace mirror { class ArtMethod; } // namespace mirror @@ -185,8 +186,11 @@ struct JdwpState { * issuing a MethodEntry on a native method. * * "eventFlags" indicates the types of events that have occurred. + * + * "returnValue" is non-null for MethodExit events only. */ - bool PostLocationEvent(const JdwpLocation* pLoc, ObjectId thisPtr, int eventFlags) + bool PostLocationEvent(const JdwpLocation* pLoc, ObjectId thisPtr, int eventFlags, + const JValue* returnValue) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); /* diff --git a/runtime/jdwp/jdwp_event.cc b/runtime/jdwp/jdwp_event.cc index 345549d1a0c..61bd1edd5d1 100644 --- a/runtime/jdwp/jdwp_event.cc +++ b/runtime/jdwp/jdwp_event.cc @@ -719,7 +719,8 @@ bool JdwpState::PostVMStart() { * - Single-step to a line with a breakpoint. Should get a single * event message with both events in it. */ -bool JdwpState::PostLocationEvent(const JdwpLocation* pLoc, ObjectId thisPtr, int eventFlags) { +bool JdwpState::PostLocationEvent(const JdwpLocation* pLoc, ObjectId thisPtr, int eventFlags, + const JValue* returnValue) { ModBasket basket; basket.pLoc = pLoc; basket.classId = pLoc->class_id; @@ -771,9 +772,7 @@ bool JdwpState::PostLocationEvent(const JdwpLocation* pLoc, ObjectId thisPtr, in } if ((eventFlags & Dbg::kMethodExit) != 0) { FindMatchingEvents(EK_METHOD_EXIT, &basket, match_list, &match_count); - - // TODO: match EK_METHOD_EXIT_WITH_RETURN_VALUE too; we need to include the 'value', though. - // FindMatchingEvents(EK_METHOD_EXIT_WITH_RETURN_VALUE, &basket, match_list, &match_count); + FindMatchingEvents(EK_METHOD_EXIT_WITH_RETURN_VALUE, &basket, match_list, &match_count); } if (match_count != 0) { VLOG(jdwp) << "EVENT: " << match_list[0]->eventKind << "(" << match_count << " total) " @@ -792,6 +791,9 @@ bool JdwpState::PostLocationEvent(const JdwpLocation* pLoc, ObjectId thisPtr, in expandBufAdd4BE(pReq, match_list[i]->requestId); expandBufAdd8BE(pReq, basket.threadId); expandBufAddLocation(pReq, *pLoc); + if (match_list[i]->eventKind == EK_METHOD_EXIT_WITH_RETURN_VALUE) { + Dbg::OutputMethodReturnValue(pLoc->method_id, returnValue, pReq); + } } } From 61b7f1b05d1fe12d4009316263bf990903e4edff Mon Sep 17 00:00:00 2001 From: Sebastien Hertz Date: Fri, 15 Nov 2013 15:59:30 +0100 Subject: [PATCH 0203/2402] Move single-step control into thread. This CL moves single-step control into the Thread structure. This is stored in Thread::single_step_control_ member. This allows to support single-stepping of multiple threads at the same time. Since each thread holds its single-step information, we no longer need to use the breakpoint lock to support single-stepping. It helps reduce lock contention on this lock while debugging. All JDWP tests passed on the host and on the target with this CL. Bug: 11667502 Change-Id: I886d5c8c625ca5a072803e296c32eec5f7e9e82d --- runtime/debugger.cc | 234 ++++++++++++++++++++------------------------ runtime/debugger.h | 38 ++++++- runtime/thread.cc | 2 + runtime/thread.h | 8 ++ 4 files changed, 154 insertions(+), 128 deletions(-) diff --git a/runtime/debugger.cc b/runtime/debugger.cc index f5377092618..d63cb1c8352 100644 --- a/runtime/debugger.cc +++ b/runtime/debugger.cc @@ -95,20 +95,6 @@ static std::ostream& operator<<(std::ostream& os, const Breakpoint& rhs) return os; } -struct SingleStepControl { - // Are we single-stepping right now? - bool is_active; - Thread* thread; - - JDWP::JdwpStepSize step_size; - JDWP::JdwpStepDepth step_depth; - - const mirror::ArtMethod* method; - int32_t line_number; // Or -1 for native methods. - std::set dex_pcs; - int stack_depth; -}; - class DebugInstrumentationListener : public instrumentation::InstrumentationListener { public: DebugInstrumentationListener() {} @@ -192,7 +178,6 @@ static size_t gAllocRecordCount GUARDED_BY(gAllocTrackerLock) = 0; // Breakpoints and single-stepping. static std::vector gBreakpoints GUARDED_BY(Locks::breakpoint_lock_); -static SingleStepControl gSingleStepControl GUARDED_BY(Locks::breakpoint_lock_); static bool IsBreakpoint(const mirror::ArtMethod* m, uint32_t dex_pc) LOCKS_EXCLUDED(Locks::breakpoint_lock_) @@ -2293,63 +2278,62 @@ void Dbg::UpdateDebugger(Thread* thread, mirror::Object* this_object, event_flags |= kBreakpoint; } - { - // If the debugger is single-stepping one of our threads, check to - // see if we're that thread and we've reached a step point. - MutexLock mu(Thread::Current(), *Locks::breakpoint_lock_); - if (gSingleStepControl.is_active && gSingleStepControl.thread == thread) { - CHECK(!m->IsNative()); - if (gSingleStepControl.step_depth == JDWP::SD_INTO) { - // Step into method calls. We break when the line number - // or method pointer changes. If we're in SS_MIN mode, we - // always stop. - if (gSingleStepControl.method != m) { - event_flags |= kSingleStep; - VLOG(jdwp) << "SS new method"; - } else if (gSingleStepControl.step_size == JDWP::SS_MIN) { + // If the debugger is single-stepping one of our threads, check to + // see if we're that thread and we've reached a step point. + const SingleStepControl* single_step_control = thread->GetSingleStepControl(); + DCHECK(single_step_control != nullptr); + if (single_step_control->is_active) { + CHECK(!m->IsNative()); + if (single_step_control->step_depth == JDWP::SD_INTO) { + // Step into method calls. We break when the line number + // or method pointer changes. If we're in SS_MIN mode, we + // always stop. + if (single_step_control->method != m) { + event_flags |= kSingleStep; + VLOG(jdwp) << "SS new method"; + } else if (single_step_control->step_size == JDWP::SS_MIN) { + event_flags |= kSingleStep; + VLOG(jdwp) << "SS new instruction"; + } else if (single_step_control->dex_pcs.find(dex_pc) == single_step_control->dex_pcs.end()) { + event_flags |= kSingleStep; + VLOG(jdwp) << "SS new line"; + } + } else if (single_step_control->step_depth == JDWP::SD_OVER) { + // Step over method calls. We break when the line number is + // different and the frame depth is <= the original frame + // depth. (We can't just compare on the method, because we + // might get unrolled past it by an exception, and it's tricky + // to identify recursion.) + + int stack_depth = GetStackDepth(thread); + + if (stack_depth < single_step_control->stack_depth) { + // Popped up one or more frames, always trigger. + event_flags |= kSingleStep; + VLOG(jdwp) << "SS method pop"; + } else if (stack_depth == single_step_control->stack_depth) { + // Same depth, see if we moved. + if (single_step_control->step_size == JDWP::SS_MIN) { event_flags |= kSingleStep; VLOG(jdwp) << "SS new instruction"; - } else if (gSingleStepControl.dex_pcs.find(dex_pc) == gSingleStepControl.dex_pcs.end()) { + } else if (single_step_control->dex_pcs.find(dex_pc) == single_step_control->dex_pcs.end()) { event_flags |= kSingleStep; VLOG(jdwp) << "SS new line"; } - } else if (gSingleStepControl.step_depth == JDWP::SD_OVER) { - // Step over method calls. We break when the line number is - // different and the frame depth is <= the original frame - // depth. (We can't just compare on the method, because we - // might get unrolled past it by an exception, and it's tricky - // to identify recursion.) - - int stack_depth = GetStackDepth(thread); - - if (stack_depth < gSingleStepControl.stack_depth) { - // popped up one or more frames, always trigger - event_flags |= kSingleStep; - VLOG(jdwp) << "SS method pop"; - } else if (stack_depth == gSingleStepControl.stack_depth) { - // same depth, see if we moved - if (gSingleStepControl.step_size == JDWP::SS_MIN) { - event_flags |= kSingleStep; - VLOG(jdwp) << "SS new instruction"; - } else if (gSingleStepControl.dex_pcs.find(dex_pc) == gSingleStepControl.dex_pcs.end()) { - event_flags |= kSingleStep; - VLOG(jdwp) << "SS new line"; - } - } - } else { - CHECK_EQ(gSingleStepControl.step_depth, JDWP::SD_OUT); - // Return from the current method. We break when the frame - // depth pops up. - - // This differs from the "method exit" break in that it stops - // with the PC at the next instruction in the returned-to - // function, rather than the end of the returning function. - - int stack_depth = GetStackDepth(thread); - if (stack_depth < gSingleStepControl.stack_depth) { - event_flags |= kSingleStep; - VLOG(jdwp) << "SS method pop"; - } + } + } else { + CHECK_EQ(single_step_control->step_depth, JDWP::SD_OUT); + // Return from the current method. We break when the frame + // depth pops up. + + // This differs from the "method exit" break in that it stops + // with the PC at the next instruction in the returned-to + // function, rather than the end of the returning function. + + int stack_depth = GetStackDepth(thread); + if (stack_depth < single_step_control->stack_depth) { + event_flags |= kSingleStep; + VLOG(jdwp) << "SS method pop"; } } } @@ -2445,50 +2429,50 @@ JDWP::JdwpError Dbg::ConfigureStep(JDWP::ObjectId thread_id, JDWP::JdwpStepSize return sts.GetError(); } - MutexLock mu2(self, *Locks::breakpoint_lock_); - // TODO: there's no theoretical reason why we couldn't support single-stepping - // of multiple threads at once, but we never did so historically. - if (gSingleStepControl.thread != NULL && sts.GetThread() != gSingleStepControl.thread) { - LOG(WARNING) << "single-step already active for " << *gSingleStepControl.thread - << "; switching to " << *sts.GetThread(); - } - // // Work out what Method* we're in, the current line number, and how deep the stack currently // is for step-out. // struct SingleStepStackVisitor : public StackVisitor { - explicit SingleStepStackVisitor(Thread* thread) - EXCLUSIVE_LOCKS_REQUIRED(Locks::breakpoint_lock_) + explicit SingleStepStackVisitor(Thread* thread, SingleStepControl* single_step_control, + int32_t* line_number) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) - : StackVisitor(thread, NULL) { - gSingleStepControl.method = NULL; - gSingleStepControl.stack_depth = 0; + : StackVisitor(thread, NULL), single_step_control_(single_step_control), + line_number_(line_number) { + DCHECK_EQ(single_step_control_, thread->GetSingleStepControl()); + single_step_control_->method = NULL; + single_step_control_->stack_depth = 0; } // TODO: Enable annotalysis. We know lock is held in constructor, but abstraction confuses // annotalysis. bool VisitFrame() NO_THREAD_SAFETY_ANALYSIS { - Locks::breakpoint_lock_->AssertHeld(Thread::Current()); - const mirror::ArtMethod* m = GetMethod(); + mirror::ArtMethod* m = GetMethod(); if (!m->IsRuntimeMethod()) { - ++gSingleStepControl.stack_depth; - if (gSingleStepControl.method == NULL) { + ++single_step_control_->stack_depth; + if (single_step_control_->method == NULL) { const mirror::DexCache* dex_cache = m->GetDeclaringClass()->GetDexCache(); - gSingleStepControl.method = m; - gSingleStepControl.line_number = -1; + single_step_control_->method = m; + *line_number_ = -1; if (dex_cache != NULL) { const DexFile& dex_file = *dex_cache->GetDexFile(); - gSingleStepControl.line_number = dex_file.GetLineNumFromPC(m, GetDexPc()); + *line_number_ = dex_file.GetLineNumFromPC(m, GetDexPc()); } } } return true; } + + SingleStepControl* const single_step_control_; + int32_t* const line_number_; }; - SingleStepStackVisitor visitor(sts.GetThread()); + Thread* const thread = sts.GetThread(); + SingleStepControl* const single_step_control = thread->GetSingleStepControl(); + DCHECK(single_step_control != nullptr); + int32_t line_number = -1; + SingleStepStackVisitor visitor(thread, single_step_control, &line_number); visitor.WalkStack(); // @@ -2496,17 +2480,14 @@ JDWP::JdwpError Dbg::ConfigureStep(JDWP::ObjectId thread_id, JDWP::JdwpStepSize // struct DebugCallbackContext { - DebugCallbackContext() EXCLUSIVE_LOCKS_REQUIRED(Locks::breakpoint_lock_) { - last_pc_valid = false; - last_pc = 0; + explicit DebugCallbackContext(SingleStepControl* single_step_control, int32_t line_number) + : single_step_control_(single_step_control), line_number_(line_number), + last_pc_valid(false), last_pc(0) { } - // TODO: Enable annotalysis. We know lock is held in constructor, but abstraction confuses - // annotalysis. - static bool Callback(void* raw_context, uint32_t address, uint32_t line_number) NO_THREAD_SAFETY_ANALYSIS { - Locks::breakpoint_lock_->AssertHeld(Thread::Current()); + static bool Callback(void* raw_context, uint32_t address, uint32_t line_number) { DebugCallbackContext* context = reinterpret_cast(raw_context); - if (static_cast(line_number) == gSingleStepControl.line_number) { + if (static_cast(line_number) == context->line_number_) { if (!context->last_pc_valid) { // Everything from this address until the next line change is ours. context->last_pc = address; @@ -2517,35 +2498,32 @@ JDWP::JdwpError Dbg::ConfigureStep(JDWP::ObjectId thread_id, JDWP::JdwpStepSize } else if (context->last_pc_valid) { // and the line number is new // Add everything from the last entry up until here to the set for (uint32_t dex_pc = context->last_pc; dex_pc < address; ++dex_pc) { - gSingleStepControl.dex_pcs.insert(dex_pc); + context->single_step_control_->dex_pcs.insert(dex_pc); } context->last_pc_valid = false; } return false; // There may be multiple entries for any given line. } - // TODO: Enable annotalysis. We know lock is held in constructor, but abstraction confuses - // annotalysis. - ~DebugCallbackContext() NO_THREAD_SAFETY_ANALYSIS { - Locks::breakpoint_lock_->AssertHeld(Thread::Current()); + ~DebugCallbackContext() { // If the line number was the last in the position table... if (last_pc_valid) { - size_t end = MethodHelper(gSingleStepControl.method).GetCodeItem()->insns_size_in_code_units_; + size_t end = MethodHelper(single_step_control_->method).GetCodeItem()->insns_size_in_code_units_; for (uint32_t dex_pc = last_pc; dex_pc < end; ++dex_pc) { - gSingleStepControl.dex_pcs.insert(dex_pc); + single_step_control_->dex_pcs.insert(dex_pc); } } } + SingleStepControl* const single_step_control_; + const int32_t line_number_; bool last_pc_valid; uint32_t last_pc; }; - gSingleStepControl.dex_pcs.clear(); - const mirror::ArtMethod* m = gSingleStepControl.method; - if (m->IsNative()) { - gSingleStepControl.line_number = -1; - } else { - DebugCallbackContext context; + single_step_control->dex_pcs.clear(); + const mirror::ArtMethod* m = single_step_control->method; + if (!m->IsNative()) { + DebugCallbackContext context(single_step_control, line_number); MethodHelper mh(m); mh.GetDexFile().DecodeDebugInfo(mh.GetCodeItem(), m->IsStatic(), m->GetDexMethodIndex(), DebugCallbackContext::Callback, NULL, &context); @@ -2555,20 +2533,19 @@ JDWP::JdwpError Dbg::ConfigureStep(JDWP::ObjectId thread_id, JDWP::JdwpStepSize // Everything else... // - gSingleStepControl.thread = sts.GetThread(); - gSingleStepControl.step_size = step_size; - gSingleStepControl.step_depth = step_depth; - gSingleStepControl.is_active = true; + single_step_control->step_size = step_size; + single_step_control->step_depth = step_depth; + single_step_control->is_active = true; if (VLOG_IS_ON(jdwp)) { - VLOG(jdwp) << "Single-step thread: " << *gSingleStepControl.thread; - VLOG(jdwp) << "Single-step step size: " << gSingleStepControl.step_size; - VLOG(jdwp) << "Single-step step depth: " << gSingleStepControl.step_depth; - VLOG(jdwp) << "Single-step current method: " << PrettyMethod(gSingleStepControl.method); - VLOG(jdwp) << "Single-step current line: " << gSingleStepControl.line_number; - VLOG(jdwp) << "Single-step current stack depth: " << gSingleStepControl.stack_depth; + VLOG(jdwp) << "Single-step thread: " << *thread; + VLOG(jdwp) << "Single-step step size: " << single_step_control->step_size; + VLOG(jdwp) << "Single-step step depth: " << single_step_control->step_depth; + VLOG(jdwp) << "Single-step current method: " << PrettyMethod(single_step_control->method); + VLOG(jdwp) << "Single-step current line: " << line_number; + VLOG(jdwp) << "Single-step current stack depth: " << single_step_control->stack_depth; VLOG(jdwp) << "Single-step dex_pc values:"; - for (std::set::iterator it = gSingleStepControl.dex_pcs.begin() ; it != gSingleStepControl.dex_pcs.end(); ++it) { + for (std::set::iterator it = single_step_control->dex_pcs.begin(); it != single_step_control->dex_pcs.end(); ++it) { VLOG(jdwp) << StringPrintf(" %#x", *it); } } @@ -2576,12 +2553,17 @@ JDWP::JdwpError Dbg::ConfigureStep(JDWP::ObjectId thread_id, JDWP::JdwpStepSize return JDWP::ERR_NONE; } -void Dbg::UnconfigureStep(JDWP::ObjectId /*thread_id*/) { - MutexLock mu(Thread::Current(), *Locks::breakpoint_lock_); - - gSingleStepControl.is_active = false; - gSingleStepControl.thread = NULL; - gSingleStepControl.dex_pcs.clear(); +void Dbg::UnconfigureStep(JDWP::ObjectId thread_id) { + ScopedObjectAccessUnchecked soa(Thread::Current()); + MutexLock mu(soa.Self(), *Locks::thread_list_lock_); + Thread* thread; + JDWP::JdwpError error = DecodeThread(soa, thread_id, thread); + if (error != JDWP::ERR_NONE) { + SingleStepControl* single_step_control = thread->GetSingleStepControl(); + DCHECK(single_step_control != nullptr); + single_step_control->is_active = false; + single_step_control->dex_pcs.clear(); + } } static char JdwpTagToShortyChar(JDWP::JdwpTag tag) { diff --git a/runtime/debugger.h b/runtime/debugger.h index 8574a3308f2..a774d55c35f 100644 --- a/runtime/debugger.h +++ b/runtime/debugger.h @@ -23,6 +23,7 @@ #include +#include #include #include "jdwp/jdwp.h" @@ -79,6 +80,39 @@ struct DebugInvokeReq { /* condition variable to wait on while the method executes */ Mutex lock_ DEFAULT_MUTEX_ACQUIRED_AFTER; ConditionVariable cond_ GUARDED_BY(lock_); + + private: + DISALLOW_COPY_AND_ASSIGN(DebugInvokeReq); +}; + +// Thread local data-structure that holds fields for controlling single-stepping. +struct SingleStepControl { + SingleStepControl() + : is_active(false), step_size(JDWP::SS_MIN), step_depth(JDWP::SD_INTO), + method(nullptr), stack_depth(0) { + } + + // Are we single-stepping right now? + bool is_active; + + // See JdwpStepSize and JdwpStepDepth for details. + JDWP::JdwpStepSize step_size; + JDWP::JdwpStepDepth step_depth; + + // The location this single-step was initiated from. + // A single-step is initiated in a suspended thread. We save here the current method and the + // set of DEX pcs associated to the source line number where the suspension occurred. + // This is used to support SD_INTO and SD_OVER single-step depths so we detect when a single-step + // causes the execution of an instruction in a different method or at a different line number. + mirror::ArtMethod* method; + std::set dex_pcs; + + // The stack depth when this single-step was initiated. This is used to support SD_OVER and SD_OUT + // single-step depth. + int stack_depth; + + private: + DISALLOW_COPY_AND_ASSIGN(SingleStepControl); }; class Dbg { @@ -353,9 +387,9 @@ class Dbg { SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); static JDWP::JdwpError ConfigureStep(JDWP::ObjectId thread_id, JDWP::JdwpStepSize size, JDWP::JdwpStepDepth depth) - LOCKS_EXCLUDED(Locks::breakpoint_lock_) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - static void UnconfigureStep(JDWP::ObjectId thread_id) LOCKS_EXCLUDED(Locks::breakpoint_lock_); + static void UnconfigureStep(JDWP::ObjectId thread_id) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); static JDWP::JdwpError InvokeMethod(JDWP::ObjectId thread_id, JDWP::ObjectId object_id, JDWP::RefTypeId class_id, JDWP::MethodId method_id, diff --git a/runtime/thread.cc b/runtime/thread.cc index 1f6dd69110d..bcfc51bbdad 100644 --- a/runtime/thread.cc +++ b/runtime/thread.cc @@ -917,6 +917,7 @@ Thread::Thread(bool daemon) throwing_OutOfMemoryError_(false), debug_suspend_count_(0), debug_invoke_req_(new DebugInvokeReq), + single_step_control_(new SingleStepControl), deoptimization_shadow_frame_(NULL), instrumentation_stack_(new std::deque), name_(new std::string(kThreadNameDuringStartup)), @@ -1018,6 +1019,7 @@ Thread::~Thread() { } delete debug_invoke_req_; + delete single_step_control_; delete instrumentation_stack_; delete name_; delete stack_trace_sample_; diff --git a/runtime/thread.h b/runtime/thread.h index 6bd3607922e..745b1aca35d 100644 --- a/runtime/thread.h +++ b/runtime/thread.h @@ -68,6 +68,7 @@ class Runtime; class ScopedObjectAccess; class ScopedObjectAccessUnchecked; class ShadowFrame; +struct SingleStepControl; class Thread; class ThreadList; @@ -513,6 +514,10 @@ class PACKED(4) Thread { return debug_invoke_req_; } + SingleStepControl* GetSingleStepControl() const { + return single_step_control_; + } + void SetDeoptimizationShadowFrame(ShadowFrame* sf); void SetDeoptimizationReturnValue(const JValue& ret_val); @@ -746,6 +751,9 @@ class PACKED(4) Thread { // JDWP invoke-during-breakpoint support. DebugInvokeReq* debug_invoke_req_; + // JDWP single-stepping support. + SingleStepControl* single_step_control_; + // Shadow frame that is used temporarily during the deoptimization of a method. ShadowFrame* deoptimization_shadow_frame_; JValue deoptimization_return_value_; From 7cb7bbcfffb2716ef8d68ecb747954ec42c4bdc5 Mon Sep 17 00:00:00 2001 From: Hiroshi Yamauchi Date: Mon, 18 Nov 2013 17:27:37 -0800 Subject: [PATCH 0204/2402] Add Valgrind rosalloc support. Bug: 9986565 Change-Id: Ibd2ba5e8b4fb7f2ed6c133a4b556a6dbb15a2f5e --- runtime/gc/space/dlmalloc_space.cc | 77 +----------------------------- runtime/gc/space/malloc_space.h | 77 ++++++++++++++++++++++++++++++ runtime/gc/space/rosalloc_space.cc | 6 +-- 3 files changed, 82 insertions(+), 78 deletions(-) diff --git a/runtime/gc/space/dlmalloc_space.cc b/runtime/gc/space/dlmalloc_space.cc index b067bbc9cf7..10e9ed8cf51 100644 --- a/runtime/gc/space/dlmalloc_space.cc +++ b/runtime/gc/space/dlmalloc_space.cc @@ -35,79 +35,6 @@ namespace space { static const bool kPrefetchDuringDlMallocFreeList = true; -// Number of bytes to use as a red zone (rdz). A red zone of this size will be placed before and -// after each allocation. 8 bytes provides long/double alignment. -const size_t kValgrindRedZoneBytes = 8; - -// A specialization of DlMallocSpace that provides information to valgrind wrt allocations. -class ValgrindDlMallocSpace : public DlMallocSpace { - public: - virtual mirror::Object* AllocWithGrowth(Thread* self, size_t num_bytes, size_t* bytes_allocated) { - void* obj_with_rdz = DlMallocSpace::AllocWithGrowth(self, num_bytes + 2 * kValgrindRedZoneBytes, - bytes_allocated); - if (obj_with_rdz == NULL) { - return NULL; - } - mirror::Object* result = reinterpret_cast( - reinterpret_cast(obj_with_rdz) + kValgrindRedZoneBytes); - // Make redzones as no access. - VALGRIND_MAKE_MEM_NOACCESS(obj_with_rdz, kValgrindRedZoneBytes); - VALGRIND_MAKE_MEM_NOACCESS(reinterpret_cast(result) + num_bytes, kValgrindRedZoneBytes); - return result; - } - - virtual mirror::Object* Alloc(Thread* self, size_t num_bytes, size_t* bytes_allocated) { - void* obj_with_rdz = DlMallocSpace::Alloc(self, num_bytes + 2 * kValgrindRedZoneBytes, - bytes_allocated); - if (obj_with_rdz == NULL) { - return NULL; - } - mirror::Object* result = reinterpret_cast( - reinterpret_cast(obj_with_rdz) + kValgrindRedZoneBytes); - // Make redzones as no access. - VALGRIND_MAKE_MEM_NOACCESS(obj_with_rdz, kValgrindRedZoneBytes); - VALGRIND_MAKE_MEM_NOACCESS(reinterpret_cast(result) + num_bytes, kValgrindRedZoneBytes); - return result; - } - - virtual size_t AllocationSize(const mirror::Object* obj) { - size_t result = DlMallocSpace::AllocationSize(reinterpret_cast( - reinterpret_cast(obj) - kValgrindRedZoneBytes)); - return result - 2 * kValgrindRedZoneBytes; - } - - virtual size_t Free(Thread* self, mirror::Object* ptr) { - void* obj_after_rdz = reinterpret_cast(ptr); - void* obj_with_rdz = reinterpret_cast(obj_after_rdz) - kValgrindRedZoneBytes; - // Make redzones undefined. - size_t allocation_size = DlMallocSpace::AllocationSize( - reinterpret_cast(obj_with_rdz)); - VALGRIND_MAKE_MEM_UNDEFINED(obj_with_rdz, allocation_size); - size_t freed = DlMallocSpace::Free(self, reinterpret_cast(obj_with_rdz)); - return freed - 2 * kValgrindRedZoneBytes; - } - - virtual size_t FreeList(Thread* self, size_t num_ptrs, mirror::Object** ptrs) { - size_t freed = 0; - for (size_t i = 0; i < num_ptrs; i++) { - freed += Free(self, ptrs[i]); - } - return freed; - } - - ValgrindDlMallocSpace(const std::string& name, MemMap* mem_map, void* mspace, byte* begin, - byte* end, byte* limit, size_t growth_limit, size_t initial_size) : - DlMallocSpace(name, mem_map, mspace, begin, end, limit, growth_limit) { - VALGRIND_MAKE_MEM_UNDEFINED(mem_map->Begin() + initial_size, mem_map->Size() - initial_size); - } - - virtual ~ValgrindDlMallocSpace() { - } - - private: - DISALLOW_COPY_AND_ASSIGN(ValgrindDlMallocSpace); -}; - DlMallocSpace::DlMallocSpace(const std::string& name, MemMap* mem_map, void* mspace, byte* begin, byte* end, byte* limit, size_t growth_limit) : MallocSpace(name, mem_map, begin, end, limit, growth_limit), @@ -155,8 +82,8 @@ DlMallocSpace* DlMallocSpace::Create(const std::string& name, size_t initial_siz DlMallocSpace* space; byte* begin = mem_map->Begin(); if (RUNNING_ON_VALGRIND > 0) { - space = new ValgrindDlMallocSpace(name, mem_map, mspace, begin, end, begin + capacity, - growth_limit, initial_size); + space = new ValgrindMallocSpace( + name, mem_map, mspace, begin, end, begin + capacity, growth_limit, initial_size); } else { space = new DlMallocSpace(name, mem_map, mspace, begin, end, begin + capacity, growth_limit); } diff --git a/runtime/gc/space/malloc_space.h b/runtime/gc/space/malloc_space.h index 189f01cfabf..0f882d33e11 100644 --- a/runtime/gc/space/malloc_space.h +++ b/runtime/gc/space/malloc_space.h @@ -19,6 +19,9 @@ #include "space.h" +#include +#include + namespace art { namespace gc { @@ -185,6 +188,80 @@ class MallocSpace : public ContinuousMemMapAllocSpace { DISALLOW_COPY_AND_ASSIGN(MallocSpace); }; +// Number of bytes to use as a red zone (rdz). A red zone of this size will be placed before and +// after each allocation. 8 bytes provides long/double alignment. +static constexpr size_t kValgrindRedZoneBytes = 8; + +// A specialization of DlMallocSpace/RosAllocSpace that provides information to valgrind wrt allocations. +template +class ValgrindMallocSpace : public BaseMallocSpaceType { + public: + virtual mirror::Object* AllocWithGrowth(Thread* self, size_t num_bytes, size_t* bytes_allocated) { + void* obj_with_rdz = BaseMallocSpaceType::AllocWithGrowth(self, num_bytes + 2 * kValgrindRedZoneBytes, + bytes_allocated); + if (obj_with_rdz == NULL) { + return NULL; + } + mirror::Object* result = reinterpret_cast( + reinterpret_cast(obj_with_rdz) + kValgrindRedZoneBytes); + // Make redzones as no access. + VALGRIND_MAKE_MEM_NOACCESS(obj_with_rdz, kValgrindRedZoneBytes); + VALGRIND_MAKE_MEM_NOACCESS(reinterpret_cast(result) + num_bytes, kValgrindRedZoneBytes); + return result; + } + + virtual mirror::Object* Alloc(Thread* self, size_t num_bytes, size_t* bytes_allocated) { + void* obj_with_rdz = BaseMallocSpaceType::Alloc(self, num_bytes + 2 * kValgrindRedZoneBytes, + bytes_allocated); + if (obj_with_rdz == NULL) { + return NULL; + } + mirror::Object* result = reinterpret_cast( + reinterpret_cast(obj_with_rdz) + kValgrindRedZoneBytes); + // Make redzones as no access. + VALGRIND_MAKE_MEM_NOACCESS(obj_with_rdz, kValgrindRedZoneBytes); + VALGRIND_MAKE_MEM_NOACCESS(reinterpret_cast(result) + num_bytes, kValgrindRedZoneBytes); + return result; + } + + virtual size_t AllocationSize(const mirror::Object* obj) { + size_t result = BaseMallocSpaceType::AllocationSize(reinterpret_cast( + reinterpret_cast(obj) - kValgrindRedZoneBytes)); + return result - 2 * kValgrindRedZoneBytes; + } + + virtual size_t Free(Thread* self, mirror::Object* ptr) { + void* obj_after_rdz = reinterpret_cast(ptr); + void* obj_with_rdz = reinterpret_cast(obj_after_rdz) - kValgrindRedZoneBytes; + // Make redzones undefined. + size_t allocation_size = BaseMallocSpaceType::AllocationSize( + reinterpret_cast(obj_with_rdz)); + VALGRIND_MAKE_MEM_UNDEFINED(obj_with_rdz, allocation_size); + size_t freed = BaseMallocSpaceType::Free(self, reinterpret_cast(obj_with_rdz)); + return freed - 2 * kValgrindRedZoneBytes; + } + + virtual size_t FreeList(Thread* self, size_t num_ptrs, mirror::Object** ptrs) { + size_t freed = 0; + for (size_t i = 0; i < num_ptrs; i++) { + freed += Free(self, ptrs[i]); + } + return freed; + } + + ValgrindMallocSpace(const std::string& name, MemMap* mem_map, AllocatorType allocator, byte* begin, + byte* end, byte* limit, size_t growth_limit, size_t initial_size) : + BaseMallocSpaceType(name, mem_map, allocator, begin, end, limit, growth_limit) { + VALGRIND_MAKE_MEM_UNDEFINED(mem_map->Begin() + initial_size, mem_map->Size() - initial_size); + } + + virtual ~ValgrindMallocSpace() { + } + + private: + DISALLOW_COPY_AND_ASSIGN(ValgrindMallocSpace); +}; + } // namespace space } // namespace gc } // namespace art diff --git a/runtime/gc/space/rosalloc_space.cc b/runtime/gc/space/rosalloc_space.cc index a3d2dfa20bc..1f8e324823d 100644 --- a/runtime/gc/space/rosalloc_space.cc +++ b/runtime/gc/space/rosalloc_space.cc @@ -1,3 +1,4 @@ + /* * Copyright (C) 2013 The Android Open Source Project * @@ -83,9 +84,8 @@ RosAllocSpace* RosAllocSpace::Create(const std::string& name, size_t initial_siz RosAllocSpace* space; byte* begin = mem_map->Begin(); if (RUNNING_ON_VALGRIND > 0) { - // TODO: support valgrind. - LOG(FATAL) << "Unimplemented"; - space = NULL; + space = new ValgrindMallocSpace( + name, mem_map, rosalloc, begin, end, begin + capacity, growth_limit, initial_size); } else { space = new RosAllocSpace(name, mem_map, rosalloc, begin, end, begin + capacity, growth_limit); } From cbb2d20bea2861f244da2e2318d8c088300a3710 Mon Sep 17 00:00:00 2001 From: Mathieu Chartier Date: Thu, 14 Nov 2013 17:45:16 -0800 Subject: [PATCH 0205/2402] Refactor allocation entrypoints. Adds support for switching entrypoints during runtime. Enables addition of new allocators with out requiring significant copy paste. Slight speedup on ritzperf probably due to more inlining. TODO: Ensuring that the entire allocation path is inlined so that the switch statement in the allocation code is optimized out. Rosalloc measurements: 4583 4453 4439 4434 4751 After change: 4184 4287 4131 4335 4097 Change-Id: I1352a3cbcdf6dae93921582726324d91312df5c9 --- runtime/Android.mk | 1 + runtime/arch/alloc_entrypoints.S | 36 --- runtime/arch/arm/entrypoints_init_arm.cc | 39 +-- runtime/arch/arm/quick_entrypoints_arm.S | 2 +- runtime/arch/mips/entrypoints_init_mips.cc | 39 +-- runtime/arch/mips/quick_entrypoints_mips.S | 162 ++-------- runtime/arch/quick_alloc_entrypoints.S | 38 +++ runtime/arch/quick_alloc_entrypoints.cc | 85 +++++ runtime/arch/x86/entrypoints_init_x86.cc | 39 +-- runtime/arch/x86/quick_entrypoints_x86.S | 2 +- runtime/class_linker.cc | 16 +- runtime/debugger.cc | 3 +- runtime/entrypoints/entrypoint_utils.cc | 41 +-- runtime/entrypoints/entrypoint_utils.h | 144 ++++----- .../portable/portable_alloc_entrypoints.cc | 16 +- .../quick/quick_alloc_entrypoints.cc | 149 +++------ runtime/gc/heap-inl.h | 223 ++++++------- runtime/gc/heap.cc | 303 ++++-------------- runtime/gc/heap.h | 137 +++----- runtime/gc/space/bump_pointer_space.h | 6 +- runtime/instrumentation.cc | 48 ++- runtime/instrumentation.h | 4 +- runtime/interpreter/interpreter_common.cc | 4 +- .../interpreter_goto_table_impl.cc | 10 +- .../interpreter/interpreter_switch_impl.cc | 10 +- runtime/mirror/array-inl.h | 45 +-- runtime/mirror/array.cc | 5 +- runtime/mirror/array.h | 18 +- runtime/mirror/class-inl.h | 27 +- runtime/mirror/class.h | 14 +- runtime/mirror/object.cc | 4 +- runtime/mirror/object_array-inl.h | 22 +- runtime/mirror/object_array.h | 5 + runtime/mirror/object_test.cc | 9 +- runtime/native/dalvik_system_VMRuntime.cc | 3 +- runtime/native/java_lang_reflect_Array.cc | 3 +- .../native/java_lang_reflect_Constructor.cc | 2 +- 37 files changed, 651 insertions(+), 1063 deletions(-) delete mode 100644 runtime/arch/alloc_entrypoints.S create mode 100644 runtime/arch/quick_alloc_entrypoints.S create mode 100644 runtime/arch/quick_alloc_entrypoints.cc diff --git a/runtime/Android.mk b/runtime/Android.mk index 2aa59d5f479..16f11c6af77 100644 --- a/runtime/Android.mk +++ b/runtime/Android.mk @@ -147,6 +147,7 @@ LIBART_COMMON_SRC_FILES += \ arch/arm/registers_arm.cc \ arch/x86/registers_x86.cc \ arch/mips/registers_mips.cc \ + arch/quick_alloc_entrypoints.cc \ entrypoints/entrypoint_utils.cc \ entrypoints/interpreter/interpreter_entrypoints.cc \ entrypoints/jni/jni_entrypoints.cc \ diff --git a/runtime/arch/alloc_entrypoints.S b/runtime/arch/alloc_entrypoints.S deleted file mode 100644 index 840f3c6197b..00000000000 --- a/runtime/arch/alloc_entrypoints.S +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (C) 2013 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. - */ - -/* Called by managed code to allocate an object */ -TWO_ARG_DOWNCALL art_quick_alloc_object, artAllocObjectFromCode, RETURN_IF_RESULT_IS_NON_ZERO -TWO_ARG_DOWNCALL art_quick_alloc_object_instrumented, artAllocObjectFromCodeInstrumented, RETURN_IF_RESULT_IS_NON_ZERO -/* Called by managed code to allocate an object when the caller doesn't know whether it has access - * to the created type. */ -TWO_ARG_DOWNCALL art_quick_alloc_object_with_access_check, artAllocObjectFromCodeWithAccessCheck, RETURN_IF_RESULT_IS_NON_ZERO -TWO_ARG_DOWNCALL art_quick_alloc_object_with_access_check_instrumented, artAllocObjectFromCodeWithAccessCheckInstrumented, RETURN_IF_RESULT_IS_NON_ZERO -/* Called by managed code to allocate an array. */ -THREE_ARG_DOWNCALL art_quick_alloc_array, artAllocArrayFromCode, RETURN_IF_RESULT_IS_NON_ZERO -THREE_ARG_DOWNCALL art_quick_alloc_array_instrumented, artAllocArrayFromCodeInstrumented, RETURN_IF_RESULT_IS_NON_ZERO -/* Called by managed code to allocate an array when the caller doesn't know whether it has access - * to the created type. */ -THREE_ARG_DOWNCALL art_quick_alloc_array_with_access_check, artAllocArrayFromCodeWithAccessCheck, RETURN_IF_RESULT_IS_NON_ZERO -THREE_ARG_DOWNCALL art_quick_alloc_array_with_access_check_instrumented, artAllocArrayFromCodeWithAccessCheckInstrumented, RETURN_IF_RESULT_IS_NON_ZERO -/* Called by managed code to allocate an array in a special case for FILLED_NEW_ARRAY. */ -THREE_ARG_DOWNCALL art_quick_check_and_alloc_array, artCheckAndAllocArrayFromCode, RETURN_IF_RESULT_IS_NON_ZERO -THREE_ARG_DOWNCALL art_quick_check_and_alloc_array_instrumented, artCheckAndAllocArrayFromCodeInstrumented, RETURN_IF_RESULT_IS_NON_ZERO -/* Called by managed code to allocate an array in a special case for FILLED_NEW_ARRAY. */ -THREE_ARG_DOWNCALL art_quick_check_and_alloc_array_with_access_check, artCheckAndAllocArrayFromCodeWithAccessCheck -THREE_ARG_DOWNCALL art_quick_check_and_alloc_array_with_access_check_instrumented, artCheckAndAllocArrayFromCodeWithAccessCheckInstrumented diff --git a/runtime/arch/arm/entrypoints_init_arm.cc b/runtime/arch/arm/entrypoints_init_arm.cc index 3dac636df95..5166d29096d 100644 --- a/runtime/arch/arm/entrypoints_init_arm.cc +++ b/runtime/arch/arm/entrypoints_init_arm.cc @@ -34,21 +34,6 @@ extern "C" void artInterpreterToCompiledCodeBridge(Thread* self, MethodHelper& m extern "C" void art_portable_resolution_trampoline(mirror::ArtMethod*); extern "C" void art_portable_to_interpreter_bridge(mirror::ArtMethod*); -// Alloc entrypoints. -extern "C" void* art_quick_alloc_array(uint32_t, void*, int32_t); -extern "C" void* art_quick_alloc_array_with_access_check(uint32_t, void*, int32_t); -extern "C" void* art_quick_alloc_object(uint32_t type_idx, void* method); -extern "C" void* art_quick_alloc_object_with_access_check(uint32_t type_idx, void* method); -extern "C" void* art_quick_check_and_alloc_array(uint32_t, void*, int32_t); -extern "C" void* art_quick_check_and_alloc_array_with_access_check(uint32_t, void*, int32_t); - -extern "C" void* art_quick_alloc_array_instrumented(uint32_t, void*, int32_t); -extern "C" void* art_quick_alloc_array_with_access_check_instrumented(uint32_t, void*, int32_t); -extern "C" void* art_quick_alloc_object_instrumented(uint32_t type_idx, void* method); -extern "C" void* art_quick_alloc_object_with_access_check_instrumented(uint32_t type_idx, void* method); -extern "C" void* art_quick_check_and_alloc_array_instrumented(uint32_t, void*, int32_t); -extern "C" void* art_quick_check_and_alloc_array_with_access_check_instrumented(uint32_t, void*, int32_t); - // Cast entrypoints. extern "C" uint32_t artIsAssignableFromCode(const mirror::Class* klass, const mirror::Class* ref_class); @@ -142,29 +127,7 @@ extern "C" void art_quick_throw_no_such_method(int32_t method_idx); extern "C" void art_quick_throw_null_pointer_exception(); extern "C" void art_quick_throw_stack_overflow(void*); -static bool quick_alloc_entry_points_instrumented = false; - -void SetQuickAllocEntryPointsInstrumented(bool instrumented) { - quick_alloc_entry_points_instrumented = instrumented; -} - -void ResetQuickAllocEntryPoints(QuickEntryPoints* qpoints) { - if (quick_alloc_entry_points_instrumented) { - qpoints->pAllocArray = art_quick_alloc_array_instrumented; - qpoints->pAllocArrayWithAccessCheck = art_quick_alloc_array_with_access_check_instrumented; - qpoints->pAllocObject = art_quick_alloc_object_instrumented; - qpoints->pAllocObjectWithAccessCheck = art_quick_alloc_object_with_access_check_instrumented; - qpoints->pCheckAndAllocArray = art_quick_check_and_alloc_array_instrumented; - qpoints->pCheckAndAllocArrayWithAccessCheck = art_quick_check_and_alloc_array_with_access_check_instrumented; - } else { - qpoints->pAllocArray = art_quick_alloc_array; - qpoints->pAllocArrayWithAccessCheck = art_quick_alloc_array_with_access_check; - qpoints->pAllocObject = art_quick_alloc_object; - qpoints->pAllocObjectWithAccessCheck = art_quick_alloc_object_with_access_check; - qpoints->pCheckAndAllocArray = art_quick_check_and_alloc_array; - qpoints->pCheckAndAllocArrayWithAccessCheck = art_quick_check_and_alloc_array_with_access_check; - } -} +extern void ResetQuickAllocEntryPoints(QuickEntryPoints* qpoints); void InitEntryPoints(InterpreterEntryPoints* ipoints, JniEntryPoints* jpoints, PortableEntryPoints* ppoints, QuickEntryPoints* qpoints) { diff --git a/runtime/arch/arm/quick_entrypoints_arm.S b/runtime/arch/arm/quick_entrypoints_arm.S index dbfb93a8463..1976af51886 100644 --- a/runtime/arch/arm/quick_entrypoints_arm.S +++ b/runtime/arch/arm/quick_entrypoints_arm.S @@ -864,7 +864,7 @@ ENTRY \name END \name .endm -#include "arch/alloc_entrypoints.S" +#include "arch/quick_alloc_entrypoints.S" /* * Called by managed code when the value in rSUSPEND has been decremented to 0. diff --git a/runtime/arch/mips/entrypoints_init_mips.cc b/runtime/arch/mips/entrypoints_init_mips.cc index 331a4613549..e1b441ac9d9 100644 --- a/runtime/arch/mips/entrypoints_init_mips.cc +++ b/runtime/arch/mips/entrypoints_init_mips.cc @@ -33,21 +33,6 @@ extern "C" void artInterpreterToCompiledCodeBridge(Thread* self, MethodHelper& m extern "C" void art_portable_resolution_trampoline(mirror::ArtMethod*); extern "C" void art_portable_to_interpreter_bridge(mirror::ArtMethod*); -// Alloc entrypoints. -extern "C" void* art_quick_alloc_array(uint32_t, void*, int32_t); -extern "C" void* art_quick_alloc_array_with_access_check(uint32_t, void*, int32_t); -extern "C" void* art_quick_alloc_object(uint32_t type_idx, void* method); -extern "C" void* art_quick_alloc_object_with_access_check(uint32_t type_idx, void* method); -extern "C" void* art_quick_check_and_alloc_array(uint32_t, void*, int32_t); -extern "C" void* art_quick_check_and_alloc_array_with_access_check(uint32_t, void*, int32_t); - -extern "C" void* art_quick_alloc_array_instrumented(uint32_t, void*, int32_t); -extern "C" void* art_quick_alloc_array_with_access_check_instrumented(uint32_t, void*, int32_t); -extern "C" void* art_quick_alloc_object_instrumented(uint32_t type_idx, void* method); -extern "C" void* art_quick_alloc_object_with_access_check_instrumented(uint32_t type_idx, void* method); -extern "C" void* art_quick_check_and_alloc_array_instrumented(uint32_t, void*, int32_t); -extern "C" void* art_quick_check_and_alloc_array_with_access_check_instrumented(uint32_t, void*, int32_t); - // Cast entrypoints. extern "C" uint32_t artIsAssignableFromCode(const mirror::Class* klass, const mirror::Class* ref_class); @@ -143,29 +128,7 @@ extern "C" void art_quick_throw_no_such_method(int32_t method_idx); extern "C" void art_quick_throw_null_pointer_exception(); extern "C" void art_quick_throw_stack_overflow(void*); -static bool quick_alloc_entry_points_instrumented = false; - -void SetQuickAllocEntryPointsInstrumented(bool instrumented) { - quick_alloc_entry_points_instrumented = instrumented; -} - -void ResetQuickAllocEntryPoints(QuickEntryPoints* qpoints) { - if (quick_alloc_entry_points_instrumented) { - qpoints->pAllocArray = art_quick_alloc_array_instrumented; - qpoints->pAllocArrayWithAccessCheck = art_quick_alloc_array_with_access_check_instrumented; - qpoints->pAllocObject = art_quick_alloc_object_instrumented; - qpoints->pAllocObjectWithAccessCheck = art_quick_alloc_object_with_access_check_instrumented; - qpoints->pCheckAndAllocArray = art_quick_check_and_alloc_array_instrumented; - qpoints->pCheckAndAllocArrayWithAccessCheck = art_quick_check_and_alloc_array_with_access_check_instrumented; - } else { - qpoints->pAllocArray = art_quick_alloc_array; - qpoints->pAllocArrayWithAccessCheck = art_quick_alloc_array_with_access_check; - qpoints->pAllocObject = art_quick_alloc_object; - qpoints->pAllocObjectWithAccessCheck = art_quick_alloc_object_with_access_check; - qpoints->pCheckAndAllocArray = art_quick_check_and_alloc_array; - qpoints->pCheckAndAllocArrayWithAccessCheck = art_quick_check_and_alloc_array_with_access_check; - } -} +extern void ResetQuickAllocEntryPoints(QuickEntryPoints* qpoints); void InitEntryPoints(InterpreterEntryPoints* ipoints, JniEntryPoints* jpoints, PortableEntryPoints* ppoints, QuickEntryPoints* qpoints) { diff --git a/runtime/arch/mips/quick_entrypoints_mips.S b/runtime/arch/mips/quick_entrypoints_mips.S index 886271166c8..6d6d7962b2c 100644 --- a/runtime/arch/mips/quick_entrypoints_mips.S +++ b/runtime/arch/mips/quick_entrypoints_mips.S @@ -231,7 +231,7 @@ DELIVER_PENDING_EXCEPTION .endm -.macro RETURN_IF_NONZERO +.macro RETURN_IF_RESULT_IS_NON_ZERO RESTORE_REF_ONLY_CALLEE_SAVE_FRAME beqz $v0, 1f # success? nop @@ -689,7 +689,7 @@ ENTRY art_quick_initialize_static_storage # artInitializeStaticStorageFromCode(uint32_t type_idx, Method* referrer, Thread*, $sp) jal artInitializeStaticStorageFromCode move $a3, $sp # pass $sp - RETURN_IF_NONZERO + RETURN_IF_RESULT_IS_NON_ZERO END art_quick_initialize_static_storage /* @@ -703,7 +703,7 @@ ENTRY art_quick_initialize_type # artInitializeTypeFromCode(uint32_t type_idx, Method* referrer, Thread*, $sp) jal artInitializeTypeFromCode move $a3, $sp # pass $sp - RETURN_IF_NONZERO + RETURN_IF_RESULT_IS_NON_ZERO END art_quick_initialize_type /* @@ -718,7 +718,7 @@ ENTRY art_quick_initialize_type_and_verify_access # artInitializeTypeFromCode(uint32_t type_idx, Method* referrer, Thread*, $sp) jal artInitializeTypeAndVerifyAccessFromCode move $a3, $sp # pass $sp - RETURN_IF_NONZERO + RETURN_IF_RESULT_IS_NON_ZERO END art_quick_initialize_type_and_verify_access /* @@ -902,156 +902,36 @@ ENTRY art_quick_resolve_string # artResolveStringFromCode(Method* referrer, uint32_t string_idx, Thread*, $sp) jal artResolveStringFromCode move $a3, $sp # pass $sp - RETURN_IF_NONZERO + RETURN_IF_RESULT_IS_NON_ZERO END art_quick_resolve_string - /* - * Called by managed code to allocate an object. - */ - .extern artAllocObjectFromCode -ENTRY art_quick_alloc_object - GENERATE_GLOBAL_POINTER - SETUP_REF_ONLY_CALLEE_SAVE_FRAME # save callee saves in case of GC - move $a2, rSELF # pass Thread::Current - jal artAllocObjectFromCode # (uint32_t type_idx, Method* method, Thread*, $sp) - move $a3, $sp # pass $sp - RETURN_IF_NONZERO -END art_quick_alloc_object - - .extern artAllocObjectFromCodeInstrumented -ENTRY art_quick_alloc_object_instrumented - GENERATE_GLOBAL_POINTER - SETUP_REF_ONLY_CALLEE_SAVE_FRAME # save callee saves in case of GC - move $a2, rSELF # pass Thread::Current - jal artAllocObjectFromCodeInstrumented # (uint32_t type_idx, Method* method, Thread*, $sp) - move $a3, $sp # pass $sp - RETURN_IF_NONZERO -END art_quick_alloc_object_instrumented - /* - * Called by managed code to allocate an object when the caller doesn't know whether it has - * access to the created type. - */ - .extern artAllocObjectFromCodeWithAccessCheck -ENTRY art_quick_alloc_object_with_access_check +// Macro to facilitate adding new allocation entrypoints. +.macro TWO_ARG_DOWNCALL name, entrypoint, return + .extern \entrypoint +ENTRY \name GENERATE_GLOBAL_POINTER SETUP_REF_ONLY_CALLEE_SAVE_FRAME # save callee saves in case of GC move $a2, rSELF # pass Thread::Current - jal artAllocObjectFromCodeWithAccessCheck # (uint32_t type_idx, Method* method, Thread*, $sp) + jal \entrypoint move $a3, $sp # pass $sp - RETURN_IF_NONZERO -END art_quick_alloc_object_with_access_check - - .extern artAllocObjectFromCodeWithAccessCheckInstrumented -ENTRY art_quick_alloc_object_with_access_check_instrumented - GENERATE_GLOBAL_POINTER - SETUP_REF_ONLY_CALLEE_SAVE_FRAME # save callee saves in case of GC - move $a2, rSELF # pass Thread::Current - jal artAllocObjectFromCodeWithAccessCheckInstrumented # (uint32_t type_idx, Method* method, Thread*, $sp) - move $a3, $sp # pass $sp - RETURN_IF_NONZERO -END art_quick_alloc_object_with_access_check_instrumented - - /* - * Called by managed code to allocate an array. - */ - .extern artAllocArrayFromCode -ENTRY art_quick_alloc_array - GENERATE_GLOBAL_POINTER - SETUP_REF_ONLY_CALLEE_SAVE_FRAME # save callee saves in case of GC - move $a3, rSELF # pass Thread::Current - # artAllocArrayFromCode(uint32_t type_idx, Method* method, int32_t component_count, Thread*, $sp) - jal artAllocArrayFromCode - sw $sp, 16($sp) # pass $sp - RETURN_IF_NONZERO -END art_quick_alloc_array - - .extern artAllocArrayFromCodeInstrumented -ENTRY art_quick_alloc_array_instrumented - GENERATE_GLOBAL_POINTER - SETUP_REF_ONLY_CALLEE_SAVE_FRAME # save callee saves in case of GC - move $a3, rSELF # pass Thread::Current - # artAllocArrayFromCodeInstrumented(uint32_t type_idx, Method* method, int32_t component_count, Thread*, $sp) - jal artAllocArrayFromCodeInstrumented - sw $sp, 16($sp) # pass $sp - RETURN_IF_NONZERO -END art_quick_alloc_array_instrumented - - /* - * Called by managed code to allocate an array when the caller doesn't know whether it has - * access to the created type. - */ - .extern artAllocArrayFromCodeWithAccessCheck -ENTRY art_quick_alloc_array_with_access_check - GENERATE_GLOBAL_POINTER - SETUP_REF_ONLY_CALLEE_SAVE_FRAME # save callee saves in case of GC - move $a3, rSELF # pass Thread::Current - # artAllocArrayFromCodeWithAccessCheck(type_idx, method, component_count, Thread*, $sp) - jal artAllocArrayFromCodeWithAccessCheck - sw $sp, 16($sp) # pass $sp - RETURN_IF_NONZERO -END art_quick_alloc_array_with_access_check - - .extern artAllocArrayFromCodeWithAccessCheckInstrumented -ENTRY art_quick_alloc_array_with_access_check_instrumented - GENERATE_GLOBAL_POINTER - SETUP_REF_ONLY_CALLEE_SAVE_FRAME # save callee saves in case of GC - move $a3, rSELF # pass Thread::Current - # artAllocArrayFromCodeWithAccessCheckInstrumented(type_idx, method, component_count, Thread*, $sp) - jal artAllocArrayFromCodeWithAccessCheckInstrumented - sw $sp, 16($sp) # pass $sp - RETURN_IF_NONZERO -END art_quick_alloc_array_with_access_check_instrumented - - /* - * Called by managed code to allocate an array in a special case for FILLED_NEW_ARRAY. - */ - .extern artCheckAndAllocArrayFromCode -ENTRY art_quick_check_and_alloc_array - GENERATE_GLOBAL_POINTER - SETUP_REF_ONLY_CALLEE_SAVE_FRAME # save callee saves in case of GC - move $a3, rSELF # pass Thread::Current - # artCheckAndAllocArrayFromCode(uint32_t type_idx, Method* method, int32_t count, Thread* , $sp) - jal artCheckAndAllocArrayFromCode - sw $sp, 16($sp) # pass $sp - RETURN_IF_NONZERO -END art_quick_check_and_alloc_array + \return +END \name +.endm - .extern artCheckAndAllocArrayFromCodeInstrumented -ENTRY art_quick_check_and_alloc_array_instrumented +.macro THREE_ARG_DOWNCALL name, entrypoint, return + .extern \entrypoint +ENTRY \name GENERATE_GLOBAL_POINTER SETUP_REF_ONLY_CALLEE_SAVE_FRAME # save callee saves in case of GC move $a3, rSELF # pass Thread::Current - # artCheckAndAllocArrayFromCodeInstrumented(uint32_t type_idx, Method* method, int32_t count, Thread* , $sp) - jal artCheckAndAllocArrayFromCodeInstrumented + jal \entrypoint sw $sp, 16($sp) # pass $sp - RETURN_IF_NONZERO -END art_quick_check_and_alloc_array_instrumented - - /* - * Called by managed code to allocate an array in a special case for FILLED_NEW_ARRAY. - */ - .extern artCheckAndAllocArrayFromCodeWithAccessCheck -ENTRY art_quick_check_and_alloc_array_with_access_check - GENERATE_GLOBAL_POINTER - SETUP_REF_ONLY_CALLEE_SAVE_FRAME # save callee saves in case of GC - move $a3, rSELF # pass Thread::Current - # artCheckAndAllocArrayFromCodeWithAccessCheck(type_idx, method, count, Thread* , $sp) - jal artCheckAndAllocArrayFromCodeWithAccessCheck - sw $sp, 16($sp) # pass $sp - RETURN_IF_NONZERO -END art_quick_check_and_alloc_array_with_access_check + \return +END \name +.endm - .extern artCheckAndAllocArrayFromCodeWithAccessCheckInstrumented -ENTRY art_quick_check_and_alloc_array_with_access_check_instrumented - GENERATE_GLOBAL_POINTER - SETUP_REF_ONLY_CALLEE_SAVE_FRAME # save callee saves in case of GC - move $a3, rSELF # pass Thread::Current - # artCheckAndAllocArrayFromCodeWithAccessCheckInstrumented(type_idx, method, count, Thread* , $sp) - jal artCheckAndAllocArrayFromCodeWithAccessCheckInstrumented - sw $sp, 16($sp) # pass $sp - RETURN_IF_NONZERO -END art_quick_check_and_alloc_array_with_access_check_instrumented +#include "arch/quick_alloc_entrypoints.S" /* * Called by managed code when the value in rSUSPEND has been decremented to 0. diff --git a/runtime/arch/quick_alloc_entrypoints.S b/runtime/arch/quick_alloc_entrypoints.S new file mode 100644 index 00000000000..0109c13c778 --- /dev/null +++ b/runtime/arch/quick_alloc_entrypoints.S @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2013 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. + */ + + +.macro GENERATE_ALLOC_ENTRYPOINTS c_suffix, cxx_suffix +// Called by managed code to allocate an object. +TWO_ARG_DOWNCALL art_quick_alloc_object\c_suffix, artAllocObjectFromCode\cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO +// Called by managed code to allocate an object when the caller doesn't know whether it has access +// to the created type. +TWO_ARG_DOWNCALL art_quick_alloc_object_with_access_check\c_suffix, artAllocObjectFromCodeWithAccessCheck\cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO +// Called by managed code to allocate an array. +THREE_ARG_DOWNCALL art_quick_alloc_array\c_suffix, artAllocArrayFromCode\cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO +// Called by managed code to allocate an array when the caller doesn't know whether it has access +// to the created type. +THREE_ARG_DOWNCALL art_quick_alloc_array_with_access_check\c_suffix, artAllocArrayFromCodeWithAccessCheck\cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO +// Called by managed code to allocate an array in a special case for FILLED_NEW_ARRAY. +THREE_ARG_DOWNCALL art_quick_check_and_alloc_array\c_suffix, artCheckAndAllocArrayFromCode\cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO +// Called by managed code to allocate an array in a special case for FILLED_NEW_ARRAY. +THREE_ARG_DOWNCALL art_quick_check_and_alloc_array_with_access_check\c_suffix, artCheckAndAllocArrayFromCodeWithAccessCheck\cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO +.endm + +GENERATE_ALLOC_ENTRYPOINTS +GENERATE_ALLOC_ENTRYPOINTS _instrumented, Instrumented +GENERATE_ALLOC_ENTRYPOINTS _bump_pointer, BumpPointer +GENERATE_ALLOC_ENTRYPOINTS _bump_pointer_instrumented, BumpPointerInstrumented diff --git a/runtime/arch/quick_alloc_entrypoints.cc b/runtime/arch/quick_alloc_entrypoints.cc new file mode 100644 index 00000000000..192b1241e5a --- /dev/null +++ b/runtime/arch/quick_alloc_entrypoints.cc @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "entrypoints/quick/quick_entrypoints.h" +#include "gc/heap.h" + +#define GENERATE_ENTRYPOINTS(suffix) \ +extern "C" void* art_quick_alloc_array##suffix(uint32_t, void*, int32_t); \ +extern "C" void* art_quick_alloc_array_with_access_check##suffix(uint32_t, void*, int32_t); \ +extern "C" void* art_quick_alloc_object##suffix(uint32_t type_idx, void* method); \ +extern "C" void* art_quick_alloc_object_with_access_check##suffix(uint32_t type_idx, void* method); \ +extern "C" void* art_quick_check_and_alloc_array##suffix(uint32_t, void*, int32_t); \ +extern "C" void* art_quick_check_and_alloc_array_with_access_check##suffix(uint32_t, void*, int32_t); \ +extern "C" void* art_quick_alloc_array##suffix##_instrumented(uint32_t, void*, int32_t); \ +extern "C" void* art_quick_alloc_array_with_access_check##suffix##_instrumented(uint32_t, void*, int32_t); \ +extern "C" void* art_quick_alloc_object##suffix##_instrumented(uint32_t type_idx, void* method); \ +extern "C" void* art_quick_alloc_object_with_access_check##suffix##_instrumented(uint32_t type_idx, void* method); \ +extern "C" void* art_quick_check_and_alloc_array##suffix##_instrumented(uint32_t, void*, int32_t); \ +extern "C" void* art_quick_check_and_alloc_array_with_access_check##suffix##_instrumented(uint32_t, void*, int32_t); \ +void SetQuickAllocEntryPoints##suffix(QuickEntryPoints* qpoints, bool instrumented) { \ + if (instrumented) { \ + qpoints->pAllocArray = art_quick_alloc_array##suffix##_instrumented; \ + qpoints->pAllocArrayWithAccessCheck = art_quick_alloc_array_with_access_check##suffix##_instrumented; \ + qpoints->pAllocObject = art_quick_alloc_object##suffix##_instrumented; \ + qpoints->pAllocObjectWithAccessCheck = art_quick_alloc_object_with_access_check##suffix##_instrumented; \ + qpoints->pCheckAndAllocArray = art_quick_check_and_alloc_array##suffix##_instrumented; \ + qpoints->pCheckAndAllocArrayWithAccessCheck = art_quick_check_and_alloc_array_with_access_check##suffix##_instrumented; \ + } else { \ + qpoints->pAllocArray = art_quick_alloc_array##suffix; \ + qpoints->pAllocArrayWithAccessCheck = art_quick_alloc_array_with_access_check##suffix; \ + qpoints->pAllocObject = art_quick_alloc_object##suffix; \ + qpoints->pAllocObjectWithAccessCheck = art_quick_alloc_object_with_access_check##suffix; \ + qpoints->pCheckAndAllocArray = art_quick_check_and_alloc_array##suffix; \ + qpoints->pCheckAndAllocArrayWithAccessCheck = art_quick_check_and_alloc_array_with_access_check##suffix; \ + } \ +} + +namespace art { + +// Generate the entrypoint functions. +GENERATE_ENTRYPOINTS(); +GENERATE_ENTRYPOINTS(_bump_pointer); + +static bool entry_points_instrumented = false; +static gc::AllocatorType entry_points_allocator = kMovingCollector ? + gc::kAllocatorTypeBumpPointer : gc::kAllocatorTypeFreeList; + +void SetQuickAllocEntryPointsAllocator(gc::AllocatorType allocator) { + entry_points_allocator = allocator; +} + +void SetQuickAllocEntryPointsInstrumented(bool instrumented) { + entry_points_instrumented = instrumented; +} + +void ResetQuickAllocEntryPoints(QuickEntryPoints* qpoints) { + switch (entry_points_allocator) { + case gc::kAllocatorTypeFreeList: { + SetQuickAllocEntryPoints(qpoints, entry_points_instrumented); + break; + } + case gc::kAllocatorTypeBumpPointer: { + SetQuickAllocEntryPoints_bump_pointer(qpoints, entry_points_instrumented); + break; + } + default: { + LOG(FATAL) << "Unimplemented"; + } + } +} + +} // namespace art diff --git a/runtime/arch/x86/entrypoints_init_x86.cc b/runtime/arch/x86/entrypoints_init_x86.cc index 99b0dd548c4..6a67079b6f4 100644 --- a/runtime/arch/x86/entrypoints_init_x86.cc +++ b/runtime/arch/x86/entrypoints_init_x86.cc @@ -32,21 +32,6 @@ extern "C" void artInterpreterToCompiledCodeBridge(Thread* self, MethodHelper& m extern "C" void art_portable_resolution_trampoline(mirror::ArtMethod*); extern "C" void art_portable_to_interpreter_bridge(mirror::ArtMethod*); -// Alloc entrypoints. -extern "C" void* art_quick_alloc_array(uint32_t, void*, int32_t); -extern "C" void* art_quick_alloc_array_with_access_check(uint32_t, void*, int32_t); -extern "C" void* art_quick_alloc_object(uint32_t type_idx, void* method); -extern "C" void* art_quick_alloc_object_with_access_check(uint32_t type_idx, void* method); -extern "C" void* art_quick_check_and_alloc_array(uint32_t, void*, int32_t); -extern "C" void* art_quick_check_and_alloc_array_with_access_check(uint32_t, void*, int32_t); - -extern "C" void* art_quick_alloc_array_instrumented(uint32_t, void*, int32_t); -extern "C" void* art_quick_alloc_array_with_access_check_instrumented(uint32_t, void*, int32_t); -extern "C" void* art_quick_alloc_object_instrumented(uint32_t type_idx, void* method); -extern "C" void* art_quick_alloc_object_with_access_check_instrumented(uint32_t type_idx, void* method); -extern "C" void* art_quick_check_and_alloc_array_instrumented(uint32_t, void*, int32_t); -extern "C" void* art_quick_check_and_alloc_array_with_access_check_instrumented(uint32_t, void*, int32_t); - // Cast entrypoints. extern "C" uint32_t art_quick_is_assignable(const mirror::Class* klass, const mirror::Class* ref_class); @@ -125,29 +110,7 @@ extern "C" void art_quick_throw_no_such_method(int32_t method_idx); extern "C" void art_quick_throw_null_pointer_exception(); extern "C" void art_quick_throw_stack_overflow(void*); -static bool quick_alloc_entry_points_instrumented = false; - -void SetQuickAllocEntryPointsInstrumented(bool instrumented) { - quick_alloc_entry_points_instrumented = instrumented; -} - -void ResetQuickAllocEntryPoints(QuickEntryPoints* qpoints) { - if (quick_alloc_entry_points_instrumented) { - qpoints->pAllocArray = art_quick_alloc_array_instrumented; - qpoints->pAllocArrayWithAccessCheck = art_quick_alloc_array_with_access_check_instrumented; - qpoints->pAllocObject = art_quick_alloc_object_instrumented; - qpoints->pAllocObjectWithAccessCheck = art_quick_alloc_object_with_access_check_instrumented; - qpoints->pCheckAndAllocArray = art_quick_check_and_alloc_array_instrumented; - qpoints->pCheckAndAllocArrayWithAccessCheck = art_quick_check_and_alloc_array_with_access_check_instrumented; - } else { - qpoints->pAllocArray = art_quick_alloc_array; - qpoints->pAllocArrayWithAccessCheck = art_quick_alloc_array_with_access_check; - qpoints->pAllocObject = art_quick_alloc_object; - qpoints->pAllocObjectWithAccessCheck = art_quick_alloc_object_with_access_check; - qpoints->pCheckAndAllocArray = art_quick_check_and_alloc_array; - qpoints->pCheckAndAllocArrayWithAccessCheck = art_quick_check_and_alloc_array_with_access_check; - } -} +extern void ResetQuickAllocEntryPoints(QuickEntryPoints* qpoints); void InitEntryPoints(InterpreterEntryPoints* ipoints, JniEntryPoints* jpoints, PortableEntryPoints* ppoints, QuickEntryPoints* qpoints) { diff --git a/runtime/arch/x86/quick_entrypoints_x86.S b/runtime/arch/x86/quick_entrypoints_x86.S index decdb500b2f..62a8b701952 100644 --- a/runtime/arch/x86/quick_entrypoints_x86.S +++ b/runtime/arch/x86/quick_entrypoints_x86.S @@ -426,7 +426,7 @@ MACRO0(RETURN_OR_DELIVER_PENDING_EXCEPTION) DELIVER_PENDING_EXCEPTION END_MACRO -#include "arch/alloc_entrypoints.S" +#include "arch/quick_alloc_entrypoints.S" TWO_ARG_DOWNCALL art_quick_resolve_string, artResolveStringFromCode, RETURN_IF_RESULT_IS_NON_ZERO TWO_ARG_DOWNCALL art_quick_initialize_static_storage, artInitializeStaticStorageFromCode, RETURN_IF_RESULT_IS_NON_ZERO diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index cfe3bf4c0fd..500cb59bee4 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -199,9 +199,8 @@ void ClassLinker::InitFromCompiler(const std::vector& boot_class gc::Heap* heap = Runtime::Current()->GetHeap(); // The GC can't handle an object with a null class since we can't get the size of this object. heap->IncrementDisableGC(self); - SirtRef java_lang_Class( - self, down_cast( - heap->AllocNonMovableObject(self, NULL, sizeof(mirror::ClassClass)))); + SirtRef java_lang_Class(self, down_cast( + heap->AllocNonMovableObject(self, nullptr, sizeof(mirror::ClassClass)))); CHECK(java_lang_Class.get() != NULL); mirror::Class::SetClassClass(java_lang_Class.get()); java_lang_Class->SetClass(java_lang_Class.get()); @@ -239,7 +238,8 @@ void ClassLinker::InitFromCompiler(const std::vector& boot_class java_lang_String->SetStatus(mirror::Class::kStatusResolved, self); // Create storage for root classes, save away our work so far (requires descriptors). - class_roots_ = mirror::ObjectArray::Alloc(self, object_array_class.get(), kClassRootsMax); + class_roots_ = mirror::ObjectArray::Alloc(self, object_array_class.get(), + kClassRootsMax); CHECK(class_roots_ != NULL); SetClassRoot(kJavaLangClass, java_lang_Class.get()); SetClassRoot(kJavaLangObject, java_lang_Object.get()); @@ -1204,7 +1204,7 @@ mirror::DexCache* ClassLinker::AllocDexCache(Thread* self, const DexFile& dex_fi SirtRef dex_cache_class(self, GetClassRoot(kJavaLangDexCache)); SirtRef dex_cache( self, down_cast( - heap->AllocObject(self, dex_cache_class.get(), dex_cache_class->GetObjectSize()))); + heap->AllocObject(self, dex_cache_class.get(), dex_cache_class->GetObjectSize()))); if (dex_cache.get() == NULL) { return NULL; } @@ -1249,7 +1249,7 @@ mirror::Class* ClassLinker::AllocClass(Thread* self, mirror::Class* java_lang_Cl size_t class_size) { DCHECK_GE(class_size, sizeof(mirror::Class)); gc::Heap* heap = Runtime::Current()->GetHeap(); - mirror::Object* k = heap->AllocNonMovableObject(self, java_lang_Class, class_size); + mirror::Object* k = heap->AllocNonMovableObject(self, java_lang_Class, class_size); if (UNLIKELY(k == NULL)) { CHECK(self->IsExceptionPending()); // OOME. return NULL; @@ -1268,12 +1268,12 @@ mirror::Class* ClassLinker::AllocClass(Thread* self, size_t class_size) { mirror::ArtField* ClassLinker::AllocArtField(Thread* self) { return down_cast( - GetClassRoot(kJavaLangReflectArtField)->Alloc(self)); + GetClassRoot(kJavaLangReflectArtField)->AllocNonMovableObject(self)); } mirror::ArtMethod* ClassLinker::AllocArtMethod(Thread* self) { return down_cast( - GetClassRoot(kJavaLangReflectArtMethod)->Alloc(self)); + GetClassRoot(kJavaLangReflectArtMethod)->AllocNonMovableObject(self)); } mirror::ObjectArray* ClassLinker::AllocStackTraceElementArray( diff --git a/runtime/debugger.cc b/runtime/debugger.cc index cecb770d4a8..c5c1dfbae60 100644 --- a/runtime/debugger.cc +++ b/runtime/debugger.cc @@ -1119,8 +1119,7 @@ JDWP::JdwpError Dbg::CreateArrayObject(JDWP::RefTypeId array_class_id, uint32_t if (c == NULL) { return status; } - new_array = gRegistry->Add( - mirror::Array::Alloc(Thread::Current(), c, length)); + new_array = gRegistry->Add(mirror::Array::Alloc(Thread::Current(), c, length)); return JDWP::ERR_NONE; } diff --git a/runtime/entrypoints/entrypoint_utils.cc b/runtime/entrypoints/entrypoint_utils.cc index d7bbe6475a9..2806f941283 100644 --- a/runtime/entrypoints/entrypoint_utils.cc +++ b/runtime/entrypoints/entrypoint_utils.cc @@ -33,20 +33,20 @@ namespace art { -static inline bool CheckFilledNewArrayAlloc(uint32_t type_idx, mirror::ArtMethod* referrer, - int32_t component_count, Thread* self, - bool access_check, mirror::Class** klass_ptr) +static inline mirror::Class* CheckFilledNewArrayAlloc(uint32_t type_idx, mirror::ArtMethod* referrer, + int32_t component_count, Thread* self, + bool access_check) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { if (UNLIKELY(component_count < 0)) { ThrowNegativeArraySizeException(component_count); - return false; // Failure + return nullptr; // Failure } mirror::Class* klass = referrer->GetDexCacheResolvedTypes()->GetWithoutChecks(type_idx); if (UNLIKELY(klass == NULL)) { // Not in dex cache so try to resolve klass = Runtime::Current()->GetClassLinker()->ResolveType(type_idx, referrer); if (klass == NULL) { // Error DCHECK(self->IsExceptionPending()); - return false; // Failure + return nullptr; // Failure } } if (UNLIKELY(klass->IsPrimitive() && !klass->IsPrimitiveInt())) { @@ -60,40 +60,43 @@ static inline bool CheckFilledNewArrayAlloc(uint32_t type_idx, mirror::ArtMethod "Found type %s; filled-new-array not implemented for anything but \'int\'", PrettyDescriptor(klass).c_str()); } - return false; // Failure + return nullptr; // Failure } if (access_check) { mirror::Class* referrer_klass = referrer->GetDeclaringClass(); if (UNLIKELY(!referrer_klass->CanAccess(klass))) { ThrowIllegalAccessErrorClass(referrer_klass, klass); - return false; // Failure + return nullptr; // Failure } } DCHECK(klass->IsArrayClass()) << PrettyClass(klass); - *klass_ptr = klass; - return true; + return klass; } // Helper function to allocate array for FILLED_NEW_ARRAY. mirror::Array* CheckAndAllocArrayFromCode(uint32_t type_idx, mirror::ArtMethod* referrer, int32_t component_count, Thread* self, - bool access_check) { - mirror::Class* klass; - if (UNLIKELY(!CheckFilledNewArrayAlloc(type_idx, referrer, component_count, self, access_check, &klass))) { - return NULL; + bool access_check, + gc::AllocatorType allocator_type) { + mirror::Class* klass = CheckFilledNewArrayAlloc(type_idx, referrer, component_count, self, + access_check); + if (UNLIKELY(klass == nullptr)) { + return nullptr; } - return mirror::Array::Alloc(self, klass, component_count); + return mirror::Array::Alloc(self, klass, component_count, allocator_type); } // Helper function to allocate array for FILLED_NEW_ARRAY. mirror::Array* CheckAndAllocArrayFromCodeInstrumented(uint32_t type_idx, mirror::ArtMethod* referrer, int32_t component_count, Thread* self, - bool access_check) { - mirror::Class* klass; - if (UNLIKELY(!CheckFilledNewArrayAlloc(type_idx, referrer, component_count, self, access_check, &klass))) { - return NULL; + bool access_check, + gc::AllocatorType allocator_type) { + mirror::Class* klass = CheckFilledNewArrayAlloc(type_idx, referrer, component_count, self, + access_check); + if (UNLIKELY(klass == nullptr)) { + return nullptr; } - return mirror::Array::Alloc(self, klass, component_count); + return mirror::Array::Alloc(self, klass, component_count, allocator_type); } void ThrowStackOverflowError(Thread* self) { diff --git a/runtime/entrypoints/entrypoint_utils.h b/runtime/entrypoints/entrypoint_utils.h index 3b58a8da300..747dd56cd8f 100644 --- a/runtime/entrypoints/entrypoint_utils.h +++ b/runtime/entrypoints/entrypoint_utils.h @@ -27,9 +27,11 @@ #include "mirror/art_method.h" #include "mirror/array.h" #include "mirror/class-inl.h" +#include "mirror/object-inl.h" #include "mirror/throwable.h" +#include "locks.h" #include "object_utils.h" - +#include "sirt_ref.h" #include "thread.h" namespace art { @@ -40,130 +42,122 @@ namespace mirror { class Object; } // namespace mirror -static inline bool CheckObjectAlloc(uint32_t type_idx, mirror::ArtMethod* method, - Thread* self, - bool access_check, - mirror::Class** klass_ptr) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { +// TODO: Fix no thread safety analysis when GCC can handle template specialization. +template +ALWAYS_INLINE static inline mirror::Class* CheckObjectAlloc(uint32_t type_idx, + mirror::ArtMethod* method, + Thread* self) + NO_THREAD_SAFETY_ANALYSIS { mirror::Class* klass = method->GetDexCacheResolvedTypes()->GetWithoutChecks(type_idx); - Runtime* runtime = Runtime::Current(); if (UNLIKELY(klass == NULL)) { - klass = runtime->GetClassLinker()->ResolveType(type_idx, method); + klass = Runtime::Current()->GetClassLinker()->ResolveType(type_idx, method); if (klass == NULL) { DCHECK(self->IsExceptionPending()); - return false; // Failure + return nullptr; // Failure } } - if (access_check) { + if (kAccessCheck) { if (UNLIKELY(!klass->IsInstantiable())) { ThrowLocation throw_location = self->GetCurrentLocationForThrow(); self->ThrowNewException(throw_location, "Ljava/lang/InstantiationError;", PrettyDescriptor(klass).c_str()); - return false; // Failure + return nullptr; // Failure } mirror::Class* referrer = method->GetDeclaringClass(); if (UNLIKELY(!referrer->CanAccess(klass))) { ThrowIllegalAccessErrorClass(referrer, klass); - return false; // Failure + return nullptr; // Failure } } - if (!klass->IsInitialized() && - !runtime->GetClassLinker()->EnsureInitialized(klass, true, true)) { - DCHECK(self->IsExceptionPending()); - return false; // Failure + if (UNLIKELY(!klass->IsInitialized())) { + SirtRef sirt_klass(self, klass); + // The class initializer might cause a GC. + if (!Runtime::Current()->GetClassLinker()->EnsureInitialized(klass, true, true)) { + DCHECK(self->IsExceptionPending()); + return nullptr; // Failure + } + return sirt_klass.get(); } - *klass_ptr = klass; - return true; + return klass; } // Given the context of a calling Method, use its DexCache to resolve a type to a Class. If it // cannot be resolved, throw an error. If it can, use it to create an instance. // When verification/compiler hasn't been able to verify access, optionally perform an access // check. -static inline mirror::Object* AllocObjectFromCode(uint32_t type_idx, mirror::ArtMethod* method, - Thread* self, - bool access_check) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - mirror::Class* klass; - if (UNLIKELY(!CheckObjectAlloc(type_idx, method, self, access_check, &klass))) { - return NULL; - } - return klass->Alloc(self); -} - -static inline mirror::Object* AllocObjectFromCodeInstrumented(uint32_t type_idx, mirror::ArtMethod* method, - Thread* self, - bool access_check) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - mirror::Class* klass; - if (UNLIKELY(!CheckObjectAlloc(type_idx, method, self, access_check, &klass))) { - return NULL; - } - return klass->Alloc(self); -} - -static inline bool CheckArrayAlloc(uint32_t type_idx, mirror::ArtMethod* method, - int32_t component_count, - bool access_check, mirror::Class** klass_ptr) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { +// TODO: Fix NO_THREAD_SAFETY_ANALYSIS when GCC is smarter. +template +ALWAYS_INLINE static inline mirror::Object* AllocObjectFromCode(uint32_t type_idx, + mirror::ArtMethod* method, + Thread* self, + gc::AllocatorType allocator_type) + NO_THREAD_SAFETY_ANALYSIS { + mirror::Class* klass = CheckObjectAlloc(type_idx, method, self); + if (UNLIKELY(klass == nullptr)) { + return nullptr; + } + return klass->Alloc(self, allocator_type); +} + +// TODO: Fix no thread safety analysis when GCC can handle template specialization. +template +ALWAYS_INLINE static inline mirror::Class* CheckArrayAlloc(uint32_t type_idx, + mirror::ArtMethod* method, + int32_t component_count) + NO_THREAD_SAFETY_ANALYSIS { if (UNLIKELY(component_count < 0)) { ThrowNegativeArraySizeException(component_count); - return false; // Failure + return nullptr; // Failure } mirror::Class* klass = method->GetDexCacheResolvedTypes()->GetWithoutChecks(type_idx); - if (UNLIKELY(klass == NULL)) { // Not in dex cache so try to resolve + if (UNLIKELY(klass == nullptr)) { // Not in dex cache so try to resolve klass = Runtime::Current()->GetClassLinker()->ResolveType(type_idx, method); if (klass == NULL) { // Error DCHECK(Thread::Current()->IsExceptionPending()); - return false; // Failure + return nullptr; // Failure } CHECK(klass->IsArrayClass()) << PrettyClass(klass); } - if (access_check) { + if (kAccessCheck) { mirror::Class* referrer = method->GetDeclaringClass(); if (UNLIKELY(!referrer->CanAccess(klass))) { ThrowIllegalAccessErrorClass(referrer, klass); - return false; // Failure + return nullptr; // Failure } } - *klass_ptr = klass; - return true; + return klass; } // Given the context of a calling Method, use its DexCache to resolve a type to an array Class. If // it cannot be resolved, throw an error. If it can, use it to create an array. // When verification/compiler hasn't been able to verify access, optionally perform an access // check. -static inline mirror::Array* AllocArrayFromCode(uint32_t type_idx, mirror::ArtMethod* method, - int32_t component_count, - Thread* self, bool access_check) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - mirror::Class* klass; - if (UNLIKELY(!CheckArrayAlloc(type_idx, method, component_count, access_check, &klass))) { - return NULL; - } - return mirror::Array::Alloc(self, klass, component_count); -} - -static inline mirror::Array* AllocArrayFromCodeInstrumented(uint32_t type_idx, mirror::ArtMethod* method, - int32_t component_count, - Thread* self, bool access_check) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - mirror::Class* klass; - if (UNLIKELY(!CheckArrayAlloc(type_idx, method, component_count, access_check, &klass))) { - return NULL; +// TODO: Fix no thread safety analysis when GCC can handle template specialization. +template +ALWAYS_INLINE static inline mirror::Array* AllocArrayFromCode(uint32_t type_idx, + mirror::ArtMethod* method, + int32_t component_count, + Thread* self, + gc::AllocatorType allocator_type) + NO_THREAD_SAFETY_ANALYSIS { + mirror::Class* klass = CheckArrayAlloc(type_idx, method, component_count); + if (UNLIKELY(klass == nullptr)) { + return nullptr; } - return mirror::Array::Alloc(self, klass, component_count); + return mirror::Array::Alloc(self, klass, component_count, allocator_type); } extern mirror::Array* CheckAndAllocArrayFromCode(uint32_t type_idx, mirror::ArtMethod* method, - int32_t component_count, - Thread* self, bool access_check) + int32_t component_count, Thread* self, + bool access_check, + gc::AllocatorType allocator_type) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); -extern mirror::Array* CheckAndAllocArrayFromCodeInstrumented(uint32_t type_idx, mirror::ArtMethod* method, - int32_t component_count, - Thread* self, bool access_check) +extern mirror::Array* CheckAndAllocArrayFromCodeInstrumented(uint32_t type_idx, + mirror::ArtMethod* method, + int32_t component_count, Thread* self, + bool access_check, + gc::AllocatorType allocator_type) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); // Type of find field operation for fast and slow case. diff --git a/runtime/entrypoints/portable/portable_alloc_entrypoints.cc b/runtime/entrypoints/portable/portable_alloc_entrypoints.cc index 91b7353bab4..6d23efe898e 100644 --- a/runtime/entrypoints/portable/portable_alloc_entrypoints.cc +++ b/runtime/entrypoints/portable/portable_alloc_entrypoints.cc @@ -24,14 +24,14 @@ extern "C" mirror::Object* art_portable_alloc_object_from_code(uint32_t type_idx mirror::ArtMethod* referrer, Thread* thread) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - return AllocObjectFromCode(type_idx, referrer, thread, false); + return AllocObjectFromCode(type_idx, referrer, thread, gc::kAllocatorTypeFreeList); } extern "C" mirror::Object* art_portable_alloc_object_from_code_with_access_check(uint32_t type_idx, mirror::ArtMethod* referrer, Thread* thread) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - return AllocObjectFromCode(type_idx, referrer, thread, true); + return AllocObjectFromCode(type_idx, referrer, thread, gc::kAllocatorTypeFreeList); } extern "C" mirror::Object* art_portable_alloc_array_from_code(uint32_t type_idx, @@ -39,7 +39,8 @@ extern "C" mirror::Object* art_portable_alloc_array_from_code(uint32_t type_idx, uint32_t length, Thread* self) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - return AllocArrayFromCode(type_idx, referrer, length, self, false); + return AllocArrayFromCode(type_idx, referrer, length, self, + gc::kAllocatorTypeFreeList); } extern "C" mirror::Object* art_portable_alloc_array_from_code_with_access_check(uint32_t type_idx, @@ -47,7 +48,8 @@ extern "C" mirror::Object* art_portable_alloc_array_from_code_with_access_check( uint32_t length, Thread* self) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - return AllocArrayFromCode(type_idx, referrer, length, self, true); + return AllocArrayFromCode(type_idx, referrer, length, self, + gc::kAllocatorTypeFreeList); } extern "C" mirror::Object* art_portable_check_and_alloc_array_from_code(uint32_t type_idx, @@ -55,7 +57,8 @@ extern "C" mirror::Object* art_portable_check_and_alloc_array_from_code(uint32_t uint32_t length, Thread* thread) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - return CheckAndAllocArrayFromCode(type_idx, referrer, length, thread, false); + return CheckAndAllocArrayFromCode(type_idx, referrer, length, thread, false, + gc::kAllocatorTypeFreeList); } extern "C" mirror::Object* art_portable_check_and_alloc_array_from_code_with_access_check(uint32_t type_idx, @@ -63,7 +66,8 @@ extern "C" mirror::Object* art_portable_check_and_alloc_array_from_code_with_acc uint32_t length, Thread* thread) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - return CheckAndAllocArrayFromCode(type_idx, referrer, length, thread, true); + return CheckAndAllocArrayFromCode(type_idx, referrer, length, thread, true, + gc::kAllocatorTypeFreeList); } } // namespace art diff --git a/runtime/entrypoints/quick/quick_alloc_entrypoints.cc b/runtime/entrypoints/quick/quick_alloc_entrypoints.cc index 6f7b1ab19bf..b71b880939e 100644 --- a/runtime/entrypoints/quick/quick_alloc_entrypoints.cc +++ b/runtime/entrypoints/quick/quick_alloc_entrypoints.cc @@ -23,110 +23,55 @@ namespace art { -extern "C" mirror::Object* artAllocObjectFromCode(uint32_t type_idx, mirror::ArtMethod* method, - Thread* self, mirror::ArtMethod** sp) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly); - return AllocObjectFromCode(type_idx, method, self, false); +#define GENERATE_ENTRYPOINTS_FOR_ALLOCATOR_INST(suffix, suffix2, instrumented_bool, allocator_type) \ +extern "C" mirror::Object* artAllocObjectFromCode ##suffix##suffix2( \ + uint32_t type_idx, mirror::ArtMethod* method, Thread* self, mirror::ArtMethod** sp) \ + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { \ + FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly); \ + return AllocObjectFromCode(type_idx, method, self, allocator_type); \ +} \ +extern "C" mirror::Object* artAllocObjectFromCodeWithAccessCheck##suffix##suffix2( \ + uint32_t type_idx, mirror::ArtMethod* method, Thread* self, mirror::ArtMethod** sp) \ + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { \ + FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly); \ + return AllocObjectFromCode(type_idx, method, self, allocator_type); \ +} \ +extern "C" mirror::Array* artAllocArrayFromCode##suffix##suffix2( \ + uint32_t type_idx, mirror::ArtMethod* method, int32_t component_count, Thread* self, \ + mirror::ArtMethod** sp) \ + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { \ + FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly); \ + return AllocArrayFromCode(type_idx, method, component_count, self, \ + allocator_type); \ +} \ +extern "C" mirror::Array* artAllocArrayFromCodeWithAccessCheck##suffix##suffix2( \ + uint32_t type_idx, mirror::ArtMethod* method, int32_t component_count, Thread* self, \ + mirror::ArtMethod** sp) \ + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { \ + FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly); \ + return AllocArrayFromCode(type_idx, method, component_count, self, \ + allocator_type); \ +} \ +extern "C" mirror::Array* artCheckAndAllocArrayFromCode##suffix##suffix2( \ + uint32_t type_idx, mirror::ArtMethod* method, int32_t component_count, Thread* self, \ + mirror::ArtMethod** sp) \ + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { \ + FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly); \ + return CheckAndAllocArrayFromCode(type_idx, method, component_count, self, false, allocator_type); \ +} \ +extern "C" mirror::Array* artCheckAndAllocArrayFromCodeWithAccessCheck##suffix##suffix2( \ + uint32_t type_idx, mirror::ArtMethod* method, int32_t component_count, Thread* self, \ + mirror::ArtMethod** sp) \ + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { \ + FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly); \ + return CheckAndAllocArrayFromCode(type_idx, method, component_count, self, true, allocator_type); \ } -extern "C" mirror::Object* artAllocObjectFromCodeWithAccessCheck(uint32_t type_idx, - mirror::ArtMethod* method, - Thread* self, - mirror::ArtMethod** sp) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly); - return AllocObjectFromCode(type_idx, method, self, true); -} - -extern "C" mirror::Array* artAllocArrayFromCode(uint32_t type_idx, mirror::ArtMethod* method, - int32_t component_count, Thread* self, - mirror::ArtMethod** sp) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly); - return AllocArrayFromCode(type_idx, method, component_count, self, false); -} - -extern "C" mirror::Array* artAllocArrayFromCodeWithAccessCheck(uint32_t type_idx, - mirror::ArtMethod* method, - int32_t component_count, - Thread* self, - mirror::ArtMethod** sp) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly); - return AllocArrayFromCode(type_idx, method, component_count, self, true); -} - -extern "C" mirror::Array* artCheckAndAllocArrayFromCode(uint32_t type_idx, - mirror::ArtMethod* method, - int32_t component_count, Thread* self, - mirror::ArtMethod** sp) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly); - return CheckAndAllocArrayFromCode(type_idx, method, component_count, self, false); -} - -extern "C" mirror::Array* artCheckAndAllocArrayFromCodeWithAccessCheck(uint32_t type_idx, - mirror::ArtMethod* method, - int32_t component_count, - Thread* self, - mirror::ArtMethod** sp) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly); - return CheckAndAllocArrayFromCode(type_idx, method, component_count, self, true); -} - -extern "C" mirror::Object* artAllocObjectFromCodeInstrumented(uint32_t type_idx, mirror::ArtMethod* method, - Thread* self, mirror::ArtMethod** sp) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly); - return AllocObjectFromCodeInstrumented(type_idx, method, self, false); -} - -extern "C" mirror::Object* artAllocObjectFromCodeWithAccessCheckInstrumented(uint32_t type_idx, - mirror::ArtMethod* method, - Thread* self, - mirror::ArtMethod** sp) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly); - return AllocObjectFromCodeInstrumented(type_idx, method, self, true); -} - -extern "C" mirror::Array* artAllocArrayFromCodeInstrumented(uint32_t type_idx, mirror::ArtMethod* method, - int32_t component_count, Thread* self, - mirror::ArtMethod** sp) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly); - return AllocArrayFromCodeInstrumented(type_idx, method, component_count, self, false); -} +#define GENERATE_ENTRYPOINTS_FOR_ALLOCATOR(suffix, allocator_type) \ + GENERATE_ENTRYPOINTS_FOR_ALLOCATOR_INST(suffix, Instrumented, true, allocator_type) \ + GENERATE_ENTRYPOINTS_FOR_ALLOCATOR_INST(suffix, , false, allocator_type) -extern "C" mirror::Array* artAllocArrayFromCodeWithAccessCheckInstrumented(uint32_t type_idx, - mirror::ArtMethod* method, - int32_t component_count, - Thread* self, - mirror::ArtMethod** sp) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly); - return AllocArrayFromCodeInstrumented(type_idx, method, component_count, self, true); -} - -extern "C" mirror::Array* artCheckAndAllocArrayFromCodeInstrumented(uint32_t type_idx, - mirror::ArtMethod* method, - int32_t component_count, Thread* self, - mirror::ArtMethod** sp) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly); - return CheckAndAllocArrayFromCodeInstrumented(type_idx, method, component_count, self, false); -} - -extern "C" mirror::Array* artCheckAndAllocArrayFromCodeWithAccessCheckInstrumented(uint32_t type_idx, - mirror::ArtMethod* method, - int32_t component_count, - Thread* self, - mirror::ArtMethod** sp) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly); - return CheckAndAllocArrayFromCodeInstrumented(type_idx, method, component_count, self, true); -} +GENERATE_ENTRYPOINTS_FOR_ALLOCATOR(, gc::kAllocatorTypeFreeList) +GENERATE_ENTRYPOINTS_FOR_ALLOCATOR(BumpPointer, gc::kAllocatorTypeBumpPointer) } // namespace art diff --git a/runtime/gc/heap-inl.h b/runtime/gc/heap-inl.h index e6829e28045..fcc07a0224e 100644 --- a/runtime/gc/heap-inl.h +++ b/runtime/gc/heap-inl.h @@ -32,152 +32,126 @@ namespace art { namespace gc { -inline mirror::Object* Heap::AllocNonMovableObjectUninstrumented(Thread* self, mirror::Class* c, - size_t byte_count) { +template +inline mirror::Object* Heap::AllocObjectWithAllocator(Thread* self, mirror::Class* c, + size_t byte_count, AllocatorType allocator) { DebugCheckPreconditionsForAllocObject(c, byte_count); + // Since allocation can cause a GC which will need to SuspendAll, make sure all allocations are + // done in the runnable state where suspension is expected. + DCHECK_EQ(self->GetState(), kRunnable); + self->AssertThreadSuspensionIsAllowable(); mirror::Object* obj; size_t bytes_allocated; AllocationTimer alloc_timer(this, &obj); - bool large_object_allocation = TryAllocLargeObjectUninstrumented(self, c, byte_count, - &obj, &bytes_allocated); - if (LIKELY(!large_object_allocation)) { - // Non-large object allocation. - if (!kUseRosAlloc) { - DCHECK(non_moving_space_->IsDlMallocSpace()); - obj = AllocateUninstrumented(self, reinterpret_cast(non_moving_space_), - byte_count, &bytes_allocated); + if (UNLIKELY(ShouldAllocLargeObject(c, byte_count))) { + obj = TryToAllocate(self, kAllocatorTypeLOS, byte_count, false, + &bytes_allocated); + allocator = kAllocatorTypeLOS; + } else { + obj = TryToAllocate(self, allocator, byte_count, false, &bytes_allocated); + } + + if (UNLIKELY(obj == nullptr)) { + SirtRef sirt_c(self, c); + obj = AllocateInternalWithGc(self, allocator, byte_count, &bytes_allocated); + if (obj == nullptr) { + return nullptr; } else { - DCHECK(non_moving_space_->IsRosAllocSpace()); - obj = AllocateUninstrumented(self, reinterpret_cast(non_moving_space_), - byte_count, &bytes_allocated); + c = sirt_c.get(); } - // Ensure that we did not allocate into a zygote space. - DCHECK(obj == NULL || !have_zygote_space_ || !FindSpaceFromObject(obj, false)->IsZygoteSpace()); } - if (LIKELY(obj != NULL)) { - obj->SetClass(c); - // Record allocation after since we want to use the atomic add for the atomic fence to guard - // the SetClass since we do not want the class to appear NULL in another thread. - size_t new_num_bytes_allocated = RecordAllocationUninstrumented(bytes_allocated, obj); - DCHECK(!Dbg::IsAllocTrackingEnabled()); - CheckConcurrentGC(self, new_num_bytes_allocated, obj); - if (kDesiredHeapVerification > kNoHeapVerification) { - VerifyObject(obj); + obj->SetClass(c); + // TODO: Set array length here. + DCHECK_GT(bytes_allocated, 0u); + const size_t new_num_bytes_allocated = + static_cast(num_bytes_allocated_.fetch_add(bytes_allocated)) + bytes_allocated; + // TODO: Deprecate. + if (kInstrumented) { + if (Runtime::Current()->HasStatsEnabled()) { + RuntimeStats* thread_stats = self->GetStats(); + ++thread_stats->allocated_objects; + thread_stats->allocated_bytes += bytes_allocated; + RuntimeStats* global_stats = Runtime::Current()->GetStats(); + ++global_stats->allocated_objects; + global_stats->allocated_bytes += bytes_allocated; } } else { - ThrowOutOfMemoryError(self, byte_count, large_object_allocation); + DCHECK(!Runtime::Current()->HasStatsEnabled()); } - if (kIsDebugBuild) { - self->VerifyStack(); - } - return obj; -} - -inline mirror::Object* Heap::AllocMovableObjectUninstrumented(Thread* self, mirror::Class* c, - size_t byte_count) { - DebugCheckPreconditionsForAllocObject(c, byte_count); - mirror::Object* obj; - AllocationTimer alloc_timer(this, &obj); - byte_count = (byte_count + 7) & ~7; - if (UNLIKELY(IsOutOfMemoryOnAllocation(byte_count, false))) { - CollectGarbageInternal(collector::kGcTypeFull, kGcCauseForAlloc, false); - if (UNLIKELY(IsOutOfMemoryOnAllocation(byte_count, true))) { - CollectGarbageInternal(collector::kGcTypeFull, kGcCauseForAlloc, true); + if (AllocatorHasAllocationStack(allocator)) { + // This is safe to do since the GC will never free objects which are neither in the allocation + // stack or the live bitmap. + while (!allocation_stack_->AtomicPushBack(obj)) { + CollectGarbageInternal(collector::kGcTypeSticky, kGcCauseForAlloc, false); } } - obj = bump_pointer_space_->AllocNonvirtual(byte_count); - if (LIKELY(obj != NULL)) { - obj->SetClass(c); - DCHECK(!obj->IsClass()); - // Record allocation after since we want to use the atomic add for the atomic fence to guard - // the SetClass since we do not want the class to appear NULL in another thread. - num_bytes_allocated_.fetch_add(byte_count); - DCHECK(!Dbg::IsAllocTrackingEnabled()); - if (kDesiredHeapVerification > kNoHeapVerification) { - VerifyObject(obj); + if (kInstrumented) { + if (Dbg::IsAllocTrackingEnabled()) { + Dbg::RecordAllocation(c, bytes_allocated); } } else { - ThrowOutOfMemoryError(self, byte_count, false); + DCHECK(!Dbg::IsAllocTrackingEnabled()); + } + if (AllocatorHasConcurrentGC(allocator)) { + CheckConcurrentGC(self, new_num_bytes_allocated, obj); } if (kIsDebugBuild) { + if (kDesiredHeapVerification > kNoHeapVerification) { + VerifyObject(obj); + } self->VerifyStack(); } return obj; } -inline size_t Heap::RecordAllocationUninstrumented(size_t size, mirror::Object* obj) { - DCHECK(obj != NULL); - DCHECK_GT(size, 0u); - size_t old_num_bytes_allocated = static_cast(num_bytes_allocated_.fetch_add(size)); - - DCHECK(!Runtime::Current()->HasStatsEnabled()); - - // This is safe to do since the GC will never free objects which are neither in the allocation - // stack or the live bitmap. - while (!allocation_stack_->AtomicPushBack(obj)) { - CollectGarbageInternal(collector::kGcTypeSticky, kGcCauseForAlloc, false); - } - - return old_num_bytes_allocated + size; -} - -inline mirror::Object* Heap::TryToAllocateUninstrumented(Thread* self, space::AllocSpace* space, size_t alloc_size, - bool grow, size_t* bytes_allocated) { +template +inline mirror::Object* Heap::TryToAllocate(Thread* self, AllocatorType allocator_type, + size_t alloc_size, bool grow, + size_t* bytes_allocated) { if (UNLIKELY(IsOutOfMemoryOnAllocation(alloc_size, grow))) { - return NULL; + return nullptr; } - DCHECK(!running_on_valgrind_); - return space->Alloc(self, alloc_size, bytes_allocated); -} - -// DlMallocSpace-specific version. -inline mirror::Object* Heap::TryToAllocateUninstrumented(Thread* self, space::DlMallocSpace* space, size_t alloc_size, - bool grow, size_t* bytes_allocated) { - if (UNLIKELY(IsOutOfMemoryOnAllocation(alloc_size, grow))) { - return NULL; - } - DCHECK(!running_on_valgrind_); - return space->AllocNonvirtual(self, alloc_size, bytes_allocated); -} - -// RosAllocSpace-specific version. -inline mirror::Object* Heap::TryToAllocateUninstrumented(Thread* self, space::RosAllocSpace* space, size_t alloc_size, - bool grow, size_t* bytes_allocated) { - if (UNLIKELY(IsOutOfMemoryOnAllocation(alloc_size, grow))) { - return NULL; - } - DCHECK(!running_on_valgrind_); - return space->AllocNonvirtual(self, alloc_size, bytes_allocated); -} - -template -inline mirror::Object* Heap::AllocateUninstrumented(Thread* self, T* space, size_t alloc_size, - size_t* bytes_allocated) { - // Since allocation can cause a GC which will need to SuspendAll, make sure all allocations are - // done in the runnable state where suspension is expected. - DCHECK_EQ(self->GetState(), kRunnable); - self->AssertThreadSuspensionIsAllowable(); - - mirror::Object* ptr = TryToAllocateUninstrumented(self, space, alloc_size, false, bytes_allocated); - if (LIKELY(ptr != NULL)) { - return ptr; + if (kInstrumented) { + if (UNLIKELY(running_on_valgrind_ && allocator_type == kAllocatorTypeFreeList)) { + return non_moving_space_->Alloc(self, alloc_size, bytes_allocated); + } } - return AllocateInternalWithGc(self, space, alloc_size, bytes_allocated); -} - -inline bool Heap::TryAllocLargeObjectUninstrumented(Thread* self, mirror::Class* c, size_t byte_count, - mirror::Object** obj_ptr, size_t* bytes_allocated) { - bool large_object_allocation = ShouldAllocLargeObject(c, byte_count); - if (UNLIKELY(large_object_allocation)) { - mirror::Object* obj = AllocateUninstrumented(self, large_object_space_, byte_count, bytes_allocated); - // Make sure that our large object didn't get placed anywhere within the space interval or else - // it breaks the immune range. - DCHECK(obj == NULL || - reinterpret_cast(obj) < continuous_spaces_.front()->Begin() || - reinterpret_cast(obj) >= continuous_spaces_.back()->End()); - *obj_ptr = obj; + mirror::Object* ret; + switch (allocator_type) { + case kAllocatorTypeBumpPointer: { + DCHECK(bump_pointer_space_ != nullptr); + alloc_size = RoundUp(alloc_size, space::BumpPointerSpace::kAlignment); + ret = bump_pointer_space_->AllocNonvirtual(alloc_size); + if (LIKELY(ret != nullptr)) { + *bytes_allocated = alloc_size; + } + break; + } + case kAllocatorTypeFreeList: { + if (kUseRosAlloc) { + ret = reinterpret_cast(non_moving_space_)->AllocNonvirtual( + self, alloc_size, bytes_allocated); + } else { + ret = reinterpret_cast(non_moving_space_)->AllocNonvirtual( + self, alloc_size, bytes_allocated); + } + break; + } + case kAllocatorTypeLOS: { + ret = large_object_space_->Alloc(self, alloc_size, bytes_allocated); + // Make sure that our large object didn't get placed anywhere within the space interval or + // else it breaks the immune range. + DCHECK(ret == nullptr || + reinterpret_cast(ret) < continuous_spaces_.front()->Begin() || + reinterpret_cast(ret) >= continuous_spaces_.back()->End()); + break; + } + default: { + LOG(FATAL) << "Invalid allocator type"; + ret = nullptr; + } } - return large_object_allocation; + return ret; } inline void Heap::DebugCheckPreconditionsForAllocObject(mirror::Class* c, size_t byte_count) { @@ -198,14 +172,14 @@ inline Heap::AllocationTimer::~AllocationTimer() { if (kMeasureAllocationTime) { mirror::Object* allocated_obj = *allocated_obj_ptr_; // Only if the allocation succeeded, record the time. - if (allocated_obj != NULL) { + if (allocated_obj != nullptr) { uint64_t allocation_end_time = NanoTime() / kTimeAdjust; heap_->total_allocation_time_.fetch_add(allocation_end_time - allocation_start_time_); } } }; -inline bool Heap::ShouldAllocLargeObject(mirror::Class* c, size_t byte_count) { +inline bool Heap::ShouldAllocLargeObject(mirror::Class* c, size_t byte_count) const { // We need to have a zygote space or else our newly allocated large object can end up in the // Zygote resulting in it being prematurely freed. // We can only do this for primitive objects since large objects will not be within the card table @@ -230,7 +204,8 @@ inline bool Heap::IsOutOfMemoryOnAllocation(size_t alloc_size, bool grow) { return false; } -inline void Heap::CheckConcurrentGC(Thread* self, size_t new_num_bytes_allocated, mirror::Object* obj) { +inline void Heap::CheckConcurrentGC(Thread* self, size_t new_num_bytes_allocated, + mirror::Object* obj) { if (UNLIKELY(new_num_bytes_allocated >= concurrent_start_bytes_)) { // The SirtRef is necessary since the calls in RequestConcurrentGC are a safepoint. SirtRef ref(self, obj); diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc index 763bfe9cbd1..c31e3e982b2 100644 --- a/runtime/gc/heap.cc +++ b/runtime/gc/heap.cc @@ -74,7 +74,7 @@ Heap::Heap(size_t initial_size, size_t growth_limit, size_t min_free, size_t max bool concurrent_gc, size_t parallel_gc_threads, size_t conc_gc_threads, bool low_memory_mode, size_t long_pause_log_threshold, size_t long_gc_log_threshold, bool ignore_max_footprint) - : non_moving_space_(NULL), + : non_moving_space_(nullptr), concurrent_gc_(!kMovingCollector && concurrent_gc), parallel_gc_threads_(parallel_gc_threads), conc_gc_threads_(conc_gc_threads), @@ -128,6 +128,8 @@ Heap::Heap(size_t initial_size, size_t growth_limit, size_t min_free, size_t max */ max_allocation_stack_size_(kGCALotMode ? kGcAlotInterval : (kDesiredHeapVerification > kVerifyAllFast) ? KB : MB), + current_allocator_(kMovingCollector ? kAllocatorTypeBumpPointer : kAllocatorTypeFreeList), + current_non_moving_allocator_(kAllocatorTypeFreeList), bump_pointer_space_(nullptr), temp_space_(nullptr), reference_referent_offset_(0), @@ -256,9 +258,13 @@ Heap::Heap(size_t initial_size, size_t growth_limit, size_t min_free, size_t max garbage_collectors_.push_back(new collector::PartialMarkSweep(this, concurrent)); garbage_collectors_.push_back(new collector::StickyMarkSweep(this, concurrent)); } + gc_plan_.push_back(collector::kGcTypeSticky); + gc_plan_.push_back(collector::kGcTypePartial); + gc_plan_.push_back(collector::kGcTypeFull); } else { semi_space_collector_ = new collector::SemiSpace(this); garbage_collectors_.push_back(semi_space_collector_); + gc_plan_.push_back(collector::kGcTypeFull); } if (running_on_valgrind_) { @@ -779,106 +785,6 @@ void Heap::ThrowOutOfMemoryError(Thread* self, size_t byte_count, bool large_obj self->ThrowOutOfMemoryError(oss.str().c_str()); } -inline bool Heap::TryAllocLargeObjectInstrumented(Thread* self, mirror::Class* c, size_t byte_count, - mirror::Object** obj_ptr, size_t* bytes_allocated) { - bool large_object_allocation = ShouldAllocLargeObject(c, byte_count); - if (UNLIKELY(large_object_allocation)) { - mirror::Object* obj = AllocateInstrumented(self, large_object_space_, byte_count, bytes_allocated); - // Make sure that our large object didn't get placed anywhere within the space interval or else - // it breaks the immune range. - DCHECK(obj == nullptr || - reinterpret_cast(obj) < continuous_spaces_.front()->Begin() || - reinterpret_cast(obj) >= continuous_spaces_.back()->End()); - *obj_ptr = obj; - } - return large_object_allocation; -} - -mirror::Object* Heap::AllocMovableObjectInstrumented(Thread* self, mirror::Class* c, - size_t byte_count) { - DebugCheckPreconditionsForAllocObject(c, byte_count); - mirror::Object* obj; - AllocationTimer alloc_timer(this, &obj); - byte_count = RoundUp(byte_count, 8); - if (UNLIKELY(IsOutOfMemoryOnAllocation(byte_count, false))) { - CollectGarbageInternal(collector::kGcTypeFull, kGcCauseForAlloc, false); - if (UNLIKELY(IsOutOfMemoryOnAllocation(byte_count, true))) { - CollectGarbageInternal(collector::kGcTypeFull, kGcCauseForAlloc, true); - } - } - obj = bump_pointer_space_->AllocNonvirtual(byte_count); - if (LIKELY(obj != NULL)) { - obj->SetClass(c); - DCHECK(!obj->IsClass()); - // Record allocation after since we want to use the atomic add for the atomic fence to guard - // the SetClass since we do not want the class to appear NULL in another thread. - num_bytes_allocated_.fetch_add(byte_count); - if (Runtime::Current()->HasStatsEnabled()) { - RuntimeStats* thread_stats = Thread::Current()->GetStats(); - ++thread_stats->allocated_objects; - thread_stats->allocated_bytes += byte_count; - RuntimeStats* global_stats = Runtime::Current()->GetStats(); - ++global_stats->allocated_objects; - global_stats->allocated_bytes += byte_count; - } - if (Dbg::IsAllocTrackingEnabled()) { - Dbg::RecordAllocation(c, byte_count); - } - if (kDesiredHeapVerification > kNoHeapVerification) { - VerifyObject(obj); - } - } else { - ThrowOutOfMemoryError(self, byte_count, false); - } - if (kIsDebugBuild) { - self->VerifyStack(); - } - return obj; -} - -mirror::Object* Heap::AllocNonMovableObjectInstrumented(Thread* self, mirror::Class* c, - size_t byte_count) { - DebugCheckPreconditionsForAllocObject(c, byte_count); - mirror::Object* obj; - size_t bytes_allocated; - AllocationTimer alloc_timer(this, &obj); - bool large_object_allocation = TryAllocLargeObjectInstrumented(self, c, byte_count, &obj, - &bytes_allocated); - if (LIKELY(!large_object_allocation)) { - // Non-large object allocation. - if (!kUseRosAlloc) { - DCHECK(non_moving_space_->IsDlMallocSpace()); - obj = AllocateInstrumented(self, reinterpret_cast(non_moving_space_), - byte_count, &bytes_allocated); - } else { - DCHECK(non_moving_space_->IsRosAllocSpace()); - obj = AllocateInstrumented(self, reinterpret_cast(non_moving_space_), - byte_count, &bytes_allocated); - } - // Ensure that we did not allocate into a zygote space. - DCHECK(obj == NULL || !have_zygote_space_ || !FindSpaceFromObject(obj, false)->IsZygoteSpace()); - } - if (LIKELY(obj != NULL)) { - obj->SetClass(c); - // Record allocation after since we want to use the atomic add for the atomic fence to guard - // the SetClass since we do not want the class to appear NULL in another thread. - size_t new_num_bytes_allocated = RecordAllocationInstrumented(bytes_allocated, obj); - if (Dbg::IsAllocTrackingEnabled()) { - Dbg::RecordAllocation(c, byte_count); - } - CheckConcurrentGC(self, new_num_bytes_allocated, obj); - if (kDesiredHeapVerification > kNoHeapVerification) { - VerifyObject(obj); - } - } else { - ThrowOutOfMemoryError(self, byte_count, large_object_allocation); - } - if (kIsDebugBuild) { - self->VerifyStack(); - } - return obj; -} - void Heap::Trim() { uint64_t start_ns = NanoTime(); // Trim the managed spaces. @@ -1059,31 +965,6 @@ void Heap::VerifyHeap() { GetLiveBitmap()->Walk(Heap::VerificationCallback, this); } -inline size_t Heap::RecordAllocationInstrumented(size_t size, mirror::Object* obj) { - DCHECK(obj != NULL); - DCHECK_GT(size, 0u); - size_t old_num_bytes_allocated = static_cast(num_bytes_allocated_.fetch_add(size)); - - if (Runtime::Current()->HasStatsEnabled()) { - RuntimeStats* thread_stats = Thread::Current()->GetStats(); - ++thread_stats->allocated_objects; - thread_stats->allocated_bytes += size; - - // TODO: Update these atomically. - RuntimeStats* global_stats = Runtime::Current()->GetStats(); - ++global_stats->allocated_objects; - global_stats->allocated_bytes += size; - } - - // This is safe to do since the GC will never free objects which are neither in the allocation - // stack or the live bitmap. - while (!allocation_stack_->AtomicPushBack(obj)) { - CollectGarbageInternal(collector::kGcTypeSticky, kGcCauseForAlloc, false); - } - - return old_num_bytes_allocated + size; -} - void Heap::RecordFree(size_t freed_objects, size_t freed_bytes) { DCHECK_LE(freed_bytes, static_cast(num_bytes_allocated_)); num_bytes_allocated_.fetch_sub(freed_bytes); @@ -1100,125 +981,50 @@ void Heap::RecordFree(size_t freed_objects, size_t freed_bytes) { } } -inline mirror::Object* Heap::TryToAllocateInstrumented(Thread* self, space::AllocSpace* space, size_t alloc_size, - bool grow, size_t* bytes_allocated) { - if (UNLIKELY(IsOutOfMemoryOnAllocation(alloc_size, grow))) { - return NULL; - } - return space->Alloc(self, alloc_size, bytes_allocated); -} - -// DlMallocSpace-specific version. -inline mirror::Object* Heap::TryToAllocateInstrumented(Thread* self, space::DlMallocSpace* space, size_t alloc_size, - bool grow, size_t* bytes_allocated) { - if (UNLIKELY(IsOutOfMemoryOnAllocation(alloc_size, grow))) { - return nullptr; - } - if (LIKELY(!running_on_valgrind_)) { - return space->AllocNonvirtual(self, alloc_size, bytes_allocated); - } else { - return space->Alloc(self, alloc_size, bytes_allocated); - } -} - -// RosAllocSpace-specific version. -inline mirror::Object* Heap::TryToAllocateInstrumented(Thread* self, space::RosAllocSpace* space, size_t alloc_size, - bool grow, size_t* bytes_allocated) { - if (UNLIKELY(IsOutOfMemoryOnAllocation(alloc_size, grow))) { - return NULL; - } - if (LIKELY(!running_on_valgrind_)) { - return space->AllocNonvirtual(self, alloc_size, bytes_allocated); - } else { - return space->Alloc(self, alloc_size, bytes_allocated); - } -} - -template -inline mirror::Object* Heap::AllocateInstrumented(Thread* self, T* space, size_t alloc_size, - size_t* bytes_allocated) { - // Since allocation can cause a GC which will need to SuspendAll, make sure all allocations are - // done in the runnable state where suspension is expected. - DCHECK_EQ(self->GetState(), kRunnable); - self->AssertThreadSuspensionIsAllowable(); - - mirror::Object* ptr = TryToAllocateInstrumented(self, space, alloc_size, false, bytes_allocated); - if (LIKELY(ptr != NULL)) { - return ptr; - } - return AllocateInternalWithGc(self, space, alloc_size, bytes_allocated); -} - -mirror::Object* Heap::AllocateInternalWithGc(Thread* self, space::AllocSpace* space, +mirror::Object* Heap::AllocateInternalWithGc(Thread* self, AllocatorType allocator, size_t alloc_size, size_t* bytes_allocated) { - mirror::Object* ptr; - + mirror::Object* ptr = nullptr; // The allocation failed. If the GC is running, block until it completes, and then retry the // allocation. collector::GcType last_gc = WaitForGcToComplete(self); if (last_gc != collector::kGcTypeNone) { // A GC was in progress and we blocked, retry allocation now that memory has been freed. - ptr = TryToAllocateInstrumented(self, space, alloc_size, false, bytes_allocated); - if (ptr != NULL) { - return ptr; - } + ptr = TryToAllocate(self, allocator, alloc_size, false, bytes_allocated); } // Loop through our different Gc types and try to Gc until we get enough free memory. - for (size_t i = static_cast(last_gc) + 1; - i < static_cast(collector::kGcTypeMax); ++i) { - bool run_gc = false; - collector::GcType gc_type = static_cast(i); - switch (gc_type) { - case collector::kGcTypeSticky: { - const size_t alloc_space_size = non_moving_space_->Size(); - run_gc = alloc_space_size > min_alloc_space_size_for_sticky_gc_ && - non_moving_space_->Capacity() - alloc_space_size >= - min_remaining_space_for_sticky_gc_; - break; - } - case collector::kGcTypePartial: - run_gc = have_zygote_space_; - break; - case collector::kGcTypeFull: - run_gc = true; - break; - default: - LOG(FATAL) << "Invalid GC type"; - } - - if (run_gc) { - // If we actually ran a different type of Gc than requested, we can skip the index forwards. - collector::GcType gc_type_ran = CollectGarbageInternal(gc_type, kGcCauseForAlloc, false); - DCHECK_GE(static_cast(gc_type_ran), i); - i = static_cast(gc_type_ran); - + for (collector::GcType gc_type : gc_plan_) { + if (ptr != nullptr) { + break; + } + // Attempt to run the collector, if we succeed, re-try the allocation. + if (CollectGarbageInternal(gc_type, kGcCauseForAlloc, false) != collector::kGcTypeNone) { // Did we free sufficient memory for the allocation to succeed? - ptr = TryToAllocateInstrumented(self, space, alloc_size, false, bytes_allocated); - if (ptr != NULL) { - return ptr; - } + ptr = TryToAllocate(self, allocator, alloc_size, false, bytes_allocated); } } - // Allocations have failed after GCs; this is an exceptional state. - // Try harder, growing the heap if necessary. - ptr = TryToAllocateInstrumented(self, space, alloc_size, true, bytes_allocated); - if (ptr != NULL) { - return ptr; + if (ptr == nullptr) { + // Try harder, growing the heap if necessary. + ptr = TryToAllocate(self, allocator, alloc_size, true, bytes_allocated); } - - // Most allocations should have succeeded by now, so the heap is really full, really fragmented, - // or the requested size is really big. Do another GC, collecting SoftReferences this time. The - // VM spec requires that all SoftReferences have been collected and cleared before throwing OOME. - - // TODO: Run finalization, but this can cause more allocations to occur. - VLOG(gc) << "Forcing collection of SoftReferences for " << PrettySize(alloc_size) - << " allocation"; - - // We don't need a WaitForGcToComplete here either. - CollectGarbageInternal(collector::kGcTypeFull, kGcCauseForAlloc, true); - return TryToAllocateInstrumented(self, space, alloc_size, true, bytes_allocated); + if (ptr == nullptr) { + // Most allocations should have succeeded by now, so the heap is really full, really fragmented, + // or the requested size is really big. Do another GC, collecting SoftReferences this time. The + // VM spec requires that all SoftReferences have been collected and cleared before throwing + // OOME. + VLOG(gc) << "Forcing collection of SoftReferences for " << PrettySize(alloc_size) + << " allocation"; + // TODO: Run finalization, but this may cause more allocations to occur. + // We don't need a WaitForGcToComplete here either. + DCHECK(!gc_plan_.empty()); + CollectGarbageInternal(gc_plan_.back(), kGcCauseForAlloc, true); + ptr = TryToAllocate(self, allocator, alloc_size, true, bytes_allocated); + if (ptr == nullptr) { + ThrowOutOfMemoryError(self, alloc_size, false); + } + } + return ptr; } void Heap::SetTargetHeapUtilization(float target) { @@ -1493,6 +1299,27 @@ collector::GcType Heap::CollectGarbageInternal(collector::GcType gc_type, GcCaus bool clear_soft_references) { Thread* self = Thread::Current(); Runtime* runtime = Runtime::Current(); + // If the heap can't run the GC, silently fail and return that no GC was run. + switch (gc_type) { + case collector::kGcTypeSticky: { + const size_t alloc_space_size = non_moving_space_->Size(); + if (alloc_space_size < min_alloc_space_size_for_sticky_gc_ || + non_moving_space_->Capacity() - alloc_space_size < min_remaining_space_for_sticky_gc_) { + return collector::kGcTypeNone; + } + break; + } + case collector::kGcTypePartial: { + if (!have_zygote_space_) { + return collector::kGcTypeNone; + } + break; + } + default: { + // Other GC types don't have any special cases which makes them not runnable. The main case + // here is full GC. + } + } ScopedThreadStateChange tsc(self, kWaitingPerformingGc); Locks::mutator_lock_->AssertNotHeld(self); if (self->IsHandlingStackOverflow()) { @@ -1512,12 +1339,10 @@ collector::GcType Heap::CollectGarbageInternal(collector::GcType gc_type, GcCaus } is_gc_running_ = true; } - if (gc_cause == kGcCauseForAlloc && runtime->HasStatsEnabled()) { ++runtime->GetStats()->gc_for_alloc_count; ++self->GetStats()->gc_for_alloc_count; } - uint64_t gc_start_time_ns = NanoTime(); uint64_t gc_start_size = GetBytesAllocated(); // Approximate allocation rate in bytes / second. @@ -1528,11 +1353,6 @@ collector::GcType Heap::CollectGarbageInternal(collector::GcType gc_type, GcCaus VLOG(heap) << "Allocation rate: " << PrettySize(allocation_rate_) << "/s"; } - if (gc_type == collector::kGcTypeSticky && - non_moving_space_->Size() < min_alloc_space_size_for_sticky_gc_) { - gc_type = collector::kGcTypePartial; - } - DCHECK_LT(gc_type, collector::kGcTypeMax); DCHECK_NE(gc_type, collector::kGcTypeNone); @@ -2347,6 +2167,9 @@ void Heap::RegisterNativeAllocation(JNIEnv* env, int bytes) { // Total number of native bytes allocated. native_bytes_allocated_.fetch_add(bytes); if (static_cast(native_bytes_allocated_) > native_footprint_gc_watermark_) { + collector::GcType gc_type = have_zygote_space_ ? collector::kGcTypePartial : + collector::kGcTypeFull; + // The second watermark is higher than the gc watermark. If you hit this it means you are // allocating native objects faster than the GC can keep up with. if (static_cast(native_bytes_allocated_) > native_footprint_limit_) { @@ -2357,7 +2180,7 @@ void Heap::RegisterNativeAllocation(JNIEnv* env, int bytes) { } // If we still are over the watermark, attempt a GC for alloc and run finalizers. if (static_cast(native_bytes_allocated_) > native_footprint_limit_) { - CollectGarbageInternal(collector::kGcTypePartial, kGcCauseForAlloc, false); + CollectGarbageInternal(gc_type, kGcCauseForAlloc, false); RunFinalization(env); native_need_to_run_finalization_ = false; CHECK(!env->ExceptionCheck()); @@ -2369,7 +2192,7 @@ void Heap::RegisterNativeAllocation(JNIEnv* env, int bytes) { if (concurrent_gc_) { RequestConcurrentGC(self); } else { - CollectGarbageInternal(collector::kGcTypePartial, kGcCauseForAlloc, false); + CollectGarbageInternal(gc_type, kGcCauseForAlloc, false); } } } diff --git a/runtime/gc/heap.h b/runtime/gc/heap.h index 3da3943aa7b..5a0372a295c 100644 --- a/runtime/gc/heap.h +++ b/runtime/gc/heap.h @@ -87,6 +87,13 @@ class AgeCardVisitor { } }; +// Different types of allocators. +enum AllocatorType { + kAllocatorTypeBumpPointer, + kAllocatorTypeFreeList, // ROSAlloc / dlmalloc + kAllocatorTypeLOS, // Large object space. +}; + // What caused the GC? enum GcCause { // GC triggered by a failed allocation. Thread doing allocation is blocked waiting for GC before @@ -143,41 +150,30 @@ class Heap { ~Heap(); // Allocates and initializes storage for an object instance. - mirror::Object* AllocObject(Thread* self, mirror::Class* klass, size_t num_bytes) + template + inline mirror::Object* AllocObject(Thread* self, mirror::Class* klass, size_t num_bytes) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - CHECK(!kMovingClasses); - return AllocObjectInstrumented(self, klass, num_bytes); + return AllocObjectWithAllocator(self, klass, num_bytes, GetCurrentAllocator()); } - // Allocates and initializes storage for an object instance. - mirror::Object* AllocNonMovableObject(Thread* self, mirror::Class* klass, size_t num_bytes) + template + inline mirror::Object* AllocNonMovableObject(Thread* self, mirror::Class* klass, + size_t num_bytes) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - CHECK(!kMovingClasses); - return AllocNonMovableObjectInstrumented(self, klass, num_bytes); + return AllocObjectWithAllocator(self, klass, num_bytes, + GetCurrentNonMovingAllocator()); } - mirror::Object* AllocObjectInstrumented(Thread* self, mirror::Class* klass, size_t num_bytes) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - CHECK(!kMovingClasses); - if (kMovingCollector) { - return AllocMovableObjectInstrumented(self, klass, num_bytes); - } else { - return AllocNonMovableObjectInstrumented(self, klass, num_bytes); - } + template + ALWAYS_INLINE mirror::Object* AllocObjectWithAllocator(Thread* self, mirror::Class* klass, + size_t num_bytes, AllocatorType allocator) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + AllocatorType GetCurrentAllocator() const { + return current_allocator_; } - mirror::Object* AllocObjectUninstrumented(Thread* self, mirror::Class* klass, size_t num_bytes) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - CHECK(!kMovingClasses); - if (kMovingCollector) { - return AllocMovableObjectUninstrumented(self, klass, num_bytes); - } else { - return AllocNonMovableObjectUninstrumented(self, klass, num_bytes); - } + + AllocatorType GetCurrentNonMovingAllocator() const { + return current_non_moving_allocator_; } - mirror::Object* AllocNonMovableObjectInstrumented(Thread* self, mirror::Class* klass, - size_t num_bytes) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - mirror::Object* AllocNonMovableObjectUninstrumented(Thread* self, mirror::Class* klass, - size_t num_bytes) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); // Visit all of the live objects in the heap. void VisitObjects(ObjectVisitorCallback callback, void* arg) @@ -488,13 +484,6 @@ class Heap { accounting::ModUnionTable* FindModUnionTableFromSpace(space::Space* space); void AddModUnionTable(accounting::ModUnionTable* mod_union_table); - mirror::Object* AllocMovableObjectInstrumented(Thread* self, mirror::Class* klass, - size_t num_bytes) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - mirror::Object* AllocMovableObjectUninstrumented(Thread* self, mirror::Class* klass, - size_t num_bytes) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - bool IsCompilingBoot() const; bool HasImageSpace() const; @@ -502,30 +491,19 @@ class Heap { void Compact(space::ContinuousMemMapAllocSpace* target_space, space::ContinuousMemMapAllocSpace* source_space); - bool TryAllocLargeObjectInstrumented(Thread* self, mirror::Class* c, size_t byte_count, - mirror::Object** obj_ptr, size_t* bytes_allocated) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - bool TryAllocLargeObjectUninstrumented(Thread* self, mirror::Class* c, size_t byte_count, - mirror::Object** obj_ptr, size_t* bytes_allocated) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - bool ShouldAllocLargeObject(mirror::Class* c, size_t byte_count); - void CheckConcurrentGC(Thread* self, size_t new_num_bytes_allocated, mirror::Object* obj); - - // Allocates uninitialized storage. Passing in a null space tries to place the object in the - // large object space. - template mirror::Object* AllocateInstrumented(Thread* self, T* space, size_t num_bytes, - size_t* bytes_allocated) - LOCKS_EXCLUDED(Locks::thread_suspend_count_lock_) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - - template mirror::Object* AllocateUninstrumented(Thread* self, T* space, size_t num_bytes, - size_t* bytes_allocated) - LOCKS_EXCLUDED(Locks::thread_suspend_count_lock_) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + static bool AllocatorHasAllocationStack(AllocatorType allocator_type) { + return allocator_type == kAllocatorTypeFreeList; + } + static bool AllocatorHasConcurrentGC(AllocatorType allocator_type) { + return allocator_type == kAllocatorTypeFreeList; + } + bool ShouldAllocLargeObject(mirror::Class* c, size_t byte_count) const; + ALWAYS_INLINE void CheckConcurrentGC(Thread* self, size_t new_num_bytes_allocated, + mirror::Object* obj); // Handles Allocate()'s slow allocation path with GC involved after // an initial allocation attempt failed. - mirror::Object* AllocateInternalWithGc(Thread* self, space::AllocSpace* space, size_t num_bytes, + mirror::Object* AllocateInternalWithGc(Thread* self, AllocatorType allocator, size_t num_bytes, size_t* bytes_allocated) LOCKS_EXCLUDED(Locks::thread_suspend_count_lock_) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); @@ -535,37 +513,12 @@ class Heap { size_t bytes) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - // Try to allocate a number of bytes, this function never does any GCs. - mirror::Object* TryToAllocateInstrumented(Thread* self, space::AllocSpace* space, size_t alloc_size, - bool grow, size_t* bytes_allocated) - LOCKS_EXCLUDED(Locks::thread_suspend_count_lock_) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - - // Try to allocate a number of bytes, this function never does any GCs. DlMallocSpace-specialized version. - mirror::Object* TryToAllocateInstrumented(Thread* self, space::DlMallocSpace* space, size_t alloc_size, - bool grow, size_t* bytes_allocated) - LOCKS_EXCLUDED(Locks::thread_suspend_count_lock_) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - - // Try to allocate a number of bytes, this function never does any GCs. RosAllocSpace-specialized version. - mirror::Object* TryToAllocateInstrumented(Thread* self, space::RosAllocSpace* space, size_t alloc_size, - bool grow, size_t* bytes_allocated) - LOCKS_EXCLUDED(Locks::thread_suspend_count_lock_) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - - mirror::Object* TryToAllocateUninstrumented(Thread* self, space::AllocSpace* space, size_t alloc_size, - bool grow, size_t* bytes_allocated) - LOCKS_EXCLUDED(Locks::thread_suspend_count_lock_) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - - mirror::Object* TryToAllocateUninstrumented(Thread* self, space::DlMallocSpace* space, size_t alloc_size, - bool grow, size_t* bytes_allocated) - LOCKS_EXCLUDED(Locks::thread_suspend_count_lock_) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - - mirror::Object* TryToAllocateUninstrumented(Thread* self, space::RosAllocSpace* space, size_t alloc_size, - bool grow, size_t* bytes_allocated) - LOCKS_EXCLUDED(Locks::thread_suspend_count_lock_) + // Try to allocate a number of bytes, this function never does any GCs. Needs to be inlined so + // that the switch statement is constant optimized in the entrypoints. + template + ALWAYS_INLINE mirror::Object* TryToAllocate(Thread* self, AllocatorType allocator_type, + size_t alloc_size, bool grow, + size_t* bytes_allocated) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); void ThrowOutOfMemoryError(Thread* self, size_t byte_count, bool large_object_allocation) @@ -816,12 +769,18 @@ class Heap { // Allocation stack, new allocations go here so that we can do sticky mark bits. This enables us // to use the live bitmap as the old mark bitmap. const size_t max_allocation_stack_size_; - bool is_allocation_stack_sorted_; UniquePtr allocation_stack_; // Second allocation stack so that we can process allocation with the heap unlocked. UniquePtr live_stack_; + // Allocator type. + const AllocatorType current_allocator_; + const AllocatorType current_non_moving_allocator_; + + // Which GCs we run in order when we an allocation fails. + std::vector gc_plan_; + // Bump pointer spaces. space::BumpPointerSpace* bump_pointer_space_; // Temp space is the space which the semispace collector copies to. diff --git a/runtime/gc/space/bump_pointer_space.h b/runtime/gc/space/bump_pointer_space.h index 0faac0ce46a..9b0b6aae3cd 100644 --- a/runtime/gc/space/bump_pointer_space.h +++ b/runtime/gc/space/bump_pointer_space.h @@ -120,6 +120,9 @@ class BumpPointerSpace : public ContinuousMemMapAllocSpace { static mirror::Object* GetNextObject(mirror::Object* obj) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + // Alignment. + static constexpr size_t kAlignment = 8; + protected: BumpPointerSpace(const std::string& name, MemMap* mem_map); @@ -132,9 +135,6 @@ class BumpPointerSpace : public ContinuousMemMapAllocSpace { AtomicInteger total_bytes_allocated_; AtomicInteger total_objects_allocated_; - // Alignment. - static constexpr size_t kAlignment = 8; - byte* growth_end_; private: diff --git a/runtime/instrumentation.cc b/runtime/instrumentation.cc index b3b4731ca9d..4ad9c633fbb 100644 --- a/runtime/instrumentation.cc +++ b/runtime/instrumentation.cc @@ -407,45 +407,39 @@ static void ResetQuickAllocEntryPointsForThread(Thread* thread, void* arg) { void Instrumentation::InstrumentQuickAllocEntryPoints() { // TODO: the read of quick_alloc_entry_points_instrumentation_counter_ is racey and this code // should be guarded by a lock. - DCHECK_GE(quick_alloc_entry_points_instrumentation_counter_, 0U); - bool enable_instrumentation = (quick_alloc_entry_points_instrumentation_counter_ == 0); - quick_alloc_entry_points_instrumentation_counter_++; + DCHECK_GE(quick_alloc_entry_points_instrumentation_counter_.load(), 0); + const bool enable_instrumentation = + quick_alloc_entry_points_instrumentation_counter_.fetch_add(1) == 0; if (enable_instrumentation) { // Instrumentation wasn't enabled so enable it. SetQuickAllocEntryPointsInstrumented(true); - Runtime* runtime = Runtime::Current(); - if (runtime->IsStarted()) { - ThreadList* tl = runtime->GetThreadList(); - Thread* self = Thread::Current(); - tl->SuspendAll(); - { - MutexLock mu(self, *Locks::thread_list_lock_); - tl->ForEach(ResetQuickAllocEntryPointsForThread, NULL); - } - tl->ResumeAll(); - } + ResetQuickAllocEntryPoints(); } } void Instrumentation::UninstrumentQuickAllocEntryPoints() { // TODO: the read of quick_alloc_entry_points_instrumentation_counter_ is racey and this code // should be guarded by a lock. - DCHECK_GT(quick_alloc_entry_points_instrumentation_counter_, 0U); - quick_alloc_entry_points_instrumentation_counter_--; - bool disable_instrumentation = (quick_alloc_entry_points_instrumentation_counter_ == 0); + DCHECK_GT(quick_alloc_entry_points_instrumentation_counter_.load(), 0); + const bool disable_instrumentation = + quick_alloc_entry_points_instrumentation_counter_.fetch_sub(1) == 1; if (disable_instrumentation) { SetQuickAllocEntryPointsInstrumented(false); - Runtime* runtime = Runtime::Current(); - if (runtime->IsStarted()) { - ThreadList* tl = Runtime::Current()->GetThreadList(); - Thread* self = Thread::Current(); - tl->SuspendAll(); - { - MutexLock mu(self, *Locks::thread_list_lock_); - tl->ForEach(ResetQuickAllocEntryPointsForThread, NULL); - } - tl->ResumeAll(); + ResetQuickAllocEntryPoints(); + } +} + +void Instrumentation::ResetQuickAllocEntryPoints() { + Runtime* runtime = Runtime::Current(); + if (runtime->IsStarted()) { + ThreadList* tl = runtime->GetThreadList(); + Thread* self = Thread::Current(); + tl->SuspendAll(); + { + MutexLock mu(self, *Locks::thread_list_lock_); + tl->ForEach(ResetQuickAllocEntryPointsForThread, NULL); } + tl->ResumeAll(); } } diff --git a/runtime/instrumentation.h b/runtime/instrumentation.h index 6bfc2d7e3dd..72a646e3e5f 100644 --- a/runtime/instrumentation.h +++ b/runtime/instrumentation.h @@ -17,6 +17,7 @@ #ifndef ART_RUNTIME_INSTRUMENTATION_H_ #define ART_RUNTIME_INSTRUMENTATION_H_ +#include "atomic_integer.h" #include "base/macros.h" #include "locks.h" @@ -125,6 +126,7 @@ class Instrumentation { void InstrumentQuickAllocEntryPoints() LOCKS_EXCLUDED(Locks::thread_list_lock_); void UninstrumentQuickAllocEntryPoints() LOCKS_EXCLUDED(Locks::thread_list_lock_); + void ResetQuickAllocEntryPoints(); // Update the code of a method respecting any installed stubs. void UpdateMethodsCode(mirror::ArtMethod* method, const void* code) const; @@ -298,7 +300,7 @@ class Instrumentation { // Greater than 0 if quick alloc entry points instrumented. // TODO: The access and changes to this is racy and should be guarded by a lock. - size_t quick_alloc_entry_points_instrumentation_counter_; + AtomicInteger quick_alloc_entry_points_instrumentation_counter_; DISALLOW_COPY_AND_ASSIGN(Instrumentation); }; diff --git a/runtime/interpreter/interpreter_common.cc b/runtime/interpreter/interpreter_common.cc index 08221b723d9..c9756ac04de 100644 --- a/runtime/interpreter/interpreter_common.cc +++ b/runtime/interpreter/interpreter_common.cc @@ -193,7 +193,7 @@ bool DoFilledNewArray(const Instruction* inst, const ShadowFrame& shadow_frame, } return false; } - Object* newArray = Array::Alloc(self, arrayClass, length); + Object* newArray = Array::Alloc(self, arrayClass, length); if (UNLIKELY(newArray == NULL)) { DCHECK(self->IsExceptionPending()); return false; @@ -279,7 +279,7 @@ static void UnstartedRuntimeInvoke(Thread* self, MethodHelper& mh, // TODO: getDeclaredField calls GetType once the field is found to ensure a // NoClassDefFoundError is thrown if the field's type cannot be resolved. Class* jlr_Field = self->DecodeJObject(WellKnownClasses::java_lang_reflect_Field)->AsClass(); - SirtRef field(self, jlr_Field->AllocObject(self)); + SirtRef field(self, jlr_Field->AllocNonMovableObject(self)); CHECK(field.get() != NULL); ArtMethod* c = jlr_Field->FindDeclaredDirectMethod("", "(Ljava/lang/reflect/ArtField;)V"); uint32_t args[1]; diff --git a/runtime/interpreter/interpreter_goto_table_impl.cc b/runtime/interpreter/interpreter_goto_table_impl.cc index aa6bcd696f4..99c85bdb2ec 100644 --- a/runtime/interpreter/interpreter_goto_table_impl.cc +++ b/runtime/interpreter/interpreter_goto_table_impl.cc @@ -509,8 +509,9 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(NEW_INSTANCE) { - Object* obj = AllocObjectFromCodeInstrumented(inst->VRegB_21c(), shadow_frame.GetMethod(), - self, do_access_check); + Object* obj = AllocObjectFromCode( + inst->VRegB_21c(), shadow_frame.GetMethod(), self, + Runtime::Current()->GetHeap()->GetCurrentAllocator()); if (UNLIKELY(obj == NULL)) { HANDLE_PENDING_EXCEPTION(); } else { @@ -522,8 +523,9 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* HANDLE_INSTRUCTION_START(NEW_ARRAY) { int32_t length = shadow_frame.GetVReg(inst->VRegB_22c(inst_data)); - Object* obj = AllocArrayFromCodeInstrumented(inst->VRegC_22c(), shadow_frame.GetMethod(), - length, self, do_access_check); + Object* obj = AllocArrayFromCode( + inst->VRegC_22c(), shadow_frame.GetMethod(), length, self, + Runtime::Current()->GetHeap()->GetCurrentAllocator()); if (UNLIKELY(obj == NULL)) { HANDLE_PENDING_EXCEPTION(); } else { diff --git a/runtime/interpreter/interpreter_switch_impl.cc b/runtime/interpreter/interpreter_switch_impl.cc index bd0d87eefc1..675095f442d 100644 --- a/runtime/interpreter/interpreter_switch_impl.cc +++ b/runtime/interpreter/interpreter_switch_impl.cc @@ -422,8 +422,9 @@ JValue ExecuteSwitchImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem } case Instruction::NEW_INSTANCE: { PREAMBLE(); - Object* obj = AllocObjectFromCodeInstrumented(inst->VRegB_21c(), shadow_frame.GetMethod(), - self, do_access_check); + Object* obj = AllocObjectFromCode( + inst->VRegB_21c(), shadow_frame.GetMethod(), self, + Runtime::Current()->GetHeap()->GetCurrentAllocator()); if (UNLIKELY(obj == NULL)) { HANDLE_PENDING_EXCEPTION(); } else { @@ -435,8 +436,9 @@ JValue ExecuteSwitchImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem case Instruction::NEW_ARRAY: { PREAMBLE(); int32_t length = shadow_frame.GetVReg(inst->VRegB_22c(inst_data)); - Object* obj = AllocArrayFromCodeInstrumented(inst->VRegC_22c(), shadow_frame.GetMethod(), - length, self, do_access_check); + Object* obj = AllocArrayFromCode( + inst->VRegC_22c(), shadow_frame.GetMethod(), length, self, + Runtime::Current()->GetHeap()->GetCurrentAllocator()); if (UNLIKELY(obj == NULL)) { HANDLE_PENDING_EXCEPTION(); } else { diff --git a/runtime/mirror/array-inl.h b/runtime/mirror/array-inl.h index ef73e4df47a..2955faa3c81 100644 --- a/runtime/mirror/array-inl.h +++ b/runtime/mirror/array-inl.h @@ -59,43 +59,44 @@ static inline size_t ComputeArraySize(Thread* self, Class* array_class, int32_t } static inline Array* SetArrayLength(Array* array, size_t length) { - if (LIKELY(array != NULL)) { + if (LIKELY(array != nullptr)) { DCHECK(array->IsArrayInstance()); array->SetLength(length); } return array; } -template +template inline Array* Array::Alloc(Thread* self, Class* array_class, int32_t component_count, - size_t component_size) { + size_t component_size, gc::AllocatorType allocator_type) { size_t size = ComputeArraySize(self, array_class, component_count, component_size); if (UNLIKELY(size == 0)) { - return NULL; + return nullptr; } gc::Heap* heap = Runtime::Current()->GetHeap(); - Array* array = nullptr; - if (kIsMovable) { - if (kIsInstrumented) { - array = down_cast(heap->AllocMovableObjectInstrumented(self, array_class, size)); - } else { - array = down_cast(heap->AllocMovableObjectUninstrumented(self, array_class, size)); - } - } else { - if (kIsInstrumented) { - array = down_cast(heap->AllocNonMovableObjectInstrumented(self, array_class, size)); - } else { - array = down_cast(heap->AllocNonMovableObjectUninstrumented(self, array_class, size)); - } - } + Array* array = down_cast( + heap->AllocObjectWithAllocator(self, array_class, size, allocator_type)); return SetArrayLength(array, component_count); } -template -inline Array* Array::Alloc(Thread* self, Class* array_class, int32_t component_count) { +template +inline Array* Array::Alloc(Thread* self, Class* array_class, int32_t component_count, + gc::AllocatorType allocator_type) { DCHECK(array_class->IsArrayClass()); - return Alloc(self, array_class, component_count, - array_class->GetComponentSize()); + return Alloc(self, array_class, component_count, array_class->GetComponentSize(), + allocator_type); +} +template +inline Array* Array::Alloc(Thread* self, Class* array_class, int32_t component_count) { + return Alloc(self, array_class, component_count, + Runtime::Current()->GetHeap()->GetCurrentAllocator()); +} + +template +inline Array* Array::Alloc(Thread* self, Class* array_class, int32_t component_count, + size_t component_size) { + return Alloc(self, array_class, component_count, component_size, + Runtime::Current()->GetHeap()->GetCurrentAllocator()); } } // namespace mirror diff --git a/runtime/mirror/array.cc b/runtime/mirror/array.cc index f8a283224c6..00b88db2996 100644 --- a/runtime/mirror/array.cc +++ b/runtime/mirror/array.cc @@ -44,8 +44,7 @@ static Array* RecursiveCreateMultiArray(Thread* self, Class* array_class, int cu SirtRef& dimensions) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { int32_t array_length = dimensions->Get(current_dimension); - SirtRef new_array(self, Array::Alloc(self, array_class, - array_length)); + SirtRef new_array(self, Array::Alloc(self, array_class, array_length)); if (UNLIKELY(new_array.get() == NULL)) { CHECK(self->IsExceptionPending()); return NULL; @@ -115,7 +114,7 @@ void Array::ThrowArrayStoreException(Object* object) const { template PrimitiveArray* PrimitiveArray::Alloc(Thread* self, size_t length) { DCHECK(array_class_ != NULL); - Array* raw_array = Array::Alloc(self, array_class_, length, sizeof(T)); + Array* raw_array = Array::Alloc(self, array_class_, length, sizeof(T)); return down_cast*>(raw_array); } diff --git a/runtime/mirror/array.h b/runtime/mirror/array.h index 584a4c095bb..a332f97c027 100644 --- a/runtime/mirror/array.h +++ b/runtime/mirror/array.h @@ -18,6 +18,7 @@ #define ART_RUNTIME_MIRROR_ARRAY_H_ #include "object.h" +#include "gc/heap.h" namespace art { namespace mirror { @@ -26,13 +27,24 @@ class MANAGED Array : public Object { public: // A convenience for code that doesn't know the component size, and doesn't want to have to work // it out itself. - template + template + static Array* Alloc(Thread* self, Class* array_class, int32_t component_count, + gc::AllocatorType allocator_type) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + template + static Array* Alloc(Thread* self, Class* array_class, int32_t component_count, + size_t component_size, gc::AllocatorType allocator_type) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + template static Array* Alloc(Thread* self, Class* array_class, int32_t component_count) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - template + template static Array* Alloc(Thread* self, Class* array_class, int32_t component_count, - size_t component_size) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + size_t component_size) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); static Array* CreateMultiArray(Thread* self, Class* element_class, IntArray* dimensions) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); diff --git a/runtime/mirror/class-inl.h b/runtime/mirror/class-inl.h index 406ab1bbb3a..4dcce1e8a18 100644 --- a/runtime/mirror/class-inl.h +++ b/runtime/mirror/class-inl.h @@ -357,23 +357,20 @@ inline void Class::CheckObjectAlloc() { DCHECK_GE(this->object_size_, sizeof(Object)); } -template -inline Object* Class::Alloc(Thread* self) { +template +inline Object* Class::Alloc(Thread* self, gc::AllocatorType allocator_type) { CheckObjectAlloc(); gc::Heap* heap = Runtime::Current()->GetHeap(); - if (kIsMovable) { - if (kIsInstrumented) { - return heap->AllocMovableObjectInstrumented(self, this, this->object_size_); - } else { - return heap->AllocMovableObjectUninstrumented(self, this, this->object_size_); - } - } else { - if (kIsInstrumented) { - return heap->AllocNonMovableObjectInstrumented(self, this, this->object_size_); - } else { - return heap->AllocNonMovableObjectUninstrumented(self, this, this->object_size_); - } - } + return heap->AllocObjectWithAllocator(self, this, this->object_size_, + allocator_type); +} + +inline Object* Class::AllocObject(Thread* self) { + return Alloc(self, Runtime::Current()->GetHeap()->GetCurrentAllocator()); +} + +inline Object* Class::AllocNonMovableObject(Thread* self) { + return Alloc(self, Runtime::Current()->GetHeap()->GetCurrentNonMovingAllocator()); } } // namespace mirror diff --git a/runtime/mirror/class.h b/runtime/mirror/class.h index 82077dc52aa..5f64bb4f8b5 100644 --- a/runtime/mirror/class.h +++ b/runtime/mirror/class.h @@ -17,6 +17,7 @@ #ifndef ART_RUNTIME_MIRROR_CLASS_H_ #define ART_RUNTIME_MIRROR_CLASS_H_ +#include "gc/heap.h" #include "modifiers.h" #include "object.h" #include "primitive.h" @@ -377,13 +378,14 @@ class MANAGED Class : public StaticStorageBase { } // Creates a raw object instance but does not invoke the default constructor. - Object* AllocObject(Thread* self) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - return Alloc(self); - } + template + ALWAYS_INLINE Object* Alloc(Thread* self, gc::AllocatorType allocator_type) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - // Creates a raw object instance but does not invoke the default constructor. - template - Object* Alloc(Thread* self) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + Object* AllocObject(Thread* self) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + Object* AllocNonMovableObject(Thread* self) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); bool IsVariableSize() const { // Classes and arrays vary in size, and so the object_size_ field cannot diff --git a/runtime/mirror/object.cc b/runtime/mirror/object.cc index 385ef5ff892..008a17356fe 100644 --- a/runtime/mirror/object.cc +++ b/runtime/mirror/object.cc @@ -75,9 +75,9 @@ Object* Object::Clone(Thread* self) { SirtRef this_object(self, this); Object* copy; if (heap->IsMovableObject(this)) { - copy = heap->AllocObject(self, GetClass(), num_bytes); + copy = heap->AllocObject(self, GetClass(), num_bytes); } else { - copy = heap->AllocNonMovableObject(self, GetClass(), num_bytes); + copy = heap->AllocNonMovableObject(self, GetClass(), num_bytes); } if (LIKELY(copy != nullptr)) { return CopyObject(self, copy, this_object.get(), num_bytes); diff --git a/runtime/mirror/object_array-inl.h b/runtime/mirror/object_array-inl.h index 478f4ec210d..be49b42f0de 100644 --- a/runtime/mirror/object_array-inl.h +++ b/runtime/mirror/object_array-inl.h @@ -30,15 +30,24 @@ namespace art { namespace mirror { template -inline ObjectArray* ObjectArray::Alloc(Thread* self, Class* object_array_class, int32_t length) { - Array* array = Array::Alloc(self, object_array_class, length, sizeof(Object*)); - if (UNLIKELY(array == NULL)) { - return NULL; +inline ObjectArray* ObjectArray::Alloc(Thread* self, Class* object_array_class, + int32_t length, gc::AllocatorType allocator_type) { + Array* array = Array::Alloc(self, object_array_class, length, sizeof(Object*), + allocator_type); + if (UNLIKELY(array == nullptr)) { + return nullptr; } else { return array->AsObjectArray(); } } +template +inline ObjectArray* ObjectArray::Alloc(Thread* self, Class* object_array_class, + int32_t length) { + return Alloc(self, object_array_class, length, + Runtime::Current()->GetHeap()->GetCurrentAllocator()); +} + template inline T* ObjectArray::Get(int32_t i) const { if (UNLIKELY(!IsValidIndex(i))) { @@ -137,7 +146,10 @@ template inline ObjectArray* ObjectArray::CopyOf(Thread* self, int32_t new_length) { // We may get copied by a compacting GC. SirtRef > sirt_this(self, this); - ObjectArray* new_array = Alloc(self, GetClass(), new_length); + gc::Heap* heap = Runtime::Current()->GetHeap(); + gc::AllocatorType allocator_type = heap->IsMovableObject(this) ? heap->GetCurrentAllocator() : + heap->GetCurrentNonMovingAllocator(); + ObjectArray* new_array = Alloc(self, GetClass(), new_length, allocator_type); if (LIKELY(new_array != nullptr)) { Copy(sirt_this.get(), 0, new_array, 0, std::min(sirt_this->GetLength(), new_length)); } diff --git a/runtime/mirror/object_array.h b/runtime/mirror/object_array.h index 09ff5193ae2..5da8845196a 100644 --- a/runtime/mirror/object_array.h +++ b/runtime/mirror/object_array.h @@ -18,6 +18,7 @@ #define ART_RUNTIME_MIRROR_OBJECT_ARRAY_H_ #include "array.h" +#include "gc/heap.h" namespace art { namespace mirror { @@ -25,6 +26,10 @@ namespace mirror { template class MANAGED ObjectArray : public Array { public: + static ObjectArray* Alloc(Thread* self, Class* object_array_class, int32_t length, + gc::AllocatorType allocator_type) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + static ObjectArray* Alloc(Thread* self, Class* object_array_class, int32_t length) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); diff --git a/runtime/mirror/object_test.cc b/runtime/mirror/object_test.cc index 853031701a3..8272ff8b1f3 100644 --- a/runtime/mirror/object_test.cc +++ b/runtime/mirror/object_test.cc @@ -144,15 +144,15 @@ TEST_F(ObjectTest, AllocObjectArray) { TEST_F(ObjectTest, AllocArray) { ScopedObjectAccess soa(Thread::Current()); Class* c = class_linker_->FindSystemClass("[I"); - SirtRef a(soa.Self(), Array::Alloc(soa.Self(), c, 1)); + SirtRef a(soa.Self(), Array::Alloc(soa.Self(), c, 1)); ASSERT_TRUE(c == a->GetClass()); c = class_linker_->FindSystemClass("[Ljava/lang/Object;"); - a.reset(Array::Alloc(soa.Self(), c, 1)); + a.reset(Array::Alloc(soa.Self(), c, 1)); ASSERT_TRUE(c == a->GetClass()); c = class_linker_->FindSystemClass("[[Ljava/lang/Object;"); - a.reset(Array::Alloc(soa.Self(), c, 1)); + a.reset(Array::Alloc(soa.Self(), c, 1)); ASSERT_TRUE(c == a->GetClass()); } @@ -221,7 +221,8 @@ TEST_F(ObjectTest, CheckAndAllocArrayFromCode) { java_lang_dex_file_->GetIndexForStringId(*string_id)); ASSERT_TRUE(type_id != NULL); uint32_t type_idx = java_lang_dex_file_->GetIndexForTypeId(*type_id); - Object* array = CheckAndAllocArrayFromCode(type_idx, sort, 3, Thread::Current(), false); + Object* array = CheckAndAllocArrayFromCode(type_idx, sort, 3, Thread::Current(), false, + Runtime::Current()->GetHeap()->GetCurrentAllocator()); EXPECT_TRUE(array->IsArrayInstance()); EXPECT_EQ(3, array->AsArray()->GetLength()); EXPECT_TRUE(array->GetClass()->IsArrayClass()); diff --git a/runtime/native/dalvik_system_VMRuntime.cc b/runtime/native/dalvik_system_VMRuntime.cc index f0efdc20742..fd3d91ef99a 100644 --- a/runtime/native/dalvik_system_VMRuntime.cc +++ b/runtime/native/dalvik_system_VMRuntime.cc @@ -71,7 +71,8 @@ static jobject VMRuntime_newNonMovableArray(JNIEnv* env, jobject, jclass javaEle descriptor += ClassHelper(element_class).GetDescriptor(); SirtRef class_loader(soa.Self(), nullptr); mirror::Class* array_class = class_linker->FindClass(descriptor.c_str(), class_loader); - mirror::Array* result = mirror::Array::Alloc(soa.Self(), array_class, length); + mirror::Array* result = mirror::Array::Alloc(soa.Self(), array_class, length, + Runtime::Current()->GetHeap()->GetCurrentNonMovingAllocator()); return soa.AddLocalReference(result); } diff --git a/runtime/native/java_lang_reflect_Array.cc b/runtime/native/java_lang_reflect_Array.cc index 808c9170d90..52cdb59a964 100644 --- a/runtime/native/java_lang_reflect_Array.cc +++ b/runtime/native/java_lang_reflect_Array.cc @@ -59,8 +59,7 @@ static jobject Array_createObjectArray(JNIEnv* env, jclass, jclass javaElementCl return NULL; } DCHECK(array_class->IsArrayClass()); - mirror::Array* new_array = mirror::Array::Alloc( - soa.Self(), array_class, length); + mirror::Array* new_array = mirror::Array::Alloc(soa.Self(), array_class, length); return soa.AddLocalReference(new_array); } diff --git a/runtime/native/java_lang_reflect_Constructor.cc b/runtime/native/java_lang_reflect_Constructor.cc index aa72755c9da..04dfcb565a9 100644 --- a/runtime/native/java_lang_reflect_Constructor.cc +++ b/runtime/native/java_lang_reflect_Constructor.cc @@ -56,7 +56,7 @@ static jobject Constructor_newInstance(JNIEnv* env, jobject javaMethod, jobjectA return NULL; } - mirror::Object* receiver = c->AllocObject(soa.Self()); + mirror::Object* receiver = c->AllocNonMovableObject(soa.Self()); if (receiver == NULL) { return NULL; } From 1febddf359ae500ef1bb01ab4883b076fcb56440 Mon Sep 17 00:00:00 2001 From: Mathieu Chartier Date: Wed, 20 Nov 2013 12:33:14 -0800 Subject: [PATCH 0206/2402] Set array length before fence in allocation code path. Could not delete SetLength since it is required by space_test. Bug: 11747779 Change-Id: Icf1ead216b6ff1b519240ab0d0ca30d68429d5b6 --- runtime/gc/heap-inl.h | 21 +++++++++++---------- runtime/gc/heap.h | 7 ++++--- runtime/mirror/array-inl.h | 24 ++++++++++++++++-------- 3 files changed, 31 insertions(+), 21 deletions(-) diff --git a/runtime/gc/heap-inl.h b/runtime/gc/heap-inl.h index fcc07a0224e..9b285555284 100644 --- a/runtime/gc/heap-inl.h +++ b/runtime/gc/heap-inl.h @@ -32,10 +32,11 @@ namespace art { namespace gc { -template -inline mirror::Object* Heap::AllocObjectWithAllocator(Thread* self, mirror::Class* c, - size_t byte_count, AllocatorType allocator) { - DebugCheckPreconditionsForAllocObject(c, byte_count); +template +inline mirror::Object* Heap::AllocObjectWithAllocator(Thread* self, mirror::Class* klass, + size_t byte_count, AllocatorType allocator, + const PreFenceVisitor& pre_fence_visitor) { + DebugCheckPreconditionsForAllocObject(klass, byte_count); // Since allocation can cause a GC which will need to SuspendAll, make sure all allocations are // done in the runnable state where suspension is expected. DCHECK_EQ(self->GetState(), kRunnable); @@ -43,7 +44,7 @@ inline mirror::Object* Heap::AllocObjectWithAllocator(Thread* self, mirror::Clas mirror::Object* obj; size_t bytes_allocated; AllocationTimer alloc_timer(this, &obj); - if (UNLIKELY(ShouldAllocLargeObject(c, byte_count))) { + if (UNLIKELY(ShouldAllocLargeObject(klass, byte_count))) { obj = TryToAllocate(self, kAllocatorTypeLOS, byte_count, false, &bytes_allocated); allocator = kAllocatorTypeLOS; @@ -52,16 +53,16 @@ inline mirror::Object* Heap::AllocObjectWithAllocator(Thread* self, mirror::Clas } if (UNLIKELY(obj == nullptr)) { - SirtRef sirt_c(self, c); + SirtRef sirt_c(self, klass); obj = AllocateInternalWithGc(self, allocator, byte_count, &bytes_allocated); if (obj == nullptr) { return nullptr; } else { - c = sirt_c.get(); + klass = sirt_c.get(); } } - obj->SetClass(c); - // TODO: Set array length here. + obj->SetClass(klass); + pre_fence_visitor(obj); DCHECK_GT(bytes_allocated, 0u); const size_t new_num_bytes_allocated = static_cast(num_bytes_allocated_.fetch_add(bytes_allocated)) + bytes_allocated; @@ -87,7 +88,7 @@ inline mirror::Object* Heap::AllocObjectWithAllocator(Thread* self, mirror::Clas } if (kInstrumented) { if (Dbg::IsAllocTrackingEnabled()) { - Dbg::RecordAllocation(c, bytes_allocated); + Dbg::RecordAllocation(klass, bytes_allocated); } } else { DCHECK(!Dbg::IsAllocTrackingEnabled()); diff --git a/runtime/gc/heap.h b/runtime/gc/heap.h index 5a0372a295c..470bacdda31 100644 --- a/runtime/gc/heap.h +++ b/runtime/gc/heap.h @@ -162,9 +162,10 @@ class Heap { return AllocObjectWithAllocator(self, klass, num_bytes, GetCurrentNonMovingAllocator()); } - template - ALWAYS_INLINE mirror::Object* AllocObjectWithAllocator(Thread* self, mirror::Class* klass, - size_t num_bytes, AllocatorType allocator) + template + ALWAYS_INLINE mirror::Object* AllocObjectWithAllocator( + Thread* self, mirror::Class* klass, size_t byte_count, AllocatorType allocator, + const PreFenceVisitor& pre_fence_visitor = VoidFunctor()) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); AllocatorType GetCurrentAllocator() const { diff --git a/runtime/mirror/array-inl.h b/runtime/mirror/array-inl.h index 2955faa3c81..46ffaaef60e 100644 --- a/runtime/mirror/array-inl.h +++ b/runtime/mirror/array-inl.h @@ -58,13 +58,20 @@ static inline size_t ComputeArraySize(Thread* self, Class* array_class, int32_t return size; } -static inline Array* SetArrayLength(Array* array, size_t length) { - if (LIKELY(array != nullptr)) { +class SetLengthVisitor { + public: + explicit SetLengthVisitor(int32_t length) : length_(length) { + } + + void operator()(mirror::Object* obj) const { + mirror::Array* array = obj->AsArray(); DCHECK(array->IsArrayInstance()); - array->SetLength(length); + array->SetLength(length_); } - return array; -} + + private: + const int32_t length_; +}; template inline Array* Array::Alloc(Thread* self, Class* array_class, int32_t component_count, @@ -74,9 +81,10 @@ inline Array* Array::Alloc(Thread* self, Class* array_class, int32_t component_c return nullptr; } gc::Heap* heap = Runtime::Current()->GetHeap(); - Array* array = down_cast( - heap->AllocObjectWithAllocator(self, array_class, size, allocator_type)); - return SetArrayLength(array, component_count); + SetLengthVisitor visitor(component_count); + return down_cast( + heap->AllocObjectWithAllocator(self, array_class, size, allocator_type, + visitor)); } template From 4b5553031edf6ca5212f4eb183073e43abac2b0d Mon Sep 17 00:00:00 2001 From: Mathieu Chartier Date: Wed, 20 Nov 2013 15:25:33 -0800 Subject: [PATCH 0207/2402] Fix memory leak caused by not adding lage objects to allocation stack. Large objects weren't being added to allocation stack. This was causing them to never be marked as live and therefore never freed. Change-Id: Ie84e3cd7a417a89870752cb21bd7d562c3427284 --- runtime/gc/heap.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/runtime/gc/heap.h b/runtime/gc/heap.h index 470bacdda31..6f94714be30 100644 --- a/runtime/gc/heap.h +++ b/runtime/gc/heap.h @@ -493,10 +493,10 @@ class Heap { space::ContinuousMemMapAllocSpace* source_space); static bool AllocatorHasAllocationStack(AllocatorType allocator_type) { - return allocator_type == kAllocatorTypeFreeList; + return allocator_type != kAllocatorTypeBumpPointer; } static bool AllocatorHasConcurrentGC(AllocatorType allocator_type) { - return allocator_type == kAllocatorTypeFreeList; + return allocator_type != kAllocatorTypeBumpPointer; } bool ShouldAllocLargeObject(mirror::Class* c, size_t byte_count) const; ALWAYS_INLINE void CheckConcurrentGC(Thread* self, size_t new_num_bytes_allocated, From 19b0a913d9127a70ca35ebae166312bc6eee3196 Mon Sep 17 00:00:00 2001 From: Mathieu Chartier Date: Wed, 20 Nov 2013 14:07:54 -0800 Subject: [PATCH 0208/2402] Improve histogram and timing logger dumping. We now dump the sum (total time) of each histogram as well as previous stats. This is useful for the GC since the same split can occur multiple times per GC iteration. Also did a few memory optimizations by changing the map in the cumulative loggers to be a set. Bug: 11789200 Change-Id: I67bcc5384200924c8dc5d9eebcff077ce72b7e57 --- runtime/base/histogram-inl.h | 10 +++++++- runtime/base/histogram.h | 2 ++ runtime/base/timing_logger.cc | 48 +++++++++++++++++++++-------------- runtime/base/timing_logger.h | 16 +++++++++--- 4 files changed, 52 insertions(+), 24 deletions(-) diff --git a/runtime/base/histogram-inl.h b/runtime/base/histogram-inl.h index 9e08ae6febc..4cd68cda446 100644 --- a/runtime/base/histogram-inl.h +++ b/runtime/base/histogram-inl.h @@ -39,6 +39,13 @@ template inline void Histogram::AddValue(Value value) { BucketiseValue(value); } +template inline Histogram::Histogram(const char* name) + : kAdjust(0), + kInitialBucketCount(0), + name_(name), + max_buckets_(0) { +} + template inline Histogram::Histogram(const char* name, Value initial_bucket_width, size_t max_buckets) @@ -162,8 +169,9 @@ inline void Histogram::PrintConfidenceIntervals(std::ostream &os, double double per_0 = (1.0 - interval) / 2.0; double per_1 = per_0 + interval; - os << Name() << ":\t"; TimeUnit unit = GetAppropriateTimeUnit(Mean() * kAdjust); + os << Name() << ":\tSum: "; + os << PrettyDuration(Sum() * kAdjust) << " "; os << (interval * 100) << "% C.I. " << FormatDuration(Percentile(per_0, data) * kAdjust, unit); os << "-" << FormatDuration(Percentile(per_1, data) * kAdjust, unit) << " "; os << "Avg: " << FormatDuration(Mean() * kAdjust, unit) << " Max: "; diff --git a/runtime/base/histogram.h b/runtime/base/histogram.h index e22b6e17b27..d4eb1f41a5a 100644 --- a/runtime/base/histogram.h +++ b/runtime/base/histogram.h @@ -40,6 +40,8 @@ template class Histogram { std::vector perc_; }; + // Used for name based comparators in the timing loggers. + explicit Histogram(const char* name); Histogram(const char* name, Value initial_bucket_width, size_t max_buckets = 100); void AddValue(Value); // Builds the cumulative distribution function from the frequency data. diff --git a/runtime/base/timing_logger.cc b/runtime/base/timing_logger.cc index bebbd70b817..c8dee6d346f 100644 --- a/runtime/base/timing_logger.cc +++ b/runtime/base/timing_logger.cc @@ -39,7 +39,7 @@ CumulativeLogger::CumulativeLogger(const std::string& name) } CumulativeLogger::~CumulativeLogger() { - STLDeleteValues(&histograms_); + STLDeleteElements(&histograms_); } void CumulativeLogger::SetName(const std::string& name) { @@ -57,7 +57,7 @@ void CumulativeLogger::End() { void CumulativeLogger::Reset() { MutexLock mu(Thread::Current(), lock_); iterations_ = 0; - STLDeleteValues(&histograms_); + STLDeleteElements(&histograms_); } uint64_t CumulativeLogger::GetTotalNs() const { @@ -67,9 +67,8 @@ uint64_t CumulativeLogger::GetTotalNs() const { uint64_t CumulativeLogger::GetTotalTime() const { MutexLock mu(Thread::Current(), lock_); uint64_t total = 0; - for (CumulativeLogger::HistogramsIterator it = histograms_.begin(), end = histograms_.end(); - it != end; ++it) { - total += it->second->Sum(); + for (Histogram* histogram : histograms_) { + total += histogram->Sum(); } return total; } @@ -95,30 +94,41 @@ void CumulativeLogger::Dump(std::ostream &os) { DumpHistogram(os); } -void CumulativeLogger::AddPair(const std::string &label, uint64_t delta_time) { +void CumulativeLogger::AddPair(const std::string& label, uint64_t delta_time) { // Convert delta time to microseconds so that we don't overflow our counters. delta_time /= kAdjust; - if (histograms_.find(label) == histograms_.end()) { - // TODO: Should this be a defined constant so we we know out of which orifice 16 and 100 were - // picked? - const size_t max_buckets = Runtime::Current()->GetHeap()->IsLowMemoryMode() ? 16 : 100; - // TODO: Should this be a defined constant so we know 50 of WTF? - histograms_[label] = new Histogram(label.c_str(), 50, max_buckets); + Histogram* histogram; + Histogram dummy(label.c_str()); + auto it = histograms_.find(&dummy); + if (it == histograms_.end()) { + const size_t max_buckets = Runtime::Current()->GetHeap()->IsLowMemoryMode() ? + kLowMemoryBucketCount : kDefaultBucketCount; + histogram = new Histogram(label.c_str(), kInitialBucketSize, max_buckets); + histograms_.insert(histogram); + } else { + histogram = *it; } - histograms_[label]->AddValue(delta_time); + histogram->AddValue(delta_time); } +class CompareHistorgramByTimeSpentDeclining { + public: + bool operator()(const Histogram* a, const Histogram* b) const { + return a->Sum() > b->Sum(); + } +}; + void CumulativeLogger::DumpHistogram(std::ostream &os) { os << "Start Dumping histograms for " << iterations_ << " iterations" << " for " << name_ << "\n"; - for (CumulativeLogger::HistogramsIterator it = histograms_.begin(), end = histograms_.end(); - it != end; ++it) { + std::set*, CompareHistorgramByTimeSpentDeclining> + sorted_histograms(histograms_.begin(), histograms_.end()); + for (Histogram* histogram : sorted_histograms) { Histogram::CumulativeData cumulative_data; - it->second->CreateHistogram(&cumulative_data); - it->second->PrintConfidenceIntervals(os, 0.99, cumulative_data); - // Reset cumulative values to save memory. We don't expect DumpHistogram to be called often, so - // it is not performance critical. + // We don't expect DumpHistogram to be called often, so it is not performance critical. + histogram->CreateHistogram(&cumulative_data); + histogram->PrintConfidenceIntervals(os, 0.99, cumulative_data); } os << "Done Dumping histograms \n"; } diff --git a/runtime/base/timing_logger.h b/runtime/base/timing_logger.h index f1f78557aae..c1ff0a3619c 100644 --- a/runtime/base/timing_logger.h +++ b/runtime/base/timing_logger.h @@ -21,9 +21,9 @@ #include "base/macros.h" #include "base/mutex.h" +#include #include #include -#include namespace art { class TimingLogger; @@ -45,15 +45,23 @@ class CumulativeLogger { size_t GetIterations() const; private: - typedef std::map *> Histograms; - typedef std::map *>::const_iterator HistogramsIterator; + class HistogramComparator { + public: + bool operator()(const Histogram* a, const Histogram* b) const { + return a->Name() < b->Name(); + } + }; + + static constexpr size_t kLowMemoryBucketCount = 16; + static constexpr size_t kDefaultBucketCount = 100; + static constexpr size_t kInitialBucketSize = 50; // 50 microseconds. void AddPair(const std::string &label, uint64_t delta_time) EXCLUSIVE_LOCKS_REQUIRED(lock_); void DumpHistogram(std::ostream &os) EXCLUSIVE_LOCKS_REQUIRED(lock_); uint64_t GetTotalTime() const; static const uint64_t kAdjust = 1000; - Histograms histograms_ GUARDED_BY(lock_); + std::set*, HistogramComparator> histograms_ GUARDED_BY(lock_); std::string name_; const std::string lock_name_; mutable Mutex lock_ DEFAULT_MUTEX_ACQUIRED_AFTER; From 3e669dba962dc5291de0642eb46ede107be4e5a4 Mon Sep 17 00:00:00 2001 From: Mathieu Chartier Date: Wed, 20 Nov 2013 15:51:12 -0800 Subject: [PATCH 0209/2402] Fix histogram test. The test compares outputted strings for some reason. Forgot to run it earlier. Change-Id: I9eed08350d0122614cb2e18ca7195e5dc0832da6 --- runtime/base/histogram_test.cc | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/runtime/base/histogram_test.cc b/runtime/base/histogram_test.cc index 9d371f57546..966b97f3b09 100644 --- a/runtime/base/histogram_test.cc +++ b/runtime/base/histogram_test.cc @@ -122,7 +122,7 @@ TEST(Histtest, UpdateRange) { std::string text; std::stringstream stream; - std::string expected("UpdateRange:\t99% C.I. 15us-212us Avg: 126.380us Max: 212us\n"); + std::string expected("UpdateRange:\tSum: 2.654ms 99% C.I. 15us-212us Avg: 126.380us Max: 212us\n"); hist->PrintConfidenceIntervals(stream, 0.99, data); EXPECT_EQ(expected, stream.str()); @@ -165,7 +165,7 @@ TEST(Histtest, Reset) { std::string text; std::stringstream stream; - std::string expected("Reset:\t99% C.I. 15us-212us Avg: 126.380us Max: 212us\n"); + std::string expected("Reset:\tSum: 2.654ms 99% C.I. 15us-212us Avg: 126.380us Max: 212us\n"); hist->PrintConfidenceIntervals(stream, 0.99, data); EXPECT_EQ(expected, stream.str()); @@ -204,7 +204,7 @@ TEST(Histtest, MultipleCreateHist) { hist->CreateHistogram(&data); PerValue = hist->Percentile(0.50, data); std::stringstream stream; - std::string expected("MultipleCreateHist:\t99% C.I. 15us-212us Avg: 126.380us Max: 212us\n"); + std::string expected("MultipleCreateHist:\tSum: 2.654ms 99% C.I. 15us-212us Avg: 126.380us Max: 212us\n"); hist->PrintConfidenceIntervals(stream, 0.99, data); EXPECT_EQ(expected, stream.str()); @@ -219,7 +219,7 @@ TEST(Histtest, SingleValue) { hist->AddValue(1); hist->CreateHistogram(&data); std::stringstream stream; - std::string expected = "SingleValue:\t99% C.I. 1us-1us Avg: 1us Max: 1us\n"; + std::string expected = "SingleValue:\tSum: 1us 99% C.I. 1us-1us Avg: 1us Max: 1us\n"; hist->PrintConfidenceIntervals(stream, 0.99, data); EXPECT_EQ(expected, stream.str()); } @@ -262,7 +262,7 @@ TEST(Histtest, SpikyValues) { hist->AddValue(10000); hist->CreateHistogram(&data); std::stringstream stream; - std::string expected = "SpikyValues:\t99% C.I. 0.089us-2541.825us Avg: 95.033us Max: 10000us\n"; + std::string expected = "SpikyValues:\tSum: 14.350ms 99% C.I. 0.089us-2541.825us Avg: 95.033us Max: 10000us\n"; hist->PrintConfidenceIntervals(stream, 0.99, data); EXPECT_EQ(expected, stream.str()); } From e9c36b34efb7460f59c6766e526c9b0de8da70b3 Mon Sep 17 00:00:00 2001 From: Vladimir Marko Date: Thu, 21 Nov 2013 15:49:16 +0000 Subject: [PATCH 0210/2402] Avoid some string allocations. Also avoid building a string one character at a time. Change-Id: I3db26226c620a730b95637d5bfc23e2d4715cfb9 --- compiler/driver/compiler_driver.cc | 4 ++-- runtime/dex_file.cc | 8 +++----- runtime/hprof/hprof.cc | 2 +- 3 files changed, 6 insertions(+), 8 deletions(-) diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc index b9df1d6f48a..7b428793ab1 100644 --- a/compiler/driver/compiler_driver.cc +++ b/compiler/driver/compiler_driver.cc @@ -697,11 +697,11 @@ void CompilerDriver::LoadImageClasses(TimingLogger& timings) ScopedObjectAccess soa(self); ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); for (auto it = image_classes_->begin(), end = image_classes_->end(); it != end;) { - std::string descriptor(*it); + const std::string& descriptor(*it); SirtRef klass(self, class_linker->FindSystemClass(descriptor.c_str())); if (klass.get() == NULL) { - image_classes_->erase(it++); VLOG(compiler) << "Failed to find class " << descriptor; + image_classes_->erase(it++); self->ClearException(); } else { ++it; diff --git a/runtime/dex_file.cc b/runtime/dex_file.cc index 0ddcdf30c93..a7575ce8890 100644 --- a/runtime/dex_file.cc +++ b/runtime/dex_file.cc @@ -555,22 +555,19 @@ bool DexFile::CreateTypeList(const StringPiece& signature, uint16_t* return_type size_t end = signature.size(); bool process_return = false; while (offset < end) { + size_t start_offset = offset; char c = signature[offset]; offset++; if (c == ')') { process_return = true; continue; } - // TODO: avoid building a string. - std::string descriptor; - descriptor += c; while (c == '[') { // process array prefix if (offset >= end) { // expect some descriptor following [ return false; } c = signature[offset]; offset++; - descriptor += c; } if (c == 'L') { // process type descriptors do { @@ -579,9 +576,10 @@ bool DexFile::CreateTypeList(const StringPiece& signature, uint16_t* return_type } c = signature[offset]; offset++; - descriptor += c; } while (c != ';'); } + // TODO: avoid creating a std::string just to get a 0-terminated char array + std::string descriptor(signature.data() + start_offset, offset - start_offset); const DexFile::StringId* string_id = FindStringId(descriptor.c_str()); if (string_id == NULL) { return false; diff --git a/runtime/hprof/hprof.cc b/runtime/hprof/hprof.cc index 67620a09f1b..9f899e87a3b 100644 --- a/runtime/hprof/hprof.cc +++ b/runtime/hprof/hprof.cc @@ -537,7 +537,7 @@ class Hprof { HprofRecord* rec = ¤t_record_; for (StringMapIterator it = strings_.begin(); it != strings_.end(); ++it) { - std::string string((*it).first); + const std::string& string = (*it).first; size_t id = (*it).second; int err = current_record_.StartNewRecord(header_fp_, HPROF_TAG_STRING, HPROF_TIME); From dcc5c7598d38fcb555266c8618df720acea3b954 Mon Sep 17 00:00:00 2001 From: Mathieu Chartier Date: Thu, 21 Nov 2013 10:13:31 -0800 Subject: [PATCH 0211/2402] Remove sleep workaround in thread pool. Not required after the race condition in bionic has been fixed. Bug: 11693195 Change-Id: Ib27ff79d86caa50676b40a0acd3cab071e65779f --- runtime/thread_pool.cc | 2 -- 1 file changed, 2 deletions(-) diff --git a/runtime/thread_pool.cc b/runtime/thread_pool.cc index 67fcd582207..aca0561a775 100644 --- a/runtime/thread_pool.cc +++ b/runtime/thread_pool.cc @@ -43,8 +43,6 @@ ThreadPoolWorker::ThreadPoolWorker(ThreadPool* thread_pool, const std::string& n ThreadPoolWorker::~ThreadPoolWorker() { CHECK_PTHREAD_CALL(pthread_join, (pthread_, NULL), "thread pool worker shutdown"); - // TODO: Delete this when race condition in pthread_join is fixed. - usleep(500); } void ThreadPoolWorker::Run() { From b2f9936cab87a187f078187c22d9b29d4a188a62 Mon Sep 17 00:00:00 2001 From: Mathieu Chartier Date: Wed, 20 Nov 2013 17:26:00 -0800 Subject: [PATCH 0212/2402] Add histogram for GC pause times. Printed when you dump the GC performance info. Bug: 10855285 Change-Id: I3bf7f958305f97c52cb31c03bdd6218c321575b9 --- runtime/base/histogram-inl.h | 3 ++- runtime/base/histogram.h | 2 +- runtime/gc/collector/garbage_collector.cc | 17 ++++++++++------- runtime/gc/collector/garbage_collector.h | 15 +++++++++++---- runtime/gc/collector/mark_sweep.cc | 3 --- runtime/gc/collector/semi_space.cc | 3 --- runtime/gc/heap.cc | 5 ++++- 7 files changed, 28 insertions(+), 20 deletions(-) diff --git a/runtime/base/histogram-inl.h b/runtime/base/histogram-inl.h index 4cd68cda446..7c0999992ec 100644 --- a/runtime/base/histogram-inl.h +++ b/runtime/base/histogram-inl.h @@ -178,7 +178,8 @@ inline void Histogram::PrintConfidenceIntervals(std::ostream &os, double os << FormatDuration(Max() * kAdjust, unit) << "\n"; } -template inline void Histogram::CreateHistogram(CumulativeData* out_data) { +template +inline void Histogram::CreateHistogram(CumulativeData* out_data) const { DCHECK_GT(sample_size_, 0ull); out_data->freq_.clear(); out_data->perc_.clear(); diff --git a/runtime/base/histogram.h b/runtime/base/histogram.h index d4eb1f41a5a..4e5d29a2977 100644 --- a/runtime/base/histogram.h +++ b/runtime/base/histogram.h @@ -49,7 +49,7 @@ template class Histogram { // cumulative_freq[i] = sum(frequency[j] : 0 < j < i ) // Accumulative summation of percentiles; which is the frequency / SampleSize // cumulative_perc[i] = sum(frequency[j] / SampleSize : 0 < j < i ) - void CreateHistogram(CumulativeData* data); + void CreateHistogram(CumulativeData* data) const; // Reset the cumulative values, next time CreateHistogram is called it will recreate the cache. void Reset(); double Mean() const; diff --git a/runtime/gc/collector/garbage_collector.cc b/runtime/gc/collector/garbage_collector.cc index 56d9ef4d4d0..cf301feafec 100644 --- a/runtime/gc/collector/garbage_collector.cc +++ b/runtime/gc/collector/garbage_collector.cc @@ -21,6 +21,7 @@ #include "garbage_collector.h" +#include "base/histogram-inl.h" #include "base/logging.h" #include "base/mutex-inl.h" #include "gc/accounting/heap_bitmap.h" @@ -40,6 +41,7 @@ GarbageCollector::GarbageCollector(Heap* heap, const std::string& name) verbose_(VLOG_IS_ON(heap)), duration_ns_(0), timings_(name_.c_str(), true, verbose_), + pause_histogram_((name_ + " paused").c_str(), kPauseBucketSize, kPauseBucketCount), cumulative_timings_(name) { ResetCumulativeStatistics(); } @@ -55,8 +57,8 @@ void GarbageCollector::RegisterPause(uint64_t nano_length) { void GarbageCollector::ResetCumulativeStatistics() { cumulative_timings_.Reset(); + pause_histogram_.Reset(); total_time_ns_ = 0; - total_paused_time_ns_ = 0; total_freed_objects_ = 0; total_freed_bytes_ = 0; } @@ -86,8 +88,7 @@ void GarbageCollector::Run(bool clear_soft_references) { GetHeap()->RevokeAllThreadLocalBuffers(); thread_list->ResumeAll(); ATRACE_END(); - uint64_t pause_end = NanoTime(); - pause_times_.push_back(pause_end - pause_start); + RegisterPause(NanoTime() - pause_start); } else { Thread* self = Thread::Current(); { @@ -110,18 +111,20 @@ void GarbageCollector::Run(bool clear_soft_references) { ATRACE_BEGIN("Resuming mutator threads"); thread_list->ResumeAll(); ATRACE_END(); - pause_times_.push_back(pause_end - pause_start); + RegisterPause(pause_end - pause_start); } { ReaderMutexLock mu(self, *Locks::mutator_lock_); ReclaimPhase(); } } - + FinishPhase(); uint64_t end_time = NanoTime(); duration_ns_ = end_time - start_time; - - FinishPhase(); + total_time_ns_ += GetDurationNs(); + for (uint64_t pause_time : pause_times_) { + pause_histogram_.AddValue(pause_time / 1000); + } } void GarbageCollector::SwapBitmaps() { diff --git a/runtime/gc/collector/garbage_collector.h b/runtime/gc/collector/garbage_collector.h index a80f5935c25..17793393fed 100644 --- a/runtime/gc/collector/garbage_collector.h +++ b/runtime/gc/collector/garbage_collector.h @@ -17,10 +17,10 @@ #ifndef ART_RUNTIME_GC_COLLECTOR_GARBAGE_COLLECTOR_H_ #define ART_RUNTIME_GC_COLLECTOR_GARBAGE_COLLECTOR_H_ +#include "base/histogram.h" +#include "base/timing_logger.h" #include "gc_type.h" #include "locks.h" -#include "base/timing_logger.h" - #include #include @@ -95,7 +95,7 @@ class GarbageCollector { } uint64_t GetTotalPausedTimeNs() const { - return total_paused_time_ns_; + return pause_histogram_.Sum(); } uint64_t GetTotalFreedBytes() const { @@ -106,6 +106,10 @@ class GarbageCollector { return total_freed_objects_; } + const Histogram& GetPauseHistogram() const { + return pause_histogram_; + } + protected: // The initial phase. Done without mutators paused. virtual void InitializePhase() = 0; @@ -122,6 +126,9 @@ class GarbageCollector { // Called after the GC is finished. Done without mutators paused. virtual void FinishPhase() = 0; + static constexpr size_t kPauseBucketSize = 500; + static constexpr size_t kPauseBucketCount = 32; + Heap* const heap_; std::string name_; @@ -134,8 +141,8 @@ class GarbageCollector { TimingLogger timings_; // Cumulative statistics. + Histogram pause_histogram_; uint64_t total_time_ns_; - uint64_t total_paused_time_ns_; uint64_t total_freed_objects_; uint64_t total_freed_bytes_; diff --git a/runtime/gc/collector/mark_sweep.cc b/runtime/gc/collector/mark_sweep.cc index 58068b1bcd9..0697a6515b9 100644 --- a/runtime/gc/collector/mark_sweep.cc +++ b/runtime/gc/collector/mark_sweep.cc @@ -1449,9 +1449,6 @@ void MarkSweep::FinishPhase() { heap->RequestHeapTrim(); // Update the cumulative statistics - total_time_ns_ += GetDurationNs(); - total_paused_time_ns_ += std::accumulate(GetPauseTimes().begin(), GetPauseTimes().end(), 0, - std::plus()); total_freed_objects_ += GetFreedObjects() + GetFreedLargeObjects(); total_freed_bytes_ += GetFreedBytes() + GetFreedLargeObjectBytes(); diff --git a/runtime/gc/collector/semi_space.cc b/runtime/gc/collector/semi_space.cc index 00794d69bcb..393935474a2 100644 --- a/runtime/gc/collector/semi_space.cc +++ b/runtime/gc/collector/semi_space.cc @@ -584,9 +584,6 @@ void SemiSpace::FinishPhase() { from_space_ = nullptr; // Update the cumulative statistics - total_time_ns_ += GetDurationNs(); - total_paused_time_ns_ += std::accumulate(GetPauseTimes().begin(), GetPauseTimes().end(), 0, - std::plus()); total_freed_objects_ += GetFreedObjects() + GetFreedLargeObjects(); total_freed_bytes_ += GetFreedBytes() + GetFreedLargeObjectBytes(); diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc index c31e3e982b2..ef9d157c50f 100644 --- a/runtime/gc/heap.cc +++ b/runtime/gc/heap.cc @@ -23,6 +23,7 @@ #include #include +#include "base/histogram-inl.h" #include "base/stl_util.h" #include "common_throws.h" #include "cutils/sched_policy.h" @@ -558,8 +559,10 @@ void Heap::DumpGcPerformanceInfo(std::ostream& os) { double seconds = NsToMs(logger.GetTotalNs()) / 1000.0; const uint64_t freed_bytes = collector->GetTotalFreedBytes(); const uint64_t freed_objects = collector->GetTotalFreedObjects(); + Histogram::CumulativeData cumulative_data; + collector->GetPauseHistogram().CreateHistogram(&cumulative_data); + collector->GetPauseHistogram().PrintConfidenceIntervals(os, 0.99, cumulative_data); os << collector->GetName() << " total time: " << PrettyDuration(total_ns) << "\n" - << collector->GetName() << " paused time: " << PrettyDuration(total_pause_ns) << "\n" << collector->GetName() << " freed: " << freed_objects << " objects with total size " << PrettySize(freed_bytes) << "\n" << collector->GetName() << " throughput: " << freed_objects / seconds << "/s / " From cbbb080e8b45e46ea43af521ba2a0b3d432702a7 Mon Sep 17 00:00:00 2001 From: Hiroshi Yamauchi Date: Thu, 21 Nov 2013 12:42:36 -0800 Subject: [PATCH 0213/2402] Fix a libartd.so boot crash in Heap::AllocObjectWithAllocator() Bug: 11806947 Change-Id: I826875f23ee2233d4128e852ff6fe7e26ced378f --- .../portable/portable_alloc_entrypoints.cc | 8 ++++---- runtime/entrypoints/quick/quick_alloc_entrypoints.cc | 12 ++++++++++-- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/runtime/entrypoints/portable/portable_alloc_entrypoints.cc b/runtime/entrypoints/portable/portable_alloc_entrypoints.cc index 6d23efe898e..0d575165d55 100644 --- a/runtime/entrypoints/portable/portable_alloc_entrypoints.cc +++ b/runtime/entrypoints/portable/portable_alloc_entrypoints.cc @@ -57,8 +57,8 @@ extern "C" mirror::Object* art_portable_check_and_alloc_array_from_code(uint32_t uint32_t length, Thread* thread) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - return CheckAndAllocArrayFromCode(type_idx, referrer, length, thread, false, - gc::kAllocatorTypeFreeList); + return CheckAndAllocArrayFromCodeInstrumented(type_idx, referrer, length, thread, false, + gc::kAllocatorTypeFreeList); } extern "C" mirror::Object* art_portable_check_and_alloc_array_from_code_with_access_check(uint32_t type_idx, @@ -66,8 +66,8 @@ extern "C" mirror::Object* art_portable_check_and_alloc_array_from_code_with_acc uint32_t length, Thread* thread) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - return CheckAndAllocArrayFromCode(type_idx, referrer, length, thread, true, - gc::kAllocatorTypeFreeList); + return CheckAndAllocArrayFromCodeInstrumented(type_idx, referrer, length, thread, true, + gc::kAllocatorTypeFreeList); } } // namespace art diff --git a/runtime/entrypoints/quick/quick_alloc_entrypoints.cc b/runtime/entrypoints/quick/quick_alloc_entrypoints.cc index b71b880939e..9155088796d 100644 --- a/runtime/entrypoints/quick/quick_alloc_entrypoints.cc +++ b/runtime/entrypoints/quick/quick_alloc_entrypoints.cc @@ -57,14 +57,22 @@ extern "C" mirror::Array* artCheckAndAllocArrayFromCode##suffix##suffix2( \ mirror::ArtMethod** sp) \ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { \ FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly); \ - return CheckAndAllocArrayFromCode(type_idx, method, component_count, self, false, allocator_type); \ + if (!instrumented_bool) { \ + return CheckAndAllocArrayFromCode(type_idx, method, component_count, self, false, allocator_type); \ + } else { \ + return CheckAndAllocArrayFromCodeInstrumented(type_idx, method, component_count, self, false, allocator_type); \ + } \ } \ extern "C" mirror::Array* artCheckAndAllocArrayFromCodeWithAccessCheck##suffix##suffix2( \ uint32_t type_idx, mirror::ArtMethod* method, int32_t component_count, Thread* self, \ mirror::ArtMethod** sp) \ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { \ FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly); \ - return CheckAndAllocArrayFromCode(type_idx, method, component_count, self, true, allocator_type); \ + if (!instrumented_bool) { \ + return CheckAndAllocArrayFromCode(type_idx, method, component_count, self, true, allocator_type); \ + } else { \ + return CheckAndAllocArrayFromCodeInstrumented(type_idx, method, component_count, self, true, allocator_type); \ + } \ } #define GENERATE_ENTRYPOINTS_FOR_ALLOCATOR(suffix, allocator_type) \ From f9ed0d38c596d22c57d57e6111819bbb09e7d0fb Mon Sep 17 00:00:00 2001 From: Mathieu Chartier Date: Thu, 21 Nov 2013 16:42:47 -0800 Subject: [PATCH 0214/2402] Fix concurrent GC to properly handle no zygote. I had introduced a regression in https://googleplex-android-review.googlesource.com/#/c/389851/ which caused concurrent GC to not get run if there was no zygote. This caused a regression in ritzperf when run from the commmand line. The fix properly handles the no zygote case by doing full GC instead. Bug: 11811477 Change-Id: Ib42b914509b951054895fea8741f6c68cdada52a --- runtime/gc/heap.cc | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc index ef9d157c50f..1cff719e652 100644 --- a/runtime/gc/heap.cc +++ b/runtime/gc/heap.cc @@ -2090,7 +2090,18 @@ void Heap::ConcurrentGC(Thread* self) { } // Wait for any GCs currently running to finish. if (WaitForGcToComplete(self) == collector::kGcTypeNone) { - CollectGarbageInternal(next_gc_type_, kGcCauseBackground, false); + // If the we can't run the GC type we wanted to run, find the next appropriate one and try that + // instead. E.g. can't do partial, so do full instead. + if (CollectGarbageInternal(next_gc_type_, kGcCauseBackground, false) == + collector::kGcTypeNone) { + for (collector::GcType gc_type : gc_plan_) { + // Attempt to run the collector, if we succeed, we are done. + if (gc_type > next_gc_type_ && + CollectGarbageInternal(gc_type, kGcCauseBackground, false) != collector::kGcTypeNone) { + break; + } + } + } } } From 201803fb1acd15b9daae51d816e1b08aededdc41 Mon Sep 17 00:00:00 2001 From: Jeff Hao Date: Wed, 20 Nov 2013 18:11:39 -0800 Subject: [PATCH 0215/2402] Search for miranda methods in virtual methods instead of interface. Also added tests that get miranda methods via reflection and jni. Miranda methods can't be found via reflection, and have the interface class as their declaring class when found via jni. Bug: 11736932 Change-Id: I92b4fdf31be64269898ed2686a28dfb6008b213a --- runtime/mirror/class-inl.h | 2 +- test/040-miranda/expected.txt | 2 ++ test/040-miranda/src/Main.java | 13 +++++++++++++ test/JniTest/JniTest.java | 22 ++++++++++++++++++++++ test/JniTest/jni_test.cc | 8 ++++++++ test/run-test | 23 +++++++++++++---------- 6 files changed, 59 insertions(+), 11 deletions(-) diff --git a/runtime/mirror/class-inl.h b/runtime/mirror/class-inl.h index 4dcce1e8a18..3a28974e4b2 100644 --- a/runtime/mirror/class-inl.h +++ b/runtime/mirror/class-inl.h @@ -248,7 +248,7 @@ inline ArtMethod* Class::FindVirtualMethodForVirtualOrInterface(ArtMethod* metho if (method->IsDirect()) { return method; } - if (method->GetDeclaringClass()->IsInterface()) { + if (method->GetDeclaringClass()->IsInterface() && !method->IsMiranda()) { return FindVirtualMethodForInterface(method); } return FindVirtualMethodForVirtual(method); diff --git a/test/040-miranda/expected.txt b/test/040-miranda/expected.txt index e22bbd974c4..011be2af86d 100644 --- a/test/040-miranda/expected.txt +++ b/test/040-miranda/expected.txt @@ -10,3 +10,5 @@ MirandaAbstract / MirandaClass2: inInterface: true inInterface2: 28 inAbstract: true +Test getting miranda method via reflection: + caught expected NoSuchMethodException diff --git a/test/040-miranda/src/Main.java b/test/040-miranda/src/Main.java index 1fd8287ba0c..ff5eba0a17c 100644 --- a/test/040-miranda/src/Main.java +++ b/test/040-miranda/src/Main.java @@ -14,6 +14,8 @@ * limitations under the License. */ +import java.lang.reflect.Method; + /** * Miranda testing. */ @@ -37,5 +39,16 @@ public static void main(String[] args) { System.out.println(" inInterface: " + mira2.inInterface()); System.out.println(" inInterface2: " + mira2.inInterface2()); System.out.println(" inAbstract: " + mira2.inAbstract()); + + System.out.println("Test getting miranda method via reflection:"); + try { + Class mirandaClass = Class.forName("MirandaAbstract"); + Method mirandaMethod = mirandaClass.getDeclaredMethod("inInterface", (Class[]) null); + System.out.println(" did not expect to find miranda method"); + } catch (NoSuchMethodException nsme) { + System.out.println(" caught expected NoSuchMethodException"); + } catch (Exception e) { + System.out.println(" caught unexpected exception " + e); + } } } diff --git a/test/JniTest/JniTest.java b/test/JniTest/JniTest.java index 7014ef93342..a1b1f0c69dd 100644 --- a/test/JniTest/JniTest.java +++ b/test/JniTest/JniTest.java @@ -14,11 +14,14 @@ * limitations under the License. */ +import java.lang.reflect.Method; + class JniTest { public static void main(String[] args) { System.loadLibrary("arttest"); testFindClassOnAttachedNativeThread(); testCallStaticVoidMethodOnSubClass(); + testGetMirandaMethod(); } private static native void testFindClassOnAttachedNativeThread(); @@ -42,4 +45,23 @@ private static void execute() { private static class testCallStaticVoidMethodOnSubClass_SubClass extends testCallStaticVoidMethodOnSubClass_SuperClass { } + + private static native Method testGetMirandaMethodNative(); + + private static void testGetMirandaMethod() { + Method m = testGetMirandaMethodNative(); + if (m.getDeclaringClass() != testGetMirandaMethod_MirandaInterface.class) { + throw new AssertionError(); + } + } + + private static abstract class testGetMirandaMethod_MirandaAbstract implements testGetMirandaMethod_MirandaInterface { + public boolean inAbstract() { + return true; + } + } + + private static interface testGetMirandaMethod_MirandaInterface { + public boolean inInterface(); + } } diff --git a/test/JniTest/jni_test.cc b/test/JniTest/jni_test.cc index 72a3309d9dd..cfcbb64f38f 100644 --- a/test/JniTest/jni_test.cc +++ b/test/JniTest/jni_test.cc @@ -81,3 +81,11 @@ extern "C" JNIEXPORT void JNICALL Java_JniTest_testCallStaticVoidMethodOnSubClas env->CallStaticVoidMethod(sub_class, execute); } + +extern "C" JNIEXPORT jobject JNICALL Java_JniTest_testGetMirandaMethodNative(JNIEnv* env, jclass) { + jclass abstract_class = env->FindClass("JniTest$testGetMirandaMethod_MirandaAbstract"); + assert(abstract_class != NULL); + jmethodID miranda_method = env->GetMethodID(abstract_class, "inInterface", "()Z"); + assert(miranda_method != NULL); + return env->ToReflectedMethod(abstract_class, miranda_method, JNI_FALSE); +} diff --git a/test/run-test b/test/run-test index f706110a2c8..c3943e70bef 100755 --- a/test/run-test +++ b/test/run-test @@ -65,7 +65,7 @@ target_mode="yes" dev_mode="no" update_mode="no" debug_mode="no" -dalvik_mode="no" +runtime="art" usage="no" build_only="no" @@ -77,6 +77,7 @@ while true; do shift elif [ "x$1" = "x--jvm" ]; then target_mode="no" + runtime="jvm" RUN="${progdir}/etc/reference-run-test-classes" NEED_DEX="false" shift @@ -85,7 +86,7 @@ while true; do shift elif [ "x$1" = "x--dalvik" ]; then lib="libdvm.so" - dalvik_mode="yes" + runtime="dalvik" shift elif [ "x$1" = "x--image" ]; then shift @@ -155,15 +156,11 @@ while true; do fi done -run_args="${run_args} --lib $lib" +if [ ! "$runtime" = "jvm" ]; then + run_args="${run_args} --lib $lib" +fi -if [ "$dalvik_mode" = "no" ]; then - if [ "$target_mode" = "no" ]; then - run_args="${run_args} --boot -Ximage:${ANDROID_HOST_OUT}/framework/core.art" - else - run_args="${run_args} --boot -Ximage:/data/art-test/core.art" - fi -else +if [ "$runtime" = "dalvik" ]; then if [ "$target_mode" = "no" ]; then framework="${OUT}/system/framework" bpath="${framework}/core.jar:${framework}/conscrypt.jar:${framework}/okhttp.jar:${framework}/core-junit.jar:${framework}/bouncycastle.jar:${framework}/ext.jar" @@ -171,6 +168,12 @@ else else true # defaults to using target BOOTCLASSPATH fi +elif [ "$runtime" = "art" ]; then + if [ "$target_mode" = "no" ]; then + run_args="${run_args} --boot -Ximage:${ANDROID_HOST_OUT}/framework/core.art" + else + run_args="${run_args} --boot -Ximage:/data/art-test/core.art" + fi fi if [ "$dev_mode" = "yes" -a "$update_mode" = "yes" ]; then From 1c282e2b9a9b432e132b2c332f861cad9feb4a73 Mon Sep 17 00:00:00 2001 From: Vladimir Marko Date: Thu, 21 Nov 2013 14:49:47 +0000 Subject: [PATCH 0216/2402] Refactor intrinsic CAS, prepare for 64-bit version. Bug: 11391018 Change-Id: Ic0f740e0cd0eb47f2c915f81be02f52f7721f8a3 --- .../dex/quick/arm/arm_dex_file_method_inliner.cc | 10 ++++++---- compiler/dex/quick/arm/codegen_arm.h | 2 +- compiler/dex/quick/arm/int_arm.cc | 13 +++++++------ compiler/dex/quick/dex_file_method_inliner.cc | 9 +++++++-- compiler/dex/quick/dex_file_method_inliner.h | 14 +++++++------- compiler/dex/quick/mips/codegen_mips.h | 2 +- compiler/dex/quick/mips/int_mips.cc | 2 +- .../dex/quick/mips/mips_dex_file_method_inliner.cc | 10 ++++++---- compiler/dex/quick/mir_to_lir.h | 2 +- compiler/dex/quick/x86/codegen_x86.h | 2 +- compiler/dex/quick/x86/int_x86.cc | 2 +- .../dex/quick/x86/x86_dex_file_method_inliner.cc | 10 ++++++---- 12 files changed, 45 insertions(+), 33 deletions(-) diff --git a/compiler/dex/quick/arm/arm_dex_file_method_inliner.cc b/compiler/dex/quick/arm/arm_dex_file_method_inliner.cc index a8ae3cd72c9..257b2c4e945 100644 --- a/compiler/dex/quick/arm/arm_dex_file_method_inliner.cc +++ b/compiler/dex/quick/arm/arm_dex_file_method_inliner.cc @@ -64,10 +64,12 @@ const DexFileMethodInliner::IntrinsicDef ArmDexFileMethodInliner::kIntrinsicMeth INTRINSIC(LibcoreIoMemory, PokeLongNative, JJ_V, kIntrinsicPoke, kLong), INTRINSIC(LibcoreIoMemory, PokeShortNative, JS_V, kIntrinsicPoke, kSignedHalf), - INTRINSIC(SunMiscUnsafe, CompareAndSwapInt, ObjectJII_Z, kIntrinsicCas32, - kIntrinsicFlagDontNeedWriteBarrier), - INTRINSIC(SunMiscUnsafe, CompareAndSwapObject, ObjectJObjectObject_Z, kIntrinsicCas32, - kIntrinsicFlagNeedWriteBarrier), + INTRINSIC(SunMiscUnsafe, CompareAndSwapInt, ObjectJII_Z, kIntrinsicCas, + kIntrinsicFlagNone), + // INTRINSIC(SunMiscUnsafe, CompareAndSwapLong, ObjectJJJ_Z, kIntrinsicCas, + // kIntrinsicFlagIsLong), + INTRINSIC(SunMiscUnsafe, CompareAndSwapObject, ObjectJObjectObject_Z, kIntrinsicCas, + kIntrinsicFlagIsObject), #define UNSAFE_GET_PUT(type, code, type_flags) \ INTRINSIC(SunMiscUnsafe, Get ## type, ObjectJ_ ## code, kIntrinsicUnsafeGet, \ diff --git a/compiler/dex/quick/arm/codegen_arm.h b/compiler/dex/quick/arm/codegen_arm.h index 15355be9d7c..de3223a106c 100644 --- a/compiler/dex/quick/arm/codegen_arm.h +++ b/compiler/dex/quick/arm/codegen_arm.h @@ -104,7 +104,7 @@ class ArmMir2Lir : public Mir2Lir { void GenCmpFP(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2); void GenConversion(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src); - bool GenInlinedCas32(CallInfo* info, bool need_write_barrier); + bool GenInlinedCas(CallInfo* info, bool is_long, bool is_object); bool GenInlinedMinMaxInt(CallInfo* info, bool is_min); bool GenInlinedSqrt(CallInfo* info); bool GenInlinedPeek(CallInfo* info, OpSize size); diff --git a/compiler/dex/quick/arm/int_arm.cc b/compiler/dex/quick/arm/int_arm.cc index 9f84b0341f0..97271794f7d 100644 --- a/compiler/dex/quick/arm/int_arm.cc +++ b/compiler/dex/quick/arm/int_arm.cc @@ -560,14 +560,15 @@ void ArmMir2Lir::OpTlsCmp(ThreadOffset offset, int val) { LOG(FATAL) << "Unexpected use of OpTlsCmp for Arm"; } -bool ArmMir2Lir::GenInlinedCas32(CallInfo* info, bool need_write_barrier) { +bool ArmMir2Lir::GenInlinedCas(CallInfo* info, bool is_long, bool is_object) { + DCHECK(!is_long); // not supported yet DCHECK_EQ(cu_->instruction_set, kThumb2); // Unused - RegLocation rl_src_unsafe = info->args[0]; - RegLocation rl_src_obj= info->args[1]; // Object - known non-null - RegLocation rl_src_offset= info->args[2]; // long low + RegLocation rl_src_obj = info->args[1]; // Object - known non-null + RegLocation rl_src_offset = info->args[2]; // long low rl_src_offset.wide = 0; // ignore high half in info->args[3] - RegLocation rl_src_expected= info->args[4]; // int or Object - RegLocation rl_src_new_value= info->args[5]; // int or Object + RegLocation rl_src_expected = info->args[4]; // int, long or Object + RegLocation rl_src_new_value = info->args[5]; // int, long or Object RegLocation rl_dest = InlineTarget(info); // boolean place for result @@ -577,7 +578,7 @@ bool ArmMir2Lir::GenInlinedCas32(CallInfo* info, bool need_write_barrier) { RegLocation rl_object = LoadValue(rl_src_obj, kCoreReg); RegLocation rl_new_value = LoadValue(rl_src_new_value, kCoreReg); - if (need_write_barrier && !mir_graph_->IsConstantNullRef(rl_new_value)) { + if (is_object && !mir_graph_->IsConstantNullRef(rl_new_value)) { // Mark card for object assuming new value is stored. MarkGCCard(rl_new_value.low_reg, rl_object.low_reg); } diff --git a/compiler/dex/quick/dex_file_method_inliner.cc b/compiler/dex/quick/dex_file_method_inliner.cc index 6a760fa2952..6c0328edb0d 100644 --- a/compiler/dex/quick/dex_file_method_inliner.cc +++ b/compiler/dex/quick/dex_file_method_inliner.cc @@ -71,6 +71,7 @@ const char* DexFileMethodInliner::kNameCacheNames[] = { "pokeLongNative", // kNameCachePokeLongNative "pokeShortNative", // kNameCachePokeShortNative "compareAndSwapInt", // kNameCacheCompareAndSwapInt + "compareAndSwapLong", // kNameCacheCompareAndSwapLong "compareAndSwapObject", // kNameCacheCompareAndSwapObject "getInt", // kNameCacheGetInt "getIntVolatile", // kNameCacheGetIntVolatile @@ -135,6 +136,9 @@ const DexFileMethodInliner::ProtoDef DexFileMethodInliner::kProtoCacheDefs[] = { // kProtoCacheObjectJII_Z { kClassCacheBoolean, 4, { kClassCacheJavaLangObject, kClassCacheLong, kClassCacheInt, kClassCacheInt } }, + // kProtoCacheObjectJJJ_Z + { kClassCacheBoolean, 4, { kClassCacheJavaLangObject, kClassCacheLong, + kClassCacheLong, kClassCacheLong } }, // kProtoCacheObjectJObjectObject_Z { kClassCacheBoolean, 4, { kClassCacheJavaLangObject, kClassCacheLong, kClassCacheJavaLangObject, kClassCacheJavaLangObject } }, @@ -205,8 +209,9 @@ bool DexFileMethodInliner::GenIntrinsic(Mir2Lir* backend, CallInfo* info) const return backend->GenInlinedPeek(info, static_cast(intrinsic.data)); case kIntrinsicPoke: return backend->GenInlinedPoke(info, static_cast(intrinsic.data)); - case kIntrinsicCas32: - return backend->GenInlinedCas32(info, intrinsic.data & kIntrinsicFlagNeedWriteBarrier); + case kIntrinsicCas: + return backend->GenInlinedCas(info, intrinsic.data & kIntrinsicFlagIsLong, + intrinsic.data & kIntrinsicFlagIsObject); case kIntrinsicUnsafeGet: return backend->GenInlinedUnsafeGet(info, intrinsic.data & kIntrinsicFlagIsLong, intrinsic.data & kIntrinsicFlagIsVolatile); diff --git a/compiler/dex/quick/dex_file_method_inliner.h b/compiler/dex/quick/dex_file_method_inliner.h index 95b8dd3adfb..bc005136ba7 100644 --- a/compiler/dex/quick/dex_file_method_inliner.h +++ b/compiler/dex/quick/dex_file_method_inliner.h @@ -41,7 +41,7 @@ enum IntrinsicOpcode { kIntrinsicCurrentThread, kIntrinsicPeek, kIntrinsicPoke, - kIntrinsicCas32, + kIntrinsicCas, kIntrinsicUnsafeGet, kIntrinsicUnsafePut, }; @@ -60,15 +60,13 @@ enum IntrinsicFlags { // kIntrinsicIndexOf kIntrinsicFlagBase0 = 1, - // kIntrinsicUnsafeCas32 - kIntrinsicFlagDontNeedWriteBarrier = 0, - kIntrinsicFlagNeedWriteBarrier = 1, - - // kIntrinsicUnsafeGet, kIntrinsicUnsafePut + // kIntrinsicUnsafeGet, kIntrinsicUnsafePut, kIntrinsicUnsafeCas kIntrinsicFlagIsLong = 1, + // kIntrinsicUnsafeGet, kIntrinsicUnsafePut kIntrinsicFlagIsVolatile = 2, - // kIntrinsicUnsafePut + // kIntrinsicUnsafePut, kIntrinsicUnsafeCas kIntrinsicFlagIsObject = 4, + // kIntrinsicUnsafePut kIntrinsicFlagIsOrdered = 8, }; @@ -176,6 +174,7 @@ class DexFileMethodInliner { kNameCachePokeLongNative, kNameCachePokeShortNative, kNameCacheCompareAndSwapInt, + kNameCacheCompareAndSwapLong, kNameCacheCompareAndSwapObject, kNameCacheGetInt, kNameCacheGetIntVolatile, @@ -224,6 +223,7 @@ class DexFileMethodInliner { kProtoCacheJJ_V, kProtoCacheJS_V, kProtoCacheObjectJII_Z, + kProtoCacheObjectJJJ_Z, kProtoCacheObjectJObjectObject_Z, kProtoCacheObjectJ_I, kProtoCacheObjectJI_V, diff --git a/compiler/dex/quick/mips/codegen_mips.h b/compiler/dex/quick/mips/codegen_mips.h index 88b244ba909..5dda44563b9 100644 --- a/compiler/dex/quick/mips/codegen_mips.h +++ b/compiler/dex/quick/mips/codegen_mips.h @@ -104,7 +104,7 @@ class MipsMir2Lir : public Mir2Lir { void GenCmpFP(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2); void GenConversion(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src); - bool GenInlinedCas32(CallInfo* info, bool need_write_barrier); + bool GenInlinedCas(CallInfo* info, bool is_long, bool is_object); bool GenInlinedMinMaxInt(CallInfo* info, bool is_min); bool GenInlinedSqrt(CallInfo* info); bool GenInlinedPeek(CallInfo* info, OpSize size); diff --git a/compiler/dex/quick/mips/int_mips.cc b/compiler/dex/quick/mips/int_mips.cc index 52294290c93..dfff26062c2 100644 --- a/compiler/dex/quick/mips/int_mips.cc +++ b/compiler/dex/quick/mips/int_mips.cc @@ -258,7 +258,7 @@ void MipsMir2Lir::OpTlsCmp(ThreadOffset offset, int val) { LOG(FATAL) << "Unexpected use of OpTlsCmp for Arm"; } -bool MipsMir2Lir::GenInlinedCas32(CallInfo* info, bool need_write_barrier) { +bool MipsMir2Lir::GenInlinedCas(CallInfo* info, bool is_long, bool is_object) { DCHECK_NE(cu_->instruction_set, kThumb2); return false; } diff --git a/compiler/dex/quick/mips/mips_dex_file_method_inliner.cc b/compiler/dex/quick/mips/mips_dex_file_method_inliner.cc index e5345ec1f59..05d8ac802f9 100644 --- a/compiler/dex/quick/mips/mips_dex_file_method_inliner.cc +++ b/compiler/dex/quick/mips/mips_dex_file_method_inliner.cc @@ -64,10 +64,12 @@ const DexFileMethodInliner::IntrinsicDef MipsDexFileMethodInliner::kIntrinsicMet // INTRINSIC(LibcoreIoMemory, PokeLongNative, JJ_V, kIntrinsicPoke, kLong), // INTRINSIC(LibcoreIoMemory, PokeShortNative, JS_V, kIntrinsicPoke, kSignedHalf), - // INTRINSIC(SunMiscUnsafe, CompareAndSwapInt, ObjectJII_Z, kIntrinsicCas32, - // kIntrinsicFlagDontNeedWriteBarrier), - // INTRINSIC(SunMiscUnsafe, CompareAndSwapObject, ObjectJObjectObject_Z, kIntrinsicCas32, - // kIntrinsicFlagNeedWriteBarrier), + // INTRINSIC(SunMiscUnsafe, CompareAndSwapInt, ObjectJII_Z, kIntrinsicCas, + // kIntrinsicFlagNone), + // INTRINSIC(SunMiscUnsafe, CompareAndSwapLong, ObjectJJJ_Z, kIntrinsicCas, + // kIntrinsicFlagIsLong), + // INTRINSIC(SunMiscUnsafe, CompareAndSwapObject, ObjectJObjectObject_Z, kIntrinsicCas, + // kIntrinsicFlagIsObject), #define UNSAFE_GET_PUT(type, code, type_flags) \ INTRINSIC(SunMiscUnsafe, Get ## type, ObjectJ_ ## code, kIntrinsicUnsafeGet, \ diff --git a/compiler/dex/quick/mir_to_lir.h b/compiler/dex/quick/mir_to_lir.h index 58a77c7a0a0..ad9b0de3d53 100644 --- a/compiler/dex/quick/mir_to_lir.h +++ b/compiler/dex/quick/mir_to_lir.h @@ -661,7 +661,7 @@ class Mir2Lir : public Backend { RegLocation rl_src1, RegLocation rl_src2) = 0; virtual void GenConversion(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src) = 0; - virtual bool GenInlinedCas32(CallInfo* info, bool need_write_barrier) = 0; + virtual bool GenInlinedCas(CallInfo* info, bool is_long, bool is_object) = 0; virtual bool GenInlinedMinMaxInt(CallInfo* info, bool is_min) = 0; virtual bool GenInlinedSqrt(CallInfo* info) = 0; virtual bool GenInlinedPeek(CallInfo* info, OpSize size) = 0; diff --git a/compiler/dex/quick/x86/codegen_x86.h b/compiler/dex/quick/x86/codegen_x86.h index 1d6509eea5c..ffe2d67cf00 100644 --- a/compiler/dex/quick/x86/codegen_x86.h +++ b/compiler/dex/quick/x86/codegen_x86.h @@ -104,7 +104,7 @@ class X86Mir2Lir : public Mir2Lir { void GenCmpFP(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2); void GenConversion(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src); - bool GenInlinedCas32(CallInfo* info, bool need_write_barrier); + bool GenInlinedCas(CallInfo* info, bool is_long, bool is_object); bool GenInlinedMinMaxInt(CallInfo* info, bool is_min); bool GenInlinedSqrt(CallInfo* info); bool GenInlinedPeek(CallInfo* info, OpSize size); diff --git a/compiler/dex/quick/x86/int_x86.cc b/compiler/dex/quick/x86/int_x86.cc index 499547bb377..01d5c1770d5 100644 --- a/compiler/dex/quick/x86/int_x86.cc +++ b/compiler/dex/quick/x86/int_x86.cc @@ -281,7 +281,7 @@ void X86Mir2Lir::OpTlsCmp(ThreadOffset offset, int val) { NewLIR2(kX86Cmp16TI8, offset.Int32Value(), val); } -bool X86Mir2Lir::GenInlinedCas32(CallInfo* info, bool need_write_barrier) { +bool X86Mir2Lir::GenInlinedCas(CallInfo* info, bool is_long, bool is_object) { DCHECK_NE(cu_->instruction_set, kThumb2); return false; } diff --git a/compiler/dex/quick/x86/x86_dex_file_method_inliner.cc b/compiler/dex/quick/x86/x86_dex_file_method_inliner.cc index 87f881aa8b6..b788c3ce056 100644 --- a/compiler/dex/quick/x86/x86_dex_file_method_inliner.cc +++ b/compiler/dex/quick/x86/x86_dex_file_method_inliner.cc @@ -64,10 +64,12 @@ const DexFileMethodInliner::IntrinsicDef X86DexFileMethodInliner::kIntrinsicMeth INTRINSIC(LibcoreIoMemory, PokeLongNative, JJ_V, kIntrinsicPoke, kLong), INTRINSIC(LibcoreIoMemory, PokeShortNative, JS_V, kIntrinsicPoke, kSignedHalf), - // INTRINSIC(SunMiscUnsafe, CompareAndSwapInt, ObjectJII_Z, kIntrinsicCas32, - // kIntrinsicFlagDontNeedWriteBarrier), - // INTRINSIC(SunMiscUnsafe, CompareAndSwapObject, ObjectJObjectObject_Z, kIntrinsicCas32, - // kIntrinsicFlagNeedWriteBarrier), + // INTRINSIC(SunMiscUnsafe, CompareAndSwapInt, ObjectJII_Z, kIntrinsicCas, + // kIntrinsicFlagNone), + // INTRINSIC(SunMiscUnsafe, CompareAndSwapLong, ObjectJJJ_Z, kIntrinsicCas, + // kIntrinsicFlagIsLong), + // INTRINSIC(SunMiscUnsafe, CompareAndSwapObject, ObjectJObjectObject_Z, kIntrinsicCas, + // kIntrinsicFlagIsObject), #define UNSAFE_GET_PUT(type, code, type_flags) \ INTRINSIC(SunMiscUnsafe, Get ## type, ObjectJ_ ## code, kIntrinsicUnsafeGet, \ From 4137f4828a6a7c48aa1b161cecb82e1e0880aa16 Mon Sep 17 00:00:00 2001 From: Jeff Hao Date: Fri, 22 Nov 2013 11:44:57 -0800 Subject: [PATCH 0217/2402] Make verifier log hard failures by default. Previously required verbose verifier logging to see reasons for hard failures. Change-Id: I2d05dfec95aeb48e8e1d9d3e4386916ab90071a4 --- runtime/verifier/method_verifier.cc | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc index c0e03706328..1e45c604756 100644 --- a/runtime/verifier/method_verifier.cc +++ b/runtime/verifier/method_verifier.cc @@ -1098,10 +1098,8 @@ bool MethodVerifier::VerifyCodeFlow() { std::ostream& MethodVerifier::DumpFailures(std::ostream& os) { DCHECK_EQ(failures_.size(), failure_messages_.size()); - if (VLOG_IS_ON(verifier)) { - for (size_t i = 0; i < failures_.size(); ++i) { - os << failure_messages_[i]->str() << "\n"; - } + for (size_t i = 0; i < failures_.size(); ++i) { + os << failure_messages_[i]->str() << "\n"; } return os; } From 3c2856e939f3daa7a95a1f8cb70f47e7a621db3c Mon Sep 17 00:00:00 2001 From: Hiroshi Yamauchi Date: Fri, 22 Nov 2013 13:42:53 -0800 Subject: [PATCH 0218/2402] Inline RosAlloc::Alloc(). Bug: 9986565 Change-Id: I9bc411b8ae39379f9d730f40974857a585405fde --- runtime/gc/allocator/rosalloc-inl.h | 45 +++++++++++++++++++++ runtime/gc/allocator/rosalloc.cc | 57 ++++++++++----------------- runtime/gc/allocator/rosalloc.h | 9 +++++ runtime/gc/space/rosalloc_space-inl.h | 1 + 4 files changed, 76 insertions(+), 36 deletions(-) create mode 100644 runtime/gc/allocator/rosalloc-inl.h diff --git a/runtime/gc/allocator/rosalloc-inl.h b/runtime/gc/allocator/rosalloc-inl.h new file mode 100644 index 00000000000..f395314a4d2 --- /dev/null +++ b/runtime/gc/allocator/rosalloc-inl.h @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ART_RUNTIME_GC_ALLOCATOR_ROSALLOC_INL_H_ +#define ART_RUNTIME_GC_ALLOCATOR_ROSALLOC_INL_H_ + +#include "rosalloc.h" + +namespace art { +namespace gc { +namespace allocator { + +inline ALWAYS_INLINE void* RosAlloc::Alloc(Thread* self, size_t size, size_t* bytes_allocated) { + if (UNLIKELY(size > kLargeSizeThreshold)) { + return AllocLargeObject(self, size, bytes_allocated); + } + void* m = AllocFromRun(self, size, bytes_allocated); + // Check if the returned memory is really all zero. + if (kCheckZeroMemory && m != NULL) { + byte* bytes = reinterpret_cast(m); + for (size_t i = 0; i < size; ++i) { + DCHECK_EQ(bytes[i], 0); + } + } + return m; +} + +} // namespace allocator +} // namespace gc +} // namespace art + +#endif // ART_RUNTIME_GC_ALLOCATOR_ROSALLOC_INL_H_ diff --git a/runtime/gc/allocator/rosalloc.cc b/runtime/gc/allocator/rosalloc.cc index 9e658948952..3030fa7fec4 100644 --- a/runtime/gc/allocator/rosalloc.cc +++ b/runtime/gc/allocator/rosalloc.cc @@ -27,11 +27,6 @@ namespace art { namespace gc { namespace allocator { -// If true, log verbose details of operations. -static const bool kTraceRosAlloc = false; -// If true, check that the returned memory is actually zero. -static const bool kCheckZeroMemory = kIsDebugBuild; - extern "C" void* art_heap_rosalloc_morecore(RosAlloc* rosalloc, intptr_t increment); size_t RosAlloc::bracketSizes[kNumOfSizeBrackets]; @@ -401,44 +396,34 @@ void RosAlloc::FreePages(Thread* self, void* ptr) { } } -void* RosAlloc::Alloc(Thread* self, size_t size, size_t* bytes_allocated) { - if (UNLIKELY(size > kLargeSizeThreshold)) { - size_t num_pages = RoundUp(size, kPageSize) / kPageSize; - void* r; - { - MutexLock mu(self, lock_); - r = AllocPages(self, num_pages, kPageMapLargeObject); - } - if (bytes_allocated != NULL) { - *bytes_allocated = num_pages * kPageSize; - } - if (kTraceRosAlloc) { - if (r != NULL) { - LOG(INFO) << "RosAlloc::Alloc() : (large obj) 0x" << std::hex << reinterpret_cast(r) - << "-0x" << (reinterpret_cast(r) + num_pages * kPageSize) - << "(" << std::dec << (num_pages * kPageSize) << ")"; - } else { - LOG(INFO) << "RosAlloc::Alloc() : (large obj) NULL"; - } - } - // Check if the returned memory is really all zero. - if (kCheckZeroMemory && r != NULL) { - byte* bytes = reinterpret_cast(r); - for (size_t i = 0; i < size; ++i) { - DCHECK_EQ(bytes[i], 0); - } +void* RosAlloc::AllocLargeObject(Thread* self, size_t size, size_t* bytes_allocated) { + DCHECK(size > kLargeSizeThreshold); + size_t num_pages = RoundUp(size, kPageSize) / kPageSize; + void* r; + { + MutexLock mu(self, lock_); + r = AllocPages(self, num_pages, kPageMapLargeObject); + } + if (bytes_allocated != NULL) { + *bytes_allocated = num_pages * kPageSize; + } + if (kTraceRosAlloc) { + if (r != NULL) { + LOG(INFO) << "RosAlloc::AllocLargeObject() : 0x" << std::hex << reinterpret_cast(r) + << "-0x" << (reinterpret_cast(r) + num_pages * kPageSize) + << "(" << std::dec << (num_pages * kPageSize) << ")"; + } else { + LOG(INFO) << "RosAlloc::AllocLargeObject() : NULL"; } - return r; } - void* m = AllocFromRun(self, size, bytes_allocated); // Check if the returned memory is really all zero. - if (kCheckZeroMemory && m != NULL) { - byte* bytes = reinterpret_cast(m); + if (kCheckZeroMemory && r != NULL) { + byte* bytes = reinterpret_cast(r); for (size_t i = 0; i < size; ++i) { DCHECK_EQ(bytes[i], 0); } } - return m; + return r; } void RosAlloc::FreeInternal(Thread* self, void* ptr) { diff --git a/runtime/gc/allocator/rosalloc.h b/runtime/gc/allocator/rosalloc.h index c81306ffd55..d5b6de1637a 100644 --- a/runtime/gc/allocator/rosalloc.h +++ b/runtime/gc/allocator/rosalloc.h @@ -345,6 +345,12 @@ class RosAlloc { // runs for the rest. static const size_t kMaxThreadLocalSizeBracketIdx = 10; + // If true, check that the returned memory is actually zero. + static constexpr bool kCheckZeroMemory = kIsDebugBuild; + + // If true, log verbose details of operations. + static constexpr bool kTraceRosAlloc = false; + struct hash_run { size_t operator()(const RosAlloc::Run* r) const { return reinterpret_cast(r); @@ -429,6 +435,9 @@ class RosAlloc { // The internal of non-bulk Free(). void FreeInternal(Thread* self, void* ptr) LOCKS_EXCLUDED(lock_); + // Allocates large objects. + void* AllocLargeObject(Thread* self, size_t size, size_t* bytes_allocated) LOCKS_EXCLUDED(lock_); + public: RosAlloc(void* base, size_t capacity); void* Alloc(Thread* self, size_t size, size_t* bytes_allocated) diff --git a/runtime/gc/space/rosalloc_space-inl.h b/runtime/gc/space/rosalloc_space-inl.h index 0fe5dd7a95a..5de4265dc2d 100644 --- a/runtime/gc/space/rosalloc_space-inl.h +++ b/runtime/gc/space/rosalloc_space-inl.h @@ -17,6 +17,7 @@ #ifndef ART_RUNTIME_GC_SPACE_ROSALLOC_SPACE_INL_H_ #define ART_RUNTIME_GC_SPACE_ROSALLOC_SPACE_INL_H_ +#include "gc/allocator/rosalloc-inl.h" #include "rosalloc_space.h" #include "thread.h" From 95a659fca9cb89d5cf592ad5808d38f0117a583e Mon Sep 17 00:00:00 2001 From: Hiroshi Yamauchi Date: Fri, 22 Nov 2013 14:43:45 -0800 Subject: [PATCH 0219/2402] Fix a libartd.so boot crash when kMovingCollector is true. Bug: 11828452 Change-Id: I208a99d7ee7cb6af37046387e97156e3b240cda6 --- runtime/gc/heap-inl.h | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/runtime/gc/heap-inl.h b/runtime/gc/heap-inl.h index 9b285555284..5eda0b95b84 100644 --- a/runtime/gc/heap-inl.h +++ b/runtime/gc/heap-inl.h @@ -140,11 +140,10 @@ inline mirror::Object* Heap::TryToAllocate(Thread* self, AllocatorType allocator } case kAllocatorTypeLOS: { ret = large_object_space_->Alloc(self, alloc_size, bytes_allocated); - // Make sure that our large object didn't get placed anywhere within the space interval or - // else it breaks the immune range. - DCHECK(ret == nullptr || - reinterpret_cast(ret) < continuous_spaces_.front()->Begin() || - reinterpret_cast(ret) >= continuous_spaces_.back()->End()); + // Note that the bump pointer spaces aren't necessarily next to + // the other continuous spaces like the non-moving alloc space or + // the zygote space. + DCHECK(ret == nullptr || large_object_space_->Contains(ret)); break; } default: { From 50482234bed852766498321f71d2ff5e46e4fec2 Mon Sep 17 00:00:00 2001 From: Mathieu Chartier Date: Thu, 21 Nov 2013 11:48:14 -0800 Subject: [PATCH 0220/2402] Compact zygote. We now start out using the bump pointer allocator and switch to the free list collector (ROSAlloc) after the zygote forks. Before compaction: Zygote size: 9060352 After compaction Zygote size: 8425864 The main reason the size doesn't reduce more is that most of the zygote space is non-movable objects allocated by VMRuntime.newNonMovableObject. The objects which are non-movable but could be movable include around 10000 classes and some number of fields and methods. Bug: 8981901 Change-Id: Iea21b70fb7af27cb7e92d72070d278a5cd4026ac --- compiler/image_writer.cc | 2 +- runtime/gc/heap.cc | 124 +++++++++++++++++++-------------------- runtime/gc/heap.h | 9 +-- runtime/globals.h | 44 +++++++------- 4 files changed, 88 insertions(+), 91 deletions(-) diff --git a/compiler/image_writer.cc b/compiler/image_writer.cc index 740babd73ac..90e2c65c898 100644 --- a/compiler/image_writer.cc +++ b/compiler/image_writer.cc @@ -525,7 +525,7 @@ void ImageWriter::CalculateNewObjectOffsets(size_t oat_loaded_size, size_t oat_d // Return to write header at start of image with future location of image_roots. At this point, // image_end_ is the size of the image (excluding bitmaps). - const size_t heap_bytes_per_bitmap_byte = 8 * gc::accounting::SpaceBitmap::kAlignment; + const size_t heap_bytes_per_bitmap_byte = kBitsPerByte * gc::accounting::SpaceBitmap::kAlignment; const size_t bitmap_bytes = RoundUp(image_end_, heap_bytes_per_bitmap_byte) / heap_bytes_per_bitmap_byte; ImageHeader image_header(reinterpret_cast(image_begin_), diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc index 1cff719e652..33e6bfd7e09 100644 --- a/runtime/gc/heap.cc +++ b/runtime/gc/heap.cc @@ -62,6 +62,9 @@ #include "well_known_classes.h" namespace art { + +extern void SetQuickAllocEntryPointsAllocator(gc::AllocatorType allocator); + namespace gc { static constexpr bool kGCALotMode = false; @@ -69,6 +72,8 @@ static constexpr size_t kGcAlotInterval = KB; static constexpr bool kDumpGcPerformanceOnShutdown = false; // Minimum amount of remaining bytes before a concurrent GC is triggered. static constexpr size_t kMinConcurrentRemainingBytes = 128 * KB; +static constexpr AllocatorType kDefaultPreZygoteAllocator = kAllocatorTypeFreeList; +static constexpr AllocatorType kDefaultPostZygoteAllocator = kAllocatorTypeFreeList; Heap::Heap(size_t initial_size, size_t growth_limit, size_t min_free, size_t max_free, double target_utilization, size_t capacity, const std::string& image_file_name, @@ -76,7 +81,7 @@ Heap::Heap(size_t initial_size, size_t growth_limit, size_t min_free, size_t max bool low_memory_mode, size_t long_pause_log_threshold, size_t long_gc_log_threshold, bool ignore_max_footprint) : non_moving_space_(nullptr), - concurrent_gc_(!kMovingCollector && concurrent_gc), + concurrent_gc_(concurrent_gc), parallel_gc_threads_(parallel_gc_threads), conc_gc_threads_(conc_gc_threads), low_memory_mode_(low_memory_mode), @@ -149,24 +154,25 @@ Heap::Heap(size_t initial_size, size_t growth_limit, size_t min_free, size_t max if (VLOG_IS_ON(heap) || VLOG_IS_ON(startup)) { LOG(INFO) << "Heap() entering"; } - + // If we aren't the zygote, switch to the default non zygote allocator. This may update the + // entrypoints. + if (!Runtime::Current()->IsZygote()) { + ChangeAllocator(kDefaultPreZygoteAllocator); + } live_bitmap_.reset(new accounting::HeapBitmap(this)); mark_bitmap_.reset(new accounting::HeapBitmap(this)); - // Requested begin for the alloc space, to follow the mapped image and oat files - byte* requested_alloc_space_begin = NULL; + byte* requested_alloc_space_begin = nullptr; if (!image_file_name.empty()) { space::ImageSpace* image_space = space::ImageSpace::Create(image_file_name.c_str()); - CHECK(image_space != NULL) << "Failed to create space for " << image_file_name; + CHECK(image_space != nullptr) << "Failed to create space for " << image_file_name; AddSpace(image_space); // Oat files referenced by image files immediately follow them in memory, ensure alloc space // isn't going to get in the middle byte* oat_file_end_addr = image_space->GetImageHeader().GetOatFileEnd(); CHECK_GT(oat_file_end_addr, image_space->End()); if (oat_file_end_addr > requested_alloc_space_begin) { - requested_alloc_space_begin = - reinterpret_cast(RoundUp(reinterpret_cast(oat_file_end_addr), - kPageSize)); + requested_alloc_space_begin = AlignUp(oat_file_end_addr, kPageSize); } } @@ -252,20 +258,19 @@ Heap::Heap(size_t initial_size, size_t growth_limit, size_t min_free, size_t max CHECK_NE(max_allowed_footprint_, 0U); // Create our garbage collectors. - if (!kMovingCollector) { - for (size_t i = 0; i < 2; ++i) { - const bool concurrent = i != 0; - garbage_collectors_.push_back(new collector::MarkSweep(this, concurrent)); - garbage_collectors_.push_back(new collector::PartialMarkSweep(this, concurrent)); - garbage_collectors_.push_back(new collector::StickyMarkSweep(this, concurrent)); - } - gc_plan_.push_back(collector::kGcTypeSticky); - gc_plan_.push_back(collector::kGcTypePartial); - gc_plan_.push_back(collector::kGcTypeFull); - } else { + for (size_t i = 0; i < 2; ++i) { + const bool concurrent = i != 0; + garbage_collectors_.push_back(new collector::MarkSweep(this, concurrent)); + garbage_collectors_.push_back(new collector::PartialMarkSweep(this, concurrent)); + garbage_collectors_.push_back(new collector::StickyMarkSweep(this, concurrent)); + } + gc_plan_.push_back(collector::kGcTypeSticky); + gc_plan_.push_back(collector::kGcTypePartial); + gc_plan_.push_back(collector::kGcTypeFull); + if (kMovingCollector) { + // TODO: Clean this up. semi_space_collector_ = new collector::SemiSpace(this); garbage_collectors_.push_back(semi_space_collector_); - gc_plan_.push_back(collector::kGcTypeFull); } if (running_on_valgrind_) { @@ -277,6 +282,15 @@ Heap::Heap(size_t initial_size, size_t growth_limit, size_t min_free, size_t max } } +void Heap::ChangeAllocator(AllocatorType allocator) { + DCHECK_NE(allocator, kAllocatorTypeLOS); + if (current_allocator_ != allocator) { + current_allocator_ = allocator; + SetQuickAllocEntryPointsAllocator(current_allocator_); + Runtime::Current()->GetInstrumentation()->ResetQuickAllocEntryPoints(); + } +} + bool Heap::IsCompilingBoot() const { for (const auto& space : continuous_spaces_) { if (space->IsImageSpace()) { @@ -1207,8 +1221,11 @@ void Heap::PreZygoteFork() { // Trim the pages at the end of the non moving space. non_moving_space_->Trim(); non_moving_space_->GetMemMap()->Protect(PROT_READ | PROT_WRITE); - // Create a new bump pointer space which we will compact into. + // Change the allocator to the post zygote one. + ChangeAllocator(kDefaultPostZygoteAllocator); + // TODO: Delete bump_pointer_space_ and temp_pointer_space_? if (semi_space_collector_ != nullptr) { + // Create a new bump pointer space which we will compact into. space::BumpPointerSpace target_space("zygote bump space", non_moving_space_->End(), non_moving_space_->Limit()); // Compact the bump pointer space to a new zygote bump pointer space. @@ -1290,7 +1307,7 @@ void Heap::SwapSemiSpaces() { void Heap::Compact(space::ContinuousMemMapAllocSpace* target_space, space::ContinuousMemMapAllocSpace* source_space) { CHECK(kMovingCollector); - CHECK_NE(target_space, source_space) << "In-place compaction unsupported"; + CHECK_NE(target_space, source_space) << "In-place compaction currently unsupported"; if (target_space != source_space) { semi_space_collector_->SetFromSpace(source_space); semi_space_collector_->SetToSpace(target_space); @@ -1360,22 +1377,25 @@ collector::GcType Heap::CollectGarbageInternal(collector::GcType gc_type, GcCaus DCHECK_NE(gc_type, collector::kGcTypeNone); collector::GarbageCollector* collector = nullptr; - if (kMovingCollector) { + // TODO: Clean this up. + if (current_allocator_ == kAllocatorTypeBumpPointer) { gc_type = semi_space_collector_->GetGcType(); CHECK_EQ(temp_space_->GetObjectsAllocated(), 0U); semi_space_collector_->SetFromSpace(bump_pointer_space_); semi_space_collector_->SetToSpace(temp_space_); mprotect(temp_space_->Begin(), temp_space_->Capacity(), PROT_READ | PROT_WRITE); - } - for (const auto& cur_collector : garbage_collectors_) { - if (cur_collector->IsConcurrent() == concurrent_gc_ && - cur_collector->GetGcType() == gc_type) { - collector = cur_collector; - break; - } - } - if (kMovingCollector) { + collector = semi_space_collector_; gc_type = collector::kGcTypeFull; + } else if (current_allocator_ == kAllocatorTypeFreeList) { + for (const auto& cur_collector : garbage_collectors_) { + if (cur_collector->IsConcurrent() == concurrent_gc_ && + cur_collector->GetGcType() == gc_type) { + collector = cur_collector; + break; + } + } + } else { + LOG(FATAL) << "Invalid current allocator " << current_allocator_; } CHECK(collector != NULL) << "Could not find garbage collector with concurrent=" << concurrent_gc_ @@ -1930,7 +1950,6 @@ void Heap::GrowForUtilization(collector::GcType gc_type, uint64_t gc_duration) { const size_t bytes_allocated = GetBytesAllocated(); last_gc_size_ = bytes_allocated; last_gc_time_ns_ = NanoTime(); - size_t target_size; if (gc_type != collector::kGcTypeSticky) { // Grow the heap for non sticky GC. @@ -1950,7 +1969,6 @@ void Heap::GrowForUtilization(collector::GcType gc_type, uint64_t gc_duration) { } else { next_gc_type_ = collector::kGcTypePartial; } - // If we have freed enough memory, shrink the heap back down. if (bytes_allocated + max_free_ < max_allowed_footprint_) { target_size = bytes_allocated + max_free_; @@ -1958,11 +1976,9 @@ void Heap::GrowForUtilization(collector::GcType gc_type, uint64_t gc_duration) { target_size = std::max(bytes_allocated, max_allowed_footprint_); } } - if (!ignore_max_footprint_) { SetIdealFootprint(target_size); - - if (concurrent_gc_) { + if (concurrent_gc_ && AllocatorHasConcurrentGC(current_allocator_)) { // Calculate when to perform the next ConcurrentGC. // Calculate the estimated GC duration. double gc_duration_seconds = NsToMs(gc_duration) / 1000.0; @@ -1978,7 +1994,8 @@ void Heap::GrowForUtilization(collector::GcType gc_type, uint64_t gc_duration) { // Start a concurrent GC when we get close to the estimated remaining bytes. When the // allocation rate is very high, remaining_bytes could tell us that we should start a GC // right away. - concurrent_start_bytes_ = std::max(max_allowed_footprint_ - remaining_bytes, bytes_allocated); + concurrent_start_bytes_ = std::max(max_allowed_footprint_ - remaining_bytes, + bytes_allocated); } DCHECK_LE(concurrent_start_bytes_, max_allowed_footprint_); DCHECK_LE(max_allowed_footprint_, growth_limit_); @@ -1992,10 +2009,10 @@ void Heap::ClearGrowthLimit() { } void Heap::SetReferenceOffsets(MemberOffset reference_referent_offset, - MemberOffset reference_queue_offset, - MemberOffset reference_queueNext_offset, - MemberOffset reference_pendingNext_offset, - MemberOffset finalizer_reference_zombie_offset) { + MemberOffset reference_queue_offset, + MemberOffset reference_queueNext_offset, + MemberOffset reference_pendingNext_offset, + MemberOffset finalizer_reference_zombie_offset) { reference_referent_offset_ = reference_referent_offset; reference_queue_offset_ = reference_queue_offset; reference_queueNext_offset_ = reference_queueNext_offset; @@ -2029,27 +2046,6 @@ void Heap::AddFinalizerReference(Thread* self, mirror::Object* object) { arg_array.GetArray(), arg_array.GetNumBytes(), &result, 'V'); } -void Heap::PrintReferenceQueue(std::ostream& os, mirror::Object** queue) { - os << "Refernece queue " << queue << "\n"; - if (queue != nullptr) { - mirror::Object* list = *queue; - if (list != nullptr) { - mirror::Object* cur = list; - do { - mirror::Object* pending_next = - cur->GetFieldObject(reference_pendingNext_offset_, false); - os << "PendingNext=" << pending_next; - if (cur->GetClass()->IsFinalizerReferenceClass()) { - os << " Zombie=" << - cur->GetFieldObject(finalizer_reference_zombie_offset_, false); - } - os << "\n"; - cur = pending_next; - } while (cur != list); - } - } -} - void Heap::EnqueueClearedReferences() { if (!cleared_references_.IsEmpty()) { // When a runtime isn't started there are no reference queues to care about so ignore. @@ -2203,7 +2199,7 @@ void Heap::RegisterNativeAllocation(JNIEnv* env, int bytes) { // finalizers released native managed allocations. UpdateMaxNativeFootprint(); } else if (!IsGCRequestPending()) { - if (concurrent_gc_) { + if (concurrent_gc_ && AllocatorHasConcurrentGC(current_allocator_)) { RequestConcurrentGC(self); } else { CollectGarbageInternal(gc_type, kGcCauseForAlloc, false); diff --git a/runtime/gc/heap.h b/runtime/gc/heap.h index 6f94714be30..9215556257e 100644 --- a/runtime/gc/heap.h +++ b/runtime/gc/heap.h @@ -189,6 +189,9 @@ class Heap { void RegisterNativeAllocation(JNIEnv* env, int bytes); void RegisterNativeFree(JNIEnv* env, int bytes); + // Change the allocator, updates entrypoints. + void ChangeAllocator(AllocatorType allocator); + // The given reference is believed to be to an object in the Java heap, check the soundness of it. void VerifyObjectImpl(const mirror::Object* o); void VerifyObject(const mirror::Object* o) { @@ -541,8 +544,6 @@ class Heap { bool IsEnqueued(mirror::Object* ref) const; void DelayReferenceReferent(mirror::Class* klass, mirror::Object* obj, RootVisitor mark_visitor, void* arg) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - // Print a reference queue. - void PrintReferenceQueue(std::ostream& os, mirror::Object** queue); // Run the finalizers. void RunFinalization(JNIEnv* env); @@ -628,7 +629,7 @@ class Heap { // What kind of concurrency behavior is the runtime after? True for concurrent mark sweep GC, // false for stop-the-world mark sweep. - const bool concurrent_gc_; + bool concurrent_gc_; // How many GC threads we may use for paused parts of garbage collection. const size_t parallel_gc_threads_; @@ -776,7 +777,7 @@ class Heap { UniquePtr live_stack_; // Allocator type. - const AllocatorType current_allocator_; + AllocatorType current_allocator_; const AllocatorType current_non_moving_allocator_; // Which GCs we run in order when we an allocation fails. diff --git a/runtime/globals.h b/runtime/globals.h index 1a25dfa4dda..c2fe67e083f 100644 --- a/runtime/globals.h +++ b/runtime/globals.h @@ -26,61 +26,61 @@ typedef uint8_t byte; typedef intptr_t word; typedef uintptr_t uword; -const size_t KB = 1024; -const size_t MB = KB * KB; -const size_t GB = KB * KB * KB; +static constexpr size_t KB = 1024; +static constexpr size_t MB = KB * KB; +static constexpr size_t GB = KB * KB * KB; -const size_t kWordSize = sizeof(word); -const size_t kPointerSize = sizeof(void*); +static constexpr size_t kWordSize = sizeof(word); +static constexpr size_t kPointerSize = sizeof(void*); -const size_t kBitsPerByte = 8; -const size_t kBitsPerByteLog2 = 3; -const int kBitsPerWord = kWordSize * kBitsPerByte; -const size_t kWordHighBitMask = 1 << (kBitsPerWord - 1); +static constexpr size_t kBitsPerByte = 8; +static constexpr size_t kBitsPerByteLog2 = 3; +static constexpr int kBitsPerWord = kWordSize * kBitsPerByte; +static constexpr size_t kWordHighBitMask = 1 << (kBitsPerWord - 1); // Required stack alignment -const size_t kStackAlignment = 16; +static constexpr size_t kStackAlignment = 16; // Required object alignment -const size_t kObjectAlignment = 8; +static constexpr size_t kObjectAlignment = 8; // ARM instruction alignment. ARM processors require code to be 4-byte aligned, // but ARM ELF requires 8.. -const size_t kArmAlignment = 8; +static constexpr size_t kArmAlignment = 8; // MIPS instruction alignment. MIPS processors require code to be 4-byte aligned. // TODO: Can this be 4? -const size_t kMipsAlignment = 8; +static constexpr size_t kMipsAlignment = 8; // X86 instruction alignment. This is the recommended alignment for maximum performance. -const size_t kX86Alignment = 16; +static constexpr size_t kX86Alignment = 16; // System page size. We check this against sysconf(_SC_PAGE_SIZE) at runtime, but use a simple // compile-time constant so the compiler can generate better code. -const int kPageSize = 4096; +static constexpr int kPageSize = 4096; // Whether or not this is a debug build. Useful in conditionals where NDEBUG isn't. #if defined(NDEBUG) -const bool kIsDebugBuild = false; +static constexpr bool kIsDebugBuild = false; #else -const bool kIsDebugBuild = true; +static constexpr bool kIsDebugBuild = true; #endif // Whether or not this is a target (vs host) build. Useful in conditionals where ART_TARGET isn't. #if defined(ART_TARGET) -const bool kIsTargetBuild = true; +static constexpr bool kIsTargetBuild = true; #else -const bool kIsTargetBuild = false; +static constexpr bool kIsTargetBuild = false; #endif #if defined(ART_USE_PORTABLE_COMPILER) -constexpr bool kUsePortableCompiler = true; +static constexpr bool kUsePortableCompiler = true; #else -constexpr bool kUsePortableCompiler = false; +static constexpr bool kUsePortableCompiler = false; #endif // Garbage collector constants. -static constexpr bool kMovingCollector = false && !kUsePortableCompiler; +static constexpr bool kMovingCollector = true && !kUsePortableCompiler; // True if we allow moving classes. static constexpr bool kMovingClasses = false; // True if we allow moving fields. From ff3b24aa929a9db79daeef7c0b0522da099700a9 Mon Sep 17 00:00:00 2001 From: Mathieu Chartier Date: Fri, 22 Nov 2013 16:04:25 -0800 Subject: [PATCH 0221/2402] Add developer option for dumping GC cumulative timings on shutdown. The option is "-XX:DumpGCPerformanceOnShutdown". Bug: 9986416 Change-Id: If6ebb26b3e611a9dead197740dbfc64e548dc388 --- runtime/gc/heap.cc | 6 +++--- runtime/gc/heap.h | 6 +++++- runtime/runtime.cc | 4 ++++ runtime/runtime.h | 1 + 4 files changed, 13 insertions(+), 4 deletions(-) diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc index 33e6bfd7e09..24ee31c4054 100644 --- a/runtime/gc/heap.cc +++ b/runtime/gc/heap.cc @@ -69,7 +69,6 @@ namespace gc { static constexpr bool kGCALotMode = false; static constexpr size_t kGcAlotInterval = KB; -static constexpr bool kDumpGcPerformanceOnShutdown = false; // Minimum amount of remaining bytes before a concurrent GC is triggered. static constexpr size_t kMinConcurrentRemainingBytes = 128 * KB; static constexpr AllocatorType kDefaultPreZygoteAllocator = kAllocatorTypeFreeList; @@ -79,7 +78,7 @@ Heap::Heap(size_t initial_size, size_t growth_limit, size_t min_free, size_t max double target_utilization, size_t capacity, const std::string& image_file_name, bool concurrent_gc, size_t parallel_gc_threads, size_t conc_gc_threads, bool low_memory_mode, size_t long_pause_log_threshold, size_t long_gc_log_threshold, - bool ignore_max_footprint) + bool dump_gc_performance_on_shutdown, bool ignore_max_footprint) : non_moving_space_(nullptr), concurrent_gc_(concurrent_gc), parallel_gc_threads_(parallel_gc_threads), @@ -87,6 +86,7 @@ Heap::Heap(size_t initial_size, size_t growth_limit, size_t min_free, size_t max low_memory_mode_(low_memory_mode), long_pause_log_threshold_(long_pause_log_threshold), long_gc_log_threshold_(long_gc_log_threshold), + dump_gc_performance_on_shutdown_(dump_gc_performance_on_shutdown), ignore_max_footprint_(ignore_max_footprint), have_zygote_space_(false), soft_reference_queue_(this), @@ -610,7 +610,7 @@ void Heap::DumpGcPerformanceInfo(std::ostream& os) { Heap::~Heap() { VLOG(heap) << "Starting ~Heap()"; - if (kDumpGcPerformanceOnShutdown) { + if (dump_gc_performance_on_shutdown_) { DumpGcPerformanceInfo(LOG(INFO)); } STLDeleteElements(&garbage_collectors_); diff --git a/runtime/gc/heap.h b/runtime/gc/heap.h index 9215556257e..30b7eb14c5c 100644 --- a/runtime/gc/heap.h +++ b/runtime/gc/heap.h @@ -145,7 +145,8 @@ class Heap { size_t max_free, double target_utilization, size_t capacity, const std::string& original_image_file_name, bool concurrent_gc, size_t parallel_gc_threads, size_t conc_gc_threads, bool low_memory_mode, - size_t long_pause_threshold, size_t long_gc_threshold, bool ignore_max_footprint); + size_t long_pause_threshold, size_t long_gc_threshold, + bool dump_gc_performance_on_shutdown, bool ignore_max_footprint); ~Heap(); @@ -647,6 +648,9 @@ class Heap { // If we get a GC longer than long GC log threshold, then we print out the GC after it finishes. const size_t long_gc_log_threshold_; + // If true, then we dump the GC cumulative timings on shutdown. + const bool dump_gc_performance_on_shutdown_; + // If we ignore the max footprint it lets the heap grow until it hits the heap capacity, this is // useful for benchmarking since it reduces time spent in GC to a low %. const bool ignore_max_footprint_; diff --git a/runtime/runtime.cc b/runtime/runtime.cc index 08c0ba0e5ce..71ad25203d1 100644 --- a/runtime/runtime.cc +++ b/runtime/runtime.cc @@ -368,6 +368,7 @@ Runtime::ParsedOptions* Runtime::ParsedOptions::Create(const Options& options, b parsed->long_pause_log_threshold_ = gc::Heap::kDefaultLongPauseLogThreshold; parsed->long_gc_log_threshold_ = gc::Heap::kDefaultLongGCLogThreshold; + parsed->dump_gc_performance_on_shutdown_ = false; parsed->ignore_max_footprint_ = false; parsed->lock_profiling_threshold_ = 0; @@ -528,6 +529,8 @@ Runtime::ParsedOptions* Runtime::ParsedOptions::Create(const Options& options, b } else if (option == "-XX:LongGCLogThreshold") { parsed->long_gc_log_threshold_ = ParseMemoryOption(option.substr(strlen("-XX:LongGCLogThreshold")).c_str(), 1024); + } else if (option == "-XX:DumpGCPerformanceOnShutdown") { + parsed->dump_gc_performance_on_shutdown_ = true; } else if (option == "-XX:IgnoreMaxFootprint") { parsed->ignore_max_footprint_ = true; } else if (option == "-XX:LowMemoryMode") { @@ -912,6 +915,7 @@ bool Runtime::Init(const Options& raw_options, bool ignore_unrecognized) { options->low_memory_mode_, options->long_pause_log_threshold_, options->long_gc_log_threshold_, + options->dump_gc_performance_on_shutdown_, options->ignore_max_footprint_); BlockSignals(); diff --git a/runtime/runtime.h b/runtime/runtime.h index d025d47b75d..d5ad299d5bc 100644 --- a/runtime/runtime.h +++ b/runtime/runtime.h @@ -103,6 +103,7 @@ class Runtime { bool is_explicit_gc_disabled_; size_t long_pause_log_threshold_; size_t long_gc_log_threshold_; + bool dump_gc_performance_on_shutdown_; bool ignore_max_footprint_; size_t heap_initial_size_; size_t heap_maximum_size_; From 2e899a92439dc6bdaaa67b8230933006284aa600 Mon Sep 17 00:00:00 2001 From: Hiroshi Yamauchi Date: Fri, 22 Nov 2013 16:50:12 -0800 Subject: [PATCH 0222/2402] Fix a crash with -XX:DumpGCPerformanceOnShutdown. DumpGcPerformanceInfo() could call RosAllocSpace::InspectAllRosAlloc() which needs the thread list to be still alive. Fix by moving the DumpGcPerformanceInfo() call from the Heap destructor up to the beginning of the Runtime destructor so that the thread list is still alive when it's called. Bug: 11830901 Change-Id: Ib094d60916943c8cb1d4b769d805b4ca03269f90 --- runtime/gc/heap.cc | 6 +----- runtime/gc/heap.h | 5 +---- runtime/runtime.cc | 10 +++++++++- runtime/runtime.h | 3 +++ 4 files changed, 14 insertions(+), 10 deletions(-) diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc index 24ee31c4054..d1784fa0ca2 100644 --- a/runtime/gc/heap.cc +++ b/runtime/gc/heap.cc @@ -78,7 +78,7 @@ Heap::Heap(size_t initial_size, size_t growth_limit, size_t min_free, size_t max double target_utilization, size_t capacity, const std::string& image_file_name, bool concurrent_gc, size_t parallel_gc_threads, size_t conc_gc_threads, bool low_memory_mode, size_t long_pause_log_threshold, size_t long_gc_log_threshold, - bool dump_gc_performance_on_shutdown, bool ignore_max_footprint) + bool ignore_max_footprint) : non_moving_space_(nullptr), concurrent_gc_(concurrent_gc), parallel_gc_threads_(parallel_gc_threads), @@ -86,7 +86,6 @@ Heap::Heap(size_t initial_size, size_t growth_limit, size_t min_free, size_t max low_memory_mode_(low_memory_mode), long_pause_log_threshold_(long_pause_log_threshold), long_gc_log_threshold_(long_gc_log_threshold), - dump_gc_performance_on_shutdown_(dump_gc_performance_on_shutdown), ignore_max_footprint_(ignore_max_footprint), have_zygote_space_(false), soft_reference_queue_(this), @@ -610,9 +609,6 @@ void Heap::DumpGcPerformanceInfo(std::ostream& os) { Heap::~Heap() { VLOG(heap) << "Starting ~Heap()"; - if (dump_gc_performance_on_shutdown_) { - DumpGcPerformanceInfo(LOG(INFO)); - } STLDeleteElements(&garbage_collectors_); // If we don't reset then the mark stack complains in its destructor. allocation_stack_->Reset(); diff --git a/runtime/gc/heap.h b/runtime/gc/heap.h index 30b7eb14c5c..877df48604b 100644 --- a/runtime/gc/heap.h +++ b/runtime/gc/heap.h @@ -146,7 +146,7 @@ class Heap { const std::string& original_image_file_name, bool concurrent_gc, size_t parallel_gc_threads, size_t conc_gc_threads, bool low_memory_mode, size_t long_pause_threshold, size_t long_gc_threshold, - bool dump_gc_performance_on_shutdown, bool ignore_max_footprint); + bool ignore_max_footprint); ~Heap(); @@ -648,9 +648,6 @@ class Heap { // If we get a GC longer than long GC log threshold, then we print out the GC after it finishes. const size_t long_gc_log_threshold_; - // If true, then we dump the GC cumulative timings on shutdown. - const bool dump_gc_performance_on_shutdown_; - // If we ignore the max footprint it lets the heap grow until it hits the heap capacity, this is // useful for benchmarking since it reduces time spent in GC to a low %. const bool ignore_max_footprint_; diff --git a/runtime/runtime.cc b/runtime/runtime.cc index 71ad25203d1..896f7ffa28b 100644 --- a/runtime/runtime.cc +++ b/runtime/runtime.cc @@ -109,6 +109,13 @@ Runtime::Runtime() } Runtime::~Runtime() { + if (dump_gc_performance_on_shutdown_) { + // This can't be called from the Heap destructor below because it + // could call RosAlloc::InspectAll() which needs the thread_list + // to be still alive. + heap_->DumpGcPerformanceInfo(LOG(INFO)); + } + Thread* self = Thread::Current(); { MutexLock mu(self, *Locks::runtime_shutdown_lock_); @@ -915,9 +922,10 @@ bool Runtime::Init(const Options& raw_options, bool ignore_unrecognized) { options->low_memory_mode_, options->long_pause_log_threshold_, options->long_gc_log_threshold_, - options->dump_gc_performance_on_shutdown_, options->ignore_max_footprint_); + dump_gc_performance_on_shutdown_ = options->dump_gc_performance_on_shutdown_; + BlockSignals(); InitPlatformSignalHandlers(); diff --git a/runtime/runtime.h b/runtime/runtime.h index d5ad299d5bc..0140ddb4bc8 100644 --- a/runtime/runtime.h +++ b/runtime/runtime.h @@ -562,6 +562,9 @@ class Runtime { // As returned by ClassLoader.getSystemClassLoader(). jobject system_class_loader_; + // If true, then we dump the GC cumulative timings on shutdown. + bool dump_gc_performance_on_shutdown_; + DISALLOW_COPY_AND_ASSIGN(Runtime); }; From 0de9f73afe3e835b63f2ee0c1416930656449f3f Mon Sep 17 00:00:00 2001 From: Mathieu Chartier Date: Fri, 22 Nov 2013 17:58:48 -0800 Subject: [PATCH 0223/2402] Add -xGc: MS, CMS, SS options to specify which GC to use. Can be used for running tests or benchmarks with semispace, marksweep or concurrent marksweep. Change-Id: Ic9ab1220150f2c7c9c30df4ffee45b9d303094b3 --- runtime/gc/collector_type.h | 36 ++++++++++++++++++++++++++++++++++++ runtime/gc/heap.cc | 26 +++++++++++++++++++++----- runtime/gc/heap.h | 11 +++++++++-- runtime/runtime.cc | 15 +++++++++------ runtime/runtime.h | 3 ++- 5 files changed, 77 insertions(+), 14 deletions(-) create mode 100644 runtime/gc/collector_type.h diff --git a/runtime/gc/collector_type.h b/runtime/gc/collector_type.h new file mode 100644 index 00000000000..a42819be18e --- /dev/null +++ b/runtime/gc/collector_type.h @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ART_RUNTIME_GC_COLLECTOR_TYPE_H_ +#define ART_RUNTIME_GC_COLLECTOR_TYPE_H_ + +#include + +namespace art { +namespace gc { + +// Which types of collections are able to be performed. +enum CollectorType { + kCollectorTypeMS, + kCollectorTypeCMS, + kCollectorTypeSS, +}; +std::ostream& operator<<(std::ostream& os, const CollectorType& collector_type); + +} // namespace gc +} // namespace art + +#endif // ART_RUNTIME_GC_COLLECTOR_TYPE_H_ diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc index d1784fa0ca2..d8902f09664 100644 --- a/runtime/gc/heap.cc +++ b/runtime/gc/heap.cc @@ -54,6 +54,7 @@ #include "mirror/object_array-inl.h" #include "object_utils.h" #include "os.h" +#include "runtime.h" #include "ScopedLocalRef.h" #include "scoped_thread_state_change.h" #include "sirt_ref.h" @@ -72,15 +73,15 @@ static constexpr size_t kGcAlotInterval = KB; // Minimum amount of remaining bytes before a concurrent GC is triggered. static constexpr size_t kMinConcurrentRemainingBytes = 128 * KB; static constexpr AllocatorType kDefaultPreZygoteAllocator = kAllocatorTypeFreeList; -static constexpr AllocatorType kDefaultPostZygoteAllocator = kAllocatorTypeFreeList; Heap::Heap(size_t initial_size, size_t growth_limit, size_t min_free, size_t max_free, double target_utilization, size_t capacity, const std::string& image_file_name, - bool concurrent_gc, size_t parallel_gc_threads, size_t conc_gc_threads, + CollectorType collector_type, size_t parallel_gc_threads, size_t conc_gc_threads, bool low_memory_mode, size_t long_pause_log_threshold, size_t long_gc_log_threshold, bool ignore_max_footprint) : non_moving_space_(nullptr), - concurrent_gc_(concurrent_gc), + concurrent_gc_(collector_type == gc::kCollectorTypeCMS), + collector_type_(collector_type), parallel_gc_threads_(parallel_gc_threads), conc_gc_threads_(conc_gc_threads), low_memory_mode_(low_memory_mode), @@ -156,7 +157,7 @@ Heap::Heap(size_t initial_size, size_t growth_limit, size_t min_free, size_t max // If we aren't the zygote, switch to the default non zygote allocator. This may update the // entrypoints. if (!Runtime::Current()->IsZygote()) { - ChangeAllocator(kDefaultPreZygoteAllocator); + ChangeCollector(collector_type_); } live_bitmap_.reset(new accounting::HeapBitmap(this)); mark_bitmap_.reset(new accounting::HeapBitmap(this)); @@ -1203,6 +1204,21 @@ void Heap::CollectGarbage(bool clear_soft_references) { CollectGarbageInternal(collector::kGcTypeFull, kGcCauseExplicit, clear_soft_references); } +void Heap::ChangeCollector(CollectorType collector_type) { + switch (collector_type) { + case kCollectorTypeSS: { + ChangeAllocator(kAllocatorTypeBumpPointer); + break; + } + case kCollectorTypeMS: + // Fall-through. + case kCollectorTypeCMS: { + ChangeAllocator(kAllocatorTypeFreeList); + break; + } + } +} + void Heap::PreZygoteFork() { static Mutex zygote_creation_lock_("zygote creation lock", kZygoteCreationLock); Thread* self = Thread::Current(); @@ -1218,7 +1234,7 @@ void Heap::PreZygoteFork() { non_moving_space_->Trim(); non_moving_space_->GetMemMap()->Protect(PROT_READ | PROT_WRITE); // Change the allocator to the post zygote one. - ChangeAllocator(kDefaultPostZygoteAllocator); + ChangeCollector(collector_type_); // TODO: Delete bump_pointer_space_ and temp_pointer_space_? if (semi_space_collector_ != nullptr) { // Create a new bump pointer space which we will compact into. diff --git a/runtime/gc/heap.h b/runtime/gc/heap.h index 877df48604b..8c5746de365 100644 --- a/runtime/gc/heap.h +++ b/runtime/gc/heap.h @@ -26,6 +26,7 @@ #include "gc/accounting/atomic_stack.h" #include "gc/accounting/card_table.h" #include "gc/collector/gc_type.h" +#include "gc/collector_type.h" #include "globals.h" #include "gtest/gtest.h" #include "jni.h" @@ -143,7 +144,7 @@ class Heap { // ImageWriter output. explicit Heap(size_t initial_size, size_t growth_limit, size_t min_free, size_t max_free, double target_utilization, size_t capacity, - const std::string& original_image_file_name, bool concurrent_gc, + const std::string& original_image_file_name, CollectorType collector_type_, size_t parallel_gc_threads, size_t conc_gc_threads, bool low_memory_mode, size_t long_pause_threshold, size_t long_gc_threshold, bool ignore_max_footprint); @@ -193,6 +194,9 @@ class Heap { // Change the allocator, updates entrypoints. void ChangeAllocator(AllocatorType allocator); + // Change the collector to be one of the possible options (MS, CMS, SS). + void ChangeCollector(CollectorType collector_type); + // The given reference is believed to be to an object in the Java heap, check the soundness of it. void VerifyObjectImpl(const mirror::Object* o); void VerifyObject(const mirror::Object* o) { @@ -630,7 +634,10 @@ class Heap { // What kind of concurrency behavior is the runtime after? True for concurrent mark sweep GC, // false for stop-the-world mark sweep. - bool concurrent_gc_; + const bool concurrent_gc_; + + // The current collector type. + CollectorType collector_type_; // How many GC threads we may use for paused parts of garbage collection. const size_t parallel_gc_threads_; diff --git a/runtime/runtime.cc b/runtime/runtime.cc index 896f7ffa28b..6bd256057d5 100644 --- a/runtime/runtime.cc +++ b/runtime/runtime.cc @@ -363,6 +363,8 @@ Runtime::ParsedOptions* Runtime::ParsedOptions::Create(const Options& options, b parsed->parallel_gc_threads_ = sysconf(_SC_NPROCESSORS_CONF) - 1; // Only the main GC thread, no workers. parsed->conc_gc_threads_ = 0; + // Default is CMS which is Sticky + Partial + Full CMS GC. + parsed->collector_type_ = gc::kCollectorTypeCMS; parsed->stack_size_ = 0; // 0 means default. parsed->max_spins_before_thin_lock_inflation_ = Monitor::kDefaultMaxSpinsBeforeThinLockInflation; parsed->low_memory_mode_ = false; @@ -370,7 +372,6 @@ Runtime::ParsedOptions* Runtime::ParsedOptions::Create(const Options& options, b parsed->is_compiler_ = false; parsed->is_zygote_ = false; parsed->interpreter_only_ = false; - parsed->is_concurrent_gc_enabled_ = true; parsed->is_explicit_gc_disabled_ = false; parsed->long_pause_log_threshold_ = gc::Heap::kDefaultLongPauseLogThreshold; @@ -556,10 +557,12 @@ Runtime::ParsedOptions* Runtime::ParsedOptions::Create(const Options& options, b std::vector gc_options; Split(option.substr(strlen("-Xgc:")), ',', gc_options); for (size_t i = 0; i < gc_options.size(); ++i) { - if (gc_options[i] == "noconcurrent") { - parsed->is_concurrent_gc_enabled_ = false; - } else if (gc_options[i] == "concurrent") { - parsed->is_concurrent_gc_enabled_ = true; + if (gc_options[i] == "MS" || gc_options[i] == "nonconcurrent") { + parsed->collector_type_ = gc::kCollectorTypeMS; + } else if (gc_options[i] == "CMS" || gc_options[i] == "concurrent") { + parsed->collector_type_ = gc::kCollectorTypeCMS; + } else if (gc_options[i] == "SS") { + parsed->collector_type_ = gc::kCollectorTypeSS; } else { LOG(WARNING) << "Ignoring unknown -Xgc option: " << gc_options[i]; } @@ -916,7 +919,7 @@ bool Runtime::Init(const Options& raw_options, bool ignore_unrecognized) { options->heap_target_utilization_, options->heap_maximum_size_, options->image_, - options->is_concurrent_gc_enabled_, + options->collector_type_, options->parallel_gc_threads_, options->conc_gc_threads_, options->low_memory_mode_, diff --git a/runtime/runtime.h b/runtime/runtime.h index 0140ddb4bc8..e6951d90bb0 100644 --- a/runtime/runtime.h +++ b/runtime/runtime.h @@ -27,6 +27,7 @@ #include "base/macros.h" #include "base/stringpiece.h" +#include "gc/collector_type.h" #include "gc/heap.h" #include "globals.h" #include "instruction_set.h" @@ -99,7 +100,6 @@ class Runtime { bool is_compiler_; bool is_zygote_; bool interpreter_only_; - bool is_concurrent_gc_enabled_; bool is_explicit_gc_disabled_; size_t long_pause_log_threshold_; size_t long_gc_log_threshold_; @@ -113,6 +113,7 @@ class Runtime { double heap_target_utilization_; size_t parallel_gc_threads_; size_t conc_gc_threads_; + gc::CollectorType collector_type_; size_t stack_size_; size_t max_spins_before_thin_lock_inflation_; bool low_memory_mode_; From 93220fc756f11e817078cd9fbf5363691b3f87df Mon Sep 17 00:00:00 2001 From: Jeff Hao Date: Wed, 30 Oct 2013 14:12:12 -0700 Subject: [PATCH 0224/2402] Update compiler blacklist to include java.net.NetworkInterface. (cherry picked from commit a52454455048d04d12e4da637a103412a55e579b) Change-Id: I3ee632e13ebf13ae08273f284966254b2c5e4d79 --- compiler/driver/compiler_driver.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc index 6eabeed34b7..a77c82083b2 100644 --- a/compiler/driver/compiler_driver.cc +++ b/compiler/driver/compiler_driver.cc @@ -1979,6 +1979,7 @@ static const char* class_initializer_black_list[] = { "Ljava/net/Inet4Address;", // Sub-class of InetAddress. "Ljava/net/Inet6Address;", // Sub-class of InetAddress. "Ljava/net/InetUnixAddress;", // Sub-class of InetAddress. + "Ljava/net/NetworkInterface;", // Calls to Random. -> System.currentTimeMillis -> OsConstants.initConstants. "Ljava/nio/charset/Charset;", // Calls Charset.getDefaultCharset -> System.getProperty -> OsConstants.initConstants. "Ljava/nio/charset/CharsetICU;", // Sub-class of Charset. "Ljava/nio/charset/Charsets;", // Calls Charset.forName. From 8250232f44cd3ec85d4832174197d9b8287b70fe Mon Sep 17 00:00:00 2001 From: Brian Carlstrom Date: Sun, 24 Nov 2013 23:15:37 -0800 Subject: [PATCH 0225/2402] Update test/044-proxy/expected.txt Change-Id: If2b2aaac869ebfba3abb0f167bbd381054688a97 --- test/044-proxy/expected.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/044-proxy/expected.txt b/test/044-proxy/expected.txt index eafaf1d1886..13e3a287c44 100644 --- a/test/044-proxy/expected.txt +++ b/test/044-proxy/expected.txt @@ -82,7 +82,7 @@ Got expected exception Invoke public abstract void InterfaceW1.bothThrowBase() throws BaseException,SubException,SubSubException (no args) Got expected exception -Proxy methods: [public final boolean $Proxy7.equals(java.lang.Object), public final java.lang.Object $Proxy7.foo(), public final java.lang.String $Proxy7.foo(), public final int $Proxy7.hashCode(), public final java.lang.String $Proxy7.toString()] +Proxy methods: [public final boolean $Proxy3.equals(java.lang.Object), public final java.lang.Object $Proxy3.foo(), public final java.lang.String $Proxy3.foo(), public final int $Proxy3.hashCode(), public final java.lang.String $Proxy3.toString()] Invocation of public abstract java.lang.String NarrowingTest$I2.foo() Invoking foo using I2 type: hello Invocation of public abstract java.lang.Object NarrowingTest$I1.foo() From d9cffeaa478bd30ad89a9dfc9680a27ce5efaadf Mon Sep 17 00:00:00 2001 From: Vladimir Marko Date: Mon, 25 Nov 2013 15:08:02 +0000 Subject: [PATCH 0226/2402] Faster Signature::operator==(const StringPiece& rhs). Avoid string allocation and resizing, return early if a parameter doesn't match. Change-Id: Ifc929d0c4a7a9d368432f7cae797d4326c6c44be --- runtime/dex_file.cc | 26 ++++++++++++++++++++++++++ runtime/dex_file.h | 5 +---- 2 files changed, 27 insertions(+), 4 deletions(-) diff --git a/runtime/dex_file.cc b/runtime/dex_file.cc index a7575ce8890..517f96c6238 100644 --- a/runtime/dex_file.cc +++ b/runtime/dex_file.cc @@ -876,6 +876,32 @@ std::string Signature::ToString() const { return result; } +bool Signature::operator==(const StringPiece& rhs) const { + if (dex_file_ == nullptr) { + return false; + } + StringPiece tail(rhs); + if (!tail.starts_with("(")) { + return false; // Invalid signature + } + tail.remove_prefix(1); // "("; + const DexFile::TypeList* params = dex_file_->GetProtoParameters(*proto_id_); + if (params != nullptr) { + for (uint32_t i = 0; i < params->Size(); ++i) { + StringPiece param(dex_file_->StringByTypeIdx(params->GetTypeItem(i).type_idx_)); + if (!tail.starts_with(param)) { + return false; + } + tail.remove_prefix(param.length()); + } + } + if (!tail.starts_with(")")) { + return false; + } + tail.remove_prefix(1); // ")"; + return tail == dex_file_->StringByTypeIdx(proto_id_->return_type_idx_); +} + std::ostream& operator<<(std::ostream& os, const Signature& sig) { return os << sig.ToString(); } diff --git a/runtime/dex_file.h b/runtime/dex_file.h index 84026a46e67..69593cdffc6 100644 --- a/runtime/dex_file.h +++ b/runtime/dex_file.h @@ -964,10 +964,7 @@ class Signature { return !(*this == rhs); } - bool operator==(const StringPiece& rhs) const { - // TODO: Avoid temporary string allocation. - return ToString() == rhs; - } + bool operator==(const StringPiece& rhs) const; private: Signature(const DexFile* dex, const DexFile::ProtoId& proto) : dex_file_(dex), proto_id_(&proto) { From d38667a055d507492fd05f78519a7e1f0b85ea03 Mon Sep 17 00:00:00 2001 From: Sebastien Hertz Date: Mon, 25 Nov 2013 15:43:54 +0100 Subject: [PATCH 0227/2402] Minor field name cleanup in debugger. Use the same naming convention for all fields of DebugInvokeReq structure. Change-Id: Ieaf65eef592f96efa47975eef15334279ed4fc8a --- runtime/debugger.cc | 34 +++++++++++++++++----------------- runtime/debugger.h | 30 +++++++++++++++--------------- runtime/jdwp/jdwp_event.cc | 10 +++++----- 3 files changed, 37 insertions(+), 37 deletions(-) diff --git a/runtime/debugger.cc b/runtime/debugger.cc index bcee076484a..0f32a96c239 100644 --- a/runtime/debugger.cc +++ b/runtime/debugger.cc @@ -2723,14 +2723,14 @@ JDWP::JdwpError Dbg::InvokeMethod(JDWP::ObjectId thread_id, JDWP::ObjectId objec } } - req->receiver_ = receiver; - req->thread_ = thread; - req->class_ = c; - req->method_ = m; - req->arg_count_ = arg_count; - req->arg_values_ = arg_values; - req->options_ = options; - req->invoke_needed_ = true; + req->receiver = receiver; + req->thread = thread; + req->klass = c; + req->method = m; + req->arg_count = arg_count; + req->arg_values = arg_values; + req->options = options; + req->invoke_needed = true; } // The fact that we've released the thread list lock is a bit risky --- if the thread goes @@ -2748,7 +2748,7 @@ JDWP::JdwpError Dbg::InvokeMethod(JDWP::ObjectId thread_id, JDWP::ObjectId objec VLOG(jdwp) << " Transferring control to event thread"; { - MutexLock mu(self, req->lock_); + MutexLock mu(self, req->lock); if ((options & JDWP::INVOKE_SINGLE_THREADED) == 0) { VLOG(jdwp) << " Resuming all threads"; @@ -2759,8 +2759,8 @@ JDWP::JdwpError Dbg::InvokeMethod(JDWP::ObjectId thread_id, JDWP::ObjectId objec } // Wait for the request to finish executing. - while (req->invoke_needed_) { - req->cond_.Wait(self); + while (req->invoke_needed) { + req->cond.Wait(self); } } VLOG(jdwp) << " Control has returned from event thread"; @@ -2817,24 +2817,24 @@ void Dbg::ExecuteMethod(DebugInvokeReq* pReq) { } // Translate the method through the vtable, unless the debugger wants to suppress it. - mirror::ArtMethod* m = pReq->method_; - if ((pReq->options_ & JDWP::INVOKE_NONVIRTUAL) == 0 && pReq->receiver_ != NULL) { - mirror::ArtMethod* actual_method = pReq->class_->FindVirtualMethodForVirtualOrInterface(pReq->method_); + mirror::ArtMethod* m = pReq->method; + if ((pReq->options & JDWP::INVOKE_NONVIRTUAL) == 0 && pReq->receiver != NULL) { + mirror::ArtMethod* actual_method = pReq->klass->FindVirtualMethodForVirtualOrInterface(pReq->method); if (actual_method != m) { VLOG(jdwp) << "ExecuteMethod translated " << PrettyMethod(m) << " to " << PrettyMethod(actual_method); m = actual_method; } } VLOG(jdwp) << "ExecuteMethod " << PrettyMethod(m) - << " receiver=" << pReq->receiver_ - << " arg_count=" << pReq->arg_count_; + << " receiver=" << pReq->receiver + << " arg_count=" << pReq->arg_count; CHECK(m != NULL); CHECK_EQ(sizeof(jvalue), sizeof(uint64_t)); MethodHelper mh(m); ArgArray arg_array(mh.GetShorty(), mh.GetShortyLength()); - arg_array.BuildArgArray(soa, pReq->receiver_, reinterpret_cast(pReq->arg_values_)); + arg_array.BuildArgArray(soa, pReq->receiver, reinterpret_cast(pReq->arg_values)); InvokeWithArgArray(soa, m, &arg_array, &pReq->result_value, mh.GetShorty()[0]); mirror::Throwable* exception = soa.Self()->GetException(NULL); diff --git a/runtime/debugger.h b/runtime/debugger.h index 0a7cf5a9bec..acbb2c6ae88 100644 --- a/runtime/debugger.h +++ b/runtime/debugger.h @@ -48,28 +48,28 @@ class ThrowLocation; */ struct DebugInvokeReq { DebugInvokeReq() - : ready(false), invoke_needed_(false), - receiver_(NULL), thread_(NULL), class_(NULL), method_(NULL), - arg_count_(0), arg_values_(NULL), options_(0), error(JDWP::ERR_NONE), + : ready(false), invoke_needed(false), + receiver(NULL), thread(NULL), klass(NULL), method(NULL), + arg_count(0), arg_values(NULL), options(0), error(JDWP::ERR_NONE), result_tag(JDWP::JT_VOID), exception(0), - lock_("a DebugInvokeReq lock", kBreakpointInvokeLock), - cond_("a DebugInvokeReq condition variable", lock_) { + lock("a DebugInvokeReq lock", kBreakpointInvokeLock), + cond("a DebugInvokeReq condition variable", lock) { } /* boolean; only set when we're in the tail end of an event handler */ bool ready; /* boolean; set if the JDWP thread wants this thread to do work */ - bool invoke_needed_; + bool invoke_needed; /* request */ - mirror::Object* receiver_; /* not used for ClassType.InvokeMethod */ - mirror::Object* thread_; - mirror::Class* class_; - mirror::ArtMethod* method_; - uint32_t arg_count_; - uint64_t* arg_values_; /* will be NULL if arg_count_ == 0 */ - uint32_t options_; + mirror::Object* receiver; /* not used for ClassType.InvokeMethod */ + mirror::Object* thread; + mirror::Class* klass; + mirror::ArtMethod* method; + uint32_t arg_count; + uint64_t* arg_values; /* will be NULL if arg_count_ == 0 */ + uint32_t options; /* result */ JDWP::JdwpError error; @@ -78,8 +78,8 @@ struct DebugInvokeReq { JDWP::ObjectId exception; /* condition variable to wait on while the method executes */ - Mutex lock_ DEFAULT_MUTEX_ACQUIRED_AFTER; - ConditionVariable cond_ GUARDED_BY(lock_); + Mutex lock DEFAULT_MUTEX_ACQUIRED_AFTER; + ConditionVariable cond GUARDED_BY(lock); private: DISALLOW_COPY_AND_ASSIGN(DebugInvokeReq); diff --git a/runtime/jdwp/jdwp_event.cc b/runtime/jdwp/jdwp_event.cc index 61bd1edd5d1..b05b49dfcd0 100644 --- a/runtime/jdwp/jdwp_event.cc +++ b/runtime/jdwp/jdwp_event.cc @@ -521,7 +521,7 @@ void JdwpState::SuspendByPolicy(JdwpSuspendPolicy suspend_policy, JDWP::ObjectId * The JDWP thread has told us (and possibly all other threads) to * resume. See if it has left anything in our DebugInvokeReq mailbox. */ - if (!pReq->invoke_needed_) { + if (!pReq->invoke_needed) { /*LOGD("SuspendByPolicy: no invoke needed");*/ break; } @@ -535,12 +535,12 @@ void JdwpState::SuspendByPolicy(JdwpSuspendPolicy suspend_policy, JDWP::ObjectId pReq->error = ERR_NONE; /* clear this before signaling */ - pReq->invoke_needed_ = false; + pReq->invoke_needed = false; VLOG(jdwp) << "invoke complete, signaling and self-suspending"; Thread* self = Thread::Current(); - MutexLock mu(self, pReq->lock_); - pReq->cond_.Signal(self); + MutexLock mu(self, pReq->lock); + pReq->cond.Signal(self); } } @@ -570,7 +570,7 @@ void JdwpState::SendRequestAndPossiblySuspend(ExpandBuf* pReq, JdwpSuspendPolicy */ bool JdwpState::InvokeInProgress() { DebugInvokeReq* pReq = Dbg::GetInvokeReq(); - return pReq->invoke_needed_; + return pReq->invoke_needed; } /* From db9f608cb77a3745a1dee593c686a4e46dd11927 Mon Sep 17 00:00:00 2001 From: Narayan Kamath Date: Mon, 28 Oct 2013 13:24:56 +0000 Subject: [PATCH 0228/2402] Remove usage of LOCAL_BUILD_HOST_DEX (cherry picked from commit cc8522f6b89e55a1c9abee181874d3ab29441466) Change-Id: Ida3baa2acb5918557e52f083aad88b41a685da9e --- test/Android.mk | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/test/Android.mk b/test/Android.mk index da469d75d7c..881d664bb2a 100644 --- a/test/Android.mk +++ b/test/Android.mk @@ -84,10 +84,9 @@ define build-art-test-dex LOCAL_JAVA_LIBRARIES := $(HOST_CORE_JARS) LOCAL_NO_STANDARD_LIBRARIES := true LOCAL_DEX_PREOPT_IMAGE := $(HOST_CORE_IMG_OUT) - LOCAL_BUILD_HOST_DEX := true LOCAL_ADDITIONAL_DEPENDENCIES := art/build/Android.common.mk LOCAL_ADDITIONAL_DEPENDENCIES += $(LOCAL_PATH)/Android.mk - include $(BUILD_HOST_JAVA_LIBRARY) + include $(BUILD_DALVIK_HOST_JAVA_LIBRARY) ART_TEST_HOST_DEX_FILES += $$(LOCAL_MODULE_PATH)/$$(LOCAL_MODULE).jar endif endef From 7410f29b4dae223befac036ea567d7f33351dad1 Mon Sep 17 00:00:00 2001 From: Mathieu Chartier Date: Sun, 24 Nov 2013 13:17:35 -0800 Subject: [PATCH 0229/2402] Fix dumpsys meminfo . Added a case for BumpPointerSpaces. Confirmed working non-debug. Should also work in debug builds. Bug: 11830794 Change-Id: I12053ff16eec403dcd4a780e13095e3212a77132 --- runtime/arch/arm/quick_entrypoints_arm.S | 5 ++++- runtime/arch/mips/quick_entrypoints_mips.S | 5 ++++- runtime/arch/quick_alloc_entrypoints.S | 3 ++- runtime/arch/x86/quick_entrypoints_x86.S | 5 ++++- runtime/base/histogram.h | 4 +++- runtime/gc/collector_type.h | 3 +++ runtime/gc/heap.cc | 3 ++- runtime/gc/space/bump_pointer_space.h | 6 +++++- runtime/gc/space/space.h | 5 +++++ runtime/mirror/array-inl.h | 1 + runtime/native/dalvik_system_VMDebug.cc | 22 ++++++++++------------ 11 files changed, 43 insertions(+), 19 deletions(-) diff --git a/runtime/arch/arm/quick_entrypoints_arm.S b/runtime/arch/arm/quick_entrypoints_arm.S index 1976af51886..61be14ba591 100644 --- a/runtime/arch/arm/quick_entrypoints_arm.S +++ b/runtime/arch/arm/quick_entrypoints_arm.S @@ -16,6 +16,8 @@ #include "asm_support_arm.S" +#include "arch/quick_alloc_entrypoints.S" + /* Deliver the given exception */ .extern artDeliverExceptionFromCode /* Deliver an exception pending on a thread */ @@ -864,7 +866,8 @@ ENTRY \name END \name .endm -#include "arch/quick_alloc_entrypoints.S" +// Generate the allocation entrypoints for each allocator. +GENERATE_ALL_ALLOC_ENTRYPOINTS /* * Called by managed code when the value in rSUSPEND has been decremented to 0. diff --git a/runtime/arch/mips/quick_entrypoints_mips.S b/runtime/arch/mips/quick_entrypoints_mips.S index 6d6d7962b2c..2d1e87aa682 100644 --- a/runtime/arch/mips/quick_entrypoints_mips.S +++ b/runtime/arch/mips/quick_entrypoints_mips.S @@ -16,6 +16,8 @@ #include "asm_support_mips.S" +#include "arch/quick_alloc_entrypoints.S" + .set noreorder .balign 4 @@ -931,7 +933,8 @@ ENTRY \name END \name .endm -#include "arch/quick_alloc_entrypoints.S" +// Generate the allocation entrypoints for each allocator. +GENERATE_ALL_ALLOC_ENTRYPOINTS /* * Called by managed code when the value in rSUSPEND has been decremented to 0. diff --git a/runtime/arch/quick_alloc_entrypoints.S b/runtime/arch/quick_alloc_entrypoints.S index 0109c13c778..bdadc51d238 100644 --- a/runtime/arch/quick_alloc_entrypoints.S +++ b/runtime/arch/quick_alloc_entrypoints.S @@ -14,7 +14,6 @@ * limitations under the License. */ - .macro GENERATE_ALLOC_ENTRYPOINTS c_suffix, cxx_suffix // Called by managed code to allocate an object. TWO_ARG_DOWNCALL art_quick_alloc_object\c_suffix, artAllocObjectFromCode\cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO @@ -32,7 +31,9 @@ THREE_ARG_DOWNCALL art_quick_check_and_alloc_array\c_suffix, artCheckAndAllocArr THREE_ARG_DOWNCALL art_quick_check_and_alloc_array_with_access_check\c_suffix, artCheckAndAllocArrayFromCodeWithAccessCheck\cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO .endm +.macro GENERATE_ALL_ALLOC_ENTRYPOINTS GENERATE_ALLOC_ENTRYPOINTS GENERATE_ALLOC_ENTRYPOINTS _instrumented, Instrumented GENERATE_ALLOC_ENTRYPOINTS _bump_pointer, BumpPointer GENERATE_ALLOC_ENTRYPOINTS _bump_pointer_instrumented, BumpPointerInstrumented +.endm diff --git a/runtime/arch/x86/quick_entrypoints_x86.S b/runtime/arch/x86/quick_entrypoints_x86.S index 62a8b701952..96794713cfa 100644 --- a/runtime/arch/x86/quick_entrypoints_x86.S +++ b/runtime/arch/x86/quick_entrypoints_x86.S @@ -16,6 +16,8 @@ #include "asm_support_x86.S" +#include "arch/quick_alloc_entrypoints.S" + // For x86, the CFA is esp+4, the address above the pushed return address on the stack. /* @@ -426,7 +428,8 @@ MACRO0(RETURN_OR_DELIVER_PENDING_EXCEPTION) DELIVER_PENDING_EXCEPTION END_MACRO -#include "arch/quick_alloc_entrypoints.S" +// Generate the allocation entrypoints for each allocator. +GENERATE_ALL_ALLOC_ENTRYPOINTS TWO_ARG_DOWNCALL art_quick_resolve_string, artResolveStringFromCode, RETURN_IF_RESULT_IS_NON_ZERO TWO_ARG_DOWNCALL art_quick_initialize_static_storage, artInitializeStaticStorageFromCode, RETURN_IF_RESULT_IS_NON_ZERO diff --git a/runtime/base/histogram.h b/runtime/base/histogram.h index 4e5d29a2977..a7d51e2078c 100644 --- a/runtime/base/histogram.h +++ b/runtime/base/histogram.h @@ -40,8 +40,10 @@ template class Histogram { std::vector perc_; }; - // Used for name based comparators in the timing loggers. + // Used by the cumulative timing logger to search the histogram set using for an existing split + // with the same name using CumulativeLogger::HistogramComparator. explicit Histogram(const char* name); + // This is the expected constructor when creating new Histograms. Histogram(const char* name, Value initial_bucket_width, size_t max_buckets = 100); void AddValue(Value); // Builds the cumulative distribution function from the frequency data. diff --git a/runtime/gc/collector_type.h b/runtime/gc/collector_type.h index a42819be18e..ba3cad6972e 100644 --- a/runtime/gc/collector_type.h +++ b/runtime/gc/collector_type.h @@ -24,8 +24,11 @@ namespace gc { // Which types of collections are able to be performed. enum CollectorType { + // Non concurrent mark-sweep. kCollectorTypeMS, + // Concurrent mark-sweep. kCollectorTypeCMS, + // Semi-space / mark-sweep hybrid, enables compaction. kCollectorTypeSS, }; std::ostream& operator<<(std::ostream& os, const CollectorType& collector_type); diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc index d8902f09664..5e62729c2ec 100644 --- a/runtime/gc/heap.cc +++ b/runtime/gc/heap.cc @@ -72,7 +72,6 @@ static constexpr bool kGCALotMode = false; static constexpr size_t kGcAlotInterval = KB; // Minimum amount of remaining bytes before a concurrent GC is triggered. static constexpr size_t kMinConcurrentRemainingBytes = 128 * KB; -static constexpr AllocatorType kDefaultPreZygoteAllocator = kAllocatorTypeFreeList; Heap::Heap(size_t initial_size, size_t growth_limit, size_t min_free, size_t max_free, double target_utilization, size_t capacity, const std::string& image_file_name, @@ -1215,6 +1214,8 @@ void Heap::ChangeCollector(CollectorType collector_type) { case kCollectorTypeCMS: { ChangeAllocator(kAllocatorTypeFreeList); break; + default: + LOG(FATAL) << "Unimplemented"; } } } diff --git a/runtime/gc/space/bump_pointer_space.h b/runtime/gc/space/bump_pointer_space.h index 9b0b6aae3cd..2edd3e2faca 100644 --- a/runtime/gc/space/bump_pointer_space.h +++ b/runtime/gc/space/bump_pointer_space.h @@ -120,7 +120,11 @@ class BumpPointerSpace : public ContinuousMemMapAllocSpace { static mirror::Object* GetNextObject(mirror::Object* obj) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - // Alignment. + virtual BumpPointerSpace* AsBumpPointerSpace() { + return this; + } + + // Object alignment within the space. static constexpr size_t kAlignment = 8; protected: diff --git a/runtime/gc/space/space.h b/runtime/gc/space/space.h index 38b602ead15..ca39175979e 100644 --- a/runtime/gc/space/space.h +++ b/runtime/gc/space/space.h @@ -43,6 +43,7 @@ class Heap; namespace space { class AllocSpace; +class BumpPointerSpace; class ContinuousSpace; class DiscontinuousSpace; class MallocSpace; @@ -138,6 +139,10 @@ class Space { bool IsBumpPointerSpace() const { return GetType() == kSpaceTypeBumpPointerSpace; } + virtual BumpPointerSpace* AsBumpPointerSpace() { + LOG(FATAL) << "Unreachable"; + return NULL; + } // Does this space hold large objects and implement the large object space abstraction? bool IsLargeObjectSpace() const { diff --git a/runtime/mirror/array-inl.h b/runtime/mirror/array-inl.h index 46ffaaef60e..a754b6942d3 100644 --- a/runtime/mirror/array-inl.h +++ b/runtime/mirror/array-inl.h @@ -58,6 +58,7 @@ static inline size_t ComputeArraySize(Thread* self, Class* array_class, int32_t return size; } +// Used for setting the array length in the allocation code path to ensure it is guarded by a CAS. class SetLengthVisitor { public: explicit SetLengthVisitor(int32_t length) : length_(length) { diff --git a/runtime/native/dalvik_system_VMDebug.cc b/runtime/native/dalvik_system_VMDebug.cc index 66fa100385c..67c45054462 100644 --- a/runtime/native/dalvik_system_VMDebug.cc +++ b/runtime/native/dalvik_system_VMDebug.cc @@ -20,6 +20,7 @@ #include "class_linker.h" #include "common_throws.h" #include "debugger.h" +#include "gc/space/bump_pointer_space.h" #include "gc/space/dlmalloc_space.h" #include "gc/space/large_object_space.h" #include "gc/space/space-inl.h" @@ -247,7 +248,7 @@ static jlong VMDebug_countInstancesOfClass(JNIEnv* env, jclass, jclass javaClass // /proc//smaps. static void VMDebug_getHeapSpaceStats(JNIEnv* env, jclass, jlongArray data) { jlong* arr = reinterpret_cast(env->GetPrimitiveArrayCritical(data, 0)); - if (arr == NULL || env->GetArrayLength(data) < 9) { + if (arr == nullptr || env->GetArrayLength(data) < 9) { return; } @@ -257,29 +258,26 @@ static void VMDebug_getHeapSpaceStats(JNIEnv* env, jclass, jlongArray data) { size_t zygoteUsed = 0; size_t largeObjectsSize = 0; size_t largeObjectsUsed = 0; - gc::Heap* heap = Runtime::Current()->GetHeap(); - const std::vector& continuous_spaces = heap->GetContinuousSpaces(); - const std::vector& discontinuous_spaces = heap->GetDiscontinuousSpaces(); - typedef std::vector::const_iterator It; - for (It it = continuous_spaces.begin(), end = continuous_spaces.end(); it != end; ++it) { - gc::space::ContinuousSpace* space = *it; + for (gc::space::ContinuousSpace* space : heap->GetContinuousSpaces()) { if (space->IsImageSpace()) { // Currently don't include the image space. } else if (space->IsZygoteSpace()) { gc::space::MallocSpace* malloc_space = space->AsMallocSpace(); zygoteSize += malloc_space->GetFootprint(); zygoteUsed += malloc_space->GetBytesAllocated(); - } else { - // This is the alloc space. + } else if (space->IsMallocSpace()) { + // This is a malloc space. gc::space::MallocSpace* malloc_space = space->AsMallocSpace(); allocSize += malloc_space->GetFootprint(); allocUsed += malloc_space->GetBytesAllocated(); + } else if (space->IsBumpPointerSpace()) { + gc::space::BumpPointerSpace* bump_pointer_space = space->AsBumpPointerSpace(); + allocSize += bump_pointer_space->Size(); + allocUsed += bump_pointer_space->GetBytesAllocated(); } } - typedef std::vector::const_iterator It2; - for (It2 it = discontinuous_spaces.begin(), end = discontinuous_spaces.end(); it != end; ++it) { - gc::space::DiscontinuousSpace* space = *it; + for (gc::space::DiscontinuousSpace* space : heap->GetDiscontinuousSpaces()) { if (space->IsLargeObjectSpace()) { largeObjectsSize += space->AsLargeObjectSpace()->GetBytesAllocated(); largeObjectsUsed += largeObjectsSize; From f0b9b466aec3332e32fb25457e1073e8649874fc Mon Sep 17 00:00:00 2001 From: Brian Carlstrom Date: Fri, 8 Nov 2013 16:42:41 -0800 Subject: [PATCH 0230/2402] Fix BUILD_DALVIK_HOST_JAVA_LIBRARY to BUILD_HOST_DALVIK_JAVA_LIBRARY (cherry picked from commit d7997ebc2ae418d593fab83afe484f161f52b455) Change-Id: Ibec6446b8e194e4feb92e18f4676088d201c7077 --- test/Android.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/Android.mk b/test/Android.mk index 881d664bb2a..e4885f321e1 100644 --- a/test/Android.mk +++ b/test/Android.mk @@ -86,7 +86,7 @@ define build-art-test-dex LOCAL_DEX_PREOPT_IMAGE := $(HOST_CORE_IMG_OUT) LOCAL_ADDITIONAL_DEPENDENCIES := art/build/Android.common.mk LOCAL_ADDITIONAL_DEPENDENCIES += $(LOCAL_PATH)/Android.mk - include $(BUILD_DALVIK_HOST_JAVA_LIBRARY) + include $(BUILD_HOST_DALVIK_JAVA_LIBRARY) ART_TEST_HOST_DEX_FILES += $$(LOCAL_MODULE_PATH)/$$(LOCAL_MODULE).jar endif endef From 73fbaadee029f310362c9d83a52177f00f2e5d77 Mon Sep 17 00:00:00 2001 From: Stephen Hines Date: Fri, 9 Aug 2013 02:14:36 -0700 Subject: [PATCH 0231/2402] Update ART for LLVM merge up to r187914. Removed NoFramePointerElimNonLeaf because this is now only specified via a function attribute (and thus covered by existing cases). Switch to llvm::sys::fs::F_* enums. Remove unused DisableSimplifyLibCalls(). (cherry picked from commit 1961a2716cf02f597f06c27a0850daa2dc917586) Change-Id: Ic7b311b635483a44265c89d47f37f4202a5b18f7 --- compiler/dex/portable/mir_to_gbc.cc | 2 +- compiler/llvm/llvm_compilation_unit.cc | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/compiler/dex/portable/mir_to_gbc.cc b/compiler/dex/portable/mir_to_gbc.cc index 963cbeb1d15..07bd2aa979e 100644 --- a/compiler/dex/portable/mir_to_gbc.cc +++ b/compiler/dex/portable/mir_to_gbc.cc @@ -1970,7 +1970,7 @@ void MirConverter::MethodMIR2Bitcode() { ::llvm::OwningPtr< ::llvm::tool_output_file> out_file( new ::llvm::tool_output_file(fname.c_str(), errmsg, - ::llvm::raw_fd_ostream::F_Binary)); + ::llvm::sys::fs::F_Binary)); if (!errmsg.empty()) { LOG(ERROR) << "Failed to create bitcode output file: " << errmsg; diff --git a/compiler/llvm/llvm_compilation_unit.cc b/compiler/llvm/llvm_compilation_unit.cc index feb495e35fe..038f5dc4eb6 100644 --- a/compiler/llvm/llvm_compilation_unit.cc +++ b/compiler/llvm/llvm_compilation_unit.cc @@ -211,7 +211,6 @@ bool LlvmCompilationUnit::MaterializeToRawOStream(::llvm::raw_ostream& out_strea ::llvm::TargetOptions target_options; target_options.FloatABIType = ::llvm::FloatABI::Soft; target_options.NoFramePointerElim = true; - target_options.NoFramePointerElimNonLeaf = true; target_options.UseSoftFloat = false; target_options.EnableFastISel = false; @@ -255,7 +254,7 @@ bool LlvmCompilationUnit::MaterializeToRawOStream(::llvm::raw_ostream& out_strea ::llvm::OwningPtr< ::llvm::tool_output_file> out_file( new ::llvm::tool_output_file(bitcode_filename_.c_str(), errmsg, - ::llvm::raw_fd_ostream::F_Binary)); + ::llvm::sys::fs::F_Binary)); if (!errmsg.empty()) { @@ -275,7 +274,6 @@ bool LlvmCompilationUnit::MaterializeToRawOStream(::llvm::raw_ostream& out_strea // pm_builder.Inliner = ::llvm::createAlwaysInlinerPass(); // pm_builder.Inliner = ::llvm::createPartialInliningPass(); pm_builder.OptLevel = 3; - pm_builder.DisableSimplifyLibCalls = 1; pm_builder.DisableUnitAtATime = 1; pm_builder.populateFunctionPassManager(fpm); pm_builder.populateModulePassManager(pm); From 218daa2d876c5989f956e8e54b8f28f33d11b31f Mon Sep 17 00:00:00 2001 From: Brian Carlstrom Date: Mon, 25 Nov 2013 14:51:44 -0800 Subject: [PATCH 0232/2402] Change thread.h to thread-inl.h for missing Thread::Current for rosalloc.cc Change-Id: Ieded9c5c93839c8f3eb5c5427229743a4c45c5ee --- runtime/gc/allocator/rosalloc.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/gc/allocator/rosalloc.cc b/runtime/gc/allocator/rosalloc.cc index 3030fa7fec4..469b0985fdf 100644 --- a/runtime/gc/allocator/rosalloc.cc +++ b/runtime/gc/allocator/rosalloc.cc @@ -15,7 +15,7 @@ */ #include "base/mutex-inl.h" -#include "thread.h" +#include "thread-inl.h" #include "thread_list.h" #include "rosalloc.h" From 5da3778746375b73e7e77c5f1371f29684674776 Mon Sep 17 00:00:00 2001 From: Brian Carlstrom Date: Mon, 25 Nov 2013 15:25:51 -0800 Subject: [PATCH 0233/2402] Turn up oat version to 11 Change-Id: Ic9627e3f38abf0ca3c143680dd372fb147413de8 --- runtime/oat.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/oat.cc b/runtime/oat.cc index 94897950e0e..50069b2a781 100644 --- a/runtime/oat.cc +++ b/runtime/oat.cc @@ -22,7 +22,7 @@ namespace art { const uint8_t OatHeader::kOatMagic[] = { 'o', 'a', 't', '\n' }; -const uint8_t OatHeader::kOatVersion[] = { '0', '1', '0', '\0' }; +const uint8_t OatHeader::kOatVersion[] = { '0', '1', '1', '\0' }; OatHeader::OatHeader() { memset(this, 0, sizeof(*this)); From ca2a24da53869a04e1947aa46d06ccce5247d6f4 Mon Sep 17 00:00:00 2001 From: Mathieu Chartier Date: Mon, 25 Nov 2013 15:12:12 -0800 Subject: [PATCH 0234/2402] Add interface for updating process state. Called from activity manager to let the heap know when it should perform compaction and trimming. Bug: 8981901 Change-Id: Ib8ea48d2dc9d6901c3f2e0554391721d6691e726 --- runtime/gc/heap.cc | 132 ++-------------------- runtime/gc/heap.h | 27 +++-- runtime/native/dalvik_system_VMRuntime.cc | 5 + 3 files changed, 31 insertions(+), 133 deletions(-) diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc index 5e62729c2ec..1e3689bbdaa 100644 --- a/runtime/gc/heap.cc +++ b/runtime/gc/heap.cc @@ -107,9 +107,8 @@ Heap::Heap(size_t initial_size, size_t growth_limit, size_t min_free, size_t max activity_thread_(NULL), application_thread_(NULL), last_process_state_id_(NULL), - // Initially care about pauses in case we never get notified of process states, or if the JNI - // code becomes broken. - care_about_pause_times_(true), + // Initially assume we perceive jank in case the process state is never updated. + process_state_(kProcessStateJankPerceptible), concurrent_start_bytes_(concurrent_gc_ ? initial_size - kMinConcurrentRemainingBytes : std::numeric_limits::max()), total_bytes_freed_ever_(0), @@ -325,6 +324,10 @@ void Heap::DecrementDisableGC(Thread* self) { --gc_disable_count_; } +void Heap::UpdateProcessState(ProcessState process_state) { + process_state_ = process_state; +} + void Heap::CreateThreadPool() { const size_t num_threads = std::max(parallel_gc_threads_, conc_gc_threads_); if (num_threads != 0) { @@ -373,124 +376,6 @@ void Heap::DeleteThreadPool() { thread_pool_.reset(nullptr); } -static bool ReadStaticInt(JNIEnvExt* env, jclass clz, const char* name, int* out_value) { - DCHECK(out_value != NULL); - jfieldID field = env->GetStaticFieldID(clz, name, "I"); - if (field == NULL) { - env->ExceptionClear(); - return false; - } - *out_value = env->GetStaticIntField(clz, field); - return true; -} - -void Heap::ListenForProcessStateChange() { - VLOG(heap) << "Heap notified of process state change"; - - Thread* self = Thread::Current(); - JNIEnvExt* env = self->GetJniEnv(); - - if (!have_zygote_space_) { - return; - } - - if (activity_thread_class_ == NULL) { - jclass clz = env->FindClass("android/app/ActivityThread"); - if (clz == NULL) { - env->ExceptionClear(); - LOG(WARNING) << "Could not find activity thread class in process state change"; - return; - } - activity_thread_class_ = reinterpret_cast(env->NewGlobalRef(clz)); - } - - if (activity_thread_class_ != NULL && activity_thread_ == NULL) { - jmethodID current_activity_method = env->GetStaticMethodID(activity_thread_class_, - "currentActivityThread", - "()Landroid/app/ActivityThread;"); - if (current_activity_method == NULL) { - env->ExceptionClear(); - LOG(WARNING) << "Could not get method for currentActivityThread"; - return; - } - - jobject obj = env->CallStaticObjectMethod(activity_thread_class_, current_activity_method); - if (obj == NULL) { - env->ExceptionClear(); - LOG(WARNING) << "Could not get current activity"; - return; - } - activity_thread_ = env->NewGlobalRef(obj); - } - - if (process_state_cares_about_pause_time_.empty()) { - // Just attempt to do this the first time. - jclass clz = env->FindClass("android/app/ActivityManager"); - if (clz == NULL) { - LOG(WARNING) << "Activity manager class is null"; - return; - } - ScopedLocalRef activity_manager(env, clz); - std::vector care_about_pauses; - care_about_pauses.push_back("PROCESS_STATE_TOP"); - care_about_pauses.push_back("PROCESS_STATE_IMPORTANT_BACKGROUND"); - // Attempt to read the constants and classify them as whether or not we care about pause times. - for (size_t i = 0; i < care_about_pauses.size(); ++i) { - int process_state = 0; - if (ReadStaticInt(env, activity_manager.get(), care_about_pauses[i], &process_state)) { - process_state_cares_about_pause_time_.insert(process_state); - VLOG(heap) << "Adding process state " << process_state - << " to set of states which care about pause time"; - } - } - } - - if (application_thread_class_ == NULL) { - jclass clz = env->FindClass("android/app/ActivityThread$ApplicationThread"); - if (clz == NULL) { - env->ExceptionClear(); - LOG(WARNING) << "Could not get application thread class"; - return; - } - application_thread_class_ = reinterpret_cast(env->NewGlobalRef(clz)); - last_process_state_id_ = env->GetFieldID(application_thread_class_, "mLastProcessState", "I"); - if (last_process_state_id_ == NULL) { - env->ExceptionClear(); - LOG(WARNING) << "Could not get last process state member"; - return; - } - } - - if (application_thread_class_ != NULL && application_thread_ == NULL) { - jmethodID get_application_thread = - env->GetMethodID(activity_thread_class_, "getApplicationThread", - "()Landroid/app/ActivityThread$ApplicationThread;"); - if (get_application_thread == NULL) { - LOG(WARNING) << "Could not get method ID for get application thread"; - return; - } - - jobject obj = env->CallObjectMethod(activity_thread_, get_application_thread); - if (obj == NULL) { - LOG(WARNING) << "Could not get application thread"; - return; - } - - application_thread_ = env->NewGlobalRef(obj); - } - - if (application_thread_ != NULL && last_process_state_id_ != NULL) { - int process_state = env->GetIntField(application_thread_, last_process_state_id_); - env->ExceptionClear(); - - care_about_pause_times_ = process_state_cares_about_pause_time_.find(process_state) != - process_state_cares_about_pause_time_.end(); - - VLOG(heap) << "New process state " << process_state - << " care about pauses " << care_about_pause_times_; - } -} - void Heap::AddSpace(space::Space* space) { DCHECK(space != NULL); WriterMutexLock mu(Thread::Current(), *Locks::heap_bitmap_lock_); @@ -1426,7 +1311,7 @@ collector::GcType Heap::CollectGarbageInternal(collector::GcType gc_type, GcCaus // Grow the heap so that we know when to perform the next GC. GrowForUtilization(gc_type, collector->GetDurationNs()); - if (care_about_pause_times_) { + if (CareAboutPauseTimes()) { const size_t duration = collector->GetDurationNs(); std::vector pauses = collector->GetPauseTimes(); // GC for alloc pauses the allocating thread, so consider it as a pause. @@ -2143,10 +2028,9 @@ void Heap::RequestHeapTrim() { } last_trim_time_ms_ = ms_time; - ListenForProcessStateChange(); // Trim only if we do not currently care about pause times. - if (!care_about_pause_times_) { + if (!CareAboutPauseTimes()) { JNIEnv* env = self->GetJniEnv(); DCHECK(WellKnownClasses::java_lang_Daemons != NULL); DCHECK(WellKnownClasses::java_lang_Daemons_requestHeapTrim != NULL); diff --git a/runtime/gc/heap.h b/runtime/gc/heap.h index 8c5746de365..046fbac319d 100644 --- a/runtime/gc/heap.h +++ b/runtime/gc/heap.h @@ -119,6 +119,18 @@ static constexpr HeapVerificationMode kDesiredHeapVerification = kNoHeapVerifica // If true, use rosalloc/RosAllocSpace instead of dlmalloc/DlMallocSpace static constexpr bool kUseRosAlloc = true; +// The process state passed in from the activity manager, used to determine when to do trimming +// and compaction. +enum ProcessState { + kProcessStateJankPerceptible = 0, + kProcessStateJankImperceptible = 1, +}; + +// If true, measure the total allocation time. +static constexpr bool kMeasureAllocationTime = false; +// Primitive arrays larger than this size are put in the large object space. +static constexpr size_t kLargeObjectThreshold = 3 * kPageSize; + class Heap { public: // If true, measure the total allocation time. @@ -287,6 +299,9 @@ class Heap { // waited for. collector::GcType WaitForGcToComplete(Thread* self) LOCKS_EXCLUDED(gc_complete_lock_); + // Update the heap's process state to a new value, may cause compaction to occur. + void UpdateProcessState(ProcessState process_state); + const std::vector& GetContinuousSpaces() const { return continuous_spaces_; } @@ -451,10 +466,7 @@ class Heap { // Mark the specified allocation stack as live. void MarkAllocStackAsLive(accounting::ObjectStack* stack) - EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_); - - // Gets called when we get notified by ActivityThread that the process state has changed. - void ListenForProcessStateChange(); + EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_); // DEPRECATED: Should remove in "near" future when support for multiple image spaces is added. // Assumes there is only one image space. @@ -475,7 +487,7 @@ class Heap { // Returns true if we currently care about pause times. bool CareAboutPauseTimes() const { - return care_about_pause_times_; + return process_state_ == kProcessStateJankPerceptible; } // Thread pool. @@ -713,11 +725,8 @@ class Heap { jobject application_thread_; jfieldID last_process_state_id_; - // Process states which care about pause times. - std::set process_state_cares_about_pause_time_; - // Whether or not we currently care about pause times. - bool care_about_pause_times_; + ProcessState process_state_; // When num_bytes_allocated_ exceeds this amount then a concurrent GC should be requested so that // it completes ahead of an allocation failing. diff --git a/runtime/native/dalvik_system_VMRuntime.cc b/runtime/native/dalvik_system_VMRuntime.cc index fd3d91ef99a..726a8f17695 100644 --- a/runtime/native/dalvik_system_VMRuntime.cc +++ b/runtime/native/dalvik_system_VMRuntime.cc @@ -166,6 +166,10 @@ static void VMRuntime_registerNativeFree(JNIEnv* env, jobject, jint bytes) { Runtime::Current()->GetHeap()->RegisterNativeFree(env, bytes); } +static void VMRuntime_updateProcessState(JNIEnv* env, jobject, jint process_state) { + Runtime::Current()->GetHeap()->UpdateProcessState(static_cast(process_state)); +} + static void VMRuntime_trimHeap(JNIEnv*, jobject) { Runtime::Current()->GetHeap()->Trim(); } @@ -496,6 +500,7 @@ static JNINativeMethod gMethods[] = { NATIVE_METHOD(VMRuntime, setTargetSdkVersionNative, "(I)V"), NATIVE_METHOD(VMRuntime, registerNativeAllocation, "(I)V"), NATIVE_METHOD(VMRuntime, registerNativeFree, "(I)V"), + NATIVE_METHOD(VMRuntime, updateProcessState, "(I)V"), NATIVE_METHOD(VMRuntime, startJitCompilation, "()V"), NATIVE_METHOD(VMRuntime, trimHeap, "()V"), NATIVE_METHOD(VMRuntime, vmVersion, "()Ljava/lang/String;"), From 87118ed6f3f99e7df33214c277cf200a7b9a7499 Mon Sep 17 00:00:00 2001 From: Sebastien Hertz Date: Tue, 26 Nov 2013 17:57:18 +0100 Subject: [PATCH 0235/2402] Fix JDWP single-step removal. Change-Id: I4b977ea7c60b32723009e571269ca5fc1c6e264e --- runtime/debugger.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/debugger.cc b/runtime/debugger.cc index 6c4d1308799..52a2141a0ba 100644 --- a/runtime/debugger.cc +++ b/runtime/debugger.cc @@ -2577,7 +2577,7 @@ void Dbg::UnconfigureStep(JDWP::ObjectId thread_id) { MutexLock mu(soa.Self(), *Locks::thread_list_lock_); Thread* thread; JDWP::JdwpError error = DecodeThread(soa, thread_id, thread); - if (error != JDWP::ERR_NONE) { + if (error == JDWP::ERR_NONE) { SingleStepControl* single_step_control = thread->GetSingleStepControl(); DCHECK(single_step_control != nullptr); single_step_control->is_active = false; From 3e5af82ae1a2cd69b7b045ac008ac3b394d17f41 Mon Sep 17 00:00:00 2001 From: Vladimir Marko Date: Thu, 21 Nov 2013 15:01:20 +0000 Subject: [PATCH 0236/2402] Intrinsic Unsafe.CompareAndSwapLong() for ARM. (cherry picked from cb53fcd79b1a5ce608208ec454b5c19f64aaba37) Change-Id: Iadd3cc8b4ed390670463b80f8efd579ce6ece226 --- .../quick/arm/arm_dex_file_method_inliner.cc | 4 +- compiler/dex/quick/arm/arm_lir.h | 10 +- compiler/dex/quick/arm/assemble_arm.cc | 13 +- compiler/dex/quick/arm/int_arm.cc | 135 +++++++++++++++--- compiler/dex/quick/mir_to_lir.h | 2 + disassembler/disassembler_arm.cc | 47 ++++-- 6 files changed, 166 insertions(+), 45 deletions(-) diff --git a/compiler/dex/quick/arm/arm_dex_file_method_inliner.cc b/compiler/dex/quick/arm/arm_dex_file_method_inliner.cc index 257b2c4e945..59f7202f33b 100644 --- a/compiler/dex/quick/arm/arm_dex_file_method_inliner.cc +++ b/compiler/dex/quick/arm/arm_dex_file_method_inliner.cc @@ -66,8 +66,8 @@ const DexFileMethodInliner::IntrinsicDef ArmDexFileMethodInliner::kIntrinsicMeth INTRINSIC(SunMiscUnsafe, CompareAndSwapInt, ObjectJII_Z, kIntrinsicCas, kIntrinsicFlagNone), - // INTRINSIC(SunMiscUnsafe, CompareAndSwapLong, ObjectJJJ_Z, kIntrinsicCas, - // kIntrinsicFlagIsLong), + INTRINSIC(SunMiscUnsafe, CompareAndSwapLong, ObjectJJJ_Z, kIntrinsicCas, + kIntrinsicFlagIsLong), INTRINSIC(SunMiscUnsafe, CompareAndSwapObject, ObjectJObjectObject_Z, kIntrinsicCas, kIntrinsicFlagIsObject), diff --git a/compiler/dex/quick/arm/arm_lir.h b/compiler/dex/quick/arm/arm_lir.h index 8cd7c9440f5..395c78828eb 100644 --- a/compiler/dex/quick/arm/arm_lir.h +++ b/compiler/dex/quick/arm/arm_lir.h @@ -426,9 +426,11 @@ enum ArmOpcode { kThumb2Vmovd_IMM8, // vmov.f64 [111011101] D [11] imm4h[19-16] vd[15-12] [10110000] imm4l[3-0]. kThumb2Mla, // mla [111110110000] rn[19-16] ra[15-12] rd[7-4] [0000] rm[3-0]. kThumb2Umull, // umull [111110111010] rn[19-16], rdlo[15-12] rdhi[11-8] [0000] rm[3-0]. - kThumb2Ldrex, // ldrex [111010000101] rn[19-16] rt[11-8] [1111] imm8[7-0]. - kThumb2Strex, // strex [111010000100] rn[19-16] rt[11-8] rd[11-8] imm8[7-0]. - kThumb2Clrex, // clrex [111100111011111110000111100101111]. + kThumb2Ldrex, // ldrex [111010000101] rn[19-16] rt[15-12] [1111] imm8[7-0]. + kThumb2Ldrexd, // ldrexd [111010001101] rn[19-16] rt[15-12] rt2[11-8] [11111111]. + kThumb2Strex, // strex [111010000100] rn[19-16] rt[15-12] rd[11-8] imm8[7-0]. + kThumb2Strexd, // strexd [111010001100] rn[19-16] rt[15-12] rt2[11-8] [0111] Rd[3-0]. + kThumb2Clrex, // clrex [11110011101111111000111100101111]. kThumb2Bfi, // bfi [111100110110] rn[19-16] [0] imm3[14-12] rd[11-8] imm2[7-6] [0] msb[4-0]. kThumb2Bfc, // bfc [11110011011011110] [0] imm3[14-12] rd[11-8] imm2[7-6] [0] msb[4-0]. kThumb2Dmb, // dmb [1111001110111111100011110101] option[3-0]. @@ -447,7 +449,7 @@ enum ArmOpcode { kThumb2MovImm16HST, // Special purpose version for switch table use. kThumb2LdmiaWB, // ldmia [111010011001[ rn[19..16] mask[15..0]. kThumb2SubsRRI12, // setflags encoding. - kThumb2OrrRRRs, // orrx [111010100101] rn[19..16] [0000] rd[11..8] [0000] rm[3..0]. + kThumb2OrrRRRs, // orrs [111010100101] rn[19..16] [0000] rd[11..8] [0000] rm[3..0]. kThumb2Push1, // t3 encoding of push. kThumb2Pop1, // t3 encoding of pop. kThumb2RsubRRR, // rsb [111010111101] rn[19..16] [0000] rd[11..8] [0000] rm[3..0]. diff --git a/compiler/dex/quick/arm/assemble_arm.cc b/compiler/dex/quick/arm/assemble_arm.cc index 1c81a5aa0a0..b3236ae23eb 100644 --- a/compiler/dex/quick/arm/assemble_arm.cc +++ b/compiler/dex/quick/arm/assemble_arm.cc @@ -877,8 +877,7 @@ const ArmEncodingMap ArmMir2Lir::EncodingMap[kArmLast] = { "vmov.f64", "!0S, #0x!1h", 4, kFixupNone), ENCODING_MAP(kThumb2Mla, 0xfb000000, kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0, - kFmtBitBlt, 15, 12, - IS_QUAD_OP | REG_DEF0 | REG_USE1 | REG_USE2 | REG_USE3, + kFmtBitBlt, 15, 12, IS_QUAD_OP | REG_DEF0_USE123, "mla", "!0C, !1C, !2C, !3C", 4, kFixupNone), ENCODING_MAP(kThumb2Umull, 0xfba00000, kFmtBitBlt, 15, 12, kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, @@ -889,10 +888,18 @@ const ArmEncodingMap ArmMir2Lir::EncodingMap[kArmLast] = { kFmtBitBlt, 15, 12, kFmtBitBlt, 19, 16, kFmtBitBlt, 7, 0, kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1 | IS_LOAD, "ldrex", "!0C, [!1C, #!2E]", 4, kFixupNone), + ENCODING_MAP(kThumb2Ldrexd, 0xe8d0007f, + kFmtBitBlt, 15, 12, kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, + kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF01_USE2 | IS_LOAD, + "ldrexd", "!0C, !1C, [!2C]", 4, kFixupNone), ENCODING_MAP(kThumb2Strex, 0xe8400000, kFmtBitBlt, 11, 8, kFmtBitBlt, 15, 12, kFmtBitBlt, 19, 16, kFmtBitBlt, 7, 0, IS_QUAD_OP | REG_DEF0_USE12 | IS_STORE, - "strex", "!0C,!1C, [!2C, #!2E]", 4, kFixupNone), + "strex", "!0C, !1C, [!2C, #!2E]", 4, kFixupNone), + ENCODING_MAP(kThumb2Strexd, 0xe8c00070, + kFmtBitBlt, 3, 0, kFmtBitBlt, 15, 12, kFmtBitBlt, 11, 8, + kFmtBitBlt, 19, 16, IS_QUAD_OP | REG_DEF0_USE123 | IS_STORE, + "strexd", "!0C, !1C, !2C, [!3C]", 4, kFixupNone), ENCODING_MAP(kThumb2Clrex, 0xf3bf8f2f, kFmtUnused, -1, -1, kFmtUnused, -1, -1, kFmtUnused, -1, -1, kFmtUnused, -1, -1, NO_OPERAND, diff --git a/compiler/dex/quick/arm/int_arm.cc b/compiler/dex/quick/arm/int_arm.cc index 97271794f7d..e839fe5c5d5 100644 --- a/compiler/dex/quick/arm/int_arm.cc +++ b/compiler/dex/quick/arm/int_arm.cc @@ -561,22 +561,67 @@ void ArmMir2Lir::OpTlsCmp(ThreadOffset offset, int val) { } bool ArmMir2Lir::GenInlinedCas(CallInfo* info, bool is_long, bool is_object) { - DCHECK(!is_long); // not supported yet DCHECK_EQ(cu_->instruction_set, kThumb2); // Unused - RegLocation rl_src_unsafe = info->args[0]; RegLocation rl_src_obj = info->args[1]; // Object - known non-null RegLocation rl_src_offset = info->args[2]; // long low rl_src_offset.wide = 0; // ignore high half in info->args[3] RegLocation rl_src_expected = info->args[4]; // int, long or Object - RegLocation rl_src_new_value = info->args[5]; // int, long or Object + // If is_long, high half is in info->args[5] + RegLocation rl_src_new_value = info->args[is_long ? 6 : 5]; // int, long or Object + // If is_long, high half is in info->args[7] RegLocation rl_dest = InlineTarget(info); // boolean place for result + // We have only 5 temporary registers available and actually only 4 if the InlineTarget + // above locked one of the temps. For a straightforward CAS64 we need 7 registers: + // r_ptr (1), new_value (2), expected(2) and ldrexd result (2). If neither expected nor + // new_value is in a non-temp core register we shall reload them in the ldrex/strex loop + // into the same temps, reducing the number of required temps down to 5. We shall work + // around the potentially locked temp by using LR for r_ptr, unconditionally. + // TODO: Pass information about the need for more temps to the stack frame generation + // code so that we can rely on being able to allocate enough temps. + DCHECK(!reg_pool_->core_regs[rARM_LR].is_temp); + MarkTemp(rARM_LR); + FreeTemp(rARM_LR); + LockTemp(rARM_LR); + bool load_early = true; + if (is_long) { + bool expected_is_core_reg = + rl_src_expected.location == kLocPhysReg && !IsFpReg(rl_src_expected.low_reg); + bool new_value_is_core_reg = + rl_src_new_value.location == kLocPhysReg && !IsFpReg(rl_src_new_value.low_reg); + bool expected_is_good_reg = expected_is_core_reg && !IsTemp(rl_src_expected.low_reg); + bool new_value_is_good_reg = new_value_is_core_reg && !IsTemp(rl_src_new_value.low_reg); + + if (!expected_is_good_reg && !new_value_is_good_reg) { + // None of expected/new_value is non-temp reg, need to load both late + load_early = false; + // Make sure they are not in the temp regs and the load will not be skipped. + if (expected_is_core_reg) { + FlushRegWide(rl_src_expected.low_reg, rl_src_expected.high_reg); + ClobberSReg(rl_src_expected.s_reg_low); + ClobberSReg(GetSRegHi(rl_src_expected.s_reg_low)); + rl_src_expected.location = kLocDalvikFrame; + } + if (new_value_is_core_reg) { + FlushRegWide(rl_src_new_value.low_reg, rl_src_new_value.high_reg); + ClobberSReg(rl_src_new_value.s_reg_low); + ClobberSReg(GetSRegHi(rl_src_new_value.s_reg_low)); + rl_src_new_value.location = kLocDalvikFrame; + } + } + } // Release store semantics, get the barrier out of the way. TODO: revisit GenMemBarrier(kStoreLoad); RegLocation rl_object = LoadValue(rl_src_obj, kCoreReg); - RegLocation rl_new_value = LoadValue(rl_src_new_value, kCoreReg); + RegLocation rl_new_value; + if (!is_long) { + rl_new_value = LoadValue(rl_src_new_value, kCoreReg); + } else if (load_early) { + rl_new_value = LoadValueWide(rl_src_new_value, kCoreReg); + } if (is_object && !mir_graph_->IsConstantNullRef(rl_new_value)) { // Mark card for object assuming new value is stored. @@ -585,7 +630,7 @@ bool ArmMir2Lir::GenInlinedCas(CallInfo* info, bool is_long, bool is_object) { RegLocation rl_offset = LoadValue(rl_src_offset, kCoreReg); - int r_ptr = AllocTemp(); + int r_ptr = rARM_LR; OpRegRegReg(kOpAdd, r_ptr, rl_object.low_reg, rl_offset.low_reg); // Free now unneeded rl_object and rl_offset to give more temps. @@ -594,29 +639,77 @@ bool ArmMir2Lir::GenInlinedCas(CallInfo* info, bool is_long, bool is_object) { ClobberSReg(rl_offset.s_reg_low); FreeTemp(rl_offset.low_reg); - RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true); - LoadConstant(rl_result.low_reg, 0); // r_result := 0 + RegLocation rl_expected; + if (!is_long) { + rl_expected = LoadValue(rl_src_expected, kCoreReg); + } else if (load_early) { + rl_expected = LoadValueWide(rl_src_expected, kCoreReg); + } else { + rl_new_value.low_reg = rl_expected.low_reg = AllocTemp(); + rl_new_value.high_reg = rl_expected.high_reg = AllocTemp(); + } + + // do { + // tmp = [r_ptr] - expected; + // } while (tmp == 0 && failure([r_ptr] <- r_new_value)); + // result = tmp != 0; - // while ([r_ptr] == rExpected && r_result == 0) { - // [r_ptr] <- r_new_value && r_result := success ? 0 : 1 - // r_result ^= 1 - // } - int r_old_value = AllocTemp(); + int r_tmp = AllocTemp(); LIR* target = NewLIR0(kPseudoTargetLabel); - NewLIR3(kThumb2Ldrex, r_old_value, r_ptr, 0); - - RegLocation rl_expected = LoadValue(rl_src_expected, kCoreReg); - OpRegReg(kOpCmp, r_old_value, rl_expected.low_reg); - FreeTemp(r_old_value); // Now unneeded. - OpIT(kCondEq, "TT"); - NewLIR4(kThumb2Strex /* eq */, rl_result.low_reg, rl_new_value.low_reg, r_ptr, 0); - FreeTemp(r_ptr); // Now unneeded. - OpRegImm(kOpXor /* eq */, rl_result.low_reg, 1); - OpRegImm(kOpCmp /* eq */, rl_result.low_reg, 0); + + if (is_long) { + int r_tmp_high = AllocTemp(); + if (!load_early) { + LoadValueDirectWide(rl_src_expected, rl_expected.low_reg, rl_expected.high_reg); + } + NewLIR3(kThumb2Ldrexd, r_tmp, r_tmp_high, r_ptr); + OpRegReg(kOpSub, r_tmp, rl_expected.low_reg); + OpRegReg(kOpSub, r_tmp_high, rl_expected.high_reg); + if (!load_early) { + LoadValueDirectWide(rl_src_new_value, rl_new_value.low_reg, rl_new_value.high_reg); + } + // Make sure we use ORR that sets the ccode + if (ARM_LOWREG(r_tmp) && ARM_LOWREG(r_tmp_high)) { + NewLIR2(kThumbOrr, r_tmp, r_tmp_high); + } else { + NewLIR4(kThumb2OrrRRRs, r_tmp, r_tmp, r_tmp_high, 0); + } + FreeTemp(r_tmp_high); // Now unneeded + + DCHECK(last_lir_insn_->u.m.def_mask & ENCODE_CCODE); + OpIT(kCondEq, "T"); + NewLIR4(kThumb2Strexd /* eq */, r_tmp, rl_new_value.low_reg, rl_new_value.high_reg, r_ptr); + + } else { + NewLIR3(kThumb2Ldrex, r_tmp, r_ptr, 0); + OpRegReg(kOpSub, r_tmp, rl_expected.low_reg); + DCHECK(last_lir_insn_->u.m.def_mask & ENCODE_CCODE); + OpIT(kCondEq, "T"); + NewLIR4(kThumb2Strex /* eq */, r_tmp, rl_new_value.low_reg, r_ptr, 0); + } + + // Still one conditional left from OpIT(kCondEq, "T") from either branch + OpRegImm(kOpCmp /* eq */, r_tmp, 1); OpCondBranch(kCondEq, target); + if (!load_early) { + FreeTemp(rl_expected.low_reg); // Now unneeded. + FreeTemp(rl_expected.high_reg); // Now unneeded. + } + + // result := (tmp1 != 0) ? 0 : 1; + RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true); + OpRegRegImm(kOpRsub, rl_result.low_reg, r_tmp, 1); + DCHECK(last_lir_insn_->u.m.def_mask & ENCODE_CCODE); + OpIT(kCondCc, ""); + LoadConstant(rl_result.low_reg, 0); /* cc */ + FreeTemp(r_tmp); // Now unneeded. + StoreValue(rl_dest, rl_result); + // Now, restore lr to its non-temp status. + Clobber(rARM_LR); + UnmarkTemp(rARM_LR); return true; } diff --git a/compiler/dex/quick/mir_to_lir.h b/compiler/dex/quick/mir_to_lir.h index ad9b0de3d53..f8a2d03ec8f 100644 --- a/compiler/dex/quick/mir_to_lir.h +++ b/compiler/dex/quick/mir_to_lir.h @@ -87,6 +87,7 @@ typedef uint32_t CodeOffset; // Native code offset in bytes. #define REG_DEF0_USE01 (REG_DEF0 | REG_USE01) #define REG_DEF0_USE0 (REG_DEF0 | REG_USE0) #define REG_DEF0_USE12 (REG_DEF0 | REG_USE12) +#define REG_DEF0_USE123 (REG_DEF0 | REG_USE123) #define REG_DEF0_USE1 (REG_DEF0 | REG_USE1) #define REG_DEF0_USE2 (REG_DEF0 | REG_USE2) #define REG_DEFAD_USEAD (REG_DEFAD_USEA | REG_USED) @@ -98,6 +99,7 @@ typedef uint32_t CodeOffset; // Native code offset in bytes. #define REG_USE02 (REG_USE0 | REG_USE2) #define REG_USE12 (REG_USE1 | REG_USE2) #define REG_USE23 (REG_USE2 | REG_USE3) +#define REG_USE123 (REG_USE1 | REG_USE2 | REG_USE3) struct BasicBlock; struct CallInfo; diff --git a/disassembler/disassembler_arm.cc b/disassembler/disassembler_arm.cc index 936fb07728b..90d84d5a725 100644 --- a/disassembler/disassembler_arm.cc +++ b/disassembler/disassembler_arm.cc @@ -440,18 +440,34 @@ size_t DisassemblerArm::DumpThumb32(std::ostream& os, const uint8_t* instr_ptr) if (op3 == 0) { // op3 is 00, op4 is 00 opcode << "strex"; args << Rd << ", " << Rt << ", [" << Rn << ", #" << (imm8 << 2) << "]"; + if (Rd.r == 13 || Rd.r == 15 || Rt.r == 13 || Rt.r == 15 || Rn.r == 15 || + Rd.r == Rn.r || Rd.r == Rt.r) { + args << " (UNPREDICTABLE)"; + } } else { // op3 is 01, op4 is 00 // this is one of strexb, strexh or strexd int op5 = (instr >> 4) & 0xf; switch (op5) { case 4: - opcode << "strexb"; - break; case 5: - opcode << "strexh"; + opcode << ((op5 == 4) ? "strexb" : "strexh"); + Rd = ArmRegister(instr, 0); + args << Rd << ", " << Rt << ", [" << Rn << "]"; + if (Rd.r == 13 || Rd.r == 15 || Rt.r == 13 || Rt.r == 15 || Rn.r == 15 || + Rd.r == Rn.r || Rd.r == Rt.r || (instr & 0xf00) != 0xf00) { + args << " (UNPREDICTABLE)"; + } break; case 7: opcode << "strexd"; + ArmRegister Rt2 = Rd; + Rd = ArmRegister(instr, 0); + args << Rd << ", " << Rt << ", " << Rt2 << ", [" << Rn << "]"; + if (Rd.r == 13 || Rd.r == 15 || Rt.r == 13 || Rt.r == 15 || + Rt2.r == 13 || Rt2.r == 15 || Rn.r == 15 || + Rd.r == Rn.r || Rd.r == Rt.r || Rd.r == Rt2.r) { + args << " (UNPREDICTABLE)"; + } break; } } @@ -460,6 +476,9 @@ size_t DisassemblerArm::DumpThumb32(std::ostream& os, const uint8_t* instr_ptr) if (op3 == 0) { // op3 is 00, op4 is 01 opcode << "ldrex"; args << Rt << ", [" << Rn << ", #" << (imm8 << 2) << "]"; + if (Rt.r == 13 || Rt.r == 15 || Rn.r == 15 || (instr & 0xf00) != 0xf00) { + args << " (UNPREDICTABLE)"; + } } else { // op3 is 01, op4 is 01 // this is one of strexb, strexh or strexd int op5 = (instr >> 4) & 0xf; @@ -471,13 +490,20 @@ size_t DisassemblerArm::DumpThumb32(std::ostream& os, const uint8_t* instr_ptr) opcode << "tbh"; break; case 4: - opcode << "ldrexb"; - break; case 5: - opcode << "ldrexh"; + opcode << ((op5 == 4) ? "ldrexb" : "ldrexh"); + args << Rt << ", [" << Rn << "]"; + if (Rt.r == 13 || Rt.r == 15 || Rn.r == 15 || (instr & 0xf0f) != 0xf0f) { + args << " (UNPREDICTABLE)"; + } break; case 7: opcode << "ldrexd"; + args << Rt << ", " << Rd /* Rt2 */ << ", [" << Rn << "]"; + if (Rt.r == 13 || Rt.r == 15 || Rd.r == 13 /* Rt2 */ || Rd.r == 15 /* Rt2 */ || + Rn.r == 15 || (instr & 0x00f) != 0x00f) { + args << " (UNPREDICTABLE)"; + } break; } } @@ -507,15 +533,6 @@ size_t DisassemblerArm::DumpThumb32(std::ostream& os, const uint8_t* instr_ptr) } } - - if (op3 == 0 && op4 == 0) { // STREX - ArmRegister Rd(instr, 8); - opcode << "strex"; - args << Rd << ", " << Rt << ", [" << Rn << ", #" << (imm8 << 2) << "]"; - } else if (op3 == 0 && op4 == 1) { // LDREX - opcode << "ldrex"; - args << Rt << ", [" << Rn << ", #" << (imm8 << 2) << "]"; - } } else if ((op2 & 0x60) == 0x20) { // 01x xxxx // Data-processing (shifted register) // |111|1110|0000|0|0000|1111|1100|00|00|0000| From 1da1e2fceb0030b4b76b43510b1710a9613e0c2e Mon Sep 17 00:00:00 2001 From: buzbee Date: Fri, 15 Nov 2013 13:37:01 -0800 Subject: [PATCH 0237/2402] More compile-time tuning Another round of compile-time tuning, this time yeilding in the vicinity of 3% total reduction in compile time (which means about double that for the Quick Compile portion). Primary improvements are skipping the basic block combine optimization pass when using Quick (because we already have big blocks), combining the null check elimination and type inference passes, and limiting expensive local value number analysis to only those blocks which might benefit from it. Following this CL, the actual compile phase consumes roughly 60% of the total dex2oat time on the host, and 55% on the target (Note, I'm subtracting out the Deduping time here, which the timing logger normally counts against the compiler). A sample breakdown of the compilation time follows (this taken on PlusOne.apk w/ a Nexus 4): 39.00% -> MIR2LIR: 1374.90 (Note: includes local optimization & scheduling) 10.25% -> MIROpt:SSATransform: 361.31 8.45% -> BuildMIRGraph: 297.80 7.55% -> Assemble: 266.16 6.87% -> MIROpt:NCE_TypeInference: 242.22 5.56% -> Dedupe: 196.15 3.45% -> MIROpt:BBOpt: 121.53 3.20% -> RegisterAllocation: 112.69 3.00% -> PcMappingTable: 105.65 2.90% -> GcMap: 102.22 2.68% -> Launchpads: 94.50 1.16% -> MIROpt:InitRegLoc: 40.94 1.16% -> Cleanup: 40.93 1.10% -> MIROpt:CodeLayout: 38.80 0.97% -> MIROpt:ConstantProp: 34.35 0.96% -> MIROpt:UseCount: 33.75 0.86% -> MIROpt:CheckFilters: 30.28 0.44% -> SpecialMIR2LIR: 15.53 0.44% -> MIROpt:BBCombine: 15.41 (cherry pick of 9e8e234af4430abe8d144414e272cd72d215b5f3) Change-Id: I86c665fa7e88b75eb75629a99fd292ff8c449969 --- compiler/dex/dataflow_iterator-inl.h | 2 + compiler/dex/dataflow_iterator.h | 4 + compiler/dex/frontend.cc | 24 +- compiler/dex/mir_dataflow.cc | 112 +++-- compiler/dex/mir_graph.cc | 6 +- compiler/dex/mir_graph.h | 85 ++-- compiler/dex/mir_optimization.cc | 209 +++++---- compiler/dex/portable/mir_to_gbc.cc | 2 +- compiler/dex/quick/arm/assemble_arm.cc | 1 - compiler/dex/quick/local_optimizations.cc | 6 +- compiler/dex/quick/mips/assemble_mips.cc | 1 - compiler/dex/quick/mir_to_lir.cc | 2 +- compiler/dex/quick/x86/assemble_x86.cc | 1 - compiler/dex/vreg_analysis.cc | 490 +++++++++++----------- 14 files changed, 485 insertions(+), 460 deletions(-) diff --git a/compiler/dex/dataflow_iterator-inl.h b/compiler/dex/dataflow_iterator-inl.h index 74f36ddd810..64e5fa64e34 100644 --- a/compiler/dex/dataflow_iterator-inl.h +++ b/compiler/dex/dataflow_iterator-inl.h @@ -37,6 +37,7 @@ inline BasicBlock* DataflowIterator::ForwardRepeatNext(bool had_change) { BasicBlock* res = NULL; if ((idx_ >= end_idx_) && changed_) { idx_ = start_idx_; + repeats_++; changed_ = false; } if (idx_ < end_idx_) { @@ -62,6 +63,7 @@ inline BasicBlock* DataflowIterator::ReverseRepeatNext(bool had_change) { BasicBlock* res = NULL; if ((idx_ < 0) && changed_) { idx_ = start_idx_; + repeats_++; changed_ = false; } if (idx_ >= 0) { diff --git a/compiler/dex/dataflow_iterator.h b/compiler/dex/dataflow_iterator.h index 26e36653bef..a0c1c123e7f 100644 --- a/compiler/dex/dataflow_iterator.h +++ b/compiler/dex/dataflow_iterator.h @@ -37,6 +37,7 @@ namespace art { class DataflowIterator { public: virtual ~DataflowIterator() {} + int32_t GetRepeatCount() { return repeats_; } protected: DataflowIterator(MIRGraph* mir_graph, int32_t start_idx, int32_t end_idx) @@ -45,6 +46,7 @@ namespace art { end_idx_(end_idx), block_id_list_(NULL), idx_(0), + repeats_(0), changed_(false) {} virtual BasicBlock* ForwardSingleNext() ALWAYS_INLINE; @@ -52,11 +54,13 @@ namespace art { virtual BasicBlock* ForwardRepeatNext(bool had_change) ALWAYS_INLINE; virtual BasicBlock* ReverseRepeatNext(bool had_change) ALWAYS_INLINE; + MIRGraph* const mir_graph_; const int32_t start_idx_; const int32_t end_idx_; GrowableArray* block_id_list_; int32_t idx_; + int32_t repeats_; bool changed_; }; // DataflowIterator diff --git a/compiler/dex/frontend.cc b/compiler/dex/frontend.cc index e53d63648b3..197bba5a58d 100644 --- a/compiler/dex/frontend.cc +++ b/compiler/dex/frontend.cc @@ -253,15 +253,15 @@ static CompiledMethod* CompileMethod(CompilerDriver& compiler, cu.mir_graph->InlineMethod(code_item, access_flags, invoke_type, class_def_idx, method_idx, class_loader, dex_file); + cu.NewTimingSplit("MIROpt:CheckFilters"); #if !defined(ART_USE_PORTABLE_COMPILER) if (cu.mir_graph->SkipCompilation(Runtime::Current()->GetCompilerFilter())) { return NULL; } #endif - cu.NewTimingSplit("MIROpt:CodeLayout"); - /* Do a code layout pass */ + cu.NewTimingSplit("MIROpt:CodeLayout"); cu.mir_graph->CodeLayout(); /* Perform SSA transformation for the whole method */ @@ -272,18 +272,23 @@ static CompiledMethod* CompileMethod(CompilerDriver& compiler, cu.NewTimingSplit("MIROpt:ConstantProp"); cu.mir_graph->PropagateConstants(); + cu.NewTimingSplit("MIROpt:InitRegLoc"); + cu.mir_graph->InitRegLocations(); + /* Count uses */ + cu.NewTimingSplit("MIROpt:UseCount"); cu.mir_graph->MethodUseCount(); - /* Perform null check elimination */ - cu.NewTimingSplit("MIROpt:NullCheckElimination"); - cu.mir_graph->NullCheckElimination(); + /* Perform null check elimination and type inference*/ + cu.NewTimingSplit("MIROpt:NCE_TypeInference"); + cu.mir_graph->NullCheckEliminationAndTypeInference(); /* Combine basic blocks where possible */ - cu.NewTimingSplit("MIROpt:BBOpt"); + cu.NewTimingSplit("MIROpt:BBCombine"); cu.mir_graph->BasicBlockCombine(); /* Do some basic block optimizations */ + cu.NewTimingSplit("MIROpt:BBOpt"); cu.mir_graph->BasicBlockOptimization(); if (cu.enable_debug & (1 << kDebugDumpCheckStats)) { @@ -294,8 +299,8 @@ static CompiledMethod* CompileMethod(CompilerDriver& compiler, cu.mir_graph->ShowOpcodeStats(); } - /* Set up regLocation[] array to describe values - one for each ssa_name. */ - cu.mir_graph->BuildRegLocations(); + /* Reassociate sreg names with original Dalvik vreg names. */ + cu.mir_graph->RemapRegLocations(); CompiledMethod* result = NULL; @@ -323,8 +328,9 @@ static CompiledMethod* CompileMethod(CompilerDriver& compiler, cu.cg->Materialize(); - cu.NewTimingSplit("Cleanup"); + cu.NewTimingSplit("Dedupe"); /* deduping takes up the vast majority of time in GetCompiledMethod(). */ result = cu.cg->GetCompiledMethod(); + cu.NewTimingSplit("Cleanup"); if (result) { VLOG(compiler) << "Compiled " << PrettyMethod(method_idx, dex_file); diff --git a/compiler/dex/mir_dataflow.cc b/compiler/dex/mir_dataflow.cc index d359ee2dfe8..728d48ad706 100644 --- a/compiler/dex/mir_dataflow.cc +++ b/compiler/dex/mir_dataflow.cc @@ -29,7 +29,7 @@ namespace art { * TODO - many optimization flags are incomplete - they will only limit the * scope of optimizations but will not cause mis-optimizations. */ -const int MIRGraph::oat_data_flow_attributes_[kMirOpLast] = { +const uint64_t MIRGraph::oat_data_flow_attributes_[kMirOpLast] = { // 00 NOP DF_NOP, @@ -235,88 +235,88 @@ const int MIRGraph::oat_data_flow_attributes_[kMirOpLast] = { DF_NOP, // 44 AGET vAA, vBB, vCC - DF_DA | DF_UB | DF_UC | DF_NULL_CHK_0 | DF_RANGE_CHK_1 | DF_REF_B | DF_CORE_C, + DF_DA | DF_UB | DF_UC | DF_NULL_CHK_0 | DF_RANGE_CHK_1 | DF_REF_B | DF_CORE_C | DF_LVN, // 45 AGET_WIDE vAA, vBB, vCC - DF_DA | DF_A_WIDE | DF_UB | DF_UC | DF_NULL_CHK_0 | DF_RANGE_CHK_1 | DF_REF_B | DF_CORE_C, + DF_DA | DF_A_WIDE | DF_UB | DF_UC | DF_NULL_CHK_0 | DF_RANGE_CHK_1 | DF_REF_B | DF_CORE_C | DF_LVN, // 46 AGET_OBJECT vAA, vBB, vCC - DF_DA | DF_UB | DF_UC | DF_NULL_CHK_0 | DF_RANGE_CHK_1 | DF_REF_A | DF_REF_B | DF_CORE_C, + DF_DA | DF_UB | DF_UC | DF_NULL_CHK_0 | DF_RANGE_CHK_1 | DF_REF_A | DF_REF_B | DF_CORE_C | DF_LVN, // 47 AGET_BOOLEAN vAA, vBB, vCC - DF_DA | DF_UB | DF_UC | DF_NULL_CHK_0 | DF_RANGE_CHK_1 | DF_REF_B | DF_CORE_C, + DF_DA | DF_UB | DF_UC | DF_NULL_CHK_0 | DF_RANGE_CHK_1 | DF_REF_B | DF_CORE_C | DF_LVN, // 48 AGET_BYTE vAA, vBB, vCC - DF_DA | DF_UB | DF_UC | DF_NULL_CHK_0 | DF_RANGE_CHK_1 | DF_REF_B | DF_CORE_C, + DF_DA | DF_UB | DF_UC | DF_NULL_CHK_0 | DF_RANGE_CHK_1 | DF_REF_B | DF_CORE_C | DF_LVN, // 49 AGET_CHAR vAA, vBB, vCC - DF_DA | DF_UB | DF_UC | DF_NULL_CHK_0 | DF_RANGE_CHK_1 | DF_REF_B | DF_CORE_C, + DF_DA | DF_UB | DF_UC | DF_NULL_CHK_0 | DF_RANGE_CHK_1 | DF_REF_B | DF_CORE_C | DF_LVN, // 4A AGET_SHORT vAA, vBB, vCC - DF_DA | DF_UB | DF_UC | DF_NULL_CHK_0 | DF_RANGE_CHK_1 | DF_REF_B | DF_CORE_C, + DF_DA | DF_UB | DF_UC | DF_NULL_CHK_0 | DF_RANGE_CHK_1 | DF_REF_B | DF_CORE_C | DF_LVN, // 4B APUT vAA, vBB, vCC - DF_UA | DF_UB | DF_UC | DF_NULL_CHK_1 | DF_RANGE_CHK_2 | DF_REF_B | DF_CORE_C, + DF_UA | DF_UB | DF_UC | DF_NULL_CHK_1 | DF_RANGE_CHK_2 | DF_REF_B | DF_CORE_C | DF_LVN, // 4C APUT_WIDE vAA, vBB, vCC - DF_UA | DF_A_WIDE | DF_UB | DF_UC | DF_NULL_CHK_2 | DF_RANGE_CHK_3 | DF_REF_B | DF_CORE_C, + DF_UA | DF_A_WIDE | DF_UB | DF_UC | DF_NULL_CHK_2 | DF_RANGE_CHK_3 | DF_REF_B | DF_CORE_C | DF_LVN, // 4D APUT_OBJECT vAA, vBB, vCC - DF_UA | DF_UB | DF_UC | DF_NULL_CHK_1 | DF_RANGE_CHK_2 | DF_REF_A | DF_REF_B | DF_CORE_C, + DF_UA | DF_UB | DF_UC | DF_NULL_CHK_1 | DF_RANGE_CHK_2 | DF_REF_A | DF_REF_B | DF_CORE_C | DF_LVN, // 4E APUT_BOOLEAN vAA, vBB, vCC - DF_UA | DF_UB | DF_UC | DF_NULL_CHK_1 | DF_RANGE_CHK_2 | DF_REF_B | DF_CORE_C, + DF_UA | DF_UB | DF_UC | DF_NULL_CHK_1 | DF_RANGE_CHK_2 | DF_REF_B | DF_CORE_C | DF_LVN, // 4F APUT_BYTE vAA, vBB, vCC - DF_UA | DF_UB | DF_UC | DF_NULL_CHK_1 | DF_RANGE_CHK_2 | DF_REF_B | DF_CORE_C, + DF_UA | DF_UB | DF_UC | DF_NULL_CHK_1 | DF_RANGE_CHK_2 | DF_REF_B | DF_CORE_C | DF_LVN, // 50 APUT_CHAR vAA, vBB, vCC - DF_UA | DF_UB | DF_UC | DF_NULL_CHK_1 | DF_RANGE_CHK_2 | DF_REF_B | DF_CORE_C, + DF_UA | DF_UB | DF_UC | DF_NULL_CHK_1 | DF_RANGE_CHK_2 | DF_REF_B | DF_CORE_C | DF_LVN, // 51 APUT_SHORT vAA, vBB, vCC - DF_UA | DF_UB | DF_UC | DF_NULL_CHK_1 | DF_RANGE_CHK_2 | DF_REF_B | DF_CORE_C, + DF_UA | DF_UB | DF_UC | DF_NULL_CHK_1 | DF_RANGE_CHK_2 | DF_REF_B | DF_CORE_C | DF_LVN, // 52 IGET vA, vB, field@CCCC - DF_DA | DF_UB | DF_NULL_CHK_0 | DF_REF_B, + DF_DA | DF_UB | DF_NULL_CHK_0 | DF_REF_B | DF_LVN, // 53 IGET_WIDE vA, vB, field@CCCC - DF_DA | DF_A_WIDE | DF_UB | DF_NULL_CHK_0 | DF_REF_B, + DF_DA | DF_A_WIDE | DF_UB | DF_NULL_CHK_0 | DF_REF_B | DF_LVN, // 54 IGET_OBJECT vA, vB, field@CCCC - DF_DA | DF_UB | DF_NULL_CHK_0 | DF_REF_A | DF_REF_B, + DF_DA | DF_UB | DF_NULL_CHK_0 | DF_REF_A | DF_REF_B | DF_LVN, // 55 IGET_BOOLEAN vA, vB, field@CCCC - DF_DA | DF_UB | DF_NULL_CHK_0 | DF_REF_B, + DF_DA | DF_UB | DF_NULL_CHK_0 | DF_REF_B | DF_LVN, // 56 IGET_BYTE vA, vB, field@CCCC - DF_DA | DF_UB | DF_NULL_CHK_0 | DF_REF_B, + DF_DA | DF_UB | DF_NULL_CHK_0 | DF_REF_B | DF_LVN, // 57 IGET_CHAR vA, vB, field@CCCC - DF_DA | DF_UB | DF_NULL_CHK_0 | DF_REF_B, + DF_DA | DF_UB | DF_NULL_CHK_0 | DF_REF_B | DF_LVN, // 58 IGET_SHORT vA, vB, field@CCCC - DF_DA | DF_UB | DF_NULL_CHK_0 | DF_REF_B, + DF_DA | DF_UB | DF_NULL_CHK_0 | DF_REF_B | DF_LVN, // 59 IPUT vA, vB, field@CCCC - DF_UA | DF_UB | DF_NULL_CHK_1 | DF_REF_B, + DF_UA | DF_UB | DF_NULL_CHK_1 | DF_REF_B | DF_LVN, // 5A IPUT_WIDE vA, vB, field@CCCC - DF_UA | DF_A_WIDE | DF_UB | DF_NULL_CHK_2 | DF_REF_B, + DF_UA | DF_A_WIDE | DF_UB | DF_NULL_CHK_2 | DF_REF_B | DF_LVN, // 5B IPUT_OBJECT vA, vB, field@CCCC - DF_UA | DF_UB | DF_NULL_CHK_1 | DF_REF_A | DF_REF_B, + DF_UA | DF_UB | DF_NULL_CHK_1 | DF_REF_A | DF_REF_B | DF_LVN, // 5C IPUT_BOOLEAN vA, vB, field@CCCC - DF_UA | DF_UB | DF_NULL_CHK_1 | DF_REF_B, + DF_UA | DF_UB | DF_NULL_CHK_1 | DF_REF_B | DF_LVN, // 5D IPUT_BYTE vA, vB, field@CCCC - DF_UA | DF_UB | DF_NULL_CHK_1 | DF_REF_B, + DF_UA | DF_UB | DF_NULL_CHK_1 | DF_REF_B | DF_LVN, // 5E IPUT_CHAR vA, vB, field@CCCC - DF_UA | DF_UB | DF_NULL_CHK_1 | DF_REF_B, + DF_UA | DF_UB | DF_NULL_CHK_1 | DF_REF_B | DF_LVN, // 5F IPUT_SHORT vA, vB, field@CCCC - DF_UA | DF_UB | DF_NULL_CHK_1 | DF_REF_B, + DF_UA | DF_UB | DF_NULL_CHK_1 | DF_REF_B | DF_LVN, // 60 SGET vAA, field@BBBB DF_DA | DF_UMS, @@ -712,10 +712,10 @@ const int MIRGraph::oat_data_flow_attributes_[kMirOpLast] = { DF_DA | DF_UB | DF_CORE_A | DF_CORE_B, // E3 IGET_VOLATILE - DF_DA | DF_UB | DF_NULL_CHK_0 | DF_REF_B, + DF_DA | DF_UB | DF_NULL_CHK_0 | DF_REF_B | DF_LVN, // E4 IPUT_VOLATILE - DF_UA | DF_UB | DF_NULL_CHK_1 | DF_REF_B, + DF_UA | DF_UB | DF_NULL_CHK_1 | DF_REF_B | DF_LVN, // E5 SGET_VOLATILE DF_DA | DF_UMS, @@ -724,13 +724,13 @@ const int MIRGraph::oat_data_flow_attributes_[kMirOpLast] = { DF_UA | DF_UMS, // E7 IGET_OBJECT_VOLATILE - DF_DA | DF_UB | DF_NULL_CHK_0 | DF_REF_A | DF_REF_B, + DF_DA | DF_UB | DF_NULL_CHK_0 | DF_REF_A | DF_REF_B | DF_LVN, // E8 IGET_WIDE_VOLATILE - DF_DA | DF_A_WIDE | DF_UB | DF_NULL_CHK_0 | DF_REF_B, + DF_DA | DF_A_WIDE | DF_UB | DF_NULL_CHK_0 | DF_REF_B | DF_LVN, // E9 IPUT_WIDE_VOLATILE - DF_UA | DF_A_WIDE | DF_UB | DF_NULL_CHK_2 | DF_REF_B, + DF_UA | DF_A_WIDE | DF_UB | DF_NULL_CHK_2 | DF_REF_B | DF_LVN, // EA SGET_WIDE_VOLATILE DF_DA | DF_A_WIDE | DF_UMS, @@ -757,22 +757,22 @@ const int MIRGraph::oat_data_flow_attributes_[kMirOpLast] = { DF_NOP, // F2 IGET_QUICK - DF_DA | DF_UB | DF_NULL_CHK_0, + DF_DA | DF_UB | DF_NULL_CHK_0 | DF_LVN, // F3 IGET_WIDE_QUICK - DF_DA | DF_A_WIDE | DF_UB | DF_NULL_CHK_0, + DF_DA | DF_A_WIDE | DF_UB | DF_NULL_CHK_0 | DF_LVN, // F4 IGET_OBJECT_QUICK - DF_DA | DF_UB | DF_NULL_CHK_0, + DF_DA | DF_UB | DF_NULL_CHK_0 | DF_LVN, // F5 IPUT_QUICK - DF_UA | DF_UB | DF_NULL_CHK_1, + DF_UA | DF_UB | DF_NULL_CHK_1 | DF_LVN, // F6 IPUT_WIDE_QUICK - DF_UA | DF_A_WIDE | DF_UB | DF_NULL_CHK_2, + DF_UA | DF_A_WIDE | DF_UB | DF_NULL_CHK_2 | DF_LVN, // F7 IPUT_OBJECT_QUICK - DF_UA | DF_UB | DF_NULL_CHK_1, + DF_UA | DF_UB | DF_NULL_CHK_1 | DF_LVN, // F8 INVOKE_VIRTUAL_QUICK DF_FORMAT_35C | DF_NULL_CHK_OUT0 | DF_UMS, @@ -787,7 +787,7 @@ const int MIRGraph::oat_data_flow_attributes_[kMirOpLast] = { DF_FORMAT_3RC | DF_NULL_CHK_OUT0 | DF_UMS, // FC IPUT_OBJECT_VOLATILE - DF_UA | DF_UB | DF_NULL_CHK_1 | DF_REF_A | DF_REF_B, + DF_UA | DF_UB | DF_NULL_CHK_1 | DF_REF_A | DF_REF_B | DF_LVN, // FD SGET_OBJECT_VOLATILE DF_DA | DF_REF_A | DF_UMS, @@ -879,7 +879,7 @@ bool MIRGraph::FindLocalLiveIn(BasicBlock* bb) { new (arena_) ArenaBitVector(arena_, cu_->num_dalvik_registers, false, kBitMapLiveIn); for (mir = bb->first_mir_insn; mir != NULL; mir = mir->next) { - int df_attributes = oat_data_flow_attributes_[mir->dalvikInsn.opcode]; + uint64_t df_attributes = oat_data_flow_attributes_[mir->dalvikInsn.opcode]; DecodedInstruction *d_insn = &mir->dalvikInsn; if (df_attributes & DF_HAS_USES) { @@ -994,7 +994,7 @@ bool MIRGraph::DoSSAConversion(BasicBlock* bb) { static_cast(arena_->Alloc(sizeof(SSARepresentation), ArenaAllocator::kAllocDFInfo)); - int df_attributes = oat_data_flow_attributes_[mir->dalvikInsn.opcode]; + uint64_t df_attributes = oat_data_flow_attributes_[mir->dalvikInsn.opcode]; // If not a pseudo-op, note non-leaf or can throw if (static_cast(mir->dalvikInsn.opcode) < @@ -1239,37 +1239,33 @@ bool MIRGraph::CountUses(struct BasicBlock* bb) { if (bb->block_type != kDalvikByteCode) { return false; } + // Each level of nesting adds *100 to count, up to 3 levels deep. + uint32_t depth = std::min(3U, static_cast(bb->nesting_depth)); + uint32_t weight = std::max(1U, depth * 100); for (MIR* mir = bb->first_mir_insn; (mir != NULL); mir = mir->next) { if (mir->ssa_rep == NULL) { continue; } - // Each level of nesting adds *100 to count, up to 3 levels deep. - uint32_t depth = std::min(3U, static_cast(bb->nesting_depth)); - uint32_t weight = std::max(1U, depth * 100); for (int i = 0; i < mir->ssa_rep->num_uses; i++) { int s_reg = mir->ssa_rep->uses[i]; raw_use_counts_.Increment(s_reg); use_counts_.Put(s_reg, use_counts_.Get(s_reg) + weight); } if (!(cu_->disable_opt & (1 << kPromoteCompilerTemps))) { - int df_attributes = oat_data_flow_attributes_[mir->dalvikInsn.opcode]; + uint64_t df_attributes = oat_data_flow_attributes_[mir->dalvikInsn.opcode]; // Implicit use of Method* ? */ if (df_attributes & DF_UMS) { /* * Some invokes will not use Method* - need to perform test similar * to that found in GenInvoke() to decide whether to count refs - * for Method* on invoke-class opcodes. - * TODO: refactor for common test here, save results for GenInvoke + * for Method* on invoke-class opcodes. This is a relatively expensive + * operation, so should only be done once. + * TODO: refactor InvokeUsesMethodStar() to perform check at parse time, + * and save results for both here and GenInvoke. For now, go ahead + * and assume all invokes use method*. */ - int uses_method_star = true; - if ((df_attributes & (DF_FORMAT_35C | DF_FORMAT_3RC)) && - !(df_attributes & DF_NON_NULL_RET)) { - uses_method_star &= InvokeUsesMethodStar(mir); - } - if (uses_method_star) { - raw_use_counts_.Increment(method_sreg_); - use_counts_.Put(method_sreg_, use_counts_.Get(method_sreg_) + weight); - } + raw_use_counts_.Increment(method_sreg_); + use_counts_.Put(method_sreg_, use_counts_.Get(method_sreg_) + weight); } } } diff --git a/compiler/dex/mir_graph.cc b/compiler/dex/mir_graph.cc index deaf2ffe804..2a182801335 100644 --- a/compiler/dex/mir_graph.cc +++ b/compiler/dex/mir_graph.cc @@ -650,12 +650,16 @@ void MIRGraph::InlineMethod(const DexFile::CodeItem* code_item, uint32_t access_ int flags = Instruction::FlagsOf(insn->dalvikInsn.opcode); - int df_flags = oat_data_flow_attributes_[insn->dalvikInsn.opcode]; + uint64_t df_flags = oat_data_flow_attributes_[insn->dalvikInsn.opcode]; if (df_flags & DF_HAS_DEFS) { def_count_ += (df_flags & DF_A_WIDE) ? 2 : 1; } + if (df_flags & DF_LVN) { + cur_block->use_lvn = true; // Run local value numbering on this basic block. + } + // Check for inline data block signatures if (opcode == Instruction::NOP) { // A simple NOP will have a width of 1 at this point, embedded data NOP > 1. diff --git a/compiler/dex/mir_graph.h b/compiler/dex/mir_graph.h index 8c20728a518..bffec394d67 100644 --- a/compiler/dex/mir_graph.h +++ b/compiler/dex/mir_graph.h @@ -92,41 +92,43 @@ enum DataFlowAttributePos { kRefB, kRefC, kUsesMethodStar, // Implicit use of Method*. + kDoLVN, // Worth computing local value numbers. }; -#define DF_NOP 0 -#define DF_UA (1 << kUA) -#define DF_UB (1 << kUB) -#define DF_UC (1 << kUC) -#define DF_A_WIDE (1 << kAWide) -#define DF_B_WIDE (1 << kBWide) -#define DF_C_WIDE (1 << kCWide) -#define DF_DA (1 << kDA) -#define DF_IS_MOVE (1 << kIsMove) -#define DF_SETS_CONST (1 << kSetsConst) -#define DF_FORMAT_35C (1 << kFormat35c) -#define DF_FORMAT_3RC (1 << kFormat3rc) -#define DF_NULL_CHK_0 (1 << kNullCheckSrc0) -#define DF_NULL_CHK_1 (1 << kNullCheckSrc1) -#define DF_NULL_CHK_2 (1 << kNullCheckSrc2) -#define DF_NULL_CHK_OUT0 (1 << kNullCheckOut0) -#define DF_NON_NULL_DST (1 << kDstNonNull) -#define DF_NON_NULL_RET (1 << kRetNonNull) -#define DF_NULL_TRANSFER_0 (1 << kNullTransferSrc0) -#define DF_NULL_TRANSFER_N (1 << kNullTransferSrcN) -#define DF_RANGE_CHK_1 (1 << kRangeCheckSrc1) -#define DF_RANGE_CHK_2 (1 << kRangeCheckSrc2) -#define DF_RANGE_CHK_3 (1 << kRangeCheckSrc3) -#define DF_FP_A (1 << kFPA) -#define DF_FP_B (1 << kFPB) -#define DF_FP_C (1 << kFPC) -#define DF_CORE_A (1 << kCoreA) -#define DF_CORE_B (1 << kCoreB) -#define DF_CORE_C (1 << kCoreC) -#define DF_REF_A (1 << kRefA) -#define DF_REF_B (1 << kRefB) -#define DF_REF_C (1 << kRefC) -#define DF_UMS (1 << kUsesMethodStar) +#define DF_NOP 0ULL +#define DF_UA (1ULL << kUA) +#define DF_UB (1ULL << kUB) +#define DF_UC (1ULL << kUC) +#define DF_A_WIDE (1ULL << kAWide) +#define DF_B_WIDE (1ULL << kBWide) +#define DF_C_WIDE (1ULL << kCWide) +#define DF_DA (1ULL << kDA) +#define DF_IS_MOVE (1ULL << kIsMove) +#define DF_SETS_CONST (1ULL << kSetsConst) +#define DF_FORMAT_35C (1ULL << kFormat35c) +#define DF_FORMAT_3RC (1ULL << kFormat3rc) +#define DF_NULL_CHK_0 (1ULL << kNullCheckSrc0) +#define DF_NULL_CHK_1 (1ULL << kNullCheckSrc1) +#define DF_NULL_CHK_2 (1ULL << kNullCheckSrc2) +#define DF_NULL_CHK_OUT0 (1ULL << kNullCheckOut0) +#define DF_NON_NULL_DST (1ULL << kDstNonNull) +#define DF_NON_NULL_RET (1ULL << kRetNonNull) +#define DF_NULL_TRANSFER_0 (1ULL << kNullTransferSrc0) +#define DF_NULL_TRANSFER_N (1ULL << kNullTransferSrcN) +#define DF_RANGE_CHK_1 (1ULL << kRangeCheckSrc1) +#define DF_RANGE_CHK_2 (1ULL << kRangeCheckSrc2) +#define DF_RANGE_CHK_3 (1ULL << kRangeCheckSrc3) +#define DF_FP_A (1ULL << kFPA) +#define DF_FP_B (1ULL << kFPB) +#define DF_FP_C (1ULL << kFPC) +#define DF_CORE_A (1ULL << kCoreA) +#define DF_CORE_B (1ULL << kCoreB) +#define DF_CORE_C (1ULL << kCoreC) +#define DF_REF_A (1ULL << kRefA) +#define DF_REF_B (1ULL << kRefB) +#define DF_REF_C (1ULL << kRefC) +#define DF_UMS (1ULL << kUsesMethodStar) +#define DF_LVN (1ULL << kDoLVN) #define DF_HAS_USES (DF_UA | DF_UB | DF_UC) @@ -273,8 +275,9 @@ struct BasicBlock { bool catch_entry:1; bool explicit_throw:1; bool conditional_branch:1; - bool terminated_by_return:1; // Block ends with a Dalvik return opcode. - bool dominates_return:1; // Is a member of return extended basic block. + bool terminated_by_return:1; // Block ends with a Dalvik return opcode. + bool dominates_return:1; // Is a member of return extended basic block. + bool use_lvn:1; // Run local value numbering on this block. MIR* first_mir_insn; MIR* last_mir_insn; BasicBlockDataFlow* data_flow_info; @@ -451,7 +454,9 @@ class MIRGraph { void DumpCFG(const char* dir_prefix, bool all_blocks); - void BuildRegLocations(); + void InitRegLocations(); + + void RemapRegLocations(); void DumpRegLocTable(RegLocation* table, int count); @@ -619,7 +624,7 @@ class MIRGraph { void MethodUseCount(); void SSATransformation(); void CheckForDominanceFrontier(BasicBlock* dom_bb, const BasicBlock* succ_bb); - void NullCheckElimination(); + void NullCheckEliminationAndTypeInference(); /* * Type inference handling helpers. Because Dalvik's bytecode is not fully typed, * we have to do some work to figure out the sreg type. For some operations it is @@ -675,7 +680,7 @@ class MIRGraph { GrowableArray compiler_temps_; SafeMap block_id_map_; // Block collapse lookup cache. - static const int oat_data_flow_attributes_[kMirOpLast]; + static const uint64_t oat_data_flow_attributes_[kMirOpLast]; static const char* extended_mir_op_names_[kMirOpLast - kMirOpFirst]; static const uint32_t analysis_attributes_[kMirOpLast]; @@ -711,7 +716,7 @@ class MIRGraph { bool FindLocalLiveIn(BasicBlock* bb); void ClearAllVisitedFlags(); bool CountUses(struct BasicBlock* bb); - bool InferTypeAndSize(BasicBlock* bb); + bool InferTypeAndSize(BasicBlock* bb, MIR* mir, bool changed); bool VerifyPredInfo(BasicBlock* bb); BasicBlock* NeedsVisit(BasicBlock* bb); BasicBlock* NextUnvisitedSuccessor(BasicBlock* bb); @@ -727,7 +732,7 @@ class MIRGraph { void SetConstantWide(int ssa_reg, int64_t value); int GetSSAUseCount(int s_reg); bool BasicBlockOpt(BasicBlock* bb); - bool EliminateNullChecks(BasicBlock* bb); + bool EliminateNullChecksAndInferTypes(BasicBlock* bb); void NullCheckEliminationInit(BasicBlock* bb); bool BuildExtendedBBList(struct BasicBlock* bb); bool FillDefBlockMatrix(BasicBlock* bb); diff --git a/compiler/dex/mir_optimization.cc b/compiler/dex/mir_optimization.cc index f5913a5ad4c..635393796a4 100644 --- a/compiler/dex/mir_optimization.cc +++ b/compiler/dex/mir_optimization.cc @@ -40,7 +40,7 @@ void MIRGraph::DoConstantPropogation(BasicBlock* bb) { MIR* mir; for (mir = bb->first_mir_insn; mir != NULL; mir = mir->next) { - int df_attributes = oat_data_flow_attributes_[mir->dalvikInsn.opcode]; + uint64_t df_attributes = oat_data_flow_attributes_[mir->dalvikInsn.opcode]; DecodedInstruction *d_insn = &mir->dalvikInsn; @@ -155,7 +155,7 @@ BasicBlock* MIRGraph::NextDominatedBlock(BasicBlock* bb) { || (bb->block_type == kExitBlock)); BasicBlock* bb_taken = GetBasicBlock(bb->taken); BasicBlock* bb_fall_through = GetBasicBlock(bb->fall_through); - if (((bb_taken != NULL) && (bb_fall_through == NULL)) && + if (((bb_fall_through == NULL) && (bb_taken != NULL)) && ((bb_taken->block_type == kDalvikByteCode) || (bb_taken->block_type == kExitBlock))) { // Follow simple unconditional branches. bb = bb_taken; @@ -216,11 +216,17 @@ bool MIRGraph::BasicBlockOpt(BasicBlock* bb) { return true; } int num_temps = 0; - LocalValueNumbering local_valnum(cu_); + bool use_lvn = bb->use_lvn; + UniquePtr local_valnum; + if (use_lvn) { + local_valnum.reset(new LocalValueNumbering(cu_)); + } while (bb != NULL) { for (MIR* mir = bb->first_mir_insn; mir != NULL; mir = mir->next) { // TUNING: use the returned value number for CSE. - local_valnum.GetValueNumber(mir); + if (use_lvn) { + local_valnum->GetValueNumber(mir); + } // Look for interesting opcodes, skip otherwise Instruction::Code opcode = mir->dalvikInsn.opcode; switch (opcode) { @@ -463,7 +469,7 @@ bool MIRGraph::BasicBlockOpt(BasicBlock* bb) { } } } - bb = NextDominatedBlock(bb); + bb = ((cu_->disable_opt & (1 << kSuppressExceptionEdges)) != 0) ? NextDominatedBlock(bb) : NULL; } if (num_temps > cu_->num_compiler_temps) { @@ -486,7 +492,7 @@ void MIRGraph::CountChecks(struct BasicBlock* bb) { if (mir->ssa_rep == NULL) { continue; } - int df_attributes = oat_data_flow_attributes_[mir->dalvikInsn.opcode]; + uint64_t df_attributes = oat_data_flow_attributes_[mir->dalvikInsn.opcode]; if (df_attributes & DF_HAS_NULL_CHKS) { checkstats_->null_checks++; if (mir->optimization_flags & MIR_IGNORE_NULL_CHECK) { @@ -571,7 +577,7 @@ bool MIRGraph::CombineBlocks(struct BasicBlock* bb) { MIR* mir = bb->last_mir_insn; // Grab the attributes from the paired opcode MIR* throw_insn = mir->meta.throw_insn; - int df_attributes = oat_data_flow_attributes_[throw_insn->dalvikInsn.opcode]; + uint64_t df_attributes = oat_data_flow_attributes_[throw_insn->dalvikInsn.opcode]; bool can_combine = true; if (df_attributes & DF_HAS_NULL_CHKS) { can_combine &= ((throw_insn->optimization_flags & MIR_IGNORE_NULL_CHECK) != 0); @@ -618,74 +624,87 @@ bool MIRGraph::CombineBlocks(struct BasicBlock* bb) { return false; } -/* Eliminate unnecessary null checks for a basic block. */ -bool MIRGraph::EliminateNullChecks(struct BasicBlock* bb) { +/* + * Eliminate unnecessary null checks for a basic block. Also, while we're doing + * an iterative walk go ahead and perform type and size inference. + */ +bool MIRGraph::EliminateNullChecksAndInferTypes(struct BasicBlock* bb) { if (bb->data_flow_info == NULL) return false; + bool infer_changed = false; + bool do_nce = ((cu_->disable_opt & (1 << kNullCheckElimination)) == 0); - /* - * Set initial state. Be conservative with catch - * blocks and start with no assumptions about null check - * status (except for "this"). - */ - if ((bb->block_type == kEntryBlock) | bb->catch_entry) { - temp_ssa_register_v_->ClearAllBits(); - // Assume all ins are objects. - for (uint16_t in_reg = cu_->num_dalvik_registers - cu_->num_ins; - in_reg < cu_->num_dalvik_registers; in_reg++) { - temp_ssa_register_v_->SetBit(in_reg); - } - if ((cu_->access_flags & kAccStatic) == 0) { - // If non-static method, mark "this" as non-null - int this_reg = cu_->num_dalvik_registers - cu_->num_ins; - temp_ssa_register_v_->ClearBit(this_reg); - } - } else if (bb->predecessors->Size() == 1) { - BasicBlock* pred_bb = GetBasicBlock(bb->predecessors->Get(0)); - temp_ssa_register_v_->Copy(pred_bb->data_flow_info->ending_null_check_v); - if (pred_bb->block_type == kDalvikByteCode) { - // Check to see if predecessor had an explicit null-check. - MIR* last_insn = pred_bb->last_mir_insn; - Instruction::Code last_opcode = last_insn->dalvikInsn.opcode; - if (last_opcode == Instruction::IF_EQZ) { - if (pred_bb->fall_through == bb->id) { - // The fall-through of a block following a IF_EQZ, set the vA of the IF_EQZ to show that - // it can't be null. - temp_ssa_register_v_->ClearBit(last_insn->ssa_rep->uses[0]); - } - } else if (last_opcode == Instruction::IF_NEZ) { - if (pred_bb->taken == bb->id) { - // The taken block following a IF_NEZ, set the vA of the IF_NEZ to show that it can't be - // null. - temp_ssa_register_v_->ClearBit(last_insn->ssa_rep->uses[0]); + if (do_nce) { + /* + * Set initial state. Be conservative with catch + * blocks and start with no assumptions about null check + * status (except for "this"). + */ + if ((bb->block_type == kEntryBlock) | bb->catch_entry) { + temp_ssa_register_v_->ClearAllBits(); + // Assume all ins are objects. + for (uint16_t in_reg = cu_->num_dalvik_registers - cu_->num_ins; + in_reg < cu_->num_dalvik_registers; in_reg++) { + temp_ssa_register_v_->SetBit(in_reg); + } + if ((cu_->access_flags & kAccStatic) == 0) { + // If non-static method, mark "this" as non-null + int this_reg = cu_->num_dalvik_registers - cu_->num_ins; + temp_ssa_register_v_->ClearBit(this_reg); + } + } else if (bb->predecessors->Size() == 1) { + BasicBlock* pred_bb = GetBasicBlock(bb->predecessors->Get(0)); + temp_ssa_register_v_->Copy(pred_bb->data_flow_info->ending_null_check_v); + if (pred_bb->block_type == kDalvikByteCode) { + // Check to see if predecessor had an explicit null-check. + MIR* last_insn = pred_bb->last_mir_insn; + Instruction::Code last_opcode = last_insn->dalvikInsn.opcode; + if (last_opcode == Instruction::IF_EQZ) { + if (pred_bb->fall_through == bb->id) { + // The fall-through of a block following a IF_EQZ, set the vA of the IF_EQZ to show that + // it can't be null. + temp_ssa_register_v_->ClearBit(last_insn->ssa_rep->uses[0]); + } + } else if (last_opcode == Instruction::IF_NEZ) { + if (pred_bb->taken == bb->id) { + // The taken block following a IF_NEZ, set the vA of the IF_NEZ to show that it can't be + // null. + temp_ssa_register_v_->ClearBit(last_insn->ssa_rep->uses[0]); + } } } - } - } else { - // Starting state is union of all incoming arcs - GrowableArray::Iterator iter(bb->predecessors); - BasicBlock* pred_bb = GetBasicBlock(iter.Next()); - DCHECK(pred_bb != NULL); - temp_ssa_register_v_->Copy(pred_bb->data_flow_info->ending_null_check_v); - while (true) { - pred_bb = GetBasicBlock(iter.Next()); - if (!pred_bb) break; - if ((pred_bb->data_flow_info == NULL) || - (pred_bb->data_flow_info->ending_null_check_v == NULL)) { - continue; + } else { + // Starting state is union of all incoming arcs + GrowableArray::Iterator iter(bb->predecessors); + BasicBlock* pred_bb = GetBasicBlock(iter.Next()); + DCHECK(pred_bb != NULL); + temp_ssa_register_v_->Copy(pred_bb->data_flow_info->ending_null_check_v); + while (true) { + pred_bb = GetBasicBlock(iter.Next()); + if (!pred_bb) break; + if ((pred_bb->data_flow_info == NULL) || + (pred_bb->data_flow_info->ending_null_check_v == NULL)) { + continue; + } + temp_ssa_register_v_->Union(pred_bb->data_flow_info->ending_null_check_v); } - temp_ssa_register_v_->Union(pred_bb->data_flow_info->ending_null_check_v); } + // At this point, temp_ssa_register_v_ shows which sregs have an object definition with + // no intervening uses. } - // At this point, temp_ssa_register_v_ shows which sregs have an object definition with - // no intervening uses. - // Walk through the instruction in the block, updating as necessary for (MIR* mir = bb->first_mir_insn; mir != NULL; mir = mir->next) { if (mir->ssa_rep == NULL) { continue; } - int df_attributes = oat_data_flow_attributes_[mir->dalvikInsn.opcode]; + + // Propagate type info. + infer_changed = InferTypeAndSize(bb, mir, infer_changed); + if (!do_nce) { + continue; + } + + uint64_t df_attributes = oat_data_flow_attributes_[mir->dalvikInsn.opcode]; // Might need a null check? if (df_attributes & DF_HAS_NULL_CHKS) { @@ -784,25 +803,25 @@ bool MIRGraph::EliminateNullChecks(struct BasicBlock* bb) { } // Did anything change? - bool changed = !temp_ssa_register_v_->Equal(bb->data_flow_info->ending_null_check_v); - if (changed) { + bool nce_changed = do_nce && !temp_ssa_register_v_->Equal(bb->data_flow_info->ending_null_check_v); + if (nce_changed) { bb->data_flow_info->ending_null_check_v->Copy(temp_ssa_register_v_); } - return changed; + return infer_changed | nce_changed; } -void MIRGraph::NullCheckElimination() { - if (!(cu_->disable_opt & (1 << kNullCheckElimination))) { - DCHECK(temp_ssa_register_v_ != NULL); +void MIRGraph::NullCheckEliminationAndTypeInference() { + DCHECK(temp_ssa_register_v_ != NULL); + if ((cu_->disable_opt & (1 << kNullCheckElimination)) == 0) { AllNodesIterator iter(this); for (BasicBlock* bb = iter.Next(); bb != NULL; bb = iter.Next()) { NullCheckEliminationInit(bb); } - RepeatingPreOrderDfsIterator iter2(this); - bool change = false; - for (BasicBlock* bb = iter2.Next(change); bb != NULL; bb = iter2.Next(change)) { - change = EliminateNullChecks(bb); - } + } + RepeatingPreOrderDfsIterator iter2(this); + bool change = false; + for (BasicBlock* bb = iter2.Next(change); bb != NULL; bb = iter2.Next(change)) { + change = EliminateNullChecksAndInferTypes(bb); } if (cu_->enable_debug & (1 << kDebugDumpCFG)) { DumpCFG("/sdcard/4_post_nce_cfg/", false); @@ -810,12 +829,14 @@ void MIRGraph::NullCheckElimination() { } void MIRGraph::BasicBlockCombine() { - PreOrderDfsIterator iter(this); - for (BasicBlock* bb = iter.Next(); bb != NULL; bb = iter.Next()) { - CombineBlocks(bb); - } - if (cu_->enable_debug & (1 << kDebugDumpCFG)) { - DumpCFG("/sdcard/5_post_bbcombine_cfg/", false); + if ((cu_->disable_opt & (1 << kSuppressExceptionEdges)) != 0) { + PreOrderDfsIterator iter(this); + for (BasicBlock* bb = iter.Next(); bb != NULL; bb = iter.Next()) { + CombineBlocks(bb); + } + if (cu_->enable_debug & (1 << kDebugDumpCFG)) { + DumpCFG("/sdcard/5_post_bbcombine_cfg/", false); + } } } @@ -868,17 +889,20 @@ bool MIRGraph::BuildExtendedBBList(struct BasicBlock* bb) { BasicBlock* start_bb = bb; extended_basic_blocks_.push_back(bb->id); bool terminated_by_return = false; + bool do_local_value_numbering = false; // Visit blocks strictly dominated by this head. while (bb != NULL) { bb->visited = true; terminated_by_return |= bb->terminated_by_return; + do_local_value_numbering |= bb->use_lvn; bb = NextDominatedBlock(bb); } - if (terminated_by_return) { - // This extended basic block contains a return, so mark all members. + if (terminated_by_return || do_local_value_numbering) { + // Do lvn for all blocks in this extended set. bb = start_bb; while (bb != NULL) { - bb->dominates_return = true; + bb->use_lvn = do_local_value_numbering; + bb->dominates_return = terminated_by_return; bb = NextDominatedBlock(bb); } } @@ -889,14 +913,21 @@ bool MIRGraph::BuildExtendedBBList(struct BasicBlock* bb) { void MIRGraph::BasicBlockOptimization() { if (!(cu_->disable_opt & (1 << kBBOpt))) { DCHECK_EQ(cu_->num_compiler_temps, 0); - ClearAllVisitedFlags(); - PreOrderDfsIterator iter2(this); - for (BasicBlock* bb = iter2.Next(); bb != NULL; bb = iter2.Next()) { - BuildExtendedBBList(bb); + if ((cu_->disable_opt & (1 << kSuppressExceptionEdges)) != 0) { + ClearAllVisitedFlags(); + PreOrderDfsIterator iter2(this); + for (BasicBlock* bb = iter2.Next(); bb != NULL; bb = iter2.Next()) { + BuildExtendedBBList(bb); + } + // Perform extended basic block optimizations. + for (unsigned int i = 0; i < extended_basic_blocks_.size(); i++) { + BasicBlockOpt(GetBasicBlock(extended_basic_blocks_[i])); + } } - // Perform extended basic block optimizations. - for (unsigned int i = 0; i < extended_basic_blocks_.size(); i++) { - BasicBlockOpt(GetBasicBlock(extended_basic_blocks_[i])); + } else { + PreOrderDfsIterator iter(this); + for (BasicBlock* bb = iter.Next(); bb != NULL; bb = iter.Next()) { + BasicBlockOpt(bb); } } if (cu_->enable_debug & (1 << kDebugDumpCFG)) { diff --git a/compiler/dex/portable/mir_to_gbc.cc b/compiler/dex/portable/mir_to_gbc.cc index 07bd2aa979e..e5b4876f082 100644 --- a/compiler/dex/portable/mir_to_gbc.cc +++ b/compiler/dex/portable/mir_to_gbc.cc @@ -705,7 +705,7 @@ bool MirConverter::ConvertMIRNode(MIR* mir, BasicBlock* bb, /* Prep Src and Dest locations */ int next_sreg = 0; int next_loc = 0; - int attrs = mir_graph_->oat_data_flow_attributes_[opcode]; + uint64_t attrs = mir_graph_->oat_data_flow_attributes_[opcode]; rl_src[0] = rl_src[1] = rl_src[2] = mir_graph_->GetBadLoc(); if (attrs & DF_UA) { if (attrs & DF_A_WIDE) { diff --git a/compiler/dex/quick/arm/assemble_arm.cc b/compiler/dex/quick/arm/assemble_arm.cc index b3236ae23eb..820b3aa24eb 100644 --- a/compiler/dex/quick/arm/assemble_arm.cc +++ b/compiler/dex/quick/arm/assemble_arm.cc @@ -1615,7 +1615,6 @@ void ArmMir2Lir::AssembleLIR() { data_offset_ = (code_buffer_.size() + 0x3) & ~0x3; - cu_->NewTimingSplit("LiteralData"); // Install literals InstallLiteralPools(); diff --git a/compiler/dex/quick/local_optimizations.cc b/compiler/dex/quick/local_optimizations.cc index 0f29578c4ec..7a2dce13dc2 100644 --- a/compiler/dex/quick/local_optimizations.cc +++ b/compiler/dex/quick/local_optimizations.cc @@ -291,9 +291,9 @@ void Mir2Lir::ApplyLoadHoisting(LIR* head_lir, LIR* tail_lir) { uint64_t target_flags = GetTargetInstFlags(this_lir->opcode); /* Skip non-interesting instructions */ - if ((this_lir->flags.is_nop == true) || - ((target_flags & (REG_DEF0 | REG_DEF1)) == (REG_DEF0 | REG_DEF1)) || - !(target_flags & IS_LOAD)) { + if (!(target_flags & IS_LOAD) || + (this_lir->flags.is_nop == true) || + ((target_flags & (REG_DEF0 | REG_DEF1)) == (REG_DEF0 | REG_DEF1))) { continue; } diff --git a/compiler/dex/quick/mips/assemble_mips.cc b/compiler/dex/quick/mips/assemble_mips.cc index 5f5e5e44ac3..bd3355f8da1 100644 --- a/compiler/dex/quick/mips/assemble_mips.cc +++ b/compiler/dex/quick/mips/assemble_mips.cc @@ -793,7 +793,6 @@ void MipsMir2Lir::AssembleLIR() { } // Install literals - cu_->NewTimingSplit("LiteralData"); InstallLiteralPools(); // Install switch tables diff --git a/compiler/dex/quick/mir_to_lir.cc b/compiler/dex/quick/mir_to_lir.cc index fa9a3ad566b..19d04be6dfa 100644 --- a/compiler/dex/quick/mir_to_lir.cc +++ b/compiler/dex/quick/mir_to_lir.cc @@ -39,7 +39,7 @@ void Mir2Lir::CompileDalvikInstruction(MIR* mir, BasicBlock* bb, LIR* label_list // Prep Src and Dest locations. int next_sreg = 0; int next_loc = 0; - int attrs = mir_graph_->oat_data_flow_attributes_[opcode]; + uint64_t attrs = mir_graph_->oat_data_flow_attributes_[opcode]; rl_src[0] = rl_src[1] = rl_src[2] = mir_graph_->GetBadLoc(); if (attrs & DF_UA) { if (attrs & DF_A_WIDE) { diff --git a/compiler/dex/quick/x86/assemble_x86.cc b/compiler/dex/quick/x86/assemble_x86.cc index 2047f307658..191c9c701bc 100644 --- a/compiler/dex/quick/x86/assemble_x86.cc +++ b/compiler/dex/quick/x86/assemble_x86.cc @@ -1503,7 +1503,6 @@ void X86Mir2Lir::AssembleLIR() { } } - cu_->NewTimingSplit("LiteralData"); // Install literals InstallLiteralPools(); diff --git a/compiler/dex/vreg_analysis.cc b/compiler/dex/vreg_analysis.cc index 32fac0b393f..bef966c8ff7 100644 --- a/compiler/dex/vreg_analysis.cc +++ b/compiler/dex/vreg_analysis.cc @@ -121,260 +121,251 @@ bool MIRGraph::SetHigh(int index) { * as it doesn't propagate. We're guaranteed at least one pass through * the cfg. */ -bool MIRGraph::InferTypeAndSize(BasicBlock* bb) { - MIR *mir; - bool changed = false; // Did anything change? - - if (bb->data_flow_info == NULL) return false; - if (bb->block_type != kDalvikByteCode && bb->block_type != kEntryBlock) - return false; - - for (mir = bb->first_mir_insn; mir != NULL; mir = mir->next) { - SSARepresentation *ssa_rep = mir->ssa_rep; - if (ssa_rep) { - int attrs = oat_data_flow_attributes_[mir->dalvikInsn.opcode]; - const int* uses = ssa_rep->uses; - const int* defs = ssa_rep->defs; +bool MIRGraph::InferTypeAndSize(BasicBlock* bb, MIR* mir, bool changed) { + SSARepresentation *ssa_rep = mir->ssa_rep; + if (ssa_rep) { + uint64_t attrs = oat_data_flow_attributes_[mir->dalvikInsn.opcode]; + const int* uses = ssa_rep->uses; + const int* defs = ssa_rep->defs; + + // Handle defs + if (attrs & DF_DA) { + if (attrs & DF_CORE_A) { + changed |= SetCore(defs[0]); + } + if (attrs & DF_REF_A) { + changed |= SetRef(defs[0]); + } + if (attrs & DF_A_WIDE) { + reg_location_[defs[0]].wide = true; + reg_location_[defs[1]].wide = true; + reg_location_[defs[1]].high_word = true; + DCHECK_EQ(SRegToVReg(defs[0])+1, + SRegToVReg(defs[1])); + } + } - // Handle defs - if (attrs & DF_DA) { - if (attrs & DF_CORE_A) { - changed |= SetCore(defs[0]); - } - if (attrs & DF_REF_A) { - changed |= SetRef(defs[0]); - } - if (attrs & DF_A_WIDE) { - reg_location_[defs[0]].wide = true; - reg_location_[defs[1]].wide = true; - reg_location_[defs[1]].high_word = true; - DCHECK_EQ(SRegToVReg(defs[0])+1, - SRegToVReg(defs[1])); - } + // Handles uses + int next = 0; + if (attrs & DF_UA) { + if (attrs & DF_CORE_A) { + changed |= SetCore(uses[next]); + } + if (attrs & DF_REF_A) { + changed |= SetRef(uses[next]); + } + if (attrs & DF_A_WIDE) { + reg_location_[uses[next]].wide = true; + reg_location_[uses[next + 1]].wide = true; + reg_location_[uses[next + 1]].high_word = true; + DCHECK_EQ(SRegToVReg(uses[next])+1, + SRegToVReg(uses[next + 1])); + next += 2; + } else { + next++; + } + } + if (attrs & DF_UB) { + if (attrs & DF_CORE_B) { + changed |= SetCore(uses[next]); + } + if (attrs & DF_REF_B) { + changed |= SetRef(uses[next]); + } + if (attrs & DF_B_WIDE) { + reg_location_[uses[next]].wide = true; + reg_location_[uses[next + 1]].wide = true; + reg_location_[uses[next + 1]].high_word = true; + DCHECK_EQ(SRegToVReg(uses[next])+1, + SRegToVReg(uses[next + 1])); + next += 2; + } else { + next++; + } + } + if (attrs & DF_UC) { + if (attrs & DF_CORE_C) { + changed |= SetCore(uses[next]); + } + if (attrs & DF_REF_C) { + changed |= SetRef(uses[next]); } + if (attrs & DF_C_WIDE) { + reg_location_[uses[next]].wide = true; + reg_location_[uses[next + 1]].wide = true; + reg_location_[uses[next + 1]].high_word = true; + DCHECK_EQ(SRegToVReg(uses[next])+1, + SRegToVReg(uses[next + 1])); + } + } - // Handles uses - int next = 0; - if (attrs & DF_UA) { - if (attrs & DF_CORE_A) { - changed |= SetCore(uses[next]); - } - if (attrs & DF_REF_A) { - changed |= SetRef(uses[next]); - } - if (attrs & DF_A_WIDE) { - reg_location_[uses[next]].wide = true; - reg_location_[uses[next + 1]].wide = true; - reg_location_[uses[next + 1]].high_word = true; - DCHECK_EQ(SRegToVReg(uses[next])+1, - SRegToVReg(uses[next + 1])); - next += 2; - } else { - next++; - } + // Special-case return handling + if ((mir->dalvikInsn.opcode == Instruction::RETURN) || + (mir->dalvikInsn.opcode == Instruction::RETURN_WIDE) || + (mir->dalvikInsn.opcode == Instruction::RETURN_OBJECT)) { + switch (cu_->shorty[0]) { + case 'I': + changed |= SetCore(uses[0]); + break; + case 'J': + changed |= SetCore(uses[0]); + changed |= SetCore(uses[1]); + reg_location_[uses[0]].wide = true; + reg_location_[uses[1]].wide = true; + reg_location_[uses[1]].high_word = true; + break; + case 'F': + changed |= SetFp(uses[0]); + break; + case 'D': + changed |= SetFp(uses[0]); + changed |= SetFp(uses[1]); + reg_location_[uses[0]].wide = true; + reg_location_[uses[1]].wide = true; + reg_location_[uses[1]].high_word = true; + break; + case 'L': + changed |= SetRef(uses[0]); + break; + default: break; } - if (attrs & DF_UB) { - if (attrs & DF_CORE_B) { - changed |= SetCore(uses[next]); - } - if (attrs & DF_REF_B) { - changed |= SetRef(uses[next]); - } - if (attrs & DF_B_WIDE) { - reg_location_[uses[next]].wide = true; - reg_location_[uses[next + 1]].wide = true; - reg_location_[uses[next + 1]].high_word = true; - DCHECK_EQ(SRegToVReg(uses[next])+1, - SRegToVReg(uses[next + 1])); - next += 2; - } else { - next++; + } + + // Special-case handling for format 35c/3rc invokes + Instruction::Code opcode = mir->dalvikInsn.opcode; + int flags = (static_cast(opcode) >= kNumPackedOpcodes) + ? 0 : Instruction::FlagsOf(mir->dalvikInsn.opcode); + if ((flags & Instruction::kInvoke) && + (attrs & (DF_FORMAT_35C | DF_FORMAT_3RC))) { + DCHECK_EQ(next, 0); + int target_idx = mir->dalvikInsn.vB; + const char* shorty = GetShortyFromTargetIdx(target_idx); + // Handle result type if floating point + if ((shorty[0] == 'F') || (shorty[0] == 'D')) { + MIR* move_result_mir = FindMoveResult(bb, mir); + // Result might not be used at all, so no move-result + if (move_result_mir && (move_result_mir->dalvikInsn.opcode != + Instruction::MOVE_RESULT_OBJECT)) { + SSARepresentation* tgt_rep = move_result_mir->ssa_rep; + DCHECK(tgt_rep != NULL); + tgt_rep->fp_def[0] = true; + changed |= SetFp(tgt_rep->defs[0]); + if (shorty[0] == 'D') { + tgt_rep->fp_def[1] = true; + changed |= SetFp(tgt_rep->defs[1]); + } } } - if (attrs & DF_UC) { - if (attrs & DF_CORE_C) { - changed |= SetCore(uses[next]); - } - if (attrs & DF_REF_C) { - changed |= SetRef(uses[next]); - } - if (attrs & DF_C_WIDE) { - reg_location_[uses[next]].wide = true; - reg_location_[uses[next + 1]].wide = true; - reg_location_[uses[next + 1]].high_word = true; - DCHECK_EQ(SRegToVReg(uses[next])+1, - SRegToVReg(uses[next + 1])); - } + int num_uses = mir->dalvikInsn.vA; + // If this is a non-static invoke, mark implicit "this" + if (((mir->dalvikInsn.opcode != Instruction::INVOKE_STATIC) && + (mir->dalvikInsn.opcode != Instruction::INVOKE_STATIC_RANGE))) { + reg_location_[uses[next]].defined = true; + reg_location_[uses[next]].ref = true; + next++; } - - // Special-case return handling - if ((mir->dalvikInsn.opcode == Instruction::RETURN) || - (mir->dalvikInsn.opcode == Instruction::RETURN_WIDE) || - (mir->dalvikInsn.opcode == Instruction::RETURN_OBJECT)) { - switch (cu_->shorty[0]) { - case 'I': - changed |= SetCore(uses[0]); + uint32_t cpos = 1; + if (strlen(shorty) > 1) { + for (int i = next; i < num_uses;) { + DCHECK_LT(cpos, strlen(shorty)); + switch (shorty[cpos++]) { + case 'D': + ssa_rep->fp_use[i] = true; + ssa_rep->fp_use[i+1] = true; + reg_location_[uses[i]].wide = true; + reg_location_[uses[i+1]].wide = true; + reg_location_[uses[i+1]].high_word = true; + DCHECK_EQ(SRegToVReg(uses[i])+1, SRegToVReg(uses[i+1])); + i++; break; case 'J': - changed |= SetCore(uses[0]); - changed |= SetCore(uses[1]); - reg_location_[uses[0]].wide = true; - reg_location_[uses[1]].wide = true; - reg_location_[uses[1]].high_word = true; + reg_location_[uses[i]].wide = true; + reg_location_[uses[i+1]].wide = true; + reg_location_[uses[i+1]].high_word = true; + DCHECK_EQ(SRegToVReg(uses[i])+1, SRegToVReg(uses[i+1])); + changed |= SetCore(uses[i]); + i++; break; case 'F': - changed |= SetFp(uses[0]); - break; - case 'D': - changed |= SetFp(uses[0]); - changed |= SetFp(uses[1]); - reg_location_[uses[0]].wide = true; - reg_location_[uses[1]].wide = true; - reg_location_[uses[1]].high_word = true; + ssa_rep->fp_use[i] = true; break; case 'L': - changed |= SetRef(uses[0]); + changed |= SetRef(uses[i]); + break; + default: + changed |= SetCore(uses[i]); break; - default: break; - } - } - - // Special-case handling for format 35c/3rc invokes - Instruction::Code opcode = mir->dalvikInsn.opcode; - int flags = (static_cast(opcode) >= kNumPackedOpcodes) - ? 0 : Instruction::FlagsOf(mir->dalvikInsn.opcode); - if ((flags & Instruction::kInvoke) && - (attrs & (DF_FORMAT_35C | DF_FORMAT_3RC))) { - DCHECK_EQ(next, 0); - int target_idx = mir->dalvikInsn.vB; - const char* shorty = GetShortyFromTargetIdx(target_idx); - // Handle result type if floating point - if ((shorty[0] == 'F') || (shorty[0] == 'D')) { - MIR* move_result_mir = FindMoveResult(bb, mir); - // Result might not be used at all, so no move-result - if (move_result_mir && (move_result_mir->dalvikInsn.opcode != - Instruction::MOVE_RESULT_OBJECT)) { - SSARepresentation* tgt_rep = move_result_mir->ssa_rep; - DCHECK(tgt_rep != NULL); - tgt_rep->fp_def[0] = true; - changed |= SetFp(tgt_rep->defs[0]); - if (shorty[0] == 'D') { - tgt_rep->fp_def[1] = true; - changed |= SetFp(tgt_rep->defs[1]); - } - } - } - int num_uses = mir->dalvikInsn.vA; - // If this is a non-static invoke, mark implicit "this" - if (((mir->dalvikInsn.opcode != Instruction::INVOKE_STATIC) && - (mir->dalvikInsn.opcode != Instruction::INVOKE_STATIC_RANGE))) { - reg_location_[uses[next]].defined = true; - reg_location_[uses[next]].ref = true; - next++; - } - uint32_t cpos = 1; - if (strlen(shorty) > 1) { - for (int i = next; i < num_uses;) { - DCHECK_LT(cpos, strlen(shorty)); - switch (shorty[cpos++]) { - case 'D': - ssa_rep->fp_use[i] = true; - ssa_rep->fp_use[i+1] = true; - reg_location_[uses[i]].wide = true; - reg_location_[uses[i+1]].wide = true; - reg_location_[uses[i+1]].high_word = true; - DCHECK_EQ(SRegToVReg(uses[i])+1, SRegToVReg(uses[i+1])); - i++; - break; - case 'J': - reg_location_[uses[i]].wide = true; - reg_location_[uses[i+1]].wide = true; - reg_location_[uses[i+1]].high_word = true; - DCHECK_EQ(SRegToVReg(uses[i])+1, SRegToVReg(uses[i+1])); - changed |= SetCore(uses[i]); - i++; - break; - case 'F': - ssa_rep->fp_use[i] = true; - break; - case 'L': - changed |= SetRef(uses[i]); - break; - default: - changed |= SetCore(uses[i]); - break; - } - i++; } + i++; } } + } - for (int i = 0; ssa_rep->fp_use && i< ssa_rep->num_uses; i++) { - if (ssa_rep->fp_use[i]) - changed |= SetFp(uses[i]); - } - for (int i = 0; ssa_rep->fp_def && i< ssa_rep->num_defs; i++) { - if (ssa_rep->fp_def[i]) - changed |= SetFp(defs[i]); - } - // Special-case handling for moves & Phi - if (attrs & (DF_IS_MOVE | DF_NULL_TRANSFER_N)) { - /* - * If any of our inputs or outputs is defined, set all. - * Some ugliness related to Phi nodes and wide values. - * The Phi set will include all low words or all high - * words, so we have to treat them specially. - */ - bool is_phi = (static_cast(mir->dalvikInsn.opcode) == - kMirOpPhi); - RegLocation rl_temp = reg_location_[defs[0]]; - bool defined_fp = rl_temp.defined && rl_temp.fp; - bool defined_core = rl_temp.defined && rl_temp.core; - bool defined_ref = rl_temp.defined && rl_temp.ref; - bool is_wide = rl_temp.wide || ((attrs & DF_A_WIDE) != 0); - bool is_high = is_phi && rl_temp.wide && rl_temp.high_word; - for (int i = 0; i < ssa_rep->num_uses; i++) { - rl_temp = reg_location_[uses[i]]; - defined_fp |= rl_temp.defined && rl_temp.fp; - defined_core |= rl_temp.defined && rl_temp.core; - defined_ref |= rl_temp.defined && rl_temp.ref; - is_wide |= rl_temp.wide; - is_high |= is_phi && rl_temp.wide && rl_temp.high_word; - } - /* - * We don't normally expect to see a Dalvik register definition used both as a - * floating point and core value, though technically it could happen with constants. - * Until we have proper typing, detect this situation and disable register promotion - * (which relies on the distinction between core a fp usages). - */ - if ((defined_fp && (defined_core | defined_ref)) && - ((cu_->disable_opt & (1 << kPromoteRegs)) == 0)) { - LOG(WARNING) << PrettyMethod(cu_->method_idx, *cu_->dex_file) - << " op at block " << bb->id - << " has both fp and core/ref uses for same def."; - cu_->disable_opt |= (1 << kPromoteRegs); - } - changed |= SetFp(defs[0], defined_fp); - changed |= SetCore(defs[0], defined_core); - changed |= SetRef(defs[0], defined_ref); - changed |= SetWide(defs[0], is_wide); - changed |= SetHigh(defs[0], is_high); - if (attrs & DF_A_WIDE) { - changed |= SetWide(defs[1]); - changed |= SetHigh(defs[1]); - } - for (int i = 0; i < ssa_rep->num_uses; i++) { - changed |= SetFp(uses[i], defined_fp); - changed |= SetCore(uses[i], defined_core); - changed |= SetRef(uses[i], defined_ref); - changed |= SetWide(uses[i], is_wide); - changed |= SetHigh(uses[i], is_high); - } - if (attrs & DF_A_WIDE) { - DCHECK_EQ(ssa_rep->num_uses, 2); - changed |= SetWide(uses[1]); - changed |= SetHigh(uses[1]); - } + for (int i = 0; ssa_rep->fp_use && i< ssa_rep->num_uses; i++) { + if (ssa_rep->fp_use[i]) + changed |= SetFp(uses[i]); + } + for (int i = 0; ssa_rep->fp_def && i< ssa_rep->num_defs; i++) { + if (ssa_rep->fp_def[i]) + changed |= SetFp(defs[i]); + } + // Special-case handling for moves & Phi + if (attrs & (DF_IS_MOVE | DF_NULL_TRANSFER_N)) { + /* + * If any of our inputs or outputs is defined, set all. + * Some ugliness related to Phi nodes and wide values. + * The Phi set will include all low words or all high + * words, so we have to treat them specially. + */ + bool is_phi = (static_cast(mir->dalvikInsn.opcode) == + kMirOpPhi); + RegLocation rl_temp = reg_location_[defs[0]]; + bool defined_fp = rl_temp.defined && rl_temp.fp; + bool defined_core = rl_temp.defined && rl_temp.core; + bool defined_ref = rl_temp.defined && rl_temp.ref; + bool is_wide = rl_temp.wide || ((attrs & DF_A_WIDE) != 0); + bool is_high = is_phi && rl_temp.wide && rl_temp.high_word; + for (int i = 0; i < ssa_rep->num_uses; i++) { + rl_temp = reg_location_[uses[i]]; + defined_fp |= rl_temp.defined && rl_temp.fp; + defined_core |= rl_temp.defined && rl_temp.core; + defined_ref |= rl_temp.defined && rl_temp.ref; + is_wide |= rl_temp.wide; + is_high |= is_phi && rl_temp.wide && rl_temp.high_word; + } + /* + * We don't normally expect to see a Dalvik register definition used both as a + * floating point and core value, though technically it could happen with constants. + * Until we have proper typing, detect this situation and disable register promotion + * (which relies on the distinction between core a fp usages). + */ + if ((defined_fp && (defined_core | defined_ref)) && + ((cu_->disable_opt & (1 << kPromoteRegs)) == 0)) { + LOG(WARNING) << PrettyMethod(cu_->method_idx, *cu_->dex_file) + << " op at block " << bb->id + << " has both fp and core/ref uses for same def."; + cu_->disable_opt |= (1 << kPromoteRegs); + } + changed |= SetFp(defs[0], defined_fp); + changed |= SetCore(defs[0], defined_core); + changed |= SetRef(defs[0], defined_ref); + changed |= SetWide(defs[0], is_wide); + changed |= SetHigh(defs[0], is_high); + if (attrs & DF_A_WIDE) { + changed |= SetWide(defs[1]); + changed |= SetHigh(defs[1]); + } + for (int i = 0; i < ssa_rep->num_uses; i++) { + changed |= SetFp(uses[i], defined_fp); + changed |= SetCore(uses[i], defined_core); + changed |= SetRef(uses[i], defined_ref); + changed |= SetWide(uses[i], is_wide); + changed |= SetHigh(uses[i], is_high); + } + if (attrs & DF_A_WIDE) { + DCHECK_EQ(ssa_rep->num_uses, 2); + changed |= SetWide(uses[1]); + changed |= SetHigh(uses[1]); } } } @@ -417,13 +408,7 @@ static const RegLocation fresh_loc = {kLocDalvikFrame, 0, 0, 0, 0, 0, 0, 0, 0, INVALID_REG, INVALID_REG, INVALID_SREG, INVALID_SREG}; -/* - * Simple register allocation. Some Dalvik virtual registers may - * be promoted to physical registers. Most of the work for temp - * allocation is done on the fly. We also do some initialization and - * type inference here. - */ -void MIRGraph::BuildRegLocations() { +void MIRGraph::InitRegLocations() { /* Allocate the location map */ RegLocation* loc = static_cast(arena_->Alloc(GetNumSSARegs() * sizeof(*loc), ArenaAllocator::kAllocRegAlloc)); @@ -493,19 +478,14 @@ void MIRGraph::BuildRegLocations() { s_reg++; } } +} - /* Do type & size inference pass */ - RepeatingPreOrderDfsIterator iter(this); - bool change = false; - for (BasicBlock* bb = iter.Next(false); bb != NULL; bb = iter.Next(change)) { - change = InferTypeAndSize(bb); - } - - /* - * Set the s_reg_low field to refer to the pre-SSA name of the - * base Dalvik virtual register. Once we add a better register - * allocator, remove this remapping. - */ +/* + * Set the s_reg_low field to refer to the pre-SSA name of the + * base Dalvik virtual register. Once we add a better register + * allocator, remove this remapping. + */ +void MIRGraph::RemapRegLocations() { for (int i = 0; i < GetNumSSARegs(); i++) { if (reg_location_[i].location != kLocCompilerTemp) { int orig_sreg = reg_location_[i].s_reg_low; From 2ef411b95eec751e8a8f0f0629df0cdb7aec9546 Mon Sep 17 00:00:00 2001 From: Pavel Chupin Date: Wed, 27 Nov 2013 13:31:37 +0400 Subject: [PATCH 0238/2402] x86_64: Updating makefile to avoid early fail of other projects build x86_64 porting should be done later Change-Id: I73b336b1c46cb2c95dfacfc33aec490be9e357a7 Signed-off-by: Pavel Chupin --- runtime/Android.mk | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/runtime/Android.mk b/runtime/Android.mk index 16f11c6af77..b471d848ffd 100644 --- a/runtime/Android.mk +++ b/runtime/Android.mk @@ -207,6 +207,16 @@ LIBART_TARGET_SRC_FILES += \ arch/x86/thread_x86.cc LIBART_LDFLAGS += -Wl,--no-fatal-warnings else # TARGET_ARCH != x86 +ifeq ($(TARGET_ARCH),x86_64) +LIBART_TARGET_SRC_FILES += \ + arch/x86/context_x86.cc \ + arch/x86/entrypoints_init_x86.cc \ + arch/x86/jni_entrypoints_x86.S \ + arch/x86/portable_entrypoints_x86.S \ + arch/x86/quick_entrypoints_x86.S \ + arch/x86/thread_x86.cc +LIBART_LDFLAGS += -Wl,--no-fatal-warnings +else # TARGET_ARCH != x86_64 ifeq ($(TARGET_ARCH),mips) LIBART_TARGET_SRC_FILES += \ arch/mips/context_mips.cc \ @@ -219,6 +229,7 @@ else # TARGET_ARCH != mips $(error unsupported TARGET_ARCH=$(TARGET_ARCH)) endif # TARGET_ARCH != mips endif # TARGET_ARCH != x86 +endif # TARGET_ARCH != x86_64 endif # TARGET_ARCH != arm From f246af27f8c0736640eb7ae78225ef6c0c0a9f0e Mon Sep 17 00:00:00 2001 From: Vladimir Marko Date: Wed, 27 Nov 2013 12:30:15 +0000 Subject: [PATCH 0239/2402] Fix intrinsic Long.reverseBytes(). Allocate temporary only if needed and initialize it from the correct register. Change-Id: Ifdc0e8b586e1ef90fb817687eb86e05fba1dadbe --- compiler/dex/quick/gen_invoke.cc | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/compiler/dex/quick/gen_invoke.cc b/compiler/dex/quick/gen_invoke.cc index 469c5770713..9992499607d 100644 --- a/compiler/dex/quick/gen_invoke.cc +++ b/compiler/dex/quick/gen_invoke.cc @@ -971,10 +971,17 @@ bool Mir2Lir::GenInlinedReverseBytes(CallInfo* info, OpSize size) { RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true); if (size == kLong) { RegLocation rl_i = LoadValueWide(rl_src_i, kCoreReg); - int reg_tmp = AllocTemp(); - OpRegCopy(reg_tmp, rl_result.low_reg); + int r_i_low = rl_i.low_reg; + if (rl_i.low_reg == rl_result.low_reg) { + // First REV shall clobber rl_result.low_reg, save the value in a temp for the second REV. + r_i_low = AllocTemp(); + OpRegCopy(r_i_low, rl_i.low_reg); + } OpRegReg(kOpRev, rl_result.low_reg, rl_i.high_reg); - OpRegReg(kOpRev, rl_result.high_reg, reg_tmp); + OpRegReg(kOpRev, rl_result.high_reg, r_i_low); + if (rl_i.low_reg == rl_result.low_reg) { + FreeTemp(r_i_low); + } StoreValueWide(rl_dest, rl_result); } else { DCHECK(size == kWord || size == kSignedHalf); From 2248c17cd3483c030571cc6163a0e0870da998c2 Mon Sep 17 00:00:00 2001 From: Chirayu Desai Date: Wed, 6 Nov 2013 20:43:21 +0530 Subject: [PATCH 0240/2402] tools: use '/usr/bin/env python' instead of '/usr/bin/python' * This is how it is done in other scripts in the AOSP tree, as '/usr/bin/python' may be python3 on some distros, which isn't supported yet. Change-Id: I0d9857fc6a122f505953fddcd6244dad75b1838a --- tools/cpplint.py | 2 +- tools/generate-operator-out.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/cpplint.py b/tools/cpplint.py index 30b5216b124..c2f6514c2bd 100755 --- a/tools/cpplint.py +++ b/tools/cpplint.py @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/env python # # Copyright (c) 2009 Google Inc. All rights reserved. # diff --git a/tools/generate-operator-out.py b/tools/generate-operator-out.py index 0c085fbe93e..19266b4f64a 100755 --- a/tools/generate-operator-out.py +++ b/tools/generate-operator-out.py @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/env python # # Copyright (C) 2012 The Android Open Source Project # From 1e6cb63d77090ddc6aa19c755d7066f66e9ff87e Mon Sep 17 00:00:00 2001 From: Vladimir Marko Date: Thu, 28 Nov 2013 16:27:29 +0000 Subject: [PATCH 0241/2402] Delta-encoding of mapping tables. Both PC offsets and dalvik offsets are delta-encoded. Since PC offsets are increasing, the deltas are then compressed as unsigned LEB128. Dalvik offsets are not monotonic, so their deltas are compressed as signed LEB128. This reduces the size of the mapping tables by about 30% on average, 25% from the PC offset and 5% from the dalvik offset delta encoding. Bug: 9437697 Change-Id: I600ab9c22dec178088d4947a811cca3bc8bd4cf4 --- compiler/dex/quick/codegen_util.cc | 73 +++++++++++++++++---- compiler/dex/quick/mir_to_lir.h | 2 +- compiler/leb128_encoder.h | 55 +++++++++++----- compiler/leb128_encoder_test.cc | 102 ++++++++++++++++++++++++++--- runtime/exception_test.cc | 20 +++--- runtime/leb128.h | 21 ++++-- runtime/mapping_table.h | 16 +++-- runtime/oat.cc | 2 +- 8 files changed, 229 insertions(+), 62 deletions(-) diff --git a/compiler/dex/quick/codegen_util.cc b/compiler/dex/quick/codegen_util.cc index 4bc0b357af0..92b24e140ae 100644 --- a/compiler/dex/quick/codegen_util.cc +++ b/compiler/dex/quick/codegen_util.cc @@ -556,12 +556,34 @@ bool Mir2Lir::VerifyCatchEntries() { void Mir2Lir::CreateMappingTables() { + uint32_t pc2dex_data_size = 0u; + uint32_t pc2dex_entries = 0u; + uint32_t pc2dex_offset = 0u; + uint32_t pc2dex_dalvik_offset = 0u; + uint32_t dex2pc_data_size = 0u; + uint32_t dex2pc_entries = 0u; + uint32_t dex2pc_offset = 0u; + uint32_t dex2pc_dalvik_offset = 0u; for (LIR* tgt_lir = first_lir_insn_; tgt_lir != NULL; tgt_lir = NEXT_LIR(tgt_lir)) { if (!tgt_lir->flags.is_nop && (tgt_lir->opcode == kPseudoSafepointPC)) { + pc2dex_entries += 1; + DCHECK(pc2dex_offset <= tgt_lir->offset); + pc2dex_data_size += UnsignedLeb128Size(tgt_lir->offset - pc2dex_offset); + pc2dex_data_size += SignedLeb128Size(static_cast(tgt_lir->dalvik_offset) - + static_cast(pc2dex_dalvik_offset)); + pc2dex_offset = tgt_lir->offset; + pc2dex_dalvik_offset = tgt_lir->dalvik_offset; pc2dex_mapping_table_.push_back(tgt_lir->offset); pc2dex_mapping_table_.push_back(tgt_lir->dalvik_offset); } if (!tgt_lir->flags.is_nop && (tgt_lir->opcode == kPseudoExportedPC)) { + dex2pc_entries += 1; + DCHECK(dex2pc_offset <= tgt_lir->offset); + dex2pc_data_size += UnsignedLeb128Size(tgt_lir->offset - dex2pc_offset); + dex2pc_data_size += SignedLeb128Size(static_cast(tgt_lir->dalvik_offset) - + static_cast(dex2pc_dalvik_offset)); + dex2pc_offset = tgt_lir->offset; + dex2pc_dalvik_offset = tgt_lir->dalvik_offset; dex2pc_mapping_table_.push_back(tgt_lir->offset); dex2pc_mapping_table_.push_back(tgt_lir->dalvik_offset); } @@ -569,32 +591,57 @@ void Mir2Lir::CreateMappingTables() { if (kIsDebugBuild) { CHECK(VerifyCatchEntries()); } - CHECK_EQ(pc2dex_mapping_table_.size() & 1, 0U); - CHECK_EQ(dex2pc_mapping_table_.size() & 1, 0U); - uint32_t total_entries = (pc2dex_mapping_table_.size() + dex2pc_mapping_table_.size()) / 2; - uint32_t pc2dex_entries = pc2dex_mapping_table_.size() / 2; - encoded_mapping_table_.PushBack(total_entries); - encoded_mapping_table_.PushBack(pc2dex_entries); - encoded_mapping_table_.InsertBack(pc2dex_mapping_table_.begin(), pc2dex_mapping_table_.end()); - encoded_mapping_table_.InsertBack(dex2pc_mapping_table_.begin(), dex2pc_mapping_table_.end()); + DCHECK_EQ(pc2dex_mapping_table_.size(), 2u * pc2dex_entries); + DCHECK_EQ(dex2pc_mapping_table_.size(), 2u * dex2pc_entries); + + uint32_t total_entries = pc2dex_entries + dex2pc_entries; + uint32_t hdr_data_size = UnsignedLeb128Size(total_entries) + UnsignedLeb128Size(pc2dex_entries); + uint32_t data_size = hdr_data_size + pc2dex_data_size + dex2pc_data_size; + encoded_mapping_table_.Reserve(data_size); + encoded_mapping_table_.PushBackUnsigned(total_entries); + encoded_mapping_table_.PushBackUnsigned(pc2dex_entries); + + dex2pc_offset = 0u; + dex2pc_dalvik_offset = 0u; + pc2dex_offset = 0u; + pc2dex_dalvik_offset = 0u; + for (uint32_t i = 0; i != pc2dex_entries; ++i) { + encoded_mapping_table_.PushBackUnsigned(pc2dex_mapping_table_[2 * i] - pc2dex_offset); + encoded_mapping_table_.PushBackSigned(static_cast(pc2dex_mapping_table_[2 * i + 1]) - + static_cast(pc2dex_dalvik_offset)); + pc2dex_offset = pc2dex_mapping_table_[2 * i]; + pc2dex_dalvik_offset = pc2dex_mapping_table_[2 * i + 1]; + } + DCHECK(encoded_mapping_table_.GetData().size() == hdr_data_size + pc2dex_data_size); + for (uint32_t i = 0; i != dex2pc_entries; ++i) { + encoded_mapping_table_.PushBackUnsigned(dex2pc_mapping_table_[2 * i] - dex2pc_offset); + encoded_mapping_table_.PushBackSigned(static_cast(dex2pc_mapping_table_[2 * i + 1]) - + static_cast(dex2pc_dalvik_offset)); + dex2pc_offset = dex2pc_mapping_table_[2 * i]; + dex2pc_dalvik_offset = dex2pc_mapping_table_[2 * i + 1]; + } + DCHECK(encoded_mapping_table_.GetData().size() == data_size); + if (kIsDebugBuild) { // Verify the encoded table holds the expected data. MappingTable table(&encoded_mapping_table_.GetData()[0]); CHECK_EQ(table.TotalSize(), total_entries); CHECK_EQ(table.PcToDexSize(), pc2dex_entries); CHECK_EQ(table.DexToPcSize(), dex2pc_mapping_table_.size() / 2); - MappingTable::PcToDexIterator it = table.PcToDexBegin(); + auto it = table.PcToDexBegin(); for (uint32_t i = 0; i < pc2dex_mapping_table_.size(); ++i, ++it) { CHECK_EQ(pc2dex_mapping_table_.at(i), it.NativePcOffset()); ++i; CHECK_EQ(pc2dex_mapping_table_.at(i), it.DexPc()); } - MappingTable::DexToPcIterator it2 = table.DexToPcBegin(); + CHECK(it == table.PcToDexEnd()); + auto it2 = table.DexToPcBegin(); for (uint32_t i = 0; i < dex2pc_mapping_table_.size(); ++i, ++it2) { CHECK_EQ(dex2pc_mapping_table_.at(i), it2.NativePcOffset()); ++i; CHECK_EQ(dex2pc_mapping_table_.at(i), it2.DexPc()); } + CHECK(it2 == table.DexToPcEnd()); } } @@ -986,11 +1033,11 @@ CompiledMethod* Mir2Lir::GetCompiledMethod() { for (uint32_t i = 0; i < fp_vmap_table_.size(); i++) { raw_vmap_table.push_back(fp_vmap_table_[i]); } - UnsignedLeb128EncodingVector vmap_encoder; + Leb128EncodingVector vmap_encoder; // Prefix the encoded data with its size. - vmap_encoder.PushBack(raw_vmap_table.size()); + vmap_encoder.PushBackUnsigned(raw_vmap_table.size()); for (uint16_t cur : raw_vmap_table) { - vmap_encoder.PushBack(cur); + vmap_encoder.PushBackUnsigned(cur); } CompiledMethod* result = new CompiledMethod(*cu_->compiler_driver, cu_->instruction_set, code_buffer_, frame_size_, diff --git a/compiler/dex/quick/mir_to_lir.h b/compiler/dex/quick/mir_to_lir.h index f8a2d03ec8f..92e21ffdf76 100644 --- a/compiler/dex/quick/mir_to_lir.h +++ b/compiler/dex/quick/mir_to_lir.h @@ -828,7 +828,7 @@ class Mir2Lir : public Backend { int live_sreg_; CodeBuffer code_buffer_; // The encoding mapping table data (dex -> pc offset and pc offset -> dex) with a size prefix. - UnsignedLeb128EncodingVector encoded_mapping_table_; + Leb128EncodingVector encoded_mapping_table_; std::vector core_vmap_table_; std::vector fp_vmap_table_; std::vector native_gc_map_; diff --git a/compiler/leb128_encoder.h b/compiler/leb128_encoder.h index e9a1c32a33f..fe38c2f5cdb 100644 --- a/compiler/leb128_encoder.h +++ b/compiler/leb128_encoder.h @@ -18,33 +18,54 @@ #define ART_COMPILER_LEB128_ENCODER_H_ #include "base/macros.h" +#include "leb128.h" namespace art { // An encoder with an API similar to vector where the data is captured in ULEB128 format. -class UnsignedLeb128EncodingVector { +class Leb128EncodingVector { public: - UnsignedLeb128EncodingVector() { + Leb128EncodingVector() { } - void PushBack(uint32_t value) { - bool done = false; - do { - uint8_t out = value & 0x7f; - if (out != value) { - data_.push_back(out | 0x80); - value >>= 7; - } else { - data_.push_back(out); - done = true; - } - } while (!done); + void Reserve(uint32_t size) { + data_.reserve(size); + } + + void PushBackUnsigned(uint32_t value) { + uint8_t out = value & 0x7f; + value >>= 7; + while (value != 0) { + data_.push_back(out | 0x80); + out = value & 0x7f; + value >>= 7; + } + data_.push_back(out); + } + + template + void InsertBackUnsigned(It cur, It end) { + for (; cur != end; ++cur) { + PushBackUnsigned(*cur); + } + } + + void PushBackSigned(int32_t value) { + uint32_t extra_bits = static_cast(value ^ (value >> 31)) >> 6; + uint8_t out = value & 0x7f; + while (extra_bits != 0u) { + data_.push_back(out | 0x80); + value >>= 7; + out = value & 0x7f; + extra_bits >>= 7; + } + data_.push_back(out); } template - void InsertBack(It cur, It end) { + void InsertBackSigned(It cur, It end) { for (; cur != end; ++cur) { - PushBack(*cur); + PushBackSigned(*cur); } } @@ -55,7 +76,7 @@ class UnsignedLeb128EncodingVector { private: std::vector data_; - DISALLOW_COPY_AND_ASSIGN(UnsignedLeb128EncodingVector); + DISALLOW_COPY_AND_ASSIGN(Leb128EncodingVector); }; } // namespace art diff --git a/compiler/leb128_encoder_test.cc b/compiler/leb128_encoder_test.cc index 4fa80757c5f..3162ca5f466 100644 --- a/compiler/leb128_encoder_test.cc +++ b/compiler/leb128_encoder_test.cc @@ -42,11 +42,61 @@ static DecodeUnsignedLeb128TestCase uleb128_tests[] = { {0xFFFFFFFF, {0xFF, 0xFF, 0xFF, 0xFF, 0xF}}, }; -TEST_F(Leb128Test, Singles) { +struct DecodeSignedLeb128TestCase { + int32_t decoded; + uint8_t leb128_data[5]; +}; + +static DecodeSignedLeb128TestCase sleb128_tests[] = { + {0, {0, 0, 0, 0, 0}}, + {1, {1, 0, 0, 0, 0}}, + {0x3F, {0x3F, 0, 0, 0, 0}}, + {0x40, {0xC0, 0 /* sign bit */, 0, 0, 0}}, + {0x41, {0xC1, 0 /* sign bit */, 0, 0, 0}}, + {0x80, {0x80, 1, 0, 0, 0}}, + {0xFF, {0xFF, 1, 0, 0, 0}}, + {0x1FFF, {0xFF, 0x3F, 0, 0, 0}}, + {0x2000, {0x80, 0xC0, 0 /* sign bit */, 0, 0}}, + {0x2001, {0x81, 0xC0, 0 /* sign bit */, 0, 0}}, + {0x2081, {0x81, 0xC1, 0 /* sign bit */, 0, 0}}, + {0x4000, {0x80, 0x80, 1, 0, 0}}, + {0x0FFFFF, {0xFF, 0xFF, 0x3F, 0, 0}}, + {0x100000, {0x80, 0x80, 0xC0, 0 /* sign bit */, 0}}, + {0x100001, {0x81, 0x80, 0xC0, 0 /* sign bit */, 0}}, + {0x100081, {0x81, 0x81, 0xC0, 0 /* sign bit */, 0}}, + {0x104081, {0x81, 0x81, 0xC1, 0 /* sign bit */, 0}}, + {0x200000, {0x80, 0x80, 0x80, 1, 0}}, + {0x7FFFFFF, {0xFF, 0xFF, 0xFF, 0x3F, 0}}, + {0x8000000, {0x80, 0x80, 0x80, 0xC0, 0 /* sign bit */}}, + {0x8000001, {0x81, 0x80, 0x80, 0xC0, 0 /* sign bit */}}, + {0x8000081, {0x81, 0x81, 0x80, 0xC0, 0 /* sign bit */}}, + {0x8004081, {0x81, 0x81, 0x81, 0xC0, 0 /* sign bit */}}, + {0x8204081, {0x81, 0x81, 0x81, 0xC1, 0 /* sign bit */}}, + {0x0FFFFFFF, {0xFF, 0xFF, 0xFF, 0xFF, 0 /* sign bit */}}, + {0x10000000, {0x80, 0x80, 0x80, 0x80, 1}}, + {0x7FFFFFFF, {0xFF, 0xFF, 0xFF, 0xFF, 0x7}}, + {-1, {0x7F, 0, 0, 0, 0}}, + {-2, {0x7E, 0, 0, 0, 0}}, + {-0x3F, {0x41, 0, 0, 0, 0}}, + {-0x40, {0x40, 0, 0, 0, 0}}, + {-0x41, {0xBF, 0x7F, 0, 0, 0}}, + {-0x80, {0x80, 0x7F, 0, 0, 0}}, + {-0x81, {0xFF, 0x7E, 0, 0, 0}}, + {-0x00002000, {0x80, 0x40, 0, 0, 0}}, + {-0x00002001, {0xFF, 0xBF, 0x7F, 0, 0}}, + {-0x00100000, {0x80, 0x80, 0x40, 0, 0}}, + {-0x00100001, {0xFF, 0xFF, 0xBF, 0x7F, 0}}, + {-0x08000000, {0x80, 0x80, 0x80, 0x40, 0}}, + {-0x08000001, {0xFF, 0xFF, 0xFF, 0xBF, 0x7F}}, + {-0x20000000, {0x80, 0x80, 0x80, 0x80, 0x7E}}, + {(-1) << 31, {0x80, 0x80, 0x80, 0x80, 0x78}}, +}; + +TEST_F(Leb128Test, UnsignedSingles) { // Test individual encodings. for (size_t i = 0; i < arraysize(uleb128_tests); ++i) { - UnsignedLeb128EncodingVector builder; - builder.PushBack(uleb128_tests[i].decoded); + Leb128EncodingVector builder; + builder.PushBackUnsigned(uleb128_tests[i].decoded); const uint8_t* data_ptr = &uleb128_tests[i].leb128_data[0]; const uint8_t* encoded_data_ptr = &builder.GetData()[0]; for (size_t j = 0; j < 5; ++j) { @@ -60,11 +110,11 @@ TEST_F(Leb128Test, Singles) { } } -TEST_F(Leb128Test, Stream) { +TEST_F(Leb128Test, UnsignedStream) { // Encode a number of entries. - UnsignedLeb128EncodingVector builder; + Leb128EncodingVector builder; for (size_t i = 0; i < arraysize(uleb128_tests); ++i) { - builder.PushBack(uleb128_tests[i].decoded); + builder.PushBackUnsigned(uleb128_tests[i].decoded); } const uint8_t* encoded_data_ptr = &builder.GetData()[0]; for (size_t i = 0; i < arraysize(uleb128_tests); ++i) { @@ -78,15 +128,51 @@ TEST_F(Leb128Test, Stream) { } } +TEST_F(Leb128Test, SignedSingles) { + // Test individual encodings. + for (size_t i = 0; i < arraysize(sleb128_tests); ++i) { + Leb128EncodingVector builder; + builder.PushBackSigned(sleb128_tests[i].decoded); + const uint8_t* data_ptr = &sleb128_tests[i].leb128_data[0]; + const uint8_t* encoded_data_ptr = &builder.GetData()[0]; + for (size_t j = 0; j < 5; ++j) { + if (j < builder.GetData().size()) { + EXPECT_EQ(data_ptr[j], encoded_data_ptr[j]) << " i = " << i << " j = " << j; + } else { + EXPECT_EQ(data_ptr[j], 0U) << " i = " << i << " j = " << j; + } + } + EXPECT_EQ(DecodeSignedLeb128(&data_ptr), sleb128_tests[i].decoded) << " i = " << i; + } +} + +TEST_F(Leb128Test, SignedStream) { + // Encode a number of entries. + Leb128EncodingVector builder; + for (size_t i = 0; i < arraysize(sleb128_tests); ++i) { + builder.PushBackSigned(sleb128_tests[i].decoded); + } + const uint8_t* encoded_data_ptr = &builder.GetData()[0]; + for (size_t i = 0; i < arraysize(sleb128_tests); ++i) { + const uint8_t* data_ptr = &sleb128_tests[i].leb128_data[0]; + for (size_t j = 0; j < 5; ++j) { + if (data_ptr[j] != 0) { + EXPECT_EQ(data_ptr[j], encoded_data_ptr[j]) << " i = " << i << " j = " << j; + } + } + EXPECT_EQ(DecodeSignedLeb128(&encoded_data_ptr), sleb128_tests[i].decoded) << " i = " << i; + } +} + TEST_F(Leb128Test, Speed) { UniquePtr > enc_hist(new Histogram("Leb128EncodeSpeedTest", 5)); UniquePtr > dec_hist(new Histogram("Leb128DecodeSpeedTest", 5)); - UnsignedLeb128EncodingVector builder; + Leb128EncodingVector builder; // Push back 1024 chunks of 1024 values measuring encoding speed. uint64_t last_time = NanoTime(); for (size_t i = 0; i < 1024; i++) { for (size_t j = 0; j < 1024; j++) { - builder.PushBack((i * 1024) + j); + builder.PushBackUnsigned((i * 1024) + j); } uint64_t cur_time = NanoTime(); enc_hist->AddValue(cur_time - last_time); diff --git a/runtime/exception_test.cc b/runtime/exception_test.cc index e9a6e4fa491..8f542d80acc 100644 --- a/runtime/exception_test.cc +++ b/runtime/exception_test.cc @@ -54,17 +54,17 @@ class ExceptionTest : public CommonTest { fake_code_.push_back(0x70 | i); } - fake_mapping_data_.PushBack(4); // first element is count - fake_mapping_data_.PushBack(4); // total (non-length) elements - fake_mapping_data_.PushBack(2); // count of pc to dex elements + fake_mapping_data_.PushBackUnsigned(4); // first element is count + fake_mapping_data_.PushBackUnsigned(4); // total (non-length) elements + fake_mapping_data_.PushBackUnsigned(2); // count of pc to dex elements // --- pc to dex table - fake_mapping_data_.PushBack(3); // offset 3 - fake_mapping_data_.PushBack(3); // maps to dex offset 3 + fake_mapping_data_.PushBackUnsigned(3 - 0); // offset 3 + fake_mapping_data_.PushBackSigned(3 - 0); // maps to dex offset 3 // --- dex to pc table - fake_mapping_data_.PushBack(3); // offset 3 - fake_mapping_data_.PushBack(3); // maps to dex offset 3 + fake_mapping_data_.PushBackUnsigned(3 - 0); // offset 3 + fake_mapping_data_.PushBackSigned(3 - 0); // maps to dex offset 3 - fake_vmap_table_data_.PushBack(0); + fake_vmap_table_data_.PushBackUnsigned(0); fake_gc_map_.push_back(0); // 0 bytes to encode references and native pc offsets. fake_gc_map_.push_back(0); @@ -91,8 +91,8 @@ class ExceptionTest : public CommonTest { const DexFile* dex_; std::vector fake_code_; - UnsignedLeb128EncodingVector fake_mapping_data_; - UnsignedLeb128EncodingVector fake_vmap_table_data_; + Leb128EncodingVector fake_mapping_data_; + Leb128EncodingVector fake_vmap_table_data_; std::vector fake_gc_map_; mirror::ArtMethod* method_f_; diff --git a/runtime/leb128.h b/runtime/leb128.h index 6041f8c31f4..7a7d38d584a 100644 --- a/runtime/leb128.h +++ b/runtime/leb128.h @@ -18,6 +18,7 @@ #define ART_RUNTIME_LEB128_H_ #include "globals.h" +#include "utils.h" namespace art { @@ -95,12 +96,20 @@ static inline int32_t DecodeSignedLeb128(const uint8_t** data) { // Returns the number of bytes needed to encode the value in unsigned LEB128. static inline uint32_t UnsignedLeb128Size(uint32_t data) { - uint32_t count = 0; - do { - data >>= 7; - count++; - } while (data != 0); - return count; + // bits_to_encode = (data != 0) ? 32 - CLZ(x) : 1 // 32 - CLZ(data | 1) + // bytes = ceil(bits_to_encode / 7.0); // (6 + bits_to_encode) / 7 + uint32_t x = 6 + 32 - CLZ(data | 1); + // Division by 7 is done by (x * 37) >> 8 where 37 = ceil(256 / 7). + // This works for 0 <= x < 256 / (7 * 37 - 256), i.e. 0 <= x <= 85. + return (x * 37) >> 8; +} + +// Returns the number of bytes needed to encode the value in unsigned LEB128. +static inline uint32_t SignedLeb128Size(int32_t data) { + // Like UnsignedLeb128Size(), but we need one bit beyond the highest bit that differs from sign. + data = data ^ (data >> 31); + uint32_t x = 1 /* we need to encode the sign bit */ + 6 + 32 - CLZ(data | 1); + return (x * 37) >> 8; } } // namespace art diff --git a/runtime/mapping_table.h b/runtime/mapping_table.h index c468c1efb4c..a82bc1c2162 100644 --- a/runtime/mapping_table.h +++ b/runtime/mapping_table.h @@ -72,7 +72,8 @@ class MappingTable { if (end_ > 0) { encoded_table_ptr_ = table_->FirstDexToPcPtr(); native_pc_offset_ = DecodeUnsignedLeb128(&encoded_table_ptr_); - dex_pc_ = DecodeUnsignedLeb128(&encoded_table_ptr_); + // First delta is always positive. + dex_pc_ = static_cast(DecodeSignedLeb128(&encoded_table_ptr_)); } } else { // An iterator wanted from the end. DCHECK_EQ(table_->DexToPcSize(), element); @@ -87,8 +88,9 @@ class MappingTable { void operator++() { ++element_; if (element_ != end_) { // Avoid reading beyond the end of the table. - native_pc_offset_ = DecodeUnsignedLeb128(&encoded_table_ptr_); - dex_pc_ = DecodeUnsignedLeb128(&encoded_table_ptr_); + native_pc_offset_ += DecodeUnsignedLeb128(&encoded_table_ptr_); + // For negative delta, unsigned overflow after static_cast does exactly what we need. + dex_pc_ += static_cast(DecodeSignedLeb128(&encoded_table_ptr_)); } } bool operator==(const DexToPcIterator& rhs) const { @@ -147,7 +149,8 @@ class MappingTable { if (end_ > 0) { encoded_table_ptr_ = table_->FirstPcToDexPtr(); native_pc_offset_ = DecodeUnsignedLeb128(&encoded_table_ptr_); - dex_pc_ = DecodeUnsignedLeb128(&encoded_table_ptr_); + // First delta is always positive. + dex_pc_ = static_cast(DecodeSignedLeb128(&encoded_table_ptr_)); } } else { // An iterator wanted from the end. DCHECK_EQ(table_->PcToDexSize(), element); @@ -162,8 +165,9 @@ class MappingTable { void operator++() { ++element_; if (element_ != end_) { // Avoid reading beyond the end of the table. - native_pc_offset_ = DecodeUnsignedLeb128(&encoded_table_ptr_); - dex_pc_ = DecodeUnsignedLeb128(&encoded_table_ptr_); + native_pc_offset_ += DecodeUnsignedLeb128(&encoded_table_ptr_); + // For negative delta, unsigned overflow after static_cast does exactly what we need. + dex_pc_ += static_cast(DecodeSignedLeb128(&encoded_table_ptr_)); } } bool operator==(const PcToDexIterator& rhs) const { diff --git a/runtime/oat.cc b/runtime/oat.cc index 50069b2a781..52e74abd750 100644 --- a/runtime/oat.cc +++ b/runtime/oat.cc @@ -22,7 +22,7 @@ namespace art { const uint8_t OatHeader::kOatMagic[] = { 'o', 'a', 't', '\n' }; -const uint8_t OatHeader::kOatVersion[] = { '0', '1', '1', '\0' }; +const uint8_t OatHeader::kOatVersion[] = { '0', '1', '2', '\0' }; OatHeader::OatHeader() { memset(this, 0, sizeof(*this)); From c29bb614c60e0eb9a2bacf90f6dfce796344021e Mon Sep 17 00:00:00 2001 From: Vladimir Marko Date: Wed, 27 Nov 2013 16:47:25 +0000 Subject: [PATCH 0242/2402] Unsafe.compareAndSwapInt()/Object() intrinsics for x86. Bug: 11391018 Change-Id: I0a97375103917b0e9e20f199304c17a7f849c361 --- compiler/dex/quick/x86/assemble_x86.cc | 1 - compiler/dex/quick/x86/int_x86.cc | 46 ++++++++++++++++++- .../quick/x86/x86_dex_file_method_inliner.cc | 8 ++-- compiler/dex/quick/x86/x86_lir.h | 2 +- 4 files changed, 49 insertions(+), 8 deletions(-) diff --git a/compiler/dex/quick/x86/assemble_x86.cc b/compiler/dex/quick/x86/assemble_x86.cc index 191c9c701bc..1bf79cc46c1 100644 --- a/compiler/dex/quick/x86/assemble_x86.cc +++ b/compiler/dex/quick/x86/assemble_x86.cc @@ -306,7 +306,6 @@ ENCODING_MAP(Cmp, IS_LOAD, 0, 0, { kX86CmpxchgRR, kRegRegStore, IS_BINARY_OP | REG_DEF0 | REG_USE01 | REG_DEFA_USEA | SETS_CCODES, { 0, 0, 0x0F, 0xB1, 0, 0, 0, 0 }, "Cmpxchg", "!0r,!1r" }, { kX86CmpxchgMR, kMemReg, IS_STORE | IS_TERTIARY_OP | REG_USE02 | REG_DEFA_USEA | SETS_CCODES, { 0, 0, 0x0F, 0xB1, 0, 0, 0, 0 }, "Cmpxchg", "[!0r+!1d],!2r" }, { kX86CmpxchgAR, kArrayReg, IS_STORE | IS_QUIN_OP | REG_USE014 | REG_DEFA_USEA | SETS_CCODES, { 0, 0, 0x0F, 0xB1, 0, 0, 0, 0 }, "Cmpxchg", "[!0r+!1r<instruction_set, kThumb2); - return false; + DCHECK_EQ(cu_->instruction_set, kX86); + // Unused - RegLocation rl_src_unsafe = info->args[0]; + RegLocation rl_src_obj = info->args[1]; // Object - known non-null + RegLocation rl_src_offset = info->args[2]; // long low + rl_src_offset.wide = 0; // ignore high half in info->args[3] + RegLocation rl_src_expected = info->args[4]; // int, long or Object + // If is_long, high half is in info->args[5] + RegLocation rl_src_new_value = info->args[is_long ? 6 : 5]; // int, long or Object + // If is_long, high half is in info->args[7] + + if (is_long) { + LOG(FATAL) << "CAS64: Not implemented"; + } else { + // EAX must hold expected for CMPXCHG. Neither rl_new_value, nor r_ptr may be in EAX. + FlushReg(r0); + LockTemp(r0); + + // Release store semantics, get the barrier out of the way. TODO: revisit + GenMemBarrier(kStoreLoad); + + RegLocation rl_object = LoadValue(rl_src_obj, kCoreReg); + RegLocation rl_new_value = LoadValue(rl_src_new_value, kCoreReg); + + if (is_object && !mir_graph_->IsConstantNullRef(rl_new_value)) { + // Mark card for object assuming new value is stored. + FreeTemp(r0); // Temporarily release EAX for MarkGCCard(). + MarkGCCard(rl_new_value.low_reg, rl_object.low_reg); + LockTemp(r0); + } + + RegLocation rl_offset = LoadValue(rl_src_offset, kCoreReg); + LoadValueDirect(rl_src_expected, r0); + NewLIR5(kX86LockCmpxchgAR, rl_object.low_reg, rl_offset.low_reg, 0, 0, rl_new_value.low_reg); + + FreeTemp(r0); + } + + // Convert ZF to boolean + RegLocation rl_dest = InlineTarget(info); // boolean place for result + RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true); + NewLIR2(kX86Set8R, rl_result.low_reg, kX86CondZ); + NewLIR2(kX86Movzx8RR, rl_result.low_reg, rl_result.low_reg); + StoreValue(rl_dest, rl_result); + return true; } LIR* X86Mir2Lir::OpPcRelLoad(int reg, LIR* target) { diff --git a/compiler/dex/quick/x86/x86_dex_file_method_inliner.cc b/compiler/dex/quick/x86/x86_dex_file_method_inliner.cc index b788c3ce056..f0e76c9e5e3 100644 --- a/compiler/dex/quick/x86/x86_dex_file_method_inliner.cc +++ b/compiler/dex/quick/x86/x86_dex_file_method_inliner.cc @@ -64,12 +64,12 @@ const DexFileMethodInliner::IntrinsicDef X86DexFileMethodInliner::kIntrinsicMeth INTRINSIC(LibcoreIoMemory, PokeLongNative, JJ_V, kIntrinsicPoke, kLong), INTRINSIC(LibcoreIoMemory, PokeShortNative, JS_V, kIntrinsicPoke, kSignedHalf), - // INTRINSIC(SunMiscUnsafe, CompareAndSwapInt, ObjectJII_Z, kIntrinsicCas, - // kIntrinsicFlagNone), + INTRINSIC(SunMiscUnsafe, CompareAndSwapInt, ObjectJII_Z, kIntrinsicCas, + kIntrinsicFlagNone), // INTRINSIC(SunMiscUnsafe, CompareAndSwapLong, ObjectJJJ_Z, kIntrinsicCas, // kIntrinsicFlagIsLong), - // INTRINSIC(SunMiscUnsafe, CompareAndSwapObject, ObjectJObjectObject_Z, kIntrinsicCas, - // kIntrinsicFlagIsObject), + INTRINSIC(SunMiscUnsafe, CompareAndSwapObject, ObjectJObjectObject_Z, kIntrinsicCas, + kIntrinsicFlagIsObject), #define UNSAFE_GET_PUT(type, code, type_flags) \ INTRINSIC(SunMiscUnsafe, Get ## type, ObjectJ_ ## code, kIntrinsicUnsafeGet, \ diff --git a/compiler/dex/quick/x86/x86_lir.h b/compiler/dex/quick/x86/x86_lir.h index 3518131cfe3..bbcae92f7de 100644 --- a/compiler/dex/quick/x86/x86_lir.h +++ b/compiler/dex/quick/x86/x86_lir.h @@ -354,7 +354,7 @@ enum X86OpCode { Binary0fOpCode(kX86Imul16), // 16bit multiply Binary0fOpCode(kX86Imul32), // 32bit multiply kX86CmpxchgRR, kX86CmpxchgMR, kX86CmpxchgAR, // compare and exchange - kX86LockCmpxchgRR, kX86LockCmpxchgMR, kX86LockCmpxchgAR, // locked compare and exchange + kX86LockCmpxchgMR, kX86LockCmpxchgAR, // locked compare and exchange Binary0fOpCode(kX86Movzx8), // zero-extend 8-bit value Binary0fOpCode(kX86Movzx16), // zero-extend 16-bit value Binary0fOpCode(kX86Movsx8), // sign-extend 8-bit value From 057c74a3a2d50d1247d4e6472763ca6f59060762 Mon Sep 17 00:00:00 2001 From: Vladimir Marko Date: Tue, 3 Dec 2013 15:20:45 +0000 Subject: [PATCH 0243/2402] Add support for emitting x86 kArray instructions. And factor out a lot of common code. Change-Id: Ib1f135e341404f8a6f92fcef0047ec04577d32cd --- compiler/dex/quick/x86/assemble_x86.cc | 474 +++++++------------------ compiler/dex/quick/x86/codegen_x86.h | 10 +- 2 files changed, 134 insertions(+), 350 deletions(-) diff --git a/compiler/dex/quick/x86/assemble_x86.cc b/compiler/dex/quick/x86/assemble_x86.cc index 1bf79cc46c1..0b7b2bd94f5 100644 --- a/compiler/dex/quick/x86/assemble_x86.cc +++ b/compiler/dex/quick/x86/assemble_x86.cc @@ -492,6 +492,37 @@ int X86Mir2Lir::GetInsnSize(LIR* lir) { return 0; } +void X86Mir2Lir::EmitPrefix(const X86EncodingMap* entry) { + if (entry->skeleton.prefix1 != 0) { + code_buffer_.push_back(entry->skeleton.prefix1); + if (entry->skeleton.prefix2 != 0) { + code_buffer_.push_back(entry->skeleton.prefix2); + } + } else { + DCHECK_EQ(0, entry->skeleton.prefix2); + } +} + +void X86Mir2Lir::EmitOpcode(const X86EncodingMap* entry) { + code_buffer_.push_back(entry->skeleton.opcode); + if (entry->skeleton.opcode == 0x0F) { + code_buffer_.push_back(entry->skeleton.extra_opcode1); + if (entry->skeleton.extra_opcode1 == 0x38 || entry->skeleton.extra_opcode1 == 0x3A) { + code_buffer_.push_back(entry->skeleton.extra_opcode2); + } else { + DCHECK_EQ(0, entry->skeleton.extra_opcode2); + } + } else { + DCHECK_EQ(0, entry->skeleton.extra_opcode1); + DCHECK_EQ(0, entry->skeleton.extra_opcode2); + } +} + +void X86Mir2Lir::EmitPrefixAndOpcode(const X86EncodingMap* entry) { + EmitPrefix(entry); + EmitOpcode(entry); +} + static uint8_t ModrmForDisp(int base, int disp) { // BP requires an explicit disp, so do not omit it in the 0 case if (disp == 0 && base != rBP) { @@ -503,7 +534,7 @@ static uint8_t ModrmForDisp(int base, int disp) { } } -void X86Mir2Lir::EmitDisp(int base, int disp) { +void X86Mir2Lir::EmitDisp(uint8_t base, int disp) { // BP requires an explicit disp, so do not omit it in the 0 case if (disp == 0 && base != rBP) { return; @@ -517,26 +548,60 @@ void X86Mir2Lir::EmitDisp(int base, int disp) { } } -void X86Mir2Lir::EmitOpRegOpcode(const X86EncodingMap* entry, uint8_t reg) { - if (entry->skeleton.prefix1 != 0) { - code_buffer_.push_back(entry->skeleton.prefix1); - if (entry->skeleton.prefix2 != 0) { - code_buffer_.push_back(entry->skeleton.prefix2); - } - } else { - DCHECK_EQ(0, entry->skeleton.prefix2); +void X86Mir2Lir::EmitModrmDisp(uint8_t reg_or_opcode, uint8_t base, int disp) { + DCHECK_LT(reg_or_opcode, 8); + DCHECK_LT(base, 8); + uint8_t modrm = (ModrmForDisp(base, disp) << 6) | (reg_or_opcode << 3) | base; + code_buffer_.push_back(modrm); + if (base == rX86_SP) { + // Special SIB for SP base + code_buffer_.push_back(0 << 6 | (rX86_SP << 3) | rX86_SP); } - code_buffer_.push_back(entry->skeleton.opcode); - if (entry->skeleton.opcode == 0x0F) { - code_buffer_.push_back(entry->skeleton.extra_opcode1); - // There's no 3-byte instruction with +rd - DCHECK_NE(0x38, entry->skeleton.extra_opcode1); - DCHECK_NE(0x3A, entry->skeleton.extra_opcode1); - DCHECK_EQ(0, entry->skeleton.extra_opcode2); - } else { - DCHECK_EQ(0, entry->skeleton.extra_opcode1); - DCHECK_EQ(0, entry->skeleton.extra_opcode2); + EmitDisp(base, disp); +} + +void X86Mir2Lir::EmitModrmSibDisp(uint8_t reg_or_opcode, uint8_t base, uint8_t index, + int scale, int disp) { + DCHECK_LT(reg_or_opcode, 8); + uint8_t modrm = (ModrmForDisp(base, disp) << 6) | (reg_or_opcode << 3) | rX86_SP; + code_buffer_.push_back(modrm); + DCHECK_LT(scale, 4); + DCHECK_LT(index, 8); + DCHECK_LT(base, 8); + uint8_t sib = (scale << 6) | (index << 3) | base; + code_buffer_.push_back(sib); + EmitDisp(base, disp); +} + +void X86Mir2Lir::EmitImm(const X86EncodingMap* entry, int imm) { + switch (entry->skeleton.immediate_bytes) { + case 1: + DCHECK(IS_SIMM8(imm)); + code_buffer_.push_back(imm & 0xFF); + break; + case 2: + DCHECK(IS_SIMM16(imm)); + code_buffer_.push_back(imm & 0xFF); + code_buffer_.push_back((imm >> 8) & 0xFF); + break; + case 4: + code_buffer_.push_back(imm & 0xFF); + code_buffer_.push_back((imm >> 8) & 0xFF); + code_buffer_.push_back((imm >> 16) & 0xFF); + code_buffer_.push_back((imm >> 24) & 0xFF); + break; + default: + LOG(FATAL) << "Unexpected immediate bytes (" << entry->skeleton.immediate_bytes + << ") for instruction: " << entry->name; + break; } +} + +void X86Mir2Lir::EmitOpRegOpcode(const X86EncodingMap* entry, uint8_t reg) { + EmitPrefixAndOpcode(entry); + // There's no 3-byte instruction with +rd + DCHECK(entry->skeleton.opcode != 0x0F || + (entry->skeleton.extra_opcode1 != 0x38 && entry->skeleton.extra_opcode1 != 0x3A)); DCHECK(!X86_FPREG(reg)); DCHECK_LT(reg, 8); code_buffer_.back() += reg; @@ -545,26 +610,7 @@ void X86Mir2Lir::EmitOpRegOpcode(const X86EncodingMap* entry, uint8_t reg) { } void X86Mir2Lir::EmitOpReg(const X86EncodingMap* entry, uint8_t reg) { - if (entry->skeleton.prefix1 != 0) { - code_buffer_.push_back(entry->skeleton.prefix1); - if (entry->skeleton.prefix2 != 0) { - code_buffer_.push_back(entry->skeleton.prefix2); - } - } else { - DCHECK_EQ(0, entry->skeleton.prefix2); - } - code_buffer_.push_back(entry->skeleton.opcode); - if (entry->skeleton.opcode == 0x0F) { - code_buffer_.push_back(entry->skeleton.extra_opcode1); - if (entry->skeleton.extra_opcode1 == 0x38 || entry->skeleton.extra_opcode1 == 0x3A) { - code_buffer_.push_back(entry->skeleton.extra_opcode2); - } else { - DCHECK_EQ(0, entry->skeleton.extra_opcode2); - } - } else { - DCHECK_EQ(0, entry->skeleton.extra_opcode1); - DCHECK_EQ(0, entry->skeleton.extra_opcode2); - } + EmitPrefixAndOpcode(entry); if (X86_FPREG(reg)) { reg = reg & X86_FP_REG_MASK; } @@ -580,48 +626,28 @@ void X86Mir2Lir::EmitOpReg(const X86EncodingMap* entry, uint8_t reg) { } void X86Mir2Lir::EmitOpMem(const X86EncodingMap* entry, uint8_t base, int disp) { - if (entry->skeleton.prefix1 != 0) { - code_buffer_.push_back(entry->skeleton.prefix1); - if (entry->skeleton.prefix2 != 0) { - code_buffer_.push_back(entry->skeleton.prefix2); - } - } else { - DCHECK_EQ(0, entry->skeleton.prefix2); - } + EmitPrefix(entry); code_buffer_.push_back(entry->skeleton.opcode); + DCHECK_NE(0x0F, entry->skeleton.opcode); DCHECK_EQ(0, entry->skeleton.extra_opcode1); DCHECK_EQ(0, entry->skeleton.extra_opcode2); - DCHECK_LT(entry->skeleton.modrm_opcode, 8); - DCHECK_LT(base, 8); - uint8_t modrm = (ModrmForDisp(base, disp) << 6) | (entry->skeleton.modrm_opcode << 3) | base; - code_buffer_.push_back(modrm); - EmitDisp(base, disp); + DCHECK_NE(rX86_SP, base); + EmitModrmDisp(entry->skeleton.modrm_opcode, base, disp); + DCHECK_EQ(0, entry->skeleton.ax_opcode); + DCHECK_EQ(0, entry->skeleton.immediate_bytes); +} + +void X86Mir2Lir::EmitOpArray(const X86EncodingMap* entry, uint8_t base, uint8_t index, + int scale, int disp) { + EmitPrefixAndOpcode(entry); + EmitModrmSibDisp(entry->skeleton.modrm_opcode, base, index, scale, disp); DCHECK_EQ(0, entry->skeleton.ax_opcode); DCHECK_EQ(0, entry->skeleton.immediate_bytes); } void X86Mir2Lir::EmitMemReg(const X86EncodingMap* entry, uint8_t base, int disp, uint8_t reg) { - if (entry->skeleton.prefix1 != 0) { - code_buffer_.push_back(entry->skeleton.prefix1); - if (entry->skeleton.prefix2 != 0) { - code_buffer_.push_back(entry->skeleton.prefix2); - } - } else { - DCHECK_EQ(0, entry->skeleton.prefix2); - } - code_buffer_.push_back(entry->skeleton.opcode); - if (entry->skeleton.opcode == 0x0F) { - code_buffer_.push_back(entry->skeleton.extra_opcode1); - if (entry->skeleton.extra_opcode1 == 0x38 || entry->skeleton.extra_opcode1 == 0x3A) { - code_buffer_.push_back(entry->skeleton.extra_opcode2); - } else { - DCHECK_EQ(0, entry->skeleton.extra_opcode2); - } - } else { - DCHECK_EQ(0, entry->skeleton.extra_opcode1); - DCHECK_EQ(0, entry->skeleton.extra_opcode2); - } + EmitPrefixAndOpcode(entry); if (X86_FPREG(reg)) { reg = reg & X86_FP_REG_MASK; } @@ -631,15 +657,7 @@ void X86Mir2Lir::EmitMemReg(const X86EncodingMap* entry, << entry->name << " " << static_cast(reg) << " in " << PrettyMethod(cu_->method_idx, *cu_->dex_file); } - DCHECK_LT(reg, 8); - DCHECK_LT(base, 8); - uint8_t modrm = (ModrmForDisp(base, disp) << 6) | (reg << 3) | base; - code_buffer_.push_back(modrm); - if (base == rX86_SP) { - // Special SIB for SP base - code_buffer_.push_back(0 << 6 | (rX86_SP << 3) | rX86_SP); - } - EmitDisp(base, disp); + EmitModrmDisp(reg, base, disp); DCHECK_EQ(0, entry->skeleton.modrm_opcode); DCHECK_EQ(0, entry->skeleton.ax_opcode); DCHECK_EQ(0, entry->skeleton.immediate_bytes); @@ -652,39 +670,12 @@ void X86Mir2Lir::EmitRegMem(const X86EncodingMap* entry, } void X86Mir2Lir::EmitRegArray(const X86EncodingMap* entry, uint8_t reg, uint8_t base, uint8_t index, - int scale, int disp) { - if (entry->skeleton.prefix1 != 0) { - code_buffer_.push_back(entry->skeleton.prefix1); - if (entry->skeleton.prefix2 != 0) { - code_buffer_.push_back(entry->skeleton.prefix2); - } - } else { - DCHECK_EQ(0, entry->skeleton.prefix2); - } - code_buffer_.push_back(entry->skeleton.opcode); - if (entry->skeleton.opcode == 0x0F) { - code_buffer_.push_back(entry->skeleton.extra_opcode1); - if (entry->skeleton.extra_opcode1 == 0x38 || entry->skeleton.extra_opcode1 == 0x3A) { - code_buffer_.push_back(entry->skeleton.extra_opcode2); - } else { - DCHECK_EQ(0, entry->skeleton.extra_opcode2); - } - } else { - DCHECK_EQ(0, entry->skeleton.extra_opcode1); - DCHECK_EQ(0, entry->skeleton.extra_opcode2); - } + int scale, int disp) { + EmitPrefixAndOpcode(entry); if (X86_FPREG(reg)) { reg = reg & X86_FP_REG_MASK; } - DCHECK_LT(reg, 8); - uint8_t modrm = (ModrmForDisp(base, disp) << 6) | (reg << 3) | rX86_SP; - code_buffer_.push_back(modrm); - DCHECK_LT(scale, 4); - DCHECK_LT(index, 8); - DCHECK_LT(base, 8); - uint8_t sib = (scale << 6) | (index << 3) | base; - code_buffer_.push_back(sib); - EmitDisp(base, disp); + EmitModrmSibDisp(reg, base, index, scale, disp); DCHECK_EQ(0, entry->skeleton.modrm_opcode); DCHECK_EQ(0, entry->skeleton.ax_opcode); DCHECK_EQ(0, entry->skeleton.immediate_bytes); @@ -698,22 +689,7 @@ void X86Mir2Lir::EmitArrayReg(const X86EncodingMap* entry, uint8_t base, uint8_t void X86Mir2Lir::EmitRegThread(const X86EncodingMap* entry, uint8_t reg, int disp) { DCHECK_NE(entry->skeleton.prefix1, 0); - code_buffer_.push_back(entry->skeleton.prefix1); - if (entry->skeleton.prefix2 != 0) { - code_buffer_.push_back(entry->skeleton.prefix2); - } - code_buffer_.push_back(entry->skeleton.opcode); - if (entry->skeleton.opcode == 0x0F) { - code_buffer_.push_back(entry->skeleton.extra_opcode1); - if (entry->skeleton.extra_opcode1 == 0x38 || entry->skeleton.extra_opcode1 == 0x3A) { - code_buffer_.push_back(entry->skeleton.extra_opcode2); - } else { - DCHECK_EQ(0, entry->skeleton.extra_opcode2); - } - } else { - DCHECK_EQ(0, entry->skeleton.extra_opcode1); - DCHECK_EQ(0, entry->skeleton.extra_opcode2); - } + EmitPrefixAndOpcode(entry); if (X86_FPREG(reg)) { reg = reg & X86_FP_REG_MASK; } @@ -734,26 +710,7 @@ void X86Mir2Lir::EmitRegThread(const X86EncodingMap* entry, uint8_t reg, int dis } void X86Mir2Lir::EmitRegReg(const X86EncodingMap* entry, uint8_t reg1, uint8_t reg2) { - if (entry->skeleton.prefix1 != 0) { - code_buffer_.push_back(entry->skeleton.prefix1); - if (entry->skeleton.prefix2 != 0) { - code_buffer_.push_back(entry->skeleton.prefix2); - } - } else { - DCHECK_EQ(0, entry->skeleton.prefix2); - } - code_buffer_.push_back(entry->skeleton.opcode); - if (entry->skeleton.opcode == 0x0F) { - code_buffer_.push_back(entry->skeleton.extra_opcode1); - if (entry->skeleton.extra_opcode1 == 0x38 || entry->skeleton.extra_opcode1 == 0x3A) { - code_buffer_.push_back(entry->skeleton.extra_opcode2); - } else { - DCHECK_EQ(0, entry->skeleton.extra_opcode2); - } - } else { - DCHECK_EQ(0, entry->skeleton.extra_opcode1); - DCHECK_EQ(0, entry->skeleton.extra_opcode2); - } + EmitPrefixAndOpcode(entry); if (X86_FPREG(reg1)) { reg1 = reg1 & X86_FP_REG_MASK; } @@ -771,26 +728,7 @@ void X86Mir2Lir::EmitRegReg(const X86EncodingMap* entry, uint8_t reg1, uint8_t r void X86Mir2Lir::EmitRegRegImm(const X86EncodingMap* entry, uint8_t reg1, uint8_t reg2, int32_t imm) { - if (entry->skeleton.prefix1 != 0) { - code_buffer_.push_back(entry->skeleton.prefix1); - if (entry->skeleton.prefix2 != 0) { - code_buffer_.push_back(entry->skeleton.prefix2); - } - } else { - DCHECK_EQ(0, entry->skeleton.prefix2); - } - code_buffer_.push_back(entry->skeleton.opcode); - if (entry->skeleton.opcode == 0x0F) { - code_buffer_.push_back(entry->skeleton.extra_opcode1); - if (entry->skeleton.extra_opcode1 == 0x38 || entry->skeleton.extra_opcode1 == 0x3A) { - code_buffer_.push_back(entry->skeleton.extra_opcode2); - } else { - DCHECK_EQ(0, entry->skeleton.extra_opcode2); - } - } else { - DCHECK_EQ(0, entry->skeleton.extra_opcode1); - DCHECK_EQ(0, entry->skeleton.extra_opcode2); - } + EmitPrefixAndOpcode(entry); if (X86_FPREG(reg1)) { reg1 = reg1 & X86_FP_REG_MASK; } @@ -803,27 +741,7 @@ void X86Mir2Lir::EmitRegRegImm(const X86EncodingMap* entry, code_buffer_.push_back(modrm); DCHECK_EQ(0, entry->skeleton.modrm_opcode); DCHECK_EQ(0, entry->skeleton.ax_opcode); - switch (entry->skeleton.immediate_bytes) { - case 1: - DCHECK(IS_SIMM8(imm)); - code_buffer_.push_back(imm & 0xFF); - break; - case 2: - DCHECK(IS_SIMM16(imm)); - code_buffer_.push_back(imm & 0xFF); - code_buffer_.push_back((imm >> 8) & 0xFF); - break; - case 4: - code_buffer_.push_back(imm & 0xFF); - code_buffer_.push_back((imm >> 8) & 0xFF); - code_buffer_.push_back((imm >> 16) & 0xFF); - code_buffer_.push_back((imm >> 24) & 0xFF); - break; - default: - LOG(FATAL) << "Unexpected immediate bytes (" << entry->skeleton.immediate_bytes - << ") for instruction: " << entry->name; - break; - } + EmitImm(entry, imm); } void X86Mir2Lir::EmitRegImm(const X86EncodingMap* entry, uint8_t reg, int imm) { @@ -838,95 +756,25 @@ void X86Mir2Lir::EmitRegImm(const X86EncodingMap* entry, uint8_t reg, int imm) { if (reg == rAX && entry->skeleton.ax_opcode != 0) { code_buffer_.push_back(entry->skeleton.ax_opcode); } else { - code_buffer_.push_back(entry->skeleton.opcode); - if (entry->skeleton.opcode == 0x0F) { - code_buffer_.push_back(entry->skeleton.extra_opcode1); - if (entry->skeleton.extra_opcode1 == 0x38 || entry->skeleton.extra_opcode1 == 0x3A) { - code_buffer_.push_back(entry->skeleton.extra_opcode2); - } else { - DCHECK_EQ(0, entry->skeleton.extra_opcode2); - } - } else { - DCHECK_EQ(0, entry->skeleton.extra_opcode1); - DCHECK_EQ(0, entry->skeleton.extra_opcode2); - } + EmitOpcode(entry); if (X86_FPREG(reg)) { reg = reg & X86_FP_REG_MASK; } uint8_t modrm = (3 << 6) | (entry->skeleton.modrm_opcode << 3) | reg; code_buffer_.push_back(modrm); } - switch (entry->skeleton.immediate_bytes) { - case 1: - DCHECK(IS_SIMM8(imm)); - code_buffer_.push_back(imm & 0xFF); - break; - case 2: - DCHECK(IS_SIMM16(imm)); - code_buffer_.push_back(imm & 0xFF); - code_buffer_.push_back((imm >> 8) & 0xFF); - break; - case 4: - code_buffer_.push_back(imm & 0xFF); - code_buffer_.push_back((imm >> 8) & 0xFF); - code_buffer_.push_back((imm >> 16) & 0xFF); - code_buffer_.push_back((imm >> 24) & 0xFF); - break; - default: - LOG(FATAL) << "Unexpected immediate bytes (" << entry->skeleton.immediate_bytes - << ") for instruction: " << entry->name; - break; - } + EmitImm(entry, imm); } void X86Mir2Lir::EmitThreadImm(const X86EncodingMap* entry, int disp, int imm) { - if (entry->skeleton.prefix1 != 0) { - code_buffer_.push_back(entry->skeleton.prefix1); - if (entry->skeleton.prefix2 != 0) { - code_buffer_.push_back(entry->skeleton.prefix2); - } - } else { - DCHECK_EQ(0, entry->skeleton.prefix2); - } - code_buffer_.push_back(entry->skeleton.opcode); - if (entry->skeleton.opcode == 0x0F) { - code_buffer_.push_back(entry->skeleton.extra_opcode1); - if (entry->skeleton.extra_opcode1 == 0x38 || entry->skeleton.extra_opcode1 == 0x3A) { - code_buffer_.push_back(entry->skeleton.extra_opcode2); - } else { - DCHECK_EQ(0, entry->skeleton.extra_opcode2); - } - } else { - DCHECK_EQ(0, entry->skeleton.extra_opcode1); - DCHECK_EQ(0, entry->skeleton.extra_opcode2); - } + EmitPrefixAndOpcode(entry); uint8_t modrm = (0 << 6) | (entry->skeleton.modrm_opcode << 3) | rBP; code_buffer_.push_back(modrm); code_buffer_.push_back(disp & 0xFF); code_buffer_.push_back((disp >> 8) & 0xFF); code_buffer_.push_back((disp >> 16) & 0xFF); code_buffer_.push_back((disp >> 24) & 0xFF); - switch (entry->skeleton.immediate_bytes) { - case 1: - DCHECK(IS_SIMM8(imm)); - code_buffer_.push_back(imm & 0xFF); - break; - case 2: - DCHECK(IS_SIMM16(imm)); - code_buffer_.push_back(imm & 0xFF); - code_buffer_.push_back((imm >> 8) & 0xFF); - break; - case 4: - code_buffer_.push_back(imm & 0xFF); - code_buffer_.push_back((imm >> 8) & 0xFF); - code_buffer_.push_back((imm >> 16) & 0xFF); - code_buffer_.push_back((imm >> 24) & 0xFF); - break; - default: - LOG(FATAL) << "Unexpected immediate bytes (" << entry->skeleton.immediate_bytes - << ") for instruction: " << entry->name; - break; - } + EmitImm(entry, imm); DCHECK_EQ(entry->skeleton.ax_opcode, 0); } @@ -940,31 +788,16 @@ void X86Mir2Lir::EmitMovRegImm(const X86EncodingMap* entry, uint8_t reg, int imm } void X86Mir2Lir::EmitShiftRegImm(const X86EncodingMap* entry, uint8_t reg, int imm) { - if (entry->skeleton.prefix1 != 0) { - code_buffer_.push_back(entry->skeleton.prefix1); - if (entry->skeleton.prefix2 != 0) { - code_buffer_.push_back(entry->skeleton.prefix2); - } - } else { - DCHECK_EQ(0, entry->skeleton.prefix2); - } + EmitPrefix(entry); if (imm != 1) { code_buffer_.push_back(entry->skeleton.opcode); } else { // Shorter encoding for 1 bit shift code_buffer_.push_back(entry->skeleton.ax_opcode); } - if (entry->skeleton.opcode == 0x0F) { - code_buffer_.push_back(entry->skeleton.extra_opcode1); - if (entry->skeleton.extra_opcode1 == 0x38 || entry->skeleton.extra_opcode1 == 0x3A) { - code_buffer_.push_back(entry->skeleton.extra_opcode2); - } else { - DCHECK_EQ(0, entry->skeleton.extra_opcode2); - } - } else { - DCHECK_EQ(0, entry->skeleton.extra_opcode1); - DCHECK_EQ(0, entry->skeleton.extra_opcode2); - } + DCHECK_NE(0x0F, entry->skeleton.opcode); + DCHECK_EQ(0, entry->skeleton.extra_opcode1); + DCHECK_EQ(0, entry->skeleton.extra_opcode2); if (reg >= 4) { DCHECK(strchr(entry->name, '8') == NULL) << entry->name << " " << static_cast(reg) << " in " << PrettyMethod(cu_->method_idx, *cu_->dex_file); @@ -981,15 +814,9 @@ void X86Mir2Lir::EmitShiftRegImm(const X86EncodingMap* entry, uint8_t reg, int i void X86Mir2Lir::EmitShiftRegCl(const X86EncodingMap* entry, uint8_t reg, uint8_t cl) { DCHECK_EQ(cl, static_cast(rCX)); - if (entry->skeleton.prefix1 != 0) { - code_buffer_.push_back(entry->skeleton.prefix1); - if (entry->skeleton.prefix2 != 0) { - code_buffer_.push_back(entry->skeleton.prefix2); - } - } else { - DCHECK_EQ(0, entry->skeleton.prefix2); - } + EmitPrefix(entry); code_buffer_.push_back(entry->skeleton.opcode); + DCHECK_NE(0x0F, entry->skeleton.opcode); DCHECK_EQ(0, entry->skeleton.extra_opcode1); DCHECK_EQ(0, entry->skeleton.extra_opcode2); DCHECK_LT(reg, 8); @@ -1059,55 +886,15 @@ void X86Mir2Lir::EmitJcc(const X86EncodingMap* entry, int rel, uint8_t cc) { } void X86Mir2Lir::EmitCallMem(const X86EncodingMap* entry, uint8_t base, int disp) { - if (entry->skeleton.prefix1 != 0) { - code_buffer_.push_back(entry->skeleton.prefix1); - if (entry->skeleton.prefix2 != 0) { - code_buffer_.push_back(entry->skeleton.prefix2); - } - } else { - DCHECK_EQ(0, entry->skeleton.prefix2); - } - code_buffer_.push_back(entry->skeleton.opcode); - if (entry->skeleton.opcode == 0x0F) { - code_buffer_.push_back(entry->skeleton.extra_opcode1); - if (entry->skeleton.extra_opcode1 == 0x38 || entry->skeleton.extra_opcode1 == 0x3A) { - code_buffer_.push_back(entry->skeleton.extra_opcode2); - } else { - DCHECK_EQ(0, entry->skeleton.extra_opcode2); - } - } else { - DCHECK_EQ(0, entry->skeleton.extra_opcode1); - DCHECK_EQ(0, entry->skeleton.extra_opcode2); - } - uint8_t modrm = (ModrmForDisp(base, disp) << 6) | (entry->skeleton.modrm_opcode << 3) | base; - code_buffer_.push_back(modrm); - if (base == rX86_SP) { - // Special SIB for SP base - code_buffer_.push_back(0 << 6 | (rX86_SP << 3) | rX86_SP); - } - EmitDisp(base, disp); + EmitPrefixAndOpcode(entry); + EmitModrmDisp(entry->skeleton.modrm_opcode, base, disp); DCHECK_EQ(0, entry->skeleton.ax_opcode); DCHECK_EQ(0, entry->skeleton.immediate_bytes); } void X86Mir2Lir::EmitCallThread(const X86EncodingMap* entry, int disp) { DCHECK_NE(entry->skeleton.prefix1, 0); - code_buffer_.push_back(entry->skeleton.prefix1); - if (entry->skeleton.prefix2 != 0) { - code_buffer_.push_back(entry->skeleton.prefix2); - } - code_buffer_.push_back(entry->skeleton.opcode); - if (entry->skeleton.opcode == 0x0F) { - code_buffer_.push_back(entry->skeleton.extra_opcode1); - if (entry->skeleton.extra_opcode1 == 0x38 || entry->skeleton.extra_opcode1 == 0x3A) { - code_buffer_.push_back(entry->skeleton.extra_opcode2); - } else { - DCHECK_EQ(0, entry->skeleton.extra_opcode2); - } - } else { - DCHECK_EQ(0, entry->skeleton.extra_opcode1); - DCHECK_EQ(0, entry->skeleton.extra_opcode2); - } + EmitPrefixAndOpcode(entry); uint8_t modrm = (0 << 6) | (entry->skeleton.modrm_opcode << 3) | rBP; code_buffer_.push_back(modrm); code_buffer_.push_back(disp & 0xFF); @@ -1131,20 +918,14 @@ void X86Mir2Lir::EmitPcRel(const X86EncodingMap* entry, uint8_t reg, reinterpret_cast(UnwrapPointer(base_or_table)); disp = tab_rec->offset; } - if (entry->skeleton.prefix1 != 0) { - code_buffer_.push_back(entry->skeleton.prefix1); - if (entry->skeleton.prefix2 != 0) { - code_buffer_.push_back(entry->skeleton.prefix2); - } - } else { - DCHECK_EQ(0, entry->skeleton.prefix2); - } + EmitPrefix(entry); if (X86_FPREG(reg)) { reg = reg & X86_FP_REG_MASK; } DCHECK_LT(reg, 8); if (entry->opcode == kX86PcRelLoadRA) { code_buffer_.push_back(entry->skeleton.opcode); + DCHECK_NE(0x0F, entry->skeleton.opcode); DCHECK_EQ(0, entry->skeleton.extra_opcode1); DCHECK_EQ(0, entry->skeleton.extra_opcode2); uint8_t modrm = (2 << 6) | (reg << 3) | rX86_SP; @@ -1320,15 +1101,7 @@ AssemblerStatus X86Mir2Lir::AssembleInstructions(CodeOffset start_addr) { case kNullary: // 1 byte of opcode DCHECK_EQ(0, entry->skeleton.prefix1); DCHECK_EQ(0, entry->skeleton.prefix2); - code_buffer_.push_back(entry->skeleton.opcode); - if (entry->skeleton.extra_opcode1 != 0) { - code_buffer_.push_back(entry->skeleton.extra_opcode1); - if (entry->skeleton.extra_opcode2 != 0) { - code_buffer_.push_back(entry->skeleton.extra_opcode2); - } - } else { - DCHECK_EQ(0, entry->skeleton.extra_opcode2); - } + EmitOpcode(entry); DCHECK_EQ(0, entry->skeleton.modrm_opcode); DCHECK_EQ(0, entry->skeleton.ax_opcode); DCHECK_EQ(0, entry->skeleton.immediate_bytes); @@ -1342,6 +1115,9 @@ AssemblerStatus X86Mir2Lir::AssembleInstructions(CodeOffset start_addr) { case kMem: // lir operands - 0: base, 1: disp EmitOpMem(entry, lir->operands[0], lir->operands[1]); break; + case kArray: // lir operands - 0: base, 1: index, 2: scale, 3: disp + EmitOpArray(entry, lir->operands[0], lir->operands[1], lir->operands[2], lir->operands[3]); + break; case kMemReg: // lir operands - 0: base, 1: disp, 2: reg EmitMemReg(entry, lir->operands[0], lir->operands[1], lir->operands[2]); break; diff --git a/compiler/dex/quick/x86/codegen_x86.h b/compiler/dex/quick/x86/codegen_x86.h index ffe2d67cf00..765a26c8a08 100644 --- a/compiler/dex/quick/x86/codegen_x86.h +++ b/compiler/dex/quick/x86/codegen_x86.h @@ -171,10 +171,18 @@ class X86Mir2Lir : public Mir2Lir { bool InexpensiveConstantDouble(int64_t value); private: - void EmitDisp(int base, int disp); + void EmitPrefix(const X86EncodingMap* entry); + void EmitOpcode(const X86EncodingMap* entry); + void EmitPrefixAndOpcode(const X86EncodingMap* entry); + void EmitDisp(uint8_t base, int disp); + void EmitModrmDisp(uint8_t reg_or_opcode, uint8_t base, int disp); + void EmitModrmSibDisp(uint8_t reg_or_opcode, uint8_t base, uint8_t index, int scale, int disp); + void EmitImm(const X86EncodingMap* entry, int imm); void EmitOpRegOpcode(const X86EncodingMap* entry, uint8_t reg); void EmitOpReg(const X86EncodingMap* entry, uint8_t reg); void EmitOpMem(const X86EncodingMap* entry, uint8_t base, int disp); + void EmitOpArray(const X86EncodingMap* entry, uint8_t base, uint8_t index, + int scale, int disp); void EmitMemReg(const X86EncodingMap* entry, uint8_t base, int disp, uint8_t reg); void EmitRegMem(const X86EncodingMap* entry, uint8_t reg, uint8_t base, int disp); void EmitRegArray(const X86EncodingMap* entry, uint8_t reg, uint8_t base, uint8_t index, From 70b797d998f2a28e39f7d6ffc8a07c9cbc47da14 Mon Sep 17 00:00:00 2001 From: Vladimir Marko Date: Tue, 3 Dec 2013 15:25:24 +0000 Subject: [PATCH 0244/2402] Unsafe.compareAndSwapLong() intrinsic for x86. Change-Id: Idbc5371a62dfdd84485a657d4548990519200205 --- compiler/dex/compiler_enums.h | 1 + compiler/dex/quick/mir_to_lir.h | 1 + compiler/dex/quick/x86/assemble_x86.cc | 6 +++++- compiler/dex/quick/x86/int_x86.cc | 21 ++++++++++++++++++- compiler/dex/quick/x86/target_x86.cc | 4 ++++ .../quick/x86/x86_dex_file_method_inliner.cc | 4 ++-- compiler/dex/quick/x86/x86_lir.h | 2 ++ disassembler/disassembler_x86.cc | 7 +++++++ 8 files changed, 42 insertions(+), 4 deletions(-) diff --git a/compiler/dex/compiler_enums.h b/compiler/dex/compiler_enums.h index 56facfd8892..d73f148dcf0 100644 --- a/compiler/dex/compiler_enums.h +++ b/compiler/dex/compiler_enums.h @@ -374,6 +374,7 @@ enum OpFeatureFlags { kRegUseA, kRegUseC, kRegUseD, + kRegUseB, kRegUseFPCSList0, kRegUseFPCSList2, kRegUseList0, diff --git a/compiler/dex/quick/mir_to_lir.h b/compiler/dex/quick/mir_to_lir.h index 92e21ffdf76..9d35667b512 100644 --- a/compiler/dex/quick/mir_to_lir.h +++ b/compiler/dex/quick/mir_to_lir.h @@ -71,6 +71,7 @@ typedef uint32_t CodeOffset; // Native code offset in bytes. #define REG_USEA (1ULL << kRegUseA) #define REG_USEC (1ULL << kRegUseC) #define REG_USED (1ULL << kRegUseD) +#define REG_USEB (1ULL << kRegUseB) #define REG_USE_FPCS_LIST0 (1ULL << kRegUseFPCSList0) #define REG_USE_FPCS_LIST2 (1ULL << kRegUseFPCSList2) #define REG_USE_LIST0 (1ULL << kRegUseList0) diff --git a/compiler/dex/quick/x86/assemble_x86.cc b/compiler/dex/quick/x86/assemble_x86.cc index 0b7b2bd94f5..96dc6ee7d16 100644 --- a/compiler/dex/quick/x86/assemble_x86.cc +++ b/compiler/dex/quick/x86/assemble_x86.cc @@ -246,7 +246,9 @@ ENCODING_MAP(Cmp, IS_LOAD, 0, 0, UNARY_ENCODING_MAP(Idivmod, 0x7, 0, SETS_CCODES, DaR, kRegRegReg, IS_UNARY_OP | REG_USE0, DaM, kRegRegMem, IS_BINARY_OP | REG_USE0, DaA, kRegRegArray, IS_QUAD_OP | REG_USE01, 0, REG_DEFA_USEA, REG_DEFAD_USEAD, REG_DEFAD_USEAD, "ah:al,ax,", "dx:ax,dx:ax,", "edx:eax,edx:eax,"), #undef UNARY_ENCODING_MAP - { kX86Bswap32R, kRegOpcode, IS_UNARY_OP | REG_DEF0_USE0, { 0, 0, 0x0F, 0xC8, 0, 0, 0, 0 }, "Bswap32R", "!0r" }, + { kX86Bswap32R, kRegOpcode, IS_UNARY_OP | REG_DEF0_USE0, { 0, 0, 0x0F, 0xC8, 0, 0, 0, 0 }, "Bswap32R", "!0r" }, + { kX86Push32R, kRegOpcode, IS_UNARY_OP | REG_USE0 | IS_STORE, { 0, 0, 0x50, 0, 0, 0, 0, 0 }, "Push32R", "!0r" }, + { kX86Pop32R, kRegOpcode, IS_UNARY_OP | REG_DEF0 | IS_LOAD, { 0, 0, 0x58, 0, 0, 0, 0, 0 }, "Pop32R", "!0r" }, #define EXT_0F_ENCODING_MAP(opname, prefix, opcode, reg_def) \ { kX86 ## opname ## RR, kRegReg, IS_BINARY_OP | reg_def | REG_USE01, { prefix, 0, 0x0F, opcode, 0, 0, 0, 0 }, #opname "RR", "!0r,!1r" }, \ @@ -308,6 +310,8 @@ ENCODING_MAP(Cmp, IS_LOAD, 0, 0, { kX86CmpxchgAR, kArrayReg, IS_STORE | IS_QUIN_OP | REG_USE014 | REG_DEFA_USEA | SETS_CCODES, { 0, 0, 0x0F, 0xB1, 0, 0, 0, 0 }, "Cmpxchg", "[!0r+!1r<args[7] if (is_long) { - LOG(FATAL) << "CAS64: Not implemented"; + FlushAllRegs(); + LockCallTemps(); + NewLIR1(kX86Push32R, rDI); + MarkTemp(rDI); + LockTemp(rDI); + NewLIR1(kX86Push32R, rSI); + MarkTemp(rSI); + LockTemp(rSI); + LoadValueDirectFixed(rl_src_obj, rDI); + LoadValueDirectFixed(rl_src_offset, rSI); + LoadValueDirectWideFixed(rl_src_expected, rAX, rDX); + LoadValueDirectWideFixed(rl_src_new_value, rBX, rCX); + NewLIR4(kX86LockCmpxchg8bA, rDI, rSI, 0, 0); + FreeTemp(rSI); + UnmarkTemp(rSI); + NewLIR1(kX86Pop32R, rSI); + FreeTemp(rDI); + UnmarkTemp(rDI); + NewLIR1(kX86Pop32R, rDI); + FreeCallTemps(); } else { // EAX must hold expected for CMPXCHG. Neither rl_new_value, nor r_ptr may be in EAX. FlushReg(r0); diff --git a/compiler/dex/quick/x86/target_x86.cc b/compiler/dex/quick/x86/target_x86.cc index 878fa769b6b..b7a607fa409 100644 --- a/compiler/dex/quick/x86/target_x86.cc +++ b/compiler/dex/quick/x86/target_x86.cc @@ -165,6 +165,10 @@ void X86Mir2Lir::SetupTargetResourceMasks(LIR* lir, uint64_t flags) { if (flags & REG_USED) { SetupRegMask(&lir->u.m.use_mask, rDX); } + + if (flags & REG_USEB) { + SetupRegMask(&lir->u.m.use_mask, rBX); + } } /* For dumping instructions */ diff --git a/compiler/dex/quick/x86/x86_dex_file_method_inliner.cc b/compiler/dex/quick/x86/x86_dex_file_method_inliner.cc index f0e76c9e5e3..88e933f868c 100644 --- a/compiler/dex/quick/x86/x86_dex_file_method_inliner.cc +++ b/compiler/dex/quick/x86/x86_dex_file_method_inliner.cc @@ -66,8 +66,8 @@ const DexFileMethodInliner::IntrinsicDef X86DexFileMethodInliner::kIntrinsicMeth INTRINSIC(SunMiscUnsafe, CompareAndSwapInt, ObjectJII_Z, kIntrinsicCas, kIntrinsicFlagNone), - // INTRINSIC(SunMiscUnsafe, CompareAndSwapLong, ObjectJJJ_Z, kIntrinsicCas, - // kIntrinsicFlagIsLong), + INTRINSIC(SunMiscUnsafe, CompareAndSwapLong, ObjectJJJ_Z, kIntrinsicCas, + kIntrinsicFlagIsLong), INTRINSIC(SunMiscUnsafe, CompareAndSwapObject, ObjectJObjectObject_Z, kIntrinsicCas, kIntrinsicFlagIsObject), diff --git a/compiler/dex/quick/x86/x86_lir.h b/compiler/dex/quick/x86/x86_lir.h index bbcae92f7de..5fe76fe2f9c 100644 --- a/compiler/dex/quick/x86/x86_lir.h +++ b/compiler/dex/quick/x86/x86_lir.h @@ -314,6 +314,7 @@ enum X86OpCode { UnaryOpcode(kX86Divmod, DaR, DaM, DaA), UnaryOpcode(kX86Idivmod, DaR, DaM, DaA), kX86Bswap32R, + kX86Push32R, kX86Pop32R, #undef UnaryOpcode #define Binary0fOpCode(opcode) \ opcode ## RR, opcode ## RM, opcode ## RA @@ -355,6 +356,7 @@ enum X86OpCode { Binary0fOpCode(kX86Imul32), // 32bit multiply kX86CmpxchgRR, kX86CmpxchgMR, kX86CmpxchgAR, // compare and exchange kX86LockCmpxchgMR, kX86LockCmpxchgAR, // locked compare and exchange + kX86LockCmpxchg8bM, kX86LockCmpxchg8bA, // locked compare and exchange Binary0fOpCode(kX86Movzx8), // zero-extend 8-bit value Binary0fOpCode(kX86Movzx16), // zero-extend 16-bit value Binary0fOpCode(kX86Movsx8), // sign-extend 8-bit value diff --git a/disassembler/disassembler_x86.cc b/disassembler/disassembler_x86.cc index 9ed65cd80a5..8781c7a2749 100644 --- a/disassembler/disassembler_x86.cc +++ b/disassembler/disassembler_x86.cc @@ -520,6 +520,13 @@ DISASSEMBLER_ENTRY(cmp, case 0xB7: opcode << "movzxw"; has_modrm = true; load = true; break; case 0xBE: opcode << "movsxb"; has_modrm = true; load = true; break; case 0xBF: opcode << "movsxw"; has_modrm = true; load = true; break; + case 0xC7: + static const char* x0FxC7_opcodes[] = { "unknown-0f-c7", "cmpxchg8b", "unknown-0f-c7", "unknown-0f-c7", "unknown-0f-c7", "unknown-0f-c7", "unknown-0f-c7", "unknown-0f-c7" }; + modrm_opcodes = x0FxC7_opcodes; + has_modrm = true; + reg_is_opcode = true; + store = true; + break; case 0xC8: case 0xC9: case 0xCA: case 0xCB: case 0xCC: case 0xCD: case 0xCE: case 0xCF: opcode << "bswap"; reg_in_opcode = true; From 85db144fbb79582b962344ca80068d91fac6e046 Mon Sep 17 00:00:00 2001 From: Mathieu Chartier Date: Tue, 3 Dec 2013 10:51:00 -0800 Subject: [PATCH 0245/2402] Fix incorrect IsHeapAddress in jni entrypoints. The behaviour of IsHeapAddress had changed in the compaction CL to not include nullptr as a valid heap address. Bug: 11982013 Change-Id: I2847f71ca6775210ea4de8ede8b9ee8c14c55228 --- runtime/entrypoints/jni/jni_entrypoints.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/entrypoints/jni/jni_entrypoints.cc b/runtime/entrypoints/jni/jni_entrypoints.cc index 16364fcdb70..4d1e5311e6f 100644 --- a/runtime/entrypoints/jni/jni_entrypoints.cc +++ b/runtime/entrypoints/jni/jni_entrypoints.cc @@ -50,7 +50,7 @@ static void WorkAroundJniBugsForJobject(intptr_t* arg_ptr) { intptr_t value = *arg_ptr; mirror::Object** value_as_jni_rep = reinterpret_cast(value); mirror::Object* value_as_work_around_rep = value_as_jni_rep != NULL ? *value_as_jni_rep : NULL; - CHECK(Runtime::Current()->GetHeap()->IsHeapAddress(value_as_work_around_rep)) + CHECK(Runtime::Current()->GetHeap()->IsValidObjectAddress(value_as_work_around_rep)) << value_as_work_around_rep; *arg_ptr = reinterpret_cast(value_as_work_around_rep); } From 228d6b8a4f0a21c1e9b2372c3104ce4ee19f65b4 Mon Sep 17 00:00:00 2001 From: Jeff Hao Date: Tue, 3 Dec 2013 15:00:05 -0800 Subject: [PATCH 0246/2402] Fix stack trace for proxy methods and added test case. Bug: 11861564 Change-Id: I9513359ff9e5d345ee71d84388afed02bd114ecf --- runtime/thread.cc | 44 ++++++++++++++++++------------- test/044-proxy/expected.txt | 5 ++-- test/044-proxy/src/BasicTest.java | 22 +++++++++++++++- 3 files changed, 50 insertions(+), 21 deletions(-) diff --git a/runtime/thread.cc b/runtime/thread.cc index 1add5077dcb..d816ca1ab51 100644 --- a/runtime/thread.cc +++ b/runtime/thread.cc @@ -1281,7 +1281,7 @@ class BuildInternalStackTraceVisitor : public StackVisitor { return true; // Ignore runtime frames (in particular callee save). } method_trace_->Set(count_, m); - dex_pc_trace_->Set(count_, GetDexPc()); + dex_pc_trace_->Set(count_, m->IsProxyMethod() ? DexFile::kDexNoIndex : GetDexPc()); ++count_; return true; } @@ -1363,19 +1363,31 @@ jobjectArray Thread::InternalStackTraceToStackTraceElementArray(JNIEnv* env, job // Prepare parameters for StackTraceElement(String cls, String method, String file, int line) mirror::ArtMethod* method = down_cast(method_trace->Get(i)); MethodHelper mh(method); - mirror::IntArray* pc_trace = down_cast(method_trace->Get(depth)); - uint32_t dex_pc = pc_trace->Get(i); - int32_t line_number = mh.GetLineNumFromDexPC(dex_pc); - // Allocate element, potentially triggering GC - // TODO: reuse class_name_object via Class::name_? - const char* descriptor = mh.GetDeclaringClassDescriptor(); - CHECK(descriptor != NULL); - std::string class_name(PrettyDescriptor(descriptor)); - SirtRef class_name_object(soa.Self(), - mirror::String::AllocFromModifiedUtf8(soa.Self(), - class_name.c_str())); - if (class_name_object.get() == NULL) { - return NULL; + int32_t line_number; + SirtRef class_name_object(soa.Self(), NULL); + SirtRef source_name_object(soa.Self(), NULL); + if (method->IsProxyMethod()) { + line_number = -1; + class_name_object.reset(method->GetDeclaringClass()->GetName()); + // source_name_object intentionally left null for proxy methods + } else { + mirror::IntArray* pc_trace = down_cast(method_trace->Get(depth)); + uint32_t dex_pc = pc_trace->Get(i); + line_number = mh.GetLineNumFromDexPC(dex_pc); + // Allocate element, potentially triggering GC + // TODO: reuse class_name_object via Class::name_? + const char* descriptor = mh.GetDeclaringClassDescriptor(); + CHECK(descriptor != NULL); + std::string class_name(PrettyDescriptor(descriptor)); + class_name_object.reset(mirror::String::AllocFromModifiedUtf8(soa.Self(), class_name.c_str())); + if (class_name_object.get() == NULL) { + return NULL; + } + const char* source_file = mh.GetDeclaringClassSourceFile(); + source_name_object.reset(mirror::String::AllocFromModifiedUtf8(soa.Self(), source_file)); + if (source_name_object.get() == NULL) { + return NULL; + } } const char* method_name = mh.GetName(); CHECK(method_name != NULL); @@ -1385,10 +1397,6 @@ jobjectArray Thread::InternalStackTraceToStackTraceElementArray(JNIEnv* env, job if (method_name_object.get() == NULL) { return NULL; } - const char* source_file = mh.GetDeclaringClassSourceFile(); - SirtRef source_name_object(soa.Self(), - mirror::String::AllocFromModifiedUtf8(soa.Self(), - source_file)); mirror::StackTraceElement* obj = mirror::StackTraceElement::Alloc( soa.Self(), class_name_object, method_name_object, source_name_object, line_number); if (obj == NULL) { diff --git a/test/044-proxy/expected.txt b/test/044-proxy/expected.txt index 13e3a287c44..12df250b953 100644 --- a/test/044-proxy/expected.txt +++ b/test/044-proxy/expected.txt @@ -42,6 +42,7 @@ Invoke public abstract java.lang.String Shapes.blob() (no args) --- blob Success: method blob res=mix +$Proxy1.getTrace null:-1 Invoke public abstract void Shapes.upChuck() (no args) Got expected ioobe @@ -49,8 +50,8 @@ Invoke public abstract void Shapes.upCheck() throws java.lang.InterruptedExcepti (no args) Got expected ie -Proxy interfaces: [interface Quads, interface Colors] -Proxy methods: [public final java.lang.String $Proxy1.blob(), public final double $Proxy1.blue(int), public final R0a $Proxy1.checkMe(), public final R0aa $Proxy1.checkMe(), public final R0base $Proxy1.checkMe(), public final void $Proxy1.circle(int), public final boolean $Proxy1.equals(java.lang.Object), public final int $Proxy1.green(double), public final int $Proxy1.hashCode(), public final int $Proxy1.mauve(java.lang.String), public final int $Proxy1.rectangle(int,int), public final int $Proxy1.red(float), public final int $Proxy1.square(int,int), public final java.lang.String $Proxy1.toString(), public final int $Proxy1.trapezoid(int,double,int), public final void $Proxy1.upCheck() throws java.lang.InterruptedException, public final void $Proxy1.upChuck()] +Proxy interfaces: [interface Quads, interface Colors, interface Trace] +Proxy methods: [public final java.lang.String $Proxy1.blob(), public final double $Proxy1.blue(int), public final R0a $Proxy1.checkMe(), public final R0aa $Proxy1.checkMe(), public final R0base $Proxy1.checkMe(), public final void $Proxy1.circle(int), public final boolean $Proxy1.equals(java.lang.Object), public final void $Proxy1.getTrace(), public final int $Proxy1.green(double), public final int $Proxy1.hashCode(), public final int $Proxy1.mauve(java.lang.String), public final int $Proxy1.rectangle(int,int), public final int $Proxy1.red(float), public final int $Proxy1.square(int,int), public final java.lang.String $Proxy1.toString(), public final int $Proxy1.trapezoid(int,double,int), public final void $Proxy1.upCheck() throws java.lang.InterruptedException, public final void $Proxy1.upChuck()] Decl annos: [] Param annos (0) : [] Dupe threw expected exception diff --git a/test/044-proxy/src/BasicTest.java b/test/044-proxy/src/BasicTest.java index 46aa3feea28..ea46f49f2b4 100644 --- a/test/044-proxy/src/BasicTest.java +++ b/test/044-proxy/src/BasicTest.java @@ -51,6 +51,8 @@ public static void main(String[] args) { colors.blue(777); colors.mauve("sorry"); colors.blob(); + Trace trace = (Trace) proxy; + trace.getTrace(); try { shapes.upChuck(); @@ -96,7 +98,7 @@ static Object createProxy(Object proxyMe) { /* create the proxy class */ Class proxyClass = Proxy.getProxyClass(Shapes.class.getClassLoader(), - new Class[] { Quads.class, Colors.class }); + new Class[] { Quads.class, Colors.class, Trace.class }); /* create a proxy object, passing the handler object in */ Object proxy = null; @@ -156,6 +158,10 @@ interface Colors { public R0aa checkMe(); } +interface Trace { + public void getTrace(); +} + /* * Some return types. */ @@ -248,6 +254,20 @@ else if (method.getName().equals("equals")) throw new RuntimeException("huh?"); } + if (method.getDeclaringClass() == Trace.class) { + if (method.getName().equals("getTrace")) { + StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace(); + for (int i = 0; i < stackTrace.length; i++) { + StackTraceElement ste = stackTrace[i]; + if (ste.getMethodName().equals("getTrace")) { + System.out.println(ste.getClassName() + "." + ste.getMethodName() + " " + + ste.getFileName() + ":" + ste.getLineNumber()); + } + } + return null; + } + } + System.out.println("Invoke " + method); if (args == null || args.length == 0) { System.out.println(" (no args)"); From 501668ae6b3c1546cd94cbce71bc2e9eb0d851b8 Mon Sep 17 00:00:00 2001 From: Hiroshi Yamauchi Date: Wed, 4 Dec 2013 17:43:52 -0800 Subject: [PATCH 0247/2402] Fix a rosalloc check failure in a test. Bug: 11884037 Change-Id: I44356bbb0b4406ac289d59f4fd29878452087c03 --- runtime/runtime.cc | 5 ----- 1 file changed, 5 deletions(-) diff --git a/runtime/runtime.cc b/runtime/runtime.cc index 6bd256057d5..4048bd32fd2 100644 --- a/runtime/runtime.cc +++ b/runtime/runtime.cc @@ -131,11 +131,6 @@ Runtime::~Runtime() { heap_->WaitForGcToComplete(self); heap_->DeleteThreadPool(); - // For RosAlloc, revoke thread local runs. Note that in tests - // (common_test.h) we repeat allocating and deleting Runtime - // objects. - heap_->RevokeAllThreadLocalBuffers(); - // Make sure our internal threads are dead before we start tearing down things they're using. Dbg::StopJdwp(); delete signal_catcher_; From 123756a041baf8421ed933312605daa5ef082f6f Mon Sep 17 00:00:00 2001 From: Sebastien Hertz Date: Wed, 27 Nov 2013 15:49:42 +0100 Subject: [PATCH 0248/2402] Fix JDWP class-only modifier. Fix Dbg::MatchType which is used for class-only and exception-only modifiers. Also fix crash happening when notifying an exception. A debugger may walk the stack of the current thread so we need to keep the instrumentation stack frame synced with the native stack. Bug: 11856587 Change-Id: Ibf95f8a83ce9ee640abf945e498b42cc88ea92a0 --- runtime/debugger.cc | 3 ++- runtime/stack.cc | 1 + runtime/thread.cc | 11 +++++++---- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/runtime/debugger.cc b/runtime/debugger.cc index 52a2141a0ba..e4b8a8a3358 100644 --- a/runtime/debugger.cc +++ b/runtime/debugger.cc @@ -452,6 +452,7 @@ void Dbg::StartJdwp() { void Dbg::StopJdwp() { delete gJdwpState; + gJdwpState = NULL; delete gRegistry; gRegistry = NULL; } @@ -1113,7 +1114,7 @@ bool Dbg::MatchType(JDWP::RefTypeId instance_class_id, JDWP::RefTypeId class_id) CHECK(c1 != NULL); mirror::Class* c2 = DecodeClass(class_id, status); CHECK(c2 != NULL); - return c1->IsAssignableFrom(c2); + return c2->IsAssignableFrom(c1); } static JDWP::FieldId ToFieldId(const mirror::ArtField* f) diff --git a/runtime/stack.cc b/runtime/stack.cc index a50538399c8..4e3fb4a307d 100644 --- a/runtime/stack.cc +++ b/runtime/stack.cc @@ -259,6 +259,7 @@ std::string StackVisitor::DescribeLocation() const { } instrumentation::InstrumentationStackFrame& StackVisitor::GetInstrumentationStackFrame(uint32_t depth) const { + CHECK_LT(depth, thread_->GetInstrumentationStack()->size()); return thread_->GetInstrumentationStack()->at(depth); } diff --git a/runtime/thread.cc b/runtime/thread.cc index 1add5077dcb..1e2a7421656 100644 --- a/runtime/thread.cc +++ b/runtime/thread.cc @@ -1818,6 +1818,12 @@ class CatchBlockStackVisitor : public StackVisitor { self_->EndAssertNoThreadSuspension(last_no_assert_suspension_cause_); // Do instrumentation events after allowing thread suspension again. instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation(); + if (!is_deoptimization_) { + // The debugger may suspend this thread and walk its stack. Let's do this before popping + // instrumentation frames. + instrumentation->ExceptionCaughtEvent(self_, throw_location_, catch_method, handler_dex_pc_, + exception_); + } for (size_t i = 0; i < instrumentation_frames_to_pop_; ++i) { // We pop the instrumentation stack here so as not to corrupt it during the stack walk. if (i != instrumentation_frames_to_pop_ - 1 || self_->GetInstrumentationStack()->front().method_ != catch_method) { @@ -1825,10 +1831,7 @@ class CatchBlockStackVisitor : public StackVisitor { instrumentation->PopMethodForUnwind(self_, is_deoptimization_); } } - if (!is_deoptimization_) { - instrumentation->ExceptionCaughtEvent(self_, throw_location_, catch_method, handler_dex_pc_, - exception_); - } else { + if (is_deoptimization_) { // TODO: proper return value. self_->SetDeoptimizationShadowFrame(top_shadow_frame_); } From 06606b9c4a1c00154ed15f719ad8ea994e54ee8e Mon Sep 17 00:00:00 2001 From: Vladimir Marko Date: Mon, 2 Dec 2013 15:31:08 +0000 Subject: [PATCH 0249/2402] Performance improvement for mapping table creation. Avoid the raw mapping tables altogether. Change-Id: I6d1c786325d369e899a75f15701edbafdd14363f --- compiler/dex/quick/codegen_util.cc | 189 +++++++++++++++-------------- compiler/dex/quick/mir_to_lir.h | 16 +-- compiler/leb128_encoder.h | 25 ++++ compiler/leb128_encoder_test.cc | 114 +++++++++++++++-- 4 files changed, 229 insertions(+), 115 deletions(-) diff --git a/compiler/dex/quick/codegen_util.cc b/compiler/dex/quick/codegen_util.cc index 92b24e140ae..a682d586464 100644 --- a/compiler/dex/quick/codegen_util.cc +++ b/compiler/dex/quick/codegen_util.cc @@ -24,6 +24,28 @@ namespace art { +namespace { + +/* Dump a mapping table */ +template +void DumpMappingTable(const char* table_name, const char* descriptor, const char* name, + const Signature& signature, uint32_t size, It first) { + if (size != 0) { + std::string line(StringPrintf("\n %s %s%s_%s_table[%zu] = {", table_name, + descriptor, name, signature.ToString().c_str(), size)); + std::replace(line.begin(), line.end(), ';', '_'); + LOG(INFO) << line; + for (uint32_t i = 0; i != size; ++i) { + line = StringPrintf(" {0x%05x, 0x%04x},", first.NativePcOffset(), first.DexPc()); + ++first; + LOG(INFO) << line; + } + LOG(INFO) <<" };\n\n"; + } +} + +} // anonymous namespace + bool Mir2Lir::IsInexpensiveConstant(RegLocation rl_src) { bool res = false; if (rl_src.is_const) { @@ -251,23 +273,6 @@ void Mir2Lir::DumpPromotionMap() { } } -/* Dump a mapping table */ -void Mir2Lir::DumpMappingTable(const char* table_name, const char* descriptor, - const char* name, const Signature& signature, - const std::vector& v) { - if (v.size() > 0) { - std::string line(StringPrintf("\n %s %s%s_%s_table[%zu] = {", table_name, - descriptor, name, signature.ToString().c_str(), v.size())); - std::replace(line.begin(), line.end(), ';', '_'); - LOG(INFO) << line; - for (uint32_t i = 0; i < v.size(); i+=2) { - line = StringPrintf(" {0x%05x, 0x%04x},", v[i], v[i+1]); - LOG(INFO) << line; - } - LOG(INFO) <<" };\n\n"; - } -} - /* Dump instructions and constant pool contents */ void Mir2Lir::CodegenDump() { LOG(INFO) << "Dumping LIR insns for " @@ -302,8 +307,13 @@ void Mir2Lir::CodegenDump() { const char* descriptor(cu_->dex_file->GetMethodDeclaringClassDescriptor(method_id)); // Dump mapping tables - DumpMappingTable("PC2Dex_MappingTable", descriptor, name, signature, pc2dex_mapping_table_); - DumpMappingTable("Dex2PC_MappingTable", descriptor, name, signature, dex2pc_mapping_table_); + if (!encoded_mapping_table_.empty()) { + MappingTable table(&encoded_mapping_table_[0]); + DumpMappingTable("PC2Dex_MappingTable", descriptor, name, signature, + table.PcToDexSize(), table.PcToDexBegin()); + DumpMappingTable("Dex2PC_MappingTable", descriptor, name, signature, + table.DexToPcSize(), table.DexToPcBegin()); + } } /* @@ -522,34 +532,34 @@ static int AssignLiteralPointerOffsetCommon(LIR* lir, CodeOffset offset) { // Make sure we have a code address for every declared catch entry bool Mir2Lir::VerifyCatchEntries() { + MappingTable table(&encoded_mapping_table_[0]); + std::vector dex_pcs; + dex_pcs.reserve(table.DexToPcSize()); + for (auto it = table.DexToPcBegin(), end = table.DexToPcEnd(); it != end; ++it) { + dex_pcs.push_back(it.DexPc()); + } + // Sort dex_pcs, so that we can quickly check it against the ordered mir_graph_->catches_. + std::sort(dex_pcs.begin(), dex_pcs.end()); + bool success = true; - for (std::set::const_iterator it = mir_graph_->catches_.begin(); - it != mir_graph_->catches_.end(); ++it) { - uint32_t dex_pc = *it; - bool found = false; - for (size_t i = 0; i < dex2pc_mapping_table_.size(); i += 2) { - if (dex_pc == dex2pc_mapping_table_[i+1]) { - found = true; - break; - } - } - if (!found) { - LOG(INFO) << "Missing native PC for catch entry @ 0x" << std::hex << dex_pc; + auto it = dex_pcs.begin(), end = dex_pcs.end(); + for (uint32_t dex_pc : mir_graph_->catches_) { + while (it != end && *it < dex_pc) { + LOG(INFO) << "Unexpected catch entry @ dex pc 0x" << std::hex << *it; + ++it; success = false; } - } - // Now, try in the other direction - for (size_t i = 0; i < dex2pc_mapping_table_.size(); i += 2) { - uint32_t dex_pc = dex2pc_mapping_table_[i+1]; - if (mir_graph_->catches_.find(dex_pc) == mir_graph_->catches_.end()) { - LOG(INFO) << "Unexpected catch entry @ dex pc 0x" << std::hex << dex_pc; + if (it == end || *it > dex_pc) { + LOG(INFO) << "Missing native PC for catch entry @ 0x" << std::hex << dex_pc; success = false; + } else { + ++it; } } if (!success) { LOG(INFO) << "Bad dex2pcMapping table in " << PrettyMethod(cu_->method_idx, *cu_->dex_file); LOG(INFO) << "Entries @ decode: " << mir_graph_->catches_.size() << ", Entries in table: " - << dex2pc_mapping_table_.size()/2; + << table.DexToPcSize(); } return success; } @@ -573,8 +583,6 @@ void Mir2Lir::CreateMappingTables() { static_cast(pc2dex_dalvik_offset)); pc2dex_offset = tgt_lir->offset; pc2dex_dalvik_offset = tgt_lir->dalvik_offset; - pc2dex_mapping_table_.push_back(tgt_lir->offset); - pc2dex_mapping_table_.push_back(tgt_lir->dalvik_offset); } if (!tgt_lir->flags.is_nop && (tgt_lir->opcode == kPseudoExportedPC)) { dex2pc_entries += 1; @@ -584,63 +592,67 @@ void Mir2Lir::CreateMappingTables() { static_cast(dex2pc_dalvik_offset)); dex2pc_offset = tgt_lir->offset; dex2pc_dalvik_offset = tgt_lir->dalvik_offset; - dex2pc_mapping_table_.push_back(tgt_lir->offset); - dex2pc_mapping_table_.push_back(tgt_lir->dalvik_offset); } } - if (kIsDebugBuild) { - CHECK(VerifyCatchEntries()); - } - DCHECK_EQ(pc2dex_mapping_table_.size(), 2u * pc2dex_entries); - DCHECK_EQ(dex2pc_mapping_table_.size(), 2u * dex2pc_entries); uint32_t total_entries = pc2dex_entries + dex2pc_entries; uint32_t hdr_data_size = UnsignedLeb128Size(total_entries) + UnsignedLeb128Size(pc2dex_entries); uint32_t data_size = hdr_data_size + pc2dex_data_size + dex2pc_data_size; - encoded_mapping_table_.Reserve(data_size); - encoded_mapping_table_.PushBackUnsigned(total_entries); - encoded_mapping_table_.PushBackUnsigned(pc2dex_entries); + encoded_mapping_table_.resize(data_size); + uint8_t* write_pos = &encoded_mapping_table_[0]; + write_pos = EncodeUnsignedLeb128(write_pos, total_entries); + write_pos = EncodeUnsignedLeb128(write_pos, pc2dex_entries); + DCHECK_EQ(static_cast(write_pos - &encoded_mapping_table_[0]), hdr_data_size); + uint8_t* write_pos2 = write_pos + pc2dex_data_size; - dex2pc_offset = 0u; - dex2pc_dalvik_offset = 0u; pc2dex_offset = 0u; pc2dex_dalvik_offset = 0u; - for (uint32_t i = 0; i != pc2dex_entries; ++i) { - encoded_mapping_table_.PushBackUnsigned(pc2dex_mapping_table_[2 * i] - pc2dex_offset); - encoded_mapping_table_.PushBackSigned(static_cast(pc2dex_mapping_table_[2 * i + 1]) - - static_cast(pc2dex_dalvik_offset)); - pc2dex_offset = pc2dex_mapping_table_[2 * i]; - pc2dex_dalvik_offset = pc2dex_mapping_table_[2 * i + 1]; - } - DCHECK(encoded_mapping_table_.GetData().size() == hdr_data_size + pc2dex_data_size); - for (uint32_t i = 0; i != dex2pc_entries; ++i) { - encoded_mapping_table_.PushBackUnsigned(dex2pc_mapping_table_[2 * i] - dex2pc_offset); - encoded_mapping_table_.PushBackSigned(static_cast(dex2pc_mapping_table_[2 * i + 1]) - - static_cast(dex2pc_dalvik_offset)); - dex2pc_offset = dex2pc_mapping_table_[2 * i]; - dex2pc_dalvik_offset = dex2pc_mapping_table_[2 * i + 1]; + dex2pc_offset = 0u; + dex2pc_dalvik_offset = 0u; + for (LIR* tgt_lir = first_lir_insn_; tgt_lir != NULL; tgt_lir = NEXT_LIR(tgt_lir)) { + if (!tgt_lir->flags.is_nop && (tgt_lir->opcode == kPseudoSafepointPC)) { + DCHECK(pc2dex_offset <= tgt_lir->offset); + write_pos = EncodeUnsignedLeb128(write_pos, tgt_lir->offset - pc2dex_offset); + write_pos = EncodeSignedLeb128(write_pos, static_cast(tgt_lir->dalvik_offset) - + static_cast(pc2dex_dalvik_offset)); + pc2dex_offset = tgt_lir->offset; + pc2dex_dalvik_offset = tgt_lir->dalvik_offset; + } + if (!tgt_lir->flags.is_nop && (tgt_lir->opcode == kPseudoExportedPC)) { + DCHECK(dex2pc_offset <= tgt_lir->offset); + write_pos2 = EncodeUnsignedLeb128(write_pos2, tgt_lir->offset - dex2pc_offset); + write_pos2 = EncodeSignedLeb128(write_pos2, static_cast(tgt_lir->dalvik_offset) - + static_cast(dex2pc_dalvik_offset)); + dex2pc_offset = tgt_lir->offset; + dex2pc_dalvik_offset = tgt_lir->dalvik_offset; + } } - DCHECK(encoded_mapping_table_.GetData().size() == data_size); + DCHECK_EQ(static_cast(write_pos - &encoded_mapping_table_[0]), + hdr_data_size + pc2dex_data_size); + DCHECK_EQ(static_cast(write_pos2 - &encoded_mapping_table_[0]), data_size); if (kIsDebugBuild) { + CHECK(VerifyCatchEntries()); + // Verify the encoded table holds the expected data. - MappingTable table(&encoded_mapping_table_.GetData()[0]); + MappingTable table(&encoded_mapping_table_[0]); CHECK_EQ(table.TotalSize(), total_entries); CHECK_EQ(table.PcToDexSize(), pc2dex_entries); - CHECK_EQ(table.DexToPcSize(), dex2pc_mapping_table_.size() / 2); auto it = table.PcToDexBegin(); - for (uint32_t i = 0; i < pc2dex_mapping_table_.size(); ++i, ++it) { - CHECK_EQ(pc2dex_mapping_table_.at(i), it.NativePcOffset()); - ++i; - CHECK_EQ(pc2dex_mapping_table_.at(i), it.DexPc()); - } - CHECK(it == table.PcToDexEnd()); auto it2 = table.DexToPcBegin(); - for (uint32_t i = 0; i < dex2pc_mapping_table_.size(); ++i, ++it2) { - CHECK_EQ(dex2pc_mapping_table_.at(i), it2.NativePcOffset()); - ++i; - CHECK_EQ(dex2pc_mapping_table_.at(i), it2.DexPc()); + for (LIR* tgt_lir = first_lir_insn_; tgt_lir != NULL; tgt_lir = NEXT_LIR(tgt_lir)) { + if (!tgt_lir->flags.is_nop && (tgt_lir->opcode == kPseudoSafepointPC)) { + CHECK_EQ(tgt_lir->offset, it.NativePcOffset()); + CHECK_EQ(tgt_lir->dalvik_offset, it.DexPc()); + ++it; + } + if (!tgt_lir->flags.is_nop && (tgt_lir->opcode == kPseudoExportedPC)) { + CHECK_EQ(tgt_lir->offset, it2.NativePcOffset()); + CHECK_EQ(tgt_lir->dalvik_offset, it2.DexPc()); + ++it2; + } } + CHECK(it == table.PcToDexEnd()); CHECK(it2 == table.DexToPcEnd()); } } @@ -724,10 +736,11 @@ class NativePcToReferenceMapBuilder { }; void Mir2Lir::CreateNativeGcMap() { - const std::vector& mapping_table = pc2dex_mapping_table_; + DCHECK(!encoded_mapping_table_.empty()); + MappingTable mapping_table(&encoded_mapping_table_[0]); uint32_t max_native_offset = 0; - for (size_t i = 0; i < mapping_table.size(); i += 2) { - uint32_t native_offset = mapping_table[i + 0]; + for (auto it = mapping_table.PcToDexBegin(), end = mapping_table.PcToDexEnd(); it != end; ++it) { + uint32_t native_offset = it.NativePcOffset(); if (native_offset > max_native_offset) { max_native_offset = native_offset; } @@ -737,12 +750,12 @@ void Mir2Lir::CreateNativeGcMap() { verifier::DexPcToReferenceMap dex_gc_map(&(*gc_map_raw)[4], gc_map_raw->size() - 4); // Compute native offset to references size. NativePcToReferenceMapBuilder native_gc_map_builder(&native_gc_map_, - mapping_table.size() / 2, max_native_offset, - dex_gc_map.RegWidth()); + mapping_table.PcToDexSize(), + max_native_offset, dex_gc_map.RegWidth()); - for (size_t i = 0; i < mapping_table.size(); i += 2) { - uint32_t native_offset = mapping_table[i + 0]; - uint32_t dex_pc = mapping_table[i + 1]; + for (auto it = mapping_table.PcToDexBegin(), end = mapping_table.PcToDexEnd(); it != end; ++it) { + uint32_t native_offset = it.NativePcOffset(); + uint32_t dex_pc = it.DexPc(); const uint8_t* references = dex_gc_map.FindBitMap(dex_pc, false); CHECK(references != NULL) << "Missing ref for dex pc 0x" << std::hex << dex_pc; native_gc_map_builder.AddEntry(native_offset, references); @@ -1041,7 +1054,7 @@ CompiledMethod* Mir2Lir::GetCompiledMethod() { } CompiledMethod* result = new CompiledMethod(*cu_->compiler_driver, cu_->instruction_set, code_buffer_, frame_size_, - core_spill_mask_, fp_spill_mask_, encoded_mapping_table_.GetData(), + core_spill_mask_, fp_spill_mask_, encoded_mapping_table_, vmap_encoder.GetData(), native_gc_map_); return result; } diff --git a/compiler/dex/quick/mir_to_lir.h b/compiler/dex/quick/mir_to_lir.h index 92e21ffdf76..fae6c4c9a81 100644 --- a/compiler/dex/quick/mir_to_lir.h +++ b/compiler/dex/quick/mir_to_lir.h @@ -341,9 +341,6 @@ class Mir2Lir : public Backend { bool EvaluateBranch(Instruction::Code opcode, int src1, int src2); bool IsInexpensiveConstant(RegLocation rl_src); ConditionCode FlipComparisonOrder(ConditionCode before); - void DumpMappingTable(const char* table_name, const char* descriptor, - const char* name, const Signature& signature, - const std::vector& v); void InstallLiteralPools(); void InstallSwitchTables(); void InstallFillArrayData(); @@ -792,17 +789,6 @@ class Mir2Lir : public Backend { GrowableArray tempreg_info_; GrowableArray reginfo_map_; GrowableArray pointer_storage_; - /* - * Holds mapping from native PC to dex PC for safepoints where we may deoptimize. - * Native PC is on the return address of the safepointed operation. Dex PC is for - * the instruction being executed at the safepoint. - */ - std::vector pc2dex_mapping_table_; - /* - * Holds mapping from Dex PC to native PC for catch entry points. Native PC and Dex PC - * immediately preceed the instruction. - */ - std::vector dex2pc_mapping_table_; CodeOffset current_code_offset_; // Working byte offset of machine instructons. CodeOffset data_offset_; // starting offset of literal pool. size_t total_size_; // header + code size. @@ -828,7 +814,7 @@ class Mir2Lir : public Backend { int live_sreg_; CodeBuffer code_buffer_; // The encoding mapping table data (dex -> pc offset and pc offset -> dex) with a size prefix. - Leb128EncodingVector encoded_mapping_table_; + std::vector encoded_mapping_table_; std::vector core_vmap_table_; std::vector fp_vmap_table_; std::vector native_gc_map_; diff --git a/compiler/leb128_encoder.h b/compiler/leb128_encoder.h index fe38c2f5cdb..67666831f0b 100644 --- a/compiler/leb128_encoder.h +++ b/compiler/leb128_encoder.h @@ -22,6 +22,31 @@ namespace art { +static inline uint8_t* EncodeUnsignedLeb128(uint8_t* dest, uint32_t value) { + uint8_t out = value & 0x7f; + value >>= 7; + while (value != 0) { + *dest++ = out | 0x80; + out = value & 0x7f; + value >>= 7; + } + *dest++ = out; + return dest; +} + +static inline uint8_t* EncodeSignedLeb128(uint8_t* dest, int32_t value) { + uint32_t extra_bits = static_cast(value ^ (value >> 31)) >> 6; + uint8_t out = value & 0x7f; + while (extra_bits != 0u) { + *dest++ = out | 0x80; + value >>= 7; + out = value & 0x7f; + extra_bits >>= 7; + } + *dest++ = out; + return dest; +} + // An encoder with an API similar to vector where the data is captured in ULEB128 format. class Leb128EncodingVector { public: diff --git a/compiler/leb128_encoder_test.cc b/compiler/leb128_encoder_test.cc index 3162ca5f466..c63dfa24975 100644 --- a/compiler/leb128_encoder_test.cc +++ b/compiler/leb128_encoder_test.cc @@ -92,11 +92,12 @@ static DecodeSignedLeb128TestCase sleb128_tests[] = { {(-1) << 31, {0x80, 0x80, 0x80, 0x80, 0x78}}, }; -TEST_F(Leb128Test, UnsignedSingles) { +TEST_F(Leb128Test, UnsignedSinglesVector) { // Test individual encodings. for (size_t i = 0; i < arraysize(uleb128_tests); ++i) { Leb128EncodingVector builder; builder.PushBackUnsigned(uleb128_tests[i].decoded); + EXPECT_EQ(UnsignedLeb128Size(uleb128_tests[i].decoded), builder.GetData().size()); const uint8_t* data_ptr = &uleb128_tests[i].leb128_data[0]; const uint8_t* encoded_data_ptr = &builder.GetData()[0]; for (size_t j = 0; j < 5; ++j) { @@ -110,7 +111,26 @@ TEST_F(Leb128Test, UnsignedSingles) { } } -TEST_F(Leb128Test, UnsignedStream) { +TEST_F(Leb128Test, UnsignedSingles) { + // Test individual encodings. + for (size_t i = 0; i < arraysize(uleb128_tests); ++i) { + uint8_t encoded_data[5]; + uint8_t* end = EncodeUnsignedLeb128(encoded_data, uleb128_tests[i].decoded); + size_t data_size = static_cast(end - encoded_data); + EXPECT_EQ(UnsignedLeb128Size(uleb128_tests[i].decoded), data_size); + const uint8_t* data_ptr = &uleb128_tests[i].leb128_data[0]; + for (size_t j = 0; j < 5; ++j) { + if (j < data_size) { + EXPECT_EQ(data_ptr[j], encoded_data[j]) << " i = " << i << " j = " << j; + } else { + EXPECT_EQ(data_ptr[j], 0U) << " i = " << i << " j = " << j; + } + } + EXPECT_EQ(DecodeUnsignedLeb128(&data_ptr), uleb128_tests[i].decoded) << " i = " << i; + } +} + +TEST_F(Leb128Test, UnsignedStreamVector) { // Encode a number of entries. Leb128EncodingVector builder; for (size_t i = 0; i < arraysize(uleb128_tests); ++i) { @@ -119,20 +139,46 @@ TEST_F(Leb128Test, UnsignedStream) { const uint8_t* encoded_data_ptr = &builder.GetData()[0]; for (size_t i = 0; i < arraysize(uleb128_tests); ++i) { const uint8_t* data_ptr = &uleb128_tests[i].leb128_data[0]; - for (size_t j = 0; j < 5; ++j) { - if (data_ptr[j] != 0) { - EXPECT_EQ(data_ptr[j], encoded_data_ptr[j]) << " i = " << i << " j = " << j; - } + for (size_t j = 0; j < UnsignedLeb128Size(uleb128_tests[i].decoded); ++j) { + EXPECT_EQ(data_ptr[j], encoded_data_ptr[j]) << " i = " << i << " j = " << j; + } + for (size_t j = UnsignedLeb128Size(uleb128_tests[i].decoded); j < 5; ++j) { + EXPECT_EQ(data_ptr[j], 0) << " i = " << i << " j = " << j; } EXPECT_EQ(DecodeUnsignedLeb128(&encoded_data_ptr), uleb128_tests[i].decoded) << " i = " << i; } + EXPECT_EQ(builder.GetData().size(), + static_cast(encoded_data_ptr - &builder.GetData()[0])); } -TEST_F(Leb128Test, SignedSingles) { +TEST_F(Leb128Test, UnsignedStream) { + // Encode a number of entries. + uint8_t encoded_data[5 * arraysize(uleb128_tests)]; + uint8_t* end = encoded_data; + for (size_t i = 0; i < arraysize(uleb128_tests); ++i) { + end = EncodeUnsignedLeb128(end, uleb128_tests[i].decoded); + } + size_t data_size = static_cast(end - encoded_data); + const uint8_t* encoded_data_ptr = encoded_data; + for (size_t i = 0; i < arraysize(uleb128_tests); ++i) { + const uint8_t* data_ptr = &uleb128_tests[i].leb128_data[0]; + for (size_t j = 0; j < UnsignedLeb128Size(uleb128_tests[i].decoded); ++j) { + EXPECT_EQ(data_ptr[j], encoded_data_ptr[j]) << " i = " << i << " j = " << j; + } + for (size_t j = UnsignedLeb128Size(uleb128_tests[i].decoded); j < 5; ++j) { + EXPECT_EQ(data_ptr[j], 0) << " i = " << i << " j = " << j; + } + EXPECT_EQ(DecodeUnsignedLeb128(&encoded_data_ptr), uleb128_tests[i].decoded) << " i = " << i; + } + EXPECT_EQ(data_size, static_cast(encoded_data_ptr - encoded_data)); +} + +TEST_F(Leb128Test, SignedSinglesVector) { // Test individual encodings. for (size_t i = 0; i < arraysize(sleb128_tests); ++i) { Leb128EncodingVector builder; builder.PushBackSigned(sleb128_tests[i].decoded); + EXPECT_EQ(SignedLeb128Size(sleb128_tests[i].decoded), builder.GetData().size()); const uint8_t* data_ptr = &sleb128_tests[i].leb128_data[0]; const uint8_t* encoded_data_ptr = &builder.GetData()[0]; for (size_t j = 0; j < 5; ++j) { @@ -146,7 +192,26 @@ TEST_F(Leb128Test, SignedSingles) { } } -TEST_F(Leb128Test, SignedStream) { +TEST_F(Leb128Test, SignedSingles) { + // Test individual encodings. + for (size_t i = 0; i < arraysize(sleb128_tests); ++i) { + uint8_t encoded_data[5]; + uint8_t* end = EncodeSignedLeb128(encoded_data, sleb128_tests[i].decoded); + size_t data_size = static_cast(end - encoded_data); + EXPECT_EQ(SignedLeb128Size(sleb128_tests[i].decoded), data_size); + const uint8_t* data_ptr = &sleb128_tests[i].leb128_data[0]; + for (size_t j = 0; j < 5; ++j) { + if (j < data_size) { + EXPECT_EQ(data_ptr[j], encoded_data[j]) << " i = " << i << " j = " << j; + } else { + EXPECT_EQ(data_ptr[j], 0U) << " i = " << i << " j = " << j; + } + } + EXPECT_EQ(DecodeSignedLeb128(&data_ptr), sleb128_tests[i].decoded) << " i = " << i; + } +} + +TEST_F(Leb128Test, SignedStreamVector) { // Encode a number of entries. Leb128EncodingVector builder; for (size_t i = 0; i < arraysize(sleb128_tests); ++i) { @@ -155,13 +220,38 @@ TEST_F(Leb128Test, SignedStream) { const uint8_t* encoded_data_ptr = &builder.GetData()[0]; for (size_t i = 0; i < arraysize(sleb128_tests); ++i) { const uint8_t* data_ptr = &sleb128_tests[i].leb128_data[0]; - for (size_t j = 0; j < 5; ++j) { - if (data_ptr[j] != 0) { - EXPECT_EQ(data_ptr[j], encoded_data_ptr[j]) << " i = " << i << " j = " << j; - } + for (size_t j = 0; j < SignedLeb128Size(sleb128_tests[i].decoded); ++j) { + EXPECT_EQ(data_ptr[j], encoded_data_ptr[j]) << " i = " << i << " j = " << j; + } + for (size_t j = SignedLeb128Size(sleb128_tests[i].decoded); j < 5; ++j) { + EXPECT_EQ(data_ptr[j], 0) << " i = " << i << " j = " << j; + } + EXPECT_EQ(DecodeSignedLeb128(&encoded_data_ptr), sleb128_tests[i].decoded) << " i = " << i; + } + EXPECT_EQ(builder.GetData().size(), + static_cast(encoded_data_ptr - &builder.GetData()[0])); +} + +TEST_F(Leb128Test, SignedStream) { + // Encode a number of entries. + uint8_t encoded_data[5 * arraysize(sleb128_tests)]; + uint8_t* end = encoded_data; + for (size_t i = 0; i < arraysize(sleb128_tests); ++i) { + end = EncodeSignedLeb128(end, sleb128_tests[i].decoded); + } + size_t data_size = static_cast(end - encoded_data); + const uint8_t* encoded_data_ptr = encoded_data; + for (size_t i = 0; i < arraysize(sleb128_tests); ++i) { + const uint8_t* data_ptr = &sleb128_tests[i].leb128_data[0]; + for (size_t j = 0; j < SignedLeb128Size(sleb128_tests[i].decoded); ++j) { + EXPECT_EQ(data_ptr[j], encoded_data_ptr[j]) << " i = " << i << " j = " << j; + } + for (size_t j = SignedLeb128Size(sleb128_tests[i].decoded); j < 5; ++j) { + EXPECT_EQ(data_ptr[j], 0) << " i = " << i << " j = " << j; } EXPECT_EQ(DecodeSignedLeb128(&encoded_data_ptr), sleb128_tests[i].decoded) << " i = " << i; } + EXPECT_EQ(data_size, static_cast(encoded_data_ptr - encoded_data)); } TEST_F(Leb128Test, Speed) { From 5f51d4b80058236759fea1d932470a57f348c199 Mon Sep 17 00:00:00 2001 From: Mathieu Chartier Date: Tue, 3 Dec 2013 14:24:05 -0800 Subject: [PATCH 0250/2402] Fix races in thread list Unregister. First race: We were releasing the thin_lock_id in Unregister before the thread was not suspended. This could cause problems in SuspendThreadByThreadId since there was a small window of time where two threads could share the same thread id. This race caused an occasional check failure in SuspendThreadByThreadId. Second race: We were setting the thin_lock_thread_id_ to 0 in Unregister before waiting to not be suspended. This caused another race in SuspendThreadByThreadId where we modified the thread suspend count, busy waited, but didn't find the thread the next iteration. This meant that we were returning null even though we had modified the suspend count. This caused the suspend count to not get decremented since the caller didn't know that the suspend count had been increased. Removing the self->thin_lock_thread_id_ = 0 in ThreadList::UnRegister fixes this race. Added a bit of additional checks and logging to prevent these issues from resurfacing, other misc cleanup. Added thread names to threads in ThreadStress. Bug: 11319866 Change-Id: I48e3a0700193b72079e450be1e924a2f88cf52e2 --- runtime/monitor.cc | 3 +- runtime/thread.cc | 11 ++- runtime/thread_list.cc | 102 +++++++++++++++------------- test/ThreadStress/ThreadStress.java | 10 +-- 4 files changed, 64 insertions(+), 62 deletions(-) diff --git a/runtime/monitor.cc b/runtime/monitor.cc index af93a562647..ef9a9cee87e 100644 --- a/runtime/monitor.cc +++ b/runtime/monitor.cc @@ -633,8 +633,7 @@ void Monitor::InflateThinLocked(Thread* self, SirtRef& obj, Lock ScopedThreadStateChange tsc(self, kBlocked); if (lock_word == obj->GetLockWord()) { // If lock word hasn't changed. bool timed_out; - Thread* owner = thread_list->SuspendThreadByThreadId(lock_word.ThinLockOwner(), false, - &timed_out); + Thread* owner = thread_list->SuspendThreadByThreadId(owner_thread_id, false, &timed_out); if (owner != nullptr) { // We succeeded in suspending the thread, check the lock's status didn't change. lock_word = obj->GetLockWord(); diff --git a/runtime/thread.cc b/runtime/thread.cc index d816ca1ab51..297fa4561d7 100644 --- a/runtime/thread.cc +++ b/runtime/thread.cc @@ -586,11 +586,11 @@ bool Thread::RequestCheckpoint(Closure* function) { if ((old_state_and_flags.as_struct.flags & kCheckpointRequest) != 0) { return false; // Fail, already a checkpoint pending. } - CHECK(checkpoint_function_ == NULL); + CHECK(checkpoint_function_ == nullptr); checkpoint_function_ = function; // Checkpoint function installed now install flag bit. // We must be runnable to request a checkpoint. - old_state_and_flags.as_struct.state = kRunnable; + DCHECK_EQ(old_state_and_flags.as_struct.state, kRunnable); union StateAndFlags new_state_and_flags = old_state_and_flags; new_state_and_flags.as_struct.flags |= kCheckpointRequest; int succeeded = android_atomic_cmpxchg(old_state_and_flags.as_int, new_state_and_flags.as_int, @@ -2120,12 +2120,11 @@ void Thread::VisitRoots(RootVisitor* visitor, void* arg) { opeer_ = visitor(opeer_, arg); } if (exception_ != nullptr) { - exception_ = reinterpret_cast(visitor(exception_, arg)); + exception_ = down_cast(visitor(exception_, arg)); } throw_location_.VisitRoots(visitor, arg); if (class_loader_override_ != nullptr) { - class_loader_override_ = reinterpret_cast( - visitor(class_loader_override_, arg)); + class_loader_override_ = down_cast(visitor(class_loader_override_, arg)); } jni_env_->locals.VisitRoots(visitor, arg); jni_env_->monitors.VisitRoots(visitor, arg); @@ -2144,7 +2143,7 @@ void Thread::VisitRoots(RootVisitor* visitor, void* arg) { frame.this_object_ = visitor(frame.this_object_, arg); } DCHECK(frame.method_ != nullptr); - frame.method_ = reinterpret_cast(visitor(frame.method_, arg)); + frame.method_ = down_cast(visitor(frame.method_, arg)); } } diff --git a/runtime/thread_list.cc b/runtime/thread_list.cc index dd3f11cb9e0..aed8c7788ec 100644 --- a/runtime/thread_list.cc +++ b/runtime/thread_list.cc @@ -162,6 +162,35 @@ static void UnsafeLogFatalForThreadSuspendAllTimeout(Thread* self) NO_THREAD_SAF } #endif +// Unlike suspending all threads where we can wait to acquire the mutator_lock_, suspending an +// individual thread requires polling. delay_us is the requested sleep and total_delay_us +// accumulates the total time spent sleeping for timeouts. The first sleep is just a yield, +// subsequently sleeps increase delay_us from 1ms to 500ms by doubling. +static void ThreadSuspendSleep(Thread* self, useconds_t* delay_us, useconds_t* total_delay_us, + bool holding_locks) { + if (!holding_locks) { + for (int i = kLockLevelCount - 1; i >= 0; --i) { + BaseMutex* held_mutex = self->GetHeldMutex(static_cast(i)); + if (held_mutex != NULL) { + LOG(FATAL) << "Holding " << held_mutex->GetName() << " while sleeping for thread suspension"; + } + } + } + useconds_t new_delay_us = (*delay_us) * 2; + CHECK_GE(new_delay_us, *delay_us); + if (new_delay_us < 500000) { // Don't allow sleeping to be more than 0.5s. + *delay_us = new_delay_us; + } + if (*delay_us == 0) { + sched_yield(); + // Default to 1 milliseconds (note that this gets multiplied by 2 before the first sleep). + *delay_us = 500; + } else { + usleep(*delay_us); + *total_delay_us += *delay_us; + } +} + size_t ThreadList::RunCheckpoint(Closure* checkpoint_function) { Thread* self = Thread::Current(); if (kIsDebugBuild) { @@ -208,17 +237,15 @@ size_t ThreadList::RunCheckpoint(Closure* checkpoint_function) { for (const auto& thread : suspended_count_modified_threads) { if (!thread->IsSuspended()) { // Wait until the thread is suspended. - uint64_t start = NanoTime(); + useconds_t total_delay_us = 0; do { - // Sleep for 100us. - usleep(100); + useconds_t delay_us = 100; + ThreadSuspendSleep(self, &delay_us, &total_delay_us, true); } while (!thread->IsSuspended()); - uint64_t end = NanoTime(); - // Shouldn't need to wait for longer than 1 millisecond. - const uint64_t threshold = 1; - if (NsToMs(end - start) > threshold) { - LOG(INFO) << "Warning: waited longer than " << threshold - << " ms for thread suspend\n"; + // Shouldn't need to wait for longer than 1000 microseconds. + constexpr useconds_t kLongWaitThresholdUS = 1000; + if (UNLIKELY(total_delay_us > kLongWaitThresholdUS)) { + LOG(WARNING) << "Waited " << total_delay_us << " us for thread suspend!"; } } // We know for sure that the thread is suspended at this point. @@ -354,34 +381,6 @@ static void ThreadSuspendByPeerWarning(Thread* self, int level, const char* mess } } -// Unlike suspending all threads where we can wait to acquire the mutator_lock_, suspending an -// individual thread requires polling. delay_us is the requested sleep and total_delay_us -// accumulates the total time spent sleeping for timeouts. The first sleep is just a yield, -// subsequently sleeps increase delay_us from 1ms to 500ms by doubling. -static void ThreadSuspendSleep(Thread* self, useconds_t* delay_us, useconds_t* total_delay_us) { - for (int i = kLockLevelCount - 1; i >= 0; --i) { - BaseMutex* held_mutex = self->GetHeldMutex(static_cast(i)); - if (held_mutex != NULL) { - LOG(FATAL) << "Holding " << held_mutex->GetName() << " while sleeping for thread suspension"; - } - } - { - useconds_t new_delay_us = (*delay_us) * 2; - CHECK_GE(new_delay_us, *delay_us); - if (new_delay_us < 500000) { // Don't allow sleeping to be more than 0.5s. - *delay_us = new_delay_us; - } - } - if ((*delay_us) == 0) { - sched_yield(); - // Default to 1 milliseconds (note that this gets multiplied by 2 before the first sleep). - (*delay_us) = 500; - } else { - usleep(*delay_us); - (*total_delay_us) += (*delay_us); - } -} - Thread* ThreadList::SuspendThreadByPeer(jobject peer, bool request_suspension, bool debug_suspension, bool* timed_out) { static const useconds_t kTimeoutUs = 30 * 1000000; // 30s. @@ -432,7 +431,7 @@ Thread* ThreadList::SuspendThreadByPeer(jobject peer, bool request_suspension, } // Release locks and come out of runnable state. } - ThreadSuspendSleep(self, &delay_us, &total_delay_us); + ThreadSuspendSleep(self, &delay_us, &total_delay_us, false); } } @@ -445,13 +444,13 @@ Thread* ThreadList::SuspendThreadByThreadId(uint32_t thread_id, bool debug_suspe static const useconds_t kTimeoutUs = 30 * 1000000; // 30s. useconds_t total_delay_us = 0; useconds_t delay_us = 0; - bool did_suspend_request = false; *timed_out = false; + Thread* suspended_thread = nullptr; Thread* self = Thread::Current(); CHECK_NE(thread_id, kInvalidThreadId); while (true) { - Thread* thread = NULL; { + Thread* thread = NULL; ScopedObjectAccess soa(self); MutexLock mu(self, *Locks::thread_list_lock_); for (const auto& it : list_) { @@ -460,17 +459,20 @@ Thread* ThreadList::SuspendThreadByThreadId(uint32_t thread_id, bool debug_suspe break; } } - if (thread == NULL) { + if (thread == nullptr) { + CHECK(suspended_thread == nullptr) << "Suspended thread " << suspended_thread + << " no longer in thread list"; // There's a race in inflating a lock and the owner giving up ownership and then dying. ThreadSuspendByThreadIdWarning(WARNING, "No such thread id for suspend", thread_id); return NULL; } { MutexLock mu(self, *Locks::thread_suspend_count_lock_); - if (!did_suspend_request) { + if (suspended_thread == nullptr) { thread->ModifySuspendCount(self, +1, debug_suspension); - did_suspend_request = true; + suspended_thread = thread; } else { + CHECK_EQ(suspended_thread, thread); // If the caller isn't requesting suspension, a suspension should have already occurred. CHECK_GT(thread->GetSuspendCount(), 0); } @@ -487,7 +489,7 @@ Thread* ThreadList::SuspendThreadByThreadId(uint32_t thread_id, bool debug_suspe } if (total_delay_us >= kTimeoutUs) { ThreadSuspendByThreadIdWarning(WARNING, "Thread suspension timed out", thread_id); - if (did_suspend_request) { + if (suspended_thread != nullptr) { thread->ModifySuspendCount(soa.Self(), -1, debug_suspension); } *timed_out = true; @@ -496,7 +498,7 @@ Thread* ThreadList::SuspendThreadByThreadId(uint32_t thread_id, bool debug_suspe } // Release locks and come out of runnable state. } - ThreadSuspendSleep(self, &delay_us, &total_delay_us); + ThreadSuspendSleep(self, &delay_us, &total_delay_us, false); } } @@ -719,9 +721,7 @@ void ThreadList::Unregister(Thread* self) { self->Destroy(); uint32_t thin_lock_id = self->thin_lock_thread_id_; - self->thin_lock_thread_id_ = 0; - ReleaseThreadId(self, thin_lock_id); - while (self != NULL) { + while (self != nullptr) { // Remove and delete the Thread* while holding the thread_list_lock_ and // thread_suspend_count_lock_ so that the unregistering thread cannot be suspended. // Note: deliberately not using MutexLock that could hold a stale self pointer. @@ -732,10 +732,14 @@ void ThreadList::Unregister(Thread* self) { if (!self->IsSuspended()) { list_.remove(self); delete self; - self = NULL; + self = nullptr; } Locks::thread_list_lock_->ExclusiveUnlock(self); } + // Release the thread ID after the thread is finished and deleted to avoid cases where we can + // temporarily have multiple threads with the same thread id. When this occurs, it causes + // problems in FindThreadByThreadId / SuspendThreadByThreadId. + ReleaseThreadId(nullptr, thin_lock_id); // Clear the TLS data, so that the underlying native thread is recognizably detached. // (It may wish to reattach later.) diff --git a/test/ThreadStress/ThreadStress.java b/test/ThreadStress/ThreadStress.java index 8d8135d24a5..795c7902121 100644 --- a/test/ThreadStress/ThreadStress.java +++ b/test/ThreadStress/ThreadStress.java @@ -128,13 +128,13 @@ public static void main(String[] args) throws Exception { Thread[] runners = new Thread[numberOfThreads]; for (int r = 0; r < runners.length; r++) { final ThreadStress ts = threadStresses[r]; - runners[r] = new Thread() { + runners[r] = new Thread("Runner thread " + r) { final ThreadStress threadStress = ts; public void run() { int id = threadStress.id; - System.out.println("Starting runner for " + id); + System.out.println("Starting worker for " + id); while (threadStress.nextOperation < operationsPerThread) { - Thread thread = new Thread(ts); + Thread thread = new Thread(ts, "Worker thread " + id); thread.start(); try { thread.join(); @@ -144,14 +144,14 @@ public void run() { + (operationsPerThread - threadStress.nextOperation) + " operations remaining."); } - System.out.println("Finishing runner for " + id); + System.out.println("Finishing worker for " + id); } }; } // The notifier thread is a daemon just loops forever to wake // up threads in Operation.WAIT - Thread notifier = new Thread() { + Thread notifier = new Thread("Notifier") { public void run() { while (true) { synchronized (lock) { From 59cde534aa295bad7de29472b3cce9576d7996a8 Mon Sep 17 00:00:00 2001 From: Chris Dearman Date: Wed, 4 Dec 2013 18:53:49 -0800 Subject: [PATCH 0251/2402] Workaround for gcc volatile struct member bug gcc does not handle struct with volatile member assignments correctly. See http://gcc.gnu.org/bugzilla/show_bug.cgi?id=47409 Using structure assignments for the StateAndFlag union can cause gcc to optimise the code incorrectly. Doing the assignment using the as_int member forces the correct behaviour. Change-Id: I6379d36add16c321b2e4d1dcd6fd8c959f3f92d6 --- runtime/thread-inl.h | 19 +++++++++++-------- runtime/thread.cc | 6 ++++-- runtime/thread.h | 11 +++++++++-- 3 files changed, 24 insertions(+), 12 deletions(-) diff --git a/runtime/thread-inl.h b/runtime/thread-inl.h index 84496072b11..e47fd372c7a 100644 --- a/runtime/thread-inl.h +++ b/runtime/thread-inl.h @@ -50,7 +50,8 @@ inline ThreadState Thread::SetState(ThreadState new_state) { // old_state_and_flags.suspend_request is true. DCHECK_NE(new_state, kRunnable); DCHECK_EQ(this, Thread::Current()); - union StateAndFlags old_state_and_flags = state_and_flags_; + union StateAndFlags old_state_and_flags; + old_state_and_flags.as_int = state_and_flags_.as_int; state_and_flags_.as_struct.state = new_state; return static_cast(old_state_and_flags.as_struct.state); } @@ -87,7 +88,7 @@ inline void Thread::TransitionFromRunnableToSuspended(ThreadState new_state) { union StateAndFlags old_state_and_flags; union StateAndFlags new_state_and_flags; do { - old_state_and_flags = state_and_flags_; + old_state_and_flags.as_int = state_and_flags_.as_int; if (UNLIKELY((old_state_and_flags.as_struct.flags & kCheckpointRequest) != 0)) { RunCheckpointFunction(); continue; @@ -104,22 +105,23 @@ inline void Thread::TransitionFromRunnableToSuspended(ThreadState new_state) { inline ThreadState Thread::TransitionFromSuspendedToRunnable() { bool done = false; - union StateAndFlags old_state_and_flags = state_and_flags_; + union StateAndFlags old_state_and_flags; + old_state_and_flags.as_int = state_and_flags_.as_int; int16_t old_state = old_state_and_flags.as_struct.state; DCHECK_NE(static_cast(old_state), kRunnable); do { Locks::mutator_lock_->AssertNotHeld(this); // Otherwise we starve GC.. - old_state_and_flags = state_and_flags_; + old_state_and_flags.as_int = state_and_flags_.as_int; DCHECK_EQ(old_state_and_flags.as_struct.state, old_state); if (UNLIKELY((old_state_and_flags.as_struct.flags & kSuspendRequest) != 0)) { // Wait while our suspend count is non-zero. MutexLock mu(this, *Locks::thread_suspend_count_lock_); - old_state_and_flags = state_and_flags_; + old_state_and_flags.as_int = state_and_flags_.as_int; DCHECK_EQ(old_state_and_flags.as_struct.state, old_state); while ((old_state_and_flags.as_struct.flags & kSuspendRequest) != 0) { // Re-check when Thread::resume_cond_ is notified. Thread::resume_cond_->Wait(this); - old_state_and_flags = state_and_flags_; + old_state_and_flags.as_int = state_and_flags_.as_int; DCHECK_EQ(old_state_and_flags.as_struct.state, old_state); } DCHECK_EQ(GetSuspendCount(), 0); @@ -127,10 +129,11 @@ inline ThreadState Thread::TransitionFromSuspendedToRunnable() { // Re-acquire shared mutator_lock_ access. Locks::mutator_lock_->SharedLock(this); // Atomically change from suspended to runnable if no suspend request pending. - old_state_and_flags = state_and_flags_; + old_state_and_flags.as_int = state_and_flags_.as_int; DCHECK_EQ(old_state_and_flags.as_struct.state, old_state); if (LIKELY((old_state_and_flags.as_struct.flags & kSuspendRequest) == 0)) { - union StateAndFlags new_state_and_flags = old_state_and_flags; + union StateAndFlags new_state_and_flags; + new_state_and_flags.as_int = old_state_and_flags.as_int; new_state_and_flags.as_struct.state = kRunnable; // CAS the value without a memory barrier, that occurred in the lock above. done = android_atomic_cas(old_state_and_flags.as_int, new_state_and_flags.as_int, diff --git a/runtime/thread.cc b/runtime/thread.cc index 297fa4561d7..fa49faa310c 100644 --- a/runtime/thread.cc +++ b/runtime/thread.cc @@ -579,7 +579,8 @@ void Thread::RunCheckpointFunction() { } bool Thread::RequestCheckpoint(Closure* function) { - union StateAndFlags old_state_and_flags = state_and_flags_; + union StateAndFlags old_state_and_flags; + old_state_and_flags.as_int = state_and_flags_.as_int; if (old_state_and_flags.as_struct.state != kRunnable) { return false; // Fail, thread is suspended and so can't run a checkpoint. } @@ -591,7 +592,8 @@ bool Thread::RequestCheckpoint(Closure* function) { // Checkpoint function installed now install flag bit. // We must be runnable to request a checkpoint. DCHECK_EQ(old_state_and_flags.as_struct.state, kRunnable); - union StateAndFlags new_state_and_flags = old_state_and_flags; + union StateAndFlags new_state_and_flags; + new_state_and_flags.as_int = old_state_and_flags.as_int; new_state_and_flags.as_struct.flags |= kCheckpointRequest; int succeeded = android_atomic_cmpxchg(old_state_and_flags.as_int, new_state_and_flags.as_int, &state_and_flags_.as_int); diff --git a/runtime/thread.h b/runtime/thread.h index db2f7b47d46..44b2186357c 100644 --- a/runtime/thread.h +++ b/runtime/thread.h @@ -147,7 +147,8 @@ class PACKED(4) Thread { } bool IsSuspended() const { - union StateAndFlags state_and_flags = state_and_flags_; + union StateAndFlags state_and_flags; + state_and_flags.as_int = state_and_flags_.as_int; return state_and_flags.as_struct.state != kRunnable && (state_and_flags.as_struct.flags & kSuspendRequest) != 0; } @@ -638,7 +639,8 @@ class PACKED(4) Thread { // 32 bits of atomically changed state and flags. Keeping as 32 bits allows and atomic CAS to // change from being Suspended to Runnable without a suspend request occurring. - union StateAndFlags { + union PACKED(4) StateAndFlags { + StateAndFlags() {} struct PACKED(4) { // Bitfield of flag values. Must be changed atomically so that flag values aren't lost. See // ThreadFlags for bit field meanings. @@ -650,6 +652,11 @@ class PACKED(4) Thread { volatile uint16_t state; } as_struct; volatile int32_t as_int; + + private: + // gcc does not handle struct with volatile member assignments correctly. + // See http://gcc.gnu.org/bugzilla/show_bug.cgi?id=47409 + DISALLOW_COPY_AND_ASSIGN(StateAndFlags); }; union StateAndFlags state_and_flags_; COMPILE_ASSERT(sizeof(union StateAndFlags) == sizeof(int32_t), From 73e08b37512a2d38cdbaf9a3869dbd7554e6e1b1 Mon Sep 17 00:00:00 2001 From: Vladimir Marko Date: Thu, 21 Nov 2013 10:58:36 +0000 Subject: [PATCH 0252/2402] Fix Mir2Lir::AllocFreeTemp() to return -1 on failure. Change-Id: I190de54de347fe3f4c9bcd07b2117ad91ea5c9a3 --- compiler/dex/quick/mips/utility_mips.cc | 4 ++-- compiler/dex/quick/ralloc_util.cc | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/compiler/dex/quick/mips/utility_mips.cc b/compiler/dex/quick/mips/utility_mips.cc index 2ba2c8487dd..65c82c08a08 100644 --- a/compiler/dex/quick/mips/utility_mips.cc +++ b/compiler/dex/quick/mips/utility_mips.cc @@ -504,13 +504,13 @@ LIR* MipsMir2Lir::LoadBaseDispBody(int rBase, int displacement, int r_dest, } } else { if (pair) { - int r_tmp = AllocFreeTemp(); + int r_tmp = AllocTemp(); res = OpRegRegImm(kOpAdd, r_tmp, rBase, displacement); load = NewLIR3(opcode, r_dest, LOWORD_OFFSET, r_tmp); load2 = NewLIR3(opcode, r_dest_hi, HIWORD_OFFSET, r_tmp); FreeTemp(r_tmp); } else { - int r_tmp = (rBase == r_dest) ? AllocFreeTemp() : r_dest; + int r_tmp = (rBase == r_dest) ? AllocTemp() : r_dest; res = OpRegRegImm(kOpAdd, r_tmp, rBase, displacement); load = NewLIR3(opcode, r_dest, 0, r_tmp); if (r_tmp != r_dest) diff --git a/compiler/dex/quick/ralloc_util.cc b/compiler/dex/quick/ralloc_util.cc index 41a57afca19..cef013e9d84 100644 --- a/compiler/dex/quick/ralloc_util.cc +++ b/compiler/dex/quick/ralloc_util.cc @@ -338,7 +338,7 @@ int Mir2Lir::AllocTempDouble() { int Mir2Lir::AllocFreeTemp() { return AllocTempBody(reg_pool_->core_regs, reg_pool_->num_core_regs, - ®_pool_->next_core_reg, true); + ®_pool_->next_core_reg, false); } int Mir2Lir::AllocTemp() { From e4e23c0b0cfebd01f17732b6686a74f9e437e978 Mon Sep 17 00:00:00 2001 From: Hiroshi Yamauchi Date: Fri, 6 Dec 2013 13:38:43 -0800 Subject: [PATCH 0253/2402] Fix valgrind-test-art-host-gtest-object_test. This had been failing with the error message: object_test F 2360 2360 art/runtime/gc/heap-inl.h:139] Check failed: !running_on_valgrind_ Then, this failing DCHECK was removed in a refactoring in cbb2d20bea2861f244da2e2318d8c088300a3710, I believe. This change adds back a DCHECK that's equivalent to the old one and fixes ObjectTest.CheckAndAllocArrayFromCode by replacing a CheckAndAllocArrayFromCode call with a CheckAndAllocArrayFromCodeInstrumented call. Bug: 11670287 Change-Id: Ica2f707b9a9ff48ef973b9e326a4d9786c1781f8 --- runtime/gc/heap-inl.h | 3 +++ runtime/mirror/object_test.cc | 4 ++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/runtime/gc/heap-inl.h b/runtime/gc/heap-inl.h index 5eda0b95b84..6e9b04ada19 100644 --- a/runtime/gc/heap-inl.h +++ b/runtime/gc/heap-inl.h @@ -116,6 +116,9 @@ inline mirror::Object* Heap::TryToAllocate(Thread* self, AllocatorType allocator if (UNLIKELY(running_on_valgrind_ && allocator_type == kAllocatorTypeFreeList)) { return non_moving_space_->Alloc(self, alloc_size, bytes_allocated); } + } else { + // If running on valgrind, we should be using the instrumented path. + DCHECK(!running_on_valgrind_); } mirror::Object* ret; switch (allocator_type) { diff --git a/runtime/mirror/object_test.cc b/runtime/mirror/object_test.cc index 8272ff8b1f3..36371810f4c 100644 --- a/runtime/mirror/object_test.cc +++ b/runtime/mirror/object_test.cc @@ -221,8 +221,8 @@ TEST_F(ObjectTest, CheckAndAllocArrayFromCode) { java_lang_dex_file_->GetIndexForStringId(*string_id)); ASSERT_TRUE(type_id != NULL); uint32_t type_idx = java_lang_dex_file_->GetIndexForTypeId(*type_id); - Object* array = CheckAndAllocArrayFromCode(type_idx, sort, 3, Thread::Current(), false, - Runtime::Current()->GetHeap()->GetCurrentAllocator()); + Object* array = CheckAndAllocArrayFromCodeInstrumented(type_idx, sort, 3, Thread::Current(), false, + Runtime::Current()->GetHeap()->GetCurrentAllocator()); EXPECT_TRUE(array->IsArrayInstance()); EXPECT_EQ(3, array->AsArray()->GetLength()); EXPECT_TRUE(array->GetClass()->IsArrayClass()); From 92572be7f754c213e615a62955cc5f65ca8c0c0e Mon Sep 17 00:00:00 2001 From: Narayan Kamath Date: Thu, 28 Nov 2013 14:06:24 +0000 Subject: [PATCH 0254/2402] Use libziparchive for art zip processing. This is part of the effort to move all VM & framework zip parsing to a common implementation. This also has the side effect of fixing various TODOs related to crc32 checking. bug: 10193060 Change-Id: I407f9ad5a94fc91d96ff43556adde00a00df1f14 --- dex2oat/dex2oat.cc | 2 +- runtime/Android.mk | 6 +- runtime/dex_file.cc | 8 +- runtime/dex_file_verifier.cc | 3 +- runtime/native/java_lang_VMClassLoader.cc | 2 +- runtime/zip_archive.cc | 524 ++-------------------- runtime/zip_archive.h | 87 +--- runtime/zip_archive_test.cc | 4 +- 8 files changed, 66 insertions(+), 570 deletions(-) diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc index 8b232700b08..28d6649ada2 100644 --- a/dex2oat/dex2oat.cc +++ b/dex2oat/dex2oat.cc @@ -213,7 +213,7 @@ class Dex2Oat { if (zip_archive.get() == NULL) { return NULL; } - UniquePtr zip_entry(zip_archive->Find(image_classes_filename)); + UniquePtr zip_entry(zip_archive->Find(image_classes_filename, error_msg)); if (zip_entry.get() == NULL) { *error_msg = StringPrintf("Failed to find '%s' within '%s': %s", image_classes_filename, zip_filename, error_msg->c_str()); diff --git a/runtime/Android.mk b/runtime/Android.mk index 16f11c6af77..4e5afab4e79 100644 --- a/runtime/Android.mk +++ b/runtime/Android.mk @@ -344,10 +344,10 @@ $$(ENUM_OPERATOR_OUT_GEN): $$(GENERATED_SRC_DIR)/%_operator_out.cc : $(LOCAL_PAT LOCAL_SHARED_LIBRARIES += liblog libnativehelper LOCAL_SHARED_LIBRARIES += libbacktrace # native stack trace support ifeq ($$(art_target_or_host),target) - LOCAL_SHARED_LIBRARIES += libcutils libz libdl libselinux + LOCAL_SHARED_LIBRARIES += libcutils libdl libselinux + LOCAL_STATIC_LIBRARIES := libziparchive libz else # host - LOCAL_STATIC_LIBRARIES += libcutils - LOCAL_SHARED_LIBRARIES += libz-host + LOCAL_STATIC_LIBRARIES += libcutils libziparchive-host libz LOCAL_LDLIBS += -ldl -lpthread ifeq ($(HOST_OS),linux) LOCAL_LDLIBS += -lrt diff --git a/runtime/dex_file.cc b/runtime/dex_file.cc index 517f96c6238..463e673279d 100644 --- a/runtime/dex_file.cc +++ b/runtime/dex_file.cc @@ -98,9 +98,10 @@ bool DexFile::GetChecksum(const char* filename, uint32_t* checksum, std::string* *error_msg = StringPrintf("Failed to open zip archive '%s'", filename); return false; } - UniquePtr zip_entry(zip_archive->Find(kClassesDex)); + UniquePtr zip_entry(zip_archive->Find(kClassesDex, error_msg)); if (zip_entry.get() == NULL) { - *error_msg = StringPrintf("Zip archive '%s' doesn\'t contain %s", filename, kClassesDex); + *error_msg = StringPrintf("Zip archive '%s' doesn\'t contain %s (error msg: %s)", filename, + kClassesDex, error_msg->c_str()); return false; } *checksum = zip_entry->GetCrc32(); @@ -240,9 +241,8 @@ const DexFile* DexFile::OpenMemory(const std::string& location, const DexFile* DexFile::Open(const ZipArchive& zip_archive, const std::string& location, std::string* error_msg) { CHECK(!location.empty()); - UniquePtr zip_entry(zip_archive.Find(kClassesDex)); + UniquePtr zip_entry(zip_archive.Find(kClassesDex, error_msg)); if (zip_entry.get() == NULL) { - *error_msg = StringPrintf("Failed to find classes.dex within '%s'", location.c_str()); return nullptr; } UniquePtr map(zip_entry->ExtractToMemMap(kClassesDex, error_msg)); diff --git a/runtime/dex_file_verifier.cc b/runtime/dex_file_verifier.cc index 56bf21d2cb8..dc9d3374733 100644 --- a/runtime/dex_file_verifier.cc +++ b/runtime/dex_file_verifier.cc @@ -16,6 +16,8 @@ #include "dex_file_verifier.h" +#include + #include "base/stringprintf.h" #include "dex_file-inl.h" #include "leb128.h" @@ -23,7 +25,6 @@ #include "UniquePtr.h" #include "utf-inl.h" #include "utils.h" -#include "zip_archive.h" namespace art { diff --git a/runtime/native/java_lang_VMClassLoader.cc b/runtime/native/java_lang_VMClassLoader.cc index af1b548aa3d..314cdb1a7eb 100644 --- a/runtime/native/java_lang_VMClassLoader.cc +++ b/runtime/native/java_lang_VMClassLoader.cc @@ -78,7 +78,7 @@ static jstring VMClassLoader_getBootClassPathResource(JNIEnv* env, jclass, jstri LOG(WARNING) << "Failed to open zip archive '" << location << "': " << error_msg; return NULL; } - UniquePtr zip_entry(zip_archive->Find(name.c_str())); + UniquePtr zip_entry(zip_archive->Find(name.c_str(), &error_msg)); if (zip_entry.get() == NULL) { return NULL; } diff --git a/runtime/zip_archive.cc b/runtime/zip_archive.cc index db273ec7bf7..8cb199375bc 100644 --- a/runtime/zip_archive.cc +++ b/runtime/zip_archive.cc @@ -30,272 +30,23 @@ namespace art { -static const size_t kBufSize = 32 * KB; - -// Get 2 little-endian bytes. -static uint32_t Le16ToHost(const byte* src) { - return ((src[0] << 0) | - (src[1] << 8)); -} - -// Get 4 little-endian bytes. -static uint32_t Le32ToHost(const byte* src) { - return ((src[0] << 0) | - (src[1] << 8) | - (src[2] << 16) | - (src[3] << 24)); -} - -uint16_t ZipEntry::GetCompressionMethod() { - return Le16ToHost(ptr_ + ZipArchive::kCDEMethod); -} - -uint32_t ZipEntry::GetCompressedLength() { - return Le32ToHost(ptr_ + ZipArchive::kCDECompLen); -} - uint32_t ZipEntry::GetUncompressedLength() { - return Le32ToHost(ptr_ + ZipArchive::kCDEUncompLen); + return zip_entry_->uncompressed_length; } uint32_t ZipEntry::GetCrc32() { - return Le32ToHost(ptr_ + ZipArchive::kCDECRC); + return zip_entry_->crc32; } -off64_t ZipEntry::GetDataOffset() { - // All we have is the offset to the Local File Header, which is - // variable size, so we have to read the contents of the struct to - // figure out where the actual data starts. - - // We also need to make sure that the lengths are not so large that - // somebody trying to map the compressed or uncompressed data runs - // off the end of the mapped region. - - off64_t dir_offset = zip_archive_->dir_offset_; - int64_t lfh_offset = Le32ToHost(ptr_ + ZipArchive::kCDELocalOffset); - if (lfh_offset + ZipArchive::kLFHLen >= dir_offset) { - LOG(WARNING) << "Zip: bad LFH offset in zip"; - return -1; - } - - if (lseek64(zip_archive_->fd_, lfh_offset, SEEK_SET) != lfh_offset) { - PLOG(WARNING) << "Zip: failed seeking to LFH at offset " << lfh_offset; - return -1; - } - - uint8_t lfh_buf[ZipArchive::kLFHLen]; - ssize_t actual = TEMP_FAILURE_RETRY(read(zip_archive_->fd_, lfh_buf, sizeof(lfh_buf))); - if (actual != sizeof(lfh_buf)) { - LOG(WARNING) << "Zip: failed reading LFH from offset " << lfh_offset; - return -1; - } - - if (Le32ToHost(lfh_buf) != ZipArchive::kLFHSignature) { - LOG(WARNING) << "Zip: didn't find signature at start of LFH, offset " << lfh_offset; - return -1; - } - - uint32_t gpbf = Le16ToHost(lfh_buf + ZipArchive::kLFHGPBFlags); - if ((gpbf & ZipArchive::kGPFUnsupportedMask) != 0) { - LOG(WARNING) << "Invalid General Purpose Bit Flag: " << gpbf; - return -1; - } - - off64_t data_offset = (lfh_offset + ZipArchive::kLFHLen - + Le16ToHost(lfh_buf + ZipArchive::kLFHNameLen) - + Le16ToHost(lfh_buf + ZipArchive::kLFHExtraLen)); - if (data_offset >= dir_offset) { - LOG(WARNING) << "Zip: bad data offset " << data_offset << " in zip"; - return -1; - } - - // check lengths - - if (static_cast(data_offset + GetCompressedLength()) > dir_offset) { - LOG(WARNING) << "Zip: bad compressed length in zip " - << "(" << data_offset << " + " << GetCompressedLength() - << " > " << dir_offset << ")"; - return -1; - } - - if (GetCompressionMethod() == kCompressStored - && static_cast(data_offset + GetUncompressedLength()) > dir_offset) { - LOG(WARNING) << "Zip: bad uncompressed length in zip " - << "(" << data_offset << " + " << GetUncompressedLength() - << " > " << dir_offset << ")"; - return -1; - } - - return data_offset; -} - -static bool CopyFdToMemory(uint8_t* begin, size_t size, int in, size_t count) { - uint8_t* dst = begin; - std::vector buf(kBufSize); - while (count != 0) { - size_t bytes_to_read = (count > kBufSize) ? kBufSize : count; - ssize_t actual = TEMP_FAILURE_RETRY(read(in, &buf[0], bytes_to_read)); - if (actual != static_cast(bytes_to_read)) { - PLOG(WARNING) << "Zip: short read"; - return false; - } - memcpy(dst, &buf[0], bytes_to_read); - dst += bytes_to_read; - count -= bytes_to_read; - } - DCHECK_EQ(dst, begin + size); - return true; -} - -class ZStream { - public: - ZStream(byte* write_buf, size_t write_buf_size) { - // Initialize the zlib stream struct. - memset(&zstream_, 0, sizeof(zstream_)); - zstream_.zalloc = Z_NULL; - zstream_.zfree = Z_NULL; - zstream_.opaque = Z_NULL; - zstream_.next_in = NULL; - zstream_.avail_in = 0; - zstream_.next_out = reinterpret_cast(write_buf); - zstream_.avail_out = write_buf_size; - zstream_.data_type = Z_UNKNOWN; - } - - z_stream& Get() { - return zstream_; - } - - ~ZStream() { - inflateEnd(&zstream_); - } - private: - z_stream zstream_; -}; - -static bool InflateToMemory(uint8_t* begin, size_t size, - int in, size_t uncompressed_length, size_t compressed_length) { - uint8_t* dst = begin; - UniquePtr read_buf(new uint8_t[kBufSize]); - UniquePtr write_buf(new uint8_t[kBufSize]); - if (read_buf.get() == NULL || write_buf.get() == NULL) { - LOG(WARNING) << "Zip: failed to allocate buffer to inflate"; - return false; - } - - UniquePtr zstream(new ZStream(write_buf.get(), kBufSize)); - - // Use the undocumented "negative window bits" feature to tell zlib - // that there's no zlib header waiting for it. - int zerr = inflateInit2(&zstream->Get(), -MAX_WBITS); - if (zerr != Z_OK) { - if (zerr == Z_VERSION_ERROR) { - LOG(ERROR) << "Installed zlib is not compatible with linked version (" << ZLIB_VERSION << ")"; - } else { - LOG(WARNING) << "Call to inflateInit2 failed (zerr=" << zerr << ")"; - } - return false; - } - - size_t remaining = compressed_length; - do { - // read as much as we can - if (zstream->Get().avail_in == 0) { - size_t bytes_to_read = (remaining > kBufSize) ? kBufSize : remaining; - - ssize_t actual = TEMP_FAILURE_RETRY(read(in, read_buf.get(), bytes_to_read)); - if (actual != static_cast(bytes_to_read)) { - LOG(WARNING) << "Zip: inflate read failed (" << actual << " vs " << bytes_to_read << ")"; - return false; - } - remaining -= bytes_to_read; - zstream->Get().next_in = read_buf.get(); - zstream->Get().avail_in = bytes_to_read; - } - - // uncompress the data - zerr = inflate(&zstream->Get(), Z_NO_FLUSH); - if (zerr != Z_OK && zerr != Z_STREAM_END) { - LOG(WARNING) << "Zip: inflate zerr=" << zerr - << " (next_in=" << zstream->Get().next_in - << " avail_in=" << zstream->Get().avail_in - << " next_out=" << zstream->Get().next_out - << " avail_out=" << zstream->Get().avail_out - << ")"; - return false; - } - - // write when we're full or when we're done - if (zstream->Get().avail_out == 0 || - (zerr == Z_STREAM_END && zstream->Get().avail_out != kBufSize)) { - size_t bytes_to_write = zstream->Get().next_out - write_buf.get(); - memcpy(dst, write_buf.get(), bytes_to_write); - dst += bytes_to_write; - zstream->Get().next_out = write_buf.get(); - zstream->Get().avail_out = kBufSize; - } - } while (zerr == Z_OK); - - DCHECK_EQ(zerr, Z_STREAM_END); // other errors should've been caught - - // paranoia - if (zstream->Get().total_out != uncompressed_length) { - LOG(WARNING) << "Zip: size mismatch on inflated file (" - << zstream->Get().total_out << " vs " << uncompressed_length << ")"; - return false; - } - - DCHECK_EQ(dst, begin + size); - return true; -} bool ZipEntry::ExtractToFile(File& file, std::string* error_msg) { - uint32_t length = GetUncompressedLength(); - int result = TEMP_FAILURE_RETRY(ftruncate(file.Fd(), length)); - if (result == -1) { - *error_msg = StringPrintf("Zip: failed to ftruncate '%s' to length %ud", file.GetPath().c_str(), - length); - return false; - } - - UniquePtr map(MemMap::MapFile(length, PROT_READ | PROT_WRITE, MAP_SHARED, file.Fd(), 0, - file.GetPath().c_str(), error_msg)); - if (map.get() == NULL) { - *error_msg = StringPrintf("Zip: failed to mmap space for '%s': %s", file.GetPath().c_str(), - error_msg->c_str()); - return false; - } - - return ExtractToMemory(map->Begin(), map->Size(), error_msg); -} - -bool ZipEntry::ExtractToMemory(uint8_t* begin, size_t size, std::string* error_msg) { - // If size is zero, data offset will be meaningless, so bail out early. - if (size == 0) { - return true; - } - off64_t data_offset = GetDataOffset(); - if (data_offset == -1) { - *error_msg = StringPrintf("Zip: data_offset=%lld", data_offset); - return false; - } - if (lseek64(zip_archive_->fd_, data_offset, SEEK_SET) != data_offset) { - *error_msg = StringPrintf("Zip: lseek to data at %lld failed", data_offset); + const int32_t error = ExtractEntryToFile(handle_, zip_entry_, file.Fd()); + if (error) { + *error_msg = std::string(ErrorCodeString(error)); return false; } - // TODO: this doesn't verify the data's CRC, but probably should (especially - // for uncompressed data). - switch (GetCompressionMethod()) { - case kCompressStored: - return CopyFdToMemory(begin, size, zip_archive_->fd_, GetUncompressedLength()); - case kCompressDeflated: - return InflateToMemory(begin, size, zip_archive_->fd_, - GetUncompressedLength(), GetCompressedLength()); - default: - *error_msg = StringPrintf("Zip: unknown compression method 0x%x", GetCompressionMethod()); - return false; - } + return true; } MemMap* ZipEntry::ExtractToMemMap(const char* entry_filename, std::string* error_msg) { @@ -303,18 +54,18 @@ MemMap* ZipEntry::ExtractToMemMap(const char* entry_filename, std::string* error name += " extracted in memory from "; name += entry_filename; UniquePtr map(MemMap::MapAnonymous(name.c_str(), - NULL, - GetUncompressedLength(), + NULL, GetUncompressedLength(), PROT_READ | PROT_WRITE, error_msg)); if (map.get() == nullptr) { DCHECK(!error_msg->empty()); - return NULL; + return nullptr; } - bool success = ExtractToMemory(map->Begin(), map->Size(), error_msg); - if (!success) { - LOG(ERROR) << "Zip: Failed to extract '" << entry_filename << "' to memory"; - return NULL; + const int32_t error = ExtractToMemory(handle_, zip_entry_, + map->Begin(), map->Size()); + if (error) { + *error_msg = std::string(ErrorCodeString(error)); + return nullptr; } return map.release(); @@ -336,238 +87,47 @@ static void SetCloseOnExec(int fd) { ZipArchive* ZipArchive::Open(const char* filename, std::string* error_msg) { DCHECK(filename != nullptr); - int fd = open(filename, O_RDONLY, 0); - if (fd == -1) { - *error_msg = StringPrintf("Zip: unable to open '%s': %s", filename, strerror(errno)); - return NULL; - } - return OpenFromFd(fd, filename, error_msg); -} -ZipArchive* ZipArchive::OpenFromFd(int fd, const char* filename, std::string* error_msg) { - SetCloseOnExec(fd); - UniquePtr zip_archive(new ZipArchive(fd, filename)); - CHECK(zip_archive.get() != nullptr); - if (!zip_archive->MapCentralDirectory(error_msg)) { - zip_archive->Close(); - return NULL; - } - if (!zip_archive->Parse(error_msg)) { - zip_archive->Close(); - return NULL; + ZipArchiveHandle handle; + const int32_t error = OpenArchive(filename, &handle); + if (error) { + *error_msg = std::string(ErrorCodeString(error)); + CloseArchive(handle); + return nullptr; } - return zip_archive.release(); -} - -ZipEntry* ZipArchive::Find(const char* name) const { - DCHECK(name != NULL); - DirEntries::const_iterator it = dir_entries_.find(name); - if (it == dir_entries_.end()) { - return NULL; - } - return new ZipEntry(this, (*it).second); -} -void ZipArchive::Close() { - if (fd_ != -1) { - close(fd_); - } - fd_ = -1; - num_entries_ = 0; - dir_offset_ = 0; -} - -std::string ZipArchive::ErrorStringPrintf(const char* fmt, ...) { - va_list ap; - va_start(ap, fmt); - std::string result(StringPrintf("Zip '%s' : ", filename_.c_str())); - StringAppendV(&result, fmt, ap); - va_end(ap); - return result; + SetCloseOnExec(GetFileDescriptor(handle)); + return new ZipArchive(handle); } -// Find the zip Central Directory and memory-map it. -// -// On success, returns true after populating fields from the EOCD area: -// num_entries_ -// dir_offset_ -// dir_map_ -bool ZipArchive::MapCentralDirectory(std::string* error_msg) { - /* - * Get and test file length. - */ - off64_t file_length = lseek64(fd_, 0, SEEK_END); - if (file_length < kEOCDLen) { - *error_msg = ErrorStringPrintf("length %lld is too small to be zip", file_length); - return false; - } - - size_t read_amount = kMaxEOCDSearch; - if (file_length < off64_t(read_amount)) { - read_amount = file_length; - } - - UniquePtr scan_buf(new uint8_t[read_amount]); - CHECK(scan_buf.get() != nullptr); - - /* - * Make sure this is a Zip archive. - */ - if (lseek64(fd_, 0, SEEK_SET) != 0) { - *error_msg = ErrorStringPrintf("seek to start failed: %s", strerror(errno)); - return false; - } - - ssize_t actual = TEMP_FAILURE_RETRY(read(fd_, scan_buf.get(), sizeof(int32_t))); - if (actual != static_cast(sizeof(int32_t))) { - *error_msg = ErrorStringPrintf("couldn\'t read first signature from zip archive: %s", - strerror(errno)); - return false; - } - - unsigned int header = Le32ToHost(scan_buf.get()); - if (header != kLFHSignature) { - *error_msg = ErrorStringPrintf("not a zip archive (found 0x%x)", header); - return false; - } - - // Perform the traditional EOCD snipe hunt. - // - // We're searching for the End of Central Directory magic number, - // which appears at the start of the EOCD block. It's followed by - // 18 bytes of EOCD stuff and up to 64KB of archive comment. We - // need to read the last part of the file into a buffer, dig through - // it to find the magic number, parse some values out, and use those - // to determine the extent of the CD. - // - // We start by pulling in the last part of the file. - off64_t search_start = file_length - read_amount; - - if (lseek64(fd_, search_start, SEEK_SET) != search_start) { - *error_msg = ErrorStringPrintf("seek %lld failed: %s", search_start, strerror(errno)); - return false; - } - actual = TEMP_FAILURE_RETRY(read(fd_, scan_buf.get(), read_amount)); - if (actual != static_cast(read_amount)) { - *error_msg = ErrorStringPrintf("read %lld, expected %zd. %s", search_start, read_amount, - strerror(errno)); - return false; - } - - - // Scan backward for the EOCD magic. In an archive without a trailing - // comment, we'll find it on the first try. (We may want to consider - // doing an initial minimal read; if we don't find it, retry with a - // second read as above.) - int i; - for (i = read_amount - kEOCDLen; i >= 0; i--) { - if (scan_buf.get()[i] == 0x50 && Le32ToHost(&(scan_buf.get())[i]) == kEOCDSignature) { - break; - } - } - if (i < 0) { - *error_msg = ErrorStringPrintf("EOCD not found, not a zip file"); - return false; - } - - off64_t eocd_offset = search_start + i; - const byte* eocd_ptr = scan_buf.get() + i; - - CHECK(eocd_offset < file_length); - - // Grab the CD offset and size, and the number of entries in the - // archive. Verify that they look reasonable. - uint16_t disk_number = Le16ToHost(eocd_ptr + kEOCDDiskNumber); - uint16_t disk_with_central_dir = Le16ToHost(eocd_ptr + kEOCDDiskNumberForCD); - uint16_t num_entries = Le16ToHost(eocd_ptr + kEOCDNumEntries); - uint16_t total_num_entries = Le16ToHost(eocd_ptr + kEOCDTotalNumEntries); - uint32_t dir_size = Le32ToHost(eocd_ptr + kEOCDSize); - uint32_t dir_offset = Le32ToHost(eocd_ptr + kEOCDFileOffset); - uint16_t comment_size = Le16ToHost(eocd_ptr + kEOCDCommentSize); - - if ((uint64_t) dir_offset + (uint64_t) dir_size > (uint64_t) eocd_offset) { - *error_msg = ErrorStringPrintf("bad offsets (dir=%ud, size=%ud, eocd=%lld)", - dir_offset, dir_size, eocd_offset); - return false; - } - if (num_entries == 0) { - *error_msg = ErrorStringPrintf("empty archive?"); - return false; - } else if (num_entries != total_num_entries || disk_number != 0 || disk_with_central_dir != 0) { - *error_msg = ErrorStringPrintf("spanned archives not supported"); - return false; - } - - // Check to see if comment is a sane size - if ((comment_size > (file_length - kEOCDLen)) - || (eocd_offset > (file_length - kEOCDLen) - comment_size)) { - *error_msg = ErrorStringPrintf("comment size runs off end of file"); - return false; - } +ZipArchive* ZipArchive::OpenFromFd(int fd, const char* filename, std::string* error_msg) { + DCHECK(filename != nullptr); + DCHECK_GT(fd, 0); - // It all looks good. Create a mapping for the CD. - dir_map_.reset(MemMap::MapFile(dir_size, PROT_READ, MAP_SHARED, fd_, dir_offset, - filename_.c_str(), error_msg)); - if (dir_map_.get() == NULL) { - return false; + ZipArchiveHandle handle; + const int32_t error = OpenArchiveFd(fd, filename, &handle); + if (error) { + *error_msg = std::string(ErrorCodeString(error)); + CloseArchive(handle); + return nullptr; } - num_entries_ = num_entries; - dir_offset_ = dir_offset; - return true; + SetCloseOnExec(GetFileDescriptor(handle)); + return new ZipArchive(handle); } -bool ZipArchive::Parse(std::string* error_msg) { - const byte* cd_ptr = dir_map_->Begin(); - size_t cd_length = dir_map_->Size(); +ZipEntry* ZipArchive::Find(const char* name, std::string* error_msg) const { + DCHECK(name != nullptr); - // Walk through the central directory, adding entries to the hash - // table and verifying values. - const byte* ptr = cd_ptr; - for (int i = 0; i < num_entries_; i++) { - if (Le32ToHost(ptr) != kCDESignature) { - *error_msg = ErrorStringPrintf("missed a central dir sig (at %d)", i); - return false; - } - if (ptr + kCDELen > cd_ptr + cd_length) { - *error_msg = ErrorStringPrintf("ran off the end (at %d)", i); - return false; - } - - int64_t local_hdr_offset = Le32ToHost(ptr + kCDELocalOffset); - if (local_hdr_offset >= dir_offset_) { - *error_msg = ErrorStringPrintf("bad LFH offset %lld at entry %d", local_hdr_offset, i); - return false; - } - - uint16_t gpbf = Le16ToHost(ptr + kCDEGPBFlags); - if ((gpbf & kGPFUnsupportedMask) != 0) { - *error_msg = ErrorStringPrintf("invalid general purpose bit flag %x", gpbf); - return false; - } - - uint16_t name_len = Le16ToHost(ptr + kCDENameLen); - uint16_t extra_len = Le16ToHost(ptr + kCDEExtraLen); - uint16_t comment_len = Le16ToHost(ptr + kCDECommentLen); - - // add the CDE filename to the hash table - const char* name = reinterpret_cast(ptr + kCDELen); - - // Check name for NULL characters - if (memchr(name, 0, name_len) != NULL) { - *error_msg = ErrorStringPrintf("filename contains NUL byte"); - return false; - } - - dir_entries_.Put(StringPiece(name, name_len), ptr); - ptr += kCDELen + name_len + extra_len + comment_len; - if (ptr > cd_ptr + cd_length) { - *error_msg = ErrorStringPrintf("bad CD advance (%p vs %p) at entry %d", - ptr, cd_ptr + cd_length, i); - return false; - } + // Resist the urge to delete the space. <: is a bigraph sequence. + UniquePtr< ::ZipEntry> zip_entry(new ::ZipEntry); + const int32_t error = FindEntry(handle_, name, zip_entry.get()); + if (error) { + *error_msg = std::string(ErrorCodeString(error)); + return nullptr; } - return true; + + return new ZipEntry(handle_, zip_entry.release()); } } // namespace art diff --git a/runtime/zip_archive.h b/runtime/zip_archive.h index 8ff952baab2..1f48e0a0049 100644 --- a/runtime/zip_archive.h +++ b/runtime/zip_archive.h @@ -18,8 +18,8 @@ #define ART_RUNTIME_ZIP_ARCHIVE_H_ #include -#include #include +#include #include "base/logging.h" #include "base/stringpiece.h" @@ -38,33 +38,17 @@ class MemMap; class ZipEntry { public: bool ExtractToFile(File& file, std::string* error_msg); - bool ExtractToMemory(uint8_t* begin, size_t size, std::string* error_msg); MemMap* ExtractToMemMap(const char* entry_filename, std::string* error_msg); uint32_t GetUncompressedLength(); uint32_t GetCrc32(); private: - ZipEntry(const ZipArchive* zip_archive, const byte* ptr) : zip_archive_(zip_archive), ptr_(ptr) {} + ZipEntry(ZipArchiveHandle handle, + ::ZipEntry* zip_entry) : handle_(handle), zip_entry_(zip_entry) {} - // Zip compression methods - enum { - kCompressStored = 0, // no compression - kCompressDeflated = 8, // standard deflate - }; - - // kCompressStored, kCompressDeflated, ... - uint16_t GetCompressionMethod(); - - uint32_t GetCompressedLength(); - - // returns -1 on error - off64_t GetDataOffset(); - - const ZipArchive* zip_archive_; - - // pointer to zip entry within central directory - const byte* ptr_; + ZipArchiveHandle handle_; + ::ZipEntry* const zip_entry_; friend class ZipArchive; DISALLOW_COPY_AND_ASSIGN(ZipEntry); @@ -72,74 +56,23 @@ class ZipEntry { class ZipArchive { public: - // Zip file constants. - static const uint32_t kEOCDSignature = 0x06054b50; - static const int32_t kEOCDLen = 22; - static const int32_t kEOCDDiskNumber = 4; // number of the current disk - static const int32_t kEOCDDiskNumberForCD = 6; // disk number with the Central Directory - static const int32_t kEOCDNumEntries = 8; // offset to #of entries in file - static const int32_t kEOCDTotalNumEntries = 10; // offset to total #of entries in spanned archives - static const int32_t kEOCDSize = 12; // size of the central directory - static const int32_t kEOCDFileOffset = 16; // offset to central directory - static const int32_t kEOCDCommentSize = 20; // offset to the length of the file comment - - static const int32_t kMaxCommentLen = 65535; // longest possible in uint16_t - static const int32_t kMaxEOCDSearch = (kMaxCommentLen + kEOCDLen); - - static const uint32_t kLFHSignature = 0x04034b50; - static const int32_t kLFHLen = 30; // excluding variable-len fields - static const int32_t kLFHGPBFlags = 6; // offset to GPB flags - static const int32_t kLFHNameLen = 26; // offset to filename length - static const int32_t kLFHExtraLen = 28; // offset to extra length - - static const uint32_t kCDESignature = 0x02014b50; - static const int32_t kCDELen = 46; // excluding variable-len fields - static const int32_t kCDEGPBFlags = 8; // offset to GPB flags - static const int32_t kCDEMethod = 10; // offset to compression method - static const int32_t kCDEModWhen = 12; // offset to modification timestamp - static const int32_t kCDECRC = 16; // offset to entry CRC - static const int32_t kCDECompLen = 20; // offset to compressed length - static const int32_t kCDEUncompLen = 24; // offset to uncompressed length - static const int32_t kCDENameLen = 28; // offset to filename length - static const int32_t kCDEExtraLen = 30; // offset to extra length - static const int32_t kCDECommentLen = 32; // offset to comment length - static const int32_t kCDELocalOffset = 42; // offset to local hdr - - // General Purpose Bit Flag - static const int32_t kGPFEncryptedFlag = (1 << 0); - static const int32_t kGPFUnsupportedMask = (kGPFEncryptedFlag); - // return new ZipArchive instance on success, NULL on error. static ZipArchive* Open(const char* filename, std::string* error_msg); static ZipArchive* OpenFromFd(int fd, const char* filename, std::string* error_msg); - ZipEntry* Find(const char* name) const; + ZipEntry* Find(const char* name, std::string* error_msg) const; ~ZipArchive() { - Close(); + CloseArchive(handle_); } private: - explicit ZipArchive(int fd, const char* filename) - : fd_(fd), num_entries_(0), dir_offset_(0), filename_(filename) {} - - bool MapCentralDirectory(std::string* error_msg); - bool Parse(std::string* error_msg); - void Close(); - std::string ErrorStringPrintf(const char* fmt, ...) - __attribute__((__format__(__printf__, 2, 3))) COLD_ATTR; - - int fd_; - uint16_t num_entries_; - off64_t dir_offset_; - UniquePtr dir_map_; - typedef SafeMap DirEntries; - DirEntries dir_entries_; - // Containing file for error reporting. - const std::string filename_; + explicit ZipArchive(ZipArchiveHandle handle) : handle_(handle) {} friend class ZipEntry; + ZipArchiveHandle handle_; + DISALLOW_COPY_AND_ASSIGN(ZipArchive); }; diff --git a/runtime/zip_archive_test.cc b/runtime/zip_archive_test.cc index 622dc89587c..16394b01280 100644 --- a/runtime/zip_archive_test.cc +++ b/runtime/zip_archive_test.cc @@ -19,6 +19,7 @@ #include #include #include +#include #include "UniquePtr.h" #include "common_test.h" @@ -33,8 +34,9 @@ TEST_F(ZipArchiveTest, FindAndExtract) { UniquePtr zip_archive(ZipArchive::Open(GetLibCoreDexFileName().c_str(), &error_msg)); ASSERT_TRUE(zip_archive.get() != false) << error_msg; ASSERT_TRUE(error_msg.empty()); - UniquePtr zip_entry(zip_archive->Find("classes.dex")); + UniquePtr zip_entry(zip_archive->Find("classes.dex", &error_msg)); ASSERT_TRUE(zip_entry.get() != false); + ASSERT_TRUE(error_msg.empty()); ScratchFile tmp; ASSERT_NE(-1, tmp.GetFd()); From 31c2aac7137b69d5622eea09597500731fbee2ef Mon Sep 17 00:00:00 2001 From: Vladimir Marko Date: Mon, 9 Dec 2013 16:31:19 +0000 Subject: [PATCH 0255/2402] Rename ClobberCalleeSave to *Caller*, fix it for x86. Change-Id: I6a72703a11985e2753fa9b4520c375a164301433 --- compiler/dex/quick/arm/call_arm.cc | 10 +++---- compiler/dex/quick/arm/codegen_arm.h | 2 +- compiler/dex/quick/arm/fp_arm.cc | 2 +- compiler/dex/quick/arm/target_arm.cc | 2 +- compiler/dex/quick/gen_common.cc | 4 +-- compiler/dex/quick/gen_invoke.cc | 38 +++++++++++++------------- compiler/dex/quick/mips/call_mips.cc | 2 +- compiler/dex/quick/mips/codegen_mips.h | 2 +- compiler/dex/quick/mips/target_mips.cc | 2 +- compiler/dex/quick/mir_to_lir.h | 2 +- compiler/dex/quick/x86/codegen_x86.h | 2 +- compiler/dex/quick/x86/target_x86.cc | 3 +- 12 files changed, 36 insertions(+), 35 deletions(-) diff --git a/compiler/dex/quick/arm/call_arm.cc b/compiler/dex/quick/arm/call_arm.cc index 51aca8540c8..23ea40712fa 100644 --- a/compiler/dex/quick/arm/call_arm.cc +++ b/compiler/dex/quick/arm/call_arm.cc @@ -434,7 +434,7 @@ void ArmMir2Lir::GenFillArrayData(uint32_t table_offset, RegLocation rl_src) { rARM_LR); // Materialize a pointer to the fill data image NewLIR3(kThumb2Adr, r1, 0, WrapPointer(tab_rec)); - ClobberCalleeSave(); + ClobberCallerSave(); LIR* call_inst = OpReg(kOpBlx, rARM_LR); MarkSafepointPC(call_inst); } @@ -471,7 +471,7 @@ void ArmMir2Lir::GenMonitorEnter(int opt_flags, RegLocation rl_src) { // TODO: move to a slow path. // Go expensive route - artLockObjectFromCode(obj); LoadWordDisp(rARM_SELF, QUICK_ENTRYPOINT_OFFSET(pLockObject).Int32Value(), rARM_LR); - ClobberCalleeSave(); + ClobberCallerSave(); LIR* call_inst = OpReg(kOpBlx, rARM_LR); MarkSafepointPC(call_inst); @@ -490,7 +490,7 @@ void ArmMir2Lir::GenMonitorEnter(int opt_flags, RegLocation rl_src) { OpIT(kCondNe, "T"); // Go expensive route - artLockObjectFromCode(self, obj); LoadWordDisp/*ne*/(rARM_SELF, QUICK_ENTRYPOINT_OFFSET(pLockObject).Int32Value(), rARM_LR); - ClobberCalleeSave(); + ClobberCallerSave(); LIR* call_inst = OpReg(kOpBlx/*ne*/, rARM_LR); MarkSafepointPC(call_inst); GenMemBarrier(kLoadLoad); @@ -530,7 +530,7 @@ void ArmMir2Lir::GenMonitorExit(int opt_flags, RegLocation rl_src) { // TODO: move to a slow path. // Go expensive route - artUnlockObjectFromCode(obj); LoadWordDisp(rARM_SELF, QUICK_ENTRYPOINT_OFFSET(pUnlockObject).Int32Value(), rARM_LR); - ClobberCalleeSave(); + ClobberCallerSave(); LIR* call_inst = OpReg(kOpBlx, rARM_LR); MarkSafepointPC(call_inst); @@ -549,7 +549,7 @@ void ArmMir2Lir::GenMonitorExit(int opt_flags, RegLocation rl_src) { StoreWordDisp/*eq*/(r0, mirror::Object::MonitorOffset().Int32Value(), r3); // Go expensive route - UnlockObjectFromCode(obj); LoadWordDisp/*ne*/(rARM_SELF, QUICK_ENTRYPOINT_OFFSET(pUnlockObject).Int32Value(), rARM_LR); - ClobberCalleeSave(); + ClobberCallerSave(); LIR* call_inst = OpReg(kOpBlx/*ne*/, rARM_LR); MarkSafepointPC(call_inst); GenMemBarrier(kStoreLoad); diff --git a/compiler/dex/quick/arm/codegen_arm.h b/compiler/dex/quick/arm/codegen_arm.h index de3223a106c..25ddc9451d2 100644 --- a/compiler/dex/quick/arm/codegen_arm.h +++ b/compiler/dex/quick/arm/codegen_arm.h @@ -60,7 +60,7 @@ class ArmMir2Lir : public Mir2Lir { uint32_t FpRegMask(); uint64_t GetRegMaskCommon(int reg); void AdjustSpillMask(); - void ClobberCalleeSave(); + void ClobberCallerSave(); void FlushReg(int reg); void FlushRegWide(int reg1, int reg2); void FreeCallTemps(); diff --git a/compiler/dex/quick/arm/fp_arm.cc b/compiler/dex/quick/arm/fp_arm.cc index 1575ece532d..dc2e0d0c794 100644 --- a/compiler/dex/quick/arm/fp_arm.cc +++ b/compiler/dex/quick/arm/fp_arm.cc @@ -315,7 +315,7 @@ bool ArmMir2Lir::GenInlinedSqrt(CallInfo* info) { S2d(rl_result.low_reg, rl_result.high_reg)); NewLIR0(kThumb2Fmstat); branch = NewLIR2(kThumbBCond, 0, kArmCondEq); - ClobberCalleeSave(); + ClobberCallerSave(); LockCallTemps(); // Using fixed registers int r_tgt = LoadHelper(QUICK_ENTRYPOINT_OFFSET(pSqrt)); NewLIR3(kThumb2Fmrrd, r0, r1, S2d(rl_src.low_reg, rl_src.high_reg)); diff --git a/compiler/dex/quick/arm/target_arm.cc b/compiler/dex/quick/arm/target_arm.cc index 52aba9b4dfd..48c9af5a166 100644 --- a/compiler/dex/quick/arm/target_arm.cc +++ b/compiler/dex/quick/arm/target_arm.cc @@ -653,7 +653,7 @@ bool ArmMir2Lir::IsFpReg(int reg) { } /* Clobber all regs that might be used by an external C call */ -void ArmMir2Lir::ClobberCalleeSave() { +void ArmMir2Lir::ClobberCallerSave() { Clobber(r0); Clobber(r1); Clobber(r2); diff --git a/compiler/dex/quick/gen_common.cc b/compiler/dex/quick/gen_common.cc index df6493dc779..a426cc77dea 100644 --- a/compiler/dex/quick/gen_common.cc +++ b/compiler/dex/quick/gen_common.cc @@ -611,7 +611,7 @@ void Mir2Lir::HandleThrowLaunchPads() { default: LOG(FATAL) << "Unexpected throw kind: " << lab->operands[0]; } - ClobberCalleeSave(); + ClobberCallerSave(); int r_tgt = CallHelperSetup(func_offset); CallHelper(r_tgt, func_offset, true /* MarkSafepointPC */); } @@ -1026,7 +1026,7 @@ void Mir2Lir::GenInstanceofCallingHelper(bool needs_access_check, bool type_know } } // TODO: only clobber when type isn't final? - ClobberCalleeSave(); + ClobberCallerSave(); /* branch targets here */ LIR* target = NewLIR0(kPseudoTargetLabel); StoreValue(rl_dest, rl_result); diff --git a/compiler/dex/quick/gen_invoke.cc b/compiler/dex/quick/gen_invoke.cc index 9992499607d..e66d4ea52cb 100644 --- a/compiler/dex/quick/gen_invoke.cc +++ b/compiler/dex/quick/gen_invoke.cc @@ -62,14 +62,14 @@ LIR* Mir2Lir::CallHelper(int r_tgt, ThreadOffset helper_offset, bool safepoint_p void Mir2Lir::CallRuntimeHelperImm(ThreadOffset helper_offset, int arg0, bool safepoint_pc) { int r_tgt = CallHelperSetup(helper_offset); LoadConstant(TargetReg(kArg0), arg0); - ClobberCalleeSave(); + ClobberCallerSave(); CallHelper(r_tgt, helper_offset, safepoint_pc); } void Mir2Lir::CallRuntimeHelperReg(ThreadOffset helper_offset, int arg0, bool safepoint_pc) { int r_tgt = CallHelperSetup(helper_offset); OpRegCopy(TargetReg(kArg0), arg0); - ClobberCalleeSave(); + ClobberCallerSave(); CallHelper(r_tgt, helper_offset, safepoint_pc); } @@ -81,7 +81,7 @@ void Mir2Lir::CallRuntimeHelperRegLocation(ThreadOffset helper_offset, RegLocati } else { LoadValueDirectWideFixed(arg0, TargetReg(kArg0), TargetReg(kArg1)); } - ClobberCalleeSave(); + ClobberCallerSave(); CallHelper(r_tgt, helper_offset, safepoint_pc); } @@ -90,7 +90,7 @@ void Mir2Lir::CallRuntimeHelperImmImm(ThreadOffset helper_offset, int arg0, int int r_tgt = CallHelperSetup(helper_offset); LoadConstant(TargetReg(kArg0), arg0); LoadConstant(TargetReg(kArg1), arg1); - ClobberCalleeSave(); + ClobberCallerSave(); CallHelper(r_tgt, helper_offset, safepoint_pc); } @@ -103,7 +103,7 @@ void Mir2Lir::CallRuntimeHelperImmRegLocation(ThreadOffset helper_offset, int ar LoadValueDirectWideFixed(arg1, TargetReg(kArg1), TargetReg(kArg2)); } LoadConstant(TargetReg(kArg0), arg0); - ClobberCalleeSave(); + ClobberCallerSave(); CallHelper(r_tgt, helper_offset, safepoint_pc); } @@ -112,7 +112,7 @@ void Mir2Lir::CallRuntimeHelperRegLocationImm(ThreadOffset helper_offset, RegLoc int r_tgt = CallHelperSetup(helper_offset); LoadValueDirectFixed(arg0, TargetReg(kArg0)); LoadConstant(TargetReg(kArg1), arg1); - ClobberCalleeSave(); + ClobberCallerSave(); CallHelper(r_tgt, helper_offset, safepoint_pc); } @@ -121,7 +121,7 @@ void Mir2Lir::CallRuntimeHelperImmReg(ThreadOffset helper_offset, int arg0, int int r_tgt = CallHelperSetup(helper_offset); OpRegCopy(TargetReg(kArg1), arg1); LoadConstant(TargetReg(kArg0), arg0); - ClobberCalleeSave(); + ClobberCallerSave(); CallHelper(r_tgt, helper_offset, safepoint_pc); } @@ -130,7 +130,7 @@ void Mir2Lir::CallRuntimeHelperRegImm(ThreadOffset helper_offset, int arg0, int int r_tgt = CallHelperSetup(helper_offset); OpRegCopy(TargetReg(kArg0), arg0); LoadConstant(TargetReg(kArg1), arg1); - ClobberCalleeSave(); + ClobberCallerSave(); CallHelper(r_tgt, helper_offset, safepoint_pc); } @@ -138,7 +138,7 @@ void Mir2Lir::CallRuntimeHelperImmMethod(ThreadOffset helper_offset, int arg0, b int r_tgt = CallHelperSetup(helper_offset); LoadCurrMethodDirect(TargetReg(kArg1)); LoadConstant(TargetReg(kArg0), arg0); - ClobberCalleeSave(); + ClobberCallerSave(); CallHelper(r_tgt, helper_offset, safepoint_pc); } @@ -168,7 +168,7 @@ void Mir2Lir::CallRuntimeHelperRegLocationRegLocation(ThreadOffset helper_offset LoadValueDirectWideFixed(arg1, arg1.fp ? TargetReg(kFArg2) : TargetReg(kArg2), arg1.fp ? TargetReg(kFArg3) : TargetReg(kArg3)); } } - ClobberCalleeSave(); + ClobberCallerSave(); CallHelper(r_tgt, helper_offset, safepoint_pc); } @@ -178,7 +178,7 @@ void Mir2Lir::CallRuntimeHelperRegReg(ThreadOffset helper_offset, int arg0, int DCHECK_NE(TargetReg(kArg0), arg1); // check copy into arg0 won't clobber arg1 OpRegCopy(TargetReg(kArg0), arg0); OpRegCopy(TargetReg(kArg1), arg1); - ClobberCalleeSave(); + ClobberCallerSave(); CallHelper(r_tgt, helper_offset, safepoint_pc); } @@ -189,7 +189,7 @@ void Mir2Lir::CallRuntimeHelperRegRegImm(ThreadOffset helper_offset, int arg0, i OpRegCopy(TargetReg(kArg0), arg0); OpRegCopy(TargetReg(kArg1), arg1); LoadConstant(TargetReg(kArg2), arg2); - ClobberCalleeSave(); + ClobberCallerSave(); CallHelper(r_tgt, helper_offset, safepoint_pc); } @@ -199,7 +199,7 @@ void Mir2Lir::CallRuntimeHelperImmMethodRegLocation(ThreadOffset helper_offset, LoadValueDirectFixed(arg2, TargetReg(kArg2)); LoadCurrMethodDirect(TargetReg(kArg1)); LoadConstant(TargetReg(kArg0), arg0); - ClobberCalleeSave(); + ClobberCallerSave(); CallHelper(r_tgt, helper_offset, safepoint_pc); } @@ -209,7 +209,7 @@ void Mir2Lir::CallRuntimeHelperImmMethodImm(ThreadOffset helper_offset, int arg0 LoadCurrMethodDirect(TargetReg(kArg1)); LoadConstant(TargetReg(kArg2), arg2); LoadConstant(TargetReg(kArg0), arg0); - ClobberCalleeSave(); + ClobberCallerSave(); CallHelper(r_tgt, helper_offset, safepoint_pc); } @@ -225,7 +225,7 @@ void Mir2Lir::CallRuntimeHelperImmRegLocationRegLocation(ThreadOffset helper_off LoadValueDirectWideFixed(arg2, TargetReg(kArg2), TargetReg(kArg3)); } LoadConstant(TargetReg(kArg0), arg0); - ClobberCalleeSave(); + ClobberCallerSave(); CallHelper(r_tgt, helper_offset, safepoint_pc); } @@ -240,7 +240,7 @@ void Mir2Lir::CallRuntimeHelperRegLocationRegLocationRegLocation(ThreadOffset he LoadValueDirectFixed(arg1, TargetReg(kArg1)); DCHECK_EQ(arg1.wide, 0U); LoadValueDirectFixed(arg2, TargetReg(kArg2)); - ClobberCalleeSave(); + ClobberCallerSave(); CallHelper(r_tgt, helper_offset, safepoint_pc); } @@ -1083,7 +1083,7 @@ bool Mir2Lir::GenInlinedIndexOf(CallInfo* info, bool zero_based) { // TODO - add Mips implementation return false; } - ClobberCalleeSave(); + ClobberCallerSave(); LockCallTemps(); // Using fixed registers int reg_ptr = TargetReg(kArg0); int reg_char = TargetReg(kArg1); @@ -1126,7 +1126,7 @@ bool Mir2Lir::GenInlinedStringCompareTo(CallInfo* info) { // TODO - add Mips implementation return false; } - ClobberCalleeSave(); + ClobberCallerSave(); LockCallTemps(); // Using fixed registers int reg_this = TargetReg(kArg0); int reg_cmp = TargetReg(kArg1); @@ -1341,7 +1341,7 @@ void Mir2Lir::GenInvoke(CallInfo* info) { } MarkSafepointPC(call_inst); - ClobberCalleeSave(); + ClobberCallerSave(); if (info->result.location != kLocInvalid) { // We have a following MOVE_RESULT - do it now. if (info->result.wide) { diff --git a/compiler/dex/quick/mips/call_mips.cc b/compiler/dex/quick/mips/call_mips.cc index 18c8cf87f2c..21d55633df3 100644 --- a/compiler/dex/quick/mips/call_mips.cc +++ b/compiler/dex/quick/mips/call_mips.cc @@ -253,7 +253,7 @@ void MipsMir2Lir::GenFillArrayData(DexOffset table_offset, RegLocation rl_src) { NewLIR4(kMipsDelta, rMIPS_ARG1, 0, WrapPointer(base_label), WrapPointer(tab_rec)); // And go... - ClobberCalleeSave(); + ClobberCallerSave(); LIR* call_inst = OpReg(kOpBlx, r_tgt); // ( array*, fill_data* ) MarkSafepointPC(call_inst); } diff --git a/compiler/dex/quick/mips/codegen_mips.h b/compiler/dex/quick/mips/codegen_mips.h index 5dda44563b9..450a44f7882 100644 --- a/compiler/dex/quick/mips/codegen_mips.h +++ b/compiler/dex/quick/mips/codegen_mips.h @@ -61,7 +61,7 @@ class MipsMir2Lir : public Mir2Lir { uint32_t FpRegMask(); uint64_t GetRegMaskCommon(int reg); void AdjustSpillMask(); - void ClobberCalleeSave(); + void ClobberCallerSave(); void FlushReg(int reg); void FlushRegWide(int reg1, int reg2); void FreeCallTemps(); diff --git a/compiler/dex/quick/mips/target_mips.cc b/compiler/dex/quick/mips/target_mips.cc index 9c598e6bee0..869706fbe06 100644 --- a/compiler/dex/quick/mips/target_mips.cc +++ b/compiler/dex/quick/mips/target_mips.cc @@ -346,7 +346,7 @@ bool MipsMir2Lir::IsFpReg(int reg) { } /* Clobber all regs that might be used by an external C call */ -void MipsMir2Lir::ClobberCalleeSave() { +void MipsMir2Lir::ClobberCallerSave() { Clobber(r_ZERO); Clobber(r_AT); Clobber(r_V0); diff --git a/compiler/dex/quick/mir_to_lir.h b/compiler/dex/quick/mir_to_lir.h index fae6c4c9a81..06cec537383 100644 --- a/compiler/dex/quick/mir_to_lir.h +++ b/compiler/dex/quick/mir_to_lir.h @@ -621,7 +621,7 @@ class Mir2Lir : public Backend { virtual uint32_t FpRegMask() = 0; virtual uint64_t GetRegMaskCommon(int reg) = 0; virtual void AdjustSpillMask() = 0; - virtual void ClobberCalleeSave() = 0; + virtual void ClobberCallerSave() = 0; virtual void FlushReg(int reg) = 0; virtual void FlushRegWide(int reg1, int reg2) = 0; virtual void FreeCallTemps() = 0; diff --git a/compiler/dex/quick/x86/codegen_x86.h b/compiler/dex/quick/x86/codegen_x86.h index ffe2d67cf00..805ef29f99f 100644 --- a/compiler/dex/quick/x86/codegen_x86.h +++ b/compiler/dex/quick/x86/codegen_x86.h @@ -61,7 +61,7 @@ class X86Mir2Lir : public Mir2Lir { uint32_t FpRegMask(); uint64_t GetRegMaskCommon(int reg); void AdjustSpillMask(); - void ClobberCalleeSave(); + void ClobberCallerSave(); void FlushReg(int reg); void FlushRegWide(int reg1, int reg2); void FreeCallTemps(); diff --git a/compiler/dex/quick/x86/target_x86.cc b/compiler/dex/quick/x86/target_x86.cc index 878fa769b6b..cbd0f15f0bb 100644 --- a/compiler/dex/quick/x86/target_x86.cc +++ b/compiler/dex/quick/x86/target_x86.cc @@ -350,10 +350,11 @@ bool X86Mir2Lir::IsFpReg(int reg) { } /* Clobber all regs that might be used by an external C call */ -void X86Mir2Lir::ClobberCalleeSave() { +void X86Mir2Lir::ClobberCallerSave() { Clobber(rAX); Clobber(rCX); Clobber(rDX); + Clobber(rBX); } RegLocation X86Mir2Lir::GetReturnWideAlt() { From fa31b3c382f5202bc2b07c81108627e476cf753d Mon Sep 17 00:00:00 2001 From: Kenny Root Date: Mon, 9 Dec 2013 13:51:32 -0800 Subject: [PATCH 0256/2402] Add some functions to the unitialized VM handling Only the Class#forName(String) call was handled during the compilation stage, but VMClassLoader#loadClass and VMClassLoader#findLoadedClass gets called through various other channels. Add these so we cover most of the use cases without going too far up the call stack. This was encountered during compilation of code that did: MyClass.class.getClassLoader().loadClass(...) during an attempt to avoid class initialization. Note that the existing Class#forName handling doesn't appear to ensure the class is initialized like would normally happen when Class#forName is called in the running VM. This is a correctness issue, but is left for another change to correct. Change-Id: I14628a1d1d5a5dd1a885cbd24455a73bb0531489 --- runtime/interpreter/interpreter_common.cc | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/runtime/interpreter/interpreter_common.cc b/runtime/interpreter/interpreter_common.cc index c9756ac04de..ca066b66d0f 100644 --- a/runtime/interpreter/interpreter_common.cc +++ b/runtime/interpreter/interpreter_common.cc @@ -231,7 +231,10 @@ static void UnstartedRuntimeInvoke(Thread* self, MethodHelper& mh, // In a runtime that's not started we intercept certain methods to avoid complicated dependency // problems in core libraries. std::string name(PrettyMethod(shadow_frame->GetMethod())); - if (name == "java.lang.Class java.lang.Class.forName(java.lang.String)") { + if (name == "java.lang.Class java.lang.Class.forName(java.lang.String)" + || name == "java.lang.Class java.lang.VMClassLoader.loadClass(java.lang.String, boolean)") { + // TODO Class#forName should actually call Class::EnsureInitialized always. Support for the + // other variants that take more arguments should also be added. std::string descriptor(DotToDescriptor(shadow_frame->GetVRegReference(arg_offset)->AsString()->ToModifiedUtf8().c_str())); SirtRef class_loader(self, nullptr); // shadow_frame.GetMethod()->GetDeclaringClass()->GetClassLoader(); @@ -240,6 +243,13 @@ static void UnstartedRuntimeInvoke(Thread* self, MethodHelper& mh, CHECK(found != NULL) << "Class.forName failed in un-started runtime for class: " << PrettyDescriptor(descriptor); result->SetL(found); + } else if (name == "java.lang.Class java.lang.VMClassLoader.findLoadedClass(java.lang.ClassLoader, java.lang.String)") { + SirtRef class_loader(self, down_cast(shadow_frame->GetVRegReference(arg_offset))); + std::string descriptor(DotToDescriptor(shadow_frame->GetVRegReference(arg_offset + 1)->AsString()->ToModifiedUtf8().c_str())); + + Class* found = Runtime::Current()->GetClassLinker()->FindClass(descriptor.c_str(), + class_loader); + result->SetL(found); } else if (name == "java.lang.Object java.lang.Class.newInstance()") { Class* klass = shadow_frame->GetVRegReference(arg_offset)->AsClass(); ArtMethod* c = klass->FindDeclaredDirectMethod("", "()V"); From 7bf82af01ec250a4ed2cee03a0e51d179fa820f9 Mon Sep 17 00:00:00 2001 From: Mathieu Chartier Date: Fri, 6 Dec 2013 16:51:45 -0800 Subject: [PATCH 0257/2402] Fix memory usage regression and clean up collector changing code. Memory usage regressed since we didn't properly update concurrent_start_bytes_ when changing collectors. Bug: 12034247 Change-Id: I1c69e71cd2919e0d3bf75485a4ac0b0aeca59278 --- runtime/gc/collector_type.h | 2 + runtime/gc/heap-inl.h | 8 ++-- runtime/gc/heap.cc | 82 ++++++++++++++++++++++++------------- runtime/gc/heap.h | 8 ++-- 4 files changed, 65 insertions(+), 35 deletions(-) diff --git a/runtime/gc/collector_type.h b/runtime/gc/collector_type.h index ba3cad6972e..06395cf687b 100644 --- a/runtime/gc/collector_type.h +++ b/runtime/gc/collector_type.h @@ -24,6 +24,8 @@ namespace gc { // Which types of collections are able to be performed. enum CollectorType { + // No collector selected. + kCollectorTypeNone, // Non concurrent mark-sweep. kCollectorTypeMS, // Concurrent mark-sweep. diff --git a/runtime/gc/heap-inl.h b/runtime/gc/heap-inl.h index 6e9b04ada19..08ab6b8c6a6 100644 --- a/runtime/gc/heap-inl.h +++ b/runtime/gc/heap-inl.h @@ -93,7 +93,7 @@ inline mirror::Object* Heap::AllocObjectWithAllocator(Thread* self, mirror::Clas } else { DCHECK(!Dbg::IsAllocTrackingEnabled()); } - if (AllocatorHasConcurrentGC(allocator)) { + if (concurrent_gc_) { CheckConcurrentGC(self, new_num_bytes_allocated, obj); } if (kIsDebugBuild) { @@ -199,9 +199,11 @@ inline bool Heap::IsOutOfMemoryOnAllocation(size_t alloc_size, bool grow) { if (!concurrent_gc_) { if (!grow) { return true; - } else { - max_allowed_footprint_ = new_footprint; } + // TODO: Grow for allocation is racy, fix it. + VLOG(heap) << "Growing heap from " << PrettySize(max_allowed_footprint_) << " to " + << PrettySize(new_footprint) << " for a " << PrettySize(alloc_size) << " allocation"; + max_allowed_footprint_ = new_footprint; } } return false; diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc index 1e3689bbdaa..f92a8212f0d 100644 --- a/runtime/gc/heap.cc +++ b/runtime/gc/heap.cc @@ -75,12 +75,13 @@ static constexpr size_t kMinConcurrentRemainingBytes = 128 * KB; Heap::Heap(size_t initial_size, size_t growth_limit, size_t min_free, size_t max_free, double target_utilization, size_t capacity, const std::string& image_file_name, - CollectorType collector_type, size_t parallel_gc_threads, size_t conc_gc_threads, - bool low_memory_mode, size_t long_pause_log_threshold, size_t long_gc_log_threshold, - bool ignore_max_footprint) + CollectorType post_zygote_collector_type, size_t parallel_gc_threads, + size_t conc_gc_threads, bool low_memory_mode, size_t long_pause_log_threshold, + size_t long_gc_log_threshold, bool ignore_max_footprint) : non_moving_space_(nullptr), - concurrent_gc_(collector_type == gc::kCollectorTypeCMS), - collector_type_(collector_type), + concurrent_gc_(false), + collector_type_(kCollectorTypeNone), + post_zygote_collector_type_(post_zygote_collector_type), parallel_gc_threads_(parallel_gc_threads), conc_gc_threads_(conc_gc_threads), low_memory_mode_(low_memory_mode), @@ -109,8 +110,7 @@ Heap::Heap(size_t initial_size, size_t growth_limit, size_t min_free, size_t max last_process_state_id_(NULL), // Initially assume we perceive jank in case the process state is never updated. process_state_(kProcessStateJankPerceptible), - concurrent_start_bytes_(concurrent_gc_ ? initial_size - kMinConcurrentRemainingBytes - : std::numeric_limits::max()), + concurrent_start_bytes_(std::numeric_limits::max()), total_bytes_freed_ever_(0), total_objects_freed_ever_(0), num_bytes_allocated_(0), @@ -155,8 +155,12 @@ Heap::Heap(size_t initial_size, size_t growth_limit, size_t min_free, size_t max // If we aren't the zygote, switch to the default non zygote allocator. This may update the // entrypoints. if (!Runtime::Current()->IsZygote()) { - ChangeCollector(collector_type_); + ChangeCollector(post_zygote_collector_type_); + } else { + // We are the zygote, use bump pointer allocation + semi space collector. + ChangeCollector(kCollectorTypeSS); } + live_bitmap_.reset(new accounting::HeapBitmap(this)); mark_bitmap_.reset(new accounting::HeapBitmap(this)); // Requested begin for the alloc space, to follow the mapped image and oat files @@ -262,9 +266,6 @@ Heap::Heap(size_t initial_size, size_t growth_limit, size_t min_free, size_t max garbage_collectors_.push_back(new collector::PartialMarkSweep(this, concurrent)); garbage_collectors_.push_back(new collector::StickyMarkSweep(this, concurrent)); } - gc_plan_.push_back(collector::kGcTypeSticky); - gc_plan_.push_back(collector::kGcTypePartial); - gc_plan_.push_back(collector::kGcTypeFull); if (kMovingCollector) { // TODO: Clean this up. semi_space_collector_ = new collector::SemiSpace(this); @@ -1085,22 +1086,46 @@ void Heap::GetReferringObjects(mirror::Object* o, int32_t max_count, void Heap::CollectGarbage(bool clear_soft_references) { // Even if we waited for a GC we still need to do another GC since weaks allocated during the // last GC will not have necessarily been cleared. - CollectGarbageInternal(collector::kGcTypeFull, kGcCauseExplicit, clear_soft_references); + CollectGarbageInternal(gc_plan_.back(), kGcCauseExplicit, clear_soft_references); } void Heap::ChangeCollector(CollectorType collector_type) { - switch (collector_type) { - case kCollectorTypeSS: { - ChangeAllocator(kAllocatorTypeBumpPointer); - break; + // TODO: Only do this with all mutators suspended to avoid races. + if (collector_type != collector_type_) { + collector_type_ = collector_type; + gc_plan_.clear(); + switch (collector_type_) { + case kCollectorTypeSS: { + concurrent_gc_ = false; + gc_plan_.push_back(collector::kGcTypeFull); + ChangeAllocator(kAllocatorTypeBumpPointer); + break; + } + case kCollectorTypeMS: { + concurrent_gc_ = false; + gc_plan_.push_back(collector::kGcTypeSticky); + gc_plan_.push_back(collector::kGcTypePartial); + gc_plan_.push_back(collector::kGcTypeFull); + ChangeAllocator(kAllocatorTypeFreeList); + break; + } + case kCollectorTypeCMS: { + concurrent_gc_ = true; + gc_plan_.push_back(collector::kGcTypeSticky); + gc_plan_.push_back(collector::kGcTypePartial); + gc_plan_.push_back(collector::kGcTypeFull); + ChangeAllocator(kAllocatorTypeFreeList); + break; + } + default: { + LOG(FATAL) << "Unimplemented"; + } } - case kCollectorTypeMS: - // Fall-through. - case kCollectorTypeCMS: { - ChangeAllocator(kAllocatorTypeFreeList); - break; - default: - LOG(FATAL) << "Unimplemented"; + if (concurrent_gc_) { + concurrent_start_bytes_ = + std::max(max_allowed_footprint_, kMinConcurrentRemainingBytes) - kMinConcurrentRemainingBytes; + } else { + concurrent_start_bytes_ = std::numeric_limits::max(); } } } @@ -1119,8 +1144,8 @@ void Heap::PreZygoteFork() { // Trim the pages at the end of the non moving space. non_moving_space_->Trim(); non_moving_space_->GetMemMap()->Protect(PROT_READ | PROT_WRITE); - // Change the allocator to the post zygote one. - ChangeCollector(collector_type_); + // Change the collector to the post zygote one. + ChangeCollector(post_zygote_collector_type_); // TODO: Delete bump_pointer_space_ and temp_pointer_space_? if (semi_space_collector_ != nullptr) { // Create a new bump pointer space which we will compact into. @@ -1295,7 +1320,7 @@ collector::GcType Heap::CollectGarbageInternal(collector::GcType gc_type, GcCaus } else { LOG(FATAL) << "Invalid current allocator " << current_allocator_; } - CHECK(collector != NULL) + CHECK(collector != nullptr) << "Could not find garbage collector with concurrent=" << concurrent_gc_ << " and type=" << gc_type; @@ -1876,7 +1901,7 @@ void Heap::GrowForUtilization(collector::GcType gc_type, uint64_t gc_duration) { } if (!ignore_max_footprint_) { SetIdealFootprint(target_size); - if (concurrent_gc_ && AllocatorHasConcurrentGC(current_allocator_)) { + if (concurrent_gc_) { // Calculate when to perform the next ConcurrentGC. // Calculate the estimated GC duration. double gc_duration_seconds = NsToMs(gc_duration) / 1000.0; @@ -1962,7 +1987,6 @@ void Heap::EnqueueClearedReferences() { void Heap::RequestConcurrentGC(Thread* self) { // Make sure that we can do a concurrent GC. Runtime* runtime = Runtime::Current(); - DCHECK(concurrent_gc_); if (runtime == NULL || !runtime->IsFinishedStarting() || runtime->IsShuttingDown(self) || self->IsHandlingStackOverflow()) { return; @@ -2096,7 +2120,7 @@ void Heap::RegisterNativeAllocation(JNIEnv* env, int bytes) { // finalizers released native managed allocations. UpdateMaxNativeFootprint(); } else if (!IsGCRequestPending()) { - if (concurrent_gc_ && AllocatorHasConcurrentGC(current_allocator_)) { + if (concurrent_gc_) { RequestConcurrentGC(self); } else { CollectGarbageInternal(gc_type, kGcCauseForAlloc, false); diff --git a/runtime/gc/heap.h b/runtime/gc/heap.h index 046fbac319d..3bff3f97045 100644 --- a/runtime/gc/heap.h +++ b/runtime/gc/heap.h @@ -644,12 +644,14 @@ class Heap { // A mod-union table remembers all of the references from the it's space to other spaces. SafeMap mod_union_tables_; - // What kind of concurrency behavior is the runtime after? True for concurrent mark sweep GC, - // false for stop-the-world mark sweep. - const bool concurrent_gc_; + // What kind of concurrency behavior is the runtime after? Currently true for concurrent mark + // sweep GC, false for other GC types. + bool concurrent_gc_; // The current collector type. CollectorType collector_type_; + // Which collector we will switch to after zygote fork. + CollectorType post_zygote_collector_type_; // How many GC threads we may use for paused parts of garbage collection. const size_t parallel_gc_threads_; From 8171fc34bf74ed0df02385787d916bc13eb7f160 Mon Sep 17 00:00:00 2001 From: Vladimir Marko Date: Tue, 26 Nov 2013 17:05:58 +0000 Subject: [PATCH 0258/2402] Don't prefix GC map by length. Bug: 11767815 Change-Id: I063917aefdf7674ee1a77736db059c9ee95ea075 --- compiler/dex/quick/codegen_util.cc | 3 ++- .../portable/portable_thread_entrypoints.cc | 6 +---- runtime/thread.cc | 6 +---- runtime/verifier/dex_gc_map.h | 10 ++++---- runtime/verifier/method_verifier.cc | 24 +++++++------------ runtime/verifier/method_verifier.h | 4 ++-- 6 files changed, 20 insertions(+), 33 deletions(-) diff --git a/compiler/dex/quick/codegen_util.cc b/compiler/dex/quick/codegen_util.cc index a682d586464..5d78ed58486 100644 --- a/compiler/dex/quick/codegen_util.cc +++ b/compiler/dex/quick/codegen_util.cc @@ -747,7 +747,8 @@ void Mir2Lir::CreateNativeGcMap() { } MethodReference method_ref(cu_->dex_file, cu_->method_idx); const std::vector* gc_map_raw = verifier::MethodVerifier::GetDexGcMap(method_ref); - verifier::DexPcToReferenceMap dex_gc_map(&(*gc_map_raw)[4], gc_map_raw->size() - 4); + verifier::DexPcToReferenceMap dex_gc_map(&(*gc_map_raw)[0]); + DCHECK_EQ(gc_map_raw->size(), dex_gc_map.RawSize()); // Compute native offset to references size. NativePcToReferenceMapBuilder native_gc_map_builder(&native_gc_map_, mapping_table.PcToDexSize(), diff --git a/runtime/entrypoints/portable/portable_thread_entrypoints.cc b/runtime/entrypoints/portable/portable_thread_entrypoints.cc index 8a2c8998aa3..4f19964e8c5 100644 --- a/runtime/entrypoints/portable/portable_thread_entrypoints.cc +++ b/runtime/entrypoints/portable/portable_thread_entrypoints.cc @@ -36,11 +36,7 @@ class ShadowFrameCopyVisitor : public StackVisitor { ShadowFrame* new_frame = ShadowFrame::Create(num_regs, NULL, method, dex_pc); const uint8_t* gc_map = method->GetNativeGcMap(); - uint32_t gc_map_length = static_cast((gc_map[0] << 24) | - (gc_map[1] << 16) | - (gc_map[2] << 8) | - (gc_map[3] << 0)); - verifier::DexPcToReferenceMap dex_gc_map(gc_map + 4, gc_map_length); + verifier::DexPcToReferenceMap dex_gc_map(gc_map); const uint8_t* reg_bitmap = dex_gc_map.FindBitMap(dex_pc); for (size_t reg = 0; reg < num_regs; ++reg) { if (TestBitmap(reg, reg_bitmap)) { diff --git a/runtime/thread.cc b/runtime/thread.cc index fa49faa310c..715be99942b 100644 --- a/runtime/thread.cc +++ b/runtime/thread.cc @@ -1994,11 +1994,7 @@ class ReferenceMapVisitor : public StackVisitor { // Portable path use DexGcMap and store in Method.native_gc_map_. const uint8_t* gc_map = m->GetNativeGcMap(); CHECK(gc_map != NULL) << PrettyMethod(m); - uint32_t gc_map_length = static_cast((gc_map[0] << 24) | - (gc_map[1] << 16) | - (gc_map[2] << 8) | - (gc_map[3] << 0)); - verifier::DexPcToReferenceMap dex_gc_map(gc_map + 4, gc_map_length); + verifier::DexPcToReferenceMap dex_gc_map(gc_map); uint32_t dex_pc = GetDexPc(); const uint8_t* reg_bitmap = dex_gc_map.FindBitMap(dex_pc); DCHECK(reg_bitmap != NULL); diff --git a/runtime/verifier/dex_gc_map.h b/runtime/verifier/dex_gc_map.h index 2a95ba22849..a045a9e3a72 100644 --- a/runtime/verifier/dex_gc_map.h +++ b/runtime/verifier/dex_gc_map.h @@ -38,11 +38,13 @@ enum RegisterMapFormat { // Lightweight wrapper for Dex PC to reference bit maps. class DexPcToReferenceMap { public: - DexPcToReferenceMap(const uint8_t* data, size_t data_length) : data_(data) { + DexPcToReferenceMap(const uint8_t* data) : data_(data) { CHECK(data_ != NULL); - // Check the size of the table agrees with the number of entries - size_t data_size = data_length - 4; - DCHECK_EQ(EntryWidth() * NumEntries(), data_size); + } + + // The total size of the reference bit map including header. + size_t RawSize() const { + return EntryWidth() * NumEntries() + 4u /* header */; } // The number of entries in the table diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc index 1e45c604756..5f5d8654517 100644 --- a/runtime/verifier/method_verifier.cc +++ b/runtime/verifier/method_verifier.cc @@ -1068,13 +1068,13 @@ bool MethodVerifier::VerifyCodeFlow() { bool compile = IsCandidateForCompilation(ref, method_access_flags_); if (compile) { /* Generate a register map and add it to the method. */ - const std::vector* dex_gc_map = GenerateLengthPrefixedGcMap(); + const std::vector* dex_gc_map = GenerateGcMap(); if (dex_gc_map == NULL) { DCHECK_NE(failures_.size(), 0U); return false; // Not a real failure, but a failure to encode } if (kIsDebugBuild) { - VerifyLengthPrefixedGcMap(*dex_gc_map); + VerifyGcMap(*dex_gc_map); } verifier::MethodVerifier::SetDexGcMap(ref, dex_gc_map); } @@ -4054,7 +4054,7 @@ MethodVerifier::PcToConcreteMethodMap* MethodVerifier::GenerateDevirtMap() { return pc_to_concrete_method_map.release(); } -const std::vector* MethodVerifier::GenerateLengthPrefixedGcMap() { +const std::vector* MethodVerifier::GenerateGcMap() { size_t num_entries, ref_bitmap_bits, pc_bits; ComputeGcMapSizes(&num_entries, &ref_bitmap_bits, &pc_bits); // There's a single byte to encode the size of each bitmap @@ -4092,12 +4092,7 @@ const std::vector* MethodVerifier::GenerateLengthPrefixedGcMap() { Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Failed to encode GC map (size=" << table_size << ")"; return NULL; } - table->reserve(table_size + 4); // table_size plus the length prefix - // Write table size - table->push_back((table_size & 0xff000000) >> 24); - table->push_back((table_size & 0x00ff0000) >> 16); - table->push_back((table_size & 0x0000ff00) >> 8); - table->push_back((table_size & 0x000000ff) >> 0); + table->reserve(table_size); // Write table header table->push_back(format | ((ref_bitmap_bytes >> DexPcToReferenceMap::kRegMapFormatShift) & ~DexPcToReferenceMap::kRegMapFormatMask)); @@ -4115,18 +4110,15 @@ const std::vector* MethodVerifier::GenerateLengthPrefixedGcMap() { line->WriteReferenceBitMap(*table, ref_bitmap_bytes); } } - DCHECK_EQ(table->size(), table_size + 4); // table_size plus the length prefix + DCHECK_EQ(table->size(), table_size); return table; } -void MethodVerifier::VerifyLengthPrefixedGcMap(const std::vector& data) { +void MethodVerifier::VerifyGcMap(const std::vector& data) { // Check that for every GC point there is a map entry, there aren't entries for non-GC points, // that the table data is well formed and all references are marked (or not) in the bitmap - DCHECK_GE(data.size(), 4u); - size_t table_size = data.size() - 4u; - DCHECK_EQ(table_size, static_cast((data[0] << 24) | (data[1] << 16) | - (data[2] << 8) | (data[3] << 0))); - DexPcToReferenceMap map(&data[4], table_size); + DexPcToReferenceMap map(&data[0]); + DCHECK_EQ(data.size(), map.RawSize()); size_t map_index = 0; for (size_t i = 0; i < code_item_->insns_size_in_code_units_; i++) { const uint8_t* reg_bitmap = map.FindBitMap(i, false); diff --git a/runtime/verifier/method_verifier.h b/runtime/verifier/method_verifier.h index f72898eee7a..892b7a886c0 100644 --- a/runtime/verifier/method_verifier.h +++ b/runtime/verifier/method_verifier.h @@ -614,10 +614,10 @@ class MethodVerifier { * encode it in some clever fashion. * Returns a pointer to a newly-allocated RegisterMap, or NULL on failure. */ - const std::vector* GenerateLengthPrefixedGcMap(); + const std::vector* GenerateGcMap(); // Verify that the GC map associated with method_ is well formed - void VerifyLengthPrefixedGcMap(const std::vector& data); + void VerifyGcMap(const std::vector& data); // Compute sizes for GC map data void ComputeGcMapSizes(size_t* gc_points, size_t* ref_bitmap_bits, size_t* log2_max_gc_pc); From 07a3212cc1ca6cb3c21727c6b7c1f41b54e0299a Mon Sep 17 00:00:00 2001 From: Vladimir Marko Date: Tue, 10 Dec 2013 10:41:18 +0000 Subject: [PATCH 0259/2402] Fix build (lint: explicit ctor). Change-Id: Ic28c43d39dc85adce013fd795590af1050c333c9 --- runtime/verifier/dex_gc_map.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/verifier/dex_gc_map.h b/runtime/verifier/dex_gc_map.h index a045a9e3a72..4570ae820e9 100644 --- a/runtime/verifier/dex_gc_map.h +++ b/runtime/verifier/dex_gc_map.h @@ -38,7 +38,7 @@ enum RegisterMapFormat { // Lightweight wrapper for Dex PC to reference bit maps. class DexPcToReferenceMap { public: - DexPcToReferenceMap(const uint8_t* data) : data_(data) { + explicit DexPcToReferenceMap(const uint8_t* data) : data_(data) { CHECK(data_ != NULL); } From 867a2b35e67ddcbec089964e8f3cd9a827186e48 Mon Sep 17 00:00:00 2001 From: Vladimir Marko Date: Tue, 10 Dec 2013 13:01:13 +0000 Subject: [PATCH 0260/2402] Get rid of platform-specific method inliners. The DexFileToMethodInlinerMap dependency on CompilerDriver and its instruction set makes it impossible to implement verification-time checking for methods we want to inline. Therefore, we get rid of the platform-specific method inliners and rely on the backend's existing ability to recognize when it can actually emit an intrinsic function. Change-Id: I57947db93f13a26c1c794cb3584130321106306f --- compiler/Android.mk | 3 - compiler/dex/frontend.cc | 16 +-- compiler/dex/frontend.h | 2 +- .../quick/arm/arm_dex_file_method_inliner.cc | 105 ----------------- .../quick/arm/arm_dex_file_method_inliner.h | 37 ------ compiler/dex/quick/dex_file_method_inliner.cc | 83 +++++++++++-- compiler/dex/quick/dex_file_method_inliner.h | 28 ++--- .../quick/dex_file_to_method_inliner_map.cc | 26 +--- .../quick/dex_file_to_method_inliner_map.h | 11 +- .../mips/mips_dex_file_method_inliner.cc | 105 ----------------- .../quick/mips/mips_dex_file_method_inliner.h | 37 ------ .../quick/x86/x86_dex_file_method_inliner.cc | 111 ------------------ .../quick/x86/x86_dex_file_method_inliner.h | 37 ------ 13 files changed, 110 insertions(+), 491 deletions(-) delete mode 100644 compiler/dex/quick/arm/arm_dex_file_method_inliner.cc delete mode 100644 compiler/dex/quick/arm/arm_dex_file_method_inliner.h delete mode 100644 compiler/dex/quick/mips/mips_dex_file_method_inliner.cc delete mode 100644 compiler/dex/quick/mips/mips_dex_file_method_inliner.h delete mode 100644 compiler/dex/quick/x86/x86_dex_file_method_inliner.cc delete mode 100644 compiler/dex/quick/x86/x86_dex_file_method_inliner.h diff --git a/compiler/Android.mk b/compiler/Android.mk index b7dc9f6c3b6..a2419d5c2ee 100644 --- a/compiler/Android.mk +++ b/compiler/Android.mk @@ -23,7 +23,6 @@ LIBART_COMPILER_SRC_FILES := \ dex/local_value_numbering.cc \ dex/arena_allocator.cc \ dex/arena_bit_vector.cc \ - dex/quick/arm/arm_dex_file_method_inliner.cc \ dex/quick/arm/assemble_arm.cc \ dex/quick/arm/call_arm.cc \ dex/quick/arm/fp_arm.cc \ @@ -41,7 +40,6 @@ LIBART_COMPILER_SRC_FILES := \ dex/quick/mips/call_mips.cc \ dex/quick/mips/fp_mips.cc \ dex/quick/mips/int_mips.cc \ - dex/quick/mips/mips_dex_file_method_inliner.cc \ dex/quick/mips/target_mips.cc \ dex/quick/mips/utility_mips.cc \ dex/quick/mir_to_lir.cc \ @@ -52,7 +50,6 @@ LIBART_COMPILER_SRC_FILES := \ dex/quick/x86/int_x86.cc \ dex/quick/x86/target_x86.cc \ dex/quick/x86/utility_x86.cc \ - dex/quick/x86/x86_dex_file_method_inliner.cc \ dex/portable/mir_to_gbc.cc \ dex/dex_to_dex_compiler.cc \ dex/mir_dataflow.cc \ diff --git a/compiler/dex/frontend.cc b/compiler/dex/frontend.cc index 197bba5a58d..6aabb2ae894 100644 --- a/compiler/dex/frontend.cc +++ b/compiler/dex/frontend.cc @@ -63,21 +63,21 @@ LLVMInfo::LLVMInfo() { LLVMInfo::~LLVMInfo() { } -QuickCompilerContext::QuickCompilerContext(CompilerDriver& compiler) - : inliner_map_(new DexFileToMethodInlinerMap(&compiler)) { +QuickCompilerContext::QuickCompilerContext() + : inliner_map_(new DexFileToMethodInlinerMap()) { } QuickCompilerContext::~QuickCompilerContext() { } -extern "C" void ArtInitQuickCompilerContext(art::CompilerDriver& compiler) { - CHECK(compiler.GetCompilerContext() == NULL); - compiler.SetCompilerContext(new QuickCompilerContext(compiler)); +extern "C" void ArtInitQuickCompilerContext(art::CompilerDriver& driver) { + CHECK(driver.GetCompilerContext() == NULL); + driver.SetCompilerContext(new QuickCompilerContext()); } -extern "C" void ArtUnInitQuickCompilerContext(art::CompilerDriver& compiler) { - delete reinterpret_cast(compiler.GetCompilerContext()); - compiler.SetCompilerContext(NULL); +extern "C" void ArtUnInitQuickCompilerContext(art::CompilerDriver& driver) { + delete reinterpret_cast(driver.GetCompilerContext()); + driver.SetCompilerContext(NULL); } /* Default optimizer/debug setting for the compiler. */ diff --git a/compiler/dex/frontend.h b/compiler/dex/frontend.h index 4a863f5294c..bcb8bf05a99 100644 --- a/compiler/dex/frontend.h +++ b/compiler/dex/frontend.h @@ -115,7 +115,7 @@ class LLVMInfo { class QuickCompilerContext { public: - explicit QuickCompilerContext(CompilerDriver& compiler); + QuickCompilerContext(); ~QuickCompilerContext(); DexFileToMethodInlinerMap* GetInlinerMap() { diff --git a/compiler/dex/quick/arm/arm_dex_file_method_inliner.cc b/compiler/dex/quick/arm/arm_dex_file_method_inliner.cc deleted file mode 100644 index 59f7202f33b..00000000000 --- a/compiler/dex/quick/arm/arm_dex_file_method_inliner.cc +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Copyright (C) 2013 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 "base/logging.h" -#include "base/macros.h" -#include "dex/compiler_enums.h" - -#include "arm_dex_file_method_inliner.h" - -namespace art { - -const DexFileMethodInliner::IntrinsicDef ArmDexFileMethodInliner::kIntrinsicMethods[] = { -#define INTRINSIC(c, n, p, o, d) \ - { { kClassCache ## c, kNameCache ## n, kProtoCache ## p }, { o, d } } - - INTRINSIC(JavaLangDouble, DoubleToRawLongBits, D_J, kIntrinsicDoubleCvt, 0), - INTRINSIC(JavaLangDouble, LongBitsToDouble, J_D, kIntrinsicDoubleCvt, 0), - INTRINSIC(JavaLangFloat, FloatToRawIntBits, F_I, kIntrinsicFloatCvt, 0), - INTRINSIC(JavaLangFloat, IntBitsToFloat, I_F, kIntrinsicFloatCvt, 0), - - INTRINSIC(JavaLangInteger, ReverseBytes, I_I, kIntrinsicReverseBytes, kWord), - INTRINSIC(JavaLangLong, ReverseBytes, J_J, kIntrinsicReverseBytes, kLong), - INTRINSIC(JavaLangShort, ReverseBytes, S_S, kIntrinsicReverseBytes, kSignedHalf), - - INTRINSIC(JavaLangMath, Abs, I_I, kIntrinsicAbsInt, 0), - INTRINSIC(JavaLangStrictMath, Abs, I_I, kIntrinsicAbsInt, 0), - INTRINSIC(JavaLangMath, Abs, J_J, kIntrinsicAbsLong, 0), - INTRINSIC(JavaLangStrictMath, Abs, J_J, kIntrinsicAbsLong, 0), - INTRINSIC(JavaLangMath, Min, II_I, kIntrinsicMinMaxInt, kIntrinsicFlagMin), - INTRINSIC(JavaLangStrictMath, Min, II_I, kIntrinsicMinMaxInt, kIntrinsicFlagMin), - INTRINSIC(JavaLangMath, Max, II_I, kIntrinsicMinMaxInt, kIntrinsicFlagMax), - INTRINSIC(JavaLangStrictMath, Max, II_I, kIntrinsicMinMaxInt, kIntrinsicFlagMax), - INTRINSIC(JavaLangMath, Sqrt, D_D, kIntrinsicSqrt, 0), - INTRINSIC(JavaLangStrictMath, Sqrt, D_D, kIntrinsicSqrt, 0), - - INTRINSIC(JavaLangString, CharAt, I_C, kIntrinsicCharAt, 0), - INTRINSIC(JavaLangString, CompareTo, String_I, kIntrinsicCompareTo, 0), - INTRINSIC(JavaLangString, IsEmpty, _Z, kIntrinsicIsEmptyOrLength, kIntrinsicFlagIsEmpty), - INTRINSIC(JavaLangString, IndexOf, II_I, kIntrinsicIndexOf, kIntrinsicFlagNone), - INTRINSIC(JavaLangString, IndexOf, I_I, kIntrinsicIndexOf, kIntrinsicFlagBase0), - INTRINSIC(JavaLangString, Length, _I, kIntrinsicIsEmptyOrLength, kIntrinsicFlagLength), - - INTRINSIC(JavaLangThread, CurrentThread, _Thread, kIntrinsicCurrentThread, 0), - - INTRINSIC(LibcoreIoMemory, PeekByte, J_B, kIntrinsicPeek, kSignedByte), - INTRINSIC(LibcoreIoMemory, PeekIntNative, J_I, kIntrinsicPeek, kWord), - INTRINSIC(LibcoreIoMemory, PeekLongNative, J_J, kIntrinsicPeek, kLong), - INTRINSIC(LibcoreIoMemory, PeekShortNative, J_S, kIntrinsicPeek, kSignedHalf), - INTRINSIC(LibcoreIoMemory, PokeByte, JB_V, kIntrinsicPoke, kSignedByte), - INTRINSIC(LibcoreIoMemory, PokeIntNative, JI_V, kIntrinsicPoke, kWord), - INTRINSIC(LibcoreIoMemory, PokeLongNative, JJ_V, kIntrinsicPoke, kLong), - INTRINSIC(LibcoreIoMemory, PokeShortNative, JS_V, kIntrinsicPoke, kSignedHalf), - - INTRINSIC(SunMiscUnsafe, CompareAndSwapInt, ObjectJII_Z, kIntrinsicCas, - kIntrinsicFlagNone), - INTRINSIC(SunMiscUnsafe, CompareAndSwapLong, ObjectJJJ_Z, kIntrinsicCas, - kIntrinsicFlagIsLong), - INTRINSIC(SunMiscUnsafe, CompareAndSwapObject, ObjectJObjectObject_Z, kIntrinsicCas, - kIntrinsicFlagIsObject), - -#define UNSAFE_GET_PUT(type, code, type_flags) \ - INTRINSIC(SunMiscUnsafe, Get ## type, ObjectJ_ ## code, kIntrinsicUnsafeGet, \ - type_flags & ~kIntrinsicFlagIsObject), \ - INTRINSIC(SunMiscUnsafe, Get ## type ## Volatile, ObjectJ_ ## code, kIntrinsicUnsafeGet, \ - (type_flags | kIntrinsicFlagIsVolatile) & ~kIntrinsicFlagIsObject), \ - INTRINSIC(SunMiscUnsafe, Put ## type, ObjectJ ## code ## _V, kIntrinsicUnsafePut, \ - type_flags), \ - INTRINSIC(SunMiscUnsafe, Put ## type ## Volatile, ObjectJ ## code ## _V, kIntrinsicUnsafePut, \ - type_flags | kIntrinsicFlagIsVolatile), \ - INTRINSIC(SunMiscUnsafe, PutOrdered ## type, ObjectJ ## code ## _V, kIntrinsicUnsafePut, \ - type_flags | kIntrinsicFlagIsOrdered) - - UNSAFE_GET_PUT(Int, I, kIntrinsicFlagNone), - UNSAFE_GET_PUT(Long, J, kIntrinsicFlagIsLong), - UNSAFE_GET_PUT(Object, Object, kIntrinsicFlagIsObject), -#undef UNSAFE_GET_PUT - -#undef INTRINSIC -}; - -ArmDexFileMethodInliner::ArmDexFileMethodInliner() { -} - -ArmDexFileMethodInliner::~ArmDexFileMethodInliner() { -} - -void ArmDexFileMethodInliner::FindIntrinsics(const DexFile* dex_file) { - IndexCache cache; - DoFindIntrinsics(dex_file, &cache, kIntrinsicMethods, arraysize(kIntrinsicMethods)); -} - -} // namespace art diff --git a/compiler/dex/quick/arm/arm_dex_file_method_inliner.h b/compiler/dex/quick/arm/arm_dex_file_method_inliner.h deleted file mode 100644 index 3428391624d..00000000000 --- a/compiler/dex/quick/arm/arm_dex_file_method_inliner.h +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (C) 2013 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef ART_COMPILER_DEX_QUICK_ARM_ARM_DEX_FILE_METHOD_INLINER_H_ -#define ART_COMPILER_DEX_QUICK_ARM_ARM_DEX_FILE_METHOD_INLINER_H_ - -#include "dex/quick/dex_file_method_inliner.h" - -namespace art { - -class ArmDexFileMethodInliner : public DexFileMethodInliner { - public: - ArmDexFileMethodInliner(); - ~ArmDexFileMethodInliner(); - - void FindIntrinsics(const DexFile* dex_file); - - private: - static const IntrinsicDef kIntrinsicMethods[]; -}; - -} // namespace art - -#endif // ART_COMPILER_DEX_QUICK_ARM_ARM_DEX_FILE_METHOD_INLINER_H_ diff --git a/compiler/dex/quick/dex_file_method_inliner.cc b/compiler/dex/quick/dex_file_method_inliner.cc index 6c0328edb0d..fb471abfa9b 100644 --- a/compiler/dex/quick/dex_file_method_inliner.cc +++ b/compiler/dex/quick/dex_file_method_inliner.cc @@ -157,8 +157,74 @@ const DexFileMethodInliner::ProtoDef DexFileMethodInliner::kProtoCacheDefs[] = { kClassCacheJavaLangObject } }, }; -DexFileMethodInliner::~DexFileMethodInliner() { -} +const DexFileMethodInliner::IntrinsicDef DexFileMethodInliner::kIntrinsicMethods[] = { +#define INTRINSIC(c, n, p, o, d) \ + { { kClassCache ## c, kNameCache ## n, kProtoCache ## p }, { o, d } } + + INTRINSIC(JavaLangDouble, DoubleToRawLongBits, D_J, kIntrinsicDoubleCvt, 0), + INTRINSIC(JavaLangDouble, LongBitsToDouble, J_D, kIntrinsicDoubleCvt, 0), + INTRINSIC(JavaLangFloat, FloatToRawIntBits, F_I, kIntrinsicFloatCvt, 0), + INTRINSIC(JavaLangFloat, IntBitsToFloat, I_F, kIntrinsicFloatCvt, 0), + + INTRINSIC(JavaLangInteger, ReverseBytes, I_I, kIntrinsicReverseBytes, kWord), + INTRINSIC(JavaLangLong, ReverseBytes, J_J, kIntrinsicReverseBytes, kLong), + INTRINSIC(JavaLangShort, ReverseBytes, S_S, kIntrinsicReverseBytes, kSignedHalf), + + INTRINSIC(JavaLangMath, Abs, I_I, kIntrinsicAbsInt, 0), + INTRINSIC(JavaLangStrictMath, Abs, I_I, kIntrinsicAbsInt, 0), + INTRINSIC(JavaLangMath, Abs, J_J, kIntrinsicAbsLong, 0), + INTRINSIC(JavaLangStrictMath, Abs, J_J, kIntrinsicAbsLong, 0), + INTRINSIC(JavaLangMath, Min, II_I, kIntrinsicMinMaxInt, kIntrinsicFlagMin), + INTRINSIC(JavaLangStrictMath, Min, II_I, kIntrinsicMinMaxInt, kIntrinsicFlagMin), + INTRINSIC(JavaLangMath, Max, II_I, kIntrinsicMinMaxInt, kIntrinsicFlagMax), + INTRINSIC(JavaLangStrictMath, Max, II_I, kIntrinsicMinMaxInt, kIntrinsicFlagMax), + INTRINSIC(JavaLangMath, Sqrt, D_D, kIntrinsicSqrt, 0), + INTRINSIC(JavaLangStrictMath, Sqrt, D_D, kIntrinsicSqrt, 0), + + INTRINSIC(JavaLangString, CharAt, I_C, kIntrinsicCharAt, 0), + INTRINSIC(JavaLangString, CompareTo, String_I, kIntrinsicCompareTo, 0), + INTRINSIC(JavaLangString, IsEmpty, _Z, kIntrinsicIsEmptyOrLength, kIntrinsicFlagIsEmpty), + INTRINSIC(JavaLangString, IndexOf, II_I, kIntrinsicIndexOf, kIntrinsicFlagNone), + INTRINSIC(JavaLangString, IndexOf, I_I, kIntrinsicIndexOf, kIntrinsicFlagBase0), + INTRINSIC(JavaLangString, Length, _I, kIntrinsicIsEmptyOrLength, kIntrinsicFlagLength), + + INTRINSIC(JavaLangThread, CurrentThread, _Thread, kIntrinsicCurrentThread, 0), + + INTRINSIC(LibcoreIoMemory, PeekByte, J_B, kIntrinsicPeek, kSignedByte), + INTRINSIC(LibcoreIoMemory, PeekIntNative, J_I, kIntrinsicPeek, kWord), + INTRINSIC(LibcoreIoMemory, PeekLongNative, J_J, kIntrinsicPeek, kLong), + INTRINSIC(LibcoreIoMemory, PeekShortNative, J_S, kIntrinsicPeek, kSignedHalf), + INTRINSIC(LibcoreIoMemory, PokeByte, JB_V, kIntrinsicPoke, kSignedByte), + INTRINSIC(LibcoreIoMemory, PokeIntNative, JI_V, kIntrinsicPoke, kWord), + INTRINSIC(LibcoreIoMemory, PokeLongNative, JJ_V, kIntrinsicPoke, kLong), + INTRINSIC(LibcoreIoMemory, PokeShortNative, JS_V, kIntrinsicPoke, kSignedHalf), + + INTRINSIC(SunMiscUnsafe, CompareAndSwapInt, ObjectJII_Z, kIntrinsicCas, + kIntrinsicFlagNone), + INTRINSIC(SunMiscUnsafe, CompareAndSwapLong, ObjectJJJ_Z, kIntrinsicCas, + kIntrinsicFlagIsLong), + INTRINSIC(SunMiscUnsafe, CompareAndSwapObject, ObjectJObjectObject_Z, kIntrinsicCas, + kIntrinsicFlagIsObject), + +#define UNSAFE_GET_PUT(type, code, type_flags) \ + INTRINSIC(SunMiscUnsafe, Get ## type, ObjectJ_ ## code, kIntrinsicUnsafeGet, \ + type_flags & ~kIntrinsicFlagIsObject), \ + INTRINSIC(SunMiscUnsafe, Get ## type ## Volatile, ObjectJ_ ## code, kIntrinsicUnsafeGet, \ + (type_flags | kIntrinsicFlagIsVolatile) & ~kIntrinsicFlagIsObject), \ + INTRINSIC(SunMiscUnsafe, Put ## type, ObjectJ ## code ## _V, kIntrinsicUnsafePut, \ + type_flags), \ + INTRINSIC(SunMiscUnsafe, Put ## type ## Volatile, ObjectJ ## code ## _V, kIntrinsicUnsafePut, \ + type_flags | kIntrinsicFlagIsVolatile), \ + INTRINSIC(SunMiscUnsafe, PutOrdered ## type, ObjectJ ## code ## _V, kIntrinsicUnsafePut, \ + type_flags | kIntrinsicFlagIsOrdered) + + UNSAFE_GET_PUT(Int, I, kIntrinsicFlagNone), + UNSAFE_GET_PUT(Long, J, kIntrinsicFlagIsLong), + UNSAFE_GET_PUT(Object, Object, kIntrinsicFlagIsObject), +#undef UNSAFE_GET_PUT + +#undef INTRINSIC +}; DexFileMethodInliner::DexFileMethodInliner() : dex_file_(NULL) { @@ -170,6 +236,9 @@ DexFileMethodInliner::DexFileMethodInliner() COMPILE_ASSERT(arraysize(kProtoCacheDefs) == kProtoCacheLast, bad_arraysize_kProtoCacheNames); } +DexFileMethodInliner::~DexFileMethodInliner() { +} + bool DexFileMethodInliner::IsIntrinsic(uint32_t method_index) const { return intrinsics_.find(method_index) != intrinsics_.end(); } @@ -333,15 +402,15 @@ DexFileMethodInliner::IndexCache::IndexCache() { std::fill_n(proto_indexes, arraysize(proto_indexes), kIndexUnresolved); } -void DexFileMethodInliner::DoFindIntrinsics(const DexFile* dex_file, IndexCache* cache, - const IntrinsicDef* defs, uint32_t def_count) { +void DexFileMethodInliner::FindIntrinsics(const DexFile* dex_file) { DCHECK(dex_file != nullptr); DCHECK(dex_file_ == nullptr); - for (uint32_t i = 0u; i != def_count; ++i) { - uint32_t method_id = FindMethodIndex(dex_file, cache, defs[i].method_def); + IndexCache cache; + for (const IntrinsicDef& def : kIntrinsicMethods) { + uint32_t method_id = FindMethodIndex(dex_file, &cache, def.method_def); if (method_id != kIndexNotFound) { DCHECK(intrinsics_.find(method_id) == intrinsics_.end()); - intrinsics_[method_id] = defs[i].intrinsic; + intrinsics_[method_id] = def.intrinsic; } } dex_file_ = dex_file; diff --git a/compiler/dex/quick/dex_file_method_inliner.h b/compiler/dex/quick/dex_file_method_inliner.h index bc005136ba7..948f4bbe564 100644 --- a/compiler/dex/quick/dex_file_method_inliner.h +++ b/compiler/dex/quick/dex_file_method_inliner.h @@ -89,12 +89,8 @@ struct Intrinsic { */ class DexFileMethodInliner { public: - virtual ~DexFileMethodInliner(); - - /** - * Find all known intrinsic methods in the dex_file and cache their indices. - */ - virtual void FindIntrinsics(const DexFile* dex_file) = 0; + DexFileMethodInliner(); + ~DexFileMethodInliner(); /** * Check whether a particular method index corresponds to an intrinsic function. @@ -103,15 +99,10 @@ class DexFileMethodInliner { /** * Generate code for an intrinsic function invocation. - * - * TODO: This should be target-specific. For the time being, - * it's shared since it dispatches everything to backend. */ bool GenIntrinsic(Mir2Lir* backend, CallInfo* info) const; - protected: - DexFileMethodInliner(); - + private: /** * To avoid multiple lookups of a class by its descriptor, we cache its * type index in the IndexCache. These are the indexes into the IndexCache @@ -290,6 +281,7 @@ class DexFileMethodInliner { static const char* kClassCacheNames[]; static const char* kNameCacheNames[]; static const ProtoDef kProtoCacheDefs[]; + static const IntrinsicDef kIntrinsicMethods[]; static const uint32_t kIndexNotFound = static_cast(-1); static const uint32_t kIndexUnresolved = static_cast(-2); @@ -303,14 +295,22 @@ class DexFileMethodInliner { static uint32_t FindMethodIndex(const DexFile* dex_file, IndexCache* cache, const MethodDef& method_def); - void DoFindIntrinsics(const DexFile* dex_file, IndexCache* cache, - const IntrinsicDef* defs, uint32_t def_count); + /** + * Find all known intrinsic methods in the dex_file and cache their indices. + * + * Only DexFileToMethodInlinerMap may call this function to initialize the inliner. + */ + void FindIntrinsics(const DexFile* dex_file); + + friend class DexFileToMethodInlinerMap; /* * Maps method indexes (for the particular DexFile) to Intrinsic defintions. */ std::map intrinsics_; const DexFile* dex_file_; + + DISALLOW_COPY_AND_ASSIGN(DexFileMethodInliner); }; } // namespace art diff --git a/compiler/dex/quick/dex_file_to_method_inliner_map.cc b/compiler/dex/quick/dex_file_to_method_inliner_map.cc index 56a42bc3dbb..0107ed3ccbd 100644 --- a/compiler/dex/quick/dex_file_to_method_inliner_map.cc +++ b/compiler/dex/quick/dex_file_to_method_inliner_map.cc @@ -22,17 +22,13 @@ #include "base/mutex-inl.h" #include "base/logging.h" #include "driver/compiler_driver.h" -#include "dex/quick/arm/arm_dex_file_method_inliner.h" -#include "dex/quick/mips/mips_dex_file_method_inliner.h" -#include "dex/quick/x86/x86_dex_file_method_inliner.h" #include "dex_file_to_method_inliner_map.h" namespace art { -DexFileToMethodInlinerMap::DexFileToMethodInlinerMap(const CompilerDriver* compiler) - : compiler_(compiler), - mutex_("inline_helper_mutex") { +DexFileToMethodInlinerMap::DexFileToMethodInlinerMap() + : lock_("inline_helper_mutex") { } DexFileToMethodInlinerMap::~DexFileToMethodInlinerMap() { @@ -44,31 +40,19 @@ DexFileToMethodInlinerMap::~DexFileToMethodInlinerMap() { const DexFileMethodInliner& DexFileToMethodInlinerMap::GetMethodInliner(const DexFile* dex_file) { Thread* self = Thread::Current(); { - ReaderMutexLock lock(self, mutex_); + ReaderMutexLock lock(self, lock_); auto it = inliners_.find(dex_file); if (it != inliners_.end()) { return *it->second; } } - WriterMutexLock lock(self, mutex_); + WriterMutexLock lock(self, lock_); DexFileMethodInliner** inliner = &inliners_[dex_file]; // inserts new entry if not found if (*inliner) { return **inliner; } - switch (compiler_->GetInstructionSet()) { - case kThumb2: - *inliner = new ArmDexFileMethodInliner; - break; - case kX86: - *inliner = new X86DexFileMethodInliner; - break; - case kMips: - *inliner = new MipsDexFileMethodInliner; - break; - default: - LOG(FATAL) << "Unexpected instruction set: " << compiler_->GetInstructionSet(); - } + *inliner = new DexFileMethodInliner(); DCHECK(*inliner != nullptr); // TODO: per-dex file locking for the intrinsics container filling. (*inliner)->FindIntrinsics(dex_file); diff --git a/compiler/dex/quick/dex_file_to_method_inliner_map.h b/compiler/dex/quick/dex_file_to_method_inliner_map.h index 77f2648ae66..476f002bb57 100644 --- a/compiler/dex/quick/dex_file_to_method_inliner_map.h +++ b/compiler/dex/quick/dex_file_to_method_inliner_map.h @@ -37,15 +37,16 @@ class DexFile; */ class DexFileToMethodInlinerMap { public: - explicit DexFileToMethodInlinerMap(const CompilerDriver* compiler); + DexFileToMethodInlinerMap(); ~DexFileToMethodInlinerMap(); - const DexFileMethodInliner& GetMethodInliner(const DexFile* dex_file) LOCKS_EXCLUDED(mutex_); + const DexFileMethodInliner& GetMethodInliner(const DexFile* dex_file) LOCKS_EXCLUDED(lock_); private: - const CompilerDriver* const compiler_; - ReaderWriterMutex mutex_; - std::map inliners_ GUARDED_BY(mutex_); + ReaderWriterMutex lock_; + std::map inliners_ GUARDED_BY(lock_); + + DISALLOW_COPY_AND_ASSIGN(DexFileToMethodInlinerMap); }; } // namespace art diff --git a/compiler/dex/quick/mips/mips_dex_file_method_inliner.cc b/compiler/dex/quick/mips/mips_dex_file_method_inliner.cc deleted file mode 100644 index 05d8ac802f9..00000000000 --- a/compiler/dex/quick/mips/mips_dex_file_method_inliner.cc +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Copyright (C) 2013 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 "base/logging.h" -#include "base/macros.h" -#include "dex/compiler_enums.h" - -#include "mips_dex_file_method_inliner.h" - -namespace art { - -const DexFileMethodInliner::IntrinsicDef MipsDexFileMethodInliner::kIntrinsicMethods[] = { -#define INTRINSIC(c, n, p, o, d) \ - { { kClassCache ## c, kNameCache ## n, kProtoCache ## p }, { o, d } } - - // INTRINSIC(JavaLangDouble, DoubleToRawLongBits, D_J, kIntrinsicDoubleCvt, 0), - // INTRINSIC(JavaLangDouble, LongBitsToDouble, J_D, kIntrinsicDoubleCvt, 0), - // INTRINSIC(JavaLangFloat, FloatToRawIntBits, F_I, kIntrinsicFloatCvt, 0), - // INTRINSIC(JavaLangFloat, IntBitsToFloat, I_F, kIntrinsicFloatCvt, 0), - - // INTRINSIC(JavaLangInteger, ReverseBytes, I_I, kIntrinsicReverseBytes, kWord), - // INTRINSIC(JavaLangLong, ReverseBytes, J_J, kIntrinsicReverseBytes, kLong), - // INTRINSIC(JavaLangShort, ReverseBytes, S_S, kIntrinsicReverseBytes, kSignedHalf), - - // INTRINSIC(JavaLangMath, Abs, I_I, kIntrinsicAbsInt, 0), - // INTRINSIC(JavaLangStrictMath, Abs, I_I, kIntrinsicAbsInt, 0), - // INTRINSIC(JavaLangMath, Abs, J_J, kIntrinsicAbsLong, 0), - // INTRINSIC(JavaLangStrictMath, Abs, J_J, kIntrinsicAbsLong, 0), - // INTRINSIC(JavaLangMath, Min, II_I, kIntrinsicMinMaxInt, kIntrinsicFlagMin), - // INTRINSIC(JavaLangStrictMath, Min, II_I, kIntrinsicMinMaxInt, kIntrinsicFlagMin), - // INTRINSIC(JavaLangMath, Max, II_I, kIntrinsicMinMaxInt, kIntrinsicFlagMax), - // INTRINSIC(JavaLangStrictMath, Max, II_I, kIntrinsicMinMaxInt, kIntrinsicFlagMax), - // INTRINSIC(JavaLangMath, Sqrt, D_D, kIntrinsicSqrt, 0), - // INTRINSIC(JavaLangStrictMath, Sqrt, D_D, kIntrinsicSqrt, 0), - - // INTRINSIC(JavaLangString, CharAt, I_C, kIntrinsicCharAt, 0), - // INTRINSIC(JavaLangString, CompareTo, String_I, kIntrinsicCompareTo, 0), - // INTRINSIC(JavaLangString, IsEmpty, _Z, kIntrinsicIsEmptyOrLength, kIntrinsicFlagIsEmpty), - // INTRINSIC(JavaLangString, IndexOf, II_I, kIntrinsicIndexOf, kIntrinsicFlagNone), - // INTRINSIC(JavaLangString, IndexOf, I_I, kIntrinsicIndexOf, kIntrinsicFlagBase0), - // INTRINSIC(JavaLangString, Length, _I, kIntrinsicIsEmptyOrLength, kIntrinsicFlagLength), - - INTRINSIC(JavaLangThread, CurrentThread, _Thread, kIntrinsicCurrentThread, 0), - - INTRINSIC(LibcoreIoMemory, PeekByte, J_B, kIntrinsicPeek, kSignedByte), - // INTRINSIC(LibcoreIoMemory, PeekIntNative, J_I, kIntrinsicPeek, kWord), - // INTRINSIC(LibcoreIoMemory, PeekLongNative, J_J, kIntrinsicPeek, kLong), - // INTRINSIC(LibcoreIoMemory, PeekShortNative, J_S, kIntrinsicPeek, kSignedHalf), - INTRINSIC(LibcoreIoMemory, PokeByte, JB_V, kIntrinsicPoke, kSignedByte), - // INTRINSIC(LibcoreIoMemory, PokeIntNative, JI_V, kIntrinsicPoke, kWord), - // INTRINSIC(LibcoreIoMemory, PokeLongNative, JJ_V, kIntrinsicPoke, kLong), - // INTRINSIC(LibcoreIoMemory, PokeShortNative, JS_V, kIntrinsicPoke, kSignedHalf), - - // INTRINSIC(SunMiscUnsafe, CompareAndSwapInt, ObjectJII_Z, kIntrinsicCas, - // kIntrinsicFlagNone), - // INTRINSIC(SunMiscUnsafe, CompareAndSwapLong, ObjectJJJ_Z, kIntrinsicCas, - // kIntrinsicFlagIsLong), - // INTRINSIC(SunMiscUnsafe, CompareAndSwapObject, ObjectJObjectObject_Z, kIntrinsicCas, - // kIntrinsicFlagIsObject), - -#define UNSAFE_GET_PUT(type, code, type_flags) \ - INTRINSIC(SunMiscUnsafe, Get ## type, ObjectJ_ ## code, kIntrinsicUnsafeGet, \ - type_flags & ~kIntrinsicFlagIsObject), \ - INTRINSIC(SunMiscUnsafe, Get ## type ## Volatile, ObjectJ_ ## code, kIntrinsicUnsafeGet, \ - (type_flags | kIntrinsicFlagIsVolatile) & ~kIntrinsicFlagIsObject), \ - INTRINSIC(SunMiscUnsafe, Put ## type, ObjectJ ## code ## _V, kIntrinsicUnsafePut, \ - type_flags), \ - INTRINSIC(SunMiscUnsafe, Put ## type ## Volatile, ObjectJ ## code ## _V, kIntrinsicUnsafePut, \ - type_flags | kIntrinsicFlagIsVolatile), \ - INTRINSIC(SunMiscUnsafe, PutOrdered ## type, ObjectJ ## code ## _V, kIntrinsicUnsafePut, \ - type_flags | kIntrinsicFlagIsOrdered) - - // UNSAFE_GET_PUT(Int, I, kIntrinsicFlagNone), - // UNSAFE_GET_PUT(Long, J, kIntrinsicFlagIsLong), - // UNSAFE_GET_PUT(Object, Object, kIntrinsicFlagIsObject), -#undef UNSAFE_GET_PUT - -#undef INTRINSIC -}; - -MipsDexFileMethodInliner::MipsDexFileMethodInliner() { -} - -MipsDexFileMethodInliner::~MipsDexFileMethodInliner() { -} - -void MipsDexFileMethodInliner::FindIntrinsics(const DexFile* dex_file) { - IndexCache cache; - DoFindIntrinsics(dex_file, &cache, kIntrinsicMethods, arraysize(kIntrinsicMethods)); -} - -} // namespace art diff --git a/compiler/dex/quick/mips/mips_dex_file_method_inliner.h b/compiler/dex/quick/mips/mips_dex_file_method_inliner.h deleted file mode 100644 index 8fe7ec771b1..00000000000 --- a/compiler/dex/quick/mips/mips_dex_file_method_inliner.h +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (C) 2013 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef ART_COMPILER_DEX_QUICK_MIPS_MIPS_DEX_FILE_METHOD_INLINER_H_ -#define ART_COMPILER_DEX_QUICK_MIPS_MIPS_DEX_FILE_METHOD_INLINER_H_ - -#include "dex/quick/dex_file_method_inliner.h" - -namespace art { - -class MipsDexFileMethodInliner : public DexFileMethodInliner { - public: - MipsDexFileMethodInliner(); - ~MipsDexFileMethodInliner(); - - void FindIntrinsics(const DexFile* dex_file); - - private: - static const IntrinsicDef kIntrinsicMethods[]; -}; - -} // namespace art - -#endif // ART_COMPILER_DEX_QUICK_MIPS_MIPS_DEX_FILE_METHOD_INLINER_H_ diff --git a/compiler/dex/quick/x86/x86_dex_file_method_inliner.cc b/compiler/dex/quick/x86/x86_dex_file_method_inliner.cc deleted file mode 100644 index 88e933f868c..00000000000 --- a/compiler/dex/quick/x86/x86_dex_file_method_inliner.cc +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Copyright (C) 2013 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 "base/logging.h" -#include "base/macros.h" -#include "dex/compiler_enums.h" - -#include "x86_dex_file_method_inliner.h" - -namespace art { - -const DexFileMethodInliner::IntrinsicDef X86DexFileMethodInliner::kIntrinsicMethods[] = { -#define INTRINSIC(c, n, p, o, d) \ - { { kClassCache ## c, kNameCache ## n, kProtoCache ## p }, { o, d } } - - INTRINSIC(JavaLangDouble, DoubleToRawLongBits, D_J, kIntrinsicDoubleCvt, 0), - INTRINSIC(JavaLangDouble, LongBitsToDouble, J_D, kIntrinsicDoubleCvt, 0), - INTRINSIC(JavaLangFloat, FloatToRawIntBits, F_I, kIntrinsicFloatCvt, 0), - INTRINSIC(JavaLangFloat, IntBitsToFloat, I_F, kIntrinsicFloatCvt, 0), - - INTRINSIC(JavaLangInteger, ReverseBytes, I_I, kIntrinsicReverseBytes, kWord), - INTRINSIC(JavaLangLong, ReverseBytes, J_J, kIntrinsicReverseBytes, kLong), - INTRINSIC(JavaLangShort, ReverseBytes, S_S, kIntrinsicReverseBytes, kSignedHalf), - - INTRINSIC(JavaLangMath, Abs, I_I, kIntrinsicAbsInt, 0), - INTRINSIC(JavaLangStrictMath, Abs, I_I, kIntrinsicAbsInt, 0), - INTRINSIC(JavaLangMath, Abs, J_J, kIntrinsicAbsLong, 0), - INTRINSIC(JavaLangStrictMath, Abs, J_J, kIntrinsicAbsLong, 0), - INTRINSIC(JavaLangMath, Min, II_I, kIntrinsicMinMaxInt, kIntrinsicFlagMin), - INTRINSIC(JavaLangStrictMath, Min, II_I, kIntrinsicMinMaxInt, kIntrinsicFlagMin), - INTRINSIC(JavaLangMath, Max, II_I, kIntrinsicMinMaxInt, kIntrinsicFlagMax), - INTRINSIC(JavaLangStrictMath, Max, II_I, kIntrinsicMinMaxInt, kIntrinsicFlagMax), - // INTRINSIC(JavaLangMath, Sqrt, D_D, kIntrinsicSqrt, 0), - // INTRINSIC(JavaLangStrictMath, Sqrt, D_D, kIntrinsicSqrt, 0), - - INTRINSIC(JavaLangString, CharAt, I_C, kIntrinsicCharAt, 0), - INTRINSIC(JavaLangString, CompareTo, String_I, kIntrinsicCompareTo, 0), - INTRINSIC(JavaLangString, IsEmpty, _Z, kIntrinsicIsEmptyOrLength, kIntrinsicFlagIsEmpty), - INTRINSIC(JavaLangString, IndexOf, II_I, kIntrinsicIndexOf, kIntrinsicFlagNone), - INTRINSIC(JavaLangString, IndexOf, I_I, kIntrinsicIndexOf, kIntrinsicFlagBase0), - INTRINSIC(JavaLangString, Length, _I, kIntrinsicIsEmptyOrLength, kIntrinsicFlagLength), - - INTRINSIC(JavaLangThread, CurrentThread, _Thread, kIntrinsicCurrentThread, 0), - - INTRINSIC(LibcoreIoMemory, PeekByte, J_B, kIntrinsicPeek, kSignedByte), - INTRINSIC(LibcoreIoMemory, PeekIntNative, J_I, kIntrinsicPeek, kWord), - INTRINSIC(LibcoreIoMemory, PeekLongNative, J_J, kIntrinsicPeek, kLong), - INTRINSIC(LibcoreIoMemory, PeekShortNative, J_S, kIntrinsicPeek, kSignedHalf), - INTRINSIC(LibcoreIoMemory, PokeByte, JB_V, kIntrinsicPoke, kSignedByte), - INTRINSIC(LibcoreIoMemory, PokeIntNative, JI_V, kIntrinsicPoke, kWord), - INTRINSIC(LibcoreIoMemory, PokeLongNative, JJ_V, kIntrinsicPoke, kLong), - INTRINSIC(LibcoreIoMemory, PokeShortNative, JS_V, kIntrinsicPoke, kSignedHalf), - - INTRINSIC(SunMiscUnsafe, CompareAndSwapInt, ObjectJII_Z, kIntrinsicCas, - kIntrinsicFlagNone), - INTRINSIC(SunMiscUnsafe, CompareAndSwapLong, ObjectJJJ_Z, kIntrinsicCas, - kIntrinsicFlagIsLong), - INTRINSIC(SunMiscUnsafe, CompareAndSwapObject, ObjectJObjectObject_Z, kIntrinsicCas, - kIntrinsicFlagIsObject), - -#define UNSAFE_GET_PUT(type, code, type_flags) \ - INTRINSIC(SunMiscUnsafe, Get ## type, ObjectJ_ ## code, kIntrinsicUnsafeGet, \ - type_flags & ~kIntrinsicFlagIsObject), \ - INTRINSIC(SunMiscUnsafe, Get ## type ## Volatile, ObjectJ_ ## code, kIntrinsicUnsafeGet, \ - (type_flags | kIntrinsicFlagIsVolatile) & ~kIntrinsicFlagIsObject), \ - INTRINSIC(SunMiscUnsafe, Put ## type, ObjectJ ## code ## _V, kIntrinsicUnsafePut, \ - type_flags), \ - INTRINSIC(SunMiscUnsafe, Put ## type ## Volatile, ObjectJ ## code ## _V, kIntrinsicUnsafePut, \ - type_flags | kIntrinsicFlagIsVolatile), \ - INTRINSIC(SunMiscUnsafe, PutOrdered ## type, ObjectJ ## code ## _V, kIntrinsicUnsafePut, \ - type_flags | kIntrinsicFlagIsOrdered) - - UNSAFE_GET_PUT(Int, I, kIntrinsicFlagNone), - UNSAFE_GET_PUT(Long, J, kIntrinsicFlagIsLong), - - // UNSAFE_GET_PUT(Object, Object, kIntrinsicFlagIsObject), - // PutObject: "TODO: fix X86, it exhausts registers for card marking." - INTRINSIC(SunMiscUnsafe, GetObject, ObjectJ_Object, kIntrinsicUnsafeGet, - kIntrinsicFlagNone), - INTRINSIC(SunMiscUnsafe, GetObjectVolatile, ObjectJ_Object, kIntrinsicUnsafeGet, - kIntrinsicFlagIsVolatile), -#undef UNSAFE_GET_PUT - -#undef INTRINSIC -}; - -X86DexFileMethodInliner::X86DexFileMethodInliner() { -} - -X86DexFileMethodInliner::~X86DexFileMethodInliner() { -} - -void X86DexFileMethodInliner::FindIntrinsics(const DexFile* dex_file) { - IndexCache cache; - DoFindIntrinsics(dex_file, &cache, kIntrinsicMethods, arraysize(kIntrinsicMethods)); -} - -} // namespace art diff --git a/compiler/dex/quick/x86/x86_dex_file_method_inliner.h b/compiler/dex/quick/x86/x86_dex_file_method_inliner.h deleted file mode 100644 index 7813e444fe5..00000000000 --- a/compiler/dex/quick/x86/x86_dex_file_method_inliner.h +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (C) 2013 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef ART_COMPILER_DEX_QUICK_X86_X86_DEX_FILE_METHOD_INLINER_H_ -#define ART_COMPILER_DEX_QUICK_X86_X86_DEX_FILE_METHOD_INLINER_H_ - -#include "dex/quick/dex_file_method_inliner.h" - -namespace art { - -class X86DexFileMethodInliner : public DexFileMethodInliner { - public: - X86DexFileMethodInliner(); - ~X86DexFileMethodInliner(); - - void FindIntrinsics(const DexFile* dex_file); - - private: - static const IntrinsicDef kIntrinsicMethods[]; -}; - -} // namespace art - -#endif // ART_COMPILER_DEX_QUICK_X86_X86_DEX_FILE_METHOD_INLINER_H_ From 4b1782f1c1af136a18f2886e76ef32216c3db3e4 Mon Sep 17 00:00:00 2001 From: Hiroshi Yamauchi Date: Thu, 5 Dec 2013 16:46:22 -0800 Subject: [PATCH 0261/2402] A simple promotion-like mechanism. If enabled, this mechanism moves objects that survive the first semi-space copying collection to the non-moving space. Bug: 11650816 Change-Id: Ia2fc902d08dc983449416f420a39449f9ed96255 --- runtime/gc/collector/semi_space.cc | 62 ++++++++++++++++++++++++++++-- runtime/gc/collector/semi_space.h | 9 +++++ 2 files changed, 67 insertions(+), 4 deletions(-) diff --git a/runtime/gc/collector/semi_space.cc b/runtime/gc/collector/semi_space.cc index 393935474a2..31a3f35dcb4 100644 --- a/runtime/gc/collector/semi_space.cc +++ b/runtime/gc/collector/semi_space.cc @@ -78,6 +78,8 @@ namespace collector { static constexpr bool kProtectFromSpace = true; static constexpr bool kResetFromSpace = true; +// TODO: move this to a new file as a new garbage collector? +static constexpr bool kEnableSimplePromo = false; // TODO: Unduplicate logic. void SemiSpace::ImmuneSpace(space::ContinuousSpace* space) { @@ -134,7 +136,9 @@ SemiSpace::SemiSpace(Heap* heap, const std::string& name_prefix) finalizer_reference_list_(nullptr), phantom_reference_list_(nullptr), cleared_reference_list_(nullptr), - self_(nullptr) { + self_(nullptr), + last_gc_to_space_end_(nullptr), + bytes_promoted_(0) { } void SemiSpace::InitializePhase() { @@ -169,6 +173,17 @@ void SemiSpace::MarkingPhase() { // Need to do this with mutators paused so that somebody doesn't accidentally allocate into the // wrong space. heap_->SwapSemiSpaces(); + if (kEnableSimplePromo) { + // If last_gc_to_space_end_ is out of the bounds of the from-space + // (the to-space from last GC), then point it to the beginning of + // the from-space. For example, the very first GC or the + // pre-zygote compaction. + if (!from_space_->HasAddress(reinterpret_cast(last_gc_to_space_end_))) { + last_gc_to_space_end_ = from_space_->Begin(); + } + // Reset this before the marking starts below. + bytes_promoted_ = 0; + } // Assume the cleared space is already empty. BindBitmaps(); // Process dirty cards and add dirty cards to mod-union tables. @@ -268,6 +283,13 @@ void SemiSpace::ReclaimPhase() { } else { mprotect(from_space_->Begin(), from_space_->Capacity(), PROT_READ); } + + if (kEnableSimplePromo) { + // Record the end (top) of the to space so we can distinguish + // between objects that were allocated since the last GC and the + // older objects. + last_gc_to_space_end_ = to_space_->End(); + } } void SemiSpace::ResizeMarkStack(size_t new_size) { @@ -308,11 +330,38 @@ Object* SemiSpace::MarkObject(Object* obj) { if (from_space_->HasAddress(obj)) { mirror::Object* forward_address = GetForwardingAddressInFromSpace(obj); // If the object has already been moved, return the new forward address. - if (!to_space_->HasAddress(forward_address)) { + if (forward_address == nullptr) { // Otherwise, we need to move the object and add it to the markstack for processing. size_t object_size = obj->SizeOf(); size_t dummy = 0; - forward_address = to_space_->Alloc(self_, object_size, &dummy); + if (kEnableSimplePromo && reinterpret_cast(obj) < last_gc_to_space_end_) { + // If it's allocated before the last GC (older), move (pseudo-promote) it to + // the non-moving space (as sort of an old generation.) + size_t bytes_promoted; + space::MallocSpace* non_moving_space = GetHeap()->GetNonMovingSpace(); + forward_address = non_moving_space->Alloc(self_, object_size, &bytes_promoted); + if (forward_address == nullptr) { + // If out of space, fall back to the to-space. + forward_address = to_space_->Alloc(self_, object_size, &dummy); + } else { + GetHeap()->num_bytes_allocated_.fetch_add(bytes_promoted); + bytes_promoted_ += bytes_promoted; + // Mark forward_address on the live bit map. + accounting::SpaceBitmap* live_bitmap = non_moving_space->GetLiveBitmap(); + DCHECK(live_bitmap != nullptr); + DCHECK(!live_bitmap->Test(forward_address)); + live_bitmap->Set(forward_address); + // Mark forward_address on the mark bit map. + accounting::SpaceBitmap* mark_bitmap = non_moving_space->GetMarkBitmap(); + DCHECK(mark_bitmap != nullptr); + DCHECK(!mark_bitmap->Test(forward_address)); + mark_bitmap->Set(forward_address); + } + DCHECK(forward_address != nullptr); + } else { + // If it's allocated after the last GC (younger), copy it to the to-space. + forward_address = to_space_->Alloc(self_, object_size, &dummy); + } // Copy over the object and add it to the mark stack since we still need to update it's // references. memcpy(reinterpret_cast(forward_address), obj, object_size); @@ -322,6 +371,9 @@ Object* SemiSpace::MarkObject(Object* obj) { monitor_size_must_be_same_as_object); obj->SetLockWord(LockWord::FromForwardingAddress(reinterpret_cast(forward_address))); MarkStackPush(forward_address); + } else { + DCHECK(to_space_->HasAddress(forward_address) || + (kEnableSimplePromo && GetHeap()->GetNonMovingSpace()->HasAddress(forward_address))); } ret = forward_address; // TODO: Do we need this if in the else statement? @@ -535,7 +587,9 @@ inline Object* SemiSpace::GetMarkedForwardAddress(mirror::Object* obj) const if (from_space_->HasAddress(obj)) { mirror::Object* forwarding_address = GetForwardingAddressInFromSpace(const_cast(obj)); // If the object is forwarded then it MUST be marked. - if (to_space_->HasAddress(forwarding_address)) { + DCHECK(forwarding_address == nullptr || to_space_->HasAddress(forwarding_address) || + (kEnableSimplePromo && GetHeap()->GetNonMovingSpace()->HasAddress(forwarding_address))); + if (forwarding_address != nullptr) { return forwarding_address; } // Must not be marked, return nullptr; diff --git a/runtime/gc/collector/semi_space.h b/runtime/gc/collector/semi_space.h index 0f0cae19661..b0724f9c0ce 100644 --- a/runtime/gc/collector/semi_space.h +++ b/runtime/gc/collector/semi_space.h @@ -281,6 +281,15 @@ class SemiSpace : public GarbageCollector { Thread* self_; + // Used for kEnableSimplePromo. The end/top of the bump pointer + // space at the end of the last collection. + byte* last_gc_to_space_end_; + + // Used for kEnableSimplePromo. During a collection, keeps track of + // how many bytes of objects have been copied so far from the bump + // pointer space to the non-moving space. + uint64_t bytes_promoted_; + private: DISALLOW_COPY_AND_ASSIGN(SemiSpace); }; From 4fa0bcd2142793e1f105b24b658de3635652b957 Mon Sep 17 00:00:00 2001 From: Brian Carlstrom Date: Tue, 10 Dec 2013 11:24:21 -0800 Subject: [PATCH 0262/2402] Remove unneeded quoting Change-Id: I87f452e338bd4ff0587e3fc7b0bec3f08a1e7fe6 --- runtime/dex_file.cc | 6 +++--- runtime/entrypoints/entrypoint_utils.cc | 2 +- runtime/interpreter/interpreter_common.cc | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/runtime/dex_file.cc b/runtime/dex_file.cc index 463e673279d..429c5162027 100644 --- a/runtime/dex_file.cc +++ b/runtime/dex_file.cc @@ -100,7 +100,7 @@ bool DexFile::GetChecksum(const char* filename, uint32_t* checksum, std::string* } UniquePtr zip_entry(zip_archive->Find(kClassesDex, error_msg)); if (zip_entry.get() == NULL) { - *error_msg = StringPrintf("Zip archive '%s' doesn\'t contain %s (error msg: %s)", filename, + *error_msg = StringPrintf("Zip archive '%s' doesn't contain %s (error msg: %s)", filename, kClassesDex, error_msg->c_str()); return false; } @@ -177,7 +177,7 @@ const DexFile* DexFile::OpenFile(int fd, const char* location, bool verify, struct stat sbuf; memset(&sbuf, 0, sizeof(sbuf)); if (fstat(fd, &sbuf) == -1) { - *error_msg = StringPrintf("DexFile: fstat \'%s\' failed: %s", location, strerror(errno)); + *error_msg = StringPrintf("DexFile: fstat '%s' failed: %s", location, strerror(errno)); return nullptr; } if (S_ISDIR(sbuf.st_mode)) { @@ -194,7 +194,7 @@ const DexFile* DexFile::OpenFile(int fd, const char* location, bool verify, if (map->Size() < sizeof(DexFile::Header)) { *error_msg = StringPrintf( - "DexFile: failed to open dex file \'%s\' that is too short to have a header", location); + "DexFile: failed to open dex file '%s' that is too short to have a header", location); return nullptr; } diff --git a/runtime/entrypoints/entrypoint_utils.cc b/runtime/entrypoints/entrypoint_utils.cc index 2806f941283..3ab8888217a 100644 --- a/runtime/entrypoints/entrypoint_utils.cc +++ b/runtime/entrypoints/entrypoint_utils.cc @@ -57,7 +57,7 @@ static inline mirror::Class* CheckFilledNewArrayAlloc(uint32_t type_idx, mirror: ThrowLocation throw_location = self->GetCurrentLocationForThrow(); DCHECK(throw_location.GetMethod() == referrer); self->ThrowNewExceptionF(throw_location, "Ljava/lang/InternalError;", - "Found type %s; filled-new-array not implemented for anything but \'int\'", + "Found type %s; filled-new-array not implemented for anything but 'int'", PrettyDescriptor(klass).c_str()); } return nullptr; // Failure diff --git a/runtime/interpreter/interpreter_common.cc b/runtime/interpreter/interpreter_common.cc index ca066b66d0f..5b9e55f9238 100644 --- a/runtime/interpreter/interpreter_common.cc +++ b/runtime/interpreter/interpreter_common.cc @@ -188,7 +188,7 @@ bool DoFilledNewArray(const Instruction* inst, const ShadowFrame& shadow_frame, } else { self->ThrowNewExceptionF(shadow_frame.GetCurrentLocationForThrow(), "Ljava/lang/InternalError;", - "Found type %s; filled-new-array not implemented for anything but \'int\'", + "Found type %s; filled-new-array not implemented for anything but 'int'", PrettyDescriptor(componentClass).c_str()); } return false; From 3ddbd42d31af95b22a6041775bf4782718f87d97 Mon Sep 17 00:00:00 2001 From: Hiroshi Yamauchi Date: Fri, 6 Dec 2013 17:43:36 -0800 Subject: [PATCH 0263/2402] Add SpaceTest for RosAllocSpace. SpaceTest now tests both DlMallocSpace and RosAllocSpace. Change-Id: Ib39ecf68f4671467f67b6ae53475a7dfb1c22d61 --- runtime/gc/space/space_test.cc | 254 ++++++++++++++++++++------------- 1 file changed, 154 insertions(+), 100 deletions(-) diff --git a/runtime/gc/space/space_test.cc b/runtime/gc/space/space_test.cc index 6b597ae0936..60c3b1c08c3 100644 --- a/runtime/gc/space/space_test.cc +++ b/runtime/gc/space/space_test.cc @@ -31,10 +31,6 @@ namespace space { class SpaceTest : public CommonTest { public: - void SizeFootPrintGrowthLimitAndTrimBody(DlMallocSpace* space, intptr_t object_size, - int round, size_t growth_limit); - void SizeFootPrintGrowthLimitAndTrimDriver(size_t object_size); - void AddSpace(ContinuousSpace* space) { // For RosAlloc, revoke the thread local runs before moving onto a // new alloc space. @@ -55,6 +51,26 @@ class SpaceTest : public CommonTest { arr->SetLength(length); EXPECT_EQ(arr->SizeOf(), size); } + + static MallocSpace* CreateDlMallocSpace(const std::string& name, size_t initial_size, size_t growth_limit, + size_t capacity, byte* requested_begin) { + return DlMallocSpace::Create(name, initial_size, growth_limit, capacity, requested_begin); + } + static MallocSpace* CreateRosAllocSpace(const std::string& name, size_t initial_size, size_t growth_limit, + size_t capacity, byte* requested_begin) { + return RosAllocSpace::Create(name, initial_size, growth_limit, capacity, requested_begin); + } + + typedef MallocSpace* (*CreateSpaceFn)(const std::string& name, size_t initial_size, size_t growth_limit, + size_t capacity, byte* requested_begin); + void InitTestBody(CreateSpaceFn create_space); + void ZygoteSpaceTestBody(CreateSpaceFn create_space); + void AllocAndFreeTestBody(CreateSpaceFn create_space); + void AllocAndFreeListTestBody(CreateSpaceFn create_space); + + void SizeFootPrintGrowthLimitAndTrimBody(MallocSpace* space, intptr_t object_size, + int round, size_t growth_limit); + void SizeFootPrintGrowthLimitAndTrimDriver(size_t object_size, CreateSpaceFn create_space); }; static size_t test_rand(size_t* seed) { @@ -62,128 +78,143 @@ static size_t test_rand(size_t* seed) { return *seed; } -TEST_F(SpaceTest, Init) { +void SpaceTest::InitTestBody(CreateSpaceFn create_space) { { // Init < max == growth - UniquePtr space(DlMallocSpace::Create("test", 16 * MB, 32 * MB, 32 * MB, NULL)); + UniquePtr space(create_space("test", 16 * MB, 32 * MB, 32 * MB, NULL)); EXPECT_TRUE(space.get() != NULL); } { // Init == max == growth - UniquePtr space(DlMallocSpace::Create("test", 16 * MB, 16 * MB, 16 * MB, NULL)); + UniquePtr space(create_space("test", 16 * MB, 16 * MB, 16 * MB, NULL)); EXPECT_TRUE(space.get() != NULL); } { // Init > max == growth - UniquePtr space(DlMallocSpace::Create("test", 32 * MB, 16 * MB, 16 * MB, NULL)); + UniquePtr space(create_space("test", 32 * MB, 16 * MB, 16 * MB, NULL)); EXPECT_TRUE(space.get() == NULL); } { // Growth == init < max - UniquePtr space(DlMallocSpace::Create("test", 16 * MB, 16 * MB, 32 * MB, NULL)); + UniquePtr space(create_space("test", 16 * MB, 16 * MB, 32 * MB, NULL)); EXPECT_TRUE(space.get() != NULL); } { // Growth < init < max - UniquePtr space(DlMallocSpace::Create("test", 16 * MB, 8 * MB, 32 * MB, NULL)); + UniquePtr space(create_space("test", 16 * MB, 8 * MB, 32 * MB, NULL)); EXPECT_TRUE(space.get() == NULL); } { // Init < growth < max - UniquePtr space(DlMallocSpace::Create("test", 8 * MB, 16 * MB, 32 * MB, NULL)); + UniquePtr space(create_space("test", 8 * MB, 16 * MB, 32 * MB, NULL)); EXPECT_TRUE(space.get() != NULL); } { // Init < max < growth - UniquePtr space(DlMallocSpace::Create("test", 8 * MB, 32 * MB, 16 * MB, NULL)); + UniquePtr space(create_space("test", 8 * MB, 32 * MB, 16 * MB, NULL)); EXPECT_TRUE(space.get() == NULL); } } +TEST_F(SpaceTest, Init_DlMallocSpace) { + InitTestBody(SpaceTest::CreateDlMallocSpace); +} +TEST_F(SpaceTest, Init_RosAllocSpace) { + InitTestBody(SpaceTest::CreateRosAllocSpace); +} + // TODO: This test is not very good, we should improve it. // The test should do more allocations before the creation of the ZygoteSpace, and then do // allocations after the ZygoteSpace is created. The test should also do some GCs to ensure that // the GC works with the ZygoteSpace. -TEST_F(SpaceTest, ZygoteSpace) { - size_t dummy = 0; - MallocSpace* space(DlMallocSpace::Create("test", 4 * MB, 16 * MB, 16 * MB, NULL)); - ASSERT_TRUE(space != NULL); - - // Make space findable to the heap, will also delete space when runtime is cleaned up - AddSpace(space); - Thread* self = Thread::Current(); - - // Succeeds, fits without adjusting the footprint limit. - mirror::Object* ptr1 = space->Alloc(self, 1 * MB, &dummy); - EXPECT_TRUE(ptr1 != NULL); - InstallClass(ptr1, 1 * MB); - - // Fails, requires a higher footprint limit. - mirror::Object* ptr2 = space->Alloc(self, 8 * MB, &dummy); - EXPECT_TRUE(ptr2 == NULL); - - // Succeeds, adjusts the footprint. - size_t ptr3_bytes_allocated; - mirror::Object* ptr3 = space->AllocWithGrowth(self, 8 * MB, &ptr3_bytes_allocated); - EXPECT_TRUE(ptr3 != NULL); - EXPECT_LE(8U * MB, ptr3_bytes_allocated); - InstallClass(ptr3, 8 * MB); - - // Fails, requires a higher footprint limit. - mirror::Object* ptr4 = space->Alloc(self, 8 * MB, &dummy); - EXPECT_TRUE(ptr4 == NULL); - - // Also fails, requires a higher allowed footprint. - mirror::Object* ptr5 = space->AllocWithGrowth(self, 8 * MB, &dummy); - EXPECT_TRUE(ptr5 == NULL); - - // Release some memory. - size_t free3 = space->AllocationSize(ptr3); - EXPECT_EQ(free3, ptr3_bytes_allocated); - EXPECT_EQ(free3, space->Free(self, ptr3)); - EXPECT_LE(8U * MB, free3); - - // Succeeds, now that memory has been freed. - mirror::Object* ptr6 = space->AllocWithGrowth(self, 9 * MB, &dummy); - EXPECT_TRUE(ptr6 != NULL); - InstallClass(ptr6, 9 * MB); - - // Final clean up. - size_t free1 = space->AllocationSize(ptr1); - space->Free(self, ptr1); - EXPECT_LE(1U * MB, free1); - - // Make sure that the zygote space isn't directly at the start of the space. - space->Alloc(self, 1U * MB, &dummy); - space = space->CreateZygoteSpace("alloc space"); - - // Make space findable to the heap, will also delete space when runtime is cleaned up - AddSpace(space); - - // Succeeds, fits without adjusting the footprint limit. - ptr1 = space->Alloc(self, 1 * MB, &dummy); - EXPECT_TRUE(ptr1 != NULL); - InstallClass(ptr1, 1 * MB); - - // Fails, requires a higher footprint limit. - ptr2 = space->Alloc(self, 8 * MB, &dummy); - EXPECT_TRUE(ptr2 == NULL); - - // Succeeds, adjusts the footprint. - ptr3 = space->AllocWithGrowth(self, 2 * MB, &dummy); - EXPECT_TRUE(ptr3 != NULL); - InstallClass(ptr3, 2 * MB); - space->Free(self, ptr3); - - // Final clean up. - free1 = space->AllocationSize(ptr1); - space->Free(self, ptr1); - EXPECT_LE(1U * MB, free1); +void SpaceTest::ZygoteSpaceTestBody(CreateSpaceFn create_space) { + size_t dummy = 0; + MallocSpace* space(create_space("test", 4 * MB, 16 * MB, 16 * MB, NULL)); + ASSERT_TRUE(space != NULL); + + // Make space findable to the heap, will also delete space when runtime is cleaned up + AddSpace(space); + Thread* self = Thread::Current(); + + // Succeeds, fits without adjusting the footprint limit. + mirror::Object* ptr1 = space->Alloc(self, 1 * MB, &dummy); + EXPECT_TRUE(ptr1 != NULL); + InstallClass(ptr1, 1 * MB); + + // Fails, requires a higher footprint limit. + mirror::Object* ptr2 = space->Alloc(self, 8 * MB, &dummy); + EXPECT_TRUE(ptr2 == NULL); + + // Succeeds, adjusts the footprint. + size_t ptr3_bytes_allocated; + mirror::Object* ptr3 = space->AllocWithGrowth(self, 8 * MB, &ptr3_bytes_allocated); + EXPECT_TRUE(ptr3 != NULL); + EXPECT_LE(8U * MB, ptr3_bytes_allocated); + InstallClass(ptr3, 8 * MB); + + // Fails, requires a higher footprint limit. + mirror::Object* ptr4 = space->Alloc(self, 8 * MB, &dummy); + EXPECT_TRUE(ptr4 == NULL); + + // Also fails, requires a higher allowed footprint. + mirror::Object* ptr5 = space->AllocWithGrowth(self, 8 * MB, &dummy); + EXPECT_TRUE(ptr5 == NULL); + + // Release some memory. + size_t free3 = space->AllocationSize(ptr3); + EXPECT_EQ(free3, ptr3_bytes_allocated); + EXPECT_EQ(free3, space->Free(self, ptr3)); + EXPECT_LE(8U * MB, free3); + + // Succeeds, now that memory has been freed. + mirror::Object* ptr6 = space->AllocWithGrowth(self, 9 * MB, &dummy); + EXPECT_TRUE(ptr6 != NULL); + InstallClass(ptr6, 9 * MB); + + // Final clean up. + size_t free1 = space->AllocationSize(ptr1); + space->Free(self, ptr1); + EXPECT_LE(1U * MB, free1); + + // Make sure that the zygote space isn't directly at the start of the space. + space->Alloc(self, 1U * MB, &dummy); + space = space->CreateZygoteSpace("alloc space"); + + // Make space findable to the heap, will also delete space when runtime is cleaned up + AddSpace(space); + + // Succeeds, fits without adjusting the footprint limit. + ptr1 = space->Alloc(self, 1 * MB, &dummy); + EXPECT_TRUE(ptr1 != NULL); + InstallClass(ptr1, 1 * MB); + + // Fails, requires a higher footprint limit. + ptr2 = space->Alloc(self, 8 * MB, &dummy); + EXPECT_TRUE(ptr2 == NULL); + + // Succeeds, adjusts the footprint. + ptr3 = space->AllocWithGrowth(self, 2 * MB, &dummy); + EXPECT_TRUE(ptr3 != NULL); + InstallClass(ptr3, 2 * MB); + space->Free(self, ptr3); + + // Final clean up. + free1 = space->AllocationSize(ptr1); + space->Free(self, ptr1); + EXPECT_LE(1U * MB, free1); +} + +TEST_F(SpaceTest, ZygoteSpace_DlMallocSpace) { + ZygoteSpaceTestBody(SpaceTest::CreateDlMallocSpace); } -TEST_F(SpaceTest, AllocAndFree) { +TEST_F(SpaceTest, ZygoteSpace_RosAllocSpace) { + ZygoteSpaceTestBody(SpaceTest::CreateRosAllocSpace); +} + +void SpaceTest::AllocAndFreeTestBody(CreateSpaceFn create_space) { size_t dummy = 0; - DlMallocSpace* space(DlMallocSpace::Create("test", 4 * MB, 16 * MB, 16 * MB, NULL)); + MallocSpace* space(create_space("test", 4 * MB, 16 * MB, 16 * MB, NULL)); ASSERT_TRUE(space != NULL); Thread* self = Thread::Current(); @@ -231,6 +262,13 @@ TEST_F(SpaceTest, AllocAndFree) { EXPECT_LE(1U * MB, free1); } +TEST_F(SpaceTest, AllocAndFree_DlMallocSpace) { + AllocAndFreeTestBody(SpaceTest::CreateDlMallocSpace); +} +TEST_F(SpaceTest, AllocAndFree_RosAllocSpace) { + AllocAndFreeTestBody(SpaceTest::CreateRosAllocSpace); +} + TEST_F(SpaceTest, LargeObjectTest) { size_t rand_seed = 0; for (size_t i = 0; i < 2; ++i) { @@ -292,8 +330,8 @@ TEST_F(SpaceTest, LargeObjectTest) { } } -TEST_F(SpaceTest, AllocAndFreeList) { - DlMallocSpace* space(DlMallocSpace::Create("test", 4 * MB, 16 * MB, 16 * MB, NULL)); +void SpaceTest::AllocAndFreeListTestBody(CreateSpaceFn create_space) { + MallocSpace* space(create_space("test", 4 * MB, 16 * MB, 16 * MB, NULL)); ASSERT_TRUE(space != NULL); // Make space findable to the heap, will also delete space when runtime is cleaned up @@ -332,7 +370,14 @@ TEST_F(SpaceTest, AllocAndFreeList) { } } -void SpaceTest::SizeFootPrintGrowthLimitAndTrimBody(DlMallocSpace* space, intptr_t object_size, +TEST_F(SpaceTest, AllocAndFreeList_DlMallocSpace) { + AllocAndFreeListTestBody(SpaceTest::CreateDlMallocSpace); +} +TEST_F(SpaceTest, AllocAndFreeList_RosAllocSpace) { + AllocAndFreeListTestBody(SpaceTest::CreateRosAllocSpace); +} + +void SpaceTest::SizeFootPrintGrowthLimitAndTrimBody(MallocSpace* space, intptr_t object_size, int round, size_t growth_limit) { if (((object_size > 0 && object_size >= static_cast(growth_limit))) || ((object_size < 0 && -object_size >= static_cast(growth_limit)))) { @@ -493,11 +538,11 @@ void SpaceTest::SizeFootPrintGrowthLimitAndTrimBody(DlMallocSpace* space, intptr EXPECT_LE(space->Size(), growth_limit); } -void SpaceTest::SizeFootPrintGrowthLimitAndTrimDriver(size_t object_size) { +void SpaceTest::SizeFootPrintGrowthLimitAndTrimDriver(size_t object_size, CreateSpaceFn create_space) { size_t initial_size = 4 * MB; size_t growth_limit = 8 * MB; size_t capacity = 16 * MB; - DlMallocSpace* space(DlMallocSpace::Create("test", initial_size, growth_limit, capacity, NULL)); + MallocSpace* space(create_space("test", initial_size, growth_limit, capacity, NULL)); ASSERT_TRUE(space != NULL); // Basic sanity @@ -518,16 +563,25 @@ void SpaceTest::SizeFootPrintGrowthLimitAndTrimDriver(size_t object_size) { } #define TEST_SizeFootPrintGrowthLimitAndTrim(name, size) \ - TEST_F(SpaceTest, SizeFootPrintGrowthLimitAndTrim_AllocationsOf_##name) { \ - SizeFootPrintGrowthLimitAndTrimDriver(size); \ + TEST_F(SpaceTest, SizeFootPrintGrowthLimitAndTrim_AllocationsOf_##name##_DlMallocSpace) { \ + SizeFootPrintGrowthLimitAndTrimDriver(size, SpaceTest::CreateDlMallocSpace); \ + } \ + TEST_F(SpaceTest, SizeFootPrintGrowthLimitAndTrim_RandomAllocationsWithMax_##name##_DlMallocSpace) { \ + SizeFootPrintGrowthLimitAndTrimDriver(-size, SpaceTest::CreateDlMallocSpace); \ } \ - TEST_F(SpaceTest, SizeFootPrintGrowthLimitAndTrim_RandomAllocationsWithMax_##name) { \ - SizeFootPrintGrowthLimitAndTrimDriver(-size); \ + TEST_F(SpaceTest, SizeFootPrintGrowthLimitAndTrim_AllocationsOf_##name##_RosAllocSpace) { \ + SizeFootPrintGrowthLimitAndTrimDriver(size, SpaceTest::CreateRosAllocSpace); \ + } \ + TEST_F(SpaceTest, SizeFootPrintGrowthLimitAndTrim_RandomAllocationsWithMax_##name##_RosAllocSpace) { \ + SizeFootPrintGrowthLimitAndTrimDriver(-size, SpaceTest::CreateRosAllocSpace); \ } // Each size test is its own test so that we get a fresh heap each time -TEST_F(SpaceTest, SizeFootPrintGrowthLimitAndTrim_AllocationsOf_12B) { - SizeFootPrintGrowthLimitAndTrimDriver(12); +TEST_F(SpaceTest, SizeFootPrintGrowthLimitAndTrim_AllocationsOf_12B_DlMallocSpace) { + SizeFootPrintGrowthLimitAndTrimDriver(12, SpaceTest::CreateDlMallocSpace); +} +TEST_F(SpaceTest, SizeFootPrintGrowthLimitAndTrim_AllocationsOf_12B_RosAllocSpace) { + SizeFootPrintGrowthLimitAndTrimDriver(12, SpaceTest::CreateRosAllocSpace); } TEST_SizeFootPrintGrowthLimitAndTrim(16B, 16) TEST_SizeFootPrintGrowthLimitAndTrim(24B, 24) From f043de4fbcf8eaa72b55597ccc6b2ea5b26a24d2 Mon Sep 17 00:00:00 2001 From: Mathieu Chartier Date: Tue, 10 Dec 2013 14:37:16 -0800 Subject: [PATCH 0264/2402] Add missing push/pop shadow frame to artInterpreterToCompiledCodeBridge. EnsureInitialized can cause GC to occur. Since the shadow frame wasn't being pushed it meant that the references inside wouldn't get updated by the moving garbage collector. Bug: 12022098 Change-Id: I1f3fda41d14a2cf51cf524874de54d7766c362e9 --- runtime/entrypoints/interpreter/interpreter_entrypoints.cc | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/runtime/entrypoints/interpreter/interpreter_entrypoints.cc b/runtime/entrypoints/interpreter/interpreter_entrypoints.cc index 05c02f22fa0..df4ec3a6cee 100644 --- a/runtime/entrypoints/interpreter/interpreter_entrypoints.cc +++ b/runtime/entrypoints/interpreter/interpreter_entrypoints.cc @@ -33,11 +33,14 @@ extern "C" void artInterpreterToCompiledCodeBridge(Thread* self, MethodHelper& m if (method->IsStatic()) { mirror::Class* declaringClass = method->GetDeclaringClass(); if (UNLIKELY(!declaringClass->IsInitializing())) { + self->PushShadowFrame(shadow_frame); if (UNLIKELY(!Runtime::Current()->GetClassLinker()->EnsureInitialized(declaringClass, true, true))) { - DCHECK(Thread::Current()->IsExceptionPending()); + self->PopShadowFrame(); + DCHECK(self->IsExceptionPending()); return; } + self->PopShadowFrame(); CHECK(declaringClass->IsInitializing()); } } From cdcfdfcb704416882beec98f5a790a65c9b798ae Mon Sep 17 00:00:00 2001 From: buzbee Date: Tue, 10 Dec 2013 15:24:09 -0800 Subject: [PATCH 0265/2402] Art: fix basic block optimization pass A bracket mismatch in Change 70885 inadvertently prevented the basic block optimization pass from running in most cases. Fixed here. Change-Id: I33f2687904cc05c90f74fb3bdc8f312d009cc0ac --- compiler/dex/mir_optimization.cc | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/compiler/dex/mir_optimization.cc b/compiler/dex/mir_optimization.cc index 635393796a4..5d839910019 100644 --- a/compiler/dex/mir_optimization.cc +++ b/compiler/dex/mir_optimization.cc @@ -923,11 +923,11 @@ void MIRGraph::BasicBlockOptimization() { for (unsigned int i = 0; i < extended_basic_blocks_.size(); i++) { BasicBlockOpt(GetBasicBlock(extended_basic_blocks_[i])); } - } - } else { - PreOrderDfsIterator iter(this); - for (BasicBlock* bb = iter.Next(); bb != NULL; bb = iter.Next()) { - BasicBlockOpt(bb); + } else { + PreOrderDfsIterator iter(this); + for (BasicBlock* bb = iter.Next(); bb != NULL; bb = iter.Next()) { + BasicBlockOpt(bb); + } } } if (cu_->enable_debug & (1 << kDebugDumpCFG)) { From c528dba35b5faece51ca658fc008b688f8b690ad Mon Sep 17 00:00:00 2001 From: Mathieu Chartier Date: Tue, 26 Nov 2013 12:00:11 -0800 Subject: [PATCH 0266/2402] Enable moving classes. Slight reduction in Zygote size, memory savings are in the noise. Before: Zygote size: 8739224 After: Zygote size: 8733568 Fixed a bug where we didn't set the concurrent start bytes after switching the allocator from bump pointer to ROSAlloc in the zygote. This caused excessive memory usage. Added the method verifiers as roots to fix an issue caused by RegTypes holding a Class*. Added logic to clear card table in the SemiSpace collector, this reduces DalvikOther from ~2400k -> ~1760k when using the SemiSpace collector. Added a missing lock to the timing loggers which caused a rare one time crash in std::set. Bug: 11771255 Bug: 8499494 Bug: 10802951 Change-Id: I99d2b528cd51c1c5ed7012e3220b3aefded680ae --- compiler/driver/compiler_driver.cc | 28 +- compiler/image_test.cc | 10 +- runtime/base/timing_logger.cc | 2 + runtime/base/timing_logger.h | 7 +- runtime/class_linker.cc | 313 ++++++++++-------- runtime/class_linker.h | 98 +++--- runtime/class_linker_test.cc | 103 +++--- runtime/debugger.cc | 18 +- runtime/entrypoints/entrypoint_utils.h | 22 +- .../interpreter/interpreter_entrypoints.cc | 6 +- .../portable_trampoline_entrypoints.cc | 6 +- .../quick/quick_trampoline_entrypoints.cc | 6 +- runtime/exception_test.cc | 8 +- runtime/gc/accounting/card_table.cc | 4 +- runtime/gc/accounting/mod_union_table.cc | 2 +- runtime/gc/collector/mark_sweep-inl.h | 7 +- runtime/gc/collector/mark_sweep.h | 8 +- runtime/gc/collector/semi_space.cc | 11 +- runtime/gc/heap-inl.h | 41 ++- runtime/gc/heap.cc | 14 +- runtime/gc/heap.h | 21 +- runtime/globals.h | 2 +- runtime/interpreter/interpreter.cc | 7 +- runtime/interpreter/interpreter_common.h | 7 +- runtime/jni_internal.cc | 54 +-- runtime/mirror/array-inl.h | 7 + runtime/mirror/array.h | 3 + runtime/mirror/art_field.cc | 7 + runtime/mirror/art_field.h | 2 + runtime/mirror/art_method.cc | 7 + runtime/mirror/art_method.h | 4 + runtime/mirror/class.cc | 6 + runtime/mirror/class.h | 2 + runtime/mirror/object.h | 10 +- runtime/mirror/stack_trace_element.cc | 7 + runtime/mirror/stack_trace_element.h | 3 +- runtime/mirror/string.cc | 27 +- runtime/mirror/string.h | 15 +- runtime/mirror/throwable.cc | 6 + runtime/mirror/throwable.h | 3 + runtime/native/java_lang_Class.cc | 14 +- .../native/java_lang_reflect_Constructor.cc | 12 +- runtime/native/java_lang_reflect_Field.cc | 22 +- runtime/object_utils.h | 18 +- runtime/reflection.cc | 8 +- runtime/runtime.cc | 73 +++- runtime/runtime.h | 18 +- runtime/thread.cc | 17 +- runtime/throw_location.cc | 7 +- runtime/verifier/method_verifier.cc | 6 + runtime/verifier/method_verifier.h | 2 + runtime/verifier/reg_type.cc | 6 + runtime/verifier/reg_type.h | 5 +- runtime/verifier/reg_type_cache.cc | 6 + runtime/verifier/reg_type_cache.h | 3 + 55 files changed, 679 insertions(+), 452 deletions(-) diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc index 7b428793ab1..8e666dd2a80 100644 --- a/compiler/driver/compiler_driver.cc +++ b/compiler/driver/compiler_driver.cc @@ -1596,7 +1596,8 @@ static void ResolveType(const ParallelCompilationManager* manager, size_t type_i ClassLinker* class_linker = manager->GetClassLinker(); const DexFile& dex_file = *manager->GetDexFile(); SirtRef dex_cache(soa.Self(), class_linker->FindDexCache(dex_file)); - SirtRef class_loader(soa.Self(), soa.Decode(manager->GetClassLoader())); + SirtRef class_loader( + soa.Self(), soa.Decode(manager->GetClassLoader())); mirror::Class* klass = class_linker->ResolveType(dex_file, type_idx, dex_cache, class_loader); if (klass == NULL) { @@ -1651,8 +1652,8 @@ static void VerifyClass(const ParallelCompilationManager* manager, size_t class_ jobject jclass_loader = manager->GetClassLoader(); SirtRef class_loader( soa.Self(), soa.Decode(jclass_loader)); - mirror::Class* klass = class_linker->FindClass(descriptor, class_loader); - if (klass == NULL) { + SirtRef klass(soa.Self(), class_linker->FindClass(descriptor, class_loader)); + if (klass.get() == nullptr) { CHECK(soa.Self()->IsExceptionPending()); soa.Self()->ClearException(); @@ -1669,8 +1670,8 @@ static void VerifyClass(const ParallelCompilationManager* manager, size_t class_ LOG(ERROR) << "Verification failed on class " << PrettyDescriptor(descriptor) << " because: " << error_msg; } - } else if (!SkipClass(jclass_loader, dex_file, klass)) { - CHECK(klass->IsResolved()) << PrettyClass(klass); + } else if (!SkipClass(jclass_loader, dex_file, klass.get())) { + CHECK(klass->IsResolved()) << PrettyClass(klass.get()); class_linker->VerifyClass(klass); if (klass->IsErroneous()) { @@ -1680,7 +1681,7 @@ static void VerifyClass(const ParallelCompilationManager* manager, size_t class_ } CHECK(klass->IsCompileTimeVerified() || klass->IsErroneous()) - << PrettyDescriptor(klass) << ": state=" << klass->GetStatus(); + << PrettyDescriptor(klass.get()) << ": state=" << klass->GetStatus(); } soa.Self()->AssertNoPendingException(); } @@ -2123,9 +2124,10 @@ static void InitializeClass(const ParallelCompilationManager* manager, size_t cl ScopedObjectAccess soa(Thread::Current()); SirtRef class_loader(soa.Self(), soa.Decode(jclass_loader)); - mirror::Class* klass = manager->GetClassLinker()->FindClass(descriptor, class_loader); + SirtRef klass(soa.Self(), + manager->GetClassLinker()->FindClass(descriptor, class_loader)); - if (klass != NULL && !SkipClass(jclass_loader, dex_file, klass)) { + if (klass.get() != nullptr && !SkipClass(jclass_loader, dex_file, klass.get())) { // Only try to initialize classes that were successfully verified. if (klass->IsVerified()) { // Attempt to initialize the class but bail if we either need to initialize the super-class @@ -2140,7 +2142,8 @@ static void InitializeClass(const ParallelCompilationManager* manager, size_t cl // parent-to-child and a child-to-parent lock ordering and consequent potential deadlock. // We need to use an ObjectLock due to potential suspension in the interpreting code. Rather // than use a special Object for the purpose we use the Class of java.lang.Class. - ObjectLock lock(soa.Self(), klass->GetClass()); + SirtRef sirt_klass(soa.Self(), klass->GetClass()); + ObjectLock lock(soa.Self(), &sirt_klass); // Attempt to initialize allowing initialization of parent classes but still not static // fields. manager->GetClassLinker()->EnsureInitialized(klass, false, true); @@ -2164,10 +2167,11 @@ static void InitializeClass(const ParallelCompilationManager* manager, size_t cl VLOG(compiler) << "Initializing: " << descriptor; if (strcmp("Ljava/lang/Void;", descriptor) == 0) { // Hand initialize j.l.Void to avoid Dex file operations in un-started runtime. - ObjectLock lock(soa.Self(), klass); + ObjectLock lock(soa.Self(), &klass); mirror::ObjectArray* fields = klass->GetSFields(); CHECK_EQ(fields->GetLength(), 1); - fields->Get(0)->SetObj(klass, manager->GetClassLinker()->FindPrimitiveClass('V')); + fields->Get(0)->SetObj(klass.get(), + manager->GetClassLinker()->FindPrimitiveClass('V')); klass->SetStatus(mirror::Class::kStatusInitialized, soa.Self()); } else { manager->GetClassLinker()->EnsureInitialized(klass, true, true); @@ -2180,7 +2184,7 @@ static void InitializeClass(const ParallelCompilationManager* manager, size_t cl // If successfully initialized place in SSB array. if (klass->IsInitialized()) { int32_t ssb_index = klass->GetDexTypeIndex(); - klass->GetDexCache()->GetInitializedStaticStorage()->Set(ssb_index, klass); + klass->GetDexCache()->GetInitializedStaticStorage()->Set(ssb_index, klass.get()); } } // Record the final class status if necessary. diff --git a/compiler/image_test.cc b/compiler/image_test.cc index c71cc977f53..3406fe62450 100644 --- a/compiler/image_test.cc +++ b/compiler/image_test.cc @@ -152,14 +152,14 @@ TEST_F(ImageTest, WriteRead) { const DexFile::ClassDef& class_def = dex->GetClassDef(i); const char* descriptor = dex->GetClassDescriptor(class_def); mirror::Class* klass = class_linker_->FindSystemClass(descriptor); - EXPECT_TRUE(klass != NULL) << descriptor; - EXPECT_LT(image_begin, reinterpret_cast(klass)) << descriptor; + EXPECT_TRUE(klass != nullptr) << descriptor; if (image_classes.find(descriptor) != image_classes.end()) { - // image classes should be located before the end of the image. + // Image classes should be located inside the image. + EXPECT_LT(image_begin, reinterpret_cast(klass)) << descriptor; EXPECT_LT(reinterpret_cast(klass), image_end) << descriptor; } else { - // non image classes should be in a space after the image. - EXPECT_GT(reinterpret_cast(klass), image_end) << descriptor; + EXPECT_TRUE(reinterpret_cast(klass) >= image_end || + reinterpret_cast(klass) < image_begin) << descriptor; } EXPECT_TRUE(Monitor::IsValidLockWord(klass->GetLockWord())); } diff --git a/runtime/base/timing_logger.cc b/runtime/base/timing_logger.cc index c8dee6d346f..bb32b2da88c 100644 --- a/runtime/base/timing_logger.cc +++ b/runtime/base/timing_logger.cc @@ -43,6 +43,7 @@ CumulativeLogger::~CumulativeLogger() { } void CumulativeLogger::SetName(const std::string& name) { + MutexLock mu(Thread::Current(), lock_); name_.assign(name); } @@ -61,6 +62,7 @@ void CumulativeLogger::Reset() { } uint64_t CumulativeLogger::GetTotalNs() const { + MutexLock mu(Thread::Current(), lock_); return GetTotalTime() * kAdjust; } diff --git a/runtime/base/timing_logger.h b/runtime/base/timing_logger.h index c1ff0a3619c..b0bcf107ae6 100644 --- a/runtime/base/timing_logger.h +++ b/runtime/base/timing_logger.h @@ -31,16 +31,15 @@ class TimingLogger; class CumulativeLogger { public: explicit CumulativeLogger(const std::string& name); - void prepare_stats(); ~CumulativeLogger(); void Start(); - void End(); - void Reset(); + void End() LOCKS_EXCLUDED(lock_); + void Reset() LOCKS_EXCLUDED(lock_); void Dump(std::ostream& os) LOCKS_EXCLUDED(lock_); uint64_t GetTotalNs() const; // Allow the name to be modified, particularly when the cumulative logger is a field within a // parent class that is unable to determine the "name" of a sub-class. - void SetName(const std::string& name); + void SetName(const std::string& name) LOCKS_EXCLUDED(lock_); void AddLogger(const TimingLogger& logger) LOCKS_EXCLUDED(lock_); size_t GetIterations() const; diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index 500cb59bee4..3e5d90dcdb5 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -530,7 +530,8 @@ void ClassLinker::RunRootClinits() { for (size_t i = 0; i < ClassLinker::kClassRootsMax; ++i) { mirror::Class* c = GetClassRoot(ClassRoot(i)); if (!c->IsArrayClass() && !c->IsPrimitive()) { - EnsureInitialized(GetClassRoot(ClassRoot(i)), true, true); + SirtRef sirt_class(self, GetClassRoot(ClassRoot(i))); + EnsureInitialized(sirt_class, true, true); self->AssertNoPendingException(); } } @@ -1133,7 +1134,7 @@ void ClassLinker::VisitRoots(RootVisitor* visitor, void* arg, bool only_dirty, b } { - ReaderMutexLock mu(self, *Locks::classlinker_classes_lock_); + WriterMutexLock mu(self, *Locks::classlinker_classes_lock_); if (!only_dirty || class_table_dirty_) { for (std::pair& it : class_table_) { it.second = down_cast(visitor(it.second, arg)); @@ -1156,7 +1157,7 @@ void ClassLinker::VisitClasses(ClassVisitor* visitor, void* arg) { if (dex_cache_image_class_lookup_required_) { MoveImageClassesToClassTable(); } - ReaderMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_); + WriterMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_); for (const std::pair& it : class_table_) { if (!visitor(it.second, arg)) { return; @@ -1249,7 +1250,10 @@ mirror::Class* ClassLinker::AllocClass(Thread* self, mirror::Class* java_lang_Cl size_t class_size) { DCHECK_GE(class_size, sizeof(mirror::Class)); gc::Heap* heap = Runtime::Current()->GetHeap(); - mirror::Object* k = heap->AllocNonMovableObject(self, java_lang_Class, class_size); + mirror::Object* k = + kMovingClasses ? + heap->AllocObject(self, java_lang_Class, class_size) : + heap->AllocNonMovableObject(self, java_lang_Class, class_size); if (UNLIKELY(k == NULL)) { CHECK(self->IsExceptionPending()); // OOME. return NULL; @@ -1287,21 +1291,23 @@ static mirror::Class* EnsureResolved(Thread* self, mirror::Class* klass) DCHECK(klass != NULL); // Wait for the class if it has not already been linked. if (!klass->IsResolved() && !klass->IsErroneous()) { - ObjectLock lock(self, klass); + SirtRef sirt_class(self, klass); + ObjectLock lock(self, &sirt_class); // Check for circular dependencies between classes. - if (!klass->IsResolved() && klass->GetClinitThreadId() == self->GetTid()) { - ThrowClassCircularityError(klass); - klass->SetStatus(mirror::Class::kStatusError, self); - return NULL; + if (!sirt_class->IsResolved() && sirt_class->GetClinitThreadId() == self->GetTid()) { + ThrowClassCircularityError(sirt_class.get()); + sirt_class->SetStatus(mirror::Class::kStatusError, self); + return nullptr; } // Wait for the pending initialization to complete. - while (!klass->IsResolved() && !klass->IsErroneous()) { + while (!sirt_class->IsResolved() && !sirt_class->IsErroneous()) { lock.WaitIgnoringInterrupts(); } + klass = sirt_class.get(); } if (klass->IsErroneous()) { ThrowEarlierClassFailure(klass); - return NULL; + return nullptr; } // Return the loaded class. No exceptions should be pending. CHECK(klass->IsResolved()) << PrettyClass(klass); @@ -1320,7 +1326,7 @@ mirror::Class* ClassLinker::FindSystemClass(const char* descriptor) { } mirror::Class* ClassLinker::FindClass(const char* descriptor, - SirtRef& class_loader) { + const SirtRef& class_loader) { DCHECK_NE(*descriptor, '\0') << "descriptor is empty string"; Thread* self = Thread::Current(); DCHECK(self != NULL); @@ -1403,7 +1409,7 @@ mirror::Class* ClassLinker::FindClass(const char* descriptor, } mirror::Class* ClassLinker::DefineClass(const char* descriptor, - SirtRef& class_loader, + const SirtRef& class_loader, const DexFile& dex_file, const DexFile::ClassDef& dex_class_def) { Thread* self = Thread::Current(); @@ -1440,7 +1446,7 @@ mirror::Class* ClassLinker::DefineClass(const char* descriptor, klass->SetStatus(mirror::Class::kStatusError, self); return NULL; } - ObjectLock lock(self, klass.get()); + ObjectLock lock(self, &klass); klass->SetClinitThreadId(self->GetTid()); // Add the newly loaded class to the loaded classes table. mirror::Class* existing = InsertClass(descriptor, klass.get(), Hash(descriptor)); @@ -1695,7 +1701,7 @@ void ClassLinker::FixupStaticTrampolines(mirror::Class* klass) { // Ignore virtual methods on the iterator. } -static void LinkCode(SirtRef& method, const OatFile::OatClass* oat_class, +static void LinkCode(const SirtRef& method, const OatFile::OatClass* oat_class, uint32_t method_index) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { // Method shouldn't have already been linked. @@ -1741,7 +1747,7 @@ static void LinkCode(SirtRef& method, const OatFile::OatClass void ClassLinker::LoadClass(const DexFile& dex_file, const DexFile::ClassDef& dex_class_def, - SirtRef& klass, + const SirtRef& klass, mirror::ClassLoader* class_loader) { CHECK(klass.get() != NULL); CHECK(klass->GetDexCache() != NULL); @@ -1861,7 +1867,8 @@ void ClassLinker::LoadClass(const DexFile& dex_file, } void ClassLinker::LoadField(const DexFile& /*dex_file*/, const ClassDataItemIterator& it, - SirtRef& klass, SirtRef& dst) { + const SirtRef& klass, + const SirtRef& dst) { uint32_t field_idx = it.GetMemberIndex(); dst->SetDexFieldIndex(field_idx); dst->SetDeclaringClass(klass.get()); @@ -1870,7 +1877,7 @@ void ClassLinker::LoadField(const DexFile& /*dex_file*/, const ClassDataItemIter mirror::ArtMethod* ClassLinker::LoadMethod(Thread* self, const DexFile& dex_file, const ClassDataItemIterator& it, - SirtRef& klass) { + const SirtRef& klass) { uint32_t dex_method_idx = it.GetMemberIndex(); const DexFile::MethodId& method_id = dex_file.GetMethodId(dex_method_idx); const char* method_name = dex_file.StringDataByIdx(method_id.name_idx_); @@ -1941,7 +1948,8 @@ void ClassLinker::AppendToBootClassPath(const DexFile& dex_file) { AppendToBootClassPath(dex_file, dex_cache); } -void ClassLinker::AppendToBootClassPath(const DexFile& dex_file, SirtRef& dex_cache) { +void ClassLinker::AppendToBootClassPath(const DexFile& dex_file, + const SirtRef& dex_cache) { CHECK(dex_cache.get() != NULL) << dex_file.GetLocation(); boot_class_path_.push_back(&dex_file); RegisterDexFile(dex_file, dex_cache); @@ -1962,7 +1970,8 @@ bool ClassLinker::IsDexFileRegistered(const DexFile& dex_file) const { return IsDexFileRegisteredLocked(dex_file); } -void ClassLinker::RegisterDexFileLocked(const DexFile& dex_file, SirtRef& dex_cache) { +void ClassLinker::RegisterDexFileLocked(const DexFile& dex_file, + const SirtRef& dex_cache) { dex_lock_.AssertExclusiveHeld(Thread::Current()); CHECK(dex_cache.get() != NULL) << dex_file.GetLocation(); CHECK(dex_cache->GetLocation()->Equals(dex_file.GetLocation())) @@ -1994,7 +2003,8 @@ void ClassLinker::RegisterDexFile(const DexFile& dex_file) { } } -void ClassLinker::RegisterDexFile(const DexFile& dex_file, SirtRef& dex_cache) { +void ClassLinker::RegisterDexFile(const DexFile& dex_file, + const SirtRef& dex_cache) { WriterMutexLock mu(Thread::Current(), dex_lock_); RegisterDexFileLocked(dex_file, dex_cache); } @@ -2040,11 +2050,13 @@ mirror::Class* ClassLinker::CreatePrimitiveClass(Thread* self, Primitive::Type t return InitializePrimitiveClass(klass, type); } -mirror::Class* ClassLinker::InitializePrimitiveClass(mirror::Class* primitive_class, Primitive::Type type) { +mirror::Class* ClassLinker::InitializePrimitiveClass(mirror::Class* primitive_class, + Primitive::Type type) { CHECK(primitive_class != NULL); // Must hold lock on object when initializing. Thread* self = Thread::Current(); - ObjectLock lock(self, primitive_class); + SirtRef sirt_class(self, primitive_class); + ObjectLock lock(self, &sirt_class); primitive_class->SetAccessFlags(kAccPublic | kAccFinal | kAccAbstract); primitive_class->SetPrimitiveType(type); primitive_class->SetStatus(mirror::Class::kStatusInitialized, self); @@ -2068,7 +2080,7 @@ mirror::Class* ClassLinker::InitializePrimitiveClass(mirror::Class* primitive_cl // // Returns NULL with an exception raised on failure. mirror::Class* ClassLinker::CreateArrayClass(const char* descriptor, - SirtRef& class_loader) { + const SirtRef& class_loader) { // Identify the underlying component type CHECK_EQ('[', descriptor[0]); mirror::Class* component_type = FindClass(descriptor + 1, class_loader); @@ -2138,7 +2150,7 @@ mirror::Class* ClassLinker::CreateArrayClass(const char* descriptor, } new_class->SetComponentType(component_type); } - ObjectLock lock(self, new_class.get()); // Must hold lock on object when initializing. + ObjectLock lock(self, &new_class); // Must hold lock on object when initializing. DCHECK(new_class->GetComponentType() != NULL); mirror::Class* java_lang_Object = GetClassRoot(kJavaLangObject); new_class->SetSuperClass(java_lang_Object); @@ -2421,10 +2433,10 @@ void ClassLinker::LookupClasses(const char* descriptor, std::vector& klass) { // TODO: assert that the monitor on the Class is held Thread* self = Thread::Current(); - ObjectLock lock(self, klass); + ObjectLock lock(self, &klass); // Don't attempt to re-verify if already sufficiently verified. if (klass->IsVerified() || @@ -2435,7 +2447,7 @@ void ClassLinker::VerifyClass(mirror::Class* klass) { // The class might already be erroneous, for example at compile time if we attempted to verify // this class as a parent to another. if (klass->IsErroneous()) { - ThrowEarlierClassFailure(klass); + ThrowEarlierClassFailure(klass.get()); return; } @@ -2443,7 +2455,7 @@ void ClassLinker::VerifyClass(mirror::Class* klass) { klass->SetStatus(mirror::Class::kStatusVerifying, self); } else { CHECK_EQ(klass->GetStatus(), mirror::Class::kStatusRetryVerificationAtRuntime) - << PrettyClass(klass); + << PrettyClass(klass.get()); CHECK(!Runtime::Current()->IsCompiler()); klass->SetStatus(mirror::Class::kStatusVerifyingAtRuntime, self); } @@ -2452,23 +2464,23 @@ void ClassLinker::VerifyClass(mirror::Class* klass) { SirtRef super(self, klass->GetSuperClass()); if (super.get() != NULL) { // Acquire lock to prevent races on verifying the super class. - ObjectLock lock(self, super.get()); + ObjectLock lock(self, &super); if (!super->IsVerified() && !super->IsErroneous()) { - VerifyClass(super.get()); + VerifyClass(super); } if (!super->IsCompileTimeVerified()) { std::string error_msg(StringPrintf("Rejecting class %s that attempts to sub-class erroneous class %s", - PrettyDescriptor(klass).c_str(), + PrettyDescriptor(klass.get()).c_str(), PrettyDescriptor(super.get()).c_str())); LOG(ERROR) << error_msg << " in " << klass->GetDexCache()->GetLocation()->ToModifiedUtf8(); SirtRef cause(self, self->GetException(NULL)); - if (cause.get() != NULL) { + if (cause.get() != nullptr) { self->ClearException(); } - ThrowVerifyError(klass, "%s", error_msg.c_str()); - if (cause.get() != NULL) { - self->GetException(NULL)->SetCause(cause.get()); + ThrowVerifyError(klass.get(), "%s", error_msg.c_str()); + if (cause.get() != nullptr) { + self->GetException(nullptr)->SetCause(cause.get()); } klass->SetStatus(mirror::Class::kStatusError, self); return; @@ -2478,26 +2490,26 @@ void ClassLinker::VerifyClass(mirror::Class* klass) { // Try to use verification information from the oat file, otherwise do runtime verification. const DexFile& dex_file = *klass->GetDexCache()->GetDexFile(); mirror::Class::Status oat_file_class_status(mirror::Class::kStatusNotReady); - bool preverified = VerifyClassUsingOatFile(dex_file, klass, oat_file_class_status); + bool preverified = VerifyClassUsingOatFile(dex_file, klass.get(), oat_file_class_status); if (oat_file_class_status == mirror::Class::kStatusError) { VLOG(class_linker) << "Skipping runtime verification of erroneous class " - << PrettyDescriptor(klass) << " in " + << PrettyDescriptor(klass.get()) << " in " << klass->GetDexCache()->GetLocation()->ToModifiedUtf8(); - ThrowVerifyError(klass, "Rejecting class %s because it failed compile-time verification", - PrettyDescriptor(klass).c_str()); + ThrowVerifyError(klass.get(), "Rejecting class %s because it failed compile-time verification", + PrettyDescriptor(klass.get()).c_str()); klass->SetStatus(mirror::Class::kStatusError, self); return; } verifier::MethodVerifier::FailureKind verifier_failure = verifier::MethodVerifier::kNoFailure; std::string error_msg; if (!preverified) { - verifier_failure = verifier::MethodVerifier::VerifyClass(klass, + verifier_failure = verifier::MethodVerifier::VerifyClass(klass.get(), Runtime::Current()->IsCompiler(), &error_msg); } if (preverified || verifier_failure != verifier::MethodVerifier::kHardFailure) { if (!preverified && verifier_failure != verifier::MethodVerifier::kNoFailure) { - VLOG(class_linker) << "Soft verification failure in class " << PrettyDescriptor(klass) + VLOG(class_linker) << "Soft verification failure in class " << PrettyDescriptor(klass.get()) << " in " << klass->GetDexCache()->GetLocation()->ToModifiedUtf8() << " because: " << error_msg; } @@ -2527,11 +2539,11 @@ void ClassLinker::VerifyClass(mirror::Class* klass) { } } } else { - LOG(ERROR) << "Verification failed on class " << PrettyDescriptor(klass) + LOG(ERROR) << "Verification failed on class " << PrettyDescriptor(klass.get()) << " in " << klass->GetDexCache()->GetLocation()->ToModifiedUtf8() << " because: " << error_msg; self->AssertNoPendingException(); - ThrowVerifyError(klass, "%s", error_msg.c_str()); + ThrowVerifyError(klass.get(), "%s", error_msg.c_str()); klass->SetStatus(mirror::Class::kStatusError, self); } if (preverified || verifier_failure == verifier::MethodVerifier::kNoFailure) { @@ -2625,7 +2637,8 @@ bool ClassLinker::VerifyClassUsingOatFile(const DexFile& dex_file, mirror::Class return false; } -void ClassLinker::ResolveClassExceptionHandlerTypes(const DexFile& dex_file, mirror::Class* klass) { +void ClassLinker::ResolveClassExceptionHandlerTypes(const DexFile& dex_file, + const SirtRef& klass) { for (size_t i = 0; i < klass->NumDirectMethods(); i++) { ResolveMethodExceptionHandlerTypes(dex_file, klass->GetDirectMethod(i)); } @@ -2763,13 +2776,13 @@ mirror::Class* ClassLinker::CreateProxyClass(ScopedObjectAccess& soa, jstring na self->AssertNoPendingException(); { - ObjectLock lock(self, klass.get()); // Must hold lock on object when resolved. + ObjectLock lock(self, &klass); // Must hold lock on object when resolved. // Link the fields and virtual methods, creating vtable and iftables SirtRef > sirt_interfaces( self, soa.Decode*>(interfaces)); if (!LinkClass(self, klass, sirt_interfaces)) { klass->SetStatus(mirror::Class::kStatusError, self); - return NULL; + return nullptr; } interfaces_sfield->SetObject(klass.get(), soa.Decode*>(interfaces)); @@ -2840,7 +2853,7 @@ mirror::ArtMethod* ClassLinker::FindMethodForProxy(const mirror::Class* proxy_cl mirror::ArtMethod* ClassLinker::CreateProxyConstructor(Thread* self, - SirtRef& klass, + const SirtRef& klass, mirror::Class* proxy_class) { // Create constructor for Proxy that must initialize h mirror::ObjectArray* proxy_direct_methods = @@ -2870,8 +2883,9 @@ static void CheckProxyConstructor(mirror::ArtMethod* constructor) DCHECK(constructor->IsPublic()); } -mirror::ArtMethod* ClassLinker::CreateProxyMethod(Thread* self, SirtRef& klass, - SirtRef& prototype) { +mirror::ArtMethod* ClassLinker::CreateProxyMethod(Thread* self, + const SirtRef& klass, + const SirtRef& prototype) { // Ensure prototype is in dex cache so that we can use the dex cache to look up the overridden // prototype method prototype->GetDeclaringClass()->GetDexCache()->SetResolvedMethod(prototype->GetDexMethodIndex(), @@ -2966,7 +2980,7 @@ bool ClassLinker::IsInitialized() const { return init_done_; } -bool ClassLinker::InitializeClass(mirror::Class* klass, bool can_init_statics, +bool ClassLinker::InitializeClass(const SirtRef& klass, bool can_init_statics, bool can_init_parents) { // see JLS 3rd edition, 12.4.2 "Detailed Initialization Procedure" for the locking protocol @@ -2978,14 +2992,14 @@ bool ClassLinker::InitializeClass(mirror::Class* klass, bool can_init_statics, } // Fast fail if initialization requires a full runtime. Not part of the JLS. - if (!CanWeInitializeClass(klass, can_init_statics, can_init_parents)) { + if (!CanWeInitializeClass(klass.get(), can_init_statics, can_init_parents)) { return false; } Thread* self = Thread::Current(); uint64_t t0; { - ObjectLock lock(self, klass); + ObjectLock lock(self, &klass); // Re-check under the lock in case another thread initialized ahead of us. if (klass->IsInitialized()) { @@ -2994,11 +3008,11 @@ bool ClassLinker::InitializeClass(mirror::Class* klass, bool can_init_statics, // Was the class already found to be erroneous? Done under the lock to match the JLS. if (klass->IsErroneous()) { - ThrowEarlierClassFailure(klass); + ThrowEarlierClassFailure(klass.get()); return false; } - CHECK(klass->IsResolved()) << PrettyClass(klass) << ": state=" << klass->GetStatus(); + CHECK(klass->IsResolved()) << PrettyClass(klass.get()) << ": state=" << klass->GetStatus(); if (!klass->IsVerified()) { VerifyClass(klass); @@ -3035,7 +3049,7 @@ bool ClassLinker::InitializeClass(mirror::Class* klass, bool can_init_statics, return false; } - CHECK_EQ(klass->GetStatus(), mirror::Class::kStatusVerified) << PrettyClass(klass); + CHECK_EQ(klass->GetStatus(), mirror::Class::kStatusVerified) << PrettyClass(klass.get()); // From here out other threads may observe that we're initializing and so changes of state // require the a notification. @@ -3051,16 +3065,17 @@ bool ClassLinker::InitializeClass(mirror::Class* klass, bool can_init_statics, if (!super_class->IsInitialized()) { CHECK(!super_class->IsInterface()); CHECK(can_init_parents); - bool super_initialized = InitializeClass(super_class, can_init_statics, true); + SirtRef sirt_super(self, super_class); + bool super_initialized = InitializeClass(sirt_super, can_init_statics, true); if (!super_initialized) { // The super class was verified ahead of entering initializing, we should only be here if // the super class became erroneous due to initialization. - CHECK(super_class->IsErroneous() && self->IsExceptionPending()) - << "Super class initialization failed for " << PrettyDescriptor(super_class) - << " that has unexpected status " << super_class->GetStatus() + CHECK(sirt_super->IsErroneous() && self->IsExceptionPending()) + << "Super class initialization failed for " << PrettyDescriptor(sirt_super.get()) + << " that has unexpected status " << sirt_super->GetStatus() << "\nPending exception:\n" << (self->GetException(NULL) != NULL ? self->GetException(NULL)->Dump() : ""); - ObjectLock lock(self, klass); + ObjectLock lock(self, &klass); // Initialization failed because the super-class is erroneous. klass->SetStatus(mirror::Class::kStatusError, self); return false; @@ -3069,7 +3084,7 @@ bool ClassLinker::InitializeClass(mirror::Class* klass, bool can_init_statics, } if (klass->NumStaticFields() > 0) { - ClassHelper kh(klass); + ClassHelper kh(klass.get()); const DexFile::ClassDef* dex_class_def = kh.GetClassDef(); CHECK(dex_class_def != NULL); const DexFile& dex_file = kh.GetDexFile(); @@ -3081,7 +3096,7 @@ bool ClassLinker::InitializeClass(mirror::Class* klass, bool can_init_statics, CHECK(can_init_statics); // We reordered the fields, so we need to be able to map the field indexes to the right fields. SafeMap field_map; - ConstructFieldMap(dex_file, *dex_class_def, klass, field_map); + ConstructFieldMap(dex_file, *dex_class_def, klass.get(), field_map); for (size_t i = 0; it.HasNext(); i++, it.Next()) { it.ReadValueToField(field_map.Get(i)); } @@ -3100,13 +3115,13 @@ bool ClassLinker::InitializeClass(mirror::Class* klass, bool can_init_statics, } // Opportunistically set static method trampolines to their destination. - FixupStaticTrampolines(klass); + FixupStaticTrampolines(klass.get()); uint64_t t1 = NanoTime(); bool success = true; { - ObjectLock lock(self, klass); + ObjectLock lock(self, &klass); if (self->IsExceptionPending()) { WrapExceptionInInitializer(); @@ -3122,7 +3137,7 @@ bool ClassLinker::InitializeClass(mirror::Class* klass, bool can_init_statics, // Set the class as initialized except if failed to initialize static fields. klass->SetStatus(mirror::Class::kStatusInitialized, self); if (VLOG_IS_ON(class_linker)) { - ClassHelper kh(klass); + ClassHelper kh(klass.get()); LOG(INFO) << "Initialized class " << kh.GetDescriptor() << " from " << kh.GetLocation(); } } @@ -3130,7 +3145,8 @@ bool ClassLinker::InitializeClass(mirror::Class* klass, bool can_init_statics, return success; } -bool ClassLinker::WaitForInitializeClass(mirror::Class* klass, Thread* self, ObjectLock& lock) +bool ClassLinker::WaitForInitializeClass(const SirtRef& klass, Thread* self, + ObjectLock& lock) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { while (true) { self->AssertNoPendingException(); @@ -3157,47 +3173,49 @@ bool ClassLinker::WaitForInitializeClass(mirror::Class* klass, Thread* self, Obj // The caller wants an exception, but it was thrown in a // different thread. Synthesize one here. ThrowNoClassDefFoundError(" failed for class %s; see exception in other thread", - PrettyDescriptor(klass).c_str()); + PrettyDescriptor(klass.get()).c_str()); return false; } if (klass->IsInitialized()) { return true; } - LOG(FATAL) << "Unexpected class status. " << PrettyClass(klass) << " is " << klass->GetStatus(); + LOG(FATAL) << "Unexpected class status. " << PrettyClass(klass.get()) << " is " + << klass->GetStatus(); } - LOG(FATAL) << "Not Reached" << PrettyClass(klass); + LOG(FATAL) << "Not Reached" << PrettyClass(klass.get()); } -bool ClassLinker::ValidateSuperClassDescriptors(const mirror::Class* klass) { +bool ClassLinker::ValidateSuperClassDescriptors(const SirtRef& klass) { if (klass->IsInterface()) { return true; } + Thread* self = Thread::Current(); // begin with the methods local to the superclass if (klass->HasSuperClass() && klass->GetClassLoader() != klass->GetSuperClass()->GetClassLoader()) { - const mirror::Class* super = klass->GetSuperClass(); + SirtRef super(self, klass->GetSuperClass()); for (int i = super->GetVTable()->GetLength() - 1; i >= 0; --i) { const mirror::ArtMethod* method = klass->GetVTable()->Get(i); if (method != super->GetVTable()->Get(i) && - !IsSameMethodSignatureInDifferentClassContexts(method, super, klass)) { - ThrowLinkageError(klass, "Class %s method %s resolves differently in superclass %s", - PrettyDescriptor(klass).c_str(), PrettyMethod(method).c_str(), - PrettyDescriptor(super).c_str()); + !IsSameMethodSignatureInDifferentClassContexts(method, super.get(), klass.get())) { + ThrowLinkageError(klass.get(), "Class %s method %s resolves differently in superclass %s", + PrettyDescriptor(klass.get()).c_str(), PrettyMethod(method).c_str(), + PrettyDescriptor(super.get()).c_str()); return false; } } } for (int32_t i = 0; i < klass->GetIfTableCount(); ++i) { - mirror::Class* interface = klass->GetIfTable()->GetInterface(i); + SirtRef interface(self, klass->GetIfTable()->GetInterface(i)); if (klass->GetClassLoader() != interface->GetClassLoader()) { for (size_t j = 0; j < interface->NumVirtualMethods(); ++j) { const mirror::ArtMethod* method = klass->GetIfTable()->GetMethodArray(i)->Get(j); - if (!IsSameMethodSignatureInDifferentClassContexts(method, interface, + if (!IsSameMethodSignatureInDifferentClassContexts(method, interface.get(), method->GetDeclaringClass())) { - ThrowLinkageError(klass, "Class %s method %s resolves differently in interface %s", + ThrowLinkageError(klass.get(), "Class %s method %s resolves differently in interface %s", PrettyDescriptor(method->GetDeclaringClass()).c_str(), PrettyMethod(method).c_str(), - PrettyDescriptor(interface).c_str()); + PrettyDescriptor(interface.get()).c_str()); return false; } } @@ -3214,17 +3232,22 @@ bool ClassLinker::IsSameMethodSignatureInDifferentClassContexts(const mirror::Ar if (klass1 == klass2) { return true; } + Thread* self = Thread::Current(); + CHECK(klass1 != nullptr); + CHECK(klass2 != nullptr); + SirtRef loader1(self, klass1->GetClassLoader()); + SirtRef loader2(self, klass2->GetClassLoader()); const DexFile& dex_file = *method->GetDeclaringClass()->GetDexCache()->GetDexFile(); const DexFile::ProtoId& proto_id = dex_file.GetMethodPrototype(dex_file.GetMethodId(method->GetDexMethodIndex())); for (DexFileParameterIterator it(dex_file, proto_id); it.HasNext(); it.Next()) { const char* descriptor = it.GetDescriptor(); - if (descriptor == NULL) { + if (descriptor == nullptr) { break; } if (descriptor[0] == 'L' || descriptor[0] == '[') { // Found a non-primitive type. - if (!IsSameDescriptorInDifferentClassContexts(descriptor, klass1, klass2)) { + if (!IsSameDescriptorInDifferentClassContexts(descriptor, loader1, loader2)) { return false; } } @@ -3232,47 +3255,42 @@ bool ClassLinker::IsSameMethodSignatureInDifferentClassContexts(const mirror::Ar // Check the return type const char* descriptor = dex_file.GetReturnTypeDescriptor(proto_id); if (descriptor[0] == 'L' || descriptor[0] == '[') { - if (!IsSameDescriptorInDifferentClassContexts(descriptor, klass1, klass2)) { + if (!IsSameDescriptorInDifferentClassContexts(descriptor, loader1, loader2)) { return false; } } return true; } -// Returns true if the descriptor resolves to the same class in the context of klass1 and klass2. +// Returns true if the descriptor resolves to the same class in the context of loader1 and loader2. bool ClassLinker::IsSameDescriptorInDifferentClassContexts(const char* descriptor, - const mirror::Class* klass1, - const mirror::Class* klass2) { - CHECK(descriptor != NULL); - CHECK(klass1 != NULL); - CHECK(klass2 != NULL); - if (klass1 == klass2) { - return true; - } + SirtRef& loader1, + SirtRef& loader2) { + CHECK(descriptor != nullptr); Thread* self = Thread::Current(); - SirtRef class_loader1(self, klass1->GetClassLoader()); - mirror::Class* found1 = FindClass(descriptor, class_loader1); - if (found1 == NULL) { - Thread::Current()->ClearException(); + SirtRef found1(self, FindClass(descriptor, loader1)); + if (found1.get() == nullptr) { + self->ClearException(); } - SirtRef class_loader2(self, klass2->GetClassLoader()); - mirror::Class* found2 = FindClass(descriptor, class_loader2); - if (found2 == NULL) { - Thread::Current()->ClearException(); + mirror::Class* found2 = FindClass(descriptor, loader2); + if (found2 == nullptr) { + self->ClearException(); } - return found1 == found2; + return found1.get() == found2; } -bool ClassLinker::EnsureInitialized(mirror::Class* c, bool can_init_fields, bool can_init_parents) { - DCHECK(c != NULL); +bool ClassLinker::EnsureInitialized(const SirtRef& c, bool can_init_fields, + bool can_init_parents) { + DCHECK(c.get() != NULL); if (c->IsInitialized()) { return true; } bool success = InitializeClass(c, can_init_fields, can_init_parents); if (!success) { - Thread* self = Thread::Current(); - CHECK(self->IsExceptionPending() || !can_init_fields || !can_init_parents) << PrettyClass(c); + if (can_init_fields && can_init_parents) { + CHECK(Thread::Current()->IsExceptionPending()) << PrettyClass(c.get()); + } } return success; } @@ -3285,13 +3303,14 @@ void ClassLinker::ConstructFieldMap(const DexFile& dex_file, const DexFile::Clas Thread* self = Thread::Current(); SirtRef dex_cache(self, c->GetDexCache()); SirtRef class_loader(self, c->GetClassLoader()); + CHECK(!kMovingFields); for (size_t i = 0; it.HasNextStaticField(); i++, it.Next()) { field_map.Put(i, ResolveField(dex_file, it.GetMemberIndex(), dex_cache, class_loader, true)); } } -bool ClassLinker::LinkClass(Thread* self, SirtRef& klass, - SirtRef >& interfaces) { +bool ClassLinker::LinkClass(Thread* self, const SirtRef& klass, + const SirtRef >& interfaces) { CHECK_EQ(mirror::Class::kStatusLoaded, klass->GetStatus()); if (!LinkSuperClass(klass)) { return false; @@ -3312,7 +3331,8 @@ bool ClassLinker::LinkClass(Thread* self, SirtRef& klass, return true; } -bool ClassLinker::LoadSuperAndInterfaces(SirtRef& klass, const DexFile& dex_file) { +bool ClassLinker::LoadSuperAndInterfaces(const SirtRef& klass, + const DexFile& dex_file) { CHECK_EQ(mirror::Class::kStatusIdx, klass->GetStatus()); const DexFile::ClassDef& class_def = dex_file.GetClassDef(klass->GetDexClassDefIndex()); uint16_t super_class_idx = class_def.superclass_idx_; @@ -3355,7 +3375,7 @@ bool ClassLinker::LoadSuperAndInterfaces(SirtRef& klass, const De return true; } -bool ClassLinker::LinkSuperClass(SirtRef& klass) { +bool ClassLinker::LinkSuperClass(const SirtRef& klass) { CHECK(!klass->IsPrimitive()); mirror::Class* super = klass->GetSuperClass(); if (klass.get() == GetClassRoot(kJavaLangObject)) { @@ -3414,8 +3434,8 @@ bool ClassLinker::LinkSuperClass(SirtRef& klass) { } // Populate the class vtable and itable. Compute return type indices. -bool ClassLinker::LinkMethods(SirtRef& klass, - SirtRef >& interfaces) { +bool ClassLinker::LinkMethods(const SirtRef& klass, + const SirtRef >& interfaces) { if (klass->IsInterface()) { // No vtable. size_t count = klass->NumVirtualMethods(); @@ -3435,7 +3455,7 @@ bool ClassLinker::LinkMethods(SirtRef& klass, return true; } -bool ClassLinker::LinkVirtualMethods(SirtRef& klass) { +bool ClassLinker::LinkVirtualMethods(const SirtRef& klass) { Thread* self = Thread::Current(); if (klass->HasSuperClass()) { uint32_t max_count = klass->NumVirtualMethods() + klass->GetSuperClass()->GetVTable()->GetLength(); @@ -3518,8 +3538,8 @@ bool ClassLinker::LinkVirtualMethods(SirtRef& klass) { return true; } -bool ClassLinker::LinkInterfaceMethods(SirtRef& klass, - SirtRef >& interfaces) { +bool ClassLinker::LinkInterfaceMethods(const SirtRef& klass, + const SirtRef >& interfaces) { // Set the imt table to be all conflicts by default. klass->SetImTable(Runtime::Current()->GetDefaultImt()); size_t super_ifcount; @@ -3529,14 +3549,17 @@ bool ClassLinker::LinkInterfaceMethods(SirtRef& klass, super_ifcount = 0; } size_t ifcount = super_ifcount; - ClassHelper kh(klass.get()); - uint32_t num_interfaces = - interfaces.get() == nullptr ? kh.NumDirectInterfaces() : interfaces->GetLength(); - ifcount += num_interfaces; - for (size_t i = 0; i < num_interfaces; i++) { - mirror::Class* interface = - interfaces.get() == nullptr ? kh.GetDirectInterface(i) : interfaces->Get(i); - ifcount += interface->GetIfTableCount(); + uint32_t num_interfaces; + { + ClassHelper kh(klass.get()); + num_interfaces = + interfaces.get() == nullptr ? kh.NumDirectInterfaces() : interfaces->GetLength(); + ifcount += num_interfaces; + for (size_t i = 0; i < num_interfaces; i++) { + mirror::Class* interface = + interfaces.get() == nullptr ? kh.GetDirectInterface(i) : interfaces->Get(i); + ifcount += interface->GetIfTableCount(); + } } if (ifcount == 0) { // Class implements no interfaces. @@ -3576,6 +3599,7 @@ bool ClassLinker::LinkInterfaceMethods(SirtRef& klass, // Flatten the interface inheritance hierarchy. size_t idx = super_ifcount; for (size_t i = 0; i < num_interfaces; i++) { + ClassHelper kh(klass.get()); mirror::Class* interface = interfaces.get() == nullptr ? kh.GetDirectInterface(i) : interfaces->Get(i); DCHECK(interface != NULL); @@ -3640,11 +3664,8 @@ bool ClassLinker::LinkInterfaceMethods(SirtRef& klass, return false; } std::vector miranda_list; - MethodHelper vtable_mh(NULL); - MethodHelper interface_mh(NULL); for (size_t i = 0; i < ifcount; ++i) { - mirror::Class* interface = iftable->GetInterface(i); - size_t num_methods = interface->NumVirtualMethods(); + size_t num_methods = iftable->GetInterface(i)->NumVirtualMethods(); if (num_methods > 0) { SirtRef > method_array(self, AllocArtMethodArray(self, num_methods)); @@ -3656,8 +3677,8 @@ bool ClassLinker::LinkInterfaceMethods(SirtRef& klass, SirtRef > vtable(self, klass->GetVTableDuringLinking()); for (size_t j = 0; j < num_methods; ++j) { - mirror::ArtMethod* interface_method = interface->GetVirtualMethod(j); - interface_mh.ChangeMethod(interface_method); + mirror::ArtMethod* interface_method = iftable->GetInterface(i)->GetVirtualMethod(j); + MethodHelper interface_mh(interface_method); int32_t k; // For each method listed in the interface's method list, find the // matching method in our class's method list. We want to favor the @@ -3669,7 +3690,7 @@ bool ClassLinker::LinkInterfaceMethods(SirtRef& klass, // matter which direction we go. We walk it backward anyway.) for (k = vtable->GetLength() - 1; k >= 0; --k) { mirror::ArtMethod* vtable_method = vtable->Get(k); - vtable_mh.ChangeMethod(vtable_method); + MethodHelper vtable_mh(vtable_method); if (interface_mh.HasSameNameAndSignature(&vtable_mh)) { if (!vtable_method->IsAbstract() && !vtable_method->IsPublic()) { ThrowIllegalAccessError(klass.get(), @@ -3694,7 +3715,7 @@ bool ClassLinker::LinkInterfaceMethods(SirtRef& klass, SirtRef miranda_method(self, NULL); for (size_t mir = 0; mir < miranda_list.size(); mir++) { mirror::ArtMethod* mir_method = miranda_list[mir]; - vtable_mh.ChangeMethod(mir_method); + MethodHelper vtable_mh(mir_method); if (interface_mh.HasSameNameAndSignature(&vtable_mh)) { miranda_method.reset(miranda_list[mir]); break; @@ -3772,12 +3793,12 @@ bool ClassLinker::LinkInterfaceMethods(SirtRef& klass, return true; } -bool ClassLinker::LinkInstanceFields(SirtRef& klass) { +bool ClassLinker::LinkInstanceFields(const SirtRef& klass) { CHECK(klass.get() != NULL); return LinkFields(klass, false); } -bool ClassLinker::LinkStaticFields(SirtRef& klass) { +bool ClassLinker::LinkStaticFields(const SirtRef& klass) { CHECK(klass.get() != NULL); size_t allocated_class_size = klass->GetClassSize(); bool success = LinkFields(klass, true); @@ -3813,7 +3834,7 @@ struct LinkFieldsComparator { } }; -bool ClassLinker::LinkFields(SirtRef& klass, bool is_static) { +bool ClassLinker::LinkFields(const SirtRef& klass, bool is_static) { size_t num_fields = is_static ? klass->NumStaticFields() : klass->NumInstanceFields(); @@ -3972,7 +3993,7 @@ bool ClassLinker::LinkFields(SirtRef& klass, bool is_static) { // Set the bitmap of reference offsets, refOffsets, from the ifields // list. -void ClassLinker::CreateReferenceInstanceOffsets(SirtRef& klass) { +void ClassLinker::CreateReferenceInstanceOffsets(const SirtRef& klass) { uint32_t reference_offsets = 0; mirror::Class* super_class = klass->GetSuperClass(); if (super_class != NULL) { @@ -3986,11 +4007,11 @@ void ClassLinker::CreateReferenceInstanceOffsets(SirtRef& klass) CreateReferenceOffsets(klass, false, reference_offsets); } -void ClassLinker::CreateReferenceStaticOffsets(SirtRef& klass) { +void ClassLinker::CreateReferenceStaticOffsets(const SirtRef& klass) { CreateReferenceOffsets(klass, true, 0); } -void ClassLinker::CreateReferenceOffsets(SirtRef& klass, bool is_static, +void ClassLinker::CreateReferenceOffsets(const SirtRef& klass, bool is_static, uint32_t reference_offsets) { size_t num_reference_fields = is_static ? klass->NumReferenceStaticFieldsDuringLinking() @@ -4023,7 +4044,7 @@ void ClassLinker::CreateReferenceOffsets(SirtRef& klass, bool is_ } mirror::String* ClassLinker::ResolveString(const DexFile& dex_file, uint32_t string_idx, - SirtRef& dex_cache) { + const SirtRef& dex_cache) { DCHECK(dex_cache.get() != nullptr); mirror::String* resolved = dex_cache->GetResolvedString(string_idx); if (resolved != NULL) { @@ -4045,8 +4066,8 @@ mirror::Class* ClassLinker::ResolveType(const DexFile& dex_file, uint16_t type_i } mirror::Class* ClassLinker::ResolveType(const DexFile& dex_file, uint16_t type_idx, - SirtRef& dex_cache, - SirtRef& class_loader) { + const SirtRef& dex_cache, + const SirtRef& class_loader) { DCHECK(dex_cache.get() != NULL); mirror::Class* resolved = dex_cache->GetResolvedType(type_idx); if (resolved == NULL) { @@ -4064,9 +4085,11 @@ mirror::Class* ClassLinker::ResolveType(const DexFile& dex_file, uint16_t type_i // Convert a ClassNotFoundException to a NoClassDefFoundError. SirtRef cause(self, self->GetException(NULL)); if (cause->InstanceOf(GetClassRoot(kJavaLangClassNotFoundException))) { + SirtRef sirt_resolved(self, resolved); Thread::Current()->ClearException(); ThrowNoClassDefFoundError("Failed resolution of: %s", descriptor); self->GetException(NULL)->SetCause(cause.get()); + resolved = sirt_resolved.get(); } } } @@ -4077,8 +4100,8 @@ mirror::Class* ClassLinker::ResolveType(const DexFile& dex_file, uint16_t type_i mirror::ArtMethod* ClassLinker::ResolveMethod(const DexFile& dex_file, uint32_t method_idx, - SirtRef& dex_cache, - SirtRef& class_loader, + const SirtRef& dex_cache, + const SirtRef& class_loader, const mirror::ArtMethod* referrer, InvokeType type) { DCHECK(dex_cache.get() != NULL); @@ -4223,8 +4246,8 @@ mirror::ArtMethod* ClassLinker::ResolveMethod(const DexFile& dex_file, } mirror::ArtField* ClassLinker::ResolveField(const DexFile& dex_file, uint32_t field_idx, - SirtRef& dex_cache, - SirtRef& class_loader, + const SirtRef& dex_cache, + const SirtRef& class_loader, bool is_static) { DCHECK(dex_cache.get() != nullptr); mirror::ArtField* resolved = dex_cache->GetResolvedField(field_idx); @@ -4263,8 +4286,8 @@ mirror::ArtField* ClassLinker::ResolveField(const DexFile& dex_file, uint32_t fi mirror::ArtField* ClassLinker::ResolveFieldJLS(const DexFile& dex_file, uint32_t field_idx, - SirtRef& dex_cache, - SirtRef& class_loader) { + const SirtRef& dex_cache, + const SirtRef& class_loader) { DCHECK(dex_cache.get() != nullptr); mirror::ArtField* resolved = dex_cache->GetResolvedField(field_idx); if (resolved != NULL) { diff --git a/runtime/class_linker.h b/runtime/class_linker.h index 4e2cc063b34..c0b5e8110b7 100644 --- a/runtime/class_linker.h +++ b/runtime/class_linker.h @@ -45,7 +45,7 @@ namespace mirror { } // namespace mirror class InternTable; -class ObjectLock; +template class ObjectLock; class ScopedObjectAccess; template class SirtRef; @@ -72,7 +72,7 @@ class ClassLinker { // Finds a class by its descriptor, loading it if necessary. // If class_loader is null, searches boot_class_path_. - mirror::Class* FindClass(const char* descriptor, SirtRef& class_loader) + mirror::Class* FindClass(const char* descriptor, const SirtRef& class_loader) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); mirror::Class* FindSystemClass(const char* descriptor) @@ -82,7 +82,8 @@ class ClassLinker { bool IsInitialized() const; // Define a new a class based on a ClassDef from a DexFile - mirror::Class* DefineClass(const char* descriptor, SirtRef& class_loader, + mirror::Class* DefineClass(const char* descriptor, + const SirtRef& class_loader, const DexFile& dex_file, const DexFile::ClassDef& dex_class_def) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); @@ -126,7 +127,7 @@ class ClassLinker { // Resolve a String with the given index from the DexFile, storing the // result in the DexCache. mirror::String* ResolveString(const DexFile& dex_file, uint32_t string_idx, - SirtRef& dex_cache) + const SirtRef& dex_cache) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); // Resolve a Type with the given index from the DexFile, storing the @@ -150,8 +151,8 @@ class ClassLinker { // type, since it may be referenced from but not contained within // the given DexFile. mirror::Class* ResolveType(const DexFile& dex_file, uint16_t type_idx, - SirtRef& dex_cache, - SirtRef& class_loader) + const SirtRef& dex_cache, + const SirtRef& class_loader) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); // Resolve a method with a given ID from the DexFile, storing the @@ -161,8 +162,8 @@ class ClassLinker { // virtual method. mirror::ArtMethod* ResolveMethod(const DexFile& dex_file, uint32_t method_idx, - SirtRef& dex_cache, - SirtRef& class_loader, + const SirtRef& dex_cache, + const SirtRef& class_loader, const mirror::ArtMethod* referrer, InvokeType type) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); @@ -182,8 +183,8 @@ class ClassLinker { // field. mirror::ArtField* ResolveField(const DexFile& dex_file, uint32_t field_idx, - SirtRef& dex_cache, - SirtRef& class_loader, + const SirtRef& dex_cache, + const SirtRef& class_loader, bool is_static) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); @@ -193,8 +194,8 @@ class ClassLinker { // field resolution semantics are followed. mirror::ArtField* ResolveFieldJLS(const DexFile& dex_file, uint32_t field_idx, - SirtRef& dex_cache, - SirtRef& class_loader) + const SirtRef& dex_cache, + const SirtRef& class_loader) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); // Get shorty from method index without resolution. Used to do handlerization. @@ -204,7 +205,8 @@ class ClassLinker { // Returns true on success, false if there's an exception pending. // can_run_clinit=false allows the compiler to attempt to init a class, // given the restriction that no execution is possible. - bool EnsureInitialized(mirror::Class* c, bool can_run_clinit, bool can_init_fields) + bool EnsureInitialized(const SirtRef& c, + bool can_init_fields, bool can_init_parents) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); // Initializes classes that have instances in the image but that have @@ -214,7 +216,7 @@ class ClassLinker { void RegisterDexFile(const DexFile& dex_file) LOCKS_EXCLUDED(dex_lock_) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - void RegisterDexFile(const DexFile& dex_file, SirtRef& dex_cache) + void RegisterDexFile(const DexFile& dex_file, const SirtRef& dex_cache) LOCKS_EXCLUDED(dex_lock_) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); @@ -303,11 +305,12 @@ class ClassLinker { size_t length) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - void VerifyClass(mirror::Class* klass) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + void VerifyClass(const SirtRef& klass) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); bool VerifyClassUsingOatFile(const DexFile& dex_file, mirror::Class* klass, mirror::Class::Status& oat_file_class_status) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - void ResolveClassExceptionHandlerTypes(const DexFile& dex_file, mirror::Class* klass) + void ResolveClassExceptionHandlerTypes(const DexFile& dex_file, + const SirtRef& klass) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); void ResolveMethodExceptionHandlerTypes(const DexFile& dex_file, mirror::ArtMethod* klass) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); @@ -380,7 +383,8 @@ class ClassLinker { // Alloc* convenience functions to avoid needing to pass in mirror::Class* // values that are known to the ClassLinker such as // kObjectArrayClass and kJavaLangString etc. - mirror::Class* AllocClass(Thread* self, size_t class_size) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + mirror::Class* AllocClass(Thread* self, size_t class_size) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); mirror::DexCache* AllocDexCache(Thread* self, const DexFile& dex_file) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); mirror::ArtField* AllocArtField(Thread* self) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); @@ -392,12 +396,12 @@ class ClassLinker { mirror::Class* CreateArrayClass(const char* descriptor, - SirtRef& class_loader) + const SirtRef& class_loader) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); void AppendToBootClassPath(const DexFile& dex_file) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - void AppendToBootClassPath(const DexFile& dex_file, SirtRef& dex_cache) + void AppendToBootClassPath(const DexFile& dex_file, const SirtRef& dex_cache) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); void ConstructFieldMap(const DexFile& dex_file, const DexFile::ClassDef& dex_class_def, @@ -409,17 +413,17 @@ class ClassLinker { void LoadClass(const DexFile& dex_file, const DexFile::ClassDef& dex_class_def, - SirtRef& klass, + const SirtRef& klass, mirror::ClassLoader* class_loader) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); void LoadField(const DexFile& dex_file, const ClassDataItemIterator& it, - SirtRef& klass, SirtRef& dst) + const SirtRef& klass, const SirtRef& dst) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); mirror::ArtMethod* LoadMethod(Thread* self, const DexFile& dex_file, const ClassDataItemIterator& dex_method, - SirtRef& klass) + const SirtRef& klass) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); void FixupStaticTrampolines(mirror::Class* klass) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); @@ -428,20 +432,22 @@ class ClassLinker { const OatFile::OatClass* GetOatClass(const DexFile& dex_file, uint16_t class_def_idx) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - void RegisterDexFileLocked(const DexFile& dex_file, SirtRef& dex_cache) + void RegisterDexFileLocked(const DexFile& dex_file, const SirtRef& dex_cache) EXCLUSIVE_LOCKS_REQUIRED(dex_lock_) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); bool IsDexFileRegisteredLocked(const DexFile& dex_file) const SHARED_LOCKS_REQUIRED(dex_lock_); - bool InitializeClass(mirror::Class* klass, bool can_run_clinit, bool can_init_parents) + bool InitializeClass(const SirtRef& klass, bool can_run_clinit, + bool can_init_parents) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - bool WaitForInitializeClass(mirror::Class* klass, Thread* self, ObjectLock& lock); - bool ValidateSuperClassDescriptors(const mirror::Class* klass) + bool WaitForInitializeClass(const SirtRef& klass, Thread* self, + ObjectLock& lock); + bool ValidateSuperClassDescriptors(const SirtRef& klass) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); bool IsSameDescriptorInDifferentClassContexts(const char* descriptor, - const mirror::Class* klass1, - const mirror::Class* klass2) + SirtRef& class_loader1, + SirtRef& class_loader2) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); bool IsSameMethodSignatureInDifferentClassContexts(const mirror::ArtMethod* method, @@ -449,40 +455,40 @@ class ClassLinker { const mirror::Class* klass2) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - bool LinkClass(Thread* self, SirtRef& klass, - SirtRef >& interfaces) + bool LinkClass(Thread* self, const SirtRef& klass, + const SirtRef >& interfaces) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - bool LinkSuperClass(SirtRef& klass) + bool LinkSuperClass(const SirtRef& klass) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - bool LoadSuperAndInterfaces(SirtRef& klass, const DexFile& dex_file) + bool LoadSuperAndInterfaces(const SirtRef& klass, const DexFile& dex_file) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - bool LinkMethods(SirtRef& klass, - SirtRef >& interfaces) + bool LinkMethods(const SirtRef& klass, + const SirtRef >& interfaces) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - bool LinkVirtualMethods(SirtRef& klass) + bool LinkVirtualMethods(const SirtRef& klass) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - bool LinkInterfaceMethods(SirtRef& klass, - SirtRef >& interfaces) + bool LinkInterfaceMethods(const SirtRef& klass, + const SirtRef >& interfaces) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - bool LinkStaticFields(SirtRef& klass) + bool LinkStaticFields(const SirtRef& klass) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - bool LinkInstanceFields(SirtRef& klass) + bool LinkInstanceFields(const SirtRef& klass) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - bool LinkFields(SirtRef& klass, bool is_static) + bool LinkFields(const SirtRef& klass, bool is_static) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - void CreateReferenceInstanceOffsets(SirtRef& klass) + void CreateReferenceInstanceOffsets(const SirtRef& klass) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - void CreateReferenceStaticOffsets(SirtRef& klass) + void CreateReferenceStaticOffsets(const SirtRef& klass) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - void CreateReferenceOffsets(SirtRef& klass, bool is_static, + void CreateReferenceOffsets(const SirtRef& klass, bool is_static, uint32_t reference_offsets) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); @@ -511,11 +517,11 @@ class ClassLinker { bool* open_failed) LOCKS_EXCLUDED(dex_lock_); - mirror::ArtMethod* CreateProxyConstructor(Thread* self, SirtRef& klass, + mirror::ArtMethod* CreateProxyConstructor(Thread* self, const SirtRef& klass, mirror::Class* proxy_class) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - mirror::ArtMethod* CreateProxyMethod(Thread* self, SirtRef& klass, - SirtRef& prototype) + mirror::ArtMethod* CreateProxyMethod(Thread* self, const SirtRef& klass, + const SirtRef& prototype) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); std::vector boot_class_path_; diff --git a/runtime/class_linker_test.cc b/runtime/class_linker_test.cc index b8bc474b107..34134fae0e6 100644 --- a/runtime/class_linker_test.cc +++ b/runtime/class_linker_test.cc @@ -95,8 +95,10 @@ class ClassLinkerTest : public CommonTest { const std::string& component_type, mirror::ClassLoader* class_loader) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - SirtRef loader(Thread::Current(), class_loader); - mirror::Class* array = class_linker_->FindClass(array_descriptor.c_str(), loader); + Thread* self = Thread::Current(); + SirtRef loader(self, class_loader); + SirtRef array(self, + class_linker_->FindClass(array_descriptor.c_str(), loader)); ClassHelper array_component_ch(array->GetComponentType()); EXPECT_STREQ(component_type.c_str(), array_component_ch.GetDescriptor()); EXPECT_EQ(class_loader, array->GetClassLoader()); @@ -104,10 +106,10 @@ class ClassLinkerTest : public CommonTest { AssertArrayClass(array_descriptor, array); } - void AssertArrayClass(const std::string& array_descriptor, mirror::Class* array) + void AssertArrayClass(const std::string& array_descriptor, const SirtRef& array) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - ClassHelper kh(array); - ASSERT_TRUE(array != NULL); + ClassHelper kh(array.get()); + ASSERT_TRUE(array.get() != NULL); ASSERT_TRUE(array->GetClass() != NULL); ASSERT_EQ(array->GetClass(), array->GetClass()->GetClass()); EXPECT_TRUE(array->GetClass()->GetSuperClass() != NULL); @@ -135,15 +137,14 @@ class ClassLinkerTest : public CommonTest { EXPECT_EQ(0U, array->NumVirtualMethods()); EXPECT_EQ(0U, array->NumInstanceFields()); EXPECT_EQ(0U, array->NumStaticFields()); - kh.ChangeClass(array); + kh.ChangeClass(array.get()); EXPECT_EQ(2U, kh.NumDirectInterfaces()); EXPECT_TRUE(array->GetVTable() != NULL); EXPECT_EQ(2, array->GetIfTableCount()); - mirror::IfTable* iftable = array->GetIfTable(); - ASSERT_TRUE(iftable != NULL); + ASSERT_TRUE(array->GetIfTable() != NULL); kh.ChangeClass(kh.GetDirectInterface(0)); EXPECT_STREQ(kh.GetDescriptor(), "Ljava/lang/Cloneable;"); - kh.ChangeClass(array); + kh.ChangeClass(array.get()); kh.ChangeClass(kh.GetDirectInterface(1)); EXPECT_STREQ(kh.GetDescriptor(), "Ljava/io/Serializable;"); } @@ -179,9 +180,9 @@ class ClassLinkerTest : public CommonTest { EXPECT_TRUE(fh.GetType() != NULL); } - void AssertClass(const std::string& descriptor, mirror::Class* klass) + void AssertClass(const std::string& descriptor, const SirtRef& klass) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - ClassHelper kh(klass); + ClassHelper kh(klass.get()); EXPECT_STREQ(descriptor.c_str(), kh.GetDescriptor()); if (descriptor == "Ljava/lang/Object;") { EXPECT_FALSE(klass->HasSuperClass()); @@ -197,7 +198,7 @@ class ClassLinkerTest : public CommonTest { EXPECT_FALSE(klass->IsErroneous()); EXPECT_FALSE(klass->IsArrayClass()); EXPECT_TRUE(klass->GetComponentType() == NULL); - EXPECT_TRUE(klass->IsInSamePackage(klass)); + EXPECT_TRUE(klass->IsInSamePackage(klass.get())); EXPECT_TRUE(mirror::Class::IsInSamePackage(kh.GetDescriptor(), kh.GetDescriptor())); if (klass->IsInterface()) { EXPECT_TRUE(klass->IsAbstract()); @@ -239,31 +240,31 @@ class ClassLinkerTest : public CommonTest { } EXPECT_FALSE(klass->IsPrimitive()); - EXPECT_TRUE(klass->CanAccess(klass)); + EXPECT_TRUE(klass->CanAccess(klass.get())); for (size_t i = 0; i < klass->NumDirectMethods(); i++) { mirror::ArtMethod* method = klass->GetDirectMethod(i); AssertMethod(method); EXPECT_TRUE(method->IsDirect()); - EXPECT_EQ(klass, method->GetDeclaringClass()); + EXPECT_EQ(klass.get(), method->GetDeclaringClass()); } for (size_t i = 0; i < klass->NumVirtualMethods(); i++) { mirror::ArtMethod* method = klass->GetVirtualMethod(i); AssertMethod(method); EXPECT_FALSE(method->IsDirect()); - EXPECT_TRUE(method->GetDeclaringClass()->IsAssignableFrom(klass)); + EXPECT_TRUE(method->GetDeclaringClass()->IsAssignableFrom(klass.get())); } for (size_t i = 0; i < klass->NumInstanceFields(); i++) { mirror::ArtField* field = klass->GetInstanceField(i); - AssertField(klass, field); + AssertField(klass.get(), field); EXPECT_FALSE(field->IsStatic()); } for (size_t i = 0; i < klass->NumStaticFields(); i++) { mirror::ArtField* field = klass->GetStaticField(i); - AssertField(klass, field); + AssertField(klass.get(), field); EXPECT_TRUE(field->IsStatic()); } @@ -291,24 +292,24 @@ class ClassLinkerTest : public CommonTest { } size_t total_num_reference_instance_fields = 0; - mirror::Class* k = klass; + mirror::Class* k = klass.get(); while (k != NULL) { total_num_reference_instance_fields += k->NumReferenceInstanceFields(); k = k->GetSuperClass(); } - EXPECT_EQ(klass->GetReferenceInstanceOffsets() == 0, - total_num_reference_instance_fields == 0); + EXPECT_EQ(klass->GetReferenceInstanceOffsets() == 0, total_num_reference_instance_fields == 0); } void AssertDexFileClass(mirror::ClassLoader* class_loader, const std::string& descriptor) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { ASSERT_TRUE(descriptor != NULL); - mirror::Class* klass = class_linker_->FindSystemClass(descriptor.c_str()); - ASSERT_TRUE(klass != NULL); - EXPECT_STREQ(descriptor.c_str(), ClassHelper(klass).GetDescriptor()); + SirtRef klass(Thread::Current(), + class_linker_->FindSystemClass(descriptor.c_str())); + ASSERT_TRUE(klass.get() != nullptr); + EXPECT_STREQ(descriptor.c_str(), ClassHelper(klass.get()).GetDescriptor()); EXPECT_EQ(class_loader, klass->GetClassLoader()); if (klass->IsPrimitive()) { - AssertPrimitiveClass(descriptor, klass); + AssertPrimitiveClass(descriptor, klass.get()); } else if (klass->IsArrayClass()) { AssertArrayClass(descriptor, klass); } else { @@ -852,7 +853,7 @@ TEST_F(ClassLinkerTest, TwoClassLoadersOneClass) { TEST_F(ClassLinkerTest, StaticFields) { ScopedObjectAccess soa(Thread::Current()); SirtRef class_loader(soa.Self(), soa.Decode(LoadDex("Statics"))); - mirror::Class* statics = class_linker_->FindClass("LStatics;", class_loader); + SirtRef statics(soa.Self(), class_linker_->FindClass("LStatics;", class_loader)); class_linker_->EnsureInitialized(statics, true, true); // Static final primitives that are initialized by a compile-time constant @@ -867,68 +868,68 @@ TEST_F(ClassLinkerTest, StaticFields) { FieldHelper fh(s0); EXPECT_STREQ(ClassHelper(s0->GetClass()).GetDescriptor(), "Ljava/lang/reflect/ArtField;"); EXPECT_TRUE(fh.GetTypeAsPrimitiveType() == Primitive::kPrimBoolean); - EXPECT_EQ(true, s0->GetBoolean(statics)); - s0->SetBoolean(statics, false); + EXPECT_EQ(true, s0->GetBoolean(statics.get())); + s0->SetBoolean(statics.get(), false); mirror::ArtField* s1 = statics->FindStaticField("s1", "B"); fh.ChangeField(s1); EXPECT_TRUE(fh.GetTypeAsPrimitiveType() == Primitive::kPrimByte); - EXPECT_EQ(5, s1->GetByte(statics)); - s1->SetByte(statics, 6); + EXPECT_EQ(5, s1->GetByte(statics.get())); + s1->SetByte(statics.get(), 6); mirror::ArtField* s2 = statics->FindStaticField("s2", "C"); fh.ChangeField(s2); EXPECT_TRUE(fh.GetTypeAsPrimitiveType() == Primitive::kPrimChar); - EXPECT_EQ('a', s2->GetChar(statics)); - s2->SetChar(statics, 'b'); + EXPECT_EQ('a', s2->GetChar(statics.get())); + s2->SetChar(statics.get(), 'b'); mirror::ArtField* s3 = statics->FindStaticField("s3", "S"); fh.ChangeField(s3); EXPECT_TRUE(fh.GetTypeAsPrimitiveType() == Primitive::kPrimShort); - EXPECT_EQ(-536, s3->GetShort(statics)); - s3->SetShort(statics, -535); + EXPECT_EQ(-536, s3->GetShort(statics.get())); + s3->SetShort(statics.get(), -535); mirror::ArtField* s4 = statics->FindStaticField("s4", "I"); fh.ChangeField(s4); EXPECT_TRUE(fh.GetTypeAsPrimitiveType() == Primitive::kPrimInt); - EXPECT_EQ(2000000000, s4->GetInt(statics)); - s4->SetInt(statics, 2000000001); + EXPECT_EQ(2000000000, s4->GetInt(statics.get())); + s4->SetInt(statics.get(), 2000000001); mirror::ArtField* s5 = statics->FindStaticField("s5", "J"); fh.ChangeField(s5); EXPECT_TRUE(fh.GetTypeAsPrimitiveType() == Primitive::kPrimLong); - EXPECT_EQ(0x1234567890abcdefLL, s5->GetLong(statics)); - s5->SetLong(statics, 0x34567890abcdef12LL); + EXPECT_EQ(0x1234567890abcdefLL, s5->GetLong(statics.get())); + s5->SetLong(statics.get(), 0x34567890abcdef12LL); mirror::ArtField* s6 = statics->FindStaticField("s6", "F"); fh.ChangeField(s6); EXPECT_TRUE(fh.GetTypeAsPrimitiveType() == Primitive::kPrimFloat); - EXPECT_EQ(0.5, s6->GetFloat(statics)); - s6->SetFloat(statics, 0.75); + EXPECT_EQ(0.5, s6->GetFloat(statics.get())); + s6->SetFloat(statics.get(), 0.75); mirror::ArtField* s7 = statics->FindStaticField("s7", "D"); fh.ChangeField(s7); EXPECT_TRUE(fh.GetTypeAsPrimitiveType() == Primitive::kPrimDouble); - EXPECT_EQ(16777217, s7->GetDouble(statics)); - s7->SetDouble(statics, 16777219); + EXPECT_EQ(16777217, s7->GetDouble(statics.get())); + s7->SetDouble(statics.get(), 16777219); mirror::ArtField* s8 = statics->FindStaticField("s8", "Ljava/lang/String;"); fh.ChangeField(s8); EXPECT_TRUE(fh.GetTypeAsPrimitiveType() == Primitive::kPrimNot); - EXPECT_TRUE(s8->GetObject(statics)->AsString()->Equals("android")); + EXPECT_TRUE(s8->GetObject(statics.get())->AsString()->Equals("android")); s8->SetObject(s8->GetDeclaringClass(), mirror::String::AllocFromModifiedUtf8(soa.Self(), "robot")); // TODO: Remove EXPECT_FALSE when GCC can handle EXPECT_EQ // http://code.google.com/p/googletest/issues/detail?id=322 - EXPECT_FALSE(s0->GetBoolean(statics)); - EXPECT_EQ(6, s1->GetByte(statics)); - EXPECT_EQ('b', s2->GetChar(statics)); - EXPECT_EQ(-535, s3->GetShort(statics)); - EXPECT_EQ(2000000001, s4->GetInt(statics)); - EXPECT_EQ(0x34567890abcdef12LL, s5->GetLong(statics)); - EXPECT_EQ(0.75, s6->GetFloat(statics)); - EXPECT_EQ(16777219, s7->GetDouble(statics)); - EXPECT_TRUE(s8->GetObject(statics)->AsString()->Equals("robot")); + EXPECT_FALSE(s0->GetBoolean(statics.get())); + EXPECT_EQ(6, s1->GetByte(statics.get())); + EXPECT_EQ('b', s2->GetChar(statics.get())); + EXPECT_EQ(-535, s3->GetShort(statics.get())); + EXPECT_EQ(2000000001, s4->GetInt(statics.get())); + EXPECT_EQ(0x34567890abcdef12LL, s5->GetLong(statics.get())); + EXPECT_EQ(0.75, s6->GetFloat(statics.get())); + EXPECT_EQ(16777219, s7->GetDouble(statics.get())); + EXPECT_TRUE(s8->GetObject(statics.get())->AsString()->Equals("robot")); } TEST_F(ClassLinkerTest, Interfaces) { diff --git a/runtime/debugger.cc b/runtime/debugger.cc index 52a2141a0ba..0b572b07b26 100644 --- a/runtime/debugger.cc +++ b/runtime/debugger.cc @@ -2816,30 +2816,30 @@ void Dbg::ExecuteMethod(DebugInvokeReq* pReq) { } // Translate the method through the vtable, unless the debugger wants to suppress it. - mirror::ArtMethod* m = pReq->method; + SirtRef m(soa.Self(), pReq->method); if ((pReq->options & JDWP::INVOKE_NONVIRTUAL) == 0 && pReq->receiver != NULL) { mirror::ArtMethod* actual_method = pReq->klass->FindVirtualMethodForVirtualOrInterface(pReq->method); - if (actual_method != m) { - VLOG(jdwp) << "ExecuteMethod translated " << PrettyMethod(m) << " to " << PrettyMethod(actual_method); - m = actual_method; + if (actual_method != m.get()) { + VLOG(jdwp) << "ExecuteMethod translated " << PrettyMethod(m.get()) << " to " << PrettyMethod(actual_method); + m.reset(actual_method); } } - VLOG(jdwp) << "ExecuteMethod " << PrettyMethod(m) + VLOG(jdwp) << "ExecuteMethod " << PrettyMethod(m.get()) << " receiver=" << pReq->receiver << " arg_count=" << pReq->arg_count; - CHECK(m != NULL); + CHECK(m.get() != nullptr); CHECK_EQ(sizeof(jvalue), sizeof(uint64_t)); - MethodHelper mh(m); + MethodHelper mh(m.get()); ArgArray arg_array(mh.GetShorty(), mh.GetShortyLength()); arg_array.BuildArgArray(soa, pReq->receiver, reinterpret_cast(pReq->arg_values)); - InvokeWithArgArray(soa, m, &arg_array, &pReq->result_value, mh.GetShorty()[0]); + InvokeWithArgArray(soa, m.get(), &arg_array, &pReq->result_value, mh.GetShorty()[0]); mirror::Throwable* exception = soa.Self()->GetException(NULL); soa.Self()->ClearException(); pReq->exception = gRegistry->Add(exception); - pReq->result_tag = BasicTagFromDescriptor(MethodHelper(m).GetShorty()); + pReq->result_tag = BasicTagFromDescriptor(MethodHelper(m.get()).GetShorty()); if (pReq->exception != 0) { VLOG(jdwp) << " JDWP invocation returning with exception=" << exception << " " << exception->Dump(); diff --git a/runtime/entrypoints/entrypoint_utils.h b/runtime/entrypoints/entrypoint_utils.h index 747dd56cd8f..bfdbd74e580 100644 --- a/runtime/entrypoints/entrypoint_utils.h +++ b/runtime/entrypoints/entrypoint_utils.h @@ -72,7 +72,7 @@ ALWAYS_INLINE static inline mirror::Class* CheckObjectAlloc(uint32_t type_idx, if (UNLIKELY(!klass->IsInitialized())) { SirtRef sirt_klass(self, klass); // The class initializer might cause a GC. - if (!Runtime::Current()->GetClassLinker()->EnsureInitialized(klass, true, true)) { + if (!Runtime::Current()->GetClassLinker()->EnsureInitialized(sirt_klass, true, true)) { DCHECK(self->IsExceptionPending()); return nullptr; // Failure } @@ -246,12 +246,15 @@ static inline mirror::ArtField* FindFieldFromCode(uint32_t field_idx, const mirr // If the class is initialized we're done. if (LIKELY(fields_class->IsInitialized())) { return resolved_field; - } else if (LIKELY(class_linker->EnsureInitialized(fields_class, true, true))) { - // Otherwise let's ensure the class is initialized before resolving the field. - return resolved_field; } else { - DCHECK(self->IsExceptionPending()); // Throw exception and unwind - return nullptr; // failure + SirtRef sirt_class(self, fields_class); + if (LIKELY(class_linker->EnsureInitialized(sirt_class, true, true))) { + // Otherwise let's ensure the class is initialized before resolving the field. + return resolved_field; + } else { + DCHECK(self->IsExceptionPending()); // Throw exception and unwind + return nullptr; // failure + } } } } @@ -535,12 +538,13 @@ static inline mirror::Class* ResolveVerifyAndClinit(uint32_t type_idx, if (klass == referring_class && referrer->IsConstructor() && referrer->IsStatic()) { return klass; } - if (!class_linker->EnsureInitialized(klass, true, true)) { + SirtRef sirt_class(self, klass); + if (!class_linker->EnsureInitialized(sirt_class, true, true)) { CHECK(self->IsExceptionPending()); return NULL; // Failure - Indicate to caller to deliver exception } - referrer->GetDexCacheInitializedStaticStorage()->Set(type_idx, klass); - return klass; + referrer->GetDexCacheInitializedStaticStorage()->Set(type_idx, sirt_class.get()); + return sirt_class.get(); } extern void ThrowStackOverflowError(Thread* self) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); diff --git a/runtime/entrypoints/interpreter/interpreter_entrypoints.cc b/runtime/entrypoints/interpreter/interpreter_entrypoints.cc index df4ec3a6cee..0df00c2f38f 100644 --- a/runtime/entrypoints/interpreter/interpreter_entrypoints.cc +++ b/runtime/entrypoints/interpreter/interpreter_entrypoints.cc @@ -34,14 +34,14 @@ extern "C" void artInterpreterToCompiledCodeBridge(Thread* self, MethodHelper& m mirror::Class* declaringClass = method->GetDeclaringClass(); if (UNLIKELY(!declaringClass->IsInitializing())) { self->PushShadowFrame(shadow_frame); - if (UNLIKELY(!Runtime::Current()->GetClassLinker()->EnsureInitialized(declaringClass, - true, true))) { + SirtRef sirt_c(self, declaringClass); + if (UNLIKELY(!Runtime::Current()->GetClassLinker()->EnsureInitialized(sirt_c, true, true))) { self->PopShadowFrame(); DCHECK(self->IsExceptionPending()); return; } self->PopShadowFrame(); - CHECK(declaringClass->IsInitializing()); + CHECK(sirt_c->IsInitializing()); } } uint16_t arg_offset = (code_item == NULL) ? 0 : code_item->registers_size_ - code_item->ins_size_; diff --git a/runtime/entrypoints/portable/portable_trampoline_entrypoints.cc b/runtime/entrypoints/portable/portable_trampoline_entrypoints.cc index 61f7440456a..2162dcc6117 100644 --- a/runtime/entrypoints/portable/portable_trampoline_entrypoints.cc +++ b/runtime/entrypoints/portable/portable_trampoline_entrypoints.cc @@ -208,8 +208,8 @@ extern "C" uint64_t artPortableToInterpreterBridge(mirror::ArtMethod* method, Th if (method->IsStatic() && !method->GetDeclaringClass()->IsInitializing()) { // Ensure static method's class is initialized. - if (!Runtime::Current()->GetClassLinker()->EnsureInitialized(method->GetDeclaringClass(), - true, true)) { + SirtRef sirt_c(self, method->GetDeclaringClass()); + if (!Runtime::Current()->GetClassLinker()->EnsureInitialized(sirt_c, true, true)) { DCHECK(Thread::Current()->IsExceptionPending()); self->PopManagedStackFragment(fragment); return 0; @@ -390,7 +390,7 @@ extern "C" const void* artPortableResolutionTrampoline(mirror::ArtMethod* called const void* code = NULL; if (LIKELY(!thread->IsExceptionPending())) { // Ensure that the called method's class is initialized. - mirror::Class* called_class = called->GetDeclaringClass(); + SirtRef called_class(thread, called->GetDeclaringClass()); linker->EnsureInitialized(called_class, true, true); if (LIKELY(called_class->IsInitialized())) { code = called->GetEntryPointFromCompiledCode(); diff --git a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc index 8ba08ee6046..b58938430e0 100644 --- a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc +++ b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc @@ -298,8 +298,8 @@ extern "C" uint64_t artQuickToInterpreterBridge(mirror::ArtMethod* method, Threa if (method->IsStatic() && !method->GetDeclaringClass()->IsInitializing()) { // Ensure static method's class is initialized. - if (!Runtime::Current()->GetClassLinker()->EnsureInitialized(method->GetDeclaringClass(), - true, true)) { + SirtRef sirt_c(self, method->GetDeclaringClass()); + if (!Runtime::Current()->GetClassLinker()->EnsureInitialized(sirt_c, true, true)) { DCHECK(Thread::Current()->IsExceptionPending()); self->PopManagedStackFragment(fragment); return 0; @@ -564,7 +564,7 @@ extern "C" const void* artQuickResolutionTrampoline(mirror::ArtMethod* called, } } // Ensure that the called method's class is initialized. - mirror::Class* called_class = called->GetDeclaringClass(); + SirtRef called_class(soa.Self(), called->GetDeclaringClass()); linker->EnsureInitialized(called_class, true, true); if (LIKELY(called_class->IsInitialized())) { code = called->GetEntryPointFromCompiledCode(); diff --git a/runtime/exception_test.cc b/runtime/exception_test.cc index 8f542d80acc..978faebbf19 100644 --- a/runtime/exception_test.cc +++ b/runtime/exception_test.cc @@ -37,11 +37,13 @@ class ExceptionTest : public CommonTest { CommonTest::SetUp(); ScopedObjectAccess soa(Thread::Current()); - SirtRef class_loader(soa.Self(), - soa.Decode(LoadDex("ExceptionHandle"))); + SirtRef class_loader( + soa.Self(), soa.Decode(LoadDex("ExceptionHandle"))); my_klass_ = class_linker_->FindClass("LExceptionHandle;", class_loader); ASSERT_TRUE(my_klass_ != NULL); - class_linker_->EnsureInitialized(my_klass_, true, true); + SirtRef sirt_klass(soa.Self(), my_klass_); + class_linker_->EnsureInitialized(sirt_klass, true, true); + my_klass_ = sirt_klass.get(); dex_ = my_klass_->GetDexCache()->GetDexFile(); diff --git a/runtime/gc/accounting/card_table.cc b/runtime/gc/accounting/card_table.cc index 7818bc8ba61..e099137f7d8 100644 --- a/runtime/gc/accounting/card_table.cc +++ b/runtime/gc/accounting/card_table.cc @@ -95,8 +95,8 @@ void CardTable::ClearSpaceCards(space::ContinuousSpace* space) { } void CardTable::ClearCardTable() { - // TODO: clear just the range of the table that has been modified - memset(mem_map_->Begin(), kCardClean, mem_map_->Size()); + COMPILE_ASSERT(kCardClean == 0, clean_card_must_be_0); + madvise(mem_map_->Begin(), mem_map_->Size(), MADV_DONTNEED); } bool CardTable::AddrIsInCardTable(const void* addr) const { diff --git a/runtime/gc/accounting/mod_union_table.cc b/runtime/gc/accounting/mod_union_table.cc index faa198a3700..b428e7486a6 100644 --- a/runtime/gc/accounting/mod_union_table.cc +++ b/runtime/gc/accounting/mod_union_table.cc @@ -82,7 +82,7 @@ class ModUnionUpdateObjectReferencesVisitor { if (ref != nullptr) { Object* new_ref = visitor_(ref, arg_); if (new_ref != ref) { - obj->SetFieldObject(offset, new_ref, true); + obj->SetFieldPtr(offset, new_ref, true); } } } diff --git a/runtime/gc/collector/mark_sweep-inl.h b/runtime/gc/collector/mark_sweep-inl.h index 7a515539929..9c1c5dc2814 100644 --- a/runtime/gc/collector/mark_sweep-inl.h +++ b/runtime/gc/collector/mark_sweep-inl.h @@ -69,15 +69,14 @@ inline void MarkSweep::VisitObjectReferences(mirror::Object* obj, const Visitor& DCHECK(obj->GetClass() != NULL); mirror::Class* klass = obj->GetClass(); DCHECK(klass != NULL); - if (visit_class) { - visitor(obj, klass, mirror::Object::ClassOffset(), false); - } if (klass == mirror::Class::GetJavaLangClass()) { DCHECK_EQ(klass->GetClass(), mirror::Class::GetJavaLangClass()); VisitClassReferences(klass, obj, visitor); } else { if (klass->IsArrayClass()) { - visitor(obj, klass, mirror::Object::ClassOffset(), false); + if (visit_class) { + visitor(obj, klass, mirror::Object::ClassOffset(), false); + } if (klass->IsObjectArrayClass()) { VisitObjectArrayReferences(obj->AsObjectArray(), visitor); } diff --git a/runtime/gc/collector/mark_sweep.h b/runtime/gc/collector/mark_sweep.h index 53d85b0d700..62991bb89d0 100644 --- a/runtime/gc/collector/mark_sweep.h +++ b/runtime/gc/collector/mark_sweep.h @@ -89,10 +89,12 @@ class MarkSweep : public GarbageCollector { SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); void MarkNonThreadRoots() - EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_); + EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - void MarkConcurrentRoots(); - EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_); + void MarkConcurrentRoots() + EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); void MarkRootsCheckpoint(Thread* self) EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_) diff --git a/runtime/gc/collector/semi_space.cc b/runtime/gc/collector/semi_space.cc index 393935474a2..63e0cfa635d 100644 --- a/runtime/gc/collector/semi_space.cc +++ b/runtime/gc/collector/semi_space.cc @@ -173,6 +173,10 @@ void SemiSpace::MarkingPhase() { BindBitmaps(); // Process dirty cards and add dirty cards to mod-union tables. heap_->ProcessCards(timings_); + // Clear the whole card table since we can not get any additional dirty cards during the + // paused GC. This saves memory but only works for pause the world collectors. + timings_.NewSplit("ClearCardTable"); + heap_->GetCardTable()->ClearCardTable(); // Need to do this before the checkpoint since we don't want any threads to add references to // the live stack during the recursive mark. timings_.NewSplit("SwapStacks"); @@ -318,8 +322,6 @@ Object* SemiSpace::MarkObject(Object* obj) { memcpy(reinterpret_cast(forward_address), obj, object_size); // Make sure to only update the forwarding address AFTER you copy the object so that the // monitor word doesn't get stomped over. - COMPILE_ASSERT(sizeof(uint32_t) == sizeof(mirror::Object*), - monitor_size_must_be_same_as_object); obj->SetLockWord(LockWord::FromForwardingAddress(reinterpret_cast(forward_address))); MarkStackPush(forward_address); } @@ -508,7 +510,10 @@ void SemiSpace::ScanObject(Object* obj) { mirror::Object* new_address = MarkObject(ref); if (new_address != ref) { DCHECK(new_address != nullptr); - obj->SetFieldObject(offset, new_address, false); + // Don't need to mark the card since we updating the object address and not changing the + // actual objects its pointing to. Using SetFieldPtr is better in this case since it does not + // dirty cards and use additional memory. + obj->SetFieldPtr(offset, new_address, false); } }, kMovingClasses); mirror::Class* klass = obj->GetClass(); diff --git a/runtime/gc/heap-inl.h b/runtime/gc/heap-inl.h index 08ab6b8c6a6..99f084ab44e 100644 --- a/runtime/gc/heap-inl.h +++ b/runtime/gc/heap-inl.h @@ -41,24 +41,20 @@ inline mirror::Object* Heap::AllocObjectWithAllocator(Thread* self, mirror::Clas // done in the runnable state where suspension is expected. DCHECK_EQ(self->GetState(), kRunnable); self->AssertThreadSuspensionIsAllowable(); + // Need to check that we arent the large object allocator since the large object allocation code + // path this function. If we didn't check we would have an infinite loop. + if (allocator != kAllocatorTypeLOS && UNLIKELY(ShouldAllocLargeObject(klass, byte_count))) { + return AllocLargeObject(self, klass, byte_count, + pre_fence_visitor); + } mirror::Object* obj; size_t bytes_allocated; AllocationTimer alloc_timer(this, &obj); - if (UNLIKELY(ShouldAllocLargeObject(klass, byte_count))) { - obj = TryToAllocate(self, kAllocatorTypeLOS, byte_count, false, - &bytes_allocated); - allocator = kAllocatorTypeLOS; - } else { - obj = TryToAllocate(self, allocator, byte_count, false, &bytes_allocated); - } - + obj = TryToAllocate(self, allocator, byte_count, &bytes_allocated); if (UNLIKELY(obj == nullptr)) { - SirtRef sirt_c(self, klass); - obj = AllocateInternalWithGc(self, allocator, byte_count, &bytes_allocated); + obj = AllocateInternalWithGc(self, allocator, byte_count, &bytes_allocated, &klass); if (obj == nullptr) { return nullptr; - } else { - klass = sirt_c.get(); } } obj->SetClass(klass); @@ -105,11 +101,19 @@ inline mirror::Object* Heap::AllocObjectWithAllocator(Thread* self, mirror::Clas return obj; } -template +template +inline mirror::Object* Heap::AllocLargeObject(Thread* self, mirror::Class* klass, + size_t byte_count, + const PreFenceVisitor& pre_fence_visitor) { + return AllocObjectWithAllocator(self, klass, byte_count, + kAllocatorTypeLOS, + pre_fence_visitor); +} + +template inline mirror::Object* Heap::TryToAllocate(Thread* self, AllocatorType allocator_type, - size_t alloc_size, bool grow, - size_t* bytes_allocated) { - if (UNLIKELY(IsOutOfMemoryOnAllocation(alloc_size, grow))) { + size_t alloc_size, size_t* bytes_allocated) { + if (UNLIKELY(IsOutOfMemoryOnAllocation(alloc_size))) { return nullptr; } if (kInstrumented) { @@ -190,14 +194,15 @@ inline bool Heap::ShouldAllocLargeObject(mirror::Class* c, size_t byte_count) co return byte_count >= kLargeObjectThreshold && have_zygote_space_ && c->IsPrimitiveArray(); } -inline bool Heap::IsOutOfMemoryOnAllocation(size_t alloc_size, bool grow) { +template +inline bool Heap::IsOutOfMemoryOnAllocation(size_t alloc_size) { size_t new_footprint = num_bytes_allocated_ + alloc_size; if (UNLIKELY(new_footprint > max_allowed_footprint_)) { if (UNLIKELY(new_footprint > growth_limit_)) { return true; } if (!concurrent_gc_) { - if (!grow) { + if (!kGrow) { return true; } // TODO: Grow for allocation is racy, fix it. diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc index f92a8212f0d..11acd33bb8e 100644 --- a/runtime/gc/heap.cc +++ b/runtime/gc/heap.cc @@ -881,14 +881,17 @@ void Heap::RecordFree(size_t freed_objects, size_t freed_bytes) { } mirror::Object* Heap::AllocateInternalWithGc(Thread* self, AllocatorType allocator, - size_t alloc_size, size_t* bytes_allocated) { + size_t alloc_size, size_t* bytes_allocated, + mirror::Class** klass) { mirror::Object* ptr = nullptr; + DCHECK(klass != nullptr); + SirtRef sirt_klass(self, *klass); // The allocation failed. If the GC is running, block until it completes, and then retry the // allocation. collector::GcType last_gc = WaitForGcToComplete(self); if (last_gc != collector::kGcTypeNone) { // A GC was in progress and we blocked, retry allocation now that memory has been freed. - ptr = TryToAllocate(self, allocator, alloc_size, false, bytes_allocated); + ptr = TryToAllocate(self, allocator, alloc_size, bytes_allocated); } // Loop through our different Gc types and try to Gc until we get enough free memory. @@ -899,13 +902,13 @@ mirror::Object* Heap::AllocateInternalWithGc(Thread* self, AllocatorType allocat // Attempt to run the collector, if we succeed, re-try the allocation. if (CollectGarbageInternal(gc_type, kGcCauseForAlloc, false) != collector::kGcTypeNone) { // Did we free sufficient memory for the allocation to succeed? - ptr = TryToAllocate(self, allocator, alloc_size, false, bytes_allocated); + ptr = TryToAllocate(self, allocator, alloc_size, bytes_allocated); } } // Allocations have failed after GCs; this is an exceptional state. if (ptr == nullptr) { // Try harder, growing the heap if necessary. - ptr = TryToAllocate(self, allocator, alloc_size, true, bytes_allocated); + ptr = TryToAllocate(self, allocator, alloc_size, bytes_allocated); } if (ptr == nullptr) { // Most allocations should have succeeded by now, so the heap is really full, really fragmented, @@ -918,11 +921,12 @@ mirror::Object* Heap::AllocateInternalWithGc(Thread* self, AllocatorType allocat // We don't need a WaitForGcToComplete here either. DCHECK(!gc_plan_.empty()); CollectGarbageInternal(gc_plan_.back(), kGcCauseForAlloc, true); - ptr = TryToAllocate(self, allocator, alloc_size, true, bytes_allocated); + ptr = TryToAllocate(self, allocator, alloc_size, bytes_allocated); if (ptr == nullptr) { ThrowOutOfMemoryError(self, alloc_size, false); } } + *klass = sirt_klass.get(); return ptr; } diff --git a/runtime/gc/heap.h b/runtime/gc/heap.h index 3bff3f97045..9788064a2c0 100644 --- a/runtime/gc/heap.h +++ b/runtime/gc/heap.h @@ -126,11 +126,6 @@ enum ProcessState { kProcessStateJankImperceptible = 1, }; -// If true, measure the total allocation time. -static constexpr bool kMeasureAllocationTime = false; -// Primitive arrays larger than this size are put in the large object space. -static constexpr size_t kLargeObjectThreshold = 3 * kPageSize; - class Heap { public: // If true, measure the total allocation time. @@ -522,10 +517,16 @@ class Heap { ALWAYS_INLINE void CheckConcurrentGC(Thread* self, size_t new_num_bytes_allocated, mirror::Object* obj); + // We don't force this to be inline since it is a slow path. + template + mirror::Object* AllocLargeObject(Thread* self, mirror::Class* klass, size_t byte_count, + const PreFenceVisitor& pre_fence_visitor) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + // Handles Allocate()'s slow allocation path with GC involved after // an initial allocation attempt failed. mirror::Object* AllocateInternalWithGc(Thread* self, AllocatorType allocator, size_t num_bytes, - size_t* bytes_allocated) + size_t* bytes_allocated, mirror::Class** klass) LOCKS_EXCLUDED(Locks::thread_suspend_count_lock_) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); @@ -536,15 +537,15 @@ class Heap { // Try to allocate a number of bytes, this function never does any GCs. Needs to be inlined so // that the switch statement is constant optimized in the entrypoints. - template + template ALWAYS_INLINE mirror::Object* TryToAllocate(Thread* self, AllocatorType allocator_type, - size_t alloc_size, bool grow, - size_t* bytes_allocated) + size_t alloc_size, size_t* bytes_allocated) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); void ThrowOutOfMemoryError(Thread* self, size_t byte_count, bool large_object_allocation) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - bool IsOutOfMemoryOnAllocation(size_t alloc_size, bool grow); + template + bool IsOutOfMemoryOnAllocation(size_t alloc_size); // Pushes a list of cleared references out to the managed heap. void SetReferenceReferent(mirror::Object* reference, mirror::Object* referent) diff --git a/runtime/globals.h b/runtime/globals.h index c2fe67e083f..a0d7e488231 100644 --- a/runtime/globals.h +++ b/runtime/globals.h @@ -82,7 +82,7 @@ static constexpr bool kUsePortableCompiler = false; // Garbage collector constants. static constexpr bool kMovingCollector = true && !kUsePortableCompiler; // True if we allow moving classes. -static constexpr bool kMovingClasses = false; +static constexpr bool kMovingClasses = true; // True if we allow moving fields. static constexpr bool kMovingFields = false; // True if we allow moving methods. diff --git a/runtime/interpreter/interpreter.cc b/runtime/interpreter/interpreter.cc index 9938478b2c1..02c90123fce 100644 --- a/runtime/interpreter/interpreter.cc +++ b/runtime/interpreter/interpreter.cc @@ -343,12 +343,13 @@ void EnterInterpreterFromInvoke(Thread* self, ArtMethod* method, Object* receive ++cur_reg; } else if (UNLIKELY(!method->GetDeclaringClass()->IsInitializing())) { ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); - if (UNLIKELY(!class_linker->EnsureInitialized(method->GetDeclaringClass(), true, true))) { + SirtRef sirt_c(self, method->GetDeclaringClass()); + if (UNLIKELY(!class_linker->EnsureInitialized(sirt_c, true, true))) { CHECK(self->IsExceptionPending()); self->PopShadowFrame(); return; } - CHECK(method->GetDeclaringClass()->IsInitializing()); + CHECK(sirt_c->IsInitializing()); } const char* shorty = mh.GetShorty(); for (size_t shorty_pos = 0, arg_pos = 0; cur_reg < num_regs; ++shorty_pos, ++arg_pos, cur_reg++) { @@ -428,7 +429,7 @@ extern "C" void artInterpreterToInterpreterBridge(Thread* self, MethodHelper& mh ArtMethod* method = shadow_frame->GetMethod(); // Ensure static methods are initialized. if (method->IsStatic()) { - Class* declaringClass = method->GetDeclaringClass(); + SirtRef declaringClass(self, method->GetDeclaringClass()); if (UNLIKELY(!declaringClass->IsInitializing())) { if (UNLIKELY(!Runtime::Current()->GetClassLinker()->EnsureInitialized(declaringClass, true, true))) { diff --git a/runtime/interpreter/interpreter_common.h b/runtime/interpreter/interpreter_common.h index 0bc834ccb1a..3b8d50bc251 100644 --- a/runtime/interpreter/interpreter_common.h +++ b/runtime/interpreter/interpreter_common.h @@ -334,13 +334,14 @@ static inline bool DoIPutQuick(const ShadowFrame& shadow_frame, const Instructio // java.lang.String class is initialized. static inline String* ResolveString(Thread* self, MethodHelper& mh, uint32_t string_idx) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + CHECK(!kMovingMethods); Class* java_lang_string_class = String::GetJavaLangString(); if (UNLIKELY(!java_lang_string_class->IsInitialized())) { ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); - if (UNLIKELY(!class_linker->EnsureInitialized(java_lang_string_class, - true, true))) { + SirtRef sirt_class(self, java_lang_string_class); + if (UNLIKELY(!class_linker->EnsureInitialized(sirt_class, true, true))) { DCHECK(self->IsExceptionPending()); - return NULL; + return nullptr; } } return mh.ResolveString(string_idx); diff --git a/runtime/jni_internal.cc b/runtime/jni_internal.cc index 466edebf591..330027962b3 100644 --- a/runtime/jni_internal.cc +++ b/runtime/jni_internal.cc @@ -225,13 +225,24 @@ static void ThrowNoSuchMethodError(ScopedObjectAccess& soa, Class* c, kind, ClassHelper(c).GetDescriptor(), name, sig); } +static mirror::Class* EnsureInitialized(Thread* self, mirror::Class* klass) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + if (LIKELY(klass->IsInitialized())) { + return klass; + } + SirtRef sirt_klass(self, klass); + if (!Runtime::Current()->GetClassLinker()->EnsureInitialized(sirt_klass, true, true)) { + return nullptr; + } + return sirt_klass.get(); +} + static jmethodID FindMethodID(ScopedObjectAccess& soa, jclass jni_class, const char* name, const char* sig, bool is_static) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - Class* c = soa.Decode(jni_class); - DCHECK(c != nullptr); - if (!Runtime::Current()->GetClassLinker()->EnsureInitialized(c, true, true)) { - return NULL; + Class* c = EnsureInitialized(soa.Self(), soa.Decode(jni_class)); + if (c == nullptr) { + return nullptr; } ArtMethod* method = NULL; @@ -284,9 +295,9 @@ static ClassLoader* GetClassLoader(const ScopedObjectAccess& soa) static jfieldID FindFieldID(const ScopedObjectAccess& soa, jclass jni_class, const char* name, const char* sig, bool is_static) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - Class* c = soa.Decode(jni_class); - if (!Runtime::Current()->GetClassLinker()->EnsureInitialized(c, true, true)) { - return NULL; + Class* c = EnsureInitialized(soa.Self(), soa.Decode(jni_class)); + if (c == nullptr) { + return nullptr; } ArtField* field = NULL; @@ -910,9 +921,9 @@ class JNI { static jobject AllocObject(JNIEnv* env, jclass java_class) { CHECK_NON_NULL_ARGUMENT(AllocObject, java_class); ScopedObjectAccess soa(env); - Class* c = soa.Decode(java_class); - if (!Runtime::Current()->GetClassLinker()->EnsureInitialized(c, true, true)) { - return NULL; + Class* c = EnsureInitialized(soa.Self(), soa.Decode(java_class)); + if (c == nullptr) { + return nullptr; } return soa.AddLocalReference(c->AllocObject(soa.Self())); } @@ -931,20 +942,20 @@ class JNI { CHECK_NON_NULL_ARGUMENT(NewObjectV, java_class); CHECK_NON_NULL_ARGUMENT(NewObjectV, mid); ScopedObjectAccess soa(env); - Class* c = soa.Decode(java_class); - if (!Runtime::Current()->GetClassLinker()->EnsureInitialized(c, true, true)) { - return NULL; + Class* c = EnsureInitialized(soa.Self(), soa.Decode(java_class)); + if (c == nullptr) { + return nullptr; } Object* result = c->AllocObject(soa.Self()); - if (result == NULL) { - return NULL; + if (result == nullptr) { + return nullptr; } jobject local_result = soa.AddLocalReference(result); CallNonvirtualVoidMethodV(env, local_result, java_class, mid, args); if (!soa.Self()->IsExceptionPending()) { return local_result; } else { - return NULL; + return nullptr; } } @@ -952,9 +963,9 @@ class JNI { CHECK_NON_NULL_ARGUMENT(NewObjectA, java_class); CHECK_NON_NULL_ARGUMENT(NewObjectA, mid); ScopedObjectAccess soa(env); - Class* c = soa.Decode(java_class); - if (!Runtime::Current()->GetClassLinker()->EnsureInitialized(c, true, true)) { - return NULL; + Class* c = EnsureInitialized(soa.Self(), soa.Decode(java_class)); + if (c == nullptr) { + return nullptr; } Object* result = c->AllocObject(soa.Self()); if (result == NULL) { @@ -3303,8 +3314,9 @@ void* JavaVMExt::FindCodeForNativeMethod(ArtMethod* m) { // If this is a static method, it could be called before the class // has been initialized. if (m->IsStatic()) { - if (!Runtime::Current()->GetClassLinker()->EnsureInitialized(c, true, true)) { - return NULL; + c = EnsureInitialized(Thread::Current(), c); + if (c == nullptr) { + return nullptr; } } else { CHECK(c->IsInitializing()) << c->GetStatus() << " " << PrettyMethod(m); diff --git a/runtime/mirror/array-inl.h b/runtime/mirror/array-inl.h index a754b6942d3..cf4b48cb02b 100644 --- a/runtime/mirror/array-inl.h +++ b/runtime/mirror/array-inl.h @@ -108,6 +108,13 @@ inline Array* Array::Alloc(Thread* self, Class* array_class, int32_t component_c Runtime::Current()->GetHeap()->GetCurrentAllocator()); } +template +inline void PrimitiveArray::VisitRoots(RootVisitor* visitor, void* arg) { + if (array_class_ != nullptr) { + array_class_ = down_cast(visitor(array_class_, arg)); + } +} + } // namespace mirror } // namespace art diff --git a/runtime/mirror/array.h b/runtime/mirror/array.h index a332f97c027..52659464fc2 100644 --- a/runtime/mirror/array.h +++ b/runtime/mirror/array.h @@ -149,6 +149,9 @@ class MANAGED PrimitiveArray : public Array { array_class_ = NULL; } + static void VisitRoots(RootVisitor* visitor, void* arg) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + private: static Class* array_class_; diff --git a/runtime/mirror/art_field.cc b/runtime/mirror/art_field.cc index a8bbe4bc6a7..c3a4efb15ae 100644 --- a/runtime/mirror/art_field.cc +++ b/runtime/mirror/art_field.cc @@ -52,5 +52,12 @@ void ArtField::SetOffset(MemberOffset num_bytes) { SetField32(OFFSET_OF_OBJECT_MEMBER(ArtField, offset_), num_bytes.Uint32Value(), false); } +void ArtField::VisitRoots(RootVisitor* visitor, void* arg) { + if (java_lang_reflect_ArtField_ != nullptr) { + java_lang_reflect_ArtField_ = down_cast( + visitor(java_lang_reflect_ArtField_, arg)); + } +} + } // namespace mirror } // namespace art diff --git a/runtime/mirror/art_field.h b/runtime/mirror/art_field.h index ae34cb1f847..62bcf062e13 100644 --- a/runtime/mirror/art_field.h +++ b/runtime/mirror/art_field.h @@ -130,6 +130,8 @@ class MANAGED ArtField : public Object { static void SetClass(Class* java_lang_reflect_ArtField); static void ResetClass(); + static void VisitRoots(RootVisitor* visitor, void* arg) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); bool IsVolatile() const { return (GetAccessFlags() & kAccVolatile) != 0; diff --git a/runtime/mirror/art_method.cc b/runtime/mirror/art_method.cc index f5c0e9f2166..a4f6b3b460e 100644 --- a/runtime/mirror/art_method.cc +++ b/runtime/mirror/art_method.cc @@ -40,6 +40,13 @@ extern "C" void art_quick_invoke_stub(ArtMethod*, uint32_t*, uint32_t, Thread*, // TODO: get global references for these Class* ArtMethod::java_lang_reflect_ArtMethod_ = NULL; +void ArtMethod::VisitRoots(RootVisitor* visitor, void* arg) { + if (java_lang_reflect_ArtMethod_ != nullptr) { + java_lang_reflect_ArtMethod_ = down_cast( + visitor(java_lang_reflect_ArtMethod_, arg)); + } +} + InvokeType ArtMethod::GetInvokeType() const { // TODO: kSuper? if (GetDeclaringClass()->IsInterface()) { diff --git a/runtime/mirror/art_method.h b/runtime/mirror/art_method.h index f396fbe4362..d5524ec87b8 100644 --- a/runtime/mirror/art_method.h +++ b/runtime/mirror/art_method.h @@ -23,6 +23,7 @@ #include "locks.h" #include "modifiers.h" #include "object.h" +#include "root_visitor.h" namespace art { @@ -381,6 +382,9 @@ class MANAGED ArtMethod : public Object { static void ResetClass(); + static void VisitRoots(RootVisitor* visitor, void* arg) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + protected: // Field order required by test "ValidateFieldOrderOfJavaCppUnionClasses". // The class we are a part of diff --git a/runtime/mirror/class.cc b/runtime/mirror/class.cc index cdc5ab2b399..2746e1e91af 100644 --- a/runtime/mirror/class.cc +++ b/runtime/mirror/class.cc @@ -50,6 +50,12 @@ void Class::ResetClass() { java_lang_Class_ = NULL; } +void Class::VisitRoots(RootVisitor* visitor, void* arg) { + if (java_lang_Class_ != nullptr) { + java_lang_Class_ = down_cast(visitor(java_lang_Class_, arg)); + } +} + void Class::SetStatus(Status new_status, Thread* self) { Status old_status = GetStatus(); ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); diff --git a/runtime/mirror/class.h b/runtime/mirror/class.h index 5f64bb4f8b5..50ede668dd8 100644 --- a/runtime/mirror/class.h +++ b/runtime/mirror/class.h @@ -787,6 +787,8 @@ class MANAGED Class : public StaticStorageBase { // Can't call this SetClass or else gets called instead of Object::SetClass in places. static void SetClassClass(Class* java_lang_Class); static void ResetClass(); + static void VisitRoots(RootVisitor* visitor, void* arg) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); // When class is verified, set the kAccPreverified flag on each method. void SetPreverifiedFlagOnAllMethods() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); diff --git a/runtime/mirror/object.h b/runtime/mirror/object.h index 0fb203917af..fe89b7ee9e2 100644 --- a/runtime/mirror/object.h +++ b/runtime/mirror/object.h @@ -225,6 +225,11 @@ class MANAGED Object { void SetField64(MemberOffset field_offset, uint64_t new_value, bool is_volatile); + template + void SetFieldPtr(MemberOffset field_offset, T new_value, bool is_volatile, bool this_is_valid = true) { + SetField32(field_offset, reinterpret_cast(new_value), is_volatile, this_is_valid); + } + protected: // Accessors for non-Java type fields template @@ -232,11 +237,6 @@ class MANAGED Object { return reinterpret_cast(GetField32(field_offset, is_volatile)); } - template - void SetFieldPtr(MemberOffset field_offset, T new_value, bool is_volatile, bool this_is_valid = true) { - SetField32(field_offset, reinterpret_cast(new_value), is_volatile, this_is_valid); - } - private: static void VerifyObject(const Object* obj) ALWAYS_INLINE; // Verify the type correctness of stores to fields. diff --git a/runtime/mirror/stack_trace_element.cc b/runtime/mirror/stack_trace_element.cc index 32a50fe470b..a7ebe07d891 100644 --- a/runtime/mirror/stack_trace_element.cc +++ b/runtime/mirror/stack_trace_element.cc @@ -58,5 +58,12 @@ StackTraceElement* StackTraceElement::Alloc(Thread* self, return trace; } +void StackTraceElement::VisitRoots(RootVisitor* visitor, void* arg) { + if (java_lang_StackTraceElement_ != nullptr) { + java_lang_StackTraceElement_ = down_cast(visitor(java_lang_StackTraceElement_, arg)); + } +} + + } // namespace mirror } // namespace art diff --git a/runtime/mirror/stack_trace_element.h b/runtime/mirror/stack_trace_element.h index 2af512823e2..d1be4dcfded 100644 --- a/runtime/mirror/stack_trace_element.h +++ b/runtime/mirror/stack_trace_element.h @@ -57,8 +57,9 @@ class MANAGED StackTraceElement : public Object { SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); static void SetClass(Class* java_lang_StackTraceElement); - static void ResetClass(); + static void VisitRoots(RootVisitor* visitor, void* arg) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); private: // Field order required by test "ValidateFieldOrderOfJavaCppUnionClasses". diff --git a/runtime/mirror/string.cc b/runtime/mirror/string.cc index b372fe7f340..d6e509d8416 100644 --- a/runtime/mirror/string.cc +++ b/runtime/mirror/string.cc @@ -122,7 +122,7 @@ String* String::AllocFromUtf16(Thread* self, const uint16_t* utf16_data_in, int32_t hash_code) { CHECK(utf16_data_in != NULL || utf16_length == 0); - String* string = Alloc(self, GetJavaLangString(), utf16_length); + String* string = Alloc(self, utf16_length); if (UNLIKELY(string == nullptr)) { return nullptr; } @@ -152,7 +152,7 @@ String* String::AllocFromModifiedUtf8(Thread* self, const char* utf) { String* String::AllocFromModifiedUtf8(Thread* self, int32_t utf16_length, const char* utf8_data_in) { - String* string = Alloc(self, GetJavaLangString(), utf16_length); + String* string = Alloc(self, utf16_length); if (UNLIKELY(string == nullptr)) { return nullptr; } @@ -163,21 +163,20 @@ String* String::AllocFromModifiedUtf8(Thread* self, int32_t utf16_length, return string; } -String* String::Alloc(Thread* self, Class* java_lang_String, int32_t utf16_length) { - CharArray* array = CharArray::Alloc(self, utf16_length); - if (UNLIKELY(array == nullptr)) { +String* String::Alloc(Thread* self, int32_t utf16_length) { + SirtRef array(self, CharArray::Alloc(self, utf16_length)); + if (UNLIKELY(array.get() == nullptr)) { return nullptr; } - return Alloc(self, java_lang_String, array); + return Alloc(self, array); } -String* String::Alloc(Thread* self, Class* java_lang_String, CharArray* array) { +String* String::Alloc(Thread* self, const SirtRef& array) { // Hold reference in case AllocObject causes GC. - SirtRef array_ref(self, array); - String* string = down_cast(java_lang_String->AllocObject(self)); + String* string = down_cast(GetJavaLangString()->AllocObject(self)); if (LIKELY(string != nullptr)) { - string->SetArray(array_ref.get()); - string->SetCount(array_ref->GetLength()); + string->SetArray(array.get()); + string->SetCount(array->GetLength()); } return string; } @@ -287,5 +286,11 @@ int32_t String::CompareTo(String* rhs) const { return countDiff; } +void String::VisitRoots(RootVisitor* visitor, void* arg) { + if (java_lang_String_ != nullptr) { + java_lang_String_ = down_cast(visitor(java_lang_String_, arg)); + } +} + } // namespace mirror } // namespace art diff --git a/runtime/mirror/string.h b/runtime/mirror/string.h index 7520c4d33c5..4bbcb9c606d 100644 --- a/runtime/mirror/string.h +++ b/runtime/mirror/string.h @@ -19,6 +19,7 @@ #include "class.h" #include "gtest/gtest.h" +#include "root_visitor.h" namespace art { @@ -77,12 +78,6 @@ class MANAGED String : public Object { const char* utf8_data_in) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - static String* Alloc(Thread* self, Class* java_lang_String, int32_t utf16_length) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - - static String* Alloc(Thread* self, Class* java_lang_String, CharArray* array) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - bool Equals(const char* modified_utf8) const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); @@ -114,6 +109,8 @@ class MANAGED String : public Object { static void SetClass(Class* java_lang_String); static void ResetClass(); + static void VisitRoots(RootVisitor* visitor, void* arg) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); private: void SetHashCode(int32_t new_hash_code) { @@ -132,6 +129,12 @@ class MANAGED String : public Object { SetField32(OFFSET_OF_OBJECT_MEMBER(String, offset_), new_offset, false); } + static String* Alloc(Thread* self, int32_t utf16_length) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + static String* Alloc(Thread* self, const SirtRef& array) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + void SetArray(CharArray* new_array) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); // Field order required by test "ValidateFieldOrderOfJavaCppUnionClasses". diff --git a/runtime/mirror/throwable.cc b/runtime/mirror/throwable.cc index 961f6deed39..b55db729b84 100644 --- a/runtime/mirror/throwable.cc +++ b/runtime/mirror/throwable.cc @@ -93,5 +93,11 @@ void Throwable::ResetClass() { java_lang_Throwable_ = NULL; } +void Throwable::VisitRoots(RootVisitor* visitor, void* arg) { + if (java_lang_Throwable_ != nullptr) { + java_lang_Throwable_ = down_cast(visitor(java_lang_Throwable_, arg)); + } +} + } // namespace mirror } // namespace art diff --git a/runtime/mirror/throwable.h b/runtime/mirror/throwable.h index 27f6e1289a1..5a905990d82 100644 --- a/runtime/mirror/throwable.h +++ b/runtime/mirror/throwable.h @@ -18,6 +18,7 @@ #define ART_RUNTIME_MIRROR_THROWABLE_H_ #include "object.h" +#include "root_visitor.h" #include "string.h" namespace art { @@ -50,6 +51,8 @@ class MANAGED Throwable : public Object { static void SetClass(Class* java_lang_Throwable); static void ResetClass(); + static void VisitRoots(RootVisitor* visitor, void* arg) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); private: Object* GetStackState() const { diff --git a/runtime/native/java_lang_Class.cc b/runtime/native/java_lang_Class.cc index 33891078f88..3e3f608c890 100644 --- a/runtime/native/java_lang_Class.cc +++ b/runtime/native/java_lang_Class.cc @@ -46,8 +46,8 @@ static mirror::Class* DecodeClass(const ScopedFastNativeObjectAccess& soa, jobje static jclass Class_classForName(JNIEnv* env, jclass, jstring javaName, jboolean initialize, jobject javaLoader) { ScopedObjectAccess soa(env); ScopedUtfChars name(env, javaName); - if (name.c_str() == NULL) { - return NULL; + if (name.c_str() == nullptr) { + return nullptr; } // We need to validate and convert the name (from x.y.z to x/y/z). This @@ -57,27 +57,27 @@ static jclass Class_classForName(JNIEnv* env, jclass, jstring javaName, jboolean ThrowLocation throw_location = soa.Self()->GetCurrentLocationForThrow(); soa.Self()->ThrowNewExceptionF(throw_location, "Ljava/lang/ClassNotFoundException;", "Invalid name: %s", name.c_str()); - return NULL; + return nullptr; } std::string descriptor(DotToDescriptor(name.c_str())); SirtRef class_loader(soa.Self(), soa.Decode(javaLoader)); ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); - mirror::Class* c = class_linker->FindClass(descriptor.c_str(), class_loader); - if (c == NULL) { + SirtRef c(soa.Self(), class_linker->FindClass(descriptor.c_str(), class_loader)); + if (c.get() == nullptr) { ScopedLocalRef cause(env, env->ExceptionOccurred()); env->ExceptionClear(); jthrowable cnfe = reinterpret_cast(env->NewObject(WellKnownClasses::java_lang_ClassNotFoundException, WellKnownClasses::java_lang_ClassNotFoundException_init, javaName, cause.get())); env->Throw(cnfe); - return NULL; + return nullptr; } if (initialize) { class_linker->EnsureInitialized(c, true, true); } - return soa.AddLocalReference(c); + return soa.AddLocalReference(c.get()); } static jstring Class_getNameNative(JNIEnv* env, jobject javaThis) { diff --git a/runtime/native/java_lang_reflect_Constructor.cc b/runtime/native/java_lang_reflect_Constructor.cc index 04dfcb565a9..581199234c2 100644 --- a/runtime/native/java_lang_reflect_Constructor.cc +++ b/runtime/native/java_lang_reflect_Constructor.cc @@ -41,24 +41,24 @@ static jobject Constructor_newInstance(JNIEnv* env, jobject javaMethod, jobjectA javaMethod, WellKnownClasses::java_lang_reflect_AbstractMethod_artMethod); mirror::ArtMethod* m = soa.Decode(art_method)->AsArtMethod(); - mirror::Class* c = m->GetDeclaringClass(); + SirtRef c(soa.Self(), m->GetDeclaringClass()); if (UNLIKELY(c->IsAbstract())) { ThrowLocation throw_location = soa.Self()->GetCurrentLocationForThrow(); soa.Self()->ThrowNewExceptionF(throw_location, "Ljava/lang/InstantiationException;", "Can't instantiate %s %s", c->IsInterface() ? "interface" : "abstract class", - PrettyDescriptor(c).c_str()); - return NULL; + PrettyDescriptor(c.get()).c_str()); + return nullptr; } if (!Runtime::Current()->GetClassLinker()->EnsureInitialized(c, true, true)) { DCHECK(soa.Self()->IsExceptionPending()); - return NULL; + return nullptr; } mirror::Object* receiver = c->AllocNonMovableObject(soa.Self()); - if (receiver == NULL) { - return NULL; + if (receiver == nullptr) { + return nullptr; } jobject javaReceiver = soa.AddLocalReference(receiver); diff --git a/runtime/native/java_lang_reflect_Field.cc b/runtime/native/java_lang_reflect_Field.cc index 4d69a688ac8..553aeb8f12a 100644 --- a/runtime/native/java_lang_reflect_Field.cc +++ b/runtime/native/java_lang_reflect_Field.cc @@ -31,10 +31,13 @@ static bool GetFieldValue(const ScopedFastNativeObjectAccess& soa, mirror::Objec mirror::ArtField* f, JValue& value, bool allow_references) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { DCHECK_EQ(value.GetJ(), 0LL); - if (!Runtime::Current()->GetClassLinker()->EnsureInitialized(f->GetDeclaringClass(), - true, true)) { + CHECK(!kMovingFields); + SirtRef sirt_obj(soa.Self(), o); + SirtRef sirt_klass(soa.Self(), f->GetDeclaringClass()); + if (!Runtime::Current()->GetClassLinker()->EnsureInitialized(sirt_klass, true, true)) { return false; } + o = sirt_obj.get(); switch (FieldHelper(f).GetTypeAsPrimitiveType()) { case Primitive::kPrimBoolean: value.SetZ(f->GetBoolean(o)); @@ -168,13 +171,16 @@ static jshort Field_getShort(JNIEnv* env, jobject javaField, jobject javaObj) { return GetPrimitiveField(env, javaField, javaObj, 'S').GetS(); } -static void SetFieldValue(mirror::Object* o, mirror::ArtField* f, const JValue& new_value, - bool allow_references) +static void SetFieldValue(ScopedFastNativeObjectAccess& soa, mirror::Object* o, + mirror::ArtField* f, const JValue& new_value, bool allow_references) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - if (!Runtime::Current()->GetClassLinker()->EnsureInitialized(f->GetDeclaringClass(), - true, true)) { + CHECK(!kMovingFields); + SirtRef sirt_obj(soa.Self(), o); + SirtRef sirt_klass(soa.Self(), f->GetDeclaringClass()); + if (!Runtime::Current()->GetClassLinker()->EnsureInitialized(sirt_klass, true, true)) { return; } + o = sirt_obj.get(); switch (FieldHelper(f).GetTypeAsPrimitiveType()) { case Primitive::kPrimBoolean: f->SetBoolean(o, new_value.GetZ()); @@ -237,7 +243,7 @@ static void Field_set(JNIEnv* env, jobject javaField, jobject javaObj, jobject j return; } - SetFieldValue(o, f, unboxed_value, true); + SetFieldValue(soa, o, f, unboxed_value, true); } static void SetPrimitiveField(JNIEnv* env, jobject javaField, jobject javaObj, char src_descriptor, @@ -264,7 +270,7 @@ static void SetPrimitiveField(JNIEnv* env, jobject javaField, jobject javaObj, c } // Write the value. - SetFieldValue(o, f, wide_value, false); + SetFieldValue(soa, o, f, wide_value, false); } static void Field_setBoolean(JNIEnv* env, jobject javaField, jobject javaObj, jboolean z) { diff --git a/runtime/object_utils.h b/runtime/object_utils.h index e37510c228c..cc996bc89d6 100644 --- a/runtime/object_utils.h +++ b/runtime/object_utils.h @@ -34,34 +34,36 @@ namespace art { +template class ObjectLock { public: - explicit ObjectLock(Thread* self, mirror::Object* object) + explicit ObjectLock(Thread* self, const SirtRef* object) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) : self_(self), obj_(object) { - CHECK(object != NULL); - obj_->MonitorEnter(self_); + CHECK(object != nullptr); + CHECK(object->get() != nullptr); + obj_->get()->MonitorEnter(self_); } ~ObjectLock() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - obj_->MonitorExit(self_); + obj_->get()->MonitorExit(self_); } void WaitIgnoringInterrupts() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - Monitor::Wait(self_, obj_, 0, 0, false, kWaiting); + Monitor::Wait(self_, obj_->get(), 0, 0, false, kWaiting); } void Notify() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - obj_->Notify(self_); + obj_->get()->Notify(self_); } void NotifyAll() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - obj_->NotifyAll(self_); + obj_->get()->NotifyAll(self_); } private: Thread* const self_; - mirror::Object* obj_; + const SirtRef* obj_; DISALLOW_COPY_AND_ASSIGN(ObjectLock); }; diff --git a/runtime/reflection.cc b/runtime/reflection.cc index 80e16aa6c06..ac8f5ef6c4f 100644 --- a/runtime/reflection.cc +++ b/runtime/reflection.cc @@ -39,8 +39,12 @@ jobject InvokeMethod(const ScopedObjectAccess& soa, jobject javaMethod, jobject mirror::ArtMethod* m = soa.DecodeMethod(mid); mirror::Class* declaring_class = m->GetDeclaringClass(); - if (!Runtime::Current()->GetClassLinker()->EnsureInitialized(declaring_class, true, true)) { - return NULL; + if (UNLIKELY(!declaring_class->IsInitialized())) { + SirtRef sirt_c(soa.Self(), declaring_class); + if (!Runtime::Current()->GetClassLinker()->EnsureInitialized(sirt_c, true, true)) { + return nullptr; + } + declaring_class = sirt_c.get(); } mirror::Object* receiver = NULL; diff --git a/runtime/runtime.cc b/runtime/runtime.cc index 4048bd32fd2..e1b4d7ec359 100644 --- a/runtime/runtime.cc +++ b/runtime/runtime.cc @@ -47,6 +47,7 @@ #include "mirror/array.h" #include "mirror/class-inl.h" #include "mirror/class_loader.h" +#include "mirror/stack_trace_element.h" #include "mirror/throwable.h" #include "monitor.h" #include "oat_file.h" @@ -86,6 +87,7 @@ Runtime::Runtime() resolution_method_(NULL), imt_conflict_method_(NULL), default_imt_(NULL), + method_verifiers_lock_("Method verifiers lock"), threads_being_born_(0), shutdown_cond_(new ConditionVariable("Runtime shutdown", *Locks::runtime_shutdown_lock_)), shutting_down_(false), @@ -699,35 +701,38 @@ jobject CreateSystemClassLoader() { } ScopedObjectAccess soa(Thread::Current()); + ClassLinker* cl = Runtime::Current()->GetClassLinker(); - mirror::Class* class_loader_class = - soa.Decode(WellKnownClasses::java_lang_ClassLoader); - CHECK(Runtime::Current()->GetClassLinker()->EnsureInitialized(class_loader_class, true, true)); + SirtRef class_loader_class( + soa.Self(), soa.Decode(WellKnownClasses::java_lang_ClassLoader)); + CHECK(cl->EnsureInitialized(class_loader_class, true, true)); mirror::ArtMethod* getSystemClassLoader = class_loader_class->FindDirectMethod("getSystemClassLoader", "()Ljava/lang/ClassLoader;"); CHECK(getSystemClassLoader != NULL); JValue result; - ArgArray arg_array(NULL, 0); + ArgArray arg_array(nullptr, 0); InvokeWithArgArray(soa, getSystemClassLoader, &arg_array, &result, 'L'); - mirror::ClassLoader* class_loader = down_cast(result.GetL()); - CHECK(class_loader != NULL); - + SirtRef class_loader(soa.Self(), + down_cast(result.GetL())); + CHECK(class_loader.get() != nullptr); JNIEnv* env = soa.Self()->GetJniEnv(); - ScopedLocalRef system_class_loader(env, soa.AddLocalReference(class_loader)); - CHECK(system_class_loader.get() != NULL); + ScopedLocalRef system_class_loader(env, + soa.AddLocalReference(class_loader.get())); + CHECK(system_class_loader.get() != nullptr); - soa.Self()->SetClassLoaderOverride(class_loader); + soa.Self()->SetClassLoaderOverride(class_loader.get()); - mirror::Class* thread_class = soa.Decode(WellKnownClasses::java_lang_Thread); - CHECK(Runtime::Current()->GetClassLinker()->EnsureInitialized(thread_class, true, true)); + SirtRef thread_class(soa.Self(), + soa.Decode(WellKnownClasses::java_lang_Thread)); + CHECK(cl->EnsureInitialized(thread_class, true, true)); - mirror::ArtField* contextClassLoader = thread_class->FindDeclaredInstanceField("contextClassLoader", - "Ljava/lang/ClassLoader;"); + mirror::ArtField* contextClassLoader = + thread_class->FindDeclaredInstanceField("contextClassLoader", "Ljava/lang/ClassLoader;"); CHECK(contextClassLoader != NULL); - contextClassLoader->SetObject(soa.Self()->GetPeer(), class_loader); + contextClassLoader->SetObject(soa.Self()->GetPeer(), class_loader.get()); return env->NewGlobalRef(system_class_loader.get()); } @@ -1188,9 +1193,25 @@ void Runtime::VisitConcurrentRoots(RootVisitor* visitor, void* arg, bool only_di } void Runtime::VisitNonThreadRoots(RootVisitor* visitor, void* arg) { + // Visit the classes held as static in mirror classes. + mirror::ArtField::VisitRoots(visitor, arg); + mirror::ArtMethod::VisitRoots(visitor, arg); + mirror::Class::VisitRoots(visitor, arg); + mirror::StackTraceElement::VisitRoots(visitor, arg); + mirror::String::VisitRoots(visitor, arg); + mirror::Throwable::VisitRoots(visitor, arg); + // Visit all the primitive array types classes. + mirror::PrimitiveArray::VisitRoots(visitor, arg); // BooleanArray + mirror::PrimitiveArray::VisitRoots(visitor, arg); // ByteArray + mirror::PrimitiveArray::VisitRoots(visitor, arg); // CharArray + mirror::PrimitiveArray::VisitRoots(visitor, arg); // DoubleArray + mirror::PrimitiveArray::VisitRoots(visitor, arg); // FloatArray + mirror::PrimitiveArray::VisitRoots(visitor, arg); // IntArray + mirror::PrimitiveArray::VisitRoots(visitor, arg); // LongArray + mirror::PrimitiveArray::VisitRoots(visitor, arg); // ShortArray java_vm_->VisitRoots(visitor, arg); if (pre_allocated_OutOfMemoryError_ != nullptr) { - pre_allocated_OutOfMemoryError_ = reinterpret_cast( + pre_allocated_OutOfMemoryError_ = down_cast( visitor(pre_allocated_OutOfMemoryError_, arg)); DCHECK(pre_allocated_OutOfMemoryError_ != nullptr); } @@ -1209,6 +1230,12 @@ void Runtime::VisitNonThreadRoots(RootVisitor* visitor, void* arg) { visitor(callee_save_methods_[i], arg)); } } + { + MutexLock mu(Thread::Current(), method_verifiers_lock_); + for (verifier::MethodVerifier* verifier : method_verifiers_) { + verifier->VisitRoots(visitor, arg); + } + } } void Runtime::VisitNonConcurrentRoots(RootVisitor* visitor, void* arg) { @@ -1355,4 +1382,18 @@ void Runtime::SetCompileTimeClassPath(jobject class_loader, std::vector& GetCompileTimeClassPath(jobject class_loader); void SetCompileTimeClassPath(jobject class_loader, std::vector& class_path); @@ -520,6 +528,10 @@ class Runtime { mirror::ObjectArray* default_imt_; + // Method verifier set, used so that we can update their GC roots. + Mutex method_verifiers_lock_; + std::set method_verifiers_; + // A non-zero value indicates that a thread has been created but not yet initialized. Guarded by // the shutdown lock so that threads aren't born while we're shutting down. size_t threads_being_born_ GUARDED_BY(Locks::runtime_shutdown_lock_); diff --git a/runtime/thread.cc b/runtime/thread.cc index 715be99942b..570379a7a1b 100644 --- a/runtime/thread.cc +++ b/runtime/thread.cc @@ -987,8 +987,9 @@ void Thread::Destroy() { mirror::Object* lock = soa.DecodeField(WellKnownClasses::java_lang_Thread_lock)->GetObject(opeer_); // (This conditional is only needed for tests, where Thread.lock won't have been set.) - if (lock != NULL) { - ObjectLock locker(self, lock); + if (lock != nullptr) { + SirtRef sirt_obj(self, lock); + ObjectLock locker(self, &sirt_obj); locker.Notify(); } } @@ -1444,9 +1445,9 @@ void Thread::ThrowNewWrappedException(const ThrowLocation& throw_location, ClearException(); Runtime* runtime = Runtime::Current(); - mirror::ClassLoader* cl = NULL; - if (throw_location.GetMethod() != NULL) { - cl = throw_location.GetMethod()->GetDeclaringClass()->GetClassLoader(); + mirror::ClassLoader* cl = nullptr; + if (saved_throw_method.get() != nullptr) { + cl = saved_throw_method.get()->GetDeclaringClass()->GetClassLoader(); } SirtRef class_loader(this, cl); SirtRef @@ -1458,7 +1459,7 @@ void Thread::ThrowNewWrappedException(const ThrowLocation& throw_location, return; } - if (UNLIKELY(!runtime->GetClassLinker()->EnsureInitialized(exception_class.get(), true, true))) { + if (UNLIKELY(!runtime->GetClassLinker()->EnsureInitialized(exception_class, true, true))) { DCHECK(IsExceptionPending()); return; } @@ -1468,7 +1469,9 @@ void Thread::ThrowNewWrappedException(const ThrowLocation& throw_location, // If we couldn't allocate the exception, throw the pre-allocated out of memory exception. if (exception.get() == nullptr) { - SetException(throw_location, Runtime::Current()->GetPreAllocatedOutOfMemoryError()); + ThrowLocation gc_safe_throw_location(saved_throw_this.get(), saved_throw_method.get(), + throw_location.GetDexPc()); + SetException(gc_safe_throw_location, Runtime::Current()->GetPreAllocatedOutOfMemoryError()); return; } diff --git a/runtime/throw_location.cc b/runtime/throw_location.cc index 01497ef0e50..1cc3e748877 100644 --- a/runtime/throw_location.cc +++ b/runtime/throw_location.cc @@ -25,7 +25,7 @@ namespace art { std::string ThrowLocation::Dump() const { - if (method_ != NULL) { + if (method_ != nullptr) { return StringPrintf("%s:%d", PrettyMethod(method_).c_str(), MethodHelper(method_).GetLineNumFromDexPC(dex_pc_)); } else { @@ -35,12 +35,11 @@ std::string ThrowLocation::Dump() const { void ThrowLocation::VisitRoots(RootVisitor* visitor, void* arg) { if (this_object_ != nullptr) { - this_object_ = const_cast(visitor(this_object_, arg)); + this_object_ = visitor(this_object_, arg); DCHECK(this_object_ != nullptr); } if (method_ != nullptr) { - method_ = const_cast( - reinterpret_cast(visitor(method_, arg))); + method_ = down_cast(visitor(method_, arg)); DCHECK(method_ != nullptr); } } diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc index 5f5d8654517..9183b5f53c4 100644 --- a/runtime/verifier/method_verifier.cc +++ b/runtime/verifier/method_verifier.cc @@ -319,10 +319,12 @@ MethodVerifier::MethodVerifier(const DexFile* dex_file, SirtRefAddMethodVerifier(this); DCHECK(class_def != nullptr); } MethodVerifier::~MethodVerifier() { + Runtime::Current()->RemoveMethodVerifier(this); STLDeleteElements(&failure_messages_); } @@ -4385,5 +4387,9 @@ bool MethodVerifier::IsClassRejected(ClassReference ref) { return (rejected_classes_->find(ref) != rejected_classes_->end()); } +void MethodVerifier::VisitRoots(RootVisitor* visitor, void* arg) { + reg_types_.VisitRoots(visitor, arg); +} + } // namespace verifier } // namespace art diff --git a/runtime/verifier/method_verifier.h b/runtime/verifier/method_verifier.h index 892b7a886c0..6b5747b2f16 100644 --- a/runtime/verifier/method_verifier.h +++ b/runtime/verifier/method_verifier.h @@ -237,6 +237,8 @@ class MethodVerifier { static bool IsCandidateForCompilation(MethodReference& method_ref, const uint32_t access_flags); + void VisitRoots(RootVisitor* visitor, void* arg) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + private: // Adds the given string to the beginning of the last failure message. void PrependToLastFailMessage(std::string); diff --git a/runtime/verifier/reg_type.cc b/runtime/verifier/reg_type.cc index d82e75de07e..f394bce7bf6 100644 --- a/runtime/verifier/reg_type.cc +++ b/runtime/verifier/reg_type.cc @@ -969,6 +969,12 @@ void RegType::CheckInvariants() const { } } +void RegType::VisitRoots(RootVisitor* visitor, void* arg) { + if (klass_ != nullptr) { + klass_ = down_cast(visitor(klass_, arg)); + } +} + void UninitializedThisReferenceType::CheckInvariants() const { CHECK_EQ(GetAllocationPc(), 0U) << *this; } diff --git a/runtime/verifier/reg_type.h b/runtime/verifier/reg_type.h index f3717330eb8..8df481fcf53 100644 --- a/runtime/verifier/reg_type.h +++ b/runtime/verifier/reg_type.h @@ -20,6 +20,7 @@ #include "base/macros.h" #include "globals.h" #include "primitive.h" +#include "root_visitor.h" #include "jni.h" @@ -269,6 +270,8 @@ class RegType { virtual ~RegType() {} + void VisitRoots(RootVisitor* visitor, void* arg) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + protected: RegType(mirror::Class* klass, const std::string& descriptor, uint16_t cache_id) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) @@ -282,7 +285,7 @@ class RegType { const std::string descriptor_; - mirror::Class* const klass_; + mirror::Class* klass_; const uint16_t cache_id_; friend class RegTypeCache; diff --git a/runtime/verifier/reg_type_cache.cc b/runtime/verifier/reg_type_cache.cc index 9c9673aafd0..3d244144938 100644 --- a/runtime/verifier/reg_type_cache.cc +++ b/runtime/verifier/reg_type_cache.cc @@ -554,5 +554,11 @@ void RegTypeCache::Dump(std::ostream& os) { } } +void RegTypeCache::VisitRoots(RootVisitor* visitor, void* arg) { + for (RegType* entry : entries_) { + entry->VisitRoots(visitor, arg); + } +} + } // namespace verifier } // namespace art diff --git a/runtime/verifier/reg_type_cache.h b/runtime/verifier/reg_type_cache.h index a9f8bff784c..a8116961259 100644 --- a/runtime/verifier/reg_type_cache.h +++ b/runtime/verifier/reg_type_cache.h @@ -21,6 +21,7 @@ #include "base/macros.h" #include "base/stl_util.h" #include "reg_type.h" +#include "root_visitor.h" #include "runtime.h" #include @@ -139,6 +140,8 @@ class RegTypeCache { void Dump(std::ostream& os) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); const RegType& RegTypeFromPrimitiveType(Primitive::Type) const; + void VisitRoots(RootVisitor* visitor, void* arg) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + private: void FillPrimitiveAndSmallConstantTypes() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); mirror::Class* ResolveClass(const char* descriptor, mirror::ClassLoader* loader) From 62509b662929175228bb0d0f014ef4ef4e33be10 Mon Sep 17 00:00:00 2001 From: Jeff Hao Date: Tue, 10 Dec 2013 17:44:56 -0800 Subject: [PATCH 0267/2402] Fix FindFieldID to use class's classloader to find field type. Also includes a test case, though the test case does not recreate this exact issue because the attached native thread would need to have a null context classloader. Bug: 11737351 Change-Id: Ieb7536702a556485d667361124cd7985b712b093 --- runtime/jni_internal.cc | 2 +- test/JniTest/JniTest.java | 12 ++++++++++++ test/JniTest/jni_test.cc | 36 ++++++++++++++++++++++++++++++++++++ 3 files changed, 49 insertions(+), 1 deletion(-) diff --git a/runtime/jni_internal.cc b/runtime/jni_internal.cc index 466edebf591..736e305881e 100644 --- a/runtime/jni_internal.cc +++ b/runtime/jni_internal.cc @@ -293,7 +293,7 @@ static jfieldID FindFieldID(const ScopedObjectAccess& soa, jclass jni_class, con Class* field_type; ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); if (sig[1] != '\0') { - SirtRef class_loader(soa.Self(), GetClassLoader(soa)); + SirtRef class_loader(soa.Self(), c->GetClassLoader()); field_type = class_linker->FindClass(sig, class_loader); } else { field_type = class_linker->FindPrimitiveClass(*sig); diff --git a/test/JniTest/JniTest.java b/test/JniTest/JniTest.java index a1b1f0c69dd..9194da581f1 100644 --- a/test/JniTest/JniTest.java +++ b/test/JniTest/JniTest.java @@ -20,12 +20,24 @@ class JniTest { public static void main(String[] args) { System.loadLibrary("arttest"); testFindClassOnAttachedNativeThread(); + testFindFieldOnAttachedNativeThread(); testCallStaticVoidMethodOnSubClass(); testGetMirandaMethod(); } private static native void testFindClassOnAttachedNativeThread(); + private static boolean testFindFieldOnAttachedNativeThreadField; + + private static void testFindFieldOnAttachedNativeThread() { + testFindFieldOnAttachedNativeThreadNative(); + if (!testFindFieldOnAttachedNativeThreadField) { + throw new AssertionError(); + } + } + + private static native void testFindFieldOnAttachedNativeThreadNative(); + private static void testCallStaticVoidMethodOnSubClass() { testCallStaticVoidMethodOnSubClassNative(); if (!testCallStaticVoidMethodOnSubClass_SuperClass.executed) { diff --git a/test/JniTest/jni_test.cc b/test/JniTest/jni_test.cc index cfcbb64f38f..d15e180c02f 100644 --- a/test/JniTest/jni_test.cc +++ b/test/JniTest/jni_test.cc @@ -67,6 +67,42 @@ extern "C" JNIEXPORT void JNICALL Java_JniTest_testFindClassOnAttachedNativeThre assert(pthread_join_result == 0); } +static void* testFindFieldOnAttachedNativeThread(void*) { + assert(jvm != NULL); + + JNIEnv* env = NULL; + JavaVMAttachArgs args = { JNI_VERSION_1_6, __FUNCTION__, NULL }; + int attach_result = jvm->AttachCurrentThread(&env, &args); + assert(attach_result == 0); + + jclass clazz = env->FindClass("JniTest"); + assert(clazz != NULL); + assert(!env->ExceptionCheck()); + + jfieldID field = env->GetStaticFieldID(clazz, "testFindFieldOnAttachedNativeThreadField", "Z"); + assert(field != NULL); + assert(!env->ExceptionCheck()); + + env->SetStaticBooleanField(clazz, field, JNI_TRUE); + + int detach_result = jvm->DetachCurrentThread(); + assert(detach_result == 0); + return NULL; +} + +extern "C" JNIEXPORT void JNICALL Java_JniTest_testFindFieldOnAttachedNativeThreadNative(JNIEnv*, + jclass) { + pthread_t pthread; + int pthread_create_result = pthread_create(&pthread, + NULL, + testFindFieldOnAttachedNativeThread, + NULL); + assert(pthread_create_result == 0); + int pthread_join_result = pthread_join(pthread, NULL); + assert(pthread_join_result == 0); +} + + // http://b/11243757 extern "C" JNIEXPORT void JNICALL Java_JniTest_testCallStaticVoidMethodOnSubClassNative(JNIEnv* env, jclass) { From 08cbf66dc4632913f80f8ac18082c39b7d52c7dd Mon Sep 17 00:00:00 2001 From: Brian Carlstrom Date: Tue, 10 Dec 2013 16:52:57 -0800 Subject: [PATCH 0268/2402] Do not require classes.dex to support stripped zip files Change-Id: Ief34c1b559dbebda85d181ae49da7d35446c9b37 --- runtime/class_linker.cc | 21 ++++++++++++++------- runtime/class_linker.h | 4 ++-- runtime/native/dalvik_system_DexFile.cc | 19 +++++++++++++------ 3 files changed, 29 insertions(+), 15 deletions(-) diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index 500cb59bee4..99145b83735 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -673,18 +673,19 @@ OatFile& ClassLinker::GetImageOatFile(gc::space::ImageSpace* space) { } const OatFile* ClassLinker::FindOpenedOatFileForDexFile(const DexFile& dex_file) { - return FindOpenedOatFileFromDexLocation(dex_file.GetLocation().c_str(), - dex_file.GetLocationChecksum()); + const char* dex_location = dex_file.GetLocation().c_str(); + uint32_t dex_location_checksum = dex_file.GetLocationChecksum(); + return FindOpenedOatFileFromDexLocation(dex_location, &dex_location_checksum); } const OatFile* ClassLinker::FindOpenedOatFileFromDexLocation(const char* dex_location, - uint32_t dex_location_checksum) { + const uint32_t* const dex_location_checksum) { ReaderMutexLock mu(Thread::Current(), dex_lock_); for (size_t i = 0; i < oat_files_.size(); i++) { const OatFile* oat_file = oat_files_[i]; DCHECK(oat_file != NULL); const OatFile::OatDexFile* oat_dex_file = oat_file->GetOatDexFile(dex_location, - &dex_location_checksum, + dex_location_checksum, false); if (oat_dex_file != NULL) { return oat_file; @@ -943,13 +944,13 @@ const DexFile* ClassLinker::VerifyAndOpenDexFileFromOatFile(const std::string& o } const DexFile* ClassLinker::FindDexFileInOatFileFromDexLocation(const char* dex_location, - uint32_t dex_location_checksum, + const uint32_t* const dex_location_checksum, std::string* error_msg) { const OatFile* open_oat_file = FindOpenedOatFileFromDexLocation(dex_location, dex_location_checksum); if (open_oat_file != nullptr) { const OatFile::OatDexFile* oat_dex_file = open_oat_file->GetOatDexFile(dex_location, - &dex_location_checksum); + dex_location_checksum); return oat_dex_file->OpenDexFile(error_msg); } @@ -962,6 +963,12 @@ const DexFile* ClassLinker::FindDexFileInOatFileFromDexLocation(const char* dex_ if (dex_file != nullptr) { return dex_file; } + if (dex_location_checksum == nullptr) { + *error_msg = StringPrintf("Failed to open oat file from %s and no classes.dex found in %s: %s", + odex_filename.c_str(), dex_location, error_msg->c_str()); + return nullptr; + } + std::string cache_error_msg; std::string cache_location(GetDalvikCacheFilenameOrDie(dex_location)); dex_file = VerifyAndOpenDexFileFromOatFile(cache_location, dex_location, &cache_error_msg, @@ -978,7 +985,7 @@ const DexFile* ClassLinker::FindDexFileInOatFileFromDexLocation(const char* dex_ // Try to generate oat file if it wasn't found or was obsolete. error_msg->clear(); - return FindOrCreateOatFileForDexLocation(dex_location, dex_location_checksum, + return FindOrCreateOatFileForDexLocation(dex_location, *dex_location_checksum, cache_location.c_str(), error_msg); } diff --git a/runtime/class_linker.h b/runtime/class_linker.h index 4e2cc063b34..edec9cb0765 100644 --- a/runtime/class_linker.h +++ b/runtime/class_linker.h @@ -268,7 +268,7 @@ class ClassLinker { // that this returns null if the location checksum of the DexFile // does not match the OatFile. const DexFile* FindDexFileInOatFileFromDexLocation(const char* location, - uint32_t location_checksum, + const uint32_t* const location_checksum, std::string* error_msg) LOCKS_EXCLUDED(dex_lock_, Locks::mutator_lock_); @@ -495,7 +495,7 @@ class ClassLinker { LOCKS_EXCLUDED(dex_lock_) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); const OatFile* FindOpenedOatFileFromDexLocation(const char* dex_location, - uint32_t dex_location_checksum) + const uint32_t* const dex_location_checksum) LOCKS_EXCLUDED(dex_lock); const OatFile* FindOpenedOatFileFromOatLocation(const std::string& oat_location) LOCKS_EXCLUDED(dex_lock_); diff --git a/runtime/native/dalvik_system_DexFile.cc b/runtime/native/dalvik_system_DexFile.cc index c9e0e83b275..600045f66a5 100644 --- a/runtime/native/dalvik_system_DexFile.cc +++ b/runtime/native/dalvik_system_DexFile.cc @@ -95,20 +95,27 @@ static jint DexFile_openDexFileNative(JNIEnv* env, jclass, jstring javaSourceNam } uint32_t dex_location_checksum; + uint32_t* dex_location_checksum_pointer = &dex_location_checksum; std::string error_msg; - if (!DexFile::GetChecksum(sourceName.c_str(), &dex_location_checksum, &error_msg)) { - ScopedObjectAccess soa(env); - DCHECK(!error_msg.empty()); - ThrowIOException("%s", error_msg.c_str()); - return 0; + if (!DexFile::GetChecksum(sourceName.c_str(), dex_location_checksum_pointer, &error_msg)) { + dex_location_checksum_pointer = NULL; } ClassLinker* linker = Runtime::Current()->GetClassLinker(); const DexFile* dex_file; if (outputName.c_str() == nullptr) { + // FindOrCreateOatFileForDexLocation can tolerate a missing dex_location_checksum + error_msg.clear(); dex_file = linker->FindDexFileInOatFileFromDexLocation(sourceName.c_str(), - dex_location_checksum, &error_msg); + dex_location_checksum_pointer, &error_msg); } else { + // FindOrCreateOatFileForDexLocation requires the dex_location_checksum + if (dex_location_checksum_pointer == NULL) { + ScopedObjectAccess soa(env); + DCHECK(!error_msg.empty()); + ThrowIOException("%s", error_msg.c_str()); + return 0; + } dex_file = linker->FindOrCreateOatFileForDexLocation(sourceName.c_str(), dex_location_checksum, outputName.c_str(), &error_msg); } From fd7ad0acf1a50e248fff1a9849c8d89e9e200ef6 Mon Sep 17 00:00:00 2001 From: Brian Carlstrom Date: Tue, 10 Dec 2013 18:20:45 -0800 Subject: [PATCH 0269/2402] Fix dependencies to be on core-libart, not core Change-Id: I002f4ab170146d48210655fbed44328a289873f5 --- Android.mk | 4 ++-- runtime/dex_method_iterator_test.cc | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Android.mk b/Android.mk index 76fb411ac6f..0492d3cc5dc 100644 --- a/Android.mk +++ b/Android.mk @@ -93,9 +93,9 @@ include $(art_path)/jdwpspy/Android.mk include $(art_build_path)/Android.oat.mk # ART_HOST_DEPENDENCIES depends on Android.executable.mk above for ART_HOST_EXECUTABLES -ART_HOST_DEPENDENCIES := $(ART_HOST_EXECUTABLES) $(HOST_OUT_JAVA_LIBRARIES)/core-hostdex.jar +ART_HOST_DEPENDENCIES := $(ART_HOST_EXECUTABLES) $(HOST_OUT_JAVA_LIBRARIES)/core-libart-hostdex.jar ART_HOST_DEPENDENCIES += $(HOST_OUT_SHARED_LIBRARIES)/libjavacore$(ART_HOST_SHLIB_EXTENSION) -ART_TARGET_DEPENDENCIES := $(ART_TARGET_EXECUTABLES) $(TARGET_OUT_JAVA_LIBRARIES)/core.jar $(TARGET_OUT_SHARED_LIBRARIES)/libjavacore.so +ART_TARGET_DEPENDENCIES := $(ART_TARGET_EXECUTABLES) $(TARGET_OUT_JAVA_LIBRARIES)/core-libart.jar $(TARGET_OUT_SHARED_LIBRARIES)/libjavacore.so ######################################################################## # test targets diff --git a/runtime/dex_method_iterator_test.cc b/runtime/dex_method_iterator_test.cc index 9961df9a558..2941db69455 100644 --- a/runtime/dex_method_iterator_test.cc +++ b/runtime/dex_method_iterator_test.cc @@ -36,7 +36,7 @@ class DexMethodIteratorTest : public CommonTest { TEST_F(DexMethodIteratorTest, Basic) { ScopedObjectAccess soa(Thread::Current()); std::vector dex_files; - dex_files.push_back(OpenDexFile("core")); + dex_files.push_back(OpenDexFile("core-libart")); dex_files.push_back(OpenDexFile("conscrypt")); dex_files.push_back(OpenDexFile("okhttp")); dex_files.push_back(OpenDexFile("core-junit")); From 1ebe2173d9c6144da16fd6c8790d14bcc9b38fa0 Mon Sep 17 00:00:00 2001 From: Sebastien Hertz Date: Wed, 11 Dec 2013 11:16:03 +0100 Subject: [PATCH 0270/2402] Remove unused method in DEX-to-DEX. Change-Id: Ice5f91b45a0ad85576475a613193f143fd76ad9f --- compiler/dex/dex_to_dex_compiler.cc | 6 ------ 1 file changed, 6 deletions(-) diff --git a/compiler/dex/dex_to_dex_compiler.cc b/compiler/dex/dex_to_dex_compiler.cc index abafbc58307..3368132a0e8 100644 --- a/compiler/dex/dex_to_dex_compiler.cc +++ b/compiler/dex/dex_to_dex_compiler.cc @@ -52,12 +52,6 @@ class DexCompiler { return *unit_.GetDexFile(); } - // TODO: since the whole compilation pipeline uses a "const DexFile", we need - // to "unconst" here. The DEX-to-DEX compiler should work on a non-const DexFile. - DexFile& GetModifiableDexFile() { - return *const_cast(unit_.GetDexFile()); - } - bool PerformOptimizations() const { return dex_to_dex_compilation_level_ >= kOptimize; } From 9e0668f2ef14838b3e5a07e23d581efca9d7b8a1 Mon Sep 17 00:00:00 2001 From: Narayan Kamath Date: Wed, 11 Dec 2013 15:32:30 +0000 Subject: [PATCH 0271/2402] Add dependency on libutils for libziparchive Change-Id: I02d58ad0d2f29cc7b94bc4214ca69d988595c4e5 --- runtime/Android.mk | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/runtime/Android.mk b/runtime/Android.mk index 4e5afab4e79..2c13284d16e 100644 --- a/runtime/Android.mk +++ b/runtime/Android.mk @@ -344,10 +344,10 @@ $$(ENUM_OPERATOR_OUT_GEN): $$(GENERATED_SRC_DIR)/%_operator_out.cc : $(LOCAL_PAT LOCAL_SHARED_LIBRARIES += liblog libnativehelper LOCAL_SHARED_LIBRARIES += libbacktrace # native stack trace support ifeq ($$(art_target_or_host),target) - LOCAL_SHARED_LIBRARIES += libcutils libdl libselinux + LOCAL_SHARED_LIBRARIES += libcutils libdl libselinux libutils LOCAL_STATIC_LIBRARIES := libziparchive libz else # host - LOCAL_STATIC_LIBRARIES += libcutils libziparchive-host libz + LOCAL_STATIC_LIBRARIES += libcutils libziparchive-host libz libutils LOCAL_LDLIBS += -ldl -lpthread ifeq ($(HOST_OS),linux) LOCAL_LDLIBS += -lrt From 45c1165558eb8f9c81843b63ed362079a60c8387 Mon Sep 17 00:00:00 2001 From: Mark Mendell Date: Wed, 11 Dec 2013 12:27:35 -0800 Subject: [PATCH 0272/2402] Define missing static constexpr variables This will prevent link errors when undefined symbols when compiling at -O0. Normally, this isn't a problem, but causes failure when compiling at -O0 to maximize debugability Change-Id: Iafe187b6b9344fead1bbc73004e376fad821756b Signed-off-by: Mark Mendell --- compiler/dex/arena_allocator.cc | 1 + compiler/dex/quick/dex_file_method_inliner.cc | 1 + runtime/base/timing_logger.cc | 2 ++ 3 files changed, 4 insertions(+) diff --git a/compiler/dex/arena_allocator.cc b/compiler/dex/arena_allocator.cc index 95e44b3e0dc..132831c3efb 100644 --- a/compiler/dex/arena_allocator.cc +++ b/compiler/dex/arena_allocator.cc @@ -28,6 +28,7 @@ namespace art { static constexpr bool kUseMemMap = false; static constexpr bool kUseMemSet = true && kUseMemMap; static constexpr size_t kValgrindRedZoneBytes = 8; +constexpr size_t Arena::kDefaultSize; static const char* alloc_names[ArenaAllocator::kNumAllocKinds] = { "Misc ", diff --git a/compiler/dex/quick/dex_file_method_inliner.cc b/compiler/dex/quick/dex_file_method_inliner.cc index fb471abfa9b..b21e37e13d8 100644 --- a/compiler/dex/quick/dex_file_method_inliner.cc +++ b/compiler/dex/quick/dex_file_method_inliner.cc @@ -22,6 +22,7 @@ namespace art { +const uint32_t DexFileMethodInliner::kIndexUnresolved; const char* DexFileMethodInliner::kClassCacheNames[] = { "Z", // kClassCacheBoolean "B", // kClassCacheByte diff --git a/runtime/base/timing_logger.cc b/runtime/base/timing_logger.cc index c8dee6d346f..076bc119c1e 100644 --- a/runtime/base/timing_logger.cc +++ b/runtime/base/timing_logger.cc @@ -31,6 +31,8 @@ namespace art { +constexpr size_t CumulativeLogger::kLowMemoryBucketCount; +constexpr size_t CumulativeLogger::kDefaultBucketCount; CumulativeLogger::CumulativeLogger(const std::string& name) : name_(name), lock_name_("CumulativeLoggerLock" + name), From f723f0cdc693f81581c0781fa472b1c85a8b42d6 Mon Sep 17 00:00:00 2001 From: Mark Mendell Date: Wed, 11 Dec 2013 17:50:58 -0800 Subject: [PATCH 0273/2402] Add missing x86 imul opcode to disassembler When playing with ART, I noticed that an integer multiply didn't disassemble properly. This patch adds the instruction. Change-Id: Ic4d4921b1b301a9d674a257f094e8b3d834ed991 Signed-off-by: Mark Mendell --- disassembler/disassembler_x86.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/disassembler/disassembler_x86.cc b/disassembler/disassembler_x86.cc index 8781c7a2749..4a08ce054f8 100644 --- a/disassembler/disassembler_x86.cc +++ b/disassembler/disassembler_x86.cc @@ -515,6 +515,7 @@ DISASSEMBLER_ENTRY(cmp, no_ops = true; } break; + case 0xAF: opcode << "imul"; has_modrm = true; load = true; break; case 0xB1: opcode << "cmpxchg"; has_modrm = true; store = true; break; case 0xB6: opcode << "movzxb"; has_modrm = true; load = true; break; case 0xB7: opcode << "movzxw"; has_modrm = true; load = true; break; From 0630ab5239a7d7be24dedbc3f66c822332446fc3 Mon Sep 17 00:00:00 2001 From: Sebastien Hertz Date: Thu, 28 Nov 2013 18:53:35 +0100 Subject: [PATCH 0274/2402] Fix null argument handling during invoke from JDWP. Fix a crash when passing null argument to a JDWP invoke. Change-Id: I167f93f3a411f0de8458db3ba5bed3169d557ee9 --- runtime/debugger.cc | 2 +- runtime/jdwp/object_registry.cc | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/runtime/debugger.cc b/runtime/debugger.cc index 52a2141a0ba..2b821675721 100644 --- a/runtime/debugger.cc +++ b/runtime/debugger.cc @@ -2712,7 +2712,7 @@ JDWP::JdwpError Dbg::InvokeMethod(JDWP::ObjectId thread_id, JDWP::ObjectId objec if (argument == ObjectRegistry::kInvalidObject) { return JDWP::ERR_INVALID_OBJECT; } - if (!argument->InstanceOf(parameter_type)) { + if (argument != NULL && !argument->InstanceOf(parameter_type)) { return JDWP::ERR_ILLEGAL_ARGUMENT; } diff --git a/runtime/jdwp/object_registry.cc b/runtime/jdwp/object_registry.cc index be2219c3115..7408e92d003 100644 --- a/runtime/jdwp/object_registry.cc +++ b/runtime/jdwp/object_registry.cc @@ -118,6 +118,9 @@ mirror::Object* ObjectRegistry::InternalGet(JDWP::ObjectId id) { } jobject ObjectRegistry::GetJObject(JDWP::ObjectId id) { + if (id == 0) { + return NULL; + } Thread* self = Thread::Current(); MutexLock mu(self, lock_); id_iterator it = id_to_entry_.find(id); From e96060aa2483529d087031f7cdcc0405f1ef9218 Mon Sep 17 00:00:00 2001 From: Sebastien Hertz Date: Wed, 11 Dec 2013 12:06:28 +0100 Subject: [PATCH 0275/2402] Manage JDWP errors related to garbage collection. Returns INVALID_OBJECT error in case of invalid object ID or null object ID in commands ObjectReference.DisableCollection, ObjectReference.EnableCollection and ObjectReference.IsCollected. Note: JDWP specs do not state ObjectReference.EnableCollection must return any error code. We choose to be more strict so these three commands all behave the same. Change-Id: I06fb75b75f7d33cf4d23e121d9541bfd70b778bb --- runtime/debugger.cc | 19 +++++++++++++++++++ runtime/jdwp/object_registry.cc | 12 +++--------- 2 files changed, 22 insertions(+), 9 deletions(-) diff --git a/runtime/debugger.cc b/runtime/debugger.cc index 52a2141a0ba..edfd21f1571 100644 --- a/runtime/debugger.cc +++ b/runtime/debugger.cc @@ -795,18 +795,37 @@ JDWP::JdwpError Dbg::GetReferringObjects(JDWP::ObjectId object_id, int32_t max_c JDWP::JdwpError Dbg::DisableCollection(JDWP::ObjectId object_id) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + mirror::Object* o = gRegistry->Get(object_id); + if (o == NULL || o == ObjectRegistry::kInvalidObject) { + return JDWP::ERR_INVALID_OBJECT; + } gRegistry->DisableCollection(object_id); return JDWP::ERR_NONE; } JDWP::JdwpError Dbg::EnableCollection(JDWP::ObjectId object_id) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + mirror::Object* o = gRegistry->Get(object_id); + // Unlike DisableCollection, JDWP specs do not state an invalid object causes an error. The RI + // also ignores these cases and never return an error. However it's not obvious why this command + // should behave differently from DisableCollection and IsCollected commands. So let's be more + // strict and return an error if this happens. + if (o == NULL || o == ObjectRegistry::kInvalidObject) { + return JDWP::ERR_INVALID_OBJECT; + } gRegistry->EnableCollection(object_id); return JDWP::ERR_NONE; } JDWP::JdwpError Dbg::IsCollected(JDWP::ObjectId object_id, bool& is_collected) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + mirror::Object* o = gRegistry->Get(object_id); + // JDWP specs state an INVALID_OBJECT error is returned if the object ID is not valid. However + // the RI seems to ignore this and does not return any error in this case. Let's comply with + // JDWP specs here. + if (o == NULL || o == ObjectRegistry::kInvalidObject) { + return JDWP::ERR_INVALID_OBJECT; + } is_collected = gRegistry->IsCollected(object_id); return JDWP::ERR_NONE; } diff --git a/runtime/jdwp/object_registry.cc b/runtime/jdwp/object_registry.cc index be2219c3115..b2371e83e7f 100644 --- a/runtime/jdwp/object_registry.cc +++ b/runtime/jdwp/object_registry.cc @@ -130,9 +130,7 @@ void ObjectRegistry::DisableCollection(JDWP::ObjectId id) { Thread* self = Thread::Current(); MutexLock mu(self, lock_); id_iterator it = id_to_entry_.find(id); - if (it == id_to_entry_.end()) { - return; - } + CHECK(it != id_to_entry_.end()); Promote(*(it->second)); } @@ -140,9 +138,7 @@ void ObjectRegistry::EnableCollection(JDWP::ObjectId id) { Thread* self = Thread::Current(); MutexLock mu(self, lock_); id_iterator it = id_to_entry_.find(id); - if (it == id_to_entry_.end()) { - return; - } + CHECK(it != id_to_entry_.end()); Demote(*(it->second)); } @@ -172,9 +168,7 @@ bool ObjectRegistry::IsCollected(JDWP::ObjectId id) { Thread* self = Thread::Current(); MutexLock mu(self, lock_); id_iterator it = id_to_entry_.find(id); - if (it == id_to_entry_.end()) { - return true; // TODO: can we report that this was an invalid id? - } + CHECK(it != id_to_entry_.end()); ObjectRegistryEntry& entry = *(it->second); if (entry.jni_reference_type == JNIWeakGlobalRefType) { From 460536209b741bc469f1b0857775449abb2102fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bernhard=20Rosenkr=C3=A4nzer?= Date: Thu, 12 Dec 2013 02:15:52 +0100 Subject: [PATCH 0276/2402] Don't rely on gcc extensions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Make the code more compatible with different compilers. clang doesn't allow extra static qualifiers on template specializations, const qualifiers on function types, or inline attributes on lambda functions, and is more picky about casting away constness with reinterpret_cast. These modifications are compatible with both gcc and clang. Change-Id: I739b10df2780bec537827a13679fd2bcc2cc7188 Signed-off-by: Bernhard Rosenkränzer --- compiler/dex/portable/mir_to_gbc.cc | 1 - compiler/driver/compiler_driver.cc | 2 +- compiler/image_writer.cc | 4 +- runtime/base/macros.h | 7 ++ runtime/entrypoints/entrypoint_utils.h | 18 ++--- .../portable/portable_invoke_entrypoints.cc | 16 ++--- .../quick/quick_invoke_entrypoints.cc | 18 ++--- runtime/gc/collector/mark_sweep.cc | 2 +- runtime/gc/collector/semi_space.cc | 2 +- runtime/interpreter/interpreter.cc | 72 +++++++++---------- runtime/interpreter/interpreter_common.h | 52 +++++++------- 11 files changed, 100 insertions(+), 94 deletions(-) diff --git a/compiler/dex/portable/mir_to_gbc.cc b/compiler/dex/portable/mir_to_gbc.cc index e5b4876f082..70b660b7ae1 100644 --- a/compiler/dex/portable/mir_to_gbc.cc +++ b/compiler/dex/portable/mir_to_gbc.cc @@ -1660,7 +1660,6 @@ bool MirConverter::BlockBitcodeConversion(BasicBlock* bb) { uint16_t arg_reg = cu_->num_regs; ::llvm::Function::arg_iterator arg_iter(func_->arg_begin()); - ::llvm::Function::arg_iterator arg_end(func_->arg_end()); const char* shorty = cu_->shorty; uint32_t shorty_size = strlen(shorty); diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc index 8e666dd2a80..43ed28c7627 100644 --- a/compiler/driver/compiler_driver.cc +++ b/compiler/driver/compiler_driver.cc @@ -1430,7 +1430,7 @@ class ParallelCompilationManager { private: ParallelCompilationManager* const manager_; const size_t end_; - const Callback* const callback_; + Callback* const callback_; }; AtomicInteger index_; diff --git a/compiler/image_writer.cc b/compiler/image_writer.cc index 90e2c65c898..02654ad55ae 100644 --- a/compiler/image_writer.cc +++ b/compiler/image_writer.cc @@ -630,10 +630,10 @@ void ImageWriter::FixupMethod(const ArtMethod* orig, ArtMethod* copy) { copy->SetEntryPointFromCompiledCode(GetOatAddress(quick_to_interpreter_bridge_offset_)); #endif copy->SetEntryPointFromInterpreter(reinterpret_cast - (GetOatAddress(interpreter_to_interpreter_bridge_offset_))); + (const_cast(GetOatAddress(interpreter_to_interpreter_bridge_offset_)))); } else { copy->SetEntryPointFromInterpreter(reinterpret_cast - (GetOatAddress(interpreter_to_compiled_code_bridge_offset_))); + (const_cast(GetOatAddress(interpreter_to_compiled_code_bridge_offset_)))); // Use original code if it exists. Otherwise, set the code pointer to the resolution // trampoline. const byte* code = GetOatAddress(orig->GetOatCodeOffset()); diff --git a/runtime/base/macros.h b/runtime/base/macros.h index 00a530a206f..cf7029a63fe 100644 --- a/runtime/base/macros.h +++ b/runtime/base/macros.h @@ -140,6 +140,13 @@ char (&ArraySizeHelper(T (&array)[N]))[N]; #define ALWAYS_INLINE __attribute__ ((always_inline)) #endif +#ifdef __clang__ +/* clang doesn't like attributes on lambda functions */ +#define ALWAYS_INLINE_LAMBDA +#else +#define ALWAYS_INLINE_LAMBDA ALWAYS_INLINE +#endif + #if defined (__APPLE__) #define HOT_ATTR #define COLD_ATTR diff --git a/runtime/entrypoints/entrypoint_utils.h b/runtime/entrypoints/entrypoint_utils.h index bfdbd74e580..a60446caba9 100644 --- a/runtime/entrypoints/entrypoint_utils.h +++ b/runtime/entrypoints/entrypoint_utils.h @@ -262,9 +262,9 @@ static inline mirror::ArtField* FindFieldFromCode(uint32_t field_idx, const mirr // Explicit template declarations of FindFieldFromCode for all field access types. #define EXPLICIT_FIND_FIELD_FROM_CODE_TEMPLATE_DECL(_type, _access_check) \ template SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) ALWAYS_INLINE \ -static mirror::ArtField* FindFieldFromCode<_type, _access_check>(uint32_t field_idx, \ - const mirror::ArtMethod* referrer, \ - Thread* self, size_t expected_size) \ +mirror::ArtField* FindFieldFromCode<_type, _access_check>(uint32_t field_idx, \ + const mirror::ArtMethod* referrer, \ + Thread* self, size_t expected_size) \ #define EXPLICIT_FIND_FIELD_FROM_CODE_TYPED_TEMPLATE_DECL(_type) \ EXPLICIT_FIND_FIELD_FROM_CODE_TEMPLATE_DECL(_type, false); \ @@ -393,12 +393,12 @@ static inline mirror::ArtMethod* FindMethodFromCode(uint32_t method_idx, mirror: } // Explicit template declarations of FindMethodFromCode for all invoke types. -#define EXPLICIT_FIND_METHOD_FROM_CODE_TEMPLATE_DECL(_type, _access_check) \ - template SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) ALWAYS_INLINE \ - static mirror::ArtMethod* FindMethodFromCode<_type, _access_check>(uint32_t method_idx, \ - mirror::Object* this_object, \ - mirror::ArtMethod* referrer, \ - Thread* self) +#define EXPLICIT_FIND_METHOD_FROM_CODE_TEMPLATE_DECL(_type, _access_check) \ + template SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) ALWAYS_INLINE \ + mirror::ArtMethod* FindMethodFromCode<_type, _access_check>(uint32_t method_idx, \ + mirror::Object* this_object, \ + mirror::ArtMethod* referrer, \ + Thread* self) #define EXPLICIT_FIND_METHOD_FROM_CODE_TYPED_TEMPLATE_DECL(_type) \ EXPLICIT_FIND_METHOD_FROM_CODE_TEMPLATE_DECL(_type, false); \ EXPLICIT_FIND_METHOD_FROM_CODE_TEMPLATE_DECL(_type, true) diff --git a/runtime/entrypoints/portable/portable_invoke_entrypoints.cc b/runtime/entrypoints/portable/portable_invoke_entrypoints.cc index e2a0cc20081..47ccbb126ed 100644 --- a/runtime/entrypoints/portable/portable_invoke_entrypoints.cc +++ b/runtime/entrypoints/portable/portable_invoke_entrypoints.cc @@ -22,8 +22,8 @@ namespace art { template -static mirror::ArtMethod* FindMethodHelper(uint32_t method_idx, mirror::Object* this_object, - mirror::ArtMethod* caller_method, Thread* thread) { +mirror::ArtMethod* FindMethodHelper(uint32_t method_idx, mirror::Object* this_object, + mirror::ArtMethod* caller_method, Thread* thread) { mirror::ArtMethod* method = FindMethodFast(method_idx, this_object, caller_method, access_check, type); if (UNLIKELY(method == NULL)) { @@ -46,12 +46,12 @@ static mirror::ArtMethod* FindMethodHelper(uint32_t method_idx, mirror::Object* } // Explicit template declarations of FindMethodHelper for all invoke types. -#define EXPLICIT_FIND_METHOD_HELPER_TEMPLATE_DECL(_type, _access_check) \ - template SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) \ - static mirror::ArtMethod* FindMethodHelper<_type, _access_check>(uint32_t method_idx, \ - mirror::Object* this_object, \ - mirror::ArtMethod* caller_method, \ - Thread* thread) +#define EXPLICIT_FIND_METHOD_HELPER_TEMPLATE_DECL(_type, _access_check) \ + template SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) \ + mirror::ArtMethod* FindMethodHelper<_type, _access_check>(uint32_t method_idx, \ + mirror::Object* this_object, \ + mirror::ArtMethod* caller_method, \ + Thread* thread) #define EXPLICIT_FIND_METHOD_HELPER_TYPED_TEMPLATE_DECL(_type) \ EXPLICIT_FIND_METHOD_HELPER_TEMPLATE_DECL(_type, false); \ EXPLICIT_FIND_METHOD_HELPER_TEMPLATE_DECL(_type, true) diff --git a/runtime/entrypoints/quick/quick_invoke_entrypoints.cc b/runtime/entrypoints/quick/quick_invoke_entrypoints.cc index b852a3292ef..5a1b3e84ade 100644 --- a/runtime/entrypoints/quick/quick_invoke_entrypoints.cc +++ b/runtime/entrypoints/quick/quick_invoke_entrypoints.cc @@ -142,9 +142,9 @@ extern "C" uint64_t artInvokeInterfaceTrampoline(mirror::ArtMethod* interface_me } template -static uint64_t artInvokeCommon(uint32_t method_idx, mirror::Object* this_object, - mirror::ArtMethod* caller_method, - Thread* self, mirror::ArtMethod** sp) { +uint64_t artInvokeCommon(uint32_t method_idx, mirror::Object* this_object, + mirror::ArtMethod* caller_method, + Thread* self, mirror::ArtMethod** sp) { mirror::ArtMethod* method = FindMethodFast(method_idx, this_object, caller_method, access_check, type); if (UNLIKELY(method == NULL)) { @@ -174,12 +174,12 @@ static uint64_t artInvokeCommon(uint32_t method_idx, mirror::Object* this_object } // Explicit template declarations of artInvokeCommon for all invoke types. -#define EXPLICIT_ART_INVOKE_COMMON_TEMPLATE_DECL(_type, _access_check) \ - template SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) \ - static uint64_t artInvokeCommon<_type, _access_check>(uint32_t method_idx, \ - mirror::Object* this_object, \ - mirror::ArtMethod* caller_method, \ - Thread* self, mirror::ArtMethod** sp) +#define EXPLICIT_ART_INVOKE_COMMON_TEMPLATE_DECL(_type, _access_check) \ + template SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) \ + uint64_t artInvokeCommon<_type, _access_check>(uint32_t method_idx, \ + mirror::Object* this_object, \ + mirror::ArtMethod* caller_method, \ + Thread* self, mirror::ArtMethod** sp) #define EXPLICIT_ART_INVOKE_COMMON_TYPED_TEMPLATE_DECL(_type) \ EXPLICIT_ART_INVOKE_COMMON_TEMPLATE_DECL(_type, false); \ diff --git a/runtime/gc/collector/mark_sweep.cc b/runtime/gc/collector/mark_sweep.cc index 0697a6515b9..28cc5108069 100644 --- a/runtime/gc/collector/mark_sweep.cc +++ b/runtime/gc/collector/mark_sweep.cc @@ -669,7 +669,7 @@ class MarkStackTask : public Task { MarkSweep* mark_sweep = chunk_task_->mark_sweep_; mark_sweep->ScanObjectVisit(obj, [mark_sweep, this](Object* /* obj */, Object* ref, const MemberOffset& /* offset */, - bool /* is_static */) ALWAYS_INLINE { + bool /* is_static */) ALWAYS_INLINE_LAMBDA { if (ref != nullptr && mark_sweep->MarkObjectParallel(ref)) { if (kUseFinger) { android_memory_barrier(); diff --git a/runtime/gc/collector/semi_space.cc b/runtime/gc/collector/semi_space.cc index b75b493d491..923560e2605 100644 --- a/runtime/gc/collector/semi_space.cc +++ b/runtime/gc/collector/semi_space.cc @@ -558,7 +558,7 @@ void SemiSpace::ScanObject(Object* obj) { DCHECK(obj != NULL); DCHECK(!from_space_->HasAddress(obj)) << "Scanning object " << obj << " in from space"; MarkSweep::VisitObjectReferences(obj, [this](Object* obj, Object* ref, const MemberOffset& offset, - bool /* is_static */) ALWAYS_INLINE NO_THREAD_SAFETY_ANALYSIS { + bool /* is_static */) ALWAYS_INLINE_LAMBDA NO_THREAD_SAFETY_ANALYSIS { mirror::Object* new_address = MarkObject(ref); if (new_address != ref) { DCHECK(new_address != nullptr); diff --git a/runtime/interpreter/interpreter.cc b/runtime/interpreter/interpreter.cc index 02c90123fce..f574a0f2cff 100644 --- a/runtime/interpreter/interpreter.cc +++ b/runtime/interpreter/interpreter.cc @@ -90,8 +90,8 @@ static void InterpreterJni(Thread* self, ArtMethod* method, const StringPiece& s ScopedObjectAccessUnchecked soa(self); if (method->IsStatic()) { if (shorty == "L") { - typedef jobject (fnptr)(JNIEnv*, jclass); - const fnptr* fn = reinterpret_cast(method->GetNativeMethod()); + typedef jobject (fntype)(JNIEnv*, jclass); + fntype* const fn = reinterpret_cast(const_cast(method->GetNativeMethod())); ScopedLocalRef klass(soa.Env(), soa.AddLocalReference(method->GetDeclaringClass())); jobject jresult; @@ -101,36 +101,36 @@ static void InterpreterJni(Thread* self, ArtMethod* method, const StringPiece& s } result->SetL(soa.Decode(jresult)); } else if (shorty == "V") { - typedef void (fnptr)(JNIEnv*, jclass); - const fnptr* fn = reinterpret_cast(method->GetNativeMethod()); + typedef void (fntype)(JNIEnv*, jclass); + fntype* const fn = reinterpret_cast(const_cast(method->GetNativeMethod())); ScopedLocalRef klass(soa.Env(), soa.AddLocalReference(method->GetDeclaringClass())); ScopedThreadStateChange tsc(self, kNative); fn(soa.Env(), klass.get()); } else if (shorty == "Z") { - typedef jboolean (fnptr)(JNIEnv*, jclass); - const fnptr* fn = reinterpret_cast(method->GetNativeMethod()); + typedef jboolean (fntype)(JNIEnv*, jclass); + fntype* const fn = reinterpret_cast(const_cast(method->GetNativeMethod())); ScopedLocalRef klass(soa.Env(), soa.AddLocalReference(method->GetDeclaringClass())); ScopedThreadStateChange tsc(self, kNative); result->SetZ(fn(soa.Env(), klass.get())); } else if (shorty == "BI") { - typedef jbyte (fnptr)(JNIEnv*, jclass, jint); - const fnptr* fn = reinterpret_cast(method->GetNativeMethod()); + typedef jbyte (fntype)(JNIEnv*, jclass, jint); + fntype* const fn = reinterpret_cast(const_cast(method->GetNativeMethod())); ScopedLocalRef klass(soa.Env(), soa.AddLocalReference(method->GetDeclaringClass())); ScopedThreadStateChange tsc(self, kNative); result->SetB(fn(soa.Env(), klass.get(), args[0])); } else if (shorty == "II") { - typedef jint (fnptr)(JNIEnv*, jclass, jint); - const fnptr* fn = reinterpret_cast(method->GetNativeMethod()); + typedef jint (fntype)(JNIEnv*, jclass, jint); + fntype* const fn = reinterpret_cast(const_cast(method->GetNativeMethod())); ScopedLocalRef klass(soa.Env(), soa.AddLocalReference(method->GetDeclaringClass())); ScopedThreadStateChange tsc(self, kNative); result->SetI(fn(soa.Env(), klass.get(), args[0])); } else if (shorty == "LL") { - typedef jobject (fnptr)(JNIEnv*, jclass, jobject); - const fnptr* fn = reinterpret_cast(method->GetNativeMethod()); + typedef jobject (fntype)(JNIEnv*, jclass, jobject); + fntype* const fn = reinterpret_cast(const_cast(method->GetNativeMethod())); ScopedLocalRef klass(soa.Env(), soa.AddLocalReference(method->GetDeclaringClass())); ScopedLocalRef arg0(soa.Env(), @@ -142,15 +142,15 @@ static void InterpreterJni(Thread* self, ArtMethod* method, const StringPiece& s } result->SetL(soa.Decode(jresult)); } else if (shorty == "IIZ") { - typedef jint (fnptr)(JNIEnv*, jclass, jint, jboolean); - const fnptr* fn = reinterpret_cast(method->GetNativeMethod()); + typedef jint (fntype)(JNIEnv*, jclass, jint, jboolean); + fntype* const fn = reinterpret_cast(const_cast(method->GetNativeMethod())); ScopedLocalRef klass(soa.Env(), soa.AddLocalReference(method->GetDeclaringClass())); ScopedThreadStateChange tsc(self, kNative); result->SetI(fn(soa.Env(), klass.get(), args[0], args[1])); } else if (shorty == "ILI") { - typedef jint (fnptr)(JNIEnv*, jclass, jobject, jint); - const fnptr* fn = reinterpret_cast(method->GetNativeMethod()); + typedef jint (fntype)(JNIEnv*, jclass, jobject, jint); + fntype* const fn = reinterpret_cast(const_cast(method->GetNativeMethod())); ScopedLocalRef klass(soa.Env(), soa.AddLocalReference(method->GetDeclaringClass())); ScopedLocalRef arg0(soa.Env(), @@ -158,22 +158,22 @@ static void InterpreterJni(Thread* self, ArtMethod* method, const StringPiece& s ScopedThreadStateChange tsc(self, kNative); result->SetI(fn(soa.Env(), klass.get(), arg0.get(), args[1])); } else if (shorty == "SIZ") { - typedef jshort (fnptr)(JNIEnv*, jclass, jint, jboolean); - const fnptr* fn = reinterpret_cast(method->GetNativeMethod()); + typedef jshort (fntype)(JNIEnv*, jclass, jint, jboolean); + fntype* const fn = reinterpret_cast(const_cast(method->GetNativeMethod())); ScopedLocalRef klass(soa.Env(), soa.AddLocalReference(method->GetDeclaringClass())); ScopedThreadStateChange tsc(self, kNative); result->SetS(fn(soa.Env(), klass.get(), args[0], args[1])); } else if (shorty == "VIZ") { - typedef void (fnptr)(JNIEnv*, jclass, jint, jboolean); - const fnptr* fn = reinterpret_cast(method->GetNativeMethod()); + typedef void (fntype)(JNIEnv*, jclass, jint, jboolean); + fntype* const fn = reinterpret_cast(const_cast(method->GetNativeMethod())); ScopedLocalRef klass(soa.Env(), soa.AddLocalReference(method->GetDeclaringClass())); ScopedThreadStateChange tsc(self, kNative); fn(soa.Env(), klass.get(), args[0], args[1]); } else if (shorty == "ZLL") { - typedef jboolean (fnptr)(JNIEnv*, jclass, jobject, jobject); - const fnptr* fn = reinterpret_cast(method->GetNativeMethod()); + typedef jboolean (fntype)(JNIEnv*, jclass, jobject, jobject); + fntype* const fn = reinterpret_cast(const_cast(method->GetNativeMethod())); ScopedLocalRef klass(soa.Env(), soa.AddLocalReference(method->GetDeclaringClass())); ScopedLocalRef arg0(soa.Env(), @@ -183,8 +183,8 @@ static void InterpreterJni(Thread* self, ArtMethod* method, const StringPiece& s ScopedThreadStateChange tsc(self, kNative); result->SetZ(fn(soa.Env(), klass.get(), arg0.get(), arg1.get())); } else if (shorty == "ZILL") { - typedef jboolean (fnptr)(JNIEnv*, jclass, jint, jobject, jobject); - const fnptr* fn = reinterpret_cast(method->GetNativeMethod()); + typedef jboolean (fntype)(JNIEnv*, jclass, jint, jobject, jobject); + fntype* const fn = reinterpret_cast(const_cast(method->GetNativeMethod())); ScopedLocalRef klass(soa.Env(), soa.AddLocalReference(method->GetDeclaringClass())); ScopedLocalRef arg1(soa.Env(), @@ -194,8 +194,8 @@ static void InterpreterJni(Thread* self, ArtMethod* method, const StringPiece& s ScopedThreadStateChange tsc(self, kNative); result->SetZ(fn(soa.Env(), klass.get(), args[0], arg1.get(), arg2.get())); } else if (shorty == "VILII") { - typedef void (fnptr)(JNIEnv*, jclass, jint, jobject, jint, jint); - const fnptr* fn = reinterpret_cast(method->GetNativeMethod()); + typedef void (fntype)(JNIEnv*, jclass, jint, jobject, jint, jint); + fntype* const fn = reinterpret_cast(const_cast(method->GetNativeMethod())); ScopedLocalRef klass(soa.Env(), soa.AddLocalReference(method->GetDeclaringClass())); ScopedLocalRef arg1(soa.Env(), @@ -203,8 +203,8 @@ static void InterpreterJni(Thread* self, ArtMethod* method, const StringPiece& s ScopedThreadStateChange tsc(self, kNative); fn(soa.Env(), klass.get(), args[0], arg1.get(), args[2], args[3]); } else if (shorty == "VLILII") { - typedef void (fnptr)(JNIEnv*, jclass, jobject, jint, jobject, jint, jint); - const fnptr* fn = reinterpret_cast(method->GetNativeMethod()); + typedef void (fntype)(JNIEnv*, jclass, jobject, jint, jobject, jint, jint); + fntype* const fn = reinterpret_cast(const_cast(method->GetNativeMethod())); ScopedLocalRef klass(soa.Env(), soa.AddLocalReference(method->GetDeclaringClass())); ScopedLocalRef arg0(soa.Env(), @@ -219,8 +219,8 @@ static void InterpreterJni(Thread* self, ArtMethod* method, const StringPiece& s } } else { if (shorty == "L") { - typedef jobject (fnptr)(JNIEnv*, jobject); - const fnptr* fn = reinterpret_cast(method->GetNativeMethod()); + typedef jobject (fntype)(JNIEnv*, jobject); + fntype* const fn = reinterpret_cast(const_cast(method->GetNativeMethod())); ScopedLocalRef rcvr(soa.Env(), soa.AddLocalReference(receiver)); jobject jresult; @@ -230,15 +230,15 @@ static void InterpreterJni(Thread* self, ArtMethod* method, const StringPiece& s } result->SetL(soa.Decode(jresult)); } else if (shorty == "V") { - typedef void (fnptr)(JNIEnv*, jobject); - const fnptr* fn = reinterpret_cast(method->GetNativeMethod()); + typedef void (fntype)(JNIEnv*, jobject); + fntype* const fn = reinterpret_cast(const_cast(method->GetNativeMethod())); ScopedLocalRef rcvr(soa.Env(), soa.AddLocalReference(receiver)); ScopedThreadStateChange tsc(self, kNative); fn(soa.Env(), rcvr.get()); } else if (shorty == "LL") { - typedef jobject (fnptr)(JNIEnv*, jobject, jobject); - const fnptr* fn = reinterpret_cast(method->GetNativeMethod()); + typedef jobject (fntype)(JNIEnv*, jobject, jobject); + fntype* const fn = reinterpret_cast(const_cast(method->GetNativeMethod())); ScopedLocalRef rcvr(soa.Env(), soa.AddLocalReference(receiver)); ScopedLocalRef arg0(soa.Env(), @@ -251,8 +251,8 @@ static void InterpreterJni(Thread* self, ArtMethod* method, const StringPiece& s result->SetL(soa.Decode(jresult)); ScopedThreadStateChange tsc(self, kNative); } else if (shorty == "III") { - typedef jint (fnptr)(JNIEnv*, jobject, jint, jint); - const fnptr* fn = reinterpret_cast(method->GetNativeMethod()); + typedef jint (fntype)(JNIEnv*, jobject, jint, jint); + fntype* const fn = reinterpret_cast(const_cast(method->GetNativeMethod())); ScopedLocalRef rcvr(soa.Env(), soa.AddLocalReference(receiver)); ScopedThreadStateChange tsc(self, kNative); diff --git a/runtime/interpreter/interpreter_common.h b/runtime/interpreter/interpreter_common.h index 3b8d50bc251..a9b8909b2b8 100644 --- a/runtime/interpreter/interpreter_common.h +++ b/runtime/interpreter/interpreter_common.h @@ -557,11 +557,11 @@ static inline bool IsBackwardBranch(int32_t branch_offset) { } // Explicitly instantiate all DoInvoke functions. -#define EXPLICIT_DO_INVOKE_TEMPLATE_DECL(_type, _is_range, _do_check) \ - template SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) ALWAYS_INLINE \ - static bool DoInvoke<_type, _is_range, _do_check>(Thread* self, ShadowFrame& shadow_frame, \ - const Instruction* inst, uint16_t inst_data, \ - JValue* result) +#define EXPLICIT_DO_INVOKE_TEMPLATE_DECL(_type, _is_range, _do_check) \ + template SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) ALWAYS_INLINE \ + bool DoInvoke<_type, _is_range, _do_check>(Thread* self, ShadowFrame& shadow_frame, \ + const Instruction* inst, uint16_t inst_data, \ + JValue* result) #define EXPLICIT_DO_INVOKE_ALL_TEMPLATE_DECL(_type) \ EXPLICIT_DO_INVOKE_TEMPLATE_DECL(_type, false, false); \ @@ -578,10 +578,10 @@ EXPLICIT_DO_INVOKE_ALL_TEMPLATE_DECL(kInterface); // invoke-interface/range. #undef EXPLICIT_DO_INVOKE_TEMPLATE_DECL // Explicitly instantiate all DoFieldGet functions. -#define EXPLICIT_DO_FIELD_GET_TEMPLATE_DECL(_find_type, _field_type, _do_check) \ - template SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) ALWAYS_INLINE \ - static bool DoFieldGet<_find_type, _field_type, _do_check>(Thread* self, ShadowFrame& shadow_frame, \ - const Instruction* inst, uint16_t inst_data) +#define EXPLICIT_DO_FIELD_GET_TEMPLATE_DECL(_find_type, _field_type, _do_check) \ + template SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) ALWAYS_INLINE \ + bool DoFieldGet<_find_type, _field_type, _do_check>(Thread* self, ShadowFrame& shadow_frame, \ + const Instruction* inst, uint16_t inst_data) #define EXPLICIT_DO_FIELD_GET_ALL_TEMPLATE_DECL(_find_type, _field_type) \ EXPLICIT_DO_FIELD_GET_TEMPLATE_DECL(_find_type, _field_type, false); \ @@ -609,10 +609,10 @@ EXPLICIT_DO_FIELD_GET_ALL_TEMPLATE_DECL(StaticObjectRead, Primitive::kPrimNot); #undef EXPLICIT_DO_FIELD_GET_TEMPLATE_DECL // Explicitly instantiate all DoFieldPut functions. -#define EXPLICIT_DO_FIELD_PUT_TEMPLATE_DECL(_find_type, _field_type, _do_check) \ - template SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) ALWAYS_INLINE \ - static bool DoFieldPut<_find_type, _field_type, _do_check>(Thread* self, const ShadowFrame& shadow_frame, \ - const Instruction* inst, uint16_t inst_data) +#define EXPLICIT_DO_FIELD_PUT_TEMPLATE_DECL(_find_type, _field_type, _do_check) \ + template SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) ALWAYS_INLINE \ + bool DoFieldPut<_find_type, _field_type, _do_check>(Thread* self, const ShadowFrame& shadow_frame, \ + const Instruction* inst, uint16_t inst_data) #define EXPLICIT_DO_FIELD_PUT_ALL_TEMPLATE_DECL(_find_type, _field_type) \ EXPLICIT_DO_FIELD_PUT_TEMPLATE_DECL(_find_type, _field_type, false); \ @@ -640,21 +640,21 @@ EXPLICIT_DO_FIELD_PUT_ALL_TEMPLATE_DECL(StaticObjectWrite, Primitive::kPrimNot); #undef EXPLICIT_DO_FIELD_PUT_TEMPLATE_DECL // Explicitly instantiate all DoInvokeVirtualQuick functions. -#define EXPLICIT_DO_INVOKE_VIRTUAL_QUICK_TEMPLATE_DECL(_is_range) \ - template SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) ALWAYS_INLINE \ - static bool DoInvokeVirtualQuick<_is_range>(Thread* self, ShadowFrame& shadow_frame, \ - const Instruction* inst, uint16_t inst_data, \ - JValue* result) +#define EXPLICIT_DO_INVOKE_VIRTUAL_QUICK_TEMPLATE_DECL(_is_range) \ + template SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) ALWAYS_INLINE \ + bool DoInvokeVirtualQuick<_is_range>(Thread* self, ShadowFrame& shadow_frame, \ + const Instruction* inst, uint16_t inst_data, \ + JValue* result) EXPLICIT_DO_INVOKE_VIRTUAL_QUICK_TEMPLATE_DECL(false); // invoke-virtual-quick. EXPLICIT_DO_INVOKE_VIRTUAL_QUICK_TEMPLATE_DECL(true); // invoke-virtual-quick-range. #undef EXPLICIT_INSTANTIATION_DO_INVOKE_VIRTUAL_QUICK // Explicitly instantiate all DoIGetQuick functions. -#define EXPLICIT_DO_IGET_QUICK_TEMPLATE_DECL(_field_type) \ - template SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) ALWAYS_INLINE \ - static bool DoIGetQuick<_field_type>(ShadowFrame& shadow_frame, const Instruction* inst, \ - uint16_t inst_data) +#define EXPLICIT_DO_IGET_QUICK_TEMPLATE_DECL(_field_type) \ + template SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) ALWAYS_INLINE \ + bool DoIGetQuick<_field_type>(ShadowFrame& shadow_frame, const Instruction* inst, \ + uint16_t inst_data) EXPLICIT_DO_IGET_QUICK_TEMPLATE_DECL(Primitive::kPrimInt); // iget-quick. EXPLICIT_DO_IGET_QUICK_TEMPLATE_DECL(Primitive::kPrimLong); // iget-wide-quick. @@ -662,10 +662,10 @@ EXPLICIT_DO_IGET_QUICK_TEMPLATE_DECL(Primitive::kPrimNot); // iget-object-qui #undef EXPLICIT_DO_IGET_QUICK_TEMPLATE_DECL // Explicitly instantiate all DoIPutQuick functions. -#define EXPLICIT_DO_IPUT_QUICK_TEMPLATE_DECL(_field_type) \ - template SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) ALWAYS_INLINE \ - static bool DoIPutQuick<_field_type>(const ShadowFrame& shadow_frame, const Instruction* inst, \ - uint16_t inst_data) +#define EXPLICIT_DO_IPUT_QUICK_TEMPLATE_DECL(_field_type) \ + template SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) ALWAYS_INLINE \ + bool DoIPutQuick<_field_type>(const ShadowFrame& shadow_frame, const Instruction* inst, \ + uint16_t inst_data) EXPLICIT_DO_IPUT_QUICK_TEMPLATE_DECL(Primitive::kPrimInt); // iget-quick. EXPLICIT_DO_IPUT_QUICK_TEMPLATE_DECL(Primitive::kPrimLong); // iget-wide-quick. From d19b55a05b52b7f7da9f894eba63ed03e2a62283 Mon Sep 17 00:00:00 2001 From: Mark Mendell Date: Thu, 12 Dec 2013 09:55:34 -0800 Subject: [PATCH 0277/2402] Disassemble more x86 instructions By using oatdump on the core.oat, I found a couple more instructions that didn't disassemble properly. These included another form of imul and some FP instructions used by the JNI code. Now the only unknown opcodes I could find seem to be literal data at the end of the method. Change-Id: Icea1da1c7d1f9dce99e6b6517cfca34b47d6827a Signed-off-by: Mark Mendell --- disassembler/disassembler_x86.cc | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/disassembler/disassembler_x86.cc b/disassembler/disassembler_x86.cc index 4a08ce054f8..1d53ca8123e 100644 --- a/disassembler/disassembler_x86.cc +++ b/disassembler/disassembler_x86.cc @@ -208,7 +208,9 @@ DISASSEMBLER_ENTRY(cmp, reg_in_opcode = true; break; case 0x68: opcode << "push"; immediate_bytes = 4; break; + case 0x69: opcode << "imul"; load = true; has_modrm = true; immediate_bytes = 4; break; case 0x6A: opcode << "push"; immediate_bytes = 1; break; + case 0x6B: opcode << "imul"; load = true; has_modrm = true; immediate_bytes = 1; break; case 0x70: case 0x71: case 0x72: case 0x73: case 0x74: case 0x75: case 0x76: case 0x77: case 0x78: case 0x79: case 0x7A: case 0x7B: case 0x7C: case 0x7D: case 0x7E: case 0x7F: static const char* condition_codes[] = @@ -595,6 +597,20 @@ DISASSEMBLER_ENTRY(cmp, reg_is_opcode = true; break; case 0xCC: opcode << "int 3"; break; + case 0xD9: + static const char* d9_opcodes[] = {"flds", "unknown-d9", "fsts", "fstps", "fldenv", "fldcw", "fnstenv", "fnstcw"}; + modrm_opcodes = d9_opcodes; + store = true; + has_modrm = true; + reg_is_opcode = true; + break; + case 0xDD: + static const char* dd_opcodes[] = {"fldl", "fisttp", "fstl", "fstpl", "frstor", "unknown-dd", "fnsave", "fnstsw"}; + modrm_opcodes = dd_opcodes; + store = true; + has_modrm = true; + reg_is_opcode = true; + break; case 0xE8: opcode << "call"; branch_bytes = 4; break; case 0xE9: opcode << "jmp"; branch_bytes = 4; break; case 0xEB: opcode << "jmp"; branch_bytes = 1; break; From 22cb09b35e8eb30c016065f0eec4b3b96666de43 Mon Sep 17 00:00:00 2001 From: Jeff Hao Date: Thu, 12 Dec 2013 14:29:15 -0800 Subject: [PATCH 0278/2402] Add class to verifier's rejected list if superclass is erroneous. This will prevent the compiler from trying to compile one of these classes, which will fail because it has no GC map. Bug: 12104117 Change-Id: I77ec77d30ee5dc92d7f4c594f1e8f1ce9b67855d --- runtime/class_linker.cc | 2 ++ runtime/verifier/method_verifier.h | 5 ++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index a98673d0aff..643c183d1f1 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -2489,6 +2489,8 @@ void ClassLinker::VerifyClass(const SirtRef& klass) { if (cause.get() != nullptr) { self->GetException(nullptr)->SetCause(cause.get()); } + ClassReference ref(klass->GetDexCache()->GetDexFile(), klass->GetDexClassDefIndex()); + verifier::MethodVerifier::AddRejectedClass(ref); klass->SetStatus(mirror::Class::kStatusError, self); return; } diff --git a/runtime/verifier/method_verifier.h b/runtime/verifier/method_verifier.h index 6b5747b2f16..dffda966aaa 100644 --- a/runtime/verifier/method_verifier.h +++ b/runtime/verifier/method_verifier.h @@ -212,6 +212,8 @@ class MethodVerifier { static void Init() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); static void Shutdown(); + static void AddRejectedClass(ClassReference ref) + LOCKS_EXCLUDED(rejected_classes_lock_); static bool IsClassRejected(ClassReference ref) LOCKS_EXCLUDED(rejected_classes_lock_); @@ -662,9 +664,6 @@ class MethodVerifier { static ReaderWriterMutex* rejected_classes_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER; static RejectedClassesTable* rejected_classes_ GUARDED_BY(rejected_classes_lock_); - static void AddRejectedClass(ClassReference ref) - LOCKS_EXCLUDED(rejected_classes_lock_); - RegTypeCache reg_types_; PcToRegisterLineTable reg_table_; From 501baec5f2de2156bb5ed7f66d23f1b1ad026267 Mon Sep 17 00:00:00 2001 From: Sebastien Hertz Date: Fri, 13 Dec 2013 12:02:36 +0100 Subject: [PATCH 0279/2402] Cleanup thread pool tasks creation. Removes unused vector when adding new ForAllClosure tasks to the thread pool. Change-Id: Ie0c8067bc5cbfac45db04a6242bc98f89c3efc8d --- compiler/driver/compiler_driver.cc | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc index 7b428793ab1..87569d5b2a8 100644 --- a/compiler/driver/compiler_driver.cc +++ b/compiler/driver/compiler_driver.cc @@ -1384,11 +1384,9 @@ class ParallelCompilationManager { self->AssertNoPendingException(); CHECK_GT(work_units, 0U); - std::vector closures(work_units); index_ = begin; for (size_t i = 0; i < work_units; ++i) { - closures[i] = new ForAllClosure(this, end, callback); - thread_pool_->AddTask(self, closures[i]); + thread_pool_->AddTask(self, new ForAllClosure(this, end, callback)); } thread_pool_->StartWorkers(self); From a6fd8ba27bc84dfb942a8fa4ea987bcb39f0f3f1 Mon Sep 17 00:00:00 2001 From: Vladimir Marko Date: Fri, 13 Dec 2013 10:53:49 +0000 Subject: [PATCH 0280/2402] Fix 64-bit CAS for x86. Bug: 12117970 Change-Id: I9fbba2291124a2594161782c89dc62201cf01c08 --- compiler/dex/quick/x86/assemble_x86.cc | 6 +++--- compiler/dex/quick/x86/int_x86.cc | 9 +++++---- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/compiler/dex/quick/x86/assemble_x86.cc b/compiler/dex/quick/x86/assemble_x86.cc index 96dc6ee7d16..c205a01ea2a 100644 --- a/compiler/dex/quick/x86/assemble_x86.cc +++ b/compiler/dex/quick/x86/assemble_x86.cc @@ -246,9 +246,9 @@ ENCODING_MAP(Cmp, IS_LOAD, 0, 0, UNARY_ENCODING_MAP(Idivmod, 0x7, 0, SETS_CCODES, DaR, kRegRegReg, IS_UNARY_OP | REG_USE0, DaM, kRegRegMem, IS_BINARY_OP | REG_USE0, DaA, kRegRegArray, IS_QUAD_OP | REG_USE01, 0, REG_DEFA_USEA, REG_DEFAD_USEAD, REG_DEFAD_USEAD, "ah:al,ax,", "dx:ax,dx:ax,", "edx:eax,edx:eax,"), #undef UNARY_ENCODING_MAP - { kX86Bswap32R, kRegOpcode, IS_UNARY_OP | REG_DEF0_USE0, { 0, 0, 0x0F, 0xC8, 0, 0, 0, 0 }, "Bswap32R", "!0r" }, - { kX86Push32R, kRegOpcode, IS_UNARY_OP | REG_USE0 | IS_STORE, { 0, 0, 0x50, 0, 0, 0, 0, 0 }, "Push32R", "!0r" }, - { kX86Pop32R, kRegOpcode, IS_UNARY_OP | REG_DEF0 | IS_LOAD, { 0, 0, 0x58, 0, 0, 0, 0, 0 }, "Pop32R", "!0r" }, + { kX86Bswap32R, kRegOpcode, IS_UNARY_OP | REG_DEF0_USE0, { 0, 0, 0x0F, 0xC8, 0, 0, 0, 0 }, "Bswap32R", "!0r" }, + { kX86Push32R, kRegOpcode, IS_UNARY_OP | REG_USE0 | REG_USE_SP | REG_DEF_SP | IS_STORE, { 0, 0, 0x50, 0, 0, 0, 0, 0 }, "Push32R", "!0r" }, + { kX86Pop32R, kRegOpcode, IS_UNARY_OP | REG_DEF0 | REG_USE_SP | REG_DEF_SP | IS_LOAD, { 0, 0, 0x58, 0, 0, 0, 0, 0 }, "Pop32R", "!0r" }, #define EXT_0F_ENCODING_MAP(opname, prefix, opcode, reg_def) \ { kX86 ## opname ## RR, kRegReg, IS_BINARY_OP | reg_def | REG_USE01, { prefix, 0, 0x0F, opcode, 0, 0, 0, 0 }, #opname "RR", "!0r,!1r" }, \ diff --git a/compiler/dex/quick/x86/int_x86.cc b/compiler/dex/quick/x86/int_x86.cc index 0133a0afaf5..340c74ae14e 100644 --- a/compiler/dex/quick/x86/int_x86.cc +++ b/compiler/dex/quick/x86/int_x86.cc @@ -295,16 +295,17 @@ bool X86Mir2Lir::GenInlinedCas(CallInfo* info, bool is_long, bool is_object) { if (is_long) { FlushAllRegs(); LockCallTemps(); + LoadValueDirectWideFixed(rl_src_expected, rAX, rDX); + LoadValueDirectWideFixed(rl_src_new_value, rBX, rCX); NewLIR1(kX86Push32R, rDI); MarkTemp(rDI); LockTemp(rDI); NewLIR1(kX86Push32R, rSI); MarkTemp(rSI); LockTemp(rSI); - LoadValueDirectFixed(rl_src_obj, rDI); - LoadValueDirectFixed(rl_src_offset, rSI); - LoadValueDirectWideFixed(rl_src_expected, rAX, rDX); - LoadValueDirectWideFixed(rl_src_new_value, rBX, rCX); + const int push_offset = 4 /* push edi */ + 4 /* push esi */; + LoadWordDisp(TargetReg(kSp), SRegOffset(rl_src_obj.s_reg_low) + push_offset, rDI); + LoadWordDisp(TargetReg(kSp), SRegOffset(rl_src_offset.s_reg_low) + push_offset, rSI); NewLIR4(kX86LockCmpxchg8bA, rDI, rSI, 0, 0); FreeTemp(rSI); UnmarkTemp(rSI); From e13717e796d338b08ea66f6a7e3470ca44de707f Mon Sep 17 00:00:00 2001 From: Vladimir Marko Date: Wed, 20 Nov 2013 12:44:55 +0000 Subject: [PATCH 0281/2402] Per-DexFile locking for inliner initialization. And clean up lock and compiler driver naming. Change-Id: I1562c7f55c4b0174a36007ba6199360da06169ff --- compiler/dex/quick/dex_file_method_inliner.cc | 24 ++++++++---- compiler/dex/quick/dex_file_method_inliner.h | 12 ++++-- .../quick/dex_file_to_method_inliner_map.cc | 37 ++++++++++++------- .../quick/dex_file_to_method_inliner_map.h | 2 +- compiler/dex/quick/gen_invoke.cc | 2 +- compiler/dex/quick/mir_to_lir.h | 2 +- compiler/driver/compiler_driver.cc | 4 +- runtime/locks.h | 2 + 8 files changed, 56 insertions(+), 29 deletions(-) diff --git a/compiler/dex/quick/dex_file_method_inliner.cc b/compiler/dex/quick/dex_file_method_inliner.cc index b21e37e13d8..ba98225024e 100644 --- a/compiler/dex/quick/dex_file_method_inliner.cc +++ b/compiler/dex/quick/dex_file_method_inliner.cc @@ -16,6 +16,10 @@ #include #include "base/macros.h" +#include "base/mutex.h" +#include "base/mutex-inl.h" +#include "thread.h" +#include "thread-inl.h" #include "dex/mir_graph.h" #include "dex_file_method_inliner.h" @@ -228,7 +232,8 @@ const DexFileMethodInliner::IntrinsicDef DexFileMethodInliner::kIntrinsicMethods }; DexFileMethodInliner::DexFileMethodInliner() - : dex_file_(NULL) { + : lock_("DexFileMethodInliner lock", kDexFileMethodInlinerLock), + dex_file_(NULL) { COMPILE_ASSERT(kClassCacheFirst == 0, kClassCacheFirst_not_0); COMPILE_ASSERT(arraysize(kClassCacheNames) == kClassCacheLast, bad_arraysize_kClassCacheNames); COMPILE_ASSERT(kNameCacheFirst == 0, kNameCacheFirst_not_0); @@ -240,16 +245,21 @@ DexFileMethodInliner::DexFileMethodInliner() DexFileMethodInliner::~DexFileMethodInliner() { } -bool DexFileMethodInliner::IsIntrinsic(uint32_t method_index) const { +bool DexFileMethodInliner::IsIntrinsic(uint32_t method_index) { + ReaderMutexLock mu(Thread::Current(), lock_); return intrinsics_.find(method_index) != intrinsics_.end(); } -bool DexFileMethodInliner::GenIntrinsic(Mir2Lir* backend, CallInfo* info) const { - auto it = intrinsics_.find(info->index); - if (it == intrinsics_.end()) { - return false; +bool DexFileMethodInliner::GenIntrinsic(Mir2Lir* backend, CallInfo* info) { + Intrinsic intrinsic; + { + ReaderMutexLock mu(Thread::Current(), lock_); + auto it = intrinsics_.find(info->index); + if (it == intrinsics_.end()) { + return false; + } + intrinsic = it->second; } - const Intrinsic& intrinsic = it->second; switch (intrinsic.opcode) { case kIntrinsicDoubleCvt: return backend->GenInlinedDoubleCvt(info); diff --git a/compiler/dex/quick/dex_file_method_inliner.h b/compiler/dex/quick/dex_file_method_inliner.h index 948f4bbe564..9198f2a29d7 100644 --- a/compiler/dex/quick/dex_file_method_inliner.h +++ b/compiler/dex/quick/dex_file_method_inliner.h @@ -19,6 +19,9 @@ #include #include +#include "base/mutex.h" +#include "base/macros.h" +#include "locks.h" namespace art { @@ -95,12 +98,12 @@ class DexFileMethodInliner { /** * Check whether a particular method index corresponds to an intrinsic function. */ - bool IsIntrinsic(uint32_t method_index) const; + bool IsIntrinsic(uint32_t method_index) LOCKS_EXCLUDED(lock_); /** * Generate code for an intrinsic function invocation. */ - bool GenIntrinsic(Mir2Lir* backend, CallInfo* info) const; + bool GenIntrinsic(Mir2Lir* backend, CallInfo* info) LOCKS_EXCLUDED(lock_); private: /** @@ -300,14 +303,15 @@ class DexFileMethodInliner { * * Only DexFileToMethodInlinerMap may call this function to initialize the inliner. */ - void FindIntrinsics(const DexFile* dex_file); + void FindIntrinsics(const DexFile* dex_file) EXCLUSIVE_LOCKS_REQUIRED(lock_); friend class DexFileToMethodInlinerMap; + ReaderWriterMutex lock_; /* * Maps method indexes (for the particular DexFile) to Intrinsic defintions. */ - std::map intrinsics_; + std::map intrinsics_ GUARDED_BY(lock_); const DexFile* dex_file_; DISALLOW_COPY_AND_ASSIGN(DexFileMethodInliner); diff --git a/compiler/dex/quick/dex_file_to_method_inliner_map.cc b/compiler/dex/quick/dex_file_to_method_inliner_map.cc index 0107ed3ccbd..2fec1832899 100644 --- a/compiler/dex/quick/dex_file_to_method_inliner_map.cc +++ b/compiler/dex/quick/dex_file_to_method_inliner_map.cc @@ -28,7 +28,7 @@ namespace art { DexFileToMethodInlinerMap::DexFileToMethodInlinerMap() - : lock_("inline_helper_mutex") { + : lock_("DexFileToMethodInlinerMap lock", kDexFileToMethodInlinerMapLock) { } DexFileToMethodInlinerMap::~DexFileToMethodInlinerMap() { @@ -37,26 +37,37 @@ DexFileToMethodInlinerMap::~DexFileToMethodInlinerMap() { } } -const DexFileMethodInliner& DexFileToMethodInlinerMap::GetMethodInliner(const DexFile* dex_file) { +DexFileMethodInliner* DexFileToMethodInlinerMap::GetMethodInliner(const DexFile* dex_file) { Thread* self = Thread::Current(); { - ReaderMutexLock lock(self, lock_); + ReaderMutexLock mu(self, lock_); auto it = inliners_.find(dex_file); if (it != inliners_.end()) { - return *it->second; + return it->second; } } - WriterMutexLock lock(self, lock_); - DexFileMethodInliner** inliner = &inliners_[dex_file]; // inserts new entry if not found - if (*inliner) { - return **inliner; + // We need to acquire our lock_ to modify inliners_ but we want to release it + // before we initialize the new inliner. However, we need to acquire the + // new inliner's lock_ before we release our lock_ to prevent another thread + // from using the uninitialized inliner. This requires explicit calls to + // ExclusiveLock()/ExclusiveUnlock() on one of the locks, the other one + // can use WriterMutexLock. + DexFileMethodInliner* locked_inliner; + { + WriterMutexLock mu(self, lock_); + DexFileMethodInliner** inliner = &inliners_[dex_file]; // inserts new entry if not found + if (*inliner) { + return *inliner; + } + *inliner = new DexFileMethodInliner; + DCHECK(*inliner != nullptr); + locked_inliner = *inliner; + locked_inliner->lock_.ExclusiveLock(self); // Acquire inliner's lock_ before releasing lock_. } - *inliner = new DexFileMethodInliner(); - DCHECK(*inliner != nullptr); - // TODO: per-dex file locking for the intrinsics container filling. - (*inliner)->FindIntrinsics(dex_file); - return **inliner; + locked_inliner->FindIntrinsics(dex_file); + locked_inliner->lock_.ExclusiveUnlock(self); + return locked_inliner; } } // namespace art diff --git a/compiler/dex/quick/dex_file_to_method_inliner_map.h b/compiler/dex/quick/dex_file_to_method_inliner_map.h index 476f002bb57..6d5b8893c52 100644 --- a/compiler/dex/quick/dex_file_to_method_inliner_map.h +++ b/compiler/dex/quick/dex_file_to_method_inliner_map.h @@ -40,7 +40,7 @@ class DexFileToMethodInlinerMap { DexFileToMethodInlinerMap(); ~DexFileToMethodInlinerMap(); - const DexFileMethodInliner& GetMethodInliner(const DexFile* dex_file) LOCKS_EXCLUDED(lock_); + DexFileMethodInliner* GetMethodInliner(const DexFile* dex_file) LOCKS_EXCLUDED(lock_); private: ReaderWriterMutex lock_; diff --git a/compiler/dex/quick/gen_invoke.cc b/compiler/dex/quick/gen_invoke.cc index e66d4ea52cb..ee6f9c8fe9e 100644 --- a/compiler/dex/quick/gen_invoke.cc +++ b/compiler/dex/quick/gen_invoke.cc @@ -1242,7 +1242,7 @@ void Mir2Lir::GenInvoke(CallInfo* info) { if (inliner_ == nullptr) { QuickCompilerContext* context = reinterpret_cast( cu_->compiler_driver->GetCompilerContext()); - inliner_ = &context->GetInlinerMap()->GetMethodInliner(cu_->dex_file); + inliner_ = context->GetInlinerMap()->GetMethodInliner(cu_->dex_file); } if (inliner_->GenIntrinsic(this, info)) { return; diff --git a/compiler/dex/quick/mir_to_lir.h b/compiler/dex/quick/mir_to_lir.h index 2a54eb32cc6..8415cbfb6d5 100644 --- a/compiler/dex/quick/mir_to_lir.h +++ b/compiler/dex/quick/mir_to_lir.h @@ -827,7 +827,7 @@ class Mir2Lir : public Backend { LIR* first_lir_insn_; LIR* last_lir_insn_; // Lazily retrieved method inliner for intrinsics. - const DexFileMethodInliner* inliner_; + DexFileMethodInliner* inliner_; }; // Class Mir2Lir } // namespace art diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc index 43ed28c7627..1879a94bff7 100644 --- a/compiler/driver/compiler_driver.cc +++ b/compiler/driver/compiler_driver.cc @@ -284,10 +284,10 @@ class AOTCompilationStats { }; extern "C" void ArtInitCompilerContext(art::CompilerDriver& driver); -extern "C" void ArtInitQuickCompilerContext(art::CompilerDriver& compiler); +extern "C" void ArtInitQuickCompilerContext(art::CompilerDriver& driver); extern "C" void ArtUnInitCompilerContext(art::CompilerDriver& driver); -extern "C" void ArtUnInitQuickCompilerContext(art::CompilerDriver& compiler); +extern "C" void ArtUnInitQuickCompilerContext(art::CompilerDriver& driver); extern "C" art::CompiledMethod* ArtCompileMethod(art::CompilerDriver& driver, const art::DexFile::CodeItem* code_item, diff --git a/runtime/locks.h b/runtime/locks.h index 2308e951b04..72d4f652ff9 100644 --- a/runtime/locks.h +++ b/runtime/locks.h @@ -41,6 +41,8 @@ enum LockLevel { kRosAllocBracketLock, kRosAllocBulkFreeLock, kAllocSpaceLock, + kDexFileMethodInlinerLock, + kDexFileToMethodInlinerMapLock, kMarkSweepMarkStackLock, kDefaultMutexLevel, kMarkSweepLargeObjectLock, From 96f36674d3cb1e0a589b71f386a4bea5e658fc75 Mon Sep 17 00:00:00 2001 From: Sebastien Hertz Date: Fri, 13 Dec 2013 15:19:18 +0100 Subject: [PATCH 0282/2402] Add verification stress test. This is more a benchmark than a real test. It highlights a pattern where Art's verifier is currently slower than Dalvik's verifier. Bug: 12126841 Change-Id: I1ed6466798696b0aa04b94edac2d2b9d0015c6a2 --- test/303-verification-stress/build | 28 ++++++++++ test/303-verification-stress/classes-gen.c | 64 ++++++++++++++++++++++ test/303-verification-stress/expected.txt | 12 ++++ test/303-verification-stress/info.txt | 7 +++ test/303-verification-stress/src/Main.java | 27 +++++++++ 5 files changed, 138 insertions(+) create mode 100644 test/303-verification-stress/build create mode 100644 test/303-verification-stress/classes-gen.c create mode 100644 test/303-verification-stress/expected.txt create mode 100644 test/303-verification-stress/info.txt create mode 100644 test/303-verification-stress/src/Main.java diff --git a/test/303-verification-stress/build b/test/303-verification-stress/build new file mode 100644 index 00000000000..2ef9beafd13 --- /dev/null +++ b/test/303-verification-stress/build @@ -0,0 +1,28 @@ +#!/bin/bash +# +# Copyright (C) 2013 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. + +# Stop if something fails. +set -e + +# Write out a bunch of source files. +gcc -o classes-gen classes-gen.c +./classes-gen + +mkdir classes +${JAVAC} -d classes src/*.java + +${DX} --debug --dex --dump-to=classes.lst --output=classes.dex classes +zip $TEST_NAME.jar classes.dex diff --git a/test/303-verification-stress/classes-gen.c b/test/303-verification-stress/classes-gen.c new file mode 100644 index 00000000000..be6cfa733d9 --- /dev/null +++ b/test/303-verification-stress/classes-gen.c @@ -0,0 +1,64 @@ +/* + * Copyright 2013 The Android Open Source Project + * + * Generate a big pile of classes with big . + */ +#include + +/* + * Create N files. + */ +static int createFiles(int count, int array_size) +{ + FILE* fp; + int i; + int k; + + for (i = 0; i < count; i++) { + char nameBuf[32]; + + snprintf(nameBuf, sizeof(nameBuf), "src/Test%03d.java", i); + fp = fopen(nameBuf, "w"); + if (fp == NULL) { + fprintf(stderr, "ERROR: unable to open %s\n", nameBuf); + return -1; + } + + fprintf(fp, "public class Test%03d {\n", i); + fprintf(fp, " static String[] array = new String[%d];\n", array_size); + fprintf(fp, " static {\n", array_size); + for (k = 0; k < array_size; k++) { + fprintf(fp, " array[%d] = \"string_%04d\";\n", k, k); + } + fprintf(fp, " }\n", array_size); + fprintf(fp, "}\n"); + fclose(fp); + } + + // Create test class. + fp = fopen("src/MainTest.java", "w"); + if (fp == NULL) { + fprintf(stderr, "ERROR: unable to open src/MainTest.java\n"); + return -1; + } + fprintf(fp, "public class MainTest {\n"); + fprintf(fp, " public static void run() {\n"); + for (i = 0; i < count; i++) { + fprintf(fp, " System.out.println(\"Create new Test%03d\");\n", i); + fprintf(fp, " new Test%03d();\n", i); + } + fprintf(fp, " }\n"); + fprintf(fp, "}\n"); + fclose(fp); + + return 0; +} + +int main() +{ + int result; + + result = createFiles(40, 2000); + + return (result != 0); +} diff --git a/test/303-verification-stress/expected.txt b/test/303-verification-stress/expected.txt new file mode 100644 index 00000000000..cdfd6cb4614 --- /dev/null +++ b/test/303-verification-stress/expected.txt @@ -0,0 +1,12 @@ +Starting test +Create new Test000 +Create new Test001 +Create new Test002 +Create new Test003 +Create new Test004 +Create new Test005 +Create new Test006 +Create new Test007 +Create new Test008 +Create new Test009 +Done diff --git a/test/303-verification-stress/info.txt b/test/303-verification-stress/info.txt new file mode 100644 index 00000000000..131682cbc80 --- /dev/null +++ b/test/303-verification-stress/info.txt @@ -0,0 +1,7 @@ +This is more a benchmark for the verifier than a real test. We create many +classes, each one initializing a big array of string in its class initializer. +This generates big methods in these classes. The goal is to stress the +verifier on such method. + +Note: these classes are generated automatically. The number of classes and the +size of string array can be modified in the script. diff --git a/test/303-verification-stress/src/Main.java b/test/303-verification-stress/src/Main.java new file mode 100644 index 00000000000..d906baec491 --- /dev/null +++ b/test/303-verification-stress/src/Main.java @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2013 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[]) { + System.out.println("Starting test"); + + // MainTest class is generated automatically. + MainTest.run(); + + System.out.println("Done"); + } +} From 368159762f69db8a014724bbc5e04e343b586aa5 Mon Sep 17 00:00:00 2001 From: Elliott Hughes Date: Fri, 13 Dec 2013 14:00:36 -0800 Subject: [PATCH 0283/2402] Use dmb. We don't support ARM processors that don't have dmb anyway. Change-Id: I41b15a1592febcc0b33d93c291431d00692a2928 --- compiler/utils/arm/assembler_arm.cc | 9 --------- 1 file changed, 9 deletions(-) diff --git a/compiler/utils/arm/assembler_arm.cc b/compiler/utils/arm/assembler_arm.cc index f0d11d8f909..828dffafa1f 100644 --- a/compiler/utils/arm/assembler_arm.cc +++ b/compiler/utils/arm/assembler_arm.cc @@ -1742,17 +1742,8 @@ void ArmAssembler::Copy(FrameOffset /*dst*/, Offset /*dest_offset*/, FrameOffset void ArmAssembler::MemoryBarrier(ManagedRegister mscratch) { CHECK_EQ(mscratch.AsArm().AsCoreRegister(), R12); #if ANDROID_SMP != 0 -#if defined(__ARM_HAVE_DMB) int32_t encoding = 0xf57ff05f; // dmb Emit(encoding); -#elif defined(__ARM_HAVE_LDREX_STREX) - LoadImmediate(R12, 0); - int32_t encoding = 0xee07cfba; // mcr p15, 0, r12, c7, c10, 5 - Emit(encoding); -#else - LoadImmediate(R12, 0xffff0fa0); // kuser_memory_barrier - blx(R12); -#endif #endif } From bff1ef0746048978b877c0664f758d2d6006f27d Mon Sep 17 00:00:00 2001 From: Mark Mendell Date: Fri, 13 Dec 2013 13:47:34 -0800 Subject: [PATCH 0284/2402] Implement GenInlinedSqrt for x86 Implemented this using the hardware instruction, which handles NaN properly. Tested manually using host mode. Change-Id: I082aa20041c933ae5fc78f12ddf491d1c775c683 Signed-off-by: Mark Mendell --- compiler/dex/quick/x86/assemble_x86.cc | 2 ++ compiler/dex/quick/x86/fp_x86.cc | 10 ++++++++-- compiler/dex/quick/x86/x86_lir.h | 2 ++ runtime/arch/x86/entrypoints_init_x86.cc | 1 + 4 files changed, 13 insertions(+), 2 deletions(-) diff --git a/compiler/dex/quick/x86/assemble_x86.cc b/compiler/dex/quick/x86/assemble_x86.cc index 96dc6ee7d16..e4e345cd7b4 100644 --- a/compiler/dex/quick/x86/assemble_x86.cc +++ b/compiler/dex/quick/x86/assemble_x86.cc @@ -288,6 +288,8 @@ ENCODING_MAP(Cmp, IS_LOAD, 0, 0, { kX86PsrlqRI, kRegImm, IS_BINARY_OP | REG_DEF0_USE0, { 0x66, 0, 0x0F, 0x73, 0, 2, 0, 1 }, "PsrlqRI", "!0r,!1d" }, { kX86PsllqRI, kRegImm, IS_BINARY_OP | REG_DEF0_USE0, { 0x66, 0, 0x0F, 0x73, 0, 6, 0, 1 }, "PsllqRI", "!0r,!1d" }, + { kX86SqrtsdRR, kRegReg, IS_BINARY_OP | REG_DEF0_USE1, { 0xF2, 0, 0x0F, 0x51, 0, 0, 0, 0 }, "SqrtsdRR", "!0r,!1r" }, + { kX86FSTPdM, kMem, IS_STORE | IS_BINARY_OP | REG_USE0, { 0x0, 0, 0xDD, 0x00, 0, 3, 0, 0 }, "FstpdM", "[!0r,!1d]" }, EXT_0F_ENCODING_MAP(Movdxr, 0x66, 0x6E, REG_DEF0), { kX86MovdrxRR, kRegRegStore, IS_BINARY_OP | REG_DEF0 | REG_USE01, { 0x66, 0, 0x0F, 0x7E, 0, 0, 0, 0 }, "MovdrxRR", "!0r,!1r" }, diff --git a/compiler/dex/quick/x86/fp_x86.cc b/compiler/dex/quick/x86/fp_x86.cc index c9d6bfc8cce..1731703a3f4 100644 --- a/compiler/dex/quick/x86/fp_x86.cc +++ b/compiler/dex/quick/x86/fp_x86.cc @@ -369,8 +369,14 @@ void X86Mir2Lir::GenNegDouble(RegLocation rl_dest, RegLocation rl_src) { } bool X86Mir2Lir::GenInlinedSqrt(CallInfo* info) { - DCHECK_NE(cu_->instruction_set, kThumb2); - return false; + RegLocation rl_src = info->args[0]; + RegLocation rl_dest = InlineTargetWide(info); // double place for result + rl_src = LoadValueWide(rl_src, kFPReg); + RegLocation rl_result = EvalLoc(rl_dest, kFPReg, true); + NewLIR2(kX86SqrtsdRR, S2d(rl_result.low_reg, rl_result.high_reg), + S2d(rl_src.low_reg, rl_src.high_reg)); + StoreValueWide(rl_dest, rl_result); + return true; } diff --git a/compiler/dex/quick/x86/x86_lir.h b/compiler/dex/quick/x86/x86_lir.h index 5fe76fe2f9c..ca5a2345733 100644 --- a/compiler/dex/quick/x86/x86_lir.h +++ b/compiler/dex/quick/x86/x86_lir.h @@ -348,6 +348,8 @@ enum X86OpCode { Binary0fOpCode(kX86Divss), // float divide kX86PsrlqRI, // right shift of floating point registers kX86PsllqRI, // left shift of floating point registers + kX86SqrtsdRR, // sqrt of floating point register + kX86FSTPdM, // Store and pop top x87 fp stack Binary0fOpCode(kX86Movdxr), // move into xmm from gpr kX86MovdrxRR, kX86MovdrxMR, kX86MovdrxAR, // move into reg from xmm kX86Set8R, kX86Set8M, kX86Set8A, // set byte depending on condition operand diff --git a/runtime/arch/x86/entrypoints_init_x86.cc b/runtime/arch/x86/entrypoints_init_x86.cc index 6a67079b6f4..b1d031aa6b0 100644 --- a/runtime/arch/x86/entrypoints_init_x86.cc +++ b/runtime/arch/x86/entrypoints_init_x86.cc @@ -176,6 +176,7 @@ void InitEntryPoints(InterpreterEntryPoints* ipoints, JniEntryPoints* jpoints, // points->pCmplDouble = NULL; // Not needed on x86. // points->pCmplFloat = NULL; // Not needed on x86. qpoints->pFmod = art_quick_fmod; + // qpoints->pSqrt = NULL; // Not needed on x86. qpoints->pL2d = art_quick_l2d; qpoints->pFmodf = art_quick_fmodf; qpoints->pL2f = art_quick_l2f; From 2d7e5aa6c707537f1906ed77b0ff29ec3dd261f7 Mon Sep 17 00:00:00 2001 From: Jeff Hao Date: Fri, 13 Dec 2013 17:39:59 -0800 Subject: [PATCH 0285/2402] Add classes that fail verification early to rejected class list. Change-Id: I5e06ec2dc3dfd061fcd6c099e10991482a1aaf65 --- runtime/verifier/method_verifier.cc | 38 ++++++++++++++--------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc index 9183b5f53c4..59ead892e1e 100644 --- a/runtime/verifier/method_verifier.cc +++ b/runtime/verifier/method_verifier.cc @@ -90,28 +90,28 @@ MethodVerifier::FailureKind MethodVerifier::VerifyClass(const mirror::Class* kla if (klass->IsVerified()) { return kNoFailure; } - mirror::Class* super = klass->GetSuperClass(); - if (super == NULL && strcmp("Ljava/lang/Object;", ClassHelper(klass).GetDescriptor()) != 0) { - *error = "Verifier rejected class "; - *error += PrettyDescriptor(klass); - *error += " that has no super class"; - return kHardFailure; - } - if (super != NULL && super->IsFinal()) { - *error = "Verifier rejected class "; - *error += PrettyDescriptor(klass); - *error += " that attempts to sub-class final class "; - *error += PrettyDescriptor(super); - return kHardFailure; - } + bool early_failure = false; + std::string failure_message; ClassHelper kh(klass); const DexFile& dex_file = kh.GetDexFile(); const DexFile::ClassDef* class_def = kh.GetClassDef(); - if (class_def == NULL) { - *error = "Verifier rejected class "; - *error += PrettyDescriptor(klass); - *error += " that isn't present in dex file "; - *error += dex_file.GetLocation(); + mirror::Class* super = klass->GetSuperClass(); + if (super == NULL && strcmp("Ljava/lang/Object;", kh.GetDescriptor()) != 0) { + early_failure = true; + failure_message = " that has no super class"; + } else if (super != NULL && super->IsFinal()) { + early_failure = true; + failure_message = " that attempts to sub-class final class " + PrettyDescriptor(super); + } else if (class_def == NULL) { + early_failure = true; + failure_message = " that isn't present in dex file " + dex_file.GetLocation(); + } + if (early_failure) { + *error = "Verifier rejected class " + PrettyDescriptor(klass) + failure_message; + if (Runtime::Current()->IsCompiler()) { + ClassReference ref(&dex_file, klass->GetDexClassDefIndex()); + AddRejectedClass(ref); + } return kHardFailure; } Thread* self = Thread::Current(); From 31050c6929b6d83a33ebefa5b8fe0bf3d6389517 Mon Sep 17 00:00:00 2001 From: Brian Carlstrom Date: Sun, 15 Dec 2013 12:36:09 -0800 Subject: [PATCH 0286/2402] Make part of the elf_writer_test specific to the portable build. Change-Id: Ic24d04b71a0267ed3a7c7fe4e7b167644b8c96e0 --- compiler/elf_writer_test.cc | 51 +++++++++++++++++++++++++------------ 1 file changed, 35 insertions(+), 16 deletions(-) diff --git a/compiler/elf_writer_test.cc b/compiler/elf_writer_test.cc index eca67a8a6e6..95e5f9f73da 100644 --- a/compiler/elf_writer_test.cc +++ b/compiler/elf_writer_test.cc @@ -29,9 +29,19 @@ class ElfWriterTest : public CommonTest { } }; -#define EXPECT_ELF_FILE_ADDRESS(ef, value, name, build_map) \ - EXPECT_EQ(value, reinterpret_cast(ef->FindSymbolAddress(::llvm::ELF::SHT_DYNSYM, name, build_map))); \ - EXPECT_EQ(value, ef->FindDynamicSymbolAddress(name)); \ +#define EXPECT_ELF_FILE_ADDRESS(ef, expected_value, symbol_name, build_map) \ + do { \ + void* addr = reinterpret_cast(ef->FindSymbolAddress(::llvm::ELF::SHT_DYNSYM, \ + symbol_name, \ + build_map)); \ + EXPECT_NE(nullptr, addr); \ + EXPECT_LT(static_cast(ART_BASE_ADDRESS), reinterpret_cast(addr)); \ + if (expected_value == nullptr) { \ + expected_value = addr; \ + } \ + EXPECT_EQ(expected_value, addr); \ + EXPECT_EQ(expected_value, ef->FindDynamicSymbolAddress(symbol_name)); \ + } while (false) TEST_F(ElfWriterTest, dlsym) { std::string elf_filename; @@ -45,22 +55,31 @@ TEST_F(ElfWriterTest, dlsym) { LOG(INFO) << "elf_filename=" << elf_filename; UnreserveImageSpace(); - void* dl_oat_so = dlopen(elf_filename.c_str(), RTLD_NOW); - ASSERT_TRUE(dl_oat_so != NULL) << dlerror(); - void* dl_oatdata = dlsym(dl_oat_so, "oatdata"); - ASSERT_TRUE(dl_oatdata != NULL); + void* dl_oatdata = NULL; + void* dl_oatexec = NULL; + void* dl_oatlastword = NULL; - OatHeader* dl_oat_header = reinterpret_cast(dl_oatdata); - ASSERT_TRUE(dl_oat_header->IsValid()); - void* dl_oatexec = dlsym(dl_oat_so, "oatexec"); - ASSERT_TRUE(dl_oatexec != NULL); - ASSERT_LT(dl_oatdata, dl_oatexec); +#if defined(ART_USE_PORTABLE_COMPILER) + { + // We only use dlopen for loading with portable. See OatFile::Open. + void* dl_oat_so = dlopen(elf_filename.c_str(), RTLD_NOW); + ASSERT_TRUE(dl_oat_so != NULL) << dlerror(); + dl_oatdata = dlsym(dl_oat_so, "oatdata"); + ASSERT_TRUE(dl_oatdata != NULL); + + OatHeader* dl_oat_header = reinterpret_cast(dl_oatdata); + ASSERT_TRUE(dl_oat_header->IsValid()); + dl_oatexec = dlsym(dl_oat_so, "oatexec"); + ASSERT_TRUE(dl_oatexec != NULL); + ASSERT_LT(dl_oatdata, dl_oatexec); - void* dl_oatlastword = dlsym(dl_oat_so, "oatlastword"); - ASSERT_TRUE(dl_oatlastword != NULL); - ASSERT_LT(dl_oatexec, dl_oatlastword); + dl_oatlastword = dlsym(dl_oat_so, "oatlastword"); + ASSERT_TRUE(dl_oatlastword != NULL); + ASSERT_LT(dl_oatexec, dl_oatlastword); - ASSERT_EQ(0, dlclose(dl_oat_so)); + ASSERT_EQ(0, dlclose(dl_oat_so)); + } +#endif UniquePtr file(OS::OpenFileForReading(elf_filename.c_str())); ASSERT_TRUE(file.get() != NULL); From 12f96283471dea664d26c185b2185445cdc49a46 Mon Sep 17 00:00:00 2001 From: Vladimir Marko Date: Mon, 16 Dec 2013 14:44:03 +0000 Subject: [PATCH 0287/2402] Fix minor style issues Follow-up to I082aa20041c933ae5fc78f12ddf491d1c775c683. Change-Id: Ia334b192bdba231b0b9a2b2f2d7d18fcff2ca836 --- compiler/dex/quick/x86/assemble_x86.cc | 2 +- compiler/dex/quick/x86/x86_lir.h | 2 +- runtime/arch/x86/entrypoints_init_x86.cc | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/compiler/dex/quick/x86/assemble_x86.cc b/compiler/dex/quick/x86/assemble_x86.cc index e4e345cd7b4..0ecc0915fac 100644 --- a/compiler/dex/quick/x86/assemble_x86.cc +++ b/compiler/dex/quick/x86/assemble_x86.cc @@ -289,7 +289,7 @@ ENCODING_MAP(Cmp, IS_LOAD, 0, 0, { kX86PsrlqRI, kRegImm, IS_BINARY_OP | REG_DEF0_USE0, { 0x66, 0, 0x0F, 0x73, 0, 2, 0, 1 }, "PsrlqRI", "!0r,!1d" }, { kX86PsllqRI, kRegImm, IS_BINARY_OP | REG_DEF0_USE0, { 0x66, 0, 0x0F, 0x73, 0, 6, 0, 1 }, "PsllqRI", "!0r,!1d" }, { kX86SqrtsdRR, kRegReg, IS_BINARY_OP | REG_DEF0_USE1, { 0xF2, 0, 0x0F, 0x51, 0, 0, 0, 0 }, "SqrtsdRR", "!0r,!1r" }, - { kX86FSTPdM, kMem, IS_STORE | IS_BINARY_OP | REG_USE0, { 0x0, 0, 0xDD, 0x00, 0, 3, 0, 0 }, "FstpdM", "[!0r,!1d]" }, + { kX86FstpdM, kMem, IS_STORE | IS_BINARY_OP | REG_USE0, { 0x0, 0, 0xDD, 0x00, 0, 3, 0, 0 }, "FstpdM", "[!0r,!1d]" }, EXT_0F_ENCODING_MAP(Movdxr, 0x66, 0x6E, REG_DEF0), { kX86MovdrxRR, kRegRegStore, IS_BINARY_OP | REG_DEF0 | REG_USE01, { 0x66, 0, 0x0F, 0x7E, 0, 0, 0, 0 }, "MovdrxRR", "!0r,!1r" }, diff --git a/compiler/dex/quick/x86/x86_lir.h b/compiler/dex/quick/x86/x86_lir.h index ca5a2345733..a2d5c3e402d 100644 --- a/compiler/dex/quick/x86/x86_lir.h +++ b/compiler/dex/quick/x86/x86_lir.h @@ -349,7 +349,7 @@ enum X86OpCode { kX86PsrlqRI, // right shift of floating point registers kX86PsllqRI, // left shift of floating point registers kX86SqrtsdRR, // sqrt of floating point register - kX86FSTPdM, // Store and pop top x87 fp stack + kX86FstpdM, // Store and pop top x87 fp stack Binary0fOpCode(kX86Movdxr), // move into xmm from gpr kX86MovdrxRR, kX86MovdrxMR, kX86MovdrxAR, // move into reg from xmm kX86Set8R, kX86Set8M, kX86Set8A, // set byte depending on condition operand diff --git a/runtime/arch/x86/entrypoints_init_x86.cc b/runtime/arch/x86/entrypoints_init_x86.cc index b1d031aa6b0..589c7d9dc9c 100644 --- a/runtime/arch/x86/entrypoints_init_x86.cc +++ b/runtime/arch/x86/entrypoints_init_x86.cc @@ -176,7 +176,7 @@ void InitEntryPoints(InterpreterEntryPoints* ipoints, JniEntryPoints* jpoints, // points->pCmplDouble = NULL; // Not needed on x86. // points->pCmplFloat = NULL; // Not needed on x86. qpoints->pFmod = art_quick_fmod; - // qpoints->pSqrt = NULL; // Not needed on x86. + // qpoints->pSqrt = NULL; // Not needed on x86. qpoints->pL2d = art_quick_l2d; qpoints->pFmodf = art_quick_fmodf; qpoints->pL2f = art_quick_l2f; From 01add2c125c39f10a0b7d4e7d9c91fb2fe52f9f1 Mon Sep 17 00:00:00 2001 From: Brian Carlstrom Date: Mon, 16 Dec 2013 09:50:35 -0800 Subject: [PATCH 0288/2402] Port "Retry mount() if it fails with EINTR" to ART Porting https://android-review.googlesource.com/#/c/72742 Change-Id: I13ba58f2d3589ea61d1df3cd06023268151ce864 --- runtime/native/dalvik_system_Zygote.cc | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/runtime/native/dalvik_system_Zygote.cc b/runtime/native/dalvik_system_Zygote.cc index 2e3d6a6ab4a..7fa9457eb35 100644 --- a/runtime/native/dalvik_system_Zygote.cc +++ b/runtime/native/dalvik_system_Zygote.cc @@ -351,13 +351,14 @@ static bool MountEmulatedStorage(uid_t uid, jint mount_mode) { if (mount_mode == MOUNT_EXTERNAL_MULTIUSER_ALL) { // Mount entire external storage tree for all users - if (mount(source, target, NULL, MS_BIND, NULL) == -1) { + if (TEMP_FAILURE_RETRY(mount(source, target, NULL, MS_BIND, NULL)) == -1) { PLOG(WARNING) << "Failed to mount " << source << " to " << target; return false; } } else { // Only mount user-specific external storage - if (mount(source_user.c_str(), target_user.c_str(), NULL, MS_BIND, NULL) == -1) { + if (TEMP_FAILURE_RETRY( + mount(source_user.c_str(), target_user.c_str(), NULL, MS_BIND, NULL)) == -1) { PLOG(WARNING) << "Failed to mount " << source_user << " to " << target_user; return false; } @@ -368,7 +369,8 @@ static bool MountEmulatedStorage(uid_t uid, jint mount_mode) { } // Finally, mount user-specific path into place for legacy users - if (mount(target_user.c_str(), legacy, NULL, MS_BIND | MS_REC, NULL) == -1) { + if (TEMP_FAILURE_RETRY( + mount(target_user.c_str(), legacy, NULL, MS_BIND | MS_REC, NULL)) == -1) { PLOG(WARNING) << "Failed to mount " << target_user << " to " << legacy; return false; } @@ -407,7 +409,8 @@ static pid_t ForkAndSpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArra jint debug_flags, jobjectArray javaRlimits, jlong permittedCapabilities, jlong effectiveCapabilities, jint mount_external, - jstring java_se_info, jstring java_se_name, bool is_system_server) { + jstring java_se_info, jstring java_se_name, + bool is_system_server) { Runtime* runtime = Runtime::Current(); CHECK(runtime->IsZygote()) << "runtime instance not started with -Xzygote"; if (!runtime->PreZygoteFork()) { @@ -527,14 +530,16 @@ static pid_t ForkAndSpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArra } static jint Zygote_nativeForkAndSpecialize(JNIEnv* env, jclass, jint uid, jint gid, jintArray gids, - jint debug_flags, jobjectArray rlimits, jint mount_external, - jstring se_info, jstring se_name) { - return ForkAndSpecializeCommon(env, uid, gid, gids, debug_flags, rlimits, 0, 0, mount_external, se_info, se_name, false); + jint debug_flags, jobjectArray rlimits, + jint mount_external, jstring se_info, jstring se_name) { + return ForkAndSpecializeCommon(env, uid, gid, gids, debug_flags, rlimits, 0, 0, mount_external, + se_info, se_name, false); } static jint Zygote_nativeForkSystemServer(JNIEnv* env, jclass, uid_t uid, gid_t gid, jintArray gids, jint debug_flags, jobjectArray rlimits, - jlong permittedCapabilities, jlong effectiveCapabilities) { + jlong permittedCapabilities, + jlong effectiveCapabilities) { pid_t pid = ForkAndSpecializeCommon(env, uid, gid, gids, debug_flags, rlimits, permittedCapabilities, effectiveCapabilities, From f0d5f55e09026a6a531504a47e4711d9a725ea79 Mon Sep 17 00:00:00 2001 From: Christopher Ferris Date: Mon, 16 Dec 2013 15:05:39 -0800 Subject: [PATCH 0289/2402] Use the asm-generic ucontext.h for arm. There is no ucontext.h in uapi kernel headers, so use the generic one. This works with or without uapi kernel headers. Bug: 11559337 Change-Id: Ia6d1dafc15ead99c802be3873dcf20f428650585 --- runtime/common_test.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/common_test.h b/runtime/common_test.h index 57cf71a50b7..d3151c36cc1 100644 --- a/runtime/common_test.h +++ b/runtime/common_test.h @@ -158,7 +158,7 @@ class ScratchFile { #include #include -#include +#include // A signal handler called when have an illegal instruction. We record the fact in From 692fafd9778141fa6ef0048c9569abd7ee0253bf Mon Sep 17 00:00:00 2001 From: Mathieu Chartier Date: Fri, 29 Nov 2013 17:24:40 -0800 Subject: [PATCH 0290/2402] Thread local bump pointer allocator. Added a thread local allocator to the heap, each thread has three pointers which specify the thread local buffer: start, cur, and end. When the remaining space in the thread local buffer isn't large enough for the allocation, the allocator allocates a new thread local buffer using the bump pointer allocator. The bump pointer space had to be modified to accomodate thread local buffers. These buffers are called "blocks", where a block is a buffer which contains a set of adjacent objects. Blocks aren't necessarily full and may have wasted memory towards the end. Blocks have an 8 byte header which specifies their size and is required for traversing bump pointer spaces. Memory usage is in between full bump pointer and ROSAlloc since madvised memory limits wasted ram to an average of 1/2 page per block. Added a runtime option -XX:UseTLAB which specifies whether or not to use the thread local allocator. Its a NOP if the garbage collector is not the semispace collector. TODO: Smarter block accounting to prevent us reading objects until we either hit the end of the block or GetClass() == null which signifies that the block isn't 100% full. This would provide a slight speedup to BumpPointerSpace::Walk. Timings: -XX:HeapMinFree=4m -XX:HeapMaxFree=8m -Xmx48m ritzperf memalloc: Dalvik -Xgc:concurrent: 11678 Dalvik -Xgc:noconcurrent: 6697 -Xgc:MS: 5978 -Xgc:SS: 4271 -Xgc:CMS: 4150 -Xgc:SS -XX:UseTLAB: 3255 Bug: 9986565 Bug: 12042213 Change-Id: Ib7e1d4b199a8199f3b1de94b0a7b6e1730689cad --- runtime/arch/quick_alloc_entrypoints.S | 2 + runtime/arch/quick_alloc_entrypoints.cc | 5 + .../quick/quick_alloc_entrypoints.cc | 1 + runtime/gc/collector/garbage_collector.cc | 2 +- runtime/gc/collector/semi_space.cc | 22 +-- runtime/gc/heap-inl.h | 41 +++-- runtime/gc/heap.cc | 88 ++++------ runtime/gc/heap.h | 42 ++--- runtime/gc/space/bump_pointer_space-inl.h | 17 +- runtime/gc/space/bump_pointer_space.cc | 152 +++++++++++++++++- runtime/gc/space/bump_pointer_space.h | 58 ++++--- runtime/gc/space/space.h | 4 - runtime/mirror/array-inl.h | 5 +- runtime/mirror/class-inl.h | 4 +- runtime/mirror/class.cc | 10 +- runtime/native/dalvik_system_VMDebug.cc | 1 + runtime/root_visitor.h | 3 + runtime/runtime.cc | 8 +- runtime/runtime.h | 1 + runtime/thread-inl.h | 12 ++ runtime/thread.cc | 14 +- runtime/thread.h | 29 ++-- 22 files changed, 360 insertions(+), 161 deletions(-) diff --git a/runtime/arch/quick_alloc_entrypoints.S b/runtime/arch/quick_alloc_entrypoints.S index bdadc51d238..2aa67160df5 100644 --- a/runtime/arch/quick_alloc_entrypoints.S +++ b/runtime/arch/quick_alloc_entrypoints.S @@ -36,4 +36,6 @@ GENERATE_ALLOC_ENTRYPOINTS GENERATE_ALLOC_ENTRYPOINTS _instrumented, Instrumented GENERATE_ALLOC_ENTRYPOINTS _bump_pointer, BumpPointer GENERATE_ALLOC_ENTRYPOINTS _bump_pointer_instrumented, BumpPointerInstrumented +GENERATE_ALLOC_ENTRYPOINTS _tlab, TLAB +GENERATE_ALLOC_ENTRYPOINTS _tlab_instrumented, TLABInstrumented .endm diff --git a/runtime/arch/quick_alloc_entrypoints.cc b/runtime/arch/quick_alloc_entrypoints.cc index 192b1241e5a..4cdb3f226fc 100644 --- a/runtime/arch/quick_alloc_entrypoints.cc +++ b/runtime/arch/quick_alloc_entrypoints.cc @@ -53,6 +53,7 @@ namespace art { // Generate the entrypoint functions. GENERATE_ENTRYPOINTS(); GENERATE_ENTRYPOINTS(_bump_pointer); +GENERATE_ENTRYPOINTS(_tlab); static bool entry_points_instrumented = false; static gc::AllocatorType entry_points_allocator = kMovingCollector ? @@ -76,6 +77,10 @@ void ResetQuickAllocEntryPoints(QuickEntryPoints* qpoints) { SetQuickAllocEntryPoints_bump_pointer(qpoints, entry_points_instrumented); break; } + case gc::kAllocatorTypeTLAB: { + SetQuickAllocEntryPoints_tlab(qpoints, entry_points_instrumented); + break; + } default: { LOG(FATAL) << "Unimplemented"; } diff --git a/runtime/entrypoints/quick/quick_alloc_entrypoints.cc b/runtime/entrypoints/quick/quick_alloc_entrypoints.cc index 9155088796d..1ae39abc9a7 100644 --- a/runtime/entrypoints/quick/quick_alloc_entrypoints.cc +++ b/runtime/entrypoints/quick/quick_alloc_entrypoints.cc @@ -81,5 +81,6 @@ extern "C" mirror::Array* artCheckAndAllocArrayFromCodeWithAccessCheck##suffix## GENERATE_ENTRYPOINTS_FOR_ALLOCATOR(, gc::kAllocatorTypeFreeList) GENERATE_ENTRYPOINTS_FOR_ALLOCATOR(BumpPointer, gc::kAllocatorTypeBumpPointer) +GENERATE_ENTRYPOINTS_FOR_ALLOCATOR(TLAB, gc::kAllocatorTypeTLAB) } // namespace art diff --git a/runtime/gc/collector/garbage_collector.cc b/runtime/gc/collector/garbage_collector.cc index cf301feafec..6baee54a1d9 100644 --- a/runtime/gc/collector/garbage_collector.cc +++ b/runtime/gc/collector/garbage_collector.cc @@ -83,9 +83,9 @@ void GarbageCollector::Run(bool clear_soft_references) { uint64_t pause_start = NanoTime(); ATRACE_BEGIN("Application threads suspended"); thread_list->SuspendAll(); + GetHeap()->RevokeAllThreadLocalBuffers(); MarkingPhase(); ReclaimPhase(); - GetHeap()->RevokeAllThreadLocalBuffers(); thread_list->ResumeAll(); ATRACE_END(); RegisterPause(NanoTime() - pause_start); diff --git a/runtime/gc/collector/semi_space.cc b/runtime/gc/collector/semi_space.cc index 923560e2605..f29eadb34b0 100644 --- a/runtime/gc/collector/semi_space.cc +++ b/runtime/gc/collector/semi_space.cc @@ -14,22 +14,6 @@ * limitations under the License. */ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - #include "semi_space.h" #include @@ -337,7 +321,7 @@ Object* SemiSpace::MarkObject(Object* obj) { if (forward_address == nullptr) { // Otherwise, we need to move the object and add it to the markstack for processing. size_t object_size = obj->SizeOf(); - size_t dummy = 0; + size_t bytes_allocated = 0; if (kEnableSimplePromo && reinterpret_cast(obj) < last_gc_to_space_end_) { // If it's allocated before the last GC (older), move (pseudo-promote) it to // the non-moving space (as sort of an old generation.) @@ -346,7 +330,7 @@ Object* SemiSpace::MarkObject(Object* obj) { forward_address = non_moving_space->Alloc(self_, object_size, &bytes_promoted); if (forward_address == nullptr) { // If out of space, fall back to the to-space. - forward_address = to_space_->Alloc(self_, object_size, &dummy); + forward_address = to_space_->Alloc(self_, object_size, &bytes_allocated); } else { GetHeap()->num_bytes_allocated_.fetch_add(bytes_promoted); bytes_promoted_ += bytes_promoted; @@ -364,7 +348,7 @@ Object* SemiSpace::MarkObject(Object* obj) { DCHECK(forward_address != nullptr); } else { // If it's allocated after the last GC (younger), copy it to the to-space. - forward_address = to_space_->Alloc(self_, object_size, &dummy); + forward_address = to_space_->Alloc(self_, object_size, &bytes_allocated); } // Copy over the object and add it to the mark stack since we still need to update it's // references. diff --git a/runtime/gc/heap-inl.h b/runtime/gc/heap-inl.h index 99f084ab44e..9fb5760bd76 100644 --- a/runtime/gc/heap-inl.h +++ b/runtime/gc/heap-inl.h @@ -32,7 +32,7 @@ namespace art { namespace gc { -template +template inline mirror::Object* Heap::AllocObjectWithAllocator(Thread* self, mirror::Class* klass, size_t byte_count, AllocatorType allocator, const PreFenceVisitor& pre_fence_visitor) { @@ -43,13 +43,13 @@ inline mirror::Object* Heap::AllocObjectWithAllocator(Thread* self, mirror::Clas self->AssertThreadSuspensionIsAllowable(); // Need to check that we arent the large object allocator since the large object allocation code // path this function. If we didn't check we would have an infinite loop. - if (allocator != kAllocatorTypeLOS && UNLIKELY(ShouldAllocLargeObject(klass, byte_count))) { + if (kCheckLargeObject && UNLIKELY(ShouldAllocLargeObject(klass, byte_count))) { return AllocLargeObject(self, klass, byte_count, pre_fence_visitor); } mirror::Object* obj; - size_t bytes_allocated; AllocationTimer alloc_timer(this, &obj); + size_t bytes_allocated; obj = TryToAllocate(self, allocator, byte_count, &bytes_allocated); if (UNLIKELY(obj == nullptr)) { obj = AllocateInternalWithGc(self, allocator, byte_count, &bytes_allocated, &klass); @@ -89,7 +89,11 @@ inline mirror::Object* Heap::AllocObjectWithAllocator(Thread* self, mirror::Clas } else { DCHECK(!Dbg::IsAllocTrackingEnabled()); } - if (concurrent_gc_) { + // concurrent_gc_ isn't known at compile time so we can optimize by not checking it for + // the BumpPointer or TLAB allocators. This is nice since it allows the entire if statement to be + // optimized out. And for the other allocators, AllocatorMayHaveConcurrentGC is a constant since + // the allocator_type should be constant propagated. + if (AllocatorMayHaveConcurrentGC(allocator) && concurrent_gc_) { CheckConcurrentGC(self, new_num_bytes_allocated, obj); } if (kIsDebugBuild) { @@ -105,15 +109,15 @@ template inline mirror::Object* Heap::AllocLargeObject(Thread* self, mirror::Class* klass, size_t byte_count, const PreFenceVisitor& pre_fence_visitor) { - return AllocObjectWithAllocator(self, klass, byte_count, - kAllocatorTypeLOS, - pre_fence_visitor); + return AllocObjectWithAllocator(self, klass, byte_count, + kAllocatorTypeLOS, + pre_fence_visitor); } template inline mirror::Object* Heap::TryToAllocate(Thread* self, AllocatorType allocator_type, size_t alloc_size, size_t* bytes_allocated) { - if (UNLIKELY(IsOutOfMemoryOnAllocation(alloc_size))) { + if (UNLIKELY(IsOutOfMemoryOnAllocation(allocator_type, alloc_size))) { return nullptr; } if (kInstrumented) { @@ -153,6 +157,21 @@ inline mirror::Object* Heap::TryToAllocate(Thread* self, AllocatorType allocator DCHECK(ret == nullptr || large_object_space_->Contains(ret)); break; } + case kAllocatorTypeTLAB: { + alloc_size = RoundUp(alloc_size, space::BumpPointerSpace::kAlignment); + if (UNLIKELY(self->TLABSize() < alloc_size)) { + // Try allocating a new thread local buffer, if the allocaiton fails the space must be + // full so return nullptr. + if (!bump_pointer_space_->AllocNewTLAB(self, alloc_size + kDefaultTLABSize)) { + return nullptr; + } + } + // The allocation can't fail. + ret = self->AllocTLAB(alloc_size); + DCHECK(ret != nullptr); + *bytes_allocated = alloc_size; + break; + } default: { LOG(FATAL) << "Invalid allocator type"; ret = nullptr; @@ -194,14 +213,14 @@ inline bool Heap::ShouldAllocLargeObject(mirror::Class* c, size_t byte_count) co return byte_count >= kLargeObjectThreshold && have_zygote_space_ && c->IsPrimitiveArray(); } -template -inline bool Heap::IsOutOfMemoryOnAllocation(size_t alloc_size) { +template +inline bool Heap::IsOutOfMemoryOnAllocation(AllocatorType allocator_type, size_t alloc_size) { size_t new_footprint = num_bytes_allocated_ + alloc_size; if (UNLIKELY(new_footprint > max_allowed_footprint_)) { if (UNLIKELY(new_footprint > growth_limit_)) { return true; } - if (!concurrent_gc_) { + if (!AllocatorMayHaveConcurrentGC(allocator_type) || !concurrent_gc_) { if (!kGrow) { return true; } diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc index 11acd33bb8e..76a8e794c8c 100644 --- a/runtime/gc/heap.cc +++ b/runtime/gc/heap.cc @@ -77,7 +77,7 @@ Heap::Heap(size_t initial_size, size_t growth_limit, size_t min_free, size_t max double target_utilization, size_t capacity, const std::string& image_file_name, CollectorType post_zygote_collector_type, size_t parallel_gc_threads, size_t conc_gc_threads, bool low_memory_mode, size_t long_pause_log_threshold, - size_t long_gc_log_threshold, bool ignore_max_footprint) + size_t long_gc_log_threshold, bool ignore_max_footprint, bool use_tlab) : non_moving_space_(nullptr), concurrent_gc_(false), collector_type_(kCollectorTypeNone), @@ -103,11 +103,6 @@ Heap::Heap(size_t initial_size, size_t growth_limit, size_t min_free, size_t max native_footprint_gc_watermark_(initial_size), native_footprint_limit_(2 * initial_size), native_need_to_run_finalization_(false), - activity_thread_class_(NULL), - application_thread_class_(NULL), - activity_thread_(NULL), - application_thread_(NULL), - last_process_state_id_(NULL), // Initially assume we perceive jank in case the process state is never updated. process_state_(kProcessStateJankPerceptible), concurrent_start_bytes_(std::numeric_limits::max()), @@ -148,7 +143,8 @@ Heap::Heap(size_t initial_size, size_t growth_limit, size_t min_free, size_t max total_allocation_time_(0), verify_object_mode_(kHeapVerificationNotPermitted), gc_disable_count_(0), - running_on_valgrind_(RUNNING_ON_VALGRIND) { + running_on_valgrind_(RUNNING_ON_VALGRIND), + use_tlab_(use_tlab) { if (VLOG_IS_ON(heap) || VLOG_IS_ON(startup)) { LOG(INFO) << "Heap() entering"; } @@ -337,36 +333,21 @@ void Heap::CreateThreadPool() { } void Heap::VisitObjects(ObjectVisitorCallback callback, void* arg) { - // Visit objects in bump pointer space. Thread* self = Thread::Current(); - // TODO: Use reference block. - std::vector*> saved_refs; + // GCs can move objects, so don't allow this. + const char* old_cause = self->StartAssertNoThreadSuspension("Visiting objects"); if (bump_pointer_space_ != nullptr) { - // Need to put all these in sirts since the callback may trigger a GC. TODO: Use a better data - // structure. - mirror::Object* obj = reinterpret_cast(bump_pointer_space_->Begin()); - const mirror::Object* end = reinterpret_cast( - bump_pointer_space_->End()); - while (obj < end) { - saved_refs.push_back(new SirtRef(self, obj)); - obj = space::BumpPointerSpace::GetNextObject(obj); - } + // Visit objects in bump pointer space. + bump_pointer_space_->Walk(callback, arg); } // TODO: Switch to standard begin and end to use ranged a based loop. for (mirror::Object** it = allocation_stack_->Begin(), **end = allocation_stack_->End(); it < end; ++it) { mirror::Object* obj = *it; - // Objects in the allocation stack might be in a movable space. - saved_refs.push_back(new SirtRef(self, obj)); + callback(obj, arg); } GetLiveBitmap()->Walk(callback, arg); - for (const auto& ref : saved_refs) { - callback(ref->get(), arg); - } - // Need to free the sirts in reverse order they were allocated. - for (size_t i = saved_refs.size(); i != 0; --i) { - delete saved_refs[i - 1]; - } + self->EndAssertNoThreadSuspension(old_cause); } void Heap::MarkAllocStackAsLive(accounting::ObjectStack* stack) { @@ -471,8 +452,6 @@ void Heap::DumpGcPerformanceInfo(std::ostream& os) { } } uint64_t allocation_time = static_cast(total_allocation_time_) * kTimeAdjust; - size_t total_objects_allocated = GetObjectsAllocatedEver(); - size_t total_bytes_allocated = GetBytesAllocatedEver(); if (total_duration != 0) { const double total_seconds = static_cast(total_duration / 1000) / 1000000.0; os << "Total time spent in GC: " << PrettyDuration(total_duration) << "\n"; @@ -481,7 +460,9 @@ void Heap::DumpGcPerformanceInfo(std::ostream& os) { os << "Mean GC object throughput: " << (GetObjectsFreedEver() / total_seconds) << " objects/s\n"; } + size_t total_objects_allocated = GetObjectsAllocatedEver(); os << "Total number of allocations: " << total_objects_allocated << "\n"; + size_t total_bytes_allocated = GetBytesAllocatedEver(); os << "Total bytes allocated " << PrettySize(total_bytes_allocated) << "\n"; if (kMeasureAllocationTime) { os << "Total time spent allocating: " << PrettyDuration(allocation_time) << "\n"; @@ -698,7 +679,7 @@ void Heap::Trim() { } } total_alloc_space_allocated = GetBytesAllocated() - large_object_space_->GetBytesAllocated() - - bump_pointer_space_->GetBytesAllocated(); + bump_pointer_space_->Size(); const float managed_utilization = static_cast(total_alloc_space_allocated) / static_cast(total_alloc_space_size); uint64_t gc_heap_end_ns = NanoTime(); @@ -867,12 +848,10 @@ void Heap::VerifyHeap() { void Heap::RecordFree(size_t freed_objects, size_t freed_bytes) { DCHECK_LE(freed_bytes, static_cast(num_bytes_allocated_)); num_bytes_allocated_.fetch_sub(freed_bytes); - if (Runtime::Current()->HasStatsEnabled()) { RuntimeStats* thread_stats = Thread::Current()->GetStats(); thread_stats->freed_objects += freed_objects; thread_stats->freed_bytes += freed_bytes; - // TODO: Do this concurrently. RuntimeStats* global_stats = Runtime::Current()->GetStats(); global_stats->freed_objects += freed_objects; @@ -945,19 +924,11 @@ size_t Heap::GetObjectsAllocated() const { } size_t Heap::GetObjectsAllocatedEver() const { - size_t total = 0; - for (space::AllocSpace* space : alloc_spaces_) { - total += space->GetTotalObjectsAllocated(); - } - return total; + return GetObjectsFreedEver() + GetObjectsAllocated(); } size_t Heap::GetBytesAllocatedEver() const { - size_t total = 0; - for (space::AllocSpace* space : alloc_spaces_) { - total += space->GetTotalBytesAllocated(); - } - return total; + return GetBytesFreedEver() + GetBytesAllocated(); } class InstanceCounter { @@ -1102,7 +1073,11 @@ void Heap::ChangeCollector(CollectorType collector_type) { case kCollectorTypeSS: { concurrent_gc_ = false; gc_plan_.push_back(collector::kGcTypeFull); - ChangeAllocator(kAllocatorTypeBumpPointer); + if (use_tlab_) { + ChangeAllocator(kAllocatorTypeTLAB); + } else { + ChangeAllocator(kAllocatorTypeBumpPointer); + } break; } case kCollectorTypeMS: { @@ -1134,6 +1109,10 @@ void Heap::ChangeCollector(CollectorType collector_type) { } } +static void MarkInBitmapCallback(mirror::Object* obj, void* arg) { + reinterpret_cast(arg)->Set(obj); +} + void Heap::PreZygoteFork() { static Mutex zygote_creation_lock_("zygote creation lock", kZygoteCreationLock); Thread* self = Thread::Current(); @@ -1158,7 +1137,7 @@ void Heap::PreZygoteFork() { // Compact the bump pointer space to a new zygote bump pointer space. temp_space_->GetMemMap()->Protect(PROT_READ | PROT_WRITE); Compact(&target_space, bump_pointer_space_); - CHECK_EQ(temp_space_->GetBytesAllocated(), 0U); + CHECK(temp_space_->IsEmpty()); total_objects_freed_ever_ += semi_space_collector_->GetFreedObjects(); total_bytes_freed_ever_ += semi_space_collector_->GetFreedBytes(); // Update the end and write out image. @@ -1167,12 +1146,7 @@ void Heap::PreZygoteFork() { accounting::SpaceBitmap* bitmap = non_moving_space_->GetLiveBitmap(); // Record the allocations in the bitmap. VLOG(heap) << "Recording zygote allocations"; - mirror::Object* obj = reinterpret_cast(target_space.Begin()); - const mirror::Object* end = reinterpret_cast(target_space.End()); - while (obj < end) { - bitmap->Set(obj); - obj = space::BumpPointerSpace::GetNextObject(obj); - } + target_space.Walk(MarkInBitmapCallback, bitmap); } // Turn the current alloc space into a zygote space and obtain the new alloc space composed of // the remaining available heap memory. @@ -1305,9 +1279,11 @@ collector::GcType Heap::CollectGarbageInternal(collector::GcType gc_type, GcCaus collector::GarbageCollector* collector = nullptr; // TODO: Clean this up. - if (current_allocator_ == kAllocatorTypeBumpPointer) { + if (collector_type_ == kCollectorTypeSS) { + DCHECK(current_allocator_ == kAllocatorTypeBumpPointer || + current_allocator_ == kAllocatorTypeTLAB); gc_type = semi_space_collector_->GetGcType(); - CHECK_EQ(temp_space_->GetObjectsAllocated(), 0U); + CHECK(temp_space_->IsEmpty()); semi_space_collector_->SetFromSpace(bump_pointer_space_); semi_space_collector_->SetToSpace(temp_space_); mprotect(temp_space_->Begin(), temp_space_->Capacity(), PROT_READ | PROT_WRITE); @@ -2070,10 +2046,16 @@ void Heap::RequestHeapTrim() { void Heap::RevokeThreadLocalBuffers(Thread* thread) { non_moving_space_->RevokeThreadLocalBuffers(thread); + if (bump_pointer_space_ != nullptr) { + bump_pointer_space_->RevokeThreadLocalBuffers(thread); + } } void Heap::RevokeAllThreadLocalBuffers() { non_moving_space_->RevokeAllThreadLocalBuffers(); + if (bump_pointer_space_ != nullptr) { + bump_pointer_space_->RevokeAllThreadLocalBuffers(); + } } bool Heap::IsGCRequestPending() const { diff --git a/runtime/gc/heap.h b/runtime/gc/heap.h index 9788064a2c0..832d5ec2abb 100644 --- a/runtime/gc/heap.h +++ b/runtime/gc/heap.h @@ -91,6 +91,7 @@ class AgeCardVisitor { // Different types of allocators. enum AllocatorType { kAllocatorTypeBumpPointer, + kAllocatorTypeTLAB, kAllocatorTypeFreeList, // ROSAlloc / dlmalloc kAllocatorTypeLOS, // Large object space. }; @@ -139,6 +140,7 @@ class Heap { static constexpr size_t kDefaultMinFree = kDefaultMaxFree / 4; static constexpr size_t kDefaultLongPauseLogThreshold = MsToNs(5); static constexpr size_t kDefaultLongGCLogThreshold = MsToNs(100); + static constexpr size_t kDefaultTLABSize = 256 * KB; // Default target utilization. static constexpr double kDefaultTargetUtilization = 0.5; @@ -154,24 +156,25 @@ class Heap { const std::string& original_image_file_name, CollectorType collector_type_, size_t parallel_gc_threads, size_t conc_gc_threads, bool low_memory_mode, size_t long_pause_threshold, size_t long_gc_threshold, - bool ignore_max_footprint); + bool ignore_max_footprint, bool use_tlab); ~Heap(); // Allocates and initializes storage for an object instance. - template + template inline mirror::Object* AllocObject(Thread* self, mirror::Class* klass, size_t num_bytes) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - return AllocObjectWithAllocator(self, klass, num_bytes, GetCurrentAllocator()); + return AllocObjectWithAllocator(self, klass, num_bytes, + GetCurrentAllocator()); } - template + template inline mirror::Object* AllocNonMovableObject(Thread* self, mirror::Class* klass, size_t num_bytes) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - return AllocObjectWithAllocator(self, klass, num_bytes, - GetCurrentNonMovingAllocator()); + return AllocObjectWithAllocator(self, klass, num_bytes, + GetCurrentNonMovingAllocator()); } - template + template ALWAYS_INLINE mirror::Object* AllocObjectWithAllocator( Thread* self, mirror::Class* klass, size_t byte_count, AllocatorType allocator, const PreFenceVisitor& pre_fence_visitor = VoidFunctor()) @@ -507,17 +510,19 @@ class Heap { void Compact(space::ContinuousMemMapAllocSpace* target_space, space::ContinuousMemMapAllocSpace* source_space); - static bool AllocatorHasAllocationStack(AllocatorType allocator_type) { - return allocator_type != kAllocatorTypeBumpPointer; + static ALWAYS_INLINE bool AllocatorHasAllocationStack(AllocatorType allocator_type) { + return + allocator_type != kAllocatorTypeBumpPointer && + allocator_type != kAllocatorTypeTLAB; } - static bool AllocatorHasConcurrentGC(AllocatorType allocator_type) { - return allocator_type != kAllocatorTypeBumpPointer; + static ALWAYS_INLINE bool AllocatorMayHaveConcurrentGC(AllocatorType allocator_type) { + return AllocatorHasAllocationStack(allocator_type); } bool ShouldAllocLargeObject(mirror::Class* c, size_t byte_count) const; ALWAYS_INLINE void CheckConcurrentGC(Thread* self, size_t new_num_bytes_allocated, mirror::Object* obj); - // We don't force this to be inline since it is a slow path. + // We don't force this to be inlined since it is a slow path. template mirror::Object* AllocLargeObject(Thread* self, mirror::Class* klass, size_t byte_count, const PreFenceVisitor& pre_fence_visitor) @@ -544,8 +549,9 @@ class Heap { void ThrowOutOfMemoryError(Thread* self, size_t byte_count, bool large_object_allocation) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - template - bool IsOutOfMemoryOnAllocation(size_t alloc_size); + + template + bool IsOutOfMemoryOnAllocation(AllocatorType allocator_type, size_t alloc_size); // Pushes a list of cleared references out to the managed heap. void SetReferenceReferent(mirror::Object* reference, mirror::Object* referent) @@ -721,13 +727,6 @@ class Heap { // Whether or not we need to run finalizers in the next native allocation. bool native_need_to_run_finalization_; - // Activity manager members. - jclass activity_thread_class_; - jclass application_thread_class_; - jobject activity_thread_; - jobject application_thread_; - jfieldID last_process_state_id_; - // Whether or not we currently care about pause times. ProcessState process_state_; @@ -845,6 +844,7 @@ class Heap { collector::SemiSpace* semi_space_collector_; const bool running_on_valgrind_; + const bool use_tlab_; friend class collector::MarkSweep; friend class collector::SemiSpace; diff --git a/runtime/gc/space/bump_pointer_space-inl.h b/runtime/gc/space/bump_pointer_space-inl.h index 85ef2f432f2..82e96a467e9 100644 --- a/runtime/gc/space/bump_pointer_space-inl.h +++ b/runtime/gc/space/bump_pointer_space-inl.h @@ -23,8 +23,8 @@ namespace art { namespace gc { namespace space { -inline mirror::Object* BumpPointerSpace::AllocNonvirtual(size_t num_bytes) { - num_bytes = RoundUp(num_bytes, kAlignment); +inline mirror::Object* BumpPointerSpace::AllocNonvirtualWithoutAccounting(size_t num_bytes) { + DCHECK(IsAligned(num_bytes)); byte* old_end; byte* new_end; do { @@ -38,13 +38,18 @@ inline mirror::Object* BumpPointerSpace::AllocNonvirtual(size_t num_bytes) { } while (android_atomic_cas(reinterpret_cast(old_end), reinterpret_cast(new_end), reinterpret_cast(&end_)) != 0); - // TODO: Less statistics? - total_bytes_allocated_.fetch_add(num_bytes); - num_objects_allocated_.fetch_add(1); - total_objects_allocated_.fetch_add(1); return reinterpret_cast(old_end); } +inline mirror::Object* BumpPointerSpace::AllocNonvirtual(size_t num_bytes) { + mirror::Object* ret = AllocNonvirtualWithoutAccounting(num_bytes); + if (ret != nullptr) { + objects_allocated_.fetch_add(1); + bytes_allocated_.fetch_add(num_bytes); + } + return ret; +} + } // namespace space } // namespace gc } // namespace art diff --git a/runtime/gc/space/bump_pointer_space.cc b/runtime/gc/space/bump_pointer_space.cc index 06ba57e03ae..7ea202c0318 100644 --- a/runtime/gc/space/bump_pointer_space.cc +++ b/runtime/gc/space/bump_pointer_space.cc @@ -18,6 +18,7 @@ #include "bump_pointer_space-inl.h" #include "mirror/object-inl.h" #include "mirror/class-inl.h" +#include "thread_list.h" namespace art { namespace gc { @@ -40,18 +41,27 @@ BumpPointerSpace* BumpPointerSpace::Create(const std::string& name, size_t capac BumpPointerSpace::BumpPointerSpace(const std::string& name, byte* begin, byte* limit) : ContinuousMemMapAllocSpace(name, nullptr, begin, begin, limit, kGcRetentionPolicyAlwaysCollect), - num_objects_allocated_(0), total_bytes_allocated_(0), total_objects_allocated_(0), - growth_end_(limit) { + growth_end_(limit), + objects_allocated_(0), bytes_allocated_(0), + block_lock_("Block lock"), + num_blocks_(0) { + CHECK_GE(Capacity(), sizeof(BlockHeader)); + end_ += sizeof(BlockHeader); } BumpPointerSpace::BumpPointerSpace(const std::string& name, MemMap* mem_map) : ContinuousMemMapAllocSpace(name, mem_map, mem_map->Begin(), mem_map->Begin(), mem_map->End(), kGcRetentionPolicyAlwaysCollect), - num_objects_allocated_(0), total_bytes_allocated_(0), total_objects_allocated_(0), - growth_end_(mem_map->End()) { + growth_end_(mem_map->End()), + objects_allocated_(0), bytes_allocated_(0), + block_lock_("Block lock"), + num_blocks_(0) { + CHECK_GE(Capacity(), sizeof(BlockHeader)); + end_ += sizeof(BlockHeader); } mirror::Object* BumpPointerSpace::Alloc(Thread*, size_t num_bytes, size_t* bytes_allocated) { + num_bytes = RoundUp(num_bytes, kAlignment); mirror::Object* ret = AllocNonvirtual(num_bytes); if (LIKELY(ret != nullptr)) { *bytes_allocated = num_bytes; @@ -68,9 +78,14 @@ void BumpPointerSpace::Clear() { CHECK_NE(madvise(Begin(), Limit() - Begin(), MADV_DONTNEED), -1) << "madvise failed"; // Reset the end of the space back to the beginning, we move the end forward as we allocate // objects. - SetEnd(Begin()); + SetEnd(Begin() + sizeof(BlockHeader)); + objects_allocated_ = 0; + bytes_allocated_ = 0; growth_end_ = Limit(); - num_objects_allocated_ = 0; + { + MutexLock mu(Thread::Current(), block_lock_); + num_blocks_ = 0; + } } void BumpPointerSpace::Dump(std::ostream& os) const { @@ -83,6 +98,131 @@ mirror::Object* BumpPointerSpace::GetNextObject(mirror::Object* obj) { return reinterpret_cast(RoundUp(position, kAlignment)); } +void BumpPointerSpace::RevokeThreadLocalBuffers(Thread* thread) { + MutexLock mu(Thread::Current(), block_lock_); + RevokeThreadLocalBuffersLocked(thread); +} + +void BumpPointerSpace::RevokeAllThreadLocalBuffers() { + Thread* self = Thread::Current(); + MutexLock mu(self, *Locks::runtime_shutdown_lock_); + MutexLock mu2(self, *Locks::thread_list_lock_); + // TODO: Not do a copy of the thread list? + std::list thread_list = Runtime::Current()->GetThreadList()->GetList(); + for (Thread* thread : thread_list) { + RevokeThreadLocalBuffers(thread); + } +} + +void BumpPointerSpace::UpdateMainBlock() { + BlockHeader* header = reinterpret_cast(Begin()); + header->size_ = Size() - sizeof(BlockHeader); + DCHECK_EQ(num_blocks_, 0U); +} + +// Returns the start of the storage. +byte* BumpPointerSpace::AllocBlock(size_t bytes) { + bytes = RoundUp(bytes, kAlignment); + if (!num_blocks_) { + UpdateMainBlock(); + } + byte* storage = reinterpret_cast( + AllocNonvirtualWithoutAccounting(bytes + sizeof(BlockHeader))); + if (LIKELY(storage != nullptr)) { + BlockHeader* header = reinterpret_cast(storage); + header->size_ = bytes; // Write out the block header. + storage += sizeof(BlockHeader); + ++num_blocks_; + } + return storage; +} + +void BumpPointerSpace::Walk(ObjectVisitorCallback callback, void* arg) { + byte* pos = Begin(); + + { + MutexLock mu(Thread::Current(), block_lock_); + // If we have 0 blocks then we need to update the main header since we have bump pointer style + // allocation into an unbounded region (actually bounded by Capacity()). + if (num_blocks_ == 0) { + UpdateMainBlock(); + } + } + + while (pos < End()) { + BlockHeader* header = reinterpret_cast(pos); + size_t block_size = header->size_; + pos += sizeof(BlockHeader); // Skip the header so that we know where the objects + mirror::Object* obj = reinterpret_cast(pos); + const mirror::Object* end = reinterpret_cast(pos + block_size); + CHECK_LE(reinterpret_cast(end), End()); + // We don't know how many objects are allocated in the current block. When we hit a null class + // assume its the end. TODO: Have a thread update the header when it flushes the block? + while (obj < end && obj->GetClass() != nullptr) { + callback(obj, arg); + obj = GetNextObject(obj); + } + pos += block_size; + } +} + +bool BumpPointerSpace::IsEmpty() const { + return Size() == sizeof(BlockHeader); +} + +uint64_t BumpPointerSpace::GetBytesAllocated() { + // Start out pre-determined amount (blocks which are not being allocated into). + uint64_t total = static_cast(bytes_allocated_.load()); + Thread* self = Thread::Current(); + MutexLock mu(self, *Locks::runtime_shutdown_lock_); + MutexLock mu2(self, *Locks::thread_list_lock_); + std::list thread_list = Runtime::Current()->GetThreadList()->GetList(); + MutexLock mu3(Thread::Current(), block_lock_); + // If we don't have any blocks, we don't have any thread local buffers. This check is required + // since there can exist multiple bump pointer spaces which exist at the same time. + if (num_blocks_ > 0) { + for (Thread* thread : thread_list) { + total += thread->thread_local_pos_ - thread->thread_local_start_; + } + } + return total; +} + +uint64_t BumpPointerSpace::GetObjectsAllocated() { + // Start out pre-determined amount (blocks which are not being allocated into). + uint64_t total = static_cast(objects_allocated_.load()); + Thread* self = Thread::Current(); + MutexLock mu(self, *Locks::runtime_shutdown_lock_); + MutexLock mu2(self, *Locks::thread_list_lock_); + std::list thread_list = Runtime::Current()->GetThreadList()->GetList(); + MutexLock mu3(Thread::Current(), block_lock_); + // If we don't have any blocks, we don't have any thread local buffers. This check is required + // since there can exist multiple bump pointer spaces which exist at the same time. + if (num_blocks_ > 0) { + for (Thread* thread : thread_list) { + total += thread->thread_local_objects_; + } + } + return total; +} + +void BumpPointerSpace::RevokeThreadLocalBuffersLocked(Thread* thread) { + objects_allocated_.fetch_add(thread->thread_local_objects_); + bytes_allocated_.fetch_add(thread->thread_local_pos_ - thread->thread_local_start_); + thread->SetTLAB(nullptr, nullptr); +} + +bool BumpPointerSpace::AllocNewTLAB(Thread* self, size_t bytes) { + MutexLock mu(Thread::Current(), block_lock_); + RevokeThreadLocalBuffersLocked(self); + byte* start = AllocBlock(bytes); + if (start == nullptr) { + return false; + } + self->SetTLAB(start, start + bytes); + return true; +} + } // namespace space } // namespace gc } // namespace art diff --git a/runtime/gc/space/bump_pointer_space.h b/runtime/gc/space/bump_pointer_space.h index 2edd3e2faca..0a4be8a0e95 100644 --- a/runtime/gc/space/bump_pointer_space.h +++ b/runtime/gc/space/bump_pointer_space.h @@ -17,6 +17,7 @@ #ifndef ART_RUNTIME_GC_SPACE_BUMP_POINTER_SPACE_H_ #define ART_RUNTIME_GC_SPACE_BUMP_POINTER_SPACE_H_ +#include "root_visitor.h" #include "space.h" namespace art { @@ -45,12 +46,13 @@ class BumpPointerSpace : public ContinuousMemMapAllocSpace { // Allocate num_bytes, returns nullptr if the space is full. virtual mirror::Object* Alloc(Thread* self, size_t num_bytes, size_t* bytes_allocated); mirror::Object* AllocNonvirtual(size_t num_bytes); + mirror::Object* AllocNonvirtualWithoutAccounting(size_t num_bytes); // Return the storage space required by obj. virtual size_t AllocationSize(const mirror::Object* obj) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - // Nos unless we support free lists. + // NOPS unless we support free lists. virtual size_t Free(Thread*, mirror::Object*) { return 0; } @@ -92,21 +94,12 @@ class BumpPointerSpace : public ContinuousMemMapAllocSpace { void Dump(std::ostream& os) const; - uint64_t GetBytesAllocated() { - return Size(); - } - - uint64_t GetObjectsAllocated() { - return num_objects_allocated_; - } + void RevokeThreadLocalBuffers(Thread* thread); + void RevokeAllThreadLocalBuffers(); - uint64_t GetTotalBytesAllocated() { - return total_bytes_allocated_; - } - - uint64_t GetTotalObjectsAllocated() { - return total_objects_allocated_; - } + uint64_t GetBytesAllocated() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + uint64_t GetObjectsAllocated() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + bool IsEmpty() const; bool Contains(const mirror::Object* obj) const { const byte* byte_obj = reinterpret_cast(obj); @@ -120,28 +113,55 @@ class BumpPointerSpace : public ContinuousMemMapAllocSpace { static mirror::Object* GetNextObject(mirror::Object* obj) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + // Allocate a new TLAB, returns false if the allocation failed. + bool AllocNewTLAB(Thread* self, size_t bytes); + virtual BumpPointerSpace* AsBumpPointerSpace() { return this; } + // Go through all of the blocks and visit the continuous objects. + void Walk(ObjectVisitorCallback callback, void* arg) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + // Object alignment within the space. static constexpr size_t kAlignment = 8; protected: BumpPointerSpace(const std::string& name, MemMap* mem_map); + // Allocate a raw block of bytes. + byte* AllocBlock(size_t bytes) EXCLUSIVE_LOCKS_REQUIRED(block_lock_); + void RevokeThreadLocalBuffersLocked(Thread* thread) EXCLUSIVE_LOCKS_REQUIRED(block_lock_); + size_t InternalAllocationSize(const mirror::Object* obj); mirror::Object* AllocWithoutGrowthLocked(size_t num_bytes, size_t* bytes_allocated) EXCLUSIVE_LOCKS_REQUIRED(lock_); - // Approximate number of bytes which have been allocated into the space. - AtomicInteger num_objects_allocated_; - AtomicInteger total_bytes_allocated_; - AtomicInteger total_objects_allocated_; + // The main block is an unbounded block where objects go when there are no other blocks. This + // enables us to maintain tightly packed objects when you are not using thread local buffers for + // allocation. + // The main block is also the block which starts at address 0. + void UpdateMainBlock() EXCLUSIVE_LOCKS_REQUIRED(block_lock_); byte* growth_end_; + AtomicInteger objects_allocated_; // Accumulated from revoked thread local regions. + AtomicInteger bytes_allocated_; // Accumulated from revoked thread local regions. + Mutex block_lock_; + + // The number of blocks in the space, if it is 0 then the space has one long continuous block + // which doesn't have an updated header. + size_t num_blocks_ GUARDED_BY(block_lock_); private: + struct BlockHeader { + size_t size_; // Size of the block in bytes, does not include the header. + size_t unused_; // Ensures alignment of kAlignment. + }; + + COMPILE_ASSERT(sizeof(BlockHeader) % kAlignment == 0, + continuous_block_must_be_kAlignment_aligned); + friend class collector::MarkSweep; DISALLOW_COPY_AND_ASSIGN(BumpPointerSpace); }; diff --git a/runtime/gc/space/space.h b/runtime/gc/space/space.h index ca39175979e..db3aca92134 100644 --- a/runtime/gc/space/space.h +++ b/runtime/gc/space/space.h @@ -198,10 +198,6 @@ class AllocSpace { virtual uint64_t GetBytesAllocated() = 0; // Number of objects currently allocated. virtual uint64_t GetObjectsAllocated() = 0; - // Number of bytes allocated since the space was created. - virtual uint64_t GetTotalBytesAllocated() = 0; - // Number of objects allocated since the space was created. - virtual uint64_t GetTotalObjectsAllocated() = 0; // Allocate num_bytes without allowing growth. If the allocation // succeeds, the output parameter bytes_allocated will be set to the diff --git a/runtime/mirror/array-inl.h b/runtime/mirror/array-inl.h index cf4b48cb02b..bd81bd5e96c 100644 --- a/runtime/mirror/array-inl.h +++ b/runtime/mirror/array-inl.h @@ -83,9 +83,10 @@ inline Array* Array::Alloc(Thread* self, Class* array_class, int32_t component_c } gc::Heap* heap = Runtime::Current()->GetHeap(); SetLengthVisitor visitor(component_count); + DCHECK(allocator_type != gc::kAllocatorTypeLOS); return down_cast( - heap->AllocObjectWithAllocator(self, array_class, size, allocator_type, - visitor)); + heap->AllocObjectWithAllocator(self, array_class, size, + allocator_type, visitor)); } template diff --git a/runtime/mirror/class-inl.h b/runtime/mirror/class-inl.h index 3a28974e4b2..e0fab8ce56c 100644 --- a/runtime/mirror/class-inl.h +++ b/runtime/mirror/class-inl.h @@ -361,8 +361,8 @@ template inline Object* Class::Alloc(Thread* self, gc::AllocatorType allocator_type) { CheckObjectAlloc(); gc::Heap* heap = Runtime::Current()->GetHeap(); - return heap->AllocObjectWithAllocator(self, this, this->object_size_, - allocator_type); + return heap->AllocObjectWithAllocator(self, this, this->object_size_, + allocator_type); } inline Object* Class::AllocObject(Thread* self) { diff --git a/runtime/mirror/class.cc b/runtime/mirror/class.cc index 2746e1e91af..bd965fa4628 100644 --- a/runtime/mirror/class.cc +++ b/runtime/mirror/class.cc @@ -139,9 +139,11 @@ void Class::SetClassSize(size_t new_class_size) { // slashes (so "java.lang.String" but "[Ljava.lang.String;"). Madness. String* Class::ComputeName() { String* name = GetName(); - if (name != NULL) { + if (name != nullptr) { return name; } + Thread* self = Thread::Current(); + SirtRef sirt_c(self, this); std::string descriptor(ClassHelper(this).GetDescriptor()); if ((descriptor[0] != 'L') && (descriptor[0] != '[')) { // The descriptor indicates that this is the class for @@ -160,7 +162,7 @@ String* Class::ComputeName() { default: LOG(FATAL) << "Unknown primitive type: " << PrintableChar(descriptor[0]); } - name = String::AllocFromModifiedUtf8(Thread::Current(), c_name); + name = String::AllocFromModifiedUtf8(self, c_name); } else { // Convert the UTF-8 name to a java.lang.String. The name must use '.' to separate package // components. @@ -169,9 +171,9 @@ String* Class::ComputeName() { descriptor.erase(descriptor.size() - 1); } std::replace(descriptor.begin(), descriptor.end(), '/', '.'); - name = String::AllocFromModifiedUtf8(Thread::Current(), descriptor.c_str()); + name = String::AllocFromModifiedUtf8(self, descriptor.c_str()); } - SetName(name); + sirt_c->SetName(name); return name; } diff --git a/runtime/native/dalvik_system_VMDebug.cc b/runtime/native/dalvik_system_VMDebug.cc index 67c45054462..6a04c3ad177 100644 --- a/runtime/native/dalvik_system_VMDebug.cc +++ b/runtime/native/dalvik_system_VMDebug.cc @@ -272,6 +272,7 @@ static void VMDebug_getHeapSpaceStats(JNIEnv* env, jclass, jlongArray data) { allocSize += malloc_space->GetFootprint(); allocUsed += malloc_space->GetBytesAllocated(); } else if (space->IsBumpPointerSpace()) { + ScopedObjectAccess soa(env); gc::space::BumpPointerSpace* bump_pointer_space = space->AsBumpPointerSpace(); allocSize += bump_pointer_space->Size(); allocUsed += bump_pointer_space->GetBytesAllocated(); diff --git a/runtime/root_visitor.h b/runtime/root_visitor.h index d52f3511519..78c30ffa060 100644 --- a/runtime/root_visitor.h +++ b/runtime/root_visitor.h @@ -17,6 +17,9 @@ #ifndef ART_RUNTIME_ROOT_VISITOR_H_ #define ART_RUNTIME_ROOT_VISITOR_H_ +// For size_t. +#include + namespace art { namespace mirror { class Object; diff --git a/runtime/runtime.cc b/runtime/runtime.cc index e1b4d7ec359..ff7b8f5b69e 100644 --- a/runtime/runtime.cc +++ b/runtime/runtime.cc @@ -355,7 +355,7 @@ Runtime::ParsedOptions* Runtime::ParsedOptions::Create(const Options& options, b parsed->heap_min_free_ = gc::Heap::kDefaultMinFree; parsed->heap_max_free_ = gc::Heap::kDefaultMaxFree; parsed->heap_target_utilization_ = gc::Heap::kDefaultTargetUtilization; - parsed->heap_growth_limit_ = 0; // 0 means no growth limit. + parsed->heap_growth_limit_ = 0; // 0 means no growth limit . // Default to number of processors minus one since the main GC thread also does work. parsed->parallel_gc_threads_ = sysconf(_SC_NPROCESSORS_CONF) - 1; // Only the main GC thread, no workers. @@ -365,6 +365,7 @@ Runtime::ParsedOptions* Runtime::ParsedOptions::Create(const Options& options, b parsed->stack_size_ = 0; // 0 means default. parsed->max_spins_before_thin_lock_inflation_ = Monitor::kDefaultMaxSpinsBeforeThinLockInflation; parsed->low_memory_mode_ = false; + parsed->use_tlab_ = false; parsed->is_compiler_ = false; parsed->is_zygote_ = false; @@ -540,6 +541,8 @@ Runtime::ParsedOptions* Runtime::ParsedOptions::Create(const Options& options, b parsed->ignore_max_footprint_ = true; } else if (option == "-XX:LowMemoryMode") { parsed->low_memory_mode_ = true; + } else if (option == "-XX:UseTLAB") { + parsed->use_tlab_ = true; } else if (StartsWith(option, "-D")) { parsed->properties_.push_back(option.substr(strlen("-D"))); } else if (StartsWith(option, "-Xjnitrace:")) { @@ -925,7 +928,8 @@ bool Runtime::Init(const Options& raw_options, bool ignore_unrecognized) { options->low_memory_mode_, options->long_pause_log_threshold_, options->long_gc_log_threshold_, - options->ignore_max_footprint_); + options->ignore_max_footprint_, + options->use_tlab_); dump_gc_performance_on_shutdown_ = options->dump_gc_performance_on_shutdown_; diff --git a/runtime/runtime.h b/runtime/runtime.h index 01a605a64e3..ce64510af56 100644 --- a/runtime/runtime.h +++ b/runtime/runtime.h @@ -104,6 +104,7 @@ class Runtime { bool is_zygote_; bool interpreter_only_; bool is_explicit_gc_disabled_; + bool use_tlab_; size_t long_pause_log_threshold_; size_t long_gc_log_threshold_; bool dump_gc_performance_on_shutdown_; diff --git a/runtime/thread-inl.h b/runtime/thread-inl.h index e47fd372c7a..6f3c117a595 100644 --- a/runtime/thread-inl.h +++ b/runtime/thread-inl.h @@ -154,6 +154,18 @@ inline void Thread::VerifyStack() { } } +inline size_t Thread::TLABSize() const { + return thread_local_end_ - thread_local_pos_; +} + +inline mirror::Object* Thread::AllocTLAB(size_t bytes) { + DCHECK_GE(TLABSize(), bytes); + ++thread_local_objects_; + mirror::Object* ret = reinterpret_cast(thread_local_pos_); + thread_local_pos_ += bytes; + return ret; +} + } // namespace art #endif // ART_RUNTIME_THREAD_INL_H_ diff --git a/runtime/thread.cc b/runtime/thread.cc index 28612132f87..bc252deecce 100644 --- a/runtime/thread.cc +++ b/runtime/thread.cc @@ -928,7 +928,11 @@ Thread::Thread(bool daemon) no_thread_suspension_(0), last_no_thread_suspension_cause_(NULL), checkpoint_function_(0), - thread_exit_check_count_(0) { + thread_exit_check_count_(0), + thread_local_start_(nullptr), + thread_local_pos_(nullptr), + thread_local_end_(nullptr), + thread_local_objects_(0) { CHECK_EQ((sizeof(Thread) % 4), 0U) << sizeof(Thread); state_and_flags_.as_struct.flags = 0; state_and_flags_.as_struct.state = kNative; @@ -2179,6 +2183,14 @@ void Thread::SetStackEndForStackOverflow() { stack_end_ = stack_begin_; } +void Thread::SetTLAB(byte* start, byte* end) { + DCHECK_LE(start, end); + thread_local_start_ = start; + thread_local_pos_ = thread_local_start_; + thread_local_end_ = end; + thread_local_objects_ = 0; +} + std::ostream& operator<<(std::ostream& os, const Thread& thread) { thread.ShortDump(os); return os; diff --git a/runtime/thread.h b/runtime/thread.h index 44b2186357c..b01ec945de0 100644 --- a/runtime/thread.h +++ b/runtime/thread.h @@ -577,10 +577,8 @@ class PACKED(4) Thread { ~Thread() LOCKS_EXCLUDED(Locks::mutator_lock_, Locks::thread_suspend_count_lock_); void Destroy(); - friend class ThreadList; // For ~Thread and Destroy. void CreatePeer(const char* name, bool as_daemon, jobject thread_group); - friend class Runtime; // For CreatePeer. // Avoid use, callers should use SetState. Used only by SignalCatcher::HandleSigQuit, ~Thread and // Dbg::Disconnected. @@ -589,8 +587,6 @@ class PACKED(4) Thread { state_and_flags_.as_struct.state = new_state; return old_state; } - friend class SignalCatcher; // For SetStateUnsafe. - friend class Dbg; // F or SetStateUnsafe. void VerifyStackImpl() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); @@ -731,9 +727,6 @@ class PACKED(4) Thread { // If we're blocked in MonitorEnter, this is the object we're trying to lock. mirror::Object* monitor_enter_object_; - friend class Monitor; - friend class MonitorInfo; - // Top of linked list of stack indirect reference tables or NULL for none StackIndirectReferenceTable* top_sirt_; @@ -799,13 +792,20 @@ class PACKED(4) Thread { PortableEntryPoints portable_entrypoints_; QuickEntryPoints quick_entrypoints_; - private: // How many times has our pthread key's destructor been called? uint32_t thread_exit_check_count_; - friend class ScopedThreadStateChange; + // Thread-local allocation pointer. + byte* thread_local_start_; + byte* thread_local_pos_; + byte* thread_local_end_; + size_t thread_local_objects_; + // Returns the remaining space in the TLAB. + size_t TLABSize() const; + // Doesn't check that there is room. + mirror::Object* AllocTLAB(size_t bytes); + void SetTLAB(byte* start, byte* end); - public: // Thread-local rosalloc runs. There are 34 size brackets in rosalloc // runs (RosAlloc::kNumOfSizeBrackets). We can't refer to the // RosAlloc class due to a header file circular dependency issue. @@ -814,6 +814,15 @@ class PACKED(4) Thread { static const size_t kRosAllocNumOfSizeBrackets = 34; void* rosalloc_runs_[kRosAllocNumOfSizeBrackets]; + private: + friend class Dbg; // F or SetStateUnsafe. + friend class Monitor; + friend class MonitorInfo; + friend class Runtime; // For CreatePeer. + friend class ScopedThreadStateChange; + friend class SignalCatcher; // For SetStateUnsafe. + friend class ThreadList; // For ~Thread and Destroy. + DISALLOW_COPY_AND_ASSIGN(Thread); }; From 2b5eaa2b49f7489bafdadc4b4463ae27e4261817 Mon Sep 17 00:00:00 2001 From: Vladimir Marko Date: Fri, 13 Dec 2013 13:59:30 +0000 Subject: [PATCH 0291/2402] Move compiler code out of method verifier. We want to detect small methods for inlining at the end of the method verification. Instead of adding more compiler code to the runtime, we create a callback from the runtime into the compiler, so that we can keep the code there. Additionally, we move the compiler-related code that was already in the method verifier to the compiler since it doesn't really belong to the runtime in the first place. Change-Id: I708ca13227c809e07917ff3879a89722017e83a9 --- compiler/Android.mk | 1 + compiler/dex/quick/codegen_util.cc | 4 +- compiler/dex/verified_methods_data.cc | 451 ++++++++++++++++++++++++ compiler/dex/verified_methods_data.h | 115 ++++++ compiler/driver/compiler_driver.cc | 15 +- compiler/driver/compiler_driver.h | 10 +- compiler/llvm/compiler_llvm.cc | 4 +- compiler/oat_writer.cc | 5 +- compiler/sea_ir/frontend.cc | 2 +- dex2oat/dex2oat.cc | 45 ++- runtime/class_linker.cc | 5 +- runtime/compiler_callbacks.h | 44 +++ runtime/runtime.cc | 13 +- runtime/runtime.h | 11 +- runtime/verifier/dex_gc_map.h | 2 - runtime/verifier/method_verifier-inl.h | 78 ++++ runtime/verifier/method_verifier.cc | 469 +------------------------ runtime/verifier/method_verifier.h | 81 +---- 18 files changed, 795 insertions(+), 560 deletions(-) create mode 100644 compiler/dex/verified_methods_data.cc create mode 100644 compiler/dex/verified_methods_data.h create mode 100644 runtime/compiler_callbacks.h create mode 100644 runtime/verifier/method_verifier-inl.h diff --git a/compiler/Android.mk b/compiler/Android.mk index a2419d5c2ee..d9a573f69b8 100644 --- a/compiler/Android.mk +++ b/compiler/Android.mk @@ -57,6 +57,7 @@ LIBART_COMPILER_SRC_FILES := \ dex/frontend.cc \ dex/mir_graph.cc \ dex/mir_analysis.cc \ + dex/verified_methods_data.cc \ dex/vreg_analysis.cc \ dex/ssa_transformation.cc \ driver/compiler_driver.cc \ diff --git a/compiler/dex/quick/codegen_util.cc b/compiler/dex/quick/codegen_util.cc index 5d78ed58486..bae8ff339a0 100644 --- a/compiler/dex/quick/codegen_util.cc +++ b/compiler/dex/quick/codegen_util.cc @@ -19,6 +19,7 @@ #include "gc_map.h" #include "mapping_table.h" #include "mir_to_lir-inl.h" +#include "dex/verified_methods_data.h" #include "verifier/dex_gc_map.h" #include "verifier/method_verifier.h" @@ -746,7 +747,8 @@ void Mir2Lir::CreateNativeGcMap() { } } MethodReference method_ref(cu_->dex_file, cu_->method_idx); - const std::vector* gc_map_raw = verifier::MethodVerifier::GetDexGcMap(method_ref); + const std::vector* gc_map_raw = + cu_->compiler_driver->GetVerifiedMethodsData()->GetDexGcMap(method_ref); verifier::DexPcToReferenceMap dex_gc_map(&(*gc_map_raw)[0]); DCHECK_EQ(gc_map_raw->size(), dex_gc_map.RawSize()); // Compute native offset to references size. diff --git a/compiler/dex/verified_methods_data.cc b/compiler/dex/verified_methods_data.cc new file mode 100644 index 00000000000..454b92cd851 --- /dev/null +++ b/compiler/dex/verified_methods_data.cc @@ -0,0 +1,451 @@ +/* + * Copyright (C) 2013 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 "base/stl_util.h" +#include "dex_file.h" +#include "dex_instruction.h" +#include "dex_instruction-inl.h" +#include "base/mutex.h" +#include "base/mutex-inl.h" +#include "mirror/art_method.h" +#include "mirror/art_method-inl.h" +#include "mirror/class.h" +#include "mirror/class-inl.h" +#include "mirror/dex_cache.h" +#include "mirror/dex_cache-inl.h" +#include "mirror/object.h" +#include "mirror/object-inl.h" +#include "verified_methods_data.h" +#include "verifier/dex_gc_map.h" +#include "verifier/method_verifier.h" +#include "verifier/method_verifier-inl.h" +#include "verifier/register_line.h" +#include "verifier/register_line-inl.h" + +namespace art { + +VerifiedMethodsData::VerifiedMethodsData() + : dex_gc_maps_lock_("compiler GC maps lock"), + dex_gc_maps_(), + safecast_map_lock_("compiler Cast Elision lock"), + safecast_map_(), + devirt_maps_lock_("compiler Devirtualization lock"), + devirt_maps_(), + rejected_classes_lock_("compiler rejected classes lock"), + rejected_classes_() { +} + +VerifiedMethodsData::~VerifiedMethodsData() { + Thread* self = Thread::Current(); + { + WriterMutexLock mu(self, dex_gc_maps_lock_); + STLDeleteValues(&dex_gc_maps_); + } + { + WriterMutexLock mu(self, safecast_map_lock_); + STLDeleteValues(&safecast_map_); + } + { + WriterMutexLock mu(self, devirt_maps_lock_); + STLDeleteValues(&devirt_maps_); + } +} + +bool VerifiedMethodsData::ProcessVerifiedMethod(verifier::MethodVerifier* method_verifier) { + MethodReference ref = method_verifier->GetMethodReference(); + bool compile = IsCandidateForCompilation(ref, method_verifier->GetAccessFlags()); + if (compile) { + /* Generate a register map and add it to the method. */ + const std::vector* dex_gc_map = GenerateGcMap(method_verifier); + if (dex_gc_map == NULL) { + DCHECK(method_verifier->HasFailures()); + return false; // Not a real failure, but a failure to encode + } + if (kIsDebugBuild) { + VerifyGcMap(method_verifier, *dex_gc_map); + } + SetDexGcMap(ref, dex_gc_map); + } + + if (method_verifier->HasCheckCasts()) { + MethodSafeCastSet* method_to_safe_casts = GenerateSafeCastSet(method_verifier); + if (method_to_safe_casts != NULL) { + SetSafeCastMap(ref, method_to_safe_casts); + } + } + + if (method_verifier->HasVirtualOrInterfaceInvokes()) { + PcToConcreteMethodMap* pc_to_concrete_method = GenerateDevirtMap(method_verifier); + if (pc_to_concrete_method != NULL) { + SetDevirtMap(ref, pc_to_concrete_method); + } + } + return true; +} + +const std::vector* VerifiedMethodsData::GetDexGcMap(MethodReference ref) { + ReaderMutexLock mu(Thread::Current(), dex_gc_maps_lock_); + DexGcMapTable::const_iterator it = dex_gc_maps_.find(ref); + CHECK(it != dex_gc_maps_.end()) + << "Didn't find GC map for: " << PrettyMethod(ref.dex_method_index, *ref.dex_file); + CHECK(it->second != NULL); + return it->second; +} + +const MethodReference* VerifiedMethodsData::GetDevirtMap(const MethodReference& ref, + uint32_t dex_pc) { + ReaderMutexLock mu(Thread::Current(), devirt_maps_lock_); + DevirtualizationMapTable::const_iterator it = devirt_maps_.find(ref); + if (it == devirt_maps_.end()) { + return NULL; + } + + // Look up the PC in the map, get the concrete method to execute and return its reference. + PcToConcreteMethodMap::const_iterator pc_to_concrete_method = it->second->find(dex_pc); + if (pc_to_concrete_method != it->second->end()) { + return &(pc_to_concrete_method->second); + } else { + return NULL; + } +} + +bool VerifiedMethodsData::IsSafeCast(MethodReference ref, uint32_t pc) { + ReaderMutexLock mu(Thread::Current(), safecast_map_lock_); + SafeCastMap::const_iterator it = safecast_map_.find(ref); + if (it == safecast_map_.end()) { + return false; + } + + // Look up the cast address in the set of safe casts + MethodSafeCastSet::const_iterator cast_it = it->second->find(pc); + return cast_it != it->second->end(); +} + +void VerifiedMethodsData::AddRejectedClass(ClassReference ref) { + { + WriterMutexLock mu(Thread::Current(), rejected_classes_lock_); + rejected_classes_.insert(ref); + } + DCHECK(IsClassRejected(ref)); +} + +bool VerifiedMethodsData::IsClassRejected(ClassReference ref) { + ReaderMutexLock mu(Thread::Current(), rejected_classes_lock_); + return (rejected_classes_.find(ref) != rejected_classes_.end()); +} + +bool VerifiedMethodsData::IsCandidateForCompilation(MethodReference& method_ref, + const uint32_t access_flags) { +#ifdef ART_SEA_IR_MODE + bool use_sea = Runtime::Current()->IsSeaIRMode(); + use_sea = use_sea && (std::string::npos != PrettyMethod( + method_ref.dex_method_index, *(method_ref.dex_file)).find("fibonacci")); + if (use_sea) return true; +#endif + // Don't compile class initializers, ever. + if (((access_flags & kAccConstructor) != 0) && ((access_flags & kAccStatic) != 0)) { + return false; + } + return (Runtime::Current()->GetCompilerFilter() != Runtime::kInterpretOnly); +} + +const std::vector* VerifiedMethodsData::GenerateGcMap( + verifier::MethodVerifier* method_verifier) { + size_t num_entries, ref_bitmap_bits, pc_bits; + ComputeGcMapSizes(method_verifier, &num_entries, &ref_bitmap_bits, &pc_bits); + // There's a single byte to encode the size of each bitmap + if (ref_bitmap_bits >= (8 /* bits per byte */ * 8192 /* 13-bit size */ )) { + // TODO: either a better GC map format or per method failures + method_verifier->Fail(verifier::VERIFY_ERROR_BAD_CLASS_HARD) + << "Cannot encode GC map for method with " << ref_bitmap_bits << " registers"; + return NULL; + } + size_t ref_bitmap_bytes = (ref_bitmap_bits + 7) / 8; + // There are 2 bytes to encode the number of entries + if (num_entries >= 65536) { + // TODO: either a better GC map format or per method failures + method_verifier->Fail(verifier::VERIFY_ERROR_BAD_CLASS_HARD) + << "Cannot encode GC map for method with " << num_entries << " entries"; + return NULL; + } + size_t pc_bytes; + verifier::RegisterMapFormat format; + if (pc_bits <= 8) { + format = verifier::kRegMapFormatCompact8; + pc_bytes = 1; + } else if (pc_bits <= 16) { + format = verifier::kRegMapFormatCompact16; + pc_bytes = 2; + } else { + // TODO: either a better GC map format or per method failures + method_verifier->Fail(verifier::VERIFY_ERROR_BAD_CLASS_HARD) + << "Cannot encode GC map for method with " + << (1 << pc_bits) << " instructions (number is rounded up to nearest power of 2)"; + return NULL; + } + size_t table_size = ((pc_bytes + ref_bitmap_bytes) * num_entries) + 4; + std::vector* table = new std::vector; + if (table == NULL) { + method_verifier->Fail(verifier::VERIFY_ERROR_BAD_CLASS_HARD) + << "Failed to encode GC map (size=" << table_size << ")"; + return NULL; + } + table->reserve(table_size); + // Write table header + table->push_back(format | ((ref_bitmap_bytes & ~0xFF) >> 5)); + table->push_back(ref_bitmap_bytes & 0xFF); + table->push_back(num_entries & 0xFF); + table->push_back((num_entries >> 8) & 0xFF); + // Write table data + const DexFile::CodeItem* code_item = method_verifier->CodeItem(); + for (size_t i = 0; i < code_item->insns_size_in_code_units_; i++) { + if (method_verifier->GetInstructionFlags(i).IsCompileTimeInfoPoint()) { + table->push_back(i & 0xFF); + if (pc_bytes == 2) { + table->push_back((i >> 8) & 0xFF); + } + verifier::RegisterLine* line = method_verifier->GetRegLine(i); + line->WriteReferenceBitMap(*table, ref_bitmap_bytes); + } + } + DCHECK_EQ(table->size(), table_size); + return table; +} + +void VerifiedMethodsData::VerifyGcMap(verifier::MethodVerifier* method_verifier, + const std::vector& data) { + // Check that for every GC point there is a map entry, there aren't entries for non-GC points, + // that the table data is well formed and all references are marked (or not) in the bitmap + verifier::DexPcToReferenceMap map(&data[0]); + DCHECK_EQ(data.size(), map.RawSize()); + size_t map_index = 0; + const DexFile::CodeItem* code_item = method_verifier->CodeItem(); + for (size_t i = 0; i < code_item->insns_size_in_code_units_; i++) { + const uint8_t* reg_bitmap = map.FindBitMap(i, false); + if (method_verifier->GetInstructionFlags(i).IsCompileTimeInfoPoint()) { + CHECK_LT(map_index, map.NumEntries()); + CHECK_EQ(map.GetDexPc(map_index), i); + CHECK_EQ(map.GetBitMap(map_index), reg_bitmap); + map_index++; + verifier::RegisterLine* line = method_verifier->GetRegLine(i); + for (size_t j = 0; j < code_item->registers_size_; j++) { + if (line->GetRegisterType(j).IsNonZeroReferenceTypes()) { + CHECK_LT(j / 8, map.RegWidth()); + CHECK_EQ((reg_bitmap[j / 8] >> (j % 8)) & 1, 1); + } else if ((j / 8) < map.RegWidth()) { + CHECK_EQ((reg_bitmap[j / 8] >> (j % 8)) & 1, 0); + } else { + // If a register doesn't contain a reference then the bitmap may be shorter than the line + } + } + } else { + CHECK(reg_bitmap == NULL); + } + } +} + +void VerifiedMethodsData::ComputeGcMapSizes(verifier::MethodVerifier* method_verifier, + size_t* gc_points, size_t* ref_bitmap_bits, + size_t* log2_max_gc_pc) { + size_t local_gc_points = 0; + size_t max_insn = 0; + size_t max_ref_reg = -1; + const DexFile::CodeItem* code_item = method_verifier->CodeItem(); + for (size_t i = 0; i < code_item->insns_size_in_code_units_; i++) { + if (method_verifier->GetInstructionFlags(i).IsCompileTimeInfoPoint()) { + local_gc_points++; + max_insn = i; + verifier::RegisterLine* line = method_verifier->GetRegLine(i); + max_ref_reg = line->GetMaxNonZeroReferenceReg(max_ref_reg); + } + } + *gc_points = local_gc_points; + *ref_bitmap_bits = max_ref_reg + 1; // if max register is 0 we need 1 bit to encode (ie +1) + size_t i = 0; + while ((1U << i) <= max_insn) { + i++; + } + *log2_max_gc_pc = i; +} + +void VerifiedMethodsData::SetDexGcMap(MethodReference ref, const std::vector* gc_map) { + DCHECK(Runtime::Current()->IsCompiler()); + { + WriterMutexLock mu(Thread::Current(), dex_gc_maps_lock_); + DexGcMapTable::iterator it = dex_gc_maps_.find(ref); + if (it != dex_gc_maps_.end()) { + delete it->second; + dex_gc_maps_.erase(it); + } + dex_gc_maps_.Put(ref, gc_map); + } + DCHECK(GetDexGcMap(ref) != NULL); +} + +VerifiedMethodsData::MethodSafeCastSet* VerifiedMethodsData::GenerateSafeCastSet( + verifier::MethodVerifier* method_verifier) { + /* + * Walks over the method code and adds any cast instructions in which + * the type cast is implicit to a set, which is used in the code generation + * to elide these casts. + */ + if (method_verifier->HasFailures()) { + return NULL; + } + UniquePtr mscs; + const DexFile::CodeItem* code_item = method_verifier->CodeItem(); + const Instruction* inst = Instruction::At(code_item->insns_); + const Instruction* end = Instruction::At(code_item->insns_ + + code_item->insns_size_in_code_units_); + + for (; inst < end; inst = inst->Next()) { + Instruction::Code code = inst->Opcode(); + if ((code == Instruction::CHECK_CAST) || (code == Instruction::APUT_OBJECT)) { + uint32_t dex_pc = inst->GetDexPc(code_item->insns_); + const verifier::RegisterLine* line = method_verifier->GetRegLine(dex_pc); + bool is_safe_cast = false; + if (code == Instruction::CHECK_CAST) { + const verifier::RegType& reg_type(line->GetRegisterType(inst->VRegA_21c())); + const verifier::RegType& cast_type = + method_verifier->ResolveCheckedClass(inst->VRegB_21c()); + is_safe_cast = cast_type.IsStrictlyAssignableFrom(reg_type); + } else { + const verifier::RegType& array_type(line->GetRegisterType(inst->VRegB_23x())); + // We only know its safe to assign to an array if the array type is precise. For example, + // an Object[] can have any type of object stored in it, but it may also be assigned a + // String[] in which case the stores need to be of Strings. + if (array_type.IsPreciseReference()) { + const verifier::RegType& value_type(line->GetRegisterType(inst->VRegA_23x())); + const verifier::RegType& component_type = method_verifier->GetRegTypeCache() + ->GetComponentType(array_type, method_verifier->GetClassLoader()); + is_safe_cast = component_type.IsStrictlyAssignableFrom(value_type); + } + } + if (is_safe_cast) { + if (mscs.get() == nullptr) { + mscs.reset(new MethodSafeCastSet()); + } + mscs->insert(dex_pc); + } + } + } + return mscs.release(); +} + +void VerifiedMethodsData::SetSafeCastMap(MethodReference ref, const MethodSafeCastSet* cast_set) { + WriterMutexLock mu(Thread::Current(), safecast_map_lock_); + SafeCastMap::iterator it = safecast_map_.find(ref); + if (it != safecast_map_.end()) { + delete it->second; + safecast_map_.erase(it); + } + safecast_map_.Put(ref, cast_set); + DCHECK(safecast_map_.find(ref) != safecast_map_.end()); +} + +VerifiedMethodsData::PcToConcreteMethodMap* VerifiedMethodsData::GenerateDevirtMap( + verifier::MethodVerifier* method_verifier) { + // It is risky to rely on reg_types for sharpening in cases of soft + // verification, we might end up sharpening to a wrong implementation. Just abort. + if (method_verifier->HasFailures()) { + return NULL; + } + + UniquePtr pc_to_concrete_method_map; + const DexFile::CodeItem* code_item = method_verifier->CodeItem(); + const uint16_t* insns = code_item->insns_; + const Instruction* inst = Instruction::At(insns); + const Instruction* end = Instruction::At(insns + code_item->insns_size_in_code_units_); + + for (; inst < end; inst = inst->Next()) { + bool is_virtual = (inst->Opcode() == Instruction::INVOKE_VIRTUAL) || + (inst->Opcode() == Instruction::INVOKE_VIRTUAL_RANGE); + bool is_interface = (inst->Opcode() == Instruction::INVOKE_INTERFACE) || + (inst->Opcode() == Instruction::INVOKE_INTERFACE_RANGE); + + if (!is_interface && !is_virtual) { + continue; + } + // Get reg type for register holding the reference to the object that will be dispatched upon. + uint32_t dex_pc = inst->GetDexPc(insns); + verifier::RegisterLine* line = method_verifier->GetRegLine(dex_pc); + bool is_range = (inst->Opcode() == Instruction::INVOKE_VIRTUAL_RANGE) || + (inst->Opcode() == Instruction::INVOKE_INTERFACE_RANGE); + const verifier::RegType& + reg_type(line->GetRegisterType(is_range ? inst->VRegC_3rc() : inst->VRegC_35c())); + + if (!reg_type.HasClass()) { + // We will compute devirtualization information only when we know the Class of the reg type. + continue; + } + mirror::Class* reg_class = reg_type.GetClass(); + if (reg_class->IsInterface()) { + // We can't devirtualize when the known type of the register is an interface. + continue; + } + if (reg_class->IsAbstract() && !reg_class->IsArrayClass()) { + // We can't devirtualize abstract classes except on arrays of abstract classes. + continue; + } + mirror::ArtMethod* abstract_method = method_verifier->GetDexCache()->GetResolvedMethod( + is_range ? inst->VRegB_3rc() : inst->VRegB_35c()); + if (abstract_method == NULL) { + // If the method is not found in the cache this means that it was never found + // by ResolveMethodAndCheckAccess() called when verifying invoke_*. + continue; + } + // Find the concrete method. + mirror::ArtMethod* concrete_method = NULL; + if (is_interface) { + concrete_method = reg_type.GetClass()->FindVirtualMethodForInterface(abstract_method); + } + if (is_virtual) { + concrete_method = reg_type.GetClass()->FindVirtualMethodForVirtual(abstract_method); + } + if (concrete_method == NULL || concrete_method->IsAbstract()) { + // In cases where concrete_method is not found, or is abstract, continue to the next invoke. + continue; + } + if (reg_type.IsPreciseReference() || concrete_method->IsFinal() || + concrete_method->GetDeclaringClass()->IsFinal()) { + // If we knew exactly the class being dispatched upon, or if the target method cannot be + // overridden record the target to be used in the compiler driver. + if (pc_to_concrete_method_map.get() == NULL) { + pc_to_concrete_method_map.reset(new PcToConcreteMethodMap()); + } + MethodReference concrete_ref( + concrete_method->GetDeclaringClass()->GetDexCache()->GetDexFile(), + concrete_method->GetDexMethodIndex()); + pc_to_concrete_method_map->Put(dex_pc, concrete_ref); + } + } + return pc_to_concrete_method_map.release(); +} + +void VerifiedMethodsData::SetDevirtMap(MethodReference ref, + const PcToConcreteMethodMap* devirt_map) { + WriterMutexLock mu(Thread::Current(), devirt_maps_lock_); + DevirtualizationMapTable::iterator it = devirt_maps_.find(ref); + if (it != devirt_maps_.end()) { + delete it->second; + devirt_maps_.erase(it); + } + + devirt_maps_.Put(ref, devirt_map); + DCHECK(devirt_maps_.find(ref) != devirt_maps_.end()); +} + +} // namespace art diff --git a/compiler/dex/verified_methods_data.h b/compiler/dex/verified_methods_data.h new file mode 100644 index 00000000000..8d5058ab2f9 --- /dev/null +++ b/compiler/dex/verified_methods_data.h @@ -0,0 +1,115 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ART_COMPILER_DEX_VERIFIED_METHODS_DATA_H_ +#define ART_COMPILER_DEX_VERIFIED_METHODS_DATA_H_ + +#include +#include +#include + +#include "base/macros.h" +#include "base/mutex.h" +#include "class_reference.h" +#include "method_reference.h" +#include "safe_map.h" + +namespace art { + +namespace verifier { +class MethodVerifier; +} // namespace verifier + +class VerifiedMethodsData { + public: + VerifiedMethodsData(); + ~VerifiedMethodsData(); + + bool ProcessVerifiedMethod(verifier::MethodVerifier* method_verifier) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) + LOCKS_EXCLUDED(dex_gc_maps_lock_, devirt_maps_lock_, safecast_map_lock_); + + const std::vector* GetDexGcMap(MethodReference ref) + LOCKS_EXCLUDED(dex_gc_maps_lock_); + + const MethodReference* GetDevirtMap(const MethodReference& ref, uint32_t dex_pc) + LOCKS_EXCLUDED(devirt_maps_lock_); + + // Returns true if the cast can statically be verified to be redundant + // by using the check-cast elision peephole optimization in the verifier + bool IsSafeCast(MethodReference ref, uint32_t pc) LOCKS_EXCLUDED(safecast_map_lock_); + + void AddRejectedClass(ClassReference ref) LOCKS_EXCLUDED(rejected_classes_lock_); + bool IsClassRejected(ClassReference ref) LOCKS_EXCLUDED(rejected_classes_lock_); + + static bool IsCandidateForCompilation(MethodReference& method_ref, + const uint32_t access_flags); + + private: + /* + * Generate the GC map for a method that has just been verified (i.e. we're doing this as part of + * verification). For type-precise determination we have all the data we need, so we just need to + * encode it in some clever fashion. + * Returns a pointer to a newly-allocated RegisterMap, or NULL on failure. + */ + const std::vector* GenerateGcMap(verifier::MethodVerifier* method_verifier); + + // Verify that the GC map associated with method_ is well formed + void VerifyGcMap(verifier::MethodVerifier* method_verifier, const std::vector& data); + + // Compute sizes for GC map data + void ComputeGcMapSizes(verifier::MethodVerifier* method_verifier, + size_t* gc_points, size_t* ref_bitmap_bits, size_t* log2_max_gc_pc); + + // All the GC maps that the verifier has created + typedef SafeMap*, + MethodReferenceComparator> DexGcMapTable; + ReaderWriterMutex dex_gc_maps_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER; + DexGcMapTable dex_gc_maps_ GUARDED_BY(dex_gc_maps_lock_); + void SetDexGcMap(MethodReference ref, const std::vector* dex_gc_map) + LOCKS_EXCLUDED(dex_gc_maps_lock_); + + // Cast elision types. + typedef std::set MethodSafeCastSet; + typedef SafeMap SafeCastMap; + MethodSafeCastSet* GenerateSafeCastSet(verifier::MethodVerifier* method_verifier) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + void SetSafeCastMap(MethodReference ref, const MethodSafeCastSet* mscs) + LOCKS_EXCLUDED(safecast_map_lock_); + ReaderWriterMutex safecast_map_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER; + SafeCastMap safecast_map_ GUARDED_BY(safecast_map_lock_); + + // Devirtualization map. + typedef SafeMap PcToConcreteMethodMap; + typedef SafeMap DevirtualizationMapTable; + PcToConcreteMethodMap* GenerateDevirtMap(verifier::MethodVerifier* method_verifier) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + ReaderWriterMutex devirt_maps_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER; + DevirtualizationMapTable devirt_maps_ GUARDED_BY(devirt_maps_lock_); + void SetDevirtMap(MethodReference ref, const PcToConcreteMethodMap* pc_method_map) + LOCKS_EXCLUDED(devirt_maps_lock_); + + // Rejected classes + typedef std::set RejectedClassesTable; + ReaderWriterMutex rejected_classes_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER; + RejectedClassesTable rejected_classes_ GUARDED_BY(rejected_classes_lock_); +}; + +} // namespace art + +#endif // ART_COMPILER_DEX_VERIFIED_METHODS_DATA_H_ diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc index af18c05fbb5..dbebd26db83 100644 --- a/compiler/driver/compiler_driver.cc +++ b/compiler/driver/compiler_driver.cc @@ -27,6 +27,7 @@ #include "class_linker.h" #include "dex_compilation_unit.h" #include "dex_file-inl.h" +#include "dex/verified_methods_data.h" #include "jni_internal.h" #include "object_utils.h" #include "runtime.h" @@ -335,11 +336,13 @@ extern "C" art::CompiledMethod* ArtQuickJniCompileMethod(art::CompilerDriver& co extern "C" void compilerLLVMSetBitcodeFileName(art::CompilerDriver& driver, std::string const& filename); -CompilerDriver::CompilerDriver(CompilerBackend compiler_backend, InstructionSet instruction_set, +CompilerDriver::CompilerDriver(VerifiedMethodsData* verified_methods_data, + CompilerBackend compiler_backend, InstructionSet instruction_set, InstructionSetFeatures instruction_set_features, bool image, DescriptorSet* image_classes, size_t thread_count, bool dump_stats) - : compiler_backend_(compiler_backend), + : verified_methods_data_(verified_methods_data), + compiler_backend_(compiler_backend), instruction_set_(instruction_set), instruction_set_features_(instruction_set_features), freezing_constructor_lock_("freezing constructor lock"), @@ -1253,7 +1256,7 @@ bool CompilerDriver::ComputeInvokeInfo(const DexCompilationUnit* mUnit, const ui // Did the verifier record a more precise invoke target based on its type information? const MethodReference caller_method(mUnit->GetDexFile(), mUnit->GetDexMethodIndex()); const MethodReference* devirt_map_target = - verifier::MethodVerifier::GetDevirtMap(caller_method, dex_pc); + verified_methods_data_->GetDevirtMap(caller_method, dex_pc); if (devirt_map_target != NULL) { SirtRef target_dex_cache(soa.Self(), mUnit->GetClassLinker()->FindDexCache(*devirt_map_target->dex_file)); SirtRef class_loader(soa.Self(), soa.Decode(mUnit->GetClassLoader())); @@ -1301,7 +1304,7 @@ bool CompilerDriver::ComputeInvokeInfo(const DexCompilationUnit* mUnit, const ui } bool CompilerDriver::IsSafeCast(const MethodReference& mr, uint32_t dex_pc) { - bool result = verifier::MethodVerifier::IsSafeCast(mr, dex_pc); + bool result = verified_methods_data_->IsSafeCast(mr, dex_pc); if (result) { stats_->SafeCast(); } else { @@ -2240,7 +2243,7 @@ void CompilerDriver::CompileClass(const ParallelCompilationManager* manager, siz } ClassReference ref(&dex_file, class_def_index); // Skip compiling classes with generic verifier failures since they will still fail at runtime - if (verifier::MethodVerifier::IsClassRejected(ref)) { + if (manager->GetCompiler()->verified_methods_data_->IsClassRejected(ref)) { return; } const byte* class_data = dex_file.GetClassData(class_def); @@ -2323,7 +2326,7 @@ void CompilerDriver::CompileMethod(const DexFile::CodeItem* code_item, uint32_t } else if ((access_flags & kAccAbstract) != 0) { } else { MethodReference method_ref(&dex_file, method_idx); - bool compile = verifier::MethodVerifier::IsCandidateForCompilation(method_ref, access_flags); + bool compile = VerifiedMethodsData::IsCandidateForCompilation(method_ref, access_flags); if (compile) { CompilerFn compiler = compiler_; diff --git a/compiler/driver/compiler_driver.h b/compiler/driver/compiler_driver.h index 7e8184975c8..14bfde672d3 100644 --- a/compiler/driver/compiler_driver.h +++ b/compiler/driver/compiler_driver.h @@ -43,6 +43,7 @@ class ParallelCompilationManager; class DexCompilationUnit; class OatWriter; class TimingLogger; +class VerifiedMethodsData; enum CompilerBackend { kQuick, @@ -90,7 +91,8 @@ class CompilerDriver { // enabled. "image_classes" lets the compiler know what classes it // can assume will be in the image, with NULL implying all available // classes. - explicit CompilerDriver(CompilerBackend compiler_backend, InstructionSet instruction_set, + explicit CompilerDriver(VerifiedMethodsData* verified_methods_data, + CompilerBackend compiler_backend, InstructionSet instruction_set, InstructionSetFeatures instruction_set_features, bool image, DescriptorSet* image_classes, size_t thread_count, bool dump_stats); @@ -105,6 +107,10 @@ class CompilerDriver { void CompileOne(const mirror::ArtMethod* method, TimingLogger& timings) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + VerifiedMethodsData* GetVerifiedMethodsData() const { + return verified_methods_data_; + } + const InstructionSet& GetInstructionSet() const { return instruction_set_; } @@ -390,6 +396,8 @@ class CompilerDriver { std::vector code_to_patch_; std::vector methods_to_patch_; + VerifiedMethodsData* verified_methods_data_; + CompilerBackend compiler_backend_; const InstructionSet instruction_set_; diff --git a/compiler/llvm/compiler_llvm.cc b/compiler/llvm/compiler_llvm.cc index d59afd48b77..35d1ecd783d 100644 --- a/compiler/llvm/compiler_llvm.cc +++ b/compiler/llvm/compiler_llvm.cc @@ -20,6 +20,7 @@ #include "base/stl_util.h" #include "class_linker.h" #include "compiled_method.h" +#include "dex/verified_methods_data.h" #include "driver/compiler_driver.h" #include "driver/dex_compilation_unit.h" #include "globals.h" @@ -155,7 +156,8 @@ CompileDexMethod(DexCompilationUnit* dex_compilation_unit, InvokeType invoke_typ MethodReference mref(dex_compilation_unit->GetDexFile(), dex_compilation_unit->GetDexMethodIndex()); return new CompiledMethod(*compiler_driver_, compiler_driver_->GetInstructionSet(), - cunit->GetElfObject(), *verifier::MethodVerifier::GetDexGcMap(mref), + cunit->GetElfObject(), + *compiler_driver_->GetVerifiedMethodsData()->GetDexGcMap(mref), cunit->GetDexCompilationUnit()->GetSymbol()); } diff --git a/compiler/oat_writer.cc b/compiler/oat_writer.cc index 83824694ae4..199a2b8d58a 100644 --- a/compiler/oat_writer.cc +++ b/compiler/oat_writer.cc @@ -23,6 +23,7 @@ #include "base/unix_file/fd_file.h" #include "class_linker.h" #include "dex_file-inl.h" +#include "dex/verified_methods_data.h" #include "gc/space/space.h" #include "mirror/art_method-inl.h" #include "mirror/array.h" @@ -217,7 +218,7 @@ size_t OatWriter::InitOatClasses(size_t offset) { mirror::Class::Status status; if (compiled_class != NULL) { status = compiled_class->GetStatus(); - } else if (verifier::MethodVerifier::IsClassRejected(class_ref)) { + } else if (compiler_driver_->GetVerifiedMethodsData()->IsClassRejected(class_ref)) { status = mirror::Class::kStatusError; } else { status = mirror::Class::kStatusNotReady; @@ -432,7 +433,7 @@ size_t OatWriter::InitOatCodeMethod(size_t offset, size_t oat_class_index, mirror::Class::Status status; if (compiled_class != NULL) { status = compiled_class->GetStatus(); - } else if (verifier::MethodVerifier::IsClassRejected(class_ref)) { + } else if (compiler_driver_->GetVerifiedMethodsData()->IsClassRejected(class_ref)) { status = mirror::Class::kStatusError; } else { status = mirror::Class::kStatusNotReady; diff --git a/compiler/sea_ir/frontend.cc b/compiler/sea_ir/frontend.cc index 3512911f439..6c779c8d102 100644 --- a/compiler/sea_ir/frontend.cc +++ b/compiler/sea_ir/frontend.cc @@ -59,7 +59,7 @@ static CompiledMethod* CompileMethodWithSeaIr(CompilerDriver& compiler, std::string llvm_code = llvm_data->GetElf(compiler.GetInstructionSet()); CompiledMethod* compiled_method = new CompiledMethod(compiler, compiler.GetInstructionSet(), llvm_code, - *verifier::MethodVerifier::GetDexGcMap(mref), symbol); + *compiler.GetVerifiedMethodsData()->GetDexGcMap(mref), symbol); LOG(INFO) << "Compiled SEA IR method " << PrettyMethod(method_idx, dex_file) << "."; return compiled_method; } diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc index 28d6649ada2..b37cc544396 100644 --- a/dex2oat/dex2oat.cc +++ b/dex2oat/dex2oat.cc @@ -30,7 +30,9 @@ #include "base/timing_logger.h" #include "base/unix_file/fd_file.h" #include "class_linker.h" +#include "compiler_callbacks.h" #include "dex_file-inl.h" +#include "dex/verified_methods_data.h" #include "driver/compiler_driver.h" #include "elf_fixup.h" #include "elf_stripper.h" @@ -162,12 +164,13 @@ class Dex2Oat { InstructionSetFeatures instruction_set_features, size_t thread_count) SHARED_TRYLOCK_FUNCTION(true, Locks::mutator_lock_) { - if (!CreateRuntime(options, instruction_set)) { + UniquePtr dex2oat(new Dex2Oat(compiler_backend, instruction_set, + instruction_set_features, thread_count)); + if (!dex2oat->CreateRuntime(options, instruction_set)) { *p_dex2oat = NULL; return false; } - *p_dex2oat = new Dex2Oat(Runtime::Current(), compiler_backend, instruction_set, - instruction_set_features, thread_count); + *p_dex2oat = dex2oat.release(); return true; } @@ -261,7 +264,8 @@ class Dex2Oat { Runtime::Current()->SetCompileTimeClassPath(class_loader, class_path_files); } - UniquePtr driver(new CompilerDriver(compiler_backend_, + UniquePtr driver(new CompilerDriver(verified_methods_data_.get(), + compiler_backend_, instruction_set_, instruction_set_features_, image, @@ -337,21 +341,42 @@ class Dex2Oat { } private: - explicit Dex2Oat(Runtime* runtime, - CompilerBackend compiler_backend, + class Dex2OatCompilerCallbacks : public CompilerCallbacks { + public: + explicit Dex2OatCompilerCallbacks(VerifiedMethodsData* verified_methods_data) + : verified_methods_data_(verified_methods_data) { } + virtual ~Dex2OatCompilerCallbacks() { } + + virtual bool MethodVerified(verifier::MethodVerifier* verifier) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + return verified_methods_data_->ProcessVerifiedMethod(verifier); + } + virtual void ClassRejected(ClassReference ref) { + verified_methods_data_->AddRejectedClass(ref); + } + + private: + VerifiedMethodsData* verified_methods_data_; + }; + + explicit Dex2Oat(CompilerBackend compiler_backend, InstructionSet instruction_set, InstructionSetFeatures instruction_set_features, size_t thread_count) : compiler_backend_(compiler_backend), instruction_set_(instruction_set), instruction_set_features_(instruction_set_features), - runtime_(runtime), + verified_methods_data_(new VerifiedMethodsData), + callbacks_(verified_methods_data_.get()), + runtime_(nullptr), thread_count_(thread_count), start_ns_(NanoTime()) { } - static bool CreateRuntime(Runtime::Options& options, InstructionSet instruction_set) + bool CreateRuntime(Runtime::Options& options, InstructionSet instruction_set) SHARED_TRYLOCK_FUNCTION(true, Locks::mutator_lock_) { + options.push_back( + std::make_pair("compilercallbacks", static_cast(&callbacks_))); if (!Runtime::Create(options, false)) { LOG(ERROR) << "Failed to create runtime"; return false; @@ -364,6 +389,7 @@ class Dex2Oat { } } runtime->GetClassLinker()->FixupDexCaches(runtime->GetResolutionMethod()); + runtime_ = runtime; return true; } @@ -405,6 +431,8 @@ class Dex2Oat { const InstructionSet instruction_set_; const InstructionSetFeatures instruction_set_features_; + UniquePtr verified_methods_data_; + Dex2OatCompilerCallbacks callbacks_; Runtime* runtime_; size_t thread_count_; uint64_t start_ns_; @@ -899,7 +927,6 @@ static int dex2oat(int argc, char** argv) { } Runtime::Options options; - options.push_back(std::make_pair("compiler", reinterpret_cast(NULL))); std::vector boot_class_path; if (boot_image_option.empty()) { size_t failure_count = OpenDexFiles(dex_filenames, dex_locations, boot_class_path); diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index 643c183d1f1..fbb47bdfaee 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -32,6 +32,7 @@ #include "base/stl_util.h" #include "base/unix_file/fd_file.h" #include "class_linker-inl.h" +#include "compiler_callbacks.h" #include "debugger.h" #include "dex_file-inl.h" #include "gc/accounting/card_table-inl.h" @@ -2490,7 +2491,9 @@ void ClassLinker::VerifyClass(const SirtRef& klass) { self->GetException(nullptr)->SetCause(cause.get()); } ClassReference ref(klass->GetDexCache()->GetDexFile(), klass->GetDexClassDefIndex()); - verifier::MethodVerifier::AddRejectedClass(ref); + if (Runtime::Current()->IsCompiler()) { + Runtime::Current()->GetCompilerCallbacks()->ClassRejected(ref); + } klass->SetStatus(mirror::Class::kStatusError, self); return; } diff --git a/runtime/compiler_callbacks.h b/runtime/compiler_callbacks.h new file mode 100644 index 00000000000..9045f3af71f --- /dev/null +++ b/runtime/compiler_callbacks.h @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ART_RUNTIME_COMPILER_CALLBACKS_H_ +#define ART_RUNTIME_COMPILER_CALLBACKS_H_ + +#include "class_reference.h" + +namespace art { + +namespace verifier { + +class MethodVerifier; + +} // namespace verifier + +class CompilerCallbacks { + public: + virtual ~CompilerCallbacks() { } + + virtual bool MethodVerified(verifier::MethodVerifier* verifier) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) = 0; + virtual void ClassRejected(ClassReference ref) = 0; + + protected: + CompilerCallbacks() { } +}; + +} // namespace art + +#endif // ART_RUNTIME_COMPILER_CALLBACKS_H_ diff --git a/runtime/runtime.cc b/runtime/runtime.cc index ff7b8f5b69e..25623a1fe73 100644 --- a/runtime/runtime.cc +++ b/runtime/runtime.cc @@ -70,7 +70,7 @@ namespace art { Runtime* Runtime::instance_ = NULL; Runtime::Runtime() - : is_compiler_(false), + : compiler_callbacks_(nullptr), is_zygote_(false), is_concurrent_gc_enabled_(true), is_explicit_gc_disabled_(false), @@ -367,7 +367,7 @@ Runtime::ParsedOptions* Runtime::ParsedOptions::Create(const Options& options, b parsed->low_memory_mode_ = false; parsed->use_tlab_ = false; - parsed->is_compiler_ = false; + parsed->compiler_callbacks_ = nullptr; parsed->is_zygote_ = false; parsed->interpreter_only_ = false; parsed->is_explicit_gc_disabled_ = false; @@ -547,8 +547,9 @@ Runtime::ParsedOptions* Runtime::ParsedOptions::Create(const Options& options, b parsed->properties_.push_back(option.substr(strlen("-D"))); } else if (StartsWith(option, "-Xjnitrace:")) { parsed->jni_trace_ = option.substr(strlen("-Xjnitrace:")); - } else if (option == "compiler") { - parsed->is_compiler_ = true; + } else if (option == "compilercallbacks") { + parsed->compiler_callbacks_ = + reinterpret_cast(const_cast(options[i].second)); } else if (option == "-Xzygote") { parsed->is_zygote_ = true; } else if (option == "-Xint") { @@ -672,7 +673,7 @@ Runtime::ParsedOptions* Runtime::ParsedOptions::Create(const Options& options, b parsed->boot_class_path_string_.replace(core_jar_pos, core_jar.size(), "/core-libart.jar"); } - if (!parsed->is_compiler_ && parsed->image_.empty()) { + if (parsed->compiler_callbacks_ == nullptr && parsed->image_.empty()) { parsed->image_ += GetAndroidRoot(); parsed->image_ += "/framework/boot.art"; } @@ -885,7 +886,7 @@ bool Runtime::Init(const Options& raw_options, bool ignore_unrecognized) { class_path_string_ = options->class_path_string_; properties_ = options->properties_; - is_compiler_ = options->is_compiler_; + compiler_callbacks_ = options->compiler_callbacks_; is_zygote_ = options->is_zygote_; is_explicit_gc_disabled_ = options->is_explicit_gc_disabled_; diff --git a/runtime/runtime.h b/runtime/runtime.h index ce64510af56..7b57dda7805 100644 --- a/runtime/runtime.h +++ b/runtime/runtime.h @@ -56,6 +56,7 @@ namespace verifier { class MethodVerifier; } class ClassLinker; +class CompilerCallbacks; class DexFile; class InternTable; struct JavaVMExt; @@ -100,7 +101,7 @@ class Runtime { std::string image_; bool check_jni_; std::string jni_trace_; - bool is_compiler_; + CompilerCallbacks* compiler_callbacks_; bool is_zygote_; bool interpreter_only_; bool is_explicit_gc_disabled_; @@ -148,7 +149,11 @@ class Runtime { SHARED_TRYLOCK_FUNCTION(true, Locks::mutator_lock_); bool IsCompiler() const { - return is_compiler_; + return compiler_callbacks_ != nullptr; + } + + CompilerCallbacks* GetCompilerCallbacks() { + return compiler_callbacks_; } bool IsZygote() const { @@ -469,7 +474,7 @@ class Runtime { // A pointer to the active runtime or NULL. static Runtime* instance_; - bool is_compiler_; + CompilerCallbacks* compiler_callbacks_; bool is_zygote_; bool is_concurrent_gc_enabled_; bool is_explicit_gc_disabled_; diff --git a/runtime/verifier/dex_gc_map.h b/runtime/verifier/dex_gc_map.h index 4570ae820e9..d77ea650fe9 100644 --- a/runtime/verifier/dex_gc_map.h +++ b/runtime/verifier/dex_gc_map.h @@ -110,8 +110,6 @@ class DexPcToReferenceMap { return data_; } - friend class MethodVerifier; - static const int kRegMapFormatShift = 5; static const uint8_t kRegMapFormatMask = 0x7; diff --git a/runtime/verifier/method_verifier-inl.h b/runtime/verifier/method_verifier-inl.h new file mode 100644 index 00000000000..5cf234ddb54 --- /dev/null +++ b/runtime/verifier/method_verifier-inl.h @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ART_RUNTIME_VERIFIER_METHOD_VERIFIER_INL_H_ +#define ART_RUNTIME_VERIFIER_METHOD_VERIFIER_INL_H_ + +#include "base/logging.h" +#include "method_verifier.h" +#include "mirror/class_loader.h" +#include "mirror/dex_cache.h" + +namespace art { +namespace verifier { + +const DexFile::CodeItem* MethodVerifier::CodeItem() const { + return code_item_; +} + +RegisterLine* MethodVerifier::GetRegLine(uint32_t dex_pc) { + return reg_table_.GetLine(dex_pc); +} + +const InstructionFlags& MethodVerifier::GetInstructionFlags(size_t index) const { + return insn_flags_[index]; +} + +mirror::ClassLoader* MethodVerifier::GetClassLoader() { + return class_loader_->get(); +} + +mirror::DexCache* MethodVerifier::GetDexCache() { + return dex_cache_->get(); +} + +MethodReference MethodVerifier::GetMethodReference() const { + return MethodReference(dex_file_, dex_method_idx_); +} + +uint32_t MethodVerifier::GetAccessFlags() const { + return method_access_flags_; +} + +bool MethodVerifier::HasCheckCasts() const { + return has_check_casts_; +} + +bool MethodVerifier::HasVirtualOrInterfaceInvokes() const { + return has_virtual_or_interface_invokes_; +} + +bool MethodVerifier::HasFailures() const { + return !failure_messages_.empty(); +} + +const RegType& MethodVerifier::ResolveCheckedClass(uint32_t class_idx) { + DCHECK(!HasFailures()); + const RegType& result = ResolveClassAndCheckAccess(class_idx); + DCHECK(!HasFailures()); + return result; +} + +} // namespace verifier +} // namespace art + +#endif // ART_RUNTIME_VERIFIER_METHOD_VERIFIER_INL_H_ diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc index 59ead892e1e..f03cdcdcd40 100644 --- a/runtime/verifier/method_verifier.cc +++ b/runtime/verifier/method_verifier.cc @@ -22,6 +22,7 @@ #include "base/mutex-inl.h" #include "base/stringpiece.h" #include "class_linker.h" +#include "compiler_callbacks.h" #include "dex_file-inl.h" #include "dex_instruction-inl.h" #include "dex_instruction_visitor.h" @@ -110,7 +111,7 @@ MethodVerifier::FailureKind MethodVerifier::VerifyClass(const mirror::Class* kla *error = "Verifier rejected class " + PrettyDescriptor(klass) + failure_message; if (Runtime::Current()->IsCompiler()) { ClassReference ref(&dex_file, klass->GetDexClassDefIndex()); - AddRejectedClass(ref); + Runtime::Current()->GetCompilerCallbacks()->ClassRejected(ref); } return kHardFailure; } @@ -441,7 +442,12 @@ bool MethodVerifier::Verify() { // Perform static instruction verification. result = result && VerifyInstructions(); // Perform code-flow analysis and return. - return result && VerifyCodeFlow(); + result = result && VerifyCodeFlow(); + // Compute information for compiler. + if (result && Runtime::Current()->IsCompiler()) { + result = Runtime::Current()->GetCompilerCallbacks()->MethodVerified(this); + } + return result; } std::ostream& MethodVerifier::Fail(VerifyError error) { @@ -480,7 +486,7 @@ std::ostream& MethodVerifier::Fail(VerifyError error) { case VERIFY_ERROR_BAD_CLASS_HARD: { if (Runtime::Current()->IsCompiler()) { ClassReference ref(dex_file_, dex_file_->GetIndexForClassDef(*class_def_)); - AddRejectedClass(ref); + Runtime::Current()->GetCompilerCallbacks()->ClassRejected(ref); } have_pending_hard_failure_ = true; break; @@ -1063,38 +1069,6 @@ bool MethodVerifier::VerifyCodeFlow() { DCHECK_NE(failures_.size(), 0U); return false; } - - // Compute information for compiler. - if (Runtime::Current()->IsCompiler()) { - MethodReference ref(dex_file_, dex_method_idx_); - bool compile = IsCandidateForCompilation(ref, method_access_flags_); - if (compile) { - /* Generate a register map and add it to the method. */ - const std::vector* dex_gc_map = GenerateGcMap(); - if (dex_gc_map == NULL) { - DCHECK_NE(failures_.size(), 0U); - return false; // Not a real failure, but a failure to encode - } - if (kIsDebugBuild) { - VerifyGcMap(*dex_gc_map); - } - verifier::MethodVerifier::SetDexGcMap(ref, dex_gc_map); - } - - if (has_check_casts_) { - MethodVerifier::MethodSafeCastSet* method_to_safe_casts = GenerateSafeCastSet(); - if (method_to_safe_casts != NULL) { - SetSafeCastMap(ref, method_to_safe_casts); - } - } - - if (has_virtual_or_interface_invokes_) { - MethodVerifier::PcToConcreteMethodMap* pc_to_concrete_method = GenerateDevirtMap(); - if (pc_to_concrete_method != NULL) { - SetDevirtMap(ref, pc_to_concrete_method); - } - } - } return true; } @@ -3910,325 +3884,6 @@ const RegType& MethodVerifier::GetDeclaringClass() { return *declaring_class_; } -void MethodVerifier::ComputeGcMapSizes(size_t* gc_points, size_t* ref_bitmap_bits, - size_t* log2_max_gc_pc) { - size_t local_gc_points = 0; - size_t max_insn = 0; - size_t max_ref_reg = -1; - for (size_t i = 0; i < code_item_->insns_size_in_code_units_; i++) { - if (insn_flags_[i].IsCompileTimeInfoPoint()) { - local_gc_points++; - max_insn = i; - RegisterLine* line = reg_table_.GetLine(i); - max_ref_reg = line->GetMaxNonZeroReferenceReg(max_ref_reg); - } - } - *gc_points = local_gc_points; - *ref_bitmap_bits = max_ref_reg + 1; // if max register is 0 we need 1 bit to encode (ie +1) - size_t i = 0; - while ((1U << i) <= max_insn) { - i++; - } - *log2_max_gc_pc = i; -} - -MethodVerifier::MethodSafeCastSet* MethodVerifier::GenerateSafeCastSet() { - /* - * Walks over the method code and adds any cast instructions in which - * the type cast is implicit to a set, which is used in the code generation - * to elide these casts. - */ - if (!failure_messages_.empty()) { - return NULL; - } - UniquePtr mscs; - const Instruction* inst = Instruction::At(code_item_->insns_); - const Instruction* end = Instruction::At(code_item_->insns_ + - code_item_->insns_size_in_code_units_); - - for (; inst < end; inst = inst->Next()) { - Instruction::Code code = inst->Opcode(); - if ((code == Instruction::CHECK_CAST) || (code == Instruction::APUT_OBJECT)) { - uint32_t dex_pc = inst->GetDexPc(code_item_->insns_); - RegisterLine* line = reg_table_.GetLine(dex_pc); - bool is_safe_cast = false; - if (code == Instruction::CHECK_CAST) { - const RegType& reg_type(line->GetRegisterType(inst->VRegA_21c())); - const RegType& cast_type = ResolveClassAndCheckAccess(inst->VRegB_21c()); - is_safe_cast = cast_type.IsStrictlyAssignableFrom(reg_type); - } else { - const RegType& array_type(line->GetRegisterType(inst->VRegB_23x())); - // We only know its safe to assign to an array if the array type is precise. For example, - // an Object[] can have any type of object stored in it, but it may also be assigned a - // String[] in which case the stores need to be of Strings. - if (array_type.IsPreciseReference()) { - const RegType& value_type(line->GetRegisterType(inst->VRegA_23x())); - const RegType& component_type(reg_types_.GetComponentType(array_type, - class_loader_->get())); - is_safe_cast = component_type.IsStrictlyAssignableFrom(value_type); - } - } - if (is_safe_cast) { - if (mscs.get() == NULL) { - mscs.reset(new MethodSafeCastSet()); - } - mscs->insert(dex_pc); - } - } - } - return mscs.release(); -} - -MethodVerifier::PcToConcreteMethodMap* MethodVerifier::GenerateDevirtMap() { - // It is risky to rely on reg_types for sharpening in cases of soft - // verification, we might end up sharpening to a wrong implementation. Just abort. - if (!failure_messages_.empty()) { - return NULL; - } - - UniquePtr pc_to_concrete_method_map; - const uint16_t* insns = code_item_->insns_; - const Instruction* inst = Instruction::At(insns); - const Instruction* end = Instruction::At(insns + code_item_->insns_size_in_code_units_); - - for (; inst < end; inst = inst->Next()) { - bool is_virtual = (inst->Opcode() == Instruction::INVOKE_VIRTUAL) || - (inst->Opcode() == Instruction::INVOKE_VIRTUAL_RANGE); - bool is_interface = (inst->Opcode() == Instruction::INVOKE_INTERFACE) || - (inst->Opcode() == Instruction::INVOKE_INTERFACE_RANGE); - - if (!is_interface && !is_virtual) { - continue; - } - // Get reg type for register holding the reference to the object that will be dispatched upon. - uint32_t dex_pc = inst->GetDexPc(insns); - RegisterLine* line = reg_table_.GetLine(dex_pc); - bool is_range = (inst->Opcode() == Instruction::INVOKE_VIRTUAL_RANGE) || - (inst->Opcode() == Instruction::INVOKE_INTERFACE_RANGE); - const RegType& - reg_type(line->GetRegisterType(is_range ? inst->VRegC_3rc() : inst->VRegC_35c())); - - if (!reg_type.HasClass()) { - // We will compute devirtualization information only when we know the Class of the reg type. - continue; - } - mirror::Class* reg_class = reg_type.GetClass(); - if (reg_class->IsInterface()) { - // We can't devirtualize when the known type of the register is an interface. - continue; - } - if (reg_class->IsAbstract() && !reg_class->IsArrayClass()) { - // We can't devirtualize abstract classes except on arrays of abstract classes. - continue; - } - mirror::ArtMethod* abstract_method = (*dex_cache_)->GetResolvedMethod( - is_range ? inst->VRegB_3rc() : inst->VRegB_35c()); - if (abstract_method == NULL) { - // If the method is not found in the cache this means that it was never found - // by ResolveMethodAndCheckAccess() called when verifying invoke_*. - continue; - } - // Find the concrete method. - mirror::ArtMethod* concrete_method = NULL; - if (is_interface) { - concrete_method = reg_type.GetClass()->FindVirtualMethodForInterface(abstract_method); - } - if (is_virtual) { - concrete_method = reg_type.GetClass()->FindVirtualMethodForVirtual(abstract_method); - } - if (concrete_method == NULL || concrete_method->IsAbstract()) { - // In cases where concrete_method is not found, or is abstract, continue to the next invoke. - continue; - } - if (reg_type.IsPreciseReference() || concrete_method->IsFinal() || - concrete_method->GetDeclaringClass()->IsFinal()) { - // If we knew exactly the class being dispatched upon, or if the target method cannot be - // overridden record the target to be used in the compiler driver. - if (pc_to_concrete_method_map.get() == NULL) { - pc_to_concrete_method_map.reset(new PcToConcreteMethodMap()); - } - MethodReference concrete_ref( - concrete_method->GetDeclaringClass()->GetDexCache()->GetDexFile(), - concrete_method->GetDexMethodIndex()); - pc_to_concrete_method_map->Put(dex_pc, concrete_ref); - } - } - return pc_to_concrete_method_map.release(); -} - -const std::vector* MethodVerifier::GenerateGcMap() { - size_t num_entries, ref_bitmap_bits, pc_bits; - ComputeGcMapSizes(&num_entries, &ref_bitmap_bits, &pc_bits); - // There's a single byte to encode the size of each bitmap - if (ref_bitmap_bits >= (8 /* bits per byte */ * 8192 /* 13-bit size */ )) { - // TODO: either a better GC map format or per method failures - Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Cannot encode GC map for method with " - << ref_bitmap_bits << " registers"; - return NULL; - } - size_t ref_bitmap_bytes = (ref_bitmap_bits + 7) / 8; - // There are 2 bytes to encode the number of entries - if (num_entries >= 65536) { - // TODO: either a better GC map format or per method failures - Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Cannot encode GC map for method with " - << num_entries << " entries"; - return NULL; - } - size_t pc_bytes; - RegisterMapFormat format; - if (pc_bits <= 8) { - format = kRegMapFormatCompact8; - pc_bytes = 1; - } else if (pc_bits <= 16) { - format = kRegMapFormatCompact16; - pc_bytes = 2; - } else { - // TODO: either a better GC map format or per method failures - Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Cannot encode GC map for method with " - << (1 << pc_bits) << " instructions (number is rounded up to nearest power of 2)"; - return NULL; - } - size_t table_size = ((pc_bytes + ref_bitmap_bytes) * num_entries) + 4; - std::vector* table = new std::vector; - if (table == NULL) { - Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Failed to encode GC map (size=" << table_size << ")"; - return NULL; - } - table->reserve(table_size); - // Write table header - table->push_back(format | ((ref_bitmap_bytes >> DexPcToReferenceMap::kRegMapFormatShift) & - ~DexPcToReferenceMap::kRegMapFormatMask)); - table->push_back(ref_bitmap_bytes & 0xFF); - table->push_back(num_entries & 0xFF); - table->push_back((num_entries >> 8) & 0xFF); - // Write table data - for (size_t i = 0; i < code_item_->insns_size_in_code_units_; i++) { - if (insn_flags_[i].IsCompileTimeInfoPoint()) { - table->push_back(i & 0xFF); - if (pc_bytes == 2) { - table->push_back((i >> 8) & 0xFF); - } - RegisterLine* line = reg_table_.GetLine(i); - line->WriteReferenceBitMap(*table, ref_bitmap_bytes); - } - } - DCHECK_EQ(table->size(), table_size); - return table; -} - -void MethodVerifier::VerifyGcMap(const std::vector& data) { - // Check that for every GC point there is a map entry, there aren't entries for non-GC points, - // that the table data is well formed and all references are marked (or not) in the bitmap - DexPcToReferenceMap map(&data[0]); - DCHECK_EQ(data.size(), map.RawSize()); - size_t map_index = 0; - for (size_t i = 0; i < code_item_->insns_size_in_code_units_; i++) { - const uint8_t* reg_bitmap = map.FindBitMap(i, false); - if (insn_flags_[i].IsCompileTimeInfoPoint()) { - CHECK_LT(map_index, map.NumEntries()); - CHECK_EQ(map.GetDexPc(map_index), i); - CHECK_EQ(map.GetBitMap(map_index), reg_bitmap); - map_index++; - RegisterLine* line = reg_table_.GetLine(i); - for (size_t j = 0; j < code_item_->registers_size_; j++) { - if (line->GetRegisterType(j).IsNonZeroReferenceTypes()) { - CHECK_LT(j / 8, map.RegWidth()); - CHECK_EQ((reg_bitmap[j / 8] >> (j % 8)) & 1, 1); - } else if ((j / 8) < map.RegWidth()) { - CHECK_EQ((reg_bitmap[j / 8] >> (j % 8)) & 1, 0); - } else { - // If a register doesn't contain a reference then the bitmap may be shorter than the line - } - } - } else { - CHECK(reg_bitmap == NULL); - } - } -} - -void MethodVerifier::SetDexGcMap(MethodReference ref, const std::vector* gc_map) { - DCHECK(Runtime::Current()->IsCompiler()); - { - WriterMutexLock mu(Thread::Current(), *dex_gc_maps_lock_); - DexGcMapTable::iterator it = dex_gc_maps_->find(ref); - if (it != dex_gc_maps_->end()) { - delete it->second; - dex_gc_maps_->erase(it); - } - dex_gc_maps_->Put(ref, gc_map); - } - DCHECK(GetDexGcMap(ref) != NULL); -} - - -void MethodVerifier::SetSafeCastMap(MethodReference ref, const MethodSafeCastSet* cast_set) { - DCHECK(Runtime::Current()->IsCompiler()); - WriterMutexLock mu(Thread::Current(), *safecast_map_lock_); - SafeCastMap::iterator it = safecast_map_->find(ref); - if (it != safecast_map_->end()) { - delete it->second; - safecast_map_->erase(it); - } - safecast_map_->Put(ref, cast_set); - DCHECK(safecast_map_->find(ref) != safecast_map_->end()); -} - -bool MethodVerifier::IsSafeCast(MethodReference ref, uint32_t pc) { - DCHECK(Runtime::Current()->IsCompiler()); - ReaderMutexLock mu(Thread::Current(), *safecast_map_lock_); - SafeCastMap::const_iterator it = safecast_map_->find(ref); - if (it == safecast_map_->end()) { - return false; - } - - // Look up the cast address in the set of safe casts - MethodVerifier::MethodSafeCastSet::const_iterator cast_it = it->second->find(pc); - return cast_it != it->second->end(); -} - -const std::vector* MethodVerifier::GetDexGcMap(MethodReference ref) { - DCHECK(Runtime::Current()->IsCompiler()); - ReaderMutexLock mu(Thread::Current(), *dex_gc_maps_lock_); - DexGcMapTable::const_iterator it = dex_gc_maps_->find(ref); - CHECK(it != dex_gc_maps_->end()) - << "Didn't find GC map for: " << PrettyMethod(ref.dex_method_index, *ref.dex_file); - CHECK(it->second != NULL); - return it->second; -} - -void MethodVerifier::SetDevirtMap(MethodReference ref, - const PcToConcreteMethodMap* devirt_map) { - DCHECK(Runtime::Current()->IsCompiler()); - WriterMutexLock mu(Thread::Current(), *devirt_maps_lock_); - DevirtualizationMapTable::iterator it = devirt_maps_->find(ref); - if (it != devirt_maps_->end()) { - delete it->second; - devirt_maps_->erase(it); - } - - devirt_maps_->Put(ref, devirt_map); - DCHECK(devirt_maps_->find(ref) != devirt_maps_->end()); -} - -const MethodReference* MethodVerifier::GetDevirtMap(const MethodReference& ref, - uint32_t dex_pc) { - DCHECK(Runtime::Current()->IsCompiler()); - ReaderMutexLock mu(Thread::Current(), *devirt_maps_lock_); - DevirtualizationMapTable::const_iterator it = devirt_maps_->find(ref); - if (it == devirt_maps_->end()) { - return NULL; - } - - // Look up the PC in the map, get the concrete method to execute and return its reference. - MethodVerifier::PcToConcreteMethodMap::const_iterator pc_to_concrete_method - = it->second->find(dex_pc); - if (pc_to_concrete_method != it->second->end()) { - return &(pc_to_concrete_method->second); - } else { - return NULL; - } -} - std::vector MethodVerifier::DescribeVRegs(uint32_t dex_pc) { RegisterLine* line = reg_table_.GetLine(dex_pc); std::vector result; @@ -4273,120 +3928,14 @@ std::vector MethodVerifier::DescribeVRegs(uint32_t dex_pc) { return result; } -bool MethodVerifier::IsCandidateForCompilation(MethodReference& method_ref, - const uint32_t access_flags) { -#ifdef ART_SEA_IR_MODE - bool use_sea = Runtime::Current()->IsSeaIRMode(); - use_sea = use_sea && (std::string::npos != PrettyMethod( - method_ref.dex_method_index, *(method_ref.dex_file)).find("fibonacci")); - if (use_sea) return true; -#endif - // Don't compile class initializers, ever. - if (((access_flags & kAccConstructor) != 0) && ((access_flags & kAccStatic) != 0)) { - return false; - } - return (Runtime::Current()->GetCompilerFilter() != Runtime::kInterpretOnly); -} - -ReaderWriterMutex* MethodVerifier::dex_gc_maps_lock_ = NULL; -MethodVerifier::DexGcMapTable* MethodVerifier::dex_gc_maps_ = NULL; - -ReaderWriterMutex* MethodVerifier::safecast_map_lock_ = NULL; -MethodVerifier::SafeCastMap* MethodVerifier::safecast_map_ = NULL; - -ReaderWriterMutex* MethodVerifier::devirt_maps_lock_ = NULL; -MethodVerifier::DevirtualizationMapTable* MethodVerifier::devirt_maps_ = NULL; - -ReaderWriterMutex* MethodVerifier::rejected_classes_lock_ = NULL; -MethodVerifier::RejectedClassesTable* MethodVerifier::rejected_classes_ = NULL; - void MethodVerifier::Init() { - if (Runtime::Current()->IsCompiler()) { - dex_gc_maps_lock_ = new ReaderWriterMutex("verifier GC maps lock"); - Thread* self = Thread::Current(); - { - WriterMutexLock mu(self, *dex_gc_maps_lock_); - dex_gc_maps_ = new MethodVerifier::DexGcMapTable; - } - - safecast_map_lock_ = new ReaderWriterMutex("verifier Cast Elision lock"); - { - WriterMutexLock mu(self, *safecast_map_lock_); - safecast_map_ = new MethodVerifier::SafeCastMap(); - } - - devirt_maps_lock_ = new ReaderWriterMutex("verifier Devirtualization lock"); - - { - WriterMutexLock mu(self, *devirt_maps_lock_); - devirt_maps_ = new MethodVerifier::DevirtualizationMapTable(); - } - - rejected_classes_lock_ = new ReaderWriterMutex("verifier rejected classes lock"); - { - WriterMutexLock mu(self, *rejected_classes_lock_); - rejected_classes_ = new MethodVerifier::RejectedClassesTable; - } - } art::verifier::RegTypeCache::Init(); } void MethodVerifier::Shutdown() { - if (Runtime::Current()->IsCompiler()) { - Thread* self = Thread::Current(); - { - WriterMutexLock mu(self, *dex_gc_maps_lock_); - STLDeleteValues(dex_gc_maps_); - delete dex_gc_maps_; - dex_gc_maps_ = NULL; - } - delete dex_gc_maps_lock_; - dex_gc_maps_lock_ = NULL; - - { - WriterMutexLock mu(self, *safecast_map_lock_); - STLDeleteValues(safecast_map_); - delete safecast_map_; - safecast_map_ = NULL; - } - delete safecast_map_lock_; - safecast_map_lock_ = NULL; - - { - WriterMutexLock mu(self, *devirt_maps_lock_); - STLDeleteValues(devirt_maps_); - delete devirt_maps_; - devirt_maps_ = NULL; - } - delete devirt_maps_lock_; - devirt_maps_lock_ = NULL; - - { - WriterMutexLock mu(self, *rejected_classes_lock_); - delete rejected_classes_; - rejected_classes_ = NULL; - } - delete rejected_classes_lock_; - rejected_classes_lock_ = NULL; - } verifier::RegTypeCache::ShutDown(); } -void MethodVerifier::AddRejectedClass(ClassReference ref) { - DCHECK(Runtime::Current()->IsCompiler()); - { - WriterMutexLock mu(Thread::Current(), *rejected_classes_lock_); - rejected_classes_->insert(ref); - } - CHECK(IsClassRejected(ref)); -} - -bool MethodVerifier::IsClassRejected(ClassReference ref) { - DCHECK(Runtime::Current()->IsCompiler()); - ReaderMutexLock mu(Thread::Current(), *rejected_classes_lock_); - return (rejected_classes_->find(ref) != rejected_classes_->end()); -} - void MethodVerifier::VisitRoots(RootVisitor* visitor, void* arg) { reg_types_.VisitRoots(visitor, arg); } diff --git a/runtime/verifier/method_verifier.h b/runtime/verifier/method_verifier.h index dffda966aaa..ac36a7ed21e 100644 --- a/runtime/verifier/method_verifier.h +++ b/runtime/verifier/method_verifier.h @@ -183,16 +183,6 @@ class MethodVerifier { // information void Dump(std::ostream& os) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - static const std::vector* GetDexGcMap(MethodReference ref) - LOCKS_EXCLUDED(dex_gc_maps_lock_); - - static const MethodReference* GetDevirtMap(const MethodReference& ref, uint32_t dex_pc) - LOCKS_EXCLUDED(devirt_maps_lock_); - - // Returns true if the cast can statically be verified to be redundant - // by using the check-cast elision peephole optimization in the verifier - static bool IsSafeCast(MethodReference ref, uint32_t pc) LOCKS_EXCLUDED(safecast_map_lock_); - // Fills 'monitor_enter_dex_pcs' with the dex pcs of the monitor-enter instructions corresponding // to the locks held at 'dex_pc' in method 'm'. static void FindLocksAtDexPc(mirror::ArtMethod* m, uint32_t dex_pc, @@ -212,11 +202,6 @@ class MethodVerifier { static void Init() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); static void Shutdown(); - static void AddRejectedClass(ClassReference ref) - LOCKS_EXCLUDED(rejected_classes_lock_); - static bool IsClassRejected(ClassReference ref) - LOCKS_EXCLUDED(rejected_classes_lock_); - bool CanLoadClasses() const { return can_load_classes_; } @@ -236,11 +221,22 @@ class MethodVerifier { // Describe VRegs at the given dex pc. std::vector DescribeVRegs(uint32_t dex_pc); - static bool IsCandidateForCompilation(MethodReference& method_ref, - const uint32_t access_flags); - void VisitRoots(RootVisitor* visitor, void* arg) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + // Accessors used by the compiler via CompilerCallback + const DexFile::CodeItem* CodeItem() const; + RegisterLine* GetRegLine(uint32_t dex_pc); + const InstructionFlags& GetInstructionFlags(size_t index) const; + mirror::ClassLoader* GetClassLoader() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + mirror::DexCache* GetDexCache() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + MethodReference GetMethodReference() const; + uint32_t GetAccessFlags() const; + bool HasCheckCasts() const; + bool HasVirtualOrInterfaceInvokes() const; + bool HasFailures() const; + const RegType& ResolveCheckedClass(uint32_t class_idx) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + private: // Adds the given string to the beginning of the last failure message. void PrependToLastFailMessage(std::string); @@ -612,57 +608,8 @@ class MethodVerifier { // Get a type representing the declaring class of the method. const RegType& GetDeclaringClass() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - /* - * Generate the GC map for a method that has just been verified (i.e. we're doing this as part of - * verification). For type-precise determination we have all the data we need, so we just need to - * encode it in some clever fashion. - * Returns a pointer to a newly-allocated RegisterMap, or NULL on failure. - */ - const std::vector* GenerateGcMap(); - - // Verify that the GC map associated with method_ is well formed - void VerifyGcMap(const std::vector& data); - - // Compute sizes for GC map data - void ComputeGcMapSizes(size_t* gc_points, size_t* ref_bitmap_bits, size_t* log2_max_gc_pc); - InstructionFlags* CurrentInsnFlags(); - // All the GC maps that the verifier has created - typedef SafeMap*, - MethodReferenceComparator> DexGcMapTable; - static ReaderWriterMutex* dex_gc_maps_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER; - static DexGcMapTable* dex_gc_maps_ GUARDED_BY(dex_gc_maps_lock_); - static void SetDexGcMap(MethodReference ref, const std::vector* dex_gc_map) - LOCKS_EXCLUDED(dex_gc_maps_lock_); - - - // Cast elision types. - typedef std::set MethodSafeCastSet; - typedef SafeMap SafeCastMap; - MethodVerifier::MethodSafeCastSet* GenerateSafeCastSet() - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - static void SetSafeCastMap(MethodReference ref, const MethodSafeCastSet* mscs); - LOCKS_EXCLUDED(safecast_map_lock_); - static ReaderWriterMutex* safecast_map_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER; - static SafeCastMap* safecast_map_ GUARDED_BY(safecast_map_lock_); - - // Devirtualization map. - typedef SafeMap PcToConcreteMethodMap; - typedef SafeMap DevirtualizationMapTable; - MethodVerifier::PcToConcreteMethodMap* GenerateDevirtMap() - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - - static ReaderWriterMutex* devirt_maps_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER; - static DevirtualizationMapTable* devirt_maps_ GUARDED_BY(devirt_maps_lock_); - static void SetDevirtMap(MethodReference ref, - const PcToConcreteMethodMap* pc_method_map) - LOCKS_EXCLUDED(devirt_maps_lock_); - typedef std::set RejectedClassesTable; - static ReaderWriterMutex* rejected_classes_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER; - static RejectedClassesTable* rejected_classes_ GUARDED_BY(rejected_classes_lock_); RegTypeCache reg_types_; From 9119c5f9e4f447f4209d51cff66d1aace510ce5e Mon Sep 17 00:00:00 2001 From: Sebastien Hertz Date: Mon, 16 Dec 2013 11:31:45 +0100 Subject: [PATCH 0292/2402] Cleanup invoke's receiver handling in interpreter. To comply with a moving collector, we used to load invoke's receiver (for non static methods only) from the caller shadow frame after resolving the method itself (in case the garbage collector is triggered inside) but before passing invoke arguments, including loading receiver from the caller's shadow frame into the callee's shadow frame. Therefore, we used to load the receiver 3 times in the fast path but only twice in the slow path. The slow path is rarely used (only in method requiring extra runtime checks) so we now move this extra reload to the slow path. Therefore an invoke using the fast path loads the receiver twice while the slow path loads it 3 times. I don't expect much improvement here. The main reason is to keep extra code in the slow path. Change-Id: I10e96b10de4b8c2992e276bd564bc3e2f191779c --- runtime/interpreter/interpreter_common.cc | 27 +++++++++++------------ runtime/interpreter/interpreter_common.h | 11 +++------ runtime/stack.h | 2 +- 3 files changed, 17 insertions(+), 23 deletions(-) diff --git a/runtime/interpreter/interpreter_common.cc b/runtime/interpreter/interpreter_common.cc index 5b9e55f9238..1d402935e6b 100644 --- a/runtime/interpreter/interpreter_common.cc +++ b/runtime/interpreter/interpreter_common.cc @@ -38,7 +38,7 @@ static inline void AssignRegister(ShadowFrame& new_shadow_frame, const ShadowFra } template -bool DoCall(ArtMethod* method, Object* receiver, Thread* self, ShadowFrame& shadow_frame, +bool DoCall(ArtMethod* method, Thread* self, ShadowFrame& shadow_frame, const Instruction* inst, uint16_t inst_data, JValue* result) { // Compute method information. MethodHelper mh(method); @@ -66,17 +66,6 @@ bool DoCall(ArtMethod* method, Object* receiver, Thread* self, ShadowFrame& shad const DexFile::TypeList* params = mh.GetParameterTypeList(); const char* shorty = mh.GetShorty(); - // Handle receiver apart since it's not part of the shorty. - size_t dest_reg = first_dest_reg; - size_t arg_offset = 0; - if (receiver != NULL) { - DCHECK(!method->IsStatic()); - new_shadow_frame->SetVRegReference(dest_reg, receiver); - ++dest_reg; - ++arg_offset; - } else { - DCHECK(method->IsStatic()); - } // TODO: find a cleaner way to separate non-range and range information without duplicating code. uint32_t arg[5]; // only used in invoke-XXX. uint32_t vregC; // only used in invoke-XXX-range. @@ -85,6 +74,16 @@ bool DoCall(ArtMethod* method, Object* receiver, Thread* self, ShadowFrame& shad } else { inst->GetArgs(arg, inst_data); } + + // Handle receiver apart since it's not part of the shorty. + size_t dest_reg = first_dest_reg; + size_t arg_offset = 0; + if (!method->IsStatic()) { + size_t receiver_reg = (is_range) ? vregC : arg[0]; + new_shadow_frame->SetVRegReference(dest_reg, shadow_frame.GetVRegReference(receiver_reg)); + ++dest_reg; + ++arg_offset; + } for (size_t shorty_pos = 0; dest_reg < num_regs; ++shorty_pos, ++dest_reg, ++arg_offset) { DCHECK_LT(shorty_pos + 1, mh.GetShortyLength()); const size_t src_reg = (is_range) ? vregC + arg_offset : arg[arg_offset]; @@ -333,8 +332,8 @@ static void UnstartedRuntimeInvoke(Thread* self, MethodHelper& mh, // Explicit DoCall template function declarations. #define EXPLICIT_DO_CALL_TEMPLATE_DECL(_is_range, _do_assignability_check) \ template SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) \ - bool DoCall<_is_range, _do_assignability_check>(ArtMethod* method, Object* receiver, \ - Thread* self, ShadowFrame& shadow_frame, \ + bool DoCall<_is_range, _do_assignability_check>(ArtMethod* method, Thread* self, \ + ShadowFrame& shadow_frame, \ const Instruction* inst, uint16_t inst_data, \ JValue* result) EXPLICIT_DO_CALL_TEMPLATE_DECL(false, false); diff --git a/runtime/interpreter/interpreter_common.h b/runtime/interpreter/interpreter_common.h index a9b8909b2b8..4481210e04f 100644 --- a/runtime/interpreter/interpreter_common.h +++ b/runtime/interpreter/interpreter_common.h @@ -87,7 +87,7 @@ static inline void DoMonitorExit(Thread* self, Object* ref) NO_THREAD_SAFETY_ANA // DoInvokeVirtualQuick functions. // Returns true on success, otherwise throws an exception and returns false. template -bool DoCall(ArtMethod* method, Object* receiver, Thread* self, ShadowFrame& shadow_frame, +bool DoCall(ArtMethod* method, Thread* self, ShadowFrame& shadow_frame, const Instruction* inst, uint16_t inst_data, JValue* result); // Handles invoke-XXX/range instructions. @@ -101,10 +101,6 @@ static inline bool DoInvoke(Thread* self, ShadowFrame& shadow_frame, const Instr ArtMethod* const method = FindMethodFromCode(method_idx, receiver, shadow_frame.GetMethod(), self); - if (type != kStatic) { - // Reload the vreg since the GC may have moved the object. - receiver = shadow_frame.GetVRegReference(vregC); - } if (UNLIKELY(method == nullptr)) { CHECK(self->IsExceptionPending()); result->SetJ(0); @@ -114,8 +110,7 @@ static inline bool DoInvoke(Thread* self, ShadowFrame& shadow_frame, const Instr result->SetJ(0); return false; } else { - return DoCall(method, receiver, self, shadow_frame, inst, - inst_data, result); + return DoCall(method, self, shadow_frame, inst, inst_data, result); } } @@ -145,7 +140,7 @@ static inline bool DoInvokeVirtualQuick(Thread* self, ShadowFrame& shadow_frame, return false; } else { // No need to check since we've been quickened. - return DoCall(method, receiver, self, shadow_frame, inst, inst_data, result); + return DoCall(method, self, shadow_frame, inst, inst_data, result); } } diff --git a/runtime/stack.h b/runtime/stack.h index 3d6b06a32d7..590f406bb34 100644 --- a/runtime/stack.h +++ b/runtime/stack.h @@ -160,7 +160,7 @@ class ShadowFrame { << ") is in protected space, reference array " << true; } // If the vreg reference is not equal to the vreg then the vreg reference is stale. - if (reinterpret_cast(ref) != vregs_[i]) { + if (UNLIKELY(reinterpret_cast(ref) != vregs_[i])) { return nullptr; } return ref; From 5816ed48bc339c983b40dc493e96b97821ce7966 Mon Sep 17 00:00:00 2001 From: Vladimir Marko Date: Wed, 27 Nov 2013 17:04:20 +0000 Subject: [PATCH 0293/2402] Detect special methods at the end of verification. This moves special method handling to method inliner and prepares for eventual inlining of these methods. Change-Id: I51c51b940fb7bc714e33135cd61be69467861352 --- compiler/dex/compiler_enums.h | 21 -- compiler/dex/frontend.cc | 11 +- compiler/dex/frontend.h | 13 - compiler/dex/mir_analysis.cc | 6 +- compiler/dex/mir_graph.cc | 59 +---- compiler/dex/mir_graph.h | 9 - compiler/dex/quick/arm/call_arm.cc | 72 +++--- compiler/dex/quick/arm/codegen_arm.h | 2 +- compiler/dex/quick/codegen_util.cc | 24 +- compiler/dex/quick/dex_file_method_inliner.cc | 235 +++++++++++++++++- compiler/dex/quick/dex_file_method_inliner.h | 89 ++++++- compiler/dex/quick/gen_invoke.cc | 9 +- compiler/dex/quick/mips/call_mips.cc | 2 +- compiler/dex/quick/mips/codegen_mips.h | 2 +- compiler/dex/quick/mir_to_lir.cc | 5 +- compiler/dex/quick/mir_to_lir.h | 7 +- compiler/dex/quick/x86/call_x86.cc | 2 +- compiler/dex/quick/x86/codegen_x86.h | 2 +- compiler/driver/compiler_driver.cc | 2 + compiler/driver/compiler_driver.h | 7 + dex2oat/dex2oat.cc | 24 +- 21 files changed, 400 insertions(+), 203 deletions(-) diff --git a/compiler/dex/compiler_enums.h b/compiler/dex/compiler_enums.h index d73f148dcf0..35d04ae137f 100644 --- a/compiler/dex/compiler_enums.h +++ b/compiler/dex/compiler_enums.h @@ -302,27 +302,6 @@ enum ThrowKind { kThrowStackOverflow, }; -enum SpecialCaseHandler { - kNoHandler, - kNullMethod, - kConstFunction, - kIGet, - kIGetBoolean, - kIGetObject, - kIGetByte, - kIGetChar, - kIGetShort, - kIGetWide, - kIPut, - kIPutBoolean, - kIPutObject, - kIPutByte, - kIPutChar, - kIPutShort, - kIPutWide, - kIdentity, -}; - enum DividePattern { DivideNone, Divide3, diff --git a/compiler/dex/frontend.cc b/compiler/dex/frontend.cc index 6aabb2ae894..adfbf2f3b58 100644 --- a/compiler/dex/frontend.cc +++ b/compiler/dex/frontend.cc @@ -63,21 +63,12 @@ LLVMInfo::LLVMInfo() { LLVMInfo::~LLVMInfo() { } -QuickCompilerContext::QuickCompilerContext() - : inliner_map_(new DexFileToMethodInlinerMap()) { -} - -QuickCompilerContext::~QuickCompilerContext() { -} - extern "C" void ArtInitQuickCompilerContext(art::CompilerDriver& driver) { CHECK(driver.GetCompilerContext() == NULL); - driver.SetCompilerContext(new QuickCompilerContext()); } extern "C" void ArtUnInitQuickCompilerContext(art::CompilerDriver& driver) { - delete reinterpret_cast(driver.GetCompilerContext()); - driver.SetCompilerContext(NULL); + CHECK(driver.GetCompilerContext() == NULL); } /* Default optimizer/debug setting for the compiler. */ diff --git a/compiler/dex/frontend.h b/compiler/dex/frontend.h index bcb8bf05a99..8eb6684d7f8 100644 --- a/compiler/dex/frontend.h +++ b/compiler/dex/frontend.h @@ -113,19 +113,6 @@ class LLVMInfo { UniquePtr ir_builder_; }; -class QuickCompilerContext { - public: - QuickCompilerContext(); - ~QuickCompilerContext(); - - DexFileToMethodInlinerMap* GetInlinerMap() { - return inliner_map_.get(); - } - - private: - UniquePtr inliner_map_; -}; - struct CompilationUnit; struct BasicBlock; diff --git a/compiler/dex/mir_analysis.cc b/compiler/dex/mir_analysis.cc index 89af06e085e..ab55333fa7d 100644 --- a/compiler/dex/mir_analysis.cc +++ b/compiler/dex/mir_analysis.cc @@ -16,6 +16,8 @@ #include "compiler_internals.h" #include "dataflow_iterator-inl.h" +#include "dex/quick/dex_file_method_inliner.h" +#include "dex/quick/dex_file_to_method_inliner_map.h" namespace art { @@ -1052,7 +1054,9 @@ bool MIRGraph::SkipCompilation(Runtime::CompilerFilter compiler_filter) { } // Filter 3: if this method is a special pattern, go ahead and emit the canned pattern. - if (IsSpecialCase()) { + if (cu_->compiler_driver->GetMethodInlinerMap() != nullptr && + cu_->compiler_driver->GetMethodInlinerMap()->GetMethodInliner(cu_->dex_file) + ->IsSpecial(cu_->method_idx)) { return false; } diff --git a/compiler/dex/mir_graph.cc b/compiler/dex/mir_graph.cc index 2a182801335..79edb871fec 100644 --- a/compiler/dex/mir_graph.cc +++ b/compiler/dex/mir_graph.cc @@ -20,40 +20,13 @@ #include "leb128.h" #include "mir_graph.h" +#include "dex/quick/dex_file_to_method_inliner_map.h" +#include "dex/quick/dex_file_method_inliner.h" + namespace art { #define MAX_PATTERN_LEN 5 -struct CodePattern { - const Instruction::Code opcodes[MAX_PATTERN_LEN]; - const SpecialCaseHandler handler_code; -}; - -static const CodePattern special_patterns[] = { - {{Instruction::RETURN_VOID}, kNullMethod}, - {{Instruction::CONST, Instruction::RETURN}, kConstFunction}, - {{Instruction::CONST_4, Instruction::RETURN}, kConstFunction}, - {{Instruction::CONST_4, Instruction::RETURN_OBJECT}, kConstFunction}, - {{Instruction::CONST_16, Instruction::RETURN}, kConstFunction}, - {{Instruction::IGET, Instruction:: RETURN}, kIGet}, - {{Instruction::IGET_BOOLEAN, Instruction::RETURN}, kIGetBoolean}, - {{Instruction::IGET_OBJECT, Instruction::RETURN_OBJECT}, kIGetObject}, - {{Instruction::IGET_BYTE, Instruction::RETURN}, kIGetByte}, - {{Instruction::IGET_CHAR, Instruction::RETURN}, kIGetChar}, - {{Instruction::IGET_SHORT, Instruction::RETURN}, kIGetShort}, - {{Instruction::IGET_WIDE, Instruction::RETURN_WIDE}, kIGetWide}, - {{Instruction::IPUT, Instruction::RETURN_VOID}, kIPut}, - {{Instruction::IPUT_BOOLEAN, Instruction::RETURN_VOID}, kIPutBoolean}, - {{Instruction::IPUT_OBJECT, Instruction::RETURN_VOID}, kIPutObject}, - {{Instruction::IPUT_BYTE, Instruction::RETURN_VOID}, kIPutByte}, - {{Instruction::IPUT_CHAR, Instruction::RETURN_VOID}, kIPutChar}, - {{Instruction::IPUT_SHORT, Instruction::RETURN_VOID}, kIPutShort}, - {{Instruction::IPUT_WIDE, Instruction::RETURN_VOID}, kIPutWide}, - {{Instruction::RETURN}, kIdentity}, - {{Instruction::RETURN_OBJECT}, kIdentity}, - {{Instruction::RETURN_WIDE}, kIdentity}, -}; - const char* MIRGraph::extended_mir_op_names_[kMirOpLast - kMirOpFirst] = { "Phi", "Copy", @@ -107,7 +80,6 @@ MIRGraph::MIRGraph(CompilationUnit* cu, ArenaAllocator* arena) method_sreg_(0), attributes_(METHOD_IS_LEAF), // Start with leaf assumption, change on encountering invoke. checkstats_(NULL), - special_case_(kNoHandler), arena_(arena), backward_branches_(0), forward_branches_(0) { @@ -612,13 +584,6 @@ void MIRGraph::InlineMethod(const DexFile::CodeItem* code_item, uint32_t access_ /* Identify code range in try blocks and set up the empty catch blocks */ ProcessTryCatchBlocks(); - /* Set up for simple method detection */ - int num_patterns = sizeof(special_patterns)/sizeof(special_patterns[0]); - bool live_pattern = (num_patterns > 0) && !(cu_->disable_opt & (1 << kMatch)); - bool* dead_pattern = - static_cast(arena_->Alloc(sizeof(bool) * num_patterns, ArenaAllocator::kAllocMisc)); - int pattern_pos = 0; - /* Parse all instructions and put them into containing basic blocks */ while (code_ptr < code_end) { MIR *insn = static_cast(arena_->Alloc(sizeof(MIR), ArenaAllocator::kAllocMIR)); @@ -631,23 +596,6 @@ void MIRGraph::InlineMethod(const DexFile::CodeItem* code_item, uint32_t access_ opcode_count_[static_cast(opcode)]++; } - /* Possible simple method? */ - if (live_pattern) { - live_pattern = false; - special_case_ = kNoHandler; - for (int i = 0; i < num_patterns; i++) { - if (!dead_pattern[i]) { - if (special_patterns[i].opcodes[pattern_pos] == opcode) { - live_pattern = true; - special_case_ = special_patterns[i].handler_code; - } else { - dead_pattern[i] = true; - } - } - } - pattern_pos++; - } - int flags = Instruction::FlagsOf(insn->dalvikInsn.opcode); uint64_t df_flags = oat_data_flow_attributes_[insn->dalvikInsn.opcode]; @@ -736,6 +684,7 @@ void MIRGraph::InlineMethod(const DexFile::CodeItem* code_item, uint32_t access_ cur_block = next_block; } } + if (cu_->enable_debug & (1 << kDebugDumpCFG)) { DumpCFG("/sdcard/1_post_parse_cfg/", true); } diff --git a/compiler/dex/mir_graph.h b/compiler/dex/mir_graph.h index bffec394d67..f9980056b88 100644 --- a/compiler/dex/mir_graph.h +++ b/compiler/dex/mir_graph.h @@ -577,14 +577,6 @@ class MIRGraph { return reg_location_[method_sreg_]; } - bool IsSpecialCase() { - return special_case_ != kNoHandler; - } - - SpecialCaseHandler GetSpecialCase() { - return special_case_; - } - bool IsBackedge(BasicBlock* branch_bb, BasicBlockId target_bb_id) { return ((target_bb_id != NullBasicBlockId) && (GetBasicBlock(target_bb_id)->start_offset <= branch_bb->start_offset)); @@ -789,7 +781,6 @@ class MIRGraph { int method_sreg_; unsigned int attributes_; Checkstats* checkstats_; - SpecialCaseHandler special_case_; ArenaAllocator* arena_; int backward_branches_; int forward_branches_; diff --git a/compiler/dex/quick/arm/call_arm.cc b/compiler/dex/quick/arm/call_arm.cc index 23ea40712fa..8226b24fe91 100644 --- a/compiler/dex/quick/arm/call_arm.cc +++ b/compiler/dex/quick/arm/call_arm.cc @@ -18,6 +18,7 @@ #include "arm_lir.h" #include "codegen_arm.h" +#include "dex/quick/dex_file_method_inliner.h" #include "dex/quick/mir_to_lir-inl.h" #include "entrypoints/quick/quick_entrypoints.h" @@ -217,58 +218,43 @@ MIR* ArmMir2Lir::SpecialIdentity(MIR* mir) { * Special-case code genration for simple non-throwing leaf methods. */ void ArmMir2Lir::GenSpecialCase(BasicBlock* bb, MIR* mir, - SpecialCaseHandler special_case) { + const InlineMethod& special) { + // TODO: Generate the method using only the data in special. (Requires FastInstance() field + // validation in DexFileMethodInliner::AnalyseIGetMethod()/AnalyseIPutMethod().) + DCHECK(special.flags & kInlineSpecial); current_dalvik_offset_ = mir->offset; MIR* next_mir = NULL; - switch (special_case) { - case kNullMethod: + switch (special.opcode) { + case kInlineOpNop: DCHECK(mir->dalvikInsn.opcode == Instruction::RETURN_VOID); next_mir = mir; break; - case kConstFunction: + case kInlineOpConst: ArmMir2Lir::GenPrintLabel(mir); - LoadConstant(rARM_RET0, mir->dalvikInsn.vB); + LoadConstant(rARM_RET0, special.data); next_mir = GetNextMir(&bb, mir); break; - case kIGet: - next_mir = SpecialIGet(&bb, mir, kWord, false, false); + case kInlineOpIGet: { + InlineIGetIPutData data; + data.data = special.data; + OpSize op_size = static_cast(data.d.op_size); + DCHECK_NE(data.d.op_size, kDouble); // The inliner doesn't distinguish kDouble, uses kLong. + bool long_or_double = (data.d.op_size == kLong); + bool is_object = data.d.is_object; + next_mir = SpecialIGet(&bb, mir, op_size, long_or_double, is_object); break; - case kIGetBoolean: - case kIGetByte: - next_mir = SpecialIGet(&bb, mir, kUnsignedByte, false, false); - break; - case kIGetObject: - next_mir = SpecialIGet(&bb, mir, kWord, false, true); - break; - case kIGetChar: - next_mir = SpecialIGet(&bb, mir, kUnsignedHalf, false, false); - break; - case kIGetShort: - next_mir = SpecialIGet(&bb, mir, kSignedHalf, false, false); - break; - case kIGetWide: - next_mir = SpecialIGet(&bb, mir, kLong, true, false); - break; - case kIPut: - next_mir = SpecialIPut(&bb, mir, kWord, false, false); - break; - case kIPutBoolean: - case kIPutByte: - next_mir = SpecialIPut(&bb, mir, kUnsignedByte, false, false); - break; - case kIPutObject: - next_mir = SpecialIPut(&bb, mir, kWord, false, true); - break; - case kIPutChar: - next_mir = SpecialIPut(&bb, mir, kUnsignedHalf, false, false); - break; - case kIPutShort: - next_mir = SpecialIPut(&bb, mir, kSignedHalf, false, false); - break; - case kIPutWide: - next_mir = SpecialIPut(&bb, mir, kLong, true, false); + } + case kInlineOpIPut: { + InlineIGetIPutData data; + data.data = special.data; + OpSize op_size = static_cast(data.d.op_size); + DCHECK_NE(data.d.op_size, kDouble); // The inliner doesn't distinguish kDouble, uses kLong. + bool long_or_double = (data.d.op_size == kLong); + bool is_object = data.d.is_object; + next_mir = SpecialIPut(&bb, mir, op_size, long_or_double, is_object); break; - case kIdentity: + } + case kInlineOpReturnArg: next_mir = SpecialIdentity(mir); break; default: @@ -276,7 +262,7 @@ void ArmMir2Lir::GenSpecialCase(BasicBlock* bb, MIR* mir, } if (next_mir != NULL) { current_dalvik_offset_ = next_mir->offset; - if (special_case != kIdentity) { + if (special.opcode != kInlineOpReturnArg) { ArmMir2Lir::GenPrintLabel(next_mir); } NewLIR1(kThumbBx, rARM_LR); diff --git a/compiler/dex/quick/arm/codegen_arm.h b/compiler/dex/quick/arm/codegen_arm.h index 25ddc9451d2..c04f1d6abf3 100644 --- a/compiler/dex/quick/arm/codegen_arm.h +++ b/compiler/dex/quick/arm/codegen_arm.h @@ -135,7 +135,7 @@ class ArmMir2Lir : public Mir2Lir { void GenNegFloat(RegLocation rl_dest, RegLocation rl_src); void GenPackedSwitch(MIR* mir, DexOffset table_offset, RegLocation rl_src); void GenSparseSwitch(MIR* mir, DexOffset table_offset, RegLocation rl_src); - void GenSpecialCase(BasicBlock* bb, MIR* mir, SpecialCaseHandler special_case); + void GenSpecialCase(BasicBlock* bb, MIR* mir, const InlineMethod& special); // Required for target - single operation generators. LIR* OpUnconditionalBranch(LIR* target); diff --git a/compiler/dex/quick/codegen_util.cc b/compiler/dex/quick/codegen_util.cc index bae8ff339a0..65286d5d2f2 100644 --- a/compiler/dex/quick/codegen_util.cc +++ b/compiler/dex/quick/codegen_util.cc @@ -19,6 +19,8 @@ #include "gc_map.h" #include "mapping_table.h" #include "mir_to_lir-inl.h" +#include "dex/quick/dex_file_method_inliner.h" +#include "dex/quick/dex_file_to_method_inliner_map.h" #include "dex/verified_methods_data.h" #include "verifier/dex_gc_map.h" #include "verifier/method_verifier.h" @@ -983,8 +985,7 @@ Mir2Lir::Mir2Lir(CompilationUnit* cu, MIRGraph* mir_graph, ArenaAllocator* arena core_spill_mask_(0), fp_spill_mask_(0), first_lir_insn_(NULL), - last_lir_insn_(NULL), - inliner_(nullptr) { + last_lir_insn_(NULL) { promotion_map_ = static_cast (arena_->Alloc((cu_->num_dalvik_registers + cu_->num_compiler_temps + 1) * sizeof(promotion_map_[0]), ArenaAllocator::kAllocRegAlloc)); @@ -1000,15 +1001,16 @@ void Mir2Lir::Materialize() { /* Allocate Registers using simple local allocation scheme */ SimpleRegAlloc(); - if (mir_graph_->IsSpecialCase()) { - /* - * Custom codegen for special cases. If for any reason the - * special codegen doesn't succeed, first_lir_insn_ will - * set to NULL; - */ - cu_->NewTimingSplit("SpecialMIR2LIR"); - SpecialMIR2LIR(mir_graph_->GetSpecialCase()); - } + /* + * Custom codegen for special cases. If for any reason the + * special codegen doesn't succeed, first_lir_insn_ will + * set to NULL; + */ + // TODO: Clean up GenSpecial() and return true only if special implementation is emitted. + // Currently, GenSpecial() returns IsSpecial() but doesn't check after SpecialMIR2LIR(). + DCHECK(cu_->compiler_driver->GetMethodInlinerMap() != nullptr); + cu_->compiler_driver->GetMethodInlinerMap()->GetMethodInliner(cu_->dex_file) + ->GenSpecial(this, cu_->method_idx); /* Convert MIR to LIR, etc. */ if (first_lir_insn_ == NULL) { diff --git a/compiler/dex/quick/dex_file_method_inliner.cc b/compiler/dex/quick/dex_file_method_inliner.cc index ba98225024e..3c331e12d99 100644 --- a/compiler/dex/quick/dex_file_method_inliner.cc +++ b/compiler/dex/quick/dex_file_method_inliner.cc @@ -18,16 +18,19 @@ #include "base/macros.h" #include "base/mutex.h" #include "base/mutex-inl.h" +#include "locks.h" #include "thread.h" #include "thread-inl.h" #include "dex/mir_graph.h" +#include "dex_instruction.h" +#include "dex_instruction-inl.h" #include "dex_file_method_inliner.h" namespace art { const uint32_t DexFileMethodInliner::kIndexUnresolved; -const char* DexFileMethodInliner::kClassCacheNames[] = { +const char* const DexFileMethodInliner::kClassCacheNames[] = { "Z", // kClassCacheBoolean "B", // kClassCacheByte "C", // kClassCacheChar @@ -51,7 +54,7 @@ const char* DexFileMethodInliner::kClassCacheNames[] = { "Lsun/misc/Unsafe;", // kClassCacheSunMiscUnsafe }; -const char* DexFileMethodInliner::kNameCacheNames[] = { +const char* const DexFileMethodInliner::kNameCacheNames[] = { "reverseBytes", // kNameCacheReverseBytes "doubleToRawLongBits", // kNameCacheDoubleToRawLongBits "longBitsToDouble", // kNameCacheLongBitsToDouble @@ -164,7 +167,7 @@ const DexFileMethodInliner::ProtoDef DexFileMethodInliner::kProtoCacheDefs[] = { const DexFileMethodInliner::IntrinsicDef DexFileMethodInliner::kIntrinsicMethods[] = { #define INTRINSIC(c, n, p, o, d) \ - { { kClassCache ## c, kNameCache ## n, kProtoCache ## p }, { o, d } } + { { kClassCache ## c, kNameCache ## n, kProtoCache ## p }, { o, kInlineIntrinsic, d } } INTRINSIC(JavaLangDouble, DoubleToRawLongBits, D_J, kIntrinsicDoubleCvt, 0), INTRINSIC(JavaLangDouble, LongBitsToDouble, J_D, kIntrinsicDoubleCvt, 0), @@ -245,17 +248,71 @@ DexFileMethodInliner::DexFileMethodInliner() DexFileMethodInliner::~DexFileMethodInliner() { } +bool DexFileMethodInliner::AnalyseMethodCode(uint32_t method_idx, + const DexFile::CodeItem* code_item) { + // We currently support only plain return or 2-instruction methods. + + DCHECK_NE(code_item->insns_size_in_code_units_, 0u); + const Instruction* instruction = Instruction::At(code_item->insns_); + Instruction::Code opcode = instruction->Opcode(); + + switch (opcode) { + case Instruction::RETURN_VOID: + return AddInlineMethod(method_idx, kInlineOpNop, kInlineSpecial, 0); + case Instruction::RETURN: + case Instruction::RETURN_OBJECT: + return AnalyseReturnMethod(method_idx, code_item, kWord); + case Instruction::RETURN_WIDE: + return AnalyseReturnMethod(method_idx, code_item, kLong); + case Instruction::CONST: + case Instruction::CONST_4: + case Instruction::CONST_16: + case Instruction::CONST_HIGH16: + // TODO: Support wide constants (RETURN_WIDE). + return AnalyseConstMethod(method_idx, code_item); + case Instruction::IGET: + return AnalyseIGetMethod(method_idx, code_item, kWord, false); + case Instruction::IGET_OBJECT: + return AnalyseIGetMethod(method_idx, code_item, kWord, true); + case Instruction::IGET_BOOLEAN: + case Instruction::IGET_BYTE: + return AnalyseIGetMethod(method_idx, code_item, kSignedByte, false); + case Instruction::IGET_CHAR: + return AnalyseIGetMethod(method_idx, code_item, kUnsignedHalf, false); + case Instruction::IGET_SHORT: + return AnalyseIGetMethod(method_idx, code_item, kSignedHalf, false); + case Instruction::IGET_WIDE: + return AnalyseIGetMethod(method_idx, code_item, kLong, false); + case Instruction::IPUT: + return AnalyseIPutMethod(method_idx, code_item, kWord, false); + case Instruction::IPUT_OBJECT: + return AnalyseIPutMethod(method_idx, code_item, kWord, true); + case Instruction::IPUT_BOOLEAN: + case Instruction::IPUT_BYTE: + return AnalyseIPutMethod(method_idx, code_item, kSignedByte, false); + case Instruction::IPUT_CHAR: + return AnalyseIPutMethod(method_idx, code_item, kUnsignedHalf, false); + case Instruction::IPUT_SHORT: + return AnalyseIPutMethod(method_idx, code_item, kSignedHalf, false); + case Instruction::IPUT_WIDE: + return AnalyseIPutMethod(method_idx, code_item, kLong, false); + default: + return false; + } +} + bool DexFileMethodInliner::IsIntrinsic(uint32_t method_index) { ReaderMutexLock mu(Thread::Current(), lock_); - return intrinsics_.find(method_index) != intrinsics_.end(); + auto it = inline_methods_.find(method_index); + return it != inline_methods_.end() && (it->second.flags & kInlineIntrinsic) != 0; } bool DexFileMethodInliner::GenIntrinsic(Mir2Lir* backend, CallInfo* info) { - Intrinsic intrinsic; + InlineMethod intrinsic; { ReaderMutexLock mu(Thread::Current(), lock_); - auto it = intrinsics_.find(info->index); - if (it == intrinsics_.end()) { + auto it = inline_methods_.find(info->index); + if (it == inline_methods_.end() || (it->second.flags & kInlineIntrinsic) == 0) { return false; } intrinsic = it->second; @@ -306,6 +363,27 @@ bool DexFileMethodInliner::GenIntrinsic(Mir2Lir* backend, CallInfo* info) { } } +bool DexFileMethodInliner::IsSpecial(uint32_t method_index) { + ReaderMutexLock mu(Thread::Current(), lock_); + auto it = inline_methods_.find(method_index); + return it != inline_methods_.end() && (it->second.flags & kInlineSpecial) != 0; +} + +bool DexFileMethodInliner::GenSpecial(Mir2Lir* backend, uint32_t method_idx) { + InlineMethod special; + { + ReaderMutexLock mu(Thread::Current(), lock_); + auto it = inline_methods_.find(method_idx); + if (it == inline_methods_.end() || (it->second.flags & kInlineSpecial) == 0) { + return false; + } + special = it->second; + } + // TODO: Return true only if special implementation is emitted. + backend->SpecialMIR2LIR(special); + return true; +} + uint32_t DexFileMethodInliner::FindClassIndex(const DexFile* dex_file, IndexCache* cache, ClassCacheIndex index) { uint32_t* class_index = &cache->class_indexes[index]; @@ -418,13 +496,148 @@ void DexFileMethodInliner::FindIntrinsics(const DexFile* dex_file) { DCHECK(dex_file_ == nullptr); IndexCache cache; for (const IntrinsicDef& def : kIntrinsicMethods) { - uint32_t method_id = FindMethodIndex(dex_file, &cache, def.method_def); - if (method_id != kIndexNotFound) { - DCHECK(intrinsics_.find(method_id) == intrinsics_.end()); - intrinsics_[method_id] = def.intrinsic; + uint32_t method_idx = FindMethodIndex(dex_file, &cache, def.method_def); + if (method_idx != kIndexNotFound) { + DCHECK(inline_methods_.find(method_idx) == inline_methods_.end()); + inline_methods_[method_idx] = def.intrinsic; } } dex_file_ = dex_file; } +bool DexFileMethodInliner::AddInlineMethod(int32_t method_idx, InlineMethodOpcode opcode, + uint16_t flags, uint32_t data) { + WriterMutexLock mu(Thread::Current(), lock_); + InlineMethod* im = &inline_methods_[method_idx]; + if (im->flags == 0) { + *im = InlineMethod{opcode, flags, data}; + return true; + } else { + // TODO: Warning about a method being already inlined? + LOG(WARNING) << "Inliner: " << PrettyMethod(method_idx, *dex_file_) << " already inline, " + << im->flags; + return false; + } +} + +bool DexFileMethodInliner::AnalyseReturnMethod(int32_t method_idx, + const DexFile::CodeItem* code_item, OpSize size) { + const Instruction* return_instruction = Instruction::At(code_item->insns_); + if (return_instruction->Opcode() == Instruction::RETURN_VOID) { + return AddInlineMethod(method_idx, kInlineOpNop, kInlineSpecial, 0); + } + uint32_t reg = return_instruction->VRegA_11x(); + uint32_t arg_start = code_item->registers_size_ - code_item->ins_size_; + DCHECK_GE(reg, arg_start); + DCHECK_LT(size == kLong ? reg + 1 : reg, code_item->registers_size_); + + InlineReturnArgData data; + data.d.arg = reg - arg_start; + data.d.op_size = size; + data.d.reserved = 0; + return AddInlineMethod(method_idx, kInlineOpReturnArg, kInlineSpecial, data.data); +} + +bool DexFileMethodInliner::AnalyseConstMethod(int32_t method_idx, + const DexFile::CodeItem* code_item) { + const Instruction* instruction = Instruction::At(code_item->insns_); + const Instruction* return_instruction = instruction->Next(); + Instruction::Code return_opcode = return_instruction->Opcode(); + if (return_opcode != Instruction::RETURN && + return_opcode != Instruction::RETURN_OBJECT) { + return false; + } + + uint32_t return_reg = return_instruction->VRegA_11x(); + DCHECK_LT(return_reg, code_item->registers_size_); + + uint32_t vA, vB, dummy; + uint64_t dummy_wide; + instruction->Decode(vA, vB, dummy_wide, dummy, nullptr); + if (instruction->Opcode() == Instruction::CONST_HIGH16) { + vB <<= 16; + } + DCHECK_LT(vA, code_item->registers_size_); + if (vA != return_reg) { + return false; // Not returning the value set by const? + } + if (return_opcode == Instruction::RETURN_OBJECT && vB != 0) { + return false; // Returning non-null reference constant? + } + return AddInlineMethod(method_idx, kInlineOpConst, kInlineSpecial, vB); +} + +bool DexFileMethodInliner::AnalyseIGetMethod(int32_t method_idx, const DexFile::CodeItem* code_item, + OpSize size, bool is_object) { + const Instruction* instruction = Instruction::At(code_item->insns_); + Instruction::Code opcode = instruction->Opcode(); + const Instruction* return_instruction = instruction->Next(); + Instruction::Code return_opcode = return_instruction->Opcode(); + if (!(return_opcode == Instruction::RETURN && size != kLong) && + !(return_opcode == Instruction::RETURN_WIDE && size == kLong) && + !(return_opcode == Instruction::RETURN_OBJECT && opcode == Instruction::IGET_OBJECT)) { + return false; + } + + uint32_t return_reg = return_instruction->VRegA_11x(); + DCHECK_LT(return_opcode == Instruction::RETURN_WIDE ? return_reg + 1 : return_reg, + code_item->registers_size_); + + uint32_t vA, vB, vC; + uint64_t dummy_wide; + instruction->Decode(vA, vB, dummy_wide, vC, nullptr); + uint32_t arg_start = code_item->registers_size_ - code_item->ins_size_; + DCHECK_GE(vB, arg_start); + DCHECK_LT(vB, code_item->registers_size_); + DCHECK_LT(size == kLong ? vA + 1 : vA, code_item->registers_size_); + if (vA != return_reg) { + return false; // Not returning the value retrieved by iget? + } + + // TODO: Check that the field is FastInstance(). + + InlineIGetIPutData data; + data.d.field = vC; + data.d.op_size = size; + data.d.is_object = is_object; + data.d.object_arg = vB - arg_start; // Allow iget on any register, not just "this" + data.d.src_arg = 0; + data.d.reserved = 0; + return AddInlineMethod(method_idx, kInlineOpIGet, kInlineSpecial, data.data); +} + +bool DexFileMethodInliner::AnalyseIPutMethod(int32_t method_idx, const DexFile::CodeItem* code_item, + OpSize size, bool is_object) { + const Instruction* instruction = Instruction::At(code_item->insns_); + const Instruction* return_instruction = instruction->Next(); + if (return_instruction->Opcode() != Instruction::RETURN_VOID) { + // TODO: Support returning an argument. + // This is needed by builder classes and generated accessor setters. + // builder.setX(value): iput value, this, fieldX; return-object this; + // object.access$nnn(value): iput value, this, fieldX; return value; + // Use InlineIGetIPutData::d::reserved to hold the information. + return false; + } + + uint32_t vA, vB, vC; + uint64_t dummy_wide; + instruction->Decode(vA, vB, dummy_wide, vC, nullptr); + uint32_t arg_start = code_item->registers_size_ - code_item->ins_size_; + DCHECK_GE(vB, arg_start); + DCHECK_GE(vA, arg_start); + DCHECK_LT(vB, code_item->registers_size_); + DCHECK_LT(size == kLong ? vA + 1 : vA, code_item->registers_size_); + + // TODO: Check that the field (vC) is FastInstance(). + + InlineIGetIPutData data; + data.d.field = vC; + data.d.op_size = size; + data.d.is_object = is_object; + data.d.object_arg = vB - arg_start; // Allow iput on any register, not just "this" + data.d.src_arg = vA - arg_start; + data.d.reserved = 0; + return AddInlineMethod(method_idx, kInlineOpIPut, kInlineSpecial, data.data); +} + } // namespace art diff --git a/compiler/dex/quick/dex_file_method_inliner.h b/compiler/dex/quick/dex_file_method_inliner.h index 9198f2a29d7..06de4fef27a 100644 --- a/compiler/dex/quick/dex_file_method_inliner.h +++ b/compiler/dex/quick/dex_file_method_inliner.h @@ -21,15 +21,16 @@ #include #include "base/mutex.h" #include "base/macros.h" +#include "dex/compiler_enums.h" +#include "dex_file.h" #include "locks.h" namespace art { class CallInfo; -class DexFile; class Mir2Lir; -enum IntrinsicOpcode { +enum InlineMethodOpcode : uint16_t { kIntrinsicDoubleCvt, kIntrinsicFloatCvt, kIntrinsicReverseBytes, @@ -47,8 +48,26 @@ enum IntrinsicOpcode { kIntrinsicCas, kIntrinsicUnsafeGet, kIntrinsicUnsafePut, + + kInlineOpNop, + kInlineOpReturnArg, + kInlineOpConst, + kInlineOpIGet, + kInlineOpIPut, +}; + +enum InlineMethodFlags { + kInlineIntrinsic = 0x0001, + kInlineSpecial = 0x0002, +}; + +struct InlineMethod { + uint16_t opcode; + uint16_t flags; + uint32_t data; }; +// IntrinsicFlags are stored in InlineMethod::data enum IntrinsicFlags { kIntrinsicFlagNone = 0, @@ -73,10 +92,32 @@ enum IntrinsicFlags { kIntrinsicFlagIsOrdered = 8, }; -struct Intrinsic { - IntrinsicOpcode opcode; +// Check that OpSize fits into 3 bits (at least the values the inliner uses). +COMPILE_ASSERT(kWord < 8 && kLong < 8 && kSingle < 8 && kDouble < 8 && kUnsignedHalf < 8 && + kSignedHalf < 8 && kUnsignedByte < 8 && kSignedByte < 8, op_size_field_too_narrow); + +union InlineIGetIPutData { uint32_t data; + struct { + uint16_t field; + uint32_t op_size : 3; // OpSize + uint32_t is_object : 1; + uint32_t object_arg : 4; + uint32_t src_arg : 4; // iput only + uint32_t reserved : 4; + } d; }; +COMPILE_ASSERT(sizeof(InlineIGetIPutData) == sizeof(uint32_t), InvalidSizeOfInlineIGetIPutData); + +union InlineReturnArgData { + uint32_t data; + struct { + uint16_t arg; + uint32_t op_size : 3; // OpSize + uint32_t reserved : 13; + } d; +}; +COMPILE_ASSERT(sizeof(InlineReturnArgData) == sizeof(uint32_t), InvalidSizeOfInlineReturnArgData); /** * Handles inlining of methods from a particular DexFile. @@ -95,6 +136,16 @@ class DexFileMethodInliner { DexFileMethodInliner(); ~DexFileMethodInliner(); + /** + * Analyse method code to determine if the method is a candidate for inlining. + * If it is, record its data for later. + * + * @param method_idx the index of the inlining candidate. + * @param code_item a previously verified code item of the method. + */ + bool AnalyseMethodCode(uint32_t method_idx, + const DexFile::CodeItem* code_item) LOCKS_EXCLUDED(lock_); + /** * Check whether a particular method index corresponds to an intrinsic function. */ @@ -105,6 +156,16 @@ class DexFileMethodInliner { */ bool GenIntrinsic(Mir2Lir* backend, CallInfo* info) LOCKS_EXCLUDED(lock_); + /** + * Check whether a particular method index corresponds to a special function. + */ + bool IsSpecial(uint32_t method_index) LOCKS_EXCLUDED(lock_); + + /** + * Generate code for a special function. + */ + bool GenSpecial(Mir2Lir* backend, uint32_t method_idx); + private: /** * To avoid multiple lookups of a class by its descriptor, we cache its @@ -261,7 +322,7 @@ class DexFileMethodInliner { */ struct IntrinsicDef { MethodDef method_def; - Intrinsic intrinsic; + InlineMethod intrinsic; }; /** @@ -281,8 +342,8 @@ class DexFileMethodInliner { uint32_t proto_indexes[kProtoCacheLast - kProtoCacheFirst]; }; - static const char* kClassCacheNames[]; - static const char* kNameCacheNames[]; + static const char* const kClassCacheNames[]; + static const char* const kNameCacheNames[]; static const ProtoDef kProtoCacheDefs[]; static const IntrinsicDef kIntrinsicMethods[]; @@ -307,11 +368,23 @@ class DexFileMethodInliner { friend class DexFileToMethodInlinerMap; + bool AddInlineMethod(int32_t method_idx, InlineMethodOpcode opcode, + uint16_t flags, uint32_t data) LOCKS_EXCLUDED(lock_); + + bool AnalyseReturnMethod(int32_t method_idx, const DexFile::CodeItem* code_item, + OpSize size) LOCKS_EXCLUDED(lock_); + bool AnalyseConstMethod(int32_t method_idx, const DexFile::CodeItem* code_item + ) LOCKS_EXCLUDED(lock_); + bool AnalyseIGetMethod(int32_t method_idx, const DexFile::CodeItem* code_item, + OpSize size, bool is_object) LOCKS_EXCLUDED(lock_); + bool AnalyseIPutMethod(int32_t method_idx, const DexFile::CodeItem* code_item, + OpSize size, bool is_object) LOCKS_EXCLUDED(lock_); + ReaderWriterMutex lock_; /* * Maps method indexes (for the particular DexFile) to Intrinsic defintions. */ - std::map intrinsics_ GUARDED_BY(lock_); + std::map inline_methods_ GUARDED_BY(lock_); const DexFile* dex_file_; DISALLOW_COPY_AND_ASSIGN(DexFileMethodInliner); diff --git a/compiler/dex/quick/gen_invoke.cc b/compiler/dex/quick/gen_invoke.cc index ee6f9c8fe9e..82a1932fe16 100644 --- a/compiler/dex/quick/gen_invoke.cc +++ b/compiler/dex/quick/gen_invoke.cc @@ -1239,12 +1239,9 @@ bool Mir2Lir::GenInlinedUnsafePut(CallInfo* info, bool is_long, void Mir2Lir::GenInvoke(CallInfo* info) { if (!(info->opt_flags & MIR_INLINED)) { - if (inliner_ == nullptr) { - QuickCompilerContext* context = reinterpret_cast( - cu_->compiler_driver->GetCompilerContext()); - inliner_ = context->GetInlinerMap()->GetMethodInliner(cu_->dex_file); - } - if (inliner_->GenIntrinsic(this, info)) { + DCHECK(cu_->compiler_driver->GetMethodInlinerMap() != nullptr); + if (cu_->compiler_driver->GetMethodInlinerMap()->GetMethodInliner(cu_->dex_file) + ->GenIntrinsic(this, info)) { return; } } diff --git a/compiler/dex/quick/mips/call_mips.cc b/compiler/dex/quick/mips/call_mips.cc index 21d55633df3..14f49aa3677 100644 --- a/compiler/dex/quick/mips/call_mips.cc +++ b/compiler/dex/quick/mips/call_mips.cc @@ -24,7 +24,7 @@ namespace art { void MipsMir2Lir::GenSpecialCase(BasicBlock* bb, MIR* mir, - SpecialCaseHandler special_case) { + const InlineMethod& special) { // TODO } diff --git a/compiler/dex/quick/mips/codegen_mips.h b/compiler/dex/quick/mips/codegen_mips.h index 450a44f7882..97dc2b3ad15 100644 --- a/compiler/dex/quick/mips/codegen_mips.h +++ b/compiler/dex/quick/mips/codegen_mips.h @@ -133,7 +133,7 @@ class MipsMir2Lir : public Mir2Lir { void GenNegFloat(RegLocation rl_dest, RegLocation rl_src); void GenPackedSwitch(MIR* mir, uint32_t table_offset, RegLocation rl_src); void GenSparseSwitch(MIR* mir, uint32_t table_offset, RegLocation rl_src); - void GenSpecialCase(BasicBlock* bb, MIR* mir, SpecialCaseHandler special_case); + void GenSpecialCase(BasicBlock* bb, MIR* mir, const InlineMethod& special); // Required for target - single operation generators. LIR* OpUnconditionalBranch(LIR* target); diff --git a/compiler/dex/quick/mir_to_lir.cc b/compiler/dex/quick/mir_to_lir.cc index 19d04be6dfa..c5bbae19239 100644 --- a/compiler/dex/quick/mir_to_lir.cc +++ b/compiler/dex/quick/mir_to_lir.cc @@ -789,7 +789,8 @@ bool Mir2Lir::MethodBlockCodeGen(BasicBlock* bb) { return false; } -void Mir2Lir::SpecialMIR2LIR(SpecialCaseHandler special_case) { +void Mir2Lir::SpecialMIR2LIR(const InlineMethod& special) { + cu_->NewTimingSplit("SpecialMIR2LIR"); // Find the first DalvikByteCode block. int num_reachable_blocks = mir_graph_->GetNumReachableBlocks(); BasicBlock*bb = NULL; @@ -815,7 +816,7 @@ void Mir2Lir::SpecialMIR2LIR(SpecialCaseHandler special_case) { ResetDefTracking(); ClobberAllRegs(); - GenSpecialCase(bb, mir, special_case); + GenSpecialCase(bb, mir, special); } void Mir2Lir::MethodMIR2LIR() { diff --git a/compiler/dex/quick/mir_to_lir.h b/compiler/dex/quick/mir_to_lir.h index 8415cbfb6d5..2a25f2f9100 100644 --- a/compiler/dex/quick/mir_to_lir.h +++ b/compiler/dex/quick/mir_to_lir.h @@ -105,6 +105,7 @@ typedef uint32_t CodeOffset; // Native code offset in bytes. struct BasicBlock; struct CallInfo; struct CompilationUnit; +struct InlineMethod; struct MIR; struct LIR; struct RegLocation; @@ -582,7 +583,7 @@ class Mir2Lir : public Backend { void CompileDalvikInstruction(MIR* mir, BasicBlock* bb, LIR* label_list); void HandleExtendedMethodMIR(BasicBlock* bb, MIR* mir); bool MethodBlockCodeGen(BasicBlock* bb); - void SpecialMIR2LIR(SpecialCaseHandler special_case); + void SpecialMIR2LIR(const InlineMethod& special); void MethodMIR2LIR(); @@ -703,7 +704,7 @@ class Mir2Lir : public Backend { virtual void GenSparseSwitch(MIR* mir, DexOffset table_offset, RegLocation rl_src) = 0; virtual void GenSpecialCase(BasicBlock* bb, MIR* mir, - SpecialCaseHandler special_case) = 0; + const InlineMethod& special) = 0; virtual void GenArrayGet(int opt_flags, OpSize size, RegLocation rl_array, RegLocation rl_index, RegLocation rl_dest, int scale) = 0; virtual void GenArrayPut(int opt_flags, OpSize size, RegLocation rl_array, @@ -826,8 +827,6 @@ class Mir2Lir : public Backend { unsigned int fp_spill_mask_; LIR* first_lir_insn_; LIR* last_lir_insn_; - // Lazily retrieved method inliner for intrinsics. - DexFileMethodInliner* inliner_; }; // Class Mir2Lir } // namespace art diff --git a/compiler/dex/quick/x86/call_x86.cc b/compiler/dex/quick/x86/call_x86.cc index 17924b0f080..4267b5b7845 100644 --- a/compiler/dex/quick/x86/call_x86.cc +++ b/compiler/dex/quick/x86/call_x86.cc @@ -23,7 +23,7 @@ namespace art { void X86Mir2Lir::GenSpecialCase(BasicBlock* bb, MIR* mir, - SpecialCaseHandler special_case) { + const InlineMethod& special) { // TODO } diff --git a/compiler/dex/quick/x86/codegen_x86.h b/compiler/dex/quick/x86/codegen_x86.h index 6552607d9e6..2d625c21344 100644 --- a/compiler/dex/quick/x86/codegen_x86.h +++ b/compiler/dex/quick/x86/codegen_x86.h @@ -133,7 +133,7 @@ class X86Mir2Lir : public Mir2Lir { void GenNegFloat(RegLocation rl_dest, RegLocation rl_src); void GenPackedSwitch(MIR* mir, DexOffset table_offset, RegLocation rl_src); void GenSparseSwitch(MIR* mir, DexOffset table_offset, RegLocation rl_src); - void GenSpecialCase(BasicBlock* bb, MIR* mir, SpecialCaseHandler special_case); + void GenSpecialCase(BasicBlock* bb, MIR* mir, const InlineMethod& special); // Single operation generators. LIR* OpUnconditionalBranch(LIR* target); diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc index dbebd26db83..11245419f7f 100644 --- a/compiler/driver/compiler_driver.cc +++ b/compiler/driver/compiler_driver.cc @@ -337,11 +337,13 @@ extern "C" void compilerLLVMSetBitcodeFileName(art::CompilerDriver& driver, std::string const& filename); CompilerDriver::CompilerDriver(VerifiedMethodsData* verified_methods_data, + DexFileToMethodInlinerMap* method_inliner_map, CompilerBackend compiler_backend, InstructionSet instruction_set, InstructionSetFeatures instruction_set_features, bool image, DescriptorSet* image_classes, size_t thread_count, bool dump_stats) : verified_methods_data_(verified_methods_data), + method_inliner_map_(method_inliner_map), compiler_backend_(compiler_backend), instruction_set_(instruction_set), instruction_set_features_(instruction_set_features), diff --git a/compiler/driver/compiler_driver.h b/compiler/driver/compiler_driver.h index 14bfde672d3..f4cc84dfe77 100644 --- a/compiler/driver/compiler_driver.h +++ b/compiler/driver/compiler_driver.h @@ -41,6 +41,7 @@ namespace art { class AOTCompilationStats; class ParallelCompilationManager; class DexCompilationUnit; +class DexFileToMethodInlinerMap; class OatWriter; class TimingLogger; class VerifiedMethodsData; @@ -92,6 +93,7 @@ class CompilerDriver { // can assume will be in the image, with NULL implying all available // classes. explicit CompilerDriver(VerifiedMethodsData* verified_methods_data, + DexFileToMethodInlinerMap* method_inliner_map, CompilerBackend compiler_backend, InstructionSet instruction_set, InstructionSetFeatures instruction_set_features, bool image, DescriptorSet* image_classes, @@ -111,6 +113,10 @@ class CompilerDriver { return verified_methods_data_; } + DexFileToMethodInlinerMap* GetMethodInlinerMap() const { + return method_inliner_map_; + } + const InstructionSet& GetInstructionSet() const { return instruction_set_; } @@ -397,6 +403,7 @@ class CompilerDriver { std::vector methods_to_patch_; VerifiedMethodsData* verified_methods_data_; + DexFileToMethodInlinerMap* method_inliner_map_; CompilerBackend compiler_backend_; diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc index b37cc544396..5662f36b587 100644 --- a/dex2oat/dex2oat.cc +++ b/dex2oat/dex2oat.cc @@ -53,9 +53,13 @@ #include "scoped_thread_state_change.h" #include "sirt_ref.h" #include "vector_output_stream.h" +#include "verifier/method_verifier.h" +#include "verifier/method_verifier-inl.h" #include "well_known_classes.h" #include "zip_archive.h" +#include "dex/quick/dex_file_to_method_inliner_map.h" + namespace art { static void UsageErrorV(const char* fmt, va_list ap) { @@ -265,6 +269,7 @@ class Dex2Oat { } UniquePtr driver(new CompilerDriver(verified_methods_data_.get(), + method_inliner_map_.get(), compiler_backend_, instruction_set_, instruction_set_features_, @@ -343,13 +348,21 @@ class Dex2Oat { private: class Dex2OatCompilerCallbacks : public CompilerCallbacks { public: - explicit Dex2OatCompilerCallbacks(VerifiedMethodsData* verified_methods_data) - : verified_methods_data_(verified_methods_data) { } + Dex2OatCompilerCallbacks(VerifiedMethodsData* verified_methods_data, + DexFileToMethodInlinerMap* method_inliner_map) + : verified_methods_data_(verified_methods_data), + method_inliner_map_(method_inliner_map) { } virtual ~Dex2OatCompilerCallbacks() { } virtual bool MethodVerified(verifier::MethodVerifier* verifier) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - return verified_methods_data_->ProcessVerifiedMethod(verifier); + bool result = verified_methods_data_->ProcessVerifiedMethod(verifier); + if (result && method_inliner_map_ != nullptr) { + MethodReference ref = verifier->GetMethodReference(); + method_inliner_map_->GetMethodInliner(ref.dex_file) + ->AnalyseMethodCode(ref.dex_method_index, verifier->CodeItem()); + } + return result; } virtual void ClassRejected(ClassReference ref) { verified_methods_data_->AddRejectedClass(ref); @@ -357,6 +370,7 @@ class Dex2Oat { private: VerifiedMethodsData* verified_methods_data_; + DexFileToMethodInlinerMap* method_inliner_map_; }; explicit Dex2Oat(CompilerBackend compiler_backend, @@ -367,7 +381,8 @@ class Dex2Oat { instruction_set_(instruction_set), instruction_set_features_(instruction_set_features), verified_methods_data_(new VerifiedMethodsData), - callbacks_(verified_methods_data_.get()), + method_inliner_map_(compiler_backend == kQuick ? new DexFileToMethodInlinerMap : nullptr), + callbacks_(verified_methods_data_.get(), method_inliner_map_.get()), runtime_(nullptr), thread_count_(thread_count), start_ns_(NanoTime()) { @@ -432,6 +447,7 @@ class Dex2Oat { const InstructionSetFeatures instruction_set_features_; UniquePtr verified_methods_data_; + UniquePtr method_inliner_map_; Dex2OatCompilerCallbacks callbacks_; Runtime* runtime_; size_t thread_count_; From 5bb149e1e33e89216af87a4c3809e02413277772 Mon Sep 17 00:00:00 2001 From: Mark Mendell Date: Tue, 17 Dec 2013 13:26:54 -0800 Subject: [PATCH 0294/2402] Fix SEGV when dumping MIR CFG I turned on kDebugDumpCFG to try to dump the CFGs, and I found that they caused a segmentation violation. The problem was that the orig_sreg was -1, and IsConst then blew up. Change-Id: Ib9acda0b11ce486e878ef2ccfae3b1bc1f82fdfb Signed-off-by: Mark Mendell --- compiler/dex/mir_graph.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/dex/mir_graph.h b/compiler/dex/mir_graph.h index bffec394d67..76c13f30c80 100644 --- a/compiler/dex/mir_graph.h +++ b/compiler/dex/mir_graph.h @@ -467,7 +467,7 @@ class MIRGraph { } bool IsConst(RegLocation loc) const { - return (IsConst(loc.orig_sreg)); + return loc.orig_sreg < 0 ? false : IsConst(loc.orig_sreg); } int32_t ConstantValue(RegLocation loc) const { From 7ab763caf16cc88a9696c1ebb727242106af61eb Mon Sep 17 00:00:00 2001 From: Brian Carlstrom Date: Mon, 9 Dec 2013 00:38:02 -0800 Subject: [PATCH 0295/2402] Move boot image generation to the build project Change-Id: I1c87640baa681ed5f0bc10bca8dc130895bb6a95 --- Android.mk | 19 +++++++----- build/Android.common.mk | 17 ++--------- build/Android.oat.mk | 64 +++++++---------------------------------- test/Android.mk | 1 - 4 files changed, 24 insertions(+), 77 deletions(-) diff --git a/Android.mk b/Android.mk index 0492d3cc5dc..612fc400ad4 100644 --- a/Android.mk +++ b/Android.mk @@ -264,9 +264,9 @@ else .PHONY: oat-target-$(1) oat-target-$(1): $$(OUT_OAT_FILE) -$$(OUT_OAT_FILE): $(PRODUCT_OUT)/$(1) $(TARGET_BOOT_IMG_OUT) $(DEX2OAT_DEPENDENCY) +$$(OUT_OAT_FILE): $(PRODUCT_OUT)/$(1) $(DEFAULT_DEX_PREOPT_BUILT_IMAGE) $(DEX2OAT_DEPENDENCY) @mkdir -p $$(dir $$@) - $(DEX2OAT) $(PARALLEL_ART_COMPILE_JOBS) --runtime-arg -Xms64m --runtime-arg -Xmx64m --boot-image=$(TARGET_BOOT_IMG_OUT) --dex-file=$(PRODUCT_OUT)/$(1) --dex-location=/$(1) --oat-file=$$@ --host-prefix=$(PRODUCT_OUT) --instruction-set=$(TARGET_ARCH) --instruction-set-features=$(TARGET_INSTRUCTION_SET_FEATURES) --android-root=$(PRODUCT_OUT)/system + $(DEX2OAT) --runtime-arg -Xms64m --runtime-arg -Xmx64m --boot-image=$(DEFAULT_DEX_PREOPT_BUILT_IMAGE) --dex-file=$(PRODUCT_OUT)/$(1) --dex-location=/$(1) --oat-file=$$@ --host-prefix=$(PRODUCT_OUT) --instruction-set=$(TARGET_ARCH) --instruction-set-features=$(TARGET_INSTRUCTION_SET_FEATURES) --android-root=$(PRODUCT_OUT)/system endif @@ -275,12 +275,12 @@ endef $(foreach file,\ $(filter-out\ - $(addprefix $(TARGET_OUT_JAVA_LIBRARIES)/,$(addsuffix .jar,$(TARGET_BOOT_JARS))),\ + $(addprefix $(TARGET_OUT_JAVA_LIBRARIES)/,$(addsuffix .jar,$(LIBART_TARGET_BOOT_JARS))),\ $(wildcard $(TARGET_OUT_APPS)/*.apk) $(wildcard $(TARGET_OUT_JAVA_LIBRARIES)/*.jar)),\ $(eval $(call declare-oat-target-target,$(subst $(PRODUCT_OUT)/,,$(file))))) .PHONY: oat-target -oat-target: $(ART_TARGET_DEPENDENCIES) $(TARGET_BOOT_OAT_OUT) $(OAT_TARGET_TARGETS) +oat-target: $(ART_TARGET_DEPENDENCIES) $(DEFAULT_DEX_PREOPT_INSTALLED_IMAGE) $(OAT_TARGET_TARGETS) .PHONY: oat-target-sync oat-target-sync: oat-target @@ -303,6 +303,11 @@ build-art-target: $(ART_TARGET_EXECUTABLES) $(ART_TARGET_TEST_EXECUTABLES) $(TAR ART_DUMP_OAT_PATH ?= $(OUT_DIR) +OATDUMP := $(HOST_OUT_EXECUTABLES)/oatdump$(HOST_EXECUTABLE_SUFFIX) +OATDUMPD := $(HOST_OUT_EXECUTABLES)/oatdumpd$(HOST_EXECUTABLE_SUFFIX) +# TODO: for now, override with debug version for better error reporting +OATDUMP := $(OATDUMPD) + .PHONY: dump-oat dump-oat: dump-oat-core dump-oat-boot @@ -325,14 +330,14 @@ endif .PHONY: dump-oat-boot ifeq ($(ART_BUILD_TARGET_NDEBUG),true) -dump-oat-boot: $(TARGET_BOOT_IMG_OUT) $(OATDUMP) - $(OATDUMP) --image=$(TARGET_BOOT_IMG_OUT) --output=$(ART_DUMP_OAT_PATH)/boot.oatdump.txt +dump-oat-boot: $(DEFAULT_DEX_PREOPT_BUILT_IMAGE) $(OATDUMP) + $(OATDUMP) --image=$(DEFAULT_DEX_PREOPT_BUILT_IMAGE) --output=$(ART_DUMP_OAT_PATH)/boot.oatdump.txt @echo Output in $(ART_DUMP_OAT_PATH)/boot.oatdump.txt endif .PHONY: dump-oat-Calculator ifeq ($(ART_BUILD_TARGET_NDEBUG),true) -dump-oat-Calculator: $(TARGET_OUT_APPS)/Calculator.odex $(TARGET_BOOT_IMG_OUT) $(OATDUMP) +dump-oat-Calculator: $(TARGET_OUT_APPS)/Calculator.odex $(DEFAULT_DEX_PREOPT_BUILT_IMAGE) $(OATDUMP) $(OATDUMP) --oat-file=$< --output=$(ART_DUMP_OAT_PATH)/Calculator.oatdump.txt @echo Output in $(ART_DUMP_OAT_PATH)/Calculator.oatdump.txt endif diff --git a/build/Android.common.mk b/build/Android.common.mk index a2c2e1ae5ee..30d7dcb42c2 100644 --- a/build/Android.common.mk +++ b/build/Android.common.mk @@ -140,19 +140,10 @@ art_debug_cflags := \ -DDYNAMIC_ANNOTATIONS_ENABLED=1 \ -UNDEBUG -# start of image reserved address space -IMG_HOST_BASE_ADDRESS := 0x60000000 - -ifeq ($(TARGET_ARCH),mips) -IMG_TARGET_BASE_ADDRESS := 0x30000000 -else -IMG_TARGET_BASE_ADDRESS := 0x60000000 -endif - -ART_HOST_CFLAGS := $(art_cflags) -DANDROID_SMP=1 -DART_BASE_ADDRESS=$(IMG_HOST_BASE_ADDRESS) +ART_HOST_CFLAGS := $(art_cflags) -DANDROID_SMP=1 -DART_BASE_ADDRESS=$(LIBART_IMG_HOST_BASE_ADDRESS) ART_HOST_CFLAGS += -DART_DEFAULT_INSTRUCTION_SET_FEATURES=default -ART_TARGET_CFLAGS := $(art_cflags) -DART_TARGET -DART_BASE_ADDRESS=$(IMG_TARGET_BASE_ADDRESS) +ART_TARGET_CFLAGS := $(art_cflags) -DART_TARGET -DART_BASE_ADDRESS=$(LIBART_IMG_TARGET_BASE_ADDRESS) ifeq ($(TARGET_CPU_SMP),true) ART_TARGET_CFLAGS += -DANDROID_SMP=1 else @@ -205,10 +196,6 @@ endif ART_TARGET_DEBUG_CFLAGS := $(art_debug_cflags) -ifeq ($(ART_USE_PORTABLE_COMPILER),true) -PARALLEL_ART_COMPILE_JOBS := -j8 -endif - ART_BUILD_TARGET := false ART_BUILD_HOST := false ART_BUILD_NDEBUG := false diff --git a/build/Android.oat.mk b/build/Android.oat.mk index c04b38b8a44..ee4541a2ac6 100644 --- a/build/Android.oat.mk +++ b/build/Android.oat.mk @@ -14,31 +14,12 @@ # limitations under the License. # -# DEX2OAT defined in build/core/config.mk -DEX2OATD := $(HOST_OUT_EXECUTABLES)/dex2oatd$(HOST_EXECUTABLE_SUFFIX) - -LIBART_COMPILER := $(HOST_OUT_SHARED_LIBRARIES)/libart-compiler$(HOST_SHLIB_SUFFIX) -LIBARTD_COMPILER := $(HOST_OUT_SHARED_LIBRARIES)/libartd-compiler$(HOST_SHLIB_SUFFIX) - -# TODO: for now, override with debug version for better error reporting -DEX2OAT := $(DEX2OATD) -LIBART_COMPILER := $(LIBARTD_COMPILER) - -# By default, do not run rerun dex2oat if the tool changes. -# Comment out the | to force dex2oat to rerun on after all changes. -DEX2OAT_DEPENDENCY := | -DEX2OAT_DEPENDENCY += $(DEX2OAT) -DEX2OAT_DEPENDENCY += $(LIBART_COMPILER) - -OATDUMP := $(HOST_OUT_EXECUTABLES)/oatdump$(HOST_EXECUTABLE_SUFFIX) -OATDUMPD := $(HOST_OUT_EXECUTABLES)/oatdumpd$(HOST_EXECUTABLE_SUFFIX) -# TODO: for now, override with debug version for better error reporting -OATDUMP := $(OATDUMPD) - -PRELOADED_CLASSES := frameworks/base/preloaded-classes - ######################################################################## -# A smaller libcore only oat file +# Rules to build a smaller "core" image to support core libraries +# (that is, non-Android frameworks) testing on the host and target +# +# The main rules to build the default "boot" image are in +# build/core/dex_preopt_libart.mk TARGET_CORE_JARS := core-libart conscrypt okhttp core-junit bouncycastle HOST_CORE_JARS := $(addsuffix -hostdex,$(TARGET_CORE_JARS)) @@ -62,17 +43,17 @@ TARGET_INSTRUCTION_SET_FEATURES := $(DEX2OAT_TARGET_INSTRUCTION_SET_FEATURES) $(HOST_CORE_IMG_OUT): $(HOST_CORE_DEX_FILES) $(DEX2OAT_DEPENDENCY) @echo "host dex2oat: $@ ($?)" @mkdir -p $(dir $@) - $(hide) $(DEX2OAT) $(PARALLEL_ART_COMPILE_JOBS) --runtime-arg -Xms16m --runtime-arg -Xmx16m --image-classes=$(PRELOADED_CLASSES) $(addprefix \ + $(hide) $(DEX2OAT) --runtime-arg -Xms16m --runtime-arg -Xmx16m --image-classes=$(PRELOADED_CLASSES) $(addprefix \ --dex-file=,$(HOST_CORE_DEX_FILES)) $(addprefix --dex-location=,$(HOST_CORE_DEX_LOCATIONS)) --oat-file=$(HOST_CORE_OAT_OUT) \ - --oat-location=$(HOST_CORE_OAT) --image=$(HOST_CORE_IMG_OUT) --base=$(IMG_HOST_BASE_ADDRESS) \ + --oat-location=$(HOST_CORE_OAT) --image=$(HOST_CORE_IMG_OUT) --base=$(LIBART_IMG_HOST_BASE_ADDRESS) \ --instruction-set=$(HOST_ARCH) --host --android-root=$(HOST_OUT) $(TARGET_CORE_IMG_OUT): $(TARGET_CORE_DEX_FILES) $(DEX2OAT_DEPENDENCY) @echo "target dex2oat: $@ ($?)" @mkdir -p $(dir $@) - $(hide) $(DEX2OAT) $(PARALLEL_ART_COMPILE_JOBS) --runtime-arg -Xms16m --runtime-arg -Xmx16m --image-classes=$(PRELOADED_CLASSES) $(addprefix \ + $(hide) $(DEX2OAT) --runtime-arg -Xms16m --runtime-arg -Xmx16m --image-classes=$(PRELOADED_CLASSES) $(addprefix \ --dex-file=,$(TARGET_CORE_DEX_FILES)) $(addprefix --dex-location=,$(TARGET_CORE_DEX_LOCATIONS)) --oat-file=$(TARGET_CORE_OAT_OUT) \ - --oat-location=$(TARGET_CORE_OAT) --image=$(TARGET_CORE_IMG_OUT) --base=$(IMG_TARGET_BASE_ADDRESS) \ + --oat-location=$(TARGET_CORE_OAT) --image=$(TARGET_CORE_IMG_OUT) --base=$(LIBART_IMG_TARGET_BASE_ADDRESS) \ --instruction-set=$(TARGET_ARCH) --instruction-set-features=$(TARGET_INSTRUCTION_SET_FEATURES) --host-prefix=$(PRODUCT_OUT) --android-root=$(PRODUCT_OUT)/system $(HOST_CORE_OAT_OUT): $(HOST_CORE_IMG_OUT) @@ -102,37 +83,12 @@ include $(BUILD_PHONY_PACKAGE) endif endif -######################################################################## -# The full system boot classpath -TARGET_BOOT_JARS := $(subst :, ,$(DEXPREOPT_BOOT_JARS)) -TARGET_BOOT_JARS := $(foreach jar,$(TARGET_BOOT_JARS),$(patsubst core, core-libart,$(jar))) -TARGET_BOOT_DEX_LOCATIONS := $(foreach jar,$(TARGET_BOOT_JARS),/$(DEXPREOPT_BOOT_JAR_DIR)/$(jar).jar) -TARGET_BOOT_DEX_FILES := $(foreach jar,$(TARGET_BOOT_JARS),$(call intermediates-dir-for,JAVA_LIBRARIES,$(jar),,COMMON)/javalib.jar) - -TARGET_BOOT_IMG_OUT := $(DEFAULT_DEX_PREOPT_IMAGE) -TARGET_BOOT_OAT_OUT := $(patsubst %.art,%.oat,$(TARGET_BOOT_IMG_OUT)) -TARGET_BOOT_OAT := $(subst $(PRODUCT_OUT),,$(TARGET_BOOT_OAT_OUT)) -TARGET_BOOT_OAT_UNSTRIPPED_OUT := $(TARGET_OUT_UNSTRIPPED)$(TARGET_BOOT_OAT) - -$(TARGET_BOOT_IMG_OUT): $(TARGET_BOOT_DEX_FILES) $(DEX2OAT_DEPENDENCY) - @echo "target dex2oat: $@ ($?)" - @mkdir -p $(dir $@) - @mkdir -p $(dir $(TARGET_BOOT_OAT_UNSTRIPPED_OUT)) - $(hide) $(DEX2OAT) $(PARALLEL_ART_COMPILE_JOBS) --runtime-arg -Xms256m --runtime-arg -Xmx256m --image-classes=$(PRELOADED_CLASSES) $(addprefix --dex-file=,$(TARGET_BOOT_DEX_FILES)) $(addprefix --dex-location=,$(TARGET_BOOT_DEX_LOCATIONS)) \ - --oat-symbols=$(TARGET_BOOT_OAT_UNSTRIPPED_OUT) --oat-file=$(TARGET_BOOT_OAT_OUT) \ - --oat-location=$(TARGET_BOOT_OAT) --image=$(TARGET_BOOT_IMG_OUT) --base=$(IMG_TARGET_BASE_ADDRESS) \ - --instruction-set=$(TARGET_ARCH) --instruction-set-features=$(TARGET_INSTRUCTION_SET_FEATURES) --host-prefix=$(PRODUCT_OUT) --android-root=$(PRODUCT_OUT)/system - -$(TARGET_BOOT_OAT_UNSTRIPPED_OUT): $(TARGET_BOOT_IMG_OUT) - -$(TARGET_BOOT_OAT_OUT): $(TARGET_BOOT_OAT_UNSTRIPPED_OUT) - ifeq ($(ART_BUILD_TARGET_NDEBUG),true) include $(CLEAR_VARS) LOCAL_MODULE := boot.art LOCAL_MODULE_TAGS := optional LOCAL_ADDITIONAL_DEPENDENCIES := art/build/Android.common.mk LOCAL_ADDITIONAL_DEPENDENCIES += art/build/Android.oat.mk -LOCAL_ADDITIONAL_DEPENDENCIES += $(TARGET_BOOT_IMG_OUT) $(TARGET_BOOT_OAT_OUT) +LOCAL_ADDITIONAL_DEPENDENCIES += $(DEFAULT_DEX_PREOPT_INSTALLED_IMAGE) include $(BUILD_PHONY_PACKAGE) endif diff --git a/test/Android.mk b/test/Android.mk index 1bf6074c207..5187724a4a6 100644 --- a/test/Android.mk +++ b/test/Android.mk @@ -71,7 +71,6 @@ define build-art-test-dex LOCAL_NO_STANDARD_LIBRARIES := true LOCAL_MODULE_PATH := $(3) LOCAL_DEX_PREOPT_IMAGE := $(TARGET_CORE_IMG_OUT) - LOCAL_DEX_PREOPT := false LOCAL_ADDITIONAL_DEPENDENCIES := art/build/Android.common.mk LOCAL_ADDITIONAL_DEPENDENCIES += $(LOCAL_PATH)/Android.mk include $(BUILD_JAVA_LIBRARY) From 573f7d2d68e1838a0485e6b40d90c967526e00c2 Mon Sep 17 00:00:00 2001 From: Hiroshi Yamauchi Date: Tue, 17 Dec 2013 11:54:23 -0800 Subject: [PATCH 0296/2402] Fix an array copy benchmark regression. Add different page release modes to rosalloc. Bug: 12064551 Change-Id: Ib837bbd1a2757741a4e2743e0a1272bf46a30252 --- runtime/gc/allocator/rosalloc.cc | 41 ++++++++++++++----- runtime/gc/allocator/rosalloc.h | 65 ++++++++++++++++++++++++++++-- runtime/gc/heap.cc | 4 +- runtime/gc/space/dlmalloc_space.h | 2 +- runtime/gc/space/malloc_space.cc | 4 +- runtime/gc/space/malloc_space.h | 13 ++++-- runtime/gc/space/rosalloc_space.cc | 30 ++++++++++---- runtime/gc/space/rosalloc_space.h | 10 ++--- runtime/gc/space/space_test.cc | 5 ++- 9 files changed, 137 insertions(+), 37 deletions(-) diff --git a/runtime/gc/allocator/rosalloc.cc b/runtime/gc/allocator/rosalloc.cc index 469b0985fdf..8ae61a3d227 100644 --- a/runtime/gc/allocator/rosalloc.cc +++ b/runtime/gc/allocator/rosalloc.cc @@ -37,12 +37,16 @@ size_t RosAlloc::bulkFreeBitMapOffsets[kNumOfSizeBrackets]; size_t RosAlloc::threadLocalFreeBitMapOffsets[kNumOfSizeBrackets]; bool RosAlloc::initialized_ = false; -RosAlloc::RosAlloc(void* base, size_t capacity) +RosAlloc::RosAlloc(void* base, size_t capacity, + PageReleaseMode page_release_mode, size_t page_release_size_threshold) : base_(reinterpret_cast(base)), footprint_(capacity), capacity_(capacity), lock_("rosalloc global lock", kRosAllocGlobalLock), - bulk_free_lock_("rosalloc bulk free lock", kRosAllocBulkFreeLock) { + bulk_free_lock_("rosalloc bulk free lock", kRosAllocBulkFreeLock), + page_release_mode_(page_release_mode), + page_release_size_threshold_(page_release_size_threshold) { DCHECK(RoundUp(capacity, kPageSize) == capacity); + CHECK(IsAligned(page_release_size_threshold_)); if (!initialized_) { Initialize(); } @@ -65,7 +69,9 @@ RosAlloc::RosAlloc(void* base, size_t capacity) } free_pages->SetByteSize(this, capacity_); DCHECK_EQ(capacity_ % kPageSize, static_cast(0)); + DCHECK(free_pages->IsFree()); free_pages->ReleasePages(this); + DCHECK(free_pages->IsFree()); free_page_runs_.insert(free_pages); if (kTraceRosAlloc) { LOG(INFO) << "RosAlloc::RosAlloc() : Inserted run 0x" << std::hex @@ -387,7 +393,9 @@ void RosAlloc::FreePages(Thread* self, void* ptr) { // Insert it. DCHECK_EQ(fpr->ByteSize(this) % kPageSize, static_cast(0)); DCHECK(free_page_runs_.find(fpr) == free_page_runs_.end()); + DCHECK(fpr->IsFree()); fpr->ReleasePages(this); + DCHECK(fpr->IsFree()); free_page_runs_.insert(fpr); DCHECK(free_page_runs_.find(fpr) != free_page_runs_.end()); if (kTraceRosAlloc) { @@ -404,20 +412,26 @@ void* RosAlloc::AllocLargeObject(Thread* self, size_t size, size_t* bytes_alloca MutexLock mu(self, lock_); r = AllocPages(self, num_pages, kPageMapLargeObject); } + if (UNLIKELY(r == nullptr)) { + if (kTraceRosAlloc) { + LOG(INFO) << "RosAlloc::AllocLargeObject() : NULL"; + } + return nullptr; + } if (bytes_allocated != NULL) { *bytes_allocated = num_pages * kPageSize; } if (kTraceRosAlloc) { - if (r != NULL) { - LOG(INFO) << "RosAlloc::AllocLargeObject() : 0x" << std::hex << reinterpret_cast(r) - << "-0x" << (reinterpret_cast(r) + num_pages * kPageSize) - << "(" << std::dec << (num_pages * kPageSize) << ")"; - } else { - LOG(INFO) << "RosAlloc::AllocLargeObject() : NULL"; - } + LOG(INFO) << "RosAlloc::AllocLargeObject() : 0x" << std::hex << reinterpret_cast(r) + << "-0x" << (reinterpret_cast(r) + num_pages * kPageSize) + << "(" << std::dec << (num_pages * kPageSize) << ")"; + } + if (!DoesReleaseAllPages()) { + // If it does not release all pages, pages may not be zeroed out. + memset(r, 0, size); } // Check if the returned memory is really all zero. - if (kCheckZeroMemory && r != NULL) { + if (kCheckZeroMemory) { byte* bytes = reinterpret_cast(r); for (size_t i = 0; i < size; ++i) { DCHECK_EQ(bytes[i], 0); @@ -1366,7 +1380,12 @@ void RosAlloc::InspectAll(void (*handler)(void* start, void* end, size_t used_by size_t fpr_size = fpr->ByteSize(this); DCHECK(IsAligned(fpr_size)); void* start = fpr; - void* end = reinterpret_cast(start) + fpr_size; + if (kIsDebugBuild) { + // In the debug build, the first page of a free page run + // contains a magic number for debugging. Exclude it. + start = reinterpret_cast(fpr) + kPageSize; + } + void* end = reinterpret_cast(fpr) + fpr_size; handler(start, end, 0, arg); size_t num_pages = fpr_size / kPageSize; if (kIsDebugBuild) { diff --git a/runtime/gc/allocator/rosalloc.h b/runtime/gc/allocator/rosalloc.h index d5b6de1637a..4eb133159fc 100644 --- a/runtime/gc/allocator/rosalloc.h +++ b/runtime/gc/allocator/rosalloc.h @@ -91,18 +91,50 @@ class RosAlloc { byte* end = fpr_base + ByteSize(rosalloc); return end; } + bool IsLargerThanPageReleaseThreshold(RosAlloc* rosalloc) + EXCLUSIVE_LOCKS_REQUIRED(rosalloc->lock_) { + return ByteSize(rosalloc) >= rosalloc->page_release_size_threshold_; + } + bool IsAtEndOfSpace(RosAlloc* rosalloc) + EXCLUSIVE_LOCKS_REQUIRED(rosalloc->lock_) { + return reinterpret_cast(this) + ByteSize(rosalloc) == rosalloc->base_ + rosalloc->footprint_; + } + bool ShouldReleasePages(RosAlloc* rosalloc) EXCLUSIVE_LOCKS_REQUIRED(rosalloc->lock_) { + switch (rosalloc->page_release_mode_) { + case kPageReleaseModeNone: + return false; + case kPageReleaseModeEnd: + return IsAtEndOfSpace(rosalloc); + case kPageReleaseModeSize: + return IsLargerThanPageReleaseThreshold(rosalloc); + case kPageReleaseModeSizeAndEnd: + return IsLargerThanPageReleaseThreshold(rosalloc) && IsAtEndOfSpace(rosalloc); + case kPageReleaseModeAll: + return true; + default: + LOG(FATAL) << "Unexpected page release mode "; + return false; + } + } void ReleasePages(RosAlloc* rosalloc) EXCLUSIVE_LOCKS_REQUIRED(rosalloc->lock_) { + byte* start = reinterpret_cast(this); size_t byte_size = ByteSize(rosalloc); DCHECK_EQ(byte_size % kPageSize, static_cast(0)); + bool release_pages = ShouldReleasePages(rosalloc); if (kIsDebugBuild) { // Exclude the first page that stores the magic number. DCHECK_GE(byte_size, static_cast(kPageSize)); + start += kPageSize; byte_size -= kPageSize; if (byte_size > 0) { - madvise(reinterpret_cast(this) + kPageSize, byte_size, MADV_DONTNEED); + if (release_pages) { + madvise(start, byte_size, MADV_DONTNEED); + } } } else { - madvise(this, byte_size, MADV_DONTNEED); + if (release_pages) { + madvise(start, byte_size, MADV_DONTNEED); + } } } }; @@ -363,6 +395,21 @@ class RosAlloc { } }; + public: + // Different page release modes. + enum PageReleaseMode { + kPageReleaseModeNone, // Release no empty pages. + kPageReleaseModeEnd, // Release empty pages at the end of the space. + kPageReleaseModeSize, // Release empty pages that are larger than the threshold. + kPageReleaseModeSizeAndEnd, // Release empty pages that are larger than the threshold or + // at the end of the space. + kPageReleaseModeAll, // Release all empty pages. + }; + + // The default value for page_release_size_threshold_. + static constexpr size_t kDefaultPageReleaseSizeThreshold = 4 * MB; + + private: // The base address of the memory region that's managed by this allocator. byte* base_; @@ -412,6 +459,12 @@ class RosAlloc { // allowing multiple individual frees at the same time. ReaderWriterMutex bulk_free_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER; + // The page release mode. + const PageReleaseMode page_release_mode_; + // Under kPageReleaseModeSize(AndEnd), if the free page run size is + // greater than or equal to this value, release pages. + const size_t page_release_size_threshold_; + // The base address of the memory region that's managed by this allocator. byte* Begin() { return base_; } // The end address of the memory region that's managed by this allocator. @@ -439,7 +492,9 @@ class RosAlloc { void* AllocLargeObject(Thread* self, size_t size, size_t* bytes_allocated) LOCKS_EXCLUDED(lock_); public: - RosAlloc(void* base, size_t capacity); + RosAlloc(void* base, size_t capacity, + PageReleaseMode page_release_mode, + size_t page_release_size_threshold = kDefaultPageReleaseSizeThreshold); void* Alloc(Thread* self, size_t size, size_t* bytes_allocated) LOCKS_EXCLUDED(lock_); void Free(Thread* self, void* ptr) @@ -480,6 +535,10 @@ class RosAlloc { // allocated and objects allocated, respectively. static void BytesAllocatedCallback(void* start, void* end, size_t used_bytes, void* arg); static void ObjectsAllocatedCallback(void* start, void* end, size_t used_bytes, void* arg); + + bool DoesReleaseAllPages() const { + return page_release_mode_ == kPageReleaseModeAll; + } }; } // namespace allocator diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc index 76a8e794c8c..61c66e70dc3 100644 --- a/runtime/gc/heap.cc +++ b/runtime/gc/heap.cc @@ -180,7 +180,7 @@ Heap::Heap(size_t initial_size, size_t growth_limit, size_t min_free, size_t max requested_alloc_space_begin); } else { non_moving_space_ = space::RosAllocSpace::Create(name, initial_size, growth_limit, capacity, - requested_alloc_space_begin); + requested_alloc_space_begin, low_memory_mode_); } if (kMovingCollector) { // TODO: Place bump-pointer spaces somewhere to minimize size of card table. @@ -1151,7 +1151,7 @@ void Heap::PreZygoteFork() { // Turn the current alloc space into a zygote space and obtain the new alloc space composed of // the remaining available heap memory. space::MallocSpace* zygote_space = non_moving_space_; - non_moving_space_ = zygote_space->CreateZygoteSpace("alloc space"); + non_moving_space_ = zygote_space->CreateZygoteSpace("alloc space", low_memory_mode_); non_moving_space_->SetFootprintLimit(non_moving_space_->Capacity()); // Change the GC retention policy of the zygote space to only collect when full. zygote_space->SetGcRetentionPolicy(space::kGcRetentionPolicyFullCollect); diff --git a/runtime/gc/space/dlmalloc_space.h b/runtime/gc/space/dlmalloc_space.h index d18d4ad2a0e..c5293147d47 100644 --- a/runtime/gc/space/dlmalloc_space.h +++ b/runtime/gc/space/dlmalloc_space.h @@ -117,7 +117,7 @@ class DlMallocSpace : public MallocSpace { mirror::Object* AllocWithoutGrowthLocked(Thread* self, size_t num_bytes, size_t* bytes_allocated) EXCLUSIVE_LOCKS_REQUIRED(lock_); - void* CreateAllocator(void* base, size_t morecore_start, size_t initial_size) { + void* CreateAllocator(void* base, size_t morecore_start, size_t initial_size, bool /*low_memory_mode*/) { return CreateMspace(base, morecore_start, initial_size); } static void* CreateMspace(void* base, size_t morecore_start, size_t initial_size); diff --git a/runtime/gc/space/malloc_space.cc b/runtime/gc/space/malloc_space.cc index 785b5ed2760..46df0a1e213 100644 --- a/runtime/gc/space/malloc_space.cc +++ b/runtime/gc/space/malloc_space.cc @@ -176,7 +176,7 @@ void MallocSpace::UnBindBitmaps() { DCHECK(temp_bitmap_.get() == NULL); } -MallocSpace* MallocSpace::CreateZygoteSpace(const char* alloc_space_name) { +MallocSpace* MallocSpace::CreateZygoteSpace(const char* alloc_space_name, bool low_memory_mode) { // For RosAlloc, revoke thread local runs before creating a new // alloc space so that we won't mix thread local runs from different // alloc spaces. @@ -213,7 +213,7 @@ MallocSpace* MallocSpace::CreateZygoteSpace(const char* alloc_space_name) { UniquePtr mem_map(GetMemMap()->RemapAtEnd(end_, alloc_space_name, PROT_READ | PROT_WRITE, &error_msg)); CHECK(mem_map.get() != nullptr) << error_msg; - void* allocator = CreateAllocator(end_, starting_size, initial_size); + void* allocator = CreateAllocator(end_, starting_size, initial_size, low_memory_mode); // Protect memory beyond the initial size. byte* end = mem_map->Begin() + starting_size; if (capacity - initial_size > 0) { diff --git a/runtime/gc/space/malloc_space.h b/runtime/gc/space/malloc_space.h index 0f882d33e11..4c8b05f924e 100644 --- a/runtime/gc/space/malloc_space.h +++ b/runtime/gc/space/malloc_space.h @@ -127,8 +127,11 @@ class MallocSpace : public ContinuousMemMapAllocSpace { virtual MallocSpace* CreateInstance(const std::string& name, MemMap* mem_map, void* allocator, byte* begin, byte* end, byte* limit, size_t growth_limit) = 0; - // Turn ourself into a zygote space and return a new alloc space which has our unused memory. - MallocSpace* CreateZygoteSpace(const char* alloc_space_name); + // Turn ourself into a zygote space and return a new alloc space + // which has our unused memory. When true, the low memory mode + // argument specifies that the heap wishes the created space to be + // more aggressive in releasing unused pages. + MallocSpace* CreateZygoteSpace(const char* alloc_space_name, bool low_memory_mode); virtual uint64_t GetBytesAllocated() = 0; virtual uint64_t GetObjectsAllocated() = 0; @@ -154,7 +157,11 @@ class MallocSpace : public ContinuousMemMapAllocSpace { static MemMap* CreateMemMap(const std::string& name, size_t starting_size, size_t* initial_size, size_t* growth_limit, size_t* capacity, byte* requested_begin); - virtual void* CreateAllocator(void* base, size_t morecore_start, size_t initial_size) = 0; + // When true the low memory mode argument specifies that the heap + // wishes the created allocator to be more aggressive in releasing + // unused pages. + virtual void* CreateAllocator(void* base, size_t morecore_start, size_t initial_size, + bool low_memory_mode) = 0; void RegisterRecentFree(mirror::Object* ptr) EXCLUSIVE_LOCKS_REQUIRED(lock_); diff --git a/runtime/gc/space/rosalloc_space.cc b/runtime/gc/space/rosalloc_space.cc index 1f8e324823d..0438f8d41e4 100644 --- a/runtime/gc/space/rosalloc_space.cc +++ b/runtime/gc/space/rosalloc_space.cc @@ -45,7 +45,7 @@ RosAllocSpace::RosAllocSpace(const std::string& name, MemMap* mem_map, } RosAllocSpace* RosAllocSpace::Create(const std::string& name, size_t initial_size, size_t growth_limit, - size_t capacity, byte* requested_begin) { + size_t capacity, byte* requested_begin, bool low_memory_mode) { uint64_t start_time = 0; if (VLOG_IS_ON(heap) || VLOG_IS_ON(startup)) { start_time = NanoTime(); @@ -68,7 +68,8 @@ RosAllocSpace* RosAllocSpace::Create(const std::string& name, size_t initial_siz << PrettySize(capacity); return NULL; } - allocator::RosAlloc* rosalloc = CreateRosAlloc(mem_map->Begin(), starting_size, initial_size); + allocator::RosAlloc* rosalloc = CreateRosAlloc(mem_map->Begin(), starting_size, initial_size, + low_memory_mode); if (rosalloc == NULL) { LOG(ERROR) << "Failed to initialize rosalloc for alloc space (" << name << ")"; return NULL; @@ -97,13 +98,18 @@ RosAllocSpace* RosAllocSpace::Create(const std::string& name, size_t initial_siz return space; } -allocator::RosAlloc* RosAllocSpace::CreateRosAlloc(void* begin, size_t morecore_start, size_t initial_size) { +allocator::RosAlloc* RosAllocSpace::CreateRosAlloc(void* begin, size_t morecore_start, size_t initial_size, + bool low_memory_mode) { // clear errno to allow PLOG on error errno = 0; // create rosalloc using our backing storage starting at begin and // with a footprint of morecore_start. When morecore_start bytes of // memory is exhaused morecore will be called. - allocator::RosAlloc* rosalloc = new art::gc::allocator::RosAlloc(begin, morecore_start); + allocator::RosAlloc* rosalloc = new art::gc::allocator::RosAlloc( + begin, morecore_start, + low_memory_mode ? + art::gc::allocator::RosAlloc::kPageReleaseModeAll : + art::gc::allocator::RosAlloc::kPageReleaseModeSizeAndEnd); if (rosalloc != NULL) { rosalloc->SetFootprintLimit(initial_size); } else { @@ -216,10 +222,18 @@ size_t RosAllocSpace::AllocationSize(const mirror::Object* obj) { } size_t RosAllocSpace::Trim() { - MutexLock mu(Thread::Current(), lock_); - // Trim to release memory at the end of the space. - rosalloc_->Trim(); - // No inspect_all necessary here as trimming of pages is built-in. + { + MutexLock mu(Thread::Current(), lock_); + // Trim to release memory at the end of the space. + rosalloc_->Trim(); + } + // Attempt to release pages if it does not release all empty pages. + if (!rosalloc_->DoesReleaseAllPages()) { + VLOG(heap) << "RosAllocSpace::Trim() "; + size_t reclaimed = 0; + InspectAllRosAlloc(DlmallocMadviseCallback, &reclaimed); + return reclaimed; + } return 0; } diff --git a/runtime/gc/space/rosalloc_space.h b/runtime/gc/space/rosalloc_space.h index 6311580d146..5b7616c4254 100644 --- a/runtime/gc/space/rosalloc_space.h +++ b/runtime/gc/space/rosalloc_space.h @@ -38,7 +38,7 @@ class RosAllocSpace : public MallocSpace { // the caller should call Begin on the returned space to confirm the // request was granted. static RosAllocSpace* Create(const std::string& name, size_t initial_size, size_t growth_limit, - size_t capacity, byte* requested_begin); + size_t capacity, byte* requested_begin, bool low_memory_mode); virtual mirror::Object* AllocWithGrowth(Thread* self, size_t num_bytes, size_t* bytes_allocated) LOCKS_EXCLUDED(lock_); @@ -115,11 +115,11 @@ class RosAllocSpace : public MallocSpace { size_t InternalAllocationSize(const mirror::Object* obj); mirror::Object* AllocWithoutGrowthLocked(Thread* self, size_t num_bytes, size_t* bytes_allocated); - void* CreateAllocator(void* base, size_t morecore_start, size_t initial_size) { - return CreateRosAlloc(base, morecore_start, initial_size); + void* CreateAllocator(void* base, size_t morecore_start, size_t initial_size, bool low_memory_mode) { + return CreateRosAlloc(base, morecore_start, initial_size, low_memory_mode); } - static allocator::RosAlloc* CreateRosAlloc(void* base, size_t morecore_start, size_t initial_size); - + static allocator::RosAlloc* CreateRosAlloc(void* base, size_t morecore_start, size_t initial_size, + bool low_memory_mode); void InspectAllRosAlloc(void (*callback)(void *start, void *end, size_t num_bytes, void* callback_arg), void* arg) diff --git a/runtime/gc/space/space_test.cc b/runtime/gc/space/space_test.cc index 60c3b1c08c3..b1be9d8a849 100644 --- a/runtime/gc/space/space_test.cc +++ b/runtime/gc/space/space_test.cc @@ -58,7 +58,8 @@ class SpaceTest : public CommonTest { } static MallocSpace* CreateRosAllocSpace(const std::string& name, size_t initial_size, size_t growth_limit, size_t capacity, byte* requested_begin) { - return RosAllocSpace::Create(name, initial_size, growth_limit, capacity, requested_begin); + return RosAllocSpace::Create(name, initial_size, growth_limit, capacity, requested_begin, + Runtime::Current()->GetHeap()->IsLowMemoryMode()); } typedef MallocSpace* (*CreateSpaceFn)(const std::string& name, size_t initial_size, size_t growth_limit, @@ -178,7 +179,7 @@ void SpaceTest::ZygoteSpaceTestBody(CreateSpaceFn create_space) { // Make sure that the zygote space isn't directly at the start of the space. space->Alloc(self, 1U * MB, &dummy); - space = space->CreateZygoteSpace("alloc space"); + space = space->CreateZygoteSpace("alloc space", Runtime::Current()->GetHeap()->IsLowMemoryMode()); // Make space findable to the heap, will also delete space when runtime is cleaned up AddSpace(space); From 8a7b39caf0eaba78a4123e1e299c8b3e13b1d980 Mon Sep 17 00:00:00 2001 From: Ying Wang Date: Tue, 17 Dec 2013 21:37:47 -0800 Subject: [PATCH 0297/2402] Build boot.art only if we are building the host toolchain. This fixed the Mac checkbuild. Change-Id: I88780cddf1fec6306e0c54b6d801a34b2ddafabf --- build/Android.oat.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/Android.oat.mk b/build/Android.oat.mk index ee4541a2ac6..ec6efbc599f 100644 --- a/build/Android.oat.mk +++ b/build/Android.oat.mk @@ -81,7 +81,6 @@ LOCAL_ADDITIONAL_DEPENDENCIES += art/build/Android.oat.mk LOCAL_ADDITIONAL_DEPENDENCIES += $(TARGET_CORE_IMG_OUT) include $(BUILD_PHONY_PACKAGE) endif -endif ifeq ($(ART_BUILD_TARGET_NDEBUG),true) include $(CLEAR_VARS) @@ -92,3 +91,4 @@ LOCAL_ADDITIONAL_DEPENDENCIES += art/build/Android.oat.mk LOCAL_ADDITIONAL_DEPENDENCIES += $(DEFAULT_DEX_PREOPT_INSTALLED_IMAGE) include $(BUILD_PHONY_PACKAGE) endif +endif From 778127aed722b9702a382f1276c0f05447c654be Mon Sep 17 00:00:00 2001 From: Vladimir Marko Date: Wed, 18 Dec 2013 09:56:15 +0000 Subject: [PATCH 0298/2402] Fix build - style issues. Change-Id: I40540dac2a51ef326e642116b5c64fb581c849f2 --- compiler/dex/quick/dex_file_method_inliner.cc | 4 +++- compiler/dex/quick/dex_file_method_inliner.h | 4 ++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/compiler/dex/quick/dex_file_method_inliner.cc b/compiler/dex/quick/dex_file_method_inliner.cc index 3c331e12d99..c21ddcc4307 100644 --- a/compiler/dex/quick/dex_file_method_inliner.cc +++ b/compiler/dex/quick/dex_file_method_inliner.cc @@ -510,7 +510,9 @@ bool DexFileMethodInliner::AddInlineMethod(int32_t method_idx, InlineMethodOpcod WriterMutexLock mu(Thread::Current(), lock_); InlineMethod* im = &inline_methods_[method_idx]; if (im->flags == 0) { - *im = InlineMethod{opcode, flags, data}; + im->opcode = opcode; + im->flags = flags; + im->data = data; return true; } else { // TODO: Warning about a method being already inlined? diff --git a/compiler/dex/quick/dex_file_method_inliner.h b/compiler/dex/quick/dex_file_method_inliner.h index 06de4fef27a..1d9973108b0 100644 --- a/compiler/dex/quick/dex_file_method_inliner.h +++ b/compiler/dex/quick/dex_file_method_inliner.h @@ -373,8 +373,8 @@ class DexFileMethodInliner { bool AnalyseReturnMethod(int32_t method_idx, const DexFile::CodeItem* code_item, OpSize size) LOCKS_EXCLUDED(lock_); - bool AnalyseConstMethod(int32_t method_idx, const DexFile::CodeItem* code_item - ) LOCKS_EXCLUDED(lock_); + bool AnalyseConstMethod(int32_t method_idx, const DexFile::CodeItem* code_item) + LOCKS_EXCLUDED(lock_); bool AnalyseIGetMethod(int32_t method_idx, const DexFile::CodeItem* code_item, OpSize size, bool is_object) LOCKS_EXCLUDED(lock_); bool AnalyseIPutMethod(int32_t method_idx, const DexFile::CodeItem* code_item, From 102a8f27cc8d719f4bf58aa294475112c1ae66b9 Mon Sep 17 00:00:00 2001 From: Sebastien Hertz Date: Wed, 18 Dec 2013 11:41:30 +0100 Subject: [PATCH 0299/2402] Fix build - test issues. Fix compilation errors in tests. Change-Id: Id2405edc2eec5bef9ebf5f241db03a0bb4a6b5a3 --- compiler/oat_test.cc | 6 +++++- runtime/common_test.h | 12 ++++++++++-- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/compiler/oat_test.cc b/compiler/oat_test.cc index fd0a69deea5..94bc30d970b 100644 --- a/compiler/oat_test.cc +++ b/compiler/oat_test.cc @@ -79,7 +79,11 @@ TEST_F(OatTest, WriteRead) { InstructionSet insn_set = kIsTargetBuild ? kThumb2 : kX86; InstructionSetFeatures insn_features; - compiler_driver_.reset(new CompilerDriver(compiler_backend, insn_set, + verified_methods_data_.reset(new VerifiedMethodsData); + method_inliner_map_.reset(new DexFileToMethodInlinerMap); + compiler_driver_.reset(new CompilerDriver(verified_methods_data_.get(), + method_inliner_map_.get(), + compiler_backend, insn_set, insn_features, false, NULL, 2, true)); jobject class_loader = NULL; if (kCompile) { diff --git a/runtime/common_test.h b/runtime/common_test.h index d3151c36cc1..8dd76fe64b0 100644 --- a/runtime/common_test.h +++ b/runtime/common_test.h @@ -25,12 +25,14 @@ #include #include "../../external/icu4c/common/unicode/uvernum.h" +#include "../compiler/dex/quick/dex_file_to_method_inliner_map.h" +#include "../compiler/dex/verified_methods_data.h" +#include "../compiler/driver/compiler_driver.h" #include "base/macros.h" #include "base/stl_util.h" #include "base/stringprintf.h" #include "base/unix_file/fd_file.h" #include "class_linker.h" -#include "compiler/driver/compiler_driver.h" #include "dex_file-inl.h" #include "entrypoints/entrypoint_utils.h" #include "gc/heap.h" @@ -454,7 +456,11 @@ class CommonTest : public testing::Test { } } class_linker_->FixupDexCaches(runtime_->GetResolutionMethod()); - compiler_driver_.reset(new CompilerDriver(compiler_backend, instruction_set, + verified_methods_data_.reset(new VerifiedMethodsData); + method_inliner_map_.reset(new DexFileToMethodInlinerMap); + compiler_driver_.reset(new CompilerDriver(verified_methods_data_.get(), + method_inliner_map_.get(), + compiler_backend, instruction_set, instruction_set_features, true, new CompilerDriver::DescriptorSet, 2, true)); @@ -634,6 +640,8 @@ class CommonTest : public testing::Test { UniquePtr runtime_; // Owned by the runtime ClassLinker* class_linker_; + UniquePtr verified_methods_data_; + UniquePtr method_inliner_map_; UniquePtr compiler_driver_; private: From a9faa706acb5a2e571ebfbc7d20f95504b3d8cdc Mon Sep 17 00:00:00 2001 From: Vladimir Marko Date: Tue, 17 Dec 2013 11:17:52 +0000 Subject: [PATCH 0300/2402] Change safecast data from set to an ordered vector. This saves memory and improves performance. Bug: 12167380 Change-Id: Ica8d9291cdd515e8eab03814fb75a85d424eb629 --- compiler/dex/verified_methods_data.cc | 8 +++++--- compiler/dex/verified_methods_data.h | 4 +++- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/compiler/dex/verified_methods_data.cc b/compiler/dex/verified_methods_data.cc index 454b92cd851..b3ceefcb630 100644 --- a/compiler/dex/verified_methods_data.cc +++ b/compiler/dex/verified_methods_data.cc @@ -130,8 +130,8 @@ bool VerifiedMethodsData::IsSafeCast(MethodReference ref, uint32_t pc) { } // Look up the cast address in the set of safe casts - MethodSafeCastSet::const_iterator cast_it = it->second->find(pc); - return cast_it != it->second->end(); + // Use binary_search for lookup in the sorted vector. + return std::binary_search(it->second->begin(), it->second->end(), pc); } void VerifiedMethodsData::AddRejectedClass(ClassReference ref) { @@ -337,8 +337,10 @@ VerifiedMethodsData::MethodSafeCastSet* VerifiedMethodsData::GenerateSafeCastSet if (is_safe_cast) { if (mscs.get() == nullptr) { mscs.reset(new MethodSafeCastSet()); + } else { + DCHECK_LT(mscs->back(), dex_pc); // Verify ordering for push_back() to the sorted vector. } - mscs->insert(dex_pc); + mscs->push_back(dex_pc); } } } diff --git a/compiler/dex/verified_methods_data.h b/compiler/dex/verified_methods_data.h index 8d5058ab2f9..d495dff7d98 100644 --- a/compiler/dex/verified_methods_data.h +++ b/compiler/dex/verified_methods_data.h @@ -83,7 +83,9 @@ class VerifiedMethodsData { LOCKS_EXCLUDED(dex_gc_maps_lock_); // Cast elision types. - typedef std::set MethodSafeCastSet; + // Since we're adding the dex PCs to the set in increasing order, a sorted vector + // is better for performance (not just memory usage), especially for large sets. + typedef std::vector MethodSafeCastSet; typedef SafeMap SafeCastMap; MethodSafeCastSet* GenerateSafeCastSet(verifier::MethodVerifier* method_verifier) From 95a4de734c2d90481af9938e0759717895fdcc59 Mon Sep 17 00:00:00 2001 From: Vladimir Marko Date: Wed, 18 Dec 2013 10:29:11 +0000 Subject: [PATCH 0301/2402] Fix test-art setup. Change-Id: I5762bd10d0bb8346541346b3d5711200a0208c4f --- compiler/oat_test.cc | 3 ++- runtime/common_test.h | 58 +++++++++++++++++++++++++++++++++++-------- 2 files changed, 50 insertions(+), 11 deletions(-) diff --git a/compiler/oat_test.cc b/compiler/oat_test.cc index 94bc30d970b..24342622799 100644 --- a/compiler/oat_test.cc +++ b/compiler/oat_test.cc @@ -80,7 +80,8 @@ TEST_F(OatTest, WriteRead) { InstructionSetFeatures insn_features; verified_methods_data_.reset(new VerifiedMethodsData); - method_inliner_map_.reset(new DexFileToMethodInlinerMap); + method_inliner_map_.reset(compiler_backend == kQuick ? new DexFileToMethodInlinerMap : nullptr); + callbacks_.Reset(verified_methods_data_.get(), method_inliner_map_.get()); compiler_driver_.reset(new CompilerDriver(verified_methods_data_.get(), method_inliner_map_.get(), compiler_backend, insn_set, diff --git a/runtime/common_test.h b/runtime/common_test.h index 8dd76fe64b0..a3cbde37400 100644 --- a/runtime/common_test.h +++ b/runtime/common_test.h @@ -33,6 +33,7 @@ #include "base/stringprintf.h" #include "base/unix_file/fd_file.h" #include "class_linker.h" +#include "compiler_callbacks.h" #include "dex_file-inl.h" #include "entrypoints/entrypoint_utils.h" #include "gc/heap.h" @@ -48,6 +49,8 @@ #include "ScopedLocalRef.h" #include "thread.h" #include "UniquePtr.h" +#include "verifier/method_verifier.h" +#include "verifier/method_verifier-inl.h" #include "well_known_classes.h" namespace art { @@ -403,8 +406,18 @@ class CommonTest : public testing::Test { std::string min_heap_string(StringPrintf("-Xms%zdm", gc::Heap::kDefaultInitialSize / MB)); std::string max_heap_string(StringPrintf("-Xmx%zdm", gc::Heap::kDefaultMaximumSize / MB)); + // TODO: make selectable +#if defined(ART_USE_PORTABLE_COMPILER) + CompilerBackend compiler_backend = kPortable; +#else + CompilerBackend compiler_backend = kQuick; +#endif + + verified_methods_data_.reset(new VerifiedMethodsData); + method_inliner_map_.reset(compiler_backend == kQuick ? new DexFileToMethodInlinerMap : nullptr); + callbacks_.Reset(verified_methods_data_.get(), method_inliner_map_.get()); Runtime::Options options; - options.push_back(std::make_pair("compiler", reinterpret_cast(NULL))); + options.push_back(std::make_pair("compilercallbacks", static_cast(&callbacks_))); options.push_back(std::make_pair("bootclasspath", &boot_class_path_)); options.push_back(std::make_pair("-Xcheck:jni", reinterpret_cast(NULL))); options.push_back(std::make_pair(min_heap_string.c_str(), reinterpret_cast(NULL))); @@ -441,13 +454,6 @@ class CommonTest : public testing::Test { instruction_set = kX86; #endif - // TODO: make selectable -#if defined(ART_USE_PORTABLE_COMPILER) - CompilerBackend compiler_backend = kPortable; -#else - CompilerBackend compiler_backend = kQuick; -#endif - for (int i = 0; i < Runtime::kLastCalleeSaveType; i++) { Runtime::CalleeSaveType type = Runtime::CalleeSaveType(i); if (!runtime_->HasCalleeSaveMethod(type)) { @@ -456,8 +462,6 @@ class CommonTest : public testing::Test { } } class_linker_->FixupDexCaches(runtime_->GetResolutionMethod()); - verified_methods_data_.reset(new VerifiedMethodsData); - method_inliner_map_.reset(new DexFileToMethodInlinerMap); compiler_driver_.reset(new CompilerDriver(verified_methods_data_.get(), method_inliner_map_.get(), compiler_backend, instruction_set, @@ -508,6 +512,9 @@ class CommonTest : public testing::Test { (*icu_cleanup_fn)(); compiler_driver_.reset(); + callbacks_.Reset(nullptr, nullptr); + method_inliner_map_.reset(); + verified_methods_data_.reset(); STLDeleteElements(&opened_dex_files_); Runtime::Current()->GetHeap()->VerifyHeap(); // Check for heap corruption after the test @@ -633,6 +640,36 @@ class CommonTest : public testing::Test { image_reservation_.reset(); } + class TestCompilerCallbacks : public CompilerCallbacks { + public: + TestCompilerCallbacks() : verified_methods_data_(nullptr), method_inliner_map_(nullptr) { } + + void Reset(VerifiedMethodsData* verified_methods_data, + DexFileToMethodInlinerMap* method_inliner_map) { + verified_methods_data_ = verified_methods_data; + method_inliner_map_ = method_inliner_map; + } + + virtual bool MethodVerified(verifier::MethodVerifier* verifier) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + CHECK(verified_methods_data_); + bool result = verified_methods_data_->ProcessVerifiedMethod(verifier); + if (result && method_inliner_map_ != nullptr) { + MethodReference ref = verifier->GetMethodReference(); + method_inliner_map_->GetMethodInliner(ref.dex_file) + ->AnalyseMethodCode(ref.dex_method_index, verifier->CodeItem()); + } + return result; + } + virtual void ClassRejected(ClassReference ref) { + verified_methods_data_->AddRejectedClass(ref); + } + + private: + VerifiedMethodsData* verified_methods_data_; + DexFileToMethodInlinerMap* method_inliner_map_; + }; + std::string android_data_; std::string dalvik_cache_; const DexFile* java_lang_dex_file_; // owned by runtime_ @@ -642,6 +679,7 @@ class CommonTest : public testing::Test { ClassLinker* class_linker_; UniquePtr verified_methods_data_; UniquePtr method_inliner_map_; + TestCompilerCallbacks callbacks_; UniquePtr compiler_driver_; private: From 5dce0c9721af5f9024765c16958ad2f77389aa15 Mon Sep 17 00:00:00 2001 From: Sebastien Hertz Date: Wed, 18 Dec 2013 11:01:49 +0100 Subject: [PATCH 0302/2402] Generate devirtualization map only for native compilation. Since devirtualization is only supported by the quick compiler, we do not need to generate devirtualization map for non-compiled method. Bug: 12171022 Change-Id: Ic7d6882396a05b70c4b8067bd14b7a339e0a93ab --- compiler/dex/verified_methods_data.cc | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/compiler/dex/verified_methods_data.cc b/compiler/dex/verified_methods_data.cc index 454b92cd851..e5b2b036a24 100644 --- a/compiler/dex/verified_methods_data.cc +++ b/compiler/dex/verified_methods_data.cc @@ -78,6 +78,14 @@ bool VerifiedMethodsData::ProcessVerifiedMethod(verifier::MethodVerifier* method VerifyGcMap(method_verifier, *dex_gc_map); } SetDexGcMap(ref, dex_gc_map); + + // TODO: move this out when DEX-to-DEX supports devirtualization. + if (method_verifier->HasVirtualOrInterfaceInvokes()) { + PcToConcreteMethodMap* pc_to_concrete_method = GenerateDevirtMap(method_verifier); + if (pc_to_concrete_method != NULL) { + SetDevirtMap(ref, pc_to_concrete_method); + } + } } if (method_verifier->HasCheckCasts()) { @@ -86,13 +94,6 @@ bool VerifiedMethodsData::ProcessVerifiedMethod(verifier::MethodVerifier* method SetSafeCastMap(ref, method_to_safe_casts); } } - - if (method_verifier->HasVirtualOrInterfaceInvokes()) { - PcToConcreteMethodMap* pc_to_concrete_method = GenerateDevirtMap(method_verifier); - if (pc_to_concrete_method != NULL) { - SetDevirtMap(ref, pc_to_concrete_method); - } - } return true; } From 343adb52d3f031b6b5e005ff51f9cb04df219b21 Mon Sep 17 00:00:00 2001 From: Mark Mendell Date: Wed, 18 Dec 2013 06:02:17 -0800 Subject: [PATCH 0303/2402] Enhance GenArrayGet, GenArrayPut for x86 As pointed out by Ian Rogers, the x86 versions didn't optimize handling of constant index expressions. Added that support, simplified checking of constant indices, and removed the use of a temporary register for the 'wide' cases by using x86 scaled addressing mode. Change-Id: I82174e4e3674752d00d7c4730496f59d69f5f173 Signed-off-by: Mark Mendell --- compiler/dex/quick/x86/assemble_x86.cc | 10 ++++ compiler/dex/quick/x86/codegen_x86.h | 3 ++ compiler/dex/quick/x86/int_x86.cc | 75 +++++++++++++++++++------- 3 files changed, 70 insertions(+), 18 deletions(-) diff --git a/compiler/dex/quick/x86/assemble_x86.cc b/compiler/dex/quick/x86/assemble_x86.cc index c9c4f55e62f..c24f0e33f89 100644 --- a/compiler/dex/quick/x86/assemble_x86.cc +++ b/compiler/dex/quick/x86/assemble_x86.cc @@ -772,6 +772,13 @@ void X86Mir2Lir::EmitRegImm(const X86EncodingMap* entry, uint8_t reg, int imm) { EmitImm(entry, imm); } +void X86Mir2Lir::EmitMemImm(const X86EncodingMap* entry, uint8_t base, int disp, int32_t imm) { + EmitPrefixAndOpcode(entry); + EmitModrmDisp(entry->skeleton.modrm_opcode, base, disp); + DCHECK_EQ(0, entry->skeleton.ax_opcode); + EmitImm(entry, imm); +} + void X86Mir2Lir::EmitThreadImm(const X86EncodingMap* entry, int disp, int imm) { EmitPrefixAndOpcode(entry); uint8_t modrm = (0 << 6) | (entry->skeleton.modrm_opcode << 3) | rBP; @@ -1127,6 +1134,9 @@ AssemblerStatus X86Mir2Lir::AssembleInstructions(CodeOffset start_addr) { case kMemReg: // lir operands - 0: base, 1: disp, 2: reg EmitMemReg(entry, lir->operands[0], lir->operands[1], lir->operands[2]); break; + case kMemImm: // lir operands - 0: base, 1: disp, 2: immediate + EmitMemImm(entry, lir->operands[0], lir->operands[1], lir->operands[2]); + break; case kArrayReg: // lir operands - 0: base, 1: index, 2: scale, 3: disp, 4: reg EmitArrayReg(entry, lir->operands[0], lir->operands[1], lir->operands[2], lir->operands[3], lir->operands[4]); diff --git a/compiler/dex/quick/x86/codegen_x86.h b/compiler/dex/quick/x86/codegen_x86.h index 6552607d9e6..62ec77a5082 100644 --- a/compiler/dex/quick/x86/codegen_x86.h +++ b/compiler/dex/quick/x86/codegen_x86.h @@ -115,6 +115,8 @@ class X86Mir2Lir : public Mir2Lir { void GenXorLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2); LIR* GenRegMemCheck(ConditionCode c_code, int reg1, int base, int offset, ThrowKind kind); + LIR* GenMemImmedCheck(ConditionCode c_code, int base, int offset, int check_value, + ThrowKind kind); RegLocation GenDivRem(RegLocation rl_dest, int reg_lo, int reg_hi, bool is_div); RegLocation GenDivRemLit(RegLocation rl_dest, int reg_lo, int lit, bool is_div); void GenCmpLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2); @@ -184,6 +186,7 @@ class X86Mir2Lir : public Mir2Lir { void EmitOpArray(const X86EncodingMap* entry, uint8_t base, uint8_t index, int scale, int disp); void EmitMemReg(const X86EncodingMap* entry, uint8_t base, int disp, uint8_t reg); + void EmitMemImm(const X86EncodingMap* entry, uint8_t base, int disp, int32_t imm); void EmitRegMem(const X86EncodingMap* entry, uint8_t reg, uint8_t base, int disp); void EmitRegArray(const X86EncodingMap* entry, uint8_t reg, uint8_t base, uint8_t index, int scale, int disp); diff --git a/compiler/dex/quick/x86/int_x86.cc b/compiler/dex/quick/x86/int_x86.cc index 340c74ae14e..8ff9ded4572 100644 --- a/compiler/dex/quick/x86/int_x86.cc +++ b/compiler/dex/quick/x86/int_x86.cc @@ -37,6 +37,20 @@ LIR* X86Mir2Lir::GenRegMemCheck(ConditionCode c_code, return branch; } +/* + * Perform a compare of memory to immediate value + */ +LIR* X86Mir2Lir::GenMemImmedCheck(ConditionCode c_code, + int base, int offset, int check_value, ThrowKind kind) { + LIR* tgt = RawLIR(0, kPseudoThrowTarget, kind, + current_dalvik_offset_, base, check_value, 0); + NewLIR3(IS_SIMM8(check_value) ? kX86Cmp32MI8 : kX86Cmp32MI, base, offset, check_value); + LIR* branch = OpCondBranch(c_code, tgt); + // Remember branch target - will process later + throw_launchpads_.Insert(tgt); + return branch; +} + /* * Compare two 64-bit values * x = y return 0 @@ -521,41 +535,49 @@ void X86Mir2Lir::GenArrayGet(int opt_flags, OpSize size, RegLocation rl_array, RegLocation rl_index, RegLocation rl_dest, int scale) { RegisterClass reg_class = oat_reg_class_by_size(size); int len_offset = mirror::Array::LengthOffset().Int32Value(); - int data_offset; RegLocation rl_result; rl_array = LoadValue(rl_array, kCoreReg); - rl_index = LoadValue(rl_index, kCoreReg); + int data_offset; if (size == kLong || size == kDouble) { data_offset = mirror::Array::DataOffset(sizeof(int64_t)).Int32Value(); } else { data_offset = mirror::Array::DataOffset(sizeof(int32_t)).Int32Value(); } + bool constant_index = rl_index.is_const; + int32_t constant_index_value = 0; + if (!constant_index) { + rl_index = LoadValue(rl_index, kCoreReg); + } else { + constant_index_value = mir_graph_->ConstantValue(rl_index); + // If index is constant, just fold it into the data offset + data_offset += constant_index_value << scale; + // treat as non array below + rl_index.low_reg = INVALID_REG; + } + /* null object? */ GenNullCheck(rl_array.s_reg_low, rl_array.low_reg, opt_flags); if (!(opt_flags & MIR_IGNORE_RANGE_CHECK)) { - /* if (rl_index >= [rl_array + len_offset]) goto kThrowArrayBounds */ - GenRegMemCheck(kCondUge, rl_index.low_reg, rl_array.low_reg, - len_offset, kThrowArrayBounds); + if (constant_index) { + GenMemImmedCheck(kCondLs, rl_array.low_reg, len_offset, + constant_index_value, kThrowConstantArrayBounds); + } else { + GenRegMemCheck(kCondUge, rl_index.low_reg, rl_array.low_reg, + len_offset, kThrowArrayBounds); + } } + rl_result = EvalLoc(rl_dest, reg_class, true); if ((size == kLong) || (size == kDouble)) { - int reg_addr = AllocTemp(); - OpLea(reg_addr, rl_array.low_reg, rl_index.low_reg, scale, data_offset); - FreeTemp(rl_array.low_reg); - FreeTemp(rl_index.low_reg); - rl_result = EvalLoc(rl_dest, reg_class, true); - LoadBaseIndexedDisp(reg_addr, INVALID_REG, 0, 0, rl_result.low_reg, + LoadBaseIndexedDisp(rl_array.low_reg, rl_index.low_reg, scale, data_offset, rl_result.low_reg, rl_result.high_reg, size, INVALID_SREG); StoreValueWide(rl_dest, rl_result); } else { - rl_result = EvalLoc(rl_dest, reg_class, true); - LoadBaseIndexedDisp(rl_array.low_reg, rl_index.low_reg, scale, data_offset, rl_result.low_reg, INVALID_REG, size, INVALID_SREG); - StoreValue(rl_dest, rl_result); } } @@ -577,14 +599,29 @@ void X86Mir2Lir::GenArrayPut(int opt_flags, OpSize size, RegLocation rl_array, } rl_array = LoadValue(rl_array, kCoreReg); - rl_index = LoadValue(rl_index, kCoreReg); + bool constant_index = rl_index.is_const; + int32_t constant_index_value = 0; + if (!constant_index) { + rl_index = LoadValue(rl_index, kCoreReg); + } else { + // If index is constant, just fold it into the data offset + constant_index_value = mir_graph_->ConstantValue(rl_index); + data_offset += constant_index_value << scale; + // treat as non array below + rl_index.low_reg = INVALID_REG; + } /* null object? */ GenNullCheck(rl_array.s_reg_low, rl_array.low_reg, opt_flags); if (!(opt_flags & MIR_IGNORE_RANGE_CHECK)) { - /* if (rl_index >= [rl_array + len_offset]) goto kThrowArrayBounds */ - GenRegMemCheck(kCondUge, rl_index.low_reg, rl_array.low_reg, len_offset, kThrowArrayBounds); + if (constant_index) { + GenMemImmedCheck(kCondLs, rl_array.low_reg, len_offset, + constant_index_value, kThrowConstantArrayBounds); + } else { + GenRegMemCheck(kCondUge, rl_index.low_reg, rl_array.low_reg, + len_offset, kThrowArrayBounds); + } } if ((size == kLong) || (size == kDouble)) { rl_src = LoadValueWide(rl_src, reg_class); @@ -603,7 +640,9 @@ void X86Mir2Lir::GenArrayPut(int opt_flags, OpSize size, RegLocation rl_array, } if (card_mark) { // Free rl_index if its a temp. Ensures there are 2 free regs for card mark. - FreeTemp(rl_index.low_reg); + if (!constant_index) { + FreeTemp(rl_index.low_reg); + } MarkGCCard(rl_src.low_reg, rl_array.low_reg); } } From e48780b8278240dec2d3164512f7d3317a5495c1 Mon Sep 17 00:00:00 2001 From: Hiroshi Yamauchi Date: Tue, 17 Dec 2013 17:19:53 -0800 Subject: [PATCH 0304/2402] Remove some unnecessary code from dlmalloc_space/rosalloc_space. Removed GetTotalBytesAllocated(), GetTotalObjectsAllocated(), and InternalAllocationSize(). No performance improvement observed. Change-Id: Ie20433a0fd328fc7f61d4afd54d18fbdc4aa4a17 --- runtime/gc/space/dlmalloc_space.cc | 17 ++++------------- runtime/gc/space/dlmalloc_space.h | 12 ------------ runtime/gc/space/malloc_space.h | 2 -- runtime/gc/space/rosalloc_space.cc | 15 +++------------ runtime/gc/space/rosalloc_space.h | 11 ----------- 5 files changed, 7 insertions(+), 50 deletions(-) diff --git a/runtime/gc/space/dlmalloc_space.cc b/runtime/gc/space/dlmalloc_space.cc index 10e9ed8cf51..a4e6edadef0 100644 --- a/runtime/gc/space/dlmalloc_space.cc +++ b/runtime/gc/space/dlmalloc_space.cc @@ -38,7 +38,7 @@ static const bool kPrefetchDuringDlMallocFreeList = true; DlMallocSpace::DlMallocSpace(const std::string& name, MemMap* mem_map, void* mspace, byte* begin, byte* end, byte* limit, size_t growth_limit) : MallocSpace(name, mem_map, begin, end, limit, growth_limit), - total_bytes_freed_(0), total_objects_freed_(0), mspace_(mspace), mspace_for_alloc_(mspace) { + mspace_(mspace), mspace_for_alloc_(mspace) { CHECK(mspace != NULL); } @@ -148,9 +148,7 @@ size_t DlMallocSpace::Free(Thread* self, mirror::Object* ptr) { CHECK(ptr != NULL); CHECK(Contains(ptr)) << "Free (" << ptr << ") not in bounds of heap " << *this; } - const size_t bytes_freed = InternalAllocationSize(ptr); - total_bytes_freed_ += bytes_freed; - ++total_objects_freed_; + const size_t bytes_freed = AllocationSizeNonvirtual(ptr); if (kRecentFreeCount > 0) { RegisterRecentFree(ptr); } @@ -170,7 +168,7 @@ size_t DlMallocSpace::FreeList(Thread* self, size_t num_ptrs, mirror::Object** p // The head of chunk for the allocation is sizeof(size_t) behind the allocation. __builtin_prefetch(reinterpret_cast(ptrs[i + look_ahead]) - sizeof(size_t)); } - bytes_freed += InternalAllocationSize(ptr); + bytes_freed += AllocationSizeNonvirtual(ptr); } if (kRecentFreeCount > 0) { @@ -196,8 +194,6 @@ size_t DlMallocSpace::FreeList(Thread* self, size_t num_ptrs, mirror::Object** p { MutexLock mu(self, lock_); - total_bytes_freed_ += bytes_freed; - total_objects_freed_ += num_ptrs; mspace_bulk_free(mspace_, reinterpret_cast(ptrs), num_ptrs); return bytes_freed; } @@ -211,13 +207,8 @@ extern "C" void* art_heap_morecore(void* mspace, intptr_t increment) { return heap->GetNonMovingSpace()->MoreCore(increment); } -// Virtual functions can't get inlined. -inline size_t DlMallocSpace::InternalAllocationSize(const mirror::Object* obj) { - return AllocationSizeNonvirtual(obj); -} - size_t DlMallocSpace::AllocationSize(const mirror::Object* obj) { - return InternalAllocationSize(obj); + return AllocationSizeNonvirtual(obj); } size_t DlMallocSpace::Trim() { diff --git a/runtime/gc/space/dlmalloc_space.h b/runtime/gc/space/dlmalloc_space.h index d18d4ad2a0e..501c014bcd1 100644 --- a/runtime/gc/space/dlmalloc_space.h +++ b/runtime/gc/space/dlmalloc_space.h @@ -86,12 +86,6 @@ class DlMallocSpace : public MallocSpace { uint64_t GetBytesAllocated(); uint64_t GetObjectsAllocated(); - uint64_t GetTotalBytesAllocated() { - return GetBytesAllocated() + total_bytes_freed_; - } - uint64_t GetTotalObjectsAllocated() { - return GetObjectsAllocated() + total_objects_freed_; - } // Returns the class of a recently freed object. mirror::Class* FindRecentFreedObject(const mirror::Object* obj); @@ -112,8 +106,6 @@ class DlMallocSpace : public MallocSpace { byte* limit, size_t growth_limit); private: - size_t InternalAllocationSize(const mirror::Object* obj); - mirror::Object* AllocWithoutGrowthLocked(Thread* self, size_t num_bytes, size_t* bytes_allocated) EXCLUSIVE_LOCKS_REQUIRED(lock_); @@ -122,10 +114,6 @@ class DlMallocSpace : public MallocSpace { } static void* CreateMspace(void* base, size_t morecore_start, size_t initial_size); - // Approximate number of bytes and objects which have been deallocated in the space. - size_t total_bytes_freed_; - size_t total_objects_freed_; - // The boundary tag overhead. static const size_t kChunkOverhead = kWordSize; diff --git a/runtime/gc/space/malloc_space.h b/runtime/gc/space/malloc_space.h index 0f882d33e11..760fcdd7c31 100644 --- a/runtime/gc/space/malloc_space.h +++ b/runtime/gc/space/malloc_space.h @@ -132,8 +132,6 @@ class MallocSpace : public ContinuousMemMapAllocSpace { virtual uint64_t GetBytesAllocated() = 0; virtual uint64_t GetObjectsAllocated() = 0; - virtual uint64_t GetTotalBytesAllocated() = 0; - virtual uint64_t GetTotalObjectsAllocated() = 0; // Returns the old mark bitmap. accounting::SpaceBitmap* BindLiveToMarkBitmap(); diff --git a/runtime/gc/space/rosalloc_space.cc b/runtime/gc/space/rosalloc_space.cc index 1f8e324823d..9ddc14b78db 100644 --- a/runtime/gc/space/rosalloc_space.cc +++ b/runtime/gc/space/rosalloc_space.cc @@ -146,9 +146,7 @@ size_t RosAllocSpace::Free(Thread* self, mirror::Object* ptr) { CHECK(ptr != NULL); CHECK(Contains(ptr)) << "Free (" << ptr << ") not in bounds of heap " << *this; } - const size_t bytes_freed = InternalAllocationSize(ptr); - total_bytes_freed_atomic_.fetch_add(bytes_freed); - ++total_objects_freed_atomic_; + const size_t bytes_freed = AllocationSizeNonvirtual(ptr); if (kRecentFreeCount > 0) { MutexLock mu(self, lock_); RegisterRecentFree(ptr); @@ -168,7 +166,7 @@ size_t RosAllocSpace::FreeList(Thread* self, size_t num_ptrs, mirror::Object** p if (kPrefetchDuringRosAllocFreeList && i + look_ahead < num_ptrs) { __builtin_prefetch(reinterpret_cast(ptrs[i + look_ahead])); } - bytes_freed += InternalAllocationSize(ptr); + bytes_freed += AllocationSizeNonvirtual(ptr); } if (kRecentFreeCount > 0) { @@ -193,8 +191,6 @@ size_t RosAllocSpace::FreeList(Thread* self, size_t num_ptrs, mirror::Object** p } rosalloc_->BulkFree(self, reinterpret_cast(ptrs), num_ptrs); - total_bytes_freed_atomic_.fetch_add(bytes_freed); - total_objects_freed_atomic_.fetch_add(num_ptrs); return bytes_freed; } @@ -206,13 +202,8 @@ extern "C" void* art_heap_rosalloc_morecore(allocator::RosAlloc* rosalloc, intpt return heap->GetNonMovingSpace()->MoreCore(increment); } -// Virtual functions can't get inlined. -inline size_t RosAllocSpace::InternalAllocationSize(const mirror::Object* obj) { - return AllocationSizeNonvirtual(obj); -} - size_t RosAllocSpace::AllocationSize(const mirror::Object* obj) { - return InternalAllocationSize(obj); + return AllocationSizeNonvirtual(obj); } size_t RosAllocSpace::Trim() { diff --git a/runtime/gc/space/rosalloc_space.h b/runtime/gc/space/rosalloc_space.h index 6311580d146..5df9d88a2f5 100644 --- a/runtime/gc/space/rosalloc_space.h +++ b/runtime/gc/space/rosalloc_space.h @@ -83,12 +83,6 @@ class RosAllocSpace : public MallocSpace { uint64_t GetBytesAllocated(); uint64_t GetObjectsAllocated(); - uint64_t GetTotalBytesAllocated() { - return GetBytesAllocated() + total_bytes_freed_atomic_; - } - uint64_t GetTotalObjectsAllocated() { - return GetObjectsAllocated() + total_objects_freed_atomic_; - } void RevokeThreadLocalBuffers(Thread* thread); void RevokeAllThreadLocalBuffers(); @@ -112,7 +106,6 @@ class RosAllocSpace : public MallocSpace { byte* begin, byte* end, byte* limit, size_t growth_limit); private: - size_t InternalAllocationSize(const mirror::Object* obj); mirror::Object* AllocWithoutGrowthLocked(Thread* self, size_t num_bytes, size_t* bytes_allocated); void* CreateAllocator(void* base, size_t morecore_start, size_t initial_size) { @@ -125,10 +118,6 @@ class RosAllocSpace : public MallocSpace { void* arg) LOCKS_EXCLUDED(Locks::runtime_shutdown_lock_, Locks::thread_list_lock_); - // Approximate number of bytes and objects which have been deallocated in the space. - AtomicInteger total_bytes_freed_atomic_; - AtomicInteger total_objects_freed_atomic_; - // Underlying rosalloc. art::gc::allocator::RosAlloc* const rosalloc_; From c97d70dec0889ba2b0ddeb00de3c5a391ab7707f Mon Sep 17 00:00:00 2001 From: Brian Carlstrom Date: Wed, 18 Dec 2013 14:32:49 -0800 Subject: [PATCH 0305/2402] Forgot to bump oat version in previous change 88474b416eb257078e590bf9bc7957cee604a186: Implement Interface Method Tables (IMT) This was causing updates between klp and master to fail Change-Id: Ib4e0f6658fe642924ad52866d3ea0f773d8f264a --- runtime/image.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/image.cc b/runtime/image.cc index dd9b6d7f466..702cc9a6770 100644 --- a/runtime/image.cc +++ b/runtime/image.cc @@ -24,7 +24,7 @@ namespace art { const byte ImageHeader::kImageMagic[] = { 'a', 'r', 't', '\n' }; -const byte ImageHeader::kImageVersion[] = { '0', '0', '5', '\0' }; +const byte ImageHeader::kImageVersion[] = { '0', '0', '6', '\0' }; ImageHeader::ImageHeader(uint32_t image_begin, uint32_t image_size, From ec8d14223b05d97b4071eddb7218710381a3ff35 Mon Sep 17 00:00:00 2001 From: Brian Carlstrom Date: Wed, 18 Dec 2013 14:32:49 -0800 Subject: [PATCH 0306/2402] Forgot to bump oat version in previous change 88474b416eb257078e590bf9bc7957cee604a186: Implement Interface Method Tables (IMT) This was causing updates between klp and master to fail Change-Id: Ib4e0f6658fe642924ad52866d3ea0f773d8f264a --- runtime/image.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/image.cc b/runtime/image.cc index dd9b6d7f466..702cc9a6770 100644 --- a/runtime/image.cc +++ b/runtime/image.cc @@ -24,7 +24,7 @@ namespace art { const byte ImageHeader::kImageMagic[] = { 'a', 'r', 't', '\n' }; -const byte ImageHeader::kImageVersion[] = { '0', '0', '5', '\0' }; +const byte ImageHeader::kImageVersion[] = { '0', '0', '6', '\0' }; ImageHeader::ImageHeader(uint32_t image_begin, uint32_t image_size, From 1279d12eb94b7664fc839d4ef7f30c7d6b20e603 Mon Sep 17 00:00:00 2001 From: Brian Carlstrom Date: Wed, 18 Dec 2013 17:42:42 -0800 Subject: [PATCH 0307/2402] Fix byte_cas to work on non-word aligned addresses Change-Id: I345ef2b98798867f76f75f57f907870e2e599914 --- runtime/gc/accounting/card_table-inl.h | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/runtime/gc/accounting/card_table-inl.h b/runtime/gc/accounting/card_table-inl.h index 45ab214d9cf..7bd53dfef31 100644 --- a/runtime/gc/accounting/card_table-inl.h +++ b/runtime/gc/accounting/card_table-inl.h @@ -29,14 +29,15 @@ namespace accounting { static inline bool byte_cas(byte old_value, byte new_value, byte* address) { // Little endian means most significant byte is on the left. - const size_t shift = reinterpret_cast(address) % sizeof(uintptr_t); + const size_t shift_in_bytes = reinterpret_cast(address) % sizeof(uintptr_t); // Align the address down. - address -= shift; + address -= shift_in_bytes; + const size_t shift_in_bits = shift_in_bytes * kBitsPerByte; int32_t* word_address = reinterpret_cast(address); // Word with the byte we are trying to cas cleared. - const int32_t cur_word = *word_address & ~(0xFF << shift); - const int32_t old_word = cur_word | (static_cast(old_value) << shift); - const int32_t new_word = cur_word | (static_cast(new_value) << shift); + const int32_t cur_word = *word_address & ~(0xFF << shift_in_bits); + const int32_t old_word = cur_word | (static_cast(old_value) << shift_in_bits); + const int32_t new_word = cur_word | (static_cast(new_value) << shift_in_bits); bool success = android_atomic_cas(old_word, new_word, word_address) == 0; return success; } From bb11f4b4f33f06c7c6e00448443b45c888ab1338 Mon Sep 17 00:00:00 2001 From: Brian Carlstrom Date: Wed, 18 Dec 2013 17:42:42 -0800 Subject: [PATCH 0308/2402] Fix byte_cas to work on non-word aligned addresses (cherry picked from commit 1279d12eb94b7664fc839d4ef7f30c7d6b20e603) Change-Id: I307f65f20609bca8ec9c9f9a0ea4725c7ab8ad74 --- runtime/gc/accounting/card_table-inl.h | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/runtime/gc/accounting/card_table-inl.h b/runtime/gc/accounting/card_table-inl.h index 45ab214d9cf..7bd53dfef31 100644 --- a/runtime/gc/accounting/card_table-inl.h +++ b/runtime/gc/accounting/card_table-inl.h @@ -29,14 +29,15 @@ namespace accounting { static inline bool byte_cas(byte old_value, byte new_value, byte* address) { // Little endian means most significant byte is on the left. - const size_t shift = reinterpret_cast(address) % sizeof(uintptr_t); + const size_t shift_in_bytes = reinterpret_cast(address) % sizeof(uintptr_t); // Align the address down. - address -= shift; + address -= shift_in_bytes; + const size_t shift_in_bits = shift_in_bytes * kBitsPerByte; int32_t* word_address = reinterpret_cast(address); // Word with the byte we are trying to cas cleared. - const int32_t cur_word = *word_address & ~(0xFF << shift); - const int32_t old_word = cur_word | (static_cast(old_value) << shift); - const int32_t new_word = cur_word | (static_cast(new_value) << shift); + const int32_t cur_word = *word_address & ~(0xFF << shift_in_bits); + const int32_t old_word = cur_word | (static_cast(old_value) << shift_in_bits); + const int32_t new_word = cur_word | (static_cast(new_value) << shift_in_bits); bool success = android_atomic_cas(old_word, new_word, word_address) == 0; return success; } From 412d4f833d8c6b43ef9725cda15bc97012d9ecdf Mon Sep 17 00:00:00 2001 From: Mark Mendell Date: Wed, 18 Dec 2013 13:32:36 -0800 Subject: [PATCH 0309/2402] Improve x86 Fused long compare to literal Generate better x86 code for the fused long comparison/branch if one of the arguments is a literal. Use the algorithm from ARM, tweaked for x86. Change-Id: I872ba5dfaeeaaba6beff756d2eb6f9c6d018ce3e Signed-off-by: Mark Mendell --- compiler/dex/quick/x86/codegen_x86.h | 2 + compiler/dex/quick/x86/int_x86.cc | 64 +++++++++++++++++++++++++++- 2 files changed, 65 insertions(+), 1 deletion(-) diff --git a/compiler/dex/quick/x86/codegen_x86.h b/compiler/dex/quick/x86/codegen_x86.h index 484d0cc8310..22c44529912 100644 --- a/compiler/dex/quick/x86/codegen_x86.h +++ b/compiler/dex/quick/x86/codegen_x86.h @@ -209,6 +209,8 @@ class X86Mir2Lir : public Mir2Lir { int scale, int table_or_disp); void EmitMacro(const X86EncodingMap* entry, uint8_t reg, int offset); void EmitUnimplemented(const X86EncodingMap* entry, LIR* lir); + void GenFusedLongCmpImmBranch(BasicBlock* bb, RegLocation rl_src1, + int64_t val, ConditionCode ccode); }; } // namespace art diff --git a/compiler/dex/quick/x86/int_x86.cc b/compiler/dex/quick/x86/int_x86.cc index 8ff9ded4572..56cf7e96e3f 100644 --- a/compiler/dex/quick/x86/int_x86.cc +++ b/compiler/dex/quick/x86/int_x86.cc @@ -183,11 +183,23 @@ void X86Mir2Lir::GenFusedLongCmpBranch(BasicBlock* bb, MIR* mir) { LIR* taken = &block_label_list_[bb->taken]; RegLocation rl_src1 = mir_graph_->GetSrcWide(mir, 0); RegLocation rl_src2 = mir_graph_->GetSrcWide(mir, 2); + ConditionCode ccode = static_cast(mir->dalvikInsn.arg[0]); + + if (rl_src1.is_const) { + std::swap(rl_src1, rl_src2); + ccode = FlipComparisonOrder(ccode); + } + if (rl_src2.is_const) { + // Do special compare/branch against simple const operand + int64_t val = mir_graph_->ConstantValueWide(rl_src2); + GenFusedLongCmpImmBranch(bb, rl_src1, val, ccode); + return; + } + FlushAllRegs(); LockCallTemps(); // Prepare for explicit register usage LoadValueDirectWideFixed(rl_src1, r0, r1); LoadValueDirectWideFixed(rl_src2, r2, r3); - ConditionCode ccode = static_cast(mir->dalvikInsn.arg[0]); // Swap operands and condition code to prevent use of zero flag. if (ccode == kCondLe || ccode == kCondGt) { // Compute (r3:r2) = (r3:r2) - (r1:r0) @@ -218,6 +230,56 @@ void X86Mir2Lir::GenFusedLongCmpBranch(BasicBlock* bb, MIR* mir) { OpCondBranch(ccode, taken); } +void X86Mir2Lir::GenFusedLongCmpImmBranch(BasicBlock* bb, RegLocation rl_src1, + int64_t val, ConditionCode ccode) { + int32_t val_lo = Low32Bits(val); + int32_t val_hi = High32Bits(val); + LIR* taken = &block_label_list_[bb->taken]; + LIR* not_taken = &block_label_list_[bb->fall_through]; + rl_src1 = LoadValueWide(rl_src1, kCoreReg); + int32_t low_reg = rl_src1.low_reg; + int32_t high_reg = rl_src1.high_reg; + + if (val == 0 && (ccode == kCondEq || ccode == kCondNe)) { + int t_reg = AllocTemp(); + OpRegRegReg(kOpOr, t_reg, low_reg, high_reg); + FreeTemp(t_reg); + OpCondBranch(ccode, taken); + return; + } + + OpRegImm(kOpCmp, high_reg, val_hi); + switch (ccode) { + case kCondEq: + case kCondNe: + OpCondBranch(kCondNe, (ccode == kCondEq) ? not_taken : taken); + break; + case kCondLt: + OpCondBranch(kCondLt, taken); + OpCondBranch(kCondGt, not_taken); + ccode = kCondUlt; + break; + case kCondLe: + OpCondBranch(kCondLt, taken); + OpCondBranch(kCondGt, not_taken); + ccode = kCondLs; + break; + case kCondGt: + OpCondBranch(kCondGt, taken); + OpCondBranch(kCondLt, not_taken); + ccode = kCondHi; + break; + case kCondGe: + OpCondBranch(kCondGt, taken); + OpCondBranch(kCondLt, not_taken); + ccode = kCondUge; + break; + default: + LOG(FATAL) << "Unexpected ccode: " << ccode; + } + OpCmpImmBranch(ccode, low_reg, val_lo, taken); +} + RegLocation X86Mir2Lir::GenDivRemLit(RegLocation rl_dest, int reg_lo, int lit, bool is_div) { LOG(FATAL) << "Unexpected use of GenDivRemLit for x86"; From 8c1d1acd24c861c9e0b6666fe6e748ba6c830a4a Mon Sep 17 00:00:00 2001 From: Ashok Bhat Date: Thu, 12 Dec 2013 17:10:59 +0000 Subject: [PATCH 0310/2402] AArch64: Temporary workaround to build art This patch adds a temporary workaround to build art for AArch64, thus allowing building of other dependent projects. This needs to be reverted once proper AArch64 port is available. Change-Id: Ib3d70b9d08ce774efbd4971ce319733dbdda86cc Signed-off-by: Ashok Bhat --- runtime/Android.mk | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/runtime/Android.mk b/runtime/Android.mk index a602c832e9c..468354385b0 100644 --- a/runtime/Android.mk +++ b/runtime/Android.mk @@ -226,7 +226,11 @@ LIBART_TARGET_SRC_FILES += \ arch/mips/quick_entrypoints_mips.S \ arch/mips/thread_mips.cc else # TARGET_ARCH != mips +ifeq ($(TARGET_ARCH),aarch64) +$(info TODOAArch64: $(LOCAL_PATH)/Android.mk Add AArch64 specific runtime files) +else $(error unsupported TARGET_ARCH=$(TARGET_ARCH)) +endif # TARGET_ARCH != aarch64 endif # TARGET_ARCH != mips endif # TARGET_ARCH != x86 endif # TARGET_ARCH != x86_64 From e8c48db6bb507d7fa20c78481c58c23be0045f67 Mon Sep 17 00:00:00 2001 From: Mathieu Chartier Date: Thu, 19 Dec 2013 14:59:00 -0800 Subject: [PATCH 0311/2402] Fix NewLocalRef, NewGlobalRef to handle cleared weak globals. We were not checking for null after decoding the reference, this meant that we incorrectly created null weak global references instead of returning null. Issue: 63929 Change-Id: I9159682e6edad8f415ef8144fc13b9aedd2cceb4 --- runtime/jni_internal.cc | 19 +++++++++++-------- runtime/thread.cc | 2 +- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/runtime/jni_internal.cc b/runtime/jni_internal.cc index 6690519eba9..bbe5fda62f4 100644 --- a/runtime/jni_internal.cc +++ b/runtime/jni_internal.cc @@ -844,13 +844,14 @@ class JNI { } static jobject NewGlobalRef(JNIEnv* env, jobject obj) { - if (obj == NULL) { - return NULL; - } ScopedObjectAccess soa(env); + Object* decoded_obj = soa.Decode(obj); + // Check for null after decoding the object to handle cleared weak globals. + if (decoded_obj == nullptr) { + return nullptr; + } JavaVMExt* vm = soa.Vm(); IndirectReferenceTable& globals = vm->globals; - Object* decoded_obj = soa.Decode(obj); WriterMutexLock mu(soa.Self(), vm->globals_lock); IndirectRef ref = globals.Add(IRT_FIRST_SEGMENT, decoded_obj); return reinterpret_cast(ref); @@ -884,11 +885,13 @@ class JNI { } static jobject NewLocalRef(JNIEnv* env, jobject obj) { - if (obj == NULL) { - return NULL; - } ScopedObjectAccess soa(env); - return soa.AddLocalReference(soa.Decode(obj)); + mirror::Object* decoded_obj = soa.Decode(obj); + // Check for null after decoding the object to handle cleared weak globals. + if (decoded_obj == nullptr) { + return nullptr; + } + return soa.AddLocalReference(decoded_obj); } static void DeleteLocalRef(JNIEnv* env, jobject obj) { diff --git a/runtime/thread.cc b/runtime/thread.cc index bc252deecce..9faa60dbf8c 100644 --- a/runtime/thread.cc +++ b/runtime/thread.cc @@ -1147,7 +1147,7 @@ mirror::Object* Thread::DecodeJObject(jobject obj) const { result = Runtime::Current()->GetJavaVM()->DecodeWeakGlobal(const_cast(this), ref); if (result == kClearedJniWeakGlobal) { // This is a special case where it's okay to return NULL. - return NULL; + return nullptr; } } From 0f5f6bbc3a0bb125875f28ad61584001989a7f10 Mon Sep 17 00:00:00 2001 From: Dave Allison Date: Fri, 22 Nov 2013 17:39:19 -0800 Subject: [PATCH 0312/2402] Fix thread checkpoint issue This changes a do...while loop into a while(true) with a break out. Fixes issue where checkpoint functions can cause the thread state to become overwritten by a garbage value. Bug: 11809176 Change-Id: Ifbbc5e5cee85649d57f71267933539eefa7e25ea --- runtime/thread-inl.h | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/runtime/thread-inl.h b/runtime/thread-inl.h index 6f3c117a595..b87a8ec3cad 100644 --- a/runtime/thread-inl.h +++ b/runtime/thread-inl.h @@ -87,18 +87,22 @@ inline void Thread::TransitionFromRunnableToSuspended(ThreadState new_state) { DCHECK_EQ(GetState(), kRunnable); union StateAndFlags old_state_and_flags; union StateAndFlags new_state_and_flags; - do { + while (true) { old_state_and_flags.as_int = state_and_flags_.as_int; if (UNLIKELY((old_state_and_flags.as_struct.flags & kCheckpointRequest) != 0)) { RunCheckpointFunction(); continue; } - // Copy over flags and try to clear the checkpoint bit if it is set. - new_state_and_flags.as_struct.flags = old_state_and_flags.as_struct.flags & ~kCheckpointRequest; + // Change the state but keep the current flags (kCheckpointRequest is clear). + DCHECK_EQ((old_state_and_flags.as_struct.flags & kCheckpointRequest), 0); + new_state_and_flags.as_struct.flags = old_state_and_flags.as_struct.flags; new_state_and_flags.as_struct.state = new_state; - // CAS the value without a memory barrier, that will occur in the unlock below. - } while (UNLIKELY(android_atomic_cas(old_state_and_flags.as_int, new_state_and_flags.as_int, - &state_and_flags_.as_int) != 0)); + int status = android_atomic_cas(old_state_and_flags.as_int, new_state_and_flags.as_int, + &state_and_flags_.as_int); + if (LIKELY(status == 0)) { + break; + } + } // Release share on mutator_lock_. Locks::mutator_lock_->SharedUnlock(this); } From cd2cfff09c916c9e72a7cfeb0686b441847dc62e Mon Sep 17 00:00:00 2001 From: Mathieu Chartier Date: Thu, 19 Dec 2013 15:46:55 -0800 Subject: [PATCH 0313/2402] Change IndirectReferenceTable::Add null DCHECK to CHECK. Change-Id: I7d3582bd5ef75c7fef6a94c0e4f1fea38fc8b526 --- runtime/indirect_reference_table.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/indirect_reference_table.cc b/runtime/indirect_reference_table.cc index 2bd83538ac0..8194a0d7674 100644 --- a/runtime/indirect_reference_table.cc +++ b/runtime/indirect_reference_table.cc @@ -80,7 +80,7 @@ IndirectRef IndirectReferenceTable::Add(uint32_t cookie, mirror::Object* obj) { prevState.all = cookie; size_t topIndex = segment_state_.parts.topIndex; - DCHECK(obj != NULL); + CHECK(obj != NULL); // TODO: stronger sanity check on the object (such as in heap) DCHECK_ALIGNED(reinterpret_cast(obj), 8); DCHECK(table_ != NULL); From ef809d09cf0d28d999ac69969e4506d8afa4624d Mon Sep 17 00:00:00 2001 From: Narayan Kamath Date: Thu, 19 Dec 2013 17:52:47 +0000 Subject: [PATCH 0314/2402] JNI: NewDirectByteBuffer should allow 0 length buffers. This makes the implementation symmetric with direct buffers allocated from java. - GetDirectBufferAddress returns the address of the buffer passed in to NewDirectByteBuffer (and not NULL). - GetDirectBufferCapaticy returns 0. bug: https://code.google.com/p/android/issues/detail?id=63055 Change-Id: I55b24623ec4f7670972fed898ea097934c6c0b5f --- runtime/check_jni.cc | 4 ++-- test/JniTest/JniTest.java | 3 +++ test/JniTest/jni_test.cc | 12 ++++++++++++ 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/runtime/check_jni.cc b/runtime/check_jni.cc index a84e18acc80..09c48b1220f 100644 --- a/runtime/check_jni.cc +++ b/runtime/check_jni.cc @@ -1754,8 +1754,8 @@ PRIMITIVE_ARRAY_FUNCTIONS(jdouble, Double, 'D'); if (address == NULL) { JniAbortF(__FUNCTION__, "non-nullable address is NULL"); } - if (capacity <= 0) { - JniAbortF(__FUNCTION__, "capacity must be greater than 0: %lld", capacity); + if (capacity < 0) { + JniAbortF(__FUNCTION__, "capacity must be non negative: %lld", capacity); } return CHECK_JNI_EXIT("L", baseEnv(env)->NewDirectByteBuffer(env, address, capacity)); } diff --git a/test/JniTest/JniTest.java b/test/JniTest/JniTest.java index 9194da581f1..d53cf5e5645 100644 --- a/test/JniTest/JniTest.java +++ b/test/JniTest/JniTest.java @@ -23,6 +23,7 @@ public static void main(String[] args) { testFindFieldOnAttachedNativeThread(); testCallStaticVoidMethodOnSubClass(); testGetMirandaMethod(); + testZeroLengthByteBuffers(); } private static native void testFindClassOnAttachedNativeThread(); @@ -67,6 +68,8 @@ private static void testGetMirandaMethod() { } } + private static native void testZeroLengthByteBuffers(); + private static abstract class testGetMirandaMethod_MirandaAbstract implements testGetMirandaMethod_MirandaInterface { public boolean inAbstract() { return true; diff --git a/test/JniTest/jni_test.cc b/test/JniTest/jni_test.cc index d15e180c02f..33af94b2ab0 100644 --- a/test/JniTest/jni_test.cc +++ b/test/JniTest/jni_test.cc @@ -17,6 +17,7 @@ #include #include #include +#include #include "jni.h" @@ -125,3 +126,14 @@ extern "C" JNIEXPORT jobject JNICALL Java_JniTest_testGetMirandaMethodNative(JNI assert(miranda_method != NULL); return env->ToReflectedMethod(abstract_class, miranda_method, JNI_FALSE); } + +// https://code.google.com/p/android/issues/detail?id=63055 +extern "C" void JNICALL Java_JniTest_testZeroLengthByteBuffers(JNIEnv* env, jclass) { + std::vector buffer(1); + jobject byte_buffer = env->NewDirectByteBuffer(&buffer[0], 0); + assert(byte_buffer != NULL); + assert(!env->ExceptionCheck()); + + assert(env->GetDirectBufferAddress(byte_buffer) == &buffer[0]); + assert(env->GetDirectBufferCapacity(byte_buffer) == 0); +} From ea6404e254f204ec42625dc2913b0a7e9340275d Mon Sep 17 00:00:00 2001 From: Mark Mendell Date: Fri, 20 Dec 2013 06:07:51 -0800 Subject: [PATCH 0315/2402] Fix host-run-test-jar to be legal for /bin/sh At least on my machine, /bin/sh complains about the [[ line. Rewrite as a standard shell 'if' Change-Id: I68efc4ceab8e2eb5636fa04ce8877883a4d76998 Signed-off-by: Mark Mendell --- test/etc/host-run-test-jar | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/test/etc/host-run-test-jar b/test/etc/host-run-test-jar index da745324c97..997e84b7f0c 100755 --- a/test/etc/host-run-test-jar +++ b/test/etc/host-run-test-jar @@ -79,7 +79,9 @@ done msg "------------------------------" mkdir $DEX_LOCATION/dalvik-cache -[[ $? -ne 0 ]] && exit +if [ $? -ne 0 ]; then + exit +fi export ANDROID_PRINTF_LOG=brief if [ "$DEV_MODE" = "y" ]; then From b122a4bbed34ab22b4c1541ee25e5cf22f12a926 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Tue, 19 Nov 2013 18:00:50 -0800 Subject: [PATCH 0316/2402] Tidy up memory barriers. Change-Id: I937ea93e6df1835ecfe2d4bb7d84c24fe7fc097b --- compiler/dex/quick/arm/int_arm.cc | 8 +- compiler/driver/compiler_driver.cc | 2 +- disassembler/disassembler_arm.cc | 19 ++- disassembler/disassembler_arm.h | 2 + runtime/arch/arm/quick_entrypoints_arm.S | 2 + runtime/atomic.cc | 121 +++--------------- runtime/atomic.h | 94 +++++++++++++- runtime/atomic_integer.h | 51 ++++---- runtime/base/bounded_fifo.h | 5 +- runtime/base/mutex.cc | 60 ++++----- runtime/base/mutex.h | 6 +- runtime/gc/accounting/atomic_stack.h | 14 +- runtime/gc/collector/mark_sweep.cc | 16 +-- runtime/gc/collector/semi_space.cc | 14 +- runtime/gc/heap-inl.h | 4 +- runtime/gc/heap.cc | 15 ++- runtime/gc/space/bump_pointer_space-inl.h | 4 +- runtime/gc/space/bump_pointer_space.cc | 8 +- runtime/gc/space/image_space.cc | 2 +- runtime/instrumentation.cc | 8 +- .../interpreter_goto_table_impl.cc | 4 +- .../interpreter/interpreter_switch_impl.cc | 4 +- runtime/mirror/object-inl.h | 43 ++++++- runtime/mirror/object.cc | 4 +- runtime/mirror/object.h | 31 +---- runtime/monitor.cc | 5 +- runtime/monitor.h | 2 +- runtime/native/java_lang_reflect_Field.cc | 2 +- runtime/native/sun_misc_Unsafe.cc | 6 +- runtime/thread_pool_test.cc | 2 +- 30 files changed, 294 insertions(+), 264 deletions(-) diff --git a/compiler/dex/quick/arm/int_arm.cc b/compiler/dex/quick/arm/int_arm.cc index e839fe5c5d5..d5173b06783 100644 --- a/compiler/dex/quick/arm/int_arm.cc +++ b/compiler/dex/quick/arm/int_arm.cc @@ -760,10 +760,10 @@ void ArmMir2Lir::GenMemBarrier(MemBarrierKind barrier_kind) { int dmb_flavor; // TODO: revisit Arm barrier kinds switch (barrier_kind) { - case kLoadStore: dmb_flavor = kSY; break; - case kLoadLoad: dmb_flavor = kSY; break; - case kStoreStore: dmb_flavor = kST; break; - case kStoreLoad: dmb_flavor = kSY; break; + case kLoadStore: dmb_flavor = kISH; break; + case kLoadLoad: dmb_flavor = kISH; break; + case kStoreStore: dmb_flavor = kISHST; break; + case kStoreLoad: dmb_flavor = kISH; break; default: LOG(FATAL) << "Unexpected MemBarrierKind: " << barrier_kind; dmb_flavor = kSY; // quiet gcc. diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc index 11245419f7f..9cffb3c4519 100644 --- a/compiler/driver/compiler_driver.cc +++ b/compiler/driver/compiler_driver.cc @@ -1404,7 +1404,7 @@ class ParallelCompilationManager { } size_t NextIndex() { - return index_.fetch_add(1); + return index_.FetchAndAdd(1); } private: diff --git a/disassembler/disassembler_arm.cc b/disassembler/disassembler_arm.cc index 90d84d5a725..71f70c4e5b1 100644 --- a/disassembler/disassembler_arm.cc +++ b/disassembler/disassembler_arm.cc @@ -81,6 +81,19 @@ void DisassemblerArm::DumpCond(std::ostream& os, uint32_t cond) { } } +void DisassemblerArm::DumpMemoryDomain(std::ostream& os, uint32_t domain) { + switch (domain) { + case 0b1111: os << "sy"; break; + case 0b1110: os << "st"; break; + case 0b1011: os << "ish"; break; + case 0b1010: os << "ishst"; break; + case 0b0111: os << "nsh"; break; + case 0b0110: os << "nshst"; break; + case 0b0011: os << "osh"; break; + case 0b0010: os << "oshst"; break; + } +} + void DisassemblerArm::DumpBranchTarget(std::ostream& os, const uint8_t* instr_ptr, int32_t imm32) { os << StringPrintf("%+d (%p)", imm32, instr_ptr + imm32); } @@ -996,9 +1009,9 @@ size_t DisassemblerArm::DumpThumb32(std::ostream& os, const uint8_t* instr_ptr) // Miscellaneous control instructions uint32_t op5 = (instr >> 4) & 0xF; switch (op5) { - case 4: opcode << "dsb"; break; - case 5: opcode << "dmb"; break; - case 6: opcode << "isb"; break; + case 4: opcode << "dsb"; DumpMemoryDomain(args, instr & 0xF); break; + case 5: opcode << "dmb"; DumpMemoryDomain(args, instr & 0xF); break; + case 6: opcode << "isb"; DumpMemoryDomain(args, instr & 0xF); break; } } break; diff --git a/disassembler/disassembler_arm.h b/disassembler/disassembler_arm.h index 2e699ffe88f..e34274e1260 100644 --- a/disassembler/disassembler_arm.h +++ b/disassembler/disassembler_arm.h @@ -30,6 +30,7 @@ class DisassemblerArm : public Disassembler { virtual size_t Dump(std::ostream& os, const uint8_t* begin); virtual void Dump(std::ostream& os, const uint8_t* begin, const uint8_t* end); + private: void DumpArm(std::ostream& os, const uint8_t* instr); @@ -39,6 +40,7 @@ class DisassemblerArm : public Disassembler { void DumpBranchTarget(std::ostream& os, const uint8_t* instr_ptr, int32_t imm32); void DumpCond(std::ostream& os, uint32_t cond); + void DumpMemoryDomain(std::ostream& os, uint32_t domain); std::vector it_conditions_; diff --git a/runtime/arch/arm/quick_entrypoints_arm.S b/runtime/arch/arm/quick_entrypoints_arm.S index 61be14ba591..34de93fb4de 100644 --- a/runtime/arch/arm/quick_entrypoints_arm.S +++ b/runtime/arch/arm/quick_entrypoints_arm.S @@ -359,6 +359,7 @@ retry_lock: @ unlocked case - r2 holds thread id with count of 0 strex r3, r2, [r0, #LOCK_WORD_OFFSET] cbnz r3, strex_fail @ store failed, retry + dmb ish @ full (LoadLoad) memory barrier bx lr strex_fail: b retry_lock @ unlikely forward branch, need to reload and recheck r1/r2 @@ -402,6 +403,7 @@ ENTRY art_quick_unlock_object bpl recursive_thin_unlock @ transition to unlocked, r3 holds 0 str r3, [r0, #LOCK_WORD_OFFSET] + dmb ish @ full (StoreLoad) memory barrier bx lr recursive_thin_unlock: sub r1, r1, #65536 diff --git a/runtime/atomic.cc b/runtime/atomic.cc index 47cee6aa2b8..bac0a992c40 100644 --- a/runtime/atomic.cc +++ b/runtime/atomic.cc @@ -15,135 +15,52 @@ */ #include "atomic.h" - -#define NEED_SWAP_MUTEXES !defined(__arm__) && !defined(__i386__) - -#if NEED_SWAP_MUTEXES -#include #include "base/mutex.h" #include "base/stl_util.h" -#include "base/stringprintf.h" #include "thread-inl.h" -#endif namespace art { -#if NEED_SWAP_MUTEXES -// We stripe across a bunch of different mutexes to reduce contention. -static const size_t kSwapMutexCount = 32; -static std::vector* gSwapMutexes; +std::vector* QuasiAtomic::gSwapMutexes = nullptr; -static Mutex& GetSwapMutex(const volatile int64_t* addr) { - return *(*gSwapMutexes)[(reinterpret_cast(addr) >> 3U) % kSwapMutexCount]; +Mutex* QuasiAtomic::GetSwapMutex(const volatile int64_t* addr) { + return (*gSwapMutexes)[(reinterpret_cast(addr) >> 3U) % kSwapMutexCount]; } -#endif void QuasiAtomic::Startup() { -#if NEED_SWAP_MUTEXES - gSwapMutexes = new std::vector; - for (size_t i = 0; i < kSwapMutexCount; ++i) { - gSwapMutexes->push_back(new Mutex("QuasiAtomic stripe")); + if (kNeedSwapMutexes) { + gSwapMutexes = new std::vector; + for (size_t i = 0; i < kSwapMutexCount; ++i) { + gSwapMutexes->push_back(new Mutex("QuasiAtomic stripe")); + } } -#endif } void QuasiAtomic::Shutdown() { -#if NEED_SWAP_MUTEXES - STLDeleteElements(gSwapMutexes); - delete gSwapMutexes; -#endif + if (kNeedSwapMutexes) { + STLDeleteElements(gSwapMutexes); + delete gSwapMutexes; + } } -int64_t QuasiAtomic::Read64(volatile const int64_t* addr) { - int64_t value; -#if NEED_SWAP_MUTEXES - MutexLock mu(Thread::Current(), GetSwapMutex(addr)); - value = *addr; -#elif defined(__arm__) - // Exclusive loads are defined not to tear, clearing the exclusive state isn't necessary. If we - // have LPAE (such as Cortex-A15) then ldrd would suffice. - __asm__ __volatile__("@ QuasiAtomic::Read64\n" - "ldrexd %0, %H0, [%1]" - : "=&r" (value) - : "r" (addr)); -#elif defined(__i386__) - __asm__ __volatile__( - "movq %1, %0\n" - : "=x" (value) - : "m" (*addr)); -#else -#error Unexpected architecture -#endif - return value; +int64_t QuasiAtomic::SwapMutexRead64(volatile const int64_t* addr) { + MutexLock mu(Thread::Current(), *GetSwapMutex(addr)); + return *addr; } -void QuasiAtomic::Write64(volatile int64_t* addr, int64_t value) { -#if NEED_SWAP_MUTEXES - MutexLock mu(Thread::Current(), GetSwapMutex(addr)); +void QuasiAtomic::SwapMutexWrite64(volatile int64_t* addr, int64_t value) { + MutexLock mu(Thread::Current(), *GetSwapMutex(addr)); *addr = value; -#elif defined(__arm__) - // The write is done as a swap so that the cache-line is in the exclusive state for the store. If - // we know that ARM architecture has LPAE (such as Cortex-A15) this isn't necessary and strd will - // suffice. - int64_t prev; - int status; - do { - __asm__ __volatile__("@ QuasiAtomic::Write64\n" - "ldrexd %0, %H0, [%3]\n" - "strexd %1, %4, %H4, [%3]" - : "=&r" (prev), "=&r" (status), "+m"(*addr) - : "r" (addr), "r" (value) - : "cc"); - } while (__builtin_expect(status != 0, 0)); -#elif defined(__i386__) - __asm__ __volatile__( - "movq %1, %0" - : "=m" (*addr) - : "x" (value)); -#else -#error Unexpected architecture -#endif } -bool QuasiAtomic::Cas64(int64_t old_value, int64_t new_value, volatile int64_t* addr) { -#if NEED_SWAP_MUTEXES - MutexLock mu(Thread::Current(), GetSwapMutex(addr)); +bool QuasiAtomic::SwapMutexCas64(int64_t old_value, int64_t new_value, volatile int64_t* addr) { + MutexLock mu(Thread::Current(), *GetSwapMutex(addr)); if (*addr == old_value) { *addr = new_value; return true; } return false; -#elif defined(__arm__) - int64_t prev; - int status; - do { - __asm__ __volatile__("@ QuasiAtomic::Cas64\n" - "ldrexd %0, %H0, [%3]\n" - "mov %1, #0\n" - "teq %0, %4\n" - "teqeq %H0, %H4\n" - "strexdeq %1, %5, %H5, [%3]" - : "=&r" (prev), "=&r" (status), "+m"(*addr) - : "r" (addr), "Ir" (old_value), "r" (new_value) - : "cc"); - } while (__builtin_expect(status != 0, 0)); - return prev == old_value; -#elif defined(__i386__) - // The compiler does the right job and works better than inline assembly, especially with -O0 - // compilation. - return __sync_bool_compare_and_swap(addr, old_value, new_value); -#else -#error Unexpected architecture -#endif -} - -bool QuasiAtomic::LongAtomicsUseMutexes() { -#if NEED_SWAP_MUTEXES - return true; -#else - return false; -#endif } } // namespace art diff --git a/runtime/atomic.h b/runtime/atomic.h index cb6f86b9213..b1e9870acfc 100644 --- a/runtime/atomic.h +++ b/runtime/atomic.h @@ -18,11 +18,14 @@ #define ART_RUNTIME_ATOMIC_H_ #include +#include #include "base/macros.h" namespace art { +class Mutex; + // NOTE: Two "quasiatomic" operations on the exact same memory address // are guaranteed to operate atomically with respect to each other, // but no guarantees are made about quasiatomic operations mixed with @@ -30,25 +33,108 @@ namespace art { // quasiatomic operations that are performed on partially-overlapping // memory. class QuasiAtomic { +#if !defined(__arm__) && !defined(__i386__) + static constexpr bool kNeedSwapMutexes = true; +#else + static constexpr bool kNeedSwapMutexes = false; +#endif + public: static void Startup(); static void Shutdown(); // Reads the 64-bit value at "addr" without tearing. - static int64_t Read64(volatile const int64_t* addr); + static int64_t Read64(volatile const int64_t* addr) { + if (!kNeedSwapMutexes) { + return *addr; + } else { + return SwapMutexRead64(addr); + } + } // Writes to the 64-bit value at "addr" without tearing. - static void Write64(volatile int64_t* addr, int64_t val); + static void Write64(volatile int64_t* addr, int64_t val) { + if (!kNeedSwapMutexes) { + *addr = val; + } else { + SwapMutexWrite64(addr, val); + } + } // Atomically compare the value at "addr" to "old_value", if equal replace it with "new_value" // and return true. Otherwise, don't swap, and return false. - static bool Cas64(int64_t old_value, int64_t new_value, volatile int64_t* addr); + static bool Cas64(int64_t old_value, int64_t new_value, volatile int64_t* addr) { + if (!kNeedSwapMutexes) { + return __sync_bool_compare_and_swap(addr, old_value, new_value); + } else { + return SwapMutexCas64(old_value, new_value, addr); + } + } // Does the architecture provide reasonable atomic long operations or do we fall back on mutexes? - static bool LongAtomicsUseMutexes(); + static bool LongAtomicsUseMutexes() { + return !kNeedSwapMutexes; + } + + static void MembarLoadStore() { + #if defined(__arm__) + __asm__ __volatile__("dmb ish" : : : "memory"); + #elif defined(__i386__) + __asm__ __volatile__("" : : : "memory"); + #elif defined(__mips__) + __asm__ __volatile__("sync" : : : "memory"); + #else + #error Unexpected architecture + #endif + } + + static void MembarLoadLoad() { + #if defined(__arm__) + __asm__ __volatile__("dmb ish" : : : "memory"); + #elif defined(__i386__) + __asm__ __volatile__("" : : : "memory"); + #elif defined(__mips__) + __asm__ __volatile__("sync" : : : "memory"); + #else + #error Unexpected architecture + #endif + } + + static void MembarStoreStore() { + #if defined(__arm__) + __asm__ __volatile__("dmb ishst" : : : "memory"); + #elif defined(__i386__) + __asm__ __volatile__("" : : : "memory"); + #elif defined(__mips__) + __asm__ __volatile__("sync" : : : "memory"); + #else + #error Unexpected architecture + #endif + } + + static void MembarStoreLoad() { + #if defined(__arm__) + __asm__ __volatile__("dmb ish" : : : "memory"); + #elif defined(__i386__) + __asm__ __volatile__("mfence" : : : "memory"); + #elif defined(__mips__) + __asm__ __volatile__("sync" : : : "memory"); + #else + #error Unexpected architecture + #endif + } private: + static Mutex* GetSwapMutex(const volatile int64_t* addr); + static int64_t SwapMutexRead64(volatile const int64_t* addr); + static void SwapMutexWrite64(volatile int64_t* addr, int64_t val); + static bool SwapMutexCas64(int64_t old_value, int64_t new_value, volatile int64_t* addr); + + // We stripe across a bunch of different mutexes to reduce contention. + static constexpr size_t kSwapMutexCount = 32; + static std::vector* gSwapMutexes; + DISALLOW_COPY_AND_ASSIGN(QuasiAtomic); }; diff --git a/runtime/atomic_integer.h b/runtime/atomic_integer.h index 132f9689d64..651ca4a6e91 100644 --- a/runtime/atomic_integer.h +++ b/runtime/atomic_integer.h @@ -17,8 +17,7 @@ #ifndef ART_RUNTIME_ATOMIC_INTEGER_H_ #define ART_RUNTIME_ATOMIC_INTEGER_H_ -#include "cutils/atomic.h" -#include "cutils/atomic-inline.h" +#include namespace art { @@ -28,53 +27,57 @@ class AtomicInteger { explicit AtomicInteger(int32_t value) : value_(value) { } - // Unsafe = operator for non atomic operations on the integer. - void store(int32_t desired) { - value_ = desired; - } - AtomicInteger& operator=(int32_t desired) { - store(desired); + Store(desired); return *this; } - int32_t load() const { + int32_t Load() const { return value_; } operator int32_t() const { - return load(); + return Load(); + } + + int32_t FetchAndAdd(const int32_t value) { + return __sync_fetch_and_add(&value_, value); // Return old_value. } - int32_t fetch_add(const int32_t value) { - return android_atomic_add(value, &value_); + int32_t FetchAndSub(const int32_t value) { + return __sync_fetch_and_sub(&value_, value); // Return old value. } - int32_t fetch_sub(const int32_t value) { - return android_atomic_add(-value, &value_); + int32_t operator++() { // Prefix operator. + return __sync_add_and_fetch(&value_, 1); // Return new value. } - int32_t operator++() { - return android_atomic_inc(&value_) + 1; + int32_t operator++(int32_t) { // Postfix operator. + return __sync_fetch_and_add(&value_, 1); // Return old value. } - int32_t operator++(int32_t) { - return android_atomic_inc(&value_); + int32_t operator--() { // Prefix operator. + return __sync_sub_and_fetch(&value_, 1); // Return new value. } - int32_t operator--() { - return android_atomic_dec(&value_) - 1; + int32_t operator--(int32_t) { // Postfix operator. + return __sync_fetch_and_sub(&value_, 1); // Return old value. } - int32_t operator--(int32_t) { - return android_atomic_dec(&value_); + bool CompareAndSwap(int32_t expected_value, int32_t desired_value) { + return __sync_bool_compare_and_swap(&value_, expected_value, desired_value); } - bool compare_and_swap(int32_t expected_value, int32_t desired_value) { - return android_atomic_cas(expected_value, desired_value, &value_) == 0; + volatile int32_t* Address() { + return &value_; } private: + // Unsafe = operator for non atomic operations on the integer. + void Store(int32_t desired) { + value_ = desired; + } + volatile int32_t value_; }; diff --git a/runtime/base/bounded_fifo.h b/runtime/base/bounded_fifo.h index cb92d40995d..d04840aa23d 100644 --- a/runtime/base/bounded_fifo.h +++ b/runtime/base/bounded_fifo.h @@ -17,9 +17,6 @@ #ifndef ART_RUNTIME_BASE_BOUNDED_FIFO_H_ #define ART_RUNTIME_BASE_BOUNDED_FIFO_H_ -#include "cutils/atomic.h" -#include "cutils/atomic-inline.h" - namespace art { // A bounded fifo is a fifo which has a bounded size. The power of two version uses a bit mask to @@ -49,7 +46,7 @@ class BoundedFifoPowerOfTwo { void push_back(const T& value) { ++size_; DCHECK_LE(size_, MaxSize); - // Relies on integer overflow behaviour. + // Relies on integer overflow behavior. data_[back_index_++ & mask_] = value; } diff --git a/runtime/base/mutex.cc b/runtime/base/mutex.cc index ec79c558d29..05e3a8327e8 100644 --- a/runtime/base/mutex.cc +++ b/runtime/base/mutex.cc @@ -21,8 +21,6 @@ #include "atomic.h" #include "base/logging.h" -#include "cutils/atomic.h" -#include "cutils/atomic-inline.h" #include "mutex-inl.h" #include "runtime.h" #include "scoped_thread_state_change.h" @@ -59,12 +57,12 @@ static struct AllMutexData gAllMutexData[kAllMutexDataSize]; class ScopedAllMutexesLock { public: explicit ScopedAllMutexesLock(const BaseMutex* mutex) : mutex_(mutex) { - while (!gAllMutexData->all_mutexes_guard.compare_and_swap(0, reinterpret_cast(mutex))) { + while (!gAllMutexData->all_mutexes_guard.CompareAndSwap(0, reinterpret_cast(mutex))) { NanoSleep(100); } } ~ScopedAllMutexesLock() { - while (!gAllMutexData->all_mutexes_guard.compare_and_swap(reinterpret_cast(mutex_), 0)) { + while (!gAllMutexData->all_mutexes_guard.CompareAndSwap(reinterpret_cast(mutex_), 0)) { NanoSleep(100); } } @@ -176,7 +174,7 @@ void BaseMutex::RecordContention(uint64_t blocked_tid, do { slot = data->cur_content_log_entry; new_slot = (slot + 1) % kContentionLogSize; - } while (!data->cur_content_log_entry.compare_and_swap(slot, new_slot)); + } while (!data->cur_content_log_entry.CompareAndSwap(slot, new_slot)); log[new_slot].blocked_tid = blocked_tid; log[new_slot].owner_tid = owner_tid; log[new_slot].count = 1; @@ -300,11 +298,11 @@ void Mutex::ExclusiveLock(Thread* self) { int32_t cur_state = state_; if (LIKELY(cur_state == 0)) { // Change state from 0 to 1. - done = android_atomic_acquire_cas(0, 1, &state_) == 0; + done = __sync_bool_compare_and_swap(&state_, 0 /* cur_state */, 1 /* new state */); } else { // Failed to acquire, hang up. ScopedContentionRecorder scr(this, SafeGetTid(self), GetExclusiveOwnerTid()); - android_atomic_inc(&num_contenders_); + num_contenders_++; if (futex(&state_, FUTEX_WAIT, 1, NULL, NULL, 0) != 0) { // EAGAIN and EINTR both indicate a spurious failure, try again from the beginning. // We don't use TEMP_FAILURE_RETRY so we can intentionally retry to acquire the lock. @@ -312,9 +310,10 @@ void Mutex::ExclusiveLock(Thread* self) { PLOG(FATAL) << "futex wait failed for " << name_; } } - android_atomic_dec(&num_contenders_); + num_contenders_--; } } while (!done); + QuasiAtomic::MembarStoreLoad(); DCHECK_EQ(state_, 1); exclusive_owner_ = SafeGetTid(self); #else @@ -342,11 +341,12 @@ bool Mutex::ExclusiveTryLock(Thread* self) { int32_t cur_state = state_; if (cur_state == 0) { // Change state from 0 to 1. - done = android_atomic_acquire_cas(0, 1, &state_) == 0; + done = __sync_bool_compare_and_swap(&state_, 0 /* cur_state */, 1 /* new state */); } else { return false; } } while (!done); + QuasiAtomic::MembarStoreLoad(); DCHECK_EQ(state_, 1); exclusive_owner_ = SafeGetTid(self); #else @@ -385,10 +385,11 @@ void Mutex::ExclusiveUnlock(Thread* self) { do { int32_t cur_state = state_; if (LIKELY(cur_state == 1)) { + QuasiAtomic::MembarStoreStore(); // We're no longer the owner. exclusive_owner_ = 0; // Change state to 0. - done = android_atomic_release_cas(cur_state, 0, &state_) == 0; + done = __sync_bool_compare_and_swap(&state_, cur_state, 0 /* new state */); if (LIKELY(done)) { // Spurious fail? // Wake a contender if (UNLIKELY(num_contenders_ > 0)) { @@ -407,6 +408,7 @@ void Mutex::ExclusiveUnlock(Thread* self) { } } } while (!done); + QuasiAtomic::MembarStoreLoad(); #else CHECK_MUTEX_CALL(pthread_mutex_unlock, (&mutex_)); #endif @@ -468,11 +470,11 @@ void ReaderWriterMutex::ExclusiveLock(Thread* self) { int32_t cur_state = state_; if (LIKELY(cur_state == 0)) { // Change state from 0 to -1. - done = android_atomic_acquire_cas(0, -1, &state_) == 0; + done = __sync_bool_compare_and_swap(&state_, 0 /* cur_state*/, -1 /* new state */); } else { // Failed to acquire, hang up. ScopedContentionRecorder scr(this, SafeGetTid(self), GetExclusiveOwnerTid()); - android_atomic_inc(&num_pending_writers_); + num_pending_writers_++; if (futex(&state_, FUTEX_WAIT, cur_state, NULL, NULL, 0) != 0) { // EAGAIN and EINTR both indicate a spurious failure, try again from the beginning. // We don't use TEMP_FAILURE_RETRY so we can intentionally retry to acquire the lock. @@ -480,7 +482,7 @@ void ReaderWriterMutex::ExclusiveLock(Thread* self) { PLOG(FATAL) << "futex wait failed for " << name_; } } - android_atomic_dec(&num_pending_writers_); + num_pending_writers_--; } } while (!done); DCHECK_EQ(state_, -1); @@ -504,7 +506,7 @@ void ReaderWriterMutex::ExclusiveUnlock(Thread* self) { // We're no longer the owner. exclusive_owner_ = 0; // Change state from -1 to 0. - done = android_atomic_release_cas(-1, 0, &state_) == 0; + done = __sync_bool_compare_and_swap(&state_, -1 /* cur_state*/, 0 /* new state */); if (LIKELY(done)) { // cmpxchg may fail due to noise? // Wake any waiters. if (UNLIKELY(num_pending_readers_ > 0 || num_pending_writers_ > 0)) { @@ -531,7 +533,7 @@ bool ReaderWriterMutex::ExclusiveLockWithTimeout(Thread* self, int64_t ms, int32 int32_t cur_state = state_; if (cur_state == 0) { // Change state from 0 to -1. - done = android_atomic_acquire_cas(0, -1, &state_) == 0; + done = __sync_bool_compare_and_swap(&state_, 0 /* cur_state */, -1 /* new state */); } else { // Failed to acquire, hang up. timespec now_abs_ts; @@ -541,10 +543,10 @@ bool ReaderWriterMutex::ExclusiveLockWithTimeout(Thread* self, int64_t ms, int32 return false; // Timed out. } ScopedContentionRecorder scr(this, SafeGetTid(self), GetExclusiveOwnerTid()); - android_atomic_inc(&num_pending_writers_); + num_pending_writers_++; if (futex(&state_, FUTEX_WAIT, cur_state, &rel_ts, NULL, 0) != 0) { if (errno == ETIMEDOUT) { - android_atomic_dec(&num_pending_writers_); + num_pending_writers_--; return false; // Timed out. } else if ((errno != EAGAIN) && (errno != EINTR)) { // EAGAIN and EINTR both indicate a spurious failure, @@ -553,7 +555,7 @@ bool ReaderWriterMutex::ExclusiveLockWithTimeout(Thread* self, int64_t ms, int32 PLOG(FATAL) << "timed futex wait failed for " << name_; } } - android_atomic_dec(&num_pending_writers_); + num_pending_writers_--; } } while (!done); exclusive_owner_ = SafeGetTid(self); @@ -583,7 +585,7 @@ bool ReaderWriterMutex::SharedTryLock(Thread* self) { int32_t cur_state = state_; if (cur_state >= 0) { // Add as an extra reader. - done = android_atomic_acquire_cas(cur_state, cur_state + 1, &state_) == 0; + done = __sync_bool_compare_and_swap(&state_, cur_state, cur_state + 1); } else { // Owner holds it exclusively. return false; @@ -666,13 +668,13 @@ void ConditionVariable::Broadcast(Thread* self) { DCHECK_EQ(guard_.GetExclusiveOwnerTid(), SafeGetTid(self)); #if ART_USE_FUTEXES if (num_waiters_ > 0) { - android_atomic_inc(&sequence_); // Indicate the broadcast occurred. + sequence_++; // Indicate the broadcast occurred. bool done = false; do { int32_t cur_sequence = sequence_; // Requeue waiters onto mutex. The waiter holds the contender count on the mutex high ensuring // mutex unlocks will awaken the requeued waiter thread. - done = futex(&sequence_, FUTEX_CMP_REQUEUE, 0, + done = futex(sequence_.Address(), FUTEX_CMP_REQUEUE, 0, reinterpret_cast(std::numeric_limits::max()), &guard_.state_, cur_sequence) != -1; if (!done) { @@ -692,10 +694,10 @@ void ConditionVariable::Signal(Thread* self) { guard_.AssertExclusiveHeld(self); #if ART_USE_FUTEXES if (num_waiters_ > 0) { - android_atomic_inc(&sequence_); // Indicate a signal occurred. + sequence_++; // Indicate a signal occurred. // Futex wake 1 waiter who will then come and in contend on mutex. It'd be nice to requeue them // to avoid this, however, requeueing can only move all waiters. - int num_woken = futex(&sequence_, FUTEX_WAKE, 1, NULL, NULL, 0); + int num_woken = futex(sequence_.Address(), FUTEX_WAKE, 1, NULL, NULL, 0); // Check something was woken or else we changed sequence_ before they had chance to wait. CHECK((num_woken == 0) || (num_woken == 1)); } @@ -716,11 +718,11 @@ void ConditionVariable::WaitHoldingLocks(Thread* self) { #if ART_USE_FUTEXES num_waiters_++; // Ensure the Mutex is contended so that requeued threads are awoken. - android_atomic_inc(&guard_.num_contenders_); + guard_.num_contenders_++; guard_.recursion_count_ = 1; int32_t cur_sequence = sequence_; guard_.ExclusiveUnlock(self); - if (futex(&sequence_, FUTEX_WAIT, cur_sequence, NULL, NULL, 0) != 0) { + if (futex(sequence_.Address(), FUTEX_WAIT, cur_sequence, NULL, NULL, 0) != 0) { // Futex failed, check it is an expected error. // EAGAIN == EWOULDBLK, so we let the caller try again. // EINTR implies a signal was sent to this thread. @@ -733,7 +735,7 @@ void ConditionVariable::WaitHoldingLocks(Thread* self) { num_waiters_--; // We awoke and so no longer require awakes from the guard_'s unlock. CHECK_GE(guard_.num_contenders_, 0); - android_atomic_dec(&guard_.num_contenders_); + guard_.num_contenders_--; #else guard_.recursion_count_ = 0; CHECK_MUTEX_CALL(pthread_cond_wait, (&cond_, &guard_.mutex_)); @@ -751,11 +753,11 @@ void ConditionVariable::TimedWait(Thread* self, int64_t ms, int32_t ns) { InitTimeSpec(false, CLOCK_REALTIME, ms, ns, &rel_ts); num_waiters_++; // Ensure the Mutex is contended so that requeued threads are awoken. - android_atomic_inc(&guard_.num_contenders_); + guard_.num_contenders_++; guard_.recursion_count_ = 1; int32_t cur_sequence = sequence_; guard_.ExclusiveUnlock(self); - if (futex(&sequence_, FUTEX_WAIT, cur_sequence, &rel_ts, NULL, 0) != 0) { + if (futex(sequence_.Address(), FUTEX_WAIT, cur_sequence, &rel_ts, NULL, 0) != 0) { if (errno == ETIMEDOUT) { // Timed out we're done. } else if ((errno == EAGAIN) || (errno == EINTR)) { @@ -769,7 +771,7 @@ void ConditionVariable::TimedWait(Thread* self, int64_t ms, int32_t ns) { num_waiters_--; // We awoke and so no longer require awakes from the guard_'s unlock. CHECK_GE(guard_.num_contenders_, 0); - android_atomic_dec(&guard_.num_contenders_); + guard_.num_contenders_--; #else #ifdef HAVE_TIMEDWAIT_MONOTONIC #define TIMEDWAIT pthread_cond_timedwait_monotonic diff --git a/runtime/base/mutex.h b/runtime/base/mutex.h index b894c0a3fa5..1c1dcaf43ff 100644 --- a/runtime/base/mutex.h +++ b/runtime/base/mutex.h @@ -191,7 +191,7 @@ class LOCKABLE Mutex : public BaseMutex { // Exclusive owner. volatile uint64_t exclusive_owner_; // Number of waiting contenders. - volatile int32_t num_contenders_; + AtomicInteger num_contenders_; #else pthread_mutex_t mutex_; #endif @@ -304,7 +304,7 @@ class LOCKABLE ReaderWriterMutex : public BaseMutex { // Pending readers. volatile int32_t num_pending_readers_; // Pending writers. - volatile int32_t num_pending_writers_; + AtomicInteger num_pending_writers_; #else pthread_rwlock_t rwlock_; #endif @@ -339,7 +339,7 @@ class ConditionVariable { // their Mutex and another thread takes it and signals, the waiting thread observes that sequence_ // changed and doesn't enter the wait. Modified while holding guard_, but is read by futex wait // without guard_ held. - volatile int32_t sequence_; + AtomicInteger sequence_; // Number of threads that have come into to wait, not the length of the waiters on the futex as // waiters may have been requeued onto guard_. Guarded by guard_. volatile int32_t num_waiters_; diff --git a/runtime/gc/accounting/atomic_stack.h b/runtime/gc/accounting/atomic_stack.h index 8fa5b86f9a8..02e01b86f7d 100644 --- a/runtime/gc/accounting/atomic_stack.h +++ b/runtime/gc/accounting/atomic_stack.h @@ -68,7 +68,7 @@ class AtomicStack { // Stack overflow. return false; } - } while (!back_index_.compare_and_swap(index, index + 1)); + } while (!back_index_.CompareAndSwap(index, index + 1)); begin_[index] = value; return true; } @@ -93,7 +93,7 @@ class AtomicStack { // Take an item from the front of the stack. T PopFront() { int32_t index = front_index_; - DCHECK_LT(index, back_index_.load()); + DCHECK_LT(index, back_index_.Load()); front_index_ = front_index_ + 1; return begin_[index]; } @@ -101,7 +101,7 @@ class AtomicStack { // Pop a number of elements. void PopBackCount(int32_t n) { DCHECK_GE(Size(), static_cast(n)); - back_index_.fetch_sub(n); + back_index_.FetchAndSub(n); } bool IsEmpty() const { @@ -132,11 +132,11 @@ class AtomicStack { } void Sort() { - int32_t start_back_index = back_index_.load(); - int32_t start_front_index = front_index_.load(); + int32_t start_back_index = back_index_.Load(); + int32_t start_front_index = front_index_.Load(); std::sort(Begin(), End()); - CHECK_EQ(start_back_index, back_index_.load()); - CHECK_EQ(start_front_index, front_index_.load()); + CHECK_EQ(start_back_index, back_index_.Load()); + CHECK_EQ(start_front_index, front_index_.Load()); if (kIsDebugBuild) { debug_is_sorted_ = true; } diff --git a/runtime/gc/collector/mark_sweep.cc b/runtime/gc/collector/mark_sweep.cc index 28cc5108069..cae2a548934 100644 --- a/runtime/gc/collector/mark_sweep.cc +++ b/runtime/gc/collector/mark_sweep.cc @@ -1109,8 +1109,8 @@ void MarkSweep::SweepCallback(size_t num_ptrs, Object** ptrs, void* arg) { // AllocSpace::FreeList clears the value in ptrs, so perform after clearing the live bit size_t freed_bytes = space->FreeList(self, num_ptrs, ptrs); heap->RecordFree(freed_objects, freed_bytes); - mark_sweep->freed_objects_.fetch_add(freed_objects); - mark_sweep->freed_bytes_.fetch_add(freed_bytes); + mark_sweep->freed_objects_.FetchAndAdd(freed_objects); + mark_sweep->freed_bytes_.FetchAndAdd(freed_bytes); } void MarkSweep::ZygoteSweepCallback(size_t num_ptrs, Object** ptrs, void* arg) { @@ -1192,10 +1192,10 @@ void MarkSweep::SweepArray(accounting::ObjectStack* allocations, bool swap_bitma VLOG(heap) << "Freed " << freed_objects << "/" << count << " objects with size " << PrettySize(freed_bytes); heap_->RecordFree(freed_objects + freed_large_objects, freed_bytes + freed_large_object_bytes); - freed_objects_.fetch_add(freed_objects); - freed_large_objects_.fetch_add(freed_large_objects); - freed_bytes_.fetch_add(freed_bytes); - freed_large_object_bytes_.fetch_add(freed_large_object_bytes); + freed_objects_.FetchAndAdd(freed_objects); + freed_large_objects_.FetchAndAdd(freed_large_objects); + freed_bytes_.FetchAndAdd(freed_bytes); + freed_large_object_bytes_.FetchAndAdd(freed_large_object_bytes); timings_.EndSplit(); timings_.StartSplit("ResetStack"); @@ -1267,8 +1267,8 @@ void MarkSweep::SweepLargeObjects(bool swap_bitmaps) { ++freed_objects; } } - freed_large_objects_.fetch_add(freed_objects); - freed_large_object_bytes_.fetch_add(freed_bytes); + freed_large_objects_.FetchAndAdd(freed_objects); + freed_large_object_bytes_.FetchAndAdd(freed_bytes); GetHeap()->RecordFree(freed_objects, freed_bytes); } diff --git a/runtime/gc/collector/semi_space.cc b/runtime/gc/collector/semi_space.cc index f29eadb34b0..a4f7121005e 100644 --- a/runtime/gc/collector/semi_space.cc +++ b/runtime/gc/collector/semi_space.cc @@ -236,8 +236,8 @@ void SemiSpace::ReclaimPhase() { int freed_bytes = from_bytes - to_bytes; int freed_objects = from_objects - to_objects; CHECK_GE(freed_bytes, 0); - freed_bytes_.fetch_add(freed_bytes); - freed_objects_.fetch_add(freed_objects); + freed_bytes_.FetchAndAdd(freed_bytes); + freed_objects_.FetchAndAdd(freed_objects); heap_->RecordFree(static_cast(freed_objects), static_cast(freed_bytes)); timings_.StartSplit("PreSweepingGcVerification"); @@ -332,7 +332,7 @@ Object* SemiSpace::MarkObject(Object* obj) { // If out of space, fall back to the to-space. forward_address = to_space_->Alloc(self_, object_size, &bytes_allocated); } else { - GetHeap()->num_bytes_allocated_.fetch_add(bytes_promoted); + GetHeap()->num_bytes_allocated_.FetchAndAdd(bytes_promoted); bytes_promoted_ += bytes_promoted; // Mark forward_address on the live bit map. accounting::SpaceBitmap* live_bitmap = non_moving_space->GetLiveBitmap(); @@ -446,8 +446,8 @@ void SemiSpace::SweepCallback(size_t num_ptrs, Object** ptrs, void* arg) { Locks::heap_bitmap_lock_->AssertExclusiveHeld(self); size_t freed_bytes = space->FreeList(self, num_ptrs, ptrs); heap->RecordFree(num_ptrs, freed_bytes); - gc->freed_objects_.fetch_add(num_ptrs); - gc->freed_bytes_.fetch_add(freed_bytes); + gc->freed_objects_.FetchAndAdd(num_ptrs); + gc->freed_bytes_.FetchAndAdd(freed_bytes); } void SemiSpace::ZygoteSweepCallback(size_t num_ptrs, Object** ptrs, void* arg) { @@ -526,8 +526,8 @@ void SemiSpace::SweepLargeObjects(bool swap_bitmaps) { ++freed_objects; } } - freed_large_objects_.fetch_add(freed_objects); - freed_large_object_bytes_.fetch_add(freed_bytes); + freed_large_objects_.FetchAndAdd(freed_objects); + freed_large_object_bytes_.FetchAndAdd(freed_bytes); GetHeap()->RecordFree(freed_objects, freed_bytes); } diff --git a/runtime/gc/heap-inl.h b/runtime/gc/heap-inl.h index 9fb5760bd76..af1b26b8706 100644 --- a/runtime/gc/heap-inl.h +++ b/runtime/gc/heap-inl.h @@ -61,7 +61,7 @@ inline mirror::Object* Heap::AllocObjectWithAllocator(Thread* self, mirror::Clas pre_fence_visitor(obj); DCHECK_GT(bytes_allocated, 0u); const size_t new_num_bytes_allocated = - static_cast(num_bytes_allocated_.fetch_add(bytes_allocated)) + bytes_allocated; + static_cast(num_bytes_allocated_.FetchAndAdd(bytes_allocated)) + bytes_allocated; // TODO: Deprecate. if (kInstrumented) { if (Runtime::Current()->HasStatsEnabled()) { @@ -200,7 +200,7 @@ inline Heap::AllocationTimer::~AllocationTimer() { // Only if the allocation succeeded, record the time. if (allocated_obj != nullptr) { uint64_t allocation_end_time = NanoTime() / kTimeAdjust; - heap_->total_allocation_time_.fetch_add(allocation_end_time - allocation_start_time_); + heap_->total_allocation_time_.FetchAndAdd(allocation_end_time - allocation_start_time_); } } }; diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc index 61c66e70dc3..e08106b2667 100644 --- a/runtime/gc/heap.cc +++ b/runtime/gc/heap.cc @@ -413,13 +413,13 @@ void Heap::AddSpace(space::Space* space) { void Heap::RegisterGCAllocation(size_t bytes) { if (this != nullptr) { - gc_memory_overhead_.fetch_add(bytes); + gc_memory_overhead_.FetchAndAdd(bytes); } } void Heap::RegisterGCDeAllocation(size_t bytes) { if (this != nullptr) { - gc_memory_overhead_.fetch_sub(bytes); + gc_memory_overhead_.FetchAndSub(bytes); } } @@ -802,7 +802,7 @@ void Heap::DumpSpaces(std::ostream& stream) { void Heap::VerifyObjectBody(const mirror::Object* obj) { CHECK(IsAligned(obj)) << "Object isn't aligned: " << obj; // Ignore early dawn of the universe verifications. - if (UNLIKELY(static_cast(num_bytes_allocated_.load()) < 10 * KB)) { + if (UNLIKELY(static_cast(num_bytes_allocated_.Load()) < 10 * KB)) { return; } const byte* raw_addr = reinterpret_cast(obj) + @@ -847,7 +847,8 @@ void Heap::VerifyHeap() { void Heap::RecordFree(size_t freed_objects, size_t freed_bytes) { DCHECK_LE(freed_bytes, static_cast(num_bytes_allocated_)); - num_bytes_allocated_.fetch_sub(freed_bytes); + num_bytes_allocated_.FetchAndSub(freed_bytes); + if (Runtime::Current()->HasStatsEnabled()) { RuntimeStats* thread_stats = Thread::Current()->GetStats(); thread_stats->freed_objects += freed_objects; @@ -2082,7 +2083,7 @@ void Heap::RegisterNativeAllocation(JNIEnv* env, int bytes) { native_need_to_run_finalization_ = false; } // Total number of native bytes allocated. - native_bytes_allocated_.fetch_add(bytes); + native_bytes_allocated_.FetchAndAdd(bytes); if (static_cast(native_bytes_allocated_) > native_footprint_gc_watermark_) { collector::GcType gc_type = have_zygote_space_ ? collector::kGcTypePartial : collector::kGcTypeFull; @@ -2118,7 +2119,7 @@ void Heap::RegisterNativeAllocation(JNIEnv* env, int bytes) { void Heap::RegisterNativeFree(JNIEnv* env, int bytes) { int expected_size, new_size; do { - expected_size = native_bytes_allocated_.load(); + expected_size = native_bytes_allocated_.Load(); new_size = expected_size - bytes; if (UNLIKELY(new_size < 0)) { ScopedObjectAccess soa(env); @@ -2127,7 +2128,7 @@ void Heap::RegisterNativeFree(JNIEnv* env, int bytes) { "registered as allocated", bytes, expected_size).c_str()); break; } - } while (!native_bytes_allocated_.compare_and_swap(expected_size, new_size)); + } while (!native_bytes_allocated_.CompareAndSwap(expected_size, new_size)); } int64_t Heap::GetTotalMemory() const { diff --git a/runtime/gc/space/bump_pointer_space-inl.h b/runtime/gc/space/bump_pointer_space-inl.h index 82e96a467e9..ac20972a70f 100644 --- a/runtime/gc/space/bump_pointer_space-inl.h +++ b/runtime/gc/space/bump_pointer_space-inl.h @@ -44,8 +44,8 @@ inline mirror::Object* BumpPointerSpace::AllocNonvirtualWithoutAccounting(size_t inline mirror::Object* BumpPointerSpace::AllocNonvirtual(size_t num_bytes) { mirror::Object* ret = AllocNonvirtualWithoutAccounting(num_bytes); if (ret != nullptr) { - objects_allocated_.fetch_add(1); - bytes_allocated_.fetch_add(num_bytes); + objects_allocated_.FetchAndAdd(1); + bytes_allocated_.FetchAndAdd(num_bytes); } return ret; } diff --git a/runtime/gc/space/bump_pointer_space.cc b/runtime/gc/space/bump_pointer_space.cc index 7ea202c0318..d5bc6674149 100644 --- a/runtime/gc/space/bump_pointer_space.cc +++ b/runtime/gc/space/bump_pointer_space.cc @@ -172,7 +172,7 @@ bool BumpPointerSpace::IsEmpty() const { uint64_t BumpPointerSpace::GetBytesAllocated() { // Start out pre-determined amount (blocks which are not being allocated into). - uint64_t total = static_cast(bytes_allocated_.load()); + uint64_t total = static_cast(bytes_allocated_.Load()); Thread* self = Thread::Current(); MutexLock mu(self, *Locks::runtime_shutdown_lock_); MutexLock mu2(self, *Locks::thread_list_lock_); @@ -190,7 +190,7 @@ uint64_t BumpPointerSpace::GetBytesAllocated() { uint64_t BumpPointerSpace::GetObjectsAllocated() { // Start out pre-determined amount (blocks which are not being allocated into). - uint64_t total = static_cast(objects_allocated_.load()); + uint64_t total = static_cast(objects_allocated_.Load()); Thread* self = Thread::Current(); MutexLock mu(self, *Locks::runtime_shutdown_lock_); MutexLock mu2(self, *Locks::thread_list_lock_); @@ -207,8 +207,8 @@ uint64_t BumpPointerSpace::GetObjectsAllocated() { } void BumpPointerSpace::RevokeThreadLocalBuffersLocked(Thread* thread) { - objects_allocated_.fetch_add(thread->thread_local_objects_); - bytes_allocated_.fetch_add(thread->thread_local_pos_ - thread->thread_local_start_); + objects_allocated_.FetchAndAdd(thread->thread_local_objects_); + bytes_allocated_.FetchAndAdd(thread->thread_local_pos_ - thread->thread_local_start_); thread->SetTLAB(nullptr, nullptr); } diff --git a/runtime/gc/space/image_space.cc b/runtime/gc/space/image_space.cc index c6177bd01d1..4777cc6fc76 100644 --- a/runtime/gc/space/image_space.cc +++ b/runtime/gc/space/image_space.cc @@ -227,7 +227,7 @@ ImageSpace* ImageSpace::Init(const char* image_file_name, bool validate_oat_file *error_msg = StringPrintf("Failed to map image bitmap: %s", error_msg->c_str()); return nullptr; } - size_t bitmap_index = bitmap_index_.fetch_add(1); + size_t bitmap_index = bitmap_index_.FetchAndAdd(1); std::string bitmap_name(StringPrintf("imagespace %s live-bitmap %u", image_file_name, bitmap_index)); UniquePtr bitmap( diff --git a/runtime/instrumentation.cc b/runtime/instrumentation.cc index 4ad9c633fbb..47c18995670 100644 --- a/runtime/instrumentation.cc +++ b/runtime/instrumentation.cc @@ -407,9 +407,9 @@ static void ResetQuickAllocEntryPointsForThread(Thread* thread, void* arg) { void Instrumentation::InstrumentQuickAllocEntryPoints() { // TODO: the read of quick_alloc_entry_points_instrumentation_counter_ is racey and this code // should be guarded by a lock. - DCHECK_GE(quick_alloc_entry_points_instrumentation_counter_.load(), 0); + DCHECK_GE(quick_alloc_entry_points_instrumentation_counter_.Load(), 0); const bool enable_instrumentation = - quick_alloc_entry_points_instrumentation_counter_.fetch_add(1) == 0; + quick_alloc_entry_points_instrumentation_counter_.FetchAndAdd(1) == 0; if (enable_instrumentation) { // Instrumentation wasn't enabled so enable it. SetQuickAllocEntryPointsInstrumented(true); @@ -420,9 +420,9 @@ void Instrumentation::InstrumentQuickAllocEntryPoints() { void Instrumentation::UninstrumentQuickAllocEntryPoints() { // TODO: the read of quick_alloc_entry_points_instrumentation_counter_ is racey and this code // should be guarded by a lock. - DCHECK_GT(quick_alloc_entry_points_instrumentation_counter_.load(), 0); + DCHECK_GT(quick_alloc_entry_points_instrumentation_counter_.Load(), 0); const bool disable_instrumentation = - quick_alloc_entry_points_instrumentation_counter_.fetch_sub(1) == 1; + quick_alloc_entry_points_instrumentation_counter_.FetchAndSub(1) == 1; if (disable_instrumentation) { SetQuickAllocEntryPointsInstrumented(false); ResetQuickAllocEntryPoints(); diff --git a/runtime/interpreter/interpreter_goto_table_impl.cc b/runtime/interpreter/interpreter_goto_table_impl.cc index 99c85bdb2ec..942c2751394 100644 --- a/runtime/interpreter/interpreter_goto_table_impl.cc +++ b/runtime/interpreter/interpreter_goto_table_impl.cc @@ -245,7 +245,7 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* // If access checks are required then the dex-to-dex compiler and analysis of // whether the class has final fields hasn't been performed. Conservatively // perform the memory barrier now. - ANDROID_MEMBAR_STORE(); + QuasiAtomic::MembarStoreLoad(); } if (UNLIKELY(self->TestAllFlags())) { CheckSuspend(self); @@ -261,7 +261,7 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(RETURN_VOID_BARRIER) { - ANDROID_MEMBAR_STORE(); + QuasiAtomic::MembarStoreLoad(); JValue result; if (UNLIKELY(self->TestAllFlags())) { CheckSuspend(self); diff --git a/runtime/interpreter/interpreter_switch_impl.cc b/runtime/interpreter/interpreter_switch_impl.cc index 675095f442d..75041eaa169 100644 --- a/runtime/interpreter/interpreter_switch_impl.cc +++ b/runtime/interpreter/interpreter_switch_impl.cc @@ -169,7 +169,7 @@ JValue ExecuteSwitchImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem // If access checks are required then the dex-to-dex compiler and analysis of // whether the class has final fields hasn't been performed. Conservatively // perform the memory barrier now. - ANDROID_MEMBAR_STORE(); + QuasiAtomic::MembarStoreLoad(); } if (UNLIKELY(self->TestAllFlags())) { CheckSuspend(self); @@ -183,7 +183,7 @@ JValue ExecuteSwitchImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem } case Instruction::RETURN_VOID_BARRIER: { PREAMBLE(); - ANDROID_MEMBAR_STORE(); + QuasiAtomic::MembarStoreLoad(); JValue result; if (UNLIKELY(self->TestAllFlags())) { CheckSuspend(self); diff --git a/runtime/mirror/object-inl.h b/runtime/mirror/object-inl.h index 7ac2c8c083a..9161bc578c6 100644 --- a/runtime/mirror/object-inl.h +++ b/runtime/mirror/object-inl.h @@ -253,11 +253,40 @@ inline size_t Object::SizeOf() const { return result; } +inline uint32_t Object::GetField32(MemberOffset field_offset, bool is_volatile) const { + VerifyObject(this); + const byte* raw_addr = reinterpret_cast(this) + field_offset.Int32Value(); + const int32_t* word_addr = reinterpret_cast(raw_addr); + if (UNLIKELY(is_volatile)) { + int32_t result = *(reinterpret_cast(const_cast(word_addr))); + QuasiAtomic::MembarLoadLoad(); + return result; + } else { + return *word_addr; + } +} + +inline void Object::SetField32(MemberOffset field_offset, uint32_t new_value, bool is_volatile, + bool this_is_valid) { + if (this_is_valid) { + VerifyObject(this); + } + byte* raw_addr = reinterpret_cast(this) + field_offset.Int32Value(); + uint32_t* word_addr = reinterpret_cast(raw_addr); + if (UNLIKELY(is_volatile)) { + QuasiAtomic::MembarStoreStore(); // Ensure this store occurs after others in the queue. + *word_addr = new_value; + QuasiAtomic::MembarStoreLoad(); // Ensure this store occurs before any loads. + } else { + *word_addr = new_value; + } +} + inline bool Object::CasField32(MemberOffset field_offset, uint32_t old_value, uint32_t new_value) { VerifyObject(this); byte* raw_addr = reinterpret_cast(this) + field_offset.Int32Value(); - int32_t* addr = reinterpret_cast(raw_addr); - return android_atomic_release_cas(old_value, new_value, addr) == 0; + volatile uint32_t* addr = reinterpret_cast(raw_addr); + return __sync_bool_compare_and_swap(addr, old_value, new_value); } inline uint64_t Object::GetField64(MemberOffset field_offset, bool is_volatile) const { @@ -266,7 +295,7 @@ inline uint64_t Object::GetField64(MemberOffset field_offset, bool is_volatile) const int64_t* addr = reinterpret_cast(raw_addr); if (UNLIKELY(is_volatile)) { uint64_t result = QuasiAtomic::Read64(addr); - ANDROID_MEMBAR_FULL(); + QuasiAtomic::MembarLoadLoad(); return result; } else { return *addr; @@ -278,9 +307,13 @@ inline void Object::SetField64(MemberOffset field_offset, uint64_t new_value, bo byte* raw_addr = reinterpret_cast(this) + field_offset.Int32Value(); int64_t* addr = reinterpret_cast(raw_addr); if (UNLIKELY(is_volatile)) { - ANDROID_MEMBAR_STORE(); + QuasiAtomic::MembarStoreStore(); // Ensure this store occurs after others in the queue. QuasiAtomic::Write64(addr, new_value); - // Post-store barrier not required due to use of atomic op or mutex. + if (!QuasiAtomic::LongAtomicsUseMutexes()) { + QuasiAtomic::MembarStoreLoad(); // Ensure this store occurs before any loads. + } else { + // Fence from from mutex is enough. + } } else { *addr = new_value; } diff --git a/runtime/mirror/object.cc b/runtime/mirror/object.cc index 008a17356fe..bdb3250d57d 100644 --- a/runtime/mirror/object.cc +++ b/runtime/mirror/object.cc @@ -89,10 +89,10 @@ int32_t Object::GenerateIdentityHashCode() { static AtomicInteger seed(987654321 + std::time(nullptr)); int32_t expected_value, new_value; do { - expected_value = static_cast(seed.load()); + expected_value = static_cast(seed.Load()); new_value = expected_value * 1103515245 + 12345; } while ((expected_value & LockWord::kHashMask) == 0 || - !seed.compare_and_swap(expected_value, new_value)); + !seed.CompareAndSwap(expected_value, new_value)); return expected_value & LockWord::kHashMask; } diff --git a/runtime/mirror/object.h b/runtime/mirror/object.h index fe89b7ee9e2..058aee7948b 100644 --- a/runtime/mirror/object.h +++ b/runtime/mirror/object.h @@ -187,37 +187,10 @@ class MANAGED Object { return reinterpret_cast(reinterpret_cast(this) + field_offset.Int32Value()); } - uint32_t GetField32(MemberOffset field_offset, bool is_volatile) const { - VerifyObject(this); - const byte* raw_addr = reinterpret_cast(this) + field_offset.Int32Value(); - const int32_t* word_addr = reinterpret_cast(raw_addr); - if (UNLIKELY(is_volatile)) { - return android_atomic_acquire_load(word_addr); - } else { - return *word_addr; - } - } + uint32_t GetField32(MemberOffset field_offset, bool is_volatile) const; void SetField32(MemberOffset field_offset, uint32_t new_value, bool is_volatile, - bool this_is_valid = true) { - if (this_is_valid) { - VerifyObject(this); - } - byte* raw_addr = reinterpret_cast(this) + field_offset.Int32Value(); - uint32_t* word_addr = reinterpret_cast(raw_addr); - if (UNLIKELY(is_volatile)) { - /* - * TODO: add an android_atomic_synchronization_store() function and - * use it in the 32-bit volatile set handlers. On some platforms we - * can use a fast atomic instruction and avoid the barriers. - */ - ANDROID_MEMBAR_STORE(); - *word_addr = new_value; - ANDROID_MEMBAR_FULL(); - } else { - *word_addr = new_value; - } - } + bool this_is_valid = true); bool CasField32(MemberOffset field_offset, uint32_t old_value, uint32_t new_value); diff --git a/runtime/monitor.cc b/runtime/monitor.cc index ef9a9cee87e..41866935af0 100644 --- a/runtime/monitor.cc +++ b/runtime/monitor.cc @@ -98,12 +98,12 @@ Monitor::Monitor(Thread* owner, mirror::Object* obj, int32_t hash_code) int32_t Monitor::GetHashCode() { while (!HasHashCode()) { - if (hash_code_.compare_and_swap(0, mirror::Object::GenerateIdentityHashCode())) { + if (hash_code_.CompareAndSwap(0, mirror::Object::GenerateIdentityHashCode())) { break; } } DCHECK(HasHashCode()); - return hash_code_.load(); + return hash_code_.Load(); } bool Monitor::Install(Thread* self) { @@ -660,6 +660,7 @@ void Monitor::MonitorEnter(Thread* self, mirror::Object* obj) { case LockWord::kUnlocked: { LockWord thin_locked(LockWord::FromThinLockId(thread_id, 0)); if (sirt_obj->CasLockWord(lock_word, thin_locked)) { + QuasiAtomic::MembarLoadLoad(); return; // Success! } continue; // Go again. diff --git a/runtime/monitor.h b/runtime/monitor.h index bfd854572ae..16e94109570 100644 --- a/runtime/monitor.h +++ b/runtime/monitor.h @@ -105,7 +105,7 @@ class Monitor { bool IsLocked() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); bool HasHashCode() const { - return hash_code_.load() != 0; + return hash_code_.Load() != 0; } static void InflateThinLocked(Thread* self, SirtRef& obj, LockWord lock_word, diff --git a/runtime/native/java_lang_reflect_Field.cc b/runtime/native/java_lang_reflect_Field.cc index 553aeb8f12a..269a4a32b3f 100644 --- a/runtime/native/java_lang_reflect_Field.cc +++ b/runtime/native/java_lang_reflect_Field.cc @@ -222,7 +222,7 @@ static void SetFieldValue(ScopedFastNativeObjectAccess& soa, mirror::Object* o, // Special handling for final fields on SMP systems. // We need a store/store barrier here (JMM requirement). if (f->IsFinal()) { - ANDROID_MEMBAR_STORE(); + QuasiAtomic::MembarStoreLoad(); } } diff --git a/runtime/native/sun_misc_Unsafe.cc b/runtime/native/sun_misc_Unsafe.cc index 2c6d2810b1e..b5fc7e7be5d 100644 --- a/runtime/native/sun_misc_Unsafe.cc +++ b/runtime/native/sun_misc_Unsafe.cc @@ -86,7 +86,7 @@ static void Unsafe_putIntVolatile(JNIEnv* env, jobject, jobject javaObj, jlong o static void Unsafe_putOrderedInt(JNIEnv* env, jobject, jobject javaObj, jlong offset, jint newValue) { ScopedFastNativeObjectAccess soa(env); mirror::Object* obj = soa.Decode(javaObj); - ANDROID_MEMBAR_STORE(); + QuasiAtomic::MembarStoreStore(); obj->SetField32(MemberOffset(offset), newValue, false); } @@ -117,7 +117,7 @@ static void Unsafe_putLongVolatile(JNIEnv* env, jobject, jobject javaObj, jlong static void Unsafe_putOrderedLong(JNIEnv* env, jobject, jobject javaObj, jlong offset, jlong newValue) { ScopedFastNativeObjectAccess soa(env); mirror::Object* obj = soa.Decode(javaObj); - ANDROID_MEMBAR_STORE(); + QuasiAtomic::MembarStoreStore(); obj->SetField64(MemberOffset(offset), newValue, false); } @@ -153,7 +153,7 @@ static void Unsafe_putOrderedObject(JNIEnv* env, jobject, jobject javaObj, jlong ScopedFastNativeObjectAccess soa(env); mirror::Object* obj = soa.Decode(javaObj); mirror::Object* newValue = soa.Decode(javaNewValue); - ANDROID_MEMBAR_STORE(); + QuasiAtomic::MembarStoreStore(); obj->SetFieldObject(MemberOffset(offset), newValue, false); } diff --git a/runtime/thread_pool_test.cc b/runtime/thread_pool_test.cc index 1b2236152fc..2029d4b2d79 100644 --- a/runtime/thread_pool_test.cc +++ b/runtime/thread_pool_test.cc @@ -94,7 +94,7 @@ TEST_F(ThreadPoolTest, StopStart) { EXPECT_EQ(0, bad_count); // Allow tasks to finish up and delete themselves. thread_pool.StartWorkers(self); - while (count.load() != num_tasks && bad_count.load() != 1) { + while (count.Load() != num_tasks && bad_count.Load() != 1) { usleep(200); } thread_pool.StopWorkers(self); From 58af1f9385742f70aca4fcb5e13aba53b8be2ef4 Mon Sep 17 00:00:00 2001 From: Vladimir Marko Date: Thu, 19 Dec 2013 13:31:15 +0000 Subject: [PATCH 0317/2402] Clean up usage of carry flag condition codes. On X86, kCondUlt and kCondUge are bound to CS and CC, respectively, while on ARM it's the other way around. The explicit binding in ConditionCode was wrong and misleading and could lead to subtle bugs. Therefore, we detach those constants and clean up usage. The CS and CC conditions are now effectively unused but we keep them around as they may eventually be useful. And some minor cleanup and comments. Change-Id: Ic5ed81d86b6c7f9392dd8fe9474b3ff718fee595 --- compiler/dex/compiler_enums.h | 8 ++--- compiler/dex/quick/arm/call_arm.cc | 2 +- compiler/dex/quick/arm/fp_arm.cc | 2 +- compiler/dex/quick/arm/int_arm.cc | 52 ++++++++++----------------- compiler/dex/quick/arm/target_arm.cc | 2 ++ compiler/dex/quick/arm/utility_arm.cc | 3 ++ compiler/dex/quick/gen_invoke.cc | 4 +-- compiler/dex/quick/mips/call_mips.cc | 2 +- compiler/dex/quick/mips/int_mips.cc | 16 ++++----- compiler/dex/quick/x86/fp_x86.cc | 4 +-- compiler/dex/quick/x86/int_x86.cc | 2 ++ 11 files changed, 43 insertions(+), 54 deletions(-) diff --git a/compiler/dex/compiler_enums.h b/compiler/dex/compiler_enums.h index 35d04ae137f..eb9ae06a3c3 100644 --- a/compiler/dex/compiler_enums.h +++ b/compiler/dex/compiler_enums.h @@ -203,10 +203,10 @@ std::ostream& operator<<(std::ostream& os, const OpKind& kind); enum ConditionCode { kCondEq, // equal kCondNe, // not equal - kCondCs, // carry set (unsigned less than) - kCondUlt = kCondCs, - kCondCc, // carry clear (unsigned greater than or same) - kCondUge = kCondCc, + kCondCs, // carry set + kCondCc, // carry clear + kCondUlt, // unsigned less than + kCondUge, // unsigned greater than or same kCondMi, // minus kCondPl, // plus, positive or zero kCondVs, // overflow diff --git a/compiler/dex/quick/arm/call_arm.cc b/compiler/dex/quick/arm/call_arm.cc index 8226b24fe91..661050ffe94 100644 --- a/compiler/dex/quick/arm/call_arm.cc +++ b/compiler/dex/quick/arm/call_arm.cc @@ -608,7 +608,7 @@ void ArmMir2Lir::GenEntrySequence(RegLocation* ArgLocs, RegLocation rl_method) { } if (!skip_overflow_check) { OpRegRegImm(kOpSub, rARM_LR, rARM_SP, frame_size_ - (spill_count * 4)); - GenRegRegCheck(kCondCc, rARM_LR, r12, kThrowStackOverflow); + GenRegRegCheck(kCondUlt, rARM_LR, r12, kThrowStackOverflow); OpRegCopy(rARM_SP, rARM_LR); // Establish stack } else { OpRegImm(kOpSub, rARM_SP, frame_size_ - (spill_count * 4)); diff --git a/compiler/dex/quick/arm/fp_arm.cc b/compiler/dex/quick/arm/fp_arm.cc index dc2e0d0c794..8af9cdd6f5a 100644 --- a/compiler/dex/quick/arm/fp_arm.cc +++ b/compiler/dex/quick/arm/fp_arm.cc @@ -216,7 +216,7 @@ void ArmMir2Lir::GenFusedFPCmpBranch(BasicBlock* bb, MIR* mir, bool gt_bias, break; case kCondGe: if (gt_bias) { - ccode = kCondCs; + ccode = kCondUge; } break; default: diff --git a/compiler/dex/quick/arm/int_arm.cc b/compiler/dex/quick/arm/int_arm.cc index e839fe5c5d5..0ef12b0d407 100644 --- a/compiler/dex/quick/arm/int_arm.cc +++ b/compiler/dex/quick/arm/int_arm.cc @@ -128,31 +128,23 @@ void ArmMir2Lir::GenFusedLongCmpImmBranch(BasicBlock* bb, RegLocation rl_src1, int32_t low_reg = rl_src1.low_reg; int32_t high_reg = rl_src1.high_reg; + if (val == 0 && (ccode == kCondEq || ccode == kCondNe)) { + int t_reg = AllocTemp(); + NewLIR4(kThumb2OrrRRRs, t_reg, low_reg, high_reg, 0); + FreeTemp(t_reg); + OpCondBranch(ccode, taken); + return; + } + switch (ccode) { case kCondEq: case kCondNe: - LIR* target; - ConditionCode condition; - if (ccode == kCondEq) { - target = not_taken; - condition = kCondEq; - } else { - target = taken; - condition = kCondNe; - } - if (val == 0) { - int t_reg = AllocTemp(); - NewLIR4(kThumb2OrrRRRs, t_reg, low_reg, high_reg, 0); - FreeTemp(t_reg); - OpCondBranch(condition, taken); - return; - } - OpCmpImmBranch(kCondNe, high_reg, val_hi, target); + OpCmpImmBranch(kCondNe, high_reg, val_hi, (ccode == kCondEq) ? not_taken : taken); break; case kCondLt: OpCmpImmBranch(kCondLt, high_reg, val_hi, taken); OpCmpImmBranch(kCondGt, high_reg, val_hi, not_taken); - ccode = kCondCc; + ccode = kCondUlt; break; case kCondLe: OpCmpImmBranch(kCondLt, high_reg, val_hi, taken); @@ -167,7 +159,7 @@ void ArmMir2Lir::GenFusedLongCmpImmBranch(BasicBlock* bb, RegLocation rl_src1, case kCondGe: OpCmpImmBranch(kCondGt, high_reg, val_hi, taken); OpCmpImmBranch(kCondLt, high_reg, val_hi, not_taken); - ccode = kCondCs; + ccode = kCondUge; break; default: LOG(FATAL) << "Unexpected ccode: " << ccode; @@ -187,7 +179,7 @@ void ArmMir2Lir::GenSelect(BasicBlock* bb, MIR* mir) { rl_result = EvalLoc(rl_dest, kCoreReg, true); if ((true_val == 1) && (false_val == 0)) { OpRegRegImm(kOpRsub, rl_result.low_reg, rl_src.low_reg, 1); - OpIT(kCondCc, ""); + OpIT(kCondUlt, ""); LoadConstant(rl_result.low_reg, 0); GenBarrier(); // Add a scheduling barrier to keep the IT shadow intact } else if (InexpensiveConstantInt(true_val) && InexpensiveConstantInt(false_val)) { @@ -238,9 +230,7 @@ void ArmMir2Lir::GenFusedLongCmpBranch(BasicBlock* bb, MIR* mir) { // Normalize such that if either operand is constant, src2 will be constant. ConditionCode ccode = static_cast(mir->dalvikInsn.arg[0]); if (rl_src1.is_const) { - RegLocation rl_temp = rl_src1; - rl_src1 = rl_src2; - rl_src2 = rl_temp; + std::swap(rl_src1, rl_src2); ccode = FlipComparisonOrder(ccode); } if (rl_src2.is_const) { @@ -268,7 +258,7 @@ void ArmMir2Lir::GenFusedLongCmpBranch(BasicBlock* bb, MIR* mir) { case kCondLt: OpCondBranch(kCondLt, taken); OpCondBranch(kCondGt, not_taken); - ccode = kCondCc; + ccode = kCondUlt; break; case kCondLe: OpCondBranch(kCondLt, taken); @@ -283,7 +273,7 @@ void ArmMir2Lir::GenFusedLongCmpBranch(BasicBlock* bb, MIR* mir) { case kCondGe: OpCondBranch(kCondGt, taken); OpCondBranch(kCondLt, not_taken); - ccode = kCondCs; + ccode = kCondUge; break; default: LOG(FATAL) << "Unexpected ccode: " << ccode; @@ -701,7 +691,7 @@ bool ArmMir2Lir::GenInlinedCas(CallInfo* info, bool is_long, bool is_object) { RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true); OpRegRegImm(kOpRsub, rl_result.low_reg, r_tmp, 1); DCHECK(last_lir_insn_->u.m.def_mask & ENCODE_CCODE); - OpIT(kCondCc, ""); + OpIT(kCondUlt, ""); LoadConstant(rl_result.low_reg, 0); /* cc */ FreeTemp(r_tmp); // Now unneeded. @@ -981,9 +971,7 @@ void ArmMir2Lir::GenArrayGet(int opt_flags, OpSize size, RegLocation rl_array, rl_result = EvalLoc(rl_dest, reg_class, true); if (needs_range_check) { - // TODO: change kCondCS to a more meaningful name, is the sense of - // carry-set/clear flipped? - GenRegRegCheck(kCondCs, rl_index.low_reg, reg_len, kThrowArrayBounds); + GenRegRegCheck(kCondUge, rl_index.low_reg, reg_len, kThrowArrayBounds); FreeTemp(reg_len); } LoadBaseIndexed(reg_ptr, rl_index.low_reg, rl_result.low_reg, scale, size); @@ -1072,7 +1060,7 @@ void ArmMir2Lir::GenArrayPut(int opt_flags, OpSize size, RegLocation rl_array, OpRegRegImm(kOpAdd, reg_ptr, rl_array.low_reg, data_offset); rl_src = LoadValue(rl_src, reg_class); if (needs_range_check) { - GenRegRegCheck(kCondCs, rl_index.low_reg, reg_len, kThrowArrayBounds); + GenRegRegCheck(kCondUge, rl_index.low_reg, reg_len, kThrowArrayBounds); FreeTemp(reg_len); } StoreBaseIndexed(reg_ptr, rl_index.low_reg, rl_src.low_reg, @@ -1172,9 +1160,7 @@ void ArmMir2Lir::GenArithImmOpLong(Instruction::Code opcode, // Normalize if (!rl_src2.is_const) { DCHECK(rl_src1.is_const); - RegLocation rl_temp = rl_src1; - rl_src1 = rl_src2; - rl_src2 = rl_temp; + std::swap(rl_src1, rl_src2); } } if (BadOverlap(rl_src1, rl_dest)) { diff --git a/compiler/dex/quick/arm/target_arm.cc b/compiler/dex/quick/arm/target_arm.cc index 48c9af5a166..d80ae3bc235 100644 --- a/compiler/dex/quick/arm/target_arm.cc +++ b/compiler/dex/quick/arm/target_arm.cc @@ -207,6 +207,8 @@ ArmConditionCode ArmMir2Lir::ArmConditionEncoding(ConditionCode ccode) { case kCondNe: res = kArmCondNe; break; case kCondCs: res = kArmCondCs; break; case kCondCc: res = kArmCondCc; break; + case kCondUlt: res = kArmCondCc; break; + case kCondUge: res = kArmCondCs; break; case kCondMi: res = kArmCondMi; break; case kCondPl: res = kArmCondPl; break; case kCondVs: res = kArmCondVs; break; diff --git a/compiler/dex/quick/arm/utility_arm.cc b/compiler/dex/quick/arm/utility_arm.cc index 8a8b168ccc1..fa05d6c5a80 100644 --- a/compiler/dex/quick/arm/utility_arm.cc +++ b/compiler/dex/quick/arm/utility_arm.cc @@ -210,6 +210,9 @@ LIR* ArmMir2Lir::OpUnconditionalBranch(LIR* target) { } LIR* ArmMir2Lir::OpCondBranch(ConditionCode cc, LIR* target) { + // This is kThumb2BCond instead of kThumbBCond for performance reasons. The assembly + // time required for a new pass after kThumbBCond is fixed up to kThumb2BCond is + // substantial. LIR* branch = NewLIR2(kThumb2BCond, 0 /* offset to be patched */, ArmConditionEncoding(cc)); branch->target = target; diff --git a/compiler/dex/quick/gen_invoke.cc b/compiler/dex/quick/gen_invoke.cc index 82a1932fe16..d942a24a188 100644 --- a/compiler/dex/quick/gen_invoke.cc +++ b/compiler/dex/quick/gen_invoke.cc @@ -896,7 +896,7 @@ bool Mir2Lir::GenInlinedCharAt(CallInfo* info) { intrinsic_launchpads_.Insert(launch_pad); OpRegReg(kOpCmp, rl_idx.low_reg, reg_max); FreeTemp(reg_max); - OpCondBranch(kCondCs, launch_pad); + OpCondBranch(kCondUge, launch_pad); } } else { if (range_check) { @@ -907,7 +907,7 @@ bool Mir2Lir::GenInlinedCharAt(CallInfo* info) { intrinsic_launchpads_.Insert(launch_pad); OpRegReg(kOpCmp, rl_idx.low_reg, reg_max); FreeTemp(reg_max); - OpCondBranch(kCondCc, launch_pad); + OpCondBranch(kCondUge, launch_pad); } reg_off = AllocTemp(); reg_ptr = AllocTemp(); diff --git a/compiler/dex/quick/mips/call_mips.cc b/compiler/dex/quick/mips/call_mips.cc index 14f49aa3677..2e385a380a8 100644 --- a/compiler/dex/quick/mips/call_mips.cc +++ b/compiler/dex/quick/mips/call_mips.cc @@ -318,7 +318,7 @@ void MipsMir2Lir::GenEntrySequence(RegLocation* ArgLocs, RegLocation rl_method) DCHECK_EQ(num_fp_spills_, 0); if (!skip_overflow_check) { OpRegRegImm(kOpSub, new_sp, rMIPS_SP, frame_size_ - (spill_count * 4)); - GenRegRegCheck(kCondCc, new_sp, check_reg, kThrowStackOverflow); + GenRegRegCheck(kCondUlt, new_sp, check_reg, kThrowStackOverflow); OpRegCopy(rMIPS_SP, new_sp); // Establish stack } else { OpRegImm(kOpSub, rMIPS_SP, frame_size_ - (spill_count * 4)); diff --git a/compiler/dex/quick/mips/int_mips.cc b/compiler/dex/quick/mips/int_mips.cc index dfff26062c2..180d56c782e 100644 --- a/compiler/dex/quick/mips/int_mips.cc +++ b/compiler/dex/quick/mips/int_mips.cc @@ -77,11 +77,11 @@ LIR* MipsMir2Lir::OpCmpBranch(ConditionCode cond, int src1, int src2, br_op = kMipsBne; cmp_zero = true; break; - case kCondCc: + case kCondUlt: slt_op = kMipsSltu; br_op = kMipsBnez; break; - case kCondCs: + case kCondUge: slt_op = kMipsSltu; br_op = kMipsBeqz; break; @@ -485,9 +485,7 @@ void MipsMir2Lir::GenArrayGet(int opt_flags, OpSize size, RegLocation rl_array, rl_result = EvalLoc(rl_dest, reg_class, true); if (needs_range_check) { - // TODO: change kCondCS to a more meaningful name, is the sense of - // carry-set/clear flipped? - GenRegRegCheck(kCondCs, rl_index.low_reg, reg_len, kThrowArrayBounds); + GenRegRegCheck(kCondUge, rl_index.low_reg, reg_len, kThrowArrayBounds); FreeTemp(reg_len); } LoadBaseDispWide(reg_ptr, 0, rl_result.low_reg, rl_result.high_reg, INVALID_SREG); @@ -498,9 +496,7 @@ void MipsMir2Lir::GenArrayGet(int opt_flags, OpSize size, RegLocation rl_array, rl_result = EvalLoc(rl_dest, reg_class, true); if (needs_range_check) { - // TODO: change kCondCS to a more meaningful name, is the sense of - // carry-set/clear flipped? - GenRegRegCheck(kCondCs, rl_index.low_reg, reg_len, kThrowArrayBounds); + GenRegRegCheck(kCondUge, rl_index.low_reg, reg_len, kThrowArrayBounds); FreeTemp(reg_len); } LoadBaseIndexed(reg_ptr, rl_index.low_reg, rl_result.low_reg, scale, size); @@ -566,7 +562,7 @@ void MipsMir2Lir::GenArrayPut(int opt_flags, OpSize size, RegLocation rl_array, rl_src = LoadValueWide(rl_src, reg_class); if (needs_range_check) { - GenRegRegCheck(kCondCs, rl_index.low_reg, reg_len, kThrowArrayBounds); + GenRegRegCheck(kCondUge, rl_index.low_reg, reg_len, kThrowArrayBounds); FreeTemp(reg_len); } @@ -574,7 +570,7 @@ void MipsMir2Lir::GenArrayPut(int opt_flags, OpSize size, RegLocation rl_array, } else { rl_src = LoadValue(rl_src, reg_class); if (needs_range_check) { - GenRegRegCheck(kCondCs, rl_index.low_reg, reg_len, kThrowArrayBounds); + GenRegRegCheck(kCondUge, rl_index.low_reg, reg_len, kThrowArrayBounds); FreeTemp(reg_len); } StoreBaseIndexed(reg_ptr, rl_index.low_reg, rl_src.low_reg, diff --git a/compiler/dex/quick/x86/fp_x86.cc b/compiler/dex/quick/x86/fp_x86.cc index 1731703a3f4..62724980657 100644 --- a/compiler/dex/quick/x86/fp_x86.cc +++ b/compiler/dex/quick/x86/fp_x86.cc @@ -322,7 +322,7 @@ void X86Mir2Lir::GenFusedFPCmpBranch(BasicBlock* bb, MIR* mir, bool gt_bias, branch = NewLIR2(kX86Jcc8, 0, kX86CondPE); branch->target = not_taken; } - ccode = kCondCs; + ccode = kCondUlt; break; case kCondLe: if (gt_bias) { @@ -343,7 +343,7 @@ void X86Mir2Lir::GenFusedFPCmpBranch(BasicBlock* bb, MIR* mir, bool gt_bias, branch = NewLIR2(kX86Jcc8, 0, kX86CondPE); branch->target = taken; } - ccode = kCondCc; + ccode = kCondUge; break; default: LOG(FATAL) << "Unexpected ccode: " << ccode; diff --git a/compiler/dex/quick/x86/int_x86.cc b/compiler/dex/quick/x86/int_x86.cc index 56cf7e96e3f..2c646d489f2 100644 --- a/compiler/dex/quick/x86/int_x86.cc +++ b/compiler/dex/quick/x86/int_x86.cc @@ -83,6 +83,8 @@ X86ConditionCode X86ConditionEncoding(ConditionCode cond) { case kCondNe: return kX86CondNe; case kCondCs: return kX86CondC; case kCondCc: return kX86CondNc; + case kCondUlt: return kX86CondC; + case kCondUge: return kX86CondNc; case kCondMi: return kX86CondS; case kCondPl: return kX86CondNs; case kCondVs: return kX86CondO; From 849600bb5cfc02bf5ab4aa9a810667ebd3b53328 Mon Sep 17 00:00:00 2001 From: Sebastien Hertz Date: Fri, 20 Dec 2013 10:28:08 +0100 Subject: [PATCH 0318/2402] Use imprecise constants at compilation time. During veriifcation, we create constant types for the following instructions: const/4, const/16, const and const/high16. We used to create "precise" constant types for each constant we process in the method being verified. Though precise constants are only useful for deoptimization which happens at runtime. This CL now creates "imprecise" constant types at compilation time. Since it reduces the number of constant types we create during verification, it should also reduce the amount of time spent in verification at compilation time. Bug: 12167380 Bug: 12126841 Change-Id: I70522c4133a74a533fc2d2cb8d4f49888e590828 --- runtime/verifier/method_verifier.cc | 49 +++++++++++++++++++++++++---- runtime/verifier/method_verifier.h | 2 ++ runtime/verifier/reg_type_cache.cc | 19 +++++++++++ runtime/verifier/reg_type_cache.h | 8 ++++- 4 files changed, 71 insertions(+), 7 deletions(-) diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc index f03cdcdcd40..9a2de47296c 100644 --- a/runtime/verifier/method_verifier.cc +++ b/runtime/verifier/method_verifier.cc @@ -1432,6 +1432,9 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { UniquePtr branch_line; UniquePtr fallthrough_line; + // We need precise constant types only for deoptimization which happens at runtime. + const bool need_precise_constant = !Runtime::Current()->IsCompiler(); + switch (inst->Opcode()) { case Instruction::NOP: /* @@ -1582,22 +1585,28 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { /* could be boolean, int, float, or a null reference */ case Instruction::CONST_4: { int32_t val = static_cast(inst->VRegB_11n() << 28) >> 28; - work_line_->SetRegisterType(inst->VRegA_11n(), reg_types_.FromCat1Const(val, true)); + work_line_->SetRegisterType(inst->VRegA_11n(), + DetermineCat1Constant(val, need_precise_constant)); break; } case Instruction::CONST_16: { int16_t val = static_cast(inst->VRegB_21s()); - work_line_->SetRegisterType(inst->VRegA_21s(), reg_types_.FromCat1Const(val, true)); + work_line_->SetRegisterType(inst->VRegA_21s(), + DetermineCat1Constant(val, need_precise_constant)); break; } - case Instruction::CONST: + case Instruction::CONST: { + int32_t val = inst->VRegB_31i(); work_line_->SetRegisterType(inst->VRegA_31i(), - reg_types_.FromCat1Const(inst->VRegB_31i(), true)); + DetermineCat1Constant(val, need_precise_constant)); break; - case Instruction::CONST_HIGH16: + } + case Instruction::CONST_HIGH16: { + int32_t val = static_cast(inst->VRegB_21h() << 16); work_line_->SetRegisterType(inst->VRegA_21h(), - reg_types_.FromCat1Const(inst->VRegB_21h() << 16, true)); + DetermineCat1Constant(val, need_precise_constant)); break; + } /* could be long or double; resolved upon use */ case Instruction::CONST_WIDE_16: { int64_t val = static_cast(inst->VRegB_21s()); @@ -3928,6 +3937,34 @@ std::vector MethodVerifier::DescribeVRegs(uint32_t dex_pc) { return result; } +const RegType& MethodVerifier::DetermineCat1Constant(int32_t value, bool precise) { + if (precise) { + // Precise constant type. + return reg_types_.FromCat1Const(value, true); + } else { + // Imprecise constant type. + if (value < -32768) { + return reg_types_.IntConstant(); + } else if (value < -128) { + return reg_types_.ShortConstant(); + } else if (value < 0) { + return reg_types_.ByteConstant(); + } else if (value == 0) { + return reg_types_.Zero(); + } else if (value == 1) { + return reg_types_.One(); + } else if (value < 128) { + return reg_types_.PosByteConstant(); + } else if (value < 32768) { + return reg_types_.PosShortConstant(); + } else if (value < 65536) { + return reg_types_.CharConstant(); + } else { + return reg_types_.IntConstant(); + } + } +} + void MethodVerifier::Init() { art::verifier::RegTypeCache::Init(); } diff --git a/runtime/verifier/method_verifier.h b/runtime/verifier/method_verifier.h index ac36a7ed21e..053cee55cad 100644 --- a/runtime/verifier/method_verifier.h +++ b/runtime/verifier/method_verifier.h @@ -610,6 +610,8 @@ class MethodVerifier { InstructionFlags* CurrentInsnFlags(); + const RegType& DetermineCat1Constant(int32_t value, bool precise) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); RegTypeCache reg_types_; diff --git a/runtime/verifier/reg_type_cache.cc b/runtime/verifier/reg_type_cache.cc index 3d244144938..c8a03d69652 100644 --- a/runtime/verifier/reg_type_cache.cc +++ b/runtime/verifier/reg_type_cache.cc @@ -438,6 +438,13 @@ const ImpreciseConstType& RegTypeCache::ByteConstant() { return *down_cast(&result); } +const ImpreciseConstType& RegTypeCache::CharConstant() { + int32_t jchar_max = static_cast(std::numeric_limits::max()); + const ConstantType& result = FromCat1Const(jchar_max, false); + DCHECK(result.IsImpreciseConstant()); + return *down_cast(&result); +} + const ImpreciseConstType& RegTypeCache::ShortConstant() { const ConstantType& result = FromCat1Const(std::numeric_limits::min(), false); DCHECK(result.IsImpreciseConstant()); @@ -450,6 +457,18 @@ const ImpreciseConstType& RegTypeCache::IntConstant() { return *down_cast(&result); } +const ImpreciseConstType& RegTypeCache::PosByteConstant() { + const ConstantType& result = FromCat1Const(std::numeric_limits::max(), false); + DCHECK(result.IsImpreciseConstant()); + return *down_cast(&result); +} + +const ImpreciseConstType& RegTypeCache::PosShortConstant() { + const ConstantType& result = FromCat1Const(std::numeric_limits::max(), false); + DCHECK(result.IsImpreciseConstant()); + return *down_cast(&result); +} + const UninitializedType& RegTypeCache::UninitializedThisArgument(const RegType& type) { UninitializedType* entry; const std::string& descriptor(type.GetDescriptor()); diff --git a/runtime/verifier/reg_type_cache.h b/runtime/verifier/reg_type_cache.h index a8116961259..41bc8c9c367 100644 --- a/runtime/verifier/reg_type_cache.h +++ b/runtime/verifier/reg_type_cache.h @@ -77,9 +77,12 @@ class RegTypeCache { SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { return From(NULL, "Ljava/lang/Throwable;", precise); } - const RegType& Zero() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + const ConstantType& Zero() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { return FromCat1Const(0, true); } + const ConstantType& One() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + return FromCat1Const(1, true); + } size_t GetCacheSize() { return entries_.size(); } @@ -133,8 +136,11 @@ class RegTypeCache { const RegType& FromUninitialized(const RegType& uninit_type) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); const ImpreciseConstType& ByteConstant() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + const ImpreciseConstType& CharConstant() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); const ImpreciseConstType& ShortConstant() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); const ImpreciseConstType& IntConstant() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + const ImpreciseConstType& PosByteConstant() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + const ImpreciseConstType& PosShortConstant() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); const RegType& GetComponentType(const RegType& array, mirror::ClassLoader* loader) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); void Dump(std::ostream& os) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); From 459f4dfae3c8732fd4303a6e4df2e351d7d54f14 Mon Sep 17 00:00:00 2001 From: Vladimir Marko Date: Fri, 20 Dec 2013 17:03:09 +0000 Subject: [PATCH 0319/2402] Fix build: style issue. Change-Id: I3407b9073776b2b40638491d9316111fa793e4ab --- compiler/dex/compiler_enums.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/dex/compiler_enums.h b/compiler/dex/compiler_enums.h index eb9ae06a3c3..5cc906fba90 100644 --- a/compiler/dex/compiler_enums.h +++ b/compiler/dex/compiler_enums.h @@ -205,8 +205,8 @@ enum ConditionCode { kCondNe, // not equal kCondCs, // carry set kCondCc, // carry clear - kCondUlt, // unsigned less than - kCondUge, // unsigned greater than or same + kCondUlt, // unsigned less than + kCondUge, // unsigned greater than or same kCondMi, // minus kCondPl, // plus, positive or zero kCondVs, // overflow From 090dd4489eeffb5f10051a5d9c1ed71b0a6bc4b9 Mon Sep 17 00:00:00 2001 From: Razvan A Lupusoru Date: Fri, 20 Dec 2013 14:35:03 -0800 Subject: [PATCH 0320/2402] Eliminate redundant x86 compare for GenDivZeroCheck For x86, the ALU operations on general purpose registers update the flags. Thus, when generating the zero check for divide/remainder operations, the compare is not needed. Change-Id: I07bfdf7d5491d3e3e9d98a932472d7f18d5b46d3 Signed-off-by: Razvan A Lupusoru --- compiler/dex/quick/mir_to_lir.h | 8 ++++++++ compiler/dex/quick/x86/int_x86.cc | 10 +++++++++- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/compiler/dex/quick/mir_to_lir.h b/compiler/dex/quick/mir_to_lir.h index 2a25f2f9100..9dc35c6f788 100644 --- a/compiler/dex/quick/mir_to_lir.h +++ b/compiler/dex/quick/mir_to_lir.h @@ -682,7 +682,15 @@ class Mir2Lir : public Backend { bool is_div) = 0; virtual void GenCmpLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2) = 0; + + /** + * @brief Used for generating code that throws ArithmeticException if both registers are zero. + * @details This is used for generating DivideByZero checks when divisor is held in two separate registers. + * @param reg_lo The register holding the lower 32-bits. + * @param reg_hi The register holding the upper 32-bits. + */ virtual void GenDivZeroCheck(int reg_lo, int reg_hi) = 0; + virtual void GenEntrySequence(RegLocation* ArgLocs, RegLocation rl_method) = 0; virtual void GenExitSequence() = 0; diff --git a/compiler/dex/quick/x86/int_x86.cc b/compiler/dex/quick/x86/int_x86.cc index 2c646d489f2..75eddd60ffa 100644 --- a/compiler/dex/quick/x86/int_x86.cc +++ b/compiler/dex/quick/x86/int_x86.cc @@ -454,9 +454,17 @@ void X86Mir2Lir::GenMultiplyByTwoBitMultiplier(RegLocation rl_src, } void X86Mir2Lir::GenDivZeroCheck(int reg_lo, int reg_hi) { + // We are not supposed to clobber either of the provided registers, so allocate + // a temporary to use for the check. int t_reg = AllocTemp(); + + // Doing an OR is a quick way to check if both registers are zero. This will set the flags. OpRegRegReg(kOpOr, t_reg, reg_lo, reg_hi); - GenImmedCheck(kCondEq, t_reg, 0, kThrowDivZero); + + // In case of zero, throw ArithmeticException. + GenCheck(kCondEq, kThrowDivZero); + + // The temp is no longer needed so free it at this time. FreeTemp(t_reg); } From 0859999b0ae32190cf8da2f2e409f7f10a0058dd Mon Sep 17 00:00:00 2001 From: Mathieu Chartier Date: Fri, 20 Dec 2013 17:17:55 -0800 Subject: [PATCH 0321/2402] Add stale weak global unit test to jni_internal_test. Prevents regressions related to: https://code.google.com/p/android/issues/detail?id=63929 Change-Id: I4a3dbb2765523eac47e308fbdefa9558a38d346d --- runtime/jni_internal_test.cc | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/runtime/jni_internal_test.cc b/runtime/jni_internal_test.cc index 26b18364cf6..224044728a4 100644 --- a/runtime/jni_internal_test.cc +++ b/runtime/jni_internal_test.cc @@ -1204,6 +1204,21 @@ TEST_F(JniInternalTest, GetObjectRefType) { // TODO: invoke a native method and test that its arguments are considered local references. } +TEST_F(JniInternalTest, StaleWeakGlobal) { + jclass java_lang_Class = env_->FindClass("java/lang/Class"); + ASSERT_TRUE(java_lang_Class != NULL); + jobjectArray local_ref = env_->NewObjectArray(1, java_lang_Class, NULL); + ASSERT_TRUE(local_ref != NULL); + jweak weak_global = env_->NewWeakGlobalRef(local_ref); + ASSERT_TRUE(weak_global != NULL); + env_->DeleteLocalRef(local_ref); + Runtime::Current()->GetHeap()->CollectGarbage(false); // GC should clear the weak global. + jobject new_global_ref = env_->NewGlobalRef(weak_global); + EXPECT_TRUE(new_global_ref == NULL); + jobject new_local_ref = env_->NewLocalRef(weak_global); + EXPECT_TRUE(new_local_ref == NULL); +} + TEST_F(JniInternalTest, NewStringUTF) { EXPECT_TRUE(env_->NewStringUTF(NULL) == NULL); jstring s; From 6e3cb66b3885c5b9ed31bf55e2d2f8594c4f840d Mon Sep 17 00:00:00 2001 From: Jean Christophe Beyler Date: Fri, 20 Dec 2013 15:47:52 -0800 Subject: [PATCH 0322/2402] DataflowIterator normalization The patch normalizes the data flow iterators. The reasoning behind it is to allow passing the base class around without knowing what underlying iterator is used. This will thus allow called functions to call the specified Next function. This feature will be required for future patches. - Made DataflowIterator a base class with an abstract Next function - Updated each derived class to use the same Next function signature - Added comments and doxygen comments Change-Id: I3b9bce6326675575172f0ebd3681369d40d55661 Signed-off-by: Jean Christophe Beyler --- compiler/dex/dataflow_iterator-inl.h | 59 ++++++-- compiler/dex/dataflow_iterator.h | 207 ++++++++++++++++++++++++--- 2 files changed, 234 insertions(+), 32 deletions(-) diff --git a/compiler/dex/dataflow_iterator-inl.h b/compiler/dex/dataflow_iterator-inl.h index 64e5fa64e34..0ca1a47f797 100644 --- a/compiler/dex/dataflow_iterator-inl.h +++ b/compiler/dex/dataflow_iterator-inl.h @@ -24,65 +24,100 @@ namespace art { // Single forward pass over the nodes. inline BasicBlock* DataflowIterator::ForwardSingleNext() { BasicBlock* res = NULL; + + // Are we not yet at the end? if (idx_ < end_idx_) { - BasicBlockId bb_id = block_id_list_->Get(idx_++); + // Get the next index. + BasicBlockId bb_id = block_id_list_->Get(idx_); res = mir_graph_->GetBasicBlock(bb_id); + idx_++; } + return res; } // Repeat full forward passes over all nodes until no change occurs during a complete pass. -inline BasicBlock* DataflowIterator::ForwardRepeatNext(bool had_change) { - changed_ |= had_change; +inline BasicBlock* DataflowIterator::ForwardRepeatNext() { BasicBlock* res = NULL; - if ((idx_ >= end_idx_) && changed_) { + + // Are we at the end and have we changed something? + if ((idx_ >= end_idx_) && changed_ == true) { + // Reset the index. idx_ = start_idx_; repeats_++; changed_ = false; } + + // Are we not yet at the end? if (idx_ < end_idx_) { - BasicBlockId bb_id = block_id_list_->Get(idx_++); + // Get the BasicBlockId. + BasicBlockId bb_id = block_id_list_->Get(idx_); res = mir_graph_->GetBasicBlock(bb_id); + idx_++; } + return res; } // Single reverse pass over the nodes. inline BasicBlock* DataflowIterator::ReverseSingleNext() { BasicBlock* res = NULL; + + // Are we not yet at the end? if (idx_ >= 0) { - BasicBlockId bb_id = block_id_list_->Get(idx_--); + // Get the BasicBlockId. + BasicBlockId bb_id = block_id_list_->Get(idx_); res = mir_graph_->GetBasicBlock(bb_id); + idx_--; } + return res; } // Repeat full backwards passes over all nodes until no change occurs during a complete pass. -inline BasicBlock* DataflowIterator::ReverseRepeatNext(bool had_change) { - changed_ |= had_change; +inline BasicBlock* DataflowIterator::ReverseRepeatNext() { BasicBlock* res = NULL; + + // Are we done and we changed something during the last iteration? if ((idx_ < 0) && changed_) { + // Reset the index. idx_ = start_idx_; repeats_++; changed_ = false; } + + // Are we not yet done? if (idx_ >= 0) { - BasicBlockId bb_id = block_id_list_->Get(idx_--); + // Get the BasicBlockId. + BasicBlockId bb_id = block_id_list_->Get(idx_); res = mir_graph_->GetBasicBlock(bb_id); + idx_--; } + return res; } // AllNodes uses the existing GrowableArray iterator, and should be considered unordered. -inline BasicBlock* AllNodesIterator::Next() { +inline BasicBlock* AllNodesIterator::Next(bool had_change) { BasicBlock* res = NULL; + + // Suppose we want to keep looking. bool keep_looking = true; - while (keep_looking) { + + // Find the next BasicBlock. + while (keep_looking == true) { + // Get next BasicBlock. res = all_nodes_iterator_->Next(); - if ((res == NULL) || (!res->hidden)) { + + // Are we done or is the BasicBlock not hidden? + if ((res == NULL) || (res->hidden == false)) { keep_looking = false; } } + + // Update changed: if had_changed is true, we remember it for the whole iteration. + changed_ |= had_change; + return res; } diff --git a/compiler/dex/dataflow_iterator.h b/compiler/dex/dataflow_iterator.h index a0c1c123e7f..12f924e3216 100644 --- a/compiler/dex/dataflow_iterator.h +++ b/compiler/dex/dataflow_iterator.h @@ -34,12 +34,40 @@ namespace art { * the iterator that once it has finished walking through the block list it should reset and * do another full pass through the list. */ + /** + * @class DataflowIterator + * @brief The main iterator class, all other iterators derive of this one to define an iteration order. + */ class DataflowIterator { public: virtual ~DataflowIterator() {} + + /** + * @brief How many times have we repeated the iterator across the BasicBlocks? + * @return the number of iteration repetitions. + */ int32_t GetRepeatCount() { return repeats_; } + /** + * @brief Has the user of the iterator reported a change yet? + * @details Does not mean there was or not a change, it is only whether the user passed a true to the Next function call. + * @return whether the user of the iterator reported a change yet. + */ + int32_t GetChanged() { return changed_; } + + /** + * @brief Get the next BasicBlock depending on iteration order. + * @param had_change did the user of the iteration change the previous BasicBlock. + * @return the next BasicBlock following the iteration order, 0 if finished. + */ + virtual BasicBlock* Next(bool had_change = false) = 0; + protected: + /** + * @param mir_graph the MIRGraph we are interested in. + * @param start_idx the first index we want to iterate across. + * @param end_idx the last index we want to iterate (not included). + */ DataflowIterator(MIRGraph* mir_graph, int32_t start_idx, int32_t end_idx) : mir_graph_(mir_graph), start_idx_(start_idx), @@ -49,115 +77,254 @@ namespace art { repeats_(0), changed_(false) {} + /** + * @brief Get the next BasicBlock iterating forward. + * @return the next BasicBlock iterating forward. + */ virtual BasicBlock* ForwardSingleNext() ALWAYS_INLINE; + + /** + * @brief Get the next BasicBlock iterating backward. + * @return the next BasicBlock iterating backward. + */ virtual BasicBlock* ReverseSingleNext() ALWAYS_INLINE; - virtual BasicBlock* ForwardRepeatNext(bool had_change) ALWAYS_INLINE; - virtual BasicBlock* ReverseRepeatNext(bool had_change) ALWAYS_INLINE; + /** + * @brief Get the next BasicBlock iterating forward, restart if a BasicBlock was reported changed during the last iteration. + * @return the next BasicBlock iterating forward, with chance of repeating the iteration. + */ + virtual BasicBlock* ForwardRepeatNext() ALWAYS_INLINE; - MIRGraph* const mir_graph_; - const int32_t start_idx_; - const int32_t end_idx_; - GrowableArray* block_id_list_; - int32_t idx_; - int32_t repeats_; - bool changed_; + /** + * @brief Get the next BasicBlock iterating backward, restart if a BasicBlock was reported changed during the last iteration. + * @return the next BasicBlock iterating backward, with chance of repeating the iteration. + */ + virtual BasicBlock* ReverseRepeatNext() ALWAYS_INLINE; + + MIRGraph* const mir_graph_; /**< @brief the MIRGraph */ + const int32_t start_idx_; /**< @brief the start index for the iteration */ + const int32_t end_idx_; /**< @brief the last index for the iteration */ + GrowableArray* block_id_list_; /**< @brief the list of BasicBlocks we want to iterate on */ + int32_t idx_; /**< @brief Current index for the iterator */ + int32_t repeats_; /**< @brief Number of repeats over the iteration */ + bool changed_; /**< @brief Has something changed during the current iteration? */ }; // DataflowIterator + /** + * @class PreOrderDfsIterator + * @brief Used to perform a Pre-order Depth-First-Search Iteration of a MIRGraph. + */ class PreOrderDfsIterator : public DataflowIterator { public: + /** + * @brief The constructor, using all of the reachable blocks of the MIRGraph. + * @param mir_graph The MIRGraph considered. + */ explicit PreOrderDfsIterator(MIRGraph* mir_graph) : DataflowIterator(mir_graph, 0, mir_graph->GetNumReachableBlocks()) { + // Extra setup for the PreOrderDfsIterator. idx_ = start_idx_; block_id_list_ = mir_graph->GetDfsOrder(); } - BasicBlock* Next() { + /** + * @brief Get the next BasicBlock depending on iteration order. + * @param had_change did the user of the iteration change the previous BasicBlock. + * @return the next BasicBlock following the iteration order, 0 if finished. + */ + virtual BasicBlock* Next(bool had_change = false) { + // Update changed: if had_changed is true, we remember it for the whole iteration. + changed_ |= had_change; + return ForwardSingleNext(); } }; + /** + * @class RepeatingPreOrderDfsIterator + * @brief Used to perform a Repeating Pre-order Depth-First-Search Iteration of a MIRGraph. + * @details If there is a change during an iteration, the iteration starts over at the end of the iteration. + */ class RepeatingPreOrderDfsIterator : public DataflowIterator { public: + /** + * @brief The constructor, using all of the reachable blocks of the MIRGraph. + * @param mir_graph The MIRGraph considered. + */ explicit RepeatingPreOrderDfsIterator(MIRGraph* mir_graph) : DataflowIterator(mir_graph, 0, mir_graph->GetNumReachableBlocks()) { + // Extra setup for the RepeatingPreOrderDfsIterator. idx_ = start_idx_; block_id_list_ = mir_graph->GetDfsOrder(); } - BasicBlock* Next(bool had_change) { - return ForwardRepeatNext(had_change); + /** + * @brief Get the next BasicBlock depending on iteration order. + * @param had_change did the user of the iteration change the previous BasicBlock. + * @return the next BasicBlock following the iteration order, 0 if finished. + */ + virtual BasicBlock* Next(bool had_change = false) { + // Update changed: if had_changed is true, we remember it for the whole iteration. + changed_ |= had_change; + + return ForwardRepeatNext(); } }; + /** + * @class RepeatingPostOrderDfsIterator + * @brief Used to perform a Repeating Post-order Depth-First-Search Iteration of a MIRGraph. + * @details If there is a change during an iteration, the iteration starts over at the end of the iteration. + */ class RepeatingPostOrderDfsIterator : public DataflowIterator { public: + /** + * @brief The constructor, using all of the reachable blocks of the MIRGraph. + * @param mir_graph The MIRGraph considered. + */ explicit RepeatingPostOrderDfsIterator(MIRGraph* mir_graph) : DataflowIterator(mir_graph, 0, mir_graph->GetNumReachableBlocks()) { + // Extra setup for the RepeatingPostOrderDfsIterator. idx_ = start_idx_; block_id_list_ = mir_graph->GetDfsPostOrder(); } - BasicBlock* Next(bool had_change) { - return ForwardRepeatNext(had_change); + /** + * @brief Get the next BasicBlock depending on iteration order. + * @param had_change did the user of the iteration change the previous BasicBlock. + * @return the next BasicBlock following the iteration order, 0 if finished. + */ + virtual BasicBlock* Next(bool had_change = false) { + // Update changed: if had_changed is true, we remember it for the whole iteration. + changed_ |= had_change; + + return ForwardRepeatNext(); } }; + /** + * @class ReversePostOrderDfsIterator + * @brief Used to perform a Reverse Post-order Depth-First-Search Iteration of a MIRGraph. + */ class ReversePostOrderDfsIterator : public DataflowIterator { public: + /** + * @brief The constructor, using all of the reachable blocks of the MIRGraph. + * @param mir_graph The MIRGraph considered. + */ explicit ReversePostOrderDfsIterator(MIRGraph* mir_graph) : DataflowIterator(mir_graph, mir_graph->GetNumReachableBlocks() -1, 0) { + // Extra setup for the ReversePostOrderDfsIterator. idx_ = start_idx_; block_id_list_ = mir_graph->GetDfsPostOrder(); } - BasicBlock* Next() { + /** + * @brief Get the next BasicBlock depending on iteration order. + * @param had_change did the user of the iteration change the previous BasicBlock. + * @return the next BasicBlock following the iteration order, 0 if finished. + */ + virtual BasicBlock* Next(bool had_change = false) { + // Update changed: if had_changed is true, we remember it for the whole iteration. + changed_ |= had_change; + return ReverseSingleNext(); } }; + /** + * @class ReversePostOrderDfsIterator + * @brief Used to perform a Repeating Reverse Post-order Depth-First-Search Iteration of a MIRGraph. + * @details If there is a change during an iteration, the iteration starts over at the end of the iteration. + */ class RepeatingReversePostOrderDfsIterator : public DataflowIterator { public: + /** + * @brief The constructor, using all of the reachable blocks of the MIRGraph. + * @param mir_graph The MIRGraph considered. + */ explicit RepeatingReversePostOrderDfsIterator(MIRGraph* mir_graph) : DataflowIterator(mir_graph, mir_graph->GetNumReachableBlocks() -1, 0) { + // Extra setup for the RepeatingReversePostOrderDfsIterator idx_ = start_idx_; block_id_list_ = mir_graph->GetDfsPostOrder(); } - BasicBlock* Next(bool had_change) { - return ReverseRepeatNext(had_change); + /** + * @brief Get the next BasicBlock depending on iteration order. + * @param had_change did the user of the iteration change the previous BasicBlock. + * @return the next BasicBlock following the iteration order, 0 if finished. + */ + virtual BasicBlock* Next(bool had_change = false) { + // Update changed: if had_changed is true, we remember it for the whole iteration. + changed_ |= had_change; + + return ReverseRepeatNext(); } }; + /** + * @class PostOrderDOMIterator + * @brief Used to perform a Post-order Domination Iteration of a MIRGraph. + */ class PostOrderDOMIterator : public DataflowIterator { public: + /** + * @brief The constructor, using all of the reachable blocks of the MIRGraph. + * @param mir_graph The MIRGraph considered. + */ explicit PostOrderDOMIterator(MIRGraph* mir_graph) : DataflowIterator(mir_graph, 0, mir_graph->GetNumReachableBlocks()) { + // Extra setup for thePostOrderDOMIterator. idx_ = start_idx_; block_id_list_ = mir_graph->GetDomPostOrder(); } - BasicBlock* Next() { + /** + * @brief Get the next BasicBlock depending on iteration order. + * @param had_change did the user of the iteration change the previous BasicBlock. + * @return the next BasicBlock following the iteration order, 0 if finished. + */ + virtual BasicBlock* Next(bool had_change = false) { + // Update changed: if had_changed is true, we remember it for the whole iteration. + changed_ |= had_change; + return ForwardSingleNext(); } }; + /** + * @class AllNodesIterator + * @brief Used to perform an iteration on all the BasicBlocks a MIRGraph. + */ class AllNodesIterator : public DataflowIterator { public: + /** + * @brief The constructor, using all of the reachable blocks of the MIRGraph. + * @param mir_graph The MIRGraph considered. + */ explicit AllNodesIterator(MIRGraph* mir_graph) : DataflowIterator(mir_graph, 0, 0) { all_nodes_iterator_ = new (mir_graph->GetArena()) GrowableArray::Iterator(mir_graph->GetBlockList()); } + /** + * @brief Resetting the iterator. + */ void Reset() { all_nodes_iterator_->Reset(); } - BasicBlock* Next() ALWAYS_INLINE; + /** + * @brief Get the next BasicBlock depending on iteration order. + * @param had_change did the user of the iteration change the previous BasicBlock. + * @return the next BasicBlock following the iteration order, 0 if finished. + */ + virtual BasicBlock* Next(bool had_change = false) ALWAYS_INLINE; private: - GrowableArray::Iterator* all_nodes_iterator_; + GrowableArray::Iterator* all_nodes_iterator_; /**< @brief The list of all the nodes */ }; } // namespace art From b73f31fd83151bfe9f73166343fd698fffa4e03b Mon Sep 17 00:00:00 2001 From: Narayan Kamath Date: Tue, 31 Dec 2013 10:31:33 +0000 Subject: [PATCH 0323/2402] Fix a typo in a comment. Change-Id: I2579261f9ea2f8f9c64975fe6cb18c3c6102da39 --- runtime/check_jni.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/check_jni.cc b/runtime/check_jni.cc index 09c48b1220f..1b79ee0c810 100644 --- a/runtime/check_jni.cc +++ b/runtime/check_jni.cc @@ -1755,7 +1755,7 @@ PRIMITIVE_ARRAY_FUNCTIONS(jdouble, Double, 'D'); JniAbortF(__FUNCTION__, "non-nullable address is NULL"); } if (capacity < 0) { - JniAbortF(__FUNCTION__, "capacity must be non negative: %lld", capacity); + JniAbortF(__FUNCTION__, "capacity must be non-negative: %lld", capacity); } return CHECK_JNI_EXIT("L", baseEnv(env)->NewDirectByteBuffer(env, address, capacity)); } From 5115473c81ec855a5646a5f755afb26aa7f2b1e9 Mon Sep 17 00:00:00 2001 From: Vladimir Marko Date: Thu, 2 Jan 2014 09:44:23 +0000 Subject: [PATCH 0324/2402] Fix oatdump "compilercallbacks" option for runtime. The "compilercallbacks" runtime option replaced "compiler" in I708ca13227c809e07917ff3879a89722017e83a9 . Fix a comment in codegen_util.cc . Change-Id: I2c5ebd56dd96f0ee8e62b602bfe45357565471ff --- compiler/dex/quick/codegen_util.cc | 2 +- oatdump/oatdump.cc | 8 +++++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/compiler/dex/quick/codegen_util.cc b/compiler/dex/quick/codegen_util.cc index 65286d5d2f2..29554c0977a 100644 --- a/compiler/dex/quick/codegen_util.cc +++ b/compiler/dex/quick/codegen_util.cc @@ -1003,7 +1003,7 @@ void Mir2Lir::Materialize() { /* * Custom codegen for special cases. If for any reason the - * special codegen doesn't succeed, first_lir_insn_ will + * special codegen doesn't succeed, first_lir_insn_ will be * set to NULL; */ // TODO: Clean up GenSpecial() and return true only if special implementation is emitted. diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc index e219dd33a2a..9bde30d90b5 100644 --- a/oatdump/oatdump.cc +++ b/oatdump/oatdump.cc @@ -26,6 +26,7 @@ #include "base/unix_file/fd_file.h" #include "class_linker.h" #include "class_linker-inl.h" +#include "compiler_callbacks.h" #include "dex_file-inl.h" #include "dex_instruction.h" #include "disassembler.h" @@ -1456,7 +1457,12 @@ static int oatdump(int argc, char** argv) { std::string boot_oat_option; // We are more like a compiler than a run-time. We don't want to execute code. - options.push_back(std::make_pair("compiler", reinterpret_cast(NULL))); + struct OatDumpCompilerCallbacks : CompilerCallbacks { + virtual bool MethodVerified(verifier::MethodVerifier* /*verifier*/) { return true; } + virtual void ClassRejected(ClassReference /*ref*/) { } + } callbacks; + options.push_back(std::make_pair("compilercallbacks", + static_cast(&callbacks))); if (boot_image_filename != NULL) { boot_image_option += "-Ximage:"; From dce164adfbf679d7ec9c9dc778fa3fb596011740 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Thu, 2 Jan 2014 17:00:38 -0800 Subject: [PATCH 0325/2402] Avoid inliner spam wrt String.length. Also, remove a use of std::map for our preferred SafeMap and be more explicit with types. Change-Id: I7b9a4bb1f73c22490fe416503e050671e7c99fe0 --- compiler/dex/quick/dex_file_method_inliner.cc | 20 +++++++++---------- compiler/dex/quick/dex_file_method_inliner.h | 17 ++++++++-------- 2 files changed, 19 insertions(+), 18 deletions(-) diff --git a/compiler/dex/quick/dex_file_method_inliner.cc b/compiler/dex/quick/dex_file_method_inliner.cc index c21ddcc4307..0937be32328 100644 --- a/compiler/dex/quick/dex_file_method_inliner.cc +++ b/compiler/dex/quick/dex_file_method_inliner.cc @@ -499,25 +499,25 @@ void DexFileMethodInliner::FindIntrinsics(const DexFile* dex_file) { uint32_t method_idx = FindMethodIndex(dex_file, &cache, def.method_def); if (method_idx != kIndexNotFound) { DCHECK(inline_methods_.find(method_idx) == inline_methods_.end()); - inline_methods_[method_idx] = def.intrinsic; + inline_methods_.Put(method_idx, def.intrinsic); } } dex_file_ = dex_file; } bool DexFileMethodInliner::AddInlineMethod(int32_t method_idx, InlineMethodOpcode opcode, - uint16_t flags, uint32_t data) { + InlineMethodFlags flags, uint32_t data) { WriterMutexLock mu(Thread::Current(), lock_); - InlineMethod* im = &inline_methods_[method_idx]; - if (im->flags == 0) { - im->opcode = opcode; - im->flags = flags; - im->data = data; + if (LIKELY(inline_methods_.find(method_idx) == inline_methods_.end())) { + InlineMethod im = {opcode, flags, data}; + inline_methods_.Put(method_idx, im); return true; } else { - // TODO: Warning about a method being already inlined? - LOG(WARNING) << "Inliner: " << PrettyMethod(method_idx, *dex_file_) << " already inline, " - << im->flags; + if (PrettyMethod(method_idx, *dex_file_) == "int java.lang.String.length()") { + // TODO: String.length is both kIntrinsicIsEmptyOrLength and kInlineOpIGet. + } else { + LOG(ERROR) << "Inliner: " << PrettyMethod(method_idx, *dex_file_) << " already inline"; + } return false; } } diff --git a/compiler/dex/quick/dex_file_method_inliner.h b/compiler/dex/quick/dex_file_method_inliner.h index 1d9973108b0..6e81303f9d2 100644 --- a/compiler/dex/quick/dex_file_method_inliner.h +++ b/compiler/dex/quick/dex_file_method_inliner.h @@ -18,9 +18,9 @@ #define ART_COMPILER_DEX_QUICK_DEX_FILE_METHOD_INLINER_H_ #include -#include #include "base/mutex.h" #include "base/macros.h" +#include "safe_map.h" #include "dex/compiler_enums.h" #include "dex_file.h" #include "locks.h" @@ -56,14 +56,15 @@ enum InlineMethodOpcode : uint16_t { kInlineOpIPut, }; -enum InlineMethodFlags { - kInlineIntrinsic = 0x0001, - kInlineSpecial = 0x0002, +enum InlineMethodFlags : uint16_t { + kNoInlineMethodFlags = 0x0000, + kInlineIntrinsic = 0x0001, + kInlineSpecial = 0x0002, }; struct InlineMethod { - uint16_t opcode; - uint16_t flags; + InlineMethodOpcode opcode; + InlineMethodFlags flags; uint32_t data; }; @@ -369,7 +370,7 @@ class DexFileMethodInliner { friend class DexFileToMethodInlinerMap; bool AddInlineMethod(int32_t method_idx, InlineMethodOpcode opcode, - uint16_t flags, uint32_t data) LOCKS_EXCLUDED(lock_); + InlineMethodFlags flags, uint32_t data) LOCKS_EXCLUDED(lock_); bool AnalyseReturnMethod(int32_t method_idx, const DexFile::CodeItem* code_item, OpSize size) LOCKS_EXCLUDED(lock_); @@ -384,7 +385,7 @@ class DexFileMethodInliner { /* * Maps method indexes (for the particular DexFile) to Intrinsic defintions. */ - std::map inline_methods_ GUARDED_BY(lock_); + SafeMap inline_methods_ GUARDED_BY(lock_); const DexFile* dex_file_; DISALLOW_COPY_AND_ASSIGN(DexFileMethodInliner); From da0a4dbede243c5ac473db88d44ba6139e7dd87e Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Thu, 2 Jan 2014 17:17:32 -0800 Subject: [PATCH 0326/2402] Remove extraneous CFI from x86 entrypoints to fix backtrace. PUSH/POP are macros that already handle cfi save/restore. Change-Id: I95765e7c4d66acfa2f115ebb343fbab4eca33c08 --- runtime/arch/x86/quick_entrypoints_x86.S | 42 ------------------------ 1 file changed, 42 deletions(-) diff --git a/runtime/arch/x86/quick_entrypoints_x86.S b/runtime/arch/x86/quick_entrypoints_x86.S index 96794713cfa..4687ecc2444 100644 --- a/runtime/arch/x86/quick_entrypoints_x86.S +++ b/runtime/arch/x86/quick_entrypoints_x86.S @@ -26,11 +26,8 @@ */ MACRO0(SETUP_SAVE_ALL_CALLEE_SAVE_FRAME) PUSH edi // Save callee saves (ebx is saved/restored by the upcall) - .cfi_rel_offset edi, -8 PUSH esi - .cfi_rel_offset esi, -12 PUSH ebp - .cfi_rel_offset ebp, -16 subl MACRO_LITERAL(16), %esp // Grow stack by 4 words, bottom word will hold Method* .cfi_adjust_cfa_offset 16 END_MACRO @@ -41,11 +38,8 @@ END_MACRO */ MACRO0(SETUP_REF_ONLY_CALLEE_SAVE_FRAME) PUSH edi // Save callee saves (ebx is saved/restored by the upcall) - .cfi_rel_offset edi, -8 PUSH esi - .cfi_rel_offset esi, -12 PUSH ebp - .cfi_rel_offset ebp, -16 subl MACRO_LITERAL(16), %esp // Grow stack by 4 words, bottom word will hold Method* .cfi_adjust_cfa_offset 16 END_MACRO @@ -53,11 +47,8 @@ END_MACRO MACRO0(RESTORE_REF_ONLY_CALLEE_SAVE_FRAME) addl MACRO_LITERAL(16), %esp // Unwind stack up to return address POP ebp // Restore callee saves (ebx is saved/restored by the upcall) - .cfi_restore ebp POP esi - .cfi_restore esi POP edi - .cfi_restore edi .cfi_adjust_cfa_offset -28 END_MACRO @@ -67,36 +58,23 @@ END_MACRO */ MACRO0(SETUP_REF_AND_ARGS_CALLEE_SAVE_FRAME) PUSH edi // Save callee saves - .cfi_rel_offset edi, -8 PUSH esi - .cfi_rel_offset esi, -12 PUSH ebp - .cfi_rel_offset ebp, -16 PUSH ebx // Save args - .cfi_rel_offset ebx, -20 PUSH edx - .cfi_rel_offset edx, -24 PUSH ecx - .cfi_rel_offset ecx, -28 PUSH eax // Align stack, eax will be clobbered by Method* - .cfi_rel_offset eax, -28 END_MACRO MACRO0(RESTORE_REF_AND_ARGS_CALLEE_SAVE_FRAME) addl MACRO_LITERAL(4), %esp // Remove padding .cfi_adjust_cfa_offset -4 POP ecx // Restore args except eax - .cfi_restore ecx POP edx - .cfi_restore edx POP ebx - .cfi_restore ebx POP ebp // Restore callee saves - .cfi_restore ebp POP esi - .cfi_restore esi POP edi - .cfi_restore edi END_MACRO /* @@ -214,19 +192,12 @@ MACRO2(INVOKE_TRAMPOLINE, c_name, cxx_name) // Set up the callee save frame to conform with Runtime::CreateCalleeSaveMethod(kRefsAndArgs) // return address PUSH edi - .cfi_rel_offset edi, -8 PUSH esi - .cfi_rel_offset esi, -12 PUSH ebp - .cfi_rel_offset ebp, -16 PUSH ebx // Save args - .cfi_rel_offset ebx, -20 PUSH edx - .cfi_rel_offset edx, -24 PUSH ecx - .cfi_rel_offset ecx, -28 PUSH eax // <-- callee save Method* to go here - .cfi_rel_offset eax, -32 movl %esp, %edx // remember SP // Outgoing argument set up subl MACRO_LITERAL(12), %esp // alignment padding @@ -243,15 +214,10 @@ MACRO2(INVOKE_TRAMPOLINE, c_name, cxx_name) addl MACRO_LITERAL(36), %esp // Pop arguments skip eax .cfi_adjust_cfa_offset -36 POP ecx // Restore args except eax - .cfi_restore ecx POP edx - .cfi_restore edx POP ebx - .cfi_restore ebx POP ebp // Restore callee saves - .cfi_restore ebp POP esi - .cfi_restore esi // Swap EDI callee save with code pointer. xchgl %edi, (%esp) testl %eax, %eax // Branch forward if exception pending. @@ -286,9 +252,7 @@ INVOKE_TRAMPOLINE art_quick_invoke_virtual_trampoline_with_access_check, artInvo */ DEFINE_FUNCTION art_quick_invoke_stub PUSH ebp // save ebp - .cfi_rel_offset ebp, -8 PUSH ebx // save ebx - .cfi_rel_offset ebx, -12 mov %esp, %ebp // copy value of stack pointer into base pointer .cfi_def_cfa_register ebp mov 20(%ebp), %ebx // get arg array size @@ -311,9 +275,7 @@ DEFINE_FUNCTION art_quick_invoke_stub mov %ebp, %esp // restore stack pointer .cfi_def_cfa_register esp POP ebx // pop ebx - .cfi_restore ebx POP ebp // pop ebp - .cfi_restore ebp mov 20(%esp), %ecx // get result pointer cmpl LITERAL(68), 24(%esp) // test if result type char == 'D' je return_double_quick @@ -527,9 +489,7 @@ END_FUNCTION art_quick_is_assignable DEFINE_FUNCTION art_quick_check_cast PUSH eax // alignment padding PUSH ecx // pass arg2 - obj->klass - .cfi_rel_offset ecx, -12 PUSH eax // pass arg1 - checked class - .cfi_rel_offset eax, -16 call SYMBOL(artIsAssignableFromCode) // (Class* klass, Class* ref_klass) testl %eax, %eax jz 1f // jump forward if not assignable @@ -538,9 +498,7 @@ DEFINE_FUNCTION art_quick_check_cast ret 1: POP eax // pop arguments - .cfi_restore eax POP ecx - .cfi_restore ecx addl LITERAL(4), %esp .cfi_adjust_cfa_offset -12 SETUP_SAVE_ALL_CALLEE_SAVE_FRAME // save all registers as basis for long jump context From 4069d33b95b43df47a522ff52b35e901d4e58a56 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Fri, 3 Jan 2014 10:28:27 -0800 Subject: [PATCH 0327/2402] Use memcpy instead of Array::Set in mirror::String::AllocFromUtf16. Also, add a DCHECK on the hashcode if provided. Change-Id: I604c09c3d0c48ebab8df8f1af0cd65b2507f94a8 --- runtime/mirror/string.cc | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/runtime/mirror/string.cc b/runtime/mirror/string.cc index d6e509d8416..1f756a1e53c 100644 --- a/runtime/mirror/string.cc +++ b/runtime/mirror/string.cc @@ -121,20 +121,18 @@ String* String::AllocFromUtf16(Thread* self, int32_t utf16_length, const uint16_t* utf16_data_in, int32_t hash_code) { - CHECK(utf16_data_in != NULL || utf16_length == 0); + CHECK(utf16_data_in != nullptr || utf16_length == 0); String* string = Alloc(self, utf16_length); if (UNLIKELY(string == nullptr)) { return nullptr; } - // TODO: use 16-bit wide memset variant CharArray* array = const_cast(string->GetCharArray()); - if (array == NULL) { - return NULL; - } - for (int i = 0; i < utf16_length; i++) { - array->Set(i, utf16_data_in[i]); + if (UNLIKELY(array == nullptr)) { + return nullptr; } + memcpy(array->GetData(), utf16_data_in, utf16_length * sizeof(uint16_t)); if (hash_code != 0) { + DCHECK_EQ(hash_code, ComputeUtf16Hash(utf16_data_in, utf16_length)); string->SetHashCode(hash_code); } else { string->ComputeHashCode(); From 1d99e4549309d05007d041d058b1878de88e9585 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Thu, 2 Jan 2014 17:36:41 -0800 Subject: [PATCH 0328/2402] Disallow JNI NewObjectArray of primitive types. Also, make jni_internal_test execute via the interpreter rather than compile methods. Add tests for passing negative array sizes to JNI routines new functions. Re-enable the tests NewStringNullCharsNonzeroLength and NewDirectBuffer_GetDirectBufferAddress_GetDirectBufferCapacity. Test and explicitly fail if the initial value argument to NewObjectArray isn't assignable to that type of array. Use unchecked ObjectArray::Set with NewObjectArray with an initial value. Change-Id: If3491cb5f974b42cf70c1b850819265f9963ee48 --- runtime/common_test.h | 16 +- runtime/interpreter/interpreter_common.cc | 3 + runtime/jni_internal.cc | 63 +++++--- runtime/jni_internal_test.cc | 171 ++++++++++++++++------ 4 files changed, 186 insertions(+), 67 deletions(-) diff --git a/runtime/common_test.h b/runtime/common_test.h index a3cbde37400..ee95d5bbbd0 100644 --- a/runtime/common_test.h +++ b/runtime/common_test.h @@ -296,7 +296,6 @@ class CommonTest : public testing::Test { void MakeExecutable(mirror::ArtMethod* method) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { CHECK(method != NULL); - LOG(INFO) << "MakeExecutable " << PrettyMethod(method); const CompiledMethod* compiled_method = NULL; if (!method->IsAbstract()) { @@ -325,7 +324,6 @@ class CommonTest : public testing::Test { const void* method_code; // No code? You must mean to go into the interpreter. method_code = GetCompiledCodeToInterpreterBridge(); - LOG(INFO) << "MakeExecutable " << PrettyMethod(method) << " code=" << method_code; OatFile::OatMethod oat_method = CreateOatMethod(method_code, kStackAlignment, 0, @@ -382,6 +380,20 @@ class CommonTest : public testing::Test { setenv("ANDROID_DATA", android_data.c_str(), 1); } + void MakeExecutable(mirror::ClassLoader* class_loader, const char* class_name) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + std::string class_descriptor(DotToDescriptor(class_name)); + SirtRef loader(Thread::Current(), class_loader); + mirror::Class* klass = class_linker_->FindClass(class_descriptor.c_str(), loader); + CHECK(klass != NULL) << "Class not found " << class_name; + for (size_t i = 0; i < klass->NumDirectMethods(); i++) { + MakeExecutable(klass->GetDirectMethod(i)); + } + for (size_t i = 0; i < klass->NumVirtualMethods(); i++) { + MakeExecutable(klass->GetVirtualMethod(i)); + } + } + protected: static bool IsHost() { return (getenv("ANDROID_BUILD_TOP") != NULL); diff --git a/runtime/interpreter/interpreter_common.cc b/runtime/interpreter/interpreter_common.cc index 5b9e55f9238..f1498287b55 100644 --- a/runtime/interpreter/interpreter_common.cc +++ b/runtime/interpreter/interpreter_common.cc @@ -151,6 +151,9 @@ bool DoCall(ArtMethod* method, Object* receiver, Thread* self, ShadowFrame& shad // Do the call now. if (LIKELY(Runtime::Current()->IsStarted())) { + if (kIsDebugBuild && method->GetEntryPointFromInterpreter() == nullptr) { + LOG(FATAL) << "Attempt to invoke non-executable method: " << PrettyMethod(method); + } (method->GetEntryPointFromInterpreter())(self, mh, code_item, new_shadow_frame, result); } else { UnstartedRuntimeInvoke(self, mh, code_item, new_shadow_frame, result, first_dest_reg); diff --git a/runtime/jni_internal.cc b/runtime/jni_internal.cc index bbe5fda62f4..81cc94b45a5 100644 --- a/runtime/jni_internal.cc +++ b/runtime/jni_internal.cc @@ -1954,8 +1954,13 @@ class JNI { } static jstring NewString(JNIEnv* env, const jchar* chars, jsize char_count) { - if (UNLIKELY(chars == NULL && char_count > 0)) { \ - JniAbortF("NewString", "char == null && char_count > 0"); \ + if (UNLIKELY(char_count < 0)) { + JniAbortF("NewString", "char_count < 0: %d", char_count); + return nullptr; + } + if (UNLIKELY(chars == nullptr && char_count > 0)) { + JniAbortF("NewString", "chars == null && char_count > 0"); + return nullptr; } ScopedObjectAccess soa(env); String* result = String::AllocFromUtf16(soa.Self(), char_count, chars); @@ -2129,32 +2134,51 @@ class JNI { return NewPrimitiveArray(soa, length); } - static jobjectArray NewObjectArray(JNIEnv* env, jsize length, jclass element_jclass, jobject initial_element) { - if (length < 0) { + static jobjectArray NewObjectArray(JNIEnv* env, jsize length, jclass element_jclass, + jobject initial_element) { + if (UNLIKELY(length < 0)) { JniAbortF("NewObjectArray", "negative array length: %d", length); + return nullptr; } // Compute the array class corresponding to the given element class. ScopedObjectAccess soa(env); - Class* element_class = soa.Decode(element_jclass); - std::string descriptor; - descriptor += "["; - descriptor += ClassHelper(element_class).GetDescriptor(); - - // Find the class. - ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); - SirtRef class_loader(soa.Self(), element_class->GetClassLoader()); - Class* array_class = class_linker->FindClass(descriptor.c_str(), class_loader); - if (array_class == NULL) { - return NULL; + Class* array_class; + { + Class* element_class = soa.Decode(element_jclass); + if (UNLIKELY(element_class->IsPrimitive())) { + JniAbortF("NewObjectArray", "not an object type: %s", + PrettyDescriptor(element_class).c_str()); + return nullptr; + } + std::string descriptor("["); + descriptor += ClassHelper(element_class).GetDescriptor(); + + // Find the class. + ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); + SirtRef class_loader(soa.Self(), element_class->GetClassLoader()); + array_class = class_linker->FindClass(descriptor.c_str(), class_loader); + if (UNLIKELY(array_class == nullptr)) { + return nullptr; + } } // Allocate and initialize if necessary. ObjectArray* result = ObjectArray::Alloc(soa.Self(), array_class, length); - if (initial_element != NULL) { + if (result != nullptr && initial_element != nullptr) { Object* initial_object = soa.Decode(initial_element); - for (jsize i = 0; i < length; ++i) { - result->Set(i, initial_object); + if (initial_object != nullptr) { + mirror::Class* element_class = result->GetClass()->GetComponentType(); + if (UNLIKELY(!element_class->IsAssignableFrom(initial_object->GetClass()))) { + JniAbortF("NewObjectArray", "cannot assign object of type '%s' to array with element " + "type of '%s'", PrettyDescriptor(initial_object->GetClass()).c_str(), + PrettyDescriptor(element_class).c_str()); + + } else { + for (jsize i = 0; i < length; ++i) { + result->SetWithoutChecks(i, initial_object); + } + } } } return soa.AddLocalReference(result); @@ -2572,8 +2596,9 @@ class JNI { template static JniT NewPrimitiveArray(const ScopedObjectAccess& soa, jsize length) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - if (length < 0) { + if (UNLIKELY(length < 0)) { JniAbortF("NewPrimitiveArray", "negative array length: %d", length); + return nullptr; } ArtT* result = ArtT::Alloc(soa.Self(), length); return soa.AddLocalReference(result); diff --git a/runtime/jni_internal_test.cc b/runtime/jni_internal_test.cc index 224044728a4..9b278f8a5a5 100644 --- a/runtime/jni_internal_test.cc +++ b/runtime/jni_internal_test.cc @@ -78,10 +78,17 @@ class JniInternalTest : public CommonTest { CommonTest::TearDown(); } - void DoCompile(mirror::ArtMethod*& method, - mirror::Object*& receiver, - bool is_static, const char* method_name, - const char* method_signature) + jclass GetPrimitiveClass(char descriptor) { + ScopedObjectAccess soa(env_); + mirror::Class* c = class_linker_->FindPrimitiveClass(descriptor); + CHECK(c != nullptr); + return soa.AddLocalReference(c); + } + + void JniInternalTestMakeExecutable(mirror::ArtMethod** method, + mirror::Object** receiver, + bool is_static, const char* method_name, + const char* method_signature) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { const char* class_name = is_static ? "StaticLeafMethods" : "NonStaticLeafMethods"; jobject jclass_loader(LoadDex(class_name)); @@ -91,21 +98,23 @@ class JniInternalTest : public CommonTest { class_loader(self, ScopedObjectAccessUnchecked(self).Decode(jclass_loader)); if (is_static) { - CompileDirectMethod(class_loader, class_name, method_name, method_signature); + MakeExecutable(ScopedObjectAccessUnchecked(self).Decode(jclass_loader), + class_name); } else { - CompileVirtualMethod(null_class_loader, "java.lang.Class", "isFinalizable", "()Z"); - CompileDirectMethod(null_class_loader, "java.lang.Object", "", "()V"); - CompileVirtualMethod(class_loader, class_name, method_name, method_signature); + MakeExecutable(nullptr, "java.lang.Class"); + MakeExecutable(nullptr, "java.lang.Object"); + MakeExecutable(ScopedObjectAccessUnchecked(self).Decode(jclass_loader), + class_name); } mirror::Class* c = class_linker_->FindClass(DotToDescriptor(class_name).c_str(), class_loader); CHECK(c != NULL); - method = is_static ? c->FindDirectMethod(method_name, method_signature) - : c->FindVirtualMethod(method_name, method_signature); - CHECK(method != NULL); + *method = is_static ? c->FindDirectMethod(method_name, method_signature) + : c->FindVirtualMethod(method_name, method_signature); + CHECK(method != nullptr); - receiver = (is_static ? NULL : c->AllocObject(self)); + *receiver = (is_static ? nullptr : c->AllocObject(self)); // Start runtime. bool started = runtime_->Start(); @@ -116,7 +125,7 @@ class JniInternalTest : public CommonTest { void InvokeNopMethod(bool is_static) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { mirror::ArtMethod* method; mirror::Object* receiver; - DoCompile(method, receiver, is_static, "nop", "()V"); + JniInternalTestMakeExecutable(&method, &receiver, is_static, "nop", "()V"); ArgArray arg_array(NULL, 0); JValue result; @@ -132,7 +141,7 @@ class JniInternalTest : public CommonTest { SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { mirror::ArtMethod* method; mirror::Object* receiver; - DoCompile(method, receiver, is_static, "identity", "(I)I"); + JniInternalTestMakeExecutable(&method, &receiver, is_static, "identity", "(I)I"); ArgArray arg_array(NULL, 0); uint32_t* args = arg_array.GetArray(); @@ -168,7 +177,7 @@ class JniInternalTest : public CommonTest { SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { mirror::ArtMethod* method; mirror::Object* receiver; - DoCompile(method, receiver, is_static, "identity", "(I)I"); + JniInternalTestMakeExecutable(&method, &receiver, is_static, "identity", "(I)I"); ArgArray arg_array(NULL, 0); uint32_t* args = arg_array.GetArray(); @@ -204,7 +213,7 @@ class JniInternalTest : public CommonTest { SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { mirror::ArtMethod* method; mirror::Object* receiver; - DoCompile(method, receiver, is_static, "identity", "(D)D"); + JniInternalTestMakeExecutable(&method, &receiver, is_static, "identity", "(D)D"); ArgArray arg_array(NULL, 0); uint32_t* args = arg_array.GetArray(); @@ -248,7 +257,7 @@ class JniInternalTest : public CommonTest { SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { mirror::ArtMethod* method; mirror::Object* receiver; - DoCompile(method, receiver, is_static, "sum", "(II)I"); + JniInternalTestMakeExecutable(&method, &receiver, is_static, "sum", "(II)I"); ArgArray arg_array(NULL, 0); uint32_t* args = arg_array.GetArray(); @@ -294,7 +303,7 @@ class JniInternalTest : public CommonTest { SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { mirror::ArtMethod* method; mirror::Object* receiver; - DoCompile(method, receiver, is_static, "sum", "(III)I"); + JniInternalTestMakeExecutable(&method, &receiver, is_static, "sum", "(III)I"); ArgArray arg_array(NULL, 0); uint32_t* args = arg_array.GetArray(); @@ -345,7 +354,7 @@ class JniInternalTest : public CommonTest { SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { mirror::ArtMethod* method; mirror::Object* receiver; - DoCompile(method, receiver, is_static, "sum", "(IIII)I"); + JniInternalTestMakeExecutable(&method, &receiver, is_static, "sum", "(IIII)I"); ArgArray arg_array(NULL, 0); uint32_t* args = arg_array.GetArray(); @@ -401,7 +410,7 @@ class JniInternalTest : public CommonTest { SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { mirror::ArtMethod* method; mirror::Object* receiver; - DoCompile(method, receiver, is_static, "sum", "(IIIII)I"); + JniInternalTestMakeExecutable(&method, &receiver, is_static, "sum", "(IIIII)I"); ArgArray arg_array(NULL, 0); uint32_t* args = arg_array.GetArray(); @@ -462,7 +471,7 @@ class JniInternalTest : public CommonTest { SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { mirror::ArtMethod* method; mirror::Object* receiver; - DoCompile(method, receiver, is_static, "sum", "(DD)D"); + JniInternalTestMakeExecutable(&method, &receiver, is_static, "sum", "(DD)D"); ArgArray arg_array(NULL, 0); uint32_t* args = arg_array.GetArray(); @@ -528,7 +537,7 @@ class JniInternalTest : public CommonTest { SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { mirror::ArtMethod* method; mirror::Object* receiver; - DoCompile(method, receiver, is_static, "sum", "(DDD)D"); + JniInternalTestMakeExecutable(&method, &receiver, is_static, "sum", "(DDD)D"); ArgArray arg_array(NULL, 0); uint32_t* args = arg_array.GetArray(); @@ -583,7 +592,7 @@ class JniInternalTest : public CommonTest { SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { mirror::ArtMethod* method; mirror::Object* receiver; - DoCompile(method, receiver, is_static, "sum", "(DDDD)D"); + JniInternalTestMakeExecutable(&method, &receiver, is_static, "sum", "(DDDD)D"); ArgArray arg_array(NULL, 0); uint32_t* args = arg_array.GetArray(); @@ -647,7 +656,7 @@ class JniInternalTest : public CommonTest { SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { mirror::ArtMethod* method; mirror::Object* receiver; - DoCompile(method, receiver, is_static, "sum", "(DDDDD)D"); + JniInternalTestMakeExecutable(&method, &receiver, is_static, "sum", "(DDDDD)D"); ArgArray arg_array(NULL, 0); uint32_t* args = arg_array.GetArray(); @@ -1011,6 +1020,14 @@ TEST_F(JniInternalTest, RegisterNatives) { release_elements_fn, \ scalar_type, \ expected_class_descriptor) \ + { \ + CheckJniAbortCatcher jni_abort_catcher; \ + /* Allocate an negative sized array and check it has the right failure type. */ \ + env_->new_fn(-1); \ + jni_abort_catcher.Check("negative array length: -1"); \ + env_->new_fn(std::numeric_limits::min()); \ + jni_abort_catcher.Check("negative array length: -2147483648"); \ + } \ jsize size = 4; \ \ /* Allocate an array and check it has the right type and length. */ \ @@ -1116,35 +1133,72 @@ TEST_F(JniInternalTest, ShortArrays) { } TEST_F(JniInternalTest, NewObjectArray) { - // TODO: death tests for negative array sizes. - - // TODO: check non-NULL initial elements. - jclass element_class = env_->FindClass("java/lang/String"); - ASSERT_TRUE(element_class != NULL); + ASSERT_TRUE(element_class != nullptr); jclass array_class = env_->FindClass("[Ljava/lang/String;"); - ASSERT_TRUE(array_class != NULL); + ASSERT_TRUE(array_class != nullptr); - jobjectArray a; - - a = env_->NewObjectArray(0, element_class, NULL); - EXPECT_TRUE(a != NULL); + jobjectArray a = env_->NewObjectArray(0, element_class, nullptr); + EXPECT_TRUE(a != nullptr); EXPECT_TRUE(env_->IsInstanceOf(a, array_class)); EXPECT_EQ(0, env_->GetArrayLength(a)); - a = env_->NewObjectArray(1, element_class, NULL); - EXPECT_TRUE(a != NULL); + a = env_->NewObjectArray(1, element_class, nullptr); + EXPECT_TRUE(a != nullptr); EXPECT_TRUE(env_->IsInstanceOf(a, array_class)); EXPECT_EQ(1, env_->GetArrayLength(a)); - EXPECT_TRUE(env_->IsSameObject(env_->GetObjectArrayElement(a, 0), NULL)); + EXPECT_TRUE(env_->IsSameObject(env_->GetObjectArrayElement(a, 0), nullptr)); +} + +TEST_F(JniInternalTest, NewObjectArrayWithNegativeLength) { + jclass element_class = env_->FindClass("java/lang/String"); + ASSERT_TRUE(element_class != nullptr); + jclass array_class = env_->FindClass("[Ljava/lang/String;"); + ASSERT_TRUE(array_class != nullptr); + CheckJniAbortCatcher jni_abort_catcher; + + env_->NewObjectArray(-1, element_class, nullptr); + jni_abort_catcher.Check("negative array length: -1"); + + env_->NewObjectArray(std::numeric_limits::min(), element_class, nullptr); + jni_abort_catcher.Check("negative array length: -2147483648"); +} + +TEST_F(JniInternalTest, NewObjectArrayWithPrimitiveClasses) { + const char* primitive_descriptors = "VZBSCIJFD"; + const char* primitive_names[] = { + "void", "boolean", "byte", "short", "char", "int", "long", "float", "double" + }; + ASSERT_EQ(strlen(primitive_descriptors), arraysize(primitive_names)); + + CheckJniAbortCatcher jni_abort_catcher; + for (size_t i = 0; i < strlen(primitive_descriptors); ++i) { + jclass primitive_class = GetPrimitiveClass(primitive_descriptors[i]); + env_->NewObjectArray(1, primitive_class, nullptr); + std::string error_msg(StringPrintf("not an object type: %s", primitive_names[i])); + jni_abort_catcher.Check(error_msg.c_str()); + } +} + +TEST_F(JniInternalTest, NewObjectArrayWithInitialValue) { + jclass element_class = env_->FindClass("java/lang/String"); + ASSERT_TRUE(element_class != nullptr); + jclass array_class = env_->FindClass("[Ljava/lang/String;"); + ASSERT_TRUE(array_class != nullptr); jstring s = env_->NewStringUTF("poop"); - a = env_->NewObjectArray(2, element_class, s); - EXPECT_TRUE(a != NULL); + jobjectArray a = env_->NewObjectArray(2, element_class, s); + EXPECT_TRUE(a != nullptr); EXPECT_TRUE(env_->IsInstanceOf(a, array_class)); EXPECT_EQ(2, env_->GetArrayLength(a)); EXPECT_TRUE(env_->IsSameObject(env_->GetObjectArrayElement(a, 0), s)); EXPECT_TRUE(env_->IsSameObject(env_->GetObjectArrayElement(a, 1), s)); + + // Attempt to incorrect create an array of strings with initial value of string arrays. + CheckJniAbortCatcher jni_abort_catcher; + env_->NewObjectArray(2, element_class, a); + jni_abort_catcher.Check("cannot assign object of type 'java.lang.String[]' to array with element " + "type of 'java.lang.String'"); } TEST_F(JniInternalTest, GetArrayLength) { @@ -1251,14 +1305,23 @@ TEST_F(JniInternalTest, NewString) { } TEST_F(JniInternalTest, NewStringNullCharsZeroLength) { - jstring s = env_->NewString(NULL, 0); - EXPECT_TRUE(s != NULL); + jstring s = env_->NewString(nullptr, 0); + EXPECT_TRUE(s != nullptr); EXPECT_EQ(0, env_->GetStringLength(s)); } -// TODO: fix gtest death tests on host http://b/5690440 (and target) -TEST_F(JniInternalTest, DISABLED_NewStringNullCharsNonzeroLength) { - ASSERT_DEATH(env_->NewString(NULL, 1), ""); +TEST_F(JniInternalTest, NewStringNullCharsNonzeroLength) { + CheckJniAbortCatcher jni_abort_catcher; + env_->NewString(nullptr, 1); + jni_abort_catcher.Check("chars == null && char_count > 0"); +} + +TEST_F(JniInternalTest, NewStringNegativeLength) { + CheckJniAbortCatcher jni_abort_catcher; + env_->NewString(nullptr, -1); + jni_abort_catcher.Check("char_count < 0: -1"); + env_->NewString(nullptr, std::numeric_limits::min()); + jni_abort_catcher.Check("char_count < 0: -2147483648"); } TEST_F(JniInternalTest, GetStringLength_GetStringUTFLength) { @@ -1884,8 +1947,24 @@ TEST_F(JniInternalTest, ThrowNew) { EXPECT_TRUE(env_->IsInstanceOf(thrown_exception, exception_class)); } -// TODO: this test is DISABLED until we can actually run java.nio.Buffer's . -TEST_F(JniInternalTest, DISABLED_NewDirectBuffer_GetDirectBufferAddress_GetDirectBufferCapacity) { +TEST_F(JniInternalTest, NewDirectBuffer_GetDirectBufferAddress_GetDirectBufferCapacity) { + // Start runtime. + Thread* self = Thread::Current(); + self->TransitionFromSuspendedToRunnable(); + MakeExecutable(nullptr, "java.lang.Class"); + MakeExecutable(nullptr, "java.lang.Object"); + MakeExecutable(nullptr, "java.nio.DirectByteBuffer"); + MakeExecutable(nullptr, "java.nio.MemoryBlock"); + MakeExecutable(nullptr, "java.nio.MemoryBlock$UnmanagedBlock"); + MakeExecutable(nullptr, "java.nio.MappedByteBuffer"); + MakeExecutable(nullptr, "java.nio.ByteBuffer"); + MakeExecutable(nullptr, "java.nio.Buffer"); + // TODO: we only load a dex file here as starting the runtime relies upon it. + const char* class_name = "StaticLeafMethods"; + LoadDex(class_name); + bool started = runtime_->Start(); + ASSERT_TRUE(started); + jclass buffer_class = env_->FindClass("java/nio/Buffer"); ASSERT_TRUE(buffer_class != NULL); From 801a811c2c59704d326dcde440e58d84ebb22b25 Mon Sep 17 00:00:00 2001 From: Vladimir Marko Date: Mon, 6 Jan 2014 14:28:16 +0000 Subject: [PATCH 0329/2402] Remove duplicate/unnecessary code from ClassLinker. Change-Id: I986c3aa36cb63ae5ea099680e8e4c42bdf891ef1 --- runtime/class_linker.cc | 20 ++------------------ 1 file changed, 2 insertions(+), 18 deletions(-) diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index fbb47bdfaee..0b728a01069 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -2390,22 +2390,7 @@ mirror::Class* ClassLinker::LookupClassFromImage(const char* descriptor) { for (int32_t i = 0; i < dex_caches->GetLength(); ++i) { mirror::DexCache* dex_cache = dex_caches->Get(i); const DexFile* dex_file = dex_cache->GetDexFile(); - // First search using the class def map, but don't bother for non-class types. - if (descriptor[0] == 'L') { - const DexFile::StringId* descriptor_string_id = dex_file->FindStringId(descriptor); - if (descriptor_string_id != NULL) { - const DexFile::TypeId* type_id = - dex_file->FindTypeId(dex_file->GetIndexForStringId(*descriptor_string_id)); - if (type_id != NULL) { - mirror::Class* klass = dex_cache->GetResolvedType(dex_file->GetIndexForTypeId(*type_id)); - if (klass != NULL) { - self->EndAssertNoThreadSuspension(old_no_suspend_cause); - return klass; - } - } - } - } - // Now try binary searching the string/type index. + // Try binary searching the string/type index. const DexFile::StringId* string_id = dex_file->FindStringId(descriptor); if (string_id != NULL) { const DexFile::TypeId* type_id = @@ -4097,11 +4082,10 @@ mirror::Class* ClassLinker::ResolveType(const DexFile& dex_file, uint16_t type_i // Convert a ClassNotFoundException to a NoClassDefFoundError. SirtRef cause(self, self->GetException(NULL)); if (cause->InstanceOf(GetClassRoot(kJavaLangClassNotFoundException))) { - SirtRef sirt_resolved(self, resolved); + DCHECK(resolved == NULL); // No SirtRef needed to preserve resolved. Thread::Current()->ClearException(); ThrowNoClassDefFoundError("Failed resolution of: %s", descriptor); self->GetException(NULL)->SetCause(cause.get()); - resolved = sirt_resolved.get(); } } } From 4044bdac490777cbc8a12d467bec675ef8aa6eb1 Mon Sep 17 00:00:00 2001 From: Jeff Hao Date: Mon, 6 Jan 2014 15:50:45 -0800 Subject: [PATCH 0330/2402] Add sample profiling interface to startMethodTracing. Art side of this change. Also changed libcore and frameworks base. Change-Id: I556678013cf1f4e9bef064a1ae43a6109303797c --- runtime/native/dalvik_system_VMDebug.cc | 14 ++++++++------ runtime/trace.cc | 1 + 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/runtime/native/dalvik_system_VMDebug.cc b/runtime/native/dalvik_system_VMDebug.cc index 6a04c3ad177..4a84cfecffe 100644 --- a/runtime/native/dalvik_system_VMDebug.cc +++ b/runtime/native/dalvik_system_VMDebug.cc @@ -66,7 +66,8 @@ static void VMDebug_startMethodTracingDdmsImpl(JNIEnv*, jclass, jint bufferSize, } static void VMDebug_startMethodTracingFd(JNIEnv* env, jclass, jstring javaTraceFilename, - jobject javaFd, jint bufferSize, jint flags) { + jobject javaFd, jint bufferSize, jint flags, + jboolean samplingEnabled, jint intervalUs) { int originalFd = jniGetFDFromFileDescriptor(env, javaFd); if (originalFd < 0) { return; @@ -85,16 +86,17 @@ static void VMDebug_startMethodTracingFd(JNIEnv* env, jclass, jstring javaTraceF if (traceFilename.c_str() == NULL) { return; } - Trace::Start(traceFilename.c_str(), fd, bufferSize, flags, false, false, 0); + Trace::Start(traceFilename.c_str(), fd, bufferSize, flags, false, samplingEnabled, intervalUs); } static void VMDebug_startMethodTracingFilename(JNIEnv* env, jclass, jstring javaTraceFilename, - jint bufferSize, jint flags) { + jint bufferSize, jint flags, + jboolean samplingEnabled, jint intervalUs) { ScopedUtfChars traceFilename(env, javaTraceFilename); if (traceFilename.c_str() == NULL) { return; } - Trace::Start(traceFilename.c_str(), -1, bufferSize, flags, false, false, 0); + Trace::Start(traceFilename.c_str(), -1, bufferSize, flags, false, samplingEnabled, intervalUs); } static jint VMDebug_getMethodTracingMode(JNIEnv*, jclass) { @@ -325,8 +327,8 @@ static JNINativeMethod gMethods[] = { NATIVE_METHOD(VMDebug, startEmulatorTracing, "()V"), NATIVE_METHOD(VMDebug, startInstructionCounting, "()V"), NATIVE_METHOD(VMDebug, startMethodTracingDdmsImpl, "(IIZI)V"), - NATIVE_METHOD(VMDebug, startMethodTracingFd, "(Ljava/lang/String;Ljava/io/FileDescriptor;II)V"), - NATIVE_METHOD(VMDebug, startMethodTracingFilename, "(Ljava/lang/String;II)V"), + NATIVE_METHOD(VMDebug, startMethodTracingFd, "(Ljava/lang/String;Ljava/io/FileDescriptor;IIZI)V"), + NATIVE_METHOD(VMDebug, startMethodTracingFilename, "(Ljava/lang/String;IIZI)V"), NATIVE_METHOD(VMDebug, stopAllocCounting, "()V"), NATIVE_METHOD(VMDebug, stopEmulatorTracing, "()V"), NATIVE_METHOD(VMDebug, stopInstructionCounting, "()V"), diff --git a/runtime/trace.cc b/runtime/trace.cc index da2c80af882..2dac71ed607 100644 --- a/runtime/trace.cc +++ b/runtime/trace.cc @@ -299,6 +299,7 @@ void Trace::CompareAndUpdateStackTrace(Thread* thread, void* Trace::RunSamplingThread(void* arg) { Runtime* runtime = Runtime::Current(); int interval_us = reinterpret_cast(arg); + CHECK_GE(interval_us, 0); CHECK(runtime->AttachCurrentThread("Sampling Profiler", true, runtime->GetSystemThreadGroup(), !runtime->IsCompiler())); From 0aded089f565008ba5908e395e5914ca4f91f2de Mon Sep 17 00:00:00 2001 From: Dave Allison Date: Thu, 7 Nov 2013 13:15:11 -0800 Subject: [PATCH 0331/2402] Add sampling profiler This adds a sampling profiler thread that runs every so often, gathering profiling data and writing the results to a file in /data/data (specific to app running). The intention is to use these files as input to the compiler so that it can determine the best methods to compile. Bug: 11539952 Change-Id: I0bfbb4146fb7966673c792f017ffac8107b6272d --- runtime/Android.mk | 1 + runtime/barrier.cc | 16 + runtime/barrier.h | 3 + runtime/locks.cc | 4 + runtime/locks.h | 6 +- runtime/native/dalvik_system_VMRuntime.cc | 16 + runtime/profiler.cc | 448 ++++++++++++++++++++++ runtime/profiler.h | 174 +++++++++ runtime/runtime.cc | 71 +++- runtime/runtime.h | 16 + runtime/thread.cc | 343 +++++++++-------- runtime/thread.h | 15 +- runtime/thread_list.cc | 2 +- runtime/thread_state.h | 2 +- 14 files changed, 948 insertions(+), 169 deletions(-) create mode 100644 runtime/profiler.cc create mode 100644 runtime/profiler.h diff --git a/runtime/Android.mk b/runtime/Android.mk index a602c832e9c..576ed1bdac2 100644 --- a/runtime/Android.mk +++ b/runtime/Android.mk @@ -131,6 +131,7 @@ LIBART_COMMON_SRC_FILES := \ thread_pool.cc \ throw_location.cc \ trace.cc \ + profiler.cc \ utf.cc \ utils.cc \ verifier/dex_gc_map.cc \ diff --git a/runtime/barrier.cc b/runtime/barrier.cc index a64499848e9..5f43bec01a1 100644 --- a/runtime/barrier.cc +++ b/runtime/barrier.cc @@ -44,11 +44,27 @@ void Barrier::Init(Thread* self, int count) { void Barrier::Increment(Thread* self, int delta) { MutexLock mu(self, lock_); SetCountLocked(self, count_ + delta); + + // Increment the count. If it becomes zero after the increment + // then all the threads have already passed the barrier. If + // it is non-zero then there is still one or more threads + // that have not yet called the Pass function. When the + // Pass function is called by the last thread, the count will + // be decremented to zero and a Broadcast will be made on the + // condition variable, thus waking this up. if (count_ != 0) { condition_.Wait(self); } } +void Barrier::Increment(Thread* self, int delta, uint32_t timeout_ms) { + MutexLock mu(self, lock_); + SetCountLocked(self, count_ + delta); + if (count_ != 0) { + condition_.TimedWait(self, timeout_ms, 0); + } +} + void Barrier::SetCountLocked(Thread* self, int count) { count_ = count; if (count_ == 0) { diff --git a/runtime/barrier.h b/runtime/barrier.h index 22f08e1a3e5..e335c327be6 100644 --- a/runtime/barrier.h +++ b/runtime/barrier.h @@ -41,6 +41,9 @@ class Barrier { // Increment the count by delta, wait on condition if count is non zero. void Increment(Thread* self, int delta); + // Increment the count by delta, wait on condition if count is non zero, with a timeout + void Increment(Thread* self, int delta, uint32_t timeout_ms) LOCKS_EXCLUDED(lock_); + private: void SetCountLocked(Thread* self, int count) EXCLUSIVE_LOCKS_REQUIRED(lock_); diff --git a/runtime/locks.cc b/runtime/locks.cc index 51a40c383a6..5b462a138f2 100644 --- a/runtime/locks.cc +++ b/runtime/locks.cc @@ -30,6 +30,7 @@ Mutex* Locks::runtime_shutdown_lock_ = NULL; Mutex* Locks::thread_list_lock_ = NULL; Mutex* Locks::thread_suspend_count_lock_ = NULL; Mutex* Locks::trace_lock_ = NULL; +Mutex* Locks::profiler_lock_ = NULL; Mutex* Locks::unexpected_signal_lock_ = NULL; void Locks::Init() { @@ -44,6 +45,7 @@ void Locks::Init() { DCHECK(thread_list_lock_ != NULL); DCHECK(thread_suspend_count_lock_ != NULL); DCHECK(trace_lock_ != NULL); + DCHECK(profiler_lock_ != NULL); DCHECK(unexpected_signal_lock_ != NULL); } else { logging_lock_ = new Mutex("logging lock", kLoggingLock, true); @@ -66,6 +68,8 @@ void Locks::Init() { thread_suspend_count_lock_ = new Mutex("thread suspend count lock", kThreadSuspendCountLock); DCHECK(trace_lock_ == NULL); trace_lock_ = new Mutex("trace lock", kTraceLock); + DCHECK(profiler_lock_ == NULL); + profiler_lock_ = new Mutex("profiler lock", kProfilerLock); DCHECK(unexpected_signal_lock_ == NULL); unexpected_signal_lock_ = new Mutex("unexpected signal lock", kUnexpectedSignalLock, true); } diff --git a/runtime/locks.h b/runtime/locks.h index 72d4f652ff9..341319c1368 100644 --- a/runtime/locks.h +++ b/runtime/locks.h @@ -54,6 +54,7 @@ enum LockLevel { kThreadListLock, kBreakpointInvokeLock, kTraceLock, + kProfilerLock, kJdwpEventListLock, kJdwpAttachLock, kJdwpStartLock, @@ -148,8 +149,11 @@ class Locks { // Guards trace requests. static Mutex* trace_lock_ ACQUIRED_AFTER(breakpoint_lock_); + // Guards profile objects. + static Mutex* profiler_lock_ ACQUIRED_AFTER(trace_lock_); + // Guards lists of classes within the class linker. - static ReaderWriterMutex* classlinker_classes_lock_ ACQUIRED_AFTER(trace_lock_); + static ReaderWriterMutex* classlinker_classes_lock_ ACQUIRED_AFTER(profiler_lock_); // When declaring any Mutex add DEFAULT_MUTEX_ACQUIRED_AFTER to use annotalysis to check the code // doesn't try to hold a higher level Mutex. diff --git a/runtime/native/dalvik_system_VMRuntime.cc b/runtime/native/dalvik_system_VMRuntime.cc index 726a8f17695..789007120dc 100644 --- a/runtime/native/dalvik_system_VMRuntime.cc +++ b/runtime/native/dalvik_system_VMRuntime.cc @@ -485,6 +485,21 @@ static void VMRuntime_preloadDexCaches(JNIEnv* env, jobject) { } } + +/* + * This is called by the framework when it knows the application directory and + * process name. We use this information to start up the sampling profiler for + * for ART. + */ +static void VMRuntime_registerAppInfo(JNIEnv* env, jclass, jstring appDir, jstring procName) { + const char *appDirChars = env->GetStringUTFChars(appDir, NULL); + const char *procNameChars = env->GetStringUTFChars(procName, NULL); + std::string profileFile = std::string(appDirChars) + "/art-profile-" + std::string(procNameChars); + Runtime::Current()->StartProfiler(profileFile.c_str()); + env->ReleaseStringUTFChars(appDir, appDirChars); + env->ReleaseStringUTFChars(procName, procNameChars); +} + static JNINativeMethod gMethods[] = { NATIVE_METHOD(VMRuntime, addressOf, "!(Ljava/lang/Object;)J"), NATIVE_METHOD(VMRuntime, bootClassPath, "()Ljava/lang/String;"), @@ -506,6 +521,7 @@ static JNINativeMethod gMethods[] = { NATIVE_METHOD(VMRuntime, vmVersion, "()Ljava/lang/String;"), NATIVE_METHOD(VMRuntime, vmLibrary, "()Ljava/lang/String;"), NATIVE_METHOD(VMRuntime, preloadDexCaches, "()V"), + NATIVE_METHOD(VMRuntime, registerAppInfo, "(Ljava/lang/String;Ljava/lang/String;)V"), }; void register_dalvik_system_VMRuntime(JNIEnv* env) { diff --git a/runtime/profiler.cc b/runtime/profiler.cc new file mode 100644 index 00000000000..0e738124f70 --- /dev/null +++ b/runtime/profiler.cc @@ -0,0 +1,448 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "profiler.h" + +#include + +#include "base/stl_util.h" +#include "base/unix_file/fd_file.h" +#include "class_linker.h" +#include "common_throws.h" +#include "debugger.h" +#include "dex_file-inl.h" +#include "instrumentation.h" +#include "mirror/art_method-inl.h" +#include "mirror/class-inl.h" +#include "mirror/dex_cache.h" +#include "mirror/object_array-inl.h" +#include "mirror/object-inl.h" +#include "object_utils.h" +#include "os.h" +#include "scoped_thread_state_change.h" +#include "ScopedLocalRef.h" +#include "thread.h" +#include "thread_list.h" +#if !defined(ART_USE_PORTABLE_COMPILER) +#include "entrypoints/quick/quick_entrypoints.h" +#endif + +namespace art { + +BackgroundMethodSamplingProfiler* BackgroundMethodSamplingProfiler::profiler_ = nullptr; +pthread_t BackgroundMethodSamplingProfiler::profiler_pthread_ = 0U; +volatile bool BackgroundMethodSamplingProfiler::shutting_down_ = false; + + +// TODO: this profiler runs regardless of the state of the machine. Maybe we should use the +// wakelock or something to modify the run characteristics. This can be done when we +// have some performance data after it's been used for a while. + + +// This is called from either a thread list traversal or from a checkpoint. Regardless +// of which caller, the mutator lock must be held. +static void GetSample(Thread* thread, void* arg) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + BackgroundMethodSamplingProfiler* profiler = + reinterpret_cast(arg); + mirror::ArtMethod* method = thread->GetCurrentMethod(nullptr); + if (false && method == nullptr) { + LOG(INFO) << "No current method available"; + std::ostringstream os; + thread->Dump(os); + std::string data(os.str()); + LOG(INFO) << data; + } + profiler->RecordMethod(method); +} + + + +// A closure that is called by the thread checkpoint code. +class SampleCheckpoint : public Closure { + public: + explicit SampleCheckpoint(BackgroundMethodSamplingProfiler* const profiler) : + profiler_(profiler) {} + + virtual void Run(Thread* thread) NO_THREAD_SAFETY_ANALYSIS { + Thread* self = Thread::Current(); + if (thread == nullptr) { + LOG(ERROR) << "Checkpoint with nullptr thread"; + return; + } + + // Grab the mutator lock (shared access). + ScopedObjectAccess soa(self); + + // Grab a sample. + GetSample(thread, this->profiler_); + + // And finally tell the barrier that we're done. + this->profiler_->GetBarrier().Pass(self); + } + + private: + BackgroundMethodSamplingProfiler* const profiler_; +}; + +bool BackgroundMethodSamplingProfiler::ShuttingDown(Thread* self) { + MutexLock mu(self, *Locks::profiler_lock_); + return shutting_down_; +} + +void* BackgroundMethodSamplingProfiler::RunProfilerThread(void* arg) { + Runtime* runtime = Runtime::Current(); + BackgroundMethodSamplingProfiler* profiler = + reinterpret_cast(arg); + + // Add a random delay for the first time run so that we don't hammer the CPU + // with all profiles running at the same time. + const int kRandomDelayMaxSecs = 30; + const double kMaxBackoffSecs = 24*60*60; // Max backoff time. + + srand(MicroTime() * getpid()); + int startup_delay = rand() % kRandomDelayMaxSecs; // random delay for startup. + + + CHECK(runtime->AttachCurrentThread("Profiler", true, runtime->GetSystemThreadGroup(), + !runtime->IsCompiler())); + + Thread* self = Thread::Current(); + + while (true) { + if (ShuttingDown(self)) { + break; + } + + { + // wait until we need to run another profile + uint64_t delay_secs = profiler->period_s_ * profiler->backoff_factor_; + + // Add a startup delay to prevent all the profiles running at once. + delay_secs += startup_delay; + + // Immediate startup for benchmarking? + if (profiler->start_immediately_ && startup_delay > 0) { + delay_secs = 0; + } + + startup_delay = 0; + + LOG(DEBUG) << "Delaying profile start for " << delay_secs << " secs"; + MutexLock mu(self, profiler->wait_lock_); + profiler->period_condition_.TimedWait(self, delay_secs * 1000, 0); + + // Expand the backoff by its coefficient, but don't go beyond the max. + double new_backoff = profiler->backoff_factor_ * profiler->backoff_coefficient_; + if (new_backoff < kMaxBackoffSecs) { + profiler->backoff_factor_ = new_backoff; + } + } + + if (ShuttingDown(self)) { + break; + } + + + uint64_t start_us = MicroTime(); + uint64_t end_us = start_us + profiler->duration_s_ * 1000000LL; + uint64_t now_us = start_us; + + LOG(DEBUG) << "Starting profiling run now for " << PrettyDuration((end_us - start_us) * 1000); + + + SampleCheckpoint check_point(profiler); + + while (now_us < end_us) { + if (ShuttingDown(self)) { + break; + } + + usleep(profiler->interval_us_); // Non-interruptible sleep. + + ThreadList* thread_list = runtime->GetThreadList(); + + profiler->profiler_barrier_->Init(self, 0); + size_t barrier_count = thread_list->RunCheckpoint(&check_point); + + ThreadState old_state = self->SetState(kWaitingForCheckPointsToRun); + + // Wait for the barrier to be crossed by all runnable threads. This wait + // is done with a timeout so that we can detect problems with the checkpoint + // running code. We should never see this. + const uint32_t kWaitTimeoutMs = 10000; + const uint32_t kWaitTimeoutUs = kWaitTimeoutMs * 1000; + + uint64_t waitstart_us = MicroTime(); + // Wait for all threads to pass the barrier. + profiler->profiler_barrier_->Increment(self, barrier_count, kWaitTimeoutMs); + uint64_t waitend_us = MicroTime(); + uint64_t waitdiff_us = waitend_us - waitstart_us; + + // We should never get a timeout. If we do, it suggests a problem with the checkpoint + // code. Crash the process in this case. + CHECK_LT(waitdiff_us, kWaitTimeoutUs); + + self->SetState(old_state); + + // Update the current time. + now_us = MicroTime(); + } + + if (!ShuttingDown(self)) { + // After the profile has been taken, write it out. + ScopedObjectAccess soa(self); // Acquire the mutator lock. + uint32_t size = profiler->WriteProfile(); + LOG(DEBUG) << "Profile size: " << size; + } + } + + LOG(INFO) << "Profiler shutdown"; + runtime->DetachCurrentThread(); + return nullptr; +} + +// Write out the profile file if we are generating a profile. +uint32_t BackgroundMethodSamplingProfiler::WriteProfile() { + UniquePtr profile_file; + Runtime* runtime = Runtime::Current(); + std::string classpath = runtime->GetClassPathString(); + size_t colon = classpath.find(':'); + if (colon != std::string::npos) { + // More than one file in the classpath. Possible? + classpath = classpath.substr(0, colon); + } + + std::replace(classpath.begin(), classpath.end(), '/', '@'); + std::string full_name = profile_file_name_; + if (classpath != "") { + full_name = StringPrintf("%s-%s", profile_file_name_.c_str(), classpath.c_str()); + } + LOG(DEBUG) << "Saving profile to " << full_name; + + profile_file.reset(OS::CreateEmptyFile(full_name.c_str())); + if (profile_file.get() == nullptr) { + // Failed to open the profile file, ignore. + LOG(INFO) << "Failed to op file"; + return 0; + } + std::ostringstream os; + uint32_t num_methods = DumpProfile(os); + std::string data(os.str()); + profile_file->WriteFully(data.c_str(), data.length()); + profile_file->Close(); + return num_methods; +} + +// Start a profile thread with the user-supplied arguments. +void BackgroundMethodSamplingProfiler::Start(int period, int duration, + std::string profile_file_name, int interval_us, + double backoff_coefficient, bool startImmediately) { + Thread* self = Thread::Current(); + { + MutexLock mu(self, *Locks::profiler_lock_); + // Don't start two profiler threads. + if (profiler_ != nullptr) { + return; + } + } + + LOG(INFO) << "Starting profile with period " << period << "s, duration " << duration << + "s, interval " << interval_us << "us. Profile file " << profile_file_name; + + { + MutexLock mu(self, *Locks::profiler_lock_); + profiler_ = new BackgroundMethodSamplingProfiler(period, duration, profile_file_name, + backoff_coefficient, + interval_us, startImmediately); + + CHECK_PTHREAD_CALL(pthread_create, (&profiler_pthread_, nullptr, &RunProfilerThread, + reinterpret_cast(profiler_)), + "Profiler thread"); + } +} + + + +void BackgroundMethodSamplingProfiler::Stop() { + BackgroundMethodSamplingProfiler* profiler = nullptr; + pthread_t profiler_pthread = 0U; + { + MutexLock trace_mu(Thread::Current(), *Locks::profiler_lock_); + profiler = profiler_; + shutting_down_ = true; + profiler_pthread = profiler_pthread_; + } + + // Now wake up the sampler thread if it sleeping. + { + MutexLock profile_mu(Thread::Current(), profiler->wait_lock_); + profiler->period_condition_.Signal(Thread::Current()); + } + // Wait for the sample thread to stop. + CHECK_PTHREAD_CALL(pthread_join, (profiler_pthread, nullptr), "profiler thread shutdown"); + + { + MutexLock mu(Thread::Current(), *Locks::profiler_lock_); + profiler_ = nullptr; + } + delete profiler; +} + + +void BackgroundMethodSamplingProfiler::Shutdown() { + Stop(); +} + +BackgroundMethodSamplingProfiler::BackgroundMethodSamplingProfiler(int period, int duration, + std::string profile_file_name, + double backoff_coefficient, int interval_us, bool startImmediately) + : profile_file_name_(profile_file_name), + period_s_(period), start_immediately_(startImmediately), + interval_us_(interval_us), backoff_factor_(1.0), + backoff_coefficient_(backoff_coefficient), duration_s_(duration), + wait_lock_("Profile wait lock"), + period_condition_("Profile condition", wait_lock_), + profile_table_(wait_lock_), + profiler_barrier_(new Barrier(0)) { + // Populate the filtered_methods set. + // This is empty right now, but to add a method, do this: + // + // filtered_methods_.insert("void java.lang.Object.wait(long, int)"); +} + +// A method has been hit, record its invocation in the method map. +// The mutator_lock must be held (shared) when this is called. +void BackgroundMethodSamplingProfiler::RecordMethod(mirror::ArtMethod* method) { + if (method == nullptr) { + profile_table_.NullMethod(); + // Don't record a nullptr method. + return; + } + + mirror::Class* cls = method->GetDeclaringClass(); + if (cls != nullptr) { + if (cls->GetClassLoader() == nullptr) { + // Don't include things in the boot + profile_table_.BootMethod(); + return; + } + } + + bool is_filtered = false; + + MethodHelper mh(method); + if (strcmp(mh.GetName(), "") == 0) { + // always filter out class init + is_filtered = true; + } + + // Filter out methods by name if there are any. + if (!is_filtered && filtered_methods_.size() > 0) { + std::string method_full_name = PrettyMethod(method); + + // Don't include specific filtered methods. + is_filtered = filtered_methods_.count(method_full_name) != 0; + } + + // Add to the profile table unless it is filtered out. + if (!is_filtered) { + profile_table_.Put(method); + } +} + +// Clean out any recordings for the method traces. +void BackgroundMethodSamplingProfiler::CleanProfile() { + profile_table_.Clear(); +} + +uint32_t BackgroundMethodSamplingProfiler::DumpProfile(std::ostream& os) { + return profile_table_.Write(os); +} + +// Profile Table. +// This holds a mapping of mirror::ArtMethod* to a count of how many times a sample +// hit it at the top of the stack. +ProfileSampleResults::ProfileSampleResults(Mutex& lock) : lock_(lock), num_samples_(0), + num_null_methods_(0), + num_boot_methods_(0) { + for (int i = 0; i < kHashSize; i++) { + table[i] = nullptr; + } +} + +ProfileSampleResults::~ProfileSampleResults() { + for (int i = 0; i < kHashSize; i++) { + delete table[i]; + } +} + +// Add a method to the profile table. If it the first time the method +// has been seen, add it with count=1, otherwise increment the count. +void ProfileSampleResults::Put(mirror::ArtMethod* method) { + lock_.Lock(Thread::Current()); + uint32_t index = Hash(method); + if (table[index] == nullptr) { + table[index] = new Map(); + } + Map::iterator i = table[index]->find(method); + if (i == table[index]->end()) { + (*table[index])[method] = 1; + } else { + i->second++; + } + num_samples_++; + lock_.Unlock(Thread::Current()); +} + +// Write the profile table to the output stream. +uint32_t ProfileSampleResults::Write(std::ostream &os) { + ScopedObjectAccess soa(Thread::Current()); + LOG(DEBUG) << "Profile: " << num_samples_ << "/" << num_null_methods_ << "/" << num_boot_methods_; + os << num_samples_ << "/" << num_null_methods_ << "/" << num_boot_methods_ << "\n"; + uint32_t num_methods = 0; + for (int i = 0 ; i < kHashSize; i++) { + Map *map = table[i]; + if (map != nullptr) { + for (const auto &meth_iter : *map) { + mirror::ArtMethod *method = meth_iter.first; + std::string method_name = PrettyMethod(method); + uint32_t method_size = method->GetCodeSize(); + os << StringPrintf("%s/%u/%u\n", method_name.c_str(), meth_iter.second, method_size); + ++num_methods; + } + } + } + return num_methods; +} + +void ProfileSampleResults::Clear() { + num_samples_ = 0; + num_null_methods_ = 0; + num_boot_methods_ = 0; + for (int i = 0; i < kHashSize; i++) { + delete table[i]; + table[i] = nullptr; + } +} + +uint32_t ProfileSampleResults::Hash(mirror::ArtMethod* method) { + uint32_t value = reinterpret_cast(method); + value >>= 2; + return value % kHashSize; +} + +} // namespace art + diff --git a/runtime/profiler.h b/runtime/profiler.h new file mode 100644 index 00000000000..e3af47cf506 --- /dev/null +++ b/runtime/profiler.h @@ -0,0 +1,174 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ART_RUNTIME_PROFILER_H_ +#define ART_RUNTIME_PROFILER_H_ + +#include +#include +#include +#include + +#include "base/macros.h" +#include "globals.h" +#include "instrumentation.h" +#include "os.h" +#include "safe_map.h" +#include "base/mutex.h" +#include "locks.h" +#include "UniquePtr.h" +#include "barrier.h" + +namespace art { + +namespace mirror { + class ArtMethod; + class Class; +} // namespace mirror +class Thread; + + +// +// This class holds all the results for all runs of the profiler. It also +// counts the number of null methods (where we can't determine the method) and +// the number of methods in the boot path (where we have already compiled the method). +// +// This object is an internal profiler object and uses the same locking as the profiler +// itself. +class ProfileSampleResults { + public: + explicit ProfileSampleResults(Mutex& lock); + ~ProfileSampleResults(); + + void Put(mirror::ArtMethod* method); + uint32_t Write(std::ostream &os); + void Clear(); + uint32_t GetNumSamples() { return num_samples_; } + void NullMethod() { ++num_null_methods_; } + void BootMethod() { ++num_boot_methods_; } + private: + uint32_t Hash(mirror::ArtMethod* method); + static constexpr int kHashSize = 17; + Mutex& lock_; // Reference to the main profiler lock - we don't need two of them. + uint32_t num_samples_; // Total number of samples taken. + uint32_t num_null_methods_; // Number of samples where can don't know the method. + uint32_t num_boot_methods_; // Number of samples in the boot path. + + typedef std::map Map; // Map of method vs its count. + Map *table[kHashSize]; +}; + +// +// The BackgroundMethodSamplingProfiler runs in a thread. Most of the time it is sleeping but +// occasionally wakes up and counts the number of times a method is called. Each time +// it ticks, it looks at the current method and records it in the ProfileSampleResults +// table. +// +// The timing is controlled by a number of variables: +// 1. Period: the time between sampling runs. +// 2. Interval: the time between each sample in a run. +// 3. Duration: the duration of a run. +// +// So the profiler thread is sleeping for the 'period' time. It wakes up and runs for the +// 'duration'. The run consists of a series of samples, each of which is 'interval' microseconds +// apart. At the end of a run, it writes the results table to a file and goes back to sleep. + +class BackgroundMethodSamplingProfiler { + public: + static void Start(int period, int duration, std::string profile_filename, int interval_us, + double backoff_coefficient, bool startImmediately) + LOCKS_EXCLUDED(Locks::mutator_lock_, + Locks::thread_list_lock_, + Locks::thread_suspend_count_lock_, + Locks::profiler_lock_); + + static void Stop() LOCKS_EXCLUDED(Locks::profiler_lock_, wait_lock_); + static void Shutdown() LOCKS_EXCLUDED(Locks::profiler_lock_); + + void RecordMethod(mirror::ArtMethod *method) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + Barrier& GetBarrier() { + return *profiler_barrier_; + } + + private: + explicit BackgroundMethodSamplingProfiler(int period, int duration, std::string profile_filename, + double backoff_coefficient, int interval_us, bool startImmediately); + + // The sampling interval in microseconds is passed as an argument. + static void* RunProfilerThread(void* arg) LOCKS_EXCLUDED(Locks::profiler_lock_); + + uint32_t WriteProfile() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + void CleanProfile(); + uint32_t DumpProfile(std::ostream& os) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + static bool ShuttingDown(Thread* self) LOCKS_EXCLUDED(Locks::profiler_lock_); + + static BackgroundMethodSamplingProfiler* profiler_ GUARDED_BY(Locks::profiler_lock_); + + // We need to shut the sample thread down at exit. Setting this to true will do that. + static volatile bool shutting_down_ GUARDED_BY(Locks::profiler_lock_); + + // Sampling thread, non-zero when sampling. + static pthread_t profiler_pthread_; + + // Some measure of the number of samples that are significant + static constexpr uint32_t kSignificantSamples = 10; + + // File to write profile data out to. Cannot be empty if we are profiling. + std::string profile_file_name_; + + // Number of seconds between profile runs. + uint32_t period_s_; + + // Most of the time we want to delay the profiler startup to prevent everything + // running at the same time (all processes). This is the default, but if we + // want to override this, set the 'start_immediately_' to true. This is done + // if the -Xprofile option is given on the command line. + bool start_immediately_; + + uint32_t interval_us_; + + // A backoff coefficent to adjust the profile period based on time. + double backoff_factor_; + + // How much to increase the backoff by on each profile iteration. + double backoff_coefficient_; + + // Duration of each profile run. The profile file will be written at the end + // of each run. + uint32_t duration_s_; + + // Profile condition support. + Mutex wait_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER; + ConditionVariable period_condition_ GUARDED_BY(wait_lock_); + + ProfileSampleResults profile_table_; + + UniquePtr profiler_barrier_; + + // Set of methods to be filtered out. This will probably be rare because + // most of the methods we want to be filtered reside in the boot path and + // are automatically filtered. + typedef std::set FilteredMethods; + FilteredMethods filtered_methods_; + + DISALLOW_COPY_AND_ASSIGN(BackgroundMethodSamplingProfiler); +}; + +} // namespace art + +#endif // ART_RUNTIME_PROFILER_H_ diff --git a/runtime/runtime.cc b/runtime/runtime.cc index 25623a1fe73..5a28b2d2c63 100644 --- a/runtime/runtime.cc +++ b/runtime/runtime.cc @@ -59,6 +59,7 @@ #include "thread.h" #include "thread_list.h" #include "trace.h" +#include "profiler.h" #include "UniquePtr.h" #include "verifier/method_verifier.h" #include "well_known_classes.h" @@ -331,6 +332,24 @@ size_t ParseIntegerOrDie(const std::string& s) { return result; } +double ParseDoubleOrDie(const std::string& option, const char* prefix, + double min, double max, bool ignore_unrecognized, + double defval) { + std::istringstream iss(option.substr(strlen(prefix))); + double value; + iss >> value; + // Ensure that we have a value, there was no cruft after it and it satisfies a sensible range. + const bool sane_val = iss.eof() && (value >= min) && (value <= max); + if (!sane_val) { + if (ignore_unrecognized) { + return defval; + } + LOG(FATAL)<< "Invalid option '" << option << "'"; + return defval; + } + return value; +} + void Runtime::SweepSystemWeaks(RootVisitor* visitor, void* arg) { GetInternTable()->SweepInternTableWeaks(visitor, arg); GetMonitorList()->SweepMonitorList(visitor, arg); @@ -408,6 +427,12 @@ Runtime::ParsedOptions* Runtime::ParsedOptions::Create(const Options& options, b parsed->method_trace_file_ = "/data/method-trace-file.bin"; parsed->method_trace_file_size_ = 10 * MB; + parsed->profile_ = false; + parsed->profile_period_s_ = 10; // Seconds. + parsed->profile_duration_s_ = 20; // Seconds. + parsed->profile_interval_us_ = 500; // Microseconds. + parsed->profile_backoff_coefficient_ = 2.0; + for (size_t i = 0; i < options.size(); ++i) { const std::string option(options[i].first); if (true && options[0].first == "-Xzygote") { @@ -495,19 +520,9 @@ Runtime::ParsedOptions* Runtime::ParsedOptions::Create(const Options& options, b } parsed->heap_max_free_ = size; } else if (StartsWith(option, "-XX:HeapTargetUtilization=")) { - std::istringstream iss(option.substr(strlen("-XX:HeapTargetUtilization="))); - double value; - iss >> value; - // Ensure that we have a value, there was no cruft after it and it satisfies a sensible range. - const bool sane_val = iss.eof() && (value >= 0.1) && (value <= 0.9); - if (!sane_val) { - if (ignore_unrecognized) { - continue; - } - LOG(FATAL) << "Invalid option '" << option << "'"; - return NULL; - } - parsed->heap_target_utilization_ = value; + parsed->heap_target_utilization_ = ParseDoubleOrDie(option, "-XX:HeapTargetUtilization=", + 0.1, 0.9, ignore_unrecognized, + parsed->heap_target_utilization_); } else if (StartsWith(option, "-XX:ParallelGCThreads=")) { parsed->parallel_gc_threads_ = ParseMemoryOption(option.substr(strlen("-XX:ParallelGCThreads=")).c_str(), 1024); @@ -631,6 +646,19 @@ Runtime::ParsedOptions* Runtime::ParsedOptions::Create(const Options& options, b Trace::SetDefaultClockSource(kProfilerClockSourceWall); } else if (option == "-Xprofile:dualclock") { Trace::SetDefaultClockSource(kProfilerClockSourceDual); + } else if (StartsWith(option, "-Xprofile:")) { + parsed->profile_output_filename_ = option.substr(strlen("-Xprofile:")); + parsed->profile_ = true; + } else if (StartsWith(option, "-Xprofile-period:")) { + parsed->profile_period_s_ = ParseIntegerOrDie(option); + } else if (StartsWith(option, "-Xprofile-duration:")) { + parsed->profile_duration_s_ = ParseIntegerOrDie(option); + } else if (StartsWith(option, "-Xprofile-interval:")) { + parsed->profile_interval_us_ = ParseIntegerOrDie(option); + } else if (StartsWith(option, "-Xprofile-backoff:")) { + parsed->profile_backoff_coefficient_ = ParseDoubleOrDie(option, "-Xprofile-backoff:", + 1.0, 10.0, ignore_unrecognized, + parsed->profile_backoff_coefficient_); } else if (option == "-compiler-filter:interpret-only") { parsed->compiler_filter_ = kInterpretOnly; } else if (option == "-compiler-filter:space") { @@ -779,6 +807,11 @@ bool Runtime::Start() { finished_starting_ = true; + if (profile_) { + // User has asked for a profile using -Xprofile + StartProfiler(profile_output_filename_.c_str(), true); + } + return true; } @@ -970,6 +1003,14 @@ bool Runtime::Init(const Options& raw_options, bool ignore_unrecognized) { method_trace_file_ = options->method_trace_file_; method_trace_file_size_ = options->method_trace_file_size_; + // Extract the profile options. + profile_period_s_ = options->profile_period_s_; + profile_duration_s_ = options->profile_duration_s_; + profile_interval_us_ = options->profile_interval_us_; + profile_backoff_coefficient_ = options->profile_backoff_coefficient_; + profile_ = options->profile_; + profile_output_filename_ = options->profile_output_filename_; + if (options->method_trace_) { Trace::Start(options->method_trace_file_.c_str(), -1, options->method_trace_file_size_, 0, false, false, 0); @@ -1401,4 +1442,8 @@ void Runtime::RemoveMethodVerifier(verifier::MethodVerifier* verifier) { method_verifiers_.erase(it); } +void Runtime::StartProfiler(const char *appDir, bool startImmediately) { + BackgroundMethodSamplingProfiler::Start(profile_period_s_, profile_duration_s_, appDir, profile_interval_us_, + profile_backoff_coefficient_, startImmediately); +} } // namespace art diff --git a/runtime/runtime.h b/runtime/runtime.h index 7b57dda7805..50da0dcfab4 100644 --- a/runtime/runtime.h +++ b/runtime/runtime.h @@ -139,6 +139,12 @@ class Runtime { size_t tiny_method_threshold_; size_t num_dex_methods_threshold_; bool sea_ir_mode_; + bool profile_; + std::string profile_output_filename_; + int profile_period_s_; + int profile_duration_s_; + int profile_interval_us_; + double profile_backoff_coefficient_; private: ParsedOptions() {} @@ -455,6 +461,8 @@ class Runtime { const std::vector& GetCompileTimeClassPath(jobject class_loader); void SetCompileTimeClassPath(jobject class_loader, std::vector& class_path); + void StartProfiler(const char *appDir, bool startImmediately = false); + private: static void InitPlatformSignalHandlers(); @@ -566,6 +574,14 @@ class Runtime { bool stats_enabled_; RuntimeStats stats_; + // Runtime profile support. + bool profile_; + std::string profile_output_filename_; + uint32_t profile_period_s_; // Generate profile every n seconds. + uint32_t profile_duration_s_; // Run profile for n seconds. + uint32_t profile_interval_us_; // Microseconds between samples. + double profile_backoff_coefficient_; // Coefficient to exponential backoff. + bool method_trace_; std::string method_trace_file_; size_t method_trace_file_size_; diff --git a/runtime/thread.cc b/runtime/thread.cc index 9faa60dbf8c..e2d51b7a7e9 100644 --- a/runtime/thread.cc +++ b/runtime/thread.cc @@ -73,7 +73,7 @@ namespace art { bool Thread::is_started_ = false; pthread_key_t Thread::pthread_key_self_; -ConditionVariable* Thread::resume_cond_ = NULL; +ConditionVariable* Thread::resume_cond_ = nullptr; static const char* kThreadNameDuringStartup = ""; @@ -124,7 +124,7 @@ void Thread::SetDeoptimizationReturnValue(const JValue& ret_val) { ShadowFrame* Thread::GetAndClearDeoptimizationShadowFrame(JValue* ret_val) { ShadowFrame* sf = deoptimization_shadow_frame_; - deoptimization_shadow_frame_ = NULL; + deoptimization_shadow_frame_ = nullptr; ret_val->SetJ(deoptimization_return_value_.GetJ()); return sf; } @@ -142,14 +142,14 @@ void Thread::InitAfterFork() { void* Thread::CreateCallback(void* arg) { Thread* self = reinterpret_cast(arg); Runtime* runtime = Runtime::Current(); - if (runtime == NULL) { + if (runtime == nullptr) { LOG(ERROR) << "Thread attaching to non-existent runtime: " << *self; - return NULL; + return nullptr; } { // TODO: pass self to MutexLock - requires self to equal Thread::Current(), which is only true // after self->Init(). - MutexLock mu(NULL, *Locks::runtime_shutdown_lock_); + MutexLock mu(nullptr, *Locks::runtime_shutdown_lock_); // Check that if we got here we cannot be shutting down (as shutdown should never have started // while threads are being born). CHECK(!runtime->IsShuttingDownLocked()); @@ -160,10 +160,10 @@ void* Thread::CreateCallback(void* arg) { ScopedObjectAccess soa(self); // Copy peer into self, deleting global reference when done. - CHECK(self->jpeer_ != NULL); + CHECK(self->jpeer_ != nullptr); self->opeer_ = soa.Decode(self->jpeer_); self->GetJniEnv()->DeleteGlobalRef(self->jpeer_); - self->jpeer_ = NULL; + self->jpeer_ = nullptr; { SirtRef thread_name(self, self->GetThreadName(soa)); @@ -177,14 +177,14 @@ void* Thread::CreateCallback(void* arg) { mirror::ArtMethod* m = receiver->GetClass()->FindVirtualMethodForVirtualOrInterface(soa.DecodeMethod(mid)); JValue result; - ArgArray arg_array(NULL, 0); + ArgArray arg_array(nullptr, 0); arg_array.Append(reinterpret_cast(receiver)); m->Invoke(self, arg_array.GetArray(), arg_array.GetNumBytes(), &result, 'V'); } // Detach and delete self. Runtime::Current()->GetThreadList()->Unregister(self); - return NULL; + return nullptr; } Thread* Thread::FromManagedThread(const ScopedObjectAccessUnchecked& soa, @@ -195,7 +195,7 @@ Thread* Thread::FromManagedThread(const ScopedObjectAccessUnchecked& soa, // to stop it from going away. if (kIsDebugBuild) { MutexLock mu(soa.Self(), *Locks::thread_suspend_count_lock_); - if (result != NULL && !result->IsSuspended()) { + if (result != nullptr && !result->IsSuspended()) { Locks::thread_list_lock_->AssertHeld(soa.Self()); } } @@ -233,7 +233,7 @@ static size_t FixStackSize(size_t stack_size) { } void Thread::CreateNativeThread(JNIEnv* env, jobject java_peer, size_t stack_size, bool is_daemon) { - CHECK(java_peer != NULL); + CHECK(java_peer != nullptr); Thread* self = static_cast(env)->self; Runtime* runtime = Runtime::Current(); @@ -279,9 +279,9 @@ void Thread::CreateNativeThread(JNIEnv* env, jobject java_peer, size_t stack_siz } // Manually delete the global reference since Thread::Init will not have been run. env->DeleteGlobalRef(child_thread->jpeer_); - child_thread->jpeer_ = NULL; + child_thread->jpeer_ = nullptr; delete child_thread; - child_thread = NULL; + child_thread = nullptr; // TODO: remove from thread group? env->SetIntField(java_peer, WellKnownClasses::java_lang_Thread_nativePeer, 0); { @@ -298,7 +298,7 @@ void Thread::Init(ThreadList* thread_list, JavaVMExt* java_vm) { // (When we create a new thread from managed code, we allocate the Thread* in Thread::Create so // we can handshake with the corresponding native thread when it's ready.) Check this native // thread hasn't been through here already... - CHECK(Thread::Current() == NULL); + CHECK(Thread::Current() == nullptr); SetUpAlternateSignalStack(); InitCpu(); InitTlsEntryPoints(); @@ -322,15 +322,15 @@ Thread* Thread::Attach(const char* thread_name, bool as_daemon, jobject thread_g bool create_peer) { Thread* self; Runtime* runtime = Runtime::Current(); - if (runtime == NULL) { + if (runtime == nullptr) { LOG(ERROR) << "Thread attaching to non-existent runtime: " << thread_name; - return NULL; + return nullptr; } { - MutexLock mu(NULL, *Locks::runtime_shutdown_lock_); + MutexLock mu(nullptr, *Locks::runtime_shutdown_lock_); if (runtime->IsShuttingDownLocked()) { LOG(ERROR) << "Thread attaching while runtime is shutting down: " << thread_name; - return NULL; + return nullptr; } else { Runtime::Current()->StartThreadBirth(); self = new Thread(as_daemon); @@ -350,7 +350,7 @@ Thread* Thread::Attach(const char* thread_name, bool as_daemon, jobject thread_g self->CreatePeer(thread_name, as_daemon, thread_group); } else { // These aren't necessary, but they improve diagnostics for unit tests & command-line tools. - if (thread_name != NULL) { + if (thread_name != nullptr) { self->name_->assign(thread_name); ::art::SetThreadName(thread_name); } @@ -364,7 +364,7 @@ void Thread::CreatePeer(const char* name, bool as_daemon, jobject thread_group) CHECK(runtime->IsStarted()); JNIEnv* env = jni_env_; - if (thread_group == NULL) { + if (thread_group == nullptr) { thread_group = runtime->GetMainThreadGroup(); } ScopedLocalRef thread_name(env, env->NewStringUTF(name)); @@ -372,7 +372,7 @@ void Thread::CreatePeer(const char* name, bool as_daemon, jobject thread_group) jboolean thread_is_daemon = as_daemon; ScopedLocalRef peer(env, env->AllocObject(WellKnownClasses::java_lang_Thread)); - if (peer.get() == NULL) { + if (peer.get() == nullptr) { CHECK(IsExceptionPending()); return; } @@ -393,7 +393,7 @@ void Thread::CreatePeer(const char* name, bool as_daemon, jobject thread_group) ScopedObjectAccess soa(self); SirtRef peer_thread_name(soa.Self(), GetThreadName(soa)); - if (peer_thread_name.get() == NULL) { + if (peer_thread_name.get() == nullptr) { // The Thread constructor should have set the Thread.name to a // non-null value. However, because we can run without code // available (in the compiler, in tests), we manually assign the @@ -409,7 +409,7 @@ void Thread::CreatePeer(const char* name, bool as_daemon, jobject thread_group) peer_thread_name.reset(GetThreadName(soa)); } // 'thread_name' may have been null, so don't trust 'peer_thread_name' to be non-null. - if (peer_thread_name.get() != NULL) { + if (peer_thread_name.get() != nullptr) { SetThreadName(peer_thread_name->ToModifiedUtf8().c_str()); } } @@ -495,7 +495,7 @@ void Thread::Dump(std::ostream& os) const { mirror::String* Thread::GetThreadName(const ScopedObjectAccessUnchecked& soa) const { mirror::ArtField* f = soa.DecodeField(WellKnownClasses::java_lang_Thread_name); - return (opeer_ != NULL) ? reinterpret_cast(f->GetObject(opeer_)) : NULL; + return (opeer_ != nullptr) ? reinterpret_cast(f->GetObject(opeer_)) : nullptr; } void Thread::GetThreadName(std::string& name) const { @@ -570,12 +570,33 @@ void Thread::ModifySuspendCount(Thread* self, int delta, bool for_debugger) { } void Thread::RunCheckpointFunction() { - CHECK(checkpoint_function_ != NULL); - ATRACE_BEGIN("Checkpoint function"); - checkpoint_function_->Run(this); - ATRACE_END(); - checkpoint_function_ = NULL; - AtomicClearFlag(kCheckpointRequest); + Closure *checkpoints[kMaxCheckpoints]; + + // Grab the suspend_count lock and copy the current set of + // checkpoints. Then clear the list and the flag. The RequestCheckpoint + // function will also grab this lock so we prevent a race between setting + // the kCheckpointRequest flag and clearing it. + { + MutexLock mu(this, *Locks::thread_suspend_count_lock_); + for (uint32_t i = 0; i < kMaxCheckpoints; ++i) { + checkpoints[i] = checkpoint_functions_[i]; + checkpoint_functions_[i] = nullptr; + } + AtomicClearFlag(kCheckpointRequest); + } + + // Outside the lock, run all the checkpoint functions that + // we collected. + bool found_checkpoint = false; + for (uint32_t i = 0; i < kMaxCheckpoints; ++i) { + if (checkpoints[i] != nullptr) { + ATRACE_BEGIN("Checkpoint function"); + checkpoints[i]->Run(this); + ATRACE_END(); + found_checkpoint = true; + } + } + CHECK(found_checkpoint); } bool Thread::RequestCheckpoint(Closure* function) { @@ -584,23 +605,34 @@ bool Thread::RequestCheckpoint(Closure* function) { if (old_state_and_flags.as_struct.state != kRunnable) { return false; // Fail, thread is suspended and so can't run a checkpoint. } - if ((old_state_and_flags.as_struct.flags & kCheckpointRequest) != 0) { - return false; // Fail, already a checkpoint pending. + + uint32_t available_checkpoint = kMaxCheckpoints; + for (uint32_t i = 0 ; i < kMaxCheckpoints; ++i) { + if (checkpoint_functions_[i] == nullptr) { + available_checkpoint = i; + break; + } } - CHECK(checkpoint_function_ == nullptr); - checkpoint_function_ = function; + if (available_checkpoint == kMaxCheckpoints) { + // No checkpoint functions available, we can't run a checkpoint + return false; + } + checkpoint_functions_[available_checkpoint] = function; + // Checkpoint function installed now install flag bit. // We must be runnable to request a checkpoint. DCHECK_EQ(old_state_and_flags.as_struct.state, kRunnable); union StateAndFlags new_state_and_flags; new_state_and_flags.as_int = old_state_and_flags.as_int; new_state_and_flags.as_struct.flags |= kCheckpointRequest; - int succeeded = android_atomic_cmpxchg(old_state_and_flags.as_int, new_state_and_flags.as_int, + int succeeded = android_atomic_acquire_cas(old_state_and_flags.as_int, new_state_and_flags.as_int, &state_and_flags_.as_int); if (UNLIKELY(succeeded != 0)) { // The thread changed state before the checkpoint was installed. - CHECK(checkpoint_function_ == function); - checkpoint_function_ = NULL; + CHECK_EQ(checkpoint_functions_[available_checkpoint], function); + checkpoint_functions_[available_checkpoint] = nullptr; + } else { + CHECK_EQ(ReadFlag(kCheckpointRequest), true); } return succeeded == 0; } @@ -622,7 +654,7 @@ void Thread::DumpState(std::ostream& os, const Thread* thread, pid_t tid) { bool is_daemon = false; Thread* self = Thread::Current(); - if (self != NULL && thread != NULL && thread->opeer_ != NULL) { + if (self != nullptr && thread != nullptr && thread->opeer_ != nullptr) { ScopedObjectAccessUnchecked soa(self); priority = soa.DecodeField(WellKnownClasses::java_lang_Thread_priority)->GetInt(thread->opeer_); is_daemon = soa.DecodeField(WellKnownClasses::java_lang_Thread_daemon)->GetBoolean(thread->opeer_); @@ -630,12 +662,12 @@ void Thread::DumpState(std::ostream& os, const Thread* thread, pid_t tid) { mirror::Object* thread_group = soa.DecodeField(WellKnownClasses::java_lang_Thread_group)->GetObject(thread->opeer_); - if (thread_group != NULL) { + if (thread_group != nullptr) { mirror::ArtField* group_name_field = soa.DecodeField(WellKnownClasses::java_lang_ThreadGroup_name); mirror::String* group_name_string = reinterpret_cast(group_name_field->GetObject(thread_group)); - group_name = (group_name_string != NULL) ? group_name_string->ToModifiedUtf8() : ""; + group_name = (group_name_string != nullptr) ? group_name_string->ToModifiedUtf8() : ""; } } else { priority = GetNativePriority(); @@ -646,7 +678,7 @@ void Thread::DumpState(std::ostream& os, const Thread* thread, pid_t tid) { scheduler_group_name = "default"; } - if (thread != NULL) { + if (thread != nullptr) { os << '"' << *thread->name_ << '"'; if (is_daemon) { os << " daemon"; @@ -664,7 +696,7 @@ void Thread::DumpState(std::ostream& os, const Thread* thread, pid_t tid) { << " (not attached)\n"; } - if (thread != NULL) { + if (thread != nullptr) { MutexLock mu(self, *Locks::thread_suspend_count_lock_); os << " | group=\"" << group_name << "\"" << " sCount=" << thread->suspend_count_ @@ -676,7 +708,7 @@ void Thread::DumpState(std::ostream& os, const Thread* thread, pid_t tid) { os << " | sysTid=" << tid << " nice=" << getpriority(PRIO_PROCESS, tid) << " cgrp=" << scheduler_group_name; - if (thread != NULL) { + if (thread != nullptr) { int policy; sched_param sp; CHECK_PTHREAD_CALL(pthread_getschedparam, (thread->pthread_self_, &policy, &sp), __FUNCTION__); @@ -705,7 +737,7 @@ void Thread::DumpState(std::ostream& os, const Thread* thread, pid_t tid) { << " stm=" << stime << " core=" << task_cpu << " HZ=" << sysconf(_SC_CLK_TCK) << "\n"; - if (thread != NULL) { + if (thread != nullptr) { os << " | stack=" << reinterpret_cast(thread->stack_begin_) << "-" << reinterpret_cast(thread->stack_end_) << " stackSize=" << PrettySize(thread->stack_size_) << "\n"; } @@ -719,7 +751,7 @@ struct StackDumpVisitor : public StackVisitor { StackDumpVisitor(std::ostream& os, Thread* thread, Context* context, bool can_allocate) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) : StackVisitor(thread, context), os(os), thread(thread), can_allocate(can_allocate), - last_method(NULL), last_line_number(0), repetition_count(0), frame_count(0) { + last_method(nullptr), last_line_number(0), repetition_count(0), frame_count(0) { } virtual ~StackDumpVisitor() { @@ -737,12 +769,12 @@ struct StackDumpVisitor : public StackVisitor { mirror::Class* c = m->GetDeclaringClass(); const mirror::DexCache* dex_cache = c->GetDexCache(); int line_number = -1; - if (dex_cache != NULL) { // be tolerant of bad input + if (dex_cache != nullptr) { // be tolerant of bad input const DexFile& dex_file = *dex_cache->GetDexFile(); line_number = dex_file.GetLineNumFromPC(m, GetDexPc()); } if (line_number == last_line_number && last_method == m) { - repetition_count++; + ++repetition_count; } else { if (repetition_count >= kMaxRepetition) { os << " ... repeated " << (repetition_count - kMaxRepetition) << " times\n"; @@ -758,7 +790,7 @@ struct StackDumpVisitor : public StackVisitor { } else { mh.ChangeMethod(m); const char* source_file(mh.GetDeclaringClassSourceFile()); - os << "(" << (source_file != NULL ? source_file : "unavailable") + os << "(" << (source_file != nullptr ? source_file : "unavailable") << ":" << line_number << ")"; } os << "\n"; @@ -808,8 +840,8 @@ static bool ShouldShowNativeStack(const Thread* thread) // We don't just check kNative because native methods will be in state kSuspended if they're // calling back into the VM, or kBlocked if they're blocked on a monitor, or one of the // thread-startup states if it's early enough in their life cycle (http://b/7432159). - mirror::ArtMethod* current_method = thread->GetCurrentMethod(NULL); - return current_method != NULL && current_method->IsNative(); + mirror::ArtMethod* current_method = thread->GetCurrentMethod(nullptr); + return current_method != nullptr && current_method->IsNative(); } void Thread::DumpStack(std::ostream& os) const { @@ -850,11 +882,11 @@ void Thread::Startup() { { // MutexLock to keep annotalysis happy. // - // Note we use NULL for the thread because Thread::Current can + // Note we use nullptr for the thread because Thread::Current can // return garbage since (is_started_ == true) and // Thread::pthread_key_self_ is not yet initialized. // This was seen on glibc. - MutexLock mu(NULL, *Locks::thread_suspend_count_lock_); + MutexLock mu(nullptr, *Locks::thread_suspend_count_lock_); resume_cond_ = new ConditionVariable("Thread resumption condition variable", *Locks::thread_suspend_count_lock_); } @@ -863,8 +895,8 @@ void Thread::Startup() { CHECK_PTHREAD_CALL(pthread_key_create, (&Thread::pthread_key_self_, Thread::ThreadExitCallback), "self key"); // Double-check the TLS slot allocation. - if (pthread_getspecific(pthread_key_self_) != NULL) { - LOG(FATAL) << "Newly-created pthread TLS slot is not NULL"; + if (pthread_getspecific(pthread_key_self_) != nullptr) { + LOG(FATAL) << "Newly-created pthread TLS slot is not nullptr"; } } @@ -884,50 +916,49 @@ void Thread::Shutdown() { is_started_ = false; CHECK_PTHREAD_CALL(pthread_key_delete, (Thread::pthread_key_self_), "self key"); MutexLock mu(Thread::Current(), *Locks::thread_suspend_count_lock_); - if (resume_cond_ != NULL) { + if (resume_cond_ != nullptr) { delete resume_cond_; - resume_cond_ = NULL; + resume_cond_ = nullptr; } } Thread::Thread(bool daemon) : suspend_count_(0), - card_table_(NULL), - exception_(NULL), - stack_end_(NULL), + card_table_(nullptr), + exception_(nullptr), + stack_end_(nullptr), managed_stack_(), - jni_env_(NULL), - self_(NULL), - opeer_(NULL), - jpeer_(NULL), - stack_begin_(NULL), + jni_env_(nullptr), + self_(nullptr), + opeer_(nullptr), + jpeer_(nullptr), + stack_begin_(nullptr), stack_size_(0), thin_lock_thread_id_(0), - stack_trace_sample_(NULL), + stack_trace_sample_(nullptr), trace_clock_base_(0), tid_(0), wait_mutex_(new Mutex("a thread wait mutex")), wait_cond_(new ConditionVariable("a thread wait condition variable", *wait_mutex_)), - wait_monitor_(NULL), + wait_monitor_(nullptr), interrupted_(false), - wait_next_(NULL), - monitor_enter_object_(NULL), - top_sirt_(NULL), - runtime_(NULL), - class_loader_override_(NULL), - long_jump_context_(NULL), + wait_next_(nullptr), + monitor_enter_object_(nullptr), + top_sirt_(nullptr), + runtime_(nullptr), + class_loader_override_(nullptr), + long_jump_context_(nullptr), throwing_OutOfMemoryError_(false), debug_suspend_count_(0), debug_invoke_req_(new DebugInvokeReq), single_step_control_(new SingleStepControl), - deoptimization_shadow_frame_(NULL), + deoptimization_shadow_frame_(nullptr), instrumentation_stack_(new std::deque), name_(new std::string(kThreadNameDuringStartup)), daemon_(daemon), pthread_self_(0), no_thread_suspension_(0), - last_no_thread_suspension_cause_(NULL), - checkpoint_function_(0), + last_no_thread_suspension_cause_(nullptr), thread_exit_check_count_(0), thread_local_start_(nullptr), thread_local_pos_(nullptr), @@ -938,22 +969,25 @@ Thread::Thread(bool daemon) state_and_flags_.as_struct.state = kNative; memset(&held_mutexes_[0], 0, sizeof(held_mutexes_)); memset(rosalloc_runs_, 0, sizeof(rosalloc_runs_)); + for (uint32_t i = 0; i < kMaxCheckpoints; ++i) { + checkpoint_functions_[i] = nullptr; + } } bool Thread::IsStillStarting() const { // You might think you can check whether the state is kStarting, but for much of thread startup, // the thread is in kNative; it might also be in kVmWait. - // You might think you can check whether the peer is NULL, but the peer is actually created and + // You might think you can check whether the peer is nullptr, but the peer is actually created and // assigned fairly early on, and needs to be. // It turns out that the last thing to change is the thread name; that's a good proxy for "has // this thread _ever_ entered kRunnable". - return (jpeer_ == NULL && opeer_ == NULL) || (*name_ == kThreadNameDuringStartup); + return (jpeer_ == nullptr && opeer_ == nullptr) || (*name_ == kThreadNameDuringStartup); } void Thread::AssertNoPendingException() const { if (UNLIKELY(IsExceptionPending())) { ScopedObjectAccess soa(Thread::Current()); - mirror::Throwable* exception = GetException(NULL); + mirror::Throwable* exception = GetException(nullptr); LOG(FATAL) << "No pending exception expected: " << exception->Dump(); } } @@ -976,7 +1010,7 @@ void Thread::Destroy() { Thread* self = this; DCHECK_EQ(self, Thread::Current()); - if (opeer_ != NULL) { + if (opeer_ != nullptr) { ScopedObjectAccess soa(self); // We may need to call user-supplied managed code, do this before final clean-up. HandleUncaughtExceptions(soa); @@ -999,30 +1033,35 @@ void Thread::Destroy() { } // On thread detach, all monitors entered with JNI MonitorEnter are automatically exited. - if (jni_env_ != NULL) { + if (jni_env_ != nullptr) { jni_env_->monitors.VisitRoots(MonitorExitVisitor, self); } } Thread::~Thread() { - if (jni_env_ != NULL && jpeer_ != NULL) { + if (jni_env_ != nullptr && jpeer_ != nullptr) { // If pthread_create fails we don't have a jni env here. jni_env_->DeleteGlobalRef(jpeer_); - jpeer_ = NULL; + jpeer_ = nullptr; } - opeer_ = NULL; + opeer_ = nullptr; delete jni_env_; - jni_env_ = NULL; + jni_env_ = nullptr; CHECK_NE(GetState(), kRunnable); + CHECK_NE(ReadFlag(kCheckpointRequest), true); + CHECK(checkpoint_functions_[0] == nullptr); + CHECK(checkpoint_functions_[1] == nullptr); + CHECK(checkpoint_functions_[2] == nullptr); + // We may be deleting a still born thread. SetStateUnsafe(kTerminated); delete wait_cond_; delete wait_mutex_; - if (long_jump_context_ != NULL) { + if (long_jump_context_ != nullptr) { delete long_jump_context_; } @@ -1052,7 +1091,7 @@ void Thread::HandleUncaughtExceptions(ScopedObjectAccess& soa) { ScopedLocalRef handler(jni_env_, jni_env_->GetObjectField(peer.get(), WellKnownClasses::java_lang_Thread_uncaughtHandler)); - if (handler.get() == NULL) { + if (handler.get() == nullptr) { // Otherwise use the thread group's default handler. handler.reset(jni_env_->GetObjectField(peer.get(), WellKnownClasses::java_lang_Thread_group)); } @@ -1070,7 +1109,7 @@ void Thread::RemoveFromThreadGroup(ScopedObjectAccess& soa) { // this.group.removeThread(this); // group can be null if we're in the compiler or a test. mirror::Object* ogroup = soa.DecodeField(WellKnownClasses::java_lang_Thread_group)->GetObject(opeer_); - if (ogroup != NULL) { + if (ogroup != nullptr) { ScopedLocalRef group(soa.Env(), soa.AddLocalReference(ogroup)); ScopedLocalRef peer(soa.Env(), soa.AddLocalReference(opeer_)); ScopedThreadStateChange tsc(soa.Self(), kNative); @@ -1101,7 +1140,7 @@ bool Thread::SirtContains(jobject obj) const { void Thread::SirtVisitRoots(RootVisitor* visitor, void* arg) { for (StackIndirectReferenceTable* cur = top_sirt_; cur; cur = cur->GetLink()) { size_t num_refs = cur->NumberOfReferences(); - for (size_t j = 0; j < num_refs; j++) { + for (size_t j = 0; j < num_refs; ++j) { mirror::Object* object = cur->GetReference(j); if (object != nullptr) { const mirror::Object* new_obj = visitor(object, arg); @@ -1116,8 +1155,8 @@ void Thread::SirtVisitRoots(RootVisitor* visitor, void* arg) { mirror::Object* Thread::DecodeJObject(jobject obj) const { Locks::mutator_lock_->AssertSharedHeld(this); - if (obj == NULL) { - return NULL; + if (obj == nullptr) { + return nullptr; } IndirectRef ref = reinterpret_cast(obj); IndirectRefKind kind = GetIndirectRefKind(ref); @@ -1146,13 +1185,13 @@ mirror::Object* Thread::DecodeJObject(jobject obj) const { DCHECK_EQ(kind, kWeakGlobal); result = Runtime::Current()->GetJavaVM()->DecodeWeakGlobal(const_cast(this), ref); if (result == kClearedJniWeakGlobal) { - // This is a special case where it's okay to return NULL. + // This is a special case where it's okay to return nullptr. return nullptr; } } - if (UNLIKELY(result == NULL)) { - JniAbortF(NULL, "use of deleted %s %p", ToStr(kind).c_str(), obj); + if (UNLIKELY(result == nullptr)) { + JniAbortF(nullptr, "use of deleted %s %p", ToStr(kind).c_str(), obj); } else { if (kIsDebugBuild && (result != kInvalidIndirectRefObject)) { Runtime::Current()->GetHeap()->VerifyObject(result); @@ -1192,7 +1231,7 @@ void Thread::Notify() { } void Thread::NotifyLocked(Thread* self) { - if (wait_monitor_ != NULL) { + if (wait_monitor_ != nullptr) { wait_cond_->Signal(self); } } @@ -1201,7 +1240,7 @@ class CountStackDepthVisitor : public StackVisitor { public: explicit CountStackDepthVisitor(Thread* thread) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) - : StackVisitor(thread, NULL), + : StackVisitor(thread, nullptr), depth_(0), skip_depth_(0), skipping_(true) {} bool VisitFrame() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { @@ -1240,8 +1279,8 @@ class CountStackDepthVisitor : public StackVisitor { class BuildInternalStackTraceVisitor : public StackVisitor { public: explicit BuildInternalStackTraceVisitor(Thread* self, Thread* thread, int skip_depth) - : StackVisitor(thread, NULL), self_(self), - skip_depth_(skip_depth), count_(0), dex_pc_trace_(NULL), method_trace_(NULL) {} + : StackVisitor(thread, nullptr), self_(self), + skip_depth_(skip_depth), count_(0), dex_pc_trace_(nullptr), method_trace_(nullptr) {} bool Init(int depth) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { @@ -1250,11 +1289,11 @@ class BuildInternalStackTraceVisitor : public StackVisitor { method_trace(self_, Runtime::Current()->GetClassLinker()->AllocObjectArray(self_, depth + 1)); - if (method_trace.get() == NULL) { + if (method_trace.get() == nullptr) { return false; } mirror::IntArray* dex_pc_trace = mirror::IntArray::Alloc(self_, depth); - if (dex_pc_trace == NULL) { + if (dex_pc_trace == nullptr) { return false; } // Save PC trace in last element of method trace, also places it into the @@ -1263,20 +1302,20 @@ class BuildInternalStackTraceVisitor : public StackVisitor { // Set the Object*s and assert that no thread suspension is now possible. const char* last_no_suspend_cause = self_->StartAssertNoThreadSuspension("Building internal stack trace"); - CHECK(last_no_suspend_cause == NULL) << last_no_suspend_cause; + CHECK(last_no_suspend_cause == nullptr) << last_no_suspend_cause; method_trace_ = method_trace.get(); dex_pc_trace_ = dex_pc_trace; return true; } virtual ~BuildInternalStackTraceVisitor() { - if (method_trace_ != NULL) { - self_->EndAssertNoThreadSuspension(NULL); + if (method_trace_ != nullptr) { + self_->EndAssertNoThreadSuspension(nullptr); } } bool VisitFrame() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - if (method_trace_ == NULL || dex_pc_trace_ == NULL) { + if (method_trace_ == nullptr || dex_pc_trace_ == nullptr) { return true; // We're probably trying to fillInStackTrace for an OutOfMemoryError. } if (skip_depth_ > 0) { @@ -1320,13 +1359,13 @@ jobject Thread::CreateInternalStackTrace(const ScopedObjectAccessUnchecked& soa) BuildInternalStackTraceVisitor build_trace_visitor(soa.Self(), const_cast(this), skip_depth); if (!build_trace_visitor.Init(depth)) { - return NULL; // Allocation failed. + return nullptr; // Allocation failed. } build_trace_visitor.WalkStack(); mirror::ObjectArray* trace = build_trace_visitor.GetInternalStackTrace(); if (kIsDebugBuild) { for (int32_t i = 0; i < trace->GetLength(); ++i) { - CHECK(trace->Get(i) != NULL); + CHECK(trace->Get(i) != nullptr); } } return soa.AddLocalReference(trace); @@ -1343,7 +1382,7 @@ jobjectArray Thread::InternalStackTraceToStackTraceElementArray(JNIEnv* env, job jobjectArray result; - if (output_array != NULL) { + if (output_array != nullptr) { // Reuse the array we were given. result = output_array; // ...adjusting the number of frames we'll write to not exceed the array length. @@ -1354,13 +1393,13 @@ jobjectArray Thread::InternalStackTraceToStackTraceElementArray(JNIEnv* env, job // Create java_trace array and place in local reference table mirror::ObjectArray* java_traces = class_linker->AllocStackTraceElementArray(soa.Self(), depth); - if (java_traces == NULL) { - return NULL; + if (java_traces == nullptr) { + return nullptr; } result = soa.AddLocalReference(java_traces); } - if (stack_depth != NULL) { + if (stack_depth != nullptr) { *stack_depth = depth; } @@ -1397,17 +1436,17 @@ jobjectArray Thread::InternalStackTraceToStackTraceElementArray(JNIEnv* env, job } } const char* method_name = mh.GetName(); - CHECK(method_name != NULL); + CHECK(method_name != nullptr); SirtRef method_name_object(soa.Self(), mirror::String::AllocFromModifiedUtf8(soa.Self(), method_name)); - if (method_name_object.get() == NULL) { - return NULL; + if (method_name_object.get() == nullptr) { + return nullptr; } mirror::StackTraceElement* obj = mirror::StackTraceElement::Alloc( soa.Self(), class_name_object, method_name_object, source_name_object, line_number); - if (obj == NULL) { - return NULL; + if (obj == nullptr) { + return nullptr; } soa.Decode*>(result)->Set(i, obj); } @@ -1445,7 +1484,7 @@ void Thread::ThrowNewWrappedException(const ThrowLocation& throw_location, SirtRef saved_throw_this(this, throw_location.GetThis()); SirtRef saved_throw_method(this, throw_location.GetMethod()); // Ignore the cause throw location. TODO: should we report this as a re-throw? - SirtRef cause(this, GetException(NULL)); + SirtRef cause(this, GetException(nullptr)); ClearException(); Runtime* runtime = Runtime::Current(); @@ -1457,7 +1496,7 @@ void Thread::ThrowNewWrappedException(const ThrowLocation& throw_location, SirtRef exception_class(this, runtime->GetClassLinker()->FindClass(exception_class_descriptor, class_loader)); - if (UNLIKELY(exception_class.get() == NULL)) { + if (UNLIKELY(exception_class.get() == nullptr)) { CHECK(IsExceptionPending()); LOG(ERROR) << "No exception class " << PrettyDescriptor(exception_class_descriptor); return; @@ -1481,21 +1520,21 @@ void Thread::ThrowNewWrappedException(const ThrowLocation& throw_location, // Choose an appropriate constructor and set up the arguments. const char* signature; - SirtRef msg_string(this, NULL); - if (msg != NULL) { + SirtRef msg_string(this, nullptr); + if (msg != nullptr) { // Ensure we remember this and the method over the String allocation. msg_string.reset(mirror::String::AllocFromModifiedUtf8(this, msg)); - if (UNLIKELY(msg_string.get() == NULL)) { + if (UNLIKELY(msg_string.get() == nullptr)) { CHECK(IsExceptionPending()); // OOME. return; } - if (cause.get() == NULL) { + if (cause.get() == nullptr) { signature = "(Ljava/lang/String;)V"; } else { signature = "(Ljava/lang/String;Ljava/lang/Throwable;)V"; } } else { - if (cause.get() == NULL) { + if (cause.get() == nullptr) { signature = "()V"; } else { signature = "(Ljava/lang/Throwable;)V"; @@ -1504,17 +1543,17 @@ void Thread::ThrowNewWrappedException(const ThrowLocation& throw_location, mirror::ArtMethod* exception_init_method = exception_class->FindDeclaredDirectMethod("", signature); - CHECK(exception_init_method != NULL) << "No " << signature << " in " + CHECK(exception_init_method != nullptr) << "No " << signature << " in " << PrettyDescriptor(exception_class_descriptor); if (UNLIKELY(!runtime->IsStarted())) { // Something is trying to throw an exception without a started runtime, which is the common // case in the compiler. We won't be able to invoke the constructor of the exception, so set // the exception fields directly. - if (msg != NULL) { + if (msg != nullptr) { exception->SetDetailMessage(msg_string.get()); } - if (cause.get() != NULL) { + if (cause.get() != nullptr) { exception->SetCause(cause.get()); } ThrowLocation gc_safe_throw_location(saved_throw_this.get(), saved_throw_method.get(), @@ -1523,10 +1562,10 @@ void Thread::ThrowNewWrappedException(const ThrowLocation& throw_location, } else { ArgArray args("VLL", 3); args.Append(reinterpret_cast(exception.get())); - if (msg != NULL) { + if (msg != nullptr) { args.Append(reinterpret_cast(msg_string.get())); } - if (cause.get() != NULL) { + if (cause.get() != nullptr) { args.Append(reinterpret_cast(cause.get())); } JValue result; @@ -1709,12 +1748,12 @@ class CatchBlockStackVisitor : public StackVisitor { SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) : StackVisitor(self, self->GetLongJumpContext()), self_(self), exception_(exception), is_deoptimization_(is_deoptimization), - to_find_(is_deoptimization ? NULL : exception->GetClass()), throw_location_(throw_location), - handler_quick_frame_(NULL), handler_quick_frame_pc_(0), handler_dex_pc_(0), + to_find_(is_deoptimization ? nullptr : exception->GetClass()), throw_location_(throw_location), + handler_quick_frame_(nullptr), handler_quick_frame_pc_(0), handler_dex_pc_(0), native_method_count_(0), clear_exception_(false), method_tracing_active_(is_deoptimization || Runtime::Current()->GetInstrumentation()->AreExitStubsInstalled()), - instrumentation_frames_to_pop_(0), top_shadow_frame_(NULL), prev_shadow_frame_(NULL) { + instrumentation_frames_to_pop_(0), top_shadow_frame_(nullptr), prev_shadow_frame_(nullptr) { // Exception not in root sets, can't allow GC. last_no_assert_suspension_cause_ = self->StartAssertNoThreadSuspension("Finding catch block"); } @@ -1725,7 +1764,7 @@ class CatchBlockStackVisitor : public StackVisitor { bool VisitFrame() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { mirror::ArtMethod* method = GetMethod(); - if (method == NULL) { + if (method == nullptr) { // This is the upcall, we remember the frame and last pc so that we may long jump to them. handler_quick_frame_pc_ = GetCurrentQuickFramePc(); handler_quick_frame_ = GetCurrentQuickFrame(); @@ -1734,7 +1773,7 @@ class CatchBlockStackVisitor : public StackVisitor { if (UNLIKELY(method_tracing_active_ && GetQuickInstrumentationExitPc() == GetReturnPc())) { // Keep count of the number of unwinds during instrumentation. - instrumentation_frames_to_pop_++; + ++instrumentation_frames_to_pop_; } if (method->IsRuntimeMethod()) { // Ignore callee save method. @@ -1751,7 +1790,7 @@ class CatchBlockStackVisitor : public StackVisitor { bool HandleTryItems(mirror::ArtMethod* method) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { uint32_t dex_pc = DexFile::kDexNoIndex; if (method->IsNative()) { - native_method_count_++; + ++native_method_count_; } else { dex_pc = GetDexPc(); } @@ -1771,12 +1810,12 @@ class CatchBlockStackVisitor : public StackVisitor { SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { MethodHelper mh(m); const DexFile::CodeItem* code_item = mh.GetCodeItem(); - CHECK(code_item != NULL); + CHECK(code_item != nullptr); uint16_t num_regs = code_item->registers_size_; uint32_t dex_pc = GetDexPc(); const Instruction* inst = Instruction::At(code_item->insns_ + dex_pc); uint32_t new_dex_pc = dex_pc + inst->SizeInCodeUnits(); - ShadowFrame* new_frame = ShadowFrame::Create(num_regs, NULL, m, new_dex_pc); + ShadowFrame* new_frame = ShadowFrame::Create(num_regs, nullptr, m, new_dex_pc); SirtRef dex_cache(self_, mh.GetDexCache()); SirtRef class_loader(self_, mh.GetClassLoader()); verifier::MethodVerifier verifier(&mh.GetDexFile(), &dex_cache, &class_loader, @@ -1784,7 +1823,7 @@ class CatchBlockStackVisitor : public StackVisitor { m->GetAccessFlags(), false, true); verifier.Verify(); std::vector kinds = verifier.DescribeVRegs(dex_pc); - for (uint16_t reg = 0; reg < num_regs; reg++) { + for (uint16_t reg = 0; reg < num_regs; ++reg) { VRegKind kind = static_cast(kinds.at(reg * 2)); switch (kind) { case kUndefined: @@ -1802,7 +1841,7 @@ class CatchBlockStackVisitor : public StackVisitor { break; } } - if (prev_shadow_frame_ != NULL) { + if (prev_shadow_frame_ != nullptr) { prev_shadow_frame_->SetLink(new_frame); } else { top_shadow_frame_ = new_frame; @@ -1813,7 +1852,7 @@ class CatchBlockStackVisitor : public StackVisitor { void DoLongJump() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { mirror::ArtMethod* catch_method = *handler_quick_frame_; - if (catch_method == NULL) { + if (catch_method == nullptr) { if (kDebugExceptionDelivery) { LOG(INFO) << "Handler is upcall"; } @@ -1893,7 +1932,7 @@ void Thread::QuickDeliverException() { // Get exception from thread. ThrowLocation throw_location; mirror::Throwable* exception = GetException(&throw_location); - CHECK(exception != NULL); + CHECK(exception != nullptr); // Don't leave exception visible while we try to find the handler, which may cause class // resolution. ClearException(); @@ -1901,7 +1940,7 @@ void Thread::QuickDeliverException() { if (kDebugExceptionDelivery) { if (!is_deoptimization) { mirror::String* msg = exception->GetDetailMessage(); - std::string str_msg(msg != NULL ? msg->ToModifiedUtf8() : ""); + std::string str_msg(msg != nullptr ? msg->ToModifiedUtf8() : ""); DumpStack(LOG(INFO) << "Delivering exception: " << PrettyTypeOf(exception) << ": " << str_msg << "\n"); } else { @@ -1916,10 +1955,10 @@ void Thread::QuickDeliverException() { Context* Thread::GetLongJumpContext() { Context* result = long_jump_context_; - if (result == NULL) { + if (result == nullptr) { result = Context::Create(); } else { - long_jump_context_ = NULL; // Avoid context being shared. + long_jump_context_ = nullptr; // Avoid context being shared. result->Reset(); } return result; @@ -1928,14 +1967,14 @@ Context* Thread::GetLongJumpContext() { struct CurrentMethodVisitor : public StackVisitor { CurrentMethodVisitor(Thread* thread, Context* context) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) - : StackVisitor(thread, context), this_object_(NULL), method_(NULL), dex_pc_(0) {} + : StackVisitor(thread, context), this_object_(nullptr), method_(nullptr), dex_pc_(0) {} virtual bool VisitFrame() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { mirror::ArtMethod* m = GetMethod(); if (m->IsRuntimeMethod()) { // Continue if this is a runtime method. return true; } - if (context_ != NULL) { + if (context_ != nullptr) { this_object_ = GetThisObject(); } method_ = m; @@ -1948,9 +1987,9 @@ struct CurrentMethodVisitor : public StackVisitor { }; mirror::ArtMethod* Thread::GetCurrentMethod(uint32_t* dex_pc) const { - CurrentMethodVisitor visitor(const_cast(this), NULL); + CurrentMethodVisitor visitor(const_cast(this), nullptr); visitor.WalkStack(false); - if (dex_pc != NULL) { + if (dex_pc != nullptr) { *dex_pc = visitor.dex_pc_; } return visitor.method_; @@ -1965,7 +2004,7 @@ ThrowLocation Thread::GetCurrentLocationForThrow() { } bool Thread::HoldsLock(mirror::Object* object) { - if (object == NULL) { + if (object == nullptr) { return false; } return object->GetLockOwnerThreadId() == thin_lock_thread_id_; @@ -1985,7 +2024,7 @@ class ReferenceMapVisitor : public StackVisitor { << StringPrintf("@ PC:%04x", GetDexPc()); } ShadowFrame* shadow_frame = GetCurrentShadowFrame(); - if (shadow_frame != NULL) { + if (shadow_frame != nullptr) { mirror::ArtMethod* m = shadow_frame->GetMethod(); size_t num_regs = shadow_frame->NumberOfVRegs(); if (m->IsNative() || shadow_frame->HasReferenceArray()) { @@ -2007,7 +2046,7 @@ class ReferenceMapVisitor : public StackVisitor { verifier::DexPcToReferenceMap dex_gc_map(gc_map); uint32_t dex_pc = GetDexPc(); const uint8_t* reg_bitmap = dex_gc_map.FindBitMap(dex_pc); - DCHECK(reg_bitmap != NULL); + DCHECK(reg_bitmap != nullptr); num_regs = std::min(dex_gc_map.RegWidth() * 8, num_regs); for (size_t reg = 0; reg < num_regs; ++reg) { if (TestBitmap(reg, reg_bitmap)) { @@ -2026,23 +2065,23 @@ class ReferenceMapVisitor : public StackVisitor { // Process register map (which native and runtime methods don't have) if (!m->IsNative() && !m->IsRuntimeMethod() && !m->IsProxyMethod()) { const uint8_t* native_gc_map = m->GetNativeGcMap(); - CHECK(native_gc_map != NULL) << PrettyMethod(m); + CHECK(native_gc_map != nullptr) << PrettyMethod(m); mh_.ChangeMethod(m); const DexFile::CodeItem* code_item = mh_.GetCodeItem(); - DCHECK(code_item != NULL) << PrettyMethod(m); // Can't be NULL or how would we compile its instructions? + DCHECK(code_item != nullptr) << PrettyMethod(m); // Can't be nullptr or how would we compile its instructions? NativePcOffsetToReferenceMap map(native_gc_map); size_t num_regs = std::min(map.RegWidth() * 8, static_cast(code_item->registers_size_)); if (num_regs > 0) { const uint8_t* reg_bitmap = map.FindBitMap(GetNativePcOffset()); - DCHECK(reg_bitmap != NULL); + DCHECK(reg_bitmap != nullptr); const VmapTable vmap_table(m->GetVmapTable()); uint32_t core_spills = m->GetCoreSpillMask(); uint32_t fp_spills = m->GetFpSpillMask(); size_t frame_size = m->GetFrameSizeInBytes(); // For all dex registers in the bitmap mirror::ArtMethod** cur_quick_frame = GetCurrentQuickFrame(); - DCHECK(cur_quick_frame != NULL); + DCHECK(cur_quick_frame != nullptr); for (size_t reg = 0; reg < num_regs; ++reg) { // Does this register hold a reference? if (TestBitmap(reg, reg_bitmap)) { diff --git a/runtime/thread.h b/runtime/thread.h index b01ec945de0..30c7e8ff864 100644 --- a/runtime/thread.h +++ b/runtime/thread.h @@ -133,9 +133,19 @@ class PACKED(4) Thread { SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); ThreadState GetState() const { + DCHECK(state_and_flags_.as_struct.state >= kTerminated && state_and_flags_.as_struct.state <= kSuspended); return static_cast(state_and_flags_.as_struct.state); } + // This function can be used to make sure a thread's state is valid. + void CheckState(int id) const { + if (state_and_flags_.as_struct.state >= kTerminated && state_and_flags_.as_struct.state <= kSuspended) { + return; + } + LOG(INFO) << "Thread " << this << " state is invalid: " << state_and_flags_.as_struct.state << " id=" << id; + CHECK(false); + } + ThreadState SetState(ThreadState new_state); int GetSuspendCount() const EXCLUSIVE_LOCKS_REQUIRED(Locks::thread_suspend_count_lock_) { @@ -780,9 +790,12 @@ class PACKED(4) Thread { // Cause for last suspension. const char* last_no_thread_suspension_cause_; + // Maximum number of checkpoint functions. + static constexpr uint32_t kMaxCheckpoints = 3; + // Pending checkpoint function or NULL if non-pending. Installation guarding by // Locks::thread_suspend_count_lock_. - Closure* checkpoint_function_; + Closure* checkpoint_functions_[kMaxCheckpoints]; public: // Entrypoint function pointers diff --git a/runtime/thread_list.cc b/runtime/thread_list.cc index aed8c7788ec..8bf099bb2cb 100644 --- a/runtime/thread_list.cc +++ b/runtime/thread_list.cc @@ -211,7 +211,7 @@ size_t ThreadList::RunCheckpoint(Closure* checkpoint_function) { if (thread != self) { while (true) { if (thread->RequestCheckpoint(checkpoint_function)) { - // This thread will run it's checkpoint some time in the near future. + // This thread will run its checkpoint some time in the near future. count++; break; } else { diff --git a/runtime/thread_state.h b/runtime/thread_state.h index 4d4bfb751ef..7615c41425a 100644 --- a/runtime/thread_state.h +++ b/runtime/thread_state.h @@ -21,7 +21,7 @@ namespace art { enum ThreadState { // Thread.State JDWP state - kTerminated, // TERMINATED TS_ZOMBIE Thread.run has returned, but Thread* still around + kTerminated = 66, // TERMINATED TS_ZOMBIE Thread.run has returned, but Thread* still around kRunnable, // RUNNABLE TS_RUNNING runnable kTimedWaiting, // TIMED_WAITING TS_WAIT in Object.wait() with a timeout kSleeping, // TIMED_WAITING TS_SLEEPING in Thread.sleep() From ec05007f8619f8b0cc868d06731e07f84bb74c5b Mon Sep 17 00:00:00 2001 From: Mathieu Chartier Date: Tue, 7 Jan 2014 16:00:07 -0800 Subject: [PATCH 0332/2402] Refactor sweeping logic into malloc space. Removes duplicated code in MarkSweep/SemiSpace. Deleted VerifyImageRoots since it had race conditions and is tested by pre/post GC heap verification. Change-Id: I9636359ff6adb3e93d56ce77a3e15299ed23dfd5 --- runtime/gc/collector/mark_sweep.cc | 171 ++--------------------------- runtime/gc/collector/mark_sweep.h | 19 ---- runtime/gc/collector/semi_space.cc | 46 ++------ runtime/gc/space/malloc_space.cc | 80 +++++++++++++- runtime/gc/space/malloc_space.h | 3 + 5 files changed, 102 insertions(+), 217 deletions(-) diff --git a/runtime/gc/collector/mark_sweep.cc b/runtime/gc/collector/mark_sweep.cc index cae2a548934..a6fb35d993a 100644 --- a/runtime/gc/collector/mark_sweep.cc +++ b/runtime/gc/collector/mark_sweep.cc @@ -333,12 +333,6 @@ void MarkSweep::ReclaimPhase() { } } - // Before freeing anything, lets verify the heap. - if (kIsDebugBuild) { - ReaderMutexLock mu(self, *Locks::heap_bitmap_lock_); - VerifyImageRoots(); - } - { WriterMutexLock mu(self, *Locks::heap_bitmap_lock_); @@ -595,23 +589,6 @@ void MarkSweep::MarkConcurrentRoots() { timings_.EndSplit(); } -void MarkSweep::CheckObject(const Object* obj) { - DCHECK(obj != NULL); - VisitObjectReferences(const_cast(obj), [this](const Object* obj, const Object* ref, - MemberOffset offset, bool is_static) NO_THREAD_SAFETY_ANALYSIS { - Locks::heap_bitmap_lock_->AssertSharedHeld(Thread::Current()); - CheckReference(obj, ref, offset, is_static); - }, true); -} - -void MarkSweep::VerifyImageRootVisitor(Object* root, void* arg) { - DCHECK(root != NULL); - DCHECK(arg != NULL); - MarkSweep* mark_sweep = reinterpret_cast(arg); - DCHECK(mark_sweep->heap_->GetMarkBitmap()->Test(root)); - mark_sweep->CheckObject(root); -} - void MarkSweep::BindLiveToMarkBitmap(space::ContinuousSpace* space) { CHECK(space->IsMallocSpace()); space::MallocSpace* alloc_space = space->AsMallocSpace(); @@ -884,30 +861,6 @@ void MarkSweep::ScanGrayObjects(bool paused, byte minimum_age) { } } -void MarkSweep::VerifyImageRoots() { - // Verify roots ensures that all the references inside the image space point - // objects which are either in the image space or marked objects in the alloc - // space - timings_.StartSplit("VerifyImageRoots"); - for (const auto& space : GetHeap()->GetContinuousSpaces()) { - if (space->IsImageSpace()) { - space::ImageSpace* image_space = space->AsImageSpace(); - uintptr_t begin = reinterpret_cast(image_space->Begin()); - uintptr_t end = reinterpret_cast(image_space->End()); - accounting::SpaceBitmap* live_bitmap = image_space->GetLiveBitmap(); - DCHECK(live_bitmap != NULL); - live_bitmap->VisitMarkedRange(begin, end, [this](const Object* obj) { - if (kCheckLocks) { - Locks::heap_bitmap_lock_->AssertSharedHeld(Thread::Current()); - } - DCHECK(obj != NULL); - CheckObject(obj); - }); - } - } - timings_.EndSplit(); -} - class RecursiveMarkTask : public MarkStackTask { public: RecursiveMarkTask(ThreadPool* thread_pool, MarkSweep* mark_sweep, @@ -1050,12 +1003,6 @@ void MarkSweep::VerifySystemWeaks() { Runtime::Current()->SweepSystemWeaks(VerifySystemWeakIsLiveCallback, this); } -struct SweepCallbackContext { - MarkSweep* mark_sweep; - space::AllocSpace* space; - Thread* self; -}; - class CheckpointMarkThreadRoots : public Closure { public: explicit CheckpointMarkThreadRoots(MarkSweep* mark_sweep) : mark_sweep_(mark_sweep) {} @@ -1095,36 +1042,6 @@ void MarkSweep::MarkRootsCheckpoint(Thread* self) { timings_.EndSplit(); } -void MarkSweep::SweepCallback(size_t num_ptrs, Object** ptrs, void* arg) { - SweepCallbackContext* context = static_cast(arg); - MarkSweep* mark_sweep = context->mark_sweep; - Heap* heap = mark_sweep->GetHeap(); - space::AllocSpace* space = context->space; - Thread* self = context->self; - Locks::heap_bitmap_lock_->AssertExclusiveHeld(self); - // Use a bulk free, that merges consecutive objects before freeing or free per object? - // Documentation suggests better free performance with merging, but this may be at the expensive - // of allocation. - size_t freed_objects = num_ptrs; - // AllocSpace::FreeList clears the value in ptrs, so perform after clearing the live bit - size_t freed_bytes = space->FreeList(self, num_ptrs, ptrs); - heap->RecordFree(freed_objects, freed_bytes); - mark_sweep->freed_objects_.FetchAndAdd(freed_objects); - mark_sweep->freed_bytes_.FetchAndAdd(freed_bytes); -} - -void MarkSweep::ZygoteSweepCallback(size_t num_ptrs, Object** ptrs, void* arg) { - SweepCallbackContext* context = static_cast(arg); - Locks::heap_bitmap_lock_->AssertExclusiveHeld(context->self); - Heap* heap = context->mark_sweep->GetHeap(); - // We don't free any actual memory to avoid dirtying the shared zygote pages. - for (size_t i = 0; i < num_ptrs; ++i) { - Object* obj = static_cast(ptrs[i]); - heap->GetLiveBitmap()->Clear(obj); - heap->GetCardTable()->MarkCard(obj); - } -} - void MarkSweep::SweepArray(accounting::ObjectStack* allocations, bool swap_bitmaps) { space::MallocSpace* space = heap_->GetNonMovingSpace(); timings_.StartSplit("SweepArray"); @@ -1206,45 +1123,19 @@ void MarkSweep::SweepArray(accounting::ObjectStack* allocations, bool swap_bitma void MarkSweep::Sweep(bool swap_bitmaps) { DCHECK(mark_stack_->IsEmpty()); TimingLogger::ScopedSplit("Sweep", &timings_); - - const bool partial = (GetGcType() == kGcTypePartial); - SweepCallbackContext scc; - scc.mark_sweep = this; - scc.self = Thread::Current(); for (const auto& space : GetHeap()->GetContinuousSpaces()) { - if (!space->IsMallocSpace()) { - continue; - } - // We always sweep always collect spaces. - bool sweep_space = space->GetGcRetentionPolicy() == space::kGcRetentionPolicyAlwaysCollect; - if (!partial && !sweep_space) { - // We sweep full collect spaces when the GC isn't a partial GC (ie its full). - sweep_space = (space->GetGcRetentionPolicy() == space::kGcRetentionPolicyFullCollect); - } - if (sweep_space) { - uintptr_t begin = reinterpret_cast(space->Begin()); - uintptr_t end = reinterpret_cast(space->End()); - scc.space = space->AsMallocSpace(); - accounting::SpaceBitmap* live_bitmap = space->GetLiveBitmap(); - accounting::SpaceBitmap* mark_bitmap = space->GetMarkBitmap(); - if (swap_bitmaps) { - std::swap(live_bitmap, mark_bitmap); - } - if (!space->IsZygoteSpace()) { - TimingLogger::ScopedSplit split("SweepAllocSpace", &timings_); - // Bitmaps are pre-swapped for optimization which enables sweeping with the heap unlocked. - accounting::SpaceBitmap::SweepWalk(*live_bitmap, *mark_bitmap, begin, end, - &SweepCallback, reinterpret_cast(&scc)); - } else { - TimingLogger::ScopedSplit split("SweepZygote", &timings_); - // Zygote sweep takes care of dirtying cards and clearing live bits, does not free actual - // memory. - accounting::SpaceBitmap::SweepWalk(*live_bitmap, *mark_bitmap, begin, end, - &ZygoteSweepCallback, reinterpret_cast(&scc)); - } + if (space->IsMallocSpace()) { + space::MallocSpace* malloc_space = space->AsMallocSpace(); + TimingLogger::ScopedSplit split( + malloc_space->IsZygoteSpace() ? "SweepZygoteSpace" : "SweepAllocSpace", &timings_); + size_t freed_objects = 0; + size_t freed_bytes = 0; + malloc_space->Sweep(swap_bitmaps, &freed_objects, &freed_bytes); + heap_->RecordFree(freed_objects, freed_bytes); + freed_objects_.FetchAndAdd(freed_objects); + freed_bytes_.FetchAndAdd(freed_bytes); } } - SweepLargeObjects(swap_bitmaps); } @@ -1272,48 +1163,6 @@ void MarkSweep::SweepLargeObjects(bool swap_bitmaps) { GetHeap()->RecordFree(freed_objects, freed_bytes); } -void MarkSweep::CheckReference(const Object* obj, const Object* ref, MemberOffset offset, bool is_static) { - for (const auto& space : GetHeap()->GetContinuousSpaces()) { - if (space->IsMallocSpace() && space->Contains(ref)) { - DCHECK(IsMarked(obj)); - - bool is_marked = IsMarked(ref); - if (!is_marked) { - LOG(INFO) << *space; - LOG(WARNING) << (is_static ? "Static ref'" : "Instance ref'") << PrettyTypeOf(ref) - << "' (" << reinterpret_cast(ref) << ") in '" << PrettyTypeOf(obj) - << "' (" << reinterpret_cast(obj) << ") at offset " - << reinterpret_cast(offset.Int32Value()) << " wasn't marked"; - - const Class* klass = is_static ? obj->AsClass() : obj->GetClass(); - DCHECK(klass != NULL); - const ObjectArray* fields = is_static ? klass->GetSFields() : klass->GetIFields(); - DCHECK(fields != NULL); - bool found = false; - for (int32_t i = 0; i < fields->GetLength(); ++i) { - const ArtField* cur = fields->Get(i); - if (cur->GetOffset().Int32Value() == offset.Int32Value()) { - LOG(WARNING) << "Field referencing the alloc space was " << PrettyField(cur); - found = true; - break; - } - } - if (!found) { - LOG(WARNING) << "Could not find field in object alloc space with offset " << offset.Int32Value(); - } - - bool obj_marked = heap_->GetCardTable()->IsDirty(obj); - if (!obj_marked) { - LOG(WARNING) << "Object '" << PrettyTypeOf(obj) << "' " - << "(" << reinterpret_cast(obj) << ") contains references to " - << "the alloc space, but wasn't card marked"; - } - } - } - break; - } -} - // Process the "referent" field in a java.lang.ref.Reference. If the // referent has not yet been marked, put it on the appropriate list in // the heap for later processing. diff --git a/runtime/gc/collector/mark_sweep.h b/runtime/gc/collector/mark_sweep.h index 62991bb89d0..e2eafb5f524 100644 --- a/runtime/gc/collector/mark_sweep.h +++ b/runtime/gc/collector/mark_sweep.h @@ -100,11 +100,6 @@ class MarkSweep : public GarbageCollector { EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - // Verify that image roots point to only marked objects within the alloc space. - void VerifyImageRoots() - EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - // Builds a mark stack and recursively mark until it empties. void RecursiveMark() EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_) @@ -251,20 +246,6 @@ class MarkSweep : public GarbageCollector { // Returns true if we need to add obj to a mark stack. bool MarkObjectParallel(const mirror::Object* obj) NO_THREAD_SAFETY_ANALYSIS; - static void SweepCallback(size_t num_ptrs, mirror::Object** ptrs, void* arg) - EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_); - - // Special sweep for zygote that just marks objects / dirties cards. - static void ZygoteSweepCallback(size_t num_ptrs, mirror::Object** ptrs, void* arg) - EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_); - - void CheckReference(const mirror::Object* obj, const mirror::Object* ref, MemberOffset offset, - bool is_static) - SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_, Locks::mutator_lock_); - - void CheckObject(const mirror::Object* obj) - SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_, Locks::mutator_lock_); - // Verify the roots of the heap and print out information related to any invalid roots. // Called in MarkObject, so may we may not hold the mutator lock. void VerifyRoots() diff --git a/runtime/gc/collector/semi_space.cc b/runtime/gc/collector/semi_space.cc index a4f7121005e..0dd87923ba8 100644 --- a/runtime/gc/collector/semi_space.cc +++ b/runtime/gc/collector/semi_space.cc @@ -465,45 +465,19 @@ void SemiSpace::ZygoteSweepCallback(size_t num_ptrs, Object** ptrs, void* arg) { void SemiSpace::Sweep(bool swap_bitmaps) { DCHECK(mark_stack_->IsEmpty()); TimingLogger::ScopedSplit("Sweep", &timings_); - - const bool partial = (GetGcType() == kGcTypePartial); - SweepCallbackContext scc; - scc.mark_sweep = this; - scc.self = Thread::Current(); for (const auto& space : GetHeap()->GetContinuousSpaces()) { - if (!space->IsMallocSpace()) { - continue; - } - // We always sweep always collect spaces. - bool sweep_space = (space->GetGcRetentionPolicy() == space::kGcRetentionPolicyAlwaysCollect); - if (!partial && !sweep_space) { - // We sweep full collect spaces when the GC isn't a partial GC (ie its full). - sweep_space = (space->GetGcRetentionPolicy() == space::kGcRetentionPolicyFullCollect); - } - if (sweep_space && space->IsMallocSpace()) { - uintptr_t begin = reinterpret_cast(space->Begin()); - uintptr_t end = reinterpret_cast(space->End()); - scc.space = space->AsMallocSpace(); - accounting::SpaceBitmap* live_bitmap = space->GetLiveBitmap(); - accounting::SpaceBitmap* mark_bitmap = space->GetMarkBitmap(); - if (swap_bitmaps) { - std::swap(live_bitmap, mark_bitmap); - } - if (!space->IsZygoteSpace()) { - TimingLogger::ScopedSplit split("SweepAllocSpace", &timings_); - // Bitmaps are pre-swapped for optimization which enables sweeping with the heap unlocked. - accounting::SpaceBitmap::SweepWalk(*live_bitmap, *mark_bitmap, begin, end, - &SweepCallback, reinterpret_cast(&scc)); - } else { - TimingLogger::ScopedSplit split("SweepZygote", &timings_); - // Zygote sweep takes care of dirtying cards and clearing live bits, does not free actual - // memory. - accounting::SpaceBitmap::SweepWalk(*live_bitmap, *mark_bitmap, begin, end, - &ZygoteSweepCallback, reinterpret_cast(&scc)); - } + if (space->IsMallocSpace() && space != from_space_ && space != to_space_) { + space::MallocSpace* malloc_space = space->AsMallocSpace(); + TimingLogger::ScopedSplit split( + malloc_space->IsZygoteSpace() ? "SweepZygoteSpace" : "SweepAllocSpace", &timings_); + size_t freed_objects = 0; + size_t freed_bytes = 0; + malloc_space->Sweep(swap_bitmaps, &freed_objects, &freed_bytes); + heap_->RecordFree(freed_objects, freed_bytes); + freed_objects_.FetchAndAdd(freed_objects); + freed_bytes_.FetchAndAdd(freed_bytes); } } - SweepLargeObjects(swap_bitmaps); } diff --git a/runtime/gc/space/malloc_space.cc b/runtime/gc/space/malloc_space.cc index 46df0a1e213..272743177b1 100644 --- a/runtime/gc/space/malloc_space.cc +++ b/runtime/gc/space/malloc_space.cc @@ -16,7 +16,8 @@ #include "malloc_space.h" -#include "gc/accounting/card_table.h" +#include "gc/accounting/card_table-inl.h" +#include "gc/accounting/space_bitmap-inl.h" #include "gc/heap.h" #include "mirror/class-inl.h" #include "mirror/object-inl.h" @@ -238,6 +239,83 @@ void MallocSpace::Dump(std::ostream& os) const { << ",name=\"" << GetName() << "\"]"; } +struct SweepCallbackContext { + bool swap_bitmaps; + Heap* heap; + space::MallocSpace* space; + Thread* self; + size_t freed_objects; + size_t freed_bytes; +}; + +static void SweepCallback(size_t num_ptrs, mirror::Object** ptrs, void* arg) { + SweepCallbackContext* context = static_cast(arg); + space::AllocSpace* space = context->space; + Thread* self = context->self; + Locks::heap_bitmap_lock_->AssertExclusiveHeld(self); + // If the bitmaps aren't swapped we need to clear the bits since the GC isn't going to re-swap + // the bitmaps as an optimization. + if (!context->swap_bitmaps) { + accounting::SpaceBitmap* bitmap = context->space->GetLiveBitmap(); + for (size_t i = 0; i < num_ptrs; ++i) { + bitmap->Clear(ptrs[i]); + } + } + // Use a bulk free, that merges consecutive objects before freeing or free per object? + // Documentation suggests better free performance with merging, but this may be at the expensive + // of allocation. + context->freed_objects += num_ptrs; + context->freed_bytes += space->FreeList(self, num_ptrs, ptrs); +} + +static void ZygoteSweepCallback(size_t num_ptrs, mirror::Object** ptrs, void* arg) { + SweepCallbackContext* context = static_cast(arg); + Locks::heap_bitmap_lock_->AssertExclusiveHeld(context->self); + accounting::CardTable* card_table = context->heap->GetCardTable(); + // If the bitmaps aren't swapped we need to clear the bits since the GC isn't going to re-swap + // the bitmaps as an optimization. + if (!context->swap_bitmaps) { + accounting::SpaceBitmap* bitmap = context->space->GetLiveBitmap(); + for (size_t i = 0; i < num_ptrs; ++i) { + bitmap->Clear(ptrs[i]); + } + } + // We don't free any actual memory to avoid dirtying the shared zygote pages. + for (size_t i = 0; i < num_ptrs; ++i) { + // Need to mark the card since this will update the mod-union table next GC cycle. + card_table->MarkCard(ptrs[i]); + } +} + +void MallocSpace::Sweep(bool swap_bitmaps, size_t* freed_objects, size_t* freed_bytes) { + DCHECK(freed_objects != nullptr); + DCHECK(freed_bytes != nullptr); + accounting::SpaceBitmap* live_bitmap = GetLiveBitmap(); + accounting::SpaceBitmap* mark_bitmap = GetMarkBitmap(); + // If the bitmaps are bound then sweeping this space clearly won't do anything. + if (live_bitmap == mark_bitmap) { + return; + } + SweepCallbackContext scc; + scc.swap_bitmaps = swap_bitmaps; + scc.heap = Runtime::Current()->GetHeap(); + scc.self = Thread::Current(); + scc.space = this; + scc.freed_objects = 0; + scc.freed_bytes = 0; + if (swap_bitmaps) { + std::swap(live_bitmap, mark_bitmap); + } + // Bitmaps are pre-swapped for optimization which enables sweeping with the heap unlocked. + accounting::SpaceBitmap::SweepWalk(*live_bitmap, *mark_bitmap, + reinterpret_cast(Begin()), + reinterpret_cast(End()), + IsZygoteSpace() ? &ZygoteSweepCallback : &SweepCallback, + reinterpret_cast(&scc)); + *freed_objects += scc.freed_objects; + *freed_bytes += scc.freed_bytes; +} + } // namespace space } // namespace gc } // namespace art diff --git a/runtime/gc/space/malloc_space.h b/runtime/gc/space/malloc_space.h index d25f9cb3478..7681b6d459e 100644 --- a/runtime/gc/space/malloc_space.h +++ b/runtime/gc/space/malloc_space.h @@ -148,6 +148,9 @@ class MallocSpace : public ContinuousMemMapAllocSpace { // don't do this we may get heap corruption instead of a segfault at null. virtual void InvalidateAllocator() = 0; + // Sweep the references in the malloc space. + void Sweep(bool swap_bitmaps, size_t* freed_objects, size_t* freed_bytes); + protected: MallocSpace(const std::string& name, MemMap* mem_map, byte* begin, byte* end, byte* limit, size_t growth_limit); From 72ba8c4895d307dd62b686e8eff777b2655a6e2c Mon Sep 17 00:00:00 2001 From: Sebastien Hertz Date: Wed, 8 Jan 2014 09:37:39 +0100 Subject: [PATCH 0333/2402] Fix run-test 303-verification-stress. Bug: 12439831 Change-Id: I3d3fc0978543ae8c6fc76d88b9edece1e17bc7ce --- test/303-verification-stress/expected.txt | 30 +++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/test/303-verification-stress/expected.txt b/test/303-verification-stress/expected.txt index cdfd6cb4614..4fa1b5733d0 100644 --- a/test/303-verification-stress/expected.txt +++ b/test/303-verification-stress/expected.txt @@ -9,4 +9,34 @@ Create new Test006 Create new Test007 Create new Test008 Create new Test009 +Create new Test010 +Create new Test011 +Create new Test012 +Create new Test013 +Create new Test014 +Create new Test015 +Create new Test016 +Create new Test017 +Create new Test018 +Create new Test019 +Create new Test020 +Create new Test021 +Create new Test022 +Create new Test023 +Create new Test024 +Create new Test025 +Create new Test026 +Create new Test027 +Create new Test028 +Create new Test029 +Create new Test030 +Create new Test031 +Create new Test032 +Create new Test033 +Create new Test034 +Create new Test035 +Create new Test036 +Create new Test037 +Create new Test038 +Create new Test039 Done From 5ddb4104ac605d66693b55b79f26f8b8a5505e63 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Tue, 7 Jan 2014 08:58:46 -0800 Subject: [PATCH 0334/2402] Remove intialized static storage from dex cache. The initialized static storage array is used by compiled code to determine if for a sget/sput class initialization is necessary. The compiled code typically doesn't require this test as the class is pre-initialized or the class being accessed is the same as the current method. Change-Id: Icbc45e692b3d0ac61e559e69edb6c9b29439e571 --- compiler/dex/quick/gen_common.cc | 135 +++++++++++++--------- compiler/driver/compiler_driver.cc | 26 +++-- compiler/driver/compiler_driver.h | 7 +- compiler/image_writer.cc | 1 - compiler/llvm/gbc_expander.cc | 61 ++++------ compiler/llvm/intrinsic_func_list.def | 7 -- runtime/asm_support.h | 4 +- runtime/class_linker.cc | 12 +- runtime/class_linker_test.cc | 14 +-- runtime/entrypoints/entrypoint_utils.h | 9 +- runtime/mirror/art_method-inl.h | 6 - runtime/mirror/art_method.cc | 5 - runtime/mirror/art_method.h | 12 -- runtime/mirror/class.h | 13 +-- runtime/mirror/dex_cache.cc | 22 ++-- runtime/mirror/dex_cache.h | 15 +-- runtime/mirror/dex_cache_test.cc | 4 - runtime/native/dalvik_system_VMRuntime.cc | 16 +-- runtime/oat.cc | 2 +- 19 files changed, 147 insertions(+), 224 deletions(-) diff --git a/compiler/dex/quick/gen_common.cc b/compiler/dex/quick/gen_common.cc index a426cc77dea..6b4cbd42865 100644 --- a/compiler/dex/quick/gen_common.cc +++ b/compiler/dex/quick/gen_common.cc @@ -330,21 +330,22 @@ void Mir2Lir::GenFilledNewArray(CallInfo* info) { void Mir2Lir::GenSput(uint32_t field_idx, RegLocation rl_src, bool is_long_or_double, bool is_object) { int field_offset; - int ssb_index; + int storage_index; bool is_volatile; bool is_referrers_class; + bool is_initialized; bool fast_path = cu_->compiler_driver->ComputeStaticFieldInfo( field_idx, mir_graph_->GetCurrentDexCompilationUnit(), true, - &field_offset, &ssb_index, &is_referrers_class, &is_volatile); + &field_offset, &storage_index, &is_referrers_class, &is_volatile, &is_initialized); if (fast_path && !SLOW_FIELD_PATH) { DCHECK_GE(field_offset, 0); - int rBase; + int r_base; if (is_referrers_class) { // Fast path, static storage base is this method's class RegLocation rl_method = LoadCurrMethod(); - rBase = AllocTemp(); + r_base = AllocTemp(); LoadWordDisp(rl_method.low_reg, - mirror::ArtMethod::DeclaringClassOffset().Int32Value(), rBase); + mirror::ArtMethod::DeclaringClassOffset().Int32Value(), r_base); if (IsTemp(rl_method.low_reg)) { FreeTemp(rl_method.low_reg); } @@ -352,33 +353,44 @@ void Mir2Lir::GenSput(uint32_t field_idx, RegLocation rl_src, bool is_long_or_do // Medium path, static storage base in a different class which requires checks that the other // class is initialized. // TODO: remove initialized check now that we are initializing classes in the compiler driver. - DCHECK_GE(ssb_index, 0); + DCHECK_GE(storage_index, 0); // May do runtime call so everything to home locations. FlushAllRegs(); // Using fixed register to sync with possible call to runtime support. int r_method = TargetReg(kArg1); LockTemp(r_method); LoadCurrMethodDirect(r_method); - rBase = TargetReg(kArg0); - LockTemp(rBase); + r_base = TargetReg(kArg0); + LockTemp(r_base); LoadWordDisp(r_method, - mirror::ArtMethod::DexCacheInitializedStaticStorageOffset().Int32Value(), - rBase); - LoadWordDisp(rBase, - mirror::Array::DataOffset(sizeof(mirror::Object*)).Int32Value() + - sizeof(int32_t*) * ssb_index, rBase); - // rBase now points at appropriate static storage base (Class*) - // or NULL if not initialized. Check for NULL and call helper if NULL. - // TUNING: fast path should fall through - LIR* branch_over = OpCmpImmBranch(kCondNe, rBase, 0, NULL); - LoadConstant(TargetReg(kArg0), ssb_index); - CallRuntimeHelperImm(QUICK_ENTRYPOINT_OFFSET(pInitializeStaticStorage), ssb_index, true); - if (cu_->instruction_set == kMips) { - // For Arm, kRet0 = kArg0 = rBase, for Mips, we need to copy - OpRegCopy(rBase, TargetReg(kRet0)); + mirror::ArtMethod::DexCacheResolvedTypesOffset().Int32Value(), + r_base); + LoadWordDisp(r_base, mirror::Array::DataOffset(sizeof(mirror::Object*)).Int32Value() + + sizeof(int32_t*) * storage_index, r_base); + // r_base now points at static storage (Class*) or NULL if the type is not yet resolved. + if (!is_initialized) { + // Check if r_base is NULL or a not yet initialized class. + // TUNING: fast path should fall through + LIR* unresolved_branch = OpCmpImmBranch(kCondEq, r_base, 0, NULL); + int r_tmp = TargetReg(kArg2); + LockTemp(r_tmp); + // TODO: Fuse the compare of a constant with memory on X86 and avoid the load. + LoadWordDisp(r_base, mirror::Class::StatusOffset().Int32Value(), r_tmp); + LIR* initialized_branch = OpCmpImmBranch(kCondGe, r_tmp, mirror::Class::kStatusInitialized, + NULL); + + LIR* unresolved_target = NewLIR0(kPseudoTargetLabel); + unresolved_branch->target = unresolved_target; + CallRuntimeHelperImm(QUICK_ENTRYPOINT_OFFSET(pInitializeStaticStorage), storage_index, + true); + // Copy helper's result into r_base, a no-op on all but MIPS. + OpRegCopy(r_base, TargetReg(kRet0)); + + LIR* initialized_target = NewLIR0(kPseudoTargetLabel); + initialized_branch->target = initialized_target; + + FreeTemp(r_tmp); } - LIR* skip_target = NewLIR0(kPseudoTargetLabel); - branch_over->target = skip_target; FreeTemp(r_method); } // rBase now holds static storage base @@ -391,18 +403,18 @@ void Mir2Lir::GenSput(uint32_t field_idx, RegLocation rl_src, bool is_long_or_do GenMemBarrier(kStoreStore); } if (is_long_or_double) { - StoreBaseDispWide(rBase, field_offset, rl_src.low_reg, + StoreBaseDispWide(r_base, field_offset, rl_src.low_reg, rl_src.high_reg); } else { - StoreWordDisp(rBase, field_offset, rl_src.low_reg); + StoreWordDisp(r_base, field_offset, rl_src.low_reg); } if (is_volatile) { GenMemBarrier(kStoreLoad); } if (is_object && !mir_graph_->IsConstantNullRef(rl_src)) { - MarkGCCard(rl_src.low_reg, rBase); + MarkGCCard(rl_src.low_reg, r_base); } - FreeTemp(rBase); + FreeTemp(r_base); } else { FlushAllRegs(); // Everything to home locations ThreadOffset setter_offset = @@ -416,64 +428,77 @@ void Mir2Lir::GenSput(uint32_t field_idx, RegLocation rl_src, bool is_long_or_do void Mir2Lir::GenSget(uint32_t field_idx, RegLocation rl_dest, bool is_long_or_double, bool is_object) { int field_offset; - int ssb_index; + int storage_index; bool is_volatile; bool is_referrers_class; + bool is_initialized; bool fast_path = cu_->compiler_driver->ComputeStaticFieldInfo( field_idx, mir_graph_->GetCurrentDexCompilationUnit(), false, - &field_offset, &ssb_index, &is_referrers_class, &is_volatile); + &field_offset, &storage_index, &is_referrers_class, &is_volatile, &is_initialized); if (fast_path && !SLOW_FIELD_PATH) { DCHECK_GE(field_offset, 0); - int rBase; + int r_base; if (is_referrers_class) { // Fast path, static storage base is this method's class RegLocation rl_method = LoadCurrMethod(); - rBase = AllocTemp(); + r_base = AllocTemp(); LoadWordDisp(rl_method.low_reg, - mirror::ArtMethod::DeclaringClassOffset().Int32Value(), rBase); + mirror::ArtMethod::DeclaringClassOffset().Int32Value(), r_base); } else { // Medium path, static storage base in a different class which requires checks that the other // class is initialized - // TODO: remove initialized check now that we are initializing classes in the compiler driver. - DCHECK_GE(ssb_index, 0); + DCHECK_GE(storage_index, 0); // May do runtime call so everything to home locations. FlushAllRegs(); // Using fixed register to sync with possible call to runtime support. int r_method = TargetReg(kArg1); LockTemp(r_method); LoadCurrMethodDirect(r_method); - rBase = TargetReg(kArg0); - LockTemp(rBase); + r_base = TargetReg(kArg0); + LockTemp(r_base); LoadWordDisp(r_method, - mirror::ArtMethod::DexCacheInitializedStaticStorageOffset().Int32Value(), - rBase); - LoadWordDisp(rBase, mirror::Array::DataOffset(sizeof(mirror::Object*)).Int32Value() + - sizeof(int32_t*) * ssb_index, rBase); - // rBase now points at appropriate static storage base (Class*) - // or NULL if not initialized. Check for NULL and call helper if NULL. - // TUNING: fast path should fall through - LIR* branch_over = OpCmpImmBranch(kCondNe, rBase, 0, NULL); - CallRuntimeHelperImm(QUICK_ENTRYPOINT_OFFSET(pInitializeStaticStorage), ssb_index, true); - if (cu_->instruction_set == kMips) { - // For Arm, kRet0 = kArg0 = rBase, for Mips, we need to copy - OpRegCopy(rBase, TargetReg(kRet0)); + mirror::ArtMethod::DexCacheResolvedTypesOffset().Int32Value(), + r_base); + LoadWordDisp(r_base, mirror::Array::DataOffset(sizeof(mirror::Object*)).Int32Value() + + sizeof(int32_t*) * storage_index, r_base); + // r_base now points at static storage (Class*) or NULL if the type is not yet resolved. + if (!is_initialized) { + // Check if r_base is NULL or a not yet initialized class. + // TUNING: fast path should fall through + LIR* unresolved_branch = OpCmpImmBranch(kCondEq, r_base, 0, NULL); + int r_tmp = TargetReg(kArg2); + LockTemp(r_tmp); + // TODO: Fuse the compare of a constant with memory on X86 and avoid the load. + LoadWordDisp(r_base, mirror::Class::StatusOffset().Int32Value(), r_tmp); + LIR* initialized_branch = OpCmpImmBranch(kCondGe, r_tmp, mirror::Class::kStatusInitialized, + NULL); + + LIR* unresolved_target = NewLIR0(kPseudoTargetLabel); + unresolved_branch->target = unresolved_target; + CallRuntimeHelperImm(QUICK_ENTRYPOINT_OFFSET(pInitializeStaticStorage), storage_index, + true); + // Copy helper's result into r_base, a no-op on all but MIPS. + OpRegCopy(r_base, TargetReg(kRet0)); + + LIR* initialized_target = NewLIR0(kPseudoTargetLabel); + initialized_branch->target = initialized_target; + + FreeTemp(r_tmp); } - LIR* skip_target = NewLIR0(kPseudoTargetLabel); - branch_over->target = skip_target; FreeTemp(r_method); } - // rBase now holds static storage base + // r_base now holds static storage base RegLocation rl_result = EvalLoc(rl_dest, kAnyReg, true); if (is_volatile) { GenMemBarrier(kLoadLoad); } if (is_long_or_double) { - LoadBaseDispWide(rBase, field_offset, rl_result.low_reg, + LoadBaseDispWide(r_base, field_offset, rl_result.low_reg, rl_result.high_reg, INVALID_SREG); } else { - LoadWordDisp(rBase, field_offset, rl_result.low_reg); + LoadWordDisp(r_base, field_offset, rl_result.low_reg); } - FreeTemp(rBase); + FreeTemp(r_base); if (is_long_or_double) { StoreValueWide(rl_dest, rl_result); } else { diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc index 9cffb3c4519..5edc8b6771b 100644 --- a/compiler/driver/compiler_driver.cc +++ b/compiler/driver/compiler_driver.cc @@ -992,14 +992,16 @@ bool CompilerDriver::ComputeInstanceFieldInfo(uint32_t field_idx, const DexCompi } bool CompilerDriver::ComputeStaticFieldInfo(uint32_t field_idx, const DexCompilationUnit* mUnit, - bool is_put, int* field_offset, int* ssb_index, - bool* is_referrers_class, bool* is_volatile) { + bool is_put, int* field_offset, int* storage_index, + bool* is_referrers_class, bool* is_volatile, + bool* is_initialized) { ScopedObjectAccess soa(Thread::Current()); // Conservative defaults. *field_offset = -1; - *ssb_index = -1; + *storage_index = -1; *is_referrers_class = false; *is_volatile = true; + *is_initialized = false; // Try to resolve field and ignore if an Incompatible Class Change Error (ie isn't static). mirror::ArtField* resolved_field = ComputeFieldReferencedFromCompilingMethod(soa, mUnit, field_idx); if (resolved_field != NULL && resolved_field->IsStatic()) { @@ -1010,6 +1012,7 @@ bool CompilerDriver::ComputeStaticFieldInfo(uint32_t field_idx, const DexCompila mirror::Class* fields_class = resolved_field->GetDeclaringClass(); if (fields_class == referrer_class) { *is_referrers_class = true; // implies no worrying about class initialization + *is_initialized = true; *field_offset = resolved_field->GetOffset().Int32Value(); *is_volatile = resolved_field->IsVolatile(); stats_->ResolvedLocalStaticField(); @@ -1034,17 +1037,19 @@ bool CompilerDriver::ComputeStaticFieldInfo(uint32_t field_idx, const DexCompila } bool is_write_to_final_from_wrong_class = is_put && resolved_field->IsFinal(); if (access_ok && !is_write_to_final_from_wrong_class) { - // We have the resolved field, we must make it into a ssbIndex for the referrer - // in its static storage base (which may fail if it doesn't have a slot for it) + // We have the resolved field, we must make it into a index for the referrer + // in its static storage (which may fail if it doesn't have a slot for it) // TODO: for images we can elide the static storage base null check // if we know there's a non-null entry in the image mirror::DexCache* dex_cache = mUnit->GetClassLinker()->FindDexCache(*mUnit->GetDexFile()); if (fields_class->GetDexCache() == dex_cache) { // common case where the dex cache of both the referrer and the field are the same, // no need to search the dex file - *ssb_index = fields_class->GetDexTypeIndex(); + *storage_index = fields_class->GetDexTypeIndex(); *field_offset = resolved_field->GetOffset().Int32Value(); *is_volatile = resolved_field->IsVolatile(); + *is_initialized = fields_class->IsInitialized() && + CanAssumeTypeIsPresentInDexCache(*mUnit->GetDexFile(), *storage_index); stats_->ResolvedStaticField(); return true; } @@ -1057,9 +1062,11 @@ bool CompilerDriver::ComputeStaticFieldInfo(uint32_t field_idx, const DexCompila mUnit->GetDexFile()->FindTypeId(mUnit->GetDexFile()->GetIndexForStringId(*string_id)); if (type_id != NULL) { // medium path, needs check of static storage base being initialized - *ssb_index = mUnit->GetDexFile()->GetIndexForTypeId(*type_id); + *storage_index = mUnit->GetDexFile()->GetIndexForTypeId(*type_id); *field_offset = resolved_field->GetOffset().Int32Value(); *is_volatile = resolved_field->IsVolatile(); + *is_initialized = fields_class->IsInitialized() && + CanAssumeTypeIsPresentInDexCache(*mUnit->GetDexFile(), *storage_index); stats_->ResolvedStaticField(); return true; } @@ -2184,11 +2191,6 @@ static void InitializeClass(const ParallelCompilationManager* manager, size_t cl } soa.Self()->AssertNoPendingException(); } - // If successfully initialized place in SSB array. - if (klass->IsInitialized()) { - int32_t ssb_index = klass->GetDexTypeIndex(); - klass->GetDexCache()->GetInitializedStaticStorage()->Set(ssb_index, klass.get()); - } } // Record the final class status if necessary. ClassReference ref(manager->GetDexFile(), class_def_index); diff --git a/compiler/driver/compiler_driver.h b/compiler/driver/compiler_driver.h index f4cc84dfe77..9e316242ba9 100644 --- a/compiler/driver/compiler_driver.h +++ b/compiler/driver/compiler_driver.h @@ -172,8 +172,7 @@ class CompilerDriver { // Callbacks from compiler to see what runtime checks must be generated. - bool CanAssumeTypeIsPresentInDexCache(const DexFile& dex_file, uint32_t type_idx) - LOCKS_EXCLUDED(Locks::mutator_lock_); + bool CanAssumeTypeIsPresentInDexCache(const DexFile& dex_file, uint32_t type_idx); bool CanAssumeStringIsPresentInDexCache(const DexFile& dex_file, uint32_t string_idx) LOCKS_EXCLUDED(Locks::mutator_lock_); @@ -198,8 +197,8 @@ class CompilerDriver { // Can we fastpath static field access? Computes field's offset, volatility and whether the // field is within the referrer (which can avoid checking class initialization). bool ComputeStaticFieldInfo(uint32_t field_idx, const DexCompilationUnit* mUnit, bool is_put, - int* field_offset, int* ssb_index, - bool* is_referrers_class, bool* is_volatile) + int* field_offset, int* storage_index, + bool* is_referrers_class, bool* is_volatile, bool* is_initialized) LOCKS_EXCLUDED(Locks::mutator_lock_); // Can we fastpath a interface, super class or virtual method call? Computes method's vtable diff --git a/compiler/image_writer.cc b/compiler/image_writer.cc index 02654ad55ae..556dec25ad0 100644 --- a/compiler/image_writer.cc +++ b/compiler/image_writer.cc @@ -316,7 +316,6 @@ void ImageWriter::PruneNonImageClasses() { Class* klass = dex_cache->GetResolvedType(i); if (klass != NULL && !IsImageClass(klass)) { dex_cache->SetResolvedType(i, NULL); - dex_cache->GetInitializedStaticStorage()->Set(i, NULL); } } for (size_t i = 0; i < dex_cache->NumResolvedMethods(); i++) { diff --git a/compiler/llvm/gbc_expander.cc b/compiler/llvm/gbc_expander.cc index b206a25f258..6423cd7dca7 100644 --- a/compiler/llvm/gbc_expander.cc +++ b/compiler/llvm/gbc_expander.cc @@ -199,8 +199,6 @@ class GBCExpanderPass : public llvm::FunctionPass { //---------------------------------------------------------------------------- llvm::Value* EmitLoadDexCacheAddr(art::MemberOffset dex_cache_offset); - llvm::Value* EmitLoadDexCacheStaticStorageFieldAddr(uint32_t type_idx); - llvm::Value* EmitLoadDexCacheResolvedTypeFieldAddr(uint32_t type_idx); llvm::Value* EmitLoadDexCacheResolvedMethodFieldAddr(uint32_t method_idx); @@ -287,8 +285,6 @@ class GBCExpanderPass : public llvm::FunctionPass { llvm::Value* Expand_LoadDeclaringClassSSB(llvm::Value* method_object_addr); - llvm::Value* Expand_LoadClassSSBFromDexCache(llvm::Value* type_idx_value); - llvm::Value* Expand_GetSDCalleeMethodObjAddrFast(llvm::Value* callee_method_idx_value); @@ -719,16 +715,6 @@ llvm::Value* GBCExpanderPass::EmitLoadDexCacheAddr(art::MemberOffset offset) { kTBAAConstJObject); } -llvm::Value* -GBCExpanderPass::EmitLoadDexCacheStaticStorageFieldAddr(uint32_t type_idx) { - llvm::Value* static_storage_dex_cache_addr = - EmitLoadDexCacheAddr(art::mirror::ArtMethod::DexCacheInitializedStaticStorageOffset()); - - llvm::Value* type_idx_value = irb_.getPtrEquivInt(type_idx); - - return EmitArrayGEP(static_storage_dex_cache_addr, type_idx_value, kObject); -} - llvm::Value* GBCExpanderPass::EmitLoadDexCacheResolvedTypeFieldAddr(uint32_t type_idx) { llvm::Value* resolved_type_dex_cache_addr = @@ -1212,17 +1198,6 @@ GBCExpanderPass::Expand_LoadDeclaringClassSSB(llvm::Value* method_object_addr) { kTBAAConstJObject); } -llvm::Value* -GBCExpanderPass::Expand_LoadClassSSBFromDexCache(llvm::Value* type_idx_value) { - uint32_t type_idx = - llvm::cast(type_idx_value)->getZExtValue(); - - llvm::Value* storage_field_addr = - EmitLoadDexCacheStaticStorageFieldAddr(type_idx); - - return irb_.CreateLoad(storage_field_addr, kTBAARuntimeInfo); -} - llvm::Value* GBCExpanderPass::Expand_GetSDCalleeMethodObjAddrFast(llvm::Value* callee_method_idx_value) { uint32_t callee_method_idx = @@ -1837,21 +1812,31 @@ llvm::Value* GBCExpanderPass::EmitLoadStaticStorage(uint32_t dex_pc, llvm::BasicBlock* block_load_static = CreateBasicBlockWithDexPC(dex_pc, "load_static"); + llvm::BasicBlock* block_check_init = CreateBasicBlockWithDexPC(dex_pc, "init"); llvm::BasicBlock* block_cont = CreateBasicBlockWithDexPC(dex_pc, "cont"); // Load static storage from dex cache - llvm::Value* storage_field_addr = - EmitLoadDexCacheStaticStorageFieldAddr(type_idx); + llvm::Value* storage_field_addr = EmitLoadDexCacheResolvedTypeFieldAddr(type_idx); llvm::Value* storage_object_addr = irb_.CreateLoad(storage_field_addr, kTBAARuntimeInfo); - llvm::BasicBlock* block_original = irb_.GetInsertBlock(); + // Test: Is the class resolved? + llvm::Value* equal_null = irb_.CreateICmpEQ(storage_object_addr, irb_.getJNull()); - // Test: Is the static storage of this class initialized? - llvm::Value* equal_null = - irb_.CreateICmpEQ(storage_object_addr, irb_.getJNull()); + irb_.CreateCondBr(equal_null, block_load_static, block_check_init, kUnlikely); - irb_.CreateCondBr(equal_null, block_load_static, block_cont, kUnlikely); + // storage_object_addr != null, so check if its initialized. + irb_.SetInsertPoint(block_check_init); + + llvm::Value* class_status = + irb_.LoadFromObjectOffset(storage_object_addr, + art::mirror::Class::StatusOffset().Int32Value(), + irb_.getJIntTy(), kTBAAHeapInstance); + + llvm::Value* is_not_initialized = + irb_.CreateICmpULT(class_status, irb_.getInt32(art::mirror::Class::kStatusInitialized)); + + irb_.CreateCondBr(is_not_initialized, block_load_static, block_cont, kUnlikely); // Failback routine to load the class object irb_.SetInsertPoint(block_load_static); @@ -1880,9 +1865,8 @@ llvm::Value* GBCExpanderPass::EmitLoadStaticStorage(uint32_t dex_pc, llvm::PHINode* phi = irb_.CreatePHI(irb_.getJObjectTy(), 2); - phi->addIncoming(storage_object_addr, block_original); + phi->addIncoming(storage_object_addr, block_check_init); phi->addIncoming(loaded_storage_object_addr, block_after_load_static); - return phi; } @@ -1895,10 +1879,11 @@ llvm::Value* GBCExpanderPass::Expand_HLSget(llvm::CallInst& call_inst, int ssb_index; bool is_referrers_class; bool is_volatile; + bool is_initialized; bool is_fast_path = driver_->ComputeStaticFieldInfo( field_idx, dex_compilation_unit_, false, - &field_offset, &ssb_index, &is_referrers_class, &is_volatile); + &field_offset, &ssb_index, &is_referrers_class, &is_volatile, &is_initialized); llvm::Value* static_field_value; @@ -1979,10 +1964,11 @@ void GBCExpanderPass::Expand_HLSput(llvm::CallInst& call_inst, int ssb_index; bool is_referrers_class; bool is_volatile; + bool is_initialized; bool is_fast_path = driver_->ComputeStaticFieldInfo( field_idx, dex_compilation_unit_, true, - &field_offset, &ssb_index, &is_referrers_class, &is_volatile); + &field_offset, &ssb_index, &is_referrers_class, &is_volatile, &is_initialized); if (!is_fast_path) { llvm::Function* runtime_func; @@ -3360,9 +3346,6 @@ GBCExpanderPass::ExpandIntrinsic(IntrinsicHelper::IntrinsicId intr_id, case IntrinsicHelper::LoadDeclaringClassSSB: { return Expand_LoadDeclaringClassSSB(call_inst.getArgOperand(0)); } - case IntrinsicHelper::LoadClassSSBFromDexCache: { - return Expand_LoadClassSSBFromDexCache(call_inst.getArgOperand(0)); - } case IntrinsicHelper::InitializeAndLoadClassSSB: { return ExpandToRuntime(InitializeStaticStorage, call_inst); } diff --git a/compiler/llvm/intrinsic_func_list.def b/compiler/llvm/intrinsic_func_list.def index 92537ba4196..887a62666fe 100644 --- a/compiler/llvm/intrinsic_func_list.def +++ b/compiler/llvm/intrinsic_func_list.def @@ -863,13 +863,6 @@ _EVAL_DEF_INTRINSICS_FUNC(LoadDeclaringClassSSB, kJavaObjectTy, _EXPAND_ARG1(kJavaMethodTy)) -// JavaObject* art_portable_load_class_ssb_from_dex_cache(uint32_t type_idx) -_EVAL_DEF_INTRINSICS_FUNC(LoadClassSSBFromDexCache, - art_portable_load_class_ssb_from_dex_cache, - kAttrReadOnly | kAttrNoThrow, - kJavaObjectTy, - _EXPAND_ARG1(kInt32ConstantTy)) - // JavaObject* art_portable_init_and_load_class_ssb(uint32_t type_idx, // Method* referrer, // Thread* thread) diff --git a/runtime/asm_support.h b/runtime/asm_support.h index e9bbf917614..06c7b53a664 100644 --- a/runtime/asm_support.h +++ b/runtime/asm_support.h @@ -39,7 +39,7 @@ #define STRING_DATA_OFFSET 12 // Offsets within java.lang.Method. -#define METHOD_DEX_CACHE_METHODS_OFFSET 16 -#define METHOD_CODE_OFFSET 40 +#define METHOD_DEX_CACHE_METHODS_OFFSET 12 +#define METHOD_CODE_OFFSET 36 #endif // ART_RUNTIME_ASM_SUPPORT_H_ diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index fbb47bdfaee..dcc50b78b42 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -1242,15 +1242,8 @@ mirror::DexCache* ClassLinker::AllocDexCache(Thread* self, const DexFile& dex_fi if (fields.get() == NULL) { return NULL; } - SirtRef > - initialized_static_storage(self, - AllocObjectArray(self, dex_file.NumTypeIds())); - if (initialized_static_storage.get() == NULL) { - return NULL; - } - dex_cache->Init(&dex_file, location.get(), strings.get(), types.get(), methods.get(), - fields.get(), initialized_static_storage.get()); + fields.get()); return dex_cache.get(); } @@ -1905,7 +1898,6 @@ mirror::ArtMethod* ClassLinker::LoadMethod(Thread* self, const DexFile& dex_file dst->SetDexCacheStrings(klass->GetDexCache()->GetStrings()); dst->SetDexCacheResolvedMethods(klass->GetDexCache()->GetResolvedMethods()); dst->SetDexCacheResolvedTypes(klass->GetDexCache()->GetResolvedTypes()); - dst->SetDexCacheInitializedStaticStorage(klass->GetDexCache()->GetInitializedStaticStorage()); uint32_t access_flags = it.GetMemberAccessFlags(); @@ -2941,8 +2933,6 @@ static void CheckProxyMethod(mirror::ArtMethod* method, CHECK_EQ(prototype->GetDexCacheStrings(), method->GetDexCacheStrings()); CHECK_EQ(prototype->GetDexCacheResolvedMethods(), method->GetDexCacheResolvedMethods()); CHECK_EQ(prototype->GetDexCacheResolvedTypes(), method->GetDexCacheResolvedTypes()); - CHECK_EQ(prototype->GetDexCacheInitializedStaticStorage(), - method->GetDexCacheInitializedStaticStorage()); CHECK_EQ(prototype->GetDexMethodIndex(), method->GetDexMethodIndex()); MethodHelper mh(method); diff --git a/runtime/class_linker_test.cc b/runtime/class_linker_test.cc index 34134fae0e6..1744050a007 100644 --- a/runtime/class_linker_test.cc +++ b/runtime/class_linker_test.cc @@ -159,15 +159,12 @@ class ClassLinkerTest : public CommonTest { EXPECT_TRUE(method->GetDexCacheStrings() != NULL); EXPECT_TRUE(method->GetDexCacheResolvedMethods() != NULL); EXPECT_TRUE(method->GetDexCacheResolvedTypes() != NULL); - EXPECT_TRUE(method->GetDexCacheInitializedStaticStorage() != NULL); EXPECT_EQ(method->GetDeclaringClass()->GetDexCache()->GetStrings(), method->GetDexCacheStrings()); EXPECT_EQ(method->GetDeclaringClass()->GetDexCache()->GetResolvedMethods(), method->GetDexCacheResolvedMethods()); EXPECT_EQ(method->GetDeclaringClass()->GetDexCache()->GetResolvedTypes(), method->GetDexCacheResolvedTypes()); - EXPECT_EQ(method->GetDeclaringClass()->GetDexCache()->GetInitializedStaticStorage(), - method->GetDexCacheInitializedStaticStorage()); } void AssertField(mirror::Class* klass, mirror::ArtField* field) @@ -468,7 +465,6 @@ struct ArtMethodOffsets : public CheckOffsets { ArtMethodOffsets() : CheckOffsets(false, "Ljava/lang/reflect/ArtMethod;") { // alphabetical references offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::ArtMethod, declaring_class_), "declaringClass")); - offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::ArtMethod, dex_cache_initialized_static_storage_), "dexCacheInitializedStaticStorage")); offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::ArtMethod, dex_cache_resolved_methods_), "dexCacheResolvedMethods")); offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::ArtMethod, dex_cache_resolved_types_), "dexCacheResolvedTypes")); offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::ArtMethod, dex_cache_strings_), "dexCacheStrings")); @@ -607,7 +603,6 @@ struct DexCacheOffsets : public CheckOffsets { DexCacheOffsets() : CheckOffsets(false, "Ljava/lang/DexCache;") { // alphabetical references offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::DexCache, dex_), "dex")); - offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::DexCache, initialized_static_storage_), "initializedStaticStorage")); offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::DexCache, location_), "location")); offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::DexCache, resolved_fields_), "resolvedFields")); offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::DexCache, resolved_methods_), "resolvedMethods")); @@ -1006,13 +1001,12 @@ TEST_F(ClassLinkerTest, ResolveVerifyAndClinit) { const DexFile::TypeId* type_id = dex_file->FindTypeId(dex_file->GetIndexForStringId(*string_id)); ASSERT_TRUE(type_id != NULL); uint32_t type_idx = dex_file->GetIndexForTypeId(*type_id); - EXPECT_TRUE(clinit->GetDexCacheInitializedStaticStorage()->Get(type_idx) == NULL); - mirror::StaticStorageBase* uninit = ResolveVerifyAndClinit(type_idx, clinit, Thread::Current(), true, false); + mirror::Class* uninit = ResolveVerifyAndClinit(type_idx, clinit, Thread::Current(), true, false); EXPECT_TRUE(uninit != NULL); - EXPECT_TRUE(clinit->GetDexCacheInitializedStaticStorage()->Get(type_idx) == NULL); - mirror::StaticStorageBase* init = ResolveVerifyAndClinit(type_idx, getS0, Thread::Current(), true, false); + EXPECT_FALSE(uninit->IsInitialized()); + mirror::Class* init = ResolveVerifyAndClinit(type_idx, getS0, Thread::Current(), true, false); EXPECT_TRUE(init != NULL); - EXPECT_EQ(init, clinit->GetDexCacheInitializedStaticStorage()->Get(type_idx)); + EXPECT_TRUE(init->IsInitialized()); } TEST_F(ClassLinkerTest, FinalizableBit) { diff --git a/runtime/entrypoints/entrypoint_utils.h b/runtime/entrypoints/entrypoint_utils.h index a60446caba9..e7fe0725d48 100644 --- a/runtime/entrypoints/entrypoint_utils.h +++ b/runtime/entrypoints/entrypoint_utils.h @@ -517,15 +517,15 @@ static inline mirror::Class* ResolveVerifyAndClinit(uint32_t type_idx, SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); mirror::Class* klass = class_linker->ResolveType(type_idx, referrer); - if (UNLIKELY(klass == NULL)) { + if (UNLIKELY(klass == nullptr)) { CHECK(self->IsExceptionPending()); - return NULL; // Failure - Indicate to caller to deliver exception + return nullptr; // Failure - Indicate to caller to deliver exception } // Perform access check if necessary. mirror::Class* referring_class = referrer->GetDeclaringClass(); if (verify_access && UNLIKELY(!referring_class->CanAccess(klass))) { ThrowIllegalAccessErrorClass(referring_class, klass); - return NULL; // Failure - Indicate to caller to deliver exception + return nullptr; // Failure - Indicate to caller to deliver exception } // If we're just implementing const-class, we shouldn't call . if (!can_run_clinit) { @@ -541,9 +541,8 @@ static inline mirror::Class* ResolveVerifyAndClinit(uint32_t type_idx, SirtRef sirt_class(self, klass); if (!class_linker->EnsureInitialized(sirt_class, true, true)) { CHECK(self->IsExceptionPending()); - return NULL; // Failure - Indicate to caller to deliver exception + return nullptr; // Failure - Indicate to caller to deliver exception } - referrer->GetDexCacheInitializedStaticStorage()->Set(type_idx, sirt_class.get()); return sirt_class.get(); } diff --git a/runtime/mirror/art_method-inl.h b/runtime/mirror/art_method-inl.h index c9bf1609a04..088f616d410 100644 --- a/runtime/mirror/art_method-inl.h +++ b/runtime/mirror/art_method-inl.h @@ -73,12 +73,6 @@ inline ObjectArray* ArtMethod::GetDexCacheResolvedTypes() const { OFFSET_OF_OBJECT_MEMBER(ArtMethod, dex_cache_resolved_types_), false); } -inline ObjectArray* ArtMethod::GetDexCacheInitializedStaticStorage() const { - return GetFieldObject*>( - OFFSET_OF_OBJECT_MEMBER(ArtMethod, dex_cache_initialized_static_storage_), - false); -} - inline uint32_t ArtMethod::GetCodeSize() const { DCHECK(!IsRuntimeMethod() && !IsProxyMethod()) << PrettyMethod(this); uintptr_t code = reinterpret_cast(GetEntryPointFromCompiledCode()); diff --git a/runtime/mirror/art_method.cc b/runtime/mirror/art_method.cc index a4f6b3b460e..f4a076cf13a 100644 --- a/runtime/mirror/art_method.cc +++ b/runtime/mirror/art_method.cc @@ -86,11 +86,6 @@ void ArtMethod::SetDexCacheResolvedTypes(ObjectArray* new_dex_cache_class new_dex_cache_classes, false); } -void ArtMethod::SetDexCacheInitializedStaticStorage(ObjectArray* new_value) { - SetFieldObject(OFFSET_OF_OBJECT_MEMBER(ArtMethod, dex_cache_initialized_static_storage_), - new_value, false); -} - size_t ArtMethod::NumArgRegisters(const StringPiece& shorty) { CHECK_LE(1, shorty.length()); uint32_t num_registers = 0; diff --git a/runtime/mirror/art_method.h b/runtime/mirror/art_method.h index d5524ec87b8..963b4d554b6 100644 --- a/runtime/mirror/art_method.h +++ b/runtime/mirror/art_method.h @@ -184,11 +184,6 @@ class MANAGED ArtMethod : public Object { return OFFSET_OF_OBJECT_MEMBER(ArtMethod, dex_cache_resolved_types_); } - static MemberOffset DexCacheInitializedStaticStorageOffset() { - return OFFSET_OF_OBJECT_MEMBER(ArtMethod, - dex_cache_initialized_static_storage_); - } - ObjectArray* GetDexCacheResolvedMethods() const; void SetDexCacheResolvedMethods(ObjectArray* new_dex_cache_methods) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); @@ -197,10 +192,6 @@ class MANAGED ArtMethod : public Object { void SetDexCacheResolvedTypes(ObjectArray* new_dex_cache_types) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - ObjectArray* GetDexCacheInitializedStaticStorage() const; - void SetDexCacheInitializedStaticStorage(ObjectArray* new_value) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - // Find the method that this method overrides ArtMethod* FindOverriddenMethod() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); @@ -390,9 +381,6 @@ class MANAGED ArtMethod : public Object { // The class we are a part of Class* declaring_class_; - // short cuts to declaring_class_->dex_cache_ member for fast compiled code access - ObjectArray* dex_cache_initialized_static_storage_; - // short cuts to declaring_class_->dex_cache_ member for fast compiled code access ObjectArray* dex_cache_resolved_methods_; diff --git a/runtime/mirror/class.h b/runtime/mirror/class.h index 50ede668dd8..9aa23d91d32 100644 --- a/runtime/mirror/class.h +++ b/runtime/mirror/class.h @@ -70,15 +70,8 @@ class ClassLoader; class DexCache; class IfTable; -// Type for the InitializedStaticStorage table. Currently the Class -// provides the static storage. However, this might change to an Array -// to improve image sharing, so we use this type to avoid assumptions -// on the current storage. -class MANAGED StaticStorageBase : public Object { -}; - // C++ mirror of java.lang.Class -class MANAGED Class : public StaticStorageBase { +class MANAGED Class : public Object { public: // Class Status // @@ -133,6 +126,10 @@ class MANAGED Class : public StaticStorageBase { void SetStatus(Status new_status, Thread* self) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + static MemberOffset StatusOffset() { + return OFFSET_OF_OBJECT_MEMBER(Class, status_); + } + // Returns true if the class has failed to link. bool IsErroneous() const { return GetStatus() == kStatusError; diff --git a/runtime/mirror/dex_cache.cc b/runtime/mirror/dex_cache.cc index 00531e30768..fa0900c0eca 100644 --- a/runtime/mirror/dex_cache.cc +++ b/runtime/mirror/dex_cache.cc @@ -36,15 +36,13 @@ void DexCache::Init(const DexFile* dex_file, ObjectArray* strings, ObjectArray* resolved_types, ObjectArray* resolved_methods, - ObjectArray* resolved_fields, - ObjectArray* initialized_static_storage) { - CHECK(dex_file != NULL); - CHECK(location != NULL); - CHECK(strings != NULL); - CHECK(resolved_types != NULL); - CHECK(resolved_methods != NULL); - CHECK(resolved_fields != NULL); - CHECK(initialized_static_storage != NULL); + ObjectArray* resolved_fields) { + CHECK(dex_file != nullptr); + CHECK(location != nullptr); + CHECK(strings != nullptr); + CHECK(resolved_types != nullptr); + CHECK(resolved_methods != nullptr); + CHECK(resolved_fields != nullptr); SetFieldPtr(OFFSET_OF_OBJECT_MEMBER(DexCache, dex_file_), dex_file, false); SetFieldObject(OFFSET_OF_OBJECT_MEMBER(DexCache, location_), location, false); @@ -52,8 +50,6 @@ void DexCache::Init(const DexFile* dex_file, SetFieldObject(OFFSET_OF_OBJECT_MEMBER(DexCache, resolved_types_), resolved_types, false); SetFieldObject(ResolvedMethodsOffset(), resolved_methods, false); SetFieldObject(ResolvedFieldsOffset(), resolved_fields, false); - SetFieldObject(OFFSET_OF_OBJECT_MEMBER(DexCache, initialized_static_storage_), - initialized_static_storage, false); Runtime* runtime = Runtime::Current(); if (runtime->HasResolutionMethod()) { @@ -68,11 +64,11 @@ void DexCache::Init(const DexFile* dex_file, void DexCache::Fixup(ArtMethod* trampoline) { // Fixup the resolve methods array to contain trampoline for resolution. - CHECK(trampoline != NULL); + CHECK(trampoline != nullptr); ObjectArray* resolved_methods = GetResolvedMethods(); size_t length = resolved_methods->GetLength(); for (size_t i = 0; i < length; i++) { - if (resolved_methods->GetWithoutChecks(i) == NULL) { + if (resolved_methods->GetWithoutChecks(i) == nullptr) { resolved_methods->SetWithoutChecks(i, trampoline); } } diff --git a/runtime/mirror/dex_cache.h b/runtime/mirror/dex_cache.h index 0522f134afa..a5fe598f5c8 100644 --- a/runtime/mirror/dex_cache.h +++ b/runtime/mirror/dex_cache.h @@ -47,8 +47,7 @@ class MANAGED DexCache : public Object { ObjectArray* strings, ObjectArray* types, ObjectArray* methods, - ObjectArray* fields, - ObjectArray* initialized_static_storage) + ObjectArray* fields) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); void Fixup(ArtMethod* trampoline) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); @@ -85,11 +84,6 @@ class MANAGED DexCache : public Object { return GetResolvedFields()->GetLength(); } - size_t NumInitializedStaticStorage() const - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - return GetInitializedStaticStorage()->GetLength(); - } - String* GetResolvedString(uint32_t string_idx) const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { return GetStrings()->Get(string_idx); @@ -149,12 +143,6 @@ class MANAGED DexCache : public Object { return GetFieldObject< ObjectArray* >(ResolvedFieldsOffset(), false); } - ObjectArray* GetInitializedStaticStorage() const - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - return GetFieldObject< ObjectArray* >( - OFFSET_OF_OBJECT_MEMBER(DexCache, initialized_static_storage_), false); - } - const DexFile* GetDexFile() const { return GetFieldPtr(OFFSET_OF_OBJECT_MEMBER(DexCache, dex_file_), false); } @@ -165,7 +153,6 @@ class MANAGED DexCache : public Object { private: Object* dex_; - ObjectArray* initialized_static_storage_; String* location_; ObjectArray* resolved_fields_; ObjectArray* resolved_methods_; diff --git a/runtime/mirror/dex_cache_test.cc b/runtime/mirror/dex_cache_test.cc index 441c6da8a04..6bed224bd59 100644 --- a/runtime/mirror/dex_cache_test.cc +++ b/runtime/mirror/dex_cache_test.cc @@ -39,13 +39,11 @@ TEST_F(DexCacheTest, Open) { EXPECT_EQ(java_lang_dex_file_->NumTypeIds(), dex_cache->NumResolvedTypes()); EXPECT_EQ(java_lang_dex_file_->NumMethodIds(), dex_cache->NumResolvedMethods()); EXPECT_EQ(java_lang_dex_file_->NumFieldIds(), dex_cache->NumResolvedFields()); - EXPECT_EQ(java_lang_dex_file_->NumTypeIds(), dex_cache->NumInitializedStaticStorage()); EXPECT_LE(0, dex_cache->GetStrings()->GetLength()); EXPECT_LE(0, dex_cache->GetResolvedTypes()->GetLength()); EXPECT_LE(0, dex_cache->GetResolvedMethods()->GetLength()); EXPECT_LE(0, dex_cache->GetResolvedFields()->GetLength()); - EXPECT_LE(0, dex_cache->GetInitializedStaticStorage()->GetLength()); EXPECT_EQ(java_lang_dex_file_->NumStringIds(), static_cast(dex_cache->GetStrings()->GetLength())); @@ -55,8 +53,6 @@ TEST_F(DexCacheTest, Open) { static_cast(dex_cache->GetResolvedMethods()->GetLength())); EXPECT_EQ(java_lang_dex_file_->NumFieldIds(), static_cast(dex_cache->GetResolvedFields()->GetLength())); - EXPECT_EQ(java_lang_dex_file_->NumTypeIds(), - static_cast(dex_cache->GetInitializedStaticStorage()->GetLength())); } } // namespace mirror diff --git a/runtime/native/dalvik_system_VMRuntime.cc b/runtime/native/dalvik_system_VMRuntime.cc index 726a8f17695..6f30cc9fd91 100644 --- a/runtime/native/dalvik_system_VMRuntime.cc +++ b/runtime/native/dalvik_system_VMRuntime.cc @@ -234,7 +234,6 @@ static void PreloadDexCachesResolveType(mirror::DexCache* dex_cache, uint32_t ty return; } // LOG(INFO) << "VMRuntime.preloadDexCaches static storage klass=" << class_name; - dex_cache->GetInitializedStaticStorage()->Set(type_idx, klass); } // Based on ClassLinker::ResolveField. @@ -306,12 +305,10 @@ struct DexCacheStats { uint32_t num_types; uint32_t num_fields; uint32_t num_methods; - uint32_t num_static_storage; DexCacheStats() : num_strings(0), num_types(0), num_fields(0), - num_methods(0), - num_static_storage(0) {} + num_methods(0) {} }; static const bool kPreloadDexCachesEnabled = true; @@ -339,7 +336,6 @@ static void PreloadDexCachesStatsTotal(DexCacheStats* total) { total->num_fields += dex_file->NumFieldIds(); total->num_methods += dex_file->NumMethodIds(); total->num_types += dex_file->NumTypeIds(); - total->num_static_storage += dex_file->NumTypeIds(); } } @@ -378,12 +374,6 @@ static void PreloadDexCachesStatsFilled(DexCacheStats* filled) filled->num_methods++; } } - for (size_t i = 0; i < dex_cache->NumInitializedStaticStorage(); i++) { - mirror::StaticStorageBase* klass = dex_cache->GetInitializedStaticStorage()->Get(i); - if (klass != NULL) { - filled->num_static_storage++; - } - } } } @@ -477,10 +467,6 @@ static void VMRuntime_preloadDexCaches(JNIEnv* env, jobject) { total.num_fields, before.num_fields, after.num_fields); LOG(INFO) << StringPrintf("VMRuntime.preloadDexCaches methods total=%d before=%d after=%d", total.num_methods, before.num_methods, after.num_methods); - LOG(INFO) << StringPrintf("VMRuntime.preloadDexCaches storage total=%d before=%d after=%d", - total.num_static_storage, - before.num_static_storage, - after.num_static_storage); LOG(INFO) << StringPrintf("VMRuntime.preloadDexCaches finished"); } } diff --git a/runtime/oat.cc b/runtime/oat.cc index 52e74abd750..caf18f1c80e 100644 --- a/runtime/oat.cc +++ b/runtime/oat.cc @@ -22,7 +22,7 @@ namespace art { const uint8_t OatHeader::kOatMagic[] = { 'o', 'a', 't', '\n' }; -const uint8_t OatHeader::kOatVersion[] = { '0', '1', '2', '\0' }; +const uint8_t OatHeader::kOatVersion[] = { '0', '1', '3', '\0' }; OatHeader::OatHeader() { memset(this, 0, sizeof(*this)); From bd288c2c1206bc99fafebfb9120a83f13cf9723b Mon Sep 17 00:00:00 2001 From: Razvan A Lupusoru Date: Fri, 20 Dec 2013 17:27:23 -0800 Subject: [PATCH 0335/2402] Add conditional move support to x86 and allow GenMinMax to use it X86 supports conditional moves which is useful for reducing branchiness. This patch adds support to the x86 backend to generate conditional reg to reg operations. Both encoder and decoder support was added for cmov. The x86 version of GenMinMax used for generating inlined version Math.min/max has been updated to make use of the conditional move support. Change-Id: I92c5428e40aa8ff88bd3071619957ac3130efae7 Signed-off-by: Razvan A Lupusoru --- compiler/dex/compiler_enums.h | 1 + compiler/dex/quick/arm/codegen_arm.h | 1 + compiler/dex/quick/arm/utility_arm.cc | 5 ++ compiler/dex/quick/mips/codegen_mips.h | 1 + compiler/dex/quick/mips/utility_mips.cc | 5 ++ compiler/dex/quick/mir_to_lir.h | 68 +++++++++++++++++++++++++ compiler/dex/quick/x86/assemble_x86.cc | 31 +++++++++++ compiler/dex/quick/x86/codegen_x86.h | 11 ++++ compiler/dex/quick/x86/int_x86.cc | 37 ++++++++++---- compiler/dex/quick/x86/utility_x86.cc | 6 +++ compiler/dex/quick/x86/x86_lir.h | 4 ++ disassembler/disassembler_x86.cc | 6 +++ 12 files changed, 167 insertions(+), 9 deletions(-) diff --git a/compiler/dex/compiler_enums.h b/compiler/dex/compiler_enums.h index 5cc906fba90..4650f25a90b 100644 --- a/compiler/dex/compiler_enums.h +++ b/compiler/dex/compiler_enums.h @@ -161,6 +161,7 @@ std::ostream& operator<<(std::ostream& os, const OpSize& kind); enum OpKind { kOpMov, + kOpCmov, kOpMvn, kOpCmp, kOpLsl, diff --git a/compiler/dex/quick/arm/codegen_arm.h b/compiler/dex/quick/arm/codegen_arm.h index c04f1d6abf3..2bc579a6758 100644 --- a/compiler/dex/quick/arm/codegen_arm.h +++ b/compiler/dex/quick/arm/codegen_arm.h @@ -153,6 +153,7 @@ class ArmMir2Lir : public Mir2Lir { LIR* OpRegImm(OpKind op, int r_dest_src1, int value); LIR* OpRegMem(OpKind op, int r_dest, int rBase, int offset); LIR* OpRegReg(OpKind op, int r_dest_src1, int r_src2); + LIR* OpCondRegReg(OpKind op, ConditionCode cc, int r_dest, int r_src); LIR* OpRegRegImm(OpKind op, int r_dest, int r_src1, int value); LIR* OpRegRegReg(OpKind op, int r_dest, int r_src1, int r_src2); LIR* OpTestSuspend(LIR* target); diff --git a/compiler/dex/quick/arm/utility_arm.cc b/compiler/dex/quick/arm/utility_arm.cc index fa05d6c5a80..07fc6c790dd 100644 --- a/compiler/dex/quick/arm/utility_arm.cc +++ b/compiler/dex/quick/arm/utility_arm.cc @@ -367,6 +367,11 @@ LIR* ArmMir2Lir::OpRegReg(OpKind op, int r_dest_src1, int r_src2) { return OpRegRegShift(op, r_dest_src1, r_src2, 0); } +LIR* ArmMir2Lir::OpCondRegReg(OpKind op, ConditionCode cc, int r_dest, int r_src) { + LOG(FATAL) << "Unexpected use of OpCondRegReg for Arm"; + return NULL; +} + LIR* ArmMir2Lir::OpRegRegRegShift(OpKind op, int r_dest, int r_src1, int r_src2, int shift) { ArmOpcode opcode = kThumbBkpt; diff --git a/compiler/dex/quick/mips/codegen_mips.h b/compiler/dex/quick/mips/codegen_mips.h index 97dc2b3ad15..a5a14d5c0e0 100644 --- a/compiler/dex/quick/mips/codegen_mips.h +++ b/compiler/dex/quick/mips/codegen_mips.h @@ -151,6 +151,7 @@ class MipsMir2Lir : public Mir2Lir { LIR* OpRegImm(OpKind op, int r_dest_src1, int value); LIR* OpRegMem(OpKind op, int r_dest, int rBase, int offset); LIR* OpRegReg(OpKind op, int r_dest_src1, int r_src2); + LIR* OpCondRegReg(OpKind op, ConditionCode cc, int r_dest, int r_src); LIR* OpRegRegImm(OpKind op, int r_dest, int r_src1, int value); LIR* OpRegRegReg(OpKind op, int r_dest, int r_src1, int r_src2); LIR* OpTestSuspend(LIR* target); diff --git a/compiler/dex/quick/mips/utility_mips.cc b/compiler/dex/quick/mips/utility_mips.cc index 65c82c08a08..c5e2b36ef2d 100644 --- a/compiler/dex/quick/mips/utility_mips.cc +++ b/compiler/dex/quick/mips/utility_mips.cc @@ -325,6 +325,11 @@ LIR* MipsMir2Lir::OpRegReg(OpKind op, int r_dest_src1, int r_src2) { return NewLIR2(opcode, r_dest_src1, r_src2); } +LIR* MipsMir2Lir::OpCondRegReg(OpKind op, ConditionCode cc, int r_dest, int r_src) { + LOG(FATAL) << "Unexpected use of OpCondRegReg for MIPS"; + return NULL; +} + LIR* MipsMir2Lir::LoadConstantWide(int r_dest_lo, int r_dest_hi, int64_t value) { LIR *res; res = LoadConstantNoClobber(r_dest_lo, Low32Bits(value)); diff --git a/compiler/dex/quick/mir_to_lir.h b/compiler/dex/quick/mir_to_lir.h index 9dc35c6f788..3f7ec1e5f09 100644 --- a/compiler/dex/quick/mir_to_lir.h +++ b/compiler/dex/quick/mir_to_lir.h @@ -421,8 +421,26 @@ class Mir2Lir : public Backend { RegLocation UpdateLoc(RegLocation loc); RegLocation UpdateLocWide(RegLocation loc); RegLocation UpdateRawLoc(RegLocation loc); + + /** + * @brief Used to load register location into a typed temporary or pair of temporaries. + * @see EvalLoc + * @param loc The register location to load from. + * @param reg_class Type of register needed. + * @param update Whether the liveness information should be updated. + * @return Returns the properly typed temporary in physical register pairs. + */ RegLocation EvalLocWide(RegLocation loc, int reg_class, bool update); + + /** + * @brief Used to load register location into a typed temporary. + * @param loc The register location to load from. + * @param reg_class Type of register needed. + * @param update Whether the liveness information should be updated. + * @return Returns the properly typed temporary in physical register. + */ RegLocation EvalLoc(RegLocation loc, int reg_class, bool update); + void CountRefs(RefCounts* core_counts, RefCounts* fp_counts, size_t num_regs); void DumpCounts(const RefCounts* arr, int size, const char* msg); void DoPromotion(); @@ -541,7 +559,22 @@ class Mir2Lir : public Backend { uint32_t vtable_idx, uintptr_t direct_code, uintptr_t direct_method, InvokeType type, bool skip_this); + + /** + * @brief Used to determine the register location of destination. + * @details This is needed during generation of inline intrinsics because it finds destination of return, + * either the physical register or the target of move-result. + * @param info Information about the invoke. + * @return Returns the destination location. + */ RegLocation InlineTarget(CallInfo* info); + + /** + * @brief Used to determine the wide register location of destination. + * @see InlineTarget + * @param info Information about the invoke. + * @return Returns the destination location. + */ RegLocation InlineTargetWide(CallInfo* info); bool GenInlinedCharAt(CallInfo* info); @@ -576,7 +609,20 @@ class Mir2Lir : public Backend { void LoadValueDirectWide(RegLocation rl_src, int reg_lo, int reg_hi); void LoadValueDirectWideFixed(RegLocation rl_src, int reg_lo, int reg_hi); LIR* StoreWordDisp(int rBase, int displacement, int r_src); + + /** + * @brief Used to do the final store in the destination as per bytecode semantics. + * @param rl_dest The destination dalvik register location. + * @param rl_src The source register location. Can be either physical register or dalvik register. + */ void StoreValue(RegLocation rl_dest, RegLocation rl_src); + + /** + * @brief Used to do the final store in a wide destination as per bytecode semantics. + * @see StoreValue + * @param rl_dest The destination dalvik register location. + * @param rl_src The source register location. Can be either physical register or dalvik register. + */ void StoreValueWide(RegLocation rl_dest, RegLocation rl_src); // Shared by all targets - implemented in mir_to_lir.cc. @@ -663,7 +709,18 @@ class Mir2Lir : public Backend { virtual void GenConversion(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src) = 0; virtual bool GenInlinedCas(CallInfo* info, bool is_long, bool is_object) = 0; + + /** + * @brief Used to generate code for intrinsic java\.lang\.Math methods min and max. + * @details This is also applicable for java\.lang\.StrictMath since it is a simple algorithm + * that applies on integers. The generated code will write the smallest or largest value + * directly into the destination register as specified by the invoke information. + * @param info Information about the invoke. + * @param is_min If true generates code that computes minimum. Otherwise computes maximum. + * @return Returns true if successfully generated + */ virtual bool GenInlinedMinMaxInt(CallInfo* info, bool is_min) = 0; + virtual bool GenInlinedSqrt(CallInfo* info) = 0; virtual bool GenInlinedPeek(CallInfo* info, OpSize size) = 0; virtual bool GenInlinedPoke(CallInfo* info, OpSize size) = 0; @@ -738,6 +795,17 @@ class Mir2Lir : public Backend { virtual LIR* OpRegImm(OpKind op, int r_dest_src1, int value) = 0; virtual LIR* OpRegMem(OpKind op, int r_dest, int rBase, int offset) = 0; virtual LIR* OpRegReg(OpKind op, int r_dest_src1, int r_src2) = 0; + + /** + * @brief Used for generating a conditional register to register operation. + * @param op The opcode kind. + * @param cc The condition code that when true will perform the opcode. + * @param r_dest The destination physical register. + * @param r_src The source physical register. + * @return Returns the newly created LIR or null in case of creation failure. + */ + virtual LIR* OpCondRegReg(OpKind op, ConditionCode cc, int r_dest, int r_src) = 0; + virtual LIR* OpRegRegImm(OpKind op, int r_dest, int r_src1, int value) = 0; virtual LIR* OpRegRegReg(OpKind op, int r_dest, int r_src1, int r_src2) = 0; virtual LIR* OpTestSuspend(LIR* target) = 0; diff --git a/compiler/dex/quick/x86/assemble_x86.cc b/compiler/dex/quick/x86/assemble_x86.cc index c24f0e33f89..1dcff652ba0 100644 --- a/compiler/dex/quick/x86/assemble_x86.cc +++ b/compiler/dex/quick/x86/assemble_x86.cc @@ -177,6 +177,8 @@ ENCODING_MAP(Cmp, IS_LOAD, 0, 0, { kX86Lea32RA, kRegArray, IS_QUIN_OP | REG_DEF0_USE12, { 0, 0, 0x8D, 0, 0, 0, 0, 0 }, "Lea32RA", "!0r,[!1r+!2r<operands[0], lir->operands[1], false); case kArrayCond: // lir operands - 0: base, 1: index, 2: scale, 3: disp, 4: cond return ComputeSize(entry, lir->operands[0], lir->operands[3], true); + case kRegRegCond: // lir operands - 0: reg, 1: reg, 2: cond + return ComputeSize(entry, 0, 0, false); case kJcc: if (lir->opcode == kX86Jcc8) { return 2; // opcode + rel8 @@ -860,6 +864,30 @@ void X86Mir2Lir::EmitRegCond(const X86EncodingMap* entry, uint8_t reg, uint8_t c DCHECK_EQ(entry->skeleton.immediate_bytes, 0); } +void X86Mir2Lir::EmitRegRegCond(const X86EncodingMap* entry, uint8_t reg1, uint8_t reg2, uint8_t condition) { + // Generate prefix and opcode without the condition + EmitPrefixAndOpcode(entry); + + // Now add the condition. The last byte of opcode is the one that receives it. + DCHECK_LE(condition, 0xF); + code_buffer_.back() += condition; + + // Not expecting to have to encode immediate or do anything special for ModR/M since there are two registers. + DCHECK_EQ(0, entry->skeleton.immediate_bytes); + DCHECK_EQ(0, entry->skeleton.modrm_opcode); + + // Check that registers requested for encoding are sane. + DCHECK_LT(reg1, 8); + DCHECK_LT(reg2, 8); + + // For register to register encoding, the mod is 3. + const uint8_t mod = (3 << 6); + + // Encode the ModR/M byte now. + const uint8_t modrm = mod | (reg1 << 3) | reg2; + code_buffer_.push_back(modrm); +} + void X86Mir2Lir::EmitJmp(const X86EncodingMap* entry, int rel) { if (entry->opcode == kX86Jmp8) { DCHECK(IS_SIMM8(rel)); @@ -1178,6 +1206,9 @@ AssemblerStatus X86Mir2Lir::AssembleInstructions(CodeOffset start_addr) { case kRegCond: // lir operands - 0: reg, 1: condition EmitRegCond(entry, lir->operands[0], lir->operands[1]); break; + case kRegRegCond: // lir operands - 0: reg, 1: reg, 2: condition + EmitRegRegCond(entry, lir->operands[0], lir->operands[1], lir->operands[2]); + break; case kJmp: // lir operands - 0: rel EmitJmp(entry, lir->operands[0]); break; diff --git a/compiler/dex/quick/x86/codegen_x86.h b/compiler/dex/quick/x86/codegen_x86.h index 22c44529912..e6621f3bcb3 100644 --- a/compiler/dex/quick/x86/codegen_x86.h +++ b/compiler/dex/quick/x86/codegen_x86.h @@ -153,6 +153,7 @@ class X86Mir2Lir : public Mir2Lir { LIR* OpRegImm(OpKind op, int r_dest_src1, int value); LIR* OpRegMem(OpKind op, int r_dest, int rBase, int offset); LIR* OpRegReg(OpKind op, int r_dest_src1, int r_src2); + LIR* OpCondRegReg(OpKind op, ConditionCode cc, int r_dest, int r_src); LIR* OpRegRegImm(OpKind op, int r_dest, int r_src1, int value); LIR* OpRegRegReg(OpKind op, int r_dest, int r_src1, int r_src2); LIR* OpTestSuspend(LIR* target); @@ -201,6 +202,16 @@ class X86Mir2Lir : public Mir2Lir { void EmitShiftRegImm(const X86EncodingMap* entry, uint8_t reg, int imm); void EmitShiftRegCl(const X86EncodingMap* entry, uint8_t reg, uint8_t cl); void EmitRegCond(const X86EncodingMap* entry, uint8_t reg, uint8_t condition); + + /** + * @brief Used for encoding conditional register to register operation. + * @param entry The entry in the encoding map for the opcode. + * @param reg1 The first physical register. + * @param reg2 The second physical register. + * @param condition The condition code for operation. + */ + void EmitRegRegCond(const X86EncodingMap* entry, uint8_t reg1, uint8_t reg2, uint8_t condition); + void EmitJmp(const X86EncodingMap* entry, int rel); void EmitJcc(const X86EncodingMap* entry, int rel, uint8_t cc); void EmitCallMem(const X86EncodingMap* entry, uint8_t base, int disp); diff --git a/compiler/dex/quick/x86/int_x86.cc b/compiler/dex/quick/x86/int_x86.cc index 75eddd60ffa..11ccd4b35b8 100644 --- a/compiler/dex/quick/x86/int_x86.cc +++ b/compiler/dex/quick/x86/int_x86.cc @@ -130,7 +130,7 @@ LIR* X86Mir2Lir::OpRegCopyNoInsert(int r_dest, int r_src) { return OpFpRegCopy(r_dest, r_src); LIR* res = RawLIR(current_dalvik_offset_, kX86Mov32RR, r_dest, r_src); - if (r_dest == r_src) { + if (!(cu_->disable_opt & (1 << kSafeOptimizations)) && r_dest == r_src) { res->flags.is_nop = true; } return res; @@ -296,20 +296,39 @@ RegLocation X86Mir2Lir::GenDivRem(RegLocation rl_dest, int reg_lo, bool X86Mir2Lir::GenInlinedMinMaxInt(CallInfo* info, bool is_min) { DCHECK_EQ(cu_->instruction_set, kX86); + + // Get the two arguments to the invoke and place them in GP registers. RegLocation rl_src1 = info->args[0]; RegLocation rl_src2 = info->args[1]; rl_src1 = LoadValue(rl_src1, kCoreReg); rl_src2 = LoadValue(rl_src2, kCoreReg); + RegLocation rl_dest = InlineTarget(info); RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true); - OpRegReg(kOpCmp, rl_src1.low_reg, rl_src2.low_reg); - DCHECK_EQ(cu_->instruction_set, kX86); - LIR* branch = NewLIR2(kX86Jcc8, 0, is_min ? kX86CondG : kX86CondL); - OpRegReg(kOpMov, rl_result.low_reg, rl_src1.low_reg); - LIR* branch2 = NewLIR1(kX86Jmp8, 0); - branch->target = NewLIR0(kPseudoTargetLabel); - OpRegReg(kOpMov, rl_result.low_reg, rl_src2.low_reg); - branch2->target = NewLIR0(kPseudoTargetLabel); + + /* + * If the result register is the same as the second element, then we need to be careful. + * The reason is that the first copy will inadvertently clobber the second element with + * the first one thus yielding the wrong result. Thus we do a swap in that case. + */ + if (rl_result.low_reg == rl_src2.low_reg) { + std::swap(rl_src1, rl_src2); + } + + // Pick the first integer as min/max. + OpRegCopy(rl_result.low_reg, rl_src1.low_reg); + + // If the integers are both in the same register, then there is nothing else to do + // because they are equal and we have already moved one into the result. + if (rl_src1.low_reg != rl_src2.low_reg) { + // It is possible we didn't pick correctly so do the actual comparison now. + OpRegReg(kOpCmp, rl_src1.low_reg, rl_src2.low_reg); + + // Conditionally move the other integer into the destination register. + ConditionCode condition_code = is_min ? kCondGt : kCondLt; + OpCondRegReg(kOpCmov, condition_code, rl_result.low_reg, rl_src2.low_reg); + } + StoreValue(rl_dest, rl_result); return true; } diff --git a/compiler/dex/quick/x86/utility_x86.cc b/compiler/dex/quick/x86/utility_x86.cc index 6ec7ebb91ae..f683affaf90 100644 --- a/compiler/dex/quick/x86/utility_x86.cc +++ b/compiler/dex/quick/x86/utility_x86.cc @@ -203,6 +203,12 @@ LIR* X86Mir2Lir::OpRegReg(OpKind op, int r_dest_src1, int r_src2) { return NewLIR2(opcode, r_dest_src1, r_src2); } +LIR* X86Mir2Lir::OpCondRegReg(OpKind op, ConditionCode cc, int r_dest, int r_src) { + // The only conditional reg to reg operation supported is Cmov + DCHECK_EQ(op, kOpCmov); + return NewLIR3(kX86Cmov32RRC, r_dest, r_src, X86ConditionEncoding(cc)); +} + LIR* X86Mir2Lir::OpRegMem(OpKind op, int r_dest, int rBase, int offset) { X86OpCode opcode = kX86Nop; diff --git a/compiler/dex/quick/x86/x86_lir.h b/compiler/dex/quick/x86/x86_lir.h index a2d5c3e402d..f38a16dc15d 100644 --- a/compiler/dex/quick/x86/x86_lir.h +++ b/compiler/dex/quick/x86/x86_lir.h @@ -279,6 +279,9 @@ enum X86OpCode { kX86Mov32RR, kX86Mov32RM, kX86Mov32RA, kX86Mov32RT, kX86Mov32RI, kX86Mov32MI, kX86Mov32AI, kX86Mov32TI, kX86Lea32RA, + // RRC - Register Register ConditionCode - cond_opcode reg1, reg2 + // - lir operands - 0: reg1, 1: reg2, 2: CC + kX86Cmov32RRC, // RC - Register CL - opcode reg, CL // - lir operands - 0: reg, 1: CL // MC - Memory CL - opcode [base + disp], CL @@ -398,6 +401,7 @@ enum X86EncodingKind { kShiftRegCl, kShiftMemCl, kShiftArrayCl, // Shift opcode with register CL. kRegRegReg, kRegRegMem, kRegRegArray, // RRR, RRM, RRA instruction kinds. kRegCond, kMemCond, kArrayCond, // R, M, A instruction kinds following by a condition. + kRegRegCond, // RR instruction kind followed by a condition. kJmp, kJcc, kCall, // Branch instruction kinds. kPcRel, // Operation with displacement that is PC relative kMacro, // An instruction composing multiple others diff --git a/disassembler/disassembler_x86.cc b/disassembler/disassembler_x86.cc index 1d53ca8123e..c51ea7b8a49 100644 --- a/disassembler/disassembler_x86.cc +++ b/disassembler/disassembler_x86.cc @@ -317,6 +317,12 @@ DISASSEMBLER_ENTRY(cmp, case 0x3A: // 3 byte extended opcode opcode << StringPrintf("unknown opcode '0F 3A %02X'", *instr); break; + case 0x40: case 0x41: case 0x42: case 0x43: case 0x44: case 0x45: case 0x46: case 0x47: + case 0x48: case 0x49: case 0x4A: case 0x4B: case 0x4C: case 0x4D: case 0x4E: case 0x4F: + opcode << "cmov" << condition_codes[*instr & 0xF]; + has_modrm = true; + load = true; + break; case 0x50: case 0x51: case 0x52: case 0x53: case 0x54: case 0x55: case 0x56: case 0x57: case 0x58: case 0x59: case 0x5C: case 0x5D: case 0x5E: case 0x5F: { switch (*instr) { From 988e6ea9ac66edf1e205851df9bb53de3f3763f3 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Wed, 8 Jan 2014 11:30:50 -0800 Subject: [PATCH 0336/2402] Fix -O0 builds. Use snprintf rather than sprintf to avoid Werror failures. Work around an annotalysis bug when compiling -O0. Change-Id: Ie7e0a70dbceea5fa85f98262b91bcdbd74fdef1c --- compiler/dex/quick/arm/target_arm.cc | 49 ++++++++++--------- .../quick/dex_file_to_method_inliner_map.h | 4 +- compiler/dex/quick/mips/target_mips.cc | 30 ++++++------ compiler/dex/quick/x86/target_x86.cc | 7 +-- 4 files changed, 48 insertions(+), 42 deletions(-) diff --git a/compiler/dex/quick/arm/target_arm.cc b/compiler/dex/quick/arm/target_arm.cc index d80ae3bc235..759104150d4 100644 --- a/compiler/dex/quick/arm/target_arm.cc +++ b/compiler/dex/quick/arm/target_arm.cc @@ -255,7 +255,7 @@ static const char* shift_names[4] = { "ror"}; /* Decode and print a ARM register name */ -static char* DecodeRegList(int opcode, int vector, char* buf) { +static char* DecodeRegList(int opcode, int vector, char* buf, size_t buf_size) { int i; bool printed = false; buf[0] = 0; @@ -268,20 +268,20 @@ static char* DecodeRegList(int opcode, int vector, char* buf) { reg_id = r15pc; } if (printed) { - sprintf(buf + strlen(buf), ", r%d", reg_id); + snprintf(buf + strlen(buf), buf_size - strlen(buf), ", r%d", reg_id); } else { printed = true; - sprintf(buf, "r%d", reg_id); + snprintf(buf, buf_size, "r%d", reg_id); } } } return buf; } -static char* DecodeFPCSRegList(int count, int base, char* buf) { - sprintf(buf, "s%d", base); +static char* DecodeFPCSRegList(int count, int base, char* buf, size_t buf_size) { + snprintf(buf, buf_size, "s%d", base); for (int i = 1; i < count; i++) { - sprintf(buf + strlen(buf), ", s%d", base + i); + snprintf(buf + strlen(buf), buf_size - strlen(buf), ", s%d", base + i); } return buf; } @@ -333,7 +333,7 @@ std::string ArmMir2Lir::BuildInsnString(const char* fmt, LIR* lir, unsigned char switch (*fmt++) { case 'H': if (operand != 0) { - sprintf(tbuf, ", %s %d", shift_names[operand & 0x3], operand >> 2); + snprintf(tbuf, arraysize(tbuf), ", %s %d", shift_names[operand & 0x3], operand >> 2); } else { strcpy(tbuf, ""); } @@ -373,41 +373,41 @@ std::string ArmMir2Lir::BuildInsnString(const char* fmt, LIR* lir, unsigned char break; case 'n': operand = ~ExpandImmediate(operand); - sprintf(tbuf, "%d [%#x]", operand, operand); + snprintf(tbuf, arraysize(tbuf), "%d [%#x]", operand, operand); break; case 'm': operand = ExpandImmediate(operand); - sprintf(tbuf, "%d [%#x]", operand, operand); + snprintf(tbuf, arraysize(tbuf), "%d [%#x]", operand, operand); break; case 's': - sprintf(tbuf, "s%d", operand & ARM_FP_REG_MASK); + snprintf(tbuf, arraysize(tbuf), "s%d", operand & ARM_FP_REG_MASK); break; case 'S': - sprintf(tbuf, "d%d", (operand & ARM_FP_REG_MASK) >> 1); + snprintf(tbuf, arraysize(tbuf), "d%d", (operand & ARM_FP_REG_MASK) >> 1); break; case 'h': - sprintf(tbuf, "%04x", operand); + snprintf(tbuf, arraysize(tbuf), "%04x", operand); break; case 'M': case 'd': - sprintf(tbuf, "%d", operand); + snprintf(tbuf, arraysize(tbuf), "%d", operand); break; case 'C': DCHECK_LT(operand, static_cast( sizeof(core_reg_names)/sizeof(core_reg_names[0]))); - sprintf(tbuf, "%s", core_reg_names[operand]); + snprintf(tbuf, arraysize(tbuf), "%s", core_reg_names[operand]); break; case 'E': - sprintf(tbuf, "%d", operand*4); + snprintf(tbuf, arraysize(tbuf), "%d", operand*4); break; case 'F': - sprintf(tbuf, "%d", operand*2); + snprintf(tbuf, arraysize(tbuf), "%d", operand*2); break; case 'c': strcpy(tbuf, cc_names[operand]); break; case 't': - sprintf(tbuf, "0x%08x (L%p)", + snprintf(tbuf, arraysize(tbuf), "0x%08x (L%p)", reinterpret_cast(base_addr) + lir->offset + 4 + (operand << 1), lir->target); @@ -419,7 +419,7 @@ std::string ArmMir2Lir::BuildInsnString(const char* fmt, LIR* lir, unsigned char (((reinterpret_cast(base_addr) + lir->offset + 4) & ~3) + (offset_1 << 21 >> 9) + (offset_2 << 1)) & 0xfffffffc; - sprintf(tbuf, "%p", reinterpret_cast(target)); + snprintf(tbuf, arraysize(tbuf), "%p", reinterpret_cast(target)); break; } @@ -428,13 +428,13 @@ std::string ArmMir2Lir::BuildInsnString(const char* fmt, LIR* lir, unsigned char strcpy(tbuf, "see above"); break; case 'R': - DecodeRegList(lir->opcode, operand, tbuf); + DecodeRegList(lir->opcode, operand, tbuf, arraysize(tbuf)); break; case 'P': - DecodeFPCSRegList(operand, 16, tbuf); + DecodeFPCSRegList(operand, 16, tbuf, arraysize(tbuf)); break; case 'Q': - DecodeFPCSRegList(operand, 0, tbuf); + DecodeFPCSRegList(operand, 0, tbuf, arraysize(tbuf)); break; default: strcpy(tbuf, "DecodeError1"); @@ -461,7 +461,7 @@ void ArmMir2Lir::DumpResourceMask(LIR* arm_lir, uint64_t mask, const char* prefi for (i = 0; i < kArmRegEnd; i++) { if (mask & (1ULL << i)) { - sprintf(num, "%d ", i); + snprintf(num, arraysize(num), "%d ", i); strcat(buf, num); } } @@ -475,8 +475,9 @@ void ArmMir2Lir::DumpResourceMask(LIR* arm_lir, uint64_t mask, const char* prefi /* Memory bits */ if (arm_lir && (mask & ENCODE_DALVIK_REG)) { - sprintf(buf + strlen(buf), "dr%d%s", DECODE_ALIAS_INFO_REG(arm_lir->flags.alias_info), - DECODE_ALIAS_INFO_WIDE(arm_lir->flags.alias_info) ? "(+1)" : ""); + snprintf(buf + strlen(buf), arraysize(buf) - strlen(buf), "dr%d%s", + DECODE_ALIAS_INFO_REG(arm_lir->flags.alias_info), + DECODE_ALIAS_INFO_WIDE(arm_lir->flags.alias_info) ? "(+1)" : ""); } if (mask & ENCODE_LITERAL) { strcat(buf, "lit "); diff --git a/compiler/dex/quick/dex_file_to_method_inliner_map.h b/compiler/dex/quick/dex_file_to_method_inliner_map.h index 6d5b8893c52..215dc12b26c 100644 --- a/compiler/dex/quick/dex_file_to_method_inliner_map.h +++ b/compiler/dex/quick/dex_file_to_method_inliner_map.h @@ -40,7 +40,9 @@ class DexFileToMethodInlinerMap { DexFileToMethodInlinerMap(); ~DexFileToMethodInlinerMap(); - DexFileMethodInliner* GetMethodInliner(const DexFile* dex_file) LOCKS_EXCLUDED(lock_); + DexFileMethodInliner* GetMethodInliner(const DexFile* dex_file) NO_THREAD_SAFETY_ANALYSIS; + // TODO: There is an irregular non-scoped use of locks that defeats annotalysis with -O0. + // Fix the NO_THREAD_SAFETY_ANALYSIS when this works and add the appropriate LOCKS_EXCLUDED. private: ReaderWriterMutex lock_; diff --git a/compiler/dex/quick/mips/target_mips.cc b/compiler/dex/quick/mips/target_mips.cc index 869706fbe06..1aee06c89ae 100644 --- a/compiler/dex/quick/mips/target_mips.cc +++ b/compiler/dex/quick/mips/target_mips.cc @@ -180,34 +180,35 @@ std::string MipsMir2Lir::BuildInsnString(const char *fmt, LIR *lir, unsigned cha } break; case 's': - sprintf(tbuf, "$f%d", operand & MIPS_FP_REG_MASK); + snprintf(tbuf, arraysize(tbuf), "$f%d", operand & MIPS_FP_REG_MASK); break; case 'S': DCHECK_EQ(((operand & MIPS_FP_REG_MASK) & 1), 0); - sprintf(tbuf, "$f%d", operand & MIPS_FP_REG_MASK); + snprintf(tbuf, arraysize(tbuf), "$f%d", operand & MIPS_FP_REG_MASK); break; case 'h': - sprintf(tbuf, "%04x", operand); + snprintf(tbuf, arraysize(tbuf), "%04x", operand); break; case 'M': case 'd': - sprintf(tbuf, "%d", operand); + snprintf(tbuf, arraysize(tbuf), "%d", operand); break; case 'D': - sprintf(tbuf, "%d", operand+1); + snprintf(tbuf, arraysize(tbuf), "%d", operand+1); break; case 'E': - sprintf(tbuf, "%d", operand*4); + snprintf(tbuf, arraysize(tbuf), "%d", operand*4); break; case 'F': - sprintf(tbuf, "%d", operand*2); + snprintf(tbuf, arraysize(tbuf), "%d", operand*2); break; case 't': - sprintf(tbuf, "0x%08x (L%p)", reinterpret_cast(base_addr) + lir->offset + 4 + - (operand << 2), lir->target); + snprintf(tbuf, arraysize(tbuf), "0x%08x (L%p)", + reinterpret_cast(base_addr) + lir->offset + 4 + (operand << 2), + lir->target); break; case 'T': - sprintf(tbuf, "0x%08x", operand << 2); + snprintf(tbuf, arraysize(tbuf), "0x%08x", operand << 2); break; case 'u': { int offset_1 = lir->operands[0]; @@ -215,7 +216,7 @@ std::string MipsMir2Lir::BuildInsnString(const char *fmt, LIR *lir, unsigned cha uintptr_t target = (((reinterpret_cast(base_addr) + lir->offset + 4) & ~3) + (offset_1 << 21 >> 9) + (offset_2 << 1)) & 0xfffffffc; - sprintf(tbuf, "%p", reinterpret_cast(target)); + snprintf(tbuf, arraysize(tbuf), "%p", reinterpret_cast(target)); break; } @@ -257,7 +258,7 @@ void MipsMir2Lir::DumpResourceMask(LIR *mips_lir, uint64_t mask, const char *pre for (i = 0; i < kMipsRegEnd; i++) { if (mask & (1ULL << i)) { - sprintf(num, "%d ", i); + snprintf(num, arraysize(num), "%d ", i); strcat(buf, num); } } @@ -270,8 +271,9 @@ void MipsMir2Lir::DumpResourceMask(LIR *mips_lir, uint64_t mask, const char *pre } /* Memory bits */ if (mips_lir && (mask & ENCODE_DALVIK_REG)) { - sprintf(buf + strlen(buf), "dr%d%s", DECODE_ALIAS_INFO_REG(mips_lir->flags.alias_info), - DECODE_ALIAS_INFO_WIDE(mips_lir->flags.alias_info) ? "(+1)" : ""); + snprintf(buf + strlen(buf), arraysize(buf) - strlen(buf), "dr%d%s", + DECODE_ALIAS_INFO_REG(mips_lir->flags.alias_info), + DECODE_ALIAS_INFO_WIDE(mips_lir->flags.alias_info) ? "(+1)" : ""); } if (mask & ENCODE_LITERAL) { strcat(buf, "lit "); diff --git a/compiler/dex/quick/x86/target_x86.cc b/compiler/dex/quick/x86/target_x86.cc index 0b8c07e3cc6..b281063a4a4 100644 --- a/compiler/dex/quick/x86/target_x86.cc +++ b/compiler/dex/quick/x86/target_x86.cc @@ -270,7 +270,7 @@ void X86Mir2Lir::DumpResourceMask(LIR *x86LIR, uint64_t mask, const char *prefix for (i = 0; i < kX86RegEnd; i++) { if (mask & (1ULL << i)) { - sprintf(num, "%d ", i); + snprintf(num, arraysize(num), "%d ", i); strcat(buf, num); } } @@ -280,8 +280,9 @@ void X86Mir2Lir::DumpResourceMask(LIR *x86LIR, uint64_t mask, const char *prefix } /* Memory bits */ if (x86LIR && (mask & ENCODE_DALVIK_REG)) { - sprintf(buf + strlen(buf), "dr%d%s", DECODE_ALIAS_INFO_REG(x86LIR->flags.alias_info), - (DECODE_ALIAS_INFO_WIDE(x86LIR->flags.alias_info)) ? "(+1)" : ""); + snprintf(buf + strlen(buf), arraysize(buf) - strlen(buf), "dr%d%s", + DECODE_ALIAS_INFO_REG(x86LIR->flags.alias_info), + (DECODE_ALIAS_INFO_WIDE(x86LIR->flags.alias_info)) ? "(+1)" : ""); } if (mask & ENCODE_LITERAL) { strcat(buf, "lit "); From ef6a776af2b4b8607d5f91add0ed0e8497100e31 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Thu, 19 Dec 2013 17:58:05 -0800 Subject: [PATCH 0337/2402] Inline codegen for long-to-double on ARM. Change-Id: I4fc443c1b942a2231d680fc2c7a1530c86104584 --- compiler/dex/quick/arm/arm_lir.h | 9 ++++++--- compiler/dex/quick/arm/assemble_arm.cc | 12 ++++++++++++ compiler/dex/quick/arm/fp_arm.cc | 19 +++++++++++++++++-- disassembler/disassembler_arm.cc | 7 ++++++- test/107-int-math2/src/Main.java | 18 ++++++++++++++++++ 5 files changed, 59 insertions(+), 6 deletions(-) diff --git a/compiler/dex/quick/arm/arm_lir.h b/compiler/dex/quick/arm/arm_lir.h index 395c78828eb..b06ebcf0129 100644 --- a/compiler/dex/quick/arm/arm_lir.h +++ b/compiler/dex/quick/arm/arm_lir.h @@ -241,7 +241,7 @@ enum ArmOpcode { kArmFirst = 0, kArm16BitData = kArmFirst, // DATA [0] rd[15..0]. kThumbAdcRR, // adc [0100000101] rm[5..3] rd[2..0]. - kThumbAddRRI3, // add(1) [0001110] imm_3[8..6] rn[5..3] rd[2..0]*/ + kThumbAddRRI3, // add(1) [0001110] imm_3[8..6] rn[5..3] rd[2..0]. kThumbAddRI8, // add(2) [00110] rd[10..8] imm_8[7..0]. kThumbAddRRR, // add(3) [0001100] rm[8..6] rn[5..3] rd[2..0]. kThumbAddRRLH, // add(4) [01000100] H12[01] rm[5..3] rd[2..0]. @@ -326,20 +326,23 @@ enum ArmOpcode { kThumb2Vaddd, // vadd vd, vn, vm [111011100011] rn[19..16] rd[15-12] [10110000] rm[3..0]. kThumb2Vdivs, // vdiv vd, vn, vm [111011101000] rn[19..16] rd[15-12] [10100000] rm[3..0]. kThumb2Vdivd, // vdiv vd, vn, vm [111011101000] rn[19..16] rd[15-12] [10110000] rm[3..0]. + kThumb2VmlaF64, // vmla.F64 vd, vn, vm [111011100000] vn[19..16] vd[15..12] [10110000] vm[3..0]. kThumb2VcvtIF, // vcvt.F32 vd, vm [1110111010111000] vd[15..12] [10101100] vm[3..0]. kThumb2VcvtID, // vcvt.F64 vd, vm [1110111010111000] vd[15..12] [10111100] vm[3..0]. kThumb2VcvtFI, // vcvt.S32.F32 vd, vm [1110111010111101] vd[15..12] [10101100] vm[3..0]. kThumb2VcvtDI, // vcvt.S32.F32 vd, vm [1110111010111101] vd[15..12] [10111100] vm[3..0]. kThumb2VcvtFd, // vcvt.F64.F32 vd, vm [1110111010110111] vd[15..12] [10101100] vm[3..0]. kThumb2VcvtDF, // vcvt.F32.F64 vd, vm [1110111010110111] vd[15..12] [10111100] vm[3..0]. + kThumb2VcvtF64S32, // vcvt.F64.S32 vd, vm [1110111010111000] vd[15..12] [10111100] vm[3..0]. + kThumb2VcvtF64U32, // vcvt.F64.U32 vd, vm [1110111010111000] vd[15..12] [10110100] vm[3..0]. kThumb2Vsqrts, // vsqrt.f32 vd, vm [1110111010110001] vd[15..12] [10101100] vm[3..0]. kThumb2Vsqrtd, // vsqrt.f64 vd, vm [1110111010110001] vd[15..12] [10111100] vm[3..0]. kThumb2MovI8M, // mov(T2) rd, # [11110] i [00001001111] imm3 rd[11..8] imm8. kThumb2MovImm16, // mov(T3) rd, # [11110] i [0010100] imm4 [0] imm3 rd[11..8] imm8. kThumb2StrRRI12, // str(Imm,T3) rd,[rn,#imm12] [111110001100] rn[19..16] rt[15..12] imm12[11..0]. kThumb2LdrRRI12, // str(Imm,T3) rd,[rn,#imm12] [111110001100] rn[19..16] rt[15..12] imm12[11..0]. - kThumb2StrRRI8Predec, // str(Imm,T4) rd,[rn,#-imm8] [111110000100] rn[19..16] rt[15..12] [1100] imm[7..0]*/ - kThumb2LdrRRI8Predec, // ldr(Imm,T4) rd,[rn,#-imm8] [111110000101] rn[19..16] rt[15..12] [1100] imm[7..0]*/ + kThumb2StrRRI8Predec, // str(Imm,T4) rd,[rn,#-imm8] [111110000100] rn[19..16] rt[15..12] [1100] imm[7..0]. + kThumb2LdrRRI8Predec, // ldr(Imm,T4) rd,[rn,#-imm8] [111110000101] rn[19..16] rt[15..12] [1100] imm[7..0]. kThumb2Cbnz, // cbnz rd,