am 64d8f18c: Merge "Support inline dex data"
* commit '64d8f18c94b23cb4ff908304aef4d9f3f5a85f39':
Support inline dex data
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc
index 038957e..49aba4d 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -495,7 +495,7 @@
void CompilerDriver::CompileAll(jobject class_loader,
const std::vector<const DexFile*>& dex_files,
- TimingLogger& timings) {
+ base::TimingLogger& timings) {
DCHECK(!Runtime::Current()->IsStarted());
UniquePtr<ThreadPool> thread_pool(new ThreadPool(thread_count_));
PreCompile(class_loader, dex_files, *thread_pool.get(), timings);
@@ -528,7 +528,7 @@
return klass->IsVerified();
}
-void CompilerDriver::CompileOne(const mirror::AbstractMethod* method, TimingLogger& timings) {
+void CompilerDriver::CompileOne(const mirror::AbstractMethod* method, base::TimingLogger& timings) {
DCHECK(!Runtime::Current()->IsStarted());
Thread* self = Thread::Current();
jobject jclass_loader;
@@ -572,7 +572,7 @@
}
void CompilerDriver::Resolve(jobject class_loader, const std::vector<const DexFile*>& dex_files,
- ThreadPool& thread_pool, TimingLogger& timings) {
+ ThreadPool& thread_pool, base::TimingLogger& timings) {
for (size_t i = 0; i != dex_files.size(); ++i) {
const DexFile* dex_file = dex_files[i];
CHECK(dex_file != NULL);
@@ -581,7 +581,7 @@
}
void CompilerDriver::PreCompile(jobject class_loader, const std::vector<const DexFile*>& dex_files,
- ThreadPool& thread_pool, TimingLogger& timings) {
+ ThreadPool& thread_pool, base::TimingLogger& timings) {
LoadImageClasses(timings);
Resolve(class_loader, dex_files, thread_pool, timings);
@@ -666,12 +666,13 @@
}
// Make a list of descriptors for classes to include in the image
-void CompilerDriver::LoadImageClasses(TimingLogger& timings)
+void CompilerDriver::LoadImageClasses(base::TimingLogger& timings)
LOCKS_EXCLUDED(Locks::mutator_lock_) {
if (image_classes_.get() == NULL) {
return;
}
+ timings.NewSplit("LoadImageClasses");
// Make a first class to load all classes explicitly listed in the file
Thread* self = Thread::Current();
ScopedObjectAccess soa(self);
@@ -726,7 +727,6 @@
class_linker->VisitClasses(RecordImageClassesVisitor, image_classes_.get());
CHECK_NE(image_classes_->size(), 0U);
- timings.AddSplit("LoadImageClasses");
}
static void MaybeAddToImageClasses(mirror::Class* klass, CompilerDriver::DescriptorSet* image_classes)
@@ -758,11 +758,13 @@
MaybeAddToImageClasses(object->GetClass(), compiler_driver->image_classes_.get());
}
-void CompilerDriver::UpdateImageClasses(TimingLogger& timings) {
+void CompilerDriver::UpdateImageClasses(base::TimingLogger& timings) {
if (image_classes_.get() == NULL) {
return;
}
+ timings.NewSplit("UpdateImageClasses");
+
// Update image_classes_ with classes for objects created by <clinit> methods.
Thread* self = Thread::Current();
const char* old_cause = self->StartAssertNoThreadSuspension("ImageWriter");
@@ -772,7 +774,6 @@
heap->FlushAllocStack();
heap->GetLiveBitmap()->Walk(FindClinitImageClassesCallback, this);
self->EndAssertNoThreadSuspension(old_cause);
- timings.AddSplit("UpdateImageClasses");
}
void CompilerDriver::RecordClassStatus(ClassReference ref, CompiledClass* compiled_class) {
@@ -1551,22 +1552,22 @@
}
void CompilerDriver::ResolveDexFile(jobject class_loader, const DexFile& dex_file,
- ThreadPool& thread_pool, TimingLogger& timings) {
+ ThreadPool& thread_pool, base::TimingLogger& timings) {
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
// TODO: we could resolve strings here, although the string table is largely filled with class
// and method names.
+ timings.NewSplit(strdup(("Resolve " + dex_file.GetLocation() + " Types").c_str()));
ParallelCompilationManager context(class_linker, class_loader, this, &dex_file, thread_pool);
context.ForAll(0, dex_file.NumTypeIds(), ResolveType, thread_count_);
- timings.AddSplit("Resolve " + dex_file.GetLocation() + " Types");
+ timings.NewSplit(strdup(("Resolve " + dex_file.GetLocation() + " MethodsAndFields").c_str()));
context.ForAll(0, dex_file.NumClassDefs(), ResolveClassFieldsAndMethods, thread_count_);
- timings.AddSplit("Resolve " + dex_file.GetLocation() + " MethodsAndFields");
}
void CompilerDriver::Verify(jobject class_loader, const std::vector<const DexFile*>& dex_files,
- ThreadPool& thread_pool, TimingLogger& timings) {
+ ThreadPool& thread_pool, base::TimingLogger& timings) {
for (size_t i = 0; i != dex_files.size(); ++i) {
const DexFile* dex_file = dex_files[i];
CHECK(dex_file != NULL);
@@ -1620,11 +1621,11 @@
}
void CompilerDriver::VerifyDexFile(jobject class_loader, const DexFile& dex_file,
- ThreadPool& thread_pool, TimingLogger& timings) {
+ ThreadPool& thread_pool, base::TimingLogger& timings) {
+ timings.NewSplit(strdup(("Verify " + dex_file.GetLocation()).c_str()));
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
ParallelCompilationManager context(class_linker, class_loader, this, &dex_file, thread_pool);
context.ForAll(0, dex_file.NumClassDefs(), VerifyClass, thread_count_);
- timings.AddSplit("Verify " + dex_file.GetLocation());
}
static const char* class_initializer_black_list[] = {
@@ -2116,7 +2117,8 @@
}
void CompilerDriver::InitializeClasses(jobject jni_class_loader, const DexFile& dex_file,
- ThreadPool& thread_pool, TimingLogger& timings) {
+ ThreadPool& thread_pool, base::TimingLogger& timings) {
+ timings.NewSplit(strdup(("InitializeNoClinit " + dex_file.GetLocation()).c_str()));
#ifndef NDEBUG
for (size_t i = 0; i < arraysize(class_initializer_black_list); ++i) {
const char* descriptor = class_initializer_black_list[i];
@@ -2126,12 +2128,11 @@
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
ParallelCompilationManager context(class_linker, jni_class_loader, this, &dex_file, thread_pool);
context.ForAll(0, dex_file.NumClassDefs(), InitializeClass, thread_count_);
- timings.AddSplit("InitializeNoClinit " + dex_file.GetLocation());
}
void CompilerDriver::InitializeClasses(jobject class_loader,
const std::vector<const DexFile*>& dex_files,
- ThreadPool& thread_pool, TimingLogger& timings) {
+ ThreadPool& thread_pool, base::TimingLogger& timings) {
for (size_t i = 0; i != dex_files.size(); ++i) {
const DexFile* dex_file = dex_files[i];
CHECK(dex_file != NULL);
@@ -2140,7 +2141,7 @@
}
void CompilerDriver::Compile(jobject class_loader, const std::vector<const DexFile*>& dex_files,
- ThreadPool& thread_pool, TimingLogger& timings) {
+ ThreadPool& thread_pool, base::TimingLogger& timings) {
for (size_t i = 0; i != dex_files.size(); ++i) {
const DexFile* dex_file = dex_files[i];
CHECK(dex_file != NULL);
@@ -2220,10 +2221,10 @@
}
void CompilerDriver::CompileDexFile(jobject class_loader, const DexFile& dex_file,
- ThreadPool& thread_pool, TimingLogger& timings) {
+ ThreadPool& thread_pool, base::TimingLogger& timings) {
+ timings.NewSplit(strdup(("Compile " + dex_file.GetLocation()).c_str()));
ParallelCompilationManager context(NULL, class_loader, this, &dex_file, thread_pool);
context.ForAll(0, dex_file.NumClassDefs(), CompilerDriver::CompileClass, thread_count_);
- timings.AddSplit("Compile " + dex_file.GetLocation());
}
void CompilerDriver::CompileMethod(const DexFile::CodeItem* code_item, uint32_t access_flags,
@@ -2239,18 +2240,8 @@
CHECK(compiled_method != NULL);
} else if ((access_flags & kAccAbstract) != 0) {
} else {
- // In small mode we only compile image classes.
- bool dont_compile = (Runtime::Current()->IsSmallMode() &&
- ((image_classes_.get() == NULL) || (image_classes_->size() == 0)));
-
- // Don't compile class initializers, ever.
- if (((access_flags & kAccConstructor) != 0) && ((access_flags & kAccStatic) != 0)) {
- dont_compile = true;
- } else if (code_item->insns_size_in_code_units_ < Runtime::Current()->GetSmallModeMethodDexSizeLimit()) {
- // Do compile small methods.
- dont_compile = false;
- }
- if (!dont_compile) {
+ bool compile = verifier::MethodVerifier::IsCandidateForCompilation(code_item, access_flags);
+ if (compile) {
CompilerFn compiler = compiler_;
#ifdef ART_SEA_IR_MODE
bool use_sea = Runtime::Current()->IsSeaIRMode();
diff --git a/compiler/driver/compiler_driver.h b/compiler/driver/compiler_driver.h
index f3f72dd..a7a47ed 100644
--- a/compiler/driver/compiler_driver.h
+++ b/compiler/driver/compiler_driver.h
@@ -78,11 +78,11 @@
~CompilerDriver();
void CompileAll(jobject class_loader, const std::vector<const DexFile*>& dex_files,
- TimingLogger& timings)
+ base::TimingLogger& timings)
LOCKS_EXCLUDED(Locks::mutator_lock_);
// Compile a single Method
- void CompileOne(const mirror::AbstractMethod* method, TimingLogger& timings)
+ void CompileOne(const mirror::AbstractMethod* method, base::TimingLogger& timings)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
InstructionSet GetInstructionSet() const {
@@ -284,42 +284,42 @@
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
void PreCompile(jobject class_loader, const std::vector<const DexFile*>& dex_files,
- ThreadPool& thread_pool, TimingLogger& timings)
+ ThreadPool& thread_pool, base::TimingLogger& timings)
LOCKS_EXCLUDED(Locks::mutator_lock_);
- void LoadImageClasses(TimingLogger& timings);
+ void LoadImageClasses(base::TimingLogger& timings);
// Attempt to resolve all type, methods, fields, and strings
// referenced from code in the dex file following PathClassLoader
// ordering semantics.
void Resolve(jobject class_loader, const std::vector<const DexFile*>& dex_files,
- ThreadPool& thread_pool, TimingLogger& timings)
+ ThreadPool& thread_pool, base::TimingLogger& timings)
LOCKS_EXCLUDED(Locks::mutator_lock_);
void ResolveDexFile(jobject class_loader, const DexFile& dex_file,
- ThreadPool& thread_pool, TimingLogger& timings)
+ ThreadPool& thread_pool, base::TimingLogger& timings)
LOCKS_EXCLUDED(Locks::mutator_lock_);
void Verify(jobject class_loader, const std::vector<const DexFile*>& dex_files,
- ThreadPool& thread_pool, TimingLogger& timings);
+ ThreadPool& thread_pool, base::TimingLogger& timings);
void VerifyDexFile(jobject class_loader, const DexFile& dex_file,
- ThreadPool& thread_pool, TimingLogger& timings)
+ ThreadPool& thread_pool, base::TimingLogger& timings)
LOCKS_EXCLUDED(Locks::mutator_lock_);
void InitializeClasses(jobject class_loader, const std::vector<const DexFile*>& dex_files,
- ThreadPool& thread_pool, TimingLogger& timings)
+ ThreadPool& thread_pool, base::TimingLogger& timings)
LOCKS_EXCLUDED(Locks::mutator_lock_);
void InitializeClasses(jobject class_loader, const DexFile& dex_file,
- ThreadPool& thread_pool, TimingLogger& timings)
+ ThreadPool& thread_pool, base::TimingLogger& timings)
LOCKS_EXCLUDED(Locks::mutator_lock_, compiled_classes_lock_);
- void UpdateImageClasses(TimingLogger& timings);
+ void UpdateImageClasses(base::TimingLogger& timings);
static void FindClinitImageClassesCallback(mirror::Object* object, void* arg)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
void Compile(jobject class_loader, const std::vector<const DexFile*>& dex_files,
- ThreadPool& thread_pool, TimingLogger& timings);
+ ThreadPool& thread_pool, base::TimingLogger& timings);
void CompileDexFile(jobject class_loader, const DexFile& dex_file,
- ThreadPool& thread_pool, TimingLogger& timings)
+ ThreadPool& thread_pool, base::TimingLogger& timings)
LOCKS_EXCLUDED(Locks::mutator_lock_);
void CompileMethod(const DexFile::CodeItem* code_item, uint32_t access_flags,
InvokeType invoke_type, uint32_t class_def_idx, uint32_t method_idx,
diff --git a/compiler/driver/compiler_driver_test.cc b/compiler/driver/compiler_driver_test.cc
index 78cacaf..8ee9cf6 100644
--- a/compiler/driver/compiler_driver_test.cc
+++ b/compiler/driver/compiler_driver_test.cc
@@ -36,7 +36,8 @@
class CompilerDriverTest : public CommonTest {
protected:
void CompileAll(jobject class_loader) LOCKS_EXCLUDED(Locks::mutator_lock_) {
- TimingLogger timings("CompilerDriverTest::CompileAll", false);
+ base::TimingLogger timings("CompilerDriverTest::CompileAll", false, false);
+ timings.StartSplit("CompileAll");
compiler_driver_->CompileAll(class_loader,
Runtime::Current()->GetCompileTimeClassPath(class_loader),
timings);
diff --git a/compiler/sea_ir/frontend.cc b/compiler/sea_ir/frontend.cc
index 8fc1cf8..ebc767c 100644
--- a/compiler/sea_ir/frontend.cc
+++ b/compiler/sea_ir/frontend.cc
@@ -40,7 +40,7 @@
// NOTE: Instead of keeping the convention from the Dalvik frontend.cc
// and silencing the cpplint.py warning, I just corrected the formatting.
VLOG(compiler) << "Compiling " << PrettyMethod(method_idx, dex_file) << "...";
- sea_ir::SeaGraph* sg = sea_ir::SeaGraph::GetCurrentGraph();
+ sea_ir::SeaGraph* sg = sea_ir::SeaGraph::GetCurrentGraph(dex_file);
sg->CompileMethod(code_item, class_def_idx, method_idx, dex_file);
sg->DumpSea("/tmp/temp.dot");
CHECK(0 && "No SEA compiled function exists yet.");
diff --git a/compiler/sea_ir/instruction_nodes.h b/compiler/sea_ir/instruction_nodes.h
index 5c9cfe1..103c16f 100644
--- a/compiler/sea_ir/instruction_nodes.h
+++ b/compiler/sea_ir/instruction_nodes.h
@@ -50,7 +50,7 @@
// Returns the set of register numbers that are used by the instruction.
virtual std::vector<int> GetUses();
// Appends to @result the .dot string representation of the instruction.
- virtual void ToDot(std::string& result) const;
+ virtual void ToDot(std::string& result, const art::DexFile& dex_file) const;
// Mark the current instruction as a downward exposed definition.
void MarkAsDEDef();
// Rename the use of @reg_no to refer to the instruction @definition,
@@ -126,7 +126,7 @@
return value_;
}
- void ToDot(std::string& result) const {
+ void ToDot(std::string& result, const art::DexFile& dex_file) const {
std::ostringstream sstream;
sstream << GetConstValue();
const std::string value_as_string(sstream.str());
@@ -140,11 +140,9 @@
for (std::map<int, InstructionNode* >::const_iterator def_it = definition_edges_.begin();
def_it != definition_edges_.end(); def_it++) {
if (NULL != def_it->second) {
- result += def_it->second->StringId() + " -> " + StringId() +"[color=red,label=\"";
- std::stringstream ss;
- ss << def_it->first;
- result.append(ss.str());
- result += "\"] ; // ssa edge\n";
+ result += def_it->second->StringId() + " -> " + StringId() +"[color=gray,label=\"";
+ result += art::StringPrintf("vR = %d", def_it->first);
+ result += "\"] ; // ssa edge\n";
}
}
}
diff --git a/compiler/sea_ir/sea.cc b/compiler/sea_ir/sea.cc
index 3488afd..17ee2dd 100644
--- a/compiler/sea_ir/sea.cc
+++ b/compiler/sea_ir/sea.cc
@@ -27,7 +27,6 @@
namespace sea_ir {
-SeaGraph SeaGraph::graph_;
int SeaNode::current_max_node_id_ = 0;
void IRVisitor::Traverse(Region* region) {
@@ -51,16 +50,16 @@
}
}
-SeaGraph* SeaGraph::GetCurrentGraph() {
- return &sea_ir::SeaGraph::graph_;
+SeaGraph* SeaGraph::GetCurrentGraph(const art::DexFile& dex_file) {
+ return new SeaGraph(dex_file);
}
void SeaGraph::DumpSea(std::string filename) const {
LOG(INFO) << "Starting to write SEA string to file.";
std::string result;
- result += "digraph seaOfNodes {\n";
+ result += "digraph seaOfNodes {\ncompound=true\n";
for (std::vector<Region*>::const_iterator cit = regions_.begin(); cit != regions_.end(); cit++) {
- (*cit)->ToDot(result);
+ (*cit)->ToDot(result, dex_file_);
}
result += "}\n";
art::File* file = art::OS::OpenFile(filename.c_str(), true, true);
@@ -490,53 +489,44 @@
return NULL;
}
-void Region::ToDot(std::string& result) const {
- result += "\n// Region: \n" + StringId() + " [label=\"region " + StringId() + "(rpo=";
+void Region::ToDot(std::string& result, const art::DexFile& dex_file) const {
+ result += "\n// Region: \nsubgraph " + StringId() + " { label=\"region " + StringId() + "(rpo=";
result += art::StringPrintf("%d", rpo_number_);
if (NULL != GetIDominator()) {
result += " dom=" + GetIDominator()->StringId();
}
- result += ")\"];\n";
+ result += ")\";\n";
+
+ for (std::vector<PhiInstructionNode*>::const_iterator cit = phi_instructions_.begin();
+ cit != phi_instructions_.end(); cit++) {
+ result += (*cit)->StringId() +";\n";
+ }
+
+ for (std::vector<InstructionNode*>::const_iterator cit = instructions_.begin();
+ cit != instructions_.end(); cit++) {
+ result += (*cit)->StringId() +";\n";
+ }
+
+ result += "} // End Region.\n";
// Save phi-nodes.
for (std::vector<PhiInstructionNode*>::const_iterator cit = phi_instructions_.begin();
cit != phi_instructions_.end(); cit++) {
- (*cit)->ToDot(result);
- result += StringId() + " -> " + (*cit)->StringId() + "; // phi-function \n";
+ (*cit)->ToDot(result, dex_file);
}
// Save instruction nodes.
for (std::vector<InstructionNode*>::const_iterator cit = instructions_.begin();
cit != instructions_.end(); cit++) {
- (*cit)->ToDot(result);
- result += StringId() + " -> " + (*cit)->StringId() + "; // region -> instruction \n";
+ (*cit)->ToDot(result, dex_file);
}
for (std::vector<Region*>::const_iterator cit = successors_.begin(); cit != successors_.end();
cit++) {
DCHECK(NULL != *cit) << "Null successor found for SeaNode" << GetLastChild()->StringId() << ".";
- result += GetLastChild()->StringId() + " -> " + (*cit)->StringId() + ";\n\n";
+ result += GetLastChild()->StringId() + " -> " + (*cit)->GetLastChild()->StringId() +
+ "[lhead=" + (*cit)->StringId() + ", " + "ltail=" + StringId() + "];\n\n";
}
- // Save reaching definitions.
- for (std::map<int, std::set<sea_ir::InstructionNode*>* >::const_iterator cit =
- reaching_defs_.begin();
- cit != reaching_defs_.end(); cit++) {
- for (std::set<sea_ir::InstructionNode*>::const_iterator
- reaching_set_it = (*cit).second->begin();
- reaching_set_it != (*cit).second->end();
- reaching_set_it++) {
- result += (*reaching_set_it)->StringId() +
- " -> " + StringId() +
- " [style=dotted]; // Reaching def.\n";
- }
- }
- // Save dominance frontier.
- for (std::set<Region*>::const_iterator cit = df_.begin(); cit != df_.end(); cit++) {
- result += StringId() +
- " -> " + (*cit)->StringId() +
- " [color=gray]; // Dominance frontier.\n";
- }
- result += "// End Region.\n";
}
void Region::ComputeDownExposedDefs() {
@@ -698,9 +688,9 @@
return sea_instructions;
}
-void InstructionNode::ToDot(std::string& result) const {
+void InstructionNode::ToDot(std::string& result, const art::DexFile& dex_file) const {
result += "// Instruction ("+StringId()+"): \n" + StringId() +
- " [label=\"" + instruction_->DumpString(NULL) + "\"";
+ " [label=\"" + instruction_->DumpString(&dex_file) + "\"";
if (de_def_) {
result += "style=bold";
}
@@ -709,9 +699,9 @@
for (std::map<int, InstructionNode* >::const_iterator def_it = definition_edges_.begin();
def_it != definition_edges_.end(); def_it++) {
if (NULL != def_it->second) {
- result += def_it->second->StringId() + " -> " + StringId() +"[color=red,label=\"";
- result += art::StringPrintf("%d", def_it->first);
- result += "\"] ; // ssa edge\n";
+ result += def_it->second->StringId() + " -> " + StringId() +"[color=gray,label=\"";
+ result += art::StringPrintf("vR = %d", def_it->first);
+ result += "\"] ; // ssa edge\n";
}
}
}
@@ -756,7 +746,7 @@
return uses;
}
-void PhiInstructionNode::ToDot(std::string& result) const {
+void PhiInstructionNode::ToDot(std::string& result, const art::DexFile& dex_file) const {
result += "// PhiInstruction: \n" + StringId() +
" [label=\"" + "PHI(";
result += art::StringPrintf("%d", register_no_);
@@ -768,7 +758,7 @@
std::vector<InstructionNode*>* defs_from_pred = *pred_it;
for (std::vector<InstructionNode* >::const_iterator def_it = defs_from_pred->begin();
def_it != defs_from_pred->end(); def_it++) {
- result += (*def_it)->StringId() + " -> " + StringId() +"[color=red,label=\"vR = ";
+ result += (*def_it)->StringId() + " -> " + StringId() +"[color=gray,label=\"vR = ";
result += art::StringPrintf("%d", GetRegisterNumber());
result += "\"] ; // phi-ssa edge\n";
}
diff --git a/compiler/sea_ir/sea.h b/compiler/sea_ir/sea.h
index 25ab1fe..c64703a 100644
--- a/compiler/sea_ir/sea.h
+++ b/compiler/sea_ir/sea.h
@@ -49,7 +49,7 @@
explicit SignatureNode(unsigned int parameter_register):InstructionNode(NULL),
parameter_register_(parameter_register) { }
- void ToDot(std::string& result) const {
+ void ToDot(std::string& result, const art::DexFile& dex_file) const {
result += StringId() +" [label=\"signature:";
result += art::StringPrintf("r%d", GetResultRegister());
result += "\"] // signature node\n";
@@ -77,7 +77,7 @@
explicit PhiInstructionNode(int register_no):
InstructionNode(NULL), register_no_(register_no), definition_edges_() {}
// Appends to @result the .dot string representation of the instruction.
- void ToDot(std::string& result) const;
+ void ToDot(std::string& result, const art::DexFile& dex_file) const;
// Returns the register on which this phi-function is used.
int GetRegisterNumber() const {
return register_no_;
@@ -125,7 +125,9 @@
public:
explicit Region():
SeaNode(), successors_(), predecessors_(), reaching_defs_size_(0),
- rpo_number_(NOT_VISITED), idom_(NULL), idominated_set_(), df_(), phi_set_() {}
+ rpo_number_(NOT_VISITED), idom_(NULL), idominated_set_(), df_(), phi_set_() {
+ string_id_ = "cluster_" + string_id_;
+ }
// Adds @instruction as an instruction node child in the current region.
void AddChild(sea_ir::InstructionNode* instruction);
// Returns the last instruction node child of the current region.
@@ -138,7 +140,7 @@
// Appends to @result a dot language formatted string representing the node and
// (by convention) outgoing edges, so that the composition of theToDot() of all nodes
// builds a complete dot graph (without prolog and epilog though).
- virtual void ToDot(std::string& result) const;
+ virtual void ToDot(std::string& result, const art::DexFile& dex_file) const;
// Computes Downward Exposed Definitions for the current node.
void ComputeDownExposedDefs();
const std::map<int, sea_ir::InstructionNode*>* GetDownExposedDefs() const;
@@ -242,7 +244,7 @@
// and acts as starting point for visitors (ex: during code generation).
class SeaGraph: IVisitable {
public:
- static SeaGraph* GetCurrentGraph();
+ static SeaGraph* GetCurrentGraph(const art::DexFile&);
void CompileMethod(const art::DexFile::CodeItem* code_item,
uint32_t class_def_idx, uint32_t method_idx, const art::DexFile& dex_file);
@@ -264,7 +266,8 @@
uint32_t method_idx_;
private:
- SeaGraph(): class_def_idx_(0), method_idx_(0), regions_(), parameters_() {
+ explicit SeaGraph(const art::DexFile& df):
+ class_def_idx_(0), method_idx_(0), regions_(), parameters_(), dex_file_(df) {
}
// Registers @childReg as a region belonging to the SeaGraph instance.
void AddRegion(Region* childReg);
@@ -319,6 +322,7 @@
static SeaGraph graph_;
std::vector<Region*> regions_;
std::vector<SignatureNode*> parameters_;
+ const art::DexFile& dex_file_;
};
} // namespace sea_ir
#endif // ART_COMPILER_SEA_IR_SEA_H_
diff --git a/compiler/sea_ir/sea_node.h b/compiler/sea_ir/sea_node.h
index 5d28f8a..c13e5d6 100644
--- a/compiler/sea_ir/sea_node.h
+++ b/compiler/sea_ir/sea_node.h
@@ -30,7 +30,7 @@
};
// This abstract class provides the essential services that
-// we want each SEA IR element should have.
+// we want each SEA IR element to have.
// At the moment, these are:
// - an id and corresponding string representation.
// - a .dot graph language representation for .dot output.
@@ -42,6 +42,7 @@
explicit SeaNode():id_(GetNewId()), string_id_() {
string_id_ = art::StringPrintf("%d", id_);
}
+
// Adds CFG predecessors and successors to each block.
void AddSuccessor(Region* successor);
void AddPredecessor(Region* predecesor);
@@ -58,7 +59,7 @@
// Appends to @result a dot language formatted string representing the node and
// (by convention) outgoing edges, so that the composition of theToDot() of all nodes
// builds a complete dot graph, but without prolog ("digraph {") and epilog ("}").
- virtual void ToDot(std::string& result) const = 0;
+ virtual void ToDot(std::string& result, const art::DexFile& dex_file) const = 0;
virtual ~SeaNode() { }
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index f79ddb1..c8c4347 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -230,7 +230,7 @@
bool image,
UniquePtr<CompilerDriver::DescriptorSet>& image_classes,
bool dump_stats,
- TimingLogger& timings)
+ base::TimingLogger& timings)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
// SirtRef and ClassLoader creation needs to come after Runtime::Create
jobject class_loader = NULL;
@@ -263,11 +263,11 @@
Thread::Current()->TransitionFromRunnableToSuspended(kNative);
- timings.AddSplit("dex2oat Setup");
driver->CompileAll(class_loader, dex_files, timings);
Thread::Current()->TransitionFromSuspendedToRunnable();
+ timings.NewSplit("dex2oat OatWriter");
std::string image_file_location;
uint32_t image_file_location_oat_checksum = 0;
uint32_t image_file_location_oat_data_begin = 0;
@@ -287,13 +287,11 @@
image_file_location_oat_data_begin,
image_file_location,
driver.get());
- timings.AddSplit("dex2oat OatWriter");
if (!driver->WriteElf(android_root, is_host, dex_files, oat_writer, oat_file)) {
LOG(ERROR) << "Failed to write ELF file " << oat_file->GetPath();
return NULL;
}
- timings.AddSplit("dex2oat ElfWriter");
return driver.release();
}
@@ -563,7 +561,7 @@
const unsigned int WatchDog::kWatchDogTimeoutSeconds;
static int dex2oat(int argc, char** argv) {
- TimingLogger timings("compiler", false);
+ base::TimingLogger timings("compiler", false, false);
InitLogging(argv);
@@ -928,6 +926,7 @@
}
}
+ timings.StartSplit("dex2oat Setup");
UniquePtr<const CompilerDriver> compiler(dex2oat->CreateOatFile(boot_image_option,
host_prefix.get(),
android_root,
@@ -998,13 +997,13 @@
// Elf32_Phdr.p_vaddr values by the desired base address.
//
if (image) {
+ timings.NewSplit("dex2oat ImageWriter");
Thread::Current()->TransitionFromRunnableToSuspended(kNative);
bool image_creation_success = dex2oat->CreateImageFile(image_filename,
image_base,
oat_unstripped,
oat_location,
*compiler.get());
- timings.AddSplit("dex2oat ImageWriter");
Thread::Current()->TransitionFromSuspendedToRunnable();
if (!image_creation_success) {
return EXIT_FAILURE;
@@ -1014,7 +1013,7 @@
if (is_host) {
if (dump_timings && timings.GetTotalNs() > MsToNs(1000)) {
- LOG(INFO) << Dumpable<TimingLogger>(timings);
+ LOG(INFO) << Dumpable<base::TimingLogger>(timings);
}
return EXIT_SUCCESS;
}
@@ -1022,6 +1021,7 @@
// If we don't want to strip in place, copy from unstripped location to stripped location.
// We need to strip after image creation because FixupElf needs to use .strtab.
if (oat_unstripped != oat_stripped) {
+ timings.NewSplit("dex2oat OatFile copy");
oat_file.reset();
UniquePtr<File> in(OS::OpenFile(oat_unstripped.c_str(), false));
UniquePtr<File> out(OS::OpenFile(oat_stripped.c_str(), true));
@@ -1036,23 +1036,25 @@
CHECK(write_ok);
}
oat_file.reset(out.release());
- timings.AddSplit("dex2oat OatFile copy");
LOG(INFO) << "Oat file copied successfully (stripped): " << oat_stripped;
}
#if ART_USE_PORTABLE_COMPILER // We currently only generate symbols on Portable
+ timings.NewSplit("dex2oat ElfStripper");
// Strip unneeded sections for target
off_t seek_actual = lseek(oat_file->Fd(), 0, SEEK_SET);
CHECK_EQ(0, seek_actual);
ElfStripper::Strip(oat_file.get());
- timings.AddSplit("dex2oat ElfStripper");
+
// We wrote the oat file successfully, and want to keep it.
LOG(INFO) << "Oat file written successfully (stripped): " << oat_location;
#endif // ART_USE_PORTABLE_COMPILER
+ timings.EndSplit();
+
if (dump_timings && timings.GetTotalNs() > MsToNs(1000)) {
- LOG(INFO) << Dumpable<TimingLogger>(timings);
+ LOG(INFO) << Dumpable<base::TimingLogger>(timings);
}
return EXIT_SUCCESS;
}
diff --git a/runtime/base/mutex.h b/runtime/base/mutex.h
index dea52a6..b924798 100644
--- a/runtime/base/mutex.h
+++ b/runtime/base/mutex.h
@@ -53,7 +53,7 @@
class ScopedContentionRecorder;
class Thread;
-const bool kDebugLocking = kIsDebugBuild;
+const bool kDebugLocking = true || kIsDebugBuild;
// Base class for all Mutex implementations
class BaseMutex {
diff --git a/runtime/base/timing_logger.cc b/runtime/base/timing_logger.cc
index bf6fd17..dfb0220 100644
--- a/runtime/base/timing_logger.cc
+++ b/runtime/base/timing_logger.cc
@@ -14,6 +14,11 @@
* limitations under the License.
*/
+
+#define ATRACE_TAG ATRACE_TAG_DALVIK
+#include <stdio.h>
+#include <cutils/trace.h>
+
#include "timing_logger.h"
#include "base/logging.h"
@@ -26,49 +31,6 @@
namespace art {
-void TimingLogger::Reset() {
- times_.clear();
- labels_.clear();
- AddSplit("");
-}
-
-TimingLogger::TimingLogger(const std::string &name, bool precise)
- : name_(name),
- precise_(precise) {
- AddSplit("");
-}
-
-void TimingLogger::AddSplit(const std::string &label) {
- times_.push_back(NanoTime());
- labels_.push_back(label);
-}
-
-uint64_t TimingLogger::GetTotalNs() const {
- return times_.back() - times_.front();
-}
-
-void TimingLogger::Dump(std::ostream &os) const {
- uint64_t largest_time = 0;
- os << name_ << ": begin\n";
- for (size_t i = 1; i < times_.size(); ++i) {
- uint64_t delta_time = times_[i] - times_[i - 1];
- largest_time = std::max(largest_time, delta_time);
- }
- // Compute which type of unit we will use for printing the timings.
- TimeUnit tu = GetAppropriateTimeUnit(largest_time);
- uint64_t divisor = GetNsToTimeUnitDivisor(tu);
- for (size_t i = 1; i < times_.size(); ++i) {
- uint64_t delta_time = times_[i] - times_[i - 1];
- if (!precise_ && divisor >= 1000) {
- // Make the fraction 0.
- delta_time -= delta_time % (divisor / 1000);
- }
- os << name_ << ": " << std::setw(8) << FormatDuration(delta_time, tu) << " "
- << labels_[i] << "\n";
- }
- os << name_ << ": end, " << NsToMs(GetTotalNs()) << " ms\n";
-}
-
CumulativeLogger::CumulativeLogger(const std::string& name)
: name_(name),
lock_name_("CumulativeLoggerLock" + name),
@@ -112,17 +74,8 @@
return total;
}
-void CumulativeLogger::AddLogger(const TimingLogger &logger) {
- MutexLock mu(Thread::Current(), lock_);
- DCHECK_EQ(logger.times_.size(), logger.labels_.size());
- for (size_t i = 1; i < logger.times_.size(); ++i) {
- const uint64_t delta_time = logger.times_[i] - logger.times_[i - 1];
- const std::string &label = logger.labels_[i];
- AddPair(label, delta_time);
- }
-}
-void CumulativeLogger::AddNewLogger(const base::NewTimingLogger &logger) {
+void CumulativeLogger::AddLogger(const base::TimingLogger &logger) {
MutexLock mu(Thread::Current(), lock_);
const std::vector<std::pair<uint64_t, const char*> >& splits = logger.GetSplits();
typedef std::vector<std::pair<uint64_t, const char*> >::const_iterator It;
@@ -183,51 +136,55 @@
namespace base {
-NewTimingLogger::NewTimingLogger(const char* name, bool precise, bool verbose)
+TimingLogger::TimingLogger(const char* name, bool precise, bool verbose)
: name_(name), precise_(precise), verbose_(verbose),
current_split_(NULL), current_split_start_ns_(0) {
}
-void NewTimingLogger::Reset() {
+void TimingLogger::Reset() {
current_split_ = NULL;
current_split_start_ns_ = 0;
splits_.clear();
}
-void NewTimingLogger::StartSplit(const char* new_split_label) {
+void TimingLogger::StartSplit(const char* new_split_label) {
DCHECK(current_split_ == NULL);
if (verbose_) {
LOG(INFO) << "Begin: " << new_split_label;
}
current_split_ = new_split_label;
+ ATRACE_BEGIN(current_split_);
current_split_start_ns_ = NanoTime();
}
// Ends the current split and starts the one given by the label.
-void NewTimingLogger::NewSplit(const char* new_split_label) {
+void TimingLogger::NewSplit(const char* new_split_label) {
DCHECK(current_split_ != NULL);
uint64_t current_time = NanoTime();
uint64_t split_time = current_time - current_split_start_ns_;
+ ATRACE_END();
splits_.push_back(std::pair<uint64_t, const char*>(split_time, current_split_));
if (verbose_) {
LOG(INFO) << "End: " << current_split_ << " " << PrettyDuration(split_time) << "\n"
<< "Begin: " << new_split_label;
}
current_split_ = new_split_label;
+ ATRACE_BEGIN(current_split_);
current_split_start_ns_ = current_time;
}
-void NewTimingLogger::EndSplit() {
+void TimingLogger::EndSplit() {
DCHECK(current_split_ != NULL);
uint64_t current_time = NanoTime();
uint64_t split_time = current_time - current_split_start_ns_;
+ ATRACE_END();
if (verbose_) {
LOG(INFO) << "End: " << current_split_ << " " << PrettyDuration(split_time);
}
splits_.push_back(std::pair<uint64_t, const char*>(split_time, current_split_));
}
-uint64_t NewTimingLogger::GetTotalNs() const {
+uint64_t TimingLogger::GetTotalNs() const {
uint64_t total_ns = 0;
typedef std::vector<std::pair<uint64_t, const char*> >::const_iterator It;
for (It it = splits_.begin(), end = splits_.end(); it != end; ++it) {
@@ -237,7 +194,7 @@
return total_ns;
}
-void NewTimingLogger::Dump(std::ostream &os) const {
+void TimingLogger::Dump(std::ostream &os) const {
uint64_t longest_split = 0;
uint64_t total_ns = 0;
typedef std::vector<std::pair<uint64_t, const char*> >::const_iterator It;
diff --git a/runtime/base/timing_logger.h b/runtime/base/timing_logger.h
index 0f00a04..0998837 100644
--- a/runtime/base/timing_logger.h
+++ b/runtime/base/timing_logger.h
@@ -26,27 +26,8 @@
namespace art {
-class CumulativeLogger;
-
-class TimingLogger {
- public:
- explicit TimingLogger(const std::string& name, bool precise);
- void AddSplit(const std::string& label);
- void Dump(std::ostream& os) const;
- void Reset();
- uint64_t GetTotalNs() const;
-
- protected:
- const std::string name_;
- const bool precise_;
- std::vector<uint64_t> times_;
- std::vector<std::string> labels_;
-
- friend class CumulativeLogger;
-};
-
namespace base {
- class NewTimingLogger;
+ class TimingLogger;
} // namespace base
class CumulativeLogger {
@@ -62,8 +43,7 @@
// Allow the name to be modified, particularly when the cumulative logger is a field within a
// parent class that is unable to determine the "name" of a sub-class.
void SetName(const std::string& name);
- void AddLogger(const TimingLogger& logger) LOCKS_EXCLUDED(lock_);
- void AddNewLogger(const base::NewTimingLogger& logger) LOCKS_EXCLUDED(lock_);
+ void AddLogger(const base::TimingLogger& logger) LOCKS_EXCLUDED(lock_);
private:
void AddPair(const std::string &label, uint64_t delta_time)
@@ -84,16 +64,15 @@
namespace base {
// A replacement to timing logger that know when a split starts for the purposes of logging.
-// TODO: replace uses of TimingLogger with base::NewTimingLogger.
-class NewTimingLogger {
+class TimingLogger {
public:
- explicit NewTimingLogger(const char* name, bool precise, bool verbose);
+ explicit TimingLogger(const char* name, bool precise, bool verbose);
// Clears current splits and labels.
void Reset();
// Starts a split, a split shouldn't be in progress.
- void StartSplit(const char* new_split_label);
+ void StartSplit(const char* new_split_label);
// Ends the current split and starts the one given by the label.
void NewSplit(const char* new_split_label);
@@ -111,7 +90,7 @@
protected:
// The name of the timing logger.
- const std::string name_;
+ const char* name_;
// Do we want to print the exactly recorded split (true) or round down to the time unit being
// used (false).
@@ -130,7 +109,7 @@
std::vector<std::pair<uint64_t, const char*> > splits_;
private:
- DISALLOW_COPY_AND_ASSIGN(NewTimingLogger);
+ DISALLOW_COPY_AND_ASSIGN(TimingLogger);
};
} // namespace base
diff --git a/runtime/common_test.h b/runtime/common_test.h
index 842f959..2c23340 100644
--- a/runtime/common_test.h
+++ b/runtime/common_test.h
@@ -473,7 +473,8 @@
void CompileMethod(mirror::AbstractMethod* method) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
CHECK(method != NULL);
- TimingLogger timings("CommonTest::CompileMethod", false);
+ base::TimingLogger timings("CommonTest::CompileMethod", false, false);
+ timings.StartSplit("CompileOne");
compiler_driver_->CompileOne(method, timings);
MakeExecutable(method);
}
diff --git a/runtime/dex_instruction.h b/runtime/dex_instruction.h
index 6be249c..13b0f1c 100644
--- a/runtime/dex_instruction.h
+++ b/runtime/dex_instruction.h
@@ -281,9 +281,7 @@
// Returns the opcode field of the instruction.
Code Opcode() const {
- const uint16_t* insns = reinterpret_cast<const uint16_t*>(this);
- int opcode = *insns & 0xFF;
- return static_cast<Code>(opcode);
+ return static_cast<Code>(Fetch16(0) & 0xFF);
}
void SetOpcode(Code opcode) {
diff --git a/runtime/gc/collector/garbage_collector.h b/runtime/gc/collector/garbage_collector.h
index 1684664..0f566c9 100644
--- a/runtime/gc/collector/garbage_collector.h
+++ b/runtime/gc/collector/garbage_collector.h
@@ -64,7 +64,7 @@
void RegisterPause(uint64_t nano_length);
- base::NewTimingLogger& GetTimings() {
+ base::TimingLogger& GetTimings() {
return timings_;
}
@@ -101,7 +101,7 @@
const bool verbose_;
uint64_t duration_ns_;
- base::NewTimingLogger timings_;
+ base::TimingLogger timings_;
// Cumulative statistics.
uint64_t total_time_ns_;
diff --git a/runtime/gc/collector/mark_sweep.cc b/runtime/gc/collector/mark_sweep.cc
index 5736e38..89c768a 100644
--- a/runtime/gc/collector/mark_sweep.cc
+++ b/runtime/gc/collector/mark_sweep.cc
@@ -1509,7 +1509,7 @@
// Update the cumulative loggers.
cumulative_timings_.Start();
- cumulative_timings_.AddNewLogger(timings_);
+ cumulative_timings_.AddLogger(timings_);
cumulative_timings_.End();
// Clear all of the spaces' mark bitmaps.
diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc
index 0c1c631..292fd29 100644
--- a/runtime/gc/heap.cc
+++ b/runtime/gc/heap.cc
@@ -148,7 +148,7 @@
CHECK(large_object_space_ != NULL) << "Failed to create large object space";
AddDiscontinuousSpace(large_object_space_);
- alloc_space_ = space::DlMallocSpace::Create("alloc space",
+ alloc_space_ = space::DlMallocSpace::Create(Runtime::Current()->IsZygote() ? "zygote space" : "alloc space",
initial_size,
growth_limit, capacity,
requested_alloc_space_begin);
@@ -972,7 +972,7 @@
// Turns the current alloc space into a Zygote space and obtain the new alloc space composed
// of the remaining available heap memory.
space::DlMallocSpace* zygote_space = alloc_space_;
- alloc_space_ = zygote_space->CreateZygoteSpace();
+ alloc_space_ = zygote_space->CreateZygoteSpace("alloc space");
alloc_space_->SetFootprintLimit(alloc_space_->Capacity());
// Change the GC retention policy of the zygote space to only collect when full.
@@ -1131,7 +1131,7 @@
<< PrettySize(total_memory) << ", " << "paused " << pause_string.str()
<< " total " << PrettyDuration((duration / 1000) * 1000);
if (VLOG_IS_ON(heap)) {
- LOG(INFO) << Dumpable<base::NewTimingLogger>(collector->GetTimings());
+ LOG(INFO) << Dumpable<base::TimingLogger>(collector->GetTimings());
}
}
@@ -1149,7 +1149,7 @@
return gc_type;
}
-void Heap::UpdateAndMarkModUnion(collector::MarkSweep* mark_sweep, base::NewTimingLogger& timings,
+void Heap::UpdateAndMarkModUnion(collector::MarkSweep* mark_sweep, base::TimingLogger& timings,
collector::GcType gc_type) {
if (gc_type == collector::kGcTypeSticky) {
// Don't need to do anything for mod union table in this case since we are only scanning dirty
@@ -1441,7 +1441,7 @@
}
}
-void Heap::ProcessCards(base::NewTimingLogger& timings) {
+void Heap::ProcessCards(base::TimingLogger& timings) {
// Clear cards and keep track of cards cleared in the mod-union table.
typedef std::vector<space::ContinuousSpace*>::iterator It;
for (It it = continuous_spaces_.begin(), end = continuous_spaces_.end(); it != end; ++it) {
@@ -1934,5 +1934,27 @@
} while (!native_bytes_allocated_.compare_and_swap(expected_size, new_size));
}
+int64_t Heap::GetTotalMemory() const {
+ int64_t ret = 0;
+ typedef std::vector<space::ContinuousSpace*>::const_iterator It;
+ for (It it = continuous_spaces_.begin(), end = continuous_spaces_.end(); it != end; ++it) {
+ space::ContinuousSpace* space = *it;
+ if (space->IsImageSpace()) {
+ // Currently don't include the image space.
+ } else if (space->IsDlMallocSpace()) {
+ // Zygote or alloc space
+ ret += space->AsDlMallocSpace()->GetFootprint();
+ }
+ }
+ typedef std::vector<space::DiscontinuousSpace*>::const_iterator It2;
+ for (It2 it = discontinuous_spaces_.begin(), end = discontinuous_spaces_.end(); it != end; ++it) {
+ space::DiscontinuousSpace* space = *it;
+ if (space->IsLargeObjectSpace()) {
+ ret += space->AsLargeObjectSpace()->GetBytesAllocated();
+ }
+ }
+ return ret;
+}
+
} // namespace gc
} // namespace art
diff --git a/runtime/gc/heap.h b/runtime/gc/heap.h
index 20512b8..7615f98 100644
--- a/runtime/gc/heap.h
+++ b/runtime/gc/heap.h
@@ -330,11 +330,7 @@
// Implements java.lang.Runtime.totalMemory, returning the amount of memory consumed by an
// application.
- int64_t GetTotalMemory() const {
- // TODO: we use the footprint limit here which is conservative wrt number of pages really used.
- // We could implement a more accurate count across all spaces.
- return max_allowed_footprint_;
- }
+ int64_t GetTotalMemory() const;
// Implements java.lang.Runtime.freeMemory.
int64_t GetFreeMemory() const {
@@ -382,7 +378,7 @@
EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_);
// Update and mark mod union table based on gc type.
- void UpdateAndMarkModUnion(collector::MarkSweep* mark_sweep, base::NewTimingLogger& timings,
+ void UpdateAndMarkModUnion(collector::MarkSweep* mark_sweep, base::TimingLogger& timings,
collector::GcType gc_type)
EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_);
@@ -475,7 +471,7 @@
void SwapStacks();
// Clear cards and update the mod union table.
- void ProcessCards(base::NewTimingLogger& timings);
+ void ProcessCards(base::TimingLogger& timings);
// All-known continuous spaces, where objects lie within fixed bounds.
std::vector<space::ContinuousSpace*> continuous_spaces_;
diff --git a/runtime/gc/space/dlmalloc_space.cc b/runtime/gc/space/dlmalloc_space.cc
index ee88eda..de4917f 100644
--- a/runtime/gc/space/dlmalloc_space.cc
+++ b/runtime/gc/space/dlmalloc_space.cc
@@ -286,7 +286,7 @@
}
}
-DlMallocSpace* DlMallocSpace::CreateZygoteSpace() {
+DlMallocSpace* DlMallocSpace::CreateZygoteSpace(const char* alloc_space_name) {
end_ = reinterpret_cast<byte*>(RoundUp(reinterpret_cast<uintptr_t>(end_), kPageSize));
DCHECK(IsAligned<accounting::CardTable::kCardSize>(begin_));
DCHECK(IsAligned<accounting::CardTable::kCardSize>(end_));
@@ -316,20 +316,19 @@
VLOG(heap) << "Size " << GetMemMap()->Size();
VLOG(heap) << "GrowthLimit " << PrettySize(growth_limit);
VLOG(heap) << "Capacity " << PrettySize(capacity);
- UniquePtr<MemMap> mem_map(MemMap::MapAnonymous(GetName(), End(), capacity, PROT_READ | PROT_WRITE));
+ UniquePtr<MemMap> mem_map(MemMap::MapAnonymous(alloc_space_name, End(), capacity, PROT_READ | PROT_WRITE));
void* mspace = CreateMallocSpace(end_, starting_size, initial_size);
// Protect memory beyond the initial size.
byte* end = mem_map->Begin() + starting_size;
if (capacity - initial_size > 0) {
- CHECK_MEMORY_CALL(mprotect, (end, capacity - initial_size, PROT_NONE), name_.c_str());
+ CHECK_MEMORY_CALL(mprotect, (end, capacity - initial_size, PROT_NONE), alloc_space_name);
}
DlMallocSpace* alloc_space =
- new DlMallocSpace(name_, mem_map.release(), mspace, end_, end, growth_limit);
+ new DlMallocSpace(alloc_space_name, mem_map.release(), mspace, end_, end, growth_limit);
live_bitmap_->SetHeapLimit(reinterpret_cast<uintptr_t>(End()));
CHECK_EQ(live_bitmap_->HeapLimit(), reinterpret_cast<uintptr_t>(End()));
mark_bitmap_->SetHeapLimit(reinterpret_cast<uintptr_t>(End()));
CHECK_EQ(mark_bitmap_->HeapLimit(), reinterpret_cast<uintptr_t>(End()));
- name_ += "-zygote-transformed";
VLOG(heap) << "zygote space creation done";
return alloc_space;
}
@@ -449,6 +448,11 @@
callback(NULL, NULL, 0, arg); // Indicate end of a space.
}
+size_t DlMallocSpace::GetFootprint() {
+ MutexLock mu(Thread::Current(), lock_);
+ return mspace_footprint(mspace_);
+}
+
size_t DlMallocSpace::GetFootprintLimit() {
MutexLock mu(Thread::Current(), lock_);
return mspace_footprint_limit(mspace_);
diff --git a/runtime/gc/space/dlmalloc_space.h b/runtime/gc/space/dlmalloc_space.h
index 8a4314c..c15d0ba 100644
--- a/runtime/gc/space/dlmalloc_space.h
+++ b/runtime/gc/space/dlmalloc_space.h
@@ -73,6 +73,10 @@
// in use, indicated by num_bytes equaling zero.
void Walk(WalkCallback callback, void* arg);
+ // Returns the number of bytes that the space has currently obtained from the system. This is
+ // greater or equal to the amount of live data in the space.
+ size_t GetFootprint();
+
// Returns the number of bytes that the heap is allowed to obtain from the system via MoreCore.
size_t GetFootprintLimit();
@@ -113,7 +117,7 @@
void SwapBitmaps();
// Turn ourself into a zygote space and return a new alloc space which has our unused memory.
- DlMallocSpace* CreateZygoteSpace();
+ DlMallocSpace* CreateZygoteSpace(const char* alloc_space_name);
uint64_t GetBytesAllocated() const {
return num_bytes_allocated_;
diff --git a/runtime/gc/space/space_test.cc b/runtime/gc/space/space_test.cc
index 08ae894..3003140 100644
--- a/runtime/gc/space/space_test.cc
+++ b/runtime/gc/space/space_test.cc
@@ -123,7 +123,7 @@
// Make sure that the zygote space isn't directly at the start of the space.
space->Alloc(self, 1U * MB);
- space = space->CreateZygoteSpace();
+ space = space->CreateZygoteSpace("alloc space");
// Make space findable to the heap, will also delete space when runtime is cleaned up
AddContinuousSpace(space);
diff --git a/runtime/image_test.cc b/runtime/image_test.cc
index 75eead4..22bed2e 100644
--- a/runtime/image_test.cc
+++ b/runtime/image_test.cc
@@ -44,7 +44,8 @@
{
jobject class_loader = NULL;
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
- TimingLogger timings("ImageTest::WriteRead", false);
+ base::TimingLogger timings("ImageTest::WriteRead", false, false);
+ timings.StartSplit("CompileAll");
compiler_driver_->CompileAll(class_loader, class_linker->GetBootClassPath(), timings);
ScopedObjectAccess soa(Thread::Current());
diff --git a/runtime/interpreter/interpreter.cc b/runtime/interpreter/interpreter.cc
index 30c7a46..d649d2a 100644
--- a/runtime/interpreter/interpreter.cc
+++ b/runtime/interpreter/interpreter.cc
@@ -1013,28 +1013,29 @@
return JValue();
}
self->VerifyStack();
- instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation();
- const uint16_t* const insns = code_item->insns_;
+ instrumentation::Instrumentation* const instrumentation = Runtime::Current()->GetInstrumentation();
// As the 'this' object won't change during the execution of current code, we
// want to cache it in local variables. Nevertheless, in order to let the
// garbage collector access it, we store it into sirt references.
SirtRef<Object> this_object_ref(self, shadow_frame.GetThisObject(code_item->ins_size_));
- const Instruction* inst = Instruction::At(insns + shadow_frame.GetDexPC());
- if (inst->GetDexPc(insns) == 0) { // We are entering the method as opposed to deoptimizing..
+ uint32_t dex_pc = shadow_frame.GetDexPC();
+ if (LIKELY(dex_pc == 0)) { // We are entering the method as opposed to deoptimizing..
if (UNLIKELY(instrumentation->HasMethodEntryListeners())) {
instrumentation->MethodEnterEvent(self, this_object_ref.get(),
shadow_frame.GetMethod(), 0);
}
}
+ const uint16_t* const insns = code_item->insns_;
+ const Instruction* inst = Instruction::At(insns + dex_pc);
while (true) {
+ dex_pc = inst->GetDexPc(insns);
+ shadow_frame.SetDexPC(dex_pc);
if (UNLIKELY(self->TestAllFlags())) {
CheckSuspend(self);
}
- const uint32_t dex_pc = inst->GetDexPc(insns);
- shadow_frame.SetDexPC(dex_pc);
- if (instrumentation->HasDexPcListeners()) {
+ if (UNLIKELY(instrumentation->HasDexPcListeners())) {
instrumentation->DexPcMovedEvent(self, this_object_ref.get(),
shadow_frame.GetMethod(), dex_pc);
}
@@ -1200,8 +1201,8 @@
}
case Instruction::CONST_4: {
PREAMBLE();
- uint32_t dst = inst->VRegA_11n();
- int32_t val = inst->VRegB_11n();
+ uint4_t dst = inst->VRegA_11n();
+ int4_t val = inst->VRegB_11n();
shadow_frame.SetVReg(dst, val);
if (val == 0) {
shadow_frame.SetVRegReference(dst, NULL);
@@ -1211,8 +1212,8 @@
}
case Instruction::CONST_16: {
PREAMBLE();
- uint32_t dst = inst->VRegA_21s();
- int32_t val = inst->VRegB_21s();
+ uint8_t dst = inst->VRegA_21s();
+ int16_t val = inst->VRegB_21s();
shadow_frame.SetVReg(dst, val);
if (val == 0) {
shadow_frame.SetVRegReference(dst, NULL);
@@ -1222,7 +1223,7 @@
}
case Instruction::CONST: {
PREAMBLE();
- uint32_t dst = inst->VRegA_31i();
+ uint8_t dst = inst->VRegA_31i();
int32_t val = inst->VRegB_31i();
shadow_frame.SetVReg(dst, val);
if (val == 0) {
@@ -1233,7 +1234,7 @@
}
case Instruction::CONST_HIGH16: {
PREAMBLE();
- uint32_t dst = inst->VRegA_21h();
+ uint8_t dst = inst->VRegA_21h();
int32_t val = static_cast<int32_t>(inst->VRegB_21h() << 16);
shadow_frame.SetVReg(dst, val);
if (val == 0) {
@@ -2546,7 +2547,7 @@
break;
case Instruction::ADD_INT_2ADDR: {
PREAMBLE();
- uint32_t vregA = inst->VRegA_12x();
+ uint4_t vregA = inst->VRegA_12x();
shadow_frame.SetVReg(vregA,
shadow_frame.GetVReg(vregA) +
shadow_frame.GetVReg(inst->VRegB_12x()));
@@ -2555,7 +2556,7 @@
}
case Instruction::SUB_INT_2ADDR: {
PREAMBLE();
- uint32_t vregA = inst->VRegA_12x();
+ uint4_t vregA = inst->VRegA_12x();
shadow_frame.SetVReg(vregA,
shadow_frame.GetVReg(vregA) -
shadow_frame.GetVReg(inst->VRegB_12x()));
@@ -2564,7 +2565,7 @@
}
case Instruction::MUL_INT_2ADDR: {
PREAMBLE();
- uint32_t vregA = inst->VRegA_12x();
+ uint4_t vregA = inst->VRegA_12x();
shadow_frame.SetVReg(vregA,
shadow_frame.GetVReg(vregA) *
shadow_frame.GetVReg(inst->VRegB_12x()));
@@ -2573,7 +2574,7 @@
}
case Instruction::DIV_INT_2ADDR: {
PREAMBLE();
- uint32_t vregA = inst->VRegA_12x();
+ uint4_t vregA = inst->VRegA_12x();
DoIntDivide(shadow_frame, vregA, shadow_frame.GetVReg(vregA),
shadow_frame.GetVReg(inst->VRegB_12x()));
inst = inst->Next_1xx();
@@ -2581,7 +2582,7 @@
}
case Instruction::REM_INT_2ADDR: {
PREAMBLE();
- uint32_t vregA = inst->VRegA_12x();
+ uint4_t vregA = inst->VRegA_12x();
DoIntRemainder(shadow_frame, vregA, shadow_frame.GetVReg(vregA),
shadow_frame.GetVReg(inst->VRegB_12x()));
POSSIBLY_HANDLE_PENDING_EXCEPTION(Next_1xx);
@@ -2589,7 +2590,7 @@
}
case Instruction::SHL_INT_2ADDR: {
PREAMBLE();
- uint32_t vregA = inst->VRegA_12x();
+ uint4_t vregA = inst->VRegA_12x();
shadow_frame.SetVReg(vregA,
shadow_frame.GetVReg(vregA) <<
(shadow_frame.GetVReg(inst->VRegB_12x()) & 0x1f));
@@ -2598,7 +2599,7 @@
}
case Instruction::SHR_INT_2ADDR: {
PREAMBLE();
- uint32_t vregA = inst->VRegA_12x();
+ uint4_t vregA = inst->VRegA_12x();
shadow_frame.SetVReg(vregA,
shadow_frame.GetVReg(vregA) >>
(shadow_frame.GetVReg(inst->VRegB_12x()) & 0x1f));
@@ -2607,7 +2608,7 @@
}
case Instruction::USHR_INT_2ADDR: {
PREAMBLE();
- uint32_t vregA = inst->VRegA_12x();
+ uint4_t vregA = inst->VRegA_12x();
shadow_frame.SetVReg(vregA,
static_cast<uint32_t>(shadow_frame.GetVReg(vregA)) >>
(shadow_frame.GetVReg(inst->VRegB_12x()) & 0x1f));
@@ -2616,7 +2617,7 @@
}
case Instruction::AND_INT_2ADDR: {
PREAMBLE();
- uint32_t vregA = inst->VRegA_12x();
+ uint4_t vregA = inst->VRegA_12x();
shadow_frame.SetVReg(vregA,
shadow_frame.GetVReg(vregA) &
shadow_frame.GetVReg(inst->VRegB_12x()));
@@ -2625,7 +2626,7 @@
}
case Instruction::OR_INT_2ADDR: {
PREAMBLE();
- uint32_t vregA = inst->VRegA_12x();
+ uint4_t vregA = inst->VRegA_12x();
shadow_frame.SetVReg(vregA,
shadow_frame.GetVReg(vregA) |
shadow_frame.GetVReg(inst->VRegB_12x()));
@@ -2634,7 +2635,7 @@
}
case Instruction::XOR_INT_2ADDR: {
PREAMBLE();
- uint32_t vregA = inst->VRegA_12x();
+ uint4_t vregA = inst->VRegA_12x();
shadow_frame.SetVReg(vregA,
shadow_frame.GetVReg(vregA) ^
shadow_frame.GetVReg(inst->VRegB_12x()));
@@ -2643,7 +2644,7 @@
}
case Instruction::ADD_LONG_2ADDR: {
PREAMBLE();
- uint32_t vregA = inst->VRegA_12x();
+ uint4_t vregA = inst->VRegA_12x();
shadow_frame.SetVRegLong(vregA,
shadow_frame.GetVRegLong(vregA) +
shadow_frame.GetVRegLong(inst->VRegB_12x()));
@@ -2652,7 +2653,7 @@
}
case Instruction::SUB_LONG_2ADDR: {
PREAMBLE();
- uint32_t vregA = inst->VRegA_12x();
+ uint4_t vregA = inst->VRegA_12x();
shadow_frame.SetVRegLong(vregA,
shadow_frame.GetVRegLong(vregA) -
shadow_frame.GetVRegLong(inst->VRegB_12x()));
@@ -2661,7 +2662,7 @@
}
case Instruction::MUL_LONG_2ADDR: {
PREAMBLE();
- uint32_t vregA = inst->VRegA_12x();
+ uint4_t vregA = inst->VRegA_12x();
shadow_frame.SetVRegLong(vregA,
shadow_frame.GetVRegLong(vregA) *
shadow_frame.GetVRegLong(inst->VRegB_12x()));
@@ -2670,7 +2671,7 @@
}
case Instruction::DIV_LONG_2ADDR: {
PREAMBLE();
- uint32_t vregA = inst->VRegA_12x();
+ uint4_t vregA = inst->VRegA_12x();
DoLongDivide(shadow_frame, vregA, shadow_frame.GetVRegLong(vregA),
shadow_frame.GetVRegLong(inst->VRegB_12x()));
POSSIBLY_HANDLE_PENDING_EXCEPTION(Next_1xx);
@@ -2678,7 +2679,7 @@
}
case Instruction::REM_LONG_2ADDR: {
PREAMBLE();
- uint32_t vregA = inst->VRegA_12x();
+ uint4_t vregA = inst->VRegA_12x();
DoLongRemainder(shadow_frame, vregA, shadow_frame.GetVRegLong(vregA),
shadow_frame.GetVRegLong(inst->VRegB_12x()));
POSSIBLY_HANDLE_PENDING_EXCEPTION(Next_1xx);
@@ -2686,7 +2687,7 @@
}
case Instruction::AND_LONG_2ADDR: {
PREAMBLE();
- uint32_t vregA = inst->VRegA_12x();
+ uint4_t vregA = inst->VRegA_12x();
shadow_frame.SetVRegLong(vregA,
shadow_frame.GetVRegLong(vregA) &
shadow_frame.GetVRegLong(inst->VRegB_12x()));
@@ -2695,7 +2696,7 @@
}
case Instruction::OR_LONG_2ADDR: {
PREAMBLE();
- uint32_t vregA = inst->VRegA_12x();
+ uint4_t vregA = inst->VRegA_12x();
shadow_frame.SetVRegLong(vregA,
shadow_frame.GetVRegLong(vregA) |
shadow_frame.GetVRegLong(inst->VRegB_12x()));
@@ -2704,7 +2705,7 @@
}
case Instruction::XOR_LONG_2ADDR: {
PREAMBLE();
- uint32_t vregA = inst->VRegA_12x();
+ uint4_t vregA = inst->VRegA_12x();
shadow_frame.SetVRegLong(vregA,
shadow_frame.GetVRegLong(vregA) ^
shadow_frame.GetVRegLong(inst->VRegB_12x()));
@@ -2713,7 +2714,7 @@
}
case Instruction::SHL_LONG_2ADDR: {
PREAMBLE();
- uint32_t vregA = inst->VRegA_12x();
+ uint4_t vregA = inst->VRegA_12x();
shadow_frame.SetVRegLong(vregA,
shadow_frame.GetVRegLong(vregA) <<
(shadow_frame.GetVReg(inst->VRegB_12x()) & 0x3f));
@@ -2722,7 +2723,7 @@
}
case Instruction::SHR_LONG_2ADDR: {
PREAMBLE();
- uint32_t vregA = inst->VRegA_12x();
+ uint4_t vregA = inst->VRegA_12x();
shadow_frame.SetVRegLong(vregA,
shadow_frame.GetVRegLong(vregA) >>
(shadow_frame.GetVReg(inst->VRegB_12x()) & 0x3f));
@@ -2731,7 +2732,7 @@
}
case Instruction::USHR_LONG_2ADDR: {
PREAMBLE();
- uint32_t vregA = inst->VRegA_12x();
+ uint4_t vregA = inst->VRegA_12x();
shadow_frame.SetVRegLong(vregA,
static_cast<uint64_t>(shadow_frame.GetVRegLong(vregA)) >>
(shadow_frame.GetVReg(inst->VRegB_12x()) & 0x3f));
@@ -2740,7 +2741,7 @@
}
case Instruction::ADD_FLOAT_2ADDR: {
PREAMBLE();
- uint32_t vregA = inst->VRegA_12x();
+ uint4_t vregA = inst->VRegA_12x();
shadow_frame.SetVRegFloat(vregA,
shadow_frame.GetVRegFloat(vregA) +
shadow_frame.GetVRegFloat(inst->VRegB_12x()));
@@ -2749,7 +2750,7 @@
}
case Instruction::SUB_FLOAT_2ADDR: {
PREAMBLE();
- uint32_t vregA = inst->VRegA_12x();
+ uint4_t vregA = inst->VRegA_12x();
shadow_frame.SetVRegFloat(vregA,
shadow_frame.GetVRegFloat(vregA) -
shadow_frame.GetVRegFloat(inst->VRegB_12x()));
@@ -2758,7 +2759,7 @@
}
case Instruction::MUL_FLOAT_2ADDR: {
PREAMBLE();
- uint32_t vregA = inst->VRegA_12x();
+ uint4_t vregA = inst->VRegA_12x();
shadow_frame.SetVRegFloat(vregA,
shadow_frame.GetVRegFloat(vregA) *
shadow_frame.GetVRegFloat(inst->VRegB_12x()));
@@ -2767,7 +2768,7 @@
}
case Instruction::DIV_FLOAT_2ADDR: {
PREAMBLE();
- uint32_t vregA = inst->VRegA_12x();
+ uint4_t vregA = inst->VRegA_12x();
shadow_frame.SetVRegFloat(vregA,
shadow_frame.GetVRegFloat(vregA) /
shadow_frame.GetVRegFloat(inst->VRegB_12x()));
@@ -2776,7 +2777,7 @@
}
case Instruction::REM_FLOAT_2ADDR: {
PREAMBLE();
- uint32_t vregA = inst->VRegA_12x();
+ uint4_t vregA = inst->VRegA_12x();
shadow_frame.SetVRegFloat(vregA,
fmodf(shadow_frame.GetVRegFloat(vregA),
shadow_frame.GetVRegFloat(inst->VRegB_12x())));
@@ -2785,7 +2786,7 @@
}
case Instruction::ADD_DOUBLE_2ADDR: {
PREAMBLE();
- uint32_t vregA = inst->VRegA_12x();
+ uint4_t vregA = inst->VRegA_12x();
shadow_frame.SetVRegDouble(vregA,
shadow_frame.GetVRegDouble(vregA) +
shadow_frame.GetVRegDouble(inst->VRegB_12x()));
@@ -2794,7 +2795,7 @@
}
case Instruction::SUB_DOUBLE_2ADDR: {
PREAMBLE();
- uint32_t vregA = inst->VRegA_12x();
+ uint4_t vregA = inst->VRegA_12x();
shadow_frame.SetVRegDouble(vregA,
shadow_frame.GetVRegDouble(vregA) -
shadow_frame.GetVRegDouble(inst->VRegB_12x()));
@@ -2803,7 +2804,7 @@
}
case Instruction::MUL_DOUBLE_2ADDR: {
PREAMBLE();
- uint32_t vregA = inst->VRegA_12x();
+ uint4_t vregA = inst->VRegA_12x();
shadow_frame.SetVRegDouble(vregA,
shadow_frame.GetVRegDouble(vregA) *
shadow_frame.GetVRegDouble(inst->VRegB_12x()));
@@ -2812,7 +2813,7 @@
}
case Instruction::DIV_DOUBLE_2ADDR: {
PREAMBLE();
- uint32_t vregA = inst->VRegA_12x();
+ uint4_t vregA = inst->VRegA_12x();
shadow_frame.SetVRegDouble(vregA,
shadow_frame.GetVRegDouble(vregA) /
shadow_frame.GetVRegDouble(inst->VRegB_12x()));
@@ -2821,7 +2822,7 @@
}
case Instruction::REM_DOUBLE_2ADDR: {
PREAMBLE();
- uint32_t vregA = inst->VRegA_12x();
+ uint4_t vregA = inst->VRegA_12x();
shadow_frame.SetVRegDouble(vregA,
fmod(shadow_frame.GetVRegDouble(vregA),
shadow_frame.GetVRegDouble(inst->VRegB_12x())));
diff --git a/runtime/native/dalvik_system_VMDebug.cc b/runtime/native/dalvik_system_VMDebug.cc
index 60624c2..e3ec3bc 100644
--- a/runtime/native/dalvik_system_VMDebug.cc
+++ b/runtime/native/dalvik_system_VMDebug.cc
@@ -20,6 +20,9 @@
#include "class_linker.h"
#include "common_throws.h"
#include "debugger.h"
+#include "gc/space/dlmalloc_space.h"
+#include "gc/space/large_object_space.h"
+#include "gc/space/space-inl.h"
#include "hprof/hprof.h"
#include "jni_internal.h"
#include "mirror/class.h"
@@ -234,6 +237,69 @@
return count;
}
+// We export the VM internal per-heap-space size/alloc/free metrics
+// for the zygote space, alloc space (application heap), and the large
+// object space for dumpsys meminfo. The other memory region data such
+// as PSS, private/shared dirty/shared data are available via
+// /proc/<pid>/smaps.
+static void VMDebug_getHeapSpaceStats(JNIEnv* env, jclass, jlongArray data) {
+ jlong* arr = reinterpret_cast<jlong*>(env->GetPrimitiveArrayCritical(data, 0));
+ if (arr == NULL || env->GetArrayLength(data) < 9) {
+ return;
+ }
+
+ size_t allocSize = 0;
+ size_t allocUsed = 0;
+ size_t zygoteSize = 0;
+ size_t zygoteUsed = 0;
+ size_t largeObjectsSize = 0;
+ size_t largeObjectsUsed = 0;
+
+ gc::Heap* heap = Runtime::Current()->GetHeap();
+ const std::vector<gc::space::ContinuousSpace*>& continuous_spaces = heap->GetContinuousSpaces();
+ const std::vector<gc::space::DiscontinuousSpace*>& discontinuous_spaces = heap->GetDiscontinuousSpaces();
+ typedef std::vector<gc::space::ContinuousSpace*>::const_iterator It;
+ for (It it = continuous_spaces.begin(), end = continuous_spaces.end(); it != end; ++it) {
+ gc::space::ContinuousSpace* space = *it;
+ if (space->IsImageSpace()) {
+ // Currently don't include the image space.
+ } else if (space->IsZygoteSpace()) {
+ gc::space::DlMallocSpace* dlmalloc_space = space->AsDlMallocSpace();
+ zygoteSize += dlmalloc_space->GetFootprint();
+ zygoteUsed += dlmalloc_space->GetBytesAllocated();
+ } else {
+ // This is the alloc space.
+ gc::space::DlMallocSpace* dlmalloc_space = space->AsDlMallocSpace();
+ allocSize += dlmalloc_space->GetFootprint();
+ allocUsed += dlmalloc_space->GetBytesAllocated();
+ }
+ }
+ typedef std::vector<gc::space::DiscontinuousSpace*>::const_iterator It2;
+ for (It2 it = discontinuous_spaces.begin(), end = discontinuous_spaces.end(); it != end; ++it) {
+ gc::space::DiscontinuousSpace* space = *it;
+ if (space->IsLargeObjectSpace()) {
+ largeObjectsSize += space->AsLargeObjectSpace()->GetBytesAllocated();
+ largeObjectsUsed += largeObjectsSize;
+ }
+ }
+
+ size_t allocFree = allocSize - allocUsed;
+ size_t zygoteFree = zygoteSize - zygoteUsed;
+ size_t largeObjectsFree = largeObjectsSize - largeObjectsUsed;
+
+ int j = 0;
+ arr[j++] = allocSize;
+ arr[j++] = allocUsed;
+ arr[j++] = allocFree;
+ arr[j++] = zygoteSize;
+ arr[j++] = zygoteUsed;
+ arr[j++] = zygoteFree;
+ arr[j++] = largeObjectsSize;
+ arr[j++] = largeObjectsUsed;
+ arr[j++] = largeObjectsFree;
+ env->ReleasePrimitiveArrayCritical(data, arr, 0);
+}
+
static JNINativeMethod gMethods[] = {
NATIVE_METHOD(VMDebug, countInstancesOfClass, "(Ljava/lang/Class;Z)J"),
NATIVE_METHOD(VMDebug, crash, "()V"),
@@ -241,6 +307,7 @@
NATIVE_METHOD(VMDebug, dumpHprofDataDdms, "()V"),
NATIVE_METHOD(VMDebug, dumpReferenceTables, "()V"),
NATIVE_METHOD(VMDebug, getAllocCount, "(I)I"),
+ NATIVE_METHOD(VMDebug, getHeapSpaceStats, "([J)V"),
NATIVE_METHOD(VMDebug, getInstructionCount, "([I)V"),
NATIVE_METHOD(VMDebug, getLoadedClassCount, "()I"),
NATIVE_METHOD(VMDebug, getVmFeatureList, "()[Ljava/lang/String;"),
diff --git a/runtime/oat_test.cc b/runtime/oat_test.cc
index ebb228e..9fb2638 100644
--- a/runtime/oat_test.cc
+++ b/runtime/oat_test.cc
@@ -77,7 +77,7 @@
compiler_driver_.reset(new CompilerDriver(compiler_backend, kThumb2, false, NULL, 2, true));
jobject class_loader = NULL;
if (compile) {
- TimingLogger timings("OatTest::WriteRead", false);
+ base::TimingLogger timings("OatTest::WriteRead", false, false);
compiler_driver_->CompileAll(class_loader, class_linker->GetBootClassPath(), timings);
}
@@ -96,7 +96,7 @@
ASSERT_TRUE(success);
if (compile) { // OatWriter strips the code, regenerate to compare
- TimingLogger timings("CommonTest::WriteRead", false);
+ base::TimingLogger timings("CommonTest::WriteRead", false, false);
compiler_driver_->CompileAll(class_loader, class_linker->GetBootClassPath(), timings);
}
UniquePtr<OatFile> oat_file(OatFile::Open(tmp.GetFilename(), tmp.GetFilename(), NULL, false));
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index 0c13ad2..35667e7 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -134,10 +134,10 @@
delete java_vm_;
Thread::Shutdown();
QuasiAtomic::Shutdown();
+ verifier::MethodVerifier::Shutdown();
// TODO: acquire a static mutex on Runtime to avoid racing.
CHECK(instance_ == NULL || instance_ == this);
instance_ = NULL;
- verifier::MethodVerifier::Shutdown();
}
struct AbortState {
diff --git a/runtime/verifier/instruction_flags.cc b/runtime/verifier/instruction_flags.cc
index 358791d..f76c226 100644
--- a/runtime/verifier/instruction_flags.cc
+++ b/runtime/verifier/instruction_flags.cc
@@ -22,16 +22,17 @@
namespace verifier {
std::string InstructionFlags::ToString() const {
- char encoding[6];
+ char encoding[7];
if (!IsOpcode()) {
- strncpy(encoding, "XXXXX", sizeof(encoding));
+ strncpy(encoding, "XXXXXX", sizeof(encoding));
} else {
- strncpy(encoding, "-----", sizeof(encoding));
- if (IsInTry()) encoding[kInTry] = 'T';
- if (IsBranchTarget()) encoding[kBranchTarget] = 'B';
+ strncpy(encoding, "------", sizeof(encoding));
+ if (IsVisited()) encoding[kVisited] = 'V';
+ if (IsChanged()) encoding[kChanged] = 'C';
+ if (IsInTry()) encoding[kInTry] = 'T';
+ if (IsBranchTarget()) encoding[kBranchTarget] = 'B';
if (IsCompileTimeInfoPoint()) encoding[kCompileTimeInfoPoint] = 'G';
- if (IsVisited()) encoding[kVisited] = 'V';
- if (IsChanged()) encoding[kChanged] = 'C';
+ if (IsReturn()) encoding[kReturn] = 'R';
}
return encoding;
}
diff --git a/runtime/verifier/instruction_flags.h b/runtime/verifier/instruction_flags.h
index 9b2e595..e50ba13 100644
--- a/runtime/verifier/instruction_flags.h
+++ b/runtime/verifier/instruction_flags.h
@@ -93,6 +93,21 @@
return IsVisited() || IsChanged();
}
+ void SetReturn() {
+ flags_ |= 1 << kReturn;
+ }
+ void ClearReturn() {
+ flags_ &= ~(1 << kReturn);
+ }
+ bool IsReturn() const {
+ return (flags_ & (1 << kReturn)) != 0;
+ }
+
+ void SetCompileTimeInfoPointAndReturn() {
+ SetCompileTimeInfoPoint();
+ SetReturn();
+ }
+
std::string ToString() const;
private:
@@ -108,6 +123,8 @@
kBranchTarget = 3,
// Location of interest to the compiler for GC maps and verifier based method sharpening.
kCompileTimeInfoPoint = 4,
+ // A return instruction.
+ kReturn = 5,
};
// Size of instruction in code units.
diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc
index ac499df..1b8b47e 100644
--- a/runtime/verifier/method_verifier.cc
+++ b/runtime/verifier/method_verifier.cc
@@ -102,7 +102,11 @@
error += dex_file.GetLocation();
return kHardFailure;
}
- return VerifyClass(&dex_file, kh.GetDexCache(), klass->GetClassLoader(), class_def_idx, error, allow_soft_failures);
+ return VerifyClass(&dex_file,
+ kh.GetDexCache(),
+ klass->GetClassLoader(),
+ class_def_idx, error,
+ allow_soft_failures);
}
MethodVerifier::FailureKind MethodVerifier::VerifyClass(const DexFile* dex_file,
@@ -142,8 +146,15 @@
// We couldn't resolve the method, but continue regardless.
Thread::Current()->ClearException();
}
- MethodVerifier::FailureKind result = VerifyMethod(method_idx, dex_file, dex_cache, class_loader,
- class_def_idx, it.GetMethodCodeItem(), method, it.GetMemberAccessFlags(), allow_soft_failures);
+ MethodVerifier::FailureKind result = VerifyMethod(method_idx,
+ dex_file,
+ dex_cache,
+ class_loader,
+ class_def_idx,
+ it.GetMethodCodeItem(),
+ method,
+ it.GetMemberAccessFlags(),
+ allow_soft_failures);
if (result != kNoFailure) {
if (result == kHardFailure) {
hard_fail = true;
@@ -177,8 +188,15 @@
// We couldn't resolve the method, but continue regardless.
Thread::Current()->ClearException();
}
- MethodVerifier::FailureKind result = VerifyMethod(method_idx, dex_file, dex_cache, class_loader,
- class_def_idx, it.GetMethodCodeItem(), method, it.GetMemberAccessFlags(), allow_soft_failures);
+ MethodVerifier::FailureKind result = VerifyMethod(method_idx,
+ dex_file,
+ dex_cache,
+ class_loader,
+ class_def_idx,
+ it.GetMethodCodeItem(),
+ method,
+ it.GetMemberAccessFlags(),
+ allow_soft_failures);
if (result != kNoFailure) {
if (result == kHardFailure) {
hard_fail = true;
@@ -282,7 +300,9 @@
new_instance_count_(0),
monitor_enter_count_(0),
can_load_classes_(can_load_classes),
- allow_soft_failures_(allow_soft_failures) {
+ allow_soft_failures_(allow_soft_failures),
+ has_check_casts_(false),
+ has_virtual_or_interface_invokes_(false) {
}
void MethodVerifier::FindLocksAtDexPc(mirror::AbstractMethod* m, uint32_t dex_pc,
@@ -470,6 +490,13 @@
new_instance_count++;
} else if (opcode == Instruction::MONITOR_ENTER) {
monitor_enter_count++;
+ } else if (opcode == Instruction::CHECK_CAST) {
+ has_check_casts_ = true;
+ } else if ((inst->Opcode() == Instruction::INVOKE_VIRTUAL) ||
+ (inst->Opcode() == Instruction::INVOKE_VIRTUAL_RANGE) ||
+ (inst->Opcode() == Instruction::INVOKE_INTERFACE) ||
+ (inst->Opcode() == Instruction::INVOKE_INTERFACE_RANGE)) {
+ has_virtual_or_interface_invokes_ = true;
}
size_t inst_size = inst->SizeInCodeUnits();
insn_flags_[dex_pc].SetLengthInCodeUnits(inst_size);
@@ -506,7 +533,8 @@
return false;
}
if (!insn_flags_[start].IsOpcode()) {
- Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "'try' block starts inside an instruction (" << start << ")";
+ Fail(VERIFY_ERROR_BAD_CLASS_HARD)
+ << "'try' block starts inside an instruction (" << start << ")";
return false;
}
for (uint32_t dex_pc = start; dex_pc < end;
@@ -523,7 +551,8 @@
for (; iterator.HasNext(); iterator.Next()) {
uint32_t dex_pc= iterator.GetHandlerAddress();
if (!insn_flags_[dex_pc].IsOpcode()) {
- Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "exception handler starts at bad address (" << dex_pc << ")";
+ Fail(VERIFY_ERROR_BAD_CLASS_HARD)
+ << "exception handler starts at bad address (" << dex_pc << ")";
return false;
}
const Instruction* inst = Instruction::At(code_item_->insns_ + dex_pc);
@@ -570,8 +599,10 @@
/* Flag instructions that are garbage collection points */
// All invoke points are marked as "Throw" points already.
// We are relying on this to also count all the invokes as interesting.
- if (inst->IsBranch() || inst->IsSwitch() || inst->IsThrow() || inst->IsReturn()) {
+ if (inst->IsBranch() || inst->IsSwitch() || inst->IsThrow()) {
insn_flags_[dex_pc].SetCompileTimeInfoPoint();
+ } else if (inst->IsReturn()) {
+ insn_flags_[dex_pc].SetCompileTimeInfoPointAndReturn();
}
dex_pc += inst->SizeInCodeUnits();
inst = inst->Next();
@@ -737,11 +768,13 @@
}
if (bracket_count == 0) {
/* The given class must be an array type. */
- Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "can't new-array class '" << descriptor << "' (not an array)";
+ Fail(VERIFY_ERROR_BAD_CLASS_HARD)
+ << "can't new-array class '" << descriptor << "' (not an array)";
return false;
} else if (bracket_count > 255) {
/* It is illegal to create an array of more than 255 dimensions. */
- Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "can't new-array class '" << descriptor << "' (exceeds limit)";
+ Fail(VERIFY_ERROR_BAD_CLASS_HARD)
+ << "can't new-array class '" << descriptor << "' (exceeds limit)";
return false;
}
return true;
@@ -759,7 +792,8 @@
if ((int32_t) cur_offset + array_data_offset < 0 ||
cur_offset + array_data_offset + 2 >= insn_count) {
Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "invalid array data start: at " << cur_offset
- << ", data offset " << array_data_offset << ", count " << insn_count;
+ << ", data offset " << array_data_offset
+ << ", count " << insn_count;
return false;
}
/* offset to array data table is a relative branch-style offset */
@@ -791,18 +825,22 @@
return false;
}
if (!selfOkay && offset == 0) {
- Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "branch offset of zero not allowed at" << reinterpret_cast<void*>(cur_offset);
+ Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "branch offset of zero not allowed at"
+ << reinterpret_cast<void*>(cur_offset);
return false;
}
// Check for 32-bit overflow. This isn't strictly necessary if we can depend on the runtime
// to have identical "wrap-around" behavior, but it's unwise to depend on that.
if (((int64_t) cur_offset + (int64_t) offset) != (int64_t) (cur_offset + offset)) {
- Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "branch target overflow " << reinterpret_cast<void*>(cur_offset) << " +" << offset;
+ Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "branch target overflow "
+ << reinterpret_cast<void*>(cur_offset) << " +" << offset;
return false;
}
const uint32_t insn_count = code_item_->insns_size_in_code_units_;
int32_t abs_offset = cur_offset + offset;
- if (abs_offset < 0 || (uint32_t) abs_offset >= insn_count || !insn_flags_[abs_offset].IsOpcode()) {
+ if (abs_offset < 0 ||
+ (uint32_t) abs_offset >= insn_count ||
+ !insn_flags_[abs_offset].IsOpcode()) {
Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "invalid branch target " << offset << " (-> "
<< reinterpret_cast<void*>(abs_offset) << ") at "
<< reinterpret_cast<void*>(cur_offset);
@@ -858,7 +896,8 @@
int32_t switch_offset = insns[1] | ((int32_t) insns[2]) << 16;
if ((int32_t) cur_offset + switch_offset < 0 || cur_offset + switch_offset + 2 >= insn_count) {
Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "invalid switch start: at " << cur_offset
- << ", switch offset " << switch_offset << ", count " << insn_count;
+ << ", switch offset " << switch_offset
+ << ", count " << insn_count;
return false;
}
/* offset to switch table is a relative branch-style offset */
@@ -885,15 +924,16 @@
}
uint32_t table_size = targets_offset + switch_count * 2;
if (switch_insns[0] != expected_signature) {
- Fail(VERIFY_ERROR_BAD_CLASS_HARD) << StringPrintf("wrong signature for switch table (%x, wanted %x)",
- switch_insns[0], expected_signature);
+ Fail(VERIFY_ERROR_BAD_CLASS_HARD)
+ << StringPrintf("wrong signature for switch table (%x, wanted %x)",
+ switch_insns[0], expected_signature);
return false;
}
/* make sure the end of the switch is in range */
if (cur_offset + switch_offset + table_size > (uint32_t) insn_count) {
- Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "invalid switch end: at " << cur_offset << ", switch offset "
- << switch_offset << ", end "
- << (cur_offset + switch_offset + table_size)
+ Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "invalid switch end: at " << cur_offset
+ << ", switch offset " << switch_offset
+ << ", end " << (cur_offset + switch_offset + table_size)
<< ", count " << insn_count;
return false;
}
@@ -916,10 +956,13 @@
int32_t offset = (int32_t) switch_insns[targets_offset + targ * 2] |
(int32_t) (switch_insns[targets_offset + targ * 2 + 1] << 16);
int32_t abs_offset = cur_offset + offset;
- if (abs_offset < 0 || abs_offset >= (int32_t) insn_count || !insn_flags_[abs_offset].IsOpcode()) {
- Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "invalid switch target " << offset << " (-> "
- << reinterpret_cast<void*>(abs_offset) << ") at "
- << reinterpret_cast<void*>(cur_offset) << "[" << targ << "]";
+ if (abs_offset < 0 ||
+ abs_offset >= (int32_t) insn_count ||
+ !insn_flags_[abs_offset].IsOpcode()) {
+ Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "invalid switch target " << offset
+ << " (-> " << reinterpret_cast<void*>(abs_offset) << ") at "
+ << reinterpret_cast<void*>(cur_offset)
+ << "[" << targ << "]";
return false;
}
insn_flags_[abs_offset].SetBranchTarget();
@@ -949,14 +992,15 @@
// vA/vC are unsigned 8-bit/16-bit quantities for /range instructions, so there's no risk of
// integer overflow when adding them here.
if (vA + vC > registers_size) {
- Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "invalid reg index " << vA << "+" << vC << " in range invoke (> "
- << registers_size << ")";
+ Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "invalid reg index " << vA << "+" << vC
+ << " in range invoke (> " << registers_size << ")";
return false;
}
return true;
}
-static const std::vector<uint8_t>* CreateLengthPrefixedDexGcMap(const std::vector<uint8_t>& gc_map) {
+static const std::vector<uint8_t>* CreateLengthPrefixedDexGcMap(
+ const std::vector<uint8_t>& gc_map) {
std::vector<uint8_t>* length_prefixed_gc_map = new std::vector<uint8_t>;
length_prefixed_gc_map->reserve(gc_map.size() + 4);
length_prefixed_gc_map->push_back((gc_map.size() & 0xff000000) >> 24);
@@ -984,7 +1028,11 @@
<< " insns_size=" << insns_size << ")";
}
/* Create and initialize table holding register status */
- reg_table_.Init(kTrackCompilerInterestPoints, insn_flags_.get(), insns_size, registers_size, this);
+ reg_table_.Init(kTrackCompilerInterestPoints,
+ insn_flags_.get(),
+ insns_size,
+ registers_size,
+ this);
work_line_.reset(new RegisterLine(registers_size, this));
@@ -1004,27 +1052,37 @@
return false;
}
- /* Generate a register map and add it to the method. */
- UniquePtr<const std::vector<uint8_t> > map(GenerateGcMap());
- if (map.get() == NULL) {
- DCHECK_NE(failures_.size(), 0U);
- return false; // Not a real failure, but a failure to encode
- }
- if (kIsDebugBuild) {
- VerifyGcMap(*map);
- }
- MethodReference ref(dex_file_, dex_method_idx_);
- const std::vector<uint8_t>* dex_gc_map = CreateLengthPrefixedDexGcMap(*(map.get()));
- verifier::MethodVerifier::SetDexGcMap(ref, *dex_gc_map);
+ // Compute information for compiler.
+ if (Runtime::Current()->IsCompiler()) {
+ MethodReference ref(dex_file_, dex_method_idx_);
+ bool compile = IsCandidateForCompilation(code_item_, method_access_flags_);
+ if (compile) {
+ /* Generate a register map and add it to the method. */
+ UniquePtr<const std::vector<uint8_t> > map(GenerateGcMap());
+ if (map.get() == NULL) {
+ DCHECK_NE(failures_.size(), 0U);
+ return false; // Not a real failure, but a failure to encode
+ }
+ if (kIsDebugBuild) {
+ VerifyGcMap(*map);
+ }
+ const std::vector<uint8_t>* dex_gc_map = CreateLengthPrefixedDexGcMap(*(map.get()));
+ verifier::MethodVerifier::SetDexGcMap(ref, *dex_gc_map);
+ }
- MethodVerifier::MethodSafeCastSet* method_to_safe_casts = GenerateSafeCastSet();
- if (method_to_safe_casts != NULL) {
- SetSafeCastMap(ref, method_to_safe_casts);
- }
+ if (has_check_casts_) {
+ MethodVerifier::MethodSafeCastSet* method_to_safe_casts = GenerateSafeCastSet();
+ if (method_to_safe_casts != NULL) {
+ SetSafeCastMap(ref, method_to_safe_casts);
+ }
+ }
- MethodVerifier::PcToConcreteMethodMap* pc_to_concrete_method = GenerateDevirtMap();
- if (pc_to_concrete_method != NULL) {
- SetDevirtMap(ref, pc_to_concrete_method);
+ if (has_virtual_or_interface_invokes_) {
+ MethodVerifier::PcToConcreteMethodMap* pc_to_concrete_method = GenerateDevirtMap();
+ if (pc_to_concrete_method != NULL) {
+ SetDevirtMap(ref, pc_to_concrete_method);
+ }
+ }
}
return true;
}
@@ -1164,13 +1222,15 @@
break;
}
default:
- Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "unexpected signature type char '" << descriptor << "'";
+ Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "unexpected signature type char '"
+ << descriptor << "'";
return false;
}
cur_arg++;
}
if (cur_arg != expected_args) {
- Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "expected " << expected_args << " arguments, found " << cur_arg;
+ Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "expected " << expected_args
+ << " arguments, found " << cur_arg;
return false;
}
const char* descriptor = dex_file_->GetReturnTypeDescriptor(proto_id);
@@ -1304,12 +1364,14 @@
if (dead_start < 0)
dead_start = insn_idx;
} else if (dead_start >= 0) {
- LogVerifyInfo() << "dead code " << reinterpret_cast<void*>(dead_start) << "-" << reinterpret_cast<void*>(insn_idx - 1);
+ LogVerifyInfo() << "dead code " << reinterpret_cast<void*>(dead_start)
+ << "-" << reinterpret_cast<void*>(insn_idx - 1);
dead_start = -1;
}
}
if (dead_start >= 0) {
- LogVerifyInfo() << "dead code " << reinterpret_cast<void*>(dead_start) << "-" << reinterpret_cast<void*>(insn_idx - 1);
+ LogVerifyInfo() << "dead code " << reinterpret_cast<void*>(dead_start)
+ << "-" << reinterpret_cast<void*>(insn_idx - 1);
}
// To dump the state of the verify after a method, do something like:
// if (PrettyMethod(dex_method_idx_, *dex_file_) ==
@@ -1466,7 +1528,8 @@
/* check the method signature */
const RegType& return_type = GetMethodReturnType();
if (!return_type.IsCategory1Types()) {
- Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "unexpected non-category 1 return type " << return_type;
+ Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "unexpected non-category 1 return type "
+ << return_type;
} else {
// Compilers may generate synthetic functions that write byte values into boolean fields.
// Also, it may use integer values for boolean, byte, short, and character return types.
@@ -1515,10 +1578,14 @@
// Disallow returning uninitialized values and verify that the reference in vAA is an
// instance of the "return_type"
if (reg_type.IsUninitializedTypes()) {
- Fail(VERIFY_ERROR_BAD_CLASS_SOFT) << "returning uninitialized object '" << reg_type << "'";
+ Fail(VERIFY_ERROR_BAD_CLASS_SOFT) << "returning uninitialized object '"
+ << reg_type << "'";
} else if (!return_type.IsAssignableFrom(reg_type)) {
- Fail(reg_type.IsUnresolvedTypes() ? VERIFY_ERROR_BAD_CLASS_SOFT : VERIFY_ERROR_BAD_CLASS_HARD)
- << "returning '" << reg_type << "', but expected from declaration '" << return_type << "'";
+ Fail(reg_type.IsUnresolvedTypes() ?
+ VERIFY_ERROR_BAD_CLASS_SOFT :
+ VERIFY_ERROR_BAD_CLASS_HARD)
+ << "returning '" << reg_type << "', but expected from declaration '"
+ << return_type << "'";
}
}
}
@@ -1738,7 +1805,8 @@
case Instruction::THROW: {
const RegType& res_type = work_line_->GetRegisterType(inst->VRegA_11x());
if (!reg_types_.JavaLangThrowable(false).IsAssignableFrom(res_type)) {
- Fail(VERIFY_ERROR_BAD_CLASS_SOFT) << "thrown class " << res_type << " not instanceof Throwable";
+ Fail(VERIFY_ERROR_BAD_CLASS_SOFT) << "thrown class " << res_type
+ << " not instanceof Throwable";
}
break;
}
@@ -1760,7 +1828,8 @@
/* array_type can be null if the reg type is Zero */
if (!array_type.IsZero()) {
if (!array_type.IsArrayTypes()) {
- Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "invalid fill-array-data with array type " << array_type;
+ Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "invalid fill-array-data with array type "
+ << array_type;
} else {
const RegType& component_type = reg_types_.GetComponentType(array_type, class_loader_);
DCHECK(!component_type.IsConflict());
@@ -1800,8 +1869,8 @@
mismatch = !reg_type1.IsIntegralTypes() || !reg_type2.IsIntegralTypes();
}
if (mismatch) {
- Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "args to if-eq/if-ne (" << reg_type1 << "," << reg_type2
- << ") must both be references or integral";
+ Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "args to if-eq/if-ne (" << reg_type1 << ","
+ << reg_type2 << ") must both be references or integral";
}
break;
}
@@ -1821,7 +1890,8 @@
case Instruction::IF_NEZ: {
const RegType& reg_type = work_line_->GetRegisterType(inst->VRegA_21t());
if (!reg_type.IsReferenceTypes() && !reg_type.IsIntegralTypes()) {
- Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "type " << reg_type << " unexpected as arg to if-eqz/if-nez";
+ Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "type " << reg_type
+ << " unexpected as arg to if-eqz/if-nez";
}
// Find previous instruction - its existence is a precondition to peephole optimization.
@@ -2143,7 +2213,10 @@
case Instruction::INVOKE_STATIC:
case Instruction::INVOKE_STATIC_RANGE: {
bool is_range = (inst->Opcode() == Instruction::INVOKE_STATIC_RANGE);
- mirror::AbstractMethod* called_method = VerifyInvocationArgs(inst, METHOD_STATIC, is_range, false);
+ mirror::AbstractMethod* called_method = VerifyInvocationArgs(inst,
+ METHOD_STATIC,
+ is_range,
+ false);
const char* descriptor;
if (called_method == NULL) {
uint32_t method_idx = (is_range) ? inst->VRegB_3rc() : inst->VRegB_35c();
@@ -2165,7 +2238,10 @@
case Instruction::INVOKE_INTERFACE:
case Instruction::INVOKE_INTERFACE_RANGE: {
bool is_range = (inst->Opcode() == Instruction::INVOKE_INTERFACE_RANGE);
- mirror::AbstractMethod* abs_method = VerifyInvocationArgs(inst, METHOD_INTERFACE, is_range, false);
+ mirror::AbstractMethod* abs_method = VerifyInvocationArgs(inst,
+ METHOD_INTERFACE,
+ is_range,
+ false);
if (abs_method != NULL) {
mirror::Class* called_interface = abs_method->GetDeclaringClass();
if (!called_interface->IsInterface() && !called_interface->IsObjectClass()) {
@@ -2329,7 +2405,11 @@
case Instruction::MUL_FLOAT:
case Instruction::DIV_FLOAT:
case Instruction::REM_FLOAT:
- work_line_->CheckBinaryOp(inst, reg_types_.Float(), reg_types_.Float(), reg_types_.Float(), false);
+ work_line_->CheckBinaryOp(inst,
+ reg_types_.Float(),
+ reg_types_.Float(),
+ reg_types_.Float(),
+ false);
break;
case Instruction::ADD_DOUBLE:
case Instruction::SUB_DOUBLE:
@@ -2347,15 +2427,27 @@
case Instruction::SHL_INT_2ADDR:
case Instruction::SHR_INT_2ADDR:
case Instruction::USHR_INT_2ADDR:
- work_line_->CheckBinaryOp2addr(inst, reg_types_.Integer(), reg_types_.Integer(), reg_types_.Integer(), false);
+ work_line_->CheckBinaryOp2addr(inst,
+ reg_types_.Integer(),
+ reg_types_.Integer(),
+ reg_types_.Integer(),
+ false);
break;
case Instruction::AND_INT_2ADDR:
case Instruction::OR_INT_2ADDR:
case Instruction::XOR_INT_2ADDR:
- work_line_->CheckBinaryOp2addr(inst, reg_types_.Integer(), reg_types_.Integer(), reg_types_.Integer(), true);
+ work_line_->CheckBinaryOp2addr(inst,
+ reg_types_.Integer(),
+ reg_types_.Integer(),
+ reg_types_.Integer(),
+ true);
break;
case Instruction::DIV_INT_2ADDR:
- work_line_->CheckBinaryOp2addr(inst, reg_types_.Integer(), reg_types_.Integer(), reg_types_.Integer(), false);
+ work_line_->CheckBinaryOp2addr(inst,
+ reg_types_.Integer(),
+ reg_types_.Integer(),
+ reg_types_.Integer(),
+ false);
break;
case Instruction::ADD_LONG_2ADDR:
case Instruction::SUB_LONG_2ADDR:
@@ -2380,7 +2472,11 @@
case Instruction::MUL_FLOAT_2ADDR:
case Instruction::DIV_FLOAT_2ADDR:
case Instruction::REM_FLOAT_2ADDR:
- work_line_->CheckBinaryOp2addr(inst, reg_types_.Float(), reg_types_.Float(), reg_types_.Float(), false);
+ work_line_->CheckBinaryOp2addr(inst,
+ reg_types_.Float(),
+ reg_types_.Float(),
+ reg_types_.Float(),
+ false);
break;
case Instruction::ADD_DOUBLE_2ADDR:
case Instruction::SUB_DOUBLE_2ADDR:
@@ -2660,6 +2756,20 @@
// Make workline consistent with fallthrough computed from peephole optimization.
work_line_->CopyFromLine(fallthrough_line.get());
}
+ if (insn_flags_[next_insn_idx].IsReturn()) {
+ // For returns we only care about the operand to the return, all other registers are dead.
+ const Instruction* ret_inst = Instruction::At(code_item_->insns_ + next_insn_idx);
+ Instruction::Code opcode = ret_inst->Opcode();
+ if ((opcode == Instruction::RETURN_VOID) || (opcode == Instruction::RETURN_VOID_BARRIER)) {
+ work_line_->MarkAllRegistersAsConflicts();
+ } else {
+ if (opcode == Instruction::RETURN_WIDE) {
+ work_line_->MarkAllRegistersAsConflictsExceptWide(ret_inst->VRegA_11x());
+ } else {
+ work_line_->MarkAllRegistersAsConflictsExcept(ret_inst->VRegA_11x());
+ }
+ }
+ }
RegisterLine* next_line = reg_table_.GetLine(next_insn_idx);
if (next_line != NULL) {
// Merge registers into what we have for the next instruction,
@@ -3072,8 +3182,9 @@
for (size_t param_index = 0; param_index < params_size; param_index++) {
if (actual_args >= expected_args) {
Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Rejecting invalid call to '" << PrettyMethod(res_method)
- << "'. Expected " << expected_args << " arguments, processing argument " << actual_args
- << " (where longs/doubles count twice).";
+ << "'. Expected " << expected_args
+ << " arguments, processing argument " << actual_args
+ << " (where longs/doubles count twice).";
return NULL;
}
const char* descriptor =
@@ -3226,7 +3337,8 @@
// The instruction agrees with the type of array, confirm the value to be stored does too
// Note: we use the instruction type (rather than the component type) for aput-object as
// incompatible classes will be caught at runtime as an array store exception
- work_line_->VerifyRegisterType(inst->VRegA_23x(), is_primitive ? component_type : insn_type);
+ work_line_->VerifyRegisterType(inst->VRegA_23x(),
+ is_primitive ? component_type : insn_type);
}
}
}
@@ -3245,8 +3357,10 @@
if (klass_type.IsUnresolvedTypes()) {
return NULL; // Can't resolve Class so no more to do here, will do checking at runtime.
}
- mirror::Field* field = Runtime::Current()->GetClassLinker()->ResolveFieldJLS(*dex_file_, field_idx,
- dex_cache_, class_loader_);
+ mirror::Field* field = Runtime::Current()->GetClassLinker()->ResolveFieldJLS(*dex_file_,
+ field_idx,
+ dex_cache_,
+ class_loader_);
if (field == NULL) {
LOG(INFO) << "Unable to resolve static field " << field_idx << " ("
<< dex_file_->GetFieldName(field_id) << ") in "
@@ -3280,8 +3394,10 @@
if (klass_type.IsUnresolvedTypes()) {
return NULL; // Can't resolve Class so no more to do here
}
- mirror::Field* field = Runtime::Current()->GetClassLinker()->ResolveFieldJLS(*dex_file_, field_idx,
- dex_cache_, class_loader_);
+ mirror::Field* field = Runtime::Current()->GetClassLinker()->ResolveFieldJLS(*dex_file_,
+ field_idx,
+ dex_cache_,
+ class_loader_);
if (field == NULL) {
LOG(INFO) << "Unable to resolve instance field " << field_idx << " ("
<< dex_file_->GetFieldName(field_id) << ") in "
@@ -3312,8 +3428,8 @@
// Field accesses through uninitialized references are only allowable for constructors where
// the field is declared in this class
Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "cannot access instance field " << PrettyField(field)
- << " of a not fully initialized object within the context of "
- << PrettyMethod(dex_method_idx_, *dex_file_);
+ << " of a not fully initialized object within the context"
+ << " of " << PrettyMethod(dex_method_idx_, *dex_file_);
return NULL;
} else if (!field_klass.IsAssignableFrom(obj_type)) {
// Trying to access C1.field1 using reference of type C2, which is neither C1 or a sub-class
@@ -3647,9 +3763,28 @@
* there's nothing to "merge". Copy the registers over and mark it as changed. (This is the
* only way a register can transition out of "unknown", so this is not just an optimization.)
*/
- target_line->CopyFromLine(merge_line);
+ if (!insn_flags_[next_insn].IsReturn()) {
+ target_line->CopyFromLine(merge_line);
+ } else {
+ // For returns we only care about the operand to the return, all other registers are dead.
+ // Initialize them as conflicts so they don't add to GC and deoptimization information.
+ const Instruction* ret_inst = Instruction::At(code_item_->insns_ + next_insn);
+ Instruction::Code opcode = ret_inst->Opcode();
+ if ((opcode == Instruction::RETURN_VOID) || (opcode == Instruction::RETURN_VOID_BARRIER)) {
+ target_line->MarkAllRegistersAsConflicts();
+ } else {
+ target_line->CopyFromLine(merge_line);
+ if (opcode == Instruction::RETURN_WIDE) {
+ target_line->MarkAllRegistersAsConflictsExceptWide(ret_inst->VRegA_11x());
+ } else {
+ target_line->MarkAllRegistersAsConflictsExcept(ret_inst->VRegA_11x());
+ }
+ }
+ }
} else {
- UniquePtr<RegisterLine> copy(gDebugVerify ? new RegisterLine(target_line->NumRegs(), this) : NULL);
+ UniquePtr<RegisterLine> copy(gDebugVerify ?
+ new RegisterLine(target_line->NumRegs(), this) :
+ NULL);
if (gDebugVerify) {
copy->CopyFromLine(target_line);
}
@@ -3686,7 +3821,8 @@
const RegType& MethodVerifier::GetDeclaringClass() {
if (declaring_class_ == NULL) {
const DexFile::MethodId& method_id = dex_file_->GetMethodId(dex_method_idx_);
- const char* descriptor = dex_file_->GetTypeDescriptor(dex_file_->GetTypeId(method_id.class_idx_));
+ const char* descriptor
+ = dex_file_->GetTypeDescriptor(dex_file_->GetTypeId(method_id.class_idx_));
if (mirror_method_ != NULL) {
mirror::Class* klass = mirror_method_->GetDeclaringClass();
declaring_class_ = ®_types_.FromClass(descriptor, klass,
@@ -3919,6 +4055,7 @@
}
void MethodVerifier::SetDexGcMap(MethodReference ref, const std::vector<uint8_t>& gc_map) {
+ DCHECK(Runtime::Current()->IsCompiler());
{
WriterMutexLock mu(Thread::Current(), *dex_gc_maps_lock_);
DexGcMapTable::iterator it = dex_gc_maps_->find(ref);
@@ -3933,6 +4070,7 @@
void MethodVerifier::SetSafeCastMap(MethodReference ref, const MethodSafeCastSet* cast_set) {
+ DCHECK(Runtime::Current()->IsCompiler());
MutexLock mu(Thread::Current(), *safecast_map_lock_);
SafeCastMap::iterator it = safecast_map_->find(ref);
if (it != safecast_map_->end()) {
@@ -3941,10 +4079,11 @@
}
safecast_map_->Put(ref, cast_set);
- CHECK(safecast_map_->find(ref) != safecast_map_->end());
+ DCHECK(safecast_map_->find(ref) != safecast_map_->end());
}
bool MethodVerifier::IsSafeCast(MethodReference ref, uint32_t pc) {
+ DCHECK(Runtime::Current()->IsCompiler());
MutexLock mu(Thread::Current(), *safecast_map_lock_);
SafeCastMap::const_iterator it = safecast_map_->find(ref);
if (it == safecast_map_->end()) {
@@ -3957,6 +4096,7 @@
}
const std::vector<uint8_t>* MethodVerifier::GetDexGcMap(MethodReference ref) {
+ DCHECK(Runtime::Current()->IsCompiler());
ReaderMutexLock mu(Thread::Current(), *dex_gc_maps_lock_);
DexGcMapTable::const_iterator it = dex_gc_maps_->find(ref);
if (it == dex_gc_maps_->end()) {
@@ -3969,6 +4109,7 @@
void MethodVerifier::SetDevirtMap(MethodReference ref,
const PcToConcreteMethodMap* devirt_map) {
+ DCHECK(Runtime::Current()->IsCompiler());
WriterMutexLock mu(Thread::Current(), *devirt_maps_lock_);
DevirtualizationMapTable::iterator it = devirt_maps_->find(ref);
if (it != devirt_maps_->end()) {
@@ -3977,11 +4118,12 @@
}
devirt_maps_->Put(ref, devirt_map);
- CHECK(devirt_maps_->find(ref) != devirt_maps_->end());
+ DCHECK(devirt_maps_->find(ref) != devirt_maps_->end());
}
const MethodReference* MethodVerifier::GetDevirtMap(const MethodReference& ref,
uint32_t dex_pc) {
+ DCHECK(Runtime::Current()->IsCompiler());
ReaderMutexLock mu(Thread::Current(), *devirt_maps_lock_);
DevirtualizationMapTable::const_iterator it = devirt_maps_->find(ref);
if (it == devirt_maps_->end()) {
@@ -3989,7 +4131,8 @@
}
// Look up the PC in the map, get the concrete method to execute and return its reference.
- MethodVerifier::PcToConcreteMethodMap::const_iterator pc_to_concrete_method = it->second->find(dex_pc);
+ MethodVerifier::PcToConcreteMethodMap::const_iterator pc_to_concrete_method
+ = it->second->find(dex_pc);
if (pc_to_concrete_method != it->second->end()) {
return &(pc_to_concrete_method->second);
} else {
@@ -4041,6 +4184,24 @@
return result;
}
+bool MethodVerifier::IsCandidateForCompilation(const DexFile::CodeItem* code_item,
+ const uint32_t access_flags) {
+ // Don't compile class initializers, ever.
+ if (((access_flags & kAccConstructor) != 0) && ((access_flags & kAccStatic) != 0)) {
+ return false;
+ }
+
+ const Runtime* runtime = Runtime::Current();
+ if (runtime->IsSmallMode() && runtime->UseCompileTimeClassPath()) {
+ // In Small mode, we only compile small methods.
+ const uint32_t code_size = code_item->insns_size_in_code_units_;
+ return (code_size < runtime->GetSmallModeMethodDexSizeLimit());
+ } else {
+ // In normal mode, we compile everything.
+ return true;
+ }
+}
+
ReaderWriterMutex* MethodVerifier::dex_gc_maps_lock_ = NULL;
MethodVerifier::DexGcMapTable* MethodVerifier::dex_gc_maps_ = NULL;
@@ -4054,65 +4215,79 @@
MethodVerifier::RejectedClassesTable* MethodVerifier::rejected_classes_ = NULL;
void MethodVerifier::Init() {
- dex_gc_maps_lock_ = new ReaderWriterMutex("verifier GC maps lock");
- Thread* self = Thread::Current();
- {
- WriterMutexLock mu(self, *dex_gc_maps_lock_);
- dex_gc_maps_ = new MethodVerifier::DexGcMapTable;
- }
+ if (Runtime::Current()->IsCompiler()) {
+ dex_gc_maps_lock_ = new ReaderWriterMutex("verifier GC maps lock");
+ Thread* self = Thread::Current();
+ {
+ WriterMutexLock mu(self, *dex_gc_maps_lock_);
+ dex_gc_maps_ = new MethodVerifier::DexGcMapTable;
+ }
- safecast_map_lock_ = new Mutex("verifier Cast Elision lock");
- {
- MutexLock mu(self, *safecast_map_lock_);
- safecast_map_ = new MethodVerifier::SafeCastMap();
- }
+ safecast_map_lock_ = new Mutex("verifier Cast Elision lock");
+ {
+ MutexLock mu(self, *safecast_map_lock_);
+ safecast_map_ = new MethodVerifier::SafeCastMap();
+ }
- devirt_maps_lock_ = new ReaderWriterMutex("verifier Devirtualization lock");
+ devirt_maps_lock_ = new ReaderWriterMutex("verifier Devirtualization lock");
- {
- WriterMutexLock mu(self, *devirt_maps_lock_);
- devirt_maps_ = new MethodVerifier::DevirtualizationMapTable();
- }
+ {
+ WriterMutexLock mu(self, *devirt_maps_lock_);
+ devirt_maps_ = new MethodVerifier::DevirtualizationMapTable();
+ }
- rejected_classes_lock_ = new Mutex("verifier rejected classes lock");
- {
- MutexLock mu(self, *rejected_classes_lock_);
- rejected_classes_ = new MethodVerifier::RejectedClassesTable;
+ rejected_classes_lock_ = new Mutex("verifier rejected classes lock");
+ {
+ MutexLock mu(self, *rejected_classes_lock_);
+ rejected_classes_ = new MethodVerifier::RejectedClassesTable;
+ }
}
art::verifier::RegTypeCache::Init();
}
void MethodVerifier::Shutdown() {
- Thread* self = Thread::Current();
- {
- WriterMutexLock mu(self, *dex_gc_maps_lock_);
- STLDeleteValues(dex_gc_maps_);
- delete dex_gc_maps_;
- dex_gc_maps_ = NULL;
- }
- delete dex_gc_maps_lock_;
- dex_gc_maps_lock_ = NULL;
+ if (Runtime::Current()->IsCompiler()) {
+ Thread* self = Thread::Current();
+ {
+ WriterMutexLock mu(self, *dex_gc_maps_lock_);
+ STLDeleteValues(dex_gc_maps_);
+ delete dex_gc_maps_;
+ dex_gc_maps_ = NULL;
+ }
+ delete dex_gc_maps_lock_;
+ dex_gc_maps_lock_ = NULL;
- {
- WriterMutexLock mu(self, *devirt_maps_lock_);
- STLDeleteValues(devirt_maps_);
- delete devirt_maps_;
- devirt_maps_ = NULL;
- }
- delete devirt_maps_lock_;
- devirt_maps_lock_ = NULL;
+ {
+ MutexLock mu(self, *safecast_map_lock_);
+ STLDeleteValues(safecast_map_);
+ delete safecast_map_;
+ safecast_map_ = NULL;
+ }
+ delete safecast_map_lock_;
+ safecast_map_lock_ = NULL;
- {
- MutexLock mu(self, *rejected_classes_lock_);
- delete rejected_classes_;
- rejected_classes_ = NULL;
+ {
+ WriterMutexLock mu(self, *devirt_maps_lock_);
+ STLDeleteValues(devirt_maps_);
+ delete devirt_maps_;
+ devirt_maps_ = NULL;
+ }
+ delete devirt_maps_lock_;
+ devirt_maps_lock_ = NULL;
+
+ {
+ MutexLock mu(self, *rejected_classes_lock_);
+ delete rejected_classes_;
+ rejected_classes_ = NULL;
+ }
+ delete rejected_classes_lock_;
+ rejected_classes_lock_ = NULL;
}
- delete rejected_classes_lock_;
- rejected_classes_lock_ = NULL;
verifier::RegTypeCache::ShutDown();
}
void MethodVerifier::AddRejectedClass(ClassReference ref) {
+ DCHECK(Runtime::Current()->IsCompiler());
{
MutexLock mu(Thread::Current(), *rejected_classes_lock_);
rejected_classes_->insert(ref);
@@ -4121,6 +4296,7 @@
}
bool MethodVerifier::IsClassRejected(ClassReference ref) {
+ DCHECK(Runtime::Current()->IsCompiler());
MutexLock mu(Thread::Current(), *rejected_classes_lock_);
return (rejected_classes_->find(ref) != rejected_classes_->end());
}
diff --git a/runtime/verifier/method_verifier.h b/runtime/verifier/method_verifier.h
index e1bcbb1..3f98a00 100644
--- a/runtime/verifier/method_verifier.h
+++ b/runtime/verifier/method_verifier.h
@@ -237,6 +237,9 @@
// Describe VRegs at the given dex pc.
std::vector<int32_t> DescribeVRegs(uint32_t dex_pc);
+ static bool IsCandidateForCompilation(const DexFile::CodeItem* code_item,
+ const uint32_t access_flags);
+
private:
// Adds the given string to the beginning of the last failure message.
void PrependToLastFailMessage(std::string);
@@ -654,7 +657,7 @@
LOCKS_EXCLUDED(devirt_maps_lock_);
typedef std::set<ClassReference> RejectedClassesTable;
static Mutex* rejected_classes_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
- static RejectedClassesTable* rejected_classes_;
+ static RejectedClassesTable* rejected_classes_ GUARDED_BY(rejected_classes_lock_);
static void AddRejectedClass(ClassReference ref)
LOCKS_EXCLUDED(rejected_classes_lock_);
@@ -717,6 +720,13 @@
// Converts soft failures to hard failures when false. Only false when the compiler isn't
// running and the verifier is called from the class linker.
const bool allow_soft_failures_;
+
+ // Indicates if the method being verified contains at least one check-cast instruction.
+ bool has_check_casts_;
+
+ // Indicates if the method being verified contains at least one invoke-virtual/range
+ // or invoke-interface/range.
+ bool has_virtual_or_interface_invokes_;
};
std::ostream& operator<<(std::ostream& os, const MethodVerifier::FailureKind& rhs);
diff --git a/runtime/verifier/register_line.cc b/runtime/verifier/register_line.cc
index d2abaac..7965c06 100644
--- a/runtime/verifier/register_line.cc
+++ b/runtime/verifier/register_line.cc
@@ -167,7 +167,7 @@
DCHECK(uninit_type.IsUninitializedTypes());
const RegType& init_type = verifier_->GetRegTypeCache()->FromUninitialized(uninit_type);
size_t changed = 0;
- for (size_t i = 0; i < num_regs_; i++) {
+ for (uint32_t i = 0; i < num_regs_; i++) {
if (GetRegisterType(i).Equals(uninit_type)) {
line_[i] = init_type.GetId();
changed++;
@@ -176,6 +176,31 @@
DCHECK_GT(changed, 0u);
}
+void RegisterLine::MarkAllRegistersAsConflicts() {
+ uint16_t conflict_type_id = verifier_->GetRegTypeCache()->Conflict().GetId();
+ for (uint32_t i = 0; i < num_regs_; i++) {
+ line_[i] = conflict_type_id;
+ }
+}
+
+void RegisterLine::MarkAllRegistersAsConflictsExcept(uint32_t vsrc) {
+ uint16_t conflict_type_id = verifier_->GetRegTypeCache()->Conflict().GetId();
+ for (uint32_t i = 0; i < num_regs_; i++) {
+ if (i != vsrc) {
+ line_[i] = conflict_type_id;
+ }
+ }
+}
+
+void RegisterLine::MarkAllRegistersAsConflictsExceptWide(uint32_t vsrc) {
+ uint16_t conflict_type_id = verifier_->GetRegTypeCache()->Conflict().GetId();
+ for (uint32_t i = 0; i < num_regs_; i++) {
+ if ((i != vsrc) && (i != (vsrc + 1))) {
+ line_[i] = conflict_type_id;
+ }
+ }
+}
+
std::string RegisterLine::Dump() const {
std::string result;
for (size_t i = 0; i < num_regs_; i++) {
diff --git a/runtime/verifier/register_line.h b/runtime/verifier/register_line.h
index cde7b9b..f380877 100644
--- a/runtime/verifier/register_line.h
+++ b/runtime/verifier/register_line.h
@@ -141,6 +141,13 @@
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
/*
+ * Update all registers to be Conflict except vsrc.
+ */
+ void MarkAllRegistersAsConflicts();
+ void MarkAllRegistersAsConflictsExcept(uint32_t vsrc);
+ void MarkAllRegistersAsConflictsExceptWide(uint32_t vsrc);
+
+ /*
* Check constraints on constructor return. Specifically, make sure that the "this" argument got
* initialized.
* The "this" argument to <init> uses code offset kUninitThisArgAddr, which puts it at the start
diff --git a/test/ReferenceMap/stack_walk_refmap_jni.cc b/test/ReferenceMap/stack_walk_refmap_jni.cc
index 10ca563..3b5d80d 100644
--- a/test/ReferenceMap/stack_walk_refmap_jni.cc
+++ b/test/ReferenceMap/stack_walk_refmap_jni.cc
@@ -103,7 +103,9 @@
// 0024: move-object v3, v2
// 0025: goto 0013
// Detaled dex instructions for ReferenceMap.java are at the end of this function.
- CHECK_REGS_CONTAIN_REFS(8, 3, 2, 1); // v8: this, v3: y, v2: y, v1: x
+ // CHECK_REGS_CONTAIN_REFS(8, 3, 2, 1); // v8: this, v3: y, v2: y, v1: x
+ // We eliminate the non-live registers at a return, so only v3 is live:
+ CHECK_REGS_CONTAIN_REFS(3); // v3: y
ref_bitmap = map.FindBitMap(m->NativePcOffset(m->ToFirstNativeSafepointPc(0x18U)));
CHECK(ref_bitmap);
@@ -188,7 +190,7 @@
// 0:[Unknown],1:[Reference: java.lang.Object[]],2:[Zero],3:[Reference: java.lang.Object],4:[32-bit Constant: 2],5:[Unknown],6:[32-bit Constant: 1],7:[Zero],8:[Reference: ReferenceMap],
// |0010: +invoke-virtual-quick {v8, v7}, [000c] // vtable #000c
-// 0:[Conflict],1:[Reference: java.lang.Object[]],2:[Reference: java.lang.Object],3:[Reference: java.lang.Object],4:[32-bit Constant: 2],5:[Conflict],6:[32-bit Constant: 1],7:[Zero],8:[Reference: ReferenceMap],
+// 0:[Conflict],1:[Conflict],2:[Conflict],3:[Reference: java.lang.Object],4:[Conflict],5:[Conflict],6:[Conflict],7:[Conflict],8:[Conflict],
// |0013: return-object v3
// |0014: move-exception v0