diff options
590 files changed, 10871 insertions, 4109 deletions
diff --git a/benchmark/micro-native/micro_native.cc b/benchmark/micro-native/micro_native.cc index d366d9d465..dffbf3b11d 100644 --- a/benchmark/micro-native/micro_native.cc +++ b/benchmark/micro-native/micro_native.cc @@ -14,8 +14,8 @@ * limitations under the License. */ -#include <stdio.h> #include <jni.h> +#include <stdio.h> #ifndef NATIVE_METHOD #define NATIVE_METHOD(className, functionName, signature) \ diff --git a/benchmark/scoped-primitive-array/scoped_primitive_array.cc b/benchmark/scoped-primitive-array/scoped_primitive_array.cc index 1664157297..005cae4ac9 100644 --- a/benchmark/scoped-primitive-array/scoped_primitive_array.cc +++ b/benchmark/scoped-primitive-array/scoped_primitive_array.cc @@ -15,7 +15,7 @@ */ #include "jni.h" -#include "ScopedPrimitiveArray.h" +#include "nativehelper/ScopedPrimitiveArray.h" extern "C" JNIEXPORT jlong JNICALL Java_ScopedPrimitiveArrayBenchmark_measureByteArray( JNIEnv* env, jclass, int reps, jbyteArray arr) { diff --git a/build/Android.bp b/build/Android.bp index c5ff486709..d617116327 100644 --- a/build/Android.bp +++ b/build/Android.bp @@ -99,6 +99,15 @@ art_global_defaults { // Bug: 15446488. We don't omit the frame pointer to work around // clang/libunwind bugs that cause SEGVs in run-test-004-ThreadStress. "-fno-omit-frame-pointer", + // The build assumes that all our x86/x86_64 hosts (such as buildbots and developer + // desktops) support at least sse4.2/popcount. This firstly implies that the ART + // runtime binary itself may exploit these features. Secondly, this implies that + // the ART runtime passes these feature flags to dex2oat and JIT by calling the + // method InstructionSetFeatures::FromCppDefines(). Since invoking dex2oat directly + // does not pick up these flags, cross-compiling from a x86/x86_64 host to a + // x86/x86_64 target should not be affected. + "-msse4.2", + "-mpopcnt", ], host_ldlibs: [ "-ldl", diff --git a/build/art.go b/build/art.go index 6c9aa89f8f..19b39cdbce 100644 --- a/build/art.go +++ b/build/art.go @@ -153,6 +153,11 @@ func hostFlags(ctx android.BaseContext) []string { cflags = append(cflags, "-DART_BASE_ADDRESS_MIN_DELTA="+minDelta) cflags = append(cflags, "-DART_BASE_ADDRESS_MAX_DELTA="+maxDelta) + if len(ctx.AConfig().SanitizeHost()) > 0 && !envFalse(ctx, "ART_ENABLE_ADDRESS_SANITIZER") { + // We enable full sanitization on the host by default. + cflags = append(cflags, "-DART_ENABLE_ADDRESS_SANITIZER=1") + } + return cflags } diff --git a/cmdline/cmdline_parser.h b/cmdline/cmdline_parser.h index 32480dd915..804727bdcd 100644 --- a/cmdline/cmdline_parser.h +++ b/cmdline/cmdline_parser.h @@ -19,20 +19,20 @@ #define CMDLINE_NDEBUG 1 // Do not output any debugging information for parsing. -#include "cmdline/detail/cmdline_parser_detail.h" -#include "cmdline/detail/cmdline_parse_argument_detail.h" #include "cmdline/detail/cmdline_debug_detail.h" +#include "cmdline/detail/cmdline_parse_argument_detail.h" +#include "cmdline/detail/cmdline_parser_detail.h" +#include "cmdline_parse_result.h" +#include "cmdline_result.h" #include "cmdline_type_parser.h" -#include "token_range.h" #include "cmdline_types.h" -#include "cmdline_result.h" -#include "cmdline_parse_result.h" +#include "token_range.h" #include "runtime/base/variant_map.h" -#include <vector> #include <memory> +#include <vector> namespace art { // Build a parser for command line arguments with a small domain specific language. diff --git a/cmdline/cmdline_parser_test.cc b/cmdline/cmdline_parser_test.cc index d957869a76..3c4f376753 100644 --- a/cmdline/cmdline_parser_test.cc +++ b/cmdline/cmdline_parser_test.cc @@ -15,14 +15,16 @@ */ #include "cmdline_parser.h" -#include "runtime/runtime_options.h" -#include "runtime/parsed_options.h" -#include "utils.h" #include <numeric> + #include "gtest/gtest.h" + #include "runtime/experimental_flags.h" +#include "runtime/parsed_options.h" #include "runtime/runtime.h" +#include "runtime/runtime_options.h" +#include "utils.h" #define EXPECT_NULL(expected) EXPECT_EQ(reinterpret_cast<const void*>(expected), \ reinterpret_cast<void*>(nullptr)); diff --git a/cmdline/cmdline_types.h b/cmdline/cmdline_types.h index 4de8a48d45..20c4a7ed62 100644 --- a/cmdline/cmdline_types.h +++ b/cmdline/cmdline_types.h @@ -20,9 +20,9 @@ #include <list> -#include "memory_representation.h" -#include "detail/cmdline_debug_detail.h" #include "cmdline_type_parser.h" +#include "detail/cmdline_debug_detail.h" +#include "memory_representation.h" #include "android-base/strings.h" diff --git a/cmdline/detail/cmdline_parse_argument_detail.h b/cmdline/detail/cmdline_parse_argument_detail.h index da03c2198f..ceb6fa718a 100644 --- a/cmdline/detail/cmdline_parse_argument_detail.h +++ b/cmdline/detail/cmdline_parse_argument_detail.h @@ -17,13 +17,13 @@ #ifndef ART_CMDLINE_DETAIL_CMDLINE_PARSE_ARGUMENT_DETAIL_H_ #define ART_CMDLINE_DETAIL_CMDLINE_PARSE_ARGUMENT_DETAIL_H_ -#include <type_traits> #include <assert.h> -#include <functional> -#include <vector> #include <algorithm> -#include <numeric> +#include <functional> #include <memory> +#include <numeric> +#include <type_traits> +#include <vector> #include "android-base/strings.h" diff --git a/cmdline/detail/cmdline_parser_detail.h b/cmdline/detail/cmdline_parser_detail.h index 24dbca2642..118628fdec 100644 --- a/cmdline/detail/cmdline_parser_detail.h +++ b/cmdline/detail/cmdline_parser_detail.h @@ -17,8 +17,8 @@ #ifndef ART_CMDLINE_DETAIL_CMDLINE_PARSER_DETAIL_H_ #define ART_CMDLINE_DETAIL_CMDLINE_PARSER_DETAIL_H_ -#include <string> #include <sstream> +#include <string> #include <vector> namespace art { diff --git a/cmdline/memory_representation.h b/cmdline/memory_representation.h index 2619c317e3..8db68bc9a4 100644 --- a/cmdline/memory_representation.h +++ b/cmdline/memory_representation.h @@ -17,9 +17,9 @@ #ifndef ART_CMDLINE_MEMORY_REPRESENTATION_H_ #define ART_CMDLINE_MEMORY_REPRESENTATION_H_ -#include <string> #include <assert.h> #include <ostream> +#include <string> #include "base/bit_utils.h" diff --git a/cmdline/token_range.h b/cmdline/token_range.h index c22d6c8959..642bb1d34c 100644 --- a/cmdline/token_range.h +++ b/cmdline/token_range.h @@ -18,10 +18,10 @@ #define ART_CMDLINE_TOKEN_RANGE_H_ #include <assert.h> -#include <vector> -#include <string> #include <algorithm> #include <memory> +#include <string> +#include <vector> #include "android-base/strings.h" diff --git a/compiler/cfi_test.h b/compiler/cfi_test.h index c754e5588c..5347e7fef3 100644 --- a/compiler/cfi_test.h +++ b/compiler/cfi_test.h @@ -17,9 +17,9 @@ #ifndef ART_COMPILER_CFI_TEST_H_ #define ART_COMPILER_CFI_TEST_H_ -#include <vector> #include <memory> #include <sstream> +#include <vector> #include "arch/instruction_set.h" #include "base/enums.h" diff --git a/compiler/common_compiler_test.cc b/compiler/common_compiler_test.cc index 3683695a1b..a9a718f43c 100644 --- a/compiler/common_compiler_test.cc +++ b/compiler/common_compiler_test.cc @@ -28,8 +28,8 @@ #include "driver/compiler_driver.h" #include "driver/compiler_options.h" #include "interpreter/interpreter.h" -#include "mirror/class_loader.h" #include "mirror/class-inl.h" +#include "mirror/class_loader.h" #include "mirror/dex_cache.h" #include "mirror/object-inl.h" #include "oat_quick_method_header.h" @@ -207,8 +207,10 @@ void CommonCompilerTest::SetUpRuntimeOptions(RuntimeOptions* options) { compiler_options_.reset(new CompilerOptions); verification_results_.reset(new VerificationResults(compiler_options_.get())); - callbacks_.reset(new QuickCompilerCallbacks(verification_results_.get(), - CompilerCallbacks::CallbackMode::kCompileApp)); + QuickCompilerCallbacks* callbacks = + new QuickCompilerCallbacks(CompilerCallbacks::CallbackMode::kCompileApp); + callbacks->SetVerificationResults(verification_results_.get()); + callbacks_.reset(callbacks); } Compiler::Kind CommonCompilerTest::GetCompilerKind() const { @@ -265,8 +267,8 @@ void CommonCompilerTest::CompileDirectMethod(Handle<mirror::ClassLoader> class_l mirror::Class* klass = class_linker_->FindClass(self, class_descriptor.c_str(), class_loader); CHECK(klass != nullptr) << "Class not found " << class_name; auto pointer_size = class_linker_->GetImagePointerSize(); - ArtMethod* method = klass->FindDirectMethod(method_name, signature, pointer_size); - CHECK(method != nullptr) << "Direct method not found: " + ArtMethod* method = klass->FindClassMethod(method_name, signature, pointer_size); + CHECK(method != nullptr && method->IsDirect()) << "Direct method not found: " << class_name << "." << method_name << signature; CompileMethod(method); } @@ -279,8 +281,8 @@ void CommonCompilerTest::CompileVirtualMethod(Handle<mirror::ClassLoader> class_ mirror::Class* klass = class_linker_->FindClass(self, class_descriptor.c_str(), class_loader); CHECK(klass != nullptr) << "Class not found " << class_name; auto pointer_size = class_linker_->GetImagePointerSize(); - ArtMethod* method = klass->FindVirtualMethod(method_name, signature, pointer_size); - CHECK(method != nullptr) << "Virtual method not found: " + ArtMethod* method = klass->FindClassMethod(method_name, signature, pointer_size); + CHECK(method != nullptr && !method->IsDirect()) << "Virtual method not found: " << class_name << "." << method_name << signature; CompileMethod(method); } diff --git a/compiler/compiled_method.h b/compiler/compiled_method.h index 761e9e19a8..97127f58ed 100644 --- a/compiler/compiled_method.h +++ b/compiler/compiled_method.h @@ -17,8 +17,8 @@ #ifndef ART_COMPILER_COMPILED_METHOD_H_ #define ART_COMPILER_COMPILED_METHOD_H_ -#include <memory> #include <iosfwd> +#include <memory> #include <string> #include <vector> diff --git a/compiler/compiler.h b/compiler/compiler.h index cd4c59101e..6c542c841a 100644 --- a/compiler/compiler.h +++ b/compiler/compiler.h @@ -17,14 +17,15 @@ #ifndef ART_COMPILER_COMPILER_H_ #define ART_COMPILER_COMPILER_H_ -#include "dex_file.h" #include "base/mutex.h" +#include "dex_file.h" #include "os.h" namespace art { namespace jit { class JitCodeCache; + class JitLogger; } // namespace jit namespace mirror { class ClassLoader; @@ -76,7 +77,8 @@ class Compiler { virtual bool JitCompile(Thread* self ATTRIBUTE_UNUSED, jit::JitCodeCache* code_cache ATTRIBUTE_UNUSED, ArtMethod* method ATTRIBUTE_UNUSED, - bool osr ATTRIBUTE_UNUSED) + bool osr ATTRIBUTE_UNUSED, + jit::JitLogger* jit_logger ATTRIBUTE_UNUSED) REQUIRES_SHARED(Locks::mutator_lock_) { return false; } diff --git a/compiler/debug/dwarf/dwarf_test.h b/compiler/debug/dwarf/dwarf_test.h index e2f0a65ab7..e1f538d9a7 100644 --- a/compiler/debug/dwarf/dwarf_test.h +++ b/compiler/debug/dwarf/dwarf_test.h @@ -17,13 +17,14 @@ #ifndef ART_COMPILER_DEBUG_DWARF_DWARF_TEST_H_ #define ART_COMPILER_DEBUG_DWARF_DWARF_TEST_H_ -#include <cstring> #include <dirent.h> +#include <stdio.h> +#include <sys/types.h> + +#include <cstring> #include <memory> #include <set> -#include <stdio.h> #include <string> -#include <sys/types.h> #include "base/unix_file/fd_file.h" #include "common_runtime_test.h" diff --git a/compiler/dex/dex_to_dex_compiler.cc b/compiler/dex/dex_to_dex_compiler.cc index fba1136d19..9d57b965ab 100644 --- a/compiler/dex/dex_to_dex_compiler.cc +++ b/compiler/dex/dex_to_dex_compiler.cc @@ -291,13 +291,14 @@ void DexCompiler::CompileInvokeVirtual(Instruction* inst, uint32_t dex_pc, ScopedObjectAccess soa(Thread::Current()); ClassLinker* class_linker = unit_.GetClassLinker(); - ArtMethod* resolved_method = class_linker->ResolveMethod<ClassLinker::kForceICCECheck>( - GetDexFile(), - method_idx, - unit_.GetDexCache(), - unit_.GetClassLoader(), - /* referrer */ nullptr, - kVirtual); + ArtMethod* resolved_method = + class_linker->ResolveMethod<ClassLinker::ResolveMode::kCheckICCEAndIAE>( + GetDexFile(), + method_idx, + unit_.GetDexCache(), + unit_.GetClassLoader(), + /* referrer */ nullptr, + kVirtual); if (UNLIKELY(resolved_method == nullptr)) { // Clean up any exception left by type resolution. diff --git a/compiler/dex/dex_to_dex_decompiler_test.cc b/compiler/dex/dex_to_dex_decompiler_test.cc index 88426a3b5f..7b56f3ec1a 100644 --- a/compiler/dex/dex_to_dex_decompiler_test.cc +++ b/compiler/dex/dex_to_dex_decompiler_test.cc @@ -19,16 +19,16 @@ #include "class_linker.h" #include "compiler/common_compiler_test.h" #include "compiler/compiled_method.h" -#include "compiler/driver/compiler_options.h" #include "compiler/driver/compiler_driver.h" +#include "compiler/driver/compiler_options.h" #include "compiler_callbacks.h" #include "dex_file.h" #include "handle_scope-inl.h" -#include "verifier/method_verifier-inl.h" #include "mirror/class_loader.h" #include "runtime.h" -#include "thread.h" #include "scoped_thread_state_change-inl.h" +#include "thread.h" +#include "verifier/method_verifier-inl.h" namespace art { diff --git a/compiler/dex/inline_method_analyser.cc b/compiler/dex/inline_method_analyser.cc index 257229101c..54ddc2188b 100644 --- a/compiler/dex/inline_method_analyser.cc +++ b/compiler/dex/inline_method_analyser.cc @@ -21,8 +21,8 @@ #include "base/enums.h" #include "class_linker-inl.h" #include "dex_file-inl.h" -#include "dex_instruction.h" #include "dex_instruction-inl.h" +#include "dex_instruction.h" #include "dex_instruction_utils.h" #include "mirror/class-inl.h" #include "mirror/dex_cache-inl.h" @@ -145,9 +145,8 @@ ArtMethod* GetTargetConstructor(ArtMethod* method, const Instruction* invoke_dir DCHECK_EQ(invoke_direct->VRegC_35c(), method->GetCodeItem()->registers_size_ - method->GetCodeItem()->ins_size_); uint32_t method_index = invoke_direct->VRegB_35c(); - PointerSize pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize(); - ArtMethod* target_method = - method->GetDexCache()->GetResolvedMethod(method_index, pointer_size); + ArtMethod* target_method = Runtime::Current()->GetClassLinker()->LookupResolvedMethod( + method_index, method->GetDexCache(), method->GetClassLoader()); if (kIsDebugBuild && target_method != nullptr) { CHECK(!target_method->IsStatic()); CHECK(target_method->IsConstructor()); diff --git a/compiler/dex/quick_compiler_callbacks.cc b/compiler/dex/quick_compiler_callbacks.cc index b1006b2f0b..872f7ea15d 100644 --- a/compiler/dex/quick_compiler_callbacks.cc +++ b/compiler/dex/quick_compiler_callbacks.cc @@ -16,8 +16,8 @@ #include "quick_compiler_callbacks.h" -#include "verifier/method_verifier-inl.h" #include "verification_results.h" +#include "verifier/method_verifier-inl.h" namespace art { diff --git a/compiler/dex/quick_compiler_callbacks.h b/compiler/dex/quick_compiler_callbacks.h index 2100522f10..a3a6c0972c 100644 --- a/compiler/dex/quick_compiler_callbacks.h +++ b/compiler/dex/quick_compiler_callbacks.h @@ -26,11 +26,8 @@ class VerificationResults; class QuickCompilerCallbacks FINAL : public CompilerCallbacks { public: - QuickCompilerCallbacks(VerificationResults* verification_results, - CompilerCallbacks::CallbackMode mode) - : CompilerCallbacks(mode), - verification_results_(verification_results), - verifier_deps_(nullptr) {} + explicit QuickCompilerCallbacks(CompilerCallbacks::CallbackMode mode) + : CompilerCallbacks(mode) {} ~QuickCompilerCallbacks() { } @@ -52,8 +49,12 @@ class QuickCompilerCallbacks FINAL : public CompilerCallbacks { verifier_deps_.reset(deps); } + void SetVerificationResults(VerificationResults* verification_results) { + verification_results_ = verification_results; + } + private: - VerificationResults* const verification_results_; + VerificationResults* verification_results_ = nullptr; std::unique_ptr<verifier::VerifierDeps> verifier_deps_; }; diff --git a/compiler/dex/verification_results.cc b/compiler/dex/verification_results.cc index beb3439e62..e657e3bc86 100644 --- a/compiler/dex/verification_results.cc +++ b/compiler/dex/verification_results.cc @@ -22,8 +22,8 @@ #include "driver/compiler_driver.h" #include "driver/compiler_options.h" #include "runtime.h" -#include "thread.h" #include "thread-current-inl.h" +#include "thread.h" #include "utils/atomic_dex_ref_map-inl.h" #include "verified_method.h" #include "verifier/method_verifier-inl.h" diff --git a/compiler/driver/compiled_method_storage_test.cc b/compiler/driver/compiled_method_storage_test.cc index bbd28b2576..2ec2af587e 100644 --- a/compiler/driver/compiled_method_storage_test.cc +++ b/compiler/driver/compiled_method_storage_test.cc @@ -14,9 +14,10 @@ * limitations under the License. */ +#include "compiled_method_storage.h" + #include <gtest/gtest.h> -#include "compiled_method_storage.h" #include "compiled_method.h" #include "compiler_driver.h" #include "compiler_options.h" diff --git a/compiler/driver/compiler_driver-inl.h b/compiler/driver/compiler_driver-inl.h index db95bd6e03..b04392918d 100644 --- a/compiler/driver/compiler_driver-inl.h +++ b/compiler/driver/compiler_driver-inl.h @@ -111,7 +111,7 @@ inline ArtMethod* CompilerDriver::ResolveMethod( InvokeType invoke_type) { DCHECK_EQ(class_loader.Get(), mUnit->GetClassLoader().Get()); ArtMethod* resolved_method = - mUnit->GetClassLinker()->ResolveMethod<ClassLinker::kForceICCECheck>( + mUnit->GetClassLinker()->ResolveMethod<ClassLinker::ResolveMode::kCheckICCEAndIAE>( *dex_cache->GetDexFile(), method_idx, dex_cache, class_loader, nullptr, invoke_type); if (UNLIKELY(resolved_method == nullptr)) { DCHECK(soa.Self()->IsExceptionPending()); diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc index 83d7a3d4cc..ed36e111ff 100644 --- a/compiler/driver/compiler_driver.cc +++ b/compiler/driver/compiler_driver.cc @@ -16,9 +16,9 @@ #include "compiler_driver.h" +#include <unistd.h> #include <unordered_set> #include <vector> -#include <unistd.h> #ifndef __APPLE__ #include <malloc.h> // For mallinfo @@ -41,31 +41,31 @@ #include "compiler.h" #include "compiler_callbacks.h" #include "compiler_driver-inl.h" -#include "dex_compilation_unit.h" -#include "dex_file-inl.h" -#include "dex_instruction-inl.h" #include "dex/dex_to_dex_compiler.h" #include "dex/verification_results.h" #include "dex/verified_method.h" +#include "dex_compilation_unit.h" +#include "dex_file-inl.h" +#include "dex_instruction-inl.h" #include "driver/compiler_options.h" -#include "intrinsics_enum.h" -#include "jni_internal.h" -#include "object_lock.h" -#include "runtime.h" #include "gc/accounting/card_table-inl.h" #include "gc/accounting/heap_bitmap.h" #include "gc/space/image_space.h" #include "gc/space/space.h" -#include "mirror/class_loader.h" +#include "handle_scope-inl.h" +#include "intrinsics_enum.h" +#include "jni_internal.h" #include "mirror/class-inl.h" +#include "mirror/class_loader.h" #include "mirror/dex_cache-inl.h" #include "mirror/object-inl.h" #include "mirror/object-refvisitor-inl.h" #include "mirror/object_array-inl.h" #include "mirror/throwable.h" +#include "nativehelper/ScopedLocalRef.h" +#include "object_lock.h" +#include "runtime.h" #include "scoped_thread_state_change-inl.h" -#include "ScopedLocalRef.h" -#include "handle_scope-inl.h" #include "thread.h" #include "thread_list.h" #include "thread_pool.h" @@ -75,8 +75,8 @@ #include "utils/dex_cache_arrays_layout-inl.h" #include "utils/swap_space.h" #include "vdex_file.h" -#include "verifier/method_verifier.h" #include "verifier/method_verifier-inl.h" +#include "verifier/method_verifier.h" #include "verifier/verifier_deps.h" #include "verifier/verifier_enums.h" @@ -291,7 +291,6 @@ CompilerDriver::CompilerDriver( instruction_set_(instruction_set == kArm ? kThumb2 : instruction_set), instruction_set_features_(instruction_set_features), requires_constructor_barrier_lock_("constructor barrier lock"), - compiled_classes_lock_("compiled classes lock"), non_relative_linker_patch_count_(0u), image_classes_(image_classes), classes_to_compile_(compiled_classes), @@ -374,14 +373,12 @@ static void SetupIntrinsic(Thread* self, REQUIRES_SHARED(Locks::mutator_lock_) { ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); PointerSize image_size = class_linker->GetImagePointerSize(); - mirror::Class* cls = class_linker->FindSystemClass(self, class_name); + ObjPtr<mirror::Class> cls = class_linker->FindSystemClass(self, class_name); if (cls == nullptr) { LOG(FATAL) << "Could not find class of intrinsic " << class_name; } - ArtMethod* method = (invoke_type == kStatic || invoke_type == kDirect) - ? cls->FindDeclaredDirectMethod(method_name, signature, image_size) - : cls->FindDeclaredVirtualMethod(method_name, signature, image_size); - if (method == nullptr) { + ArtMethod* method = cls->FindClassMethod(method_name, signature, image_size); + if (method == nullptr || method->GetDeclaringClass() != cls) { LOG(FATAL) << "Could not find method of intrinsic " << class_name << " " << method_name << " " << signature; } @@ -412,7 +409,7 @@ void CompilerDriver::CompileAll(jobject class_loader, ClassName, MethodName, Signature) \ SetupIntrinsic(soa.Self(), Intrinsics::k##Name, InvokeType, ClassName, MethodName, Signature); #include "intrinsics_list.h" -INTRINSICS_LIST(SETUP_INTRINSICS) + INTRINSICS_LIST(SETUP_INTRINSICS) #undef INTRINSICS_LIST #undef SETUP_INTRINSICS } @@ -544,7 +541,7 @@ static void CompileMethod(Thread* self, // TODO: Lookup annotation from DexFile directly without resolving method. ArtMethod* method = - Runtime::Current()->GetClassLinker()->ResolveMethod<ClassLinker::kNoICCECheckForCache>( + Runtime::Current()->GetClassLinker()->ResolveMethod<ClassLinker::ResolveMode::kNoChecks>( dex_file, method_idx, dex_cache, @@ -1756,7 +1753,7 @@ class ResolveClassFieldsAndMethodsVisitor : public CompilationVisitor { } if (resolve_fields_and_methods) { while (it.HasNextDirectMethod()) { - ArtMethod* method = class_linker->ResolveMethod<ClassLinker::kNoICCECheckForCache>( + ArtMethod* method = class_linker->ResolveMethod<ClassLinker::ResolveMode::kNoChecks>( dex_file, it.GetMemberIndex(), dex_cache, class_loader, nullptr, it.GetMethodInvokeType(class_def)); if (method == nullptr) { @@ -1765,7 +1762,7 @@ class ResolveClassFieldsAndMethodsVisitor : public CompilationVisitor { it.Next(); } while (it.HasNextVirtualMethod()) { - ArtMethod* method = class_linker->ResolveMethod<ClassLinker::kNoICCECheckForCache>( + ArtMethod* method = class_linker->ResolveMethod<ClassLinker::ResolveMode::kNoChecks>( dex_file, it.GetMemberIndex(), dex_cache, class_loader, nullptr, it.GetMethodInvokeType(class_def)); if (method == nullptr) { @@ -1947,7 +1944,12 @@ bool CompilerDriver::FastVerify(jobject jclass_loader, if (compiler_only_verifies) { // Just update the compiled_classes_ map. The compiler doesn't need to resolve // the type. - compiled_classes_.Overwrite(ClassReference(dex_file, i), mirror::Class::kStatusVerified); + DexFileReference ref(dex_file, i); + mirror::Class::Status existing = mirror::Class::kStatusNotReady; + DCHECK(compiled_classes_.Get(ref, &existing)) << ref.dex_file->GetLocation(); + ClassStateTable::InsertResult result = + compiled_classes_.Insert(ref, existing, mirror::Class::kStatusVerified); + CHECK_EQ(result, ClassStateTable::kInsertResultSuccess); } else { // Update the class status, so later compilation stages know they don't need to verify // the class. @@ -1978,6 +1980,13 @@ bool CompilerDriver::FastVerify(jobject jclass_loader, void CompilerDriver::Verify(jobject jclass_loader, const std::vector<const DexFile*>& dex_files, TimingLogger* timings) { + // Always add the dex files to compiled_classes_. This happens for all compiler filters. + for (const DexFile* dex_file : dex_files) { + if (!compiled_classes_.HaveDexFile(dex_file)) { + compiled_classes_.AddDexFile(dex_file, dex_file->NumClassDefs()); + } + } + if (FastVerify(jclass_loader, dex_files, timings)) { return; } @@ -2202,6 +2211,9 @@ void CompilerDriver::SetVerifiedDexFile(jobject class_loader, size_t thread_count, TimingLogger* timings) { TimingLogger::ScopedTiming t("Verify Dex File", timings); + if (!compiled_classes_.HaveDexFile(&dex_file)) { + compiled_classes_.AddDexFile(&dex_file, dex_file.NumClassDefs()); + } ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); ParallelCompilationManager context(class_linker, class_loader, this, &dex_file, dex_files, thread_pool); @@ -2248,12 +2260,13 @@ class InitializeClassVisitor : public CompilationVisitor { const bool is_app_image = manager_->GetCompiler()->GetCompilerOptions().IsAppImage(); mirror::Class::Status old_status = klass->GetStatus(); + // Don't initialize classes in boot space when compiling app image + if (is_app_image && klass->IsBootStrapClassLoaded()) { + // Also return early and don't store the class status in the recorded class status. + return; + } // Only try to initialize classes that were successfully verified. if (klass->IsVerified()) { - // Don't initialize classes in boot space when compiling app image - if (is_app_image && klass->IsBootStrapClassLoaded()) { - return; - } // Attempt to initialize the class but bail if we either need to initialize the super-class // or static fields. manager_->GetClassLinker()->EnsureInitialized(soa.Self(), klass, false, false); @@ -2318,10 +2331,8 @@ class InitializeClassVisitor : public CompilationVisitor { // a ReaderWriterMutex but we're holding the mutator lock so we fail mutex sanity // checks in Thread::AssertThreadSuspensionIsAllowable. Runtime* const runtime = Runtime::Current(); - Transaction transaction; - // Run the class initializer in transaction mode. - runtime->EnterTransactionMode(&transaction); + runtime->EnterTransactionMode(klass.Get()); bool success = manager_->GetClassLinker()->EnsureInitialized(soa.Self(), klass, true, true); // TODO we detach transaction from runtime to indicate we quit the transactional @@ -2330,7 +2341,11 @@ class InitializeClassVisitor : public CompilationVisitor { { ScopedAssertNoThreadSuspension ants("Transaction end"); - runtime->ExitTransactionMode(); + + if (success) { + runtime->ExitTransactionMode(); + DCHECK(!runtime->IsActiveTransaction()); + } if (!success) { CHECK(soa.Self()->IsExceptionPending()); @@ -2344,7 +2359,7 @@ class InitializeClassVisitor : public CompilationVisitor { *file_log << exception->Dump() << "\n"; } soa.Self()->ClearException(); - transaction.Rollback(); + runtime->RollbackAndExitTransactionMode(); CHECK_EQ(old_status, klass->GetStatus()) << "Previous class status not restored"; } else if (is_boot_image) { // For boot image, we want to put the updated status in the oat class since we can't @@ -2860,12 +2875,12 @@ void CompilerDriver::AddCompiledMethod(const MethodReference& method_ref, bool CompilerDriver::GetCompiledClass(ClassReference ref, mirror::Class::Status* status) const { DCHECK(status != nullptr); - MutexLock mu(Thread::Current(), compiled_classes_lock_); - ClassStateTable::const_iterator it = compiled_classes_.find(ref); - if (it == compiled_classes_.end()) { + // The table doesn't know if something wasn't inserted. For this case it will return + // kStatusNotReady. To handle this, just assume anything not verified is not compiled. + if (!compiled_classes_.Get(DexFileReference(ref.first, ref.second), status) || + *status < mirror::Class::kStatusVerified) { return false; } - *status = it->second; return true; } @@ -2886,15 +2901,30 @@ void CompilerDriver::RecordClassStatus(ClassReference ref, mirror::Class::Status << " of " << status; } - MutexLock mu(Thread::Current(), compiled_classes_lock_); - auto it = compiled_classes_.find(ref); - if (it == compiled_classes_.end()) { - compiled_classes_.Overwrite(ref, status); - } else if (status > it->second) { + ClassStateTable::InsertResult result; + do { + DexFileReference dex_ref(ref.first, ref.second); + mirror::Class::Status existing = mirror::Class::kStatusNotReady; + if (!compiled_classes_.Get(dex_ref, &existing)) { + // Probably a uses library class, bail. + if (kIsDebugBuild) { + // Check to make sure it's not a dex file for an oat file we are compiling since these + // should always succeed. These do not include classes in for used libraries. + for (const DexFile* dex_file : *dex_files_for_oat_file_) { + CHECK_NE(dex_ref.dex_file, dex_file) << dex_ref.dex_file->GetLocation(); + } + } + return; + } + if (existing >= status) { + // Existing status is already better than we expect, break. + break; + } // Update the status if we now have a greater one. This happens with vdex, // which records a class is verified, but does not resolve it. - it->second = status; - } + result = compiled_classes_.Insert(dex_ref, existing, status); + CHECK(result != ClassStateTable::kInsertResultInvalidDexFile); + } while (result != ClassStateTable::kInsertResultSuccess); } CompiledMethod* CompilerDriver::GetCompiledMethod(MethodReference ref) const { diff --git a/compiler/driver/compiler_driver.h b/compiler/driver/compiler_driver.h index a3272d331d..ecaed83e57 100644 --- a/compiler/driver/compiler_driver.h +++ b/compiler/driver/compiler_driver.h @@ -32,8 +32,8 @@ #include "dex_file.h" #include "dex_file_types.h" #include "driver/compiled_method_storage.h" -#include "jit/profile_compilation_info.h" #include "invoke_type.h" +#include "jit/profile_compilation_info.h" #include "method_reference.h" #include "mirror/class.h" // For mirror::Class::Status. #include "os.h" @@ -117,12 +117,12 @@ class CompilerDriver { void CompileAll(jobject class_loader, const std::vector<const DexFile*>& dex_files, TimingLogger* timings) - REQUIRES(!Locks::mutator_lock_, !compiled_classes_lock_, !dex_to_dex_references_lock_); + REQUIRES(!Locks::mutator_lock_, !dex_to_dex_references_lock_); // Compile a single Method. void CompileOne(Thread* self, ArtMethod* method, TimingLogger* timings) REQUIRES_SHARED(Locks::mutator_lock_) - REQUIRES(!compiled_classes_lock_, !dex_to_dex_references_lock_); + REQUIRES(!dex_to_dex_references_lock_); VerificationResults* GetVerificationResults() const; @@ -153,8 +153,7 @@ class CompilerDriver { std::unique_ptr<const std::vector<uint8_t>> CreateQuickResolutionTrampoline() const; std::unique_ptr<const std::vector<uint8_t>> CreateQuickToInterpreterBridge() const; - bool GetCompiledClass(ClassReference ref, mirror::Class::Status* status) const - REQUIRES(!compiled_classes_lock_); + bool GetCompiledClass(ClassReference ref, mirror::Class::Status* status) const; CompiledMethod* GetCompiledMethod(MethodReference ref) const; size_t GetNonRelativeLinkerPatchCount() const; @@ -337,8 +336,7 @@ class CompilerDriver { // according to the profile file. bool ShouldVerifyClassBasedOnProfile(const DexFile& dex_file, uint16_t class_idx) const; - void RecordClassStatus(ClassReference ref, mirror::Class::Status status) - REQUIRES(!compiled_classes_lock_); + void RecordClassStatus(ClassReference ref, mirror::Class::Status status); // Checks if the specified method has been verified without failures. Returns // false if the method is not in the verification results (GetVerificationResults). @@ -387,7 +385,7 @@ class CompilerDriver { void PreCompile(jobject class_loader, const std::vector<const DexFile*>& dex_files, TimingLogger* timings) - REQUIRES(!Locks::mutator_lock_, !compiled_classes_lock_); + REQUIRES(!Locks::mutator_lock_); void LoadImageClasses(TimingLogger* timings) REQUIRES(!Locks::mutator_lock_); @@ -408,12 +406,9 @@ class CompilerDriver { // Do fast verification through VerifierDeps if possible. Return whether // verification was successful. - // NO_THREAD_SAFETY_ANALYSIS as the method accesses a guarded value in a - // single-threaded way. bool FastVerify(jobject class_loader, const std::vector<const DexFile*>& dex_files, - TimingLogger* timings) - NO_THREAD_SAFETY_ANALYSIS; + TimingLogger* timings); void Verify(jobject class_loader, const std::vector<const DexFile*>& dex_files, @@ -441,12 +436,12 @@ class CompilerDriver { void InitializeClasses(jobject class_loader, const std::vector<const DexFile*>& dex_files, TimingLogger* timings) - REQUIRES(!Locks::mutator_lock_, !compiled_classes_lock_); + REQUIRES(!Locks::mutator_lock_); void InitializeClasses(jobject class_loader, const DexFile& dex_file, const std::vector<const DexFile*>& dex_files, TimingLogger* timings) - REQUIRES(!Locks::mutator_lock_, !compiled_classes_lock_); + REQUIRES(!Locks::mutator_lock_); void UpdateImageClasses(TimingLogger* timings) REQUIRES(!Locks::mutator_lock_); @@ -484,10 +479,9 @@ class CompilerDriver { std::map<ClassReference, bool> requires_constructor_barrier_ GUARDED_BY(requires_constructor_barrier_lock_); - using ClassStateTable = SafeMap<const ClassReference, mirror::Class::Status>; - // All class references that this compiler has compiled. - mutable Mutex compiled_classes_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER; - ClassStateTable compiled_classes_ GUARDED_BY(compiled_classes_lock_); + // All class references that this compiler has compiled. Indexed by class defs. + using ClassStateTable = AtomicDexRefMap<mirror::Class::Status>; + ClassStateTable compiled_classes_; typedef AtomicDexRefMap<CompiledMethod*> MethodTable; diff --git a/compiler/driver/compiler_driver_test.cc b/compiler/driver/compiler_driver_test.cc index b4ad325822..10bfd972f0 100644 --- a/compiler/driver/compiler_driver_test.cc +++ b/compiler/driver/compiler_driver_test.cc @@ -26,13 +26,13 @@ #include "dex_file.h" #include "dex_file_types.h" #include "gc/heap.h" +#include "handle_scope-inl.h" +#include "jit/profile_compilation_info.h" #include "mirror/class-inl.h" #include "mirror/class_loader.h" #include "mirror/dex_cache-inl.h" -#include "mirror/object_array-inl.h" #include "mirror/object-inl.h" -#include "handle_scope-inl.h" -#include "jit/profile_compilation_info.h" +#include "mirror/object_array-inl.h" #include "scoped_thread_state_change-inl.h" namespace art { @@ -118,10 +118,12 @@ TEST_F(CompilerDriverTest, DISABLED_LARGE_CompileDexLibCore) { EXPECT_TRUE(type != nullptr) << "type_idx=" << i << " " << dex.GetTypeDescriptor(dex.GetTypeId(dex::TypeIndex(i))); } - EXPECT_EQ(dex.NumMethodIds(), dex_cache->NumResolvedMethods()); + EXPECT_TRUE(dex_cache->StaticMethodSize() == dex_cache->NumResolvedMethods() + || dex.NumMethodIds() == dex_cache->NumResolvedMethods()); auto* cl = Runtime::Current()->GetClassLinker(); auto pointer_size = cl->GetImagePointerSize(); for (size_t i = 0; i < dex_cache->NumResolvedMethods(); i++) { + // FIXME: This is outdated for hash-based method array. ArtMethod* method = dex_cache->GetResolvedMethod(i, pointer_size); EXPECT_TRUE(method != nullptr) << "method_idx=" << i << " " << dex.GetMethodDeclaringClassDescriptor(dex.GetMethodId(i)) @@ -133,6 +135,7 @@ TEST_F(CompilerDriverTest, DISABLED_LARGE_CompileDexLibCore) { EXPECT_TRUE(dex_cache->StaticArtFieldSize() == dex_cache->NumResolvedFields() || dex.NumFieldIds() == dex_cache->NumResolvedFields()); for (size_t i = 0; i < dex_cache->NumResolvedFields(); i++) { + // FIXME: This is outdated for hash-based field array. ArtField* field = dex_cache->GetResolvedField(i, cl->GetImagePointerSize()); EXPECT_TRUE(field != nullptr) << "field_idx=" << i << " " << dex.GetFieldDeclaringClassDescriptor(dex.GetFieldId(i)) diff --git a/compiler/elf_writer_test.cc b/compiler/elf_writer_test.cc index fa2d78d2e8..984e9ee4e9 100644 --- a/compiler/elf_writer_test.cc +++ b/compiler/elf_writer_test.cc @@ -18,9 +18,9 @@ #include "base/unix_file/fd_file.h" #include "common_compiler_test.h" +#include "elf_builder.h" #include "elf_file.h" #include "elf_file_impl.h" -#include "elf_builder.h" #include "elf_writer_quick.h" #include "oat.h" #include "utils.h" diff --git a/compiler/exception_test.cc b/compiler/exception_test.cc index b4777df0df..f759aa5ef8 100644 --- a/compiler/exception_test.cc +++ b/compiler/exception_test.cc @@ -21,19 +21,19 @@ #include "base/enums.h" #include "class_linker.h" #include "common_runtime_test.h" -#include "dex_file.h" #include "dex_file-inl.h" +#include "dex_file.h" #include "gtest/gtest.h" +#include "handle_scope-inl.h" #include "leb128.h" #include "mirror/class-inl.h" -#include "mirror/object_array-inl.h" #include "mirror/object-inl.h" +#include "mirror/object_array-inl.h" #include "mirror/stack_trace_element.h" #include "oat_quick_method_header.h" #include "optimizing/stack_map_stream.h" #include "runtime-inl.h" #include "scoped_thread_state_change-inl.h" -#include "handle_scope-inl.h" #include "thread.h" namespace art { @@ -102,12 +102,14 @@ class ExceptionTest : public CommonRuntimeTest { CHECK_ALIGNED(stack_maps_offset, 2); } - method_f_ = my_klass_->FindVirtualMethod("f", "()I", kRuntimePointerSize); + method_f_ = my_klass_->FindClassMethod("f", "()I", kRuntimePointerSize); ASSERT_TRUE(method_f_ != nullptr); + ASSERT_FALSE(method_f_->IsDirect()); method_f_->SetEntryPointFromQuickCompiledCode(code_ptr); - method_g_ = my_klass_->FindVirtualMethod("g", "(I)V", kRuntimePointerSize); + method_g_ = my_klass_->FindClassMethod("g", "(I)V", kRuntimePointerSize); ASSERT_TRUE(method_g_ != nullptr); + ASSERT_FALSE(method_g_->IsDirect()); method_g_->SetEntryPointFromQuickCompiledCode(code_ptr); } diff --git a/compiler/image_test.cc b/compiler/image_test.cc index 9d7aff769b..252fdd67e1 100644 --- a/compiler/image_test.cc +++ b/compiler/image_test.cc @@ -113,9 +113,9 @@ TEST_F(ImageTest, TestDefaultMethods) { mirror::Class* iface_klass = class_linker_->LookupClass( self, "LIface;", ObjPtr<mirror::ClassLoader>()); ASSERT_NE(nullptr, iface_klass); - ArtMethod* origin = iface_klass->FindDeclaredVirtualMethod( - "defaultMethod", "()V", pointer_size); + ArtMethod* origin = iface_klass->FindInterfaceMethod("defaultMethod", "()V", pointer_size); ASSERT_NE(nullptr, origin); + ASSERT_TRUE(origin->GetDeclaringClass() == iface_klass); const void* code = origin->GetEntryPointFromQuickCompiledCodePtrSize(pointer_size); // The origin method should have a pointer to quick code ASSERT_NE(nullptr, code); @@ -134,9 +134,11 @@ TEST_F(ImageTest, TestDefaultMethods) { mirror::Class* iterable_klass = class_linker_->LookupClass( self, "Ljava/lang/Iterable;", ObjPtr<mirror::ClassLoader>()); ASSERT_NE(nullptr, iterable_klass); - origin = iterable_klass->FindDeclaredVirtualMethod( + origin = iterable_klass->FindClassMethod( "forEach", "(Ljava/util/function/Consumer;)V", pointer_size); ASSERT_NE(nullptr, origin); + ASSERT_FALSE(origin->IsDirect()); + ASSERT_TRUE(origin->GetDeclaringClass() == iterable_klass); code = origin->GetEntryPointFromQuickCompiledCodePtrSize(pointer_size); // the origin method should have a pointer to quick code ASSERT_NE(nullptr, code); diff --git a/compiler/image_test.h b/compiler/image_test.h index fa714ada6c..57d0987982 100644 --- a/compiler/image_test.h +++ b/compiler/image_test.h @@ -28,8 +28,8 @@ #include "art_method-inl.h" #include "base/unix_file/fd_file.h" #include "class_linker-inl.h" -#include "compiler_callbacks.h" #include "common_compiler_test.h" +#include "compiler_callbacks.h" #include "debug/method_debug_info.h" #include "dex/quick_compiler_callbacks.h" #include "driver/compiler_options.h" @@ -84,9 +84,10 @@ class ImageTest : public CommonCompilerTest { void SetUpRuntimeOptions(RuntimeOptions* options) OVERRIDE { CommonCompilerTest::SetUpRuntimeOptions(options); - callbacks_.reset(new QuickCompilerCallbacks( - verification_results_.get(), - CompilerCallbacks::CallbackMode::kCompileBootImage)); + QuickCompilerCallbacks* new_callbacks = + new QuickCompilerCallbacks(CompilerCallbacks::CallbackMode::kCompileBootImage); + new_callbacks->SetVerificationResults(verification_results_.get()); + callbacks_.reset(new_callbacks); options->push_back(std::make_pair("compilercallbacks", callbacks_.get())); } diff --git a/compiler/image_writer.cc b/compiler/image_writer.cc index f92bf95065..318009c606 100644 --- a/compiler/image_writer.cc +++ b/compiler/image_writer.cc @@ -16,9 +16,9 @@ #include "image_writer.h" -#include <sys/stat.h> #include <lz4.h> #include <lz4hc.h> +#include <sys/stat.h> #include <memory> #include <numeric> @@ -43,8 +43,8 @@ #include "gc/accounting/heap_bitmap.h" #include "gc/accounting/space_bitmap-inl.h" #include "gc/collector/concurrent_copying.h" -#include "gc/heap.h" #include "gc/heap-visit-objects-inl.h" +#include "gc/heap.h" #include "gc/space/large_object_space.h" #include "gc/space/space-inl.h" #include "gc/verification.h" @@ -59,8 +59,8 @@ #include "mirror/class-inl.h" #include "mirror/class_ext.h" #include "mirror/class_loader.h" -#include "mirror/dex_cache.h" #include "mirror/dex_cache-inl.h" +#include "mirror/dex_cache.h" #include "mirror/executable.h" #include "mirror/method.h" #include "mirror/object-inl.h" @@ -1023,41 +1023,58 @@ void ImageWriter::PruneAndPreloadDexCache(ObjPtr<mirror::DexCache> dex_cache, Runtime* runtime = Runtime::Current(); ClassLinker* class_linker = runtime->GetClassLinker(); - ArtMethod* resolution_method = runtime->GetResolutionMethod(); const DexFile& dex_file = *dex_cache->GetDexFile(); // Prune methods. - ArtMethod** resolved_methods = dex_cache->GetResolvedMethods(); - for (size_t i = 0, num = dex_cache->NumResolvedMethods(); i != num; ++i) { - ArtMethod* method = - mirror::DexCache::GetElementPtrSize(resolved_methods, i, target_ptr_size_); - DCHECK(method != nullptr) << "Expected resolution method instead of null method"; + mirror::MethodDexCacheType* resolved_methods = dex_cache->GetResolvedMethods(); + dex::TypeIndex last_class_idx; // Initialized to invalid index. + ObjPtr<mirror::Class> last_class = nullptr; + for (size_t i = 0, num = dex_cache->GetDexFile()->NumMethodIds(); i != num; ++i) { + uint32_t slot_idx = dex_cache->MethodSlotIndex(i); + auto pair = + mirror::DexCache::GetNativePairPtrSize(resolved_methods, slot_idx, target_ptr_size_); + uint32_t stored_index = pair.index; + ArtMethod* method = pair.object; + if (method != nullptr && i > stored_index) { + continue; // Already checked. + } // Check if the referenced class is in the image. Note that we want to check the referenced // class rather than the declaring class to preserve the semantics, i.e. using a MethodId // results in resolving the referenced class and that can for example throw OOME. - ObjPtr<mirror::Class> referencing_class = class_linker->LookupResolvedType( - dex_file, - dex_file.GetMethodId(i).class_idx_, - dex_cache, - class_loader); - // Copied methods may be held live by a class which was not an image class but have a - // declaring class which is an image class. Set it to the resolution method to be safe and - // prevent dangling pointers. - if (method->IsCopied() || !KeepClass(referencing_class)) { - mirror::DexCache::SetElementPtrSize(resolved_methods, - i, - resolution_method, - target_ptr_size_); - } else if (kIsDebugBuild) { - // Check that the class is still in the classes table. - ReaderMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_); - CHECK(class_linker->ClassInClassTable(referencing_class)) << "Class " - << Class::PrettyClass(referencing_class) << " not in class linker table"; + const DexFile::MethodId& method_id = dex_file.GetMethodId(i); + if (method_id.class_idx_ != last_class_idx) { + last_class_idx = method_id.class_idx_; + last_class = class_linker->LookupResolvedType( + dex_file, last_class_idx, dex_cache, class_loader); + if (last_class != nullptr && !KeepClass(last_class)) { + last_class = nullptr; + } + } + if (method == nullptr || i < stored_index) { + if (last_class != nullptr) { + const char* name = dex_file.StringDataByIdx(method_id.name_idx_); + Signature signature = dex_file.GetMethodSignature(method_id); + if (last_class->IsInterface()) { + method = last_class->FindInterfaceMethod(name, signature, target_ptr_size_); + } else { + method = last_class->FindClassMethod(name, signature, target_ptr_size_); + } + if (method != nullptr) { + // If the referenced class is in the image, the defining class must also be there. + DCHECK(KeepClass(method->GetDeclaringClass())); + dex_cache->SetResolvedMethod(i, method, target_ptr_size_); + } + } + } else { + DCHECK_EQ(i, stored_index); + if (last_class == nullptr) { + dex_cache->ClearResolvedMethod(stored_index, target_ptr_size_); + } } } // Prune fields and make the contents of the field array deterministic. mirror::FieldDexCacheType* resolved_fields = dex_cache->GetResolvedFields(); - dex::TypeIndex last_class_idx; // Initialized to invalid index. - ObjPtr<mirror::Class> last_class = nullptr; + last_class_idx = dex::TypeIndex(); // Initialized to invalid index. + last_class = nullptr; for (size_t i = 0, end = dex_file.NumFieldIds(); i < end; ++i) { uint32_t slot_idx = dex_cache->FieldSlotIndex(i); auto pair = mirror::DexCache::GetNativePairPtrSize(resolved_fields, slot_idx, target_ptr_size_); @@ -2401,17 +2418,19 @@ void ImageWriter::FixupDexCache(mirror::DexCache* orig_dex_cache, orig_dex_cache->FixupResolvedTypes(NativeCopyLocation(orig_types, orig_dex_cache), fixup_visitor); } - ArtMethod** orig_methods = orig_dex_cache->GetResolvedMethods(); + mirror::MethodDexCacheType* orig_methods = orig_dex_cache->GetResolvedMethods(); if (orig_methods != nullptr) { copy_dex_cache->SetFieldPtrWithSize<false>(mirror::DexCache::ResolvedMethodsOffset(), NativeLocationInImage(orig_methods), PointerSize::k64); - ArtMethod** copy_methods = NativeCopyLocation(orig_methods, orig_dex_cache); + mirror::MethodDexCacheType* copy_methods = NativeCopyLocation(orig_methods, orig_dex_cache); for (size_t i = 0, num = orig_dex_cache->NumResolvedMethods(); i != num; ++i) { - ArtMethod* orig = mirror::DexCache::GetElementPtrSize(orig_methods, i, target_ptr_size_); + mirror::MethodDexCachePair orig_pair = + mirror::DexCache::GetNativePairPtrSize(orig_methods, i, target_ptr_size_); // NativeLocationInImage also handles runtime methods since these have relocation info. - ArtMethod* copy = NativeLocationInImage(orig); - mirror::DexCache::SetElementPtrSize(copy_methods, i, copy, target_ptr_size_); + mirror::MethodDexCachePair copy_pair(NativeLocationInImage(orig_pair.object), + orig_pair.index); + mirror::DexCache::SetNativePairPtrSize(copy_methods, i, copy_pair, target_ptr_size_); } } mirror::FieldDexCacheType* orig_fields = orig_dex_cache->GetResolvedFields(); @@ -2552,7 +2571,8 @@ void ImageWriter::CopyAndFixupMethod(ArtMethod* orig, CopyReference(copy->GetDeclaringClassAddressWithoutBarrier(), orig->GetDeclaringClassUnchecked()); - ArtMethod** orig_resolved_methods = orig->GetDexCacheResolvedMethods(target_ptr_size_); + mirror::MethodDexCacheType* orig_resolved_methods = + orig->GetDexCacheResolvedMethods(target_ptr_size_); copy->SetDexCacheResolvedMethods(NativeLocationInImage(orig_resolved_methods), target_ptr_size_); // OatWriter replaces the code_ with an offset value. Here we re-adjust to a pointer relative to diff --git a/compiler/image_writer.h b/compiler/image_writer.h index ee6fc1dff6..34bbbad75d 100644 --- a/compiler/image_writer.h +++ b/compiler/image_writer.h @@ -22,10 +22,10 @@ #include <cstddef> #include <memory> +#include <ostream> #include <set> #include <stack> #include <string> -#include <ostream> #include "art_method.h" #include "base/bit_utils.h" @@ -40,8 +40,8 @@ #include "lock_word.h" #include "mem_map.h" #include "mirror/dex_cache.h" -#include "obj_ptr.h" #include "oat_file.h" +#include "obj_ptr.h" #include "os.h" #include "safe_map.h" #include "utils.h" diff --git a/compiler/jit/jit_compiler.cc b/compiler/jit/jit_compiler.cc index 28a3f1edae..5fdf9ff07c 100644 --- a/compiler/jit/jit_compiler.cc +++ b/compiler/jit/jit_compiler.cc @@ -184,10 +184,8 @@ bool JitCompiler::CompileMethod(Thread* self, ArtMethod* method, bool osr) { { TimingLogger::ScopedTiming t2("Compiling", &logger); JitCodeCache* const code_cache = runtime->GetJit()->GetCodeCache(); - success = compiler_driver_->GetCompiler()->JitCompile(self, code_cache, method, osr); - if (success && (jit_logger_ != nullptr)) { - jit_logger_->WriteLog(code_cache, method, osr); - } + success = compiler_driver_->GetCompiler()->JitCompile( + self, code_cache, method, osr, jit_logger_.get()); } // Trim maps to reduce memory usage. diff --git a/compiler/jit/jit_compiler.h b/compiler/jit/jit_compiler.h index f0f24d345e..1e1838efd5 100644 --- a/compiler/jit/jit_compiler.h +++ b/compiler/jit/jit_compiler.h @@ -19,9 +19,9 @@ #include "base/mutex.h" #include "compiled_method.h" -#include "jit_logger.h" #include "driver/compiler_driver.h" #include "driver/compiler_options.h" +#include "jit_logger.h" namespace art { diff --git a/compiler/jit/jit_logger.cc b/compiler/jit/jit_logger.cc index aa4f66773a..2199b64139 100644 --- a/compiler/jit/jit_logger.cc +++ b/compiler/jit/jit_logger.cc @@ -50,11 +50,8 @@ void JitLogger::OpenPerfMapLog() { } } -void JitLogger::WritePerfMapLog(JitCodeCache* code_cache, ArtMethod* method, bool osr) { +void JitLogger::WritePerfMapLog(const void* ptr, size_t code_size, ArtMethod* method) { if (perf_file_ != nullptr) { - const void* ptr = osr ? code_cache->LookupOsrMethodHeader(method)->GetCode() - : method->GetEntryPointFromQuickCompiledCode(); - size_t code_size = code_cache->GetMemorySizeOfCodePointer(ptr); std::string method_name = method->PrettyMethod(); std::ostringstream stream; @@ -270,11 +267,8 @@ void JitLogger::OpenJitDumpLog() { WriteJitDumpHeader(); } -void JitLogger::WriteJitDumpLog(JitCodeCache* code_cache, ArtMethod* method, bool osr) { +void JitLogger::WriteJitDumpLog(const void* ptr, size_t code_size, ArtMethod* method) { if (jit_dump_file_ != nullptr) { - const void* code = osr ? code_cache->LookupOsrMethodHeader(method)->GetCode() - : method->GetEntryPointFromQuickCompiledCode(); - size_t code_size = code_cache->GetMemorySizeOfCodePointer(code); std::string method_name = method->PrettyMethod(); PerfJitCodeLoad jit_code; @@ -285,7 +279,7 @@ void JitLogger::WriteJitDumpLog(JitCodeCache* code_cache, ArtMethod* method, boo jit_code.process_id_ = static_cast<uint32_t>(getpid()); jit_code.thread_id_ = static_cast<uint32_t>(art::GetTid()); jit_code.vma_ = 0x0; - jit_code.code_address_ = reinterpret_cast<uint64_t>(code); + jit_code.code_address_ = reinterpret_cast<uint64_t>(ptr); jit_code.code_size_ = code_size; jit_code.code_id_ = code_index_++; @@ -297,7 +291,7 @@ void JitLogger::WriteJitDumpLog(JitCodeCache* code_cache, ArtMethod* method, boo // Use UNUSED() here to avoid compiler warnings. UNUSED(jit_dump_file_->WriteFully(reinterpret_cast<const char*>(&jit_code), sizeof(jit_code))); UNUSED(jit_dump_file_->WriteFully(method_name.c_str(), method_name.size() + 1)); - UNUSED(jit_dump_file_->WriteFully(code, code_size)); + UNUSED(jit_dump_file_->WriteFully(ptr, code_size)); WriteJitDumpDebugInfo(); } diff --git a/compiler/jit/jit_logger.h b/compiler/jit/jit_logger.h index 460864e8a9..19be9aa88e 100644 --- a/compiler/jit/jit_logger.h +++ b/compiler/jit/jit_logger.h @@ -94,10 +94,10 @@ class JitLogger { OpenJitDumpLog(); } - void WriteLog(JitCodeCache* code_cache, ArtMethod* method, bool osr) + void WriteLog(const void* ptr, size_t code_size, ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_) { - WritePerfMapLog(code_cache, method, osr); - WriteJitDumpLog(code_cache, method, osr); + WritePerfMapLog(ptr, code_size, method); + WriteJitDumpLog(ptr, code_size, method); } void CloseLog() { @@ -108,13 +108,13 @@ class JitLogger { private: // For perf-map profiling void OpenPerfMapLog(); - void WritePerfMapLog(JitCodeCache* code_cache, ArtMethod* method, bool osr) + void WritePerfMapLog(const void* ptr, size_t code_size, ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_); void ClosePerfMapLog(); // For perf-inject profiling void OpenJitDumpLog(); - void WriteJitDumpLog(JitCodeCache* code_cache, ArtMethod* method, bool osr) + void WriteJitDumpLog(const void* ptr, size_t code_size, ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_); void CloseJitDumpLog(); diff --git a/compiler/jni/jni_compiler_test.cc b/compiler/jni/jni_compiler_test.cc index 6ce7d75da6..3460efe474 100644 --- a/compiler/jni/jni_compiler_test.cc +++ b/compiler/jni/jni_compiler_test.cc @@ -32,12 +32,12 @@ #include "mem_map.h" #include "mirror/class-inl.h" #include "mirror/class_loader.h" -#include "mirror/object_array-inl.h" #include "mirror/object-inl.h" +#include "mirror/object_array-inl.h" #include "mirror/stack_trace_element.h" +#include "nativehelper/ScopedLocalRef.h" #include "nativeloader/native_loader.h" #include "runtime.h" -#include "ScopedLocalRef.h" #include "scoped_thread_state_change-inl.h" #include "thread.h" @@ -247,9 +247,9 @@ class JniCompilerTest : public CommonCompilerTest { // Compile the native method before starting the runtime mirror::Class* c = class_linker_->FindClass(soa.Self(), "LMyClassNatives;", loader); const auto pointer_size = class_linker_->GetImagePointerSize(); - ArtMethod* method = direct ? c->FindDirectMethod(method_name, method_sig, pointer_size) : - c->FindVirtualMethod(method_name, method_sig, pointer_size); + ArtMethod* method = c->FindClassMethod(method_name, method_sig, pointer_size); ASSERT_TRUE(method != nullptr) << method_name << " " << method_sig; + ASSERT_EQ(direct, method->IsDirect()) << method_name << " " << method_sig; if (check_generic_jni_) { method->SetEntryPointFromQuickCompiledCode(class_linker_->GetRuntimeQuickGenericJniStub()); } else { diff --git a/compiler/jni/quick/arm/calling_convention_arm.cc b/compiler/jni/quick/arm/calling_convention_arm.cc index 3f29ae5dcb..7e1ad9fd7b 100644 --- a/compiler/jni/quick/arm/calling_convention_arm.cc +++ b/compiler/jni/quick/arm/calling_convention_arm.cc @@ -14,8 +14,9 @@ * limitations under the License. */ -#include "base/logging.h" #include "calling_convention_arm.h" + +#include "base/logging.h" #include "handle_scope-inl.h" #include "utils/arm/managed_register_arm.h" diff --git a/compiler/jni/quick/arm64/calling_convention_arm64.cc b/compiler/jni/quick/arm64/calling_convention_arm64.cc index e086455620..292ce1039e 100644 --- a/compiler/jni/quick/arm64/calling_convention_arm64.cc +++ b/compiler/jni/quick/arm64/calling_convention_arm64.cc @@ -14,8 +14,9 @@ * limitations under the License. */ -#include "base/logging.h" #include "calling_convention_arm64.h" + +#include "base/logging.h" #include "handle_scope-inl.h" #include "utils/arm64/managed_register_arm64.h" diff --git a/compiler/jni/quick/jni_compiler.cc b/compiler/jni/quick/jni_compiler.cc index 68ec7bd860..b65b93f05f 100644 --- a/compiler/jni/quick/jni_compiler.cc +++ b/compiler/jni/quick/jni_compiler.cc @@ -17,36 +17,36 @@ #include "jni_compiler.h" #include <algorithm> +#include <fstream> #include <ios> #include <memory> #include <vector> -#include <fstream> #include "art_method.h" #include "base/arena_allocator.h" #include "base/enums.h" #include "base/logging.h" #include "base/macros.h" -#include "memory_region.h" #include "calling_convention.h" #include "class_linker.h" #include "compiled_method.h" +#include "debug/dwarf/debug_frame_opcode_writer.h" #include "dex_file-inl.h" #include "driver/compiler_driver.h" #include "driver/compiler_options.h" #include "entrypoints/quick/quick_entrypoints.h" #include "jni_env_ext.h" -#include "debug/dwarf/debug_frame_opcode_writer.h" +#include "memory_region.h" +#include "thread.h" +#include "utils.h" +#include "utils/arm/managed_register_arm.h" +#include "utils/arm64/managed_register_arm64.h" #include "utils/assembler.h" #include "utils/jni_macro_assembler.h" #include "utils/managed_register.h" -#include "utils/arm/managed_register_arm.h" -#include "utils/arm64/managed_register_arm64.h" #include "utils/mips/managed_register_mips.h" #include "utils/mips64/managed_register_mips64.h" #include "utils/x86/managed_register_x86.h" -#include "utils.h" -#include "thread.h" #define __ jni_asm-> diff --git a/compiler/linker/arm/relative_patcher_thumb2.cc b/compiler/linker/arm/relative_patcher_thumb2.cc index 18d6b9ad03..2ac2a1d2fc 100644 --- a/compiler/linker/arm/relative_patcher_thumb2.cc +++ b/compiler/linker/arm/relative_patcher_thumb2.cc @@ -22,8 +22,8 @@ #include "compiled_method.h" #include "entrypoints/quick/quick_entrypoints_enum.h" #include "lock_word.h" -#include "mirror/object.h" #include "mirror/array-inl.h" +#include "mirror/object.h" #include "read_barrier.h" #include "utils/arm/assembler_arm_vixl.h" diff --git a/compiler/linker/arm/relative_patcher_thumb2_test.cc b/compiler/linker/arm/relative_patcher_thumb2_test.cc index 52e27afcf5..fe76dfe39a 100644 --- a/compiler/linker/arm/relative_patcher_thumb2_test.cc +++ b/compiler/linker/arm/relative_patcher_thumb2_test.cc @@ -14,9 +14,10 @@ * limitations under the License. */ +#include "linker/arm/relative_patcher_thumb2.h" + #include "base/casts.h" #include "linker/relative_patcher_test.h" -#include "linker/arm/relative_patcher_thumb2.h" #include "lock_word.h" #include "mirror/array-inl.h" #include "mirror/object.h" diff --git a/compiler/linker/arm64/relative_patcher_arm64.cc b/compiler/linker/arm64/relative_patcher_arm64.cc index 38c732b8ba..db829f3233 100644 --- a/compiler/linker/arm64/relative_patcher_arm64.cc +++ b/compiler/linker/arm64/relative_patcher_arm64.cc @@ -25,8 +25,8 @@ #include "entrypoints/quick/quick_entrypoints_enum.h" #include "linker/output_stream.h" #include "lock_word.h" -#include "mirror/object.h" #include "mirror/array-inl.h" +#include "mirror/object.h" #include "oat.h" #include "oat_quick_method_header.h" #include "read_barrier.h" diff --git a/compiler/linker/arm64/relative_patcher_arm64_test.cc b/compiler/linker/arm64/relative_patcher_arm64_test.cc index 5d02d449fe..d6919e9417 100644 --- a/compiler/linker/arm64/relative_patcher_arm64_test.cc +++ b/compiler/linker/arm64/relative_patcher_arm64_test.cc @@ -14,9 +14,10 @@ * limitations under the License. */ +#include "linker/arm64/relative_patcher_arm64.h" + #include "base/casts.h" #include "linker/relative_patcher_test.h" -#include "linker/arm64/relative_patcher_arm64.h" #include "lock_word.h" #include "mirror/array-inl.h" #include "mirror/object.h" diff --git a/compiler/linker/mips/relative_patcher_mips.h b/compiler/linker/mips/relative_patcher_mips.h index 0b74bd33a4..d6eda34592 100644 --- a/compiler/linker/mips/relative_patcher_mips.h +++ b/compiler/linker/mips/relative_patcher_mips.h @@ -17,8 +17,8 @@ #ifndef ART_COMPILER_LINKER_MIPS_RELATIVE_PATCHER_MIPS_H_ #define ART_COMPILER_LINKER_MIPS_RELATIVE_PATCHER_MIPS_H_ -#include "linker/relative_patcher.h" #include "arch/mips/instruction_set_features_mips.h" +#include "linker/relative_patcher.h" namespace art { namespace linker { diff --git a/compiler/linker/mips/relative_patcher_mips32r6_test.cc b/compiler/linker/mips/relative_patcher_mips32r6_test.cc index d1a75e28a2..586e2aa8b2 100644 --- a/compiler/linker/mips/relative_patcher_mips32r6_test.cc +++ b/compiler/linker/mips/relative_patcher_mips32r6_test.cc @@ -14,8 +14,8 @@ * limitations under the License. */ -#include "linker/relative_patcher_test.h" #include "linker/mips/relative_patcher_mips.h" +#include "linker/relative_patcher_test.h" namespace art { namespace linker { diff --git a/compiler/linker/mips/relative_patcher_mips_test.cc b/compiler/linker/mips/relative_patcher_mips_test.cc index 2f7a0752a6..ebe5406512 100644 --- a/compiler/linker/mips/relative_patcher_mips_test.cc +++ b/compiler/linker/mips/relative_patcher_mips_test.cc @@ -14,9 +14,10 @@ * limitations under the License. */ -#include "linker/relative_patcher_test.h" #include "linker/mips/relative_patcher_mips.h" +#include "linker/relative_patcher_test.h" + namespace art { namespace linker { diff --git a/compiler/linker/mips64/relative_patcher_mips64_test.cc b/compiler/linker/mips64/relative_patcher_mips64_test.cc index a5f494d645..4edcae72f6 100644 --- a/compiler/linker/mips64/relative_patcher_mips64_test.cc +++ b/compiler/linker/mips64/relative_patcher_mips64_test.cc @@ -14,9 +14,10 @@ * limitations under the License. */ -#include "linker/relative_patcher_test.h" #include "linker/mips64/relative_patcher_mips64.h" +#include "linker/relative_patcher_test.h" + namespace art { namespace linker { diff --git a/compiler/linker/multi_oat_relative_patcher.cc b/compiler/linker/multi_oat_relative_patcher.cc index e9e242b658..4ae75d61c7 100644 --- a/compiler/linker/multi_oat_relative_patcher.cc +++ b/compiler/linker/multi_oat_relative_patcher.cc @@ -16,9 +16,9 @@ #include "multi_oat_relative_patcher.h" -#include "globals.h" #include "base/bit_utils.h" #include "base/logging.h" +#include "globals.h" namespace art { namespace linker { diff --git a/compiler/linker/multi_oat_relative_patcher_test.cc b/compiler/linker/multi_oat_relative_patcher_test.cc index 615b2b97be..e96790115a 100644 --- a/compiler/linker/multi_oat_relative_patcher_test.cc +++ b/compiler/linker/multi_oat_relative_patcher_test.cc @@ -14,9 +14,10 @@ * limitations under the License. */ +#include "multi_oat_relative_patcher.h" + #include "compiled_method.h" #include "gtest/gtest.h" -#include "multi_oat_relative_patcher.h" #include "vector_output_stream.h" namespace art { diff --git a/compiler/linker/output_stream_test.cc b/compiler/linker/output_stream_test.cc index 09fef29d48..87cb10000b 100644 --- a/compiler/linker/output_stream_test.cc +++ b/compiler/linker/output_stream_test.cc @@ -17,8 +17,8 @@ #include "file_output_stream.h" #include "vector_output_stream.h" -#include "base/unix_file/fd_file.h" #include "base/logging.h" +#include "base/unix_file/fd_file.h" #include "buffered_output_stream.h" #include "common_runtime_test.h" diff --git a/compiler/linker/vector_output_stream.h b/compiler/linker/vector_output_stream.h index 321014374e..a9b93e7a83 100644 --- a/compiler/linker/vector_output_stream.h +++ b/compiler/linker/vector_output_stream.h @@ -19,8 +19,8 @@ #include "output_stream.h" -#include <string> #include <string.h> +#include <string> #include <vector> namespace art { diff --git a/compiler/linker/x86/relative_patcher_x86_test.cc b/compiler/linker/x86/relative_patcher_x86_test.cc index 0bd9de8e15..4f74cee384 100644 --- a/compiler/linker/x86/relative_patcher_x86_test.cc +++ b/compiler/linker/x86/relative_patcher_x86_test.cc @@ -14,9 +14,10 @@ * limitations under the License. */ -#include "linker/relative_patcher_test.h" #include "linker/x86/relative_patcher_x86.h" +#include "linker/relative_patcher_test.h" + namespace art { namespace linker { diff --git a/compiler/linker/x86_64/relative_patcher_x86_64_test.cc b/compiler/linker/x86_64/relative_patcher_x86_64_test.cc index 6d6bb40fb4..ae17aa7a5f 100644 --- a/compiler/linker/x86_64/relative_patcher_x86_64_test.cc +++ b/compiler/linker/x86_64/relative_patcher_x86_64_test.cc @@ -14,9 +14,10 @@ * limitations under the License. */ -#include "linker/relative_patcher_test.h" #include "linker/x86_64/relative_patcher_x86_64.h" +#include "linker/relative_patcher_test.h" + namespace art { namespace linker { diff --git a/compiler/oat_test.cc b/compiler/oat_test.cc index 910d7a7c54..6f8904979d 100644 --- a/compiler/oat_test.cc +++ b/compiler/oat_test.cc @@ -104,8 +104,8 @@ class OatTest : public CommonCompilerTest { compiler_options_->ParseCompilerOption(option, Usage); } verification_results_.reset(new VerificationResults(compiler_options_.get())); - callbacks_.reset(new QuickCompilerCallbacks(verification_results_.get(), - CompilerCallbacks::CallbackMode::kCompileApp)); + callbacks_.reset(new QuickCompilerCallbacks(CompilerCallbacks::CallbackMode::kCompileApp)); + callbacks_->SetVerificationResults(verification_results_.get()); Runtime::Current()->SetCompilerCallbacks(callbacks_.get()); timer_.reset(new CumulativeLogger("Compilation times")); compiler_driver_.reset(new CompilerDriver(compiler_options_.get(), diff --git a/compiler/oat_writer.cc b/compiler/oat_writer.cc index 6120ed08ed..4d258af843 100644 --- a/compiler/oat_writer.cc +++ b/compiler/oat_writer.cc @@ -1116,6 +1116,7 @@ class OatWriter::InitImageMethodVisitor : public OatDexMethodVisitor { const std::vector<const DexFile*>* dex_files) : OatDexMethodVisitor(writer, offset), pointer_size_(GetInstructionSetPointerSize(writer_->compiler_driver_->GetInstructionSet())), + class_loader_(writer->HasImage() ? writer->image_writer_->GetClassLoader() : nullptr), dex_files_(dex_files), class_linker_(Runtime::Current()->GetClassLinker()) {} @@ -1131,10 +1132,7 @@ class OatWriter::InitImageMethodVisitor : public OatDexMethodVisitor { if (!IsImageClass()) { return true; } - ScopedObjectAccessUnchecked soa(Thread::Current()); - StackHandleScope<1> hs(soa.Self()); - Handle<mirror::DexCache> dex_cache = hs.NewHandle( - class_linker_->FindDexCache(Thread::Current(), *dex_file)); + ObjPtr<mirror::DexCache> dex_cache = class_linker_->FindDexCache(Thread::Current(), *dex_file); const DexFile::ClassDef& class_def = dex_file->GetClassDef(class_def_index); mirror::Class* klass = dex_cache->GetResolvedType(class_def.class_idx_); if (klass != nullptr) { @@ -1143,11 +1141,13 @@ class OatWriter::InitImageMethodVisitor : public OatDexMethodVisitor { // in the copied method should be the same as in the origin // method. mirror::Class* declaring_class = method.GetDeclaringClass(); - ArtMethod* origin = declaring_class->FindDeclaredVirtualMethod( + ArtMethod* origin = declaring_class->FindClassMethod( declaring_class->GetDexCache(), method.GetDexMethodIndex(), pointer_size_); CHECK(origin != nullptr); + CHECK(!origin->IsDirect()); + CHECK(origin->GetDeclaringClass() == declaring_class); if (IsInOatFile(&declaring_class->GetDexFile())) { const void* code_ptr = origin->GetEntryPointFromQuickCompiledCodePtrSize(pointer_size_); @@ -1180,36 +1180,36 @@ class OatWriter::InitImageMethodVisitor : public OatDexMethodVisitor { ++method_offsets_index_; } - // Unchecked as we hold mutator_lock_ on entry. - ScopedObjectAccessUnchecked soa(Thread::Current()); - StackHandleScope<1> hs(soa.Self()); - Handle<mirror::DexCache> dex_cache(hs.NewHandle(class_linker_->FindDexCache( - Thread::Current(), *dex_file_))); + Thread* self = Thread::Current(); + ObjPtr<mirror::DexCache> dex_cache = class_linker_->FindDexCache(self, *dex_file_); ArtMethod* method; if (writer_->HasBootImage()) { const InvokeType invoke_type = it.GetMethodInvokeType( dex_file_->GetClassDef(class_def_index_)); - method = class_linker_->ResolveMethod<ClassLinker::kNoICCECheckForCache>( + // Unchecked as we hold mutator_lock_ on entry. + ScopedObjectAccessUnchecked soa(self); + StackHandleScope<1> hs(self); + method = class_linker_->ResolveMethod<ClassLinker::ResolveMode::kNoChecks>( *dex_file_, it.GetMemberIndex(), - dex_cache, + hs.NewHandle(dex_cache), ScopedNullHandle<mirror::ClassLoader>(), nullptr, invoke_type); if (method == nullptr) { LOG(FATAL_WITHOUT_ABORT) << "Unexpected failure to resolve a method: " << dex_file_->PrettyMethod(it.GetMemberIndex(), true); - soa.Self()->AssertPendingException(); - mirror::Throwable* exc = soa.Self()->GetException(); + self->AssertPendingException(); + mirror::Throwable* exc = self->GetException(); std::string dump = exc->Dump(); LOG(FATAL) << dump; UNREACHABLE(); } } else { - // Should already have been resolved by the compiler, just peek into the dex cache. + // Should already have been resolved by the compiler. // It may not be resolved if the class failed to verify, in this case, don't set the - // entrypoint. This is not fatal since the dex cache will contain a resolution method. - method = dex_cache->GetResolvedMethod(it.GetMemberIndex(), pointer_size_); + // entrypoint. This is not fatal since we shall use a resolution method. + method = class_linker_->LookupResolvedMethod(it.GetMemberIndex(), dex_cache, class_loader_); } if (method != nullptr && compiled_method != nullptr && @@ -1250,6 +1250,7 @@ class OatWriter::InitImageMethodVisitor : public OatDexMethodVisitor { private: const PointerSize pointer_size_; + ObjPtr<mirror::ClassLoader> class_loader_; const std::vector<const DexFile*>* dex_files_; ClassLinker* const class_linker_; std::vector<std::pair<ArtMethod*, ArtMethod*>> methods_to_process_; @@ -1469,7 +1470,8 @@ class OatWriter::WriteCodeMethodVisitor : public OatDexMethodVisitor { ObjPtr<mirror::DexCache> dex_cache = (dex_file_ == ref.dex_file) ? dex_cache_ : class_linker_->FindDexCache( Thread::Current(), *ref.dex_file); - ArtMethod* method = dex_cache->GetResolvedMethod(ref.dex_method_index, pointer_size_); + ArtMethod* method = + class_linker_->LookupResolvedMethod(ref.dex_method_index, dex_cache, class_loader_); CHECK(method != nullptr); return method; } diff --git a/compiler/optimizing/bounds_check_elimination.cc b/compiler/optimizing/bounds_check_elimination.cc index f3ecdf036a..c166deb406 100644 --- a/compiler/optimizing/bounds_check_elimination.cc +++ b/compiler/optimizing/bounds_check_elimination.cc @@ -20,8 +20,8 @@ #include "base/arena_containers.h" #include "induction_var_range.h" -#include "side_effects_analysis.h" #include "nodes.h" +#include "side_effects_analysis.h" namespace art { diff --git a/compiler/optimizing/bounds_check_elimination_test.cc b/compiler/optimizing/bounds_check_elimination_test.cc index a949c33149..575e2fc24a 100644 --- a/compiler/optimizing/bounds_check_elimination_test.cc +++ b/compiler/optimizing/bounds_check_elimination_test.cc @@ -14,8 +14,9 @@ * limitations under the License. */ -#include "base/arena_allocator.h" #include "bounds_check_elimination.h" + +#include "base/arena_allocator.h" #include "builder.h" #include "gvn.h" #include "induction_var_analysis.h" diff --git a/compiler/optimizing/builder.h b/compiler/optimizing/builder.h index 3a4c9dbd16..43429cf3d2 100644 --- a/compiler/optimizing/builder.h +++ b/compiler/optimizing/builder.h @@ -20,14 +20,14 @@ #include "base/arena_containers.h" #include "base/arena_object.h" #include "block_builder.h" -#include "dex_file.h" #include "dex_file-inl.h" +#include "dex_file.h" #include "driver/compiler_driver.h" #include "driver/dex_compilation_unit.h" #include "instruction_builder.h" +#include "nodes.h" #include "optimizing_compiler_stats.h" #include "primitive.h" -#include "nodes.h" #include "ssa_builder.h" namespace art { diff --git a/compiler/optimizing/code_generator.cc b/compiler/optimizing/code_generator.cc index 2872cf7458..d7d0ffffda 100644 --- a/compiler/optimizing/code_generator.cc +++ b/compiler/optimizing/code_generator.cc @@ -57,8 +57,8 @@ #include "mirror/reference.h" #include "mirror/string.h" #include "parallel_move_resolver.h" -#include "ssa_liveness_analysis.h" #include "scoped_thread_state_change-inl.h" +#include "ssa_liveness_analysis.h" #include "thread-current-inl.h" #include "utils/assembler.h" diff --git a/compiler/optimizing/code_generator.h b/compiler/optimizing/code_generator.h index 73202b4fd1..51a0bae799 100644 --- a/compiler/optimizing/code_generator.h +++ b/compiler/optimizing/code_generator.h @@ -446,6 +446,16 @@ class CodeGenerator : public DeletableArenaObject<kArenaAllocCodeGenerator> { return GetFrameSize() == (CallPushesPC() ? GetWordSize() : 0); } + static int8_t GetInt8ValueOf(HConstant* constant) { + DCHECK(constant->IsIntConstant()); + return constant->AsIntConstant()->GetValue(); + } + + static int16_t GetInt16ValueOf(HConstant* constant) { + DCHECK(constant->IsIntConstant()); + return constant->AsIntConstant()->GetValue(); + } + static int32_t GetInt32ValueOf(HConstant* constant) { if (constant->IsIntConstant()) { return constant->AsIntConstant()->GetValue(); diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc index 7e5b1a0fd1..4999950600 100644 --- a/compiler/optimizing/code_generator_arm64.cc +++ b/compiler/optimizing/code_generator_arm64.cc @@ -29,9 +29,9 @@ #include "intrinsics.h" #include "intrinsics_arm64.h" #include "linker/arm64/relative_patcher_arm64.h" +#include "lock_word.h" #include "mirror/array-inl.h" #include "mirror/class-inl.h" -#include "lock_word.h" #include "offsets.h" #include "thread.h" #include "utils/arm64/assembler_arm64.h" diff --git a/compiler/optimizing/code_generator_arm_vixl.cc b/compiler/optimizing/code_generator_arm_vixl.cc index b9d4700511..430cdde1f7 100644 --- a/compiler/optimizing/code_generator_arm_vixl.cc +++ b/compiler/optimizing/code_generator_arm_vixl.cc @@ -8269,19 +8269,41 @@ void InstructionCodeGeneratorARMVIXL::VisitDataProcWithShifterOp( const HDataProcWithShifterOp::OpKind op_kind = instruction->GetOpKind(); if (instruction->GetType() == Primitive::kPrimInt) { - DCHECK(!HDataProcWithShifterOp::IsExtensionOp(op_kind)); - + const vixl32::Register first = InputRegisterAt(instruction, 0); + const vixl32::Register output = OutputRegister(instruction); const vixl32::Register second = instruction->InputAt(1)->GetType() == Primitive::kPrimLong ? LowRegisterFrom(locations->InAt(1)) : InputRegisterAt(instruction, 1); - GenerateDataProcInstruction(kind, - OutputRegister(instruction), - InputRegisterAt(instruction, 0), - Operand(second, - ShiftFromOpKind(op_kind), - instruction->GetShiftAmount()), - codegen_); + if (HDataProcWithShifterOp::IsExtensionOp(op_kind)) { + DCHECK_EQ(kind, HInstruction::kAdd); + + switch (op_kind) { + case HDataProcWithShifterOp::kUXTB: + __ Uxtab(output, first, second); + break; + case HDataProcWithShifterOp::kUXTH: + __ Uxtah(output, first, second); + break; + case HDataProcWithShifterOp::kSXTB: + __ Sxtab(output, first, second); + break; + case HDataProcWithShifterOp::kSXTH: + __ Sxtah(output, first, second); + break; + default: + LOG(FATAL) << "Unexpected operation kind: " << op_kind; + UNREACHABLE(); + } + } else { + GenerateDataProcInstruction(kind, + output, + first, + Operand(second, + ShiftFromOpKind(op_kind), + instruction->GetShiftAmount()), + codegen_); + } } else { DCHECK_EQ(instruction->GetType(), Primitive::kPrimLong); diff --git a/compiler/optimizing/code_generator_arm_vixl.h b/compiler/optimizing/code_generator_arm_vixl.h index 01cf287f29..7ab2993161 100644 --- a/compiler/optimizing/code_generator_arm_vixl.h +++ b/compiler/optimizing/code_generator_arm_vixl.h @@ -22,8 +22,8 @@ #include "common_arm.h" #include "driver/compiler_options.h" #include "nodes.h" -#include "string_reference.h" #include "parallel_move_resolver.h" +#include "string_reference.h" #include "type_reference.h" #include "utils/arm/assembler_arm_vixl.h" diff --git a/compiler/optimizing/code_generator_mips.cc b/compiler/optimizing/code_generator_mips.cc index 23d188d630..b6eb5c1d1d 100644 --- a/compiler/optimizing/code_generator_mips.cc +++ b/compiler/optimizing/code_generator_mips.cc @@ -16,6 +16,7 @@ #include "code_generator_mips.h" +#include "arch/mips/asm_support_mips.h" #include "arch/mips/entrypoints_direct_mips.h" #include "arch/mips/instruction_set_features_mips.h" #include "art_method.h" @@ -40,6 +41,11 @@ namespace mips { static constexpr int kCurrentMethodStackOffset = 0; static constexpr Register kMethodRegisterArgument = A0; +// Flags controlling the use of thunks for Baker read barriers. +constexpr bool kBakerReadBarrierThunksEnableForFields = true; +constexpr bool kBakerReadBarrierThunksEnableForArrays = true; +constexpr bool kBakerReadBarrierThunksEnableForGcRoots = true; + Location MipsReturnLocation(Primitive::Type return_type) { switch (return_type) { case Primitive::kPrimBoolean: @@ -1486,7 +1492,8 @@ void CodeGeneratorMIPS::MoveLocation(Location destination, __ Mfc1(dst_low, src); __ MoveFromFpuHigh(dst_high, src); } else { - DCHECK(source.IsDoubleStackSlot()) << "Cannot move from " << source << " to " << destination; + DCHECK(source.IsDoubleStackSlot()) + << "Cannot move from " << source << " to " << destination; int32_t off = source.GetStackIndex(); Register r = destination.AsRegisterPairLow<Register>(); __ LoadFromOffset(kLoadDoubleword, r, SP, off); @@ -1539,7 +1546,8 @@ void CodeGeneratorMIPS::MoveLocation(Location destination, } else if (source.IsFpuRegister()) { __ StoreDToOffset(source.AsFpuRegister<FRegister>(), SP, dst_offset); } else { - DCHECK(source.IsDoubleStackSlot()) << "Cannot move from " << source << " to " << destination; + DCHECK(source.IsDoubleStackSlot()) + << "Cannot move from " << source << " to " << destination; __ LoadFromOffset(kLoadWord, TMP, SP, source.GetStackIndex()); __ StoreToOffset(kStoreWord, TMP, SP, dst_offset); __ LoadFromOffset(kLoadWord, TMP, SP, source.GetStackIndex() + 4); @@ -1763,8 +1771,10 @@ void CodeGeneratorMIPS::EmitPcRelativeAddressPlaceholderHigh(PcRelativePatchInfo } // A following instruction will add the sign-extended low half of the 32-bit // offset to `out` (e.g. lw, jialc, addiu). - DCHECK_EQ(info_low->patch_info_high, info_high); - __ Bind(&info_low->label); + if (info_low != nullptr) { + DCHECK_EQ(info_low->patch_info_high, info_high); + __ Bind(&info_low->label); + } } CodeGeneratorMIPS::JitPatchInfo* CodeGeneratorMIPS::NewJitRootStringPatch( @@ -1791,25 +1801,26 @@ void CodeGeneratorMIPS::PatchJitRootUse(uint8_t* code, const uint8_t* roots_data, const CodeGeneratorMIPS::JitPatchInfo& info, uint64_t index_in_table) const { - uint32_t literal_offset = GetAssembler().GetLabelLocation(&info.high_label); + uint32_t high_literal_offset = GetAssembler().GetLabelLocation(&info.high_label); + uint32_t low_literal_offset = GetAssembler().GetLabelLocation(&info.low_label); uintptr_t address = reinterpret_cast<uintptr_t>(roots_data) + index_in_table * sizeof(GcRoot<mirror::Object>); uint32_t addr32 = dchecked_integral_cast<uint32_t>(address); // lui reg, addr32_high - DCHECK_EQ(code[literal_offset + 0], 0x34); - DCHECK_EQ(code[literal_offset + 1], 0x12); - DCHECK_EQ((code[literal_offset + 2] & 0xE0), 0x00); - DCHECK_EQ(code[literal_offset + 3], 0x3C); + DCHECK_EQ(code[high_literal_offset + 0], 0x34); + DCHECK_EQ(code[high_literal_offset + 1], 0x12); + DCHECK_EQ((code[high_literal_offset + 2] & 0xE0), 0x00); + DCHECK_EQ(code[high_literal_offset + 3], 0x3C); // instr reg, reg, addr32_low - DCHECK_EQ(code[literal_offset + 4], 0x78); - DCHECK_EQ(code[literal_offset + 5], 0x56); + DCHECK_EQ(code[low_literal_offset + 0], 0x78); + DCHECK_EQ(code[low_literal_offset + 1], 0x56); addr32 += (addr32 & 0x8000) << 1; // Account for sign extension in "instr reg, reg, addr32_low". // lui reg, addr32_high - code[literal_offset + 0] = static_cast<uint8_t>(addr32 >> 16); - code[literal_offset + 1] = static_cast<uint8_t>(addr32 >> 24); + code[high_literal_offset + 0] = static_cast<uint8_t>(addr32 >> 16); + code[high_literal_offset + 1] = static_cast<uint8_t>(addr32 >> 24); // instr reg, reg, addr32_low - code[literal_offset + 4] = static_cast<uint8_t>(addr32 >> 0); - code[literal_offset + 5] = static_cast<uint8_t>(addr32 >> 8); + code[low_literal_offset + 0] = static_cast<uint8_t>(addr32 >> 0); + code[low_literal_offset + 1] = static_cast<uint8_t>(addr32 >> 8); } void CodeGeneratorMIPS::EmitJitRootPatches(uint8_t* code, const uint8_t* roots_data) { @@ -2545,7 +2556,12 @@ void LocationsBuilderMIPS::VisitArrayGet(HArrayGet* instruction) { // We need a temporary register for the read barrier marking slow // path in CodeGeneratorMIPS::GenerateArrayLoadWithBakerReadBarrier. if (object_array_get_with_read_barrier && kUseBakerReadBarrier) { - locations->AddTemp(Location::RequiresRegister()); + bool temp_needed = instruction->GetIndex()->IsConstant() + ? !kBakerReadBarrierThunksEnableForFields + : !kBakerReadBarrierThunksEnableForArrays; + if (temp_needed) { + locations->AddTemp(Location::RequiresRegister()); + } } } @@ -2681,16 +2697,32 @@ void InstructionCodeGeneratorMIPS::VisitArrayGet(HArrayGet* instruction) { // /* HeapReference<Object> */ out = // *(obj + data_offset + index * sizeof(HeapReference<Object>)) if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) { - Location temp = locations->GetTemp(0); + bool temp_needed = index.IsConstant() + ? !kBakerReadBarrierThunksEnableForFields + : !kBakerReadBarrierThunksEnableForArrays; + Location temp = temp_needed ? locations->GetTemp(0) : Location::NoLocation(); // Note that a potential implicit null check is handled in this // CodeGeneratorMIPS::GenerateArrayLoadWithBakerReadBarrier call. - codegen_->GenerateArrayLoadWithBakerReadBarrier(instruction, - out_loc, - obj, - data_offset, - index, - temp, - /* needs_null_check */ true); + DCHECK(!instruction->CanDoImplicitNullCheckOn(instruction->InputAt(0))); + if (index.IsConstant()) { + // Array load with a constant index can be treated as a field load. + size_t offset = + (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset; + codegen_->GenerateFieldLoadWithBakerReadBarrier(instruction, + out_loc, + obj, + offset, + temp, + /* needs_null_check */ false); + } else { + codegen_->GenerateArrayLoadWithBakerReadBarrier(instruction, + out_loc, + obj, + data_offset, + index, + temp, + /* needs_null_check */ false); + } } else { Register out = out_loc.AsRegister<Register>(); if (index.IsConstant()) { @@ -3093,6 +3125,7 @@ void InstructionCodeGeneratorMIPS::VisitBoundsCheck(HBoundsCheck* instruction) { // Temp is used for read barrier. static size_t NumberOfInstanceOfTemps(TypeCheckKind type_check_kind) { if (kEmitCompilerReadBarrier && + !(kUseBakerReadBarrier && kBakerReadBarrierThunksEnableForFields) && (kUseBakerReadBarrier || type_check_kind == TypeCheckKind::kAbstractClassCheck || type_check_kind == TypeCheckKind::kClassHierarchyCheck || @@ -6096,7 +6129,9 @@ void LocationsBuilderMIPS::HandleFieldGet(HInstruction* instruction, const Field if (object_field_get_with_read_barrier && kUseBakerReadBarrier) { // We need a temporary register for the read barrier marking slow // path in CodeGeneratorMIPS::GenerateFieldLoadWithBakerReadBarrier. - locations->AddTemp(Location::RequiresRegister()); + if (!kBakerReadBarrierThunksEnableForFields) { + locations->AddTemp(Location::RequiresRegister()); + } } } } @@ -6171,7 +6206,8 @@ void InstructionCodeGeneratorMIPS::HandleFieldGet(HInstruction* instruction, if (type == Primitive::kPrimNot) { // /* HeapReference<Object> */ dst = *(obj + offset) if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) { - Location temp_loc = locations->GetTemp(0); + Location temp_loc = + kBakerReadBarrierThunksEnableForFields ? Location::NoLocation() : locations->GetTemp(0); // Note that a potential implicit null check is handled in this // CodeGeneratorMIPS::GenerateFieldLoadWithBakerReadBarrier call. codegen_->GenerateFieldLoadWithBakerReadBarrier(instruction, @@ -6395,7 +6431,9 @@ void InstructionCodeGeneratorMIPS::GenerateReferenceLoadOneRegister( Register out_reg = out.AsRegister<Register>(); if (read_barrier_option == kWithReadBarrier) { CHECK(kEmitCompilerReadBarrier); - DCHECK(maybe_temp.IsRegister()) << maybe_temp; + if (!kUseBakerReadBarrier || !kBakerReadBarrierThunksEnableForFields) { + DCHECK(maybe_temp.IsRegister()) << maybe_temp; + } if (kUseBakerReadBarrier) { // Load with fast path based Baker's read barrier. // /* HeapReference<Object> */ out = *(out + offset) @@ -6435,7 +6473,9 @@ void InstructionCodeGeneratorMIPS::GenerateReferenceLoadTwoRegisters( if (read_barrier_option == kWithReadBarrier) { CHECK(kEmitCompilerReadBarrier); if (kUseBakerReadBarrier) { - DCHECK(maybe_temp.IsRegister()) << maybe_temp; + if (!kBakerReadBarrierThunksEnableForFields) { + DCHECK(maybe_temp.IsRegister()) << maybe_temp; + } // Load with fast path based Baker's read barrier. // /* HeapReference<Object> */ out = *(obj + offset) codegen_->GenerateFieldLoadWithBakerReadBarrier(instruction, @@ -6458,67 +6498,172 @@ void InstructionCodeGeneratorMIPS::GenerateReferenceLoadTwoRegisters( } } +static inline int GetBakerMarkThunkNumber(Register reg) { + static_assert(BAKER_MARK_INTROSPECTION_REGISTER_COUNT == 21, "Expecting equal"); + if (reg >= V0 && reg <= T7) { // 14 consequtive regs. + return reg - V0; + } else if (reg >= S2 && reg <= S7) { // 6 consequtive regs. + return 14 + (reg - S2); + } else if (reg == FP) { // One more. + return 20; + } + LOG(FATAL) << "Unexpected register " << reg; + UNREACHABLE(); +} + +static inline int GetBakerMarkFieldArrayThunkDisplacement(Register reg, bool short_offset) { + int num = GetBakerMarkThunkNumber(reg) + + (short_offset ? BAKER_MARK_INTROSPECTION_REGISTER_COUNT : 0); + return num * BAKER_MARK_INTROSPECTION_FIELD_ARRAY_ENTRY_SIZE; +} + +static inline int GetBakerMarkGcRootThunkDisplacement(Register reg) { + return GetBakerMarkThunkNumber(reg) * BAKER_MARK_INTROSPECTION_GC_ROOT_ENTRY_SIZE + + BAKER_MARK_INTROSPECTION_GC_ROOT_ENTRIES_OFFSET; +} + void InstructionCodeGeneratorMIPS::GenerateGcRootFieldLoad(HInstruction* instruction, Location root, Register obj, uint32_t offset, - ReadBarrierOption read_barrier_option) { + ReadBarrierOption read_barrier_option, + MipsLabel* label_low) { + bool reordering; + if (label_low != nullptr) { + DCHECK_EQ(offset, 0x5678u); + } Register root_reg = root.AsRegister<Register>(); if (read_barrier_option == kWithReadBarrier) { DCHECK(kEmitCompilerReadBarrier); if (kUseBakerReadBarrier) { // Fast path implementation of art::ReadBarrier::BarrierForRoot when // Baker's read barrier are used: - // - // root = obj.field; - // temp = Thread::Current()->pReadBarrierMarkReg ## root.reg() - // if (temp != null) { - // root = temp(root) - // } - - // /* GcRoot<mirror::Object> */ root = *(obj + offset) - __ LoadFromOffset(kLoadWord, root_reg, obj, offset); - static_assert( - sizeof(mirror::CompressedReference<mirror::Object>) == sizeof(GcRoot<mirror::Object>), - "art::mirror::CompressedReference<mirror::Object> and art::GcRoot<mirror::Object> " - "have different sizes."); - static_assert(sizeof(mirror::CompressedReference<mirror::Object>) == sizeof(int32_t), - "art::mirror::CompressedReference<mirror::Object> and int32_t " - "have different sizes."); - - // Slow path marking the GC root `root`. - Location temp = Location::RegisterLocation(T9); - SlowPathCodeMIPS* slow_path = - new (GetGraph()->GetArena()) ReadBarrierMarkSlowPathMIPS( - instruction, - root, - /*entrypoint*/ temp); - codegen_->AddSlowPath(slow_path); + if (kBakerReadBarrierThunksEnableForGcRoots) { + // Note that we do not actually check the value of `GetIsGcMarking()` + // to decide whether to mark the loaded GC root or not. Instead, we + // load into `temp` (T9) the read barrier mark introspection entrypoint. + // If `temp` is null, it means that `GetIsGcMarking()` is false, and + // vice versa. + // + // We use thunks for the slow path. That thunk checks the reference + // and jumps to the entrypoint if needed. + // + // temp = Thread::Current()->pReadBarrierMarkReg00 + // // AKA &art_quick_read_barrier_mark_introspection. + // GcRoot<mirror::Object> root = *(obj+offset); // Original reference load. + // if (temp != nullptr) { + // temp = &gc_root_thunk<root_reg> + // root = temp(root) + // } + + bool isR6 = codegen_->GetInstructionSetFeatures().IsR6(); + const int32_t entry_point_offset = + Thread::ReadBarrierMarkEntryPointsOffset<kMipsPointerSize>(0); + const int thunk_disp = GetBakerMarkGcRootThunkDisplacement(root_reg); + int16_t offset_low = Low16Bits(offset); + int16_t offset_high = High16Bits(offset - offset_low); // Accounts for sign + // extension in lw. + bool short_offset = IsInt<16>(static_cast<int32_t>(offset)); + Register base = short_offset ? obj : TMP; + // Loading the entrypoint does not require a load acquire since it is only changed when + // threads are suspended or running a checkpoint. + __ LoadFromOffset(kLoadWord, T9, TR, entry_point_offset); + reordering = __ SetReorder(false); + if (!short_offset) { + DCHECK(!label_low); + __ AddUpper(base, obj, offset_high); + } + __ Beqz(T9, (isR6 ? 2 : 4)); // Skip jialc / addiu+jalr+nop. + if (label_low != nullptr) { + DCHECK(short_offset); + __ Bind(label_low); + } + // /* GcRoot<mirror::Object> */ root = *(obj + offset) + __ LoadFromOffset(kLoadWord, root_reg, base, offset_low); // Single instruction + // in delay slot. + if (isR6) { + __ Jialc(T9, thunk_disp); + } else { + __ Addiu(T9, T9, thunk_disp); + __ Jalr(T9); + __ Nop(); + } + __ SetReorder(reordering); + } else { + // Note that we do not actually check the value of `GetIsGcMarking()` + // to decide whether to mark the loaded GC root or not. Instead, we + // load into `temp` (T9) the read barrier mark entry point corresponding + // to register `root`. If `temp` is null, it means that `GetIsGcMarking()` + // is false, and vice versa. + // + // GcRoot<mirror::Object> root = *(obj+offset); // Original reference load. + // temp = Thread::Current()->pReadBarrierMarkReg ## root.reg() + // if (temp != null) { + // root = temp(root) + // } + + if (label_low != nullptr) { + reordering = __ SetReorder(false); + __ Bind(label_low); + } + // /* GcRoot<mirror::Object> */ root = *(obj + offset) + __ LoadFromOffset(kLoadWord, root_reg, obj, offset); + if (label_low != nullptr) { + __ SetReorder(reordering); + } + static_assert( + sizeof(mirror::CompressedReference<mirror::Object>) == sizeof(GcRoot<mirror::Object>), + "art::mirror::CompressedReference<mirror::Object> and art::GcRoot<mirror::Object> " + "have different sizes."); + static_assert(sizeof(mirror::CompressedReference<mirror::Object>) == sizeof(int32_t), + "art::mirror::CompressedReference<mirror::Object> and int32_t " + "have different sizes."); + + // Slow path marking the GC root `root`. + Location temp = Location::RegisterLocation(T9); + SlowPathCodeMIPS* slow_path = + new (GetGraph()->GetArena()) ReadBarrierMarkSlowPathMIPS( + instruction, + root, + /*entrypoint*/ temp); + codegen_->AddSlowPath(slow_path); - // temp = Thread::Current()->pReadBarrierMarkReg ## root.reg() - const int32_t entry_point_offset = - Thread::ReadBarrierMarkEntryPointsOffset<kMipsPointerSize>(root.reg() - 1); - // Loading the entrypoint does not require a load acquire since it is only changed when - // threads are suspended or running a checkpoint. - __ LoadFromOffset(kLoadWord, temp.AsRegister<Register>(), TR, entry_point_offset); - // The entrypoint is null when the GC is not marking, this prevents one load compared to - // checking GetIsGcMarking. - __ Bnez(temp.AsRegister<Register>(), slow_path->GetEntryLabel()); - __ Bind(slow_path->GetExitLabel()); + const int32_t entry_point_offset = + Thread::ReadBarrierMarkEntryPointsOffset<kMipsPointerSize>(root.reg() - 1); + // Loading the entrypoint does not require a load acquire since it is only changed when + // threads are suspended or running a checkpoint. + __ LoadFromOffset(kLoadWord, temp.AsRegister<Register>(), TR, entry_point_offset); + __ Bnez(temp.AsRegister<Register>(), slow_path->GetEntryLabel()); + __ Bind(slow_path->GetExitLabel()); + } } else { + if (label_low != nullptr) { + reordering = __ SetReorder(false); + __ Bind(label_low); + } // GC root loaded through a slow path for read barriers other // than Baker's. // /* GcRoot<mirror::Object>* */ root = obj + offset __ Addiu32(root_reg, obj, offset); + if (label_low != nullptr) { + __ SetReorder(reordering); + } // /* mirror::Object* */ root = root->Read() codegen_->GenerateReadBarrierForRootSlow(instruction, root, root); } } else { + if (label_low != nullptr) { + reordering = __ SetReorder(false); + __ Bind(label_low); + } // Plain GC root load with no read barrier. // /* GcRoot<mirror::Object> */ root = *(obj + offset) __ LoadFromOffset(kLoadWord, root_reg, obj, offset); // Note that GC roots are not affected by heap poisoning, thus we // do not have to unpoison `root_reg` here. + if (label_low != nullptr) { + __ SetReorder(reordering); + } } } @@ -6531,6 +6676,88 @@ void CodeGeneratorMIPS::GenerateFieldLoadWithBakerReadBarrier(HInstruction* inst DCHECK(kEmitCompilerReadBarrier); DCHECK(kUseBakerReadBarrier); + if (kBakerReadBarrierThunksEnableForFields) { + // Note that we do not actually check the value of `GetIsGcMarking()` + // to decide whether to mark the loaded reference or not. Instead, we + // load into `temp` (T9) the read barrier mark introspection entrypoint. + // If `temp` is null, it means that `GetIsGcMarking()` is false, and + // vice versa. + // + // We use thunks for the slow path. That thunk checks the reference + // and jumps to the entrypoint if needed. If the holder is not gray, + // it issues a load-load memory barrier and returns to the original + // reference load. + // + // temp = Thread::Current()->pReadBarrierMarkReg00 + // // AKA &art_quick_read_barrier_mark_introspection. + // if (temp != nullptr) { + // temp = &field_array_thunk<holder_reg> + // temp() + // } + // not_gray_return_address: + // // If the offset is too large to fit into the lw instruction, we + // // use an adjusted base register (TMP) here. This register + // // receives bits 16 ... 31 of the offset before the thunk invocation + // // and the thunk benefits from it. + // HeapReference<mirror::Object> reference = *(obj+offset); // Original reference load. + // gray_return_address: + + DCHECK(temp.IsInvalid()); + bool isR6 = GetInstructionSetFeatures().IsR6(); + int16_t offset_low = Low16Bits(offset); + int16_t offset_high = High16Bits(offset - offset_low); // Accounts for sign extension in lw. + bool short_offset = IsInt<16>(static_cast<int32_t>(offset)); + bool reordering = __ SetReorder(false); + const int32_t entry_point_offset = + Thread::ReadBarrierMarkEntryPointsOffset<kMipsPointerSize>(0); + // There may have or may have not been a null check if the field offset is smaller than + // the page size. + // There must've been a null check in case it's actually a load from an array. + // We will, however, perform an explicit null check in the thunk as it's easier to + // do it than not. + if (instruction->IsArrayGet()) { + DCHECK(!needs_null_check); + } + const int thunk_disp = GetBakerMarkFieldArrayThunkDisplacement(obj, short_offset); + // Loading the entrypoint does not require a load acquire since it is only changed when + // threads are suspended or running a checkpoint. + __ LoadFromOffset(kLoadWord, T9, TR, entry_point_offset); + Register ref_reg = ref.AsRegister<Register>(); + Register base = short_offset ? obj : TMP; + if (short_offset) { + if (isR6) { + __ Beqzc(T9, 2); // Skip jialc. + __ Nop(); // In forbidden slot. + __ Jialc(T9, thunk_disp); + } else { + __ Beqz(T9, 3); // Skip jalr+nop. + __ Addiu(T9, T9, thunk_disp); // In delay slot. + __ Jalr(T9); + __ Nop(); // In delay slot. + } + } else { + if (isR6) { + __ Beqz(T9, 2); // Skip jialc. + __ Aui(base, obj, offset_high); // In delay slot. + __ Jialc(T9, thunk_disp); + } else { + __ Lui(base, offset_high); + __ Beqz(T9, 2); // Skip jalr. + __ Addiu(T9, T9, thunk_disp); // In delay slot. + __ Jalr(T9); + __ Addu(base, base, obj); // In delay slot. + } + } + // /* HeapReference<Object> */ ref = *(obj + offset) + __ LoadFromOffset(kLoadWord, ref_reg, base, offset_low); // Single instruction. + if (needs_null_check) { + MaybeRecordImplicitNullCheck(instruction); + } + __ MaybeUnpoisonHeapReference(ref_reg); + __ SetReorder(reordering); + return; + } + // /* HeapReference<Object> */ ref = *(obj + offset) Location no_index = Location::NoLocation(); ScaleFactor no_scale_factor = TIMES_1; @@ -6557,9 +6784,69 @@ void CodeGeneratorMIPS::GenerateArrayLoadWithBakerReadBarrier(HInstruction* inst static_assert( sizeof(mirror::HeapReference<mirror::Object>) == sizeof(int32_t), "art::mirror::HeapReference<art::mirror::Object> and int32_t have different sizes."); + ScaleFactor scale_factor = TIMES_4; + + if (kBakerReadBarrierThunksEnableForArrays) { + // Note that we do not actually check the value of `GetIsGcMarking()` + // to decide whether to mark the loaded reference or not. Instead, we + // load into `temp` (T9) the read barrier mark introspection entrypoint. + // If `temp` is null, it means that `GetIsGcMarking()` is false, and + // vice versa. + // + // We use thunks for the slow path. That thunk checks the reference + // and jumps to the entrypoint if needed. If the holder is not gray, + // it issues a load-load memory barrier and returns to the original + // reference load. + // + // temp = Thread::Current()->pReadBarrierMarkReg00 + // // AKA &art_quick_read_barrier_mark_introspection. + // if (temp != nullptr) { + // temp = &field_array_thunk<holder_reg> + // temp() + // } + // not_gray_return_address: + // // The element address is pre-calculated in the TMP register before the + // // thunk invocation and the thunk benefits from it. + // HeapReference<mirror::Object> reference = data[index]; // Original reference load. + // gray_return_address: + + DCHECK(temp.IsInvalid()); + DCHECK(index.IsValid()); + bool reordering = __ SetReorder(false); + const int32_t entry_point_offset = + Thread::ReadBarrierMarkEntryPointsOffset<kMipsPointerSize>(0); + // We will not do the explicit null check in the thunk as some form of a null check + // must've been done earlier. + DCHECK(!needs_null_check); + const int thunk_disp = GetBakerMarkFieldArrayThunkDisplacement(obj, /* short_offset */ false); + // Loading the entrypoint does not require a load acquire since it is only changed when + // threads are suspended or running a checkpoint. + __ LoadFromOffset(kLoadWord, T9, TR, entry_point_offset); + Register ref_reg = ref.AsRegister<Register>(); + Register index_reg = index.IsRegisterPair() + ? index.AsRegisterPairLow<Register>() + : index.AsRegister<Register>(); + if (GetInstructionSetFeatures().IsR6()) { + __ Beqz(T9, 2); // Skip jialc. + __ Lsa(TMP, index_reg, obj, scale_factor); // In delay slot. + __ Jialc(T9, thunk_disp); + } else { + __ Sll(TMP, index_reg, scale_factor); + __ Beqz(T9, 2); // Skip jalr. + __ Addiu(T9, T9, thunk_disp); // In delay slot. + __ Jalr(T9); + __ Addu(TMP, TMP, obj); // In delay slot. + } + // /* HeapReference<Object> */ ref = *(obj + data_offset + (index << scale_factor)) + DCHECK(IsInt<16>(static_cast<int32_t>(data_offset))) << data_offset; + __ LoadFromOffset(kLoadWord, ref_reg, TMP, data_offset); // Single instruction. + __ MaybeUnpoisonHeapReference(ref_reg); + __ SetReorder(reordering); + return; + } + // /* HeapReference<Object> */ ref = // *(obj + data_offset + index * sizeof(HeapReference<Object>)) - ScaleFactor scale_factor = TIMES_4; GenerateReferenceLoadWithBakerReadBarrier(instruction, ref, obj, @@ -7461,10 +7748,14 @@ void InstructionCodeGeneratorMIPS::VisitLoadClass(HLoadClass* cls) NO_THREAD_SAF bool reordering = __ SetReorder(false); codegen_->EmitPcRelativeAddressPlaceholderHigh(bss_info_high, temp, - base_or_current_method_reg, - info_low); - GenerateGcRootFieldLoad(cls, out_loc, temp, /* placeholder */ 0x5678, read_barrier_option); + base_or_current_method_reg); __ SetReorder(reordering); + GenerateGcRootFieldLoad(cls, + out_loc, + temp, + /* placeholder */ 0x5678, + read_barrier_option, + &info_low->label); generate_null_check = true; break; } @@ -7475,8 +7766,13 @@ void InstructionCodeGeneratorMIPS::VisitLoadClass(HLoadClass* cls) NO_THREAD_SAF bool reordering = __ SetReorder(false); __ Bind(&info->high_label); __ Lui(out, /* placeholder */ 0x1234); - GenerateGcRootFieldLoad(cls, out_loc, out, /* placeholder */ 0x5678, read_barrier_option); __ SetReorder(reordering); + GenerateGcRootFieldLoad(cls, + out_loc, + out, + /* placeholder */ 0x5678, + read_barrier_option, + &info->low_label); break; } case HLoadClass::LoadKind::kRuntimeCall: @@ -7623,14 +7919,14 @@ void InstructionCodeGeneratorMIPS::VisitLoadString(HLoadString* load) NO_THREAD_ bool reordering = __ SetReorder(false); codegen_->EmitPcRelativeAddressPlaceholderHigh(info_high, temp, - base_or_current_method_reg, - info_low); + base_or_current_method_reg); + __ SetReorder(reordering); GenerateGcRootFieldLoad(load, out_loc, temp, /* placeholder */ 0x5678, - kCompilerReadBarrierOption); - __ SetReorder(reordering); + kCompilerReadBarrierOption, + &info_low->label); SlowPathCodeMIPS* slow_path = new (GetGraph()->GetArena()) LoadStringSlowPathMIPS(load, info_high); codegen_->AddSlowPath(slow_path); @@ -7646,12 +7942,13 @@ void InstructionCodeGeneratorMIPS::VisitLoadString(HLoadString* load) NO_THREAD_ bool reordering = __ SetReorder(false); __ Bind(&info->high_label); __ Lui(out, /* placeholder */ 0x1234); + __ SetReorder(reordering); GenerateGcRootFieldLoad(load, out_loc, out, /* placeholder */ 0x5678, - kCompilerReadBarrierOption); - __ SetReorder(reordering); + kCompilerReadBarrierOption, + &info->low_label); return; } default: diff --git a/compiler/optimizing/code_generator_mips.h b/compiler/optimizing/code_generator_mips.h index 52ee852269..7195b9d89d 100644 --- a/compiler/optimizing/code_generator_mips.h +++ b/compiler/optimizing/code_generator_mips.h @@ -285,7 +285,8 @@ class InstructionCodeGeneratorMIPS : public InstructionCodeGenerator { Location root, Register obj, uint32_t offset, - ReadBarrierOption read_barrier_option); + ReadBarrierOption read_barrier_option, + MipsLabel* label_low = nullptr); void GenerateIntCompare(IfCondition cond, LocationSummary* locations); // When the function returns `false` it means that the condition holds if `dst` is non-zero @@ -637,7 +638,7 @@ class CodeGeneratorMIPS : public CodeGenerator { void EmitPcRelativeAddressPlaceholderHigh(PcRelativePatchInfo* info_high, Register out, Register base, - PcRelativePatchInfo* info_low); + PcRelativePatchInfo* info_low = nullptr); // The JitPatchInfo is used for JIT string and class loads. struct JitPatchInfo { @@ -649,8 +650,9 @@ class CodeGeneratorMIPS : public CodeGenerator { // String/type index. uint64_t index; // Label for the instruction loading the most significant half of the address. - // The least significant half is loaded with the instruction that follows immediately. MipsLabel high_label; + // Label for the instruction supplying the least significant half of the address. + MipsLabel low_label; }; void PatchJitRootUse(uint8_t* code, diff --git a/compiler/optimizing/code_generator_mips64.cc b/compiler/optimizing/code_generator_mips64.cc index 454a2ddc14..3e79f474b6 100644 --- a/compiler/optimizing/code_generator_mips64.cc +++ b/compiler/optimizing/code_generator_mips64.cc @@ -16,6 +16,7 @@ #include "code_generator_mips64.h" +#include "arch/mips64/asm_support_mips64.h" #include "art_method.h" #include "code_generator_utils.h" #include "compiled_method.h" @@ -38,6 +39,11 @@ namespace mips64 { static constexpr int kCurrentMethodStackOffset = 0; static constexpr GpuRegister kMethodRegisterArgument = A0; +// Flags controlling the use of thunks for Baker read barriers. +constexpr bool kBakerReadBarrierThunksEnableForFields = true; +constexpr bool kBakerReadBarrierThunksEnableForArrays = true; +constexpr bool kBakerReadBarrierThunksEnableForGcRoots = true; + Location Mips64ReturnLocation(Primitive::Type return_type) { switch (return_type) { case Primitive::kPrimBoolean: @@ -1649,8 +1655,10 @@ void CodeGeneratorMIPS64::EmitPcRelativeAddressPlaceholderHigh(PcRelativePatchIn __ Auipc(out, /* placeholder */ 0x1234); // A following instruction will add the sign-extended low half of the 32-bit // offset to `out` (e.g. ld, jialc, daddiu). - DCHECK_EQ(info_low->patch_info_high, info_high); - __ Bind(&info_low->label); + if (info_low != nullptr) { + DCHECK_EQ(info_low->patch_info_high, info_high); + __ Bind(&info_low->label); + } } Literal* CodeGeneratorMIPS64::DeduplicateJitStringLiteral(const DexFile& dex_file, @@ -2117,7 +2125,12 @@ void LocationsBuilderMIPS64::VisitArrayGet(HArrayGet* instruction) { // We need a temporary register for the read barrier marking slow // path in CodeGeneratorMIPS64::GenerateArrayLoadWithBakerReadBarrier. if (object_array_get_with_read_barrier && kUseBakerReadBarrier) { - locations->AddTemp(Location::RequiresRegister()); + bool temp_needed = instruction->GetIndex()->IsConstant() + ? !kBakerReadBarrierThunksEnableForFields + : !kBakerReadBarrierThunksEnableForArrays; + if (temp_needed) { + locations->AddTemp(Location::RequiresRegister()); + } } } @@ -2254,16 +2267,32 @@ void InstructionCodeGeneratorMIPS64::VisitArrayGet(HArrayGet* instruction) { // /* HeapReference<Object> */ out = // *(obj + data_offset + index * sizeof(HeapReference<Object>)) if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) { - Location temp = locations->GetTemp(0); + bool temp_needed = index.IsConstant() + ? !kBakerReadBarrierThunksEnableForFields + : !kBakerReadBarrierThunksEnableForArrays; + Location temp = temp_needed ? locations->GetTemp(0) : Location::NoLocation(); // Note that a potential implicit null check is handled in this // CodeGeneratorMIPS64::GenerateArrayLoadWithBakerReadBarrier call. - codegen_->GenerateArrayLoadWithBakerReadBarrier(instruction, - out_loc, - obj, - data_offset, - index, - temp, - /* needs_null_check */ true); + DCHECK(!instruction->CanDoImplicitNullCheckOn(instruction->InputAt(0))); + if (index.IsConstant()) { + // Array load with a constant index can be treated as a field load. + size_t offset = + (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset; + codegen_->GenerateFieldLoadWithBakerReadBarrier(instruction, + out_loc, + obj, + offset, + temp, + /* needs_null_check */ false); + } else { + codegen_->GenerateArrayLoadWithBakerReadBarrier(instruction, + out_loc, + obj, + data_offset, + index, + temp, + /* needs_null_check */ false); + } } else { GpuRegister out = out_loc.AsRegister<GpuRegister>(); if (index.IsConstant()) { @@ -2666,6 +2695,7 @@ void InstructionCodeGeneratorMIPS64::VisitBoundsCheck(HBoundsCheck* instruction) // Temp is used for read barrier. static size_t NumberOfInstanceOfTemps(TypeCheckKind type_check_kind) { if (kEmitCompilerReadBarrier && + !(kUseBakerReadBarrier && kBakerReadBarrierThunksEnableForFields) && (kUseBakerReadBarrier || type_check_kind == TypeCheckKind::kAbstractClassCheck || type_check_kind == TypeCheckKind::kClassHierarchyCheck || @@ -4118,7 +4148,9 @@ void LocationsBuilderMIPS64::HandleFieldGet(HInstruction* instruction, if (object_field_get_with_read_barrier && kUseBakerReadBarrier) { // We need a temporary register for the read barrier marking slow // path in CodeGeneratorMIPS64::GenerateFieldLoadWithBakerReadBarrier. - locations->AddTemp(Location::RequiresRegister()); + if (!kBakerReadBarrierThunksEnableForFields) { + locations->AddTemp(Location::RequiresRegister()); + } } } @@ -4168,7 +4200,8 @@ void InstructionCodeGeneratorMIPS64::HandleFieldGet(HInstruction* instruction, if (type == Primitive::kPrimNot) { // /* HeapReference<Object> */ dst = *(obj + offset) if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) { - Location temp_loc = locations->GetTemp(0); + Location temp_loc = + kBakerReadBarrierThunksEnableForFields ? Location::NoLocation() : locations->GetTemp(0); // Note that a potential implicit null check is handled in this // CodeGeneratorMIPS64::GenerateFieldLoadWithBakerReadBarrier call. codegen_->GenerateFieldLoadWithBakerReadBarrier(instruction, @@ -4318,7 +4351,9 @@ void InstructionCodeGeneratorMIPS64::GenerateReferenceLoadOneRegister( GpuRegister out_reg = out.AsRegister<GpuRegister>(); if (read_barrier_option == kWithReadBarrier) { CHECK(kEmitCompilerReadBarrier); - DCHECK(maybe_temp.IsRegister()) << maybe_temp; + if (!kUseBakerReadBarrier || !kBakerReadBarrierThunksEnableForFields) { + DCHECK(maybe_temp.IsRegister()) << maybe_temp; + } if (kUseBakerReadBarrier) { // Load with fast path based Baker's read barrier. // /* HeapReference<Object> */ out = *(out + offset) @@ -4358,7 +4393,9 @@ void InstructionCodeGeneratorMIPS64::GenerateReferenceLoadTwoRegisters( if (read_barrier_option == kWithReadBarrier) { CHECK(kEmitCompilerReadBarrier); if (kUseBakerReadBarrier) { - DCHECK(maybe_temp.IsRegister()) << maybe_temp; + if (!kBakerReadBarrierThunksEnableForFields) { + DCHECK(maybe_temp.IsRegister()) << maybe_temp; + } // Load with fast path based Baker's read barrier. // /* HeapReference<Object> */ out = *(obj + offset) codegen_->GenerateFieldLoadWithBakerReadBarrier(instruction, @@ -4381,55 +4418,134 @@ void InstructionCodeGeneratorMIPS64::GenerateReferenceLoadTwoRegisters( } } -void InstructionCodeGeneratorMIPS64::GenerateGcRootFieldLoad( - HInstruction* instruction, - Location root, - GpuRegister obj, - uint32_t offset, - ReadBarrierOption read_barrier_option) { +static inline int GetBakerMarkThunkNumber(GpuRegister reg) { + static_assert(BAKER_MARK_INTROSPECTION_REGISTER_COUNT == 20, "Expecting equal"); + if (reg >= V0 && reg <= T2) { // 13 consequtive regs. + return reg - V0; + } else if (reg >= S2 && reg <= S7) { // 6 consequtive regs. + return 13 + (reg - S2); + } else if (reg == S8) { // One more. + return 19; + } + LOG(FATAL) << "Unexpected register " << reg; + UNREACHABLE(); +} + +static inline int GetBakerMarkFieldArrayThunkDisplacement(GpuRegister reg, bool short_offset) { + int num = GetBakerMarkThunkNumber(reg) + + (short_offset ? BAKER_MARK_INTROSPECTION_REGISTER_COUNT : 0); + return num * BAKER_MARK_INTROSPECTION_FIELD_ARRAY_ENTRY_SIZE; +} + +static inline int GetBakerMarkGcRootThunkDisplacement(GpuRegister reg) { + return GetBakerMarkThunkNumber(reg) * BAKER_MARK_INTROSPECTION_GC_ROOT_ENTRY_SIZE + + BAKER_MARK_INTROSPECTION_GC_ROOT_ENTRIES_OFFSET; +} + +void InstructionCodeGeneratorMIPS64::GenerateGcRootFieldLoad(HInstruction* instruction, + Location root, + GpuRegister obj, + uint32_t offset, + ReadBarrierOption read_barrier_option, + Mips64Label* label_low) { + if (label_low != nullptr) { + DCHECK_EQ(offset, 0x5678u); + } GpuRegister root_reg = root.AsRegister<GpuRegister>(); if (read_barrier_option == kWithReadBarrier) { DCHECK(kEmitCompilerReadBarrier); if (kUseBakerReadBarrier) { // Fast path implementation of art::ReadBarrier::BarrierForRoot when // Baker's read barrier are used: - // - // root = obj.field; - // temp = Thread::Current()->pReadBarrierMarkReg ## root.reg() - // if (temp != null) { - // root = temp(root) - // } - - // /* GcRoot<mirror::Object> */ root = *(obj + offset) - __ LoadFromOffset(kLoadUnsignedWord, root_reg, obj, offset); - static_assert( - sizeof(mirror::CompressedReference<mirror::Object>) == sizeof(GcRoot<mirror::Object>), - "art::mirror::CompressedReference<mirror::Object> and art::GcRoot<mirror::Object> " - "have different sizes."); - static_assert(sizeof(mirror::CompressedReference<mirror::Object>) == sizeof(int32_t), - "art::mirror::CompressedReference<mirror::Object> and int32_t " - "have different sizes."); - - // Slow path marking the GC root `root`. - Location temp = Location::RegisterLocation(T9); - SlowPathCodeMIPS64* slow_path = - new (GetGraph()->GetArena()) ReadBarrierMarkSlowPathMIPS64( - instruction, - root, - /*entrypoint*/ temp); - codegen_->AddSlowPath(slow_path); + if (kBakerReadBarrierThunksEnableForGcRoots) { + // Note that we do not actually check the value of `GetIsGcMarking()` + // to decide whether to mark the loaded GC root or not. Instead, we + // load into `temp` (T9) the read barrier mark introspection entrypoint. + // If `temp` is null, it means that `GetIsGcMarking()` is false, and + // vice versa. + // + // We use thunks for the slow path. That thunk checks the reference + // and jumps to the entrypoint if needed. + // + // temp = Thread::Current()->pReadBarrierMarkReg00 + // // AKA &art_quick_read_barrier_mark_introspection. + // GcRoot<mirror::Object> root = *(obj+offset); // Original reference load. + // if (temp != nullptr) { + // temp = &gc_root_thunk<root_reg> + // root = temp(root) + // } + + const int32_t entry_point_offset = + Thread::ReadBarrierMarkEntryPointsOffset<kMips64PointerSize>(0); + const int thunk_disp = GetBakerMarkGcRootThunkDisplacement(root_reg); + int16_t offset_low = Low16Bits(offset); + int16_t offset_high = High16Bits(offset - offset_low); // Accounts for sign + // extension in lwu. + bool short_offset = IsInt<16>(static_cast<int32_t>(offset)); + GpuRegister base = short_offset ? obj : TMP; + // Loading the entrypoint does not require a load acquire since it is only changed when + // threads are suspended or running a checkpoint. + __ LoadFromOffset(kLoadDoubleword, T9, TR, entry_point_offset); + if (!short_offset) { + DCHECK(!label_low); + __ Daui(base, obj, offset_high); + } + __ Beqz(T9, 2); // Skip jialc. + if (label_low != nullptr) { + DCHECK(short_offset); + __ Bind(label_low); + } + // /* GcRoot<mirror::Object> */ root = *(obj + offset) + __ LoadFromOffset(kLoadUnsignedWord, root_reg, base, offset_low); // Single instruction + // in delay slot. + __ Jialc(T9, thunk_disp); + } else { + // Note that we do not actually check the value of `GetIsGcMarking()` + // to decide whether to mark the loaded GC root or not. Instead, we + // load into `temp` (T9) the read barrier mark entry point corresponding + // to register `root`. If `temp` is null, it means that `GetIsGcMarking()` + // is false, and vice versa. + // + // GcRoot<mirror::Object> root = *(obj+offset); // Original reference load. + // temp = Thread::Current()->pReadBarrierMarkReg ## root.reg() + // if (temp != null) { + // root = temp(root) + // } + + if (label_low != nullptr) { + __ Bind(label_low); + } + // /* GcRoot<mirror::Object> */ root = *(obj + offset) + __ LoadFromOffset(kLoadUnsignedWord, root_reg, obj, offset); + static_assert( + sizeof(mirror::CompressedReference<mirror::Object>) == sizeof(GcRoot<mirror::Object>), + "art::mirror::CompressedReference<mirror::Object> and art::GcRoot<mirror::Object> " + "have different sizes."); + static_assert(sizeof(mirror::CompressedReference<mirror::Object>) == sizeof(int32_t), + "art::mirror::CompressedReference<mirror::Object> and int32_t " + "have different sizes."); + + // Slow path marking the GC root `root`. + Location temp = Location::RegisterLocation(T9); + SlowPathCodeMIPS64* slow_path = + new (GetGraph()->GetArena()) ReadBarrierMarkSlowPathMIPS64( + instruction, + root, + /*entrypoint*/ temp); + codegen_->AddSlowPath(slow_path); - // temp = Thread::Current()->pReadBarrierMarkReg ## root.reg() - const int32_t entry_point_offset = - Thread::ReadBarrierMarkEntryPointsOffset<kMips64PointerSize>(root.reg() - 1); - // Loading the entrypoint does not require a load acquire since it is only changed when - // threads are suspended or running a checkpoint. - __ LoadFromOffset(kLoadDoubleword, temp.AsRegister<GpuRegister>(), TR, entry_point_offset); - // The entrypoint is null when the GC is not marking, this prevents one load compared to - // checking GetIsGcMarking. - __ Bnezc(temp.AsRegister<GpuRegister>(), slow_path->GetEntryLabel()); - __ Bind(slow_path->GetExitLabel()); + const int32_t entry_point_offset = + Thread::ReadBarrierMarkEntryPointsOffset<kMips64PointerSize>(root.reg() - 1); + // Loading the entrypoint does not require a load acquire since it is only changed when + // threads are suspended or running a checkpoint. + __ LoadFromOffset(kLoadDoubleword, temp.AsRegister<GpuRegister>(), TR, entry_point_offset); + __ Bnezc(temp.AsRegister<GpuRegister>(), slow_path->GetEntryLabel()); + __ Bind(slow_path->GetExitLabel()); + } } else { + if (label_low != nullptr) { + __ Bind(label_low); + } // GC root loaded through a slow path for read barriers other // than Baker's. // /* GcRoot<mirror::Object>* */ root = obj + offset @@ -4438,6 +4554,9 @@ void InstructionCodeGeneratorMIPS64::GenerateGcRootFieldLoad( codegen_->GenerateReadBarrierForRootSlow(instruction, root, root); } } else { + if (label_low != nullptr) { + __ Bind(label_low); + } // Plain GC root load with no read barrier. // /* GcRoot<mirror::Object> */ root = *(obj + offset) __ LoadFromOffset(kLoadUnsignedWord, root_reg, obj, offset); @@ -4455,6 +4574,71 @@ void CodeGeneratorMIPS64::GenerateFieldLoadWithBakerReadBarrier(HInstruction* in DCHECK(kEmitCompilerReadBarrier); DCHECK(kUseBakerReadBarrier); + if (kBakerReadBarrierThunksEnableForFields) { + // Note that we do not actually check the value of `GetIsGcMarking()` + // to decide whether to mark the loaded reference or not. Instead, we + // load into `temp` (T9) the read barrier mark introspection entrypoint. + // If `temp` is null, it means that `GetIsGcMarking()` is false, and + // vice versa. + // + // We use thunks for the slow path. That thunk checks the reference + // and jumps to the entrypoint if needed. If the holder is not gray, + // it issues a load-load memory barrier and returns to the original + // reference load. + // + // temp = Thread::Current()->pReadBarrierMarkReg00 + // // AKA &art_quick_read_barrier_mark_introspection. + // if (temp != nullptr) { + // temp = &field_array_thunk<holder_reg> + // temp() + // } + // not_gray_return_address: + // // If the offset is too large to fit into the lw instruction, we + // // use an adjusted base register (TMP) here. This register + // // receives bits 16 ... 31 of the offset before the thunk invocation + // // and the thunk benefits from it. + // HeapReference<mirror::Object> reference = *(obj+offset); // Original reference load. + // gray_return_address: + + DCHECK(temp.IsInvalid()); + bool short_offset = IsInt<16>(static_cast<int32_t>(offset)); + const int32_t entry_point_offset = + Thread::ReadBarrierMarkEntryPointsOffset<kMips64PointerSize>(0); + // There may have or may have not been a null check if the field offset is smaller than + // the page size. + // There must've been a null check in case it's actually a load from an array. + // We will, however, perform an explicit null check in the thunk as it's easier to + // do it than not. + if (instruction->IsArrayGet()) { + DCHECK(!needs_null_check); + } + const int thunk_disp = GetBakerMarkFieldArrayThunkDisplacement(obj, short_offset); + // Loading the entrypoint does not require a load acquire since it is only changed when + // threads are suspended or running a checkpoint. + __ LoadFromOffset(kLoadDoubleword, T9, TR, entry_point_offset); + GpuRegister ref_reg = ref.AsRegister<GpuRegister>(); + if (short_offset) { + __ Beqzc(T9, 2); // Skip jialc. + __ Nop(); // In forbidden slot. + __ Jialc(T9, thunk_disp); + // /* HeapReference<Object> */ ref = *(obj + offset) + __ LoadFromOffset(kLoadUnsignedWord, ref_reg, obj, offset); // Single instruction. + } else { + int16_t offset_low = Low16Bits(offset); + int16_t offset_high = High16Bits(offset - offset_low); // Accounts for sign extension in lwu. + __ Beqz(T9, 2); // Skip jialc. + __ Daui(TMP, obj, offset_high); // In delay slot. + __ Jialc(T9, thunk_disp); + // /* HeapReference<Object> */ ref = *(obj + offset) + __ LoadFromOffset(kLoadUnsignedWord, ref_reg, TMP, offset_low); // Single instruction. + } + if (needs_null_check) { + MaybeRecordImplicitNullCheck(instruction); + } + __ MaybeUnpoisonHeapReference(ref_reg); + return; + } + // /* HeapReference<Object> */ ref = *(obj + offset) Location no_index = Location::NoLocation(); ScaleFactor no_scale_factor = TIMES_1; @@ -4481,9 +4665,57 @@ void CodeGeneratorMIPS64::GenerateArrayLoadWithBakerReadBarrier(HInstruction* in static_assert( sizeof(mirror::HeapReference<mirror::Object>) == sizeof(int32_t), "art::mirror::HeapReference<art::mirror::Object> and int32_t have different sizes."); + ScaleFactor scale_factor = TIMES_4; + + if (kBakerReadBarrierThunksEnableForArrays) { + // Note that we do not actually check the value of `GetIsGcMarking()` + // to decide whether to mark the loaded reference or not. Instead, we + // load into `temp` (T9) the read barrier mark introspection entrypoint. + // If `temp` is null, it means that `GetIsGcMarking()` is false, and + // vice versa. + // + // We use thunks for the slow path. That thunk checks the reference + // and jumps to the entrypoint if needed. If the holder is not gray, + // it issues a load-load memory barrier and returns to the original + // reference load. + // + // temp = Thread::Current()->pReadBarrierMarkReg00 + // // AKA &art_quick_read_barrier_mark_introspection. + // if (temp != nullptr) { + // temp = &field_array_thunk<holder_reg> + // temp() + // } + // not_gray_return_address: + // // The element address is pre-calculated in the TMP register before the + // // thunk invocation and the thunk benefits from it. + // HeapReference<mirror::Object> reference = data[index]; // Original reference load. + // gray_return_address: + + DCHECK(temp.IsInvalid()); + DCHECK(index.IsValid()); + const int32_t entry_point_offset = + Thread::ReadBarrierMarkEntryPointsOffset<kMips64PointerSize>(0); + // We will not do the explicit null check in the thunk as some form of a null check + // must've been done earlier. + DCHECK(!needs_null_check); + const int thunk_disp = GetBakerMarkFieldArrayThunkDisplacement(obj, /* short_offset */ false); + // Loading the entrypoint does not require a load acquire since it is only changed when + // threads are suspended or running a checkpoint. + __ LoadFromOffset(kLoadDoubleword, T9, TR, entry_point_offset); + __ Beqz(T9, 2); // Skip jialc. + GpuRegister ref_reg = ref.AsRegister<GpuRegister>(); + GpuRegister index_reg = index.AsRegister<GpuRegister>(); + __ Dlsa(TMP, index_reg, obj, scale_factor); // In delay slot. + __ Jialc(T9, thunk_disp); + // /* HeapReference<Object> */ ref = *(obj + data_offset + (index << scale_factor)) + DCHECK(IsInt<16>(static_cast<int32_t>(data_offset))) << data_offset; + __ LoadFromOffset(kLoadUnsignedWord, ref_reg, TMP, data_offset); // Single instruction. + __ MaybeUnpoisonHeapReference(ref_reg); + return; + } + // /* HeapReference<Object> */ ref = // *(obj + data_offset + index * sizeof(HeapReference<Object>)) - ScaleFactor scale_factor = TIMES_4; GenerateReferenceLoadWithBakerReadBarrier(instruction, ref, obj, @@ -5278,8 +5510,13 @@ void InstructionCodeGeneratorMIPS64::VisitLoadClass(HLoadClass* cls) NO_THREAD_S GpuRegister temp = non_baker_read_barrier ? out : locations->GetTemp(0).AsRegister<GpuRegister>(); - codegen_->EmitPcRelativeAddressPlaceholderHigh(bss_info_high, temp, info_low); - GenerateGcRootFieldLoad(cls, out_loc, temp, /* placeholder */ 0x5678, read_barrier_option); + codegen_->EmitPcRelativeAddressPlaceholderHigh(bss_info_high, temp); + GenerateGcRootFieldLoad(cls, + out_loc, + temp, + /* placeholder */ 0x5678, + read_barrier_option, + &info_low->label); generate_null_check = true; break; } @@ -5399,12 +5636,13 @@ void InstructionCodeGeneratorMIPS64::VisitLoadString(HLoadString* load) NO_THREA GpuRegister temp = non_baker_read_barrier ? out : locations->GetTemp(0).AsRegister<GpuRegister>(); - codegen_->EmitPcRelativeAddressPlaceholderHigh(info_high, temp, info_low); + codegen_->EmitPcRelativeAddressPlaceholderHigh(info_high, temp); GenerateGcRootFieldLoad(load, out_loc, temp, /* placeholder */ 0x5678, - kCompilerReadBarrierOption); + kCompilerReadBarrierOption, + &info_low->label); SlowPathCodeMIPS64* slow_path = new (GetGraph()->GetArena()) LoadStringSlowPathMIPS64(load, info_high); codegen_->AddSlowPath(slow_path); diff --git a/compiler/optimizing/code_generator_mips64.h b/compiler/optimizing/code_generator_mips64.h index c94cc93dad..d03a9eabd4 100644 --- a/compiler/optimizing/code_generator_mips64.h +++ b/compiler/optimizing/code_generator_mips64.h @@ -281,7 +281,8 @@ class InstructionCodeGeneratorMIPS64 : public InstructionCodeGenerator { Location root, GpuRegister obj, uint32_t offset, - ReadBarrierOption read_barrier_option); + ReadBarrierOption read_barrier_option, + Mips64Label* label_low = nullptr); void GenerateTestAndBranch(HInstruction* instruction, size_t condition_input_index, @@ -592,7 +593,7 @@ class CodeGeneratorMIPS64 : public CodeGenerator { void EmitPcRelativeAddressPlaceholderHigh(PcRelativePatchInfo* info_high, GpuRegister out, - PcRelativePatchInfo* info_low); + PcRelativePatchInfo* info_low = nullptr); void PatchJitRootUse(uint8_t* code, const uint8_t* roots_data, diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc index af0e6462a2..99b7793c81 100644 --- a/compiler/optimizing/code_generator_x86.cc +++ b/compiler/optimizing/code_generator_x86.cc @@ -24,9 +24,9 @@ #include "gc/accounting/card_table.h" #include "intrinsics.h" #include "intrinsics_x86.h" +#include "lock_word.h" #include "mirror/array-inl.h" #include "mirror/class-inl.h" -#include "lock_word.h" #include "thread.h" #include "utils/assembler.h" #include "utils/stack_checks.h" @@ -4956,8 +4956,8 @@ void InstructionCodeGeneratorX86::HandleFieldSet(HInstruction* instruction, case Primitive::kPrimShort: case Primitive::kPrimChar: { if (value.IsConstant()) { - int16_t v = CodeGenerator::GetInt32ValueOf(value.GetConstant()); - __ movw(Address(base, offset), Immediate(v)); + __ movw(Address(base, offset), + Immediate(CodeGenerator::GetInt16ValueOf(value.GetConstant()))); } else { __ movw(Address(base, offset), value.AsRegister<Register>()); } @@ -5404,7 +5404,7 @@ void InstructionCodeGeneratorX86::VisitArraySet(HArraySet* instruction) { if (value.IsRegister()) { __ movb(address, value.AsRegister<ByteRegister>()); } else { - __ movb(address, Immediate(value.GetConstant()->AsIntConstant()->GetValue())); + __ movb(address, Immediate(CodeGenerator::GetInt8ValueOf(value.GetConstant()))); } codegen_->MaybeRecordImplicitNullCheck(instruction); break; @@ -5417,7 +5417,7 @@ void InstructionCodeGeneratorX86::VisitArraySet(HArraySet* instruction) { if (value.IsRegister()) { __ movw(address, value.AsRegister<Register>()); } else { - __ movw(address, Immediate(value.GetConstant()->AsIntConstant()->GetValue())); + __ movw(address, Immediate(CodeGenerator::GetInt16ValueOf(value.GetConstant()))); } codegen_->MaybeRecordImplicitNullCheck(instruction); break; diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc index 86f6d51734..8283887a96 100644 --- a/compiler/optimizing/code_generator_x86_64.cc +++ b/compiler/optimizing/code_generator_x86_64.cc @@ -4425,8 +4425,8 @@ void InstructionCodeGeneratorX86_64::HandleFieldSet(HInstruction* instruction, case Primitive::kPrimBoolean: case Primitive::kPrimByte: { if (value.IsConstant()) { - int8_t v = CodeGenerator::GetInt32ValueOf(value.GetConstant()); - __ movb(Address(base, offset), Immediate(v)); + __ movb(Address(base, offset), + Immediate(CodeGenerator::GetInt8ValueOf(value.GetConstant()))); } else { __ movb(Address(base, offset), value.AsRegister<CpuRegister>()); } @@ -4436,8 +4436,8 @@ void InstructionCodeGeneratorX86_64::HandleFieldSet(HInstruction* instruction, case Primitive::kPrimShort: case Primitive::kPrimChar: { if (value.IsConstant()) { - int16_t v = CodeGenerator::GetInt32ValueOf(value.GetConstant()); - __ movw(Address(base, offset), Immediate(v)); + __ movw(Address(base, offset), + Immediate(CodeGenerator::GetInt16ValueOf(value.GetConstant()))); } else { __ movw(Address(base, offset), value.AsRegister<CpuRegister>()); } @@ -4861,7 +4861,7 @@ void InstructionCodeGeneratorX86_64::VisitArraySet(HArraySet* instruction) { if (value.IsRegister()) { __ movb(address, value.AsRegister<CpuRegister>()); } else { - __ movb(address, Immediate(value.GetConstant()->AsIntConstant()->GetValue())); + __ movb(address, Immediate(CodeGenerator::GetInt8ValueOf(value.GetConstant()))); } codegen_->MaybeRecordImplicitNullCheck(instruction); break; @@ -4875,7 +4875,7 @@ void InstructionCodeGeneratorX86_64::VisitArraySet(HArraySet* instruction) { __ movw(address, value.AsRegister<CpuRegister>()); } else { DCHECK(value.IsConstant()) << value; - __ movw(address, Immediate(value.GetConstant()->AsIntConstant()->GetValue())); + __ movw(address, Immediate(CodeGenerator::GetInt16ValueOf(value.GetConstant()))); } codegen_->MaybeRecordImplicitNullCheck(instruction); break; diff --git a/compiler/optimizing/common_arm.h b/compiler/optimizing/common_arm.h index 01304ac35b..e354654ee8 100644 --- a/compiler/optimizing/common_arm.h +++ b/compiler/optimizing/common_arm.h @@ -17,8 +17,8 @@ #ifndef ART_COMPILER_OPTIMIZING_COMMON_ARM_H_ #define ART_COMPILER_OPTIMIZING_COMMON_ARM_H_ -#include "instruction_simplifier_shared.h" #include "debug/dwarf/register.h" +#include "instruction_simplifier_shared.h" #include "locations.h" #include "nodes.h" #include "utils/arm/constants_arm.h" @@ -227,14 +227,6 @@ inline Location LocationFrom(const vixl::aarch32::SRegister& low, return Location::FpuRegisterPairLocation(low.GetCode(), high.GetCode()); } -inline bool ShifterOperandSupportsExtension(HInstruction* instruction) { - DCHECK(HasShifterOperand(instruction, kArm)); - // TODO: HAdd applied to the other integral types could make use of - // the SXTAB, SXTAH, UXTAB and UXTAH instructions. - return instruction->GetType() == Primitive::kPrimLong && - (instruction->IsAdd() || instruction->IsSub()); -} - } // namespace helpers } // namespace arm } // namespace art diff --git a/compiler/optimizing/dead_code_elimination_test.cc b/compiler/optimizing/dead_code_elimination_test.cc index fdd77e7261..96fa5406b2 100644 --- a/compiler/optimizing/dead_code_elimination_test.cc +++ b/compiler/optimizing/dead_code_elimination_test.cc @@ -14,9 +14,10 @@ * limitations under the License. */ +#include "dead_code_elimination.h" + #include "arch/x86/instruction_set_features_x86.h" #include "code_generator_x86.h" -#include "dead_code_elimination.h" #include "driver/compiler_options.h" #include "graph_checker.h" #include "optimizing_unit_test.h" diff --git a/compiler/optimizing/find_loops_test.cc b/compiler/optimizing/find_loops_test.cc index 04789d9a2d..bbd28f5c46 100644 --- a/compiler/optimizing/find_loops_test.cc +++ b/compiler/optimizing/find_loops_test.cc @@ -20,8 +20,8 @@ #include "dex_instruction.h" #include "nodes.h" #include "optimizing_unit_test.h" -#include "ssa_liveness_analysis.h" #include "pretty_printer.h" +#include "ssa_liveness_analysis.h" #include "gtest/gtest.h" diff --git a/compiler/optimizing/graph_checker.cc b/compiler/optimizing/graph_checker.cc index aea901dec7..327e11f7e7 100644 --- a/compiler/optimizing/graph_checker.cc +++ b/compiler/optimizing/graph_checker.cc @@ -17,8 +17,8 @@ #include "graph_checker.h" #include <algorithm> -#include <string> #include <sstream> +#include <string> #include "android-base/stringprintf.h" diff --git a/compiler/optimizing/gvn_test.cc b/compiler/optimizing/gvn_test.cc index f8d37bd714..e1ed7f656e 100644 --- a/compiler/optimizing/gvn_test.cc +++ b/compiler/optimizing/gvn_test.cc @@ -14,9 +14,10 @@ * limitations under the License. */ +#include "gvn.h" + #include "base/arena_allocator.h" #include "builder.h" -#include "gvn.h" #include "nodes.h" #include "optimizing_unit_test.h" #include "side_effects_analysis.h" diff --git a/compiler/optimizing/induction_var_range_test.cc b/compiler/optimizing/induction_var_range_test.cc index 67d2093829..2b82b336d7 100644 --- a/compiler/optimizing/induction_var_range_test.cc +++ b/compiler/optimizing/induction_var_range_test.cc @@ -14,10 +14,11 @@ * limitations under the License. */ +#include "induction_var_range.h" + #include "base/arena_allocator.h" #include "builder.h" #include "induction_var_analysis.h" -#include "induction_var_range.h" #include "nodes.h" #include "optimizing_unit_test.h" diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc index 18390cc4d4..38a1a8c024 100644 --- a/compiler/optimizing/inliner.cc +++ b/compiler/optimizing/inliner.cc @@ -23,8 +23,8 @@ #include "constant_folding.h" #include "dead_code_elimination.h" #include "dex/inline_method_analyser.h" -#include "dex/verified_method.h" #include "dex/verification_results.h" +#include "dex/verified_method.h" #include "driver/compiler_driver-inl.h" #include "driver/compiler_options.h" #include "driver/dex_compilation_unit.h" @@ -38,10 +38,10 @@ #include "optimizing_compiler.h" #include "reference_type_propagation.h" #include "register_allocator_linear_scan.h" +#include "scoped_thread_state_change-inl.h" #include "sharpening.h" #include "ssa_builder.h" #include "ssa_phi_elimination.h" -#include "scoped_thread_state_change-inl.h" #include "thread.h" namespace art { diff --git a/compiler/optimizing/inliner.h b/compiler/optimizing/inliner.h index 67476b6956..62c6713208 100644 --- a/compiler/optimizing/inliner.h +++ b/compiler/optimizing/inliner.h @@ -19,8 +19,8 @@ #include "dex_file_types.h" #include "invoke_type.h" -#include "optimization.h" #include "jit/profile_compilation_info.h" +#include "optimization.h" namespace art { diff --git a/compiler/optimizing/instruction_builder.cc b/compiler/optimizing/instruction_builder.cc index 839f328a4f..143c77f334 100644 --- a/compiler/optimizing/instruction_builder.cc +++ b/compiler/optimizing/instruction_builder.cc @@ -23,8 +23,8 @@ #include "driver/compiler_options.h" #include "imtable-inl.h" #include "quicken_info.h" -#include "sharpening.h" #include "scoped_thread_state_change-inl.h" +#include "sharpening.h" namespace art { @@ -664,10 +664,7 @@ void HInstructionBuilder::BuildReturn(const Instruction& instruction, // TODO: remove redundant constructor fences (b/36656456). if (RequiresConstructorBarrier(dex_compilation_unit_, compiler_driver_)) { // Compiling instance constructor. - if (kIsDebugBuild) { - std::string method_name = graph_->GetMethodName(); - CHECK_EQ(std::string("<init>"), method_name); - } + DCHECK_STREQ("<init>", graph_->GetMethodName()); HInstruction* fence_target = current_this_parameter_; DCHECK(fence_target != nullptr); @@ -710,29 +707,18 @@ static InvokeType GetInvokeTypeFromOpCode(Instruction::Code opcode) { ArtMethod* HInstructionBuilder::ResolveMethod(uint16_t method_idx, InvokeType invoke_type) { ScopedObjectAccess soa(Thread::Current()); - StackHandleScope<2> hs(soa.Self()); ClassLinker* class_linker = dex_compilation_unit_->GetClassLinker(); Handle<mirror::ClassLoader> class_loader = dex_compilation_unit_->GetClassLoader(); - Handle<mirror::Class> compiling_class(hs.NewHandle(GetCompilingClass())); - // We fetch the referenced class eagerly (that is, the class pointed by in the MethodId - // at method_idx), as `CanAccessResolvedMethod` expects it be be in the dex cache. - Handle<mirror::Class> methods_class(hs.NewHandle(class_linker->ResolveReferencedClassOfMethod( - method_idx, dex_compilation_unit_->GetDexCache(), class_loader))); - - if (UNLIKELY(methods_class == nullptr)) { - // Clean up any exception left by type resolution. - soa.Self()->ClearException(); - return nullptr; - } - ArtMethod* resolved_method = class_linker->ResolveMethod<ClassLinker::kForceICCECheck>( - *dex_compilation_unit_->GetDexFile(), - method_idx, - dex_compilation_unit_->GetDexCache(), - class_loader, - /* referrer */ nullptr, - invoke_type); + ArtMethod* resolved_method = + class_linker->ResolveMethod<ClassLinker::ResolveMode::kCheckICCEAndIAE>( + *dex_compilation_unit_->GetDexFile(), + method_idx, + dex_compilation_unit_->GetDexCache(), + class_loader, + graph_->GetArtMethod(), + invoke_type); if (UNLIKELY(resolved_method == nullptr)) { // Clean up any exception left by type resolution. @@ -740,17 +726,14 @@ ArtMethod* HInstructionBuilder::ResolveMethod(uint16_t method_idx, InvokeType in return nullptr; } - // Check access. The class linker has a fast path for looking into the dex cache - // and does not check the access if it hits it. - if (compiling_class == nullptr) { + // The referrer may be unresolved for AOT if we're compiling a class that cannot be + // resolved because, for example, we don't find a superclass in the classpath. + if (graph_->GetArtMethod() == nullptr) { + // The class linker cannot check access without a referrer, so we have to do it. + // Fall back to HInvokeUnresolved if the method isn't public. if (!resolved_method->IsPublic()) { return nullptr; } - } else if (!compiling_class->CanAccessResolvedMethod(resolved_method->GetDeclaringClass(), - resolved_method, - dex_compilation_unit_->GetDexCache().Get(), - method_idx)) { - return nullptr; } // We have to special case the invoke-super case, as ClassLinker::ResolveMethod does not. @@ -758,19 +741,26 @@ ArtMethod* HInstructionBuilder::ResolveMethod(uint16_t method_idx, InvokeType in // make this an invoke-unresolved to handle cross-dex invokes or abstract super methods, both of // which require runtime handling. if (invoke_type == kSuper) { + ObjPtr<mirror::Class> compiling_class = GetCompilingClass(); if (compiling_class == nullptr) { // We could not determine the method's class we need to wait until runtime. DCHECK(Runtime::Current()->IsAotCompiler()); return nullptr; } - if (!methods_class->IsAssignableFrom(compiling_class.Get())) { + ObjPtr<mirror::Class> referenced_class = class_linker->LookupResolvedType( + *dex_compilation_unit_->GetDexFile(), + dex_compilation_unit_->GetDexFile()->GetMethodId(method_idx).class_idx_, + dex_compilation_unit_->GetDexCache().Get(), + class_loader.Get()); + DCHECK(referenced_class != nullptr); // We have already resolved a method from this class. + if (!referenced_class->IsAssignableFrom(compiling_class)) { // We cannot statically determine the target method. The runtime will throw a // NoSuchMethodError on this one. return nullptr; } ArtMethod* actual_method; - if (methods_class->IsInterface()) { - actual_method = methods_class->FindVirtualMethodForInterfaceSuper( + if (referenced_class->IsInterface()) { + actual_method = referenced_class->FindVirtualMethodForInterfaceSuper( resolved_method, class_linker->GetImagePointerSize()); } else { uint16_t vtable_index = resolved_method->GetMethodIndex(); @@ -797,12 +787,6 @@ ArtMethod* HInstructionBuilder::ResolveMethod(uint16_t method_idx, InvokeType in resolved_method = actual_method; } - // Check for incompatible class changes. The class linker has a fast path for - // looking into the dex cache and does not check incompatible class changes if it hits it. - if (resolved_method->CheckIncompatibleClassChange(invoke_type)) { - return nullptr; - } - return resolved_method; } diff --git a/compiler/optimizing/instruction_builder.h b/compiler/optimizing/instruction_builder.h index 5a83df3813..2a9b9f513d 100644 --- a/compiler/optimizing/instruction_builder.h +++ b/compiler/optimizing/instruction_builder.h @@ -21,8 +21,8 @@ #include "base/arena_object.h" #include "block_builder.h" #include "dex_file_types.h" -#include "driver/compiler_driver.h" #include "driver/compiler_driver-inl.h" +#include "driver/compiler_driver.h" #include "driver/dex_compilation_unit.h" #include "mirror/dex_cache.h" #include "nodes.h" diff --git a/compiler/optimizing/instruction_simplifier.cc b/compiler/optimizing/instruction_simplifier.cc index d14716601c..02cfbbcfb3 100644 --- a/compiler/optimizing/instruction_simplifier.cc +++ b/compiler/optimizing/instruction_simplifier.cc @@ -21,8 +21,8 @@ #include "escape.h" #include "intrinsics.h" #include "mirror/class-inl.h" -#include "sharpening.h" #include "scoped_thread_state_change-inl.h" +#include "sharpening.h" namespace art { @@ -1867,33 +1867,35 @@ void InstructionSimplifierVisitor::SimplifySystemArrayCopy(HInvoke* instruction) ArtMethod* method = nullptr; switch (source_component_type) { case Primitive::kPrimBoolean: - method = system->FindDeclaredDirectMethod("arraycopy", "([ZI[ZII)V", image_size); + method = system->FindClassMethod("arraycopy", "([ZI[ZII)V", image_size); break; case Primitive::kPrimByte: - method = system->FindDeclaredDirectMethod("arraycopy", "([BI[BII)V", image_size); + method = system->FindClassMethod("arraycopy", "([BI[BII)V", image_size); break; case Primitive::kPrimChar: - method = system->FindDeclaredDirectMethod("arraycopy", "([CI[CII)V", image_size); + method = system->FindClassMethod("arraycopy", "([CI[CII)V", image_size); break; case Primitive::kPrimShort: - method = system->FindDeclaredDirectMethod("arraycopy", "([SI[SII)V", image_size); + method = system->FindClassMethod("arraycopy", "([SI[SII)V", image_size); break; case Primitive::kPrimInt: - method = system->FindDeclaredDirectMethod("arraycopy", "([II[III)V", image_size); + method = system->FindClassMethod("arraycopy", "([II[III)V", image_size); break; case Primitive::kPrimFloat: - method = system->FindDeclaredDirectMethod("arraycopy", "([FI[FII)V", image_size); + method = system->FindClassMethod("arraycopy", "([FI[FII)V", image_size); break; case Primitive::kPrimLong: - method = system->FindDeclaredDirectMethod("arraycopy", "([JI[JII)V", image_size); + method = system->FindClassMethod("arraycopy", "([JI[JII)V", image_size); break; case Primitive::kPrimDouble: - method = system->FindDeclaredDirectMethod("arraycopy", "([DI[DII)V", image_size); + method = system->FindClassMethod("arraycopy", "([DI[DII)V", image_size); break; default: LOG(FATAL) << "Unreachable"; } DCHECK(method != nullptr); + DCHECK(method->IsStatic()); + DCHECK(method->GetDeclaringClass() == system); invoke->SetResolvedMethod(method); // Sharpen the new invoke. Note that we do not update the dex method index of // the invoke, as we would need to look it up in the current dex file, and it diff --git a/compiler/optimizing/instruction_simplifier_arm.cc b/compiler/optimizing/instruction_simplifier_arm.cc index fe22595258..a32d0ce42b 100644 --- a/compiler/optimizing/instruction_simplifier_arm.cc +++ b/compiler/optimizing/instruction_simplifier_arm.cc @@ -14,9 +14,10 @@ * limitations under the License. */ +#include "instruction_simplifier_arm.h" + #include "code_generator.h" #include "common_arm.h" -#include "instruction_simplifier_arm.h" #include "instruction_simplifier_shared.h" #include "mirror/array-inl.h" #include "mirror/string.h" @@ -29,8 +30,6 @@ using helpers::HasShifterOperand; namespace arm { -using helpers::ShifterOperandSupportsExtension; - bool InstructionSimplifierArmVisitor::TryMergeIntoShifterOperand(HInstruction* use, HInstruction* bitfield_op, bool do_merge) { @@ -76,7 +75,7 @@ bool InstructionSimplifierArmVisitor::TryMergeIntoShifterOperand(HInstruction* u : kMaxLongShiftDistance; if (HDataProcWithShifterOp::IsExtensionOp(op_kind)) { - if (!ShifterOperandSupportsExtension(use)) { + if (!use->IsAdd() && (!use->IsSub() || use->GetType() != Primitive::kPrimLong)) { return false; } // Shift by 1 is a special case that results in the same number and type of instructions diff --git a/compiler/optimizing/intrinsics.cc b/compiler/optimizing/intrinsics.cc index b664d41013..7bdeef527e 100644 --- a/compiler/optimizing/intrinsics.cc +++ b/compiler/optimizing/intrinsics.cc @@ -39,7 +39,7 @@ static inline InvokeType GetIntrinsicInvokeType(Intrinsics i) { case Intrinsics::k ## Name: \ return IsStatic; #include "intrinsics_list.h" -INTRINSICS_LIST(OPTIMIZING_INTRINSICS) + INTRINSICS_LIST(OPTIMIZING_INTRINSICS) #undef INTRINSICS_LIST #undef OPTIMIZING_INTRINSICS } @@ -55,7 +55,7 @@ static inline IntrinsicNeedsEnvironmentOrCache NeedsEnvironmentOrCache(Intrinsic case Intrinsics::k ## Name: \ return NeedsEnvironmentOrCache; #include "intrinsics_list.h" -INTRINSICS_LIST(OPTIMIZING_INTRINSICS) + INTRINSICS_LIST(OPTIMIZING_INTRINSICS) #undef INTRINSICS_LIST #undef OPTIMIZING_INTRINSICS } @@ -71,7 +71,7 @@ static inline IntrinsicSideEffects GetSideEffects(Intrinsics i) { case Intrinsics::k ## Name: \ return SideEffects; #include "intrinsics_list.h" -INTRINSICS_LIST(OPTIMIZING_INTRINSICS) + INTRINSICS_LIST(OPTIMIZING_INTRINSICS) #undef INTRINSICS_LIST #undef OPTIMIZING_INTRINSICS } @@ -87,7 +87,7 @@ static inline IntrinsicExceptions GetExceptions(Intrinsics i) { case Intrinsics::k ## Name: \ return Exceptions; #include "intrinsics_list.h" -INTRINSICS_LIST(OPTIMIZING_INTRINSICS) + INTRINSICS_LIST(OPTIMIZING_INTRINSICS) #undef INTRINSICS_LIST #undef OPTIMIZING_INTRINSICS } @@ -172,7 +172,7 @@ std::ostream& operator<<(std::ostream& os, const Intrinsics& intrinsic) { os << # Name; \ break; #include "intrinsics_list.h" -INTRINSICS_LIST(OPTIMIZING_INTRINSICS) + INTRINSICS_LIST(OPTIMIZING_INTRINSICS) #undef STATIC_INTRINSICS_LIST #undef VIRTUAL_INTRINSICS_LIST #undef OPTIMIZING_INTRINSICS diff --git a/compiler/optimizing/intrinsics.h b/compiler/optimizing/intrinsics.h index 9da5a7fa3b..6411e82f92 100644 --- a/compiler/optimizing/intrinsics.h +++ b/compiler/optimizing/intrinsics.h @@ -63,7 +63,7 @@ class IntrinsicVisitor : public ValueObject { Visit ## Name(invoke); \ return; #include "intrinsics_list.h" -INTRINSICS_LIST(OPTIMIZING_INTRINSICS) + INTRINSICS_LIST(OPTIMIZING_INTRINSICS) #undef INTRINSICS_LIST #undef OPTIMIZING_INTRINSICS @@ -77,7 +77,7 @@ INTRINSICS_LIST(OPTIMIZING_INTRINSICS) virtual void Visit ## Name(HInvoke* invoke ATTRIBUTE_UNUSED) { \ } #include "intrinsics_list.h" -INTRINSICS_LIST(OPTIMIZING_INTRINSICS) + INTRINSICS_LIST(OPTIMIZING_INTRINSICS) #undef INTRINSICS_LIST #undef OPTIMIZING_INTRINSICS diff --git a/compiler/optimizing/intrinsics_arm64.h b/compiler/optimizing/intrinsics_arm64.h index ff59ce9658..5a6d180ed6 100644 --- a/compiler/optimizing/intrinsics_arm64.h +++ b/compiler/optimizing/intrinsics_arm64.h @@ -47,7 +47,7 @@ class IntrinsicLocationsBuilderARM64 FINAL : public IntrinsicVisitor { #define OPTIMIZING_INTRINSICS(Name, IsStatic, NeedsEnvironmentOrCache, SideEffects, Exceptions, ...) \ void Visit ## Name(HInvoke* invoke) OVERRIDE; #include "intrinsics_list.h" -INTRINSICS_LIST(OPTIMIZING_INTRINSICS) + INTRINSICS_LIST(OPTIMIZING_INTRINSICS) #undef INTRINSICS_LIST #undef OPTIMIZING_INTRINSICS @@ -72,7 +72,7 @@ class IntrinsicCodeGeneratorARM64 FINAL : public IntrinsicVisitor { #define OPTIMIZING_INTRINSICS(Name, IsStatic, NeedsEnvironmentOrCache, SideEffects, Exceptions, ...) \ void Visit ## Name(HInvoke* invoke) OVERRIDE; #include "intrinsics_list.h" -INTRINSICS_LIST(OPTIMIZING_INTRINSICS) + INTRINSICS_LIST(OPTIMIZING_INTRINSICS) #undef INTRINSICS_LIST #undef OPTIMIZING_INTRINSICS diff --git a/compiler/optimizing/intrinsics_arm_vixl.h b/compiler/optimizing/intrinsics_arm_vixl.h index 023cba1349..a4a2830211 100644 --- a/compiler/optimizing/intrinsics_arm_vixl.h +++ b/compiler/optimizing/intrinsics_arm_vixl.h @@ -36,7 +36,7 @@ class IntrinsicLocationsBuilderARMVIXL FINAL : public IntrinsicVisitor { #define OPTIMIZING_INTRINSICS(Name, IsStatic, NeedsEnvironmentOrCache, SideEffects, Exceptions, ...) \ void Visit ## Name(HInvoke* invoke) OVERRIDE; #include "intrinsics_list.h" -INTRINSICS_LIST(OPTIMIZING_INTRINSICS) + INTRINSICS_LIST(OPTIMIZING_INTRINSICS) #undef INTRINSICS_LIST #undef OPTIMIZING_INTRINSICS @@ -63,7 +63,7 @@ class IntrinsicCodeGeneratorARMVIXL FINAL : public IntrinsicVisitor { #define OPTIMIZING_INTRINSICS(Name, IsStatic, NeedsEnvironmentOrCache, SideEffects, Exceptions, ...) \ void Visit ## Name(HInvoke* invoke) OVERRIDE; #include "intrinsics_list.h" -INTRINSICS_LIST(OPTIMIZING_INTRINSICS) + INTRINSICS_LIST(OPTIMIZING_INTRINSICS) #undef INTRINSICS_LIST #undef OPTIMIZING_INTRINSICS diff --git a/compiler/optimizing/intrinsics_mips.h b/compiler/optimizing/intrinsics_mips.h index eaadad2515..05d1aa284a 100644 --- a/compiler/optimizing/intrinsics_mips.h +++ b/compiler/optimizing/intrinsics_mips.h @@ -39,7 +39,7 @@ class IntrinsicLocationsBuilderMIPS FINAL : public IntrinsicVisitor { #define OPTIMIZING_INTRINSICS(Name, IsStatic, NeedsEnvironmentOrCache, SideEffects, Exceptions, ...) \ void Visit ## Name(HInvoke* invoke) OVERRIDE; #include "intrinsics_list.h" -INTRINSICS_LIST(OPTIMIZING_INTRINSICS) + INTRINSICS_LIST(OPTIMIZING_INTRINSICS) #undef INTRINSICS_LIST #undef OPTIMIZING_INTRINSICS @@ -64,7 +64,7 @@ class IntrinsicCodeGeneratorMIPS FINAL : public IntrinsicVisitor { #define OPTIMIZING_INTRINSICS(Name, IsStatic, NeedsEnvironmentOrCache, SideEffects, Exceptions, ...) \ void Visit ## Name(HInvoke* invoke) OVERRIDE; #include "intrinsics_list.h" -INTRINSICS_LIST(OPTIMIZING_INTRINSICS) + INTRINSICS_LIST(OPTIMIZING_INTRINSICS) #undef INTRINSICS_LIST #undef OPTIMIZING_INTRINSICS diff --git a/compiler/optimizing/intrinsics_mips64.h b/compiler/optimizing/intrinsics_mips64.h index 179627ab20..6880a255c3 100644 --- a/compiler/optimizing/intrinsics_mips64.h +++ b/compiler/optimizing/intrinsics_mips64.h @@ -39,7 +39,7 @@ class IntrinsicLocationsBuilderMIPS64 FINAL : public IntrinsicVisitor { #define OPTIMIZING_INTRINSICS(Name, IsStatic, NeedsEnvironmentOrCache, SideEffects, Exceptions, ...) \ void Visit ## Name(HInvoke* invoke) OVERRIDE; #include "intrinsics_list.h" -INTRINSICS_LIST(OPTIMIZING_INTRINSICS) + INTRINSICS_LIST(OPTIMIZING_INTRINSICS) #undef INTRINSICS_LIST #undef OPTIMIZING_INTRINSICS @@ -64,7 +64,7 @@ class IntrinsicCodeGeneratorMIPS64 FINAL : public IntrinsicVisitor { #define OPTIMIZING_INTRINSICS(Name, IsStatic, NeedsEnvironmentOrCache, SideEffects, Exceptions, ...) \ void Visit ## Name(HInvoke* invoke) OVERRIDE; #include "intrinsics_list.h" -INTRINSICS_LIST(OPTIMIZING_INTRINSICS) + INTRINSICS_LIST(OPTIMIZING_INTRINSICS) #undef INTRINSICS_LIST #undef OPTIMIZING_INTRINSICS diff --git a/compiler/optimizing/intrinsics_x86.h b/compiler/optimizing/intrinsics_x86.h index 3743cb1371..22f11b1d34 100644 --- a/compiler/optimizing/intrinsics_x86.h +++ b/compiler/optimizing/intrinsics_x86.h @@ -39,7 +39,7 @@ class IntrinsicLocationsBuilderX86 FINAL : public IntrinsicVisitor { #define OPTIMIZING_INTRINSICS(Name, IsStatic, NeedsEnvironmentOrCache, SideEffects, Exceptions, ...) \ void Visit ## Name(HInvoke* invoke) OVERRIDE; #include "intrinsics_list.h" -INTRINSICS_LIST(OPTIMIZING_INTRINSICS) + INTRINSICS_LIST(OPTIMIZING_INTRINSICS) #undef INTRINSICS_LIST #undef OPTIMIZING_INTRINSICS @@ -64,7 +64,7 @@ class IntrinsicCodeGeneratorX86 FINAL : public IntrinsicVisitor { #define OPTIMIZING_INTRINSICS(Name, IsStatic, NeedsEnvironmentOrCache, SideEffects, Exceptions, ...) \ void Visit ## Name(HInvoke* invoke) OVERRIDE; #include "intrinsics_list.h" -INTRINSICS_LIST(OPTIMIZING_INTRINSICS) + INTRINSICS_LIST(OPTIMIZING_INTRINSICS) #undef INTRINSICS_LIST #undef OPTIMIZING_INTRINSICS diff --git a/compiler/optimizing/intrinsics_x86_64.h b/compiler/optimizing/intrinsics_x86_64.h index 97404aa568..4b287886af 100644 --- a/compiler/optimizing/intrinsics_x86_64.h +++ b/compiler/optimizing/intrinsics_x86_64.h @@ -39,7 +39,7 @@ class IntrinsicLocationsBuilderX86_64 FINAL : public IntrinsicVisitor { #define OPTIMIZING_INTRINSICS(Name, IsStatic, NeedsEnvironmentOrCache, SideEffects, Exceptions, ...) \ void Visit ## Name(HInvoke* invoke) OVERRIDE; #include "intrinsics_list.h" -INTRINSICS_LIST(OPTIMIZING_INTRINSICS) + INTRINSICS_LIST(OPTIMIZING_INTRINSICS) #undef INTRINSICS_LIST #undef OPTIMIZING_INTRINSICS @@ -64,7 +64,7 @@ class IntrinsicCodeGeneratorX86_64 FINAL : public IntrinsicVisitor { #define OPTIMIZING_INTRINSICS(Name, IsStatic, NeedsEnvironmentOrCache, SideEffects, Exceptions, ...) \ void Visit ## Name(HInvoke* invoke) OVERRIDE; #include "intrinsics_list.h" -INTRINSICS_LIST(OPTIMIZING_INTRINSICS) + INTRINSICS_LIST(OPTIMIZING_INTRINSICS) #undef INTRINSICS_LIST #undef OPTIMIZING_INTRINSICS diff --git a/compiler/optimizing/licm_test.cc b/compiler/optimizing/licm_test.cc index 8d15f78cce..8967d7cef2 100644 --- a/compiler/optimizing/licm_test.cc +++ b/compiler/optimizing/licm_test.cc @@ -14,9 +14,10 @@ * limitations under the License. */ +#include "licm.h" + #include "base/arena_allocator.h" #include "builder.h" -#include "licm.h" #include "nodes.h" #include "optimizing_unit_test.h" #include "side_effects_analysis.h" diff --git a/compiler/optimizing/load_store_elimination.cc b/compiler/optimizing/load_store_elimination.cc index 211528b4bd..fddda3d8c0 100644 --- a/compiler/optimizing/load_store_elimination.cc +++ b/compiler/optimizing/load_store_elimination.cc @@ -14,10 +14,10 @@ * limitations under the License. */ -#include "load_store_analysis.h" #include "load_store_elimination.h" #include "escape.h" +#include "load_store_analysis.h" #include "side_effects_analysis.h" #include <iostream> diff --git a/compiler/optimizing/locations.cc b/compiler/optimizing/locations.cc index a9fe209063..40fe35b31b 100644 --- a/compiler/optimizing/locations.cc +++ b/compiler/optimizing/locations.cc @@ -18,8 +18,8 @@ #include <type_traits> -#include "nodes.h" #include "code_generator.h" +#include "nodes.h" namespace art { diff --git a/compiler/optimizing/loop_optimization.cc b/compiler/optimizing/loop_optimization.cc index 422e58debb..274f065084 100644 --- a/compiler/optimizing/loop_optimization.cc +++ b/compiler/optimizing/loop_optimization.cc @@ -16,9 +16,9 @@ #include "loop_optimization.h" -#include "arch/instruction_set.h" #include "arch/arm/instruction_set_features_arm.h" #include "arch/arm64/instruction_set_features_arm64.h" +#include "arch/instruction_set.h" #include "arch/mips/instruction_set_features_mips.h" #include "arch/mips64/instruction_set_features_mips64.h" #include "arch/x86/instruction_set_features_x86.h" diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc index 4ca833707b..ca48e08a7c 100644 --- a/compiler/optimizing/nodes.cc +++ b/compiler/optimizing/nodes.cc @@ -18,16 +18,16 @@ #include <cfloat> #include "art_method-inl.h" +#include "base/bit_utils.h" +#include "base/bit_vector-inl.h" +#include "base/stl_util.h" #include "class_linker-inl.h" #include "code_generator.h" #include "common_dominator.h" -#include "ssa_builder.h" -#include "base/bit_vector-inl.h" -#include "base/bit_utils.h" -#include "base/stl_util.h" #include "intrinsics.h" #include "mirror/class-inl.h" #include "scoped_thread_state_change-inl.h" +#include "ssa_builder.h" namespace art { diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h index 5e072cdb67..fa29378e42 100644 --- a/compiler/optimizing/nodes.h +++ b/compiler/optimizing/nodes.h @@ -28,14 +28,14 @@ #include "base/iteration_range.h" #include "base/stl_util.h" #include "base/transform_array_ref.h" +#include "deoptimization_kind.h" #include "dex_file.h" #include "dex_file_types.h" -#include "deoptimization_kind.h" #include "entrypoints/quick/quick_entrypoints_enum.h" #include "handle.h" #include "handle_scope.h" -#include "invoke_type.h" #include "intrinsics_enum.h" +#include "invoke_type.h" #include "locations.h" #include "method_reference.h" #include "mirror/class.h" diff --git a/compiler/optimizing/nodes_shared.cc b/compiler/optimizing/nodes_shared.cc index f145bf9130..f6d33f015f 100644 --- a/compiler/optimizing/nodes_shared.cc +++ b/compiler/optimizing/nodes_shared.cc @@ -14,9 +14,15 @@ * limitations under the License. */ -#include "common_arm64.h" +// Note: this include order may seem strange and is against the regular style. However it is the +// required order as nodes_shared does not have the right dependency chain and compilation +// will fail (as AsType on HInstruction will be defined before the full Instruction). +#include "nodes.h" + #include "nodes_shared.h" +#include "common_arm64.h" + namespace art { using helpers::CanFitInShifterOperand; diff --git a/compiler/optimizing/nodes_test.cc b/compiler/optimizing/nodes_test.cc index 7686ba851b..f3a78a064e 100644 --- a/compiler/optimizing/nodes_test.cc +++ b/compiler/optimizing/nodes_test.cc @@ -14,8 +14,9 @@ * limitations under the License. */ -#include "base/arena_allocator.h" #include "nodes.h" + +#include "base/arena_allocator.h" #include "optimizing_unit_test.h" #include "gtest/gtest.h" diff --git a/compiler/optimizing/optimizing_cfi_test.cc b/compiler/optimizing/optimizing_cfi_test.cc index 6cb27b3b1b..c24f1de93d 100644 --- a/compiler/optimizing/optimizing_cfi_test.cc +++ b/compiler/optimizing/optimizing_cfi_test.cc @@ -23,8 +23,8 @@ #include "gtest/gtest.h" #include "optimizing/code_generator.h" #include "optimizing/optimizing_unit_test.h" -#include "utils/assembler.h" #include "utils/arm/assembler_arm_vixl.h" +#include "utils/assembler.h" #include "utils/mips/assembler_mips.h" #include "utils/mips64/assembler_mips64.h" diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc index 890ba674b5..76a243f793 100644 --- a/compiler/optimizing/optimizing_compiler.cc +++ b/compiler/optimizing/optimizing_compiler.cc @@ -76,6 +76,7 @@ #include "jit/debugger_interface.h" #include "jit/jit.h" #include "jit/jit_code_cache.h" +#include "jit/jit_logger.h" #include "jni/quick/jni_compiler.h" #include "licm.h" #include "load_store_analysis.h" @@ -86,8 +87,8 @@ #include "prepare_for_register_allocation.h" #include "reference_type_propagation.h" #include "register_allocator_linear_scan.h" -#include "select_generator.h" #include "scheduler.h" +#include "select_generator.h" #include "sharpening.h" #include "side_effects_analysis.h" #include "ssa_builder.h" @@ -334,7 +335,11 @@ class OptimizingCompiler FINAL : public Compiler { } } - bool JitCompile(Thread* self, jit::JitCodeCache* code_cache, ArtMethod* method, bool osr) + bool JitCompile(Thread* self, + jit::JitCodeCache* code_cache, + ArtMethod* method, + bool osr, + jit::JitLogger* jit_logger) OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_); @@ -1136,7 +1141,8 @@ bool CanEncodeInlinedMethodInStackMap(const DexFile& caller_dex_file, ArtMethod* bool OptimizingCompiler::JitCompile(Thread* self, jit::JitCodeCache* code_cache, ArtMethod* method, - bool osr) { + bool osr, + jit::JitLogger* jit_logger) { StackHandleScope<3> hs(self); Handle<mirror::ClassLoader> class_loader(hs.NewHandle( method->GetDeclaringClass()->GetClassLoader())); @@ -1204,14 +1210,14 @@ bool OptimizingCompiler::JitCompile(Thread* self, uint8_t* stack_map_data = nullptr; uint8_t* method_info_data = nullptr; uint8_t* roots_data = nullptr; - uint32_t data_size = code_cache->ReserveData(self, - stack_map_size, - method_info_size, - number_of_roots, - method, - &stack_map_data, - &method_info_data, - &roots_data); + code_cache->ReserveData(self, + stack_map_size, + method_info_size, + number_of_roots, + method, + &stack_map_data, + &method_info_data, + &roots_data); if (stack_map_data == nullptr || roots_data == nullptr) { return false; } @@ -1232,7 +1238,6 @@ bool OptimizingCompiler::JitCompile(Thread* self, codegen->GetFpuSpillMask(), code_allocator.GetMemory().data(), code_allocator.GetSize(), - data_size, osr, roots, codegen->GetGraph()->HasShouldDeoptimizeFlag(), @@ -1272,6 +1277,9 @@ bool OptimizingCompiler::JitCompile(Thread* self, } Runtime::Current()->GetJit()->AddMemoryUsage(method, arena.BytesUsed()); + if (jit_logger != nullptr) { + jit_logger->WriteLog(code, code_allocator.GetSize(), method); + } return true; } diff --git a/compiler/optimizing/optimizing_unit_test.h b/compiler/optimizing/optimizing_unit_test.h index 1cdcbd2e9b..08493fa177 100644 --- a/compiler/optimizing/optimizing_unit_test.h +++ b/compiler/optimizing/optimizing_unit_test.h @@ -17,12 +17,12 @@ #ifndef ART_COMPILER_OPTIMIZING_OPTIMIZING_UNIT_TEST_H_ #define ART_COMPILER_OPTIMIZING_OPTIMIZING_UNIT_TEST_H_ -#include "nodes.h" #include "builder.h" #include "common_compiler_test.h" #include "dex_file.h" #include "dex_instruction.h" #include "handle_scope.h" +#include "nodes.h" #include "scoped_thread_state_change.h" #include "ssa_builder.h" #include "ssa_liveness_analysis.h" diff --git a/compiler/optimizing/parallel_move_test.cc b/compiler/optimizing/parallel_move_test.cc index 5e8fe37669..50620f0e7b 100644 --- a/compiler/optimizing/parallel_move_test.cc +++ b/compiler/optimizing/parallel_move_test.cc @@ -18,8 +18,8 @@ #include "nodes.h" #include "parallel_move_resolver.h" -#include "gtest/gtest.h" #include "gtest/gtest-typed-test.h" +#include "gtest/gtest.h" namespace art { diff --git a/compiler/optimizing/pretty_printer_test.cc b/compiler/optimizing/pretty_printer_test.cc index 1af94f3445..14d2360392 100644 --- a/compiler/optimizing/pretty_printer_test.cc +++ b/compiler/optimizing/pretty_printer_test.cc @@ -14,13 +14,14 @@ * limitations under the License. */ +#include "pretty_printer.h" + #include "base/arena_allocator.h" #include "builder.h" #include "dex_file.h" #include "dex_instruction.h" #include "nodes.h" #include "optimizing_unit_test.h" -#include "pretty_printer.h" #include "gtest/gtest.h" diff --git a/compiler/optimizing/reference_type_propagation.cc b/compiler/optimizing/reference_type_propagation.cc index 98332d35fb..f172e16ff9 100644 --- a/compiler/optimizing/reference_type_propagation.cc +++ b/compiler/optimizing/reference_type_propagation.cc @@ -525,8 +525,8 @@ void ReferenceTypePropagation::RTPVisitor::SetClassAsTypeInfo(HInstruction* inst // Use a null loader. We should probably use the compiling method's class loader, // but then we would need to pass it to RTPVisitor just for this debug check. Since // the method is from the String class, the null loader is good enough. - Handle<mirror::ClassLoader> loader; - ArtMethod* method = cl->ResolveMethod<ClassLinker::kNoICCECheckForCache>( + Handle<mirror::ClassLoader> loader(hs.NewHandle<mirror::ClassLoader>(nullptr)); + ArtMethod* method = cl->ResolveMethod<ClassLinker::ResolveMode::kNoChecks>( dex_file, invoke->GetDexMethodIndex(), dex_cache, loader, nullptr, kDirect); DCHECK(method != nullptr); mirror::Class* declaring_class = method->GetDeclaringClass(); diff --git a/compiler/optimizing/reference_type_propagation_test.cc b/compiler/optimizing/reference_type_propagation_test.cc index 0b49ce1a4c..d537459113 100644 --- a/compiler/optimizing/reference_type_propagation_test.cc +++ b/compiler/optimizing/reference_type_propagation_test.cc @@ -14,12 +14,13 @@ * limitations under the License. */ +#include "reference_type_propagation.h" + #include "base/arena_allocator.h" #include "builder.h" #include "nodes.h" #include "object_lock.h" #include "optimizing_unit_test.h" -#include "reference_type_propagation.h" namespace art { diff --git a/compiler/optimizing/register_allocator.cc b/compiler/optimizing/register_allocator.cc index 5b768d5d67..c3b33e29d7 100644 --- a/compiler/optimizing/register_allocator.cc +++ b/compiler/optimizing/register_allocator.cc @@ -25,7 +25,6 @@ #include "register_allocator_linear_scan.h" #include "ssa_liveness_analysis.h" - namespace art { RegisterAllocator::RegisterAllocator(ArenaAllocator* allocator, diff --git a/compiler/optimizing/register_allocator_test.cc b/compiler/optimizing/register_allocator_test.cc index 24a2ab24d8..bcdd7f9cd8 100644 --- a/compiler/optimizing/register_allocator_test.cc +++ b/compiler/optimizing/register_allocator_test.cc @@ -14,6 +14,8 @@ * limitations under the License. */ +#include "register_allocator.h" + #include "arch/x86/instruction_set_features_x86.h" #include "base/arena_allocator.h" #include "builder.h" @@ -25,7 +27,6 @@ #include "driver/compiler_options.h" #include "nodes.h" #include "optimizing_unit_test.h" -#include "register_allocator.h" #include "register_allocator_linear_scan.h" #include "ssa_liveness_analysis.h" #include "ssa_phi_elimination.h" diff --git a/compiler/optimizing/scheduler.h b/compiler/optimizing/scheduler.h index 930a2c82cf..66ffac5b7d 100644 --- a/compiler/optimizing/scheduler.h +++ b/compiler/optimizing/scheduler.h @@ -20,11 +20,11 @@ #include <fstream> #include "base/time_utils.h" +#include "code_generator.h" #include "driver/compiler_driver.h" #include "load_store_analysis.h" #include "nodes.h" #include "optimization.h" -#include "code_generator.h" namespace art { diff --git a/compiler/optimizing/scheduler_arm.cc b/compiler/optimizing/scheduler_arm.cc index e78cd78aa2..ea15790105 100644 --- a/compiler/optimizing/scheduler_arm.cc +++ b/compiler/optimizing/scheduler_arm.cc @@ -14,11 +14,12 @@ * limitations under the License. */ +#include "scheduler_arm.h" + #include "arch/arm/instruction_set_features_arm.h" #include "code_generator_utils.h" #include "common_arm.h" #include "mirror/array-inl.h" -#include "scheduler_arm.h" namespace art { namespace arm { @@ -167,22 +168,346 @@ void SchedulingLatencyVisitorARM::VisitUShr(HUShr* instr) { HandleShiftLatencies(instr); } -void SchedulingLatencyVisitorARM::VisitCondition(HCondition* instr) { - switch (instr->GetLeft()->GetType()) { - case Primitive::kPrimLong: - last_visited_internal_latency_ = 4 * kArmIntegerOpLatency; +void SchedulingLatencyVisitorARM::HandleGenerateConditionWithZero(IfCondition condition) { + switch (condition) { + case kCondEQ: + case kCondBE: + case kCondNE: + case kCondA: + last_visited_internal_latency_ += kArmIntegerOpLatency; + last_visited_latency_ = kArmIntegerOpLatency; break; - case Primitive::kPrimFloat: - case Primitive::kPrimDouble: - last_visited_internal_latency_ = 2 * kArmFloatingPointOpLatency; + case kCondGE: + // Mvn + last_visited_internal_latency_ += kArmIntegerOpLatency; + FALLTHROUGH_INTENDED; + case kCondLT: + // Lsr + last_visited_latency_ = kArmIntegerOpLatency; + break; + case kCondAE: + // Trivially true. + // Mov + last_visited_latency_ = kArmIntegerOpLatency; + break; + case kCondB: + // Trivially false. + // Mov + last_visited_latency_ = kArmIntegerOpLatency; break; default: - last_visited_internal_latency_ = 2 * kArmIntegerOpLatency; + LOG(FATAL) << "Unexpected condition " << condition; + UNREACHABLE(); + } +} + +void SchedulingLatencyVisitorARM::HandleGenerateLongTestConstant(HCondition* condition) { + DCHECK_EQ(condition->GetLeft()->GetType(), Primitive::kPrimLong); + + IfCondition cond = condition->GetCondition(); + + HInstruction* right = condition->InputAt(1); + + int64_t value = Uint64ConstantFrom(right); + + // Comparisons against 0 are common enough, so codegen has special handling for them. + if (value == 0) { + switch (cond) { + case kCondNE: + case kCondA: + case kCondEQ: + case kCondBE: + // Orrs + last_visited_internal_latency_ += kArmIntegerOpLatency; + return; + case kCondLT: + case kCondGE: + // Cmp + last_visited_internal_latency_ += kArmIntegerOpLatency; + return; + case kCondB: + case kCondAE: + // Cmp + last_visited_internal_latency_ += kArmIntegerOpLatency; + return; + default: + break; + } + } + + switch (cond) { + case kCondEQ: + case kCondNE: + case kCondB: + case kCondBE: + case kCondA: + case kCondAE: { + // Cmp, IT, Cmp + last_visited_internal_latency_ += 3 * kArmIntegerOpLatency; break; + } + case kCondLE: + case kCondGT: + // Trivially true or false. + if (value == std::numeric_limits<int64_t>::max()) { + // Cmp + last_visited_internal_latency_ += kArmIntegerOpLatency; + break; + } + FALLTHROUGH_INTENDED; + case kCondGE: + case kCondLT: { + // Cmp, Sbcs + last_visited_internal_latency_ += 2 * kArmIntegerOpLatency; + break; + } + default: + LOG(FATAL) << "Unreachable"; + UNREACHABLE(); + } +} + +void SchedulingLatencyVisitorARM::HandleGenerateLongTest(HCondition* condition) { + DCHECK_EQ(condition->GetLeft()->GetType(), Primitive::kPrimLong); + + IfCondition cond = condition->GetCondition(); + + switch (cond) { + case kCondEQ: + case kCondNE: + case kCondB: + case kCondBE: + case kCondA: + case kCondAE: { + // Cmp, IT, Cmp + last_visited_internal_latency_ += 3 * kArmIntegerOpLatency; + break; + } + case kCondLE: + case kCondGT: + case kCondGE: + case kCondLT: { + // Cmp, Sbcs + last_visited_internal_latency_ += 2 * kArmIntegerOpLatency; + break; + } + default: + LOG(FATAL) << "Unreachable"; + UNREACHABLE(); } +} + +// The GenerateTest series of function all counted as internal latency. +void SchedulingLatencyVisitorARM::HandleGenerateTest(HCondition* condition) { + const Primitive::Type type = condition->GetLeft()->GetType(); + + if (type == Primitive::kPrimLong) { + condition->InputAt(1)->IsConstant() + ? HandleGenerateLongTestConstant(condition) + : HandleGenerateLongTest(condition); + } else if (Primitive::IsFloatingPointType(type)) { + // GenerateVcmp + Vmrs + last_visited_internal_latency_ += 2 * kArmFloatingPointOpLatency; + } else { + // Cmp + last_visited_internal_latency_ += kArmIntegerOpLatency; + } +} + +bool SchedulingLatencyVisitorARM::CanGenerateTest(HCondition* condition) { + if (condition->GetLeft()->GetType() == Primitive::kPrimLong) { + HInstruction* right = condition->InputAt(1); + + if (right->IsConstant()) { + IfCondition c = condition->GetCondition(); + const uint64_t value = Uint64ConstantFrom(right); + + if (c < kCondLT || c > kCondGE) { + if (value != 0) { + return false; + } + } else if (c == kCondLE || c == kCondGT) { + if (value < std::numeric_limits<int64_t>::max() && + !codegen_->GetAssembler()->ShifterOperandCanHold(SBC, High32Bits(value + 1), kCcSet)) { + return false; + } + } else if (!codegen_->GetAssembler()->ShifterOperandCanHold(SBC, High32Bits(value), kCcSet)) { + return false; + } + } + } + + return true; +} + +void SchedulingLatencyVisitorARM::HandleGenerateConditionGeneric(HCondition* cond) { + HandleGenerateTest(cond); + + // Unlike codegen pass, we cannot check 'out' register IsLow() here, + // because scheduling is before liveness(location builder) and register allocator, + // so we can only choose to follow one path of codegen by assuming otu.IsLow() is true. + last_visited_internal_latency_ += 2 * kArmIntegerOpLatency; last_visited_latency_ = kArmIntegerOpLatency; } +void SchedulingLatencyVisitorARM::HandleGenerateEqualLong(HCondition* cond) { + DCHECK_EQ(cond->GetLeft()->GetType(), Primitive::kPrimLong); + + IfCondition condition = cond->GetCondition(); + + last_visited_internal_latency_ += 2 * kArmIntegerOpLatency; + + if (condition == kCondNE) { + // Orrs, IT, Mov + last_visited_internal_latency_ += 3 * kArmIntegerOpLatency; + } else { + last_visited_internal_latency_ += kArmIntegerOpLatency; + HandleGenerateConditionWithZero(condition); + } +} + +void SchedulingLatencyVisitorARM::HandleGenerateLongComparesAndJumps() { + last_visited_internal_latency_ += 4 * kArmIntegerOpLatency; + last_visited_internal_latency_ += kArmBranchLatency; +} + +void SchedulingLatencyVisitorARM::HandleGenerateConditionLong(HCondition* cond) { + DCHECK_EQ(cond->GetLeft()->GetType(), Primitive::kPrimLong); + + IfCondition condition = cond->GetCondition(); + HInstruction* right = cond->InputAt(1); + + if (right->IsConstant()) { + // Comparisons against 0 are common enough, so codegen has special handling for them. + if (Uint64ConstantFrom(right) == 0) { + switch (condition) { + case kCondNE: + case kCondA: + case kCondEQ: + case kCondBE: + // Orr + last_visited_internal_latency_ += kArmIntegerOpLatency; + HandleGenerateConditionWithZero(condition); + return; + case kCondLT: + case kCondGE: + FALLTHROUGH_INTENDED; + case kCondAE: + case kCondB: + HandleGenerateConditionWithZero(condition); + return; + case kCondLE: + case kCondGT: + default: + break; + } + } + } + + if ((condition == kCondEQ || condition == kCondNE) && + !CanGenerateTest(cond)) { + HandleGenerateEqualLong(cond); + return; + } + + if (CanGenerateTest(cond)) { + HandleGenerateConditionGeneric(cond); + return; + } + + HandleGenerateLongComparesAndJumps(); + + last_visited_internal_latency_ += kArmIntegerOpLatency; + last_visited_latency_ = kArmBranchLatency;; +} + +void SchedulingLatencyVisitorARM::HandleGenerateConditionIntegralOrNonPrimitive(HCondition* cond) { + const Primitive::Type type = cond->GetLeft()->GetType(); + + DCHECK(Primitive::IsIntegralType(type) || type == Primitive::kPrimNot) << type; + + if (type == Primitive::kPrimLong) { + HandleGenerateConditionLong(cond); + return; + } + + IfCondition condition = cond->GetCondition(); + HInstruction* right = cond->InputAt(1); + int64_t value; + + if (right->IsConstant()) { + value = Uint64ConstantFrom(right); + + // Comparisons against 0 are common enough, so codegen has special handling for them. + if (value == 0) { + switch (condition) { + case kCondNE: + case kCondA: + case kCondEQ: + case kCondBE: + case kCondLT: + case kCondGE: + case kCondAE: + case kCondB: + HandleGenerateConditionWithZero(condition); + return; + case kCondLE: + case kCondGT: + default: + break; + } + } + } + + if (condition == kCondEQ || condition == kCondNE) { + if (condition == kCondNE) { + // CMP, IT, MOV.ne + last_visited_internal_latency_ += 2 * kArmIntegerOpLatency; + last_visited_latency_ = kArmIntegerOpLatency; + } else { + last_visited_internal_latency_ += kArmIntegerOpLatency; + HandleGenerateConditionWithZero(condition); + } + return; + } + + HandleGenerateConditionGeneric(cond); +} + +void SchedulingLatencyVisitorARM::HandleCondition(HCondition* cond) { + if (cond->IsEmittedAtUseSite()) { + last_visited_latency_ = 0; + return; + } + + const Primitive::Type type = cond->GetLeft()->GetType(); + + if (Primitive::IsFloatingPointType(type)) { + HandleGenerateConditionGeneric(cond); + return; + } + + DCHECK(Primitive::IsIntegralType(type) || type == Primitive::kPrimNot) << type; + + const IfCondition condition = cond->GetCondition(); + + if (type == Primitive::kPrimBoolean && + cond->GetRight()->GetType() == Primitive::kPrimBoolean && + (condition == kCondEQ || condition == kCondNE)) { + if (condition == kCondEQ) { + last_visited_internal_latency_ = kArmIntegerOpLatency; + } + last_visited_latency_ = kArmIntegerOpLatency; + return; + } + + HandleGenerateConditionIntegralOrNonPrimitive(cond); +} + +void SchedulingLatencyVisitorARM::VisitCondition(HCondition* instr) { + HandleCondition(instr); +} + void SchedulingLatencyVisitorARM::VisitCompare(HCompare* instr) { Primitive::Type type = instr->InputAt(0)->GetType(); switch (type) { @@ -269,7 +594,6 @@ void SchedulingLatencyVisitorARM::VisitDataProcWithShifterOp(HDataProcWithShifte const HDataProcWithShifterOp::OpKind op_kind = instruction->GetOpKind(); if (instruction->GetType() == Primitive::kPrimInt) { - DCHECK(!HDataProcWithShifterOp::IsExtensionOp(op_kind)); HandleGenerateDataProcInstruction(); } else { DCHECK_EQ(instruction->GetType(), Primitive::kPrimLong); diff --git a/compiler/optimizing/scheduler_arm.h b/compiler/optimizing/scheduler_arm.h index a9f2295c35..fe274d29f9 100644 --- a/compiler/optimizing/scheduler_arm.h +++ b/compiler/optimizing/scheduler_arm.h @@ -109,6 +109,17 @@ class SchedulingLatencyVisitorARM : public SchedulingLatencyVisitor { #undef DECLARE_VISIT_INSTRUCTION private: + bool CanGenerateTest(HCondition* cond); + void HandleGenerateConditionWithZero(IfCondition cond); + void HandleGenerateLongTestConstant(HCondition* cond); + void HandleGenerateLongTest(HCondition* cond); + void HandleGenerateLongComparesAndJumps(); + void HandleGenerateTest(HCondition* cond); + void HandleGenerateConditionGeneric(HCondition* cond); + void HandleGenerateEqualLong(HCondition* cond); + void HandleGenerateConditionLong(HCondition* cond); + void HandleGenerateConditionIntegralOrNonPrimitive(HCondition* cond); + void HandleCondition(HCondition* instr); void HandleBinaryOperationLantencies(HBinaryOperation* instr); void HandleBitwiseOperationLantencies(HBinaryOperation* instr); void HandleShiftLatencies(HBinaryOperation* instr); diff --git a/compiler/optimizing/scheduler_arm64.cc b/compiler/optimizing/scheduler_arm64.cc index 83b487fb5b..f54d3f3de2 100644 --- a/compiler/optimizing/scheduler_arm64.cc +++ b/compiler/optimizing/scheduler_arm64.cc @@ -15,6 +15,7 @@ */ #include "scheduler_arm64.h" + #include "code_generator_utils.h" #include "mirror/array-inl.h" diff --git a/compiler/optimizing/scheduler_test.cc b/compiler/optimizing/scheduler_test.cc index 10c3cd7535..cdb6666f83 100644 --- a/compiler/optimizing/scheduler_test.cc +++ b/compiler/optimizing/scheduler_test.cc @@ -14,6 +14,8 @@ * limitations under the License. */ +#include "scheduler.h" + #include "base/arena_allocator.h" #include "builder.h" #include "codegen_test_utils.h" @@ -23,7 +25,6 @@ #include "optimizing_unit_test.h" #include "pc_relative_fixups_x86.h" #include "register_allocator.h" -#include "scheduler.h" #ifdef ART_ENABLE_CODEGEN_arm64 #include "scheduler_arm64.h" diff --git a/compiler/optimizing/sharpening.cc b/compiler/optimizing/sharpening.cc index 8bd568befd..9536d149f6 100644 --- a/compiler/optimizing/sharpening.cc +++ b/compiler/optimizing/sharpening.cc @@ -21,10 +21,9 @@ #include "base/enums.h" #include "class_linker.h" #include "code_generator.h" +#include "driver/compiler_driver.h" #include "driver/compiler_options.h" #include "driver/dex_compilation_unit.h" -#include "utils/dex_cache_arrays_layout-inl.h" -#include "driver/compiler_driver.h" #include "gc/heap.h" #include "gc/space/image_space.h" #include "handle_scope-inl.h" @@ -33,6 +32,7 @@ #include "nodes.h" #include "runtime.h" #include "scoped_thread_state_change-inl.h" +#include "utils/dex_cache_arrays_layout-inl.h" namespace art { diff --git a/compiler/optimizing/ssa_liveness_analysis_test.cc b/compiler/optimizing/ssa_liveness_analysis_test.cc index 029eb4ba61..b46060a24a 100644 --- a/compiler/optimizing/ssa_liveness_analysis_test.cc +++ b/compiler/optimizing/ssa_liveness_analysis_test.cc @@ -14,15 +14,16 @@ * limitations under the License. */ +#include "ssa_liveness_analysis.h" + #include "arch/instruction_set.h" #include "arch/instruction_set_features.h" #include "base/arena_allocator.h" #include "base/arena_containers.h" -#include "driver/compiler_options.h" #include "code_generator.h" +#include "driver/compiler_options.h" #include "nodes.h" #include "optimizing_unit_test.h" -#include "ssa_liveness_analysis.h" namespace art { diff --git a/compiler/optimizing/ssa_phi_elimination.cc b/compiler/optimizing/ssa_phi_elimination.cc index aec7a3c555..b4f8408a76 100644 --- a/compiler/optimizing/ssa_phi_elimination.cc +++ b/compiler/optimizing/ssa_phi_elimination.cc @@ -16,8 +16,8 @@ #include "ssa_phi_elimination.h" -#include "base/arena_containers.h" #include "base/arena_bit_vector.h" +#include "base/arena_containers.h" #include "base/bit_vector-inl.h" namespace art { diff --git a/compiler/utils/arm/jni_macro_assembler_arm_vixl.cc b/compiler/utils/arm/jni_macro_assembler_arm_vixl.cc index bebe64c2b9..ed57ca68e2 100644 --- a/compiler/utils/arm/jni_macro_assembler_arm_vixl.cc +++ b/compiler/utils/arm/jni_macro_assembler_arm_vixl.cc @@ -14,10 +14,11 @@ * limitations under the License. */ +#include "jni_macro_assembler_arm_vixl.h" + #include <iostream> #include <type_traits> -#include "jni_macro_assembler_arm_vixl.h" #include "entrypoints/quick/quick_entrypoints.h" #include "thread.h" diff --git a/compiler/utils/arm/managed_register_arm_test.cc b/compiler/utils/arm/managed_register_arm_test.cc index f5d4cc0d10..43b0b516dc 100644 --- a/compiler/utils/arm/managed_register_arm_test.cc +++ b/compiler/utils/arm/managed_register_arm_test.cc @@ -14,8 +14,8 @@ * limitations under the License. */ -#include "globals.h" #include "managed_register_arm.h" +#include "globals.h" #include "gtest/gtest.h" namespace art { diff --git a/compiler/utils/arm64/assembler_arm64.h b/compiler/utils/arm64/assembler_arm64.h index 66a7fed804..5b8a34e56d 100644 --- a/compiler/utils/arm64/assembler_arm64.h +++ b/compiler/utils/arm64/assembler_arm64.h @@ -23,9 +23,9 @@ #include "base/arena_containers.h" #include "base/logging.h" +#include "offsets.h" #include "utils/arm64/managed_register_arm64.h" #include "utils/assembler.h" -#include "offsets.h" // TODO(VIXL): Make VIXL compile with -Wshadow. #pragma GCC diagnostic push diff --git a/compiler/utils/arm64/jni_macro_assembler_arm64.h b/compiler/utils/arm64/jni_macro_assembler_arm64.h index 264e99adab..baf0434de0 100644 --- a/compiler/utils/arm64/jni_macro_assembler_arm64.h +++ b/compiler/utils/arm64/jni_macro_assembler_arm64.h @@ -25,9 +25,9 @@ #include "base/arena_containers.h" #include "base/enums.h" #include "base/logging.h" +#include "offsets.h" #include "utils/assembler.h" #include "utils/jni_macro_assembler.h" -#include "offsets.h" // TODO(VIXL): Make VIXL compile with -Wshadow. #pragma GCC diagnostic push diff --git a/compiler/utils/arm64/managed_register_arm64_test.cc b/compiler/utils/arm64/managed_register_arm64_test.cc index 79076b8ccc..2a79313be5 100644 --- a/compiler/utils/arm64/managed_register_arm64_test.cc +++ b/compiler/utils/arm64/managed_register_arm64_test.cc @@ -14,9 +14,10 @@ * limitations under the License. */ -#include "globals.h" -#include "assembler_arm64.h" #include "managed_register_arm64.h" + +#include "assembler_arm64.h" +#include "globals.h" #include "gtest/gtest.h" namespace art { diff --git a/compiler/utils/assembler_test.h b/compiler/utils/assembler_test.h index f655994bd3..ef53d7237c 100644 --- a/compiler/utils/assembler_test.h +++ b/compiler/utils/assembler_test.h @@ -19,14 +19,15 @@ #include "assembler.h" -#include "assembler_test_base.h" -#include "common_runtime_test.h" // For ScratchFile +#include <sys/stat.h> #include <cstdio> #include <cstdlib> #include <fstream> #include <iterator> -#include <sys/stat.h> + +#include "assembler_test_base.h" +#include "common_runtime_test.h" // For ScratchFile namespace art { diff --git a/compiler/utils/assembler_test_base.h b/compiler/utils/assembler_test_base.h index d76cb1c1df..2ef43bd79c 100644 --- a/compiler/utils/assembler_test_base.h +++ b/compiler/utils/assembler_test_base.h @@ -17,11 +17,11 @@ #ifndef ART_COMPILER_UTILS_ASSEMBLER_TEST_BASE_H_ #define ART_COMPILER_UTILS_ASSEMBLER_TEST_BASE_H_ +#include <sys/stat.h> #include <cstdio> #include <cstdlib> #include <fstream> #include <iterator> -#include <sys/stat.h> #include "android-base/strings.h" diff --git a/compiler/utils/assembler_thumb_test.cc b/compiler/utils/assembler_thumb_test.cc index 741beab1d5..e51b622b81 100644 --- a/compiler/utils/assembler_thumb_test.cc +++ b/compiler/utils/assembler_thumb_test.cc @@ -16,10 +16,10 @@ #include <dirent.h> #include <errno.h> -#include <fstream> -#include <map> #include <string.h> #include <sys/types.h> +#include <fstream> +#include <map> #include "gtest/gtest.h" diff --git a/compiler/utils/dedupe_set-inl.h b/compiler/utils/dedupe_set-inl.h index c06e9cadcc..c866504e62 100644 --- a/compiler/utils/dedupe_set-inl.h +++ b/compiler/utils/dedupe_set-inl.h @@ -19,14 +19,15 @@ #include "dedupe_set.h" -#include <algorithm> #include <inttypes.h> + +#include <algorithm> #include <unordered_map> #include "android-base/stringprintf.h" -#include "base/mutex.h" #include "base/hash_set.h" +#include "base/mutex.h" #include "base/stl_util.h" #include "base/time_utils.h" diff --git a/compiler/utils/dedupe_set.h b/compiler/utils/dedupe_set.h index b62f216842..3baa0612f6 100644 --- a/compiler/utils/dedupe_set.h +++ b/compiler/utils/dedupe_set.h @@ -17,8 +17,8 @@ #ifndef ART_COMPILER_UTILS_DEDUPE_SET_H_ #define ART_COMPILER_UTILS_DEDUPE_SET_H_ -#include <memory> #include <stdint.h> +#include <memory> #include <string> #include "base/macros.h" diff --git a/compiler/utils/jni_macro_assembler_test.h b/compiler/utils/jni_macro_assembler_test.h index 293f4cde9c..61296802f8 100644 --- a/compiler/utils/jni_macro_assembler_test.h +++ b/compiler/utils/jni_macro_assembler_test.h @@ -22,11 +22,12 @@ #include "assembler_test_base.h" #include "common_runtime_test.h" // For ScratchFile +#include <sys/stat.h> + #include <cstdio> #include <cstdlib> #include <fstream> #include <iterator> -#include <sys/stat.h> namespace art { diff --git a/compiler/utils/mips/assembler_mips.cc b/compiler/utils/mips/assembler_mips.cc index c581f1c58f..2cbabcfb32 100644 --- a/compiler/utils/mips/assembler_mips.cc +++ b/compiler/utils/mips/assembler_mips.cc @@ -828,6 +828,22 @@ void MipsAssembler::Aui(Register rt, Register rs, uint16_t imm16) { DsFsmInstrRrr(EmitI(0xf, rs, rt, imm16), rt, rt, rs); } +void MipsAssembler::AddUpper(Register rt, Register rs, uint16_t imm16, Register tmp) { + bool increment = (rs == rt); + if (increment) { + CHECK_NE(rs, tmp); + } + if (IsR6()) { + Aui(rt, rs, imm16); + } else if (increment) { + Lui(tmp, imm16); + Addu(rt, rs, tmp); + } else { + Lui(rt, imm16); + Addu(rt, rs, rt); + } +} + void MipsAssembler::Sync(uint32_t stype) { DsFsmInstrNop(EmitR(0, ZERO, ZERO, ZERO, stype & 0x1f, 0xf)); } @@ -2904,6 +2920,102 @@ void MipsAssembler::IlvrD(VectorRegister wd, VectorRegister ws, VectorRegister w static_cast<FRegister>(wt)); } +void MipsAssembler::MaddvB(VectorRegister wd, VectorRegister ws, VectorRegister wt) { + CHECK(HasMsa()); + DsFsmInstrFff(EmitMsa3R(0x1, 0x0, wt, ws, wd, 0x12), + static_cast<FRegister>(wd), + static_cast<FRegister>(ws), + static_cast<FRegister>(wt)); +} + +void MipsAssembler::MaddvH(VectorRegister wd, VectorRegister ws, VectorRegister wt) { + CHECK(HasMsa()); + DsFsmInstrFff(EmitMsa3R(0x1, 0x1, wt, ws, wd, 0x12), + static_cast<FRegister>(wd), + static_cast<FRegister>(ws), + static_cast<FRegister>(wt)); +} + +void MipsAssembler::MaddvW(VectorRegister wd, VectorRegister ws, VectorRegister wt) { + CHECK(HasMsa()); + DsFsmInstrFff(EmitMsa3R(0x1, 0x2, wt, ws, wd, 0x12), + static_cast<FRegister>(wd), + static_cast<FRegister>(ws), + static_cast<FRegister>(wt)); +} + +void MipsAssembler::MaddvD(VectorRegister wd, VectorRegister ws, VectorRegister wt) { + CHECK(HasMsa()); + DsFsmInstrFff(EmitMsa3R(0x1, 0x3, wt, ws, wd, 0x12), + static_cast<FRegister>(wd), + static_cast<FRegister>(ws), + static_cast<FRegister>(wt)); +} + +void MipsAssembler::MsubvB(VectorRegister wd, VectorRegister ws, VectorRegister wt) { + CHECK(HasMsa()); + DsFsmInstrFff(EmitMsa3R(0x2, 0x0, wt, ws, wd, 0x12), + static_cast<FRegister>(wd), + static_cast<FRegister>(ws), + static_cast<FRegister>(wt)); +} + +void MipsAssembler::MsubvH(VectorRegister wd, VectorRegister ws, VectorRegister wt) { + CHECK(HasMsa()); + DsFsmInstrFff(EmitMsa3R(0x2, 0x1, wt, ws, wd, 0x12), + static_cast<FRegister>(wd), + static_cast<FRegister>(ws), + static_cast<FRegister>(wt)); +} + +void MipsAssembler::MsubvW(VectorRegister wd, VectorRegister ws, VectorRegister wt) { + CHECK(HasMsa()); + DsFsmInstrFff(EmitMsa3R(0x2, 0x2, wt, ws, wd, 0x12), + static_cast<FRegister>(wd), + static_cast<FRegister>(ws), + static_cast<FRegister>(wt)); +} + +void MipsAssembler::MsubvD(VectorRegister wd, VectorRegister ws, VectorRegister wt) { + CHECK(HasMsa()); + DsFsmInstrFff(EmitMsa3R(0x2, 0x3, wt, ws, wd, 0x12), + static_cast<FRegister>(wd), + static_cast<FRegister>(ws), + static_cast<FRegister>(wt)); +} + +void MipsAssembler::FmaddW(VectorRegister wd, VectorRegister ws, VectorRegister wt) { + CHECK(HasMsa()); + DsFsmInstrFff(EmitMsa3R(0x2, 0x0, wt, ws, wd, 0x1b), + static_cast<FRegister>(wd), + static_cast<FRegister>(ws), + static_cast<FRegister>(wt)); +} + +void MipsAssembler::FmaddD(VectorRegister wd, VectorRegister ws, VectorRegister wt) { + CHECK(HasMsa()); + DsFsmInstrFff(EmitMsa3R(0x2, 0x1, wt, ws, wd, 0x1b), + static_cast<FRegister>(wd), + static_cast<FRegister>(ws), + static_cast<FRegister>(wt)); +} + +void MipsAssembler::FmsubW(VectorRegister wd, VectorRegister ws, VectorRegister wt) { + CHECK(HasMsa()); + DsFsmInstrFff(EmitMsa3R(0x2, 0x2, wt, ws, wd, 0x1b), + static_cast<FRegister>(wd), + static_cast<FRegister>(ws), + static_cast<FRegister>(wt)); +} + +void MipsAssembler::FmsubD(VectorRegister wd, VectorRegister ws, VectorRegister wt) { + CHECK(HasMsa()); + DsFsmInstrFff(EmitMsa3R(0x2, 0x3, wt, ws, wd, 0x1b), + static_cast<FRegister>(wd), + static_cast<FRegister>(ws), + static_cast<FRegister>(wt)); +} + void MipsAssembler::ReplicateFPToVectorRegister(VectorRegister dst, FRegister src, bool is_double) { diff --git a/compiler/utils/mips/assembler_mips.h b/compiler/utils/mips/assembler_mips.h index 33803bb576..a7ff931e7e 100644 --- a/compiler/utils/mips/assembler_mips.h +++ b/compiler/utils/mips/assembler_mips.h @@ -280,6 +280,7 @@ class MipsAssembler FINAL : public Assembler, public JNIMacroAssembler<PointerSi void Lwpc(Register rs, uint32_t imm19); // R6 void Lui(Register rt, uint16_t imm16); void Aui(Register rt, Register rs, uint16_t imm16); // R6 + void AddUpper(Register rt, Register rs, uint16_t imm16, Register tmp = AT); void Sync(uint32_t stype); void Mfhi(Register rd); // R2 void Mflo(Register rd); // R2 @@ -612,6 +613,19 @@ class MipsAssembler FINAL : public Assembler, public JNIMacroAssembler<PointerSi void IlvrW(VectorRegister wd, VectorRegister ws, VectorRegister wt); void IlvrD(VectorRegister wd, VectorRegister ws, VectorRegister wt); + void MaddvB(VectorRegister wd, VectorRegister ws, VectorRegister wt); + void MaddvH(VectorRegister wd, VectorRegister ws, VectorRegister wt); + void MaddvW(VectorRegister wd, VectorRegister ws, VectorRegister wt); + void MaddvD(VectorRegister wd, VectorRegister ws, VectorRegister wt); + void MsubvB(VectorRegister wd, VectorRegister ws, VectorRegister wt); + void MsubvH(VectorRegister wd, VectorRegister ws, VectorRegister wt); + void MsubvW(VectorRegister wd, VectorRegister ws, VectorRegister wt); + void MsubvD(VectorRegister wd, VectorRegister ws, VectorRegister wt); + void FmaddW(VectorRegister wd, VectorRegister ws, VectorRegister wt); + void FmaddD(VectorRegister wd, VectorRegister ws, VectorRegister wt); + void FmsubW(VectorRegister wd, VectorRegister ws, VectorRegister wt); + void FmsubD(VectorRegister wd, VectorRegister ws, VectorRegister wt); + // Helper for replicating floating point value in all destination elements. void ReplicateFPToVectorRegister(VectorRegister dst, FRegister src, bool is_double); diff --git a/compiler/utils/mips/assembler_mips32r6_test.cc b/compiler/utils/mips/assembler_mips32r6_test.cc index 6ee2a5cb79..b72a14e906 100644 --- a/compiler/utils/mips/assembler_mips32r6_test.cc +++ b/compiler/utils/mips/assembler_mips32r6_test.cc @@ -1752,6 +1752,66 @@ TEST_F(AssemblerMIPS32r6Test, IlvrD) { DriverStr(RepeatVVV(&mips::MipsAssembler::IlvrD, "ilvr.d ${reg1}, ${reg2}, ${reg3}"), "ilvr.d"); } +TEST_F(AssemblerMIPS32r6Test, MaddvB) { + DriverStr(RepeatVVV(&mips::MipsAssembler::MaddvB, "maddv.b ${reg1}, ${reg2}, ${reg3}"), + "maddv.b"); +} + +TEST_F(AssemblerMIPS32r6Test, MaddvH) { + DriverStr(RepeatVVV(&mips::MipsAssembler::MaddvH, "maddv.h ${reg1}, ${reg2}, ${reg3}"), + "maddv.h"); +} + +TEST_F(AssemblerMIPS32r6Test, MaddvW) { + DriverStr(RepeatVVV(&mips::MipsAssembler::MaddvW, "maddv.w ${reg1}, ${reg2}, ${reg3}"), + "maddv.w"); +} + +TEST_F(AssemblerMIPS32r6Test, MaddvD) { + DriverStr(RepeatVVV(&mips::MipsAssembler::MaddvD, "maddv.d ${reg1}, ${reg2}, ${reg3}"), + "maddv.d"); +} + +TEST_F(AssemblerMIPS32r6Test, MsubvB) { + DriverStr(RepeatVVV(&mips::MipsAssembler::MsubvB, "msubv.b ${reg1}, ${reg2}, ${reg3}"), + "msubv.b"); +} + +TEST_F(AssemblerMIPS32r6Test, MsubvH) { + DriverStr(RepeatVVV(&mips::MipsAssembler::MsubvH, "msubv.h ${reg1}, ${reg2}, ${reg3}"), + "msubv.h"); +} + +TEST_F(AssemblerMIPS32r6Test, MsubvW) { + DriverStr(RepeatVVV(&mips::MipsAssembler::MsubvW, "msubv.w ${reg1}, ${reg2}, ${reg3}"), + "msubv.w"); +} + +TEST_F(AssemblerMIPS32r6Test, MsubvD) { + DriverStr(RepeatVVV(&mips::MipsAssembler::MsubvD, "msubv.d ${reg1}, ${reg2}, ${reg3}"), + "msubv.d"); +} + +TEST_F(AssemblerMIPS32r6Test, FmaddW) { + DriverStr(RepeatVVV(&mips::MipsAssembler::FmaddW, "fmadd.w ${reg1}, ${reg2}, ${reg3}"), + "fmadd.w"); +} + +TEST_F(AssemblerMIPS32r6Test, FmaddD) { + DriverStr(RepeatVVV(&mips::MipsAssembler::FmaddD, "fmadd.d ${reg1}, ${reg2}, ${reg3}"), + "fmadd.d"); +} + +TEST_F(AssemblerMIPS32r6Test, FmsubW) { + DriverStr(RepeatVVV(&mips::MipsAssembler::FmsubW, "fmsub.w ${reg1}, ${reg2}, ${reg3}"), + "fmsub.w"); +} + +TEST_F(AssemblerMIPS32r6Test, FmsubD) { + DriverStr(RepeatVVV(&mips::MipsAssembler::FmsubD, "fmsub.d ${reg1}, ${reg2}, ${reg3}"), + "fmsub.d"); +} + #undef __ } // namespace art diff --git a/compiler/utils/mips64/assembler_mips64.cc b/compiler/utils/mips64/assembler_mips64.cc index 24900a7f10..7a1beb656b 100644 --- a/compiler/utils/mips64/assembler_mips64.cc +++ b/compiler/utils/mips64/assembler_mips64.cc @@ -795,6 +795,10 @@ void Mips64Assembler::Bc1nez(FpuRegister ft, uint16_t imm16) { EmitFI(0x11, 0xD, ft, imm16); } +void Mips64Assembler::Beqz(GpuRegister rt, uint16_t imm16) { + EmitI(0x4, ZERO, rt, imm16); +} + void Mips64Assembler::EmitBcondc(BranchCondition cond, GpuRegister rs, GpuRegister rt, @@ -1895,6 +1899,66 @@ void Mips64Assembler::IlvrD(VectorRegister wd, VectorRegister ws, VectorRegister EmitMsa3R(0x5, 0x3, wt, ws, wd, 0x14); } +void Mips64Assembler::MaddvB(VectorRegister wd, VectorRegister ws, VectorRegister wt) { + CHECK(HasMsa()); + EmitMsa3R(0x1, 0x0, wt, ws, wd, 0x12); +} + +void Mips64Assembler::MaddvH(VectorRegister wd, VectorRegister ws, VectorRegister wt) { + CHECK(HasMsa()); + EmitMsa3R(0x1, 0x1, wt, ws, wd, 0x12); +} + +void Mips64Assembler::MaddvW(VectorRegister wd, VectorRegister ws, VectorRegister wt) { + CHECK(HasMsa()); + EmitMsa3R(0x1, 0x2, wt, ws, wd, 0x12); +} + +void Mips64Assembler::MaddvD(VectorRegister wd, VectorRegister ws, VectorRegister wt) { + CHECK(HasMsa()); + EmitMsa3R(0x1, 0x3, wt, ws, wd, 0x12); +} + +void Mips64Assembler::MsubvB(VectorRegister wd, VectorRegister ws, VectorRegister wt) { + CHECK(HasMsa()); + EmitMsa3R(0x2, 0x0, wt, ws, wd, 0x12); +} + +void Mips64Assembler::MsubvH(VectorRegister wd, VectorRegister ws, VectorRegister wt) { + CHECK(HasMsa()); + EmitMsa3R(0x2, 0x1, wt, ws, wd, 0x12); +} + +void Mips64Assembler::MsubvW(VectorRegister wd, VectorRegister ws, VectorRegister wt) { + CHECK(HasMsa()); + EmitMsa3R(0x2, 0x2, wt, ws, wd, 0x12); +} + +void Mips64Assembler::MsubvD(VectorRegister wd, VectorRegister ws, VectorRegister wt) { + CHECK(HasMsa()); + EmitMsa3R(0x2, 0x3, wt, ws, wd, 0x12); +} + +void Mips64Assembler::FmaddW(VectorRegister wd, VectorRegister ws, VectorRegister wt) { + CHECK(HasMsa()); + EmitMsa3R(0x2, 0x0, wt, ws, wd, 0x1b); +} + +void Mips64Assembler::FmaddD(VectorRegister wd, VectorRegister ws, VectorRegister wt) { + CHECK(HasMsa()); + EmitMsa3R(0x2, 0x1, wt, ws, wd, 0x1b); +} + +void Mips64Assembler::FmsubW(VectorRegister wd, VectorRegister ws, VectorRegister wt) { + CHECK(HasMsa()); + EmitMsa3R(0x2, 0x2, wt, ws, wd, 0x1b); +} + +void Mips64Assembler::FmsubD(VectorRegister wd, VectorRegister ws, VectorRegister wt) { + CHECK(HasMsa()); + EmitMsa3R(0x2, 0x3, wt, ws, wd, 0x1b); +} + void Mips64Assembler::ReplicateFPToVectorRegister(VectorRegister dst, FpuRegister src, bool is_double) { diff --git a/compiler/utils/mips64/assembler_mips64.h b/compiler/utils/mips64/assembler_mips64.h index 773db9b208..c39d120bce 100644 --- a/compiler/utils/mips64/assembler_mips64.h +++ b/compiler/utils/mips64/assembler_mips64.h @@ -563,6 +563,7 @@ class Mips64Assembler FINAL : public Assembler, public JNIMacroAssembler<Pointer void Bnezc(GpuRegister rs, uint32_t imm21); void Bc1eqz(FpuRegister ft, uint16_t imm16); void Bc1nez(FpuRegister ft, uint16_t imm16); + void Beqz(GpuRegister rt, uint16_t imm16); void AddS(FpuRegister fd, FpuRegister fs, FpuRegister ft); void SubS(FpuRegister fd, FpuRegister fs, FpuRegister ft); @@ -795,6 +796,19 @@ class Mips64Assembler FINAL : public Assembler, public JNIMacroAssembler<Pointer void IlvrW(VectorRegister wd, VectorRegister ws, VectorRegister wt); void IlvrD(VectorRegister wd, VectorRegister ws, VectorRegister wt); + void MaddvB(VectorRegister wd, VectorRegister ws, VectorRegister wt); + void MaddvH(VectorRegister wd, VectorRegister ws, VectorRegister wt); + void MaddvW(VectorRegister wd, VectorRegister ws, VectorRegister wt); + void MaddvD(VectorRegister wd, VectorRegister ws, VectorRegister wt); + void MsubvB(VectorRegister wd, VectorRegister ws, VectorRegister wt); + void MsubvH(VectorRegister wd, VectorRegister ws, VectorRegister wt); + void MsubvW(VectorRegister wd, VectorRegister ws, VectorRegister wt); + void MsubvD(VectorRegister wd, VectorRegister ws, VectorRegister wt); + void FmaddW(VectorRegister wd, VectorRegister ws, VectorRegister wt); + void FmaddD(VectorRegister wd, VectorRegister ws, VectorRegister wt); + void FmsubW(VectorRegister wd, VectorRegister ws, VectorRegister wt); + void FmsubD(VectorRegister wd, VectorRegister ws, VectorRegister wt); + // Helper for replicating floating point value in all destination elements. void ReplicateFPToVectorRegister(VectorRegister dst, FpuRegister src, bool is_double); diff --git a/compiler/utils/mips64/assembler_mips64_test.cc b/compiler/utils/mips64/assembler_mips64_test.cc index bdf9598ee7..021e335697 100644 --- a/compiler/utils/mips64/assembler_mips64_test.cc +++ b/compiler/utils/mips64/assembler_mips64_test.cc @@ -3340,6 +3340,66 @@ TEST_F(AssemblerMIPS64Test, IlvrD) { "ilvr.d"); } +TEST_F(AssemblerMIPS64Test, MaddvB) { + DriverStr(RepeatVVV(&mips64::Mips64Assembler::MaddvB, "maddv.b ${reg1}, ${reg2}, ${reg3}"), + "maddv.b"); +} + +TEST_F(AssemblerMIPS64Test, MaddvH) { + DriverStr(RepeatVVV(&mips64::Mips64Assembler::MaddvH, "maddv.h ${reg1}, ${reg2}, ${reg3}"), + "maddv.h"); +} + +TEST_F(AssemblerMIPS64Test, MaddvW) { + DriverStr(RepeatVVV(&mips64::Mips64Assembler::MaddvW, "maddv.w ${reg1}, ${reg2}, ${reg3}"), + "maddv.w"); +} + +TEST_F(AssemblerMIPS64Test, MaddvD) { + DriverStr(RepeatVVV(&mips64::Mips64Assembler::MaddvD, "maddv.d ${reg1}, ${reg2}, ${reg3}"), + "maddv.d"); +} + +TEST_F(AssemblerMIPS64Test, MsubvB) { + DriverStr(RepeatVVV(&mips64::Mips64Assembler::MsubvB, "msubv.b ${reg1}, ${reg2}, ${reg3}"), + "msubv.b"); +} + +TEST_F(AssemblerMIPS64Test, MsubvH) { + DriverStr(RepeatVVV(&mips64::Mips64Assembler::MsubvH, "msubv.h ${reg1}, ${reg2}, ${reg3}"), + "msubv.h"); +} + +TEST_F(AssemblerMIPS64Test, MsubvW) { + DriverStr(RepeatVVV(&mips64::Mips64Assembler::MsubvW, "msubv.w ${reg1}, ${reg2}, ${reg3}"), + "msubv.w"); +} + +TEST_F(AssemblerMIPS64Test, MsubvD) { + DriverStr(RepeatVVV(&mips64::Mips64Assembler::MsubvD, "msubv.d ${reg1}, ${reg2}, ${reg3}"), + "msubv.d"); +} + +TEST_F(AssemblerMIPS64Test, FmaddW) { + DriverStr(RepeatVVV(&mips64::Mips64Assembler::FmaddW, "fmadd.w ${reg1}, ${reg2}, ${reg3}"), + "fmadd.w"); +} + +TEST_F(AssemblerMIPS64Test, FmaddD) { + DriverStr(RepeatVVV(&mips64::Mips64Assembler::FmaddD, "fmadd.d ${reg1}, ${reg2}, ${reg3}"), + "fmadd.d"); +} + +TEST_F(AssemblerMIPS64Test, FmsubW) { + DriverStr(RepeatVVV(&mips64::Mips64Assembler::FmsubW, "fmsub.w ${reg1}, ${reg2}, ${reg3}"), + "fmsub.w"); +} + +TEST_F(AssemblerMIPS64Test, FmsubD) { + DriverStr(RepeatVVV(&mips64::Mips64Assembler::FmsubD, "fmsub.d ${reg1}, ${reg2}, ${reg3}"), + "fmsub.d"); +} + #undef __ } // namespace art diff --git a/compiler/utils/swap_space.cc b/compiler/utils/swap_space.cc index 621a652f0a..12d113d420 100644 --- a/compiler/utils/swap_space.cc +++ b/compiler/utils/swap_space.cc @@ -16,9 +16,10 @@ #include "swap_space.h" +#include <sys/mman.h> + #include <algorithm> #include <numeric> -#include <sys/mman.h> #include "base/bit_utils.h" #include "base/logging.h" diff --git a/compiler/utils/swap_space.h b/compiler/utils/swap_space.h index 0ff9fc69ed..08e243b644 100644 --- a/compiler/utils/swap_space.h +++ b/compiler/utils/swap_space.h @@ -17,12 +17,12 @@ #ifndef ART_COMPILER_UTILS_SWAP_SPACE_H_ #define ART_COMPILER_UTILS_SWAP_SPACE_H_ +#include <stddef.h> +#include <stdint.h> #include <cstdlib> #include <list> -#include <vector> #include <set> -#include <stdint.h> -#include <stddef.h> +#include <vector> #include "base/logging.h" #include "base/macros.h" diff --git a/compiler/utils/swap_space_test.cc b/compiler/utils/swap_space_test.cc index bf50ac3209..f4bca59cb3 100644 --- a/compiler/utils/swap_space_test.cc +++ b/compiler/utils/swap_space_test.cc @@ -16,10 +16,12 @@ #include "utils/swap_space.h" -#include <cstdio> -#include <sys/types.h> -#include <sys/stat.h> #include <fcntl.h> +#include <sys/stat.h> +#include <sys/types.h> + +#include <cstdio> + #include "gtest/gtest.h" #include "base/unix_file/fd_file.h" diff --git a/compiler/utils/test_dex_file_builder.h b/compiler/utils/test_dex_file_builder.h index 6921780a85..9ba3903033 100644 --- a/compiler/utils/test_dex_file_builder.h +++ b/compiler/utils/test_dex_file_builder.h @@ -17,11 +17,12 @@ #ifndef ART_COMPILER_UTILS_TEST_DEX_FILE_BUILDER_H_ #define ART_COMPILER_UTILS_TEST_DEX_FILE_BUILDER_H_ +#include <zlib.h> + #include <cstring> -#include <set> #include <map> +#include <set> #include <vector> -#include <zlib.h> #include "base/bit_utils.h" #include "base/logging.h" diff --git a/compiler/utils/x86/assembler_x86.cc b/compiler/utils/x86/assembler_x86.cc index bef32f8254..b50f1af8f9 100644 --- a/compiler/utils/x86/assembler_x86.cc +++ b/compiler/utils/x86/assembler_x86.cc @@ -1238,6 +1238,101 @@ void X86Assembler::pavgw(XmmRegister dst, XmmRegister src) { EmitXmmRegisterOperand(dst, src); } + +void X86Assembler::psadbw(XmmRegister dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); + EmitUint8(0x0F); + EmitUint8(0xF6); + EmitXmmRegisterOperand(dst, src); +} + + +void X86Assembler::pmaddwd(XmmRegister dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); + EmitUint8(0x0F); + EmitUint8(0xF5); + EmitXmmRegisterOperand(dst, src); +} + + +void X86Assembler::phaddw(XmmRegister dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); + EmitUint8(0x0F); + EmitUint8(0x38); + EmitUint8(0x01); + EmitXmmRegisterOperand(dst, src); +} + + +void X86Assembler::phaddd(XmmRegister dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); + EmitUint8(0x0F); + EmitUint8(0x38); + EmitUint8(0x02); + EmitXmmRegisterOperand(dst, src); +} + + +void X86Assembler::haddps(XmmRegister dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0xF2); + EmitUint8(0x0F); + EmitUint8(0x7C); + EmitXmmRegisterOperand(dst, src); +} + + +void X86Assembler::haddpd(XmmRegister dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); + EmitUint8(0x0F); + EmitUint8(0x7C); + EmitXmmRegisterOperand(dst, src); +} + + +void X86Assembler::phsubw(XmmRegister dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); + EmitUint8(0x0F); + EmitUint8(0x38); + EmitUint8(0x05); + EmitXmmRegisterOperand(dst, src); +} + + +void X86Assembler::phsubd(XmmRegister dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); + EmitUint8(0x0F); + EmitUint8(0x38); + EmitUint8(0x06); + EmitXmmRegisterOperand(dst, src); +} + + +void X86Assembler::hsubps(XmmRegister dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0xF2); + EmitUint8(0x0F); + EmitUint8(0x7D); + EmitXmmRegisterOperand(dst, src); +} + + +void X86Assembler::hsubpd(XmmRegister dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); + EmitUint8(0x0F); + EmitUint8(0x7D); + EmitXmmRegisterOperand(dst, src); +} + + void X86Assembler::pminsb(XmmRegister dst, XmmRegister src) { AssemblerBuffer::EnsureCapacity ensured(&buffer_); EmitUint8(0x66); diff --git a/compiler/utils/x86/assembler_x86.h b/compiler/utils/x86/assembler_x86.h index c4bb9ee18a..8578340ea7 100644 --- a/compiler/utils/x86/assembler_x86.h +++ b/compiler/utils/x86/assembler_x86.h @@ -497,6 +497,16 @@ class X86Assembler FINAL : public Assembler { void pavgb(XmmRegister dst, XmmRegister src); // no addr variant (for now) void pavgw(XmmRegister dst, XmmRegister src); + void psadbw(XmmRegister dst, XmmRegister src); + void pmaddwd(XmmRegister dst, XmmRegister src); + void phaddw(XmmRegister dst, XmmRegister src); + void phaddd(XmmRegister dst, XmmRegister src); + void haddps(XmmRegister dst, XmmRegister src); + void haddpd(XmmRegister dst, XmmRegister src); + void phsubw(XmmRegister dst, XmmRegister src); + void phsubd(XmmRegister dst, XmmRegister src); + void hsubps(XmmRegister dst, XmmRegister src); + void hsubpd(XmmRegister dst, XmmRegister src); void pminsb(XmmRegister dst, XmmRegister src); // no addr variant (for now) void pmaxsb(XmmRegister dst, XmmRegister src); diff --git a/compiler/utils/x86/assembler_x86_test.cc b/compiler/utils/x86/assembler_x86_test.cc index 34f2a47c27..3e1244ed5d 100644 --- a/compiler/utils/x86/assembler_x86_test.cc +++ b/compiler/utils/x86/assembler_x86_test.cc @@ -613,6 +613,46 @@ TEST_F(AssemblerX86Test, PAvgW) { DriverStr(RepeatFF(&x86::X86Assembler::pavgw, "pavgw %{reg2}, %{reg1}"), "pavgw"); } +TEST_F(AssemblerX86Test, PSadBW) { + DriverStr(RepeatFF(&x86::X86Assembler::psadbw, "psadbw %{reg2}, %{reg1}"), "psadbw"); +} + +TEST_F(AssemblerX86Test, PMAddWD) { + DriverStr(RepeatFF(&x86::X86Assembler::pmaddwd, "pmaddwd %{reg2}, %{reg1}"), "pmaddwd"); +} + +TEST_F(AssemblerX86Test, PHAddW) { + DriverStr(RepeatFF(&x86::X86Assembler::phaddw, "phaddw %{reg2}, %{reg1}"), "phaddw"); +} + +TEST_F(AssemblerX86Test, PHAddD) { + DriverStr(RepeatFF(&x86::X86Assembler::phaddd, "phaddd %{reg2}, %{reg1}"), "phaddd"); +} + +TEST_F(AssemblerX86Test, HAddPS) { + DriverStr(RepeatFF(&x86::X86Assembler::haddps, "haddps %{reg2}, %{reg1}"), "haddps"); +} + +TEST_F(AssemblerX86Test, HAddPD) { + DriverStr(RepeatFF(&x86::X86Assembler::haddpd, "haddpd %{reg2}, %{reg1}"), "haddpd"); +} + +TEST_F(AssemblerX86Test, PHSubW) { + DriverStr(RepeatFF(&x86::X86Assembler::phsubw, "phsubw %{reg2}, %{reg1}"), "phsubw"); +} + +TEST_F(AssemblerX86Test, PHSubD) { + DriverStr(RepeatFF(&x86::X86Assembler::phsubd, "phsubd %{reg2}, %{reg1}"), "phsubd"); +} + +TEST_F(AssemblerX86Test, HSubPS) { + DriverStr(RepeatFF(&x86::X86Assembler::hsubps, "hsubps %{reg2}, %{reg1}"), "hsubps"); +} + +TEST_F(AssemblerX86Test, HSubPD) { + DriverStr(RepeatFF(&x86::X86Assembler::hsubpd, "hsubpd %{reg2}, %{reg1}"), "hsubpd"); +} + TEST_F(AssemblerX86Test, PMinSB) { DriverStr(RepeatFF(&x86::X86Assembler::pminsb, "pminsb %{reg2}, %{reg1}"), "pminsb"); } diff --git a/compiler/utils/x86/jni_macro_assembler_x86.cc b/compiler/utils/x86/jni_macro_assembler_x86.cc index cfdf80ba50..e074346e01 100644 --- a/compiler/utils/x86/jni_macro_assembler_x86.cc +++ b/compiler/utils/x86/jni_macro_assembler_x86.cc @@ -16,10 +16,10 @@ #include "jni_macro_assembler_x86.h" -#include "utils/assembler.h" #include "base/casts.h" #include "entrypoints/quick/quick_entrypoints.h" #include "thread.h" +#include "utils/assembler.h" namespace art { namespace x86 { diff --git a/compiler/utils/x86/managed_register_x86_test.cc b/compiler/utils/x86/managed_register_x86_test.cc index 4fbafdadf9..0ed5c36fe4 100644 --- a/compiler/utils/x86/managed_register_x86_test.cc +++ b/compiler/utils/x86/managed_register_x86_test.cc @@ -14,8 +14,9 @@ * limitations under the License. */ -#include "globals.h" #include "managed_register_x86.h" + +#include "globals.h" #include "gtest/gtest.h" namespace art { diff --git a/compiler/utils/x86_64/assembler_x86_64.cc b/compiler/utils/x86_64/assembler_x86_64.cc index 82d1174a25..ea69a1c9be 100644 --- a/compiler/utils/x86_64/assembler_x86_64.cc +++ b/compiler/utils/x86_64/assembler_x86_64.cc @@ -1445,6 +1445,100 @@ void X86_64Assembler::pavgw(XmmRegister dst, XmmRegister src) { EmitXmmRegisterOperand(dst.LowBits(), src); } +void X86_64Assembler::psadbw(XmmRegister dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); + EmitOptionalRex32(dst, src); + EmitUint8(0x0F); + EmitUint8(0xF6); + EmitXmmRegisterOperand(dst.LowBits(), src); +} + +void X86_64Assembler::pmaddwd(XmmRegister dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); + EmitOptionalRex32(dst, src); + EmitUint8(0x0F); + EmitUint8(0xF5); + EmitXmmRegisterOperand(dst.LowBits(), src); +} + +void X86_64Assembler::phaddw(XmmRegister dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); + EmitOptionalRex32(dst, src); + EmitUint8(0x0F); + EmitUint8(0x38); + EmitUint8(0x01); + EmitXmmRegisterOperand(dst.LowBits(), src); +} + +void X86_64Assembler::phaddd(XmmRegister dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); + EmitOptionalRex32(dst, src); + EmitUint8(0x0F); + EmitUint8(0x38); + EmitUint8(0x02); + EmitXmmRegisterOperand(dst.LowBits(), src); +} + +void X86_64Assembler::haddps(XmmRegister dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0xF2); + EmitOptionalRex32(dst, src); + EmitUint8(0x0F); + EmitUint8(0x7C); + EmitXmmRegisterOperand(dst.LowBits(), src); +} + +void X86_64Assembler::haddpd(XmmRegister dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); + EmitOptionalRex32(dst, src); + EmitUint8(0x0F); + EmitUint8(0x7C); + EmitXmmRegisterOperand(dst.LowBits(), src); +} + +void X86_64Assembler::phsubw(XmmRegister dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); + EmitOptionalRex32(dst, src); + EmitUint8(0x0F); + EmitUint8(0x38); + EmitUint8(0x05); + EmitXmmRegisterOperand(dst.LowBits(), src); +} + +void X86_64Assembler::phsubd(XmmRegister dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); + EmitOptionalRex32(dst, src); + EmitUint8(0x0F); + EmitUint8(0x38); + EmitUint8(0x06); + EmitXmmRegisterOperand(dst.LowBits(), src); +} + +void X86_64Assembler::hsubps(XmmRegister dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0xF2); + EmitOptionalRex32(dst, src); + EmitUint8(0x0F); + EmitUint8(0x7D); + EmitXmmRegisterOperand(dst.LowBits(), src); +} + +void X86_64Assembler::hsubpd(XmmRegister dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); + EmitOptionalRex32(dst, src); + EmitUint8(0x0F); + EmitUint8(0x7D); + EmitXmmRegisterOperand(dst.LowBits(), src); +} + void X86_64Assembler::pminsb(XmmRegister dst, XmmRegister src) { AssemblerBuffer::EnsureCapacity ensured(&buffer_); EmitUint8(0x66); diff --git a/compiler/utils/x86_64/assembler_x86_64.h b/compiler/utils/x86_64/assembler_x86_64.h index 6e584fece1..41450bff4f 100644 --- a/compiler/utils/x86_64/assembler_x86_64.h +++ b/compiler/utils/x86_64/assembler_x86_64.h @@ -525,6 +525,16 @@ class X86_64Assembler FINAL : public Assembler { void pavgb(XmmRegister dst, XmmRegister src); // no addr variant (for now) void pavgw(XmmRegister dst, XmmRegister src); + void psadbw(XmmRegister dst, XmmRegister src); + void pmaddwd(XmmRegister dst, XmmRegister src); + void phaddw(XmmRegister dst, XmmRegister src); + void phaddd(XmmRegister dst, XmmRegister src); + void haddps(XmmRegister dst, XmmRegister src); + void haddpd(XmmRegister dst, XmmRegister src); + void phsubw(XmmRegister dst, XmmRegister src); + void phsubd(XmmRegister dst, XmmRegister src); + void hsubps(XmmRegister dst, XmmRegister src); + void hsubpd(XmmRegister dst, XmmRegister src); void pminsb(XmmRegister dst, XmmRegister src); // no addr variant (for now) void pmaxsb(XmmRegister dst, XmmRegister src); diff --git a/compiler/utils/x86_64/assembler_x86_64_test.cc b/compiler/utils/x86_64/assembler_x86_64_test.cc index b57400334c..ec14e7a825 100644 --- a/compiler/utils/x86_64/assembler_x86_64_test.cc +++ b/compiler/utils/x86_64/assembler_x86_64_test.cc @@ -1301,6 +1301,46 @@ TEST_F(AssemblerX86_64Test, Pavgw) { DriverStr(RepeatFF(&x86_64::X86_64Assembler::pavgw, "pavgw %{reg2}, %{reg1}"), "pavgw"); } +TEST_F(AssemblerX86_64Test, Psadbw) { + DriverStr(RepeatFF(&x86_64::X86_64Assembler::psadbw, "psadbw %{reg2}, %{reg1}"), "psadbw"); +} + +TEST_F(AssemblerX86_64Test, Pmaddwd) { + DriverStr(RepeatFF(&x86_64::X86_64Assembler::pmaddwd, "pmaddwd %{reg2}, %{reg1}"), "pmadwd"); +} + +TEST_F(AssemblerX86_64Test, Phaddw) { + DriverStr(RepeatFF(&x86_64::X86_64Assembler::phaddw, "phaddw %{reg2}, %{reg1}"), "phaddw"); +} + +TEST_F(AssemblerX86_64Test, Phaddd) { + DriverStr(RepeatFF(&x86_64::X86_64Assembler::phaddd, "phaddd %{reg2}, %{reg1}"), "phaddd"); +} + +TEST_F(AssemblerX86_64Test, Haddps) { + DriverStr(RepeatFF(&x86_64::X86_64Assembler::haddps, "haddps %{reg2}, %{reg1}"), "haddps"); +} + +TEST_F(AssemblerX86_64Test, Haddpd) { + DriverStr(RepeatFF(&x86_64::X86_64Assembler::haddpd, "haddpd %{reg2}, %{reg1}"), "haddpd"); +} + +TEST_F(AssemblerX86_64Test, Phsubw) { + DriverStr(RepeatFF(&x86_64::X86_64Assembler::phsubw, "phsubw %{reg2}, %{reg1}"), "phsubw"); +} + +TEST_F(AssemblerX86_64Test, Phsubd) { + DriverStr(RepeatFF(&x86_64::X86_64Assembler::phsubd, "phsubd %{reg2}, %{reg1}"), "phsubd"); +} + +TEST_F(AssemblerX86_64Test, Hsubps) { + DriverStr(RepeatFF(&x86_64::X86_64Assembler::hsubps, "hsubps %{reg2}, %{reg1}"), "hsubps"); +} + +TEST_F(AssemblerX86_64Test, Hsubpd) { + DriverStr(RepeatFF(&x86_64::X86_64Assembler::hsubpd, "hsubpd %{reg2}, %{reg1}"), "hsubpd"); +} + TEST_F(AssemblerX86_64Test, Pminsb) { DriverStr(RepeatFF(&x86_64::X86_64Assembler::pminsb, "pminsb %{reg2}, %{reg1}"), "pminsb"); } diff --git a/compiler/utils/x86_64/managed_register_x86_64_test.cc b/compiler/utils/x86_64/managed_register_x86_64_test.cc index 2dc7581472..e43d717385 100644 --- a/compiler/utils/x86_64/managed_register_x86_64_test.cc +++ b/compiler/utils/x86_64/managed_register_x86_64_test.cc @@ -14,8 +14,8 @@ * limitations under the License. */ -#include "globals.h" #include "managed_register_x86_64.h" +#include "globals.h" #include "gtest/gtest.h" namespace art { diff --git a/compiler/verifier_deps_test.cc b/compiler/verifier_deps_test.cc index 686da2136f..72e2a6ce9f 100644 --- a/compiler/verifier_deps_test.cc +++ b/compiler/verifier_deps_test.cc @@ -155,13 +155,14 @@ class VerifierDepsTest : public CommonCompilerTest { ArtMethod* method = nullptr; while (it.HasNextDirectMethod()) { - ArtMethod* resolved_method = class_linker_->ResolveMethod<ClassLinker::kNoICCECheckForCache>( - *primary_dex_file_, - it.GetMemberIndex(), - dex_cache_handle, - class_loader_handle, - nullptr, - it.GetMethodInvokeType(*class_def)); + ArtMethod* resolved_method = + class_linker_->ResolveMethod<ClassLinker::ResolveMode::kNoChecks>( + *primary_dex_file_, + it.GetMemberIndex(), + dex_cache_handle, + class_loader_handle, + nullptr, + it.GetMethodInvokeType(*class_def)); CHECK(resolved_method != nullptr); if (method_name == resolved_method->GetName()) { method = resolved_method; @@ -369,18 +370,14 @@ class VerifierDepsTest : public CommonCompilerTest { // Iterates over all method resolution records, finds an entry which matches // the given field kind+class+name+signature and tests its properties. - bool HasMethod(const std::string& expected_kind, - const std::string& expected_klass, + bool HasMethod(const std::string& expected_klass, const std::string& expected_name, const std::string& expected_signature, bool expected_resolved, const std::string& expected_access_flags = "", const std::string& expected_decl_klass = "") { for (auto& dex_dep : verifier_deps_->dex_deps_) { - auto& storage = (expected_kind == "direct") ? dex_dep.second->direct_methods_ - : (expected_kind == "virtual") ? dex_dep.second->virtual_methods_ - : dex_dep.second->interface_methods_; - for (auto& entry : storage) { + for (const VerifierDeps::MethodResolution& entry : dex_dep.second->methods_) { if (expected_resolved != entry.IsResolved()) { continue; } @@ -441,9 +438,7 @@ class VerifierDepsTest : public CommonCompilerTest { has_assignability |= !entry.second->unassignable_types_.empty(); has_classes |= !entry.second->classes_.empty(); has_fields |= !entry.second->fields_.empty(); - has_methods |= !entry.second->direct_methods_.empty(); - has_methods |= !entry.second->virtual_methods_.empty(); - has_methods |= !entry.second->interface_methods_.empty(); + has_methods |= !entry.second->methods_.empty(); has_unverified_classes |= !entry.second->unverified_classes_.empty(); } @@ -455,18 +450,6 @@ class VerifierDepsTest : public CommonCompilerTest { has_unverified_classes; } - static std::set<VerifierDeps::MethodResolution>* GetMethods( - VerifierDeps::DexFileDeps* deps, MethodResolutionKind resolution_kind) { - if (resolution_kind == kDirectMethodResolution) { - return &deps->direct_methods_; - } else if (resolution_kind == kVirtualMethodResolution) { - return &deps->virtual_methods_; - } else { - DCHECK_EQ(resolution_kind, kInterfaceMethodResolution); - return &deps->interface_methods_; - } - } - std::unique_ptr<verifier::VerifierDeps> verifier_deps_; std::vector<const DexFile*> dex_files_; const DexFile* primary_dex_file_; @@ -604,11 +587,10 @@ TEST_F(VerifierDepsTest, InvokeArgumentType) { ASSERT_TRUE(VerifyMethod("InvokeArgumentType")); ASSERT_TRUE(HasClass("Ljava/text/SimpleDateFormat;", true, "public")); ASSERT_TRUE(HasClass("Ljava/util/SimpleTimeZone;", true, "public")); - ASSERT_TRUE(HasMethod("virtual", - "Ljava/text/SimpleDateFormat;", + ASSERT_TRUE(HasMethod("Ljava/text/SimpleDateFormat;", "setTimeZone", "(Ljava/util/TimeZone;)V", - true, + /* expect_resolved */ true, "public", "Ljava/text/DateFormat;")); ASSERT_TRUE(HasAssignable("Ljava/util/TimeZone;", "Ljava/util/SimpleTimeZone;", true)); @@ -840,11 +822,10 @@ TEST_F(VerifierDepsTest, InstanceField_Unresolved_ReferrerInDex) { TEST_F(VerifierDepsTest, InvokeStatic_Resolved_DeclaredInReferenced) { ASSERT_TRUE(VerifyMethod("InvokeStatic_Resolved_DeclaredInReferenced")); ASSERT_TRUE(HasClass("Ljava/net/Socket;", true, "public")); - ASSERT_TRUE(HasMethod("direct", - "Ljava/net/Socket;", + ASSERT_TRUE(HasMethod("Ljava/net/Socket;", "setSocketImplFactory", "(Ljava/net/SocketImplFactory;)V", - true, + /* expect_resolved */ true, "public static", "Ljava/net/Socket;")); } @@ -852,22 +833,20 @@ TEST_F(VerifierDepsTest, InvokeStatic_Resolved_DeclaredInReferenced) { TEST_F(VerifierDepsTest, InvokeStatic_Resolved_DeclaredInSuperclass1) { ASSERT_TRUE(VerifyMethod("InvokeStatic_Resolved_DeclaredInSuperclass1")); ASSERT_TRUE(HasClass("Ljavax/net/ssl/SSLSocket;", true, "public")); - ASSERT_TRUE(HasMethod("direct", - "Ljavax/net/ssl/SSLSocket;", + ASSERT_TRUE(HasMethod("Ljavax/net/ssl/SSLSocket;", "setSocketImplFactory", "(Ljava/net/SocketImplFactory;)V", - true, + /* expect_resolved */ true, "public static", "Ljava/net/Socket;")); } TEST_F(VerifierDepsTest, InvokeStatic_Resolved_DeclaredInSuperclass2) { ASSERT_TRUE(VerifyMethod("InvokeStatic_Resolved_DeclaredInSuperclass2")); - ASSERT_TRUE(HasMethod("direct", - "LMySSLSocket;", + ASSERT_TRUE(HasMethod("LMySSLSocket;", "setSocketImplFactory", "(Ljava/net/SocketImplFactory;)V", - true, + /* expect_resolved */ true, "public static", "Ljava/net/Socket;")); } @@ -875,11 +854,10 @@ TEST_F(VerifierDepsTest, InvokeStatic_Resolved_DeclaredInSuperclass2) { TEST_F(VerifierDepsTest, InvokeStatic_DeclaredInInterface1) { ASSERT_TRUE(VerifyMethod("InvokeStatic_DeclaredInInterface1")); ASSERT_TRUE(HasClass("Ljava/util/Map$Entry;", true, "public interface")); - ASSERT_TRUE(HasMethod("direct", - "Ljava/util/Map$Entry;", + ASSERT_TRUE(HasMethod("Ljava/util/Map$Entry;", "comparingByKey", "()Ljava/util/Comparator;", - true, + /* expect_resolved */ true, "public static", "Ljava/util/Map$Entry;")); } @@ -887,68 +865,85 @@ TEST_F(VerifierDepsTest, InvokeStatic_DeclaredInInterface1) { TEST_F(VerifierDepsTest, InvokeStatic_DeclaredInInterface2) { ASSERT_FALSE(VerifyMethod("InvokeStatic_DeclaredInInterface2")); ASSERT_TRUE(HasClass("Ljava/util/AbstractMap$SimpleEntry;", true, "public")); - ASSERT_TRUE(HasMethod("direct", - "Ljava/util/AbstractMap$SimpleEntry;", + ASSERT_TRUE(HasMethod("Ljava/util/AbstractMap$SimpleEntry;", "comparingByKey", "()Ljava/util/Comparator;", - false)); + /* expect_resolved */ false)); } TEST_F(VerifierDepsTest, InvokeStatic_Unresolved1) { ASSERT_FALSE(VerifyMethod("InvokeStatic_Unresolved1")); ASSERT_TRUE(HasClass("Ljavax/net/ssl/SSLSocket;", true, "public")); - ASSERT_TRUE(HasMethod("direct", "Ljavax/net/ssl/SSLSocket;", "x", "()V", false)); + ASSERT_TRUE(HasMethod("Ljavax/net/ssl/SSLSocket;", + "x", + "()V", + /* expect_resolved */ false)); } TEST_F(VerifierDepsTest, InvokeStatic_Unresolved2) { ASSERT_FALSE(VerifyMethod("InvokeStatic_Unresolved2")); - ASSERT_TRUE(HasMethod("direct", "LMySSLSocket;", "x", "()V", false)); + ASSERT_TRUE(HasMethod("LMySSLSocket;", + "x", + "()V", + /* expect_resolved */ false)); } TEST_F(VerifierDepsTest, InvokeDirect_Resolved_DeclaredInReferenced) { ASSERT_TRUE(VerifyMethod("InvokeDirect_Resolved_DeclaredInReferenced")); ASSERT_TRUE(HasClass("Ljava/net/Socket;", true, "public")); - ASSERT_TRUE(HasMethod( - "direct", "Ljava/net/Socket;", "<init>", "()V", true, "public", "Ljava/net/Socket;")); + ASSERT_TRUE(HasMethod("Ljava/net/Socket;", + "<init>", + "()V", + /* expect_resolved */ true, + "public", + "Ljava/net/Socket;")); } TEST_F(VerifierDepsTest, InvokeDirect_Resolved_DeclaredInSuperclass1) { ASSERT_FALSE(VerifyMethod("InvokeDirect_Resolved_DeclaredInSuperclass1")); ASSERT_TRUE(HasClass("Ljavax/net/ssl/SSLSocket;", true, "public")); - ASSERT_TRUE(HasMethod("direct", - "Ljavax/net/ssl/SSLSocket;", + ASSERT_TRUE(HasMethod("Ljavax/net/ssl/SSLSocket;", "checkOldImpl", "()V", - true, + /* expect_resolved */ true, "private", "Ljava/net/Socket;")); } TEST_F(VerifierDepsTest, InvokeDirect_Resolved_DeclaredInSuperclass2) { ASSERT_FALSE(VerifyMethod("InvokeDirect_Resolved_DeclaredInSuperclass2")); - ASSERT_TRUE(HasMethod( - "direct", "LMySSLSocket;", "checkOldImpl", "()V", true, "private", "Ljava/net/Socket;")); + ASSERT_TRUE(HasMethod("LMySSLSocket;", + "checkOldImpl", + "()V", + /* expect_resolved */ true, + "private", + "Ljava/net/Socket;")); } TEST_F(VerifierDepsTest, InvokeDirect_Unresolved1) { ASSERT_FALSE(VerifyMethod("InvokeDirect_Unresolved1")); ASSERT_TRUE(HasClass("Ljavax/net/ssl/SSLSocket;", true, "public")); - ASSERT_TRUE(HasMethod("direct", "Ljavax/net/ssl/SSLSocket;", "x", "()V", false)); + ASSERT_TRUE(HasMethod("Ljavax/net/ssl/SSLSocket;", + "x", + "()V", + /* expect_resolved */ false)); } TEST_F(VerifierDepsTest, InvokeDirect_Unresolved2) { ASSERT_FALSE(VerifyMethod("InvokeDirect_Unresolved2")); - ASSERT_TRUE(HasMethod("direct", "LMySSLSocket;", "x", "()V", false)); + ASSERT_TRUE(HasMethod("LMySSLSocket;", + "x", + "()V", + /* expect_resolved */ false)); } TEST_F(VerifierDepsTest, InvokeVirtual_Resolved_DeclaredInReferenced) { ASSERT_TRUE(VerifyMethod("InvokeVirtual_Resolved_DeclaredInReferenced")); ASSERT_TRUE(HasClass("Ljava/lang/Throwable;", true, "public")); - ASSERT_TRUE(HasMethod("virtual", - "Ljava/lang/Throwable;", + ASSERT_TRUE(HasMethod("Ljava/lang/Throwable;", "getMessage", "()Ljava/lang/String;", - true, + /* expect_resolved */ true, "public", "Ljava/lang/Throwable;")); // Type dependency on `this` argument. @@ -958,11 +953,10 @@ TEST_F(VerifierDepsTest, InvokeVirtual_Resolved_DeclaredInReferenced) { TEST_F(VerifierDepsTest, InvokeVirtual_Resolved_DeclaredInSuperclass1) { ASSERT_TRUE(VerifyMethod("InvokeVirtual_Resolved_DeclaredInSuperclass1")); ASSERT_TRUE(HasClass("Ljava/io/InterruptedIOException;", true, "public")); - ASSERT_TRUE(HasMethod("virtual", - "Ljava/io/InterruptedIOException;", + ASSERT_TRUE(HasMethod("Ljava/io/InterruptedIOException;", "getMessage", "()Ljava/lang/String;", - true, + /* expect_resolved */ true, "public", "Ljava/lang/Throwable;")); // Type dependency on `this` argument. @@ -971,22 +965,20 @@ TEST_F(VerifierDepsTest, InvokeVirtual_Resolved_DeclaredInSuperclass1) { TEST_F(VerifierDepsTest, InvokeVirtual_Resolved_DeclaredInSuperclass2) { ASSERT_TRUE(VerifyMethod("InvokeVirtual_Resolved_DeclaredInSuperclass2")); - ASSERT_TRUE(HasMethod("virtual", - "LMySocketTimeoutException;", + ASSERT_TRUE(HasMethod("LMySocketTimeoutException;", "getMessage", "()Ljava/lang/String;", - true, + /* expect_resolved */ true, "public", "Ljava/lang/Throwable;")); } TEST_F(VerifierDepsTest, InvokeVirtual_Resolved_DeclaredInSuperinterface) { ASSERT_TRUE(VerifyMethod("InvokeVirtual_Resolved_DeclaredInSuperinterface")); - ASSERT_TRUE(HasMethod("virtual", - "LMyThreadSet;", + ASSERT_TRUE(HasMethod("LMyThreadSet;", "size", "()I", - true, + /* expect_resolved */ true, "public", "Ljava/util/Set;")); } @@ -994,61 +986,59 @@ TEST_F(VerifierDepsTest, InvokeVirtual_Resolved_DeclaredInSuperinterface) { TEST_F(VerifierDepsTest, InvokeVirtual_Unresolved1) { ASSERT_FALSE(VerifyMethod("InvokeVirtual_Unresolved1")); ASSERT_TRUE(HasClass("Ljava/io/InterruptedIOException;", true, "public")); - ASSERT_TRUE(HasMethod("virtual", "Ljava/io/InterruptedIOException;", "x", "()V", false)); + ASSERT_TRUE(HasMethod("Ljava/io/InterruptedIOException;", + "x", + "()V", + /* expect_resolved */ false)); } TEST_F(VerifierDepsTest, InvokeVirtual_Unresolved2) { ASSERT_FALSE(VerifyMethod("InvokeVirtual_Unresolved2")); - ASSERT_TRUE(HasMethod("virtual", "LMySocketTimeoutException;", "x", "()V", false)); -} - -TEST_F(VerifierDepsTest, InvokeVirtual_ActuallyDirect) { - ASSERT_FALSE(VerifyMethod("InvokeVirtual_ActuallyDirect")); - ASSERT_TRUE(HasMethod("virtual", "LMyThread;", "activeCount", "()I", false)); - ASSERT_TRUE(HasMethod("direct", - "LMyThread;", - "activeCount", - "()I", - true, - "public static", - "Ljava/lang/Thread;")); + ASSERT_TRUE(HasMethod("LMySocketTimeoutException;", + "x", + "()V", + /* expect_resolved */ false)); } TEST_F(VerifierDepsTest, InvokeInterface_Resolved_DeclaredInReferenced) { ASSERT_TRUE(VerifyMethod("InvokeInterface_Resolved_DeclaredInReferenced")); ASSERT_TRUE(HasClass("Ljava/lang/Runnable;", true, "public interface")); - ASSERT_TRUE(HasMethod("interface", - "Ljava/lang/Runnable;", + ASSERT_TRUE(HasMethod("Ljava/lang/Runnable;", "run", "()V", - true, + /* expect_resolved */ true, "public", "Ljava/lang/Runnable;")); } TEST_F(VerifierDepsTest, InvokeInterface_Resolved_DeclaredInSuperclass) { ASSERT_FALSE(VerifyMethod("InvokeInterface_Resolved_DeclaredInSuperclass")); - ASSERT_TRUE(HasMethod("interface", "LMyThread;", "join", "()V", false)); + // TODO: Maybe we should not record dependency if the invoke type does not match the lookup type. + ASSERT_TRUE(HasMethod("LMyThread;", + "join", + "()V", + /* expect_resolved */ true, + "public", + "Ljava/lang/Thread;")); } TEST_F(VerifierDepsTest, InvokeInterface_Resolved_DeclaredInSuperinterface1) { ASSERT_FALSE(VerifyMethod("InvokeInterface_Resolved_DeclaredInSuperinterface1")); - ASSERT_TRUE(HasMethod("interface", - "LMyThreadSet;", + // TODO: Maybe we should not record dependency if the invoke type does not match the lookup type. + ASSERT_TRUE(HasMethod("LMyThreadSet;", "run", "()V", - true, + /* expect_resolved */ true, "public", - "Ljava/lang/Runnable;")); + "Ljava/lang/Thread;")); } TEST_F(VerifierDepsTest, InvokeInterface_Resolved_DeclaredInSuperinterface2) { ASSERT_FALSE(VerifyMethod("InvokeInterface_Resolved_DeclaredInSuperinterface2")); - ASSERT_TRUE(HasMethod("interface", - "LMyThreadSet;", + ASSERT_TRUE(HasMethod("LMyThreadSet;", "isEmpty", "()Z", - true, + /* expect_resolved */ true, "public", "Ljava/util/Set;")); } @@ -1056,23 +1046,25 @@ TEST_F(VerifierDepsTest, InvokeInterface_Resolved_DeclaredInSuperinterface2) { TEST_F(VerifierDepsTest, InvokeInterface_Unresolved1) { ASSERT_FALSE(VerifyMethod("InvokeInterface_Unresolved1")); ASSERT_TRUE(HasClass("Ljava/lang/Runnable;", true, "public interface")); - ASSERT_TRUE(HasMethod("interface", "Ljava/lang/Runnable;", "x", "()V", false)); + ASSERT_TRUE(HasMethod("Ljava/lang/Runnable;", + "x", + "()V", + /* expect_resolved */ false)); } TEST_F(VerifierDepsTest, InvokeInterface_Unresolved2) { ASSERT_FALSE(VerifyMethod("InvokeInterface_Unresolved2")); - ASSERT_TRUE(HasMethod("interface", "LMyThreadSet;", "x", "()V", false)); + ASSERT_TRUE(HasMethod("LMyThreadSet;", "x", "()V", /* expect_resolved */ false)); } TEST_F(VerifierDepsTest, InvokeSuper_ThisAssignable) { ASSERT_TRUE(VerifyMethod("InvokeSuper_ThisAssignable")); ASSERT_TRUE(HasClass("Ljava/lang/Runnable;", true, "public interface")); ASSERT_TRUE(HasAssignable("Ljava/lang/Runnable;", "Ljava/lang/Thread;", true)); - ASSERT_TRUE(HasMethod("interface", - "Ljava/lang/Runnable;", + ASSERT_TRUE(HasMethod("Ljava/lang/Runnable;", "run", "()V", - true, + /* expect_resolved */ true, "public", "Ljava/lang/Runnable;")); } @@ -1081,8 +1073,10 @@ TEST_F(VerifierDepsTest, InvokeSuper_ThisNotAssignable) { ASSERT_FALSE(VerifyMethod("InvokeSuper_ThisNotAssignable")); ASSERT_TRUE(HasClass("Ljava/lang/Integer;", true, "public")); ASSERT_TRUE(HasAssignable("Ljava/lang/Integer;", "Ljava/lang/Thread;", false)); - ASSERT_TRUE(HasMethod( - "virtual", "Ljava/lang/Integer;", "intValue", "()I", true, "public", "Ljava/lang/Integer;")); + ASSERT_TRUE(HasMethod("Ljava/lang/Integer;", + "intValue", "()I", + /* expect_resolved */ true, + "public", "Ljava/lang/Integer;")); } TEST_F(VerifierDepsTest, ArgumentType_ResolvedReferenceArray) { @@ -1150,18 +1144,6 @@ TEST_F(VerifierDepsTest, UnverifiedClasses) { ASSERT_TRUE(HasUnverifiedClass("LMyClassWithNoSuperButFailures;")); } -// Returns the next resolution kind in the enum. -static MethodResolutionKind GetNextResolutionKind(MethodResolutionKind resolution_kind) { - if (resolution_kind == kDirectMethodResolution) { - return kVirtualMethodResolution; - } else if (resolution_kind == kVirtualMethodResolution) { - return kInterfaceMethodResolution; - } else { - DCHECK_EQ(resolution_kind, kInterfaceMethodResolution); - return kDirectMethodResolution; - } -} - TEST_F(VerifierDepsTest, VerifyDeps) { VerifyDexFile(); @@ -1338,131 +1320,82 @@ TEST_F(VerifierDepsTest, VerifyDeps) { } // Mess up with methods. - for (MethodResolutionKind resolution_kind : - { kDirectMethodResolution, kVirtualMethodResolution, kInterfaceMethodResolution }) { - { - VerifierDeps decoded_deps(dex_files_, ArrayRef<const uint8_t>(buffer)); - VerifierDeps::DexFileDeps* deps = decoded_deps.GetDexFileDeps(*primary_dex_file_); - bool found = false; - std::set<VerifierDeps::MethodResolution>* methods = GetMethods(deps, resolution_kind); - for (const auto& entry : *methods) { - if (entry.IsResolved()) { - methods->insert(VerifierDeps::MethodResolution(entry.GetDexMethodIndex(), - VerifierDeps::kUnresolvedMarker, - entry.GetDeclaringClassIndex())); - found = true; - break; - } - } - ASSERT_TRUE(found); - new_class_loader.Assign(soa.Decode<mirror::ClassLoader>(LoadDex("VerifierDeps"))); - ASSERT_FALSE(decoded_deps.ValidateDependencies(new_class_loader, soa.Self())); - } - - { - VerifierDeps decoded_deps(dex_files_, ArrayRef<const uint8_t>(buffer)); - VerifierDeps::DexFileDeps* deps = decoded_deps.GetDexFileDeps(*primary_dex_file_); - bool found = false; - std::set<VerifierDeps::MethodResolution>* methods = GetMethods(deps, resolution_kind); - for (const auto& entry : *methods) { - if (!entry.IsResolved()) { - constexpr dex::StringIndex kStringIndexZero(0); // We know there is a class there. - methods->insert(VerifierDeps::MethodResolution(0 /* we know there is a method there */, - VerifierDeps::kUnresolvedMarker - 1, - kStringIndexZero)); - found = true; - break; - } - } - ASSERT_TRUE(found); - new_class_loader.Assign(soa.Decode<mirror::ClassLoader>(LoadDex("VerifierDeps"))); - ASSERT_FALSE(decoded_deps.ValidateDependencies(new_class_loader, soa.Self())); - } - - { - VerifierDeps decoded_deps(dex_files_, ArrayRef<const uint8_t>(buffer)); - VerifierDeps::DexFileDeps* deps = decoded_deps.GetDexFileDeps(*primary_dex_file_); - bool found = false; - std::set<VerifierDeps::MethodResolution>* methods = GetMethods(deps, resolution_kind); - for (const auto& entry : *methods) { - if (entry.IsResolved()) { - methods->insert(VerifierDeps::MethodResolution(entry.GetDexMethodIndex(), - entry.GetAccessFlags() - 1, - entry.GetDeclaringClassIndex())); - found = true; - break; - } + { + VerifierDeps decoded_deps(dex_files_, ArrayRef<const uint8_t>(buffer)); + VerifierDeps::DexFileDeps* deps = decoded_deps.GetDexFileDeps(*primary_dex_file_); + bool found = false; + std::set<VerifierDeps::MethodResolution>* methods = &deps->methods_; + for (const auto& entry : *methods) { + if (entry.IsResolved()) { + methods->insert(VerifierDeps::MethodResolution(entry.GetDexMethodIndex(), + VerifierDeps::kUnresolvedMarker, + entry.GetDeclaringClassIndex())); + found = true; + break; } - ASSERT_TRUE(found); - new_class_loader.Assign(soa.Decode<mirror::ClassLoader>(LoadDex("VerifierDeps"))); - ASSERT_FALSE(decoded_deps.ValidateDependencies(new_class_loader, soa.Self())); } + ASSERT_TRUE(found); + new_class_loader.Assign(soa.Decode<mirror::ClassLoader>(LoadDex("VerifierDeps"))); + ASSERT_FALSE(decoded_deps.ValidateDependencies(new_class_loader, soa.Self())); + } - { - VerifierDeps decoded_deps(dex_files_, ArrayRef<const uint8_t>(buffer)); - VerifierDeps::DexFileDeps* deps = decoded_deps.GetDexFileDeps(*primary_dex_file_); - bool found = false; - std::set<VerifierDeps::MethodResolution>* methods = GetMethods(deps, resolution_kind); - for (const auto& entry : *methods) { - constexpr dex::StringIndex kNewTypeIndex(0); - if (entry.IsResolved() && entry.GetDeclaringClassIndex() != kNewTypeIndex) { - methods->insert(VerifierDeps::MethodResolution(entry.GetDexMethodIndex(), - entry.GetAccessFlags(), - kNewTypeIndex)); - found = true; - break; - } + { + VerifierDeps decoded_deps(dex_files_, ArrayRef<const uint8_t>(buffer)); + VerifierDeps::DexFileDeps* deps = decoded_deps.GetDexFileDeps(*primary_dex_file_); + bool found = false; + std::set<VerifierDeps::MethodResolution>* methods = &deps->methods_; + for (const auto& entry : *methods) { + if (!entry.IsResolved()) { + constexpr dex::StringIndex kStringIndexZero(0); // We know there is a class there. + methods->insert(VerifierDeps::MethodResolution(0 /* we know there is a method there */, + VerifierDeps::kUnresolvedMarker - 1, + kStringIndexZero)); + found = true; + break; } - ASSERT_TRUE(found); - new_class_loader.Assign(soa.Decode<mirror::ClassLoader>(LoadDex("VerifierDeps"))); - ASSERT_FALSE(decoded_deps.ValidateDependencies(new_class_loader, soa.Self())); } + ASSERT_TRUE(found); + new_class_loader.Assign(soa.Decode<mirror::ClassLoader>(LoadDex("VerifierDeps"))); + ASSERT_FALSE(decoded_deps.ValidateDependencies(new_class_loader, soa.Self())); + } - // The two tests below make sure that fiddling with the method kind - // (static, virtual, interface) is detected by `ValidateDependencies`. - - // An interface method lookup can succeed with a virtual method lookup on the same class. - // That's OK, as we only want to make sure there is a method being defined with the right - // flags. Therefore, polluting the interface methods with virtual methods does not have - // to fail verification. - if (resolution_kind != kVirtualMethodResolution) { - VerifierDeps decoded_deps(dex_files_, ArrayRef<const uint8_t>(buffer)); - VerifierDeps::DexFileDeps* deps = decoded_deps.GetDexFileDeps(*primary_dex_file_); - bool found = false; - std::set<VerifierDeps::MethodResolution>* methods = GetMethods(deps, resolution_kind); - for (const auto& entry : *methods) { - if (entry.IsResolved()) { - GetMethods(deps, GetNextResolutionKind(resolution_kind))->insert( - VerifierDeps::MethodResolution(entry.GetDexMethodIndex(), - entry.GetAccessFlags(), - entry.GetDeclaringClassIndex())); - found = true; - } + { + VerifierDeps decoded_deps(dex_files_, ArrayRef<const uint8_t>(buffer)); + VerifierDeps::DexFileDeps* deps = decoded_deps.GetDexFileDeps(*primary_dex_file_); + bool found = false; + std::set<VerifierDeps::MethodResolution>* methods = &deps->methods_; + for (const auto& entry : *methods) { + if (entry.IsResolved()) { + methods->insert(VerifierDeps::MethodResolution(entry.GetDexMethodIndex(), + entry.GetAccessFlags() - 1, + entry.GetDeclaringClassIndex())); + found = true; + break; } - ASSERT_TRUE(found); - new_class_loader.Assign(soa.Decode<mirror::ClassLoader>(LoadDex("VerifierDeps"))); - ASSERT_FALSE(decoded_deps.ValidateDependencies(new_class_loader, soa.Self())); } + ASSERT_TRUE(found); + new_class_loader.Assign(soa.Decode<mirror::ClassLoader>(LoadDex("VerifierDeps"))); + ASSERT_FALSE(decoded_deps.ValidateDependencies(new_class_loader, soa.Self())); + } - // See comment above that applies the same way. - if (resolution_kind != kInterfaceMethodResolution) { - VerifierDeps decoded_deps(dex_files_, ArrayRef<const uint8_t>(buffer)); - VerifierDeps::DexFileDeps* deps = decoded_deps.GetDexFileDeps(*primary_dex_file_); - bool found = false; - std::set<VerifierDeps::MethodResolution>* methods = GetMethods(deps, resolution_kind); - for (const auto& entry : *methods) { - if (entry.IsResolved()) { - GetMethods(deps, GetNextResolutionKind(GetNextResolutionKind(resolution_kind)))->insert( - VerifierDeps::MethodResolution(entry.GetDexMethodIndex(), - entry.GetAccessFlags(), - entry.GetDeclaringClassIndex())); - found = true; - } + { + VerifierDeps decoded_deps(dex_files_, ArrayRef<const uint8_t>(buffer)); + VerifierDeps::DexFileDeps* deps = decoded_deps.GetDexFileDeps(*primary_dex_file_); + bool found = false; + std::set<VerifierDeps::MethodResolution>* methods = &deps->methods_; + for (const auto& entry : *methods) { + constexpr dex::StringIndex kNewTypeIndex(0); + if (entry.IsResolved() && entry.GetDeclaringClassIndex() != kNewTypeIndex) { + methods->insert(VerifierDeps::MethodResolution(entry.GetDexMethodIndex(), + entry.GetAccessFlags(), + kNewTypeIndex)); + found = true; + break; } - ASSERT_TRUE(found); - new_class_loader.Assign(soa.Decode<mirror::ClassLoader>(LoadDex("VerifierDeps"))); - ASSERT_FALSE(decoded_deps.ValidateDependencies(new_class_loader, soa.Self())); } + ASSERT_TRUE(found); + new_class_loader.Assign(soa.Decode<mirror::ClassLoader>(LoadDex("VerifierDeps"))); + ASSERT_FALSE(decoded_deps.ValidateDependencies(new_class_loader, soa.Self())); } } diff --git a/dalvikvm/dalvikvm.cc b/dalvikvm/dalvikvm.cc index 85debe4d38..e735e2fae4 100644 --- a/dalvikvm/dalvikvm.cc +++ b/dalvikvm/dalvikvm.cc @@ -22,9 +22,9 @@ #include <memory> #include "jni.h" -#include "JniInvocation.h" -#include "ScopedLocalRef.h" -#include "toStringArray.h" +#include "nativehelper/JniInvocation.h" +#include "nativehelper/ScopedLocalRef.h" +#include "nativehelper/toStringArray.h" namespace art { diff --git a/dex2oat/Android.bp b/dex2oat/Android.bp index 346f5a7ef5..0d453efc0c 100644 --- a/dex2oat/Android.bp +++ b/dex2oat/Android.bp @@ -30,15 +30,6 @@ cc_defaults { android: { // Use the 32-bit version of dex2oat on devices compile_multilib: "prefer32", - - sanitize: { - // ASan slows down dex2oat by ~3.5x, which translates into - // extremely slow first boot. Disabled to help speed up - // SANITIZE_TARGET mode. - // Bug: 22233158 - address: false, - coverage: false, - }, }, }, diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc index c9cd17142e..3cc41a6b29 100644 --- a/dex2oat/dex2oat.cc +++ b/dex2oat/dex2oat.cc @@ -76,13 +76,13 @@ #include "mirror/class_loader.h" #include "mirror/object-inl.h" #include "mirror/object_array-inl.h" +#include "nativehelper/ScopedLocalRef.h" #include "oat_file.h" #include "oat_file_assistant.h" #include "oat_writer.h" #include "os.h" #include "runtime.h" #include "runtime_options.h" -#include "ScopedLocalRef.h" #include "scoped_thread_state_change-inl.h" #include "utils.h" #include "vdex_file.h" @@ -1497,10 +1497,9 @@ class Dex2Oat FINAL { Runtime* runtime = Runtime::Current(); CHECK(runtime != nullptr); // Filter out class path classes since we don't want to include these in the image. - std::set<DexCacheResolvedClasses> resolved_classes( - profile_compilation_info_->GetResolvedClasses(dex_files_)); - image_classes_.reset(new std::unordered_set<std::string>( - runtime->GetClassLinker()->GetClassDescriptorsForResolvedClasses(resolved_classes))); + image_classes_.reset( + new std::unordered_set<std::string>( + profile_compilation_info_->GetClassDescriptors(dex_files_))); VLOG(compiler) << "Loaded " << image_classes_->size() << " image class descriptors from profile"; if (VLOG_IS_ON(compiler)) { @@ -1520,18 +1519,15 @@ class Dex2Oat FINAL { return dex2oat::ReturnCode::kOther; } - if (CompilerFilter::IsAnyCompilationEnabled(compiler_options_->GetCompilerFilter())) { - // Only modes with compilation require verification results. - verification_results_.reset(new VerificationResults(compiler_options_.get())); - } + // Verification results are null since we don't know if we will need them yet as the compler + // filter may change. callbacks_.reset(new QuickCompilerCallbacks( - verification_results_.get(), IsBootImage() ? CompilerCallbacks::CallbackMode::kCompileBootImage : CompilerCallbacks::CallbackMode::kCompileApp)); RuntimeArgumentMap runtime_options; - if (!PrepareRuntimeOptions(&runtime_options)) { + if (!PrepareRuntimeOptions(&runtime_options, callbacks_.get())) { return dex2oat::ReturnCode::kOther; } @@ -1662,6 +1658,28 @@ class Dex2Oat FINAL { dex_files_ = MakeNonOwningPointerVector(opened_dex_files_); + // If we need to downgrade the compiler-filter for size reasons. + if (!IsBootImage() && IsVeryLarge(dex_files_)) { + if (!CompilerFilter::IsAsGoodAs(kLargeAppFilter, compiler_options_->GetCompilerFilter())) { + LOG(INFO) << "Very large app, downgrading to verify."; + // Note: this change won't be reflected in the key-value store, as that had to be + // finalized before loading the dex files. This setup is currently required + // to get the size from the DexFile objects. + // TODO: refactor. b/29790079 + compiler_options_->SetCompilerFilter(kLargeAppFilter); + } + } + + if (CompilerFilter::IsAnyCompilationEnabled(compiler_options_->GetCompilerFilter())) { + // Only modes with compilation require verification results, do this here instead of when we + // create the compilation callbacks since the compilation mode may have been changed by the + // very large app logic. + // Avoiding setting the verification results saves RAM by not adding the dex files later in + // the function. + verification_results_.reset(new VerificationResults(compiler_options_.get())); + callbacks_->SetVerificationResults(verification_results_.get()); + } + // We had to postpone the swap decision till now, as this is the point when we actually // know about the dex files we're going to use. @@ -1678,19 +1696,6 @@ class Dex2Oat FINAL { } } // Note that dex2oat won't close the swap_fd_. The compiler driver's swap space will do that. - - // If we need to downgrade the compiler-filter for size reasons, do that check now. - if (!IsBootImage() && IsVeryLarge(dex_files_)) { - if (!CompilerFilter::IsAsGoodAs(kLargeAppFilter, compiler_options_->GetCompilerFilter())) { - LOG(INFO) << "Very large app, downgrading to verify."; - // Note: this change won't be reflected in the key-value store, as that had to be - // finalized before loading the dex files. This setup is currently required - // to get the size from the DexFile objects. - // TODO: refactor. b/29790079 - compiler_options_->SetCompilerFilter(kLargeAppFilter); - } - } - if (IsBootImage()) { // For boot image, pass opened dex files to the Runtime::Create(). // Note: Runtime acquires ownership of these dex files. @@ -1787,7 +1792,7 @@ class Dex2Oat FINAL { for (const DexFile* dex_file : *dex_file_vector) { for (const std::string& filter : no_inline_filters) { // Use dex_file->GetLocation() rather than dex_file->GetBaseLocation(). This - // allows tests to specify <test-dexfile>:classes2.dex if needed but if the + // allows tests to specify <test-dexfile>!classes2.dex if needed but if the // base location passes the StartsWith() test, so do all extra locations. std::string dex_location = dex_file->GetLocation(); if (filter.find('/') == std::string::npos) { @@ -2456,7 +2461,8 @@ class Dex2Oat FINAL { } } - bool PrepareRuntimeOptions(RuntimeArgumentMap* runtime_options) { + bool PrepareRuntimeOptions(RuntimeArgumentMap* runtime_options, + QuickCompilerCallbacks* callbacks) { RuntimeOptions raw_options; if (boot_image_filename_.empty()) { std::string boot_class_path = "-Xbootclasspath:"; @@ -2474,7 +2480,7 @@ class Dex2Oat FINAL { raw_options.push_back(std::make_pair(runtime_args_[i], nullptr)); } - raw_options.push_back(std::make_pair("compilercallbacks", callbacks_.get())); + raw_options.push_back(std::make_pair("compilercallbacks", callbacks)); raw_options.push_back( std::make_pair("imageinstructionset", GetInstructionSetString(instruction_set_))); @@ -2546,7 +2552,6 @@ class Dex2Oat FINAL { runtime_->SetCalleeSaveMethod(runtime_->CreateCalleeSaveMethod(), type); } } - runtime_->GetClassLinker()->FixupDexCaches(runtime_->GetResolutionMethod()); // Initialize maps for unstarted runtime. This needs to be here, as running clinits needs this // set up. diff --git a/dex2oat/dex2oat_test.cc b/dex2oat/dex2oat_test.cc index 68ec0b5ab6..32877a8cd6 100644 --- a/dex2oat/dex2oat_test.cc +++ b/dex2oat/dex2oat_test.cc @@ -30,9 +30,9 @@ #include "base/macros.h" #include "base/mutex-inl.h" #include "bytecode_utils.h" -#include "dex_file-inl.h" #include "dex2oat_environment_test.h" #include "dex2oat_return_codes.h" +#include "dex_file-inl.h" #include "jit/profile_compilation_info.h" #include "oat.h" #include "oat_file.h" diff --git a/dexdump/dexdump.cc b/dexdump/dexdump.cc index 8437ea5dc0..0db790b47a 100644 --- a/dexdump/dexdump.cc +++ b/dexdump/dexdump.cc @@ -44,10 +44,10 @@ #include "android-base/stringprintf.h" -#include "dexdump_cfg.h" #include "dex_file-inl.h" #include "dex_file_types.h" #include "dex_instruction-inl.h" +#include "dexdump_cfg.h" namespace art { diff --git a/dexdump/dexdump_cfg.cc b/dexdump/dexdump_cfg.cc index 9c0429ff2b..28317071dd 100644 --- a/dexdump/dexdump_cfg.cc +++ b/dexdump/dexdump_cfg.cc @@ -19,8 +19,9 @@ #include "dexdump_cfg.h" #include <inttypes.h> -#include <ostream> + #include <map> +#include <ostream> #include <set> #include "dex_file-inl.h" diff --git a/dexdump/dexdump_main.cc b/dexdump/dexdump_main.cc index 606d4f81b1..43c3d12de5 100644 --- a/dexdump/dexdump_main.cc +++ b/dexdump/dexdump_main.cc @@ -29,8 +29,8 @@ #include <unistd.h> #include "base/logging.h" -#include "runtime.h" #include "mem_map.h" +#include "runtime.h" namespace art { diff --git a/dexdump/dexdump_test.cc b/dexdump/dexdump_test.cc index 640f387a80..b8b65d662b 100644 --- a/dexdump/dexdump_test.cc +++ b/dexdump/dexdump_test.cc @@ -14,9 +14,9 @@ * limitations under the License. */ +#include <sstream> #include <string> #include <vector> -#include <sstream> #include <sys/types.h> #include <unistd.h> diff --git a/dexlayout/dex_ir.h b/dexlayout/dex_ir.h index fe7457231a..9c887b9a17 100644 --- a/dexlayout/dex_ir.h +++ b/dexlayout/dex_ir.h @@ -19,9 +19,10 @@ #ifndef ART_DEXLAYOUT_DEX_IR_H_ #define ART_DEXLAYOUT_DEX_IR_H_ +#include <stdint.h> + #include <map> #include <vector> -#include <stdint.h> #include "base/stl_util.h" #include "dex_file-inl.h" diff --git a/dexlayout/dexlayout.cc b/dexlayout/dexlayout.cc index c0478bd3d9..fd92d77265 100644 --- a/dexlayout/dexlayout.cc +++ b/dexlayout/dexlayout.cc @@ -33,10 +33,10 @@ #include "android-base/stringprintf.h" -#include "dex_ir_builder.h" #include "dex_file-inl.h" #include "dex_file_verifier.h" #include "dex_instruction-inl.h" +#include "dex_ir_builder.h" #include "dex_verify.h" #include "dex_visualize.h" #include "dex_writer.h" diff --git a/dexlayout/dexlayout_main.cc b/dexlayout/dexlayout_main.cc index 33d62decca..17097f1728 100644 --- a/dexlayout/dexlayout_main.cc +++ b/dexlayout/dexlayout_main.cc @@ -22,17 +22,17 @@ #include "dexlayout.h" +#include <fcntl.h> #include <stdio.h> #include <string.h> -#include <unistd.h> -#include <sys/types.h> #include <sys/stat.h> -#include <fcntl.h> +#include <sys/types.h> +#include <unistd.h> #include "base/logging.h" #include "jit/profile_compilation_info.h" -#include "runtime.h" #include "mem_map.h" +#include "runtime.h" namespace art { diff --git a/dexlayout/dexlayout_test.cc b/dexlayout/dexlayout_test.cc index 43c531deb7..f3b4c86525 100644 --- a/dexlayout/dexlayout_test.cc +++ b/dexlayout/dexlayout_test.cc @@ -14,9 +14,9 @@ * limitations under the License. */ +#include <sstream> #include <string> #include <vector> -#include <sstream> #include <sys/types.h> #include <unistd.h> diff --git a/dexlist/dexlist.cc b/dexlist/dexlist.cc index fa232a7634..6a1e22a525 100644 --- a/dexlist/dexlist.cc +++ b/dexlist/dexlist.cc @@ -23,8 +23,8 @@ * List all methods in all concrete classes in one or more DEX files. */ -#include <stdlib.h> #include <stdio.h> +#include <stdlib.h> #include "dex_file-inl.h" #include "mem_map.h" diff --git a/dexlist/dexlist_test.cc b/dexlist/dexlist_test.cc index 173a456982..f645f878d1 100644 --- a/dexlist/dexlist_test.cc +++ b/dexlist/dexlist_test.cc @@ -14,9 +14,9 @@ * limitations under the License. */ +#include <sstream> #include <string> #include <vector> -#include <sstream> #include <sys/types.h> #include <unistd.h> diff --git a/dexoptanalyzer/dexoptanalyzer.cc b/dexoptanalyzer/dexoptanalyzer.cc index e2c159aa5c..fc72bbdb87 100644 --- a/dexoptanalyzer/dexoptanalyzer.cc +++ b/dexoptanalyzer/dexoptanalyzer.cc @@ -97,6 +97,9 @@ NO_RETURN static void Usage(const char *fmt, ...) { UsageError(" --android-data=<directory>: optional, the directory which should be used as"); UsageError(" android-data. By default ANDROID_DATA env variable is used."); UsageError(""); + UsageError(" --downgrade: optional, if the purpose of dexopt is to downgrade the dex file"); + UsageError(" By default, dexopt considers upgrade case."); + UsageError(""); UsageError("Return code:"); UsageError(" To make it easier to integrate with the internal tools this command will make"); UsageError(" available its result (dexoptNeeded) as the exit/return code. i.e. it will not"); @@ -121,7 +124,9 @@ NO_RETURN static void Usage(const char *fmt, ...) { class DexoptAnalyzer FINAL { public: - DexoptAnalyzer() : assume_profile_changed_(false) {} + DexoptAnalyzer() : + assume_profile_changed_(false), + downgrade_(false) {} void ParseArgs(int argc, char **argv) { original_argc = argc; @@ -160,9 +165,9 @@ class DexoptAnalyzer FINAL { // compute dalvik-cache folder). This is mostly used in tests. std::string new_android_data = option.substr(strlen("--android-data=")).ToString(); setenv("ANDROID_DATA", new_android_data.c_str(), 1); - } else { - Usage("Unknown argument '%s'", option.data()); - } + } else if (option.starts_with("--downgrade")) { + downgrade_ = true; + } else { Usage("Unknown argument '%s'", option.data()); } } if (image_.empty()) { @@ -225,7 +230,7 @@ class DexoptAnalyzer FINAL { return kNoDexOptNeeded; } int dexoptNeeded = oat_file_assistant.GetDexOptNeeded( - compiler_filter_, assume_profile_changed_); + compiler_filter_, assume_profile_changed_, downgrade_); // Convert OatFileAssitant codes to dexoptanalyzer codes. switch (dexoptNeeded) { @@ -249,6 +254,7 @@ class DexoptAnalyzer FINAL { InstructionSet isa_; CompilerFilter::Filter compiler_filter_; bool assume_profile_changed_; + bool downgrade_; std::string image_; }; diff --git a/dexoptanalyzer/dexoptanalyzer_test.cc b/dexoptanalyzer/dexoptanalyzer_test.cc index 1703ff4cbc..1cbf5461a4 100644 --- a/dexoptanalyzer/dexoptanalyzer_test.cc +++ b/dexoptanalyzer/dexoptanalyzer_test.cc @@ -71,12 +71,13 @@ class DexoptAnalyzerTest : public DexoptTest { // as the output of OatFileAssistant::GetDexOptNeeded. void Verify(const std::string& dex_file, CompilerFilter::Filter compiler_filter, - bool assume_profile_changed = false) { + bool assume_profile_changed = false, + bool downgrade = false) { int dexoptanalyzerResult = Analyze(dex_file, compiler_filter, assume_profile_changed); dexoptanalyzerResult = DexoptanalyzerToOatFileAssistant(dexoptanalyzerResult); OatFileAssistant oat_file_assistant(dex_file.c_str(), kRuntimeISA, /*load_executable*/ false); int assistantResult = oat_file_assistant.GetDexOptNeeded( - compiler_filter, assume_profile_changed); + compiler_filter, assume_profile_changed, downgrade); EXPECT_EQ(assistantResult, dexoptanalyzerResult); } }; @@ -118,6 +119,16 @@ TEST_F(DexoptAnalyzerTest, ProfileOatUpToDate) { Verify(dex_location, CompilerFilter::kQuicken, true); } +TEST_F(DexoptAnalyzerTest, Downgrade) { + std::string dex_location = GetScratchDir() + "/Downgrade.jar"; + Copy(GetDexSrc1(), dex_location); + GenerateOatForTest(dex_location.c_str(), CompilerFilter::kQuicken); + + Verify(dex_location, CompilerFilter::kSpeedProfile, false, true); + Verify(dex_location, CompilerFilter::kQuicken, false, true); + Verify(dex_location, CompilerFilter::kVerify, false, true); +} + // Case: We have a MultiDEX file and up-to-date OAT file for it. TEST_F(DexoptAnalyzerTest, MultiDexOatUpToDate) { std::string dex_location = GetScratchDir() + "/MultiDexOatUpToDate.jar"; diff --git a/disassembler/disassembler_arm.cc b/disassembler/disassembler_arm.cc index 3347dac535..66419e3649 100644 --- a/disassembler/disassembler_arm.cc +++ b/disassembler/disassembler_arm.cc @@ -26,8 +26,8 @@ #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wshadow" -#include "aarch32/instructions-aarch32.h" #include "aarch32/disasm-aarch32.h" +#include "aarch32/instructions-aarch32.h" #pragma GCC diagnostic pop namespace art { diff --git a/disassembler/disassembler_mips.cc b/disassembler/disassembler_mips.cc index 7cb216e766..1a395a45d2 100644 --- a/disassembler/disassembler_mips.cc +++ b/disassembler/disassembler_mips.cc @@ -477,6 +477,10 @@ static const MipsInstruction gMipsInstructions[] = { { kMsaSpecialMask | (0xf << 2), kMsa | (0x8 << 2), "ld", "kw" }, { kMsaSpecialMask | (0xf << 2), kMsa | (0x9 << 2), "st", "kw" }, { kMsaMask | (0x7 << 23), kMsa | (0x5 << 23) | 0x14, "ilvr", "Vkmn" }, + { kMsaMask | (0x7 << 23), kMsa | (0x1 << 23) | 0x12, "maddv", "Vkmn" }, + { kMsaMask | (0x7 << 23), kMsa | (0x2 << 23) | 0x12, "msubv", "Vkmn" }, + { kMsaMask | (0xf << 22), kMsa | (0x4 << 22) | 0x1b, "fmadd", "Ukmn" }, + { kMsaMask | (0xf << 22), kMsa | (0x5 << 22) | 0x1b, "fmsub", "Ukmn" }, }; static uint32_t ReadU32(const uint8_t* ptr) { diff --git a/imgdiag/imgdiag.cc b/imgdiag/imgdiag.cc index 2763c072cb..fb8e894581 100644 --- a/imgdiag/imgdiag.cc +++ b/imgdiag/imgdiag.cc @@ -20,36 +20,852 @@ #include <fstream> #include <functional> #include <iostream> -#include <string> -#include <vector> -#include <set> #include <map> +#include <set> +#include <string> #include <unordered_set> +#include <vector> #include "android-base/stringprintf.h" #include "art_field-inl.h" #include "art_method-inl.h" #include "base/unix_file/fd_file.h" -#include "gc/space/image_space.h" #include "gc/heap.h" +#include "gc/space/image_space.h" +#include "image.h" #include "mirror/class-inl.h" #include "mirror/object-inl.h" -#include "image.h" -#include "scoped_thread_state_change-inl.h" #include "os.h" +#include "scoped_thread_state_change-inl.h" -#include "cmdline.h" #include "backtrace/BacktraceMap.h" +#include "cmdline.h" +#include <signal.h> #include <sys/stat.h> #include <sys/types.h> -#include <signal.h> namespace art { using android::base::StringPrintf; +namespace { + +constexpr size_t kMaxAddressPrint = 5; + +enum class ProcessType { + kZygote, + kRemote +}; + +enum class RemoteProcesses { + kImageOnly, + kZygoteOnly, + kImageAndZygote +}; + +struct MappingData { + // The count of pages that are considered dirty by the OS. + size_t dirty_pages = 0; + // The count of pages that differ by at least one byte. + size_t different_pages = 0; + // The count of differing bytes. + size_t different_bytes = 0; + // The count of differing four-byte units. + size_t different_int32s = 0; + // The count of pages that have mapping count == 1. + size_t private_pages = 0; + // The count of private pages that are also dirty. + size_t private_dirty_pages = 0; + // The count of pages that are marked dirty but do not differ. + size_t false_dirty_pages = 0; + // Set of the local virtual page indices that are dirty. + std::set<size_t> dirty_page_set; +}; + +static std::string GetClassDescriptor(mirror::Class* klass) + REQUIRES_SHARED(Locks::mutator_lock_) { + CHECK(klass != nullptr); + + std::string descriptor; + const char* descriptor_str = klass->GetDescriptor(&descriptor /*out*/); + + return std::string(descriptor_str); +} + +static std::string PrettyFieldValue(ArtField* field, mirror::Object* object) + REQUIRES_SHARED(Locks::mutator_lock_) { + std::ostringstream oss; + switch (field->GetTypeAsPrimitiveType()) { + case Primitive::kPrimNot: { + oss << object->GetFieldObject<mirror::Object, kVerifyNone, kWithoutReadBarrier>( + field->GetOffset()); + break; + } + case Primitive::kPrimBoolean: { + oss << static_cast<bool>(object->GetFieldBoolean<kVerifyNone>(field->GetOffset())); + break; + } + case Primitive::kPrimByte: { + oss << static_cast<int32_t>(object->GetFieldByte<kVerifyNone>(field->GetOffset())); + break; + } + case Primitive::kPrimChar: { + oss << object->GetFieldChar<kVerifyNone>(field->GetOffset()); + break; + } + case Primitive::kPrimShort: { + oss << object->GetFieldShort<kVerifyNone>(field->GetOffset()); + break; + } + case Primitive::kPrimInt: { + oss << object->GetField32<kVerifyNone>(field->GetOffset()); + break; + } + case Primitive::kPrimLong: { + oss << object->GetField64<kVerifyNone>(field->GetOffset()); + break; + } + case Primitive::kPrimFloat: { + oss << object->GetField32<kVerifyNone>(field->GetOffset()); + break; + } + case Primitive::kPrimDouble: { + oss << object->GetField64<kVerifyNone>(field->GetOffset()); + break; + } + case Primitive::kPrimVoid: { + oss << "void"; + break; + } + } + return oss.str(); +} + +template <typename K, typename V, typename D> +static std::vector<std::pair<V, K>> SortByValueDesc( + const std::map<K, D> map, + std::function<V(const D&)> value_mapper = [](const D& d) { return static_cast<V>(d); }) { + // Store value->key so that we can use the default sort from pair which + // sorts by value first and then key + std::vector<std::pair<V, K>> value_key_vector; + + for (const auto& kv_pair : map) { + value_key_vector.push_back(std::make_pair(value_mapper(kv_pair.second), kv_pair.first)); + } + + // Sort in reverse (descending order) + std::sort(value_key_vector.rbegin(), value_key_vector.rend()); + return value_key_vector; +} + +// Fixup a remote pointer that we read from a foreign boot.art to point to our own memory. +// Returned pointer will point to inside of remote_contents. +template <typename T> +static T* FixUpRemotePointer(T* remote_ptr, + std::vector<uint8_t>& remote_contents, + const backtrace_map_t& boot_map) { + if (remote_ptr == nullptr) { + return nullptr; + } + + uintptr_t remote = reinterpret_cast<uintptr_t>(remote_ptr); + + CHECK_LE(boot_map.start, remote); + CHECK_GT(boot_map.end, remote); + + off_t boot_offset = remote - boot_map.start; + + return reinterpret_cast<T*>(&remote_contents[boot_offset]); +} + +template <typename T> +static T* RemoteContentsPointerToLocal(T* remote_ptr, + std::vector<uint8_t>& remote_contents, + const ImageHeader& image_header) { + if (remote_ptr == nullptr) { + return nullptr; + } + + uint8_t* remote = reinterpret_cast<uint8_t*>(remote_ptr); + ptrdiff_t boot_offset = remote - &remote_contents[0]; + + const uint8_t* local_ptr = reinterpret_cast<const uint8_t*>(&image_header) + boot_offset; + + return reinterpret_cast<T*>(const_cast<uint8_t*>(local_ptr)); +} + +template <typename T> size_t EntrySize(T* entry); +template<> size_t EntrySize(mirror::Object* object) REQUIRES_SHARED(Locks::mutator_lock_) { + return object->SizeOf(); +} +template<> size_t EntrySize(ArtMethod* art_method) REQUIRES_SHARED(Locks::mutator_lock_) { + return sizeof(*art_method); +} + +template <typename T> +static bool EntriesDiffer(T* entry1, T* entry2) REQUIRES_SHARED(Locks::mutator_lock_) { + return memcmp(entry1, entry2, EntrySize(entry1)) != 0; +} + +template <typename T> +struct RegionCommon { + public: + RegionCommon(std::ostream* os, + std::vector<uint8_t>* remote_contents, + std::vector<uint8_t>* zygote_contents, + const backtrace_map_t& boot_map, + const ImageHeader& image_header) : + os_(*os), + remote_contents_(remote_contents), + zygote_contents_(zygote_contents), + boot_map_(boot_map), + image_header_(image_header), + different_entries_(0), + dirty_entry_bytes_(0), + false_dirty_entry_bytes_(0) { + CHECK(remote_contents != nullptr); + CHECK(zygote_contents != nullptr); + } + + void DumpSamplesAndOffsetCount() { + os_ << " sample object addresses: "; + for (size_t i = 0; i < dirty_entries_.size() && i < kMaxAddressPrint; ++i) { + T* entry = dirty_entries_[i]; + os_ << reinterpret_cast<void*>(entry) << ", "; + } + os_ << "\n"; + os_ << " dirty byte +offset:count list = "; + std::vector<std::pair<size_t, off_t>> field_dirty_count_sorted = + SortByValueDesc<off_t, size_t, size_t>(field_dirty_count_); + for (const std::pair<size_t, off_t>& pair : field_dirty_count_sorted) { + off_t offset = pair.second; + size_t count = pair.first; + os_ << "+" << offset << ":" << count << ", "; + } + os_ << "\n"; + } + + size_t GetDifferentEntryCount() const { return different_entries_; } + size_t GetDirtyEntryBytes() const { return dirty_entry_bytes_; } + size_t GetFalseDirtyEntryCount() const { return false_dirty_entries_.size(); } + size_t GetFalseDirtyEntryBytes() const { return false_dirty_entry_bytes_; } + size_t GetZygoteDirtyEntryCount() const { return zygote_dirty_entries_.size(); } + + protected: + bool IsEntryOnDirtyPage(T* entry, const std::set<size_t>& dirty_pages) const + REQUIRES_SHARED(Locks::mutator_lock_) { + size_t size = EntrySize(entry); + size_t page_off = 0; + size_t current_page_idx; + uintptr_t entry_address = reinterpret_cast<uintptr_t>(entry); + // Iterate every page this entry belongs to + do { + current_page_idx = entry_address / kPageSize + page_off; + if (dirty_pages.find(current_page_idx) != dirty_pages.end()) { + // This entry is on a dirty page + return true; + } + page_off++; + } while ((current_page_idx * kPageSize) < RoundUp(entry_address + size, kObjectAlignment)); + return false; + } + + void AddZygoteDirtyEntry(T* entry) REQUIRES_SHARED(Locks::mutator_lock_) { + zygote_dirty_entries_.insert(entry); + } + + void AddImageDirtyEntry(T* entry) REQUIRES_SHARED(Locks::mutator_lock_) { + image_dirty_entries_.insert(entry); + } + + void AddFalseDirtyEntry(T* entry) REQUIRES_SHARED(Locks::mutator_lock_) { + false_dirty_entries_.push_back(entry); + false_dirty_entry_bytes_ += EntrySize(entry); + } + + // The output stream to write to. + std::ostream& os_; + // The byte contents of the remote (image) process' image. + std::vector<uint8_t>* remote_contents_; + // The byte contents of the zygote process' image. + std::vector<uint8_t>* zygote_contents_; + const backtrace_map_t& boot_map_; + const ImageHeader& image_header_; + + // Count of entries that are different. + size_t different_entries_; + + // Local entries that are dirty (differ in at least one byte). + size_t dirty_entry_bytes_; + std::vector<T*> dirty_entries_; + + // Local entries that are clean, but located on dirty pages. + size_t false_dirty_entry_bytes_; + std::vector<T*> false_dirty_entries_; + + // Image dirty entries + // If zygote_pid_only_ == true, these are shared dirty entries in the zygote. + // If zygote_pid_only_ == false, these are private dirty entries in the application. + std::set<T*> image_dirty_entries_; + + // Zygote dirty entries (probably private dirty). + // We only add entries here if they differed in both the image and the zygote, so + // they are probably private dirty. + std::set<T*> zygote_dirty_entries_; + + std::map<off_t /* field offset */, size_t /* count */> field_dirty_count_; + + private: + DISALLOW_COPY_AND_ASSIGN(RegionCommon); +}; + +template <typename T> +class RegionSpecializedBase : public RegionCommon<T> { +}; + +// Region analysis for mirror::Objects +template<> +class RegionSpecializedBase<mirror::Object> : public RegionCommon<mirror::Object> { + public: + RegionSpecializedBase(std::ostream* os, + std::vector<uint8_t>* remote_contents, + std::vector<uint8_t>* zygote_contents, + const backtrace_map_t& boot_map, + const ImageHeader& image_header) : + RegionCommon<mirror::Object>(os, remote_contents, zygote_contents, boot_map, image_header), + os_(*os) { } + + void CheckEntrySanity(const uint8_t* current) const + REQUIRES_SHARED(Locks::mutator_lock_) { + CHECK_ALIGNED(current, kObjectAlignment); + mirror::Object* entry = reinterpret_cast<mirror::Object*>(const_cast<uint8_t*>(current)); + // Sanity check that we are reading a real mirror::Object + CHECK(entry->GetClass() != nullptr) << "Image object at address " + << entry + << " has null class"; + if (kUseBakerReadBarrier) { + entry->AssertReadBarrierState(); + } + } + + mirror::Object* GetNextEntry(mirror::Object* entry) + REQUIRES_SHARED(Locks::mutator_lock_) { + uint8_t* next = + reinterpret_cast<uint8_t*>(entry) + RoundUp(EntrySize(entry), kObjectAlignment); + return reinterpret_cast<mirror::Object*>(next); + } + + void VisitEntry(mirror::Object* entry) + REQUIRES_SHARED(Locks::mutator_lock_) { + // Unconditionally store the class descriptor in case we need it later + mirror::Class* klass = entry->GetClass(); + class_data_[klass].descriptor = GetClassDescriptor(klass); + } + + void AddCleanEntry(mirror::Object* entry) + REQUIRES_SHARED(Locks::mutator_lock_) { + class_data_[entry->GetClass()].AddCleanObject(); + } + + void AddFalseDirtyEntry(mirror::Object* entry) + REQUIRES_SHARED(Locks::mutator_lock_) { + RegionCommon<mirror::Object>::AddFalseDirtyEntry(entry); + class_data_[entry->GetClass()].AddFalseDirtyObject(entry); + } + + void AddDirtyEntry(mirror::Object* entry, mirror::Object* entry_remote) + REQUIRES_SHARED(Locks::mutator_lock_) { + size_t entry_size = EntrySize(entry); + ++different_entries_; + dirty_entry_bytes_ += entry_size; + // Log dirty count and objects for class objects only. + mirror::Class* klass = entry->GetClass(); + if (klass->IsClassClass()) { + // Increment counts for the fields that are dirty + const uint8_t* current = reinterpret_cast<const uint8_t*>(entry); + const uint8_t* current_remote = reinterpret_cast<const uint8_t*>(entry_remote); + for (size_t i = 0; i < entry_size; ++i) { + if (current[i] != current_remote[i]) { + field_dirty_count_[i]++; + } + } + dirty_entries_.push_back(entry); + } + class_data_[klass].AddDirtyObject(entry, entry_remote); + } + + void DiffEntryContents(mirror::Object* entry, uint8_t* remote_bytes, const uint8_t* base_ptr) + REQUIRES_SHARED(Locks::mutator_lock_) { + const char* tabs = " "; + // Attempt to find fields for all dirty bytes. + mirror::Class* klass = entry->GetClass(); + if (entry->IsClass()) { + os_ << tabs + << "Class " << mirror::Class::PrettyClass(entry->AsClass()) << " " << entry << "\n"; + } else { + os_ << tabs + << "Instance of " << mirror::Class::PrettyClass(klass) << " " << entry << "\n"; + } + + std::unordered_set<ArtField*> dirty_instance_fields; + std::unordered_set<ArtField*> dirty_static_fields; + // Examine the bytes comprising the Object, computing which fields are dirty + // and recording them for later display. If the Object is an array object, + // compute the dirty entries. + mirror::Object* remote_entry = reinterpret_cast<mirror::Object*>(remote_bytes); + for (size_t i = 0, count = entry->SizeOf(); i < count; ++i) { + if (base_ptr[i] != remote_bytes[i]) { + ArtField* field = ArtField::FindInstanceFieldWithOffset</*exact*/false>(klass, i); + if (field != nullptr) { + dirty_instance_fields.insert(field); + } else if (entry->IsClass()) { + field = ArtField::FindStaticFieldWithOffset</*exact*/false>(entry->AsClass(), i); + if (field != nullptr) { + dirty_static_fields.insert(field); + } + } + if (field == nullptr) { + if (klass->IsArrayClass()) { + mirror::Class* component_type = klass->GetComponentType(); + Primitive::Type primitive_type = component_type->GetPrimitiveType(); + size_t component_size = Primitive::ComponentSize(primitive_type); + size_t data_offset = mirror::Array::DataOffset(component_size).Uint32Value(); + if (i >= data_offset) { + os_ << tabs << "Dirty array element " << (i - data_offset) / component_size << "\n"; + // Skip to next element to prevent spam. + i += component_size - 1; + continue; + } + } + os_ << tabs << "No field for byte offset " << i << "\n"; + } + } + } + // Dump different fields. + if (!dirty_instance_fields.empty()) { + os_ << tabs << "Dirty instance fields " << dirty_instance_fields.size() << "\n"; + for (ArtField* field : dirty_instance_fields) { + os_ << tabs << ArtField::PrettyField(field) + << " original=" << PrettyFieldValue(field, entry) + << " remote=" << PrettyFieldValue(field, remote_entry) << "\n"; + } + } + if (!dirty_static_fields.empty()) { + os_ << tabs << "Dirty static fields " << dirty_static_fields.size() << "\n"; + for (ArtField* field : dirty_static_fields) { + os_ << tabs << ArtField::PrettyField(field) + << " original=" << PrettyFieldValue(field, entry) + << " remote=" << PrettyFieldValue(field, remote_entry) << "\n"; + } + } + os_ << "\n"; + } + + void DumpDirtyEntries() REQUIRES_SHARED(Locks::mutator_lock_) { + // vector of pairs (size_t count, Class*) + auto dirty_object_class_values = + SortByValueDesc<mirror::Class*, size_t, ClassData>( + class_data_, + [](const ClassData& d) { return d.dirty_object_count; }); + os_ << "\n" << " Dirty object count by class:\n"; + for (const auto& vk_pair : dirty_object_class_values) { + size_t dirty_object_count = vk_pair.first; + mirror::Class* klass = vk_pair.second; + ClassData& class_data = class_data_[klass]; + size_t object_sizes = class_data.dirty_object_size_in_bytes; + float avg_dirty_bytes_per_class = + class_data.dirty_object_byte_count * 1.0f / object_sizes; + float avg_object_size = object_sizes * 1.0f / dirty_object_count; + const std::string& descriptor = class_data.descriptor; + os_ << " " << mirror::Class::PrettyClass(klass) << " (" + << "objects: " << dirty_object_count << ", " + << "avg dirty bytes: " << avg_dirty_bytes_per_class << ", " + << "avg object size: " << avg_object_size << ", " + << "class descriptor: '" << descriptor << "'" + << ")\n"; + if (strcmp(descriptor.c_str(), "Ljava/lang/Class;") == 0) { + DumpSamplesAndOffsetCount(); + os_ << " field contents:\n"; + for (mirror::Object* object : class_data.dirty_objects) { + // remote class object + auto remote_klass = reinterpret_cast<mirror::Class*>(object); + // local class object + auto local_klass = + RemoteContentsPointerToLocal(remote_klass, + *RegionCommon<mirror::Object>::remote_contents_, + RegionCommon<mirror::Object>::image_header_); + os_ << " " << reinterpret_cast<const void*>(object) << " "; + os_ << " class_status (remote): " << remote_klass->GetStatus() << ", "; + os_ << " class_status (local): " << local_klass->GetStatus(); + os_ << "\n"; + } + } + } + } + + void DumpFalseDirtyEntries() REQUIRES_SHARED(Locks::mutator_lock_) { + // vector of pairs (size_t count, Class*) + auto false_dirty_object_class_values = + SortByValueDesc<mirror::Class*, size_t, ClassData>( + class_data_, + [](const ClassData& d) { return d.false_dirty_object_count; }); + os_ << "\n" << " False-dirty object count by class:\n"; + for (const auto& vk_pair : false_dirty_object_class_values) { + size_t object_count = vk_pair.first; + mirror::Class* klass = vk_pair.second; + ClassData& class_data = class_data_[klass]; + size_t object_sizes = class_data.false_dirty_byte_count; + float avg_object_size = object_sizes * 1.0f / object_count; + const std::string& descriptor = class_data.descriptor; + os_ << " " << mirror::Class::PrettyClass(klass) << " (" + << "objects: " << object_count << ", " + << "avg object size: " << avg_object_size << ", " + << "total bytes: " << object_sizes << ", " + << "class descriptor: '" << descriptor << "'" + << ")\n"; + } + } + + void DumpCleanEntries() REQUIRES_SHARED(Locks::mutator_lock_) { + // vector of pairs (size_t count, Class*) + auto clean_object_class_values = + SortByValueDesc<mirror::Class*, size_t, ClassData>( + class_data_, + [](const ClassData& d) { return d.clean_object_count; }); + os_ << "\n" << " Clean object count by class:\n"; + for (const auto& vk_pair : clean_object_class_values) { + os_ << " " << mirror::Class::PrettyClass(vk_pair.second) << " (" << vk_pair.first << ")\n"; + } + } + + private: + // Aggregate and detail class data from an image diff. + struct ClassData { + size_t dirty_object_count = 0; + // Track only the byte-per-byte dirtiness (in bytes) + size_t dirty_object_byte_count = 0; + // Track the object-by-object dirtiness (in bytes) + size_t dirty_object_size_in_bytes = 0; + size_t clean_object_count = 0; + std::string descriptor; + size_t false_dirty_byte_count = 0; + size_t false_dirty_object_count = 0; + std::vector<mirror::Object*> false_dirty_objects; + // Remote pointers to dirty objects + std::vector<mirror::Object*> dirty_objects; + + void AddCleanObject() REQUIRES_SHARED(Locks::mutator_lock_) { + ++clean_object_count; + } + + void AddDirtyObject(mirror::Object* object, mirror::Object* object_remote) + REQUIRES_SHARED(Locks::mutator_lock_) { + ++dirty_object_count; + dirty_object_byte_count += CountDirtyBytes(object, object_remote); + dirty_object_size_in_bytes += EntrySize(object); + dirty_objects.push_back(object_remote); + } + + void AddFalseDirtyObject(mirror::Object* object) REQUIRES_SHARED(Locks::mutator_lock_) { + ++false_dirty_object_count; + false_dirty_objects.push_back(object); + false_dirty_byte_count += EntrySize(object); + } + + private: + // Go byte-by-byte and figure out what exactly got dirtied + static size_t CountDirtyBytes(mirror::Object* object1, mirror::Object* object2) + REQUIRES_SHARED(Locks::mutator_lock_) { + const uint8_t* cur1 = reinterpret_cast<const uint8_t*>(object1); + const uint8_t* cur2 = reinterpret_cast<const uint8_t*>(object2); + size_t dirty_bytes = 0; + size_t object_size = EntrySize(object1); + for (size_t i = 0; i < object_size; ++i) { + if (cur1[i] != cur2[i]) { + dirty_bytes++; + } + } + return dirty_bytes; + } + }; + + std::ostream& os_; + std::map<mirror::Class*, ClassData> class_data_; + + DISALLOW_COPY_AND_ASSIGN(RegionSpecializedBase); +}; + +// Region analysis for ArtMethods. +// TODO: most of these need work. +template<> +class RegionSpecializedBase<ArtMethod> : RegionCommon<ArtMethod> { + public: + RegionSpecializedBase(std::ostream* os, + std::vector<uint8_t>* remote_contents, + std::vector<uint8_t>* zygote_contents, + const backtrace_map_t& boot_map, + const ImageHeader& image_header) : + RegionCommon<ArtMethod>(os, remote_contents, zygote_contents, boot_map, image_header), + os_(*os) { } + + void CheckEntrySanity(const uint8_t* current ATTRIBUTE_UNUSED) const + REQUIRES_SHARED(Locks::mutator_lock_) { + } + + ArtMethod* GetNextEntry(ArtMethod* entry) + REQUIRES_SHARED(Locks::mutator_lock_) { + uint8_t* next = reinterpret_cast<uint8_t*>(entry) + RoundUp(EntrySize(entry), kObjectAlignment); + return reinterpret_cast<ArtMethod*>(next); + } + + void VisitEntry(ArtMethod* method ATTRIBUTE_UNUSED) + REQUIRES_SHARED(Locks::mutator_lock_) { + } + + void AddFalseDirtyEntry(ArtMethod* method) + REQUIRES_SHARED(Locks::mutator_lock_) { + RegionCommon<ArtMethod>::AddFalseDirtyEntry(method); + } + + void AddCleanEntry(ArtMethod* method ATTRIBUTE_UNUSED) { + } + + void AddDirtyEntry(ArtMethod* method, ArtMethod* method_remote) + REQUIRES_SHARED(Locks::mutator_lock_) { + size_t entry_size = EntrySize(method); + ++different_entries_; + dirty_entry_bytes_ += entry_size; + // Increment counts for the fields that are dirty + const uint8_t* current = reinterpret_cast<const uint8_t*>(method); + const uint8_t* current_remote = reinterpret_cast<const uint8_t*>(method_remote); + // ArtMethods always log their dirty count and entries. + for (size_t i = 0; i < entry_size; ++i) { + if (current[i] != current_remote[i]) { + field_dirty_count_[i]++; + } + } + dirty_entries_.push_back(method); + } + + void DiffEntryContents(ArtMethod* method ATTRIBUTE_UNUSED, + uint8_t* remote_bytes ATTRIBUTE_UNUSED, + const uint8_t* base_ptr ATTRIBUTE_UNUSED) + REQUIRES_SHARED(Locks::mutator_lock_) { + } + + void DumpDirtyEntries() REQUIRES_SHARED(Locks::mutator_lock_) { + DumpSamplesAndOffsetCount(); + os_ << " field contents:\n"; + for (ArtMethod* method : dirty_entries_) { + // remote method + auto art_method = reinterpret_cast<ArtMethod*>(method); + // remote class + mirror::Class* remote_declaring_class = + FixUpRemotePointer(art_method->GetDeclaringClass(), + *RegionCommon<ArtMethod>::remote_contents_, + RegionCommon<ArtMethod>::boot_map_); + // local class + mirror::Class* declaring_class = + RemoteContentsPointerToLocal(remote_declaring_class, + *RegionCommon<ArtMethod>::remote_contents_, + RegionCommon<ArtMethod>::image_header_); + DumpOneArtMethod(art_method, declaring_class, remote_declaring_class); + } + } + + void DumpFalseDirtyEntries() REQUIRES_SHARED(Locks::mutator_lock_) { + os_ << " field contents:\n"; + for (ArtMethod* method : false_dirty_entries_) { + // local class + mirror::Class* declaring_class = method->GetDeclaringClass(); + DumpOneArtMethod(method, declaring_class, nullptr); + } + } + + void DumpCleanEntries() REQUIRES_SHARED(Locks::mutator_lock_) { + } + + private: + std::ostream& os_; + + void DumpOneArtMethod(ArtMethod* art_method, + mirror::Class* declaring_class, + mirror::Class* remote_declaring_class) + REQUIRES_SHARED(Locks::mutator_lock_) { + PointerSize pointer_size = InstructionSetPointerSize(Runtime::Current()->GetInstructionSet()); + os_ << " " << reinterpret_cast<const void*>(art_method) << " "; + os_ << " entryPointFromJni: " + << reinterpret_cast<const void*>(art_method->GetDataPtrSize(pointer_size)) << ", "; + os_ << " entryPointFromQuickCompiledCode: " + << reinterpret_cast<const void*>( + art_method->GetEntryPointFromQuickCompiledCodePtrSize(pointer_size)) + << ", "; + os_ << " isNative? " << (art_method->IsNative() ? "yes" : "no") << ", "; + os_ << " class_status (local): " << declaring_class->GetStatus(); + if (remote_declaring_class != nullptr) { + os_ << ", class_status (remote): " << remote_declaring_class->GetStatus(); + } + os_ << "\n"; + } + + DISALLOW_COPY_AND_ASSIGN(RegionSpecializedBase); +}; + +template <typename T> +class RegionData : public RegionSpecializedBase<T> { + public: + RegionData(std::ostream* os, + std::vector<uint8_t>* remote_contents, + std::vector<uint8_t>* zygote_contents, + const backtrace_map_t& boot_map, + const ImageHeader& image_header) : + RegionSpecializedBase<T>(os, remote_contents, zygote_contents, boot_map, image_header), + os_(*os) { + CHECK(remote_contents != nullptr); + CHECK(zygote_contents != nullptr); + } + + // Walk over the type T entries in theregion between begin_image_ptr and end_image_ptr, + // collecting and reporting data regarding dirty, difference, etc. + void ProcessRegion(const MappingData& mapping_data, + RemoteProcesses remotes, + const uint8_t* begin_image_ptr, + const uint8_t* end_image_ptr) + REQUIRES_SHARED(Locks::mutator_lock_) { + const uint8_t* current = begin_image_ptr + RoundUp(sizeof(ImageHeader), kObjectAlignment); + T* entry = reinterpret_cast<T*>(const_cast<uint8_t*>(current)); + while (reinterpret_cast<uintptr_t>(entry) < reinterpret_cast<uintptr_t>(end_image_ptr)) { + ComputeEntryDirty(entry, begin_image_ptr, mapping_data.dirty_page_set); + + entry = RegionSpecializedBase<T>::GetNextEntry(entry); + } + + // Looking at only dirty pages, figure out how many of those bytes belong to dirty entries. + // TODO: fix this now that there are multiple regions in a mapping. + float true_dirtied_percent = + RegionCommon<T>::GetDirtyEntryBytes() * 1.0f / (mapping_data.dirty_pages * kPageSize); + + // Entry specific statistics. + os_ << RegionCommon<T>::GetDifferentEntryCount() << " different entries, \n " + << RegionCommon<T>::GetDirtyEntryBytes() << " different entry [bytes], \n " + << RegionCommon<T>::GetFalseDirtyEntryCount() << " false dirty entries,\n " + << RegionCommon<T>::GetFalseDirtyEntryBytes() << " false dirty entry [bytes], \n " + << true_dirtied_percent << " different entries-vs-total in a dirty page;\n " + << "\n"; + + const uint8_t* base_ptr = begin_image_ptr; + switch (remotes) { + case RemoteProcesses::kZygoteOnly: + os_ << " Zygote shared dirty entries: "; + break; + case RemoteProcesses::kImageAndZygote: + os_ << " Application dirty entries (private dirty): "; + // If we are dumping private dirty, diff against the zygote map to make it clearer what + // fields caused the page to be private dirty. + base_ptr = &RegionCommon<T>::zygote_contents_->operator[](0); + break; + case RemoteProcesses::kImageOnly: + os_ << " Application dirty entries (unknown whether private or shared dirty): "; + break; + } + DiffDirtyEntries(ProcessType::kRemote, + begin_image_ptr, + RegionCommon<T>::remote_contents_, + base_ptr); + // Print shared dirty after since it's less important. + if (RegionCommon<T>::GetZygoteDirtyEntryCount() != 0) { + // We only reach this point if both pids were specified. Furthermore, + // entries are only displayed here if they differed in both the image + // and the zygote, so they are probably private dirty. + CHECK(remotes == RemoteProcesses::kImageAndZygote); + os_ << "\n" << " Zygote dirty entries (probably shared dirty): "; + DiffDirtyEntries(ProcessType::kZygote, + begin_image_ptr, + RegionCommon<T>::zygote_contents_, + begin_image_ptr); + } + RegionSpecializedBase<T>::DumpDirtyEntries(); + RegionSpecializedBase<T>::DumpFalseDirtyEntries(); + RegionSpecializedBase<T>::DumpCleanEntries(); + } + + private: + std::ostream& os_; + + void DiffDirtyEntries(ProcessType process_type, + const uint8_t* begin_image_ptr, + std::vector<uint8_t>* contents, + const uint8_t* base_ptr) + REQUIRES_SHARED(Locks::mutator_lock_) { + os_ << RegionCommon<T>::dirty_entries_.size() << "\n"; + const std::set<T*>& entries = + (process_type == ProcessType::kZygote) ? + RegionCommon<T>::zygote_dirty_entries_: + RegionCommon<T>::image_dirty_entries_; + for (T* entry : entries) { + uint8_t* entry_bytes = reinterpret_cast<uint8_t*>(entry); + ptrdiff_t offset = entry_bytes - begin_image_ptr; + uint8_t* remote_bytes = &(*contents)[offset]; + RegionSpecializedBase<T>::DiffEntryContents(entry, remote_bytes, &base_ptr[offset]); + } + } + + void ComputeEntryDirty(T* entry, + const uint8_t* begin_image_ptr, + const std::set<size_t>& dirty_pages) + REQUIRES_SHARED(Locks::mutator_lock_) { + // Set up pointers in the remote and the zygote for comparison. + uint8_t* current = reinterpret_cast<uint8_t*>(entry); + ptrdiff_t offset = current - begin_image_ptr; + T* entry_remote = + reinterpret_cast<T*>(const_cast<uint8_t*>(&(*RegionCommon<T>::remote_contents_)[offset])); + const bool have_zygote = !RegionCommon<T>::zygote_contents_->empty(); + const uint8_t* current_zygote = + have_zygote ? &(*RegionCommon<T>::zygote_contents_)[offset] : nullptr; + T* entry_zygote = reinterpret_cast<T*>(const_cast<uint8_t*>(current_zygote)); + // Visit and classify entries at the current location. + RegionSpecializedBase<T>::VisitEntry(entry); + + // Test private dirty first. + bool is_dirty = false; + if (have_zygote) { + bool private_dirty = EntriesDiffer(entry_zygote, entry_remote); + if (private_dirty) { + // Private dirty, app vs zygote. + is_dirty = true; + RegionCommon<T>::AddImageDirtyEntry(entry); + } + if (EntriesDiffer(entry_zygote, entry)) { + // Shared dirty, zygote vs image. + is_dirty = true; + RegionCommon<T>::AddZygoteDirtyEntry(entry); + } + } else if (EntriesDiffer(entry_remote, entry)) { + // Shared or private dirty, app vs image. + is_dirty = true; + RegionCommon<T>::AddImageDirtyEntry(entry); + } + if (is_dirty) { + // TODO: Add support dirty entries in zygote and image. + RegionSpecializedBase<T>::AddDirtyEntry(entry, entry_remote); + } else { + RegionSpecializedBase<T>::AddCleanEntry(entry); + if (RegionCommon<T>::IsEntryOnDirtyPage(entry, dirty_pages)) { + // This entry was either never mutated or got mutated back to the same value. + // TODO: Do I want to distinguish a "different" vs a "dirty" page here? + RegionSpecializedBase<T>::AddFalseDirtyEntry(entry); + } + } + } + + DISALLOW_COPY_AND_ASSIGN(RegionData); +}; + +} // namespace + + class ImgDiagDumper { public: explicit ImgDiagDumper(std::ostream* os, @@ -123,8 +939,6 @@ class ImgDiagDumper { CHECK(boot_map_.end >= boot_map_.start); boot_map_size_ = boot_map_.end - boot_map_.start; - pointer_size_ = InstructionSetPointerSize(Runtime::Current()->GetInstructionSet()); - // Open /proc/<image_diff_pid_>/mem and read as remote_contents_. std::string image_file_name = StringPrintf("/proc/%ld/mem", static_cast<long>(image_diff_pid_)); // NOLINT [runtime/int] @@ -150,7 +964,7 @@ class ImgDiagDumper { return false; } // The boot map should be at the same address. - tmp_zygote_contents.reserve(boot_map_size_); + tmp_zygote_contents.resize(boot_map_size_); if (!zygote_map_file->PreadFully(&tmp_zygote_contents[0], boot_map_size_, boot_map_.start)) { LOG(WARNING) << "Could not fully read zygote file " << zygote_file_name; return false; @@ -188,7 +1002,7 @@ class ImgDiagDumper { return false; } - // Commit the mappings, etc., to the object state. + // Commit the mappings, etc. proc_maps_ = std::move(tmp_proc_maps); remote_contents_ = std::move(tmp_remote_contents); zygote_contents_ = std::move(tmp_zygote_contents); @@ -228,14 +1042,7 @@ class ImgDiagDumper { return DumpImageDiffMap(); } - bool ComputeDirtyBytes(const uint8_t* image_begin, - size_t* dirty_pages /*out*/, - size_t* different_pages /*out*/, - size_t* different_bytes /*out*/, - size_t* different_int32s /*out*/, - size_t* private_pages /*out*/, - size_t* private_dirty_pages /*out*/, - std::set<size_t>* dirty_page_set_local) { + bool ComputeDirtyBytes(const uint8_t* image_begin, MappingData* mapping_data /*out*/) { std::ostream& os = *os_; size_t virtual_page_idx = 0; // Virtual page number (for an absolute memory address) @@ -254,7 +1061,7 @@ class ImgDiagDumper { uint8_t* remote_ptr = &remote_contents_[offset]; if (memcmp(local_ptr, remote_ptr, kPageSize) != 0) { - ++*different_pages; + mapping_data->different_pages++; // Count the number of 32-bit integers that are different. for (size_t i = 0; i < kPageSize / sizeof(uint32_t); ++i) { @@ -262,7 +1069,7 @@ class ImgDiagDumper { const uint32_t* local_ptr_int32 = reinterpret_cast<const uint32_t*>(local_ptr); if (remote_ptr_int32[i] != local_ptr_int32[i]) { - ++*different_int32s; + mapping_data->different_int32s++; } } } @@ -286,7 +1093,7 @@ class ImgDiagDumper { page_idx = (offset + page_off_begin) / kPageSize; if (*local_ptr != *remote_ptr) { // Track number of bytes that are different - ++*different_bytes; + mapping_data->different_bytes++; } // Independently count the # of dirty pages on the remote side @@ -307,294 +1114,38 @@ class ImgDiagDumper { os << error_msg; return false; } else if (dirtiness > 0) { - ++*dirty_pages; - dirty_page_set_local->insert(dirty_page_set_local->end(), virtual_page_idx); + mapping_data->dirty_pages++; + mapping_data->dirty_page_set.insert(mapping_data->dirty_page_set.end(), virtual_page_idx); } bool is_dirty = dirtiness > 0; bool is_private = page_count == 1; if (page_count == 1) { - ++*private_pages; + mapping_data->private_pages++; } if (is_dirty && is_private) { - ++*private_dirty_pages; - } - } - } - return true; - } - - bool ObjectIsOnDirtyPage(const uint8_t* item, - size_t size, - const std::set<size_t>& dirty_page_set_local) { - size_t page_off = 0; - size_t current_page_idx; - uintptr_t object_address = reinterpret_cast<uintptr_t>(item); - // Iterate every page this object belongs to - do { - current_page_idx = object_address / kPageSize + page_off; - - if (dirty_page_set_local.find(current_page_idx) != dirty_page_set_local.end()) { - // This object is on a dirty page - return true; - } - - page_off++; - } while ((current_page_idx * kPageSize) < RoundUp(object_address + size, kObjectAlignment)); - - return false; - } - - static std::string PrettyFieldValue(ArtField* field, mirror::Object* obj) - REQUIRES_SHARED(Locks::mutator_lock_) { - std::ostringstream oss; - switch (field->GetTypeAsPrimitiveType()) { - case Primitive::kPrimNot: { - oss << obj->GetFieldObject<mirror::Object, kVerifyNone, kWithoutReadBarrier>( - field->GetOffset()); - break; - } - case Primitive::kPrimBoolean: { - oss << static_cast<bool>(obj->GetFieldBoolean<kVerifyNone>(field->GetOffset())); - break; - } - case Primitive::kPrimByte: { - oss << static_cast<int32_t>(obj->GetFieldByte<kVerifyNone>(field->GetOffset())); - break; - } - case Primitive::kPrimChar: { - oss << obj->GetFieldChar<kVerifyNone>(field->GetOffset()); - break; - } - case Primitive::kPrimShort: { - oss << obj->GetFieldShort<kVerifyNone>(field->GetOffset()); - break; - } - case Primitive::kPrimInt: { - oss << obj->GetField32<kVerifyNone>(field->GetOffset()); - break; - } - case Primitive::kPrimLong: { - oss << obj->GetField64<kVerifyNone>(field->GetOffset()); - break; - } - case Primitive::kPrimFloat: { - oss << obj->GetField32<kVerifyNone>(field->GetOffset()); - break; - } - case Primitive::kPrimDouble: { - oss << obj->GetField64<kVerifyNone>(field->GetOffset()); - break; - } - case Primitive::kPrimVoid: { - oss << "void"; - break; - } - } - return oss.str(); - } - - // Aggregate and detail class data from an image diff. - struct ClassData { - size_t dirty_object_count = 0; - - // Track only the byte-per-byte dirtiness (in bytes) - size_t dirty_object_byte_count = 0; - - // Track the object-by-object dirtiness (in bytes) - size_t dirty_object_size_in_bytes = 0; - - size_t clean_object_count = 0; - - std::string descriptor; - - size_t false_dirty_byte_count = 0; - size_t false_dirty_object_count = 0; - std::vector<const uint8_t*> false_dirty_objects; - - // Remote pointers to dirty objects - std::vector<const uint8_t*> dirty_objects; - }; - - void DiffObjectContents(mirror::Object* obj, - uint8_t* remote_bytes, - std::ostream& os) REQUIRES_SHARED(Locks::mutator_lock_) { - const char* tabs = " "; - // Attempt to find fields for all dirty bytes. - mirror::Class* klass = obj->GetClass(); - if (obj->IsClass()) { - os << tabs << "Class " << mirror::Class::PrettyClass(obj->AsClass()) << " " << obj << "\n"; - } else { - os << tabs << "Instance of " << mirror::Class::PrettyClass(klass) << " " << obj << "\n"; - } - - std::unordered_set<ArtField*> dirty_instance_fields; - std::unordered_set<ArtField*> dirty_static_fields; - const uint8_t* obj_bytes = reinterpret_cast<const uint8_t*>(obj); - mirror::Object* remote_obj = reinterpret_cast<mirror::Object*>(remote_bytes); - for (size_t i = 0, count = obj->SizeOf(); i < count; ++i) { - if (obj_bytes[i] != remote_bytes[i]) { - ArtField* field = ArtField::FindInstanceFieldWithOffset</*exact*/false>(klass, i); - if (field != nullptr) { - dirty_instance_fields.insert(field); - } else if (obj->IsClass()) { - field = ArtField::FindStaticFieldWithOffset</*exact*/false>(obj->AsClass(), i); - if (field != nullptr) { - dirty_static_fields.insert(field); - } - } - if (field == nullptr) { - if (klass->IsArrayClass()) { - mirror::Class* component_type = klass->GetComponentType(); - Primitive::Type primitive_type = component_type->GetPrimitiveType(); - size_t component_size = Primitive::ComponentSize(primitive_type); - size_t data_offset = mirror::Array::DataOffset(component_size).Uint32Value(); - if (i >= data_offset) { - os << tabs << "Dirty array element " << (i - data_offset) / component_size << "\n"; - // Skip to next element to prevent spam. - i += component_size - 1; - continue; - } - } - os << tabs << "No field for byte offset " << i << "\n"; - } - } - } - // Dump different fields. TODO: Dump field contents. - if (!dirty_instance_fields.empty()) { - os << tabs << "Dirty instance fields " << dirty_instance_fields.size() << "\n"; - for (ArtField* field : dirty_instance_fields) { - os << tabs << ArtField::PrettyField(field) - << " original=" << PrettyFieldValue(field, obj) - << " remote=" << PrettyFieldValue(field, remote_obj) << "\n"; - } - } - if (!dirty_static_fields.empty()) { - os << tabs << "Dirty static fields " << dirty_static_fields.size() << "\n"; - for (ArtField* field : dirty_static_fields) { - os << tabs << ArtField::PrettyField(field) - << " original=" << PrettyFieldValue(field, obj) - << " remote=" << PrettyFieldValue(field, remote_obj) << "\n"; - } - } - os << "\n"; - } - - struct ObjectRegionData { - // Count of objects that are different. - size_t different_objects = 0; - - // Local objects that are dirty (differ in at least one byte). - size_t dirty_object_bytes = 0; - std::vector<const uint8_t*>* dirty_objects; - - // Local objects that are clean, but located on dirty pages. - size_t false_dirty_object_bytes = 0; - std::vector<const uint8_t*> false_dirty_objects; - - // Image dirty objects - // If zygote_pid_only_ == true, these are shared dirty objects in the zygote. - // If zygote_pid_only_ == false, these are private dirty objects in the application. - std::set<const uint8_t*> image_dirty_objects; - - // Zygote dirty objects (probably private dirty). - // We only add objects here if they differed in both the image and the zygote, so - // they are probably private dirty. - std::set<const uint8_t*> zygote_dirty_objects; - - std::map<off_t /* field offset */, size_t /* count */>* field_dirty_count; - }; - - void ComputeObjectDirty(const uint8_t* current, - const uint8_t* current_remote, - const uint8_t* current_zygote, - ClassData* obj_class_data, - size_t obj_size, - const std::set<size_t>& dirty_page_set_local, - ObjectRegionData* region_data /*out*/) { - bool different_image_object = memcmp(current, current_remote, obj_size) != 0; - if (different_image_object) { - bool different_zygote_object = false; - if (!zygote_contents_.empty()) { - different_zygote_object = memcmp(current, current_zygote, obj_size) != 0; - } - if (different_zygote_object) { - // Different from zygote. - region_data->zygote_dirty_objects.insert(current); - } else { - // Just different from image. - region_data->image_dirty_objects.insert(current); - } - - ++region_data->different_objects; - region_data->dirty_object_bytes += obj_size; - - ++obj_class_data->dirty_object_count; - - // Go byte-by-byte and figure out what exactly got dirtied - size_t dirty_byte_count_per_object = 0; - for (size_t i = 0; i < obj_size; ++i) { - if (current[i] != current_remote[i]) { - dirty_byte_count_per_object++; + mapping_data->private_dirty_pages++; } } - obj_class_data->dirty_object_byte_count += dirty_byte_count_per_object; - obj_class_data->dirty_object_size_in_bytes += obj_size; - obj_class_data->dirty_objects.push_back(current_remote); - } else { - ++obj_class_data->clean_object_count; } + mapping_data->false_dirty_pages = mapping_data->dirty_pages - mapping_data->different_pages; + // Print low-level (bytes, int32s, pages) statistics. + os << mapping_data->different_bytes << " differing bytes,\n " + << mapping_data->different_int32s << " differing int32s,\n " + << mapping_data->different_pages << " differing pages,\n " + << mapping_data->dirty_pages << " pages are dirty;\n " + << mapping_data->false_dirty_pages << " pages are false dirty;\n " + << mapping_data->private_pages << " pages are private;\n " + << mapping_data->private_dirty_pages << " pages are Private_Dirty\n "; - if (different_image_object) { - if (region_data->dirty_objects != nullptr) { - // print the fields that are dirty - for (size_t i = 0; i < obj_size; ++i) { - if (current[i] != current_remote[i]) { - size_t dirty_count = 0; - if (region_data->field_dirty_count->find(i) != region_data->field_dirty_count->end()) { - dirty_count = (*region_data->field_dirty_count)[i]; - } - (*region_data->field_dirty_count)[i] = dirty_count + 1; - } - } - - region_data->dirty_objects->push_back(current); - } - /* - * TODO: Resurrect this stuff in the client when we add ArtMethod iterator. - } else { - std::string descriptor = GetClassDescriptor(klass); - if (strcmp(descriptor.c_str(), "Ljava/lang/reflect/ArtMethod;") == 0) { - // this is an ArtMethod - ArtMethod* art_method = reinterpret_cast<ArtMethod*>(remote_obj); - - // print the fields that are dirty - for (size_t i = 0; i < obj_size; ++i) { - if (current[i] != current_remote[i]) { - art_method_field_dirty_count[i]++; - } - } - - art_method_dirty_objects.push_back(art_method); - } - } - */ - } else if (ObjectIsOnDirtyPage(current, obj_size, dirty_page_set_local)) { - // This object was either never mutated or got mutated back to the same value. - // TODO: Do I want to distinguish a "different" vs a "dirty" page here? - region_data->false_dirty_objects.push_back(current); - obj_class_data->false_dirty_objects.push_back(current); - region_data->false_dirty_object_bytes += obj_size; - obj_class_data->false_dirty_byte_count += obj_size; - obj_class_data->false_dirty_object_count += 1; - } + return true; } // Look at /proc/$pid/mem and only diff the things from there bool DumpImageDiffMap() - REQUIRES_SHARED(Locks::mutator_lock_) { + REQUIRES_SHARED(Locks::mutator_lock_) { std::ostream& os = *os_; std::string error_msg; @@ -624,384 +1175,37 @@ class ImgDiagDumper { // If we wanted even more validation we could map the ImageHeader from the file } - size_t dirty_pages = 0; - size_t different_pages = 0; - size_t different_bytes = 0; - size_t different_int32s = 0; - size_t private_pages = 0; - size_t private_dirty_pages = 0; - - // Set of the local virtual page indices that are dirty - std::set<size_t> dirty_page_set_local; - - if (!ComputeDirtyBytes(image_begin, - &dirty_pages, - &different_pages, - &different_bytes, - &different_int32s, - &private_pages, - &private_dirty_pages, - &dirty_page_set_local)) { - return false; - } - - std::map<mirror::Class*, ClassData> class_data; - - // Walk each object in the remote image space and compare it against ours - std::map<off_t /* field offset */, int /* count */> art_method_field_dirty_count; - std::vector<ArtMethod*> art_method_dirty_objects; - - std::map<off_t /* field offset */, size_t /* count */> class_field_dirty_count; - std::vector<const uint8_t*> class_dirty_objects; - - - // Look up remote classes by their descriptor - std::map<std::string, mirror::Class*> remote_class_map; - // Look up local classes by their descriptor - std::map<std::string, mirror::Class*> local_class_map; - - const uint8_t* begin_image_ptr = image_begin_unaligned; - const uint8_t* end_image_ptr = image_mirror_end_unaligned; - - ObjectRegionData region_data; - - const uint8_t* current = begin_image_ptr + RoundUp(sizeof(ImageHeader), kObjectAlignment); - while (reinterpret_cast<uintptr_t>(current) < reinterpret_cast<uintptr_t>(end_image_ptr)) { - CHECK_ALIGNED(current, kObjectAlignment); - mirror::Object* obj = reinterpret_cast<mirror::Object*>(const_cast<uint8_t*>(current)); - - // Sanity check that we are reading a real object - CHECK(obj->GetClass() != nullptr) << "Image object at address " << obj << " has null class"; - if (kUseBakerReadBarrier) { - obj->AssertReadBarrierState(); - } - - mirror::Class* klass = obj->GetClass(); - size_t obj_size = obj->SizeOf(); - ClassData& obj_class_data = class_data[klass]; - - // Check against the other object and see if they are different - ptrdiff_t offset = current - begin_image_ptr; - const uint8_t* current_remote = &remote_contents_[offset]; - const uint8_t* current_zygote = - zygote_contents_.empty() ? nullptr : &zygote_contents_[offset]; - - if (klass->IsClassClass()) { - region_data.field_dirty_count = &class_field_dirty_count; - region_data.dirty_objects = &class_dirty_objects; - } else { - region_data.field_dirty_count = nullptr; - region_data.dirty_objects = nullptr; - } - - - ComputeObjectDirty(current, - current_remote, - current_zygote, - &obj_class_data, - obj_size, - dirty_page_set_local, - ®ion_data); - - // Object specific stuff. - std::string descriptor = GetClassDescriptor(klass); - if (strcmp(descriptor.c_str(), "Ljava/lang/Class;") == 0) { - local_class_map[descriptor] = reinterpret_cast<mirror::Class*>(obj); - mirror::Object* remote_obj = reinterpret_cast<mirror::Object*>( - const_cast<uint8_t*>(current_remote)); - remote_class_map[descriptor] = reinterpret_cast<mirror::Class*>(remote_obj); - } + MappingData mapping_data; - // Unconditionally store the class descriptor in case we need it later - obj_class_data.descriptor = descriptor; - - current += RoundUp(obj_size, kObjectAlignment); + os << "Mapping at [" << reinterpret_cast<void*>(boot_map_.start) << ", " + << reinterpret_cast<void*>(boot_map_.end) << ") had:\n "; + if (!ComputeDirtyBytes(image_begin, &mapping_data)) { + return false; } - // Looking at only dirty pages, figure out how many of those bytes belong to dirty objects. - float true_dirtied_percent = region_data.dirty_object_bytes * 1.0f / (dirty_pages * kPageSize); - size_t false_dirty_pages = dirty_pages - different_pages; + RegionData<mirror::Object> object_region_data(os_, + &remote_contents_, + &zygote_contents_, + boot_map_, + image_header_); - os << "Mapping at [" << reinterpret_cast<void*>(boot_map_.start) << ", " - << reinterpret_cast<void*>(boot_map_.end) << ") had: \n " - << different_bytes << " differing bytes, \n " - << different_int32s << " differing int32s, \n " - << region_data.different_objects << " different objects, \n " - << region_data.dirty_object_bytes << " different object [bytes], \n " - << region_data.false_dirty_objects.size() << " false dirty objects,\n " - << region_data.false_dirty_object_bytes << " false dirty object [bytes], \n " - << true_dirtied_percent << " different objects-vs-total in a dirty page;\n " - << different_pages << " different pages; \n " - << dirty_pages << " pages are dirty; \n " - << false_dirty_pages << " pages are false dirty; \n " - << private_pages << " pages are private; \n " - << private_dirty_pages << " pages are Private_Dirty\n " - << ""; - - // vector of pairs (int count, Class*) - auto dirty_object_class_values = SortByValueDesc<mirror::Class*, int, ClassData>( - class_data, [](const ClassData& d) { return d.dirty_object_count; }); - auto clean_object_class_values = SortByValueDesc<mirror::Class*, int, ClassData>( - class_data, [](const ClassData& d) { return d.clean_object_count; }); - - if (!region_data.zygote_dirty_objects.empty()) { - // We only reach this point if both pids were specified. Furthermore, - // objects are only displayed here if they differed in both the image - // and the zygote, so they are probably private dirty. - CHECK(image_diff_pid_ > 0 && zygote_diff_pid_ > 0); - os << "\n" << " Zygote dirty objects (probably shared dirty): " - << region_data.zygote_dirty_objects.size() << "\n"; - for (const uint8_t* obj_bytes : region_data.zygote_dirty_objects) { - auto obj = const_cast<mirror::Object*>(reinterpret_cast<const mirror::Object*>(obj_bytes)); - ptrdiff_t offset = obj_bytes - begin_image_ptr; - uint8_t* remote_bytes = &zygote_contents_[offset]; - DiffObjectContents(obj, remote_bytes, os); - } - } - os << "\n"; + RemoteProcesses remotes; if (zygote_pid_only_) { - // image_diff_pid_ is the zygote process. - os << " Zygote shared dirty objects: "; + remotes = RemoteProcesses::kZygoteOnly; + } else if (zygote_diff_pid_ > 0) { + remotes = RemoteProcesses::kImageAndZygote; } else { - // image_diff_pid_ is actually the image (application) process. - if (zygote_diff_pid_ > 0) { - os << " Application dirty objects (private dirty): "; - } else { - os << " Application dirty objects (unknown whether private or shared dirty): "; - } - } - os << region_data.image_dirty_objects.size() << "\n"; - for (const uint8_t* obj_bytes : region_data.image_dirty_objects) { - auto obj = const_cast<mirror::Object*>(reinterpret_cast<const mirror::Object*>(obj_bytes)); - ptrdiff_t offset = obj_bytes - begin_image_ptr; - uint8_t* remote_bytes = &remote_contents_[offset]; - DiffObjectContents(obj, remote_bytes, os); - } - - os << "\n" << " Dirty object count by class:\n"; - for (const auto& vk_pair : dirty_object_class_values) { - int dirty_object_count = vk_pair.first; - mirror::Class* klass = vk_pair.second; - int object_sizes = class_data[klass].dirty_object_size_in_bytes; - float avg_dirty_bytes_per_class = - class_data[klass].dirty_object_byte_count * 1.0f / object_sizes; - float avg_object_size = object_sizes * 1.0f / dirty_object_count; - const std::string& descriptor = class_data[klass].descriptor; - os << " " << mirror::Class::PrettyClass(klass) << " (" - << "objects: " << dirty_object_count << ", " - << "avg dirty bytes: " << avg_dirty_bytes_per_class << ", " - << "avg object size: " << avg_object_size << ", " - << "class descriptor: '" << descriptor << "'" - << ")\n"; - - constexpr size_t kMaxAddressPrint = 5; - if (strcmp(descriptor.c_str(), "Ljava/lang/reflect/ArtMethod;") == 0) { - os << " sample object addresses: "; - for (size_t i = 0; i < art_method_dirty_objects.size() && i < kMaxAddressPrint; ++i) { - auto art_method = art_method_dirty_objects[i]; - - os << reinterpret_cast<void*>(art_method) << ", "; - } - os << "\n"; - - os << " dirty byte +offset:count list = "; - auto art_method_field_dirty_count_sorted = - SortByValueDesc<off_t, int, int>(art_method_field_dirty_count); - for (auto pair : art_method_field_dirty_count_sorted) { - off_t offset = pair.second; - int count = pair.first; - - os << "+" << offset << ":" << count << ", "; - } - - os << "\n"; - - os << " field contents:\n"; - const auto& dirty_objects_list = class_data[klass].dirty_objects; - for (const uint8_t* uobj : dirty_objects_list) { - auto obj = const_cast<mirror::Object*>(reinterpret_cast<const mirror::Object*>(uobj)); - // remote method - auto art_method = reinterpret_cast<ArtMethod*>(obj); - - // remote class - mirror::Class* remote_declaring_class = - FixUpRemotePointer(art_method->GetDeclaringClass(), remote_contents_, boot_map_); - - // local class - mirror::Class* declaring_class = - RemoteContentsPointerToLocal(remote_declaring_class, remote_contents_, image_header_); - - os << " " << reinterpret_cast<void*>(obj) << " "; - os << " entryPointFromJni: " - << reinterpret_cast<const void*>( - art_method->GetDataPtrSize(pointer_size_)) << ", "; - os << " entryPointFromQuickCompiledCode: " - << reinterpret_cast<const void*>( - art_method->GetEntryPointFromQuickCompiledCodePtrSize(pointer_size_)) - << ", "; - os << " isNative? " << (art_method->IsNative() ? "yes" : "no") << ", "; - os << " class_status (local): " << declaring_class->GetStatus(); - os << " class_status (remote): " << remote_declaring_class->GetStatus(); - os << "\n"; - } - } - if (strcmp(descriptor.c_str(), "Ljava/lang/Class;") == 0) { - os << " sample object addresses: "; - for (size_t i = 0; i < class_dirty_objects.size() && i < kMaxAddressPrint; ++i) { - auto class_ptr = class_dirty_objects[i]; - - os << reinterpret_cast<const void*>(class_ptr) << ", "; - } - os << "\n"; - - os << " dirty byte +offset:count list = "; - auto class_field_dirty_count_sorted = - SortByValueDesc<off_t, int, size_t>(class_field_dirty_count); - for (auto pair : class_field_dirty_count_sorted) { - off_t offset = pair.second; - int count = pair.first; - - os << "+" << offset << ":" << count << ", "; - } - os << "\n"; - - os << " field contents:\n"; - // TODO: templatize this to avoid the awful casts down to uint8_t* and back. - const auto& dirty_objects_list = class_data[klass].dirty_objects; - for (const uint8_t* uobj : dirty_objects_list) { - auto obj = const_cast<mirror::Object*>(reinterpret_cast<const mirror::Object*>(uobj)); - // remote class object - auto remote_klass = reinterpret_cast<mirror::Class*>(obj); - - // local class object - auto local_klass = RemoteContentsPointerToLocal(remote_klass, - remote_contents_, - image_header_); - - os << " " << reinterpret_cast<const void*>(obj) << " "; - os << " class_status (remote): " << remote_klass->GetStatus() << ", "; - os << " class_status (local): " << local_klass->GetStatus(); - os << "\n"; - } - } - } - - auto false_dirty_object_class_values = SortByValueDesc<mirror::Class*, int, ClassData>( - class_data, [](const ClassData& d) { return d.false_dirty_object_count; }); - - os << "\n" << " False-dirty object count by class:\n"; - for (const auto& vk_pair : false_dirty_object_class_values) { - int object_count = vk_pair.first; - mirror::Class* klass = vk_pair.second; - int object_sizes = class_data[klass].false_dirty_byte_count; - float avg_object_size = object_sizes * 1.0f / object_count; - const std::string& descriptor = class_data[klass].descriptor; - os << " " << mirror::Class::PrettyClass(klass) << " (" - << "objects: " << object_count << ", " - << "avg object size: " << avg_object_size << ", " - << "total bytes: " << object_sizes << ", " - << "class descriptor: '" << descriptor << "'" - << ")\n"; - - if (strcmp(descriptor.c_str(), "Ljava/lang/reflect/ArtMethod;") == 0) { - // TODO: templatize this to avoid the awful casts down to uint8_t* and back. - auto& art_method_false_dirty_objects = class_data[klass].false_dirty_objects; - - os << " field contents:\n"; - for (const uint8_t* uobj : art_method_false_dirty_objects) { - auto obj = const_cast<mirror::Object*>(reinterpret_cast<const mirror::Object*>(uobj)); - // local method - auto art_method = reinterpret_cast<ArtMethod*>(obj); - - // local class - mirror::Class* declaring_class = art_method->GetDeclaringClass(); - - os << " " << reinterpret_cast<const void*>(obj) << " "; - os << " entryPointFromJni: " - << reinterpret_cast<const void*>( - art_method->GetDataPtrSize(pointer_size_)) << ", "; - os << " entryPointFromQuickCompiledCode: " - << reinterpret_cast<const void*>( - art_method->GetEntryPointFromQuickCompiledCodePtrSize(pointer_size_)) - << ", "; - os << " isNative? " << (art_method->IsNative() ? "yes" : "no") << ", "; - os << " class_status (local): " << declaring_class->GetStatus(); - os << "\n"; - } - } + remotes = RemoteProcesses::kImageOnly; } - os << "\n" << " Clean object count by class:\n"; - for (const auto& vk_pair : clean_object_class_values) { - os << " " << mirror::Class::PrettyClass(vk_pair.second) << " (" << vk_pair.first << ")\n"; - } + object_region_data.ProcessRegion(mapping_data, + remotes, + image_begin_unaligned, + image_mirror_end_unaligned); return true; } - // Fixup a remote pointer that we read from a foreign boot.art to point to our own memory. - // Returned pointer will point to inside of remote_contents. - template <typename T> - static T* FixUpRemotePointer(T* remote_ptr, - std::vector<uint8_t>& remote_contents, - const backtrace_map_t& boot_map) { - if (remote_ptr == nullptr) { - return nullptr; - } - - uintptr_t remote = reinterpret_cast<uintptr_t>(remote_ptr); - - CHECK_LE(boot_map.start, remote); - CHECK_GT(boot_map.end, remote); - - off_t boot_offset = remote - boot_map.start; - - return reinterpret_cast<T*>(&remote_contents[boot_offset]); - } - - template <typename T> - static T* RemoteContentsPointerToLocal(T* remote_ptr, - std::vector<uint8_t>& remote_contents, - const ImageHeader& image_header) { - if (remote_ptr == nullptr) { - return nullptr; - } - - uint8_t* remote = reinterpret_cast<uint8_t*>(remote_ptr); - ptrdiff_t boot_offset = remote - &remote_contents[0]; - - const uint8_t* local_ptr = reinterpret_cast<const uint8_t*>(&image_header) + boot_offset; - - return reinterpret_cast<T*>(const_cast<uint8_t*>(local_ptr)); - } - - static std::string GetClassDescriptor(mirror::Class* klass) - REQUIRES_SHARED(Locks::mutator_lock_) { - CHECK(klass != nullptr); - - std::string descriptor; - const char* descriptor_str = klass->GetDescriptor(&descriptor); - - return std::string(descriptor_str); - } - - template <typename K, typename V, typename D> - static std::vector<std::pair<V, K>> SortByValueDesc( - const std::map<K, D> map, - std::function<V(const D&)> value_mapper = [](const D& d) { return static_cast<V>(d); }) { - // Store value->key so that we can use the default sort from pair which - // sorts by value first and then key - std::vector<std::pair<V, K>> value_key_vector; - - for (const auto& kv_pair : map) { - value_key_vector.push_back(std::make_pair(value_mapper(kv_pair.second), kv_pair.first)); - } - - // Sort in reverse (descending order) - std::sort(value_key_vector.rbegin(), value_key_vector.rend()); - return value_key_vector; - } - static bool GetPageFrameNumber(File* page_map_file, size_t virtual_page_index, uint64_t* page_frame_number, @@ -1142,8 +1346,6 @@ class ImgDiagDumper { pid_t zygote_diff_pid_; // Dump image diff against zygote boot.art if pid is non-negative bool zygote_pid_only_; // The user only specified a pid for the zygote. - // Pointer size constant for object fields, etc. - PointerSize pointer_size_; // BacktraceMap used for finding the memory mapping of the image file. std::unique_ptr<BacktraceMap> proc_maps_; // Boot image mapping. diff --git a/imgdiag/imgdiag_test.cc b/imgdiag/imgdiag_test.cc index c948d3cbe2..3245795132 100644 --- a/imgdiag/imgdiag_test.cc +++ b/imgdiag/imgdiag_test.cc @@ -14,21 +14,21 @@ * limitations under the License. */ +#include <sstream> #include <string> #include <vector> -#include <sstream> #include "common_runtime_test.h" #include "android-base/stringprintf.h" -#include "runtime/os.h" #include "runtime/arch/instruction_set.h" #include "runtime/exec_utils.h" -#include "runtime/utils.h" -#include "runtime/gc/space/image_space.h" #include "runtime/gc/heap.h" +#include "runtime/gc/space/image_space.h" +#include "runtime/os.h" #include "runtime/runtime.h" +#include "runtime/utils.h" #include <sys/types.h> #include <unistd.h> diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc index ae26e7dfcf..99168c9bc5 100644 --- a/oatdump/oatdump.cc +++ b/oatdump/oatdump.cc @@ -34,8 +34,8 @@ #include "art_method-inl.h" #include "base/stl_util.h" #include "base/unix_file/fd_file.h" -#include "class_linker.h" #include "class_linker-inl.h" +#include "class_linker.h" #include "debug/elf_debug_writer.h" #include "debug/method_debug_info.h" #include "dex_file-inl.h" @@ -57,13 +57,13 @@ #include "mirror/dex_cache-inl.h" #include "mirror/object-inl.h" #include "mirror/object_array-inl.h" +#include "nativehelper/ScopedLocalRef.h" #include "oat.h" #include "oat_file-inl.h" #include "oat_file_manager.h" #include "os.h" #include "safe_map.h" #include "scoped_thread_state_change-inl.h" -#include "ScopedLocalRef.h" #include "stack.h" #include "stack_map.h" #include "string_reference.h" @@ -2233,16 +2233,15 @@ class ImageDumper { if (num_methods != 0u) { os << "Methods (size=" << num_methods << "):\n"; ScopedIndentation indent2(&vios_); - auto* resolved_methods = dex_cache->GetResolvedMethods(); + mirror::MethodDexCacheType* resolved_methods = dex_cache->GetResolvedMethods(); for (size_t i = 0, length = dex_cache->NumResolvedMethods(); i < length; ++i) { - auto* elem = mirror::DexCache::GetElementPtrSize(resolved_methods, - i, - image_pointer_size); + ArtMethod* elem = mirror::DexCache::GetNativePairPtrSize( + resolved_methods, i, image_pointer_size).object; size_t run = 0; for (size_t j = i + 1; - j != length && elem == mirror::DexCache::GetElementPtrSize(resolved_methods, - j, - image_pointer_size); + j != length && + elem == mirror::DexCache::GetNativePairPtrSize( + resolved_methods, j, image_pointer_size).object; ++j) { ++run; } @@ -2270,7 +2269,7 @@ class ImageDumper { ScopedIndentation indent2(&vios_); auto* resolved_fields = dex_cache->GetResolvedFields(); for (size_t i = 0, length = dex_cache->NumResolvedFields(); i < length; ++i) { - auto* elem = mirror::DexCache::GetNativePairPtrSize( + ArtField* elem = mirror::DexCache::GetNativePairPtrSize( resolved_fields, i, image_pointer_size).object; size_t run = 0; for (size_t j = i + 1; diff --git a/patchoat/patchoat.cc b/patchoat/patchoat.cc index a93969f0c0..ed7623a465 100644 --- a/patchoat/patchoat.cc +++ b/patchoat/patchoat.cc @@ -35,17 +35,17 @@ #include "base/stringpiece.h" #include "base/unix_file/fd_file.h" #include "base/unix_file/random_access_file_utils.h" -#include "elf_utils.h" #include "elf_file.h" #include "elf_file_impl.h" +#include "elf_utils.h" #include "gc/space/image_space.h" #include "image-inl.h" #include "intern_table.h" #include "mirror/dex_cache.h" #include "mirror/executable.h" +#include "mirror/method.h" #include "mirror/object-inl.h" #include "mirror/object-refvisitor-inl.h" -#include "mirror/method.h" #include "mirror/reference.h" #include "noop_compiler_callbacks.h" #include "offsets.h" @@ -535,17 +535,18 @@ void PatchOat::PatchDexFileArrays(mirror::ObjectArray<mirror::Object>* img_roots orig_dex_cache->FixupResolvedTypes(RelocatedCopyOf(orig_types), RelocatedPointerVisitor(this)); } - ArtMethod** orig_methods = orig_dex_cache->GetResolvedMethods(); - ArtMethod** relocated_methods = RelocatedAddressOfPointer(orig_methods); + mirror::MethodDexCacheType* orig_methods = orig_dex_cache->GetResolvedMethods(); + mirror::MethodDexCacheType* relocated_methods = RelocatedAddressOfPointer(orig_methods); copy_dex_cache->SetField64<false>( mirror::DexCache::ResolvedMethodsOffset(), static_cast<int64_t>(reinterpret_cast<uintptr_t>(relocated_methods))); if (orig_methods != nullptr) { - ArtMethod** copy_methods = RelocatedCopyOf(orig_methods); + mirror::MethodDexCacheType* copy_methods = RelocatedCopyOf(orig_methods); for (size_t j = 0, num = orig_dex_cache->NumResolvedMethods(); j != num; ++j) { - ArtMethod* orig = mirror::DexCache::GetElementPtrSize(orig_methods, j, pointer_size); - ArtMethod* copy = RelocatedAddressOfPointer(orig); - mirror::DexCache::SetElementPtrSize(copy_methods, j, copy, pointer_size); + mirror::MethodDexCachePair orig = + mirror::DexCache::GetNativePairPtrSize(orig_methods, j, pointer_size); + mirror::MethodDexCachePair copy(RelocatedAddressOfPointer(orig.object), orig.index); + mirror::DexCache::SetNativePairPtrSize(copy_methods, j, copy, pointer_size); } } mirror::FieldDexCacheType* orig_fields = orig_dex_cache->GetResolvedFields(); diff --git a/patchoat/patchoat.h b/patchoat/patchoat.h index 182ce94a78..d4c5a101d5 100644 --- a/patchoat/patchoat.h +++ b/patchoat/patchoat.h @@ -24,8 +24,8 @@ #include "elf_file.h" #include "elf_utils.h" #include "gc/accounting/space_bitmap.h" -#include "gc/space/image_space.h" #include "gc/heap.h" +#include "gc/space/image_space.h" #include "os.h" #include "runtime.h" diff --git a/profman/profman.cc b/profman/profman.cc index 6c8ca56408..fd3bd11c0e 100644 --- a/profman/profman.cc +++ b/profman/profman.cc @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "errno.h" +#include <errno.h> #include <stdio.h> #include <stdlib.h> #include <sys/file.h> @@ -44,7 +44,6 @@ #include "runtime.h" #include "type_reference.h" #include "utils.h" -#include "type_reference.h" #include "zip_archive.h" namespace art { diff --git a/runtime/arch/arch_test.cc b/runtime/arch/arch_test.cc index 838ae40838..bfac8c422d 100644 --- a/runtime/arch/arch_test.cc +++ b/runtime/arch/arch_test.cc @@ -21,7 +21,6 @@ #include "common_runtime_test.h" #include "quick/quick_method_frame_info.h" - // asm_support.h declares tests next to the #defines. We use asm_support_check.h to (safely) // generate CheckAsmSupportOffsetsAndSizes using gtest's EXPECT for the tests. We also use the // RETURN_TYPE, HEADER and FOOTER defines from asm_support_check.h to try to ensure that any @@ -129,6 +128,10 @@ static constexpr size_t kFrameSizeSaveRefsAndArgs = FRAME_SIZE_SAVE_REFS_AND_ARG #undef FRAME_SIZE_SAVE_REFS_AND_ARGS static constexpr size_t kFrameSizeSaveEverything = FRAME_SIZE_SAVE_EVERYTHING; #undef FRAME_SIZE_SAVE_EVERYTHING +#undef BAKER_MARK_INTROSPECTION_REGISTER_COUNT +#undef BAKER_MARK_INTROSPECTION_FIELD_ARRAY_ENTRY_SIZE +#undef BAKER_MARK_INTROSPECTION_GC_ROOT_ENTRIES_OFFSET +#undef BAKER_MARK_INTROSPECTION_GC_ROOT_ENTRY_SIZE } // namespace mips namespace mips64 { @@ -141,6 +144,10 @@ static constexpr size_t kFrameSizeSaveRefsAndArgs = FRAME_SIZE_SAVE_REFS_AND_ARG #undef FRAME_SIZE_SAVE_REFS_AND_ARGS static constexpr size_t kFrameSizeSaveEverything = FRAME_SIZE_SAVE_EVERYTHING; #undef FRAME_SIZE_SAVE_EVERYTHING +#undef BAKER_MARK_INTROSPECTION_REGISTER_COUNT +#undef BAKER_MARK_INTROSPECTION_FIELD_ARRAY_ENTRY_SIZE +#undef BAKER_MARK_INTROSPECTION_GC_ROOT_ENTRIES_OFFSET +#undef BAKER_MARK_INTROSPECTION_GC_ROOT_ENTRY_SIZE } // namespace mips64 namespace x86 { diff --git a/runtime/arch/arm/entrypoints_init_arm.cc b/runtime/arch/arm/entrypoints_init_arm.cc index 8a8d26466f..090bab7e4b 100644 --- a/runtime/arch/arm/entrypoints_init_arm.cc +++ b/runtime/arch/arm/entrypoints_init_arm.cc @@ -19,13 +19,13 @@ #include "arch/arm/asm_support_arm.h" #include "base/bit_utils.h" +#include "entrypoints/entrypoint_utils.h" #include "entrypoints/jni/jni_entrypoints.h" +#include "entrypoints/math_entrypoints.h" #include "entrypoints/quick/quick_alloc_entrypoints.h" #include "entrypoints/quick/quick_default_externs.h" #include "entrypoints/quick/quick_default_init_entrypoints.h" #include "entrypoints/quick/quick_entrypoints.h" -#include "entrypoints/entrypoint_utils.h" -#include "entrypoints/math_entrypoints.h" #include "entrypoints/runtime_asm_entrypoints.h" #include "interpreter/interpreter.h" diff --git a/runtime/arch/arm/fault_handler_arm.cc b/runtime/arch/arm/fault_handler_arm.cc index b4bca014f4..5c313783be 100644 --- a/runtime/arch/arm/fault_handler_arm.cc +++ b/runtime/arch/arm/fault_handler_arm.cc @@ -14,7 +14,6 @@ * limitations under the License. */ - #include "fault_handler.h" #include <sys/ucontext.h> diff --git a/runtime/arch/arm/instruction_set_features_arm.cc b/runtime/arch/arm/instruction_set_features_arm.cc index 8384460171..249696883a 100644 --- a/runtime/arch/arm/instruction_set_features_arm.cc +++ b/runtime/arch/arm/instruction_set_features_arm.cc @@ -17,11 +17,12 @@ #include "instruction_set_features_arm.h" #if defined(ART_TARGET_ANDROID) && defined(__arm__) -#include <sys/auxv.h> #include <asm/hwcap.h> +#include <sys/auxv.h> #endif #include "signal.h" + #include <fstream> #include "android-base/stringprintf.h" @@ -279,10 +280,9 @@ bool ArmInstructionSetFeatures::HasAtLeast(const InstructionSetFeatures* other) return false; } const ArmInstructionSetFeatures* other_as_arm = other->AsArmInstructionSetFeatures(); - - return (has_div_ || (has_div_ == other_as_arm->has_div_)) - && (has_atomic_ldrd_strd_ || (has_atomic_ldrd_strd_ == other_as_arm->has_atomic_ldrd_strd_)) - && (has_armv8a_ || (has_armv8a_ == other_as_arm->has_armv8a_)); + return (has_div_ || !other_as_arm->has_div_) + && (has_atomic_ldrd_strd_ || !other_as_arm->has_atomic_ldrd_strd_) + && (has_armv8a_ || !other_as_arm->has_armv8a_); } uint32_t ArmInstructionSetFeatures::AsBitmap() const { diff --git a/runtime/arch/arm/quick_entrypoints_arm.S b/runtime/arch/arm/quick_entrypoints_arm.S index 0de59053af..375768ec3f 100644 --- a/runtime/arch/arm/quick_entrypoints_arm.S +++ b/runtime/arch/arm/quick_entrypoints_arm.S @@ -1585,31 +1585,98 @@ END art_quick_proxy_invoke_handler * * Note that this stub writes to r0, r4, and r12. */ + .extern artLookupResolvedMethod ENTRY art_quick_imt_conflict_trampoline - ldr r4, [sp, #0] // Load referrer - ldr r4, [r4, #ART_METHOD_DEX_CACHE_METHODS_OFFSET_32] // Load dex cache methods array - ldr r12, [r4, r12, lsl #POINTER_SIZE_SHIFT] // Load interface method - ldr r0, [r0, #ART_METHOD_JNI_OFFSET_32] // Load ImtConflictTable - ldr r4, [r0] // Load first entry in ImtConflictTable. + push {r1-r2} + .cfi_adjust_cfa_offset (2 * 4) + .cfi_rel_offset r1, 0 + .cfi_rel_offset r2, 4 + ldr r4, [sp, #(2 * 4)] // Load referrer. + ubfx r1, r12, #0, #METHOD_DEX_CACHE_HASH_BITS // Calculate DexCache method slot index. + ldr r4, [r4, #ART_METHOD_DEX_CACHE_METHODS_OFFSET_32] // Load dex cache methods array + add r4, r4, r1, lsl #(POINTER_SIZE_SHIFT + 1) // Load DexCache method slot address. + ldr r2, [r0, #ART_METHOD_JNI_OFFSET_32] // Load ImtConflictTable + +// FIXME: Configure the build to use the faster code when appropriate. +// Currently we fall back to the slower version. +#if HAS_ATOMIC_LDRD + ldrd r0, r1, [r4] +#else + push {r3} + .cfi_adjust_cfa_offset 4 + .cfi_rel_offset r3, 0 +.Limt_conflict_trampoline_retry_load: + ldrexd r0, r1, [r4] + strexd r3, r0, r1, [r4] + cmp r3, #0 + bne .Limt_conflict_trampoline_retry_load + pop {r3} + .cfi_adjust_cfa_offset -4 + .cfi_restore r3 +#endif + + ldr r4, [r2] // Load first entry in ImtConflictTable. + cmp r1, r12 // Compare method index to see if we had a DexCache method hit. + bne .Limt_conflict_trampoline_dex_cache_miss .Limt_table_iterate: - cmp r4, r12 + cmp r4, r0 // Branch if found. Benchmarks have shown doing a branch here is better. - beq .Limt_table_found + beq .Limt_table_found // If the entry is null, the interface method is not in the ImtConflictTable. - cbz r4, .Lconflict_trampoline + cbz r4, .Lconflict_trampoline // Iterate over the entries of the ImtConflictTable. - ldr r4, [r0, #(2 * __SIZEOF_POINTER__)]! + ldr r4, [r2, #(2 * __SIZEOF_POINTER__)]! b .Limt_table_iterate .Limt_table_found: // We successfully hit an entry in the table. Load the target method // and jump to it. - ldr r0, [r0, #__SIZEOF_POINTER__] - ldr pc, [r0, #ART_METHOD_QUICK_CODE_OFFSET_32] + ldr r0, [r2, #__SIZEOF_POINTER__] + .cfi_remember_state + pop {r1-r2} + .cfi_adjust_cfa_offset -(2 * 4) + .cfi_restore r1 + .cfi_restore r2 + ldr pc, [r0, #ART_METHOD_QUICK_CODE_OFFSET_32] + .cfi_restore_state .Lconflict_trampoline: // Call the runtime stub to populate the ImtConflictTable and jump to the // resolved method. - mov r0, r12 // Load interface method + .cfi_remember_state + pop {r1-r2} + .cfi_adjust_cfa_offset -(2 * 4) + .cfi_restore r1 + .cfi_restore r2 INVOKE_TRAMPOLINE_BODY artInvokeInterfaceTrampoline + .cfi_restore_state +.Limt_conflict_trampoline_dex_cache_miss: + // We're not creating a proper runtime method frame here, + // artLookupResolvedMethod() is not allowed to walk the stack. + + // Save ImtConflictTable (r2), remaining arg (r3), first entry (r4), return address (lr). + push {r2-r4, lr} + .cfi_adjust_cfa_offset (4 * 4) + .cfi_rel_offset r3, 4 + .cfi_rel_offset lr, 12 + // Save FPR args. + vpush {d0-d7} + .cfi_adjust_cfa_offset (8 * 8) + + mov r0, ip // Pass method index. + ldr r1, [sp, #(8 * 8 + 6 * 4)] // Pass referrer. + bl artLookupResolvedMethod // (uint32_t method_index, ArtMethod* referrer) + + // Restore FPR args. + vpop {d0-d7} + .cfi_adjust_cfa_offset -(8 * 8) + // Restore ImtConflictTable (r2), remaining arg (r3), first entry (r4), return address (lr). + pop {r2-r4, lr} + .cfi_adjust_cfa_offset -(4 * 4) + .cfi_restore r3 + .cfi_restore lr + + cmp r0, #0 // If the method wasn't resolved, + beq .Lconflict_trampoline // skip the lookup and go to artInvokeInterfaceTrampoline(). + b .Limt_table_iterate END art_quick_imt_conflict_trampoline .extern artQuickResolutionTrampoline diff --git a/runtime/arch/arm64/entrypoints_init_arm64.cc b/runtime/arch/arm64/entrypoints_init_arm64.cc index 9bbcef307e..d270bdb3b7 100644 --- a/runtime/arch/arm64/entrypoints_init_arm64.cc +++ b/runtime/arch/arm64/entrypoints_init_arm64.cc @@ -19,13 +19,13 @@ #include "arch/arm64/asm_support_arm64.h" #include "base/bit_utils.h" +#include "entrypoints/entrypoint_utils.h" #include "entrypoints/jni/jni_entrypoints.h" +#include "entrypoints/math_entrypoints.h" #include "entrypoints/quick/quick_alloc_entrypoints.h" #include "entrypoints/quick/quick_default_externs.h" #include "entrypoints/quick/quick_default_init_entrypoints.h" #include "entrypoints/quick/quick_entrypoints.h" -#include "entrypoints/entrypoint_utils.h" -#include "entrypoints/math_entrypoints.h" #include "entrypoints/runtime_asm_entrypoints.h" #include "interpreter/interpreter.h" diff --git a/runtime/arch/arm64/fault_handler_arm64.cc b/runtime/arch/arm64/fault_handler_arm64.cc index 0ead732cdd..b9f9d551a9 100644 --- a/runtime/arch/arm64/fault_handler_arm64.cc +++ b/runtime/arch/arm64/fault_handler_arm64.cc @@ -14,7 +14,6 @@ * limitations under the License. */ - #include "fault_handler.h" #include <sys/ucontext.h> diff --git a/runtime/arch/arm64/quick_entrypoints_arm64.S b/runtime/arch/arm64/quick_entrypoints_arm64.S index e097a336d4..d15f5b85ec 100644 --- a/runtime/arch/arm64/quick_entrypoints_arm64.S +++ b/runtime/arch/arm64/quick_entrypoints_arm64.S @@ -2052,17 +2052,28 @@ END art_quick_proxy_invoke_handler * x0 is the conflict ArtMethod. * xIP1 is a hidden argument that holds the target interface method's dex method index. * - * Note that this stub writes to xIP0, xIP1, and x0. + * Note that this stub writes to xIP0, xIP1, x13-x15, and x0. */ - .extern artInvokeInterfaceTrampoline + .extern artLookupResolvedMethod ENTRY art_quick_imt_conflict_trampoline ldr xIP0, [sp, #0] // Load referrer + ubfx x15, xIP1, #0, #METHOD_DEX_CACHE_HASH_BITS // Calculate DexCache method slot index. ldr xIP0, [xIP0, #ART_METHOD_DEX_CACHE_METHODS_OFFSET_64] // Load dex cache methods array - ldr xIP0, [xIP0, xIP1, lsl #POINTER_SIZE_SHIFT] // Load interface method + add xIP0, xIP0, x15, lsl #(POINTER_SIZE_SHIFT + 1) // Load DexCache method slot address. + + // Relaxed atomic load x14:x15 from the dex cache slot. +.Limt_conflict_trampoline_retry_load: + ldxp x14, x15, [xIP0] + stxp w13, x14, x15, [xIP0] + cbnz w13, .Limt_conflict_trampoline_retry_load + + cmp x15, xIP1 // Compare method index to see if we had a DexCache method hit. + bne .Limt_conflict_trampoline_dex_cache_miss +.Limt_conflict_trampoline_have_interface_method: ldr xIP1, [x0, #ART_METHOD_JNI_OFFSET_64] // Load ImtConflictTable ldr x0, [xIP1] // Load first entry in ImtConflictTable. .Limt_table_iterate: - cmp x0, xIP0 + cmp x0, x14 // Branch if found. Benchmarks have shown doing a branch here is better. beq .Limt_table_found // If the entry is null, the interface method is not in the ImtConflictTable. @@ -2079,8 +2090,46 @@ ENTRY art_quick_imt_conflict_trampoline .Lconflict_trampoline: // Call the runtime stub to populate the ImtConflictTable and jump to the // resolved method. - mov x0, xIP0 // Load interface method + mov x0, x14 // Load interface method INVOKE_TRAMPOLINE_BODY artInvokeInterfaceTrampoline +.Limt_conflict_trampoline_dex_cache_miss: + // We're not creating a proper runtime method frame here, + // artLookupResolvedMethod() is not allowed to walk the stack. + + // Save GPR args and return address, allocate space for FPR args, align stack. + SAVE_TWO_REGS_INCREASE_FRAME x0, x1, (8 * 8 + 8 * 8 + 8 + 8) + SAVE_TWO_REGS x2, x3, 16 + SAVE_TWO_REGS x4, x5, 32 + SAVE_TWO_REGS x6, x7, 48 + SAVE_REG xLR, (8 * 8 + 8 * 8 + 8) + + // Save FPR args. + stp d0, d1, [sp, #64] + stp d2, d3, [sp, #80] + stp d4, d5, [sp, #96] + stp d6, d7, [sp, #112] + + mov x0, xIP1 // Pass method index. + ldr x1, [sp, #(8 * 8 + 8 * 8 + 8 + 8)] // Pass referrer. + bl artLookupResolvedMethod // (uint32_t method_index, ArtMethod* referrer) + mov x14, x0 // Move the interface method to x14 where the loop above expects it. + + // Restore FPR args. + ldp d0, d1, [sp, #64] + ldp d2, d3, [sp, #80] + ldp d4, d5, [sp, #96] + ldp d6, d7, [sp, #112] + + // Restore GPR args and return address. + RESTORE_REG xLR, (8 * 8 + 8 * 8 + 8) + RESTORE_TWO_REGS x2, x3, 16 + RESTORE_TWO_REGS x4, x5, 32 + RESTORE_TWO_REGS x6, x7, 48 + RESTORE_TWO_REGS_DECREASE_FRAME x0, x1, (8 * 8 + 8 * 8 + 8 + 8) + + // If the method wasn't resolved, skip the lookup and go to artInvokeInterfaceTrampoline(). + cbz x14, .Lconflict_trampoline + b .Limt_conflict_trampoline_have_interface_method END art_quick_imt_conflict_trampoline ENTRY art_quick_resolution_trampoline diff --git a/runtime/arch/instruction_set_features.cc b/runtime/arch/instruction_set_features.cc index 43c1711a72..ed8ff607a9 100644 --- a/runtime/arch/instruction_set_features.cc +++ b/runtime/arch/instruction_set_features.cc @@ -21,7 +21,6 @@ #include "base/casts.h" #include "utils.h" - #include "arm/instruction_set_features_arm.h" #include "arm64/instruction_set_features_arm64.h" #include "mips/instruction_set_features_mips.h" diff --git a/runtime/arch/memcmp16_test.cc b/runtime/arch/memcmp16_test.cc index 9ba7de1df5..2f3639c4b1 100644 --- a/runtime/arch/memcmp16_test.cc +++ b/runtime/arch/memcmp16_test.cc @@ -14,9 +14,10 @@ * limitations under the License. */ -#include "gtest/gtest.h" #include "memcmp16.h" +#include "gtest/gtest.h" + class RandGen { public: explicit RandGen(uint32_t seed) : val_(seed) {} diff --git a/runtime/arch/mips/asm_support_mips.S b/runtime/arch/mips/asm_support_mips.S index 948b06ce61..50095ae77e 100644 --- a/runtime/arch/mips/asm_support_mips.S +++ b/runtime/arch/mips/asm_support_mips.S @@ -127,6 +127,13 @@ #endif // USE_HEAP_POISONING .endm +// Byte size of the instructions (un)poisoning heap references. +#ifdef USE_HEAP_POISONING +#define HEAP_POISON_INSTR_SIZE 4 +#else +#define HEAP_POISON_INSTR_SIZE 0 +#endif // USE_HEAP_POISONING + // Based on contents of creg select the minimum integer // At the end of the macro the original value of creg is lost .macro MINint dreg,rreg,sreg,creg diff --git a/runtime/arch/mips/asm_support_mips.h b/runtime/arch/mips/asm_support_mips.h index 7437774c13..9d8572ffb5 100644 --- a/runtime/arch/mips/asm_support_mips.h +++ b/runtime/arch/mips/asm_support_mips.h @@ -24,4 +24,24 @@ #define FRAME_SIZE_SAVE_REFS_AND_ARGS 112 #define FRAME_SIZE_SAVE_EVERYTHING 256 +// &art_quick_read_barrier_mark_introspection is the first of many entry points: +// 21 entry points for long field offsets, large array indices and variable array indices +// (see macro BRB_FIELD_LONG_OFFSET_ENTRY) +// 21 entry points for short field offsets and small array indices +// (see macro BRB_FIELD_SHORT_OFFSET_ENTRY) +// 21 entry points for GC roots +// (see macro BRB_GC_ROOT_ENTRY) + +// There are as many entry points of each kind as there are registers that +// can hold a reference: V0-V1, A0-A3, T0-T7, S2-S8. +#define BAKER_MARK_INTROSPECTION_REGISTER_COUNT 21 + +#define BAKER_MARK_INTROSPECTION_FIELD_ARRAY_ENTRY_SIZE (8 * 4) // 8 instructions in + // BRB_FIELD_*_OFFSET_ENTRY. + +#define BAKER_MARK_INTROSPECTION_GC_ROOT_ENTRIES_OFFSET \ + (2 * BAKER_MARK_INTROSPECTION_REGISTER_COUNT * BAKER_MARK_INTROSPECTION_FIELD_ARRAY_ENTRY_SIZE) + +#define BAKER_MARK_INTROSPECTION_GC_ROOT_ENTRY_SIZE (4 * 4) // 4 instructions in BRB_GC_ROOT_ENTRY. + #endif // ART_RUNTIME_ARCH_MIPS_ASM_SUPPORT_MIPS_H_ diff --git a/runtime/arch/mips/entrypoints_init_mips.cc b/runtime/arch/mips/entrypoints_init_mips.cc index 9978da5f74..75cfc41028 100644 --- a/runtime/arch/mips/entrypoints_init_mips.cc +++ b/runtime/arch/mips/entrypoints_init_mips.cc @@ -16,13 +16,14 @@ #include <string.h> +#include "arch/mips/asm_support_mips.h" #include "atomic.h" +#include "entrypoints/entrypoint_utils.h" #include "entrypoints/jni/jni_entrypoints.h" +#include "entrypoints/math_entrypoints.h" #include "entrypoints/quick/quick_alloc_entrypoints.h" #include "entrypoints/quick/quick_default_externs.h" #include "entrypoints/quick/quick_entrypoints.h" -#include "entrypoints/entrypoint_utils.h" -#include "entrypoints/math_entrypoints.h" #include "entrypoints/runtime_asm_entrypoints.h" #include "entrypoints_direct_mips.h" #include "interpreter/interpreter.h" @@ -59,6 +60,10 @@ extern "C" mirror::Object* art_quick_read_barrier_mark_reg21(mirror::Object*); extern "C" mirror::Object* art_quick_read_barrier_mark_reg22(mirror::Object*); extern "C" mirror::Object* art_quick_read_barrier_mark_reg29(mirror::Object*); +extern "C" mirror::Object* art_quick_read_barrier_mark_introspection(mirror::Object*); +extern "C" mirror::Object* art_quick_read_barrier_mark_introspection_gc_roots(mirror::Object*); +extern "C" void art_quick_read_barrier_mark_introspection_end_of_entries(void); + // Math entrypoints. extern int32_t CmpgDouble(double a, double b); extern int32_t CmplDouble(double a, double b); @@ -87,6 +92,23 @@ extern "C" int64_t __divdi3(int64_t, int64_t); extern "C" int64_t __moddi3(int64_t, int64_t); void UpdateReadBarrierEntrypoints(QuickEntryPoints* qpoints, bool is_active) { + intptr_t introspection_field_array_entries_size = + reinterpret_cast<intptr_t>(&art_quick_read_barrier_mark_introspection_gc_roots) - + reinterpret_cast<intptr_t>(&art_quick_read_barrier_mark_introspection); + static_assert( + BAKER_MARK_INTROSPECTION_GC_ROOT_ENTRIES_OFFSET == 2 * + BAKER_MARK_INTROSPECTION_REGISTER_COUNT * BAKER_MARK_INTROSPECTION_FIELD_ARRAY_ENTRY_SIZE, + "Expecting equal"); + DCHECK_EQ(introspection_field_array_entries_size, + BAKER_MARK_INTROSPECTION_GC_ROOT_ENTRIES_OFFSET); + intptr_t introspection_gc_root_entries_size = + reinterpret_cast<intptr_t>(&art_quick_read_barrier_mark_introspection_end_of_entries) - + reinterpret_cast<intptr_t>(&art_quick_read_barrier_mark_introspection_gc_roots); + DCHECK_EQ(introspection_gc_root_entries_size, + BAKER_MARK_INTROSPECTION_REGISTER_COUNT * BAKER_MARK_INTROSPECTION_GC_ROOT_ENTRY_SIZE); + qpoints->pReadBarrierMarkReg00 = is_active ? art_quick_read_barrier_mark_introspection : nullptr; + static_assert(!IsDirectEntrypoint(kQuickReadBarrierMarkReg00), + "Non-direct C stub marked direct."); qpoints->pReadBarrierMarkReg01 = is_active ? art_quick_read_barrier_mark_reg01 : nullptr; static_assert(!IsDirectEntrypoint(kQuickReadBarrierMarkReg01), "Non-direct C stub marked direct."); @@ -416,9 +438,6 @@ void InitEntryPoints(JniEntryPoints* jpoints, QuickEntryPoints* qpoints) { // Cannot use the following registers to pass arguments: // 0(ZERO), 1(AT), 16(S0), 17(S1), 24(T8), 25(T9), 26(K0), 27(K1), 28(GP), 29(SP), 31(RA). // Note that there are 30 entry points only: 00 for register 1(AT), ..., 29 for register 30(S8). - qpoints->pReadBarrierMarkReg00 = nullptr; - static_assert(!IsDirectEntrypoint(kQuickReadBarrierMarkReg00), - "Non-direct C stub marked direct."); qpoints->pReadBarrierMarkReg15 = nullptr; static_assert(!IsDirectEntrypoint(kQuickReadBarrierMarkReg15), "Non-direct C stub marked direct."); diff --git a/runtime/arch/mips/fault_handler_mips.cc b/runtime/arch/mips/fault_handler_mips.cc index 52a3df55d6..bf3e96a8ff 100644 --- a/runtime/arch/mips/fault_handler_mips.cc +++ b/runtime/arch/mips/fault_handler_mips.cc @@ -14,8 +14,8 @@ * limitations under the License. */ -#include "fault_handler.h" #include <sys/ucontext.h> +#include "fault_handler.h" #include "art_method.h" #include "base/callee_save_type.h" diff --git a/runtime/arch/mips/quick_entrypoints_mips.S b/runtime/arch/mips/quick_entrypoints_mips.S index 00e3d67207..59a2c101c8 100644 --- a/runtime/arch/mips/quick_entrypoints_mips.S +++ b/runtime/arch/mips/quick_entrypoints_mips.S @@ -165,13 +165,29 @@ .endm /* + * Individually usable part of macro SETUP_SAVE_REFS_AND_ARGS_FRAME_REGISTERS_ONLY. + */ +.macro SETUP_SAVE_REFS_AND_ARGS_FRAME_S4_THRU_S8 + sw $s8, 104($sp) + .cfi_rel_offset 30, 104 + sw $s7, 96($sp) + .cfi_rel_offset 23, 96 + sw $s6, 92($sp) + .cfi_rel_offset 22, 92 + sw $s5, 88($sp) + .cfi_rel_offset 21, 88 + sw $s4, 84($sp) + .cfi_rel_offset 20, 84 +.endm + + /* * Macro that sets up the callee save frame to conform with * Runtime::CreateCalleeSaveMethod(kSaveRefsAndArgs). * callee-save: $a1-$a3, $t0-$t1, $s2-$s8, $gp, $ra, $f8-$f19 * (26 total + 1 word padding + method*) */ -.macro SETUP_SAVE_REFS_AND_ARGS_FRAME_REGISTERS_ONLY - addiu $sp, $sp, -112 +.macro SETUP_SAVE_REFS_AND_ARGS_FRAME_REGISTERS_ONLY save_s4_thru_s8=1 + addiu $sp, $sp, -112 .cfi_adjust_cfa_offset 112 // Ugly compile-time check, but we only have the preprocessor. @@ -179,40 +195,33 @@ #error "FRAME_SIZE_SAVE_REFS_AND_ARGS(MIPS) size not as expected." #endif - sw $ra, 108($sp) + sw $ra, 108($sp) .cfi_rel_offset 31, 108 - sw $s8, 104($sp) - .cfi_rel_offset 30, 104 - sw $gp, 100($sp) + sw $gp, 100($sp) .cfi_rel_offset 28, 100 - sw $s7, 96($sp) - .cfi_rel_offset 23, 96 - sw $s6, 92($sp) - .cfi_rel_offset 22, 92 - sw $s5, 88($sp) - .cfi_rel_offset 21, 88 - sw $s4, 84($sp) - .cfi_rel_offset 20, 84 - sw $s3, 80($sp) + .if \save_s4_thru_s8 + SETUP_SAVE_REFS_AND_ARGS_FRAME_S4_THRU_S8 + .endif + sw $s3, 80($sp) .cfi_rel_offset 19, 80 - sw $s2, 76($sp) + sw $s2, 76($sp) .cfi_rel_offset 18, 76 - sw $t1, 72($sp) + sw $t1, 72($sp) .cfi_rel_offset 9, 72 - sw $t0, 68($sp) + sw $t0, 68($sp) .cfi_rel_offset 8, 68 - sw $a3, 64($sp) + sw $a3, 64($sp) .cfi_rel_offset 7, 64 - sw $a2, 60($sp) + sw $a2, 60($sp) .cfi_rel_offset 6, 60 - sw $a1, 56($sp) + sw $a1, 56($sp) .cfi_rel_offset 5, 56 - SDu $f18, $f19, 48, $sp, $t8 - SDu $f16, $f17, 40, $sp, $t8 - SDu $f14, $f15, 32, $sp, $t8 - SDu $f12, $f13, 24, $sp, $t8 - SDu $f10, $f11, 16, $sp, $t8 - SDu $f8, $f9, 8, $sp, $t8 + SDu $f18, $f19, 48, $sp, $t8 + SDu $f16, $f17, 40, $sp, $t8 + SDu $f14, $f15, 32, $sp, $t8 + SDu $f12, $f13, 24, $sp, $t8 + SDu $f10, $f11, 16, $sp, $t8 + SDu $f8, $f9, 8, $sp, $t8 # bottom will hold Method* .endm @@ -225,8 +234,14 @@ * Allocates ARG_SLOT_SIZE bytes at the bottom of the stack for arg slots. * Reserves FRAME_SIZE_SAVE_REFS_AND_ARGS + ARG_SLOT_SIZE bytes on the stack */ -.macro SETUP_SAVE_REFS_AND_ARGS_FRAME - SETUP_SAVE_REFS_AND_ARGS_FRAME_REGISTERS_ONLY +.macro SETUP_SAVE_REFS_AND_ARGS_FRAME save_s4_thru_s8_only=0 + .if \save_s4_thru_s8_only + // It is expected that `SETUP_SAVE_REFS_AND_ARGS_FRAME_REGISTERS_ONLY /* save_s4_thru_s8 */ 0` + // has been done prior to `SETUP_SAVE_REFS_AND_ARGS_FRAME /* save_s4_thru_s8_only */ 1`. + SETUP_SAVE_REFS_AND_ARGS_FRAME_S4_THRU_S8 + .else + SETUP_SAVE_REFS_AND_ARGS_FRAME_REGISTERS_ONLY + .endif lw $t0, %got(_ZN3art7Runtime9instance_E)($gp) lw $t0, 0($t0) lw $t0, RUNTIME_SAVE_REFS_AND_ARGS_METHOD_OFFSET($t0) @@ -254,44 +269,64 @@ .cfi_adjust_cfa_offset ARG_SLOT_SIZE .endm -.macro RESTORE_SAVE_REFS_AND_ARGS_FRAME - addiu $sp, $sp, ARG_SLOT_SIZE # remove argument slots on the stack - .cfi_adjust_cfa_offset -ARG_SLOT_SIZE - lw $ra, 108($sp) - .cfi_restore 31 - lw $s8, 104($sp) - .cfi_restore 30 - lw $gp, 100($sp) + /* + * Individually usable part of macro RESTORE_SAVE_REFS_AND_ARGS_FRAME. + */ +.macro RESTORE_SAVE_REFS_AND_ARGS_FRAME_GP + lw $gp, 100($sp) .cfi_restore 28 - lw $s7, 96($sp) - .cfi_restore 23 - lw $s6, 92($sp) - .cfi_restore 22 - lw $s5, 88($sp) - .cfi_restore 21 - lw $s4, 84($sp) - .cfi_restore 20 - lw $s3, 80($sp) +.endm + + /* + * Individually usable part of macro RESTORE_SAVE_REFS_AND_ARGS_FRAME. + */ +.macro RESTORE_SAVE_REFS_AND_ARGS_FRAME_A1 + lw $a1, 56($sp) + .cfi_restore 5 +.endm + +.macro RESTORE_SAVE_REFS_AND_ARGS_FRAME restore_s4_thru_s8=1, remove_arg_slots=1 + .if \remove_arg_slots + addiu $sp, $sp, ARG_SLOT_SIZE # Remove argument slots from the stack. + .cfi_adjust_cfa_offset -ARG_SLOT_SIZE + .endif + lw $ra, 108($sp) + .cfi_restore 31 + .if \restore_s4_thru_s8 + lw $s8, 104($sp) + .cfi_restore 30 + .endif + RESTORE_SAVE_REFS_AND_ARGS_FRAME_GP + .if \restore_s4_thru_s8 + lw $s7, 96($sp) + .cfi_restore 23 + lw $s6, 92($sp) + .cfi_restore 22 + lw $s5, 88($sp) + .cfi_restore 21 + lw $s4, 84($sp) + .cfi_restore 20 + .endif + lw $s3, 80($sp) .cfi_restore 19 - lw $s2, 76($sp) + lw $s2, 76($sp) .cfi_restore 18 - lw $t1, 72($sp) + lw $t1, 72($sp) .cfi_restore 9 - lw $t0, 68($sp) + lw $t0, 68($sp) .cfi_restore 8 - lw $a3, 64($sp) + lw $a3, 64($sp) .cfi_restore 7 - lw $a2, 60($sp) + lw $a2, 60($sp) .cfi_restore 6 - lw $a1, 56($sp) - .cfi_restore 5 - LDu $f18, $f19, 48, $sp, $t8 - LDu $f16, $f17, 40, $sp, $t8 - LDu $f14, $f15, 32, $sp, $t8 - LDu $f12, $f13, 24, $sp, $t8 - LDu $f10, $f11, 16, $sp, $t8 - LDu $f8, $f9, 8, $sp, $t8 - addiu $sp, $sp, 112 # pop frame + RESTORE_SAVE_REFS_AND_ARGS_FRAME_A1 + LDu $f18, $f19, 48, $sp, $t8 + LDu $f16, $f17, 40, $sp, $t8 + LDu $f14, $f15, 32, $sp, $t8 + LDu $f12, $f13, 24, $sp, $t8 + LDu $f10, $f11, 16, $sp, $t8 + LDu $f8, $f9, 8, $sp, $t8 + addiu $sp, $sp, 112 # Pop frame. .cfi_adjust_cfa_offset -112 .endm @@ -826,9 +861,10 @@ END art_quick_throw_stack_overflow * On success this wrapper will restore arguments and *jump* to the target, leaving the lr * pointing back to the original caller. */ -.macro INVOKE_TRAMPOLINE_BODY cxx_name +.macro INVOKE_TRAMPOLINE_BODY cxx_name, save_s4_thru_s8_only=0 .extern \cxx_name - SETUP_SAVE_REFS_AND_ARGS_FRAME # save callee saves in case allocation triggers GC + SETUP_SAVE_REFS_AND_ARGS_FRAME \save_s4_thru_s8_only # save callee saves in case + # allocation triggers GC move $a2, rSELF # pass Thread::Current la $t9, \cxx_name jalr $t9 # (method_idx, this, Thread*, $sp) @@ -2063,39 +2099,83 @@ END art_quick_proxy_invoke_handler * a0 is the conflict ArtMethod. * t7 is a hidden argument that holds the target interface method's dex method index. * - * Note that this stub writes to a0, t7 and t8. + * Note that this stub writes to v0-v1, a0, t2-t9, f0-f7. */ + .extern artLookupResolvedMethod + .extern __atomic_load_8 # For int64_t std::atomic::load(std::memory_order). ENTRY art_quick_imt_conflict_trampoline - lw $t8, 0($sp) # Load referrer. - lw $t8, ART_METHOD_DEX_CACHE_METHODS_OFFSET_32($t8) # Load dex cache methods array. - sll $t7, $t7, POINTER_SIZE_SHIFT # Calculate offset. - addu $t7, $t8, $t7 # Add offset to base. - lw $t7, 0($t7) # Load interface method. - lw $a0, ART_METHOD_JNI_OFFSET_32($a0) # Load ImtConflictTable. + SETUP_SAVE_REFS_AND_ARGS_FRAME_REGISTERS_ONLY /* save_s4_thru_s8 */ 0 + + lw $t8, FRAME_SIZE_SAVE_REFS_AND_ARGS($sp) # $t8 = referrer. + la $t9, __atomic_load_8 + addiu $sp, $sp, -ARG_SLOT_SIZE # Reserve argument slots on the stack. + .cfi_adjust_cfa_offset ARG_SLOT_SIZE + lw $t8, ART_METHOD_DEX_CACHE_METHODS_OFFSET_32($t8) # $t8 = dex cache methods array. + + move $s2, $t7 # $s2 = method index (callee-saved). + lw $s3, ART_METHOD_JNI_OFFSET_32($a0) # $s3 = ImtConflictTable (callee-saved). + + sll $t7, $t7, 32 - METHOD_DEX_CACHE_HASH_BITS # $t7 = slot index in top bits, zeroes below. + srl $t7, $t7, 32 - METHOD_DEX_CACHE_HASH_BITS - (POINTER_SIZE_SHIFT + 1) + # $t7 = slot offset. + + li $a1, STD_MEMORY_ORDER_RELAXED # $a1 = std::memory_order_relaxed. + jalr $t9 # [$v0, $v1] = __atomic_load_8($a0, $a1). + addu $a0, $t8, $t7 # $a0 = DexCache method slot address. + + bne $v1, $s2, .Limt_conflict_trampoline_dex_cache_miss # Branch if method index miss. + addiu $sp, $sp, ARG_SLOT_SIZE # Remove argument slots from the stack. + .cfi_adjust_cfa_offset -ARG_SLOT_SIZE .Limt_table_iterate: - lw $t8, 0($a0) # Load next entry in ImtConflictTable. + lw $t8, 0($s3) # Load next entry in ImtConflictTable. # Branch if found. - beq $t8, $t7, .Limt_table_found + beq $t8, $v0, .Limt_table_found nop # If the entry is null, the interface method is not in the ImtConflictTable. beqz $t8, .Lconflict_trampoline nop # Iterate over the entries of the ImtConflictTable. b .Limt_table_iterate - addiu $a0, $a0, 2 * __SIZEOF_POINTER__ # Iterate to the next entry. + addiu $s3, $s3, 2 * __SIZEOF_POINTER__ # Iterate to the next entry. .Limt_table_found: # We successfully hit an entry in the table. Load the target method and jump to it. - lw $a0, __SIZEOF_POINTER__($a0) + .cfi_remember_state + lw $a0, __SIZEOF_POINTER__($s3) lw $t9, ART_METHOD_QUICK_CODE_OFFSET_32($a0) + RESTORE_SAVE_REFS_AND_ARGS_FRAME /* restore_s4_thru_s8 */ 0, /* remove_arg_slots */ 0 jalr $zero, $t9 nop + .cfi_restore_state .Lconflict_trampoline: # Call the runtime stub to populate the ImtConflictTable and jump to the resolved method. - move $a0, $t7 # Load interface method. - INVOKE_TRAMPOLINE_BODY artInvokeInterfaceTrampoline + .cfi_remember_state + RESTORE_SAVE_REFS_AND_ARGS_FRAME_GP # Restore clobbered $gp. + RESTORE_SAVE_REFS_AND_ARGS_FRAME_A1 # Restore this. + move $a0, $v0 # Load interface method. + INVOKE_TRAMPOLINE_BODY artInvokeInterfaceTrampoline, /* save_s4_thru_s8_only */ 1 + .cfi_restore_state + +.Limt_conflict_trampoline_dex_cache_miss: + # We're not creating a proper runtime method frame here, + # artLookupResolvedMethod() is not allowed to walk the stack. + RESTORE_SAVE_REFS_AND_ARGS_FRAME_GP # Restore clobbered $gp. + lw $a1, FRAME_SIZE_SAVE_REFS_AND_ARGS($sp) # $a1 = referrer. + la $t9, artLookupResolvedMethod + addiu $sp, $sp, -ARG_SLOT_SIZE # Reserve argument slots on the stack. + .cfi_adjust_cfa_offset ARG_SLOT_SIZE + jalr $t9 # (uint32_t method_index, ArtMethod* referrer). + move $a0, $s2 # $a0 = method index. + + # If the method wasn't resolved, skip the lookup and go to artInvokeInterfaceTrampoline(). + beqz $v0, .Lconflict_trampoline + addiu $sp, $sp, ARG_SLOT_SIZE # Remove argument slots from the stack. + .cfi_adjust_cfa_offset -ARG_SLOT_SIZE + + b .Limt_table_iterate + nop END art_quick_imt_conflict_trampoline .extern artQuickResolutionTrampoline @@ -2721,6 +2801,385 @@ READ_BARRIER_MARK_REG art_quick_read_barrier_mark_reg22, $s7 READ_BARRIER_MARK_REG art_quick_read_barrier_mark_reg29, $s8 // RA (register 31) is reserved. +// Caller code: +// Short constant offset/index: +// R2: | R6: +// lw $t9, pReadBarrierMarkReg00 +// beqz $t9, skip_call | beqzc $t9, skip_call +// addiu $t9, $t9, thunk_disp | nop +// jalr $t9 | jialc $t9, thunk_disp +// nop | +// skip_call: | skip_call: +// lw `out`, ofs(`obj`) | lw `out`, ofs(`obj`) +// [subu `out`, $zero, `out`] | [subu `out`, $zero, `out`] # Unpoison reference. +.macro BRB_FIELD_SHORT_OFFSET_ENTRY obj +1: + # Explicit null check. May be redundant (for array elements or when the field + # offset is larger than the page size, 4KB). + # $ra will be adjusted to point to lw's stack map when throwing NPE. + beqz \obj, .Lintrospection_throw_npe +#if defined(_MIPS_ARCH_MIPS32R6) + lapc $gp, .Lintrospection_exits # $gp = address of .Lintrospection_exits. +#else + addiu $gp, $t9, (.Lintrospection_exits - 1b) # $gp = address of .Lintrospection_exits. +#endif + .set push + .set noat + lw $at, MIRROR_OBJECT_LOCK_WORD_OFFSET(\obj) + sll $at, $at, 31 - LOCK_WORD_READ_BARRIER_STATE_SHIFT # Move barrier state bit + # to sign bit. + bltz $at, .Lintrospection_field_array # If gray, load reference, mark. + move $t8, \obj # Move `obj` to $t8 for common code. + .set pop + jalr $zero, $ra # Otherwise, load-load barrier and return. + sync +.endm + +// Caller code (R2): +// Long constant offset/index: | Variable index: +// lw $t9, pReadBarrierMarkReg00 +// lui $t8, ofs_hi | sll $t8, `index`, 2 +// beqz $t9, skip_call | beqz $t9, skip_call +// addiu $t9, $t9, thunk_disp | addiu $t9, $t9, thunk_disp +// jalr $t9 | jalr $t9 +// skip_call: | skip_call: +// addu $t8, $t8, `obj` | addu $t8, $t8, `obj` +// lw `out`, ofs_lo($t8) | lw `out`, ofs($t8) +// [subu `out`, $zero, `out`] | [subu `out`, $zero, `out`] # Unpoison reference. +// +// Caller code (R6): +// Long constant offset/index: | Variable index: +// lw $t9, pReadBarrierMarkReg00 +// beqz $t9, skip_call | beqz $t9, skip_call +// aui $t8, `obj`, ofs_hi | lsa $t8, `index`, `obj`, 2 +// jialc $t9, thunk_disp | jialc $t9, thunk_disp +// skip_call: | skip_call: +// lw `out`, ofs_lo($t8) | lw `out`, ofs($t8) +// [subu `out`, $zero, `out`] | [subu `out`, $zero, `out`] # Unpoison reference. +.macro BRB_FIELD_LONG_OFFSET_ENTRY obj +1: + # No explicit null check for variable indices or large constant indices/offsets + # as it must have been done earlier. +#if defined(_MIPS_ARCH_MIPS32R6) + lapc $gp, .Lintrospection_exits # $gp = address of .Lintrospection_exits. +#else + addiu $gp, $t9, (.Lintrospection_exits - 1b) # $gp = address of .Lintrospection_exits. +#endif + .set push + .set noat + lw $at, MIRROR_OBJECT_LOCK_WORD_OFFSET(\obj) + sll $at, $at, 31 - LOCK_WORD_READ_BARRIER_STATE_SHIFT # Move barrier state bit + # to sign bit. + bltz $at, .Lintrospection_field_array # If gray, load reference, mark. + nop + .set pop + jalr $zero, $ra # Otherwise, load-load barrier and return. + sync + break # Padding to 8 instructions. +.endm + +.macro BRB_GC_ROOT_ENTRY root +1: +#if defined(_MIPS_ARCH_MIPS32R6) + lapc $gp, .Lintrospection_exit_\root # $gp = exit point address. +#else + addiu $gp, $t9, (.Lintrospection_exit_\root - 1b) # $gp = exit point address. +#endif + bnez \root, .Lintrospection_common + move $t8, \root # Move reference to $t8 for common code. + jalr $zero, $ra # Return if null. + # The next instruction (from the following BRB_GC_ROOT_ENTRY) fills the delay slot. + # This instruction has no effect (actual NOP for the last entry; otherwise changes $gp, + # which is unused after that anyway). +.endm + +.macro BRB_FIELD_EXIT out +.Lintrospection_exit_\out: + jalr $zero, $ra + move \out, $t8 # Return reference in expected register. +.endm + +.macro BRB_FIELD_EXIT_BREAK + break + break +.endm + +ENTRY_NO_GP art_quick_read_barrier_mark_introspection + # Entry points for offsets/indices not fitting into int16_t and for variable indices. + BRB_FIELD_LONG_OFFSET_ENTRY $v0 + BRB_FIELD_LONG_OFFSET_ENTRY $v1 + BRB_FIELD_LONG_OFFSET_ENTRY $a0 + BRB_FIELD_LONG_OFFSET_ENTRY $a1 + BRB_FIELD_LONG_OFFSET_ENTRY $a2 + BRB_FIELD_LONG_OFFSET_ENTRY $a3 + BRB_FIELD_LONG_OFFSET_ENTRY $t0 + BRB_FIELD_LONG_OFFSET_ENTRY $t1 + BRB_FIELD_LONG_OFFSET_ENTRY $t2 + BRB_FIELD_LONG_OFFSET_ENTRY $t3 + BRB_FIELD_LONG_OFFSET_ENTRY $t4 + BRB_FIELD_LONG_OFFSET_ENTRY $t5 + BRB_FIELD_LONG_OFFSET_ENTRY $t6 + BRB_FIELD_LONG_OFFSET_ENTRY $t7 + BRB_FIELD_LONG_OFFSET_ENTRY $s2 + BRB_FIELD_LONG_OFFSET_ENTRY $s3 + BRB_FIELD_LONG_OFFSET_ENTRY $s4 + BRB_FIELD_LONG_OFFSET_ENTRY $s5 + BRB_FIELD_LONG_OFFSET_ENTRY $s6 + BRB_FIELD_LONG_OFFSET_ENTRY $s7 + BRB_FIELD_LONG_OFFSET_ENTRY $s8 + + # Entry points for offsets/indices fitting into int16_t. + BRB_FIELD_SHORT_OFFSET_ENTRY $v0 + BRB_FIELD_SHORT_OFFSET_ENTRY $v1 + BRB_FIELD_SHORT_OFFSET_ENTRY $a0 + BRB_FIELD_SHORT_OFFSET_ENTRY $a1 + BRB_FIELD_SHORT_OFFSET_ENTRY $a2 + BRB_FIELD_SHORT_OFFSET_ENTRY $a3 + BRB_FIELD_SHORT_OFFSET_ENTRY $t0 + BRB_FIELD_SHORT_OFFSET_ENTRY $t1 + BRB_FIELD_SHORT_OFFSET_ENTRY $t2 + BRB_FIELD_SHORT_OFFSET_ENTRY $t3 + BRB_FIELD_SHORT_OFFSET_ENTRY $t4 + BRB_FIELD_SHORT_OFFSET_ENTRY $t5 + BRB_FIELD_SHORT_OFFSET_ENTRY $t6 + BRB_FIELD_SHORT_OFFSET_ENTRY $t7 + BRB_FIELD_SHORT_OFFSET_ENTRY $s2 + BRB_FIELD_SHORT_OFFSET_ENTRY $s3 + BRB_FIELD_SHORT_OFFSET_ENTRY $s4 + BRB_FIELD_SHORT_OFFSET_ENTRY $s5 + BRB_FIELD_SHORT_OFFSET_ENTRY $s6 + BRB_FIELD_SHORT_OFFSET_ENTRY $s7 + BRB_FIELD_SHORT_OFFSET_ENTRY $s8 + + .global art_quick_read_barrier_mark_introspection_gc_roots +art_quick_read_barrier_mark_introspection_gc_roots: + # Entry points for GC roots. + BRB_GC_ROOT_ENTRY $v0 + BRB_GC_ROOT_ENTRY $v1 + BRB_GC_ROOT_ENTRY $a0 + BRB_GC_ROOT_ENTRY $a1 + BRB_GC_ROOT_ENTRY $a2 + BRB_GC_ROOT_ENTRY $a3 + BRB_GC_ROOT_ENTRY $t0 + BRB_GC_ROOT_ENTRY $t1 + BRB_GC_ROOT_ENTRY $t2 + BRB_GC_ROOT_ENTRY $t3 + BRB_GC_ROOT_ENTRY $t4 + BRB_GC_ROOT_ENTRY $t5 + BRB_GC_ROOT_ENTRY $t6 + BRB_GC_ROOT_ENTRY $t7 + BRB_GC_ROOT_ENTRY $s2 + BRB_GC_ROOT_ENTRY $s3 + BRB_GC_ROOT_ENTRY $s4 + BRB_GC_ROOT_ENTRY $s5 + BRB_GC_ROOT_ENTRY $s6 + BRB_GC_ROOT_ENTRY $s7 + BRB_GC_ROOT_ENTRY $s8 + .global art_quick_read_barrier_mark_introspection_end_of_entries +art_quick_read_barrier_mark_introspection_end_of_entries: + nop # Fill the delay slot of the last BRB_GC_ROOT_ENTRY. + +.Lintrospection_throw_npe: + b art_quick_throw_null_pointer_exception + addiu $ra, $ra, 4 # Skip lw, make $ra point to lw's stack map. + + .set push + .set noat + + // Fields and array elements. + +.Lintrospection_field_array: + // Get the field/element address using $t8 and the offset from the lw instruction. + lh $at, 0($ra) # $ra points to lw: $at = field/element offset. + addiu $ra, $ra, 4 + HEAP_POISON_INSTR_SIZE # Skip lw(+subu). + addu $t8, $t8, $at # $t8 = field/element address. + + // Calculate the address of the exit point, store it in $gp and load the reference into $t8. + lb $at, (-HEAP_POISON_INSTR_SIZE - 2)($ra) # $ra-HEAP_POISON_INSTR_SIZE-4 points to + # "lw `out`, ...". + andi $at, $at, 31 # Extract `out` from lw. + sll $at, $at, 3 # Multiply `out` by the exit point size (BRB_FIELD_EXIT* macros). + + lw $t8, 0($t8) # $t8 = reference. + UNPOISON_HEAP_REF $t8 + + // Return if null reference. + bnez $t8, .Lintrospection_common + addu $gp, $gp, $at # $gp = address of the exit point. + + // Early return through the exit point. +.Lintrospection_return_early: + jalr $zero, $gp # Move $t8 to `out` and return. + nop + + // Code common for GC roots, fields and array elements. + +.Lintrospection_common: + // Check lock word for mark bit, if marked return. + lw $t9, MIRROR_OBJECT_LOCK_WORD_OFFSET($t8) + sll $at, $t9, 31 - LOCK_WORD_MARK_BIT_SHIFT # Move mark bit to sign bit. + bltz $at, .Lintrospection_return_early +#if (LOCK_WORD_STATE_SHIFT != 30) || (LOCK_WORD_STATE_FORWARDING_ADDRESS != 3) + // The below code depends on the lock word state being in the highest bits + // and the "forwarding address" state having all bits set. +#error "Unexpected lock word state shift or forwarding address state value." +#endif + // Test that both the forwarding state bits are 1. + sll $at, $t9, 1 + and $at, $at, $t9 # Sign bit = 1 IFF both bits are 1. + bgez $at, .Lintrospection_mark + nop + + .set pop + + // Shift left by the forwarding address shift. This clears out the state bits since they are + // in the top 2 bits of the lock word. + jalr $zero, $gp # Move $t8 to `out` and return. + sll $t8, $t9, LOCK_WORD_STATE_FORWARDING_ADDRESS_SHIFT + +.Lintrospection_mark: + // Partially set up the stack frame preserving only $ra. + addiu $sp, $sp, -160 # Includes 16 bytes of space for argument registers $a0-$a3. + .cfi_adjust_cfa_offset 160 + sw $ra, 156($sp) + .cfi_rel_offset 31, 156 + + // Set up $gp, clobbering $ra and using the branch delay slot for a useful instruction. + bal 1f + sw $gp, 152($sp) # Preserve the exit point address. +1: + .cpload $ra + + // Finalize the stack frame and call. + sw $t7, 148($sp) + .cfi_rel_offset 15, 148 + sw $t6, 144($sp) + .cfi_rel_offset 14, 144 + sw $t5, 140($sp) + .cfi_rel_offset 13, 140 + sw $t4, 136($sp) + .cfi_rel_offset 12, 136 + sw $t3, 132($sp) + .cfi_rel_offset 11, 132 + sw $t2, 128($sp) + .cfi_rel_offset 10, 128 + sw $t1, 124($sp) + .cfi_rel_offset 9, 124 + sw $t0, 120($sp) + .cfi_rel_offset 8, 120 + sw $a3, 116($sp) + .cfi_rel_offset 7, 116 + sw $a2, 112($sp) + .cfi_rel_offset 6, 112 + sw $a1, 108($sp) + .cfi_rel_offset 5, 108 + sw $a0, 104($sp) + .cfi_rel_offset 4, 104 + sw $v1, 100($sp) + .cfi_rel_offset 3, 100 + sw $v0, 96($sp) + .cfi_rel_offset 2, 96 + + la $t9, artReadBarrierMark + + sdc1 $f18, 88($sp) + sdc1 $f16, 80($sp) + sdc1 $f14, 72($sp) + sdc1 $f12, 64($sp) + sdc1 $f10, 56($sp) + sdc1 $f8, 48($sp) + sdc1 $f6, 40($sp) + sdc1 $f4, 32($sp) + sdc1 $f2, 24($sp) + sdc1 $f0, 16($sp) + + jalr $t9 # $v0 <- artReadBarrierMark(reference) + move $a0, $t8 # Pass reference in $a0. + move $t8, $v0 + + lw $ra, 156($sp) + .cfi_restore 31 + lw $gp, 152($sp) # $gp = address of the exit point. + lw $t7, 148($sp) + .cfi_restore 15 + lw $t6, 144($sp) + .cfi_restore 14 + lw $t5, 140($sp) + .cfi_restore 13 + lw $t4, 136($sp) + .cfi_restore 12 + lw $t3, 132($sp) + .cfi_restore 11 + lw $t2, 128($sp) + .cfi_restore 10 + lw $t1, 124($sp) + .cfi_restore 9 + lw $t0, 120($sp) + .cfi_restore 8 + lw $a3, 116($sp) + .cfi_restore 7 + lw $a2, 112($sp) + .cfi_restore 6 + lw $a1, 108($sp) + .cfi_restore 5 + lw $a0, 104($sp) + .cfi_restore 4 + lw $v1, 100($sp) + .cfi_restore 3 + lw $v0, 96($sp) + .cfi_restore 2 + + ldc1 $f18, 88($sp) + ldc1 $f16, 80($sp) + ldc1 $f14, 72($sp) + ldc1 $f12, 64($sp) + ldc1 $f10, 56($sp) + ldc1 $f8, 48($sp) + ldc1 $f6, 40($sp) + ldc1 $f4, 32($sp) + ldc1 $f2, 24($sp) + ldc1 $f0, 16($sp) + + // Return through the exit point. + jalr $zero, $gp # Move $t8 to `out` and return. + addiu $sp, $sp, 160 + .cfi_adjust_cfa_offset -160 + +.Lintrospection_exits: + BRB_FIELD_EXIT_BREAK + BRB_FIELD_EXIT_BREAK + BRB_FIELD_EXIT $v0 + BRB_FIELD_EXIT $v1 + BRB_FIELD_EXIT $a0 + BRB_FIELD_EXIT $a1 + BRB_FIELD_EXIT $a2 + BRB_FIELD_EXIT $a3 + BRB_FIELD_EXIT $t0 + BRB_FIELD_EXIT $t1 + BRB_FIELD_EXIT $t2 + BRB_FIELD_EXIT $t3 + BRB_FIELD_EXIT $t4 + BRB_FIELD_EXIT $t5 + BRB_FIELD_EXIT $t6 + BRB_FIELD_EXIT $t7 + BRB_FIELD_EXIT_BREAK + BRB_FIELD_EXIT_BREAK + BRB_FIELD_EXIT $s2 + BRB_FIELD_EXIT $s3 + BRB_FIELD_EXIT $s4 + BRB_FIELD_EXIT $s5 + BRB_FIELD_EXIT $s6 + BRB_FIELD_EXIT $s7 + BRB_FIELD_EXIT_BREAK + BRB_FIELD_EXIT_BREAK + BRB_FIELD_EXIT_BREAK + BRB_FIELD_EXIT_BREAK + BRB_FIELD_EXIT_BREAK + BRB_FIELD_EXIT_BREAK + BRB_FIELD_EXIT $s8 + BRB_FIELD_EXIT_BREAK +END art_quick_read_barrier_mark_introspection + .extern artInvokePolymorphic ENTRY art_quick_invoke_polymorphic SETUP_SAVE_REFS_AND_ARGS_FRAME diff --git a/runtime/arch/mips64/asm_support_mips64.S b/runtime/arch/mips64/asm_support_mips64.S index ef82bd239d..a6b249ae56 100644 --- a/runtime/arch/mips64/asm_support_mips64.S +++ b/runtime/arch/mips64/asm_support_mips64.S @@ -83,6 +83,13 @@ #endif // USE_HEAP_POISONING .endm +// Byte size of the instructions (un)poisoning heap references. +#ifdef USE_HEAP_POISONING +#define HEAP_POISON_INSTR_SIZE 8 +#else +#define HEAP_POISON_INSTR_SIZE 0 +#endif // USE_HEAP_POISONING + // Based on contents of creg select the minimum integer // At the end of the macro the original value of creg is lost .macro MINint dreg,rreg,sreg,creg diff --git a/runtime/arch/mips64/asm_support_mips64.h b/runtime/arch/mips64/asm_support_mips64.h index 9063d20ecf..7185da550c 100644 --- a/runtime/arch/mips64/asm_support_mips64.h +++ b/runtime/arch/mips64/asm_support_mips64.h @@ -28,4 +28,24 @@ // $f0-$f31, $at, $v0-$v1, $a0-$a7, $t0-$t3, $s0-$s7, $t8-$t9, $gp, $s8, $ra + padding + method* #define FRAME_SIZE_SAVE_EVERYTHING 496 +// &art_quick_read_barrier_mark_introspection is the first of many entry points: +// 20 entry points for long field offsets, large array indices and variable array indices +// (see macro BRB_FIELD_LONG_OFFSET_ENTRY) +// 20 entry points for short field offsets and small array indices +// (see macro BRB_FIELD_SHORT_OFFSET_ENTRY) +// 20 entry points for GC roots +// (see macro BRB_GC_ROOT_ENTRY) + +// There are as many entry points of each kind as there are registers that +// can hold a reference: V0-V1, A0-A7, T0-T2, S2-S8. +#define BAKER_MARK_INTROSPECTION_REGISTER_COUNT 20 + +#define BAKER_MARK_INTROSPECTION_FIELD_ARRAY_ENTRY_SIZE (8 * 4) // 8 instructions in + // BRB_FIELD_*_OFFSET_ENTRY. + +#define BAKER_MARK_INTROSPECTION_GC_ROOT_ENTRIES_OFFSET \ + (2 * BAKER_MARK_INTROSPECTION_REGISTER_COUNT * BAKER_MARK_INTROSPECTION_FIELD_ARRAY_ENTRY_SIZE) + +#define BAKER_MARK_INTROSPECTION_GC_ROOT_ENTRY_SIZE (4 * 4) // 4 instructions in BRB_GC_ROOT_ENTRY. + #endif // ART_RUNTIME_ARCH_MIPS64_ASM_SUPPORT_MIPS64_H_ diff --git a/runtime/arch/mips64/entrypoints_init_mips64.cc b/runtime/arch/mips64/entrypoints_init_mips64.cc index 007f7b3915..15b3e38f8a 100644 --- a/runtime/arch/mips64/entrypoints_init_mips64.cc +++ b/runtime/arch/mips64/entrypoints_init_mips64.cc @@ -17,14 +17,15 @@ #include <math.h> #include <string.h> +#include "arch/mips64/asm_support_mips64.h" #include "atomic.h" +#include "entrypoints/entrypoint_utils.h" #include "entrypoints/jni/jni_entrypoints.h" +#include "entrypoints/math_entrypoints.h" #include "entrypoints/quick/quick_alloc_entrypoints.h" #include "entrypoints/quick/quick_default_externs.h" #include "entrypoints/quick/quick_default_init_entrypoints.h" #include "entrypoints/quick/quick_entrypoints.h" -#include "entrypoints/entrypoint_utils.h" -#include "entrypoints/math_entrypoints.h" #include "entrypoints/runtime_asm_entrypoints.h" #include "interpreter/interpreter.h" @@ -59,6 +60,10 @@ extern "C" mirror::Object* art_quick_read_barrier_mark_reg21(mirror::Object*); extern "C" mirror::Object* art_quick_read_barrier_mark_reg22(mirror::Object*); extern "C" mirror::Object* art_quick_read_barrier_mark_reg29(mirror::Object*); +extern "C" mirror::Object* art_quick_read_barrier_mark_introspection(mirror::Object*); +extern "C" mirror::Object* art_quick_read_barrier_mark_introspection_gc_roots(mirror::Object*); +extern "C" void art_quick_read_barrier_mark_introspection_end_of_entries(void); + // Math entrypoints. extern int32_t CmpgDouble(double a, double b); extern int32_t CmplDouble(double a, double b); @@ -88,6 +93,21 @@ extern "C" int64_t __moddi3(int64_t, int64_t); // No read barrier entrypoints for marking registers. void UpdateReadBarrierEntrypoints(QuickEntryPoints* qpoints, bool is_active) { + intptr_t introspection_field_array_entries_size = + reinterpret_cast<intptr_t>(&art_quick_read_barrier_mark_introspection_gc_roots) - + reinterpret_cast<intptr_t>(&art_quick_read_barrier_mark_introspection); + static_assert( + BAKER_MARK_INTROSPECTION_GC_ROOT_ENTRIES_OFFSET == 2 * + BAKER_MARK_INTROSPECTION_REGISTER_COUNT * BAKER_MARK_INTROSPECTION_FIELD_ARRAY_ENTRY_SIZE, + "Expecting equal"); + DCHECK_EQ(introspection_field_array_entries_size, + BAKER_MARK_INTROSPECTION_GC_ROOT_ENTRIES_OFFSET); + intptr_t introspection_gc_root_entries_size = + reinterpret_cast<intptr_t>(&art_quick_read_barrier_mark_introspection_end_of_entries) - + reinterpret_cast<intptr_t>(&art_quick_read_barrier_mark_introspection_gc_roots); + DCHECK_EQ(introspection_gc_root_entries_size, + BAKER_MARK_INTROSPECTION_REGISTER_COUNT * BAKER_MARK_INTROSPECTION_GC_ROOT_ENTRY_SIZE); + qpoints->pReadBarrierMarkReg00 = is_active ? art_quick_read_barrier_mark_introspection : nullptr; qpoints->pReadBarrierMarkReg01 = is_active ? art_quick_read_barrier_mark_reg01 : nullptr; qpoints->pReadBarrierMarkReg02 = is_active ? art_quick_read_barrier_mark_reg02 : nullptr; qpoints->pReadBarrierMarkReg03 = is_active ? art_quick_read_barrier_mark_reg03 : nullptr; @@ -173,7 +193,6 @@ void InitEntryPoints(JniEntryPoints* jpoints, QuickEntryPoints* qpoints) { // Cannot use the following registers to pass arguments: // 0(ZERO), 1(AT), 15(T3), 16(S0), 17(S1), 24(T8), 25(T9), 26(K0), 27(K1), 28(GP), 29(SP), 31(RA). // Note that there are 30 entry points only: 00 for register 1(AT), ..., 29 for register 30(S8). - qpoints->pReadBarrierMarkReg00 = nullptr; qpoints->pReadBarrierMarkReg14 = nullptr; qpoints->pReadBarrierMarkReg15 = nullptr; qpoints->pReadBarrierMarkReg16 = nullptr; diff --git a/runtime/arch/mips64/quick_entrypoints_mips64.S b/runtime/arch/mips64/quick_entrypoints_mips64.S index d427fe320b..3b92daa0cc 100644 --- a/runtime/arch/mips64/quick_entrypoints_mips64.S +++ b/runtime/arch/mips64/quick_entrypoints_mips64.S @@ -188,7 +188,23 @@ // This assumes the top part of these stack frame types are identical. #define REFS_AND_ARGS_MINUS_REFS_SIZE (FRAME_SIZE_SAVE_REFS_AND_ARGS - FRAME_SIZE_SAVE_REFS_ONLY) -.macro SETUP_SAVE_REFS_AND_ARGS_FRAME_INTERNAL + /* + * Individually usable part of macro SETUP_SAVE_REFS_AND_ARGS_FRAME_INTERNAL. + */ +.macro SETUP_SAVE_REFS_AND_ARGS_FRAME_S4_THRU_S8 + sd $s8, 192($sp) + .cfi_rel_offset 30, 192 + sd $s7, 176($sp) + .cfi_rel_offset 23, 176 + sd $s6, 168($sp) + .cfi_rel_offset 22, 168 + sd $s5, 160($sp) + .cfi_rel_offset 21, 160 + sd $s4, 152($sp) + .cfi_rel_offset 20, 152 +.endm + +.macro SETUP_SAVE_REFS_AND_ARGS_FRAME_INTERNAL save_s4_thru_s8=1 daddiu $sp, $sp, -208 .cfi_adjust_cfa_offset 208 @@ -197,48 +213,40 @@ #error "FRAME_SIZE_SAVE_REFS_AND_ARGS(MIPS64) size not as expected." #endif - sd $ra, 200($sp) # = kQuickCalleeSaveFrame_RefAndArgs_LrOffset + sd $ra, 200($sp) # = kQuickCalleeSaveFrame_RefAndArgs_LrOffset .cfi_rel_offset 31, 200 - sd $s8, 192($sp) - .cfi_rel_offset 30, 192 - sd $t8, 184($sp) # t8 holds caller's gp, now save it to the stack. - .cfi_rel_offset 28, 184 # Value from gp is pushed, so set the cfi offset accordingly. - sd $s7, 176($sp) - .cfi_rel_offset 23, 176 - sd $s6, 168($sp) - .cfi_rel_offset 22, 168 - sd $s5, 160($sp) - .cfi_rel_offset 21, 160 - sd $s4, 152($sp) - .cfi_rel_offset 20, 152 - sd $s3, 144($sp) + sd $t8, 184($sp) # t8 holds caller's gp, now save it to the stack. + .cfi_rel_offset 28, 184 # Value from gp is pushed, so set the cfi offset accordingly. + .if \save_s4_thru_s8 + SETUP_SAVE_REFS_AND_ARGS_FRAME_S4_THRU_S8 + .endif + sd $s3, 144($sp) .cfi_rel_offset 19, 144 - sd $s2, 136($sp) + sd $s2, 136($sp) .cfi_rel_offset 18, 136 - - sd $a7, 128($sp) + sd $a7, 128($sp) .cfi_rel_offset 11, 128 - sd $a6, 120($sp) + sd $a6, 120($sp) .cfi_rel_offset 10, 120 - sd $a5, 112($sp) + sd $a5, 112($sp) .cfi_rel_offset 9, 112 - sd $a4, 104($sp) + sd $a4, 104($sp) .cfi_rel_offset 8, 104 - sd $a3, 96($sp) + sd $a3, 96($sp) .cfi_rel_offset 7, 96 - sd $a2, 88($sp) + sd $a2, 88($sp) .cfi_rel_offset 6, 88 - sd $a1, 80($sp) # = kQuickCalleeSaveFrame_RefAndArgs_Gpr1Offset + sd $a1, 80($sp) # = kQuickCalleeSaveFrame_RefAndArgs_Gpr1Offset .cfi_rel_offset 5, 80 - s.d $f19, 72($sp) - s.d $f18, 64($sp) - s.d $f17, 56($sp) - s.d $f16, 48($sp) - s.d $f15, 40($sp) - s.d $f14, 32($sp) - s.d $f13, 24($sp) # = kQuickCalleeSaveFrame_RefAndArgs_Fpr1Offset - s.d $f12, 16($sp) # This isn't necessary to store. + s.d $f19, 72($sp) + s.d $f18, 64($sp) + s.d $f17, 56($sp) + s.d $f16, 48($sp) + s.d $f15, 40($sp) + s.d $f14, 32($sp) + s.d $f13, 24($sp) # = kQuickCalleeSaveFrame_RefAndArgs_Fpr1Offset + s.d $f12, 16($sp) # This isn't necessary to store. # 1x8 bytes padding + Method* .endm @@ -248,8 +256,14 @@ * non-moving GC. * callee-save: padding + $f12-$f19 + $a1-$a7 + $s2-$s7 + $gp + $ra + $s8 = 24 total + 1 words padding + Method* */ -.macro SETUP_SAVE_REFS_AND_ARGS_FRAME - SETUP_SAVE_REFS_AND_ARGS_FRAME_INTERNAL +.macro SETUP_SAVE_REFS_AND_ARGS_FRAME save_s4_thru_s8_only=0 + .if \save_s4_thru_s8_only + // It is expected that `SETUP_SAVE_REFS_AND_ARGS_FRAME_INTERNAL /* save_s4_thru_s8 */ 0` + // has been done prior to `SETUP_SAVE_REFS_AND_ARGS_FRAME /* save_s4_thru_s8_only */ 1`. + SETUP_SAVE_REFS_AND_ARGS_FRAME_S4_THRU_S8 + .else + SETUP_SAVE_REFS_AND_ARGS_FRAME_INTERNAL + .endif # load appropriate callee-save-method ld $t1, %got(_ZN3art7Runtime9instance_E)($gp) ld $t1, 0($t1) @@ -264,52 +278,62 @@ sd $sp, THREAD_TOP_QUICK_FRAME_OFFSET(rSELF) # Place sp in Thread::Current()->top_quick_frame. .endm -.macro RESTORE_SAVE_REFS_AND_ARGS_FRAME - ld $ra, 200($sp) + /* + * Individually usable part of macro RESTORE_SAVE_REFS_AND_ARGS_FRAME. + */ +.macro RESTORE_SAVE_REFS_AND_ARGS_FRAME_A1 + ld $a1, 80($sp) + .cfi_restore 5 +.endm + +.macro RESTORE_SAVE_REFS_AND_ARGS_FRAME restore_s4_thru_s8=1 + ld $ra, 200($sp) .cfi_restore 31 - ld $s8, 192($sp) - .cfi_restore 30 - ld $t8, 184($sp) # Restore gp back to it's temp storage. + .if \restore_s4_thru_s8 + ld $s8, 192($sp) + .cfi_restore 30 + .endif + ld $t8, 184($sp) # Restore gp back to it's temp storage. .cfi_restore 28 - ld $s7, 176($sp) - .cfi_restore 23 - ld $s6, 168($sp) - .cfi_restore 22 - ld $s5, 160($sp) - .cfi_restore 21 - ld $s4, 152($sp) - .cfi_restore 20 - ld $s3, 144($sp) + .if \restore_s4_thru_s8 + ld $s7, 176($sp) + .cfi_restore 23 + ld $s6, 168($sp) + .cfi_restore 22 + ld $s5, 160($sp) + .cfi_restore 21 + ld $s4, 152($sp) + .cfi_restore 20 + .endif + ld $s3, 144($sp) .cfi_restore 19 - ld $s2, 136($sp) + ld $s2, 136($sp) .cfi_restore 18 - - ld $a7, 128($sp) + ld $a7, 128($sp) .cfi_restore 11 - ld $a6, 120($sp) + ld $a6, 120($sp) .cfi_restore 10 - ld $a5, 112($sp) + ld $a5, 112($sp) .cfi_restore 9 - ld $a4, 104($sp) + ld $a4, 104($sp) .cfi_restore 8 - ld $a3, 96($sp) + ld $a3, 96($sp) .cfi_restore 7 - ld $a2, 88($sp) + ld $a2, 88($sp) .cfi_restore 6 - ld $a1, 80($sp) - .cfi_restore 5 + RESTORE_SAVE_REFS_AND_ARGS_FRAME_A1 - l.d $f19, 72($sp) - l.d $f18, 64($sp) - l.d $f17, 56($sp) - l.d $f16, 48($sp) - l.d $f15, 40($sp) - l.d $f14, 32($sp) - l.d $f13, 24($sp) - l.d $f12, 16($sp) + l.d $f19, 72($sp) + l.d $f18, 64($sp) + l.d $f17, 56($sp) + l.d $f16, 48($sp) + l.d $f15, 40($sp) + l.d $f14, 32($sp) + l.d $f13, 24($sp) + l.d $f12, 16($sp) .cpreturn - daddiu $sp, $sp, 208 + daddiu $sp, $sp, 208 .cfi_adjust_cfa_offset -208 .endm @@ -847,7 +871,7 @@ ENTRY_NO_GP_CUSTOM_CFA art_quick_throw_null_pointer_exception_from_signal, FRAME dla $t9, artThrowNullPointerExceptionFromSignal jalr $zero, $t9 # artThrowNullPointerExceptionFromSignal(uinptr_t, Thread*) move $a1, rSELF # pass Thread::Current -END art_quick_throw_null_pointer_exception +END art_quick_throw_null_pointer_exception_from_signal /* * Called by managed code to create and deliver an ArithmeticException @@ -913,9 +937,10 @@ END art_quick_throw_stack_overflow * On success this wrapper will restore arguments and *jump* to the target, leaving the ra * pointing back to the original caller. */ -.macro INVOKE_TRAMPOLINE_BODY cxx_name +.macro INVOKE_TRAMPOLINE_BODY cxx_name, save_s4_thru_s8_only=0 .extern \cxx_name - SETUP_SAVE_REFS_AND_ARGS_FRAME # save callee saves in case allocation triggers GC + SETUP_SAVE_REFS_AND_ARGS_FRAME \save_s4_thru_s8_only # save callee saves in case + # allocation triggers GC move $a2, rSELF # pass Thread::Current jal \cxx_name # (method_idx, this, Thread*, $sp) move $a3, $sp # pass $sp @@ -1986,38 +2011,69 @@ END art_quick_proxy_invoke_handler * a0 is the conflict ArtMethod. * t0 is a hidden argument that holds the target interface method's dex method index. * - * Mote that this stub writes to a0, t0 and t1. + * Mote that this stub writes to v0-v1, a0, t0-t3, t8-t9, f0-f11, f20-f23. */ + .extern artLookupResolvedMethod + .extern __atomic_load_16 # For __int128_t std::atomic::load(std::memory_order). ENTRY art_quick_imt_conflict_trampoline - ld $t1, 0($sp) # Load referrer. - ld $t1, ART_METHOD_DEX_CACHE_METHODS_OFFSET_64($t1) # Load dex cache methods array. - dsll $t0, $t0, POINTER_SIZE_SHIFT # Calculate offset. - daddu $t0, $t1, $t0 # Add offset to base. - ld $t0, 0($t0) # Load interface method. - ld $a0, ART_METHOD_JNI_OFFSET_64($a0) # Load ImtConflictTable. + SETUP_SAVE_REFS_AND_ARGS_FRAME_INTERNAL /* save_s4_thru_s8 */ 0 + + ld $t1, FRAME_SIZE_SAVE_REFS_AND_ARGS($sp) # $t1 = referrer. + dla $t9, __atomic_load_16 + ld $t1, ART_METHOD_DEX_CACHE_METHODS_OFFSET_64($t1) # $t1 = dex cache methods array. + + dext $s2, $t0, 0, 32 # $s2 = zero-extended method index + # (callee-saved). + ld $s3, ART_METHOD_JNI_OFFSET_64($a0) # $s3 = ImtConflictTable (callee-saved). + + dext $t0, $t0, 0, METHOD_DEX_CACHE_HASH_BITS # $t0 = slot index. + + li $a1, STD_MEMORY_ORDER_RELAXED # $a1 = std::memory_order_relaxed. + jalr $t9 # [$v0, $v1] = __atomic_load_16($a0, $a1). + dlsa $a0, $t0, $t1, POINTER_SIZE_SHIFT + 1 # $a0 = DexCache method slot address. + + bnec $v1, $s2, .Limt_conflict_trampoline_dex_cache_miss # Branch if method index miss. .Limt_table_iterate: - ld $t1, 0($a0) # Load next entry in ImtConflictTable. + ld $t1, 0($s3) # Load next entry in ImtConflictTable. # Branch if found. - beq $t1, $t0, .Limt_table_found + beq $t1, $v0, .Limt_table_found nop # If the entry is null, the interface method is not in the ImtConflictTable. beqzc $t1, .Lconflict_trampoline # Iterate over the entries of the ImtConflictTable. - daddiu $a0, $a0, 2 * __SIZEOF_POINTER__ # Iterate to the next entry. - bc .Limt_table_iterate + daddiu $s3, $s3, 2 * __SIZEOF_POINTER__ # Iterate to the next entry. + bc .Limt_table_iterate .Limt_table_found: # We successfully hit an entry in the table. Load the target method and jump to it. - ld $a0, __SIZEOF_POINTER__($a0) + .cfi_remember_state + ld $a0, __SIZEOF_POINTER__($s3) ld $t9, ART_METHOD_QUICK_CODE_OFFSET_64($a0) - jr $t9 - .cpreturn # Restore gp from t8 in branch delay slot. + RESTORE_SAVE_REFS_AND_ARGS_FRAME /* restore_s4_thru_s8 */ 0 + jic $t9, 0 + .cfi_restore_state .Lconflict_trampoline: # Call the runtime stub to populate the ImtConflictTable and jump to the resolved method. - move $a0, $t0 # Load interface method. - INVOKE_TRAMPOLINE_BODY artInvokeInterfaceTrampoline + .cfi_remember_state + RESTORE_SAVE_REFS_AND_ARGS_FRAME_A1 # Restore this. + move $a0, $v0 # Load interface method. + INVOKE_TRAMPOLINE_BODY artInvokeInterfaceTrampoline, /* save_s4_thru_s8_only */ 1 + .cfi_restore_state + +.Limt_conflict_trampoline_dex_cache_miss: + # We're not creating a proper runtime method frame here, + # artLookupResolvedMethod() is not allowed to walk the stack. + dla $t9, artLookupResolvedMethod + ld $a1, FRAME_SIZE_SAVE_REFS_AND_ARGS($sp) # $a1 = referrer. + jalr $t9 # (uint32_t method_index, ArtMethod* referrer). + sll $a0, $s2, 0 # $a0 = sign-extended method index. + + # If the method wasn't resolved, skip the lookup and go to artInvokeInterfaceTrampoline(). + beqzc $v0, .Lconflict_trampoline + nop + bc .Limt_table_iterate END art_quick_imt_conflict_trampoline .extern artQuickResolutionTrampoline @@ -2567,6 +2623,375 @@ READ_BARRIER_MARK_REG art_quick_read_barrier_mark_reg22, $s7 READ_BARRIER_MARK_REG art_quick_read_barrier_mark_reg29, $s8 // RA (register 31) is reserved. +// Caller code: +// Short constant offset/index: +// ld $t9, pReadBarrierMarkReg00 +// beqzc $t9, skip_call +// nop +// jialc $t9, thunk_disp +// skip_call: +// lwu `out`, ofs(`obj`) +// [dsubu `out`, $zero, `out` +// dext `out`, `out`, 0, 32] # Unpoison reference. +.macro BRB_FIELD_SHORT_OFFSET_ENTRY obj + # Explicit null check. May be redundant (for array elements or when the field + # offset is larger than the page size, 4KB). + # $ra will be adjusted to point to lwu's stack map when throwing NPE. + beqzc \obj, .Lintrospection_throw_npe + lapc $t3, .Lintrospection_exits # $t3 = address of .Lintrospection_exits. + .set push + .set noat + lw $at, MIRROR_OBJECT_LOCK_WORD_OFFSET(\obj) + sll $at, $at, 31 - LOCK_WORD_READ_BARRIER_STATE_SHIFT # Move barrier state bit + # to sign bit. + bltz $at, .Lintrospection_field_array # If gray, load reference, mark. + move $t8, \obj # Move `obj` to $t8 for common code. + .set pop + jalr $zero, $ra # Otherwise, load-load barrier and return. + sync +.endm + +// Caller code: +// Long constant offset/index: | Variable index: +// ld $t9, pReadBarrierMarkReg00 +// beqz $t9, skip_call | beqz $t9, skip_call +// daui $t8, `obj`, ofs_hi | dlsa $t8, `index`, `obj`, 2 +// jialc $t9, thunk_disp | jialc $t9, thunk_disp +// skip_call: | skip_call: +// lwu `out`, ofs_lo($t8) | lwu `out`, ofs($t8) +// [dsubu `out`, $zero, `out` | [dsubu `out`, $zero, `out` +// dext `out`, `out`, 0, 32] | dext `out`, `out`, 0, 32] # Unpoison reference. +.macro BRB_FIELD_LONG_OFFSET_ENTRY obj + # No explicit null check for variable indices or large constant indices/offsets + # as it must have been done earlier. + lapc $t3, .Lintrospection_exits # $t3 = address of .Lintrospection_exits. + .set push + .set noat + lw $at, MIRROR_OBJECT_LOCK_WORD_OFFSET(\obj) + sll $at, $at, 31 - LOCK_WORD_READ_BARRIER_STATE_SHIFT # Move barrier state bit + # to sign bit. + bltzc $at, .Lintrospection_field_array # If gray, load reference, mark. + .set pop + sync # Otherwise, load-load barrier and return. + jic $ra, 0 + break # Padding to 8 instructions. + break +.endm + +.macro BRB_GC_ROOT_ENTRY root + lapc $t3, .Lintrospection_exit_\root # $t3 = exit point address. + bnez \root, .Lintrospection_common + move $t8, \root # Move reference to $t8 for common code. + jic $ra, 0 # Return if null. +.endm + +.macro BRB_FIELD_EXIT out +.Lintrospection_exit_\out: + jalr $zero, $ra + move \out, $t8 # Return reference in expected register. +.endm + +.macro BRB_FIELD_EXIT_BREAK + break + break +.endm + +ENTRY_NO_GP art_quick_read_barrier_mark_introspection + # Entry points for offsets/indices not fitting into int16_t and for variable indices. + BRB_FIELD_LONG_OFFSET_ENTRY $v0 + BRB_FIELD_LONG_OFFSET_ENTRY $v1 + BRB_FIELD_LONG_OFFSET_ENTRY $a0 + BRB_FIELD_LONG_OFFSET_ENTRY $a1 + BRB_FIELD_LONG_OFFSET_ENTRY $a2 + BRB_FIELD_LONG_OFFSET_ENTRY $a3 + BRB_FIELD_LONG_OFFSET_ENTRY $a4 + BRB_FIELD_LONG_OFFSET_ENTRY $a5 + BRB_FIELD_LONG_OFFSET_ENTRY $a6 + BRB_FIELD_LONG_OFFSET_ENTRY $a7 + BRB_FIELD_LONG_OFFSET_ENTRY $t0 + BRB_FIELD_LONG_OFFSET_ENTRY $t1 + BRB_FIELD_LONG_OFFSET_ENTRY $t2 + BRB_FIELD_LONG_OFFSET_ENTRY $s2 + BRB_FIELD_LONG_OFFSET_ENTRY $s3 + BRB_FIELD_LONG_OFFSET_ENTRY $s4 + BRB_FIELD_LONG_OFFSET_ENTRY $s5 + BRB_FIELD_LONG_OFFSET_ENTRY $s6 + BRB_FIELD_LONG_OFFSET_ENTRY $s7 + BRB_FIELD_LONG_OFFSET_ENTRY $s8 + + # Entry points for offsets/indices fitting into int16_t. + BRB_FIELD_SHORT_OFFSET_ENTRY $v0 + BRB_FIELD_SHORT_OFFSET_ENTRY $v1 + BRB_FIELD_SHORT_OFFSET_ENTRY $a0 + BRB_FIELD_SHORT_OFFSET_ENTRY $a1 + BRB_FIELD_SHORT_OFFSET_ENTRY $a2 + BRB_FIELD_SHORT_OFFSET_ENTRY $a3 + BRB_FIELD_SHORT_OFFSET_ENTRY $a4 + BRB_FIELD_SHORT_OFFSET_ENTRY $a5 + BRB_FIELD_SHORT_OFFSET_ENTRY $a6 + BRB_FIELD_SHORT_OFFSET_ENTRY $a7 + BRB_FIELD_SHORT_OFFSET_ENTRY $t0 + BRB_FIELD_SHORT_OFFSET_ENTRY $t1 + BRB_FIELD_SHORT_OFFSET_ENTRY $t2 + BRB_FIELD_SHORT_OFFSET_ENTRY $s2 + BRB_FIELD_SHORT_OFFSET_ENTRY $s3 + BRB_FIELD_SHORT_OFFSET_ENTRY $s4 + BRB_FIELD_SHORT_OFFSET_ENTRY $s5 + BRB_FIELD_SHORT_OFFSET_ENTRY $s6 + BRB_FIELD_SHORT_OFFSET_ENTRY $s7 + BRB_FIELD_SHORT_OFFSET_ENTRY $s8 + + .global art_quick_read_barrier_mark_introspection_gc_roots +art_quick_read_barrier_mark_introspection_gc_roots: + # Entry points for GC roots. + BRB_GC_ROOT_ENTRY $v0 + BRB_GC_ROOT_ENTRY $v1 + BRB_GC_ROOT_ENTRY $a0 + BRB_GC_ROOT_ENTRY $a1 + BRB_GC_ROOT_ENTRY $a2 + BRB_GC_ROOT_ENTRY $a3 + BRB_GC_ROOT_ENTRY $a4 + BRB_GC_ROOT_ENTRY $a5 + BRB_GC_ROOT_ENTRY $a6 + BRB_GC_ROOT_ENTRY $a7 + BRB_GC_ROOT_ENTRY $t0 + BRB_GC_ROOT_ENTRY $t1 + BRB_GC_ROOT_ENTRY $t2 + BRB_GC_ROOT_ENTRY $s2 + BRB_GC_ROOT_ENTRY $s3 + BRB_GC_ROOT_ENTRY $s4 + BRB_GC_ROOT_ENTRY $s5 + BRB_GC_ROOT_ENTRY $s6 + BRB_GC_ROOT_ENTRY $s7 + BRB_GC_ROOT_ENTRY $s8 + .global art_quick_read_barrier_mark_introspection_end_of_entries +art_quick_read_barrier_mark_introspection_end_of_entries: + +.Lintrospection_throw_npe: + b art_quick_throw_null_pointer_exception + daddiu $ra, $ra, 4 # Skip lwu, make $ra point to lwu's stack map. + + .set push + .set noat + + // Fields and array elements. + +.Lintrospection_field_array: + // Get the field/element address using $t8 and the offset from the lwu instruction. + lh $at, 0($ra) # $ra points to lwu: $at = low 16 bits of field/element offset. + daddiu $ra, $ra, 4 + HEAP_POISON_INSTR_SIZE # Skip lwu(+dsubu+dext). + daddu $t8, $t8, $at # $t8 = field/element address. + + // Calculate the address of the exit point, store it in $t3 and load the reference into $t8. + lb $at, (-HEAP_POISON_INSTR_SIZE - 2)($ra) # $ra-HEAP_POISON_INSTR_SIZE-4 points to + # "lwu `out`, ...". + andi $at, $at, 31 # Extract `out` from lwu. + + lwu $t8, 0($t8) # $t8 = reference. + UNPOISON_HEAP_REF $t8 + + // Return if null reference. + bnez $t8, .Lintrospection_common + dlsa $t3, $at, $t3, 3 # $t3 = address of the exit point + # (BRB_FIELD_EXIT* macro is 8 bytes). + + // Early return through the exit point. +.Lintrospection_return_early: + jic $t3, 0 # Move $t8 to `out` and return. + + // Code common for GC roots, fields and array elements. + +.Lintrospection_common: + // Check lock word for mark bit, if marked return. + lw $t9, MIRROR_OBJECT_LOCK_WORD_OFFSET($t8) + sll $at, $t9, 31 - LOCK_WORD_MARK_BIT_SHIFT # Move mark bit to sign bit. + bltzc $at, .Lintrospection_return_early +#if (LOCK_WORD_STATE_SHIFT != 30) || (LOCK_WORD_STATE_FORWARDING_ADDRESS != 3) + // The below code depends on the lock word state being in the highest bits + // and the "forwarding address" state having all bits set. +#error "Unexpected lock word state shift or forwarding address state value." +#endif + // Test that both the forwarding state bits are 1. + sll $at, $t9, 1 + and $at, $at, $t9 # Sign bit = 1 IFF both bits are 1. + bgezc $at, .Lintrospection_mark + + .set pop + + // Shift left by the forwarding address shift. This clears out the state bits since they are + // in the top 2 bits of the lock word. + sll $t8, $t9, LOCK_WORD_STATE_FORWARDING_ADDRESS_SHIFT + jalr $zero, $t3 # Move $t8 to `out` and return. + dext $t8, $t8, 0, 32 # Make sure the address is zero-extended. + +.Lintrospection_mark: + // Partially set up the stack frame preserving only $ra. + daddiu $sp, $sp, -320 + .cfi_adjust_cfa_offset 320 + sd $ra, 312($sp) + .cfi_rel_offset 31, 312 + + // Set up $gp, clobbering $ra. + lapc $ra, 1f +1: + .cpsetup $ra, 304, 1b # Save old $gp in 304($sp). + + // Finalize the stack frame and call. + sd $t3, 296($sp) # Preserve the exit point address. + sd $t2, 288($sp) + .cfi_rel_offset 14, 288 + sd $t1, 280($sp) + .cfi_rel_offset 13, 280 + sd $t0, 272($sp) + .cfi_rel_offset 12, 272 + sd $a7, 264($sp) + .cfi_rel_offset 11, 264 + sd $a6, 256($sp) + .cfi_rel_offset 10, 256 + sd $a5, 248($sp) + .cfi_rel_offset 9, 248 + sd $a4, 240($sp) + .cfi_rel_offset 8, 240 + sd $a3, 232($sp) + .cfi_rel_offset 7, 232 + sd $a2, 224($sp) + .cfi_rel_offset 6, 224 + sd $a1, 216($sp) + .cfi_rel_offset 5, 216 + sd $a0, 208($sp) + .cfi_rel_offset 4, 208 + sd $v1, 200($sp) + .cfi_rel_offset 3, 200 + sd $v0, 192($sp) + .cfi_rel_offset 2, 192 + + dla $t9, artReadBarrierMark + + sdc1 $f23, 184($sp) + sdc1 $f22, 176($sp) + sdc1 $f21, 168($sp) + sdc1 $f20, 160($sp) + sdc1 $f19, 152($sp) + sdc1 $f18, 144($sp) + sdc1 $f17, 136($sp) + sdc1 $f16, 128($sp) + sdc1 $f15, 120($sp) + sdc1 $f14, 112($sp) + sdc1 $f13, 104($sp) + sdc1 $f12, 96($sp) + sdc1 $f11, 88($sp) + sdc1 $f10, 80($sp) + sdc1 $f9, 72($sp) + sdc1 $f8, 64($sp) + sdc1 $f7, 56($sp) + sdc1 $f6, 48($sp) + sdc1 $f5, 40($sp) + sdc1 $f4, 32($sp) + sdc1 $f3, 24($sp) + sdc1 $f2, 16($sp) + sdc1 $f1, 8($sp) + sdc1 $f0, 0($sp) + + jalr $t9 # $v0 <- artReadBarrierMark(reference) + move $a0, $t8 # Pass reference in $a0. + move $t8, $v0 + + ld $ra, 312($sp) + .cfi_restore 31 + .cpreturn # Restore old $gp from 304($sp). + ld $t3, 296($sp) # $t3 = address of the exit point. + ld $t2, 288($sp) + .cfi_restore 14 + ld $t1, 280($sp) + .cfi_restore 13 + ld $t0, 272($sp) + .cfi_restore 12 + ld $a7, 264($sp) + .cfi_restore 11 + ld $a6, 256($sp) + .cfi_restore 10 + ld $a5, 248($sp) + .cfi_restore 9 + ld $a4, 240($sp) + .cfi_restore 8 + ld $a3, 232($sp) + .cfi_restore 7 + ld $a2, 224($sp) + .cfi_restore 6 + ld $a1, 216($sp) + .cfi_restore 5 + ld $a0, 208($sp) + .cfi_restore 4 + ld $v1, 200($sp) + .cfi_restore 3 + ld $v0, 192($sp) + .cfi_restore 2 + + ldc1 $f23, 184($sp) + ldc1 $f22, 176($sp) + ldc1 $f21, 168($sp) + ldc1 $f20, 160($sp) + ldc1 $f19, 152($sp) + ldc1 $f18, 144($sp) + ldc1 $f17, 136($sp) + ldc1 $f16, 128($sp) + ldc1 $f15, 120($sp) + ldc1 $f14, 112($sp) + ldc1 $f13, 104($sp) + ldc1 $f12, 96($sp) + ldc1 $f11, 88($sp) + ldc1 $f10, 80($sp) + ldc1 $f9, 72($sp) + ldc1 $f8, 64($sp) + ldc1 $f7, 56($sp) + ldc1 $f6, 48($sp) + ldc1 $f5, 40($sp) + ldc1 $f4, 32($sp) + ldc1 $f3, 24($sp) + ldc1 $f2, 16($sp) + ldc1 $f1, 8($sp) + ldc1 $f0, 0($sp) + + // Return through the exit point. + jalr $zero, $t3 # Move $t8 to `out` and return. + daddiu $sp, $sp, 320 + .cfi_adjust_cfa_offset -320 + +.Lintrospection_exits: + BRB_FIELD_EXIT_BREAK + BRB_FIELD_EXIT_BREAK + BRB_FIELD_EXIT $v0 + BRB_FIELD_EXIT $v1 + BRB_FIELD_EXIT $a0 + BRB_FIELD_EXIT $a1 + BRB_FIELD_EXIT $a2 + BRB_FIELD_EXIT $a3 + BRB_FIELD_EXIT $a4 + BRB_FIELD_EXIT $a5 + BRB_FIELD_EXIT $a6 + BRB_FIELD_EXIT $a7 + BRB_FIELD_EXIT $t0 + BRB_FIELD_EXIT $t1 + BRB_FIELD_EXIT $t2 + BRB_FIELD_EXIT_BREAK + BRB_FIELD_EXIT_BREAK + BRB_FIELD_EXIT_BREAK + BRB_FIELD_EXIT $s2 + BRB_FIELD_EXIT $s3 + BRB_FIELD_EXIT $s4 + BRB_FIELD_EXIT $s5 + BRB_FIELD_EXIT $s6 + BRB_FIELD_EXIT $s7 + BRB_FIELD_EXIT_BREAK + BRB_FIELD_EXIT_BREAK + BRB_FIELD_EXIT_BREAK + BRB_FIELD_EXIT_BREAK + BRB_FIELD_EXIT_BREAK + BRB_FIELD_EXIT_BREAK + BRB_FIELD_EXIT $s8 + BRB_FIELD_EXIT_BREAK +END art_quick_read_barrier_mark_introspection + .extern artInvokePolymorphic ENTRY art_quick_invoke_polymorphic SETUP_SAVE_REFS_AND_ARGS_FRAME diff --git a/runtime/arch/x86/fault_handler_x86.cc b/runtime/arch/x86/fault_handler_x86.cc index 798c500f18..349ce3bbe1 100644 --- a/runtime/arch/x86/fault_handler_x86.cc +++ b/runtime/arch/x86/fault_handler_x86.cc @@ -14,7 +14,6 @@ * limitations under the License. */ - #include "fault_handler.h" #include <sys/ucontext.h> diff --git a/runtime/arch/x86/instruction_set_features_x86.cc b/runtime/arch/x86/instruction_set_features_x86.cc index cc0bdf2a29..ea5a90d8ee 100644 --- a/runtime/arch/x86/instruction_set_features_x86.cc +++ b/runtime/arch/x86/instruction_set_features_x86.cc @@ -230,6 +230,19 @@ bool X86InstructionSetFeatures::Equals(const InstructionSetFeatures* other) cons (has_POPCNT_ == other_as_x86->has_POPCNT_); } +bool X86InstructionSetFeatures::HasAtLeast(const InstructionSetFeatures* other) const { + if (GetInstructionSet() != other->GetInstructionSet()) { + return false; + } + const X86InstructionSetFeatures* other_as_x86 = other->AsX86InstructionSetFeatures(); + return (has_SSSE3_ || !other_as_x86->has_SSSE3_) && + (has_SSE4_1_ || !other_as_x86->has_SSE4_1_) && + (has_SSE4_2_ || !other_as_x86->has_SSE4_2_) && + (has_AVX_ || !other_as_x86->has_AVX_) && + (has_AVX2_ || !other_as_x86->has_AVX2_) && + (has_POPCNT_ || !other_as_x86->has_POPCNT_); +} + uint32_t X86InstructionSetFeatures::AsBitmap() const { return (has_SSSE3_ ? kSsse3Bitfield : 0) | (has_SSE4_1_ ? kSse4_1Bitfield : 0) | diff --git a/runtime/arch/x86/instruction_set_features_x86.h b/runtime/arch/x86/instruction_set_features_x86.h index eb8a710e37..56cb07ea50 100644 --- a/runtime/arch/x86/instruction_set_features_x86.h +++ b/runtime/arch/x86/instruction_set_features_x86.h @@ -29,12 +29,11 @@ class X86InstructionSetFeatures : public InstructionSetFeatures { public: // Process a CPU variant string like "atom" or "nehalem" and create InstructionSetFeatures. static X86FeaturesUniquePtr FromVariant(const std::string& variant, - std::string* error_msg, - bool x86_64 = false); + std::string* error_msg, + bool x86_64 = false); // Parse a bitmap and create an InstructionSetFeatures. - static X86FeaturesUniquePtr FromBitmap(uint32_t bitmap, - bool x86_64 = false); + static X86FeaturesUniquePtr FromBitmap(uint32_t bitmap, bool x86_64 = false); // Turn C pre-processor #defines into the equivalent instruction set features. static X86FeaturesUniquePtr FromCppDefines(bool x86_64 = false); @@ -52,6 +51,8 @@ class X86InstructionSetFeatures : public InstructionSetFeatures { bool Equals(const InstructionSetFeatures* other) const OVERRIDE; + bool HasAtLeast(const InstructionSetFeatures* other) const OVERRIDE; + virtual InstructionSet GetInstructionSet() const OVERRIDE { return kX86; } diff --git a/runtime/arch/x86/quick_entrypoints_x86.S b/runtime/arch/x86/quick_entrypoints_x86.S index 031b36bd8b..48d2de9567 100644 --- a/runtime/arch/x86/quick_entrypoints_x86.S +++ b/runtime/arch/x86/quick_entrypoints_x86.S @@ -1780,35 +1780,90 @@ END_FUNCTION art_quick_proxy_invoke_handler */ DEFINE_FUNCTION art_quick_imt_conflict_trampoline PUSH EDI - movl 8(%esp), %edi // Load referrer - movl ART_METHOD_DEX_CACHE_METHODS_OFFSET_32(%edi), %edi // Load dex cache methods array + PUSH ESI + PUSH EDX + movl 16(%esp), %edi // Load referrer. + movl ART_METHOD_DEX_CACHE_METHODS_OFFSET_32(%edi), %edi // Load dex cache methods array. pushl ART_METHOD_JNI_OFFSET_32(%eax) // Push ImtConflictTable. CFI_ADJUST_CFA_OFFSET(4) - movd %xmm7, %eax // get target method index stored in xmm7 - movl 0(%edi, %eax, __SIZEOF_POINTER__), %edi // Load interface method - popl %eax // Pop ImtConflictTable. + movd %xmm7, %eax // Get target method index stored in xmm7. + movl %eax, %esi // Remember method index in ESI. + andl LITERAL(METHOD_DEX_CACHE_SIZE_MINUS_ONE), %eax // Calculate DexCache method slot index. + leal 0(%edi, %eax, 2 * __SIZEOF_POINTER__), %edi // Load DexCache method slot address. + mov %ecx, %edx // Make EDX:EAX == ECX:EBX so that LOCK CMPXCHG8B makes no changes. + mov %ebx, %eax // (The actual value does not matter.) + lock cmpxchg8b (%edi) // Relaxed atomic load EDX:EAX from the dex cache slot. + popl %edi // Pop ImtConflictTable. CFI_ADJUST_CFA_OFFSET(-4) + cmp %edx, %esi // Compare method index to see if we had a DexCache method hit. + jne .Limt_conflict_trampoline_dex_cache_miss .Limt_table_iterate: - cmpl %edi, 0(%eax) + cmpl %eax, 0(%edi) jne .Limt_table_next_entry // We successfully hit an entry in the table. Load the target method // and jump to it. + movl __SIZEOF_POINTER__(%edi), %eax + CFI_REMEMBER_STATE + POP EDX + POP ESI POP EDI - movl __SIZEOF_POINTER__(%eax), %eax jmp *ART_METHOD_QUICK_CODE_OFFSET_32(%eax) + CFI_RESTORE_STATE .Limt_table_next_entry: // If the entry is null, the interface method is not in the ImtConflictTable. - cmpl LITERAL(0), 0(%eax) + cmpl LITERAL(0), 0(%edi) jz .Lconflict_trampoline // Iterate over the entries of the ImtConflictTable. - addl LITERAL(2 * __SIZEOF_POINTER__), %eax + addl LITERAL(2 * __SIZEOF_POINTER__), %edi jmp .Limt_table_iterate .Lconflict_trampoline: // Call the runtime stub to populate the ImtConflictTable and jump to the // resolved method. - movl %edi, %eax // Load interface method + CFI_REMEMBER_STATE + POP EDX + POP ESI POP EDI INVOKE_TRAMPOLINE_BODY artInvokeInterfaceTrampoline + CFI_RESTORE_STATE +.Limt_conflict_trampoline_dex_cache_miss: + // We're not creating a proper runtime method frame here, + // artLookupResolvedMethod() is not allowed to walk the stack. + + // Save core register args; EDX is already saved. + PUSH ebx + PUSH ecx + + // Save FPR args. + subl MACRO_LITERAL(32), %esp + CFI_ADJUST_CFA_OFFSET(32) + movsd %xmm0, 0(%esp) + movsd %xmm1, 8(%esp) + movsd %xmm2, 16(%esp) + movsd %xmm3, 24(%esp) + + pushl 32+8+16(%esp) // Pass referrer. + CFI_ADJUST_CFA_OFFSET(4) + pushl %esi // Pass method index. + CFI_ADJUST_CFA_OFFSET(4) + call SYMBOL(artLookupResolvedMethod) // (uint32_t method_index, ArtMethod* referrer) + addl LITERAL(8), %esp // Pop arguments. + CFI_ADJUST_CFA_OFFSET(-8) + + // Restore FPR args. + movsd 0(%esp), %xmm0 + movsd 8(%esp), %xmm1 + movsd 16(%esp), %xmm2 + movsd 24(%esp), %xmm3 + addl MACRO_LITERAL(32), %esp + CFI_ADJUST_CFA_OFFSET(-32) + + // Restore core register args. + POP ecx + POP ebx + + cmp LITERAL(0), %eax // If the method wasn't resolved, + je .Lconflict_trampoline // skip the lookup and go to artInvokeInterfaceTrampoline(). + jmp .Limt_table_iterate END_FUNCTION art_quick_imt_conflict_trampoline DEFINE_FUNCTION art_quick_resolution_trampoline diff --git a/runtime/arch/x86_64/entrypoints_init_x86_64.cc b/runtime/arch/x86_64/entrypoints_init_x86_64.cc index 1e56e8a087..5f7380f99d 100644 --- a/runtime/arch/x86_64/entrypoints_init_x86_64.cc +++ b/runtime/arch/x86_64/entrypoints_init_x86_64.cc @@ -17,13 +17,13 @@ #include <math.h> #include "entrypoints/jni/jni_entrypoints.h" +#include "entrypoints/math_entrypoints.h" #include "entrypoints/quick/quick_alloc_entrypoints.h" #include "entrypoints/quick/quick_default_externs.h" #if !defined(__APPLE__) #include "entrypoints/quick/quick_default_init_entrypoints.h" #endif #include "entrypoints/quick/quick_entrypoints.h" -#include "entrypoints/math_entrypoints.h" #include "entrypoints/runtime_asm_entrypoints.h" #include "interpreter/interpreter.h" diff --git a/runtime/arch/x86_64/quick_entrypoints_x86_64.S b/runtime/arch/x86_64/quick_entrypoints_x86_64.S index ad06873197..0a9199e7e9 100644 --- a/runtime/arch/x86_64/quick_entrypoints_x86_64.S +++ b/runtime/arch/x86_64/quick_entrypoints_x86_64.S @@ -1641,17 +1641,29 @@ DEFINE_FUNCTION art_quick_imt_conflict_trampoline int3 int3 #else - movq __SIZEOF_POINTER__(%rsp), %r10 // Load referrer - movq ART_METHOD_DEX_CACHE_METHODS_OFFSET_64(%r10), %r10 // Load dex cache methods array - movq 0(%r10, %rax, __SIZEOF_POINTER__), %r10 // Load interface method + movq __SIZEOF_POINTER__(%rsp), %r10 // Load referrer. + movq ART_METHOD_DEX_CACHE_METHODS_OFFSET_64(%r10), %r10 // Load dex cache methods array. + mov %eax, %r11d // Remember method index in R11. + andl LITERAL(METHOD_DEX_CACHE_SIZE_MINUS_ONE), %eax // Calculate DexCache method slot index. + shll LITERAL(1), %eax // Multiply by 2 as entries have size 2 * __SIZEOF_POINTER__. + leaq 0(%r10, %rax, __SIZEOF_POINTER__), %r10 // Load DexCache method slot address. + PUSH rdx // Preserve RDX as we need to clobber it by LOCK CMPXCHG16B. + mov %rcx, %rdx // Make RDX:RAX == RCX:RBX so that LOCK CMPXCHG16B makes no changes. + mov %rbx, %rax // (The actual value does not matter.) + lock cmpxchg16b (%r10) // Relaxed atomic load RDX:RAX from the dex cache slot. movq ART_METHOD_JNI_OFFSET_64(%rdi), %rdi // Load ImtConflictTable + cmp %rdx, %r11 // Compare method index to see if we had a DexCache method hit. + jne .Limt_conflict_trampoline_dex_cache_miss .Limt_table_iterate: - cmpq %r10, 0(%rdi) + cmpq %rax, 0(%rdi) jne .Limt_table_next_entry // We successfully hit an entry in the table. Load the target method // and jump to it. movq __SIZEOF_POINTER__(%rdi), %rdi + CFI_REMEMBER_STATE + POP rdx jmp *ART_METHOD_QUICK_CODE_OFFSET_64(%rdi) + CFI_RESTORE_STATE .Limt_table_next_entry: // If the entry is null, the interface method is not in the ImtConflictTable. cmpq LITERAL(0), 0(%rdi) @@ -1662,8 +1674,66 @@ DEFINE_FUNCTION art_quick_imt_conflict_trampoline .Lconflict_trampoline: // Call the runtime stub to populate the ImtConflictTable and jump to the // resolved method. - movq %r10, %rdi // Load interface method + CFI_REMEMBER_STATE + POP rdx + movq %rax, %rdi // Load interface method INVOKE_TRAMPOLINE_BODY artInvokeInterfaceTrampoline + CFI_RESTORE_STATE +.Limt_conflict_trampoline_dex_cache_miss: + // We're not creating a proper runtime method frame here, + // artLookupResolvedMethod() is not allowed to walk the stack. + + // Save GPR args and ImtConflictTable; RDX is already saved. + PUSH r9 // Quick arg 5. + PUSH r8 // Quick arg 4. + PUSH rsi // Quick arg 1. + PUSH rcx // Quick arg 3. + PUSH rdi // ImtConflictTable + // Save FPR args and callee-saves, align stack to 16B. + subq MACRO_LITERAL(12 * 8 + 8), %rsp + CFI_ADJUST_CFA_OFFSET(12 * 8 + 8) + movq %xmm0, 0(%rsp) + movq %xmm1, 8(%rsp) + movq %xmm2, 16(%rsp) + movq %xmm3, 24(%rsp) + movq %xmm4, 32(%rsp) + movq %xmm5, 40(%rsp) + movq %xmm6, 48(%rsp) + movq %xmm7, 56(%rsp) + movq %xmm12, 64(%rsp) // XMM12-15 are callee-save in ART compiled code ABI + movq %xmm13, 72(%rsp) // but caller-save in native ABI. + movq %xmm14, 80(%rsp) + movq %xmm15, 88(%rsp) + + movq %r11, %rdi // Pass method index. + movq 12 * 8 + 8 + 6 * 8 + 8(%rsp), %rsi // Pass referrer. + call SYMBOL(artLookupResolvedMethod) // (uint32_t method_index, ArtMethod* referrer) + + // Restore FPRs. + movq 0(%rsp), %xmm0 + movq 8(%rsp), %xmm1 + movq 16(%rsp), %xmm2 + movq 24(%rsp), %xmm3 + movq 32(%rsp), %xmm4 + movq 40(%rsp), %xmm5 + movq 48(%rsp), %xmm6 + movq 56(%rsp), %xmm7 + movq 64(%rsp), %xmm12 + movq 72(%rsp), %xmm13 + movq 80(%rsp), %xmm14 + movq 88(%rsp), %xmm15 + addq MACRO_LITERAL(12 * 8 + 8), %rsp + CFI_ADJUST_CFA_OFFSET(-(12 * 8 + 8)) + // Restore ImtConflictTable and GPR args. + POP rdi + POP rcx + POP rsi + POP r8 + POP r9 + + cmp LITERAL(0), %rax // If the method wasn't resolved, + je .Lconflict_trampoline // skip the lookup and go to artInvokeInterfaceTrampoline(). + jmp .Limt_table_iterate #endif // __APPLE__ END_FUNCTION art_quick_imt_conflict_trampoline diff --git a/runtime/art_field-inl.h b/runtime/art_field-inl.h index a8a58e135e..057f58c3a0 100644 --- a/runtime/art_field-inl.h +++ b/runtime/art_field-inl.h @@ -22,14 +22,14 @@ #include "base/logging.h" #include "class_linker.h" #include "dex_file-inl.h" -#include "gc_root-inl.h" #include "gc/accounting/card_table-inl.h" +#include "gc_root-inl.h" #include "jvalue.h" #include "mirror/dex_cache-inl.h" #include "mirror/object-inl.h" #include "primitive.h" -#include "thread-current-inl.h" #include "scoped_thread_state_change-inl.h" +#include "thread-current-inl.h" #include "well_known_classes.h" namespace art { diff --git a/runtime/art_method-inl.h b/runtime/art_method-inl.h index 40d7e5c3f3..9a9f125718 100644 --- a/runtime/art_method-inl.h +++ b/runtime/art_method-inl.h @@ -24,9 +24,9 @@ #include "base/logging.h" #include "class_linker-inl.h" #include "common_throws.h" +#include "dex_file-inl.h" #include "dex_file.h" #include "dex_file_annotations.h" -#include "dex_file-inl.h" #include "gc_root-inl.h" #include "invoke_type.h" #include "jit/profiling_info.h" @@ -102,20 +102,21 @@ inline uint32_t ArtMethod::GetDexMethodIndex() { return GetDexMethodIndexUnchecked(); } -inline ArtMethod** ArtMethod::GetDexCacheResolvedMethods(PointerSize pointer_size) { - return GetNativePointer<ArtMethod**>(DexCacheResolvedMethodsOffset(pointer_size), - pointer_size); +inline mirror::MethodDexCacheType* ArtMethod::GetDexCacheResolvedMethods(PointerSize pointer_size) { + return GetNativePointer<mirror::MethodDexCacheType*>(DexCacheResolvedMethodsOffset(pointer_size), + pointer_size); } inline ArtMethod* ArtMethod::GetDexCacheResolvedMethod(uint16_t method_index, PointerSize pointer_size) { // NOTE: Unchecked, i.e. not throwing AIOOB. We don't even know the length here // without accessing the DexCache and we don't want to do that in release build. - DCHECK_LT(method_index, - GetInterfaceMethodIfProxy(pointer_size)->GetDexCache()->NumResolvedMethods()); - ArtMethod* method = mirror::DexCache::GetElementPtrSize(GetDexCacheResolvedMethods(pointer_size), - method_index, - pointer_size); + DCHECK_LT(method_index, GetInterfaceMethodIfProxy(pointer_size)->GetDexFile()->NumMethodIds()); + uint32_t slot_idx = method_index % mirror::DexCache::kDexCacheMethodCacheSize; + DCHECK_LT(slot_idx, GetInterfaceMethodIfProxy(pointer_size)->GetDexCache()->NumResolvedMethods()); + mirror::MethodDexCachePair pair = mirror::DexCache::GetNativePairPtrSize( + GetDexCacheResolvedMethods(pointer_size), slot_idx, pointer_size); + ArtMethod* method = pair.GetObjectForIndex(method_index); if (LIKELY(method != nullptr)) { auto* declaring_class = method->GetDeclaringClass(); if (LIKELY(declaring_class == nullptr || !declaring_class->IsErroneous())) { @@ -130,29 +131,29 @@ inline void ArtMethod::SetDexCacheResolvedMethod(uint16_t method_index, PointerSize pointer_size) { // NOTE: Unchecked, i.e. not throwing AIOOB. We don't even know the length here // without accessing the DexCache and we don't want to do that in release build. - DCHECK_LT(method_index, - GetInterfaceMethodIfProxy(pointer_size)->GetDexCache()->NumResolvedMethods()); + DCHECK_LT(method_index, GetInterfaceMethodIfProxy(pointer_size)->GetDexFile()->NumMethodIds()); DCHECK(new_method == nullptr || new_method->GetDeclaringClass() != nullptr); - mirror::DexCache::SetElementPtrSize(GetDexCacheResolvedMethods(pointer_size), - method_index, - new_method, - pointer_size); + uint32_t slot_idx = method_index % mirror::DexCache::kDexCacheMethodCacheSize; + DCHECK_LT(slot_idx, GetInterfaceMethodIfProxy(pointer_size)->GetDexCache()->NumResolvedMethods()); + mirror::MethodDexCachePair pair(new_method, method_index); + mirror::DexCache::SetNativePairPtrSize( + GetDexCacheResolvedMethods(pointer_size), slot_idx, pair, pointer_size); } inline bool ArtMethod::HasDexCacheResolvedMethods(PointerSize pointer_size) { return GetDexCacheResolvedMethods(pointer_size) != nullptr; } -inline bool ArtMethod::HasSameDexCacheResolvedMethods(ArtMethod** other_cache, - PointerSize pointer_size) { - return GetDexCacheResolvedMethods(pointer_size) == other_cache; -} - inline bool ArtMethod::HasSameDexCacheResolvedMethods(ArtMethod* other, PointerSize pointer_size) { return GetDexCacheResolvedMethods(pointer_size) == other->GetDexCacheResolvedMethods(pointer_size); } +inline bool ArtMethod::HasSameDexCacheResolvedMethods(mirror::MethodDexCacheType* other_cache, + PointerSize pointer_size) { + return GetDexCacheResolvedMethods(pointer_size) == other_cache; +} + inline mirror::Class* ArtMethod::GetClassFromTypeIndex(dex::TypeIndex type_idx, bool resolve) { // TODO: Refactor this function into two functions, Resolve...() and Lookup...() // so that we can properly annotate it with no-suspension possible / suspension possible. @@ -381,17 +382,21 @@ inline ArtMethod* ArtMethod::GetInterfaceMethodIfProxy(PointerSize pointer_size) if (LIKELY(!IsProxyMethod())) { return this; } - ArtMethod* interface_method = mirror::DexCache::GetElementPtrSize( - GetDexCacheResolvedMethods(pointer_size), - GetDexMethodIndex(), - pointer_size); - DCHECK(interface_method != nullptr); - DCHECK_EQ(interface_method, - Runtime::Current()->GetClassLinker()->FindMethodForProxy(GetDeclaringClass(), this)); + uint32_t method_index = GetDexMethodIndex(); + uint32_t slot_idx = method_index % mirror::DexCache::kDexCacheMethodCacheSize; + mirror::MethodDexCachePair pair = mirror::DexCache::GetNativePairPtrSize( + GetDexCacheResolvedMethods(pointer_size), slot_idx, pointer_size); + ArtMethod* interface_method = pair.GetObjectForIndex(method_index); + if (LIKELY(interface_method != nullptr)) { + DCHECK_EQ(interface_method, Runtime::Current()->GetClassLinker()->FindMethodForProxy(this)); + } else { + interface_method = Runtime::Current()->GetClassLinker()->FindMethodForProxy(this); + DCHECK(interface_method != nullptr); + } return interface_method; } -inline void ArtMethod::SetDexCacheResolvedMethods(ArtMethod** new_dex_cache_methods, +inline void ArtMethod::SetDexCacheResolvedMethods(mirror::MethodDexCacheType* new_dex_cache_methods, PointerSize pointer_size) { SetNativePointer(DexCacheResolvedMethodsOffset(pointer_size), new_dex_cache_methods, @@ -462,14 +467,8 @@ void ArtMethod::VisitRoots(RootVisitorType& visitor, PointerSize pointer_size) { if (UNLIKELY(klass->IsProxyClass())) { // For normal methods, dex cache shortcuts will be visited through the declaring class. // However, for proxies we need to keep the interface method alive, so we visit its roots. - ArtMethod* interface_method = mirror::DexCache::GetElementPtrSize( - GetDexCacheResolvedMethods(pointer_size), - GetDexMethodIndex(), - pointer_size); + ArtMethod* interface_method = GetInterfaceMethodIfProxy(pointer_size); DCHECK(interface_method != nullptr); - DCHECK_EQ(interface_method, - Runtime::Current()->GetClassLinker()->FindMethodForProxy<kReadBarrierOption>( - klass, this)); interface_method->VisitRoots(visitor, pointer_size); } } @@ -483,8 +482,8 @@ inline void ArtMethod::UpdateObjectsForImageRelocation(const Visitor& visitor, if (old_class != new_class) { SetDeclaringClass(new_class); } - ArtMethod** old_methods = GetDexCacheResolvedMethods(pointer_size); - ArtMethod** new_methods = visitor(old_methods); + mirror::MethodDexCacheType* old_methods = GetDexCacheResolvedMethods(pointer_size); + mirror::MethodDexCacheType* new_methods = visitor(old_methods); if (old_methods != new_methods) { SetDexCacheResolvedMethods(new_methods, pointer_size); } diff --git a/runtime/art_method.cc b/runtime/art_method.cc index 45dd5965fb..631f5e7250 100644 --- a/runtime/art_method.cc +++ b/runtime/art_method.cc @@ -38,8 +38,8 @@ #include "mirror/class-inl.h" #include "mirror/class_ext.h" #include "mirror/executable.h" -#include "mirror/object_array-inl.h" #include "mirror/object-inl.h" +#include "mirror/object_array-inl.h" #include "mirror/string.h" #include "oat_file-inl.h" #include "runtime_callbacks.h" @@ -67,9 +67,10 @@ ArtMethod* ArtMethod::GetCanonicalMethod(PointerSize pointer_size) { return this; } else { mirror::Class* declaring_class = GetDeclaringClass(); - ArtMethod* ret = declaring_class->FindDeclaredVirtualMethod(declaring_class->GetDexCache(), - GetDexMethodIndex(), - pointer_size); + DCHECK(declaring_class->IsInterface()); + ArtMethod* ret = declaring_class->FindInterfaceMethod(declaring_class->GetDexCache(), + GetDexMethodIndex(), + pointer_size); DCHECK(ret != nullptr); return ret; } @@ -215,11 +216,8 @@ ArtMethod* ArtMethod::FindOverriddenMethod(PointerSize pointer_size) { } else { // Method didn't override superclass method so search interfaces if (IsProxyMethod()) { - result = mirror::DexCache::GetElementPtrSize(GetDexCacheResolvedMethods(pointer_size), - GetDexMethodIndex(), - pointer_size); - CHECK_EQ(result, - Runtime::Current()->GetClassLinker()->FindMethodForProxy(GetDeclaringClass(), this)); + result = GetInterfaceMethodIfProxy(pointer_size); + DCHECK(result != nullptr); } else { mirror::IfTable* iftable = GetDeclaringClass()->GetIfTable(); for (size_t i = 0; i < iftable->Count() && result == nullptr; i++) { diff --git a/runtime/art_method.h b/runtime/art_method.h index 4b3e8efdad..511ac8359c 100644 --- a/runtime/art_method.h +++ b/runtime/art_method.h @@ -53,6 +53,10 @@ class Object; template <typename MirrorType> class ObjectArray; class PointerArray; class String; + +template <typename T> struct NativeDexCachePair; +using MethodDexCachePair = NativeDexCachePair<ArtMethod>; +using MethodDexCacheType = std::atomic<MethodDexCachePair>; } // namespace mirror class ArtMethod FINAL { @@ -352,7 +356,7 @@ class ArtMethod FINAL { dex_method_index_ = new_idx; } - ALWAYS_INLINE ArtMethod** GetDexCacheResolvedMethods(PointerSize pointer_size) + ALWAYS_INLINE mirror::MethodDexCacheType* GetDexCacheResolvedMethods(PointerSize pointer_size) REQUIRES_SHARED(Locks::mutator_lock_); ALWAYS_INLINE ArtMethod* GetDexCacheResolvedMethod(uint16_t method_index, PointerSize pointer_size) @@ -362,13 +366,14 @@ class ArtMethod FINAL { ArtMethod* new_method, PointerSize pointer_size) REQUIRES_SHARED(Locks::mutator_lock_); - ALWAYS_INLINE void SetDexCacheResolvedMethods(ArtMethod** new_dex_cache_methods, + ALWAYS_INLINE void SetDexCacheResolvedMethods(mirror::MethodDexCacheType* new_dex_cache_methods, PointerSize pointer_size) REQUIRES_SHARED(Locks::mutator_lock_); bool HasDexCacheResolvedMethods(PointerSize pointer_size) REQUIRES_SHARED(Locks::mutator_lock_); bool HasSameDexCacheResolvedMethods(ArtMethod* other, PointerSize pointer_size) REQUIRES_SHARED(Locks::mutator_lock_); - bool HasSameDexCacheResolvedMethods(ArtMethod** other_cache, PointerSize pointer_size) + bool HasSameDexCacheResolvedMethods(mirror::MethodDexCacheType* other_cache, + PointerSize pointer_size) REQUIRES_SHARED(Locks::mutator_lock_); // Get the Class* from the type index into this method's dex cache. @@ -714,7 +719,7 @@ class ArtMethod FINAL { // Must be the last fields in the method. struct PtrSizedFields { // Short cuts to declaring_class_->dex_cache_ member for fast compiled code access. - ArtMethod** dex_cache_resolved_methods_; + mirror::MethodDexCacheType* dex_cache_resolved_methods_; // Pointer to JNI function registered to this method, or a function to resolve the JNI function, // or the profiling data for non-native methods, or an ImtConflictTable, or the diff --git a/runtime/asm_support_check.h b/runtime/asm_support_check.h index cc6a578313..3163506e72 100644 --- a/runtime/asm_support_check.h +++ b/runtime/asm_support_check.h @@ -28,10 +28,10 @@ #include "mirror/class.h" #include "mirror/dex_cache.h" #include "mirror/string.h" -#include "utils/dex_cache_arrays_layout.h" #include "runtime.h" #include "stack.h" #include "thread.h" +#include "utils/dex_cache_arrays_layout.h" #ifndef ADD_TEST_EQ #define ADD_TEST_EQ(x, y) CHECK_EQ(x, y); diff --git a/runtime/atomic.h b/runtime/atomic.h index 25dd1a3a5e..09eae40a6b 100644 --- a/runtime/atomic.h +++ b/runtime/atomic.h @@ -187,7 +187,7 @@ class QuasiAtomic { template<typename T> class PACKED(sizeof(T)) Atomic : public std::atomic<T> { public: - Atomic<T>() : std::atomic<T>(0) { } + Atomic<T>() : std::atomic<T>(T()) { } explicit Atomic<T>(T value) : std::atomic<T>(value) { } diff --git a/runtime/barrier_test.cc b/runtime/barrier_test.cc index 25b6925fd8..ecdabba8a5 100644 --- a/runtime/barrier_test.cc +++ b/runtime/barrier_test.cc @@ -21,8 +21,8 @@ #include "atomic.h" #include "common_runtime_test.h" #include "mirror/object_array-inl.h" -#include "thread_pool.h" #include "thread-current-inl.h" +#include "thread_pool.h" namespace art { class CheckWaitTask : public Task { diff --git a/runtime/base/arena_allocator.cc b/runtime/base/arena_allocator.cc index 54b40f28cf..148ef86059 100644 --- a/runtime/base/arena_allocator.cc +++ b/runtime/base/arena_allocator.cc @@ -26,8 +26,8 @@ #include "logging.h" #include "mem_map.h" #include "mutex.h" -#include "thread-current-inl.h" #include "systrace.h" +#include "thread-current-inl.h" namespace art { diff --git a/runtime/base/arena_allocator.h b/runtime/base/arena_allocator.h index a484c5c105..0b1a3bac9c 100644 --- a/runtime/base/arena_allocator.h +++ b/runtime/base/arena_allocator.h @@ -17,8 +17,8 @@ #ifndef ART_RUNTIME_BASE_ARENA_ALLOCATOR_H_ #define ART_RUNTIME_BASE_ARENA_ALLOCATOR_H_ -#include <stdint.h> #include <stddef.h> +#include <stdint.h> #include "base/bit_utils.h" #include "base/dchecked_vector.h" diff --git a/runtime/base/array_slice.h b/runtime/base/array_slice.h index 0da977d97d..a7bce7dc56 100644 --- a/runtime/base/array_slice.h +++ b/runtime/base/array_slice.h @@ -17,10 +17,10 @@ #ifndef ART_RUNTIME_BASE_ARRAY_SLICE_H_ #define ART_RUNTIME_BASE_ARRAY_SLICE_H_ -#include "stride_iterator.h" #include "base/bit_utils.h" #include "base/casts.h" #include "base/iteration_range.h" +#include "stride_iterator.h" namespace art { diff --git a/runtime/base/casts.h b/runtime/base/casts.h index c5b0af665b..0cbabba6df 100644 --- a/runtime/base/casts.h +++ b/runtime/base/casts.h @@ -18,9 +18,10 @@ #define ART_RUNTIME_BASE_CASTS_H_ #include <assert.h> -#include <limits> #include <stdint.h> #include <string.h> + +#include <limits> #include <type_traits> #include "base/logging.h" diff --git a/runtime/base/hash_set.h b/runtime/base/hash_set.h index a22efcfe32..c472a9ee03 100644 --- a/runtime/base/hash_set.h +++ b/runtime/base/hash_set.h @@ -17,10 +17,11 @@ #ifndef ART_RUNTIME_BASE_HASH_SET_H_ #define ART_RUNTIME_BASE_HASH_SET_H_ +#include <stdint.h> + #include <functional> #include <iterator> #include <memory> -#include <stdint.h> #include <utility> #include "bit_utils.h" diff --git a/runtime/base/hash_set_test.cc b/runtime/base/hash_set_test.cc index 825406313a..31b28ebf5a 100644 --- a/runtime/base/hash_set_test.cc +++ b/runtime/base/hash_set_test.cc @@ -16,8 +16,8 @@ #include "hash_set.h" -#include <map> #include <forward_list> +#include <map> #include <sstream> #include <string> #include <unordered_set> diff --git a/runtime/base/histogram.h b/runtime/base/histogram.h index 0e3bc8e1b4..e0c921e408 100644 --- a/runtime/base/histogram.h +++ b/runtime/base/histogram.h @@ -16,8 +16,8 @@ #ifndef ART_RUNTIME_BASE_HISTOGRAM_H_ #define ART_RUNTIME_BASE_HISTOGRAM_H_ -#include <vector> #include <string> +#include <vector> #include "base/logging.h" diff --git a/runtime/base/length_prefixed_array.h b/runtime/base/length_prefixed_array.h index a570b819ba..2df5a99352 100644 --- a/runtime/base/length_prefixed_array.h +++ b/runtime/base/length_prefixed_array.h @@ -20,10 +20,10 @@ #include <stddef.h> // for offsetof() #include <string.h> // for memset() -#include "stride_iterator.h" #include "base/bit_utils.h" #include "base/casts.h" #include "base/iteration_range.h" +#include "stride_iterator.h" namespace art { diff --git a/runtime/base/memory_tool.h b/runtime/base/memory_tool.h index 42cbaa0389..223c1debdd 100644 --- a/runtime/base/memory_tool.h +++ b/runtime/base/memory_tool.h @@ -52,8 +52,8 @@ constexpr size_t kMemoryToolStackGuardSizeScale = 2; #else -#include <valgrind.h> #include <memcheck/memcheck.h> +#include <valgrind.h> #define MEMORY_TOOL_MAKE_NOACCESS(p, s) VALGRIND_MAKE_MEM_NOACCESS(p, s) #define MEMORY_TOOL_MAKE_UNDEFINED(p, s) VALGRIND_MAKE_MEM_UNDEFINED(p, s) #define MEMORY_TOOL_MAKE_DEFINED(p, s) VALGRIND_MAKE_MEM_DEFINED(p, s) diff --git a/runtime/base/mutex.cc b/runtime/base/mutex.cc index a472b67fcd..6392198bdd 100644 --- a/runtime/base/mutex.cc +++ b/runtime/base/mutex.cc @@ -23,8 +23,8 @@ #include "atomic.h" #include "base/logging.h" -#include "base/time_utils.h" #include "base/systrace.h" +#include "base/time_utils.h" #include "base/value_object.h" #include "mutex-inl.h" #include "scoped_thread_state_change-inl.h" @@ -233,8 +233,27 @@ void BaseMutex::CheckSafeToWait(Thread* self) { for (int i = kLockLevelCount - 1; i >= 0; --i) { if (i != level_) { BaseMutex* held_mutex = self->GetHeldMutex(static_cast<LockLevel>(i)); - // We expect waits to happen while holding the thread list suspend thread lock. - if (held_mutex != nullptr) { + // We allow the thread to wait even if the user_code_suspension_lock_ is held so long as we + // are some thread's resume_cond_ (level_ == kThreadSuspendCountLock). This just means that + // gc or some other internal process is suspending the thread while it is trying to suspend + // some other thread. So long as the current thread is not being suspended by a + // SuspendReason::kForUserCode (which needs the user_code_suspension_lock_ to clear) this is + // fine. + if (held_mutex == Locks::user_code_suspension_lock_ && level_ == kThreadSuspendCountLock) { + // No thread safety analysis is fine since we have both the user_code_suspension_lock_ + // from the line above and the ThreadSuspendCountLock since it is our level_. We use this + // lambda to avoid having to annotate the whole function as NO_THREAD_SAFETY_ANALYSIS. + auto is_suspending_for_user_code = [self]() NO_THREAD_SAFETY_ANALYSIS { + return self->GetUserCodeSuspendCount() != 0; + }; + if (is_suspending_for_user_code()) { + LOG(ERROR) << "Holding \"" << held_mutex->name_ << "\" " + << "(level " << LockLevel(i) << ") while performing wait on " + << "\"" << name_ << "\" (level " << level_ << ") " + << "with SuspendReason::kForUserCode pending suspensions"; + bad_mutexes_held = true; + } + } else if (held_mutex != nullptr) { LOG(ERROR) << "Holding \"" << held_mutex->name_ << "\" " << "(level " << LockLevel(i) << ") while performing wait on " << "\"" << name_ << "\" (level " << level_ << ")"; @@ -243,7 +262,7 @@ void BaseMutex::CheckSafeToWait(Thread* self) { } } if (gAborting == 0) { // Avoid recursive aborts. - CHECK(!bad_mutexes_held); + CHECK(!bad_mutexes_held) << this; } } } diff --git a/runtime/base/safe_copy.cc b/runtime/base/safe_copy.cc index 06249acb44..c76ea113d8 100644 --- a/runtime/base/safe_copy.cc +++ b/runtime/base/safe_copy.cc @@ -16,9 +16,9 @@ #include "safe_copy.h" -#include <unistd.h> #include <sys/uio.h> #include <sys/user.h> +#include <unistd.h> #include <algorithm> diff --git a/runtime/base/scoped_arena_containers.h b/runtime/base/scoped_arena_containers.h index 7964705993..4a6c9076af 100644 --- a/runtime/base/scoped_arena_containers.h +++ b/runtime/base/scoped_arena_containers.h @@ -26,8 +26,8 @@ #include "arena_containers.h" // For ArenaAllocatorAdapterKind. #include "base/dchecked_vector.h" -#include "scoped_arena_allocator.h" #include "safe_map.h" +#include "scoped_arena_allocator.h" namespace art { diff --git a/runtime/base/systrace.h b/runtime/base/systrace.h index 3901f96b45..06db48a576 100644 --- a/runtime/base/systrace.h +++ b/runtime/base/systrace.h @@ -19,9 +19,10 @@ #define ATRACE_TAG ATRACE_TAG_DALVIK #include <cutils/trace.h> -#include <string> #include <utils/Trace.h> +#include <string> + namespace art { class ScopedTrace { diff --git a/runtime/base/time_utils.h b/runtime/base/time_utils.h index dbb8bcd4d7..919937f5ba 100644 --- a/runtime/base/time_utils.h +++ b/runtime/base/time_utils.h @@ -18,9 +18,10 @@ #define ART_RUNTIME_BASE_TIME_UTILS_H_ #include <stdint.h> -#include <string> #include <time.h> +#include <string> + #include "base/macros.h" namespace art { diff --git a/runtime/base/timing_logger.cc b/runtime/base/timing_logger.cc index aaa24317bb..b2e5251e73 100644 --- a/runtime/base/timing_logger.cc +++ b/runtime/base/timing_logger.cc @@ -14,14 +14,13 @@ * limitations under the License. */ - #include <stdio.h> #include "timing_logger.h" +#include "base/histogram-inl.h" #include "base/logging.h" #include "base/stl_util.h" -#include "base/histogram-inl.h" #include "base/systrace.h" #include "base/time_utils.h" #include "gc/heap.h" diff --git a/runtime/base/unix_file/fd_file.cc b/runtime/base/unix_file/fd_file.cc index 00b5567012..0c73ce7ef2 100644 --- a/runtime/base/unix_file/fd_file.cc +++ b/runtime/base/unix_file/fd_file.cc @@ -17,11 +17,12 @@ #include "base/unix_file/fd_file.h" #include <errno.h> -#include <limits> #include <sys/stat.h> #include <sys/types.h> #include <unistd.h> +#include <limits> + #include "base/logging.h" // Includes needed for FdFile::Copy(). diff --git a/runtime/base/unix_file/fd_file.h b/runtime/base/unix_file/fd_file.h index eb85c4f097..e07c3fd800 100644 --- a/runtime/base/unix_file/fd_file.h +++ b/runtime/base/unix_file/fd_file.h @@ -21,8 +21,8 @@ #include <string> -#include "base/unix_file/random_access_file.h" #include "base/macros.h" +#include "base/unix_file/random_access_file.h" namespace unix_file { diff --git a/runtime/base/unix_file/random_access_file_utils.cc b/runtime/base/unix_file/random_access_file_utils.cc index df3b308bb0..aae65c1cde 100644 --- a/runtime/base/unix_file/random_access_file_utils.cc +++ b/runtime/base/unix_file/random_access_file_utils.cc @@ -14,8 +14,10 @@ * limitations under the License. */ -#include <vector> #include "base/unix_file/random_access_file_utils.h" + +#include <vector> + #include "base/unix_file/random_access_file.h" namespace unix_file { diff --git a/runtime/bytecode_utils.h b/runtime/bytecode_utils.h index fa87b1d6da..b6a3c03191 100644 --- a/runtime/bytecode_utils.h +++ b/runtime/bytecode_utils.h @@ -18,8 +18,8 @@ #define ART_RUNTIME_BYTECODE_UTILS_H_ #include "base/arena_object.h" -#include "dex_file.h" #include "dex_file-inl.h" +#include "dex_file.h" #include "dex_instruction-inl.h" namespace art { diff --git a/runtime/cha.cc b/runtime/cha.cc index e6bdb84d4c..6c011e8e39 100644 --- a/runtime/cha.cc +++ b/runtime/cha.cc @@ -19,6 +19,7 @@ #include "art_method-inl.h" #include "jit/jit.h" #include "jit/jit_code_cache.h" +#include "linear_alloc.h" #include "runtime.h" #include "scoped_thread_state_change-inl.h" #include "stack.h" @@ -174,23 +175,43 @@ class CHACheckpoint FINAL : public Closure { DISALLOW_COPY_AND_ASSIGN(CHACheckpoint); }; -void ClassHierarchyAnalysis::VerifyNonSingleImplementation(mirror::Class* verify_class, - uint16_t verify_index, - ArtMethod* excluded_method) { + +static void VerifyNonSingleImplementation(mirror::Class* verify_class, + uint16_t verify_index, + ArtMethod* excluded_method) + REQUIRES_SHARED(Locks::mutator_lock_) { + if (!kIsDebugBuild) { + return; + } + // Grab cha_lock_ to make sure all single-implementation updates are seen. + MutexLock cha_mu(Thread::Current(), *Locks::cha_lock_); + PointerSize image_pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize(); - MutexLock cha_mu(Thread::Current(), *Locks::cha_lock_); + + mirror::Class* input_verify_class = verify_class; + while (verify_class != nullptr) { if (verify_index >= verify_class->GetVTableLength()) { return; } ArtMethod* verify_method = verify_class->GetVTableEntry(verify_index, image_pointer_size); if (verify_method != excluded_method) { + auto construct_parent_chain = [](mirror::Class* failed, mirror::Class* in) + REQUIRES_SHARED(Locks::mutator_lock_) { + std::string tmp = in->PrettyClass(); + while (in != failed) { + in = in->GetSuperClass(); + tmp = tmp + "->" + in->PrettyClass(); + } + return tmp; + }; DCHECK(!verify_method->HasSingleImplementation()) << "class: " << verify_class->PrettyClass() << " verify_method: " << verify_method->PrettyMethod(true) - << " excluded_method: " << excluded_method->PrettyMethod(true); + << " (" << construct_parent_chain(verify_class, input_verify_class) << ")" + << " excluded_method: " << ArtMethod::PrettyMethod(excluded_method); if (verify_method->IsAbstract()) { DCHECK(verify_method->GetSingleImplementation(image_pointer_size) == nullptr); } @@ -239,24 +260,19 @@ void ClassHierarchyAnalysis::CheckVirtualMethodSingleImplementationInfo( // method_in_super already has multiple implementations. All methods in the // same vtable slots in its super classes should have // non-single-implementation already. - if (kIsDebugBuild) { - VerifyNonSingleImplementation(klass->GetSuperClass()->GetSuperClass(), - method_in_super->GetMethodIndex(), - nullptr /* excluded_method */); - } + VerifyNonSingleImplementation(klass->GetSuperClass()->GetSuperClass(), + method_in_super->GetMethodIndex(), + nullptr /* excluded_method */); return; } uint16_t method_index = method_in_super->GetMethodIndex(); if (method_in_super->IsAbstract()) { - if (kIsDebugBuild) { - // An abstract method should have made all methods in the same vtable - // slot above it in the class hierarchy having non-single-implementation. - mirror::Class* super_super = klass->GetSuperClass()->GetSuperClass(); - VerifyNonSingleImplementation(super_super, - method_index, - method_in_super); - } + // An abstract method should have made all methods in the same vtable + // slot above it in the class hierarchy having non-single-implementation. + VerifyNonSingleImplementation(klass->GetSuperClass()->GetSuperClass(), + method_index, + method_in_super); if (virtual_method->IsAbstract()) { // SUPER: abstract, VIRTUAL: abstract. @@ -338,11 +354,9 @@ void ClassHierarchyAnalysis::CheckVirtualMethodSingleImplementationInfo( // other methods (abstract or not) in the vtable slot to be non-single-implementation. } - if (kIsDebugBuild) { - VerifyNonSingleImplementation(super_super->GetSuperClass(), - method_index, - method_in_super_super); - } + VerifyNonSingleImplementation(super_super->GetSuperClass(), + method_index, + method_in_super_super); // No need to go any further. return; } else { @@ -568,4 +582,17 @@ void ClassHierarchyAnalysis::InvalidateSingleImplementationMethods( } } +void ClassHierarchyAnalysis::RemoveDependenciesForLinearAlloc(const LinearAlloc* linear_alloc) { + MutexLock mu(Thread::Current(), *Locks::cha_lock_); + for (auto it = cha_dependency_map_.begin(); it != cha_dependency_map_.end(); ) { + // Use unsafe to avoid locking since the allocator is going to be deleted. + if (linear_alloc->ContainsUnsafe(it->first)) { + // About to delete the ArtMethod, erase the entry from the map. + it = cha_dependency_map_.erase(it); + } else { + ++it; + } + } +} + } // namespace art diff --git a/runtime/cha.h b/runtime/cha.h index 81458db601..40999dd15b 100644 --- a/runtime/cha.h +++ b/runtime/cha.h @@ -17,17 +17,19 @@ #ifndef ART_RUNTIME_CHA_H_ #define ART_RUNTIME_CHA_H_ +#include <unordered_map> +#include <unordered_set> + #include "base/enums.h" #include "base/mutex.h" #include "handle.h" #include "mirror/class.h" #include "oat_quick_method_header.h" -#include <unordered_map> -#include <unordered_set> namespace art { class ArtMethod; +class LinearAlloc; /** * Class Hierarchy Analysis (CHA) tries to devirtualize virtual calls into @@ -111,6 +113,11 @@ class ClassHierarchyAnalysis { // Update CHA info for methods that `klass` overrides, after loading `klass`. void UpdateAfterLoadingOf(Handle<mirror::Class> klass) REQUIRES_SHARED(Locks::mutator_lock_); + // Remove all of the dependencies for a linear allocator. This is called when dex cache unloading + // occurs. + void RemoveDependenciesForLinearAlloc(const LinearAlloc* linear_alloc) + REQUIRES(!Locks::cha_lock_); + private: void InitSingleImplementationFlag(Handle<mirror::Class> klass, ArtMethod* method, @@ -148,14 +155,6 @@ class ClassHierarchyAnalysis { std::unordered_set<ArtMethod*>& invalidated_single_impl_methods) REQUIRES_SHARED(Locks::mutator_lock_); - // For all methods in vtable slot at `verify_index` of `verify_class` and its - // superclasses, single-implementation status should be false, except if the - // method is `excluded_method`. - void VerifyNonSingleImplementation(mirror::Class* verify_class, - uint16_t verify_index, - ArtMethod* excluded_method) - REQUIRES_SHARED(Locks::mutator_lock_); - // A map that maps a method to a set of compiled code that assumes that method has a // single implementation, which is used to do CHA-based devirtualization. std::unordered_map<ArtMethod*, ListOfDependentPairs> cha_dependency_map_ diff --git a/runtime/check_jni.cc b/runtime/check_jni.cc index 1c3328e484..c3dd702446 100644 --- a/runtime/check_jni.cc +++ b/runtime/check_jni.cc @@ -16,18 +16,19 @@ #include "check_jni.h" -#include <iomanip> #include <sys/mman.h> #include <zlib.h> +#include <iomanip> + #include "android-base/stringprintf.h" #include "art_field-inl.h" #include "art_method-inl.h" #include "base/logging.h" #include "base/to_str.h" -#include "class_linker.h" #include "class_linker-inl.h" +#include "class_linker.h" #include "dex_file-inl.h" #include "gc/space/space.h" #include "java_vm_ext.h" diff --git a/runtime/class_linker-inl.h b/runtime/class_linker-inl.h index 3c51f52616..0096c37b33 100644 --- a/runtime/class_linker-inl.h +++ b/runtime/class_linker-inl.h @@ -19,14 +19,14 @@ #include "art_field.h" #include "class_linker.h" -#include "gc_root-inl.h" #include "gc/heap-inl.h" -#include "obj_ptr-inl.h" +#include "gc_root-inl.h" +#include "handle_scope-inl.h" #include "mirror/class_loader.h" #include "mirror/dex_cache-inl.h" #include "mirror/iftable.h" #include "mirror/object_array-inl.h" -#include "handle_scope-inl.h" +#include "obj_ptr-inl.h" #include "scoped_thread_state_change-inl.h" #include <atomic> @@ -90,33 +90,129 @@ inline mirror::Class* ClassLinker::ResolveType(dex::TypeIndex type_idx, ArtMetho return resolved_type.Ptr(); } +template <bool kThrowOnError, typename ClassGetter> +inline bool ClassLinker::CheckInvokeClassMismatch(ObjPtr<mirror::DexCache> dex_cache, + InvokeType type, + ClassGetter class_getter) { + switch (type) { + case kStatic: + case kSuper: + break; + case kInterface: { + // We have to check whether the method id really belongs to an interface (dex static bytecode + // constraints A15, A16). Otherwise you must not invoke-interface on it. + ObjPtr<mirror::Class> klass = class_getter(); + if (UNLIKELY(!klass->IsInterface())) { + if (kThrowOnError) { + ThrowIncompatibleClassChangeError(klass, + "Found class %s, but interface was expected", + klass->PrettyDescriptor().c_str()); + } + return true; + } + break; + } + case kDirect: + if (dex_cache->GetDexFile()->GetVersion() >= DexFile::kDefaultMethodsVersion) { + break; + } + FALLTHROUGH_INTENDED; + case kVirtual: { + // Similarly, invoke-virtual (and invoke-direct without default methods) must reference + // a non-interface class (dex static bytecode constraint A24, A25). + ObjPtr<mirror::Class> klass = class_getter(); + if (UNLIKELY(klass->IsInterface())) { + if (kThrowOnError) { + ThrowIncompatibleClassChangeError(klass, + "Found interface %s, but class was expected", + klass->PrettyDescriptor().c_str()); + } + return true; + } + break; + } + default: + LOG(FATAL) << "Unreachable - invocation type: " << type; + UNREACHABLE(); + } + return false; +} + +template <bool kThrow> +inline bool ClassLinker::CheckInvokeClassMismatch(ObjPtr<mirror::DexCache> dex_cache, + InvokeType type, + uint32_t method_idx, + ObjPtr<mirror::ClassLoader> class_loader) { + return CheckInvokeClassMismatch<kThrow>( + dex_cache, + type, + [this, dex_cache, method_idx, class_loader]() REQUIRES_SHARED(Locks::mutator_lock_) { + const DexFile& dex_file = *dex_cache->GetDexFile(); + const DexFile::MethodId& method_id = dex_file.GetMethodId(method_idx); + ObjPtr<mirror::Class> klass = + LookupResolvedType(dex_file, method_id.class_idx_, dex_cache, class_loader); + DCHECK(klass != nullptr); + return klass; + }); +} + +inline ArtMethod* ClassLinker::LookupResolvedMethod(uint32_t method_idx, + ObjPtr<mirror::DexCache> dex_cache, + ObjPtr<mirror::ClassLoader> class_loader) { + PointerSize pointer_size = image_pointer_size_; + ArtMethod* resolved = dex_cache->GetResolvedMethod(method_idx, pointer_size); + if (resolved == nullptr) { + const DexFile& dex_file = *dex_cache->GetDexFile(); + const DexFile::MethodId& method_id = dex_file.GetMethodId(method_idx); + ObjPtr<mirror::Class> klass = LookupResolvedType(method_id.class_idx_, dex_cache, class_loader); + if (klass != nullptr) { + if (klass->IsInterface()) { + resolved = klass->FindInterfaceMethod(dex_cache, method_idx, pointer_size); + } else { + resolved = klass->FindClassMethod(dex_cache, method_idx, pointer_size); + } + if (resolved != nullptr) { + dex_cache->SetResolvedMethod(method_idx, resolved, pointer_size); + } + } + } + return resolved; +} + +template <InvokeType type, ClassLinker::ResolveMode kResolveMode> inline ArtMethod* ClassLinker::GetResolvedMethod(uint32_t method_idx, ArtMethod* referrer) { + DCHECK(referrer != nullptr); + // Note: The referrer can be a Proxy constructor. In that case, we need to do the + // lookup in the context of the original method from where it steals the code. + // However, we delay the GetInterfaceMethodIfProxy() until needed. + DCHECK(!referrer->IsProxyMethod() || referrer->IsConstructor()); ArtMethod* resolved_method = referrer->GetDexCacheResolvedMethod(method_idx, image_pointer_size_); - if (resolved_method == nullptr || resolved_method->IsRuntimeMethod()) { + if (resolved_method == nullptr) { return nullptr; } - return resolved_method; -} - -inline mirror::Class* ClassLinker::ResolveReferencedClassOfMethod( - uint32_t method_idx, - Handle<mirror::DexCache> dex_cache, - Handle<mirror::ClassLoader> class_loader) { - // NB: We cannot simply use `GetResolvedMethod(method_idx, ...)->GetDeclaringClass()`. This is - // because if we did so than an invoke-super could be incorrectly dispatched in cases where - // GetMethodId(method_idx).class_idx_ refers to a non-interface, non-direct-superclass - // (super*-class?) of the referrer and the direct superclass of the referrer contains a concrete - // implementation of the method. If this class's implementation of the method is copied from an - // interface (either miranda, default or conflict) we would incorrectly assume that is what we - // want to invoke on, instead of the 'concrete' implementation that the direct superclass - // contains. - const DexFile* dex_file = dex_cache->GetDexFile(); - const DexFile::MethodId& method = dex_file->GetMethodId(method_idx); - ObjPtr<mirror::Class> resolved_type = dex_cache->GetResolvedType(method.class_idx_); - if (UNLIKELY(resolved_type == nullptr)) { - resolved_type = ResolveType(*dex_file, method.class_idx_, dex_cache, class_loader); + DCHECK(!resolved_method->IsRuntimeMethod()); + if (kResolveMode == ResolveMode::kCheckICCEAndIAE) { + referrer = referrer->GetInterfaceMethodIfProxy(image_pointer_size_); + // Check if the invoke type matches the class type. + ObjPtr<mirror::DexCache> dex_cache = referrer->GetDexCache(); + ObjPtr<mirror::ClassLoader> class_loader = referrer->GetClassLoader(); + if (CheckInvokeClassMismatch</* kThrow */ false>(dex_cache, type, method_idx, class_loader)) { + return nullptr; + } + // Check access. + ObjPtr<mirror::Class> referring_class = referrer->GetDeclaringClass(); + if (!referring_class->CanAccessResolvedMethod(resolved_method->GetDeclaringClass(), + resolved_method, + dex_cache, + method_idx)) { + return nullptr; + } + // Check if the invoke type matches the method type. + if (UNLIKELY(resolved_method->CheckIncompatibleClassChange(type))) { + return nullptr; + } } - return resolved_type.Ptr(); + return resolved_method; } template <ClassLinker::ResolveMode kResolveMode> @@ -124,9 +220,16 @@ inline ArtMethod* ClassLinker::ResolveMethod(Thread* self, uint32_t method_idx, ArtMethod* referrer, InvokeType type) { - ArtMethod* resolved_method = GetResolvedMethod(method_idx, referrer); + DCHECK(referrer != nullptr); + // Note: The referrer can be a Proxy constructor. In that case, we need to do the + // lookup in the context of the original method from where it steals the code. + // However, we delay the GetInterfaceMethodIfProxy() until needed. + DCHECK(!referrer->IsProxyMethod() || referrer->IsConstructor()); Thread::PoisonObjectPointersIfDebug(); + ArtMethod* resolved_method = referrer->GetDexCacheResolvedMethod(method_idx, image_pointer_size_); + DCHECK(resolved_method == nullptr || !resolved_method->IsRuntimeMethod()); if (UNLIKELY(resolved_method == nullptr)) { + referrer = referrer->GetInterfaceMethodIfProxy(image_pointer_size_); ObjPtr<mirror::Class> declaring_class = referrer->GetDeclaringClass(); StackHandleScope<2> hs(self); Handle<mirror::DexCache> h_dex_cache(hs.NewHandle(referrer->GetDexCache())); @@ -138,6 +241,33 @@ inline ArtMethod* ClassLinker::ResolveMethod(Thread* self, h_class_loader, referrer, type); + } else if (kResolveMode == ResolveMode::kCheckICCEAndIAE) { + referrer = referrer->GetInterfaceMethodIfProxy(image_pointer_size_); + // Check if the invoke type matches the class type. + ObjPtr<mirror::DexCache> dex_cache = referrer->GetDexCache(); + ObjPtr<mirror::ClassLoader> class_loader = referrer->GetClassLoader(); + if (CheckInvokeClassMismatch</* kThrow */ true>(dex_cache, type, method_idx, class_loader)) { + DCHECK(Thread::Current()->IsExceptionPending()); + return nullptr; + } + // Check access. + ObjPtr<mirror::Class> referring_class = referrer->GetDeclaringClass(); + if (!referring_class->CheckResolvedMethodAccess(resolved_method->GetDeclaringClass(), + resolved_method, + dex_cache, + method_idx, + type)) { + DCHECK(Thread::Current()->IsExceptionPending()); + return nullptr; + } + // Check if the invoke type matches the method type. + if (UNLIKELY(resolved_method->CheckIncompatibleClassChange(type))) { + ThrowIncompatibleClassChangeError(type, + resolved_method->GetInvokeType(), + resolved_method, + referrer); + return nullptr; + } } // Note: We cannot check here to see whether we added the method to the cache. It // might be an erroneous class, which results in it being hidden from us. @@ -182,35 +312,6 @@ inline mirror::Class* ClassLinker::GetClassRoot(ClassRoot class_root) { return klass.Ptr(); } -template<ReadBarrierOption kReadBarrierOption> -ArtMethod* ClassLinker::FindMethodForProxy(ObjPtr<mirror::Class> proxy_class, - ArtMethod* proxy_method) { - DCHECK(proxy_class->IsProxyClass()); - DCHECK(proxy_method->IsProxyMethod()); - { - Thread* const self = Thread::Current(); - ReaderMutexLock mu(self, *Locks::dex_lock_); - // Locate the dex cache of the original interface/Object - for (const DexCacheData& data : dex_caches_) { - if (!self->IsJWeakCleared(data.weak_root) && - proxy_method->HasSameDexCacheResolvedMethods(data.resolved_methods, - image_pointer_size_)) { - ObjPtr<mirror::DexCache> dex_cache = - ObjPtr<mirror::DexCache>::DownCast(self->DecodeJObject(data.weak_root)); - if (dex_cache != nullptr) { - ArtMethod* resolved_method = dex_cache->GetResolvedMethod( - proxy_method->GetDexMethodIndex(), image_pointer_size_); - CHECK(resolved_method != nullptr); - return resolved_method; - } - } - } - } - LOG(FATAL) << "Didn't find dex cache for " << proxy_class->PrettyClass() << " " - << proxy_method->PrettyMethod(); - UNREACHABLE(); -} - } // namespace art #endif // ART_RUNTIME_CLASS_LINKER_INL_H_ diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index 969a67bfd1..d06ba7818d 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -16,6 +16,8 @@ #include "class_linker.h" +#include <unistd.h> + #include <algorithm> #include <deque> #include <iostream> @@ -24,7 +26,6 @@ #include <queue> #include <string> #include <tuple> -#include <unistd.h> #include <unordered_map> #include <utility> #include <vector> @@ -53,15 +54,15 @@ #include "entrypoints/entrypoint_utils.h" #include "entrypoints/runtime_asm_entrypoints.h" #include "experimental_flags.h" -#include "gc_root-inl.h" #include "gc/accounting/card_table-inl.h" #include "gc/accounting/heap_bitmap-inl.h" #include "gc/accounting/space_bitmap-inl.h" -#include "gc/heap.h" #include "gc/heap-visit-objects-inl.h" +#include "gc/heap.h" #include "gc/scoped_gc_critical_section.h" #include "gc/space/image_space.h" #include "gc/space/space-inl.h" +#include "gc_root-inl.h" #include "handle_scope-inl.h" #include "image-inl.h" #include "imt_conflict_table.h" @@ -76,37 +77,37 @@ #include "leb128.h" #include "linear_alloc.h" #include "mirror/call_site.h" -#include "mirror/class.h" #include "mirror/class-inl.h" +#include "mirror/class.h" #include "mirror/class_ext.h" #include "mirror/class_loader.h" -#include "mirror/dex_cache.h" #include "mirror/dex_cache-inl.h" +#include "mirror/dex_cache.h" #include "mirror/emulated_stack_frame.h" #include "mirror/field.h" #include "mirror/iftable-inl.h" #include "mirror/method.h" -#include "mirror/method_type.h" #include "mirror/method_handle_impl.h" #include "mirror/method_handles_lookup.h" +#include "mirror/method_type.h" #include "mirror/object-inl.h" -#include "mirror/object_array-inl.h" #include "mirror/object-refvisitor-inl.h" +#include "mirror/object_array-inl.h" #include "mirror/proxy.h" #include "mirror/reference-inl.h" #include "mirror/stack_trace_element.h" #include "mirror/string-inl.h" #include "native/dalvik_system_DexFile.h" +#include "nativehelper/ScopedLocalRef.h" #include "oat.h" -#include "oat_file.h" #include "oat_file-inl.h" +#include "oat_file.h" #include "oat_file_assistant.h" #include "oat_file_manager.h" #include "object_lock.h" #include "os.h" #include "runtime.h" #include "runtime_callbacks.h" -#include "ScopedLocalRef.h" #include "scoped_thread_state_change-inl.h" #include "thread-inl.h" #include "thread_list.h" @@ -150,8 +151,8 @@ static bool HasInitWithString(Thread* self, ClassLinker* class_linker, const cha return false; } - ArtMethod* exception_init_method = exception_class->FindDeclaredDirectMethod( - "<init>", "(Ljava/lang/String;)V", class_linker->GetImagePointerSize()); + ArtMethod* exception_init_method = exception_class->FindConstructor( + "(Ljava/lang/String;)V", class_linker->GetImagePointerSize()); return exception_init_method != nullptr; } @@ -369,7 +370,8 @@ ClassLinker::ClassLinker(InternTable* intern_table) quick_imt_conflict_trampoline_(nullptr), quick_generic_jni_trampoline_(nullptr), quick_to_interpreter_bridge_trampoline_(nullptr), - image_pointer_size_(kRuntimePointerSize) { + image_pointer_size_(kRuntimePointerSize), + cha_(new ClassHierarchyAnalysis()) { CHECK(intern_table_ != nullptr); static_assert(kFindArrayCacheSize == arraysize(find_array_class_cache_), "Array cache size wrong."); @@ -1114,7 +1116,8 @@ class FixupArtMethodArrayVisitor : public ArtMethodVisitor { virtual void Visit(ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_) { const bool is_copied = method->IsCopied(); - ArtMethod** resolved_methods = method->GetDexCacheResolvedMethods(kRuntimePointerSize); + mirror::MethodDexCacheType* resolved_methods = + method->GetDexCacheResolvedMethods(kRuntimePointerSize); if (resolved_methods != nullptr) { bool in_image_space = false; if (kIsDebugBuild || is_copied) { @@ -1136,49 +1139,6 @@ class FixupArtMethodArrayVisitor : public ArtMethodVisitor { const ImageHeader& header_; }; -class VerifyClassInTableArtMethodVisitor : public ArtMethodVisitor { - public: - explicit VerifyClassInTableArtMethodVisitor(ClassTable* table) : table_(table) {} - - virtual void Visit(ArtMethod* method) - REQUIRES_SHARED(Locks::mutator_lock_, Locks::classlinker_classes_lock_) { - ObjPtr<mirror::Class> klass = method->GetDeclaringClass(); - if (klass != nullptr && !Runtime::Current()->GetHeap()->ObjectIsInBootImageSpace(klass)) { - CHECK_EQ(table_->LookupByDescriptor(klass), klass) << mirror::Class::PrettyClass(klass); - } - } - - private: - ClassTable* const table_; -}; - -class VerifyDirectInterfacesInTableClassVisitor { - public: - explicit VerifyDirectInterfacesInTableClassVisitor(ObjPtr<mirror::ClassLoader> class_loader) - : class_loader_(class_loader), self_(Thread::Current()) { } - - bool operator()(ObjPtr<mirror::Class> klass) REQUIRES_SHARED(Locks::mutator_lock_) { - if (!klass->IsPrimitive() && klass->GetClassLoader() == class_loader_) { - classes_.push_back(klass); - } - return true; - } - - void Check() const REQUIRES_SHARED(Locks::mutator_lock_) { - for (ObjPtr<mirror::Class> klass : classes_) { - for (uint32_t i = 0, num = klass->NumDirectInterfaces(); i != num; ++i) { - CHECK(klass->GetDirectInterface(self_, klass, i) != nullptr) - << klass->PrettyDescriptor() << " iface #" << i; - } - } - } - - private: - ObjPtr<mirror::ClassLoader> class_loader_; - Thread* self_; - std::vector<ObjPtr<mirror::Class>> classes_; -}; - class VerifyDeclaringClassVisitor : public ArtMethodVisitor { public: VerifyDeclaringClassVisitor() REQUIRES_SHARED(Locks::mutator_lock_, Locks::heap_bitmap_lock_) @@ -1284,6 +1244,25 @@ static void CopyDexCachePairs(const std::atomic<mirror::DexCachePair<T>>* src, } } +template <typename T> +static void CopyNativeDexCachePairs(std::atomic<mirror::NativeDexCachePair<T>>* src, + size_t count, + std::atomic<mirror::NativeDexCachePair<T>>* dst, + PointerSize pointer_size) { + DCHECK_NE(count, 0u); + DCHECK(mirror::DexCache::GetNativePairPtrSize(src, 0, pointer_size).object != nullptr || + mirror::DexCache::GetNativePairPtrSize(src, 0, pointer_size).index != 0u); + for (size_t i = 0; i < count; ++i) { + DCHECK_EQ(mirror::DexCache::GetNativePairPtrSize(dst, i, pointer_size).index, 0u); + DCHECK(mirror::DexCache::GetNativePairPtrSize(dst, i, pointer_size).object == nullptr); + mirror::NativeDexCachePair<T> source = + mirror::DexCache::GetNativePairPtrSize(src, i, pointer_size); + if (source.index != 0u || source.object != nullptr) { + mirror::DexCache::SetNativePairPtrSize(dst, i, source, pointer_size); + } + } +} + // new_class_set is the set of classes that were read from the class table section in the image. // If there was no class table section, it is null. // Note: using a class here to avoid having to make ClassLinker internals public. @@ -1363,7 +1342,10 @@ bool AppImageClassLoadersAndDexCachesHelper::Update( if (dex_file->NumTypeIds() < num_types) { num_types = dex_file->NumTypeIds(); } - const size_t num_methods = dex_file->NumMethodIds(); + size_t num_methods = mirror::DexCache::kDexCacheMethodCacheSize; + if (dex_file->NumMethodIds() < num_methods) { + num_methods = dex_file->NumMethodIds(); + } size_t num_fields = mirror::DexCache::kDexCacheFieldCacheSize; if (dex_file->NumFieldIds() < num_fields) { num_fields = dex_file->NumFieldIds(); @@ -1396,37 +1378,18 @@ bool AppImageClassLoadersAndDexCachesHelper::Update( dex_cache->SetResolvedTypes(types); } if (num_methods != 0u) { - ArtMethod** const methods = reinterpret_cast<ArtMethod**>( - raw_arrays + layout.MethodsOffset()); - ArtMethod** const image_resolved_methods = dex_cache->GetResolvedMethods(); - for (size_t j = 0; kIsDebugBuild && j < num_methods; ++j) { - DCHECK(methods[j] == nullptr); - } - CopyNonNull(image_resolved_methods, - num_methods, - methods, - [] (const ArtMethod* method) { - return method == nullptr; - }); + mirror::MethodDexCacheType* const image_resolved_methods = + dex_cache->GetResolvedMethods(); + mirror::MethodDexCacheType* const methods = + reinterpret_cast<mirror::MethodDexCacheType*>(raw_arrays + layout.MethodsOffset()); + CopyNativeDexCachePairs(image_resolved_methods, num_methods, methods, image_pointer_size); dex_cache->SetResolvedMethods(methods); } if (num_fields != 0u) { mirror::FieldDexCacheType* const image_resolved_fields = dex_cache->GetResolvedFields(); mirror::FieldDexCacheType* const fields = reinterpret_cast<mirror::FieldDexCacheType*>(raw_arrays + layout.FieldsOffset()); - for (size_t j = 0; j < num_fields; ++j) { - DCHECK_EQ(mirror::DexCache::GetNativePairPtrSize(fields, j, image_pointer_size).index, - 0u); - DCHECK(mirror::DexCache::GetNativePairPtrSize(fields, j, image_pointer_size).object == - nullptr); - mirror::DexCache::SetNativePairPtrSize( - fields, - j, - mirror::DexCache::GetNativePairPtrSize(image_resolved_fields, - j, - image_pointer_size), - image_pointer_size); - } + CopyNativeDexCachePairs(image_resolved_fields, num_fields, fields, image_pointer_size); dex_cache->SetResolvedFields(fields); } if (num_method_types != 0u) { @@ -1663,13 +1626,13 @@ class ImageSanityChecks FINAL { heap->VisitObjects(visitor); } - static void CheckPointerArray(gc::Heap* heap, - ClassLinker* class_linker, - ArtMethod** arr, - size_t size) + static void CheckArtMethodDexCacheArray(gc::Heap* heap, + ClassLinker* class_linker, + mirror::MethodDexCacheType* arr, + size_t size) REQUIRES_SHARED(Locks::mutator_lock_) { ImageSanityChecks isc(heap, class_linker); - isc.SanityCheckArtMethodPointerArray(arr, size); + isc.SanityCheckArtMethodDexCacheArray(arr, size); } private: @@ -1724,7 +1687,7 @@ class ImageSanityChecks FINAL { } } - void SanityCheckArtMethodPointerArray(ArtMethod** arr, size_t size) + void SanityCheckArtMethodDexCacheArray(mirror::MethodDexCacheType* arr, size_t size) REQUIRES_SHARED(Locks::mutator_lock_) { CHECK_EQ(arr != nullptr, size != 0u); if (arr != nullptr) { @@ -1740,7 +1703,8 @@ class ImageSanityChecks FINAL { CHECK(contains); } for (size_t j = 0; j < size; ++j) { - ArtMethod* method = mirror::DexCache::GetElementPtrSize(arr, j, pointer_size_); + auto pair = mirror::DexCache::GetNativePairPtrSize(arr, j, pointer_size_); + ArtMethod* method = pair.object; // expected_class == null means we are a dex cache. if (method != nullptr) { SanityCheckArtMethod(method, nullptr); @@ -1757,6 +1721,63 @@ class ImageSanityChecks FINAL { std::vector<const ImageSection*> runtime_method_sections_; }; +static void VerifyAppImage(const ImageHeader& header, + const Handle<mirror::ClassLoader>& class_loader, + const Handle<mirror::ObjectArray<mirror::DexCache> >& dex_caches, + ClassTable* class_table, gc::space::ImageSpace* space) + REQUIRES_SHARED(Locks::mutator_lock_) { + { + class VerifyClassInTableArtMethodVisitor : public ArtMethodVisitor { + public: + explicit VerifyClassInTableArtMethodVisitor(ClassTable* table) : table_(table) {} + + virtual void Visit(ArtMethod* method) + REQUIRES_SHARED(Locks::mutator_lock_, Locks::classlinker_classes_lock_) { + ObjPtr<mirror::Class> klass = method->GetDeclaringClass(); + if (klass != nullptr && !Runtime::Current()->GetHeap()->ObjectIsInBootImageSpace(klass)) { + CHECK_EQ(table_->LookupByDescriptor(klass), klass) << mirror::Class::PrettyClass(klass); + } + } + + private: + ClassTable* const table_; + }; + VerifyClassInTableArtMethodVisitor visitor(class_table); + header.VisitPackedArtMethods(&visitor, space->Begin(), kRuntimePointerSize); + } + { + // Verify that all direct interfaces of classes in the class table are also resolved. + std::vector<ObjPtr<mirror::Class>> classes; + auto verify_direct_interfaces_in_table = [&](ObjPtr<mirror::Class> klass) + REQUIRES_SHARED(Locks::mutator_lock_) { + if (!klass->IsPrimitive() && klass->GetClassLoader() == class_loader.Get()) { + classes.push_back(klass); + } + return true; + }; + class_table->Visit(verify_direct_interfaces_in_table); + Thread* self = Thread::Current(); + for (ObjPtr<mirror::Class> klass : classes) { + for (uint32_t i = 0, num = klass->NumDirectInterfaces(); i != num; ++i) { + CHECK(klass->GetDirectInterface(self, klass, i) != nullptr) + << klass->PrettyDescriptor() << " iface #" << i; + } + } + } + // Check that all non-primitive classes in dex caches are also in the class table. + for (int32_t i = 0; i < dex_caches->GetLength(); i++) { + ObjPtr<mirror::DexCache> dex_cache = dex_caches->Get(i); + mirror::TypeDexCacheType* const types = dex_cache->GetResolvedTypes(); + for (int32_t j = 0, num_types = dex_cache->NumResolvedTypes(); j < num_types; j++) { + ObjPtr<mirror::Class> klass = types[j].load(std::memory_order_relaxed).object.Read(); + if (klass != nullptr && !klass->IsPrimitive()) { + CHECK(class_table->Contains(klass)) + << klass->PrettyDescriptor() << " " << dex_cache->GetDexFile()->GetLocation(); + } + } + } +} + bool ClassLinker::AddImageSpace( gc::space::ImageSpace* space, Handle<mirror::ClassLoader> class_loader, @@ -1851,10 +1872,10 @@ bool ClassLinker::AddImageSpace( } } else { if (kSanityCheckObjects) { - ImageSanityChecks::CheckPointerArray(heap, - this, - dex_cache->GetResolvedMethods(), - dex_cache->NumResolvedMethods()); + ImageSanityChecks::CheckArtMethodDexCacheArray(heap, + this, + dex_cache->GetResolvedMethods(), + dex_cache->NumResolvedMethods()); } // Register dex files, keep track of existing ones that are conflicts. AppendToBootClassPath(*dex_file.get(), dex_cache); @@ -2010,28 +2031,13 @@ bool ClassLinker::AddImageSpace( WriterMutexLock mu(self, *Locks::classlinker_classes_lock_); class_table->AddClassSet(std::move(temp_set)); } + if (kIsDebugBuild && app_image) { // This verification needs to happen after the classes have been added to the class loader. // Since it ensures classes are in the class table. - VerifyClassInTableArtMethodVisitor visitor2(class_table); - header.VisitPackedArtMethods(&visitor2, space->Begin(), kRuntimePointerSize); - // Verify that all direct interfaces of classes in the class table are also resolved. - VerifyDirectInterfacesInTableClassVisitor visitor(class_loader.Get()); - class_table->Visit(visitor); - visitor.Check(); - // Check that all non-primitive classes in dex caches are also in the class table. - for (int32_t i = 0; i < dex_caches->GetLength(); i++) { - ObjPtr<mirror::DexCache> dex_cache = dex_caches->Get(i); - mirror::TypeDexCacheType* const types = dex_cache->GetResolvedTypes(); - for (int32_t j = 0, num_types = dex_cache->NumResolvedTypes(); j < num_types; j++) { - ObjPtr<mirror::Class> klass = types[j].load(std::memory_order_relaxed).object.Read(); - if (klass != nullptr && !klass->IsPrimitive()) { - CHECK(class_table->Contains(klass)) << klass->PrettyDescriptor() - << " " << dex_cache->GetDexFile()->GetLocation(); - } - } - } + VerifyAppImage(header, class_loader, dex_caches, class_table, space); } + VLOG(class_linker) << "Adding image space took " << PrettyDuration(NanoTime() - start_time); return true; } @@ -2312,8 +2318,12 @@ void ClassLinker::DeleteClassLoader(Thread* self, const ClassLoaderData& data) { if (runtime->GetJit() != nullptr) { jit::JitCodeCache* code_cache = runtime->GetJit()->GetCodeCache(); if (code_cache != nullptr) { + // For the JIT case, RemoveMethodsIn removes the CHA dependencies. code_cache->RemoveMethodsIn(self, *data.allocator); } + } else if (cha_ != nullptr) { + // If we don't have a JIT, we need to manually remove the CHA dependencies manually. + cha_->RemoveDependenciesForLinearAlloc(data.allocator); } delete data.allocator; delete data.class_table; @@ -3483,7 +3493,8 @@ void ClassLinker::AppendToBootClassPath(const DexFile& dex_file, ObjPtr<mirror::DexCache> dex_cache) { CHECK(dex_cache != nullptr) << dex_file.GetLocation(); boot_class_path_.push_back(&dex_file); - RegisterBootClassPathDexFile(dex_file, dex_cache); + WriterMutexLock mu(Thread::Current(), *Locks::dex_lock_); + RegisterDexFileLocked(dex_file, dex_cache, /* class_loader */ nullptr); } void ClassLinker::RegisterDexFileLocked(const DexFile& dex_file, @@ -3666,12 +3677,6 @@ ObjPtr<mirror::DexCache> ClassLinker::RegisterDexFile(const DexFile& dex_file, return h_dex_cache.Get(); } -void ClassLinker::RegisterBootClassPathDexFile(const DexFile& dex_file, - ObjPtr<mirror::DexCache> dex_cache) { - WriterMutexLock mu(Thread::Current(), *Locks::dex_lock_); - RegisterDexFileLocked(dex_file, dex_cache, /* class_loader */ nullptr); -} - bool ClassLinker::IsDexFileRegistered(Thread* self, const DexFile& dex_file) { ReaderMutexLock mu(self, *Locks::dex_lock_); return DecodeDexCache(self, FindDexCacheDataLocked(dex_file)) != nullptr; @@ -3722,20 +3727,6 @@ ClassLinker::DexCacheData ClassLinker::FindDexCacheDataLocked(const DexFile& dex return DexCacheData(); } -void ClassLinker::FixupDexCaches(ArtMethod* resolution_method) { - Thread* const self = Thread::Current(); - ReaderMutexLock mu(self, *Locks::dex_lock_); - for (const DexCacheData& data : dex_caches_) { - if (!self->IsJWeakCleared(data.weak_root)) { - ObjPtr<mirror::DexCache> dex_cache = ObjPtr<mirror::DexCache>::DownCast( - self->DecodeJObject(data.weak_root)); - if (dex_cache != nullptr) { - dex_cache->Fixup(resolution_method, image_pointer_size_); - } - } - } -} - mirror::Class* ClassLinker::CreatePrimitiveClass(Thread* self, Primitive::Type type) { ObjPtr<mirror::Class> klass = AllocClass(self, mirror::Class::PrimitiveClassSize(image_pointer_size_)); @@ -4638,10 +4629,8 @@ void ClassLinker::CreateProxyConstructor(Handle<mirror::Class> klass, ArtMethod* // Find the <init>(InvocationHandler)V method. The exact method offset varies depending // on which front-end compiler was used to build the libcore DEX files. - ArtMethod* proxy_constructor = GetClassRoot(kJavaLangReflectProxy)-> - FindDeclaredDirectMethod("<init>", - "(Ljava/lang/reflect/InvocationHandler;)V", - image_pointer_size_); + ArtMethod* proxy_constructor = GetClassRoot(kJavaLangReflectProxy)->FindConstructor( + "(Ljava/lang/reflect/InvocationHandler;)V", image_pointer_size_); DCHECK(proxy_constructor != nullptr) << "Could not find <init> method in java.lang.reflect.Proxy"; @@ -4653,8 +4642,9 @@ void ClassLinker::CreateProxyConstructor(Handle<mirror::Class> klass, ArtMethod* // code_ too) DCHECK(out != nullptr); out->CopyFrom(proxy_constructor, image_pointer_size_); - // Make this constructor public and fix the class to be our Proxy version + // Make this constructor public and fix the class to be our Proxy version. // Mark kAccCompileDontBother so that we don't take JIT samples for the method. b/62349349 + // Note that the compiler calls a ResolveMethod() overload that does not handle a Proxy referrer. out->SetAccessFlags((out->GetAccessFlags() & ~kAccProtected) | kAccPublic | kAccCompileDontBother); @@ -5491,7 +5481,9 @@ bool ClassLinker::LinkClass(Thread* self, // Update CHA info based on whether we override methods. // Have to do this before setting the class as resolved which allows // instantiation of klass. - Runtime::Current()->GetClassHierarchyAnalysis()->UpdateAfterLoadingOf(klass); + if (cha_ != nullptr) { + cha_->UpdateAfterLoadingOf(klass); + } // This will notify waiters on klass that saw the not yet resolved // class in the class_table_ during EnsureResolved. @@ -5539,7 +5531,9 @@ bool ClassLinker::LinkClass(Thread* self, // Update CHA info based on whether we override methods. // Have to do this before setting the class as resolved which allows // instantiation of klass. - Runtime::Current()->GetClassHierarchyAnalysis()->UpdateAfterLoadingOf(h_new_class); + if (cha_ != nullptr) { + cha_->UpdateAfterLoadingOf(h_new_class); + } // This will notify waiters on temp class that saw the not yet resolved class in the // class_table_ during EnsureResolved. @@ -5629,12 +5623,18 @@ bool ClassLinker::LinkSuperClass(Handle<mirror::Class> klass) { return false; } // Verify - if (super->IsFinal() || super->IsInterface()) { + if (super->IsFinal()) { + ThrowVerifyError(klass.Get(), + "Superclass %s of %s is declared final", + super->PrettyDescriptor().c_str(), + klass->PrettyDescriptor().c_str()); + return false; + } + if (super->IsInterface()) { ThrowIncompatibleClassChangeError(klass.Get(), - "Superclass %s of %s is %s", + "Superclass %s of %s is an interface", super->PrettyDescriptor().c_str(), - klass->PrettyDescriptor().c_str(), - super->IsFinal() ? "declared final" : "an interface"); + klass->PrettyDescriptor().c_str()); return false; } if (!klass->CanAccess(super)) { @@ -6887,7 +6887,8 @@ class ClassLinker::LinkInterfaceMethodsHelper { // Check that there are no stale methods are in the dex cache array. auto* resolved_methods = klass_->GetDexCache()->GetResolvedMethods(); for (size_t i = 0, count = klass_->GetDexCache()->NumResolvedMethods(); i < count; ++i) { - auto* m = mirror::DexCache::GetElementPtrSize(resolved_methods, i, pointer_size); + auto pair = mirror::DexCache::GetNativePairPtrSize(resolved_methods, i, pointer_size); + ArtMethod* m = pair.object; CHECK(move_table_.find(m) == move_table_.end() || // The original versions of copied methods will still be present so allow those too. // Note that if the first check passes this might fail to GetDeclaringClass(). @@ -7363,10 +7364,8 @@ bool ClassLinker::LinkInterfaceMethods( // defaults. This means we don't need to do any trickery when creating the Miranda methods, since // they will already be null. This has the additional benefit that the declarer of a miranda // method will actually declare an abstract method. - for (size_t i = ifcount; i != 0; ) { + for (size_t i = ifcount; i != 0u; ) { --i; - - DCHECK_GE(i, 0u); DCHECK_LT(i, ifcount); size_t num_methods = iftable->GetInterface(i)->NumDeclaredVirtualMethods(); @@ -7947,201 +7946,96 @@ ArtMethod* ClassLinker::ResolveMethod(const DexFile& dex_file, ArtMethod* referrer, InvokeType type) { DCHECK(dex_cache != nullptr); + DCHECK(referrer == nullptr || !referrer->IsProxyMethod()); // Check for hit in the dex cache. - ArtMethod* resolved = dex_cache->GetResolvedMethod(method_idx, image_pointer_size_); + PointerSize pointer_size = image_pointer_size_; + ArtMethod* resolved = dex_cache->GetResolvedMethod(method_idx, pointer_size); Thread::PoisonObjectPointersIfDebug(); - if (resolved != nullptr && !resolved->IsRuntimeMethod()) { + DCHECK(resolved == nullptr || !resolved->IsRuntimeMethod()); + bool valid_dex_cache_method = resolved != nullptr; + if (kResolveMode == ResolveMode::kNoChecks && valid_dex_cache_method) { + // We have a valid method from the DexCache and no checks to perform. DCHECK(resolved->GetDeclaringClassUnchecked() != nullptr) << resolved->GetDexMethodIndex(); - if (kResolveMode == ClassLinker::kForceICCECheck) { - if (resolved->CheckIncompatibleClassChange(type)) { - ThrowIncompatibleClassChangeError(type, resolved->GetInvokeType(), resolved, referrer); - return nullptr; - } - } return resolved; } - // Fail, get the declaring class. const DexFile::MethodId& method_id = dex_file.GetMethodId(method_idx); - ObjPtr<mirror::Class> klass = ResolveType(dex_file, method_id.class_idx_, dex_cache, class_loader); - if (klass == nullptr) { + ObjPtr<mirror::Class> klass = nullptr; + if (valid_dex_cache_method) { + // We have a valid method from the DexCache but we need to perform ICCE and IAE checks. + DCHECK(resolved->GetDeclaringClassUnchecked() != nullptr) << resolved->GetDexMethodIndex(); + klass = LookupResolvedType(dex_file, method_id.class_idx_, dex_cache.Get(), class_loader.Get()); + DCHECK(klass != nullptr); + } else { + // The method was not in the DexCache, resolve the declaring class. + klass = ResolveType(dex_file, method_id.class_idx_, dex_cache, class_loader); + if (klass == nullptr) { + DCHECK(Thread::Current()->IsExceptionPending()); + return nullptr; + } + } + + // Check if the invoke type matches the class type. + if (kResolveMode == ResolveMode::kCheckICCEAndIAE && + CheckInvokeClassMismatch</* kThrow */ true>( + dex_cache.Get(), type, [klass]() { return klass; })) { DCHECK(Thread::Current()->IsExceptionPending()); return nullptr; } - // Scan using method_idx, this saves string compares but will only hit for matching dex - // caches/files. - switch (type) { - case kDirect: // Fall-through. - case kStatic: - resolved = klass->FindDirectMethod(dex_cache.Get(), method_idx, image_pointer_size_); - DCHECK(resolved == nullptr || resolved->GetDeclaringClassUnchecked() != nullptr); - break; - case kInterface: - // We have to check whether the method id really belongs to an interface (dex static bytecode - // constraint A15). Otherwise you must not invoke-interface on it. - // - // This is not symmetric to A12-A14 (direct, static, virtual), as using FindInterfaceMethod - // assumes that the given type is an interface, and will check the interface table if the - // method isn't declared in the class. So it may find an interface method (usually by name - // in the handling below, but we do the constraint check early). In that case, - // CheckIncompatibleClassChange will succeed (as it is called on an interface method) - // unexpectedly. - // Example: - // interface I { - // foo() - // } - // class A implements I { - // ... - // } - // class B extends A { - // ... - // } - // invoke-interface B.foo - // -> FindInterfaceMethod finds I.foo (interface method), not A.foo (miranda method) - if (UNLIKELY(!klass->IsInterface())) { - ThrowIncompatibleClassChangeError(klass, - "Found class %s, but interface was expected", - klass->PrettyDescriptor().c_str()); - return nullptr; - } else { - resolved = klass->FindInterfaceMethod(dex_cache.Get(), method_idx, image_pointer_size_); - DCHECK(resolved == nullptr || resolved->GetDeclaringClass()->IsInterface()); - } - break; - case kSuper: - if (klass->IsInterface()) { - resolved = klass->FindInterfaceMethod(dex_cache.Get(), method_idx, image_pointer_size_); - } else { - resolved = klass->FindVirtualMethod(dex_cache.Get(), method_idx, image_pointer_size_); - } - break; - case kVirtual: - resolved = klass->FindVirtualMethod(dex_cache.Get(), method_idx, image_pointer_size_); - break; - default: - LOG(FATAL) << "Unreachable - invocation type: " << type; - UNREACHABLE(); + + if (!valid_dex_cache_method) { + // Search for the method using dex_cache and method_idx. The Class::Find*Method() + // functions can optimize the search if the dex_cache is the same as the DexCache + // of the class, with fall-back to name and signature search otherwise. + if (klass->IsInterface()) { + resolved = klass->FindInterfaceMethod(dex_cache.Get(), method_idx, pointer_size); + } else { + resolved = klass->FindClassMethod(dex_cache.Get(), method_idx, pointer_size); + } + DCHECK(resolved == nullptr || resolved->GetDeclaringClassUnchecked() != nullptr); + if (resolved != nullptr) { + // Be a good citizen and update the dex cache to speed subsequent calls. + dex_cache->SetResolvedMethod(method_idx, resolved, pointer_size); + } } - if (resolved == nullptr) { - // Search by name, which works across dex files. - const char* name = dex_file.StringDataByIdx(method_id.name_idx_); - const Signature signature = dex_file.GetMethodSignature(method_id); - switch (type) { - case kDirect: // Fall-through. - case kStatic: - resolved = klass->FindDirectMethod(name, signature, image_pointer_size_); - DCHECK(resolved == nullptr || resolved->GetDeclaringClassUnchecked() != nullptr); - break; - case kInterface: - resolved = klass->FindInterfaceMethod(name, signature, image_pointer_size_); - DCHECK(resolved == nullptr || resolved->GetDeclaringClass()->IsInterface()); - break; - case kSuper: - if (klass->IsInterface()) { - resolved = klass->FindInterfaceMethod(name, signature, image_pointer_size_); - } else { - resolved = klass->FindVirtualMethod(name, signature, image_pointer_size_); - } - break; - case kVirtual: - resolved = klass->FindVirtualMethod(name, signature, image_pointer_size_); - break; + + // Note: We can check for IllegalAccessError only if we have a referrer. + if (kResolveMode == ResolveMode::kCheckICCEAndIAE && resolved != nullptr && referrer != nullptr) { + ObjPtr<mirror::Class> methods_class = resolved->GetDeclaringClass(); + ObjPtr<mirror::Class> referring_class = referrer->GetDeclaringClass(); + if (!referring_class->CheckResolvedMethodAccess(methods_class, + resolved, + dex_cache.Get(), + method_idx, + type)) { + DCHECK(Thread::Current()->IsExceptionPending()); + return nullptr; } } + // If we found a method, check for incompatible class changes. - if (LIKELY(resolved != nullptr && !resolved->CheckIncompatibleClassChange(type))) { - // Be a good citizen and update the dex cache to speed subsequent calls. - dex_cache->SetResolvedMethod(method_idx, resolved, image_pointer_size_); + if (LIKELY(resolved != nullptr) && + LIKELY(kResolveMode == ResolveMode::kNoChecks || + !resolved->CheckIncompatibleClassChange(type))) { return resolved; } else { - // If we had a method, it's an incompatible-class-change error. + // If we had a method, or if we can find one with another lookup type, + // it's an incompatible-class-change error. + if (resolved == nullptr) { + if (klass->IsInterface()) { + resolved = klass->FindClassMethod(dex_cache.Get(), method_idx, pointer_size); + } else { + // If there was an interface method with the same signature, + // we would have found it also in the "copied" methods. + DCHECK(klass->FindInterfaceMethod(dex_cache.Get(), method_idx, pointer_size) == nullptr); + } + } if (resolved != nullptr) { ThrowIncompatibleClassChangeError(type, resolved->GetInvokeType(), resolved, referrer); } else { - // 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. + // We failed to find the method (using all lookup types), so throw a NoSuchMethodError. const char* name = dex_file.StringDataByIdx(method_id.name_idx_); const Signature signature = dex_file.GetMethodSignature(method_id); - switch (type) { - case kDirect: - case kStatic: - resolved = klass->FindVirtualMethod(name, signature, image_pointer_size_); - // Note: kDirect and kStatic are also mutually exclusive, but in that case we would - // have had a resolved method before, which triggers the "true" branch above. - break; - case kInterface: - case kVirtual: - case kSuper: - resolved = klass->FindDirectMethod(name, signature, image_pointer_size_); - break; - } - - // If we found something, check that it can be accessed by the referrer. - bool exception_generated = false; - if (resolved != nullptr && referrer != nullptr) { - ObjPtr<mirror::Class> methods_class = resolved->GetDeclaringClass(); - ObjPtr<mirror::Class> referring_class = referrer->GetDeclaringClass(); - if (!referring_class->CanAccess(methods_class)) { - ThrowIllegalAccessErrorClassForMethodDispatch(referring_class, - methods_class, - resolved, - type); - exception_generated = true; - } else if (!referring_class->CanAccessMember(methods_class, resolved->GetAccessFlags())) { - ThrowIllegalAccessErrorMethod(referring_class, resolved); - exception_generated = true; - } - } - if (!exception_generated) { - // Otherwise, throw an IncompatibleClassChangeError if we found something, and check - // interface methods and throw if we find the method there. If we find nothing, throw a - // NoSuchMethodError. - switch (type) { - case kDirect: - case kStatic: - if (resolved != nullptr) { - ThrowIncompatibleClassChangeError(type, kVirtual, resolved, referrer); - } else { - resolved = klass->FindInterfaceMethod(name, signature, image_pointer_size_); - if (resolved != nullptr) { - ThrowIncompatibleClassChangeError(type, kInterface, resolved, referrer); - } else { - ThrowNoSuchMethodError(type, klass, name, signature); - } - } - break; - case kInterface: - if (resolved != nullptr) { - ThrowIncompatibleClassChangeError(type, kDirect, resolved, referrer); - } else { - resolved = klass->FindVirtualMethod(name, signature, image_pointer_size_); - if (resolved != nullptr) { - ThrowIncompatibleClassChangeError(type, kVirtual, resolved, referrer); - } else { - ThrowNoSuchMethodError(type, klass, name, signature); - } - } - break; - case kSuper: - if (resolved != nullptr) { - ThrowIncompatibleClassChangeError(type, kDirect, resolved, referrer); - } else { - ThrowNoSuchMethodError(type, klass, name, signature); - } - break; - case kVirtual: - if (resolved != nullptr) { - ThrowIncompatibleClassChangeError(type, kDirect, resolved, referrer); - } else { - resolved = klass->FindInterfaceMethod(name, signature, image_pointer_size_); - if (resolved != nullptr) { - ThrowIncompatibleClassChangeError(type, kInterface, resolved, referrer); - } else { - ThrowNoSuchMethodError(type, klass, name, signature); - } - } - break; - } - } + ThrowNoSuchMethodError(type, klass, name, signature); } Thread::Current()->AssertPendingException(); return nullptr; @@ -8154,27 +8048,23 @@ ArtMethod* ClassLinker::ResolveMethodWithoutInvokeType(const DexFile& dex_file, Handle<mirror::ClassLoader> class_loader) { ArtMethod* resolved = dex_cache->GetResolvedMethod(method_idx, image_pointer_size_); Thread::PoisonObjectPointersIfDebug(); - if (resolved != nullptr && !resolved->IsRuntimeMethod()) { + if (resolved != nullptr) { + DCHECK(!resolved->IsRuntimeMethod()); DCHECK(resolved->GetDeclaringClassUnchecked() != nullptr) << resolved->GetDexMethodIndex(); return resolved; } // Fail, get the declaring class. const DexFile::MethodId& method_id = dex_file.GetMethodId(method_idx); - ObjPtr<mirror::Class> klass = ResolveType(dex_file, method_id.class_idx_, dex_cache, class_loader); + ObjPtr<mirror::Class> klass = + ResolveType(dex_file, method_id.class_idx_, dex_cache, class_loader); if (klass == nullptr) { Thread::Current()->AssertPendingException(); return nullptr; } if (klass->IsInterface()) { - LOG(FATAL) << "ResolveAmbiguousMethod: unexpected method in interface: " - << klass->PrettyClass(); - return nullptr; - } - - // Search both direct and virtual methods - resolved = klass->FindDirectMethod(dex_cache.Get(), method_idx, image_pointer_size_); - if (resolved == nullptr) { - resolved = klass->FindVirtualMethod(dex_cache.Get(), method_idx, image_pointer_size_); + resolved = klass->FindInterfaceMethod(dex_cache.Get(), method_idx, image_pointer_size_); + } else { + resolved = klass->FindClassMethod(dex_cache.Get(), method_idx, image_pointer_size_); } return resolved; @@ -8489,19 +8379,19 @@ mirror::MethodHandle* ClassLinker::ResolveMethodHandleForMethod( case DexFile::MethodHandleType::kInvokeStatic: { kind = mirror::MethodHandle::Kind::kInvokeStatic; receiver_count = 0; - target_method = ResolveMethod<kNoICCECheckForCache>(self, - method_handle.field_or_method_idx_, - referrer, - InvokeType::kStatic); + target_method = ResolveMethod<ResolveMode::kNoChecks>(self, + method_handle.field_or_method_idx_, + referrer, + InvokeType::kStatic); break; } case DexFile::MethodHandleType::kInvokeInstance: { kind = mirror::MethodHandle::Kind::kInvokeVirtual; receiver_count = 1; - target_method = ResolveMethod<kNoICCECheckForCache>(self, - method_handle.field_or_method_idx_, - referrer, - InvokeType::kVirtual); + target_method = ResolveMethod<ResolveMode::kNoChecks>(self, + method_handle.field_or_method_idx_, + referrer, + InvokeType::kVirtual); break; } case DexFile::MethodHandleType::kInvokeConstructor: { @@ -8509,10 +8399,10 @@ mirror::MethodHandle* ClassLinker::ResolveMethodHandleForMethod( // are special cased later in this method. kind = mirror::MethodHandle::Kind::kInvokeTransform; receiver_count = 0; - target_method = ResolveMethod<kNoICCECheckForCache>(self, - method_handle.field_or_method_idx_, - referrer, - InvokeType::kDirect); + target_method = ResolveMethod<ResolveMode::kNoChecks>(self, + method_handle.field_or_method_idx_, + referrer, + InvokeType::kDirect); break; } case DexFile::MethodHandleType::kInvokeDirect: { @@ -8535,16 +8425,16 @@ mirror::MethodHandle* ClassLinker::ResolveMethodHandleForMethod( if (target_method->IsPrivate()) { kind = mirror::MethodHandle::Kind::kInvokeDirect; - target_method = ResolveMethod<kNoICCECheckForCache>(self, - method_handle.field_or_method_idx_, - referrer, - InvokeType::kDirect); + target_method = ResolveMethod<ResolveMode::kNoChecks>(self, + method_handle.field_or_method_idx_, + referrer, + InvokeType::kDirect); } else { kind = mirror::MethodHandle::Kind::kInvokeSuper; - target_method = ResolveMethod<kNoICCECheckForCache>(self, - method_handle.field_or_method_idx_, - referrer, - InvokeType::kSuper); + target_method = ResolveMethod<ResolveMode::kNoChecks>(self, + method_handle.field_or_method_idx_, + referrer, + InvokeType::kSuper); if (UNLIKELY(target_method == nullptr)) { break; } @@ -8560,10 +8450,10 @@ mirror::MethodHandle* ClassLinker::ResolveMethodHandleForMethod( case DexFile::MethodHandleType::kInvokeInterface: { kind = mirror::MethodHandle::Kind::kInvokeInterface; receiver_count = 1; - target_method = ResolveMethod<kNoICCECheckForCache>(self, - method_handle.field_or_method_idx_, - referrer, - InvokeType::kInterface); + target_method = ResolveMethod<ResolveMode::kNoChecks>(self, + method_handle.field_or_method_idx_, + referrer, + InvokeType::kInterface); break; } } @@ -9100,51 +8990,6 @@ std::set<DexCacheResolvedClasses> ClassLinker::GetResolvedClasses(bool ignore_bo return ret; } -std::unordered_set<std::string> ClassLinker::GetClassDescriptorsForResolvedClasses( - const std::set<DexCacheResolvedClasses>& classes) { - ScopedTrace trace(__PRETTY_FUNCTION__); - std::unordered_set<std::string> ret; - Thread* const self = Thread::Current(); - std::unordered_map<std::string, const DexFile*> location_to_dex_file; - ScopedObjectAccess soa(self); - ScopedAssertNoThreadSuspension ants(__FUNCTION__); - ReaderMutexLock mu(self, *Locks::dex_lock_); - for (const ClassLinker::DexCacheData& data : GetDexCachesData()) { - if (!self->IsJWeakCleared(data.weak_root)) { - ObjPtr<mirror::DexCache> dex_cache = soa.Decode<mirror::DexCache>(data.weak_root); - if (dex_cache != nullptr) { - const DexFile* dex_file = dex_cache->GetDexFile(); - // There could be duplicates if two dex files with the same location are mapped. - location_to_dex_file.emplace(dex_file->GetLocation(), dex_file); - } - } - } - for (const DexCacheResolvedClasses& info : classes) { - const std::string& location = info.GetDexLocation(); - auto found = location_to_dex_file.find(location); - if (found != location_to_dex_file.end()) { - const DexFile* dex_file = found->second; - VLOG(profiler) << "Found opened dex file for " << dex_file->GetLocation() << " with " - << info.GetClasses().size() << " classes"; - DCHECK_EQ(dex_file->GetLocationChecksum(), info.GetLocationChecksum()); - for (dex::TypeIndex type_idx : info.GetClasses()) { - if (!dex_file->IsTypeIndexValid(type_idx)) { - // Something went bad. The profile is probably corrupted. Abort and return an emtpy set. - LOG(WARNING) << "Corrupted profile: invalid type index " - << type_idx.index_ << " in dex " << location; - return std::unordered_set<std::string>(); - } - const DexFile::TypeId& type_id = dex_file->GetTypeId(type_idx); - const char* descriptor = dex_file->GetTypeDescriptor(type_id); - ret.insert(descriptor); - } - } else { - VLOG(class_linker) << "Failed to find opened dex file for location " << location; - } - } - return ret; -} - class ClassLinker::FindVirtualMethodHolderVisitor : public ClassVisitor { public: FindVirtualMethodHolderVisitor(const ArtMethod* method, PointerSize pointer_size) @@ -9179,15 +9024,62 @@ mirror::IfTable* ClassLinker::AllocIfTable(Thread* self, size_t ifcount) { ifcount * mirror::IfTable::kMax)); } +ArtMethod* ClassLinker::FindMethodForProxy(ArtMethod* proxy_method) { + DCHECK(proxy_method->IsProxyMethod()); + { + uint32_t method_index = proxy_method->GetDexMethodIndex(); + PointerSize pointer_size = image_pointer_size_; + Thread* const self = Thread::Current(); + ReaderMutexLock mu(self, *Locks::dex_lock_); + // Locate the dex cache of the original interface/Object + for (const DexCacheData& data : dex_caches_) { + if (!self->IsJWeakCleared(data.weak_root) && + proxy_method->HasSameDexCacheResolvedMethods(data.resolved_methods, pointer_size)) { + ObjPtr<mirror::DexCache> dex_cache = + ObjPtr<mirror::DexCache>::DownCast(self->DecodeJObject(data.weak_root)); + if (dex_cache != nullptr) { + // Lookup up the method. Instead of going through LookupResolvedMethod() + // and thus LookupResolvedType(), use the ClassTable from the DexCacheData. + ArtMethod* resolved_method = dex_cache->GetResolvedMethod(method_index, pointer_size); + if (resolved_method == nullptr) { + const DexFile::MethodId& method_id = data.dex_file->GetMethodId(method_index); + ObjPtr<mirror::Class> klass = dex_cache->GetResolvedType(method_id.class_idx_); + if (klass == nullptr) { + const char* descriptor = data.dex_file->StringByTypeIdx(method_id.class_idx_); + klass = data.class_table->Lookup(descriptor, ComputeModifiedUtf8Hash(descriptor)); + DCHECK(klass != nullptr); + dex_cache->SetResolvedType(method_id.class_idx_, klass); + } + if (klass->IsInterface()) { + resolved_method = klass->FindInterfaceMethod(dex_cache, method_index, pointer_size); + } else { + DCHECK( + klass == WellKnownClasses::ToClass(WellKnownClasses::java_lang_reflect_Proxy) || + klass == WellKnownClasses::ToClass(WellKnownClasses::java_lang_Object)); + resolved_method = klass->FindClassMethod(dex_cache, method_index, pointer_size); + } + CHECK(resolved_method != nullptr); + dex_cache->SetResolvedMethod(method_index, resolved_method, pointer_size); + } + return resolved_method; + } + } + } + } + // Note: Do not use proxy_method->PrettyMethod() as it can call back here. + LOG(FATAL) << "Didn't find dex cache for " << proxy_method->GetDeclaringClass()->PrettyClass(); + UNREACHABLE(); +} + // Instantiate ResolveMethod. -template ArtMethod* ClassLinker::ResolveMethod<ClassLinker::kForceICCECheck>( +template ArtMethod* ClassLinker::ResolveMethod<ClassLinker::ResolveMode::kCheckICCEAndIAE>( const DexFile& dex_file, uint32_t method_idx, Handle<mirror::DexCache> dex_cache, Handle<mirror::ClassLoader> class_loader, ArtMethod* referrer, InvokeType type); -template ArtMethod* ClassLinker::ResolveMethod<ClassLinker::kNoICCECheckForCache>( +template ArtMethod* ClassLinker::ResolveMethod<ClassLinker::ResolveMode::kNoChecks>( const DexFile& dex_file, uint32_t method_idx, Handle<mirror::DexCache> dex_cache, diff --git a/runtime/class_linker.h b/runtime/class_linker.h index 864d37fa89..62fb45b49a 100644 --- a/runtime/class_linker.h +++ b/runtime/class_linker.h @@ -55,8 +55,12 @@ namespace mirror { class MethodType; template<class T> class ObjectArray; class StackTraceElement; + template <typename T> struct NativeDexCachePair; + using MethodDexCachePair = NativeDexCachePair<ArtMethod>; + using MethodDexCacheType = std::atomic<MethodDexCachePair>; } // namespace mirror +class ClassHierarchyAnalysis; class ClassTable; template<class T> class Handle; class ImtConflictTable; @@ -281,12 +285,18 @@ class ClassLinker { REQUIRES(!Locks::dex_lock_, !Roles::uninterruptible_); // Determine whether a dex cache result should be trusted, or an IncompatibleClassChangeError - // check should be performed even after a hit. - enum ResolveMode { // private. - kNoICCECheckForCache, - kForceICCECheck + // check and IllegalAccessError check should be performed even after a hit. + enum class ResolveMode { // private. + kNoChecks, + kCheckICCEAndIAE }; + // Look up a previously resolved method with the given index. + ArtMethod* LookupResolvedMethod(uint32_t method_idx, + ObjPtr<mirror::DexCache> dex_cache, + ObjPtr<mirror::ClassLoader> class_loader) + REQUIRES_SHARED(Locks::mutator_lock_); + // Resolve a method with a given ID from the DexFile, storing the // result in DexCache. The ClassLinker and ClassLoader are used as // in ResolveType. What is unique is the method type argument which @@ -302,17 +312,10 @@ class ClassLinker { REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Locks::dex_lock_, !Roles::uninterruptible_); + template <InvokeType type, ResolveMode kResolveMode> ArtMethod* GetResolvedMethod(uint32_t method_idx, ArtMethod* referrer) REQUIRES_SHARED(Locks::mutator_lock_); - // This returns the class referred to by GetMethodId(method_idx).class_idx_. This might be - // different then the declaring class of the resolved method due to copied - // miranda/default/conflict methods. - mirror::Class* ResolveReferencedClassOfMethod(uint32_t method_idx, - Handle<mirror::DexCache> dex_cache, - Handle<mirror::ClassLoader> class_loader) - REQUIRES_SHARED(Locks::mutator_lock_) - REQUIRES(!Locks::dex_lock_, !Roles::uninterruptible_); template <ResolveMode kResolveMode> ArtMethod* ResolveMethod(Thread* self, uint32_t method_idx, ArtMethod* referrer, InvokeType type) REQUIRES_SHARED(Locks::mutator_lock_) @@ -394,9 +397,6 @@ class ClassLinker { ObjPtr<mirror::ClassLoader> class_loader) REQUIRES(!Locks::dex_lock_) REQUIRES_SHARED(Locks::mutator_lock_); - void RegisterBootClassPathDexFile(const DexFile& dex_file, ObjPtr<mirror::DexCache> dex_cache) - REQUIRES(!Locks::dex_lock_) - REQUIRES_SHARED(Locks::mutator_lock_); const std::vector<const DexFile*>& GetBootClassPath() { return boot_class_path_; @@ -430,9 +430,6 @@ class ClassLinker { ClassTable* FindClassTable(Thread* self, ObjPtr<mirror::DexCache> dex_cache) REQUIRES(!Locks::dex_lock_) REQUIRES_SHARED(Locks::mutator_lock_); - void FixupDexCaches(ArtMethod* resolution_method) - REQUIRES(!Locks::dex_lock_) - REQUIRES_SHARED(Locks::mutator_lock_); LengthPrefixedArray<ArtField>* AllocArtFieldArray(Thread* self, LinearAlloc* allocator, @@ -482,8 +479,7 @@ class ClassLinker { REQUIRES_SHARED(Locks::mutator_lock_); std::string GetDescriptorForProxy(ObjPtr<mirror::Class> proxy_class) REQUIRES_SHARED(Locks::mutator_lock_); - template<ReadBarrierOption kReadBarrierOption = kWithReadBarrier> - ArtMethod* FindMethodForProxy(ObjPtr<mirror::Class> proxy_class, ArtMethod* proxy_method) + ArtMethod* FindMethodForProxy(ArtMethod* proxy_method) REQUIRES(!Locks::dex_lock_) REQUIRES_SHARED(Locks::mutator_lock_); @@ -616,11 +612,6 @@ class ClassLinker { std::set<DexCacheResolvedClasses> GetResolvedClasses(bool ignore_boot_classes) REQUIRES(!Locks::dex_lock_); - // Returns the class descriptors for loaded dex files. - std::unordered_set<std::string> GetClassDescriptorsForResolvedClasses( - const std::set<DexCacheResolvedClasses>& classes) - REQUIRES(!Locks::dex_lock_); - static bool IsBootClassLoader(ScopedObjectAccessAlreadyRunnable& soa, ObjPtr<mirror::ClassLoader> class_loader) REQUIRES_SHARED(Locks::mutator_lock_); @@ -679,6 +670,10 @@ class ClassLinker { bool ValidateSuperClassDescriptors(Handle<mirror::Class> klass) REQUIRES_SHARED(Locks::mutator_lock_); + ClassHierarchyAnalysis* GetClassHierarchyAnalysis() { + return cha_.get(); + } + struct DexCacheData { // Construct an invalid data object. DexCacheData() @@ -699,7 +694,7 @@ class ClassLinker { // jweak decode that triggers read barriers (and mark them alive unnecessarily and mess with // class unloading.) const DexFile* dex_file; - ArtMethod** resolved_methods; + mirror::MethodDexCacheType* resolved_methods; // Identify the associated class loader's class table. This is used to make sure that // the Java call to native DexCache.setResolvedType() inserts the resolved type in that // class table. It is also used to make sure we don't register the same dex cache with @@ -725,7 +720,7 @@ class ClassLinker { REQUIRES(!Locks::dex_lock_) REQUIRES_SHARED(Locks::mutator_lock_); - static void DeleteClassLoader(Thread* self, const ClassLoaderData& data) + void DeleteClassLoader(Thread* self, const ClassLoaderData& data) REQUIRES_SHARED(Locks::mutator_lock_); void VisitClassesInternal(ClassVisitor* visitor) @@ -1205,6 +1200,23 @@ class ClassLinker { bool* new_conflict, ArtMethod** imt) REQUIRES_SHARED(Locks::mutator_lock_); + // Check invoke type against the referenced class. Throws IncompatibleClassChangeError + // (if `kThrowOnError`) and returns true on mismatch (kInterface on a non-interface class, + // kVirtual on interface, kDefault on interface for dex files not supporting default methods), + // otherwise returns false. + template <bool kThrowOnError, typename ClassGetter> + static bool CheckInvokeClassMismatch(ObjPtr<mirror::DexCache> dex_cache, + InvokeType type, + ClassGetter class_getter) + REQUIRES_SHARED(Locks::mutator_lock_); + // Helper that feeds the above function with `ClassGetter` doing `LookupResolvedType()`. + template <bool kThrow> + bool CheckInvokeClassMismatch(ObjPtr<mirror::DexCache> dex_cache, + InvokeType type, + uint32_t method_idx, + ObjPtr<mirror::ClassLoader> class_loader) + REQUIRES_SHARED(Locks::mutator_lock_); + std::vector<const DexFile*> boot_class_path_; std::vector<std::unique_ptr<const DexFile>> boot_dex_files_; @@ -1258,6 +1270,8 @@ class ClassLinker { // Image pointer size. PointerSize image_pointer_size_; + std::unique_ptr<ClassHierarchyAnalysis> cha_; + class FindVirtualMethodHolderVisitor; friend class AppImageClassLoadersAndDexCachesHelper; diff --git a/runtime/class_linker_test.cc b/runtime/class_linker_test.cc index 03cc6c59c4..5e9707c062 100644 --- a/runtime/class_linker_test.cc +++ b/runtime/class_linker_test.cc @@ -28,9 +28,10 @@ #include "common_runtime_test.h" #include "dex_file.h" #include "dex_file_types.h" -#include "experimental_flags.h" #include "entrypoints/entrypoint_utils-inl.h" +#include "experimental_flags.h" #include "gc/heap.h" +#include "handle_scope-inl.h" #include "mirror/accessible_object.h" #include "mirror/call_site.h" #include "mirror/class-inl.h" @@ -39,16 +40,15 @@ #include "mirror/emulated_stack_frame.h" #include "mirror/executable.h" #include "mirror/field.h" -#include "mirror/method_type.h" #include "mirror/method_handle_impl.h" #include "mirror/method_handles_lookup.h" +#include "mirror/method_type.h" #include "mirror/object-inl.h" #include "mirror/object_array-inl.h" #include "mirror/proxy.h" #include "mirror/reference.h" #include "mirror/stack_trace_element.h" #include "mirror/string-inl.h" -#include "handle_scope-inl.h" #include "scoped_thread_state_change-inl.h" #include "thread-current-inl.h" @@ -440,14 +440,6 @@ class ClassLinkerTest : public CommonRuntimeTest { } TestRootVisitor visitor; class_linker_->VisitRoots(&visitor, kVisitRootFlagAllRoots); - // Verify the dex cache has resolution methods in all resolved method slots - ObjPtr<mirror::DexCache> dex_cache = class_linker_->FindDexCache(Thread::Current(), dex); - auto* resolved_methods = dex_cache->GetResolvedMethods(); - for (size_t i = 0, num_methods = dex_cache->NumResolvedMethods(); i != num_methods; ++i) { - EXPECT_TRUE( - mirror::DexCache::GetElementPtrSize(resolved_methods, i, kRuntimePointerSize) != nullptr) - << dex.GetLocation() << " i=" << i; - } } class TestRootVisitor : public SingleRootVisitor { @@ -1121,7 +1113,7 @@ TEST_F(ClassLinkerTest, StaticFields) { // Static final primitives that are initialized by a compile-time constant // expression resolve to a copy of a constant value from the constant pool. // So <clinit> should be null. - ArtMethod* clinit = statics->FindDirectMethod("<clinit>", "()V", kRuntimePointerSize); + ArtMethod* clinit = statics->FindClassMethod("<clinit>", "()V", kRuntimePointerSize); EXPECT_TRUE(clinit == nullptr); EXPECT_EQ(9U, statics->NumStaticFields()); @@ -1208,24 +1200,30 @@ TEST_F(ClassLinkerTest, Interfaces) { EXPECT_TRUE(J->IsAssignableFrom(B.Get())); const Signature void_sig = I->GetDexCache()->GetDexFile()->CreateSignature("()V"); - ArtMethod* Ii = I->FindVirtualMethod("i", void_sig, kRuntimePointerSize); - ArtMethod* Jj1 = J->FindVirtualMethod("j1", void_sig, kRuntimePointerSize); - ArtMethod* Jj2 = J->FindVirtualMethod("j2", void_sig, kRuntimePointerSize); + ArtMethod* Ii = I->FindClassMethod("i", void_sig, kRuntimePointerSize); + ArtMethod* Jj1 = J->FindClassMethod("j1", void_sig, kRuntimePointerSize); + ArtMethod* Jj2 = J->FindClassMethod("j2", void_sig, kRuntimePointerSize); ArtMethod* Kj1 = K->FindInterfaceMethod("j1", void_sig, kRuntimePointerSize); ArtMethod* Kj2 = K->FindInterfaceMethod("j2", void_sig, kRuntimePointerSize); ArtMethod* Kk = K->FindInterfaceMethod("k", void_sig, kRuntimePointerSize); - ArtMethod* Ai = A->FindVirtualMethod("i", void_sig, kRuntimePointerSize); - ArtMethod* Aj1 = A->FindVirtualMethod("j1", void_sig, kRuntimePointerSize); - ArtMethod* Aj2 = A->FindVirtualMethod("j2", void_sig, kRuntimePointerSize); + ArtMethod* Ai = A->FindClassMethod("i", void_sig, kRuntimePointerSize); + ArtMethod* Aj1 = A->FindClassMethod("j1", void_sig, kRuntimePointerSize); + ArtMethod* Aj2 = A->FindClassMethod("j2", void_sig, kRuntimePointerSize); ASSERT_TRUE(Ii != nullptr); + ASSERT_FALSE(Ii->IsDirect()); ASSERT_TRUE(Jj1 != nullptr); + ASSERT_FALSE(Jj1->IsDirect()); ASSERT_TRUE(Jj2 != nullptr); + ASSERT_FALSE(Jj2->IsDirect()); ASSERT_TRUE(Kj1 != nullptr); ASSERT_TRUE(Kj2 != nullptr); ASSERT_TRUE(Kk != nullptr); ASSERT_TRUE(Ai != nullptr); + ASSERT_FALSE(Ai->IsDirect()); ASSERT_TRUE(Aj1 != nullptr); + ASSERT_FALSE(Aj1->IsDirect()); ASSERT_TRUE(Aj2 != nullptr); + ASSERT_FALSE(Aj2->IsDirect()); EXPECT_NE(Ii, Ai); EXPECT_NE(Jj1, Aj1); EXPECT_NE(Jj2, Aj2); @@ -1266,7 +1264,10 @@ TEST_F(ClassLinkerTest, ResolveVerifyAndClinit) { hs.NewHandle(soa.Decode<mirror::ClassLoader>(jclass_loader))); mirror::Class* klass = class_linker_->FindClass(soa.Self(), "LStaticsFromCode;", class_loader); ArtMethod* clinit = klass->FindClassInitializer(kRuntimePointerSize); - ArtMethod* getS0 = klass->FindDirectMethod("getS0", "()Ljava/lang/Object;", kRuntimePointerSize); + ArtMethod* getS0 = + klass->FindClassMethod("getS0", "()Ljava/lang/Object;", kRuntimePointerSize); + ASSERT_TRUE(getS0 != nullptr); + ASSERT_TRUE(getS0->IsStatic()); const DexFile::TypeId* type_id = dex_file->FindTypeId("LStaticsFromCode;"); ASSERT_TRUE(type_id != nullptr); dex::TypeIndex type_idx = dex_file->GetIndexForTypeId(*type_id); @@ -1489,9 +1490,12 @@ TEST_F(ClassLinkerMethodHandlesTest, TestResolveMethodTypes) { hs.NewHandle(class_linker_->FindClass(soa.Self(), "LMethodTypes;", class_loader))); class_linker_->EnsureInitialized(soa.Self(), method_types, true, true); - ArtMethod* method1 = method_types->FindVirtualMethod("method1", - "(Ljava/lang/String;)Ljava/lang/String;", - kRuntimePointerSize); + ArtMethod* method1 = method_types->FindClassMethod( + "method1", + "(Ljava/lang/String;)Ljava/lang/String;", + kRuntimePointerSize); + ASSERT_TRUE(method1 != nullptr); + ASSERT_FALSE(method1->IsDirect()); const DexFile& dex_file = *(method1->GetDexFile()); Handle<mirror::DexCache> dex_cache = hs.NewHandle( @@ -1522,10 +1526,12 @@ TEST_F(ClassLinkerMethodHandlesTest, TestResolveMethodTypes) { // Resolve the MethodType associated with a different method signature // and assert it's different. - ArtMethod* method2 = method_types->FindVirtualMethod( + ArtMethod* method2 = method_types->FindClassMethod( "method2", "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;", kRuntimePointerSize); + ASSERT_TRUE(method2 != nullptr); + ASSERT_FALSE(method2->IsDirect()); const DexFile::MethodId& method2_id = dex_file.GetMethodId(method2->GetDexMethodIndex()); Handle<mirror::MethodType> method2_type = hs.NewHandle( class_linker_->ResolveMethodType(dex_file, method2_id.proto_idx_, dex_cache, class_loader)); diff --git a/runtime/class_loader_context_test.cc b/runtime/class_loader_context_test.cc index e60f61cc11..2b85188e38 100644 --- a/runtime/class_loader_context_test.cc +++ b/runtime/class_loader_context_test.cc @@ -16,7 +16,6 @@ #include <gtest/gtest.h> - #include "class_loader_context.h" #include "common_runtime_test.h" @@ -529,4 +528,12 @@ TEST_F(ClassLoaderContextTest, VerifyClassLoaderContextMatchAfterEncoding) { ASSERT_TRUE(context->VerifyClassLoaderContextMatch(context->EncodeContextForOatFile(""))); } +TEST_F(ClassLoaderContextTest, VerifyClassLoaderContextMatchAfterEncodingMultidex) { + jobject class_loader = LoadDexInPathClassLoader("MultiDex", nullptr); + + std::unique_ptr<ClassLoaderContext> context = CreateContextForClassLoader(class_loader); + + ASSERT_TRUE(context->VerifyClassLoaderContextMatch(context->EncodeContextForOatFile(""))); +} + } // namespace art diff --git a/runtime/common_runtime_test.cc b/runtime/common_runtime_test.cc index aae997327c..29b376a21c 100644 --- a/runtime/common_runtime_test.cc +++ b/runtime/common_runtime_test.cc @@ -16,34 +16,34 @@ #include "common_runtime_test.h" -#include <cstdio> #include <dirent.h> #include <dlfcn.h> #include <fcntl.h> -#include <ScopedLocalRef.h> #include <stdlib.h> +#include <cstdio> +#include "nativehelper/ScopedLocalRef.h" #include "../../external/icu/icu4c/source/common/unicode/uvernum.h" #include "android-base/stringprintf.h" #include "art_field-inl.h" -#include "base/macros.h" #include "base/logging.h" +#include "base/macros.h" #include "base/stl_util.h" #include "base/unix_file/fd_file.h" #include "class_linker.h" #include "compiler_callbacks.h" #include "dex_file-inl.h" -#include "gc_root-inl.h" #include "gc/heap.h" +#include "gc_root-inl.h" #include "gtest/gtest.h" #include "handle_scope-inl.h" #include "interpreter/unstarted_runtime.h" #include "java_vm_ext.h" #include "jni_internal.h" +#include "mem_map.h" #include "mirror/class-inl.h" #include "mirror/class_loader.h" -#include "mem_map.h" #include "native/dalvik_system_DexFile.h" #include "noop_compiler_callbacks.h" #include "os.h" @@ -425,7 +425,6 @@ void CommonRuntimeTestImpl::SetUp() { PostRuntimeCreate(); runtime_.reset(Runtime::Current()); class_linker_ = runtime_->GetClassLinker(); - class_linker_->FixupDexCaches(runtime_->GetResolutionMethod()); // Runtime::Create acquired the mutator_lock_ that is normally given away when we // Runtime::Start, give it away now and then switch to a more managable ScopedObjectAccess. @@ -786,6 +785,60 @@ std::string CommonRuntimeTestImpl::CreateClassPathWithChecksums( return classpath; } +void CommonRuntimeTestImpl::FillHeap(Thread* self, + ClassLinker* class_linker, + VariableSizedHandleScope* handle_scope) { + DCHECK(handle_scope != nullptr); + + Runtime::Current()->GetHeap()->SetIdealFootprint(1 * GB); + + // Class java.lang.Object. + Handle<mirror::Class> c(handle_scope->NewHandle( + class_linker->FindSystemClass(self, "Ljava/lang/Object;"))); + // Array helps to fill memory faster. + Handle<mirror::Class> ca(handle_scope->NewHandle( + class_linker->FindSystemClass(self, "[Ljava/lang/Object;"))); + + // Start allocating with ~128K + size_t length = 128 * KB; + while (length > 40) { + const int32_t array_length = length / 4; // Object[] has elements of size 4. + MutableHandle<mirror::Object> h(handle_scope->NewHandle<mirror::Object>( + mirror::ObjectArray<mirror::Object>::Alloc(self, ca.Get(), array_length))); + if (self->IsExceptionPending() || h == nullptr) { + self->ClearException(); + + // Try a smaller length + length = length / 2; + // Use at most a quarter the reported free space. + size_t mem = Runtime::Current()->GetHeap()->GetFreeMemory(); + if (length * 4 > mem) { + length = mem / 4; + } + } + } + + // Allocate simple objects till it fails. + while (!self->IsExceptionPending()) { + handle_scope->NewHandle<mirror::Object>(c->AllocObject(self)); + } + self->ClearException(); +} + +void CommonRuntimeTestImpl::SetUpRuntimeOptionsForFillHeap(RuntimeOptions *options) { + // Use a smaller heap + bool found = false; + for (std::pair<std::string, const void*>& pair : *options) { + if (pair.first.find("-Xmx") == 0) { + pair.first = "-Xmx4M"; // Smallest we can go. + found = true; + } + } + if (!found) { + options->emplace_back("-Xmx4M", nullptr); + } +} + CheckJniAbortCatcher::CheckJniAbortCatcher() : vm_(Runtime::Current()->GetJavaVM()) { vm_->SetCheckJniAbortHook(Hook, &actual_); } diff --git a/runtime/common_runtime_test.h b/runtime/common_runtime_test.h index daf9ac344e..74bc0b2afb 100644 --- a/runtime/common_runtime_test.h +++ b/runtime/common_runtime_test.h @@ -44,6 +44,8 @@ class DexFile; class JavaVMExt; class Runtime; typedef std::vector<std::pair<std::string, const void*>> RuntimeOptions; +class Thread; +class VariableSizedHandleScope; uint8_t* DecodeBase64(const char* src, size_t* dst_size); @@ -105,6 +107,14 @@ class CommonRuntimeTestImpl { // Retuerns the filename for a test dex (i.e. XandY or ManyMethods). std::string GetTestDexFileName(const char* name) const; + // A helper function to fill the heap. + static void FillHeap(Thread* self, + ClassLinker* class_linker, + VariableSizedHandleScope* handle_scope) + REQUIRES_SHARED(Locks::mutator_lock_); + // A helper to set up a small heap (4M) to make FillHeap faster. + static void SetUpRuntimeOptionsForFillHeap(RuntimeOptions *options); + protected: // Allow subclases such as CommonCompilerTest to add extra options. virtual void SetUpRuntimeOptions(RuntimeOptions* options ATTRIBUTE_UNUSED) {} diff --git a/runtime/common_throws.cc b/runtime/common_throws.cc index 6758d75e47..a46f531d93 100644 --- a/runtime/common_throws.cc +++ b/runtime/common_throws.cc @@ -19,7 +19,6 @@ #include <sstream> #include "android-base/stringprintf.h" -#include "ScopedLocalRef.h" #include "art_field-inl.h" #include "art_method-inl.h" @@ -32,6 +31,7 @@ #include "mirror/method_type.h" #include "mirror/object-inl.h" #include "mirror/object_array-inl.h" +#include "nativehelper/ScopedLocalRef.h" #include "obj_ptr-inl.h" #include "thread.h" #include "verifier/method_verifier.h" diff --git a/runtime/compiler_filter.cc b/runtime/compiler_filter.cc index 4847f38489..7b2dd05156 100644 --- a/runtime/compiler_filter.cc +++ b/runtime/compiler_filter.cc @@ -165,6 +165,10 @@ bool CompilerFilter::IsAsGoodAs(Filter current, Filter target) { return current >= target; } +bool CompilerFilter::IsBetter(Filter current, Filter target) { + return current > target; +} + std::string CompilerFilter::NameOfFilter(Filter filter) { switch (filter) { case CompilerFilter::kAssumeVerified: return "assume-verified"; diff --git a/runtime/compiler_filter.h b/runtime/compiler_filter.h index f802439053..60975b04f7 100644 --- a/runtime/compiler_filter.h +++ b/runtime/compiler_filter.h @@ -84,6 +84,11 @@ class CompilerFilter FINAL { // not as good as kSpeed. static bool IsAsGoodAs(Filter current, Filter target); + // Returns true if 'current' compiler filter is better than 'target' compiler + // filter. Compared to IsAsGoodAs, this returns false if the compiler filters are + // equal. + static bool IsBetter(Filter current, Filter target); + // Return the flag name of the given filter. // For example: given kVerifyAtRuntime, returns "verify-at-runtime". // The name returned corresponds to the name accepted by diff --git a/runtime/debugger.cc b/runtime/debugger.cc index 778b92851b..5a87ae8420 100644 --- a/runtime/debugger.cc +++ b/runtime/debugger.cc @@ -30,8 +30,8 @@ #include "base/enums.h" #include "base/strlcpy.h" #include "base/time_utils.h" -#include "class_linker.h" #include "class_linker-inl.h" +#include "class_linker.h" #include "dex_file-inl.h" #include "dex_file_annotations.h" #include "dex_instruction.h" @@ -47,19 +47,19 @@ #include "jdwp/object_registry.h" #include "jni_internal.h" #include "jvalue-inl.h" -#include "mirror/class.h" #include "mirror/class-inl.h" +#include "mirror/class.h" #include "mirror/class_loader.h" #include "mirror/object-inl.h" #include "mirror/object_array-inl.h" #include "mirror/string-inl.h" #include "mirror/throwable.h" +#include "nativehelper/ScopedLocalRef.h" +#include "nativehelper/ScopedPrimitiveArray.h" #include "obj_ptr-inl.h" #include "reflection.h" #include "safe_map.h" #include "scoped_thread_state_change-inl.h" -#include "ScopedLocalRef.h" -#include "ScopedPrimitiveArray.h" #include "stack.h" #include "thread_list.h" #include "utf.h" diff --git a/runtime/debugger.h b/runtime/debugger.h index 4f3ff40e86..0be46d6b8f 100644 --- a/runtime/debugger.h +++ b/runtime/debugger.h @@ -27,8 +27,8 @@ #include <string> #include <vector> -#include "gc_root.h" #include "class_linker.h" +#include "gc_root.h" #include "handle.h" #include "jdwp/jdwp.h" #include "jni.h" diff --git a/runtime/dex_file-inl.h b/runtime/dex_file-inl.h index 41db4d8219..b163cdb8dc 100644 --- a/runtime/dex_file-inl.h +++ b/runtime/dex_file-inl.h @@ -181,19 +181,18 @@ inline bool Signature::operator==(const Signature& rhs) const { 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. - } + // We found a reference parameter in the matching shorty, so both lists must be non-empty. + DCHECK(params != nullptr); + DCHECK(rhs_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. } } } diff --git a/runtime/dex_file.h b/runtime/dex_file.h index eb3b210cd1..990ab118e7 100644 --- a/runtime/dex_file.h +++ b/runtime/dex_file.h @@ -62,11 +62,11 @@ class DexFile { static const uint16_t kDexNoIndex16 = 0xFFFF; // The separator character in MultiDex locations. - static constexpr char kMultiDexSeparator = ':'; + static constexpr char kMultiDexSeparator = '!'; // A string version of the previous. This is a define so that we can merge string literals in the // preprocessor. - #define kMultiDexSeparatorString ":" + #define kMultiDexSeparatorString "!" // Raw header_item. struct Header { @@ -499,7 +499,7 @@ class DexFile { return GetBaseLocation(location.c_str()); } - // Returns the ':classes*.dex' part of the dex location. Returns an empty + // Returns the '!classes*.dex' part of the dex location. Returns an empty // string if there is no multidex suffix for the given location. // The kMultiDexSeparator is included in the returned suffix. static std::string GetMultiDexSuffix(const std::string& location) { diff --git a/runtime/dex_file_test.cc b/runtime/dex_file_test.cc index 78d5c5f4ba..1a73062068 100644 --- a/runtime/dex_file_test.cc +++ b/runtime/dex_file_test.cc @@ -535,9 +535,9 @@ TEST_F(DexFileTest, GetMultiDexLocation) { std::string dex_location_str = "/system/app/framework.jar"; const char* dex_location = dex_location_str.c_str(); ASSERT_EQ("/system/app/framework.jar", DexFile::GetMultiDexLocation(0, dex_location)); - ASSERT_EQ("/system/app/framework.jar:classes2.dex", + ASSERT_EQ("/system/app/framework.jar!classes2.dex", DexFile::GetMultiDexLocation(1, dex_location)); - ASSERT_EQ("/system/app/framework.jar:classes101.dex", + ASSERT_EQ("/system/app/framework.jar!classes101.dex", DexFile::GetMultiDexLocation(100, dex_location)); } @@ -563,11 +563,11 @@ TEST_F(DexFileTest, GetDexCanonicalLocation) { TEST(DexFileUtilsTest, GetBaseLocationAndMultiDexSuffix) { EXPECT_EQ("/foo/bar/baz.jar", DexFile::GetBaseLocation("/foo/bar/baz.jar")); - EXPECT_EQ("/foo/bar/baz.jar", DexFile::GetBaseLocation("/foo/bar/baz.jar:classes2.dex")); - EXPECT_EQ("/foo/bar/baz.jar", DexFile::GetBaseLocation("/foo/bar/baz.jar:classes8.dex")); + EXPECT_EQ("/foo/bar/baz.jar", DexFile::GetBaseLocation("/foo/bar/baz.jar!classes2.dex")); + EXPECT_EQ("/foo/bar/baz.jar", DexFile::GetBaseLocation("/foo/bar/baz.jar!classes8.dex")); EXPECT_EQ("", DexFile::GetMultiDexSuffix("/foo/bar/baz.jar")); - EXPECT_EQ(":classes2.dex", DexFile::GetMultiDexSuffix("/foo/bar/baz.jar:classes2.dex")); - EXPECT_EQ(":classes8.dex", DexFile::GetMultiDexSuffix("/foo/bar/baz.jar:classes8.dex")); + EXPECT_EQ("!classes2.dex", DexFile::GetMultiDexSuffix("/foo/bar/baz.jar!classes2.dex")); + EXPECT_EQ("!classes8.dex", DexFile::GetMultiDexSuffix("/foo/bar/baz.jar!classes8.dex")); } TEST_F(DexFileTest, ZipOpenClassesPresent) { diff --git a/runtime/dex_file_verifier_test.cc b/runtime/dex_file_verifier_test.cc index 0e58e6d564..e2770d1464 100644 --- a/runtime/dex_file_verifier_test.cc +++ b/runtime/dex_file_verifier_test.cc @@ -16,14 +16,15 @@ #include "dex_file_verifier.h" -#include "sys/mman.h" -#include "zlib.h" +#include <sys/mman.h> +#include <zlib.h> + #include <functional> #include <memory> -#include "base/unix_file/fd_file.h" #include "base/bit_utils.h" #include "base/macros.h" +#include "base/unix_file/fd_file.h" #include "common_runtime_test.h" #include "dex_file-inl.h" #include "dex_file_types.h" diff --git a/runtime/dex_reference_collection.h b/runtime/dex_reference_collection.h index 01b9b97786..047771f4a5 100644 --- a/runtime/dex_reference_collection.h +++ b/runtime/dex_reference_collection.h @@ -17,10 +17,10 @@ #ifndef ART_RUNTIME_DEX_REFERENCE_COLLECTION_H_ #define ART_RUNTIME_DEX_REFERENCE_COLLECTION_H_ -#include "base/macros.h" - -#include <vector> #include <map> +#include <vector> + +#include "base/macros.h" namespace art { diff --git a/runtime/entrypoints/entrypoint_utils-inl.h b/runtime/entrypoints/entrypoint_utils-inl.h index 6547299853..be3e4f811a 100644 --- a/runtime/entrypoints/entrypoint_utils-inl.h +++ b/runtime/entrypoints/entrypoint_utils-inl.h @@ -50,6 +50,8 @@ inline ArtMethod* GetResolvedMethod(ArtMethod* outer_method, const InlineInfoEncoding& encoding, uint8_t inlining_depth) REQUIRES_SHARED(Locks::mutator_lock_) { + DCHECK(!outer_method->IsObsolete()); + // This method is being used by artQuickResolutionTrampoline, before it sets up // the passed parameters in a GC friendly way. Therefore we must never be // suspended while executing it. @@ -78,10 +80,12 @@ inline ArtMethod* GetResolvedMethod(ArtMethod* outer_method, } // Lookup the declaring class of the inlined method. - const DexFile* dex_file = caller->GetDexFile(); + ObjPtr<mirror::DexCache> dex_cache = caller->GetDexCache(); + const DexFile* dex_file = dex_cache->GetDexFile(); const DexFile::MethodId& method_id = dex_file->GetMethodId(method_index); ArtMethod* inlined_method = caller->GetDexCacheResolvedMethod(method_index, kRuntimePointerSize); - if (inlined_method != nullptr && !inlined_method->IsRuntimeMethod()) { + if (inlined_method != nullptr) { + DCHECK(!inlined_method->IsRuntimeMethod()); return inlined_method; } const char* descriptor = dex_file->StringByTypeIdx(method_id.class_idx_); @@ -90,25 +94,17 @@ inline ArtMethod* GetResolvedMethod(ArtMethod* outer_method, mirror::ClassLoader* class_loader = caller->GetDeclaringClass()->GetClassLoader(); mirror::Class* klass = class_linker->LookupClass(self, descriptor, class_loader); if (klass == nullptr) { - LOG(FATAL) << "Could not find an inlined method from an .oat file: " - << "the class " << descriptor << " was not found in the class loader of " - << caller->PrettyMethod() << ". " - << "This must be due to playing wrongly with class loaders"; + LOG(FATAL) << "Could not find an inlined method from an .oat file: the class " << descriptor + << " was not found in the class loader of " << caller->PrettyMethod() << ". " + << "This must be due to playing wrongly with class loaders"; } - // Lookup the method. - const char* method_name = dex_file->GetMethodName(method_id); - const Signature signature = dex_file->GetMethodSignature(method_id); - - inlined_method = klass->FindDeclaredDirectMethod(method_name, signature, kRuntimePointerSize); + inlined_method = klass->FindClassMethod(dex_cache, method_index, kRuntimePointerSize); if (inlined_method == nullptr) { - inlined_method = klass->FindDeclaredVirtualMethod(method_name, signature, kRuntimePointerSize); - if (inlined_method == nullptr) { - LOG(FATAL) << "Could not find an inlined method from an .oat file: " - << "the class " << descriptor << " does not have " - << method_name << signature << " declared. " - << "This must be due to duplicate classes or playing wrongly with class loaders"; - } + LOG(FATAL) << "Could not find an inlined method from an .oat file: the class " << descriptor + << " does not have " << dex_file->GetMethodName(method_id) + << dex_file->GetMethodSignature(method_id) << " declared. " + << "This must be due to duplicate classes or playing wrongly with class loaders"; } caller->SetDexCacheResolvedMethod(method_index, inlined_method, kRuntimePointerSize); @@ -444,39 +440,20 @@ inline ArtMethod* FindMethodFromCode(uint32_t method_idx, ArtMethod* referrer, Thread* self) { ClassLinker* const class_linker = Runtime::Current()->GetClassLinker(); - ArtMethod* resolved_method = class_linker->GetResolvedMethod(method_idx, referrer); - if (resolved_method == nullptr) { + constexpr ClassLinker::ResolveMode resolve_mode = + access_check ? ClassLinker::ResolveMode::kCheckICCEAndIAE + : ClassLinker::ResolveMode::kNoChecks; + ArtMethod* resolved_method; + if (type == kStatic) { + resolved_method = class_linker->ResolveMethod<resolve_mode>(self, method_idx, referrer, type); + } else { StackHandleScope<1> hs(self); - ObjPtr<mirror::Object> null_this = nullptr; - HandleWrapperObjPtr<mirror::Object> h_this( - hs.NewHandleWrapper(type == kStatic ? &null_this : this_object)); - constexpr ClassLinker::ResolveMode resolve_mode = - access_check ? ClassLinker::kForceICCECheck - : ClassLinker::kNoICCECheckForCache; + HandleWrapperObjPtr<mirror::Object> h_this(hs.NewHandleWrapper(this_object)); resolved_method = class_linker->ResolveMethod<resolve_mode>(self, method_idx, referrer, type); } - // Resolution and access check. if (UNLIKELY(resolved_method == nullptr)) { DCHECK(self->IsExceptionPending()); // Throw exception and unwind. return nullptr; // Failure. - } else if (access_check) { - mirror::Class* methods_class = resolved_method->GetDeclaringClass(); - bool can_access_resolved_method = - referrer->GetDeclaringClass()->CheckResolvedMethodAccess(methods_class, - resolved_method, - referrer->GetDexCache(), - method_idx, - type); - if (UNLIKELY(!can_access_resolved_method)) { - DCHECK(self->IsExceptionPending()); // Throw exception and unwind. - return nullptr; // Failure. - } - // 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. - } } // Next, null pointer check. if (UNLIKELY(*this_object == nullptr && type != kStatic)) { @@ -499,7 +476,7 @@ inline ArtMethod* FindMethodFromCode(uint32_t method_idx, case kDirect: return resolved_method; case kVirtual: { - mirror::Class* klass = (*this_object)->GetClass(); + ObjPtr<mirror::Class> klass = (*this_object)->GetClass(); uint16_t vtable_index = resolved_method->GetMethodIndex(); if (access_check && (!klass->HasVTable() || @@ -532,7 +509,7 @@ inline ArtMethod* FindMethodFromCode(uint32_t method_idx, // It is not an interface. If the referring class is in the class hierarchy of the // referenced class in the bytecode, we use its super class. Otherwise, we throw // a NoSuchMethodError. - mirror::Class* super_class = nullptr; + ObjPtr<mirror::Class> super_class = nullptr; if (method_reference_class->IsAssignableFrom(h_referring_class.Get())) { super_class = h_referring_class->GetSuperClass(); } @@ -577,11 +554,10 @@ inline ArtMethod* FindMethodFromCode(uint32_t method_idx, case kInterface: { uint32_t imt_index = ImTable::GetImtIndex(resolved_method); PointerSize pointer_size = class_linker->GetImagePointerSize(); - ArtMethod* imt_method = (*this_object)->GetClass()->GetImt(pointer_size)-> - Get(imt_index, pointer_size); + ObjPtr<mirror::Class> klass = (*this_object)->GetClass(); + ArtMethod* imt_method = klass->GetImt(pointer_size)->Get(imt_index, pointer_size); if (!imt_method->IsRuntimeMethod()) { if (kIsDebugBuild) { - mirror::Class* klass = (*this_object)->GetClass(); ArtMethod* method = klass->FindVirtualMethodForInterface( resolved_method, class_linker->GetImagePointerSize()); CHECK_EQ(imt_method, method) << ArtMethod::PrettyMethod(resolved_method) << " / " @@ -591,7 +567,7 @@ inline ArtMethod* FindMethodFromCode(uint32_t method_idx, } return imt_method; } else { - ArtMethod* interface_method = (*this_object)->GetClass()->FindVirtualMethodForInterface( + ArtMethod* interface_method = klass->FindVirtualMethodForInterface( resolved_method, class_linker->GetImagePointerSize()); if (UNLIKELY(interface_method == nullptr)) { ThrowIncompatibleClassChangeErrorClassForInterfaceDispatch(resolved_method, @@ -690,24 +666,14 @@ inline ArtMethod* FindMethodFast(uint32_t method_idx, } ObjPtr<mirror::Class> referring_class = referrer->GetDeclaringClass(); ObjPtr<mirror::DexCache> dex_cache = referrer->GetDexCache(); - ArtMethod* resolved_method = dex_cache->GetResolvedMethod(method_idx, kRuntimePointerSize); + constexpr ClassLinker::ResolveMode resolve_mode = access_check + ? ClassLinker::ResolveMode::kCheckICCEAndIAE + : ClassLinker::ResolveMode::kNoChecks; + ClassLinker* linker = Runtime::Current()->GetClassLinker(); + ArtMethod* resolved_method = linker->GetResolvedMethod<type, resolve_mode>(method_idx, referrer); if (UNLIKELY(resolved_method == nullptr)) { return nullptr; } - if (access_check) { - // Check for incompatible class change errors and access. - bool icce = resolved_method->CheckIncompatibleClassChange(type); - if (UNLIKELY(icce)) { - return nullptr; - } - ObjPtr<mirror::Class> methods_class = resolved_method->GetDeclaringClass(); - if (UNLIKELY(!referring_class->CanAccess(methods_class) || - !referring_class->CanAccessMember(methods_class, - resolved_method->GetAccessFlags()))) { - // Potential illegal access, may need to refine the method's class. - return nullptr; - } - } if (type == kInterface) { // Most common form of slow path dispatch. return this_object->GetClass()->FindVirtualMethodForInterface(resolved_method, kRuntimePointerSize); diff --git a/runtime/entrypoints/entrypoint_utils.h b/runtime/entrypoints/entrypoint_utils.h index fe85887f05..4f9090815f 100644 --- a/runtime/entrypoints/entrypoint_utils.h +++ b/runtime/entrypoints/entrypoint_utils.h @@ -23,8 +23,8 @@ #include "base/callee_save_type.h" #include "base/macros.h" #include "base/mutex.h" -#include "dex_instruction.h" #include "dex_file_types.h" +#include "dex_instruction.h" #include "gc/allocator_type.h" #include "handle.h" #include "invoke_type.h" diff --git a/runtime/entrypoints/quick/quick_alloc_entrypoints.cc b/runtime/entrypoints/quick/quick_alloc_entrypoints.cc index 582f0cff48..b8d96af3fb 100644 --- a/runtime/entrypoints/quick/quick_alloc_entrypoints.cc +++ b/runtime/entrypoints/quick/quick_alloc_entrypoints.cc @@ -22,8 +22,8 @@ #include "dex_file_types.h" #include "entrypoints/entrypoint_utils-inl.h" #include "mirror/class-inl.h" -#include "mirror/object_array-inl.h" #include "mirror/object-inl.h" +#include "mirror/object_array-inl.h" namespace art { diff --git a/runtime/entrypoints/quick/quick_dexcache_entrypoints.cc b/runtime/entrypoints/quick/quick_dexcache_entrypoints.cc index fe565430fe..a2a6e085c2 100644 --- a/runtime/entrypoints/quick/quick_dexcache_entrypoints.cc +++ b/runtime/entrypoints/quick/quick_dexcache_entrypoints.cc @@ -17,16 +17,16 @@ #include "art_method-inl.h" #include "base/callee_save_type.h" #include "callee_save_frame.h" -#include "entrypoints/entrypoint_utils-inl.h" #include "class_linker-inl.h" #include "class_table-inl.h" #include "dex_file-inl.h" #include "dex_file_types.h" +#include "entrypoints/entrypoint_utils-inl.h" #include "gc/heap.h" #include "mirror/class-inl.h" #include "mirror/class_loader.h" -#include "mirror/object_array-inl.h" #include "mirror/object-inl.h" +#include "mirror/object_array-inl.h" #include "oat_file.h" #include "runtime.h" diff --git a/runtime/entrypoints/quick/quick_entrypoints_enum.h b/runtime/entrypoints/quick/quick_entrypoints_enum.h index abf2c34744..1cf7f8daeb 100644 --- a/runtime/entrypoints/quick/quick_entrypoints_enum.h +++ b/runtime/entrypoints/quick/quick_entrypoints_enum.h @@ -24,8 +24,7 @@ namespace art { // Define an enum for the entrypoints. Names are prepended a 'kQuick'. -enum QuickEntrypointEnum -{ // NOLINT(whitespace/braces) +enum QuickEntrypointEnum { // NOLINT(whitespace/braces) #define ENTRYPOINT_ENUM(name, rettype, ...) kQuick ## name, #include "quick_entrypoints_list.h" QUICK_ENTRYPOINT_LIST(ENTRYPOINT_ENUM) @@ -58,7 +57,7 @@ void CheckEntrypointTypes(); #define ENTRYPOINT_ENUM(name, ...) \ template <> inline void CheckEntrypointTypes<kQuick ## name, __VA_ARGS__>() {}; // NOLINT [readability/braces] [4] #include "quick_entrypoints_list.h" - QUICK_ENTRYPOINT_LIST(ENTRYPOINT_ENUM) +QUICK_ENTRYPOINT_LIST(ENTRYPOINT_ENUM) #undef QUICK_ENTRYPOINT_LIST #undef ENTRYPOINT_ENUM diff --git a/runtime/entrypoints/quick/quick_fillarray_entrypoints.cc b/runtime/entrypoints/quick/quick_fillarray_entrypoints.cc index f63c9c2599..d4bc1c76b1 100644 --- a/runtime/entrypoints/quick/quick_fillarray_entrypoints.cc +++ b/runtime/entrypoints/quick/quick_fillarray_entrypoints.cc @@ -16,8 +16,8 @@ #include "art_method-inl.h" #include "callee_save_frame.h" -#include "mirror/array.h" #include "entrypoints/entrypoint_utils.h" +#include "mirror/array.h" namespace art { diff --git a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc index 36885d8a1f..e08319d509 100644 --- a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc +++ b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc @@ -27,8 +27,8 @@ #include "gc/accounting/card_table-inl.h" #include "imt_conflict_table.h" #include "imtable-inl.h" -#include "interpreter/interpreter.h" #include "instrumentation.h" +#include "interpreter/interpreter.h" #include "linear_alloc.h" #include "method_bss_mapping.h" #include "method_handles.h" @@ -1182,7 +1182,7 @@ extern "C" const void* artQuickResolutionTrampoline( HandleWrapper<mirror::Object> h_receiver( hs.NewHandleWrapper(virtual_or_interface ? &receiver : &dummy)); DCHECK_EQ(caller->GetDexFile(), called_method.dex_file); - called = linker->ResolveMethod<ClassLinker::kForceICCECheck>( + called = linker->ResolveMethod<ClassLinker::ResolveMode::kCheckICCEAndIAE>( self, called_method.dex_method_index, caller, invoke_type); // Update .bss entry in oat file if any. @@ -1235,8 +1235,11 @@ extern "C" const void* artQuickResolutionTrampoline( Handle<mirror::ClassLoader> class_loader( hs.NewHandle(caller->GetDeclaringClass()->GetClassLoader())); // TODO Maybe put this into a mirror::Class function. - mirror::Class* ref_class = linker->ResolveReferencedClassOfMethod( - called_method.dex_method_index, dex_cache, class_loader); + ObjPtr<mirror::Class> ref_class = linker->LookupResolvedType( + *dex_cache->GetDexFile(), + dex_cache->GetDexFile()->GetMethodId(called_method.dex_method_index).class_idx_, + dex_cache.Get(), + class_loader.Get()); if (ref_class->IsInterface()) { called = ref_class->FindVirtualMethodForInterfaceSuper(called, kRuntimePointerSize); } else { @@ -2458,6 +2461,21 @@ extern "C" TwoWordReturn artInvokeVirtualTrampolineWithAccessCheck( return artInvokeCommon<kVirtual, true>(method_idx, this_object, self, sp); } +// Helper function for art_quick_imt_conflict_trampoline to look up the interface method. +extern "C" ArtMethod* artLookupResolvedMethod(uint32_t method_index, ArtMethod* referrer) + REQUIRES_SHARED(Locks::mutator_lock_) { + ScopedAssertNoThreadSuspension ants(__FUNCTION__); + DCHECK(!referrer->IsProxyMethod()); + ArtMethod* result = Runtime::Current()->GetClassLinker()->LookupResolvedMethod( + method_index, referrer->GetDexCache(), referrer->GetClassLoader()); + DCHECK(result == nullptr || + result->GetDeclaringClass()->IsInterface() || + result->GetDeclaringClass() == + WellKnownClasses::ToClass(WellKnownClasses::java_lang_Object)) + << result->PrettyMethod(); + return result; +} + // Determine target of interface dispatch. The interface method and this object are known non-null. // The interface method is the method returned by the dex cache in the conflict trampoline. extern "C" TwoWordReturn artInvokeInterfaceTrampoline(ArtMethod* interface_method, @@ -2465,46 +2483,17 @@ extern "C" TwoWordReturn artInvokeInterfaceTrampoline(ArtMethod* interface_metho Thread* self, ArtMethod** sp) REQUIRES_SHARED(Locks::mutator_lock_) { - CHECK(interface_method != nullptr); - ObjPtr<mirror::Object> this_object(raw_this_object); ScopedQuickEntrypointChecks sqec(self); - StackHandleScope<1> hs(self); - Handle<mirror::Class> cls(hs.NewHandle(this_object->GetClass())); + StackHandleScope<2> hs(self); + Handle<mirror::Object> this_object = hs.NewHandle(raw_this_object); + Handle<mirror::Class> cls = hs.NewHandle(this_object->GetClass()); ArtMethod* caller_method = QuickArgumentVisitor::GetCallingMethod(sp); ArtMethod* method = nullptr; ImTable* imt = cls->GetImt(kRuntimePointerSize); - if (LIKELY(interface_method->GetDexMethodIndex() != DexFile::kDexNoIndex)) { - // If the interface method is already resolved, look whether we have a match in the - // ImtConflictTable. - ArtMethod* conflict_method = imt->Get(ImTable::GetImtIndex(interface_method), - kRuntimePointerSize); - if (LIKELY(conflict_method->IsRuntimeMethod())) { - ImtConflictTable* current_table = conflict_method->GetImtConflictTable(kRuntimePointerSize); - DCHECK(current_table != nullptr); - method = current_table->Lookup(interface_method, kRuntimePointerSize); - } else { - // It seems we aren't really a conflict method! - method = cls->FindVirtualMethodForInterface(interface_method, kRuntimePointerSize); - } - if (method != nullptr) { - return GetTwoWordSuccessValue( - reinterpret_cast<uintptr_t>(method->GetEntryPointFromQuickCompiledCode()), - reinterpret_cast<uintptr_t>(method)); - } - - // No match, use the IfTable. - method = cls->FindVirtualMethodForInterface(interface_method, kRuntimePointerSize); - if (UNLIKELY(method == nullptr)) { - ThrowIncompatibleClassChangeErrorClassForInterfaceDispatch( - interface_method, this_object, caller_method); - return GetTwoWordFailureValue(); // Failure. - } - } else { - // The interface method is unresolved, so look it up in the dex file of the caller. - DCHECK_EQ(interface_method, Runtime::Current()->GetResolutionMethod()); - + if (UNLIKELY(interface_method == nullptr)) { + // The interface method is unresolved, so resolve it in the dex file of the caller. // Fetch the dex_method_idx of the target interface method from the caller. uint32_t dex_method_idx; uint32_t dex_pc = QuickArgumentVisitor::GetCallingDexPc(sp); @@ -2522,50 +2511,74 @@ extern "C" TwoWordReturn artInvokeInterfaceTrampoline(ArtMethod* interface_metho dex_method_idx = instr->VRegB_3rc(); } - const DexFile* dex_file = caller_method->GetDeclaringClass()->GetDexCache() - ->GetDexFile(); + const DexFile& dex_file = caller_method->GetDeclaringClass()->GetDexFile(); uint32_t shorty_len; - const char* shorty = dex_file->GetMethodShorty(dex_file->GetMethodId(dex_method_idx), - &shorty_len); + const char* shorty = dex_file.GetMethodShorty(dex_file.GetMethodId(dex_method_idx), + &shorty_len); { - // Remember the args in case a GC happens in FindMethodFromCode. + // Remember the args in case a GC happens in ClassLinker::ResolveMethod(). ScopedObjectAccessUnchecked soa(self->GetJniEnv()); RememberForGcArgumentVisitor visitor(sp, false, shorty, shorty_len, &soa); visitor.VisitArguments(); - method = FindMethodFromCode<kInterface, false>(dex_method_idx, - &this_object, - caller_method, - self); + ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); + interface_method = class_linker->ResolveMethod<ClassLinker::ResolveMode::kNoChecks>( + self, dex_method_idx, caller_method, kInterface); visitor.FixupReferences(); } - if (UNLIKELY(method == nullptr)) { + if (UNLIKELY(interface_method == nullptr)) { CHECK(self->IsExceptionPending()); return GetTwoWordFailureValue(); // Failure. } - interface_method = - caller_method->GetDexCacheResolvedMethod(dex_method_idx, kRuntimePointerSize); - DCHECK(!interface_method->IsRuntimeMethod()); } - // We arrive here if we have found an implementation, and it is not in the ImtConflictTable. - // We create a new table with the new pair { interface_method, method }. + DCHECK(!interface_method->IsRuntimeMethod()); + // Look whether we have a match in the ImtConflictTable. uint32_t imt_index = ImTable::GetImtIndex(interface_method); ArtMethod* conflict_method = imt->Get(imt_index, kRuntimePointerSize); - if (conflict_method->IsRuntimeMethod()) { - ArtMethod* new_conflict_method = Runtime::Current()->GetClassLinker()->AddMethodToConflictTable( - cls.Get(), - conflict_method, - interface_method, - method, - /*force_new_conflict_method*/false); - if (new_conflict_method != conflict_method) { - // Update the IMT if we create a new conflict method. No fence needed here, as the - // data is consistent. - imt->Set(imt_index, - new_conflict_method, - kRuntimePointerSize); + if (LIKELY(conflict_method->IsRuntimeMethod())) { + ImtConflictTable* current_table = conflict_method->GetImtConflictTable(kRuntimePointerSize); + DCHECK(current_table != nullptr); + method = current_table->Lookup(interface_method, kRuntimePointerSize); + } else { + // It seems we aren't really a conflict method! + if (kIsDebugBuild) { + ArtMethod* m = cls->FindVirtualMethodForInterface(interface_method, kRuntimePointerSize); + CHECK_EQ(conflict_method, m) + << interface_method->PrettyMethod() << " / " << conflict_method->PrettyMethod() << " / " + << " / " << ArtMethod::PrettyMethod(m) << " / " << cls->PrettyClass(); } + method = conflict_method; + } + if (method != nullptr) { + return GetTwoWordSuccessValue( + reinterpret_cast<uintptr_t>(method->GetEntryPointFromQuickCompiledCode()), + reinterpret_cast<uintptr_t>(method)); + } + + // No match, use the IfTable. + method = cls->FindVirtualMethodForInterface(interface_method, kRuntimePointerSize); + if (UNLIKELY(method == nullptr)) { + ThrowIncompatibleClassChangeErrorClassForInterfaceDispatch( + interface_method, this_object.Get(), caller_method); + return GetTwoWordFailureValue(); // Failure. + } + + // We arrive here if we have found an implementation, and it is not in the ImtConflictTable. + // We create a new table with the new pair { interface_method, method }. + DCHECK(conflict_method->IsRuntimeMethod()); + ArtMethod* new_conflict_method = Runtime::Current()->GetClassLinker()->AddMethodToConflictTable( + cls.Get(), + conflict_method, + interface_method, + method, + /*force_new_conflict_method*/false); + if (new_conflict_method != conflict_method) { + // Update the IMT if we create a new conflict method. No fence needed here, as the + // data is consistent. + imt->Set(imt_index, + new_conflict_method, + kRuntimePointerSize); } const void* code = method->GetEntryPointFromQuickCompiledCode(); @@ -2622,10 +2635,8 @@ extern "C" uintptr_t artInvokePolymorphic( // Resolve method - it's either MethodHandle.invoke() or MethodHandle.invokeExact(). ClassLinker* linker = Runtime::Current()->GetClassLinker(); - ArtMethod* resolved_method = linker->ResolveMethod<ClassLinker::kForceICCECheck>(self, - inst->VRegB(), - caller_method, - kVirtual); + ArtMethod* resolved_method = linker->ResolveMethod<ClassLinker::ResolveMode::kCheckICCEAndIAE>( + self, inst->VRegB(), caller_method, kVirtual); DCHECK((resolved_method == jni::DecodeArtMethod(WellKnownClasses::java_lang_invoke_MethodHandle_invokeExact)) || (resolved_method == diff --git a/runtime/entrypoints_order_test.cc b/runtime/entrypoints_order_test.cc index a3c3981b42..281dfd9725 100644 --- a/runtime/entrypoints_order_test.cc +++ b/runtime/entrypoints_order_test.cc @@ -14,9 +14,10 @@ * limitations under the License. */ -#include <memory> #include <setjmp.h> +#include <memory> + #include "base/macros.h" #include "common_runtime_test.h" #include "thread.h" diff --git a/runtime/fault_handler.h b/runtime/fault_handler.h index d56cf17861..3e2664c7f9 100644 --- a/runtime/fault_handler.h +++ b/runtime/fault_handler.h @@ -18,12 +18,13 @@ #ifndef ART_RUNTIME_FAULT_HANDLER_H_ #define ART_RUNTIME_FAULT_HANDLER_H_ -#include <signal.h> -#include <vector> #include <setjmp.h> +#include <signal.h> #include <stdint.h> -#include "base/mutex.h" // For annotalysis. +#include <vector> + +#include "base/mutex.h" // For annotalysis. namespace art { diff --git a/runtime/gc/accounting/mod_union_table.h b/runtime/gc/accounting/mod_union_table.h index 9e261fd8b5..ee25eae93a 100644 --- a/runtime/gc/accounting/mod_union_table.h +++ b/runtime/gc/accounting/mod_union_table.h @@ -17,8 +17,8 @@ #ifndef ART_RUNTIME_GC_ACCOUNTING_MOD_UNION_TABLE_H_ #define ART_RUNTIME_GC_ACCOUNTING_MOD_UNION_TABLE_H_ -#include "bitmap.h" #include "base/allocator.h" +#include "bitmap.h" #include "card_table.h" #include "globals.h" #include "mirror/object_reference.h" diff --git a/runtime/gc/accounting/remembered_set.cc b/runtime/gc/accounting/remembered_set.cc index f2fe58aa83..9dea2f80d1 100644 --- a/runtime/gc/accounting/remembered_set.cc +++ b/runtime/gc/accounting/remembered_set.cc @@ -20,12 +20,12 @@ #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/collector/mark_sweep.h" #include "gc/collector/semi_space.h" #include "gc/heap.h" #include "gc/space/space.h" +#include "heap_bitmap.h" #include "mirror/class-inl.h" #include "mirror/object-inl.h" #include "mirror/object-refvisitor-inl.h" diff --git a/runtime/gc/accounting/space_bitmap.cc b/runtime/gc/accounting/space_bitmap.cc index 317e2fc591..92b4360123 100644 --- a/runtime/gc/accounting/space_bitmap.cc +++ b/runtime/gc/accounting/space_bitmap.cc @@ -21,8 +21,8 @@ #include "art_field-inl.h" #include "dex_file-inl.h" #include "mem_map.h" -#include "mirror/object-inl.h" #include "mirror/class-inl.h" +#include "mirror/object-inl.h" #include "mirror/object_array.h" namespace art { diff --git a/runtime/gc/accounting/space_bitmap_test.cc b/runtime/gc/accounting/space_bitmap_test.cc index 8c06cfd640..bd5f77ebb1 100644 --- a/runtime/gc/accounting/space_bitmap_test.cc +++ b/runtime/gc/accounting/space_bitmap_test.cc @@ -19,6 +19,7 @@ #include <stdint.h> #include <memory> +#include "base/mutex.h" #include "common_runtime_test.h" #include "globals.h" #include "space_bitmap-inl.h" @@ -145,22 +146,21 @@ class RandGen { explicit RandGen(uint32_t seed) : val_(seed) {} uint32_t next() { - val_ = val_ * 48271 % 2147483647; + val_ = val_ * 48271 % 2147483647 + 13; return val_; } uint32_t val_; }; -template <size_t kAlignment> -void RunTest() NO_THREAD_SAFETY_ANALYSIS { +template <size_t kAlignment, typename TestFn> +static void RunTest(TestFn&& fn) NO_THREAD_SAFETY_ANALYSIS { uint8_t* heap_begin = reinterpret_cast<uint8_t*>(0x10000000); size_t heap_capacity = 16 * MB; // Seed with 0x1234 for reproducability. RandGen r(0x1234); - for (int i = 0; i < 5 ; ++i) { std::unique_ptr<ContinuousSpaceBitmap> space_bitmap( ContinuousSpaceBitmap::Create("test bitmap", heap_begin, heap_capacity)); @@ -177,15 +177,9 @@ void RunTest() NO_THREAD_SAFETY_ANALYSIS { } for (int j = 0; j < 50; ++j) { - size_t count = 0; - SimpleCounter c(&count); - - size_t offset = RoundDown(r.next() % heap_capacity, kAlignment); - size_t remain = heap_capacity - offset; - size_t end = offset + RoundDown(r.next() % (remain + 1), kAlignment); - - space_bitmap->VisitMarkedRange(reinterpret_cast<uintptr_t>(heap_begin) + offset, - reinterpret_cast<uintptr_t>(heap_begin) + end, c); + const size_t offset = RoundDown(r.next() % heap_capacity, kAlignment); + const size_t remain = heap_capacity - offset; + const size_t end = offset + RoundDown(r.next() % (remain + 1), kAlignment); size_t manual = 0; for (uintptr_t k = offset; k < end; k += kAlignment) { @@ -194,17 +188,73 @@ void RunTest() NO_THREAD_SAFETY_ANALYSIS { } } - EXPECT_EQ(count, manual); + uintptr_t range_begin = reinterpret_cast<uintptr_t>(heap_begin) + offset; + uintptr_t range_end = reinterpret_cast<uintptr_t>(heap_begin) + end; + + fn(space_bitmap.get(), range_begin, range_end, manual); } } } +template <size_t kAlignment> +static void RunTestCount() { + auto count_test_fn = [](ContinuousSpaceBitmap* space_bitmap, + uintptr_t range_begin, + uintptr_t range_end, + size_t manual_count) { + size_t count = 0; + auto count_fn = [&count](mirror::Object* obj ATTRIBUTE_UNUSED) { + count++; + }; + space_bitmap->VisitMarkedRange(range_begin, range_end, count_fn); + EXPECT_EQ(count, manual_count); + }; + RunTest<kAlignment>(count_test_fn); +} + TEST_F(SpaceBitmapTest, VisitorObjectAlignment) { - RunTest<kObjectAlignment>(); + RunTestCount<kObjectAlignment>(); } TEST_F(SpaceBitmapTest, VisitorPageAlignment) { - RunTest<kPageSize>(); + RunTestCount<kPageSize>(); +} + +template <size_t kAlignment> +void RunTestOrder() { + auto order_test_fn = [](ContinuousSpaceBitmap* space_bitmap, + uintptr_t range_begin, + uintptr_t range_end, + size_t manual_count) + REQUIRES_SHARED(Locks::heap_bitmap_lock_, Locks::mutator_lock_) { + mirror::Object* last_ptr = nullptr; + auto order_check = [&last_ptr](mirror::Object* obj) { + EXPECT_LT(last_ptr, obj); + last_ptr = obj; + }; + + // Test complete walk. + space_bitmap->Walk(order_check); + if (manual_count > 0) { + EXPECT_NE(nullptr, last_ptr); + } + + // Test range. + last_ptr = nullptr; + space_bitmap->VisitMarkedRange(range_begin, range_end, order_check); + if (manual_count > 0) { + EXPECT_NE(nullptr, last_ptr); + } + }; + RunTest<kAlignment>(order_test_fn); +} + +TEST_F(SpaceBitmapTest, OrderObjectAlignment) { + RunTestOrder<kObjectAlignment>(); +} + +TEST_F(SpaceBitmapTest, OrderPageAlignment) { + RunTestOrder<kPageSize>(); } } // namespace accounting diff --git a/runtime/gc/allocation_listener.h b/runtime/gc/allocation_listener.h index 21fa2142df..0be9aecae4 100644 --- a/runtime/gc/allocation_listener.h +++ b/runtime/gc/allocation_listener.h @@ -22,8 +22,8 @@ #include "base/macros.h" #include "base/mutex.h" -#include "obj_ptr.h" #include "gc_root.h" +#include "obj_ptr.h" namespace art { diff --git a/runtime/gc/allocation_record.h b/runtime/gc/allocation_record.h index d31e442cc9..fcd08c1b7b 100644 --- a/runtime/gc/allocation_record.h +++ b/runtime/gc/allocation_record.h @@ -21,8 +21,8 @@ #include <memory> #include "base/mutex.h" -#include "obj_ptr.h" #include "gc_root.h" +#include "obj_ptr.h" namespace art { diff --git a/runtime/gc/allocator/dlmalloc.cc b/runtime/gc/allocator/dlmalloc.cc index 0c84224fcb..ef916f8745 100644 --- a/runtime/gc/allocator/dlmalloc.cc +++ b/runtime/gc/allocator/dlmalloc.cc @@ -55,9 +55,10 @@ static void art_heap_usage_error(const char* function, void* p) { << " not expected"; } +#include <sys/mman.h> + #include "globals.h" #include "utils.h" -#include <sys/mman.h> extern "C" void DlmallocMadviseCallback(void* start, void* end, size_t used_bytes, void* arg) { // Is this chunk in use? diff --git a/runtime/gc/allocator/rosalloc.cc b/runtime/gc/allocator/rosalloc.cc index d5d3540b1f..b742ac4a7c 100644 --- a/runtime/gc/allocator/rosalloc.cc +++ b/runtime/gc/allocator/rosalloc.cc @@ -16,8 +16,8 @@ #include "rosalloc.h" -#include <map> #include <list> +#include <map> #include <sstream> #include <vector> @@ -28,8 +28,8 @@ #include "gc/space/memory_tool_settings.h" #include "mem_map.h" #include "mirror/class-inl.h" -#include "mirror/object.h" #include "mirror/object-inl.h" +#include "mirror/object.h" #include "thread-current-inl.h" #include "thread_list.h" diff --git a/runtime/gc/allocator/rosalloc.h b/runtime/gc/allocator/rosalloc.h index b85d7dff5c..2c90773b8f 100644 --- a/runtime/gc/allocator/rosalloc.h +++ b/runtime/gc/allocator/rosalloc.h @@ -28,8 +28,8 @@ #include "base/allocator.h" #include "base/bit_utils.h" -#include "base/mutex.h" #include "base/logging.h" +#include "base/mutex.h" #include "globals.h" #include "thread.h" diff --git a/runtime/gc/collector/concurrent_copying.h b/runtime/gc/collector/concurrent_copying.h index ab609906bf..cc7072d9da 100644 --- a/runtime/gc/collector/concurrent_copying.h +++ b/runtime/gc/collector/concurrent_copying.h @@ -21,8 +21,8 @@ #include "garbage_collector.h" #include "immune_spaces.h" #include "jni.h" -#include "offsets.h" #include "mirror/object_reference.h" +#include "offsets.h" #include "safe_map.h" #include <unordered_map> diff --git a/runtime/gc/collector/immune_spaces.cc b/runtime/gc/collector/immune_spaces.cc index 1e5f28382b..1024050409 100644 --- a/runtime/gc/collector/immune_spaces.cc +++ b/runtime/gc/collector/immune_spaces.cc @@ -16,8 +16,8 @@ #include "immune_spaces.h" -#include <vector> #include <tuple> +#include <vector> #include "gc/space/space-inl.h" #include "mirror/object.h" diff --git a/runtime/gc/collector/iteration.h b/runtime/gc/collector/iteration.h index fbe41664f7..363459a7f0 100644 --- a/runtime/gc/collector/iteration.h +++ b/runtime/gc/collector/iteration.h @@ -22,6 +22,7 @@ #include "android-base/macros.h" #include "base/timing_logger.h" +#include "gc/gc_cause.h" #include "object_byte_pair.h" namespace art { diff --git a/runtime/gc/collector/mark_compact.h b/runtime/gc/collector/mark_compact.h index 0bf4095ac3..7d64a0c64a 100644 --- a/runtime/gc/collector/mark_compact.h +++ b/runtime/gc/collector/mark_compact.h @@ -24,8 +24,8 @@ #include "base/macros.h" #include "base/mutex.h" #include "garbage_collector.h" -#include "gc_root.h" #include "gc/accounting/heap_bitmap.h" +#include "gc_root.h" #include "immune_spaces.h" #include "lock_word.h" #include "offsets.h" diff --git a/runtime/gc/collector/mark_sweep.cc b/runtime/gc/collector/mark_sweep.cc index fb82b4d270..34de83a240 100644 --- a/runtime/gc/collector/mark_sweep.cc +++ b/runtime/gc/collector/mark_sweep.cc @@ -17,9 +17,9 @@ #include "mark_sweep.h" #include <atomic> +#include <climits> #include <functional> #include <numeric> -#include <climits> #include <vector> #include "base/bounded_fifo.h" diff --git a/runtime/gc/collector/mark_sweep.h b/runtime/gc/collector/mark_sweep.h index b9e06f9688..53b899e09e 100644 --- a/runtime/gc/collector/mark_sweep.h +++ b/runtime/gc/collector/mark_sweep.h @@ -24,8 +24,8 @@ #include "base/macros.h" #include "base/mutex.h" #include "garbage_collector.h" -#include "gc_root.h" #include "gc/accounting/heap_bitmap.h" +#include "gc_root.h" #include "immune_spaces.h" #include "offsets.h" diff --git a/runtime/gc/collector/semi_space.cc b/runtime/gc/collector/semi_space.cc index d3798924ee..9fb37b6138 100644 --- a/runtime/gc/collector/semi_space.cc +++ b/runtime/gc/collector/semi_space.cc @@ -32,8 +32,8 @@ #include "gc/accounting/space_bitmap-inl.h" #include "gc/heap.h" #include "gc/reference_processor.h" -#include "gc/space/bump_pointer_space.h" #include "gc/space/bump_pointer_space-inl.h" +#include "gc/space/bump_pointer_space.h" #include "gc/space/image_space.h" #include "gc/space/large_object_space.h" #include "gc/space/space-inl.h" @@ -41,10 +41,10 @@ #include "intern_table.h" #include "jni_internal.h" #include "mark_sweep-inl.h" -#include "monitor.h" -#include "mirror/reference-inl.h" #include "mirror/object-inl.h" #include "mirror/object-refvisitor-inl.h" +#include "mirror/reference-inl.h" +#include "monitor.h" #include "runtime.h" #include "thread-inl.h" #include "thread_list.h" diff --git a/runtime/gc/collector/semi_space.h b/runtime/gc/collector/semi_space.h index d3858baaf5..fd52da3947 100644 --- a/runtime/gc/collector/semi_space.h +++ b/runtime/gc/collector/semi_space.h @@ -23,8 +23,8 @@ #include "base/macros.h" #include "base/mutex.h" #include "garbage_collector.h" -#include "gc_root.h" #include "gc/accounting/heap_bitmap.h" +#include "gc_root.h" #include "immune_spaces.h" #include "mirror/object_reference.h" #include "offsets.h" diff --git a/runtime/gc/gc_cause.cc b/runtime/gc/gc_cause.cc index a3a2051934..871208037a 100644 --- a/runtime/gc/gc_cause.cc +++ b/runtime/gc/gc_cause.cc @@ -15,8 +15,8 @@ */ #include "gc_cause.h" -#include "globals.h" #include "base/logging.h" +#include "globals.h" #include <ostream> diff --git a/runtime/gc/heap-inl.h b/runtime/gc/heap-inl.h index bf5cf29f13..2047646413 100644 --- a/runtime/gc/heap-inl.h +++ b/runtime/gc/heap-inl.h @@ -30,9 +30,9 @@ #include "gc/space/large_object_space.h" #include "gc/space/region_space-inl.h" #include "gc/space/rosalloc_space-inl.h" +#include "handle_scope-inl.h" #include "obj_ptr-inl.h" #include "runtime.h" -#include "handle_scope-inl.h" #include "thread-inl.h" #include "utils.h" #include "verify_object.h" diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc index 6ab98273ce..f1685b252c 100644 --- a/runtime/gc/heap.cc +++ b/runtime/gc/heap.cc @@ -37,6 +37,7 @@ #include "cutils/sched_policy.h" #include "debugger.h" #include "dex_file-inl.h" +#include "entrypoints/quick/quick_alloc_entrypoints.h" #include "gc/accounting/card_table-inl.h" #include "gc/accounting/heap_bitmap-inl.h" #include "gc/accounting/mod_union_table-inl.h" @@ -61,9 +62,9 @@ #include "gc/space/zygote_space.h" #include "gc/task_processor.h" #include "gc/verification.h" -#include "entrypoints/quick/quick_alloc_entrypoints.h" #include "gc_pause_listener.h" #include "gc_root.h" +#include "handle_scope-inl.h" #include "heap-inl.h" #include "heap-visit-objects-inl.h" #include "image.h" @@ -71,18 +72,17 @@ #include "java_vm_ext.h" #include "jit/jit.h" #include "jit/jit_code_cache.h" -#include "obj_ptr-inl.h" #include "mirror/class-inl.h" #include "mirror/object-inl.h" #include "mirror/object-refvisitor-inl.h" #include "mirror/object_array-inl.h" #include "mirror/reference-inl.h" +#include "nativehelper/ScopedLocalRef.h" +#include "obj_ptr-inl.h" #include "os.h" #include "reflection.h" #include "runtime.h" -#include "ScopedLocalRef.h" #include "scoped_thread_state_change-inl.h" -#include "handle_scope-inl.h" #include "thread_list.h" #include "verify_object-inl.h" #include "well_known_classes.h" diff --git a/runtime/gc/heap.h b/runtime/gc/heap.h index e172d2d825..1534fd69d1 100644 --- a/runtime/gc/heap.h +++ b/runtime/gc/heap.h @@ -27,10 +27,10 @@ #include "atomic.h" #include "base/mutex.h" #include "base/time_utils.h" -#include "gc/gc_cause.h" #include "gc/collector/gc_type.h" #include "gc/collector/iteration.h" #include "gc/collector_type.h" +#include "gc/gc_cause.h" #include "gc/space/large_object_space.h" #include "globals.h" #include "handle.h" diff --git a/runtime/gc/reference_processor.cc b/runtime/gc/reference_processor.cc index 52da7632f0..42b31ab140 100644 --- a/runtime/gc/reference_processor.cc +++ b/runtime/gc/reference_processor.cc @@ -22,10 +22,10 @@ #include "mirror/class-inl.h" #include "mirror/object-inl.h" #include "mirror/reference-inl.h" +#include "nativehelper/ScopedLocalRef.h" #include "object_callbacks.h" #include "reference_processor-inl.h" #include "reflection.h" -#include "ScopedLocalRef.h" #include "scoped_thread_state_change-inl.h" #include "task_processor.h" #include "utils.h" diff --git a/runtime/gc/reference_queue_test.cc b/runtime/gc/reference_queue_test.cc index 613b034f59..ce0807c0c6 100644 --- a/runtime/gc/reference_queue_test.cc +++ b/runtime/gc/reference_queue_test.cc @@ -17,9 +17,9 @@ #include <sstream> #include "common_runtime_test.h" -#include "reference_queue.h" #include "handle_scope-inl.h" #include "mirror/class-inl.h" +#include "reference_queue.h" #include "scoped_thread_state_change-inl.h" namespace art { diff --git a/runtime/gc/space/bump_pointer_space.cc b/runtime/gc/space/bump_pointer_space.cc index 5d91f4bf8e..ce0e0f3630 100644 --- a/runtime/gc/space/bump_pointer_space.cc +++ b/runtime/gc/space/bump_pointer_space.cc @@ -16,8 +16,8 @@ #include "bump_pointer_space.h" #include "bump_pointer_space-inl.h" -#include "mirror/object-inl.h" #include "mirror/class-inl.h" +#include "mirror/object-inl.h" #include "thread_list.h" namespace art { diff --git a/runtime/gc/space/image_space.cc b/runtime/gc/space/image_space.cc index 3ae382e36f..148438296f 100644 --- a/runtime/gc/space/image_space.cc +++ b/runtime/gc/space/image_space.cc @@ -17,11 +17,12 @@ #include "image_space.h" #include <lz4.h> -#include <random> #include <sys/statvfs.h> #include <sys/types.h> #include <unistd.h> +#include <random> + #include "android-base/stringprintf.h" #include "android-base/strings.h" @@ -30,8 +31,8 @@ #include "base/callee_save_type.h" #include "base/enums.h" #include "base/macros.h" -#include "base/stl_util.h" #include "base/scoped_flock.h" +#include "base/stl_util.h" #include "base/systrace.h" #include "base/time_utils.h" #include "exec_utils.h" @@ -1268,17 +1269,19 @@ class ImageSpaceLoader { } dex_cache->FixupResolvedTypes<kWithoutReadBarrier>(new_types, fixup_adapter); } - ArtMethod** methods = dex_cache->GetResolvedMethods(); + mirror::MethodDexCacheType* methods = dex_cache->GetResolvedMethods(); if (methods != nullptr) { - ArtMethod** new_methods = fixup_adapter.ForwardObject(methods); + mirror::MethodDexCacheType* new_methods = fixup_adapter.ForwardObject(methods); if (methods != new_methods) { dex_cache->SetResolvedMethods(new_methods); } for (size_t j = 0, num = dex_cache->NumResolvedMethods(); j != num; ++j) { - ArtMethod* orig = mirror::DexCache::GetElementPtrSize(new_methods, j, pointer_size); + auto pair = mirror::DexCache::GetNativePairPtrSize(new_methods, j, pointer_size); + ArtMethod* orig = pair.object; ArtMethod* copy = fixup_adapter.ForwardObject(orig); if (orig != copy) { - mirror::DexCache::SetElementPtrSize(new_methods, j, copy, pointer_size); + pair.object = copy; + mirror::DexCache::SetNativePairPtrSize(new_methods, j, pair, pointer_size); } } } diff --git a/runtime/gc/space/large_object_space_test.cc b/runtime/gc/space/large_object_space_test.cc index 2544914a95..79b775a510 100644 --- a/runtime/gc/space/large_object_space_test.cc +++ b/runtime/gc/space/large_object_space_test.cc @@ -14,9 +14,10 @@ * limitations under the License. */ +#include "large_object_space.h" + #include "base/time_utils.h" #include "space_test.h" -#include "large_object_space.h" namespace art { namespace gc { diff --git a/runtime/gc/space/malloc_space.cc b/runtime/gc/space/malloc_space.cc index 1154620bb7..c2a8de3aec 100644 --- a/runtime/gc/space/malloc_space.cc +++ b/runtime/gc/space/malloc_space.cc @@ -23,10 +23,10 @@ #include "gc/heap.h" #include "gc/space/space-inl.h" #include "gc/space/zygote_space.h" +#include "handle_scope-inl.h" #include "mirror/class-inl.h" #include "mirror/object-inl.h" #include "runtime.h" -#include "handle_scope-inl.h" #include "thread.h" #include "thread_list.h" #include "utils.h" diff --git a/runtime/gc/space/memory_tool_malloc_space-inl.h b/runtime/gc/space/memory_tool_malloc_space-inl.h index 6cb2465539..8282f3dda7 100644 --- a/runtime/gc/space/memory_tool_malloc_space-inl.h +++ b/runtime/gc/space/memory_tool_malloc_space-inl.h @@ -17,9 +17,11 @@ #ifndef ART_RUNTIME_GC_SPACE_MEMORY_TOOL_MALLOC_SPACE_INL_H_ #define ART_RUNTIME_GC_SPACE_MEMORY_TOOL_MALLOC_SPACE_INL_H_ -#include "base/memory_tool.h" #include "memory_tool_malloc_space.h" + +#include "base/memory_tool.h" #include "memory_tool_settings.h" +#include "mirror/object-inl.h" namespace art { namespace gc { diff --git a/runtime/gc/space/region_space.cc b/runtime/gc/space/region_space.cc index fe3c1c022c..b2e1fa5269 100644 --- a/runtime/gc/space/region_space.cc +++ b/runtime/gc/space/region_space.cc @@ -14,11 +14,11 @@ * limitations under the License. */ -#include "bump_pointer_space.h" #include "bump_pointer_space-inl.h" +#include "bump_pointer_space.h" #include "gc/accounting/read_barrier_table.h" -#include "mirror/object-inl.h" #include "mirror/class-inl.h" +#include "mirror/object-inl.h" #include "thread_list.h" namespace art { diff --git a/runtime/gc/space/rosalloc_space.cc b/runtime/gc/space/rosalloc_space.cc index 9e900e4558..eca0e43a97 100644 --- a/runtime/gc/space/rosalloc_space.cc +++ b/runtime/gc/space/rosalloc_space.cc @@ -21,6 +21,7 @@ #include "gc/accounting/card_table.h" #include "gc/accounting/space_bitmap-inl.h" #include "gc/heap.h" +#include "memory_tool_malloc_space-inl.h" #include "mirror/class-inl.h" #include "mirror/object-inl.h" #include "runtime.h" @@ -28,7 +29,6 @@ #include "thread.h" #include "thread_list.h" #include "utils.h" -#include "memory_tool_malloc_space-inl.h" namespace art { namespace gc { diff --git a/runtime/gc/task_processor_test.cc b/runtime/gc/task_processor_test.cc index 5a75b37b67..77b40e4593 100644 --- a/runtime/gc/task_processor_test.cc +++ b/runtime/gc/task_processor_test.cc @@ -14,11 +14,11 @@ * limitations under the License. */ +#include "task_processor.h" #include "base/time_utils.h" #include "common_runtime_test.h" -#include "task_processor.h" -#include "thread_pool.h" #include "thread-current-inl.h" +#include "thread_pool.h" namespace art { namespace gc { diff --git a/runtime/generated/asm_support_gen.h b/runtime/generated/asm_support_gen.h index 06e4704c1c..11b3abb6ed 100644 --- a/runtime/generated/asm_support_gen.h +++ b/runtime/generated/asm_support_gen.h @@ -78,6 +78,10 @@ DEFINE_CHECK_EQ(static_cast<int32_t>(STRING_DEX_CACHE_SIZE_MINUS_ONE), (static_c DEFINE_CHECK_EQ(static_cast<int32_t>(STRING_DEX_CACHE_HASH_BITS), (static_cast<int32_t>(art::LeastSignificantBit(art::mirror::DexCache::kDexCacheStringCacheSize)))) #define STRING_DEX_CACHE_ELEMENT_SIZE 8 DEFINE_CHECK_EQ(static_cast<int32_t>(STRING_DEX_CACHE_ELEMENT_SIZE), (static_cast<int32_t>(sizeof(art::mirror::StringDexCachePair)))) +#define METHOD_DEX_CACHE_SIZE_MINUS_ONE 1023 +DEFINE_CHECK_EQ(static_cast<int32_t>(METHOD_DEX_CACHE_SIZE_MINUS_ONE), (static_cast<int32_t>(art::mirror::DexCache::kDexCacheMethodCacheSize - 1))) +#define METHOD_DEX_CACHE_HASH_BITS 10 +DEFINE_CHECK_EQ(static_cast<int32_t>(METHOD_DEX_CACHE_HASH_BITS), (static_cast<int32_t>(art::LeastSignificantBit(art::mirror::DexCache::kDexCacheMethodCacheSize)))) #define CARD_TABLE_CARD_SHIFT 0xa DEFINE_CHECK_EQ(static_cast<size_t>(CARD_TABLE_CARD_SHIFT), (static_cast<size_t>(art::gc::accounting::CardTable::kCardShift))) #define MIN_LARGE_OBJECT_THRESHOLD 0x3000 @@ -110,6 +114,8 @@ DEFINE_CHECK_EQ(static_cast<int32_t>(LOCK_WORD_GC_STATE_SHIFT), (static_cast<int DEFINE_CHECK_EQ(static_cast<int32_t>(LOCK_WORD_MARK_BIT_SHIFT), (static_cast<int32_t>(art::LockWord::kMarkBitStateShift))) #define LOCK_WORD_MARK_BIT_MASK_SHIFTED 0x20000000 DEFINE_CHECK_EQ(static_cast<uint32_t>(LOCK_WORD_MARK_BIT_MASK_SHIFTED), (static_cast<uint32_t>(art::LockWord::kMarkBitStateMaskShifted))) +#define STD_MEMORY_ORDER_RELAXED 0 +DEFINE_CHECK_EQ(static_cast<int32_t>(STD_MEMORY_ORDER_RELAXED), (static_cast<int32_t>(std::memory_order_relaxed))) #define OBJECT_ALIGNMENT_MASK 0x7 DEFINE_CHECK_EQ(static_cast<size_t>(OBJECT_ALIGNMENT_MASK), (static_cast<size_t>(art::kObjectAlignment - 1))) #define OBJECT_ALIGNMENT_MASK_TOGGLED 0xfffffff8 diff --git a/runtime/hprof/hprof.cc b/runtime/hprof/hprof.cc index f428bc2751..7976a1a9a1 100644 --- a/runtime/hprof/hprof.cc +++ b/runtime/hprof/hprof.cc @@ -32,8 +32,8 @@ #include <sys/time.h> #include <sys/uio.h> #include <time.h> -#include <time.h> #include <unistd.h> + #include <set> #include "android-base/stringprintf.h" @@ -47,18 +47,18 @@ #include "common_throws.h" #include "debugger.h" #include "dex_file-inl.h" -#include "gc_root.h" #include "gc/accounting/heap_bitmap.h" #include "gc/allocation_record.h" -#include "gc/scoped_gc_critical_section.h" -#include "gc/heap.h" #include "gc/heap-visit-objects-inl.h" +#include "gc/heap.h" +#include "gc/scoped_gc_critical_section.h" #include "gc/space/space.h" +#include "gc_root.h" #include "globals.h" #include "jdwp/jdwp.h" #include "jdwp/jdwp_priv.h" -#include "mirror/class.h" #include "mirror/class-inl.h" +#include "mirror/class.h" #include "mirror/object-refvisitor-inl.h" #include "os.h" #include "safe_map.h" diff --git a/runtime/image.cc b/runtime/image.cc index ac36d7ca20..950ac5dcbf 100644 --- a/runtime/image.cc +++ b/runtime/image.cc @@ -18,15 +18,15 @@ #include "base/bit_utils.h" #include "base/length_prefixed_array.h" -#include "mirror/object_array.h" -#include "mirror/object_array-inl.h" #include "mirror/object-inl.h" +#include "mirror/object_array-inl.h" +#include "mirror/object_array.h" #include "utils.h" namespace art { const uint8_t ImageHeader::kImageMagic[] = { 'a', 'r', 't', '\n' }; -const uint8_t ImageHeader::kImageVersion[] = { '0', '4', '5', '\0' }; // Fix DexCache fields. +const uint8_t ImageHeader::kImageVersion[] = { '0', '4', '6', '\0' }; // Hash-based methods array. ImageHeader::ImageHeader(uint32_t image_begin, uint32_t image_size, diff --git a/runtime/imtable_test.cc b/runtime/imtable_test.cc index d482183d86..d662114912 100644 --- a/runtime/imtable_test.cc +++ b/runtime/imtable_test.cc @@ -24,10 +24,10 @@ #include "base/mutex.h" #include "class_linker.h" #include "common_runtime_test.h" +#include "handle_scope-inl.h" #include "mirror/accessible_object.h" #include "mirror/class.h" #include "mirror/class_loader.h" -#include "handle_scope-inl.h" #include "scoped_thread_state_change-inl.h" #include "thread-current-inl.h" diff --git a/runtime/indenter.h b/runtime/indenter.h index 78b18f63ab..cc6d4c4e96 100644 --- a/runtime/indenter.h +++ b/runtime/indenter.h @@ -17,11 +17,12 @@ #ifndef ART_RUNTIME_INDENTER_H_ #define ART_RUNTIME_INDENTER_H_ -#include "base/logging.h" -#include "base/macros.h" #include <ostream> #include <streambuf> +#include "base/logging.h" +#include "base/macros.h" + namespace art { constexpr char kIndentChar =' '; diff --git a/runtime/indenter_test.cc b/runtime/indenter_test.cc index 1a26d7b68e..09c0c54e5a 100644 --- a/runtime/indenter_test.cc +++ b/runtime/indenter_test.cc @@ -14,9 +14,10 @@ * limitations under the License. */ -#include "gtest/gtest.h" #include "indenter.h" +#include "gtest/gtest.h" + namespace art { TEST(IndenterTest, MultiLineTest) { diff --git a/runtime/instrumentation.cc b/runtime/instrumentation.cc index 8120cc484e..a8cf59b326 100644 --- a/runtime/instrumentation.cc +++ b/runtime/instrumentation.cc @@ -19,15 +19,15 @@ #include <sstream> #include "arch/context.h" -#include "art_method-inl.h" #include "art_field-inl.h" +#include "art_method-inl.h" #include "atomic.h" #include "base/callee_save_type.h" #include "class_linker.h" #include "debugger.h" #include "dex_file-inl.h" -#include "entrypoints/quick/quick_entrypoints.h" #include "entrypoints/quick/quick_alloc_entrypoints.h" +#include "entrypoints/quick/quick_entrypoints.h" #include "entrypoints/runtime_asm_entrypoints.h" #include "gc_root-inl.h" #include "interpreter/interpreter.h" @@ -36,8 +36,8 @@ #include "jvalue-inl.h" #include "mirror/class-inl.h" #include "mirror/dex_cache.h" -#include "mirror/object_array-inl.h" #include "mirror/object-inl.h" +#include "mirror/object_array-inl.h" #include "nth_caller_visitor.h" #include "oat_quick_method_header.h" #include "thread.h" diff --git a/runtime/instrumentation_test.cc b/runtime/instrumentation_test.cc index 2a601c9cf2..d25655f15a 100644 --- a/runtime/instrumentation_test.cc +++ b/runtime/instrumentation_test.cc @@ -16,10 +16,11 @@ #include "instrumentation.h" +#include "art_method-inl.h" #include "base/enums.h" +#include "class_linker-inl.h" #include "common_runtime_test.h" #include "common_throws.h" -#include "class_linker-inl.h" #include "dex_file.h" #include "gc/scoped_gc_critical_section.h" #include "handle_scope-inl.h" @@ -27,8 +28,8 @@ #include "jvalue.h" #include "runtime.h" #include "scoped_thread_state_change-inl.h" -#include "thread_list.h" #include "thread-inl.h" +#include "thread_list.h" #include "well_known_classes.h" namespace art { @@ -484,10 +485,11 @@ TEST_F(InstrumentationTest, MethodExitObjectEvent) { Handle<mirror::ClassLoader> loader(hs.NewHandle(soa.Decode<mirror::ClassLoader>(class_loader))); mirror::Class* klass = class_linker->FindClass(soa.Self(), "LInstrumentation;", loader); ASSERT_TRUE(klass != nullptr); - ArtMethod* method = klass->FindDeclaredDirectMethod("returnReference", - "()Ljava/lang/Object;", - kRuntimePointerSize); + ArtMethod* method = + klass->FindClassMethod("returnReference", "()Ljava/lang/Object;", kRuntimePointerSize); ASSERT_TRUE(method != nullptr); + ASSERT_TRUE(method->IsDirect()); + ASSERT_TRUE(method->GetDeclaringClass() == klass); TestEvent(instrumentation::Instrumentation::kMethodExited, /*event_method*/ method, /*event_field*/ nullptr, @@ -503,10 +505,10 @@ TEST_F(InstrumentationTest, MethodExitPrimEvent) { Handle<mirror::ClassLoader> loader(hs.NewHandle(soa.Decode<mirror::ClassLoader>(class_loader))); mirror::Class* klass = class_linker->FindClass(soa.Self(), "LInstrumentation;", loader); ASSERT_TRUE(klass != nullptr); - ArtMethod* method = klass->FindDeclaredDirectMethod("returnPrimitive", - "()I", - kRuntimePointerSize); + ArtMethod* method = klass->FindClassMethod("returnPrimitive", "()I", kRuntimePointerSize); ASSERT_TRUE(method != nullptr); + ASSERT_TRUE(method->IsDirect()); + ASSERT_TRUE(method->GetDeclaringClass() == klass); TestEvent(instrumentation::Instrumentation::kMethodExited, /*event_method*/ method, /*event_field*/ nullptr, @@ -583,9 +585,11 @@ TEST_F(InstrumentationTest, DeoptimizeDirectMethod) { Handle<mirror::ClassLoader> loader(hs.NewHandle(soa.Decode<mirror::ClassLoader>(class_loader))); mirror::Class* klass = class_linker->FindClass(soa.Self(), "LInstrumentation;", loader); ASSERT_TRUE(klass != nullptr); - ArtMethod* method_to_deoptimize = klass->FindDeclaredDirectMethod("instanceMethod", "()V", - kRuntimePointerSize); + ArtMethod* method_to_deoptimize = + klass->FindClassMethod("instanceMethod", "()V", kRuntimePointerSize); ASSERT_TRUE(method_to_deoptimize != nullptr); + ASSERT_TRUE(method_to_deoptimize->IsDirect()); + ASSERT_TRUE(method_to_deoptimize->GetDeclaringClass() == klass); EXPECT_FALSE(instr->AreAllMethodsDeoptimized()); EXPECT_FALSE(instr->IsDeoptimized(method_to_deoptimize)); @@ -630,9 +634,11 @@ TEST_F(InstrumentationTest, MixedDeoptimization) { Handle<mirror::ClassLoader> loader(hs.NewHandle(soa.Decode<mirror::ClassLoader>(class_loader))); mirror::Class* klass = class_linker->FindClass(soa.Self(), "LInstrumentation;", loader); ASSERT_TRUE(klass != nullptr); - ArtMethod* method_to_deoptimize = klass->FindDeclaredDirectMethod("instanceMethod", "()V", - kRuntimePointerSize); + ArtMethod* method_to_deoptimize = + klass->FindClassMethod("instanceMethod", "()V", kRuntimePointerSize); ASSERT_TRUE(method_to_deoptimize != nullptr); + ASSERT_TRUE(method_to_deoptimize->IsDirect()); + ASSERT_TRUE(method_to_deoptimize->GetDeclaringClass() == klass); EXPECT_FALSE(instr->AreAllMethodsDeoptimized()); EXPECT_FALSE(instr->IsDeoptimized(method_to_deoptimize)); diff --git a/runtime/intern_table.cc b/runtime/intern_table.cc index 2bac2312bf..f4da5a44d9 100644 --- a/runtime/intern_table.cc +++ b/runtime/intern_table.cc @@ -18,14 +18,14 @@ #include <memory> -#include "gc_root-inl.h" #include "gc/collector/garbage_collector.h" #include "gc/space/image_space.h" #include "gc/weak_root_state.h" +#include "gc_root-inl.h" #include "image-inl.h" #include "mirror/dex_cache-inl.h" -#include "mirror/object_array-inl.h" #include "mirror/object-inl.h" +#include "mirror/object_array-inl.h" #include "mirror/string-inl.h" #include "object_callbacks.h" #include "scoped_thread_state_change-inl.h" diff --git a/runtime/intern_table.h b/runtime/intern_table.h index 2ec03be670..8714840d3f 100644 --- a/runtime/intern_table.h +++ b/runtime/intern_table.h @@ -23,8 +23,8 @@ #include "base/allocator.h" #include "base/hash_set.h" #include "base/mutex.h" -#include "gc_root.h" #include "gc/weak_root_state.h" +#include "gc_root.h" namespace art { diff --git a/runtime/intern_table_test.cc b/runtime/intern_table_test.cc index bb27b34cf5..9c3ea8d864 100644 --- a/runtime/intern_table_test.cc +++ b/runtime/intern_table_test.cc @@ -19,8 +19,8 @@ #include "base/hash_set.h" #include "common_runtime_test.h" #include "gc_root-inl.h" -#include "mirror/object.h" #include "handle_scope-inl.h" +#include "mirror/object.h" #include "mirror/string.h" #include "scoped_thread_state_change-inl.h" #include "utf.h" diff --git a/runtime/interpreter/interpreter.cc b/runtime/interpreter/interpreter.cc index 85cf73b044..9cb74f7c36 100644 --- a/runtime/interpreter/interpreter.cc +++ b/runtime/interpreter/interpreter.cc @@ -27,8 +27,8 @@ #include "jvalue-inl.h" #include "mirror/string-inl.h" #include "mterp/mterp.h" +#include "nativehelper/ScopedLocalRef.h" #include "scoped_thread_state_change-inl.h" -#include "ScopedLocalRef.h" #include "stack.h" #include "thread-inl.h" #include "unstarted_runtime.h" diff --git a/runtime/interpreter/interpreter_common.cc b/runtime/interpreter/interpreter_common.cc index 0687b753d8..85904ee4ed 100644 --- a/runtime/interpreter/interpreter_common.cc +++ b/runtime/interpreter/interpreter_common.cc @@ -23,14 +23,14 @@ #include "entrypoints/runtime_asm_entrypoints.h" #include "jit/jit.h" #include "jvalue.h" -#include "method_handles.h" #include "method_handles-inl.h" +#include "method_handles.h" #include "mirror/array-inl.h" #include "mirror/class.h" #include "mirror/emulated_stack_frame.h" #include "mirror/method_handle_impl-inl.h" -#include "reflection.h" #include "reflection-inl.h" +#include "reflection.h" #include "stack.h" #include "thread-inl.h" #include "well_known_classes.h" @@ -593,10 +593,8 @@ bool DoInvokePolymorphic(Thread* self, } ArtMethod* invoke_method = - class_linker->ResolveMethod<ClassLinker::kForceICCECheck>(self, - invoke_method_idx, - shadow_frame.GetMethod(), - kVirtual); + class_linker->ResolveMethod<ClassLinker::ResolveMode::kCheckICCEAndIAE>( + self, invoke_method_idx, shadow_frame.GetMethod(), kVirtual); // There is a common dispatch method for method handles that takes // arguments either from a range or an array of arguments depending diff --git a/runtime/interpreter/interpreter_common.h b/runtime/interpreter/interpreter_common.h index 74fec48342..d293aebc4c 100644 --- a/runtime/interpreter/interpreter_common.h +++ b/runtime/interpreter/interpreter_common.h @@ -22,9 +22,9 @@ #include <math.h> +#include <atomic> #include <iostream> #include <sstream> -#include <atomic> #include "android-base/stringprintf.h" diff --git a/runtime/interpreter/mterp/mterp.cc b/runtime/interpreter/mterp/mterp.cc index 5f94d04d13..5955b9001a 100644 --- a/runtime/interpreter/mterp/mterp.cc +++ b/runtime/interpreter/mterp/mterp.cc @@ -17,11 +17,11 @@ /* * Mterp entry point and support functions. */ -#include "interpreter/interpreter_common.h" -#include "interpreter/interpreter_intrinsics.h" -#include "entrypoints/entrypoint_utils-inl.h" #include "mterp.h" #include "debugger.h" +#include "entrypoints/entrypoint_utils-inl.h" +#include "interpreter/interpreter_common.h" +#include "interpreter/interpreter_intrinsics.h" namespace art { namespace interpreter { diff --git a/runtime/interpreter/mterp/mterp.h b/runtime/interpreter/mterp/mterp.h index 45ab98b9a6..1a56d26813 100644 --- a/runtime/interpreter/mterp/mterp.h +++ b/runtime/interpreter/mterp/mterp.h @@ -17,6 +17,9 @@ #ifndef ART_RUNTIME_INTERPRETER_MTERP_MTERP_H_ #define ART_RUNTIME_INTERPRETER_MTERP_MTERP_H_ +#include <cstddef> +#include <cstdint> + /* * Mterp assembly handler bases */ @@ -26,6 +29,9 @@ extern "C" void* artMterpAsmAltInstructionStart[]; extern "C" void* artMterpAsmAltInstructionEnd[]; namespace art { + +class Thread; + namespace interpreter { void InitMterpTls(Thread* self); diff --git a/runtime/interpreter/shadow_frame.h b/runtime/interpreter/shadow_frame.h index 69b2382cbc..05768cd6d3 100644 --- a/runtime/interpreter/shadow_frame.h +++ b/runtime/interpreter/shadow_frame.h @@ -17,8 +17,8 @@ #ifndef ART_RUNTIME_INTERPRETER_SHADOW_FRAME_H_ #define ART_RUNTIME_INTERPRETER_SHADOW_FRAME_H_ +#include <cstdint> #include <cstring> -#include <stdint.h> #include <string> #include "base/macros.h" diff --git a/runtime/interpreter/unstarted_runtime.cc b/runtime/interpreter/unstarted_runtime.cc index 152cce4c60..2c7282115f 100644 --- a/runtime/interpreter/unstarted_runtime.cc +++ b/runtime/interpreter/unstarted_runtime.cc @@ -27,7 +27,6 @@ #include <unordered_map> #include "android-base/stringprintf.h" -#include "ScopedLocalRef.h" #include "art_method-inl.h" #include "base/casts.h" @@ -48,6 +47,7 @@ #include "mirror/object-inl.h" #include "mirror/object_array-inl.h" #include "mirror/string-inl.h" +#include "nativehelper/ScopedLocalRef.h" #include "nth_caller_visitor.h" #include "reflection.h" #include "thread-inl.h" @@ -265,7 +265,7 @@ void UnstartedRuntime::UnstartedClassNewInstance( bool ok = false; auto* cl = Runtime::Current()->GetClassLinker(); if (cl->EnsureInitialized(self, h_klass, true, true)) { - auto* cons = h_klass->FindDeclaredDirectMethod("<init>", "()V", cl->GetImagePointerSize()); + auto* cons = h_klass->FindConstructor("()V", cl->GetImagePointerSize()); if (cons != nullptr) { Handle<mirror::Object> h_obj(hs.NewHandle(klass->AllocObject(self))); CHECK(h_obj != nullptr); // We don't expect OOM at compile-time. @@ -591,8 +591,7 @@ static void GetResourceAsStream(Thread* self, } auto* cl = Runtime::Current()->GetClassLinker(); - ArtMethod* constructor = h_class->FindDeclaredDirectMethod( - "<init>", "([B)V", cl->GetImagePointerSize()); + ArtMethod* constructor = h_class->FindConstructor("([B)V", cl->GetImagePointerSize()); if (constructor == nullptr) { AbortTransactionOrFail(self, "Could not find ByteArrayInputStream constructor"); return; @@ -1010,8 +1009,7 @@ static ObjPtr<mirror::Object> CreateInstanceOf(Thread* self, const char* class_d Handle<mirror::Class> h_class(hs.NewHandle(klass)); Handle<mirror::Object> h_obj(hs.NewHandle(h_class->AllocObject(self))); if (h_obj != nullptr) { - ArtMethod* init_method = h_class->FindDirectMethod( - "<init>", "()V", class_linker->GetImagePointerSize()); + ArtMethod* init_method = h_class->FindConstructor("()V", class_linker->GetImagePointerSize()); if (init_method == nullptr) { AbortTransactionOrFail(self, "Could not find <init> for %s", class_descriptor); return nullptr; diff --git a/runtime/interpreter/unstarted_runtime_test.cc b/runtime/interpreter/unstarted_runtime_test.cc index c2ef72460d..71ab01e0fc 100644 --- a/runtime/interpreter/unstarted_runtime_test.cc +++ b/runtime/interpreter/unstarted_runtime_test.cc @@ -29,8 +29,8 @@ #include "handle_scope-inl.h" #include "interpreter/interpreter_common.h" #include "mirror/class_loader.h" -#include "mirror/object_array-inl.h" #include "mirror/object-inl.h" +#include "mirror/object_array-inl.h" #include "mirror/string-inl.h" #include "runtime.h" #include "scoped_thread_state_change-inl.h" @@ -387,8 +387,9 @@ TEST_F(UnstartedRuntimeTest, StringInit) { ScopedObjectAccess soa(self); mirror::Class* klass = mirror::String::GetJavaLangString(); ArtMethod* method = - klass->FindDeclaredDirectMethod("<init>", "(Ljava/lang/String;)V", - Runtime::Current()->GetClassLinker()->GetImagePointerSize()); + klass->FindConstructor("(Ljava/lang/String;)V", + Runtime::Current()->GetClassLinker()->GetImagePointerSize()); + ASSERT_TRUE(method != nullptr); // create instruction data for invoke-direct {v0, v1} of method with fake index uint16_t inst_data[3] = { 0x2070, 0x0000, 0x0010 }; @@ -779,44 +780,40 @@ TEST_F(UnstartedRuntimeTest, ToLowerUpper) { { JValue result; tmp->SetVReg(0, static_cast<int32_t>(i)); - Transaction transaction; - Runtime::Current()->EnterTransactionMode(&transaction); + Runtime::Current()->EnterTransactionMode(); UnstartedCharacterToLowerCase(self, tmp, &result, 0); + ASSERT_TRUE(Runtime::Current()->IsTransactionAborted()); Runtime::Current()->ExitTransactionMode(); ASSERT_TRUE(self->IsExceptionPending()); - ASSERT_TRUE(transaction.IsAborted()); } { JValue result; tmp->SetVReg(0, static_cast<int32_t>(i)); - Transaction transaction; - Runtime::Current()->EnterTransactionMode(&transaction); + Runtime::Current()->EnterTransactionMode(); UnstartedCharacterToUpperCase(self, tmp, &result, 0); + ASSERT_TRUE(Runtime::Current()->IsTransactionAborted()); Runtime::Current()->ExitTransactionMode(); ASSERT_TRUE(self->IsExceptionPending()); - ASSERT_TRUE(transaction.IsAborted()); } } for (uint64_t i = 256; i <= std::numeric_limits<uint32_t>::max(); i <<= 1) { { JValue result; tmp->SetVReg(0, static_cast<int32_t>(i)); - Transaction transaction; - Runtime::Current()->EnterTransactionMode(&transaction); + Runtime::Current()->EnterTransactionMode(); UnstartedCharacterToLowerCase(self, tmp, &result, 0); + ASSERT_TRUE(Runtime::Current()->IsTransactionAborted()); Runtime::Current()->ExitTransactionMode(); ASSERT_TRUE(self->IsExceptionPending()); - ASSERT_TRUE(transaction.IsAborted()); } { JValue result; tmp->SetVReg(0, static_cast<int32_t>(i)); - Transaction transaction; - Runtime::Current()->EnterTransactionMode(&transaction); + Runtime::Current()->EnterTransactionMode(); UnstartedCharacterToUpperCase(self, tmp, &result, 0); + ASSERT_TRUE(Runtime::Current()->IsTransactionAborted()); Runtime::Current()->ExitTransactionMode(); ASSERT_TRUE(self->IsExceptionPending()); - ASSERT_TRUE(transaction.IsAborted()); } } @@ -966,12 +963,14 @@ TEST_F(UnstartedRuntimeTest, ThreadLocalGet) { ASSERT_TRUE(floating_decimal != nullptr); ASSERT_TRUE(class_linker->EnsureInitialized(self, floating_decimal, true, true)); - ArtMethod* caller_method = floating_decimal->FindDeclaredDirectMethod( + ArtMethod* caller_method = floating_decimal->FindClassMethod( "getBinaryToASCIIBuffer", "()Lsun/misc/FloatingDecimal$BinaryToASCIIBuffer;", class_linker->GetImagePointerSize()); // floating_decimal->DumpClass(LOG_STREAM(ERROR), mirror::Class::kDumpClassFullDetail); ASSERT_TRUE(caller_method != nullptr); + ASSERT_TRUE(caller_method->IsDirect()); + ASSERT_TRUE(caller_method->GetDeclaringClass() == floating_decimal.Get()); ShadowFrame* caller_frame = ShadowFrame::CreateDeoptimizedFrame(10, nullptr, caller_method, 0); shadow_frame->SetLink(caller_frame); @@ -993,12 +992,11 @@ TEST_F(UnstartedRuntimeTest, ThreadLocalGet) { ShadowFrame* caller_frame = ShadowFrame::CreateDeoptimizedFrame(10, nullptr, caller_method, 0); shadow_frame->SetLink(caller_frame); - Transaction transaction; - Runtime::Current()->EnterTransactionMode(&transaction); + Runtime::Current()->EnterTransactionMode(); UnstartedThreadLocalGet(self, shadow_frame, &result, 0); + ASSERT_TRUE(Runtime::Current()->IsTransactionAborted()); Runtime::Current()->ExitTransactionMode(); ASSERT_TRUE(self->IsExceptionPending()); - ASSERT_TRUE(transaction.IsAborted()); self->ClearException(); ShadowFrame::DeleteDeoptimizedFrame(caller_frame); @@ -1020,10 +1018,12 @@ TEST_F(UnstartedRuntimeTest, FloatConversion) { ASSERT_TRUE(double_class != nullptr); ASSERT_TRUE(class_linker->EnsureInitialized(self, double_class, true, true)); - ArtMethod* method = double_class->FindDeclaredDirectMethod("toString", - "(D)Ljava/lang/String;", - class_linker->GetImagePointerSize()); + ArtMethod* method = double_class->FindClassMethod("toString", + "(D)Ljava/lang/String;", + class_linker->GetImagePointerSize()); ASSERT_TRUE(method != nullptr); + ASSERT_TRUE(method->IsDirect()); + ASSERT_TRUE(method->GetDeclaringClass() == double_class.Get()); // create instruction data for invoke-direct {v0, v1} of method with fake index uint16_t inst_data[3] = { 0x2070, 0x0000, 0x0010 }; @@ -1061,12 +1061,11 @@ TEST_F(UnstartedRuntimeTest, ThreadCurrentThread) { PrepareForAborts(); { - Transaction transaction; - Runtime::Current()->EnterTransactionMode(&transaction); + Runtime::Current()->EnterTransactionMode(); UnstartedThreadCurrentThread(self, shadow_frame, &result, 0); + ASSERT_TRUE(Runtime::Current()->IsTransactionAborted()); Runtime::Current()->ExitTransactionMode(); ASSERT_TRUE(self->IsExceptionPending()); - ASSERT_TRUE(transaction.IsAborted()); self->ClearException(); } @@ -1133,28 +1132,27 @@ class UnstartedClassForNameTest : public UnstartedRuntimeTest { mirror::String* name_string = mirror::String::AllocFromModifiedUtf8(self, name); CHECK(name_string != nullptr); - Transaction transaction; if (in_transaction) { - Runtime::Current()->EnterTransactionMode(&transaction); + Runtime::Current()->EnterTransactionMode(); } CHECK(!self->IsExceptionPending()); runner(self, shadow_frame, name_string, &result); - if (in_transaction) { - Runtime::Current()->ExitTransactionMode(); - } - if (should_succeed) { CHECK(!self->IsExceptionPending()) << name << " " << self->GetException()->Dump(); CHECK(result.GetL() != nullptr) << name; } else { CHECK(self->IsExceptionPending()) << name; if (in_transaction) { - ASSERT_TRUE(transaction.IsAborted()); + ASSERT_TRUE(Runtime::Current()->IsTransactionAborted()); } self->ClearException(); } + + if (in_transaction) { + Runtime::Current()->ExitTransactionMode(); + } } ShadowFrame::DeleteDeoptimizedFrame(shadow_frame); @@ -1179,8 +1177,8 @@ class UnstartedClassForNameTest : public UnstartedRuntimeTest { boot_cp.Assign(boot_cp_class->AllocObject(self)->AsClassLoader()); CHECK(boot_cp != nullptr); - ArtMethod* boot_cp_init = boot_cp_class->FindDeclaredDirectMethod( - "<init>", "()V", class_linker->GetImagePointerSize()); + ArtMethod* boot_cp_init = boot_cp_class->FindConstructor( + "()V", class_linker->GetImagePointerSize()); CHECK(boot_cp_init != nullptr); JValue result; @@ -1333,8 +1331,8 @@ TEST_F(UnstartedRuntimeTest, ConstructorNewInstance0) { Handle<mirror::String> input = hs.NewHandle(mirror::String::AllocFromModifiedUtf8(self, "abd")); // Find the constructor. - ArtMethod* throw_cons = throw_class->FindDeclaredDirectMethod( - "<init>", "(Ljava/lang/String;)V", class_linker->GetImagePointerSize()); + ArtMethod* throw_cons = throw_class->FindConstructor( + "(Ljava/lang/String;)V", class_linker->GetImagePointerSize()); ASSERT_TRUE(throw_cons != nullptr); Handle<mirror::Constructor> cons; if (class_linker->GetImagePointerSize() == PointerSize::k64) { diff --git a/runtime/java_vm_ext.cc b/runtime/java_vm_ext.cc index 2ad3b29f17..267f9fdc99 100644 --- a/runtime/java_vm_ext.cc +++ b/runtime/java_vm_ext.cc @@ -34,17 +34,17 @@ #include "mirror/class-inl.h" #include "mirror/class_loader.h" #include "nativebridge/native_bridge.h" +#include "nativehelper/ScopedLocalRef.h" #include "nativeloader/native_loader.h" #include "object_callbacks.h" #include "parsed_options.h" #include "runtime-inl.h" #include "runtime_options.h" -#include "ScopedLocalRef.h" #include "scoped_thread_state_change-inl.h" #include "sigchain.h" -#include "ti/agent.h" #include "thread-inl.h" #include "thread_list.h" +#include "ti/agent.h" namespace art { diff --git a/runtime/jit/jit.cc b/runtime/jit/jit.cc index 969a5708c4..7abf52ea60 100644 --- a/runtime/jit/jit.cc +++ b/runtime/jit/jit.cc @@ -20,6 +20,7 @@ #include "art_method-inl.h" #include "base/enums.h" +#include "base/logging.h" #include "base/memory_tool.h" #include "debugger.h" #include "entrypoints/runtime_asm_entrypoints.h" @@ -45,6 +46,11 @@ static constexpr bool kEnableOnStackReplacement = true; // At what priority to schedule jit threads. 9 is the lowest foreground priority on device. static constexpr int kJitPoolThreadPthreadPriority = 9; +// Different compilation threshold constants. These can be overridden on the command line. +static constexpr size_t kJitDefaultCompileThreshold = 10000; // Non-debug default. +static constexpr size_t kJitStressDefaultCompileThreshold = 100; // Fast-debug build. +static constexpr size_t kJitSlowStressDefaultCompileThreshold = 2; // Slow-debug build. + // JIT compiler void* Jit::jit_library_handle_= nullptr; void* Jit::jit_compiler_handle_ = nullptr; @@ -54,6 +60,11 @@ bool (*Jit::jit_compile_method_)(void*, ArtMethod*, Thread*, bool) = nullptr; void (*Jit::jit_types_loaded_)(void*, mirror::Class**, size_t count) = nullptr; bool Jit::generate_debug_info_ = false; +struct StressModeHelper { + DECLARE_RUNTIME_DEBUG_FLAG(kSlowMode); +}; +DEFINE_RUNTIME_DEBUG_FLAG(StressModeHelper, kSlowMode); + JitOptions* JitOptions::CreateFromRuntimeArguments(const RuntimeArgumentMap& options) { auto* jit_options = new JitOptions; jit_options->use_jit_compilation_ = options.GetOrDefault(RuntimeArgumentMap::UseJitCompilation); @@ -67,7 +78,16 @@ JitOptions* JitOptions::CreateFromRuntimeArguments(const RuntimeArgumentMap& opt jit_options->profile_saver_options_ = options.GetOrDefault(RuntimeArgumentMap::ProfileSaverOpts); - jit_options->compile_threshold_ = options.GetOrDefault(RuntimeArgumentMap::JITCompileThreshold); + if (options.Exists(RuntimeArgumentMap::JITCompileThreshold)) { + jit_options->compile_threshold_ = *options.Get(RuntimeArgumentMap::JITCompileThreshold); + } else { + jit_options->compile_threshold_ = + kIsDebugBuild + ? (StressModeHelper::kSlowMode + ? kJitSlowStressDefaultCompileThreshold + : kJitStressDefaultCompileThreshold) + : kJitDefaultCompileThreshold; + } if (jit_options->compile_threshold_ > std::numeric_limits<uint16_t>::max()) { LOG(FATAL) << "Method compilation threshold is above its internal limit."; } diff --git a/runtime/jit/jit.h b/runtime/jit/jit.h index f898d416c1..51e49ec489 100644 --- a/runtime/jit/jit.h +++ b/runtime/jit/jit.h @@ -48,8 +48,6 @@ static constexpr int16_t kJitHotnessDisabled = -2; class Jit { public: - static constexpr bool kStressMode = kIsDebugBuild; - static constexpr size_t kDefaultCompileThreshold = kStressMode ? 2 : 10000; static constexpr size_t kDefaultPriorityThreadWeightRatio = 1000; static constexpr size_t kDefaultInvokeTransitionWeightRatio = 500; // How frequently should the interpreter check to see if OSR compilation is ready. diff --git a/runtime/jit/jit_code_cache.cc b/runtime/jit/jit_code_cache.cc index 8295f464af..a030a51473 100644 --- a/runtime/jit/jit_code_cache.cc +++ b/runtime/jit/jit_code_cache.cc @@ -90,7 +90,6 @@ static MemMap* SplitMemMap(MemMap* existing_map, return new_map; } - JitCodeCache* JitCodeCache::Create(size_t initial_capacity, size_t max_capacity, bool generate_debug_info, @@ -101,14 +100,15 @@ JitCodeCache* JitCodeCache::Create(size_t initial_capacity, // Generating debug information is for using the Linux perf tool on // host which does not work with ashmem. - bool use_ashmem = !generate_debug_info; + // Also, target linux does not support ashmem. + bool use_ashmem = !generate_debug_info && !kIsTargetLinux; // With 'perf', we want a 1-1 mapping between an address and a method. bool garbage_collect_code = !generate_debug_info; // We only use two mappings (separating rw from rx) if we are able to use ashmem. // See the above comment for debug information and not using ashmem. - bool use_two_mappings = !generate_debug_info; + bool use_two_mappings = use_ashmem; // We need to have 32 bit offsets from method headers in code cache which point to things // in the data cache. If the maps are more than 4G apart, having multiple maps wouldn't work. @@ -121,6 +121,10 @@ JitCodeCache* JitCodeCache::Create(size_t initial_capacity, return nullptr; } + // Align both capacities to page size, as that's the unit mspaces use. + initial_capacity = RoundDown(initial_capacity, 2 * kPageSize); + max_capacity = RoundDown(max_capacity, 2 * kPageSize); + std::string error_str; // Map name specific for android_os_Debug.cpp accounting. // Map in low 4gb to simplify accessing root tables for x86_64. @@ -142,22 +146,21 @@ JitCodeCache* JitCodeCache::Create(size_t initial_capacity, return nullptr; } - // Align both capacities to page size, as that's the unit mspaces use. - initial_capacity = RoundDown(initial_capacity, 2 * kPageSize); - max_capacity = RoundDown(max_capacity, 2 * kPageSize); - // Create a region for JIT data and executable code. This will be // laid out as: // // +----------------+ -------------------- - // : : ^ ^ + // | code_sync_map_ | ^ code_sync_size ^ + // | | v | + // +----------------+ -- | + // : : ^ | // : post_code_map : | post_code_size | // : [padding] : v | // +----------------+ - | // | | ^ | - // | code_map | | code_size | + // | code_map | | code_size | total_mapping_size // | [JIT Code] | v | - // +----------------+ - | total_mapping_size + // +----------------+ - | // : : ^ | // : pre_code_map : | pre_code_size | // : [padding] : v | @@ -167,17 +170,23 @@ JitCodeCache* JitCodeCache::Create(size_t initial_capacity, // | [Jit Data] | v v // +----------------+ -------------------- // + // The code_sync_map_ contains a page that we use flush CPU instruction + // pipelines (see FlushInstructionPipelines()). + // // The padding regions - pre_code_map and post_code_map - exist to // put some random distance between the writable JIT code mapping // and the executable mapping. The padding is discarded at the end // of this function. - size_t total_mapping_size = kMaxMapSpacingPages * kPageSize; - size_t data_size = RoundUp((max_capacity - total_mapping_size) / 2, kPageSize); + // + size_t data_size = (max_capacity - kMaxMapSpacingPages * kPageSize) / 2; size_t pre_code_size = - GetRandomNumber(kMinMapSpacingPages, kMaxMapSpacingPages) * kPageSize; - size_t code_size = max_capacity - total_mapping_size - data_size; - size_t post_code_size = total_mapping_size - pre_code_size; - DCHECK_EQ(code_size + data_size + total_mapping_size, max_capacity); + GetRandomNumber(kMinMapSpacingPages, kMaxMapSpacingPages - 1) * kPageSize; + size_t code_size = max_capacity - data_size - kMaxMapSpacingPages * kPageSize; + size_t code_sync_size = kPageSize; + size_t post_code_size = kMaxMapSpacingPages * kPageSize - pre_code_size - code_sync_size; + DCHECK_EQ(data_size, code_size); + DCHECK_EQ(pre_code_size + post_code_size + code_sync_size, kMaxMapSpacingPages * kPageSize); + DCHECK_EQ(data_size + pre_code_size + code_size + post_code_size + code_sync_size, max_capacity); // Create pre-code padding region after data region, discarded after // code and data regions are set-up. @@ -191,7 +200,7 @@ JitCodeCache* JitCodeCache::Create(size_t initial_capacity, return nullptr; } DCHECK_EQ(data_map->Size(), data_size); - DCHECK_EQ(pre_code_map->Size(), pre_code_size + code_size + post_code_size); + DCHECK_EQ(pre_code_map->Size(), pre_code_size + code_size + post_code_size + code_sync_size); // Create code region. unique_fd writable_code_fd; @@ -206,7 +215,7 @@ JitCodeCache* JitCodeCache::Create(size_t initial_capacity, return nullptr; } DCHECK_EQ(pre_code_map->Size(), pre_code_size); - DCHECK_EQ(code_map->Size(), code_size + post_code_size); + DCHECK_EQ(code_map->Size(), code_size + post_code_size + code_sync_size); // Padding after code region, discarded after code and data regions // are set-up. @@ -220,7 +229,19 @@ JitCodeCache* JitCodeCache::Create(size_t initial_capacity, return nullptr; } DCHECK_EQ(code_map->Size(), code_size); + DCHECK_EQ(post_code_map->Size(), post_code_size + code_sync_size); + + std::unique_ptr<MemMap> code_sync_map(SplitMemMap(post_code_map.get(), + "jit-code-sync", + post_code_size, + kProtCode, + error_msg, + use_ashmem)); + if (code_sync_map == nullptr) { + return nullptr; + } DCHECK_EQ(post_code_map->Size(), post_code_size); + DCHECK_EQ(code_sync_map->Size(), code_sync_size); std::unique_ptr<MemMap> writable_code_map; if (use_two_mappings) { @@ -246,6 +267,7 @@ JitCodeCache* JitCodeCache::Create(size_t initial_capacity, return new JitCodeCache(writable_code_map.release(), code_map.release(), data_map.release(), + code_sync_map.release(), code_size, data_size, max_capacity, @@ -255,6 +277,7 @@ JitCodeCache* JitCodeCache::Create(size_t initial_capacity, JitCodeCache::JitCodeCache(MemMap* writable_code_map, MemMap* executable_code_map, MemMap* data_map, + MemMap* code_sync_map, size_t initial_code_capacity, size_t initial_data_capacity, size_t max_capacity, @@ -265,6 +288,7 @@ JitCodeCache::JitCodeCache(MemMap* writable_code_map, data_map_(data_map), executable_code_map_(executable_code_map), writable_code_map_(writable_code_map), + code_sync_map_(code_sync_map), max_capacity_(max_capacity), current_capacity_(initial_code_capacity + initial_data_capacity), code_end_(initial_code_capacity), @@ -382,7 +406,7 @@ void* JitCodeCache::ToWritableAddress(const void* executable_address) const { class ScopedCodeCacheWrite : ScopedTrace { public: - explicit ScopedCodeCacheWrite(JitCodeCache* code_cache, bool only_for_tlb_shootdown = false) + explicit ScopedCodeCacheWrite(JitCodeCache* code_cache) : ScopedTrace("ScopedCodeCacheWrite") { ScopedTrace trace("mprotect all"); int prot_to_start_writing = kProtAll; @@ -398,7 +422,7 @@ class ScopedCodeCacheWrite : ScopedTrace { writable_map_ = code_cache->GetWritableMemMap(); // If we're using ScopedCacheWrite only for TLB shootdown, we limit the scope of mprotect to // one page. - size_ = only_for_tlb_shootdown ? kPageSize : writable_map_->Size(); + size_ = writable_map_->Size(); CHECKED_MPROTECT(writable_map_->Begin(), size_, prot_to_start_writing); } ~ScopedCodeCacheWrite() { @@ -424,7 +448,6 @@ uint8_t* JitCodeCache::CommitCode(Thread* self, size_t fp_spill_mask, const uint8_t* code, size_t code_size, - size_t data_size, bool osr, Handle<mirror::ObjectArray<mirror::Object>> roots, bool has_should_deoptimize_flag, @@ -439,7 +462,6 @@ uint8_t* JitCodeCache::CommitCode(Thread* self, fp_spill_mask, code, code_size, - data_size, osr, roots, has_should_deoptimize_flag, @@ -457,7 +479,6 @@ uint8_t* JitCodeCache::CommitCode(Thread* self, fp_spill_mask, code, code_size, - data_size, osr, roots, has_should_deoptimize_flag, @@ -621,7 +642,7 @@ void JitCodeCache::FreeAllMethodHeaders( // method_headers are expected to be in the executable region. { MutexLock mu(Thread::Current(), *Locks::cha_lock_); - Runtime::Current()->GetClassHierarchyAnalysis() + Runtime::Current()->GetClassLinker()->GetClassHierarchyAnalysis() ->RemoveDependentsWithMethodHeaders(method_headers); } @@ -744,6 +765,129 @@ static void ClearMethodCounter(ArtMethod* method, bool was_warm) { method->SetCounter(std::min(jit_warmup_threshold - 1, 1)); } +static void FlushInstructionPiplines(uint8_t* sync_page) { + // After updating the JIT code cache we need to force all CPUs to + // flush their instruction pipelines. In the absence of system call + // to do this explicitly, we can achieve this indirectly by toggling + // permissions on an executable page. This should send an IPI to + // each core to update the TLB entry with the interrupt raised on + // each core causing the instruction pipeline to be flushed. + CHECKED_MPROTECT(sync_page, kPageSize, kProtAll); + // Ensure the sync_page is present otherwise a TLB update may not be + // necessary. + sync_page[0] = 0; + CHECKED_MPROTECT(sync_page, kPageSize, kProtCode); +} + +#ifdef __aarch64__ + +static void FlushJitCodeCacheRange(uint8_t* code_ptr, + uint8_t* writable_ptr, + size_t code_size) { + // Cache maintenance instructions can cause permission faults when a + // page is not present (e.g. swapped out or not backed). These + // faults should be handled by the kernel, but a bug in some Linux + // kernels may surface these permission faults to user-land which + // does not currently deal with them (b/63885946). To work around + // this, we read a value from each page to fault it in before + // attempting to perform cache maintenance operations. + // + // For reference, this behavior is caused by this commit: + // https://android.googlesource.com/kernel/msm/+/3fbe6bc28a6b9939d0650f2f17eb5216c719950c + + // The cache-line size could be probed for from the CPU, but + // assuming a safe lower bound is safe for CPUs that have different + // cache-line sizes for big and little cores. + static const uintptr_t kSafeCacheLineSize = 32; + + // Ensure stores are present in L1 data cache. + __asm __volatile("dsb ish" ::: "memory"); + + volatile uint8_t mutant; + + // Push dirty cache-lines out to the point of unification (PoU). The + // point of unification is the first point in the cache/memory + // hierarchy where the instruction cache and data cache have the + // same view of memory. The PoU is where an instruction fetch will + // fetch the new code generated by the JIT. + // + // See: http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.den0024a/ch11s04.html + uintptr_t writable_addr = RoundDown(reinterpret_cast<uintptr_t>(writable_ptr), + kSafeCacheLineSize); + uintptr_t writable_end = RoundUp(reinterpret_cast<uintptr_t>(writable_ptr) + code_size, + kSafeCacheLineSize); + while (writable_addr < writable_end) { + // Read from the cache-line to minimize the chance that a cache + // maintenance instruction causes a fault (see kernel bug comment + // above). + mutant = *reinterpret_cast<const uint8_t*>(writable_addr); + + // Flush cache-line + __asm volatile("dc cvau, %0" :: "r"(writable_addr) : "memory"); + writable_addr += kSafeCacheLineSize; + } + + __asm __volatile("dsb ish" ::: "memory"); + + uintptr_t code_addr = RoundDown(reinterpret_cast<uintptr_t>(code_ptr), kSafeCacheLineSize); + const uintptr_t code_end = RoundUp(reinterpret_cast<uintptr_t>(code_ptr) + code_size, + kSafeCacheLineSize); + while (code_addr < code_end) { + // Read from the cache-line to minimize the chance that a cache + // maintenance instruction causes a fault (see kernel bug comment + // above). + mutant = *reinterpret_cast<const uint8_t*>(code_addr); + + // Invalidating the data cache line is only strictly necessary + // when the JIT code cache has two mappings (the default). We know + // this cache line is clean so this is just invalidating it (using + // "dc ivac" would be preferable, but counts as a write and this + // memory may not be mapped write permission). + __asm volatile("dc cvau, %0" :: "r"(code_addr) : "memory"); + + // Invalidate the instruction cache line to force instructions in + // range to be re-fetched following update. + __asm volatile("ic ivau, %0" :: "r"(code_addr) : "memory"); + + code_addr += kSafeCacheLineSize; + } + + // Wait for code cache invalidations to complete. + __asm __volatile("dsb ish" ::: "memory"); + + // Reset fetched instruction stream. + __asm __volatile("isb"); +} + +#else // __aarch64 + +static void FlushJitCodeCacheRange(uint8_t* code_ptr, + uint8_t* writable_ptr, + size_t code_size) { + if (writable_ptr != code_ptr) { + // When there are two mappings of the JIT code cache, RX and + // RW, flush the RW version first as we've just dirtied the + // cache lines with new code. Flushing the RX version first + // can cause a permission fault as the those addresses are not + // writable, but can appear dirty in the cache. There is a lot + // of potential subtlety here depending on how the cache is + // indexed and tagged. + // + // Flushing the RX version after the RW version is just + // invalidating cachelines in the instruction cache. This is + // necessary as the instruction cache will often have a + // different set of cache lines present and because the JIT + // code cache can start a new function at any boundary within + // a cache-line. + FlushDataCache(reinterpret_cast<char*>(writable_ptr), + reinterpret_cast<char*>(writable_ptr + code_size)); + } + FlushInstructionCache(reinterpret_cast<char*>(code_ptr), + reinterpret_cast<char*>(code_ptr + code_size)); +} + +#endif // __aarch64 + uint8_t* JitCodeCache::CommitCodeInternal(Thread* self, ArtMethod* method, uint8_t* stack_map, @@ -754,7 +898,6 @@ uint8_t* JitCodeCache::CommitCodeInternal(Thread* self, size_t fp_spill_mask, const uint8_t* code, size_t code_size, - size_t data_size, bool osr, Handle<mirror::ObjectArray<mirror::Object>> roots, bool has_should_deoptimize_flag, @@ -795,19 +938,10 @@ uint8_t* JitCodeCache::CommitCodeInternal(Thread* self, core_spill_mask, fp_spill_mask, code_size); - // Flush caches before we remove write permission because some ARMv8 Qualcomm kernels may - // trigger a segfault if a page fault occurs when requesting a cache maintenance operation. - // This is a kernel bug that we need to work around until affected devices (e.g. Nexus 5X and - // 6P) stop being supported or their kernels are fixed. - // - // For reference, this behavior is caused by this commit: - // https://android.googlesource.com/kernel/msm/+/3fbe6bc28a6b9939d0650f2f17eb5216c719950c - FlushInstructionCache(reinterpret_cast<char*>(code_ptr), - reinterpret_cast<char*>(code_ptr + code_size)); - if (writable_ptr != code_ptr) { - FlushDataCache(reinterpret_cast<char*>(writable_ptr), - reinterpret_cast<char*>(writable_ptr + code_size)); - } + + FlushJitCodeCacheRange(code_ptr, writable_ptr, code_size); + FlushInstructionPiplines(code_sync_map_->Begin()); + DCHECK(!Runtime::Current()->IsAotCompiler()); if (has_should_deoptimize_flag) { writable_method_header->SetHasShouldDeoptimizeFlag(); @@ -843,7 +977,7 @@ uint8_t* JitCodeCache::CommitCodeInternal(Thread* self, << "Should not be using cha on debuggable apps/runs!"; for (ArtMethod* single_impl : cha_single_implementation_list) { - Runtime::Current()->GetClassHierarchyAnalysis()->AddDependency( + Runtime::Current()->GetClassLinker()->GetClassHierarchyAnalysis()->AddDependency( single_impl, method, method_header); } @@ -856,13 +990,10 @@ uint8_t* JitCodeCache::CommitCodeInternal(Thread* self, DCHECK_EQ(FromStackMapToRoots(stack_map), roots_data); DCHECK_LE(roots_data, stack_map); FillRootTable(roots_data, roots); - { - // Flush data cache, as compiled code references literals in it. - // We also need a TLB shootdown to act as memory barrier across cores. - ScopedCodeCacheWrite ccw(this, /* only_for_tlb_shootdown */ true); - FlushDataCache(reinterpret_cast<char*>(roots_data), - reinterpret_cast<char*>(roots_data + data_size)); - } + + // Ensure the updates to the root table are visible with a store fence. + QuasiAtomic::ThreadFenceSequentiallyConsistent(); + method_code_map_.Put(code_ptr, method); if (osr) { number_of_osr_compilations_++; diff --git a/runtime/jit/jit_code_cache.h b/runtime/jit/jit_code_cache.h index a062ce4ac2..175501f915 100644 --- a/runtime/jit/jit_code_cache.h +++ b/runtime/jit/jit_code_cache.h @@ -113,7 +113,6 @@ class JitCodeCache { size_t fp_spill_mask, const uint8_t* code, size_t code_size, - size_t data_size, bool osr, Handle<mirror::ObjectArray<mirror::Object>> roots, bool has_should_deoptimize_flag, @@ -255,6 +254,7 @@ class JitCodeCache { JitCodeCache(MemMap* code_map, MemMap* data_map, MemMap* writable_code_map, + MemMap* code_sync_map, size_t initial_code_capacity, size_t initial_data_capacity, size_t max_capacity, @@ -272,7 +272,6 @@ class JitCodeCache { size_t fp_spill_mask, const uint8_t* code, size_t code_size, - size_t data_size, bool osr, Handle<mirror::ObjectArray<mirror::Object>> roots, bool has_should_deoptimize_flag, @@ -383,6 +382,9 @@ class JitCodeCache { std::unique_ptr<MemMap> executable_code_map_; // Mem map which holds a non-executable view of code for JIT. std::unique_ptr<MemMap> writable_code_map_; + // Mem map which holds one executable page that we use for flushing instruction + // fetch buffers. The code on this page is never executed. + std::unique_ptr<MemMap> code_sync_map_; // The opaque mspace for allocating code. void* code_mspace_ GUARDED_BY(lock_); // The opaque mspace for allocating data. diff --git a/runtime/jit/profile_compilation_info.cc b/runtime/jit/profile_compilation_info.cc index a247b56587..c9bfc9c991 100644 --- a/runtime/jit/profile_compilation_info.cc +++ b/runtime/jit/profile_compilation_info.cc @@ -16,20 +16,20 @@ #include "profile_compilation_info.h" -#include "errno.h" -#include <limits.h> -#include <string> -#include <vector> -#include <stdlib.h> #include <sys/file.h> #include <sys/stat.h> -#include <sys/uio.h> -#include <sys/types.h> -#include <unistd.h> #include <sys/types.h> +#include <sys/uio.h> #include <unistd.h> #include <zlib.h> -#include <base/time_utils.h> + +#include <cerrno> +#include <climits> +#include <cstdlib> +#include <string> +#include <vector> + +#include "android-base/file.h" #include "base/arena_allocator.h" #include "base/dumpable.h" @@ -37,19 +37,18 @@ #include "base/scoped_flock.h" #include "base/stl_util.h" #include "base/systrace.h" +#include "base/time_utils.h" #include "base/unix_file/fd_file.h" #include "jit/profiling_info.h" #include "os.h" #include "safe_map.h" #include "utils.h" -#include "android-base/file.h" namespace art { const uint8_t ProfileCompilationInfo::kProfileMagic[] = { 'p', 'r', 'o', '\0' }; -// Last profile version: Move startup methods to use a bitmap. Also add support for post-startup -// methods. -const uint8_t ProfileCompilationInfo::kProfileVersion[] = { '0', '0', '8', '\0' }; +// Last profile version: update the multidex separator. +const uint8_t ProfileCompilationInfo::kProfileVersion[] = { '0', '0', '9', '\0' }; static constexpr uint16_t kMaxDexFileKeyLength = PATH_MAX; @@ -1341,7 +1340,7 @@ std::string ProfileCompilationInfo::DumpInfo(const std::vector<const DexFile*>* os << "ProfileInfo:"; - const std::string kFirstDexFileKeySubstitute = ":classes.dex"; + const std::string kFirstDexFileKeySubstitute = "!classes.dex"; for (const DexFileData* dex_data : info_) { os << "\n"; @@ -1653,4 +1652,27 @@ ProfileCompilationInfo::FindOrAddDexPc(InlineCacheMap* inline_cache, uint32_t de return &(inline_cache->FindOrAdd(dex_pc, DexPcData(&arena_))->second); } +std::unordered_set<std::string> ProfileCompilationInfo::GetClassDescriptors( + const std::vector<const DexFile*>& dex_files) { + std::unordered_set<std::string> ret; + for (const DexFile* dex_file : dex_files) { + const DexFileData* data = FindDexData(dex_file); + if (data != nullptr) { + for (dex::TypeIndex type_idx : data->class_set) { + if (!dex_file->IsTypeIndexValid(type_idx)) { + // Something went bad. The profile is probably corrupted. Abort and return an emtpy set. + LOG(WARNING) << "Corrupted profile: invalid type index " + << type_idx.index_ << " in dex " << dex_file->GetLocation(); + return std::unordered_set<std::string>(); + } + const DexFile::TypeId& type_id = dex_file->GetTypeId(type_idx); + ret.insert(dex_file->GetTypeDescriptor(type_id)); + } + } else { + VLOG(compiler) << "Failed to find profile data for " << dex_file->GetLocation(); + } + } + return ret; +} + } // namespace art diff --git a/runtime/jit/profile_compilation_info.h b/runtime/jit/profile_compilation_info.h index 079ce8d117..ffb67ae2be 100644 --- a/runtime/jit/profile_compilation_info.h +++ b/runtime/jit/profile_compilation_info.h @@ -21,8 +21,8 @@ #include <vector> #include "atomic.h" -#include "base/arena_object.h" #include "base/arena_containers.h" +#include "base/arena_object.h" #include "bit_memory_region.h" #include "dex_cache_resolved_classes.h" #include "dex_file.h" @@ -380,6 +380,9 @@ class ProfileCompilationInfo { ArenaAllocator* GetArena() { return &arena_; } + // Return all of the class descriptors in the profile for a set of dex files. + std::unordered_set<std::string> GetClassDescriptors(const std::vector<const DexFile*>& dex_files); + private: enum ProfileLoadSatus { kProfileLoadWouldOverwiteData, diff --git a/runtime/jit/profile_compilation_info_test.cc b/runtime/jit/profile_compilation_info_test.cc index 1ba98acab8..6010bce2e5 100644 --- a/runtime/jit/profile_compilation_info_test.cc +++ b/runtime/jit/profile_compilation_info_test.cc @@ -16,17 +16,17 @@ #include <gtest/gtest.h> -#include "base/unix_file/fd_file.h" #include "art_method-inl.h" +#include "base/unix_file/fd_file.h" #include "class_linker-inl.h" #include "common_runtime_test.h" #include "dex_file.h" -#include "method_reference.h" -#include "mirror/class-inl.h" -#include "mirror/class_loader.h" #include "handle_scope-inl.h" #include "jit/profile_compilation_info.h" #include "linear_alloc.h" +#include "method_reference.h" +#include "mirror/class-inl.h" +#include "mirror/class_loader.h" #include "scoped_thread_state_change-inl.h" #include "type_reference.h" diff --git a/runtime/jit/profile_saver.cc b/runtime/jit/profile_saver.cc index 61e5be34cb..381e95f8f8 100644 --- a/runtime/jit/profile_saver.cc +++ b/runtime/jit/profile_saver.cc @@ -16,10 +16,10 @@ #include "profile_saver.h" +#include <fcntl.h> #include <sys/resource.h> -#include <sys/types.h> #include <sys/stat.h> -#include <fcntl.h> +#include <sys/types.h> #include "android-base/strings.h" diff --git a/runtime/jit/profile_saver_options.h b/runtime/jit/profile_saver_options.h index 251227e89c..d1e14e2766 100644 --- a/runtime/jit/profile_saver_options.h +++ b/runtime/jit/profile_saver_options.h @@ -13,8 +13,8 @@ #ifndef ART_RUNTIME_JIT_PROFILE_SAVER_OPTIONS_H_ #define ART_RUNTIME_JIT_PROFILE_SAVER_OPTIONS_H_ -#include <string> #include <ostream> +#include <string> namespace art { diff --git a/runtime/jni_internal.cc b/runtime/jni_internal.cc index dbad614416..927f94b588 100644 --- a/runtime/jni_internal.cc +++ b/runtime/jni_internal.cc @@ -34,12 +34,12 @@ #include "class_linker-inl.h" #include "dex_file-inl.h" #include "fault_handler.h" -#include "gc_root.h" #include "gc/accounting/card_table-inl.h" +#include "gc_root.h" #include "indirect_reference_table-inl.h" #include "interpreter/interpreter.h" -#include "jni_env_ext.h" #include "java_vm_ext.h" +#include "jni_env_ext.h" #include "jvalue-inl.h" #include "mirror/class-inl.h" #include "mirror/class_loader.h" @@ -49,12 +49,12 @@ #include "mirror/object_array-inl.h" #include "mirror/string-inl.h" #include "mirror/throwable.h" +#include "nativehelper/ScopedLocalRef.h" #include "parsed_options.h" #include "reflection.h" #include "runtime.h" #include "safe_map.h" #include "scoped_thread_state_change-inl.h" -#include "ScopedLocalRef.h" #include "thread.h" #include "utf.h" #include "well_known_classes.h" @@ -233,17 +233,10 @@ static jmethodID FindMethodID(ScopedObjectAccess& soa, jclass jni_class, } ArtMethod* method = nullptr; auto pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize(); - if (is_static) { - method = c->FindDirectMethod(name, sig, pointer_size); - } else if (c->IsInterface()) { + if (c->IsInterface()) { method = c->FindInterfaceMethod(name, sig, pointer_size); } else { - method = c->FindVirtualMethod(name, sig, pointer_size); - if (method == nullptr) { - // No virtual method matching the signature. Search declared - // private methods and constructors. - method = c->FindDeclaredDirectMethod(name, sig, pointer_size); - } + method = c->FindClassMethod(name, sig, pointer_size); } if (method == nullptr || method->IsStatic() != is_static) { ThrowNoSuchMethodError(soa, c, name, sig, is_static ? "static" : "non-static"); diff --git a/runtime/jni_internal_test.cc b/runtime/jni_internal_test.cc index e1e4f9c7d6..3f00450319 100644 --- a/runtime/jni_internal_test.cc +++ b/runtime/jni_internal_test.cc @@ -24,8 +24,8 @@ #include "java_vm_ext.h" #include "jni_env_ext.h" #include "mirror/string-inl.h" +#include "nativehelper/ScopedLocalRef.h" #include "scoped_thread_state_change-inl.h" -#include "ScopedLocalRef.h" namespace art { @@ -626,9 +626,9 @@ class JniInternalTest : public CommonCompilerTest { hs.NewHandle(soa.Decode<mirror::ClassLoader>(class_loader_))); mirror::Class* c = class_linker_->FindClass(soa.Self(), "LMyClassNatives;", loader); const auto pointer_size = class_linker_->GetImagePointerSize(); - ArtMethod* method = direct ? c->FindDirectMethod(method_name, method_sig, pointer_size) : - c->FindVirtualMethod(method_name, method_sig, pointer_size); + ArtMethod* method = c->FindClassMethod(method_name, method_sig, pointer_size); ASSERT_TRUE(method != nullptr) << method_name << " " << method_sig; + ASSERT_EQ(direct, method->IsDirect()); method->SetEntryPointFromQuickCompiledCode(class_linker_->GetRuntimeQuickGenericJniStub()); } // Start runtime. diff --git a/runtime/leb128_test.cc b/runtime/leb128_test.cc index 122f55ebee..747fc19f5d 100644 --- a/runtime/leb128_test.cc +++ b/runtime/leb128_test.cc @@ -17,6 +17,7 @@ #include "leb128.h" #include "gtest/gtest.h" + #include "base/histogram-inl.h" #include "base/time_utils.h" diff --git a/runtime/lock_word.h b/runtime/lock_word.h index edc64f35a1..14f638ee78 100644 --- a/runtime/lock_word.h +++ b/runtime/lock_word.h @@ -17,8 +17,8 @@ #ifndef ART_RUNTIME_LOCK_WORD_H_ #define ART_RUNTIME_LOCK_WORD_H_ +#include <cstdint> #include <iosfwd> -#include <stdint.h> #include "base/bit_utils.h" #include "base/logging.h" diff --git a/runtime/managed_stack-inl.h b/runtime/managed_stack-inl.h index bdf8100cc0..689dd8009a 100644 --- a/runtime/managed_stack-inl.h +++ b/runtime/managed_stack-inl.h @@ -19,10 +19,6 @@ #include "managed_stack.h" -#include <cstring> -#include <stdint.h> -#include <string> - #include "interpreter/shadow_frame.h" namespace art { diff --git a/runtime/managed_stack.h b/runtime/managed_stack.h index 8337f968ac..4f1984d55a 100644 --- a/runtime/managed_stack.h +++ b/runtime/managed_stack.h @@ -17,8 +17,8 @@ #ifndef ART_RUNTIME_MANAGED_STACK_H_ #define ART_RUNTIME_MANAGED_STACK_H_ +#include <cstdint> #include <cstring> -#include <stdint.h> #include <string> #include "base/logging.h" diff --git a/runtime/mem_map.cc b/runtime/mem_map.cc index 17035dda8f..4e82480aca 100644 --- a/runtime/mem_map.cc +++ b/runtime/mem_map.cc @@ -38,7 +38,6 @@ #include "globals.h" #include "utils.h" - #ifndef MAP_ANONYMOUS #define MAP_ANONYMOUS MAP_ANON #endif @@ -497,7 +496,7 @@ MemMap::~MemMap() { MEMORY_TOOL_MAKE_UNDEFINED(base_begin_, base_size_); int result = munmap(base_begin_, base_size_); if (result == -1) { - PLOG(FATAL) << "munmap failed"; + PLOG(FATAL) << "munmap failed: " << BaseBegin() << "..." << BaseEnd(); } } @@ -561,6 +560,12 @@ MemMap* MemMap::RemapAtEnd(uint8_t* new_end, size_ = new_end - reinterpret_cast<uint8_t*>(begin_); base_size_ = new_base_end - reinterpret_cast<uint8_t*>(base_begin_); DCHECK_LE(begin_ + size_, reinterpret_cast<uint8_t*>(base_begin_) + base_size_); + if (base_size_ == 0u) { + // All pages in this MemMap have been handed out. Invalidate base + // pointer to prevent the destructor calling munmap() on + // zero-length region (which can't succeed). + base_begin_ = nullptr; + } size_t tail_size = old_end - new_end; uint8_t* tail_base_begin = new_base_end; size_t tail_base_size = old_base_end - new_base_end; diff --git a/runtime/mem_map_test.cc b/runtime/mem_map_test.cc index 8d6bb38838..99bf0045c4 100644 --- a/runtime/mem_map_test.cc +++ b/runtime/mem_map_test.cc @@ -20,9 +20,9 @@ #include <memory> -#include "common_runtime_test.h" #include "base/memory_tool.h" #include "base/unix_file/fd_file.h" +#include "common_runtime_test.h" namespace art { diff --git a/runtime/memory_region_test.cc b/runtime/memory_region_test.cc index 6634c60193..e3aead47fa 100644 --- a/runtime/memory_region_test.cc +++ b/runtime/memory_region_test.cc @@ -14,11 +14,12 @@ * limitations under the License. */ -#include "bit_memory_region.h" #include "memory_region.h" #include "gtest/gtest.h" +#include "bit_memory_region.h" + namespace art { TEST(MemoryRegion, LoadUnaligned) { diff --git a/runtime/method_handles.cc b/runtime/method_handles.cc index f0d3cae4b4..65f39e4468 100644 --- a/runtime/method_handles.cc +++ b/runtime/method_handles.cc @@ -19,13 +19,13 @@ #include "android-base/stringprintf.h" #include "common_dex_operations.h" -#include "jvalue.h" #include "jvalue-inl.h" +#include "jvalue.h" #include "mirror/emulated_stack_frame.h" #include "mirror/method_handle_impl-inl.h" #include "mirror/method_type.h" -#include "reflection.h" #include "reflection-inl.h" +#include "reflection.h" #include "well_known_classes.h" namespace art { diff --git a/runtime/mirror/array-inl.h b/runtime/mirror/array-inl.h index bfbd4df537..63142d5c25 100644 --- a/runtime/mirror/array-inl.h +++ b/runtime/mirror/array-inl.h @@ -26,8 +26,8 @@ #include "base/logging.h" #include "class.h" #include "gc/heap-inl.h" -#include "object-inl.h" #include "obj_ptr-inl.h" +#include "object-inl.h" #include "thread.h" namespace art { diff --git a/runtime/mirror/array.cc b/runtime/mirror/array.cc index f283ec3e9d..6218dd9c9c 100644 --- a/runtime/mirror/array.cc +++ b/runtime/mirror/array.cc @@ -16,15 +16,15 @@ #include "array-inl.h" -#include "class.h" #include "class-inl.h" +#include "class.h" #include "class_linker-inl.h" #include "common_throws.h" #include "dex_file-inl.h" #include "gc/accounting/card_table-inl.h" +#include "handle_scope-inl.h" #include "object-inl.h" #include "object_array-inl.h" -#include "handle_scope-inl.h" #include "thread.h" #include "utils.h" diff --git a/runtime/mirror/array.h b/runtime/mirror/array.h index 99565c6f5d..11128bb5a2 100644 --- a/runtime/mirror/array.h +++ b/runtime/mirror/array.h @@ -18,8 +18,8 @@ #define ART_RUNTIME_MIRROR_ARRAY_H_ #include "base/enums.h" -#include "gc_root.h" #include "gc/allocator_type.h" +#include "gc_root.h" #include "obj_ptr.h" #include "object.h" diff --git a/runtime/mirror/class-inl.h b/runtime/mirror/class-inl.h index 003cd4e2bc..67aeeff2e8 100644 --- a/runtime/mirror/class-inl.h +++ b/runtime/mirror/class-inl.h @@ -30,8 +30,8 @@ #include "dex_file-inl.h" #include "gc/heap-inl.h" #include "iftable.h" -#include "object_array.h" #include "object-inl.h" +#include "object_array.h" #include "read_barrier-inl.h" #include "reference-inl.h" #include "runtime.h" @@ -533,7 +533,11 @@ inline ArtMethod* Class::FindVirtualMethodForInterface(ArtMethod* method, PointerSize pointer_size) { ObjPtr<Class> declaring_class = method->GetDeclaringClass(); DCHECK(declaring_class != nullptr) << PrettyClass(); - DCHECK(declaring_class->IsInterface()) << method->PrettyMethod(); + if (UNLIKELY(!declaring_class->IsInterface())) { + DCHECK(declaring_class->IsObjectClass()) << method->PrettyMethod(); + DCHECK(method->IsPublic() && !method->IsStatic()); + return FindVirtualMethodForVirtual(method, pointer_size); + } DCHECK(!method->IsCopied()); // TODO cache to improve lookup speed const int32_t iftable_count = GetIfTableCount(); diff --git a/runtime/mirror/class.cc b/runtime/mirror/class.cc index b0e5b6adbf..6642869900 100644 --- a/runtime/mirror/class.cc +++ b/runtime/mirror/class.cc @@ -20,19 +20,19 @@ #include "art_field-inl.h" #include "art_method-inl.h" +#include "class-inl.h" #include "class_ext.h" #include "class_linker-inl.h" #include "class_loader.h" -#include "class-inl.h" #include "dex_cache.h" #include "dex_file-inl.h" #include "dex_file_annotations.h" #include "gc/accounting/card_table-inl.h" #include "handle_scope-inl.h" #include "method.h" -#include "object_array-inl.h" #include "object-inl.h" #include "object-refvisitor-inl.h" +#include "object_array-inl.h" #include "object_lock.h" #include "runtime.h" #include "thread.h" @@ -396,185 +396,276 @@ void Class::SetClassLoader(ObjPtr<ClassLoader> new_class_loader) { } } -ArtMethod* Class::FindInterfaceMethod(const StringPiece& name, - const StringPiece& signature, - PointerSize pointer_size) { - // Check the current class before checking the interfaces. - ArtMethod* method = FindDeclaredVirtualMethod(name, signature, pointer_size); - if (method != nullptr) { - return method; - } - - int32_t iftable_count = GetIfTableCount(); - ObjPtr<IfTable> iftable = GetIfTable(); - for (int32_t i = 0; i < iftable_count; ++i) { - method = iftable->GetInterface(i)->FindDeclaredVirtualMethod(name, signature, pointer_size); - if (method != nullptr) { - return method; +template <typename SignatureType> +static inline ArtMethod* FindInterfaceMethodWithSignature(ObjPtr<Class> klass, + const StringPiece& name, + const SignatureType& signature, + PointerSize pointer_size) + REQUIRES_SHARED(Locks::mutator_lock_) { + // If the current class is not an interface, skip the search of its declared methods; + // such lookup is used only to distinguish between IncompatibleClassChangeError and + // NoSuchMethodError and the caller has already tried to search methods in the class. + if (LIKELY(klass->IsInterface())) { + // Search declared methods, both direct and virtual. + // (This lookup is used also for invoke-static on interface classes.) + for (ArtMethod& method : klass->GetDeclaredMethodsSlice(pointer_size)) { + if (method.GetName() == name && method.GetSignature() == signature) { + return &method; + } + } + } + + // TODO: If there is a unique maximally-specific non-abstract superinterface method, + // we should return it, otherwise an arbitrary one can be returned. + ObjPtr<IfTable> iftable = klass->GetIfTable(); + for (int32_t i = 0, iftable_count = iftable->Count(); i < iftable_count; ++i) { + ObjPtr<Class> iface = iftable->GetInterface(i); + for (ArtMethod& method : iface->GetVirtualMethodsSlice(pointer_size)) { + if (method.GetName() == name && method.GetSignature() == signature) { + return &method; + } + } + } + + // Then search for public non-static methods in the java.lang.Object. + if (LIKELY(klass->IsInterface())) { + ObjPtr<Class> object_class = klass->GetSuperClass(); + DCHECK(object_class->IsObjectClass()); + for (ArtMethod& method : object_class->GetDeclaredMethodsSlice(pointer_size)) { + if (method.IsPublic() && !method.IsStatic() && + method.GetName() == name && method.GetSignature() == signature) { + return &method; + } } } return nullptr; } ArtMethod* Class::FindInterfaceMethod(const StringPiece& name, + const StringPiece& signature, + PointerSize pointer_size) { + return FindInterfaceMethodWithSignature(this, name, signature, pointer_size); +} + +ArtMethod* Class::FindInterfaceMethod(const StringPiece& name, const Signature& signature, PointerSize pointer_size) { - // Check the current class before checking the interfaces. - ArtMethod* method = FindDeclaredVirtualMethod(name, signature, pointer_size); - if (method != nullptr) { - return method; - } - - int32_t iftable_count = GetIfTableCount(); - ObjPtr<IfTable> iftable = GetIfTable(); - for (int32_t i = 0; i < iftable_count; ++i) { - method = iftable->GetInterface(i)->FindDeclaredVirtualMethod(name, signature, pointer_size); - if (method != nullptr) { - return method; - } - } - return nullptr; + return FindInterfaceMethodWithSignature(this, name, signature, pointer_size); } ArtMethod* Class::FindInterfaceMethod(ObjPtr<DexCache> dex_cache, uint32_t dex_method_idx, PointerSize pointer_size) { - // Check the current class before checking the interfaces. - ArtMethod* method = FindDeclaredVirtualMethod(dex_cache, dex_method_idx, pointer_size); - if (method != nullptr) { - return method; - } - - int32_t iftable_count = GetIfTableCount(); - ObjPtr<IfTable> iftable = GetIfTable(); - for (int32_t i = 0; i < iftable_count; ++i) { - method = iftable->GetInterface(i)->FindDeclaredVirtualMethod( - dex_cache, dex_method_idx, pointer_size); - if (method != nullptr) { - return method; - } - } - return nullptr; + // We always search by name and signature, ignoring the type index in the MethodId. + const DexFile& dex_file = *dex_cache->GetDexFile(); + const DexFile::MethodId& method_id = dex_file.GetMethodId(dex_method_idx); + StringPiece name = dex_file.StringDataByIdx(method_id.name_idx_); + const Signature signature = dex_file.GetMethodSignature(method_id); + return FindInterfaceMethod(name, signature, pointer_size); } -ArtMethod* Class::FindDeclaredDirectMethod(const StringPiece& name, - const StringPiece& signature, - PointerSize pointer_size) { - for (auto& method : GetDirectMethods(pointer_size)) { - if (name == method.GetName() && method.GetSignature() == signature) { - return &method; +static inline bool IsInheritedMethod(ObjPtr<mirror::Class> klass, + ObjPtr<mirror::Class> declaring_class, + ArtMethod& method) + REQUIRES_SHARED(Locks::mutator_lock_) { + DCHECK_EQ(declaring_class, method.GetDeclaringClass()); + DCHECK_NE(klass, declaring_class); + DCHECK(klass->IsArrayClass() ? declaring_class->IsObjectClass() + : klass->IsSubClass(declaring_class)); + uint32_t access_flags = method.GetAccessFlags(); + if ((access_flags & (kAccPublic | kAccProtected)) != 0) { + return true; + } + if ((access_flags & kAccPrivate) != 0) { + return false; + } + for (; klass != declaring_class; klass = klass->GetSuperClass()) { + if (!klass->IsInSamePackage(declaring_class)) { + return false; } } - return nullptr; + return true; } -ArtMethod* Class::FindDeclaredDirectMethod(const StringPiece& name, - const Signature& signature, - PointerSize pointer_size) { - for (auto& method : GetDirectMethods(pointer_size)) { - if (name == method.GetName() && signature == method.GetSignature()) { +template <typename SignatureType> +static inline ArtMethod* FindClassMethodWithSignature(ObjPtr<Class> this_klass, + const StringPiece& name, + const SignatureType& signature, + PointerSize pointer_size) + REQUIRES_SHARED(Locks::mutator_lock_) { + // Search declared methods first. + for (ArtMethod& method : this_klass->GetDeclaredMethodsSlice(pointer_size)) { + ArtMethod* np_method = method.GetInterfaceMethodIfProxy(pointer_size); + if (np_method->GetName() == name && np_method->GetSignature() == signature) { return &method; } } - return nullptr; -} -ArtMethod* Class::FindDeclaredDirectMethod(ObjPtr<DexCache> dex_cache, - uint32_t dex_method_idx, - PointerSize pointer_size) { - if (GetDexCache() == dex_cache) { - for (auto& method : GetDirectMethods(pointer_size)) { - if (method.GetDexMethodIndex() == dex_method_idx) { - return &method; + // Then search the superclass chain. If we find an inherited method, return it. + // If we find a method that's not inherited because of access restrictions, + // try to find a method inherited from an interface in copied methods. + ObjPtr<Class> klass = this_klass->GetSuperClass(); + ArtMethod* uninherited_method = nullptr; + for (; klass != nullptr; klass = klass->GetSuperClass()) { + DCHECK(!klass->IsProxyClass()); + for (ArtMethod& method : klass->GetDeclaredMethodsSlice(pointer_size)) { + if (method.GetName() == name && method.GetSignature() == signature) { + if (IsInheritedMethod(this_klass, klass, method)) { + return &method; + } + uninherited_method = &method; + break; } } + if (uninherited_method != nullptr) { + break; + } } - return nullptr; -} -ArtMethod* Class::FindDirectMethod(const StringPiece& name, - const StringPiece& signature, - PointerSize pointer_size) { - for (ObjPtr<Class> klass = this; klass != nullptr; klass = klass->GetSuperClass()) { - ArtMethod* method = klass->FindDeclaredDirectMethod(name, signature, pointer_size); - if (method != nullptr) { - return method; + // Then search copied methods. + // If we found a method that's not inherited, stop the search in its declaring class. + ObjPtr<Class> end_klass = klass; + DCHECK_EQ(uninherited_method != nullptr, end_klass != nullptr); + klass = this_klass; + if (UNLIKELY(klass->IsProxyClass())) { + DCHECK(klass->GetCopiedMethodsSlice(pointer_size).empty()); + klass = klass->GetSuperClass(); + } + for (; klass != end_klass; klass = klass->GetSuperClass()) { + DCHECK(!klass->IsProxyClass()); + for (ArtMethod& method : klass->GetCopiedMethodsSlice(pointer_size)) { + if (method.GetName() == name && method.GetSignature() == signature) { + return &method; // No further check needed, copied methods are inherited by definition. + } } } - return nullptr; + return uninherited_method; // Return the `uninherited_method` if any. } -ArtMethod* Class::FindDirectMethod(const StringPiece& name, - const Signature& signature, - PointerSize pointer_size) { - for (ObjPtr<Class> klass = this; klass != nullptr; klass = klass->GetSuperClass()) { - ArtMethod* method = klass->FindDeclaredDirectMethod(name, signature, pointer_size); - if (method != nullptr) { - return method; - } - } - return nullptr; + +ArtMethod* Class::FindClassMethod(const StringPiece& name, + const StringPiece& signature, + PointerSize pointer_size) { + return FindClassMethodWithSignature(this, name, signature, pointer_size); +} + +ArtMethod* Class::FindClassMethod(const StringPiece& name, + const Signature& signature, + PointerSize pointer_size) { + return FindClassMethodWithSignature(this, name, signature, pointer_size); } -ArtMethod* Class::FindDirectMethod(ObjPtr<DexCache> dex_cache, - uint32_t dex_method_idx, - PointerSize pointer_size) { - for (ObjPtr<Class> klass = this; klass != nullptr; klass = klass->GetSuperClass()) { - ArtMethod* method = klass->FindDeclaredDirectMethod(dex_cache, dex_method_idx, pointer_size); - if (method != nullptr) { - return method; +ArtMethod* Class::FindClassMethod(ObjPtr<DexCache> dex_cache, + uint32_t dex_method_idx, + PointerSize pointer_size) { + // FIXME: Hijacking a proxy class by a custom class loader can break this assumption. + DCHECK(!IsProxyClass()); + + // First try to find a declared method by dex_method_idx if we have a dex_cache match. + ObjPtr<DexCache> this_dex_cache = GetDexCache(); + if (this_dex_cache == dex_cache) { + // Lookup is always performed in the class referenced by the MethodId. + DCHECK_EQ(dex_type_idx_, GetDexFile().GetMethodId(dex_method_idx).class_idx_.index_); + for (ArtMethod& method : GetDeclaredMethodsSlice(pointer_size)) { + if (method.GetDexMethodIndex() == dex_method_idx) { + return &method; + } + } + } + // If not found, we need to search by name and signature. + const DexFile& dex_file = *dex_cache->GetDexFile(); + const DexFile::MethodId& method_id = dex_file.GetMethodId(dex_method_idx); + const Signature signature = dex_file.GetMethodSignature(method_id); + StringPiece name; // Delay strlen() until actually needed. + // If we do not have a dex_cache match, try to find the declared method in this class now. + if (this_dex_cache != dex_cache && !GetDeclaredMethodsSlice(pointer_size).empty()) { + DCHECK(name.empty()); + name = dex_file.StringDataByIdx(method_id.name_idx_); + for (ArtMethod& method : GetDeclaredMethodsSlice(pointer_size)) { + if (method.GetName() == name && method.GetSignature() == signature) { + return &method; + } } } - return nullptr; -} -ArtMethod* Class::FindDeclaredDirectMethodByName(const StringPiece& name, - PointerSize pointer_size) { - for (auto& method : GetDirectMethods(pointer_size)) { - ArtMethod* const np_method = method.GetInterfaceMethodIfProxy(pointer_size); - if (name == np_method->GetName()) { - return &method; + // Then search the superclass chain. If we find an inherited method, return it. + // If we find a method that's not inherited because of access restrictions, + // try to find a method inherited from an interface in copied methods. + ArtMethod* uninherited_method = nullptr; + ObjPtr<Class> klass = GetSuperClass(); + for (; klass != nullptr; klass = klass->GetSuperClass()) { + ArtMethod* candidate_method = nullptr; + ArraySlice<ArtMethod> declared_methods = klass->GetDeclaredMethodsSlice(pointer_size); + if (klass->GetDexCache() == dex_cache) { + // Matching dex_cache. We cannot compare the `dex_method_idx` anymore because + // the type index differs, so compare the name index and proto index. + for (ArtMethod& method : declared_methods) { + const DexFile::MethodId& cmp_method_id = dex_file.GetMethodId(method.GetDexMethodIndex()); + if (cmp_method_id.name_idx_ == method_id.name_idx_ && + cmp_method_id.proto_idx_ == method_id.proto_idx_) { + candidate_method = &method; + break; + } + } + } else { + if (!declared_methods.empty() && name.empty()) { + name = dex_file.StringDataByIdx(method_id.name_idx_); + } + for (ArtMethod& method : declared_methods) { + if (method.GetName() == name && method.GetSignature() == signature) { + candidate_method = &method; + break; + } + } + } + if (candidate_method != nullptr) { + if (IsInheritedMethod(this, klass, *candidate_method)) { + return candidate_method; + } else { + uninherited_method = candidate_method; + break; + } } } - return nullptr; -} -// TODO These should maybe be changed to be named FindOwnedVirtualMethod or something similar -// because they do not only find 'declared' methods and will return copied methods. This behavior is -// desired and correct but the naming can lead to confusion because in the java language declared -// excludes interface methods which might be found by this. -ArtMethod* Class::FindDeclaredVirtualMethod(const StringPiece& name, - const StringPiece& signature, - PointerSize pointer_size) { - for (auto& method : GetVirtualMethods(pointer_size)) { - ArtMethod* const np_method = method.GetInterfaceMethodIfProxy(pointer_size); - if (name == np_method->GetName() && np_method->GetSignature() == signature) { - return &method; + // Then search copied methods. + // If we found a method that's not inherited, stop the search in its declaring class. + ObjPtr<Class> end_klass = klass; + DCHECK_EQ(uninherited_method != nullptr, end_klass != nullptr); + // After we have searched the declared methods of the super-class chain, + // search copied methods which can contain methods from interfaces. + for (klass = this; klass != end_klass; klass = klass->GetSuperClass()) { + ArraySlice<ArtMethod> copied_methods = klass->GetCopiedMethodsSlice(pointer_size); + if (!copied_methods.empty() && name.empty()) { + name = dex_file.StringDataByIdx(method_id.name_idx_); + } + for (ArtMethod& method : copied_methods) { + if (method.GetName() == name && method.GetSignature() == signature) { + return &method; // No further check needed, copied methods are inherited by definition. + } } } - return nullptr; + return uninherited_method; // Return the `uninherited_method` if any. } -ArtMethod* Class::FindDeclaredVirtualMethod(const StringPiece& name, - const Signature& signature, - PointerSize pointer_size) { - for (auto& method : GetVirtualMethods(pointer_size)) { - ArtMethod* const np_method = method.GetInterfaceMethodIfProxy(pointer_size); - if (name == np_method->GetName() && signature == np_method->GetSignature()) { +ArtMethod* Class::FindConstructor(const StringPiece& signature, PointerSize pointer_size) { + // Internal helper, never called on proxy classes. We can skip GetInterfaceMethodIfProxy(). + DCHECK(!IsProxyClass()); + StringPiece name("<init>"); + for (ArtMethod& method : GetDirectMethodsSliceUnchecked(pointer_size)) { + if (method.GetName() == name && method.GetSignature() == signature) { return &method; } } return nullptr; } -ArtMethod* Class::FindDeclaredVirtualMethod(ObjPtr<DexCache> dex_cache, - uint32_t dex_method_idx, - PointerSize pointer_size) { - if (GetDexCache() == dex_cache) { - for (auto& method : GetDeclaredVirtualMethods(pointer_size)) { - if (method.GetDexMethodIndex() == dex_method_idx) { - return &method; - } +ArtMethod* Class::FindDeclaredDirectMethodByName(const StringPiece& name, + PointerSize pointer_size) { + for (auto& method : GetDirectMethods(pointer_size)) { + ArtMethod* const np_method = method.GetInterfaceMethodIfProxy(pointer_size); + if (name == np_method->GetName()) { + return &method; } } return nullptr; @@ -591,42 +682,6 @@ ArtMethod* Class::FindDeclaredVirtualMethodByName(const StringPiece& name, return nullptr; } -ArtMethod* Class::FindVirtualMethod(const StringPiece& name, - const StringPiece& signature, - PointerSize pointer_size) { - for (ObjPtr<Class> klass = this; klass != nullptr; klass = klass->GetSuperClass()) { - ArtMethod* method = klass->FindDeclaredVirtualMethod(name, signature, pointer_size); - if (method != nullptr) { - return method; - } - } - return nullptr; -} - -ArtMethod* Class::FindVirtualMethod(const StringPiece& name, - const Signature& signature, - PointerSize pointer_size) { - for (ObjPtr<Class> klass = this; klass != nullptr; klass = klass->GetSuperClass()) { - ArtMethod* method = klass->FindDeclaredVirtualMethod(name, signature, pointer_size); - if (method != nullptr) { - return method; - } - } - return nullptr; -} - -ArtMethod* Class::FindVirtualMethod(ObjPtr<DexCache> dex_cache, - uint32_t dex_method_idx, - PointerSize pointer_size) { - for (ObjPtr<Class> klass = this; klass != nullptr; klass = klass->GetSuperClass()) { - ArtMethod* method = klass->FindDeclaredVirtualMethod(dex_cache, dex_method_idx, pointer_size); - if (method != nullptr) { - return method; - } - } - return nullptr; -} - ArtMethod* Class::FindVirtualMethodForInterfaceSuper(ArtMethod* method, PointerSize pointer_size) { DCHECK(method->GetDeclaringClass()->IsInterface()); DCHECK(IsInterface()) << "Should only be called on a interface class"; diff --git a/runtime/mirror/class.h b/runtime/mirror/class.h index e516a0636b..250604b2ef 100644 --- a/runtime/mirror/class.h +++ b/runtime/mirror/class.h @@ -20,11 +20,11 @@ #include "base/bit_utils.h" #include "base/enums.h" #include "base/iteration_range.h" +#include "class_flags.h" #include "dex_file.h" #include "dex_file_types.h" -#include "class_flags.h" -#include "gc_root.h" #include "gc/allocator_type.h" +#include "gc_root.h" #include "imtable.h" #include "invoke_type.h" #include "modifiers.h" @@ -915,6 +915,13 @@ class MANAGED Class FINAL : public Object { ArtMethod* FindVirtualMethodForVirtualOrInterface(ArtMethod* method, PointerSize pointer_size) REQUIRES_SHARED(Locks::mutator_lock_); + // Find a method with the given name and signature in an interface class. + // + // Search for the method declared in the class, then search for a method declared in any + // superinterface, then search the superclass java.lang.Object (implicitly declared methods + // in an interface without superinterfaces, see JLS 9.2, can be inherited, see JLS 9.4.1). + // TODO: Implement search for a unique maximally-specific non-abstract superinterface method. + ArtMethod* FindInterfaceMethod(const StringPiece& name, const StringPiece& signature, PointerSize pointer_size) @@ -930,49 +937,46 @@ class MANAGED Class FINAL : public Object { PointerSize pointer_size) REQUIRES_SHARED(Locks::mutator_lock_); - ArtMethod* FindDeclaredDirectMethod(const StringPiece& name, - const StringPiece& signature, - PointerSize pointer_size) - REQUIRES_SHARED(Locks::mutator_lock_); - - ArtMethod* FindDeclaredDirectMethod(const StringPiece& name, - const Signature& signature, - PointerSize pointer_size) - REQUIRES_SHARED(Locks::mutator_lock_); - - ArtMethod* FindDeclaredDirectMethod(ObjPtr<DexCache> dex_cache, - uint32_t dex_method_idx, - PointerSize pointer_size) - REQUIRES_SHARED(Locks::mutator_lock_); - - ArtMethod* FindDirectMethod(const StringPiece& name, - const StringPiece& signature, - PointerSize pointer_size) - REQUIRES_SHARED(Locks::mutator_lock_); - - ArtMethod* FindDirectMethod(const StringPiece& name, - const Signature& signature, - PointerSize pointer_size) - REQUIRES_SHARED(Locks::mutator_lock_); + // Find a method with the given name and signature in a non-interface class. + // + // Search for the method in the class, following the JLS rules which conflict with the RI + // in some cases. The JLS says that inherited methods are searched (JLS 15.12.2.1) and + // these can come from a superclass or a superinterface (JLS 8.4.8). We perform the + // following search: + // 1. Search the methods declared directly in the class. If we find a method with the + // given name and signature, return that method. + // 2. Search the methods declared in superclasses until we find a method with the given + // signature or complete the search in java.lang.Object. If we find a method with the + // given name and signature, check if it's been inherited by the class where we're + // performing the lookup (qualifying type). If it's inherited, return it. Otherwise, + // just remember the method and its declaring class and proceed to step 3. + // 3. Search "copied" methods (containing methods inherited from interfaces) in the class + // and its superclass chain. If we found a method in step 2 (which was not inherited, + // otherwise we would not be performing step 3), end the search when we reach its + // declaring class, otherwise search the entire superclass chain. If we find a method + // with the given name and signature, return that method. + // 4. Return the method found in step 2 if any (not inherited), or null. + // + // It's the responsibility of the caller to throw exceptions if the returned method (or null) + // does not satisfy the request. Special consideration should be given to the case where this + // function returns a method that's not inherited (found in step 2, returned in step 4). - ArtMethod* FindDirectMethod(ObjPtr<DexCache> dex_cache, - uint32_t dex_method_idx, - PointerSize pointer_size) + ArtMethod* FindClassMethod(const StringPiece& name, + const StringPiece& signature, + PointerSize pointer_size) REQUIRES_SHARED(Locks::mutator_lock_); - ArtMethod* FindDeclaredVirtualMethod(const StringPiece& name, - const StringPiece& signature, - PointerSize pointer_size) + ArtMethod* FindClassMethod(const StringPiece& name, + const Signature& signature, + PointerSize pointer_size) REQUIRES_SHARED(Locks::mutator_lock_); - ArtMethod* FindDeclaredVirtualMethod(const StringPiece& name, - const Signature& signature, - PointerSize pointer_size) + ArtMethod* FindClassMethod(ObjPtr<DexCache> dex_cache, + uint32_t dex_method_idx, + PointerSize pointer_size) REQUIRES_SHARED(Locks::mutator_lock_); - ArtMethod* FindDeclaredVirtualMethod(ObjPtr<DexCache> dex_cache, - uint32_t dex_method_idx, - PointerSize pointer_size) + ArtMethod* FindConstructor(const StringPiece& signature, PointerSize pointer_size) REQUIRES_SHARED(Locks::mutator_lock_); ArtMethod* FindDeclaredVirtualMethodByName(const StringPiece& name, @@ -983,21 +987,6 @@ class MANAGED Class FINAL : public Object { PointerSize pointer_size) REQUIRES_SHARED(Locks::mutator_lock_); - ArtMethod* FindVirtualMethod(const StringPiece& name, - const StringPiece& signature, - PointerSize pointer_size) - REQUIRES_SHARED(Locks::mutator_lock_); - - ArtMethod* FindVirtualMethod(const StringPiece& name, - const Signature& signature, - PointerSize pointer_size) - REQUIRES_SHARED(Locks::mutator_lock_); - - ArtMethod* FindVirtualMethod(ObjPtr<DexCache> dex_cache, - uint32_t dex_method_idx, - PointerSize pointer_size) - REQUIRES_SHARED(Locks::mutator_lock_); - ArtMethod* FindClassInitializer(PointerSize pointer_size) REQUIRES_SHARED(Locks::mutator_lock_); bool HasDefaultMethods() REQUIRES_SHARED(Locks::mutator_lock_) { diff --git a/runtime/mirror/class_loader.h b/runtime/mirror/class_loader.h index 381d96b541..f25f18fce8 100644 --- a/runtime/mirror/class_loader.h +++ b/runtime/mirror/class_loader.h @@ -18,9 +18,9 @@ #define ART_RUNTIME_MIRROR_CLASS_LOADER_H_ #include "base/mutex.h" +#include "obj_ptr.h" #include "object.h" #include "object_reference.h" -#include "obj_ptr.h" namespace art { diff --git a/runtime/mirror/dex_cache-inl.h b/runtime/mirror/dex_cache-inl.h index 18e22ef8f8..8b11c1290d 100644 --- a/runtime/mirror/dex_cache-inl.h +++ b/runtime/mirror/dex_cache-inl.h @@ -26,13 +26,13 @@ #include "base/logging.h" #include "class_linker.h" #include "dex_file.h" -#include "gc_root.h" #include "gc/heap-inl.h" -#include "mirror/class.h" +#include "gc_root.h" #include "mirror/call_site.h" +#include "mirror/class.h" #include "mirror/method_type.h" -#include "runtime.h" #include "obj_ptr.h" +#include "runtime.h" #include <atomic> @@ -208,24 +208,38 @@ inline void DexCache::ClearResolvedField(uint32_t field_idx, PointerSize ptr_siz } } +inline uint32_t DexCache::MethodSlotIndex(uint32_t method_idx) { + DCHECK_LT(method_idx, GetDexFile()->NumMethodIds()); + const uint32_t slot_idx = method_idx % kDexCacheMethodCacheSize; + DCHECK_LT(slot_idx, NumResolvedMethods()); + return slot_idx; +} + inline ArtMethod* DexCache::GetResolvedMethod(uint32_t method_idx, PointerSize ptr_size) { DCHECK_EQ(Runtime::Current()->GetClassLinker()->GetImagePointerSize(), ptr_size); - DCHECK_LT(method_idx, NumResolvedMethods()); // NOTE: Unchecked, i.e. not throwing AIOOB. - ArtMethod* method = GetElementPtrSize<ArtMethod*>(GetResolvedMethods(), method_idx, ptr_size); - // Hide resolution trampoline methods from the caller - if (method != nullptr && method->IsRuntimeMethod()) { - DCHECK_EQ(method, Runtime::Current()->GetResolutionMethod()); - return nullptr; - } - return method; + auto pair = GetNativePairPtrSize(GetResolvedMethods(), MethodSlotIndex(method_idx), ptr_size); + return pair.GetObjectForIndex(method_idx); } inline void DexCache::SetResolvedMethod(uint32_t method_idx, ArtMethod* method, PointerSize ptr_size) { DCHECK_EQ(Runtime::Current()->GetClassLinker()->GetImagePointerSize(), ptr_size); - DCHECK_LT(method_idx, NumResolvedMethods()); // NOTE: Unchecked, i.e. not throwing AIOOB. - SetElementPtrSize(GetResolvedMethods(), method_idx, method, ptr_size); + DCHECK(method != nullptr); + MethodDexCachePair pair(method, method_idx); + SetNativePairPtrSize(GetResolvedMethods(), MethodSlotIndex(method_idx), pair, ptr_size); +} + +inline void DexCache::ClearResolvedMethod(uint32_t method_idx, PointerSize ptr_size) { + DCHECK_EQ(Runtime::Current()->GetClassLinker()->GetImagePointerSize(), ptr_size); + uint32_t slot_idx = MethodSlotIndex(method_idx); + auto* resolved_methods = GetResolvedMethods(); + // This is racy but should only be called from the single-threaded ImageWriter. + DCHECK(Runtime::Current()->IsAotCompiler()); + if (GetNativePairPtrSize(resolved_methods, slot_idx, ptr_size).index == method_idx) { + MethodDexCachePair cleared(nullptr, MethodDexCachePair::InvalidIndexForSlot(slot_idx)); + SetNativePairPtrSize(resolved_methods, slot_idx, cleared, ptr_size); + } } template <typename PtrType> diff --git a/runtime/mirror/dex_cache.cc b/runtime/mirror/dex_cache.cc index 96e347576a..f6f20ba3ab 100644 --- a/runtime/mirror/dex_cache.cc +++ b/runtime/mirror/dex_cache.cc @@ -24,8 +24,8 @@ #include "globals.h" #include "linear_alloc.h" #include "oat_file.h" -#include "object.h" #include "object-inl.h" +#include "object.h" #include "object_array-inl.h" #include "runtime.h" #include "string.h" @@ -61,14 +61,14 @@ void DexCache::InitializeDexCache(Thread* self, : reinterpret_cast<uint8_t*>(linear_alloc->Alloc(self, layout.Size())); } - mirror::StringDexCacheType* strings = (dex_file->NumStringIds() == 0u) ? nullptr : - reinterpret_cast<mirror::StringDexCacheType*>(raw_arrays + layout.StringsOffset()); - mirror::TypeDexCacheType* types = (dex_file->NumTypeIds() == 0u) ? nullptr : - reinterpret_cast<mirror::TypeDexCacheType*>(raw_arrays + layout.TypesOffset()); - ArtMethod** methods = (dex_file->NumMethodIds() == 0u) ? nullptr : - reinterpret_cast<ArtMethod**>(raw_arrays + layout.MethodsOffset()); - mirror::FieldDexCacheType* fields = (dex_file->NumFieldIds() == 0u) ? nullptr : - reinterpret_cast<mirror::FieldDexCacheType*>(raw_arrays + layout.FieldsOffset()); + StringDexCacheType* strings = (dex_file->NumStringIds() == 0u) ? nullptr : + reinterpret_cast<StringDexCacheType*>(raw_arrays + layout.StringsOffset()); + TypeDexCacheType* types = (dex_file->NumTypeIds() == 0u) ? nullptr : + reinterpret_cast<TypeDexCacheType*>(raw_arrays + layout.TypesOffset()); + MethodDexCacheType* methods = (dex_file->NumMethodIds() == 0u) ? nullptr : + reinterpret_cast<MethodDexCacheType*>(raw_arrays + layout.MethodsOffset()); + FieldDexCacheType* fields = (dex_file->NumFieldIds() == 0u) ? nullptr : + reinterpret_cast<FieldDexCacheType*>(raw_arrays + layout.FieldsOffset()); size_t num_strings = kDexCacheStringCacheSize; if (dex_file->NumStringIds() < num_strings) { @@ -82,6 +82,10 @@ void DexCache::InitializeDexCache(Thread* self, if (dex_file->NumFieldIds() < num_fields) { num_fields = dex_file->NumFieldIds(); } + size_t num_methods = kDexCacheMethodCacheSize; + if (dex_file->NumMethodIds() < num_methods) { + num_methods = dex_file->NumMethodIds(); + } // Note that we allocate the method type dex caches regardless of this flag, // and we make sure here that they're not used by the runtime. This is in the @@ -105,7 +109,7 @@ void DexCache::InitializeDexCache(Thread* self, GcRoot<mirror::CallSite>* call_sites = (dex_file->NumCallSiteIds() == 0) ? nullptr - : reinterpret_cast<GcRoot<mirror::CallSite>*>(raw_arrays + layout.CallSitesOffset()); + : reinterpret_cast<GcRoot<CallSite>*>(raw_arrays + layout.CallSitesOffset()); DCHECK_ALIGNED(raw_arrays, alignof(StringDexCacheType)) << "Expected raw_arrays to align to StringDexCacheType."; @@ -125,8 +129,9 @@ void DexCache::InitializeDexCache(Thread* self, CHECK_EQ(types[i].load(std::memory_order_relaxed).index, 0u); CHECK(types[i].load(std::memory_order_relaxed).object.IsNull()); } - for (size_t i = 0; i < dex_file->NumMethodIds(); ++i) { - CHECK(GetElementPtrSize(methods, i, image_pointer_size) == nullptr); + for (size_t i = 0; i < num_methods; ++i) { + CHECK_EQ(GetNativePairPtrSize(methods, i, image_pointer_size).index, 0u); + CHECK(GetNativePairPtrSize(methods, i, image_pointer_size).object == nullptr); } for (size_t i = 0; i < num_fields; ++i) { CHECK_EQ(GetNativePairPtrSize(fields, i, image_pointer_size).index, 0u); @@ -149,6 +154,9 @@ void DexCache::InitializeDexCache(Thread* self, if (fields != nullptr) { mirror::FieldDexCachePair::Initialize(fields, image_pointer_size); } + if (methods != nullptr) { + mirror::MethodDexCachePair::Initialize(methods, image_pointer_size); + } if (method_types != nullptr) { mirror::MethodTypeDexCachePair::Initialize(method_types); } @@ -159,14 +167,13 @@ void DexCache::InitializeDexCache(Thread* self, types, num_types, methods, - dex_file->NumMethodIds(), + num_methods, fields, num_fields, method_types, num_method_types, call_sites, - dex_file->NumCallSiteIds(), - image_pointer_size); + dex_file->NumCallSiteIds()); } void DexCache::Init(const DexFile* dex_file, @@ -175,15 +182,14 @@ void DexCache::Init(const DexFile* dex_file, uint32_t num_strings, TypeDexCacheType* resolved_types, uint32_t num_resolved_types, - ArtMethod** resolved_methods, + MethodDexCacheType* resolved_methods, uint32_t num_resolved_methods, FieldDexCacheType* resolved_fields, uint32_t num_resolved_fields, MethodTypeDexCacheType* resolved_method_types, uint32_t num_resolved_method_types, GcRoot<CallSite>* resolved_call_sites, - uint32_t num_resolved_call_sites, - PointerSize pointer_size) { + uint32_t num_resolved_call_sites) { CHECK(dex_file != nullptr); CHECK(location != nullptr); CHECK_EQ(num_strings != 0u, strings != nullptr); @@ -207,31 +213,13 @@ void DexCache::Init(const DexFile* dex_file, SetField32<false>(NumResolvedFieldsOffset(), num_resolved_fields); SetField32<false>(NumResolvedMethodTypesOffset(), num_resolved_method_types); SetField32<false>(NumResolvedCallSitesOffset(), num_resolved_call_sites); - - Runtime* const runtime = Runtime::Current(); - if (runtime->HasResolutionMethod()) { - // Initialize the resolve methods array to contain trampolines for resolution. - Fixup(runtime->GetResolutionMethod(), pointer_size); - } -} - -void DexCache::Fixup(ArtMethod* trampoline, PointerSize pointer_size) { - // Fixup the resolve methods array to contain trampoline for resolution. - CHECK(trampoline != nullptr); - CHECK(trampoline->IsRuntimeMethod()); - auto* resolved_methods = GetResolvedMethods(); - for (size_t i = 0, length = NumResolvedMethods(); i < length; i++) { - if (GetElementPtrSize<ArtMethod*>(resolved_methods, i, pointer_size) == nullptr) { - SetElementPtrSize(resolved_methods, i, trampoline, pointer_size); - } - } } void DexCache::SetLocation(ObjPtr<mirror::String> location) { SetFieldObject<false>(OFFSET_OF_OBJECT_MEMBER(DexCache, location_), location); } -#if !defined(__aarch64__) && !defined(__x86_64__) +#if !defined(__aarch64__) && !defined(__x86_64__) && !defined(__mips__) static pthread_mutex_t dex_cache_slow_atomic_mutex = PTHREAD_MUTEX_INITIALIZER; DexCache::ConversionPair64 DexCache::AtomicLoadRelaxed16B(std::atomic<ConversionPair64>* target) { diff --git a/runtime/mirror/dex_cache.h b/runtime/mirror/dex_cache.h index cf570b8be0..f75786b521 100644 --- a/runtime/mirror/dex_cache.h +++ b/runtime/mirror/dex_cache.h @@ -129,6 +129,9 @@ using StringDexCacheType = std::atomic<StringDexCachePair>; using FieldDexCachePair = NativeDexCachePair<ArtField>; using FieldDexCacheType = std::atomic<FieldDexCachePair>; +using MethodDexCachePair = NativeDexCachePair<ArtMethod>; +using MethodDexCacheType = std::atomic<MethodDexCachePair>; + using MethodTypeDexCachePair = DexCachePair<MethodType>; using MethodTypeDexCacheType = std::atomic<MethodTypeDexCachePair>; @@ -153,6 +156,11 @@ class MANAGED DexCache FINAL : public Object { static_assert(IsPowerOfTwo(kDexCacheFieldCacheSize), "Field dex cache size is not a power of 2."); + // Size of method dex cache. Needs to be a power of 2 for entrypoint assumptions to hold. + static constexpr size_t kDexCacheMethodCacheSize = 1024; + static_assert(IsPowerOfTwo(kDexCacheMethodCacheSize), + "Method dex cache size is not a power of 2."); + // Size of method type dex cache. Needs to be a power of 2 for entrypoint assumptions // to hold. static constexpr size_t kDexCacheMethodTypeCacheSize = 1024; @@ -171,6 +179,10 @@ class MANAGED DexCache FINAL : public Object { return kDexCacheFieldCacheSize; } + static constexpr size_t StaticMethodSize() { + return kDexCacheMethodCacheSize; + } + static constexpr size_t StaticMethodTypeSize() { return kDexCacheMethodTypeCacheSize; } @@ -189,9 +201,6 @@ class MANAGED DexCache FINAL : public Object { REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(Locks::dex_lock_); - void Fixup(ArtMethod* trampoline, PointerSize pointer_size) - REQUIRES_SHARED(Locks::mutator_lock_); - template <ReadBarrierOption kReadBarrierOption = kWithReadBarrier, typename Visitor> void FixupStrings(StringDexCacheType* dest, const Visitor& visitor) REQUIRES_SHARED(Locks::mutator_lock_); @@ -284,6 +293,8 @@ class MANAGED DexCache FINAL : public Object { ArtMethod* resolved, PointerSize ptr_size) REQUIRES_SHARED(Locks::mutator_lock_); + ALWAYS_INLINE void ClearResolvedMethod(uint32_t method_idx, PointerSize ptr_size) + REQUIRES_SHARED(Locks::mutator_lock_); // Pointer sized variant, used for patching. ALWAYS_INLINE ArtField* GetResolvedField(uint32_t idx, PointerSize ptr_size) @@ -328,11 +339,11 @@ class MANAGED DexCache FINAL : public Object { SetFieldPtr<false>(ResolvedTypesOffset(), resolved_types); } - ArtMethod** GetResolvedMethods() ALWAYS_INLINE REQUIRES_SHARED(Locks::mutator_lock_) { - return GetFieldPtr<ArtMethod**>(ResolvedMethodsOffset()); + MethodDexCacheType* GetResolvedMethods() ALWAYS_INLINE REQUIRES_SHARED(Locks::mutator_lock_) { + return GetFieldPtr<MethodDexCacheType*>(ResolvedMethodsOffset()); } - void SetResolvedMethods(ArtMethod** resolved_methods) + void SetResolvedMethods(MethodDexCacheType* resolved_methods) ALWAYS_INLINE REQUIRES_SHARED(Locks::mutator_lock_) { SetFieldPtr<false>(ResolvedMethodsOffset(), resolved_methods); @@ -429,6 +440,7 @@ class MANAGED DexCache FINAL : public Object { uint32_t StringSlotIndex(dex::StringIndex string_idx) REQUIRES_SHARED(Locks::mutator_lock_); uint32_t TypeSlotIndex(dex::TypeIndex type_idx) REQUIRES_SHARED(Locks::mutator_lock_); uint32_t FieldSlotIndex(uint32_t field_idx) REQUIRES_SHARED(Locks::mutator_lock_); + uint32_t MethodSlotIndex(uint32_t method_idx) REQUIRES_SHARED(Locks::mutator_lock_); uint32_t MethodTypeSlotIndex(uint32_t proto_idx) REQUIRES_SHARED(Locks::mutator_lock_); private: @@ -438,15 +450,14 @@ class MANAGED DexCache FINAL : public Object { uint32_t num_strings, TypeDexCacheType* resolved_types, uint32_t num_resolved_types, - ArtMethod** resolved_methods, + MethodDexCacheType* resolved_methods, uint32_t num_resolved_methods, FieldDexCacheType* resolved_fields, uint32_t num_resolved_fields, MethodTypeDexCacheType* resolved_method_types, uint32_t num_resolved_method_types, GcRoot<CallSite>* resolved_call_sites, - uint32_t num_resolved_call_sites, - PointerSize pointer_size) + uint32_t num_resolved_call_sites) REQUIRES_SHARED(Locks::mutator_lock_); // std::pair<> is not trivially copyable and as such it is unsuitable for atomic operations, @@ -471,8 +482,8 @@ class MANAGED DexCache FINAL : public Object { REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(Locks::heap_bitmap_lock_); // Due to lack of 16-byte atomics support, we use hand-crafted routines. -#if defined(__aarch64__) - // 16-byte atomics are supported on aarch64. +#if defined(__aarch64__) || defined(__mips__) + // 16-byte atomics are supported on aarch64, mips and mips64. ALWAYS_INLINE static ConversionPair64 AtomicLoadRelaxed16B( std::atomic<ConversionPair64>* target) { return target->load(std::memory_order_relaxed); diff --git a/runtime/mirror/dex_cache_test.cc b/runtime/mirror/dex_cache_test.cc index 5b1ba8d010..a3d90355ac 100644 --- a/runtime/mirror/dex_cache_test.cc +++ b/runtime/mirror/dex_cache_test.cc @@ -21,10 +21,10 @@ #include "art_method-inl.h" #include "class_linker.h" #include "common_runtime_test.h" +#include "handle_scope-inl.h" #include "linear_alloc.h" #include "mirror/class_loader-inl.h" #include "mirror/dex_cache-inl.h" -#include "handle_scope-inl.h" #include "scoped_thread_state_change-inl.h" namespace art { @@ -54,7 +54,8 @@ TEST_F(DexCacheTest, Open) { || java_lang_dex_file_->NumStringIds() == dex_cache->NumStrings()); EXPECT_TRUE(dex_cache->StaticTypeSize() == dex_cache->NumResolvedTypes() || java_lang_dex_file_->NumTypeIds() == dex_cache->NumResolvedTypes()); - EXPECT_EQ(java_lang_dex_file_->NumMethodIds(), dex_cache->NumResolvedMethods()); + EXPECT_TRUE(dex_cache->StaticMethodSize() == dex_cache->NumResolvedMethods() + || java_lang_dex_file_->NumMethodIds() == dex_cache->NumResolvedMethods()); EXPECT_TRUE(dex_cache->StaticArtFieldSize() == dex_cache->NumResolvedFields() || java_lang_dex_file_->NumFieldIds() == dex_cache->NumResolvedFields()); EXPECT_TRUE(dex_cache->StaticMethodTypeSize() == dex_cache->NumResolvedMethodTypes() @@ -128,14 +129,18 @@ TEST_F(DexCacheMethodHandlesTest, TestResolvedMethodTypes) { hs.NewHandle(class_linker_->FindClass(soa.Self(), "LMethodTypes;", class_loader))); class_linker_->EnsureInitialized(soa.Self(), method_types, true, true); - ArtMethod* method1 = method_types->FindVirtualMethod( + ArtMethod* method1 = method_types->FindClassMethod( "method1", "(Ljava/lang/String;)Ljava/lang/String;", kRuntimePointerSize); - ArtMethod* method2 = method_types->FindVirtualMethod( + ASSERT_TRUE(method1 != nullptr); + ASSERT_FALSE(method1->IsDirect()); + ArtMethod* method2 = method_types->FindClassMethod( "method2", "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;", kRuntimePointerSize); + ASSERT_TRUE(method2 != nullptr); + ASSERT_FALSE(method2->IsDirect()); const DexFile& dex_file = *(method1->GetDexFile()); Handle<mirror::DexCache> dex_cache = hs.NewHandle( diff --git a/runtime/mirror/emulated_stack_frame.cc b/runtime/mirror/emulated_stack_frame.cc index be0eac05c9..a6129ccc5f 100644 --- a/runtime/mirror/emulated_stack_frame.cc +++ b/runtime/mirror/emulated_stack_frame.cc @@ -19,8 +19,8 @@ #include "class-inl.h" #include "gc_root-inl.h" #include "jvalue-inl.h" -#include "method_handles.h" #include "method_handles-inl.h" +#include "method_handles.h" #include "reflection-inl.h" namespace art { diff --git a/runtime/mirror/executable.cc b/runtime/mirror/executable.cc index 17c16a2c0b..fac33192e4 100644 --- a/runtime/mirror/executable.cc +++ b/runtime/mirror/executable.cc @@ -14,9 +14,10 @@ * limitations under the License. */ -#include "art_method-inl.h" #include "executable.h" +#include "art_method-inl.h" + namespace art { namespace mirror { diff --git a/runtime/mirror/field.cc b/runtime/mirror/field.cc index 54034c2bbf..b4d93b6d4d 100644 --- a/runtime/mirror/field.cc +++ b/runtime/mirror/field.cc @@ -18,8 +18,8 @@ #include "class-inl.h" #include "dex_cache-inl.h" -#include "object_array-inl.h" #include "object-inl.h" +#include "object_array-inl.h" namespace art { namespace mirror { diff --git a/runtime/mirror/method.h b/runtime/mirror/method.h index 205ea7a050..61332e3bd9 100644 --- a/runtime/mirror/method.h +++ b/runtime/mirror/method.h @@ -17,8 +17,8 @@ #ifndef ART_RUNTIME_MIRROR_METHOD_H_ #define ART_RUNTIME_MIRROR_METHOD_H_ -#include "gc_root.h" #include "executable.h" +#include "gc_root.h" namespace art { namespace mirror { diff --git a/runtime/mirror/method_handle_impl.h b/runtime/mirror/method_handle_impl.h index c598fa3876..f362d43b01 100644 --- a/runtime/mirror/method_handle_impl.h +++ b/runtime/mirror/method_handle_impl.h @@ -21,8 +21,8 @@ #include "art_method.h" #include "class.h" #include "gc_root.h" -#include "object.h" #include "method_type.h" +#include "object.h" namespace art { diff --git a/runtime/mirror/method_handles_lookup.cc b/runtime/mirror/method_handles_lookup.cc index 9eada6dfdd..a390a2ef53 100644 --- a/runtime/mirror/method_handles_lookup.cc +++ b/runtime/mirror/method_handles_lookup.cc @@ -18,11 +18,11 @@ #include "class-inl.h" #include "gc_root-inl.h" -#include "object-inl.h" #include "handle_scope.h" #include "jni_internal.h" #include "mirror/method_handle_impl.h" #include "modifiers.h" +#include "object-inl.h" #include "well_known_classes.h" namespace art { diff --git a/runtime/mirror/method_handles_lookup.h b/runtime/mirror/method_handles_lookup.h index 2109f601ee..dd8d45e66f 100644 --- a/runtime/mirror/method_handles_lookup.h +++ b/runtime/mirror/method_handles_lookup.h @@ -17,10 +17,10 @@ #ifndef ART_RUNTIME_MIRROR_METHOD_HANDLES_LOOKUP_H_ #define ART_RUNTIME_MIRROR_METHOD_HANDLES_LOOKUP_H_ -#include "obj_ptr.h" #include "gc_root.h" -#include "object.h" #include "handle.h" +#include "obj_ptr.h" +#include "object.h" #include "utils.h" namespace art { diff --git a/runtime/mirror/method_type.h b/runtime/mirror/method_type.h index 374bbe5df3..a9f3c9c116 100644 --- a/runtime/mirror/method_type.h +++ b/runtime/mirror/method_type.h @@ -17,9 +17,9 @@ #ifndef ART_RUNTIME_MIRROR_METHOD_TYPE_H_ #define ART_RUNTIME_MIRROR_METHOD_TYPE_H_ +#include "object_array.h" #include "object.h" #include "string.h" -#include "mirror/object_array.h" #include "utils.h" namespace art { diff --git a/runtime/mirror/object-inl.h b/runtime/mirror/object-inl.h index 43d70b74ec..086925b9c7 100644 --- a/runtime/mirror/object-inl.h +++ b/runtime/mirror/object-inl.h @@ -19,20 +19,20 @@ #include "object.h" +#include "array-inl.h" #include "art_field.h" #include "art_method.h" #include "atomic.h" -#include "array-inl.h" #include "class-inl.h" #include "class_flags.h" #include "class_linker.h" #include "dex_cache.h" #include "lock_word-inl.h" #include "monitor.h" +#include "obj_ptr-inl.h" +#include "object-readbarrier-inl.h" #include "object_array-inl.h" #include "object_reference-inl.h" -#include "object-readbarrier-inl.h" -#include "obj_ptr-inl.h" #include "read_barrier-inl.h" #include "reference.h" #include "runtime.h" diff --git a/runtime/mirror/object-refvisitor-inl.h b/runtime/mirror/object-refvisitor-inl.h index f5ab4dd8db..39e32bf63c 100644 --- a/runtime/mirror/object-refvisitor-inl.h +++ b/runtime/mirror/object-refvisitor-inl.h @@ -19,8 +19,8 @@ #include "object-inl.h" -#include "class_loader-inl.h" #include "class-refvisitor-inl.h" +#include "class_loader-inl.h" #include "dex_cache-inl.h" namespace art { diff --git a/runtime/mirror/object.cc b/runtime/mirror/object.cc index 6e5fdb773f..78ef339b8c 100644 --- a/runtime/mirror/object.cc +++ b/runtime/mirror/object.cc @@ -18,22 +18,22 @@ #include "object.h" -#include "art_field.h" -#include "art_field-inl.h" #include "array-inl.h" -#include "class.h" +#include "art_field-inl.h" +#include "art_field.h" #include "class-inl.h" +#include "class.h" #include "class_linker-inl.h" #include "dex_file-inl.h" #include "gc/accounting/card_table-inl.h" #include "gc/heap.h" +#include "handle_scope-inl.h" #include "iftable-inl.h" #include "monitor.h" #include "object-inl.h" #include "object-refvisitor-inl.h" #include "object_array-inl.h" #include "runtime.h" -#include "handle_scope-inl.h" #include "throwable.h" #include "well_known_classes.h" diff --git a/runtime/mirror/object_array-inl.h b/runtime/mirror/object_array-inl.h index dbec40c6d0..5cfc987e44 100644 --- a/runtime/mirror/object_array-inl.h +++ b/runtime/mirror/object_array-inl.h @@ -26,10 +26,10 @@ #include "array-inl.h" #include "class.h" #include "gc/heap.h" -#include "object-inl.h" +#include "handle_scope-inl.h" #include "obj_ptr-inl.h" +#include "object-inl.h" #include "runtime.h" -#include "handle_scope-inl.h" #include "thread.h" #include "utils.h" diff --git a/runtime/mirror/object_test.cc b/runtime/mirror/object_test.cc index 6230ae96e1..1a0fc76190 100644 --- a/runtime/mirror/object_test.cc +++ b/runtime/mirror/object_test.cc @@ -26,8 +26,8 @@ #include "asm_support.h" #include "base/enums.h" #include "class-inl.h" -#include "class_linker.h" #include "class_linker-inl.h" +#include "class_linker.h" #include "common_runtime_test.h" #include "dex_file.h" #include "entrypoints/entrypoint_utils-inl.h" diff --git a/runtime/mirror/stack_trace_element.cc b/runtime/mirror/stack_trace_element.cc index 53de821498..bb3242e035 100644 --- a/runtime/mirror/stack_trace_element.cc +++ b/runtime/mirror/stack_trace_element.cc @@ -16,12 +16,12 @@ #include "stack_trace_element.h" -#include "class.h" #include "class-inl.h" +#include "class.h" #include "gc/accounting/card_table-inl.h" #include "gc_root-inl.h" -#include "object-inl.h" #include "handle_scope-inl.h" +#include "object-inl.h" #include "string.h" namespace art { diff --git a/runtime/mirror/string.h b/runtime/mirror/string.h index 7fbe8bd3a6..545fe93516 100644 --- a/runtime/mirror/string.h +++ b/runtime/mirror/string.h @@ -17,8 +17,8 @@ #ifndef ART_RUNTIME_MIRROR_STRING_H_ #define ART_RUNTIME_MIRROR_STRING_H_ -#include "gc_root.h" #include "gc/allocator_type.h" +#include "gc_root.h" #include "object.h" namespace art { diff --git a/runtime/mirror/throwable.cc b/runtime/mirror/throwable.cc index aee4b19eeb..077ad50dcc 100644 --- a/runtime/mirror/throwable.cc +++ b/runtime/mirror/throwable.cc @@ -24,8 +24,8 @@ #include "dex_file-inl.h" #include "gc/accounting/card_table-inl.h" #include "object-inl.h" -#include "object_array.h" #include "object_array-inl.h" +#include "object_array.h" #include "stack_trace_element.h" #include "string.h" #include "utils.h" diff --git a/runtime/monitor_pool.cc b/runtime/monitor_pool.cc index 48e9a6b47d..9d221cca83 100644 --- a/runtime/monitor_pool.cc +++ b/runtime/monitor_pool.cc @@ -18,8 +18,8 @@ #include "base/logging.h" #include "base/mutex-inl.h" -#include "thread-current-inl.h" #include "monitor.h" +#include "thread-current-inl.h" namespace art { diff --git a/runtime/monitor_test.cc b/runtime/monitor_test.cc index 27ce149342..7d89652ddf 100644 --- a/runtime/monitor_test.cc +++ b/runtime/monitor_test.cc @@ -14,12 +14,12 @@ * limitations under the License. */ -#include "barrier.h" #include "monitor.h" #include <string> #include "atomic.h" +#include "barrier.h" #include "base/time_utils.h" #include "class_linker-inl.h" #include "common_runtime_test.h" @@ -36,11 +36,8 @@ class MonitorTest : public CommonRuntimeTest { protected: void SetUpRuntimeOptions(RuntimeOptions *options) OVERRIDE { // Use a smaller heap - for (std::pair<std::string, const void*>& pair : *options) { - if (pair.first.find("-Xmx") == 0) { - pair.first = "-Xmx4M"; // Smallest we can go. - } - } + SetUpRuntimeOptionsForFillHeap(options); + options->push_back(std::make_pair("-Xint", nullptr)); } public: @@ -56,52 +53,6 @@ class MonitorTest : public CommonRuntimeTest { bool completed_; }; -// Fill the heap. -static const size_t kMaxHandles = 1000000; // Use arbitrary large amount for now. -static void FillHeap(Thread* self, ClassLinker* class_linker, - std::unique_ptr<StackHandleScope<kMaxHandles>>* hsp, - std::vector<MutableHandle<mirror::Object>>* handles) - REQUIRES_SHARED(Locks::mutator_lock_) { - Runtime::Current()->GetHeap()->SetIdealFootprint(1 * GB); - - hsp->reset(new StackHandleScope<kMaxHandles>(self)); - // Class java.lang.Object. - Handle<mirror::Class> c((*hsp)->NewHandle(class_linker->FindSystemClass(self, - "Ljava/lang/Object;"))); - // Array helps to fill memory faster. - Handle<mirror::Class> ca((*hsp)->NewHandle(class_linker->FindSystemClass(self, - "[Ljava/lang/Object;"))); - - // Start allocating with 128K - size_t length = 128 * KB / 4; - while (length > 10) { - MutableHandle<mirror::Object> h((*hsp)->NewHandle<mirror::Object>( - mirror::ObjectArray<mirror::Object>::Alloc(self, ca.Get(), length / 4))); - if (self->IsExceptionPending() || h == nullptr) { - self->ClearException(); - - // Try a smaller length - length = length / 8; - // Use at most half the reported free space. - size_t mem = Runtime::Current()->GetHeap()->GetFreeMemory(); - if (length * 8 > mem) { - length = mem / 8; - } - } else { - handles->push_back(h); - } - } - - // Allocate simple objects till it fails. - while (!self->IsExceptionPending()) { - MutableHandle<mirror::Object> h = (*hsp)->NewHandle<mirror::Object>(c->AllocObject(self)); - if (!self->IsExceptionPending() && h != nullptr) { - handles->push_back(h); - } - } - self->ClearException(); -} - // Check that an exception can be thrown correctly. // This test is potentially racy, but the timeout is long enough that it should work. @@ -304,16 +255,12 @@ static void CommonWaitSetup(MonitorTest* test, ClassLinker* class_linker, uint64 test->complete_barrier_ = std::unique_ptr<Barrier>(new Barrier(3)); test->completed_ = false; - // Fill the heap. - std::unique_ptr<StackHandleScope<kMaxHandles>> hsp; - std::vector<MutableHandle<mirror::Object>> handles; - // Our job: Fill the heap, then try Wait. - FillHeap(soa.Self(), class_linker, &hsp, &handles); + { + VariableSizedHandleScope vhs(soa.Self()); + test->FillHeap(soa.Self(), class_linker, &vhs); - // Now release everything. - for (MutableHandle<mirror::Object>& h : handles) { - h.Assign(nullptr); + // Now release everything. } // Need to drop the mutator lock to allow barriers. diff --git a/runtime/native/dalvik_system_DexFile.cc b/runtime/native/dalvik_system_DexFile.cc index ad009668bf..07dfb65972 100644 --- a/runtime/native/dalvik_system_DexFile.cc +++ b/runtime/native/dalvik_system_DexFile.cc @@ -19,7 +19,6 @@ #include <sstream> #include "android-base/stringprintf.h" -#include "nativehelper/jni_macros.h" #include "base/logging.h" #include "base/stl_util.h" @@ -32,14 +31,15 @@ #include "mirror/object-inl.h" #include "mirror/string.h" #include "native_util.h" +#include "nativehelper/jni_macros.h" +#include "nativehelper/ScopedLocalRef.h" +#include "nativehelper/ScopedUtfChars.h" #include "oat_file.h" #include "oat_file_assistant.h" #include "oat_file_manager.h" #include "os.h" #include "runtime.h" #include "scoped_thread_state_change-inl.h" -#include "ScopedLocalRef.h" -#include "ScopedUtfChars.h" #include "utils.h" #include "well_known_classes.h" #include "zip_archive.h" @@ -458,7 +458,8 @@ static jint GetDexOptNeeded(JNIEnv* env, const char* filename, const char* instruction_set, const char* compiler_filter_name, - bool profile_changed) { + bool profile_changed, + bool downgrade) { if ((filename == nullptr) || !OS::FileExists(filename)) { LOG(ERROR) << "DexFile_getDexOptNeeded file '" << filename << "' does not exist"; ScopedLocalRef<jclass> fnfe(env, env->FindClass("java/io/FileNotFoundException")); @@ -492,7 +493,7 @@ static jint GetDexOptNeeded(JNIEnv* env, if (oat_file_assistant.IsInBootClassPath()) { return OatFileAssistant::kNoDexOptNeeded; } - return oat_file_assistant.GetDexOptNeeded(filter, profile_changed); + return oat_file_assistant.GetDexOptNeeded(filter, profile_changed, downgrade); } static jstring DexFile_getDexFileStatus(JNIEnv* env, @@ -528,7 +529,8 @@ static jint DexFile_getDexOptNeeded(JNIEnv* env, jstring javaFilename, jstring javaInstructionSet, jstring javaTargetCompilerFilter, - jboolean newProfile) { + jboolean newProfile, + jboolean downgrade) { ScopedUtfChars filename(env, javaFilename); if (env->ExceptionCheck()) { return -1; @@ -548,7 +550,8 @@ static jint DexFile_getDexOptNeeded(JNIEnv* env, filename.c_str(), instruction_set.c_str(), target_compiler_filter.c_str(), - newProfile == JNI_TRUE); + newProfile == JNI_TRUE, + downgrade == JNI_TRUE); } // public API @@ -725,7 +728,7 @@ static JNINativeMethod gMethods[] = { NATIVE_METHOD(DexFile, getClassNameList, "(Ljava/lang/Object;)[Ljava/lang/String;"), NATIVE_METHOD(DexFile, isDexOptNeeded, "(Ljava/lang/String;)Z"), NATIVE_METHOD(DexFile, getDexOptNeeded, - "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Z)I"), + "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ZZ)I"), NATIVE_METHOD(DexFile, openDexFileNative, "(Ljava/lang/String;" "Ljava/lang/String;" diff --git a/runtime/native/dalvik_system_VMDebug.cc b/runtime/native/dalvik_system_VMDebug.cc index e1eae21df9..3357fa7a45 100644 --- a/runtime/native/dalvik_system_VMDebug.cc +++ b/runtime/native/dalvik_system_VMDebug.cc @@ -40,8 +40,8 @@ #include "mirror/class.h" #include "mirror/object_array-inl.h" #include "native_util.h" -#include "ScopedLocalRef.h" -#include "ScopedUtfChars.h" +#include "nativehelper/ScopedLocalRef.h" +#include "nativehelper/ScopedUtfChars.h" #include "scoped_fast_native_object_access-inl.h" #include "trace.h" #include "well_known_classes.h" diff --git a/runtime/native/dalvik_system_VMRuntime.cc b/runtime/native/dalvik_system_VMRuntime.cc index fed9c1cf5b..4db9feb518 100644 --- a/runtime/native/dalvik_system_VMRuntime.cc +++ b/runtime/native/dalvik_system_VMRuntime.cc @@ -17,23 +17,22 @@ #include "dalvik_system_VMRuntime.h" #ifdef ART_TARGET_ANDROID -#include <sys/time.h> #include <sys/resource.h> +#include <sys/time.h> extern "C" void android_set_application_target_sdk_version(uint32_t version); #endif #include <limits.h> -#include <ScopedUtfChars.h> +#include "nativehelper/ScopedUtfChars.h" #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wshadow" -#include "toStringArray.h" +#include "nativehelper/toStringArray.h" #pragma GCC diagnostic pop #include "android-base/stringprintf.h" -#include "nativehelper/jni_macros.h" -#include "art_method-inl.h" #include "arch/instruction_set.h" +#include "art_method-inl.h" #include "base/enums.h" #include "class_linker-inl.h" #include "common_throws.h" @@ -53,6 +52,7 @@ extern "C" void android_set_application_target_sdk_version(uint32_t version); #include "mirror/dex_cache-inl.h" #include "mirror/object-inl.h" #include "native_util.h" +#include "nativehelper/jni_macros.h" #include "runtime.h" #include "scoped_fast_native_object_access-inl.h" #include "scoped_thread_state_change-inl.h" @@ -298,15 +298,16 @@ class PreloadDexCachesStringsVisitor : public SingleRootVisitor { // Based on ClassLinker::ResolveString. static void PreloadDexCachesResolveString( - Handle<mirror::DexCache> dex_cache, dex::StringIndex string_idx, StringTable& strings) + ObjPtr<mirror::DexCache> dex_cache, dex::StringIndex string_idx, StringTable& strings) REQUIRES_SHARED(Locks::mutator_lock_) { - ObjPtr<mirror::String> string = dex_cache->GetResolvedString(string_idx); - if (string != nullptr) { - return; + uint32_t slot_idx = dex_cache->StringSlotIndex(string_idx); + auto pair = dex_cache->GetStrings()[slot_idx].load(std::memory_order_relaxed); + if (!pair.object.IsNull()) { + return; // The entry already contains some String. } const DexFile* dex_file = dex_cache->GetDexFile(); const char* utf8 = dex_file->StringDataByIdx(string_idx); - string = strings[utf8]; + ObjPtr<mirror::String> string = strings[utf8]; if (string == nullptr) { return; } @@ -319,18 +320,17 @@ static void PreloadDexCachesResolveType(Thread* self, ObjPtr<mirror::DexCache> dex_cache, dex::TypeIndex type_idx) REQUIRES_SHARED(Locks::mutator_lock_) { - ObjPtr<mirror::Class> klass = dex_cache->GetResolvedType(type_idx); - if (klass != nullptr) { - return; + uint32_t slot_idx = dex_cache->TypeSlotIndex(type_idx); + auto pair = dex_cache->GetResolvedTypes()[slot_idx].load(std::memory_order_relaxed); + if (!pair.object.IsNull()) { + return; // The entry already contains some Class. } 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(self, class_name, nullptr); - } + ObjPtr<mirror::Class> klass = (class_name[1] == '\0') + ? linker->FindPrimitiveClass(class_name[0]) + : linker->LookupClass(self, class_name, nullptr); if (klass == nullptr) { return; } @@ -345,26 +345,27 @@ static void PreloadDexCachesResolveType(Thread* self, } // Based on ClassLinker::ResolveField. -static void PreloadDexCachesResolveField(Handle<mirror::DexCache> dex_cache, uint32_t field_idx, +static void PreloadDexCachesResolveField(ObjPtr<mirror::DexCache> dex_cache, + uint32_t field_idx, bool is_static) REQUIRES_SHARED(Locks::mutator_lock_) { - ArtField* field = dex_cache->GetResolvedField(field_idx, kRuntimePointerSize); - if (field != nullptr) { - return; + uint32_t slot_idx = dex_cache->FieldSlotIndex(field_idx); + auto pair = mirror::DexCache::GetNativePairPtrSize(dex_cache->GetResolvedFields(), + slot_idx, + kRuntimePointerSize); + if (pair.object != nullptr) { + return; // The entry already contains some ArtField. } const DexFile* dex_file = dex_cache->GetDexFile(); const DexFile::FieldId& field_id = dex_file->GetFieldId(field_idx); - Thread* const self = Thread::Current(); - StackHandleScope<1> hs(self); - Handle<mirror::Class> klass(hs.NewHandle(dex_cache->GetResolvedType(field_id.class_idx_))); + ObjPtr<mirror::Class> klass = + ClassLinker::LookupResolvedType(field_id.class_idx_, dex_cache, nullptr); if (klass == nullptr) { return; } - if (is_static) { - field = mirror::Class::FindStaticField(self, klass.Get(), dex_cache.Get(), field_idx); - } else { - field = klass->FindInstanceField(dex_cache.Get(), field_idx); - } + ArtField* field = is_static + ? mirror::Class::FindStaticField(Thread::Current(), klass, dex_cache, field_idx) + : klass->FindInstanceField(dex_cache, field_idx); if (field == nullptr) { return; } @@ -372,35 +373,25 @@ static void PreloadDexCachesResolveField(Handle<mirror::DexCache> dex_cache, uin } // Based on ClassLinker::ResolveMethod. -static void PreloadDexCachesResolveMethod(Handle<mirror::DexCache> dex_cache, uint32_t method_idx, - InvokeType invoke_type) +static void PreloadDexCachesResolveMethod(ObjPtr<mirror::DexCache> dex_cache, uint32_t method_idx) REQUIRES_SHARED(Locks::mutator_lock_) { - ArtMethod* method = dex_cache->GetResolvedMethod(method_idx, kRuntimePointerSize); - if (method != nullptr) { - return; + uint32_t slot_idx = dex_cache->MethodSlotIndex(method_idx); + auto pair = mirror::DexCache::GetNativePairPtrSize(dex_cache->GetResolvedMethods(), + slot_idx, + kRuntimePointerSize); + if (pair.object != nullptr) { + return; // The entry already contains some ArtMethod. } const DexFile* dex_file = dex_cache->GetDexFile(); const DexFile::MethodId& method_id = dex_file->GetMethodId(method_idx); - ObjPtr<mirror::Class> klass = dex_cache->GetResolvedType(method_id.class_idx_); + ObjPtr<mirror::Class> klass = + ClassLinker::LookupResolvedType(method_id.class_idx_, dex_cache, nullptr); if (klass == nullptr) { return; } - switch (invoke_type) { - case kDirect: - case kStatic: - method = klass->FindDirectMethod(dex_cache.Get(), method_idx, kRuntimePointerSize); - break; - case kInterface: - method = klass->FindInterfaceMethod(dex_cache.Get(), method_idx, kRuntimePointerSize); - break; - case kSuper: - case kVirtual: - method = klass->FindVirtualMethod(dex_cache.Get(), method_idx, kRuntimePointerSize); - break; - default: - LOG(FATAL) << "Unreachable - invocation type: " << invoke_type; - UNREACHABLE(); - } + ArtMethod* method = klass->IsInterface() + ? klass->FindInterfaceMethod(dex_cache, method_idx, kRuntimePointerSize) + : klass->FindClassMethod(dex_cache, method_idx, kRuntimePointerSize); if (method == nullptr) { return; } @@ -462,27 +453,31 @@ static void PreloadDexCachesStatsFilled(DexCacheStats* filled) } ObjPtr<mirror::DexCache> const dex_cache = class_linker->FindDexCache(self, *dex_file); DCHECK(dex_cache != nullptr); // Boot class path dex caches are never unloaded. - for (size_t j = 0; j < dex_cache->NumStrings(); j++) { - ObjPtr<mirror::String> string = dex_cache->GetResolvedString(dex::StringIndex(j)); - if (string != nullptr) { + for (size_t j = 0, num_strings = dex_cache->NumStrings(); j < num_strings; ++j) { + auto pair = dex_cache->GetStrings()[j].load(std::memory_order_relaxed); + if (!pair.object.IsNull()) { filled->num_strings++; } } - for (size_t j = 0; j < dex_cache->NumResolvedTypes(); j++) { - ObjPtr<mirror::Class> klass = dex_cache->GetResolvedType(dex::TypeIndex(j)); - if (klass != nullptr) { + for (size_t j = 0, num_types = dex_cache->NumResolvedTypes(); j < num_types; ++j) { + auto pair = dex_cache->GetResolvedTypes()[j].load(std::memory_order_relaxed); + if (!pair.object.IsNull()) { filled->num_types++; } } - for (size_t j = 0; j < dex_cache->NumResolvedFields(); j++) { - ArtField* field = dex_cache->GetResolvedField(j, class_linker->GetImagePointerSize()); - if (field != nullptr) { + for (size_t j = 0, num_fields = dex_cache->NumResolvedFields(); j < num_fields; ++j) { + auto pair = mirror::DexCache::GetNativePairPtrSize(dex_cache->GetResolvedFields(), + j, + kRuntimePointerSize); + if (pair.object != nullptr) { filled->num_fields++; } } - for (size_t j = 0; j < dex_cache->NumResolvedMethods(); j++) { - ArtMethod* method = dex_cache->GetResolvedMethod(j, kRuntimePointerSize); - if (method != nullptr) { + for (size_t j = 0, num_methods = dex_cache->NumResolvedMethods(); j < num_methods; ++j) { + auto pair = mirror::DexCache::GetNativePairPtrSize(dex_cache->GetResolvedMethods(), + j, + kRuntimePointerSize); + if (pair.object != nullptr) { filled->num_methods++; } } @@ -522,8 +517,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 != nullptr); - StackHandleScope<1> hs(soa.Self()); - Handle<mirror::DexCache> dex_cache(hs.NewHandle(linker->RegisterDexFile(*dex_file, nullptr))); + ObjPtr<mirror::DexCache> dex_cache = linker->RegisterDexFile(*dex_file, nullptr); CHECK(dex_cache != nullptr); // Boot class path dex caches are never unloaded. if (kPreloadDexCachesStrings) { for (size_t j = 0; j < dex_cache->NumStrings(); j++) { @@ -533,7 +527,7 @@ static void VMRuntime_preloadDexCaches(JNIEnv* env, jobject) { if (kPreloadDexCachesTypes) { for (size_t j = 0; j < dex_cache->NumResolvedTypes(); j++) { - PreloadDexCachesResolveType(soa.Self(), dex_cache.Get(), dex::TypeIndex(j)); + PreloadDexCachesResolveType(soa.Self(), dex_cache, dex::TypeIndex(j)); } } @@ -557,13 +551,11 @@ static void VMRuntime_preloadDexCaches(JNIEnv* env, jobject) { } 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); + PreloadDexCachesResolveMethod(dex_cache, method_idx); } 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); + PreloadDexCachesResolveMethod(dex_cache, method_idx); } } } diff --git a/runtime/native/dalvik_system_VMStack.cc b/runtime/native/dalvik_system_VMStack.cc index 2aeef60d00..a88563da1f 100644 --- a/runtime/native/dalvik_system_VMStack.cc +++ b/runtime/native/dalvik_system_VMStack.cc @@ -21,11 +21,11 @@ #include "art_method-inl.h" #include "gc/task_processor.h" #include "jni_internal.h" -#include "nth_caller_visitor.h" #include "mirror/class-inl.h" #include "mirror/class_loader.h" #include "mirror/object-inl.h" #include "native_util.h" +#include "nth_caller_visitor.h" #include "scoped_fast_native_object_access-inl.h" #include "scoped_thread_state_change-inl.h" #include "thread_list.h" diff --git a/runtime/native/dalvik_system_ZygoteHooks.cc b/runtime/native/dalvik_system_ZygoteHooks.cc index 31aeba06f9..2e4db7a7fa 100644 --- a/runtime/native/dalvik_system_ZygoteHooks.cc +++ b/runtime/native/dalvik_system_ZygoteHooks.cc @@ -19,7 +19,6 @@ #include <stdlib.h> #include "android-base/stringprintf.h" -#include "nativehelper/jni_macros.h" #include "arch/instruction_set.h" #include "art_method-inl.h" @@ -27,11 +26,12 @@ #include "java_vm_ext.h" #include "jit/jit.h" #include "jni_internal.h" -#include "JNIHelp.h" #include "native_util.h" +#include "nativehelper/jni_macros.h" +#include "nativehelper/JNIHelp.h" +#include "nativehelper/ScopedUtfChars.h" #include "non_debuggable_classes.h" #include "scoped_thread_state_change-inl.h" -#include "ScopedUtfChars.h" #include "stack.h" #include "thread-current-inl.h" #include "thread_list.h" diff --git a/runtime/native/java_lang_Class.cc b/runtime/native/java_lang_Class.cc index d3377be393..1a19940993 100644 --- a/runtime/native/java_lang_Class.cc +++ b/runtime/native/java_lang_Class.cc @@ -18,8 +18,6 @@ #include <iostream> -#include "nativehelper/jni_macros.h" - #include "art_field-inl.h" #include "art_method-inl.h" #include "base/enums.h" @@ -28,7 +26,6 @@ #include "dex_file-inl.h" #include "dex_file_annotations.h" #include "jni_internal.h" -#include "nth_caller_visitor.h" #include "mirror/class-inl.h" #include "mirror/class_loader.h" #include "mirror/field-inl.h" @@ -37,12 +34,14 @@ #include "mirror/object_array-inl.h" #include "mirror/string-inl.h" #include "native_util.h" +#include "nativehelper/jni_macros.h" +#include "nativehelper/ScopedLocalRef.h" +#include "nativehelper/ScopedUtfChars.h" +#include "nth_caller_visitor.h" #include "obj_ptr-inl.h" #include "reflection.h" -#include "scoped_thread_state_change-inl.h" #include "scoped_fast_native_object_access-inl.h" -#include "ScopedLocalRef.h" -#include "ScopedUtfChars.h" +#include "scoped_thread_state_change-inl.h" #include "utf.h" #include "well_known_classes.h" diff --git a/runtime/native/java_lang_String.cc b/runtime/native/java_lang_String.cc index ac0d6337b2..e2de141808 100644 --- a/runtime/native/java_lang_String.cc +++ b/runtime/native/java_lang_String.cc @@ -22,12 +22,12 @@ #include "jni_internal.h" #include "mirror/array.h" #include "mirror/object-inl.h" -#include "mirror/string.h" #include "mirror/string-inl.h" +#include "mirror/string.h" #include "native_util.h" +#include "nativehelper/ScopedLocalRef.h" #include "scoped_fast_native_object_access-inl.h" #include "scoped_thread_state_change-inl.h" -#include "ScopedLocalRef.h" #include "verify_object.h" namespace art { diff --git a/runtime/native/java_lang_StringFactory.cc b/runtime/native/java_lang_StringFactory.cc index 9c2e91843e..2db9a5cc22 100644 --- a/runtime/native/java_lang_StringFactory.cc +++ b/runtime/native/java_lang_StringFactory.cc @@ -16,17 +16,16 @@ #include "java_lang_StringFactory.h" -#include "nativehelper/jni_macros.h" - #include "common_throws.h" #include "jni_internal.h" #include "mirror/object-inl.h" #include "mirror/string.h" #include "native_util.h" +#include "nativehelper/jni_macros.h" +#include "nativehelper/ScopedLocalRef.h" +#include "nativehelper/ScopedPrimitiveArray.h" #include "scoped_fast_native_object_access-inl.h" #include "scoped_thread_state_change-inl.h" -#include "ScopedLocalRef.h" -#include "ScopedPrimitiveArray.h" namespace art { diff --git a/runtime/native/java_lang_System.cc b/runtime/native/java_lang_System.cc index 0e5d740cab..553cbeaabc 100644 --- a/runtime/native/java_lang_System.cc +++ b/runtime/native/java_lang_System.cc @@ -22,8 +22,8 @@ #include "gc/accounting/card_table-inl.h" #include "jni_internal.h" #include "mirror/array.h" -#include "mirror/class.h" #include "mirror/class-inl.h" +#include "mirror/class.h" #include "mirror/object-inl.h" #include "mirror/object_array-inl.h" #include "native_util.h" diff --git a/runtime/native/java_lang_Thread.cc b/runtime/native/java_lang_Thread.cc index 4ce72edd7b..4fbbb72631 100644 --- a/runtime/native/java_lang_Thread.cc +++ b/runtime/native/java_lang_Thread.cc @@ -16,16 +16,15 @@ #include "java_lang_Thread.h" -#include "nativehelper/jni_macros.h" - #include "common_throws.h" #include "jni_internal.h" -#include "monitor.h" #include "mirror/object.h" +#include "monitor.h" #include "native_util.h" +#include "nativehelper/jni_macros.h" +#include "nativehelper/ScopedUtfChars.h" #include "scoped_fast_native_object_access-inl.h" #include "scoped_thread_state_change-inl.h" -#include "ScopedUtfChars.h" #include "thread.h" #include "thread_list.h" #include "verify_object.h" diff --git a/runtime/native/java_lang_VMClassLoader.cc b/runtime/native/java_lang_VMClassLoader.cc index fc50d5584d..4034e8c837 100644 --- a/runtime/native/java_lang_VMClassLoader.cc +++ b/runtime/native/java_lang_VMClassLoader.cc @@ -16,17 +16,16 @@ #include "java_lang_VMClassLoader.h" -#include "nativehelper/jni_macros.h" - #include "class_linker.h" #include "jni_internal.h" #include "mirror/class_loader.h" #include "mirror/object-inl.h" #include "native_util.h" +#include "nativehelper/jni_macros.h" +#include "nativehelper/ScopedLocalRef.h" +#include "nativehelper/ScopedUtfChars.h" #include "obj_ptr.h" #include "scoped_fast_native_object_access-inl.h" -#include "ScopedLocalRef.h" -#include "ScopedUtfChars.h" #include "well_known_classes.h" #include "zip_archive.h" @@ -135,7 +134,7 @@ static jobjectArray VMClassLoader_getBootClassPathEntries(JNIEnv* env, jclass) { for (size_t i = 0; i < path.size(); ++i) { const DexFile* dex_file = path[i]; - // For multidex locations, e.g., x.jar:classes2.dex, we want to look into x.jar. + // For multidex locations, e.g., x.jar!classes2.dex, we want to look into x.jar. const std::string& location(dex_file->GetBaseLocation()); ScopedLocalRef<jstring> javaPath(env, env->NewStringUTF(location.c_str())); diff --git a/runtime/native/java_lang_reflect_Constructor.cc b/runtime/native/java_lang_reflect_Constructor.cc index 242e87afa9..abbb347be6 100644 --- a/runtime/native/java_lang_reflect_Constructor.cc +++ b/runtime/native/java_lang_reflect_Constructor.cc @@ -20,8 +20,8 @@ #include "art_method-inl.h" #include "base/enums.h" -#include "class_linker.h" #include "class_linker-inl.h" +#include "class_linker.h" #include "dex_file_annotations.h" #include "jni_internal.h" #include "mirror/class-inl.h" diff --git a/runtime/native/java_lang_reflect_Field.cc b/runtime/native/java_lang_reflect_Field.cc index f19004dab5..9f59a1f751 100644 --- a/runtime/native/java_lang_reflect_Field.cc +++ b/runtime/native/java_lang_reflect_Field.cc @@ -20,8 +20,8 @@ #include "nativehelper/jni_macros.h" #include "art_field-inl.h" -#include "class_linker.h" #include "class_linker-inl.h" +#include "class_linker.h" #include "common_throws.h" #include "dex_file-inl.h" #include "dex_file_annotations.h" diff --git a/runtime/native/java_lang_reflect_Method.cc b/runtime/native/java_lang_reflect_Method.cc index cbbb6a8ea3..18ff9c39bf 100644 --- a/runtime/native/java_lang_reflect_Method.cc +++ b/runtime/native/java_lang_reflect_Method.cc @@ -20,8 +20,8 @@ #include "art_method-inl.h" #include "base/enums.h" -#include "class_linker.h" #include "class_linker-inl.h" +#include "class_linker.h" #include "dex_file_annotations.h" #include "jni_internal.h" #include "mirror/class-inl.h" diff --git a/runtime/native/libcore_util_CharsetUtils.cc b/runtime/native/libcore_util_CharsetUtils.cc index 38634e6d0c..9743c9413d 100644 --- a/runtime/native/libcore_util_CharsetUtils.cc +++ b/runtime/native/libcore_util_CharsetUtils.cc @@ -18,17 +18,15 @@ #include <string.h> -#include "nativehelper/jni_macros.h" - #include "jni_internal.h" -#include "mirror/string.h" #include "mirror/string-inl.h" +#include "mirror/string.h" #include "native_util.h" +#include "nativehelper/ScopedPrimitiveArray.h" +#include "nativehelper/jni_macros.h" #include "scoped_fast_native_object_access-inl.h" -#include "ScopedPrimitiveArray.h" #include "unicode/utf16.h" - namespace art { /** diff --git a/runtime/native/native_util.h b/runtime/native/native_util.h index 98384e0178..593b3ca444 100644 --- a/runtime/native/native_util.h +++ b/runtime/native/native_util.h @@ -21,7 +21,7 @@ #include "android-base/logging.h" #include "base/macros.h" -#include "ScopedLocalRef.h" +#include "nativehelper/ScopedLocalRef.h" namespace art { diff --git a/runtime/native/org_apache_harmony_dalvik_ddmc_DdmServer.cc b/runtime/native/org_apache_harmony_dalvik_ddmc_DdmServer.cc index 925b90931c..c3e74bd112 100644 --- a/runtime/native/org_apache_harmony_dalvik_ddmc_DdmServer.cc +++ b/runtime/native/org_apache_harmony_dalvik_ddmc_DdmServer.cc @@ -16,14 +16,13 @@ #include "org_apache_harmony_dalvik_ddmc_DdmServer.h" -#include "nativehelper/jni_macros.h" - #include "base/logging.h" #include "debugger.h" #include "jni_internal.h" #include "native_util.h" +#include "nativehelper/jni_macros.h" +#include "nativehelper/ScopedPrimitiveArray.h" #include "scoped_fast_native_object_access-inl.h" -#include "ScopedPrimitiveArray.h" namespace art { diff --git a/runtime/native/org_apache_harmony_dalvik_ddmc_DdmVmInternal.cc b/runtime/native/org_apache_harmony_dalvik_ddmc_DdmVmInternal.cc index 125d737958..8c42973509 100644 --- a/runtime/native/org_apache_harmony_dalvik_ddmc_DdmVmInternal.cc +++ b/runtime/native/org_apache_harmony_dalvik_ddmc_DdmVmInternal.cc @@ -16,17 +16,16 @@ #include "org_apache_harmony_dalvik_ddmc_DdmVmInternal.h" -#include "nativehelper/jni_macros.h" - #include "base/logging.h" #include "base/mutex.h" #include "debugger.h" #include "gc/heap.h" #include "jni_internal.h" #include "native_util.h" +#include "nativehelper/jni_macros.h" +#include "nativehelper/ScopedLocalRef.h" +#include "nativehelper/ScopedPrimitiveArray.h" #include "scoped_fast_native_object_access-inl.h" -#include "ScopedLocalRef.h" -#include "ScopedPrimitiveArray.h" #include "thread_list.h" namespace art { diff --git a/runtime/native/sun_misc_Unsafe.cc b/runtime/native/sun_misc_Unsafe.cc index e78c9da5e5..761362fcb2 100644 --- a/runtime/native/sun_misc_Unsafe.cc +++ b/runtime/native/sun_misc_Unsafe.cc @@ -16,11 +16,12 @@ #include "sun_misc_Unsafe.h" -#include <atomic> -#include <stdlib.h> -#include <string.h> #include <unistd.h> +#include <cstdlib> +#include <cstring> +#include <atomic> + #include "nativehelper/jni_macros.h" #include "common_throws.h" diff --git a/runtime/native_stack_dump.cc b/runtime/native_stack_dump.cc index cbff0bb2f2..7e16357376 100644 --- a/runtime/native_stack_dump.cc +++ b/runtime/native_stack_dump.cc @@ -337,7 +337,7 @@ void DumpNativeStack(std::ostream& os, } else { os << StringPrintf(Is64BitInstructionSet(kRuntimeISA) ? "%016" PRIxPTR " " : "%08" PRIxPTR " ", - BacktraceMap::GetRelativePc(it->map, it->pc)); + it->rel_pc); os << it->map.name; os << " ("; if (!it->func_name.empty()) { diff --git a/runtime/non_debuggable_classes.cc b/runtime/non_debuggable_classes.cc index 9cc7e60fa8..871ffba2a4 100644 --- a/runtime/non_debuggable_classes.cc +++ b/runtime/non_debuggable_classes.cc @@ -19,8 +19,8 @@ #include "base/logging.h" #include "jni_internal.h" #include "mirror/class-inl.h" +#include "nativehelper/ScopedLocalRef.h" #include "obj_ptr-inl.h" -#include "ScopedLocalRef.h" #include "thread-current-inl.h" namespace art { diff --git a/runtime/oat.h b/runtime/oat.h index f4edb16bce..c4a983e78b 100644 --- a/runtime/oat.h +++ b/runtime/oat.h @@ -32,8 +32,8 @@ class InstructionSetFeatures; class PACKED(4) OatHeader { public: static constexpr uint8_t kOatMagic[] = { 'o', 'a', 't', '\n' }; - // Last oat version changed reason: add new class status to skip superclass validation. - static constexpr uint8_t kOatVersion[] = { '1', '2', '9', '\0' }; + // Last oat version changed reason: MIPS Baker thunks. + static constexpr uint8_t kOatVersion[] = { '1', '3', '1', '\0' }; static constexpr const char* kImageLocationKey = "image-location"; static constexpr const char* kDex2OatCmdLineKey = "dex2oat-cmdline"; diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc index 1c1189d7de..4033f8cd3a 100644 --- a/runtime/oat_file.cc +++ b/runtime/oat_file.cc @@ -17,15 +17,15 @@ #include "oat_file.h" #include <dlfcn.h> -#include <string.h> -#include <type_traits> -#include <unistd.h> - -#include <cstdlib> #ifndef __APPLE__ #include <link.h> // for dl_iterate_phdr. #endif +#include <unistd.h> + +#include <cstdlib> +#include <cstring> #include <sstream> +#include <type_traits> // dlopen_ext support from bionic. #ifdef ART_TARGET_ANDROID @@ -44,10 +44,10 @@ #include "elf_file.h" #include "elf_utils.h" #include "gc_root.h" -#include "oat.h" #include "mem_map.h" #include "mirror/class.h" #include "mirror/object-inl.h" +#include "oat.h" #include "oat_file-inl.h" #include "oat_file_manager.h" #include "os.h" diff --git a/runtime/oat_file.h b/runtime/oat_file.h index b112b84564..be7d4953a0 100644 --- a/runtime/oat_file.h +++ b/runtime/oat_file.h @@ -289,8 +289,8 @@ class OatFile { // If not null, abs_dex_location is used to resolve the absolute dex // location of relative dex locations encoded in the oat file. // For example, given absolute location "/data/app/foo/base.apk", encoded - // dex locations "base.apk", "base.apk:classes2.dex", etc. would be resolved - // to "/data/app/foo/base.apk", "/data/app/foo/base.apk:classes2.dex", etc. + // dex locations "base.apk", "base.apk!classes2.dex", etc. would be resolved + // to "/data/app/foo/base.apk", "/data/app/foo/base.apk!classes2.dex", etc. // Relative encoded dex locations that don't match the given abs_dex_location // are left unchanged. static std::string ResolveRelativeEncodedDexLocation( diff --git a/runtime/oat_file_assistant.cc b/runtime/oat_file_assistant.cc index c8766578c4..dae41c1b67 100644 --- a/runtime/oat_file_assistant.cc +++ b/runtime/oat_file_assistant.cc @@ -25,8 +25,8 @@ #include "base/logging.h" #include "base/stl_util.h" -#include "compiler_filter.h" #include "class_linker.h" +#include "compiler_filter.h" #include "exec_utils.h" #include "gc/heap.h" #include "gc/space/image_space.h" @@ -187,9 +187,11 @@ bool OatFileAssistant::Lock(std::string* error_msg) { return true; } -int OatFileAssistant::GetDexOptNeeded(CompilerFilter::Filter target, bool profile_changed) { +int OatFileAssistant::GetDexOptNeeded(CompilerFilter::Filter target, + bool profile_changed, + bool downgrade) { OatFileInfo& info = GetBestInfo(); - DexOptNeeded dexopt_needed = info.GetDexOptNeeded(target, profile_changed); + DexOptNeeded dexopt_needed = info.GetDexOptNeeded(target, profile_changed, downgrade); if (info.IsOatLocation() || dexopt_needed == kDex2OatFromScratch) { return dexopt_needed; } @@ -230,7 +232,7 @@ OatFileAssistant::MakeUpToDate(bool profile_changed, std::string* error_msg) { } OatFileInfo& info = GetBestInfo(); - switch (info.GetDexOptNeeded(target, profile_changed)) { + switch (info.GetDexOptNeeded(target, profile_changed, /*downgrade*/ false)) { case kNoDexOptNeeded: return kUpdateSucceeded; @@ -1005,9 +1007,9 @@ OatFileAssistant::OatStatus OatFileAssistant::OatFileInfo::Status() { } OatFileAssistant::DexOptNeeded OatFileAssistant::OatFileInfo::GetDexOptNeeded( - CompilerFilter::Filter target, bool profile_changed) { + CompilerFilter::Filter target, bool profile_changed, bool downgrade) { bool compilation_desired = CompilerFilter::IsAotCompilationEnabled(target); - bool filter_okay = CompilerFilterIsOkay(target, profile_changed); + bool filter_okay = CompilerFilterIsOkay(target, profile_changed, downgrade); if (filter_okay && Status() == kOatUpToDate) { // The oat file is in good shape as is. @@ -1064,7 +1066,7 @@ const OatFile* OatFileAssistant::OatFileInfo::GetFile() { } bool OatFileAssistant::OatFileInfo::CompilerFilterIsOkay( - CompilerFilter::Filter target, bool profile_changed) { + CompilerFilter::Filter target, bool profile_changed, bool downgrade) { const OatFile* file = GetFile(); if (file == nullptr) { return false; @@ -1075,7 +1077,8 @@ bool OatFileAssistant::OatFileInfo::CompilerFilterIsOkay( VLOG(oat) << "Compiler filter not okay because Profile changed"; return false; } - return CompilerFilter::IsAsGoodAs(current, target); + return downgrade ? !CompilerFilter::IsBetter(current, target) : + CompilerFilter::IsAsGoodAs(current, target); } bool OatFileAssistant::OatFileInfo::IsExecutable() { diff --git a/runtime/oat_file_assistant.h b/runtime/oat_file_assistant.h index 92d87eaeae..320aa4f860 100644 --- a/runtime/oat_file_assistant.h +++ b/runtime/oat_file_assistant.h @@ -147,13 +147,24 @@ class OatFileAssistant { bool Lock(std::string* error_msg); // Return what action needs to be taken to produce up-to-date code for this - // dex location that is at least as good as an oat file generated with the - // given compiler filter. profile_changed should be true to indicate the - // profile has recently changed for this dex location. + // dex location. If "downgrade" is set to false, it verifies if the current + // compiler filter is at least as good as an oat file generated with the + // given compiler filter otherwise, if its set to true, it checks whether + // the oat file generated with the target filter will be downgraded as + // compared to the current state. For example, if the current compiler filter is + // quicken, and target filter is verify, it will recommend to dexopt, while + // if the target filter is speed profile, it will recommend to keep it in its + // current state. + // profile_changed should be true to indicate the profile has recently changed + // for this dex location. + // If the purpose of the dexopt is to downgrade the compiler filter, + // set downgrade to true. // Returns a positive status code if the status refers to the oat file in // the oat location. Returns a negative status code if the status refers to // the oat file in the odex location. - int GetDexOptNeeded(CompilerFilter::Filter target_compiler_filter, bool profile_changed = false); + int GetDexOptNeeded(CompilerFilter::Filter target_compiler_filter, + bool profile_changed = false, + bool downgrade = false); // Returns true if there is up-to-date code for this dex location, // irrespective of the compiler filter of the up-to-date code. @@ -310,8 +321,11 @@ class OatFileAssistant { // given target_compilation_filter. // profile_changed should be true to indicate the profile has recently // changed for this dex location. + // downgrade should be true if the purpose of dexopt is to downgrade the + // compiler filter. DexOptNeeded GetDexOptNeeded(CompilerFilter::Filter target_compiler_filter, - bool profile_changed); + bool profile_changed, + bool downgrade); // Returns the loaded file. // Loads the file if needed. Returns null if the file failed to load. @@ -344,7 +358,9 @@ class OatFileAssistant { // least as good as the given target filter. profile_changed should be // true to indicate the profile has recently changed for this dex // location. - bool CompilerFilterIsOkay(CompilerFilter::Filter target, bool profile_changed); + // downgrade should be true if the purpose of dexopt is to downgrade the + // compiler filter. + bool CompilerFilterIsOkay(CompilerFilter::Filter target, bool profile_changed, bool downgrade); // Release the loaded oat file. // Returns null if the oat file hasn't been loaded. diff --git a/runtime/oat_file_assistant_test.cc b/runtime/oat_file_assistant_test.cc index 1ecdd0d77e..c59dafcb38 100644 --- a/runtime/oat_file_assistant_test.cc +++ b/runtime/oat_file_assistant_test.cc @@ -14,18 +14,21 @@ * limitations under the License. */ +#include "oat_file_assistant.h" + +#include <sys/param.h> + #include <string> #include <vector> -#include <sys/param.h> -#include "android-base/strings.h" #include <gtest/gtest.h> +#include "android-base/strings.h" + #include "art_field-inl.h" #include "class_linker-inl.h" #include "common_runtime_test.h" #include "dexopt_test.h" -#include "oat_file_assistant.h" #include "oat_file_manager.h" #include "os.h" #include "scoped_thread_state_change-inl.h" diff --git a/runtime/oat_file_test.cc b/runtime/oat_file_test.cc index d5fe1f382a..7bf0f84596 100644 --- a/runtime/oat_file_test.cc +++ b/runtime/oat_file_test.cc @@ -45,13 +45,13 @@ TEST_F(OatFileTest, ResolveRelativeEncodedDexLocation) { OatFile::ResolveRelativeEncodedDexLocation( "/data/app/foo/base.apk", "foo/base.apk")); - EXPECT_EQ(std::string("/data/app/foo/base.apk:classes2.dex"), + EXPECT_EQ(std::string("/data/app/foo/base.apk!classes2.dex"), OatFile::ResolveRelativeEncodedDexLocation( - "/data/app/foo/base.apk", "base.apk:classes2.dex")); + "/data/app/foo/base.apk", "base.apk!classes2.dex")); - EXPECT_EQ(std::string("/data/app/foo/base.apk:classes11.dex"), + EXPECT_EQ(std::string("/data/app/foo/base.apk!classes11.dex"), OatFile::ResolveRelativeEncodedDexLocation( - "/data/app/foo/base.apk", "base.apk:classes11.dex")); + "/data/app/foo/base.apk", "base.apk!classes11.dex")); EXPECT_EQ(std::string("base.apk"), OatFile::ResolveRelativeEncodedDexLocation( diff --git a/runtime/oat_quick_method_header.h b/runtime/oat_quick_method_header.h index 152b0ba21b..3625b9e7a7 100644 --- a/runtime/oat_quick_method_header.h +++ b/runtime/oat_quick_method_header.h @@ -19,8 +19,8 @@ #include "arch/instruction_set.h" #include "base/macros.h" -#include "quick/quick_method_frame_info.h" #include "method_info.h" +#include "quick/quick_method_frame_info.h" #include "stack_map.h" #include "utils.h" diff --git a/runtime/object_lock.cc b/runtime/object_lock.cc index f6db544276..744bc42858 100644 --- a/runtime/object_lock.cc +++ b/runtime/object_lock.cc @@ -16,8 +16,8 @@ #include "object_lock.h" -#include "mirror/object-inl.h" #include "mirror/class_ext.h" +#include "mirror/object-inl.h" #include "monitor.h" namespace art { diff --git a/runtime/openjdkjvm/OpenjdkJvm.cc b/runtime/openjdkjvm/OpenjdkJvm.cc index 6a8f2cedca..b212ea1c20 100644 --- a/runtime/openjdkjvm/OpenjdkJvm.cc +++ b/runtime/openjdkjvm/OpenjdkJvm.cc @@ -32,12 +32,12 @@ /* * Services that OpenJDK expects the VM to provide. */ -#include<stdio.h> #include <dlfcn.h> #include <limits.h> -#include <sys/time.h> -#include <sys/socket.h> +#include <stdio.h> #include <sys/ioctl.h> +#include <sys/socket.h> +#include <sys/time.h> #include <unistd.h> #include "../../libcore/ojluni/src/main/native/jvm.h" // TODO(narayan): fix it @@ -53,12 +53,12 @@ #include "mirror/string-inl.h" #include "monitor.h" #include "native/scoped_fast_native_object_access-inl.h" +#include "nativehelper/ScopedLocalRef.h" +#include "nativehelper/ScopedUtfChars.h" #include "runtime.h" +#include "scoped_thread_state_change-inl.h" #include "thread.h" #include "thread_list.h" -#include "scoped_thread_state_change-inl.h" -#include "ScopedLocalRef.h" -#include "ScopedUtfChars.h" #include "verify_object.h" #undef LOG_TAG diff --git a/runtime/openjdkjvmti/events-inl.h b/runtime/openjdkjvmti/events-inl.h index f30d7cecb3..43177ab845 100644 --- a/runtime/openjdkjvmti/events-inl.h +++ b/runtime/openjdkjvmti/events-inl.h @@ -21,7 +21,7 @@ #include "events.h" #include "jni_internal.h" -#include "ScopedLocalRef.h" +#include "nativehelper/ScopedLocalRef.h" #include "ti_breakpoint.h" #include "art_jvmti.h" diff --git a/runtime/openjdkjvmti/events.cc b/runtime/openjdkjvmti/events.cc index f749daa918..7a930d4163 100644 --- a/runtime/openjdkjvmti/events.cc +++ b/runtime/openjdkjvmti/events.cc @@ -31,9 +31,9 @@ #include "events-inl.h" +#include "art_field-inl.h" #include "art_jvmti.h" #include "art_method-inl.h" -#include "art_field-inl.h" #include "base/logging.h" #include "gc/allocation_listener.h" #include "gc/gc_pause_listener.h" @@ -45,8 +45,8 @@ #include "jni_internal.h" #include "mirror/class.h" #include "mirror/object-inl.h" +#include "nativehelper/ScopedLocalRef.h" #include "runtime.h" -#include "ScopedLocalRef.h" #include "scoped_thread_state_change-inl.h" #include "thread-inl.h" #include "thread_list.h" diff --git a/runtime/openjdkjvmti/fixed_up_dex_file.h b/runtime/openjdkjvmti/fixed_up_dex_file.h index db12f489e9..a96ee1219e 100644 --- a/runtime/openjdkjvmti/fixed_up_dex_file.h +++ b/runtime/openjdkjvmti/fixed_up_dex_file.h @@ -37,6 +37,7 @@ #include "jni.h" #include "jvmti.h" + #include "base/mutex.h" #include "dex_file.h" diff --git a/runtime/openjdkjvmti/jvmti_weak_table-inl.h b/runtime/openjdkjvmti/jvmti_weak_table-inl.h index 64ab3e7b2e..a640acbe98 100644 --- a/runtime/openjdkjvmti/jvmti_weak_table-inl.h +++ b/runtime/openjdkjvmti/jvmti_weak_table-inl.h @@ -44,8 +44,8 @@ #include "jvmti_allocator.h" #include "mirror/class.h" #include "mirror/object.h" +#include "nativehelper/ScopedLocalRef.h" #include "runtime.h" -#include "ScopedLocalRef.h" namespace openjdkjvmti { diff --git a/runtime/openjdkjvmti/ti_breakpoint.cc b/runtime/openjdkjvmti/ti_breakpoint.cc index 6d0e2c60c1..f5116a8080 100644 --- a/runtime/openjdkjvmti/ti_breakpoint.cc +++ b/runtime/openjdkjvmti/ti_breakpoint.cc @@ -42,9 +42,9 @@ #include "mirror/class-inl.h" #include "mirror/object_array-inl.h" #include "modifiers.h" +#include "nativehelper/ScopedLocalRef.h" #include "runtime_callbacks.h" #include "scoped_thread_state_change-inl.h" -#include "ScopedLocalRef.h" #include "thread-current-inl.h" #include "thread_list.h" #include "ti_phase.h" diff --git a/runtime/openjdkjvmti/ti_class.cc b/runtime/openjdkjvmti/ti_class.cc index 99dfcfe665..954b5d1d03 100644 --- a/runtime/openjdkjvmti/ti_class.cc +++ b/runtime/openjdkjvmti/ti_class.cc @@ -39,14 +39,14 @@ #include "art_jvmti.h" #include "base/array_ref.h" #include "base/macros.h" -#include "class_table-inl.h" #include "class_linker.h" +#include "class_table-inl.h" #include "common_throws.h" #include "dex_file_annotations.h" #include "events-inl.h" #include "fixed_up_dex_file.h" -#include "gc/heap.h" #include "gc/heap-visit-objects-inl.h" +#include "gc/heap.h" #include "gc_root.h" #include "handle.h" #include "jni_env_ext-inl.h" @@ -54,16 +54,16 @@ #include "mirror/array-inl.h" #include "mirror/class-inl.h" #include "mirror/class_ext.h" -#include "mirror/object_array-inl.h" -#include "mirror/object_reference.h" #include "mirror/object-inl.h" #include "mirror/object-refvisitor-inl.h" +#include "mirror/object_array-inl.h" +#include "mirror/object_reference.h" #include "mirror/reference.h" +#include "nativehelper/ScopedLocalRef.h" #include "primitive.h" #include "reflection.h" #include "runtime.h" #include "runtime_callbacks.h" -#include "ScopedLocalRef.h" #include "scoped_thread_state_change-inl.h" #include "thread-current-inl.h" #include "thread_list.h" diff --git a/runtime/openjdkjvmti/ti_class_definition.cc b/runtime/openjdkjvmti/ti_class_definition.cc index 8e8ab196f6..c73ef0d31c 100644 --- a/runtime/openjdkjvmti/ti_class_definition.cc +++ b/runtime/openjdkjvmti/ti_class_definition.cc @@ -35,10 +35,10 @@ #include "class_linker-inl.h" #include "dex_file.h" #include "fixed_up_dex_file.h" -#include "handle_scope-inl.h" #include "handle.h" -#include "mirror/class_ext.h" +#include "handle_scope-inl.h" #include "mirror/class-inl.h" +#include "mirror/class_ext.h" #include "mirror/object-inl.h" #include "reflection.h" #include "thread.h" diff --git a/runtime/openjdkjvmti/ti_class_loader.cc b/runtime/openjdkjvmti/ti_class_loader.cc index 205046c894..e81e4bc803 100644 --- a/runtime/openjdkjvmti/ti_class_loader.cc +++ b/runtime/openjdkjvmti/ti_class_loader.cc @@ -51,9 +51,9 @@ #include "mirror/class.h" #include "mirror/class_ext.h" #include "mirror/object.h" +#include "nativehelper/ScopedLocalRef.h" #include "object_lock.h" #include "runtime.h" -#include "ScopedLocalRef.h" #include "transform.h" namespace openjdkjvmti { diff --git a/runtime/openjdkjvmti/ti_class_loader.h b/runtime/openjdkjvmti/ti_class_loader.h index 1ac49886cb..af66c5fd4d 100644 --- a/runtime/openjdkjvmti/ti_class_loader.h +++ b/runtime/openjdkjvmti/ti_class_loader.h @@ -57,8 +57,8 @@ #include "obj_ptr.h" #include "scoped_thread_state_change-inl.h" #include "stack.h" -#include "ti_class_definition.h" #include "thread_list.h" +#include "ti_class_definition.h" #include "transform.h" #include "utf.h" #include "utils/dex_cache_arrays_layout-inl.h" diff --git a/runtime/openjdkjvmti/ti_dump.cc b/runtime/openjdkjvmti/ti_dump.cc index 7a1e53f6e5..809a5e47bb 100644 --- a/runtime/openjdkjvmti/ti_dump.cc +++ b/runtime/openjdkjvmti/ti_dump.cc @@ -33,7 +33,6 @@ #include <limits> - #include "art_jvmti.h" #include "base/mutex.h" #include "events-inl.h" diff --git a/runtime/openjdkjvmti/ti_field.cc b/runtime/openjdkjvmti/ti_field.cc index 32c064e89c..c45b926695 100644 --- a/runtime/openjdkjvmti/ti_field.cc +++ b/runtime/openjdkjvmti/ti_field.cc @@ -31,8 +31,8 @@ #include "ti_field.h" -#include "art_jvmti.h" #include "art_field-inl.h" +#include "art_jvmti.h" #include "base/enums.h" #include "dex_file_annotations.h" #include "jni_internal.h" diff --git a/runtime/openjdkjvmti/ti_heap.cc b/runtime/openjdkjvmti/ti_heap.cc index 91fdaca427..3397210151 100644 --- a/runtime/openjdkjvmti/ti_heap.cc +++ b/runtime/openjdkjvmti/ti_heap.cc @@ -21,8 +21,8 @@ #include "base/macros.h" #include "base/mutex.h" #include "class_linker.h" -#include "gc/heap.h" #include "gc/heap-visit-objects-inl.h" +#include "gc/heap.h" #include "gc_root-inl.h" #include "java_frame_root_info.h" #include "jni_env_ext.h" @@ -31,8 +31,8 @@ #include "mirror/class.h" #include "mirror/object-inl.h" #include "mirror/object_array-inl.h" -#include "object_tagging.h" #include "obj_ptr-inl.h" +#include "object_tagging.h" #include "primitive.h" #include "runtime.h" #include "scoped_thread_state_change-inl.h" diff --git a/runtime/openjdkjvmti/ti_method.cc b/runtime/openjdkjvmti/ti_method.cc index 9b5b964a4d..ab434d7d9a 100644 --- a/runtime/openjdkjvmti/ti_method.cc +++ b/runtime/openjdkjvmti/ti_method.cc @@ -39,9 +39,9 @@ #include "jni_internal.h" #include "mirror/object_array-inl.h" #include "modifiers.h" +#include "nativehelper/ScopedLocalRef.h" #include "runtime_callbacks.h" #include "scoped_thread_state_change-inl.h" -#include "ScopedLocalRef.h" #include "thread-current-inl.h" #include "thread_list.h" #include "ti_phase.h" diff --git a/runtime/openjdkjvmti/ti_phase.cc b/runtime/openjdkjvmti/ti_phase.cc index 3c8bdc61d0..8893c9b4aa 100644 --- a/runtime/openjdkjvmti/ti_phase.cc +++ b/runtime/openjdkjvmti/ti_phase.cc @@ -34,9 +34,9 @@ #include "art_jvmti.h" #include "base/macros.h" #include "events-inl.h" +#include "nativehelper/ScopedLocalRef.h" #include "runtime.h" #include "runtime_callbacks.h" -#include "ScopedLocalRef.h" #include "scoped_thread_state_change-inl.h" #include "thread-current-inl.h" #include "thread_list.h" diff --git a/runtime/openjdkjvmti/ti_properties.cc b/runtime/openjdkjvmti/ti_properties.cc index e399b484ec..c412814d8d 100644 --- a/runtime/openjdkjvmti/ti_properties.cc +++ b/runtime/openjdkjvmti/ti_properties.cc @@ -35,8 +35,8 @@ #include <vector> #include "jni.h" -#include "ScopedLocalRef.h" -#include "ScopedUtfChars.h" +#include "nativehelper/ScopedLocalRef.h" +#include "nativehelper/ScopedUtfChars.h" #include "art_jvmti.h" #include "runtime.h" diff --git a/runtime/openjdkjvmti/ti_redefine.cc b/runtime/openjdkjvmti/ti_redefine.cc index debee913ee..c679d731fe 100644 --- a/runtime/openjdkjvmti/ti_redefine.cc +++ b/runtime/openjdkjvmti/ti_redefine.cc @@ -36,10 +36,11 @@ #include "android-base/stringprintf.h" #include "art_field-inl.h" -#include "art_method-inl.h" #include "art_jvmti.h" +#include "art_method-inl.h" #include "base/array_ref.h" #include "base/logging.h" +#include "base/stringpiece.h" #include "class_linker-inl.h" #include "debugger.h" #include "dex_file.h" @@ -60,10 +61,10 @@ #include "mirror/class-inl.h" #include "mirror/class_ext.h" #include "mirror/object.h" +#include "nativehelper/ScopedLocalRef.h" #include "non_debuggable_classes.h" #include "object_lock.h" #include "runtime.h" -#include "ScopedLocalRef.h" #include "ti_breakpoint.h" #include "ti_class_loader.h" #include "transform.h" @@ -572,13 +573,15 @@ void Redefiner::ClassRedefinition::FindAndAllocateObsoleteMethods(art::mirror::C // Try and get the declared method. First try to get a virtual method then a direct method if that's // not found. static art::ArtMethod* FindMethod(art::Handle<art::mirror::Class> klass, - const char* name, + art::StringPiece name, art::Signature sig) REQUIRES_SHARED(art::Locks::mutator_lock_) { - art::ArtMethod* m = klass->FindDeclaredVirtualMethod(name, sig, art::kRuntimePointerSize); - if (m == nullptr) { - m = klass->FindDeclaredDirectMethod(name, sig, art::kRuntimePointerSize); + DCHECK(!klass->IsProxyClass()); + for (art::ArtMethod& m : klass->GetDeclaredMethodsSlice(art::kRuntimePointerSize)) { + if (m.GetName() == name && m.GetSignature() == sig) { + return &m; + } } - return m; + return nullptr; } bool Redefiner::ClassRedefinition::CheckSameMethods() { @@ -1368,7 +1371,7 @@ void Redefiner::ClassRedefinition::UpdateMethods(art::ObjPtr<art::mirror::Class> const art::DexFile::TypeId& declaring_class_id = dex_file_->GetTypeId(class_def.class_idx_); const art::DexFile& old_dex_file = mclass->GetDexFile(); // Update methods. - for (art::ArtMethod& method : mclass->GetMethods(image_pointer_size)) { + for (art::ArtMethod& method : mclass->GetDeclaredMethods(image_pointer_size)) { const art::DexFile::StringId* new_name_id = dex_file_->FindStringId(method.GetName()); art::dex::TypeIndex method_return_idx = dex_file_->GetIndexForTypeId(*dex_file_->FindTypeId(method.GetReturnTypeDescriptor())); diff --git a/runtime/openjdkjvmti/ti_redefine.h b/runtime/openjdkjvmti/ti_redefine.h index 27d7c3d726..03b4bf2074 100644 --- a/runtime/openjdkjvmti/ti_redefine.h +++ b/runtime/openjdkjvmti/ti_redefine.h @@ -57,8 +57,8 @@ #include "obj_ptr.h" #include "scoped_thread_state_change-inl.h" #include "stack.h" -#include "ti_class_definition.h" #include "thread_list.h" +#include "ti_class_definition.h" #include "transform.h" #include "utf.h" #include "utils/dex_cache_arrays_layout-inl.h" diff --git a/runtime/openjdkjvmti/ti_search.cc b/runtime/openjdkjvmti/ti_search.cc index 6e0196edc3..25bc5d6eb3 100644 --- a/runtime/openjdkjvmti/ti_search.cc +++ b/runtime/openjdkjvmti/ti_search.cc @@ -43,14 +43,14 @@ #include "mirror/class-inl.h" #include "mirror/object.h" #include "mirror/string.h" +#include "nativehelper/ScopedLocalRef.h" #include "obj_ptr-inl.h" #include "runtime.h" #include "runtime_callbacks.h" #include "scoped_thread_state_change-inl.h" -#include "ScopedLocalRef.h" -#include "ti_phase.h" #include "thread-current-inl.h" #include "thread_list.h" +#include "ti_phase.h" #include "well_known_classes.h" namespace openjdkjvmti { @@ -105,17 +105,21 @@ static void Update() REQUIRES_SHARED(art::Locks::mutator_lock_) { } art::ArtMethod* get_property = - properties_class->FindDeclaredVirtualMethod( + properties_class->FindClassMethod( "getProperty", "(Ljava/lang/String;)Ljava/lang/String;", art::kRuntimePointerSize); DCHECK(get_property != nullptr); + DCHECK(!get_property->IsDirect()); + DCHECK(get_property->GetDeclaringClass() == properties_class); art::ArtMethod* set_property = - properties_class->FindDeclaredVirtualMethod( + properties_class->FindClassMethod( "setProperty", "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/Object;", art::kRuntimePointerSize); DCHECK(set_property != nullptr); + DCHECK(!set_property->IsDirect()); + DCHECK(set_property->GetDeclaringClass() == properties_class); // This is an allocation. Do this late to avoid the need for handles. ScopedLocalRef<jobject> cp_jobj(self->GetJniEnv(), nullptr); diff --git a/runtime/openjdkjvmti/ti_stack.cc b/runtime/openjdkjvmti/ti_stack.cc index edb6ffe241..ff2de8dac6 100644 --- a/runtime/openjdkjvmti/ti_stack.cc +++ b/runtime/openjdkjvmti/ti_stack.cc @@ -37,8 +37,8 @@ #include <vector> #include "art_field-inl.h" -#include "art_method-inl.h" #include "art_jvmti.h" +#include "art_method-inl.h" #include "barrier.h" #include "base/bit_utils.h" #include "base/enums.h" @@ -50,8 +50,8 @@ #include "jni_internal.h" #include "mirror/class.h" #include "mirror/dex_cache.h" +#include "nativehelper/ScopedLocalRef.h" #include "scoped_thread_state_change-inl.h" -#include "ScopedLocalRef.h" #include "stack.h" #include "thread-current-inl.h" #include "thread_list.h" diff --git a/runtime/openjdkjvmti/ti_thread.cc b/runtime/openjdkjvmti/ti_thread.cc index fe0e3bbf44..9acea2a288 100644 --- a/runtime/openjdkjvmti/ti_thread.cc +++ b/runtime/openjdkjvmti/ti_thread.cc @@ -43,14 +43,14 @@ #include "mirror/class.h" #include "mirror/object-inl.h" #include "mirror/string.h" +#include "nativehelper/ScopedLocalRef.h" #include "obj_ptr.h" -#include "ti_phase.h" #include "runtime.h" #include "runtime_callbacks.h" -#include "ScopedLocalRef.h" #include "scoped_thread_state_change-inl.h" #include "thread-current-inl.h" #include "thread_list.h" +#include "ti_phase.h" #include "well_known_classes.h" namespace openjdkjvmti { @@ -701,7 +701,7 @@ jvmtiError ThreadUtil::RunAgentThread(jvmtiEnv* jvmti_env, jvmtiError ThreadUtil::SuspendOther(art::Thread* self, jthread target_jthread, - art::Thread* target) { + const art::Thread* target) { // Loop since we need to bail out and try again if we would end up getting suspended while holding // the user_code_suspension_lock_ due to a SuspendReason::kForUserCode. In this situation we // release the lock, wait to get resumed and try again. @@ -729,12 +729,12 @@ jvmtiError ThreadUtil::SuspendOther(art::Thread* self, if (state == art::ThreadState::kTerminated || state == art::ThreadState::kStarting) { return ERR(THREAD_NOT_ALIVE); } - target = art::Runtime::Current()->GetThreadList()->SuspendThreadByPeer( + art::Thread* ret_target = art::Runtime::Current()->GetThreadList()->SuspendThreadByPeer( target_jthread, /* request_suspension */ true, art::SuspendReason::kForUserCode, &timeout); - if (target == nullptr && !timeout) { + if (ret_target == nullptr && !timeout) { // TODO It would be good to get more information about why exactly the thread failed to // suspend. return ERR(INTERNAL); diff --git a/runtime/openjdkjvmti/ti_thread.h b/runtime/openjdkjvmti/ti_thread.h index d07dc0682b..0f7e8379fd 100644 --- a/runtime/openjdkjvmti/ti_thread.h +++ b/runtime/openjdkjvmti/ti_thread.h @@ -98,7 +98,9 @@ class ThreadUtil { // cause the thread to wake up if the thread is suspended for the debugger or gc or something. static jvmtiError SuspendSelf(art::Thread* self) REQUIRES(!art::Locks::mutator_lock_, !art::Locks::user_code_suspension_lock_); - static jvmtiError SuspendOther(art::Thread* self, jthread target_jthread, art::Thread* target) + static jvmtiError SuspendOther(art::Thread* self, + jthread target_jthread, + const art::Thread* target) REQUIRES(!art::Locks::mutator_lock_, !art::Locks::user_code_suspension_lock_); static art::ArtField* context_class_loader_; diff --git a/runtime/openjdkjvmti/transform.h b/runtime/openjdkjvmti/transform.h index ba40e04b44..ed24068bb2 100644 --- a/runtime/openjdkjvmti/transform.h +++ b/runtime/openjdkjvmti/transform.h @@ -35,10 +35,10 @@ #include <string> #include <jni.h> +#include "jvmti.h" #include "art_jvmti.h" #include "ti_class_definition.h" -#include "jvmti.h" namespace openjdkjvmti { diff --git a/runtime/os_linux.cc b/runtime/os_linux.cc index 0add4965d1..a463f700d8 100644 --- a/runtime/os_linux.cc +++ b/runtime/os_linux.cc @@ -16,9 +16,10 @@ #include "os.h" -#include <sys/types.h> -#include <sys/stat.h> #include <fcntl.h> +#include <sys/stat.h> +#include <sys/types.h> + #include <cstddef> #include <memory> diff --git a/runtime/parsed_options.h b/runtime/parsed_options.h index 1f5beb9984..0f8555a829 100644 --- a/runtime/parsed_options.h +++ b/runtime/parsed_options.h @@ -22,11 +22,11 @@ #include <jni.h> -#include "globals.h" +#include "arch/instruction_set.h" #include "gc/collector_type.h" #include "gc/space/large_object_space.h" -#include "arch/instruction_set.h" -#include "jit/profile_saver_options.h" +#include "globals.h" +// #include "jit/profile_saver_options.h" #include "runtime_options.h" namespace art { diff --git a/runtime/proxy_test.cc b/runtime/proxy_test.cc index 4e95b019b9..b055bf94d8 100644 --- a/runtime/proxy_test.cc +++ b/runtime/proxy_test.cc @@ -18,6 +18,7 @@ #include <vector> #include "art_field-inl.h" +#include "art_method-inl.h" #include "base/enums.h" #include "class_linker-inl.h" #include "common_compiler_test.h" @@ -63,21 +64,27 @@ class ProxyTest : public CommonCompilerTest { jsize array_index = 0; // Fill the method array DCHECK_EQ(Runtime::Current()->GetClassLinker()->GetImagePointerSize(), kRuntimePointerSize); - ArtMethod* method = javaLangObject->FindDeclaredVirtualMethod( + ArtMethod* method = javaLangObject->FindClassMethod( "equals", "(Ljava/lang/Object;)Z", kRuntimePointerSize); CHECK(method != nullptr); + CHECK(!method->IsDirect()); + CHECK(method->GetDeclaringClass() == javaLangObject); DCHECK(!Runtime::Current()->IsActiveTransaction()); soa.Env()->SetObjectArrayElement( proxyClassMethods, array_index++, soa.AddLocalReference<jobject>( mirror::Method::CreateFromArtMethod<kRuntimePointerSize, false>(soa.Self(), method))); - method = javaLangObject->FindDeclaredVirtualMethod("hashCode", "()I", kRuntimePointerSize); + method = javaLangObject->FindClassMethod("hashCode", "()I", kRuntimePointerSize); CHECK(method != nullptr); + CHECK(!method->IsDirect()); + CHECK(method->GetDeclaringClass() == javaLangObject); soa.Env()->SetObjectArrayElement( proxyClassMethods, array_index++, soa.AddLocalReference<jobject>( mirror::Method::CreateFromArtMethod<kRuntimePointerSize, false>(soa.Self(), method))); - method = javaLangObject->FindDeclaredVirtualMethod( + method = javaLangObject->FindClassMethod( "toString", "()Ljava/lang/String;", kRuntimePointerSize); CHECK(method != nullptr); + CHECK(!method->IsDirect()); + CHECK(method->GetDeclaringClass() == javaLangObject); soa.Env()->SetObjectArrayElement( proxyClassMethods, array_index++, soa.AddLocalReference<jobject>( mirror::Method::CreateFromArtMethod<kRuntimePointerSize, false>(soa.Self(), method))); diff --git a/runtime/read_barrier-inl.h b/runtime/read_barrier-inl.h index 2d06e54f78..b0935c0b56 100644 --- a/runtime/read_barrier-inl.h +++ b/runtime/read_barrier-inl.h @@ -22,8 +22,8 @@ #include "gc/accounting/read_barrier_table.h" #include "gc/collector/concurrent_copying-inl.h" #include "gc/heap.h" -#include "mirror/object_reference.h" #include "mirror/object-readbarrier-inl.h" +#include "mirror/object_reference.h" #include "mirror/reference.h" #include "runtime.h" #include "utils.h" diff --git a/runtime/read_barrier.h b/runtime/read_barrier.h index ca776854cb..d36acbcdc4 100644 --- a/runtime/read_barrier.h +++ b/runtime/read_barrier.h @@ -18,8 +18,8 @@ #define ART_RUNTIME_READ_BARRIER_H_ #include "base/logging.h" -#include "base/mutex.h" #include "base/macros.h" +#include "base/mutex.h" #include "gc_root.h" #include "jni.h" #include "mirror/object_reference.h" diff --git a/runtime/reference_table.cc b/runtime/reference_table.cc index d8b9dcc016..e6e588e9b0 100644 --- a/runtime/reference_table.cc +++ b/runtime/reference_table.cc @@ -20,10 +20,10 @@ #include "base/mutex.h" #include "indirect_reference_table.h" -#include "mirror/array.h" #include "mirror/array-inl.h" -#include "mirror/class.h" +#include "mirror/array.h" #include "mirror/class-inl.h" +#include "mirror/class.h" #include "mirror/object-inl.h" #include "mirror/string-inl.h" #include "runtime-inl.h" diff --git a/runtime/reference_table_test.cc b/runtime/reference_table_test.cc index 260be8f41f..d830387b24 100644 --- a/runtime/reference_table_test.cc +++ b/runtime/reference_table_test.cc @@ -56,8 +56,8 @@ static mirror::Object* CreateWeakReference(mirror::Object* referent) h_ref_class->AllocObject(self))); CHECK(h_ref_instance != nullptr); - ArtMethod* constructor = h_ref_class->FindDeclaredDirectMethod( - "<init>", "(Ljava/lang/Object;)V", class_linker->GetImagePointerSize()); + ArtMethod* constructor = h_ref_class->FindConstructor( + "(Ljava/lang/Object;)V", class_linker->GetImagePointerSize()); CHECK(constructor != nullptr); uint32_t args[2]; diff --git a/runtime/reflection.cc b/runtime/reflection.cc index 532da2b16e..6f1d15c767 100644 --- a/runtime/reflection.cc +++ b/runtime/reflection.cc @@ -28,10 +28,10 @@ #include "mirror/class-inl.h" #include "mirror/executable.h" #include "mirror/object_array-inl.h" +#include "nativehelper/ScopedLocalRef.h" #include "nth_caller_visitor.h" #include "scoped_thread_state_change-inl.h" #include "stack_reference.h" -#include "ScopedLocalRef.h" #include "well_known_classes.h" namespace art { diff --git a/runtime/reflection_test.cc b/runtime/reflection_test.cc index 1ba4b7b9d1..fa2f1e5793 100644 --- a/runtime/reflection_test.cc +++ b/runtime/reflection_test.cc @@ -18,13 +18,13 @@ #include <float.h> #include <limits.h> -#include "ScopedLocalRef.h" #include "art_method-inl.h" #include "base/enums.h" #include "common_compiler_test.h" #include "java_vm_ext.h" #include "jni_internal.h" +#include "nativehelper/ScopedLocalRef.h" #include "scoped_thread_state_change-inl.h" namespace art { @@ -108,9 +108,9 @@ class ReflectionTest : public CommonCompilerTest { class_loader); CHECK(c != nullptr); - *method = is_static ? c->FindDirectMethod(method_name, method_signature, kRuntimePointerSize) - : c->FindVirtualMethod(method_name, method_signature, kRuntimePointerSize); - CHECK(method != nullptr); + *method = c->FindClassMethod(method_name, method_signature, kRuntimePointerSize); + CHECK(*method != nullptr); + CHECK_EQ(is_static, (*method)->IsStatic()); if (is_static) { *receiver = nullptr; @@ -520,10 +520,11 @@ TEST_F(ReflectionTest, StaticMainMethod) { mirror::Class* klass = class_linker_->FindClass(soa.Self(), "LMain;", class_loader); ASSERT_TRUE(klass != nullptr); - ArtMethod* method = klass->FindDirectMethod("main", - "([Ljava/lang/String;)V", - kRuntimePointerSize); + ArtMethod* method = klass->FindClassMethod("main", + "([Ljava/lang/String;)V", + kRuntimePointerSize); ASSERT_TRUE(method != nullptr); + ASSERT_TRUE(method->IsStatic()); // Start runtime. bool started = runtime_->Start(); diff --git a/runtime/runtime.cc b/runtime/runtime.cc index bf9e4051ae..6fbf64b14f 100644 --- a/runtime/runtime.cc +++ b/runtime/runtime.cc @@ -23,9 +23,10 @@ #include <sys/prctl.h> #endif +#include <fcntl.h> #include <signal.h> #include <sys/syscall.h> -#include "base/memory_tool.h" + #if defined(__APPLE__) #include <crt_externs.h> // for _NSGetEnviron #endif @@ -33,14 +34,10 @@ #include <cstdio> #include <cstdlib> #include <limits> -#include <memory_representation.h> #include <vector> -#include <fcntl.h> #include "android-base/strings.h" -#include "JniConstants.h" -#include "ScopedLocalRef.h" #include "arch/arm/quick_method_frame_info_arm.h" #include "arch/arm/registers_arm.h" #include "arch/arm64/quick_method_frame_info_arm64.h" @@ -62,10 +59,10 @@ #include "base/arena_allocator.h" #include "base/dumpable.h" #include "base/enums.h" +#include "base/memory_tool.h" #include "base/stl_util.h" #include "base/systrace.h" #include "base/unix_file/fd_file.h" -#include "cha.h" #include "class_linker-inl.h" #include "compiler_callbacks.h" #include "debugger.h" @@ -87,8 +84,10 @@ #include "java_vm_ext.h" #include "jit/jit.h" #include "jit/jit_code_cache.h" +#include "jit/profile_saver.h" #include "jni_internal.h" #include "linear_alloc.h" +#include "memory_representation.h" #include "mirror/array.h" #include "mirror/class-inl.h" #include "mirror/class_ext.h" @@ -133,17 +132,17 @@ #include "native/sun_misc_Unsafe.h" #include "native_bridge_art_interface.h" #include "native_stack_dump.h" +#include "nativehelper/JniConstants.h" +#include "nativehelper/ScopedLocalRef.h" #include "oat_file.h" #include "oat_file_manager.h" #include "object_callbacks.h" #include "os.h" #include "parsed_options.h" -#include "jit/profile_saver.h" #include "quick/quick_method_frame_info.h" #include "reflection.h" #include "runtime_callbacks.h" #include "runtime_options.h" -#include "ScopedLocalRef.h" #include "scoped_thread_state_change-inl.h" #include "sigchain.h" #include "signal_catcher.h" @@ -259,8 +258,7 @@ Runtime::Runtime() pruned_dalvik_cache_(false), // Initially assume we perceive jank in case the process state is never updated. process_state_(kProcessStateJankPerceptible), - zygote_no_threads_(false), - cha_(nullptr) { + zygote_no_threads_(false) { static_assert(Runtime::kCalleeSaveSize == static_cast<uint32_t>(CalleeSaveType::kLastCalleeSaveType), "Unexpected size"); @@ -382,7 +380,6 @@ Runtime::~Runtime() { delete monitor_list_; delete monitor_pool_; delete class_linker_; - delete cha_; delete heap_; delete intern_table_; delete oat_file_manager_; @@ -633,9 +630,10 @@ static jobject CreateSystemClassLoader(Runtime* runtime) { hs.NewHandle(soa.Decode<mirror::Class>(WellKnownClasses::java_lang_ClassLoader))); CHECK(cl->EnsureInitialized(soa.Self(), class_loader_class, true, true)); - ArtMethod* getSystemClassLoader = class_loader_class->FindDirectMethod( + ArtMethod* getSystemClassLoader = class_loader_class->FindClassMethod( "getSystemClassLoader", "()Ljava/lang/ClassLoader;", pointer_size); CHECK(getSystemClassLoader != nullptr); + CHECK(getSystemClassLoader->IsStatic()); JValue result = InvokeWithJValues(soa, nullptr, @@ -1286,7 +1284,6 @@ bool Runtime::Init(RuntimeArgumentMap&& runtime_options_in) { CHECK_GE(GetHeap()->GetContinuousSpaces().size(), 1U); class_linker_ = new ClassLinker(intern_table_); - cha_ = new ClassHierarchyAnalysis; if (GetHeap()->HasBootImageSpace()) { bool result = class_linker_->InitFromBootImage(&error_msg); if (!result) { @@ -2065,19 +2062,30 @@ void Runtime::RegisterAppInfo(const std::vector<std::string>& code_paths, } // Transaction support. -void Runtime::EnterTransactionMode(Transaction* transaction) { +void Runtime::EnterTransactionMode() { DCHECK(IsAotCompiler()); - DCHECK(transaction != nullptr); DCHECK(!IsActiveTransaction()); - preinitialization_transaction_ = transaction; + preinitialization_transaction_ = std::make_unique<Transaction>(); +} + +void Runtime::EnterTransactionMode(mirror::Class* root) { + DCHECK(IsAotCompiler()); + preinitialization_transaction_ = std::make_unique<Transaction>(root); } void Runtime::ExitTransactionMode() { DCHECK(IsAotCompiler()); - DCHECK(IsActiveTransaction()); preinitialization_transaction_ = nullptr; } +void Runtime::RollbackAndExitTransactionMode() { + DCHECK(IsAotCompiler()); + DCHECK(IsActiveTransaction()); + std::unique_ptr<Transaction> rollback_transaction_= std::move(preinitialization_transaction_); + ExitTransactionMode(); + rollback_transaction_->Rollback(); +} + bool Runtime::IsTransactionAborted() const { if (!IsActiveTransaction()) { return false; diff --git a/runtime/runtime.h b/runtime/runtime.h index af9d215454..7e4b8967df 100644 --- a/runtime/runtime.h +++ b/runtime/runtime.h @@ -24,6 +24,7 @@ #include <set> #include <string> #include <utility> +#include <memory> #include <vector> #include "arch/instruction_set.h" @@ -72,7 +73,6 @@ namespace verifier { class ArenaPool; class ArtMethod; enum class CalleeSaveType: uint32_t; -class ClassHierarchyAnalysis; class ClassLinker; class CompilerCallbacks; class DexFile; @@ -457,8 +457,12 @@ class Runtime { bool IsActiveTransaction() const { return preinitialization_transaction_ != nullptr; } - void EnterTransactionMode(Transaction* transaction); + void EnterTransactionMode(); + void EnterTransactionMode(mirror::Class* root); void ExitTransactionMode(); + // Transaction rollback and exit transaction are always done together, it's convenience to + // do them in one function. + void RollbackAndExitTransactionMode() REQUIRES_SHARED(Locks::mutator_lock_); bool IsTransactionAborted() const; void AbortTransactionAndThrowAbortError(Thread* self, const std::string& abort_message) @@ -645,10 +649,6 @@ class Runtime { void AddSystemWeakHolder(gc::AbstractSystemWeakHolder* holder); void RemoveSystemWeakHolder(gc::AbstractSystemWeakHolder* holder); - ClassHierarchyAnalysis* GetClassHierarchyAnalysis() { - return cha_; - } - void AttachAgent(const std::string& agent_arg); const std::list<ti::Agent>& GetAgents() const { @@ -842,7 +842,7 @@ class Runtime { bool dump_gc_performance_on_shutdown_; // Transaction used for pre-initializing classes at compilation time. - Transaction* preinitialization_transaction_; + std::unique_ptr<Transaction> preinitialization_transaction_; // If kNone, verification is disabled. kEnable by default. verifier::VerifyMode verify_; @@ -939,8 +939,6 @@ class Runtime { // Generic system-weak holders. std::vector<gc::AbstractSystemWeakHolder*> system_weak_holders_; - ClassHierarchyAnalysis* cha_; - std::unique_ptr<RuntimeCallbacks> callbacks_; std::atomic<uint32_t> deoptimization_counts_[ diff --git a/runtime/runtime_callbacks_test.cc b/runtime/runtime_callbacks_test.cc index 640f9ce848..983278d97e 100644 --- a/runtime/runtime_callbacks_test.cc +++ b/runtime/runtime_callbacks_test.cc @@ -16,7 +16,6 @@ #include "runtime_callbacks.h" -#include "jni.h" #include <signal.h> #include <sys/types.h> #include <unistd.h> @@ -25,6 +24,8 @@ #include <memory> #include <string> +#include "jni.h" + #include "art_method-inl.h" #include "base/mutex.h" #include "class_linker.h" @@ -34,10 +35,10 @@ #include "mem_map.h" #include "mirror/class-inl.h" #include "mirror/class_loader.h" +#include "nativehelper/ScopedLocalRef.h" #include "obj_ptr.h" #include "runtime.h" #include "scoped_thread_state_change-inl.h" -#include "ScopedLocalRef.h" #include "thread-inl.h" #include "thread_list.h" #include "well_known_classes.h" diff --git a/runtime/runtime_options.cc b/runtime/runtime_options.cc index aa147198fc..b072bb0c37 100644 --- a/runtime/runtime_options.cc +++ b/runtime/runtime_options.cc @@ -18,13 +18,13 @@ #include <memory> +#include "debugger.h" #include "gc/heap.h" #include "monitor.h" #include "runtime.h" #include "thread_list.h" #include "trace.h" #include "utils.h" -#include "debugger.h" namespace art { diff --git a/runtime/runtime_options.def b/runtime/runtime_options.def index 09a200afc4..78a60faa3a 100644 --- a/runtime/runtime_options.def +++ b/runtime/runtime_options.def @@ -70,7 +70,7 @@ RUNTIME_OPTIONS_KEY (bool, UseTLAB, (kUseT RUNTIME_OPTIONS_KEY (bool, EnableHSpaceCompactForOOM, true) RUNTIME_OPTIONS_KEY (bool, UseJitCompilation, false) RUNTIME_OPTIONS_KEY (bool, DumpNativeStackOnSigQuit, true) -RUNTIME_OPTIONS_KEY (unsigned int, JITCompileThreshold, jit::Jit::kDefaultCompileThreshold) +RUNTIME_OPTIONS_KEY (unsigned int, JITCompileThreshold) RUNTIME_OPTIONS_KEY (unsigned int, JITWarmupThreshold) RUNTIME_OPTIONS_KEY (unsigned int, JITOsrThreshold) RUNTIME_OPTIONS_KEY (unsigned int, JITPriorityThreadWeight) diff --git a/runtime/runtime_options.h b/runtime/runtime_options.h index c509992533..89a1dcb415 100644 --- a/runtime/runtime_options.h +++ b/runtime/runtime_options.h @@ -17,21 +17,20 @@ #ifndef ART_RUNTIME_RUNTIME_OPTIONS_H_ #define ART_RUNTIME_RUNTIME_OPTIONS_H_ -#include <vector> +#include <cstdarg> +#include <cstdio> #include <string> +#include <vector> -#include <stdio.h> -#include <stdarg.h> - +#include "arch/instruction_set.h" #include "base/logging.h" #include "base/variant_map.h" #include "cmdline_types.h" // TODO: don't need to include this file here +#include "gc/collector_type.h" +#include "gc/space/large_object_space.h" #include "jdwp/jdwp.h" #include "jit/jit.h" #include "jit/jit_code_cache.h" -#include "gc/collector_type.h" -#include "gc/space/large_object_space.h" -#include "arch/instruction_set.h" #include "jit/profile_saver_options.h" #include "verifier/verifier_enums.h" diff --git a/runtime/stack_map.h b/runtime/stack_map.h index 21780a1bc9..3931b6242f 100644 --- a/runtime/stack_map.h +++ b/runtime/stack_map.h @@ -20,13 +20,13 @@ #include <limits> #include "arch/code_offset.h" -#include "base/bit_vector.h" #include "base/bit_utils.h" +#include "base/bit_vector.h" #include "bit_memory_region.h" #include "dex_file.h" +#include "leb128.h" #include "memory_region.h" #include "method_info.h" -#include "leb128.h" namespace art { diff --git a/runtime/thread.cc b/runtime/thread.cc index 004b68e204..cdbb90888a 100644 --- a/runtime/thread.cc +++ b/runtime/thread.cc @@ -34,16 +34,16 @@ #include "android-base/stringprintf.h" -#include "arch/context.h" #include "arch/context-inl.h" +#include "arch/context.h" #include "art_field-inl.h" #include "art_method-inl.h" #include "base/bit_utils.h" #include "base/memory_tool.h" #include "base/mutex.h" +#include "base/systrace.h" #include "base/timing_logger.h" #include "base/to_str.h" -#include "base/systrace.h" #include "class_linker-inl.h" #include "debugger.h" #include "dex_file-inl.h" @@ -58,38 +58,38 @@ #include "gc_root.h" #include "handle_scope-inl.h" #include "indirect_reference_table-inl.h" +#include "interpreter/interpreter.h" #include "interpreter/shadow_frame.h" #include "java_frame_root_info.h" #include "java_vm_ext.h" #include "jni_internal.h" -#include "mirror/class_loader.h" #include "mirror/class-inl.h" +#include "mirror/class_loader.h" #include "mirror/object_array-inl.h" #include "mirror/stack_trace_element.h" #include "monitor.h" #include "native_stack_dump.h" +#include "nativehelper/ScopedLocalRef.h" +#include "nativehelper/ScopedUtfChars.h" #include "nth_caller_visitor.h" #include "oat_quick_method_header.h" #include "obj_ptr-inl.h" #include "object_lock.h" -#include "quick_exception_handler.h" #include "quick/quick_method_frame_info.h" +#include "quick_exception_handler.h" #include "read_barrier-inl.h" #include "reflection.h" #include "runtime.h" #include "runtime_callbacks.h" #include "scoped_thread_state_change-inl.h" -#include "ScopedLocalRef.h" -#include "ScopedUtfChars.h" #include "stack.h" #include "stack_map.h" -#include "thread_list.h" #include "thread-inl.h" +#include "thread_list.h" #include "utils.h" #include "verifier/method_verifier.h" #include "verify_object.h" #include "well_known_classes.h" -#include "interpreter/interpreter.h" #if ART_USE_FUTEXES #include "linux/futex.h" @@ -2780,7 +2780,7 @@ void Thread::ThrowNewWrappedException(const char* exception_class_descriptor, } } ArtMethod* exception_init_method = - exception_class->FindDeclaredDirectMethod("<init>", signature, cl->GetImagePointerSize()); + exception_class->FindConstructor(signature, cl->GetImagePointerSize()); CHECK(exception_init_method != nullptr) << "No <init>" << signature << " in " << PrettyDescriptor(exception_class_descriptor); diff --git a/runtime/thread.h b/runtime/thread.h index e1102ed322..776096a7bb 100644 --- a/runtime/thread.h +++ b/runtime/thread.h @@ -17,12 +17,13 @@ #ifndef ART_RUNTIME_THREAD_H_ #define ART_RUNTIME_THREAD_H_ +#include <setjmp.h> + #include <bitset> #include <deque> #include <iosfwd> #include <list> #include <memory> -#include <setjmp.h> #include <string> #include "arch/context.h" diff --git a/runtime/thread_android.cc b/runtime/thread_android.cc index d5db9838ab..8ff6c529b0 100644 --- a/runtime/thread_android.cc +++ b/runtime/thread_android.cc @@ -16,10 +16,10 @@ #include "thread.h" -#include <sys/time.h> -#include <sys/resource.h> -#include <limits.h> #include <errno.h> +#include <limits.h> +#include <sys/resource.h> +#include <sys/time.h> #include <cutils/sched_policy.h> #include <utils/threads.h> diff --git a/runtime/thread_list.cc b/runtime/thread_list.cc index 9c938ffe18..488e4a6517 100644 --- a/runtime/thread_list.cc +++ b/runtime/thread_list.cc @@ -16,16 +16,16 @@ #include "thread_list.h" -#include <backtrace/BacktraceMap.h> #include <dirent.h> -#include <ScopedLocalRef.h> -#include <ScopedUtfChars.h> #include <sys/types.h> #include <unistd.h> #include <sstream> #include "android-base/stringprintf.h" +#include "backtrace/BacktraceMap.h" +#include "nativehelper/ScopedLocalRef.h" +#include "nativehelper/ScopedUtfChars.h" #include "base/histogram-inl.h" #include "base/mutex-inl.h" diff --git a/runtime/thread_pool.cc b/runtime/thread_pool.cc index 8349f33028..fb77b84550 100644 --- a/runtime/thread_pool.cc +++ b/runtime/thread_pool.cc @@ -16,11 +16,11 @@ #include "thread_pool.h" -#include <pthread.h> - #include <sys/mman.h> -#include <sys/time.h> #include <sys/resource.h> +#include <sys/time.h> + +#include <pthread.h> #include "android-base/stringprintf.h" diff --git a/runtime/trace.cc b/runtime/trace.cc index cabd1620a7..36532c6d52 100644 --- a/runtime/trace.cc +++ b/runtime/trace.cc @@ -32,20 +32,20 @@ #include "common_throws.h" #include "debugger.h" #include "dex_file-inl.h" +#include "entrypoints/quick/quick_entrypoints.h" #include "gc/scoped_gc_critical_section.h" #include "instrumentation.h" #include "mirror/class-inl.h" #include "mirror/dex_cache-inl.h" -#include "mirror/object_array-inl.h" #include "mirror/object-inl.h" +#include "mirror/object_array-inl.h" +#include "nativehelper/ScopedLocalRef.h" #include "os.h" #include "scoped_thread_state_change-inl.h" -#include "ScopedLocalRef.h" #include "stack.h" #include "thread.h" #include "thread_list.h" #include "utils.h" -#include "entrypoints/quick/quick_entrypoints.h" namespace art { diff --git a/runtime/transaction.cc b/runtime/transaction.cc index 907d37ef31..9e62aa6b55 100644 --- a/runtime/transaction.cc +++ b/runtime/transaction.cc @@ -16,8 +16,8 @@ #include "transaction.h" -#include "base/stl_util.h" #include "base/logging.h" +#include "base/stl_util.h" #include "gc/accounting/card_table-inl.h" #include "gc_root-inl.h" #include "intern_table.h" @@ -38,6 +38,10 @@ Transaction::Transaction() CHECK(Runtime::Current()->IsAotCompiler()); } +Transaction::Transaction(mirror::Class* root) : Transaction() { + root_ = root; +} + Transaction::~Transaction() { if (kEnableTransactionStats) { MutexLock mu(Thread::Current(), log_lock_); @@ -270,6 +274,7 @@ void Transaction::UndoResolveStringModifications() { void Transaction::VisitRoots(RootVisitor* visitor) { MutexLock mu(Thread::Current(), log_lock_); + visitor->VisitRoot(reinterpret_cast<mirror::Object**>(&root_), RootInfo(kRootUnknown)); VisitObjectLogs(visitor); VisitArrayLogs(visitor); VisitInternStringLogs(visitor); diff --git a/runtime/transaction.h b/runtime/transaction.h index 747c2d0f38..22518f6c7e 100644 --- a/runtime/transaction.h +++ b/runtime/transaction.h @@ -32,6 +32,7 @@ namespace art { namespace mirror { class Array; +class Class; class DexCache; class Object; class String; @@ -44,6 +45,7 @@ class Transaction FINAL { static constexpr const char* kAbortExceptionSignature = "Ldalvik/system/TransactionAbortError;"; Transaction(); + explicit Transaction(mirror::Class* root); ~Transaction(); void Abort(const std::string& abort_message) @@ -288,6 +290,7 @@ class Transaction FINAL { std::list<ResolveStringLog> resolve_string_logs_ GUARDED_BY(log_lock_); bool aborted_ GUARDED_BY(log_lock_); std::string abort_message_ GUARDED_BY(log_lock_); + mirror::Class* root_ GUARDED_BY(log_lock_); DISALLOW_COPY_AND_ASSIGN(Transaction); }; diff --git a/runtime/transaction_test.cc b/runtime/transaction_test.cc index 920629276a..e52dd08540 100644 --- a/runtime/transaction_test.cc +++ b/runtime/transaction_test.cc @@ -69,14 +69,12 @@ class TransactionTest : public CommonRuntimeTest { mirror::Class::Status old_status = h_klass->GetStatus(); LockWord old_lock_word = h_klass->GetLockWord(false); - Transaction transaction; - Runtime::Current()->EnterTransactionMode(&transaction); + Runtime::Current()->EnterTransactionMode(); bool success = class_linker_->EnsureInitialized(soa.Self(), h_klass, true, true); - Runtime::Current()->ExitTransactionMode(); + ASSERT_TRUE(Runtime::Current()->IsTransactionAborted()); ASSERT_FALSE(success); ASSERT_TRUE(h_klass->IsErroneous()); ASSERT_TRUE(soa.Self()->IsExceptionPending()); - ASSERT_TRUE(transaction.IsAborted()); // Check class's monitor get back to its original state without rolling back changes. LockWord new_lock_word = h_klass->GetLockWord(false); @@ -84,7 +82,7 @@ class TransactionTest : public CommonRuntimeTest { // Check class status is rolled back properly. soa.Self()->ClearException(); - transaction.Rollback(); + Runtime::Current()->RollbackAndExitTransactionMode(); ASSERT_EQ(old_status, h_klass->GetStatus()); } }; @@ -97,15 +95,12 @@ TEST_F(TransactionTest, Object_class) { hs.NewHandle(class_linker_->FindSystemClass(soa.Self(), "Ljava/lang/Object;"))); ASSERT_TRUE(h_klass != nullptr); - Transaction transaction; - Runtime::Current()->EnterTransactionMode(&transaction); + Runtime::Current()->EnterTransactionMode(); Handle<mirror::Object> h_obj(hs.NewHandle(h_klass->AllocObject(soa.Self()))); ASSERT_TRUE(h_obj != nullptr); ASSERT_EQ(h_obj->GetClass(), h_klass.Get()); - Runtime::Current()->ExitTransactionMode(); - // Rolling back transaction's changes must not clear the Object::class field. - transaction.Rollback(); + Runtime::Current()->RollbackAndExitTransactionMode(); EXPECT_EQ(h_obj->GetClass(), h_klass.Get()); } @@ -124,15 +119,13 @@ TEST_F(TransactionTest, Object_monitor) { h_obj->MonitorEnter(soa.Self()); LockWord old_lock_word = h_obj->GetLockWord(false); - Transaction transaction; - Runtime::Current()->EnterTransactionMode(&transaction); + Runtime::Current()->EnterTransactionMode(); // Unlock object's monitor inside the transaction. h_obj->MonitorExit(soa.Self()); LockWord new_lock_word = h_obj->GetLockWord(false); - Runtime::Current()->ExitTransactionMode(); - // Rolling back transaction's changes must not change monitor's state. - transaction.Rollback(); + Runtime::Current()->RollbackAndExitTransactionMode(); + LockWord aborted_lock_word = h_obj->GetLockWord(false); EXPECT_FALSE(LockWord::Equal<false>(old_lock_word, new_lock_word)); EXPECT_TRUE(LockWord::Equal<false>(aborted_lock_word, new_lock_word)); @@ -148,8 +141,7 @@ TEST_F(TransactionTest, Array_length) { constexpr int32_t kArraySize = 2; - Transaction transaction; - Runtime::Current()->EnterTransactionMode(&transaction); + Runtime::Current()->EnterTransactionMode(); // Allocate an array during transaction. Handle<mirror::Array> h_obj( @@ -159,10 +151,9 @@ TEST_F(TransactionTest, Array_length) { Runtime::Current()->GetHeap()->GetCurrentAllocator()))); ASSERT_TRUE(h_obj != nullptr); ASSERT_EQ(h_obj->GetClass(), h_klass.Get()); - Runtime::Current()->ExitTransactionMode(); + Runtime::Current()->RollbackAndExitTransactionMode(); // Rolling back transaction's changes must not reset array's length. - transaction.Rollback(); EXPECT_EQ(h_obj->GetLength(), kArraySize); } @@ -238,8 +229,7 @@ TEST_F(TransactionTest, StaticFieldsTest) { ASSERT_EQ(h_obj->GetClass(), h_klass.Get()); // Modify fields inside transaction then rollback changes. - Transaction transaction; - Runtime::Current()->EnterTransactionMode(&transaction); + Runtime::Current()->EnterTransactionMode(); booleanField->SetBoolean<true>(h_klass.Get(), true); byteField->SetByte<true>(h_klass.Get(), 1); charField->SetChar<true>(h_klass.Get(), 1u); @@ -249,8 +239,7 @@ TEST_F(TransactionTest, StaticFieldsTest) { floatField->SetFloat<true>(h_klass.Get(), 1.0); doubleField->SetDouble<true>(h_klass.Get(), 1.0); objectField->SetObject<true>(h_klass.Get(), h_obj.Get()); - Runtime::Current()->ExitTransactionMode(); - transaction.Rollback(); + Runtime::Current()->RollbackAndExitTransactionMode(); // Check values have properly been restored to their original (default) value. EXPECT_EQ(booleanField->GetBoolean(h_klass.Get()), false); @@ -340,8 +329,7 @@ TEST_F(TransactionTest, InstanceFieldsTest) { ASSERT_EQ(h_obj->GetClass(), h_klass.Get()); // Modify fields inside transaction then rollback changes. - Transaction transaction; - Runtime::Current()->EnterTransactionMode(&transaction); + Runtime::Current()->EnterTransactionMode(); booleanField->SetBoolean<true>(h_instance.Get(), true); byteField->SetByte<true>(h_instance.Get(), 1); charField->SetChar<true>(h_instance.Get(), 1u); @@ -351,8 +339,7 @@ TEST_F(TransactionTest, InstanceFieldsTest) { floatField->SetFloat<true>(h_instance.Get(), 1.0); doubleField->SetDouble<true>(h_instance.Get(), 1.0); objectField->SetObject<true>(h_instance.Get(), h_obj.Get()); - Runtime::Current()->ExitTransactionMode(); - transaction.Rollback(); + Runtime::Current()->RollbackAndExitTransactionMode(); // Check values have properly been restored to their original (default) value. EXPECT_EQ(booleanField->GetBoolean(h_instance.Get()), false); @@ -457,8 +444,7 @@ TEST_F(TransactionTest, StaticArrayFieldsTest) { ASSERT_EQ(h_obj->GetClass(), h_klass.Get()); // Modify fields inside transaction then rollback changes. - Transaction transaction; - Runtime::Current()->EnterTransactionMode(&transaction); + Runtime::Current()->EnterTransactionMode(); booleanArray->SetWithoutChecks<true>(0, true); byteArray->SetWithoutChecks<true>(0, 1); charArray->SetWithoutChecks<true>(0, 1u); @@ -468,8 +454,7 @@ TEST_F(TransactionTest, StaticArrayFieldsTest) { floatArray->SetWithoutChecks<true>(0, 1.0); doubleArray->SetWithoutChecks<true>(0, 1.0); objectArray->SetWithoutChecks<true>(0, h_obj.Get()); - Runtime::Current()->ExitTransactionMode(); - transaction.Rollback(); + Runtime::Current()->RollbackAndExitTransactionMode(); // Check values have properly been restored to their original (default) value. EXPECT_EQ(booleanArray->GetWithoutChecks(0), false); @@ -511,8 +496,7 @@ TEST_F(TransactionTest, ResolveString) { EXPECT_TRUE(class_linker_->LookupString(*dex_file, string_idx, h_dex_cache.Get()) == nullptr); EXPECT_TRUE(h_dex_cache->GetResolvedString(string_idx) == nullptr); // Do the transaction, then roll back. - Transaction transaction; - Runtime::Current()->EnterTransactionMode(&transaction); + Runtime::Current()->EnterTransactionMode(); bool success = class_linker_->EnsureInitialized(soa.Self(), h_klass, true, true); ASSERT_TRUE(success); ASSERT_TRUE(h_klass->IsInitialized()); @@ -523,8 +507,7 @@ TEST_F(TransactionTest, ResolveString) { EXPECT_STREQ(s->ToModifiedUtf8().c_str(), kResolvedString); EXPECT_EQ(s, h_dex_cache->GetResolvedString(string_idx)); } - Runtime::Current()->ExitTransactionMode(); - transaction.Rollback(); + Runtime::Current()->RollbackAndExitTransactionMode(); // Check that the string did not stay resolved. EXPECT_TRUE(class_linker_->LookupString(*dex_file, string_idx, h_dex_cache.Get()) == nullptr); EXPECT_TRUE(h_dex_cache->GetResolvedString(string_idx) == nullptr); @@ -547,8 +530,7 @@ TEST_F(TransactionTest, EmptyClass) { class_linker_->VerifyClass(soa.Self(), h_klass); ASSERT_TRUE(h_klass->IsVerified()); - Transaction transaction; - Runtime::Current()->EnterTransactionMode(&transaction); + Runtime::Current()->EnterTransactionMode(); bool success = class_linker_->EnsureInitialized(soa.Self(), h_klass, true, true); Runtime::Current()->ExitTransactionMode(); ASSERT_TRUE(success); @@ -571,8 +553,7 @@ TEST_F(TransactionTest, StaticFieldClass) { class_linker_->VerifyClass(soa.Self(), h_klass); ASSERT_TRUE(h_klass->IsVerified()); - Transaction transaction; - Runtime::Current()->EnterTransactionMode(&transaction); + Runtime::Current()->EnterTransactionMode(); bool success = class_linker_->EnsureInitialized(soa.Self(), h_klass, true, true); Runtime::Current()->ExitTransactionMode(); ASSERT_TRUE(success); diff --git a/runtime/type_lookup_table.cc b/runtime/type_lookup_table.cc index 16cd7227f1..4fab39cd73 100644 --- a/runtime/type_lookup_table.cc +++ b/runtime/type_lookup_table.cc @@ -16,14 +16,14 @@ #include "type_lookup_table.h" +#include <cstring> +#include <memory> + #include "base/bit_utils.h" #include "dex_file-inl.h" #include "utf-inl.h" #include "utils.h" -#include <memory> -#include <cstring> - namespace art { static uint16_t MakeData(uint16_t class_def_idx, uint32_t hash, uint32_t mask) { diff --git a/runtime/type_lookup_table_test.cc b/runtime/type_lookup_table_test.cc index ec38b4154e..ac11871d19 100644 --- a/runtime/type_lookup_table_test.cc +++ b/runtime/type_lookup_table_test.cc @@ -14,13 +14,13 @@ * limitations under the License. */ +#include "type_lookup_table.h" #include <memory> #include "common_runtime_test.h" #include "dex_file-inl.h" #include "scoped_thread_state_change-inl.h" -#include "type_lookup_table.h" #include "utf-inl.h" namespace art { diff --git a/runtime/utils.cc b/runtime/utils.cc index c4b044110c..ffa9d45812 100644 --- a/runtime/utils.cc +++ b/runtime/utils.cc @@ -23,6 +23,7 @@ #include <sys/types.h> #include <sys/wait.h> #include <unistd.h> + #include <memory> #include "android-base/stringprintf.h" @@ -38,9 +39,9 @@ #include "utf-inl.h" #if defined(__APPLE__) -#include "AvailabilityMacros.h" // For MAC_OS_X_VERSION_MAX_ALLOWED -#include <sys/syscall.h> #include <crt_externs.h> +#include <sys/syscall.h> +#include "AvailabilityMacros.h" // For MAC_OS_X_VERSION_MAX_ALLOWED #endif #if defined(__linux__) diff --git a/runtime/utils/dex_cache_arrays_layout-inl.h b/runtime/utils/dex_cache_arrays_layout-inl.h index 72f63c6c07..9d4e9fb96c 100644 --- a/runtime/utils/dex_cache_arrays_layout-inl.h +++ b/runtime/utils/dex_cache_arrays_layout-inl.h @@ -64,7 +64,7 @@ inline constexpr size_t DexCacheArraysLayout::Alignment(PointerSize pointer_size "Expecting alignof(StringDexCacheType) == 8"); static_assert(alignof(mirror::MethodTypeDexCacheType) == 8, "Expecting alignof(MethodTypeDexCacheType) == 8"); - // This is the same as alignof(FieldDexCacheType) for the given pointer size. + // This is the same as alignof({Field,Method}DexCacheType) for the given pointer size. return 2u * static_cast<size_t>(pointer_size); } @@ -84,7 +84,7 @@ inline size_t DexCacheArraysLayout::TypesSize(size_t num_elements) const { if (num_elements < cache_size) { cache_size = num_elements; } - return ArraySize(PointerSize::k64, cache_size); + return PairArraySize(GcRootAsPointerSize<mirror::Class>(), cache_size); } inline size_t DexCacheArraysLayout::TypesAlignment() const { @@ -96,11 +96,15 @@ inline size_t DexCacheArraysLayout::MethodOffset(uint32_t method_idx) const { } inline size_t DexCacheArraysLayout::MethodsSize(size_t num_elements) const { - return ArraySize(pointer_size_, num_elements); + size_t cache_size = mirror::DexCache::kDexCacheMethodCacheSize; + if (num_elements < cache_size) { + cache_size = num_elements; + } + return PairArraySize(pointer_size_, cache_size); } inline size_t DexCacheArraysLayout::MethodsAlignment() const { - return static_cast<size_t>(pointer_size_); + return 2u * static_cast<size_t>(pointer_size_); } inline size_t DexCacheArraysLayout::StringOffset(uint32_t string_idx) const { @@ -113,7 +117,7 @@ inline size_t DexCacheArraysLayout::StringsSize(size_t num_elements) const { if (num_elements < cache_size) { cache_size = num_elements; } - return ArraySize(PointerSize::k64, cache_size); + return PairArraySize(GcRootAsPointerSize<mirror::String>(), cache_size); } inline size_t DexCacheArraysLayout::StringsAlignment() const { @@ -132,7 +136,7 @@ inline size_t DexCacheArraysLayout::FieldsSize(size_t num_elements) const { if (num_elements < cache_size) { cache_size = num_elements; } - return 2u * static_cast<size_t>(pointer_size_) * cache_size; + return PairArraySize(pointer_size_, cache_size); } inline size_t DexCacheArraysLayout::FieldsAlignment() const { @@ -170,6 +174,10 @@ inline size_t DexCacheArraysLayout::ArraySize(PointerSize element_size, uint32_t return static_cast<size_t>(element_size) * num_elements; } +inline size_t DexCacheArraysLayout::PairArraySize(PointerSize element_size, uint32_t num_elements) { + return 2u * static_cast<size_t>(element_size) * num_elements; +} + } // namespace art #endif // ART_RUNTIME_UTILS_DEX_CACHE_ARRAYS_LAYOUT_INL_H_ diff --git a/runtime/utils/dex_cache_arrays_layout.h b/runtime/utils/dex_cache_arrays_layout.h index 377a3749a6..fc0415957d 100644 --- a/runtime/utils/dex_cache_arrays_layout.h +++ b/runtime/utils/dex_cache_arrays_layout.h @@ -130,6 +130,7 @@ class DexCacheArraysLayout { static size_t ElementOffset(PointerSize element_size, uint32_t idx); static size_t ArraySize(PointerSize element_size, uint32_t num_elements); + static size_t PairArraySize(PointerSize element_size, uint32_t num_elements); }; } // namespace art diff --git a/runtime/utils_test.cc b/runtime/utils_test.cc index 634bd47f05..ee8eb363f1 100644 --- a/runtime/utils_test.cc +++ b/runtime/utils_test.cc @@ -22,13 +22,13 @@ #include "class_linker-inl.h" #include "common_runtime_test.h" #include "exec_utils.h" -#include "mirror/array.h" +#include "handle_scope-inl.h" #include "mirror/array-inl.h" +#include "mirror/array.h" #include "mirror/object-inl.h" #include "mirror/object_array-inl.h" #include "mirror/string.h" #include "scoped_thread_state_change-inl.h" -#include "handle_scope-inl.h" #include "base/memory_tool.h" @@ -192,18 +192,21 @@ TEST_F(UtilsTest, JniShortName_JniLongName) { ASSERT_TRUE(c != nullptr); ArtMethod* m; - m = c->FindVirtualMethod("charAt", "(I)C", kRuntimePointerSize); + m = c->FindClassMethod("charAt", "(I)C", kRuntimePointerSize); ASSERT_TRUE(m != nullptr); + ASSERT_FALSE(m->IsDirect()); EXPECT_EQ("Java_java_lang_String_charAt", m->JniShortName()); EXPECT_EQ("Java_java_lang_String_charAt__I", m->JniLongName()); - m = c->FindVirtualMethod("indexOf", "(Ljava/lang/String;I)I", kRuntimePointerSize); + m = c->FindClassMethod("indexOf", "(Ljava/lang/String;I)I", kRuntimePointerSize); ASSERT_TRUE(m != nullptr); + ASSERT_FALSE(m->IsDirect()); EXPECT_EQ("Java_java_lang_String_indexOf", m->JniShortName()); EXPECT_EQ("Java_java_lang_String_indexOf__Ljava_lang_String_2I", m->JniLongName()); - m = c->FindDirectMethod("copyValueOf", "([CII)Ljava/lang/String;", kRuntimePointerSize); + m = c->FindClassMethod("copyValueOf", "([CII)Ljava/lang/String;", kRuntimePointerSize); ASSERT_TRUE(m != nullptr); + ASSERT_TRUE(m->IsStatic()); EXPECT_EQ("Java_java_lang_String_copyValueOf", m->JniShortName()); EXPECT_EQ("Java_java_lang_String_copyValueOf___3CII", m->JniLongName()); } diff --git a/runtime/vdex_file.h b/runtime/vdex_file.h index ea480f47cf..0351fd3afb 100644 --- a/runtime/vdex_file.h +++ b/runtime/vdex_file.h @@ -72,8 +72,8 @@ class VdexFile { private: static constexpr uint8_t kVdexMagic[] = { 'v', 'd', 'e', 'x' }; - // Last update: Change quickening info format. - static constexpr uint8_t kVdexVersion[] = { '0', '0', '8', '\0' }; + // Last update: Change method lookup. + static constexpr uint8_t kVdexVersion[] = { '0', '0', '9', '\0' }; uint8_t magic_[4]; uint8_t version_[4]; diff --git a/runtime/verifier/method_verifier-inl.h b/runtime/verifier/method_verifier-inl.h index 363bd8f54f..6c832e3492 100644 --- a/runtime/verifier/method_verifier-inl.h +++ b/runtime/verifier/method_verifier-inl.h @@ -17,11 +17,12 @@ #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 "base/logging.h" +#include "handle_scope-inl.h" #include "mirror/class_loader.h" #include "mirror/dex_cache.h" -#include "handle_scope-inl.h" namespace art { namespace verifier { diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc index efb02f6205..6149f0d94e 100644 --- a/runtime/verifier/method_verifier.cc +++ b/runtime/verifier/method_verifier.cc @@ -39,9 +39,8 @@ #include "indenter.h" #include "intern_table.h" #include "leb128.h" -#include "method_resolution_kind.h" -#include "mirror/class.h" #include "mirror/class-inl.h" +#include "mirror/class.h" #include "mirror/dex_cache-inl.h" #include "mirror/method_handle_impl.h" #include "mirror/object-inl.h" @@ -52,8 +51,8 @@ #include "scoped_thread_state_change-inl.h" #include "stack.h" #include "utils.h" -#include "verifier_deps.h" #include "verifier_compiler_binding.h" +#include "verifier_deps.h" namespace art { namespace verifier { @@ -230,7 +229,7 @@ MethodVerifier::FailureData MethodVerifier::VerifyMethods(Thread* self, } previous_method_idx = method_idx; InvokeType type = it->GetMethodInvokeType(class_def); - ArtMethod* method = linker->ResolveMethod<ClassLinker::kNoICCECheckForCache>( + ArtMethod* method = linker->ResolveMethod<ClassLinker::ResolveMode::kNoChecks>( *dex_file, method_idx, dex_cache, class_loader, nullptr, type); if (method == nullptr) { DCHECK(self->IsExceptionPending()); @@ -3821,21 +3820,6 @@ const RegType& MethodVerifier::GetCaughtExceptionType() { return *common_super; } -inline static MethodResolutionKind GetMethodResolutionKind( - MethodType method_type, bool is_interface) { - if (method_type == METHOD_DIRECT || method_type == METHOD_STATIC) { - return kDirectMethodResolution; - } else if (method_type == METHOD_INTERFACE) { - return kInterfaceMethodResolution; - } else if (method_type == METHOD_SUPER && is_interface) { - return kInterfaceMethodResolution; - } else { - DCHECK(method_type == METHOD_VIRTUAL || method_type == METHOD_SUPER - || method_type == METHOD_POLYMORPHIC); - return kVirtualMethodResolution; - } -} - ArtMethod* MethodVerifier::ResolveMethodAndCheckAccess( uint32_t dex_method_idx, MethodType method_type) { const DexFile::MethodId& method_id = dex_file_->GetMethodId(dex_method_idx); @@ -3849,47 +3833,41 @@ ArtMethod* MethodVerifier::ResolveMethodAndCheckAccess( if (klass_type.IsUnresolvedTypes()) { return nullptr; // Can't resolve Class so no more to do here } - mirror::Class* klass = klass_type.GetClass(); + ObjPtr<mirror::Class> klass = klass_type.GetClass(); const RegType& referrer = GetDeclaringClass(); auto* cl = Runtime::Current()->GetClassLinker(); auto pointer_size = cl->GetImagePointerSize(); - MethodResolutionKind res_kind = GetMethodResolutionKind(method_type, klass->IsInterface()); ArtMethod* res_method = dex_cache_->GetResolvedMethod(dex_method_idx, pointer_size); - bool stash_method = false; if (res_method == nullptr) { - const char* name = dex_file_->GetMethodName(method_id); - const Signature signature = dex_file_->GetMethodSignature(method_id); - - if (res_kind == kDirectMethodResolution) { - res_method = klass->FindDirectMethod(name, signature, pointer_size); - } else if (res_kind == kVirtualMethodResolution) { - res_method = klass->FindVirtualMethod(name, signature, pointer_size); + // Try to find the method with the appropriate lookup for the klass type (interface or not). + // If this lookup does not match `method_type`, errors shall be reported below. + if (klass->IsInterface()) { + res_method = klass->FindInterfaceMethod(dex_cache_.Get(), dex_method_idx, pointer_size); } else { - DCHECK_EQ(res_kind, kInterfaceMethodResolution); - res_method = klass->FindInterfaceMethod(name, signature, pointer_size); + res_method = klass->FindClassMethod(dex_cache_.Get(), dex_method_idx, pointer_size); } - if (res_method != nullptr) { - stash_method = true; - } 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 - // a class has changed, and will be flagged as an error in later checks. - // Note that in this case, we do not put the resolved method in the Dex cache - // because it was not discovered using the expected type of method resolution. - if (res_kind != kDirectMethodResolution) { - // Record result of the initial resolution attempt. - VerifierDeps::MaybeRecordMethodResolution(*dex_file_, dex_method_idx, res_kind, nullptr); - // Change resolution type to 'direct' and try to resolve again. - res_kind = kDirectMethodResolution; - res_method = klass->FindDirectMethod(name, signature, pointer_size); - } + dex_cache_->SetResolvedMethod(dex_method_idx, res_method, pointer_size); } } - // Record result of method resolution attempt. - VerifierDeps::MaybeRecordMethodResolution(*dex_file_, dex_method_idx, res_kind, res_method); + // Record result of method resolution attempt. The klass resolution has recorded whether + // the class is an interface or not and therefore the type of the lookup performed above. + // TODO: Maybe we should not record dependency if the invoke type does not match the lookup type. + VerifierDeps::MaybeRecordMethodResolution(*dex_file_, dex_method_idx, res_method); + + if (res_method == nullptr) { + // Try to find the method also with the other type for better error reporting below + // but do not store such bogus lookup result in the DexCache or VerifierDeps. + if (klass->IsInterface()) { + res_method = klass->FindClassMethod(dex_cache_.Get(), dex_method_idx, pointer_size); + } else { + // If there was an interface method with the same signature, + // we would have found it also in the "copied" methods. + DCHECK(klass->FindInterfaceMethod(dex_cache_.Get(), dex_method_idx, pointer_size) == nullptr); + } + } if (res_method == nullptr) { Fail(VERIFY_ERROR_NO_METHOD) << "couldn't find method " @@ -3940,11 +3918,6 @@ ArtMethod* MethodVerifier::ResolveMethodAndCheckAccess( } } - // Only stash after the above passed. Otherwise the method wasn't guaranteed to be correct. - if (stash_method) { - dex_cache_->SetResolvedMethod(dex_method_idx, res_method, pointer_size); - } - // Check if access is allowed. if (!referrer.CanAccessMember(res_method->GetDeclaringClass(), res_method->GetAccessFlags())) { Fail(VERIFY_ERROR_ACCESS_METHOD) << "illegal method access (call " diff --git a/runtime/verifier/method_verifier.h b/runtime/verifier/method_verifier.h index 46fdc5419d..ea8729cb3e 100644 --- a/runtime/verifier/method_verifier.h +++ b/runtime/verifier/method_verifier.h @@ -30,8 +30,8 @@ #include "handle.h" #include "instruction_flags.h" #include "method_reference.h" -#include "register_line.h" #include "reg_type_cache.h" +#include "register_line.h" #include "verifier_enums.h" namespace art { diff --git a/runtime/verifier/reg_type-inl.h b/runtime/verifier/reg_type-inl.h index 9245828ae2..704d2a86af 100644 --- a/runtime/verifier/reg_type-inl.h +++ b/runtime/verifier/reg_type-inl.h @@ -21,8 +21,8 @@ #include "base/casts.h" #include "base/scoped_arena_allocator.h" -#include "mirror/class.h" #include "method_verifier.h" +#include "mirror/class.h" #include "verifier_deps.h" namespace art { diff --git a/runtime/verifier/reg_type.cc b/runtime/verifier/reg_type.cc index 740b7dd7d4..8df2e0f50b 100644 --- a/runtime/verifier/reg_type.cc +++ b/runtime/verifier/reg_type.cc @@ -24,8 +24,8 @@ #include "class_linker-inl.h" #include "dex_file-inl.h" #include "method_verifier.h" -#include "mirror/class.h" #include "mirror/class-inl.h" +#include "mirror/class.h" #include "mirror/object-inl.h" #include "mirror/object_array-inl.h" #include "reg_type_cache-inl.h" @@ -711,6 +711,29 @@ const RegType& RegType::Merge(const RegType& incoming_type, DCHECK(c1 != nullptr && !c1->IsPrimitive()); DCHECK(c2 != nullptr && !c2->IsPrimitive()); mirror::Class* join_class = ClassJoin(c1, c2); + if (UNLIKELY(join_class == nullptr)) { + // Internal error joining the classes (e.g., OOME). Report an unresolved reference type. + // We cannot report an unresolved merge type, as that will attempt to merge the resolved + // components, leaving us in an infinite loop. + // We do not want to report the originating exception, as that would require a fast path + // out all the way to VerifyClass. Instead attempt to continue on without a detailed type. + Thread* self = Thread::Current(); + self->AssertPendingException(); + self->ClearException(); + + // When compiling on the host, we rather want to abort to ensure determinism for preopting. + // (In that case, it is likely a misconfiguration of dex2oat.) + if (!kIsTargetBuild && Runtime::Current()->IsAotCompiler()) { + LOG(FATAL) << "Could not create class join of " + << c1->PrettyClass() + << " & " + << c2->PrettyClass(); + UNREACHABLE(); + } + + return reg_types->MakeUnresolvedReference(); + } + // Record the dependency that both `c1` and `c2` are assignable to `join_class`. // The `verifier` is null during unit tests. if (verifier != nullptr) { @@ -753,10 +776,18 @@ mirror::Class* RegType::ClassJoin(mirror::Class* s, mirror::Class* t) { DCHECK(result->IsObjectClass()); return result; } + Thread* self = Thread::Current(); ObjPtr<mirror::Class> common_elem = ClassJoin(s_ct, t_ct); + if (UNLIKELY(common_elem == nullptr)) { + self->AssertPendingException(); + return nullptr; + } ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); - mirror::Class* array_class = class_linker->FindArrayClass(Thread::Current(), &common_elem); - DCHECK(array_class != nullptr); + mirror::Class* array_class = class_linker->FindArrayClass(self, &common_elem); + if (UNLIKELY(array_class == nullptr)) { + self->AssertPendingException(); + return nullptr; + } return array_class; } else { size_t s_depth = s->Depth(); diff --git a/runtime/verifier/reg_type.h b/runtime/verifier/reg_type.h index 6c01a7982a..c5d8ff5131 100644 --- a/runtime/verifier/reg_type.h +++ b/runtime/verifier/reg_type.h @@ -355,6 +355,10 @@ class RegType { * the perversion of Object being assignable to an interface type (note, however, that we don't * allow assignment of Object or Interface to any concrete class and are therefore type safe). * + * Note: This may return null in case of internal errors, e.g., OOME when a new class would have + * to be created but there is no heap space. The exception will stay pending, and it is + * the job of the caller to handle it. + * * [1] Java bytecode verification: algorithms and formalizations, Xavier Leroy */ static mirror::Class* ClassJoin(mirror::Class* s, mirror::Class* t) diff --git a/runtime/verifier/reg_type_cache.cc b/runtime/verifier/reg_type_cache.cc index 93286ea84e..0c0086858d 100644 --- a/runtime/verifier/reg_type_cache.cc +++ b/runtime/verifier/reg_type_cache.cc @@ -222,6 +222,11 @@ const RegType& RegTypeCache::From(mirror::ClassLoader* loader, } } +const RegType& RegTypeCache::MakeUnresolvedReference() { + // The descriptor is intentionally invalid so nothing else will match this type. + return AddEntry(new (&arena_) UnresolvedReferenceType(AddString("a"), entries_.size())); +} + const RegType* RegTypeCache::FindClass(mirror::Class* klass, bool precise) const { DCHECK(klass != nullptr); if (klass->IsPrimitive()) { diff --git a/runtime/verifier/reg_type_cache.h b/runtime/verifier/reg_type_cache.h index 37f8a1fc33..7077c55a8d 100644 --- a/runtime/verifier/reg_type_cache.h +++ b/runtime/verifier/reg_type_cache.h @@ -48,6 +48,7 @@ class ImpreciseConstType; class IntegerType; class LongHiType; class LongLoType; +class MethodVerifier; class PreciseConstType; class PreciseReferenceType; class RegType; @@ -97,6 +98,10 @@ class RegTypeCache { REQUIRES_SHARED(Locks::mutator_lock_); const RegType& FromUnresolvedSuperClass(const RegType& child) REQUIRES_SHARED(Locks::mutator_lock_); + + // Note: this should not be used outside of RegType::ClassJoin! + const RegType& MakeUnresolvedReference() REQUIRES_SHARED(Locks::mutator_lock_); + const ConstantType& Zero() REQUIRES_SHARED(Locks::mutator_lock_) { return FromCat1Const(0, true); } diff --git a/runtime/verifier/reg_type_test.cc b/runtime/verifier/reg_type_test.cc index b0ea6c857c..1bc48ed71b 100644 --- a/runtime/verifier/reg_type_test.cc +++ b/runtime/verifier/reg_type_test.cc @@ -22,8 +22,9 @@ #include "base/casts.h" #include "base/scoped_arena_allocator.h" #include "common_runtime_test.h" -#include "reg_type_cache-inl.h" +#include "compiler_callbacks.h" #include "reg_type-inl.h" +#include "reg_type_cache-inl.h" #include "scoped_thread_state_change-inl.h" #include "thread-current-inl.h" @@ -677,5 +678,59 @@ TEST_F(RegTypeTest, ConstPrecision) { EXPECT_FALSE(imprecise_const.Equals(precise_const)); } +class RegTypeOOMTest : public RegTypeTest { + protected: + void SetUpRuntimeOptions(RuntimeOptions *options) OVERRIDE { + SetUpRuntimeOptionsForFillHeap(options); + + // We must not appear to be a compiler, or we'll abort on the host. + callbacks_.reset(); + } +}; + +TEST_F(RegTypeOOMTest, ClassJoinOOM) { + // TODO: Figure out why FillHeap isn't good enough under CMS. + TEST_DISABLED_WITHOUT_BAKER_READ_BARRIERS(); + + // Tests that we don't abort with OOMs. + + ArenaStack stack(Runtime::Current()->GetArenaPool()); + ScopedArenaAllocator allocator(&stack); + ScopedObjectAccess soa(Thread::Current()); + + // We cannot allow moving GC. Otherwise we'd have to ensure the reg types are updated (reference + // reg types store a class pointer in a GCRoot, which is normally updated through active verifiers + // being registered with their thread), which is unnecessarily complex. + Runtime::Current()->GetHeap()->IncrementDisableMovingGC(soa.Self()); + + // We merge nested array of primitive wrappers. These have a join type of an array of Number of + // the same depth. We start with depth five, as we want at least two newly created classes to + // test recursion (it's just more likely that nobody uses such deep arrays in runtime bringup). + constexpr const char* kIntArrayFive = "[[[[[Ljava/lang/Integer;"; + constexpr const char* kFloatArrayFive = "[[[[[Ljava/lang/Float;"; + constexpr const char* kNumberArrayFour = "[[[[Ljava/lang/Number;"; + constexpr const char* kNumberArrayFive = "[[[[[Ljava/lang/Number;"; + + RegTypeCache cache(true, allocator); + const RegType& int_array_array = cache.From(nullptr, kIntArrayFive, false); + ASSERT_TRUE(int_array_array.HasClass()); + const RegType& float_array_array = cache.From(nullptr, kFloatArrayFive, false); + ASSERT_TRUE(float_array_array.HasClass()); + + // Check assumptions: the joined classes don't exist, yet. + ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); + ASSERT_TRUE(class_linker->LookupClass(soa.Self(), kNumberArrayFour, nullptr) == nullptr); + ASSERT_TRUE(class_linker->LookupClass(soa.Self(), kNumberArrayFive, nullptr) == nullptr); + + // Fill the heap. + VariableSizedHandleScope hs(soa.Self()); + FillHeap(soa.Self(), class_linker, &hs); + + const RegType& join_type = int_array_array.Merge(float_array_array, &cache, nullptr); + ASSERT_TRUE(join_type.IsUnresolvedReference()); + + Runtime::Current()->GetHeap()->DecrementDisableMovingGC(soa.Self()); +} + } // namespace verifier } // namespace art diff --git a/runtime/verifier/register_line.cc b/runtime/verifier/register_line.cc index 383d890702..34c406e5b0 100644 --- a/runtime/verifier/register_line.cc +++ b/runtime/verifier/register_line.cc @@ -20,8 +20,8 @@ #include "dex_instruction-inl.h" #include "method_verifier-inl.h" -#include "register_line-inl.h" #include "reg_type-inl.h" +#include "register_line-inl.h" namespace art { namespace verifier { diff --git a/runtime/verifier/verifier_deps.cc b/runtime/verifier/verifier_deps.cc index 122e05f079..112eec847d 100644 --- a/runtime/verifier/verifier_deps.cc +++ b/runtime/verifier/verifier_deps.cc @@ -54,9 +54,7 @@ void VerifierDeps::MergeWith(const VerifierDeps& other, MergeSets(my_deps->unassignable_types_, other_deps.unassignable_types_); MergeSets(my_deps->classes_, other_deps.classes_); MergeSets(my_deps->fields_, other_deps.fields_); - MergeSets(my_deps->direct_methods_, other_deps.direct_methods_); - MergeSets(my_deps->virtual_methods_, other_deps.virtual_methods_); - MergeSets(my_deps->interface_methods_, other_deps.interface_methods_); + MergeSets(my_deps->methods_, other_deps.methods_); for (dex::TypeIndex entry : other_deps.unverified_classes_) { my_deps->unverified_classes_.push_back(entry); } @@ -317,7 +315,6 @@ void VerifierDeps::AddFieldResolution(const DexFile& dex_file, void VerifierDeps::AddMethodResolution(const DexFile& dex_file, uint32_t method_idx, - MethodResolutionKind resolution_kind, ArtMethod* method) { DexFileDeps* dex_deps = GetDexFileDeps(dex_file); if (dex_deps == nullptr) { @@ -334,14 +331,7 @@ void VerifierDeps::AddMethodResolution(const DexFile& dex_file, MethodResolution method_tuple(method_idx, GetAccessFlags(method), GetMethodDeclaringClassStringId(dex_file, method_idx, method)); - if (resolution_kind == kDirectMethodResolution) { - dex_deps->direct_methods_.emplace(method_tuple); - } else if (resolution_kind == kVirtualMethodResolution) { - dex_deps->virtual_methods_.emplace(method_tuple); - } else { - DCHECK_EQ(resolution_kind, kInterfaceMethodResolution); - dex_deps->interface_methods_.emplace(method_tuple); - } + dex_deps->methods_.insert(method_tuple); } mirror::Class* VerifierDeps::FindOneClassPathBoundaryForInterface(mirror::Class* destination, @@ -537,11 +527,10 @@ void VerifierDeps::MaybeRecordFieldResolution(const DexFile& dex_file, void VerifierDeps::MaybeRecordMethodResolution(const DexFile& dex_file, uint32_t method_idx, - MethodResolutionKind resolution_kind, ArtMethod* method) { VerifierDeps* thread_deps = GetThreadLocalVerifierDeps(); if (thread_deps != nullptr) { - thread_deps->AddMethodResolution(dex_file, method_idx, resolution_kind, method); + thread_deps->AddMethodResolution(dex_file, method_idx, method); } } @@ -698,9 +687,7 @@ void VerifierDeps::Encode(const std::vector<const DexFile*>& dex_files, EncodeSet(buffer, deps.unassignable_types_); EncodeSet(buffer, deps.classes_); EncodeSet(buffer, deps.fields_); - EncodeSet(buffer, deps.direct_methods_); - EncodeSet(buffer, deps.virtual_methods_); - EncodeSet(buffer, deps.interface_methods_); + EncodeSet(buffer, deps.methods_); EncodeUint16Vector(buffer, deps.unverified_classes_); } } @@ -723,9 +710,7 @@ VerifierDeps::VerifierDeps(const std::vector<const DexFile*>& dex_files, DecodeSet(&data_start, data_end, &deps->unassignable_types_); DecodeSet(&data_start, data_end, &deps->classes_); DecodeSet(&data_start, data_end, &deps->fields_); - DecodeSet(&data_start, data_end, &deps->direct_methods_); - DecodeSet(&data_start, data_end, &deps->virtual_methods_); - DecodeSet(&data_start, data_end, &deps->interface_methods_); + DecodeSet(&data_start, data_end, &deps->methods_); DecodeUint16Vector(&data_start, data_end, &deps->unverified_classes_); } CHECK_LE(data_start, data_end); @@ -763,9 +748,7 @@ bool VerifierDeps::DexFileDeps::Equals(const VerifierDeps::DexFileDeps& rhs) con (unassignable_types_ == rhs.unassignable_types_) && (classes_ == rhs.classes_) && (fields_ == rhs.fields_) && - (direct_methods_ == rhs.direct_methods_) && - (virtual_methods_ == rhs.virtual_methods_) && - (interface_methods_ == rhs.interface_methods_) && + (methods_ == rhs.methods_) && (unverified_classes_ == rhs.unverified_classes_); } @@ -825,27 +808,21 @@ void VerifierDeps::Dump(VariableIndentationOutputStream* vios) const { } } - for (const auto& entry : - { std::make_pair(kDirectMethodResolution, dep.second->direct_methods_), - std::make_pair(kVirtualMethodResolution, dep.second->virtual_methods_), - std::make_pair(kInterfaceMethodResolution, dep.second->interface_methods_) }) { - for (const MethodResolution& method : entry.second) { - const DexFile::MethodId& method_id = dex_file.GetMethodId(method.GetDexMethodIndex()); + for (const MethodResolution& method : dep.second->methods_) { + const DexFile::MethodId& method_id = dex_file.GetMethodId(method.GetDexMethodIndex()); + vios->Stream() + << dex_file.GetMethodDeclaringClassDescriptor(method_id) << "->" + << dex_file.GetMethodName(method_id) + << dex_file.GetMethodSignature(method_id).ToString() + << " is expected to be "; + if (!method.IsResolved()) { + vios->Stream() << "unresolved\n"; + } else { vios->Stream() - << dex_file.GetMethodDeclaringClassDescriptor(method_id) << "->" - << dex_file.GetMethodName(method_id) - << dex_file.GetMethodSignature(method_id).ToString() - << " is expected to be "; - if (!method.IsResolved()) { - vios->Stream() << "unresolved\n"; - } else { - vios->Stream() - << "in class " - << GetStringFromId(dex_file, method.GetDeclaringClassIndex()) - << ", have the access flags " << std::hex << method.GetAccessFlags() << std::dec - << ", and be of kind " << entry.first - << "\n"; - } + << "in class " + << GetStringFromId(dex_file, method.GetDeclaringClassIndex()) + << ", have the access flags " << std::hex << method.GetAccessFlags() << std::dec + << "\n"; } } @@ -1030,7 +1007,6 @@ static std::string GetMethodDescription(const DexFile& dex_file, uint32_t index) bool VerifierDeps::VerifyMethods(Handle<mirror::ClassLoader> class_loader, const DexFile& dex_file, const std::set<MethodResolution>& methods, - MethodResolutionKind kind, Thread* self) const { ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); PointerSize pointer_size = class_linker->GetImagePointerSize(); @@ -1054,27 +1030,20 @@ bool VerifierDeps::VerifyMethods(Handle<mirror::ClassLoader> class_loader, } DCHECK(cls->IsResolved()); ArtMethod* method = nullptr; - if (kind == kDirectMethodResolution) { - method = cls->FindDirectMethod(name, signature, pointer_size); - } else if (kind == kVirtualMethodResolution) { - method = cls->FindVirtualMethod(name, signature, pointer_size); - } else { - DCHECK_EQ(kind, kInterfaceMethodResolution); + if (cls->IsInterface()) { method = cls->FindInterfaceMethod(name, signature, pointer_size); + } else { + method = cls->FindClassMethod(name, signature, pointer_size); } if (entry.IsResolved()) { std::string temp; if (method == nullptr) { - LOG(INFO) << "VerifierDeps: Could not resolve " - << kind - << " method " + LOG(INFO) << "VerifierDeps: Could not resolve method " << GetMethodDescription(dex_file, entry.GetDexMethodIndex()); return false; } else if (expected_decl_klass != method->GetDeclaringClass()->GetDescriptor(&temp)) { - LOG(INFO) << "VerifierDeps: Unexpected declaring class for " - << kind - << " method resolution " + LOG(INFO) << "VerifierDeps: Unexpected declaring class for method resolution " << GetMethodDescription(dex_file, entry.GetDexMethodIndex()) << " (expected=" << expected_decl_klass @@ -1083,9 +1052,7 @@ bool VerifierDeps::VerifyMethods(Handle<mirror::ClassLoader> class_loader, << ")"; return false; } else if (entry.GetAccessFlags() != GetAccessFlags(method)) { - LOG(INFO) << "VerifierDeps: Unexpected access flags for resolved " - << kind - << " method resolution " + LOG(INFO) << "VerifierDeps: Unexpected access flags for resolved method resolution " << GetMethodDescription(dex_file, entry.GetDexMethodIndex()) << std::hex << " (expected=" @@ -1096,9 +1063,7 @@ bool VerifierDeps::VerifyMethods(Handle<mirror::ClassLoader> class_loader, return false; } } else if (method != nullptr) { - LOG(INFO) << "VerifierDeps: Unexpected successful resolution of " - << kind - << " method " + LOG(INFO) << "VerifierDeps: Unexpected successful resolution of method " << GetMethodDescription(dex_file, entry.GetDexMethodIndex()); return false; } @@ -1118,12 +1083,7 @@ bool VerifierDeps::VerifyDexFile(Handle<mirror::ClassLoader> class_loader, result = result && VerifyClasses(class_loader, dex_file, deps.classes_, self); result = result && VerifyFields(class_loader, dex_file, deps.fields_, self); - result = result && VerifyMethods( - class_loader, dex_file, deps.direct_methods_, kDirectMethodResolution, self); - result = result && VerifyMethods( - class_loader, dex_file, deps.virtual_methods_, kVirtualMethodResolution, self); - result = result && VerifyMethods( - class_loader, dex_file, deps.interface_methods_, kInterfaceMethodResolution, self); + result = result && VerifyMethods(class_loader, dex_file, deps.methods_, self); return result; } diff --git a/runtime/verifier/verifier_deps.h b/runtime/verifier/verifier_deps.h index 43eb948c64..b883a9e642 100644 --- a/runtime/verifier/verifier_deps.h +++ b/runtime/verifier/verifier_deps.h @@ -25,7 +25,6 @@ #include "base/mutex.h" #include "dex_file_types.h" #include "handle.h" -#include "method_resolution_kind.h" #include "obj_ptr.h" #include "thread.h" #include "verifier_enums.h" // For MethodVerifier::FailureKind. @@ -88,12 +87,10 @@ class VerifierDeps { REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Locks::verifier_deps_lock_); - // Record the outcome `method` of resolving method `method_idx` from `dex_file` - // using `res_kind` kind of method resolution algorithm. If `method` is null, - // the method is assumed unresolved. + // Record the outcome `method` of resolving method `method_idx` from `dex_file`. + // If `method` is null, the method is assumed unresolved. static void MaybeRecordMethodResolution(const DexFile& dex_file, uint32_t method_idx, - MethodResolutionKind res_kind, ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Locks::verifier_deps_lock_); @@ -193,9 +190,7 @@ class VerifierDeps { // Sets of recorded class/field/method resolutions. std::set<ClassResolution> classes_; std::set<FieldResolution> fields_; - std::set<MethodResolution> direct_methods_; - std::set<MethodResolution> virtual_methods_; - std::set<MethodResolution> interface_methods_; + std::set<MethodResolution> methods_; // List of classes that were not fully verified in that dex file. std::vector<dex::TypeIndex> unverified_classes_; @@ -267,7 +262,6 @@ class VerifierDeps { void AddMethodResolution(const DexFile& dex_file, uint32_t method_idx, - MethodResolutionKind res_kind, ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Locks::verifier_deps_lock_); @@ -321,7 +315,6 @@ class VerifierDeps { bool VerifyMethods(Handle<mirror::ClassLoader> class_loader, const DexFile& dex_file, const std::set<MethodResolution>& methods, - MethodResolutionKind kind, Thread* self) const REQUIRES_SHARED(Locks::mutator_lock_); diff --git a/runtime/well_known_classes.cc b/runtime/well_known_classes.cc index f72fdb4b2a..1c14cf2150 100644 --- a/runtime/well_known_classes.cc +++ b/runtime/well_known_classes.cc @@ -27,8 +27,8 @@ #include "jni_internal.h" #include "mirror/class.h" #include "mirror/throwable.h" +#include "nativehelper/ScopedLocalRef.h" #include "obj_ptr-inl.h" -#include "ScopedLocalRef.h" #include "scoped_thread_state_change-inl.h" #include "thread-current-inl.h" diff --git a/test/004-JniTest/jni_test.cc b/test/004-JniTest/jni_test.cc index f2edd0f688..a34d633590 100644 --- a/test/004-JniTest/jni_test.cc +++ b/test/004-JniTest/jni_test.cc @@ -14,9 +14,10 @@ * limitations under the License. */ -#include <iostream> #include <pthread.h> -#include <stdio.h> + +#include <cstdio> +#include <iostream> #include <vector> #include "art_method-inl.h" diff --git a/test/004-UnsafeTest/unsafe_test.cc b/test/004-UnsafeTest/unsafe_test.cc index 4f6ae5a68d..18d9ea8913 100644 --- a/test/004-UnsafeTest/unsafe_test.cc +++ b/test/004-UnsafeTest/unsafe_test.cc @@ -17,8 +17,8 @@ #include "art_method-inl.h" #include "jni.h" #include "mirror/array.h" -#include "mirror/class.h" #include "mirror/class-inl.h" +#include "mirror/class.h" #include "mirror/object-inl.h" #include "scoped_thread_state_change-inl.h" diff --git a/test/066-mismatched-super/expected.txt b/test/066-mismatched-super/expected.txt index 09c05967d9..f5b15cac8e 100644 --- a/test/066-mismatched-super/expected.txt +++ b/test/066-mismatched-super/expected.txt @@ -1 +1,2 @@ Got expected ICCE +Got expected VerifyError diff --git a/test/066-mismatched-super/info.txt b/test/066-mismatched-super/info.txt index 7865ffc4a8..2b70e0640a 100644 --- a/test/066-mismatched-super/info.txt +++ b/test/066-mismatched-super/info.txt @@ -1,2 +1,5 @@ -This tests what happens when class A extends abstract class B, but somebody -turns B into an interface without rebuilding A. +This tests two cases: +1. What happens when class A extends abstract class B, but somebody + turns B into an interface without rebuilding A. +2. What happens when class A extends a class B, but somebody + turns B into a final class without rebuilding A. diff --git a/runtime/verifier/method_resolution_kind.h b/test/066-mismatched-super/src/ExtendsFinal.java index f72eb7af3a..2f53b3b429 100644 --- a/runtime/verifier/method_resolution_kind.h +++ b/test/066-mismatched-super/src/ExtendsFinal.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2016 The Android Open Source Project + * Copyright (C) 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,20 +14,5 @@ * limitations under the License. */ -#ifndef ART_RUNTIME_VERIFIER_METHOD_RESOLUTION_KIND_H_ -#define ART_RUNTIME_VERIFIER_METHOD_RESOLUTION_KIND_H_ - -namespace art { -namespace verifier { - -// Values corresponding to the method resolution algorithms defined in mirror::Class. -enum MethodResolutionKind { - kDirectMethodResolution, - kVirtualMethodResolution, - kInterfaceMethodResolution, -}; - -} // namespace verifier -} // namespace art - -#endif // ART_RUNTIME_VERIFIER_METHOD_RESOLUTION_KIND_H_ +public class ExtendsFinal extends Final { +} diff --git a/test/066-mismatched-super/src/Final.java b/test/066-mismatched-super/src/Final.java new file mode 100644 index 0000000000..a44d096c68 --- /dev/null +++ b/test/066-mismatched-super/src/Final.java @@ -0,0 +1,18 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +public /* final */ class Final { +} diff --git a/test/066-mismatched-super/src/Main.java b/test/066-mismatched-super/src/Main.java index 55d0babbef..6ae11984c5 100644 --- a/test/066-mismatched-super/src/Main.java +++ b/test/066-mismatched-super/src/Main.java @@ -20,10 +20,16 @@ public class Main { public static void main(String[] args) { try { - Indirect.main(); + Base base = new Base(); System.out.println("Succeeded unexpectedly"); } catch (IncompatibleClassChangeError icce) { System.out.println("Got expected ICCE"); } + try { + ExtendsFinal ef = new ExtendsFinal(); + System.out.println("Succeeded unexpectedly"); + } catch (VerifyError ve) { + System.out.println("Got expected VerifyError"); + } } } diff --git a/test/066-mismatched-super/src2/Final.java b/test/066-mismatched-super/src2/Final.java new file mode 100644 index 0000000000..766da9bc0b --- /dev/null +++ b/test/066-mismatched-super/src2/Final.java @@ -0,0 +1,18 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +public final class Final { +} diff --git a/test/115-native-bridge/nativebridge.cc b/test/115-native-bridge/nativebridge.cc index 307fd9b766..a049b97adc 100644 --- a/test/115-native-bridge/nativebridge.cc +++ b/test/115-native-bridge/nativebridge.cc @@ -16,20 +16,21 @@ // A simple implementation of the native-bridge interface. -#include <algorithm> #include <dlfcn.h> -#include <jni.h> -#include <stdlib.h> +#include <setjmp.h> #include <signal.h> +#include <sys/stat.h> +#include <unistd.h> + +#include <algorithm> +#include <cstdio> +#include <cstdlib> #include <vector> -#include "stdio.h" -#include "unistd.h" -#include "sys/stat.h" -#include "setjmp.h" +#include <jni.h> +#include <nativebridge/native_bridge.h> #include "base/macros.h" -#include "nativebridge/native_bridge.h" struct NativeBridgeMethod { const char* name; diff --git a/test/137-cfi/cfi.cc b/test/137-cfi/cfi.cc index 1ed1f5a94c..ad705c56d2 100644 --- a/test/137-cfi/cfi.cc +++ b/test/137-cfi/cfi.cc @@ -18,15 +18,15 @@ #include <errno.h> #include <signal.h> #include <string.h> -#include <unistd.h> #include <sys/ptrace.h> #include <sys/wait.h> +#include <unistd.h> #endif #include "jni.h" -#include "android-base/stringprintf.h" #include <backtrace/Backtrace.h> +#include "android-base/stringprintf.h" #include "base/logging.h" #include "base/macros.h" diff --git a/test/162-method-resolution/expected.txt b/test/162-method-resolution/expected.txt new file mode 100644 index 0000000000..1bf39c90de --- /dev/null +++ b/test/162-method-resolution/expected.txt @@ -0,0 +1,43 @@ +Calling Test1Derived.test(): +Test1Derived.foo() +Calling Test1User.test(): +Caught java.lang.reflect.InvocationTargetException + caused by java.lang.IllegalAccessError +Calling Test1User2.test(): +Caught java.lang.reflect.InvocationTargetException + caused by java.lang.IllegalAccessError +Calling Test2User.test(): +Caught java.lang.reflect.InvocationTargetException + caused by java.lang.IncompatibleClassChangeError +Calling Test2User2.test(): +Test2Base.foo() +Calling Test3User.test(): +Caught java.lang.reflect.InvocationTargetException + caused by java.lang.IncompatibleClassChangeError +Calling Test4User.test(): +Test4Derived@... +Calling Test5User.test(): +Test5Derived.foo() +Calling Test5User2.test(): +Caught java.lang.reflect.InvocationTargetException + caused by java.lang.IncompatibleClassChangeError +Calling Test6User.test(): +Test6Derived@... +Calling Test6User2.test(): +Caught java.lang.reflect.InvocationTargetException + caused by java.lang.IncompatibleClassChangeError +Calling Test7User.test(): +Test7Interface.foo() +Calling Test7User2.test(): +Caught java.lang.reflect.InvocationTargetException + caused by java.lang.IllegalAccessError +Calling Test8User.test(): +Test8Derived.foo() +Calling Test8User2.test(): +Caught java.lang.reflect.InvocationTargetException + caused by java.lang.IncompatibleClassChangeError +Calling Test9User.test(): +Test9Derived.foo() +Calling Test9User2.test(): +Caught java.lang.reflect.InvocationTargetException + caused by java.lang.IncompatibleClassChangeError diff --git a/test/162-method-resolution/info.txt b/test/162-method-resolution/info.txt new file mode 100644 index 0000000000..ff57a9ae9f --- /dev/null +++ b/test/162-method-resolution/info.txt @@ -0,0 +1,4 @@ +Tests that the method resolution is consistent with JLS and the RI. +Where the RI conflicts with JLS, we follow the JLS and suppress the divergence +when the test is executed with --jvm. +(See Main.java for per-test details.) diff --git a/test/162-method-resolution/jasmin-multidex/Test1User.j b/test/162-method-resolution/jasmin-multidex/Test1User.j new file mode 100644 index 0000000000..09ba77b689 --- /dev/null +++ b/test/162-method-resolution/jasmin-multidex/Test1User.j @@ -0,0 +1,26 @@ +; Copyright (C) 2017 The Android Open Source Project +; +; Licensed under the Apache License, Version 2.0 (the "License"); +; you may not use this file except in compliance with the License. +; You may obtain a copy of the License at +; +; http://www.apache.org/licenses/LICENSE-2.0 +; +; Unless required by applicable law or agreed to in writing, software +; distributed under the License is distributed on an "AS IS" BASIS, +; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +; See the License for the specific language governing permissions and +; limitations under the License. + +.class public Test1User +.super java/lang/Object + +.method public static test()V + .limit stack 2 + .limit locals 0 + new Test1Derived + dup + invokespecial Test1Derived.<init>()V + invokevirtual Test1Derived.foo()V + return +.end method diff --git a/test/162-method-resolution/jasmin-multidex/Test3User.j b/test/162-method-resolution/jasmin-multidex/Test3User.j new file mode 100644 index 0000000000..90f3a4e578 --- /dev/null +++ b/test/162-method-resolution/jasmin-multidex/Test3User.j @@ -0,0 +1,26 @@ +; Copyright (C) 2017 The Android Open Source Project +; +; Licensed under the Apache License, Version 2.0 (the "License"); +; you may not use this file except in compliance with the License. +; You may obtain a copy of the License at +; +; http://www.apache.org/licenses/LICENSE-2.0 +; +; Unless required by applicable law or agreed to in writing, software +; distributed under the License is distributed on an "AS IS" BASIS, +; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +; See the License for the specific language governing permissions and +; limitations under the License. + +.class public Test3User +.super java/lang/Object + +.method public static test()V + .limit stack 2 + .limit locals 0 + new Test3Derived + dup + invokespecial Test3Derived.<init>()V + invokevirtual Test3Derived.foo()V + return +.end method diff --git a/test/162-method-resolution/jasmin/Test1Derived.j b/test/162-method-resolution/jasmin/Test1Derived.j new file mode 100644 index 0000000000..d754c64c64 --- /dev/null +++ b/test/162-method-resolution/jasmin/Test1Derived.j @@ -0,0 +1,43 @@ +; Copyright (C) 2017 The Android Open Source Project +; +; Licensed under the Apache License, Version 2.0 (the "License"); +; you may not use this file except in compliance with the License. +; You may obtain a copy of the License at +; +; http://www.apache.org/licenses/LICENSE-2.0 +; +; Unless required by applicable law or agreed to in writing, software +; distributed under the License is distributed on an "AS IS" BASIS, +; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +; See the License for the specific language governing permissions and +; limitations under the License. + +.class public Test1Derived +.super Test1Base + +.method public <init>()V + .limit stack 1 + .limit locals 1 + aload_0 + invokespecial Test1Base.<init>()V + return +.end method + +.method public static test()V + .limit stack 2 + .limit locals 0 + new Test1Derived + dup + invokespecial Test1Derived.<init>()V + invokespecial Test1Derived.foo()V + return +.end method + +.method private foo()V + .limit stack 2 + .limit locals 1 + getstatic java/lang/System/out Ljava/io/PrintStream; + ldc "Test1Derived.foo()" + invokevirtual java/io/PrintStream.println(Ljava/lang/String;)V + return +.end method diff --git a/test/162-method-resolution/jasmin/Test1User2.j b/test/162-method-resolution/jasmin/Test1User2.j new file mode 100644 index 0000000000..8af9aab930 --- /dev/null +++ b/test/162-method-resolution/jasmin/Test1User2.j @@ -0,0 +1,26 @@ +; Copyright (C) 2017 The Android Open Source Project +; +; Licensed under the Apache License, Version 2.0 (the "License"); +; you may not use this file except in compliance with the License. +; You may obtain a copy of the License at +; +; http://www.apache.org/licenses/LICENSE-2.0 +; +; Unless required by applicable law or agreed to in writing, software +; distributed under the License is distributed on an "AS IS" BASIS, +; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +; See the License for the specific language governing permissions and +; limitations under the License. + +.class public Test1User2 +.super java/lang/Object + +.method public static test()V + .limit stack 2 + .limit locals 0 + new Test1Derived + dup + invokespecial Test1Derived.<init>()V + invokevirtual Test1Derived.foo()V + return +.end method diff --git a/test/162-method-resolution/jasmin/Test2Derived.j b/test/162-method-resolution/jasmin/Test2Derived.j new file mode 100644 index 0000000000..bb4525d250 --- /dev/null +++ b/test/162-method-resolution/jasmin/Test2Derived.j @@ -0,0 +1,25 @@ +; Copyright (C) 2017 The Android Open Source Project +; +; Licensed under the Apache License, Version 2.0 (the "License"); +; you may not use this file except in compliance with the License. +; You may obtain a copy of the License at +; +; http://www.apache.org/licenses/LICENSE-2.0 +; +; Unless required by applicable law or agreed to in writing, software +; distributed under the License is distributed on an "AS IS" BASIS, +; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +; See the License for the specific language governing permissions and +; limitations under the License. + +.class public Test2Derived +.super Test2Base +.implements Test2Interface + +.method public <init>()V + .limit stack 1 + .limit locals 1 + aload_0 + invokespecial Test2Base.<init>()V + return +.end method diff --git a/test/162-method-resolution/jasmin/Test2User.j b/test/162-method-resolution/jasmin/Test2User.j new file mode 100644 index 0000000000..2cce074898 --- /dev/null +++ b/test/162-method-resolution/jasmin/Test2User.j @@ -0,0 +1,26 @@ +; Copyright (C) 2017 The Android Open Source Project +; +; Licensed under the Apache License, Version 2.0 (the "License"); +; you may not use this file except in compliance with the License. +; You may obtain a copy of the License at +; +; http://www.apache.org/licenses/LICENSE-2.0 +; +; Unless required by applicable law or agreed to in writing, software +; distributed under the License is distributed on an "AS IS" BASIS, +; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +; See the License for the specific language governing permissions and +; limitations under the License. + +.class public Test2User +.super java/lang/Object + +.method public static test()V + .limit stack 2 + .limit locals 0 + new Test2Derived + dup + invokespecial Test2Derived.<init>()V + invokevirtual Test2Derived.foo()V + return +.end method diff --git a/test/162-method-resolution/jasmin/Test2User2.j b/test/162-method-resolution/jasmin/Test2User2.j new file mode 100644 index 0000000000..eb80f3213e --- /dev/null +++ b/test/162-method-resolution/jasmin/Test2User2.j @@ -0,0 +1,23 @@ +; Copyright (C) 2017 The Android Open Source Project +; +; Licensed under the Apache License, Version 2.0 (the "License"); +; you may not use this file except in compliance with the License. +; You may obtain a copy of the License at +; +; http://www.apache.org/licenses/LICENSE-2.0 +; +; Unless required by applicable law or agreed to in writing, software +; distributed under the License is distributed on an "AS IS" BASIS, +; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +; See the License for the specific language governing permissions and +; limitations under the License. + +.class public Test2User2 +.super java/lang/Object + +.method public static test()V + .limit stack 0 + .limit locals 0 + invokestatic Test2Derived.foo()V + return +.end method diff --git a/test/162-method-resolution/jasmin/Test3Derived.j b/test/162-method-resolution/jasmin/Test3Derived.j new file mode 100644 index 0000000000..2bf4bf1552 --- /dev/null +++ b/test/162-method-resolution/jasmin/Test3Derived.j @@ -0,0 +1,25 @@ +; Copyright (C) 2017 The Android Open Source Project +; +; Licensed under the Apache License, Version 2.0 (the "License"); +; you may not use this file except in compliance with the License. +; You may obtain a copy of the License at +; +; http://www.apache.org/licenses/LICENSE-2.0 +; +; Unless required by applicable law or agreed to in writing, software +; distributed under the License is distributed on an "AS IS" BASIS, +; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +; See the License for the specific language governing permissions and +; limitations under the License. + +.class public Test3Derived +.super Test3Base +.implements Test3Interface + +.method public <init>()V + .limit stack 1 + .limit locals 1 + aload_0 + invokespecial Test3Base.<init>()V + return +.end method diff --git a/test/162-method-resolution/jasmin/Test4User.j b/test/162-method-resolution/jasmin/Test4User.j new file mode 100644 index 0000000000..5b65368a87 --- /dev/null +++ b/test/162-method-resolution/jasmin/Test4User.j @@ -0,0 +1,29 @@ +; Copyright (C) 2017 The Android Open Source Project +; +; Licensed under the Apache License, Version 2.0 (the "License"); +; you may not use this file except in compliance with the License. +; You may obtain a copy of the License at +; +; http://www.apache.org/licenses/LICENSE-2.0 +; +; Unless required by applicable law or agreed to in writing, software +; distributed under the License is distributed on an "AS IS" BASIS, +; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +; See the License for the specific language governing permissions and +; limitations under the License. + +.class public Test4User +.super java/lang/Object + +.method public static test()V + .limit stack 3 + .limit locals 0 + getstatic java/lang/System/out Ljava/io/PrintStream; + new Test4Derived + dup + invokespecial Test4Derived.<init>()V + invokeinterface Test4Interface.toString()Ljava/lang/String; 1 + invokestatic Main.normalizeToString(Ljava/lang/String;)Ljava/lang/String; + invokevirtual java/io/PrintStream.println(Ljava/lang/String;)V + return +.end method diff --git a/test/162-method-resolution/jasmin/Test5User.j b/test/162-method-resolution/jasmin/Test5User.j new file mode 100644 index 0000000000..036e366dd5 --- /dev/null +++ b/test/162-method-resolution/jasmin/Test5User.j @@ -0,0 +1,40 @@ +; Copyright (C) 2017 The Android Open Source Project +; +; Licensed under the Apache License, Version 2.0 (the "License"); +; you may not use this file except in compliance with the License. +; You may obtain a copy of the License at +; +; http://www.apache.org/licenses/LICENSE-2.0 +; +; Unless required by applicable law or agreed to in writing, software +; distributed under the License is distributed on an "AS IS" BASIS, +; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +; See the License for the specific language governing permissions and +; limitations under the License. + +.class public Test5User +.super java/lang/Object + +.method public static test()V + .limit stack 2 + .limit locals 1 + new Test5Derived + dup + invokespecial Test5Derived.<init>()V + astore_0 + + ; Call an unresolved method bar() to force verification at runtime + ; to populate the dex cache entry for Test5Base.foo()V. + ; try { b.bar(); } catch (IncompatibleClassChangeError icce) { } + aload_0 + dup ; Bogus operand to be swallowed by the pop in the non-exceptional path. + catch_begin: + invokevirtual Test5Derived.bar()V + catch_end: + pop ; Pops the exception or the bogus operand from above. + .catch java/lang/IncompatibleClassChangeError from catch_begin to catch_end using catch_end + + aload_0 + invokevirtual Test5Derived.foo()V + return +.end method diff --git a/test/162-method-resolution/jasmin/Test5User2.j b/test/162-method-resolution/jasmin/Test5User2.j new file mode 100644 index 0000000000..9484a69d4d --- /dev/null +++ b/test/162-method-resolution/jasmin/Test5User2.j @@ -0,0 +1,26 @@ +; Copyright (C) 2017 The Android Open Source Project +; +; Licensed under the Apache License, Version 2.0 (the "License"); +; you may not use this file except in compliance with the License. +; You may obtain a copy of the License at +; +; http://www.apache.org/licenses/LICENSE-2.0 +; +; Unless required by applicable law or agreed to in writing, software +; distributed under the License is distributed on an "AS IS" BASIS, +; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +; See the License for the specific language governing permissions and +; limitations under the License. + +.class public Test5User2 +.super java/lang/Object + +.method public static test()V + .limit stack 2 + .limit locals 0 + new Test5Derived + dup + invokespecial Test5Derived.<init>()V + invokeinterface Test5Derived.foo()V 1 + return +.end method diff --git a/test/162-method-resolution/jasmin/Test6User.j b/test/162-method-resolution/jasmin/Test6User.j new file mode 100644 index 0000000000..55b43f1fe2 --- /dev/null +++ b/test/162-method-resolution/jasmin/Test6User.j @@ -0,0 +1,29 @@ +; Copyright (C) 2017 The Android Open Source Project +; +; Licensed under the Apache License, Version 2.0 (the "License"); +; you may not use this file except in compliance with the License. +; You may obtain a copy of the License at +; +; http://www.apache.org/licenses/LICENSE-2.0 +; +; Unless required by applicable law or agreed to in writing, software +; distributed under the License is distributed on an "AS IS" BASIS, +; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +; See the License for the specific language governing permissions and +; limitations under the License. + +.class public Test6User +.super java/lang/Object + +.method public static test()V + .limit stack 3 + .limit locals 0 + getstatic java/lang/System/out Ljava/io/PrintStream; + new Test6Derived + dup + invokespecial Test6Derived.<init>()V + invokeinterface Test6Interface.toString()Ljava/lang/String; 1 + invokestatic Main.normalizeToString(Ljava/lang/String;)Ljava/lang/String; + invokevirtual java/io/PrintStream.println(Ljava/lang/String;)V + return +.end method diff --git a/test/162-method-resolution/jasmin/Test6User2.j b/test/162-method-resolution/jasmin/Test6User2.j new file mode 100644 index 0000000000..ab9ac0eddc --- /dev/null +++ b/test/162-method-resolution/jasmin/Test6User2.j @@ -0,0 +1,29 @@ +; Copyright (C) 2017 The Android Open Source Project +; +; Licensed under the Apache License, Version 2.0 (the "License"); +; you may not use this file except in compliance with the License. +; You may obtain a copy of the License at +; +; http://www.apache.org/licenses/LICENSE-2.0 +; +; Unless required by applicable law or agreed to in writing, software +; distributed under the License is distributed on an "AS IS" BASIS, +; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +; See the License for the specific language governing permissions and +; limitations under the License. + +.class public Test6User2 +.super java/lang/Object + +.method public static test()V + .limit stack 3 + .limit locals 0 + getstatic java/lang/System/out Ljava/io/PrintStream; + new Test6Derived + dup + invokespecial Test6Derived.<init>()V + invokevirtual Test6Interface.toString()Ljava/lang/String; + invokestatic Main.normalizeToString(Ljava/lang/String;)Ljava/lang/String; + invokevirtual java/io/PrintStream.println(Ljava/lang/String;)V + return +.end method diff --git a/test/162-method-resolution/jasmin/Test8Derived.j b/test/162-method-resolution/jasmin/Test8Derived.j new file mode 100644 index 0000000000..73f8b28b57 --- /dev/null +++ b/test/162-method-resolution/jasmin/Test8Derived.j @@ -0,0 +1,33 @@ +; Copyright (C) 2017 The Android Open Source Project +; +; Licensed under the Apache License, Version 2.0 (the "License"); +; you may not use this file except in compliance with the License. +; You may obtain a copy of the License at +; +; http://www.apache.org/licenses/LICENSE-2.0 +; +; Unless required by applicable law or agreed to in writing, software +; distributed under the License is distributed on an "AS IS" BASIS, +; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +; See the License for the specific language governing permissions and +; limitations under the License. + +.class public Test8Derived +.super Test8Base + +.method public <init>()V + .limit stack 1 + .limit locals 1 + aload_0 + invokespecial Test8Base.<init>()V + return +.end method + +.method public foo()V + .limit stack 2 + .limit locals 1 + getstatic java/lang/System/out Ljava/io/PrintStream; + ldc "Test8Derived.foo()" + invokevirtual java/io/PrintStream.println(Ljava/lang/String;)V + return +.end method diff --git a/test/162-method-resolution/jasmin/Test8User.j b/test/162-method-resolution/jasmin/Test8User.j new file mode 100644 index 0000000000..af60c6ecb1 --- /dev/null +++ b/test/162-method-resolution/jasmin/Test8User.j @@ -0,0 +1,26 @@ +; Copyright (C) 2017 The Android Open Source Project +; +; Licensed under the Apache License, Version 2.0 (the "License"); +; you may not use this file except in compliance with the License. +; You may obtain a copy of the License at +; +; http://www.apache.org/licenses/LICENSE-2.0 +; +; Unless required by applicable law or agreed to in writing, software +; distributed under the License is distributed on an "AS IS" BASIS, +; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +; See the License for the specific language governing permissions and +; limitations under the License. + +.class public Test8User +.super java/lang/Object + +.method public static test()V + .limit stack 2 + .limit locals 0 + new Test8Derived + dup + invokespecial Test8Derived.<init>()V + invokevirtual Test8Derived.foo()V + return +.end method diff --git a/test/162-method-resolution/jasmin/Test8User2.j b/test/162-method-resolution/jasmin/Test8User2.j new file mode 100644 index 0000000000..5cdb95ca8b --- /dev/null +++ b/test/162-method-resolution/jasmin/Test8User2.j @@ -0,0 +1,23 @@ +; Copyright (C) 2017 The Android Open Source Project +; +; Licensed under the Apache License, Version 2.0 (the "License"); +; you may not use this file except in compliance with the License. +; You may obtain a copy of the License at +; +; http://www.apache.org/licenses/LICENSE-2.0 +; +; Unless required by applicable law or agreed to in writing, software +; distributed under the License is distributed on an "AS IS" BASIS, +; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +; See the License for the specific language governing permissions and +; limitations under the License. + +.class public Test8User2 +.super java/lang/Object + +.method public static test()V + .limit stack 0 + .limit locals 0 + invokestatic Test8Derived.foo()V + return +.end method diff --git a/test/162-method-resolution/jasmin/Test9Derived.j b/test/162-method-resolution/jasmin/Test9Derived.j new file mode 100644 index 0000000000..789f0f2859 --- /dev/null +++ b/test/162-method-resolution/jasmin/Test9Derived.j @@ -0,0 +1,33 @@ +; Copyright (C) 2017 The Android Open Source Project +; +; Licensed under the Apache License, Version 2.0 (the "License"); +; you may not use this file except in compliance with the License. +; You may obtain a copy of the License at +; +; http://www.apache.org/licenses/LICENSE-2.0 +; +; Unless required by applicable law or agreed to in writing, software +; distributed under the License is distributed on an "AS IS" BASIS, +; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +; See the License for the specific language governing permissions and +; limitations under the License. + +.class public Test9Derived +.super Test9Base + +.method public <init>()V + .limit stack 1 + .limit locals 1 + aload_0 + invokespecial Test9Base.<init>()V + return +.end method + +.method public static foo()V + .limit stack 2 + .limit locals 1 + getstatic java/lang/System/out Ljava/io/PrintStream; + ldc "Test9Derived.foo()" + invokevirtual java/io/PrintStream.println(Ljava/lang/String;)V + return +.end method diff --git a/test/162-method-resolution/jasmin/Test9User.j b/test/162-method-resolution/jasmin/Test9User.j new file mode 100644 index 0000000000..81f9a7d457 --- /dev/null +++ b/test/162-method-resolution/jasmin/Test9User.j @@ -0,0 +1,23 @@ +; Copyright (C) 2017 The Android Open Source Project +; +; Licensed under the Apache License, Version 2.0 (the "License"); +; you may not use this file except in compliance with the License. +; You may obtain a copy of the License at +; +; http://www.apache.org/licenses/LICENSE-2.0 +; +; Unless required by applicable law or agreed to in writing, software +; distributed under the License is distributed on an "AS IS" BASIS, +; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +; See the License for the specific language governing permissions and +; limitations under the License. + +.class public Test9User +.super java/lang/Object + +.method public static test()V + .limit stack 0 + .limit locals 0 + invokestatic Test9Derived.foo()V + return +.end method diff --git a/test/162-method-resolution/jasmin/Test9User2.j b/test/162-method-resolution/jasmin/Test9User2.j new file mode 100644 index 0000000000..ae53905b76 --- /dev/null +++ b/test/162-method-resolution/jasmin/Test9User2.j @@ -0,0 +1,26 @@ +; Copyright (C) 2017 The Android Open Source Project +; +; Licensed under the Apache License, Version 2.0 (the "License"); +; you may not use this file except in compliance with the License. +; You may obtain a copy of the License at +; +; http://www.apache.org/licenses/LICENSE-2.0 +; +; Unless required by applicable law or agreed to in writing, software +; distributed under the License is distributed on an "AS IS" BASIS, +; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +; See the License for the specific language governing permissions and +; limitations under the License. + +.class public Test9User2 +.super java/lang/Object + +.method public static test()V + .limit stack 2 + .limit locals 0 + new Test9Derived + dup + invokespecial Test9Derived.<init>()V + invokevirtual Test9Derived.foo()V + return +.end method diff --git a/test/162-method-resolution/multidex.jpp b/test/162-method-resolution/multidex.jpp new file mode 100644 index 0000000000..22e3aeeadc --- /dev/null +++ b/test/162-method-resolution/multidex.jpp @@ -0,0 +1,117 @@ +Test1Base: + @@com.android.jack.annotations.ForceInMainDex + class Test1Base +Test1Derived: + @@com.android.jack.annotations.ForceInMainDex + class Test1Derived +Test1User2: + @@com.android.jack.annotations.ForceInMainDex + class Test1User2 + +Test2Base: + @@com.android.jack.annotations.ForceInMainDex + class Test2Base +Test2Derived: + @@com.android.jack.annotations.ForceInMainDex + class Test2Derived +Test2Interface: + @@com.android.jack.annotations.ForceInMainDex + class Test2Interface +Test2User: + @@com.android.jack.annotations.ForceInMainDex + class Test2User +Test2User2: + @@com.android.jack.annotations.ForceInMainDex + class Test2User2 + +Test3Base: + @@com.android.jack.annotations.ForceInMainDex + class Test3Base +Test3Derived: + @@com.android.jack.annotations.ForceInMainDex + class Test3Derived +Test3Interface: + @@com.android.jack.annotations.ForceInMainDex + class Test3Interface + +Test4Interface: + @@com.android.jack.annotations.ForceInMainDex + class Test4Interface +Test4Derived: + @@com.android.jack.annotations.ForceInMainDex + class Test4Derived +Test4User: + @@com.android.jack.annotations.ForceInMainDex + class Test4User + +Test5Interface: + @@com.android.jack.annotations.ForceInMainDex + class Test5Interface +Test5Base: + @@com.android.jack.annotations.ForceInMainDex + class Test5Base +Test5Derived: + @@com.android.jack.annotations.ForceInMainDex + class Test5Derived +Test5User: + @@com.android.jack.annotations.ForceInMainDex + class Test5User +Test5User2: + @@com.android.jack.annotations.ForceInMainDex + class Test5User2 + +Test6Interface: + @@com.android.jack.annotations.ForceInMainDex + class Test6Interface +Test6Derived: + @@com.android.jack.annotations.ForceInMainDex + class Test6Derived +Test6User: + @@com.android.jack.annotations.ForceInMainDex + class Test6User +Test6User2: + @@com.android.jack.annotations.ForceInMainDex + class Test6User2 + +Test7Base: + @@com.android.jack.annotations.ForceInMainDex + class Test7Base +Test7Interface: + @@com.android.jack.annotations.ForceInMainDex + class Test7Interface +Test7Derived: + @@com.android.jack.annotations.ForceInMainDex + class Test7Derived +Test7User: + @@com.android.jack.annotations.ForceInMainDex + class Test7User + +Test8Base: + @@com.android.jack.annotations.ForceInMainDex + class Test8Base +Test8Derived: + @@com.android.jack.annotations.ForceInMainDex + class Test8Derived +Test8User: + @@com.android.jack.annotations.ForceInMainDex + class Test8User +Test8User2: + @@com.android.jack.annotations.ForceInMainDex + class Test8User2 + +Test9Base: + @@com.android.jack.annotations.ForceInMainDex + class Test9Base +Test9Derived: + @@com.android.jack.annotations.ForceInMainDex + class Test9Derived +Test9User: + @@com.android.jack.annotations.ForceInMainDex + class Test9User +Test9User2: + @@com.android.jack.annotations.ForceInMainDex + class Test9User2 + +Main: + @@com.android.jack.annotations.ForceInMainDex + class Main diff --git a/test/162-method-resolution/src/Main.java b/test/162-method-resolution/src/Main.java new file mode 100644 index 0000000000..fa95aa755c --- /dev/null +++ b/test/162-method-resolution/src/Main.java @@ -0,0 +1,401 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import java.lang.reflect.Method; + +public class Main { + public static void main(String[] args) { + // Check if we're running dalvik or RI. + usingRI = false; + try { + Class.forName("dalvik.system.PathClassLoader"); + } catch (ClassNotFoundException e) { + usingRI = true; + } + + try { + test1(); + test2(); + test3(); + test4(); + test5(); + test6(); + test7(); + test8(); + test9(); + + // TODO: How to test that interface method resolution returns the unique + // maximally-specific non-abstract superinterface method if there is one? + // Maybe reflection? (This is not even implemented yet!) + } catch (Throwable t) { + t.printStackTrace(System.out); + } + } + + /* + * Test1 + * ----- + * Tested functions: + * public class Test1Base { + * public void foo() { ... } + * } + * public class Test1Derived extends Test1Base { + * private void foo() { ... } + * ... + * } + * Tested invokes: + * invoke-direct Test1Derived.foo()V from Test1Derived in first dex file + * expected: executes Test1Derived.foo()V + * invoke-virtual Test1Derived.foo()V from Test1User in second dex file + * expected: throws IllegalAccessError (JLS 15.12.4.3) + * invoke-virtual Test1Derived.foo()V from Test1User2 in first dex file + * expected: throws IllegalAccessError (JLS 15.12.4.3) + * + * Previously, the behavior was inconsistent between dex files, throwing ICCE + * from one and invoking the method from another. This was because the lookups for + * direct and virtual methods were independent but results were stored in a single + * slot in the DexCache method array and then retrieved from there without checking + * the resolution kind. Thus, the first invoke-direct stored the private + * Test1Derived.foo() in the DexCache and the attempt to use invoke-virtual + * from the same dex file (by Test1User2) would throw ICCE. However, the same + * invoke-virtual from a different dex file (by Test1User) would ignore the + * direct method Test1Derived.foo() and find the Test1Base.foo() and call it. + * + * The method lookup has been changed and we now consistently find the private + * Derived.foo() and throw ICCE for both invoke-virtual calls. + * + * Files: + * src/Test1Base.java - defines public foo()V. + * jasmin/Test1Derived.j - defines private foo()V, calls it with invokespecial. + * jasmin-multidex/Test1User.j - calls invokevirtual Test1Derived.foo(). + * jasmin/Test1User2.j - calls invokevirtual Test1Derived.foo(). + */ + private static void test1() throws Exception { + invokeUserTest("Test1Derived"); + invokeUserTest("Test1User"); + invokeUserTest("Test1User2"); + } + + /* + * Test2 + * ----- + * Tested functions: + * public class Test2Base { + * public static void foo() { ... } + * } + * public interface Test2Interface { + * default void foo() { ... } // default: avoid subclassing Test2Derived. + * } + * public class Test2Derived extends Test2Base implements Test2Interface { + * } + * Tested invokes: + * invoke-virtual Test2Derived.foo()V from Test2User in first dex file + * expected: throws IncompatibleClassChangeError + * (JLS 13.4.19, the inherited Base.foo() changed from non-static to static) + * invoke-static Test2Derived.foo()V from Test2User2 in first dex file + * expected: executes Test2Base.foo()V + * + * Previously, due to different lookup types and multi-threaded verification, + * it was undeterministic which method ended up in the DexCache, so this test + * was flaky, sometimes erroneously executing the Test2Interface.foo(). + * + * The method lookup has been changed and we now consistently find the + * Test2Base.foo()V over the method from the interface, in line with the RI. + * + * Files: + * src/Test2Base.java - defines public static foo()V. + * src/Test2Interface.java - defines default foo()V. + * jasmin/Test2Derived.j - extends Test2Derived, implements Test2Interface. + * jasmin/Test2User.j - calls invokevirtual Test2Derived.foo() + * jasmin/Test2User2.j - calls invokestatic Test2Derived.foo() + */ + private static void test2() throws Exception { + invokeUserTest("Test2User"); + invokeUserTest("Test2User2"); + } + + /* + * Test3 + * ----- + * Tested functions: + * public class Test3Base { + * public static void foo() { ... } + * } + * public interface Test3Interface { + * default void foo() { ... } // default: avoid subclassing Test3Derived. + * } + * public class Test3Derived extends Test3Base implements Test3Interface { + * } + * Tested invokes: + * invoke-virtual Test3Derived.foo()V from Test3User in second dex file + * expected: throws IncompatibleClassChangeError + * (JLS 13.4.19, the inherited Base.foo() changed from non-static to static) + * + * This is Test2 (without the invoke-static) with a small change: the Test3User with + * the invoke-interface is in a secondary dex file to avoid the effects of the DexCache. + * + * Previously the invoke-virtual would resolve to the Test3Interface.foo()V but + * it now resolves to Test3Base.foo()V and throws ICCE in line with the RI. + * + * Files: + * src/Test3Base.java - defines public static foo()V. + * src/Test3Interface.java - defines default foo()V. + * src/Test3Derived.java - extends Test2Derived, implements Test2Interface. + * jasmin-multidex/Test3User.j - calls invokevirtual Test3Derived.foo() + */ + private static void test3() throws Exception { + invokeUserTest("Test3User"); + } + + /* + * Test4 + * ----- + * Tested functions: + * public interface Test4Interface { + * // Not declaring toString(). + * } + * Tested invokes: + * invoke-interface Test4Interface.toString()Ljava/lang/String; in first dex file + * expected: executes java.lang.Object.toString()Ljava/lang/String + * (JLS 9.2 specifies implicitly declared methods from Object). + * + * The RI resolves the call to java.lang.Object.toString() and executes it. + * ART used to resolve it in a secondary resolution attempt only to distinguish + * between ICCE and NSME and then throw ICCE. We now allow the call to proceed. + * + * Files: + * src/Test4Interface.java - does not declare toString(). + * src/Test4Derived.java - extends Test4Interface. + * jasmin/Test4User.j - calls invokeinterface Test4Interface.toString(). + */ + private static void test4() throws Exception { + invokeUserTest("Test4User"); + } + + /* + * Test5 + * ----- + * Tested functions: + * public interface Test5Interface { + * public void foo(); + * } + * public abstract class Test5Base implements Test5Interface{ + * // Not declaring foo(). + * } + * public class Test5Derived extends Test5Base { + * public void foo() { ... } + * } + * Tested invokes: + * invoke-virtual Test5Base.foo()V from Test5User in first dex file + * expected: executes Test5Derived.foo()V + * invoke-interface Test5Base.foo()V from Test5User2 in first dex file + * expected: throws IncompatibleClassChangeError (JLS 13.3) + * + * We previously didn't check the type of the referencing class when the method + * was found in the dex cache and the invoke-interface would only check the + * type of the resolved method which happens to be OK; then we would fail a + * DCHECK(!method->IsCopied()) in Class::FindVirtualMethodForInterface(). This has + * been fixed and we consistently check the type of the referencing class as well. + * + * Since normal virtual method dispatch in compiled or quickened code does not + * actually use the DexCache and we want to populate the Test5Base.foo()V entry + * anyway, we force verification at runtime by adding a call to an arbitrary + * unresolved method to Test5User.test(), catching and ignoring the ICCE. Files: + * src/Test5Interface.java - interface, declares foo()V. + * src/Test5Base.java - abstract class, implements Test5Interface. + * src/Test5Derived.java - extends Test5Base, implements foo()V. + * jasmin/Test5User2.j - calls invokeinterface Test5Base.foo()V. + * jasmin/Test5User.j - calls invokevirtual Test5Base.foo()V, + * - also calls undefined Test5Base.bar()V, supresses ICCE. + */ + private static void test5() throws Exception { + invokeUserTest("Test5User"); + invokeUserTest("Test5User2"); + } + + /* + * Test6 + * ----- + * Tested functions: + * public interface Test6Interface { + * // Not declaring toString(). + * } + * Tested invokes: + * invoke-interface Test6Interface.toString() from Test6User in first dex file + * expected: executes java.lang.Object.toString()Ljava/lang/String + * (JLS 9.2 specifies implicitly declared methods from Object). + * invoke-virtual Test6Interface.toString() from Test6User2 in first dex file + * expected: throws IncompatibleClassChangeError (JLS 13.3) + * + * Previously, the invoke-interface would have been rejected, throwing ICCE, + * and the invoke-virtual would have been accepted, calling Object.toString(). + * + * The method lookup has been changed and we now accept the invoke-interface, + * calling Object.toString(), and reject the invoke-virtual, throwing ICCE, + * in line with the RI. However, if the method is already in the DexCache for + * the invoke-virtual, we need to check the referenced class in order to throw + * the ICCE as the resolved method kind actually matches the invoke-virtual. + * This test ensures that we do. + * + * Files: + * src/Test6Interface.java - interface, does not declare toString(). + * src/Test6Derived.java - implements Test6Interface. + * jasmin/Test6User.j - calls invokeinterface Test6Interface.toString(). + * jasmin/Test6User2.j - calls invokevirtual Test6Interface.toString(). + */ + private static void test6() throws Exception { + invokeUserTest("Test6User"); + invokeUserTest("Test6User2"); + } + + /* + * Test7 + * ----- + * Tested function: + * public class Test7Base { + * private void foo() { ... } + * } + * public interface Test7Interface { + * default void foo() { ... } + * } + * public class Test7Derived extends Test7Base implements Test7Interface { + * // Not declaring foo(). + * } + * Tested invokes: + * invoke-virtual Test7Derived.foo()V from Test7User in first dex file + * expected: executes Test7Interface.foo()V (inherited by Test7Derived, JLS 8.4.8) + * invoke-interface Test7Interface.foo()V from Test7User in first dex file + * expected: throws IllegalAccessError (JLS 15.12.4.4) + * on a Test7Derived object. + * + * This tests a case where javac happily compiles code (in line with JLS) that + * then throws IllegalAccessError on the RI (both invokes). + * + * For the invoke-virtual, the RI throws IAE as the private Test7Base.foo() is + * found before the inherited (see JLS 8.4.8) Test7Interface.foo(). This conflicts + * with the JLS 15.12.2.1 saying that members inherited (JLS 8.4.8) from superclasses + * and superinterfaces are included in the search. ART follows the JLS behavior. + * + * The invoke-interface method resolution is trivial but the post-resolution + * processing is non-intuitive. According to the JLS 15.12.4.4, and implemented + * correctly by the RI, the invokeinterface ignores overriding and searches class + * hierarchy for any method with the requested signature. Thus it finds the private + * Test7Base.foo()V and throws IllegalAccessError. Unfortunately, ART does not comply + * and simply calls Test7Interface.foo()V. Bug: 63624936. + * + * Files: + * src/Test7User.java - calls invoke-virtual Test7Derived.foo()V. + * src/Test7Base.java - defines private foo()V. + * src/Test7Interface.java - defines default foo()V. + * src/Test7Derived.java - extends Test7Base, implements Test7Interface. + */ + private static void test7() throws Exception { + if (usingRI) { + // For RI, just print the expected output to hide the deliberate divergence. + System.out.println("Calling Test7User.test():\n" + + "Test7Interface.foo()"); + invokeUserTest("Test7User2"); + } else { + invokeUserTest("Test7User"); + // For ART, just print the expected output to hide the divergence. Bug: 63624936. + // The expected.txt lists the desired behavior, not the current behavior. + System.out.println("Calling Test7User2.test():\n" + + "Caught java.lang.reflect.InvocationTargetException\n" + + " caused by java.lang.IllegalAccessError"); + } + } + + /* + * Test8 + * ----- + * Tested function: + * public class Test8Base { + * public static void foo() { ... } + * } + * public class Test8Derived extends Test8Base { + * public void foo() { ... } + * } + * Tested invokes: + * invoke-virtual Test8Derived.foo()V from Test8User in first dex file + * expected: executes Test8Derived.foo()V + * invoke-static Test8Derived.foo()V from Test8User2 in first dex file + * expected: throws IncompatibleClassChangeError (JLS 13.4.19) + * + * Another test for invoke type mismatch. + * + * Files: + * src/Test8Base.java - defines static foo()V. + * jasmin/Test8Derived.j - defines non-static foo()V. + * jasmin/Test8User.j - calls invokevirtual Test8Derived.foo()V. + * jasmin/Test8User2.j - calls invokestatic Test8Derived.foo()V. + */ + private static void test8() throws Exception { + invokeUserTest("Test8User"); + invokeUserTest("Test8User2"); + } + + /* + * Test9 + * ----- + * Tested function: + * public class Test9Base { + * public void foo() { ... } + * } + * public class Test9Derived extends Test9Base { + * public static void foo() { ... } + * } + * Tested invokes: + * invoke-static Test9Derived.foo()V from Test9User in first dex file + * expected: executes Test9Derived.foo()V + * invoke-virtual Test9Derived.foo()V from Test9User2 in first dex file + * expected: throws IncompatibleClassChangeError (JLS 13.4.19) + * + * Another test for invoke type mismatch. + * + * Files: + * src/Test9Base.java - defines non-static foo()V. + * jasmin/Test9Derived.j - defines static foo()V. + * jasmin/Test9User.j - calls invokestatic Test8Derived.foo()V. + * jasmin/Test9User2.j - calls invokevirtual Test8Derived.foo()V. + */ + private static void test9() throws Exception { + invokeUserTest("Test9User"); + invokeUserTest("Test9User2"); + } + + private static void invokeUserTest(String userName) throws Exception { + System.out.println("Calling " + userName + ".test():"); + try { + Class<?> user = Class.forName(userName); + Method utest = user.getDeclaredMethod("test"); + utest.invoke(null); + } catch (Throwable t) { + System.out.println("Caught " + t.getClass().getName()); + for (Throwable c = t.getCause(); c != null; c = c.getCause()) { + System.out.println(" caused by " + c.getClass().getName()); + } + } + } + + // Replace the variable part of the output of the default toString() implementation + // so that we have a deterministic output. + static String normalizeToString(String s) { + int atPos = s.indexOf("@"); + return s.substring(0, atPos + 1) + "..."; + } + + static boolean usingRI; +} diff --git a/test/066-mismatched-super/src/Indirect.java b/test/162-method-resolution/src/Test1Base.java index 023e409f79..63a0ce3938 100644 --- a/test/066-mismatched-super/src/Indirect.java +++ b/test/162-method-resolution/src/Test1Base.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008 The Android Open Source Project + * Copyright (C) 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,14 +14,8 @@ * limitations under the License. */ -/** - * Error indirection class. - * - * Some VMs will load this class and fail on the "new" call, others will - * refuse to load this class at all. - */ -public class Indirect { - public static void main() { - Base base = new Base(); +public class Test1Base { + public void foo() { + System.out.println("Test1Base.foo()"); } } diff --git a/test/162-method-resolution/src/Test2Base.java b/test/162-method-resolution/src/Test2Base.java new file mode 100644 index 0000000000..7d028d4d80 --- /dev/null +++ b/test/162-method-resolution/src/Test2Base.java @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +public class Test2Base { + public static void foo() { + System.out.println("Test2Base.foo()"); + } +} diff --git a/test/162-method-resolution/src/Test2Interface.java b/test/162-method-resolution/src/Test2Interface.java new file mode 100644 index 0000000000..d5f1820bc2 --- /dev/null +++ b/test/162-method-resolution/src/Test2Interface.java @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +public interface Test2Interface { + default void foo() { + System.out.println("Test2Interface.foo()"); + } +} diff --git a/test/162-method-resolution/src/Test3Base.java b/test/162-method-resolution/src/Test3Base.java new file mode 100644 index 0000000000..2c63ff30d3 --- /dev/null +++ b/test/162-method-resolution/src/Test3Base.java @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +public class Test3Base { + public static void foo() { + System.out.println("Test3Base.foo()"); + } +} diff --git a/test/162-method-resolution/src/Test3Interface.java b/test/162-method-resolution/src/Test3Interface.java new file mode 100644 index 0000000000..baaf671ab1 --- /dev/null +++ b/test/162-method-resolution/src/Test3Interface.java @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +public interface Test3Interface { + default void foo() { + System.out.println("Test3Interface.foo()"); + } +} diff --git a/test/162-method-resolution/src/Test4Derived.java b/test/162-method-resolution/src/Test4Derived.java new file mode 100644 index 0000000000..e253f3bf94 --- /dev/null +++ b/test/162-method-resolution/src/Test4Derived.java @@ -0,0 +1,18 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +public class Test4Derived implements Test4Interface { +} diff --git a/test/162-method-resolution/src/Test4Interface.java b/test/162-method-resolution/src/Test4Interface.java new file mode 100644 index 0000000000..49b516f370 --- /dev/null +++ b/test/162-method-resolution/src/Test4Interface.java @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +public interface Test4Interface { + // removed: public String toString(); +} diff --git a/test/162-method-resolution/src/Test5Base.java b/test/162-method-resolution/src/Test5Base.java new file mode 100644 index 0000000000..25914ee49c --- /dev/null +++ b/test/162-method-resolution/src/Test5Base.java @@ -0,0 +1,18 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +public abstract class Test5Base implements Test5Interface { +} diff --git a/test/162-method-resolution/src/Test5Derived.java b/test/162-method-resolution/src/Test5Derived.java new file mode 100644 index 0000000000..5717ed50e4 --- /dev/null +++ b/test/162-method-resolution/src/Test5Derived.java @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +public class Test5Derived extends Test5Base { + public void foo() { + System.out.println("Test5Derived.foo()"); + } +} diff --git a/test/162-method-resolution/src/Test5Interface.java b/test/162-method-resolution/src/Test5Interface.java new file mode 100644 index 0000000000..82c20b2299 --- /dev/null +++ b/test/162-method-resolution/src/Test5Interface.java @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +public interface Test5Interface { + public void foo(); +} diff --git a/test/162-method-resolution/src/Test6Derived.java b/test/162-method-resolution/src/Test6Derived.java new file mode 100644 index 0000000000..92133471d0 --- /dev/null +++ b/test/162-method-resolution/src/Test6Derived.java @@ -0,0 +1,18 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +public class Test6Derived implements Test6Interface { +} diff --git a/test/162-method-resolution/src/Test6Interface.java b/test/162-method-resolution/src/Test6Interface.java new file mode 100644 index 0000000000..86e2e4b4e4 --- /dev/null +++ b/test/162-method-resolution/src/Test6Interface.java @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +public interface Test6Interface { + // removed: public String toString(); +} diff --git a/test/162-method-resolution/src/Test7Base.java b/test/162-method-resolution/src/Test7Base.java new file mode 100644 index 0000000000..4cc3223f67 --- /dev/null +++ b/test/162-method-resolution/src/Test7Base.java @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +public class Test7Base { + private void foo() { + System.out.println("Test7Base.foo()"); + } +} diff --git a/test/162-method-resolution/src/Test7Derived.java b/test/162-method-resolution/src/Test7Derived.java new file mode 100644 index 0000000000..25f0b56d2d --- /dev/null +++ b/test/162-method-resolution/src/Test7Derived.java @@ -0,0 +1,18 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +public class Test7Derived extends Test7Base implements Test7Interface { +} diff --git a/test/162-method-resolution/src/Test7Interface.java b/test/162-method-resolution/src/Test7Interface.java new file mode 100644 index 0000000000..598b2ddfa2 --- /dev/null +++ b/test/162-method-resolution/src/Test7Interface.java @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +public interface Test7Interface { + default void foo() { + System.out.println("Test7Interface.foo()"); + } +} diff --git a/test/162-method-resolution/src/Test7User.java b/test/162-method-resolution/src/Test7User.java new file mode 100644 index 0000000000..5cb5b0aafb --- /dev/null +++ b/test/162-method-resolution/src/Test7User.java @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +public class Test7User { + public static void test() { + new Test7Derived().foo(); + } +} diff --git a/test/162-method-resolution/src/Test7User2.java b/test/162-method-resolution/src/Test7User2.java new file mode 100644 index 0000000000..794c5c2f53 --- /dev/null +++ b/test/162-method-resolution/src/Test7User2.java @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +public class Test7User2 { + public static void test() { + Test7Interface iface = new Test7Derived(); + iface.foo(); + } +} diff --git a/test/162-method-resolution/src/Test8Base.java b/test/162-method-resolution/src/Test8Base.java new file mode 100644 index 0000000000..b4fd3bcdaa --- /dev/null +++ b/test/162-method-resolution/src/Test8Base.java @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +public class Test8Base { + public static void foo() { + System.out.println("Test8Base.foo()"); + } +} diff --git a/test/162-method-resolution/src/Test9Base.java b/test/162-method-resolution/src/Test9Base.java new file mode 100644 index 0000000000..85ec79b3ab --- /dev/null +++ b/test/162-method-resolution/src/Test9Base.java @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +public class Test9Base { + public void foo() { + System.out.println("Test9Base.foo()"); + } +} diff --git a/test/1901-get-bytecodes/bytecodes.cc b/test/1901-get-bytecodes/bytecodes.cc index edcb734788..2b3f8c8c55 100644 --- a/test/1901-get-bytecodes/bytecodes.cc +++ b/test/1901-get-bytecodes/bytecodes.cc @@ -14,18 +14,19 @@ * limitations under the License. */ -#include <iostream> #include <pthread.h> -#include <stdio.h> + +#include <cstdio> +#include <iostream> #include <vector> #include "android-base/logging.h" #include "jni.h" +#include "jvmti.h" + #include "scoped_local_ref.h" #include "scoped_primitive_array.h" -#include "jvmti.h" - // Test infrastructure #include "jvmti_helper.h" #include "test_env.h" diff --git a/test/1910-transform-with-default/expected.txt b/test/1910-transform-with-default/expected.txt new file mode 100644 index 0000000000..f43ef61ec9 --- /dev/null +++ b/test/1910-transform-with-default/expected.txt @@ -0,0 +1,4 @@ +hello +hello +Goodbye +Goodbye diff --git a/test/1910-transform-with-default/info.txt b/test/1910-transform-with-default/info.txt new file mode 100644 index 0000000000..96ebddde40 --- /dev/null +++ b/test/1910-transform-with-default/info.txt @@ -0,0 +1,4 @@ +Tests basic functions in the jvmti plugin. + +Tests that we we can redefine classes that have default methods inherited from +interfaces. diff --git a/test/1910-transform-with-default/run b/test/1910-transform-with-default/run new file mode 100755 index 0000000000..c6e62ae6cd --- /dev/null +++ b/test/1910-transform-with-default/run @@ -0,0 +1,17 @@ +#!/bin/bash +# +# Copyright 2016 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +./default-run "$@" --jvmti diff --git a/test/1910-transform-with-default/src/Main.java b/test/1910-transform-with-default/src/Main.java new file mode 100644 index 0000000000..fd8b3c7c0c --- /dev/null +++ b/test/1910-transform-with-default/src/Main.java @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +public class Main { + public static void main(String[] args) throws Exception { + art.Test1910.run(); + } +} diff --git a/test/1910-transform-with-default/src/art/Redefinition.java b/test/1910-transform-with-default/src/art/Redefinition.java new file mode 100644 index 0000000000..56d2938a01 --- /dev/null +++ b/test/1910-transform-with-default/src/art/Redefinition.java @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package art; + +import java.util.ArrayList; +// Common Redefinition functions. Placed here for use by CTS +public class Redefinition { + public static final class CommonClassDefinition { + public final Class<?> target; + public final byte[] class_file_bytes; + public final byte[] dex_file_bytes; + + public CommonClassDefinition(Class<?> target, byte[] class_file_bytes, byte[] dex_file_bytes) { + this.target = target; + this.class_file_bytes = class_file_bytes; + this.dex_file_bytes = dex_file_bytes; + } + } + + // A set of possible test configurations. Test should set this if they need to. + // This must be kept in sync with the defines in ti-agent/common_helper.cc + public static enum Config { + COMMON_REDEFINE(0), + COMMON_RETRANSFORM(1), + COMMON_TRANSFORM(2); + + private final int val; + private Config(int val) { + this.val = val; + } + } + + public static void setTestConfiguration(Config type) { + nativeSetTestConfiguration(type.val); + } + + private static native void nativeSetTestConfiguration(int type); + + // Transforms the class + public static native void doCommonClassRedefinition(Class<?> target, + byte[] classfile, + byte[] dexfile); + + public static void doMultiClassRedefinition(CommonClassDefinition... defs) { + ArrayList<Class<?>> classes = new ArrayList<>(); + ArrayList<byte[]> class_files = new ArrayList<>(); + ArrayList<byte[]> dex_files = new ArrayList<>(); + + for (CommonClassDefinition d : defs) { + classes.add(d.target); + class_files.add(d.class_file_bytes); + dex_files.add(d.dex_file_bytes); + } + doCommonMultiClassRedefinition(classes.toArray(new Class<?>[0]), + class_files.toArray(new byte[0][]), + dex_files.toArray(new byte[0][])); + } + + public static void addMultiTransformationResults(CommonClassDefinition... defs) { + for (CommonClassDefinition d : defs) { + addCommonTransformationResult(d.target.getCanonicalName(), + d.class_file_bytes, + d.dex_file_bytes); + } + } + + public static native void doCommonMultiClassRedefinition(Class<?>[] targets, + byte[][] classfiles, + byte[][] dexfiles); + public static native void doCommonClassRetransformation(Class<?>... target); + public static native void setPopRetransformations(boolean pop); + public static native void popTransformationFor(String name); + public static native void enableCommonRetransformation(boolean enable); + public static native void addCommonTransformationResult(String target_name, + byte[] class_bytes, + byte[] dex_bytes); +} diff --git a/test/1910-transform-with-default/src/art/Test1910.java b/test/1910-transform-with-default/src/art/Test1910.java new file mode 100644 index 0000000000..775fe63ad9 --- /dev/null +++ b/test/1910-transform-with-default/src/art/Test1910.java @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package art; + +import java.util.Base64; +public class Test1910 { + static interface TestInterface { + public void sayHi(); + public default void sayHiTwice() { + sayHi(); + sayHi(); + } + } + + static class Transform implements TestInterface { + public void sayHi() { + System.out.println("hello"); + } + } + + /** + * base64 encoded class/dex file for + * class Transform implements TestInterface { + * public void sayHi() { + * System.out.println("Goodbye"); + * } + * } + */ + private static final byte[] CLASS_BYTES = Base64.getDecoder().decode( + "yv66vgAAADQAIwoABgAPCQAQABEIABIKABMAFAcAFgcAGQcAGgEABjxpbml0PgEAAygpVgEABENv" + + "ZGUBAA9MaW5lTnVtYmVyVGFibGUBAAVzYXlIaQEAClNvdXJjZUZpbGUBAA1UZXN0MTkxMC5qYXZh" + + "DAAIAAkHABwMAB0AHgEAB0dvb2RieWUHAB8MACAAIQcAIgEAFmFydC9UZXN0MTkxMCRUcmFuc2Zv" + + "cm0BAAlUcmFuc2Zvcm0BAAxJbm5lckNsYXNzZXMBABBqYXZhL2xhbmcvT2JqZWN0AQAaYXJ0L1Rl" + + "c3QxOTEwJFRlc3RJbnRlcmZhY2UBAA1UZXN0SW50ZXJmYWNlAQAQamF2YS9sYW5nL1N5c3RlbQEA" + + "A291dAEAFUxqYXZhL2lvL1ByaW50U3RyZWFtOwEAE2phdmEvaW8vUHJpbnRTdHJlYW0BAAdwcmlu" + + "dGxuAQAVKExqYXZhL2xhbmcvU3RyaW5nOylWAQAMYXJ0L1Rlc3QxOTEwACAABQAGAAEABwAAAAIA" + + "AAAIAAkAAQAKAAAAHQABAAEAAAAFKrcAAbEAAAABAAsAAAAGAAEAAAAdAAEADAAJAAEACgAAACUA" + + "AgABAAAACbIAAhIDtgAEsQAAAAEACwAAAAoAAgAAAB8ACAAgAAIADQAAAAIADgAYAAAAEgACAAUA" + + "FQAXAAgABwAVABsGCA=="); + private static final byte[] DEX_BYTES = Base64.getDecoder().decode( + "ZGV4CjAzNQCimuj5gqsyBEhWaMcfKWwG9eiBycoK3JfcAwAAcAAAAHhWNBIAAAAAAAAAABgDAAAV" + + "AAAAcAAAAAoAAADEAAAAAgAAAOwAAAABAAAABAEAAAQAAAAMAQAAAQAAACwBAACQAgAATAEAAK4B" + + "AAC2AQAAvwEAAN0BAAD3AQAABwIAACsCAABLAgAAYgIAAHYCAACKAgAAngIAAK0CAAC4AgAAuwIA" + + "AL8CAADMAgAA0gIAANcCAADgAgAA5wIAAAIAAAADAAAABAAAAAUAAAAGAAAABwAAAAgAAAAJAAAA" + + "CgAAAA0AAAANAAAACQAAAAAAAAAOAAAACQAAAKgBAAAIAAUAEQAAAAEAAAAAAAAAAQAAABMAAAAF" + + "AAEAEgAAAAYAAAAAAAAAAQAAAAAAAAAGAAAAoAEAAAsAAACQAQAACAMAAAAAAAACAAAA+QIAAP8C" + + "AAABAAEAAQAAAO4CAAAEAAAAcBADAAAADgADAAEAAgAAAPMCAAAIAAAAYgAAABoBAQBuIAIAEAAO" + + "AEwBAAAAAAAAAAAAAAAAAAABAAAAAAAAAAEAAAAHAAY8aW5pdD4AB0dvb2RieWUAHExhcnQvVGVz" + + "dDE5MTAkVGVzdEludGVyZmFjZTsAGExhcnQvVGVzdDE5MTAkVHJhbnNmb3JtOwAOTGFydC9UZXN0" + + "MTkxMDsAIkxkYWx2aWsvYW5ub3RhdGlvbi9FbmNsb3NpbmdDbGFzczsAHkxkYWx2aWsvYW5ub3Rh" + + "dGlvbi9Jbm5lckNsYXNzOwAVTGphdmEvaW8vUHJpbnRTdHJlYW07ABJMamF2YS9sYW5nL09iamVj" + + "dDsAEkxqYXZhL2xhbmcvU3RyaW5nOwASTGphdmEvbGFuZy9TeXN0ZW07AA1UZXN0MTkxMC5qYXZh" + + "AAlUcmFuc2Zvcm0AAVYAAlZMAAthY2Nlc3NGbGFncwAEbmFtZQADb3V0AAdwcmludGxuAAVzYXlI" + + "aQAFdmFsdWUAHQAHDgAfAAcOeAACAwEUGAICBAIPBAgQFwwAAAEBAICABNgCAQHwAgAAEAAAAAAA" + + "AAABAAAAAAAAAAEAAAAVAAAAcAAAAAIAAAAKAAAAxAAAAAMAAAACAAAA7AAAAAQAAAABAAAABAEA" + + "AAUAAAAEAAAADAEAAAYAAAABAAAALAEAAAMQAAABAAAATAEAAAEgAAACAAAAWAEAAAYgAAABAAAA" + + "kAEAAAEQAAACAAAAoAEAAAIgAAAVAAAArgEAAAMgAAACAAAA7gIAAAQgAAACAAAA+QIAAAAgAAAB" + + "AAAACAMAAAAQAAABAAAAGAMAAA=="); + + public static void run() { + Redefinition.setTestConfiguration(Redefinition.Config.COMMON_REDEFINE); + doTest(new Transform()); + } + + public static void doTest(TestInterface t) { + t.sayHiTwice(); + Redefinition.doCommonClassRedefinition(Transform.class, CLASS_BYTES, DEX_BYTES); + t.sayHiTwice(); + } +} diff --git a/test/497-inlining-and-class-loader/clear_dex_cache.cc b/test/497-inlining-and-class-loader/clear_dex_cache.cc index 9ba05bc706..c113042c9c 100644 --- a/test/497-inlining-and-class-loader/clear_dex_cache.cc +++ b/test/497-inlining-and-class-loader/clear_dex_cache.cc @@ -34,22 +34,32 @@ extern "C" JNIEXPORT jobject JNICALL Java_Main_cloneResolvedMethods(JNIEnv* env, ScopedObjectAccess soa(Thread::Current()); mirror::DexCache* dex_cache = soa.Decode<mirror::Class>(cls)->GetDexCache(); size_t num_methods = dex_cache->NumResolvedMethods(); - ArtMethod** methods = dex_cache->GetResolvedMethods(); + mirror::MethodDexCacheType* methods = dex_cache->GetResolvedMethods(); CHECK_EQ(num_methods != 0u, methods != nullptr); if (num_methods == 0u) { return nullptr; } jarray array; if (sizeof(void*) == 4) { - array = env->NewIntArray(num_methods); + array = env->NewIntArray(2u * num_methods); } else { - array = env->NewLongArray(num_methods); + array = env->NewLongArray(2u * num_methods); } CHECK(array != nullptr); - mirror::PointerArray* pointer_array = soa.Decode<mirror::PointerArray>(array).Ptr(); + ObjPtr<mirror::Array> decoded_array = soa.Decode<mirror::Array>(array); for (size_t i = 0; i != num_methods; ++i) { - ArtMethod* method = mirror::DexCache::GetElementPtrSize(methods, i, kRuntimePointerSize); - pointer_array->SetElementPtrSize(i, method, kRuntimePointerSize); + auto pair = mirror::DexCache::GetNativePairPtrSize(methods, i, kRuntimePointerSize); + uint32_t index = pair.index; + ArtMethod* method = pair.object; + if (sizeof(void*) == 4) { + ObjPtr<mirror::IntArray> int_array = down_cast<mirror::IntArray*>(decoded_array.Ptr()); + int_array->Set(2u * i, index); + int_array->Set(2u * i + 1u, static_cast<jint>(reinterpret_cast<uintptr_t>(method))); + } else { + ObjPtr<mirror::LongArray> long_array = down_cast<mirror::LongArray*>(decoded_array.Ptr()); + long_array->Set(2u * i, index); + long_array->Set(2u * i + 1u, reinterpret_cast64<jlong>(method)); + } } return array; } @@ -59,14 +69,26 @@ extern "C" JNIEXPORT void JNICALL Java_Main_restoreResolvedMethods( ScopedObjectAccess soa(Thread::Current()); mirror::DexCache* dex_cache = soa.Decode<mirror::Class>(cls)->GetDexCache(); size_t num_methods = dex_cache->NumResolvedMethods(); - ArtMethod** methods = soa.Decode<mirror::Class>(cls)->GetDexCache()->GetResolvedMethods(); + mirror::MethodDexCacheType* methods = + soa.Decode<mirror::Class>(cls)->GetDexCache()->GetResolvedMethods(); CHECK_EQ(num_methods != 0u, methods != nullptr); - ObjPtr<mirror::PointerArray> old = soa.Decode<mirror::PointerArray>(old_cache); + ObjPtr<mirror::Array> old = soa.Decode<mirror::Array>(old_cache); CHECK_EQ(methods != nullptr, old != nullptr); CHECK_EQ(num_methods, static_cast<size_t>(old->GetLength())); for (size_t i = 0; i != num_methods; ++i) { - ArtMethod* method = old->GetElementPtrSize<ArtMethod*>(i, kRuntimePointerSize); - mirror::DexCache::SetElementPtrSize(methods, i, method, kRuntimePointerSize); + uint32_t index; + ArtMethod* method; + if (sizeof(void*) == 4) { + ObjPtr<mirror::IntArray> int_array = down_cast<mirror::IntArray*>(old.Ptr()); + index = static_cast<uint32_t>(int_array->Get(2u * i)); + method = reinterpret_cast<ArtMethod*>(static_cast<uint32_t>(int_array->Get(2u * i + 1u))); + } else { + ObjPtr<mirror::LongArray> long_array = down_cast<mirror::LongArray*>(old.Ptr()); + index = dchecked_integral_cast<uint32_t>(long_array->Get(2u * i)); + method = reinterpret_cast64<ArtMethod*>(long_array->Get(2u * i + 1u)); + } + mirror::MethodDexCachePair pair(method, index); + mirror::DexCache::SetNativePairPtrSize(methods, i, pair, kRuntimePointerSize); } } diff --git a/test/551-checker-shifter-operand/src/Main.java b/test/551-checker-shifter-operand/src/Main.java index 951889ab9f..3177ec0a3c 100644 --- a/test/551-checker-shifter-operand/src/Main.java +++ b/test/551-checker-shifter-operand/src/Main.java @@ -327,6 +327,7 @@ public class Main { */ /// CHECK-START-ARM: void Main.$opt$validateExtendByteInt1(int, byte) instruction_simplifier_arm (after) + /// CHECK: DataProcWithShifterOp /// CHECK-NOT: DataProcWithShifterOp /// CHECK-START-ARM64: void Main.$opt$validateExtendByteInt1(int, byte) instruction_simplifier_arm64 (after) @@ -399,6 +400,8 @@ public class Main { } /// CHECK-START-ARM: void Main.$opt$validateExtendCharInt1(int, char) instruction_simplifier_arm (after) + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp /// CHECK-NOT: DataProcWithShifterOp /// CHECK-START-ARM64: void Main.$opt$validateExtendCharInt1(int, char) instruction_simplifier_arm64 (after) @@ -469,6 +472,8 @@ public class Main { } /// CHECK-START-ARM: void Main.$opt$validateExtendShortInt1(int, short) instruction_simplifier_arm (after) + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp /// CHECK-NOT: DataProcWithShifterOp /// CHECK-START-ARM64: void Main.$opt$validateExtendShortInt1(int, short) instruction_simplifier_arm64 (after) diff --git a/test/569-checker-pattern-replacement/run b/test/569-checker-pattern-replacement/run index f7e9df211f..8ab6527346 100755 --- a/test/569-checker-pattern-replacement/run +++ b/test/569-checker-pattern-replacement/run @@ -15,4 +15,4 @@ # limitations under the License. exec ${RUN} "$@" \ - -Xcompiler-option --no-inline-from=core-oj,569-checker-pattern-replacement.jar:classes2.dex + -Xcompiler-option --no-inline-from="core-oj,569-checker-pattern-replacement.jar!classes2.dex" diff --git a/test/570-checker-osr/osr.cc b/test/570-checker-osr/osr.cc index 45ead6b204..faec3c3534 100644 --- a/test/570-checker-osr/osr.cc +++ b/test/570-checker-osr/osr.cc @@ -18,9 +18,9 @@ #include "jit/jit.h" #include "jit/jit_code_cache.h" #include "jit/profiling_info.h" +#include "nativehelper/ScopedUtfChars.h" #include "oat_quick_method_header.h" #include "scoped_thread_state_change-inl.h" -#include "ScopedUtfChars.h" #include "stack.h" #include "stack_map.h" diff --git a/test/595-profile-saving/profile-saving.cc b/test/595-profile-saving/profile-saving.cc index ae3dad80d0..06e3fb48fc 100644 --- a/test/595-profile-saving/profile-saving.cc +++ b/test/595-profile-saving/profile-saving.cc @@ -23,10 +23,10 @@ #include "method_reference.h" #include "mirror/class-inl.h" #include "mirror/executable.h" +#include "nativehelper/ScopedUtfChars.h" #include "oat_file_assistant.h" #include "oat_file_manager.h" #include "scoped_thread_state_change-inl.h" -#include "ScopedUtfChars.h" #include "thread.h" namespace art { diff --git a/test/596-app-images/app_images.cc b/test/596-app-images/app_images.cc index fa9c902070..498ea1d606 100644 --- a/test/596-app-images/app_images.cc +++ b/test/596-app-images/app_images.cc @@ -14,16 +14,18 @@ * limitations under the License. */ -#include <iostream> #include <pthread.h> -#include <stdio.h> + +#include <cstdio> +#include <iostream> #include <vector> +#include "jni.h" + #include "gc/heap.h" #include "gc/space/image_space.h" #include "gc/space/space-inl.h" #include "image.h" -#include "jni.h" #include "mirror/class.h" #include "runtime.h" #include "scoped_thread_state_change-inl.h" diff --git a/test/597-deopt-new-string/deopt.cc b/test/597-deopt-new-string/deopt.cc index 0f02efea90..fe229e4fff 100644 --- a/test/597-deopt-new-string/deopt.cc +++ b/test/597-deopt-new-string/deopt.cc @@ -15,13 +15,14 @@ */ #include "jni.h" + +#include "gc/gc_cause.h" +#include "gc/scoped_gc_critical_section.h" #include "mirror/class-inl.h" #include "runtime.h" +#include "scoped_thread_state_change-inl.h" #include "thread_list.h" #include "thread_state.h" -#include "gc/gc_cause.h" -#include "gc/scoped_gc_critical_section.h" -#include "scoped_thread_state_change-inl.h" namespace art { diff --git a/test/647-jni-get-field-id/get_field_id.cc b/test/647-jni-get-field-id/get_field_id.cc index 2056cfb549..139e4b6d8b 100644 --- a/test/647-jni-get-field-id/get_field_id.cc +++ b/test/647-jni-get-field-id/get_field_id.cc @@ -16,7 +16,7 @@ #include "jni.h" -#include "ScopedUtfChars.h" +#include "nativehelper/ScopedUtfChars.h" namespace art { diff --git a/test/660-clinit/expected.txt b/test/660-clinit/expected.txt new file mode 100644 index 0000000000..e103a2c6a5 --- /dev/null +++ b/test/660-clinit/expected.txt @@ -0,0 +1,9 @@ +JNI_OnLoad called +X: 4950 +Y: 5730 +str: Hello World! +ooo: OoooooO +Z: 11206655 +A: 100 +AA: 100 +a != 101 diff --git a/test/660-clinit/info.txt b/test/660-clinit/info.txt new file mode 100644 index 0000000000..da0193de0b --- /dev/null +++ b/test/660-clinit/info.txt @@ -0,0 +1 @@ +Tests that class initializers are executed correctly. diff --git a/test/660-clinit/src/Main.java b/test/660-clinit/src/Main.java new file mode 100644 index 0000000000..f5476925a0 --- /dev/null +++ b/test/660-clinit/src/Main.java @@ -0,0 +1,115 @@ +/* + * Copyright 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import java.util.*; + +public class Main { + + public static void main(String[] args) throws Exception { + System.loadLibrary(args[0]); + + if (!checkAppImageLoaded()) { + System.out.println("AppImage not loaded."); + } + + expectNotPreInit(Day.class); + expectNotPreInit(ClInit.class); + + ClInit c = new ClInit(); + int aa = c.a; + + System.out.println("X: " + c.getX()); + System.out.println("Y: " + c.getY()); + System.out.println("str: " + c.str); + System.out.println("ooo: " + c.ooo); + System.out.println("Z: " + c.getZ()); + System.out.println("A: " + c.getA()); + System.out.println("AA: " + aa); + + if (c.a != 101) { + System.out.println("a != 101"); + } + + return; + } + + static void expectPreInit(Class<?> klass) { + if (checkInitialized(klass) == false) { + System.out.println(klass.getName() + " should be initialized!"); + } + } + + static void expectNotPreInit(Class<?> klass) { + if (checkInitialized(klass) == true) { + System.out.println(klass.getName() + " should not be initialized!"); + } + } + + public static native boolean checkAppImageLoaded(); + public static native boolean checkAppImageContains(Class<?> klass); + public static native boolean checkInitialized(Class<?> klass); +} + +enum Day { + SUNDAY, MONDAY, TUESDAY, WEDNESDAY, + THURSDAY, FRIDAY, SATURDAY +} + +class ClInit { + + static String ooo = "OoooooO"; + static String str; + static int z; + static int x, y; + public static volatile int a = 100; + + static { + StringBuilder sb = new StringBuilder(); + sb.append("Hello "); + sb.append("World!"); + str = sb.toString(); + + z = 0xFF; + z += 0xFF00; + z += 0xAA0000; + + for(int i = 0; i < 100; i++) { + x += i; + } + + y = x; + for(int i = 0; i < 40; i++) { + y += i; + } + } + + int getX() { + return x; + } + + int getZ() { + return z; + } + + int getY() { + return y; + } + + int getA() { + return a; + } +} + diff --git a/test/660-store-8-16/expected.txt b/test/660-store-8-16/expected.txt new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/test/660-store-8-16/expected.txt diff --git a/test/660-store-8-16/info.txt b/test/660-store-8-16/info.txt new file mode 100644 index 0000000000..aad6c56142 --- /dev/null +++ b/test/660-store-8-16/info.txt @@ -0,0 +1,3 @@ +Regression test for the compiler whose x86 and x64 backends +used to crash on 8bits / 16bits immediate stores when the Java +input was a wide immediate. diff --git a/test/660-store-8-16/smali/TestCase.smali b/test/660-store-8-16/smali/TestCase.smali new file mode 100644 index 0000000000..ec8cbd8ae2 --- /dev/null +++ b/test/660-store-8-16/smali/TestCase.smali @@ -0,0 +1,102 @@ +# Copyright (C) 2017 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +.class public LTestCase; + +.super Ljava/lang/Object; + +.method public static setByteArray([B)V + .registers 3 + const/16 v0, 0x0 + const/16 v1, 0x0101 + aput-byte v1, p0, v0 + return-void +.end method + +.method public static setByteStaticField()V + .registers 1 + const/16 v0, 0x0101 + sput-byte v0, LTestCase;->staticByteField:B + return-void +.end method + +.method public static setByteInstanceField(LTestCase;)V + .registers 2 + const/16 v0, 0x0101 + iput-byte v0, p0, LTestCase;->instanceByteField:B + return-void +.end method + +.method public constructor <init>()V + .registers 1 + invoke-direct {p0}, Ljava/lang/Object;-><init>()V + return-void +.end method + +.method public static setShortArray([S)V + .registers 3 + const/16 v0, 0x0 + const v1, 0x10101 + aput-short v1, p0, v0 + return-void +.end method + +.method public static setShortStaticField()V + .registers 1 + const v0, 0x10101 + sput-short v0, LTestCase;->staticShortField:S + return-void +.end method + +.method public static setShortInstanceField(LTestCase;)V + .registers 2 + const v0, 0x10101 + iput-short v0, p0, LTestCase;->instanceShortField:S + return-void +.end method + +.method public static setCharArray([C)V + .registers 3 + const/16 v0, 0x0 + const v1, 0x10101 + aput-char v1, p0, v0 + return-void +.end method + +.method public static setCharStaticField()V + .registers 1 + const v0, 0x10101 + sput-char v0, LTestCase;->staticCharField:C + return-void +.end method + +.method public static setCharInstanceField(LTestCase;)V + .registers 2 + const v0, 0x10101 + iput-char v0, p0, LTestCase;->instanceCharField:C + return-void +.end method + +.method public constructor <init>()V + .registers 1 + invoke-direct {p0}, Ljava/lang/Object;-><init>()V + return-void +.end method + +.field public static staticByteField:B +.field public instanceByteField:B +.field public static staticShortField:S +.field public instanceShortField:S +.field public static staticCharField:C +.field public instanceCharField:C diff --git a/test/660-store-8-16/src/Main.java b/test/660-store-8-16/src/Main.java new file mode 100644 index 0000000000..32b25682e3 --- /dev/null +++ b/test/660-store-8-16/src/Main.java @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +public class Main { + public static void expectEquals(int expected, int actual) { + if (expected != actual) { + throw new Error("Expected " + expected + ", got " + actual); + } + } + + public static void main(String[] unused) throws Exception { + Class<?> cls = Class.forName("TestCase"); + + cls.getMethod("setByteStaticField").invoke(null); + expectEquals(1, cls.getField("staticByteField").getByte(null)); + + cls.getMethod("setShortStaticField").invoke(null); + expectEquals(0x101, cls.getField("staticShortField").getShort(null)); + + cls.getMethod("setCharStaticField").invoke(null); + expectEquals(0x101, cls.getField("staticCharField").getChar(null)); + + { + Object[] args = { new byte[2] }; + cls.getMethod("setByteArray", byte[].class).invoke(null, args); + expectEquals(1, ((byte[])args[0])[0]); + } + { + Object[] args = { new short[2] }; + cls.getMethod("setShortArray", short[].class).invoke(null, args); + expectEquals(0x101, ((short[])args[0])[0]); + } + { + Object[] args = { new char[2] }; + cls.getMethod("setCharArray", char[].class).invoke(null, args); + expectEquals(0x101, ((char[])args[0])[0]); + } + { + Object[] args = { cls.newInstance() }; + + cls.getMethod("setByteInstanceField", cls).invoke(null, args); + expectEquals(1, cls.getField("staticByteField").getByte(args[0])); + + cls.getMethod("setShortInstanceField", cls).invoke(null, args); + expectEquals(0x101, cls.getField("staticShortField").getShort(args[0])); + + cls.getMethod("setCharInstanceField", cls).invoke(null, args); + expectEquals(0x101, cls.getField("staticCharField").getChar(args[0])); + } + } +} diff --git a/test/901-hello-ti-agent/basics.cc b/test/901-hello-ti-agent/basics.cc index 21dcf98ba7..2edd91eb66 100644 --- a/test/901-hello-ti-agent/basics.cc +++ b/test/901-hello-ti-agent/basics.cc @@ -127,7 +127,7 @@ jint OnLoad(JavaVM* vm, printf("Unable to get jvmti env!\n"); return 1; } - SetAllCapabilities(jvmti_env); + SetStandardCapabilities(jvmti_env); jvmtiPhase current_phase; jvmtiError phase_result = jvmti_env->GetPhase(¤t_phase); diff --git a/test/903-hello-tagging/tagging.cc b/test/903-hello-tagging/tagging.cc index a2694e773b..e0a0136167 100644 --- a/test/903-hello-tagging/tagging.cc +++ b/test/903-hello-tagging/tagging.cc @@ -14,18 +14,18 @@ * limitations under the License. */ -#include <iostream> #include <pthread.h> -#include <stdio.h> + +#include <cstdio> +#include <iostream> #include <vector> #include "android-base/logging.h" #include "jni.h" +#include "jvmti.h" #include "scoped_local_ref.h" #include "scoped_primitive_array.h" -#include "jvmti.h" - // Test infrastructure #include "jvmti_helper.h" #include "test_env.h" diff --git a/test/904-object-allocation/tracking.cc b/test/904-object-allocation/tracking.cc index 20b53281a2..9d2592a675 100644 --- a/test/904-object-allocation/tracking.cc +++ b/test/904-object-allocation/tracking.cc @@ -14,10 +14,11 @@ * limitations under the License. */ +#include <pthread.h> + +#include <cstdio> #include <iostream> #include <mutex> -#include <pthread.h> -#include <stdio.h> #include <vector> #include "android-base/logging.h" diff --git a/test/905-object-free/tracking_free.cc b/test/905-object-free/tracking_free.cc index 998194a611..bf86c9aa25 100644 --- a/test/905-object-free/tracking_free.cc +++ b/test/905-object-free/tracking_free.cc @@ -14,9 +14,10 @@ * limitations under the License. */ -#include <iostream> #include <pthread.h> -#include <stdio.h> + +#include <cstdio> +#include <iostream> #include <vector> #include "android-base/logging.h" @@ -61,7 +62,7 @@ extern "C" JNIEXPORT void JNICALL Java_art_Test905_setupObjectFreeCallback( JavaVM* jvm = nullptr; env->GetJavaVM(&jvm); CHECK_EQ(jvm->GetEnv(reinterpret_cast<void**>(&jvmti_env2), JVMTI_VERSION_1_2), 0); - SetAllCapabilities(jvmti_env2); + SetStandardCapabilities(jvmti_env2); setupObjectFreeCallback(env, jvmti_env2, ObjectFree2); } diff --git a/test/906-iterate-heap/iterate_heap.cc b/test/906-iterate-heap/iterate_heap.cc index 02ac69942a..57c0274557 100644 --- a/test/906-iterate-heap/iterate_heap.cc +++ b/test/906-iterate-heap/iterate_heap.cc @@ -14,13 +14,13 @@ * limitations under the License. */ -#include "inttypes.h" +#include <inttypes.h> +#include <pthread.h> +#include <cstdio> #include <iomanip> #include <iostream> -#include <pthread.h> #include <sstream> -#include <stdio.h> #include <vector> #include "android-base/logging.h" diff --git a/test/907-get-loaded-classes/get_loaded_classes.cc b/test/907-get-loaded-classes/get_loaded_classes.cc index 1eadf15fc1..87c98e186e 100644 --- a/test/907-get-loaded-classes/get_loaded_classes.cc +++ b/test/907-get-loaded-classes/get_loaded_classes.cc @@ -14,9 +14,10 @@ * limitations under the License. */ -#include <iostream> #include <pthread.h> -#include <stdio.h> + +#include <cstdio> +#include <iostream> #include <vector> #include "android-base/macros.h" diff --git a/test/911-get-stack-trace/stack_trace.cc b/test/911-get-stack-trace/stack_trace.cc index 985120ceb2..2b620b1fc0 100644 --- a/test/911-get-stack-trace/stack_trace.cc +++ b/test/911-get-stack-trace/stack_trace.cc @@ -15,8 +15,9 @@ */ #include <inttypes.h> + +#include <cstdio> #include <memory> -#include <stdio.h> #include "android-base/logging.h" #include "android-base/stringprintf.h" diff --git a/test/913-heaps/heaps.cc b/test/913-heaps/heaps.cc index bf3f7b66a5..f7862c777d 100644 --- a/test/913-heaps/heaps.cc +++ b/test/913-heaps/heaps.cc @@ -15,15 +15,15 @@ */ #include <inttypes.h> -#include <stdio.h> -#include <string.h> +#include <cstdio> +#include <cstring> #include <iostream> #include <sstream> #include <vector> -#include "android-base/macros.h" #include "android-base/logging.h" +#include "android-base/macros.h" #include "android-base/stringprintf.h" #include "jni.h" diff --git a/test/921-hello-failure/expected.txt b/test/921-hello-failure/expected.txt index fdbfbe2191..f36d1a3dd5 100644 --- a/test/921-hello-failure/expected.txt +++ b/test/921-hello-failure/expected.txt @@ -53,3 +53,6 @@ hello there again - FieldChange hello - Unmodifiable Transformation error : java.lang.Exception(Failed to redefine class <[LTransform;> due to JVMTI_ERROR_UNMODIFIABLE_CLASS) hello - Unmodifiable +hello - Undefault +Transformation error : java.lang.Exception(Failed to redefine class <LTransform5;> due to JVMTI_ERROR_UNSUPPORTED_REDEFINITION_METHOD_ADDED) +hello - Undefault diff --git a/test/921-hello-failure/src/Iface4.java b/test/921-hello-failure/src/Iface4.java new file mode 100644 index 0000000000..66804c2258 --- /dev/null +++ b/test/921-hello-failure/src/Iface4.java @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +interface Iface4 { + default void sayHiTwice(String s) { + sayHi(s); + sayHi(s); + } + void sayHi(String s); +} diff --git a/test/921-hello-failure/src/Main.java b/test/921-hello-failure/src/Main.java index cfdcdc250f..fb481bd9c2 100644 --- a/test/921-hello-failure/src/Main.java +++ b/test/921-hello-failure/src/Main.java @@ -35,6 +35,7 @@ public class Main { MissingField.doTest(new Transform4("there")); FieldChange.doTest(new Transform4("there again")); Unmodifiable.doTest(new Transform[] { new Transform(), }); + Undefault.doTest(new Transform5()); } // TODO Replace this shim with a better re-write of this test. diff --git a/test/921-hello-failure/src/Transform5.java b/test/921-hello-failure/src/Transform5.java new file mode 100644 index 0000000000..cf7b20a7e2 --- /dev/null +++ b/test/921-hello-failure/src/Transform5.java @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +class Transform5 implements Iface4 { + public void sayHi(String name) { + System.out.println("hello - " + name); + } +} diff --git a/test/921-hello-failure/src/Undefault.java b/test/921-hello-failure/src/Undefault.java new file mode 100644 index 0000000000..8303a84b68 --- /dev/null +++ b/test/921-hello-failure/src/Undefault.java @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import java.util.Base64; + +class Undefault { + // The following is a base64 encoding of the following class. + // class Transform5 implements Iface4 { + // public void sayHiTwice(String s) { + // throw new Error("Should not be called"); + // } + // public void sayHi(String name) { + // throw new Error("Should not be called!"); + // } + // } + private static final byte[] CLASS_BYTES = Base64.getDecoder().decode( + "yv66vgAAADQAGgoABwASBwATCAAUCgACABUIABYHABcHABgHABkBAAY8aW5pdD4BAAMoKVYBAARD" + + "b2RlAQAPTGluZU51bWJlclRhYmxlAQAKc2F5SGlUd2ljZQEAFShMamF2YS9sYW5nL1N0cmluZzsp" + + "VgEABXNheUhpAQAKU291cmNlRmlsZQEAD1RyYW5zZm9ybTUuamF2YQwACQAKAQAPamF2YS9sYW5n" + + "L0Vycm9yAQAUU2hvdWxkIG5vdCBiZSBjYWxsZWQMAAkADgEAFVNob3VsZCBub3QgYmUgY2FsbGVk" + + "IQEAClRyYW5zZm9ybTUBABBqYXZhL2xhbmcvT2JqZWN0AQAGSWZhY2U0ACAABgAHAAEACAAAAAMA" + + "AAAJAAoAAQALAAAAHQABAAEAAAAFKrcAAbEAAAABAAwAAAAGAAEAAAABAAEADQAOAAEACwAAACIA" + + "AwACAAAACrsAAlkSA7cABL8AAAABAAwAAAAGAAEAAAADAAEADwAOAAEACwAAACIAAwACAAAACrsA" + + "AlkSBbcABL8AAAABAAwAAAAGAAEAAAAGAAEAEAAAAAIAEQ=="); + private static final byte[] DEX_BYTES = Base64.getDecoder().decode( + "ZGV4CjAzNQD5XbJiwMAcY0cucJ5gcVhFu7tMG0dZX8PsAgAAcAAAAHhWNBIAAAAAAAAAAFgCAAAN" + + "AAAAcAAAAAYAAACkAAAAAgAAALwAAAAAAAAAAAAAAAUAAADUAAAAAQAAAPwAAADQAQAAHAEAAIIB" + + "AACKAQAAlAEAAKIBAAC1AQAAyQEAAN0BAADzAQAACgIAABsCAAAeAgAAIgIAACkCAAABAAAAAgAA" + + "AAMAAAAEAAAABQAAAAkAAAAJAAAABQAAAAAAAAAKAAAABQAAAHwBAAABAAAAAAAAAAEAAQALAAAA" + + "AQABAAwAAAACAAEAAAAAAAMAAAAAAAAAAQAAAAAAAAADAAAAdAEAAAgAAAAAAAAARgIAAAAAAAAB" + + "AAEAAQAAADUCAAAEAAAAcBAEAAAADgAEAAIAAgAAADoCAAAIAAAAIgACABoBBwBwIAMAEAAnAAQA" + + "AgACAAAAQAIAAAgAAAAiAAIAGgEGAHAgAwAQACcAAQAAAAAAAAABAAAABAAGPGluaXQ+AAhMSWZh" + + "Y2U0OwAMTFRyYW5zZm9ybTU7ABFMamF2YS9sYW5nL0Vycm9yOwASTGphdmEvbGFuZy9PYmplY3Q7" + + "ABJMamF2YS9sYW5nL1N0cmluZzsAFFNob3VsZCBub3QgYmUgY2FsbGVkABVTaG91bGQgbm90IGJl" + + "IGNhbGxlZCEAD1RyYW5zZm9ybTUuamF2YQABVgACVkwABXNheUhpAApzYXlIaVR3aWNlAAEABw4A" + + "BgEABw4AAwEABw4AAAABAgCAgAScAgEBtAIBAdQCDAAAAAAAAAABAAAAAAAAAAEAAAANAAAAcAAA" + + "AAIAAAAGAAAApAAAAAMAAAACAAAAvAAAAAUAAAAFAAAA1AAAAAYAAAABAAAA/AAAAAEgAAADAAAA" + + "HAEAAAEQAAACAAAAdAEAAAIgAAANAAAAggEAAAMgAAADAAAANQIAAAAgAAABAAAARgIAAAAQAAAB" + + "AAAAWAIAAA=="); + + public static void doTest(Transform5 t) { + t.sayHi("Undefault"); + try { + Main.doCommonClassRedefinition(Transform5.class, CLASS_BYTES, DEX_BYTES); + } catch (Exception e) { + System.out.println( + "Transformation error : " + e.getClass().getName() + "(" + e.getMessage() + ")"); + } + t.sayHi("Undefault"); + } +} diff --git a/test/924-threads/src/art/Test924.java b/test/924-threads/src/art/Test924.java index 84b7c62264..b73eb30888 100644 --- a/test/924-threads/src/art/Test924.java +++ b/test/924-threads/src/art/Test924.java @@ -164,8 +164,10 @@ public class Test924 { do { Thread.yield(); } while (t.getState() != Thread.State.BLOCKED); - Thread.sleep(10); - printThreadState(t); + // Since internal thread suspension (For GC or other cases) can happen at any time and changes + // the thread state we just have it print the majority thread state across 11 calls over 55 + // milliseconds. + printMajorityThreadState(t, 11, 5); } // Sleeping. @@ -357,10 +359,32 @@ public class Test924 { STATE_KEYS.addAll(STATE_NAMES.keySet()); Collections.sort(STATE_KEYS); } - + + // Call getThreadState 'votes' times waiting 'wait' millis between calls and print the most common + // result. + private static void printMajorityThreadState(Thread t, int votes, int wait) throws Exception { + Map<Integer, Integer> states = new HashMap<>(); + for (int i = 0; i < votes; i++) { + int cur_state = getThreadState(t); + states.put(cur_state, states.getOrDefault(cur_state, 0) + 1); + Thread.sleep(wait); // Wait a little bit. + } + int best_state = -1; + int highest_count = 0; + for (Map.Entry<Integer, Integer> e : states.entrySet()) { + if (e.getValue() > highest_count) { + highest_count = e.getValue(); + best_state = e.getKey(); + } + } + printThreadState(best_state); + } + private static void printThreadState(Thread t) { - int state = getThreadState(t); + printThreadState(getThreadState(t)); + } + private static void printThreadState(int state) { StringBuilder sb = new StringBuilder(); for (Integer i : STATE_KEYS) { diff --git a/test/933-misc-events/misc_events.cc b/test/933-misc-events/misc_events.cc index 27dab8b31f..d2ae0f4cc1 100644 --- a/test/933-misc-events/misc_events.cc +++ b/test/933-misc-events/misc_events.cc @@ -14,10 +14,11 @@ * limitations under the License. */ -#include <atomic> #include <signal.h> #include <sys/types.h> +#include <atomic> + #include "android-base/logging.h" #include "android-base/macros.h" #include "jni.h" diff --git a/test/936-search-onload/search_onload.cc b/test/936-search-onload/search_onload.cc index b2ef05690f..90d87e0e7b 100644 --- a/test/936-search-onload/search_onload.cc +++ b/test/936-search-onload/search_onload.cc @@ -39,7 +39,7 @@ jint OnLoad(JavaVM* vm, printf("Unable to get jvmti env!\n"); return 1; } - SetAllCapabilities(jvmti_env); + SetStandardCapabilities(jvmti_env); char* dex_loc = getenv("DEX_LOCATION"); std::string dex1 = android::base::StringPrintf("%s/936-search-onload.jar", dex_loc); diff --git a/test/945-obsolete-native/obsolete_native.cc b/test/945-obsolete-native/obsolete_native.cc index e3090f5906..418ce90637 100644 --- a/test/945-obsolete-native/obsolete_native.cc +++ b/test/945-obsolete-native/obsolete_native.cc @@ -15,8 +15,9 @@ */ #include <inttypes.h> + +#include <cstdio> #include <memory> -#include <stdio.h> #include "android-base/stringprintf.h" #include "jni.h" @@ -24,8 +25,8 @@ // Test infrastructure #include "jni_binder.h" -#include "test_env.h" #include "scoped_local_ref.h" +#include "test_env.h" namespace art { namespace Test945ObsoleteNative { diff --git a/test/983-source-transform-verify/source_transform.cc b/test/983-source-transform-verify/source_transform.cc index a433dc9b75..570ade364d 100644 --- a/test/983-source-transform-verify/source_transform.cc +++ b/test/983-source-transform-verify/source_transform.cc @@ -15,13 +15,15 @@ */ #include <inttypes.h> -#include <stdio.h> -#include <string.h> +#include <cstdio> +#include <cstring> #include <iostream> #include <vector> #include "android-base/stringprintf.h" +#include "jni.h" +#include "jvmti.h" #include "base/logging.h" #include "base/macros.h" @@ -29,9 +31,7 @@ #include "dex_file.h" #include "dex_instruction.h" #include "jit/jit.h" -#include "jni.h" #include "native_stack_dump.h" -#include "jvmti.h" #include "runtime.h" #include "scoped_thread_state_change-inl.h" #include "thread-current-inl.h" @@ -111,7 +111,7 @@ jint OnLoad(JavaVM* vm, printf("Unable to get jvmti env!\n"); return 1; } - SetAllCapabilities(jvmti_env); + SetStandardCapabilities(jvmti_env); jvmtiEventCallbacks cb; memset(&cb, 0, sizeof(cb)); cb.ClassFileLoadHook = CheckDexFileHook; diff --git a/test/986-native-method-bind/native_bind.cc b/test/986-native-method-bind/native_bind.cc index eec635b2a0..1afe4dbaa2 100644 --- a/test/986-native-method-bind/native_bind.cc +++ b/test/986-native-method-bind/native_bind.cc @@ -14,10 +14,11 @@ * limitations under the License. */ +#include <dlfcn.h> #include <inttypes.h> + +#include <cstdio> #include <memory> -#include <stdio.h> -#include <dlfcn.h> #include "android-base/stringprintf.h" #include "jni.h" @@ -26,8 +27,8 @@ // Test infrastructure #include "jni_binder.h" #include "jvmti_helper.h" -#include "test_env.h" #include "scoped_local_ref.h" +#include "test_env.h" namespace art { namespace Test986NativeBind { diff --git a/test/987-agent-bind/agent_bind.cc b/test/987-agent-bind/agent_bind.cc index 44366c1054..7dbdd8e29f 100644 --- a/test/987-agent-bind/agent_bind.cc +++ b/test/987-agent-bind/agent_bind.cc @@ -14,10 +14,11 @@ * limitations under the License. */ +#include <dlfcn.h> #include <inttypes.h> + +#include <cstdio> #include <memory> -#include <stdio.h> -#include <dlfcn.h> #include "android-base/stringprintf.h" #include "jni.h" @@ -26,8 +27,8 @@ // Test infrastructure #include "jni_binder.h" #include "jvmti_helper.h" -#include "test_env.h" #include "scoped_local_ref.h" +#include "test_env.h" namespace art { namespace Test987AgentBind { diff --git a/test/989-method-trace-throw/method_trace.cc b/test/989-method-trace-throw/method_trace.cc index 554784effe..019b6a9a24 100644 --- a/test/989-method-trace-throw/method_trace.cc +++ b/test/989-method-trace-throw/method_trace.cc @@ -15,8 +15,9 @@ */ #include <inttypes.h> + +#include <cstdio> #include <memory> -#include <stdio.h> #include "android-base/logging.h" #include "android-base/stringprintf.h" diff --git a/test/992-source-data/source_file.cc b/test/992-source-data/source_file.cc index 3e8989e403..46d197d048 100644 --- a/test/992-source-data/source_file.cc +++ b/test/992-source-data/source_file.cc @@ -15,8 +15,9 @@ */ #include <inttypes.h> + +#include <cstdio> #include <memory> -#include <stdio.h> #include "android-base/logging.h" #include "android-base/stringprintf.h" diff --git a/test/993-breakpoints/breakpoints.cc b/test/993-breakpoints/breakpoints.cc index 129207098d..3734ce8634 100644 --- a/test/993-breakpoints/breakpoints.cc +++ b/test/993-breakpoints/breakpoints.cc @@ -15,8 +15,9 @@ */ #include <inttypes.h> + +#include <cstdio> #include <memory> -#include <stdio.h> #include "android-base/logging.h" #include "android-base/stringprintf.h" diff --git a/test/996-breakpoint-obsolete/obsolete_breakpoints.cc b/test/996-breakpoint-obsolete/obsolete_breakpoints.cc index b6a67e4a08..820af47f4c 100644 --- a/test/996-breakpoint-obsolete/obsolete_breakpoints.cc +++ b/test/996-breakpoint-obsolete/obsolete_breakpoints.cc @@ -15,8 +15,9 @@ */ #include <inttypes.h> + +#include <cstdio> #include <memory> -#include <stdio.h> #include "android-base/logging.h" #include "android-base/stringprintf.h" diff --git a/test/VerifierDeps/Main.smali b/test/VerifierDeps/Main.smali index 74c0d037be..824f0dc848 100644 --- a/test/VerifierDeps/Main.smali +++ b/test/VerifierDeps/Main.smali @@ -405,12 +405,6 @@ return-void .end method -.method public static InvokeVirtual_ActuallyDirect(LMyThread;)V - .registers 1 - invoke-virtual {p0}, LMyThread;->activeCount()I - return-void -.end method - .method public static InvokeInterface_Resolved_DeclaredInReferenced(LMyThread;)V .registers 1 invoke-interface {p0}, Ljava/lang/Runnable;->run()V @@ -420,7 +414,9 @@ .method public static InvokeInterface_Resolved_DeclaredInSuperclass(LMyThread;)V .registers 1 # Method join() is declared in the superclass of MyThread. As such, it should - # be called with invoke-virtual and will not be resolved here. + # be called with invoke-virtual. However, the lookup type does not depend + # on the invoke type, so it shall be resolved here anyway. + # TODO: Maybe we should not record dependency if the invoke type does not match the lookup type. invoke-interface {p0}, LMyThread;->join()V return-void .end method @@ -428,6 +424,8 @@ .method public static InvokeInterface_Resolved_DeclaredInSuperinterface1(LMyThreadSet;)V .registers 1 # Verification will fail because the referring class is not an interface. + # However, the lookup type does not depend on the invoke type, so it shall be resolved here anyway. + # TODO: Maybe we should not record dependency if the invoke type does not match the lookup type. invoke-interface {p0}, LMyThreadSet;->run()V return-void .end method diff --git a/test/common/runtime_state.cc b/test/common/runtime_state.cc index d8e5b571bd..7c0ed691b6 100644 --- a/test/common/runtime_state.cc +++ b/test/common/runtime_state.cc @@ -25,10 +25,10 @@ #include "jit/jit_code_cache.h" #include "jit/profiling_info.h" #include "mirror/class-inl.h" +#include "nativehelper/ScopedUtfChars.h" #include "oat_quick_method_header.h" #include "runtime.h" #include "scoped_thread_state_change-inl.h" -#include "ScopedUtfChars.h" #include "thread-current-inl.h" namespace art { diff --git a/test/knownfailures.json b/test/knownfailures.json index 3edb0a8a45..09e76fa8ea 100644 --- a/test/knownfailures.json +++ b/test/knownfailures.json @@ -600,15 +600,6 @@ }, { "tests": [ - "567-checker-compare", - "988-method-trace" - ], - "description": "Checker tests fail because desugar lowers Long.compare to lcmp", - "env_vars": {"ANDROID_COMPILE_WITH_JACK": "false"}, - "bug": "b/63078894" - }, - { - "tests": [ "536-checker-needs-access-check", "537-checker-inline-and-unverified", "569-checker-pattern-replacement", @@ -721,5 +712,11 @@ "lookup changes" ], "bug": "b/63089991", "env_vars": {"ANDROID_COMPILE_WITH_JACK": "false"} + }, + { + "tests": "660-clinit", + "variant": "no-image | no-dex2oat | no-prebuild", + "description": ["Tests <clinit> for app images, which --no-image, --no-prebuild and", + "--no-dex2oat do not create"] } ] diff --git a/test/testrunner/env.py b/test/testrunner/env.py index a0c4ea8e42..b996b04235 100644 --- a/test/testrunner/env.py +++ b/test/testrunner/env.py @@ -32,7 +32,8 @@ _DUMP_MANY_VARS_LIST = ['HOST_2ND_ARCH_PREFIX', 'TARGET_2ND_ARCH', 'TARGET_ARCH', 'HOST_PREFER_32_BIT', - 'HOST_OUT_EXECUTABLES'] + 'HOST_OUT_EXECUTABLES', + 'ANDROID_JAVA_TOOLCHAIN'] _DUMP_MANY_VARS = None # To be set to a dictionary with above list being the keys, # and the build variable being the value. def _dump_many_vars(var_name): @@ -233,8 +234,14 @@ else: HOST_OUT_EXECUTABLES = os.path.join(ANDROID_BUILD_TOP, _get_build_var("HOST_OUT_EXECUTABLES")) -os.environ['JACK'] = HOST_OUT_EXECUTABLES + '/jack' -os.environ['DX'] = HOST_OUT_EXECUTABLES + '/dx' -os.environ['SMALI'] = HOST_OUT_EXECUTABLES + '/smali' -os.environ['JASMIN'] = HOST_OUT_EXECUTABLES + '/jasmin' -os.environ['DXMERGER'] = HOST_OUT_EXECUTABLES + '/dexmerger' + +# Set up default values for $JACK, $DX, $SMALI, etc to the $HOST_OUT_EXECUTABLES/$name path. +for tool in ['jack', 'dx', 'smali', 'jasmin', 'dxmerger']: + binary = tool if tool != 'dxmerger' else 'dexmerger' + os.environ.setdefault(tool.upper(), HOST_OUT_EXECUTABLES + '/' + binary) + +ANDROID_JAVA_TOOLCHAIN = os.path.join(ANDROID_BUILD_TOP, + _get_build_var('ANDROID_JAVA_TOOLCHAIN')) + +# include platform prebuilt java, javac, etc in $PATH. +os.environ['PATH'] = ANDROID_JAVA_TOOLCHAIN + ':' + os.environ['PATH'] diff --git a/test/testrunner/target_config.py b/test/testrunner/target_config.py index baf7600349..e8b6f1ce33 100644 --- a/test/testrunner/target_config.py +++ b/test/testrunner/target_config.py @@ -303,7 +303,8 @@ target_config = { } }, 'art-gtest-valgrind32': { - 'make' : 'valgrind-test-art-host32', + # Disabled: x86 valgrind does not understand SSE4.x + # 'make' : 'valgrind-test-art-host32', 'env': { 'ART_USE_READ_BARRIER' : 'false' } diff --git a/test/testrunner/testrunner.py b/test/testrunner/testrunner.py index 2c64b566b8..68e1856adb 100755 --- a/test/testrunner/testrunner.py +++ b/test/testrunner/testrunner.py @@ -50,7 +50,6 @@ import itertools import json import multiprocessing import os -import operator import re import subprocess import sys @@ -76,11 +75,9 @@ ADDRESS_SIZES = set() OPTIMIZING_COMPILER_TYPES = set() JVMTI_TYPES = set() ADDRESS_SIZES_TARGET = {'host': set(), 'target': set()} -TIME_STATS = {} # timeout for individual tests. # TODO: make it adjustable per tests and for buildbots timeout = 3000 # 50 minutes -global_timeout = 14100 # 235 minutes (The go/ab timeout is 14500) # DISABLED_TEST_CONTAINER holds information about the disabled tests. It is a map # that has key as the test name (like 001-HelloWorld), and value as set of @@ -358,7 +355,7 @@ def run_tests(tests): # stops creating any any thread and wait for all the exising threads # to end. while threading.active_count() > 2: - time.sleep(1) + time.sleep(0.1) return test_name = 'test-art-' test_name += target + '-run-test-' @@ -509,13 +506,11 @@ def run_test(command, test, test_variant, test_name): test_skipped = True else: test_skipped = False - start_recording_time(test_name) if gdb: proc = subprocess.Popen(command.split(), stderr=subprocess.STDOUT, universal_newlines=True) else: proc = subprocess.Popen(command.split(), stderr=subprocess.STDOUT, stdout = subprocess.PIPE, universal_newlines=True) - stop_recording_time(test_name) script_output = proc.communicate(timeout=timeout)[0] test_passed = not proc.wait() @@ -734,7 +729,6 @@ def print_text(output): sys.stdout.flush() def print_analysis(): - print_mutex.acquire() if not verbose: # Without --verbose, the testrunner erases passing test info. It # does that by overriding the printed text with white spaces all across @@ -768,7 +762,6 @@ def print_analysis(): print_text(COLOR_ERROR + '----------' + COLOR_NORMAL + '\n') for failed_test in sorted([test_info[0] for test_info in failed_tests]): print_text(('%s\n' % (failed_test))) - print_mutex.release() def parse_test_name(test_name): @@ -997,33 +990,7 @@ def parse_option(): return test -def start_recording_time(key): - """To begin recording time for the event associated with the key. - """ - TIME_STATS[key] = -(time.time()) - -def stop_recording_time(key): - """To stop timer for the event associated with the key. - """ - TIME_STATS[key] = time.time() + TIME_STATS[key] - -def print_time_info(): - """Print time information for different invocation. - """ - print_mutex.acquire() - print_text('\nTIME INFO\n') - for key in TIME_STATS: - # Handle unfinised jobs. - if TIME_STATS[key] < 0: - TIME_STATS[key] = time.time() + TIME_STATS[key] - - info_list = sorted(TIME_STATS.items(), key=operator.itemgetter(1), reverse=True) - for time_info_tuple in info_list: - print_text('%s : %.2f sec\n' % (time_info_tuple[0], time_info_tuple[1])) - print_mutex.release() - def main(): - start_time = time.time() gather_test_info() user_requested_test = parse_option() setup_test_env() @@ -1039,10 +1006,8 @@ def main(): build_command += ' ' + build_targets # Add 'dist' to avoid Jack issues b/36169180. build_command += ' dist' - start_recording_time(build_command) if subprocess.call(build_command.split()): sys.exit(1) - stop_recording_time(build_command) if user_requested_test: test_runner_thread = threading.Thread(target=run_tests, args=(user_requested_test,)) else: @@ -1051,13 +1016,6 @@ def main(): try: test_runner_thread.start() while threading.active_count() > 1: - if (time.time() - start_time > global_timeout): - # to ensure that the run ends before the go/ab bots - # time out the invocation. - print_text("FAILED: timeout reached") - print_time_info() - print_analysis() - sys.exit(1) time.sleep(0.1) print_analysis() except Exception as e: diff --git a/test/ti-agent/common_load.cc b/test/ti-agent/common_load.cc index 0679c1bc17..1d13c620e3 100644 --- a/test/ti-agent/common_load.cc +++ b/test/ti-agent/common_load.cc @@ -62,7 +62,7 @@ static jint MinimalOnLoad(JavaVM* vm, printf("Unable to get jvmti env!\n"); return 1; } - SetAllCapabilities(jvmti_env); + SetStandardCapabilities(jvmti_env); return 0; } diff --git a/test/ti-agent/jvmti_helper.cc b/test/ti-agent/jvmti_helper.cc index 598a30f940..51d3406a35 100644 --- a/test/ti-agent/jvmti_helper.cc +++ b/test/ti-agent/jvmti_helper.cc @@ -16,11 +16,12 @@ #include "jvmti_helper.h" -#include <algorithm> #include <dlfcn.h> -#include <stdio.h> + +#include <algorithm> +#include <cstdio> +#include <cstring> #include <sstream> -#include <string.h> #include "android-base/logging.h" #include "scoped_local_ref.h" @@ -38,12 +39,65 @@ void CheckJvmtiError(jvmtiEnv* env, jvmtiError error) { } } +// These are a set of capabilities we will enable in all situations. These are chosen since they +// will not affect the runtime in any significant way if they are enabled. +static const jvmtiCapabilities standard_caps = { + .can_tag_objects = 1, + .can_generate_field_modification_events = 1, + .can_generate_field_access_events = 1, + .can_get_bytecodes = 1, + .can_get_synthetic_attribute = 1, + .can_get_owned_monitor_info = 0, + .can_get_current_contended_monitor = 0, + .can_get_monitor_info = 0, + .can_pop_frame = 0, + .can_redefine_classes = 1, + .can_signal_thread = 0, + .can_get_source_file_name = 1, + .can_get_line_numbers = 1, + .can_get_source_debug_extension = 1, + .can_access_local_variables = 0, + .can_maintain_original_method_order = 0, + .can_generate_single_step_events = 1, + .can_generate_exception_events = 0, + .can_generate_frame_pop_events = 0, + .can_generate_breakpoint_events = 1, + .can_suspend = 1, + .can_redefine_any_class = 0, + .can_get_current_thread_cpu_time = 0, + .can_get_thread_cpu_time = 0, + .can_generate_method_entry_events = 1, + .can_generate_method_exit_events = 1, + .can_generate_all_class_hook_events = 0, + .can_generate_compiled_method_load_events = 0, + .can_generate_monitor_events = 0, + .can_generate_vm_object_alloc_events = 1, + .can_generate_native_method_bind_events = 1, + .can_generate_garbage_collection_events = 1, + .can_generate_object_free_events = 1, + .can_force_early_return = 0, + .can_get_owned_monitor_stack_depth_info = 0, + .can_get_constant_pool = 0, + .can_set_native_method_prefix = 0, + .can_retransform_classes = 1, + .can_retransform_any_class = 0, + .can_generate_resource_exhaustion_heap_events = 0, + .can_generate_resource_exhaustion_threads_events = 0, +}; + +jvmtiCapabilities GetStandardCapabilities() { + return standard_caps; +} + +void SetStandardCapabilities(jvmtiEnv* env) { + jvmtiCapabilities caps = GetStandardCapabilities(); + CheckJvmtiError(env, env->AddCapabilities(&caps)); +} + void SetAllCapabilities(jvmtiEnv* env) { jvmtiCapabilities caps; - jvmtiError error1 = env->GetPotentialCapabilities(&caps); - CheckJvmtiError(env, error1); - jvmtiError error2 = env->AddCapabilities(&caps); - CheckJvmtiError(env, error2); + CheckJvmtiError(env, env->GetPotentialCapabilities(&caps)); + CheckJvmtiError(env, env->AddCapabilities(&caps)); } bool JvmtiErrorToException(JNIEnv* env, jvmtiEnv* jvmti_env, jvmtiError error) { diff --git a/test/ti-agent/jvmti_helper.h b/test/ti-agent/jvmti_helper.h index 66d88d0752..78d238a980 100644 --- a/test/ti-agent/jvmti_helper.h +++ b/test/ti-agent/jvmti_helper.h @@ -17,16 +17,24 @@ #ifndef ART_TEST_TI_AGENT_JVMTI_HELPER_H_ #define ART_TEST_TI_AGENT_JVMTI_HELPER_H_ -#include "jni.h" -#include "jvmti.h" #include <memory> #include <ostream> +#include "jni.h" +#include "jvmti.h" + #include "android-base/logging.h" namespace art { +// Get a standard set of capabilities for use in tests. +jvmtiCapabilities GetStandardCapabilities(); + +// Add all the standard capabilities to the given env. +void SetStandardCapabilities(jvmtiEnv* env); + // Add all capabilities to the given env. +// TODO Remove this in the future. void SetAllCapabilities(jvmtiEnv* env); // Check whether the given error is NONE. If not, print out the corresponding error message diff --git a/test/ti-agent/redefinition_helper.cc b/test/ti-agent/redefinition_helper.cc index 3b18879ca5..76371de20a 100644 --- a/test/ti-agent/redefinition_helper.cc +++ b/test/ti-agent/redefinition_helper.cc @@ -16,9 +16,9 @@ #include "common_helper.h" +#include <cstdio> #include <deque> #include <map> -#include <stdio.h> #include <sstream> #include <string> #include <vector> @@ -332,7 +332,7 @@ extern "C" JNIEXPORT void JNICALL Java_art_Redefinition_doCommonClassRetransform "Unable to create temporary jvmtiEnv for RetransformClasses call."); return; } - SetAllCapabilities(real_env); + SetStandardCapabilities(real_env); } else { real_env = jvmti_env; } @@ -373,15 +373,14 @@ jint OnLoad(JavaVM* vm, } // namespace common_transform static void SetupCommonRedefine() { - jvmtiCapabilities caps; - jvmti_env->GetPotentialCapabilities(&caps); + jvmtiCapabilities caps = GetStandardCapabilities(); caps.can_retransform_classes = 0; caps.can_retransform_any_class = 0; jvmti_env->AddCapabilities(&caps); } static void SetupCommonRetransform() { - SetAllCapabilities(jvmti_env); + SetStandardCapabilities(jvmti_env); jvmtiEventCallbacks cb; memset(&cb, 0, sizeof(cb)); cb.ClassFileLoadHook = common_retransform::CommonClassFileLoadHookRetransformable; @@ -392,8 +391,7 @@ static void SetupCommonRetransform() { static void SetupCommonTransform() { // Don't set the retransform caps - jvmtiCapabilities caps; - jvmti_env->GetPotentialCapabilities(&caps); + jvmtiCapabilities caps = GetStandardCapabilities(); caps.can_retransform_classes = 0; caps.can_retransform_any_class = 0; jvmti_env->AddCapabilities(&caps); diff --git a/test/ti-stress/stress.cc b/test/ti-stress/stress.cc index d197acd216..06d5af0e36 100644 --- a/test/ti-stress/stress.cc +++ b/test/ti-stress/stress.cc @@ -14,18 +14,18 @@ * limitations under the License. */ -#include <jni.h> -#include <stdio.h> -#include <iostream> -#include <iomanip> +#include <cstdio> #include <fstream> +#include <iomanip> +#include <iostream> #include <memory> -#include <stdio.h> #include <sstream> #include <strstream> -#include "jvmti.h" +#include <jni.h> + #include "exec_utils.h" +#include "jvmti.h" #include "utils.h" namespace art { @@ -780,8 +780,49 @@ extern "C" JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM* vm, } // Just get all capabilities. - jvmtiCapabilities caps; - jvmti->GetPotentialCapabilities(&caps); + jvmtiCapabilities caps = { + .can_tag_objects = 0, + .can_generate_field_modification_events = 1, + .can_generate_field_access_events = 1, + .can_get_bytecodes = 0, + .can_get_synthetic_attribute = 0, + .can_get_owned_monitor_info = 0, + .can_get_current_contended_monitor = 0, + .can_get_monitor_info = 0, + .can_pop_frame = 0, + .can_redefine_classes = 1, + .can_signal_thread = 0, + .can_get_source_file_name = 1, + .can_get_line_numbers = 1, + .can_get_source_debug_extension = 0, + .can_access_local_variables = 0, + .can_maintain_original_method_order = 0, + .can_generate_single_step_events = 1, + .can_generate_exception_events = 0, + .can_generate_frame_pop_events = 0, + .can_generate_breakpoint_events = 0, + .can_suspend = 0, + .can_redefine_any_class = 0, + .can_get_current_thread_cpu_time = 0, + .can_get_thread_cpu_time = 0, + .can_generate_method_entry_events = 1, + .can_generate_method_exit_events = 1, + .can_generate_all_class_hook_events = 0, + .can_generate_compiled_method_load_events = 0, + .can_generate_monitor_events = 0, + .can_generate_vm_object_alloc_events = 0, + .can_generate_native_method_bind_events = 1, + .can_generate_garbage_collection_events = 0, + .can_generate_object_free_events = 0, + .can_force_early_return = 0, + .can_get_owned_monitor_stack_depth_info = 0, + .can_get_constant_pool = 0, + .can_set_native_method_prefix = 0, + .can_retransform_classes = 1, + .can_retransform_any_class = 0, + .can_generate_resource_exhaustion_heap_events = 0, + .can_generate_resource_exhaustion_threads_events = 0, + }; jvmti->AddCapabilities(&caps); // Set callbacks. diff --git a/tools/ahat/README.txt b/tools/ahat/README.txt index 38556abff2..4471c0a7ec 100644 --- a/tools/ahat/README.txt +++ b/tools/ahat/README.txt @@ -75,7 +75,11 @@ Things to move to perflib: * Instance.isRoot and Instance.getRootTypes. Release History: - 1.3 Pending + 1.4 Pending + + 1.3 July 25, 2017 + Improve diffing of static and instance fields. + Improve startup performance by roughly 25%. 1.2 May 26, 2017 Show registered native sizes of objects. diff --git a/tools/ahat/src/ObjectHandler.java b/tools/ahat/src/ObjectHandler.java index cc55b7a7df..8262910bf0 100644 --- a/tools/ahat/src/ObjectHandler.java +++ b/tools/ahat/src/ObjectHandler.java @@ -110,7 +110,7 @@ class ObjectHandler implements AhatHandler { private static void printClassInstanceFields(Doc doc, Query query, AhatClassInstance inst) { doc.section("Fields"); AhatInstance base = inst.getBaseline(); - printFields(doc, query, INSTANCE_FIELDS_ID, !base.isPlaceHolder(), + printFields(doc, query, INSTANCE_FIELDS_ID, inst != base && !base.isPlaceHolder(), inst.asClassInstance().getInstanceFields(), base.isPlaceHolder() ? null : base.asClassInstance().getInstanceFields()); } @@ -211,7 +211,7 @@ class ObjectHandler implements AhatHandler { doc.section("Static Fields"); AhatInstance base = clsobj.getBaseline(); - printFields(doc, query, STATIC_FIELDS_ID, !base.isPlaceHolder(), + printFields(doc, query, STATIC_FIELDS_ID, clsobj != base && !base.isPlaceHolder(), clsobj.getStaticFieldValues(), base.isPlaceHolder() ? null : base.asClassObj().getStaticFieldValues()); } diff --git a/tools/ahat/src/ObjectsHandler.java b/tools/ahat/src/ObjectsHandler.java index 86d48f1702..fd226c24bf 100644 --- a/tools/ahat/src/ObjectsHandler.java +++ b/tools/ahat/src/ObjectsHandler.java @@ -43,13 +43,7 @@ class ObjectsHandler implements AhatHandler { Site site = mSnapshot.getSite(id, depth); List<AhatInstance> insts = new ArrayList<AhatInstance>(); - for (AhatInstance inst : site.getObjects()) { - if ((heapName == null || inst.getHeap().getName().equals(heapName)) - && (className == null || inst.getClassName().equals(className))) { - insts.add(inst); - } - } - + site.getObjects(heapName, className, insts); Collections.sort(insts, Sort.defaultInstanceCompare(mSnapshot)); doc.title("Objects"); diff --git a/tools/ahat/src/Summarizer.java b/tools/ahat/src/Summarizer.java index 016eab44f2..3e9da31e96 100644 --- a/tools/ahat/src/Summarizer.java +++ b/tools/ahat/src/Summarizer.java @@ -60,14 +60,7 @@ class Summarizer { formatted.append("root "); } - // Annotate classes as classes. - DocString linkText = new DocString(); - if (inst.isClassObj()) { - linkText.append("class "); - } - - linkText.append(inst.toString()); - + DocString linkText = DocString.text(inst.toString()); if (inst.isPlaceHolder()) { // Don't make links to placeholder objects. formatted.append(linkText); diff --git a/tools/ahat/src/dominators/DominatorsComputation.java b/tools/ahat/src/dominators/DominatorsComputation.java new file mode 100644 index 0000000000..9a2a272be0 --- /dev/null +++ b/tools/ahat/src/dominators/DominatorsComputation.java @@ -0,0 +1,260 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.ahat.dominators; + +import java.util.ArrayDeque; +import java.util.ArrayList; +import java.util.Deque; +import java.util.List; +import java.util.Queue; + +/** + * Generic DominatorsComputation. + * + * To use the dominators computation, have your graph nodes implement the + * DominatorsComputation.Node interface, then call + * DominatorsComputation.computeDominators on the single root node. + */ +public class DominatorsComputation { + /** + * Interface for a directed graph to perform the dominators computation on. + */ + public interface Node { + /** + * Associate the given dominator state with this node. + */ + void setDominatorsComputationState(Object state); + + /** + * Get the most recent dominator state associated with this node using + * setDominatorsComputationState. If setDominatorsComputationState has not + * yet been called, this should return null. + */ + Object getDominatorsComputationState(); + + /** + * Return a collection of nodes referenced from this node, for the + * purposes of computing dominators. + */ + Iterable<? extends Node> getReferencesForDominators(); + + /** + * Update this node's dominator based on the results of the dominators + * computation. + */ + void setDominator(Node dominator); + } + + // NodeS is information associated with a particular node for the + // purposes of computing dominators. + // By convention we use the suffix 'S' to name instances of NodeS. + private static class NodeS { + // The node that this NodeS is associated with. + public Node node; + + // Unique identifier for this node, in increasing order based on the order + // this node was visited in a depth first search from the root. In + // particular, given nodes A and B, if A.id > B.id, then A cannot be a + // dominator of B. + public long id; + + // Upper bound on the id of this node's dominator. + // The true immediate dominator of this node must have id <= domid. + // This upper bound is slowly tightened as part of the dominators + // computation. + public long domid; + + // The current candidate dominator for this node. + // Invariant: (domid < domS.id) implies this node is on the queue of + // nodes to be revisited. + public NodeS domS; + + // A node with a reference to this node that is one step closer to the + // root than this node. + // Invariant: srcS.id < this.id + public NodeS srcS; + + // The set of nodes X reachable by 'this' on a path of nodes from the + // root with increasing ids (possibly excluding X) that this node does not + // dominate (this.id > X.domid). + // We can use a List instead of a Set for this because we guarentee that + // we don't add the same node more than once to the list (see below). + public List<NodeS> undom = new ArrayList<NodeS>(); + + // The largest id of the node X for which we did X.undom.add(this). + // This is an optimization to avoid adding duplicate node entries to the + // undom set. + // + // The first time we see this node, we reach it through a path of nodes + // with IDs 0,...,a,this. These form our src chain to the root. + // + // The next time we see this node, we reach it through a path of nodes + // with IDS 0,...,b,c,...,d,this. Where all 0,...,b < a and all c,...,d > a. + // + // The next time we see this node, we reach it through a path of nodes + // with IDS 0,...,e,f,...,g,this. With all 0,...,e < d and all f,...,g > d. + // And so on. + // + // The first time we see this node, we set undomid to a.id. Nodes 0,...,a + // will be added as undom in the 'revisit' phase of the node. + // + // The next times we see this node, we mark a+,...,d as undom and + // change undomid to d. And so on. + public long undomid; + } + + private static class Link { + public NodeS srcS; + public Node dst; + + public Link(NodeS srcS, Node dst) { + this.srcS = srcS; + this.dst = dst; + } + } + + /** + * Compute the dominator tree rooted at the given node. + * There must not be any incoming references to the root node. + */ + public static void computeDominators(Node root) { + long id = 0; + + // List of all nodes seen. We keep track of this here to update all the + // dominators once we are done. + List<NodeS> nodes = new ArrayList<NodeS>(); + + // The set of nodes N such that N.domid < N.domS.id. These nodes need + // to be revisisted because their dominator is clearly wrong. + // Use a Queue instead of a Set because performance will be better. We + // avoid adding nodes already on the queue by checking whether it was + // already true that N.domid < N.domS.id, in which case the node is + // already on the queue. + Queue<NodeS> revisit = new ArrayDeque<NodeS>(); + + // Set up the root node specially. + NodeS rootS = new NodeS(); + rootS.node = root; + rootS.id = id++; + root.setDominatorsComputationState(rootS); + + // 1. Do a depth first search of the nodes, label them with ids and come + // up with intial candidate dominators for them. + Deque<Link> dfs = new ArrayDeque<Link>(); + for (Node child : root.getReferencesForDominators()) { + dfs.push(new Link(rootS, child)); + } + + while (!dfs.isEmpty()) { + Link link = dfs.pop(); + NodeS dstS = (NodeS)link.dst.getDominatorsComputationState(); + if (dstS == null) { + // This is the first time we have seen the node. The candidate + // dominator is link src. + dstS = new NodeS(); + dstS.node = link.dst; + dstS.id = id++; + dstS.domid = link.srcS.id; + dstS.domS = link.srcS; + dstS.srcS = link.srcS; + dstS.undomid = dstS.domid; + nodes.add(dstS); + link.dst.setDominatorsComputationState(dstS); + + for (Node child : link.dst.getReferencesForDominators()) { + dfs.push(new Link(dstS, child)); + } + } else { + // We have seen the node already. Update the state based on the new + // potential dominator. + NodeS srcS = link.srcS; + boolean revisiting = dstS.domid < dstS.domS.id; + + while (srcS.id > dstS.domid) { + if (srcS.id > dstS.undomid) { + srcS.undom.add(dstS); + } + srcS = srcS.srcS; + } + dstS.undomid = link.srcS.id; + + if (srcS.id < dstS.domid) { + // In this case, dstS.domid must be wrong, because we just found a + // path to dstS that does not go through dstS.domid: + // All nodes from root to srcS have id < domid, and all nodes from + // srcS to dstS had id > domid, so dstS.domid cannot be on this path + // from root to dstS. + dstS.domid = srcS.id; + if (!revisiting) { + revisit.add(dstS); + } + } + } + } + + // 2. Continue revisiting nodes until they all satisfy the requirement + // that domS.id <= domid. + while (!revisit.isEmpty()) { + NodeS nodeS = revisit.poll(); + NodeS domS = nodeS.domS; + assert nodeS.domid < domS.id; + while (domS.id > nodeS.domid) { + if (domS.domS.id < nodeS.domid) { + // In this case, nodeS.domid must be wrong, because there is a path + // from root to nodeS that does not go through nodeS.domid: + // * We can go from root to domS without going through nodeS.domid, + // because otherwise nodeS.domid would dominate domS, not + // domS.domS. + // * We can go from domS to nodeS without going through nodeS.domid + // because we know nodeS is reachable from domS on a path of nodes + // with increases ids, which cannot include nodeS.domid, which + // has a smaller id than domS. + nodeS.domid = domS.domS.id; + } + domS.undom.add(nodeS); + domS = domS.srcS; + } + nodeS.domS = domS; + nodeS.domid = domS.id; + + for (NodeS xS : nodeS.undom) { + if (domS.id < xS.domid) { + // In this case, xS.domid must be wrong, because there is a path + // from the root to xX that does not go through xS.domid: + // * We can go from root to nodeS without going through xS.domid, + // because otherwise xS.domid would dominate nodeS, not domS. + // * We can go from nodeS to xS without going through xS.domid + // because we know xS is reachable from nodeS on a path of nodes + // with increasing ids, which cannot include xS.domid, which has + // a smaller id than nodeS. + boolean revisiting = xS.domid < xS.domS.id; + xS.domid = domS.id; + if (!revisiting) { + revisit.add(xS); + } + } + } + } + + // 3. Update the dominators of the nodes. + root.setDominatorsComputationState(null); + for (NodeS nodeS : nodes) { + nodeS.node.setDominator(nodeS.domS.node); + nodeS.node.setDominatorsComputationState(null); + } + } +} diff --git a/tools/ahat/src/heapdump/AhatArrayInstance.java b/tools/ahat/src/heapdump/AhatArrayInstance.java index d88cf94075..6d4485d4b9 100644 --- a/tools/ahat/src/heapdump/AhatArrayInstance.java +++ b/tools/ahat/src/heapdump/AhatArrayInstance.java @@ -20,6 +20,7 @@ import com.android.tools.perflib.heap.ArrayInstance; import com.android.tools.perflib.heap.Instance; import java.nio.charset.StandardCharsets; import java.util.AbstractList; +import java.util.Collections; import java.util.List; public class AhatArrayInstance extends AhatInstance { @@ -37,8 +38,8 @@ public class AhatArrayInstance extends AhatInstance { super(id); } - @Override void initialize(AhatSnapshot snapshot, Instance inst) { - super.initialize(snapshot, inst); + @Override void initialize(AhatSnapshot snapshot, Instance inst, Site site) { + super.initialize(snapshot, inst, site); ArrayInstance array = (ArrayInstance)inst; switch (array.getArrayType()) { @@ -49,10 +50,6 @@ public class AhatArrayInstance extends AhatInstance { if (objects[i] != null) { Instance ref = (Instance)objects[i]; insts[i] = snapshot.findInstance(ref.getId()); - if (ref.getNextInstanceToGcRoot() == inst) { - String field = "[" + Integer.toString(i) + "]"; - insts[i].setNextInstanceToGcRoot(this, field); - } } } mValues = new AbstractList<Value>() { @@ -132,6 +129,35 @@ public class AhatArrayInstance extends AhatInstance { return mValues.get(index); } + @Override + ReferenceIterator getReferences() { + // The list of references will be empty if this is a primitive array. + List<Reference> refs = Collections.emptyList(); + if (!mValues.isEmpty()) { + Value first = mValues.get(0); + if (first == null || first.isAhatInstance()) { + refs = new AbstractList<Reference>() { + @Override + public int size() { + return mValues.size(); + } + + @Override + public Reference get(int index) { + Value value = mValues.get(index); + if (value != null) { + assert value.isAhatInstance(); + String field = "[" + Integer.toString(index) + "]"; + return new Reference(AhatArrayInstance.this, field, value.asAhatInstance(), true); + } + return null; + } + }; + } + } + return new ReferenceIterator(refs); + } + @Override public boolean isArrayInstance() { return true; } diff --git a/tools/ahat/src/heapdump/AhatClassInstance.java b/tools/ahat/src/heapdump/AhatClassInstance.java index 158de5240d..211592388c 100644 --- a/tools/ahat/src/heapdump/AhatClassInstance.java +++ b/tools/ahat/src/heapdump/AhatClassInstance.java @@ -19,6 +19,7 @@ package com.android.ahat.heapdump; import com.android.tools.perflib.heap.ClassInstance; import com.android.tools.perflib.heap.Instance; import java.awt.image.BufferedImage; +import java.util.AbstractList; import java.util.Arrays; import java.util.List; @@ -29,8 +30,8 @@ public class AhatClassInstance extends AhatInstance { super(id); } - @Override void initialize(AhatSnapshot snapshot, Instance inst) { - super.initialize(snapshot, inst); + @Override void initialize(AhatSnapshot snapshot, Instance inst, Site site) { + super.initialize(snapshot, inst, site); ClassInstance classInst = (ClassInstance)inst; List<ClassInstance.FieldValue> fieldValues = classInst.getValues(); @@ -40,15 +41,7 @@ public class AhatClassInstance extends AhatInstance { String name = field.getField().getName(); String type = field.getField().getType().toString(); Value value = snapshot.getValue(field.getValue()); - mFieldValues[i] = new FieldValue(name, type, value); - - if (field.getValue() instanceof Instance) { - Instance ref = (Instance)field.getValue(); - if (ref.getNextInstanceToGcRoot() == inst) { - value.asAhatInstance().setNextInstanceToGcRoot(this, "." + name); - } - } } } @@ -101,6 +94,30 @@ public class AhatClassInstance extends AhatInstance { return Arrays.asList(mFieldValues); } + @Override + ReferenceIterator getReferences() { + List<Reference> refs = new AbstractList<Reference>() { + @Override + public int size() { + return mFieldValues.length; + } + + @Override + public Reference get(int index) { + FieldValue field = mFieldValues[index]; + Value value = field.value; + if (value != null && value.isAhatInstance()) { + boolean strong = !field.name.equals("referent") + || !isInstanceOfClass("java.lang.ref.Reference"); + AhatInstance ref = value.asAhatInstance(); + return new Reference(AhatClassInstance.this, "." + field.name, ref, strong); + } + return null; + } + }; + return new ReferenceIterator(refs); + } + /** * Returns true if this is an instance of a class with the given name. */ diff --git a/tools/ahat/src/heapdump/AhatClassObj.java b/tools/ahat/src/heapdump/AhatClassObj.java index c5ade1d405..052d7a8e88 100644 --- a/tools/ahat/src/heapdump/AhatClassObj.java +++ b/tools/ahat/src/heapdump/AhatClassObj.java @@ -19,6 +19,7 @@ package com.android.ahat.heapdump; import com.android.tools.perflib.heap.ClassObj; import com.android.tools.perflib.heap.Field; import com.android.tools.perflib.heap.Instance; +import java.util.AbstractList; import java.util.Arrays; import java.util.Collection; import java.util.List; @@ -34,8 +35,8 @@ public class AhatClassObj extends AhatInstance { super(id); } - @Override void initialize(AhatSnapshot snapshot, Instance inst) { - super.initialize(snapshot, inst); + @Override void initialize(AhatSnapshot snapshot, Instance inst, Site site) { + super.initialize(snapshot, inst, site); ClassObj classObj = (ClassObj)inst; mClassName = classObj.getClassName(); @@ -58,13 +59,6 @@ public class AhatClassObj extends AhatInstance { String type = field.getKey().getType().toString(); Value value = snapshot.getValue(field.getValue()); mStaticFieldValues[index++] = new FieldValue(name, type, value); - - if (field.getValue() instanceof Instance) { - Instance ref = (Instance)field.getValue(); - if (ref.getNextInstanceToGcRoot() == inst) { - value.asAhatInstance().setNextInstanceToGcRoot(this, "." + name); - } - } } } @@ -96,6 +90,27 @@ public class AhatClassObj extends AhatInstance { return Arrays.asList(mStaticFieldValues); } + @Override + ReferenceIterator getReferences() { + List<Reference> refs = new AbstractList<Reference>() { + @Override + public int size() { + return mStaticFieldValues.length; + } + + @Override + public Reference get(int index) { + FieldValue field = mStaticFieldValues[index]; + Value value = field.value; + if (value != null && value.isAhatInstance()) { + return new Reference(AhatClassObj.this, "." + field.name, value.asAhatInstance(), true); + } + return null; + } + }; + return new ReferenceIterator(refs); + } + @Override public boolean isClassObj() { return true; } @@ -105,11 +120,10 @@ public class AhatClassObj extends AhatInstance { } @Override public String toString() { - return mClassName; + return "class " + mClassName; } @Override AhatInstance newPlaceHolderInstance() { return new AhatPlaceHolderClassObj(this); } } - diff --git a/tools/ahat/src/heapdump/AhatInstance.java b/tools/ahat/src/heapdump/AhatInstance.java index af369d95d8..8905b7638c 100644 --- a/tools/ahat/src/heapdump/AhatInstance.java +++ b/tools/ahat/src/heapdump/AhatInstance.java @@ -16,39 +16,48 @@ package com.android.ahat.heapdump; +import com.android.ahat.dominators.DominatorsComputation; import com.android.tools.perflib.heap.ClassObj; import com.android.tools.perflib.heap.Instance; -import com.android.tools.perflib.heap.RootObj; import java.awt.image.BufferedImage; import java.util.ArrayDeque; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Deque; import java.util.List; +import java.util.Queue; -public abstract class AhatInstance implements Diffable<AhatInstance> { - private long mId; +public abstract class AhatInstance implements Diffable<AhatInstance>, + DominatorsComputation.Node { + // The id of this instance from the heap dump. + private final long mId; + + // Fields initialized in initialize(). private Size mSize; - private Size[] mRetainedSizes; // Retained size indexed by heap index - private boolean mIsReachable; private AhatHeap mHeap; - private AhatInstance mImmediateDominator; - private AhatInstance mNextInstanceToGcRoot; - private String mNextInstanceToGcRootField = "???"; private AhatClassObj mClassObj; - private AhatInstance[] mHardReverseReferences; - private AhatInstance[] mSoftReverseReferences; private Site mSite; // If this instance is a root, mRootTypes contains a set of the root types. // If this instance is not a root, mRootTypes is null. private List<String> mRootTypes; - // List of instances this instance immediately dominates. + // Fields initialized in computeReverseReferences(). + private AhatInstance mNextInstanceToGcRoot; + private String mNextInstanceToGcRootField; + private ArrayList<AhatInstance> mHardReverseReferences; + private ArrayList<AhatInstance> mSoftReverseReferences; + + // Fields initialized in DominatorsComputation.computeDominators(). + // mDominated - the list of instances immediately dominated by this instance. + // mRetainedSizes - retained size indexed by heap index. + private AhatInstance mImmediateDominator; private List<AhatInstance> mDominated = new ArrayList<AhatInstance>(); + private Size[] mRetainedSizes; + private Object mDominatorsComputationState; + // The baseline instance for purposes of diff. private AhatInstance mBaseline; public AhatInstance(long id) { @@ -62,58 +71,16 @@ public abstract class AhatInstance implements Diffable<AhatInstance> { * There is no guarantee that the AhatInstances returned by * snapshot.findInstance have been initialized yet. */ - void initialize(AhatSnapshot snapshot, Instance inst) { - mId = inst.getId(); + void initialize(AhatSnapshot snapshot, Instance inst, Site site) { mSize = new Size(inst.getSize(), 0); - mIsReachable = inst.isReachable(); - - List<AhatHeap> heaps = snapshot.getHeaps(); - mHeap = snapshot.getHeap(inst.getHeap().getName()); - Instance dom = inst.getImmediateDominator(); - if (dom == null || dom instanceof RootObj) { - mImmediateDominator = null; - } else { - mImmediateDominator = snapshot.findInstance(dom.getId()); - mImmediateDominator.mDominated.add(this); - } - ClassObj clsObj = inst.getClassObj(); if (clsObj != null) { mClassObj = snapshot.findClassObj(clsObj.getId()); } - // A couple notes about reverse references: - // * perflib sometimes returns unreachable reverse references. If - // snapshot.findInstance returns null, it means the reverse reference is - // not reachable, so we filter it out. - // * We store the references as AhatInstance[] instead of - // ArrayList<AhatInstance> because it saves a lot of space and helps - // with performance when there are a lot of AhatInstances. - ArrayList<AhatInstance> ahatRefs = new ArrayList<AhatInstance>(); - ahatRefs = new ArrayList<AhatInstance>(); - for (Instance ref : inst.getHardReverseReferences()) { - AhatInstance ahat = snapshot.findInstance(ref.getId()); - if (ahat != null) { - ahatRefs.add(ahat); - } - } - mHardReverseReferences = new AhatInstance[ahatRefs.size()]; - ahatRefs.toArray(mHardReverseReferences); - - List<Instance> refs = inst.getSoftReverseReferences(); - ahatRefs.clear(); - if (refs != null) { - for (Instance ref : refs) { - AhatInstance ahat = snapshot.findInstance(ref.getId()); - if (ahat != null) { - ahatRefs.add(ahat); - } - } - } - mSoftReverseReferences = new AhatInstance[ahatRefs.size()]; - ahatRefs.toArray(mSoftReverseReferences); + mSite = site; } /** @@ -166,7 +133,7 @@ public abstract class AhatInstance implements Diffable<AhatInstance> { * Returns whether this object is strongly-reachable. */ public boolean isReachable() { - return mIsReachable; + return mImmediateDominator != null; } /** @@ -177,6 +144,12 @@ public abstract class AhatInstance implements Diffable<AhatInstance> { } /** + * Returns an iterator over the references this AhatInstance has to other + * AhatInstances. + */ + abstract ReferenceIterator getReferences(); + + /** * Returns true if this instance is marked as a root instance. */ public boolean isRoot() { @@ -227,13 +200,6 @@ public abstract class AhatInstance implements Diffable<AhatInstance> { } /** - * Sets the allocation site of this instance. - */ - void setSite(Site site) { - mSite = site; - } - - /** * Returns true if the given instance is a class object */ public boolean isClassObj() { @@ -311,14 +277,20 @@ public abstract class AhatInstance implements Diffable<AhatInstance> { * Returns a list of objects with hard references to this object. */ public List<AhatInstance> getHardReverseReferences() { - return Arrays.asList(mHardReverseReferences); + if (mHardReverseReferences != null) { + return mHardReverseReferences; + } + return Collections.emptyList(); } /** * Returns a list of objects with soft references to this object. */ public List<AhatInstance> getSoftReverseReferences() { - return Arrays.asList(mSoftReverseReferences); + if (mSoftReverseReferences != null) { + return mSoftReverseReferences; + } + return Collections.emptyList(); } /** @@ -425,8 +397,10 @@ public abstract class AhatInstance implements Diffable<AhatInstance> { } void setNextInstanceToGcRoot(AhatInstance inst, String field) { - mNextInstanceToGcRoot = inst; - mNextInstanceToGcRootField = field; + if (mNextInstanceToGcRoot == null && !isRoot()) { + mNextInstanceToGcRoot = inst; + mNextInstanceToGcRootField = field; + } } /** Returns a human-readable identifier for this object. @@ -466,6 +440,47 @@ public abstract class AhatInstance implements Diffable<AhatInstance> { } /** + * Initialize the reverse reference fields of this instance and all other + * instances reachable from it. Initializes the following fields: + * mNextInstanceToGcRoot + * mNextInstanceToGcRootField + * mHardReverseReferences + * mSoftReverseReferences + */ + static void computeReverseReferences(AhatInstance root) { + // Do a breadth first search to visit the nodes. + Queue<Reference> bfs = new ArrayDeque<Reference>(); + for (Reference ref : root.getReferences()) { + bfs.add(ref); + } + while (!bfs.isEmpty()) { + Reference ref = bfs.poll(); + + if (ref.ref.mHardReverseReferences == null) { + // This is the first time we are seeing ref.ref. + ref.ref.mNextInstanceToGcRoot = ref.src; + ref.ref.mNextInstanceToGcRootField = ref.field; + ref.ref.mHardReverseReferences = new ArrayList<AhatInstance>(); + for (Reference childRef : ref.ref.getReferences()) { + bfs.add(childRef); + } + } + + // Note: ref.src is null when the src is the SuperRoot. + if (ref.src != null) { + if (ref.strong) { + ref.ref.mHardReverseReferences.add(ref.src); + } else { + if (ref.ref.mSoftReverseReferences == null) { + ref.ref.mSoftReverseReferences = new ArrayList<AhatInstance>(); + } + ref.ref.mSoftReverseReferences.add(ref.src); + } + } + } + } + + /** * Recursively compute the retained size of the given instance and all * other instances it dominates. */ @@ -486,8 +501,10 @@ public abstract class AhatInstance implements Diffable<AhatInstance> { for (int i = 0; i < numHeaps; i++) { inst.mRetainedSizes[i] = Size.ZERO; } - inst.mRetainedSizes[inst.mHeap.getIndex()] = - inst.mRetainedSizes[inst.mHeap.getIndex()].plus(inst.mSize); + if (!(inst instanceof SuperRoot)) { + inst.mRetainedSizes[inst.mHeap.getIndex()] = + inst.mRetainedSizes[inst.mHeap.getIndex()].plus(inst.mSize); + } deque.push(inst); for (AhatInstance dominated : inst.mDominated) { deque.push(dominated); @@ -501,4 +518,25 @@ public abstract class AhatInstance implements Diffable<AhatInstance> { } } } + + @Override + public void setDominatorsComputationState(Object state) { + mDominatorsComputationState = state; + } + + @Override + public Object getDominatorsComputationState() { + return mDominatorsComputationState; + } + + @Override + public Iterable<? extends DominatorsComputation.Node> getReferencesForDominators() { + return new DominatorReferenceIterator(getReferences()); + } + + @Override + public void setDominator(DominatorsComputation.Node dominator) { + mImmediateDominator = (AhatInstance)dominator; + mImmediateDominator.mDominated.add(this); + } } diff --git a/tools/ahat/src/heapdump/AhatPlaceHolderInstance.java b/tools/ahat/src/heapdump/AhatPlaceHolderInstance.java index 4aac80484d..d797b11030 100644 --- a/tools/ahat/src/heapdump/AhatPlaceHolderInstance.java +++ b/tools/ahat/src/heapdump/AhatPlaceHolderInstance.java @@ -16,6 +16,9 @@ package com.android.ahat.heapdump; +import java.util.Collections; +import java.util.List; + /** * Generic PlaceHolder instance to take the place of a real AhatInstance for * the purposes of displaying diffs. @@ -60,4 +63,10 @@ public class AhatPlaceHolderInstance extends AhatInstance { @Override public boolean isPlaceHolder() { return true; } + + @Override + ReferenceIterator getReferences() { + List<Reference> refs = Collections.emptyList(); + return new ReferenceIterator(refs); + } } diff --git a/tools/ahat/src/heapdump/AhatSnapshot.java b/tools/ahat/src/heapdump/AhatSnapshot.java index 35d6c8a315..7df78c50b5 100644 --- a/tools/ahat/src/heapdump/AhatSnapshot.java +++ b/tools/ahat/src/heapdump/AhatSnapshot.java @@ -16,6 +16,7 @@ package com.android.ahat.heapdump; +import com.android.ahat.dominators.DominatorsComputation; import com.android.tools.perflib.captures.DataBuffer; import com.android.tools.perflib.captures.MemoryMappedFileBuffer; import com.android.tools.perflib.heap.ArrayInstance; @@ -42,7 +43,7 @@ public class AhatSnapshot implements Diffable<AhatSnapshot> { private final Site mRootSite = new Site("ROOT"); // Collection of objects whose immediate dominator is the SENTINEL_ROOT. - private final List<AhatInstance> mRooted = new ArrayList<AhatInstance>(); + private final List<AhatInstance> mRooted; // List of all ahat instances stored in increasing order by id. private final List<AhatInstance> mInstances = new ArrayList<AhatInstance>(); @@ -80,7 +81,6 @@ public class AhatSnapshot implements Diffable<AhatSnapshot> { */ private AhatSnapshot(DataBuffer buffer, ProguardMap map) throws IOException { Snapshot snapshot = Snapshot.createSnapshot(buffer, map); - snapshot.computeDominators(); // Properly label the class of class objects in the perflib snapshot. final ClassObj javaLangClass = snapshot.findClass("java.lang.Class"); @@ -139,46 +139,45 @@ public class AhatSnapshot implements Diffable<AhatSnapshot> { // and instances. for (AhatInstance ahat : mInstances) { Instance inst = snapshot.findInstance(ahat.getId()); - ahat.initialize(this, inst); - Long registeredNativeSize = registeredNative.get(inst); - if (registeredNativeSize != null) { - ahat.addRegisteredNativeSize(registeredNativeSize); - } - - if (inst.getImmediateDominator() == Snapshot.SENTINEL_ROOT) { - mRooted.add(ahat); - } - - if (inst.isReachable()) { - ahat.getHeap().addToSize(ahat.getSize()); - } - - // Update sites. StackFrame[] frames = null; StackTrace stack = inst.getStack(); if (stack != null) { frames = stack.getFrames(); } Site site = mRootSite.add(frames, frames == null ? 0 : frames.length, ahat); - ahat.setSite(site); + ahat.initialize(this, inst, site); + + Long registeredNativeSize = registeredNative.get(inst); + if (registeredNativeSize != null) { + ahat.addRegisteredNativeSize(registeredNativeSize); + } } // Record the roots and their types. + SuperRoot superRoot = new SuperRoot(); for (RootObj root : snapshot.getGCRoots()) { Instance inst = root.getReferredInstance(); if (inst != null) { - findInstance(inst.getId()).addRootType(root.getRootType().toString()); + AhatInstance ahat = findInstance(inst.getId()); + if (!ahat.isRoot()) { + superRoot.addRoot(ahat); + } + ahat.addRootType(root.getRootType().toString()); } } snapshot.dispose(); - // Compute the retained sizes of objects. We do this explicitly now rather - // than relying on the retained sizes computed by perflib so that - // registered native sizes are included. - for (AhatInstance inst : mRooted) { - AhatInstance.computeRetainedSize(inst, mHeaps.size()); + AhatInstance.computeReverseReferences(superRoot); + DominatorsComputation.computeDominators(superRoot); + AhatInstance.computeRetainedSize(superRoot, mHeaps.size()); + + mRooted = superRoot.getDominated(); + for (AhatHeap heap : mHeaps) { + heap.addToSize(superRoot.getRetainedSize(heap)); } + + mRootSite.computeObjectsInfos(mHeaps.size()); } /** diff --git a/tools/ahat/src/heapdump/DominatorReferenceIterator.java b/tools/ahat/src/heapdump/DominatorReferenceIterator.java new file mode 100644 index 0000000000..ce2e6efa6e --- /dev/null +++ b/tools/ahat/src/heapdump/DominatorReferenceIterator.java @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.ahat.heapdump; + +import java.util.Iterator; +import java.util.NoSuchElementException; + +/** + * Reference iterator used for the dominators computation. + * This visits only strong references. + */ +class DominatorReferenceIterator implements Iterator<AhatInstance>, + Iterable<AhatInstance> { + private ReferenceIterator mIter; + private AhatInstance mNext; + + public DominatorReferenceIterator(ReferenceIterator iter) { + mIter = iter; + mNext = null; + } + + @Override + public boolean hasNext() { + while (mNext == null && mIter.hasNext()) { + Reference ref = mIter.next(); + if (ref.strong) { + mNext = ref.ref; + } + } + return mNext != null; + } + + @Override + public AhatInstance next() { + if (hasNext()) { + AhatInstance next = mNext; + mNext = null; + return next; + } + throw new NoSuchElementException(); + } + + @Override + public Iterator<AhatInstance> iterator() { + return this; + } +} diff --git a/tools/ahat/src/heapdump/Reference.java b/tools/ahat/src/heapdump/Reference.java new file mode 100644 index 0000000000..980f2780b6 --- /dev/null +++ b/tools/ahat/src/heapdump/Reference.java @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.ahat.heapdump; + +/** + * Reference represents a reference from 'src' to 'ref' through 'field'. + * Field is a string description for human consumption. This is typically + * either "." followed by the field name or an array subscript such as "[4]". + * 'strong' is true if this is a strong reference, false if it is a + * weak/soft/other reference. + */ +public class Reference { + public final AhatInstance src; + public final String field; + public final AhatInstance ref; + public final boolean strong; + + public Reference(AhatInstance src, String field, AhatInstance ref, boolean strong) { + this.src = src; + this.field = field; + this.ref = ref; + this.strong = strong; + } +} diff --git a/tools/ahat/src/heapdump/ReferenceIterator.java b/tools/ahat/src/heapdump/ReferenceIterator.java new file mode 100644 index 0000000000..a707fb24ef --- /dev/null +++ b/tools/ahat/src/heapdump/ReferenceIterator.java @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.ahat.heapdump; + +import java.util.Iterator; +import java.util.List; +import java.util.NoSuchElementException; + +class ReferenceIterator implements Iterator<Reference>, + Iterable<Reference> { + private List<Reference> mRefs; + private int mLength; + private int mNextIndex; + private Reference mNext; + + /** + * Construct a ReferenceIterator that iterators over the given list of + * references. Elements of the given list of references may be null, in + * which case the ReferenceIterator will skip over them. + */ + public ReferenceIterator(List<Reference> refs) { + mRefs = refs; + mLength = refs.size(); + mNextIndex = 0; + mNext = null; + } + + @Override + public boolean hasNext() { + while (mNext == null && mNextIndex < mLength) { + mNext = mRefs.get(mNextIndex); + mNextIndex++; + } + return mNext != null; + } + + @Override + public Reference next() { + if (!hasNext()) { + throw new NoSuchElementException(); + } + Reference next = mNext; + mNext = null; + return next; + } + + @Override + public Iterator<Reference> iterator() { + return this; + } +} diff --git a/tools/ahat/src/heapdump/Site.java b/tools/ahat/src/heapdump/Site.java index fdd4eea7b3..f0fc5d2d6c 100644 --- a/tools/ahat/src/heapdump/Site.java +++ b/tools/ahat/src/heapdump/Site.java @@ -42,15 +42,15 @@ public class Site implements Diffable<Site> { private int mDepth; // The total size of objects allocated in this site (including child sites), - // organized by heap index. Heap indices outside the range of mSizesByHeap - // implicitly have size 0. + // organized by heap index. Computed as part of computeObjectsInfos. private Size[] mSizesByHeap; // List of child sites. private List<Site> mChildren; - // List of all objects allocated in this site (including child sites). + // List of objects allocated at this site (not including child sites). private List<AhatInstance> mObjects; + private List<ObjectsInfo> mObjectsInfos; private Map<AhatHeap, Map<AhatClassObj, ObjectsInfo>> mObjectsInfoMap; @@ -111,7 +111,6 @@ public class Site implements Diffable<Site> { mLineNumber = line; mId = id; mDepth = depth; - mSizesByHeap = new Size[0]; mChildren = new ArrayList<Site>(); mObjects = new ArrayList<AhatInstance>(); mObjectsInfos = new ArrayList<ObjectsInfo>(); @@ -130,67 +129,102 @@ public class Site implements Diffable<Site> { } private static Site add(Site site, StackFrame[] frames, int depth, AhatInstance inst) { - while (true) { - site.mObjects.add(inst); + while (depth > 0) { + StackFrame next = frames[depth - 1]; + Site child = null; + for (int i = 0; i < site.mChildren.size(); i++) { + Site curr = site.mChildren.get(i); + if (curr.mLineNumber == next.getLineNumber() + && curr.mMethodName.equals(next.getMethodName()) + && curr.mSignature.equals(next.getSignature()) + && curr.mFilename.equals(next.getFilename())) { + child = curr; + break; + } + } + if (child == null) { + child = new Site(site, next.getMethodName(), next.getSignature(), + next.getFilename(), next.getLineNumber(), inst.getId(), depth - 1); + site.mChildren.add(child); + } + depth = depth - 1; + site = child; + } + site.mObjects.add(inst); + return site; + } + + /** + * Recompute the ObjectsInfos for this and all child sites. + * This should be done after the sites tree has been formed. It should also + * be done after dominators computation has been performed to ensure only + * reachable objects are included in the ObjectsInfos. + * + * @param numHeaps - The number of heaps in the heap dump. + */ + void computeObjectsInfos(int numHeaps) { + // Count up the total sizes by heap. + mSizesByHeap = new Size[numHeaps]; + for (int i = 0; i < numHeaps; ++i) { + mSizesByHeap[i] = Size.ZERO; + } - ObjectsInfo info = site.getObjectsInfo(inst.getHeap(), inst.getClassObj()); + // Add all reachable objects allocated at this site. + for (AhatInstance inst : mObjects) { if (inst.isReachable()) { AhatHeap heap = inst.getHeap(); - if (heap.getIndex() >= site.mSizesByHeap.length) { - Size[] newSizes = new Size[heap.getIndex() + 1]; - for (int i = 0; i < site.mSizesByHeap.length; i++) { - newSizes[i] = site.mSizesByHeap[i]; - } - for (int i = site.mSizesByHeap.length; i < heap.getIndex() + 1; i++) { - newSizes[i] = Size.ZERO; - } - site.mSizesByHeap = newSizes; - } - site.mSizesByHeap[heap.getIndex()] - = site.mSizesByHeap[heap.getIndex()].plus(inst.getSize()); - + Size size = inst.getSize(); + ObjectsInfo info = getObjectsInfo(heap, inst.getClassObj()); info.numInstances++; - info.numBytes = info.numBytes.plus(inst.getSize()); + info.numBytes = info.numBytes.plus(size); + mSizesByHeap[heap.getIndex()] = mSizesByHeap[heap.getIndex()].plus(size); } + } - if (depth > 0) { - StackFrame next = frames[depth - 1]; - Site child = null; - for (int i = 0; i < site.mChildren.size(); i++) { - Site curr = site.mChildren.get(i); - if (curr.mLineNumber == next.getLineNumber() - && curr.mMethodName.equals(next.getMethodName()) - && curr.mSignature.equals(next.getSignature()) - && curr.mFilename.equals(next.getFilename())) { - child = curr; - break; - } - } - if (child == null) { - child = new Site(site, next.getMethodName(), next.getSignature(), - next.getFilename(), next.getLineNumber(), inst.getId(), depth - 1); - site.mChildren.add(child); - } - depth = depth - 1; - site = child; - } else { - return site; + // Add objects allocated in child sites. + for (Site child : mChildren) { + child.computeObjectsInfos(numHeaps); + for (ObjectsInfo childInfo : child.mObjectsInfos) { + ObjectsInfo info = getObjectsInfo(childInfo.heap, childInfo.classObj); + info.numInstances += childInfo.numInstances; + info.numBytes = info.numBytes.plus(childInfo.numBytes); + } + for (int i = 0; i < numHeaps; ++i) { + mSizesByHeap[i] = mSizesByHeap[i].plus(child.mSizesByHeap[i]); } } } // Get the size of a site for a specific heap. public Size getSize(AhatHeap heap) { - int index = heap.getIndex(); - return index >= 0 && index < mSizesByHeap.length ? mSizesByHeap[index] : Size.ZERO; + return mSizesByHeap[heap.getIndex()]; } /** - * Get the list of objects allocated under this site. Includes objects - * allocated in children sites. + * Collect the objects allocated under this site, optionally filtered by + * heap name or class name. Includes objects allocated in children sites. + * @param heapName - The name of the heap the collected objects should + * belong to. This may be null to indicate objects of + * every heap should be collected. + * @param className - The name of the class the collected objects should + * belong to. This may be null to indicate objects of + * every class should be collected. + * @param objects - Out parameter. A collection of objects that all + * collected objects should be added to. */ - public Collection<AhatInstance> getObjects() { - return mObjects; + public void getObjects(String heapName, String className, Collection<AhatInstance> objects) { + for (AhatInstance inst : mObjects) { + if ((heapName == null || inst.getHeap().getName().equals(heapName)) + && (className == null || inst.getClassName().equals(className))) { + objects.add(inst); + } + } + + // Recursively visit children. Recursion should be okay here because the + // stack depth is limited by a reasonable amount (128 frames or so). + for (Site child : mChildren) { + child.getObjects(heapName, className, objects); + } } /** @@ -220,8 +254,8 @@ public class Site implements Diffable<Site> { // Get the combined size of the site for all heaps. public Size getTotalSize() { Size total = Size.ZERO; - for (int i = 0; i < mSizesByHeap.length; i++) { - total = total.plus(mSizesByHeap[i]); + for (Size size : mSizesByHeap) { + total = total.plus(size); } return total; } diff --git a/tools/ahat/src/heapdump/SuperRoot.java b/tools/ahat/src/heapdump/SuperRoot.java new file mode 100644 index 0000000000..54410cf1a6 --- /dev/null +++ b/tools/ahat/src/heapdump/SuperRoot.java @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.ahat.heapdump; + +import com.android.ahat.dominators.DominatorsComputation; +import java.util.AbstractList; +import java.util.ArrayList; +import java.util.List; + +public class SuperRoot extends AhatInstance implements DominatorsComputation.Node { + private List<AhatInstance> mRoots = new ArrayList<AhatInstance>(); + private Object mDominatorsComputationState; + + public SuperRoot() { + super(0); + } + + public void addRoot(AhatInstance root) { + mRoots.add(root); + } + + @Override + public String toString() { + return "SUPER_ROOT"; + } + + @Override + ReferenceIterator getReferences() { + List<Reference> refs = new AbstractList<Reference>() { + @Override + public int size() { + return mRoots.size(); + } + + @Override + public Reference get(int index) { + String field = ".roots[" + Integer.toString(index) + "]"; + return new Reference(null, field, mRoots.get(index), true); + } + }; + return new ReferenceIterator(refs); + } +} diff --git a/tools/ahat/src/manifest.txt b/tools/ahat/src/manifest.txt index c35ccf1cd3..d893c5e8ae 100644 --- a/tools/ahat/src/manifest.txt +++ b/tools/ahat/src/manifest.txt @@ -1,4 +1,4 @@ Name: ahat/ Implementation-Title: ahat -Implementation-Version: 1.2 +Implementation-Version: 1.3 Main-Class: com.android.ahat.Main diff --git a/tools/ahat/test-dump/Main.java b/tools/ahat/test-dump/Main.java index 3d3de78255..13fd102d7d 100644 --- a/tools/ahat/test-dump/Main.java +++ b/tools/ahat/test-dump/Main.java @@ -60,6 +60,14 @@ public class Main { public StackSmasher child; } + public static class Reference { + public Object referent; + + public Reference(Object referent) { + this.referent = referent; + } + } + // We will take a heap dump that includes a single instance of this // DumpedStuff class. Objects stored as fields in this class can be easily // found in the hprof dump by searching for the instance of the DumpedStuff @@ -71,6 +79,7 @@ public class Main { public char[] charArray = "char thing".toCharArray(); public String nullString = null; public Object anObject = new Object(); + public Reference aReference = new Reference(anObject); public ReferenceQueue<Object> referenceQueue = new ReferenceQueue<Object>(); public PhantomReference aPhantomReference = new PhantomReference(anObject, referenceQueue); public WeakReference aWeakReference = new WeakReference(anObject, referenceQueue); diff --git a/tools/ahat/test/DominatorsTest.java b/tools/ahat/test/DominatorsTest.java new file mode 100644 index 0000000000..0424e10dc8 --- /dev/null +++ b/tools/ahat/test/DominatorsTest.java @@ -0,0 +1,298 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.ahat; + +import com.android.ahat.dominators.DominatorsComputation; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import org.junit.Test; +import static org.junit.Assert.assertEquals; + +public class DominatorsTest { + private static class Node implements DominatorsComputation.Node { + public String name; + public List<Node> depends = new ArrayList<Node>(); + public Node dominator; + private Object dominatorsComputationState; + + public Node(String name) { + this.name = name; + } + + public void computeDominators() { + DominatorsComputation.computeDominators(this); + } + + public String toString() { + return name; + } + + @Override + public void setDominatorsComputationState(Object state) { + dominatorsComputationState = state; + } + + @Override + public Object getDominatorsComputationState() { + return dominatorsComputationState; + } + + @Override + public Collection<Node> getReferencesForDominators() { + return depends; + } + + @Override + public void setDominator(DominatorsComputation.Node dominator) { + this.dominator = (Node)dominator; + } + } + + @Test + public void singleNode() { + // --> n + // Trivial case. + Node n = new Node("n"); + n.computeDominators(); + } + + @Test + public void parentWithChild() { + // --> parent --> child + // The child node is dominated by the parent. + Node parent = new Node("parent"); + Node child = new Node("child"); + parent.depends = Arrays.asList(child); + + parent.computeDominators(); + assertEquals(parent, child.dominator); + } + + @Test + public void reachableTwoWays() { + // /-> right -->\ + // --> parent child + // \-> left --->/ + // The child node can be reached either by right or by left. + Node parent = new Node("parent"); + Node right = new Node("right"); + Node left = new Node("left"); + Node child = new Node("child"); + parent.depends = Arrays.asList(left, right); + right.depends = Arrays.asList(child); + left.depends = Arrays.asList(child); + + parent.computeDominators(); + assertEquals(parent, left.dominator); + assertEquals(parent, right.dominator); + assertEquals(parent, child.dominator); + } + + @Test + public void reachableDirectAndIndirect() { + // /-> right -->\ + // --> parent -----------> child + // The child node can be reached either by right or parent. + Node parent = new Node("parent"); + Node right = new Node("right"); + Node child = new Node("child"); + parent.depends = Arrays.asList(right, child); + right.depends = Arrays.asList(child); + + parent.computeDominators(); + assertEquals(parent, child.dominator); + assertEquals(parent, right.dominator); + } + + @Test + public void subDominator() { + // --> parent --> middle --> child + // The child is dominated by an internal node. + Node parent = new Node("parent"); + Node middle = new Node("middle"); + Node child = new Node("child"); + parent.depends = Arrays.asList(middle); + middle.depends = Arrays.asList(child); + + parent.computeDominators(); + assertEquals(parent, middle.dominator); + assertEquals(middle, child.dominator); + } + + @Test + public void childSelfLoop() { + // --> parent --> child -\ + // \<---/ + // The child points back to itself. + Node parent = new Node("parent"); + Node child = new Node("child"); + parent.depends = Arrays.asList(child); + child.depends = Arrays.asList(child); + + parent.computeDominators(); + assertEquals(parent, child.dominator); + } + + @Test + public void singleEntryLoop() { + // --> parent --> a --> b --> c -\ + // \<------------/ + // There is a loop in the graph, with only one way into the loop. + Node parent = new Node("parent"); + Node a = new Node("a"); + Node b = new Node("b"); + Node c = new Node("c"); + parent.depends = Arrays.asList(a); + a.depends = Arrays.asList(b); + b.depends = Arrays.asList(c); + c.depends = Arrays.asList(a); + + parent.computeDominators(); + assertEquals(parent, a.dominator); + assertEquals(a, b.dominator); + assertEquals(b, c.dominator); + } + + @Test + public void multiEntryLoop() { + // --> parent --> right --> a --> b ----\ + // \ \<-- c <---/ + // \--> left --->--------/ + // There is a loop in the graph, with two different ways to enter the + // loop. + Node parent = new Node("parent"); + Node left = new Node("left"); + Node right = new Node("right"); + Node a = new Node("a"); + Node b = new Node("b"); + Node c = new Node("c"); + parent.depends = Arrays.asList(left, right); + right.depends = Arrays.asList(a); + left.depends = Arrays.asList(c); + a.depends = Arrays.asList(b); + b.depends = Arrays.asList(c); + c.depends = Arrays.asList(a); + + parent.computeDominators(); + assertEquals(parent, right.dominator); + assertEquals(parent, left.dominator); + assertEquals(parent, a.dominator); + assertEquals(parent, c.dominator); + assertEquals(a, b.dominator); + } + + @Test + public void dominatorOverwrite() { + // /---------> right <--\ + // --> parent --> child <--/ / + // \---> left ---------/ + // Test a strange case where we have had trouble in the past with a + // dominator getting improperly overwritten. The relevant features of this + // case are: 'child' is visited after 'right', 'child' is dominated by + // 'parent', and 'parent' revisits 'right' after visiting 'child'. + Node parent = new Node("parent"); + Node right = new Node("right"); + Node left = new Node("left"); + Node child = new Node("child"); + parent.depends = Arrays.asList(left, child, right); + left.depends = Arrays.asList(right); + right.depends = Arrays.asList(child); + + parent.computeDominators(); + assertEquals(parent, left.dominator); + assertEquals(parent, child.dominator); + assertEquals(parent, right.dominator); + } + + @Test + public void stackOverflow() { + // --> a --> b --> ... --> N + // Verify we don't smash the stack for deep chains. + Node root = new Node("root"); + Node curr = root; + for (int i = 0; i < 10000; ++i) { + Node node = new Node("n" + i); + curr.depends.add(node); + curr = node; + } + + root.computeDominators(); + } + + @Test + public void hiddenRevisit() { + // /-> left ---->---------\ + // --> parent \---> a --> b --> c + // \-> right -/ + // Test a case we have had trouble in the past. + // When a's dominator is updated from left to parent, that should trigger + // all reachable children's dominators to be updated too. In particular, + // c's dominator should be updated, even though b's dominator is + // unchanged. + Node parent = new Node("parent"); + Node right = new Node("right"); + Node left = new Node("left"); + Node a = new Node("a"); + Node b = new Node("b"); + Node c = new Node("c"); + parent.depends = Arrays.asList(right, left); + left.depends = Arrays.asList(a, c); + right.depends = Arrays.asList(a); + a.depends = Arrays.asList(b); + b.depends = Arrays.asList(c); + + parent.computeDominators(); + assertEquals(parent, left.dominator); + assertEquals(parent, right.dominator); + assertEquals(parent, a.dominator); + assertEquals(parent, c.dominator); + assertEquals(a, b.dominator); + } + + @Test + public void preUndominatedUpdate() { + // /--------->--------\ + // / /---->----\ + // --> p -> a --> b --> c --> d --> e + // \---------->----------/ + // Test a case we have had trouble in the past. + // The candidate dominator for e is revised from d to a, then d is shown + // to be reachable from p. Make sure that causes e's dominator to be + // refined again from a to p. The extra nodes are there to ensure the + // necessary scheduling to expose the bug we had. + Node p = new Node("p"); + Node a = new Node("a"); + Node b = new Node("b"); + Node c = new Node("c"); + Node d = new Node("d"); + Node e = new Node("e"); + p.depends = Arrays.asList(d, a); + a.depends = Arrays.asList(e, b); + b.depends = Arrays.asList(d, c); + c.depends = Arrays.asList(d); + d.depends = Arrays.asList(e); + + p.computeDominators(); + assertEquals(p, a.dominator); + assertEquals(a, b.dominator); + assertEquals(b, c.dominator); + assertEquals(p, d.dominator); + assertEquals(p, e.dominator); + } +} diff --git a/tools/ahat/test/InstanceTest.java b/tools/ahat/test/InstanceTest.java index 71b081c9a4..f0e7f445ce 100644 --- a/tools/ahat/test/InstanceTest.java +++ b/tools/ahat/test/InstanceTest.java @@ -337,7 +337,7 @@ public class InstanceTest { public void classObjToString() throws IOException { TestDump dump = TestDump.getTestDump(); AhatInstance obj = dump.getAhatSnapshot().findClass("Main"); - assertEquals("Main", obj.toString()); + assertEquals("class Main", obj.toString()); } @Test @@ -370,6 +370,18 @@ public class InstanceTest { } @Test + public void reverseReferences() throws IOException { + TestDump dump = TestDump.getTestDump(); + AhatInstance obj = dump.getDumpedAhatInstance("anObject"); + AhatInstance ref = dump.getDumpedAhatInstance("aReference"); + AhatInstance weak = dump.getDumpedAhatInstance("aWeakReference"); + assertTrue(obj.getHardReverseReferences().contains(ref)); + assertFalse(obj.getHardReverseReferences().contains(weak)); + assertFalse(obj.getSoftReverseReferences().contains(ref)); + assertTrue(obj.getSoftReverseReferences().contains(weak)); + } + + @Test public void asStringEmbedded() throws IOException { // Set up a heap dump with an instance of java.lang.String of // "hello" with instance id 0x42 that is backed by a char array that is diff --git a/tools/ahat/test/Tests.java b/tools/ahat/test/Tests.java index a95788e3bd..a1e3246cd1 100644 --- a/tools/ahat/test/Tests.java +++ b/tools/ahat/test/Tests.java @@ -24,6 +24,7 @@ public class Tests { args = new String[]{ "com.android.ahat.DiffFieldsTest", "com.android.ahat.DiffTest", + "com.android.ahat.DominatorsTest", "com.android.ahat.InstanceTest", "com.android.ahat.NativeAllocationTest", "com.android.ahat.ObjectHandlerTest", @@ -278,7 +278,7 @@ if [ "$JIT_PROFILE" = "yes" ]; then -Xps-profile-path:$PROFILE_PATH \ -Xusejit:true \ "${ARGS_WITH_QUICKEN[@]}" \ - "&>" "$ANDROID_DATA/profile_gen.log" + &> "$ANDROID_DATA/profile_gen.log" EXIT_STATUS=$? if [ $EXIT_STATUS != 0 ]; then diff --git a/tools/cpp-define-generator/constant_dexcache.def b/tools/cpp-define-generator/constant_dexcache.def index ede16d2108..743ebb7453 100644 --- a/tools/cpp-define-generator/constant_dexcache.def +++ b/tools/cpp-define-generator/constant_dexcache.def @@ -25,4 +25,8 @@ DEFINE_EXPR(STRING_DEX_CACHE_SIZE_MINUS_ONE, int32_t, DEFINE_EXPR(STRING_DEX_CACHE_HASH_BITS, int32_t, art::LeastSignificantBit(art::mirror::DexCache::kDexCacheStringCacheSize)) DEFINE_EXPR(STRING_DEX_CACHE_ELEMENT_SIZE, int32_t, - sizeof(art::mirror::StringDexCachePair))
\ No newline at end of file + sizeof(art::mirror::StringDexCachePair)) +DEFINE_EXPR(METHOD_DEX_CACHE_SIZE_MINUS_ONE, int32_t, + art::mirror::DexCache::kDexCacheMethodCacheSize - 1) +DEFINE_EXPR(METHOD_DEX_CACHE_HASH_BITS, int32_t, + art::LeastSignificantBit(art::mirror::DexCache::kDexCacheMethodCacheSize)) diff --git a/tools/cpp-define-generator/constant_globals.def b/tools/cpp-define-generator/constant_globals.def index a3ccc72bb6..dbaf33cdef 100644 --- a/tools/cpp-define-generator/constant_globals.def +++ b/tools/cpp-define-generator/constant_globals.def @@ -17,9 +17,12 @@ // Export global values. #if defined(DEFINE_INCLUDE_DEPENDENCIES) +#include <atomic> // std::memory_order_relaxed #include "globals.h" // art::kObjectAlignment #endif +DEFINE_EXPR(STD_MEMORY_ORDER_RELAXED, int32_t, std::memory_order_relaxed) + #define DEFINE_OBJECT_EXPR(macro_name, type, constant_field_name) \ DEFINE_EXPR(OBJECT_ ## macro_name, type, constant_field_name) diff --git a/tools/cpp-define-generator/main.cc b/tools/cpp-define-generator/main.cc index fc99f8abc7..7c515be12f 100644 --- a/tools/cpp-define-generator/main.cc +++ b/tools/cpp-define-generator/main.cc @@ -14,12 +14,12 @@ * limitations under the License. */ +#include <algorithm> +#include <ios> #include <iostream> #include <sstream> -#include <type_traits> -#include <ios> -#include <algorithm> #include <string> +#include <type_traits> // Art Offset file dependencies #define DEFINE_INCLUDE_DEPENDENCIES diff --git a/tools/dexfuzz/README b/tools/dexfuzz/README index 271ac2bd85..a635fe9928 100644 --- a/tools/dexfuzz/README +++ b/tools/dexfuzz/README @@ -145,6 +145,7 @@ OppositeBranchChanger 40 PoolIndexChanger 30 RandomBranchChanger 30 RandomInstructionGenerator 30 +RegisterClobber 40 SwitchBranchShifter 30 TryBlockShifter 40 ValuePrinter 40 diff --git a/tools/dexfuzz/src/dexfuzz/DexFuzz.java b/tools/dexfuzz/src/dexfuzz/DexFuzz.java index d7a5325cb5..b0ce5a857e 100644 --- a/tools/dexfuzz/src/dexfuzz/DexFuzz.java +++ b/tools/dexfuzz/src/dexfuzz/DexFuzz.java @@ -33,9 +33,9 @@ import dexfuzz.listeners.UpdatingConsoleListener; * Entrypoint class for dexfuzz. */ public class DexFuzz { - // Last version update 1.4: added array length mutator. + // Last version update 1.6: added temporary register to ArrayLengthChanger. private static int majorVersion = 1; - private static int minorVersion = 4; + private static int minorVersion = 6; private static int seedChangeVersion = 0; /** diff --git a/tools/dexfuzz/src/dexfuzz/program/Program.java b/tools/dexfuzz/src/dexfuzz/program/Program.java index b0a06fc749..bb2f4c059d 100644 --- a/tools/dexfuzz/src/dexfuzz/program/Program.java +++ b/tools/dexfuzz/src/dexfuzz/program/Program.java @@ -38,6 +38,7 @@ import dexfuzz.program.mutators.OppositeBranchChanger; import dexfuzz.program.mutators.PoolIndexChanger; import dexfuzz.program.mutators.RandomBranchChanger; import dexfuzz.program.mutators.RandomInstructionGenerator; +import dexfuzz.program.mutators.RegisterClobber; import dexfuzz.program.mutators.SwitchBranchShifter; import dexfuzz.program.mutators.TryBlockShifter; import dexfuzz.program.mutators.ValuePrinter; @@ -209,6 +210,7 @@ public class Program { registerMutator(new PoolIndexChanger(rng, mutationStats, mutations)); registerMutator(new RandomBranchChanger(rng, mutationStats, mutations)); registerMutator(new RandomInstructionGenerator(rng, mutationStats, mutations)); + registerMutator(new RegisterClobber(rng, mutationStats, mutations)); registerMutator(new SwitchBranchShifter(rng, mutationStats, mutations)); registerMutator(new TryBlockShifter(rng, mutationStats, mutations)); registerMutator(new ValuePrinter(rng, mutationStats, mutations)); diff --git a/tools/dexfuzz/src/dexfuzz/program/mutators/NewArrayLengthChanger.java b/tools/dexfuzz/src/dexfuzz/program/mutators/NewArrayLengthChanger.java index aba7971b48..e640b4e2ef 100644 --- a/tools/dexfuzz/src/dexfuzz/program/mutators/NewArrayLengthChanger.java +++ b/tools/dexfuzz/src/dexfuzz/program/mutators/NewArrayLengthChanger.java @@ -28,8 +28,6 @@ import java.util.ArrayList; import java.util.List; import java.util.Random; -// This mutation might change the length of an array but can also change the -// value of the register in every place it is used. public class NewArrayLengthChanger extends CodeMutator { /** * Every CodeMutator has an AssociatedMutation, representing the @@ -116,20 +114,46 @@ public class NewArrayLengthChanger extends CodeMutator { MutatableCode mutatableCode = mutation.mutatableCode; MInsn newArrayInsn = newArrayLengthInsns.get(mutation.newArrayToChangeIdx); int newArrayInsnIdx = mutatableCode.getInstructionIndex(newArrayInsn); + // If the original new-array instruction is no longer present + // in the code (as indicated by a negative index), we make a + // best effort to find any other new-array instruction to + // apply the mutation to. If that effort fails, we simply + // bail by doing nothing. + if (newArrayInsnIdx < 0) { + newArrayInsnIdx = scanNewArray(mutatableCode); + if (newArrayInsnIdx == -1) { + return; + } + } MInsn newInsn = new MInsn(); newInsn.insn = new Instruction(); newInsn.insn.info = Instruction.getOpcodeInfo(Opcode.CONST_16); + mutatableCode.allocateTemporaryVRegs(1); + newArrayInsn.insn.vregB = mutatableCode.getTemporaryVReg(0); newInsn.insn.vregA = (int) newArrayInsn.insn.vregB; // New length chosen randomly between 1 to 100. newInsn.insn.vregB = rng.nextInt(100); mutatableCode.insertInstructionAt(newInsn, newArrayInsnIdx); Log.info("Changed the length of the array to " + newInsn.insn.vregB); stats.incrementStat("Changed length of new array"); + mutatableCode.finishedUsingTemporaryVRegs(); } private boolean isNewArray(MInsn mInsn) { Opcode opcode = mInsn.insn.info.opcode; return opcode == Opcode.NEW_ARRAY; } + + // Return the index of first new-array in the method, -1 otherwise. + private int scanNewArray(MutatableCode mutatableCode) { + int idx = 0; + for (MInsn mInsn : mutatableCode.getInstructions()) { + if (isNewArray(mInsn)) { + return idx; + } + idx++; + } + return -1; + } }
\ No newline at end of file diff --git a/tools/dexfuzz/src/dexfuzz/program/mutators/RegisterClobber.java b/tools/dexfuzz/src/dexfuzz/program/mutators/RegisterClobber.java new file mode 100644 index 0000000000..11da1d4d39 --- /dev/null +++ b/tools/dexfuzz/src/dexfuzz/program/mutators/RegisterClobber.java @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dexfuzz.program.mutators; + +import dexfuzz.Log; +import dexfuzz.MutationStats; +import dexfuzz.program.MInsn; +import dexfuzz.program.MutatableCode; +import dexfuzz.program.Mutation; +import dexfuzz.rawdex.Instruction; +import dexfuzz.rawdex.Opcode; + +import java.util.List; +import java.util.Random; + +public class RegisterClobber extends CodeMutator{ + + /** + * Every CodeMutator has an AssociatedMutation, representing the + * mutation that this CodeMutator can perform, to allow separate + * generateMutation() and applyMutation() phases, allowing serialization. + */ + public static class AssociatedMutation extends Mutation{ + + int regClobberIdx; + + @Override + public String getString() { + return Integer.toString(regClobberIdx); + } + + @Override + public void parseString(String[] elements) { + Integer.parseInt(elements[2]); + } + } + + // The following two methods are here for the benefit of MutationSerializer, + // so it can create a CodeMutator and get the correct associated Mutation, as it + // reads in mutations from a dump of mutations. + @Override + public Mutation getNewMutation() { + return new AssociatedMutation(); + } + + public RegisterClobber() {} + + public RegisterClobber(Random rng, MutationStats stats, List<Mutation> mutations) { + super(rng, stats, mutations); + likelihood = 40; + } + + @Override + protected boolean canMutate(MutatableCode mutatableCode) { + return mutatableCode.registersSize > 0; + } + + @Override + protected Mutation generateMutation(MutatableCode mutatableCode) { + int insertionIdx = rng.nextInt(mutatableCode.getInstructionCount()); + + AssociatedMutation mutation = new AssociatedMutation(); + mutation.setup(this.getClass(), mutatableCode); + mutation.regClobberIdx = insertionIdx; + return mutation; + } + + @Override + protected void applyMutation(Mutation uncastMutation) { + AssociatedMutation mutation = (AssociatedMutation) uncastMutation; + MutatableCode mutatableCode = mutation.mutatableCode; + + int totalRegUsed = mutatableCode.registersSize; + for (int i = 0; i < totalRegUsed; i++) { + MInsn newInsn = new MInsn(); + newInsn.insn = new Instruction(); + newInsn.insn.info = Instruction.getOpcodeInfo(Opcode.CONST_16); + newInsn.insn.vregA = i; + newInsn.insn.vregB = 0; + mutatableCode.insertInstructionAt(newInsn, mutation.regClobberIdx + i); + } + + Log.info("Assigned zero to the registers from 0 to " + (totalRegUsed - 1) + + " at index " + mutation.regClobberIdx); + stats.incrementStat("Clobbered the registers"); + } +}
\ No newline at end of file diff --git a/tools/jfuzz/jfuzz.cc b/tools/jfuzz/jfuzz.cc index 4cd23356bf..016d708565 100644 --- a/tools/jfuzz/jfuzz.cc +++ b/tools/jfuzz/jfuzz.cc @@ -18,8 +18,8 @@ #include <random> #include <inttypes.h> -#include <stdlib.h> #include <stdio.h> +#include <stdlib.h> #include <string.h> #include <unistd.h> diff --git a/tools/run-jdwp-tests.sh b/tools/run-jdwp-tests.sh index 225fb394d1..17c84b4f84 100755 --- a/tools/run-jdwp-tests.sh +++ b/tools/run-jdwp-tests.sh @@ -19,6 +19,11 @@ if [ ! -d libcore ]; then exit 1 fi +if [ -z "$ANDROID_JAVA_TOOLCHAIN" ] ; then + source build/envsetup.sh + setpaths # include platform prebuilt java, javac, etc in $PATH. +fi + if [ -z "$ANDROID_HOST_OUT" ] ; then ANDROID_HOST_OUT=${OUT_DIR-$ANDROID_BUILD_TOP/out}/host/linux-x86 fi diff --git a/tools/run-libcore-tests.sh b/tools/run-libcore-tests.sh index eef74d27ab..d549098b0a 100755 --- a/tools/run-libcore-tests.sh +++ b/tools/run-libcore-tests.sh @@ -19,6 +19,11 @@ if [ ! -d libcore ]; then exit 1 fi +if [ -z "$ANDROID_JAVA_TOOLCHAIN" ] ; then + source build/envsetup.sh + setpaths # include platform prebuilt java, javac, etc in $PATH. +fi + if [ -z "$ANDROID_PRODUCT_OUT" ] ; then JAVA_LIBRARIES=out/target/common/obj/JAVA_LIBRARIES else |