Merge "Make dex2dex return a CompiledMethod after quickening."
diff --git a/build/Android.common_build.mk b/build/Android.common_build.mk
index 77f39c4..ee0cb09 100644
--- a/build/Android.common_build.mk
+++ b/build/Android.common_build.mk
@@ -40,7 +40,7 @@
ART_BUILD_HOST_STATIC := false
endif
-ifneq ($$(HOST_OS),linux)
+ifneq ($(HOST_OS),linux)
ART_BUILD_HOST_STATIC := false
endif
diff --git a/compiler/optimizing/builder.cc b/compiler/optimizing/builder.cc
index bb1b40a..997319a 100644
--- a/compiler/optimizing/builder.cc
+++ b/compiler/optimizing/builder.cc
@@ -333,18 +333,41 @@
return;
}
- const size_t num_blocks = graph_->GetBlocks().Size();
- ArenaBitVector can_block_throw(arena_, num_blocks, false);
+ // Bit vector stores information on which blocks contain throwing instructions.
+ // Must be expandable because catch blocks may be split into two.
+ ArenaBitVector can_block_throw(arena_, graph_->GetBlocks().Size(), /* expandable */ true);
// Scan blocks and mark those which contain throwing instructions.
- for (size_t block_id = 0; block_id < num_blocks; ++block_id) {
+ for (size_t block_id = 0, e = graph_->GetBlocks().Size(); block_id < e; ++block_id) {
HBasicBlock* block = graph_->GetBlocks().Get(block_id);
+ bool can_throw = false;
for (HInstructionIterator insn(block->GetInstructions()); !insn.Done(); insn.Advance()) {
if (insn.Current()->CanThrow()) {
- can_block_throw.SetBit(block_id);
+ can_throw = true;
break;
}
}
+
+ if (can_throw) {
+ if (block->IsCatchBlock()) {
+ // Catch blocks are always considered an entry point into the TryItem in
+ // order to avoid splitting exceptional edges. We split the block after
+ // the move-exception (if present) and mark the first part non-throwing.
+ // Later on, a TryBoundary will be inserted between the two blocks.
+ HInstruction* first_insn = block->GetFirstInstruction();
+ if (first_insn->IsLoadException()) {
+ // Catch block starts with a LoadException. Split the block after the
+ // StoreLocal that must come after the load.
+ DCHECK(first_insn->GetNext()->IsStoreLocal());
+ block = block->SplitBefore(first_insn->GetNext()->GetNext());
+ } else {
+ // Catch block does not load the exception. Split at the beginning to
+ // create an empty catch block.
+ block = block->SplitBefore(first_insn);
+ }
+ }
+ can_block_throw.SetBit(block->GetBlockId());
+ }
}
// Iterate over all blocks, find those covered by some TryItem and:
@@ -353,7 +376,7 @@
// (c) link the new blocks to corresponding exception handlers.
// We cannot iterate only over blocks in `branch_targets_` because switch-case
// blocks share the same dex_pc.
- for (size_t block_id = 0; block_id < num_blocks; ++block_id) {
+ for (size_t block_id = 0, e = graph_->GetBlocks().Size(); block_id < e; ++block_id) {
HBasicBlock* try_block = graph_->GetBlocks().Get(block_id);
// TryBoundary blocks are added at the end of the list and not iterated over.
@@ -365,57 +388,35 @@
continue;
}
- if (try_block->IsCatchBlock()) {
- // Catch blocks are always considered an entry point into the TryItem in
- // order to avoid splitting exceptional edges (they might not have been
- // created yet). We separate the move-exception (if present) from the
- // rest of the block and insert a TryBoundary after it, creating a
- // landing pad for the exceptional edges.
- HInstruction* first_insn = try_block->GetFirstInstruction();
- HInstruction* split_position = nullptr;
- if (first_insn->IsLoadException()) {
- // Catch block starts with a LoadException. Split the block after the
- // StoreLocal that must come after the load.
- DCHECK(first_insn->GetNext()->IsStoreLocal());
- split_position = first_insn->GetNext()->GetNext();
- } else {
- // Catch block does not obtain the exception. Split at the beginning
- // to create an empty catch block.
- split_position = first_insn;
- }
- DCHECK(split_position != nullptr);
- HBasicBlock* catch_block = try_block;
- try_block = catch_block->SplitBefore(split_position);
- SplitTryBoundaryEdge(catch_block, try_block, HTryBoundary::kEntry, code_item, *try_item);
- } else {
- // For non-catch blocks, find predecessors which are not covered by the
- // same TryItem range. Such edges enter the try block and will have
- // a TryBoundary inserted.
- for (size_t i = 0; i < try_block->GetPredecessors().Size(); ++i) {
- HBasicBlock* predecessor = try_block->GetPredecessors().Get(i);
- if (predecessor->IsSingleTryBoundary()) {
- // The edge was already split because of an exit from a neighbouring
- // TryItem. We split it again and insert an entry point.
- if (kIsDebugBuild) {
- HTryBoundary* last_insn = predecessor->GetLastInstruction()->AsTryBoundary();
- const DexFile::TryItem* predecessor_try_item =
- GetTryItem(predecessor->GetSinglePredecessor(), code_item, can_block_throw);
- DCHECK(!last_insn->IsEntry());
- DCHECK_EQ(last_insn->GetNormalFlowSuccessor(), try_block);
- DCHECK(try_block->IsFirstIndexOfPredecessor(predecessor, i));
- DCHECK_NE(try_item, predecessor_try_item);
- }
- } else if (GetTryItem(predecessor, code_item, can_block_throw) != try_item) {
- // This is an entry point into the TryItem and the edge has not been
- // split yet. That means that `predecessor` is not in a TryItem, or
- // it is in a different TryItem and we happened to iterate over this
- // block first. We split the edge and insert an entry point.
- } else {
- // Not an edge on the boundary of the try block.
- continue;
+ // Catch blocks were split earlier and cannot throw.
+ DCHECK(!try_block->IsCatchBlock());
+
+ // Find predecessors which are not covered by the same TryItem range. Such
+ // edges enter the try block and will have a TryBoundary inserted.
+ for (size_t i = 0; i < try_block->GetPredecessors().Size(); ++i) {
+ HBasicBlock* predecessor = try_block->GetPredecessors().Get(i);
+ if (predecessor->IsSingleTryBoundary()) {
+ // The edge was already split because of an exit from a neighbouring
+ // TryItem. We split it again and insert an entry point.
+ if (kIsDebugBuild) {
+ HTryBoundary* last_insn = predecessor->GetLastInstruction()->AsTryBoundary();
+ const DexFile::TryItem* predecessor_try_item =
+ GetTryItem(predecessor->GetSinglePredecessor(), code_item, can_block_throw);
+ DCHECK(!last_insn->IsEntry());
+ DCHECK_EQ(last_insn->GetNormalFlowSuccessor(), try_block);
+ DCHECK(try_block->IsFirstIndexOfPredecessor(predecessor, i));
+ DCHECK_NE(try_item, predecessor_try_item);
}
- SplitTryBoundaryEdge(predecessor, try_block, HTryBoundary::kEntry, code_item, *try_item);
+ } else if (GetTryItem(predecessor, code_item, can_block_throw) != try_item) {
+ // This is an entry point into the TryItem and the edge has not been
+ // split yet. That means that `predecessor` is not in a TryItem, or
+ // it is in a different TryItem and we happened to iterate over this
+ // block first. We split the edge and insert an entry point.
+ } else {
+ // Not an edge on the boundary of the try block.
+ continue;
}
+ SplitTryBoundaryEdge(predecessor, try_block, HTryBoundary::kEntry, code_item, *try_item);
}
// Find successors which are not covered by the same TryItem range. Such
@@ -2328,27 +2329,27 @@
}
case Instruction::CMP_LONG: {
- Binop_23x_cmp(instruction, Primitive::kPrimLong, kNoBias, dex_pc);
+ Binop_23x_cmp(instruction, Primitive::kPrimLong, ComparisonBias::kNoBias, dex_pc);
break;
}
case Instruction::CMPG_FLOAT: {
- Binop_23x_cmp(instruction, Primitive::kPrimFloat, kGtBias, dex_pc);
+ Binop_23x_cmp(instruction, Primitive::kPrimFloat, ComparisonBias::kGtBias, dex_pc);
break;
}
case Instruction::CMPG_DOUBLE: {
- Binop_23x_cmp(instruction, Primitive::kPrimDouble, kGtBias, dex_pc);
+ Binop_23x_cmp(instruction, Primitive::kPrimDouble, ComparisonBias::kGtBias, dex_pc);
break;
}
case Instruction::CMPL_FLOAT: {
- Binop_23x_cmp(instruction, Primitive::kPrimFloat, kLtBias, dex_pc);
+ Binop_23x_cmp(instruction, Primitive::kPrimFloat, ComparisonBias::kLtBias, dex_pc);
break;
}
case Instruction::CMPL_DOUBLE: {
- Binop_23x_cmp(instruction, Primitive::kPrimDouble, kLtBias, dex_pc);
+ Binop_23x_cmp(instruction, Primitive::kPrimDouble, ComparisonBias::kLtBias, dex_pc);
break;
}
diff --git a/compiler/optimizing/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc
index e3683ef..ff12329 100644
--- a/compiler/optimizing/code_generator_arm.cc
+++ b/compiler/optimizing/code_generator_arm.cc
@@ -334,7 +334,7 @@
#undef __
#define __ down_cast<ArmAssembler*>(GetAssembler())->
-inline Condition ARMCondition(IfCondition cond) {
+inline Condition ARMSignedOrFPCondition(IfCondition cond) {
switch (cond) {
case kCondEQ: return EQ;
case kCondNE: return NE;
@@ -342,24 +342,22 @@
case kCondLE: return LE;
case kCondGT: return GT;
case kCondGE: return GE;
- default:
- LOG(FATAL) << "Unknown if condition";
}
- return EQ; // Unreachable.
+ LOG(FATAL) << "Unreachable";
+ UNREACHABLE();
}
-inline Condition ARMOppositeCondition(IfCondition cond) {
+inline Condition ARMUnsignedCondition(IfCondition cond) {
switch (cond) {
- case kCondEQ: return NE;
- case kCondNE: return EQ;
- case kCondLT: return GE;
- case kCondLE: return GT;
- case kCondGT: return LE;
- case kCondGE: return LT;
- default:
- LOG(FATAL) << "Unknown if condition";
+ case kCondEQ: return EQ;
+ case kCondNE: return NE;
+ case kCondLT: return LO;
+ case kCondLE: return LS;
+ case kCondGT: return HI;
+ case kCondGE: return HS;
}
- return EQ; // Unreachable.
+ LOG(FATAL) << "Unreachable";
+ UNREACHABLE();
}
void CodeGeneratorARM::DumpCoreRegister(std::ostream& stream, int reg) const {
@@ -1008,6 +1006,142 @@
UNUSED(exit);
}
+void InstructionCodeGeneratorARM::GenerateCompareWithImmediate(Register left, int32_t right) {
+ ShifterOperand operand;
+ if (GetAssembler()->ShifterOperandCanHold(R0, left, CMP, right, &operand)) {
+ __ cmp(left, operand);
+ } else {
+ Register temp = IP;
+ __ LoadImmediate(temp, right);
+ __ cmp(left, ShifterOperand(temp));
+ }
+}
+
+void InstructionCodeGeneratorARM::GenerateFPJumps(HCondition* cond,
+ Label* true_label,
+ Label* false_label) {
+ __ vmstat(); // transfer FP status register to ARM APSR.
+ if (cond->IsFPConditionTrueIfNaN()) {
+ __ b(true_label, VS); // VS for unordered.
+ } else if (cond->IsFPConditionFalseIfNaN()) {
+ __ b(false_label, VS); // VS for unordered.
+ }
+ __ b(true_label, ARMSignedOrFPCondition(cond->GetCondition()));
+}
+
+void InstructionCodeGeneratorARM::GenerateLongComparesAndJumps(HCondition* cond,
+ Label* true_label,
+ Label* false_label) {
+ LocationSummary* locations = cond->GetLocations();
+ Location left = locations->InAt(0);
+ Location right = locations->InAt(1);
+ IfCondition if_cond = cond->GetCondition();
+
+ Register left_high = left.AsRegisterPairHigh<Register>();
+ Register left_low = left.AsRegisterPairLow<Register>();
+ IfCondition true_high_cond = if_cond;
+ IfCondition false_high_cond = cond->GetOppositeCondition();
+ Condition final_condition = ARMUnsignedCondition(if_cond);
+
+ // Set the conditions for the test, remembering that == needs to be
+ // decided using the low words.
+ switch (if_cond) {
+ case kCondEQ:
+ case kCondNE:
+ // Nothing to do.
+ break;
+ case kCondLT:
+ false_high_cond = kCondGT;
+ break;
+ case kCondLE:
+ true_high_cond = kCondLT;
+ break;
+ case kCondGT:
+ false_high_cond = kCondLT;
+ break;
+ case kCondGE:
+ true_high_cond = kCondGT;
+ break;
+ }
+ if (right.IsConstant()) {
+ int64_t value = right.GetConstant()->AsLongConstant()->GetValue();
+ int32_t val_low = Low32Bits(value);
+ int32_t val_high = High32Bits(value);
+
+ GenerateCompareWithImmediate(left_high, val_high);
+ if (if_cond == kCondNE) {
+ __ b(true_label, ARMSignedOrFPCondition(true_high_cond));
+ } else if (if_cond == kCondEQ) {
+ __ b(false_label, ARMSignedOrFPCondition(false_high_cond));
+ } else {
+ __ b(true_label, ARMSignedOrFPCondition(true_high_cond));
+ __ b(false_label, ARMSignedOrFPCondition(false_high_cond));
+ }
+ // Must be equal high, so compare the lows.
+ GenerateCompareWithImmediate(left_low, val_low);
+ } else {
+ Register right_high = right.AsRegisterPairHigh<Register>();
+ Register right_low = right.AsRegisterPairLow<Register>();
+
+ __ cmp(left_high, ShifterOperand(right_high));
+ if (if_cond == kCondNE) {
+ __ b(true_label, ARMSignedOrFPCondition(true_high_cond));
+ } else if (if_cond == kCondEQ) {
+ __ b(false_label, ARMSignedOrFPCondition(false_high_cond));
+ } else {
+ __ b(true_label, ARMSignedOrFPCondition(true_high_cond));
+ __ b(false_label, ARMSignedOrFPCondition(false_high_cond));
+ }
+ // Must be equal high, so compare the lows.
+ __ cmp(left_low, ShifterOperand(right_low));
+ }
+ // The last comparison might be unsigned.
+ __ b(true_label, final_condition);
+}
+
+void InstructionCodeGeneratorARM::GenerateCompareTestAndBranch(HIf* if_instr,
+ HCondition* condition,
+ Label* true_target,
+ Label* false_target,
+ Label* always_true_target) {
+ LocationSummary* locations = condition->GetLocations();
+ Location left = locations->InAt(0);
+ Location right = locations->InAt(1);
+
+ // We don't want true_target as a nullptr.
+ if (true_target == nullptr) {
+ true_target = always_true_target;
+ }
+ bool falls_through = (false_target == nullptr);
+
+ // FP compares don't like null false_targets.
+ if (false_target == nullptr) {
+ false_target = codegen_->GetLabelOf(if_instr->IfFalseSuccessor());
+ }
+
+ Primitive::Type type = condition->InputAt(0)->GetType();
+ switch (type) {
+ case Primitive::kPrimLong:
+ GenerateLongComparesAndJumps(condition, true_target, false_target);
+ break;
+ case Primitive::kPrimFloat:
+ __ vcmps(left.AsFpuRegister<SRegister>(), right.AsFpuRegister<SRegister>());
+ GenerateFPJumps(condition, true_target, false_target);
+ break;
+ case Primitive::kPrimDouble:
+ __ vcmpd(FromLowSToD(left.AsFpuRegisterPairLow<SRegister>()),
+ FromLowSToD(right.AsFpuRegisterPairLow<SRegister>()));
+ GenerateFPJumps(condition, true_target, false_target);
+ break;
+ default:
+ LOG(FATAL) << "Unexpected compare type " << type;
+ }
+
+ if (!falls_through) {
+ __ b(false_target);
+ }
+}
+
void InstructionCodeGeneratorARM::GenerateTestAndBranch(HInstruction* instruction,
Label* true_target,
Label* false_target,
@@ -1033,25 +1167,27 @@
} else {
// Condition has not been materialized, use its inputs as the
// comparison and its condition as the branch condition.
+ Primitive::Type type =
+ cond->IsCondition() ? cond->InputAt(0)->GetType() : Primitive::kPrimInt;
+ // Is this a long or FP comparison that has been folded into the HCondition?
+ if (type == Primitive::kPrimLong || Primitive::IsFloatingPointType(type)) {
+ // Generate the comparison directly.
+ GenerateCompareTestAndBranch(instruction->AsIf(), cond->AsCondition(),
+ true_target, false_target, always_true_target);
+ return;
+ }
+
LocationSummary* locations = cond->GetLocations();
DCHECK(locations->InAt(0).IsRegister()) << locations->InAt(0);
Register left = locations->InAt(0).AsRegister<Register>();
- if (locations->InAt(1).IsRegister()) {
- __ cmp(left, ShifterOperand(locations->InAt(1).AsRegister<Register>()));
+ Location right = locations->InAt(1);
+ if (right.IsRegister()) {
+ __ cmp(left, ShifterOperand(right.AsRegister<Register>()));
} else {
- DCHECK(locations->InAt(1).IsConstant());
- HConstant* constant = locations->InAt(1).GetConstant();
- int32_t value = CodeGenerator::GetInt32ValueOf(constant);
- ShifterOperand operand;
- if (GetAssembler()->ShifterOperandCanHold(R0, left, CMP, value, &operand)) {
- __ cmp(left, operand);
- } else {
- Register temp = IP;
- __ LoadImmediate(temp, value);
- __ cmp(left, ShifterOperand(temp));
- }
+ DCHECK(right.IsConstant());
+ GenerateCompareWithImmediate(left, CodeGenerator::GetInt32ValueOf(right.GetConstant()));
}
- __ b(true_target, ARMCondition(cond->AsCondition()->GetCondition()));
+ __ b(true_target, ARMSignedOrFPCondition(cond->AsCondition()->GetCondition()));
}
}
if (false_target != nullptr) {
@@ -1104,37 +1240,88 @@
void LocationsBuilderARM::VisitCondition(HCondition* cond) {
LocationSummary* locations =
new (GetGraph()->GetArena()) LocationSummary(cond, LocationSummary::kNoCall);
- locations->SetInAt(0, Location::RequiresRegister());
- locations->SetInAt(1, Location::RegisterOrConstant(cond->InputAt(1)));
- if (cond->NeedsMaterialization()) {
- locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
+ // Handle the long/FP comparisons made in instruction simplification.
+ switch (cond->InputAt(0)->GetType()) {
+ case Primitive::kPrimLong:
+ locations->SetInAt(0, Location::RequiresRegister());
+ locations->SetInAt(1, Location::RegisterOrConstant(cond->InputAt(1)));
+ if (cond->NeedsMaterialization()) {
+ locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
+ }
+ break;
+
+ case Primitive::kPrimFloat:
+ case Primitive::kPrimDouble:
+ locations->SetInAt(0, Location::RequiresFpuRegister());
+ locations->SetInAt(1, Location::RequiresFpuRegister());
+ if (cond->NeedsMaterialization()) {
+ locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
+ }
+ break;
+
+ default:
+ locations->SetInAt(0, Location::RequiresRegister());
+ locations->SetInAt(1, Location::RegisterOrConstant(cond->InputAt(1)));
+ if (cond->NeedsMaterialization()) {
+ locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
+ }
}
}
void InstructionCodeGeneratorARM::VisitCondition(HCondition* cond) {
- if (!cond->NeedsMaterialization()) return;
- LocationSummary* locations = cond->GetLocations();
- Register left = locations->InAt(0).AsRegister<Register>();
-
- if (locations->InAt(1).IsRegister()) {
- __ cmp(left, ShifterOperand(locations->InAt(1).AsRegister<Register>()));
- } else {
- DCHECK(locations->InAt(1).IsConstant());
- int32_t value = CodeGenerator::GetInt32ValueOf(locations->InAt(1).GetConstant());
- ShifterOperand operand;
- if (GetAssembler()->ShifterOperandCanHold(R0, left, CMP, value, &operand)) {
- __ cmp(left, operand);
- } else {
- Register temp = IP;
- __ LoadImmediate(temp, value);
- __ cmp(left, ShifterOperand(temp));
- }
+ if (!cond->NeedsMaterialization()) {
+ return;
}
- __ it(ARMCondition(cond->GetCondition()), kItElse);
- __ mov(locations->Out().AsRegister<Register>(), ShifterOperand(1),
- ARMCondition(cond->GetCondition()));
- __ mov(locations->Out().AsRegister<Register>(), ShifterOperand(0),
- ARMOppositeCondition(cond->GetCondition()));
+
+ LocationSummary* locations = cond->GetLocations();
+ Location left = locations->InAt(0);
+ Location right = locations->InAt(1);
+ Register out = locations->Out().AsRegister<Register>();
+ Label true_label, false_label;
+
+ switch (cond->InputAt(0)->GetType()) {
+ default: {
+ // Integer case.
+ if (right.IsRegister()) {
+ __ cmp(left.AsRegister<Register>(), ShifterOperand(right.AsRegister<Register>()));
+ } else {
+ DCHECK(right.IsConstant());
+ GenerateCompareWithImmediate(left.AsRegister<Register>(),
+ CodeGenerator::GetInt32ValueOf(right.GetConstant()));
+ }
+ __ it(ARMSignedOrFPCondition(cond->GetCondition()), kItElse);
+ __ mov(locations->Out().AsRegister<Register>(), ShifterOperand(1),
+ ARMSignedOrFPCondition(cond->GetCondition()));
+ __ mov(locations->Out().AsRegister<Register>(), ShifterOperand(0),
+ ARMSignedOrFPCondition(cond->GetOppositeCondition()));
+ return;
+ }
+ case Primitive::kPrimLong:
+ GenerateLongComparesAndJumps(cond, &true_label, &false_label);
+ break;
+ case Primitive::kPrimFloat:
+ __ vcmps(left.AsFpuRegister<SRegister>(), right.AsFpuRegister<SRegister>());
+ GenerateFPJumps(cond, &true_label, &false_label);
+ break;
+ case Primitive::kPrimDouble:
+ __ vcmpd(FromLowSToD(left.AsFpuRegisterPairLow<SRegister>()),
+ FromLowSToD(right.AsFpuRegisterPairLow<SRegister>()));
+ GenerateFPJumps(cond, &true_label, &false_label);
+ break;
+ }
+
+ // Convert the jumps into the result.
+ Label done_label;
+
+ // False case: result = 0.
+ __ Bind(&false_label);
+ __ LoadImmediate(out, 0);
+ __ b(&done_label);
+
+ // True case: result = 1.
+ __ Bind(&true_label);
+ __ LoadImmediate(out, 1);
+ __ Bind(&done_label);
}
void LocationsBuilderARM::VisitEqual(HEqual* comp) {
@@ -2913,7 +3100,7 @@
ShifterOperand(right.AsRegisterPairHigh<Register>())); // Signed compare.
__ b(&less, LT);
__ b(&greater, GT);
- // Do LoadImmediate before any `cmp`, as LoadImmediate might affect the status flags.
+ // Do LoadImmediate before the last `cmp`, as LoadImmediate might affect the status flags.
__ LoadImmediate(out, 0);
__ cmp(left.AsRegisterPairLow<Register>(),
ShifterOperand(right.AsRegisterPairLow<Register>())); // Unsigned compare.
@@ -2936,7 +3123,7 @@
LOG(FATAL) << "Unexpected compare type " << type;
}
__ b(&done, EQ);
- __ b(&less, CC); // CC is for both: unsigned compare for longs and 'less than' for floats.
+ __ b(&less, LO); // LO is for both: unsigned compare for longs and 'less than' for floats.
__ Bind(&greater);
__ LoadImmediate(out, 1);
@@ -3710,7 +3897,7 @@
Register length = locations->InAt(1).AsRegister<Register>();
__ cmp(index, ShifterOperand(length));
- __ b(slow_path->GetEntryLabel(), CS);
+ __ b(slow_path->GetEntryLabel(), HS);
}
void CodeGeneratorARM::MarkGCCard(Register temp,
diff --git a/compiler/optimizing/code_generator_arm.h b/compiler/optimizing/code_generator_arm.h
index 1d10293..53bd766 100644
--- a/compiler/optimizing/code_generator_arm.h
+++ b/compiler/optimizing/code_generator_arm.h
@@ -207,6 +207,14 @@
Label* true_target,
Label* false_target,
Label* always_true_target);
+ void GenerateCompareWithImmediate(Register left, int32_t right);
+ void GenerateCompareTestAndBranch(HIf* if_instr,
+ HCondition* condition,
+ Label* true_target,
+ Label* false_target,
+ Label* always_true_target);
+ void GenerateFPJumps(HCondition* cond, Label* true_label, Label* false_label);
+ void GenerateLongComparesAndJumps(HCondition* cond, Label* true_label, Label* false_label);
void DivRemOneOrMinusOne(HBinaryOperation* instruction);
void DivRemByPowerOfTwo(HBinaryOperation* instruction);
void GenerateDivRemWithAnyConstant(HBinaryOperation* instruction);
diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc
index be71443..fd4bd18 100644
--- a/compiler/optimizing/code_generator_x86.cc
+++ b/compiler/optimizing/code_generator_x86.cc
@@ -344,7 +344,7 @@
#undef __
#define __ down_cast<X86Assembler*>(GetAssembler())->
-inline Condition X86Condition(IfCondition cond) {
+inline Condition X86SignedCondition(IfCondition cond) {
switch (cond) {
case kCondEQ: return kEqual;
case kCondNE: return kNotEqual;
@@ -352,10 +352,22 @@
case kCondLE: return kLessEqual;
case kCondGT: return kGreater;
case kCondGE: return kGreaterEqual;
- default:
- LOG(FATAL) << "Unknown if condition";
}
- return kEqual;
+ LOG(FATAL) << "Unreachable";
+ UNREACHABLE();
+}
+
+inline Condition X86UnsignedOrFPCondition(IfCondition cond) {
+ switch (cond) {
+ case kCondEQ: return kEqual;
+ case kCondNE: return kNotEqual;
+ case kCondLT: return kBelow;
+ case kCondLE: return kBelowEqual;
+ case kCondGT: return kAbove;
+ case kCondGE: return kAboveEqual;
+ }
+ LOG(FATAL) << "Unreachable";
+ UNREACHABLE();
}
void CodeGeneratorX86::DumpCoreRegister(std::ostream& stream, int reg) const {
@@ -892,46 +904,12 @@
void InstructionCodeGeneratorX86::GenerateFPJumps(HCondition* cond,
Label* true_label,
Label* false_label) {
- bool gt_bias = cond->IsGtBias();
- IfCondition if_cond = cond->GetCondition();
- Condition ccode = X86Condition(if_cond);
- switch (if_cond) {
- case kCondEQ:
- if (!gt_bias) {
- __ j(kParityEven, false_label);
- }
- break;
- case kCondNE:
- if (!gt_bias) {
- __ j(kParityEven, true_label);
- }
- break;
- case kCondLT:
- if (gt_bias) {
- __ j(kParityEven, false_label);
- }
- ccode = kBelow;
- break;
- case kCondLE:
- if (gt_bias) {
- __ j(kParityEven, false_label);
- }
- ccode = kBelowEqual;
- break;
- case kCondGT:
- if (gt_bias) {
- __ j(kParityEven, true_label);
- }
- ccode = kAbove;
- break;
- case kCondGE:
- if (gt_bias) {
- __ j(kParityEven, true_label);
- }
- ccode = kAboveEqual;
- break;
+ if (cond->IsFPConditionTrueIfNaN()) {
+ __ j(kUnordered, true_label);
+ } else if (cond->IsFPConditionFalseIfNaN()) {
+ __ j(kUnordered, false_label);
}
- __ j(ccode, true_label);
+ __ j(X86UnsignedOrFPCondition(cond->GetCondition()), true_label);
}
void InstructionCodeGeneratorX86::GenerateLongComparesAndJumps(HCondition* cond,
@@ -942,43 +920,37 @@
Location right = locations->InAt(1);
IfCondition if_cond = cond->GetCondition();
- Register left_low = left.AsRegisterPairLow<Register>();
Register left_high = left.AsRegisterPairHigh<Register>();
+ Register left_low = left.AsRegisterPairLow<Register>();
IfCondition true_high_cond = if_cond;
IfCondition false_high_cond = cond->GetOppositeCondition();
- Condition final_condition = X86Condition(if_cond);
+ Condition final_condition = X86UnsignedOrFPCondition(if_cond);
// Set the conditions for the test, remembering that == needs to be
// decided using the low words.
switch (if_cond) {
case kCondEQ:
- false_high_cond = kCondNE;
- break;
case kCondNE:
- false_high_cond = kCondEQ;
+ // Nothing to do.
break;
case kCondLT:
false_high_cond = kCondGT;
- final_condition = kBelow;
break;
case kCondLE:
true_high_cond = kCondLT;
- final_condition = kBelowEqual;
break;
case kCondGT:
false_high_cond = kCondLT;
- final_condition = kAbove;
break;
case kCondGE:
true_high_cond = kCondGT;
- final_condition = kAboveEqual;
break;
}
if (right.IsConstant()) {
int64_t value = right.GetConstant()->AsLongConstant()->GetValue();
- int32_t val_low = Low32Bits(value);
int32_t val_high = High32Bits(value);
+ int32_t val_low = Low32Bits(value);
if (val_high == 0) {
__ testl(left_high, left_high);
@@ -986,12 +958,12 @@
__ cmpl(left_high, Immediate(val_high));
}
if (if_cond == kCondNE) {
- __ j(X86Condition(true_high_cond), true_label);
+ __ j(X86SignedCondition(true_high_cond), true_label);
} else if (if_cond == kCondEQ) {
- __ j(X86Condition(false_high_cond), false_label);
+ __ j(X86SignedCondition(false_high_cond), false_label);
} else {
- __ j(X86Condition(true_high_cond), true_label);
- __ j(X86Condition(false_high_cond), false_label);
+ __ j(X86SignedCondition(true_high_cond), true_label);
+ __ j(X86SignedCondition(false_high_cond), false_label);
}
// Must be equal high, so compare the lows.
if (val_low == 0) {
@@ -1000,17 +972,17 @@
__ cmpl(left_low, Immediate(val_low));
}
} else {
- Register right_low = right.AsRegisterPairLow<Register>();
Register right_high = right.AsRegisterPairHigh<Register>();
+ Register right_low = right.AsRegisterPairLow<Register>();
__ cmpl(left_high, right_high);
if (if_cond == kCondNE) {
- __ j(X86Condition(true_high_cond), true_label);
+ __ j(X86SignedCondition(true_high_cond), true_label);
} else if (if_cond == kCondEQ) {
- __ j(X86Condition(false_high_cond), false_label);
+ __ j(X86SignedCondition(false_high_cond), false_label);
} else {
- __ j(X86Condition(true_high_cond), true_label);
- __ j(X86Condition(false_high_cond), false_label);
+ __ j(X86SignedCondition(true_high_cond), true_label);
+ __ j(X86SignedCondition(false_high_cond), false_label);
}
// Must be equal high, so compare the lows.
__ cmpl(left_low, right_low);
@@ -1045,12 +1017,10 @@
GenerateLongComparesAndJumps(condition, true_target, false_target);
break;
case Primitive::kPrimFloat:
- DCHECK(right.IsFpuRegister());
__ ucomiss(left.AsFpuRegister<XmmRegister>(), right.AsFpuRegister<XmmRegister>());
GenerateFPJumps(condition, true_target, false_target);
break;
case Primitive::kPrimDouble:
- DCHECK(right.IsFpuRegister());
__ ucomisd(left.AsFpuRegister<XmmRegister>(), right.AsFpuRegister<XmmRegister>());
GenerateFPJumps(condition, true_target, false_target);
break;
@@ -1080,7 +1050,7 @@
DCHECK_EQ(cond_value, 0);
}
} else {
- bool materialized =
+ bool is_materialized =
!cond->IsCondition() || cond->AsCondition()->NeedsMaterialization();
// Moves do not affect the eflags register, so if the condition is
// evaluated just before the if, we don't need to evaluate it
@@ -1089,8 +1059,8 @@
Primitive::Type type = cond->IsCondition() ? cond->InputAt(0)->GetType() : Primitive::kPrimInt;
bool eflags_set = cond->IsCondition()
&& cond->AsCondition()->IsBeforeWhenDisregardMoves(instruction)
- && type == Primitive::kPrimInt;
- if (materialized) {
+ && (type != Primitive::kPrimLong && !Primitive::IsFloatingPointType(type));
+ if (is_materialized) {
if (!eflags_set) {
// Materialized condition, compare against 0.
Location lhs = instruction->GetLocations()->InAt(0);
@@ -1101,9 +1071,12 @@
}
__ j(kNotEqual, true_target);
} else {
- __ j(X86Condition(cond->AsCondition()->GetCondition()), true_target);
+ __ j(X86SignedCondition(cond->AsCondition()->GetCondition()), true_target);
}
} else {
+ // Condition has not been materialized, use its inputs as the
+ // comparison and its condition as the branch condition.
+
// Is this a long or FP comparison that has been folded into the HCondition?
if (type == Primitive::kPrimLong || Primitive::IsFloatingPointType(type)) {
// Generate the comparison directly.
@@ -1114,6 +1087,7 @@
always_true_target);
return;
}
+
Location lhs = cond->GetLocations()->InAt(0);
Location rhs = cond->GetLocations()->InAt(1);
// LHS is guaranteed to be in a register (see
@@ -1130,7 +1104,7 @@
} else {
__ cmpl(lhs.AsRegister<Register>(), Address(ESP, rhs.GetStackIndex()));
}
- __ j(X86Condition(cond->AsCondition()->GetCondition()), true_target);
+ __ j(X86SignedCondition(cond->AsCondition()->GetCondition()), true_target);
}
}
if (false_target != nullptr) {
@@ -1288,7 +1262,7 @@
} else {
__ cmpl(lhs.AsRegister<Register>(), Address(ESP, rhs.GetStackIndex()));
}
- __ setb(X86Condition(cond->GetCondition()), reg);
+ __ setb(X86SignedCondition(cond->GetCondition()), reg);
return;
}
case Primitive::kPrimLong:
@@ -1307,12 +1281,12 @@
// Convert the jumps into the result.
Label done_label;
- // false case: result = 0;
+ // False case: result = 0.
__ Bind(&false_label);
__ xorl(reg, reg);
__ jmp(&done_label);
- // True case: result = 1
+ // True case: result = 1.
__ Bind(&true_label);
__ movl(reg, Immediate(1));
__ Bind(&done_label);
diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc
index ddaa60d..ae7bcc8 100644
--- a/compiler/optimizing/code_generator_x86_64.cc
+++ b/compiler/optimizing/code_generator_x86_64.cc
@@ -363,7 +363,7 @@
#undef __
#define __ down_cast<X86_64Assembler*>(GetAssembler())->
-inline Condition X86_64Condition(IfCondition cond) {
+inline Condition X86_64IntegerCondition(IfCondition cond) {
switch (cond) {
case kCondEQ: return kEqual;
case kCondNE: return kNotEqual;
@@ -371,10 +371,22 @@
case kCondLE: return kLessEqual;
case kCondGT: return kGreater;
case kCondGE: return kGreaterEqual;
- default:
- LOG(FATAL) << "Unknown if condition";
}
- return kEqual;
+ LOG(FATAL) << "Unreachable";
+ UNREACHABLE();
+}
+
+inline Condition X86_64FPCondition(IfCondition cond) {
+ switch (cond) {
+ case kCondEQ: return kEqual;
+ case kCondNE: return kNotEqual;
+ case kCondLT: return kBelow;
+ case kCondLE: return kBelowEqual;
+ case kCondGT: return kAbove;
+ case kCondGE: return kAboveEqual;
+ };
+ LOG(FATAL) << "Unreachable";
+ UNREACHABLE();
}
void CodeGeneratorX86_64::GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invoke,
@@ -836,46 +848,12 @@
void InstructionCodeGeneratorX86_64::GenerateFPJumps(HCondition* cond,
Label* true_label,
Label* false_label) {
- bool gt_bias = cond->IsGtBias();
- IfCondition if_cond = cond->GetCondition();
- Condition ccode = X86_64Condition(if_cond);
- switch (if_cond) {
- case kCondEQ:
- if (!gt_bias) {
- __ j(kParityEven, false_label);
- }
- break;
- case kCondNE:
- if (!gt_bias) {
- __ j(kParityEven, true_label);
- }
- break;
- case kCondLT:
- if (gt_bias) {
- __ j(kParityEven, false_label);
- }
- ccode = kBelow;
- break;
- case kCondLE:
- if (gt_bias) {
- __ j(kParityEven, false_label);
- }
- ccode = kBelowEqual;
- break;
- case kCondGT:
- if (gt_bias) {
- __ j(kParityEven, true_label);
- }
- ccode = kAbove;
- break;
- case kCondGE:
- if (gt_bias) {
- __ j(kParityEven, true_label);
- }
- ccode = kAboveEqual;
- break;
+ if (cond->IsFPConditionTrueIfNaN()) {
+ __ j(kUnordered, true_label);
+ } else if (cond->IsFPConditionFalseIfNaN()) {
+ __ j(kUnordered, false_label);
}
- __ j(ccode, true_label);
+ __ j(X86_64FPCondition(cond->GetCondition()), true_label);
}
void InstructionCodeGeneratorX86_64::GenerateCompareTestAndBranch(HIf* if_instr,
@@ -911,7 +889,7 @@
__ cmpq(left_reg, Immediate(static_cast<int32_t>(value)));
}
} else {
- // Value won't fit in an 32-bit integer.
+ // Value won't fit in a 32-bit integer.
__ cmpq(left_reg, codegen_->LiteralInt64Address(value));
}
} else if (right.IsDoubleStackSlot()) {
@@ -919,7 +897,7 @@
} else {
__ cmpq(left_reg, right.AsRegister<CpuRegister>());
}
- __ j(X86_64Condition(condition->GetCondition()), true_target);
+ __ j(X86_64IntegerCondition(condition->GetCondition()), true_target);
break;
}
case Primitive::kPrimFloat: {
@@ -978,7 +956,7 @@
DCHECK_EQ(cond_value, 0);
}
} else {
- bool materialized =
+ bool is_materialized =
!cond->IsCondition() || cond->AsCondition()->NeedsMaterialization();
// Moves do not affect the eflags register, so if the condition is
// evaluated just before the if, we don't need to evaluate it
@@ -989,7 +967,7 @@
&& cond->AsCondition()->IsBeforeWhenDisregardMoves(instruction)
&& !Primitive::IsFloatingPointType(type);
- if (materialized) {
+ if (is_materialized) {
if (!eflags_set) {
// Materialized condition, compare against 0.
Location lhs = instruction->GetLocations()->InAt(0);
@@ -1001,16 +979,20 @@
}
__ j(kNotEqual, true_target);
} else {
- __ j(X86_64Condition(cond->AsCondition()->GetCondition()), true_target);
+ __ j(X86_64IntegerCondition(cond->AsCondition()->GetCondition()), true_target);
}
} else {
+ // Condition has not been materialized, use its inputs as the
+ // comparison and its condition as the branch condition.
+
// Is this a long or FP comparison that has been folded into the HCondition?
if (type == Primitive::kPrimLong || Primitive::IsFloatingPointType(type)) {
- // Generate the comparison directly
+ // Generate the comparison directly.
GenerateCompareTestAndBranch(instruction->AsIf(), cond->AsCondition(),
true_target, false_target, always_true_target);
return;
}
+
Location lhs = cond->GetLocations()->InAt(0);
Location rhs = cond->GetLocations()->InAt(1);
if (rhs.IsRegister()) {
@@ -1026,7 +1008,7 @@
__ cmpl(lhs.AsRegister<CpuRegister>(),
Address(CpuRegister(RSP), rhs.GetStackIndex()));
}
- __ j(X86_64Condition(cond->AsCondition()->GetCondition()), true_target);
+ __ j(X86_64IntegerCondition(cond->AsCondition()->GetCondition()), true_target);
}
}
if (false_target != nullptr) {
@@ -1175,7 +1157,7 @@
} else {
__ cmpl(lhs.AsRegister<CpuRegister>(), Address(CpuRegister(RSP), rhs.GetStackIndex()));
}
- __ setcc(X86_64Condition(cond->GetCondition()), reg);
+ __ setcc(X86_64IntegerCondition(cond->GetCondition()), reg);
return;
case Primitive::kPrimLong:
// Clear output register: setcc only sets the low byte.
@@ -1198,7 +1180,7 @@
} else {
__ cmpq(lhs.AsRegister<CpuRegister>(), Address(CpuRegister(RSP), rhs.GetStackIndex()));
}
- __ setcc(X86_64Condition(cond->GetCondition()), reg);
+ __ setcc(X86_64IntegerCondition(cond->GetCondition()), reg);
return;
case Primitive::kPrimFloat: {
XmmRegister lhs_reg = lhs.AsFpuRegister<XmmRegister>();
@@ -1231,12 +1213,12 @@
// Convert the jumps into the result.
Label done_label;
- // false case: result = 0;
+ // False case: result = 0.
__ Bind(&false_label);
__ xorl(reg, reg);
__ jmp(&done_label);
- // True case: result = 1
+ // True case: result = 1.
__ Bind(&true_label);
__ movl(reg, Immediate(1));
__ Bind(&done_label);
diff --git a/compiler/optimizing/instruction_simplifier.cc b/compiler/optimizing/instruction_simplifier.cc
index 337cf5b..017b678 100644
--- a/compiler/optimizing/instruction_simplifier.cc
+++ b/compiler/optimizing/instruction_simplifier.cc
@@ -517,10 +517,10 @@
void InstructionSimplifierVisitor::VisitCondition(HCondition* condition) {
// Try to fold an HCompare into this HCondition.
- // This simplification is currently only supported on x86 and x86_64.
- // TODO: Implement it for ARM, ARM64 and MIPS64.
+ // This simplification is currently only supported on x86, x86_64 and ARM.
+ // TODO: Implement it for ARM64 and MIPS64.
InstructionSet instruction_set = GetGraph()->GetInstructionSet();
- if (instruction_set != kX86 && instruction_set != kX86_64) {
+ if (instruction_set != kX86 && instruction_set != kX86_64 && instruction_set != kThumb2) {
return;
}
diff --git a/compiler/optimizing/instruction_simplifier.h b/compiler/optimizing/instruction_simplifier.h
index faee2dd..cc4b6f6 100644
--- a/compiler/optimizing/instruction_simplifier.h
+++ b/compiler/optimizing/instruction_simplifier.h
@@ -31,7 +31,7 @@
InstructionSimplifier(HGraph* graph,
OptimizingCompilerStats* stats = nullptr,
const char* name = kInstructionSimplifierPassName)
- : HOptimization(graph, name, stats) {}
+ : HOptimization(graph, name, stats) {}
static constexpr const char* kInstructionSimplifierPassName = "instruction_simplifier";
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index 59255d1..7d26062 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -2149,7 +2149,7 @@
// The comparison bias applies for floating point operations and indicates how NaN
// comparisons are treated:
-enum ComparisonBias {
+enum class ComparisonBias {
kNoBias, // bias is not applicable (i.e. for long operation)
kGtBias, // return 1 for NaN comparisons
kLtBias, // return -1 for NaN comparisons
@@ -2160,7 +2160,7 @@
HCondition(HInstruction* first, HInstruction* second)
: HBinaryOperation(Primitive::kPrimBoolean, first, second),
needs_materialization_(true),
- bias_(kNoBias) {}
+ bias_(ComparisonBias::kNoBias) {}
bool NeedsMaterialization() const { return needs_materialization_; }
void ClearNeedsMaterialization() { needs_materialization_ = false; }
@@ -2175,7 +2175,7 @@
virtual IfCondition GetOppositeCondition() const = 0;
- bool IsGtBias() { return bias_ == kGtBias; }
+ bool IsGtBias() const { return bias_ == ComparisonBias::kGtBias; }
void SetBias(ComparisonBias bias) { bias_ = bias; }
@@ -2183,6 +2183,18 @@
return bias_ == other->AsCondition()->bias_;
}
+ bool IsFPConditionTrueIfNaN() const {
+ DCHECK(Primitive::IsFloatingPointType(InputAt(0)->GetType()));
+ IfCondition if_cond = GetCondition();
+ return IsGtBias() ? ((if_cond == kCondGT) || (if_cond == kCondGE)) : (if_cond == kCondNE);
+ }
+
+ bool IsFPConditionFalseIfNaN() const {
+ DCHECK(Primitive::IsFloatingPointType(InputAt(0)->GetType()));
+ IfCondition if_cond = GetCondition();
+ return IsGtBias() ? ((if_cond == kCondLT) || (if_cond == kCondLE)) : (if_cond == kCondEQ);
+ }
+
private:
// For register allocation purposes, returns whether this instruction needs to be
// materialized (that is, not just be in the processor flags).
@@ -2390,7 +2402,7 @@
ComparisonBias GetBias() const { return bias_; }
- bool IsGtBias() { return bias_ == kGtBias; }
+ bool IsGtBias() { return bias_ == ComparisonBias::kGtBias; }
uint32_t GetDexPc() const { return dex_pc_; }
diff --git a/compiler/utils/arm/constants_arm.h b/compiler/utils/arm/constants_arm.h
index 1513296..6b4daed 100644
--- a/compiler/utils/arm/constants_arm.h
+++ b/compiler/utils/arm/constants_arm.h
@@ -32,8 +32,9 @@
// Defines constants and accessor classes to assemble, disassemble and
// simulate ARM instructions.
//
-// Section references in the code refer to the "ARM Architecture Reference
-// Manual" from July 2005 (available at http://www.arm.com/miscPDFs/14128.pdf)
+// Section references in the code refer to the "ARM Architecture
+// Reference Manual ARMv7-A and ARMv7-R edition", issue C.b (24 July
+// 2012).
//
// Constants for specific fields are defined in their respective named enums.
// General constants are in an anonymous enum in class Instr.
@@ -97,26 +98,32 @@
std::ostream& operator<<(std::ostream& os, const DRegister& rhs);
-// Values for the condition field as defined in section A3.2.
+// Values for the condition field as defined in Table A8-1 "Condition
+// codes" (refer to Section A8.3 "Conditional execution").
enum Condition { // private marker to avoid generate-operator-out.py from processing.
kNoCondition = -1,
- EQ = 0, // equal
- NE = 1, // not equal
- CS = 2, // carry set/unsigned higher or same
- CC = 3, // carry clear/unsigned lower
- MI = 4, // minus/negative
- PL = 5, // plus/positive or zero
- VS = 6, // overflow
- VC = 7, // no overflow
- HI = 8, // unsigned higher
- LS = 9, // unsigned lower or same
- GE = 10, // signed greater than or equal
- LT = 11, // signed less than
- GT = 12, // signed greater than
- LE = 13, // signed less than or equal
- AL = 14, // always (unconditional)
- kSpecialCondition = 15, // special condition (refer to section A3.2.1)
+ // Meaning (integer) | Meaning (floating-point)
+ // ---------------------------------------+-----------------------------------------
+ EQ = 0, // Equal | Equal
+ NE = 1, // Not equal | Not equal, or unordered
+ CS = 2, // Carry set | Greater than, equal, or unordered
+ CC = 3, // Carry clear | Less than
+ MI = 4, // Minus, negative | Less than
+ PL = 5, // Plus, positive or zero | Greater than, equal, or unordered
+ VS = 6, // Overflow | Unordered (i.e. at least one NaN operand)
+ VC = 7, // No overflow | Not unordered
+ HI = 8, // Unsigned higher | Greater than, or unordered
+ LS = 9, // Unsigned lower or same | Less than or equal
+ GE = 10, // Signed greater than or equal | Greater than or equal
+ LT = 11, // Signed less than | Less than, or unordered
+ GT = 12, // Signed greater than | Greater than
+ LE = 13, // Signed less than or equal | Less than, equal, or unordered
+ AL = 14, // Always (unconditional) | Always (unconditional)
+ kSpecialCondition = 15, // Special condition (refer to Section A8.3 "Conditional execution").
kMaxCondition = 16,
+
+ HS = CS, // HS (unsigned higher or same) is a synonym for CS.
+ LO = CC // LO (unsigned lower) is a synonym for CC.
};
std::ostream& operator<<(std::ostream& os, const Condition& rhs);
diff --git a/dexdump/dexdump.cc b/dexdump/dexdump.cc
index f55dccd..84c465f 100644
--- a/dexdump/dexdump.cc
+++ b/dexdump/dexdump.cc
@@ -52,7 +52,7 @@
struct Options gOptions;
/*
- * Output file. Defaults to stdout, but tests can modify.
+ * Output file. Defaults to stdout.
*/
FILE* gOutFile = stdout;
@@ -63,8 +63,6 @@
typedef uint16_t u2;
typedef uint32_t u4;
typedef uint64_t u8;
-typedef int8_t s1;
-typedef int16_t s2;
typedef int32_t s4;
typedef int64_t s8;
@@ -1274,23 +1272,14 @@
return -1;
}
- // Determine if opening file yielded a single dex file. On failure,
- // the parse error message of the original dexdump utility is shown.
- //
- // TODO(ajcbik): this restriction is not really needed, but kept
- // for now to stay close to original dexdump; we can
- // later relax this!
- //
- if (dex_files.size() != 1) {
- fprintf(stderr, "ERROR: DEX parse failed\n");
- return -1;
- }
-
- // Success. Either report checksum verification or process dex file.
+ // Success. Either report checksum verification or process
+ // all dex files found in given file.
if (gOptions.checksumOnly) {
fprintf(gOutFile, "Checksum verified\n");
} else {
- processDexFile(fileName, dex_files[0].get());
+ for (size_t i = 0; i < dex_files.size(); i++) {
+ processDexFile(fileName, dex_files[i].get());
+ }
}
return 0;
}
diff --git a/dexdump/dexdump_main.cc b/dexdump/dexdump_main.cc
index 756f879..9be0922 100644
--- a/dexdump/dexdump_main.cc
+++ b/dexdump/dexdump_main.cc
@@ -108,8 +108,8 @@
default:
wantUsage = true;
break;
- }
- }
+ } // switch
+ } // while
// Detect early problems.
if (optind == argc) {
@@ -138,7 +138,7 @@
int result = 0;
while (optind < argc) {
result |= processFile(argv[optind++]);
- }
+ } // while
return result != 0;
}
diff --git a/dexlist/dexlist.cc b/dexlist/dexlist.cc
index d7c0e4c..d8fd242 100644
--- a/dexlist/dexlist.cc
+++ b/dexlist/dexlist.cc
@@ -51,11 +51,8 @@
* Data types that match the definitions in the VM specification.
*/
typedef uint8_t u1;
-typedef uint16_t u2;
typedef uint32_t u4;
typedef uint64_t u8;
-typedef int32_t s4;
-typedef int64_t s8;
/*
* Returns a newly-allocated string for the "dot version" of the class
@@ -193,23 +190,15 @@
return -1;
}
- // Determine if opening file yielded a single dex file.
- //
- // TODO(ajcbik): this restriction is not really needed, but kept
- // for now to stay close to original dexlist; we can
- // later relax this!
- //
- if (dex_files.size() != 1) {
- fprintf(stderr, "ERROR: DEX parse failed\n");
- return -1;
- }
- const DexFile* pDexFile = dex_files[0].get();
-
- // Success. Iterate over all classes.
+ // Success. Iterate over all dex files found in given file.
fprintf(gOutFile, "#%s\n", fileName);
- const u4 classDefsSize = pDexFile->GetHeader().class_defs_size_;
- for (u4 idx = 0; idx < classDefsSize; idx++) {
- dumpClass(pDexFile, idx);
+ for (size_t i = 0; i < dex_files.size(); i++) {
+ // Iterate over all classes in one dex file.
+ const DexFile* pDexFile = dex_files[i].get();
+ const u4 classDefsSize = pDexFile->GetHeader().class_defs_size_;
+ for (u4 idx = 0; idx < classDefsSize; idx++) {
+ dumpClass(pDexFile, idx);
+ }
}
return 0;
}
@@ -246,7 +235,7 @@
gOptions.outputFileName = optarg;
break;
case 'm':
- // If -m X.Y.Z is given, then find all instances of the
+ // If -m x.y.z is given, then find all instances of the
// fully-qualified method name. This isn't really what
// dexlist is for, but it's easy to do it here.
{
diff --git a/runtime/arch/mips/entrypoints_init_mips.cc b/runtime/arch/mips/entrypoints_init_mips.cc
index ff04106..09a018e 100644
--- a/runtime/arch/mips/entrypoints_init_mips.cc
+++ b/runtime/arch/mips/entrypoints_init_mips.cc
@@ -267,6 +267,10 @@
qpoints->pThrowStackOverflow = art_quick_throw_stack_overflow;
static_assert(!IsDirectEntrypoint(kQuickThrowStackOverflow), "Non-direct C stub marked direct.");
+ // Deoptimize
+ qpoints->pDeoptimize = art_quick_deoptimize;
+ static_assert(!IsDirectEntrypoint(kQuickDeoptimize), "Non-direct C stub marked direct.");
+
// Atomic 64-bit load/store
qpoints->pA64Load = QuasiAtomic::Read64;
static_assert(IsDirectEntrypoint(kQuickA64Load), "Non-direct C stub marked direct.");
diff --git a/runtime/arch/mips64/entrypoints_init_mips64.cc b/runtime/arch/mips64/entrypoints_init_mips64.cc
index 321c27b..4904af9 100644
--- a/runtime/arch/mips64/entrypoints_init_mips64.cc
+++ b/runtime/arch/mips64/entrypoints_init_mips64.cc
@@ -176,6 +176,9 @@
qpoints->pThrowNullPointer = art_quick_throw_null_pointer_exception;
qpoints->pThrowStackOverflow = art_quick_throw_stack_overflow;
+ // Deoptimize
+ qpoints->pDeoptimize = art_quick_deoptimize;
+
// TODO - use lld/scd instructions for Mips64
// Atomic 64-bit load/store
qpoints->pA64Load = QuasiAtomic::Read64;
diff --git a/runtime/base/logging.h b/runtime/base/logging.h
index 35b50d1..93d4edc 100644
--- a/runtime/base/logging.h
+++ b/runtime/base/logging.h
@@ -17,7 +17,6 @@
#ifndef ART_RUNTIME_BASE_LOGGING_H_
#define ART_RUNTIME_BASE_LOGGING_H_
-#include <memory>
#include <ostream>
#include "base/macros.h"
diff --git a/runtime/class_linker.h b/runtime/class_linker.h
index d9935cb..e4f7b7a 100644
--- a/runtime/class_linker.h
+++ b/runtime/class_linker.h
@@ -17,7 +17,6 @@
#ifndef ART_RUNTIME_CLASS_LINKER_H_
#define ART_RUNTIME_CLASS_LINKER_H_
-#include <deque>
#include <string>
#include <utility>
#include <vector>
diff --git a/runtime/gc/allocator_type.h b/runtime/gc/allocator_type.h
index f9a2ff6..185a9b7 100644
--- a/runtime/gc/allocator_type.h
+++ b/runtime/gc/allocator_type.h
@@ -17,7 +17,7 @@
#ifndef ART_RUNTIME_GC_ALLOCATOR_TYPE_H_
#define ART_RUNTIME_GC_ALLOCATOR_TYPE_H_
-#include <ostream>
+#include <iosfwd>
namespace art {
namespace gc {
diff --git a/runtime/gc/collector/concurrent_copying.cc b/runtime/gc/collector/concurrent_copying.cc
index 5e69b79..ccf5154 100644
--- a/runtime/gc/collector/concurrent_copying.cc
+++ b/runtime/gc/collector/concurrent_copying.cc
@@ -19,6 +19,7 @@
#include "art_field-inl.h"
#include "gc/accounting/heap_bitmap-inl.h"
#include "gc/accounting/space_bitmap-inl.h"
+#include "gc/reference_processor.h"
#include "gc/space/image_space.h"
#include "gc/space/space.h"
#include "intern_table.h"
diff --git a/runtime/gc/collector/gc_type.h b/runtime/gc/collector/gc_type.h
index f18e40f..401444a 100644
--- a/runtime/gc/collector/gc_type.h
+++ b/runtime/gc/collector/gc_type.h
@@ -17,7 +17,7 @@
#ifndef ART_RUNTIME_GC_COLLECTOR_GC_TYPE_H_
#define ART_RUNTIME_GC_COLLECTOR_GC_TYPE_H_
-#include <ostream>
+#include <iosfwd>
namespace art {
namespace gc {
diff --git a/runtime/gc/collector_type.h b/runtime/gc/collector_type.h
index 9275e6d..95ba380 100644
--- a/runtime/gc/collector_type.h
+++ b/runtime/gc/collector_type.h
@@ -17,7 +17,7 @@
#ifndef ART_RUNTIME_GC_COLLECTOR_TYPE_H_
#define ART_RUNTIME_GC_COLLECTOR_TYPE_H_
-#include <ostream>
+#include <iosfwd>
namespace art {
namespace gc {
diff --git a/runtime/gc/gc_cause.h b/runtime/gc/gc_cause.h
index 1f2643a..0536f32d 100644
--- a/runtime/gc/gc_cause.h
+++ b/runtime/gc/gc_cause.h
@@ -17,7 +17,7 @@
#ifndef ART_RUNTIME_GC_GC_CAUSE_H_
#define ART_RUNTIME_GC_GC_CAUSE_H_
-#include <ostream>
+#include <iosfwd>
namespace art {
namespace gc {
diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc
index a782fc8..0ae9cdf 100644
--- a/runtime/gc/heap.cc
+++ b/runtime/gc/heap.cc
@@ -37,13 +37,12 @@
#include "gc/accounting/atomic_stack.h"
#include "gc/accounting/card_table-inl.h"
#include "gc/accounting/heap_bitmap-inl.h"
-#include "gc/accounting/mod_union_table.h"
#include "gc/accounting/mod_union_table-inl.h"
#include "gc/accounting/remembered_set.h"
#include "gc/accounting/space_bitmap-inl.h"
#include "gc/collector/concurrent_copying.h"
#include "gc/collector/mark_compact.h"
-#include "gc/collector/mark_sweep-inl.h"
+#include "gc/collector/mark_sweep.h"
#include "gc/collector/partial_mark_sweep.h"
#include "gc/collector/semi_space.h"
#include "gc/collector/sticky_mark_sweep.h"
@@ -62,7 +61,6 @@
#include "image.h"
#include "intern_table.h"
#include "mirror/class-inl.h"
-#include "mirror/object.h"
#include "mirror/object-inl.h"
#include "mirror/object_array-inl.h"
#include "mirror/reference-inl.h"
@@ -467,6 +465,7 @@
gc_complete_cond_.reset(new ConditionVariable("GC complete condition variable",
*gc_complete_lock_));
task_processor_.reset(new TaskProcessor());
+ reference_processor_.reset(new ReferenceProcessor());
pending_task_lock_ = new Mutex("Pending task lock");
if (ignore_max_footprint_) {
SetIdealFootprint(std::numeric_limits<size_t>::max());
@@ -1705,11 +1704,12 @@
mirror::Class* instance_class = obj->GetClass();
CHECK(instance_class != nullptr);
for (size_t i = 0; i < instance_counter->classes_.size(); ++i) {
+ mirror::Class* klass = instance_counter->classes_[i];
if (instance_counter->use_is_assignable_from_) {
- if (instance_counter->classes_[i]->IsAssignableFrom(instance_class)) {
+ if (klass != nullptr && klass->IsAssignableFrom(instance_class)) {
++instance_counter->counts_[i];
}
- } else if (instance_class == instance_counter->classes_[i]) {
+ } else if (instance_class == klass) {
++instance_counter->counts_[i];
}
}
@@ -1865,7 +1865,7 @@
static_cast<double>(space_size_before_compaction);
tl->ResumeAll();
// Finish GC.
- reference_processor_.EnqueueClearedReferences(self);
+ reference_processor_->EnqueueClearedReferences(self);
GrowForUtilization(semi_space_collector_);
LogGC(kGcCauseHomogeneousSpaceCompact, collector);
FinishGC(self, collector::kGcTypeFull);
@@ -1998,7 +1998,7 @@
ChangeCollector(collector_type);
tl->ResumeAll();
// Can't call into java code with all threads suspended.
- reference_processor_.EnqueueClearedReferences(self);
+ reference_processor_->EnqueueClearedReferences(self);
uint64_t duration = NanoTime() - start_time;
GrowForUtilization(semi_space_collector_);
DCHECK(collector != nullptr);
@@ -2472,7 +2472,7 @@
total_bytes_freed_ever_ += GetCurrentGcIteration()->GetFreedBytes();
RequestTrim(self);
// Enqueue cleared references.
- reference_processor_.EnqueueClearedReferences(self);
+ reference_processor_->EnqueueClearedReferences(self);
// Grow the heap so that we know when to perform the next GC.
GrowForUtilization(collector, bytes_allocated_before_gc);
LogGC(gc_cause, collector);
diff --git a/runtime/gc/heap.h b/runtime/gc/heap.h
index cfb6e06..d0040f2 100644
--- a/runtime/gc/heap.h
+++ b/runtime/gc/heap.h
@@ -26,22 +26,17 @@
#include "arch/instruction_set.h"
#include "atomic.h"
#include "base/time_utils.h"
-#include "base/timing_logger.h"
#include "gc/accounting/atomic_stack.h"
#include "gc/accounting/card_table.h"
#include "gc/accounting/read_barrier_table.h"
#include "gc/gc_cause.h"
-#include "gc/collector/garbage_collector.h"
#include "gc/collector/gc_type.h"
#include "gc/collector_type.h"
#include "gc/space/large_object_space.h"
#include "globals.h"
-#include "jni.h"
#include "object_callbacks.h"
#include "offsets.h"
-#include "reference_processor.h"
#include "safe_map.h"
-#include "thread_pool.h"
#include "verify_object.h"
namespace art {
@@ -50,6 +45,7 @@
class Mutex;
class StackVisitor;
class Thread;
+class ThreadPool;
class TimingLogger;
namespace mirror {
@@ -631,7 +627,7 @@
bool HasImageSpace() const;
ReferenceProcessor* GetReferenceProcessor() {
- return &reference_processor_;
+ return reference_processor_.get();
}
TaskProcessor* GetTaskProcessor() {
return task_processor_.get();
@@ -1015,7 +1011,7 @@
std::unique_ptr<ConditionVariable> gc_complete_cond_ GUARDED_BY(gc_complete_lock_);
// Reference processor;
- ReferenceProcessor reference_processor_;
+ std::unique_ptr<ReferenceProcessor> reference_processor_;
// Task processor, proxies heap trim requests to the daemon threads.
std::unique_ptr<TaskProcessor> task_processor_;
diff --git a/runtime/gc/space/large_object_space.cc b/runtime/gc/space/large_object_space.cc
index 52192e2..2b567fe 100644
--- a/runtime/gc/space/large_object_space.cc
+++ b/runtime/gc/space/large_object_space.cc
@@ -16,7 +16,9 @@
#include "large_object_space.h"
+#include <valgrind.h>
#include <memory>
+#include <memcheck/memcheck.h>
#include "gc/accounting/heap_bitmap-inl.h"
#include "gc/accounting/space_bitmap-inl.h"
diff --git a/runtime/gc/space/malloc_space.h b/runtime/gc/space/malloc_space.h
index 5f3a1db..9495864 100644
--- a/runtime/gc/space/malloc_space.h
+++ b/runtime/gc/space/malloc_space.h
@@ -20,8 +20,6 @@
#include "space.h"
#include <ostream>
-#include <valgrind.h>
-#include <memcheck/memcheck.h>
namespace art {
namespace gc {
diff --git a/runtime/gc/space/rosalloc_space-inl.h b/runtime/gc/space/rosalloc_space-inl.h
index 25d4445..f94ec23 100644
--- a/runtime/gc/space/rosalloc_space-inl.h
+++ b/runtime/gc/space/rosalloc_space-inl.h
@@ -17,6 +17,8 @@
#ifndef ART_RUNTIME_GC_SPACE_ROSALLOC_SPACE_INL_H_
#define ART_RUNTIME_GC_SPACE_ROSALLOC_SPACE_INL_H_
+#include <valgrind.h>
+
#include "gc/allocator/rosalloc-inl.h"
#include "gc/space/valgrind_settings.h"
#include "rosalloc_space.h"
diff --git a/runtime/mirror/class-inl.h b/runtime/mirror/class-inl.h
index 7f89b1d..fc27315 100644
--- a/runtime/mirror/class-inl.h
+++ b/runtime/mirror/class-inl.h
@@ -832,11 +832,22 @@
f->VisitRoots(visitor);
}
}
- for (auto& m : GetDirectMethods(pointer_size)) {
- m.VisitRoots(visitor);
+ // We may see GetDirectMethodsPtr() == null with NumDirectMethods() != 0 if the root marking
+ // thread reads a null DirectMethodsBegin() but a non null DirectMethodsBegin() due to a race
+ // SetDirectMethodsPtr from class linking. Same for virtual methods.
+ // In this case, it is safe to avoid marking the roots since we must be either the CC or CMS. If
+ // we are CMS then the roots are already marked through other sources, otherwise the roots are
+ // already marked due to the to-space invariant.
+ // Unchecked versions since we may visit roots of classes that aren't yet loaded.
+ if (GetDirectMethodsPtrUnchecked() != nullptr) {
+ for (auto& m : GetDirectMethods(pointer_size)) {
+ m.VisitRoots(visitor);
+ }
}
- for (auto& m : GetVirtualMethods(pointer_size)) {
- m.VisitRoots(visitor);
+ if (GetVirtualMethodsPtrUnchecked() != nullptr) {
+ for (auto& m : GetVirtualMethods(pointer_size)) {
+ m.VisitRoots(visitor);
+ }
}
}
diff --git a/runtime/mirror/class.cc b/runtime/mirror/class.cc
index f0b7bfd..5bd6583 100644
--- a/runtime/mirror/class.cc
+++ b/runtime/mirror/class.cc
@@ -471,7 +471,8 @@
ArtMethod* Class::FindDeclaredVirtualMethod(const StringPiece& name, const StringPiece& signature,
size_t pointer_size) {
for (auto& method : GetVirtualMethods(pointer_size)) {
- if (name == method.GetName() && method.GetSignature() == signature) {
+ ArtMethod* const np_method = method.GetInterfaceMethodIfProxy(pointer_size);
+ if (name == np_method->GetName() && np_method->GetSignature() == signature) {
return &method;
}
}
@@ -481,7 +482,8 @@
ArtMethod* Class::FindDeclaredVirtualMethod(const StringPiece& name, const Signature& signature,
size_t pointer_size) {
for (auto& method : GetVirtualMethods(pointer_size)) {
- if (name == method.GetName() && signature == method.GetSignature()) {
+ ArtMethod* const np_method = method.GetInterfaceMethodIfProxy(pointer_size);
+ if (name == np_method->GetName() && signature == np_method->GetSignature()) {
return &method;
}
}
diff --git a/runtime/native/dalvik_system_VMDebug.cc b/runtime/native/dalvik_system_VMDebug.cc
index 1078492..8febb62 100644
--- a/runtime/native/dalvik_system_VMDebug.cc
+++ b/runtime/native/dalvik_system_VMDebug.cc
@@ -257,21 +257,45 @@
static jlong VMDebug_countInstancesOfClass(JNIEnv* env, jclass, jclass javaClass,
jboolean countAssignable) {
ScopedObjectAccess soa(env);
- gc::Heap* heap = Runtime::Current()->GetHeap();
- // We only want reachable instances, so do a GC. Heap::VisitObjects visits all of the heap
- // objects in the all spaces and the allocation stack.
- heap->CollectGarbage(false);
+ gc::Heap* const heap = Runtime::Current()->GetHeap();
+ // Caller's responsibility to do GC if desired.
mirror::Class* c = soa.Decode<mirror::Class*>(javaClass);
if (c == nullptr) {
return 0;
}
- std::vector<mirror::Class*> classes;
- classes.push_back(c);
+ std::vector<mirror::Class*> classes {c};
uint64_t count = 0;
heap->CountInstances(classes, countAssignable, &count);
return count;
}
+static jlongArray VMDebug_countInstancesOfClasses(JNIEnv* env, jclass, jobjectArray javaClasses,
+ jboolean countAssignable) {
+ ScopedObjectAccess soa(env);
+ gc::Heap* const heap = Runtime::Current()->GetHeap();
+ // Caller's responsibility to do GC if desired.
+ auto* decoded_classes = soa.Decode<mirror::ObjectArray<mirror::Class>*>(javaClasses);
+ if (decoded_classes == nullptr) {
+ return nullptr;
+ }
+ std::vector<mirror::Class*> classes;
+ for (size_t i = 0, count = decoded_classes->GetLength(); i < count; ++i) {
+ classes.push_back(decoded_classes->Get(i));
+ }
+ std::vector<uint64_t> counts(classes.size(), 0u);
+ // Heap::CountInstances can handle null and will put 0 for these classes.
+ heap->CountInstances(classes, countAssignable, &counts[0]);
+ auto* long_counts = mirror::LongArray::Alloc(soa.Self(), counts.size());
+ if (long_counts == nullptr) {
+ soa.Self()->AssertPendingOOMException();
+ return nullptr;
+ }
+ for (size_t i = 0; i < counts.size(); ++i) {
+ long_counts->Set(i, counts[i]);
+ }
+ return soa.AddLocalReference<jlongArray>(long_counts);
+}
+
// We export the VM internal per-heap-space size/alloc/free metrics
// for the zygote space, alloc space (application heap), and the large
// object space for dumpsys meminfo. The other memory region data such
@@ -452,6 +476,7 @@
static JNINativeMethod gMethods[] = {
NATIVE_METHOD(VMDebug, countInstancesOfClass, "(Ljava/lang/Class;Z)J"),
+ NATIVE_METHOD(VMDebug, countInstancesOfClasses, "([Ljava/lang/Class;Z)[J"),
NATIVE_METHOD(VMDebug, crash, "()V"),
NATIVE_METHOD(VMDebug, dumpHprofData, "(Ljava/lang/String;Ljava/io/FileDescriptor;)V"),
NATIVE_METHOD(VMDebug, dumpHprofDataDdms, "()V"),
diff --git a/runtime/object_callbacks.h b/runtime/object_callbacks.h
index 8e99dbb..df34ce7 100644
--- a/runtime/object_callbacks.h
+++ b/runtime/object_callbacks.h
@@ -17,13 +17,6 @@
#ifndef ART_RUNTIME_OBJECT_CALLBACKS_H_
#define ART_RUNTIME_OBJECT_CALLBACKS_H_
-// For ostream.
-#include <ostream>
-// For uint32_t.
-#include <stdint.h>
-// For size_t.
-#include <stdlib.h>
-
#include "base/macros.h"
namespace art {
diff --git a/runtime/thread-inl.h b/runtime/thread-inl.h
index f7ef894..5f965f1 100644
--- a/runtime/thread-inl.h
+++ b/runtime/thread-inl.h
@@ -25,6 +25,7 @@
#include "base/mutex-inl.h"
#include "gc/heap.h"
#include "jni_env_ext.h"
+#include "thread_pool.h"
namespace art {
diff --git a/runtime/thread.cc b/runtime/thread.cc
index 37a86f1..6656fe5 100644
--- a/runtime/thread.cc
+++ b/runtime/thread.cc
@@ -388,6 +388,24 @@
void Thread::CreateNativeThread(JNIEnv* env, jobject java_peer, size_t stack_size, bool is_daemon) {
CHECK(java_peer != nullptr);
Thread* self = static_cast<JNIEnvExt*>(env)->self;
+
+ if (VLOG_IS_ON(threads)) {
+ ScopedObjectAccess soa(env);
+
+ ArtField* f = soa.DecodeField(WellKnownClasses::java_lang_Thread_name);
+ mirror::String* java_name = reinterpret_cast<mirror::String*>(f->GetObject(
+ soa.Decode<mirror::Object*>(java_peer)));
+ std::string thread_name;
+ if (java_name != nullptr) {
+ thread_name = java_name->ToModifiedUtf8();
+ } else {
+ thread_name = "(Unnamed)";
+ }
+
+ VLOG(threads) << "Creating native thread for " << thread_name;
+ self->Dump(LOG(INFO));
+ }
+
Runtime* runtime = Runtime::Current();
// Atomically start the birth of the thread ensuring the runtime isn't shutting down.
@@ -556,6 +574,16 @@
}
}
+ if (VLOG_IS_ON(threads)) {
+ if (thread_name != nullptr) {
+ VLOG(threads) << "Attaching thread " << thread_name;
+ } else {
+ VLOG(threads) << "Attaching unnamed thread.";
+ }
+ ScopedObjectAccess soa(self);
+ self->Dump(LOG(INFO));
+ }
+
{
ScopedObjectAccess soa(self);
Dbg::PostThreadStart(self);
diff --git a/runtime/thread_list.cc b/runtime/thread_list.cc
index b697b43..7e8128f 100644
--- a/runtime/thread_list.cc
+++ b/runtime/thread_list.cc
@@ -28,7 +28,6 @@
#include <sstream>
#include "base/histogram-inl.h"
-#include "base/mutex.h"
#include "base/mutex-inl.h"
#include "base/time_utils.h"
#include "base/timing_logger.h"
diff --git a/runtime/thread_pool.cc b/runtime/thread_pool.cc
index 1e84c9d..d8f80fa 100644
--- a/runtime/thread_pool.cc
+++ b/runtime/thread_pool.cc
@@ -201,112 +201,4 @@
return tasks_.size();
}
-WorkStealingWorker::WorkStealingWorker(ThreadPool* thread_pool, const std::string& name,
- size_t stack_size)
- : ThreadPoolWorker(thread_pool, name, stack_size), task_(nullptr) {}
-
-void WorkStealingWorker::Run() {
- Thread* self = Thread::Current();
- Task* task = nullptr;
- WorkStealingThreadPool* thread_pool = down_cast<WorkStealingThreadPool*>(thread_pool_);
- while ((task = thread_pool_->GetTask(self)) != nullptr) {
- WorkStealingTask* stealing_task = down_cast<WorkStealingTask*>(task);
-
- {
- CHECK(task_ == nullptr);
- MutexLock mu(self, thread_pool->work_steal_lock_);
- // Register that we are running the task
- ++stealing_task->ref_count_;
- task_ = stealing_task;
- }
- stealing_task->Run(self);
- // Mark ourselves as not running a task so that nobody tries to steal from us.
- // There is a race condition that someone starts stealing from us at this point. This is okay
- // due to the reference counting.
- task_ = nullptr;
-
- bool finalize;
-
- // Steal work from tasks until there is none left to steal. Note: There is a race, but
- // all that happens when the race occurs is that we steal some work instead of processing a
- // task from the queue.
- while (thread_pool->GetTaskCount(self) == 0) {
- WorkStealingTask* steal_from_task = nullptr;
-
- {
- MutexLock mu(self, thread_pool->work_steal_lock_);
- // Try finding a task to steal from.
- steal_from_task = thread_pool->FindTaskToStealFrom();
- if (steal_from_task != nullptr) {
- CHECK_NE(stealing_task, steal_from_task)
- << "Attempting to steal from completed self task";
- steal_from_task->ref_count_++;
- } else {
- break;
- }
- }
-
- if (steal_from_task != nullptr) {
- // Task which completed earlier is going to steal some work.
- stealing_task->StealFrom(self, steal_from_task);
-
- {
- // We are done stealing from the task, lets decrement its reference count.
- MutexLock mu(self, thread_pool->work_steal_lock_);
- finalize = !--steal_from_task->ref_count_;
- }
-
- if (finalize) {
- steal_from_task->Finalize();
- }
- }
- }
-
- {
- MutexLock mu(self, thread_pool->work_steal_lock_);
- // If nobody is still referencing task_ we can finalize it.
- finalize = !--stealing_task->ref_count_;
- }
-
- if (finalize) {
- stealing_task->Finalize();
- }
- }
-}
-
-WorkStealingWorker::~WorkStealingWorker() {}
-
-WorkStealingThreadPool::WorkStealingThreadPool(const char* name, size_t num_threads)
- : ThreadPool(name, 0),
- work_steal_lock_("work stealing lock"),
- steal_index_(0) {
- while (GetThreadCount() < num_threads) {
- const std::string worker_name = StringPrintf("Work stealing worker %zu", GetThreadCount());
- threads_.push_back(new WorkStealingWorker(this, worker_name,
- ThreadPoolWorker::kDefaultStackSize));
- }
-}
-
-WorkStealingTask* WorkStealingThreadPool::FindTaskToStealFrom() {
- const size_t thread_count = GetThreadCount();
- for (size_t i = 0; i < thread_count; ++i) {
- // TODO: Use CAS instead of lock.
- ++steal_index_;
- if (steal_index_ >= thread_count) {
- steal_index_-= thread_count;
- }
-
- WorkStealingWorker* worker = down_cast<WorkStealingWorker*>(threads_[steal_index_]);
- WorkStealingTask* task = worker->task_;
- if (task) {
- // Not null, we can probably steal from this worker.
- return task;
- }
- }
- // Couldn't find something to steal.
- return nullptr;
-}
-
-WorkStealingThreadPool::~WorkStealingThreadPool() {}
-
} // namespace art
diff --git a/runtime/thread_pool.h b/runtime/thread_pool.h
index 0557708..88700e6 100644
--- a/runtime/thread_pool.h
+++ b/runtime/thread_pool.h
@@ -144,58 +144,6 @@
DISALLOW_COPY_AND_ASSIGN(ThreadPool);
};
-class WorkStealingTask : public Task {
- public:
- WorkStealingTask() : ref_count_(0) {}
-
- size_t GetRefCount() const {
- return ref_count_;
- }
-
- virtual void StealFrom(Thread* self, WorkStealingTask* source) = 0;
-
- private:
- // How many people are referencing this task.
- size_t ref_count_;
-
- friend class WorkStealingWorker;
-};
-
-class WorkStealingWorker : public ThreadPoolWorker {
- public:
- virtual ~WorkStealingWorker();
-
- bool IsRunningTask() const {
- return task_ != nullptr;
- }
-
- protected:
- WorkStealingTask* task_;
-
- WorkStealingWorker(ThreadPool* thread_pool, const std::string& name, size_t stack_size);
- virtual void Run();
-
- private:
- friend class WorkStealingThreadPool;
- DISALLOW_COPY_AND_ASSIGN(WorkStealingWorker);
-};
-
-class WorkStealingThreadPool : public ThreadPool {
- public:
- explicit WorkStealingThreadPool(const char* name, size_t num_threads);
- virtual ~WorkStealingThreadPool();
-
- private:
- Mutex work_steal_lock_;
- // Which thread we are stealing from (round robin).
- size_t steal_index_;
-
- // Find a task to steal from
- WorkStealingTask* FindTaskToStealFrom() EXCLUSIVE_LOCKS_REQUIRED(work_steal_lock_);
-
- friend class WorkStealingWorker;
-};
-
} // namespace art
#endif // ART_RUNTIME_THREAD_POOL_H_
diff --git a/runtime/verifier/reg_type.cc b/runtime/verifier/reg_type.cc
index c8aa4fd..1435607 100644
--- a/runtime/verifier/reg_type.cc
+++ b/runtime/verifier/reg_type.cc
@@ -585,7 +585,15 @@
DCHECK(!Equals(incoming_type)); // Trivial equality handled by caller
// Perform pointer equality tests for conflict to avoid virtual method dispatch.
const ConflictType& conflict = reg_types->Conflict();
- if (this == &conflict) {
+ if (IsUndefined() || incoming_type.IsUndefined()) {
+ // There is a difference between undefined and conflict. Conflicts may be copied around, but
+ // not used. Undefined registers must not be copied. So any merge with undefined should return
+ // undefined.
+ if (IsUndefined()) {
+ return *this;
+ }
+ return incoming_type;
+ } else if (this == &conflict) {
DCHECK(IsConflict());
return *this; // Conflict MERGE * => Conflict
} else if (&incoming_type == &conflict) {
diff --git a/test/004-JniTest/jni_test.cc b/test/004-JniTest/jni_test.cc
index ca256ec..db0dd32 100644
--- a/test/004-JniTest/jni_test.cc
+++ b/test/004-JniTest/jni_test.cc
@@ -626,3 +626,7 @@
assert(strcmp(test_array, chars6) == 0);
env->ReleaseStringUTFChars(s6, chars6);
}
+
+extern "C" JNIEXPORT jlong JNICALL Java_Main_testGetMethodID(JNIEnv* env, jclass, jclass c) {
+ return reinterpret_cast<jlong>(env->GetMethodID(c, "a", "()V"));
+}
diff --git a/test/004-JniTest/src/Main.java b/test/004-JniTest/src/Main.java
index ac20417..810dda0 100644
--- a/test/004-JniTest/src/Main.java
+++ b/test/004-JniTest/src/Main.java
@@ -14,7 +14,9 @@
* limitations under the License.
*/
+import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
public class Main {
public static void main(String[] args) {
@@ -35,6 +37,7 @@
testCallNonvirtual();
testNewStringObject();
testRemoveLocalObject();
+ testProxyGetMethodID();
}
private static native void testFindClassOnAttachedNativeThread();
@@ -194,6 +197,31 @@
private static native void testCallNonvirtual();
private static native void testNewStringObject();
+
+ private interface SimpleInterface {
+ void a();
+ }
+
+ private static class DummyInvocationHandler implements InvocationHandler {
+ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
+ return null;
+ }
+ }
+
+ private static void testProxyGetMethodID() {
+ InvocationHandler handler = new DummyInvocationHandler();
+ SimpleInterface proxy =
+ (SimpleInterface) Proxy.newProxyInstance(SimpleInterface.class.getClassLoader(),
+ new Class[] {SimpleInterface.class}, handler);
+ if (testGetMethodID(SimpleInterface.class) == 0) {
+ throw new AssertionError();
+ }
+ if (testGetMethodID(proxy.getClass()) == 0) {
+ throw new AssertionError();
+ }
+ }
+
+ private static native long testGetMethodID(Class<?> c);
}
class JniCallNonvirtualTest {
diff --git a/test/099-vmdebug/expected.txt b/test/099-vmdebug/expected.txt
index 579f98f..b8d72f6 100644
--- a/test/099-vmdebug/expected.txt
+++ b/test/099-vmdebug/expected.txt
@@ -17,3 +17,9 @@
Got expected exception
Test sampling with bogus (<= 0) interval
Got expected exception
+Instances of ClassA 2
+Instances of ClassB 1
+Instances of null 0
+Instances of ClassA assignable 3
+Array counts [2, 1, 0]
+Array counts assignable [3, 1, 0]
diff --git a/test/099-vmdebug/src/Main.java b/test/099-vmdebug/src/Main.java
index add2ff6..1be5765 100644
--- a/test/099-vmdebug/src/Main.java
+++ b/test/099-vmdebug/src/Main.java
@@ -17,6 +17,8 @@
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Method;
+import java.util.Arrays;
+import java.util.ArrayList;
import java.util.Map;
public class Main {
@@ -30,7 +32,9 @@
return;
}
testMethodTracing();
+ testCountInstances();
testRuntimeStat();
+ testRuntimeStats();
}
private static File createTempFile() throws Exception {
@@ -220,12 +224,39 @@
checkHistogram(blocking_gc_count_rate_histogram);
}
+ static class ClassA { }
+ static class ClassB { }
+ static class ClassC extends ClassA { }
+
+ private static void testCountInstances() throws Exception {
+ ArrayList<Object> l = new ArrayList<Object>();
+ l.add(new ClassA());
+ l.add(new ClassB());
+ l.add(new ClassA());
+ l.add(new ClassC());
+ Runtime.getRuntime().gc();
+ System.out.println("Instances of ClassA " +
+ VMDebug.countInstancesofClass(ClassA.class, false));
+ System.out.println("Instances of ClassB " +
+ VMDebug.countInstancesofClass(ClassB.class, false));
+ System.out.println("Instances of null " + VMDebug.countInstancesofClass(null, false));
+ System.out.println("Instances of ClassA assignable " +
+ VMDebug.countInstancesofClass(ClassA.class, true));
+ Class[] classes = new Class[]{ClassA.class, ClassB.class, null};
+ long[] counts = VMDebug.countInstancesofClasses(classes, false);
+ System.out.println("Array counts " + Arrays.toString(counts));
+ counts = VMDebug.countInstancesofClasses(classes, true);
+ System.out.println("Array counts assignable " + Arrays.toString(counts));
+ }
+
private static class VMDebug {
private static final Method startMethodTracingMethod;
private static final Method stopMethodTracingMethod;
private static final Method getMethodTracingModeMethod;
private static final Method getRuntimeStatMethod;
private static final Method getRuntimeStatsMethod;
+ private static final Method countInstancesOfClassMethod;
+ private static final Method countInstancesOfClassesMethod;
static {
try {
Class c = Class.forName("dalvik.system.VMDebug");
@@ -235,6 +266,10 @@
getMethodTracingModeMethod = c.getDeclaredMethod("getMethodTracingMode");
getRuntimeStatMethod = c.getDeclaredMethod("getRuntimeStat", String.class);
getRuntimeStatsMethod = c.getDeclaredMethod("getRuntimeStats");
+ countInstancesOfClassMethod = c.getDeclaredMethod("countInstancesOfClass",
+ Class.class, Boolean.TYPE);
+ countInstancesOfClassesMethod = c.getDeclaredMethod("countInstancesOfClasses",
+ Class[].class, Boolean.TYPE);
} catch (Exception e) {
throw new RuntimeException(e);
}
@@ -257,5 +292,13 @@
public static Map<String, String> getRuntimeStats() throws Exception {
return (Map<String, String>) getRuntimeStatsMethod.invoke(null);
}
+ public static long countInstancesofClass(Class c, boolean assignable) throws Exception {
+ return (long) countInstancesOfClassMethod.invoke(null, new Object[]{c, assignable});
+ }
+ public static long[] countInstancesofClasses(Class[] classes, boolean assignable)
+ throws Exception {
+ return (long[]) countInstancesOfClassesMethod.invoke(
+ null, new Object[]{classes, assignable});
+ }
}
}
diff --git a/test/510-checker-try-catch/smali/Builder.smali b/test/510-checker-try-catch/smali/Builder.smali
index 87e4064..2274ba4 100644
--- a/test/510-checker-try-catch/smali/Builder.smali
+++ b/test/510-checker-try-catch/smali/Builder.smali
@@ -914,6 +914,14 @@
## CHECK: name "<<BReturn:B\d+>>"
## CHECK: predecessors "<<BExitTry2>>"
+## CHECK: name "{{B\d+}}"
+## CHECK: Exit
+
+## CHECK: name "<<BTry2:B\d+>>"
+## CHECK: predecessors "<<BEnterTry2>>"
+## CHECK: successors "<<BExitTry2>>"
+## CHECK: Div
+
## CHECK: name "<<BEnterTry1>>"
## CHECK: predecessors "B0"
## CHECK: successors "<<BTry1>>"
@@ -926,11 +934,6 @@
## CHECK: xhandlers "<<BCatch>>"
## CHECK: TryBoundary kind:exit
-## CHECK: name "<<BTry2:B\d+>>"
-## CHECK: predecessors "<<BEnterTry2>>"
-## CHECK: successors "<<BExitTry2>>"
-## CHECK: Div
-
## CHECK: name "<<BEnterTry2>>"
## CHECK: predecessors "<<BCatch>>"
## CHECK: successors "<<BTry2>>"
@@ -987,6 +990,11 @@
## CHECK: successors "<<BExitTry1>>"
## CHECK: Div
+## CHECK: name "<<BTry2:B\d+>>"
+## CHECK: predecessors "<<BEnterTry2>>"
+## CHECK: successors "<<BExitTry2>>"
+## CHECK: Div
+
## CHECK: name "<<BEnterTry1>>"
## CHECK: predecessors "<<BCatch1>>"
## CHECK: successors "<<BTry1>>"
@@ -999,11 +1007,6 @@
## CHECK: xhandlers "<<BCatch2>>"
## CHECK: TryBoundary kind:exit
-## CHECK: name "<<BTry2:B\d+>>"
-## CHECK: predecessors "<<BEnterTry2>>"
-## CHECK: successors "<<BExitTry2>>"
-## CHECK: Div
-
## CHECK: name "<<BEnterTry2>>"
## CHECK: predecessors "<<BCatch2>>"
## CHECK: successors "<<BTry2>>"
diff --git a/test/523-checker-can-throw-regression/expected.txt b/test/523-checker-can-throw-regression/expected.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/523-checker-can-throw-regression/expected.txt
diff --git a/test/523-checker-can-throw-regression/info.txt b/test/523-checker-can-throw-regression/info.txt
new file mode 100644
index 0000000..720dc85
--- /dev/null
+++ b/test/523-checker-can-throw-regression/info.txt
@@ -0,0 +1,2 @@
+Regression test for the HGraphBuilder which would split a throwing catch block
+but would not update information about which blocks throw.
\ No newline at end of file
diff --git a/test/523-checker-can-throw-regression/smali/Test.smali b/test/523-checker-can-throw-regression/smali/Test.smali
new file mode 100644
index 0000000..87192ea
--- /dev/null
+++ b/test/523-checker-can-throw-regression/smali/Test.smali
@@ -0,0 +1,53 @@
+#
+# Copyright (C) 2015 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.
+
+.class public LTest;
+
+.super Ljava/lang/Object;
+
+## CHECK-START: int Test.testCase(int, int, int) builder (after)
+## CHECK: TryBoundary kind:entry
+## CHECK: TryBoundary kind:entry
+## CHECK-NOT: TryBoundary kind:entry
+
+## CHECK-START: int Test.testCase(int, int, int) builder (after)
+## CHECK: TryBoundary kind:exit
+## CHECK: TryBoundary kind:exit
+## CHECK-NOT: TryBoundary kind:exit
+
+.method public static testCase(III)I
+ .registers 4
+
+ :try_start_1
+ div-int/2addr p0, p1
+ return p0
+ :try_end_1
+ .catchall {:try_start_1 .. :try_end_1} :catchall
+
+ :catchall
+ :try_start_2
+ move-exception v0
+ # Block would be split here but second part not marked as throwing.
+ div-int/2addr p0, p1
+ if-eqz p2, :else
+
+ div-int/2addr p0, p1
+ :else
+ div-int/2addr p0, p2
+ return p0
+ :try_end_2
+ .catchall {:try_start_2 .. :try_end_2} :catchall
+
+.end method
diff --git a/test/523-checker-can-throw-regression/src/Main.java b/test/523-checker-can-throw-regression/src/Main.java
new file mode 100644
index 0000000..3ff48f3
--- /dev/null
+++ b/test/523-checker-can-throw-regression/src/Main.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2015 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.Method;
+import java.lang.reflect.Type;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.Callable;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.CancellationException;
+import java.util.concurrent.TimeoutException;
+
+public class Main {
+
+ // Workaround for b/18051191.
+ class InnerClass {}
+
+ public static void main(String args[]) {}
+}
diff --git a/test/800-smali/expected.txt b/test/800-smali/expected.txt
index 6ee7a5b..4c17240 100644
--- a/test/800-smali/expected.txt
+++ b/test/800-smali/expected.txt
@@ -29,4 +29,6 @@
b/21645819
b/22244733
b/22331663
+b/22331663 (pass)
+b/22331663 (fail)
Done!
diff --git a/test/800-smali/smali/b_22331663.smali b/test/800-smali/smali/b_22331663.smali
index af99152..057fc7f 100644
--- a/test/800-smali/smali/b_22331663.smali
+++ b/test/800-smali/smali/b_22331663.smali
@@ -4,6 +4,9 @@
.method public static run(Z)V
.registers 6
+ # Make v4 defined, just use null.
+ const v4, 0
+
if-eqz v5, :Label2
:Label1
diff --git a/test/800-smali/smali/b_22331663_fail.smali b/test/800-smali/smali/b_22331663_fail.smali
new file mode 100644
index 0000000..0c25e30
--- /dev/null
+++ b/test/800-smali/smali/b_22331663_fail.smali
@@ -0,0 +1,20 @@
+.class public LB22331663Fail;
+.super Ljava/lang/Object;
+
+
+.method public static run(Z)V
+.registers 6
+ if-eqz v5, :Label1
+
+ # Construct a java.lang.Object completely. This makes v4 of reference type.
+ new-instance v4, Ljava/lang/Object;
+ invoke-direct {v4}, Ljava/lang/Object;-><init>()V
+
+:Label1
+ # At this point, v4 is the merge of Undefined and ReferenceType. The verifier should
+ # reject any use of this, even a copy. Previously this was a conflict. Conflicts must
+ # be movable now, so ensure that we do not get a conflict (and then allow the move).
+ move-object v0, v4
+
+ return-void
+.end method
diff --git a/test/800-smali/smali/b_22331663_pass.smali b/test/800-smali/smali/b_22331663_pass.smali
new file mode 100644
index 0000000..1b54180
--- /dev/null
+++ b/test/800-smali/smali/b_22331663_pass.smali
@@ -0,0 +1,22 @@
+.class public LB22331663Pass;
+.super Ljava/lang/Object;
+
+
+.method public static run(Z)V
+.registers 6
+ if-eqz v5, :Label1
+
+ # Construct a java.lang.Object completely. This makes v4 of reference type.
+ new-instance v4, Ljava/lang/Object;
+ invoke-direct {v4}, Ljava/lang/Object;-><init>()V
+
+:Label1
+ # At this point, v4 is the merge of Undefined and ReferenceType. The verifier should not
+ # reject this if it is unused.
+
+ # Do an allocation here. This will force heap checking in gcstress mode.
+ new-instance v0, Ljava/lang/Object;
+ invoke-direct {v0}, Ljava/lang/Object;-><init>()V
+
+ return-void
+.end method
diff --git a/test/800-smali/src/Main.java b/test/800-smali/src/Main.java
index 3dbba8d..8be6418 100644
--- a/test/800-smali/src/Main.java
+++ b/test/800-smali/src/Main.java
@@ -105,6 +105,10 @@
null, "abc"));
testCases.add(new TestCase("b/22331663", "B22331663", "run", new Object[] { false },
null, null));
+ testCases.add(new TestCase("b/22331663 (pass)", "B22331663Pass", "run",
+ new Object[] { false }, null, null));
+ testCases.add(new TestCase("b/22331663 (fail)", "B22331663Fail", "run",
+ new Object[] { false }, new VerifyError(), null));
}
public void runTests() {
diff --git a/tools/symbolize.sh b/tools/symbolize.sh
index 7365a9b..f5686e6 100755
--- a/tools/symbolize.sh
+++ b/tools/symbolize.sh
@@ -22,6 +22,7 @@
INTERACTIVE="no"
if [ "x$1" = "x--interactive" ] ; then
INTERACTIVE="yes"
+ shift
fi
# Pull the file from the device and symbolize it.
@@ -57,4 +58,15 @@
done
}
-all
+if [ "x$1" = "x" ] ; then
+ # No further arguments, iterate over all oat files on device.
+ all
+else
+ # Take the parameters as a list of paths on device.
+ while (($#)); do
+ DIR=$(dirname $1)
+ NAME=$(basename $1)
+ one $DIR $NAME
+ shift
+ done
+fi