summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--build/Android.gtest.mk5
-rw-r--r--compiler/dex/dex_to_dex_compiler.cc85
-rw-r--r--compiler/driver/compiler_driver.cc2
-rw-r--r--compiler/sea_ir/sea.cc482
-rw-r--r--compiler/sea_ir/sea.h236
-rw-r--r--compiler/utils/scoped_hashtable.h71
-rw-r--r--compiler/utils/scoped_hashtable_test.cc68
-rw-r--r--runtime/atomic_integer.h40
-rw-r--r--runtime/base/mutex.h2
-rw-r--r--runtime/class_linker.cc25
-rw-r--r--runtime/dex_instruction.cc75
-rw-r--r--runtime/dex_instruction.h9
-rw-r--r--runtime/gc/accounting/atomic_stack.h12
-rw-r--r--runtime/gc/collector/mark_sweep.cc20
-rw-r--r--runtime/gc/heap.cc91
-rw-r--r--runtime/gc/heap.h16
-rw-r--r--runtime/gc/space/space.h1
-rw-r--r--runtime/instrumentation.cc38
-rw-r--r--runtime/instrumentation.h2
-rw-r--r--runtime/jdwp/jdwp.h6
-rw-r--r--runtime/jdwp/jdwp_handler.cc1
-rw-r--r--runtime/jdwp/jdwp_main.cc99
-rw-r--r--runtime/locks.h2
-rw-r--r--runtime/mirror/abstract_method-inl.h12
-rw-r--r--runtime/native/dalvik_system_VMRuntime.cc21
-rw-r--r--runtime/stack.cc5
-rw-r--r--runtime/thread.cc37
-rw-r--r--runtime/thread.h3
-rw-r--r--runtime/thread_android.cc8
-rw-r--r--runtime/thread_linux.cc37
-rw-r--r--runtime/thread_pool_test.cc2
-rw-r--r--runtime/well_known_classes.cc5
-rw-r--r--runtime/well_known_classes.h4
-rw-r--r--test/Android.mk1
-rw-r--r--test/NativeAllocations/NativeAllocations.java65
35 files changed, 1290 insertions, 298 deletions
diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk
index 2b677be..407cef9 100644
--- a/build/Android.gtest.mk
+++ b/build/Android.gtest.mk
@@ -57,6 +57,11 @@ TEST_COMMON_SRC_FILES := \
compiler/elf_writer_test.cc \
compiler/jni/jni_compiler_test.cc
+ifeq ($(ART_SEA_IR_MODE),true)
+TEST_COMMON_SRC_FILES += \
+ compiler/utils/scoped_hashtable_test.cc
+endif
+
TEST_TARGET_SRC_FILES := \
$(TEST_COMMON_SRC_FILES)
diff --git a/compiler/dex/dex_to_dex_compiler.cc b/compiler/dex/dex_to_dex_compiler.cc
index ee68a5d..4419fed 100644
--- a/compiler/dex/dex_to_dex_compiler.cc
+++ b/compiler/dex/dex_to_dex_compiler.cc
@@ -30,8 +30,8 @@ namespace optimizer {
// Controls quickening activation.
const bool kEnableQuickening = true;
-// Controls logging.
-const bool kEnableLogging = false;
+// Control check-cast elision.
+const bool kEnableCheckCastEllision = true;
class DexCompiler {
public:
@@ -59,6 +59,11 @@ class DexCompiler {
// a barrier is required.
void CompileReturnVoid(Instruction* inst, uint32_t dex_pc);
+ // Compiles a CHECK-CAST into 2 NOP instructions if it is known to be safe. In
+ // this case, returns the second NOP instruction pointer. Otherwise, returns
+ // the given "inst".
+ Instruction* CompileCheckCast(Instruction* inst, uint32_t dex_pc);
+
// Compiles a field access into a quick field access.
// The field index is replaced by an offset within an Object where we can read
// from / write to this field. Therefore, this does not involve any resolution
@@ -145,6 +150,10 @@ void DexCompiler::Compile() {
CompileReturnVoid(inst, dex_pc);
break;
+ case Instruction::CHECK_CAST:
+ inst = CompileCheckCast(inst, dex_pc);
+ break;
+
case Instruction::IGET:
CompileInstanceFieldAccess(inst, dex_pc, Instruction::IGET_QUICK, false);
break;
@@ -202,16 +211,44 @@ void DexCompiler::CompileReturnVoid(Instruction* inst, uint32_t dex_pc) {
return;
}
// Replace RETURN_VOID by RETURN_VOID_BARRIER.
- if (kEnableLogging) {
- LOG(INFO) << "Replacing " << Instruction::Name(inst->Opcode())
- << " by " << Instruction::Name(Instruction::RETURN_VOID_BARRIER)
- << " at dex pc " << StringPrintf("0x%x", dex_pc) << " in method "
- << PrettyMethod(unit_.GetDexMethodIndex(), GetDexFile(), true);
- }
+ VLOG(compiler) << "Replacing " << Instruction::Name(inst->Opcode())
+ << " by " << Instruction::Name(Instruction::RETURN_VOID_BARRIER)
+ << " at dex pc " << StringPrintf("0x%x", dex_pc) << " in method "
+ << PrettyMethod(unit_.GetDexMethodIndex(), GetDexFile(), true);
ScopedDexWriteAccess sdwa(GetModifiableDexFile(), inst, 2u);
inst->SetOpcode(Instruction::RETURN_VOID_BARRIER);
}
+Instruction* DexCompiler::CompileCheckCast(Instruction* inst, uint32_t dex_pc) {
+ if (!kEnableCheckCastEllision) {
+ return inst;
+ }
+ MethodReference referrer(&GetDexFile(), unit_.GetDexMethodIndex());
+ if (!driver_.IsSafeCast(referrer, dex_pc)) {
+ return inst;
+ }
+ // Ok, this is a safe cast. Since the "check-cast" instruction size is 2 code
+ // units and a "nop" instruction size is 1 code unit, we need to replace it by
+ // 2 consecutive NOP instructions.
+ // Because the caller loops over instructions by calling Instruction::Next onto
+ // the current instruction, we need to return the 2nd NOP instruction. Indeed,
+ // its next instruction is the former check-cast's next instruction.
+ VLOG(compiler) << "Removing " << Instruction::Name(inst->Opcode())
+ << " by replacing it with 2 NOPs at dex pc "
+ << StringPrintf("0x%x", dex_pc) << " in method "
+ << PrettyMethod(unit_.GetDexMethodIndex(), GetDexFile(), true);
+ // We are modifying 4 consecutive bytes.
+ ScopedDexWriteAccess sdwa(GetModifiableDexFile(), inst, 4u);
+ inst->SetOpcode(Instruction::NOP);
+ inst->SetVRegA_10x(0u); // keep compliant with verifier.
+ // Get to next instruction which is the second half of check-cast and replace
+ // it by a NOP.
+ inst = const_cast<Instruction*>(inst->Next());
+ inst->SetOpcode(Instruction::NOP);
+ inst->SetVRegA_10x(0u); // keep compliant with verifier.
+ return inst;
+}
+
void DexCompiler::CompileInstanceFieldAccess(Instruction* inst,
uint32_t dex_pc,
Instruction::Code new_opcode,
@@ -225,15 +262,12 @@ void DexCompiler::CompileInstanceFieldAccess(Instruction* inst,
bool fast_path = driver_.ComputeInstanceFieldInfo(field_idx, &unit_, field_offset,
is_volatile, is_put);
if (fast_path && !is_volatile && IsUint(16, field_offset)) {
- // TODO: use VLOG ?
- if (kEnableLogging) {
- LOG(INFO) << "Quickening " << Instruction::Name(inst->Opcode())
- << " to " << Instruction::Name(new_opcode)
- << " by replacing field index " << field_idx
- << " by field offset " << field_offset
- << " at dex pc " << StringPrintf("0x%x", dex_pc) << " in method "
- << PrettyMethod(unit_.GetDexMethodIndex(), GetDexFile(), true);
- }
+ VLOG(compiler) << "Quickening " << Instruction::Name(inst->Opcode())
+ << " to " << Instruction::Name(new_opcode)
+ << " by replacing field index " << field_idx
+ << " by field offset " << field_offset
+ << " at dex pc " << StringPrintf("0x%x", dex_pc) << " in method "
+ << PrettyMethod(unit_.GetDexMethodIndex(), GetDexFile(), true);
// We are modifying 4 consecutive bytes.
ScopedDexWriteAccess sdwa(GetModifiableDexFile(), inst, 4u);
inst->SetOpcode(new_opcode);
@@ -263,16 +297,13 @@ void DexCompiler::CompileInvokeVirtual(Instruction* inst,
// TODO: support devirtualization.
if (fast_path && original_invoke_type == invoke_type) {
if (vtable_idx >= 0 && IsUint(16, vtable_idx)) {
- // TODO: use VLOG ?
- if (kEnableLogging) {
- LOG(INFO) << "Quickening " << Instruction::Name(inst->Opcode())
- << "(" << PrettyMethod(method_idx, GetDexFile(), true) << ")"
- << " to " << Instruction::Name(new_opcode)
- << " by replacing method index " << method_idx
- << " by vtable index " << vtable_idx
- << " at dex pc " << StringPrintf("0x%x", dex_pc) << " in method "
- << PrettyMethod(unit_.GetDexMethodIndex(), GetDexFile(), true);
- }
+ VLOG(compiler) << "Quickening " << Instruction::Name(inst->Opcode())
+ << "(" << PrettyMethod(method_idx, GetDexFile(), true) << ")"
+ << " to " << Instruction::Name(new_opcode)
+ << " by replacing method index " << method_idx
+ << " by vtable index " << vtable_idx
+ << " at dex pc " << StringPrintf("0x%x", dex_pc) << " in method "
+ << PrettyMethod(unit_.GetDexMethodIndex(), GetDexFile(), true);
// We are modifying 4 consecutive bytes.
ScopedDexWriteAccess sdwa(GetModifiableDexFile(), inst, 4u);
inst->SetOpcode(new_opcode);
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc
index d1d21b1..67adae6 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -2264,7 +2264,7 @@ void CompilerDriver::CompileMethod(const DexFile::CodeItem* code_item, uint32_t
CompilerFn compiler = compiler_;
#ifdef ART_SEA_IR_MODE
bool use_sea = Runtime::Current()->IsSeaIRMode();
- use_sea &&= (std::string::npos != PrettyMethod(method_idx, dex_file).find("fibonacci"));
+ use_sea = use_sea && (std::string::npos != PrettyMethod(method_idx, dex_file).find("fibonacci"));
if (use_sea) {
compiler = sea_ir_compiler_;
}
diff --git a/compiler/sea_ir/sea.cc b/compiler/sea_ir/sea.cc
index 95c36e5..c5ec2b9 100644
--- a/compiler/sea_ir/sea.cc
+++ b/compiler/sea_ir/sea.cc
@@ -17,6 +17,8 @@
#include "sea.h"
#include "file_output_stream.h"
+#include "instruction_tools.h"
+
#define MAX_REACHING_DEF_ITERERATIONS (10)
@@ -31,6 +33,7 @@ SeaGraph* SeaGraph::GetCurrentGraph() {
}
void SeaGraph::DumpSea(std::string filename) const {
+ LOG(INFO) << "Starting to write SEA string to file.";
std::string result;
result += "digraph seaOfNodes {\n";
for (std::vector<Region*>::const_iterator cit = regions_.begin(); cit != regions_.end(); cit++) {
@@ -48,6 +51,97 @@ void SeaGraph::AddEdge(Region* src, Region* dst) const {
dst->AddPredecessor(src);
}
+void SeaGraph::ComputeRPO(Region* current_region, int& current_rpo) {
+ current_region->SetRPO(VISITING);
+ std::vector<sea_ir::Region*>* succs = current_region->GetSuccessors();
+ for (std::vector<sea_ir::Region*>::iterator succ_it = succs->begin();
+ succ_it != succs->end(); ++succ_it) {
+ if (NOT_VISITED == (*succ_it)->GetRPO()) {
+ SeaGraph::ComputeRPO(*succ_it, current_rpo);
+ }
+ }
+ current_region->SetRPO(current_rpo--);
+}
+
+void SeaGraph::ComputeIDominators() {
+ bool changed = true;
+ while (changed) {
+ changed = false;
+ // Entry node has itself as IDOM.
+ std::vector<Region*>::iterator crt_it;
+ std::set<Region*> processedNodes;
+ // Find and mark the entry node(s).
+ for (crt_it = regions_.begin(); crt_it != regions_.end(); ++crt_it) {
+ if ((*crt_it)->GetPredecessors()->size() == 0) {
+ processedNodes.insert(*crt_it);
+ (*crt_it)->SetIDominator(*crt_it);
+ }
+ }
+ for (crt_it = regions_.begin(); crt_it != regions_.end(); ++crt_it) {
+ if ((*crt_it)->GetPredecessors()->size() == 0) {
+ continue;
+ }
+ // NewIDom = first (processed) predecessor of b.
+ Region* new_dom = NULL;
+ std::vector<Region*>* preds = (*crt_it)->GetPredecessors();
+ DCHECK(NULL != preds);
+ Region* root_pred = NULL;
+ for (std::vector<Region*>::iterator pred_it = preds->begin();
+ pred_it != preds->end(); ++pred_it) {
+ if (processedNodes.end() != processedNodes.find((*pred_it))) {
+ root_pred = *pred_it;
+ new_dom = root_pred;
+ break;
+ }
+ }
+ // For all other predecessors p of b, if idom is not set,
+ // then NewIdom = Intersect(p, NewIdom)
+ for (std::vector<Region*>::const_iterator pred_it = preds->begin();
+ pred_it != preds->end(); ++pred_it) {
+ DCHECK(NULL != *pred_it);
+ // if IDOMS[p] != UNDEFINED
+ if ((*pred_it != root_pred) && (*pred_it)->GetIDominator() != NULL) {
+ DCHECK(NULL != new_dom);
+ new_dom = SeaGraph::Intersect(*pred_it, new_dom);
+ }
+ }
+ DCHECK(NULL != *crt_it);
+ if ((*crt_it)->GetIDominator() != new_dom) {
+ (*crt_it)->SetIDominator(new_dom);
+ changed = true;
+ }
+ processedNodes.insert(*crt_it);
+ }
+ }
+
+ // For easily ordering of regions we need edges dominator->dominated.
+ for (std::vector<Region*>::iterator region_it = regions_.begin();
+ region_it != regions_.end(); region_it++) {
+ Region* idom = (*region_it)->GetIDominator();
+ if (idom != *region_it) {
+ idom->AddToIDominatedSet(*region_it);
+ }
+ }
+}
+
+Region* SeaGraph::Intersect(Region* i, Region* j) {
+ Region* finger1 = i;
+ Region* finger2 = j;
+ while (finger1 != finger2) {
+ while (finger1->GetRPO() > finger2->GetRPO()) {
+ DCHECK(NULL != finger1);
+ finger1 = finger1->GetIDominator(); // should have: finger1 != NULL
+ DCHECK(NULL != finger1);
+ }
+ while (finger1->GetRPO() < finger2->GetRPO()) {
+ DCHECK(NULL != finger2);
+ finger2 = finger2->GetIDominator(); // should have: finger1 != NULL
+ DCHECK(NULL != finger2);
+ }
+ }
+ return finger1; // finger1 should be equal to finger2 at this point.
+}
+
void SeaGraph::ComputeDownExposedDefs() {
for (std::vector<Region*>::iterator region_it = regions_.begin();
region_it != regions_.end(); region_it++) {
@@ -74,39 +168,39 @@ void SeaGraph::ComputeReachingDefs() {
}
-void SeaGraph::CompileMethod(const art::DexFile::CodeItem* code_item,
- uint32_t class_def_idx, uint32_t method_idx, const art::DexFile& dex_file) {
+void SeaGraph::BuildMethodSeaGraph(const art::DexFile::CodeItem* code_item,
+ const art::DexFile& dex_file) {
const uint16_t* code = code_item->insns_;
const size_t size_in_code_units = code_item->insns_size_in_code_units_;
-
- Region* r = NULL;
- // This maps target instruction pointers to their corresponding region objects.
+ // This maps target instruction pointers to their corresponding region objects.
std::map<const uint16_t*, Region*> target_regions;
size_t i = 0;
-
// Pass: Find the start instruction of basic blocks
// by locating targets and flow-though instructions of branches.
while (i < size_in_code_units) {
const art::Instruction* inst = art::Instruction::At(&code[i]);
- if (inst->IsBranch()||inst->IsUnconditional()) {
+ if (inst->IsBranch() || inst->IsUnconditional()) {
int32_t offset = inst->GetTargetOffset();
- if (target_regions.end() == target_regions.find(&code[i+offset])) {
+ if (target_regions.end() == target_regions.find(&code[i + offset])) {
Region* region = GetNewRegion();
- target_regions.insert(std::pair<const uint16_t*, Region*>(&code[i+offset], region));
+ target_regions.insert(std::pair<const uint16_t*, Region*>(&code[i + offset], region));
}
- if (inst->CanFlowThrough() &&
- (target_regions.end() == target_regions.find(&code[i+inst->SizeInCodeUnits()]))) {
+ if (inst->CanFlowThrough()
+ && (target_regions.end() == target_regions.find(&code[i + inst->SizeInCodeUnits()]))) {
Region* region = GetNewRegion();
- target_regions.insert(std::pair<const uint16_t*, Region*>(&code[i+inst->SizeInCodeUnits()], region));
+ target_regions.insert(
+ std::pair<const uint16_t*, Region*>(&code[i + inst->SizeInCodeUnits()], region));
}
}
i += inst->SizeInCodeUnits();
}
-
// Pass: Assign instructions to region nodes and
// assign branches their control flow successors.
i = 0;
- r = GetNewRegion();
+ Region* r = GetNewRegion();
+ SignatureNode* parameter_def_node = new sea_ir::SignatureNode(code_item->registers_size_-1,
+ code_item->ins_size_);
+ r->AddChild(parameter_def_node);
sea_ir::InstructionNode* last_node = NULL;
sea_ir::InstructionNode* node = NULL;
while (i < size_in_code_units) {
@@ -116,7 +210,7 @@ void SeaGraph::CompileMethod(const art::DexFile::CodeItem* code_item,
if (inst->IsBranch() || inst->IsUnconditional()) {
int32_t offset = inst->GetTargetOffset();
- std::map<const uint16_t*, Region*>::iterator it = target_regions.find(&code[i+offset]);
+ std::map<const uint16_t*, Region*>::iterator it = target_regions.find(&code[i + offset]);
DCHECK(it != target_regions.end());
AddEdge(r, it->second); // Add edge to branch target.
}
@@ -125,24 +219,187 @@ void SeaGraph::CompileMethod(const art::DexFile::CodeItem* code_item,
if (target_regions.end() != it) {
// Get the already created region because this is a branch target.
Region* nextRegion = it->second;
- if (last_node->GetInstruction()->IsBranch() && last_node->GetInstruction()->CanFlowThrough()) {
+ if (last_node->GetInstruction()->IsBranch()
+ && last_node->GetInstruction()->CanFlowThrough()) {
AddEdge(r, it->second); // Add flow-through edge.
}
r = nextRegion;
}
- bool definesRegister = (0 !=
- InstructionTools::instruction_attributes_[inst->Opcode()] && (1 << kDA));
- LOG(INFO) << inst->GetDexPc(code) << "*** " << inst->DumpString(&dex_file)
- << " region:" <<r->StringId() << "Definition?" << definesRegister << std::endl;
+ bool definesRegister = (0 != InstructionTools::instruction_attributes_[inst->Opcode()]
+ && (1 << kDA));
+ LOG(INFO)<< inst->GetDexPc(code) << "*** " << inst->DumpString(&dex_file)
+ << " region:" <<r->StringId() << "Definition?" << definesRegister << std::endl;
r->AddChild(node);
i += inst->SizeInCodeUnits();
}
+}
+
+void SeaGraph::ComputeRPO() {
+ int rpo_id = regions_.size() - 1;
+ for (std::vector<Region*>::const_iterator crt_it = regions_.begin(); crt_it != regions_.end();
+ ++crt_it) {
+ if ((*crt_it)->GetPredecessors()->size() == 0) {
+ ComputeRPO(*crt_it, rpo_id);
+ }
+ }
+}
+
+// Performs the renaming phase in traditional SSA transformations.
+// See: Cooper & Torczon, "Engineering a Compiler", second edition, page 505.)
+void SeaGraph::RenameAsSSA() {
+ utils::ScopedHashtable<int, InstructionNode*> scoped_table;
+ scoped_table.OpenScope();
+ for (std::vector<Region*>::iterator region_it = regions_.begin(); region_it != regions_.end();
+ region_it++) {
+ if ((*region_it)->GetIDominator() == *region_it) {
+ RenameAsSSA(*region_it, &scoped_table);
+ }
+ }
+
+ scoped_table.CloseScope();
+}
+
+void SeaGraph::ConvertToSSA() {
+ // Pass: find global names.
+ // The map @block maps registers to the blocks in which they are defined.
+ std::map<int, std::set<Region*> > blocks;
+ // The set @globals records registers whose use
+ // is in a different block than the corresponding definition.
+ std::set<int> globals;
+ for (std::vector<Region*>::iterator region_it = regions_.begin(); region_it != regions_.end();
+ region_it++) {
+ std::set<int> var_kill;
+ std::vector<InstructionNode*>* instructions = (*region_it)->GetInstructions();
+ for (std::vector<InstructionNode*>::iterator inst_it = instructions->begin();
+ inst_it != instructions->end(); inst_it++) {
+ std::vector<int> used_regs = (*inst_it)->GetUses();
+ for (std::size_t i = 0; i < used_regs.size(); i++) {
+ int used_reg = used_regs[i];
+ if (var_kill.find(used_reg) == var_kill.end()) {
+ globals.insert(used_reg);
+ }
+ }
+ const int reg_def = (*inst_it)->GetResultRegister();
+ if (reg_def != NO_REGISTER) {
+ var_kill.insert(reg_def);
+ }
+
+ blocks.insert(std::pair<int, std::set<Region*> >(reg_def, std::set<Region*>()));
+ std::set<Region*>* reg_def_blocks = &(blocks.find(reg_def)->second);
+ reg_def_blocks->insert(*region_it);
+ }
+ }
+
+ // Pass: Actually add phi-nodes to regions.
+ for (std::set<int>::const_iterator globals_it = globals.begin();
+ globals_it != globals.end(); globals_it++) {
+ int global = *globals_it;
+ // Copy the set, because we will modify the worklist as we go.
+ std::set<Region*> worklist((*(blocks.find(global))).second);
+ for (std::set<Region*>::const_iterator b_it = worklist.begin(); b_it != worklist.end(); b_it++) {
+ std::set<Region*>* df = (*b_it)->GetDominanceFrontier();
+ for (std::set<Region*>::const_iterator df_it = df->begin(); df_it != df->end(); df_it++) {
+ if ((*df_it)->InsertPhiFor(global)) {
+ // Check that the dominance frontier element is in the worklist already
+ // because we only want to break if the element is actually not there yet.
+ if (worklist.find(*df_it) == worklist.end()) {
+ worklist.insert(*df_it);
+ b_it = worklist.begin();
+ break;
+ }
+ }
+ }
+ }
+ }
+ // Pass: Build edges to the definition corresponding to each use.
+ // (This corresponds to the renaming phase in traditional SSA transformations.
+ // See: Cooper & Torczon, "Engineering a Compiler", second edition, page 505.)
+ RenameAsSSA();
+}
+
+void SeaGraph::RenameAsSSA(Region* crt_region,
+ utils::ScopedHashtable<int, InstructionNode*>* scoped_table) {
+ scoped_table->OpenScope();
+ // Rename phi nodes defined in the current region.
+ std::vector<PhiInstructionNode*>* phis = crt_region->GetPhiNodes();
+ for (std::vector<PhiInstructionNode*>::iterator phi_it = phis->begin();
+ phi_it != phis->end(); phi_it++) {
+ int reg_no = (*phi_it)->GetRegisterNumber();
+ scoped_table->Add(reg_no, (*phi_it));
+ }
+ // Rename operands of instructions from the current region.
+ std::vector<InstructionNode*>* instructions = crt_region->GetInstructions();
+ for (std::vector<InstructionNode*>::const_iterator instructions_it = instructions->begin();
+ instructions_it != instructions->end(); instructions_it++) {
+ InstructionNode* current_instruction = (*instructions_it);
+ // Rename uses.
+ std::vector<int> used_regs = current_instruction->GetUses();
+ for (std::vector<int>::const_iterator reg_it = used_regs.begin();
+ reg_it != used_regs.end(); reg_it++) {
+ int current_used_reg = (*reg_it);
+ InstructionNode* definition = scoped_table->Lookup(current_used_reg);
+ current_instruction->RenameToSSA(current_used_reg, definition);
+ }
+ // Update scope table with latest definitions.
+ std::vector<int> def_regs = current_instruction->GetDefinitions();
+ for (std::vector<int>::const_iterator reg_it = def_regs.begin();
+ reg_it != def_regs.end(); reg_it++) {
+ int current_defined_reg = (*reg_it);
+ scoped_table->Add(current_defined_reg, current_instruction);
+ }
+ }
+ // Fill in uses of phi functions in CFG successor regions.
+ const std::vector<Region*>* successors = crt_region->GetSuccessors();
+ for (std::vector<Region*>::const_iterator successors_it = successors->begin();
+ successors_it != successors->end(); successors_it++) {
+ Region* successor = (*successors_it);
+ successor->SetPhiDefinitionsForUses(scoped_table, crt_region);
+ }
+
+ // Rename all successors in the dominators tree.
+ const std::set<Region*>* dominated_nodes = crt_region->GetIDominatedSet();
+ for (std::set<Region*>::const_iterator dominated_nodes_it = dominated_nodes->begin();
+ dominated_nodes_it != dominated_nodes->end(); dominated_nodes_it++) {
+ Region* dominated_node = (*dominated_nodes_it);
+ RenameAsSSA(dominated_node, scoped_table);
+ }
+ scoped_table->CloseScope();
+}
+void SeaGraph::CompileMethod(const art::DexFile::CodeItem* code_item,
+ uint32_t class_def_idx, uint32_t method_idx, const art::DexFile& dex_file) {
+ // Two passes: Builds the intermediate structure (non-SSA) of the sea-ir for the function.
+ BuildMethodSeaGraph(code_item, dex_file);
+ //Pass: Compute reverse post-order of regions.
+ ComputeRPO();
+ // Multiple passes: compute immediate dominators.
+ ComputeIDominators();
// Pass: compute downward-exposed definitions.
ComputeDownExposedDefs();
-
- // Multiple Passes: Compute reaching definitions (iterative fixed-point algorithm)
+ // Multiple Passes (iterative fixed-point algorithm): Compute reaching definitions
ComputeReachingDefs();
+ // Pass (O(nlogN)): Compute the dominance frontier for region nodes.
+ ComputeDominanceFrontier();
+ // Two Passes: Phi node insertion.
+ ConvertToSSA();
+}
+
+
+void SeaGraph::ComputeDominanceFrontier() {
+ for (std::vector<Region*>::iterator region_it = regions_.begin();
+ region_it != regions_.end(); region_it++) {
+ std::vector<Region*>* preds = (*region_it)->GetPredecessors();
+ if (preds->size() > 1) {
+ for (std::vector<Region*>::iterator pred_it = preds->begin();
+ pred_it != preds->end(); pred_it++) {
+ Region* runner = *pred_it;
+ while (runner != (*region_it)->GetIDominator()) {
+ runner->AddToDominanceFrontier(*region_it);
+ runner = runner->GetIDominator();
+ }
+ }
+ }
+ }
}
Region* SeaGraph::GetNewRegion() {
@@ -156,6 +413,17 @@ void SeaGraph::AddRegion(Region* r) {
regions_.push_back(r);
}
+void SeaNode::AddSuccessor(Region* successor) {
+ DCHECK(successor) << "Tried to add NULL successor to SEA node.";
+ successors_.push_back(successor);
+ return;
+}
+
+void SeaNode::AddPredecessor(Region* predecessor) {
+ DCHECK(predecessor) << "Tried to add NULL predecessor to SEA node.";
+ predecessors_.push_back(predecessor);
+}
+
void Region::AddChild(sea_ir::InstructionNode* instruction) {
DCHECK(instruction) << "Tried to add NULL instruction to region node.";
instructions_.push_back(instruction);
@@ -168,33 +436,28 @@ SeaNode* Region::GetLastChild() const {
return NULL;
}
-void InstructionNode::ToDot(std::string& result) const {
- result += "// Instruction: \n" + StringId() +
- " [label=\"" + instruction_->DumpString(NULL) + "\"";
- if (de_def_) {
- result += "style=bold";
+void Region::ToDot(std::string& result) const {
+ result += "\n// Region: \n" + StringId() + " [label=\"region " + StringId() + "(rpo=";
+ std::stringstream ss;
+ ss << rpo_;
+ result.append(ss.str());
+ if (NULL != GetIDominator()) {
+ result += " dom=" + GetIDominator()->StringId();
}
- result += "];\n";
-}
+ result += ")\"];\n";
-int InstructionNode::GetResultRegister() const {
- if (!InstructionTools::IsDefinition(instruction_)) {
- return NO_REGISTER;
+ // 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";
}
- return instruction_->VRegA();
-}
-
-void InstructionNode::MarkAsDEDef() {
- de_def_ = true;
-}
-void Region::ToDot(std::string& result) const {
- result += "\n// Region: \n" + StringId() + " [label=\"region " + StringId() + "\"];";
- // Save instruction nodes that belong to this region.
+ // Save instruction nodes.
for (std::vector<InstructionNode*>::const_iterator cit = instructions_.begin();
cit != instructions_.end(); cit++) {
(*cit)->ToDot(result);
- result += StringId() + " -> " + (*cit)->StringId() + ";\n";
+ result += StringId() + " -> " + (*cit)->StringId() + "; // region -> instruction \n";
}
for (std::vector<Region*>::const_iterator cit = successors_.begin(); cit != successors_.end();
@@ -202,7 +465,6 @@ void Region::ToDot(std::string& result) const {
DCHECK(NULL != *cit) << "Null successor found for SeaNode" << GetLastChild()->StringId() << ".";
result += GetLastChild()->StringId() + " -> " + (*cit)->StringId() + ";\n\n";
}
-
// Save reaching definitions.
for (std::map<int, std::set<sea_ir::InstructionNode*>* >::const_iterator cit =
reaching_defs_.begin();
@@ -216,11 +478,15 @@ void Region::ToDot(std::string& result) const {
" [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() {
for (std::vector<InstructionNode*>::const_iterator inst_it = instructions_.begin();
inst_it != instructions_.end(); inst_it++) {
@@ -232,14 +498,12 @@ void Region::ComputeDownExposedDefs() {
res->second = *inst_it;
}
}
-
for (std::map<int, sea_ir::InstructionNode*>::const_iterator cit = de_defs_.begin();
cit != de_defs_.end(); cit++) {
(*cit).second->MarkAsDEDef();
}
}
-
const std::map<int, sea_ir::InstructionNode*>* Region::GetDownExposedDefs() const {
return &de_defs_;
}
@@ -268,7 +532,6 @@ bool Region::UpdateReachingDefs() {
reaching_defs.insert(
std::pair<int const, std::set<InstructionNode*>*>(de_def->first, solo_def));
}
- LOG(INFO) << "Adding to " <<StringId() << "reaching set of " << (*pred_it)->StringId();
reaching_defs.insert(pred_reaching->begin(), pred_reaching->end());
// Now we combine the reaching map coming from the current predecessor (reaching_defs)
@@ -315,15 +578,124 @@ bool Region::UpdateReachingDefs() {
return changed;
}
-void SeaNode::AddSuccessor(Region* successor) {
- DCHECK(successor) << "Tried to add NULL successor to SEA node.";
- successors_.push_back(successor);
- return;
+bool Region::InsertPhiFor(int reg_no) {
+ if (!ContainsPhiFor(reg_no)) {
+ phi_set_.insert(reg_no);
+ PhiInstructionNode* new_phi = new PhiInstructionNode(reg_no);
+ phi_instructions_.push_back(new_phi);
+ return true;
+ }
+ return false;
}
-void SeaNode::AddPredecessor(Region* predecessor) {
- DCHECK(predecessor) << "Tried to add NULL predecessor to SEA node.";
- predecessors_.push_back(predecessor);
+void Region::SetPhiDefinitionsForUses(
+ const utils::ScopedHashtable<int, InstructionNode*>* scoped_table, Region* predecessor) {
+ int predecessor_id = -1;
+ for (unsigned int crt_pred_id = 0; crt_pred_id < predecessors_.size(); crt_pred_id++) {
+ if (predecessors_.at(crt_pred_id) == predecessor) {
+ predecessor_id = crt_pred_id;
+ }
+ }
+ DCHECK_NE(-1, predecessor_id);
+ for (std::vector<PhiInstructionNode*>::iterator phi_it = phi_instructions_.begin();
+ phi_it != phi_instructions_.end(); phi_it++) {
+ PhiInstructionNode* phi = (*phi_it);
+ int reg_no = phi->GetRegisterNumber();
+ InstructionNode* definition = scoped_table->Lookup(reg_no);
+ phi->RenameToSSA(reg_no, definition, predecessor_id);
+ }
}
+void InstructionNode::ToDot(std::string& result) const {
+ result += "// Instruction: \n" + StringId() +
+ " [label=\"" + instruction_->DumpString(NULL) + "\"";
+ if (de_def_) {
+ result += "style=bold";
+ }
+ result += "];\n";
+ // SSA definitions:
+ 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";
+ }
+ }
+}
+
+void InstructionNode::MarkAsDEDef() {
+ de_def_ = true;
+}
+
+int InstructionNode::GetResultRegister() const {
+ if (instruction_->HasVRegA()) {
+ return instruction_->VRegA();
+ }
+ return NO_REGISTER;
+}
+
+std::vector<int> InstructionNode::GetDefinitions() const {
+ // TODO: Extend this to handle instructions defining more than one register (if any)
+ // The return value should be changed to pointer to field then; for now it is an object
+ // so that we avoid possible memory leaks from allocating objects dynamically.
+ std::vector<int> definitions;
+ int result = GetResultRegister();
+ if (NO_REGISTER != result) {
+ definitions.push_back(result);
+ }
+ return definitions;
+}
+
+std::vector<int> InstructionNode::GetUses() {
+ std::vector<int> uses; // Using vector<> instead of set<> because order matters.
+
+ if (!InstructionTools::IsDefinition(instruction_) && (instruction_->HasVRegA())) {
+ int vA = instruction_->VRegA();
+ uses.push_back(vA);
+ }
+ if (instruction_->HasVRegB()) {
+ int vB = instruction_->VRegB();
+ uses.push_back(vB);
+ }
+ if (instruction_->HasVRegC()) {
+ int vC = instruction_->VRegC();
+ uses.push_back(vC);
+ }
+ // TODO: Add support for function argument registers.
+ return uses;
+}
+
+void PhiInstructionNode::ToDot(std::string& result) const {
+ result += "// PhiInstruction: \n" + StringId() +
+ " [label=\"" + "PHI(";
+ std::stringstream phi_reg_stream;
+ phi_reg_stream << register_no_;
+ result.append(phi_reg_stream.str());
+ result += ")\"";
+ result += "];\n";
+
+ for (std::vector<std::map<int, InstructionNode*>*>::const_iterator pred_it = definition_edges_.begin();
+ pred_it != definition_edges_.end(); pred_it++) {
+ std::map<int, InstructionNode*>* defs_from_pred = *pred_it;
+ for (std::map<int, InstructionNode* >::const_iterator def_it = defs_from_pred->begin();
+ def_it != defs_from_pred->end(); def_it++) {
+ if (NULL != def_it->second) {
+ result += def_it->second->StringId() + " -> " + StringId() +"[color=red,label=\"vR = ";
+ std::stringstream ss;
+ ss << def_it->first;
+ result.append(ss.str());
+ result += "\"] ; // phi-ssa edge\n";
+ } else {
+ result += StringId() + " -> " + StringId() +"[color=blue,label=\"vR = ";
+ std::stringstream ss;
+ ss << def_it->first;
+ result.append(ss.str());
+ result += "\"] ; // empty phi-ssa edge\n";
+ }
+ }
+ }
+}
} // end namespace sea_ir
diff --git a/compiler/sea_ir/sea.h b/compiler/sea_ir/sea.h
index ce4624d..7491d21 100644
--- a/compiler/sea_ir/sea.h
+++ b/compiler/sea_ir/sea.h
@@ -24,11 +24,22 @@
#include "dex_file.h"
#include "dex_instruction.h"
#include "sea_ir/instruction_tools.h"
+#include "utils/scoped_hashtable.h"
-#define NO_REGISTER (-1)
namespace sea_ir {
+
+#define NO_REGISTER (-1)
+
+// Reverse post-order numbering constants
+enum RegionNumbering {
+ NOT_VISITED = -1,
+ VISITING = -2
+};
+
class Region;
+class InstructionNode;
+class PhiInstructionNode;
class SeaNode {
public:
@@ -37,16 +48,20 @@ class SeaNode {
ss << id_;
string_id_.append(ss.str());
}
-
// Adds CFG predecessors and successors to each block.
void AddSuccessor(Region* successor);
void AddPredecessor(Region* predecesor);
+ std::vector<sea_ir::Region*>* GetSuccessors() {
+ return &successors_;
+ }
+ std::vector<sea_ir::Region*>* GetPredecessors() {
+ return &predecessors_;
+ }
// Returns the id of the current block as string
const std::string& StringId() const {
return string_id_;
}
-
// 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 ("}").
@@ -70,93 +85,252 @@ class SeaNode {
class InstructionNode: public SeaNode {
public:
- explicit InstructionNode(const art::Instruction* in):SeaNode(), instruction_(in), de_def_(false) {}
-
+ explicit InstructionNode(const art::Instruction* in):
+ SeaNode(), instruction_(in), de_def_(false) {}
+ // Returns the Dalvik instruction around which this InstructionNode is wrapped.
const art::Instruction* GetInstruction() const {
DCHECK(NULL != instruction_) << "Tried to access NULL instruction in an InstructionNode.";
return instruction_;
}
// Returns the register that is defined by the current instruction, or NO_REGISTER otherwise.
- int GetResultRegister() const;
+ virtual int GetResultRegister() const;
+ // Returns the set of registers defined by the current instruction.
+ virtual std::vector<int> GetDefinitions() const;
+ // 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.
void ToDot(std::string& result) const;
+ // Mark the current instruction as a dowward exposed definition.
void MarkAsDEDef();
+ // Rename the use of @reg_no to refer to the instruction @definition,
+ // essentially creating SSA form.
+ void RenameToSSA(int reg_no, InstructionNode* definition) {
+ definition_edges_.insert(std::pair<int, InstructionNode*>(reg_no, definition));
+ }
private:
const art::Instruction* const instruction_;
+ std::map<int, InstructionNode* > definition_edges_;
bool de_def_;
};
+class SignatureNode: public InstructionNode {
+ public:
+ explicit SignatureNode(unsigned int start_register, unsigned int count):
+ InstructionNode(NULL), defined_regs_() {
+ for (unsigned int crt_offset = 0; crt_offset < count; crt_offset++) {
+ defined_regs_.push_back(start_register - crt_offset);
+ }
+ }
+
+ void ToDot(std::string& result) const {
+ result += StringId() +"[label=\"signature:";
+ std::stringstream vector_printer;
+ if (!defined_regs_.empty()) {
+ for (unsigned int crt_el = 0; crt_el < defined_regs_.size()-1; crt_el++) {
+ vector_printer << defined_regs_[crt_el] <<",";
+ }
+ vector_printer << defined_regs_[defined_regs_.size()-1] <<";";
+ }
+ result += "\"] // signature node\n";
+ }
+
+ std::vector<int> GetDefinitions() const {
+ return defined_regs_;
+ }
+
+ int GetResultRegister() const {
+ return NO_REGISTER;
+ }
+
+ std::vector<int> GetUses() {
+ return std::vector<int>();
+ }
+
+ private:
+ std::vector<int> defined_regs_;
+};
+
+class PhiInstructionNode: public InstructionNode {
+ public:
+ 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;
+ // Returns the register on which this phi-function is used.
+ int GetRegisterNumber() {
+ return register_no_;
+ }
+
+ // Rename the use of @reg_no to refer to the instruction @definition.
+ // Phi-functions are different than normal instructions in that they
+ // have multiple predecessor regions; this is why RenameToSSA has
+ // the additional parameter specifying that @parameter_id is the incoming
+ // edge for @definition, essentially creating SSA form.
+ void RenameToSSA(int reg_no, InstructionNode* definition, unsigned int predecessor_id) {
+ DCHECK(NULL != definition) << "Tried to rename to SSA using a NULL definition for "
+ << StringId() << " register " << reg_no;
+ if (definition_edges_.size() < predecessor_id+1) {
+ definition_edges_.resize(predecessor_id+1, NULL);
+ }
+
+ if (NULL == definition_edges_.at(predecessor_id)) {
+ definition_edges_[predecessor_id] = new std::map<int, InstructionNode*>();
+ }
+ definition_edges_[predecessor_id]->insert(std::pair<int, InstructionNode*>(reg_no, definition));
+ }
+
+ private:
+ int register_no_;
+ std::vector<std::map<int, InstructionNode*>*> definition_edges_;
+};
class Region : public SeaNode {
public:
- explicit Region():SeaNode(), reaching_defs_size_(-1) {}
+ explicit Region():
+ SeaNode(), reaching_defs_size_(0), rpo_(NOT_VISITED), idom_(NULL),
+ idominated_set_(), df_(), phi_set_() {}
- // Adds @inst as an instruction node child in the current region.
- void AddChild(sea_ir::InstructionNode* inst);
+ // Adds @instruction as an instruction node child in the current region.
+ void AddChild(sea_ir::InstructionNode* insttruction);
// Returns the last instruction node child of the current region.
// This child has the CFG successors pointing to the new regions.
SeaNode* GetLastChild() const;
-
+ // Returns all the child instructions of this region, in program order.
+ std::vector<InstructionNode*>* GetInstructions() {
+ return &instructions_;
+ }
// 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;
-
// Computes Downward Exposed Definitions for the current node.
+
void ComputeDownExposedDefs();
const std::map<int, sea_ir::InstructionNode*>* GetDownExposedDefs() const;
// Performs one iteration of the reaching definitions algorithm
// and returns true if the reaching definitions set changed.
bool UpdateReachingDefs();
-
// Returns the set of reaching definitions for the current region.
std::map<int, std::set<sea_ir::InstructionNode*>* >* GetReachingDefs();
+ void SetRPO(int rpo) {
+ rpo_ = rpo;
+ }
+
+ int GetRPO() {
+ return rpo_;
+ }
+
+ void SetIDominator(Region* dom) {
+ idom_ = dom;
+ }
+
+ Region* GetIDominator() const {
+ return idom_;
+ }
+
+ void AddToIDominatedSet(Region* dominated) {
+ idominated_set_.insert(dominated);
+ }
+
+ const std::set<Region*>* GetIDominatedSet() {
+ return &idominated_set_;
+ }
+
+ // Adds @df_reg to the dominance frontier of the current region.
+ void AddToDominanceFrontier(Region* df_reg) {
+ df_.insert(df_reg);
+ }
+ // Returns the dominance frontier of the current region.
+ // Preconditions: SeaGraph.ComputeDominanceFrontier()
+ std::set<Region*>* GetDominanceFrontier() {
+ return &df_;
+ }
+ // Returns true if the region contains a phi function for @reg_no.
+ bool ContainsPhiFor(int reg_no) {
+ return (phi_set_.end() != phi_set_.find(reg_no));
+ }
+ // Returns the phi-functions from the region.
+ std::vector<PhiInstructionNode*>* GetPhiNodes() {
+ return &phi_instructions_;
+ }
+ // Adds a phi-function for @reg_no to this region.
+ // Note: The insertion order does not matter, as phi-functions
+ // are conceptually executed at the same time.
+ bool InsertPhiFor(int reg_no);
+ // Sets the phi-function uses to be as defined in @scoped_table for predecessor @@predecessor.
+ void SetPhiDefinitionsForUses(const utils::ScopedHashtable<int, InstructionNode*>* scoped_table,
+ Region* predecessor);
+
private:
std::vector<sea_ir::InstructionNode*> instructions_;
std::map<int, sea_ir::InstructionNode*> de_defs_;
std::map<int, std::set<sea_ir::InstructionNode*>* > reaching_defs_;
int reaching_defs_size_;
+ int rpo_;
+ // Immediate dominator node.
+ Region* idom_;
+ // The set of nodes immediately dominated by the region.
+ std::set<Region*> idominated_set_;
+ // Records the dominance frontier.
+ std::set<Region*> df_;
+ // Records the set of register numbers that have phi nodes in this region.
+ std::set<int> phi_set_;
+ std::vector<PhiInstructionNode*> phi_instructions_;
};
-
-
class SeaGraph {
public:
static SeaGraph* GetCurrentGraph();
+
void CompileMethod(const art::DexFile::CodeItem* code_item,
uint32_t class_def_idx, uint32_t method_idx, const art::DexFile& dex_file);
-
- // Returns a string representation of the region and its Instruction children
+ // Returns a string representation of the region and its Instruction children.
void DumpSea(std::string filename) const;
+ // Recursively computes the reverse postorder value for @crt_bb and successors.
+ static void ComputeRPO(Region* crt_bb, int& crt_rpo);
+ // Returns the "lowest common ancestor" of @i and @j in the dominator tree.
+ static Region* Intersect(Region* i, Region* j);
+ private:
+ // Registers @childReg as a region belonging to the SeaGraph instance.
+ void AddRegion(Region* childReg);
+ // Returns new region and registers it with the SeaGraph instance.
+ Region* GetNewRegion();
// Adds a CFG edge from @src node to @dst node.
void AddEdge(Region* src, Region* dst) const;
-
+ // Builds the non-SSA sea-ir representation of the function @code_item from @dex_file.
+ void BuildMethodSeaGraph(const art::DexFile::CodeItem* code_item, const art::DexFile& dex_file);
+ // Computes immediate dominators for each region.
+ // Precondition: ComputeMethodSeaGraph()
+ void ComputeIDominators();
// Computes Downward Exposed Definitions for all regions in the graph.
void ComputeDownExposedDefs();
-
// Computes the reaching definitions set following the equations from
- // Cooper & Torczon, "Engineering a Compiler", second edition, page 491
+ // Cooper & Torczon, "Engineering a Compiler", second edition, page 491.
+ // Precondition: ComputeDEDefs()
void ComputeReachingDefs();
+ // Computes the reverse-postorder numbering for the region nodes.
+ // Precondition: ComputeDEDefs()
+ void ComputeRPO();
+ // Computes the dominance frontier for all regions in the graph,
+ // following the algorithm from
+ // Cooper & Torczon, "Engineering a Compiler", second edition, page 499.
+ // Precondition: ComputeIDominators()
+ void ComputeDominanceFrontier();
+
+ void ConvertToSSA();
+ // Identifies the definitions corresponding to uses for region @node
+ // by using the scoped hashtable of names @ scoped_table.
+ void RenameAsSSA(Region* node, utils::ScopedHashtable<int, InstructionNode*>* scoped_table);
+ void RenameAsSSA();
- /*** Static helper functions follow: ***/
- static int ParseInstruction(const uint16_t* code_ptr,
- art::DecodedInstruction* decoded_instruction);
- static bool IsInstruction(const uint16_t* code_ptr);
-
- private:
- // Registers the parameter as a child region of the SeaGraph instance
- void AddRegion(Region* r);
- // Returns new region and registers it with the SeaGraph instance
- Region* GetNewRegion();
static SeaGraph graph_;
std::vector<Region*> regions_;
};
-
-
} // end namespace sea_ir
#endif // ART_COMPILER_SEA_IR_SEA_H_
diff --git a/compiler/utils/scoped_hashtable.h b/compiler/utils/scoped_hashtable.h
new file mode 100644
index 0000000..5e6c64b
--- /dev/null
+++ b/compiler/utils/scoped_hashtable.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stddef.h>
+#include <map>
+#include <list>
+
+#ifndef SCOPED_HASHTABLE_
+#define SCOPED_HASHTABLE_
+
+namespace utils {
+template <typename K, typename V>
+class ScopedHashtable {
+ public:
+ explicit ScopedHashtable():scopes() {
+ }
+
+ void OpenScope() {
+ scopes.push_front(std::map<K, V>());
+ }
+
+ // Lookups entry K starting from the current (topmost) scope
+ // and returns its value if found or NULL.
+ V Lookup(K k) const {
+ for (typename std::list<std::map<K, V> >::const_iterator scopes_it = scopes.begin();
+ scopes_it != scopes.end(); scopes_it++) {
+ typename std::map<K, V>::const_iterator result_it = (*scopes_it).find(k);
+ if (result_it != (*scopes_it).end()) {
+ return (*result_it).second;
+ }
+ }
+ return NULL;
+ }
+
+ // Adds a new entry in the current (topmost) scope.
+ void Add(K k, V v) {
+ scopes.front().erase(k);
+ scopes.front().insert(std::pair< K, V >(k, v));
+ }
+
+ // Removes the topmost scope.
+ bool CloseScope() {
+ // Added check to uniformly handle undefined behavior
+ // when removing scope and the list of scopes is empty.
+ if (scopes.size() > 0) {
+ scopes.pop_front();
+ return true;
+ }
+ return false;
+ }
+
+ private:
+ std::list<std::map<K, V> > scopes;
+};
+} // end namespace utils
+
+#endif
diff --git a/compiler/utils/scoped_hashtable_test.cc b/compiler/utils/scoped_hashtable_test.cc
new file mode 100644
index 0000000..072da8c
--- /dev/null
+++ b/compiler/utils/scoped_hashtable_test.cc
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "common_test.h"
+#include "scoped_hashtable.h"
+
+using utils::ScopedHashtable;
+
+namespace art {
+
+class Value {
+ public:
+ explicit Value(int v):value_(v) {}
+ int value_;
+};
+
+class ScopedHashtableTest : public CommonTest {
+};
+
+TEST_F(ScopedHashtableTest, Basics) {
+ ScopedHashtable<int, Value*> sht;
+ // Check table is empty when no scope is open.
+ EXPECT_TRUE(NULL == sht.Lookup(1));
+
+ // Check table is empty when scope open.
+ sht.OpenScope();
+ EXPECT_TRUE(NULL == sht.Lookup(1));
+ // Check table is empty after closing scope.
+ EXPECT_EQ(sht.CloseScope(), true);
+ // Check closing scope on empty table is no-op.
+ EXPECT_EQ(sht.CloseScope(), false);
+ // Check that find in current scope works.
+ sht.OpenScope();
+ sht.Add(1, new Value(1));
+ EXPECT_EQ(sht.Lookup(1)->value_, 1);
+ // Check that updating values in current scope works.
+ sht.Add(1, new Value(2));
+ EXPECT_EQ(sht.Lookup(1)->value_, 2);
+ // Check that find works in previous scope.
+ sht.OpenScope();
+ EXPECT_EQ(sht.Lookup(1)->value_, 2);
+ // Check that shadowing scopes works.
+ sht.Add(1, new Value(3));
+ EXPECT_EQ(sht.Lookup(1)->value_, 3);
+ // Check that having multiple keys work correctly.
+ sht.Add(2, new Value(4));
+ EXPECT_EQ(sht.Lookup(1)->value_, 3);
+ EXPECT_EQ(sht.Lookup(2)->value_, 4);
+ // Check that scope removal works corectly.
+ sht.CloseScope();
+ EXPECT_EQ(sht.Lookup(1)->value_, 2);
+ EXPECT_TRUE(NULL == sht.Lookup(2));
+}
+
+} // end namespace art
diff --git a/runtime/atomic_integer.h b/runtime/atomic_integer.h
index 117e837..3492487 100644
--- a/runtime/atomic_integer.h
+++ b/runtime/atomic_integer.h
@@ -29,47 +29,51 @@ class AtomicInteger {
explicit AtomicInteger(int32_t value) : value_(value) { }
// Unsafe = operator for non atomic operations on the integer.
- AtomicInteger& operator = (int32_t new_value) {
- value_ = new_value;
+ void store(int32_t desired) {
+ value_ = desired;
+ }
+
+ AtomicInteger& operator=(int32_t desired) {
+ store(desired);
return *this;
}
- operator int32_t () const {
+ int32_t load() const {
return value_;
}
- int32_t get() const {
- return value_;
+ operator int32_t() const {
+ return load();
}
- int32_t operator += (const int32_t value) {
+ int32_t fetch_add(const int32_t value) {
return android_atomic_add(value, &value_);
}
- int32_t operator -= (const int32_t value) {
+ int32_t fetch_sub(const int32_t value) {
return android_atomic_add(-value, &value_);
}
- int32_t operator |= (const int32_t value) {
- return android_atomic_or(value, &value_);
+ int32_t operator++() {
+ return android_atomic_inc(&value_) + 1;
}
- int32_t operator &= (const int32_t value) {
- return android_atomic_and(-value, &value_);
+ int32_t operator++(int32_t) {
+ return android_atomic_inc(&value_);
}
- int32_t operator ++ () {
- return android_atomic_inc(&value_) + 1;
+ int32_t operator--() {
+ return android_atomic_dec(&value_) - 1;
}
- int32_t operator -- () {
- return android_atomic_dec(&value_) - 1;
+ int32_t operator--(int32_t) {
+ return android_atomic_dec(&value_);
}
- bool CompareAndSwap(int expected_value, int new_value) {
- bool success = android_atomic_cas(expected_value, new_value, &value_) == 0;
- return success;
+ bool compare_and_swap(int32_t expected_value, int32_t desired_value) {
+ return android_atomic_cas(expected_value, desired_value, &value_) == 0;
}
+
private:
volatile int32_t value_;
};
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 @@ namespace art {
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/class_linker.cc b/runtime/class_linker.cc
index fbceb59..7cf6cd9 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -2301,12 +2301,25 @@ void ClassLinker::VerifyClass(mirror::Class* klass) {
bool ClassLinker::VerifyClassUsingOatFile(const DexFile& dex_file, mirror::Class* klass,
mirror::Class::Status& oat_file_class_status) {
- if (!Runtime::Current()->IsStarted()) {
- return false;
- }
- if (Runtime::Current()->UseCompileTimeClassPath()) {
- return false;
+
+ // If we're compiling, we can only verify the class using the oat file if
+ // we are not compiling the image or if the class we're verifying is not part of
+ // the app. In other words, we will only check for preverification of bootclasspath
+ // classes.
+ if (Runtime::Current()->IsCompiler()) {
+ // Are we compiling the bootclasspath?
+ if (!Runtime::Current()->UseCompileTimeClassPath()) {
+ return false;
+ }
+ // We are compiling an app (not the image).
+
+ // Is this an app class? (I.e. not a bootclasspath class)
+ if (klass->GetClassLoader() != NULL) {
+ return false;
+ }
}
+
+
const OatFile* oat_file = FindOpenedOatFileForDexFile(dex_file);
CHECK(oat_file != NULL) << dex_file.GetLocation() << " " << PrettyClass(klass);
const OatFile::OatDexFile* oat_dex_file = oat_file->GetOatDexFile(dex_file.GetLocation());
@@ -2321,7 +2334,7 @@ bool ClassLinker::VerifyClassUsingOatFile(const DexFile& dex_file, mirror::Class
oat_file_class_status = oat_class->GetStatus();
if (oat_file_class_status == mirror::Class::kStatusVerified ||
oat_file_class_status == mirror::Class::kStatusInitialized) {
- return true;
+ return true;
}
if (oat_file_class_status == mirror::Class::kStatusRetryVerificationAtRuntime) {
// Compile time verification failed with a soft error. Compile time verification can fail
diff --git a/runtime/dex_instruction.cc b/runtime/dex_instruction.cc
index 6527f10..427baf2 100644
--- a/runtime/dex_instruction.cc
+++ b/runtime/dex_instruction.cc
@@ -82,54 +82,84 @@ static inline uint32_t fetch_uint32_impl(uint32_t offset, const uint16_t* insns)
return insns[offset] | ((uint32_t) insns[offset+1] << 16);
}
+
+bool Instruction::HasVRegC() const {
+ switch (FormatOf(Opcode())) {
+ case k23x: return true;
+ case k35c: return true;
+ case k3rc: return true;
+ default: return false;
+ }
+}
+
+bool Instruction::HasVRegB() const {
+ switch (FormatOf(Opcode())) {
+ case k12x: return true;
+ case k22b: return true;
+ case k22c: return true;
+ case k22s: return true;
+ case k22t: return true;
+ case k22x: return true;
+ case k32x: return true;
+ default: return false;
+ }
+}
+
+bool Instruction::HasVRegA() const {
+ switch (FormatOf(Opcode())) {
+ case k11n: return true;
+ case k11x: return true;
+ case k12x: return true;
+ case k21c: return true;
+ case k21h: return true;
+ case k21s: return true;
+ case k21t: return true;
+ case k22b: return true;
+ case k22c: return true;
+ case k22s: return true;
+ case k22t: return true;
+ case k22x: return true;
+ case k23x: return true;
+ case k31c: return true;
+ case k31i: return true;
+ case k31t: return true;
+ case k32x: return true;
+ case k51l: return true;
+ default: return false;
+ }
+}
+
int32_t Instruction::VRegC() const {
switch (FormatOf(Opcode())) {
- case k22b: return VRegC_22b();
- case k22c: return VRegC_22c();
- case k22s: return VRegC_22s();
- case k22t: return VRegC_22t();
case k23x: return VRegC_23x();
case k35c: return VRegC_35c();
case k3rc: return VRegC_3rc();
default: LOG(FATAL) << "Tried to access vC of instruction " << Name() <<
" which has no C operand.";
}
- return 0;
+ return -1;
}
int32_t Instruction::VRegB() const {
switch (FormatOf(Opcode())) {
- case k11n: return VRegB_11n();
case k12x: return VRegB_12x();
- case k21c: return VRegB_21c();
- case k21h: return VRegB_21h();
- case k21t: return VRegB_21t();
case k22b: return VRegB_22b();
case k22c: return VRegB_22c();
case k22s: return VRegB_22s();
case k22t: return VRegB_22t();
case k22x: return VRegB_22x();
- case k31c: return VRegB_31c();
- case k31i: return VRegB_31i();
- case k31t: return VRegB_31t();
case k32x: return VRegB_32x();
- case k35c: return VRegB_35c();
- case k3rc: return VRegB_3rc();
- case k51l: return VRegB_51l();
default: LOG(FATAL) << "Tried to access vB of instruction " << Name() <<
" which has no B operand.";
}
- return 0;
+ return -1;
}
int32_t Instruction::VRegA() const {
switch (FormatOf(Opcode())) {
- case k10t: return VRegA_10t();
- case k10x: return VRegA_10x();
case k11n: return VRegA_11n();
case k11x: return VRegA_11x();
case k12x: return VRegA_12x();
- case k20t: return VRegA_20t();
case k21c: return VRegA_21c();
case k21h: return VRegA_21h();
case k21s: return VRegA_21s();
@@ -140,18 +170,15 @@ int32_t Instruction::VRegA() const {
case k22t: return VRegA_22t();
case k22x: return VRegA_22x();
case k23x: return VRegA_23x();
- case k30t: return VRegA_30t();
case k31c: return VRegA_31c();
case k31i: return VRegA_31i();
case k31t: return VRegA_31t();
case k32x: return VRegA_32x();
- case k35c: return VRegA_35c();
- case k3rc: return VRegA_3rc();
case k51l: return VRegA_51l();
- default: LOG(FATAL) << "Tried to access vA of instruction "<< Name() <<
+ default: LOG(FATAL) << "Tried to access vA of instruction " << Name() <<
" which has no A operand.";
}
- return 0;
+ return -1;
}
int32_t Instruction::GetTargetOffset() const {
diff --git a/runtime/dex_instruction.h b/runtime/dex_instruction.h
index c3424dc..aea3371 100644
--- a/runtime/dex_instruction.h
+++ b/runtime/dex_instruction.h
@@ -215,6 +215,7 @@ class Instruction {
}
// VRegA
+ bool HasVRegA() const;
int32_t VRegA() const;
int8_t VRegA_10t() const;
uint8_t VRegA_10x() const;
@@ -242,6 +243,7 @@ class Instruction {
uint8_t VRegA_51l() const;
// VRegB
+ bool HasVRegB() const;
int32_t VRegB() const;
int4_t VRegB_11n() const;
uint4_t VRegB_12x() const;
@@ -264,6 +266,7 @@ class Instruction {
uint64_t VRegB_51l() const; // vB_wide
// VRegC
+ bool HasVRegC() const;
int32_t VRegC() const;
int8_t VRegC_22b() const;
uint16_t VRegC_22c() const;
@@ -289,6 +292,12 @@ class Instruction {
insns[0] = (insns[0] & 0xff00) | static_cast<uint16_t>(opcode);
}
+ void SetVRegA_10x(uint8_t val) {
+ DCHECK(FormatOf(Opcode()) == k10x);
+ uint16_t* insns = reinterpret_cast<uint16_t*>(this);
+ insns[0] = (val << 8) | (insns[0] & 0x00ff);
+ }
+
void SetVRegB_3rc(uint16_t val) {
DCHECK(FormatOf(Opcode()) == k3rc);
uint16_t* insns = reinterpret_cast<uint16_t*>(this);
diff --git a/runtime/gc/accounting/atomic_stack.h b/runtime/gc/accounting/atomic_stack.h
index 5310c18..d677ade 100644
--- a/runtime/gc/accounting/atomic_stack.h
+++ b/runtime/gc/accounting/atomic_stack.h
@@ -66,7 +66,7 @@ class AtomicStack {
// Stack overflow.
return false;
}
- } while(!back_index_.CompareAndSwap(index, index + 1));
+ } while(!back_index_.compare_and_swap(index, index + 1));
begin_[index] = value;
return true;
}
@@ -89,7 +89,7 @@ class AtomicStack {
// Take an item from the front of the stack.
T PopFront() {
int32_t index = front_index_;
- DCHECK_LT(index, back_index_.get());
+ DCHECK_LT(index, back_index_.load());
front_index_ = front_index_ + 1;
return begin_[index];
}
@@ -123,12 +123,12 @@ class AtomicStack {
void Sort() {
if (!is_sorted_) {
- int32_t start_back_index = back_index_.get();
- int32_t start_front_index = front_index_.get();
+ int32_t start_back_index = back_index_.load();
+ int32_t start_front_index = front_index_.load();
is_sorted_ = true;
std::sort(Begin(), End());
- CHECK_EQ(start_back_index, back_index_.get());
- CHECK_EQ(start_front_index, front_index_.get());
+ CHECK_EQ(start_back_index, back_index_.load());
+ CHECK_EQ(start_front_index, front_index_.load());
}
}
diff --git a/runtime/gc/collector/mark_sweep.cc b/runtime/gc/collector/mark_sweep.cc
index a5bad2f..865ee13 100644
--- a/runtime/gc/collector/mark_sweep.cc
+++ b/runtime/gc/collector/mark_sweep.cc
@@ -401,9 +401,7 @@ bool MarkSweep::MarkLargeObject(const Object* obj) {
++large_object_test_;
}
if (UNLIKELY(!large_objects->Test(obj))) {
- // TODO: mark may be called holding the JNI global references lock, Contains will hold the
- // large object space lock causing a lock level violation. Bug: 9414652;
- if (!kDebugLocking && !large_object_space->Contains(obj)) {
+ if (!large_object_space->Contains(obj)) {
LOG(ERROR) << "Tried to mark " << obj << " not contained by any spaces";
LOG(ERROR) << "Attempting see if it's a bad root";
VerifyRoots();
@@ -846,8 +844,8 @@ void MarkSweep::SweepCallback(size_t num_ptrs, Object** ptrs, void* arg) {
// AllocSpace::FreeList clears the value in ptrs, so perform after clearing the live bit
size_t freed_bytes = space->FreeList(self, num_ptrs, ptrs);
heap->RecordFree(freed_objects, freed_bytes);
- mark_sweep->freed_objects_ += freed_objects;
- mark_sweep->freed_bytes_ += freed_bytes;
+ mark_sweep->freed_objects_.fetch_add(freed_objects);
+ mark_sweep->freed_bytes_.fetch_add(freed_bytes);
}
void MarkSweep::ZygoteSweepCallback(size_t num_ptrs, Object** ptrs, void* arg) {
@@ -912,8 +910,8 @@ void MarkSweep::SweepArray(accounting::ObjectStack* allocations, bool swap_bitma
VLOG(heap) << "Freed " << freed_objects << "/" << count
<< " objects with size " << PrettySize(freed_bytes);
heap_->RecordFree(freed_objects + freed_large_objects, freed_bytes);
- freed_objects_ += freed_objects;
- freed_bytes_ += freed_bytes;
+ freed_objects_.fetch_add(freed_objects);
+ freed_bytes_.fetch_add(freed_bytes);
timings_.NewSplit("ResetStack");
allocations->Reset();
@@ -991,8 +989,8 @@ void MarkSweep::SweepLargeObjects(bool swap_bitmaps) {
++freed_objects;
}
}
- freed_objects_ += freed_objects;
- freed_bytes_ += freed_bytes;
+ freed_objects_.fetch_add(freed_objects);
+ freed_bytes_.fetch_add(freed_bytes);
GetHeap()->RecordFree(freed_objects, freed_bytes);
}
@@ -1196,7 +1194,7 @@ class MarkStackChunk : public Task {
thread_pool_->AddTask(Thread::Current(), output_);
output_ = NULL;
if (kMeasureOverhead) {
- mark_sweep_->overhead_time_ += NanoTime() - start;
+ mark_sweep_->overhead_time_.fetch_add(NanoTime() - start);
}
}
}
@@ -1208,7 +1206,7 @@ class MarkStackChunk : public Task {
}
output_ = new MarkStackChunk(thread_pool_, mark_sweep_, NULL, NULL);
if (kMeasureOverhead) {
- mark_sweep_->overhead_time_ += NanoTime() - start;
+ mark_sweep_->overhead_time_.fetch_add(NanoTime() - start);
}
}
diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc
index 85b7bf0..21a0725 100644
--- a/runtime/gc/heap.cc
+++ b/runtime/gc/heap.cc
@@ -23,6 +23,7 @@
#include <vector>
#include "base/stl_util.h"
+#include "common_throws.h"
#include "cutils/sched_policy.h"
#include "debugger.h"
#include "gc/accounting/atomic_stack.h"
@@ -78,12 +79,15 @@ Heap::Heap(size_t initial_size, size_t growth_limit, size_t min_free, size_t max
capacity_(capacity),
growth_limit_(growth_limit),
max_allowed_footprint_(initial_size),
+ native_footprint_gc_watermark_(initial_size),
+ native_footprint_limit_(2 * initial_size),
concurrent_start_bytes_(concurrent_gc ? initial_size - (kMinConcurrentRemainingBytes)
: std::numeric_limits<size_t>::max()),
total_bytes_freed_ever_(0),
total_objects_freed_ever_(0),
large_object_threshold_(3 * kPageSize),
num_bytes_allocated_(0),
+ native_bytes_allocated_(0),
verify_missing_card_marks_(false),
verify_system_weaks_(false),
verify_pre_gc_heap_(false),
@@ -452,9 +456,6 @@ mirror::Object* Heap::AllocObject(Thread* self, mirror::Class* c, size_t byte_co
Dbg::RecordAllocation(c, byte_count);
}
if (static_cast<size_t>(num_bytes_allocated_) >= concurrent_start_bytes_) {
- // We already have a request pending, no reason to start more until we update
- // concurrent_start_bytes_.
- concurrent_start_bytes_ = std::numeric_limits<size_t>::max();
// The SirtRef is necessary since the calls in RequestConcurrentGC are a safepoint.
SirtRef<mirror::Object> ref(self, obj);
RequestConcurrentGC(self);
@@ -462,7 +463,7 @@ mirror::Object* Heap::AllocObject(Thread* self, mirror::Class* c, size_t byte_co
VerifyObject(obj);
if (measure_allocation_time_) {
- total_allocation_time_ += NanoTime() / kTimeAdjust - allocation_start;
+ total_allocation_time_.fetch_add(NanoTime() / kTimeAdjust - allocation_start);
}
return obj;
@@ -611,7 +612,7 @@ void Heap::VerifyHeap() {
void Heap::RecordAllocation(size_t size, mirror::Object* obj) {
DCHECK(obj != NULL);
DCHECK_GT(size, 0u);
- num_bytes_allocated_ += size;
+ num_bytes_allocated_.fetch_add(size);
if (Runtime::Current()->HasStatsEnabled()) {
RuntimeStats* thread_stats = Thread::Current()->GetStats();
@@ -633,7 +634,7 @@ void Heap::RecordAllocation(size_t size, mirror::Object* obj) {
void Heap::RecordFree(size_t freed_objects, size_t freed_bytes) {
DCHECK_LE(freed_bytes, static_cast<size_t>(num_bytes_allocated_));
- num_bytes_allocated_ -= freed_bytes;
+ num_bytes_allocated_.fetch_sub(freed_bytes);
if (Runtime::Current()->HasStatsEnabled()) {
RuntimeStats* thread_stats = Thread::Current()->GetStats();
@@ -1569,6 +1570,19 @@ void Heap::SetIdealFootprint(size_t max_allowed_footprint) {
max_allowed_footprint_ = max_allowed_footprint;
}
+void Heap::UpdateMaxNativeFootprint() {
+ size_t native_size = native_bytes_allocated_;
+ // TODO: Tune the native heap utilization to be a value other than the java heap utilization.
+ size_t target_size = native_size / GetTargetHeapUtilization();
+ if (target_size > native_size + max_free_) {
+ target_size = native_size + max_free_;
+ } else if (target_size < native_size + min_free_) {
+ target_size = native_size + min_free_;
+ }
+ native_footprint_gc_watermark_ = target_size;
+ native_footprint_limit_ = 2 * target_size - native_size;
+}
+
void Heap::GrowForUtilization(collector::GcType gc_type, uint64_t gc_duration) {
// We know what our utilization is at this moment.
// This doesn't actually resize any memory. It just lets the heap grow more when necessary.
@@ -1625,6 +1639,8 @@ void Heap::GrowForUtilization(collector::GcType gc_type, uint64_t gc_duration) {
DCHECK_LE(concurrent_start_bytes_, max_allowed_footprint_);
DCHECK_LE(max_allowed_footprint_, growth_limit_);
}
+
+ UpdateMaxNativeFootprint();
}
void Heap::ClearGrowthLimit() {
@@ -1760,6 +1776,10 @@ void Heap::RequestConcurrentGC(Thread* self) {
return;
}
+ // We already have a request pending, no reason to start more until we update
+ // concurrent_start_bytes_.
+ concurrent_start_bytes_ = std::numeric_limits<size_t>::max();
+
JNIEnv* env = self->GetJniEnv();
DCHECK(WellKnownClasses::java_lang_Daemons != NULL);
DCHECK(WellKnownClasses::java_lang_Daemons_requestGC != NULL);
@@ -1837,5 +1857,64 @@ size_t Heap::Trim() {
return alloc_space_->Trim();
}
+bool Heap::IsGCRequestPending() const {
+ return concurrent_start_bytes_ != std::numeric_limits<size_t>::max();
+}
+
+void Heap::RegisterNativeAllocation(int bytes) {
+ // Total number of native bytes allocated.
+ native_bytes_allocated_.fetch_add(bytes);
+ Thread* self = Thread::Current();
+ if (static_cast<size_t>(native_bytes_allocated_) > native_footprint_gc_watermark_) {
+ // The second watermark is higher than the gc watermark. If you hit this it means you are
+ // allocating native objects faster than the GC can keep up with.
+ if (static_cast<size_t>(native_bytes_allocated_) > native_footprint_limit_) {
+ JNIEnv* env = self->GetJniEnv();
+ // Can't do this in WellKnownClasses::Init since System is not properly set up at that
+ // point.
+ if (WellKnownClasses::java_lang_System_runFinalization == NULL) {
+ DCHECK(WellKnownClasses::java_lang_System != NULL);
+ WellKnownClasses::java_lang_System_runFinalization =
+ CacheMethod(env, WellKnownClasses::java_lang_System, true, "runFinalization", "()V");
+ assert(WellKnownClasses::java_lang_System_runFinalization != NULL);
+ }
+ if (WaitForConcurrentGcToComplete(self) != collector::kGcTypeNone) {
+ // Just finished a GC, attempt to run finalizers.
+ env->CallStaticVoidMethod(WellKnownClasses::java_lang_System,
+ WellKnownClasses::java_lang_System_runFinalization);
+ CHECK(!env->ExceptionCheck());
+ }
+
+ // If we still are over the watermark, attempt a GC for alloc and run finalizers.
+ if (static_cast<size_t>(native_bytes_allocated_) > native_footprint_limit_) {
+ CollectGarbageInternal(collector::kGcTypePartial, kGcCauseForAlloc, false);
+ env->CallStaticVoidMethod(WellKnownClasses::java_lang_System,
+ WellKnownClasses::java_lang_System_runFinalization);
+ CHECK(!env->ExceptionCheck());
+ }
+ // We have just run finalizers, update the native watermark since it is very likely that
+ // finalizers released native managed allocations.
+ UpdateMaxNativeFootprint();
+ } else {
+ if (!IsGCRequestPending()) {
+ RequestConcurrentGC(self);
+ }
+ }
+ }
+}
+
+void Heap::RegisterNativeFree(int bytes) {
+ int expected_size, new_size;
+ do {
+ expected_size = native_bytes_allocated_.load();
+ new_size = expected_size - bytes;
+ if (new_size < 0) {
+ ThrowRuntimeException("attempted to free %d native bytes with only %d native bytes registered as allocated",
+ bytes, expected_size);
+ break;
+ }
+ } while (!native_bytes_allocated_.compare_and_swap(expected_size, new_size));
+}
+
} // namespace gc
} // namespace art
diff --git a/runtime/gc/heap.h b/runtime/gc/heap.h
index 630d063..aaf449b 100644
--- a/runtime/gc/heap.h
+++ b/runtime/gc/heap.h
@@ -126,6 +126,10 @@ class Heap {
mirror::Object* AllocObject(Thread* self, mirror::Class* klass, size_t num_bytes)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ void RegisterNativeAllocation(int bytes)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ void RegisterNativeFree(int bytes) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
// The given reference is believed to be to an object in the Java heap, check the soundness of it.
void VerifyObjectImpl(const mirror::Object* o);
void VerifyObject(const mirror::Object* o) {
@@ -400,6 +404,7 @@ class Heap {
void RequestHeapTrim() LOCKS_EXCLUDED(Locks::runtime_shutdown_lock_);
void RequestConcurrentGC(Thread* self) LOCKS_EXCLUDED(Locks::runtime_shutdown_lock_);
+ bool IsGCRequestPending() const;
void RecordAllocation(size_t size, mirror::Object* object)
LOCKS_EXCLUDED(GlobalSynchronization::heap_bitmap_lock_)
@@ -418,6 +423,10 @@ class Heap {
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
void PostGcVerification(collector::GarbageCollector* gc);
+ // Update the watermark for the native allocated bytes based on the current number of native
+ // bytes allocated and the target utilization ratio.
+ void UpdateMaxNativeFootprint();
+
// Given the current contents of the alloc space, increase the allowed heap footprint to match
// the target utilization ratio. This should only be called immediately after a full garbage
// collection.
@@ -495,6 +504,10 @@ class Heap {
// When the number of bytes allocated exceeds the footprint TryAllocate returns NULL indicating
// a GC should be triggered.
size_t max_allowed_footprint_;
+ // The watermark at which a concurrent GC is requested by registerNativeAllocation.
+ size_t native_footprint_gc_watermark_;
+ // The watermark at which a GC is performed inside of registerNativeAllocation.
+ size_t native_footprint_limit_;
// When num_bytes_allocated_ exceeds this amount then a concurrent GC should be requested so that
// it completes ahead of an allocation failing.
@@ -512,6 +525,9 @@ class Heap {
// Number of bytes allocated. Adjusted after each allocation and free.
AtomicInteger num_bytes_allocated_;
+ // Bytes which are allocated and managed by native code but still need to be accounted for.
+ AtomicInteger native_bytes_allocated_;
+
// Heap verification flags.
const bool verify_missing_card_marks_;
const bool verify_system_weaks_;
diff --git a/runtime/gc/space/space.h b/runtime/gc/space/space.h
index 48f0579..011e155 100644
--- a/runtime/gc/space/space.h
+++ b/runtime/gc/space/space.h
@@ -112,7 +112,6 @@ class Space {
bool IsZygoteSpace() const {
return GetType() == kSpaceTypeZygoteSpace;
}
- DlMallocSpace* AsZygoteSpace();
// Does this space hold large objects and implement the large object space abstraction?
bool IsLargeObjectSpace() const {
diff --git a/runtime/instrumentation.cc b/runtime/instrumentation.cc
index 8598d6d..091f66a 100644
--- a/runtime/instrumentation.cc
+++ b/runtime/instrumentation.cc
@@ -59,7 +59,9 @@ bool Instrumentation::InstallStubsForClass(mirror::Class* klass) {
if (!method->IsAbstract()) {
const void* new_code;
if (uninstall) {
- if (is_initialized || !method->IsStatic() || method->IsConstructor()) {
+ if (forced_interpret_only_ && !method->IsNative() && !method->IsProxyMethod()) {
+ new_code = GetInterpreterEntryPoint();
+ } else if (is_initialized || !method->IsStatic() || method->IsConstructor()) {
new_code = class_linker->GetOatCodeFor(method);
} else {
new_code = GetResolutionTrampoline(class_linker);
@@ -79,7 +81,11 @@ bool Instrumentation::InstallStubsForClass(mirror::Class* klass) {
if (!method->IsAbstract()) {
const void* new_code;
if (uninstall) {
- new_code = class_linker->GetOatCodeFor(method);
+ if (forced_interpret_only_ && !method->IsNative() && !method->IsProxyMethod()) {
+ new_code = GetInterpreterEntryPoint();
+ } else {
+ new_code = class_linker->GetOatCodeFor(method);
+ }
} else { // !uninstall
if (!interpreter_stubs_installed_ || method->IsNative()) {
new_code = GetInstrumentationEntryPoint();
@@ -376,6 +382,12 @@ void Instrumentation::ConfigureStubs(bool require_entry_exit_stubs, bool require
void Instrumentation::UpdateMethodsCode(mirror::AbstractMethod* method, const void* code) const {
if (LIKELY(!instrumentation_stubs_installed_)) {
method->SetEntryPointFromCompiledCode(code);
+ } else {
+ if (!interpreter_stubs_installed_ || method->IsNative()) {
+ method->SetEntryPointFromCompiledCode(GetInstrumentationEntryPoint());
+ } else {
+ method->SetEntryPointFromCompiledCode(GetInterpreterEntryPoint());
+ }
}
}
@@ -396,9 +408,14 @@ void Instrumentation::MethodEnterEventImpl(Thread* thread, mirror::Object* this_
const mirror::AbstractMethod* method,
uint32_t dex_pc) const {
typedef std::list<InstrumentationListener*>::const_iterator It; // TODO: C++0x auto
- for (It it = method_entry_listeners_.begin(), end = method_entry_listeners_.end(); it != end;
- ++it) {
- (*it)->MethodEntered(thread, this_object, method, dex_pc);
+ It it = method_entry_listeners_.begin();
+ bool is_end = (it == method_entry_listeners_.end());
+ // Implemented this way to prevent problems caused by modification of the list while iterating.
+ while (!is_end) {
+ InstrumentationListener* cur = *it;
+ ++it;
+ is_end = (it == method_entry_listeners_.end());
+ cur->MethodEntered(thread, this_object, method, dex_pc);
}
}
@@ -406,9 +423,14 @@ void Instrumentation::MethodExitEventImpl(Thread* thread, mirror::Object* this_o
const mirror::AbstractMethod* method,
uint32_t dex_pc, const JValue& return_value) const {
typedef std::list<InstrumentationListener*>::const_iterator It; // TODO: C++0x auto
- for (It it = method_exit_listeners_.begin(), end = method_exit_listeners_.end(); it != end;
- ++it) {
- (*it)->MethodExited(thread, this_object, method, dex_pc, return_value);
+ It it = method_exit_listeners_.begin();
+ bool is_end = (it == method_exit_listeners_.end());
+ // Implemented this way to prevent problems caused by modification of the list while iterating.
+ while (!is_end) {
+ InstrumentationListener* cur = *it;
+ ++it;
+ is_end = (it == method_exit_listeners_.end());
+ cur->MethodExited(thread, this_object, method, dex_pc, return_value);
}
}
diff --git a/runtime/instrumentation.h b/runtime/instrumentation.h
index e0f1fa9..798b7ab 100644
--- a/runtime/instrumentation.h
+++ b/runtime/instrumentation.h
@@ -284,7 +284,7 @@ struct InstrumentationStackFrame {
mirror::AbstractMethod* method_;
const uintptr_t return_pc_;
const size_t frame_id_;
- bool interpreter_entry_;
+ const bool interpreter_entry_;
};
} // namespace instrumentation
diff --git a/runtime/jdwp/jdwp.h b/runtime/jdwp/jdwp.h
index 6a5d0d1..40ec431 100644
--- a/runtime/jdwp/jdwp.h
+++ b/runtime/jdwp/jdwp.h
@@ -17,6 +17,7 @@
#ifndef ART_RUNTIME_JDWP_JDWP_H_
#define ART_RUNTIME_JDWP_JDWP_H_
+#include "atomic_integer.h"
#include "base/mutex.h"
#include "jdwp/jdwp_bits.h"
#include "jdwp/jdwp_constants.h"
@@ -319,9 +320,8 @@ struct JdwpState {
int64_t last_activity_time_ms_;
// Global counters and a mutex to protect them.
- Mutex serial_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
- uint32_t request_serial_ GUARDED_BY(serial_lock_);
- uint32_t event_serial_ GUARDED_BY(serial_lock_);
+ AtomicInteger request_serial_;
+ AtomicInteger event_serial_;
// Linked list of events requested by the debugger (breakpoints, class prep, etc).
Mutex event_list_lock_;
diff --git a/runtime/jdwp/jdwp_handler.cc b/runtime/jdwp/jdwp_handler.cc
index 8ef146c..4feeafb 100644
--- a/runtime/jdwp/jdwp_handler.cc
+++ b/runtime/jdwp/jdwp_handler.cc
@@ -1476,7 +1476,6 @@ static JdwpError DDM_Chunk(JdwpState* state, Request& request, ExpandBuf* pReply
// instead of copying it into the expanding buffer. The reduction in
// heap requirements is probably more valuable than the efficiency.
CHECK_GT(replyLen, 0);
- CHECK_LT(replyLen, 1*1024*1024);
memcpy(expandBufAddSpace(pReply, replyLen), replyBuf, replyLen);
free(replyBuf);
}
diff --git a/runtime/jdwp/jdwp_main.cc b/runtime/jdwp/jdwp_main.cc
index 3b6dd81..8e61d23 100644
--- a/runtime/jdwp/jdwp_main.cc
+++ b/runtime/jdwp/jdwp_main.cc
@@ -36,7 +36,7 @@ static void* StartJdwpThread(void* arg);
* JdwpNetStateBase class implementation
*/
JdwpNetStateBase::JdwpNetStateBase(JdwpState* state)
- : state_(state), socket_lock_("JdwpNetStateBase lock", kJdwpSerialSocketLock) {
+ : state_(state), socket_lock_("JdwpNetStateBase lock", kJdwpSocketLock) {
clientSock = -1;
wake_pipe_[0] = -1;
wake_pipe_[1] = -1;
@@ -185,7 +185,6 @@ void JdwpState::SendRequest(ExpandBuf* pReq) {
* packets to the debugger.
*/
uint32_t JdwpState::NextRequestSerial() {
- MutexLock mu(Thread::Current(), serial_lock_);
return request_serial_++;
}
@@ -194,7 +193,6 @@ uint32_t JdwpState::NextRequestSerial() {
* message type EventRequest.Set.
*/
uint32_t JdwpState::NextEventSerial() {
- MutexLock mu(Thread::Current(), serial_lock_);
return event_serial_++;
}
@@ -211,7 +209,6 @@ JdwpState::JdwpState(const JdwpOptions* options)
attach_lock_("JDWP attach lock", kJdwpAttachLock),
attach_cond_("JDWP attach condition variable", attach_lock_),
last_activity_time_ms_(0),
- serial_lock_("JDWP serial lock", kJdwpSerialSocketLock),
request_serial_(0x10000000),
event_serial_(0x20000000),
event_list_lock_("JDWP event list lock", kJdwpEventListLock),
@@ -248,14 +245,30 @@ JdwpState* JdwpState::Create(const JdwpOptions* options) {
LOG(FATAL) << "Unknown transport: " << options->transport;
}
- /*
- * Grab a mutex or two before starting the thread. This ensures they
- * won't signal the cond var before we're waiting.
- */
- {
+ if (!options->suspend) {
+ /*
+ * Grab a mutex before starting the thread. This ensures they
+ * won't signal the cond var before we're waiting.
+ */
MutexLock thread_start_locker(self, state->thread_start_lock_);
- const bool should_suspend = options->suspend;
- if (!should_suspend) {
+ /*
+ * We have bound to a port, or are trying to connect outbound to a
+ * debugger. Create the JDWP thread and let it continue the mission.
+ */
+ CHECK_PTHREAD_CALL(pthread_create, (&state->pthread_, NULL, StartJdwpThread, state.get()), "JDWP thread");
+
+ /*
+ * Wait until the thread finishes basic initialization.
+ * TODO: cond vars should be waited upon in a loop
+ */
+ state->thread_start_cond_.Wait(self);
+ } else {
+ {
+ /*
+ * Grab a mutex before starting the thread. This ensures they
+ * won't signal the cond var before we're waiting.
+ */
+ MutexLock thread_start_locker(self, state->thread_start_lock_);
/*
* We have bound to a port, or are trying to connect outbound to a
* debugger. Create the JDWP thread and let it continue the mission.
@@ -267,47 +280,33 @@ JdwpState* JdwpState::Create(const JdwpOptions* options) {
* TODO: cond vars should be waited upon in a loop
*/
state->thread_start_cond_.Wait(self);
- } else {
- {
- /*
- * We have bound to a port, or are trying to connect outbound to a
- * debugger. Create the JDWP thread and let it continue the mission.
- */
- CHECK_PTHREAD_CALL(pthread_create, (&state->pthread_, NULL, StartJdwpThread, state.get()), "JDWP thread");
-
- /*
- * Wait until the thread finishes basic initialization.
- * TODO: cond vars should be waited upon in a loop
- */
- state->thread_start_cond_.Wait(self);
-
- /*
- * For suspend=y, wait for the debugger to connect to us or for us to
- * connect to the debugger.
- *
- * The JDWP thread will signal us when it connects successfully or
- * times out (for timeout=xxx), so we have to check to see what happened
- * when we wake up.
- */
- {
- ScopedThreadStateChange tsc(self, kWaitingForDebuggerToAttach);
- MutexLock attach_locker(self, state->attach_lock_);
- state->attach_cond_.Wait(self);
- }
- }
- if (!state->IsActive()) {
- LOG(ERROR) << "JDWP connection failed";
- return NULL;
- }
-
- LOG(INFO) << "JDWP connected";
+ }
- /*
- * Ordinarily we would pause briefly to allow the debugger to set
- * breakpoints and so on, but for "suspend=y" the VM init code will
- * pause the VM when it sends the VM_START message.
- */
+ /*
+ * For suspend=y, wait for the debugger to connect to us or for us to
+ * connect to the debugger.
+ *
+ * The JDWP thread will signal us when it connects successfully or
+ * times out (for timeout=xxx), so we have to check to see what happened
+ * when we wake up.
+ */
+ {
+ ScopedThreadStateChange tsc(self, kWaitingForDebuggerToAttach);
+ MutexLock attach_locker(self, state->attach_lock_);
+ state->attach_cond_.Wait(self);
}
+ if (!state->IsActive()) {
+ LOG(ERROR) << "JDWP connection failed";
+ return NULL;
+ }
+
+ LOG(INFO) << "JDWP connected";
+
+ /*
+ * Ordinarily we would pause briefly to allow the debugger to set
+ * breakpoints and so on, but for "suspend=y" the VM init code will
+ * pause the VM when it sends the VM_START message.
+ */
}
return state.release();
diff --git a/runtime/locks.h b/runtime/locks.h
index 6b0e96f..df3f27b 100644
--- a/runtime/locks.h
+++ b/runtime/locks.h
@@ -36,7 +36,7 @@ enum LockLevel {
kUnexpectedSignalLock,
kThreadSuspendCountLock,
kAbortLock,
- kJdwpSerialSocketLock,
+ kJdwpSocketLock,
kAllocSpaceLock,
kDefaultMutexLevel,
kMarkSweepLargeObjectLock,
diff --git a/runtime/mirror/abstract_method-inl.h b/runtime/mirror/abstract_method-inl.h
index 6fcd705..2df1367 100644
--- a/runtime/mirror/abstract_method-inl.h
+++ b/runtime/mirror/abstract_method-inl.h
@@ -114,17 +114,21 @@ inline void AbstractMethod::AssertPcIsWithinCode(uintptr_t pc) const {
if (IsNative() || IsRuntimeMethod() || IsProxyMethod()) {
return;
}
- if (GetEntryPointFromCompiledCode() == GetInterpreterEntryPoint()) {
+ if (pc == GetInstrumentationExitPc()) {
+ return;
+ }
+ const void* code = GetEntryPointFromCompiledCode();
+ if (code == GetInterpreterEntryPoint() || code == GetInstrumentationEntryPoint()) {
return;
}
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
- if (GetEntryPointFromCompiledCode() == GetResolutionTrampoline(class_linker)) {
- return;
+ if (code == GetResolutionTrampoline(class_linker)) {
+ return;
}
DCHECK(IsWithinCode(pc))
<< PrettyMethod(this)
<< " pc=" << std::hex << pc
- << " code=" << GetEntryPointFromCompiledCode()
+ << " code=" << code
<< " size=" << GetCodeSize();
}
diff --git a/runtime/native/dalvik_system_VMRuntime.cc b/runtime/native/dalvik_system_VMRuntime.cc
index ce3cc93..baae8a3 100644
--- a/runtime/native/dalvik_system_VMRuntime.cc
+++ b/runtime/native/dalvik_system_VMRuntime.cc
@@ -165,6 +165,24 @@ static void VMRuntime_setTargetSdkVersion(JNIEnv* env, jobject, jint targetSdkVe
}
}
+static void VMRuntime_registerNativeAllocation(JNIEnv* env, jobject, jint bytes) {
+ ScopedObjectAccess soa(env);
+ if (bytes < 0) {
+ ThrowRuntimeException("allocation size negative %d", bytes);
+ return;
+ }
+ Runtime::Current()->GetHeap()->RegisterNativeAllocation(bytes);
+}
+
+static void VMRuntime_registerNativeFree(JNIEnv* env, jobject, jint bytes) {
+ ScopedObjectAccess soa(env);
+ if (bytes < 0) {
+ ThrowRuntimeException("allocation size negative %d", bytes);
+ return;
+ }
+ Runtime::Current()->GetHeap()->RegisterNativeFree(bytes);
+}
+
static void VMRuntime_trimHeap(JNIEnv*, jobject) {
uint64_t start_ns = NanoTime();
@@ -210,10 +228,13 @@ static JNINativeMethod gMethods[] = {
NATIVE_METHOD(VMRuntime, newNonMovableArray, "(Ljava/lang/Class;I)Ljava/lang/Object;"),
NATIVE_METHOD(VMRuntime, properties, "()[Ljava/lang/String;"),
NATIVE_METHOD(VMRuntime, setTargetSdkVersion, "(I)V"),
+ NATIVE_METHOD(VMRuntime, registerNativeAllocation, "(I)V"),
+ NATIVE_METHOD(VMRuntime, registerNativeFree, "(I)V"),
NATIVE_METHOD(VMRuntime, startJitCompilation, "()V"),
NATIVE_METHOD(VMRuntime, trimHeap, "()V"),
NATIVE_METHOD(VMRuntime, vmVersion, "()Ljava/lang/String;"),
NATIVE_METHOD(VMRuntime, vmLibrary, "()Ljava/lang/String;"),
+
};
void register_dalvik_system_VMRuntime(JNIEnv* env) {
diff --git a/runtime/stack.cc b/runtime/stack.cc
index fcd0f2d..f4ae81d 100644
--- a/runtime/stack.cc
+++ b/runtime/stack.cc
@@ -289,7 +289,6 @@ void StackVisitor::WalkStack(bool include_transitions) {
DCHECK(current_fragment->GetTopShadowFrame() == NULL);
mirror::AbstractMethod* method = *cur_quick_frame_;
while (method != NULL) {
- DCHECK(cur_quick_frame_pc_ != GetInstrumentationExitPc());
SanityCheckFrame();
bool should_continue = VisitFrame();
if (UNLIKELY(!should_continue)) {
@@ -312,9 +311,7 @@ void StackVisitor::WalkStack(bool include_transitions) {
instrumentation_stack_depth++;
if (instrumentation_frame.interpreter_entry_) {
mirror::AbstractMethod* callee = Runtime::Current()->GetCalleeSaveMethod(Runtime::kRefsAndArgs);
- if (GetMethod() != callee) {
- LOG(FATAL) << "Expected: " << callee << " Found: " << PrettyMethod(GetMethod());
- }
+ CHECK_EQ(GetMethod(), callee);
} else if (instrumentation_frame.method_ != GetMethod()) {
LOG(FATAL) << "Expected: " << PrettyMethod(instrumentation_frame.method_)
<< " Found: " << PrettyMethod(GetMethod());
diff --git a/runtime/thread.cc b/runtime/thread.cc
index 6114c63..c0be4d2 100644
--- a/runtime/thread.cc
+++ b/runtime/thread.cc
@@ -216,43 +216,6 @@ static size_t FixStackSize(size_t stack_size) {
return stack_size;
}
-static void SigAltStack(stack_t* new_stack, stack_t* old_stack) {
- if (sigaltstack(new_stack, old_stack) == -1) {
- PLOG(FATAL) << "sigaltstack failed";
- }
-}
-
-static void SetUpAlternateSignalStack() {
- // Create and set an alternate signal stack.
- stack_t ss;
- ss.ss_sp = new uint8_t[SIGSTKSZ];
- ss.ss_size = SIGSTKSZ;
- ss.ss_flags = 0;
- CHECK(ss.ss_sp != NULL);
- SigAltStack(&ss, NULL);
-
- // Double-check that it worked.
- ss.ss_sp = NULL;
- SigAltStack(NULL, &ss);
- VLOG(threads) << "Alternate signal stack is " << PrettySize(ss.ss_size) << " at " << ss.ss_sp;
-}
-
-static void TearDownAlternateSignalStack() {
- // Get the pointer so we can free the memory.
- stack_t ss;
- SigAltStack(NULL, &ss);
- uint8_t* allocated_signal_stack = reinterpret_cast<uint8_t*>(ss.ss_sp);
-
- // Tell the kernel to stop using it.
- ss.ss_sp = NULL;
- ss.ss_flags = SS_DISABLE;
- ss.ss_size = SIGSTKSZ; // Avoid ENOMEM failure with Mac OS' buggy libc.
- SigAltStack(&ss, NULL);
-
- // Free it.
- delete[] allocated_signal_stack;
-}
-
void Thread::CreateNativeThread(JNIEnv* env, jobject java_peer, size_t stack_size, bool is_daemon) {
CHECK(java_peer != NULL);
Thread* self = static_cast<JNIEnvExt*>(env)->self;
diff --git a/runtime/thread.h b/runtime/thread.h
index 64ff7c2..3b66943 100644
--- a/runtime/thread.h
+++ b/runtime/thread.h
@@ -618,6 +618,9 @@ class PACKED(4) Thread {
void InitPthreadKeySelf();
void InitStackHwm();
+ void SetUpAlternateSignalStack();
+ void TearDownAlternateSignalStack();
+
void NotifyLocked(Thread* self) EXCLUSIVE_LOCKS_REQUIRED(wait_mutex_);
static void ThreadExitCallback(void* arg);
diff --git a/runtime/thread_android.cc b/runtime/thread_android.cc
index 7c4551f..73a9e54 100644
--- a/runtime/thread_android.cc
+++ b/runtime/thread_android.cc
@@ -87,4 +87,12 @@ int Thread::GetNativePriority() {
return managed_priority;
}
+void Thread::SetUpAlternateSignalStack() {
+ // Bionic does this for us.
+}
+
+void Thread::TearDownAlternateSignalStack() {
+ // Bionic does this for us.
+}
+
} // namespace art
diff --git a/runtime/thread_linux.cc b/runtime/thread_linux.cc
index 6f4b75d..1bd708a 100644
--- a/runtime/thread_linux.cc
+++ b/runtime/thread_linux.cc
@@ -26,4 +26,41 @@ int Thread::GetNativePriority() {
return kNormThreadPriority;
}
+static void SigAltStack(stack_t* new_stack, stack_t* old_stack) {
+ if (sigaltstack(new_stack, old_stack) == -1) {
+ PLOG(FATAL) << "sigaltstack failed";
+ }
+}
+
+void Thread::SetUpAlternateSignalStack() {
+ // Create and set an alternate signal stack.
+ stack_t ss;
+ ss.ss_sp = new uint8_t[SIGSTKSZ];
+ ss.ss_size = SIGSTKSZ;
+ ss.ss_flags = 0;
+ CHECK(ss.ss_sp != NULL);
+ SigAltStack(&ss, NULL);
+
+ // Double-check that it worked.
+ ss.ss_sp = NULL;
+ SigAltStack(NULL, &ss);
+ VLOG(threads) << "Alternate signal stack is " << PrettySize(ss.ss_size) << " at " << ss.ss_sp;
+}
+
+void Thread::TearDownAlternateSignalStack() {
+ // Get the pointer so we can free the memory.
+ stack_t ss;
+ SigAltStack(NULL, &ss);
+ uint8_t* allocated_signal_stack = reinterpret_cast<uint8_t*>(ss.ss_sp);
+
+ // Tell the kernel to stop using it.
+ ss.ss_sp = NULL;
+ ss.ss_flags = SS_DISABLE;
+ ss.ss_size = SIGSTKSZ; // Avoid ENOMEM failure with Mac OS' buggy libc.
+ SigAltStack(&ss, NULL);
+
+ // Free it.
+ delete[] allocated_signal_stack;
+}
+
} // namespace art
diff --git a/runtime/thread_pool_test.cc b/runtime/thread_pool_test.cc
index 9b66318..10954e8 100644
--- a/runtime/thread_pool_test.cc
+++ b/runtime/thread_pool_test.cc
@@ -94,7 +94,7 @@ TEST_F(ThreadPoolTest, StopStart) {
EXPECT_EQ(0, bad_count);
// Allow tasks to finish up and delete themselves.
thread_pool.StartWorkers(self);
- while (count.get() != num_tasks && bad_count.get() != 1) {
+ while (count.load() != num_tasks && bad_count.load() != 1) {
usleep(200);
}
thread_pool.StopWorkers(self);
diff --git a/runtime/well_known_classes.cc b/runtime/well_known_classes.cc
index 4d34c73..434fcf0 100644
--- a/runtime/well_known_classes.cc
+++ b/runtime/well_known_classes.cc
@@ -37,6 +37,7 @@ jclass WellKnownClasses::java_lang_reflect_AbstractMethod;
jclass WellKnownClasses::java_lang_reflect_Proxy;
jclass WellKnownClasses::java_lang_RuntimeException;
jclass WellKnownClasses::java_lang_StackOverflowError;
+jclass WellKnownClasses::java_lang_System;
jclass WellKnownClasses::java_lang_Thread;
jclass WellKnownClasses::java_lang_Thread$UncaughtExceptionHandler;
jclass WellKnownClasses::java_lang_ThreadGroup;
@@ -63,6 +64,7 @@ jmethodID WellKnownClasses::java_lang_ref_ReferenceQueue_add;
jmethodID WellKnownClasses::java_lang_reflect_InvocationHandler_invoke;
jmethodID WellKnownClasses::java_lang_Runtime_nativeLoad;
jmethodID WellKnownClasses::java_lang_Short_valueOf;
+jmethodID WellKnownClasses::java_lang_System_runFinalization = NULL;
jmethodID WellKnownClasses::java_lang_Thread_init;
jmethodID WellKnownClasses::java_lang_Thread_run;
jmethodID WellKnownClasses::java_lang_Thread$UncaughtExceptionHandler_uncaughtException;
@@ -105,7 +107,7 @@ static jfieldID CacheField(JNIEnv* env, jclass c, bool is_static, const char* na
return fid;
}
-static jmethodID CacheMethod(JNIEnv* env, jclass c, bool is_static, const char* name, const char* signature) {
+jmethodID CacheMethod(JNIEnv* env, jclass c, bool is_static, const char* name, const char* signature) {
jmethodID mid = is_static ? env->GetStaticMethodID(c, name, signature) : env->GetMethodID(c, name, signature);
if (mid == NULL) {
LOG(FATAL) << "Couldn't find method \"" << name << "\" with signature \"" << signature << "\"";
@@ -132,6 +134,7 @@ void WellKnownClasses::InitClasses(JNIEnv* env) {
java_lang_reflect_Proxy = CacheClass(env, "java/lang/reflect/Proxy");
java_lang_RuntimeException = CacheClass(env, "java/lang/RuntimeException");
java_lang_StackOverflowError = CacheClass(env, "java/lang/StackOverflowError");
+ java_lang_System = CacheClass(env, "java/lang/System");
java_lang_Thread = CacheClass(env, "java/lang/Thread");
java_lang_Thread$UncaughtExceptionHandler = CacheClass(env, "java/lang/Thread$UncaughtExceptionHandler");
java_lang_ThreadGroup = CacheClass(env, "java/lang/ThreadGroup");
diff --git a/runtime/well_known_classes.h b/runtime/well_known_classes.h
index a8069bc..fc2bffb 100644
--- a/runtime/well_known_classes.h
+++ b/runtime/well_known_classes.h
@@ -29,6 +29,8 @@ class Class;
// them up. Similar to libcore's JniConstants (except there's no overlap, so
// we keep them separate).
+jmethodID CacheMethod(JNIEnv* env, jclass c, bool is_static, const char* name, const char* signature);
+
struct WellKnownClasses {
static void InitClasses(JNIEnv* env);
static void Init(JNIEnv* env); // Run before native methods are registered.
@@ -49,6 +51,7 @@ struct WellKnownClasses {
static jclass java_lang_reflect_Proxy;
static jclass java_lang_RuntimeException;
static jclass java_lang_StackOverflowError;
+ static jclass java_lang_System;
static jclass java_lang_Thread;
static jclass java_lang_ThreadGroup;
static jclass java_lang_Thread$UncaughtExceptionHandler;
@@ -75,6 +78,7 @@ struct WellKnownClasses {
static jmethodID java_lang_reflect_InvocationHandler_invoke;
static jmethodID java_lang_Runtime_nativeLoad;
static jmethodID java_lang_Short_valueOf;
+ static jmethodID java_lang_System_runFinalization;
static jmethodID java_lang_Thread_init;
static jmethodID java_lang_Thread_run;
static jmethodID java_lang_Thread$UncaughtExceptionHandler_uncaughtException;
diff --git a/test/Android.mk b/test/Android.mk
index fdb7273..a91ed3d 100644
--- a/test/Android.mk
+++ b/test/Android.mk
@@ -44,6 +44,7 @@ TEST_OAT_DIRECTORIES := \
Main \
HelloWorld \
\
+ NativeAllocations \
ParallelGC \
ReferenceMap \
StackWalk \
diff --git a/test/NativeAllocations/NativeAllocations.java b/test/NativeAllocations/NativeAllocations.java
new file mode 100644
index 0000000..9423b91
--- /dev/null
+++ b/test/NativeAllocations/NativeAllocations.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.lang.reflect.*;
+
+class NativeAllocations {
+ static Object nativeLock = new Object();
+ static int nativeBytes = 0;
+ static Object runtime;
+ static Method register_native_allocation;
+ static Method register_native_free;
+ static int maxMem = 64 * 1024 * 1024;
+
+ static class NativeAllocation {
+ private int bytes;
+
+ NativeAllocation(int bytes) throws Exception {
+ this.bytes = bytes;
+ register_native_allocation.invoke(runtime, bytes);
+ synchronized (nativeLock) {
+ nativeBytes += bytes;
+ if (nativeBytes > maxMem) {
+ throw new OutOfMemoryError();
+ }
+ }
+ }
+
+ protected void finalize() throws Exception {
+ synchronized (nativeLock) {
+ nativeBytes -= bytes;
+ }
+ register_native_free.invoke(runtime, bytes);
+ }
+ }
+
+ public static void main(String[] args) throws Exception {
+ Class<?> vm_runtime = Class.forName("dalvik.system.VMRuntime");
+ Method get_runtime = vm_runtime.getDeclaredMethod("getRuntime");
+ runtime = get_runtime.invoke(null);
+ register_native_allocation = vm_runtime.getDeclaredMethod("registerNativeAllocation", Integer.TYPE);
+ register_native_free = vm_runtime.getDeclaredMethod("registerNativeFree", Integer.TYPE);
+ int count = 16;
+ int size = 512 * 0x400;
+ int allocation_count = 256;
+ NativeAllocation[] allocations = new NativeAllocation[count];
+ for (int i = 0; i < allocation_count; ++i) {
+ allocations[i % count] = new NativeAllocation(size);
+ }
+ System.out.println("Test complete");
+ }
+}
+