Merge "Avoid unaligned accesses (SIGBUG/BUS_ADRALN) in IRT."
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/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/gc/space/image_space.cc b/runtime/gc/space/image_space.cc
index 261d3c2..d873e6d 100644
--- a/runtime/gc/space/image_space.cc
+++ b/runtime/gc/space/image_space.cc
@@ -19,6 +19,7 @@
#include <dirent.h>
#include <sys/statvfs.h>
#include <sys/types.h>
+#include <unistd.h>
#include <random>
@@ -122,21 +123,50 @@
// every zygote boot and delete it when the boot completes. If we find a file already
// present, it usually means the boot didn't complete. We wipe the entire dalvik
// cache if that's the case.
-static void MarkZygoteStart(const InstructionSet isa) {
+static void MarkZygoteStart(const InstructionSet isa, const uint32_t max_failed_boots) {
const std::string isa_subdir = GetDalvikCacheOrDie(GetInstructionSetString(isa), false);
const std::string boot_marker = isa_subdir + "/.booting";
+ const char* file_name = boot_marker.c_str();
- if (OS::FileExists(boot_marker.c_str())) {
+ uint32_t num_failed_boots = 0;
+ std::unique_ptr<File> file(OS::OpenFileReadWrite(file_name));
+ if (file.get() == nullptr) {
+ file.reset(OS::CreateEmptyFile(file_name));
+
+ if (file.get() == nullptr) {
+ PLOG(WARNING) << "Failed to create boot marker.";
+ return;
+ }
+ } else {
+ if (!file->ReadFully(&num_failed_boots, sizeof(num_failed_boots))) {
+ PLOG(WARNING) << "Failed to read boot marker.";
+ file->Erase();
+ return;
+ }
+ }
+
+ if (max_failed_boots != 0 && num_failed_boots > max_failed_boots) {
LOG(WARNING) << "Incomplete boot detected. Pruning dalvik cache";
RealPruneDalvikCache(isa_subdir);
}
- VLOG(startup) << "Creating boot start marker: " << boot_marker;
- std::unique_ptr<File> f(OS::CreateEmptyFile(boot_marker.c_str()));
- if (f.get() != nullptr) {
- if (f->FlushCloseOrErase() != 0) {
- PLOG(WARNING) << "Failed to write boot marker.";
- }
+ ++num_failed_boots;
+ VLOG(startup) << "Number of failed boots on : " << boot_marker << " = " << num_failed_boots;
+
+ if (lseek(file->Fd(), 0, SEEK_SET) == -1) {
+ PLOG(WARNING) << "Failed to write boot marker.";
+ file->Erase();
+ return;
+ }
+
+ if (!file->WriteFully(&num_failed_boots, sizeof(num_failed_boots))) {
+ PLOG(WARNING) << "Failed to write boot marker.";
+ file->Erase();
+ return;
+ }
+
+ if (file->FlushCloseOrErase() != 0) {
+ PLOG(WARNING) << "Failed to flush boot marker.";
}
}
@@ -450,7 +480,7 @@
&has_cache, &is_global_cache);
if (Runtime::Current()->IsZygote()) {
- MarkZygoteStart(image_isa);
+ MarkZygoteStart(image_isa, Runtime::Current()->GetZygoteMaxFailedBoots());
}
ImageSpace* space;
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/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 daa4373..99369ca 100644
--- a/runtime/parsed_options.cc
+++ b/runtime/parsed_options.cc
@@ -238,6 +238,9 @@
.Define("-XX:NativeBridge=_")
.WithType<std::string>()
.IntoKey(M::NativeBridge)
+ .Define("-Xzygote-max-failed-boots=_")
+ .WithType<unsigned int>()
+ .IntoKey(M::ZygoteMaxFailedBoots)
.Ignore({
"-ea", "-da", "-enableassertions", "-disableassertions", "--runtime-arg", "-esa",
"-dsa", "-enablesystemassertions", "-disablesystemassertions", "-Xrs", "-Xint:_",
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 0ccc97c..f38f65e 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -173,7 +173,8 @@
implicit_null_checks_(false),
implicit_so_checks_(false),
implicit_suspend_checks_(false),
- is_native_bridge_loaded_(false) {
+ is_native_bridge_loaded_(false),
+ zygote_max_failed_boots_(0) {
CheckAsmSupportOffsetsAndSizes();
}
@@ -766,6 +767,8 @@
GetInstrumentation()->ForceInterpretOnly();
}
+ zygote_max_failed_boots_ = runtime_options.GetOrDefault(Opt::ZygoteMaxFailedBoots);
+
XGcOption xgc_option = runtime_options.GetOrDefault(Opt::GcOption);
heap_ = new gc::Heap(runtime_options.GetOrDefault(Opt::MemoryInitialSize),
runtime_options.GetOrDefault(Opt::HeapGrowthLimit),
diff --git a/runtime/runtime.h b/runtime/runtime.h
index 118c838..fb9ca40 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;
@@ -516,6 +517,10 @@
return target_sdk_version_;
}
+ uint32_t GetZygoteMaxFailedBoots() const {
+ return zygote_max_failed_boots_;
+ }
+
private:
static void InitPlatformSignalHandlers();
@@ -682,6 +687,11 @@
// that there's no native bridge.
bool is_native_bridge_loaded_;
+ // The maximum number of failed boots we allow before pruning the dalvik cache
+ // and trying again. This option is only inspected when we're running as a
+ // zygote.
+ uint32_t zygote_max_failed_boots_;
+
DISALLOW_COPY_AND_ASSIGN(Runtime);
};
std::ostream& operator<<(std::ostream& os, const Runtime::CalleeSaveType& rhs);
diff --git a/runtime/runtime_options.def b/runtime/runtime_options.def
index 1bdc980..71a0152 100644
--- a/runtime/runtime_options.def
+++ b/runtime/runtime_options.def
@@ -114,6 +114,7 @@
// We don't call abort(3) by default; see
// Runtime::Abort.
RUNTIME_OPTIONS_KEY (void (*)(), HookAbort, nullptr)
+RUNTIME_OPTIONS_KEY (unsigned int, ZygoteMaxFailedBoots, 1)
#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/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;
+ }
+}