diff options
| -rw-r--r-- | compiler/optimizing/induction_var_range.cc | 5 | ||||
| -rw-r--r-- | compiler/optimizing/induction_var_range.h | 10 | ||||
| -rw-r--r-- | compiler/optimizing/induction_var_range_test.cc | 8 | ||||
| -rw-r--r-- | compiler/optimizing/loop_optimization.cc | 4 | ||||
| -rw-r--r-- | profman/profile_assistant_test.cc | 24 | ||||
| -rw-r--r-- | profman/profman.cc | 135 | ||||
| -rw-r--r-- | runtime/jit/profile_compilation_info.cc | 39 | ||||
| -rw-r--r-- | runtime/jit/profile_compilation_info.h | 17 | ||||
| -rw-r--r-- | runtime/mirror/class_ext.cc | 5 | ||||
| -rw-r--r-- | runtime/openjdkjvmti/ti_redefine.cc | 115 | ||||
| -rw-r--r-- | runtime/openjdkjvmti/ti_redefine.h | 13 | ||||
| -rw-r--r-- | test/115-native-bridge/nativebridge.cc | 16 | ||||
| -rw-r--r-- | test/623-checker-loop-regressions/src/Main.java | 19 |
13 files changed, 261 insertions, 149 deletions
diff --git a/compiler/optimizing/induction_var_range.cc b/compiler/optimizing/induction_var_range.cc index d6513c8e34..1c8674d522 100644 --- a/compiler/optimizing/induction_var_range.cc +++ b/compiler/optimizing/induction_var_range.cc @@ -383,12 +383,13 @@ bool InductionVarRange::IsFinite(HLoopInformation* loop, /*out*/ int64_t* tc) co return false; } -bool InductionVarRange::IsUnitStride(HInstruction* instruction, +bool InductionVarRange::IsUnitStride(HInstruction* context, + HInstruction* instruction, /*out*/ HInstruction** offset) const { HLoopInformation* loop = nullptr; HInductionVarAnalysis::InductionInfo* info = nullptr; HInductionVarAnalysis::InductionInfo* trip = nullptr; - if (HasInductionInfo(instruction, instruction, &loop, &info, &trip)) { + if (HasInductionInfo(context, instruction, &loop, &info, &trip)) { if (info->induction_class == HInductionVarAnalysis::kLinear && info->op_b->operation == HInductionVarAnalysis::kFetch && !HInductionVarAnalysis::IsNarrowingLinear(info)) { diff --git a/compiler/optimizing/induction_var_range.h b/compiler/optimizing/induction_var_range.h index 0858d73982..a8ee829d08 100644 --- a/compiler/optimizing/induction_var_range.h +++ b/compiler/optimizing/induction_var_range.h @@ -156,10 +156,14 @@ class InductionVarRange { bool IsFinite(HLoopInformation* loop, /*out*/ int64_t* tc) const; /** - * Checks if instruction is a unit stride induction inside the closest enveloping loop. - * Returns invariant offset on success. + * Checks if the given instruction is a unit stride induction inside the closest enveloping + * loop of the context that is defined by the first parameter (e.g. pass an array reference + * as context and the index as instruction to make sure the stride is tested against the + * loop that envelops the reference the closest). Returns invariant offset on success. */ - bool IsUnitStride(HInstruction* instruction, /*out*/ HInstruction** offset) const; + bool IsUnitStride(HInstruction* context, + HInstruction* instruction, + /*out*/ HInstruction** offset) const; /** * Generates the trip count expression for the given loop. Code is generated in given block diff --git a/compiler/optimizing/induction_var_range_test.cc b/compiler/optimizing/induction_var_range_test.cc index fcdf8eb7dc..d01d3146fc 100644 --- a/compiler/optimizing/induction_var_range_test.cc +++ b/compiler/optimizing/induction_var_range_test.cc @@ -770,7 +770,7 @@ TEST_F(InductionVarRangeTest, ConstantTripCountUp) { EXPECT_TRUE(range_.IsFinite(loop_header_->GetLoopInformation(), &tc)); EXPECT_EQ(1000, tc); HInstruction* offset = nullptr; - EXPECT_TRUE(range_.IsUnitStride(phi, &offset)); + EXPECT_TRUE(range_.IsUnitStride(phi, phi, &offset)); EXPECT_TRUE(offset == nullptr); HInstruction* tce = range_.GenerateTripCount( loop_header_->GetLoopInformation(), graph_, loop_preheader_); @@ -826,7 +826,7 @@ TEST_F(InductionVarRangeTest, ConstantTripCountDown) { EXPECT_TRUE(range_.IsFinite(loop_header_->GetLoopInformation(), &tc)); EXPECT_EQ(1000, tc); HInstruction* offset = nullptr; - EXPECT_FALSE(range_.IsUnitStride(phi, &offset)); + EXPECT_FALSE(range_.IsUnitStride(phi, phi, &offset)); HInstruction* tce = range_.GenerateTripCount( loop_header_->GetLoopInformation(), graph_, loop_preheader_); ASSERT_TRUE(tce != nullptr); @@ -908,7 +908,7 @@ TEST_F(InductionVarRangeTest, SymbolicTripCountUp) { EXPECT_TRUE(range_.IsFinite(loop_header_->GetLoopInformation(), &tc)); EXPECT_EQ(0, tc); // unknown HInstruction* offset = nullptr; - EXPECT_TRUE(range_.IsUnitStride(phi, &offset)); + EXPECT_TRUE(range_.IsUnitStride(phi, phi, &offset)); EXPECT_TRUE(offset == nullptr); HInstruction* tce = range_.GenerateTripCount( loop_header_->GetLoopInformation(), graph_, loop_preheader_); @@ -994,7 +994,7 @@ TEST_F(InductionVarRangeTest, SymbolicTripCountDown) { EXPECT_TRUE(range_.IsFinite(loop_header_->GetLoopInformation(), &tc)); EXPECT_EQ(0, tc); // unknown HInstruction* offset = nullptr; - EXPECT_FALSE(range_.IsUnitStride(phi, &offset)); + EXPECT_FALSE(range_.IsUnitStride(phi, phi, &offset)); HInstruction* tce = range_.GenerateTripCount( loop_header_->GetLoopInformation(), graph_, loop_preheader_); ASSERT_TRUE(tce != nullptr); diff --git a/compiler/optimizing/loop_optimization.cc b/compiler/optimizing/loop_optimization.cc index 1a79601a93..bf18cc9bbc 100644 --- a/compiler/optimizing/loop_optimization.cc +++ b/compiler/optimizing/loop_optimization.cc @@ -580,7 +580,7 @@ bool HLoopOptimization::VectorizeDef(LoopNode* node, HInstruction* offset = nullptr; if (TrySetVectorType(type, &restrictions) && node->loop_info->IsDefinedOutOfTheLoop(base) && - induction_range_.IsUnitStride(index, &offset) && + induction_range_.IsUnitStride(instruction, index, &offset) && VectorizeUse(node, value, generate_code, type, restrictions)) { if (generate_code) { GenerateVecSub(index, offset); @@ -633,7 +633,7 @@ bool HLoopOptimization::VectorizeUse(LoopNode* node, HInstruction* offset = nullptr; if (type == instruction->GetType() && node->loop_info->IsDefinedOutOfTheLoop(base) && - induction_range_.IsUnitStride(index, &offset)) { + induction_range_.IsUnitStride(instruction, index, &offset)) { if (generate_code) { GenerateVecSub(index, offset); GenerateVecMem(instruction, vector_map_->Get(index), nullptr, type); diff --git a/profman/profile_assistant_test.cc b/profman/profile_assistant_test.cc index 94f6e708fe..38254e2436 100644 --- a/profman/profile_assistant_test.cc +++ b/profman/profile_assistant_test.cc @@ -179,12 +179,12 @@ class ProfileAssistantTest : public CommonRuntimeTest { return true; } - bool DumpClasses(const std::string& filename, std::string* file_contents) { + bool DumpClassesAndMethods(const std::string& filename, std::string* file_contents) { ScratchFile class_names_file; std::string profman_cmd = GetProfmanCmd(); std::vector<std::string> argv_str; argv_str.push_back(profman_cmd); - argv_str.push_back("--dump-classes"); + argv_str.push_back("--dump-classes-and-methods"); argv_str.push_back("--profile-file=" + filename); argv_str.push_back("--apk=" + GetLibCoreDexFileNames()[0]); argv_str.push_back("--dex-location=" + GetLibCoreDexFileNames()[0]); @@ -208,7 +208,7 @@ class ProfileAssistantTest : public CommonRuntimeTest { profile_file.GetFilename(), GetLibCoreDexFileNames()[0])); profile_file.GetFile()->ResetOffset(); - EXPECT_TRUE(DumpClasses(profile_file.GetFilename(), output_file_contents)); + EXPECT_TRUE(DumpClassesAndMethods(profile_file.GetFilename(), output_file_contents)); return true; } @@ -501,18 +501,16 @@ TEST_F(ProfileAssistantTest, TestProfileCreationAllMatch) { std::vector<std::string> class_names = { "Ljava/lang/Comparable;", "Ljava/lang/Math;", - "Ljava/lang/Object;" + "Ljava/lang/Object;", + "Ljava/lang/Object;-><init>()V" }; - std::string input_file_contents; - std::string expected_contents; + std::string file_contents; for (std::string& class_name : class_names) { - input_file_contents += class_name + std::string("\n"); - expected_contents += DescriptorToDot(class_name.c_str()) + - std::string("\n"); + file_contents += class_name + std::string("\n"); } std::string output_file_contents; - ASSERT_TRUE(CreateAndDump(input_file_contents, &output_file_contents)); - ASSERT_EQ(output_file_contents, expected_contents); + ASSERT_TRUE(CreateAndDump(file_contents, &output_file_contents)); + ASSERT_EQ(output_file_contents, file_contents); } TEST_F(ProfileAssistantTest, TestProfileCreationGenerateMethods) { @@ -567,8 +565,8 @@ TEST_F(ProfileAssistantTest, TestProfileCreationOneNotMatched) { std::string output_file_contents; ASSERT_TRUE(CreateAndDump(input_file_contents, &output_file_contents)); std::string expected_contents = - DescriptorToDot(class_names[1].c_str()) + std::string("\n") + - DescriptorToDot(class_names[2].c_str()) + std::string("\n"); + class_names[1] + std::string("\n") + + class_names[2] + std::string("\n"); ASSERT_EQ(output_file_contents, expected_contents); } diff --git a/profman/profman.cc b/profman/profman.cc index 5504695a96..cf1ffe1ec6 100644 --- a/profman/profman.cc +++ b/profman/profman.cc @@ -90,8 +90,9 @@ NO_RETURN static void Usage(const char *fmt, ...) { UsageError(""); UsageError(" --dump-output-to-fd=<number>: redirects --dump-only output to a file descriptor."); UsageError(""); - UsageError(" --dump-classes: dumps a sorted list of classes that are in the specified profile"); - UsageError(" file to standard output (default) in a human readable form."); + UsageError(" --dump-classes-and-methods: dumps a sorted list of classes and methods that are"); + UsageError(" in the specified profile file to standard output (default) in a human"); + UsageError(" readable form. The output is valid input for --create-profile-from"); UsageError(""); UsageError(" --profile-file=<filename>: specify profiler output file to use for compilation."); UsageError(" Can be specified multiple time, in which case the data from the different"); @@ -120,7 +121,8 @@ NO_RETURN static void Usage(const char *fmt, ...) { UsageError(" --generate-test-profile-seed=<number>: seed for random number generator used when"); UsageError(" generating random test profiles. Defaults to using NanoTime."); UsageError(""); - UsageError(" --create-profile-from=<filename>: creates a profile from a list of classes."); + UsageError(" --create-profile-from=<filename>: creates a profile from a list of classes and"); + UsageError(" methods."); UsageError(""); UsageError(" --dex-location=<string>: location string to use with corresponding"); UsageError(" apk-fd to find dex files"); @@ -153,7 +155,7 @@ class ProfMan FINAL { ProfMan() : reference_profile_file_fd_(kInvalidFd), dump_only_(false), - dump_classes_(false), + dump_classes_and_methods_(false), dump_output_to_fd_(kInvalidFd), test_profile_num_dex_(kDefaultTestProfileNumDex), test_profile_method_ratio_(kDefaultTestProfileMethodRatio), @@ -187,8 +189,8 @@ class ProfMan FINAL { } if (option == "--dump-only") { dump_only_ = true; - } else if (option == "--dump-classes") { - dump_classes_ = true; + } else if (option == "--dump-classes-and-methods") { + dump_classes_and_methods_ = true; } else if (option.starts_with("--create-profile-from=")) { create_profile_from_file_ = option.substr(strlen("--create-profile-from=")).ToString(); } else if (option.starts_with("--dump-output-to-fd=")) { @@ -411,27 +413,45 @@ class ProfMan FINAL { return dump_only_; } - bool GetClassNames(int fd, - std::vector<std::unique_ptr<const DexFile>>* dex_files, - std::set<std::string>* class_names) { + bool GetClassNamesAndMethods(int fd, + std::vector<std::unique_ptr<const DexFile>>* dex_files, + std::set<std::string>* out_lines) { ProfileCompilationInfo profile_info; if (!profile_info.Load(fd)) { LOG(ERROR) << "Cannot load profile info"; return false; } - profile_info.GetClassNames(dex_files, class_names); + for (const std::unique_ptr<const DexFile>& dex_file : *dex_files) { + std::set<dex::TypeIndex> class_types; + ProfileCompilationInfo::MethodMap methods; + if (profile_info.GetClassesAndMethods(dex_file.get(), &class_types, &methods)) { + for (const dex::TypeIndex& type_index : class_types) { + const DexFile::TypeId& type_id = dex_file->GetTypeId(type_index); + out_lines->insert(std::string(dex_file->GetTypeDescriptor(type_id))); + } + for (const auto& pair : methods) { + // TODO: Process inline caches. + const uint16_t dex_method_idx = pair.first; + const DexFile::MethodId& id = dex_file->GetMethodId(dex_method_idx); + std::string signature_string(dex_file->GetMethodSignature(id).ToString()); + std::string type_string(dex_file->GetTypeDescriptor(dex_file->GetTypeId(id.class_idx_))); + std::string method_name(dex_file->GetMethodName(id)); + out_lines->insert(type_string + kMethodSep + method_name + signature_string); + } + } + } return true; } - bool GetClassNames(const std::string& profile_file, - std::vector<std::unique_ptr<const DexFile>>* dex_files, - std::set<std::string>* class_names) { + bool GetClassNamesAndMethods(const std::string& profile_file, + std::vector<std::unique_ptr<const DexFile>>* dex_files, + std::set<std::string>* out_lines) { int fd = open(profile_file.c_str(), O_RDONLY); if (!FdIsValid(fd)) { LOG(ERROR) << "Cannot open " << profile_file << strerror(errno); return false; } - if (!GetClassNames(fd, dex_files, class_names)) { + if (!GetClassNamesAndMethods(fd, dex_files, out_lines)) { return false; } if (close(fd) < 0) { @@ -455,26 +475,26 @@ class ProfMan FINAL { std::set<std::string> class_names; if (!profile_files_fd_.empty()) { for (int profile_file_fd : profile_files_fd_) { - if (!GetClassNames(profile_file_fd, &dex_files, &class_names)) { + if (!GetClassNamesAndMethods(profile_file_fd, &dex_files, &class_names)) { return -1; } } } if (!profile_files_.empty()) { for (const std::string& profile_file : profile_files_) { - if (!GetClassNames(profile_file, &dex_files, &class_names)) { + if (!GetClassNamesAndMethods(profile_file, &dex_files, &class_names)) { return -1; } } } // Concatenate class names from reference profile file. if (FdIsValid(reference_profile_file_fd_)) { - if (!GetClassNames(reference_profile_file_fd_, &dex_files, &class_names)) { + if (!GetClassNamesAndMethods(reference_profile_file_fd_, &dex_files, &class_names)) { return -1; } } if (!reference_profile_file_.empty()) { - if (!GetClassNames(reference_profile_file_, &dex_files, &class_names)) { + if (!GetClassNamesAndMethods(reference_profile_file_, &dex_files, &class_names)) { return -1; } } @@ -494,8 +514,8 @@ class ProfMan FINAL { return 0; } - bool ShouldOnlyDumpClasses() { - return dump_classes_; + bool ShouldOnlyDumpClassesAndMethods() { + return dump_classes_and_methods_; } // Read lines from the given file, dropping comments and empty lines. Post-process each line with @@ -561,17 +581,9 @@ class ProfMan FINAL { return false; } - // Find the method specified by method_spec in the class class_ref. The method - // must have a single INVOKE_VIRTUAL in its byte code. - // Upon success it returns true and stores the method index and the invoke dex pc - // in the output parameters. - // The format of the method spec is "inlinePolymorphic(LSuper;)I+LSubA;,LSubB;,LSubC;". - // - // TODO(calin): support INVOKE_INTERFACE and the range variants. - bool FindMethodWithSingleInvoke(const ProfileMethodInfo::ProfileClassReference& class_ref, - const std::string& method_spec, - /*out*/uint16_t* method_index, - /*out*/uint32_t* dex_pc) { + // Find the method specified by method_spec in the class class_ref. + uint32_t FindMethodIndex(const ProfileMethodInfo::ProfileClassReference& class_ref, + const std::string& method_spec) { std::vector<std::string> name_and_signature; Split(method_spec, kProfileParsingFirstCharInSignature, &name_and_signature); if (name_and_signature.size() != 2) { @@ -584,38 +596,50 @@ class ProfMan FINAL { const DexFile::StringId* name_id = dex_file->FindStringId(name.c_str()); if (name_id == nullptr) { LOG(ERROR) << "Could not find name: " << name; - return false; + return DexFile::kDexNoIndex; } dex::TypeIndex return_type_idx; std::vector<dex::TypeIndex> param_type_idxs; if (!dex_file->CreateTypeList(signature, &return_type_idx, ¶m_type_idxs)) { LOG(ERROR) << "Could not create type list" << signature; - return false; + return DexFile::kDexNoIndex; } const DexFile::ProtoId* proto_id = dex_file->FindProtoId(return_type_idx, param_type_idxs); if (proto_id == nullptr) { LOG(ERROR) << "Could not find proto_id: " << name; - return false; + return DexFile::kDexNoIndex; } const DexFile::MethodId* method_id = dex_file->FindMethodId( dex_file->GetTypeId(class_ref.type_index), *name_id, *proto_id); if (method_id == nullptr) { LOG(ERROR) << "Could not find method_id: " << name; - return false; + return DexFile::kDexNoIndex; } - *method_index = dex_file->GetIndexForMethodId(*method_id); + return dex_file->GetIndexForMethodId(*method_id); + } + // Given a method, return true if the method has a single INVOKE_VIRTUAL in its byte code. + // Upon success it returns true and stores the method index and the invoke dex pc + // in the output parameters. + // The format of the method spec is "inlinePolymorphic(LSuper;)I+LSubA;,LSubB;,LSubC;". + // + // TODO(calin): support INVOKE_INTERFACE and the range variants. + bool HasSingleInvoke(const ProfileMethodInfo::ProfileClassReference& class_ref, + uint16_t method_index, + /*out*/uint32_t* dex_pc) { + const DexFile* dex_file = class_ref.dex_file; uint32_t offset = dex_file->FindCodeItemOffset( *dex_file->FindClassDef(class_ref.type_index), - *method_index); + method_index); const DexFile::CodeItem* code_item = dex_file->GetCodeItem(offset); bool found_invoke = false; for (CodeItemIterator it(*code_item); !it.Done(); it.Advance()) { if (it.CurrentInstruction().Opcode() == Instruction::INVOKE_VIRTUAL) { if (found_invoke) { - LOG(ERROR) << "Multiple invoke INVOKE_VIRTUAL found: " << name; + LOG(ERROR) << "Multiple invoke INVOKE_VIRTUAL found: " + << dex_file->PrettyMethod(method_index); return false; } found_invoke = true; @@ -623,7 +647,7 @@ class ProfMan FINAL { } } if (!found_invoke) { - LOG(ERROR) << "Could not find any INVOKE_VIRTUAL: " << name; + LOG(ERROR) << "Could not find any INVOKE_VIRTUAL: " << dex_file->PrettyMethod(method_index); } return found_invoke; } @@ -709,24 +733,29 @@ class ProfMan FINAL { return false; } - uint16_t method_index; - uint32_t dex_pc; - if (!FindMethodWithSingleInvoke(class_ref, method_spec, &method_index, &dex_pc)) { + const uint32_t method_index = FindMethodIndex(class_ref, method_spec); + if (method_index == DexFile::kDexNoIndex) { return false; } - std::vector<ProfileMethodInfo::ProfileClassReference> classes(inline_cache_elems.size()); - size_t class_it = 0; - for (const std::string& ic_class : inline_cache_elems) { - if (!FindClass(dex_files, ic_class, &(classes[class_it++]))) { - LOG(ERROR) << "Could not find class: " << ic_class; + + std::vector<ProfileMethodInfo> pmi; + std::vector<ProfileMethodInfo::ProfileInlineCache> inline_caches; + if (is_missing_types || !inline_cache_elems.empty()) { + uint32_t dex_pc; + if (!HasSingleInvoke(class_ref, method_index, &dex_pc)) { return false; } + std::vector<ProfileMethodInfo::ProfileClassReference> classes(inline_cache_elems.size()); + size_t class_it = 0; + for (const std::string& ic_class : inline_cache_elems) { + if (!FindClass(dex_files, ic_class, &(classes[class_it++]))) { + LOG(ERROR) << "Could not find class: " << ic_class; + return false; + } + } + inline_caches.emplace_back(dex_pc, is_missing_types, classes); } - std::vector<ProfileMethodInfo::ProfileInlineCache> inline_caches; - inline_caches.emplace_back(dex_pc, is_missing_types, classes); - std::vector<ProfileMethodInfo> pmi; pmi.emplace_back(class_ref.dex_file, method_index, inline_caches); - profile->AddMethodsAndClasses(pmi, std::set<DexCacheResolvedClasses>()); return true; } @@ -877,7 +906,7 @@ class ProfMan FINAL { std::string reference_profile_file_; int reference_profile_file_fd_; bool dump_only_; - bool dump_classes_; + bool dump_classes_and_methods_; int dump_output_to_fd_; std::string test_profile_; std::string create_profile_from_file_; @@ -901,7 +930,7 @@ static int profman(int argc, char** argv) { if (profman.ShouldOnlyDumpProfile()) { return profman.DumpProfileInfo(); } - if (profman.ShouldOnlyDumpClasses()) { + if (profman.ShouldOnlyDumpClassesAndMethods()) { return profman.DumpClasses(); } if (profman.ShouldCreateProfile()) { diff --git a/runtime/jit/profile_compilation_info.cc b/runtime/jit/profile_compilation_info.cc index 24ea27529a..2d1601e883 100644 --- a/runtime/jit/profile_compilation_info.cc +++ b/runtime/jit/profile_compilation_info.cc @@ -1081,35 +1081,18 @@ std::string ProfileCompilationInfo::DumpInfo(const std::vector<const DexFile*>* return os.str(); } -void ProfileCompilationInfo::GetClassNames( - const std::vector<std::unique_ptr<const DexFile>>* dex_files, - std::set<std::string>* class_names) const { - std::unique_ptr<const std::vector<const DexFile*>> non_owning_dex_files( - MakeNonOwningVector(dex_files)); - GetClassNames(non_owning_dex_files.get(), class_names); -} - -void ProfileCompilationInfo::GetClassNames(const std::vector<const DexFile*>* dex_files, - std::set<std::string>* class_names) const { - if (info_.empty()) { - return; - } - for (const DexFileData* dex_data : info_) { - const DexFile* dex_file = nullptr; - if (dex_files != nullptr) { - for (size_t i = 0; i < dex_files->size(); i++) { - if (dex_data->profile_key == GetProfileDexFileKey((*dex_files)[i]->GetLocation()) && - dex_data->checksum == (*dex_files)[i]->GetLocationChecksum()) { - dex_file = (*dex_files)[i]; - } - } - } - for (const auto class_it : dex_data->class_set) { - if (dex_file != nullptr) { - class_names->insert(std::string(dex_file->PrettyType(class_it))); - } - } +bool ProfileCompilationInfo::GetClassesAndMethods(const DexFile* dex_file, + std::set<dex::TypeIndex>* class_set, + MethodMap* method_map) const { + std::set<std::string> ret; + std::string profile_key = GetProfileDexFileKey(dex_file->GetLocation()); + const DexFileData* dex_data = FindDexData(profile_key); + if (dex_data == nullptr || dex_data->checksum != dex_file->GetLocationChecksum()) { + return false; } + *method_map = dex_data->method_map; + *class_set = dex_data->class_set; + return true; } bool ProfileCompilationInfo::Equals(const ProfileCompilationInfo& other) { diff --git a/runtime/jit/profile_compilation_info.h b/runtime/jit/profile_compilation_info.h index 87f763686e..f68ed5df67 100644 --- a/runtime/jit/profile_compilation_info.h +++ b/runtime/jit/profile_compilation_info.h @@ -168,6 +168,9 @@ class ProfileCompilationInfo { // The inline cache map: DexPc -> DexPcData. using InlineCacheMap = SafeMap<uint16_t, DexPcData>; + // Maps a method dex index to its inline cache. + using MethodMap = SafeMap<uint16_t, InlineCacheMap>; + // Encodes the full set of inline caches for a given method. // The dex_references vector is indexed according to the ClassReference::dex_profile_index. // i.e. the dex file of any ClassReference present in the inline caches can be found at @@ -234,11 +237,12 @@ class ProfileCompilationInfo { std::string DumpInfo(const std::vector<const DexFile*>* dex_files, bool print_full_dex_location = true) const; - void GetClassNames(const std::vector<std::unique_ptr<const DexFile>>* dex_files, - std::set<std::string>* class_names) const; - - void GetClassNames(const std::vector<const DexFile*>* dex_files, - std::set<std::string>* class_names) const; + // Return the classes and methods for a given dex file through out args. The otu args are the set + // of class as well as the methods and their associated inline caches. Returns true if the dex + // file is register and has a matching checksum, false otherwise. + bool GetClassesAndMethods(const DexFile* dex_file, + std::set<dex::TypeIndex>* class_set, + MethodMap* method_map) const; // Perform an equality test with the `other` profile information. bool Equals(const ProfileCompilationInfo& other); @@ -280,9 +284,6 @@ class ProfileCompilationInfo { kProfileLoadSuccess }; - // Maps a method dex index to its inline cache. - using MethodMap = SafeMap<uint16_t, InlineCacheMap>; - // Internal representation of the profile information belonging to a dex file. // Note that we could do without profile_key (the key used to encode the dex // file in the profile) and profile_index (the index of the dex file in the diff --git a/runtime/mirror/class_ext.cc b/runtime/mirror/class_ext.cc index 94e4b88f6c..32d49bbc10 100644 --- a/runtime/mirror/class_ext.cc +++ b/runtime/mirror/class_ext.cc @@ -40,8 +40,6 @@ uint32_t ClassExt::ClassSize(PointerSize pointer_size) { void ClassExt::SetObsoleteArrays(ObjPtr<PointerArray> methods, ObjPtr<ObjectArray<DexCache>> dex_caches) { - DCHECK_EQ(GetLockOwnerThreadId(), Thread::Current()->GetThreadId()) - << "Obsolete arrays are set without synchronization!"; CHECK_EQ(methods.IsNull(), dex_caches.IsNull()); auto obsolete_dex_cache_off = OFFSET_OF_OBJECT_MEMBER(ClassExt, obsolete_dex_caches_); auto obsolete_methods_off = OFFSET_OF_OBJECT_MEMBER(ClassExt, obsolete_methods_); @@ -54,8 +52,7 @@ void ClassExt::SetObsoleteArrays(ObjPtr<PointerArray> methods, // these arrays are written into without all threads being suspended we have a race condition! This // race could cause obsolete methods to be missed. bool ClassExt::ExtendObsoleteArrays(Thread* self, uint32_t increase) { - DCHECK_EQ(GetLockOwnerThreadId(), Thread::Current()->GetThreadId()) - << "Obsolete arrays are set without synchronization!"; + // TODO It would be good to check that we have locked the class associated with this ClassExt. StackHandleScope<5> hs(self); Handle<ClassExt> h_this(hs.NewHandle(this)); Handle<PointerArray> old_methods(hs.NewHandle(h_this->GetObsoleteMethods())); diff --git a/runtime/openjdkjvmti/ti_redefine.cc b/runtime/openjdkjvmti/ti_redefine.cc index 7d95de823e..06550791c1 100644 --- a/runtime/openjdkjvmti/ti_redefine.cc +++ b/runtime/openjdkjvmti/ti_redefine.cc @@ -216,7 +216,6 @@ class ObsoleteMethodStackVisitor : public art::StackVisitor { jvmtiError Redefiner::IsModifiableClass(jvmtiEnv* env ATTRIBUTE_UNUSED, jclass klass, jboolean* is_redefinable) { - // TODO Check for the appropriate feature flags once we have enabled them. art::Thread* self = art::Thread::Current(); art::ScopedObjectAccess soa(self); art::StackHandleScope<1> hs(self); @@ -790,9 +789,11 @@ class RedefinitionDataHolder { kSlotNewDexCache = 3, kSlotMirrorClass = 4, kSlotOrigDexFile = 5, + kSlotOldObsoleteMethods = 6, + kSlotOldDexCaches = 7, // Must be last one. - kNumSlots = 6, + kNumSlots = 8, }; // This needs to have a HandleScope passed in that is capable of creating a new Handle without @@ -815,7 +816,6 @@ class RedefinitionDataHolder { return arr_.IsNull(); } - // TODO Maybe make an iterable view type to simplify using this. art::mirror::ClassLoader* GetSourceClassLoader(jint klass_index) const REQUIRES_SHARED(art::Locks::mutator_lock_) { return art::down_cast<art::mirror::ClassLoader*>(GetSlot(klass_index, kSlotSourceClassLoader)); @@ -842,6 +842,18 @@ class RedefinitionDataHolder { return art::down_cast<art::mirror::Object*>(GetSlot(klass_index, kSlotOrigDexFile)); } + art::mirror::PointerArray* GetOldObsoleteMethods(jint klass_index) const + REQUIRES_SHARED(art::Locks::mutator_lock_) { + return art::down_cast<art::mirror::PointerArray*>( + GetSlot(klass_index, kSlotOldObsoleteMethods)); + } + + art::mirror::ObjectArray<art::mirror::DexCache>* GetOldDexCaches(jint klass_index) const + REQUIRES_SHARED(art::Locks::mutator_lock_) { + return art::down_cast<art::mirror::ObjectArray<art::mirror::DexCache>*>( + GetSlot(klass_index, kSlotOldDexCaches)); + } + void SetSourceClassLoader(jint klass_index, art::mirror::ClassLoader* loader) REQUIRES_SHARED(art::Locks::mutator_lock_) { SetSlot(klass_index, kSlotSourceClassLoader, loader); @@ -866,6 +878,14 @@ class RedefinitionDataHolder { REQUIRES_SHARED(art::Locks::mutator_lock_) { SetSlot(klass_index, kSlotOrigDexFile, bytes); } + void SetOldObsoleteMethods(jint klass_index, art::mirror::PointerArray* methods) + REQUIRES_SHARED(art::Locks::mutator_lock_) { + SetSlot(klass_index, kSlotOldObsoleteMethods, methods); + } + void SetOldDexCaches(jint klass_index, art::mirror::ObjectArray<art::mirror::DexCache>* caches) + REQUIRES_SHARED(art::Locks::mutator_lock_) { + SetSlot(klass_index, kSlotOldDexCaches, caches); + } int32_t Length() const REQUIRES_SHARED(art::Locks::mutator_lock_) { return arr_->GetLength() / kNumSlots; @@ -979,6 +999,15 @@ class RedefinitionDataIter { REQUIRES_SHARED(art::Locks::mutator_lock_) { return holder_.GetOriginalDexFile(idx_); } + art::mirror::PointerArray* GetOldObsoleteMethods() const + REQUIRES_SHARED(art::Locks::mutator_lock_) { + return holder_.GetOldObsoleteMethods(idx_); + } + art::mirror::ObjectArray<art::mirror::DexCache>* GetOldDexCaches() const + REQUIRES_SHARED(art::Locks::mutator_lock_) { + return holder_.GetOldDexCaches(idx_); + } + int32_t GetIndex() const { return idx_; } @@ -1004,6 +1033,14 @@ class RedefinitionDataIter { REQUIRES_SHARED(art::Locks::mutator_lock_) { holder_.SetOriginalDexFile(idx_, bytes); } + void SetOldObsoleteMethods(art::mirror::PointerArray* methods) + REQUIRES_SHARED(art::Locks::mutator_lock_) { + holder_.SetOldObsoleteMethods(idx_, methods); + } + void SetOldDexCaches(art::mirror::ObjectArray<art::mirror::DexCache>* caches) + REQUIRES_SHARED(art::Locks::mutator_lock_) { + holder_.SetOldDexCaches(idx_, caches); + } private: int32_t idx_; @@ -1164,9 +1201,15 @@ bool Redefiner::CheckAllRedefinitionAreValid() { return true; } -bool Redefiner::EnsureAllClassAllocationsFinished() { - for (Redefiner::ClassRedefinition& redef : redefinitions_) { - if (!redef.EnsureClassAllocationsFinished()) { +void Redefiner::RestoreObsoleteMethodMapsIfUnneeded(RedefinitionDataHolder& holder) { + for (RedefinitionDataIter data = holder.begin(); data != holder.end(); ++data) { + data.GetRedefinition().RestoreObsoleteMethodMapsIfUnneeded(&data); + } +} + +bool Redefiner::EnsureAllClassAllocationsFinished(RedefinitionDataHolder& holder) { + for (RedefinitionDataIter data = holder.begin(); data != holder.end(); ++data) { + if (!data.GetRedefinition().EnsureClassAllocationsFinished(&data)) { return false; } } @@ -1239,13 +1282,9 @@ jvmtiError Redefiner::Run() { // between allocating them and pausing all threads before we can update them so we need to do a // try loop. if (!CheckAllRedefinitionAreValid() || - !EnsureAllClassAllocationsFinished() || + !EnsureAllClassAllocationsFinished(holder) || !FinishAllRemainingAllocations(holder) || !CheckAllClassesAreVerified(holder)) { - // TODO Null out the ClassExt fields we allocated (if possible, might be racing with another - // redefineclass call which made it even bigger. Leak shouldn't be huge (2x array of size - // declared_methods_.length) but would be good to get rid of. All other allocations should be - // cleaned up by the GC eventually. return result_; } @@ -1277,11 +1316,11 @@ jvmtiError Redefiner::Run() { redef.FindAndAllocateObsoleteMethods(klass); redef.UpdateClass(klass, data.GetNewDexCache(), data.GetOriginalDexFile()); } + RestoreObsoleteMethodMapsIfUnneeded(holder); // TODO We should check for if any of the redefined methods are intrinsic methods here and, if any // are, force a full-world deoptimization before finishing redefinition. If we don't do this then // methods that have been jitted prior to the current redefinition being applied might continue // to use the old versions of the intrinsics! - // TODO Shrink the obsolete method maps if possible? // TODO Do the dex_file release at a more reasonable place. This works but it muddles who really // owns the DexFile and when ownership is transferred. ReleaseAllDexFiles(); @@ -1372,9 +1411,35 @@ void Redefiner::ClassRedefinition::UpdateClass( ext->SetOriginalDexFile(original_dex_file); } +// Restores the old obsolete methods maps if it turns out they weren't needed (ie there were no new +// obsolete methods). +void Redefiner::ClassRedefinition::RestoreObsoleteMethodMapsIfUnneeded( + const RedefinitionDataIter* cur_data) { + art::mirror::Class* klass = GetMirrorClass(); + art::mirror::ClassExt* ext = klass->GetExtData(); + art::mirror::PointerArray* methods = ext->GetObsoleteMethods(); + int32_t old_length = + cur_data->GetOldDexCaches() == nullptr ? 0 : cur_data->GetOldDexCaches()->GetLength(); + int32_t expected_length = + old_length + klass->NumDirectMethods() + klass->NumDeclaredVirtualMethods(); + // Check to make sure we are only undoing this one. + if (expected_length == methods->GetLength()) { + for (int32_t i = old_length; i < expected_length; i++) { + if (methods->GetElementPtrSize<art::ArtMethod*>(i, art::kRuntimePointerSize) != nullptr) { + // We actually have some new obsolete methods. Just abort since we cannot safely shrink the + // obsolete methods array. + return; + } + } + // No new obsolete methods! We can get rid of the maps. + ext->SetObsoleteArrays(cur_data->GetOldObsoleteMethods(), cur_data->GetOldDexCaches()); + } +} + // This function does all (java) allocations we need to do for the Class being redefined. // TODO Change this name maybe? -bool Redefiner::ClassRedefinition::EnsureClassAllocationsFinished() { +bool Redefiner::ClassRedefinition::EnsureClassAllocationsFinished( + /*out*/RedefinitionDataIter* cur_data) { art::StackHandleScope<2> hs(driver_->self_); art::Handle<art::mirror::Class> klass(hs.NewHandle( driver_->self_->DecodeJObject(klass_)->AsClass())); @@ -1391,22 +1456,20 @@ bool Redefiner::ClassRedefinition::EnsureClassAllocationsFinished() { RecordFailure(ERR(OUT_OF_MEMORY), "Could not allocate ClassExt"); return false; } - // Allocate the 2 arrays that make up the obsolete methods map. Since the contents of the arrays + // First save the old values of the 2 arrays that make up the obsolete methods maps. Then + // allocate the 2 arrays that make up the obsolete methods map. Since the contents of the arrays // are only modified when all threads (other than the modifying one) are suspended we don't need // to worry about missing the unsyncronized writes to the array. We do synchronize when setting it // however, since that can happen at any time. - // TODO Clear these after we walk the stacks in order to free them in the (likely?) event there - // are no obsolete methods. - { - art::ObjectLock<art::mirror::ClassExt> lock(driver_->self_, ext); - if (!ext->ExtendObsoleteArrays( - driver_->self_, klass->GetDeclaredMethodsSlice(art::kRuntimePointerSize).size())) { - // OOM. Clear exception and return error. - driver_->self_->AssertPendingOOMException(); - driver_->self_->ClearException(); - RecordFailure(ERR(OUT_OF_MEMORY), "Unable to allocate/extend obsolete methods map"); - return false; - } + cur_data->SetOldObsoleteMethods(ext->GetObsoleteMethods()); + cur_data->SetOldDexCaches(ext->GetObsoleteDexCaches()); + if (!ext->ExtendObsoleteArrays( + driver_->self_, klass->GetDeclaredMethodsSlice(art::kRuntimePointerSize).size())) { + // OOM. Clear exception and return error. + driver_->self_->AssertPendingOOMException(); + driver_->self_->ClearException(); + RecordFailure(ERR(OUT_OF_MEMORY), "Unable to allocate/extend obsolete methods map"); + return false; } return true; } diff --git a/runtime/openjdkjvmti/ti_redefine.h b/runtime/openjdkjvmti/ti_redefine.h index 809a681902..586259a3f6 100644 --- a/runtime/openjdkjvmti/ti_redefine.h +++ b/runtime/openjdkjvmti/ti_redefine.h @@ -166,7 +166,8 @@ class Redefiner { // Preallocates all needed allocations in klass so that we can pause execution safely. // TODO We should be able to free the arrays if they end up not being used. Investigate doing // this in the future. For now we will just take the memory hit. - bool EnsureClassAllocationsFinished() REQUIRES_SHARED(art::Locks::mutator_lock_); + bool EnsureClassAllocationsFinished(/*out*/RedefinitionDataIter* data) + REQUIRES_SHARED(art::Locks::mutator_lock_); // This will check that no constraints are violated (more than 1 class in dex file, any changes // in number/declaration of methods & fields, changes in access flags, etc.) @@ -198,6 +199,9 @@ class Redefiner { art::ObjPtr<art::mirror::Object> original_dex_file) REQUIRES(art::Locks::mutator_lock_); + void RestoreObsoleteMethodMapsIfUnneeded(const RedefinitionDataIter* cur_data) + REQUIRES(art::Locks::mutator_lock_); + void ReleaseDexFile() REQUIRES_SHARED(art::Locks::mutator_lock_); void UnregisterBreakpoints() REQUIRES_SHARED(art::Locks::mutator_lock_); @@ -241,11 +245,16 @@ class Redefiner { bool CheckAllRedefinitionAreValid() REQUIRES_SHARED(art::Locks::mutator_lock_); bool CheckAllClassesAreVerified(RedefinitionDataHolder& holder) REQUIRES_SHARED(art::Locks::mutator_lock_); - bool EnsureAllClassAllocationsFinished() REQUIRES_SHARED(art::Locks::mutator_lock_); + bool EnsureAllClassAllocationsFinished(RedefinitionDataHolder& holder) + REQUIRES_SHARED(art::Locks::mutator_lock_); bool FinishAllRemainingAllocations(RedefinitionDataHolder& holder) REQUIRES_SHARED(art::Locks::mutator_lock_); void ReleaseAllDexFiles() REQUIRES_SHARED(art::Locks::mutator_lock_); void UnregisterAllBreakpoints() REQUIRES_SHARED(art::Locks::mutator_lock_); + // Restores the old obsolete methods maps if it turns out they weren't needed (ie there were no + // new obsolete methods). + void RestoreObsoleteMethodMapsIfUnneeded(RedefinitionDataHolder& holder) + REQUIRES(art::Locks::mutator_lock_); void RecordFailure(jvmtiError result, const std::string& class_sig, const std::string& error_msg); void RecordFailure(jvmtiError result, const std::string& error_msg) { diff --git a/test/115-native-bridge/nativebridge.cc b/test/115-native-bridge/nativebridge.cc index f913cf69d0..87287f8acf 100644 --- a/test/115-native-bridge/nativebridge.cc +++ b/test/115-native-bridge/nativebridge.cc @@ -446,9 +446,9 @@ extern "C" bool native_bridge_isPathSupported(const char* library_path ATTRIBUTE return false; } -extern "C" bool native_bridge_initNamespace(const char* public_ns_sonames ATTRIBUTE_UNUSED, - const char* anon_ns_library_path ATTRIBUTE_UNUSED) { - printf("Initializing namespaces in native bridge.\n"); +extern "C" bool native_bridge_initAnonymousNamespace(const char* public_ns_sonames ATTRIBUTE_UNUSED, + const char* anon_ns_library_path ATTRIBUTE_UNUSED) { + printf("Initializing anonymous namespace in native bridge.\n"); return false; } @@ -463,6 +463,13 @@ native_bridge_createNamespace(const char* name ATTRIBUTE_UNUSED, return nullptr; } +extern "C" bool native_bridge_linkNamespaces(android::native_bridge_namespace_t* from ATTRIBUTE_UNUSED, + android::native_bridge_namespace_t* to ATTRIBUTE_UNUSED, + const char* shared_libs_sonames ATTRIBUTE_UNUSED) { + printf("Linking namespaces in native bridge.\n"); + return false; +} + extern "C" void* native_bridge_loadLibraryExt(const char* libpath ATTRIBUTE_UNUSED, int flag ATTRIBUTE_UNUSED, android::native_bridge_namespace_t* ns ATTRIBUTE_UNUSED) { @@ -487,7 +494,8 @@ android::NativeBridgeCallbacks NativeBridgeItf { .unloadLibrary = &native_bridge_unloadLibrary, .getError = &native_bridge_getError, .isPathSupported = &native_bridge_isPathSupported, - .initNamespace = &native_bridge_initNamespace, + .initAnonymousNamespace = &native_bridge_initAnonymousNamespace, .createNamespace = &native_bridge_createNamespace, + .linkNamespaces = &native_bridge_linkNamespaces, .loadLibraryExt = &native_bridge_loadLibraryExt }; diff --git a/test/623-checker-loop-regressions/src/Main.java b/test/623-checker-loop-regressions/src/Main.java index eee90ab285..182c07d983 100644 --- a/test/623-checker-loop-regressions/src/Main.java +++ b/test/623-checker-loop-regressions/src/Main.java @@ -270,6 +270,16 @@ public class Main { } } + // If vectorized, invariant stride should be recognized + // as a reduction, not a unit stride in outer loop. + static void reduc(int[] xx, int[] yy) { + for (int i0 = 0; i0 < 2; i0++) { + for (int i1 = 0; i1 < 469; i1++) { + xx[i0] -= (++yy[i1]); + } + } + } + public static void main(String[] args) { expectEquals(10, earlyExitFirst(-1)); for (int i = 0; i <= 10; i++) { @@ -335,6 +345,15 @@ public class Main { expectEquals(2.0f, a[i]); } + int[] xx = new int[2]; + int[] yy = new int[469]; + reduc(xx, yy); + expectEquals(-469, xx[0]); + expectEquals(-938, xx[1]); + for (int i = 0; i < 469; i++) { + expectEquals(2, yy[i]); + } + System.out.println("passed"); } |