diff options
64 files changed, 853 insertions, 1007 deletions
diff --git a/cmdline/cmdline_parser_test.cc b/cmdline/cmdline_parser_test.cc index 9f12f64a31..07639e8a7d 100644 --- a/cmdline/cmdline_parser_test.cc +++ b/cmdline/cmdline_parser_test.cc @@ -35,7 +35,7 @@ namespace art { return lhs.enabled_ == rhs.enabled_ && lhs.min_save_period_ms_ == rhs.min_save_period_ms_ && lhs.save_resolved_classes_delay_ms_ == rhs.save_resolved_classes_delay_ms_ && - lhs.startup_method_samples_ == rhs.startup_method_samples_ && + lhs.hot_startup_method_samples_ == rhs.hot_startup_method_samples_ && lhs.min_methods_to_save_ == rhs.min_methods_to_save_ && lhs.min_classes_to_save_ == rhs.min_classes_to_save_ && lhs.min_notification_before_wake_ == rhs.min_notification_before_wake_ && @@ -490,7 +490,7 @@ TEST_F(CmdlineParserTest, ProfileSaverOptions) { "-Xjitsaveprofilinginfo " "-Xps-min-save-period-ms:1 " "-Xps-save-resolved-classes-delay-ms:2 " - "-Xps-startup-method-samples:3 " + "-Xps-hot-startup-method-samples:3 " "-Xps-min-methods-to-save:4 " "-Xps-min-classes-to-save:5 " "-Xps-min-notification-before-wake:6 " diff --git a/cmdline/cmdline_types.h b/cmdline/cmdline_types.h index 0d2aed8ad1..185a0e403e 100644 --- a/cmdline/cmdline_types.h +++ b/cmdline/cmdline_types.h @@ -727,10 +727,10 @@ struct CmdlineType<ProfileSaverOptions> : CmdlineTypeParser<ProfileSaverOptions> &ProfileSaverOptions::save_resolved_classes_delay_ms_, type_parser.Parse(suffix)); } - if (android::base::StartsWith(option, "startup-method-samples:")) { + if (android::base::StartsWith(option, "hot-startup-method-samples:")) { CmdlineType<unsigned int> type_parser; return ParseInto(existing, - &ProfileSaverOptions::startup_method_samples_, + &ProfileSaverOptions::hot_startup_method_samples_, type_parser.Parse(suffix)); } if (android::base::StartsWith(option, "min-methods-to-save:")) { diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc index 93f678c64a..29413d9a0a 100644 --- a/compiler/driver/compiler_driver.cc +++ b/compiler/driver/compiler_driver.cc @@ -1000,8 +1000,9 @@ bool CompilerDriver::ShouldCompileBasedOnProfile(const MethodReference& method_r if (profile_compilation_info_ == nullptr) { return false; } - // TODO: Revisit compiling all startup methods. b/36457259 - bool result = profile_compilation_info_->IsStartupOrHotMethod(method_ref); + // Compile only hot methods, it is the profile saver's job to decide what startup methods to mark + // as hot. + bool result = profile_compilation_info_->ContainsHotMethod(method_ref); if (kDebugProfileGuidedCompilation) { LOG(INFO) << "[ProfileGuidedCompilation] " @@ -2240,7 +2241,7 @@ class InitializeClassVisitor : public CompilationVisitor { public: explicit InitializeClassVisitor(const ParallelCompilationManager* manager) : manager_(manager) {} - void Visit(size_t class_def_index) OVERRIDE { + void Visit(size_t class_def_index) REQUIRES(!Locks::mutator_lock_) OVERRIDE { ATRACE_CALL(); jobject jclass_loader = manager_->GetClassLoader(); const DexFile& dex_file = *manager_->GetDexFile(); @@ -2255,132 +2256,89 @@ class InitializeClassVisitor : public CompilationVisitor { Handle<mirror::Class> klass( hs.NewHandle(manager_->GetClassLinker()->FindClass(soa.Self(), descriptor, class_loader))); - if (klass != nullptr && !SkipClass(manager_->GetClassLoader(), dex_file, klass.Get())) { - TryInitializeClass(klass, class_loader); - } - // Clear any class not found or verification exceptions. - soa.Self()->ClearException(); - } - - // A helper function for initializing klass. - void TryInitializeClass(Handle<mirror::Class> klass, Handle<mirror::ClassLoader>& class_loader) - REQUIRES_SHARED(Locks::mutator_lock_) { - const DexFile& dex_file = klass->GetDexFile(); - const DexFile::ClassDef* class_def = klass->GetClassDef(); - const DexFile::TypeId& class_type_id = dex_file.GetTypeId(class_def->class_idx_); - const char* descriptor = dex_file.StringDataByIdx(class_type_id.descriptor_idx_); - ScopedObjectAccessUnchecked soa(Thread::Current()); - StackHandleScope<3> hs(soa.Self()); - - mirror::Class::Status old_status = klass->GetStatus();; - // Only try to initialize classes that were successfully verified. - if (klass->IsVerified()) { - // Attempt to initialize the class but bail if we either need to initialize the super-class - // or static fields. - manager_->GetClassLinker()->EnsureInitialized(soa.Self(), klass, false, false); - old_status = klass->GetStatus(); - if (!klass->IsInitialized()) { - // We don't want non-trivial class initialization occurring on multiple threads due to - // deadlock problems. For example, a parent class is initialized (holding its lock) that - // refers to a sub-class in its static/class initializer causing it to try to acquire the - // sub-class' lock. While on a second thread the sub-class is initialized (holding its lock) - // after first initializing its parents, whose locks are acquired. This leads to a - // parent-to-child and a child-to-parent lock ordering and consequent potential deadlock. - // We need to use an ObjectLock due to potential suspension in the interpreting code. Rather - // than use a special Object for the purpose we use the Class of java.lang.Class. - Handle<mirror::Class> h_klass(hs.NewHandle(klass->GetClass())); - ObjectLock<mirror::Class> lock(soa.Self(), h_klass); - // Attempt to initialize allowing initialization of parent classes but still not static - // fields. - bool is_superclass_initialized = true; - if (!manager_->GetCompiler()->GetCompilerOptions().IsAppImage()) { - // If not an app image case, the compiler won't initialize too much things and do a fast - // fail, don't check dependencies. + if (klass != nullptr && !SkipClass(jclass_loader, dex_file, klass.Get())) { + // Only try to initialize classes that were successfully verified. + if (klass->IsVerified()) { + // Attempt to initialize the class but bail if we either need to initialize the super-class + // or static fields. + manager_->GetClassLinker()->EnsureInitialized(soa.Self(), klass, false, false); + if (!klass->IsInitialized()) { + // We don't want non-trivial class initialization occurring on multiple threads due to + // deadlock problems. For example, a parent class is initialized (holding its lock) that + // refers to a sub-class in its static/class initializer causing it to try to acquire the + // sub-class' lock. While on a second thread the sub-class is initialized (holding its lock) + // after first initializing its parents, whose locks are acquired. This leads to a + // parent-to-child and a child-to-parent lock ordering and consequent potential deadlock. + // We need to use an ObjectLock due to potential suspension in the interpreting code. Rather + // than use a special Object for the purpose we use the Class of java.lang.Class. + Handle<mirror::Class> h_klass(hs.NewHandle(klass->GetClass())); + ObjectLock<mirror::Class> lock(soa.Self(), h_klass); + // Attempt to initialize allowing initialization of parent classes but still not static + // fields. manager_->GetClassLinker()->EnsureInitialized(soa.Self(), klass, false, true); - } else { - // For app images, do the initialization recursively and resolve types encountered to make - // sure the compiler runs without error. - is_superclass_initialized = InitializeDependencies(klass, class_loader, soa.Self()); - if (is_superclass_initialized) { - manager_->GetClassLinker()->EnsureInitialized(soa.Self(), klass, false, true); - } - } - old_status = klass->GetStatus(); - // If superclass cannot be initialized, no need to proceed. - if (!klass->IsInitialized() && - is_superclass_initialized && - manager_->GetCompiler()->IsImageClass(descriptor)) { - bool can_init_static_fields = false; - if (manager_->GetCompiler()->GetCompilerOptions().IsBootImage()) { + if (!klass->IsInitialized()) { // We need to initialize static fields, we only do this for image classes that aren't // marked with the $NoPreloadHolder (which implies this should not be initialized early). - can_init_static_fields = !StringPiece(descriptor).ends_with("$NoPreloadHolder;"); - } else { - can_init_static_fields = manager_->GetCompiler()->GetCompilerOptions().IsAppImage() && - !soa.Self()->IsExceptionPending() && - NoClinitInDependency(klass, soa.Self(), &class_loader); - // TODO The checking for clinit can be removed since it's already - // checked when init superclass. Currently keep it because it contains - // processing of intern strings. Will be removed later when intern strings - // and clinit are both initialized. - } - - if (can_init_static_fields) { - VLOG(compiler) << "Initializing: " << descriptor; - // TODO multithreading support. We should ensure the current compilation thread has - // exclusive access to the runtime and the transaction. To achieve this, we could use - // 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); - bool success = manager_->GetClassLinker()->EnsureInitialized(soa.Self(), klass, true, - true); - // TODO we detach transaction from runtime to indicate we quit the transactional - // mode which prevents the GC from visiting objects modified during the transaction. - // Ensure GC is not run so don't access freed objects when aborting transaction. - - { - ScopedAssertNoThreadSuspension ants("Transaction end"); - runtime->ExitTransactionMode(); - - if (!success) { - CHECK(soa.Self()->IsExceptionPending()); - mirror::Throwable* exception = soa.Self()->GetException(); - VLOG(compiler) << "Initialization of " << descriptor << " aborted because of " - << exception->Dump(); - std::ostream* file_log = manager_->GetCompiler()-> - GetCompilerOptions().GetInitFailureOutput(); - if (file_log != nullptr) { - *file_log << descriptor << "\n"; - *file_log << exception->Dump() << "\n"; + bool can_init_static_fields = + manager_->GetCompiler()->GetCompilerOptions().IsBootImage() && + manager_->GetCompiler()->IsImageClass(descriptor) && + !StringPiece(descriptor).ends_with("$NoPreloadHolder;"); + if (can_init_static_fields) { + VLOG(compiler) << "Initializing: " << descriptor; + // TODO multithreading support. We should ensure the current compilation thread has + // exclusive access to the runtime and the transaction. To achieve this, we could use + // 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); + const mirror::Class::Status old_status = klass->GetStatus(); + bool success = manager_->GetClassLinker()->EnsureInitialized(soa.Self(), klass, true, + true); + // TODO we detach transaction from runtime to indicate we quit the transactional + // mode which prevents the GC from visiting objects modified during the transaction. + // Ensure GC is not run so don't access freed objects when aborting transaction. + + { + ScopedAssertNoThreadSuspension ants("Transaction end"); + runtime->ExitTransactionMode(); + + if (!success) { + CHECK(soa.Self()->IsExceptionPending()); + mirror::Throwable* exception = soa.Self()->GetException(); + VLOG(compiler) << "Initialization of " << descriptor << " aborted because of " + << exception->Dump(); + std::ostream* file_log = manager_->GetCompiler()-> + GetCompilerOptions().GetInitFailureOutput(); + if (file_log != nullptr) { + *file_log << descriptor << "\n"; + *file_log << exception->Dump() << "\n"; + } + soa.Self()->ClearException(); + transaction.Rollback(); + CHECK_EQ(old_status, klass->GetStatus()) << "Previous class status not restored"; } - soa.Self()->ClearException(); - transaction.Rollback(); - CHECK_EQ(old_status, klass->GetStatus()) << "Previous class status not restored"; } - } - if (!success) { - // On failure, still intern strings of static fields and seen in <clinit>, as these - // will be created in the zygote. This is separated from the transaction code just - // above as we will allocate strings, so must be allowed to suspend. - if (&klass->GetDexFile() == manager_->GetDexFile()) { + if (!success) { + // On failure, still intern strings of static fields and seen in <clinit>, as these + // will be created in the zygote. This is separated from the transaction code just + // above as we will allocate strings, so must be allowed to suspend. InternStrings(klass, class_loader); } } } + soa.Self()->AssertNoPendingException(); } - soa.Self()->AssertNoPendingException(); } + // Record the final class status if necessary. + ClassReference ref(manager_->GetDexFile(), class_def_index); + manager_->GetCompiler()->RecordClassStatus(ref, klass->GetStatus()); } - // Record the final class status if necessary. - ClassReference ref(&dex_file, klass->GetDexClassDefIndex()); - // Back up the status before doing initialization for static encoded fields, - // because the static encoded branch wants to keep the status to uninitialized. - manager_->GetCompiler()->RecordClassStatus(ref, old_status); + // Clear any class not found or verification exceptions. + soa.Self()->ClearException(); } private: @@ -2435,136 +2393,6 @@ class InitializeClassVisitor : public CompilationVisitor { } } - bool ResolveTypesOfMethods(Thread* self, ArtMethod* m) - REQUIRES_SHARED(Locks::mutator_lock_) { - auto rtn_type = m->GetReturnType(true); - if (rtn_type == nullptr) { - self->ClearException(); - return false; - } - const DexFile::TypeList* types = m->GetParameterTypeList(); - if (types != nullptr) { - for (uint32_t i = 0; i < types->Size(); ++i) { - dex::TypeIndex param_type_idx = types->GetTypeItem(i).type_idx_; - auto param_type = m->GetClassFromTypeIndex(param_type_idx, true); - if (param_type == nullptr) { - self->ClearException(); - return false; - } - } - } - return true; - } - - // Pre resolve types mentioned in all method signatures before start a transaction - // since ResolveType doesn't work in transaction mode. - bool PreResolveTypes(Thread* self, const Handle<mirror::Class>& klass) - REQUIRES_SHARED(Locks::mutator_lock_) { - PointerSize pointer_size = manager_->GetClassLinker()->GetImagePointerSize(); - for (ArtMethod& m : klass->GetMethods(pointer_size)) { - if (!ResolveTypesOfMethods(self, &m)) { - return false; - } - } - if (klass->IsInterface()) { - return true; - } else if (klass->HasSuperClass()) { - StackHandleScope<1> hs(self); - MutableHandle<mirror::Class> super_klass(hs.NewHandle<mirror::Class>(klass->GetSuperClass())); - for (int i = super_klass->GetVTableLength() - 1; i >= 0; --i) { - ArtMethod* m = klass->GetVTableEntry(i, pointer_size); - ArtMethod* super_m = super_klass->GetVTableEntry(i, pointer_size); - if (!ResolveTypesOfMethods(self, m) || !ResolveTypesOfMethods(self, super_m)) { - return false; - } - } - for (int32_t i = 0; i < klass->GetIfTableCount(); ++i) { - super_klass.Assign(klass->GetIfTable()->GetInterface(i)); - if (klass->GetClassLoader() != super_klass->GetClassLoader()) { - uint32_t num_methods = super_klass->NumVirtualMethods(); - for (uint32_t j = 0; j < num_methods; ++j) { - ArtMethod* m = klass->GetIfTable()->GetMethodArray(i)->GetElementPtrSize<ArtMethod*>( - j, pointer_size); - ArtMethod* super_m = super_klass->GetVirtualMethod(j, pointer_size); - if (!ResolveTypesOfMethods(self, m) || !ResolveTypesOfMethods(self, super_m)) { - return false; - } - } - } - } - } - return true; - } - - // Initialize the klass's dependencies recursively before initializing itself. - // Checking for interfaces is also necessary since interfaces can contain - // both default methods and static encoded fields. - bool InitializeDependencies(const Handle<mirror::Class>& klass, - Handle<mirror::ClassLoader> class_loader, - Thread* self) - REQUIRES_SHARED(Locks::mutator_lock_) { - if (klass->HasSuperClass()) { - ObjPtr<mirror::Class> super_class = klass->GetSuperClass(); - StackHandleScope<1> hs(self); - Handle<mirror::Class> handle_scope_super(hs.NewHandle(super_class)); - if (!handle_scope_super->IsInitialized()) { - this->TryInitializeClass(handle_scope_super, class_loader); - if (!handle_scope_super->IsInitialized()) { - return false; - } - } - } - - uint32_t num_if = klass->NumDirectInterfaces(); - for (size_t i = 0; i < num_if; i++) { - ObjPtr<mirror::Class> - interface = mirror::Class::GetDirectInterface(self, klass.Get(), i); - StackHandleScope<1> hs(self); - Handle<mirror::Class> handle_interface(hs.NewHandle(interface)); - - TryInitializeClass(handle_interface, class_loader); - - if (!handle_interface->IsInitialized()) { - return false; - } - } - - return PreResolveTypes(self, klass); - } - - // In this phase the classes containing class initializers are ignored. Make sure no - // clinit appears in kalss's super class chain and interfaces. - bool NoClinitInDependency(const Handle<mirror::Class>& klass, - Thread* self, - Handle<mirror::ClassLoader>* class_loader) - REQUIRES_SHARED(Locks::mutator_lock_) { - ArtMethod* clinit = - klass->FindClassInitializer(manager_->GetClassLinker()->GetImagePointerSize()); - if (clinit != nullptr) { - VLOG(compiler) << klass->PrettyClass() << ' ' << clinit->PrettyMethod(true); - return false; - } - if (klass->HasSuperClass()) { - ObjPtr<mirror::Class> super_class = klass->GetSuperClass(); - StackHandleScope<1> hs(self); - Handle<mirror::Class> handle_scope_super(hs.NewHandle(super_class)); - if (!NoClinitInDependency(handle_scope_super, self, class_loader)) - return false; - } - - uint32_t num_if = klass->NumDirectInterfaces(); - for (size_t i = 0; i < num_if; i++) { - ObjPtr<mirror::Class> - interface = mirror::Class::GetDirectInterface(self, klass.Get(), i); - StackHandleScope<1> hs(self); - Handle<mirror::Class> handle_interface(hs.NewHandle(interface)); - if (!NoClinitInDependency(handle_interface, self, class_loader)) - return false; - } - - return true; - } - const ParallelCompilationManager* const manager_; }; @@ -2584,10 +2412,7 @@ void CompilerDriver::InitializeClasses(jobject jni_class_loader, ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); ParallelCompilationManager context(class_linker, jni_class_loader, this, &dex_file, dex_files, init_thread_pool); - - if (GetCompilerOptions().IsBootImage() || GetCompilerOptions().IsAppImage()) { - // Set the concurrency thread to 1 to support initialization for App Images since transaction - // doesn't support multithreading now. + if (GetCompilerOptions().IsBootImage()) { // TODO: remove this when transactional mode supports multithreading. init_thread_count = 1U; } diff --git a/compiler/jit/jit_compiler.cc b/compiler/jit/jit_compiler.cc index fed1f48d65..66135414f7 100644 --- a/compiler/jit/jit_compiler.cc +++ b/compiler/jit/jit_compiler.cc @@ -189,18 +189,12 @@ JitCompiler::~JitCompiler() { bool JitCompiler::CompileMethod(Thread* self, ArtMethod* method, bool osr) { DCHECK(!method->IsProxyMethod()); + DCHECK(method->GetDeclaringClass()->IsResolved()); + TimingLogger logger("JIT compiler timing logger", true, VLOG_IS_ON(jit)); - StackHandleScope<2> hs(self); self->AssertNoPendingException(); Runtime* runtime = Runtime::Current(); - // Ensure the class is initialized. - Handle<mirror::Class> h_class(hs.NewHandle(method->GetDeclaringClass())); - if (!runtime->GetClassLinker()->EnsureInitialized(self, h_class, true, true)) { - VLOG(jit) << "JIT failed to initialize " << method->PrettyMethod(); - return false; - } - // Do the compilation. bool success = false; { diff --git a/compiler/optimizing/code_generator.cc b/compiler/optimizing/code_generator.cc index c918ee6687..136401898c 100644 --- a/compiler/optimizing/code_generator.cc +++ b/compiler/optimizing/code_generator.cc @@ -337,7 +337,7 @@ void CodeGenerator::CreateCommonInvokeLocationSummary( case HInvokeStaticOrDirect::MethodLoadKind::kRecursive: locations->SetInAt(call->GetSpecialInputIndex(), visitor->GetMethodLocation()); break; - case HInvokeStaticOrDirect::MethodLoadKind::kDexCacheViaMethod: + case HInvokeStaticOrDirect::MethodLoadKind::kRuntimeCall: locations->AddTemp(visitor->GetMethodLocation()); locations->SetInAt(call->GetSpecialInputIndex(), Location::RequiresRegister()); break; @@ -350,6 +350,34 @@ void CodeGenerator::CreateCommonInvokeLocationSummary( } } +void CodeGenerator::GenerateInvokeStaticOrDirectRuntimeCall( + HInvokeStaticOrDirect* invoke, Location temp, SlowPathCode* slow_path) { + MoveConstant(temp, invoke->GetDexMethodIndex()); + + // The access check is unnecessary but we do not want to introduce + // extra entrypoints for the codegens that do not support some + // invoke type and fall back to the runtime call. + + // Initialize to anything to silent compiler warnings. + QuickEntrypointEnum entrypoint = kQuickInvokeStaticTrampolineWithAccessCheck; + switch (invoke->GetInvokeType()) { + case kStatic: + entrypoint = kQuickInvokeStaticTrampolineWithAccessCheck; + break; + case kDirect: + entrypoint = kQuickInvokeDirectTrampolineWithAccessCheck; + break; + case kSuper: + entrypoint = kQuickInvokeSuperTrampolineWithAccessCheck; + break; + case kVirtual: + case kInterface: + LOG(FATAL) << "Unexpected invoke type: " << invoke->GetInvokeType(); + UNREACHABLE(); + } + + InvokeRuntime(entrypoint, invoke, invoke->GetDexPc(), slow_path); +} void CodeGenerator::GenerateInvokeUnresolvedRuntimeCall(HInvokeUnresolved* invoke) { MoveConstant(invoke->GetLocations()->GetTemp(0), invoke->GetDexMethodIndex()); diff --git a/compiler/optimizing/code_generator.h b/compiler/optimizing/code_generator.h index c9ba5c3357..7bf43f7971 100644 --- a/compiler/optimizing/code_generator.h +++ b/compiler/optimizing/code_generator.h @@ -496,6 +496,8 @@ class CodeGenerator : public DeletableArenaObject<kArenaAllocCodeGenerator> { static void CreateCommonInvokeLocationSummary( HInvoke* invoke, InvokeDexCallingConventionVisitor* visitor); + void GenerateInvokeStaticOrDirectRuntimeCall( + HInvokeStaticOrDirect* invoke, Location temp, SlowPathCode* slow_path); void GenerateInvokeUnresolvedRuntimeCall(HInvokeUnresolved* invoke); void GenerateInvokePolymorphicCall(HInvokePolymorphic* invoke); @@ -564,9 +566,11 @@ class CodeGenerator : public DeletableArenaObject<kArenaAllocCodeGenerator> { HInvokeStaticOrDirect* invoke) = 0; // Generate a call to a static or direct method. - virtual void GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invoke, Location temp) = 0; + virtual void GenerateStaticOrDirectCall( + HInvokeStaticOrDirect* invoke, Location temp, SlowPathCode* slow_path = nullptr) = 0; // Generate a call to a virtual method. - virtual void GenerateVirtualCall(HInvokeVirtual* invoke, Location temp) = 0; + virtual void GenerateVirtualCall( + HInvokeVirtual* invoke, Location temp, SlowPathCode* slow_path = nullptr) = 0; // Copy the result of a call into the given target. virtual void MoveFromReturnRegister(Location trg, Primitive::Type type) = 0; diff --git a/compiler/optimizing/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc index 097e4833d0..8300f8186e 100644 --- a/compiler/optimizing/code_generator_arm.cc +++ b/compiler/optimizing/code_generator_arm.cc @@ -47,7 +47,6 @@ static bool ExpectedPairLayout(Location location) { return ((location.low() & 1) == 0) && (location.low() + 1 == location.high()); } -static constexpr int kCurrentMethodStackOffset = 0; static constexpr Register kMethodRegisterArgument = R0; static constexpr Register kCoreAlwaysSpillRegister = R5; @@ -3589,7 +3588,6 @@ void InstructionCodeGeneratorARM::VisitInvokeStaticOrDirect(HInvokeStaticOrDirec LocationSummary* locations = invoke->GetLocations(); codegen_->GenerateStaticOrDirectCall( invoke, locations->HasTemps() ? locations->GetTemp(0) : Location::NoLocation()); - codegen_->RecordPcInfo(invoke, invoke->GetDexPc()); } void LocationsBuilderARM::HandleInvoke(HInvoke* invoke) { @@ -3613,7 +3611,6 @@ void InstructionCodeGeneratorARM::VisitInvokeVirtual(HInvokeVirtual* invoke) { codegen_->GenerateVirtualCall(invoke, invoke->GetLocations()->GetTemp(0)); DCHECK(!codegen_->IsLeafMethod()); - codegen_->RecordPcInfo(invoke, invoke->GetDexPc()); } void LocationsBuilderARM::VisitInvokeInterface(HInvokeInterface* invoke) { @@ -8955,7 +8952,8 @@ Register CodeGeneratorARM::GetInvokeStaticOrDirectExtraParameter(HInvokeStaticOr return location.AsRegister<Register>(); } -void CodeGeneratorARM::GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invoke, Location temp) { +void CodeGeneratorARM::GenerateStaticOrDirectCall( + HInvokeStaticOrDirect* invoke, Location temp, SlowPathCode* slow_path) { Location callee_method = temp; // For all kinds except kRecursive, callee will be in temp. switch (invoke->GetMethodLoadKind()) { case HInvokeStaticOrDirect::MethodLoadKind::kStringInit: { @@ -8992,28 +8990,9 @@ void CodeGeneratorARM::GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invoke, __ LoadFromOffset(kLoadWord, temp.AsRegister<Register>(), base_reg, offset); break; } - case HInvokeStaticOrDirect::MethodLoadKind::kDexCacheViaMethod: { - Location current_method = invoke->GetLocations()->InAt(invoke->GetSpecialInputIndex()); - Register method_reg; - Register reg = temp.AsRegister<Register>(); - if (current_method.IsRegister()) { - method_reg = current_method.AsRegister<Register>(); - } else { - DCHECK(invoke->GetLocations()->Intrinsified()); - DCHECK(!current_method.IsValid()); - method_reg = reg; - __ LoadFromOffset(kLoadWord, reg, SP, kCurrentMethodStackOffset); - } - // /* ArtMethod*[] */ temp = temp.ptr_sized_fields_->dex_cache_resolved_methods_; - __ LoadFromOffset(kLoadWord, - reg, - method_reg, - ArtMethod::DexCacheResolvedMethodsOffset(kArmPointerSize).Int32Value()); - // temp = temp[index_in_cache]; - // Note: Don't use invoke->GetTargetMethod() as it may point to a different dex file. - uint32_t index_in_cache = invoke->GetDexMethodIndex(); - __ LoadFromOffset(kLoadWord, reg, reg, CodeGenerator::GetCachePointerOffset(index_in_cache)); - break; + case HInvokeStaticOrDirect::MethodLoadKind::kRuntimeCall: { + GenerateInvokeStaticOrDirectRuntimeCall(invoke, temp, slow_path); + return; // No code pointer retrieval; the runtime performs the call directly. } } @@ -9030,11 +9009,13 @@ void CodeGeneratorARM::GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invoke, __ blx(LR); break; } + RecordPcInfo(invoke, invoke->GetDexPc(), slow_path); DCHECK(!IsLeafMethod()); } -void CodeGeneratorARM::GenerateVirtualCall(HInvokeVirtual* invoke, Location temp_location) { +void CodeGeneratorARM::GenerateVirtualCall( + HInvokeVirtual* invoke, Location temp_location, SlowPathCode* slow_path) { Register temp = temp_location.AsRegister<Register>(); uint32_t method_offset = mirror::Class::EmbeddedVTableEntryOffset( invoke->GetVTableIndex(), kArmPointerSize).Uint32Value(); @@ -9065,6 +9046,7 @@ void CodeGeneratorARM::GenerateVirtualCall(HInvokeVirtual* invoke, Location temp __ LoadFromOffset(kLoadWord, LR, temp, entry_point); // LR(); __ blx(LR); + RecordPcInfo(invoke, invoke->GetDexPc(), slow_path); } CodeGeneratorARM::PcRelativePatchInfo* CodeGeneratorARM::NewPcRelativeMethodPatch( diff --git a/compiler/optimizing/code_generator_arm.h b/compiler/optimizing/code_generator_arm.h index 5f37d3bff1..398b6ed7d8 100644 --- a/compiler/optimizing/code_generator_arm.h +++ b/compiler/optimizing/code_generator_arm.h @@ -455,8 +455,10 @@ class CodeGeneratorARM : public CodeGenerator { const HInvokeStaticOrDirect::DispatchInfo& desired_dispatch_info, HInvokeStaticOrDirect* invoke) OVERRIDE; - void GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invoke, Location temp) OVERRIDE; - void GenerateVirtualCall(HInvokeVirtual* invoke, Location temp) OVERRIDE; + void GenerateStaticOrDirectCall( + HInvokeStaticOrDirect* invoke, Location temp, SlowPathCode* slow_path = nullptr) OVERRIDE; + void GenerateVirtualCall( + HInvokeVirtual* invoke, Location temp, SlowPathCode* slow_path = nullptr) OVERRIDE; void MoveFromReturnRegister(Location trg, Primitive::Type type) OVERRIDE; diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc index d8e709c7a9..a84f8f3308 100644 --- a/compiler/optimizing/code_generator_arm64.cc +++ b/compiler/optimizing/code_generator_arm64.cc @@ -78,7 +78,6 @@ using helpers::VIXLRegCodeFromART; using helpers::WRegisterFrom; using helpers::XRegisterFrom; -static constexpr int kCurrentMethodStackOffset = 0; // The compare/jump sequence will generate about (1.5 * num_entries + 3) instructions. While jump // table version generates 7 instructions and num_entries literals. Compare/jump sequence will // generates less code/data with a small num_entries. @@ -4497,7 +4496,8 @@ HInvokeStaticOrDirect::DispatchInfo CodeGeneratorARM64::GetSupportedInvokeStatic return desired_dispatch_info; } -void CodeGeneratorARM64::GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invoke, Location temp) { +void CodeGeneratorARM64::GenerateStaticOrDirectCall( + HInvokeStaticOrDirect* invoke, Location temp, SlowPathCode* slow_path) { // Make sure that ArtMethod* is passed in kArtMethodRegister as per the calling convention. Location callee_method = temp; // For all kinds except kRecursive, callee will be in temp. switch (invoke->GetMethodLoadKind()) { @@ -4538,34 +4538,22 @@ void CodeGeneratorARM64::GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invok EmitLdrOffsetPlaceholder(ldr_label, XRegisterFrom(temp), XRegisterFrom(temp)); break; } - case HInvokeStaticOrDirect::MethodLoadKind::kDexCacheViaMethod: { - Location current_method = invoke->GetLocations()->InAt(invoke->GetSpecialInputIndex()); - Register reg = XRegisterFrom(temp); - Register method_reg; - if (current_method.IsRegister()) { - method_reg = XRegisterFrom(current_method); - } else { - DCHECK(invoke->GetLocations()->Intrinsified()); - DCHECK(!current_method.IsValid()); - method_reg = reg; - __ Ldr(reg.X(), MemOperand(sp, kCurrentMethodStackOffset)); - } - - // /* ArtMethod*[] */ temp = temp.ptr_sized_fields_->dex_cache_resolved_methods_; - __ Ldr(reg.X(), - MemOperand(method_reg.X(), - ArtMethod::DexCacheResolvedMethodsOffset(kArm64PointerSize).Int32Value())); - // temp = temp[index_in_cache]; - // Note: Don't use invoke->GetTargetMethod() as it may point to a different dex file. - uint32_t index_in_cache = invoke->GetDexMethodIndex(); - __ Ldr(reg.X(), MemOperand(reg.X(), GetCachePointerOffset(index_in_cache))); - break; + case HInvokeStaticOrDirect::MethodLoadKind::kRuntimeCall: { + GenerateInvokeStaticOrDirectRuntimeCall(invoke, temp, slow_path); + return; // No code pointer retrieval; the runtime performs the call directly. } } switch (invoke->GetCodePtrLocation()) { case HInvokeStaticOrDirect::CodePtrLocation::kCallSelf: - __ Bl(&frame_entry_label_); + { + // Use a scope to help guarantee that `RecordPcInfo()` records the correct pc. + ExactAssemblyScope eas(GetVIXLAssembler(), + kInstructionSize, + CodeBufferCheckScope::kExactSize); + __ bl(&frame_entry_label_); + RecordPcInfo(invoke, invoke->GetDexPc(), slow_path); + } break; case HInvokeStaticOrDirect::CodePtrLocation::kCallArtMethod: // LR = callee_method->entry_point_from_quick_compiled_code_; @@ -4573,14 +4561,13 @@ void CodeGeneratorARM64::GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invok XRegisterFrom(callee_method), ArtMethod::EntryPointFromQuickCompiledCodeOffset(kArm64PointerSize).Int32Value())); { - // To ensure that the pc position is recorded immediately after the `blr` instruction - // BLR must be the last instruction emitted in this function. - // Recording the pc will occur right after returning from this function. + // Use a scope to help guarantee that `RecordPcInfo()` records the correct pc. ExactAssemblyScope eas(GetVIXLAssembler(), kInstructionSize, CodeBufferCheckScope::kExactSize); // lr() __ blr(lr); + RecordPcInfo(invoke, invoke->GetDexPc(), slow_path); } break; } @@ -4588,7 +4575,8 @@ void CodeGeneratorARM64::GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invok DCHECK(!IsLeafMethod()); } -void CodeGeneratorARM64::GenerateVirtualCall(HInvokeVirtual* invoke, Location temp_in) { +void CodeGeneratorARM64::GenerateVirtualCall( + HInvokeVirtual* invoke, Location temp_in, SlowPathCode* slow_path) { // Use the calling convention instead of the location of the receiver, as // intrinsics may have put the receiver in a different register. In the intrinsics // slow path, the arguments have been moved to the right place, so here we are @@ -4622,12 +4610,11 @@ void CodeGeneratorARM64::GenerateVirtualCall(HInvokeVirtual* invoke, Location te // lr = temp->GetEntryPoint(); __ Ldr(lr, MemOperand(temp, entry_point.SizeValue())); { - // To ensure that the pc position is recorded immediately after the `blr` instruction - // BLR should be the last instruction emitted in this function. - // Recording the pc will occur right after returning from this function. + // Use a scope to help guarantee that `RecordPcInfo()` records the correct pc. ExactAssemblyScope eas(GetVIXLAssembler(), kInstructionSize, CodeBufferCheckScope::kExactSize); // lr(); __ blr(lr); + RecordPcInfo(invoke, invoke->GetDexPc(), slow_path); } } @@ -4824,7 +4811,6 @@ void InstructionCodeGeneratorARM64::VisitInvokeStaticOrDirect(HInvokeStaticOrDir LocationSummary* locations = invoke->GetLocations(); codegen_->GenerateStaticOrDirectCall( invoke, locations->HasTemps() ? locations->GetTemp(0) : Location::NoLocation()); - codegen_->RecordPcInfo(invoke, invoke->GetDexPc()); } void InstructionCodeGeneratorARM64::VisitInvokeVirtual(HInvokeVirtual* invoke) { @@ -4837,7 +4823,6 @@ void InstructionCodeGeneratorARM64::VisitInvokeVirtual(HInvokeVirtual* invoke) { EmissionCheckScope guard(GetVIXLAssembler(), kInvokeCodeMarginSizeInBytes); codegen_->GenerateVirtualCall(invoke, invoke->GetLocations()->GetTemp(0)); DCHECK(!codegen_->IsLeafMethod()); - codegen_->RecordPcInfo(invoke, invoke->GetDexPc()); } HLoadClass::LoadKind CodeGeneratorARM64::GetSupportedLoadClassKind( diff --git a/compiler/optimizing/code_generator_arm64.h b/compiler/optimizing/code_generator_arm64.h index 747fc9f0b1..5bb2ab57df 100644 --- a/compiler/optimizing/code_generator_arm64.h +++ b/compiler/optimizing/code_generator_arm64.h @@ -540,8 +540,10 @@ class CodeGeneratorARM64 : public CodeGenerator { const HInvokeStaticOrDirect::DispatchInfo& desired_dispatch_info, HInvokeStaticOrDirect* invoke) OVERRIDE; - void GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invoke, Location temp) OVERRIDE; - void GenerateVirtualCall(HInvokeVirtual* invoke, Location temp) OVERRIDE; + void GenerateStaticOrDirectCall( + HInvokeStaticOrDirect* invoke, Location temp, SlowPathCode* slow_path = nullptr) OVERRIDE; + void GenerateVirtualCall( + HInvokeVirtual* invoke, Location temp, SlowPathCode* slow_path = nullptr) OVERRIDE; void MoveFromReturnRegister(Location trg ATTRIBUTE_UNUSED, Primitive::Type type ATTRIBUTE_UNUSED) OVERRIDE { diff --git a/compiler/optimizing/code_generator_arm_vixl.cc b/compiler/optimizing/code_generator_arm_vixl.cc index 4d5f88e14a..d5e3723e68 100644 --- a/compiler/optimizing/code_generator_arm_vixl.cc +++ b/compiler/optimizing/code_generator_arm_vixl.cc @@ -76,7 +76,6 @@ static bool ExpectedPairLayout(Location location) { // Use a local definition to prevent copying mistakes. static constexpr size_t kArmWordSize = static_cast<size_t>(kArmPointerSize); static constexpr size_t kArmBitsPerWord = kArmWordSize * kBitsPerByte; -static constexpr int kCurrentMethodStackOffset = 0; static constexpr uint32_t kPackedSwitchCompareJumpThreshold = 7; // Reference load (except object array loads) is using LDR Rt, [Rn, #offset] which can handle @@ -3678,7 +3677,6 @@ void InstructionCodeGeneratorARMVIXL::VisitInvokeStaticOrDirect(HInvokeStaticOrD LocationSummary* locations = invoke->GetLocations(); codegen_->GenerateStaticOrDirectCall( invoke, locations->HasTemps() ? locations->GetTemp(0) : Location::NoLocation()); - codegen_->RecordPcInfo(invoke, invoke->GetDexPc()); } void LocationsBuilderARMVIXL::HandleInvoke(HInvoke* invoke) { @@ -3701,7 +3699,6 @@ void InstructionCodeGeneratorARMVIXL::VisitInvokeVirtual(HInvokeVirtual* invoke) } codegen_->GenerateVirtualCall(invoke, invoke->GetLocations()->GetTemp(0)); - codegen_->RecordPcInfo(invoke, invoke->GetDexPc()); DCHECK(!codegen_->IsLeafMethod()); } @@ -9120,7 +9117,7 @@ vixl32::Register CodeGeneratorARMVIXL::GetInvokeStaticOrDirectExtraParameter( } void CodeGeneratorARMVIXL::GenerateStaticOrDirectCall( - HInvokeStaticOrDirect* invoke, Location temp) { + HInvokeStaticOrDirect* invoke, Location temp, SlowPathCode* slow_path) { Location callee_method = temp; // For all kinds except kRecursive, callee will be in temp. switch (invoke->GetMethodLoadKind()) { case HInvokeStaticOrDirect::MethodLoadKind::kStringInit: { @@ -9151,36 +9148,22 @@ void CodeGeneratorARMVIXL::GenerateStaticOrDirectCall( GetAssembler()->LoadFromOffset(kLoadWord, RegisterFrom(temp), base_reg, offset); break; } - case HInvokeStaticOrDirect::MethodLoadKind::kDexCacheViaMethod: { - Location current_method = invoke->GetLocations()->InAt(invoke->GetSpecialInputIndex()); - vixl32::Register method_reg; - vixl32::Register reg = RegisterFrom(temp); - if (current_method.IsRegister()) { - method_reg = RegisterFrom(current_method); - } else { - DCHECK(invoke->GetLocations()->Intrinsified()); - DCHECK(!current_method.IsValid()); - method_reg = reg; - GetAssembler()->LoadFromOffset(kLoadWord, reg, sp, kCurrentMethodStackOffset); - } - // /* ArtMethod*[] */ temp = temp.ptr_sized_fields_->dex_cache_resolved_methods_; - GetAssembler()->LoadFromOffset( - kLoadWord, - reg, - method_reg, - ArtMethod::DexCacheResolvedMethodsOffset(kArmPointerSize).Int32Value()); - // temp = temp[index_in_cache]; - // Note: Don't use invoke->GetTargetMethod() as it may point to a different dex file. - uint32_t index_in_cache = invoke->GetDexMethodIndex(); - GetAssembler()->LoadFromOffset( - kLoadWord, reg, reg, CodeGenerator::GetCachePointerOffset(index_in_cache)); - break; + case HInvokeStaticOrDirect::MethodLoadKind::kRuntimeCall: { + GenerateInvokeStaticOrDirectRuntimeCall(invoke, temp, slow_path); + return; // No code pointer retrieval; the runtime performs the call directly. } } switch (invoke->GetCodePtrLocation()) { case HInvokeStaticOrDirect::CodePtrLocation::kCallSelf: - __ Bl(GetFrameEntryLabel()); + { + // Use a scope to help guarantee that `RecordPcInfo()` records the correct pc. + ExactAssemblyScope aas(GetVIXLAssembler(), + vixl32::k32BitT32InstructionSizeInBytes, + CodeBufferCheckScope::kMaximumSize); + __ bl(GetFrameEntryLabel()); + RecordPcInfo(invoke, invoke->GetDexPc(), slow_path); + } break; case HInvokeStaticOrDirect::CodePtrLocation::kCallArtMethod: // LR = callee_method->entry_point_from_quick_compiled_code_ @@ -9190,12 +9173,14 @@ void CodeGeneratorARMVIXL::GenerateStaticOrDirectCall( RegisterFrom(callee_method), ArtMethod::EntryPointFromQuickCompiledCodeOffset(kArmPointerSize).Int32Value()); { + // Use a scope to help guarantee that `RecordPcInfo()` records the correct pc. // blx in T32 has only 16bit encoding that's why a stricter check for the scope is used. ExactAssemblyScope aas(GetVIXLAssembler(), vixl32::k16BitT32InstructionSizeInBytes, CodeBufferCheckScope::kExactSize); // LR() __ blx(lr); + RecordPcInfo(invoke, invoke->GetDexPc(), slow_path); } break; } @@ -9203,7 +9188,8 @@ void CodeGeneratorARMVIXL::GenerateStaticOrDirectCall( DCHECK(!IsLeafMethod()); } -void CodeGeneratorARMVIXL::GenerateVirtualCall(HInvokeVirtual* invoke, Location temp_location) { +void CodeGeneratorARMVIXL::GenerateVirtualCall( + HInvokeVirtual* invoke, Location temp_location, SlowPathCode* slow_path) { vixl32::Register temp = RegisterFrom(temp_location); uint32_t method_offset = mirror::Class::EmbeddedVTableEntryOffset( invoke->GetVTableIndex(), kArmPointerSize).Uint32Value(); @@ -9239,15 +9225,16 @@ void CodeGeneratorARMVIXL::GenerateVirtualCall(HInvokeVirtual* invoke, Location GetAssembler()->LoadFromOffset(kLoadWord, temp, temp, method_offset); // LR = temp->GetEntryPoint(); GetAssembler()->LoadFromOffset(kLoadWord, lr, temp, entry_point); - // LR(); - // This `blx` *must* be the *last* instruction generated by this stub, so that calls to - // `RecordPcInfo()` immediately following record the correct pc. Use a scope to help guarantee - // that. - // blx in T32 has only 16bit encoding that's why a stricter check for the scope is used. - ExactAssemblyScope aas(GetVIXLAssembler(), - vixl32::k16BitT32InstructionSizeInBytes, - CodeBufferCheckScope::kExactSize); - __ blx(lr); + { + // Use a scope to help guarantee that `RecordPcInfo()` records the correct pc. + // blx in T32 has only 16bit encoding that's why a stricter check for the scope is used. + ExactAssemblyScope aas(GetVIXLAssembler(), + vixl32::k16BitT32InstructionSizeInBytes, + CodeBufferCheckScope::kExactSize); + // LR(); + __ blx(lr); + RecordPcInfo(invoke, invoke->GetDexPc(), slow_path); + } } CodeGeneratorARMVIXL::PcRelativePatchInfo* CodeGeneratorARMVIXL::NewPcRelativeMethodPatch( diff --git a/compiler/optimizing/code_generator_arm_vixl.h b/compiler/optimizing/code_generator_arm_vixl.h index f6e4de33a8..5320f71290 100644 --- a/compiler/optimizing/code_generator_arm_vixl.h +++ b/compiler/optimizing/code_generator_arm_vixl.h @@ -538,8 +538,10 @@ class CodeGeneratorARMVIXL : public CodeGenerator { const HInvokeStaticOrDirect::DispatchInfo& desired_dispatch_info, HInvokeStaticOrDirect* invoke) OVERRIDE; - void GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invoke, Location temp) OVERRIDE; - void GenerateVirtualCall(HInvokeVirtual* invoke, Location temp) OVERRIDE; + void GenerateStaticOrDirectCall( + HInvokeStaticOrDirect* invoke, Location temp, SlowPathCode* slow_path = nullptr) OVERRIDE; + void GenerateVirtualCall( + HInvokeVirtual* invoke, Location temp, SlowPathCode* slow_path = nullptr) OVERRIDE; void MoveFromReturnRegister(Location trg, Primitive::Type type) OVERRIDE; diff --git a/compiler/optimizing/code_generator_mips.cc b/compiler/optimizing/code_generator_mips.cc index 951d75a708..8560e3e5f6 100644 --- a/compiler/optimizing/code_generator_mips.cc +++ b/compiler/optimizing/code_generator_mips.cc @@ -7105,13 +7105,14 @@ HInvokeStaticOrDirect::DispatchInfo CodeGeneratorMIPS::GetSupportedInvokeStaticO break; } if (fallback_load) { - dispatch_info.method_load_kind = HInvokeStaticOrDirect::MethodLoadKind::kDexCacheViaMethod; + dispatch_info.method_load_kind = HInvokeStaticOrDirect::MethodLoadKind::kRuntimeCall; dispatch_info.method_load_data = 0; } return dispatch_info; } -void CodeGeneratorMIPS::GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invoke, Location temp) { +void CodeGeneratorMIPS::GenerateStaticOrDirectCall( + HInvokeStaticOrDirect* invoke, Location temp, SlowPathCode* slow_path) { // All registers are assumed to be correctly set up per the calling convention. Location callee_method = temp; // For all kinds except kRecursive, callee will be in temp. HInvokeStaticOrDirect::MethodLoadKind method_load_kind = invoke->GetMethodLoadKind(); @@ -7165,33 +7166,9 @@ void CodeGeneratorMIPS::GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invoke __ LoadFromOffset(kLoadWord, temp.AsRegister<Register>(), base_reg, offset); } break; - case HInvokeStaticOrDirect::MethodLoadKind::kDexCacheViaMethod: { - Location current_method = invoke->GetLocations()->InAt(invoke->GetSpecialInputIndex()); - Register reg = temp.AsRegister<Register>(); - Register method_reg; - if (current_method.IsRegister()) { - method_reg = current_method.AsRegister<Register>(); - } else { - // TODO: use the appropriate DCHECK() here if possible. - // DCHECK(invoke->GetLocations()->Intrinsified()); - DCHECK(!current_method.IsValid()); - method_reg = reg; - __ Lw(reg, SP, kCurrentMethodStackOffset); - } - - // temp = temp->dex_cache_resolved_methods_; - __ LoadFromOffset(kLoadWord, - reg, - method_reg, - ArtMethod::DexCacheResolvedMethodsOffset(kMipsPointerSize).Int32Value()); - // temp = temp[index_in_cache]; - // Note: Don't use invoke->GetTargetMethod() as it may point to a different dex file. - uint32_t index_in_cache = invoke->GetDexMethodIndex(); - __ LoadFromOffset(kLoadWord, - reg, - reg, - CodeGenerator::GetCachePointerOffset(index_in_cache)); - break; + case HInvokeStaticOrDirect::MethodLoadKind::kRuntimeCall: { + GenerateInvokeStaticOrDirectRuntimeCall(invoke, temp, slow_path); + return; // No code pointer retrieval; the runtime performs the call directly. } } @@ -7211,6 +7188,8 @@ void CodeGeneratorMIPS::GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invoke __ NopIfNoReordering(); break; } + RecordPcInfo(invoke, invoke->GetDexPc(), slow_path); + DCHECK(!IsLeafMethod()); } @@ -7228,10 +7207,10 @@ void InstructionCodeGeneratorMIPS::VisitInvokeStaticOrDirect(HInvokeStaticOrDire locations->HasTemps() ? locations->GetTemp(0) : Location::NoLocation()); - codegen_->RecordPcInfo(invoke, invoke->GetDexPc()); } -void CodeGeneratorMIPS::GenerateVirtualCall(HInvokeVirtual* invoke, Location temp_location) { +void CodeGeneratorMIPS::GenerateVirtualCall( + HInvokeVirtual* invoke, Location temp_location, SlowPathCode* slow_path) { // Use the calling convention instead of the location of the receiver, as // intrinsics may have put the receiver in a different register. In the intrinsics // slow path, the arguments have been moved to the right place, so here we are @@ -7263,6 +7242,7 @@ void CodeGeneratorMIPS::GenerateVirtualCall(HInvokeVirtual* invoke, Location tem // T9(); __ Jalr(T9); __ NopIfNoReordering(); + RecordPcInfo(invoke, invoke->GetDexPc(), slow_path); } void InstructionCodeGeneratorMIPS::VisitInvokeVirtual(HInvokeVirtual* invoke) { @@ -7272,7 +7252,6 @@ void InstructionCodeGeneratorMIPS::VisitInvokeVirtual(HInvokeVirtual* invoke) { codegen_->GenerateVirtualCall(invoke, invoke->GetLocations()->GetTemp(0)); DCHECK(!codegen_->IsLeafMethod()); - codegen_->RecordPcInfo(invoke, invoke->GetDexPc()); } void LocationsBuilderMIPS::VisitLoadClass(HLoadClass* cls) { diff --git a/compiler/optimizing/code_generator_mips.h b/compiler/optimizing/code_generator_mips.h index 736b5070d9..d774219ba9 100644 --- a/compiler/optimizing/code_generator_mips.h +++ b/compiler/optimizing/code_generator_mips.h @@ -552,8 +552,10 @@ class CodeGeneratorMIPS : public CodeGenerator { const HInvokeStaticOrDirect::DispatchInfo& desired_dispatch_info, HInvokeStaticOrDirect* invoke) OVERRIDE; - void GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invoke, Location temp); - void GenerateVirtualCall(HInvokeVirtual* invoke, Location temp) OVERRIDE; + void GenerateStaticOrDirectCall( + HInvokeStaticOrDirect* invoke, Location temp, SlowPathCode* slow_path = nullptr) OVERRIDE; + void GenerateVirtualCall( + HInvokeVirtual* invoke, Location temp, SlowPathCode* slow_path = nullptr) OVERRIDE; void MoveFromReturnRegister(Location trg ATTRIBUTE_UNUSED, Primitive::Type type ATTRIBUTE_UNUSED) OVERRIDE { diff --git a/compiler/optimizing/code_generator_mips64.cc b/compiler/optimizing/code_generator_mips64.cc index 6026814f04..da43c4e757 100644 --- a/compiler/optimizing/code_generator_mips64.cc +++ b/compiler/optimizing/code_generator_mips64.cc @@ -4915,7 +4915,8 @@ HInvokeStaticOrDirect::DispatchInfo CodeGeneratorMIPS64::GetSupportedInvokeStati return desired_dispatch_info; } -void CodeGeneratorMIPS64::GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invoke, Location temp) { +void CodeGeneratorMIPS64::GenerateStaticOrDirectCall( + HInvokeStaticOrDirect* invoke, Location temp, SlowPathCode* slow_path) { // All registers are assumed to be correctly set up per the calling convention. Location callee_method = temp; // For all kinds except kRecursive, callee will be in temp. HInvokeStaticOrDirect::MethodLoadKind method_load_kind = invoke->GetMethodLoadKind(); @@ -4956,33 +4957,9 @@ void CodeGeneratorMIPS64::GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invo __ Ld(temp.AsRegister<GpuRegister>(), AT, /* placeholder */ 0x5678); break; } - case HInvokeStaticOrDirect::MethodLoadKind::kDexCacheViaMethod: { - Location current_method = invoke->GetLocations()->InAt(invoke->GetSpecialInputIndex()); - GpuRegister reg = temp.AsRegister<GpuRegister>(); - GpuRegister method_reg; - if (current_method.IsRegister()) { - method_reg = current_method.AsRegister<GpuRegister>(); - } else { - // TODO: use the appropriate DCHECK() here if possible. - // DCHECK(invoke->GetLocations()->Intrinsified()); - DCHECK(!current_method.IsValid()); - method_reg = reg; - __ Ld(reg, SP, kCurrentMethodStackOffset); - } - - // temp = temp->dex_cache_resolved_methods_; - __ LoadFromOffset(kLoadDoubleword, - reg, - method_reg, - ArtMethod::DexCacheResolvedMethodsOffset(kMips64PointerSize).Int32Value()); - // temp = temp[index_in_cache]; - // Note: Don't use invoke->GetTargetMethod() as it may point to a different dex file. - uint32_t index_in_cache = invoke->GetDexMethodIndex(); - __ LoadFromOffset(kLoadDoubleword, - reg, - reg, - CodeGenerator::GetCachePointerOffset(index_in_cache)); - break; + case HInvokeStaticOrDirect::MethodLoadKind::kRuntimeCall: { + GenerateInvokeStaticOrDirectRuntimeCall(invoke, temp, slow_path); + return; // No code pointer retrieval; the runtime performs the call directly. } } @@ -5002,6 +4979,8 @@ void CodeGeneratorMIPS64::GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invo __ Nop(); break; } + RecordPcInfo(invoke, invoke->GetDexPc(), slow_path); + DCHECK(!IsLeafMethod()); } @@ -5019,10 +4998,10 @@ void InstructionCodeGeneratorMIPS64::VisitInvokeStaticOrDirect(HInvokeStaticOrDi locations->HasTemps() ? locations->GetTemp(0) : Location::NoLocation()); - codegen_->RecordPcInfo(invoke, invoke->GetDexPc()); } -void CodeGeneratorMIPS64::GenerateVirtualCall(HInvokeVirtual* invoke, Location temp_location) { +void CodeGeneratorMIPS64::GenerateVirtualCall( + HInvokeVirtual* invoke, Location temp_location, SlowPathCode* slow_path) { // Use the calling convention instead of the location of the receiver, as // intrinsics may have put the receiver in a different register. In the intrinsics // slow path, the arguments have been moved to the right place, so here we are @@ -5054,6 +5033,7 @@ void CodeGeneratorMIPS64::GenerateVirtualCall(HInvokeVirtual* invoke, Location t // T9(); __ Jalr(T9); __ Nop(); + RecordPcInfo(invoke, invoke->GetDexPc(), slow_path); } void InstructionCodeGeneratorMIPS64::VisitInvokeVirtual(HInvokeVirtual* invoke) { @@ -5063,7 +5043,6 @@ void InstructionCodeGeneratorMIPS64::VisitInvokeVirtual(HInvokeVirtual* invoke) codegen_->GenerateVirtualCall(invoke, invoke->GetLocations()->GetTemp(0)); DCHECK(!codegen_->IsLeafMethod()); - codegen_->RecordPcInfo(invoke, invoke->GetDexPc()); } void LocationsBuilderMIPS64::VisitLoadClass(HLoadClass* cls) { diff --git a/compiler/optimizing/code_generator_mips64.h b/compiler/optimizing/code_generator_mips64.h index 9c6b6f62cb..2e8af2185a 100644 --- a/compiler/optimizing/code_generator_mips64.h +++ b/compiler/optimizing/code_generator_mips64.h @@ -521,8 +521,10 @@ class CodeGeneratorMIPS64 : public CodeGenerator { const HInvokeStaticOrDirect::DispatchInfo& desired_dispatch_info, HInvokeStaticOrDirect* invoke) OVERRIDE; - void GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invoke, Location temp) OVERRIDE; - void GenerateVirtualCall(HInvokeVirtual* invoke, Location temp) OVERRIDE; + void GenerateStaticOrDirectCall( + HInvokeStaticOrDirect* invoke, Location temp, SlowPathCode* slow_path = nullptr) OVERRIDE; + void GenerateVirtualCall( + HInvokeVirtual* invoke, Location temp, SlowPathCode* slow_path = nullptr) OVERRIDE; void MoveFromReturnRegister(Location trg ATTRIBUTE_UNUSED, Primitive::Type type ATTRIBUTE_UNUSED) OVERRIDE { diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc index b8465cd9d5..ca921b843e 100644 --- a/compiler/optimizing/code_generator_x86.cc +++ b/compiler/optimizing/code_generator_x86.cc @@ -2204,7 +2204,6 @@ void InstructionCodeGeneratorX86::VisitInvokeStaticOrDirect(HInvokeStaticOrDirec LocationSummary* locations = invoke->GetLocations(); codegen_->GenerateStaticOrDirectCall( invoke, locations->HasTemps() ? locations->GetTemp(0) : Location::NoLocation()); - codegen_->RecordPcInfo(invoke, invoke->GetDexPc()); } void LocationsBuilderX86::VisitInvokeVirtual(HInvokeVirtual* invoke) { @@ -2228,7 +2227,6 @@ void InstructionCodeGeneratorX86::VisitInvokeVirtual(HInvokeVirtual* invoke) { codegen_->GenerateVirtualCall(invoke, invoke->GetLocations()->GetTemp(0)); DCHECK(!codegen_->IsLeafMethod()); - codegen_->RecordPcInfo(invoke, invoke->GetDexPc()); } void LocationsBuilderX86::VisitInvokeInterface(HInvokeInterface* invoke) { @@ -4530,7 +4528,8 @@ Register CodeGeneratorX86::GetInvokeStaticOrDirectExtraParameter(HInvokeStaticOr return location.AsRegister<Register>(); } -void CodeGeneratorX86::GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invoke, Location temp) { +void CodeGeneratorX86::GenerateStaticOrDirectCall( + HInvokeStaticOrDirect* invoke, Location temp, SlowPathCode* slow_path) { Location callee_method = temp; // For all kinds except kRecursive, callee will be in temp. switch (invoke->GetMethodLoadKind()) { case HInvokeStaticOrDirect::MethodLoadKind::kStringInit: { @@ -4566,26 +4565,9 @@ void CodeGeneratorX86::GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invoke, offset)); break; } - case HInvokeStaticOrDirect::MethodLoadKind::kDexCacheViaMethod: { - Location current_method = invoke->GetLocations()->InAt(invoke->GetSpecialInputIndex()); - Register method_reg; - Register reg = temp.AsRegister<Register>(); - if (current_method.IsRegister()) { - method_reg = current_method.AsRegister<Register>(); - } else { - DCHECK(invoke->GetLocations()->Intrinsified()); - DCHECK(!current_method.IsValid()); - method_reg = reg; - __ movl(reg, Address(ESP, kCurrentMethodStackOffset)); - } - // /* ArtMethod*[] */ temp = temp.ptr_sized_fields_->dex_cache_resolved_methods_; - __ movl(reg, Address(method_reg, - ArtMethod::DexCacheResolvedMethodsOffset(kX86PointerSize).Int32Value())); - // temp = temp[index_in_cache]; - // Note: Don't use invoke->GetTargetMethod() as it may point to a different dex file. - uint32_t index_in_cache = invoke->GetDexMethodIndex(); - __ movl(reg, Address(reg, CodeGenerator::GetCachePointerOffset(index_in_cache))); - break; + case HInvokeStaticOrDirect::MethodLoadKind::kRuntimeCall: { + GenerateInvokeStaticOrDirectRuntimeCall(invoke, temp, slow_path); + return; // No code pointer retrieval; the runtime performs the call directly. } } @@ -4600,11 +4582,13 @@ void CodeGeneratorX86::GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invoke, kX86PointerSize).Int32Value())); break; } + RecordPcInfo(invoke, invoke->GetDexPc(), slow_path); DCHECK(!IsLeafMethod()); } -void CodeGeneratorX86::GenerateVirtualCall(HInvokeVirtual* invoke, Location temp_in) { +void CodeGeneratorX86::GenerateVirtualCall( + HInvokeVirtual* invoke, Location temp_in, SlowPathCode* slow_path) { Register temp = temp_in.AsRegister<Register>(); uint32_t method_offset = mirror::Class::EmbeddedVTableEntryOffset( invoke->GetVTableIndex(), kX86PointerSize).Uint32Value(); @@ -4632,6 +4616,7 @@ void CodeGeneratorX86::GenerateVirtualCall(HInvokeVirtual* invoke, Location temp // call temp->GetEntryPoint(); __ call(Address( temp, ArtMethod::EntryPointFromQuickCompiledCodeOffset(kX86PointerSize).Int32Value())); + RecordPcInfo(invoke, invoke->GetDexPc(), slow_path); } void CodeGeneratorX86::RecordBootMethodPatch(HInvokeStaticOrDirect* invoke) { diff --git a/compiler/optimizing/code_generator_x86.h b/compiler/optimizing/code_generator_x86.h index 8130bd9d25..689f93e31a 100644 --- a/compiler/optimizing/code_generator_x86.h +++ b/compiler/optimizing/code_generator_x86.h @@ -408,9 +408,11 @@ class CodeGeneratorX86 : public CodeGenerator { HInvokeStaticOrDirect* invoke) OVERRIDE; // Generate a call to a static or direct method. - void GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invoke, Location temp) OVERRIDE; + void GenerateStaticOrDirectCall( + HInvokeStaticOrDirect* invoke, Location temp, SlowPathCode* slow_path = nullptr) OVERRIDE; // Generate a call to a virtual method. - void GenerateVirtualCall(HInvokeVirtual* invoke, Location temp) OVERRIDE; + void GenerateVirtualCall( + HInvokeVirtual* invoke, Location temp, SlowPathCode* slow_path = nullptr) OVERRIDE; void RecordBootMethodPatch(HInvokeStaticOrDirect* invoke); void RecordBootTypePatch(HLoadClass* load_class); diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc index 8dde298267..148f55139e 100644 --- a/compiler/optimizing/code_generator_x86_64.cc +++ b/compiler/optimizing/code_generator_x86_64.cc @@ -977,8 +977,8 @@ HInvokeStaticOrDirect::DispatchInfo CodeGeneratorX86_64::GetSupportedInvokeStati return desired_dispatch_info; } -void CodeGeneratorX86_64::GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invoke, - Location temp) { +void CodeGeneratorX86_64::GenerateStaticOrDirectCall( + HInvokeStaticOrDirect* invoke, Location temp, SlowPathCode* slow_path) { // All registers are assumed to be correctly set up. Location callee_method = temp; // For all kinds except kRecursive, callee will be in temp. @@ -1010,27 +1010,9 @@ void CodeGeneratorX86_64::GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invo __ Bind(NewPcRelativeDexCacheArrayPatch(invoke->GetDexFileForPcRelativeDexCache(), offset)); break; } - case HInvokeStaticOrDirect::MethodLoadKind::kDexCacheViaMethod: { - Location current_method = invoke->GetLocations()->InAt(invoke->GetSpecialInputIndex()); - Register method_reg; - CpuRegister reg = temp.AsRegister<CpuRegister>(); - if (current_method.IsRegister()) { - method_reg = current_method.AsRegister<Register>(); - } else { - DCHECK(invoke->GetLocations()->Intrinsified()); - DCHECK(!current_method.IsValid()); - method_reg = reg.AsRegister(); - __ movq(reg, Address(CpuRegister(RSP), kCurrentMethodStackOffset)); - } - // /* ArtMethod*[] */ temp = temp.ptr_sized_fields_->dex_cache_resolved_methods_; - __ movq(reg, - Address(CpuRegister(method_reg), - ArtMethod::DexCacheResolvedMethodsOffset(kX86_64PointerSize).SizeValue())); - // temp = temp[index_in_cache]; - // Note: Don't use invoke->GetTargetMethod() as it may point to a different dex file. - uint32_t index_in_cache = invoke->GetDexMethodIndex(); - __ movq(reg, Address(reg, CodeGenerator::GetCachePointerOffset(index_in_cache))); - break; + case HInvokeStaticOrDirect::MethodLoadKind::kRuntimeCall: { + GenerateInvokeStaticOrDirectRuntimeCall(invoke, temp, slow_path); + return; // No code pointer retrieval; the runtime performs the call directly. } } @@ -1045,11 +1027,13 @@ void CodeGeneratorX86_64::GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invo kX86_64PointerSize).SizeValue())); break; } + RecordPcInfo(invoke, invoke->GetDexPc(), slow_path); DCHECK(!IsLeafMethod()); } -void CodeGeneratorX86_64::GenerateVirtualCall(HInvokeVirtual* invoke, Location temp_in) { +void CodeGeneratorX86_64::GenerateVirtualCall( + HInvokeVirtual* invoke, Location temp_in, SlowPathCode* slow_path) { CpuRegister temp = temp_in.AsRegister<CpuRegister>(); size_t method_offset = mirror::Class::EmbeddedVTableEntryOffset( invoke->GetVTableIndex(), kX86_64PointerSize).SizeValue(); @@ -1078,6 +1062,7 @@ void CodeGeneratorX86_64::GenerateVirtualCall(HInvokeVirtual* invoke, Location t // call temp->GetEntryPoint(); __ call(Address(temp, ArtMethod::EntryPointFromQuickCompiledCodeOffset( kX86_64PointerSize).SizeValue())); + RecordPcInfo(invoke, invoke->GetDexPc(), slow_path); } void CodeGeneratorX86_64::RecordBootMethodPatch(HInvokeStaticOrDirect* invoke) { @@ -2387,7 +2372,6 @@ void InstructionCodeGeneratorX86_64::VisitInvokeStaticOrDirect(HInvokeStaticOrDi LocationSummary* locations = invoke->GetLocations(); codegen_->GenerateStaticOrDirectCall( invoke, locations->HasTemps() ? locations->GetTemp(0) : Location::NoLocation()); - codegen_->RecordPcInfo(invoke, invoke->GetDexPc()); } void LocationsBuilderX86_64::HandleInvoke(HInvoke* invoke) { @@ -2411,7 +2395,6 @@ void InstructionCodeGeneratorX86_64::VisitInvokeVirtual(HInvokeVirtual* invoke) codegen_->GenerateVirtualCall(invoke, invoke->GetLocations()->GetTemp(0)); DCHECK(!codegen_->IsLeafMethod()); - codegen_->RecordPcInfo(invoke, invoke->GetDexPc()); } void LocationsBuilderX86_64::VisitInvokeInterface(HInvokeInterface* invoke) { diff --git a/compiler/optimizing/code_generator_x86_64.h b/compiler/optimizing/code_generator_x86_64.h index 25479814d0..31debde0ce 100644 --- a/compiler/optimizing/code_generator_x86_64.h +++ b/compiler/optimizing/code_generator_x86_64.h @@ -404,8 +404,10 @@ class CodeGeneratorX86_64 : public CodeGenerator { const HInvokeStaticOrDirect::DispatchInfo& desired_dispatch_info, HInvokeStaticOrDirect* invoke) OVERRIDE; - void GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invoke, Location temp) OVERRIDE; - void GenerateVirtualCall(HInvokeVirtual* invoke, Location temp) OVERRIDE; + void GenerateStaticOrDirectCall( + HInvokeStaticOrDirect* invoke, Location temp, SlowPathCode* slow_path = nullptr) OVERRIDE; + void GenerateVirtualCall( + HInvokeVirtual* invoke, Location temp, SlowPathCode* slow_path = nullptr) OVERRIDE; void RecordBootMethodPatch(HInvokeStaticOrDirect* invoke); void RecordBootTypePatch(HLoadClass* load_class); diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc index 9be6a512f5..142c95780e 100644 --- a/compiler/optimizing/inliner.cc +++ b/compiler/optimizing/inliner.cc @@ -56,7 +56,7 @@ static constexpr size_t kMaximumNumberOfInstructionsForSmallMethod = 3; // Limit the number of dex registers that we accumulate while inlining // to avoid creating large amount of nested environments. -static constexpr size_t kMaximumNumberOfCumulatedDexRegisters = 64; +static constexpr size_t kMaximumNumberOfCumulatedDexRegisters = 32; // Limit recursive call inlining, which do not benefit from too // much inlining compared to code locality. diff --git a/compiler/optimizing/instruction_builder.cc b/compiler/optimizing/instruction_builder.cc index df9e7164ed..a73b1246d8 100644 --- a/compiler/optimizing/instruction_builder.cc +++ b/compiler/optimizing/instruction_builder.cc @@ -888,7 +888,7 @@ bool HInstructionBuilder::BuildInvoke(const Instruction& instruction, } HInvokeStaticOrDirect::DispatchInfo dispatch_info = { - HInvokeStaticOrDirect::MethodLoadKind::kDexCacheViaMethod, + HInvokeStaticOrDirect::MethodLoadKind::kRuntimeCall, HInvokeStaticOrDirect::CodePtrLocation::kCallArtMethod, 0u }; diff --git a/compiler/optimizing/intrinsics_arm64.cc b/compiler/optimizing/intrinsics_arm64.cc index 990a773a95..37d79814be 100644 --- a/compiler/optimizing/intrinsics_arm64.cc +++ b/compiler/optimizing/intrinsics_arm64.cc @@ -124,12 +124,12 @@ class IntrinsicSlowPathARM64 : public SlowPathCodeARM64 { // are no pools emitted. vixl::EmissionCheckScope guard(codegen->GetVIXLAssembler(), kInvokeCodeMarginSizeInBytes); if (invoke_->IsInvokeStaticOrDirect()) { - codegen->GenerateStaticOrDirectCall(invoke_->AsInvokeStaticOrDirect(), - LocationFrom(kArtMethodRegister)); + codegen->GenerateStaticOrDirectCall( + invoke_->AsInvokeStaticOrDirect(), LocationFrom(kArtMethodRegister), this); } else { - codegen->GenerateVirtualCall(invoke_->AsInvokeVirtual(), LocationFrom(kArtMethodRegister)); + codegen->GenerateVirtualCall( + invoke_->AsInvokeVirtual(), LocationFrom(kArtMethodRegister), this); } - codegen->RecordPcInfo(invoke_, invoke_->GetDexPc(), this); } // Copy the result back to the expected output. diff --git a/compiler/optimizing/intrinsics_arm_vixl.cc b/compiler/optimizing/intrinsics_arm_vixl.cc index 0e04b9a950..3c9b613803 100644 --- a/compiler/optimizing/intrinsics_arm_vixl.cc +++ b/compiler/optimizing/intrinsics_arm_vixl.cc @@ -97,11 +97,10 @@ class IntrinsicSlowPathARMVIXL : public SlowPathCodeARMVIXL { Location method_loc = MoveArguments(codegen); if (invoke_->IsInvokeStaticOrDirect()) { - codegen->GenerateStaticOrDirectCall(invoke_->AsInvokeStaticOrDirect(), method_loc); + codegen->GenerateStaticOrDirectCall(invoke_->AsInvokeStaticOrDirect(), method_loc, this); } else { - codegen->GenerateVirtualCall(invoke_->AsInvokeVirtual(), method_loc); + codegen->GenerateVirtualCall(invoke_->AsInvokeVirtual(), method_loc, this); } - codegen->RecordPcInfo(invoke_, invoke_->GetDexPc(), this); // Copy the result back to the expected output. Location out = invoke_->GetLocations()->Out(); diff --git a/compiler/optimizing/intrinsics_mips.cc b/compiler/optimizing/intrinsics_mips.cc index ea3e9e5ec9..4cea6dfdfb 100644 --- a/compiler/optimizing/intrinsics_mips.cc +++ b/compiler/optimizing/intrinsics_mips.cc @@ -112,12 +112,12 @@ class IntrinsicSlowPathMIPS : public SlowPathCodeMIPS { MoveArguments(invoke_, codegen); if (invoke_->IsInvokeStaticOrDirect()) { - codegen->GenerateStaticOrDirectCall(invoke_->AsInvokeStaticOrDirect(), - Location::RegisterLocation(A0)); + codegen->GenerateStaticOrDirectCall( + invoke_->AsInvokeStaticOrDirect(), Location::RegisterLocation(A0), this); } else { - codegen->GenerateVirtualCall(invoke_->AsInvokeVirtual(), Location::RegisterLocation(A0)); + codegen->GenerateVirtualCall( + invoke_->AsInvokeVirtual(), Location::RegisterLocation(A0), this); } - codegen->RecordPcInfo(invoke_, invoke_->GetDexPc(), this); // Copy the result back to the expected output. Location out = invoke_->GetLocations()->Out(); diff --git a/compiler/optimizing/intrinsics_mips64.cc b/compiler/optimizing/intrinsics_mips64.cc index 2ecb1a3b02..d785567e0f 100644 --- a/compiler/optimizing/intrinsics_mips64.cc +++ b/compiler/optimizing/intrinsics_mips64.cc @@ -101,12 +101,12 @@ class IntrinsicSlowPathMIPS64 : public SlowPathCodeMIPS64 { MoveArguments(invoke_, codegen); if (invoke_->IsInvokeStaticOrDirect()) { - codegen->GenerateStaticOrDirectCall(invoke_->AsInvokeStaticOrDirect(), - Location::RegisterLocation(A0)); + codegen->GenerateStaticOrDirectCall( + invoke_->AsInvokeStaticOrDirect(), Location::RegisterLocation(A0), this); } else { - codegen->GenerateVirtualCall(invoke_->AsInvokeVirtual(), Location::RegisterLocation(A0)); + codegen->GenerateVirtualCall( + invoke_->AsInvokeVirtual(), Location::RegisterLocation(A0), this); } - codegen->RecordPcInfo(invoke_, invoke_->GetDexPc(), this); // Copy the result back to the expected output. Location out = invoke_->GetLocations()->Out(); diff --git a/compiler/optimizing/intrinsics_utils.h b/compiler/optimizing/intrinsics_utils.h index c1f9ae6425..8c69d9b643 100644 --- a/compiler/optimizing/intrinsics_utils.h +++ b/compiler/optimizing/intrinsics_utils.h @@ -56,11 +56,10 @@ class IntrinsicSlowPath : public SlowPathCode { Location method_loc = MoveArguments(codegen); if (invoke_->IsInvokeStaticOrDirect()) { - codegen->GenerateStaticOrDirectCall(invoke_->AsInvokeStaticOrDirect(), method_loc); + codegen->GenerateStaticOrDirectCall(invoke_->AsInvokeStaticOrDirect(), method_loc, this); } else { - codegen->GenerateVirtualCall(invoke_->AsInvokeVirtual(), method_loc); + codegen->GenerateVirtualCall(invoke_->AsInvokeVirtual(), method_loc, this); } - codegen->RecordPcInfo(invoke_, invoke_->GetDexPc(), this); // Copy the result back to the expected output. Location out = invoke_->GetLocations()->Out(); diff --git a/compiler/optimizing/intrinsics_x86.cc b/compiler/optimizing/intrinsics_x86.cc index a9da15d2ce..6b4851d541 100644 --- a/compiler/optimizing/intrinsics_x86.cc +++ b/compiler/optimizing/intrinsics_x86.cc @@ -796,7 +796,6 @@ static void InvokeOutOfLineIntrinsic(CodeGeneratorX86* codegen, HInvoke* invoke) DCHECK(invoke->IsInvokeStaticOrDirect()); codegen->GenerateStaticOrDirectCall(invoke->AsInvokeStaticOrDirect(), Location::RegisterLocation(EAX)); - codegen->RecordPcInfo(invoke, invoke->GetDexPc()); // Copy the result back to the expected output. Location out = invoke->GetLocations()->Out(); diff --git a/compiler/optimizing/intrinsics_x86_64.cc b/compiler/optimizing/intrinsics_x86_64.cc index 8100645e54..ef98b7be30 100644 --- a/compiler/optimizing/intrinsics_x86_64.cc +++ b/compiler/optimizing/intrinsics_x86_64.cc @@ -567,7 +567,6 @@ static void InvokeOutOfLineIntrinsic(CodeGeneratorX86_64* codegen, HInvoke* invo DCHECK(invoke->IsInvokeStaticOrDirect()); codegen->GenerateStaticOrDirectCall( invoke->AsInvokeStaticOrDirect(), Location::RegisterLocation(RDI)); - codegen->RecordPcInfo(invoke, invoke->GetDexPc()); // Copy the result back to the expected output. Location out = invoke->GetLocations()->Out(); diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc index 9a91287670..e53209f941 100644 --- a/compiler/optimizing/nodes.cc +++ b/compiler/optimizing/nodes.cc @@ -2623,7 +2623,7 @@ const DexFile& HInvokeStaticOrDirect::GetDexFileForPcRelativeDexCache() const { } bool HInvokeStaticOrDirect::NeedsDexCacheOfDeclaringClass() const { - if (GetMethodLoadKind() != MethodLoadKind::kDexCacheViaMethod) { + if (GetMethodLoadKind() != MethodLoadKind::kRuntimeCall) { return false; } if (!IsIntrinsic()) { @@ -2645,8 +2645,8 @@ std::ostream& operator<<(std::ostream& os, HInvokeStaticOrDirect::MethodLoadKind return os << "DirectAddress"; case HInvokeStaticOrDirect::MethodLoadKind::kDexCachePcRelative: return os << "DexCachePcRelative"; - case HInvokeStaticOrDirect::MethodLoadKind::kDexCacheViaMethod: - return os << "DexCacheViaMethod"; + case HInvokeStaticOrDirect::MethodLoadKind::kRuntimeCall: + return os << "RuntimeCall"; default: LOG(FATAL) << "Unknown MethodLoadKind: " << static_cast<int>(rhs); UNREACHABLE(); diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h index befd0ff97b..74bb2ab3c4 100644 --- a/compiler/optimizing/nodes.h +++ b/compiler/optimizing/nodes.h @@ -4172,11 +4172,9 @@ class HInvokeStaticOrDirect FINAL : public HInvoke { // and we know that we can access the dex cache arrays using a PC-relative load. kDexCachePcRelative, - // Use ArtMethod* from the resolved methods of the compiled method's own ArtMethod*. - // Used for JIT when we need to use the dex cache. This is also the last-resort-kind - // used when other kinds are unavailable (say, dex cache arrays are not PC-relative) - // or unimplemented or impractical (i.e. slow) on a particular architecture. - kDexCacheViaMethod, + // Make a runtime call to resolve and call the method. This is the last-resort-kind + // used when other kinds are unimplemented on a particular architecture. + kRuntimeCall, }; // Determines the location of the code pointer. @@ -4376,7 +4374,7 @@ class HInvokeStaticOrDirect FINAL : public HInvoke { // Does this method load kind need the current method as an input? static bool NeedsCurrentMethodInput(MethodLoadKind kind) { - return kind == MethodLoadKind::kRecursive || kind == MethodLoadKind::kDexCacheViaMethod; + return kind == MethodLoadKind::kRecursive || kind == MethodLoadKind::kRuntimeCall; } DECLARE_INSTRUCTION(InvokeStaticOrDirect); diff --git a/dexlayout/dex_visualize.cc b/dexlayout/dex_visualize.cc index 829e9feda8..d279bcb65c 100644 --- a/dexlayout/dex_visualize.cc +++ b/dexlayout/dex_visualize.cc @@ -174,7 +174,7 @@ class Dumper { ProfileCompilationInfo* profile_info) { if (profile_info != nullptr) { uint32_t method_idx = method->GetMethodId()->GetIndex(); - if (!profile_info->ContainsMethod(MethodReference(dex_file, method_idx))) { + if (!profile_info->ContainsHotMethod(MethodReference(dex_file, method_idx))) { return; } } diff --git a/runtime/art_method-inl.h b/runtime/art_method-inl.h index d1afcb8dd3..07ca35fb00 100644 --- a/runtime/art_method-inl.h +++ b/runtime/art_method-inl.h @@ -36,7 +36,9 @@ #include "mirror/string.h" #include "oat.h" #include "obj_ptr-inl.h" +#include "primitive.h" #include "quick/quick_method_frame_info.h" +#include "read_barrier-inl.h" #include "runtime-inl.h" #include "scoped_thread_state_change-inl.h" #include "thread-current-inl.h" @@ -340,6 +342,10 @@ inline const char* ArtMethod::GetReturnTypeDescriptor() { return dex_file->GetTypeDescriptor(dex_file->GetTypeId(proto_id.return_type_idx_)); } +inline Primitive::Type ArtMethod::GetReturnTypePrimitive() { + return Primitive::GetType(GetReturnTypeDescriptor()[0]); +} + inline const char* ArtMethod::GetTypeDescriptorFromTypeIdx(dex::TypeIndex type_idx) { DCHECK(!IsProxyMethod()); const DexFile* dex_file = GetDexFile(); diff --git a/runtime/art_method.h b/runtime/art_method.h index d8dfdd7959..3a8d279606 100644 --- a/runtime/art_method.h +++ b/runtime/art_method.h @@ -27,6 +27,7 @@ #include "modifiers.h" #include "obj_ptr.h" #include "offsets.h" +#include "primitive.h" #include "read_barrier_option.h" namespace art { @@ -569,6 +570,8 @@ class ArtMethod FINAL { const char* GetReturnTypeDescriptor() REQUIRES_SHARED(Locks::mutator_lock_); + ALWAYS_INLINE Primitive::Type GetReturnTypePrimitive() REQUIRES_SHARED(Locks::mutator_lock_); + const char* GetTypeDescriptorFromTypeIdx(dex::TypeIndex type_idx) REQUIRES_SHARED(Locks::mutator_lock_); diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index a81c832450..0921bd6783 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -55,7 +55,6 @@ #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/scoped_gc_critical_section.h" #include "gc/space/image_space.h" @@ -89,7 +88,6 @@ #include "mirror/method_handles_lookup.h" #include "mirror/object-inl.h" #include "mirror/object_array-inl.h" -#include "mirror/object-refvisitor-inl.h" #include "mirror/proxy.h" #include "mirror/reference-inl.h" #include "mirror/stack_trace_element.h" @@ -1195,63 +1193,6 @@ class VerifyDeclaringClassVisitor : public ArtMethodVisitor { gc::accounting::HeapBitmap* const live_bitmap_; }; -class FixupInternVisitor { - public: - ALWAYS_INLINE ObjPtr<mirror::Object> TryInsertIntern(mirror::Object* obj) const - REQUIRES_SHARED(Locks::mutator_lock_) { - if (obj != nullptr && obj->IsString()) { - const auto intern = Runtime::Current()->GetInternTable()->InternStrong(obj->AsString()); - return intern; - } - return obj; - } - - ALWAYS_INLINE void VisitRootIfNonNull( - mirror::CompressedReference<mirror::Object>* root) const - REQUIRES_SHARED(Locks::mutator_lock_) { - if (!root->IsNull()) { - VisitRoot(root); - } - } - - ALWAYS_INLINE void VisitRoot(mirror::CompressedReference<mirror::Object>* root) const - REQUIRES_SHARED(Locks::mutator_lock_) { - root->Assign(TryInsertIntern(root->AsMirrorPtr())); - } - - // Visit Class Fields - ALWAYS_INLINE void operator()(ObjPtr<mirror::Object> obj, - MemberOffset offset, - bool is_static ATTRIBUTE_UNUSED) const - REQUIRES_SHARED(Locks::mutator_lock_) { - // There could be overlap between ranges, we must avoid visiting the same reference twice. - // Avoid the class field since we already fixed it up in FixupClassVisitor. - if (offset.Uint32Value() != mirror::Object::ClassOffset().Uint32Value()) { - // Updating images, don't do a read barrier. - // Only string fields are fixed, don't do a verify. - mirror::Object* ref = obj->GetFieldObject<mirror::Object, kVerifyNone, kWithoutReadBarrier>( - offset); - obj->SetFieldObject<false, false>(offset, TryInsertIntern(ref)); - } - } - - void operator()(ObjPtr<mirror::Class> klass ATTRIBUTE_UNUSED, - ObjPtr<mirror::Reference> ref) const - REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(Locks::heap_bitmap_lock_) { - this->operator()(ref, mirror::Reference::ReferentOffset(), false); - } - - void operator()(mirror::Object* obj) const - REQUIRES_SHARED(Locks::mutator_lock_) { - if (obj->IsDexCache()) { - obj->VisitReferences<true, kVerifyNone, kWithoutReadBarrier>(*this, *this); - } else { - // Don't visit native roots for non-dex-cache - obj->VisitReferences<false, kVerifyNone, kWithoutReadBarrier>(*this, *this); - } - } -}; - // Copies data from one array to another array at the same position // if pred returns false. If there is a page of continuous data in // the src array for which pred consistently returns true then @@ -1344,7 +1285,6 @@ bool AppImageClassLoadersAndDexCachesHelper::Update( return false; } } - // Only add the classes to the class loader after the points where we can return false. for (size_t i = 0; i < num_dex_caches; i++) { ObjPtr<mirror::DexCache> dex_cache = dex_caches->Get(i); @@ -1508,21 +1448,6 @@ bool AppImageClassLoadersAndDexCachesHelper::Update( } } } - { - // Fixup all the literal strings happens at app images which are supposed to be interned. - ScopedTrace timing("Fixup String Intern in image and dex_cache"); - const auto& image_header = space->GetImageHeader(); - const auto bitmap = space->GetMarkBitmap(); // bitmap of objects - const uint8_t* target_base = space->GetMemMap()->Begin(); - const ImageSection& objects_section = - image_header.GetImageSection(ImageHeader::kSectionObjects); - - uintptr_t objects_begin = reinterpret_cast<uintptr_t>(target_base + objects_section.Offset()); - uintptr_t objects_end = reinterpret_cast<uintptr_t>(target_base + objects_section.End()); - - FixupInternVisitor fixup_intern_visitor; - bitmap->VisitMarkedRange(objects_begin, objects_end, fixup_intern_visitor); - } if (*out_forward_dex_cache_array) { ScopedTrace timing("Fixup ArtMethod dex cache arrays"); FixupArtMethodArrayVisitor visitor(header); diff --git a/runtime/debugger.cc b/runtime/debugger.cc index cfa56a5769..7e70b7564c 100644 --- a/runtime/debugger.cc +++ b/runtime/debugger.cc @@ -149,7 +149,9 @@ class DebugInstrumentationListener FINAL : public instrumentation::Instrumentati DebugInstrumentationListener() {} virtual ~DebugInstrumentationListener() {} - void MethodEntered(Thread* thread, mirror::Object* this_object, ArtMethod* method, + void MethodEntered(Thread* thread, + Handle<mirror::Object> this_object, + ArtMethod* method, uint32_t dex_pc) OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_) { if (method->IsNative()) { @@ -171,12 +173,15 @@ class DebugInstrumentationListener FINAL : public instrumentation::Instrumentati // also group it with other events for this location like BREAKPOINT or SINGLE_STEP. thread->SetDebugMethodEntry(); } else { - Dbg::UpdateDebugger(thread, this_object, method, 0, Dbg::kMethodEntry, nullptr); + Dbg::UpdateDebugger(thread, this_object.Get(), method, 0, Dbg::kMethodEntry, nullptr); } } - void MethodExited(Thread* thread, mirror::Object* this_object, ArtMethod* method, - uint32_t dex_pc, const JValue& return_value) + void MethodExited(Thread* thread, + Handle<mirror::Object> this_object, + ArtMethod* method, + uint32_t dex_pc, + const JValue& return_value) OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_) { if (method->IsNative()) { // TODO: post location events is a suspension point and native method entry stubs aren't. @@ -189,18 +194,22 @@ class DebugInstrumentationListener FINAL : public instrumentation::Instrumentati events |= Dbg::kMethodEntry; thread->ClearDebugMethodEntry(); } - Dbg::UpdateDebugger(thread, this_object, method, dex_pc, events, &return_value); + Dbg::UpdateDebugger(thread, this_object.Get(), method, dex_pc, events, &return_value); } - void MethodUnwind(Thread* thread ATTRIBUTE_UNUSED, mirror::Object* this_object ATTRIBUTE_UNUSED, - ArtMethod* method, uint32_t dex_pc) + void MethodUnwind(Thread* thread ATTRIBUTE_UNUSED, + Handle<mirror::Object> this_object ATTRIBUTE_UNUSED, + ArtMethod* method, + uint32_t dex_pc) OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_) { // We're not recorded to listen to this kind of event, so complain. LOG(ERROR) << "Unexpected method unwind event in debugger " << ArtMethod::PrettyMethod(method) << " " << dex_pc; } - void DexPcMoved(Thread* thread, mirror::Object* this_object, ArtMethod* method, + void DexPcMoved(Thread* thread, + Handle<mirror::Object> this_object, + ArtMethod* method, uint32_t new_dex_pc) OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_) { if (IsListeningToMethodExit() && IsReturn(method, new_dex_pc)) { @@ -217,26 +226,33 @@ class DebugInstrumentationListener FINAL : public instrumentation::Instrumentati events = Dbg::kMethodEntry; thread->ClearDebugMethodEntry(); } - Dbg::UpdateDebugger(thread, this_object, method, new_dex_pc, events, nullptr); + Dbg::UpdateDebugger(thread, this_object.Get(), method, new_dex_pc, events, nullptr); } } - void FieldRead(Thread* thread ATTRIBUTE_UNUSED, mirror::Object* this_object, - ArtMethod* method, uint32_t dex_pc, ArtField* field) + void FieldRead(Thread* thread ATTRIBUTE_UNUSED, + Handle<mirror::Object> this_object, + ArtMethod* method, + uint32_t dex_pc, + ArtField* field) OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_) { - Dbg::PostFieldAccessEvent(method, dex_pc, this_object, field); + Dbg::PostFieldAccessEvent(method, dex_pc, this_object.Get(), field); } - void FieldWritten(Thread* thread ATTRIBUTE_UNUSED, mirror::Object* this_object, - ArtMethod* method, uint32_t dex_pc, ArtField* field, + void FieldWritten(Thread* thread ATTRIBUTE_UNUSED, + Handle<mirror::Object> this_object, + ArtMethod* method, + uint32_t dex_pc, + ArtField* field, const JValue& field_value) OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_) { - Dbg::PostFieldModificationEvent(method, dex_pc, this_object, field, &field_value); + Dbg::PostFieldModificationEvent(method, dex_pc, this_object.Get(), field, &field_value); } - void ExceptionCaught(Thread* thread ATTRIBUTE_UNUSED, mirror::Throwable* exception_object) + void ExceptionCaught(Thread* thread ATTRIBUTE_UNUSED, + Handle<mirror::Throwable> exception_object) OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_) { - Dbg::PostException(exception_object); + Dbg::PostException(exception_object.Get()); } // We only care about branches in the Jit. @@ -248,10 +264,10 @@ class DebugInstrumentationListener FINAL : public instrumentation::Instrumentati // We only care about invokes in the Jit. void InvokeVirtualOrInterface(Thread* thread ATTRIBUTE_UNUSED, - mirror::Object*, + Handle<mirror::Object> this_object ATTRIBUTE_UNUSED, ArtMethod* method, uint32_t dex_pc, - ArtMethod*) + ArtMethod* target ATTRIBUTE_UNUSED) OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_) { LOG(ERROR) << "Unexpected invoke event in debugger " << ArtMethod::PrettyMethod(method) << " " << dex_pc; diff --git a/runtime/instrumentation.cc b/runtime/instrumentation.cc index d862ff2708..540b2e7e9b 100644 --- a/runtime/instrumentation.cc +++ b/runtime/instrumentation.cc @@ -20,6 +20,7 @@ #include "arch/context.h" #include "art_method-inl.h" +#include "art_field-inl.h" #include "atomic.h" #include "class_linker.h" #include "debugger.h" @@ -31,6 +32,7 @@ #include "interpreter/interpreter.h" #include "jit/jit.h" #include "jit/jit_code_cache.h" +#include "jvalue-inl.h" #include "mirror/class-inl.h" #include "mirror/dex_cache.h" #include "mirror/object_array-inl.h" @@ -45,6 +47,30 @@ namespace instrumentation { constexpr bool kVerboseInstrumentation = false; +void InstrumentationListener::MethodExited(Thread* thread, + Handle<mirror::Object> this_object, + ArtMethod* method, + uint32_t dex_pc, + Handle<mirror::Object> return_value) { + DCHECK_EQ(method->GetInterfaceMethodIfProxy(kRuntimePointerSize)->GetReturnTypePrimitive(), + Primitive::kPrimNot); + JValue v; + v.SetL(return_value.Get()); + MethodExited(thread, this_object, method, dex_pc, v); +} + +void InstrumentationListener::FieldWritten(Thread* thread, + Handle<mirror::Object> this_object, + ArtMethod* method, + uint32_t dex_pc, + ArtField* field, + Handle<mirror::Object> field_value) { + DCHECK(!field->IsPrimitiveType()); + JValue v; + v.SetL(field_value.Get()); + FieldWritten(thread, this_object, method, dex_pc, field, v); +} + // Instrumentation works on non-inlined frames by updating returned PCs // of compiled frames. static constexpr StackVisitor::StackWalkKind kInstrumentationStackWalk = @@ -916,48 +942,75 @@ const void* Instrumentation::GetQuickCodeFor(ArtMethod* method, PointerSize poin return class_linker->GetQuickOatCodeFor(method); } -void Instrumentation::MethodEnterEventImpl(Thread* thread, mirror::Object* this_object, +void Instrumentation::MethodEnterEventImpl(Thread* thread, + ObjPtr<mirror::Object> this_object, ArtMethod* method, uint32_t dex_pc) const { if (HasMethodEntryListeners()) { + Thread* self = Thread::Current(); + StackHandleScope<1> hs(self); + Handle<mirror::Object> thiz(hs.NewHandle(this_object)); for (InstrumentationListener* listener : method_entry_listeners_) { if (listener != nullptr) { - listener->MethodEntered(thread, this_object, method, dex_pc); + listener->MethodEntered(thread, thiz, method, dex_pc); } } } } -void Instrumentation::MethodExitEventImpl(Thread* thread, mirror::Object* this_object, +void Instrumentation::MethodExitEventImpl(Thread* thread, + ObjPtr<mirror::Object> this_object, ArtMethod* method, - uint32_t dex_pc, const JValue& return_value) const { + uint32_t dex_pc, + const JValue& return_value) const { if (HasMethodExitListeners()) { - for (InstrumentationListener* listener : method_exit_listeners_) { - if (listener != nullptr) { - listener->MethodExited(thread, this_object, method, dex_pc, return_value); + Thread* self = Thread::Current(); + StackHandleScope<2> hs(self); + Handle<mirror::Object> thiz(hs.NewHandle(this_object)); + if (method->GetInterfaceMethodIfProxy(kRuntimePointerSize) + ->GetReturnTypePrimitive() != Primitive::kPrimNot) { + for (InstrumentationListener* listener : method_exit_listeners_) { + if (listener != nullptr) { + listener->MethodExited(thread, thiz, method, dex_pc, return_value); + } + } + } else { + Handle<mirror::Object> ret(hs.NewHandle(return_value.GetL())); + for (InstrumentationListener* listener : method_exit_listeners_) { + if (listener != nullptr) { + listener->MethodExited(thread, thiz, method, dex_pc, ret); + } } } } } -void Instrumentation::MethodUnwindEvent(Thread* thread, mirror::Object* this_object, +void Instrumentation::MethodUnwindEvent(Thread* thread, + mirror::Object* this_object, ArtMethod* method, uint32_t dex_pc) const { if (HasMethodUnwindListeners()) { + Thread* self = Thread::Current(); + StackHandleScope<1> hs(self); + Handle<mirror::Object> thiz(hs.NewHandle(this_object)); for (InstrumentationListener* listener : method_unwind_listeners_) { if (listener != nullptr) { - listener->MethodUnwind(thread, this_object, method, dex_pc); + listener->MethodUnwind(thread, thiz, method, dex_pc); } } } } -void Instrumentation::DexPcMovedEventImpl(Thread* thread, mirror::Object* this_object, +void Instrumentation::DexPcMovedEventImpl(Thread* thread, + ObjPtr<mirror::Object> this_object, ArtMethod* method, uint32_t dex_pc) const { + Thread* self = Thread::Current(); + StackHandleScope<1> hs(self); + Handle<mirror::Object> thiz(hs.NewHandle(this_object)); for (InstrumentationListener* listener : dex_pc_listeners_) { if (listener != nullptr) { - listener->DexPcMoved(thread, this_object, method, dex_pc); + listener->DexPcMoved(thread, thiz, method, dex_pc); } } } @@ -974,36 +1027,56 @@ void Instrumentation::BranchImpl(Thread* thread, } void Instrumentation::InvokeVirtualOrInterfaceImpl(Thread* thread, - mirror::Object* this_object, + ObjPtr<mirror::Object> this_object, ArtMethod* caller, uint32_t dex_pc, ArtMethod* callee) const { - // We cannot have thread suspension since that would cause the this_object parameter to - // potentially become a dangling pointer. An alternative could be to put it in a handle instead. - ScopedAssertNoThreadSuspension ants(__FUNCTION__); + Thread* self = Thread::Current(); + StackHandleScope<1> hs(self); + Handle<mirror::Object> thiz(hs.NewHandle(this_object)); for (InstrumentationListener* listener : invoke_virtual_or_interface_listeners_) { if (listener != nullptr) { - listener->InvokeVirtualOrInterface(thread, this_object, caller, dex_pc, callee); + listener->InvokeVirtualOrInterface(thread, thiz, caller, dex_pc, callee); } } } -void Instrumentation::FieldReadEventImpl(Thread* thread, mirror::Object* this_object, - ArtMethod* method, uint32_t dex_pc, +void Instrumentation::FieldReadEventImpl(Thread* thread, + ObjPtr<mirror::Object> this_object, + ArtMethod* method, + uint32_t dex_pc, ArtField* field) const { + Thread* self = Thread::Current(); + StackHandleScope<1> hs(self); + Handle<mirror::Object> thiz(hs.NewHandle(this_object)); for (InstrumentationListener* listener : field_read_listeners_) { if (listener != nullptr) { - listener->FieldRead(thread, this_object, method, dex_pc, field); + listener->FieldRead(thread, thiz, method, dex_pc, field); } } } -void Instrumentation::FieldWriteEventImpl(Thread* thread, mirror::Object* this_object, - ArtMethod* method, uint32_t dex_pc, - ArtField* field, const JValue& field_value) const { - for (InstrumentationListener* listener : field_write_listeners_) { - if (listener != nullptr) { - listener->FieldWritten(thread, this_object, method, dex_pc, field, field_value); +void Instrumentation::FieldWriteEventImpl(Thread* thread, + ObjPtr<mirror::Object> this_object, + ArtMethod* method, + uint32_t dex_pc, + ArtField* field, + const JValue& field_value) const { + Thread* self = Thread::Current(); + StackHandleScope<2> hs(self); + Handle<mirror::Object> thiz(hs.NewHandle(this_object)); + if (field->IsPrimitiveType()) { + for (InstrumentationListener* listener : field_write_listeners_) { + if (listener != nullptr) { + listener->FieldWritten(thread, thiz, method, dex_pc, field, field_value); + } + } + } else { + Handle<mirror::Object> val(hs.NewHandle(field_value.GetL())); + for (InstrumentationListener* listener : field_write_listeners_) { + if (listener != nullptr) { + listener->FieldWritten(thread, thiz, method, dex_pc, field, val); + } } } } @@ -1018,7 +1091,7 @@ void Instrumentation::ExceptionCaughtEvent(Thread* thread, thread->ClearException(); for (InstrumentationListener* listener : exception_caught_listeners_) { if (listener != nullptr) { - listener->ExceptionCaught(thread, h_exception.Get()); + listener->ExceptionCaught(thread, h_exception); } } thread->SetException(h_exception.Get()); diff --git a/runtime/instrumentation.h b/runtime/instrumentation.h index 01071a541f..363985fd62 100644 --- a/runtime/instrumentation.h +++ b/runtime/instrumentation.h @@ -36,6 +36,7 @@ namespace mirror { } // namespace mirror class ArtField; class ArtMethod; +template <typename T> class Handle; union JValue; class Thread; @@ -62,37 +63,70 @@ struct InstrumentationListener { virtual ~InstrumentationListener() {} // Call-back for when a method is entered. - virtual void MethodEntered(Thread* thread, mirror::Object* this_object, + virtual void MethodEntered(Thread* thread, + Handle<mirror::Object> this_object, ArtMethod* method, uint32_t dex_pc) REQUIRES_SHARED(Locks::mutator_lock_) = 0; - // Call-back for when a method is exited. - virtual void MethodExited(Thread* thread, mirror::Object* this_object, - ArtMethod* method, uint32_t dex_pc, + virtual void MethodExited(Thread* thread, + Handle<mirror::Object> this_object, + ArtMethod* method, + uint32_t dex_pc, + Handle<mirror::Object> return_value) + REQUIRES_SHARED(Locks::mutator_lock_); + + // Call-back for when a method is exited. The implementor should either handler-ize the return + // value (if appropriate) or use the alternate MethodExited callback instead if they need to + // go through a suspend point. + virtual void MethodExited(Thread* thread, + Handle<mirror::Object> this_object, + ArtMethod* method, + uint32_t dex_pc, const JValue& return_value) REQUIRES_SHARED(Locks::mutator_lock_) = 0; // Call-back for when a method is popped due to an exception throw. A method will either cause a // MethodExited call-back or a MethodUnwind call-back when its activation is removed. - virtual void MethodUnwind(Thread* thread, mirror::Object* this_object, - ArtMethod* method, uint32_t dex_pc) + virtual void MethodUnwind(Thread* thread, + Handle<mirror::Object> this_object, + ArtMethod* method, + uint32_t dex_pc) REQUIRES_SHARED(Locks::mutator_lock_) = 0; // Call-back for when the dex pc moves in a method. - virtual void DexPcMoved(Thread* thread, mirror::Object* this_object, - ArtMethod* method, uint32_t new_dex_pc) + virtual void DexPcMoved(Thread* thread, + Handle<mirror::Object> this_object, + ArtMethod* method, + uint32_t new_dex_pc) REQUIRES_SHARED(Locks::mutator_lock_) = 0; // Call-back for when we read from a field. - virtual void FieldRead(Thread* thread, mirror::Object* this_object, ArtMethod* method, - uint32_t dex_pc, ArtField* field) = 0; + virtual void FieldRead(Thread* thread, + Handle<mirror::Object> this_object, + ArtMethod* method, + uint32_t dex_pc, + ArtField* field) = 0; + + virtual void FieldWritten(Thread* thread, + Handle<mirror::Object> this_object, + ArtMethod* method, + uint32_t dex_pc, + ArtField* field, + Handle<mirror::Object> field_value) + REQUIRES_SHARED(Locks::mutator_lock_); // Call-back for when we write into a field. - virtual void FieldWritten(Thread* thread, mirror::Object* this_object, ArtMethod* method, - uint32_t dex_pc, ArtField* field, const JValue& field_value) = 0; + virtual void FieldWritten(Thread* thread, + Handle<mirror::Object> this_object, + ArtMethod* method, + uint32_t dex_pc, + ArtField* field, + const JValue& field_value) + REQUIRES_SHARED(Locks::mutator_lock_) = 0; // Call-back when an exception is caught. - virtual void ExceptionCaught(Thread* thread, mirror::Throwable* exception_object) + virtual void ExceptionCaught(Thread* thread, + Handle<mirror::Throwable> exception_object) REQUIRES_SHARED(Locks::mutator_lock_) = 0; // Call-back for when we execute a branch. @@ -104,11 +138,10 @@ struct InstrumentationListener { // Call-back for when we get an invokevirtual or an invokeinterface. virtual void InvokeVirtualOrInterface(Thread* thread, - mirror::Object* this_object, + Handle<mirror::Object> this_object, ArtMethod* caller, uint32_t dex_pc, ArtMethod* callee) - REQUIRES(Roles::uninterruptible_) REQUIRES_SHARED(Locks::mutator_lock_) = 0; }; @@ -323,8 +356,10 @@ class Instrumentation { } // Inform listeners that a method has been exited. - void MethodExitEvent(Thread* thread, mirror::Object* this_object, - ArtMethod* method, uint32_t dex_pc, + void MethodExitEvent(Thread* thread, + mirror::Object* this_object, + ArtMethod* method, + uint32_t dex_pc, const JValue& return_value) const REQUIRES_SHARED(Locks::mutator_lock_) { if (UNLIKELY(HasMethodExitListeners())) { @@ -465,31 +500,42 @@ class Instrumentation { // exclusive access to mutator lock which you can't get if the runtime isn't started. void SetEntrypointsInstrumented(bool instrumented) NO_THREAD_SAFETY_ANALYSIS; - void MethodEnterEventImpl(Thread* thread, mirror::Object* this_object, - ArtMethod* method, uint32_t dex_pc) const + void MethodEnterEventImpl(Thread* thread, + ObjPtr<mirror::Object> this_object, + ArtMethod* method, + uint32_t dex_pc) const REQUIRES_SHARED(Locks::mutator_lock_); - void MethodExitEventImpl(Thread* thread, mirror::Object* this_object, + void MethodExitEventImpl(Thread* thread, + ObjPtr<mirror::Object> this_object, ArtMethod* method, - uint32_t dex_pc, const JValue& return_value) const + uint32_t dex_pc, + const JValue& return_value) const REQUIRES_SHARED(Locks::mutator_lock_); - void DexPcMovedEventImpl(Thread* thread, mirror::Object* this_object, - ArtMethod* method, uint32_t dex_pc) const + void DexPcMovedEventImpl(Thread* thread, + ObjPtr<mirror::Object> this_object, + ArtMethod* method, + uint32_t dex_pc) const REQUIRES_SHARED(Locks::mutator_lock_); void BranchImpl(Thread* thread, ArtMethod* method, uint32_t dex_pc, int32_t offset) const REQUIRES_SHARED(Locks::mutator_lock_); void InvokeVirtualOrInterfaceImpl(Thread* thread, - mirror::Object* this_object, + ObjPtr<mirror::Object> this_object, ArtMethod* caller, uint32_t dex_pc, ArtMethod* callee) const REQUIRES_SHARED(Locks::mutator_lock_); - void FieldReadEventImpl(Thread* thread, mirror::Object* this_object, - ArtMethod* method, uint32_t dex_pc, - ArtField* field) const + void FieldReadEventImpl(Thread* thread, + ObjPtr<mirror::Object> this_object, + ArtMethod* method, + uint32_t dex_pc, + ArtField* field) const REQUIRES_SHARED(Locks::mutator_lock_); - void FieldWriteEventImpl(Thread* thread, mirror::Object* this_object, - ArtMethod* method, uint32_t dex_pc, - ArtField* field, const JValue& field_value) const + void FieldWriteEventImpl(Thread* thread, + ObjPtr<mirror::Object> this_object, + ArtMethod* method, + uint32_t dex_pc, + ArtField* field, + const JValue& field_value) const REQUIRES_SHARED(Locks::mutator_lock_); // Read barrier-aware utility functions for accessing deoptimized_methods_ diff --git a/runtime/instrumentation_test.cc b/runtime/instrumentation_test.cc index 9926ee7386..2a601c9cf2 100644 --- a/runtime/instrumentation_test.cc +++ b/runtime/instrumentation_test.cc @@ -23,11 +23,13 @@ #include "dex_file.h" #include "gc/scoped_gc_critical_section.h" #include "handle_scope-inl.h" +#include "jni_internal.h" #include "jvalue.h" #include "runtime.h" #include "scoped_thread_state_change-inl.h" #include "thread_list.h" -#include "thread-current-inl.h" +#include "thread-inl.h" +#include "well_known_classes.h" namespace art { namespace instrumentation { @@ -35,16 +37,22 @@ namespace instrumentation { class TestInstrumentationListener FINAL : public instrumentation::InstrumentationListener { public: TestInstrumentationListener() - : received_method_enter_event(false), received_method_exit_event(false), - received_method_unwind_event(false), received_dex_pc_moved_event(false), - received_field_read_event(false), received_field_written_event(false), - received_exception_caught_event(false), received_branch_event(false), + : received_method_enter_event(false), + received_method_exit_event(false), + received_method_exit_object_event(false), + received_method_unwind_event(false), + received_dex_pc_moved_event(false), + received_field_read_event(false), + received_field_written_event(false), + received_field_written_object_event(false), + received_exception_caught_event(false), + received_branch_event(false), received_invoke_virtual_or_interface_event(false) {} virtual ~TestInstrumentationListener() {} void MethodEntered(Thread* thread ATTRIBUTE_UNUSED, - mirror::Object* this_object ATTRIBUTE_UNUSED, + Handle<mirror::Object> this_object ATTRIBUTE_UNUSED, ArtMethod* method ATTRIBUTE_UNUSED, uint32_t dex_pc ATTRIBUTE_UNUSED) OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_) { @@ -52,7 +60,16 @@ class TestInstrumentationListener FINAL : public instrumentation::Instrumentatio } void MethodExited(Thread* thread ATTRIBUTE_UNUSED, - mirror::Object* this_object ATTRIBUTE_UNUSED, + Handle<mirror::Object> this_object ATTRIBUTE_UNUSED, + ArtMethod* method ATTRIBUTE_UNUSED, + uint32_t dex_pc ATTRIBUTE_UNUSED, + Handle<mirror::Object> return_value ATTRIBUTE_UNUSED) + OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_) { + received_method_exit_object_event = true; + } + + void MethodExited(Thread* thread ATTRIBUTE_UNUSED, + Handle<mirror::Object> this_object ATTRIBUTE_UNUSED, ArtMethod* method ATTRIBUTE_UNUSED, uint32_t dex_pc ATTRIBUTE_UNUSED, const JValue& return_value ATTRIBUTE_UNUSED) @@ -61,7 +78,7 @@ class TestInstrumentationListener FINAL : public instrumentation::Instrumentatio } void MethodUnwind(Thread* thread ATTRIBUTE_UNUSED, - mirror::Object* this_object ATTRIBUTE_UNUSED, + Handle<mirror::Object> this_object ATTRIBUTE_UNUSED, ArtMethod* method ATTRIBUTE_UNUSED, uint32_t dex_pc ATTRIBUTE_UNUSED) OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_) { @@ -69,7 +86,7 @@ class TestInstrumentationListener FINAL : public instrumentation::Instrumentatio } void DexPcMoved(Thread* thread ATTRIBUTE_UNUSED, - mirror::Object* this_object ATTRIBUTE_UNUSED, + Handle<mirror::Object> this_object ATTRIBUTE_UNUSED, ArtMethod* method ATTRIBUTE_UNUSED, uint32_t new_dex_pc ATTRIBUTE_UNUSED) OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_) { @@ -77,7 +94,7 @@ class TestInstrumentationListener FINAL : public instrumentation::Instrumentatio } void FieldRead(Thread* thread ATTRIBUTE_UNUSED, - mirror::Object* this_object ATTRIBUTE_UNUSED, + Handle<mirror::Object> this_object ATTRIBUTE_UNUSED, ArtMethod* method ATTRIBUTE_UNUSED, uint32_t dex_pc ATTRIBUTE_UNUSED, ArtField* field ATTRIBUTE_UNUSED) @@ -86,7 +103,17 @@ class TestInstrumentationListener FINAL : public instrumentation::Instrumentatio } void FieldWritten(Thread* thread ATTRIBUTE_UNUSED, - mirror::Object* this_object ATTRIBUTE_UNUSED, + Handle<mirror::Object> this_object ATTRIBUTE_UNUSED, + ArtMethod* method ATTRIBUTE_UNUSED, + uint32_t dex_pc ATTRIBUTE_UNUSED, + ArtField* field ATTRIBUTE_UNUSED, + Handle<mirror::Object> field_value ATTRIBUTE_UNUSED) + OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_) { + received_field_written_object_event = true; + } + + void FieldWritten(Thread* thread ATTRIBUTE_UNUSED, + Handle<mirror::Object> this_object ATTRIBUTE_UNUSED, ArtMethod* method ATTRIBUTE_UNUSED, uint32_t dex_pc ATTRIBUTE_UNUSED, ArtField* field ATTRIBUTE_UNUSED, @@ -96,7 +123,7 @@ class TestInstrumentationListener FINAL : public instrumentation::Instrumentatio } void ExceptionCaught(Thread* thread ATTRIBUTE_UNUSED, - mirror::Throwable* exception_object ATTRIBUTE_UNUSED) + Handle<mirror::Throwable> exception_object ATTRIBUTE_UNUSED) OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_) { received_exception_caught_event = true; } @@ -110,7 +137,7 @@ class TestInstrumentationListener FINAL : public instrumentation::Instrumentatio } void InvokeVirtualOrInterface(Thread* thread ATTRIBUTE_UNUSED, - mirror::Object* this_object ATTRIBUTE_UNUSED, + Handle<mirror::Object> this_object ATTRIBUTE_UNUSED, ArtMethod* caller ATTRIBUTE_UNUSED, uint32_t dex_pc ATTRIBUTE_UNUSED, ArtMethod* callee ATTRIBUTE_UNUSED) @@ -121,10 +148,12 @@ class TestInstrumentationListener FINAL : public instrumentation::Instrumentatio void Reset() { received_method_enter_event = false; received_method_exit_event = false; + received_method_exit_object_event = false; received_method_unwind_event = false; received_dex_pc_moved_event = false; received_field_read_event = false; received_field_written_event = false; + received_field_written_object_event = false; received_exception_caught_event = false; received_branch_event = false; received_invoke_virtual_or_interface_event = false; @@ -132,10 +161,12 @@ class TestInstrumentationListener FINAL : public instrumentation::Instrumentatio bool received_method_enter_event; bool received_method_exit_event; + bool received_method_exit_object_event; bool received_method_unwind_event; bool received_dex_pc_moved_event; bool received_field_read_event; bool received_field_written_event; + bool received_field_written_object_event; bool received_exception_caught_event; bool received_branch_event; bool received_invoke_virtual_or_interface_event; @@ -171,6 +202,13 @@ class InstrumentationTest : public CommonRuntimeTest { } void TestEvent(uint32_t instrumentation_event) { + TestEvent(instrumentation_event, nullptr, nullptr, false); + } + + void TestEvent(uint32_t instrumentation_event, + ArtMethod* event_method, + ArtField* event_field, + bool with_object) { ScopedObjectAccess soa(Thread::Current()); instrumentation::Instrumentation* instr = Runtime::Current()->GetInstrumentation(); TestInstrumentationListener listener; @@ -180,15 +218,20 @@ class InstrumentationTest : public CommonRuntimeTest { instr->AddListener(&listener, instrumentation_event); } - ArtMethod* const event_method = nullptr; mirror::Object* const event_obj = nullptr; const uint32_t event_dex_pc = 0; // Check the listener is registered and is notified of the event. EXPECT_TRUE(HasEventListener(instr, instrumentation_event)); - EXPECT_FALSE(DidListenerReceiveEvent(listener, instrumentation_event)); - ReportEvent(instr, instrumentation_event, soa.Self(), event_method, event_obj, event_dex_pc); - EXPECT_TRUE(DidListenerReceiveEvent(listener, instrumentation_event)); + EXPECT_FALSE(DidListenerReceiveEvent(listener, instrumentation_event, with_object)); + ReportEvent(instr, + instrumentation_event, + soa.Self(), + event_method, + event_obj, + event_field, + event_dex_pc); + EXPECT_TRUE(DidListenerReceiveEvent(listener, instrumentation_event, with_object)); listener.Reset(); { @@ -199,9 +242,15 @@ class InstrumentationTest : public CommonRuntimeTest { // Check the listener is not registered and is not notified of the event. EXPECT_FALSE(HasEventListener(instr, instrumentation_event)); - EXPECT_FALSE(DidListenerReceiveEvent(listener, instrumentation_event)); - ReportEvent(instr, instrumentation_event, soa.Self(), event_method, event_obj, event_dex_pc); - EXPECT_FALSE(DidListenerReceiveEvent(listener, instrumentation_event)); + EXPECT_FALSE(DidListenerReceiveEvent(listener, instrumentation_event, with_object)); + ReportEvent(instr, + instrumentation_event, + soa.Self(), + event_method, + event_obj, + event_field, + event_dex_pc); + EXPECT_FALSE(DidListenerReceiveEvent(listener, instrumentation_event, with_object)); } void DeoptimizeMethod(Thread* self, ArtMethod* method, bool enable_deoptimization) @@ -317,8 +366,12 @@ class InstrumentationTest : public CommonRuntimeTest { } } - static void ReportEvent(const instrumentation::Instrumentation* instr, uint32_t event_type, - Thread* self, ArtMethod* method, mirror::Object* obj, + static void ReportEvent(const instrumentation::Instrumentation* instr, + uint32_t event_type, + Thread* self, + ArtMethod* method, + mirror::Object* obj, + ArtField* field, uint32_t dex_pc) REQUIRES_SHARED(Locks::mutator_lock_) { switch (event_type) { @@ -337,11 +390,11 @@ class InstrumentationTest : public CommonRuntimeTest { instr->DexPcMovedEvent(self, obj, method, dex_pc); break; case instrumentation::Instrumentation::kFieldRead: - instr->FieldReadEvent(self, obj, method, dex_pc, nullptr); + instr->FieldReadEvent(self, obj, method, dex_pc, field); break; case instrumentation::Instrumentation::kFieldWritten: { JValue value; - instr->FieldWriteEvent(self, obj, method, dex_pc, nullptr, value); + instr->FieldWriteEvent(self, obj, method, dex_pc, field, value); break; } case instrumentation::Instrumentation::kExceptionCaught: { @@ -364,12 +417,14 @@ class InstrumentationTest : public CommonRuntimeTest { } static bool DidListenerReceiveEvent(const TestInstrumentationListener& listener, - uint32_t event_type) { + uint32_t event_type, + bool with_object) { switch (event_type) { case instrumentation::Instrumentation::kMethodEntered: return listener.received_method_enter_event; case instrumentation::Instrumentation::kMethodExited: - return listener.received_method_exit_event; + return (!with_object && listener.received_method_exit_event) || + (with_object && listener.received_method_exit_object_event); case instrumentation::Instrumentation::kMethodUnwind: return listener.received_method_unwind_event; case instrumentation::Instrumentation::kDexPcMoved: @@ -377,7 +432,8 @@ class InstrumentationTest : public CommonRuntimeTest { case instrumentation::Instrumentation::kFieldRead: return listener.received_field_read_event; case instrumentation::Instrumentation::kFieldWritten: - return listener.received_field_written_event; + return (!with_object && listener.received_field_written_event) || + (with_object && listener.received_field_written_object_event); case instrumentation::Instrumentation::kExceptionCaught: return listener.received_exception_caught_event; case instrumentation::Instrumentation::kBranch: @@ -419,8 +475,42 @@ TEST_F(InstrumentationTest, MethodEntryEvent) { TestEvent(instrumentation::Instrumentation::kMethodEntered); } -TEST_F(InstrumentationTest, MethodExitEvent) { - TestEvent(instrumentation::Instrumentation::kMethodExited); +TEST_F(InstrumentationTest, MethodExitObjectEvent) { + ScopedObjectAccess soa(Thread::Current()); + jobject class_loader = LoadDex("Instrumentation"); + Runtime* const runtime = Runtime::Current(); + ClassLinker* class_linker = runtime->GetClassLinker(); + StackHandleScope<1> hs(soa.Self()); + 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); + ASSERT_TRUE(method != nullptr); + TestEvent(instrumentation::Instrumentation::kMethodExited, + /*event_method*/ method, + /*event_field*/ nullptr, + /*with_object*/ true); +} + +TEST_F(InstrumentationTest, MethodExitPrimEvent) { + ScopedObjectAccess soa(Thread::Current()); + jobject class_loader = LoadDex("Instrumentation"); + Runtime* const runtime = Runtime::Current(); + ClassLinker* class_linker = runtime->GetClassLinker(); + StackHandleScope<1> hs(soa.Self()); + 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); + ASSERT_TRUE(method != nullptr); + TestEvent(instrumentation::Instrumentation::kMethodExited, + /*event_method*/ method, + /*event_field*/ nullptr, + /*with_object*/ false); } TEST_F(InstrumentationTest, MethodUnwindEvent) { @@ -435,8 +525,40 @@ TEST_F(InstrumentationTest, FieldReadEvent) { TestEvent(instrumentation::Instrumentation::kFieldRead); } -TEST_F(InstrumentationTest, FieldWriteEvent) { - TestEvent(instrumentation::Instrumentation::kFieldWritten); +TEST_F(InstrumentationTest, FieldWriteObjectEvent) { + ScopedObjectAccess soa(Thread::Current()); + jobject class_loader = LoadDex("Instrumentation"); + Runtime* const runtime = Runtime::Current(); + ClassLinker* class_linker = runtime->GetClassLinker(); + StackHandleScope<1> hs(soa.Self()); + 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); + ArtField* field = klass->FindDeclaredStaticField("referenceField", "Ljava/lang/Object;"); + ASSERT_TRUE(field != nullptr); + + TestEvent(instrumentation::Instrumentation::kFieldWritten, + /*event_method*/ nullptr, + /*event_field*/ field, + /*with_object*/ true); +} + +TEST_F(InstrumentationTest, FieldWritePrimEvent) { + ScopedObjectAccess soa(Thread::Current()); + jobject class_loader = LoadDex("Instrumentation"); + Runtime* const runtime = Runtime::Current(); + ClassLinker* class_linker = runtime->GetClassLinker(); + StackHandleScope<1> hs(soa.Self()); + 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); + ArtField* field = klass->FindDeclaredStaticField("primitiveField", "I"); + ASSERT_TRUE(field != nullptr); + + TestEvent(instrumentation::Instrumentation::kFieldWritten, + /*event_method*/ nullptr, + /*event_field*/ field, + /*with_object*/ false); } TEST_F(InstrumentationTest, ExceptionCaughtEvent) { diff --git a/runtime/interpreter/interpreter_switch_impl.cc b/runtime/interpreter/interpreter_switch_impl.cc index b191dd79a1..32a23783b7 100644 --- a/runtime/interpreter/interpreter_switch_impl.cc +++ b/runtime/interpreter/interpreter_switch_impl.cc @@ -307,6 +307,8 @@ JValue ExecuteSwitchImpl(Thread* self, const DexFile::CodeItem* code_item, instrumentation->MethodExitEvent(self, shadow_frame.GetThisObject(code_item->ins_size_), shadow_frame.GetMethod(), inst->GetDexPc(insns), result); + // Re-load since it might have moved during the MethodExitEvent. + result.SetL(shadow_frame.GetVRegReference(ref_idx)); } if (interpret_one_instruction) { /* Signal mterp to return to caller */ diff --git a/runtime/jit/profile_compilation_info.cc b/runtime/jit/profile_compilation_info.cc index a292a6eab9..3852a5bc53 100644 --- a/runtime/jit/profile_compilation_info.cc +++ b/runtime/jit/profile_compilation_info.cc @@ -47,8 +47,8 @@ namespace art { const uint8_t ProfileCompilationInfo::kProfileMagic[] = { 'p', 'r', 'o', '\0' }; -// Last profile version: Instead of method index, put the difference with the last -// method's index. +// 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' }; static constexpr uint16_t kMaxDexFileKeyLength = PATH_MAX; @@ -284,10 +284,13 @@ static constexpr size_t kLineHeaderSize = /** * Serialization format: * magic,version,number_of_dex_files,uncompressed_size_of_zipped_data,compressed_data_size, - * zipped[dex_location1,number_of_classes1,methods_region_size,dex_location_checksum1, \ + * zipped[dex_location1,number_of_classes1,methods_region_size,dex_location_checksum1 + * num_method_ids, * method_encoding_11,method_encoding_12...,class_id1,class_id2... - * dex_location2,number_of_classes2,methods_region_size,dex_location_checksum2, \ + * startup/post startup bitmap, + * dex_location2,number_of_classes2,methods_region_size,dex_location_checksum2, num_method_ids, * method_encoding_21,method_encoding_22...,,class_id1,class_id2... + * startup/post startup bitmap, * .....] * The method_encoding is: * method_id,number_of_inline_caches,inline_cache1,inline_cache2... @@ -995,15 +998,6 @@ bool ProfileCompilationInfo::Load(int fd) { } } -void ProfileCompilationInfo::DexFileData::CreateBitmap() { - const size_t num_bits = num_method_ids * kMethodBitCount; - bitmap_storage.resize(RoundUp(num_bits, kBitsPerByte) / kBitsPerByte); - if (!bitmap_storage.empty()) { - method_bitmap = - BitMemoryRegion(MemoryRegion(&bitmap_storage[0], bitmap_storage.size()), 0, num_bits); - } -} - // TODO(calin): fail fast if the dex checksums don't match. ProfileCompilationInfo::ProfileLoadSatus ProfileCompilationInfo::LoadInternal( int fd, std::string* error) { @@ -1256,7 +1250,7 @@ bool ProfileCompilationInfo::IsStartupOrHotMethod(const std::string& dex_locatio return method_it != methods.end(); } -bool ProfileCompilationInfo::ContainsMethod(const MethodReference& method_ref) const { +bool ProfileCompilationInfo::ContainsHotMethod(const MethodReference& method_ref) const { return FindMethod(method_ref.dex_file->GetLocation(), method_ref.dex_file->GetLocationChecksum(), method_ref.dex_method_index) != nullptr; diff --git a/runtime/jit/profile_compilation_info.h b/runtime/jit/profile_compilation_info.h index 2b89a41dd8..a9f2fb6dd8 100644 --- a/runtime/jit/profile_compilation_info.h +++ b/runtime/jit/profile_compilation_info.h @@ -246,8 +246,8 @@ class ProfileCompilationInfo { uint32_t dex_checksum, uint16_t dex_method_index) const; - // Return true if the method reference is present in the profiling info. - bool ContainsMethod(const MethodReference& method_ref) const; + // Return true if the method reference iS present and hot in the profiling info. + bool ContainsHotMethod(const MethodReference& method_ref) const; // Return true if the class's type is present in the profiling info. bool ContainsClass(const DexFile& dex_file, dex::TypeIndex type_idx) const; @@ -343,7 +343,12 @@ class ProfileCompilationInfo { class_set(std::less<dex::TypeIndex>(), arena->Adapter(kArenaAllocProfile)), num_method_ids(num_methods), bitmap_storage(arena->Adapter(kArenaAllocProfile)) { - CreateBitmap(); + const size_t num_bits = num_method_ids * kBitmapCount; + bitmap_storage.resize(RoundUp(num_bits, kBitsPerByte) / kBitsPerByte); + if (!bitmap_storage.empty()) { + method_bitmap = + BitMemoryRegion(MemoryRegion(&bitmap_storage[0], bitmap_storage.size()), 0, num_bits); + } } bool operator==(const DexFileData& other) const { @@ -359,6 +364,13 @@ class ProfileCompilationInfo { return method_bitmap.LoadBit(MethodBitIndex(startup, index)); } + void MergeBitmap(const DexFileData& other) { + DCHECK_EQ(bitmap_storage.size(), other.bitmap_storage.size()); + for (size_t i = 0; i < bitmap_storage.size(); ++i) { + bitmap_storage[i] |= other.bitmap_storage[i]; + } + } + // The arena used to allocate new inline cache maps. ArenaAllocator* arena_; // The profile key this data belongs to. @@ -380,28 +392,21 @@ class ProfileCompilationInfo { ArenaVector<uint8_t> bitmap_storage; BitMemoryRegion method_bitmap; - void CreateBitmap(); - - void MergeBitmap(const DexFileData& other) { - DCHECK_EQ(bitmap_storage.size(), other.bitmap_storage.size()); - for (size_t i = 0; i < bitmap_storage.size(); ++i) { - bitmap_storage[i] |= other.bitmap_storage[i]; - } - } - private: - enum Bits { - kMethodBitStartup, - kMethodBitAfterStartup, - kMethodBitCount, + enum BitmapIndex { + kBitmapStartup, + kBitmapPostStartup, + kBitmapCount, }; size_t MethodBitIndex(bool startup, size_t index) const { DCHECK_LT(index, num_method_ids); - if (!startup) { - index += num_method_ids; - } - return index; + // The format is [startup bitmap][post startup bitmap] + // This compresses better than ([startup bit][post statup bit])* + + return index + (startup + ? kBitmapStartup * num_method_ids + : kBitmapPostStartup * num_method_ids); } }; diff --git a/runtime/jit/profile_compilation_info_test.cc b/runtime/jit/profile_compilation_info_test.cc index 615149feb3..39670afb0c 100644 --- a/runtime/jit/profile_compilation_info_test.cc +++ b/runtime/jit/profile_compilation_info_test.cc @@ -298,7 +298,8 @@ TEST_F(ProfileCompilationInfoTest, SaveArtMethods) { { ScopedObjectAccess soa(self); for (ArtMethod* m : main_methods) { - ASSERT_TRUE(info1.ContainsMethod(MethodReference(m->GetDexFile(), m->GetDexMethodIndex()))); + ASSERT_TRUE(info1.ContainsHotMethod( + MethodReference(m->GetDexFile(), m->GetDexMethodIndex()))); } } @@ -314,10 +315,12 @@ TEST_F(ProfileCompilationInfoTest, SaveArtMethods) { { ScopedObjectAccess soa(self); for (ArtMethod* m : main_methods) { - ASSERT_TRUE(info2.ContainsMethod(MethodReference(m->GetDexFile(), m->GetDexMethodIndex()))); + ASSERT_TRUE( + info2.ContainsHotMethod(MethodReference(m->GetDexFile(), m->GetDexMethodIndex()))); } for (ArtMethod* m : second_methods) { - ASSERT_TRUE(info2.ContainsMethod(MethodReference(m->GetDexFile(), m->GetDexMethodIndex()))); + ASSERT_TRUE( + info2.ContainsHotMethod(MethodReference(m->GetDexFile(), m->GetDexMethodIndex()))); } } } @@ -663,7 +666,8 @@ TEST_F(ProfileCompilationInfoTest, SaveArtMethodsWithInlineCaches) { { ScopedObjectAccess soa(self); for (ArtMethod* m : main_methods) { - ASSERT_TRUE(info.ContainsMethod(MethodReference(m->GetDexFile(), m->GetDexMethodIndex()))); + ASSERT_TRUE( + info.ContainsHotMethod(MethodReference(m->GetDexFile(), m->GetDexMethodIndex()))); const ProfileMethodInfo& pmi = profile_methods_map.find(m)->second; std::unique_ptr<ProfileCompilationInfo::OfflineProfileMethodInfo> offline_pmi = info.GetMethod(m->GetDexFile()->GetLocation(), diff --git a/runtime/jit/profile_saver.cc b/runtime/jit/profile_saver.cc index c96ca88874..6128d82e24 100644 --- a/runtime/jit/profile_saver.cc +++ b/runtime/jit/profile_saver.cc @@ -184,11 +184,11 @@ void ProfileSaver::NotifyJitActivityInternal() { class GetMethodsVisitor : public ClassVisitor { public: GetMethodsVisitor(std::vector<MethodReference>* hot_methods, - std::vector<MethodReference>* startup_methods, - uint32_t startup_method_samples) + std::vector<MethodReference>* sampled_methods, + uint32_t hot_method_sample_threshold) : hot_methods_(hot_methods), - startup_methods_(startup_methods), - startup_method_samples_(startup_method_samples) {} + sampled_methods_(sampled_methods), + hot_method_sample_threshold_(hot_method_sample_threshold) {} virtual bool operator()(ObjPtr<mirror::Class> klass) REQUIRES_SHARED(Locks::mutator_lock_) { if (Runtime::Current()->GetHeap()->ObjectIsInBootImageSpace(klass)) { @@ -198,12 +198,14 @@ class GetMethodsVisitor : public ClassVisitor { if (!method.IsNative() && !method.IsProxyMethod()) { const uint16_t counter = method.GetCounter(); MethodReference ref(method.GetDexFile(), method.GetDexMethodIndex()); + // Mark startup methods as hot if they have more than hot_method_sample_threshold_ samples. + // This means they will get compiled by the compiler driver. if (method.GetProfilingInfo(kRuntimePointerSize) != nullptr || - (method.GetAccessFlags() & kAccPreviouslyWarm) != 0) { + (method.GetAccessFlags() & kAccPreviouslyWarm) != 0 || + counter >= hot_method_sample_threshold_) { hot_methods_->push_back(ref); - startup_methods_->push_back(ref); - } else if (counter >= startup_method_samples_) { - startup_methods_->push_back(ref); + } else if (counter != 0) { + sampled_methods_->push_back(ref); } } else { CHECK_EQ(method.GetCounter(), 0u); @@ -214,8 +216,8 @@ class GetMethodsVisitor : public ClassVisitor { private: std::vector<MethodReference>* const hot_methods_; - std::vector<MethodReference>* const startup_methods_; - uint32_t startup_method_samples_; + std::vector<MethodReference>* const sampled_methods_; + uint32_t hot_method_sample_threshold_; }; void ProfileSaver::FetchAndCacheResolvedClassesAndMethods() { @@ -241,11 +243,11 @@ void ProfileSaver::FetchAndCacheResolvedClassesAndMethods() { ScopedTrace trace2("Get hot methods"); GetMethodsVisitor visitor(&hot_methods, &startup_methods, - options_.GetStartupMethodSamples()); + options_.GetHotStartupMethodSamples()); class_linker->VisitClasses(&visitor); VLOG(profiler) << "Profile saver recorded " << hot_methods.size() << " hot methods and " << startup_methods.size() << " startup methods with threshold " - << options_.GetStartupMethodSamples(); + << options_.GetHotStartupMethodSamples(); } } MutexLock mu(self, *Locks::profiler_lock_); @@ -256,12 +258,14 @@ void ProfileSaver::FetchAndCacheResolvedClassesAndMethods() { const std::string& filename = it.first; const std::set<std::string>& locations = it.second; std::vector<ProfileMethodInfo> profile_methods_for_location; + std::vector<MethodReference> startup_methods_for_locations; for (const MethodReference& ref : hot_methods) { if (locations.find(ref.dex_file->GetBaseLocation()) != locations.end()) { profile_methods_for_location.emplace_back(ref.dex_file, ref.dex_method_index); + // Hot methods are also startup methods since this function is only invoked during startup. + startup_methods_for_locations.push_back(ref); } } - std::vector<MethodReference> startup_methods_for_locations; for (const MethodReference& ref : startup_methods) { if (locations.find(ref.dex_file->GetBaseLocation()) != locations.end()) { startup_methods_for_locations.push_back(ref); @@ -618,7 +622,7 @@ bool ProfileSaver::HasSeenMethod(const std::string& profile, if (!info.Load(profile, /*clear_if_invalid*/false)) { return false; } - return info.ContainsMethod(MethodReference(dex_file, method_idx)); + return info.ContainsHotMethod(MethodReference(dex_file, method_idx)); } return false; } diff --git a/runtime/jit/profile_saver_options.h b/runtime/jit/profile_saver_options.h index 07aeb66eb6..455bc1aae4 100644 --- a/runtime/jit/profile_saver_options.h +++ b/runtime/jit/profile_saver_options.h @@ -22,8 +22,8 @@ struct ProfileSaverOptions { public: static constexpr uint32_t kMinSavePeriodMs = 40 * 1000; // 40 seconds static constexpr uint32_t kSaveResolvedClassesDelayMs = 5 * 1000; // 5 seconds - // Minimum number of JIT samples during launch to include a method into the profile. - static constexpr uint32_t kStartupMethodSamples = 1; + // Minimum number of JIT samples during launch to mark a method as hot in the profile. + static constexpr uint32_t kHotStartupMethodSamples = 1; static constexpr uint32_t kMinMethodsToSave = 10; static constexpr uint32_t kMinClassesToSave = 10; static constexpr uint32_t kMinNotificationBeforeWake = 10; @@ -33,7 +33,7 @@ struct ProfileSaverOptions { enabled_(false), min_save_period_ms_(kMinSavePeriodMs), save_resolved_classes_delay_ms_(kSaveResolvedClassesDelayMs), - startup_method_samples_(kStartupMethodSamples), + hot_startup_method_samples_(kHotStartupMethodSamples), min_methods_to_save_(kMinMethodsToSave), min_classes_to_save_(kMinClassesToSave), min_notification_before_wake_(kMinNotificationBeforeWake), @@ -44,7 +44,7 @@ struct ProfileSaverOptions { bool enabled, uint32_t min_save_period_ms, uint32_t save_resolved_classes_delay_ms, - uint32_t startup_method_samples, + uint32_t hot_startup_method_samples, uint32_t min_methods_to_save, uint32_t min_classes_to_save, uint32_t min_notification_before_wake, @@ -53,7 +53,7 @@ struct ProfileSaverOptions { enabled_(enabled), min_save_period_ms_(min_save_period_ms), save_resolved_classes_delay_ms_(save_resolved_classes_delay_ms), - startup_method_samples_(startup_method_samples), + hot_startup_method_samples_(hot_startup_method_samples), min_methods_to_save_(min_methods_to_save), min_classes_to_save_(min_classes_to_save), min_notification_before_wake_(min_notification_before_wake), @@ -73,8 +73,8 @@ struct ProfileSaverOptions { uint32_t GetSaveResolvedClassesDelayMs() const { return save_resolved_classes_delay_ms_; } - uint32_t GetStartupMethodSamples() const { - return startup_method_samples_; + uint32_t GetHotStartupMethodSamples() const { + return hot_startup_method_samples_; } uint32_t GetMinMethodsToSave() const { return min_methods_to_save_; @@ -96,7 +96,7 @@ struct ProfileSaverOptions { os << "enabled_" << pso.enabled_ << ", min_save_period_ms_" << pso.min_save_period_ms_ << ", save_resolved_classes_delay_ms_" << pso.save_resolved_classes_delay_ms_ - << ", startup_method_samples_" << pso.startup_method_samples_ + << ", hot_startup_method_samples_" << pso.hot_startup_method_samples_ << ", min_methods_to_save_" << pso.min_methods_to_save_ << ", min_classes_to_save_" << pso.min_classes_to_save_ << ", min_notification_before_wake_" << pso.min_notification_before_wake_ @@ -107,7 +107,7 @@ struct ProfileSaverOptions { bool enabled_; uint32_t min_save_period_ms_; uint32_t save_resolved_classes_delay_ms_; - uint32_t startup_method_samples_; + uint32_t hot_startup_method_samples_; uint32_t min_methods_to_save_; uint32_t min_classes_to_save_; uint32_t min_notification_before_wake_; diff --git a/runtime/parsed_options.cc b/runtime/parsed_options.cc index ef4957c0ba..abb6f8c018 100644 --- a/runtime/parsed_options.cc +++ b/runtime/parsed_options.cc @@ -709,7 +709,7 @@ void ParsedOptions::Usage(const char* fmt, ...) { UsageMessage(stream, " -Xmethod-trace-file-size:integervalue\n"); UsageMessage(stream, " -Xps-min-save-period-ms:integervalue\n"); UsageMessage(stream, " -Xps-save-resolved-classes-delay-ms:integervalue\n"); - UsageMessage(stream, " -Xps-startup-method-samples:integervalue\n"); + UsageMessage(stream, " -Xps-hot-startup-method-samples:integervalue\n"); UsageMessage(stream, " -Xps-min-methods-to-save:integervalue\n"); UsageMessage(stream, " -Xps-min-classes-to-save:integervalue\n"); UsageMessage(stream, " -Xps-min-notification-before-wake:integervalue\n"); diff --git a/runtime/trace.cc b/runtime/trace.cc index 3550d56bd8..cabd1620a7 100644 --- a/runtime/trace.cc +++ b/runtime/trace.cc @@ -740,7 +740,7 @@ void Trace::FinishTracing() { } void Trace::DexPcMoved(Thread* thread ATTRIBUTE_UNUSED, - mirror::Object* this_object ATTRIBUTE_UNUSED, + Handle<mirror::Object> this_object ATTRIBUTE_UNUSED, ArtMethod* method, uint32_t new_dex_pc) { // We're not recorded to listen to this kind of event, so complain. @@ -749,7 +749,7 @@ void Trace::DexPcMoved(Thread* thread ATTRIBUTE_UNUSED, } void Trace::FieldRead(Thread* thread ATTRIBUTE_UNUSED, - mirror::Object* this_object ATTRIBUTE_UNUSED, + Handle<mirror::Object> this_object ATTRIBUTE_UNUSED, ArtMethod* method, uint32_t dex_pc, ArtField* field ATTRIBUTE_UNUSED) @@ -760,7 +760,7 @@ void Trace::FieldRead(Thread* thread ATTRIBUTE_UNUSED, } void Trace::FieldWritten(Thread* thread ATTRIBUTE_UNUSED, - mirror::Object* this_object ATTRIBUTE_UNUSED, + Handle<mirror::Object> this_object ATTRIBUTE_UNUSED, ArtMethod* method, uint32_t dex_pc, ArtField* field ATTRIBUTE_UNUSED, @@ -771,8 +771,10 @@ void Trace::FieldWritten(Thread* thread ATTRIBUTE_UNUSED, << " " << dex_pc; } -void Trace::MethodEntered(Thread* thread, mirror::Object* this_object ATTRIBUTE_UNUSED, - ArtMethod* method, uint32_t dex_pc ATTRIBUTE_UNUSED) { +void Trace::MethodEntered(Thread* thread, + Handle<mirror::Object> this_object ATTRIBUTE_UNUSED, + ArtMethod* method, + uint32_t dex_pc ATTRIBUTE_UNUSED) { uint32_t thread_clock_diff = 0; uint32_t wall_clock_diff = 0; ReadClocks(thread, &thread_clock_diff, &wall_clock_diff); @@ -780,8 +782,10 @@ void Trace::MethodEntered(Thread* thread, mirror::Object* this_object ATTRIBUTE_ thread_clock_diff, wall_clock_diff); } -void Trace::MethodExited(Thread* thread, mirror::Object* this_object ATTRIBUTE_UNUSED, - ArtMethod* method, uint32_t dex_pc ATTRIBUTE_UNUSED, +void Trace::MethodExited(Thread* thread, + Handle<mirror::Object> this_object ATTRIBUTE_UNUSED, + ArtMethod* method, + uint32_t dex_pc ATTRIBUTE_UNUSED, const JValue& return_value ATTRIBUTE_UNUSED) { uint32_t thread_clock_diff = 0; uint32_t wall_clock_diff = 0; @@ -790,8 +794,10 @@ void Trace::MethodExited(Thread* thread, mirror::Object* this_object ATTRIBUTE_U thread_clock_diff, wall_clock_diff); } -void Trace::MethodUnwind(Thread* thread, mirror::Object* this_object ATTRIBUTE_UNUSED, - ArtMethod* method, uint32_t dex_pc ATTRIBUTE_UNUSED) { +void Trace::MethodUnwind(Thread* thread, + Handle<mirror::Object> this_object ATTRIBUTE_UNUSED, + ArtMethod* method, + uint32_t dex_pc ATTRIBUTE_UNUSED) { uint32_t thread_clock_diff = 0; uint32_t wall_clock_diff = 0; ReadClocks(thread, &thread_clock_diff, &wall_clock_diff); @@ -800,7 +806,7 @@ void Trace::MethodUnwind(Thread* thread, mirror::Object* this_object ATTRIBUTE_U } void Trace::ExceptionCaught(Thread* thread ATTRIBUTE_UNUSED, - mirror::Throwable* exception_object ATTRIBUTE_UNUSED) + Handle<mirror::Throwable> exception_object ATTRIBUTE_UNUSED) REQUIRES_SHARED(Locks::mutator_lock_) { LOG(ERROR) << "Unexpected exception caught event in tracing"; } @@ -812,7 +818,7 @@ void Trace::Branch(Thread* /*thread*/, ArtMethod* method, } void Trace::InvokeVirtualOrInterface(Thread*, - mirror::Object*, + Handle<mirror::Object>, ArtMethod* method, uint32_t dex_pc, ArtMethod*) { diff --git a/runtime/trace.h b/runtime/trace.h index 485e9a133a..ad1025045c 100644 --- a/runtime/trace.h +++ b/runtime/trace.h @@ -140,36 +140,54 @@ class Trace FINAL : public instrumentation::InstrumentationListener { REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!*unique_methods_lock_, !*streaming_lock_); // InstrumentationListener implementation. - void MethodEntered(Thread* thread, mirror::Object* this_object, - ArtMethod* method, uint32_t dex_pc) + void MethodEntered(Thread* thread, + Handle<mirror::Object> this_object, + ArtMethod* method, + uint32_t dex_pc) REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!*unique_methods_lock_, !*streaming_lock_) OVERRIDE; - void MethodExited(Thread* thread, mirror::Object* this_object, - ArtMethod* method, uint32_t dex_pc, + void MethodExited(Thread* thread, + Handle<mirror::Object> this_object, + ArtMethod* method, + uint32_t dex_pc, const JValue& return_value) REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!*unique_methods_lock_, !*streaming_lock_) OVERRIDE; - void MethodUnwind(Thread* thread, mirror::Object* this_object, - ArtMethod* method, uint32_t dex_pc) + void MethodUnwind(Thread* thread, + Handle<mirror::Object> this_object, + ArtMethod* method, + uint32_t dex_pc) REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!*unique_methods_lock_, !*streaming_lock_) OVERRIDE; - void DexPcMoved(Thread* thread, mirror::Object* this_object, - ArtMethod* method, uint32_t new_dex_pc) + void DexPcMoved(Thread* thread, + Handle<mirror::Object> this_object, + ArtMethod* method, + uint32_t new_dex_pc) REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!*unique_methods_lock_, !*streaming_lock_) OVERRIDE; - void FieldRead(Thread* thread, mirror::Object* this_object, - ArtMethod* method, uint32_t dex_pc, ArtField* field) + void FieldRead(Thread* thread, + Handle<mirror::Object> this_object, + ArtMethod* method, + uint32_t dex_pc, + ArtField* field) REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!*unique_methods_lock_) OVERRIDE; - void FieldWritten(Thread* thread, mirror::Object* this_object, - ArtMethod* method, uint32_t dex_pc, ArtField* field, + void FieldWritten(Thread* thread, + Handle<mirror::Object> this_object, + ArtMethod* method, + uint32_t dex_pc, + ArtField* field, const JValue& field_value) REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!*unique_methods_lock_) OVERRIDE; - void ExceptionCaught(Thread* thread, mirror::Throwable* exception_object) + void ExceptionCaught(Thread* thread, + Handle<mirror::Throwable> exception_object) REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!*unique_methods_lock_) OVERRIDE; - void Branch(Thread* thread, ArtMethod* method, uint32_t dex_pc, int32_t dex_pc_offset) + void Branch(Thread* thread, + ArtMethod* method, + uint32_t dex_pc, + int32_t dex_pc_offset) REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!*unique_methods_lock_) OVERRIDE; void InvokeVirtualOrInterface(Thread* thread, - mirror::Object* this_object, + Handle<mirror::Object> this_object, ArtMethod* caller, uint32_t dex_pc, ArtMethod* callee) diff --git a/test/552-checker-sharpening/src/Main.java b/test/552-checker-sharpening/src/Main.java index 196831b921..4bb179ecc4 100644 --- a/test/552-checker-sharpening/src/Main.java +++ b/test/552-checker-sharpening/src/Main.java @@ -42,7 +42,7 @@ public class Main { } /// CHECK-START: int Main.testSimple(int) sharpening (before) - /// CHECK: InvokeStaticOrDirect method_load_kind:DexCacheViaMethod + /// CHECK: InvokeStaticOrDirect method_load_kind:RuntimeCall /// CHECK-START-ARM: int Main.testSimple(int) sharpening (after) /// CHECK-NOT: ArmDexCacheArraysBase @@ -78,7 +78,7 @@ public class Main { } /// CHECK-START: int Main.testDiamond(boolean, int) sharpening (before) - /// CHECK: InvokeStaticOrDirect method_load_kind:DexCacheViaMethod + /// CHECK: InvokeStaticOrDirect method_load_kind:RuntimeCall /// CHECK-START-ARM: int Main.testDiamond(boolean, int) sharpening (after) /// CHECK-NOT: ArmDexCacheArraysBase diff --git a/test/564-checker-irreducible-loop/smali/IrreducibleLoop.smali b/test/564-checker-irreducible-loop/smali/IrreducibleLoop.smali index e4bf236266..5f73bbe759 100644 --- a/test/564-checker-irreducible-loop/smali/IrreducibleLoop.smali +++ b/test/564-checker-irreducible-loop/smali/IrreducibleLoop.smali @@ -18,8 +18,8 @@ ## CHECK-START-X86: int IrreducibleLoop.simpleLoop(int) dead_code_elimination$initial (before) ## CHECK-DAG: <<Constant:i\d+>> IntConstant 42 -## CHECK-DAG: InvokeStaticOrDirect [<<Constant>>] loop:{{B\d+}} irreducible:true -## CHECK-DAG: InvokeStaticOrDirect [<<Constant>>] loop:none +## CHECK-DAG: InvokeStaticOrDirect [<<Constant>>{{(,[ij]\d+)?}}] loop:{{B\d+}} irreducible:true +## CHECK-DAG: InvokeStaticOrDirect [<<Constant>>{{(,[ij]\d+)?}}] loop:none .method public static simpleLoop(I)I .registers 3 const/16 v0, 42 diff --git a/test/588-checker-irreducib-lifetime-hole/smali/IrreducibleLoop.smali b/test/588-checker-irreducib-lifetime-hole/smali/IrreducibleLoop.smali index 9b8aa510a4..3058358033 100644 --- a/test/588-checker-irreducib-lifetime-hole/smali/IrreducibleLoop.smali +++ b/test/588-checker-irreducib-lifetime-hole/smali/IrreducibleLoop.smali @@ -19,8 +19,8 @@ ## CHECK-START-X86: int IrreducibleLoop.simpleLoop1(int) dead_code_elimination$initial (before) ## CHECK-DAG: <<Constant:i\d+>> IntConstant 42 ## CHECK-DAG: Goto irreducible:true -## CHECK-DAG: InvokeStaticOrDirect [<<Constant>>] loop:none -## CHECK-DAG: InvokeStaticOrDirect [{{i\d+}}] loop:none +## CHECK-DAG: InvokeStaticOrDirect [<<Constant>>{{(,[ij]\d+)?}}] loop:none +## CHECK-DAG: InvokeStaticOrDirect [{{i\d+}}{{(,[ij]\d+)?}}] loop:none .method public static simpleLoop1(I)I .registers 3 const/16 v0, 42 @@ -59,8 +59,8 @@ ## CHECK-START-X86: int IrreducibleLoop.simpleLoop2(int) dead_code_elimination$initial (before) ## CHECK-DAG: <<Constant:i\d+>> IntConstant 42 ## CHECK-DAG: Goto irreducible:true -## CHECK-DAG: InvokeStaticOrDirect [<<Constant>>] loop:none -## CHECK-DAG: InvokeStaticOrDirect [{{i\d+}}] loop:none +## CHECK-DAG: InvokeStaticOrDirect [<<Constant>>{{(,[ij]\d+)?}}] loop:none +## CHECK-DAG: InvokeStaticOrDirect [{{i\d+}}{{(,[ij]\d+)?}}] loop:none .method public static simpleLoop2(I)I .registers 3 const/16 v0, 42 diff --git a/test/596-app-images/app_images.cc b/test/596-app-images/app_images.cc index fa9c902070..42211f7548 100644 --- a/test/596-app-images/app_images.cc +++ b/test/596-app-images/app_images.cc @@ -63,12 +63,6 @@ extern "C" JNIEXPORT jboolean JNICALL Java_Main_checkAppImageContains(JNIEnv*, j return JNI_FALSE; } -extern "C" JNIEXPORT jboolean JNICALL Java_Main_checkInitialized(JNIEnv*, jclass, jclass c) { - ScopedObjectAccess soa(Thread::Current()); - ObjPtr<mirror::Class> klass_ptr = soa.Decode<mirror::Class>(c); - return klass_ptr->IsInitialized(); -} - } // namespace } // namespace art diff --git a/test/596-app-images/src/Main.java b/test/596-app-images/src/Main.java index 88d95f4162..75b31b8061 100644 --- a/test/596-app-images/src/Main.java +++ b/test/596-app-images/src/Main.java @@ -14,17 +14,9 @@ * limitations under the License. */ -import java.lang.reflect.Field; -import java.lang.reflect.Constructor; -import java.lang.reflect.Method; - class Main { static class Inner { - final public static int abc = 10; - } - - static class Nested { - + public static int abc = 0; } public static void main(String[] args) { @@ -34,122 +26,8 @@ class Main { } else if (!checkAppImageContains(Inner.class)) { System.out.println("App image does not contain Inner!"); } - - if (!checkInitialized(Inner.class)) - System.out.println("Inner class is not initialized!"); - - if (!checkInitialized(Nested.class)) - System.out.println("Nested class is not initialized!"); - - if (!checkInitialized(StaticFields.class)) - System.out.println("StaticFields class is not initialized!"); - - if (!checkInitialized(StaticFieldsInitSub.class)) - System.out.println("StaticFieldsInitSub class is not initialized!"); - - if (!checkInitialized(StaticFieldsInit.class)) - System.out.println("StaticFieldsInit class is not initialized!"); - - if (!checkInitialized(StaticInternString.class)) - System.out.println("StaticInternString class is not initialized!"); - - StringBuffer sb = new StringBuffer(); - sb.append("java."); - sb.append("abc."); - sb.append("Action"); - - String tmp = sb.toString(); - String intern = tmp.intern(); - - assertNotEqual(tmp, intern, "Dynamically constructed String, not interned."); - assertEqual(intern, StaticInternString.intent, "Static encoded literal String not interned."); - assertEqual(BootInternedString.boot, BootInternedString.boot.intern(), - "Static encoded literal String not moved back to runtime intern table."); - - try { - Field f = StaticInternString.class.getDeclaredField("intent"); - assertEqual(intern, f.get(null), "String Literals are not interned properly."); - - } catch (Exception e) { - System.out.println("Exception"); - } - - assertEqual(StaticInternString.getIntent(), StaticInternString2.getIntent(), - "String Literals are not intenred properly, App image static strings duplicated."); - - // reload the class StaticInternString, check whether static strings interned properly - final String DEX_FILE = System.getenv("DEX_LOCATION") + "/596-app-images.jar"; - final String LIBRARY_SEARCH_PATH = System.getProperty("java.library.path"); - - try { - Class<?> pathClassLoader = Class.forName("dalvik.system.PathClassLoader"); - if (pathClassLoader == null) { - throw new AssertionError("Counldn't find path class loader class"); - } - Constructor<?> ctor = - pathClassLoader.getDeclaredConstructor(String.class, String.class, ClassLoader.class); - ClassLoader loader = (ClassLoader) ctor.newInstance( - DEX_FILE, LIBRARY_SEARCH_PATH, null); - - Class<?> staticInternString = loader.loadClass("StaticInternString"); - - if (!checkAppImageContains(staticInternString)) { - System.out.println("Not loaded again."); - } - Method getIntent = staticInternString.getDeclaredMethod("getIntent"); - - assertEqual(StaticInternString.getIntent(), getIntent.invoke(staticInternString), - "Dynamically loaded app image's literal strings not interned properly."); - } catch (Exception e) { - e.printStackTrace(System.out); - } - } public static native boolean checkAppImageLoaded(); public static native boolean checkAppImageContains(Class<?> klass); - public static native boolean checkInitialized(Class<?> klass); - - public static void assertEqual(Object a, Object b, String msg) { - if (a != b) - System.out.println(msg); - } - - public static void assertNotEqual(Object a, Object b, String msg) { - if (a == b) - System.out.println(msg); - } - -} - -class StaticFields{ - public static int abc; -} - -class StaticFieldsInitSub extends StaticFieldsInit { - final public static int def = 10; -} - -class StaticFieldsInit{ - final public static int abc = 10; -} - -class StaticInternString { - final public static String intent = "java.abc.Action"; - static public String getIntent() { - return intent; - } -} - -class BootInternedString { - final public static String boot = "double"; } - -class StaticInternString2 { - final public static String intent = "java.abc.Action"; - - static String getIntent() { - return intent; - } -} - diff --git a/test/655-jit-clinit/expected.txt b/test/655-jit-clinit/expected.txt new file mode 100644 index 0000000000..6a5618ebc6 --- /dev/null +++ b/test/655-jit-clinit/expected.txt @@ -0,0 +1 @@ +JNI_OnLoad called diff --git a/test/655-jit-clinit/info.txt b/test/655-jit-clinit/info.txt new file mode 100644 index 0000000000..39c6dec368 --- /dev/null +++ b/test/655-jit-clinit/info.txt @@ -0,0 +1,3 @@ +Regression test for the JIT compiler, which used to wait +on a class object, meaning applocation code could just block +all JIT compilations. diff --git a/test/655-jit-clinit/src/Main.java b/test/655-jit-clinit/src/Main.java new file mode 100644 index 0000000000..5d32180c50 --- /dev/null +++ b/test/655-jit-clinit/src/Main.java @@ -0,0 +1,46 @@ +/* + * 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 { + System.loadLibrary(args[0]); + if (!hasJit()) { + return; + } + Foo.hotMethod(); + } + + public native static boolean isJitCompiled(Class<?> cls, String methodName); + private native static boolean hasJit(); +} + +class Foo { + static void hotMethod() { + for (int i = 0; i < array.length; ++i) { + array[i] = array; + } + } + + static { + array = new Object[10000]; + while (!Main.isJitCompiled(Foo.class, "hotMethod")) { + Foo.hotMethod(); + Thread.yield(); + } + } + + static Object[] array; +} diff --git a/test/906-iterate-heap/expected.txt b/test/906-iterate-heap/expected.txt index 73b7129bba..b6af8435de 100644 --- a/test/906-iterate-heap/expected.txt +++ b/test/906-iterate-heap/expected.txt @@ -18,14 +18,14 @@ 2 1@0 (32, 2xD '0000000000000000000000000000f03f') 2 -doTestPrimitiveFieldsClasses 10000@0 (static, int, index=3) 0000000000000000 10001 10000@0 (static, int, index=11) 0000000000000000 10001 +10000@0 (static, int, index=0) 0000000000000000 10001 +10000@0 (static, int, index=1) 0000000000000000 10001 -doTestPrimitiveFieldsIntegral 10000@0 (instance, int, index=2) 0000000000000000 10001@0 (instance, byte, index=4) 0000000000000001 10002@0 (instance, char, index=5) 0000000000000061 @@ -33,7 +33,6 @@ doTestPrimitiveFieldsIntegral 10004@0 (instance, long, index=7) 0000000000000004 10005@0 (instance, short, index=9) 0000000000000002 10006 -doTestPrimitiveFieldsFloat 10000@0 (instance, int, index=3) 0000000000000000 10001@0 (instance, byte, index=5) 0000000000000001 10002@0 (instance, char, index=6) 0000000000000061 diff --git a/test/906-iterate-heap/iterate_heap.cc b/test/906-iterate-heap/iterate_heap.cc index 02ac69942a..6534b4c3db 100644 --- a/test/906-iterate-heap/iterate_heap.cc +++ b/test/906-iterate-heap/iterate_heap.cc @@ -408,15 +408,5 @@ extern "C" JNIEXPORT jstring JNICALL Java_art_Test906_iterateThroughHeapPrimitiv return env->NewStringUTF(ffc.data.c_str()); } -extern "C" JNIEXPORT jboolean JNICALL Java_art_Test906_checkInitialized( - JNIEnv* env, jclass, jclass c) { - jint status; - jvmtiError error = jvmti_env->GetClassStatus(c, &status); - if (JvmtiErrorToException(env, jvmti_env, error)) { - return false; - } - return (status & JVMTI_CLASS_STATUS_INITIALIZED) != 0; -} - } // namespace Test906IterateHeap } // namespace art diff --git a/test/906-iterate-heap/src/art/Test906.java b/test/906-iterate-heap/src/art/Test906.java index 65c2c8c560..fe18e38501 100644 --- a/test/906-iterate-heap/src/art/Test906.java +++ b/test/906-iterate-heap/src/art/Test906.java @@ -142,7 +142,6 @@ public class Test906 { } private static void doTestPrimitiveFieldsClasses() { - System.out.println("doTestPrimitiveFieldsClasses"); setTag(IntObject.class, 10000); System.out.println(iterateThroughHeapPrimitiveFields(10000)); System.out.println(getTag(IntObject.class)); @@ -153,40 +152,18 @@ public class Test906 { System.out.println(getTag(FloatObject.class)); setTag(FloatObject.class, 0); - boolean correctHeapValue = false; setTag(Inf1.class, 10000); - String heapTrace = iterateThroughHeapPrimitiveFields(10000); - - if (!checkInitialized(Inf1.class)) { - correctHeapValue = heapTrace.equals("10000@0 (static, int, index=0) 0000000000000000"); - } else { - correctHeapValue = heapTrace.equals("10000@0 (static, int, index=0) 0000000000000001"); - } - - if (!correctHeapValue) - System.out.println("Heap Trace for Inf1 is not as expected:\n" + heapTrace); - + System.out.println(iterateThroughHeapPrimitiveFields(10000)); System.out.println(getTag(Inf1.class)); setTag(Inf1.class, 0); setTag(Inf2.class, 10000); - heapTrace = iterateThroughHeapPrimitiveFields(10000); - - if (!checkInitialized(Inf2.class)) { - correctHeapValue = heapTrace.equals("10000@0 (static, int, index=1) 0000000000000000"); - } else { - correctHeapValue = heapTrace.equals("10000@0 (static, int, index=1) 0000000000000001"); - } - - if (!correctHeapValue) - System.out.println("Heap Trace for Inf2 is not as expected:\n" + heapTrace); + System.out.println(iterateThroughHeapPrimitiveFields(10000)); System.out.println(getTag(Inf2.class)); - setTag(Inf2.class, 0); } private static void doTestPrimitiveFieldsIntegral() { - System.out.println("doTestPrimitiveFieldsIntegral"); IntObject intObject = new IntObject(); setTag(intObject, 10000); System.out.println(iterateThroughHeapPrimitiveFields(10000)); @@ -194,7 +171,6 @@ public class Test906 { } private static void doTestPrimitiveFieldsFloat() { - System.out.println("doTestPrimitiveFieldsFloat"); FloatObject floatObject = new FloatObject(); setTag(floatObject, 10000); System.out.println(iterateThroughHeapPrimitiveFields(10000)); @@ -289,7 +265,6 @@ public class Test906 { return Main.getTag(o); } - private static native boolean checkInitialized(Class<?> klass); private static native int iterateThroughHeapCount(int heapFilter, Class<?> klassFilter, int stopAfter); private static native int iterateThroughHeapData(int heapFilter, diff --git a/test/913-heaps/expected.txt b/test/913-heaps/expected.txt index 80f8b9e947..b128d1cb70 100644 --- a/test/913-heaps/expected.txt +++ b/test/913-heaps/expected.txt @@ -140,7 +140,9 @@ root@root --(thread)--> 3000@0 [size=136, length=-1] 10001 10000@0 (static, int, index=11) 0000000000000000 10001 +10000@0 (static, int, index=0) 0000000000000000 10001 +10000@0 (static, int, index=1) 0000000000000000 10001 10000@0 (instance, int, index=2) 0000000000000000 10001@0 (instance, byte, index=4) 0000000000000001 diff --git a/test/913-heaps/heaps.cc b/test/913-heaps/heaps.cc index bf3f7b66a5..ec36cebd43 100644 --- a/test/913-heaps/heaps.cc +++ b/test/913-heaps/heaps.cc @@ -1078,14 +1078,5 @@ extern "C" JNIEXPORT void JNICALL Java_art_Test913_iterateThroughHeapExt( CHECK(gFoundExt); } -extern "C" JNIEXPORT jboolean JNICALL Java_art_Test913_checkInitialized(JNIEnv* env, jclass, jclass c) { - jint status; - jvmtiError error = jvmti_env->GetClassStatus(c, &status); - if (JvmtiErrorToException(env, jvmti_env, error)) { - return false; - } - return (status & JVMTI_CLASS_STATUS_INITIALIZED) != 0; -} - } // namespace Test913Heaps } // namespace art diff --git a/test/913-heaps/src/art/Test913.java b/test/913-heaps/src/art/Test913.java index b9990010ff..97f48eea03 100644 --- a/test/913-heaps/src/art/Test913.java +++ b/test/913-heaps/src/art/Test913.java @@ -195,33 +195,13 @@ public class Test913 { System.out.println(getTag(FloatObject.class)); setTag(FloatObject.class, 0); - boolean correctHeapValue = false; setTag(Inf1.class, 10000); - String heapTrace = followReferencesPrimitiveFields(Inf1.class); - - if (!checkInitialized(Inf1.class)) { - correctHeapValue = heapTrace.equals("10000@0 (static, int, index=0) 0000000000000000"); - } else { - correctHeapValue = heapTrace.equals("10000@0 (static, int, index=0) 0000000000000001"); - } - - if (!correctHeapValue) - System.out.println("Heap Trace for Inf1 is not as expected:\n" + heapTrace); - + System.out.println(followReferencesPrimitiveFields(Inf1.class)); System.out.println(getTag(Inf1.class)); setTag(Inf1.class, 0); setTag(Inf2.class, 10000); - heapTrace = followReferencesPrimitiveFields(Inf2.class); - - if (!checkInitialized(Inf2.class)) { - correctHeapValue = heapTrace.equals("10000@0 (static, int, index=1) 0000000000000000"); - } else { - correctHeapValue = heapTrace.equals("10000@0 (static, int, index=1) 0000000000000001"); - } - - if (!correctHeapValue) - System.out.println("Heap Trace for Inf2 is not as expected:\n" + heapTrace); + System.out.println(followReferencesPrimitiveFields(Inf2.class)); System.out.println(getTag(Inf2.class)); setTag(Inf2.class, 0); } @@ -732,7 +712,6 @@ public class Test913 { return Main.getTag(o); } - private static native boolean checkInitialized(Class<?> klass); private static native void setupGcCallback(); private static native void enableGcTracking(boolean enable); private static native int getGcStarts(); diff --git a/test/Instrumentation/Instrumentation.java b/test/Instrumentation/Instrumentation.java index 09d434213b..b44f78fc3b 100644 --- a/test/Instrumentation/Instrumentation.java +++ b/test/Instrumentation/Instrumentation.java @@ -15,8 +15,21 @@ */ public class Instrumentation { + private static int primitiveField; + private static Object referenceField; + // Direct method private void instanceMethod() { System.out.println("instanceMethod"); } + + private Object returnReference() { + System.out.println("returnReference"); + return null; + } + + private int returnPrimitive() { + System.out.println("returnPrimitive"); + return 0; + } } diff --git a/test/common/runtime_state.cc b/test/common/runtime_state.cc index 7ac019e5d3..d8e5b571bd 100644 --- a/test/common/runtime_state.cc +++ b/test/common/runtime_state.cc @@ -148,12 +148,24 @@ extern "C" JNIEXPORT jboolean JNICALL Java_Main_isAotCompiled(JNIEnv* env, CHECK(chars.c_str() != nullptr); ArtMethod* method = soa.Decode<mirror::Class>(cls)->FindDeclaredDirectMethodByName( chars.c_str(), kRuntimePointerSize); - const void* code = method->GetOatMethodQuickCode(kRuntimePointerSize); - jit::Jit* jit = Runtime::Current()->GetJit(); - if (jit != nullptr && jit->GetCodeCache()->ContainsPc(code)) { - return true; + return method->GetOatMethodQuickCode(kRuntimePointerSize) != nullptr; +} + +extern "C" JNIEXPORT jboolean JNICALL Java_Main_isJitCompiled(JNIEnv* env, + jclass, + jclass cls, + jstring method_name) { + jit::Jit* jit = GetJitIfEnabled(); + if (jit == nullptr) { + return false; } - return code != nullptr; + Thread* self = Thread::Current(); + ScopedObjectAccess soa(self); + ScopedUtfChars chars(env, method_name); + CHECK(chars.c_str() != nullptr); + ArtMethod* method = soa.Decode<mirror::Class>(cls)->FindDeclaredDirectMethodByName( + chars.c_str(), kRuntimePointerSize); + return jit->GetCodeCache()->ContainsPc(method->GetEntryPointFromQuickCompiledCode()); } extern "C" JNIEXPORT void JNICALL Java_Main_ensureJitCompiled(JNIEnv* env, |