diff options
Diffstat (limited to 'compiler')
36 files changed, 1053 insertions, 514 deletions
diff --git a/compiler/compiler.h b/compiler/compiler.h index 908d3669ed..2ca0b77a73 100644 --- a/compiler/compiler.h +++ b/compiler/compiler.h @@ -27,7 +27,6 @@ namespace jit { class JitCodeCache; } namespace mirror { - class ClassLoader; class DexCache; } @@ -64,7 +63,7 @@ class Compiler { InvokeType invoke_type, uint16_t class_def_idx, uint32_t method_idx, - Handle<mirror::ClassLoader> class_loader, + jobject class_loader, const DexFile& dex_file, Handle<mirror::DexCache> dex_cache) const = 0; diff --git a/compiler/dex/dex_to_dex_compiler.cc b/compiler/dex/dex_to_dex_compiler.cc index 76aeaa55d7..d4f6545c59 100644 --- a/compiler/dex/dex_to_dex_compiler.cc +++ b/compiler/dex/dex_to_dex_compiler.cc @@ -284,13 +284,16 @@ void DexCompiler::CompileInvokeVirtual(Instruction* inst, uint32_t dex_pc, } uint32_t method_idx = is_range ? inst->VRegB_3rc() : inst->VRegB_35c(); ScopedObjectAccess soa(Thread::Current()); + StackHandleScope<1> hs(soa.Self()); + Handle<mirror::ClassLoader> class_loader(hs.NewHandle( + soa.Decode<mirror::ClassLoader>(unit_.GetClassLoader()))); ClassLinker* class_linker = unit_.GetClassLinker(); ArtMethod* resolved_method = class_linker->ResolveMethod<ClassLinker::kForceICCECheck>( GetDexFile(), method_idx, unit_.GetDexCache(), - unit_.GetClassLoader(), + class_loader, /* referrer */ nullptr, kVirtual); @@ -327,7 +330,7 @@ CompiledMethod* ArtCompileDEX( InvokeType invoke_type ATTRIBUTE_UNUSED, uint16_t class_def_idx, uint32_t method_idx, - Handle<mirror::ClassLoader> class_loader, + jobject class_loader, const DexFile& dex_file, DexToDexCompilationLevel dex_to_dex_compilation_level) { DCHECK(driver != nullptr); diff --git a/compiler/dex/dex_to_dex_compiler.h b/compiler/dex/dex_to_dex_compiler.h index 00c596d60e..0a00d45297 100644 --- a/compiler/dex/dex_to_dex_compiler.h +++ b/compiler/dex/dex_to_dex_compiler.h @@ -18,7 +18,6 @@ #define ART_COMPILER_DEX_DEX_TO_DEX_COMPILER_H_ #include "dex_file.h" -#include "handle.h" #include "invoke_type.h" namespace art { @@ -26,10 +25,6 @@ namespace art { class CompiledMethod; class CompilerDriver; -namespace mirror { -class ClassLoader; -} // namespace mirror - namespace optimizer { enum class DexToDexCompilationLevel { @@ -45,7 +40,7 @@ CompiledMethod* ArtCompileDEX(CompilerDriver* driver, InvokeType invoke_type, uint16_t class_def_idx, uint32_t method_idx, - Handle<mirror::ClassLoader> class_loader, + jobject class_loader, const DexFile& dex_file, DexToDexCompilationLevel dex_to_dex_compilation_level); diff --git a/compiler/driver/compiler_driver-inl.h b/compiler/driver/compiler_driver-inl.h index 582330611d..f296851ebf 100644 --- a/compiler/driver/compiler_driver-inl.h +++ b/compiler/driver/compiler_driver-inl.h @@ -31,12 +31,17 @@ namespace art { +inline mirror::ClassLoader* CompilerDriver::GetClassLoader(const ScopedObjectAccess& soa, + const DexCompilationUnit* mUnit) { + return soa.Decode<mirror::ClassLoader>(mUnit->GetClassLoader()).Ptr(); +} + inline mirror::Class* CompilerDriver::ResolveClass( const ScopedObjectAccess& soa, Handle<mirror::DexCache> dex_cache, Handle<mirror::ClassLoader> class_loader, dex::TypeIndex cls_index, const DexCompilationUnit* mUnit) { DCHECK_EQ(dex_cache->GetDexFile(), mUnit->GetDexFile()); - DCHECK_EQ(class_loader.Get(), mUnit->GetClassLoader().Get()); + DCHECK_EQ(class_loader.Get(), GetClassLoader(soa, mUnit)); mirror::Class* cls = mUnit->GetClassLinker()->ResolveType( *mUnit->GetDexFile(), cls_index, dex_cache, class_loader); DCHECK_EQ(cls == nullptr, soa.Self()->IsExceptionPending()); @@ -51,7 +56,7 @@ inline mirror::Class* CompilerDriver::ResolveCompilingMethodsClass( const ScopedObjectAccess& soa, Handle<mirror::DexCache> dex_cache, Handle<mirror::ClassLoader> class_loader, const DexCompilationUnit* mUnit) { DCHECK_EQ(dex_cache->GetDexFile(), mUnit->GetDexFile()); - DCHECK_EQ(class_loader.Get(), mUnit->GetClassLoader().Get()); + DCHECK_EQ(class_loader.Get(), GetClassLoader(soa, mUnit)); const DexFile::MethodId& referrer_method_id = mUnit->GetDexFile()->GetMethodId(mUnit->GetDexMethodIndex()); return ResolveClass(soa, dex_cache, class_loader, referrer_method_id.class_idx_, mUnit); @@ -82,7 +87,7 @@ inline ArtField* CompilerDriver::ResolveField( const ScopedObjectAccess& soa, Handle<mirror::DexCache> dex_cache, Handle<mirror::ClassLoader> class_loader, const DexCompilationUnit* mUnit, uint32_t field_idx, bool is_static) { - DCHECK_EQ(class_loader.Get(), mUnit->GetClassLoader().Get()); + DCHECK_EQ(class_loader.Get(), GetClassLoader(soa, mUnit)); return ResolveFieldWithDexFile(soa, dex_cache, class_loader, mUnit->GetDexFile(), field_idx, is_static); } @@ -134,7 +139,7 @@ inline ArtMethod* CompilerDriver::ResolveMethod( ScopedObjectAccess& soa, Handle<mirror::DexCache> dex_cache, Handle<mirror::ClassLoader> class_loader, const DexCompilationUnit* mUnit, uint32_t method_idx, InvokeType invoke_type, bool check_incompatible_class_change) { - DCHECK_EQ(class_loader.Get(), mUnit->GetClassLoader().Get()); + DCHECK_EQ(class_loader.Get(), GetClassLoader(soa, mUnit)); ArtMethod* resolved_method = check_incompatible_class_change ? mUnit->GetClassLinker()->ResolveMethod<ClassLinker::kForceICCECheck>( diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc index b738d5ce7e..26c0818b85 100644 --- a/compiler/driver/compiler_driver.cc +++ b/compiler/driver/compiler_driver.cc @@ -311,9 +311,6 @@ CompilerDriver::CompilerDriver( compiler_->Init(); - if (compiler_options->VerifyOnlyProfile()) { - CHECK(profile_compilation_info_ != nullptr) << "Requires profile"; - } if (GetCompilerOptions().IsBootImage()) { CHECK(image_classes_.get() != nullptr) << "Expected image classes for boot image"; } @@ -583,7 +580,7 @@ static void CompileMethod(Thread* self, InvokeType invoke_type, uint16_t class_def_idx, uint32_t method_idx, - Handle<mirror::ClassLoader> class_loader, + jobject class_loader, const DexFile& dex_file, optimizer::DexToDexCompilationLevel dex_to_dex_compilation_level, bool compilation_enabled, @@ -624,6 +621,9 @@ static void CompileMethod(Thread* self, // Look-up the ArtMethod associated with this code_item (if any) // -- It is later used to lookup any [optimization] annotations for this method. ScopedObjectAccess soa(self); + StackHandleScope<1> hs(soa.Self()); + Handle<mirror::ClassLoader> class_loader_handle(hs.NewHandle( + soa.Decode<mirror::ClassLoader>(class_loader))); // TODO: Lookup annotation from DexFile directly without resolving method. ArtMethod* method = @@ -631,7 +631,7 @@ static void CompileMethod(Thread* self, dex_file, method_idx, dex_cache, - class_loader, + class_loader_handle, /* referrer */ nullptr, invoke_type); @@ -678,14 +678,9 @@ static void CompileMethod(Thread* self, if (compile) { // NOTE: if compiler declines to compile this method, it will return null. - compiled_method = driver->GetCompiler()->Compile(code_item, - access_flags, - invoke_type, - class_def_idx, - method_idx, - class_loader, - dex_file, - dex_cache); + compiled_method = driver->GetCompiler()->Compile(code_item, access_flags, invoke_type, + class_def_idx, method_idx, class_loader, + dex_file, dex_cache); } if (compiled_method == nullptr && dex_to_dex_compilation_level != optimizer::DexToDexCompilationLevel::kDontDexToDexCompile) { @@ -732,14 +727,12 @@ void CompilerDriver::CompileOne(Thread* self, ArtMethod* method, TimingLogger* t uint32_t method_idx = method->GetDexMethodIndex(); uint32_t access_flags = method->GetAccessFlags(); InvokeType invoke_type = method->GetInvokeType(); - StackHandleScope<2> hs(self); + StackHandleScope<1> hs(self); Handle<mirror::DexCache> dex_cache(hs.NewHandle(method->GetDexCache())); - Handle<mirror::ClassLoader> class_loader( - hs.NewHandle(method->GetDeclaringClass()->GetClassLoader())); { ScopedObjectAccessUnchecked soa(self); ScopedLocalRef<jobject> local_class_loader( - soa.Env(), soa.AddLocalReference<jobject>(class_loader.Get())); + soa.Env(), soa.AddLocalReference<jobject>(method->GetDeclaringClass()->GetClassLoader())); jclass_loader = soa.Env()->NewGlobalRef(local_class_loader.get()); // Find the dex_file dex_file = method->GetDexFile(); @@ -773,7 +766,7 @@ void CompilerDriver::CompileOne(Thread* self, ArtMethod* method, TimingLogger* t invoke_type, class_def_idx, method_idx, - class_loader, + jclass_loader, *dex_file, dex_to_dex_compilation_level, true, @@ -799,7 +792,7 @@ void CompilerDriver::CompileOne(Thread* self, ArtMethod* method, TimingLogger* t invoke_type, class_def_idx, method_idx, - class_loader, + jclass_loader, *dex_file, dex_to_dex_compilation_level, true, @@ -964,7 +957,7 @@ static void EnsureVerifiedOrVerifyAtRuntime(jobject jclass_loader, const DexFile::ClassDef& class_def = dex_file->GetClassDef(i); const char* descriptor = dex_file->GetClassDescriptor(class_def); cls.Assign(class_linker->FindClass(soa.Self(), descriptor, class_loader)); - if (cls.Get() == nullptr) { + if (cls == nullptr) { soa.Self()->ClearException(); } else if (&cls->GetDexFile() == dex_file) { DCHECK(cls->IsErroneous() || cls->IsVerified() || cls->IsCompileTimeVerified()) @@ -1074,30 +1067,22 @@ bool CompilerDriver::ShouldCompileBasedOnProfile(const MethodReference& method_r class ResolveCatchBlockExceptionsClassVisitor : public ClassVisitor { public: - ResolveCatchBlockExceptionsClassVisitor() : classes_() {} + explicit ResolveCatchBlockExceptionsClassVisitor( + std::set<std::pair<dex::TypeIndex, const DexFile*>>& exceptions_to_resolve) + : exceptions_to_resolve_(exceptions_to_resolve) {} virtual bool operator()(ObjPtr<mirror::Class> c) OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_) { - classes_.push_back(c); - return true; - } - - void FindExceptionTypesToResolve( - std::set<std::pair<dex::TypeIndex, const DexFile*>>* exceptions_to_resolve) - REQUIRES_SHARED(Locks::mutator_lock_) { const auto pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize(); - for (ObjPtr<mirror::Class> klass : classes_) { - for (ArtMethod& method : klass->GetMethods(pointer_size)) { - FindExceptionTypesToResolveForMethod(&method, exceptions_to_resolve); - } + for (auto& m : c->GetMethods(pointer_size)) { + ResolveExceptionsForMethod(&m); } + return true; } private: - void FindExceptionTypesToResolveForMethod( - ArtMethod* method, - std::set<std::pair<dex::TypeIndex, const DexFile*>>* exceptions_to_resolve) + void ResolveExceptionsForMethod(ArtMethod* method_handle) REQUIRES_SHARED(Locks::mutator_lock_) { - const DexFile::CodeItem* code_item = method->GetCodeItem(); + const DexFile::CodeItem* code_item = method_handle->GetCodeItem(); if (code_item == nullptr) { return; // native or abstract method } @@ -1117,9 +1102,9 @@ 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->IsResolvedTypeIdx(encoded_catch_handler_handlers_type_idx)) { - exceptions_to_resolve->emplace(encoded_catch_handler_handlers_type_idx, - method->GetDexFile()); + if (!method_handle->IsResolvedTypeIdx(encoded_catch_handler_handlers_type_idx)) { + exceptions_to_resolve_.emplace(encoded_catch_handler_handlers_type_idx, + method_handle->GetDexFile()); } // ignore address associated with catch handler DecodeUnsignedLeb128(&encoded_catch_handler_list); @@ -1131,7 +1116,7 @@ class ResolveCatchBlockExceptionsClassVisitor : public ClassVisitor { } } - std::vector<ObjPtr<mirror::Class>> classes_; + std::set<std::pair<dex::TypeIndex, const DexFile*>>& exceptions_to_resolve_; }; class RecordImageClassesVisitor : public ClassVisitor { @@ -1167,7 +1152,7 @@ void CompilerDriver::LoadImageClasses(TimingLogger* timings) { StackHandleScope<1> hs(self); Handle<mirror::Class> klass( hs.NewHandle(class_linker->FindSystemClass(self, descriptor.c_str()))); - if (klass.Get() == nullptr) { + if (klass == nullptr) { VLOG(compiler) << "Failed to find class " << descriptor; image_classes_->erase(it++); self->ClearException(); @@ -1185,14 +1170,8 @@ void CompilerDriver::LoadImageClasses(TimingLogger* timings) { hs.NewHandle(class_linker->FindSystemClass(self, "Ljava/lang/Throwable;"))); do { unresolved_exception_types.clear(); - { - // Thread suspension is not allowed while ResolveCatchBlockExceptionsClassVisitor - // is using a std::vector<ObjPtr<mirror::Class>>. - ScopedAssertNoThreadSuspension ants(__FUNCTION__); - ResolveCatchBlockExceptionsClassVisitor visitor; - class_linker->VisitClasses(&visitor); - visitor.FindExceptionTypesToResolve(&unresolved_exception_types); - } + ResolveCatchBlockExceptionsClassVisitor visitor(unresolved_exception_types); + class_linker->VisitClasses(&visitor); for (const auto& exception_type : unresolved_exception_types) { dex::TypeIndex exception_type_idx = exception_type.first; const DexFile* dex_file = exception_type.second; @@ -1200,13 +1179,13 @@ void CompilerDriver::LoadImageClasses(TimingLogger* timings) { Handle<mirror::DexCache> dex_cache(hs2.NewHandle(class_linker->RegisterDexFile(*dex_file, nullptr))); Handle<mirror::Class> klass(hs2.NewHandle( - (dex_cache.Get() != nullptr) + (dex_cache != nullptr) ? class_linker->ResolveType(*dex_file, exception_type_idx, dex_cache, ScopedNullHandle<mirror::ClassLoader>()) : nullptr)); - if (klass.Get() == nullptr) { + if (klass == nullptr) { const DexFile::TypeId& type_id = dex_file->GetTypeId(exception_type_idx); const char* descriptor = dex_file->GetTypeDescriptor(type_id); LOG(FATAL) << "Failed to resolve class " << descriptor; @@ -1443,14 +1422,19 @@ void CompilerDriver::MarkForDexToDexCompilation(Thread* self, const MethodRefere dex_to_dex_references_.back().GetMethodIndexes().SetBit(method_ref.dex_method_index); } -bool CompilerDriver::CanAccessTypeWithoutChecks(ObjPtr<mirror::Class> referrer_class, - ObjPtr<mirror::Class> resolved_class) { +bool CompilerDriver::CanAccessTypeWithoutChecks(uint32_t referrer_idx, + Handle<mirror::DexCache> dex_cache, + dex::TypeIndex type_idx) { + // Get type from dex cache assuming it was populated by the verifier + mirror::Class* resolved_class = dex_cache->GetResolvedType(type_idx); if (resolved_class == nullptr) { stats_->TypeNeedsAccessCheck(); return false; // Unknown class needs access checks. } + const DexFile::MethodId& method_id = dex_cache->GetDexFile()->GetMethodId(referrer_idx); bool is_accessible = resolved_class->IsPublic(); // Public classes are always accessible. if (!is_accessible) { + mirror::Class* referrer_class = dex_cache->GetResolvedType(method_id.class_idx_); if (referrer_class == nullptr) { stats_->TypeNeedsAccessCheck(); return false; // Incomplete referrer knowledge needs access check. @@ -1467,9 +1451,12 @@ bool CompilerDriver::CanAccessTypeWithoutChecks(ObjPtr<mirror::Class> referrer_c return is_accessible; } -bool CompilerDriver::CanAccessInstantiableTypeWithoutChecks(ObjPtr<mirror::Class> referrer_class, - ObjPtr<mirror::Class> resolved_class, +bool CompilerDriver::CanAccessInstantiableTypeWithoutChecks(uint32_t referrer_idx, + Handle<mirror::DexCache> dex_cache, + dex::TypeIndex type_idx, bool* finalizable) { + // Get type from dex cache assuming it was populated by the verifier. + mirror::Class* resolved_class = dex_cache->GetResolvedType(type_idx); if (resolved_class == nullptr) { stats_->TypeNeedsAccessCheck(); // Be conservative. @@ -1477,8 +1464,10 @@ bool CompilerDriver::CanAccessInstantiableTypeWithoutChecks(ObjPtr<mirror::Class return false; // Unknown class needs access checks. } *finalizable = resolved_class->IsFinalizable(); + const DexFile::MethodId& method_id = dex_cache->GetDexFile()->GetMethodId(referrer_idx); bool is_accessible = resolved_class->IsPublic(); // Public classes are always accessible. if (!is_accessible) { + mirror::Class* referrer_class = dex_cache->GetResolvedType(method_id.class_idx_); if (referrer_class == nullptr) { stats_->TypeNeedsAccessCheck(); return false; // Incomplete referrer knowledge needs access check. @@ -1522,7 +1511,9 @@ ArtField* CompilerDriver::ComputeInstanceFieldInfo(uint32_t field_idx, mirror::Class* referrer_class; Handle<mirror::DexCache> dex_cache(mUnit->GetDexCache()); { - Handle<mirror::ClassLoader> class_loader_handle = mUnit->GetClassLoader(); + StackHandleScope<1> hs(soa.Self()); + Handle<mirror::ClassLoader> class_loader_handle( + hs.NewHandle(soa.Decode<mirror::ClassLoader>(mUnit->GetClassLoader()))); resolved_field = ResolveField(soa, dex_cache, class_loader_handle, mUnit, field_idx, false); referrer_class = resolved_field != nullptr ? ResolveCompilingMethodsClass(soa, dex_cache, class_loader_handle, mUnit) : nullptr; @@ -1883,7 +1874,7 @@ class ResolveTypeVisitor : public CompilationVisitor { Handle<mirror::DexCache> dex_cache(hs.NewHandle(class_linker->RegisterDexFile( dex_file, class_loader.Get()))); - ObjPtr<mirror::Class> klass = (dex_cache.Get() != nullptr) + ObjPtr<mirror::Class> klass = (dex_cache != nullptr) ? class_linker->ResolveType(dex_file, dex::TypeIndex(type_idx), dex_cache, class_loader) : nullptr; @@ -1984,7 +1975,7 @@ static void LoadAndUpdateStatus(const DexFile& dex_file, ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); Handle<mirror::Class> cls(hs.NewHandle<mirror::Class>( class_linker->FindClass(self, descriptor, class_loader))); - if (cls.Get() != nullptr) { + if (cls != 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. @@ -2132,7 +2123,7 @@ class VerifyClassVisitor : public CompilationVisitor { Handle<mirror::Class> klass( hs.NewHandle(class_linker->FindClass(soa.Self(), descriptor, class_loader))); verifier::MethodVerifier::FailureKind failure_kind; - if (klass.Get() == nullptr) { + if (klass == nullptr) { CHECK(soa.Self()->IsExceptionPending()); soa.Self()->ClearException(); @@ -2234,7 +2225,7 @@ class SetVerifiedClassVisitor : public CompilationVisitor { Handle<mirror::Class> klass( hs.NewHandle(class_linker->FindClass(soa.Self(), descriptor, class_loader))); // Class might have failed resolution. Then don't set it to verified. - if (klass.Get() != nullptr) { + if (klass != nullptr) { // Only do this if the class is resolved. If even resolution fails, quickening will go very, // very wrong. if (klass->IsResolved() && !klass->IsErroneousResolved()) { @@ -2296,7 +2287,7 @@ class InitializeClassVisitor : public CompilationVisitor { Handle<mirror::Class> klass( hs.NewHandle(manager_->GetClassLinker()->FindClass(soa.Self(), descriptor, class_loader))); - if (klass.Get() != nullptr && !SkipClass(jclass_loader, dex_file, klass.Get())) { + if (klass != nullptr && !SkipClass(jclass_loader, dex_file, klass.Get())) { // Only try to initialize classes that were successfully verified. if (klass->IsVerified()) { // Attempt to initialize the class but bail if we either need to initialize the super-class @@ -2546,7 +2537,7 @@ class CompileClassVisitor : public CompilationVisitor { Handle<mirror::Class> klass( hs.NewHandle(class_linker->FindClass(soa.Self(), descriptor, class_loader))); Handle<mirror::DexCache> dex_cache; - if (klass.Get() == nullptr) { + if (klass == nullptr) { soa.Self()->AssertPendingException(); soa.Self()->ClearException(); dex_cache = hs.NewHandle(class_linker->FindDexCache(soa.Self(), dex_file)); @@ -2594,18 +2585,10 @@ class CompileClassVisitor : public CompilationVisitor { continue; } previous_direct_method_idx = method_idx; - CompileMethod(soa.Self(), - driver, - it.GetMethodCodeItem(), - it.GetMethodAccessFlags(), - it.GetMethodInvokeType(class_def), - class_def_index, - method_idx, - class_loader, - dex_file, - dex_to_dex_compilation_level, - compilation_enabled, - dex_cache); + CompileMethod(soa.Self(), driver, it.GetMethodCodeItem(), it.GetMethodAccessFlags(), + it.GetMethodInvokeType(class_def), class_def_index, + method_idx, jclass_loader, dex_file, dex_to_dex_compilation_level, + compilation_enabled, dex_cache); it.Next(); } // Compile virtual methods @@ -2619,17 +2602,10 @@ class CompileClassVisitor : public CompilationVisitor { continue; } previous_virtual_method_idx = method_idx; - CompileMethod(soa.Self(), - driver, it.GetMethodCodeItem(), - it.GetMethodAccessFlags(), - it.GetMethodInvokeType(class_def), - class_def_index, - method_idx, - class_loader, - dex_file, - dex_to_dex_compilation_level, - compilation_enabled, - dex_cache); + CompileMethod(soa.Self(), driver, it.GetMethodCodeItem(), it.GetMethodAccessFlags(), + it.GetMethodInvokeType(class_def), class_def_index, + method_idx, jclass_loader, dex_file, dex_to_dex_compilation_level, + compilation_enabled, dex_cache); it.Next(); } DCHECK(!it.HasNext()); diff --git a/compiler/driver/compiler_driver.h b/compiler/driver/compiler_driver.h index 1e5c43d833..5b4c751c4a 100644 --- a/compiler/driver/compiler_driver.h +++ b/compiler/driver/compiler_driver.h @@ -187,14 +187,16 @@ class CompilerDriver { REQUIRES(!requires_constructor_barrier_lock_); // Are runtime access checks necessary in the compiled code? - bool CanAccessTypeWithoutChecks(ObjPtr<mirror::Class> referrer_class, - ObjPtr<mirror::Class> resolved_class) + bool CanAccessTypeWithoutChecks(uint32_t referrer_idx, + Handle<mirror::DexCache> dex_cache, + dex::TypeIndex type_idx) REQUIRES_SHARED(Locks::mutator_lock_); // Are runtime access and instantiable checks necessary in the code? // out_is_finalizable is set to whether the type is finalizable. - bool CanAccessInstantiableTypeWithoutChecks(ObjPtr<mirror::Class> referrer_class, - ObjPtr<mirror::Class> resolved_class, + bool CanAccessInstantiableTypeWithoutChecks(uint32_t referrer_idx, + Handle<mirror::DexCache> dex_cache, + dex::TypeIndex type_idx, bool* out_is_finalizable) REQUIRES_SHARED(Locks::mutator_lock_); @@ -368,6 +370,10 @@ class CompilerDriver { uint32_t field_idx) REQUIRES_SHARED(Locks::mutator_lock_); + mirror::ClassLoader* GetClassLoader(const ScopedObjectAccess& soa, + const DexCompilationUnit* mUnit) + REQUIRES_SHARED(Locks::mutator_lock_); + private: void PreCompile(jobject class_loader, const std::vector<const DexFile*>& dex_files, diff --git a/compiler/driver/compiler_driver_test.cc b/compiler/driver/compiler_driver_test.cc index e4b66ebc5a..1e4ca16844 100644 --- a/compiler/driver/compiler_driver_test.cc +++ b/compiler/driver/compiler_driver_test.cc @@ -101,7 +101,6 @@ class CompilerDriverTest : public CommonCompilerTest { }; // Disabled due to 10 second runtime on host -// TODO: Update the test for hash-based dex cache arrays. Bug: 30627598 TEST_F(CompilerDriverTest, DISABLED_LARGE_CompileDexLibCore) { CompileAll(nullptr); diff --git a/compiler/driver/dex_compilation_unit.cc b/compiler/driver/dex_compilation_unit.cc index 7e8e812c4a..47b19297e5 100644 --- a/compiler/driver/dex_compilation_unit.cc +++ b/compiler/driver/dex_compilation_unit.cc @@ -21,7 +21,7 @@ namespace art { -DexCompilationUnit::DexCompilationUnit(Handle<mirror::ClassLoader> class_loader, +DexCompilationUnit::DexCompilationUnit(jobject class_loader, ClassLinker* class_linker, const DexFile& dex_file, const DexFile::CodeItem* code_item, diff --git a/compiler/driver/dex_compilation_unit.h b/compiler/driver/dex_compilation_unit.h index 24a9a5b653..854927d747 100644 --- a/compiler/driver/dex_compilation_unit.h +++ b/compiler/driver/dex_compilation_unit.h @@ -34,7 +34,7 @@ class VerifiedMethod; class DexCompilationUnit : public DeletableArenaObject<kArenaAllocMisc> { public: - DexCompilationUnit(Handle<mirror::ClassLoader> class_loader, + DexCompilationUnit(jobject class_loader, ClassLinker* class_linker, const DexFile& dex_file, const DexFile::CodeItem* code_item, @@ -44,7 +44,7 @@ class DexCompilationUnit : public DeletableArenaObject<kArenaAllocMisc> { const VerifiedMethod* verified_method, Handle<mirror::DexCache> dex_cache); - Handle<mirror::ClassLoader> GetClassLoader() const { + jobject GetClassLoader() const { return class_loader_; } @@ -113,7 +113,7 @@ class DexCompilationUnit : public DeletableArenaObject<kArenaAllocMisc> { } private: - const Handle<mirror::ClassLoader> class_loader_; + const jobject class_loader_; ClassLinker* const class_linker_; @@ -125,7 +125,7 @@ class DexCompilationUnit : public DeletableArenaObject<kArenaAllocMisc> { const uint32_t access_flags_; const VerifiedMethod* verified_method_; - const Handle<mirror::DexCache> dex_cache_; + Handle<mirror::DexCache> dex_cache_; std::string symbol_; }; diff --git a/compiler/image_writer.cc b/compiler/image_writer.cc index 3e9ae0834c..d2dd30d8e6 100644 --- a/compiler/image_writer.cc +++ b/compiler/image_writer.cc @@ -940,11 +940,9 @@ void ImageWriter::PruneNonImageClasses() { } ObjPtr<mirror::DexCache> dex_cache = self->DecodeJObject(data.weak_root)->AsDexCache(); for (size_t i = 0; i < dex_cache->NumResolvedTypes(); i++) { - mirror::TypeDexCachePair pair = - dex_cache->GetResolvedTypes()[i].load(std::memory_order_relaxed); - mirror::Class* klass = pair.object.Read(); + Class* klass = dex_cache->GetResolvedType(dex::TypeIndex(i)); if (klass != nullptr && !KeepClass(klass)) { - dex_cache->ClearResolvedType(dex::TypeIndex(pair.index)); + dex_cache->SetResolvedType(dex::TypeIndex(i), nullptr); } } ArtMethod** resolved_methods = dex_cache->GetResolvedMethods(); @@ -1080,7 +1078,7 @@ ObjectArray<Object>* ImageWriter::CreateImageRoots(size_t oat_index) const { } Handle<ObjectArray<Object>> dex_caches( hs.NewHandle(ObjectArray<Object>::Alloc(self, object_array_class.Get(), dex_cache_count))); - CHECK(dex_caches.Get() != nullptr) << "Failed to allocate a dex cache array."; + CHECK(dex_caches != nullptr) << "Failed to allocate a dex cache array."; { ReaderMutexLock mu(self, *Locks::dex_lock_); size_t non_image_dex_caches = 0; @@ -1924,7 +1922,8 @@ void ImageWriter::CopyAndFixupNativeData(size_t oat_index) { // above comment for intern tables. ClassTable temp_class_table; temp_class_table.ReadFromMemory(class_table_memory_ptr); - ObjPtr<mirror::ClassLoader> class_loader = GetClassLoader(); + CHECK_EQ(class_loaders_.size(), compile_app_image_ ? 1u : 0u); + mirror::ClassLoader* class_loader = compile_app_image_ ? *class_loaders_.begin() : nullptr; CHECK_EQ(temp_class_table.NumZygoteClasses(class_loader), table->NumNonZygoteClasses(class_loader) + table->NumZygoteClasses(class_loader)); UnbufferedRootVisitor visitor(&root_visitor, RootInfo(kRootUnknown)); @@ -2214,7 +2213,7 @@ void ImageWriter::FixupDexCache(mirror::DexCache* orig_dex_cache, orig_dex_cache->FixupStrings(NativeCopyLocation(orig_strings, orig_dex_cache), ImageAddressVisitor(this)); } - mirror::TypeDexCacheType* orig_types = orig_dex_cache->GetResolvedTypes(); + GcRoot<mirror::Class>* orig_types = orig_dex_cache->GetResolvedTypes(); if (orig_types != nullptr) { copy_dex_cache->SetFieldPtrWithSize<false>(mirror::DexCache::ResolvedTypesOffset(), NativeLocationInImage(orig_types), @@ -2255,6 +2254,14 @@ void ImageWriter::FixupDexCache(mirror::DexCache* orig_dex_cache, orig_dex_cache->FixupResolvedMethodTypes(NativeCopyLocation(orig_method_types, orig_dex_cache), ImageAddressVisitor(this)); } + GcRoot<mirror::CallSite>* orig_call_sites = orig_dex_cache->GetResolvedCallSites(); + if (orig_call_sites != nullptr) { + copy_dex_cache->SetFieldPtrWithSize<false>(mirror::DexCache::ResolvedCallSitesOffset(), + NativeLocationInImage(orig_call_sites), + PointerSize::k64); + orig_dex_cache->FixupResolvedCallSites(NativeCopyLocation(orig_call_sites, orig_dex_cache), + ImageAddressVisitor(this)); + } // Remove the DexFile pointers. They will be fixed up when the runtime loads the oat file. Leaving // compiler pointers in here will make the output non-deterministic. diff --git a/compiler/image_writer.h b/compiler/image_writer.h index bdc7146632..cc7df1ce21 100644 --- a/compiler/image_writer.h +++ b/compiler/image_writer.h @@ -51,13 +51,8 @@ class ImageSpace; } // namespace space } // namespace gc -namespace mirror { -class ClassLoader; -} // namespace mirror - class ClassLoaderVisitor; class ClassTable; -class ImtConflictTable; static constexpr int kInvalidFd = -1; @@ -84,11 +79,6 @@ class ImageWriter FINAL { return true; } - ObjPtr<mirror::ClassLoader> GetClassLoader() { - CHECK_EQ(class_loaders_.size(), compile_app_image_ ? 1u : 0u); - return compile_app_image_ ? *class_loaders_.begin() : nullptr; - } - template <typename T> T* GetImageAddress(T* object) const REQUIRES_SHARED(Locks::mutator_lock_) { if (object == nullptr || IsInBootImage(object)) { diff --git a/compiler/oat_writer.cc b/compiler/oat_writer.cc index 0ea11255a8..7c0cdbf270 100644 --- a/compiler/oat_writer.cc +++ b/compiler/oat_writer.cc @@ -1060,7 +1060,6 @@ class OatWriter::WriteCodeMethodVisitor : public OatDexMethodVisitor { WriteCodeMethodVisitor(OatWriter* writer, OutputStream* out, const size_t file_offset, size_t relative_offset) SHARED_LOCK_FUNCTION(Locks::mutator_lock_) : OatDexMethodVisitor(writer, relative_offset), - class_loader_(writer->HasImage() ? writer->image_writer_->GetClassLoader() : nullptr), out_(out), file_offset_(file_offset), soa_(Thread::Current()), @@ -1246,7 +1245,6 @@ class OatWriter::WriteCodeMethodVisitor : public OatDexMethodVisitor { } private: - ObjPtr<mirror::ClassLoader> class_loader_; OutputStream* const out_; const size_t file_offset_; const ScopedObjectAccess soa_; @@ -1305,12 +1303,10 @@ class OatWriter::WriteCodeMethodVisitor : public OatDexMethodVisitor { } mirror::Class* GetTargetType(const LinkerPatch& patch) REQUIRES_SHARED(Locks::mutator_lock_) { - DCHECK(writer_->HasImage()); ObjPtr<mirror::DexCache> dex_cache = GetDexCache(patch.TargetTypeDexFile()); - ObjPtr<mirror::Class> type = - ClassLinker::LookupResolvedType(patch.TargetTypeIndex(), dex_cache, class_loader_); + mirror::Class* type = dex_cache->GetResolvedType(patch.TargetTypeIndex()); CHECK(type != nullptr); - return type.Ptr(); + return type; } mirror::String* GetTargetString(const LinkerPatch& patch) REQUIRES_SHARED(Locks::mutator_lock_) { diff --git a/compiler/optimizing/builder.h b/compiler/optimizing/builder.h index 3a4c9dbd16..e4ad4222fb 100644 --- a/compiler/optimizing/builder.h +++ b/compiler/optimizing/builder.h @@ -54,10 +54,7 @@ class HGraphBuilder : public ValueObject { compiler_driver_(driver), compilation_stats_(compiler_stats), block_builder_(graph, dex_file, code_item), - ssa_builder_(graph, - dex_compilation_unit->GetClassLoader(), - dex_compilation_unit->GetDexCache(), - handles), + ssa_builder_(graph, dex_compilation_unit->GetDexCache(), handles), instruction_builder_(graph, &block_builder_, &ssa_builder_, @@ -83,12 +80,10 @@ class HGraphBuilder : public ValueObject { code_item_(code_item), dex_compilation_unit_(nullptr), compiler_driver_(nullptr), + null_dex_cache_(), compilation_stats_(nullptr), block_builder_(graph, nullptr, code_item), - ssa_builder_(graph, - handles->NewHandle<mirror::ClassLoader>(nullptr), - handles->NewHandle<mirror::DexCache>(nullptr), - handles), + ssa_builder_(graph, null_dex_cache_, handles), instruction_builder_(graph, &block_builder_, &ssa_builder_, @@ -101,7 +96,7 @@ class HGraphBuilder : public ValueObject { /* code_generator */ nullptr, /* interpreter_metadata */ nullptr, /* compiler_stats */ nullptr, - handles->NewHandle<mirror::DexCache>(nullptr), + null_dex_cache_, handles) {} GraphAnalysisResult BuildGraph(); @@ -122,6 +117,8 @@ class HGraphBuilder : public ValueObject { CompilerDriver* const compiler_driver_; + ScopedNullHandle<mirror::DexCache> null_dex_cache_; + OptimizingCompilerStats* compilation_stats_; HBasicBlockBuilder block_builder_; diff --git a/compiler/optimizing/code_generator.cc b/compiler/optimizing/code_generator.cc index d68aa51b1b..bac16cd5df 100644 --- a/compiler/optimizing/code_generator.cc +++ b/compiler/optimizing/code_generator.cc @@ -1419,7 +1419,7 @@ void CodeGenerator::EmitJitRoots(uint8_t* code, QuickEntrypointEnum CodeGenerator::GetArrayAllocationEntrypoint(Handle<mirror::Class> array_klass) { ScopedObjectAccess soa(Thread::Current()); - if (array_klass.Get() == nullptr) { + if (array_klass == nullptr) { // This can only happen for non-primitive arrays, as primitive arrays can always // be resolved. return kQuickAllocArrayResolved32; diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc index b56ef0f866..e012a4287f 100644 --- a/compiler/optimizing/inliner.cc +++ b/compiler/optimizing/inliner.cc @@ -69,38 +69,32 @@ void HInliner::Run() { // doing some logic in the runtime to discover if a method could have been inlined. return; } - const ArenaVector<HBasicBlock*>& blocks = graph_->GetReversePostOrder(); + // Keep a copy of all blocks when starting the visit. + ArenaVector<HBasicBlock*> blocks = graph_->GetReversePostOrder(); DCHECK(!blocks.empty()); - HBasicBlock* next_block = blocks[0]; - for (size_t i = 0; i < blocks.size(); ++i) { - // Because we are changing the graph when inlining, we need to remember the next block. - // This avoids doing the inlining work again on the inlined blocks. - if (blocks[i] != next_block) { - continue; - } - HBasicBlock* block = next_block; - next_block = (i == blocks.size() - 1) ? nullptr : blocks[i + 1]; + // Because we are changing the graph when inlining, + // we just iterate over the blocks of the outer method. + // This avoids doing the inlining work again on the inlined blocks. + for (HBasicBlock* block : blocks) { for (HInstruction* instruction = block->GetFirstInstruction(); instruction != nullptr;) { HInstruction* next = instruction->GetNext(); HInvoke* call = instruction->AsInvoke(); // As long as the call is not intrinsified, it is worth trying to inline. if (call != nullptr && call->GetIntrinsic() == Intrinsics::kNone) { - // We use the original invoke type to ensure the resolution of the called method - // works properly. - if (!TryInline(call)) { - if (kIsDebugBuild && IsCompilingWithCoreImage()) { - std::string callee_name = - outer_compilation_unit_.GetDexFile()->PrettyMethod(call->GetDexMethodIndex()); - bool should_inline = callee_name.find("$inline$") != std::string::npos; - CHECK(!should_inline) << "Could not inline " << callee_name; + if (kIsDebugBuild && IsCompilingWithCoreImage()) { + // Debugging case: directives in method names control or assert on inlining. + std::string callee_name = outer_compilation_unit_.GetDexFile()->PrettyMethod( + call->GetDexMethodIndex(), /* with_signature */ false); + // Tests prevent inlining by having $noinline$ in their method names. + if (callee_name.find("$noinline$") == std::string::npos) { + if (!TryInline(call)) { + bool should_have_inlined = (callee_name.find("$inline$") != std::string::npos); + CHECK(!should_have_inlined) << "Could not inline " << callee_name; + } } } else { - if (kIsDebugBuild && IsCompilingWithCoreImage()) { - std::string callee_name = - outer_compilation_unit_.GetDexFile()->PrettyMethod(call->GetDexMethodIndex()); - bool must_not_inline = callee_name.find("$noinline$") != std::string::npos; - CHECK(!must_not_inline) << "Should not have inlined " << callee_name; - } + // Normal case: try to inline. + TryInline(call); } } instruction = next; @@ -198,9 +192,9 @@ static uint32_t FindMethodIndexIn(ArtMethod* method, } static dex::TypeIndex FindClassIndexIn(mirror::Class* cls, - const DexCompilationUnit& compilation_unit) + const DexFile& dex_file, + Handle<mirror::DexCache> dex_cache) REQUIRES_SHARED(Locks::mutator_lock_) { - const DexFile& dex_file = *compilation_unit.GetDexFile(); dex::TypeIndex index; if (cls->GetDexCache() == nullptr) { DCHECK(cls->IsArrayClass()) << cls->PrettyClass(); @@ -209,19 +203,22 @@ static dex::TypeIndex FindClassIndexIn(mirror::Class* cls, DCHECK(cls->IsProxyClass()) << cls->PrettyClass(); // TODO: deal with proxy classes. } else if (IsSameDexFile(cls->GetDexFile(), dex_file)) { - DCHECK_EQ(cls->GetDexCache(), compilation_unit.GetDexCache().Get()); + DCHECK_EQ(cls->GetDexCache(), dex_cache.Get()); index = cls->GetDexTypeIndex(); + // Update the dex cache to ensure the class is in. The generated code will + // consider it is. We make it safe by updating the dex cache, as other + // dex files might also load the class, and there is no guarantee the dex + // cache of the dex file of the class will be updated. + if (dex_cache->GetResolvedType(index) == nullptr) { + dex_cache->SetResolvedType(index, cls); + } } else { index = cls->FindTypeIndexInOtherDexFile(dex_file); - // We cannot guarantee the entry will resolve to the same class, + // We cannot guarantee the entry in the dex cache will resolve to the same class, // as there may be different class loaders. So only return the index if it's - // the right class already resolved with the class loader. - if (index.IsValid()) { - ObjPtr<mirror::Class> resolved = ClassLinker::LookupResolvedType( - index, compilation_unit.GetDexCache().Get(), compilation_unit.GetClassLoader().Get()); - if (resolved != cls) { - index = dex::TypeIndex::Invalid(); - } + // the right class in the dex cache already. + if (index.IsValid() && dex_cache->GetResolvedType(index) != cls) { + index = dex::TypeIndex::Invalid(); } } @@ -377,7 +374,7 @@ bool HInliner::TryInline(HInvoke* invoke_instruction) { soa.Self(), class_linker->GetClassRoot(ClassLinker::kClassArrayClass), InlineCache::kIndividualCacheSize)); - if (inline_cache.Get() == nullptr) { + if (inline_cache == nullptr) { // We got an OOME. Just clear the exception, and don't inline. DCHECK(soa.Self()->IsExceptionPending()); soa.Self()->ClearException(); @@ -448,8 +445,9 @@ bool HInliner::TryInlineMonomorphicCall(HInvoke* invoke_instruction, DCHECK(invoke_instruction->IsInvokeVirtual() || invoke_instruction->IsInvokeInterface()) << invoke_instruction->DebugName(); + const DexFile& caller_dex_file = *caller_compilation_unit_.GetDexFile(); dex::TypeIndex class_index = FindClassIndexIn( - GetMonomorphicType(classes), caller_compilation_unit_); + GetMonomorphicType(classes), caller_dex_file, caller_compilation_unit_.GetDexCache()); if (!class_index.IsValid()) { VLOG(compiler) << "Call to " << ArtMethod::PrettyMethod(resolved_method) << " from inline cache is not inlined because its class is not" @@ -492,7 +490,6 @@ bool HInliner::TryInlineMonomorphicCall(HInvoke* invoke_instruction, // Run type propagation to get the guard typed, and eventually propagate the // type of the receiver. ReferenceTypePropagation rtp_fixup(graph_, - outer_compilation_unit_.GetClassLoader(), outer_compilation_unit_.GetDexCache(), handles_, /* is_first_run */ false); @@ -563,7 +560,6 @@ HInstruction* HInliner::AddTypeGuard(HInstruction* receiver, bb_cursor->InsertInstructionAfter(load_class, receiver_class); load_class->SetLoadKind(kind); - // TODO: Extend reference type propagation to understand the guard. HNotEqual* compare = new (graph_->GetArena()) HNotEqual(load_class, receiver_class); bb_cursor->InsertInstructionAfter(compare, load_class); if (with_deoptimization) { @@ -587,6 +583,7 @@ bool HInliner::TryInlinePolymorphicCall(HInvoke* invoke_instruction, ClassLinker* class_linker = caller_compilation_unit_.GetClassLinker(); PointerSize pointer_size = class_linker->GetImagePointerSize(); + const DexFile& caller_dex_file = *caller_compilation_unit_.GetDexFile(); bool all_targets_inlined = true; bool one_target_inlined = false; @@ -608,7 +605,8 @@ bool HInliner::TryInlinePolymorphicCall(HInvoke* invoke_instruction, HInstruction* cursor = invoke_instruction->GetPrevious(); HBasicBlock* bb_cursor = invoke_instruction->GetBlock(); - dex::TypeIndex class_index = FindClassIndexIn(handle.Get(), caller_compilation_unit_); + dex::TypeIndex class_index = FindClassIndexIn( + handle.Get(), caller_dex_file, caller_compilation_unit_.GetDexCache()); HInstruction* return_replacement = nullptr; if (!class_index.IsValid() || !TryBuildAndInline(invoke_instruction, @@ -664,7 +662,6 @@ bool HInliner::TryInlinePolymorphicCall(HInvoke* invoke_instruction, // Run type propagation to get the guards typed. ReferenceTypePropagation rtp_fixup(graph_, - outer_compilation_unit_.GetClassLoader(), outer_compilation_unit_.GetDexCache(), handles_, /* is_first_run */ false); @@ -846,7 +843,6 @@ bool HInliner::TryInlinePolymorphicCallToSameTarget( if (outermost_graph_->IsCompilingOsr()) { CreateDiamondPatternForPolymorphicInline(compare, return_replacement, invoke_instruction); } else { - // TODO: Extend reference type propagation to understand the guard. HDeoptimize* deoptimize = new (graph_->GetArena()) HDeoptimize( compare, invoke_instruction->GetDexPc()); bb_cursor->InsertInstructionAfter(deoptimize, compare); @@ -859,7 +855,6 @@ bool HInliner::TryInlinePolymorphicCallToSameTarget( // Run type propagation to get the guard typed. ReferenceTypePropagation rtp_fixup(graph_, - outer_compilation_unit_.GetClassLoader(), outer_compilation_unit_.GetDexCache(), handles_, /* is_first_run */ false); @@ -928,7 +923,6 @@ bool HInliner::TryInlineAndReplace(HInvoke* invoke_instruction, // Actual return value has a more specific type than the method's declared // return type. Run RTP again on the outer graph to propagate it. ReferenceTypePropagation(graph_, - outer_compilation_unit_.GetClassLoader(), outer_compilation_unit_.GetDexCache(), handles_, /* is_first_run */ false).Run(); @@ -1181,11 +1175,7 @@ HInstanceFieldGet* HInliner::CreateInstanceFieldGet(Handle<mirror::DexCache> dex /* dex_pc */ 0); if (iget->GetType() == Primitive::kPrimNot) { // Use the same dex_cache that we used for field lookup as the hint_dex_cache. - ReferenceTypePropagation rtp(graph_, - outer_compilation_unit_.GetClassLoader(), - dex_cache, - handles_, - /* is_first_run */ false); + ReferenceTypePropagation rtp(graph_, dex_cache, handles_, /* is_first_run */ false); rtp.Visit(iget); } return iget; @@ -1231,7 +1221,7 @@ bool HInliner::TryBuildAndInlineHelper(HInvoke* invoke_instruction, resolved_method->GetDeclaringClass()->GetClassLoader())); DexCompilationUnit dex_compilation_unit( - class_loader, + class_loader.ToJObject(), class_linker, callee_dex_file, code_item, @@ -1348,7 +1338,6 @@ bool HInliner::TryBuildAndInlineHelper(HInvoke* invoke_instruction, // are more specific than the declared ones, run RTP again on the inner graph. if (run_rtp || ArgumentTypesMoreSpecific(invoke_instruction, resolved_method)) { ReferenceTypePropagation(callee_graph, - outer_compilation_unit_.GetClassLoader(), dex_compilation_unit.GetDexCache(), handles_, /* is_first_run */ false).Run(); @@ -1359,9 +1348,6 @@ bool HInliner::TryBuildAndInlineHelper(HInvoke* invoke_instruction, RunOptimizations(callee_graph, code_item, dex_compilation_unit); number_of_instructions_budget += number_of_inlined_instructions; - // TODO: We should abort only if all predecessors throw. However, - // HGraph::InlineInto currently does not handle an exit block with - // a throw predecessor. HBasicBlock* exit_block = callee_graph->GetExitBlock(); if (exit_block == nullptr) { VLOG(compiler) << "Method " << callee_dex_file.PrettyMethod(method_index) @@ -1369,16 +1355,30 @@ bool HInliner::TryBuildAndInlineHelper(HInvoke* invoke_instruction, return false; } - bool has_throw_predecessor = false; + bool has_one_return = false; for (HBasicBlock* predecessor : exit_block->GetPredecessors()) { if (predecessor->GetLastInstruction()->IsThrow()) { - has_throw_predecessor = true; - break; + if (invoke_instruction->GetBlock()->IsTryBlock()) { + // TODO(ngeoffray): Support adding HTryBoundary in Hgraph::InlineInto. + VLOG(compiler) << "Method " << callee_dex_file.PrettyMethod(method_index) + << " could not be inlined because one branch always throws and" + << " caller is in a try/catch block"; + return false; + } else if (graph_->GetExitBlock() == nullptr) { + // TODO(ngeoffray): Support adding HExit in the caller graph. + VLOG(compiler) << "Method " << callee_dex_file.PrettyMethod(method_index) + << " could not be inlined because one branch always throws and" + << " caller does not have an exit block"; + return false; + } + } else { + has_one_return = true; } } - if (has_throw_predecessor) { + + if (!has_one_return) { VLOG(compiler) << "Method " << callee_dex_file.PrettyMethod(method_index) - << " could not be inlined because one branch always throws"; + << " could not be inlined because it always throws"; return false; } diff --git a/compiler/optimizing/instruction_builder.cc b/compiler/optimizing/instruction_builder.cc index 3aaf2ca102..3374e42955 100644 --- a/compiler/optimizing/instruction_builder.cc +++ b/compiler/optimizing/instruction_builder.cc @@ -669,17 +669,18 @@ static InvokeType GetInvokeTypeFromOpCode(Instruction::Code opcode) { ArtMethod* HInstructionBuilder::ResolveMethod(uint16_t method_idx, InvokeType invoke_type) { ScopedObjectAccess soa(Thread::Current()); - StackHandleScope<2> hs(soa.Self()); + StackHandleScope<3> hs(soa.Self()); ClassLinker* class_linker = dex_compilation_unit_->GetClassLinker(); - Handle<mirror::ClassLoader> class_loader = dex_compilation_unit_->GetClassLoader(); + Handle<mirror::ClassLoader> class_loader(hs.NewHandle( + soa.Decode<mirror::ClassLoader>(dex_compilation_unit_->GetClassLoader()))); Handle<mirror::Class> compiling_class(hs.NewHandle(GetCompilingClass())); // We fetch the referenced class eagerly (that is, the class pointed by in the MethodId // at method_idx), as `CanAccessResolvedMethod` expects it be be in the dex cache. Handle<mirror::Class> methods_class(hs.NewHandle(class_linker->ResolveReferencedClassOfMethod( method_idx, dex_compilation_unit_->GetDexCache(), class_loader))); - if (UNLIKELY(methods_class.Get() == nullptr)) { + if (UNLIKELY(methods_class == nullptr)) { // Clean up any exception left by type resolution. soa.Self()->ClearException(); return nullptr; @@ -701,7 +702,7 @@ ArtMethod* HInstructionBuilder::ResolveMethod(uint16_t method_idx, InvokeType in // Check access. The class linker has a fast path for looking into the dex cache // and does not check the access if it hits it. - if (compiling_class.Get() == nullptr) { + if (compiling_class == nullptr) { if (!resolved_method->IsPublic()) { return nullptr; } @@ -717,7 +718,7 @@ ArtMethod* HInstructionBuilder::ResolveMethod(uint16_t method_idx, InvokeType in // make this an invoke-unresolved to handle cross-dex invokes or abstract super methods, both of // which require runtime handling. if (invoke_type == kSuper) { - if (compiling_class.Get() == nullptr) { + if (compiling_class == nullptr) { // We could not determine the method's class we need to wait until runtime. DCHECK(Runtime::Current()->IsAotCompiler()); return nullptr; @@ -953,7 +954,7 @@ bool HInstructionBuilder::BuildNewInstance(dex::TypeIndex type_index, uint32_t d } // Consider classes we haven't resolved as potentially finalizable. - bool finalizable = (klass.Get() == nullptr) || klass->IsFinalizable(); + bool finalizable = (klass == nullptr) || klass->IsFinalizable(); AppendInstruction(new (arena_) HNewInstance( cls, @@ -971,7 +972,7 @@ static bool IsSubClass(mirror::Class* to_test, mirror::Class* super_class) } bool HInstructionBuilder::IsInitialized(Handle<mirror::Class> cls) const { - if (cls.Get() == nullptr) { + if (cls == nullptr) { return false; } @@ -1259,7 +1260,9 @@ bool HInstructionBuilder::BuildInstanceFieldAccess(const Instruction& instructio static mirror::Class* GetClassFrom(CompilerDriver* driver, const DexCompilationUnit& compilation_unit) { ScopedObjectAccess soa(Thread::Current()); - Handle<mirror::ClassLoader> class_loader = compilation_unit.GetClassLoader(); + StackHandleScope<1> hs(soa.Self()); + Handle<mirror::ClassLoader> class_loader(hs.NewHandle( + soa.Decode<mirror::ClassLoader>(compilation_unit.GetClassLoader()))); Handle<mirror::DexCache> dex_cache = compilation_unit.GetDexCache(); return driver->ResolveCompilingMethodsClass(soa, dex_cache, class_loader, &compilation_unit); @@ -1275,9 +1278,10 @@ mirror::Class* HInstructionBuilder::GetCompilingClass() const { bool HInstructionBuilder::IsOutermostCompilingClass(dex::TypeIndex type_index) const { ScopedObjectAccess soa(Thread::Current()); - StackHandleScope<2> hs(soa.Self()); + StackHandleScope<3> hs(soa.Self()); Handle<mirror::DexCache> dex_cache = dex_compilation_unit_->GetDexCache(); - Handle<mirror::ClassLoader> class_loader = dex_compilation_unit_->GetClassLoader(); + Handle<mirror::ClassLoader> class_loader(hs.NewHandle( + soa.Decode<mirror::ClassLoader>(dex_compilation_unit_->GetClassLoader()))); Handle<mirror::Class> cls(hs.NewHandle(compiler_driver_->ResolveClass( soa, dex_cache, class_loader, type_index, dex_compilation_unit_))); Handle<mirror::Class> outer_class(hs.NewHandle(GetOutermostCompilingClass())); @@ -1288,7 +1292,7 @@ bool HInstructionBuilder::IsOutermostCompilingClass(dex::TypeIndex type_index) c // When this happens we cannot establish a direct relation between the current // class and the outer class, so we return false. // (Note that this is only used for optimizing invokes and field accesses) - return (cls.Get() != nullptr) && (outer_class.Get() == cls.Get()); + return (cls != nullptr) && (outer_class.Get() == cls.Get()); } void HInstructionBuilder::BuildUnresolvedStaticFieldAccess(const Instruction& instruction, @@ -1313,7 +1317,8 @@ ArtField* HInstructionBuilder::ResolveField(uint16_t field_idx, bool is_static, StackHandleScope<2> hs(soa.Self()); ClassLinker* class_linker = dex_compilation_unit_->GetClassLinker(); - Handle<mirror::ClassLoader> class_loader = dex_compilation_unit_->GetClassLoader(); + Handle<mirror::ClassLoader> class_loader(hs.NewHandle( + soa.Decode<mirror::ClassLoader>(dex_compilation_unit_->GetClassLoader()))); Handle<mirror::Class> compiling_class(hs.NewHandle(GetCompilingClass())); ArtField* resolved_field = class_linker->ResolveField(*dex_compilation_unit_->GetDexFile(), @@ -1335,7 +1340,7 @@ ArtField* HInstructionBuilder::ResolveField(uint16_t field_idx, bool is_static, } // Check access. - if (compiling_class.Get() == nullptr) { + if (compiling_class == nullptr) { if (!resolved_field->IsPublic()) { return nullptr; } @@ -1607,7 +1612,7 @@ void HInstructionBuilder::BuildFillWideArrayData(HInstruction* object, static TypeCheckKind ComputeTypeCheckKind(Handle<mirror::Class> cls) REQUIRES_SHARED(Locks::mutator_lock_) { - if (cls.Get() == nullptr) { + if (cls == nullptr) { return TypeCheckKind::kUnresolvedCheck; } else if (cls->IsInterface()) { return TypeCheckKind::kInterfaceCheck; @@ -1630,13 +1635,15 @@ static TypeCheckKind ComputeTypeCheckKind(Handle<mirror::Class> cls) HLoadClass* HInstructionBuilder::BuildLoadClass(dex::TypeIndex type_index, uint32_t dex_pc) { ScopedObjectAccess soa(Thread::Current()); + StackHandleScope<2> hs(soa.Self()); const DexFile& dex_file = *dex_compilation_unit_->GetDexFile(); - Handle<mirror::ClassLoader> class_loader = dex_compilation_unit_->GetClassLoader(); + Handle<mirror::ClassLoader> class_loader(hs.NewHandle( + soa.Decode<mirror::ClassLoader>(dex_compilation_unit_->GetClassLoader()))); Handle<mirror::Class> klass = handles_->NewHandle(compiler_driver_->ResolveClass( soa, dex_compilation_unit_->GetDexCache(), class_loader, type_index, dex_compilation_unit_)); bool needs_access_check = true; - if (klass.Get() != nullptr) { + if (klass != nullptr) { if (klass->IsPublic()) { needs_access_check = false; } else { @@ -1672,7 +1679,7 @@ HLoadClass* HInstructionBuilder::BuildLoadClass(dex::TypeIndex type_index, type_index, *actual_dex_file, klass, - klass.Get() != nullptr && (klass.Get() == GetOutermostCompilingClass()), + klass != nullptr && (klass.Get() == GetOutermostCompilingClass()), dex_pc, needs_access_check); @@ -1715,9 +1722,17 @@ void HInstructionBuilder::BuildTypeCheck(const Instruction& instruction, } } -bool HInstructionBuilder::NeedsAccessCheck(dex::TypeIndex type_index, bool* finalizable) const { +bool HInstructionBuilder::NeedsAccessCheck(dex::TypeIndex type_index, + Handle<mirror::DexCache> dex_cache, + bool* finalizable) const { return !compiler_driver_->CanAccessInstantiableTypeWithoutChecks( - LookupReferrerClass(), LookupResolvedType(type_index, *dex_compilation_unit_), finalizable); + dex_compilation_unit_->GetDexMethodIndex(), dex_cache, type_index, finalizable); +} + +bool HInstructionBuilder::NeedsAccessCheck(dex::TypeIndex type_index, bool* finalizable) const { + ScopedObjectAccess soa(Thread::Current()); + Handle<mirror::DexCache> dex_cache = dex_compilation_unit_->GetDexCache(); + return NeedsAccessCheck(type_index, dex_cache, finalizable); } bool HInstructionBuilder::CanDecodeQuickenedInfo() const { @@ -2757,18 +2772,4 @@ bool HInstructionBuilder::ProcessDexInstruction(const Instruction& instruction, return true; } // NOLINT(readability/fn_size) -ObjPtr<mirror::Class> HInstructionBuilder::LookupResolvedType( - dex::TypeIndex type_index, - const DexCompilationUnit& compilation_unit) const { - return ClassLinker::LookupResolvedType( - type_index, compilation_unit.GetDexCache().Get(), compilation_unit.GetClassLoader().Get()); -} - -ObjPtr<mirror::Class> HInstructionBuilder::LookupReferrerClass() const { - // TODO: Cache the result in a Handle<mirror::Class>. - const DexFile::MethodId& method_id = - dex_compilation_unit_->GetDexFile()->GetMethodId(dex_compilation_unit_->GetDexMethodIndex()); - return LookupResolvedType(method_id.class_idx_, *dex_compilation_unit_); -} - } // namespace art diff --git a/compiler/optimizing/instruction_builder.h b/compiler/optimizing/instruction_builder.h index e735a0c46d..3bb680ce44 100644 --- a/compiler/optimizing/instruction_builder.h +++ b/compiler/optimizing/instruction_builder.h @@ -106,8 +106,11 @@ class HInstructionBuilder : public ValueObject { // Returns whether the current method needs access check for the type. // Output parameter finalizable is set to whether the type is finalizable. - bool NeedsAccessCheck(dex::TypeIndex type_index, /*out*/bool* finalizable) const + bool NeedsAccessCheck(dex::TypeIndex type_index, + Handle<mirror::DexCache> dex_cache, + /*out*/bool* finalizable) const REQUIRES_SHARED(Locks::mutator_lock_); + bool NeedsAccessCheck(dex::TypeIndex type_index, /*out*/bool* finalizable) const; template<typename T> void Unop_12x(const Instruction& instruction, Primitive::Type type, uint32_t dex_pc); @@ -297,12 +300,6 @@ class HInstructionBuilder : public ValueObject { // be found. ArtField* ResolveField(uint16_t field_idx, bool is_static, bool is_put); - ObjPtr<mirror::Class> LookupResolvedType(dex::TypeIndex type_index, - const DexCompilationUnit& compilation_unit) const - REQUIRES_SHARED(Locks::mutator_lock_); - - ObjPtr<mirror::Class> LookupReferrerClass() const REQUIRES_SHARED(Locks::mutator_lock_); - ArenaAllocator* const arena_; HGraph* const graph_; VariableSizedHandleScope* handles_; diff --git a/compiler/optimizing/intrinsics_arm64.cc b/compiler/optimizing/intrinsics_arm64.cc index 1047d3beb6..86e54294ae 100644 --- a/compiler/optimizing/intrinsics_arm64.cc +++ b/compiler/optimizing/intrinsics_arm64.cc @@ -23,7 +23,7 @@ #include "entrypoints/quick/quick_entrypoints.h" #include "intrinsics.h" #include "mirror/array-inl.h" -#include "mirror/string.h" +#include "mirror/string-inl.h" #include "thread.h" #include "utils/arm64/assembler_arm64.h" @@ -1450,16 +1450,47 @@ void IntrinsicCodeGeneratorARM64::VisitStringCompareTo(HInvoke* invoke) { } } +// The cut off for unrolling the loop in String.equals() intrinsic for const strings. +// The normal loop plus the pre-header is 9 instructions without string compression and 12 +// instructions with string compression. We can compare up to 8 bytes in 4 instructions +// (LDR+LDR+CMP+BNE) and up to 16 bytes in 5 instructions (LDP+LDP+CMP+CCMP+BNE). Allow up +// to 10 instructions for the unrolled loop. +constexpr size_t kShortConstStringEqualsCutoffInBytes = 32; + +static const char* GetConstString(HInstruction* candidate, uint32_t* utf16_length) { + if (candidate->IsLoadString()) { + HLoadString* load_string = candidate->AsLoadString(); + const DexFile& dex_file = load_string->GetDexFile(); + return dex_file.StringDataAndUtf16LengthByIdx(load_string->GetStringIndex(), utf16_length); + } + return nullptr; +} + void IntrinsicLocationsBuilderARM64::VisitStringEquals(HInvoke* invoke) { LocationSummary* locations = new (arena_) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified); locations->SetInAt(0, Location::RequiresRegister()); locations->SetInAt(1, Location::RequiresRegister()); - // Temporary registers to store lengths of strings and for calculations. - locations->AddTemp(Location::RequiresRegister()); - locations->AddTemp(Location::RequiresRegister()); + // For the generic implementation and for long const strings we need a temporary. + // We do not need it for short const strings, up to 8 bytes, see code generation below. + uint32_t const_string_length = 0u; + const char* const_string = GetConstString(invoke->InputAt(0), &const_string_length); + if (const_string == nullptr) { + const_string = GetConstString(invoke->InputAt(1), &const_string_length); + } + bool is_compressed = + mirror::kUseStringCompression && + const_string != nullptr && + mirror::String::DexFileStringAllASCII(const_string, const_string_length); + if (const_string == nullptr || const_string_length > (is_compressed ? 8u : 4u)) { + locations->AddTemp(Location::RequiresRegister()); + } + + // TODO: If the String.equals() is used only for an immediately following HIf, we can + // mark it as emitted-at-use-site and emit branches directly to the appropriate blocks. + // Then we shall need an extra temporary register instead of the output register. locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap); } @@ -1473,8 +1504,7 @@ void IntrinsicCodeGeneratorARM64::VisitStringEquals(HInvoke* invoke) { UseScratchRegisterScope scratch_scope(masm); Register temp = scratch_scope.AcquireW(); - Register temp1 = WRegisterFrom(locations->GetTemp(0)); - Register temp2 = WRegisterFrom(locations->GetTemp(1)); + Register temp1 = scratch_scope.AcquireW(); vixl::aarch64::Label loop; vixl::aarch64::Label end; @@ -1510,46 +1540,98 @@ void IntrinsicCodeGeneratorARM64::VisitStringEquals(HInvoke* invoke) { __ B(&return_false, ne); } - // Load `count` fields of this and argument strings. - __ Ldr(temp, MemOperand(str.X(), count_offset)); - __ Ldr(temp1, MemOperand(arg.X(), count_offset)); - // Check if `count` fields are equal, return false if they're not. - // Also compares the compression style, if differs return false. - __ Cmp(temp, temp1); - __ B(&return_false, ne); - // Return true if both strings are empty. Even with string compression `count == 0` means empty. - static_assert(static_cast<uint32_t>(mirror::StringCompressionFlag::kCompressed) == 0u, - "Expecting 0=compressed, 1=uncompressed"); - __ Cbz(temp, &return_true); + // Check if one of the inputs is a const string. Do not special-case both strings + // being const, such cases should be handled by constant folding if needed. + uint32_t const_string_length = 0u; + const char* const_string = GetConstString(invoke->InputAt(0), &const_string_length); + if (const_string == nullptr) { + const_string = GetConstString(invoke->InputAt(1), &const_string_length); + if (const_string != nullptr) { + std::swap(str, arg); // Make sure the const string is in `str`. + } + } + bool is_compressed = + mirror::kUseStringCompression && + const_string != nullptr && + mirror::String::DexFileStringAllASCII(const_string, const_string_length); + + if (const_string != nullptr) { + // Load `count` field of the argument string and check if it matches the const string. + // Also compares the compression style, if differs return false. + __ Ldr(temp, MemOperand(arg.X(), count_offset)); + __ Cmp(temp, Operand(mirror::String::GetFlaggedCount(const_string_length, is_compressed))); + __ B(&return_false, ne); + } else { + // Load `count` fields of this and argument strings. + __ Ldr(temp, MemOperand(str.X(), count_offset)); + __ Ldr(temp1, MemOperand(arg.X(), count_offset)); + // Check if `count` fields are equal, return false if they're not. + // Also compares the compression style, if differs return false. + __ Cmp(temp, temp1); + __ B(&return_false, ne); + } // Assertions that must hold in order to compare strings 8 bytes at a time. DCHECK_ALIGNED(value_offset, 8); static_assert(IsAligned<8>(kObjectAlignment), "String of odd length is not zero padded"); - if (mirror::kUseStringCompression) { - // For string compression, calculate the number of bytes to compare (not chars). - // This could in theory exceed INT32_MAX, so treat temp as unsigned. - __ Lsr(temp, temp, 1u); // Extract length. - __ And(temp1, temp1, Operand(1)); // Extract compression flag. - __ Lsl(temp, temp, temp1); // Calculate number of bytes to compare. - } - - // Store offset of string value in preparation for comparison loop - __ Mov(temp1, value_offset); + if (const_string != nullptr && + const_string_length < (is_compressed ? kShortConstStringEqualsCutoffInBytes + : kShortConstStringEqualsCutoffInBytes / 2u)) { + // Load and compare the contents. Though we know the contents of the short const string + // at compile time, materializing constants may be more code than loading from memory. + int32_t offset = value_offset; + size_t remaining_bytes = + RoundUp(is_compressed ? const_string_length : const_string_length * 2u, 8u); + temp = temp.X(); + temp1 = temp1.X(); + while (remaining_bytes > 8u) { + Register temp2 = XRegisterFrom(locations->GetTemp(0)); + __ Ldp(temp, temp1, MemOperand(str.X(), offset)); + __ Ldp(temp2, out, MemOperand(arg.X(), offset)); + __ Cmp(temp, temp2); + __ Ccmp(temp1, out, NoFlag, eq); + __ B(&return_false, ne); + offset += 2u * sizeof(uint64_t); + remaining_bytes -= 2u * sizeof(uint64_t); + } + if (remaining_bytes != 0u) { + __ Ldr(temp, MemOperand(str.X(), offset)); + __ Ldr(temp1, MemOperand(arg.X(), offset)); + __ Cmp(temp, temp1); + __ B(&return_false, ne); + } + } else { + // Return true if both strings are empty. Even with string compression `count == 0` means empty. + static_assert(static_cast<uint32_t>(mirror::StringCompressionFlag::kCompressed) == 0u, + "Expecting 0=compressed, 1=uncompressed"); + __ Cbz(temp, &return_true); + + if (mirror::kUseStringCompression) { + // For string compression, calculate the number of bytes to compare (not chars). + // This could in theory exceed INT32_MAX, so treat temp as unsigned. + __ And(temp1, temp, Operand(1)); // Extract compression flag. + __ Lsr(temp, temp, 1u); // Extract length. + __ Lsl(temp, temp, temp1); // Calculate number of bytes to compare. + } - temp1 = temp1.X(); - temp2 = temp2.X(); - // Loop to compare strings 8 bytes at a time starting at the front of the string. - // Ok to do this because strings are zero-padded to kObjectAlignment. - __ Bind(&loop); - __ Ldr(out, MemOperand(str.X(), temp1)); - __ Ldr(temp2, MemOperand(arg.X(), temp1)); - __ Add(temp1, temp1, Operand(sizeof(uint64_t))); - __ Cmp(out, temp2); - __ B(&return_false, ne); - // With string compression, we have compared 8 bytes, otherwise 4 chars. - __ Sub(temp, temp, Operand(mirror::kUseStringCompression ? 8 : 4), SetFlags); - __ B(&loop, hi); + // Store offset of string value in preparation for comparison loop + __ Mov(temp1, value_offset); + + temp1 = temp1.X(); + Register temp2 = XRegisterFrom(locations->GetTemp(0)); + // Loop to compare strings 8 bytes at a time starting at the front of the string. + // Ok to do this because strings are zero-padded to kObjectAlignment. + __ Bind(&loop); + __ Ldr(out, MemOperand(str.X(), temp1)); + __ Ldr(temp2, MemOperand(arg.X(), temp1)); + __ Add(temp1, temp1, Operand(sizeof(uint64_t))); + __ Cmp(out, temp2); + __ B(&return_false, ne); + // With string compression, we have compared 8 bytes, otherwise 4 chars. + __ Sub(temp, temp, Operand(mirror::kUseStringCompression ? 8 : 4), SetFlags); + __ B(&loop, hi); + } // Return true and exit the function. // If loop does not result in returning false, we return true. diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc index abbb91a1a9..71a26ebe79 100644 --- a/compiler/optimizing/nodes.cc +++ b/compiler/optimizing/nodes.cc @@ -2038,6 +2038,8 @@ HInstruction* HGraph::InlineInto(HGraph* outer_graph, HInvoke* invoke) { HInstruction* return_value = nullptr; if (GetBlocks().size() == 3) { + // Inliner already made sure we don't inline methods that always throw. + DCHECK(!GetBlocks()[1]->GetLastInstruction()->IsThrow()); // Simple case of an entry block, a body block, and an exit block. // Put the body block's instruction into `invoke`'s block. HBasicBlock* body = GetBlocks()[1]; @@ -2119,33 +2121,60 @@ HInstruction* HGraph::InlineInto(HGraph* outer_graph, HInvoke* invoke) { UpdateLoopAndTryInformationOfNewBlock(to, at, /* replace_if_back_edge */ true); // Update all predecessors of the exit block (now the `to` block) - // to not `HReturn` but `HGoto` instead. - bool returns_void = to->GetPredecessors()[0]->GetLastInstruction()->IsReturnVoid(); - if (to->GetPredecessors().size() == 1) { - HBasicBlock* predecessor = to->GetPredecessors()[0]; + // to not `HReturn` but `HGoto` instead. Special case throwing blocks + // to now get the outer graph exit block as successor. Note that the inliner + // currently doesn't support inlining methods with try/catch. + HPhi* return_value_phi = nullptr; + bool rerun_dominance = false; + bool rerun_loop_analysis = false; + for (size_t pred = 0; pred < to->GetPredecessors().size(); ++pred) { + HBasicBlock* predecessor = to->GetPredecessors()[pred]; HInstruction* last = predecessor->GetLastInstruction(); - if (!returns_void) { - return_value = last->InputAt(0); - } - predecessor->AddInstruction(new (allocator) HGoto(last->GetDexPc())); - predecessor->RemoveInstruction(last); - } else { - if (!returns_void) { - // There will be multiple returns. - return_value = new (allocator) HPhi( - allocator, kNoRegNumber, 0, HPhi::ToPhiType(invoke->GetType()), to->GetDexPc()); - to->AddPhi(return_value->AsPhi()); - } - for (HBasicBlock* predecessor : to->GetPredecessors()) { - HInstruction* last = predecessor->GetLastInstruction(); - if (!returns_void) { + if (last->IsThrow()) { + DCHECK(!at->IsTryBlock()); + predecessor->ReplaceSuccessor(to, outer_graph->GetExitBlock()); + --pred; + // We need to re-run dominance information, as the exit block now has + // a new dominator. + rerun_dominance = true; + if (predecessor->GetLoopInformation() != nullptr) { + // The exit block and blocks post dominated by the exit block do not belong + // to any loop. Because we do not compute the post dominators, we need to re-run + // loop analysis to get the loop information correct. + rerun_loop_analysis = true; + } + } else { + if (last->IsReturnVoid()) { + DCHECK(return_value == nullptr); + DCHECK(return_value_phi == nullptr); + } else { DCHECK(last->IsReturn()); - return_value->AsPhi()->AddInput(last->InputAt(0)); + if (return_value_phi != nullptr) { + return_value_phi->AddInput(last->InputAt(0)); + } else if (return_value == nullptr) { + return_value = last->InputAt(0); + } else { + // There will be multiple returns. + return_value_phi = new (allocator) HPhi( + allocator, kNoRegNumber, 0, HPhi::ToPhiType(invoke->GetType()), to->GetDexPc()); + to->AddPhi(return_value_phi); + return_value_phi->AddInput(return_value); + return_value_phi->AddInput(last->InputAt(0)); + return_value = return_value_phi; + } } predecessor->AddInstruction(new (allocator) HGoto(last->GetDexPc())); predecessor->RemoveInstruction(last); } } + if (rerun_loop_analysis) { + outer_graph->ClearLoopInformation(); + outer_graph->ClearDominanceInformation(); + outer_graph->BuildDominatorTree(); + } else if (rerun_dominance) { + outer_graph->ClearDominanceInformation(); + outer_graph->ComputeDominanceInformation(); + } } // Walk over the entry block and: diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc index 0375c66e42..8638e346fb 100644 --- a/compiler/optimizing/optimizing_compiler.cc +++ b/compiler/optimizing/optimizing_compiler.cc @@ -306,7 +306,7 @@ class OptimizingCompiler FINAL : public Compiler { InvokeType invoke_type, uint16_t class_def_idx, uint32_t method_idx, - Handle<mirror::ClassLoader> class_loader, + jobject class_loader, const DexFile& dex_file, Handle<mirror::DexCache> dex_cache) const OVERRIDE; @@ -375,7 +375,7 @@ class OptimizingCompiler FINAL : public Compiler { InvokeType invoke_type, uint16_t class_def_idx, uint32_t method_idx, - Handle<mirror::ClassLoader> class_loader, + jobject class_loader, const DexFile& dex_file, Handle<mirror::DexCache> dex_cache, ArtMethod* method, @@ -875,7 +875,7 @@ CodeGenerator* OptimizingCompiler::TryCompile(ArenaAllocator* arena, InvokeType invoke_type, uint16_t class_def_idx, uint32_t method_idx, - Handle<mirror::ClassLoader> class_loader, + jobject class_loader, const DexFile& dex_file, Handle<mirror::DexCache> dex_cache, ArtMethod* method, @@ -946,8 +946,11 @@ CodeGenerator* OptimizingCompiler::TryCompile(ArenaAllocator* arena, const uint8_t* interpreter_metadata = nullptr; if (method == nullptr) { ScopedObjectAccess soa(Thread::Current()); + StackHandleScope<1> hs(soa.Self()); + Handle<mirror::ClassLoader> loader(hs.NewHandle( + soa.Decode<mirror::ClassLoader>(class_loader))); method = compiler_driver->ResolveMethod( - soa, dex_cache, class_loader, &dex_compilation_unit, method_idx, invoke_type); + soa, dex_cache, loader, &dex_compilation_unit, method_idx, invoke_type); } // For AOT compilation, we may not get a method, for example if its class is erroneous. // JIT should always have a method. @@ -956,6 +959,16 @@ CodeGenerator* OptimizingCompiler::TryCompile(ArenaAllocator* arena, graph->SetArtMethod(method); ScopedObjectAccess soa(Thread::Current()); interpreter_metadata = method->GetQuickenedInfo(class_linker->GetImagePointerSize()); + dex::TypeIndex type_index = method->GetDeclaringClass()->GetDexTypeIndex(); + + // Update the dex cache if the type is not in it yet. Note that under AOT, + // the verifier must have set it, but under JIT, there's no guarantee, as we + // don't necessarily run the verifier. + // The compiler and the compiler driver assume the compiling class is + // in the dex cache. + if (dex_cache->GetResolvedType(type_index) == nullptr) { + dex_cache->SetResolvedType(type_index, method->GetDeclaringClass()); + } } std::unique_ptr<CodeGenerator> codegen( @@ -1036,7 +1049,7 @@ CompiledMethod* OptimizingCompiler::Compile(const DexFile::CodeItem* code_item, InvokeType invoke_type, uint16_t class_def_idx, uint32_t method_idx, - Handle<mirror::ClassLoader> jclass_loader, + jobject jclass_loader, const DexFile& dex_file, Handle<mirror::DexCache> dex_cache) const { CompilerDriver* compiler_driver = GetCompilerDriver(); @@ -1150,6 +1163,7 @@ bool OptimizingCompiler::JitCompile(Thread* self, Handle<mirror::DexCache> dex_cache(hs.NewHandle(method->GetDexCache())); DCHECK(method->IsCompilable()); + jobject jclass_loader = class_loader.ToJObject(); const DexFile* dex_file = method->GetDexFile(); const uint16_t class_def_idx = method->GetClassDefIndex(); const DexFile::CodeItem* code_item = dex_file->GetCodeItem(method->GetCodeItemOffset()); @@ -1173,7 +1187,7 @@ bool OptimizingCompiler::JitCompile(Thread* self, invoke_type, class_def_idx, method_idx, - class_loader, + jclass_loader, *dex_file, dex_cache, method, @@ -1200,7 +1214,7 @@ bool OptimizingCompiler::JitCompile(Thread* self, Handle<mirror::ObjectArray<mirror::Object>> roots( hs.NewHandle(mirror::ObjectArray<mirror::Object>::Alloc( self, class_linker->GetClassRoot(ClassLinker::kObjectArrayClass), number_of_roots))); - if (roots.Get() == nullptr) { + if (roots == nullptr) { // Out of memory, just clear the exception to avoid any Java exception uncaught problems. DCHECK(self->IsExceptionPending()); self->ClearException(); diff --git a/compiler/optimizing/reference_type_propagation.cc b/compiler/optimizing/reference_type_propagation.cc index 6e332ca59b..c55fccc7d3 100644 --- a/compiler/optimizing/reference_type_propagation.cc +++ b/compiler/optimizing/reference_type_propagation.cc @@ -65,13 +65,11 @@ ReferenceTypeInfo::TypeHandle ReferenceTypePropagation::HandleCache::GetThrowabl class ReferenceTypePropagation::RTPVisitor : public HGraphDelegateVisitor { public: RTPVisitor(HGraph* graph, - Handle<mirror::ClassLoader> class_loader, Handle<mirror::DexCache> hint_dex_cache, HandleCache* handle_cache, ArenaVector<HInstruction*>* worklist, bool is_first_run) : HGraphDelegateVisitor(graph), - class_loader_(class_loader), hint_dex_cache_(hint_dex_cache), handle_cache_(handle_cache), worklist_(worklist), @@ -103,7 +101,6 @@ class ReferenceTypePropagation::RTPVisitor : public HGraphDelegateVisitor { bool is_exact); private: - Handle<mirror::ClassLoader> class_loader_; Handle<mirror::DexCache> hint_dex_cache_; HandleCache* handle_cache_; ArenaVector<HInstruction*>* worklist_; @@ -111,13 +108,11 @@ class ReferenceTypePropagation::RTPVisitor : public HGraphDelegateVisitor { }; ReferenceTypePropagation::ReferenceTypePropagation(HGraph* graph, - Handle<mirror::ClassLoader> class_loader, Handle<mirror::DexCache> hint_dex_cache, VariableSizedHandleScope* handles, bool is_first_run, const char* name) : HOptimization(graph, name), - class_loader_(class_loader), hint_dex_cache_(hint_dex_cache), handle_cache_(handles), worklist_(graph->GetArena()->Adapter(kArenaAllocReferenceTypePropagation)), @@ -152,12 +147,7 @@ void ReferenceTypePropagation::ValidateTypes() { } void ReferenceTypePropagation::Visit(HInstruction* instruction) { - RTPVisitor visitor(graph_, - class_loader_, - hint_dex_cache_, - &handle_cache_, - &worklist_, - is_first_run_); + RTPVisitor visitor(graph_, hint_dex_cache_, &handle_cache_, &worklist_, is_first_run_); instruction->Accept(&visitor); } @@ -331,12 +321,7 @@ void ReferenceTypePropagation::Run() { } void ReferenceTypePropagation::VisitBasicBlock(HBasicBlock* block) { - RTPVisitor visitor(graph_, - class_loader_, - hint_dex_cache_, - &handle_cache_, - &worklist_, - is_first_run_); + RTPVisitor visitor(graph_, hint_dex_cache_, &handle_cache_, &worklist_, is_first_run_); // Handle Phis first as there might be instructions in the same block who depend on them. for (HInstructionIterator it(block->GetPhis()); !it.Done(); it.Advance()) { VisitPhi(it.Current()->AsPhi()); @@ -557,9 +542,8 @@ void ReferenceTypePropagation::RTPVisitor::UpdateReferenceTypeInfo(HInstruction* ScopedObjectAccess soa(Thread::Current()); ObjPtr<mirror::DexCache> dex_cache = FindDexCacheWithHint(soa.Self(), dex_file, hint_dex_cache_); - ObjPtr<mirror::Class> klass = - ClassLinker::LookupResolvedType(type_idx, dex_cache, class_loader_.Get()); - SetClassAsTypeInfo(instr, klass, is_exact); + // Get type from dex cache assuming it was populated by the verifier. + SetClassAsTypeInfo(instr, dex_cache->GetResolvedType(type_idx), is_exact); } void ReferenceTypePropagation::RTPVisitor::VisitNewInstance(HNewInstance* instr) { @@ -572,13 +556,25 @@ void ReferenceTypePropagation::RTPVisitor::VisitNewArray(HNewArray* instr) { SetClassAsTypeInfo(instr, instr->GetLoadClass()->GetClass().Get(), /* is_exact */ true); } +static mirror::Class* GetClassFromDexCache(Thread* self, + const DexFile& dex_file, + dex::TypeIndex type_idx, + Handle<mirror::DexCache> hint_dex_cache) + REQUIRES_SHARED(Locks::mutator_lock_) { + ObjPtr<mirror::DexCache> dex_cache = FindDexCacheWithHint(self, dex_file, hint_dex_cache); + // Get type from dex cache assuming it was populated by the verifier. + return dex_cache->GetResolvedType(type_idx); +} + void ReferenceTypePropagation::RTPVisitor::VisitParameterValue(HParameterValue* instr) { // We check if the existing type is valid: the inliner may have set it. if (instr->GetType() == Primitive::kPrimNot && !instr->GetReferenceTypeInfo().IsValid()) { - UpdateReferenceTypeInfo(instr, - instr->GetTypeIndex(), - instr->GetDexFile(), - /* is_exact */ false); + ScopedObjectAccess soa(Thread::Current()); + mirror::Class* resolved_class = GetClassFromDexCache(soa.Self(), + instr->GetDexFile(), + instr->GetTypeIndex(), + hint_dex_cache_); + SetClassAsTypeInfo(instr, resolved_class, /* is_exact */ false); } } diff --git a/compiler/optimizing/reference_type_propagation.h b/compiler/optimizing/reference_type_propagation.h index 215e96786b..4663471729 100644 --- a/compiler/optimizing/reference_type_propagation.h +++ b/compiler/optimizing/reference_type_propagation.h @@ -33,7 +33,6 @@ namespace art { class ReferenceTypePropagation : public HOptimization { public: ReferenceTypePropagation(HGraph* graph, - Handle<mirror::ClassLoader> class_loader, Handle<mirror::DexCache> hint_dex_cache, VariableSizedHandleScope* handles, bool is_first_run, @@ -106,8 +105,6 @@ class ReferenceTypePropagation : public HOptimization { void ValidateTypes(); - Handle<mirror::ClassLoader> class_loader_; - // Note: hint_dex_cache_ is usually, but not necessarily, the dex cache associated with // graph_->GetDexFile(). Since we may look up also in other dex files, it's used only // as a hint, to reduce the number of calls to the costly ClassLinker::FindDexCache(). diff --git a/compiler/optimizing/reference_type_propagation_test.cc b/compiler/optimizing/reference_type_propagation_test.cc index 84a4bab1a9..b061c871b0 100644 --- a/compiler/optimizing/reference_type_propagation_test.cc +++ b/compiler/optimizing/reference_type_propagation_test.cc @@ -38,7 +38,6 @@ class ReferenceTypePropagationTest : public CommonCompilerTest { void SetupPropagation(VariableSizedHandleScope* handles) { graph_->InitializeInexactObjectRTI(handles); propagation_ = new (&allocator_) ReferenceTypePropagation(graph_, - Handle<mirror::ClassLoader>(), Handle<mirror::DexCache>(), handles, true, diff --git a/compiler/optimizing/sharpening.cc b/compiler/optimizing/sharpening.cc index f07f02a719..be400925d5 100644 --- a/compiler/optimizing/sharpening.cc +++ b/compiler/optimizing/sharpening.cc @@ -163,7 +163,7 @@ HLoadClass::LoadKind HSharpening::SharpenClass(HLoadClass* load_class, if (!compiler_driver->GetSupportBootImageFixup()) { // compiler_driver_test. Do not sharpen. desired_load_kind = HLoadClass::LoadKind::kDexCacheViaMethod; - } else if ((klass.Get() != nullptr) && compiler_driver->IsImageClass( + } else if ((klass != 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() @@ -175,7 +175,7 @@ HLoadClass::LoadKind HSharpening::SharpenClass(HLoadClass* load_class, desired_load_kind = HLoadClass::LoadKind::kBssEntry; } } else { - is_in_boot_image = (klass.Get() != nullptr) && + is_in_boot_image = (klass != 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. @@ -183,7 +183,7 @@ HLoadClass::LoadKind HSharpening::SharpenClass(HLoadClass* load_class, 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; - } else if (klass.Get() != nullptr) { + } else if (klass != nullptr) { desired_load_kind = HLoadClass::LoadKind::kJitTableAddress; } else { // Class not loaded yet. This happens when the dex code requesting diff --git a/compiler/optimizing/ssa_builder.cc b/compiler/optimizing/ssa_builder.cc index 50ab11bc23..487e4dd498 100644 --- a/compiler/optimizing/ssa_builder.cc +++ b/compiler/optimizing/ssa_builder.cc @@ -499,11 +499,7 @@ GraphAnalysisResult SsaBuilder::BuildSsa() { // 4) Compute type of reference type instructions. The pass assumes that // NullConstant has been fixed up. - ReferenceTypePropagation(graph_, - class_loader_, - dex_cache_, - handles_, - /* is_first_run */ true).Run(); + ReferenceTypePropagation(graph_, dex_cache_, handles_, /* is_first_run */ true).Run(); // 5) HInstructionBuilder duplicated ArrayGet instructions with ambiguous type // (int/float or long/double) and marked ArraySets with ambiguous input type. diff --git a/compiler/optimizing/ssa_builder.h b/compiler/optimizing/ssa_builder.h index 978f113ec4..45dac54115 100644 --- a/compiler/optimizing/ssa_builder.h +++ b/compiler/optimizing/ssa_builder.h @@ -48,11 +48,9 @@ namespace art { class SsaBuilder : public ValueObject { public: SsaBuilder(HGraph* graph, - Handle<mirror::ClassLoader> class_loader, Handle<mirror::DexCache> dex_cache, VariableSizedHandleScope* handles) : graph_(graph), - class_loader_(class_loader), dex_cache_(dex_cache), handles_(handles), agets_fixed_(false), @@ -117,7 +115,6 @@ class SsaBuilder : public ValueObject { void RemoveRedundantUninitializedStrings(); HGraph* graph_; - Handle<mirror::ClassLoader> class_loader_; Handle<mirror::DexCache> dex_cache_; VariableSizedHandleScope* const handles_; diff --git a/compiler/optimizing/stack_map_stream.cc b/compiler/optimizing/stack_map_stream.cc index f8e01b7537..1bcc8e1ace 100644 --- a/compiler/optimizing/stack_map_stream.cc +++ b/compiler/optimizing/stack_map_stream.cc @@ -38,19 +38,14 @@ void StackMapStream::BeginStackMapEntry(uint32_t dex_pc, current_entry_.native_pc_code_offset = CodeOffset::FromOffset(native_pc_offset, instruction_set_); current_entry_.register_mask = register_mask; current_entry_.sp_mask = sp_mask; - current_entry_.num_dex_registers = num_dex_registers; current_entry_.inlining_depth = inlining_depth; - current_entry_.dex_register_locations_start_index = dex_register_locations_.size(); current_entry_.inline_infos_start_index = inline_infos_.size(); - current_entry_.dex_register_map_hash = 0; - current_entry_.same_dex_register_map_as_ = kNoSameDexMapFound; current_entry_.stack_mask_index = 0; - if (num_dex_registers != 0) { - current_entry_.live_dex_registers_mask = - ArenaBitVector::Create(allocator_, num_dex_registers, true, kArenaAllocStackMapStream); - } else { - current_entry_.live_dex_registers_mask = nullptr; - } + current_entry_.dex_register_entry.num_dex_registers = num_dex_registers; + current_entry_.dex_register_entry.locations_start_index = dex_register_locations_.size(); + current_entry_.dex_register_entry.live_dex_registers_mask = (num_dex_registers != 0) + ? ArenaBitVector::Create(allocator_, num_dex_registers, true, kArenaAllocStackMapStream) + : nullptr; if (sp_mask != nullptr) { stack_mask_max_ = std::max(stack_mask_max_, sp_mask->GetHighestBitSet()); @@ -65,7 +60,7 @@ void StackMapStream::BeginStackMapEntry(uint32_t dex_pc, } void StackMapStream::EndStackMapEntry() { - current_entry_.same_dex_register_map_as_ = FindEntryWithTheSameDexMap(); + current_entry_.dex_register_map_index = AddDexRegisterMapEntry(current_entry_.dex_register_entry); stack_maps_.push_back(current_entry_); current_entry_ = StackMapEntry(); } @@ -91,19 +86,15 @@ void StackMapStream::AddDexRegisterEntry(DexRegisterLocation::Kind kind, int32_t dex_register_locations_.push_back(index); location_catalog_entries_indices_.Insert(std::make_pair(location, index)); } - - if (in_inline_frame_) { - // TODO: Support sharing DexRegisterMap across InlineInfo. - DCHECK_LT(current_dex_register_, current_inline_info_.num_dex_registers); - current_inline_info_.live_dex_registers_mask->SetBit(current_dex_register_); - } else { - DCHECK_LT(current_dex_register_, current_entry_.num_dex_registers); - current_entry_.live_dex_registers_mask->SetBit(current_dex_register_); - current_entry_.dex_register_map_hash += (1 << - (current_dex_register_ % (sizeof(current_entry_.dex_register_map_hash) * kBitsPerByte))); - current_entry_.dex_register_map_hash += static_cast<uint32_t>(value); - current_entry_.dex_register_map_hash += static_cast<uint32_t>(kind); - } + DexRegisterMapEntry* const entry = in_inline_frame_ + ? ¤t_inline_info_.dex_register_entry + : ¤t_entry_.dex_register_entry; + DCHECK_LT(current_dex_register_, entry->num_dex_registers); + entry->live_dex_registers_mask->SetBit(current_dex_register_); + entry->hash += (1 << + (current_dex_register_ % (sizeof(DexRegisterMapEntry::hash) * kBitsPerByte))); + entry->hash += static_cast<uint32_t>(value); + entry->hash += static_cast<uint32_t>(kind); } current_dex_register_++; } @@ -124,20 +115,19 @@ void StackMapStream::BeginInlineInfoEntry(ArtMethod* method, current_inline_info_.method_index = method->GetDexMethodIndexUnchecked(); } current_inline_info_.dex_pc = dex_pc; - 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) { - current_inline_info_.live_dex_registers_mask = - ArenaBitVector::Create(allocator_, num_dex_registers, true, kArenaAllocStackMapStream); - } else { - current_inline_info_.live_dex_registers_mask = nullptr; - } + current_inline_info_.dex_register_entry.num_dex_registers = num_dex_registers; + current_inline_info_.dex_register_entry.locations_start_index = dex_register_locations_.size(); + current_inline_info_.dex_register_entry.live_dex_registers_mask = (num_dex_registers != 0) + ? ArenaBitVector::Create(allocator_, num_dex_registers, true, kArenaAllocStackMapStream) + : nullptr; current_dex_register_ = 0; } void StackMapStream::EndInlineInfoEntry() { + current_inline_info_.dex_register_map_index = + AddDexRegisterMapEntry(current_inline_info_.dex_register_entry); DCHECK(in_inline_frame_); - DCHECK_EQ(current_dex_register_, current_inline_info_.num_dex_registers) + DCHECK_EQ(current_dex_register_, current_inline_info_.dex_register_entry.num_dex_registers) << "Inline information contains less registers than expected"; in_inline_frame_ = false; inline_infos_.push_back(current_inline_info_); @@ -193,8 +183,7 @@ size_t StackMapStream::ComputeDexRegisterLocationCatalogSize() const { return size; } -size_t StackMapStream::ComputeDexRegisterMapSize(uint32_t num_dex_registers, - const BitVector* live_dex_registers_mask) const { +size_t StackMapStream::DexRegisterMapEntry::ComputeSize(size_t catalog_size) const { // For num_dex_registers == 0u live_dex_registers_mask may be null. if (num_dex_registers == 0u) { return 0u; // No register map will be emitted. @@ -208,8 +197,7 @@ size_t StackMapStream::ComputeDexRegisterMapSize(uint32_t num_dex_registers, // Compute the size of the set of live Dex register entries. size_t number_of_live_dex_registers = live_dex_registers_mask->NumSetBits(); size_t map_entries_size_in_bits = - DexRegisterMap::SingleEntrySizeInBits(location_catalog_entries_.size()) - * number_of_live_dex_registers; + DexRegisterMap::SingleEntrySizeInBits(catalog_size) * number_of_live_dex_registers; size_t map_entries_size_in_bytes = RoundUp(map_entries_size_in_bits, kBitsPerByte) / kBitsPerByte; size += map_entries_size_in_bytes; @@ -218,18 +206,8 @@ size_t StackMapStream::ComputeDexRegisterMapSize(uint32_t num_dex_registers, size_t StackMapStream::ComputeDexRegisterMapsSize() const { size_t size = 0; - size_t inline_info_index = 0; - for (const StackMapEntry& entry : stack_maps_) { - if (entry.same_dex_register_map_as_ == kNoSameDexMapFound) { - size += ComputeDexRegisterMapSize(entry.num_dex_registers, entry.live_dex_registers_mask); - } else { - // Entries with the same dex map will have the same offset. - } - for (size_t j = 0; j < entry.inlining_depth; ++j) { - InlineInfoEntry inline_entry = inline_infos_[inline_info_index++]; - size += ComputeDexRegisterMapSize(inline_entry.num_dex_registers, - inline_entry.live_dex_registers_mask); - } + for (const DexRegisterMapEntry& entry : dex_register_entries_) { + size += entry.ComputeSize(location_catalog_entries_.size()); } return size; } @@ -264,6 +242,30 @@ void StackMapStream::ComputeInlineInfoEncoding(InlineInfoEncoding* encoding, encoding->SetFromSizes(method_index_max, dex_pc_max, extra_data_max, dex_register_maps_bytes); } +size_t StackMapStream::MaybeCopyDexRegisterMap(DexRegisterMapEntry& entry, + size_t* current_offset, + MemoryRegion dex_register_locations_region) { + DCHECK(current_offset != nullptr); + if ((entry.num_dex_registers == 0) || (entry.live_dex_registers_mask->NumSetBits() == 0)) { + // No dex register map needed. + return StackMap::kNoDexRegisterMap; + } + if (entry.offset == DexRegisterMapEntry::kOffsetUnassigned) { + // Not already copied, need to copy and and assign an offset. + entry.offset = *current_offset; + const size_t entry_size = entry.ComputeSize(location_catalog_entries_.size()); + DexRegisterMap dex_register_map( + dex_register_locations_region.Subregion(entry.offset, entry_size)); + *current_offset += entry_size; + // Fill in the map since it was just added. + FillInDexRegisterMap(dex_register_map, + entry.num_dex_registers, + *entry.live_dex_registers_mask, + entry.locations_start_index); + } + return entry.offset; +} + void StackMapStream::FillIn(MemoryRegion region) { DCHECK_EQ(0u, current_entry_.dex_pc) << "EndStackMapEntry not called after BeginStackMapEntry"; DCHECK_NE(0u, needed_size_) << "PrepareForFillIn not called before FillIn"; @@ -311,35 +313,10 @@ void StackMapStream::FillIn(MemoryRegion region) { stack_map.SetRegisterMaskIndex(encoding.stack_map.encoding, entry.register_mask_index); stack_map.SetStackMaskIndex(encoding.stack_map.encoding, entry.stack_mask_index); - if (entry.num_dex_registers == 0 || (entry.live_dex_registers_mask->NumSetBits() == 0)) { - // No dex map available. - stack_map.SetDexRegisterMapOffset(encoding.stack_map.encoding, StackMap::kNoDexRegisterMap); - } else { - // Search for an entry with the same dex map. - if (entry.same_dex_register_map_as_ != kNoSameDexMapFound) { - // If we have a hit reuse the offset. - stack_map.SetDexRegisterMapOffset( - encoding.stack_map.encoding, - code_info.GetStackMapAt(entry.same_dex_register_map_as_, encoding) - .GetDexRegisterMapOffset(encoding.stack_map.encoding)); - } else { - // New dex registers maps should be added to the stack map. - MemoryRegion register_region = dex_register_locations_region.Subregion( - next_dex_register_map_offset, - ComputeDexRegisterMapSize(entry.num_dex_registers, entry.live_dex_registers_mask)); - next_dex_register_map_offset += register_region.size(); - DexRegisterMap dex_register_map(register_region); - stack_map.SetDexRegisterMapOffset( - encoding.stack_map.encoding, - register_region.begin() - dex_register_locations_region.begin()); - - // Set the dex register location. - FillInDexRegisterMap(dex_register_map, - entry.num_dex_registers, - *entry.live_dex_registers_mask, - entry.dex_register_locations_start_index); - } - } + size_t offset = MaybeCopyDexRegisterMap(dex_register_entries_[entry.dex_register_map_index], + &next_dex_register_map_offset, + dex_register_locations_region); + stack_map.SetDexRegisterMapOffset(encoding.stack_map.encoding, offset); // Set the inlining info. if (entry.inlining_depth != 0) { @@ -371,29 +348,13 @@ void StackMapStream::FillIn(MemoryRegion region) { inline_info.SetExtraDataAtDepth(encoding.inline_info.encoding, depth, 1); } inline_info.SetDexPcAtDepth(encoding.inline_info.encoding, depth, inline_entry.dex_pc); - if (inline_entry.num_dex_registers == 0) { - // No dex map available. - inline_info.SetDexRegisterMapOffsetAtDepth(encoding.inline_info.encoding, - depth, - StackMap::kNoDexRegisterMap); - DCHECK(inline_entry.live_dex_registers_mask == nullptr); - } else { - MemoryRegion register_region = dex_register_locations_region.Subregion( - next_dex_register_map_offset, - ComputeDexRegisterMapSize(inline_entry.num_dex_registers, - inline_entry.live_dex_registers_mask)); - next_dex_register_map_offset += register_region.size(); - DexRegisterMap dex_register_map(register_region); - inline_info.SetDexRegisterMapOffsetAtDepth( - encoding.inline_info.encoding, - depth, - register_region.begin() - dex_register_locations_region.begin()); - - FillInDexRegisterMap(dex_register_map, - inline_entry.num_dex_registers, - *inline_entry.live_dex_registers_mask, - inline_entry.dex_register_locations_start_index); - } + size_t dex_register_map_offset = MaybeCopyDexRegisterMap( + dex_register_entries_[inline_entry.dex_register_map_index], + &next_dex_register_map_offset, + dex_register_locations_region); + inline_info.SetDexRegisterMapOffsetAtDepth(encoding.inline_info.encoding, + depth, + dex_register_map_offset); } } else if (encoding.stack_map.encoding.GetInlineInfoEncoding().BitSize() > 0) { stack_map.SetInlineInfoIndex(encoding.stack_map.encoding, StackMap::kNoInlineInfo); @@ -448,34 +409,31 @@ void StackMapStream::FillInDexRegisterMap(DexRegisterMap dex_register_map, } } -size_t StackMapStream::FindEntryWithTheSameDexMap() { - size_t current_entry_index = stack_maps_.size(); - auto entries_it = dex_map_hash_to_stack_map_indices_.find(current_entry_.dex_register_map_hash); +size_t StackMapStream::AddDexRegisterMapEntry(const DexRegisterMapEntry& entry) { + const size_t current_entry_index = dex_register_entries_.size(); + auto entries_it = dex_map_hash_to_stack_map_indices_.find(entry.hash); if (entries_it == dex_map_hash_to_stack_map_indices_.end()) { // We don't have a perfect hash functions so we need a list to collect all stack maps // which might have the same dex register map. ArenaVector<uint32_t> stack_map_indices(allocator_->Adapter(kArenaAllocStackMapStream)); stack_map_indices.push_back(current_entry_index); - dex_map_hash_to_stack_map_indices_.Put(current_entry_.dex_register_map_hash, - std::move(stack_map_indices)); - return kNoSameDexMapFound; - } - - // We might have collisions, so we need to check whether or not we really have a match. - for (uint32_t test_entry_index : entries_it->second) { - if (HaveTheSameDexMaps(GetStackMap(test_entry_index), current_entry_)) { - return test_entry_index; + dex_map_hash_to_stack_map_indices_.Put(entry.hash, std::move(stack_map_indices)); + } else { + // We might have collisions, so we need to check whether or not we really have a match. + for (uint32_t test_entry_index : entries_it->second) { + if (DexRegisterMapEntryEquals(dex_register_entries_[test_entry_index], entry)) { + return test_entry_index; + } } + entries_it->second.push_back(current_entry_index); } - entries_it->second.push_back(current_entry_index); - return kNoSameDexMapFound; + dex_register_entries_.push_back(entry); + return current_entry_index; } -bool StackMapStream::HaveTheSameDexMaps(const StackMapEntry& a, const StackMapEntry& b) const { - if (a.live_dex_registers_mask == nullptr && b.live_dex_registers_mask == nullptr) { - return true; - } - if (a.live_dex_registers_mask == nullptr || b.live_dex_registers_mask == nullptr) { +bool StackMapStream::DexRegisterMapEntryEquals(const DexRegisterMapEntry& a, + const DexRegisterMapEntry& b) const { + if ((a.live_dex_registers_mask == nullptr) != (b.live_dex_registers_mask == nullptr)) { return false; } if (a.num_dex_registers != b.num_dex_registers) { @@ -489,12 +447,12 @@ bool StackMapStream::HaveTheSameDexMaps(const StackMapEntry& a, const StackMapEn } size_t number_of_live_dex_registers = a.live_dex_registers_mask->NumSetBits(); DCHECK_LE(number_of_live_dex_registers, dex_register_locations_.size()); - DCHECK_LE(a.dex_register_locations_start_index, + DCHECK_LE(a.locations_start_index, dex_register_locations_.size() - number_of_live_dex_registers); - DCHECK_LE(b.dex_register_locations_start_index, + DCHECK_LE(b.locations_start_index, dex_register_locations_.size() - number_of_live_dex_registers); - auto a_begin = dex_register_locations_.begin() + a.dex_register_locations_start_index; - auto b_begin = dex_register_locations_.begin() + b.dex_register_locations_start_index; + auto a_begin = dex_register_locations_.begin() + a.locations_start_index; + auto b_begin = dex_register_locations_.begin() + b.locations_start_index; if (!std::equal(a_begin, a_begin + number_of_live_dex_registers, b_begin)) { return false; } @@ -597,10 +555,10 @@ void StackMapStream::CheckCodeInfo(MemoryRegion region) const { CheckDexRegisterMap(code_info, code_info.GetDexRegisterMapOf( - stack_map, encoding, entry.num_dex_registers), - entry.num_dex_registers, - entry.live_dex_registers_mask, - entry.dex_register_locations_start_index); + stack_map, encoding, entry.dex_register_entry.num_dex_registers), + entry.dex_register_entry.num_dex_registers, + entry.dex_register_entry.live_dex_registers_mask, + entry.dex_register_entry.locations_start_index); // Check inline info. DCHECK_EQ(stack_map.HasInlineInfo(stack_map_encoding), (entry.inlining_depth != 0)); @@ -623,10 +581,13 @@ void StackMapStream::CheckCodeInfo(MemoryRegion region) const { CheckDexRegisterMap(code_info, code_info.GetDexRegisterMapAtDepth( - d, inline_info, encoding, inline_entry.num_dex_registers), - inline_entry.num_dex_registers, - inline_entry.live_dex_registers_mask, - inline_entry.dex_register_locations_start_index); + d, + inline_info, + encoding, + inline_entry.dex_register_entry.num_dex_registers), + inline_entry.dex_register_entry.num_dex_registers, + inline_entry.dex_register_entry.live_dex_registers_mask, + inline_entry.dex_register_entry.locations_start_index); } } } diff --git a/compiler/optimizing/stack_map_stream.h b/compiler/optimizing/stack_map_stream.h index 08c1d3e3c0..bba3d51e62 100644 --- a/compiler/optimizing/stack_map_stream.h +++ b/compiler/optimizing/stack_map_stream.h @@ -70,6 +70,7 @@ class StackMapStream : public ValueObject { inline_infos_(allocator->Adapter(kArenaAllocStackMapStream)), stack_masks_(allocator->Adapter(kArenaAllocStackMapStream)), register_masks_(allocator->Adapter(kArenaAllocStackMapStream)), + dex_register_entries_(allocator->Adapter(kArenaAllocStackMapStream)), stack_mask_max_(-1), dex_pc_max_(0), register_mask_max_(0), @@ -89,30 +90,42 @@ class StackMapStream : public ValueObject { code_info_encoding_.reserve(16); } + // A dex register map entry for a single stack map entry, contains what registers are live as + // well as indices into the location catalog. + class DexRegisterMapEntry { + public: + static const size_t kOffsetUnassigned = -1; + + BitVector* live_dex_registers_mask; + uint32_t num_dex_registers; + size_t locations_start_index; + // Computed fields + size_t hash = 0; + size_t offset = kOffsetUnassigned; + + size_t ComputeSize(size_t catalog_size) const; + }; + // See runtime/stack_map.h to know what these fields contain. struct StackMapEntry { uint32_t dex_pc; CodeOffset native_pc_code_offset; uint32_t register_mask; BitVector* sp_mask; - uint32_t num_dex_registers; uint8_t inlining_depth; - size_t dex_register_locations_start_index; size_t inline_infos_start_index; - BitVector* live_dex_registers_mask; - uint32_t dex_register_map_hash; - size_t same_dex_register_map_as_; uint32_t stack_mask_index; uint32_t register_mask_index; + DexRegisterMapEntry dex_register_entry; + size_t dex_register_map_index; }; struct InlineInfoEntry { uint32_t dex_pc; // DexFile::kDexNoIndex for intrinsified native methods. ArtMethod* method; uint32_t method_index; - uint32_t num_dex_registers; - BitVector* live_dex_registers_mask; - size_t dex_register_locations_start_index; + DexRegisterMapEntry dex_register_entry; + size_t dex_register_map_index; }; void BeginStackMapEntry(uint32_t dex_pc, @@ -140,7 +153,8 @@ class StackMapStream : public ValueObject { } void SetStackMapNativePcOffset(size_t i, uint32_t native_pc_offset) { - stack_maps_[i].native_pc_code_offset = CodeOffset::FromOffset(native_pc_offset, instruction_set_); + stack_maps_[i].native_pc_code_offset = + CodeOffset::FromOffset(native_pc_offset, instruction_set_); } // Prepares the stream to fill in a memory region. Must be called before FillIn. @@ -150,8 +164,6 @@ class StackMapStream : public ValueObject { private: size_t ComputeDexRegisterLocationCatalogSize() const; - size_t ComputeDexRegisterMapSize(uint32_t num_dex_registers, - const BitVector* live_dex_registers_mask) const; size_t ComputeDexRegisterMapsSize() const; void ComputeInlineInfoEncoding(InlineInfoEncoding* encoding, size_t dex_register_maps_bytes); @@ -164,15 +176,24 @@ class StackMapStream : public ValueObject { // Returns the number of unique register masks. size_t PrepareRegisterMasks(); - // Returns the index of an entry with the same dex register map as the current_entry, - // or kNoSameDexMapFound if no such entry exists. - size_t FindEntryWithTheSameDexMap(); - bool HaveTheSameDexMaps(const StackMapEntry& a, const StackMapEntry& b) const; + // Deduplicate entry if possible and return the corresponding index into dex_register_entries_ + // array. If entry is not a duplicate, a new entry is added to dex_register_entries_. + size_t AddDexRegisterMapEntry(const DexRegisterMapEntry& entry); + + // Return true if the two dex register map entries are equal. + bool DexRegisterMapEntryEquals(const DexRegisterMapEntry& a, const DexRegisterMapEntry& b) const; + + // Fill in the corresponding entries of a register map. void FillInDexRegisterMap(DexRegisterMap dex_register_map, uint32_t num_dex_registers, const BitVector& live_dex_registers_mask, uint32_t start_index_in_dex_register_locations) const; + // Returns the offset for the dex register inside of the dex register location region. See FillIn. + // Only copies the dex register map if the offset for the entry is not already assigned. + size_t MaybeCopyDexRegisterMap(DexRegisterMapEntry& entry, + size_t* current_offset, + MemoryRegion dex_register_locations_region); void CheckDexRegisterMap(const CodeInfo& code_info, const DexRegisterMap& dex_register_map, size_t num_dex_registers, @@ -199,6 +220,7 @@ class StackMapStream : public ValueObject { ArenaVector<InlineInfoEntry> inline_infos_; ArenaVector<uint8_t> stack_masks_; ArenaVector<uint32_t> register_masks_; + ArenaVector<DexRegisterMapEntry> dex_register_entries_; int stack_mask_max_; uint32_t dex_pc_max_; uint32_t register_mask_max_; diff --git a/compiler/optimizing/stack_map_test.cc b/compiler/optimizing/stack_map_test.cc index bd0aa6dea7..041695187b 100644 --- a/compiler/optimizing/stack_map_test.cc +++ b/compiler/optimizing/stack_map_test.cc @@ -410,6 +410,100 @@ TEST(StackMapTest, Test2) { } } +TEST(StackMapTest, TestDeduplicateInlineInfoDexRegisterMap) { + ArenaPool pool; + ArenaAllocator arena(&pool); + StackMapStream stream(&arena, kRuntimeISA); + ArtMethod art_method; + + ArenaBitVector sp_mask1(&arena, 0, true); + sp_mask1.SetBit(2); + sp_mask1.SetBit(4); + const size_t number_of_dex_registers = 2; + const size_t number_of_dex_registers_in_inline_info = 2; + stream.BeginStackMapEntry(0, 64, 0x3, &sp_mask1, number_of_dex_registers, 1); + stream.AddDexRegisterEntry(Kind::kInStack, 0); // Short location. + stream.AddDexRegisterEntry(Kind::kConstant, -2); // Large location. + stream.BeginInlineInfoEntry(&art_method, 3, number_of_dex_registers_in_inline_info); + stream.AddDexRegisterEntry(Kind::kInStack, 0); // Short location. + stream.AddDexRegisterEntry(Kind::kConstant, -2); // Large location. + stream.EndInlineInfoEntry(); + stream.EndStackMapEntry(); + + size_t size = stream.PrepareForFillIn(); + void* memory = arena.Alloc(size, kArenaAllocMisc); + MemoryRegion region(memory, size); + stream.FillIn(region); + + CodeInfo code_info(region); + CodeInfoEncoding encoding = code_info.ExtractEncoding(); + ASSERT_EQ(1u, code_info.GetNumberOfStackMaps(encoding)); + + uint32_t number_of_catalog_entries = code_info.GetNumberOfLocationCatalogEntries(encoding); + ASSERT_EQ(2u, number_of_catalog_entries); + DexRegisterLocationCatalog location_catalog = code_info.GetDexRegisterLocationCatalog(encoding); + // The Dex register location catalog contains: + // - one 1-byte short Dex register locations, and + // - one 5-byte large Dex register location. + const size_t expected_location_catalog_size = 1u + 5u; + ASSERT_EQ(expected_location_catalog_size, location_catalog.Size()); + + // First stack map. + { + StackMap stack_map = code_info.GetStackMapAt(0, encoding); + ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForDexPc(0, encoding))); + ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForNativePcOffset(64, encoding))); + ASSERT_EQ(0u, stack_map.GetDexPc(encoding.stack_map.encoding)); + ASSERT_EQ(64u, stack_map.GetNativePcOffset(encoding.stack_map.encoding, kRuntimeISA)); + ASSERT_EQ(0x3u, code_info.GetRegisterMaskOf(encoding, stack_map)); + + ASSERT_TRUE(CheckStackMask(code_info, encoding, stack_map, sp_mask1)); + + ASSERT_TRUE(stack_map.HasDexRegisterMap(encoding.stack_map.encoding)); + DexRegisterMap map(code_info.GetDexRegisterMapOf(stack_map, encoding, number_of_dex_registers)); + ASSERT_TRUE(map.IsDexRegisterLive(0)); + ASSERT_TRUE(map.IsDexRegisterLive(1)); + ASSERT_EQ(2u, map.GetNumberOfLiveDexRegisters(number_of_dex_registers)); + // The Dex register map contains: + // - one 1-byte live bit mask, and + // - one 1-byte set of location catalog entry indices composed of two 2-bit values. + size_t expected_map_size = 1u + 1u; + ASSERT_EQ(expected_map_size, map.Size()); + + ASSERT_EQ(Kind::kInStack, map.GetLocationKind(0, number_of_dex_registers, code_info, encoding)); + ASSERT_EQ(Kind::kConstant, + map.GetLocationKind(1, number_of_dex_registers, code_info, encoding)); + ASSERT_EQ(Kind::kInStack, + map.GetLocationInternalKind(0, number_of_dex_registers, code_info, encoding)); + ASSERT_EQ(Kind::kConstantLargeValue, + map.GetLocationInternalKind(1, number_of_dex_registers, code_info, encoding)); + ASSERT_EQ(0, map.GetStackOffsetInBytes(0, number_of_dex_registers, code_info, encoding)); + ASSERT_EQ(-2, map.GetConstant(1, number_of_dex_registers, code_info, encoding)); + + const size_t index0 = + map.GetLocationCatalogEntryIndex(0, number_of_dex_registers, number_of_catalog_entries); + const size_t index1 = + map.GetLocationCatalogEntryIndex(1, number_of_dex_registers, number_of_catalog_entries); + ASSERT_EQ(0u, index0); + ASSERT_EQ(1u, index1); + DexRegisterLocation location0 = location_catalog.GetDexRegisterLocation(index0); + DexRegisterLocation location1 = location_catalog.GetDexRegisterLocation(index1); + ASSERT_EQ(Kind::kInStack, location0.GetKind()); + ASSERT_EQ(Kind::kConstant, location1.GetKind()); + ASSERT_EQ(Kind::kInStack, location0.GetInternalKind()); + ASSERT_EQ(Kind::kConstantLargeValue, location1.GetInternalKind()); + ASSERT_EQ(0, location0.GetValue()); + ASSERT_EQ(-2, location1.GetValue()); + + // Test that the inline info dex register map deduplicated to the same offset as the stack map + // one. + ASSERT_TRUE(stack_map.HasInlineInfo(encoding.stack_map.encoding)); + InlineInfo inline_info = code_info.GetInlineInfoOf(stack_map, encoding); + EXPECT_EQ(inline_info.GetDexRegisterMapOffsetAtDepth(encoding.inline_info.encoding, 0), + stack_map.GetDexRegisterMapOffset(encoding.stack_map.encoding)); + } +} + TEST(StackMapTest, TestNonLiveDexRegisters) { ArenaPool pool; ArenaAllocator arena(&pool); diff --git a/compiler/utils/x86/assembler_x86.cc b/compiler/utils/x86/assembler_x86.cc index a24d49e08d..5a466e1d5d 100644 --- a/compiler/utils/x86/assembler_x86.cc +++ b/compiler/utils/x86/assembler_x86.cc @@ -783,6 +783,79 @@ void X86Assembler::divpd(XmmRegister dst, XmmRegister src) { } +void X86Assembler::movdqa(XmmRegister dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); + EmitUint8(0x0F); + EmitUint8(0x6F); + EmitXmmRegisterOperand(dst, src); +} + + +void X86Assembler::movdqa(XmmRegister dst, const Address& src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); + EmitUint8(0x0F); + EmitUint8(0x6F); + EmitOperand(dst, src); +} + + +void X86Assembler::movdqu(XmmRegister dst, const Address& src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0xF3); + EmitUint8(0x0F); + EmitUint8(0x6F); + EmitOperand(dst, src); +} + + +void X86Assembler::movdqa(const Address& dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); + EmitUint8(0x0F); + EmitUint8(0x7F); + EmitOperand(src, dst); +} + + +void X86Assembler::movdqu(const Address& dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0xF3); + EmitUint8(0x0F); + EmitUint8(0x7F); + EmitOperand(src, dst); +} + + +void X86Assembler::paddd(XmmRegister dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); + EmitUint8(0x0F); + EmitUint8(0xFE); + EmitXmmRegisterOperand(dst, src); +} + + +void X86Assembler::psubd(XmmRegister dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); + EmitUint8(0x0F); + EmitUint8(0xFA); + EmitXmmRegisterOperand(dst, src); +} + + +void X86Assembler::pmulld(XmmRegister dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); + EmitUint8(0x0F); + EmitUint8(0x38); + EmitUint8(0x40); + EmitXmmRegisterOperand(dst, src); +} + + void X86Assembler::cvtsi2ss(XmmRegister dst, Register src) { AssemblerBuffer::EnsureCapacity ensured(&buffer_); EmitUint8(0xF3); @@ -990,10 +1063,27 @@ void X86Assembler::xorpd(XmmRegister dst, XmmRegister src) { } -void X86Assembler::andps(XmmRegister dst, XmmRegister src) { +void X86Assembler::xorps(XmmRegister dst, const Address& src) { AssemblerBuffer::EnsureCapacity ensured(&buffer_); EmitUint8(0x0F); - EmitUint8(0x54); + EmitUint8(0x57); + EmitOperand(dst, src); +} + + +void X86Assembler::xorps(XmmRegister dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x0F); + EmitUint8(0x57); + EmitXmmRegisterOperand(dst, src); +} + + +void X86Assembler::pxor(XmmRegister dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); + EmitUint8(0x0F); + EmitUint8(0xEF); EmitXmmRegisterOperand(dst, src); } @@ -1007,53 +1097,63 @@ void X86Assembler::andpd(XmmRegister dst, XmmRegister src) { } -void X86Assembler::orpd(XmmRegister dst, XmmRegister src) { +void X86Assembler::andpd(XmmRegister dst, const Address& src) { AssemblerBuffer::EnsureCapacity ensured(&buffer_); EmitUint8(0x66); EmitUint8(0x0F); - EmitUint8(0x56); + EmitUint8(0x54); + EmitOperand(dst, src); +} + + +void X86Assembler::andps(XmmRegister dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x0F); + EmitUint8(0x54); EmitXmmRegisterOperand(dst, src); } -void X86Assembler::xorps(XmmRegister dst, const Address& src) { +void X86Assembler::andps(XmmRegister dst, const Address& src) { AssemblerBuffer::EnsureCapacity ensured(&buffer_); EmitUint8(0x0F); - EmitUint8(0x57); + EmitUint8(0x54); EmitOperand(dst, src); } -void X86Assembler::orps(XmmRegister dst, XmmRegister src) { +void X86Assembler::pand(XmmRegister dst, XmmRegister src) { AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); EmitUint8(0x0F); - EmitUint8(0x56); + EmitUint8(0xDB); EmitXmmRegisterOperand(dst, src); } -void X86Assembler::xorps(XmmRegister dst, XmmRegister src) { +void X86Assembler::orpd(XmmRegister dst, XmmRegister src) { AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); EmitUint8(0x0F); - EmitUint8(0x57); + EmitUint8(0x56); EmitXmmRegisterOperand(dst, src); } -void X86Assembler::andps(XmmRegister dst, const Address& src) { +void X86Assembler::orps(XmmRegister dst, XmmRegister src) { AssemblerBuffer::EnsureCapacity ensured(&buffer_); EmitUint8(0x0F); - EmitUint8(0x54); - EmitOperand(dst, src); + EmitUint8(0x56); + EmitXmmRegisterOperand(dst, src); } -void X86Assembler::andpd(XmmRegister dst, const Address& src) { +void X86Assembler::por(XmmRegister dst, XmmRegister src) { AssemblerBuffer::EnsureCapacity ensured(&buffer_); EmitUint8(0x66); EmitUint8(0x0F); - EmitUint8(0x54); - EmitOperand(dst, src); + EmitUint8(0xEB); + EmitXmmRegisterOperand(dst, src); } @@ -1076,6 +1176,16 @@ void X86Assembler::shufps(XmmRegister dst, XmmRegister src, const Immediate& imm } +void X86Assembler::pshufd(XmmRegister dst, XmmRegister src, const Immediate& imm) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); + EmitUint8(0x0F); + EmitUint8(0x70); + EmitXmmRegisterOperand(dst, src); + EmitUint8(imm.value()); +} + + void X86Assembler::fldl(const Address& src) { AssemblerBuffer::EnsureCapacity ensured(&buffer_); EmitUint8(0xDD); diff --git a/compiler/utils/x86/assembler_x86.h b/compiler/utils/x86/assembler_x86.h index 4056ca67fb..4343e2e734 100644 --- a/compiler/utils/x86/assembler_x86.h +++ b/compiler/utils/x86/assembler_x86.h @@ -430,6 +430,16 @@ class X86Assembler FINAL : public Assembler { void mulpd(XmmRegister dst, XmmRegister src); void divpd(XmmRegister dst, XmmRegister src); + void movdqa(XmmRegister dst, XmmRegister src); // move + void movdqa(XmmRegister dst, const Address& src); // load aligned + void movdqu(XmmRegister dst, const Address& src); // load unaligned + void movdqa(const Address& dst, XmmRegister src); // store aligned + void movdqu(const Address& dst, XmmRegister src); // store unaligned + + void paddd(XmmRegister dst, XmmRegister src); // no addr variant (for now) + void psubd(XmmRegister dst, XmmRegister src); + void pmulld(XmmRegister dst, XmmRegister src); + void cvtsi2ss(XmmRegister dst, Register src); void cvtsi2sd(XmmRegister dst, Register src); @@ -463,17 +473,21 @@ class X86Assembler FINAL : public Assembler { void xorpd(XmmRegister dst, XmmRegister src); void xorps(XmmRegister dst, const Address& src); void xorps(XmmRegister dst, XmmRegister src); + void pxor(XmmRegister dst, XmmRegister src); // no addr variant (for now) void andpd(XmmRegister dst, XmmRegister src); void andpd(XmmRegister dst, const Address& src); void andps(XmmRegister dst, XmmRegister src); void andps(XmmRegister dst, const Address& src); + void pand(XmmRegister dst, XmmRegister src); // no addr variant (for now) - void orpd(XmmRegister dst, XmmRegister src); + void orpd(XmmRegister dst, XmmRegister src); // no addr variant (for now) void orps(XmmRegister dst, XmmRegister src); + void por(XmmRegister dst, XmmRegister src); void shufpd(XmmRegister dst, XmmRegister src, const Immediate& imm); void shufps(XmmRegister dst, XmmRegister src, const Immediate& imm); + void pshufd(XmmRegister dst, XmmRegister src, const Immediate& imm); void flds(const Address& src); void fstps(const Address& dst); diff --git a/compiler/utils/x86/assembler_x86_test.cc b/compiler/utils/x86/assembler_x86_test.cc index 1768d8b715..c6ab893aea 100644 --- a/compiler/utils/x86/assembler_x86_test.cc +++ b/compiler/utils/x86/assembler_x86_test.cc @@ -467,6 +467,28 @@ TEST_F(AssemblerX86Test, MovupdAddr) { DriverStr(expected, "movupd_address"); } +TEST_F(AssemblerX86Test, Movdqa) { + DriverStr(RepeatFF(&x86::X86Assembler::movdqa, "movdqa %{reg2}, %{reg1}"), "movdqa"); +} + +TEST_F(AssemblerX86Test, MovdqaAddr) { + GetAssembler()->movdqa(x86::XmmRegister(x86::XMM0), x86::Address(x86::Register(x86::ESP), 4)); + GetAssembler()->movdqa(x86::Address(x86::Register(x86::ESP), 2), x86::XmmRegister(x86::XMM1)); + const char* expected = + "movdqa 0x4(%ESP), %xmm0\n" + "movdqa %xmm1, 0x2(%ESP)\n"; + DriverStr(expected, "movdqa_address"); +} + +TEST_F(AssemblerX86Test, MovdquAddr) { + GetAssembler()->movdqu(x86::XmmRegister(x86::XMM0), x86::Address(x86::Register(x86::ESP), 4)); + GetAssembler()->movdqu(x86::Address(x86::Register(x86::ESP), 2), x86::XmmRegister(x86::XMM1)); + const char* expected = + "movdqu 0x4(%ESP), %xmm0\n" + "movdqu %xmm1, 0x2(%ESP)\n"; + DriverStr(expected, "movdqu_address"); +} + TEST_F(AssemblerX86Test, AddPS) { DriverStr(RepeatFF(&x86::X86Assembler::addps, "addps %{reg2}, %{reg1}"), "addps"); } @@ -499,6 +521,54 @@ TEST_F(AssemblerX86Test, DivPD) { DriverStr(RepeatFF(&x86::X86Assembler::divpd, "divpd %{reg2}, %{reg1}"), "divpd"); } +TEST_F(AssemblerX86Test, PAddD) { + DriverStr(RepeatFF(&x86::X86Assembler::paddd, "paddd %{reg2}, %{reg1}"), "paddd"); +} + +TEST_F(AssemblerX86Test, PSubD) { + DriverStr(RepeatFF(&x86::X86Assembler::psubd, "psubd %{reg2}, %{reg1}"), "psubd"); +} + +TEST_F(AssemblerX86Test, PMullD) { + DriverStr(RepeatFF(&x86::X86Assembler::pmulld, "pmulld %{reg2}, %{reg1}"), "pmulld"); +} + +TEST_F(AssemblerX86Test, XorPD) { + DriverStr(RepeatFF(&x86::X86Assembler::xorpd, "xorpd %{reg2}, %{reg1}"), "xorpd"); +} + +TEST_F(AssemblerX86Test, XorPS) { + DriverStr(RepeatFF(&x86::X86Assembler::xorps, "xorps %{reg2}, %{reg1}"), "xorps"); +} + +TEST_F(AssemblerX86Test, PXor) { + DriverStr(RepeatFF(&x86::X86Assembler::pxor, "pxor %{reg2}, %{reg1}"), "pxor"); +} + +TEST_F(AssemblerX86Test, AndPD) { + DriverStr(RepeatFF(&x86::X86Assembler::andpd, "andpd %{reg2}, %{reg1}"), "andpd"); +} + +TEST_F(AssemblerX86Test, AndPS) { + DriverStr(RepeatFF(&x86::X86Assembler::andps, "andps %{reg2}, %{reg1}"), "andps"); +} + +TEST_F(AssemblerX86Test, PAnd) { + DriverStr(RepeatFF(&x86::X86Assembler::pand, "pand %{reg2}, %{reg1}"), "pand"); +} + +TEST_F(AssemblerX86Test, OrPD) { + DriverStr(RepeatFF(&x86::X86Assembler::orpd, "orpd %{reg2}, %{reg1}"), "orpd"); +} + +TEST_F(AssemblerX86Test, OrPS) { + DriverStr(RepeatFF(&x86::X86Assembler::orps, "orps %{reg2}, %{reg1}"), "orps"); +} + +TEST_F(AssemblerX86Test, POr) { + DriverStr(RepeatFF(&x86::X86Assembler::por, "por %{reg2}, %{reg1}"), "por"); +} + TEST_F(AssemblerX86Test, ShufPS) { DriverStr(RepeatFFI(&x86::X86Assembler::shufps, 1, "shufps ${imm}, %{reg2}, %{reg1}"), "shufps"); } @@ -507,6 +577,10 @@ TEST_F(AssemblerX86Test, ShufPD) { DriverStr(RepeatFFI(&x86::X86Assembler::shufpd, 1, "shufpd ${imm}, %{reg2}, %{reg1}"), "shufpd"); } +TEST_F(AssemblerX86Test, PShufD) { + DriverStr(RepeatFFI(&x86::X86Assembler::pshufd, 1, "pshufd ${imm}, %{reg2}, %{reg1}"), "pshufd"); +} + ///////////////// // Near labels // ///////////////// diff --git a/compiler/utils/x86_64/assembler_x86_64.cc b/compiler/utils/x86_64/assembler_x86_64.cc index c2c44ab58c..b41be80ae4 100644 --- a/compiler/utils/x86_64/assembler_x86_64.cc +++ b/compiler/utils/x86_64/assembler_x86_64.cc @@ -832,6 +832,87 @@ void X86_64Assembler::divpd(XmmRegister dst, XmmRegister src) { } +void X86_64Assembler::movdqa(XmmRegister dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); + EmitOptionalRex32(dst, src); + EmitUint8(0x0F); + EmitUint8(0x6F); + EmitXmmRegisterOperand(dst.LowBits(), src); +} + + +void X86_64Assembler::movdqa(XmmRegister dst, const Address& src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); + EmitOptionalRex32(dst, src); + EmitUint8(0x0F); + EmitUint8(0x6F); + EmitOperand(dst.LowBits(), src); +} + + +void X86_64Assembler::movdqu(XmmRegister dst, const Address& src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0xF3); + EmitOptionalRex32(dst, src); + EmitUint8(0x0F); + EmitUint8(0x6F); + EmitOperand(dst.LowBits(), src); +} + + +void X86_64Assembler::movdqa(const Address& dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); + EmitOptionalRex32(src, dst); + EmitUint8(0x0F); + EmitUint8(0x7F); + EmitOperand(src.LowBits(), dst); +} + + +void X86_64Assembler::movdqu(const Address& dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0xF3); + EmitOptionalRex32(src, dst); + EmitUint8(0x0F); + EmitUint8(0x7F); + EmitOperand(src.LowBits(), dst); +} + + +void X86_64Assembler::paddd(XmmRegister dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); + EmitOptionalRex32(dst, src); + EmitUint8(0x0F); + EmitUint8(0xFE); + EmitXmmRegisterOperand(dst.LowBits(), src); +} + + +void X86_64Assembler::psubd(XmmRegister dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); + EmitOptionalRex32(dst, src); + EmitUint8(0x0F); + EmitUint8(0xFA); + EmitXmmRegisterOperand(dst.LowBits(), src); +} + + +void X86_64Assembler::pmulld(XmmRegister dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); + EmitOptionalRex32(dst, src); + EmitUint8(0x0F); + EmitUint8(0x38); + EmitUint8(0x40); + EmitXmmRegisterOperand(dst.LowBits(), src); +} + + void X86_64Assembler::cvtsi2ss(XmmRegister dst, CpuRegister src) { cvtsi2ss(dst, src, false); } @@ -1170,6 +1251,16 @@ void X86_64Assembler::xorps(XmmRegister dst, XmmRegister src) { } +void X86_64Assembler::pxor(XmmRegister dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); + EmitOptionalRex32(dst, src); + EmitUint8(0x0F); + EmitUint8(0xEF); + EmitXmmRegisterOperand(dst.LowBits(), src); +} + + void X86_64Assembler::andpd(XmmRegister dst, const Address& src) { AssemblerBuffer::EnsureCapacity ensured(&buffer_); EmitUint8(0x66); @@ -1196,6 +1287,15 @@ void X86_64Assembler::andps(XmmRegister dst, XmmRegister src) { EmitXmmRegisterOperand(dst.LowBits(), src); } +void X86_64Assembler::pand(XmmRegister dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); + EmitOptionalRex32(dst, src); + EmitUint8(0x0F); + EmitUint8(0xDB); + EmitXmmRegisterOperand(dst.LowBits(), src); +} + void X86_64Assembler::orpd(XmmRegister dst, XmmRegister src) { AssemblerBuffer::EnsureCapacity ensured(&buffer_); EmitUint8(0x66); @@ -1213,6 +1313,14 @@ void X86_64Assembler::orps(XmmRegister dst, XmmRegister src) { EmitXmmRegisterOperand(dst.LowBits(), src); } +void X86_64Assembler::por(XmmRegister dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); + EmitOptionalRex32(dst, src); + EmitUint8(0x0F); + EmitUint8(0xEB); + EmitXmmRegisterOperand(dst.LowBits(), src); +} void X86_64Assembler::shufpd(XmmRegister dst, XmmRegister src, const Immediate& imm) { AssemblerBuffer::EnsureCapacity ensured(&buffer_); @@ -1235,6 +1343,17 @@ void X86_64Assembler::shufps(XmmRegister dst, XmmRegister src, const Immediate& } +void X86_64Assembler::pshufd(XmmRegister dst, XmmRegister src, const Immediate& imm) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); + EmitOptionalRex32(dst, src); + EmitUint8(0x0F); + EmitUint8(0x70); + EmitXmmRegisterOperand(dst.LowBits(), src); + EmitUint8(imm.value()); +} + + void X86_64Assembler::fldl(const Address& src) { AssemblerBuffer::EnsureCapacity ensured(&buffer_); EmitUint8(0xDD); diff --git a/compiler/utils/x86_64/assembler_x86_64.h b/compiler/utils/x86_64/assembler_x86_64.h index e140b45a00..43ea12a4cb 100644 --- a/compiler/utils/x86_64/assembler_x86_64.h +++ b/compiler/utils/x86_64/assembler_x86_64.h @@ -446,6 +446,16 @@ class X86_64Assembler FINAL : public Assembler { void mulpd(XmmRegister dst, XmmRegister src); void divpd(XmmRegister dst, XmmRegister src); + void movdqa(XmmRegister dst, XmmRegister src); // move + void movdqa(XmmRegister dst, const Address& src); // load aligned + void movdqu(XmmRegister dst, const Address& src); // load unaligned + void movdqa(const Address& dst, XmmRegister src); // store aligned + void movdqu(const Address& dst, XmmRegister src); // store unaligned + + void paddd(XmmRegister dst, XmmRegister src); // no addr variant (for now) + void psubd(XmmRegister dst, XmmRegister src); + void pmulld(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); @@ -487,16 +497,20 @@ class X86_64Assembler FINAL : public Assembler { void xorpd(XmmRegister dst, XmmRegister src); void xorps(XmmRegister dst, const Address& src); void xorps(XmmRegister dst, XmmRegister src); + void pxor(XmmRegister dst, XmmRegister src); // no addr variant (for now) void andpd(XmmRegister dst, const Address& src); void andpd(XmmRegister dst, XmmRegister src); - void andps(XmmRegister dst, XmmRegister src); + void andps(XmmRegister dst, XmmRegister src); // no addr variant (for now) + void pand(XmmRegister dst, XmmRegister src); - void orpd(XmmRegister dst, XmmRegister src); + void orpd(XmmRegister dst, XmmRegister src); // no addr variant (for now) void orps(XmmRegister dst, XmmRegister src); + void por(XmmRegister dst, XmmRegister src); void shufpd(XmmRegister dst, XmmRegister src, const Immediate& imm); void shufps(XmmRegister dst, XmmRegister src, const Immediate& imm); + void pshufd(XmmRegister dst, XmmRegister src, const Immediate& imm); void flds(const Address& src); void fstps(const Address& dst); diff --git a/compiler/utils/x86_64/assembler_x86_64_test.cc b/compiler/utils/x86_64/assembler_x86_64_test.cc index efa5cc97ea..aeb1911835 100644 --- a/compiler/utils/x86_64/assembler_x86_64_test.cc +++ b/compiler/utils/x86_64/assembler_x86_64_test.cc @@ -1034,6 +1034,28 @@ TEST_F(AssemblerX86_64Test, Movsd) { DriverStr(RepeatFF(&x86_64::X86_64Assembler::movsd, "movsd %{reg2}, %{reg1}"), "movsd"); } +TEST_F(AssemblerX86_64Test, Movdqa) { + DriverStr(RepeatFF(&x86_64::X86_64Assembler::movdqa, "movdqa %{reg2}, %{reg1}"), "movapd"); +} + +TEST_F(AssemblerX86_64Test, MovdqaAddr) { + GetAssembler()->movdqa(x86_64::XmmRegister(x86_64::XMM0), x86_64::Address(x86_64::CpuRegister(x86_64::RSP), 4)); + GetAssembler()->movdqa(x86_64::Address(x86_64::CpuRegister(x86_64::RSP), 2), x86_64::XmmRegister(x86_64::XMM1)); + const char* expected = + "movdqa 0x4(%RSP), %xmm0\n" + "movdqa %xmm1, 0x2(%RSP)\n"; + DriverStr(expected, "movdqa_address"); +} + +TEST_F(AssemblerX86_64Test, MovdquAddr) { + GetAssembler()->movdqu(x86_64::XmmRegister(x86_64::XMM0), x86_64::Address(x86_64::CpuRegister(x86_64::RSP), 4)); + GetAssembler()->movdqu(x86_64::Address(x86_64::CpuRegister(x86_64::RSP), 2), x86_64::XmmRegister(x86_64::XMM1)); + const char* expected = + "movdqu 0x4(%RSP), %xmm0\n" + "movdqu %xmm1, 0x2(%RSP)\n"; + DriverStr(expected, "movdqu_address"); +} + TEST_F(AssemblerX86_64Test, Movd1) { DriverStr(RepeatFR(&x86_64::X86_64Assembler::movd, "movd %{reg2}, %{reg1}"), "movd.1"); } @@ -1106,6 +1128,18 @@ TEST_F(AssemblerX86_64Test, Divpd) { DriverStr(RepeatFF(&x86_64::X86_64Assembler::divpd, "divpd %{reg2}, %{reg1}"), "divpd"); } +TEST_F(AssemblerX86_64Test, Paddd) { + DriverStr(RepeatFF(&x86_64::X86_64Assembler::paddd, "paddd %{reg2}, %{reg1}"), "paddd"); +} + +TEST_F(AssemblerX86_64Test, Psubd) { + DriverStr(RepeatFF(&x86_64::X86_64Assembler::psubd, "psubd %{reg2}, %{reg1}"), "psubd"); +} + +TEST_F(AssemblerX86_64Test, Pmulld) { + DriverStr(RepeatFF(&x86_64::X86_64Assembler::pmulld, "pmulld %{reg2}, %{reg1}"), "pmulld"); +} + TEST_F(AssemblerX86_64Test, Cvtsi2ss) { DriverStr(RepeatFr(&x86_64::X86_64Assembler::cvtsi2ss, "cvtsi2ss %{reg2}, %{reg1}"), "cvtsi2ss"); } @@ -1187,6 +1221,10 @@ TEST_F(AssemblerX86_64Test, Xorpd) { DriverStr(RepeatFF(&x86_64::X86_64Assembler::xorpd, "xorpd %{reg2}, %{reg1}"), "xorpd"); } +TEST_F(AssemblerX86_64Test, Pxor) { + DriverStr(RepeatFF(&x86_64::X86_64Assembler::pxor, "pxor %{reg2}, %{reg1}"), "pxor"); +} + TEST_F(AssemblerX86_64Test, Andps) { DriverStr(RepeatFF(&x86_64::X86_64Assembler::andps, "andps %{reg2}, %{reg1}"), "andps"); } @@ -1195,6 +1233,10 @@ TEST_F(AssemblerX86_64Test, Andpd) { DriverStr(RepeatFF(&x86_64::X86_64Assembler::andpd, "andpd %{reg2}, %{reg1}"), "andpd"); } +TEST_F(AssemblerX86_64Test, Pand) { + DriverStr(RepeatFF(&x86_64::X86_64Assembler::pand, "pand %{reg2}, %{reg1}"), "pand"); +} + TEST_F(AssemblerX86_64Test, Orps) { DriverStr(RepeatFF(&x86_64::X86_64Assembler::orps, "orps %{reg2}, %{reg1}"), "orps"); } @@ -1203,6 +1245,10 @@ TEST_F(AssemblerX86_64Test, Orpd) { DriverStr(RepeatFF(&x86_64::X86_64Assembler::orpd, "orpd %{reg2}, %{reg1}"), "orpd"); } +TEST_F(AssemblerX86_64Test, Por) { + DriverStr(RepeatFF(&x86_64::X86_64Assembler::por, "por %{reg2}, %{reg1}"), "por"); +} + TEST_F(AssemblerX86_64Test, Shufps) { DriverStr(RepeatFFI(&x86_64::X86_64Assembler::shufps, 1, "shufps ${imm}, %{reg2}, %{reg1}"), "shufps"); } @@ -1211,6 +1257,10 @@ TEST_F(AssemblerX86_64Test, Shufpd) { DriverStr(RepeatFFI(&x86_64::X86_64Assembler::shufpd, 1, "shufpd ${imm}, %{reg2}, %{reg1}"), "shufpd"); } +TEST_F(AssemblerX86_64Test, PShufd) { + DriverStr(RepeatFFI(&x86_64::X86_64Assembler::pshufd, 1, "pshufd ${imm}, %{reg2}, %{reg1}"), "pshufd"); +} + TEST_F(AssemblerX86_64Test, UcomissAddress) { GetAssembler()->ucomiss(x86_64::XmmRegister(x86_64::XMM0), x86_64::Address( x86_64::CpuRegister(x86_64::RDI), x86_64::CpuRegister(x86_64::RBX), x86_64::TIMES_4, 12)); diff --git a/compiler/verifier_deps_test.cc b/compiler/verifier_deps_test.cc index 5fc9972d09..c892b25ed3 100644 --- a/compiler/verifier_deps_test.cc +++ b/compiler/verifier_deps_test.cc @@ -233,7 +233,7 @@ class VerifierDepsTest : public CommonCompilerTest { const DexFile::ClassDef& class_def = dex_file->GetClassDef(i); const char* descriptor = dex_file->GetClassDescriptor(class_def); cls.Assign(class_linker_->FindClass(soa.Self(), descriptor, class_loader_handle)); - if (cls.Get() == nullptr) { + if (cls == nullptr) { CHECK(soa.Self()->IsExceptionPending()); soa.Self()->ClearException(); } else if (set.find(class_def.class_idx_) == set.end()) { |