summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--compiler/optimizing/induction_var_range.cc5
-rw-r--r--compiler/optimizing/induction_var_range.h10
-rw-r--r--compiler/optimizing/induction_var_range_test.cc8
-rw-r--r--compiler/optimizing/loop_optimization.cc4
-rw-r--r--profman/profile_assistant_test.cc24
-rw-r--r--profman/profman.cc135
-rw-r--r--runtime/jit/profile_compilation_info.cc39
-rw-r--r--runtime/jit/profile_compilation_info.h17
-rw-r--r--runtime/mirror/class_ext.cc5
-rw-r--r--runtime/openjdkjvmti/ti_redefine.cc115
-rw-r--r--runtime/openjdkjvmti/ti_redefine.h13
-rw-r--r--test/115-native-bridge/nativebridge.cc16
-rw-r--r--test/623-checker-loop-regressions/src/Main.java19
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, &param_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");
}