Merge "ART: Enable more libcore tests (libcore.* package)"
diff --git a/cmdline/cmdline_parser_test.cc b/cmdline/cmdline_parser_test.cc
index 288f7ac..b740b41 100644
--- a/cmdline/cmdline_parser_test.cc
+++ b/cmdline/cmdline_parser_test.cc
@@ -202,6 +202,7 @@
EXPECT_SINGLE_PARSE_VALUE(false, "-XX:DisableHSpaceCompactForOOM", M::EnableHSpaceCompactForOOM);
EXPECT_SINGLE_PARSE_VALUE(0.5, "-XX:HeapTargetUtilization=0.5", M::HeapTargetUtilization);
EXPECT_SINGLE_PARSE_VALUE(5u, "-XX:ParallelGCThreads=5", M::ParallelGCThreads);
+ EXPECT_SINGLE_PARSE_EXISTS("-Xno-dex-file-fallback", M::NoDexFileFallback);
} // TEST_F
TEST_F(CmdlineParserTest, TestSimpleFailures) {
diff --git a/compiler/optimizing/bounds_check_elimination.cc b/compiler/optimizing/bounds_check_elimination.cc
index ae9974d..811a3bd 100644
--- a/compiler/optimizing/bounds_check_elimination.cc
+++ b/compiler/optimizing/bounds_check_elimination.cc
@@ -32,14 +32,7 @@
if (instruction != nullptr && instruction->IsIntConstant()) {
// Normalize ValueBound with constant instruction.
int32_t instr_const = instruction->AsIntConstant()->GetValue();
- if (constant >= 0 && (instr_const <= INT_MAX - constant)) {
- // No overflow.
- instruction_ = nullptr;
- constant_ = instr_const + constant;
- return;
- }
- if (constant < 0 && (instr_const >= INT_MIN - constant)) {
- // No underflow.
+ if (!WouldAddOverflowOrUnderflow(instr_const, constant)) {
instruction_ = nullptr;
constant_ = instr_const + constant;
return;
@@ -49,6 +42,22 @@
constant_ = constant;
}
+ // Return whether (left + right) overflows or underflows.
+ static bool WouldAddOverflowOrUnderflow(int32_t left, int32_t right) {
+ if (right == 0) {
+ return false;
+ }
+ if ((right > 0) && (left <= INT_MAX - right)) {
+ // No overflow.
+ return false;
+ }
+ if ((right < 0) && (left >= INT_MIN - right)) {
+ // No underflow.
+ return false;
+ }
+ return true;
+ }
+
static bool IsAddOrSubAConstant(HInstruction* instruction,
HInstruction** left_instruction,
int* right_constant) {
@@ -463,10 +472,23 @@
// Narrow the value range of `instruction` at the end of `basic_block` with `range`,
// and push the narrowed value range to `successor`.
void ApplyRangeFromComparison(HInstruction* instruction, HBasicBlock* basic_block,
- HBasicBlock* successor, ValueRange* range) {
+ HBasicBlock* successor, ValueRange* range) {
ValueRange* existing_range = LookupValueRange(instruction, basic_block);
- ValueRange* narrowed_range = (existing_range == nullptr) ?
- range : existing_range->Narrow(range);
+ if (existing_range == nullptr) {
+ if (range != nullptr) {
+ GetValueRangeMap(successor)->Overwrite(instruction->GetId(), range);
+ }
+ return;
+ }
+ if (existing_range->IsMonotonicValueRange()) {
+ DCHECK(instruction->IsLoopHeaderPhi());
+ // Make sure the comparison is in the loop header so each increment is
+ // checked with a comparison.
+ if (instruction->GetBlock() != basic_block) {
+ return;
+ }
+ }
+ ValueRange* narrowed_range = existing_range->Narrow(range);
if (narrowed_range != nullptr) {
GetValueRangeMap(successor)->Overwrite(instruction->GetId(), narrowed_range);
}
@@ -705,6 +727,15 @@
// Here we are interested in the typical triangular case of nested loops,
// such as the inner loop 'for (int j=0; j<array.length-i; j++)' where i
// is the index for outer loop. In this case, we know j is bounded by array.length-1.
+
+ // Try to handle (array.length - i) or (array.length + c - i) format.
+ HInstruction* left_of_left; // left input of left.
+ int32_t right_const = 0;
+ if (ValueBound::IsAddOrSubAConstant(left, &left_of_left, &right_const)) {
+ left = left_of_left;
+ }
+ // The value of left input of the sub equals (left + right_const).
+
if (left->IsArrayLength()) {
HInstruction* array_length = left->AsArrayLength();
ValueRange* right_range = LookupValueRange(right, sub->GetBlock());
@@ -715,19 +746,83 @@
HInstruction* upper_inst = upper.GetInstruction();
// Make sure it's the same array.
if (ValueBound::Equal(array_length, upper_inst)) {
- // (array.length - v) where v is in [c1, array.length + c2]
- // gets [-c2, array.length - c1] as its value range.
- ValueRange* range = new (GetGraph()->GetArena()) ValueRange(
- GetGraph()->GetArena(),
- ValueBound(nullptr, - upper.GetConstant()),
- ValueBound(array_length, - lower.GetConstant()));
- GetValueRangeMap(sub->GetBlock())->Overwrite(sub->GetId(), range);
+ int32_t c0 = right_const;
+ int32_t c1 = lower.GetConstant();
+ int32_t c2 = upper.GetConstant();
+ // (array.length + c0 - v) where v is in [c1, array.length + c2]
+ // gets [c0 - c2, array.length + c0 - c1] as its value range.
+ if (!ValueBound::WouldAddOverflowOrUnderflow(c0, -c2) &&
+ !ValueBound::WouldAddOverflowOrUnderflow(c0, -c1)) {
+ if ((c0 - c1) <= 0) {
+ // array.length + (c0 - c1) won't overflow/underflow.
+ ValueRange* range = new (GetGraph()->GetArena()) ValueRange(
+ GetGraph()->GetArena(),
+ ValueBound(nullptr, right_const - upper.GetConstant()),
+ ValueBound(array_length, right_const - lower.GetConstant()));
+ GetValueRangeMap(sub->GetBlock())->Overwrite(sub->GetId(), range);
+ }
+ }
}
}
}
}
}
+ void FindAndHandlePartialArrayLength(HBinaryOperation* instruction) {
+ DCHECK(instruction->IsDiv() || instruction->IsShr() || instruction->IsUShr());
+ HInstruction* right = instruction->GetRight();
+ int32_t right_const;
+ if (right->IsIntConstant()) {
+ right_const = right->AsIntConstant()->GetValue();
+ // Detect division by two or more.
+ if ((instruction->IsDiv() && right_const <= 1) ||
+ (instruction->IsShr() && right_const < 1) ||
+ (instruction->IsUShr() && right_const < 1)) {
+ return;
+ }
+ } else {
+ return;
+ }
+
+ // Try to handle array.length/2 or (array.length-1)/2 format.
+ HInstruction* left = instruction->GetLeft();
+ HInstruction* left_of_left; // left input of left.
+ int32_t c = 0;
+ if (ValueBound::IsAddOrSubAConstant(left, &left_of_left, &c)) {
+ left = left_of_left;
+ }
+ // The value of left input of instruction equals (left + c).
+
+ // (array_length + 1) or smaller divided by two or more
+ // always generate a value in [INT_MIN, array_length].
+ // This is true even if array_length is INT_MAX.
+ if (left->IsArrayLength() && c <= 1) {
+ if (instruction->IsUShr() && c < 0) {
+ // Make sure for unsigned shift, left side is not negative.
+ // e.g. if array_length is 2, ((array_length - 3) >>> 2) is way bigger
+ // than array_length.
+ return;
+ }
+ ValueRange* range = new (GetGraph()->GetArena()) ValueRange(
+ GetGraph()->GetArena(),
+ ValueBound(nullptr, INT_MIN),
+ ValueBound(left, 0));
+ GetValueRangeMap(instruction->GetBlock())->Overwrite(instruction->GetId(), range);
+ }
+ }
+
+ void VisitDiv(HDiv* div) {
+ FindAndHandlePartialArrayLength(div);
+ }
+
+ void VisitShr(HShr* shr) {
+ FindAndHandlePartialArrayLength(shr);
+ }
+
+ void VisitUShr(HUShr* ushr) {
+ FindAndHandlePartialArrayLength(ushr);
+ }
+
void VisitNewArray(HNewArray* new_array) {
HInstruction* len = new_array->InputAt(0);
if (!len->IsIntConstant()) {
diff --git a/compiler/optimizing/bounds_check_elimination_test.cc b/compiler/optimizing/bounds_check_elimination_test.cc
index 17cb8f3..a298413 100644
--- a/compiler/optimizing/bounds_check_elimination_test.cc
+++ b/compiler/optimizing/bounds_check_elimination_test.cc
@@ -400,7 +400,6 @@
loop_body->AddSuccessor(loop_header);
HPhi* phi = new (allocator) HPhi(allocator, 0, 0, Primitive::kPrimInt);
- phi->AddInput(constant_initial);
HInstruction* null_check = new (allocator) HNullCheck(parameter, 0);
HInstruction* array_length = new (allocator) HArrayLength(null_check);
HInstruction* cmp = nullptr;
@@ -416,6 +415,7 @@
loop_header->AddInstruction(array_length);
loop_header->AddInstruction(cmp);
loop_header->AddInstruction(if_inst);
+ phi->AddInput(constant_initial);
null_check = new (allocator) HNullCheck(parameter, 0);
array_length = new (allocator) HArrayLength(null_check);
@@ -544,7 +544,6 @@
loop_body->AddSuccessor(loop_header);
HPhi* phi = new (allocator) HPhi(allocator, 0, 0, Primitive::kPrimInt);
- phi->AddInput(array_length);
HInstruction* cmp = nullptr;
if (cond == kCondLE) {
cmp = new (allocator) HLessThanOrEqual(phi, constant_initial);
@@ -556,6 +555,7 @@
loop_header->AddPhi(phi);
loop_header->AddInstruction(cmp);
loop_header->AddInstruction(if_inst);
+ phi->AddInput(array_length);
HInstruction* add = new (allocator) HAdd(Primitive::kPrimInt, phi, constant_minus_1);
null_check = new (allocator) HNullCheck(parameter, 0);
@@ -632,7 +632,7 @@
ASSERT_TRUE(IsRemoved(bounds_check));
}
-// int[] array = new array[10];
+// int[] array = new int[10];
// for (int i=0; i<10; i+=increment) { array[i] = 10; }
static HGraph* BuildSSAGraph3(ArenaAllocator* allocator,
HInstruction** bounds_check,
@@ -672,7 +672,6 @@
loop_body->AddSuccessor(loop_header);
HPhi* phi = new (allocator) HPhi(allocator, 0, 0, Primitive::kPrimInt);
- phi->AddInput(constant_initial);
HInstruction* cmp = nullptr;
if (cond == kCondGE) {
cmp = new (allocator) HGreaterThanOrEqual(phi, constant_10);
@@ -684,6 +683,7 @@
loop_header->AddPhi(phi);
loop_header->AddInstruction(cmp);
loop_header->AddInstruction(if_inst);
+ phi->AddInput(constant_initial);
HNullCheck* null_check = new (allocator) HNullCheck(new_array, 0);
HArrayLength* array_length = new (allocator) HArrayLength(null_check);
@@ -708,7 +708,7 @@
ArenaPool pool;
ArenaAllocator allocator(&pool);
- // int[] array = new array[10];
+ // int[] array = new int[10];
// for (int i=0; i<10; i++) { array[i] = 10; // Can eliminate. }
HInstruction* bounds_check = nullptr;
HGraph* graph = BuildSSAGraph3(&allocator, &bounds_check, 0, 1, kCondGE);
@@ -718,7 +718,7 @@
bounds_check_elimination_after_gvn.Run();
ASSERT_TRUE(IsRemoved(bounds_check));
- // int[] array = new array[10];
+ // int[] array = new int[10];
// for (int i=1; i<10; i++) { array[i] = 10; // Can eliminate. }
graph = BuildSSAGraph3(&allocator, &bounds_check, 1, 1, kCondGE);
graph->BuildDominatorTree();
@@ -727,7 +727,7 @@
bounds_check_elimination_with_initial_1.Run();
ASSERT_TRUE(IsRemoved(bounds_check));
- // int[] array = new array[10];
+ // int[] array = new int[10];
// for (int i=0; i<=10; i++) { array[i] = 10; // Can't eliminate. }
graph = BuildSSAGraph3(&allocator, &bounds_check, 0, 1, kCondGT);
graph->BuildDominatorTree();
@@ -736,7 +736,7 @@
bounds_check_elimination_with_greater_than.Run();
ASSERT_FALSE(IsRemoved(bounds_check));
- // int[] array = new array[10];
+ // int[] array = new int[10];
// for (int i=1; i<10; i+=8) { array[i] = 10; // Can eliminate. }
graph = BuildSSAGraph3(&allocator, &bounds_check, 1, 8, kCondGE);
graph->BuildDominatorTree();
@@ -785,7 +785,6 @@
loop_body->AddSuccessor(loop_header);
HPhi* phi = new (allocator) HPhi(allocator, 0, 0, Primitive::kPrimInt);
- phi->AddInput(constant_initial);
HInstruction* null_check = new (allocator) HNullCheck(parameter, 0);
HInstruction* array_length = new (allocator) HArrayLength(null_check);
HInstruction* cmp = nullptr;
@@ -800,6 +799,7 @@
loop_header->AddInstruction(array_length);
loop_header->AddInstruction(cmp);
loop_header->AddInstruction(if_inst);
+ phi->AddInput(constant_initial);
null_check = new (allocator) HNullCheck(parameter, 0);
array_length = new (allocator) HArrayLength(null_check);
@@ -904,7 +904,6 @@
HBasicBlock* outer_header = new (&allocator) HBasicBlock(graph);
graph->AddBlock(outer_header);
HPhi* phi_i = new (&allocator) HPhi(&allocator, 0, 0, Primitive::kPrimInt);
- phi_i->AddInput(constant_0);
HNullCheck* null_check = new (&allocator) HNullCheck(parameter, 0);
HArrayLength* array_length = new (&allocator) HArrayLength(null_check);
HAdd* add = new (&allocator) HAdd(Primitive::kPrimInt, array_length, constant_minus_1);
@@ -916,11 +915,11 @@
outer_header->AddInstruction(add);
outer_header->AddInstruction(cmp);
outer_header->AddInstruction(if_inst);
+ phi_i->AddInput(constant_0);
HBasicBlock* inner_header = new (&allocator) HBasicBlock(graph);
graph->AddBlock(inner_header);
HPhi* phi_j = new (&allocator) HPhi(&allocator, 0, 0, Primitive::kPrimInt);
- phi_j->AddInput(constant_0);
null_check = new (&allocator) HNullCheck(parameter, 0);
array_length = new (&allocator) HArrayLength(null_check);
HSub* sub = new (&allocator) HSub(Primitive::kPrimInt, array_length, phi_i);
@@ -934,6 +933,7 @@
inner_header->AddInstruction(add);
inner_header->AddInstruction(cmp);
inner_header->AddInstruction(if_inst);
+ phi_j->AddInput(constant_0);
HBasicBlock* inner_body_compare = new (&allocator) HBasicBlock(graph);
graph->AddBlock(inner_body_compare);
diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc
index e151c6b..1101569 100644
--- a/compiler/optimizing/code_generator_x86.cc
+++ b/compiler/optimizing/code_generator_x86.cc
@@ -1809,7 +1809,7 @@
case Primitive::kPrimFloat:
case Primitive::kPrimDouble: {
locations->SetInAt(0, Location::RequiresFpuRegister());
- locations->SetInAt(1, Location::Any());
+ locations->SetInAt(1, Location::RequiresFpuRegister());
locations->SetOut(Location::SameAsFirstInput());
break;
}
@@ -1853,8 +1853,6 @@
case Primitive::kPrimFloat: {
if (second.IsFpuRegister()) {
__ addss(first.AsFpuRegister<XmmRegister>(), second.AsFpuRegister<XmmRegister>());
- } else {
- __ addss(first.AsFpuRegister<XmmRegister>(), Address(ESP, second.GetStackIndex()));
}
break;
}
@@ -1862,8 +1860,6 @@
case Primitive::kPrimDouble: {
if (second.IsFpuRegister()) {
__ addsd(first.AsFpuRegister<XmmRegister>(), second.AsFpuRegister<XmmRegister>());
- } else {
- __ addsd(first.AsFpuRegister<XmmRegister>(), Address(ESP, second.GetStackIndex()));
}
break;
}
diff --git a/compiler/optimizing/graph_checker.cc b/compiler/optimizing/graph_checker.cc
index ef10428..a7f1f74 100644
--- a/compiler/optimizing/graph_checker.cc
+++ b/compiler/optimizing/graph_checker.cc
@@ -160,6 +160,22 @@
instruction->GetId()));
}
}
+
+ // Ensure 'instruction' has pointers to its inputs' use entries.
+ for (size_t i = 0, e = instruction->InputCount(); i < e; ++i) {
+ HUserRecord<HInstruction*> input_record = instruction->InputRecordAt(i);
+ HInstruction* input = input_record.GetInstruction();
+ HUseListNode<HInstruction*>* use_node = input_record.GetUseNode();
+ if (use_node == nullptr || !input->GetUses().Contains(use_node)) {
+ AddError(StringPrintf("Instruction %s:%d has an invalid pointer to use entry "
+ "at input %u (%s:%d).",
+ instruction->DebugName(),
+ instruction->GetId(),
+ static_cast<unsigned>(i),
+ input->DebugName(),
+ input->GetId()));
+ }
+ }
}
void SSAChecker::VisitBasicBlock(HBasicBlock* block) {
diff --git a/compiler/optimizing/gvn.cc b/compiler/optimizing/gvn.cc
index 89bba2d..cb448c8 100644
--- a/compiler/optimizing/gvn.cc
+++ b/compiler/optimizing/gvn.cc
@@ -270,7 +270,7 @@
set = new (allocator_) ValueSet(allocator_);
} else {
HBasicBlock* dominator = block->GetDominator();
- set = sets_.Get(dominator->GetBlockId())->Copy();
+ set = sets_.Get(dominator->GetBlockId());
if (dominator->GetSuccessors().Size() != 1 || dominator->GetSuccessors().Get(0) != block) {
// We have to copy if the dominator has other successors, or `block` is not a successor
// of the dominator.
diff --git a/compiler/optimizing/licm.cc b/compiler/optimizing/licm.cc
index 10f24d8..bf9b8e5 100644
--- a/compiler/optimizing/licm.cc
+++ b/compiler/optimizing/licm.cc
@@ -66,8 +66,7 @@
for (size_t i = 0, e = environment->Size(); i < e; ++i) {
HInstruction* input = environment->GetInstructionAt(i);
if (input != nullptr && IsPhiOf(input, info->GetHeader())) {
- HUseListNode<HEnvironment*>* env_use = environment->GetInstructionEnvUseAt(i);
- input->RemoveEnvironmentUser(env_use);
+ environment->RemoveAsUserOfInput(i);
HInstruction* incoming = input->InputAt(0);
environment->SetRawEnvAt(i, incoming);
incoming->AddEnvUseAt(environment, i);
diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc
index 7a75d26..93787b8 100644
--- a/compiler/optimizing/nodes.cc
+++ b/compiler/optimizing/nodes.cc
@@ -34,17 +34,14 @@
static void RemoveAsUser(HInstruction* instruction) {
for (size_t i = 0; i < instruction->InputCount(); i++) {
- instruction->InputAt(i)->RemoveUser(instruction, i);
+ instruction->RemoveAsUserOfInput(i);
}
HEnvironment* environment = instruction->GetEnvironment();
if (environment != nullptr) {
for (size_t i = 0, e = environment->Size(); i < e; ++i) {
- HUseListNode<HEnvironment*>* vreg_env_use = environment->GetInstructionEnvUseAt(i);
- if (vreg_env_use != nullptr) {
- HInstruction* vreg = environment->GetInstructionAt(i);
- DCHECK(vreg != nullptr);
- vreg->RemoveEnvironmentUser(vreg_env_use);
+ if (environment->GetInstructionAt(i) != nullptr) {
+ environment->RemoveAsUserOfInput(i);
}
}
}
@@ -64,22 +61,19 @@
}
}
-void HGraph::RemoveBlock(HBasicBlock* block) const {
- for (size_t j = 0; j < block->GetSuccessors().Size(); ++j) {
- block->GetSuccessors().Get(j)->RemovePredecessor(block);
- }
- for (HInstructionIterator it(block->GetPhis()); !it.Done(); it.Advance()) {
- block->RemovePhi(it.Current()->AsPhi());
- }
- for (HInstructionIterator it(block->GetInstructions()); !it.Done(); it.Advance()) {
- block->RemoveInstruction(it.Current());
- }
-}
-
void HGraph::RemoveDeadBlocks(const ArenaBitVector& visited) const {
for (size_t i = 0; i < blocks_.Size(); ++i) {
if (!visited.IsBitSet(i)) {
- RemoveBlock(blocks_.Get(i));
+ HBasicBlock* block = blocks_.Get(i);
+ for (size_t j = 0; j < block->GetSuccessors().Size(); ++j) {
+ block->GetSuccessors().Get(j)->RemovePredecessor(block);
+ }
+ for (HInstructionIterator it(block->GetPhis()); !it.Done(); it.Advance()) {
+ block->RemovePhi(it.Current()->AsPhi(), /*ensure_safety=*/ false);
+ }
+ for (HInstructionIterator it(block->GetInstructions()); !it.Done(); it.Advance()) {
+ block->RemoveInstruction(it.Current(), /*ensure_safety=*/ false);
+ }
}
}
}
@@ -439,22 +433,24 @@
static void Remove(HInstructionList* instruction_list,
HBasicBlock* block,
- HInstruction* instruction) {
+ HInstruction* instruction,
+ bool ensure_safety) {
DCHECK_EQ(block, instruction->GetBlock());
- DCHECK(instruction->GetUses().IsEmpty());
- DCHECK(instruction->GetEnvUses().IsEmpty());
instruction->SetBlock(nullptr);
instruction_list->RemoveInstruction(instruction);
-
- RemoveAsUser(instruction);
+ if (ensure_safety) {
+ DCHECK(instruction->GetUses().IsEmpty());
+ DCHECK(instruction->GetEnvUses().IsEmpty());
+ RemoveAsUser(instruction);
+ }
}
-void HBasicBlock::RemoveInstruction(HInstruction* instruction) {
- Remove(&instructions_, this, instruction);
+void HBasicBlock::RemoveInstruction(HInstruction* instruction, bool ensure_safety) {
+ Remove(&instructions_, this, instruction, ensure_safety);
}
-void HBasicBlock::RemovePhi(HPhi* phi) {
- Remove(&phis_, this, phi);
+void HBasicBlock::RemovePhi(HPhi* phi, bool ensure_safety) {
+ Remove(&phis_, this, phi, ensure_safety);
}
void HEnvironment::CopyFrom(HEnvironment* env) {
@@ -467,15 +463,9 @@
}
}
-template <typename T>
-static void RemoveFromUseList(T user, size_t input_index, HUseList<T>* list) {
- HUseListNode<T>* current;
- for (HUseIterator<HInstruction*> use_it(*list); !use_it.Done(); use_it.Advance()) {
- current = use_it.Current();
- if (current->GetUser() == user && current->GetIndex() == input_index) {
- list->Remove(current);
- }
- }
+void HEnvironment::RemoveAsUserOfInput(size_t index) const {
+ const HUserRecord<HEnvironment*> user_record = vregs_.Get(index);
+ user_record.GetInstruction()->RemoveEnvironmentUser(user_record.GetUseNode());
}
HInstruction* HInstruction::GetNextDisregardingMoves() const {
@@ -494,14 +484,6 @@
return previous;
}
-void HInstruction::RemoveUser(HInstruction* user, size_t input_index) {
- RemoveFromUseList(user, input_index, &uses_);
-}
-
-void HInstruction::RemoveEnvironmentUser(HUseListNode<HEnvironment*>* use) {
- env_uses_.Remove(use);
-}
-
void HInstructionList::AddInstruction(HInstruction* instruction) {
if (first_instruction_ == nullptr) {
DCHECK(last_instruction_ == nullptr);
@@ -612,7 +594,7 @@
}
void HInstruction::ReplaceInput(HInstruction* replacement, size_t index) {
- InputAt(index)->RemoveUser(this, index);
+ RemoveAsUserOfInput(index);
SetRawInputAt(index, replacement);
replacement->AddUseAt(this, index);
}
@@ -623,7 +605,7 @@
void HPhi::AddInput(HInstruction* input) {
DCHECK(input->GetBlock() != nullptr);
- inputs_.Add(input);
+ inputs_.Add(HUserRecord<HInstruction*>(input));
input->AddUseAt(this, inputs_.Size() - 1);
}
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index 352403d..de448cc 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -31,6 +31,7 @@
namespace art {
+class GraphChecker;
class HBasicBlock;
class HEnvironment;
class HInstruction;
@@ -211,7 +212,6 @@
ArenaBitVector* visiting);
void RemoveInstructionsAsUsersFromDeadBlocks(const ArenaBitVector& visited) const;
void RemoveDeadBlocks(const ArenaBitVector& visited) const;
- void RemoveBlock(HBasicBlock* block) const;
ArenaAllocator* const arena_;
@@ -490,14 +490,17 @@
void ReplaceWith(HBasicBlock* other);
void AddInstruction(HInstruction* instruction);
- void RemoveInstruction(HInstruction* instruction);
void InsertInstructionBefore(HInstruction* instruction, HInstruction* cursor);
// Replace instruction `initial` with `replacement` within this block.
void ReplaceAndRemoveInstructionWith(HInstruction* initial,
HInstruction* replacement);
void AddPhi(HPhi* phi);
void InsertPhiAfter(HPhi* instruction, HPhi* cursor);
- void RemovePhi(HPhi* phi);
+ // RemoveInstruction and RemovePhi delete a given instruction from the respective
+ // instruction list. With 'ensure_safety' set to true, it verifies that the
+ // instruction is not in use and removes it from the use lists of its inputs.
+ void RemoveInstruction(HInstruction* instruction, bool ensure_safety = true);
+ void RemovePhi(HPhi* phi, bool ensure_safety = true);
bool IsLoopHeader() const {
return (loop_information_ != nullptr) && (loop_information_->GetHeader() == this);
@@ -715,6 +718,9 @@
}
void Remove(HUseListNode<T>* node) {
+ DCHECK(node != nullptr);
+ DCHECK(Contains(node));
+
if (node->prev_ != nullptr) {
node->prev_->next_ = node->next_;
}
@@ -726,6 +732,18 @@
}
}
+ bool Contains(const HUseListNode<T>* node) const {
+ if (node == nullptr) {
+ return false;
+ }
+ for (HUseListNode<T>* current = first_; current != nullptr; current = current->GetNext()) {
+ if (current == node) {
+ return true;
+ }
+ }
+ return false;
+ }
+
bool IsEmpty() const {
return first_ == nullptr;
}
@@ -761,6 +779,33 @@
friend class HValue;
};
+// This class is used by HEnvironment and HInstruction classes to record the
+// instructions they use and pointers to the corresponding HUseListNodes kept
+// by the used instructions.
+template <typename T>
+class HUserRecord : public ValueObject {
+ public:
+ HUserRecord() : instruction_(nullptr), use_node_(nullptr) {}
+ explicit HUserRecord(HInstruction* instruction) : instruction_(instruction), use_node_(nullptr) {}
+
+ HUserRecord(const HUserRecord<T>& old_record, HUseListNode<T>* use_node)
+ : instruction_(old_record.instruction_), use_node_(use_node) {
+ DCHECK(instruction_ != nullptr);
+ DCHECK(use_node_ != nullptr);
+ DCHECK(old_record.use_node_ == nullptr);
+ }
+
+ HInstruction* GetInstruction() const { return instruction_; }
+ HUseListNode<T>* GetUseNode() const { return use_node_; }
+
+ private:
+ // Instruction used by the user.
+ HInstruction* instruction_;
+
+ // Corresponding entry in the use list kept by 'instruction_'.
+ HUseListNode<T>* use_node_;
+};
+
// Represents the side effects an instruction may have.
class SideEffects : public ValueObject {
public:
@@ -831,46 +876,36 @@
: vregs_(arena, number_of_vregs) {
vregs_.SetSize(number_of_vregs);
for (size_t i = 0; i < number_of_vregs; i++) {
- vregs_.Put(i, VRegInfo(nullptr, nullptr));
+ vregs_.Put(i, HUserRecord<HEnvironment*>());
}
}
void CopyFrom(HEnvironment* env);
void SetRawEnvAt(size_t index, HInstruction* instruction) {
- vregs_.Put(index, VRegInfo(instruction, nullptr));
- }
-
- // Record instructions' use entries of this environment for constant-time removal.
- void RecordEnvUse(HUseListNode<HEnvironment*>* env_use) {
- DCHECK(env_use->GetUser() == this);
- size_t index = env_use->GetIndex();
- VRegInfo info = vregs_.Get(index);
- DCHECK(info.vreg_ != nullptr);
- DCHECK(info.node_ == nullptr);
- vregs_.Put(index, VRegInfo(info.vreg_, env_use));
+ vregs_.Put(index, HUserRecord<HEnvironment*>(instruction));
}
HInstruction* GetInstructionAt(size_t index) const {
- return vregs_.Get(index).vreg_;
+ return vregs_.Get(index).GetInstruction();
}
- HUseListNode<HEnvironment*>* GetInstructionEnvUseAt(size_t index) const {
- return vregs_.Get(index).node_;
- }
+ void RemoveAsUserOfInput(size_t index) const;
size_t Size() const { return vregs_.Size(); }
private:
- struct VRegInfo {
- HInstruction* vreg_;
- HUseListNode<HEnvironment*>* node_;
+ // Record instructions' use entries of this environment for constant-time removal.
+ // It should only be called by HInstruction when a new environment use is added.
+ void RecordEnvUse(HUseListNode<HEnvironment*>* env_use) {
+ DCHECK(env_use->GetUser() == this);
+ size_t index = env_use->GetIndex();
+ vregs_.Put(index, HUserRecord<HEnvironment*>(vregs_.Get(index), env_use));
+ }
- VRegInfo(HInstruction* instruction, HUseListNode<HEnvironment*>* env_use)
- : vreg_(instruction), node_(env_use) {}
- };
+ GrowableArray<HUserRecord<HEnvironment*> > vregs_;
- GrowableArray<VRegInfo> vregs_;
+ friend HInstruction;
DISALLOW_COPY_AND_ASSIGN(HEnvironment);
};
@@ -989,13 +1024,15 @@
bool IsLoopHeaderPhi() { return IsPhi() && block_->IsLoopHeader(); }
virtual size_t InputCount() const = 0;
- virtual HInstruction* InputAt(size_t i) const = 0;
+ HInstruction* InputAt(size_t i) const { return InputRecordAt(i).GetInstruction(); }
virtual void Accept(HGraphVisitor* visitor) = 0;
virtual const char* DebugName() const = 0;
virtual Primitive::Type GetType() const { return Primitive::kPrimVoid; }
- virtual void SetRawInputAt(size_t index, HInstruction* input) = 0;
+ void SetRawInputAt(size_t index, HInstruction* input) {
+ SetRawInputRecordAt(index, HUserRecord<HInstruction*>(input));
+ }
virtual bool NeedsEnvironment() const { return false; }
virtual bool IsControlFlow() const { return false; }
@@ -1018,7 +1055,10 @@
ReferenceTypeInfo GetReferenceTypeInfo() const { return reference_type_info_; }
void AddUseAt(HInstruction* user, size_t index) {
- uses_.AddUse(user, index, GetBlock()->GetGraph()->GetArena());
+ DCHECK(user != nullptr);
+ HUseListNode<HInstruction*>* use =
+ uses_.AddUse(user, index, GetBlock()->GetGraph()->GetArena());
+ user->SetRawInputRecordAt(index, HUserRecord<HInstruction*>(user->InputRecordAt(index), use));
}
void AddEnvUseAt(HEnvironment* user, size_t index) {
@@ -1028,11 +1068,13 @@
user->RecordEnvUse(env_use);
}
- void RemoveUser(HInstruction* user, size_t index);
- void RemoveEnvironmentUser(HUseListNode<HEnvironment*>* use);
+ void RemoveAsUserOfInput(size_t input) {
+ HUserRecord<HInstruction*> input_use = InputRecordAt(input);
+ input_use.GetInstruction()->uses_.Remove(input_use.GetUseNode());
+ }
- const HUseList<HInstruction*>& GetUses() { return uses_; }
- const HUseList<HEnvironment*>& GetEnvUses() { return env_uses_; }
+ const HUseList<HInstruction*>& GetUses() const { return uses_; }
+ const HUseList<HEnvironment*>& GetEnvUses() const { return env_uses_; }
bool HasUses() const { return !uses_.IsEmpty() || !env_uses_.IsEmpty(); }
bool HasEnvironmentUses() const { return !env_uses_.IsEmpty(); }
@@ -1126,7 +1168,13 @@
return NeedsEnvironment() || IsLoadClass() || IsLoadString();
}
+ protected:
+ virtual const HUserRecord<HInstruction*> InputRecordAt(size_t i) const = 0;
+ virtual void SetRawInputRecordAt(size_t index, const HUserRecord<HInstruction*>& input) = 0;
+
private:
+ void RemoveEnvironmentUser(HUseListNode<HEnvironment*>* use_node) { env_uses_.Remove(use_node); }
+
HInstruction* previous_;
HInstruction* next_;
HBasicBlock* block_;
@@ -1164,7 +1212,9 @@
// TODO: for primitive types this should be marked as invalid.
ReferenceTypeInfo reference_type_info_;
+ friend class GraphChecker;
friend class HBasicBlock;
+ friend class HEnvironment;
friend class HGraph;
friend class HInstructionList;
@@ -1284,15 +1334,16 @@
virtual ~HTemplateInstruction() {}
virtual size_t InputCount() const { return N; }
- virtual HInstruction* InputAt(size_t i) const { return inputs_[i]; }
protected:
- virtual void SetRawInputAt(size_t i, HInstruction* instruction) {
- inputs_[i] = instruction;
+ const HUserRecord<HInstruction*> InputRecordAt(size_t i) const OVERRIDE { return inputs_[i]; }
+
+ void SetRawInputRecordAt(size_t i, const HUserRecord<HInstruction*>& input) OVERRIDE {
+ inputs_[i] = input;
}
private:
- EmbeddedArray<HInstruction*, N> inputs_;
+ EmbeddedArray<HUserRecord<HInstruction*>, N> inputs_;
friend class SsaBuilder;
};
@@ -1848,7 +1899,6 @@
class HInvoke : public HInstruction {
public:
virtual size_t InputCount() const { return inputs_.Size(); }
- virtual HInstruction* InputAt(size_t i) const { return inputs_.Get(i); }
// Runtime needs to walk the stack, so Dex -> Dex calls need to
// know their environment.
@@ -1858,10 +1908,6 @@
SetRawInputAt(index, argument);
}
- virtual void SetRawInputAt(size_t index, HInstruction* input) {
- inputs_.Put(index, input);
- }
-
virtual Primitive::Type GetType() const { return return_type_; }
uint32_t GetDexPc() const { return dex_pc_; }
@@ -1893,7 +1939,12 @@
inputs_.SetSize(number_of_arguments);
}
- GrowableArray<HInstruction*> inputs_;
+ const HUserRecord<HInstruction*> InputRecordAt(size_t i) const OVERRIDE { return inputs_.Get(i); }
+ void SetRawInputRecordAt(size_t index, const HUserRecord<HInstruction*>& input) OVERRIDE {
+ inputs_.Put(index, input);
+ }
+
+ GrowableArray<HUserRecord<HInstruction*> > inputs_;
const Primitive::Type return_type_;
const uint32_t dex_pc_;
const uint32_t dex_method_index_;
@@ -2389,11 +2440,6 @@
}
size_t InputCount() const OVERRIDE { return inputs_.Size(); }
- HInstruction* InputAt(size_t i) const OVERRIDE { return inputs_.Get(i); }
-
- void SetRawInputAt(size_t index, HInstruction* input) OVERRIDE {
- inputs_.Put(index, input);
- }
void AddInput(HInstruction* input);
@@ -2412,8 +2458,15 @@
DECLARE_INSTRUCTION(Phi);
+ protected:
+ const HUserRecord<HInstruction*> InputRecordAt(size_t i) const OVERRIDE { return inputs_.Get(i); }
+
+ void SetRawInputRecordAt(size_t index, const HUserRecord<HInstruction*>& input) OVERRIDE {
+ inputs_.Put(index, input);
+ }
+
private:
- GrowableArray<HInstruction*> inputs_;
+ GrowableArray<HUserRecord<HInstruction*> > inputs_;
const uint32_t reg_number_;
Primitive::Type type_;
bool is_live_;
diff --git a/compiler/optimizing/register_allocator.cc b/compiler/optimizing/register_allocator.cc
index bfbe63f..54e62a5 100644
--- a/compiler/optimizing/register_allocator.cc
+++ b/compiler/optimizing/register_allocator.cc
@@ -48,7 +48,10 @@
physical_core_register_intervals_(allocator, codegen->GetNumberOfCoreRegisters()),
physical_fp_register_intervals_(allocator, codegen->GetNumberOfFloatingPointRegisters()),
temp_intervals_(allocator, 4),
- spill_slots_(allocator, kDefaultNumberOfSpillSlots),
+ int_spill_slots_(allocator, kDefaultNumberOfSpillSlots),
+ long_spill_slots_(allocator, kDefaultNumberOfSpillSlots),
+ float_spill_slots_(allocator, kDefaultNumberOfSpillSlots),
+ double_spill_slots_(allocator, kDefaultNumberOfSpillSlots),
safepoints_(allocator, 0),
processing_core_registers_(false),
number_of_registers_(-1),
@@ -438,7 +441,7 @@
}
}
- return ValidateIntervals(intervals, spill_slots_.Size(), reserved_out_slots_, *codegen_,
+ return ValidateIntervals(intervals, GetNumberOfSpillSlots(), reserved_out_slots_, *codegen_,
allocator_, processing_core_registers_, log_fatal_on_failure);
}
@@ -1133,41 +1136,62 @@
}
size_t end = last_sibling->GetEnd();
+ GrowableArray<size_t>* spill_slots = nullptr;
+ switch (interval->GetType()) {
+ case Primitive::kPrimDouble:
+ spill_slots = &double_spill_slots_;
+ break;
+ case Primitive::kPrimLong:
+ spill_slots = &long_spill_slots_;
+ break;
+ case Primitive::kPrimFloat:
+ spill_slots = &float_spill_slots_;
+ break;
+ case Primitive::kPrimNot:
+ case Primitive::kPrimInt:
+ case Primitive::kPrimChar:
+ case Primitive::kPrimByte:
+ case Primitive::kPrimBoolean:
+ case Primitive::kPrimShort:
+ spill_slots = &int_spill_slots_;
+ break;
+ case Primitive::kPrimVoid:
+ LOG(FATAL) << "Unexpected type for interval " << interval->GetType();
+ }
+
// Find an available spill slot.
size_t slot = 0;
- for (size_t e = spill_slots_.Size(); slot < e; ++slot) {
- // We check if it is less rather than less or equal because the parallel move
- // resolver does not work when a single spill slot needs to be exchanged with
- // a double spill slot. The strict comparison avoids needing to exchange these
- // locations at the same lifetime position.
- if (spill_slots_.Get(slot) < parent->GetStart()
- && (slot == (e - 1) || spill_slots_.Get(slot + 1) < parent->GetStart())) {
+ for (size_t e = spill_slots->Size(); slot < e; ++slot) {
+ if (spill_slots->Get(slot) <= parent->GetStart()
+ && (slot == (e - 1) || spill_slots->Get(slot + 1) <= parent->GetStart())) {
break;
}
}
if (parent->NeedsTwoSpillSlots()) {
- if (slot == spill_slots_.Size()) {
+ if (slot == spill_slots->Size()) {
// We need a new spill slot.
- spill_slots_.Add(end);
- spill_slots_.Add(end);
- } else if (slot == spill_slots_.Size() - 1) {
- spill_slots_.Put(slot, end);
- spill_slots_.Add(end);
+ spill_slots->Add(end);
+ spill_slots->Add(end);
+ } else if (slot == spill_slots->Size() - 1) {
+ spill_slots->Put(slot, end);
+ spill_slots->Add(end);
} else {
- spill_slots_.Put(slot, end);
- spill_slots_.Put(slot + 1, end);
+ spill_slots->Put(slot, end);
+ spill_slots->Put(slot + 1, end);
}
} else {
- if (slot == spill_slots_.Size()) {
+ if (slot == spill_slots->Size()) {
// We need a new spill slot.
- spill_slots_.Add(end);
+ spill_slots->Add(end);
} else {
- spill_slots_.Put(slot, end);
+ spill_slots->Put(slot, end);
}
}
- parent->SetSpillSlot((slot + reserved_out_slots_) * kVRegSize);
+ // Note that the exact spill slot location will be computed when we resolve,
+ // that is when we know the number of spill slots for each type.
+ parent->SetSpillSlot(slot);
}
static bool IsValidDestination(Location destination) {
@@ -1516,7 +1540,7 @@
}
void RegisterAllocator::Resolve() {
- codegen_->InitializeCodeGeneration(spill_slots_.Size(),
+ codegen_->InitializeCodeGeneration(GetNumberOfSpillSlots(),
maximum_number_of_live_core_registers_,
maximum_number_of_live_fp_registers_,
reserved_out_slots_,
@@ -1542,6 +1566,39 @@
} else if (current->HasSpillSlot()) {
current->SetSpillSlot(current->GetSpillSlot() + codegen_->GetFrameSize());
}
+ } else if (current->HasSpillSlot()) {
+ // Adjust the stack slot, now that we know the number of them for each type.
+ // The way this implementation lays out the stack is the following:
+ // [parameter slots ]
+ // [double spill slots ]
+ // [long spill slots ]
+ // [float spill slots ]
+ // [int/ref values ]
+ // [maximum out values ] (number of arguments for calls)
+ // [art method ].
+ uint32_t slot = current->GetSpillSlot();
+ switch (current->GetType()) {
+ case Primitive::kPrimDouble:
+ slot += long_spill_slots_.Size();
+ FALLTHROUGH_INTENDED;
+ case Primitive::kPrimLong:
+ slot += float_spill_slots_.Size();
+ FALLTHROUGH_INTENDED;
+ case Primitive::kPrimFloat:
+ slot += int_spill_slots_.Size();
+ FALLTHROUGH_INTENDED;
+ case Primitive::kPrimNot:
+ case Primitive::kPrimInt:
+ case Primitive::kPrimChar:
+ case Primitive::kPrimByte:
+ case Primitive::kPrimBoolean:
+ case Primitive::kPrimShort:
+ slot += reserved_out_slots_;
+ break;
+ case Primitive::kPrimVoid:
+ LOG(FATAL) << "Unexpected type for interval " << current->GetType();
+ }
+ current->SetSpillSlot(slot * kVRegSize);
}
Location source = current->ToLocation();
diff --git a/compiler/optimizing/register_allocator.h b/compiler/optimizing/register_allocator.h
index b8f70bd..ff2f106 100644
--- a/compiler/optimizing/register_allocator.h
+++ b/compiler/optimizing/register_allocator.h
@@ -75,7 +75,10 @@
}
size_t GetNumberOfSpillSlots() const {
- return spill_slots_.Size();
+ return int_spill_slots_.Size()
+ + long_spill_slots_.Size()
+ + float_spill_slots_.Size()
+ + double_spill_slots_.Size();
}
private:
@@ -171,8 +174,14 @@
// where an instruction requires a temporary.
GrowableArray<LiveInterval*> temp_intervals_;
- // The spill slots allocated for live intervals.
- GrowableArray<size_t> spill_slots_;
+ // The spill slots allocated for live intervals. We ensure spill slots
+ // are typed to avoid (1) doing moves and swaps between two different kinds
+ // of registers, and (2) swapping between a single stack slot and a double
+ // stack slot. This simplifies the parallel move resolver.
+ GrowableArray<size_t> int_spill_slots_;
+ GrowableArray<size_t> long_spill_slots_;
+ GrowableArray<size_t> float_spill_slots_;
+ GrowableArray<size_t> double_spill_slots_;
// Instructions that need a safepoint.
GrowableArray<HInstruction*> safepoints_;
diff --git a/compiler/optimizing/ssa_phi_elimination.cc b/compiler/optimizing/ssa_phi_elimination.cc
index f66a1c8..2f2e2d1 100644
--- a/compiler/optimizing/ssa_phi_elimination.cc
+++ b/compiler/optimizing/ssa_phi_elimination.cc
@@ -64,31 +64,33 @@
HBasicBlock* block = it.Current();
HInstruction* current = block->GetFirstPhi();
HInstruction* next = nullptr;
+ HPhi* phi;
while (current != nullptr) {
+ phi = current->AsPhi();
next = current->GetNext();
- if (current->AsPhi()->IsDead()) {
- if (current->HasUses()) {
- for (HUseIterator<HInstruction*> use_it(current->GetUses()); !use_it.Done();
+ if (phi->IsDead()) {
+ // Make sure the phi is only used by other dead phis.
+ if (kIsDebugBuild) {
+ for (HUseIterator<HInstruction*> use_it(phi->GetUses()); !use_it.Done();
use_it.Advance()) {
- HUseListNode<HInstruction*>* user_node = use_it.Current();
- HInstruction* user = user_node->GetUser();
+ HInstruction* user = use_it.Current()->GetUser();
DCHECK(user->IsLoopHeaderPhi()) << user->GetId();
DCHECK(user->AsPhi()->IsDead()) << user->GetId();
- // Just put itself as an input. The phi will be removed in this loop anyway.
- user->SetRawInputAt(user_node->GetIndex(), user);
- current->RemoveUser(user, user_node->GetIndex());
}
}
- if (current->HasEnvironmentUses()) {
- for (HUseIterator<HEnvironment*> use_it(current->GetEnvUses()); !use_it.Done();
- use_it.Advance()) {
- HUseListNode<HEnvironment*>* user_node = use_it.Current();
- HEnvironment* user = user_node->GetUser();
- user->SetRawEnvAt(user_node->GetIndex(), nullptr);
- current->RemoveEnvironmentUser(user_node);
- }
+ // Remove the phi from use lists of its inputs.
+ for (size_t i = 0, e = phi->InputCount(); i < e; ++i) {
+ phi->RemoveAsUserOfInput(i);
}
- block->RemovePhi(current->AsPhi());
+ // Remove the phi from environments that use it.
+ for (HUseIterator<HEnvironment*> use_it(phi->GetEnvUses()); !use_it.Done();
+ use_it.Advance()) {
+ HUseListNode<HEnvironment*>* user_node = use_it.Current();
+ HEnvironment* user = user_node->GetUser();
+ user->SetRawEnvAt(user_node->GetIndex(), nullptr);
+ }
+ // Delete it from the instruction list.
+ block->RemovePhi(phi, /*ensure_safety=*/ false);
}
current = next;
}
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index e607e15..0b1f14d 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -36,6 +36,7 @@
#include "arch/instruction_set_features.h"
#include "arch/mips/instruction_set_features_mips.h"
#include "base/dumpable.h"
+#include "base/macros.h"
#include "base/stl_util.h"
#include "base/stringpiece.h"
#include "base/timing_logger.h"
@@ -97,7 +98,7 @@
va_end(ap);
}
-[[noreturn]] static void Usage(const char* fmt, ...) {
+NO_RETURN static void Usage(const char* fmt, ...) {
va_list ap;
va_start(ap, fmt);
UsageErrorV(fmt, ap);
@@ -326,7 +327,7 @@
message.c_str());
}
- [[noreturn]] static void Fatal(const std::string& message) {
+ NO_RETURN static void Fatal(const std::string& message) {
Message('F', message);
exit(1);
}
diff --git a/patchoat/patchoat.cc b/patchoat/patchoat.cc
index 2059a96..3c6a23d 100644
--- a/patchoat/patchoat.cc
+++ b/patchoat/patchoat.cc
@@ -763,7 +763,7 @@
va_end(ap);
}
-[[noreturn]] static void Usage(const char *fmt, ...) {
+NO_RETURN static void Usage(const char *fmt, ...) {
va_list ap;
va_start(ap, fmt);
UsageErrorV(fmt, ap);
diff --git a/runtime/arch/arm/context_arm.cc b/runtime/arch/arm/context_arm.cc
index c181e43..5bd23d0 100644
--- a/runtime/arch/arm/context_arm.cc
+++ b/runtime/arch/arm/context_arm.cc
@@ -106,7 +106,7 @@
fprs_[S15] = nullptr;
}
-extern "C" void art_quick_do_long_jump(uint32_t*, uint32_t*);
+extern "C" NO_RETURN void art_quick_do_long_jump(uint32_t*, uint32_t*);
void ArmContext::DoLongJump() {
uintptr_t gprs[kNumberOfCoreRegisters];
diff --git a/runtime/arch/arm/context_arm.h b/runtime/arch/arm/context_arm.h
index 1ca973e..5bdeda7 100644
--- a/runtime/arch/arm/context_arm.h
+++ b/runtime/arch/arm/context_arm.h
@@ -19,6 +19,7 @@
#include "arch/context.h"
#include "base/logging.h"
+#include "base/macros.h"
#include "registers_arm.h"
namespace art {
@@ -76,7 +77,7 @@
void SetFPR(uint32_t reg, uintptr_t value) OVERRIDE;
void SmashCallerSaves() OVERRIDE;
- void DoLongJump() OVERRIDE;
+ NO_RETURN void DoLongJump() OVERRIDE;
private:
// Pointers to register locations, initialized to NULL or the specific registers below.
diff --git a/runtime/arch/arm64/context_arm64.cc b/runtime/arch/arm64/context_arm64.cc
index 7fc0555..ec9c122 100644
--- a/runtime/arch/arm64/context_arm64.cc
+++ b/runtime/arch/arm64/context_arm64.cc
@@ -133,7 +133,7 @@
fprs_[D31] = nullptr;
}
-extern "C" void art_quick_do_long_jump(uint64_t*, uint64_t*);
+extern "C" NO_RETURN void art_quick_do_long_jump(uint64_t*, uint64_t*);
void Arm64Context::DoLongJump() {
uint64_t gprs[kNumberOfXRegisters];
diff --git a/runtime/arch/arm64/context_arm64.h b/runtime/arch/arm64/context_arm64.h
index 6a4485b..f486779 100644
--- a/runtime/arch/arm64/context_arm64.h
+++ b/runtime/arch/arm64/context_arm64.h
@@ -19,6 +19,7 @@
#include "arch/context.h"
#include "base/logging.h"
+#include "base/macros.h"
#include "registers_arm64.h"
namespace art {
@@ -76,7 +77,7 @@
void SetFPR(uint32_t reg, uintptr_t value) OVERRIDE;
void SmashCallerSaves() OVERRIDE;
- void DoLongJump() OVERRIDE;
+ NO_RETURN void DoLongJump() OVERRIDE;
private:
// Pointers to register locations, initialized to NULL or the specific registers below.
diff --git a/runtime/arch/context.h b/runtime/arch/context.h
index ed8cab0..f86f9ae 100644
--- a/runtime/arch/context.h
+++ b/runtime/arch/context.h
@@ -20,6 +20,7 @@
#include <stddef.h>
#include <stdint.h>
+#include "base/macros.h"
#include "base/mutex.h"
namespace art {
@@ -78,7 +79,7 @@
virtual void SmashCallerSaves() = 0;
// Switches execution of the executing context to this context
- virtual void DoLongJump() = 0;
+ NO_RETURN virtual void DoLongJump() = 0;
protected:
enum {
diff --git a/runtime/arch/mips/context_mips.cc b/runtime/arch/mips/context_mips.cc
index 6c0ab98..3b525be 100644
--- a/runtime/arch/mips/context_mips.cc
+++ b/runtime/arch/mips/context_mips.cc
@@ -90,7 +90,7 @@
gprs_[A3] = nullptr;
}
-extern "C" void art_quick_do_long_jump(uint32_t*, uint32_t*);
+extern "C" NO_RETURN void art_quick_do_long_jump(uint32_t*, uint32_t*);
void MipsContext::DoLongJump() {
uintptr_t gprs[kNumberOfCoreRegisters];
diff --git a/runtime/arch/mips/context_mips.h b/runtime/arch/mips/context_mips.h
index d8a0b67..cbad3f963 100644
--- a/runtime/arch/mips/context_mips.h
+++ b/runtime/arch/mips/context_mips.h
@@ -19,6 +19,7 @@
#include "arch/context.h"
#include "base/logging.h"
+#include "base/macros.h"
#include "registers_mips.h"
namespace art {
@@ -75,7 +76,7 @@
void SetFPR(uint32_t reg, uintptr_t value) OVERRIDE;
void SmashCallerSaves() OVERRIDE;
- void DoLongJump() OVERRIDE;
+ NO_RETURN void DoLongJump() OVERRIDE;
private:
// Pointers to registers in the stack, initialized to NULL except for the special cases below.
diff --git a/runtime/arch/mips64/context_mips64.cc b/runtime/arch/mips64/context_mips64.cc
index 1c96bd4..ce99b40 100644
--- a/runtime/arch/mips64/context_mips64.cc
+++ b/runtime/arch/mips64/context_mips64.cc
@@ -121,7 +121,7 @@
fprs_[F23] = nullptr;
}
-extern "C" void art_quick_do_long_jump(uintptr_t*, uintptr_t*);
+extern "C" NO_RETURN void art_quick_do_long_jump(uintptr_t*, uintptr_t*);
void Mips64Context::DoLongJump() {
uintptr_t gprs[kNumberOfGpuRegisters];
diff --git a/runtime/arch/mips64/context_mips64.h b/runtime/arch/mips64/context_mips64.h
index 1046723..2cc2b8d 100644
--- a/runtime/arch/mips64/context_mips64.h
+++ b/runtime/arch/mips64/context_mips64.h
@@ -19,6 +19,7 @@
#include "arch/context.h"
#include "base/logging.h"
+#include "base/macros.h"
#include "registers_mips64.h"
namespace art {
@@ -75,7 +76,7 @@
void SetFPR(uint32_t reg, uintptr_t value) OVERRIDE;
void SmashCallerSaves() OVERRIDE;
- void DoLongJump() OVERRIDE;
+ NO_RETURN void DoLongJump() OVERRIDE;
private:
// Pointers to registers in the stack, initialized to NULL except for the special cases below.
diff --git a/runtime/arch/x86/context_x86.cc b/runtime/arch/x86/context_x86.cc
index 4ea4684..52a35dd 100644
--- a/runtime/arch/x86/context_x86.cc
+++ b/runtime/arch/x86/context_x86.cc
@@ -133,6 +133,7 @@
#else
UNIMPLEMENTED(FATAL);
#endif
+ UNREACHABLE();
}
} // namespace x86
diff --git a/runtime/arch/x86/context_x86.h b/runtime/arch/x86/context_x86.h
index c66a9dc..ace4670 100644
--- a/runtime/arch/x86/context_x86.h
+++ b/runtime/arch/x86/context_x86.h
@@ -19,6 +19,7 @@
#include "arch/context.h"
#include "base/logging.h"
+#include "base/macros.h"
#include "registers_x86.h"
namespace art {
@@ -75,7 +76,7 @@
void SetFPR(uint32_t reg, uintptr_t value) OVERRIDE;
void SmashCallerSaves() OVERRIDE;
- void DoLongJump() OVERRIDE;
+ NO_RETURN void DoLongJump() OVERRIDE;
private:
// Pretend XMM registers are made of uin32_t pieces, because they are manipulated
diff --git a/runtime/arch/x86_64/context_x86_64.cc b/runtime/arch/x86_64/context_x86_64.cc
index cdc2ec7..6336541 100644
--- a/runtime/arch/x86_64/context_x86_64.cc
+++ b/runtime/arch/x86_64/context_x86_64.cc
@@ -105,7 +105,7 @@
*fprs_[reg] = value;
}
-extern "C" void art_quick_do_long_jump(uintptr_t*, uintptr_t*);
+extern "C" NO_RETURN void art_quick_do_long_jump(uintptr_t*, uintptr_t*);
void X86_64Context::DoLongJump() {
#if defined(__x86_64__)
@@ -127,6 +127,7 @@
art_quick_do_long_jump(gprs, fprs);
#else
UNIMPLEMENTED(FATAL);
+ UNREACHABLE();
#endif
}
diff --git a/runtime/arch/x86_64/context_x86_64.h b/runtime/arch/x86_64/context_x86_64.h
index 0dda06e..d03aa45 100644
--- a/runtime/arch/x86_64/context_x86_64.h
+++ b/runtime/arch/x86_64/context_x86_64.h
@@ -19,6 +19,7 @@
#include "arch/context.h"
#include "base/logging.h"
+#include "base/macros.h"
#include "registers_x86_64.h"
namespace art {
@@ -75,7 +76,7 @@
void SetFPR(uint32_t reg, uintptr_t value) OVERRIDE;
void SmashCallerSaves() OVERRIDE;
- void DoLongJump() OVERRIDE;
+ NO_RETURN void DoLongJump() OVERRIDE;
private:
// Pointers to register locations. Values are initialized to NULL or the special registers below.
diff --git a/runtime/base/macros.h b/runtime/base/macros.h
index f705469..3a9de5f 100644
--- a/runtime/base/macros.h
+++ b/runtime/base/macros.h
@@ -186,6 +186,9 @@
// without the UNREACHABLE a return statement would be necessary.
#define UNREACHABLE __builtin_unreachable
+// Add the C++11 noreturn attribute.
+#define NO_RETURN [[ noreturn ]] // NOLINT[whitespace/braces] [5]
+
// The FALLTHROUGH_INTENDED macro can be used to annotate implicit fall-through
// between switch labels:
// switch (x) {
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index ee66b49..3278751 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -1008,10 +1008,15 @@
// Failed, bail.
if (open_oat_file.get() == nullptr) {
- std::string error_msg;
// dex2oat was disabled or crashed. Add the dex file in the list of dex_files to make progress.
- DexFile::Open(dex_location, dex_location, &error_msg, dex_files);
- error_msgs->push_back(error_msg);
+ if (Runtime::Current()->IsDexFileFallbackEnabled()) {
+ std::string error_msg;
+ if (!DexFile::Open(dex_location, dex_location, &error_msg, dex_files)) {
+ error_msgs->push_back(error_msg);
+ }
+ } else {
+ error_msgs->push_back("Fallback mode disabled, skipping dex files.");
+ }
return false;
}
diff --git a/runtime/debugger.cc b/runtime/debugger.cc
index a3d3b47..13bbdeb 100644
--- a/runtime/debugger.cc
+++ b/runtime/debugger.cc
@@ -605,7 +605,7 @@
}
void Dbg::ClearWaitForEventThread() {
- gJdwpState->ClearWaitForEventThread();
+ gJdwpState->ReleaseJdwpTokenForEvent();
}
void Dbg::Connected() {
@@ -3644,6 +3644,11 @@
thread_list->Resume(targetThread, true);
}
+ // The target thread is resumed but needs the JDWP token we're holding.
+ // We release it now and will acquire it again when the invocation is
+ // complete and the target thread suspends itself.
+ gJdwpState->ReleaseJdwpTokenForCommand();
+
// Wait for the request to finish executing.
while (req->invoke_needed) {
req->cond.Wait(self);
@@ -3653,6 +3658,10 @@
/* wait for thread to re-suspend itself */
SuspendThread(thread_id, false /* request_suspension */);
+
+ // Now the thread is suspended again, we can re-acquire the JDWP token.
+ gJdwpState->AcquireJdwpTokenForCommand();
+
self->TransitionFromSuspendedToRunnable();
}
@@ -3660,7 +3669,7 @@
* Suspend the threads. We waited for the target thread to suspend
* itself, so all we need to do is suspend the others.
*
- * The suspendAllThreads() call will double-suspend the event thread,
+ * The SuspendAllForDebugger() call will double-suspend the event thread,
* so we want to resume the target thread once to keep the books straight.
*/
if ((options & JDWP::INVOKE_SINGLE_THREADED) == 0) {
diff --git a/runtime/gc_root.h b/runtime/gc_root.h
index 7e0be64..c5feda5 100644
--- a/runtime/gc_root.h
+++ b/runtime/gc_root.h
@@ -71,7 +71,7 @@
typedef void (RootCallback)(mirror::Object** root, void* arg, const RootInfo& root_info);
template<class MirrorType>
-class PACKED(4) GcRoot {
+class GcRoot {
public:
template<ReadBarrierOption kReadBarrierOption = kWithReadBarrier>
ALWAYS_INLINE MirrorType* Read() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
diff --git a/runtime/indirect_reference_table.h b/runtime/indirect_reference_table.h
index 7f7870a..576a604 100644
--- a/runtime/indirect_reference_table.h
+++ b/runtime/indirect_reference_table.h
@@ -197,7 +197,7 @@
// Contains multiple entries but only one active one, this helps us detect use after free errors
// since the serial stored in the indirect ref wont match.
static const size_t kIRTPrevCount = kIsDebugBuild ? 7 : 3;
-class PACKED(4) IrtEntry {
+class IrtEntry {
public:
void Add(mirror::Object* obj) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
++serial_;
@@ -218,6 +218,8 @@
uint32_t serial_;
GcRoot<mirror::Object> references_[kIRTPrevCount];
};
+static_assert(sizeof(IrtEntry) == (1 + kIRTPrevCount) * sizeof(uintptr_t),
+ "Unexpected sizeof(IrtEntry)");
class IrtIterator {
public:
diff --git a/runtime/interpreter/interpreter_common.h b/runtime/interpreter/interpreter_common.h
index ce7c1c3..06b809f 100644
--- a/runtime/interpreter/interpreter_common.h
+++ b/runtime/interpreter/interpreter_common.h
@@ -25,6 +25,7 @@
#include <sstream>
#include "base/logging.h"
+#include "base/macros.h"
#include "class_linker-inl.h"
#include "common_throws.h"
#include "dex_file-inl.h"
@@ -349,8 +350,8 @@
uint32_t dex_pc, const instrumentation::Instrumentation* instrumentation)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-void UnexpectedOpcode(const Instruction* inst, const ShadowFrame& shadow_frame)
- __attribute__((cold, noreturn))
+NO_RETURN void UnexpectedOpcode(const Instruction* inst, const ShadowFrame& shadow_frame)
+ __attribute__((cold))
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
static inline void TraceExecution(const ShadowFrame& shadow_frame, const Instruction* inst,
diff --git a/runtime/jdwp/jdwp.h b/runtime/jdwp/jdwp.h
index 9f37998..e16221c 100644
--- a/runtime/jdwp/jdwp.h
+++ b/runtime/jdwp/jdwp.h
@@ -149,29 +149,19 @@
void ExitAfterReplying(int exit_status);
- /*
- * When we hit a debugger event that requires suspension, it's important
- * that we wait for the thread to suspend itself before processing any
- * additional requests. (Otherwise, if the debugger immediately sends a
- * "resume thread" command, the resume might arrive before the thread has
- * suspended itself.)
- *
- * The thread should call the "set" function before sending the event to
- * the debugger. The main JDWP handler loop calls "get" before processing
- * an event, and will wait for thread suspension if it's set. Once the
- * thread has suspended itself, the JDWP handler calls "clear" and
- * continues processing the current event. This works in the suspend-all
- * case because the event thread doesn't suspend itself until everything
- * else has suspended.
- *
- * It's possible that multiple threads could encounter thread-suspending
- * events at the same time, so we grab a mutex in the "set" call, and
- * release it in the "clear" call.
- */
- // ObjectId GetWaitForEventThread();
- void SetWaitForEventThread(ObjectId threadId)
- LOCKS_EXCLUDED(event_thread_lock_, process_request_lock_);
- void ClearWaitForEventThread() LOCKS_EXCLUDED(event_thread_lock_);
+ // Acquires/releases the JDWP synchronization token for the debugger
+ // thread (command handler) so no event thread posts an event while
+ // it processes a command. This must be called only from the debugger
+ // thread.
+ void AcquireJdwpTokenForCommand() LOCKS_EXCLUDED(jdwp_token_lock_);
+ void ReleaseJdwpTokenForCommand() LOCKS_EXCLUDED(jdwp_token_lock_);
+
+ // Acquires/releases the JDWP synchronization token for the event thread
+ // so no other thread (debugger thread or event thread) interleaves with
+ // it when posting an event. This must NOT be called from the debugger
+ // thread, only event thread.
+ void AcquireJdwpTokenForEvent(ObjectId threadId) LOCKS_EXCLUDED(jdwp_token_lock_);
+ void ReleaseJdwpTokenForEvent() LOCKS_EXCLUDED(jdwp_token_lock_);
/*
* These notify the debug code that something interesting has happened. This
@@ -330,9 +320,37 @@
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
void SendBufferedRequest(uint32_t type, const std::vector<iovec>& iov);
- void StartProcessingRequest() LOCKS_EXCLUDED(process_request_lock_);
- void EndProcessingRequest() LOCKS_EXCLUDED(process_request_lock_);
- void WaitForProcessingRequest() LOCKS_EXCLUDED(process_request_lock_);
+ /*
+ * When we hit a debugger event that requires suspension, it's important
+ * that we wait for the thread to suspend itself before processing any
+ * additional requests. Otherwise, if the debugger immediately sends a
+ * "resume thread" command, the resume might arrive before the thread has
+ * suspended itself.
+ *
+ * It's also important no event thread suspends while we process a command
+ * from the debugger. Otherwise we could post an event ("thread death")
+ * before sending the reply of the command being processed ("resume") and
+ * cause bad synchronization with the debugger.
+ *
+ * The thread wanting "exclusive" access to the JDWP world must call the
+ * SetWaitForJdwpToken method before processing a command from the
+ * debugger or sending an event to the debugger.
+ * Once the command is processed or the event thread has posted its event,
+ * it must call the ClearWaitForJdwpToken method to allow another thread
+ * to do JDWP stuff.
+ *
+ * Therefore the main JDWP handler loop will wait for the event thread
+ * suspension before processing the next command. Once the event thread
+ * has suspended itself and cleared the token, the JDWP handler continues
+ * processing commands. This works in the suspend-all case because the
+ * event thread doesn't suspend itself until everything else has suspended.
+ *
+ * It's possible that multiple threads could encounter thread-suspending
+ * events at the same time, so we grab a mutex in the SetWaitForJdwpToken
+ * call, and release it in the ClearWaitForJdwpToken call.
+ */
+ void SetWaitForJdwpToken(ObjectId threadId) LOCKS_EXCLUDED(jdwp_token_lock_);
+ void ClearWaitForJdwpToken() LOCKS_EXCLUDED(jdwp_token_lock_);
public: // TODO: fix privacy
const JdwpOptions* options_;
@@ -368,24 +386,21 @@
// Linked list of events requested by the debugger (breakpoints, class prep, etc).
Mutex event_list_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER ACQUIRED_BEFORE(Locks::breakpoint_lock_);
-
JdwpEvent* event_list_ GUARDED_BY(event_list_lock_);
size_t event_list_size_ GUARDED_BY(event_list_lock_); // Number of elements in event_list_.
- // Used to synchronize suspension of the event thread (to avoid receiving "resume"
- // events before the thread has finished suspending itself).
- Mutex event_thread_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
- ConditionVariable event_thread_cond_ GUARDED_BY(event_thread_lock_);
- ObjectId event_thread_id_;
-
- // Used to synchronize request processing and event sending (to avoid sending an event before
- // sending the reply of a command being processed).
- Mutex process_request_lock_ ACQUIRED_AFTER(event_thread_lock_);
- ConditionVariable process_request_cond_ GUARDED_BY(process_request_lock_);
- bool processing_request_ GUARDED_BY(process_request_lock_);
+ // Used to synchronize JDWP command handler thread and event threads so only one
+ // thread does JDWP stuff at a time. This prevent from interleaving command handling
+ // and event notification. Otherwise we could receive a "resume" command for an
+ // event thread that is not suspended yet, or post a "thread death" or event "VM death"
+ // event before sending the reply of the "resume" command that caused it.
+ Mutex jdwp_token_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
+ ConditionVariable jdwp_token_cond_ GUARDED_BY(jdwp_token_lock_);
+ ObjectId jdwp_token_owner_thread_id_;
bool ddm_is_active_;
+ // Used for VirtualMachine.Exit command handling.
bool should_exit_;
int exit_status_;
};
diff --git a/runtime/jdwp/jdwp_event.cc b/runtime/jdwp/jdwp_event.cc
index a8eaa26..b71f6cd 100644
--- a/runtime/jdwp/jdwp_event.cc
+++ b/runtime/jdwp/jdwp_event.cc
@@ -612,7 +612,7 @@
}
/* grab this before posting/suspending again */
- SetWaitForEventThread(thread_self_id);
+ AcquireJdwpTokenForEvent(thread_self_id);
/* leave pReq->invoke_needed_ raised so we can check reentrancy */
Dbg::ExecuteMethod(pReq);
@@ -630,7 +630,7 @@
JDWP::ObjectId thread_self_id = Dbg::GetThreadSelfId();
self->TransitionFromRunnableToSuspended(kWaitingForDebuggerSend);
if (suspend_policy != SP_NONE) {
- SetWaitForEventThread(threadId);
+ AcquireJdwpTokenForEvent(threadId);
}
EventFinish(pReq);
SuspendByPolicy(suspend_policy, thread_self_id);
@@ -649,63 +649,82 @@
return pReq->invoke_needed;
}
+void JdwpState::AcquireJdwpTokenForCommand() {
+ CHECK_EQ(Thread::Current(), GetDebugThread()) << "Expected debugger thread";
+ SetWaitForJdwpToken(debug_thread_id_);
+}
+
+void JdwpState::ReleaseJdwpTokenForCommand() {
+ CHECK_EQ(Thread::Current(), GetDebugThread()) << "Expected debugger thread";
+ ClearWaitForJdwpToken();
+}
+
+void JdwpState::AcquireJdwpTokenForEvent(ObjectId threadId) {
+ CHECK_NE(Thread::Current(), GetDebugThread()) << "Expected event thread";
+ CHECK_NE(debug_thread_id_, threadId) << "Not expected debug thread";
+ SetWaitForJdwpToken(threadId);
+}
+
+void JdwpState::ReleaseJdwpTokenForEvent() {
+ CHECK_NE(Thread::Current(), GetDebugThread()) << "Expected event thread";
+ ClearWaitForJdwpToken();
+}
+
/*
* We need the JDWP thread to hold off on doing stuff while we post an
* event and then suspend ourselves.
*
- * Call this with a threadId of zero if you just want to wait for the
- * current thread operation to complete.
- *
* This could go to sleep waiting for another thread, so it's important
* that the thread be marked as VMWAIT before calling here.
*/
-void JdwpState::SetWaitForEventThread(ObjectId threadId) {
+void JdwpState::SetWaitForJdwpToken(ObjectId threadId) {
bool waited = false;
+ Thread* const self = Thread::Current();
+ CHECK_NE(threadId, 0u);
+ CHECK_NE(self->GetState(), kRunnable);
+ Locks::mutator_lock_->AssertNotHeld(self);
/* this is held for very brief periods; contention is unlikely */
- Thread* self = Thread::Current();
- MutexLock mu(self, event_thread_lock_);
+ MutexLock mu(self, jdwp_token_lock_);
+
+ CHECK_NE(jdwp_token_owner_thread_id_, threadId) << "Thread is already holding event thread lock";
/*
* If another thread is already doing stuff, wait for it. This can
* go to sleep indefinitely.
*/
- while (event_thread_id_ != 0) {
+ while (jdwp_token_owner_thread_id_ != 0) {
VLOG(jdwp) << StringPrintf("event in progress (%#" PRIx64 "), %#" PRIx64 " sleeping",
- event_thread_id_, threadId);
+ jdwp_token_owner_thread_id_, threadId);
waited = true;
- event_thread_cond_.Wait(self);
+ jdwp_token_cond_.Wait(self);
}
- if (waited || threadId != 0) {
+ if (waited || threadId != debug_thread_id_) {
VLOG(jdwp) << StringPrintf("event token grabbed (%#" PRIx64 ")", threadId);
}
- if (threadId != 0) {
- event_thread_id_ = threadId;
- }
+ jdwp_token_owner_thread_id_ = threadId;
}
/*
* Clear the threadId and signal anybody waiting.
*/
-void JdwpState::ClearWaitForEventThread() {
+void JdwpState::ClearWaitForJdwpToken() {
/*
* Grab the mutex. Don't try to go in/out of VMWAIT mode, as this
- * function is called by dvmSuspendSelf(), and the transition back
+ * function is called by Dbg::SuspendSelf(), and the transition back
* to RUNNING would confuse it.
*/
- Thread* self = Thread::Current();
- MutexLock mu(self, event_thread_lock_);
+ Thread* const self = Thread::Current();
+ MutexLock mu(self, jdwp_token_lock_);
- CHECK_NE(event_thread_id_, 0U);
- VLOG(jdwp) << StringPrintf("cleared event token (%#" PRIx64 ")", event_thread_id_);
+ CHECK_NE(jdwp_token_owner_thread_id_, 0U);
+ VLOG(jdwp) << StringPrintf("cleared event token (%#" PRIx64 ")", jdwp_token_owner_thread_id_);
- event_thread_id_ = 0;
-
- event_thread_cond_.Signal(self);
+ jdwp_token_owner_thread_id_ = 0;
+ jdwp_token_cond_.Signal(self);
}
-
/*
* Prep an event. Allocates storage for the message and leaves space for
* the header.
@@ -730,11 +749,6 @@
Set1(buf + 9, kJdwpEventCommandSet);
Set1(buf + 10, kJdwpCompositeCommand);
- // Prevents from interleaving commands and events. Otherwise we could end up in sending an event
- // before sending the reply of the command being processed and would lead to bad synchronization
- // between the debugger and the debuggee.
- WaitForProcessingRequest();
-
SendRequest(pReq);
expandBufFree(pReq);
diff --git a/runtime/jdwp/jdwp_handler.cc b/runtime/jdwp/jdwp_handler.cc
index a1d2a6c..0ce4de7 100644
--- a/runtime/jdwp/jdwp_handler.cc
+++ b/runtime/jdwp/jdwp_handler.cc
@@ -1633,27 +1633,15 @@
/*
* If a debugger event has fired in another thread, wait until the
- * initiating thread has suspended itself before processing messages
+ * initiating thread has suspended itself before processing commands
* from the debugger. Otherwise we (the JDWP thread) could be told to
* resume the thread before it has suspended.
*
- * We call with an argument of zero to wait for the current event
- * thread to finish, and then clear the block. Depending on the thread
- * suspend policy, this may allow events in other threads to fire,
- * but those events have no bearing on what the debugger has sent us
- * in the current request->
- *
* Note that we MUST clear the event token before waking the event
* thread up, or risk waiting for the thread to suspend after we've
* told it to resume.
*/
- SetWaitForEventThread(0);
-
- /*
- * We do not want events to be sent while we process a request-> Indicate the JDWP thread starts
- * to process a request so other threads wait for it to finish before sending an event.
- */
- StartProcessingRequest();
+ AcquireJdwpTokenForCommand();
/*
* Tell the VM that we're running and shouldn't be interrupted by GC.
@@ -1719,50 +1707,6 @@
return replyLength;
}
-/*
- * Indicates a request is about to be processed. If a thread wants to send an event in the meantime,
- * it will need to wait until we processed this request (see EndProcessingRequest).
- */
-void JdwpState::StartProcessingRequest() {
- Thread* self = Thread::Current();
- CHECK_EQ(self, GetDebugThread()) << "Requests are only processed by debug thread";
- MutexLock mu(self, process_request_lock_);
- CHECK_EQ(processing_request_, false);
- processing_request_ = true;
-}
-
-/*
- * Indicates a request has been processed (and we sent its reply). All threads waiting for us (see
- * WaitForProcessingRequest) are waken up so they can send events again.
- */
-void JdwpState::EndProcessingRequest() {
- Thread* self = Thread::Current();
- CHECK_EQ(self, GetDebugThread()) << "Requests are only processed by debug thread";
- MutexLock mu(self, process_request_lock_);
- CHECK_EQ(processing_request_, true);
- processing_request_ = false;
- process_request_cond_.Broadcast(self);
-}
-
-/*
- * Waits for any request being processed so we do not send an event in the meantime.
- */
-void JdwpState::WaitForProcessingRequest() {
- Thread* self = Thread::Current();
- CHECK_NE(self, GetDebugThread()) << "Events should not be posted by debug thread";
- MutexLock mu(self, process_request_lock_);
- bool waited = false;
- while (processing_request_) {
- VLOG(jdwp) << StringPrintf("wait for processing request");
- waited = true;
- process_request_cond_.Wait(self);
- }
- if (waited) {
- VLOG(jdwp) << StringPrintf("finished waiting for processing request");
- }
- CHECK_EQ(processing_request_, false);
-}
-
} // namespace JDWP
} // namespace art
diff --git a/runtime/jdwp/jdwp_main.cc b/runtime/jdwp/jdwp_main.cc
index b04aa6e..b6fedd9 100644
--- a/runtime/jdwp/jdwp_main.cc
+++ b/runtime/jdwp/jdwp_main.cc
@@ -220,12 +220,9 @@
event_list_lock_("JDWP event list lock", kJdwpEventListLock),
event_list_(nullptr),
event_list_size_(0),
- event_thread_lock_("JDWP event thread lock"),
- event_thread_cond_("JDWP event thread condition variable", event_thread_lock_),
- event_thread_id_(0),
- process_request_lock_("JDWP process request lock"),
- process_request_cond_("JDWP process request condition variable", process_request_lock_),
- processing_request_(false),
+ jdwp_token_lock_("JDWP token lock"),
+ jdwp_token_cond_("JDWP token condition variable", jdwp_token_lock_),
+ jdwp_token_owner_thread_id_(0),
ddm_is_active_(false),
should_exit_(false),
exit_status_(0) {
@@ -331,7 +328,7 @@
* Should not have one of these in progress. If the debugger went away
* mid-request, though, we could see this.
*/
- if (event_thread_id_ != 0) {
+ if (jdwp_token_owner_thread_id_ != 0) {
LOG(WARNING) << "Resetting state while event in progress";
DCHECK(false);
}
@@ -382,10 +379,9 @@
ssize_t cc = netStateBase->WritePacket(pReply, replyLength);
/*
- * We processed this request and sent its reply. Notify other threads waiting for us they can now
- * send events.
+ * We processed this request and sent its reply so we can release the JDWP token.
*/
- EndProcessingRequest();
+ ReleaseJdwpTokenForCommand();
if (cc != static_cast<ssize_t>(replyLength)) {
PLOG(ERROR) << "Failed sending reply to debugger";
diff --git a/runtime/native/java_lang_Runtime.cc b/runtime/native/java_lang_Runtime.cc
index 97b17bf..84b18ab 100644
--- a/runtime/native/java_lang_Runtime.cc
+++ b/runtime/native/java_lang_Runtime.cc
@@ -20,6 +20,7 @@
#include <limits.h>
#include <unistd.h>
+#include "base/macros.h"
#include "gc/heap.h"
#include "handle_scope-inl.h"
#include "jni_internal.h"
@@ -39,7 +40,7 @@
Runtime::Current()->GetHeap()->CollectGarbage(false);
}
-[[noreturn]] static void Runtime_nativeExit(JNIEnv*, jclass, jint status) {
+NO_RETURN static void Runtime_nativeExit(JNIEnv*, jclass, jint status) {
LOG(INFO) << "System.exit called, status: " << status;
Runtime::Current()->CallExitHook(status);
exit(status);
diff --git a/runtime/parsed_options.cc b/runtime/parsed_options.cc
index 99369ca..c0c7baa 100644
--- a/runtime/parsed_options.cc
+++ b/runtime/parsed_options.cc
@@ -241,6 +241,8 @@
.Define("-Xzygote-max-failed-boots=_")
.WithType<unsigned int>()
.IntoKey(M::ZygoteMaxFailedBoots)
+ .Define("-Xno-dex-file-fallback")
+ .IntoKey(M::NoDexFileFallback)
.Ignore({
"-ea", "-da", "-enableassertions", "-disableassertions", "--runtime-arg", "-esa",
"-dsa", "-enablesystemassertions", "-disablesystemassertions", "-Xrs", "-Xint:_",
@@ -631,6 +633,8 @@
UsageMessage(stream, " -X[no]relocate\n");
UsageMessage(stream, " -X[no]dex2oat (Whether to invoke dex2oat on the application)\n");
UsageMessage(stream, " -X[no]image-dex2oat (Whether to create and use a boot image)\n");
+ UsageMessage(stream, " -Xno-dex-file-fallback "
+ "(Don't fall back to dex files without oat files)\n");
UsageMessage(stream, "\n");
UsageMessage(stream, "The following previously supported Dalvik options are ignored:\n");
diff --git a/runtime/quick_exception_handler.cc b/runtime/quick_exception_handler.cc
index 85ec803..7bdf652 100644
--- a/runtime/quick_exception_handler.cc
+++ b/runtime/quick_exception_handler.cc
@@ -353,6 +353,7 @@
context_->SetPC(handler_quick_frame_pc_);
context_->SmashCallerSaves();
context_->DoLongJump();
+ UNREACHABLE();
}
} // namespace art
diff --git a/runtime/quick_exception_handler.h b/runtime/quick_exception_handler.h
index 31622de..162b1dc 100644
--- a/runtime/quick_exception_handler.h
+++ b/runtime/quick_exception_handler.h
@@ -18,6 +18,7 @@
#define ART_RUNTIME_QUICK_EXCEPTION_HANDLER_H_
#include "base/logging.h"
+#include "base/macros.h"
#include "base/mutex.h"
#include "stack.h" // StackReference
@@ -48,7 +49,7 @@
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
void DeoptimizeStack() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
void UpdateInstrumentationStack() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- void DoLongJump() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ NO_RETURN void DoLongJump() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
void SetHandlerQuickFrame(StackReference<mirror::ArtMethod>* handler_quick_frame) {
handler_quick_frame_ = handler_quick_frame;
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index f38f65e..8cd9e24 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -169,6 +169,7 @@
dump_gc_performance_on_shutdown_(false),
preinitialization_transaction_(nullptr),
verify_(false),
+ allow_dex_file_fallback_(true),
target_sdk_version_(0),
implicit_null_checks_(false),
implicit_so_checks_(false),
@@ -190,7 +191,8 @@
}
Thread* self = Thread::Current();
- if (self == nullptr) {
+ const bool attach_shutdown_thread = self == nullptr;
+ if (attach_shutdown_thread) {
CHECK(AttachCurrentThread("Shutdown thread", false, nullptr, false));
self = Thread::Current();
} else {
@@ -212,8 +214,10 @@
self->GetJniEnv()->CallStaticVoidMethod(WellKnownClasses::java_lang_Daemons,
WellKnownClasses::java_lang_Daemons_stop);
}
- DetachCurrentThread();
- self = nullptr;
+ if (attach_shutdown_thread) {
+ DetachCurrentThread();
+ self = nullptr;
+ }
// Shut down background profiler before the runtime exits.
if (profiler_started_) {
@@ -380,7 +384,9 @@
InitLogging(NULL); // Calls Locks::Init() as a side effect.
instance_ = new Runtime;
if (!instance_->Init(options, ignore_unrecognized)) {
- delete instance_;
+ // TODO: Currently deleting the instance will abort the runtime on destruction. Now This will
+ // leak memory, instead. Fix the destructor. b/19100793.
+ // delete instance_;
instance_ = NULL;
return false;
}
@@ -762,6 +768,7 @@
intern_table_ = new InternTable;
verify_ = runtime_options.GetOrDefault(Opt::Verify);
+ allow_dex_file_fallback_ = !runtime_options.Exists(Opt::NoDexFileFallback);
if (runtime_options.GetOrDefault(Opt::Interpret)) {
GetInstrumentation()->ForceInterpretOnly();
@@ -800,6 +807,11 @@
runtime_options.GetOrDefault(Opt::EnableHSpaceCompactForOOM),
runtime_options.GetOrDefault(Opt::HSpaceCompactForOOMMinIntervalsMs));
+ if (heap_->GetImageSpace() == nullptr && !allow_dex_file_fallback_) {
+ LOG(ERROR) << "Dex file fallback disabled, cannot continue without image.";
+ return false;
+ }
+
dump_gc_performance_on_shutdown_ = runtime_options.Exists(Opt::DumpGCPerformanceOnShutdown);
if (runtime_options.Exists(Opt::JdwpOptions)) {
diff --git a/runtime/runtime.h b/runtime/runtime.h
index e7d95fb..944c8bd 100644
--- a/runtime/runtime.h
+++ b/runtime/runtime.h
@@ -28,6 +28,7 @@
#include "arch/instruction_set.h"
#include "base/allocator.h"
+#include "base/macros.h"
#include "compiler_callbacks.h"
#include "gc_root.h"
#include "instrumentation.h"
@@ -185,7 +186,7 @@
// Aborts semi-cleanly. Used in the implementation of LOG(FATAL), which most
// callers should prefer.
- [[noreturn]] static void Abort() LOCKS_EXCLUDED(Locks::abort_lock_);
+ NO_RETURN static void Abort() LOCKS_EXCLUDED(Locks::abort_lock_);
// Returns the "main" ThreadGroup, used when attaching user threads.
jobject GetMainThreadGroup() const;
@@ -504,6 +505,10 @@
return verify_;
}
+ bool IsDexFileFallbackEnabled() const {
+ return allow_dex_file_fallback_;
+ }
+
bool RunningOnValgrind() const {
return running_on_valgrind_;
}
@@ -667,6 +672,10 @@
// If false, verification is disabled. True by default.
bool verify_;
+ // If true, the runtime may use dex files directly with the interpreter if an oat file is not
+ // available/usable.
+ bool allow_dex_file_fallback_;
+
// Specifies target SDK version to allow workarounds for certain API levels.
int32_t target_sdk_version_;
diff --git a/runtime/runtime_options.def b/runtime/runtime_options.def
index 71a0152..d9cc4d4 100644
--- a/runtime/runtime_options.def
+++ b/runtime/runtime_options.def
@@ -115,6 +115,6 @@
// Runtime::Abort.
RUNTIME_OPTIONS_KEY (void (*)(), HookAbort, nullptr)
RUNTIME_OPTIONS_KEY (unsigned int, ZygoteMaxFailedBoots, 1)
-
+RUNTIME_OPTIONS_KEY (Unit, NoDexFileFallback)
#undef RUNTIME_OPTIONS_KEY
diff --git a/runtime/thread.cc b/runtime/thread.cc
index cb6ed64..3b48f49 100644
--- a/runtime/thread.cc
+++ b/runtime/thread.cc
@@ -2044,8 +2044,6 @@
}
exception_handler.UpdateInstrumentationStack();
exception_handler.DoLongJump();
- LOG(FATAL) << "UNREACHABLE";
- UNREACHABLE();
}
Context* Thread::GetLongJumpContext() {
diff --git a/runtime/thread.h b/runtime/thread.h
index 26b7b6f..83cedbb 100644
--- a/runtime/thread.h
+++ b/runtime/thread.h
@@ -353,7 +353,7 @@
}
// Find catch block and perform long jump to appropriate exception handle
- void QuickDeliverException() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ NO_RETURN void QuickDeliverException() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
Context* GetLongJumpContext();
void ReleaseLongJumpContext(Context* context) {
diff --git a/runtime/thread_list.cc b/runtime/thread_list.cc
index 05a0bff..d0f014a 100644
--- a/runtime/thread_list.cc
+++ b/runtime/thread_list.cc
@@ -228,8 +228,7 @@
#if HAVE_TIMED_RWLOCK
// Attempt to rectify locks so that we dump thread list with required locks before exiting.
-static void UnsafeLogFatalForThreadSuspendAllTimeout() __attribute__((noreturn));
-static void UnsafeLogFatalForThreadSuspendAllTimeout() {
+NO_RETURN static void UnsafeLogFatalForThreadSuspendAllTimeout() {
Runtime* runtime = Runtime::Current();
std::ostringstream ss;
ss << "Thread suspend timeout\n";
diff --git a/test/116-nodex2oat/run b/test/116-nodex2oat/run
index 9e5c7dd..2cdb3f7 100755
--- a/test/116-nodex2oat/run
+++ b/test/116-nodex2oat/run
@@ -24,7 +24,7 @@
exit 1
fi
-# Make sure we can run without an oat file,
+# Make sure we can run without an oat file.
echo "Run -Xnodex2oat"
${RUN} ${flags} --runtime-option -Xnodex2oat
diff --git a/test/118-noimage-dex2oat/expected.txt b/test/118-noimage-dex2oat/expected.txt
index bcb695d..0103e89 100644
--- a/test/118-noimage-dex2oat/expected.txt
+++ b/test/118-noimage-dex2oat/expected.txt
@@ -1,6 +1,8 @@
Run -Xnoimage-dex2oat
Has image is false, is image dex2oat enabled is false, is BOOTCLASSPATH on disk is false.
testB18485243 PASS
+Run -Xnoimage-dex2oat -Xno-dex-file-fallback
+Failed to initialize runtime (check log for details)
Run -Ximage-dex2oat
Has image is true, is image dex2oat enabled is true, is BOOTCLASSPATH on disk is true.
testB18485243 PASS
diff --git a/test/118-noimage-dex2oat/run b/test/118-noimage-dex2oat/run
index 2037797..4b1d0ce 100644
--- a/test/118-noimage-dex2oat/run
+++ b/test/118-noimage-dex2oat/run
@@ -46,10 +46,14 @@
bpath_arg="--runtime-option -Xbootclasspath:${bpath}"
-# Make sure we can run without an oat file,
+# Make sure we can run without an oat file.
echo "Run -Xnoimage-dex2oat"
${RUN} ${flags} ${bpath_arg} --runtime-option -Xnoimage-dex2oat --runtime-option -Xnodex2oat
+# Make sure we cannot run without an oat file without fallback.
+echo "Run -Xnoimage-dex2oat -Xno-dex-file-fallback"
+${RUN} ${flags} ${bpath_arg} --runtime-option -Xnoimage-dex2oat --runtime-option -Xnodex2oat --runtime-option -Xno-dex-file-fallback
+
# Make sure we can run with the oat file.
echo "Run -Ximage-dex2oat"
${RUN} ${flags} ${bpath_arg} --runtime-option -Ximage-dex2oat
diff --git a/test/119-noimage-patchoat/check b/test/119-noimage-patchoat/check
new file mode 100755
index 0000000..57111bc
--- /dev/null
+++ b/test/119-noimage-patchoat/check
@@ -0,0 +1,20 @@
+#!/bin/bash
+#
+# Copyright (C) 2014 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.
+
+# Strip the process pids and line numbers from exact error messages.
+sed -e '/^art E.*\] /d' "$2" > "$2.tmp"
+
+diff --strip-trailing-cr -q "$1" "$2.tmp" >/dev/null
diff --git a/test/119-noimage-patchoat/expected.txt b/test/119-noimage-patchoat/expected.txt
index e864268..ed13662 100644
--- a/test/119-noimage-patchoat/expected.txt
+++ b/test/119-noimage-patchoat/expected.txt
@@ -1,5 +1,7 @@
Run -Xnoimage-dex2oat -Xpatchoat:/system/bin/false
Has image is false, is image dex2oat enabled is false.
+Run -Xnoimage-dex2oat -Xpatchoat:/system/bin/false -Xno-dex-file-fallback
+Failed to initialize runtime (check log for details)
Run -Ximage-dex2oat
Has image is true, is image dex2oat enabled is true.
Run default
diff --git a/test/119-noimage-patchoat/run b/test/119-noimage-patchoat/run
index c409cbb..02a64f8 100644
--- a/test/119-noimage-patchoat/run
+++ b/test/119-noimage-patchoat/run
@@ -29,10 +29,14 @@
false_bin="/system/bin/false"
fi
-# Make sure we can run without an image file,
+# Make sure we can run without an image file.
echo "Run -Xnoimage-dex2oat -Xpatchoat:/system/bin/false"
${RUN} ${flags} ${BPATH} --runtime-option -Xnoimage-dex2oat --runtime-option -Xpatchoat:${false_bin}
+# Make sure we cannot run without an image file without fallback.
+echo "Run -Xnoimage-dex2oat -Xpatchoat:/system/bin/false -Xno-dex-file-fallback"
+${RUN} ${flags} ${BPATH} --runtime-option -Xnoimage-dex2oat --runtime-option -Xpatchoat:${false_bin} --runtime-option -Xno-dex-file-fallback
+
# Make sure we can run with the image file.
echo "Run -Ximage-dex2oat"
${RUN} ${flags} ${BPATH} --runtime-option -Ximage-dex2oat
diff --git a/test/134-nodex2oat-nofallback/check b/test/134-nodex2oat-nofallback/check
new file mode 100755
index 0000000..d929c8f
--- /dev/null
+++ b/test/134-nodex2oat-nofallback/check
@@ -0,0 +1,32 @@
+#!/bin/bash
+#
+# Copyright (C) 2014 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.
+
+# The head of the log.
+HEAD=$(head -n 1 "$2")
+EXPECTED_HEAD="Unable to locate class 'Main'"
+
+# Content somewhere inside the output.
+grep 'Suppressed: java.io.IOException: Fallback mode disabled, skipping dex files.' "$2" >/dev/null
+MSG_FOUND=$?
+
+if [[ "$HEAD" != "$EXPECTED_HEAD" || $MSG_FOUND -ne 0 ]] ; then
+ # Print out the log and return with error.
+ cat "$2"
+ exit 1
+fi
+
+# Success.
+exit 0
\ No newline at end of file
diff --git a/test/134-nodex2oat-nofallback/expected.txt b/test/134-nodex2oat-nofallback/expected.txt
new file mode 100644
index 0000000..9b6c846
--- /dev/null
+++ b/test/134-nodex2oat-nofallback/expected.txt
@@ -0,0 +1,64 @@
+# This file isn't used, but gives an example of the output we expect.
+Unable to locate class 'Main'
+java.lang.ClassNotFoundException: Didn't find class "Main" on path: DexPathList[[zip file "/tmp/user/test-12345/134-nodex2oat-nofallback.jar"],nativeLibraryDirectories=[/ssd2/aosp-master3/out/host/linux-x86/lib]]
+ at dalvik.system.BaseDexClassLoader.findClass(BaseDexClassLoader.java:56)
+ at java.lang.ClassLoader.loadClass(ClassLoader.java:511)
+ at java.lang.ClassLoader.loadClass(ClassLoader.java:469)
+ Suppressed: java.io.IOException: Fallback mode disabled, skipping dex files.
+ at dalvik.system.DexFile.openDexFileNative(Native Method)
+ at dalvik.system.DexFile.openDexFile(DexFile.java:295)
+ at dalvik.system.DexFile.<init>(DexFile.java:80)
+ at dalvik.system.DexFile.<init>(DexFile.java:59)
+ at dalvik.system.DexPathList.loadDexFile(DexPathList.java:262)
+ at dalvik.system.DexPathList.makeDexElements(DexPathList.java:231)
+ at dalvik.system.DexPathList.<init>(DexPathList.java:109)
+ at dalvik.system.BaseDexClassLoader.<init>(BaseDexClassLoader.java:48)
+ at dalvik.system.PathClassLoader.<init>(PathClassLoader.java:38)
+ at java.lang.ClassLoader.createSystemClassLoader(ClassLoader.java:128)
+ at java.lang.ClassLoader.access$000(ClassLoader.java:65)
+ at java.lang.ClassLoader$SystemClassLoader.<clinit>(ClassLoader.java:81)
+ at java.lang.ClassLoader.getSystemClassLoader(ClassLoader.java:137)
+ Caused by: java.io.IOException: Failed to open oat file from dex location '/tmp/user/test-12345/134-nodex2oat-nofallback.jar'
+ ... 13 more
+ Caused by: java.io.IOException: Failed to open oat file from /tmp/user/test-12345/x86/134-nodex2oat-nofallback.odex (error Failed to open oat filename for reading: No such file or directory) (no dalvik_cache availible) and relocation failed.
+ ... 13 more
+ Caused by: java.io.IOException
+ ... 13 more
+ Suppressed: java.lang.ClassNotFoundException: Main
+ at java.lang.Class.classForName(Native Method)
+ at java.lang.BootClassLoader.findClass(ClassLoader.java:781)
+ at java.lang.BootClassLoader.loadClass(ClassLoader.java:841)
+ at java.lang.ClassLoader.loadClass(ClassLoader.java:504)
+ ... 1 more
+ Caused by: java.lang.NoClassDefFoundError: Class not found using the boot class loader; no stack trace available
+java.lang.ClassNotFoundException: Didn't find class "Main" on path: DexPathList[[zip file "/tmp/user/test-12345/134-nodex2oat-nofallback.jar"],nativeLibraryDirectories=[/ssd2/aosp-master3/out/host/linux-x86/lib]]
+ at dalvik.system.BaseDexClassLoader.findClass(BaseDexClassLoader.java:56)
+ at java.lang.ClassLoader.loadClass(ClassLoader.java:511)
+ at java.lang.ClassLoader.loadClass(ClassLoader.java:469)
+ Suppressed: java.io.IOException: Fallback mode disabled, skipping dex files.
+ at dalvik.system.DexFile.openDexFileNative(Native Method)
+ at dalvik.system.DexFile.openDexFile(DexFile.java:295)
+ at dalvik.system.DexFile.<init>(DexFile.java:80)
+ at dalvik.system.DexFile.<init>(DexFile.java:59)
+ at dalvik.system.DexPathList.loadDexFile(DexPathList.java:262)
+ at dalvik.system.DexPathList.makeDexElements(DexPathList.java:231)
+ at dalvik.system.DexPathList.<init>(DexPathList.java:109)
+ at dalvik.system.BaseDexClassLoader.<init>(BaseDexClassLoader.java:48)
+ at dalvik.system.PathClassLoader.<init>(PathClassLoader.java:38)
+ at java.lang.ClassLoader.createSystemClassLoader(ClassLoader.java:128)
+ at java.lang.ClassLoader.access$000(ClassLoader.java:65)
+ at java.lang.ClassLoader$SystemClassLoader.<clinit>(ClassLoader.java:81)
+ at java.lang.ClassLoader.getSystemClassLoader(ClassLoader.java:137)
+ Caused by: java.io.IOException: Failed to open oat file from dex location '/tmp/user/test-12345/134-nodex2oat-nofallback.jar'
+ ... 13 more
+ Caused by: java.io.IOException: Failed to open oat file from /tmp/user/test-12345/x86/134-nodex2oat-nofallback.odex (error Failed to open oat filename for reading: No such file or directory) (no dalvik_cache availible) and relocation failed.
+ ... 13 more
+ Caused by: java.io.IOException:
+ ... 13 more
+ Suppressed: java.lang.ClassNotFoundException: Main
+ at java.lang.Class.classForName(Native Method)
+ at java.lang.BootClassLoader.findClass(ClassLoader.java:781)
+ at java.lang.BootClassLoader.loadClass(ClassLoader.java:841)
+ at java.lang.ClassLoader.loadClass(ClassLoader.java:504)
+ ... 1 more
+ Caused by: java.lang.NoClassDefFoundError: Class not found using the boot class loader; no stack trace available
diff --git a/test/134-nodex2oat-nofallback/info.txt b/test/134-nodex2oat-nofallback/info.txt
new file mode 100644
index 0000000..3004729
--- /dev/null
+++ b/test/134-nodex2oat-nofallback/info.txt
@@ -0,0 +1,2 @@
+Test that disables dex2oat'ing the application, and disable fallback. This is derived from test
+116. It needs it separate test as it needs a custom check script.
\ No newline at end of file
diff --git a/test/134-nodex2oat-nofallback/run b/test/134-nodex2oat-nofallback/run
new file mode 100755
index 0000000..38b4adb
--- /dev/null
+++ b/test/134-nodex2oat-nofallback/run
@@ -0,0 +1,20 @@
+#!/bin/bash
+#
+# Copyright (C) 2014 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.
+
+flags="${@}"
+
+# Make sure we cannot run without an oat file without fallback.
+${RUN} ${flags} --runtime-option -Xnodex2oat --runtime-option -Xno-dex-file-fallback
diff --git a/test/134-nodex2oat-nofallback/src/Main.java b/test/134-nodex2oat-nofallback/src/Main.java
new file mode 100644
index 0000000..37ac9d5
--- /dev/null
+++ b/test/134-nodex2oat-nofallback/src/Main.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+
+public class Main {
+ public static void main(String[] args) {
+ System.out.println(
+ "Has oat is " + hasOat() + ", is dex2oat enabled is " + isDex2OatEnabled() + ".");
+
+ if (hasOat() && !isDex2OatEnabled()) {
+ throw new Error("Application with dex2oat disabled runs with an oat file");
+ } else if (!hasOat() && isDex2OatEnabled()) {
+ throw new Error("Application with dex2oat enabled runs without an oat file");
+ }
+ }
+
+ static {
+ System.loadLibrary("arttest");
+ }
+
+ private native static boolean hasOat();
+
+ private native static boolean isDex2OatEnabled();
+}
diff --git a/test/449-checker-bce/src/Main.java b/test/449-checker-bce/src/Main.java
index 5a0e13b..ad4092b 100644
--- a/test/449-checker-bce/src/Main.java
+++ b/test/449-checker-bce/src/Main.java
@@ -46,6 +46,7 @@
return primeCount;
}
+
// CHECK-START: void Main.narrow(int[], int) BCE (before)
// CHECK: BoundsCheck
// CHECK: ArraySet
@@ -61,8 +62,12 @@
// CHECK: ArraySet
// CHECK: BoundsCheck
// CHECK: ArraySet
+ // CHECK-NOT: BoundsCheck
+ // CHECK: ArraySet
+ // CHECK: BoundsCheck
+ // CHECK: ArraySet
- static void narrow(int array[], int offset) {
+ static void narrow(int[] array, int offset) {
if (offset < 0) {
return;
}
@@ -87,10 +92,386 @@
// eliminate this bounds check.
array[biased_offset2] = 1;
}
+
+ // offset_sub1 won't underflow since offset is no less than 0.
+ int offset_sub1 = offset - Integer.MAX_VALUE;
+ if (offset_sub1 >= 0) {
+ array[offset_sub1] = 1; // Bounds check can be eliminated.
+ }
+
+ // offset_sub2 can underflow.
+ int offset_sub2 = offset_sub1 - Integer.MAX_VALUE;
+ if (offset_sub2 >= 0) {
+ array[offset_sub2] = 1; // Bounds check can't be eliminated.
+ }
}
}
+
+ // CHECK-START: void Main.constantIndexing(int[]) BCE (before)
+ // CHECK: BoundsCheck
+ // CHECK: ArraySet
+ // CHECK: BoundsCheck
+ // CHECK: ArraySet
+ // CHECK: BoundsCheck
+ // CHECK: ArraySet
+
+ // CHECK-START: void Main.constantIndexing(int[]) BCE (after)
+ // CHECK: BoundsCheck
+ // CHECK: ArraySet
+ // CHECK-NOT: BoundsCheck
+ // CHECK: ArraySet
+ // CHECK: BoundsCheck
+ // CHECK: ArraySet
+
+ static void constantIndexing(int[] array) {
+ array[5] = 1;
+ array[4] = 1;
+ array[6] = 1;
+ }
+
+
+ // CHECK-START: void Main.loopPattern1(int[]) BCE (before)
+ // CHECK: BoundsCheck
+ // CHECK: ArraySet
+ // CHECK: BoundsCheck
+ // CHECK: ArraySet
+ // CHECK: BoundsCheck
+ // CHECK: ArraySet
+ // CHECK: BoundsCheck
+ // CHECK: ArraySet
+ // CHECK: BoundsCheck
+ // CHECK: ArraySet
+ // CHECK: BoundsCheck
+ // CHECK: ArraySet
+ // CHECK: BoundsCheck
+ // CHECK: ArraySet
+
+ // CHECK-START: void Main.loopPattern1(int[]) BCE (after)
+ // CHECK-NOT: BoundsCheck
+ // CHECK: ArraySet
+ // CHECK-NOT: BoundsCheck
+ // CHECK: ArraySet
+ // CHECK-NOT: BoundsCheck
+ // CHECK: ArraySet
+ // CHECK: BoundsCheck
+ // CHECK: ArraySet
+ // CHECK: BoundsCheck
+ // CHECK: ArraySet
+ // CHECK: BoundsCheck
+ // CHECK: ArraySet
+ // CHECK-NOT: BoundsCheck
+ // CHECK: ArraySet
+
+ static void loopPattern1(int[] array) {
+ for (int i = 0; i < array.length; i++) {
+ array[i] = 1; // Bounds check can be eliminated.
+ }
+
+ for (int i = 1; i < array.length; i++) {
+ array[i] = 1; // Bounds check can be eliminated.
+ }
+
+ for (int i = 1; i < array.length - 1; i++) {
+ array[i] = 1; // Bounds check can be eliminated.
+ }
+
+ for (int i = -1; i < array.length; i++) {
+ array[i] = 1; // Bounds check can't be eliminated.
+ }
+
+ for (int i = 0; i <= array.length; i++) {
+ array[i] = 1; // Bounds check can't be eliminated.
+ }
+
+ for (int i = 0; i < array.length; i += 2) {
+ // We don't have any assumption on max array length yet.
+ // Bounds check can't be eliminated due to overflow concern.
+ array[i] = 1;
+ }
+
+ for (int i = 1; i < array.length; i += 2) {
+ // Bounds check can be eliminated since i is odd so the last
+ // i that's less than array.length is at most (Integer.MAX_VALUE - 2).
+ array[i] = 1;
+ }
+ }
+
+
+ // CHECK-START: void Main.loopPattern2(int[]) BCE (before)
+ // CHECK: BoundsCheck
+ // CHECK: ArraySet
+ // CHECK: BoundsCheck
+ // CHECK: ArraySet
+ // CHECK: BoundsCheck
+ // CHECK: ArraySet
+ // CHECK: BoundsCheck
+ // CHECK: ArraySet
+ // CHECK: BoundsCheck
+ // CHECK: ArraySet
+ // CHECK: BoundsCheck
+ // CHECK: ArraySet
+
+ // CHECK-START: void Main.loopPattern2(int[]) BCE (after)
+ // CHECK-NOT: BoundsCheck
+ // CHECK: ArraySet
+ // CHECK-NOT: BoundsCheck
+ // CHECK: ArraySet
+ // CHECK-NOT: BoundsCheck
+ // CHECK: ArraySet
+ // CHECK: BoundsCheck
+ // CHECK: ArraySet
+ // CHECK: BoundsCheck
+ // CHECK: ArraySet
+ // CHECK-NOT: BoundsCheck
+ // CHECK: ArraySet
+
+ static void loopPattern2(int[] array) {
+ for (int i = array.length - 1; i >= 0; i--) {
+ array[i] = 1; // Bounds check can be eliminated.
+ }
+
+ for (int i = array.length; i > 0; i--) {
+ array[i - 1] = 1; // Bounds check can be eliminated.
+ }
+
+ for (int i = array.length - 1; i > 0; i--) {
+ array[i] = 1; // Bounds check can be eliminated.
+ }
+
+ for (int i = array.length; i >= 0; i--) {
+ array[i] = 1; // Bounds check can't be eliminated.
+ }
+
+ for (int i = array.length; i >= 0; i--) {
+ array[i - 1] = 1; // Bounds check can't be eliminated.
+ }
+
+ for (int i = array.length; i > 0; i -= 20) {
+ // For i >= 0, (i - 20 - 1) is guaranteed not to underflow.
+ array[i - 1] = 1; // Bounds check can be eliminated.
+ }
+ }
+
+
+ // CHECK-START: void Main.loopPattern3(int[]) BCE (before)
+ // CHECK: BoundsCheck
+ // CHECK: ArraySet
+
+ // CHECK-START: void Main.loopPattern3(int[]) BCE (after)
+ // CHECK: BoundsCheck
+ // CHECK: ArraySet
+
+ static void loopPattern3(int[] array) {
+ java.util.Random random = new java.util.Random();
+ for (int i = 0; ; i++) {
+ if (random.nextInt() % 1000 == 0 && i < array.length) {
+ // Can't eliminate the bound check since not every i++ is
+ // matched with a array length check, so there is some chance that i
+ // overflows and is negative.
+ array[i] = 1;
+ }
+ }
+ }
+
+
+ // CHECK-START: void Main.constantNewArray() BCE (before)
+ // CHECK: BoundsCheck
+ // CHECK: ArraySet
+ // CHECK: BoundsCheck
+ // CHECK: ArraySet
+ // CHECK: BoundsCheck
+ // CHECK: ArraySet
+ // CHECK: BoundsCheck
+ // CHECK: ArraySet
+ // CHECK: BoundsCheck
+ // CHECK: ArraySet
+
+ // CHECK-START: void Main.constantNewArray() BCE (after)
+ // CHECK-NOT: BoundsCheck
+ // CHECK: ArraySet
+ // CHECK: BoundsCheck
+ // CHECK: ArraySet
+ // CHECK-NOT: BoundsCheck
+ // CHECK: ArraySet
+ // CHECK-NOT: BoundsCheck
+ // CHECK: ArraySet
+ // CHECK: BoundsCheck
+ // CHECK: ArraySet
+
+ static void constantNewArray() {
+ int[] array = new int[10];
+ for (int i = 0; i < 10; i++) {
+ array[i] = 1; // Bounds check can be eliminated.
+ }
+
+ for (int i = 0; i <= 10; i++) {
+ array[i] = 1; // Bounds check can't be eliminated.
+ }
+
+ array[0] = 1; // Bounds check can be eliminated.
+ array[9] = 1; // Bounds check can be eliminated.
+ array[10] = 1; // Bounds check can't be eliminated.
+ }
+
+ // CHECK-START: void Main.pyramid1(int[]) BCE (before)
+ // CHECK: BoundsCheck
+ // CHECK: ArraySet
+ // CHECK: BoundsCheck
+ // CHECK: ArraySet
+
+ // CHECK-START: void Main.pyramid1(int[]) BCE (after)
+ // CHECK-NOT: BoundsCheck
+ // CHECK: ArraySet
+ // CHECK-NOT: BoundsCheck
+ // CHECK: ArraySet
+
+ // Set array to something like {0, 1, 2, 3, 2, 1, 0}.
+ static void pyramid1(int[] array) {
+ for (int i = 0; i < (array.length + 1) / 2; i++) {
+ array[i] = i;
+ array[array.length - 1 - i] = i;
+ }
+ }
+
+
+ // CHECK-START: void Main.pyramid2(int[]) BCE (before)
+ // CHECK: BoundsCheck
+ // CHECK: ArraySet
+ // CHECK: BoundsCheck
+ // CHECK: ArraySet
+
+ // CHECK-START: void Main.pyramid2(int[]) BCE (after)
+ // CHECK-NOT: BoundsCheck
+ // CHECK: ArraySet
+ // CHECK-NOT: BoundsCheck
+ // CHECK: ArraySet
+
+ // Set array to something like {0, 1, 2, 3, 2, 1, 0}.
+ static void pyramid2(int[] array) {
+ for (int i = 0; i < (array.length + 1) >> 1; i++) {
+ array[i] = i;
+ array[array.length - 1 - i] = i;
+ }
+ }
+
+
+ // CHECK-START: void Main.pyramid3(int[]) BCE (before)
+ // CHECK: BoundsCheck
+ // CHECK: ArraySet
+ // CHECK: BoundsCheck
+ // CHECK: ArraySet
+
+ // CHECK-START: void Main.pyramid3(int[]) BCE (after)
+ // CHECK-NOT: BoundsCheck
+ // CHECK: ArraySet
+ // CHECK-NOT: BoundsCheck
+ // CHECK: ArraySet
+
+ // Set array to something like {0, 1, 2, 3, 2, 1, 0}.
+ static void pyramid3(int[] array) {
+ for (int i = 0; i < (array.length + 1) >>> 1; i++) {
+ array[i] = i;
+ array[array.length - 1 - i] = i;
+ }
+ }
+
+
+ // TODO: bce on the array accesses in this method.
+ static boolean isPyramid(int[] array) {
+ int i = 0;
+ int j = array.length - 1;
+ while (i <= j) {
+ if (array[i] != i) {
+ return false;
+ }
+ if (array[j] != i) {
+ return false;
+ }
+ i++; j--;
+ }
+ return true;
+ }
+
+
+ // CHECK-START: void Main.bubbleSort(int[]) GVN (before)
+ // CHECK: BoundsCheck
+ // CHECK: ArrayGet
+ // CHECK: BoundsCheck
+ // CHECK: ArrayGet
+ // CHECK: BoundsCheck
+ // CHECK: ArrayGet
+ // CHECK: BoundsCheck
+ // CHECK: ArrayGet
+ // CHECK: BoundsCheck
+ // CHECK: ArraySet
+ // CHECK: BoundsCheck
+ // CHECK: ArraySet
+
+ // CHECK-START: void Main.bubbleSort(int[]) GVN (after)
+ // CHECK: BoundsCheck
+ // CHECK: ArrayGet
+ // CHECK: BoundsCheck
+ // CHECK: ArrayGet
+ // CHECK-NOT: ArrayGet
+ // CHECK-NOT: ArrayGet
+ // CHECK-NOT: BoundsCheck
+ // CHECK: ArraySet
+ // CHECK-NOT: BoundsCheck
+ // CHECK: ArraySet
+
+ // CHECK-START: void Main.bubbleSort(int[]) BCE (after)
+ // CHECK-NOT: BoundsCheck
+ // CHECK: ArrayGet
+ // CHECK-NOT: BoundsCheck
+ // CHECK: ArrayGet
+ // CHECK-NOT: ArrayGet
+ // CHECK-NOT: ArrayGet
+ // CHECK-NOT: BoundsCheck
+ // CHECK: ArraySet
+ // CHECK-NOT: BoundsCheck
+ // CHECK: ArraySet
+
+ static void bubbleSort(int[] array) {
+ for (int i = 0; i < array.length - 1; i++) {
+ for (int j = 0; j < array.length - i - 1; j++) {
+ if (array[j] > array[j + 1]) {
+ int temp = array[j + 1];
+ array[j + 1] = array[j];
+ array[j] = temp;
+ }
+ }
+ }
+ }
+
+
public static void main(String[] args) {
sieve(20);
+
+ int[] array = {5, 2, 3, 7, 0, 1, 6, 4};
+ bubbleSort(array);
+ for (int i = 0; i < 8; i++) {
+ if (array[i] != i) {
+ System.out.println("bubble sort failed!");
+ }
+ }
+
+ array = new int[7];
+ pyramid1(array);
+ if (!isPyramid(array)) {
+ System.out.println("pyramid1 failed!");
+ }
+
+ array = new int[8];
+ pyramid2(array);
+ if (!isPyramid(array)) {
+ System.out.println("pyramid2 failed!");
+ }
+
+ java.util.Arrays.fill(array, -1);
+ pyramid3(array);
+ if (!isPyramid(array)) {
+ System.out.println("pyramid3 failed!");
+ }
}
}
diff --git a/test/451-regression-add-float/expected.txt b/test/451-regression-add-float/expected.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/451-regression-add-float/expected.txt
diff --git a/test/451-regression-add-float/info.txt b/test/451-regression-add-float/info.txt
new file mode 100644
index 0000000..83a5f9d
--- /dev/null
+++ b/test/451-regression-add-float/info.txt
@@ -0,0 +1,2 @@
+Tests a regression in float addition for optimizing. The second argument
+could be now be a constant for floating point numbers.
diff --git a/test/451-regression-add-float/src/Main.java b/test/451-regression-add-float/src/Main.java
new file mode 100644
index 0000000..0d4bf06
--- /dev/null
+++ b/test/451-regression-add-float/src/Main.java
@@ -0,0 +1,72 @@
+/*
+* 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.
+*/
+
+
+public class Main {
+
+ public static void main(String[] args) {
+ assertEqual(4, add3(1));
+ assertEqual(4l, add3(1l));
+ assertEqual(4f, add3(1f));
+ assertEqual(4d, add3(1d));
+ }
+
+ public static int add3(int a) {
+ return 1 + a + 2;
+ }
+
+ public static long add3(long a) {
+ return 1l + a + 2l;
+ }
+
+ public static float add3(float a) {
+ return 1f + a + 2f;
+ }
+
+ public static double add3(double a) {
+ return 1d + a + 2d;
+ }
+
+ public static void assertEqual(int a, int b) {
+ if (a != b) {
+ throw new RuntimeException("Expected: " + a + " Found: " + b);
+ }
+ }
+
+ public static void assertEqual(long a, long b) {
+ if (a != b) {
+ throw new RuntimeException("Expected: " + a + " Found: " + b);
+ }
+ }
+
+ public static void assertEqual(float a, float b) {
+ boolean aproxEquals = (a > b)
+ ? ((a - b) < 0.0001f)
+ : ((b - a) < 0.0001f);
+ if (!aproxEquals) {
+ throw new RuntimeException("Expected: " + a + " Found: " + b);
+ }
+ }
+
+ public static void assertEqual(double a, double b) {
+ boolean aproxEquals = (a > b)
+ ? ((a - b) < 0.0001d)
+ : ((b - a) < 0.0001d);
+ if (!aproxEquals) {
+ throw new RuntimeException("Expected: " + a + " Found: " + b);
+ }
+ }
+}
diff --git a/test/451-spill-splot/expected.txt b/test/451-spill-splot/expected.txt
new file mode 100644
index 0000000..efc3f2e
--- /dev/null
+++ b/test/451-spill-splot/expected.txt
@@ -0,0 +1,6 @@
+85.0
+45.0
+20.0
+56.0
+20.0
+20.0
diff --git a/test/451-spill-splot/info.txt b/test/451-spill-splot/info.txt
new file mode 100644
index 0000000..1772ce7
--- /dev/null
+++ b/test/451-spill-splot/info.txt
@@ -0,0 +1,2 @@
+Regression test for the optimizing compiler and the
+way it spills intervals of different types.
diff --git a/test/451-spill-splot/src/Main.java b/test/451-spill-splot/src/Main.java
new file mode 100644
index 0000000..f631ebd
--- /dev/null
+++ b/test/451-spill-splot/src/Main.java
@@ -0,0 +1,96 @@
+/*
+ * 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.
+ */
+
+public class Main {
+ public static void main(String[] args) {
+ // Create a few local variables to make sure some get spilled, and we get
+ // a conflict of swapping a single entry stack slot (float) with a double entry
+ // stack slot (double).
+ double a = 0.0;
+ double b = 1.0;
+ double c = 2.0;
+ double d = 3.0;
+ double e = 4.0;
+ double f = 5.0;
+ double g = 6.0;
+ double h = 7.0;
+ double i = 8.0;
+ double j = 9.0;
+
+ float aa = 0;
+ float bb = 1;
+ float cc = 2;
+ float dd = 3;
+ float ee = 4;
+ float ff = 5;
+ float gg = 6;
+ float hh = 7;
+ float ii = 8;
+ float jj = 9;
+ float kk = 10;
+ float ll = 10;
+ float mm = 10;
+ float nn = 10;
+
+ for (int count = 0; count < 2; count++) {
+ System.out.println(aa + bb + cc + dd + ee + ff + gg + hh + ii + jj + kk + ll + mm + nn);
+ System.out.println(a + b + c + d + e + f + g + h + i + j);
+ a = computeDouble();
+ b = computeDouble();
+ c = computeDouble();
+ d = computeDouble();
+ e = computeDouble();
+ f = computeDouble();
+ g = computeDouble();
+ h = computeDouble();
+ i = computeDouble();
+ j = computeDouble();
+ System.out.println(a + b + c + d + e + f + g + h + i + j);
+ aa = computeFloat();
+ bb = computeFloat();
+ cc = computeFloat();
+ dd = computeFloat();
+ ee = computeFloat();
+ ff = computeFloat();
+ gg = computeFloat();
+ hh = computeFloat();
+ ii = computeFloat();
+ jj = computeFloat();
+ kk = computeFloat();
+ ll = computeFloat();
+ mm = computeFloat();
+ nn = computeFloat();
+ }
+ }
+
+ static boolean doThrow = false;
+
+ public static double computeDouble() {
+ if (doThrow) {
+ // Try defeating inlining.
+ throw new Error();
+ }
+ return 2.0;
+ }
+
+ public static float computeFloat() {
+ if (doThrow) {
+ // Try defeating inlining.
+ throw new Error();
+ }
+ return 4.0f;
+ }
+}
diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk
index c8e0ec5..8e4b46b 100644
--- a/test/Android.run-test.mk
+++ b/test/Android.run-test.mk
@@ -181,7 +181,8 @@
# Note 116-nodex2oat is not broken per-se it just doesn't (and isn't meant to) work with --prebuild.
TEST_ART_BROKEN_PREBUILD_RUN_TESTS := \
116-nodex2oat \
- 118-noimage-dex2oat
+ 118-noimage-dex2oat \
+ 134-nodex2oat-nofallback
ifneq (,$(filter prebuild,$(PREBUILD_TYPES)))
ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES),prebuild, \