diff options
142 files changed, 2765 insertions, 1292 deletions
diff --git a/cmdline/cmdline_types.h b/cmdline/cmdline_types.h index e41d9bde59..28c009edf0 100644 --- a/cmdline/cmdline_types.h +++ b/cmdline/cmdline_types.h @@ -770,8 +770,6 @@ struct CmdlineType<ExperimentalFlags> : CmdlineTypeParser<ExperimentalFlags> { existing = existing | ExperimentalFlags::kAgents; } else if (option == "runtime-plugins") { existing = existing | ExperimentalFlags::kRuntimePlugins; - } else if (option == "method-handles") { - existing = existing | ExperimentalFlags::kMethodHandles; } else { return Result::Failure(std::string("Unknown option '") + option + "'"); } diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc index 29502666ad..faf8b41be1 100644 --- a/compiler/driver/compiler_driver.cc +++ b/compiler/driver/compiler_driver.cc @@ -1060,13 +1060,13 @@ class ResolveCatchBlockExceptionsClassVisitor : public ClassVisitor { virtual bool operator()(ObjPtr<mirror::Class> c) OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_) { const auto pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize(); for (auto& m : c->GetMethods(pointer_size)) { - ResolveExceptionsForMethod(&m, pointer_size); + ResolveExceptionsForMethod(&m); } return true; } private: - void ResolveExceptionsForMethod(ArtMethod* method_handle, PointerSize pointer_size) + void ResolveExceptionsForMethod(ArtMethod* method_handle) REQUIRES_SHARED(Locks::mutator_lock_) { const DexFile::CodeItem* code_item = method_handle->GetCodeItem(); if (code_item == nullptr) { @@ -1088,8 +1088,7 @@ class ResolveCatchBlockExceptionsClassVisitor : public ClassVisitor { dex::TypeIndex encoded_catch_handler_handlers_type_idx = dex::TypeIndex(DecodeUnsignedLeb128(&encoded_catch_handler_list)); // Add to set of types to resolve if not already in the dex cache resolved types - if (!method_handle->IsResolvedTypeIdx(encoded_catch_handler_handlers_type_idx, - pointer_size)) { + if (!method_handle->IsResolvedTypeIdx(encoded_catch_handler_handlers_type_idx)) { exceptions_to_resolve_.emplace(encoded_catch_handler_handlers_type_idx, method_handle->GetDexFile()); } @@ -1950,66 +1949,82 @@ static void PopulateVerifiedMethods(const DexFile& dex_file, DCHECK(!it.HasNext()); } -void CompilerDriver::Verify(jobject jclass_loader, - const std::vector<const DexFile*>& dex_files, - TimingLogger* timings) { +bool CompilerDriver::FastVerify(jobject jclass_loader, + const std::vector<const DexFile*>& dex_files, + TimingLogger* timings) { verifier::VerifierDeps* verifier_deps = Runtime::Current()->GetCompilerCallbacks()->GetVerifierDeps(); // If there is an existing `VerifierDeps`, try to use it for fast verification. - if (verifier_deps != nullptr) { - TimingLogger::ScopedTiming t("Fast Verify", timings); - ScopedObjectAccess soa(Thread::Current()); - StackHandleScope<2> hs(soa.Self()); - Handle<mirror::ClassLoader> class_loader( - hs.NewHandle(soa.Decode<mirror::ClassLoader>(jclass_loader))); - MutableHandle<mirror::Class> cls(hs.NewHandle<mirror::Class>(nullptr)); - ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); - if (verifier_deps->ValidateDependencies(class_loader, soa.Self())) { - // We successfully validated the dependencies, now update class status - // of verified classes. Note that the dependencies also record which classes - // could not be fully verified; we could try again, but that would hurt verification - // time. So instead we assume these classes still need to be verified at - // runtime. - for (const DexFile* dex_file : dex_files) { - // Fetch the list of unverified classes and turn it into a set for faster - // lookups. - const std::vector<dex::TypeIndex>& unverified_classes = - verifier_deps->GetUnverifiedClasses(*dex_file); - std::set<dex::TypeIndex> set(unverified_classes.begin(), unverified_classes.end()); - for (uint32_t i = 0; i < dex_file->NumClassDefs(); ++i) { - const DexFile::ClassDef& class_def = dex_file->GetClassDef(i); - if (set.find(class_def.class_idx_) == set.end()) { - if (!GetCompilerOptions().IsAnyMethodCompilationEnabled()) { - // Just update the compiled_classes_ map. The compiler doesn't need to resolve - // the type. - compiled_classes_.Overwrite( - ClassReference(dex_file, i), new CompiledClass(mirror::Class::kStatusVerified)); - } else { - // Resolve the type, so later compilation stages know they don't need to verify - // the class. - const char* descriptor = dex_file->GetClassDescriptor(class_def); - cls.Assign(class_linker->FindClass(soa.Self(), descriptor, class_loader)); - if (cls.Get() != nullptr) { - ObjectLock<mirror::Class> lock(soa.Self(), cls); - mirror::Class::SetStatus(cls, mirror::Class::kStatusVerified, soa.Self()); - } else { - DCHECK(soa.Self()->IsExceptionPending()); - soa.Self()->ClearException(); - } - // Create `VerifiedMethod`s for each methods, the compiler expects one for - // quickening or compiling. - // Note that this means: - // - We're only going to compile methods that did verify. - // - Quickening will not do checkcast ellision. - // TODO(ngeoffray): Reconsider this once we refactor compiler filters. - PopulateVerifiedMethods(*dex_file, i, verification_results_); + if (verifier_deps == nullptr) { + return false; + } + TimingLogger::ScopedTiming t("Fast Verify", timings); + ScopedObjectAccess soa(Thread::Current()); + StackHandleScope<2> hs(soa.Self()); + Handle<mirror::ClassLoader> class_loader( + hs.NewHandle(soa.Decode<mirror::ClassLoader>(jclass_loader))); + MutableHandle<mirror::Class> cls(hs.NewHandle<mirror::Class>(nullptr)); + ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); + if (!verifier_deps->ValidateDependencies(class_loader, soa.Self())) { + return false; + } + + // We successfully validated the dependencies, now update class status + // of verified classes. Note that the dependencies also record which classes + // could not be fully verified; we could try again, but that would hurt verification + // time. So instead we assume these classes still need to be verified at + // runtime. + for (const DexFile* dex_file : dex_files) { + // Fetch the list of unverified classes and turn it into a set for faster + // lookups. + const std::vector<dex::TypeIndex>& unverified_classes = + verifier_deps->GetUnverifiedClasses(*dex_file); + std::set<dex::TypeIndex> set(unverified_classes.begin(), unverified_classes.end()); + for (uint32_t i = 0; i < dex_file->NumClassDefs(); ++i) { + const DexFile::ClassDef& class_def = dex_file->GetClassDef(i); + if (set.find(class_def.class_idx_) == set.end()) { + if (!GetCompilerOptions().IsAnyMethodCompilationEnabled()) { + // Just update the compiled_classes_ map. The compiler doesn't need to resolve + // the type. + compiled_classes_.Overwrite( + ClassReference(dex_file, i), new CompiledClass(mirror::Class::kStatusVerified)); + } else { + // Resolve the type, so later compilation stages know they don't need to verify + // the class. + const char* descriptor = dex_file->GetClassDescriptor(class_def); + cls.Assign(class_linker->FindClass(soa.Self(), descriptor, class_loader)); + if (cls.Get() != nullptr) { + // Check that the class is resolved with the current dex file. We might get + // a boot image class, or a class in a different dex file for multidex, and + // we should not update the status in that case. + if (&cls->GetDexFile() == dex_file) { + ObjectLock<mirror::Class> lock(soa.Self(), cls); + mirror::Class::SetStatus(cls, mirror::Class::kStatusVerified, soa.Self()); } + } else { + DCHECK(soa.Self()->IsExceptionPending()); + soa.Self()->ClearException(); } + // Create `VerifiedMethod`s for each methods, the compiler expects one for + // quickening or compiling. + // Note that this means: + // - We're only going to compile methods that did verify. + // - Quickening will not do checkcast ellision. + // TODO(ngeoffray): Reconsider this once we refactor compiler filters. + PopulateVerifiedMethods(*dex_file, i, verification_results_); } } - return; } } + return true; +} + +void CompilerDriver::Verify(jobject jclass_loader, + const std::vector<const DexFile*>& dex_files, + TimingLogger* timings) { + if (FastVerify(jclass_loader, dex_files, timings)) { + return; + } // If there is no existing `verifier_deps` (because of non-existing vdex), or // the existing `verifier_deps` is not valid anymore, create a new one for @@ -2017,7 +2032,7 @@ void CompilerDriver::Verify(jobject jclass_loader, // Then dex2oat can update the vdex file with these new dependencies. if (!GetCompilerOptions().IsBootImage()) { // Create the main VerifierDeps, and set it to this thread. - verifier_deps = new verifier::VerifierDeps(dex_files); + verifier::VerifierDeps* verifier_deps = new verifier::VerifierDeps(dex_files); Runtime::Current()->GetCompilerCallbacks()->SetVerifierDeps(verifier_deps); Thread::Current()->SetVerifierDeps(verifier_deps); // Create per-thread VerifierDeps to avoid contention on the main one. @@ -2026,6 +2041,7 @@ void CompilerDriver::Verify(jobject jclass_loader, worker->GetThread()->SetVerifierDeps(new verifier::VerifierDeps(dex_files)); } } + // Note: verification should not be pulling in classes anymore when compiling the boot image, // as all should have been resolved before. As such, doing this in parallel should still // be deterministic. @@ -2041,6 +2057,7 @@ void CompilerDriver::Verify(jobject jclass_loader, if (!GetCompilerOptions().IsBootImage()) { // Merge all VerifierDeps into the main one. + verifier::VerifierDeps* verifier_deps = Thread::Current()->GetVerifierDeps(); for (ThreadPoolWorker* worker : parallel_thread_pool_->GetWorkers()) { verifier::VerifierDeps* thread_deps = worker->GetThread()->GetVerifierDeps(); worker->GetThread()->SetVerifierDeps(nullptr); @@ -2061,7 +2078,10 @@ class VerifyClassVisitor : public CompilationVisitor { ScopedObjectAccess soa(Thread::Current()); const DexFile& dex_file = *manager_->GetDexFile(); if (!manager_->GetCompiler()->ShouldVerifyClassBasedOnProfile(dex_file, class_def_index)) { - // Skip verification since the class is not in the profile. + // Skip verification since the class is not in the profile, and let the VerifierDeps know + // that the class will need to be verified at runtime. + verifier::VerifierDeps::MaybeRecordVerificationStatus( + dex_file, dex::TypeIndex(class_def_index), verifier::MethodVerifier::kSoftFailure); return; } const DexFile::ClassDef& class_def = dex_file.GetClassDef(class_def_index); diff --git a/compiler/driver/compiler_driver.h b/compiler/driver/compiler_driver.h index 2e3b7c8eb5..6bfdd4da9c 100644 --- a/compiler/driver/compiler_driver.h +++ b/compiler/driver/compiler_driver.h @@ -433,12 +433,18 @@ class CompilerDriver { TimingLogger* timings) REQUIRES(!Locks::mutator_lock_); + // Do fast verification through VerifierDeps if possible. Return whether + // verification was successful. // NO_THREAD_SAFETY_ANALYSIS as the method accesses a guarded value in a // single-threaded way. + bool FastVerify(jobject class_loader, + const std::vector<const DexFile*>& dex_files, + TimingLogger* timings) + NO_THREAD_SAFETY_ANALYSIS; + void Verify(jobject class_loader, const std::vector<const DexFile*>& dex_files, - TimingLogger* timings) - NO_THREAD_SAFETY_ANALYSIS; + TimingLogger* timings); void VerifyDexFile(jobject class_loader, const DexFile& dex_file, diff --git a/compiler/oat_test.cc b/compiler/oat_test.cc index 86d92ff0b5..c69ed3198b 100644 --- a/compiler/oat_test.cc +++ b/compiler/oat_test.cc @@ -487,7 +487,7 @@ TEST_F(OatTest, OatHeaderSizeCheck) { EXPECT_EQ(72U, sizeof(OatHeader)); EXPECT_EQ(4U, sizeof(OatMethodOffsets)); EXPECT_EQ(20U, sizeof(OatQuickMethodHeader)); - EXPECT_EQ(164 * static_cast<size_t>(GetInstructionSetPointerSize(kRuntimeISA)), + EXPECT_EQ(159 * static_cast<size_t>(GetInstructionSetPointerSize(kRuntimeISA)), sizeof(QuickEntryPoints)); } diff --git a/compiler/optimizing/builder.h b/compiler/optimizing/builder.h index f896f1199e..8cf4089eba 100644 --- a/compiler/optimizing/builder.h +++ b/compiler/optimizing/builder.h @@ -63,7 +63,8 @@ class HGraphBuilder : public ValueObject { driver, interpreter_metadata, compiler_stats, - dex_cache) {} + dex_cache, + handles) {} // Only for unit testing. HGraphBuilder(HGraph* graph, @@ -90,7 +91,8 @@ class HGraphBuilder : public ValueObject { /* compiler_driver */ nullptr, /* interpreter_metadata */ nullptr, /* compiler_stats */ nullptr, - null_dex_cache_) {} + null_dex_cache_, + handles) {} GraphAnalysisResult BuildGraph(); diff --git a/compiler/optimizing/code_generator.cc b/compiler/optimizing/code_generator.cc index 6c680c8dc6..70c2738010 100644 --- a/compiler/optimizing/code_generator.cc +++ b/compiler/optimizing/code_generator.cc @@ -936,10 +936,10 @@ void CodeGenerator::EmitEnvironment(HEnvironment* environment, SlowPathCode* slo if (environment->GetParent() != nullptr) { // We emit the parent environment first. EmitEnvironment(environment->GetParent(), slow_path); - stack_map_stream_.BeginInlineInfoEntry(environment->GetMethodIdx(), + stack_map_stream_.BeginInlineInfoEntry(environment->GetMethod(), environment->GetDexPc(), - environment->GetInvokeType(), - environment->Size()); + environment->Size(), + &graph_->GetDexFile()); } // Walk over the environment, and record the location of dex registers. diff --git a/compiler/optimizing/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc index ef4bd1e59d..07b174698a 100644 --- a/compiler/optimizing/code_generator_arm.cc +++ b/compiler/optimizing/code_generator_arm.cc @@ -5770,7 +5770,9 @@ void LocationsBuilderARM::VisitLoadClass(HLoadClass* cls) { locations->SetOut(Location::RequiresRegister()); } -void InstructionCodeGeneratorARM::VisitLoadClass(HLoadClass* cls) { +// NO_THREAD_SAFETY_ANALYSIS as we manipulate handles whose internal object we know does not +// move. +void InstructionCodeGeneratorARM::VisitLoadClass(HLoadClass* cls) NO_THREAD_SAFETY_ANALYSIS { HLoadClass::LoadKind load_kind = cls->GetLoadKind(); if (load_kind == HLoadClass::LoadKind::kDexCacheViaMethod) { codegen_->GenerateLoadClassRuntimeCall(cls); @@ -5821,8 +5823,9 @@ void InstructionCodeGeneratorARM::VisitLoadClass(HLoadClass* cls) { } case HLoadClass::LoadKind::kBootImageAddress: { DCHECK_EQ(read_barrier_option, kWithoutReadBarrier); - DCHECK_NE(cls->GetAddress(), 0u); - uint32_t address = dchecked_integral_cast<uint32_t>(cls->GetAddress()); + uint32_t address = dchecked_integral_cast<uint32_t>( + reinterpret_cast<uintptr_t>(cls->GetClass().Get())); + DCHECK_NE(address, 0u); __ LoadLiteral(out, codegen_->DeduplicateBootImageAddressLiteral(address)); break; } @@ -5842,7 +5845,7 @@ void InstructionCodeGeneratorARM::VisitLoadClass(HLoadClass* cls) { case HLoadClass::LoadKind::kJitTableAddress: { __ LoadLiteral(out, codegen_->DeduplicateJitClassLiteral(cls->GetDexFile(), cls->GetTypeIndex(), - cls->GetAddress())); + cls->GetClass())); // /* GcRoot<mirror::Class> */ out = *out GenerateGcRootFieldLoad(cls, out_loc, out, /* offset */ 0, kCompilerReadBarrierOption); break; @@ -7348,8 +7351,9 @@ Literal* CodeGeneratorARM::DeduplicateJitStringLiteral(const DexFile& dex_file, Literal* CodeGeneratorARM::DeduplicateJitClassLiteral(const DexFile& dex_file, dex::TypeIndex type_index, - uint64_t address) { - jit_class_roots_.Overwrite(TypeReference(&dex_file, type_index), address); + Handle<mirror::Class> handle) { + jit_class_roots_.Overwrite(TypeReference(&dex_file, type_index), + reinterpret_cast64<uint64_t>(handle.GetReference())); return jit_class_patches_.GetOrCreate( TypeReference(&dex_file, type_index), [this]() { return __ NewLiteral<uint32_t>(/* placeholder */ 0u); }); diff --git a/compiler/optimizing/code_generator_arm.h b/compiler/optimizing/code_generator_arm.h index bd237e96a5..52d18575ff 100644 --- a/compiler/optimizing/code_generator_arm.h +++ b/compiler/optimizing/code_generator_arm.h @@ -496,7 +496,7 @@ class CodeGeneratorARM : public CodeGenerator { Handle<mirror::String> handle); Literal* DeduplicateJitClassLiteral(const DexFile& dex_file, dex::TypeIndex type_index, - uint64_t address); + Handle<mirror::Class> handle); void EmitLinkerPatches(ArenaVector<LinkerPatch>* linker_patches) OVERRIDE; diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc index a9617e1212..b094e54f8a 100644 --- a/compiler/optimizing/code_generator_arm64.cc +++ b/compiler/optimizing/code_generator_arm64.cc @@ -4013,7 +4013,7 @@ void CodeGeneratorARM64::GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invok break; case HInvokeStaticOrDirect::MethodLoadKind::kDexCachePcRelative: { // Add ADRP with its PC-relative DexCache access patch. - const DexFile& dex_file = invoke->GetDexFile(); + const DexFile& dex_file = invoke->GetDexFileForPcRelativeDexCache(); uint32_t element_offset = invoke->GetDexCacheArrayOffset(); vixl::aarch64::Label* adrp_label = NewPcRelativeDexCacheArrayPatch(dex_file, element_offset); EmitAdrpPlaceholder(adrp_label, XRegisterFrom(temp)); @@ -4181,8 +4181,9 @@ vixl::aarch64::Literal<uint32_t>* CodeGeneratorARM64::DeduplicateJitStringLitera } vixl::aarch64::Literal<uint32_t>* CodeGeneratorARM64::DeduplicateJitClassLiteral( - const DexFile& dex_file, dex::TypeIndex type_index, uint64_t address) { - jit_class_roots_.Overwrite(TypeReference(&dex_file, type_index), address); + const DexFile& dex_file, dex::TypeIndex type_index, Handle<mirror::Class> handle) { + jit_class_roots_.Overwrite(TypeReference(&dex_file, type_index), + reinterpret_cast64<uint64_t>(handle.GetReference())); return jit_class_patches_.GetOrCreate( TypeReference(&dex_file, type_index), [this]() { return __ CreateLiteralDestroyedWithPool<uint32_t>(/* placeholder */ 0u); }); @@ -4377,7 +4378,9 @@ void LocationsBuilderARM64::VisitLoadClass(HLoadClass* cls) { locations->SetOut(Location::RequiresRegister()); } -void InstructionCodeGeneratorARM64::VisitLoadClass(HLoadClass* cls) { +// NO_THREAD_SAFETY_ANALYSIS as we manipulate handles whose internal object we know does not +// move. +void InstructionCodeGeneratorARM64::VisitLoadClass(HLoadClass* cls) NO_THREAD_SAFETY_ANALYSIS { HLoadClass::LoadKind load_kind = cls->GetLoadKind(); if (load_kind == HLoadClass::LoadKind::kDexCacheViaMethod) { codegen_->GenerateLoadClassRuntimeCall(cls); @@ -4426,8 +4429,10 @@ void InstructionCodeGeneratorARM64::VisitLoadClass(HLoadClass* cls) { } case HLoadClass::LoadKind::kBootImageAddress: { DCHECK_EQ(read_barrier_option, kWithoutReadBarrier); - DCHECK(cls->GetAddress() != 0u && IsUint<32>(cls->GetAddress())); - __ Ldr(out.W(), codegen_->DeduplicateBootImageAddressLiteral(cls->GetAddress())); + uint32_t address = dchecked_integral_cast<uint32_t>( + reinterpret_cast<uintptr_t>(cls->GetClass().Get())); + DCHECK_NE(address, 0u); + __ Ldr(out.W(), codegen_->DeduplicateBootImageAddressLiteral(address)); break; } case HLoadClass::LoadKind::kBssEntry: { @@ -4452,7 +4457,7 @@ void InstructionCodeGeneratorARM64::VisitLoadClass(HLoadClass* cls) { case HLoadClass::LoadKind::kJitTableAddress: { __ Ldr(out, codegen_->DeduplicateJitClassLiteral(cls->GetDexFile(), cls->GetTypeIndex(), - cls->GetAddress())); + cls->GetClass())); GenerateGcRootFieldLoad(cls, out_loc, out.X(), diff --git a/compiler/optimizing/code_generator_arm64.h b/compiler/optimizing/code_generator_arm64.h index c7a06145e4..a9dca92980 100644 --- a/compiler/optimizing/code_generator_arm64.h +++ b/compiler/optimizing/code_generator_arm64.h @@ -579,7 +579,7 @@ class CodeGeneratorARM64 : public CodeGenerator { Handle<mirror::String> handle); vixl::aarch64::Literal<uint32_t>* DeduplicateJitClassLiteral(const DexFile& dex_file, dex::TypeIndex string_index, - uint64_t address); + Handle<mirror::Class> handle); void EmitAdrpPlaceholder(vixl::aarch64::Label* fixup_label, vixl::aarch64::Register reg); void EmitAddPlaceholder(vixl::aarch64::Label* fixup_label, diff --git a/compiler/optimizing/code_generator_arm_vixl.cc b/compiler/optimizing/code_generator_arm_vixl.cc index f496d29b59..ecabc58c4d 100644 --- a/compiler/optimizing/code_generator_arm_vixl.cc +++ b/compiler/optimizing/code_generator_arm_vixl.cc @@ -5848,7 +5848,9 @@ void LocationsBuilderARMVIXL::VisitLoadClass(HLoadClass* cls) { locations->SetOut(Location::RequiresRegister()); } -void InstructionCodeGeneratorARMVIXL::VisitLoadClass(HLoadClass* cls) { +// NO_THREAD_SAFETY_ANALYSIS as we manipulate handles whose internal object we know does not +// move. +void InstructionCodeGeneratorARMVIXL::VisitLoadClass(HLoadClass* cls) NO_THREAD_SAFETY_ANALYSIS { HLoadClass::LoadKind load_kind = cls->GetLoadKind(); if (load_kind == HLoadClass::LoadKind::kDexCacheViaMethod) { codegen_->GenerateLoadClassRuntimeCall(cls); @@ -5894,8 +5896,9 @@ void InstructionCodeGeneratorARMVIXL::VisitLoadClass(HLoadClass* cls) { } case HLoadClass::LoadKind::kBootImageAddress: { DCHECK_EQ(read_barrier_option, kWithoutReadBarrier); - DCHECK_NE(cls->GetAddress(), 0u); - uint32_t address = dchecked_integral_cast<uint32_t>(cls->GetAddress()); + uint32_t address = dchecked_integral_cast<uint32_t>( + reinterpret_cast<uintptr_t>(cls->GetClass().Get())); + DCHECK_NE(address, 0u); __ Ldr(out, codegen_->DeduplicateBootImageAddressLiteral(address)); break; } @@ -5910,7 +5913,7 @@ void InstructionCodeGeneratorARMVIXL::VisitLoadClass(HLoadClass* cls) { case HLoadClass::LoadKind::kJitTableAddress: { __ Ldr(out, codegen_->DeduplicateJitClassLiteral(cls->GetDexFile(), cls->GetTypeIndex(), - cls->GetAddress())); + cls->GetClass())); // /* GcRoot<mirror::Class> */ out = *out GenerateGcRootFieldLoad(cls, out_loc, out, /* offset */ 0, kCompilerReadBarrierOption); break; @@ -7468,8 +7471,9 @@ VIXLUInt32Literal* CodeGeneratorARMVIXL::DeduplicateJitStringLiteral( VIXLUInt32Literal* CodeGeneratorARMVIXL::DeduplicateJitClassLiteral(const DexFile& dex_file, dex::TypeIndex type_index, - uint64_t address) { - jit_class_roots_.Overwrite(TypeReference(&dex_file, type_index), address); + Handle<mirror::Class> handle) { + jit_class_roots_.Overwrite(TypeReference(&dex_file, type_index), + reinterpret_cast64<uint64_t>(handle.GetReference())); return jit_class_patches_.GetOrCreate( TypeReference(&dex_file, type_index), [this]() { diff --git a/compiler/optimizing/code_generator_arm_vixl.h b/compiler/optimizing/code_generator_arm_vixl.h index 0f0a9540ba..be653535ea 100644 --- a/compiler/optimizing/code_generator_arm_vixl.h +++ b/compiler/optimizing/code_generator_arm_vixl.h @@ -579,7 +579,7 @@ class CodeGeneratorARMVIXL : public CodeGenerator { Handle<mirror::String> handle); VIXLUInt32Literal* DeduplicateJitClassLiteral(const DexFile& dex_file, dex::TypeIndex type_index, - uint64_t address); + Handle<mirror::Class> handle); void EmitLinkerPatches(ArenaVector<LinkerPatch>* linker_patches) OVERRIDE; diff --git a/compiler/optimizing/code_generator_mips.cc b/compiler/optimizing/code_generator_mips.cc index 1f4ff279e8..24234e18c1 100644 --- a/compiler/optimizing/code_generator_mips.cc +++ b/compiler/optimizing/code_generator_mips.cc @@ -5490,7 +5490,9 @@ void LocationsBuilderMIPS::VisitLoadClass(HLoadClass* cls) { locations->SetOut(Location::RequiresRegister()); } -void InstructionCodeGeneratorMIPS::VisitLoadClass(HLoadClass* cls) { +// NO_THREAD_SAFETY_ANALYSIS as we manipulate handles whose internal object we know does not +// move. +void InstructionCodeGeneratorMIPS::VisitLoadClass(HLoadClass* cls) NO_THREAD_SAFETY_ANALYSIS { HLoadClass::LoadKind load_kind = cls->GetLoadKind(); if (load_kind == HLoadClass::LoadKind::kDexCacheViaMethod) { codegen_->GenerateLoadClassRuntimeCall(cls); @@ -5548,8 +5550,9 @@ void InstructionCodeGeneratorMIPS::VisitLoadClass(HLoadClass* cls) { } case HLoadClass::LoadKind::kBootImageAddress: { DCHECK(!kEmitCompilerReadBarrier); - DCHECK_NE(cls->GetAddress(), 0u); - uint32_t address = dchecked_integral_cast<uint32_t>(cls->GetAddress()); + uint32_t address = dchecked_integral_cast<uint32_t>( + reinterpret_cast<uintptr_t>(cls->GetClass().Get())); + DCHECK_NE(address, 0u); __ LoadLiteral(out, base_or_current_method_reg, codegen_->DeduplicateBootImageAddressLiteral(address)); diff --git a/compiler/optimizing/code_generator_mips64.cc b/compiler/optimizing/code_generator_mips64.cc index a350de7a51..fc8fb7acb2 100644 --- a/compiler/optimizing/code_generator_mips64.cc +++ b/compiler/optimizing/code_generator_mips64.cc @@ -3395,7 +3395,7 @@ void CodeGeneratorMIPS64::GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invo case HInvokeStaticOrDirect::MethodLoadKind::kDexCachePcRelative: { uint32_t offset = invoke->GetDexCacheArrayOffset(); CodeGeneratorMIPS64::PcRelativePatchInfo* info = - NewPcRelativeDexCacheArrayPatch(invoke->GetDexFile(), offset); + NewPcRelativeDexCacheArrayPatch(invoke->GetDexFileForPcRelativeDexCache(), offset); EmitPcRelativeAddressPlaceholderHigh(info, AT); __ Ld(temp.AsRegister<GpuRegister>(), AT, /* placeholder */ 0x5678); break; @@ -3524,7 +3524,9 @@ void LocationsBuilderMIPS64::VisitLoadClass(HLoadClass* cls) { locations->SetOut(Location::RequiresRegister()); } -void InstructionCodeGeneratorMIPS64::VisitLoadClass(HLoadClass* cls) { +// NO_THREAD_SAFETY_ANALYSIS as we manipulate handles whose internal object we know does not +// move. +void InstructionCodeGeneratorMIPS64::VisitLoadClass(HLoadClass* cls) NO_THREAD_SAFETY_ANALYSIS { HLoadClass::LoadKind load_kind = cls->GetLoadKind(); if (load_kind == HLoadClass::LoadKind::kDexCacheViaMethod) { codegen_->GenerateLoadClassRuntimeCall(cls); @@ -3569,8 +3571,9 @@ void InstructionCodeGeneratorMIPS64::VisitLoadClass(HLoadClass* cls) { } case HLoadClass::LoadKind::kBootImageAddress: { DCHECK(!kEmitCompilerReadBarrier); - DCHECK_NE(cls->GetAddress(), 0u); - uint32_t address = dchecked_integral_cast<uint32_t>(cls->GetAddress()); + uint32_t address = dchecked_integral_cast<uint32_t>( + reinterpret_cast<uintptr_t>(cls->GetClass().Get())); + DCHECK_NE(address, 0u); __ LoadLiteral(out, kLoadUnsignedWord, codegen_->DeduplicateBootImageAddressLiteral(address)); diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc index a850d38dca..cc727d2068 100644 --- a/compiler/optimizing/code_generator_x86.cc +++ b/compiler/optimizing/code_generator_x86.cc @@ -4521,7 +4521,7 @@ Location CodeGeneratorX86::GenerateCalleeMethodStaticOrDirectCall(HInvokeStaticO __ movl(temp.AsRegister<Register>(), Address(base_reg, kDummy32BitOffset)); // Bind a new fixup label at the end of the "movl" insn. uint32_t offset = invoke->GetDexCacheArrayOffset(); - __ Bind(NewPcRelativeDexCacheArrayPatch(invoke->GetDexFile(), offset)); + __ Bind(NewPcRelativeDexCacheArrayPatch(invoke->GetDexFileForPcRelativeDexCache(), offset)); break; } case HInvokeStaticOrDirect::MethodLoadKind::kDexCacheViaMethod: { @@ -6055,15 +6055,18 @@ void LocationsBuilderX86::VisitLoadClass(HLoadClass* cls) { Label* CodeGeneratorX86::NewJitRootClassPatch(const DexFile& dex_file, dex::TypeIndex dex_index, - uint64_t address) { - jit_class_roots_.Overwrite(TypeReference(&dex_file, dex_index), address); + Handle<mirror::Class> handle) { + jit_class_roots_.Overwrite(TypeReference(&dex_file, dex_index), + reinterpret_cast64<uint64_t>(handle.GetReference())); // Add a patch entry and return the label. jit_class_patches_.emplace_back(dex_file, dex_index.index_); PatchInfo<Label>* info = &jit_class_patches_.back(); return &info->label; } -void InstructionCodeGeneratorX86::VisitLoadClass(HLoadClass* cls) { +// NO_THREAD_SAFETY_ANALYSIS as we manipulate handles whose internal object we know does not +// move. +void InstructionCodeGeneratorX86::VisitLoadClass(HLoadClass* cls) NO_THREAD_SAFETY_ANALYSIS { HLoadClass::LoadKind load_kind = cls->GetLoadKind(); if (load_kind == HLoadClass::LoadKind::kDexCacheViaMethod) { codegen_->GenerateLoadClassRuntimeCall(cls); @@ -6110,8 +6113,9 @@ void InstructionCodeGeneratorX86::VisitLoadClass(HLoadClass* cls) { } case HLoadClass::LoadKind::kBootImageAddress: { DCHECK_EQ(read_barrier_option, kWithoutReadBarrier); - DCHECK_NE(cls->GetAddress(), 0u); - uint32_t address = dchecked_integral_cast<uint32_t>(cls->GetAddress()); + uint32_t address = dchecked_integral_cast<uint32_t>( + reinterpret_cast<uintptr_t>(cls->GetClass().Get())); + DCHECK_NE(address, 0u); __ movl(out, Immediate(address)); codegen_->RecordSimplePatch(); break; @@ -6127,7 +6131,7 @@ void InstructionCodeGeneratorX86::VisitLoadClass(HLoadClass* cls) { case HLoadClass::LoadKind::kJitTableAddress: { Address address = Address::Absolute(CodeGeneratorX86::kDummy32BitOffset); Label* fixup_label = codegen_->NewJitRootClassPatch( - cls->GetDexFile(), cls->GetTypeIndex(), cls->GetAddress()); + cls->GetDexFile(), cls->GetTypeIndex(), cls->GetClass()); // /* GcRoot<mirror::Class> */ out = *address GenerateGcRootFieldLoad(cls, out_loc, address, fixup_label, kCompilerReadBarrierOption); break; diff --git a/compiler/optimizing/code_generator_x86.h b/compiler/optimizing/code_generator_x86.h index b86d080aa6..9eb97658da 100644 --- a/compiler/optimizing/code_generator_x86.h +++ b/compiler/optimizing/code_generator_x86.h @@ -419,7 +419,9 @@ class CodeGeneratorX86 : public CodeGenerator { Label* NewJitRootStringPatch(const DexFile& dex_file, dex::StringIndex dex_index, Handle<mirror::String> handle); - Label* NewJitRootClassPatch(const DexFile& dex_file, dex::TypeIndex dex_index, uint64_t address); + Label* NewJitRootClassPatch(const DexFile& dex_file, + dex::TypeIndex dex_index, + Handle<mirror::Class> handle); void MoveFromReturnRegister(Location trg, Primitive::Type type) OVERRIDE; diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc index 2691af8245..9adedab130 100644 --- a/compiler/optimizing/code_generator_x86_64.cc +++ b/compiler/optimizing/code_generator_x86_64.cc @@ -991,7 +991,7 @@ Location CodeGeneratorX86_64::GenerateCalleeMethodStaticOrDirectCall(HInvokeStat Address::Absolute(kDummy32BitOffset, /* no_rip */ false)); // Bind a new fixup label at the end of the "movl" insn. uint32_t offset = invoke->GetDexCacheArrayOffset(); - __ Bind(NewPcRelativeDexCacheArrayPatch(invoke->GetDexFile(), offset)); + __ Bind(NewPcRelativeDexCacheArrayPatch(invoke->GetDexFileForPcRelativeDexCache(), offset)); break; } case HInvokeStaticOrDirect::MethodLoadKind::kDexCacheViaMethod: { @@ -5482,15 +5482,18 @@ void LocationsBuilderX86_64::VisitLoadClass(HLoadClass* cls) { Label* CodeGeneratorX86_64::NewJitRootClassPatch(const DexFile& dex_file, dex::TypeIndex dex_index, - uint64_t address) { - jit_class_roots_.Overwrite(TypeReference(&dex_file, dex_index), address); + Handle<mirror::Class> handle) { + jit_class_roots_.Overwrite( + TypeReference(&dex_file, dex_index), reinterpret_cast64<uint64_t>(handle.GetReference())); // Add a patch entry and return the label. jit_class_patches_.emplace_back(dex_file, dex_index.index_); PatchInfo<Label>* info = &jit_class_patches_.back(); return &info->label; } -void InstructionCodeGeneratorX86_64::VisitLoadClass(HLoadClass* cls) { +// NO_THREAD_SAFETY_ANALYSIS as we manipulate handles whose internal object we know does not +// move. +void InstructionCodeGeneratorX86_64::VisitLoadClass(HLoadClass* cls) NO_THREAD_SAFETY_ANALYSIS { HLoadClass::LoadKind load_kind = cls->GetLoadKind(); if (load_kind == HLoadClass::LoadKind::kDexCacheViaMethod) { codegen_->GenerateLoadClassRuntimeCall(cls); @@ -5528,8 +5531,9 @@ void InstructionCodeGeneratorX86_64::VisitLoadClass(HLoadClass* cls) { break; case HLoadClass::LoadKind::kBootImageAddress: { DCHECK_EQ(read_barrier_option, kWithoutReadBarrier); - DCHECK_NE(cls->GetAddress(), 0u); - uint32_t address = dchecked_integral_cast<uint32_t>(cls->GetAddress()); + uint32_t address = dchecked_integral_cast<uint32_t>( + reinterpret_cast<uintptr_t>(cls->GetClass().Get())); + DCHECK_NE(address, 0u); __ movl(out, Immediate(address)); // Zero-extended. codegen_->RecordSimplePatch(); break; @@ -5547,7 +5551,7 @@ void InstructionCodeGeneratorX86_64::VisitLoadClass(HLoadClass* cls) { Address address = Address::Absolute(CodeGeneratorX86_64::kDummy32BitOffset, /* no_rip */ true); Label* fixup_label = - codegen_->NewJitRootClassPatch(cls->GetDexFile(), cls->GetTypeIndex(), cls->GetAddress()); + codegen_->NewJitRootClassPatch(cls->GetDexFile(), cls->GetTypeIndex(), cls->GetClass()); // /* GcRoot<mirror::Class> */ out = *address GenerateGcRootFieldLoad(cls, out_loc, address, fixup_label, kCompilerReadBarrierOption); break; diff --git a/compiler/optimizing/code_generator_x86_64.h b/compiler/optimizing/code_generator_x86_64.h index 8b3ab4c438..3438b8159f 100644 --- a/compiler/optimizing/code_generator_x86_64.h +++ b/compiler/optimizing/code_generator_x86_64.h @@ -416,7 +416,9 @@ class CodeGeneratorX86_64 : public CodeGenerator { Label* NewJitRootStringPatch(const DexFile& dex_file, dex::StringIndex dex_index, Handle<mirror::String> handle); - Label* NewJitRootClassPatch(const DexFile& dex_file, dex::TypeIndex dex_index, uint64_t address); + Label* NewJitRootClassPatch(const DexFile& dex_file, + dex::TypeIndex dex_index, + Handle<mirror::Class> handle); void MoveFromReturnRegister(Location trg, Primitive::Type type) OVERRIDE; diff --git a/compiler/optimizing/dex_cache_array_fixups_arm.cc b/compiler/optimizing/dex_cache_array_fixups_arm.cc index a3140d00cb..9ddcd563ca 100644 --- a/compiler/optimizing/dex_cache_array_fixups_arm.cc +++ b/compiler/optimizing/dex_cache_array_fixups_arm.cc @@ -64,9 +64,10 @@ class DexCacheArrayFixupsVisitor : public HGraphVisitor { // we need to add the dex cache arrays base as the special input. if (invoke->HasPcRelativeDexCache() && !IsCallFreeIntrinsic<IntrinsicLocationsBuilderARMType>(invoke, codegen_)) { - HArmDexCacheArraysBase* base = GetOrCreateDexCacheArrayBase(invoke->GetDexFile()); + HArmDexCacheArraysBase* base = + GetOrCreateDexCacheArrayBase(invoke->GetDexFileForPcRelativeDexCache()); // Update the element offset in base. - DexCacheArraysLayout layout(kArmPointerSize, &invoke->GetDexFile()); + DexCacheArraysLayout layout(kArmPointerSize, &invoke->GetDexFileForPcRelativeDexCache()); base->UpdateElementOffset(layout.MethodOffset(invoke->GetDexMethodIndex())); // Add the special argument base to the method. DCHECK(!invoke->HasCurrentMethodInput()); diff --git a/compiler/optimizing/dex_cache_array_fixups_mips.cc b/compiler/optimizing/dex_cache_array_fixups_mips.cc index 9a34f544ad..04a4294c48 100644 --- a/compiler/optimizing/dex_cache_array_fixups_mips.cc +++ b/compiler/optimizing/dex_cache_array_fixups_mips.cc @@ -59,9 +59,10 @@ class DexCacheArrayFixupsVisitor : public HGraphVisitor { if (invoke->HasPcRelativeDexCache() && !IsCallFreeIntrinsic<IntrinsicLocationsBuilderMIPS>(invoke, codegen_)) { // Initialize base for target method dex file if needed. - HMipsDexCacheArraysBase* base = GetOrCreateDexCacheArrayBase(invoke->GetDexFile()); + HMipsDexCacheArraysBase* base = + GetOrCreateDexCacheArrayBase(invoke->GetDexFileForPcRelativeDexCache()); // Update the element offset in base. - DexCacheArraysLayout layout(kMipsPointerSize, &invoke->GetDexFile()); + DexCacheArraysLayout layout(kMipsPointerSize, &invoke->GetDexFileForPcRelativeDexCache()); base->UpdateElementOffset(layout.MethodOffset(invoke->GetDexMethodIndex())); // Add the special argument base to the method. DCHECK(!invoke->HasCurrentMethodInput()); diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc index d7da46bbe7..78a4251e3a 100644 --- a/compiler/optimizing/inliner.cc +++ b/compiler/optimizing/inliner.cc @@ -474,10 +474,10 @@ bool HInliner::TryInlineMonomorphicCall(HInvoke* invoke_instruction, HInstruction* receiver = invoke_instruction->InputAt(0); HInstruction* cursor = invoke_instruction->GetPrevious(); HBasicBlock* bb_cursor = invoke_instruction->GetBlock(); - Handle<mirror::Class> handle = handles_->NewHandle(GetMonomorphicType(classes)); + Handle<mirror::Class> monomorphic_type = handles_->NewHandle(GetMonomorphicType(classes)); if (!TryInlineAndReplace(invoke_instruction, resolved_method, - ReferenceTypeInfo::Create(handle, /* is_exact */ true), + ReferenceTypeInfo::Create(monomorphic_type, /* is_exact */ true), /* do_rtp */ false, /* cha_devirtualize */ false)) { return false; @@ -488,7 +488,7 @@ bool HInliner::TryInlineMonomorphicCall(HInvoke* invoke_instruction, cursor, bb_cursor, class_index, - GetMonomorphicType(classes), + monomorphic_type, invoke_instruction, /* with_deoptimization */ true); @@ -533,11 +533,9 @@ HInstruction* HInliner::AddTypeGuard(HInstruction* receiver, HInstruction* cursor, HBasicBlock* bb_cursor, dex::TypeIndex class_index, - mirror::Class* klass, + Handle<mirror::Class> klass, HInstruction* invoke_instruction, bool with_deoptimization) { - ScopedAssertNoThreadSuspension sants("Adding compiler type guard"); - ClassLinker* class_linker = caller_compilation_unit_.GetClassLinker(); HInstanceFieldGet* receiver_class = BuildGetReceiverClass( class_linker, receiver, invoke_instruction->GetDexPc()); @@ -548,19 +546,20 @@ HInstruction* HInliner::AddTypeGuard(HInstruction* receiver, } const DexFile& caller_dex_file = *caller_compilation_unit_.GetDexFile(); - bool is_referrer = (klass == outermost_graph_->GetArtMethod()->GetDeclaringClass()); + bool is_referrer = (klass.Get() == outermost_graph_->GetArtMethod()->GetDeclaringClass()); // Note that we will just compare the classes, so we don't need Java semantics access checks. // Note that the type index and the dex file are relative to the method this type guard is // inlined into. HLoadClass* load_class = new (graph_->GetArena()) HLoadClass(graph_->GetCurrentMethod(), class_index, caller_dex_file, + klass, is_referrer, invoke_instruction->GetDexPc(), /* needs_access_check */ false); bb_cursor->InsertInstructionAfter(load_class, receiver_class); // Sharpen after adding the instruction, as the sharpening may remove inputs. - HSharpening::SharpenClass(load_class, klass, handles_, codegen_, compiler_driver_); + HSharpening::SharpenClass(load_class, codegen_, compiler_driver_); // TODO: Extend reference type propagation to understand the guard. HNotEqual* compare = new (graph_->GetArena()) HNotEqual(load_class, receiver_class); @@ -637,7 +636,7 @@ bool HInliner::TryInlinePolymorphicCall(HInvoke* invoke_instruction, cursor, bb_cursor, class_index, - handle.Get(), + handle, invoke_instruction, deoptimize); if (deoptimize) { @@ -1539,8 +1538,6 @@ bool HInliner::ArgumentTypesMoreSpecific(HInvoke* invoke_instruction, ArtMethod* } } - PointerSize pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize(); - // Iterate over the list of parameter types and test whether any of the // actual inputs has a more specific reference type than the type declared in // the signature. @@ -1552,9 +1549,9 @@ bool HInliner::ArgumentTypesMoreSpecific(HInvoke* invoke_instruction, ArtMethod* ++param_idx, ++input_idx) { HInstruction* input = invoke_instruction->InputAt(input_idx); if (input->GetType() == Primitive::kPrimNot) { - mirror::Class* param_cls = resolved_method->GetDexCacheResolvedType( + mirror::Class* param_cls = resolved_method->GetClassFromTypeIndex( param_list->GetTypeItem(param_idx).type_idx_, - pointer_size); + /* resolve */ false); if (IsReferenceTypeRefinement(GetClassRTI(param_cls), /* declared_can_be_null */ true, input)) { @@ -1603,8 +1600,7 @@ void HInliner::FixUpReturnReferenceType(ArtMethod* resolved_method, // TODO: we could be more precise by merging the phi inputs but that requires // some functionality from the reference type propagation. DCHECK(return_replacement->IsPhi()); - PointerSize pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize(); - mirror::Class* cls = resolved_method->GetReturnType(false /* resolve */, pointer_size); + mirror::Class* cls = resolved_method->GetReturnType(false /* resolve */); return_replacement->SetReferenceTypeInfo(GetClassRTI(cls)); } } diff --git a/compiler/optimizing/inliner.h b/compiler/optimizing/inliner.h index 4c0b990f26..11aacab802 100644 --- a/compiler/optimizing/inliner.h +++ b/compiler/optimizing/inliner.h @@ -170,7 +170,7 @@ class HInliner : public HOptimization { HInstruction* cursor, HBasicBlock* bb_cursor, dex::TypeIndex class_index, - mirror::Class* klass, + Handle<mirror::Class> klass, HInstruction* invoke_instruction, bool with_deoptimization) REQUIRES_SHARED(Locks::mutator_lock_); diff --git a/compiler/optimizing/instruction_builder.cc b/compiler/optimizing/instruction_builder.cc index 3cfabddf3a..8ed0e7fa06 100644 --- a/compiler/optimizing/instruction_builder.cc +++ b/compiler/optimizing/instruction_builder.cc @@ -208,10 +208,8 @@ void HInstructionBuilder::InitializeInstruction(HInstruction* instruction) { HEnvironment* environment = new (arena_) HEnvironment( arena_, current_locals_->size(), - graph_->GetDexFile(), - graph_->GetMethodIdx(), + graph_->GetArtMethod(), instruction->GetDexPc(), - graph_->GetInvokeType(), instruction); environment->CopyFrom(*current_locals_); instruction->SetRawEnvironment(environment); @@ -936,48 +934,40 @@ bool HInstructionBuilder::BuildInvokePolymorphic(const Instruction& instruction bool HInstructionBuilder::BuildNewInstance(dex::TypeIndex type_index, uint32_t dex_pc) { ScopedObjectAccess soa(Thread::Current()); - StackHandleScope<1> hs(soa.Self()); Handle<mirror::DexCache> dex_cache = dex_compilation_unit_->GetDexCache(); - Handle<mirror::Class> resolved_class(hs.NewHandle(dex_cache->GetResolvedType(type_index))); - const DexFile& outer_dex_file = *outer_compilation_unit_->GetDexFile(); Handle<mirror::DexCache> outer_dex_cache = outer_compilation_unit_->GetDexCache(); - bool finalizable; - bool needs_access_check = NeedsAccessCheck(type_index, dex_cache, &finalizable); - - // Only the access check entrypoint handles the finalizable class case. If we - // need access checks, then we haven't resolved the method and the class may - // again be finalizable. - QuickEntrypointEnum entrypoint = (finalizable || needs_access_check) - ? kQuickAllocObjectWithChecks - : kQuickAllocObjectInitialized; - if (outer_dex_cache.Get() != dex_cache.Get()) { // We currently do not support inlining allocations across dex files. return false; } - HLoadClass* load_class = new (arena_) HLoadClass( - graph_->GetCurrentMethod(), - type_index, - outer_dex_file, - IsOutermostCompilingClass(type_index), - dex_pc, - needs_access_check); + HLoadClass* load_class = BuildLoadClass(type_index, dex_pc, /* check_access */ true); - AppendInstruction(load_class); HInstruction* cls = load_class; - if (!IsInitialized(resolved_class)) { + Handle<mirror::Class> klass = load_class->GetClass(); + + if (!IsInitialized(klass)) { cls = new (arena_) HClinitCheck(load_class, dex_pc); AppendInstruction(cls); } + // Only the access check entrypoint handles the finalizable class case. If we + // need access checks, then we haven't resolved the method and the class may + // again be finalizable. + QuickEntrypointEnum entrypoint = kQuickAllocObjectInitialized; + if (load_class->NeedsAccessCheck() || klass->IsFinalizable() || !klass->IsInstantiable()) { + entrypoint = kQuickAllocObjectWithChecks; + } + + // Consider classes we haven't resolved as potentially finalizable. + bool finalizable = (klass.Get() == nullptr) || klass->IsFinalizable(); + AppendInstruction(new (arena_) HNewInstance( cls, dex_pc, type_index, *dex_compilation_unit_->GetDexFile(), - needs_access_check, finalizable, entrypoint)); return true; @@ -1018,7 +1008,6 @@ HClinitCheck* HInstructionBuilder::ProcessClinitCheckForInvoke( ArtMethod* resolved_method, uint32_t method_idx, HInvokeStaticOrDirect::ClinitCheckRequirement* clinit_check_requirement) { - const DexFile& outer_dex_file = *outer_compilation_unit_->GetDexFile(); Thread* self = Thread::Current(); StackHandleScope<2> hs(self); Handle<mirror::DexCache> dex_cache = dex_compilation_unit_->GetDexCache(); @@ -1046,15 +1035,9 @@ HClinitCheck* HInstructionBuilder::ProcessClinitCheckForInvoke( *clinit_check_requirement = HInvokeStaticOrDirect::ClinitCheckRequirement::kNone; } else if (storage_index.IsValid()) { *clinit_check_requirement = HInvokeStaticOrDirect::ClinitCheckRequirement::kExplicit; - HLoadClass* load_class = new (arena_) HLoadClass( - graph_->GetCurrentMethod(), - storage_index, - outer_dex_file, - is_outer_class, - dex_pc, - /*needs_access_check*/ false); - AppendInstruction(load_class); - clinit_check = new (arena_) HClinitCheck(load_class, dex_pc); + HLoadClass* cls = BuildLoadClass( + storage_index, dex_pc, /* check_access */ false, /* outer */ true); + clinit_check = new (arena_) HClinitCheck(cls, dex_pc); AppendInstruction(clinit_check); } return clinit_check; @@ -1376,7 +1359,6 @@ bool HInstructionBuilder::BuildStaticFieldAccess(const Instruction& instruction, } Primitive::Type field_type = resolved_field->GetTypeAsPrimitiveType(); - const DexFile& outer_dex_file = *outer_compilation_unit_->GetDexFile(); Handle<mirror::DexCache> outer_dex_cache = outer_compilation_unit_->GetDexCache(); Handle<mirror::Class> outer_class(hs.NewHandle(GetOutermostCompilingClass())); @@ -1404,16 +1386,10 @@ bool HInstructionBuilder::BuildStaticFieldAccess(const Instruction& instruction, } } - HLoadClass* constant = new (arena_) HLoadClass(graph_->GetCurrentMethod(), - storage_index, - outer_dex_file, - is_outer_class, - dex_pc, - /*needs_access_check*/ false); - AppendInstruction(constant); + HLoadClass* constant = BuildLoadClass( + storage_index, dex_pc, /* check_access */ false, /* outer */ true); HInstruction* cls = constant; - Handle<mirror::Class> klass(hs.NewHandle(resolved_field->GetDeclaringClass())); if (!IsInitialized(klass)) { cls = new (arena_) HClinitCheck(constant, dex_pc); @@ -1660,33 +1636,53 @@ static TypeCheckKind ComputeTypeCheckKind(Handle<mirror::Class> cls) } } -void HInstructionBuilder::BuildTypeCheck(const Instruction& instruction, - uint8_t destination, - uint8_t reference, - dex::TypeIndex type_index, - uint32_t dex_pc) { +HLoadClass* HInstructionBuilder::BuildLoadClass(dex::TypeIndex type_index, + uint32_t dex_pc, + bool check_access, + bool outer) { ScopedObjectAccess soa(Thread::Current()); - StackHandleScope<1> hs(soa.Self()); - const DexFile& dex_file = *dex_compilation_unit_->GetDexFile(); - Handle<mirror::DexCache> dex_cache = dex_compilation_unit_->GetDexCache(); - Handle<mirror::Class> resolved_class(hs.NewHandle(dex_cache->GetResolvedType(type_index))); - - bool can_access = compiler_driver_->CanAccessTypeWithoutChecks( - dex_compilation_unit_->GetDexMethodIndex(), - dex_cache, - type_index); + const DexCompilationUnit* compilation_unit = + outer ? outer_compilation_unit_ : dex_compilation_unit_; + const DexFile& dex_file = *compilation_unit->GetDexFile(); + Handle<mirror::DexCache> dex_cache = compilation_unit->GetDexCache(); + bool is_accessible = false; + Handle<mirror::Class> klass = handles_->NewHandle(dex_cache->GetResolvedType(type_index)); + if (!check_access) { + is_accessible = true; + } else if (klass.Get() != nullptr) { + if (klass->IsPublic()) { + is_accessible = true; + } else { + mirror::Class* compiling_class = GetCompilingClass(); + if (compiling_class != nullptr && compiling_class->CanAccess(klass.Get())) { + is_accessible = true; + } + } + } - HInstruction* object = LoadLocal(reference, Primitive::kPrimNot); - HLoadClass* cls = new (arena_) HLoadClass( + HLoadClass* load_class = new (arena_) HLoadClass( graph_->GetCurrentMethod(), type_index, dex_file, - IsOutermostCompilingClass(type_index), + klass, + klass.Get() != nullptr && (klass.Get() == GetOutermostCompilingClass()), dex_pc, - !can_access); - AppendInstruction(cls); + !is_accessible); - TypeCheckKind check_kind = ComputeTypeCheckKind(resolved_class); + AppendInstruction(load_class); + return load_class; +} + +void HInstructionBuilder::BuildTypeCheck(const Instruction& instruction, + uint8_t destination, + uint8_t reference, + dex::TypeIndex type_index, + uint32_t dex_pc) { + HInstruction* object = LoadLocal(reference, Primitive::kPrimNot); + HLoadClass* cls = BuildLoadClass(type_index, dex_pc, /* check_access */ true); + + ScopedObjectAccess soa(Thread::Current()); + TypeCheckKind check_kind = ComputeTypeCheckKind(cls->GetClass()); if (instruction.Opcode() == Instruction::INSTANCE_OF) { AppendInstruction(new (arena_) HInstanceOf(object, cls, check_kind, dex_pc)); UpdateLocal(destination, current_block_->GetLastInstruction()); @@ -2690,21 +2686,7 @@ bool HInstructionBuilder::ProcessDexInstruction(const Instruction& instruction, case Instruction::CONST_CLASS: { dex::TypeIndex type_index(instruction.VRegB_21c()); - // `CanAccessTypeWithoutChecks` will tell whether the method being - // built is trying to access its own class, so that the generated - // code can optimize for this case. However, the optimization does not - // work for inlining, so we use `IsOutermostCompilingClass` instead. - ScopedObjectAccess soa(Thread::Current()); - Handle<mirror::DexCache> dex_cache = dex_compilation_unit_->GetDexCache(); - bool can_access = compiler_driver_->CanAccessTypeWithoutChecks( - dex_compilation_unit_->GetDexMethodIndex(), dex_cache, type_index); - AppendInstruction(new (arena_) HLoadClass( - graph_->GetCurrentMethod(), - type_index, - *dex_file_, - IsOutermostCompilingClass(type_index), - dex_pc, - !can_access)); + BuildLoadClass(type_index, dex_pc, /* check_access */ true); UpdateLocal(instruction.VRegA_21c(), current_block_->GetLastInstruction()); break; } diff --git a/compiler/optimizing/instruction_builder.h b/compiler/optimizing/instruction_builder.h index aef0b94c1f..5efe95094c 100644 --- a/compiler/optimizing/instruction_builder.h +++ b/compiler/optimizing/instruction_builder.h @@ -46,9 +46,11 @@ class HInstructionBuilder : public ValueObject { CompilerDriver* driver, const uint8_t* interpreter_metadata, OptimizingCompilerStats* compiler_stats, - Handle<mirror::DexCache> dex_cache) + Handle<mirror::DexCache> dex_cache, + VariableSizedHandleScope* handles) : arena_(graph->GetArena()), graph_(graph), + handles_(handles), dex_file_(dex_file), code_item_(code_item), return_type_(return_type), @@ -223,6 +225,14 @@ class HInstructionBuilder : public ValueObject { // Builds an instruction sequence for a switch statement. void BuildSwitch(const Instruction& instruction, uint32_t dex_pc); + // Builds a `HLoadClass` loading the given `type_index`. If `outer` is true, + // this method will use the outer class's dex file to lookup the type at + // `type_index`. + HLoadClass* BuildLoadClass(dex::TypeIndex type_index, + uint32_t dex_pc, + bool check_access, + bool outer = false); + // Returns the outer-most compiling method's class. mirror::Class* GetOutermostCompilingClass() const; @@ -282,6 +292,7 @@ class HInstructionBuilder : public ValueObject { ArenaAllocator* const arena_; HGraph* const graph_; + VariableSizedHandleScope* handles_; // The dex file where the method being compiled is, and the bytecode data. const DexFile* const dex_file_; diff --git a/compiler/optimizing/intrinsics.cc b/compiler/optimizing/intrinsics.cc index fc6ff7b197..17d683f357 100644 --- a/compiler/optimizing/intrinsics.cc +++ b/compiler/optimizing/intrinsics.cc @@ -145,7 +145,7 @@ void IntrinsicsRecognizer::Run() { if (!CheckInvokeType(intrinsic, invoke)) { LOG(WARNING) << "Found an intrinsic with unexpected invoke type: " << intrinsic << " for " - << invoke->GetDexFile().PrettyMethod(invoke->GetDexMethodIndex()) + << art_method->PrettyMethod() << invoke->DebugName(); } else { invoke->SetIntrinsic(intrinsic, diff --git a/compiler/optimizing/load_store_elimination.cc b/compiler/optimizing/load_store_elimination.cc index 4f30b11753..2d3c00fb97 100644 --- a/compiler/optimizing/load_store_elimination.cc +++ b/compiler/optimizing/load_store_elimination.cc @@ -979,7 +979,7 @@ class LSEVisitor : public HGraphVisitor { } if (ref_info->IsSingletonAndRemovable() && !new_instance->IsFinalizable() && - !new_instance->NeedsAccessCheck()) { + !new_instance->NeedsChecks()) { singleton_new_instances_.push_back(new_instance); } ArenaVector<HInstruction*>& heap_values = diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc index 0af0d19849..d15145e673 100644 --- a/compiler/optimizing/nodes.cc +++ b/compiler/optimizing/nodes.cc @@ -2391,6 +2391,14 @@ bool HInvoke::NeedsEnvironment() const { return !opt.GetDoesNotNeedEnvironment(); } +const DexFile& HInvokeStaticOrDirect::GetDexFileForPcRelativeDexCache() const { + ArtMethod* caller = GetEnvironment()->GetMethod(); + ScopedObjectAccess soa(Thread::Current()); + // `caller` is null for a top-level graph representing a method whose declaring + // class was not resolved. + return caller == nullptr ? GetBlock()->GetGraph()->GetDexFile() : *caller->GetDexFile(); +} + bool HInvokeStaticOrDirect::NeedsDexCacheOfDeclaringClass() const { if (GetMethodLoadKind() != MethodLoadKind::kDexCacheViaMethod) { return false; @@ -2434,17 +2442,6 @@ std::ostream& operator<<(std::ostream& os, HInvokeStaticOrDirect::ClinitCheckReq } } -// Helper for InstructionDataEquals to fetch the mirror Class out -// from a kJitTableAddress LoadClass kind. -// NO_THREAD_SAFETY_ANALYSIS because even though we're accessing -// mirrors, they are stored in a variable size handle scope which is always -// visited during a pause. Also, the only caller of this helper -// only uses the mirror for pointer comparison. -static inline mirror::Class* AsMirrorInternal(uint64_t address) - NO_THREAD_SAFETY_ANALYSIS { - return reinterpret_cast<StackReference<mirror::Class>*>(address)->AsMirrorPtr(); -} - bool HLoadClass::InstructionDataEquals(const HInstruction* other) const { const HLoadClass* other_load_class = other->AsLoadClass(); // TODO: To allow GVN for HLoadClass from different dex files, we should compare the type @@ -2455,9 +2452,10 @@ bool HLoadClass::InstructionDataEquals(const HInstruction* other) const { } switch (GetLoadKind()) { case LoadKind::kBootImageAddress: - return GetAddress() == other_load_class->GetAddress(); - case LoadKind::kJitTableAddress: - return AsMirrorInternal(GetAddress()) == AsMirrorInternal(other_load_class->GetAddress()); + case LoadKind::kJitTableAddress: { + ScopedObjectAccess soa(Thread::Current()); + return GetClass().Get() == other_load_class->GetClass().Get(); + } default: DCHECK(HasTypeReference(GetLoadKind())); return IsSameDexFile(GetDexFile(), other_load_class->GetDexFile()); @@ -2502,17 +2500,6 @@ std::ostream& operator<<(std::ostream& os, HLoadClass::LoadKind rhs) { } } -// Helper for InstructionDataEquals to fetch the mirror String out -// from a kJitTableAddress LoadString kind. -// NO_THREAD_SAFETY_ANALYSIS because even though we're accessing -// mirrors, they are stored in a variable size handle scope which is always -// visited during a pause. Also, the only caller of this helper -// only uses the mirror for pointer comparison. -static inline mirror::String* AsMirrorInternal(Handle<mirror::String> handle) - NO_THREAD_SAFETY_ANALYSIS { - return handle.Get(); -} - bool HLoadString::InstructionDataEquals(const HInstruction* other) const { const HLoadString* other_load_string = other->AsLoadString(); // TODO: To allow GVN for HLoadString from different dex files, we should compare the strings @@ -2523,8 +2510,10 @@ bool HLoadString::InstructionDataEquals(const HInstruction* other) const { } switch (GetLoadKind()) { case LoadKind::kBootImageAddress: - case LoadKind::kJitTableAddress: - return AsMirrorInternal(GetString()) == AsMirrorInternal(other_load_string->GetString()); + case LoadKind::kJitTableAddress: { + ScopedObjectAccess soa(Thread::Current()); + return GetString().Get() == other_load_string->GetString().Get(); + } default: return IsSameDexFile(GetDexFile(), other_load_string->GetDexFile()); } diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h index 3e7914c8c5..53b0fdde75 100644 --- a/compiler/optimizing/nodes.h +++ b/compiler/optimizing/nodes.h @@ -1724,28 +1724,22 @@ class HEnvironment : public ArenaObject<kArenaAllocEnvironment> { public: HEnvironment(ArenaAllocator* arena, size_t number_of_vregs, - const DexFile& dex_file, - uint32_t method_idx, + ArtMethod* method, uint32_t dex_pc, - InvokeType invoke_type, HInstruction* holder) : vregs_(number_of_vregs, arena->Adapter(kArenaAllocEnvironmentVRegs)), locations_(number_of_vregs, arena->Adapter(kArenaAllocEnvironmentLocations)), parent_(nullptr), - dex_file_(dex_file), - method_idx_(method_idx), + method_(method), dex_pc_(dex_pc), - invoke_type_(invoke_type), holder_(holder) { } HEnvironment(ArenaAllocator* arena, const HEnvironment& to_copy, HInstruction* holder) : HEnvironment(arena, to_copy.Size(), - to_copy.GetDexFile(), - to_copy.GetMethodIdx(), + to_copy.GetMethod(), to_copy.GetDexPc(), - to_copy.GetInvokeType(), holder) {} void SetAndCopyParentChain(ArenaAllocator* allocator, HEnvironment* parent) { @@ -1794,16 +1788,8 @@ class HEnvironment : public ArenaObject<kArenaAllocEnvironment> { return dex_pc_; } - uint32_t GetMethodIdx() const { - return method_idx_; - } - - InvokeType GetInvokeType() const { - return invoke_type_; - } - - const DexFile& GetDexFile() const { - return dex_file_; + ArtMethod* GetMethod() const { + return method_; } HInstruction* GetHolder() const { @@ -1819,10 +1805,8 @@ class HEnvironment : public ArenaObject<kArenaAllocEnvironment> { ArenaVector<HUserRecord<HEnvironment*>> vregs_; ArenaVector<Location> locations_; HEnvironment* parent_; - const DexFile& dex_file_; - const uint32_t method_idx_; + ArtMethod* method_; const uint32_t dex_pc_; - const InvokeType invoke_type_; // The instruction that holds this environment. HInstruction* const holder_; @@ -3784,14 +3768,12 @@ class HNewInstance FINAL : public HExpression<1> { uint32_t dex_pc, dex::TypeIndex type_index, const DexFile& dex_file, - bool needs_access_check, bool finalizable, QuickEntrypointEnum entrypoint) : HExpression(Primitive::kPrimNot, SideEffects::CanTriggerGC(), dex_pc), type_index_(type_index), dex_file_(dex_file), entrypoint_(entrypoint) { - SetPackedFlag<kFlagNeedsAccessCheck>(needs_access_check); SetPackedFlag<kFlagFinalizable>(finalizable); SetRawInputAt(0, cls); } @@ -3805,8 +3787,9 @@ class HNewInstance FINAL : public HExpression<1> { // Can throw errors when out-of-memory or if it's not instantiable/accessible. bool CanThrow() const OVERRIDE { return true; } - // Needs to call into runtime to make sure it's instantiable/accessible. - bool NeedsAccessCheck() const { return GetPackedFlag<kFlagNeedsAccessCheck>(); } + bool NeedsChecks() const { + return entrypoint_ == kQuickAllocObjectWithChecks; + } bool IsFinalizable() const { return GetPackedFlag<kFlagFinalizable>(); } @@ -3823,8 +3806,7 @@ class HNewInstance FINAL : public HExpression<1> { DECLARE_INSTRUCTION(NewInstance); private: - static constexpr size_t kFlagNeedsAccessCheck = kNumberOfExpressionPackedBits; - static constexpr size_t kFlagFinalizable = kFlagNeedsAccessCheck + 1; + static constexpr size_t kFlagFinalizable = kNumberOfExpressionPackedBits; static constexpr size_t kNumberOfNewInstancePackedBits = kFlagFinalizable + 1; static_assert(kNumberOfNewInstancePackedBits <= kMaxNumberOfPackedBits, "Too many packed fields."); @@ -3870,7 +3852,6 @@ class HInvoke : public HVariableInputSizeInstruction { Primitive::Type GetType() const OVERRIDE { return GetPackedField<ReturnTypeField>(); } uint32_t GetDexMethodIndex() const { return dex_method_index_; } - const DexFile& GetDexFile() const { return GetEnvironment()->GetDexFile(); } InvokeType GetInvokeType() const { return GetPackedField<InvokeTypeField>(); @@ -4190,6 +4171,8 @@ class HInvokeStaticOrDirect FINAL : public HInvoke { return dispatch_info_.method_load_data; } + const DexFile& GetDexFileForPcRelativeDexCache() const; + ClinitCheckRequirement GetClinitCheckRequirement() const { return GetPackedField<ClinitCheckRequirementField>(); } @@ -5449,10 +5432,10 @@ class HBoundsCheck FINAL : public HExpression<2> { HBoundsCheck(HInstruction* index, HInstruction* length, uint32_t dex_pc, - uint32_t string_char_at_method_index = DexFile::kDexNoIndex) - : HExpression(index->GetType(), SideEffects::CanTriggerGC(), dex_pc), - string_char_at_method_index_(string_char_at_method_index) { + bool string_char_at = false) + : HExpression(index->GetType(), SideEffects::CanTriggerGC(), dex_pc) { DCHECK_EQ(Primitive::kPrimInt, Primitive::PrimitiveKind(index->GetType())); + SetPackedFlag<kFlagIsStringCharAt>(string_char_at); SetRawInputAt(0, index); SetRawInputAt(1, length); } @@ -5466,22 +5449,14 @@ class HBoundsCheck FINAL : public HExpression<2> { bool CanThrow() const OVERRIDE { return true; } - bool IsStringCharAt() const { return GetStringCharAtMethodIndex() != DexFile::kDexNoIndex; } - uint32_t GetStringCharAtMethodIndex() const { return string_char_at_method_index_; } + bool IsStringCharAt() const { return GetPackedFlag<kFlagIsStringCharAt>(); } HInstruction* GetIndex() const { return InputAt(0); } DECLARE_INSTRUCTION(BoundsCheck); private: - // We treat a String as an array, creating the HBoundsCheck from String.charAt() - // intrinsic in the instruction simplifier. We want to include the String.charAt() - // in the stack trace if we actually throw the StringIndexOutOfBoundsException, - // so we need to create an HEnvironment which will be translated to an InlineInfo - // indicating the extra stack frame. Since we add this HEnvironment quite late, - // in the PrepareForRegisterAllocation pass, we need to remember the method index - // from the invoke as we don't want to look again at the dex bytecode. - uint32_t string_char_at_method_index_; // DexFile::kDexNoIndex if regular array. + static constexpr size_t kFlagIsStringCharAt = kNumberOfExpressionPackedBits; DISALLOW_COPY_AND_ASSIGN(HBoundsCheck); }; @@ -5567,6 +5542,7 @@ class HLoadClass FINAL : public HInstruction { HLoadClass(HCurrentMethod* current_method, dex::TypeIndex type_index, const DexFile& dex_file, + Handle<mirror::Class> klass, bool is_referrers_class, uint32_t dex_pc, bool needs_access_check) @@ -5574,7 +5550,7 @@ class HLoadClass FINAL : public HInstruction { special_input_(HUserRecord<HInstruction*>(current_method)), type_index_(type_index), dex_file_(dex_file), - address_(0u), + klass_(klass), loaded_class_rti_(ReferenceTypeInfo::CreateInvalid()) { // Referrers class should not need access check. We never inline unverified // methods so we can't possibly end up in this situation. @@ -5587,10 +5563,7 @@ class HLoadClass FINAL : public HInstruction { SetPackedFlag<kFlagGenerateClInitCheck>(false); } - void SetLoadKindWithAddress(LoadKind load_kind, uint64_t address) { - DCHECK(HasAddress(load_kind)); - DCHECK_NE(address, 0u); - address_ = address; + void SetLoadKind(LoadKind load_kind) { SetLoadKindInternal(load_kind); } @@ -5657,11 +5630,6 @@ class HLoadClass FINAL : public HInstruction { dex::TypeIndex GetTypeIndex() const { return type_index_; } const DexFile& GetDexFile() const { return dex_file_; } - uint64_t GetAddress() const { - DCHECK(HasAddress(GetLoadKind())); - return address_; - } - bool NeedsDexCacheOfDeclaringClass() const OVERRIDE { return GetLoadKind() == LoadKind::kDexCacheViaMethod; } @@ -5691,6 +5659,10 @@ class HLoadClass FINAL : public HInstruction { return Primitive::kPrimNot; } + Handle<mirror::Class> GetClass() const { + return klass_; + } + DECLARE_INSTRUCTION(LoadClass); private: @@ -5714,11 +5686,6 @@ class HLoadClass FINAL : public HInstruction { load_kind == LoadKind::kDexCacheViaMethod; } - static bool HasAddress(LoadKind load_kind) { - return load_kind == LoadKind::kBootImageAddress || - load_kind == LoadKind::kJitTableAddress; - } - void SetLoadKindInternal(LoadKind load_kind); // The special input is the HCurrentMethod for kDexCacheViaMethod or kReferrersClass. @@ -5729,7 +5696,7 @@ class HLoadClass FINAL : public HInstruction { const dex::TypeIndex type_index_; const DexFile& dex_file_; - uint64_t address_; // Up to 64-bit, needed for kJitTableAddress on 64-bit targets. + Handle<mirror::Class> klass_; ReferenceTypeInfo loaded_class_rti_; diff --git a/compiler/optimizing/nodes_test.cc b/compiler/optimizing/nodes_test.cc index 5d9a6528ca..7686ba851b 100644 --- a/compiler/optimizing/nodes_test.cc +++ b/compiler/optimizing/nodes_test.cc @@ -52,7 +52,7 @@ TEST(Node, RemoveInstruction) { exit_block->AddInstruction(new (&allocator) HExit()); HEnvironment* environment = new (&allocator) HEnvironment( - &allocator, 1, graph->GetDexFile(), graph->GetMethodIdx(), 0, kStatic, null_check); + &allocator, 1, graph->GetArtMethod(), 0, null_check); null_check->SetRawEnvironment(environment); environment->SetRawEnvAt(0, parameter); parameter->AddEnvUseAt(null_check->GetEnvironment(), 0); @@ -137,7 +137,7 @@ TEST(Node, ParentEnvironment) { ASSERT_TRUE(parameter1->GetUses().HasExactlyOneElement()); HEnvironment* environment = new (&allocator) HEnvironment( - &allocator, 1, graph->GetDexFile(), graph->GetMethodIdx(), 0, kStatic, with_environment); + &allocator, 1, graph->GetArtMethod(), 0, with_environment); ArenaVector<HInstruction*> array(allocator.Adapter()); array.push_back(parameter1); @@ -148,13 +148,13 @@ TEST(Node, ParentEnvironment) { ASSERT_TRUE(parameter1->GetEnvUses().HasExactlyOneElement()); HEnvironment* parent1 = new (&allocator) HEnvironment( - &allocator, 1, graph->GetDexFile(), graph->GetMethodIdx(), 0, kStatic, nullptr); + &allocator, 1, graph->GetArtMethod(), 0, nullptr); parent1->CopyFrom(array); ASSERT_EQ(parameter1->GetEnvUses().SizeSlow(), 2u); HEnvironment* parent2 = new (&allocator) HEnvironment( - &allocator, 1, graph->GetDexFile(), graph->GetMethodIdx(), 0, kStatic, nullptr); + &allocator, 1, graph->GetArtMethod(), 0, nullptr); parent2->CopyFrom(array); parent1->SetAndCopyParentChain(&allocator, parent2); diff --git a/compiler/optimizing/prepare_for_register_allocation.cc b/compiler/optimizing/prepare_for_register_allocation.cc index db7c1fbb06..efbaf6c221 100644 --- a/compiler/optimizing/prepare_for_register_allocation.cc +++ b/compiler/optimizing/prepare_for_register_allocation.cc @@ -16,6 +16,9 @@ #include "prepare_for_register_allocation.h" +#include "jni_internal.h" +#include "well_known_classes.h" + namespace art { void PrepareForRegisterAllocation::Run() { @@ -42,16 +45,12 @@ void PrepareForRegisterAllocation::VisitBoundsCheck(HBoundsCheck* check) { if (check->IsStringCharAt()) { // Add a fake environment for String.charAt() inline info as we want // the exception to appear as being thrown from there. - const DexFile& dex_file = check->GetEnvironment()->GetDexFile(); - DCHECK_STREQ(dex_file.PrettyMethod(check->GetStringCharAtMethodIndex()).c_str(), - "char java.lang.String.charAt(int)"); + ArtMethod* char_at_method = jni::DecodeArtMethod(WellKnownClasses::java_lang_String_charAt); ArenaAllocator* arena = GetGraph()->GetArena(); HEnvironment* environment = new (arena) HEnvironment(arena, /* number_of_vregs */ 0u, - dex_file, - check->GetStringCharAtMethodIndex(), + char_at_method, /* dex_pc */ DexFile::kDexNoIndex, - kVirtual, check); check->InsertRawEnvironment(environment); } @@ -199,8 +198,7 @@ bool PrepareForRegisterAllocation::CanMoveClinitCheck(HInstruction* input, return false; } if (user_environment->GetDexPc() != input_environment->GetDexPc() || - user_environment->GetMethodIdx() != input_environment->GetMethodIdx() || - !IsSameDexFile(user_environment->GetDexFile(), input_environment->GetDexFile())) { + user_environment->GetMethod() != input_environment->GetMethod()) { return false; } user_environment = user_environment->GetParent(); diff --git a/compiler/optimizing/reference_type_propagation.cc b/compiler/optimizing/reference_type_propagation.cc index f8a4469712..8854a2b08b 100644 --- a/compiler/optimizing/reference_type_propagation.cc +++ b/compiler/optimizing/reference_type_propagation.cc @@ -295,13 +295,13 @@ static void BoundTypeForClassCheck(HInstruction* check) { } if (check->IsIf()) { - HBasicBlock* trueBlock = check->IsEqual() + HBasicBlock* trueBlock = compare->IsEqual() ? check->AsIf()->IfTrueSuccessor() : check->AsIf()->IfFalseSuccessor(); BoundTypeIn(receiver, trueBlock, /* start_instruction */ nullptr, class_rti); } else { DCHECK(check->IsDeoptimize()); - if (check->IsEqual()) { + if (compare->IsEqual()) { BoundTypeIn(receiver, check->GetBlock(), check, class_rti); } } @@ -499,18 +499,19 @@ void ReferenceTypePropagation::RTPVisitor::SetClassAsTypeInfo(HInstruction* inst if (instr->IsInvokeStaticOrDirect() && instr->AsInvokeStaticOrDirect()->IsStringInit()) { // Calls to String.<init> are replaced with a StringFactory. if (kIsDebugBuild) { - HInvoke* invoke = instr->AsInvoke(); + HInvokeStaticOrDirect* invoke = instr->AsInvokeStaticOrDirect(); ClassLinker* cl = Runtime::Current()->GetClassLinker(); Thread* self = Thread::Current(); StackHandleScope<2> hs(self); + const DexFile& dex_file = *invoke->GetTargetMethod().dex_file; Handle<mirror::DexCache> dex_cache( - hs.NewHandle(FindDexCacheWithHint(self, invoke->GetDexFile(), hint_dex_cache_))); + hs.NewHandle(FindDexCacheWithHint(self, dex_file, hint_dex_cache_))); // Use a null loader. We should probably use the compiling method's class loader, // but then we would need to pass it to RTPVisitor just for this debug check. Since // the method is from the String class, the null loader is good enough. Handle<mirror::ClassLoader> loader; ArtMethod* method = cl->ResolveMethod<ClassLinker::kNoICCECheckForCache>( - invoke->GetDexFile(), invoke->GetDexMethodIndex(), dex_cache, loader, nullptr, kDirect); + dex_file, invoke->GetDexMethodIndex(), dex_cache, loader, nullptr, kDirect); DCHECK(method != nullptr); mirror::Class* declaring_class = method->GetDeclaringClass(); DCHECK(declaring_class != nullptr); @@ -619,14 +620,10 @@ void ReferenceTypePropagation::RTPVisitor::VisitUnresolvedStaticFieldGet( void ReferenceTypePropagation::RTPVisitor::VisitLoadClass(HLoadClass* instr) { ScopedObjectAccess soa(Thread::Current()); - // Get type from dex cache assuming it was populated by the verifier. - mirror::Class* resolved_class = GetClassFromDexCache(soa.Self(), - instr->GetDexFile(), - instr->GetTypeIndex(), - hint_dex_cache_); - if (IsAdmissible(resolved_class)) { + Handle<mirror::Class> resolved_class = instr->GetClass(); + if (IsAdmissible(resolved_class.Get())) { instr->SetLoadedClassRTI(ReferenceTypeInfo::Create( - handle_cache_->NewHandle(resolved_class), /* is_exact */ true)); + resolved_class, /* is_exact */ true)); } instr->SetReferenceTypeInfo( ReferenceTypeInfo::Create(handle_cache_->GetClassClassHandle(), /* is_exact */ true)); @@ -843,12 +840,8 @@ void ReferenceTypePropagation::RTPVisitor::VisitInvoke(HInvoke* instr) { } ScopedObjectAccess soa(Thread::Current()); - ClassLinker* cl = Runtime::Current()->GetClassLinker(); - mirror::DexCache* dex_cache = - FindDexCacheWithHint(soa.Self(), instr->GetDexFile(), hint_dex_cache_); - PointerSize pointer_size = cl->GetImagePointerSize(); - ArtMethod* method = dex_cache->GetResolvedMethod(instr->GetDexMethodIndex(), pointer_size); - mirror::Class* klass = (method == nullptr) ? nullptr : method->GetReturnType(false, pointer_size); + ArtMethod* method = instr->GetResolvedMethod(); + mirror::Class* klass = (method == nullptr) ? nullptr : method->GetReturnType(/* resolve */ false); SetClassAsTypeInfo(instr, klass, /* is_exact */ false); } diff --git a/compiler/optimizing/sharpening.cc b/compiler/optimizing/sharpening.cc index eabda2675e..c5294107ae 100644 --- a/compiler/optimizing/sharpening.cc +++ b/compiler/optimizing/sharpening.cc @@ -133,24 +133,13 @@ void HSharpening::ProcessInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) { void HSharpening::ProcessLoadClass(HLoadClass* load_class) { ScopedObjectAccess soa(Thread::Current()); - StackHandleScope<1> hs(soa.Self()); - Runtime* runtime = Runtime::Current(); - ClassLinker* class_linker = runtime->GetClassLinker(); - const DexFile& dex_file = load_class->GetDexFile(); - dex::TypeIndex type_index = load_class->GetTypeIndex(); - Handle<mirror::DexCache> dex_cache = IsSameDexFile(dex_file, *compilation_unit_.GetDexFile()) - ? compilation_unit_.GetDexCache() - : hs.NewHandle(class_linker->FindDexCache(soa.Self(), dex_file)); - mirror::Class* cls = dex_cache->GetResolvedType(type_index); - SharpenClass(load_class, cls, handles_, codegen_, compiler_driver_); + SharpenClass(load_class, codegen_, compiler_driver_); } void HSharpening::SharpenClass(HLoadClass* load_class, - mirror::Class* klass, - VariableSizedHandleScope* handles, CodeGenerator* codegen, CompilerDriver* compiler_driver) { - ScopedAssertNoThreadSuspension sants("Sharpening class in compiler"); + Handle<mirror::Class> klass = load_class->GetClass(); DCHECK(load_class->GetLoadKind() == HLoadClass::LoadKind::kDexCacheViaMethod || load_class->GetLoadKind() == HLoadClass::LoadKind::kReferrersClass) << load_class->GetLoadKind(); @@ -174,7 +163,6 @@ void HSharpening::SharpenClass(HLoadClass* load_class, bool is_in_boot_image = false; HLoadClass::LoadKind desired_load_kind = static_cast<HLoadClass::LoadKind>(-1); - uint64_t address = 0u; // Class or dex cache element address. Runtime* runtime = Runtime::Current(); if (codegen->GetCompilerOptions().IsBootImage()) { // Compiling boot image. Check if the class is a boot image class. @@ -182,7 +170,7 @@ void HSharpening::SharpenClass(HLoadClass* load_class, if (!compiler_driver->GetSupportBootImageFixup()) { // compiler_driver_test. Do not sharpen. desired_load_kind = HLoadClass::LoadKind::kDexCacheViaMethod; - } else if ((klass != nullptr) && compiler_driver->IsImageClass( + } else if ((klass.Get() != nullptr) && compiler_driver->IsImageClass( dex_file.StringDataByIdx(dex_file.GetTypeId(type_index).descriptor_idx_))) { is_in_boot_image = true; desired_load_kind = codegen->GetCompilerOptions().GetCompilePic() @@ -194,20 +182,16 @@ void HSharpening::SharpenClass(HLoadClass* load_class, desired_load_kind = HLoadClass::LoadKind::kBssEntry; } } else { - is_in_boot_image = (klass != nullptr) && runtime->GetHeap()->ObjectIsInBootImageSpace(klass); + is_in_boot_image = (klass.Get() != nullptr) && + runtime->GetHeap()->ObjectIsInBootImageSpace(klass.Get()); if (runtime->UseJitCompilation()) { // TODO: Make sure we don't set the "compile PIC" flag for JIT as that's bogus. // DCHECK(!codegen_->GetCompilerOptions().GetCompilePic()); if (is_in_boot_image) { // TODO: Use direct pointers for all non-moving spaces, not just boot image. Bug: 29530787 desired_load_kind = HLoadClass::LoadKind::kBootImageAddress; - address = reinterpret_cast64<uint64_t>(klass); - } else if (klass != nullptr) { + } else if (klass.Get() != nullptr) { desired_load_kind = HLoadClass::LoadKind::kJitTableAddress; - // We store in the address field the location of the stack reference maintained - // by the handle. We do this now so that the code generation does not need to figure - // out which class loader to use. - address = reinterpret_cast<uint64_t>(handles->NewHandle(klass).GetReference()); } else { // Class not loaded yet. This happens when the dex code requesting // this `HLoadClass` hasn't been executed in the interpreter. @@ -218,7 +202,6 @@ void HSharpening::SharpenClass(HLoadClass* load_class, } else if (is_in_boot_image && !codegen->GetCompilerOptions().GetCompilePic()) { // AOT app compilation. Check if the class is in the boot image. desired_load_kind = HLoadClass::LoadKind::kBootImageAddress; - address = reinterpret_cast64<uint64_t>(klass); } else { // Not JIT and either the klass is not in boot image or we are compiling in PIC mode. desired_load_kind = HLoadClass::LoadKind::kBssEntry; @@ -240,8 +223,7 @@ void HSharpening::SharpenClass(HLoadClass* load_class, break; case HLoadClass::LoadKind::kBootImageAddress: case HLoadClass::LoadKind::kJitTableAddress: - DCHECK_NE(address, 0u); - load_class->SetLoadKindWithAddress(load_kind, address); + load_class->SetLoadKind(load_kind); break; default: LOG(FATAL) << "Unexpected load kind: " << load_kind; diff --git a/compiler/optimizing/sharpening.h b/compiler/optimizing/sharpening.h index ae5ccb33ab..ae3d83ef2c 100644 --- a/compiler/optimizing/sharpening.h +++ b/compiler/optimizing/sharpening.h @@ -49,8 +49,6 @@ class HSharpening : public HOptimization { // Used internally but also by the inliner. static void SharpenClass(HLoadClass* load_class, - mirror::Class* klass, - VariableSizedHandleScope* handles, CodeGenerator* codegen, CompilerDriver* compiler_driver) REQUIRES_SHARED(Locks::mutator_lock_); diff --git a/compiler/optimizing/stack_map_stream.cc b/compiler/optimizing/stack_map_stream.cc index fc8af6462a..6087e36507 100644 --- a/compiler/optimizing/stack_map_stream.cc +++ b/compiler/optimizing/stack_map_stream.cc @@ -13,8 +13,13 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + #include "stack_map_stream.h" +#include "art_method.h" +#include "runtime.h" +#include "scoped_thread_state_change-inl.h" + namespace art { void StackMapStream::BeginStackMapEntry(uint32_t dex_pc, @@ -98,15 +103,27 @@ void StackMapStream::AddDexRegisterEntry(DexRegisterLocation::Kind kind, int32_t current_dex_register_++; } -void StackMapStream::BeginInlineInfoEntry(uint32_t method_index, +static bool EncodeArtMethodInInlineInfo(ArtMethod* method ATTRIBUTE_UNUSED) { + // Note: the runtime is null only for unit testing. + return Runtime::Current() == nullptr || !Runtime::Current()->IsAotCompiler(); +} + +void StackMapStream::BeginInlineInfoEntry(ArtMethod* method, uint32_t dex_pc, - InvokeType invoke_type, - uint32_t num_dex_registers) { + uint32_t num_dex_registers, + const DexFile* outer_dex_file) { DCHECK(!in_inline_frame_); in_inline_frame_ = true; - current_inline_info_.method_index = method_index; + if (EncodeArtMethodInInlineInfo(method)) { + current_inline_info_.method = method; + } else { + if (dex_pc != static_cast<uint32_t>(-1) && kIsDebugBuild) { + ScopedObjectAccess soa(Thread::Current()); + DCHECK(IsSameDexFile(*outer_dex_file, *method->GetDexFile())); + } + current_inline_info_.method_index = method->GetDexMethodIndexUnchecked(); + } current_inline_info_.dex_pc = dex_pc; - current_inline_info_.invoke_type = invoke_type; current_inline_info_.num_dex_registers = num_dex_registers; current_inline_info_.dex_register_locations_start_index = dex_register_locations_.size(); if (num_dex_registers != 0) { @@ -229,25 +246,32 @@ size_t StackMapStream::ComputeDexRegisterMapsSize() const { void StackMapStream::ComputeInlineInfoEncoding() { uint32_t method_index_max = 0; uint32_t dex_pc_max = DexFile::kDexNoIndex; - uint32_t invoke_type_max = 0; + uint32_t extra_data_max = 0; uint32_t inline_info_index = 0; for (const StackMapEntry& entry : stack_maps_) { for (size_t j = 0; j < entry.inlining_depth; ++j) { InlineInfoEntry inline_entry = inline_infos_[inline_info_index++]; - method_index_max = std::max(method_index_max, inline_entry.method_index); + if (inline_entry.method == nullptr) { + method_index_max = std::max(method_index_max, inline_entry.method_index); + extra_data_max = std::max(extra_data_max, 1u); + } else { + method_index_max = std::max( + method_index_max, High32Bits(reinterpret_cast<uintptr_t>(inline_entry.method))); + extra_data_max = std::max( + extra_data_max, Low32Bits(reinterpret_cast<uintptr_t>(inline_entry.method))); + } if (inline_entry.dex_pc != DexFile::kDexNoIndex && (dex_pc_max == DexFile::kDexNoIndex || dex_pc_max < inline_entry.dex_pc)) { dex_pc_max = inline_entry.dex_pc; } - invoke_type_max = std::max(invoke_type_max, static_cast<uint32_t>(inline_entry.invoke_type)); } } DCHECK_EQ(inline_info_index, inline_infos_.size()); inline_info_encoding_.SetFromSizes(method_index_max, dex_pc_max, - invoke_type_max, + extra_data_max, dex_register_maps_size_); } @@ -354,9 +378,20 @@ void StackMapStream::FillIn(MemoryRegion region) { DCHECK_LE(entry.inline_infos_start_index + entry.inlining_depth, inline_infos_.size()); for (size_t depth = 0; depth < entry.inlining_depth; ++depth) { InlineInfoEntry inline_entry = inline_infos_[depth + entry.inline_infos_start_index]; - inline_info.SetMethodIndexAtDepth(inline_info_encoding_, depth, inline_entry.method_index); + if (inline_entry.method != nullptr) { + inline_info.SetMethodIndexAtDepth( + inline_info_encoding_, + depth, + High32Bits(reinterpret_cast<uintptr_t>(inline_entry.method))); + inline_info.SetExtraDataAtDepth( + inline_info_encoding_, + depth, + Low32Bits(reinterpret_cast<uintptr_t>(inline_entry.method))); + } else { + inline_info.SetMethodIndexAtDepth(inline_info_encoding_, depth, inline_entry.method_index); + inline_info.SetExtraDataAtDepth(inline_info_encoding_, depth, 1); + } inline_info.SetDexPcAtDepth(inline_info_encoding_, depth, inline_entry.dex_pc); - inline_info.SetInvokeTypeAtDepth(inline_info_encoding_, depth, inline_entry.invoke_type); if (inline_entry.num_dex_registers == 0) { // No dex map available. inline_info.SetDexRegisterMapOffsetAtDepth(inline_info_encoding_, @@ -544,10 +579,13 @@ void StackMapStream::CheckCodeInfo(MemoryRegion region) const { InlineInfoEntry inline_entry = inline_infos_[inline_info_index]; DCHECK_EQ(inline_info.GetDexPcAtDepth(encoding.inline_info_encoding, d), inline_entry.dex_pc); - DCHECK_EQ(inline_info.GetMethodIndexAtDepth(encoding.inline_info_encoding, d), - inline_entry.method_index); - DCHECK_EQ(inline_info.GetInvokeTypeAtDepth(encoding.inline_info_encoding, d), - inline_entry.invoke_type); + if (inline_info.EncodesArtMethodAtDepth(encoding.inline_info_encoding, d)) { + DCHECK_EQ(inline_info.GetArtMethodAtDepth(encoding.inline_info_encoding, d), + inline_entry.method); + } else { + DCHECK_EQ(inline_info.GetMethodIndexAtDepth(encoding.inline_info_encoding, d), + inline_entry.method_index); + } CheckDexRegisterMap(code_info, code_info.GetDexRegisterMapAtDepth( diff --git a/compiler/optimizing/stack_map_stream.h b/compiler/optimizing/stack_map_stream.h index 53a9795d52..d6f42b373c 100644 --- a/compiler/optimizing/stack_map_stream.h +++ b/compiler/optimizing/stack_map_stream.h @@ -109,8 +109,8 @@ class StackMapStream : public ValueObject { struct InlineInfoEntry { uint32_t dex_pc; // DexFile::kDexNoIndex for intrinsified native methods. + ArtMethod* method; uint32_t method_index; - InvokeType invoke_type; uint32_t num_dex_registers; BitVector* live_dex_registers_mask; size_t dex_register_locations_start_index; @@ -126,10 +126,10 @@ class StackMapStream : public ValueObject { void AddDexRegisterEntry(DexRegisterLocation::Kind kind, int32_t value); - void BeginInlineInfoEntry(uint32_t method_index, + void BeginInlineInfoEntry(ArtMethod* method, uint32_t dex_pc, - InvokeType invoke_type, - uint32_t num_dex_registers); + uint32_t num_dex_registers, + const DexFile* outer_dex_file = nullptr); void EndInlineInfoEntry(); size_t GetNumberOfStackMaps() const { diff --git a/compiler/optimizing/stack_map_test.cc b/compiler/optimizing/stack_map_test.cc index 967fd96561..22810ea4f7 100644 --- a/compiler/optimizing/stack_map_test.cc +++ b/compiler/optimizing/stack_map_test.cc @@ -16,6 +16,7 @@ #include "stack_map.h" +#include "art_method.h" #include "base/arena_bit_vector.h" #include "stack_map_stream.h" @@ -128,6 +129,7 @@ TEST(StackMapTest, Test2) { ArenaPool pool; ArenaAllocator arena(&pool); StackMapStream stream(&arena); + ArtMethod art_method; ArenaBitVector sp_mask1(&arena, 0, true); sp_mask1.SetBit(2); @@ -137,9 +139,9 @@ TEST(StackMapTest, Test2) { stream.BeginStackMapEntry(0, 64, 0x3, &sp_mask1, number_of_dex_registers, 2); stream.AddDexRegisterEntry(Kind::kInStack, 0); // Short location. stream.AddDexRegisterEntry(Kind::kConstant, -2); // Large location. - stream.BeginInlineInfoEntry(82, 3, kDirect, number_of_dex_registers_in_inline_info); + stream.BeginInlineInfoEntry(&art_method, 3, number_of_dex_registers_in_inline_info); stream.EndInlineInfoEntry(); - stream.BeginInlineInfoEntry(42, 2, kStatic, number_of_dex_registers_in_inline_info); + stream.BeginInlineInfoEntry(&art_method, 2, number_of_dex_registers_in_inline_info); stream.EndInlineInfoEntry(); stream.EndStackMapEntry(); @@ -238,12 +240,10 @@ TEST(StackMapTest, Test2) { ASSERT_TRUE(stack_map.HasInlineInfo(encoding.stack_map_encoding)); InlineInfo inline_info = code_info.GetInlineInfoOf(stack_map, encoding); ASSERT_EQ(2u, inline_info.GetDepth(encoding.inline_info_encoding)); - ASSERT_EQ(82u, inline_info.GetMethodIndexAtDepth(encoding.inline_info_encoding, 0)); - ASSERT_EQ(42u, inline_info.GetMethodIndexAtDepth(encoding.inline_info_encoding, 1)); ASSERT_EQ(3u, inline_info.GetDexPcAtDepth(encoding.inline_info_encoding, 0)); ASSERT_EQ(2u, inline_info.GetDexPcAtDepth(encoding.inline_info_encoding, 1)); - ASSERT_EQ(kDirect, inline_info.GetInvokeTypeAtDepth(encoding.inline_info_encoding, 0)); - ASSERT_EQ(kStatic, inline_info.GetInvokeTypeAtDepth(encoding.inline_info_encoding, 1)); + ASSERT_TRUE(inline_info.EncodesArtMethodAtDepth(encoding.inline_info_encoding, 0)); + ASSERT_TRUE(inline_info.EncodesArtMethodAtDepth(encoding.inline_info_encoding, 1)); } // Second stack map. @@ -662,6 +662,7 @@ TEST(StackMapTest, InlineTest) { ArenaPool pool; ArenaAllocator arena(&pool); StackMapStream stream(&arena); + ArtMethod art_method; ArenaBitVector sp_mask1(&arena, 0, true); sp_mask1.SetBit(2); @@ -672,10 +673,10 @@ TEST(StackMapTest, InlineTest) { stream.AddDexRegisterEntry(Kind::kInStack, 0); stream.AddDexRegisterEntry(Kind::kConstant, 4); - stream.BeginInlineInfoEntry(42, 2, kStatic, 1); + stream.BeginInlineInfoEntry(&art_method, 2, 1); stream.AddDexRegisterEntry(Kind::kInStack, 8); stream.EndInlineInfoEntry(); - stream.BeginInlineInfoEntry(82, 3, kStatic, 3); + stream.BeginInlineInfoEntry(&art_method, 3, 3); stream.AddDexRegisterEntry(Kind::kInStack, 16); stream.AddDexRegisterEntry(Kind::kConstant, 20); stream.AddDexRegisterEntry(Kind::kInRegister, 15); @@ -688,15 +689,15 @@ TEST(StackMapTest, InlineTest) { stream.AddDexRegisterEntry(Kind::kInStack, 56); stream.AddDexRegisterEntry(Kind::kConstant, 0); - stream.BeginInlineInfoEntry(42, 2, kDirect, 1); + stream.BeginInlineInfoEntry(&art_method, 2, 1); stream.AddDexRegisterEntry(Kind::kInStack, 12); stream.EndInlineInfoEntry(); - stream.BeginInlineInfoEntry(82, 3, kStatic, 3); + stream.BeginInlineInfoEntry(&art_method, 3, 3); stream.AddDexRegisterEntry(Kind::kInStack, 80); stream.AddDexRegisterEntry(Kind::kConstant, 10); stream.AddDexRegisterEntry(Kind::kInRegister, 5); stream.EndInlineInfoEntry(); - stream.BeginInlineInfoEntry(52, 5, kVirtual, 0); + stream.BeginInlineInfoEntry(&art_method, 5, 0); stream.EndInlineInfoEntry(); stream.EndStackMapEntry(); @@ -712,12 +713,12 @@ TEST(StackMapTest, InlineTest) { stream.AddDexRegisterEntry(Kind::kInStack, 56); stream.AddDexRegisterEntry(Kind::kConstant, 0); - stream.BeginInlineInfoEntry(42, 2, kVirtual, 0); + stream.BeginInlineInfoEntry(&art_method, 2, 0); stream.EndInlineInfoEntry(); - stream.BeginInlineInfoEntry(52, 5, kInterface, 1); + stream.BeginInlineInfoEntry(&art_method, 5, 1); stream.AddDexRegisterEntry(Kind::kInRegister, 2); stream.EndInlineInfoEntry(); - stream.BeginInlineInfoEntry(52, 10, kStatic, 2); + stream.BeginInlineInfoEntry(&art_method, 10, 2); stream.AddDexRegisterEntry(Kind::kNone, 0); stream.AddDexRegisterEntry(Kind::kInRegister, 3); stream.EndInlineInfoEntry(); @@ -743,11 +744,9 @@ TEST(StackMapTest, InlineTest) { InlineInfo if0 = ci.GetInlineInfoOf(sm0, encoding); ASSERT_EQ(2u, if0.GetDepth(encoding.inline_info_encoding)); ASSERT_EQ(2u, if0.GetDexPcAtDepth(encoding.inline_info_encoding, 0)); - ASSERT_EQ(42u, if0.GetMethodIndexAtDepth(encoding.inline_info_encoding, 0)); - ASSERT_EQ(kStatic, if0.GetInvokeTypeAtDepth(encoding.inline_info_encoding, 0)); + ASSERT_TRUE(if0.EncodesArtMethodAtDepth(encoding.inline_info_encoding, 0)); ASSERT_EQ(3u, if0.GetDexPcAtDepth(encoding.inline_info_encoding, 1)); - ASSERT_EQ(82u, if0.GetMethodIndexAtDepth(encoding.inline_info_encoding, 1)); - ASSERT_EQ(kStatic, if0.GetInvokeTypeAtDepth(encoding.inline_info_encoding, 1)); + ASSERT_TRUE(if0.EncodesArtMethodAtDepth(encoding.inline_info_encoding, 1)); DexRegisterMap dex_registers1 = ci.GetDexRegisterMapAtDepth(0, if0, encoding, 1); ASSERT_EQ(8, dex_registers1.GetStackOffsetInBytes(0, 1, ci, encoding)); @@ -769,14 +768,11 @@ TEST(StackMapTest, InlineTest) { InlineInfo if1 = ci.GetInlineInfoOf(sm1, encoding); ASSERT_EQ(3u, if1.GetDepth(encoding.inline_info_encoding)); ASSERT_EQ(2u, if1.GetDexPcAtDepth(encoding.inline_info_encoding, 0)); - ASSERT_EQ(42u, if1.GetMethodIndexAtDepth(encoding.inline_info_encoding, 0)); - ASSERT_EQ(kDirect, if1.GetInvokeTypeAtDepth(encoding.inline_info_encoding, 0)); + ASSERT_TRUE(if1.EncodesArtMethodAtDepth(encoding.inline_info_encoding, 0)); ASSERT_EQ(3u, if1.GetDexPcAtDepth(encoding.inline_info_encoding, 1)); - ASSERT_EQ(82u, if1.GetMethodIndexAtDepth(encoding.inline_info_encoding, 1)); - ASSERT_EQ(kStatic, if1.GetInvokeTypeAtDepth(encoding.inline_info_encoding, 1)); + ASSERT_TRUE(if1.EncodesArtMethodAtDepth(encoding.inline_info_encoding, 1)); ASSERT_EQ(5u, if1.GetDexPcAtDepth(encoding.inline_info_encoding, 2)); - ASSERT_EQ(52u, if1.GetMethodIndexAtDepth(encoding.inline_info_encoding, 2)); - ASSERT_EQ(kVirtual, if1.GetInvokeTypeAtDepth(encoding.inline_info_encoding, 2)); + ASSERT_TRUE(if1.EncodesArtMethodAtDepth(encoding.inline_info_encoding, 2)); DexRegisterMap dex_registers1 = ci.GetDexRegisterMapAtDepth(0, if1, encoding, 1); ASSERT_EQ(12, dex_registers1.GetStackOffsetInBytes(0, 1, ci, encoding)); @@ -810,14 +806,11 @@ TEST(StackMapTest, InlineTest) { InlineInfo if2 = ci.GetInlineInfoOf(sm3, encoding); ASSERT_EQ(3u, if2.GetDepth(encoding.inline_info_encoding)); ASSERT_EQ(2u, if2.GetDexPcAtDepth(encoding.inline_info_encoding, 0)); - ASSERT_EQ(42u, if2.GetMethodIndexAtDepth(encoding.inline_info_encoding, 0)); - ASSERT_EQ(kVirtual, if2.GetInvokeTypeAtDepth(encoding.inline_info_encoding, 0)); + ASSERT_TRUE(if2.EncodesArtMethodAtDepth(encoding.inline_info_encoding, 0)); ASSERT_EQ(5u, if2.GetDexPcAtDepth(encoding.inline_info_encoding, 1)); - ASSERT_EQ(52u, if2.GetMethodIndexAtDepth(encoding.inline_info_encoding, 1)); - ASSERT_EQ(kInterface, if2.GetInvokeTypeAtDepth(encoding.inline_info_encoding, 1)); + ASSERT_TRUE(if2.EncodesArtMethodAtDepth(encoding.inline_info_encoding, 1)); ASSERT_EQ(10u, if2.GetDexPcAtDepth(encoding.inline_info_encoding, 2)); - ASSERT_EQ(52u, if2.GetMethodIndexAtDepth(encoding.inline_info_encoding, 2)); - ASSERT_EQ(kStatic, if2.GetInvokeTypeAtDepth(encoding.inline_info_encoding, 2)); + ASSERT_TRUE(if2.EncodesArtMethodAtDepth(encoding.inline_info_encoding, 2)); ASSERT_FALSE(if2.HasDexRegisterMapAtDepth(encoding.inline_info_encoding, 0)); diff --git a/compiler/utils/assembler_thumb_test_expected.cc.inc b/compiler/utils/assembler_thumb_test_expected.cc.inc index ab4f9e944c..a3fce02970 100644 --- a/compiler/utils/assembler_thumb_test_expected.cc.inc +++ b/compiler/utils/assembler_thumb_test_expected.cc.inc @@ -5610,7 +5610,7 @@ const char* const VixlJniHelpersResults[] = { " 214: ecbd 8a10 vpop {s16-s31}\n", " 218: e8bd 8de0 ldmia.w sp!, {r5, r6, r7, r8, sl, fp, pc}\n", " 21c: 4660 mov r0, ip\n", - " 21e: f8d9 c2b0 ldr.w ip, [r9, #688] ; 0x2b0\n", + " 21e: f8d9 c2ac ldr.w ip, [r9, #684] ; 0x2ac\n", " 222: 47e0 blx ip\n", nullptr }; diff --git a/compiler/utils/x86/assembler_x86.cc b/compiler/utils/x86/assembler_x86.cc index cd30872986..d3b15ac8cf 100644 --- a/compiler/utils/x86/assembler_x86.cc +++ b/compiler/utils/x86/assembler_x86.cc @@ -350,6 +350,38 @@ void X86Assembler::movaps(XmmRegister dst, XmmRegister src) { } +void X86Assembler::movaps(XmmRegister dst, const Address& src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x0F); + EmitUint8(0x28); + EmitOperand(dst, src); +} + + +void X86Assembler::movups(XmmRegister dst, const Address& src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x0F); + EmitUint8(0x10); + EmitOperand(dst, src); +} + + +void X86Assembler::movaps(const Address& dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x0F); + EmitUint8(0x29); + EmitOperand(src, dst); +} + + +void X86Assembler::movups(const Address& dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x0F); + EmitUint8(0x11); + EmitOperand(src, dst); +} + + void X86Assembler::movss(XmmRegister dst, const Address& src) { AssemblerBuffer::EnsureCapacity ensured(&buffer_); EmitUint8(0xF3); @@ -467,6 +499,83 @@ void X86Assembler::divss(XmmRegister dst, const Address& src) { } +void X86Assembler::addps(XmmRegister dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x0F); + EmitUint8(0x58); + EmitXmmRegisterOperand(dst, src); +} + + +void X86Assembler::subps(XmmRegister dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x0F); + EmitUint8(0x5C); + EmitXmmRegisterOperand(dst, src); +} + + +void X86Assembler::mulps(XmmRegister dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x0F); + EmitUint8(0x59); + EmitXmmRegisterOperand(dst, src); +} + + +void X86Assembler::divps(XmmRegister dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x0F); + EmitUint8(0x5E); + EmitXmmRegisterOperand(dst, src); +} + + +void X86Assembler::movapd(XmmRegister dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); + EmitUint8(0x0F); + EmitUint8(0x28); + EmitXmmRegisterOperand(dst, src); +} + + +void X86Assembler::movapd(XmmRegister dst, const Address& src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); + EmitUint8(0x0F); + EmitUint8(0x28); + EmitOperand(dst, src); +} + + +void X86Assembler::movupd(XmmRegister dst, const Address& src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); + EmitUint8(0x0F); + EmitUint8(0x10); + EmitOperand(dst, src); +} + + +void X86Assembler::movapd(const Address& dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); + EmitUint8(0x0F); + EmitUint8(0x29); + EmitOperand(src, dst); +} + + +void X86Assembler::movupd(const Address& dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); + EmitUint8(0x0F); + EmitUint8(0x11); + EmitOperand(src, dst); +} + + void X86Assembler::flds(const Address& src) { AssemblerBuffer::EnsureCapacity ensured(&buffer_); EmitUint8(0xD9); @@ -638,6 +747,42 @@ void X86Assembler::divsd(XmmRegister dst, const Address& src) { } +void X86Assembler::addpd(XmmRegister dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); + EmitUint8(0x0F); + EmitUint8(0x58); + EmitXmmRegisterOperand(dst, src); +} + + +void X86Assembler::subpd(XmmRegister dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); + EmitUint8(0x0F); + EmitUint8(0x5C); + EmitXmmRegisterOperand(dst, src); +} + + +void X86Assembler::mulpd(XmmRegister dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); + EmitUint8(0x0F); + EmitUint8(0x59); + EmitXmmRegisterOperand(dst, src); +} + + +void X86Assembler::divpd(XmmRegister dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); + EmitUint8(0x0F); + EmitUint8(0x5E); + EmitXmmRegisterOperand(dst, src); +} + + void X86Assembler::cvtsi2ss(XmmRegister dst, Register src) { AssemblerBuffer::EnsureCapacity ensured(&buffer_); EmitUint8(0xF3); diff --git a/compiler/utils/x86/assembler_x86.h b/compiler/utils/x86/assembler_x86.h index 114986b3e7..a93616c3e5 100644 --- a/compiler/utils/x86/assembler_x86.h +++ b/compiler/utils/x86/assembler_x86.h @@ -371,7 +371,12 @@ class X86Assembler FINAL : public Assembler { void setb(Condition condition, Register dst); - void movaps(XmmRegister dst, XmmRegister src); + void movaps(XmmRegister dst, XmmRegister src); // move + void movaps(XmmRegister dst, const Address& src); // load aligned + void movups(XmmRegister dst, const Address& src); // load unaligned + void movaps(const Address& dst, XmmRegister src); // store aligned + void movups(const Address& dst, XmmRegister src); // store unaligned + void movss(XmmRegister dst, const Address& src); void movss(const Address& dst, XmmRegister src); void movss(XmmRegister dst, XmmRegister src); @@ -388,6 +393,17 @@ class X86Assembler FINAL : public Assembler { void divss(XmmRegister dst, XmmRegister src); void divss(XmmRegister dst, const Address& src); + void addps(XmmRegister dst, XmmRegister src); // no addr variant (for now) + void subps(XmmRegister dst, XmmRegister src); + void mulps(XmmRegister dst, XmmRegister src); + void divps(XmmRegister dst, XmmRegister src); + + void movapd(XmmRegister dst, XmmRegister src); // move + void movapd(XmmRegister dst, const Address& src); // load aligned + void movupd(XmmRegister dst, const Address& src); // load unaligned + void movapd(const Address& dst, XmmRegister src); // store aligned + void movupd(const Address& dst, XmmRegister src); // store unaligned + void movsd(XmmRegister dst, const Address& src); void movsd(const Address& dst, XmmRegister src); void movsd(XmmRegister dst, XmmRegister src); @@ -409,6 +425,11 @@ class X86Assembler FINAL : public Assembler { void divsd(XmmRegister dst, XmmRegister src); void divsd(XmmRegister dst, const Address& src); + void addpd(XmmRegister dst, XmmRegister src); // no addr variant (for now) + void subpd(XmmRegister dst, XmmRegister src); + void mulpd(XmmRegister dst, XmmRegister src); + void divpd(XmmRegister dst, XmmRegister src); + void cvtsi2ss(XmmRegister dst, Register src); void cvtsi2sd(XmmRegister dst, Register src); diff --git a/compiler/utils/x86/assembler_x86_test.cc b/compiler/utils/x86/assembler_x86_test.cc index 9bae6c20bd..4d60a12cb9 100644 --- a/compiler/utils/x86/assembler_x86_test.cc +++ b/compiler/utils/x86/assembler_x86_test.cc @@ -423,6 +423,98 @@ TEST_F(AssemblerX86Test, TestlAddressImmediate) { DriverStr(expected, "TestlAddressImmediate"); } +TEST_F(AssemblerX86Test, Movaps) { + DriverStr(RepeatFF(&x86::X86Assembler::movaps, "movaps %{reg2}, %{reg1}"), "movaps"); +} + +TEST_F(AssemblerX86Test, MovapsAddr) { + GetAssembler()->movaps(x86::XmmRegister(x86::XMM0), x86::Address(x86::Register(x86::ESP), 4)); + GetAssembler()->movaps(x86::Address(x86::Register(x86::ESP), 2), x86::XmmRegister(x86::XMM1)); + const char* expected = + "movaps 0x4(%ESP), %xmm0\n" + "movaps %xmm1, 0x2(%ESP)\n"; + DriverStr(expected, "movaps_address"); +} + +TEST_F(AssemblerX86Test, MovupsAddr) { + GetAssembler()->movups(x86::XmmRegister(x86::XMM0), x86::Address(x86::Register(x86::ESP), 4)); + GetAssembler()->movups(x86::Address(x86::Register(x86::ESP), 2), x86::XmmRegister(x86::XMM1)); + const char* expected = + "movups 0x4(%ESP), %xmm0\n" + "movups %xmm1, 0x2(%ESP)\n"; + DriverStr(expected, "movups_address"); +} + +TEST_F(AssemblerX86Test, Movapd) { + DriverStr(RepeatFF(&x86::X86Assembler::movapd, "movapd %{reg2}, %{reg1}"), "movapd"); +} + +TEST_F(AssemblerX86Test, MovapdAddr) { + GetAssembler()->movapd(x86::XmmRegister(x86::XMM0), x86::Address(x86::Register(x86::ESP), 4)); + GetAssembler()->movapd(x86::Address(x86::Register(x86::ESP), 2), x86::XmmRegister(x86::XMM1)); + const char* expected = + "movapd 0x4(%ESP), %xmm0\n" + "movapd %xmm1, 0x2(%ESP)\n"; + DriverStr(expected, "movapd_address"); +} + +TEST_F(AssemblerX86Test, MovupdAddr) { + GetAssembler()->movupd(x86::XmmRegister(x86::XMM0), x86::Address(x86::Register(x86::ESP), 4)); + GetAssembler()->movupd(x86::Address(x86::Register(x86::ESP), 2), x86::XmmRegister(x86::XMM1)); + const char* expected = + "movupd 0x4(%ESP), %xmm0\n" + "movupd %xmm1, 0x2(%ESP)\n"; + DriverStr(expected, "movupd_address"); +} + +TEST_F(AssemblerX86Test, AddPS) { + GetAssembler()->addps(x86::XmmRegister(x86::XMM0), x86::XmmRegister(x86::XMM1)); + const char* expected = "addps %xmm1, %xmm0\n"; + DriverStr(expected, "addps"); +} + +TEST_F(AssemblerX86Test, AddPD) { + GetAssembler()->addpd(x86::XmmRegister(x86::XMM0), x86::XmmRegister(x86::XMM1)); + const char* expected = "addpd %xmm1, %xmm0\n"; + DriverStr(expected, "addpd"); +} + +TEST_F(AssemblerX86Test, SubPS) { + GetAssembler()->subps(x86::XmmRegister(x86::XMM0), x86::XmmRegister(x86::XMM1)); + const char* expected = "subps %xmm1, %xmm0\n"; + DriverStr(expected, "subps"); +} + +TEST_F(AssemblerX86Test, SubPD) { + GetAssembler()->subpd(x86::XmmRegister(x86::XMM0), x86::XmmRegister(x86::XMM1)); + const char* expected = "subpd %xmm1, %xmm0\n"; + DriverStr(expected, "subpd"); +} + +TEST_F(AssemblerX86Test, MulPS) { + GetAssembler()->mulps(x86::XmmRegister(x86::XMM0), x86::XmmRegister(x86::XMM1)); + const char* expected = "mulps %xmm1, %xmm0\n"; + DriverStr(expected, "mulps"); +} + +TEST_F(AssemblerX86Test, MulPD) { + GetAssembler()->mulpd(x86::XmmRegister(x86::XMM0), x86::XmmRegister(x86::XMM1)); + const char* expected = "mulpd %xmm1, %xmm0\n"; + DriverStr(expected, "mulpd"); +} + +TEST_F(AssemblerX86Test, DivPS) { + GetAssembler()->divps(x86::XmmRegister(x86::XMM0), x86::XmmRegister(x86::XMM1)); + const char* expected = "divps %xmm1, %xmm0\n"; + DriverStr(expected, "divps"); +} + +TEST_F(AssemblerX86Test, DivPD) { + GetAssembler()->divpd(x86::XmmRegister(x86::XMM0), x86::XmmRegister(x86::XMM1)); + const char* expected = "divpd %xmm1, %xmm0\n"; + DriverStr(expected, "divpd"); +} + ///////////////// // Near labels // ///////////////// diff --git a/compiler/utils/x86_64/assembler_x86_64.cc b/compiler/utils/x86_64/assembler_x86_64.cc index e9a0607290..2366b68f11 100644 --- a/compiler/utils/x86_64/assembler_x86_64.cc +++ b/compiler/utils/x86_64/assembler_x86_64.cc @@ -386,6 +386,42 @@ void X86_64Assembler::movaps(XmmRegister dst, XmmRegister src) { } +void X86_64Assembler::movaps(XmmRegister dst, const Address& src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitOptionalRex32(dst, src); + EmitUint8(0x0F); + EmitUint8(0x28); + EmitOperand(dst.LowBits(), src); +} + + +void X86_64Assembler::movups(XmmRegister dst, const Address& src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitOptionalRex32(dst, src); + EmitUint8(0x0F); + EmitUint8(0x10); + EmitOperand(dst.LowBits(), src); +} + + +void X86_64Assembler::movaps(const Address& dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitOptionalRex32(src, dst); + EmitUint8(0x0F); + EmitUint8(0x29); + EmitOperand(src.LowBits(), dst); +} + + +void X86_64Assembler::movups(const Address& dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitOptionalRex32(src, dst); + EmitUint8(0x0F); + EmitUint8(0x11); + EmitOperand(src.LowBits(), dst); +} + + void X86_64Assembler::movss(XmmRegister dst, const Address& src) { AssemblerBuffer::EnsureCapacity ensured(&buffer_); EmitUint8(0xF3); @@ -539,6 +575,42 @@ void X86_64Assembler::divss(XmmRegister dst, const Address& src) { } +void X86_64Assembler::addps(XmmRegister dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitOptionalRex32(dst, src); + EmitUint8(0x0F); + EmitUint8(0x58); + EmitXmmRegisterOperand(dst.LowBits(), src); +} + + +void X86_64Assembler::subps(XmmRegister dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitOptionalRex32(dst, src); + EmitUint8(0x0F); + EmitUint8(0x5C); + EmitXmmRegisterOperand(dst.LowBits(), src); +} + + +void X86_64Assembler::mulps(XmmRegister dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitOptionalRex32(dst, src); + EmitUint8(0x0F); + EmitUint8(0x59); + EmitXmmRegisterOperand(dst.LowBits(), src); +} + + +void X86_64Assembler::divps(XmmRegister dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitOptionalRex32(dst, src); + EmitUint8(0x0F); + EmitUint8(0x5E); + EmitXmmRegisterOperand(dst.LowBits(), src); +} + + void X86_64Assembler::flds(const Address& src) { AssemblerBuffer::EnsureCapacity ensured(&buffer_); EmitUint8(0xD9); @@ -560,6 +632,56 @@ void X86_64Assembler::fstps(const Address& dst) { } +void X86_64Assembler::movapd(XmmRegister dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); + EmitOptionalRex32(dst, src); + EmitUint8(0x0F); + EmitUint8(0x28); + EmitXmmRegisterOperand(dst.LowBits(), src); +} + + +void X86_64Assembler::movapd(XmmRegister dst, const Address& src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); + EmitOptionalRex32(dst, src); + EmitUint8(0x0F); + EmitUint8(0x28); + EmitOperand(dst.LowBits(), src); +} + + +void X86_64Assembler::movupd(XmmRegister dst, const Address& src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); + EmitOptionalRex32(dst, src); + EmitUint8(0x0F); + EmitUint8(0x10); + EmitOperand(dst.LowBits(), src); +} + + +void X86_64Assembler::movapd(const Address& dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); + EmitOptionalRex32(src, dst); + EmitUint8(0x0F); + EmitUint8(0x29); + EmitOperand(src.LowBits(), dst); +} + + +void X86_64Assembler::movupd(const Address& dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); + EmitOptionalRex32(src, dst); + EmitUint8(0x0F); + EmitUint8(0x11); + EmitOperand(src.LowBits(), dst); +} + + void X86_64Assembler::movsd(XmmRegister dst, const Address& src) { AssemblerBuffer::EnsureCapacity ensured(&buffer_); EmitUint8(0xF2); @@ -670,6 +792,46 @@ void X86_64Assembler::divsd(XmmRegister dst, const Address& src) { } +void X86_64Assembler::addpd(XmmRegister dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); + EmitOptionalRex32(dst, src); + EmitUint8(0x0F); + EmitUint8(0x58); + EmitXmmRegisterOperand(dst.LowBits(), src); +} + + +void X86_64Assembler::subpd(XmmRegister dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); + EmitOptionalRex32(dst, src); + EmitUint8(0x0F); + EmitUint8(0x5C); + EmitXmmRegisterOperand(dst.LowBits(), src); +} + + +void X86_64Assembler::mulpd(XmmRegister dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); + EmitOptionalRex32(dst, src); + EmitUint8(0x0F); + EmitUint8(0x59); + EmitXmmRegisterOperand(dst.LowBits(), src); +} + + +void X86_64Assembler::divpd(XmmRegister dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); + EmitOptionalRex32(dst, src); + EmitUint8(0x0F); + EmitUint8(0x5E); + EmitXmmRegisterOperand(dst.LowBits(), src); +} + + void X86_64Assembler::cvtsi2ss(XmmRegister dst, CpuRegister src) { cvtsi2ss(dst, src, false); } diff --git a/compiler/utils/x86_64/assembler_x86_64.h b/compiler/utils/x86_64/assembler_x86_64.h index acad86d161..5923a41fe3 100644 --- a/compiler/utils/x86_64/assembler_x86_64.h +++ b/compiler/utils/x86_64/assembler_x86_64.h @@ -390,7 +390,11 @@ class X86_64Assembler FINAL : public Assembler { void leaq(CpuRegister dst, const Address& src); void leal(CpuRegister dst, const Address& src); - void movaps(XmmRegister dst, XmmRegister src); + void movaps(XmmRegister dst, XmmRegister src); // move + void movaps(XmmRegister dst, const Address& src); // load aligned + void movups(XmmRegister dst, const Address& src); // load unaligned + void movaps(const Address& dst, XmmRegister src); // store aligned + void movups(const Address& dst, XmmRegister src); // store unaligned void movss(XmmRegister dst, const Address& src); void movss(const Address& dst, XmmRegister src); @@ -413,6 +417,17 @@ class X86_64Assembler FINAL : public Assembler { void divss(XmmRegister dst, XmmRegister src); void divss(XmmRegister dst, const Address& src); + void addps(XmmRegister dst, XmmRegister src); // no addr variant (for now) + void subps(XmmRegister dst, XmmRegister src); + void mulps(XmmRegister dst, XmmRegister src); + void divps(XmmRegister dst, XmmRegister src); + + void movapd(XmmRegister dst, XmmRegister src); // move + void movapd(XmmRegister dst, const Address& src); // load aligned + void movupd(XmmRegister dst, const Address& src); // load unaligned + void movapd(const Address& dst, XmmRegister src); // store aligned + void movupd(const Address& dst, XmmRegister src); // store unaligned + void movsd(XmmRegister dst, const Address& src); void movsd(const Address& dst, XmmRegister src); void movsd(XmmRegister dst, XmmRegister src); @@ -426,6 +441,11 @@ class X86_64Assembler FINAL : public Assembler { void divsd(XmmRegister dst, XmmRegister src); void divsd(XmmRegister dst, const Address& src); + void addpd(XmmRegister dst, XmmRegister src); // no addr variant (for now) + void subpd(XmmRegister dst, XmmRegister src); + void mulpd(XmmRegister dst, XmmRegister src); + void divpd(XmmRegister dst, XmmRegister src); + void cvtsi2ss(XmmRegister dst, CpuRegister src); // Note: this is the r/m32 version. void cvtsi2ss(XmmRegister dst, CpuRegister src, bool is64bit); void cvtsi2ss(XmmRegister dst, const Address& src, bool is64bit); diff --git a/compiler/utils/x86_64/assembler_x86_64_test.cc b/compiler/utils/x86_64/assembler_x86_64_test.cc index ff01429058..2812c34406 100644 --- a/compiler/utils/x86_64/assembler_x86_64_test.cc +++ b/compiler/utils/x86_64/assembler_x86_64_test.cc @@ -986,10 +986,50 @@ TEST_F(AssemblerX86_64Test, Movaps) { DriverStr(RepeatFF(&x86_64::X86_64Assembler::movaps, "movaps %{reg2}, %{reg1}"), "movaps"); } +TEST_F(AssemblerX86_64Test, MovapsAddr) { + GetAssembler()->movaps(x86_64::XmmRegister(x86_64::XMM0), x86_64::Address(x86_64::CpuRegister(x86_64::RSP), 4)); + GetAssembler()->movaps(x86_64::Address(x86_64::CpuRegister(x86_64::RSP), 2), x86_64::XmmRegister(x86_64::XMM1)); + const char* expected = + "movaps 0x4(%RSP), %xmm0\n" + "movaps %xmm1, 0x2(%RSP)\n"; + DriverStr(expected, "movaps_address"); +} + +TEST_F(AssemblerX86_64Test, MovupsAddr) { + GetAssembler()->movups(x86_64::XmmRegister(x86_64::XMM0), x86_64::Address(x86_64::CpuRegister(x86_64::RSP), 4)); + GetAssembler()->movups(x86_64::Address(x86_64::CpuRegister(x86_64::RSP), 2), x86_64::XmmRegister(x86_64::XMM1)); + const char* expected = + "movups 0x4(%RSP), %xmm0\n" + "movups %xmm1, 0x2(%RSP)\n"; + DriverStr(expected, "movups_address"); +} + TEST_F(AssemblerX86_64Test, Movss) { DriverStr(RepeatFF(&x86_64::X86_64Assembler::movss, "movss %{reg2}, %{reg1}"), "movss"); } +TEST_F(AssemblerX86_64Test, Movapd) { + DriverStr(RepeatFF(&x86_64::X86_64Assembler::movapd, "movapd %{reg2}, %{reg1}"), "movapd"); +} + +TEST_F(AssemblerX86_64Test, MovapdAddr) { + GetAssembler()->movapd(x86_64::XmmRegister(x86_64::XMM0), x86_64::Address(x86_64::CpuRegister(x86_64::RSP), 4)); + GetAssembler()->movapd(x86_64::Address(x86_64::CpuRegister(x86_64::RSP), 2), x86_64::XmmRegister(x86_64::XMM1)); + const char* expected = + "movapd 0x4(%RSP), %xmm0\n" + "movapd %xmm1, 0x2(%RSP)\n"; + DriverStr(expected, "movapd_address"); +} + +TEST_F(AssemblerX86_64Test, MovupdAddr) { + GetAssembler()->movupd(x86_64::XmmRegister(x86_64::XMM0), x86_64::Address(x86_64::CpuRegister(x86_64::RSP), 4)); + GetAssembler()->movupd(x86_64::Address(x86_64::CpuRegister(x86_64::RSP), 2), x86_64::XmmRegister(x86_64::XMM1)); + const char* expected = + "movupd 0x4(%RSP), %xmm0\n" + "movupd %xmm1, 0x2(%RSP)\n"; + DriverStr(expected, "movupd_address"); +} + TEST_F(AssemblerX86_64Test, Movsd) { DriverStr(RepeatFF(&x86_64::X86_64Assembler::movsd, "movsd %{reg2}, %{reg1}"), "movsd"); } @@ -1010,6 +1050,14 @@ TEST_F(AssemblerX86_64Test, Addsd) { DriverStr(RepeatFF(&x86_64::X86_64Assembler::addsd, "addsd %{reg2}, %{reg1}"), "addsd"); } +TEST_F(AssemblerX86_64Test, Addps) { + DriverStr(RepeatFF(&x86_64::X86_64Assembler::addps, "addps %{reg2}, %{reg1}"), "addps"); +} + +TEST_F(AssemblerX86_64Test, Addpd) { + DriverStr(RepeatFF(&x86_64::X86_64Assembler::addpd, "addpd %{reg2}, %{reg1}"), "addpd"); +} + TEST_F(AssemblerX86_64Test, Subss) { DriverStr(RepeatFF(&x86_64::X86_64Assembler::subss, "subss %{reg2}, %{reg1}"), "subss"); } @@ -1018,6 +1066,14 @@ TEST_F(AssemblerX86_64Test, Subsd) { DriverStr(RepeatFF(&x86_64::X86_64Assembler::subsd, "subsd %{reg2}, %{reg1}"), "subsd"); } +TEST_F(AssemblerX86_64Test, Subps) { + DriverStr(RepeatFF(&x86_64::X86_64Assembler::subps, "subps %{reg2}, %{reg1}"), "subps"); +} + +TEST_F(AssemblerX86_64Test, Subpd) { + DriverStr(RepeatFF(&x86_64::X86_64Assembler::subpd, "subpd %{reg2}, %{reg1}"), "subpd"); +} + TEST_F(AssemblerX86_64Test, Mulss) { DriverStr(RepeatFF(&x86_64::X86_64Assembler::mulss, "mulss %{reg2}, %{reg1}"), "mulss"); } @@ -1026,6 +1082,14 @@ TEST_F(AssemblerX86_64Test, Mulsd) { DriverStr(RepeatFF(&x86_64::X86_64Assembler::mulsd, "mulsd %{reg2}, %{reg1}"), "mulsd"); } +TEST_F(AssemblerX86_64Test, Mulps) { + DriverStr(RepeatFF(&x86_64::X86_64Assembler::mulps, "mulps %{reg2}, %{reg1}"), "mulps"); +} + +TEST_F(AssemblerX86_64Test, Mulpd) { + DriverStr(RepeatFF(&x86_64::X86_64Assembler::mulpd, "mulpd %{reg2}, %{reg1}"), "mulpd"); +} + TEST_F(AssemblerX86_64Test, Divss) { DriverStr(RepeatFF(&x86_64::X86_64Assembler::divss, "divss %{reg2}, %{reg1}"), "divss"); } @@ -1034,6 +1098,14 @@ TEST_F(AssemblerX86_64Test, Divsd) { DriverStr(RepeatFF(&x86_64::X86_64Assembler::divsd, "divsd %{reg2}, %{reg1}"), "divsd"); } +TEST_F(AssemblerX86_64Test, Divps) { + DriverStr(RepeatFF(&x86_64::X86_64Assembler::divps, "divps %{reg2}, %{reg1}"), "divps"); +} + +TEST_F(AssemblerX86_64Test, Divpd) { + DriverStr(RepeatFF(&x86_64::X86_64Assembler::divpd, "divpd %{reg2}, %{reg1}"), "divpd"); +} + TEST_F(AssemblerX86_64Test, Cvtsi2ss) { DriverStr(RepeatFr(&x86_64::X86_64Assembler::cvtsi2ss, "cvtsi2ss %{reg2}, %{reg1}"), "cvtsi2ss"); } diff --git a/runtime/arch/arm/quick_entrypoints_arm.S b/runtime/arch/arm/quick_entrypoints_arm.S index 102c313a6a..db1cad670d 100644 --- a/runtime/arch/arm/quick_entrypoints_arm.S +++ b/runtime/arch/arm/quick_entrypoints_arm.S @@ -856,27 +856,6 @@ END art_quick_check_instance_of #endif // USE_READ_BARRIER .endm - /* - * Entry from managed code for array put operations of objects where the value being stored - * needs to be checked for compatibility. - * r0 = array, r1 = index, r2 = value - */ -ENTRY art_quick_aput_obj_with_null_and_bound_check - tst r0, r0 - bne art_quick_aput_obj_with_bound_check - b art_quick_throw_null_pointer_exception -END art_quick_aput_obj_with_null_and_bound_check - - .hidden art_quick_aput_obj_with_bound_check -ENTRY art_quick_aput_obj_with_bound_check - ldr r3, [r0, #MIRROR_ARRAY_LENGTH_OFFSET] - cmp r3, r1 - bhi art_quick_aput_obj - mov r0, r1 - mov r1, r3 - b art_quick_throw_array_bounds -END art_quick_aput_obj_with_bound_check - #ifdef USE_READ_BARRIER .extern artReadBarrierSlow #endif diff --git a/runtime/arch/arm64/quick_entrypoints_arm64.S b/runtime/arch/arm64/quick_entrypoints_arm64.S index 3b3783cad4..ed24a0723f 100644 --- a/runtime/arch/arm64/quick_entrypoints_arm64.S +++ b/runtime/arch/arm64/quick_entrypoints_arm64.S @@ -1404,33 +1404,6 @@ END art_quick_check_instance_of #endif // USE_READ_BARRIER .endm - /* - * Entry from managed code for array put operations of objects where the value being stored - * needs to be checked for compatibility. - * x0 = array, x1 = index, x2 = value - * - * Currently all values should fit into w0/w1/w2, and w1 always will as indices are 32b. We - * assume, though, that the upper 32b are zeroed out. At least for x1/w1 we can do better by - * using index-zero-extension in load/stores. - * - * Temporaries: x3, x4 - * TODO: x4 OK? ip seems wrong here. - */ -ENTRY art_quick_aput_obj_with_null_and_bound_check - tst x0, x0 - bne art_quick_aput_obj_with_bound_check - b art_quick_throw_null_pointer_exception -END art_quick_aput_obj_with_null_and_bound_check - -ENTRY art_quick_aput_obj_with_bound_check - ldr w3, [x0, #MIRROR_ARRAY_LENGTH_OFFSET] - cmp w3, w1 - bhi art_quick_aput_obj - mov x0, x1 - mov x1, x3 - b art_quick_throw_array_bounds -END art_quick_aput_obj_with_bound_check - #ifdef USE_READ_BARRIER .extern artReadBarrierSlow #endif @@ -1675,8 +1648,6 @@ GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_WITH_ACCESS_CHECK(_region_tlab, RegionTL // GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY(_region_tlab, RegionTLAB) implemented in asm // GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(_region_tlab, RegionTLAB) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_WITH_ACCESS_CHECK(_region_tlab, RegionTLAB) -GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY(_region_tlab, RegionTLAB) -GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY_WITH_ACCESS_CHECK(_region_tlab, RegionTLAB) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_BYTES(_region_tlab, RegionTLAB) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_CHARS(_region_tlab, RegionTLAB) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_region_tlab, RegionTLAB) diff --git a/runtime/arch/mips/entrypoints_init_mips.cc b/runtime/arch/mips/entrypoints_init_mips.cc index 5c569232ac..7a0bd3e40d 100644 --- a/runtime/arch/mips/entrypoints_init_mips.cc +++ b/runtime/arch/mips/entrypoints_init_mips.cc @@ -262,6 +262,7 @@ void InitEntryPoints(JniEntryPoints* jpoints, QuickEntryPoints* qpoints) { art_quick_invoke_virtual_trampoline_with_access_check; static_assert(!IsDirectEntrypoint(kQuickInvokeVirtualTrampolineWithAccessCheck), "Non-direct C stub marked direct."); + qpoints->pInvokePolymorphic = art_quick_invoke_polymorphic; // Thread qpoints->pTestSuspend = art_quick_test_suspend; diff --git a/runtime/arch/mips/quick_entrypoints_mips.S b/runtime/arch/mips/quick_entrypoints_mips.S index 3acc0a9d5b..76218fb542 100644 --- a/runtime/arch/mips/quick_entrypoints_mips.S +++ b/runtime/arch/mips/quick_entrypoints_mips.S @@ -1389,28 +1389,6 @@ END art_quick_check_instance_of #endif // USE_READ_BARRIER .endm - /* - * Entry from managed code for array put operations of objects where the value being stored - * needs to be checked for compatibility. - * a0 = array, a1 = index, a2 = value - */ -ENTRY art_quick_aput_obj_with_null_and_bound_check - bnez $a0, .Lart_quick_aput_obj_with_bound_check_gp_set - nop - b art_quick_throw_null_pointer_exception - nop -END art_quick_aput_obj_with_null_and_bound_check - -ENTRY art_quick_aput_obj_with_bound_check - lw $t0, MIRROR_ARRAY_LENGTH_OFFSET($a0) - sltu $t1, $a1, $t0 - bnez $t1, .Lart_quick_aput_obj_gp_set - nop - move $a0, $a1 - b art_quick_throw_array_bounds - move $a1, $t0 -END art_quick_aput_obj_with_bound_check - #ifdef USE_READ_BARRIER .extern artReadBarrierSlow #endif @@ -2285,15 +2263,12 @@ END art_quick_string_compareto ENTRY art_quick_invoke_polymorphic SETUP_SAVE_REFS_AND_ARGS_FRAME move $a2, rSELF # Make $a2 an alias for the current Thread. - move $a3, $sp # Make $a3 a pointer to the saved frame context. - addiu $sp, $sp, -24 # Reserve space for JValue result and 4 words for callee. - .cfi_adjust_cfa_offset 24 + addiu $a3, $sp, ARG_SLOT_SIZE # Make $a3 a pointer to the saved frame context. sw $zero, 20($sp) # Initialize JValue result. sw $zero, 16($sp) - addiu $a0, $sp, 16 # Make $a0 a pointer to the JValue result la $t9, artInvokePolymorphic jalr $t9 # (result, receiver, Thread*, context) - nop + addiu $a0, $sp, 16 # Make $a0 a pointer to the JValue result .macro MATCH_RETURN_TYPE c, handler li $t0, \c beq $v0, $t0, \handler @@ -2307,18 +2282,17 @@ ENTRY art_quick_invoke_polymorphic MATCH_RETURN_TYPE 'D', .Lstore_double_result MATCH_RETURN_TYPE 'F', .Lstore_float_result MATCH_RETURN_TYPE 'S', .Lstore_int_result + MATCH_RETURN_TYPE 'Z', .Lstore_boolean_result .purgem MATCH_RETURN_TYPE nop b .Lcleanup_and_return nop .Lstore_boolean_result: - lbu $v0, 16($sp) # Move byte from JValue result to return value register. b .Lcleanup_and_return - nop + lbu $v0, 16($sp) # Move byte from JValue result to return value register. .Lstore_char_result: - lhu $v0, 16($sp) # Move char from JValue result to return value register. b .Lcleanup_and_return - nop + lhu $v0, 16($sp) # Move char from JValue result to return value register. .Lstore_double_result: .Lstore_float_result: LDu $f0, $f1, 16, $sp, $t0 # Move double/float from JValue result to return value register. @@ -2331,8 +2305,6 @@ ENTRY art_quick_invoke_polymorphic lw $v0, 16($sp) # Move lower bits from JValue result to return value register. // Fall-through to clean up and return. .Lcleanup_and_return: - addiu $sp, $sp, 24 # Remove space for JValue result and the 4 words for the callee. - .cfi_adjust_cfa_offset -24 lw $t7, THREAD_EXCEPTION_OFFSET(rSELF) # Load Thread::Current()->exception_ RESTORE_SAVE_REFS_AND_ARGS_FRAME bnez $t7, 1f # Success if no exception is pending. diff --git a/runtime/arch/mips64/quick_entrypoints_mips64.S b/runtime/arch/mips64/quick_entrypoints_mips64.S index ae786fe626..b53fd100fa 100644 --- a/runtime/arch/mips64/quick_entrypoints_mips64.S +++ b/runtime/arch/mips64/quick_entrypoints_mips64.S @@ -1360,29 +1360,6 @@ END art_quick_check_instance_of #endif // USE_READ_BARRIER .endm - /* - * Entry from managed code for array put operations of objects where the value being stored - * needs to be checked for compatibility. - * a0 = array, a1 = index, a2 = value - */ -ENTRY art_quick_aput_obj_with_null_and_bound_check - bne $a0, $zero, .Lart_quick_aput_obj_with_bound_check_gp_set - nop - b art_quick_throw_null_pointer_exception - .cpreturn # Restore gp from t8 in branch delay slot. -END art_quick_aput_obj_with_null_and_bound_check - -ENTRY art_quick_aput_obj_with_bound_check - lwu $t0, MIRROR_ARRAY_LENGTH_OFFSET($a0) - sltu $t1, $a1, $t0 - bne $t1, $zero, .Lart_quick_aput_obj_gp_set - nop - move $a0, $a1 - move $a1, $t0 - b art_quick_throw_array_bounds - .cpreturn # Restore gp from t8 in branch delay slot. -END art_quick_aput_obj_with_bound_check - ENTRY art_quick_aput_obj beq $a2, $zero, .Ldo_aput_null nop @@ -2132,9 +2109,8 @@ ENTRY art_quick_invoke_polymorphic daddiu $sp, $sp, -8 # Reserve space for JValue result. .cfi_adjust_cfa_offset 8 sd $zero, 0($sp) # Initialize JValue result. - move $a0, $sp # Make $a0 a pointer to the JValue result jal artInvokePolymorphic # (result, receiver, Thread*, context) - nop + move $a0, $sp # Make $a0 a pointer to the JValue result .macro MATCH_RETURN_TYPE c, handler li $t0, \c beq $v0, $t0, \handler @@ -2148,27 +2124,24 @@ ENTRY art_quick_invoke_polymorphic MATCH_RETURN_TYPE 'D', .Lstore_double_result MATCH_RETURN_TYPE 'F', .Lstore_float_result MATCH_RETURN_TYPE 'S', .Lstore_long_result + MATCH_RETURN_TYPE 'Z', .Lstore_boolean_result .purgem MATCH_RETURN_TYPE nop b .Lcleanup_and_return nop .Lstore_boolean_result: - lbu $v0, 0($sp) # Move byte from JValue result to return value register. b .Lcleanup_and_return - nop + lbu $v0, 0($sp) # Move byte from JValue result to return value register. .Lstore_char_result: - lhu $v0, 0($sp) # Move char from JValue result to return value register. b .Lcleanup_and_return - nop + lhu $v0, 0($sp) # Move char from JValue result to return value register. .Lstore_double_result: .Lstore_float_result: - l.d $f0, 0($sp) # Move double/float from JValue result to return value register. b .Lcleanup_and_return - nop + l.d $f0, 0($sp) # Move double/float from JValue result to return value register. .Lstore_ref_result: - lwu $v0, 0($sp) # Move zero extended lower 32-bits to return value register. b .Lcleanup_and_return - nop + lwu $v0, 0($sp) # Move zero extended lower 32-bits to return value register. .Lstore_long_result: ld $v0, 0($sp) # Move long from JValue result to return value register. // Fall-through to clean up and return. diff --git a/runtime/arch/quick_alloc_entrypoints.S b/runtime/arch/quick_alloc_entrypoints.S index abd9046174..3959cbee05 100644 --- a/runtime/arch/quick_alloc_entrypoints.S +++ b/runtime/arch/quick_alloc_entrypoints.S @@ -29,10 +29,6 @@ THREE_ARG_DOWNCALL art_quick_alloc_array_resolved\c_suffix, artAllocArrayFromCod // Called by managed code to allocate an array when the caller doesn't know whether it has access // to the created type. THREE_ARG_DOWNCALL art_quick_alloc_array_with_access_check\c_suffix, artAllocArrayFromCodeWithAccessCheck\cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER -// Called by managed code to allocate an array in a special case for FILLED_NEW_ARRAY. -THREE_ARG_DOWNCALL art_quick_check_and_alloc_array\c_suffix, artCheckAndAllocArrayFromCode\cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER -// Called by managed code to allocate an array in a special case for FILLED_NEW_ARRAY. -THREE_ARG_DOWNCALL art_quick_check_and_alloc_array_with_access_check\c_suffix, artCheckAndAllocArrayFromCodeWithAccessCheck\cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER // Called by managed code to allocate a string from bytes FOUR_ARG_DOWNCALL art_quick_alloc_string_from_bytes\c_suffix, artAllocStringFromBytesFromCode\cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER // Called by managed code to allocate a string from chars @@ -71,10 +67,6 @@ GENERATE_ALLOC_ENTRYPOINTS _region_tlab_instrumented, RegionTLABInstrumented THREE_ARG_DOWNCALL art_quick_alloc_array_resolved ## c_suffix, artAllocArrayFromCodeResolved ## cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER #define GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_WITH_ACCESS_CHECK(c_suffix, cxx_suffix) \ THREE_ARG_DOWNCALL art_quick_alloc_array_with_access_check ## c_suffix, artAllocArrayFromCodeWithAccessCheck ## cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER -#define GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY(c_suffix, cxx_suffix) \ - THREE_ARG_DOWNCALL art_quick_check_and_alloc_array ## c_suffix, artCheckAndAllocArrayFromCode ## cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER -#define GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY_WITH_ACCESS_CHECK(c_suffix, cxx_suffix) \ - THREE_ARG_DOWNCALL art_quick_check_and_alloc_array_with_access_check ## c_suffix, artCheckAndAllocArrayFromCodeWithAccessCheck ## cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER #define GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_BYTES(c_suffix, cxx_suffix) \ FOUR_ARG_DOWNCALL art_quick_alloc_string_from_bytes ## c_suffix, artAllocStringFromBytesFromCode ## cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER #define GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_CHARS(c_suffix, cxx_suffix) \ @@ -95,8 +87,6 @@ GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_WITH_ACCESS_CHECK(_region_tlab, RegionTL GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY(_region_tlab, RegionTLAB) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(_region_tlab, RegionTLAB) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_WITH_ACCESS_CHECK(_region_tlab, RegionTLAB) -GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY(_region_tlab, RegionTLAB) -GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY_WITH_ACCESS_CHECK(_region_tlab, RegionTLAB) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_BYTES(_region_tlab, RegionTLAB) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_CHARS(_region_tlab, RegionTLAB) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_region_tlab, RegionTLAB) @@ -110,8 +100,6 @@ GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_WITH_ACCESS_CHECK(_tlab, TLAB) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY(_tlab, TLAB) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(_tlab, TLAB) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_WITH_ACCESS_CHECK(_tlab, TLAB) -GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY(_tlab, TLAB) -GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY_WITH_ACCESS_CHECK(_tlab, TLAB) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_BYTES(_tlab, TLAB) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_CHARS(_tlab, TLAB) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_tlab, TLAB) @@ -129,8 +117,6 @@ GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_WITH_ACCESS_CHECK(_dlmalloc, DlMalloc) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY(_dlmalloc, DlMalloc) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(_dlmalloc, DlMalloc) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_WITH_ACCESS_CHECK(_dlmalloc, DlMalloc) -GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY(_dlmalloc, DlMalloc) -GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY_WITH_ACCESS_CHECK(_dlmalloc, DlMalloc) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_BYTES(_dlmalloc, DlMalloc) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_CHARS(_dlmalloc, DlMalloc) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_dlmalloc, DlMalloc) @@ -141,8 +127,6 @@ GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_WITH_ACCESS_CHECK(_dlmalloc_instrumented GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY(_dlmalloc_instrumented, DlMallocInstrumented) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(_dlmalloc_instrumented, DlMallocInstrumented) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_WITH_ACCESS_CHECK(_dlmalloc_instrumented, DlMallocInstrumented) -GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY(_dlmalloc_instrumented, DlMallocInstrumented) -GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY_WITH_ACCESS_CHECK(_dlmalloc_instrumented, DlMallocInstrumented) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_BYTES(_dlmalloc_instrumented, DlMallocInstrumented) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_CHARS(_dlmalloc_instrumented, DlMallocInstrumented) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_dlmalloc_instrumented, DlMallocInstrumented) @@ -154,8 +138,6 @@ GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_WITH_ACCESS_CHECK(_rosalloc, RosAlloc) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY(_rosalloc, RosAlloc) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(_rosalloc, RosAlloc) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_WITH_ACCESS_CHECK(_rosalloc, RosAlloc) -GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY(_rosalloc, RosAlloc) -GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY_WITH_ACCESS_CHECK(_rosalloc, RosAlloc) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_BYTES(_rosalloc, RosAlloc) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_CHARS(_rosalloc, RosAlloc) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_rosalloc, RosAlloc) @@ -166,8 +148,6 @@ GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_WITH_ACCESS_CHECK(_rosalloc_instrumented GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY(_rosalloc_instrumented, RosAllocInstrumented) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(_rosalloc_instrumented, RosAllocInstrumented) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_WITH_ACCESS_CHECK(_rosalloc_instrumented, RosAllocInstrumented) -GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY(_rosalloc_instrumented, RosAllocInstrumented) -GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY_WITH_ACCESS_CHECK(_rosalloc_instrumented, RosAllocInstrumented) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_BYTES(_rosalloc_instrumented, RosAllocInstrumented) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_CHARS(_rosalloc_instrumented, RosAllocInstrumented) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_rosalloc_instrumented, RosAllocInstrumented) @@ -178,8 +158,6 @@ GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_WITH_ACCESS_CHECK(_bump_pointer, BumpPoi GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY(_bump_pointer, BumpPointer) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(_bump_pointer, BumpPointer) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_WITH_ACCESS_CHECK(_bump_pointer, BumpPointer) -GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY(_bump_pointer, BumpPointer) -GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY_WITH_ACCESS_CHECK(_bump_pointer, BumpPointer) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_BYTES(_bump_pointer, BumpPointer) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_CHARS(_bump_pointer, BumpPointer) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_bump_pointer, BumpPointer) @@ -190,8 +168,6 @@ GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_WITH_ACCESS_CHECK(_bump_pointer_instrume GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY(_bump_pointer_instrumented, BumpPointerInstrumented) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(_bump_pointer_instrumented, BumpPointerInstrumented) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_WITH_ACCESS_CHECK(_bump_pointer_instrumented, BumpPointerInstrumented) -GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY(_bump_pointer_instrumented, BumpPointerInstrumented) -GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY_WITH_ACCESS_CHECK(_bump_pointer_instrumented, BumpPointerInstrumented) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_BYTES(_bump_pointer_instrumented, BumpPointerInstrumented) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_CHARS(_bump_pointer_instrumented, BumpPointerInstrumented) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_bump_pointer_instrumented, BumpPointerInstrumented) @@ -202,8 +178,6 @@ GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_WITH_ACCESS_CHECK(_tlab_instrumented, TL GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY(_tlab_instrumented, TLABInstrumented) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(_tlab_instrumented, TLABInstrumented) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_WITH_ACCESS_CHECK(_tlab_instrumented, TLABInstrumented) -GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY(_tlab_instrumented, TLABInstrumented) -GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY_WITH_ACCESS_CHECK(_tlab_instrumented, TLABInstrumented) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_BYTES(_tlab_instrumented, TLABInstrumented) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_CHARS(_tlab_instrumented, TLABInstrumented) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_tlab_instrumented, TLABInstrumented) @@ -214,8 +188,6 @@ GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_WITH_ACCESS_CHECK(_region, Region) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY(_region, Region) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(_region, Region) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_WITH_ACCESS_CHECK(_region, Region) -GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY(_region, Region) -GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY_WITH_ACCESS_CHECK(_region, Region) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_BYTES(_region, Region) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_CHARS(_region, Region) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_region, Region) @@ -226,8 +198,6 @@ GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_WITH_ACCESS_CHECK(_region_instrumented, GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY(_region_instrumented, RegionInstrumented) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(_region_instrumented, RegionInstrumented) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_WITH_ACCESS_CHECK(_region_instrumented, RegionInstrumented) -GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY(_region_instrumented, RegionInstrumented) -GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY_WITH_ACCESS_CHECK(_region_instrumented, RegionInstrumented) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_BYTES(_region_instrumented, RegionInstrumented) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_CHARS(_region_instrumented, RegionInstrumented) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_region_instrumented, RegionInstrumented) @@ -238,8 +208,6 @@ GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_WITH_ACCESS_CHECK(_region_tlab_instrumen GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY(_region_tlab_instrumented, RegionTLABInstrumented) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(_region_tlab_instrumented, RegionTLABInstrumented) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_WITH_ACCESS_CHECK(_region_tlab_instrumented, RegionTLABInstrumented) -GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY(_region_tlab_instrumented, RegionTLABInstrumented) -GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY_WITH_ACCESS_CHECK(_region_tlab_instrumented, RegionTLABInstrumented) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_BYTES(_region_tlab_instrumented, RegionTLABInstrumented) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_CHARS(_region_tlab_instrumented, RegionTLABInstrumented) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_region_tlab_instrumented, RegionTLABInstrumented) diff --git a/runtime/arch/stub_test.cc b/runtime/arch/stub_test.cc index ee65fa8ab0..393dfe6d19 100644 --- a/runtime/arch/stub_test.cc +++ b/runtime/arch/stub_test.cc @@ -908,139 +908,6 @@ TEST_F(StubTest, CheckCast) { #endif } - -TEST_F(StubTest, APutObj) { -#if defined(__i386__) || defined(__arm__) || defined(__aarch64__) || defined(__mips__) || \ - (defined(__x86_64__) && !defined(__APPLE__)) - Thread* self = Thread::Current(); - - // Do not check non-checked ones, we'd need handlers and stuff... - const uintptr_t art_quick_aput_obj_with_null_and_bound_check = - StubTest::GetEntrypoint(self, kQuickAputObjectWithNullAndBoundCheck); - - // Create an object - ScopedObjectAccess soa(self); - // garbage is created during ClassLinker::Init - - StackHandleScope<5> hs(soa.Self()); - Handle<mirror::Class> c( - hs.NewHandle(class_linker_->FindSystemClass(soa.Self(), "Ljava/lang/Object;"))); - Handle<mirror::Class> ca( - hs.NewHandle(class_linker_->FindSystemClass(soa.Self(), "[Ljava/lang/String;"))); - - // Build a string array of size 1 - Handle<mirror::ObjectArray<mirror::Object>> array( - hs.NewHandle(mirror::ObjectArray<mirror::Object>::Alloc(soa.Self(), ca.Get(), 10))); - - // Build a string -> should be assignable - Handle<mirror::String> str_obj( - hs.NewHandle(mirror::String::AllocFromModifiedUtf8(soa.Self(), "hello, world!"))); - - // Build a generic object -> should fail assigning - Handle<mirror::Object> obj_obj(hs.NewHandle(c->AllocObject(soa.Self()))); - - // Play with it... - - // 1) Success cases - // 1.1) Assign str_obj to array[0..3] - - EXPECT_FALSE(self->IsExceptionPending()); - - Invoke3(reinterpret_cast<size_t>(array.Get()), 0U, reinterpret_cast<size_t>(str_obj.Get()), - art_quick_aput_obj_with_null_and_bound_check, self); - - EXPECT_FALSE(self->IsExceptionPending()); - EXPECT_EQ(str_obj.Get(), array->Get(0)); - - Invoke3(reinterpret_cast<size_t>(array.Get()), 1U, reinterpret_cast<size_t>(str_obj.Get()), - art_quick_aput_obj_with_null_and_bound_check, self); - - EXPECT_FALSE(self->IsExceptionPending()); - EXPECT_EQ(str_obj.Get(), array->Get(1)); - - Invoke3(reinterpret_cast<size_t>(array.Get()), 2U, reinterpret_cast<size_t>(str_obj.Get()), - art_quick_aput_obj_with_null_and_bound_check, self); - - EXPECT_FALSE(self->IsExceptionPending()); - EXPECT_EQ(str_obj.Get(), array->Get(2)); - - Invoke3(reinterpret_cast<size_t>(array.Get()), 3U, reinterpret_cast<size_t>(str_obj.Get()), - art_quick_aput_obj_with_null_and_bound_check, self); - - EXPECT_FALSE(self->IsExceptionPending()); - EXPECT_EQ(str_obj.Get(), array->Get(3)); - - // 1.2) Assign null to array[0..3] - - Invoke3(reinterpret_cast<size_t>(array.Get()), 0U, reinterpret_cast<size_t>(nullptr), - art_quick_aput_obj_with_null_and_bound_check, self); - - EXPECT_FALSE(self->IsExceptionPending()); - EXPECT_EQ(nullptr, array->Get(0)); - - Invoke3(reinterpret_cast<size_t>(array.Get()), 1U, reinterpret_cast<size_t>(nullptr), - art_quick_aput_obj_with_null_and_bound_check, self); - - EXPECT_FALSE(self->IsExceptionPending()); - EXPECT_EQ(nullptr, array->Get(1)); - - Invoke3(reinterpret_cast<size_t>(array.Get()), 2U, reinterpret_cast<size_t>(nullptr), - art_quick_aput_obj_with_null_and_bound_check, self); - - EXPECT_FALSE(self->IsExceptionPending()); - EXPECT_EQ(nullptr, array->Get(2)); - - Invoke3(reinterpret_cast<size_t>(array.Get()), 3U, reinterpret_cast<size_t>(nullptr), - art_quick_aput_obj_with_null_and_bound_check, self); - - EXPECT_FALSE(self->IsExceptionPending()); - EXPECT_EQ(nullptr, array->Get(3)); - - // TODO: Check _which_ exception is thrown. Then make 3) check that it's the right check order. - - // 2) Failure cases (str into str[]) - // 2.1) Array = null - // TODO: Throwing NPE needs actual DEX code - -// Invoke3(reinterpret_cast<size_t>(nullptr), 0U, reinterpret_cast<size_t>(str_obj.Get()), -// reinterpret_cast<uintptr_t>(&art_quick_aput_obj_with_null_and_bound_check), self); -// -// EXPECT_TRUE(self->IsExceptionPending()); -// self->ClearException(); - - // 2.2) Index < 0 - - Invoke3(reinterpret_cast<size_t>(array.Get()), static_cast<size_t>(-1), - reinterpret_cast<size_t>(str_obj.Get()), - art_quick_aput_obj_with_null_and_bound_check, self); - - EXPECT_TRUE(self->IsExceptionPending()); - self->ClearException(); - - // 2.3) Index > 0 - - Invoke3(reinterpret_cast<size_t>(array.Get()), 10U, reinterpret_cast<size_t>(str_obj.Get()), - art_quick_aput_obj_with_null_and_bound_check, self); - - EXPECT_TRUE(self->IsExceptionPending()); - self->ClearException(); - - // 3) Failure cases (obj into str[]) - - Invoke3(reinterpret_cast<size_t>(array.Get()), 0U, reinterpret_cast<size_t>(obj_obj.Get()), - art_quick_aput_obj_with_null_and_bound_check, self); - - EXPECT_TRUE(self->IsExceptionPending()); - self->ClearException(); - - // Tests done. -#else - LOG(INFO) << "Skipping aput_obj as I don't know how to do that on " << kRuntimeISA; - // Force-print to std::cout so it's also outside the logcat. - std::cout << "Skipping aput_obj as I don't know how to do that on " << kRuntimeISA << std::endl; -#endif -} - TEST_F(StubTest, AllocObject) { #if defined(__i386__) || defined(__arm__) || defined(__aarch64__) || defined(__mips__) || \ (defined(__x86_64__) && !defined(__APPLE__)) diff --git a/runtime/arch/x86/quick_entrypoints_x86.S b/runtime/arch/x86/quick_entrypoints_x86.S index 1d979d852e..c4202596f2 100644 --- a/runtime/arch/x86/quick_entrypoints_x86.S +++ b/runtime/arch/x86/quick_entrypoints_x86.S @@ -1352,26 +1352,6 @@ MACRO4(READ_BARRIER, obj_reg, offset, dest_reg, pop_eax) #endif // USE_READ_BARRIER END_MACRO - /* - * Entry from managed code for array put operations of objects where the value being stored - * needs to be checked for compatibility. - * eax = array, ecx = index, edx = value - */ -DEFINE_FUNCTION art_quick_aput_obj_with_null_and_bound_check - testl %eax, %eax - jnz SYMBOL(art_quick_aput_obj_with_bound_check) - jmp SYMBOL(art_quick_throw_null_pointer_exception) -END_FUNCTION art_quick_aput_obj_with_null_and_bound_check - -DEFINE_FUNCTION art_quick_aput_obj_with_bound_check - movl MIRROR_ARRAY_LENGTH_OFFSET(%eax), %ebx - cmpl %ebx, %ecx - jb SYMBOL(art_quick_aput_obj) - mov %ecx, %eax - mov %ebx, %ecx - jmp SYMBOL(art_quick_throw_array_bounds) -END_FUNCTION art_quick_aput_obj_with_bound_check - DEFINE_FUNCTION art_quick_aput_obj test %edx, %edx // store of null jz .Ldo_aput_null diff --git a/runtime/arch/x86_64/quick_entrypoints_x86_64.S b/runtime/arch/x86_64/quick_entrypoints_x86_64.S index 28034c9bae..550a5e8452 100644 --- a/runtime/arch/x86_64/quick_entrypoints_x86_64.S +++ b/runtime/arch/x86_64/quick_entrypoints_x86_64.S @@ -989,8 +989,6 @@ GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_WITH_ACCESS_CHECK(_region_tlab, RegionTL // GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY(_region_tlab, RegionTLAB) // GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(_region_tlab, RegionTLAB) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_WITH_ACCESS_CHECK(_region_tlab, RegionTLAB) -GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY(_region_tlab, RegionTLAB) -GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY_WITH_ACCESS_CHECK(_region_tlab, RegionTLAB) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_BYTES(_region_tlab, RegionTLAB) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_CHARS(_region_tlab, RegionTLAB) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_region_tlab, RegionTLAB) @@ -1000,8 +998,6 @@ GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_region_tlab, RegionTLAB) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_WITH_ACCESS_CHECK(_tlab, TLAB) // GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(_tlab, TLAB) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_WITH_ACCESS_CHECK(_tlab, TLAB) -GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY(_tlab, TLAB) -GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY_WITH_ACCESS_CHECK(_tlab, TLAB) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_BYTES(_tlab, TLAB) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_CHARS(_tlab, TLAB) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_tlab, TLAB) @@ -1466,7 +1462,7 @@ END_MACRO * 64b PUSH/POP and 32b argument. * TODO: When read barrier has a fast path, add heap unpoisoning support for the fast path. * - * As with art_quick_aput_obj* functions, the 64b versions are in comments. + * As with art_quick_aput_obj function, the 64b versions are in comments. */ MACRO4(READ_BARRIER, obj_reg, offset, dest_reg32, dest_reg64) #ifdef USE_READ_BARRIER @@ -1503,46 +1499,6 @@ MACRO4(READ_BARRIER, obj_reg, offset, dest_reg32, dest_reg64) #endif // USE_READ_BARRIER END_MACRO - /* - * Entry from managed code for array put operations of objects where the value being stored - * needs to be checked for compatibility. - * - * Currently all the parameters should fit into the 32b portions of the registers. Index always - * will. So we optimize for a tighter encoding. The 64b versions are in comments. - * - * rdi(edi) = array, rsi(esi) = index, rdx(edx) = value - */ -DEFINE_FUNCTION art_quick_aput_obj_with_null_and_bound_check -#if defined(__APPLE__) - int3 - int3 -#else - testl %edi, %edi -// testq %rdi, %rdi - jnz art_quick_aput_obj_with_bound_check - jmp art_quick_throw_null_pointer_exception -#endif // __APPLE__ -END_FUNCTION art_quick_aput_obj_with_null_and_bound_check - - -DEFINE_FUNCTION art_quick_aput_obj_with_bound_check -#if defined(__APPLE__) - int3 - int3 -#else - movl MIRROR_ARRAY_LENGTH_OFFSET(%edi), %ecx -// movl MIRROR_ARRAY_LENGTH_OFFSET(%rdi), %ecx // This zero-extends, so value(%rcx)=value(%ecx) - cmpl %ecx, %esi - jb art_quick_aput_obj - mov %esi, %edi -// mov %rsi, %rdi - mov %ecx, %esi -// mov %rcx, %rsi - jmp art_quick_throw_array_bounds -#endif // __APPLE__ -END_FUNCTION art_quick_aput_obj_with_bound_check - - DEFINE_FUNCTION art_quick_aput_obj testl %edx, %edx // store of null // test %rdx, %rdx diff --git a/runtime/art_method-inl.h b/runtime/art_method-inl.h index 96976d9bce..0fd891c9ec 100644 --- a/runtime/art_method-inl.h +++ b/runtime/art_method-inl.h @@ -121,7 +121,7 @@ inline uint16_t ArtMethod::GetMethodIndexDuringLinking() { inline uint32_t ArtMethod::GetDexMethodIndex() { DCHECK(IsRuntimeMethod() || GetDeclaringClass()->IsIdxLoaded() || GetDeclaringClass()->IsErroneous()); - return dex_method_index_; + return GetDexMethodIndexUnchecked(); } inline ArtMethod** ArtMethod::GetDexCacheResolvedMethods(PointerSize pointer_size) { @@ -180,20 +180,6 @@ inline GcRoot<mirror::Class>* ArtMethod::GetDexCacheResolvedTypes(PointerSize po pointer_size); } -template <bool kWithCheck> -inline mirror::Class* ArtMethod::GetDexCacheResolvedType(dex::TypeIndex type_index, - PointerSize pointer_size) { - if (kWithCheck) { - mirror::DexCache* dex_cache = GetInterfaceMethodIfProxy(pointer_size)->GetDexCache(); - if (UNLIKELY(type_index.index_ >= dex_cache->NumResolvedTypes())) { - ThrowArrayIndexOutOfBoundsException(type_index.index_, dex_cache->NumResolvedTypes()); - return nullptr; - } - } - mirror::Class* klass = GetDexCacheResolvedTypes(pointer_size)[type_index.index_].Read(); - return (klass != nullptr && !klass->IsErroneous()) ? klass : nullptr; -} - inline bool ArtMethod::HasDexCacheResolvedTypes(PointerSize pointer_size) { return GetDexCacheResolvedTypes(pointer_size) != nullptr; } @@ -207,15 +193,15 @@ inline bool ArtMethod::HasSameDexCacheResolvedTypes(ArtMethod* other, PointerSiz return GetDexCacheResolvedTypes(pointer_size) == other->GetDexCacheResolvedTypes(pointer_size); } -inline mirror::Class* ArtMethod::GetClassFromTypeIndex(dex::TypeIndex type_idx, - bool resolve, - PointerSize pointer_size) { - mirror::Class* type = GetDexCacheResolvedType(type_idx, pointer_size); - if (type == nullptr && resolve) { - type = Runtime::Current()->GetClassLinker()->ResolveType(type_idx, this); +inline mirror::Class* ArtMethod::GetClassFromTypeIndex(dex::TypeIndex type_idx, bool resolve) { + ObjPtr<mirror::DexCache> dex_cache = GetDexCache(); + ObjPtr<mirror::Class> type = dex_cache->GetResolvedType(type_idx); + if (UNLIKELY(type == nullptr) && resolve) { + ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); + type = class_linker->ResolveType(type_idx, this); CHECK(type != nullptr || Thread::Current()->IsExceptionPending()); } - return type; + return type.Ptr(); } inline bool ArtMethod::CheckIncompatibleClassChange(InvokeType type) { @@ -333,9 +319,9 @@ inline const DexFile::CodeItem* ArtMethod::GetCodeItem() { return GetDexFile()->GetCodeItem(GetCodeItemOffset()); } -inline bool ArtMethod::IsResolvedTypeIdx(dex::TypeIndex type_idx, PointerSize pointer_size) { +inline bool ArtMethod::IsResolvedTypeIdx(dex::TypeIndex type_idx) { DCHECK(!IsProxyMethod()); - return GetDexCacheResolvedType(type_idx, pointer_size) != nullptr; + return GetClassFromTypeIndex(type_idx, /* resolve */ false) != nullptr; } inline int32_t ArtMethod::GetLineNumFromDexPC(uint32_t dex_pc) { @@ -435,18 +421,13 @@ inline void ArtMethod::SetDexCacheResolvedTypes(GcRoot<mirror::Class>* new_dex_c SetNativePointer(DexCacheResolvedTypesOffset(pointer_size), new_dex_cache_types, pointer_size); } -inline mirror::Class* ArtMethod::GetReturnType(bool resolve, PointerSize pointer_size) { +inline mirror::Class* ArtMethod::GetReturnType(bool resolve) { DCHECK(!IsProxyMethod()); const DexFile* dex_file = GetDexFile(); const DexFile::MethodId& method_id = dex_file->GetMethodId(GetDexMethodIndex()); const DexFile::ProtoId& proto_id = dex_file->GetMethodPrototype(method_id); dex::TypeIndex return_type_idx = proto_id.return_type_idx_; - mirror::Class* type = GetDexCacheResolvedType(return_type_idx, pointer_size); - if (type == nullptr && resolve) { - type = Runtime::Current()->GetClassLinker()->ResolveType(return_type_idx, this); - CHECK(type != nullptr || Thread::Current()->IsExceptionPending()); - } - return type; + return GetClassFromTypeIndex(return_type_idx, resolve); } inline bool ArtMethod::HasSingleImplementation() { diff --git a/runtime/art_method.cc b/runtime/art_method.cc index dfc7837aea..d7d39afa8f 100644 --- a/runtime/art_method.cc +++ b/runtime/art_method.cc @@ -236,7 +236,6 @@ uint32_t ArtMethod::FindCatchBlock(Handle<mirror::Class> exception_type, // Default to handler not found. uint32_t found_dex_pc = DexFile::kDexNoIndex; // Iterate over the catch handlers associated with dex_pc. - PointerSize pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize(); for (CatchHandlerIterator it(*code_item, dex_pc); it.HasNext(); it.Next()) { dex::TypeIndex iter_type_idx = it.GetHandlerTypeIndex(); // Catch all case @@ -245,9 +244,7 @@ uint32_t ArtMethod::FindCatchBlock(Handle<mirror::Class> exception_type, break; } // Does this catch exception type apply? - mirror::Class* iter_exception_type = GetClassFromTypeIndex(iter_type_idx, - true /* resolve */, - pointer_size); + mirror::Class* iter_exception_type = GetClassFromTypeIndex(iter_type_idx, true /* resolve */); if (UNLIKELY(iter_exception_type == nullptr)) { // Now have a NoClassDefFoundError as exception. Ignore in case the exception class was // removed by a pro-guard like tool. diff --git a/runtime/art_method.h b/runtime/art_method.h index 11dcc35df5..912df85815 100644 --- a/runtime/art_method.h +++ b/runtime/art_method.h @@ -322,6 +322,9 @@ class ArtMethod FINAL { // Number of 32bit registers that would be required to hold all the arguments static size_t NumArgRegisters(const StringPiece& shorty); + ALWAYS_INLINE uint32_t GetDexMethodIndexUnchecked() { + return dex_method_index_; + } ALWAYS_INLINE uint32_t GetDexMethodIndex() REQUIRES_SHARED(Locks::mutator_lock_); void SetDexMethodIndex(uint32_t new_idx) { @@ -348,9 +351,6 @@ class ArtMethod FINAL { bool HasSameDexCacheResolvedMethods(ArtMethod** other_cache, PointerSize pointer_size) REQUIRES_SHARED(Locks::mutator_lock_); - template <bool kWithCheck = true> - mirror::Class* GetDexCacheResolvedType(dex::TypeIndex type_idx, PointerSize pointer_size) - REQUIRES_SHARED(Locks::mutator_lock_); void SetDexCacheResolvedTypes(GcRoot<mirror::Class>* new_dex_cache_types, PointerSize pointer_size) REQUIRES_SHARED(Locks::mutator_lock_); @@ -361,9 +361,7 @@ class ArtMethod FINAL { REQUIRES_SHARED(Locks::mutator_lock_); // Get the Class* from the type index into this method's dex cache. - mirror::Class* GetClassFromTypeIndex(dex::TypeIndex type_idx, - bool resolve, - PointerSize pointer_size) + mirror::Class* GetClassFromTypeIndex(dex::TypeIndex type_idx, bool resolve) REQUIRES_SHARED(Locks::mutator_lock_); // Returns true if this method has the same name and signature of the other method. @@ -555,8 +553,7 @@ class ArtMethod FINAL { const DexFile::CodeItem* GetCodeItem() REQUIRES_SHARED(Locks::mutator_lock_); - bool IsResolvedTypeIdx(dex::TypeIndex type_idx, PointerSize pointer_size) - REQUIRES_SHARED(Locks::mutator_lock_); + bool IsResolvedTypeIdx(dex::TypeIndex type_idx) REQUIRES_SHARED(Locks::mutator_lock_); int32_t GetLineNumFromDexPC(uint32_t dex_pc) REQUIRES_SHARED(Locks::mutator_lock_); @@ -577,8 +574,7 @@ class ArtMethod FINAL { // May cause thread suspension due to GetClassFromTypeIdx calling ResolveType this caused a large // number of bugs at call sites. - mirror::Class* GetReturnType(bool resolve, PointerSize pointer_size) - REQUIRES_SHARED(Locks::mutator_lock_); + mirror::Class* GetReturnType(bool resolve) REQUIRES_SHARED(Locks::mutator_lock_); mirror::ClassLoader* GetClassLoader() REQUIRES_SHARED(Locks::mutator_lock_); diff --git a/runtime/asm_support.h b/runtime/asm_support.h index e4972da13d..18a53c916e 100644 --- a/runtime/asm_support.h +++ b/runtime/asm_support.h @@ -90,7 +90,7 @@ ADD_TEST_EQ(THREAD_SELF_OFFSET, art::Thread::SelfOffset<POINTER_SIZE>().Int32Value()) // Offset of field Thread::tlsPtr_.thread_local_pos. -#define THREAD_LOCAL_POS_OFFSET (THREAD_CARD_TABLE_OFFSET + 198 * __SIZEOF_POINTER__) +#define THREAD_LOCAL_POS_OFFSET (THREAD_CARD_TABLE_OFFSET + 34 * __SIZEOF_POINTER__) ADD_TEST_EQ(THREAD_LOCAL_POS_OFFSET, art::Thread::ThreadLocalPosOffset<POINTER_SIZE>().Int32Value()) // Offset of field Thread::tlsPtr_.thread_local_end. @@ -98,11 +98,13 @@ ADD_TEST_EQ(THREAD_LOCAL_POS_OFFSET, ADD_TEST_EQ(THREAD_LOCAL_END_OFFSET, art::Thread::ThreadLocalEndOffset<POINTER_SIZE>().Int32Value()) // Offset of field Thread::tlsPtr_.thread_local_objects. -#define THREAD_LOCAL_OBJECTS_OFFSET (THREAD_LOCAL_END_OFFSET + 2 * __SIZEOF_POINTER__) +#define THREAD_LOCAL_OBJECTS_OFFSET (THREAD_LOCAL_END_OFFSET + __SIZEOF_POINTER__) ADD_TEST_EQ(THREAD_LOCAL_OBJECTS_OFFSET, art::Thread::ThreadLocalObjectsOffset<POINTER_SIZE>().Int32Value()) + // Offset of field Thread::tlsPtr_.mterp_current_ibase. -#define THREAD_CURRENT_IBASE_OFFSET (THREAD_LOCAL_OBJECTS_OFFSET + __SIZEOF_SIZE_T__) +#define THREAD_CURRENT_IBASE_OFFSET \ + (THREAD_LOCAL_OBJECTS_OFFSET + __SIZEOF_SIZE_T__ + (1 + 159) * __SIZEOF_POINTER__) ADD_TEST_EQ(THREAD_CURRENT_IBASE_OFFSET, art::Thread::MterpCurrentIBaseOffset<POINTER_SIZE>().Int32Value()) // Offset of field Thread::tlsPtr_.mterp_default_ibase. diff --git a/runtime/class_linker-inl.h b/runtime/class_linker-inl.h index 5fc5f1a2f5..2e17dd85e6 100644 --- a/runtime/class_linker-inl.h +++ b/runtime/class_linker-inl.h @@ -25,7 +25,6 @@ #include "mirror/class_loader.h" #include "mirror/dex_cache-inl.h" #include "mirror/iftable.h" -#include "mirror/throwable.h" #include "mirror/object_array.h" #include "handle_scope-inl.h" #include "scoped_thread_state_change-inl.h" @@ -90,25 +89,16 @@ inline mirror::Class* ClassLinker::ResolveType(dex::TypeIndex type_idx, ArtMetho if (kIsDebugBuild) { Thread::Current()->AssertNoPendingException(); } - ObjPtr<mirror::Class> resolved_type = - referrer->GetDexCacheResolvedType(type_idx, image_pointer_size_); + ObjPtr<mirror::Class> resolved_type = referrer->GetDexCache()->GetResolvedType(type_idx); if (UNLIKELY(resolved_type == nullptr)) { StackHandleScope<2> hs(Thread::Current()); - // There could be an out of bounds exception from GetDexCacheResolvedType, don't call - // ResolveType for this case. - if (LIKELY(!hs.Self()->IsExceptionPending())) { - ObjPtr<mirror::Class> declaring_class = referrer->GetDeclaringClass(); - Handle<mirror::DexCache> dex_cache(hs.NewHandle(declaring_class->GetDexCache())); - Handle<mirror::ClassLoader> class_loader(hs.NewHandle(declaring_class->GetClassLoader())); - const DexFile& dex_file = *dex_cache->GetDexFile(); - resolved_type = ResolveType(dex_file, type_idx, dex_cache, class_loader); - // Note: We cannot check here to see whether we added the type to the cache. The type - // might be an erroneous class, which results in it being hidden from us. - } else { - // Make sure its an array out of bounds exception. - DCHECK(hs.Self()->GetException()->GetClass()->DescriptorEquals( - "Ljava/lang/ArrayIndexOutOfBoundsException;")); - } + ObjPtr<mirror::Class> declaring_class = referrer->GetDeclaringClass(); + Handle<mirror::DexCache> dex_cache(hs.NewHandle(declaring_class->GetDexCache())); + Handle<mirror::ClassLoader> class_loader(hs.NewHandle(declaring_class->GetClassLoader())); + const DexFile& dex_file = *dex_cache->GetDexFile(); + resolved_type = ResolveType(dex_file, type_idx, dex_cache, class_loader); + // Note: We cannot check here to see whether we added the type to the cache. The type + // might be an erroneous class, which results in it being hidden from us. } return resolved_type.Ptr(); } @@ -256,8 +246,8 @@ ArtMethod* ClassLinker::FindMethodForProxy(ObjPtr<mirror::Class> proxy_class, // Locate the dex cache of the original interface/Object for (const DexCacheData& data : dex_caches_) { if (!self->IsJWeakCleared(data.weak_root) && - proxy_method->HasSameDexCacheResolvedTypes(data.resolved_types, - image_pointer_size_)) { + proxy_method->HasSameDexCacheResolvedMethods(data.resolved_methods, + image_pointer_size_)) { ObjPtr<mirror::DexCache> dex_cache = ObjPtr<mirror::DexCache>::DownCast(self->DecodeJObject(data.weak_root)); if (dex_cache != nullptr) { diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index 129c93f575..07c6eda03f 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -2711,10 +2711,6 @@ mirror::Class* ClassLinker::DefineClass(Thread* self, CHECK(h_new_class.Get() != nullptr) << descriptor; CHECK(h_new_class->IsResolved()) << descriptor; - // Update the dex cache of where the class is defined. Inlining depends on having - // this filled. - h_new_class->GetDexCache()->SetResolvedType(h_new_class->GetDexTypeIndex(), h_new_class.Get()); - // Instrumentation may have updated entrypoints for all methods of all // classes. However it could not update methods of this class while we // were loading it. Now the class is resolved, we can update entrypoints @@ -3307,7 +3303,7 @@ void ClassLinker::RegisterDexFileLocked(const DexFile& dex_file, DexCacheData data; data.weak_root = dex_cache_jweak; data.dex_file = dex_cache->GetDexFile(); - data.resolved_types = dex_cache->GetResolvedTypes(); + data.resolved_methods = dex_cache->GetResolvedMethods(); dex_caches_.push_back(data); } @@ -4377,8 +4373,7 @@ void ClassLinker::CheckProxyMethod(ArtMethod* method, ArtMethod* prototype) cons CHECK_STREQ(np->GetName(), prototype->GetName()); CHECK_STREQ(np->GetShorty(), prototype->GetShorty()); // More complex sanity - via dex cache - CHECK_EQ(np->GetReturnType(true /* resolve */, image_pointer_size_), - prototype->GetReturnType(true /* resolve */, image_pointer_size_)); + CHECK_EQ(np->GetReturnType(true /* resolve */), prototype->GetReturnType(true /* resolve */)); } bool ClassLinker::CanWeInitializeClass(ObjPtr<mirror::Class> klass, bool can_init_statics, @@ -4840,7 +4835,6 @@ static void ThrowSignatureMismatch(Handle<mirror::Class> klass, } static bool HasSameSignatureWithDifferentClassLoaders(Thread* self, - PointerSize pointer_size, Handle<mirror::Class> klass, Handle<mirror::Class> super_klass, ArtMethod* method1, @@ -4848,14 +4842,12 @@ static bool HasSameSignatureWithDifferentClassLoaders(Thread* self, REQUIRES_SHARED(Locks::mutator_lock_) { { StackHandleScope<1> hs(self); - Handle<mirror::Class> return_type(hs.NewHandle(method1->GetReturnType(true /* resolve */, - pointer_size))); + Handle<mirror::Class> return_type(hs.NewHandle(method1->GetReturnType(true /* resolve */))); if (UNLIKELY(return_type.Get() == nullptr)) { ThrowSignatureCheckResolveReturnTypeException(klass, super_klass, method1, method1); return false; } - ObjPtr<mirror::Class> other_return_type = method2->GetReturnType(true /* resolve */, - pointer_size); + ObjPtr<mirror::Class> other_return_type = method2->GetReturnType(true /* resolve */); if (UNLIKELY(other_return_type == nullptr)) { ThrowSignatureCheckResolveReturnTypeException(klass, super_klass, method1, method2); return false; @@ -4900,7 +4892,7 @@ static bool HasSameSignatureWithDifferentClassLoaders(Thread* self, StackHandleScope<1> hs(self); dex::TypeIndex param_type_idx = types1->GetTypeItem(i).type_idx_; Handle<mirror::Class> param_type(hs.NewHandle( - method1->GetClassFromTypeIndex(param_type_idx, true /* resolve */, pointer_size))); + method1->GetClassFromTypeIndex(param_type_idx, true /* resolve */))); if (UNLIKELY(param_type.Get() == nullptr)) { ThrowSignatureCheckResolveArgException(klass, super_klass, method1, method1, i, param_type_idx); @@ -4908,7 +4900,7 @@ static bool HasSameSignatureWithDifferentClassLoaders(Thread* self, } dex::TypeIndex other_param_type_idx = types2->GetTypeItem(i).type_idx_; ObjPtr<mirror::Class> other_param_type = - method2->GetClassFromTypeIndex(other_param_type_idx, true /* resolve */, pointer_size); + method2->GetClassFromTypeIndex(other_param_type_idx, true /* resolve */); if (UNLIKELY(other_param_type == nullptr)) { ThrowSignatureCheckResolveArgException(klass, super_klass, method1, method2, i, other_param_type_idx); @@ -4944,9 +4936,11 @@ bool ClassLinker::ValidateSuperClassDescriptors(Handle<mirror::Class> klass) { auto* m = klass->GetVTableEntry(i, image_pointer_size_); auto* super_m = klass->GetSuperClass()->GetVTableEntry(i, image_pointer_size_); if (m != super_m) { - if (UNLIKELY(!HasSameSignatureWithDifferentClassLoaders(self, image_pointer_size_, - klass, super_klass, - m, super_m))) { + if (UNLIKELY(!HasSameSignatureWithDifferentClassLoaders(self, + klass, + super_klass, + m, + super_m))) { self->AssertPendingException(); return false; } @@ -4962,9 +4956,11 @@ bool ClassLinker::ValidateSuperClassDescriptors(Handle<mirror::Class> klass) { j, image_pointer_size_); auto* super_m = super_klass->GetVirtualMethod(j, image_pointer_size_); if (m != super_m) { - if (UNLIKELY(!HasSameSignatureWithDifferentClassLoaders(self, image_pointer_size_, - klass, super_klass, - m, super_m))) { + if (UNLIKELY(!HasSameSignatureWithDifferentClassLoaders(self, + klass, + super_klass, + m, + super_m))) { self->AssertPendingException(); return false; } diff --git a/runtime/class_linker.h b/runtime/class_linker.h index 580acb7068..823848b3bc 100644 --- a/runtime/class_linker.h +++ b/runtime/class_linker.h @@ -645,6 +645,10 @@ class ClassLinker { mirror::Class* GetHoldingClassOfCopiedMethod(ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_); + // Returns null if not found. + ClassTable* ClassTableForClassLoader(ObjPtr<mirror::ClassLoader> class_loader) + REQUIRES_SHARED(Locks::mutator_lock_); + struct DexCacheData { // Weak root to the DexCache. Note: Do not decode this unnecessarily or else class unloading may // not work properly. @@ -653,7 +657,7 @@ class ClassLinker { // jweak decode that triggers read barriers (and mark them alive unnecessarily and mess with // class unloading.) const DexFile* dex_file; - GcRoot<mirror::Class>* resolved_types; + ArtMethod** resolved_methods; }; private: @@ -1039,10 +1043,6 @@ class ClassLinker { REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(Locks::classlinker_classes_lock_); - // Returns null if not found. - ClassTable* ClassTableForClassLoader(ObjPtr<mirror::ClassLoader> class_loader) - REQUIRES_SHARED(Locks::mutator_lock_); - // Insert a new class table if not found. ClassTable* InsertClassTableForClassLoader(ObjPtr<mirror::ClassLoader> class_loader) REQUIRES_SHARED(Locks::mutator_lock_) diff --git a/runtime/class_linker_test.cc b/runtime/class_linker_test.cc index 42108d8621..bbe7280bb3 100644 --- a/runtime/class_linker_test.cc +++ b/runtime/class_linker_test.cc @@ -460,7 +460,6 @@ class ClassLinkerMethodHandlesTest : public ClassLinkerTest { protected: virtual void SetUpRuntimeOptions(RuntimeOptions* options) OVERRIDE { CommonRuntimeTest::SetUpRuntimeOptions(options); - options->push_back(std::make_pair("-Xexperimental:method-handles", nullptr)); } }; @@ -757,6 +756,7 @@ struct MethodHandleImplOffsets : public CheckOffsets<mirror::MethodHandleImpl> { struct EmulatedStackFrameOffsets : public CheckOffsets<mirror::EmulatedStackFrame> { EmulatedStackFrameOffsets() : CheckOffsets<mirror::EmulatedStackFrame>( false, "Ldalvik/system/EmulatedStackFrame;") { + addOffset(OFFSETOF_MEMBER(mirror::EmulatedStackFrame, callsite_type_), "callsiteType"); addOffset(OFFSETOF_MEMBER(mirror::EmulatedStackFrame, references_), "references"); addOffset(OFFSETOF_MEMBER(mirror::EmulatedStackFrame, stack_frame_), "stackFrame"); addOffset(OFFSETOF_MEMBER(mirror::EmulatedStackFrame, type_), "type"); @@ -899,7 +899,6 @@ TEST_F(ClassLinkerTest, LookupResolvedType) { dex::TypeIndex type_idx = klass->GetClassDef()->class_idx_; ObjPtr<mirror::DexCache> dex_cache = klass->GetDexCache(); const DexFile& dex_file = klass->GetDexFile(); - EXPECT_OBJ_PTR_EQ(dex_cache->GetResolvedType(type_idx), klass); EXPECT_OBJ_PTR_EQ( class_linker_->LookupResolvedType(dex_file, type_idx, dex_cache, class_loader.Get()), klass); diff --git a/runtime/debugger.cc b/runtime/debugger.cc index df4413d52c..006476bafc 100644 --- a/runtime/debugger.cc +++ b/runtime/debugger.cc @@ -3985,9 +3985,7 @@ JDWP::JdwpError Dbg::PrepareInvokeMethod(uint32_t request_id, JDWP::ObjectId thr if (shorty[i + 1] == 'L') { // Did we really get an argument of an appropriate reference type? mirror::Class* parameter_type = - m->GetClassFromTypeIndex(types->GetTypeItem(i).type_idx_, - true /* resolve */, - kRuntimePointerSize); + m->GetClassFromTypeIndex(types->GetTypeItem(i).type_idx_, true /* resolve */); mirror::Object* argument = gRegistry->Get<mirror::Object*>(arg_values[i], &error); if (error != JDWP::ERR_NONE) { return JDWP::ERR_INVALID_OBJECT; diff --git a/runtime/dex_file_annotations.cc b/runtime/dex_file_annotations.cc index 9504e8bd29..16a447b0a6 100644 --- a/runtime/dex_file_annotations.cc +++ b/runtime/dex_file_annotations.cc @@ -608,7 +608,7 @@ mirror::Object* CreateAnnotationMember(Handle<mirror::Class> klass, return nullptr; } Handle<mirror::Class> method_return(hs.NewHandle( - annotation_method->GetReturnType(true /* resolve */, pointer_size))); + annotation_method->GetReturnType(true /* resolve */))); DexFile::AnnotationValue annotation_value; if (!ProcessAnnotationValue(klass, annotation, &annotation_value, method_return, @@ -948,9 +948,7 @@ mirror::Object* GetAnnotationDefaultValue(ArtMethod* method) { DexFile::AnnotationValue annotation_value; StackHandleScope<2> hs(Thread::Current()); Handle<mirror::Class> h_klass(hs.NewHandle(klass)); - PointerSize pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize(); - Handle<mirror::Class> return_type(hs.NewHandle( - method->GetReturnType(true /* resolve */, pointer_size))); + Handle<mirror::Class> return_type(hs.NewHandle(method->GetReturnType(true /* resolve */))); if (!ProcessAnnotationValue(h_klass, &annotation, &annotation_value, return_type, DexFile::kAllObjects)) { return nullptr; diff --git a/runtime/entrypoints/entrypoint_utils-inl.h b/runtime/entrypoints/entrypoint_utils-inl.h index 5ff95b480f..87046bc09d 100644 --- a/runtime/entrypoints/entrypoint_utils-inl.h +++ b/runtime/entrypoints/entrypoint_utils-inl.h @@ -52,21 +52,19 @@ inline ArtMethod* GetResolvedMethod(ArtMethod* outer_method, // suspended while executing it. ScopedAssertNoThreadSuspension sants(__FUNCTION__); + if (inline_info.EncodesArtMethodAtDepth(encoding, inlining_depth)) { + return inline_info.GetArtMethodAtDepth(encoding, inlining_depth); + } + uint32_t method_index = inline_info.GetMethodIndexAtDepth(encoding, inlining_depth); - InvokeType invoke_type = static_cast<InvokeType>( - inline_info.GetInvokeTypeAtDepth(encoding, inlining_depth)); - ArtMethod* inlined_method = outer_method->GetDexCacheResolvedMethod(method_index, - kRuntimePointerSize); - if (!inlined_method->IsRuntimeMethod()) { + if (inline_info.GetDexPcAtDepth(encoding, inlining_depth) == static_cast<uint32_t>(-1)) { + // "charAt" special case. It is the only non-leaf method we inline across dex files. + ArtMethod* inlined_method = jni::DecodeArtMethod(WellKnownClasses::java_lang_String_charAt); + DCHECK_EQ(inlined_method->GetDexMethodIndex(), method_index); return inlined_method; } - // The method in the dex cache is the runtime method responsible for invoking - // the stub that will then update the dex cache. Therefore, we need to do the - // resolution ourselves. - - // We first find the dex cache of our caller. If it is the outer method, we can directly - // use its dex cache. Otherwise, we also need to resolve our caller. + // Find which method did the call in the inlining hierarchy. ArtMethod* caller = outer_method; if (inlining_depth != 0) { caller = GetResolvedMethod(outer_method, @@ -74,51 +72,38 @@ inline ArtMethod* GetResolvedMethod(ArtMethod* outer_method, encoding, inlining_depth - 1); } - DCHECK_EQ(caller->GetDexCache(), outer_method->GetDexCache()) - << "Compiler only supports inlining calls within the same dex cache"; - const DexFile* dex_file = outer_method->GetDexFile(); - const DexFile::MethodId& method_id = dex_file->GetMethodId(method_index); - if (inline_info.GetDexPcAtDepth(encoding, inlining_depth) == static_cast<uint32_t>(-1)) { - // "charAt" special case. It is the only non-leaf method we inline across dex files. - if (kIsDebugBuild) { - const char* name = dex_file->StringDataByIdx(method_id.name_idx_); - DCHECK_EQ(std::string(name), "charAt"); - DCHECK_EQ(std::string(dex_file->GetMethodShorty(method_id)), "CI") - << std::string(dex_file->GetMethodShorty(method_id)); - DCHECK_EQ(std::string(dex_file->StringByTypeIdx(method_id.class_idx_)), "Ljava/lang/String;") - << std::string(dex_file->StringByTypeIdx(method_id.class_idx_)); - } - mirror::Class* cls = - Runtime::Current()->GetClassLinker()->GetClassRoot(ClassLinker::kJavaLangString); - // Update the dex cache for future lookups. - caller->GetDexCache()->SetResolvedType(method_id.class_idx_, cls); - inlined_method = cls->FindVirtualMethod("charAt", "(I)C", kRuntimePointerSize); - } else { - mirror::Class* klass = caller->GetDexCache()->GetResolvedType(method_id.class_idx_); - DCHECK_EQ(klass->GetDexCache(), caller->GetDexCache()) - << "Compiler only supports inlining calls within the same dex cache"; - switch (invoke_type) { - case kDirect: - case kStatic: - inlined_method = - klass->FindDirectMethod(klass->GetDexCache(), method_index, kRuntimePointerSize); - break; - case kSuper: - case kVirtual: - inlined_method = - klass->FindVirtualMethod(klass->GetDexCache(), method_index, kRuntimePointerSize); - break; - default: - LOG(FATAL) << "Unimplemented inlined invocation type: " << invoke_type; - UNREACHABLE(); + // Lookup the declaring class of the inlined method. + const DexFile* dex_file = caller->GetDexFile(); + const DexFile::MethodId& method_id = dex_file->GetMethodId(method_index); + const char* descriptor = dex_file->StringByTypeIdx(method_id.class_idx_); + ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); + Thread* self = Thread::Current(); + mirror::ClassLoader* class_loader = caller->GetDeclaringClass()->GetClassLoader(); + mirror::Class* klass = class_linker->LookupClass(self, descriptor, class_loader); + if (klass == nullptr) { + LOG(FATAL) << "Could not find an inlined method from an .oat file: " + << "the class " << descriptor << " was not found in the class loader of " + << caller->PrettyMethod() << ". " + << "This must be due to playing wrongly with class loaders"; + } + + // Lookup the method. + const char* method_name = dex_file->GetMethodName(method_id); + const Signature signature = dex_file->GetMethodSignature(method_id); + + ArtMethod* inlined_method = + klass->FindDeclaredDirectMethod(method_name, signature, kRuntimePointerSize); + if (inlined_method == nullptr) { + inlined_method = klass->FindDeclaredVirtualMethod(method_name, signature, kRuntimePointerSize); + if (inlined_method == nullptr) { + LOG(FATAL) << "Could not find an inlined method from an .oat file: " + << "the class " << descriptor << " does not have " + << method_name << signature << " declared. " + << "This must be due to duplicate classes or playing wrongly with class loaders"; } } - // Update the dex cache for future lookups. Note that for static methods, this is safe - // when the class is being initialized, as the entrypoint for the ArtMethod is at - // this point still the resolution trampoline. - outer_method->SetDexCacheResolvedMethod(method_index, inlined_method, kRuntimePointerSize); return inlined_method; } @@ -256,10 +241,9 @@ inline mirror::Class* CheckArrayAlloc(dex::TypeIndex type_idx, *slow_path = true; return nullptr; // Failure } - ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); - PointerSize pointer_size = class_linker->GetImagePointerSize(); - mirror::Class* klass = method->GetDexCacheResolvedType<false>(type_idx, pointer_size); + mirror::Class* klass = method->GetDexCache()->GetResolvedType(type_idx); if (UNLIKELY(klass == nullptr)) { // Not in dex cache so try to resolve + ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); klass = class_linker->ResolveType(type_idx, method); *slow_path = true; if (klass == nullptr) { // Error diff --git a/runtime/entrypoints/entrypoint_utils.cc b/runtime/entrypoints/entrypoint_utils.cc index b17e1a8ab1..25fd727968 100644 --- a/runtime/entrypoints/entrypoint_utils.cc +++ b/runtime/entrypoints/entrypoint_utils.cc @@ -38,98 +38,13 @@ namespace art { -static inline mirror::Class* CheckFilledNewArrayAlloc(dex::TypeIndex type_idx, - int32_t component_count, - ArtMethod* referrer, - Thread* self, - bool access_check) - REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_) { - if (UNLIKELY(component_count < 0)) { - ThrowNegativeArraySizeException(component_count); - return nullptr; // Failure - } - ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); - PointerSize pointer_size = class_linker->GetImagePointerSize(); - mirror::Class* klass = referrer->GetDexCacheResolvedType<false>(type_idx, pointer_size); - if (UNLIKELY(klass == nullptr)) { // Not in dex cache so try to resolve - klass = class_linker->ResolveType(type_idx, referrer); - if (klass == nullptr) { // Error - DCHECK(self->IsExceptionPending()); - return nullptr; // Failure - } - } - if (UNLIKELY(klass->IsPrimitive() && !klass->IsPrimitiveInt())) { - if (klass->IsPrimitiveLong() || klass->IsPrimitiveDouble()) { - ThrowRuntimeException("Bad filled array request for type %s", - klass->PrettyDescriptor().c_str()); - } else { - self->ThrowNewExceptionF( - "Ljava/lang/InternalError;", - "Found type %s; filled-new-array not implemented for anything but 'int'", - klass->PrettyDescriptor().c_str()); - } - return nullptr; // Failure - } - if (access_check) { - mirror::Class* referrer_klass = referrer->GetDeclaringClass(); - if (UNLIKELY(!referrer_klass->CanAccess(klass))) { - ThrowIllegalAccessErrorClass(referrer_klass, klass); - return nullptr; // Failure - } - } - DCHECK(klass->IsArrayClass()) << klass->PrettyClass(); - return klass; -} - -// Helper function to allocate array for FILLED_NEW_ARRAY. -mirror::Array* CheckAndAllocArrayFromCode(dex::TypeIndex type_idx, - int32_t component_count, - ArtMethod* referrer, - Thread* self, - bool access_check, - gc::AllocatorType allocator_type ATTRIBUTE_UNUSED) { - mirror::Class* klass = CheckFilledNewArrayAlloc(type_idx, component_count, referrer, self, - access_check); - if (UNLIKELY(klass == nullptr)) { - return nullptr; - } - // Always go slow path for now, filled new array is not common. - gc::Heap* heap = Runtime::Current()->GetHeap(); - // Use the current allocator type in case CheckFilledNewArrayAlloc caused us to suspend and then - // the heap switched the allocator type while we were suspended. - return mirror::Array::Alloc<false>(self, klass, component_count, - klass->GetComponentSizeShift(), - heap->GetCurrentAllocator()); -} - -// Helper function to allocate array for FILLED_NEW_ARRAY. -mirror::Array* CheckAndAllocArrayFromCodeInstrumented( - dex::TypeIndex type_idx, - int32_t component_count, - ArtMethod* referrer, - Thread* self, - bool access_check, - gc::AllocatorType allocator_type ATTRIBUTE_UNUSED) { - mirror::Class* klass = CheckFilledNewArrayAlloc(type_idx, component_count, referrer, self, - access_check); - if (UNLIKELY(klass == nullptr)) { - return nullptr; - } - gc::Heap* heap = Runtime::Current()->GetHeap(); - // Use the current allocator type in case CheckFilledNewArrayAlloc caused us to suspend and then - // the heap switched the allocator type while we were suspended. - return mirror::Array::Alloc<true>(self, klass, component_count, - klass->GetComponentSizeShift(), - heap->GetCurrentAllocator()); -} - void CheckReferenceResult(Handle<mirror::Object> o, Thread* self) { if (o.Get() == nullptr) { return; } // Make sure that the result is an instance of the type this method was expected to return. ArtMethod* method = self->GetCurrentMethod(nullptr); - mirror::Class* return_type = method->GetReturnType(true /* resolve */, kRuntimePointerSize); + mirror::Class* return_type = method->GetReturnType(true /* resolve */); if (!o->InstanceOf(return_type)) { Runtime::Current()->GetJavaVM()->JniAbortF(nullptr, @@ -192,8 +107,7 @@ JValue InvokeProxyInvocationHandler(ScopedObjectAccessAlreadyRunnable& soa, cons ArtMethod* interface_method = soa.Decode<mirror::Method>(interface_method_jobj)->GetArtMethod(); // This can cause thread suspension. - PointerSize pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize(); - mirror::Class* result_type = interface_method->GetReturnType(true /* resolve */, pointer_size); + mirror::Class* result_type = interface_method->GetReturnType(true /* resolve */); ObjPtr<mirror::Object> result_ref = soa.Decode<mirror::Object>(result); JValue result_unboxed; if (!UnboxPrimitiveForResult(result_ref.Ptr(), result_type, &result_unboxed)) { diff --git a/runtime/entrypoints/entrypoint_utils.h b/runtime/entrypoints/entrypoint_utils.h index d4cf83c8de..b427c0717f 100644 --- a/runtime/entrypoints/entrypoint_utils.h +++ b/runtime/entrypoints/entrypoint_utils.h @@ -102,24 +102,6 @@ ALWAYS_INLINE inline mirror::Array* AllocArrayFromCodeResolved(mirror::Class* kl REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_); -mirror::Array* CheckAndAllocArrayFromCode(dex::TypeIndex type_idx, - int32_t component_count, - ArtMethod* method, - Thread* self, - bool access_check, - gc::AllocatorType allocator_type) - REQUIRES_SHARED(Locks::mutator_lock_) - REQUIRES(!Roles::uninterruptible_); - -mirror::Array* CheckAndAllocArrayFromCodeInstrumented(dex::TypeIndex type_idx, - int32_t component_count, - ArtMethod* method, - Thread* self, - bool access_check, - gc::AllocatorType allocator_type) - REQUIRES_SHARED(Locks::mutator_lock_) - REQUIRES(!Roles::uninterruptible_); - // Type of find field operation for fast and slow case. enum FindFieldType { InstanceObjectRead, diff --git a/runtime/entrypoints/quick/quick_alloc_entrypoints.cc b/runtime/entrypoints/quick/quick_alloc_entrypoints.cc index 2d06508069..23a99a06c6 100644 --- a/runtime/entrypoints/quick/quick_alloc_entrypoints.cc +++ b/runtime/entrypoints/quick/quick_alloc_entrypoints.cc @@ -109,46 +109,6 @@ extern "C" mirror::Array* artAllocArrayFromCodeWithAccessCheck##suffix##suffix2( self, \ allocator_type); \ } \ -extern "C" mirror::Array* artCheckAndAllocArrayFromCode##suffix##suffix2( \ - uint32_t type_idx, int32_t component_count, ArtMethod* method, Thread* self) \ - REQUIRES_SHARED(Locks::mutator_lock_) { \ - ScopedQuickEntrypointChecks sqec(self); \ - if (!(instrumented_bool)) { \ - return CheckAndAllocArrayFromCode(dex::TypeIndex(type_idx), \ - component_count, \ - method, \ - self, \ - false, \ - allocator_type); \ - } else { \ - return CheckAndAllocArrayFromCodeInstrumented(dex::TypeIndex(type_idx), \ - component_count, \ - method, \ - self, \ - false, \ - allocator_type); \ - } \ -} \ -extern "C" mirror::Array* artCheckAndAllocArrayFromCodeWithAccessCheck##suffix##suffix2( \ - uint32_t type_idx, int32_t component_count, ArtMethod* method, Thread* self) \ - REQUIRES_SHARED(Locks::mutator_lock_) { \ - ScopedQuickEntrypointChecks sqec(self); \ - if (!(instrumented_bool)) { \ - return CheckAndAllocArrayFromCode(dex::TypeIndex(type_idx), \ - component_count, \ - method, \ - self, \ - true, \ - allocator_type); \ - } else { \ - return CheckAndAllocArrayFromCodeInstrumented(dex::TypeIndex(type_idx), \ - component_count, \ - method, \ - self, \ - true, \ - allocator_type); \ - } \ -} \ extern "C" mirror::String* artAllocStringFromBytesFromCode##suffix##suffix2( \ mirror::ByteArray* byte_array, int32_t high, int32_t offset, int32_t byte_count, \ Thread* self) \ @@ -219,8 +179,6 @@ void SetQuickAllocEntryPoints##suffix(QuickEntryPoints* qpoints, bool instrument qpoints->pAllocObjectResolved = art_quick_alloc_object_resolved##suffix##_instrumented; \ qpoints->pAllocObjectInitialized = art_quick_alloc_object_initialized##suffix##_instrumented; \ qpoints->pAllocObjectWithChecks = art_quick_alloc_object_with_checks##suffix##_instrumented; \ - qpoints->pCheckAndAllocArray = art_quick_check_and_alloc_array##suffix##_instrumented; \ - qpoints->pCheckAndAllocArrayWithAccessCheck = art_quick_check_and_alloc_array_with_access_check##suffix##_instrumented; \ qpoints->pAllocStringFromBytes = art_quick_alloc_string_from_bytes##suffix##_instrumented; \ qpoints->pAllocStringFromChars = art_quick_alloc_string_from_chars##suffix##_instrumented; \ qpoints->pAllocStringFromString = art_quick_alloc_string_from_string##suffix##_instrumented; \ @@ -231,8 +189,6 @@ void SetQuickAllocEntryPoints##suffix(QuickEntryPoints* qpoints, bool instrument qpoints->pAllocObjectResolved = art_quick_alloc_object_resolved##suffix; \ qpoints->pAllocObjectInitialized = art_quick_alloc_object_initialized##suffix; \ qpoints->pAllocObjectWithChecks = art_quick_alloc_object_with_checks##suffix; \ - qpoints->pCheckAndAllocArray = art_quick_check_and_alloc_array##suffix; \ - qpoints->pCheckAndAllocArrayWithAccessCheck = art_quick_check_and_alloc_array_with_access_check##suffix; \ qpoints->pAllocStringFromBytes = art_quick_alloc_string_from_bytes##suffix; \ qpoints->pAllocStringFromChars = art_quick_alloc_string_from_chars##suffix; \ qpoints->pAllocStringFromString = art_quick_alloc_string_from_string##suffix; \ diff --git a/runtime/entrypoints/quick/quick_default_init_entrypoints.h b/runtime/entrypoints/quick/quick_default_init_entrypoints.h index 8ce61c1021..6481b97ae1 100644 --- a/runtime/entrypoints/quick/quick_default_init_entrypoints.h +++ b/runtime/entrypoints/quick/quick_default_init_entrypoints.h @@ -66,10 +66,7 @@ void DefaultInitEntryPoints(JniEntryPoints* jpoints, QuickEntryPoints* qpoints) qpoints->pGetObjStatic = art_quick_get_obj_static; // Array - qpoints->pAputObjectWithNullAndBoundCheck = art_quick_aput_obj_with_null_and_bound_check; - qpoints->pAputObjectWithBoundCheck = art_quick_aput_obj_with_bound_check; qpoints->pAputObject = art_quick_aput_obj; - qpoints->pHandleFillArrayData = art_quick_handle_fill_data; // JNI qpoints->pJniMethodStart = JniMethodStart; diff --git a/runtime/entrypoints/quick/quick_entrypoints_list.h b/runtime/entrypoints/quick/quick_entrypoints_list.h index 4d5d6de41d..0e75e94d52 100644 --- a/runtime/entrypoints/quick/quick_entrypoints_list.h +++ b/runtime/entrypoints/quick/quick_entrypoints_list.h @@ -26,8 +26,6 @@ V(AllocObjectResolved, void*, mirror::Class*) \ V(AllocObjectInitialized, void*, mirror::Class*) \ V(AllocObjectWithChecks, void*, mirror::Class*) \ - V(CheckAndAllocArray, void*, uint32_t, int32_t, ArtMethod*) \ - V(CheckAndAllocArrayWithAccessCheck, void*, uint32_t, int32_t, ArtMethod*) \ V(AllocStringFromBytes, void*, void*, int32_t, int32_t, int32_t) \ V(AllocStringFromChars, void*, int32_t, int32_t, void*) \ V(AllocStringFromString, void*, void*) \ @@ -65,10 +63,7 @@ V(GetObjInstance, void*, uint32_t, void*) \ V(GetObjStatic, void*, uint32_t) \ \ - V(AputObjectWithNullAndBoundCheck, void, mirror::Array*, int32_t, mirror::Object*) \ - V(AputObjectWithBoundCheck, void, mirror::Array*, int32_t, mirror::Object*) \ V(AputObject, void, mirror::Array*, int32_t, mirror::Object*) \ - V(HandleFillArrayData, void, void*, void*) \ \ V(JniMethodStart, uint32_t, Thread*) \ V(JniMethodFastStart, uint32_t, Thread*) \ diff --git a/runtime/entrypoints_order_test.cc b/runtime/entrypoints_order_test.cc index 96e17daa08..b9caf0f555 100644 --- a/runtime/entrypoints_order_test.cc +++ b/runtime/entrypoints_order_test.cc @@ -117,15 +117,14 @@ class EntrypointsOrderTest : public CommonRuntimeTest { sizeof(void*)); EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, checkpoint_function, active_suspend_barriers, sizeof(void*)); - EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, active_suspend_barriers, jni_entrypoints, + EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, active_suspend_barriers, thread_local_start, sizeof(Thread::tls_ptr_sized_values::active_suspend_barriers)); - - // Skip across the entrypoints structures. + EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, thread_local_start, thread_local_pos, sizeof(void*)); EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, thread_local_pos, thread_local_end, sizeof(void*)); - EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, thread_local_end, thread_local_start, sizeof(void*)); - EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, thread_local_start, thread_local_objects, sizeof(void*)); + EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, thread_local_end, thread_local_objects, sizeof(void*)); + EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, thread_local_objects, jni_entrypoints, sizeof(size_t)); - EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, thread_local_objects, mterp_current_ibase, sizeof(size_t)); + // Skip across the entrypoints structures. EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, mterp_current_ibase, mterp_default_ibase, sizeof(void*)); EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, mterp_default_ibase, mterp_alt_ibase, sizeof(void*)); EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, mterp_alt_ibase, rosalloc_runs, sizeof(void*)); @@ -162,12 +161,8 @@ class EntrypointsOrderTest : public CommonRuntimeTest { sizeof(void*)); EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pAllocObjectInitialized, pAllocObjectWithChecks, sizeof(void*)); - EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pAllocObjectWithChecks, pCheckAndAllocArray, + EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pAllocObjectWithChecks, pAllocStringFromBytes, sizeof(void*)); - EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pCheckAndAllocArray, pCheckAndAllocArrayWithAccessCheck, - sizeof(void*)); - EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pCheckAndAllocArrayWithAccessCheck, - pAllocStringFromBytes, sizeof(void*)); EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pAllocStringFromBytes, pAllocStringFromChars, sizeof(void*)); EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pAllocStringFromChars, pAllocStringFromString, @@ -206,13 +201,8 @@ class EntrypointsOrderTest : public CommonRuntimeTest { EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pGet64Instance, pGet64Static, sizeof(void*)); EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pGet64Static, pGetObjInstance, sizeof(void*)); EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pGetObjInstance, pGetObjStatic, sizeof(void*)); - EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pGetObjStatic, pAputObjectWithNullAndBoundCheck, - sizeof(void*)); - EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pAputObjectWithNullAndBoundCheck, - pAputObjectWithBoundCheck, sizeof(void*)); - EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pAputObjectWithBoundCheck, pAputObject, sizeof(void*)); - EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pAputObject, pHandleFillArrayData, sizeof(void*)); - EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pHandleFillArrayData, pJniMethodStart, sizeof(void*)); + EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pGetObjStatic, pAputObject, sizeof(void*)); + EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pAputObject, pJniMethodStart, sizeof(void*)); EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pJniMethodStart, pJniMethodFastStart, sizeof(void*)); EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pJniMethodFastStart, pJniMethodStartSynchronized, diff --git a/runtime/interpreter/interpreter_common.cc b/runtime/interpreter/interpreter_common.cc index ca26207093..76777d938b 100644 --- a/runtime/interpreter/interpreter_common.cc +++ b/runtime/interpreter/interpreter_common.cc @@ -751,16 +751,14 @@ static inline bool DoCallCommon(ArtMethod* called_method, case 'L': { ObjPtr<mirror::Object> o = shadow_frame.GetVRegReference(src_reg); if (do_assignability_check && o != nullptr) { - PointerSize pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize(); const dex::TypeIndex type_idx = params->GetTypeItem(shorty_pos).type_idx_; - ObjPtr<mirror::Class> arg_type = method->GetDexCacheResolvedType(type_idx, - pointer_size); + ObjPtr<mirror::Class> arg_type = method->GetDexCache()->GetResolvedType(type_idx); if (arg_type == nullptr) { StackHandleScope<1> hs(self); // Preserve o since it is used below and GetClassFromTypeIndex may cause thread // suspension. HandleWrapperObjPtr<mirror::Object> h = hs.NewHandleWrapper(&o); - arg_type = method->GetClassFromTypeIndex(type_idx, true /* resolve */, pointer_size); + arg_type = method->GetClassFromTypeIndex(type_idx, true /* resolve */); if (arg_type == nullptr) { CHECK(self->IsExceptionPending()); return false; diff --git a/runtime/interpreter/interpreter_switch_impl.cc b/runtime/interpreter/interpreter_switch_impl.cc index d7dfcd4408..a77a3fc2b3 100644 --- a/runtime/interpreter/interpreter_switch_impl.cc +++ b/runtime/interpreter/interpreter_switch_impl.cc @@ -285,9 +285,7 @@ JValue ExecuteSwitchImpl(Thread* self, const DexFile::CodeItem* code_item, const size_t ref_idx = inst->VRegA_11x(inst_data); ObjPtr<mirror::Object> obj_result = shadow_frame.GetVRegReference(ref_idx); if (do_assignability_check && obj_result != nullptr) { - PointerSize pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize(); - ObjPtr<mirror::Class> return_type = method->GetReturnType(true /* resolve */, - pointer_size); + ObjPtr<mirror::Class> return_type = method->GetReturnType(true /* resolve */); // Re-load since it might have moved. obj_result = shadow_frame.GetVRegReference(ref_idx); if (return_type == nullptr) { diff --git a/runtime/mirror/dex_cache_test.cc b/runtime/mirror/dex_cache_test.cc index 916f1cf1ef..8f978e122c 100644 --- a/runtime/mirror/dex_cache_test.cc +++ b/runtime/mirror/dex_cache_test.cc @@ -35,7 +35,6 @@ class DexCacheMethodHandlesTest : public DexCacheTest { protected: virtual void SetUpRuntimeOptions(RuntimeOptions* options) OVERRIDE { CommonRuntimeTest::SetUpRuntimeOptions(options); - options->push_back(std::make_pair("-Xexperimental:method-handles", nullptr)); } }; diff --git a/runtime/mirror/emulated_stack_frame.cc b/runtime/mirror/emulated_stack_frame.cc index d607040486..978cc32320 100644 --- a/runtime/mirror/emulated_stack_frame.cc +++ b/runtime/mirror/emulated_stack_frame.cc @@ -195,6 +195,7 @@ mirror::EmulatedStackFrame* EmulatedStackFrame::CreateFromShadowFrameAndArgs( // Step 5: Construct the EmulatedStackFrame object. Handle<EmulatedStackFrame> sf(hs.NewHandle( ObjPtr<EmulatedStackFrame>::DownCast(StaticClass()->AllocObject(self)))); + sf->SetFieldObject<false>(CallsiteTypeOffset(), caller_type.Get()); sf->SetFieldObject<false>(TypeOffset(), callee_type.Get()); sf->SetFieldObject<false>(ReferencesOffset(), references.Get()); sf->SetFieldObject<false>(StackFrameOffset(), stack_frame.Get()); diff --git a/runtime/mirror/emulated_stack_frame.h b/runtime/mirror/emulated_stack_frame.h index d83a53652b..ddd84a167d 100644 --- a/runtime/mirror/emulated_stack_frame.h +++ b/runtime/mirror/emulated_stack_frame.h @@ -81,6 +81,10 @@ class MANAGED EmulatedStackFrame : public Object { OFFSET_OF_OBJECT_MEMBER(EmulatedStackFrame, stack_frame_)); } + static MemberOffset CallsiteTypeOffset() { + return MemberOffset(OFFSETOF_MEMBER(EmulatedStackFrame, callsite_type_)); + } + static MemberOffset TypeOffset() { return MemberOffset(OFFSETOF_MEMBER(EmulatedStackFrame, type_)); } @@ -93,6 +97,7 @@ class MANAGED EmulatedStackFrame : public Object { return MemberOffset(OFFSETOF_MEMBER(EmulatedStackFrame, stack_frame_)); } + HeapReference<mirror::MethodType> callsite_type_; HeapReference<mirror::ObjectArray<mirror::Object>> references_; HeapReference<mirror::ByteArray> stack_frame_; HeapReference<mirror::MethodType> type_; diff --git a/runtime/mirror/object_test.cc b/runtime/mirror/object_test.cc index a6f56aee42..6a4ec9dca7 100644 --- a/runtime/mirror/object_test.cc +++ b/runtime/mirror/object_test.cc @@ -306,23 +306,6 @@ TEST_F(ObjectTest, PrimitiveArray_Float_Alloc) { } -TEST_F(ObjectTest, CheckAndAllocArrayFromCode) { - // pretend we are trying to call 'new char[3]' from String.toCharArray - ScopedObjectAccess soa(Thread::Current()); - Class* java_util_Arrays = class_linker_->FindSystemClass(soa.Self(), "Ljava/util/Arrays;"); - ArtMethod* sort = java_util_Arrays->FindDirectMethod("sort", "([I)V", kRuntimePointerSize); - const DexFile::TypeId* type_id = java_lang_dex_file_->FindTypeId("[I"); - ASSERT_TRUE(type_id != nullptr); - dex::TypeIndex type_idx = java_lang_dex_file_->GetIndexForTypeId(*type_id); - Object* array = CheckAndAllocArrayFromCodeInstrumented( - type_idx, 3, sort, Thread::Current(), false, - Runtime::Current()->GetHeap()->GetCurrentAllocator()); - EXPECT_TRUE(array->IsArrayInstance()); - EXPECT_EQ(3, array->AsArray()->GetLength()); - EXPECT_TRUE(array->GetClass()->IsArrayClass()); - EXPECT_TRUE(array->GetClass()->GetComponentType()->IsPrimitive()); -} - TEST_F(ObjectTest, CreateMultiArray) { ScopedObjectAccess soa(Thread::Current()); diff --git a/runtime/native/java_lang_Class.cc b/runtime/native/java_lang_Class.cc index 3341f531e4..5438a6ddb4 100644 --- a/runtime/native/java_lang_Class.cc +++ b/runtime/native/java_lang_Class.cc @@ -428,6 +428,10 @@ static jobjectArray Class_getDeclaredMethodsUnchecked(JNIEnv* env, jobject javaT } auto ret = hs.NewHandle(mirror::ObjectArray<mirror::Method>::Alloc( soa.Self(), mirror::Method::ArrayClass(), num_methods)); + if (ret.Get() == nullptr) { + soa.Self()->AssertPendingOOMException(); + return nullptr; + } num_methods = 0; for (auto& m : klass->GetDeclaredMethods(kRuntimePointerSize)) { auto modifiers = m.GetAccessFlags(); diff --git a/runtime/oat.h b/runtime/oat.h index f2d457c2ee..90a9c150b1 100644 --- a/runtime/oat.h +++ b/runtime/oat.h @@ -32,7 +32,7 @@ class InstructionSetFeatures; class PACKED(4) OatHeader { public: static constexpr uint8_t kOatMagic[] = { 'o', 'a', 't', '\n' }; - static constexpr uint8_t kOatVersion[] = { '0', '9', '7', '\0' }; // HLoadClass/kBssEntry change + static constexpr uint8_t kOatVersion[] = { '0', '9', '9', '\0' }; // Remove old entrypoints static constexpr const char* kImageLocationKey = "image-location"; static constexpr const char* kDex2OatCmdLineKey = "dex2oat-cmdline"; diff --git a/runtime/openjdkjvmti/Android.bp b/runtime/openjdkjvmti/Android.bp index 4bd21b4c2f..af027f6ba6 100644 --- a/runtime/openjdkjvmti/Android.bp +++ b/runtime/openjdkjvmti/Android.bp @@ -30,6 +30,8 @@ cc_defaults { "ti_stack.cc", "ti_redefine.cc", "ti_thread.cc", + "ti_threadgroup.cc", + "ti_timers.cc", "transform.cc"], include_dirs: ["art/runtime"], shared_libs: [ diff --git a/runtime/openjdkjvmti/OpenjdkJvmTi.cc b/runtime/openjdkjvmti/OpenjdkJvmTi.cc index 8674e6cae7..d9aea01ef8 100644 --- a/runtime/openjdkjvmti/OpenjdkJvmTi.cc +++ b/runtime/openjdkjvmti/OpenjdkJvmTi.cc @@ -30,6 +30,7 @@ */ #include <string> +#include <type_traits> #include <vector> #include <jni.h> @@ -37,6 +38,7 @@ #include "openjdkjvmti/jvmti.h" #include "art_jvmti.h" +#include "base/logging.h" #include "base/mutex.h" #include "events-inl.h" #include "jni_env_ext-inl.h" @@ -56,6 +58,8 @@ #include "ti_redefine.h" #include "ti_stack.h" #include "ti_thread.h" +#include "ti_threadgroup.h" +#include "ti_timers.h" #include "transform.h" // TODO Remove this at some point by annotating all the methods. It was put in to make the skeleton @@ -202,13 +206,13 @@ class JvmtiFunctions { static jvmtiError GetTopThreadGroups(jvmtiEnv* env, jint* group_count_ptr, jthreadGroup** groups_ptr) { - return ERR(NOT_IMPLEMENTED); + return ThreadGroupUtil::GetTopThreadGroups(env, group_count_ptr, groups_ptr); } static jvmtiError GetThreadGroupInfo(jvmtiEnv* env, jthreadGroup group, jvmtiThreadGroupInfo* info_ptr) { - return ERR(NOT_IMPLEMENTED); + return ThreadGroupUtil::GetThreadGroupInfo(env, group, info_ptr); } static jvmtiError GetThreadGroupChildren(jvmtiEnv* env, @@ -217,7 +221,12 @@ class JvmtiFunctions { jthread** threads_ptr, jint* group_count_ptr, jthreadGroup** groups_ptr) { - return ERR(NOT_IMPLEMENTED); + return ThreadGroupUtil::GetThreadGroupChildren(env, + group, + thread_count_ptr, + threads_ptr, + group_count_ptr, + groups_ptr); } static jvmtiError GetStackTrace(jvmtiEnv* env, @@ -535,7 +544,7 @@ class JvmtiFunctions { jobject initiating_loader, jint* class_count_ptr, jclass** classes_ptr) { - return ERR(NOT_IMPLEMENTED); + return ClassUtil::GetClassLoaderClasses(env, initiating_loader, class_count_ptr, classes_ptr); } static jvmtiError GetClassSignature(jvmtiEnv* env, @@ -1031,15 +1040,15 @@ class JvmtiFunctions { } static jvmtiError GetTimerInfo(jvmtiEnv* env, jvmtiTimerInfo* info_ptr) { - return ERR(NOT_IMPLEMENTED); + return TimerUtil::GetTimerInfo(env, info_ptr); } static jvmtiError GetTime(jvmtiEnv* env, jlong* nanos_ptr) { - return ERR(NOT_IMPLEMENTED); + return TimerUtil::GetTime(env, nanos_ptr); } static jvmtiError GetAvailableProcessors(jvmtiEnv* env, jint* processor_count_ptr) { - return ERR(NOT_IMPLEMENTED); + return TimerUtil::GetAvailableProcessors(env, processor_count_ptr); } static jvmtiError AddToBootstrapClassLoaderSearch(jvmtiEnv* env, const char* segment) { @@ -1168,7 +1177,57 @@ class JvmtiFunctions { } static jvmtiError SetVerboseFlag(jvmtiEnv* env, jvmtiVerboseFlag flag, jboolean value) { - return ERR(NOT_IMPLEMENTED); + if (flag == jvmtiVerboseFlag::JVMTI_VERBOSE_OTHER) { + // OTHER is special, as it's 0, so can't do a bit check. + bool val = (value == JNI_TRUE) ? true : false; + + art::gLogVerbosity.collector = val; + art::gLogVerbosity.compiler = val; + art::gLogVerbosity.deopt = val; + art::gLogVerbosity.heap = val; + art::gLogVerbosity.jdwp = val; + art::gLogVerbosity.jit = val; + art::gLogVerbosity.monitor = val; + art::gLogVerbosity.oat = val; + art::gLogVerbosity.profiler = val; + art::gLogVerbosity.signals = val; + art::gLogVerbosity.simulator = val; + art::gLogVerbosity.startup = val; + art::gLogVerbosity.third_party_jni = val; + art::gLogVerbosity.threads = val; + art::gLogVerbosity.verifier = val; + art::gLogVerbosity.image = val; + + // Note: can't switch systrace_lock_logging. That requires changing entrypoints. + + art::gLogVerbosity.agents = val; + } else { + // Spec isn't clear whether "flag" is a mask or supposed to be single. We implement the mask + // semantics. + constexpr std::underlying_type<jvmtiVerboseFlag>::type kMask = + jvmtiVerboseFlag::JVMTI_VERBOSE_GC | + jvmtiVerboseFlag::JVMTI_VERBOSE_CLASS | + jvmtiVerboseFlag::JVMTI_VERBOSE_JNI; + if ((flag & ~kMask) != 0) { + return ERR(ILLEGAL_ARGUMENT); + } + + bool val = (value == JNI_TRUE) ? true : false; + + if ((flag & jvmtiVerboseFlag::JVMTI_VERBOSE_GC) != 0) { + art::gLogVerbosity.gc = val; + } + + if ((flag & jvmtiVerboseFlag::JVMTI_VERBOSE_CLASS) != 0) { + art::gLogVerbosity.class_linker = val; + } + + if ((flag & jvmtiVerboseFlag::JVMTI_VERBOSE_JNI) != 0) { + art::gLogVerbosity.jni = val; + } + } + + return ERR(NONE); } static jvmtiError GetJLocationFormat(jvmtiEnv* env, jvmtiJlocationFormat* format_ptr) { diff --git a/runtime/openjdkjvmti/ti_class.cc b/runtime/openjdkjvmti/ti_class.cc index 0d1704ca4d..d1324bc13f 100644 --- a/runtime/openjdkjvmti/ti_class.cc +++ b/runtime/openjdkjvmti/ti_class.cc @@ -32,7 +32,10 @@ #include "ti_class.h" #include "art_jvmti.h" +#include "class_table-inl.h" +#include "class_linker.h" #include "jni_internal.h" +#include "runtime.h" #include "scoped_thread_state_change-inl.h" #include "thread-inl.h" @@ -328,4 +331,90 @@ jvmtiError ClassUtil::GetClassLoader(jvmtiEnv* env ATTRIBUTE_UNUSED, return ERR(NONE); } +jvmtiError ClassUtil::GetClassLoaderClasses(jvmtiEnv* env, + jobject initiating_loader, + jint* class_count_ptr, + jclass** classes_ptr) { + UNUSED(env, initiating_loader, class_count_ptr, classes_ptr); + + if (class_count_ptr == nullptr || classes_ptr == nullptr) { + return ERR(NULL_POINTER); + } + art::Thread* self = art::Thread::Current(); + if (!self->GetJniEnv()->IsInstanceOf(initiating_loader, + art::WellKnownClasses::java_lang_ClassLoader)) { + return ERR(ILLEGAL_ARGUMENT); + } + if (self->GetJniEnv()->IsInstanceOf(initiating_loader, + art::WellKnownClasses::java_lang_BootClassLoader)) { + // Need to use null for the BootClassLoader. + initiating_loader = nullptr; + } + + art::ScopedObjectAccess soa(self); + art::ObjPtr<art::mirror::ClassLoader> class_loader = + soa.Decode<art::mirror::ClassLoader>(initiating_loader); + + art::ClassLinker* class_linker = art::Runtime::Current()->GetClassLinker(); + + art::ReaderMutexLock mu(self, *art::Locks::classlinker_classes_lock_); + + art::ClassTable* class_table = class_linker->ClassTableForClassLoader(class_loader); + if (class_table == nullptr) { + // Nothing loaded. + *class_count_ptr = 0; + *classes_ptr = nullptr; + return ERR(NONE); + } + + struct ClassTableCount { + bool operator()(art::ObjPtr<art::mirror::Class> klass) { + DCHECK(klass != nullptr); + ++count; + return true; + } + + size_t count = 0; + }; + ClassTableCount ctc; + class_table->Visit(ctc); + + if (ctc.count == 0) { + // Nothing loaded. + *class_count_ptr = 0; + *classes_ptr = nullptr; + return ERR(NONE); + } + + unsigned char* data; + jvmtiError data_result = env->Allocate(ctc.count * sizeof(jclass), &data); + if (data_result != ERR(NONE)) { + return data_result; + } + jclass* class_array = reinterpret_cast<jclass*>(data); + + struct ClassTableFill { + bool operator()(art::ObjPtr<art::mirror::Class> klass) + REQUIRES_SHARED(art::Locks::mutator_lock_) { + DCHECK(klass != nullptr); + DCHECK_LT(count, ctc_ref.count); + local_class_array[count++] = soa_ptr->AddLocalReference<jclass>(klass); + return true; + } + + jclass* local_class_array; + const ClassTableCount& ctc_ref; + art::ScopedObjectAccess* soa_ptr; + size_t count; + }; + ClassTableFill ctf = { class_array, ctc, &soa, 0 }; + class_table->Visit(ctf); + DCHECK_EQ(ctc.count, ctf.count); + + *class_count_ptr = ctc.count; + *classes_ptr = class_array; + + return ERR(NONE); +} + } // namespace openjdkjvmti diff --git a/runtime/openjdkjvmti/ti_class.h b/runtime/openjdkjvmti/ti_class.h index 577fc8e866..7a0fafbc83 100644 --- a/runtime/openjdkjvmti/ti_class.h +++ b/runtime/openjdkjvmti/ti_class.h @@ -65,6 +65,11 @@ class ClassUtil { static jvmtiError GetClassLoader(jvmtiEnv* env, jclass klass, jobject* classloader_ptr); + static jvmtiError GetClassLoaderClasses(jvmtiEnv* env, + jobject initiating_loader, + jint* class_count_ptr, + jclass** classes_ptr); + static jvmtiError IsInterface(jvmtiEnv* env, jclass klass, jboolean* is_interface_ptr); static jvmtiError IsArrayClass(jvmtiEnv* env, jclass klass, jboolean* is_array_class_ptr); }; diff --git a/runtime/openjdkjvmti/ti_thread.cc b/runtime/openjdkjvmti/ti_thread.cc index f7f63bd19c..2bcdd8cda1 100644 --- a/runtime/openjdkjvmti/ti_thread.cc +++ b/runtime/openjdkjvmti/ti_thread.cc @@ -35,11 +35,14 @@ #include "art_jvmti.h" #include "base/logging.h" #include "base/mutex.h" +#include "gc/system_weak.h" +#include "gc_root-inl.h" #include "jni_internal.h" #include "mirror/class.h" #include "mirror/object-inl.h" #include "mirror/string.h" #include "obj_ptr.h" +#include "runtime.h" #include "scoped_thread_state_change-inl.h" #include "thread-inl.h" #include "thread_list.h" @@ -400,7 +403,43 @@ jvmtiError ThreadUtil::GetAllThreads(jvmtiEnv* env, *threads_count_ptr = static_cast<jint>(peers.size()); *threads_ptr = threads; } + return ERR(NONE); +} + +jvmtiError ThreadUtil::SetThreadLocalStorage(jvmtiEnv* env ATTRIBUTE_UNUSED, + jthread thread, + const void* data) { + art::ScopedObjectAccess soa(art::Thread::Current()); + art::Thread* self = GetNativeThread(thread, soa); + if (self == nullptr && thread == nullptr) { + return ERR(INVALID_THREAD); + } + if (self == nullptr) { + return ERR(THREAD_NOT_ALIVE); + } + + self->SetCustomTLS(data); + + return ERR(NONE); +} + +jvmtiError ThreadUtil::GetThreadLocalStorage(jvmtiEnv* env ATTRIBUTE_UNUSED, + jthread thread, + void** data_ptr) { + if (data_ptr == nullptr) { + return ERR(NULL_POINTER); + } + + art::ScopedObjectAccess soa(art::Thread::Current()); + art::Thread* self = GetNativeThread(thread, soa); + if (self == nullptr && thread == nullptr) { + return ERR(INVALID_THREAD); + } + if (self == nullptr) { + return ERR(THREAD_NOT_ALIVE); + } + *data_ptr = const_cast<void*>(self->GetCustomTLS()); return ERR(NONE); } diff --git a/runtime/openjdkjvmti/ti_thread.h b/runtime/openjdkjvmti/ti_thread.h index fca42ad2b5..290e9d49b2 100644 --- a/runtime/openjdkjvmti/ti_thread.h +++ b/runtime/openjdkjvmti/ti_thread.h @@ -46,6 +46,9 @@ class ThreadUtil { static jvmtiError GetThreadInfo(jvmtiEnv* env, jthread thread, jvmtiThreadInfo* info_ptr); static jvmtiError GetThreadState(jvmtiEnv* env, jthread thread, jint* thread_state_ptr); + + static jvmtiError SetThreadLocalStorage(jvmtiEnv* env, jthread thread, const void* data); + static jvmtiError GetThreadLocalStorage(jvmtiEnv* env, jthread thread, void** data_ptr); }; } // namespace openjdkjvmti diff --git a/runtime/openjdkjvmti/ti_threadgroup.cc b/runtime/openjdkjvmti/ti_threadgroup.cc new file mode 100644 index 0000000000..35b1bfd920 --- /dev/null +++ b/runtime/openjdkjvmti/ti_threadgroup.cc @@ -0,0 +1,285 @@ +/* Copyright (C) 2017 The Android Open Source Project + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This file implements interfaces from the file jvmti.h. This implementation + * is licensed under the same terms as the file jvmti.h. The + * copyright and license information for the file jvmti.h follows. + * + * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#include "ti_threadgroup.h" + +#include "art_field.h" +#include "art_jvmti.h" +#include "base/logging.h" +#include "base/macros.h" +#include "base/mutex.h" +#include "handle_scope-inl.h" +#include "jni_internal.h" +#include "mirror/class.h" +#include "mirror/object-inl.h" +#include "mirror/string.h" +#include "obj_ptr.h" +#include "object_lock.h" +#include "runtime.h" +#include "scoped_thread_state_change-inl.h" +#include "thread-inl.h" +#include "thread_list.h" +#include "well_known_classes.h" + +namespace openjdkjvmti { + + +jvmtiError ThreadGroupUtil::GetTopThreadGroups(jvmtiEnv* env, + jint* group_count_ptr, + jthreadGroup** groups_ptr) { + // We only have a single top group. So we can take the current thread and move upwards. + if (group_count_ptr == nullptr || groups_ptr == nullptr) { + return ERR(NULL_POINTER); + } + + art::Runtime* runtime = art::Runtime::Current(); + if (runtime == nullptr) { + // Must be starting the runtime, or dying. + return ERR(WRONG_PHASE); + } + + jobject sys_thread_group = runtime->GetSystemThreadGroup(); + if (sys_thread_group == nullptr) { + // Seems we're still starting up. + return ERR(WRONG_PHASE); + } + + unsigned char* data; + jvmtiError result = env->Allocate(sizeof(jthreadGroup), &data); + if (result != ERR(NONE)) { + return result; + } + + jthreadGroup* groups = reinterpret_cast<jthreadGroup*>(data); + *groups = + reinterpret_cast<JNIEnv*>(art::Thread::Current()->GetJniEnv())->NewLocalRef(sys_thread_group); + *groups_ptr = groups; + *group_count_ptr = 1; + + return ERR(NONE); +} + +jvmtiError ThreadGroupUtil::GetThreadGroupInfo(jvmtiEnv* env, + jthreadGroup group, + jvmtiThreadGroupInfo* info_ptr) { + if (group == nullptr) { + return ERR(INVALID_THREAD_GROUP); + } + + art::ScopedObjectAccess soa(art::Thread::Current()); + if (soa.Env()->IsInstanceOf(group, art::WellKnownClasses::java_lang_ThreadGroup) == JNI_FALSE) { + return ERR(INVALID_THREAD_GROUP); + } + + art::ObjPtr<art::mirror::Object> obj = soa.Decode<art::mirror::Object>(group); + + // Do the name first. It's the only thing that can fail. + { + art::ArtField* name_field = + art::jni::DecodeArtField(art::WellKnownClasses::java_lang_ThreadGroup_name); + CHECK(name_field != nullptr); + art::ObjPtr<art::mirror::String> name_obj = + art::ObjPtr<art::mirror::String>::DownCast(name_field->GetObject(obj)); + std::string tmp_str; + const char* tmp_cstr; + if (name_obj == nullptr) { + tmp_cstr = ""; + } else { + tmp_str = name_obj->ToModifiedUtf8(); + tmp_cstr = tmp_str.c_str(); + } + jvmtiError result = + CopyString(env, tmp_cstr, reinterpret_cast<unsigned char**>(&info_ptr->name)); + if (result != ERR(NONE)) { + return result; + } + } + + // Parent. + { + art::ArtField* parent_field = + art::jni::DecodeArtField(art::WellKnownClasses::java_lang_ThreadGroup_parent); + CHECK(parent_field != nullptr); + art::ObjPtr<art::mirror::Object> parent_group = parent_field->GetObject(obj); + info_ptr->parent = parent_group == nullptr + ? nullptr + : soa.AddLocalReference<jthreadGroup>(parent_group); + } + + // Max priority. + { + art::ArtField* prio_field = obj->GetClass()->FindDeclaredInstanceField("maxPriority", "I"); + CHECK(prio_field != nullptr); + info_ptr->max_priority = static_cast<jint>(prio_field->GetInt(obj)); + } + + // Daemon. + { + art::ArtField* daemon_field = obj->GetClass()->FindDeclaredInstanceField("daemon", "Z"); + CHECK(daemon_field != nullptr); + info_ptr->is_daemon = daemon_field->GetBoolean(obj) == 0 ? JNI_FALSE : JNI_TRUE; + } + + return ERR(NONE); +} + + +static bool IsInDesiredThreadGroup(art::Handle<art::mirror::Object> desired_thread_group, + art::ObjPtr<art::mirror::Object> peer) + REQUIRES_SHARED(art::Locks::mutator_lock_) { + CHECK(desired_thread_group.Get() != nullptr); + + art::ArtField* thread_group_field = + art::jni::DecodeArtField(art::WellKnownClasses::java_lang_Thread_group); + DCHECK(thread_group_field != nullptr); + art::ObjPtr<art::mirror::Object> group = thread_group_field->GetObject(peer); + return (group == desired_thread_group.Get()); +} + +static void GetThreads(art::Handle<art::mirror::Object> thread_group, + std::vector<art::ObjPtr<art::mirror::Object>>* thread_peers) + REQUIRES_SHARED(art::Locks::mutator_lock_) REQUIRES(!art::Locks::thread_list_lock_) { + CHECK(thread_group.Get() != nullptr); + + art::MutexLock mu(art::Thread::Current(), *art::Locks::thread_list_lock_); + for (art::Thread* t : art::Runtime::Current()->GetThreadList()->GetList()) { + if (t->IsStillStarting()) { + continue; + } + art::ObjPtr<art::mirror::Object> peer = t->GetPeer(); + if (peer == nullptr) { + continue; + } + if (IsInDesiredThreadGroup(thread_group, peer)) { + thread_peers->push_back(peer); + } + } +} + +static void GetChildThreadGroups(art::Handle<art::mirror::Object> thread_group, + std::vector<art::ObjPtr<art::mirror::Object>>* thread_groups) + REQUIRES_SHARED(art::Locks::mutator_lock_) { + CHECK(thread_group.Get() != nullptr); + + // Get the ThreadGroup[] "groups" out of this thread group... + art::ArtField* groups_field = + art::jni::DecodeArtField(art::WellKnownClasses::java_lang_ThreadGroup_groups); + art::ObjPtr<art::mirror::Object> groups_array = groups_field->GetObject(thread_group.Get()); + + if (groups_array == nullptr) { + return; + } + CHECK(groups_array->IsObjectArray()); + + art::ObjPtr<art::mirror::ObjectArray<art::mirror::Object>> groups_array_as_array = + groups_array->AsObjectArray<art::mirror::Object>(); + + // Copy all non-null elements. + for (int32_t i = 0; i < groups_array_as_array->GetLength(); ++i) { + art::ObjPtr<art::mirror::Object> entry = groups_array_as_array->Get(i); + if (entry != nullptr) { + thread_groups->push_back(entry); + } + } +} + +jvmtiError ThreadGroupUtil::GetThreadGroupChildren(jvmtiEnv* env, + jthreadGroup group, + jint* thread_count_ptr, + jthread** threads_ptr, + jint* group_count_ptr, + jthreadGroup** groups_ptr) { + if (group == nullptr) { + return ERR(INVALID_THREAD_GROUP); + } + + art::ScopedObjectAccess soa(art::Thread::Current()); + + if (!soa.Env()->IsInstanceOf(group, art::WellKnownClasses::java_lang_ThreadGroup)) { + return ERR(INVALID_THREAD_GROUP); + } + + art::StackHandleScope<1> hs(soa.Self()); + art::Handle<art::mirror::Object> thread_group = hs.NewHandle( + soa.Decode<art::mirror::Object>(group)); + + art::ObjectLock<art::mirror::Object> thread_group_lock(soa.Self(), thread_group); + + std::vector<art::ObjPtr<art::mirror::Object>> thread_peers; + GetThreads(thread_group, &thread_peers); + + std::vector<art::ObjPtr<art::mirror::Object>> thread_groups; + GetChildThreadGroups(thread_group, &thread_groups); + + jthread* thread_data = nullptr; + JvmtiUniquePtr peers_uptr; + if (!thread_peers.empty()) { + unsigned char* data; + jvmtiError res = env->Allocate(sizeof(jthread) * thread_peers.size(), &data); + if (res != ERR(NONE)) { + return res; + } + thread_data = reinterpret_cast<jthread*>(data); + peers_uptr = MakeJvmtiUniquePtr(env, data); + } + + jthreadGroup* group_data = nullptr; + if (!thread_groups.empty()) { + unsigned char* data; + jvmtiError res = env->Allocate(sizeof(jthreadGroup) * thread_groups.size(), &data); + if (res != ERR(NONE)) { + return res; + } + group_data = reinterpret_cast<jthreadGroup*>(data); + } + + // Can't fail anymore from here on. + + // Copy data into out buffers. + for (size_t i = 0; i != thread_peers.size(); ++i) { + thread_data[i] = soa.AddLocalReference<jthread>(thread_peers[i]); + } + for (size_t i = 0; i != thread_groups.size(); ++i) { + group_data[i] = soa.AddLocalReference<jthreadGroup>(thread_groups[i]); + } + + *thread_count_ptr = static_cast<jint>(thread_peers.size()); + *threads_ptr = thread_data; + *group_count_ptr = static_cast<jint>(thread_groups.size()); + *groups_ptr = group_data; + + // Everything's fine. + peers_uptr.release(); + + return ERR(NONE); +} + +} // namespace openjdkjvmti diff --git a/runtime/openjdkjvmti/ti_threadgroup.h b/runtime/openjdkjvmti/ti_threadgroup.h new file mode 100644 index 0000000000..c3a0ff5e15 --- /dev/null +++ b/runtime/openjdkjvmti/ti_threadgroup.h @@ -0,0 +1,60 @@ +/* Copyright (C) 2017 The Android Open Source Project + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This file implements interfaces from the file jvmti.h. This implementation + * is licensed under the same terms as the file jvmti.h. The + * copyright and license information for the file jvmti.h follows. + * + * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#ifndef ART_RUNTIME_OPENJDKJVMTI_TI_THREADGROUP_H_ +#define ART_RUNTIME_OPENJDKJVMTI_TI_THREADGROUP_H_ + +#include "jni.h" +#include "jvmti.h" + +namespace openjdkjvmti { + +class ThreadGroupUtil { + public: + static jvmtiError GetTopThreadGroups(jvmtiEnv* env, + jint* group_count_ptr, + jthreadGroup** groups_ptr); + + static jvmtiError GetThreadGroupInfo(jvmtiEnv* env, + jthreadGroup group, + jvmtiThreadGroupInfo* info_ptr); + + static jvmtiError GetThreadGroupChildren(jvmtiEnv* env, + jthreadGroup group, + jint* thread_count_ptr, + jthread** threads_ptr, + jint* group_count_ptr, + jthreadGroup** groups_ptr); +}; + +} // namespace openjdkjvmti + +#endif // ART_RUNTIME_OPENJDKJVMTI_TI_THREADGROUP_H_ diff --git a/runtime/openjdkjvmti/ti_timers.cc b/runtime/openjdkjvmti/ti_timers.cc new file mode 100644 index 0000000000..24fb0419ee --- /dev/null +++ b/runtime/openjdkjvmti/ti_timers.cc @@ -0,0 +1,93 @@ +/* Copyright (C) 2017 The Android Open Source Project + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This file implements interfaces from the file jvmti.h. This implementation + * is licensed under the same terms as the file jvmti.h. The + * copyright and license information for the file jvmti.h follows. + * + * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#include "ti_timers.h" + +#include <limits> + +#ifndef __APPLE__ +#include <time.h> +#else +#include <sys/time.h> +#endif +#include <unistd.h> + +#include "art_jvmti.h" +#include "base/macros.h" + +namespace openjdkjvmti { + +jvmtiError TimerUtil::GetAvailableProcessors(jvmtiEnv* env ATTRIBUTE_UNUSED, + jint* processor_count_ptr) { + if (processor_count_ptr == nullptr) { + return ERR(NULL_POINTER); + } + + *processor_count_ptr = static_cast<jint>(sysconf(_SC_NPROCESSORS_CONF)); + + return ERR(NONE); +} + +jvmtiError TimerUtil::GetTimerInfo(jvmtiEnv* env ATTRIBUTE_UNUSED, jvmtiTimerInfo* info_ptr) { + if (info_ptr == nullptr) { + return ERR(NULL_POINTER); + } + + info_ptr->max_value = static_cast<jlong>(std::numeric_limits<uint64_t>::max()); + info_ptr->may_skip_forward = JNI_TRUE; + info_ptr->may_skip_backward = JNI_TRUE; + info_ptr->kind = jvmtiTimerKind::JVMTI_TIMER_ELAPSED; + + return ERR(NONE); +} + +jvmtiError TimerUtil::GetTime(jvmtiEnv* env ATTRIBUTE_UNUSED, jlong* nanos_ptr) { + if (nanos_ptr == nullptr) { + return ERR(NULL_POINTER); + } + +#ifndef __APPLE__ + // Use the same implementation as System.nanoTime. + struct timespec now; + clock_gettime(CLOCK_MONOTONIC, &now); + *nanos_ptr = now.tv_sec * 1000000000LL + now.tv_nsec; +#else + // No CLOCK_MONOTONIC support on older Mac OS. + struct timeval t; + t.tv_sec = t.tv_usec = 0; + gettimeofday(&t, NULL); + *nanos_ptr = static_cast<jlong>(t.tv_sec)*1000000000LL + static_cast<jlong>(t.tv_usec)*1000LL; +#endif + + return ERR(NONE); +} + +} // namespace openjdkjvmti diff --git a/runtime/openjdkjvmti/ti_timers.h b/runtime/openjdkjvmti/ti_timers.h new file mode 100644 index 0000000000..6300678ff7 --- /dev/null +++ b/runtime/openjdkjvmti/ti_timers.h @@ -0,0 +1,51 @@ +/* Copyright (C) 2017 The Android Open Source Project + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This file implements interfaces from the file jvmti.h. This implementation + * is licensed under the same terms as the file jvmti.h. The + * copyright and license information for the file jvmti.h follows. + * + * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#ifndef ART_RUNTIME_OPENJDKJVMTI_TI_TIMERS_H_ +#define ART_RUNTIME_OPENJDKJVMTI_TI_TIMERS_H_ + +#include "jni.h" +#include "jvmti.h" + +namespace openjdkjvmti { + +class TimerUtil { + public: + static jvmtiError GetAvailableProcessors(jvmtiEnv* env, jint* processor_count_ptr); + + static jvmtiError GetTimerInfo(jvmtiEnv* env, jvmtiTimerInfo* info_ptr); + + static jvmtiError GetTime(jvmtiEnv* env, jlong* nanos_ptr); +}; + +} // namespace openjdkjvmti + +#endif // ART_RUNTIME_OPENJDKJVMTI_TI_TIMERS_H_ diff --git a/runtime/parsed_options.cc b/runtime/parsed_options.cc index e1022b000b..a72159bc80 100644 --- a/runtime/parsed_options.cc +++ b/runtime/parsed_options.cc @@ -763,8 +763,6 @@ void ParsedOptions::Usage(const char* fmt, ...) { "(Enable new and experimental agent support)\n"); UsageMessage(stream, " -Xexperimental:agents" "(Enable new and experimental agent support)\n"); - UsageMessage(stream, " -Xexperimental:method-handles" - "(Enable new and experimental method handles support)\n"); UsageMessage(stream, "\n"); UsageMessage(stream, "The following previously supported Dalvik options are ignored:\n"); diff --git a/runtime/reflection.cc b/runtime/reflection.cc index 4d2450135e..75176f938e 100644 --- a/runtime/reflection.cc +++ b/runtime/reflection.cc @@ -227,11 +227,11 @@ class ArgArray { for (size_t i = 1, args_offset = 0; i < shorty_len_; ++i, ++args_offset) { ObjPtr<mirror::Object> arg(args->Get(args_offset)); if (((shorty_[i] == 'L') && (arg != nullptr)) || ((arg == nullptr && shorty_[i] != 'L'))) { - PointerSize pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize(); + // Note: The method's parameter's type must have been previously resolved. ObjPtr<mirror::Class> dst_class( m->GetClassFromTypeIndex(classes->GetTypeItem(args_offset).type_idx_, - true /* resolve */, - pointer_size)); + false /* resolve */)); + DCHECK(dst_class != nullptr) << m->PrettyMethod() << " arg #" << i; if (UNLIKELY(arg == nullptr || !arg->InstanceOf(dst_class))) { ThrowIllegalArgumentException( StringPrintf("method %s argument %zd has type %s, got %s", @@ -363,12 +363,9 @@ static void CheckMethodArguments(JavaVMExt* vm, ArtMethod* m, uint32_t* args) } // TODO: If args contain object references, it may cause problems. Thread* const self = Thread::Current(); - PointerSize pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize(); for (uint32_t i = 0; i < num_params; i++) { dex::TypeIndex type_idx = params->GetTypeItem(i).type_idx_; - ObjPtr<mirror::Class> param_type(m->GetClassFromTypeIndex(type_idx, - true /* resolve*/, - pointer_size)); + ObjPtr<mirror::Class> param_type(m->GetClassFromTypeIndex(type_idx, true /* resolve */)); if (param_type == nullptr) { CHECK(self->IsExceptionPending()); LOG(ERROR) << "Internal error: unresolvable type for argument type in JNI invoke: " diff --git a/runtime/runtime.h b/runtime/runtime.h index 8fc211c6a3..a87e1c136b 100644 --- a/runtime/runtime.h +++ b/runtime/runtime.h @@ -304,7 +304,7 @@ class Runtime { } bool IsMethodHandlesEnabled() const { - return experimental_flags_ & ExperimentalFlags::kMethodHandles; + return true; } void DisallowNewSystemWeaks() REQUIRES_SHARED(Locks::mutator_lock_); diff --git a/runtime/runtime_options.def b/runtime/runtime_options.def index d1970fe7a2..ecabf9adc5 100644 --- a/runtime/runtime_options.def +++ b/runtime/runtime_options.def @@ -117,7 +117,7 @@ RUNTIME_OPTIONS_KEY (unsigned int, ZygoteMaxFailedBoots, 10) RUNTIME_OPTIONS_KEY (Unit, NoDexFileFallback) RUNTIME_OPTIONS_KEY (std::string, CpuAbiList) RUNTIME_OPTIONS_KEY (std::string, Fingerprint) -RUNTIME_OPTIONS_KEY (ExperimentalFlags, Experimental, ExperimentalFlags::kNone) // -Xexperimental:{none, agents, method-handles} +RUNTIME_OPTIONS_KEY (ExperimentalFlags, Experimental, ExperimentalFlags::kNone) // -Xexperimental:{none, agents} RUNTIME_OPTIONS_KEY (std::vector<ti::Agent>, AgentLib) // -agentlib:<libname>=<options>, Requires -Xexperimental:agents RUNTIME_OPTIONS_KEY (std::vector<ti::Agent>, AgentPath) // -agentpath:<libname>=<options>, Requires -Xexperimental:agents RUNTIME_OPTIONS_KEY (std::vector<Plugin>, Plugins) // -Xplugin:<library> Requires -Xexperimental:runtime-plugins diff --git a/runtime/stack_map.cc b/runtime/stack_map.cc index a7e7c21a42..9ebf9a7bdd 100644 --- a/runtime/stack_map.cc +++ b/runtime/stack_map.cc @@ -18,8 +18,9 @@ #include <stdint.h> +#include "art_method.h" #include "indenter.h" -#include "invoke_type.h" +#include "scoped_thread_state_change-inl.h" namespace art { @@ -106,7 +107,7 @@ void InlineInfoEncoding::Dump(VariableIndentationOutputStream* vios) const { << "InlineInfoEncoding" << " (method_index_bit_offset=" << static_cast<uint32_t>(kMethodIndexBitOffset) << ", dex_pc_bit_offset=" << static_cast<uint32_t>(dex_pc_bit_offset_) - << ", invoke_type_bit_offset=" << static_cast<uint32_t>(invoke_type_bit_offset_) + << ", extra_data_bit_offset=" << static_cast<uint32_t>(extra_data_bit_offset_) << ", dex_register_map_bit_offset=" << static_cast<uint32_t>(dex_register_map_bit_offset_) << ", total_bit_size=" << static_cast<uint32_t>(total_bit_size_) << ")\n"; @@ -230,12 +231,16 @@ void InlineInfo::Dump(VariableIndentationOutputStream* vios, vios->Stream() << " At depth " << i << std::hex - << " (dex_pc=0x" << GetDexPcAtDepth(inline_info_encoding, i) - << std::dec - << ", method_index=" << GetMethodIndexAtDepth(inline_info_encoding, i) - << ", invoke_type=" << static_cast<InvokeType>(GetInvokeTypeAtDepth(inline_info_encoding, - i)) - << ")\n"; + << " (dex_pc=0x" << GetDexPcAtDepth(inline_info_encoding, i); + if (EncodesArtMethodAtDepth(inline_info_encoding, i)) { + ScopedObjectAccess soa(Thread::Current()); + vios->Stream() << ", method=" << GetArtMethodAtDepth(inline_info_encoding, i)->PrettyMethod(); + } else { + vios->Stream() + << std::dec + << ", method_index=" << GetMethodIndexAtDepth(inline_info_encoding, i); + } + vios->Stream() << ")\n"; if (HasDexRegisterMapAtDepth(inline_info_encoding, i) && (number_of_dex_registers != nullptr)) { CodeInfoEncoding encoding = code_info.ExtractEncoding(); DexRegisterMap dex_register_map = diff --git a/runtime/stack_map.h b/runtime/stack_map.h index 5e556be286..15d7816031 100644 --- a/runtime/stack_map.h +++ b/runtime/stack_map.h @@ -35,6 +35,7 @@ static constexpr ssize_t kFrameSlotSize = 4; // Size of Dex virtual registers. static constexpr size_t kVRegSize = 4; +class ArtMethod; class CodeInfo; class StackMapEncoding; struct CodeInfoEncoding; @@ -887,7 +888,7 @@ class InlineInfoEncoding { public: void SetFromSizes(size_t method_index_max, size_t dex_pc_max, - size_t invoke_type_max, + size_t extra_data_max, size_t dex_register_map_size) { total_bit_size_ = kMethodIndexBitOffset; total_bit_size_ += MinimumBitsToStore(method_index_max); @@ -899,8 +900,8 @@ class InlineInfoEncoding { total_bit_size_ += MinimumBitsToStore(1 /* kNoDexPc */ + dex_pc_max); } - invoke_type_bit_offset_ = dchecked_integral_cast<uint8_t>(total_bit_size_); - total_bit_size_ += MinimumBitsToStore(invoke_type_max); + extra_data_bit_offset_ = dchecked_integral_cast<uint8_t>(total_bit_size_); + total_bit_size_ += MinimumBitsToStore(extra_data_max); // We also need +1 for kNoDexRegisterMap, but since the size is strictly // greater than any offset we might try to encode, we already implicitly have it. @@ -912,10 +913,10 @@ class InlineInfoEncoding { return FieldEncoding(kMethodIndexBitOffset, dex_pc_bit_offset_); } ALWAYS_INLINE FieldEncoding GetDexPcEncoding() const { - return FieldEncoding(dex_pc_bit_offset_, invoke_type_bit_offset_, -1 /* min_value */); + return FieldEncoding(dex_pc_bit_offset_, extra_data_bit_offset_, -1 /* min_value */); } - ALWAYS_INLINE FieldEncoding GetInvokeTypeEncoding() const { - return FieldEncoding(invoke_type_bit_offset_, dex_register_map_bit_offset_); + ALWAYS_INLINE FieldEncoding GetExtraDataEncoding() const { + return FieldEncoding(extra_data_bit_offset_, dex_register_map_bit_offset_); } ALWAYS_INLINE FieldEncoding GetDexRegisterMapEncoding() const { return FieldEncoding(dex_register_map_bit_offset_, total_bit_size_, -1 /* min_value */); @@ -930,7 +931,7 @@ class InlineInfoEncoding { static constexpr uint8_t kIsLastBitOffset = 0; static constexpr uint8_t kMethodIndexBitOffset = 1; uint8_t dex_pc_bit_offset_; - uint8_t invoke_type_bit_offset_; + uint8_t extra_data_bit_offset_; uint8_t dex_register_map_bit_offset_; uint8_t total_bit_size_; }; @@ -938,7 +939,11 @@ class InlineInfoEncoding { /** * Inline information for a specific PC. The information is of the form: * - * [is_last, method_index, dex_pc, invoke_type, dex_register_map_offset]+. + * [is_last, + * method_index (or ArtMethod high bits), + * dex_pc, + * extra_data (ArtMethod low bits or 1), + * dex_register_map_offset]+. */ class InlineInfo { public: @@ -960,6 +965,7 @@ class InlineInfo { ALWAYS_INLINE uint32_t GetMethodIndexAtDepth(const InlineInfoEncoding& encoding, uint32_t depth) const { + DCHECK(!EncodesArtMethodAtDepth(encoding, depth)); return encoding.GetMethodIndexEncoding().Load(GetRegionAtDepth(encoding, depth)); } @@ -980,15 +986,28 @@ class InlineInfo { encoding.GetDexPcEncoding().Store(GetRegionAtDepth(encoding, depth), dex_pc); } - ALWAYS_INLINE uint32_t GetInvokeTypeAtDepth(const InlineInfoEncoding& encoding, - uint32_t depth) const { - return encoding.GetInvokeTypeEncoding().Load(GetRegionAtDepth(encoding, depth)); + ALWAYS_INLINE bool EncodesArtMethodAtDepth(const InlineInfoEncoding& encoding, + uint32_t depth) const { + return (encoding.GetExtraDataEncoding().Load(GetRegionAtDepth(encoding, depth)) & 1) == 0; + } + + ALWAYS_INLINE void SetExtraDataAtDepth(const InlineInfoEncoding& encoding, + uint32_t depth, + uint32_t extra_data) { + encoding.GetExtraDataEncoding().Store(GetRegionAtDepth(encoding, depth), extra_data); } - ALWAYS_INLINE void SetInvokeTypeAtDepth(const InlineInfoEncoding& encoding, - uint32_t depth, - uint32_t invoke_type) { - encoding.GetInvokeTypeEncoding().Store(GetRegionAtDepth(encoding, depth), invoke_type); + ALWAYS_INLINE ArtMethod* GetArtMethodAtDepth(const InlineInfoEncoding& encoding, + uint32_t depth) const { + uint32_t low_bits = encoding.GetExtraDataEncoding().Load(GetRegionAtDepth(encoding, depth)); + uint32_t high_bits = encoding.GetMethodIndexEncoding().Load(GetRegionAtDepth(encoding, depth)); + if (high_bits == 0) { + return reinterpret_cast<ArtMethod*>(low_bits); + } else { + uint64_t address = high_bits; + address = address << 32; + return reinterpret_cast<ArtMethod*>(address | low_bits); + } } ALWAYS_INLINE uint32_t GetDexRegisterMapOffsetAtDepth(const InlineInfoEncoding& encoding, diff --git a/runtime/thread.cc b/runtime/thread.cc index 40b6d73d94..016a379437 100644 --- a/runtime/thread.cc +++ b/runtime/thread.cc @@ -2632,8 +2632,6 @@ void Thread::DumpThreadOffset(std::ostream& os, uint32_t offset) { QUICK_ENTRY_POINT_INFO(pAllocObjectResolved) QUICK_ENTRY_POINT_INFO(pAllocObjectInitialized) QUICK_ENTRY_POINT_INFO(pAllocObjectWithChecks) - QUICK_ENTRY_POINT_INFO(pCheckAndAllocArray) - QUICK_ENTRY_POINT_INFO(pCheckAndAllocArrayWithAccessCheck) QUICK_ENTRY_POINT_INFO(pAllocStringFromBytes) QUICK_ENTRY_POINT_INFO(pAllocStringFromChars) QUICK_ENTRY_POINT_INFO(pAllocStringFromString) @@ -2667,10 +2665,7 @@ void Thread::DumpThreadOffset(std::ostream& os, uint32_t offset) { QUICK_ENTRY_POINT_INFO(pGet64Static) QUICK_ENTRY_POINT_INFO(pGetObjInstance) QUICK_ENTRY_POINT_INFO(pGetObjStatic) - QUICK_ENTRY_POINT_INFO(pAputObjectWithNullAndBoundCheck) - QUICK_ENTRY_POINT_INFO(pAputObjectWithBoundCheck) QUICK_ENTRY_POINT_INFO(pAputObject) - QUICK_ENTRY_POINT_INFO(pHandleFillArrayData) QUICK_ENTRY_POINT_INFO(pJniMethodStart) QUICK_ENTRY_POINT_INFO(pJniMethodStartSynchronized) QUICK_ENTRY_POINT_INFO(pJniMethodEnd) diff --git a/runtime/thread.h b/runtime/thread.h index d54a80df99..2b451bcaee 100644 --- a/runtime/thread.h +++ b/runtime/thread.h @@ -1140,6 +1140,14 @@ class Thread { return debug_disallow_read_barrier_; } + const void* GetCustomTLS() const { + return custom_tls_; + } + + void SetCustomTLS(const void* data) { + custom_tls_ = data; + } + // Returns true if the current thread is the jit sensitive thread. bool IsJitSensitiveThread() const { return this == jit_sensitive_thread_; @@ -1418,7 +1426,7 @@ class Thread { stacked_shadow_frame_record(nullptr), deoptimization_context_stack(nullptr), frame_id_to_shadow_frame(nullptr), name(nullptr), pthread_self(0), last_no_thread_suspension_cause(nullptr), checkpoint_function(nullptr), - thread_local_pos(nullptr), thread_local_end(nullptr), thread_local_start(nullptr), + thread_local_start(nullptr), thread_local_pos(nullptr), thread_local_end(nullptr), thread_local_objects(0), mterp_current_ibase(nullptr), mterp_default_ibase(nullptr), mterp_alt_ibase(nullptr), thread_local_alloc_stack_top(nullptr), thread_local_alloc_stack_end(nullptr), nested_signal_state(nullptr), @@ -1537,21 +1545,21 @@ class Thread { // to avoid additional cost of a mutex and a condition variable, as used in art::Barrier. AtomicInteger* active_suspend_barriers[kMaxSuspendBarriers]; - // Entrypoint function pointers. - // TODO: move this to more of a global offset table model to avoid per-thread duplication. - JniEntryPoints jni_entrypoints; - QuickEntryPoints quick_entrypoints; + // Thread-local allocation pointer. Moved here to force alignment for thread_local_pos on ARM. + uint8_t* thread_local_start; // thread_local_pos and thread_local_end must be consecutive for ldrd and are 8 byte aligned for // potentially better performance. uint8_t* thread_local_pos; uint8_t* thread_local_end; - // Thread-local allocation pointer. - uint8_t* thread_local_start; - size_t thread_local_objects; + // Entrypoint function pointers. + // TODO: move this to more of a global offset table model to avoid per-thread duplication. + JniEntryPoints jni_entrypoints; + QuickEntryPoints quick_entrypoints; + // Mterp jump table bases. void* mterp_current_ibase; void* mterp_default_ibase; @@ -1600,6 +1608,10 @@ class Thread { // Pending extra checkpoints if checkpoint_function_ is already used. std::list<Closure*> checkpoint_overflow_ GUARDED_BY(Locks::thread_suspend_count_lock_); + // Custom TLS field that can be used by plugins. + // TODO: Generalize once we have more plugins. + const void* custom_tls_; + // True if the thread is allowed to call back into java (for e.g. during class resolution). // By default this is true. bool can_call_into_java_; diff --git a/runtime/vdex_file.h b/runtime/vdex_file.h index 3b114a9f10..bb9844af65 100644 --- a/runtime/vdex_file.h +++ b/runtime/vdex_file.h @@ -61,7 +61,7 @@ class VdexFile { private: static constexpr uint8_t kVdexMagic[] = { 'v', 'd', 'e', 'x' }; - static constexpr uint8_t kVdexVersion[] = { '0', '0', '1', '\0' }; + static constexpr uint8_t kVdexVersion[] = { '0', '0', '2', '\0' }; // Handle verify-profile uint8_t magic_[4]; uint8_t version_[4]; diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc index 25a179bd32..b915457557 100644 --- a/runtime/verifier/method_verifier.cc +++ b/runtime/verifier/method_verifier.cc @@ -2901,9 +2901,7 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { ArtMethod* called_method = VerifyInvocationArgs(inst, type, is_range); const RegType* return_type = nullptr; if (called_method != nullptr) { - PointerSize pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize(); - mirror::Class* return_type_class = called_method->GetReturnType(can_load_classes_, - pointer_size); + mirror::Class* return_type_class = called_method->GetReturnType(can_load_classes_); if (return_type_class != nullptr) { return_type = &FromClass(called_method->GetReturnTypeDescriptor(), return_type_class, @@ -2946,9 +2944,7 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { } else { is_constructor = called_method->IsConstructor(); return_type_descriptor = called_method->GetReturnTypeDescriptor(); - PointerSize pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize(); - mirror::Class* return_type_class = called_method->GetReturnType(can_load_classes_, - pointer_size); + mirror::Class* return_type_class = called_method->GetReturnType(can_load_classes_); if (return_type_class != nullptr) { return_type = &FromClass(return_type_descriptor, return_type_class, @@ -5133,9 +5129,7 @@ InstructionFlags* MethodVerifier::CurrentInsnFlags() { const RegType& MethodVerifier::GetMethodReturnType() { if (return_type_ == nullptr) { if (mirror_method_ != nullptr) { - PointerSize pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize(); - mirror::Class* return_type_class = mirror_method_->GetReturnType(can_load_classes_, - pointer_size); + mirror::Class* return_type_class = mirror_method_->GetReturnType(can_load_classes_); if (return_type_class != nullptr) { return_type_ = &FromClass(mirror_method_->GetReturnTypeDescriptor(), return_type_class, diff --git a/runtime/well_known_classes.cc b/runtime/well_known_classes.cc index a5b275cc3b..507ea165e5 100644 --- a/runtime/well_known_classes.cc +++ b/runtime/well_known_classes.cc @@ -103,6 +103,7 @@ jmethodID WellKnownClasses::java_lang_reflect_Parameter_init; jmethodID WellKnownClasses::java_lang_reflect_Proxy_invoke; jmethodID WellKnownClasses::java_lang_Runtime_nativeLoad; jmethodID WellKnownClasses::java_lang_Short_valueOf; +jmethodID WellKnownClasses::java_lang_String_charAt; jmethodID WellKnownClasses::java_lang_System_runFinalization = nullptr; jmethodID WellKnownClasses::java_lang_Thread_dispatchUncaughtException; jmethodID WellKnownClasses::java_lang_Thread_init; @@ -337,6 +338,7 @@ void WellKnownClasses::Init(JNIEnv* env) { java_lang_ref_ReferenceQueue_add = CacheMethod(env, java_lang_ref_ReferenceQueue.get(), true, "add", "(Ljava/lang/ref/Reference;)V"); java_lang_reflect_Parameter_init = CacheMethod(env, java_lang_reflect_Parameter, false, "<init>", "(Ljava/lang/String;ILjava/lang/reflect/Executable;I)V"); + java_lang_String_charAt = CacheMethod(env, java_lang_String, false, "charAt", "(I)C"); java_lang_Thread_dispatchUncaughtException = CacheMethod(env, java_lang_Thread, false, "dispatchUncaughtException", "(Ljava/lang/Throwable;)V"); java_lang_Thread_init = CacheMethod(env, java_lang_Thread, false, "<init>", "(Ljava/lang/ThreadGroup;Ljava/lang/String;IZ)V"); java_lang_Thread_run = CacheMethod(env, java_lang_Thread, false, "run", "()V"); diff --git a/runtime/well_known_classes.h b/runtime/well_known_classes.h index 371be61f43..b3ce3d1597 100644 --- a/runtime/well_known_classes.h +++ b/runtime/well_known_classes.h @@ -113,6 +113,7 @@ struct WellKnownClasses { static jmethodID java_lang_reflect_Proxy_invoke; static jmethodID java_lang_Runtime_nativeLoad; static jmethodID java_lang_Short_valueOf; + static jmethodID java_lang_String_charAt; static jmethodID java_lang_System_runFinalization; static jmethodID java_lang_Thread_dispatchUncaughtException; static jmethodID java_lang_Thread_init; diff --git a/test/080-oom-throw/expected.txt b/test/080-oom-throw/expected.txt index 904393bc3b..0967278314 100644 --- a/test/080-oom-throw/expected.txt +++ b/test/080-oom-throw/expected.txt @@ -1,3 +1,4 @@ Test reflection correctly threw +Test reflection2 correctly threw NEW_ARRAY correctly threw OOME NEW_INSTANCE correctly threw OOME diff --git a/test/080-oom-throw/src/Main.java b/test/080-oom-throw/src/Main.java index 0ae92a96dc..a6c18b75fc 100644 --- a/test/080-oom-throw/src/Main.java +++ b/test/080-oom-throw/src/Main.java @@ -53,6 +53,30 @@ public class Main { } } + public static Object eatAllMemory() { + Object[] result = null; + int size = 1000000; + while (result == null && size != 0) { + try { + result = new Object[size]; + } catch (OutOfMemoryError oome) { + size /= 2; + } + } + if (result != null) { + int index = 0; + while (index != result.length && size != 0) { + try { + result[index] = new byte[size]; + ++index; + } catch (OutOfMemoryError oome) { + size /= 2; + } + } + } + return result; + } + static boolean triggerArrayOOM() { ArrayMemEater.blowup(new char[128 * 1024][]); return ArrayMemEater.sawOome; @@ -74,6 +98,9 @@ public class Main { if (triggerReflectionOOM()) { System.out.println("Test reflection correctly threw"); } + if (triggerReflectionOOM2()) { + System.out.println("Test reflection2 correctly threw"); + } if (triggerArrayOOM()) { System.out.println("NEW_ARRAY correctly threw OOME"); @@ -125,4 +152,20 @@ public class Main { } return true; } + + static boolean triggerReflectionOOM2() { + Object memory = eatAllMemory(); + boolean result = false; + try { + Main.class.getDeclaredMethods(); + } catch (OutOfMemoryError e) { + result = true; + } + if (!result) { + boolean memoryWasAllocated = (memory != null); + memory = null; + System.out.println("memoryWasAllocated = " + memoryWasAllocated); + } + return result; + } } diff --git a/test/129-ThreadGetId/src/Main.java b/test/129-ThreadGetId/src/Main.java index 6ba01ff0b5..4e48e0e1a6 100644 --- a/test/129-ThreadGetId/src/Main.java +++ b/test/129-ThreadGetId/src/Main.java @@ -23,7 +23,6 @@ public class Main implements Runnable { public static void main(String[] args) throws Exception { final Thread[] threads = new Thread[numberOfThreads]; - test_getStackTraces(); for (int t = 0; t < threads.length; t++) { threads[t] = new Thread(new Main()); threads[t].start(); @@ -31,6 +30,9 @@ public class Main implements Runnable { for (Thread t : threads) { t.join(); } + // Do this test after the other part to leave some time for the heap task daemon to start + // up. + test_getStackTraces(); System.out.println("Finishing"); } diff --git a/test/633-checker-rtp-getclass/expected.txt b/test/633-checker-rtp-getclass/expected.txt new file mode 100644 index 0000000000..a178d0414b --- /dev/null +++ b/test/633-checker-rtp-getclass/expected.txt @@ -0,0 +1,3 @@ +2 +3 +6 diff --git a/test/633-checker-rtp-getclass/info.txt b/test/633-checker-rtp-getclass/info.txt new file mode 100644 index 0000000000..e98a0acbbb --- /dev/null +++ b/test/633-checker-rtp-getclass/info.txt @@ -0,0 +1,3 @@ +Regression test for the RTP pass of the compiler, which +used the wrong block when bounding a type after a obj.getClass() +check. diff --git a/test/633-checker-rtp-getclass/src/Main.java b/test/633-checker-rtp-getclass/src/Main.java new file mode 100644 index 0000000000..f29c139f63 --- /dev/null +++ b/test/633-checker-rtp-getclass/src/Main.java @@ -0,0 +1,76 @@ +/* + * 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) { + System.out.println($opt$noinline$foo(new Main())); + System.out.println($opt$noinline$foo(new SubMain())); + System.out.println($opt$noinline$foo(new SubSubMain())); + } + + + // Checker test to make sure the only inlined instruction is + // SubMain.bar. + /// CHECK-START: int Main.$opt$noinline$foo(Main) inliner (after) + /// CHECK-DAG: InvokeVirtual method_name:Main.foo + /// CHECK-DAG: <<Const:i\d+>> IntConstant 3 + /// CHECK: begin_block + /// CHECK: BoundType klass:SubMain + /// CHECK: Return [<<Const>>] + /// CHECK-NOT: begin_block + /// CHECK: end_block + public static int $opt$noinline$foo(Main o) { + if (doThrow) { throw new Error(); } + // To exercise the bug on Jack, we need two getClass compares. + if (o.getClass() == Main.class || o.getClass() != SubMain.class) { + return o.foo(); + } else { + // We used to wrongly bound the type of o to `Main` here and then realize that's + // impossible and mark this branch as dead. + return o.bar(); + } + } + + public int bar() { + return 1; + } + + public int foo() { + return 2; + } + + public static boolean doThrow = false; +} + +class SubMain extends Main { + public int bar() { + return 3; + } + + public int foo() { + return 4; + } +} + +class SubSubMain extends SubMain { + public int bar() { + return 5; + } + + public int foo() { + return 6; + } +} diff --git a/test/634-vdex-duplicate/expected.txt b/test/634-vdex-duplicate/expected.txt new file mode 100644 index 0000000000..557db03de9 --- /dev/null +++ b/test/634-vdex-duplicate/expected.txt @@ -0,0 +1 @@ +Hello World diff --git a/test/634-vdex-duplicate/info.txt b/test/634-vdex-duplicate/info.txt new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/test/634-vdex-duplicate/info.txt diff --git a/test/634-vdex-duplicate/run b/test/634-vdex-duplicate/run new file mode 100644 index 0000000000..1ccb84150b --- /dev/null +++ b/test/634-vdex-duplicate/run @@ -0,0 +1,17 @@ +#!/bin/bash +# +# Copyright (C) 2016 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +exec ${RUN} -Xcompiler-option --compiler-filter=verify-profile --vdex-filter speed --vdex "${@}" diff --git a/test/634-vdex-duplicate/src/Main.java b/test/634-vdex-duplicate/src/Main.java new file mode 100644 index 0000000000..228310697a --- /dev/null +++ b/test/634-vdex-duplicate/src/Main.java @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +public class Main { + public static void main(String[] args) { + System.out.println("Hello World"); + } +} diff --git a/test/634-vdex-duplicate/src/sun/misc/Unsafe.java b/test/634-vdex-duplicate/src/sun/misc/Unsafe.java new file mode 100644 index 0000000000..c32868cf37 --- /dev/null +++ b/test/634-vdex-duplicate/src/sun/misc/Unsafe.java @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package sun.misc; + +public class Unsafe { +} diff --git a/test/901-hello-ti-agent/basics.cc b/test/901-hello-ti-agent/basics.cc index 3a475c6fef..052fb9ac13 100644 --- a/test/901-hello-ti-agent/basics.cc +++ b/test/901-hello-ti-agent/basics.cc @@ -22,6 +22,9 @@ #include "base/macros.h" #include "openjdkjvmti/jvmti.h" +#include "ti-agent/common_helper.h" +#include "ti-agent/common_load.h" + namespace art { namespace Test901HelloTi { @@ -72,9 +75,22 @@ jint OnLoad(JavaVM* vm, CHECK_CALL_SUCCESS(env->DisposeEnvironment()); CHECK_CALL_SUCCESS(env2->DisposeEnvironment()); #undef CHECK_CALL_SUCCESS + + if (vm->GetEnv(reinterpret_cast<void**>(&jvmti_env), JVMTI_VERSION_1_0)) { + printf("Unable to get jvmti env!\n"); + return 1; + } + SetAllCapabilities(jvmti_env); + return JNI_OK; } +extern "C" JNIEXPORT void JNICALL Java_Main_setVerboseFlag( + JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jint iflag, jboolean val) { + jvmtiVerboseFlag flag = static_cast<jvmtiVerboseFlag>(iflag); + jvmtiError result = jvmti_env->SetVerboseFlag(flag, val); + JvmtiErrorToException(env, result); +} } // namespace Test901HelloTi } // namespace art diff --git a/test/901-hello-ti-agent/expected.txt b/test/901-hello-ti-agent/expected.txt index 414eb3b2ae..2aee99b25a 100644 --- a/test/901-hello-ti-agent/expected.txt +++ b/test/901-hello-ti-agent/expected.txt @@ -1,2 +1,8 @@ Loaded Agent for test 901-hello-ti-agent Hello, world! +0 +1 +2 +4 +8 +JVMTI_ERROR_ILLEGAL_ARGUMENT diff --git a/test/901-hello-ti-agent/src/Main.java b/test/901-hello-ti-agent/src/Main.java index 1ef6289559..775e5c2e0c 100644 --- a/test/901-hello-ti-agent/src/Main.java +++ b/test/901-hello-ti-agent/src/Main.java @@ -16,6 +16,26 @@ public class Main { public static void main(String[] args) { + System.loadLibrary(args[1]); + System.out.println("Hello, world!"); + + set(0); // OTHER + set(1); // GC + set(2); // CLASS + set(4); // JNI + set(8); // Error. } + + private static void set(int i) { + System.out.println(i); + try { + setVerboseFlag(i, true); + setVerboseFlag(i, false); + } catch (RuntimeException e) { + System.out.println(e.getMessage()); + } + } + + private static native void setVerboseFlag(int flag, boolean value); } diff --git a/test/912-classes/classes.cc b/test/912-classes/classes.cc index 69301c7925..a22d1d72d8 100644 --- a/test/912-classes/classes.cc +++ b/test/912-classes/classes.cc @@ -222,5 +222,24 @@ extern "C" JNIEXPORT jobject JNICALL Java_Main_getClassLoader( return classloader; } +extern "C" JNIEXPORT jobjectArray JNICALL Java_Main_getClassLoaderClasses( + JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jobject jclassloader) { + jint count = 0; + jclass* classes = nullptr; + jvmtiError result = jvmti_env->GetClassLoaderClasses(jclassloader, &count, &classes); + if (JvmtiErrorToException(env, result)) { + return nullptr; + } + + auto callback = [&](jint i) { + return classes[i]; + }; + jobjectArray ret = CreateObjectArray(env, count, "java/lang/Class", callback); + if (classes != nullptr) { + jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(classes)); + } + return ret; +} + } // namespace Test912Classes } // namespace art diff --git a/test/912-classes/expected.txt b/test/912-classes/expected.txt index 44c861a3b7..a95a465860 100644 --- a/test/912-classes/expected.txt +++ b/test/912-classes/expected.txt @@ -43,3 +43,19 @@ class java.lang.String null class [Ljava.lang.String; null interface Main$InfA dalvik.system.PathClassLoader class $Proxy0 dalvik.system.PathClassLoader + +boot <- src <- src-ex (A,B) +912-classes-ex.jar+ -> 912-classes.jar+ -> +[class A, class B, class java.lang.Object] +912-classes.jar+ -> +[class B, class java.lang.Object] + +boot <- src (B) <- src-ex (A, List) +912-classes-ex.jar+ -> 912-classes.jar+ -> +[class A, class java.lang.Object, interface java.util.List] +912-classes.jar+ -> +[class B, class java.lang.Object] + +boot <- src+src-ex (A,B) +912-classes.jar+ -> +[class A, class B, class java.lang.Object] diff --git a/test/912-classes/run b/test/912-classes/run index 4379349cb2..20dfc4b767 100755 --- a/test/912-classes/run +++ b/test/912-classes/run @@ -14,6 +14,11 @@ # See the License for the specific language governing permissions and # limitations under the License. +# This test checks which classes are initiated by a classloader. App images preload classes. +# In certain configurations, the app images may be valid even in a new classloader. Turn off +# app images to avoid the issue. + ./default-run "$@" --experimental agents \ --experimental runtime-plugins \ - --jvmti + --jvmti \ + --no-app-image diff --git a/test/912-classes/src-ex/A.java b/test/912-classes/src-ex/A.java new file mode 100644 index 0000000000..64acb2fcfe --- /dev/null +++ b/test/912-classes/src-ex/A.java @@ -0,0 +1,18 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +public class A { +}
\ No newline at end of file diff --git a/test/912-classes/src/B.java b/test/912-classes/src/B.java new file mode 100644 index 0000000000..f1458c3bca --- /dev/null +++ b/test/912-classes/src/B.java @@ -0,0 +1,18 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +public class B { +}
\ No newline at end of file diff --git a/test/912-classes/src/Main.java b/test/912-classes/src/Main.java index e627d4227a..ea3c49c87b 100644 --- a/test/912-classes/src/Main.java +++ b/test/912-classes/src/Main.java @@ -14,8 +14,10 @@ * limitations under the License. */ +import java.lang.reflect.Constructor; import java.lang.reflect.Proxy; import java.util.Arrays; +import java.util.Comparator; public class Main { public static void main(String[] args) throws Exception { @@ -76,6 +78,8 @@ public class Main { testClassLoader(String[].class); testClassLoader(InfA.class); testClassLoader(getProxyClass()); + + testClassLoaderClasses(); } private static Class<?> proxyClass = null; @@ -151,6 +155,95 @@ public class Main { } } + private static void testClassLoaderClasses() throws Exception { + ClassLoader boot = ClassLoader.getSystemClassLoader().getParent(); + while (boot.getParent() != null) { + boot = boot.getParent(); + } + + System.out.println(); + System.out.println("boot <- src <- src-ex (A,B)"); + ClassLoader cl1 = create(create(boot, DEX1), DEX2); + Class.forName("B", false, cl1); + Class.forName("A", false, cl1); + printClassLoaderClasses(cl1); + + System.out.println(); + System.out.println("boot <- src (B) <- src-ex (A, List)"); + ClassLoader cl2 = create(create(boot, DEX1), DEX2); + Class.forName("A", false, cl2); + Class.forName("java.util.List", false, cl2); + Class.forName("B", false, cl2.getParent()); + printClassLoaderClasses(cl2); + + System.out.println(); + System.out.println("boot <- src+src-ex (A,B)"); + ClassLoader cl3 = create(boot, DEX1, DEX2); + Class.forName("B", false, cl3); + Class.forName("A", false, cl3); + printClassLoaderClasses(cl3); + + // Check that the boot classloader dumps something non-empty. + Class<?>[] bootClasses = getClassLoaderClasses(boot); + if (bootClasses.length == 0) { + throw new RuntimeException("No classes initiated by boot classloader."); + } + // Check that at least java.util.List is loaded. + boolean foundList = false; + for (Class<?> c : bootClasses) { + if (c == java.util.List.class) { + foundList = true; + break; + } + } + if (!foundList) { + System.out.println(Arrays.toString(bootClasses)); + throw new RuntimeException("Could not find class java.util.List."); + } + } + + private static void printClassLoaderClasses(ClassLoader cl) { + for (;;) { + if (cl == null || !cl.getClass().getName().startsWith("dalvik.system")) { + break; + } + + ClassLoader saved = cl; + for (;;) { + if (cl == null || !cl.getClass().getName().startsWith("dalvik.system")) { + break; + } + String s = cl.toString(); + int index1 = s.indexOf("zip file"); + int index2 = s.indexOf(']', index1); + if (index2 < 0) { + throw new RuntimeException("Unexpected classloader " + s); + } + String zip_file = s.substring(index1, index2); + int index3 = zip_file.indexOf('"'); + int index4 = zip_file.indexOf('"', index3 + 1); + if (index4 < 0) { + throw new RuntimeException("Unexpected classloader " + s); + } + String paths = zip_file.substring(index3 + 1, index4); + String pathArray[] = paths.split(":"); + for (String path : pathArray) { + int index5 = path.lastIndexOf('/'); + System.out.print(path.substring(index5 + 1)); + System.out.print('+'); + } + System.out.print(" -> "); + cl = cl.getParent(); + } + System.out.println(); + Class<?> classes[] = getClassLoaderClasses(saved); + Arrays.sort(classes, new ClassNameComparator()); + System.out.println(Arrays.toString(classes)); + + cl = saved.getParent(); + } + } + private static native boolean isModifiableClass(Class<?> c); private static native String[] getClassSignature(Class<?> c); @@ -161,12 +254,14 @@ public class Main { private static native Object[] getClassFields(Class<?> c); private static native Object[] getClassMethods(Class<?> c); - private static native Class[] getImplementedInterfaces(Class<?> c); + private static native Class<?>[] getImplementedInterfaces(Class<?> c); private static native int getClassStatus(Class<?> c); private static native Object getClassLoader(Class<?> c); + private static native Class<?>[] getClassLoaderClasses(ClassLoader cl); + private static class TestForNonInit { public static double dummy = Math.random(); // So it can't be compile-time initialized. } @@ -188,4 +283,23 @@ public class Main { } public abstract static class ClassC implements InfA, InfC { } + + private static final String DEX1 = System.getenv("DEX_LOCATION") + "/912-classes.jar"; + private static final String DEX2 = System.getenv("DEX_LOCATION") + "/912-classes-ex.jar"; + + private static ClassLoader create(ClassLoader parent, String... elements) throws Exception { + // Note: We use a PathClassLoader, as we do not care about code performance. We only load + // the classes, and they're empty. + Class<?> pathClassLoaderClass = Class.forName("dalvik.system.PathClassLoader"); + Constructor<?> pathClassLoaderInit = pathClassLoaderClass.getConstructor(String.class, + ClassLoader.class); + String path = String.join(":", elements); + return (ClassLoader) pathClassLoaderInit.newInstance(path, parent); + } + + private static class ClassNameComparator implements Comparator<Class<?>> { + public int compare(Class<?> c1, Class<?> c2) { + return c1.getName().compareTo(c2.getName()); + } + } } diff --git a/test/916-obsolete-jit/src/Main.java b/test/916-obsolete-jit/src/Main.java index 74eb003d5c..1e43f7ee9d 100644 --- a/test/916-obsolete-jit/src/Main.java +++ b/test/916-obsolete-jit/src/Main.java @@ -157,38 +157,13 @@ public class Main { doCommonClassRedefinition(Transform.class, CLASS_BYTES, DEX_BYTES); } }; - // This does nothing. - Runnable noop = () -> {}; // This just prints something out to show we are running the Runnable. Runnable say_nothing = () -> { w.accept("Not doing anything here"); }; - // This checks to see if we have jitted the methods we are testing. - Runnable check_interpreting = () -> { - // TODO remove the second check when we remove the doCall function. We need to check that - // both of these functions aren't being interpreted because if sayHi is the test doesn't do - // anything and if doCall is then there will be a runtime call right above the sayHi - // function preventing sayHi from being deoptimized. - interpreting = has_jit && (Main.isInterpretedFunction(say_hi_method, true) || - Main.isInterpretedFunction(do_call_method, false)); - }; do { - w.clear(); - // Wait for the methods to be jitted - long j = 0; - do { - for (int i = 0; i < 10000; i++) { - t.sayHi(noop, w); - j++; - // Clear so that we won't OOM if we go around a few times. - w.clear(); - } - t.sayHi(check_interpreting, w); - if (j >= 1000000) { - System.out.println("FAIL: Could not make sayHi be Jitted!"); - return; - } - j++; - } while(interpreting); - // Clear output. Now we try for real. + // Run ensureJitCompiled here since it might get GCd + ensureJitCompiled(Transform.class, "sayHi"); + ensureJitCompiled(Main.class, "doCall"); + // Clear output. w.clear(); // Try and redefine. t.sayHi(say_nothing, w); @@ -203,6 +178,8 @@ public class Main { private static native boolean isInterpretedFunction(Method m, boolean require_deoptimizable); + private static native void ensureJitCompiled(Class c, String name); + // Transforms the class private static native void doCommonClassRedefinition(Class<?> target, byte[] classfile, diff --git a/test/953-invoke-polymorphic-compiler/run b/test/925-threadgroups/build index a9f182288c..898e2e54a2 100755 --- a/test/953-invoke-polymorphic-compiler/run +++ b/test/925-threadgroups/build @@ -14,7 +14,4 @@ # See the License for the specific language governing permissions and # limitations under the License. -# make us exit on a failure -set -e - -./default-run "$@" --experimental method-handles +./default-build "$@" --experimental agents diff --git a/test/925-threadgroups/expected.txt b/test/925-threadgroups/expected.txt new file mode 100644 index 0000000000..7d1a259c89 --- /dev/null +++ b/test/925-threadgroups/expected.txt @@ -0,0 +1,16 @@ +java.lang.ThreadGroup[name=main,maxpri=10] + java.lang.ThreadGroup[name=system,maxpri=10] + main + 10 + false +java.lang.ThreadGroup[name=system,maxpri=10] + null + system + 10 + false +main: + [Thread[main,5,main]] + [] +system: + [Thread[FinalizerDaemon,5,system], Thread[FinalizerWatchdogDaemon,5,system], Thread[HeapTaskDaemon,5,system], Thread[ReferenceQueueDaemon,5,system], Thread[Signal Catcher,5,system]] + [java.lang.ThreadGroup[name=main,maxpri=10]] diff --git a/test/925-threadgroups/info.txt b/test/925-threadgroups/info.txt new file mode 100644 index 0000000000..875a5f6ec1 --- /dev/null +++ b/test/925-threadgroups/info.txt @@ -0,0 +1 @@ +Tests basic functions in the jvmti plugin. diff --git a/test/955-methodhandles-smali/run b/test/925-threadgroups/run index a9f182288c..4379349cb2 100755 --- a/test/955-methodhandles-smali/run +++ b/test/925-threadgroups/run @@ -14,7 +14,6 @@ # See the License for the specific language governing permissions and # limitations under the License. -# make us exit on a failure -set -e - -./default-run "$@" --experimental method-handles +./default-run "$@" --experimental agents \ + --experimental runtime-plugins \ + --jvmti diff --git a/test/925-threadgroups/src/Main.java b/test/925-threadgroups/src/Main.java new file mode 100644 index 0000000000..c59efe2f7b --- /dev/null +++ b/test/925-threadgroups/src/Main.java @@ -0,0 +1,113 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import java.util.Arrays; +import java.util.Comparator; + +public class Main { + public static void main(String[] args) throws Exception { + System.loadLibrary(args[1]); + + doTest(); + } + + private static void doTest() throws Exception { + Thread t1 = Thread.currentThread(); + ThreadGroup curGroup = t1.getThreadGroup(); + + ThreadGroup rootGroup = curGroup; + while (rootGroup.getParent() != null) { + rootGroup = rootGroup.getParent(); + } + + ThreadGroup topGroups[] = getTopThreadGroups(); + if (topGroups == null || topGroups.length != 1 || topGroups[0] != rootGroup) { + System.out.println(Arrays.toString(topGroups)); + throw new RuntimeException("Unexpected topGroups"); + } + + printThreadGroupInfo(curGroup); + printThreadGroupInfo(rootGroup); + + waitGroupChildren(rootGroup, 5 /* # daemons */, 30 /* timeout in seconds */); + + checkChildren(curGroup); + } + + private static void printThreadGroupInfo(ThreadGroup tg) { + Object[] threadGroupInfo = getThreadGroupInfo(tg); + if (threadGroupInfo == null || threadGroupInfo.length != 4) { + System.out.println(Arrays.toString(threadGroupInfo)); + throw new RuntimeException("threadGroupInfo length wrong"); + } + + System.out.println(tg); + System.out.println(" " + threadGroupInfo[0]); // Parent + System.out.println(" " + threadGroupInfo[1]); // Name + System.out.println(" " + threadGroupInfo[2]); // Priority + System.out.println(" " + threadGroupInfo[3]); // Daemon + } + + private static void checkChildren(ThreadGroup tg) { + Object[] data = getThreadGroupChildren(tg); + Thread[] threads = (Thread[])data[0]; + ThreadGroup[] groups = (ThreadGroup[])data[1]; + + Arrays.sort(threads, THREAD_COMP); + Arrays.sort(groups, THREADGROUP_COMP); + System.out.println(tg.getName() + ":"); + System.out.println(" " + Arrays.toString(threads)); + System.out.println(" " + Arrays.toString(groups)); + + if (tg.getParent() != null) { + checkChildren(tg.getParent()); + } + } + + private static void waitGroupChildren(ThreadGroup tg, int expectedChildCount, int timeoutS) + throws Exception { + for (int i = 0; i < timeoutS; i++) { + Object[] data = getThreadGroupChildren(tg); + Thread[] threads = (Thread[])data[0]; + if (threads.length == expectedChildCount) { + return; + } + Thread.sleep(1000); + } + + Object[] data = getThreadGroupChildren(tg); + Thread[] threads = (Thread[])data[0]; + System.out.println(Arrays.toString(threads)); + throw new RuntimeException("Waited unsuccessfully for " + expectedChildCount + " children."); + } + + private final static Comparator<Thread> THREAD_COMP = new Comparator<Thread>() { + public int compare(Thread o1, Thread o2) { + return o1.getName().compareTo(o2.getName()); + } + }; + + private final static Comparator<ThreadGroup> THREADGROUP_COMP = new Comparator<ThreadGroup>() { + public int compare(ThreadGroup o1, ThreadGroup o2) { + return o1.getName().compareTo(o2.getName()); + } + }; + + private static native ThreadGroup[] getTopThreadGroups(); + private static native Object[] getThreadGroupInfo(ThreadGroup tg); + // Returns an array where element 0 is an array of threads and element 1 is an array of groups. + private static native Object[] getThreadGroupChildren(ThreadGroup tg); +} diff --git a/test/925-threadgroups/threadgroups.cc b/test/925-threadgroups/threadgroups.cc new file mode 100644 index 0000000000..6c6e835dd3 --- /dev/null +++ b/test/925-threadgroups/threadgroups.cc @@ -0,0 +1,127 @@ +/* + * 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. + */ + +#include <stdio.h> + +#include "android-base/stringprintf.h" +#include "base/macros.h" +#include "base/logging.h" +#include "jni.h" +#include "openjdkjvmti/jvmti.h" +#include "ScopedLocalRef.h" + +#include "ti-agent/common_helper.h" +#include "ti-agent/common_load.h" + +namespace art { +namespace Test925ThreadGroups { + +// private static native Object[] getThreadGroupInfo(); +// // Returns an array where element 0 is an array of threads and element 1 is an array of groups. +// private static native Object[] getThreadGroupChildren(); + +extern "C" JNIEXPORT jobjectArray JNICALL Java_Main_getTopThreadGroups( + JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED) { + jthreadGroup* groups; + jint group_count; + jvmtiError result = jvmti_env->GetTopThreadGroups(&group_count, &groups); + if (JvmtiErrorToException(env, result)) { + return nullptr; + } + + auto callback = [&](jint index) -> jobject { + return groups[index]; + }; + jobjectArray ret = CreateObjectArray(env, group_count, "java/lang/ThreadGroup", callback); + + jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(groups)); + + return ret; +} + +extern "C" JNIEXPORT jobjectArray JNICALL Java_Main_getThreadGroupInfo( + JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jthreadGroup group) { + jvmtiThreadGroupInfo info; + jvmtiError result = jvmti_env->GetThreadGroupInfo(group, &info); + if (JvmtiErrorToException(env, result)) { + return nullptr; + } + + auto callback = [&](jint index) -> jobject { + switch (index) { + // The parent. + case 0: + return info.parent; + + // The name. + case 1: + return (info.name == nullptr) ? nullptr : env->NewStringUTF(info.name); + + // The priority. Use a string for simplicity of construction. + case 2: + return env->NewStringUTF(android::base::StringPrintf("%d", info.max_priority).c_str()); + + // Whether it's a daemon. Use a string for simplicity of construction. + case 3: + return env->NewStringUTF(info.is_daemon == JNI_TRUE ? "true" : "false"); + } + LOG(FATAL) << "Should not reach here"; + UNREACHABLE(); + }; + return CreateObjectArray(env, 4, "java/lang/Object", callback); +} + +extern "C" JNIEXPORT jobjectArray JNICALL Java_Main_getThreadGroupChildren( + JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jthreadGroup group) { + jint thread_count; + jthread* threads; + jint threadgroup_count; + jthreadGroup* groups; + + jvmtiError result = jvmti_env->GetThreadGroupChildren(group, + &thread_count, + &threads, + &threadgroup_count, + &groups); + if (JvmtiErrorToException(env, result)) { + return nullptr; + } + + auto callback = [&](jint component_index) -> jobject { + if (component_index == 0) { + // Threads. + auto inner_callback = [&](jint index) { + return threads[index]; + }; + return CreateObjectArray(env, thread_count, "java/lang/Thread", inner_callback); + } else { + // Groups. + auto inner_callback = [&](jint index) { + return groups[index]; + }; + return CreateObjectArray(env, threadgroup_count, "java/lang/ThreadGroup", inner_callback); + } + }; + jobjectArray ret = CreateObjectArray(env, 2, "java/lang/Object", callback); + + jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(threads)); + jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(groups)); + + return ret; +} + +} // namespace Test925ThreadGroups +} // namespace art diff --git a/test/954-invoke-polymorphic-verifier/run b/test/927-timers/build index a9f182288c..898e2e54a2 100755 --- a/test/954-invoke-polymorphic-verifier/run +++ b/test/927-timers/build @@ -14,7 +14,4 @@ # See the License for the specific language governing permissions and # limitations under the License. -# make us exit on a failure -set -e - -./default-run "$@" --experimental method-handles +./default-build "$@" --experimental agents diff --git a/test/927-timers/expected.txt b/test/927-timers/expected.txt new file mode 100644 index 0000000000..a4ef4424b6 --- /dev/null +++ b/test/927-timers/expected.txt @@ -0,0 +1,3 @@ +availableProcessors OK +[-1, true, true, 32] +Time OK diff --git a/test/927-timers/info.txt b/test/927-timers/info.txt new file mode 100644 index 0000000000..875a5f6ec1 --- /dev/null +++ b/test/927-timers/info.txt @@ -0,0 +1 @@ +Tests basic functions in the jvmti plugin. diff --git a/test/956-methodhandles/run b/test/927-timers/run index a9f182288c..4379349cb2 100755 --- a/test/956-methodhandles/run +++ b/test/927-timers/run @@ -14,7 +14,6 @@ # See the License for the specific language governing permissions and # limitations under the License. -# make us exit on a failure -set -e - -./default-run "$@" --experimental method-handles +./default-run "$@" --experimental agents \ + --experimental runtime-plugins \ + --jvmti diff --git a/test/927-timers/src/Main.java b/test/927-timers/src/Main.java new file mode 100644 index 0000000000..2f5c85cab5 --- /dev/null +++ b/test/927-timers/src/Main.java @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import java.util.Arrays; + +public class Main { + public static void main(String[] args) throws Exception { + System.loadLibrary(args[1]); + + doTest(); + } + + private static void doTest() { + int all1 = Runtime.getRuntime().availableProcessors(); + int all2 = getAvailableProcessors(); + if (all1 != all2) { + throw new RuntimeException("Available processors doesn't match: " + all1 + " vs " + all2); + } + System.out.println("availableProcessors OK"); + + Object info[] = getTimerInfo(); + System.out.println(Arrays.toString(info)); + + // getTime checks. + // Note: there isn't really much to check independent from the implementation. So we check + // a few details of the ART implementation. This may fail on other runtimes. + long time1 = getTime(); + long time2 = getTime(); + + // Under normal circumstances, time1 <= time2. + if (time2 < time1) { + throw new RuntimeException("Time unexpectedly decreased: " + time1 + " vs " + time2); + } + + long time3 = System.nanoTime(); + long time4 = getTime(); + + final long MINUTE = 60l * 1000 * 1000 * 1000; + if (time4 < time3 || (time4 - time3 > MINUTE)) { + throw new RuntimeException("Time unexpectedly divergent: " + time3 + " vs " + time4); + } + + System.out.println("Time OK"); + } + + private static native int getAvailableProcessors(); + private static native Object[] getTimerInfo(); + private static native long getTime(); +} diff --git a/test/927-timers/timers.cc b/test/927-timers/timers.cc new file mode 100644 index 0000000000..58d5c271e6 --- /dev/null +++ b/test/927-timers/timers.cc @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <inttypes.h> + +#include "android-base/stringprintf.h" +#include "base/logging.h" +#include "base/macros.h" +#include "jni.h" +#include "openjdkjvmti/jvmti.h" + +#include "ti-agent/common_helper.h" +#include "ti-agent/common_load.h" + +namespace art { +namespace Test926Timers { + +extern "C" JNIEXPORT jint JNICALL Java_Main_getAvailableProcessors( + JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED) { + jint count; + jvmtiError result = jvmti_env->GetAvailableProcessors(&count); + if (JvmtiErrorToException(env, result)) { + return -1; + } + return count; +} + +extern "C" JNIEXPORT jlong JNICALL Java_Main_getTime( + JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED) { + jlong time; + jvmtiError result = jvmti_env->GetTime(&time); + if (JvmtiErrorToException(env, result)) { + return -1; + } + return time; +} + +extern "C" JNIEXPORT jobjectArray JNICALL Java_Main_getTimerInfo( + JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED) { + jvmtiTimerInfo info; + jvmtiError result = jvmti_env->GetTimerInfo(&info); + if (JvmtiErrorToException(env, result)) { + return nullptr; + } + + auto callback = [&](jint index) -> jobject { + switch (index) { + // Max value. + case 0: + return env->NewStringUTF(android::base::StringPrintf("%" PRId64, info.max_value).c_str()); + + // Skip forward. + case 1: + return env->NewStringUTF(info.may_skip_forward == JNI_TRUE ? "true" : "false"); + // Skip backward. + case 2: + return env->NewStringUTF(info.may_skip_forward == JNI_TRUE ? "true" : "false"); + + // The kind. + case 3: + return env->NewStringUTF( + android::base::StringPrintf("%d", static_cast<jint>(info.kind)).c_str()); + } + LOG(FATAL) << "Should not reach here"; + UNREACHABLE(); + }; + return CreateObjectArray(env, 4, "java/lang/Object", callback); +} + +} // namespace Test926Timers +} // namespace art diff --git a/test/956-methodhandles/src/Main.java b/test/956-methodhandles/src/Main.java index 17b56b4fd9..f8daba6239 100644 --- a/test/956-methodhandles/src/Main.java +++ b/test/956-methodhandles/src/Main.java @@ -76,6 +76,7 @@ public class Main { testStringConstructors(); testReturnValueConversions(); testVariableArity(); + testVariableArity_MethodHandles_bind(); } public static void testfindSpecial_invokeSuperBehaviour() throws Throwable { @@ -1466,4 +1467,23 @@ public class Main { fail(); } catch (WrongMethodTypeException e) {} } + + // The same tests as the above, except that we use use MethodHandles.bind instead of + // MethodHandle.bindTo. + public static void testVariableArity_MethodHandles_bind() throws Throwable { + VariableArityTester vat = new VariableArityTester(); + MethodHandle mh = MethodHandles.lookup().bind(vat, "update", + MethodType.methodType(String.class, boolean[].class)); + assertTrue(mh.isVarargsCollector()); + + assertEquals("[]", mh.invoke()); + assertEquals("[true, false, true]", mh.invoke(true, false, true)); + assertEquals("[true, false, true]", mh.invoke(new boolean[] { true, false, true})); + assertEquals("[false, true]", mh.invoke(Boolean.valueOf(false), Boolean.valueOf(true))); + + try { + mh.invoke(true, true, 0); + fail(); + } catch (WrongMethodTypeException e) {} + } } diff --git a/test/957-methodhandle-transforms/run b/test/957-methodhandle-transforms/run deleted file mode 100755 index a9f182288c..0000000000 --- a/test/957-methodhandle-transforms/run +++ /dev/null @@ -1,20 +0,0 @@ -#!/bin/bash -# -# Copyright 2016 The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# make us exit on a failure -set -e - -./default-run "$@" --experimental method-handles diff --git a/test/957-methodhandle-transforms/src/Main.java b/test/957-methodhandle-transforms/src/Main.java index 9e79ff4c10..eebf55fb61 100644 --- a/test/957-methodhandle-transforms/src/Main.java +++ b/test/957-methodhandle-transforms/src/Main.java @@ -33,6 +33,7 @@ public class Main { testBindTo(); testFilterReturnValue(); testPermuteArguments(); + testInvokers(); } public static void testThrowException() throws Throwable { @@ -888,6 +889,38 @@ public class Main { } } + private static Object returnBar() { + return "bar"; + } + + public static void testInvokers() throws Throwable { + final MethodType targetType = MethodType.methodType(String.class, String.class); + final MethodHandle target = MethodHandles.lookup().findVirtual( + String.class, "concat", targetType); + + MethodHandle invoker = MethodHandles.invoker(target.type()); + assertEquals("barbar", (String) invoker.invoke(target, "bar", "bar")); + assertEquals("barbar", (String) invoker.invoke(target, (Object) returnBar(), "bar")); + try { + String foo = (String) invoker.invoke(target, "bar", "bar", 24); + fail(); + } catch (WrongMethodTypeException expected) { + } + + MethodHandle exactInvoker = MethodHandles.exactInvoker(target.type()); + assertEquals("barbar", (String) exactInvoker.invoke(target, "bar", "bar")); + try { + String foo = (String) exactInvoker.invoke(target, (Object) returnBar(), "bar"); + fail(); + } catch (WrongMethodTypeException expected) { + } + try { + String foo = (String) exactInvoker.invoke(target, "bar", "bar", 24); + fail(); + } catch (WrongMethodTypeException expected) { + } + } + public static void fail() { System.out.println("FAIL"); Thread.dumpStack(); diff --git a/test/958-methodhandle-emulated-stackframe/run b/test/958-methodhandle-emulated-stackframe/run deleted file mode 100755 index a9f182288c..0000000000 --- a/test/958-methodhandle-emulated-stackframe/run +++ /dev/null @@ -1,20 +0,0 @@ -#!/bin/bash -# -# Copyright 2016 The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# make us exit on a failure -set -e - -./default-run "$@" --experimental method-handles diff --git a/test/959-invoke-polymorphic-accessors/run b/test/959-invoke-polymorphic-accessors/run deleted file mode 100644 index a9f182288c..0000000000 --- a/test/959-invoke-polymorphic-accessors/run +++ /dev/null @@ -1,20 +0,0 @@ -#!/bin/bash -# -# Copyright 2016 The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# make us exit on a failure -set -e - -./default-run "$@" --experimental method-handles diff --git a/test/Android.bp b/test/Android.bp index b0f0e5a98d..c551b9de45 100644 --- a/test/Android.bp +++ b/test/Android.bp @@ -265,6 +265,8 @@ art_cc_defaults { "922-properties/properties.cc", "923-monitors/monitors.cc", "924-threads/threads.cc", + "925-threadgroups/threadgroups.cc", + "927-timers/timers.cc", ], shared_libs: [ "libbase", diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk index 60688760a4..9da96de6a6 100644 --- a/test/Android.run-test.mk +++ b/test/Android.run-test.mk @@ -303,7 +303,9 @@ TEST_ART_BROKEN_TARGET_TESTS += \ 922-properties \ 923-monitors \ 924-threads \ + 925-threadgroups \ 926-multi-obsolescence \ + 927-timers \ ifneq (,$(filter target,$(TARGET_TYPES))) ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,target,$(RUN_TYPES),$(PREBUILD_TYPES), \ @@ -464,10 +466,12 @@ TEST_ART_BROKEN_FALLBACK_RUN_TESTS := \ 629-vdex-speed # This test fails without an image. -# 964 often times out due to the large number of classes it tries to compile. +# 018, 961, 964 often time out. b/34369284 TEST_ART_BROKEN_NO_IMAGE_RUN_TESTS := \ 137-cfi \ 138-duplicate-classes-check \ + 018-stack-overflow \ + 961-default-iface-resolution-gen \ 964-default-iface-init ifneq (,$(filter no-dex2oat,$(PREBUILD_TYPES))) diff --git a/test/common/runtime_state.cc b/test/common/runtime_state.cc index 1b6fc7033a..a841f9e6a2 100644 --- a/test/common/runtime_state.cc +++ b/test/common/runtime_state.cc @@ -33,12 +33,18 @@ namespace art { // public static native boolean hasJit(); -extern "C" JNIEXPORT jboolean JNICALL Java_Main_hasJit(JNIEnv*, jclass) { +static jit::Jit* GetJitIfEnabled() { Runtime* runtime = Runtime::Current(); - return runtime != nullptr + bool can_jit = + runtime != nullptr && runtime->GetJit() != nullptr && runtime->GetInstrumentation()->GetCurrentInstrumentationLevel() != instrumentation::Instrumentation::InstrumentationLevel::kInstrumentWithInterpreter; + return can_jit ? runtime->GetJit() : nullptr; +} + +extern "C" JNIEXPORT jboolean JNICALL Java_Main_hasJit(JNIEnv*, jclass) { + return GetJitIfEnabled() != nullptr; } // public static native boolean hasOatFile(); @@ -152,7 +158,7 @@ extern "C" JNIEXPORT void JNICALL Java_Main_ensureJitCompiled(JNIEnv* env, jclass, jclass cls, jstring method_name) { - jit::Jit* jit = Runtime::Current()->GetJit(); + jit::Jit* jit = GetJitIfEnabled(); if (jit == nullptr) { return; } @@ -166,6 +172,11 @@ extern "C" JNIEXPORT void JNICALL Java_Main_ensureJitCompiled(JNIEnv* env, CHECK(chars.c_str() != nullptr); method = soa.Decode<mirror::Class>(cls)->FindDeclaredDirectMethodByName( chars.c_str(), kRuntimePointerSize); + if (method == nullptr) { + method = soa.Decode<mirror::Class>(cls)->FindDeclaredVirtualMethodByName( + chars.c_str(), kRuntimePointerSize); + } + DCHECK(method != nullptr) << "Unable to find method called " << chars.c_str(); } jit::JitCodeCache* code_cache = jit->GetCodeCache(); diff --git a/test/etc/run-test-jar b/test/etc/run-test-jar index 4794f6b7c1..5f1071f658 100755 --- a/test/etc/run-test-jar +++ b/test/etc/run-test-jar @@ -62,6 +62,7 @@ DRY_RUN="n" # if y prepare to run the test but don't run it. TEST_VDEX="n" TEST_IS_NDEBUG="n" APP_IMAGE="y" +VDEX_FILTER="" while true; do if [ "x$1" = "x--quiet" ]; then @@ -256,6 +257,11 @@ while true; do elif [ "x$1" = "x--vdex" ]; then TEST_VDEX="y" shift + elif [ "x$1" = "x--vdex-filter" ]; then + shift + option="$1" + VDEX_FILTER="--compiler-filter=$option" + shift elif expr "x$1" : "x--" >/dev/null 2>&1; then echo "unknown $0 option: $1" 1>&2 exit 1 @@ -514,7 +520,7 @@ if [ "$PREBUILD" = "y" ]; then dex2oat_cmdline="timeout -k 1m -s SIGRTMIN+2 1m ${dex2oat_cmdline}" fi if [ "$TEST_VDEX" = "y" ]; then - vdex_cmdline="${dex2oat_cmdline} --input-vdex=$DEX_LOCATION/oat/$ISA/$TEST_NAME.vdex" + vdex_cmdline="${dex2oat_cmdline} ${VDEX_FILTER} --input-vdex=$DEX_LOCATION/oat/$ISA/$TEST_NAME.vdex" fi fi diff --git a/test/run-test b/test/run-test index abe73c3159..a913e783d3 100755 --- a/test/run-test +++ b/test/run-test @@ -354,6 +354,11 @@ while true; do elif [ "x$1" = "x--vdex" ]; then run_args="${run_args} --vdex" shift + elif [ "x$1" = "x--vdex-filter" ]; then + shift + filter=$1 + run_args="${run_args} --vdex-filter $filter" + shift elif expr "x$1" : "x--" >/dev/null 2>&1; then echo "unknown $0 option: $1" 1>&2 usage="yes" |