summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid Brazdil <dbrazdil@google.com>2015-03-24 17:31:29 +0000
committerGerrit Code Review <noreply-gerritcodereview@google.com>2015-03-24 17:31:31 +0000
commitb64b782f9ae7a94ecbbf64c83cbcdc7d716ba560 (patch)
treedf3aa814ff7762d681c50781c413fd510440ae61
parent2c2d00e8ca841aa2f57fa2f852e896378ef67144 (diff)
parent46e2a3915aa68c77426b71e95b9f3658250646b7 (diff)
downloadandroid_art-b64b782f9ae7a94ecbbf64c83cbcdc7d716ba560.tar.gz
android_art-b64b782f9ae7a94ecbbf64c83cbcdc7d716ba560.tar.bz2
android_art-b64b782f9ae7a94ecbbf64c83cbcdc7d716ba560.zip
Merge "ART: Boolean simplifier"
-rw-r--r--compiler/Android.mk1
-rw-r--r--compiler/optimizing/boolean_simplifier.cc140
-rw-r--r--compiler/optimizing/boolean_simplifier.h74
-rw-r--r--compiler/optimizing/builder.cc26
-rw-r--r--compiler/optimizing/builder.h9
-rw-r--r--compiler/optimizing/code_generator.cc16
-rw-r--r--compiler/optimizing/code_generator_arm.cc29
-rw-r--r--compiler/optimizing/code_generator_arm64.cc2
-rw-r--r--compiler/optimizing/code_generator_x86.cc29
-rw-r--r--compiler/optimizing/code_generator_x86_64.cc30
-rw-r--r--compiler/optimizing/graph_checker.cc4
-rw-r--r--compiler/optimizing/nodes.cc134
-rw-r--r--compiler/optimizing/nodes.h24
-rw-r--r--compiler/optimizing/optimizing_compiler.cc5
-rw-r--r--compiler/optimizing/ssa_liveness_analysis.cc4
-rw-r--r--runtime/primitive.h3
-rw-r--r--test/463-checker-boolean-simplifier/expected.txt0
-rw-r--r--test/463-checker-boolean-simplifier/info.txt1
-rw-r--r--test/463-checker-boolean-simplifier/src/Main.java174
19 files changed, 642 insertions, 63 deletions
diff --git a/compiler/Android.mk b/compiler/Android.mk
index 090675356f..6b0e6ff121 100644
--- a/compiler/Android.mk
+++ b/compiler/Android.mk
@@ -94,6 +94,7 @@ LIBART_COMPILER_SRC_FILES := \
jni/quick/x86_64/calling_convention_x86_64.cc \
jni/quick/calling_convention.cc \
jni/quick/jni_compiler.cc \
+ optimizing/boolean_simplifier.cc \
optimizing/builder.cc \
optimizing/bounds_check_elimination.cc \
optimizing/code_generator.cc \
diff --git a/compiler/optimizing/boolean_simplifier.cc b/compiler/optimizing/boolean_simplifier.cc
new file mode 100644
index 0000000000..ecf9fa28a7
--- /dev/null
+++ b/compiler/optimizing/boolean_simplifier.cc
@@ -0,0 +1,140 @@
+/*
+ * 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.
+ */
+
+#include "boolean_simplifier.h"
+
+namespace art {
+
+static bool EndsWithAnIf(HBasicBlock* block) {
+ return block->GetLastInstruction()->IsIf();
+}
+
+static bool HasSinglePhi(HBasicBlock* block) {
+ return !block->GetPhis().IsEmpty()
+ && block->GetFirstPhi()->GetNext() == nullptr;
+}
+
+// Returns true if 'block1' and 'block2' are empty, merge into the same single
+// successor and the successor can only be reached from them.
+static bool BlocksDoMergeTogether(HBasicBlock* block1, HBasicBlock* block2) {
+ if (!block1->IsSingleGoto() || !block2->IsSingleGoto()) return false;
+ HBasicBlock* succ1 = block1->GetSuccessors().Get(0);
+ HBasicBlock* succ2 = block2->GetSuccessors().Get(0);
+ return succ1 == succ2 && succ1->GetPredecessors().Size() == 2u;
+}
+
+// Returns true if the outcome of the branching matches the boolean value of
+// the branching condition.
+static bool PreservesCondition(HInstruction* input_true, HInstruction* input_false) {
+ return input_true->IsIntConstant() && input_true->AsIntConstant()->GetValue() == 1
+ && input_false->IsIntConstant() && input_false->AsIntConstant()->GetValue() == 0;
+}
+
+// Returns true if the outcome of the branching is exactly opposite of the
+// boolean value of the branching condition.
+static bool NegatesCondition(HInstruction* input_true, HInstruction* input_false) {
+ return input_true->IsIntConstant() && input_true->AsIntConstant()->GetValue() == 0
+ && input_false->IsIntConstant() && input_false->AsIntConstant()->GetValue() == 1;
+}
+
+// Returns an instruction with the opposite boolean value from 'cond'.
+static HInstruction* GetOppositeCondition(HInstruction* cond) {
+ HGraph* graph = cond->GetBlock()->GetGraph();
+ ArenaAllocator* allocator = graph->GetArena();
+
+ if (cond->IsCondition()) {
+ HInstruction* lhs = cond->InputAt(0);
+ HInstruction* rhs = cond->InputAt(1);
+ if (cond->IsEqual()) {
+ return new (allocator) HNotEqual(lhs, rhs);
+ } else if (cond->IsNotEqual()) {
+ return new (allocator) HEqual(lhs, rhs);
+ } else if (cond->IsLessThan()) {
+ return new (allocator) HGreaterThanOrEqual(lhs, rhs);
+ } else if (cond->IsLessThanOrEqual()) {
+ return new (allocator) HGreaterThan(lhs, rhs);
+ } else if (cond->IsGreaterThan()) {
+ return new (allocator) HLessThanOrEqual(lhs, rhs);
+ } else if (cond->IsGreaterThanOrEqual()) {
+ return new (allocator) HLessThan(lhs, rhs);
+ }
+ } else if (cond->IsIntConstant()) {
+ int32_t value = cond->AsIntConstant()->GetValue();
+ if (value == 0) {
+ return graph->GetIntConstant1();
+ } else {
+ DCHECK_EQ(value, 1);
+ return graph->GetIntConstant0();
+ }
+ }
+
+ LOG(FATAL) << "Instruction " << cond->DebugName() << " used as a condition";
+ UNREACHABLE();
+}
+
+void HBooleanSimplifier::Run() {
+ // Iterate in post order in the unlikely case that removing one occurrence of
+ // the pattern empties a branch block of another occurrence. Otherwise the
+ // order does not matter.
+ for (HPostOrderIterator it(*graph_); !it.Done(); it.Advance()) {
+ HBasicBlock* block = it.Current();
+ if (!EndsWithAnIf(block)) continue;
+
+ // Find elements of the pattern.
+ HIf* if_instruction = block->GetLastInstruction()->AsIf();
+ HBasicBlock* true_block = if_instruction->IfTrueSuccessor();
+ HBasicBlock* false_block = if_instruction->IfFalseSuccessor();
+ if (!BlocksDoMergeTogether(true_block, false_block)) {
+ continue;
+ }
+ HBasicBlock* merge_block = true_block->GetSuccessors().Get(0);
+ if (!HasSinglePhi(merge_block)) {
+ continue;
+ }
+ HPhi* phi = merge_block->GetFirstPhi()->AsPhi();
+ HInstruction* true_value = phi->InputAt(merge_block->GetPredecessorIndexOf(true_block));
+ HInstruction* false_value = phi->InputAt(merge_block->GetPredecessorIndexOf(false_block));
+
+ // Check if the selection negates/preserves the value of the condition and
+ // if so, generate a suitable replacement instruction.
+ HInstruction* if_condition = if_instruction->InputAt(0);
+ HInstruction* replacement;
+ if (NegatesCondition(true_value, false_value)) {
+ replacement = GetOppositeCondition(if_condition);
+ if (replacement->GetBlock() == nullptr) {
+ block->InsertInstructionBefore(replacement, if_instruction);
+ }
+ } else if (PreservesCondition(true_value, false_value)) {
+ replacement = if_condition;
+ } else {
+ continue;
+ }
+
+ // Replace the selection outcome with the new instruction.
+ phi->ReplaceWith(replacement);
+ merge_block->RemovePhi(phi);
+
+ // Link the start/end blocks and remove empty branches.
+ graph_->MergeEmptyBranches(block, merge_block);
+
+ // Remove the original condition if it is now unused.
+ if (!if_condition->HasUses()) {
+ if_condition->GetBlock()->RemoveInstruction(if_condition);
+ }
+ }
+}
+
+} // namespace art
diff --git a/compiler/optimizing/boolean_simplifier.h b/compiler/optimizing/boolean_simplifier.h
new file mode 100644
index 0000000000..9fa9c5acdb
--- /dev/null
+++ b/compiler/optimizing/boolean_simplifier.h
@@ -0,0 +1,74 @@
+/*
+ * 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.
+ */
+
+// This optimization recognizes a common pattern where a boolean value is
+// either casted to an integer or negated by selecting from zero/one integer
+// constants with an If statement. Because boolean values are internally
+// represented as zero/one, we can safely replace the pattern with a suitable
+// condition instruction.
+
+// Example: Negating a boolean value
+// B1:
+// z1 ParameterValue
+// i2 IntConstant 0
+// i3 IntConstant 1
+// v4 Goto B2
+// B2:
+// z5 NotEquals [ z1 i2 ]
+// v6 If [ z5 ] then B3 else B4
+// B3:
+// v7 Goto B5
+// B4:
+// v8 Goto B5
+// B5:
+// i9 Phi [ i3 i2 ]
+// v10 Return [ i9 ]
+// turns into
+// B1:
+// z1 ParameterValue
+// i2 IntConstant 0
+// v4 Goto B2
+// B2:
+// z11 Equals [ z1 i2 ]
+// v10 Return [ z11 ]
+// B3, B4, B5: removed
+
+// Note: in order to recognize empty blocks, this optimization must be run
+// after the instruction simplifier has removed redundant suspend checks.
+
+#ifndef ART_COMPILER_OPTIMIZING_BOOLEAN_SIMPLIFIER_H_
+#define ART_COMPILER_OPTIMIZING_BOOLEAN_SIMPLIFIER_H_
+
+#include "optimization.h"
+
+namespace art {
+
+class HBooleanSimplifier : public HOptimization {
+ public:
+ explicit HBooleanSimplifier(HGraph* graph)
+ : HOptimization(graph, true, kBooleanSimplifierPassName) {}
+
+ void Run() OVERRIDE;
+
+ static constexpr const char* kBooleanSimplifierPassName = "boolean_simplifier";
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(HBooleanSimplifier);
+};
+
+} // namespace art
+
+#endif // ART_COMPILER_OPTIMIZING_BOOLEAN_SIMPLIFIER_H_
diff --git a/compiler/optimizing/builder.cc b/compiler/optimizing/builder.cc
index ec7fd62975..a21c311d90 100644
--- a/compiler/optimizing/builder.cc
+++ b/compiler/optimizing/builder.cc
@@ -2058,31 +2058,13 @@ bool HGraphBuilder::AnalyzeDexInstruction(const Instruction& instruction, uint32
return true;
} // NOLINT(readability/fn_size)
-HIntConstant* HGraphBuilder::GetIntConstant0() {
- if (constant0_ != nullptr) {
- return constant0_;
- }
- constant0_ = new(arena_) HIntConstant(0);
- entry_block_->AddInstruction(constant0_);
- return constant0_;
-}
-
-HIntConstant* HGraphBuilder::GetIntConstant1() {
- if (constant1_ != nullptr) {
- return constant1_;
- }
- constant1_ = new(arena_) HIntConstant(1);
- entry_block_->AddInstruction(constant1_);
- return constant1_;
-}
-
HIntConstant* HGraphBuilder::GetIntConstant(int32_t constant) {
switch (constant) {
- case 0: return GetIntConstant0();
- case 1: return GetIntConstant1();
+ case 0: return graph_->GetIntConstant0();
+ case 1: return graph_->GetIntConstant1();
default: {
HIntConstant* instruction = new (arena_) HIntConstant(constant);
- entry_block_->AddInstruction(instruction);
+ graph_->AddConstant(instruction);
return instruction;
}
}
@@ -2090,7 +2072,7 @@ HIntConstant* HGraphBuilder::GetIntConstant(int32_t constant) {
HLongConstant* HGraphBuilder::GetLongConstant(int64_t constant) {
HLongConstant* instruction = new (arena_) HLongConstant(constant);
- entry_block_->AddInstruction(instruction);
+ graph_->AddConstant(instruction);
return instruction;
}
diff --git a/compiler/optimizing/builder.h b/compiler/optimizing/builder.h
index 96196de588..c70170bb46 100644
--- a/compiler/optimizing/builder.h
+++ b/compiler/optimizing/builder.h
@@ -47,8 +47,6 @@ class HGraphBuilder : public ValueObject {
exit_block_(nullptr),
current_block_(nullptr),
graph_(graph),
- constant0_(nullptr),
- constant1_(nullptr),
dex_file_(dex_file),
dex_compilation_unit_(dex_compilation_unit),
compiler_driver_(driver),
@@ -67,8 +65,6 @@ class HGraphBuilder : public ValueObject {
exit_block_(nullptr),
current_block_(nullptr),
graph_(graph),
- constant0_(nullptr),
- constant1_(nullptr),
dex_file_(nullptr),
dex_compilation_unit_(nullptr),
compiler_driver_(nullptr),
@@ -100,8 +96,6 @@ class HGraphBuilder : public ValueObject {
void MaybeUpdateCurrentBlock(size_t index);
HBasicBlock* FindBlockStartingAt(int32_t index) const;
- HIntConstant* GetIntConstant0();
- HIntConstant* GetIntConstant1();
HIntConstant* GetIntConstant(int32_t constant);
HLongConstant* GetLongConstant(int64_t constant);
void InitializeLocals(uint16_t count);
@@ -253,9 +247,6 @@ class HGraphBuilder : public ValueObject {
HBasicBlock* current_block_;
HGraph* const graph_;
- HIntConstant* constant0_;
- HIntConstant* constant1_;
-
// The dex file where the method being compiled is.
const DexFile* const dex_file_;
diff --git a/compiler/optimizing/code_generator.cc b/compiler/optimizing/code_generator.cc
index 787a170d0d..bd6e943bf0 100644
--- a/compiler/optimizing/code_generator.cc
+++ b/compiler/optimizing/code_generator.cc
@@ -40,16 +40,6 @@ size_t CodeGenerator::GetCacheOffset(uint32_t index) {
return mirror::ObjectArray<mirror::Object>::OffsetOfElement(index).SizeValue();
}
-static bool IsSingleGoto(HBasicBlock* block) {
- HLoopInformation* loop_info = block->GetLoopInformation();
- // TODO: Remove the null check b/19084197.
- return (block->GetFirstInstruction() != nullptr)
- && (block->GetFirstInstruction() == block->GetLastInstruction())
- && block->GetLastInstruction()->IsGoto()
- // Back edges generate the suspend check.
- && (loop_info == nullptr || !loop_info->IsBackEdge(block));
-}
-
void CodeGenerator::CompileBaseline(CodeAllocator* allocator, bool is_leaf) {
Initialize();
if (!is_leaf) {
@@ -74,7 +64,7 @@ bool CodeGenerator::GoesToNextBlock(HBasicBlock* current, HBasicBlock* next) con
HBasicBlock* CodeGenerator::GetNextBlockToEmit() const {
for (size_t i = current_block_index_ + 1; i < block_order_->Size(); ++i) {
HBasicBlock* block = block_order_->Get(i);
- if (!IsSingleGoto(block)) {
+ if (!block->IsSingleGoto()) {
return block;
}
}
@@ -82,7 +72,7 @@ HBasicBlock* CodeGenerator::GetNextBlockToEmit() const {
}
HBasicBlock* CodeGenerator::FirstNonEmptyBlock(HBasicBlock* block) const {
- while (IsSingleGoto(block)) {
+ while (block->IsSingleGoto()) {
block = block->GetSuccessors().Get(0);
}
return block;
@@ -97,7 +87,7 @@ void CodeGenerator::CompileInternal(CodeAllocator* allocator, bool is_baseline)
// Don't generate code for an empty block. Its predecessors will branch to its successor
// directly. Also, the label of that block will not be emitted, so this helps catch
// errors where we reference that label.
- if (IsSingleGoto(block)) continue;
+ if (block->IsSingleGoto()) continue;
Bind(block);
for (HInstructionIterator it(block->GetInstructions()); !it.Done(); it.Advance()) {
HInstruction* current = it.Current();
diff --git a/compiler/optimizing/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc
index 0a069a75ef..d783903349 100644
--- a/compiler/optimizing/code_generator_arm.cc
+++ b/compiler/optimizing/code_generator_arm.cc
@@ -883,7 +883,7 @@ void InstructionCodeGeneratorARM::VisitGoto(HGoto* got) {
HInstruction* previous = got->GetPrevious();
HLoopInformation* info = block->GetLoopInformation();
- if (info != nullptr && info->IsBackEdge(block) && info->HasSuspendCheck()) {
+ if (info != nullptr && info->IsBackEdge(*block) && info->HasSuspendCheck()) {
codegen_->ClearSpillSlotsFromLoopPhisInStackMap(info->GetSuspendCheck());
GenerateSuspendCheck(info->GetSuspendCheck(), successor);
return;
@@ -1388,9 +1388,14 @@ void LocationsBuilderARM::VisitTypeConversion(HTypeConversion* conversion) {
LocationSummary* locations =
new (GetGraph()->GetArena()) LocationSummary(conversion, call_kind);
+ // Java language does not allow treating boolean as an integral type but our
+ // bit representation makes it safe.
+
switch (result_type) {
case Primitive::kPrimByte:
switch (input_type) {
+ case Primitive::kPrimBoolean:
+ // Boolean input is a result of code transformations.
case Primitive::kPrimShort:
case Primitive::kPrimInt:
case Primitive::kPrimChar:
@@ -1407,6 +1412,8 @@ void LocationsBuilderARM::VisitTypeConversion(HTypeConversion* conversion) {
case Primitive::kPrimShort:
switch (input_type) {
+ case Primitive::kPrimBoolean:
+ // Boolean input is a result of code transformations.
case Primitive::kPrimByte:
case Primitive::kPrimInt:
case Primitive::kPrimChar:
@@ -1451,6 +1458,8 @@ void LocationsBuilderARM::VisitTypeConversion(HTypeConversion* conversion) {
case Primitive::kPrimLong:
switch (input_type) {
+ case Primitive::kPrimBoolean:
+ // Boolean input is a result of code transformations.
case Primitive::kPrimByte:
case Primitive::kPrimShort:
case Primitive::kPrimInt:
@@ -1487,6 +1496,8 @@ void LocationsBuilderARM::VisitTypeConversion(HTypeConversion* conversion) {
case Primitive::kPrimChar:
switch (input_type) {
+ case Primitive::kPrimBoolean:
+ // Boolean input is a result of code transformations.
case Primitive::kPrimByte:
case Primitive::kPrimShort:
case Primitive::kPrimInt:
@@ -1503,6 +1514,8 @@ void LocationsBuilderARM::VisitTypeConversion(HTypeConversion* conversion) {
case Primitive::kPrimFloat:
switch (input_type) {
+ case Primitive::kPrimBoolean:
+ // Boolean input is a result of code transformations.
case Primitive::kPrimByte:
case Primitive::kPrimShort:
case Primitive::kPrimInt:
@@ -1536,6 +1549,8 @@ void LocationsBuilderARM::VisitTypeConversion(HTypeConversion* conversion) {
case Primitive::kPrimDouble:
switch (input_type) {
+ case Primitive::kPrimBoolean:
+ // Boolean input is a result of code transformations.
case Primitive::kPrimByte:
case Primitive::kPrimShort:
case Primitive::kPrimInt:
@@ -1582,6 +1597,8 @@ void InstructionCodeGeneratorARM::VisitTypeConversion(HTypeConversion* conversio
switch (result_type) {
case Primitive::kPrimByte:
switch (input_type) {
+ case Primitive::kPrimBoolean:
+ // Boolean input is a result of code transformations.
case Primitive::kPrimShort:
case Primitive::kPrimInt:
case Primitive::kPrimChar:
@@ -1597,6 +1614,8 @@ void InstructionCodeGeneratorARM::VisitTypeConversion(HTypeConversion* conversio
case Primitive::kPrimShort:
switch (input_type) {
+ case Primitive::kPrimBoolean:
+ // Boolean input is a result of code transformations.
case Primitive::kPrimByte:
case Primitive::kPrimInt:
case Primitive::kPrimChar:
@@ -1654,6 +1673,8 @@ void InstructionCodeGeneratorARM::VisitTypeConversion(HTypeConversion* conversio
case Primitive::kPrimLong:
switch (input_type) {
+ case Primitive::kPrimBoolean:
+ // Boolean input is a result of code transformations.
case Primitive::kPrimByte:
case Primitive::kPrimShort:
case Primitive::kPrimInt:
@@ -1692,6 +1713,8 @@ void InstructionCodeGeneratorARM::VisitTypeConversion(HTypeConversion* conversio
case Primitive::kPrimChar:
switch (input_type) {
+ case Primitive::kPrimBoolean:
+ // Boolean input is a result of code transformations.
case Primitive::kPrimByte:
case Primitive::kPrimShort:
case Primitive::kPrimInt:
@@ -1707,6 +1730,8 @@ void InstructionCodeGeneratorARM::VisitTypeConversion(HTypeConversion* conversio
case Primitive::kPrimFloat:
switch (input_type) {
+ case Primitive::kPrimBoolean:
+ // Boolean input is a result of code transformations.
case Primitive::kPrimByte:
case Primitive::kPrimShort:
case Primitive::kPrimInt:
@@ -1773,6 +1798,8 @@ void InstructionCodeGeneratorARM::VisitTypeConversion(HTypeConversion* conversio
case Primitive::kPrimDouble:
switch (input_type) {
+ case Primitive::kPrimBoolean:
+ // Boolean input is a result of code transformations.
case Primitive::kPrimByte:
case Primitive::kPrimShort:
case Primitive::kPrimInt:
diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc
index 99283a0056..9455a918d4 100644
--- a/compiler/optimizing/code_generator_arm64.cc
+++ b/compiler/optimizing/code_generator_arm64.cc
@@ -1621,7 +1621,7 @@ void InstructionCodeGeneratorARM64::VisitGoto(HGoto* got) {
HInstruction* previous = got->GetPrevious();
HLoopInformation* info = block->GetLoopInformation();
- if (info != nullptr && info->IsBackEdge(block) && info->HasSuspendCheck()) {
+ if (info != nullptr && info->IsBackEdge(*block) && info->HasSuspendCheck()) {
codegen_->ClearSpillSlotsFromLoopPhisInStackMap(info->GetSuspendCheck());
GenerateSuspendCheck(info->GetSuspendCheck(), successor);
return;
diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc
index e808a122ea..0a7d3fece3 100644
--- a/compiler/optimizing/code_generator_x86.cc
+++ b/compiler/optimizing/code_generator_x86.cc
@@ -792,7 +792,7 @@ void InstructionCodeGeneratorX86::VisitGoto(HGoto* got) {
HInstruction* previous = got->GetPrevious();
HLoopInformation* info = block->GetLoopInformation();
- if (info != nullptr && info->IsBackEdge(block) && info->HasSuspendCheck()) {
+ if (info != nullptr && info->IsBackEdge(*block) && info->HasSuspendCheck()) {
codegen_->ClearSpillSlotsFromLoopPhisInStackMap(info->GetSuspendCheck());
GenerateSuspendCheck(info->GetSuspendCheck(), successor);
return;
@@ -1370,9 +1370,14 @@ void LocationsBuilderX86::VisitTypeConversion(HTypeConversion* conversion) {
LocationSummary* locations =
new (GetGraph()->GetArena()) LocationSummary(conversion, call_kind);
+ // Java language does not allow treating boolean as an integral type but our
+ // bit representation makes it safe.
+
switch (result_type) {
case Primitive::kPrimByte:
switch (input_type) {
+ case Primitive::kPrimBoolean:
+ // Boolean input is a result of code transformations.
case Primitive::kPrimShort:
case Primitive::kPrimInt:
case Primitive::kPrimChar:
@@ -1391,6 +1396,8 @@ void LocationsBuilderX86::VisitTypeConversion(HTypeConversion* conversion) {
case Primitive::kPrimShort:
switch (input_type) {
+ case Primitive::kPrimBoolean:
+ // Boolean input is a result of code transformations.
case Primitive::kPrimByte:
case Primitive::kPrimInt:
case Primitive::kPrimChar:
@@ -1435,6 +1442,8 @@ void LocationsBuilderX86::VisitTypeConversion(HTypeConversion* conversion) {
case Primitive::kPrimLong:
switch (input_type) {
+ case Primitive::kPrimBoolean:
+ // Boolean input is a result of code transformations.
case Primitive::kPrimByte:
case Primitive::kPrimShort:
case Primitive::kPrimInt:
@@ -1464,6 +1473,8 @@ void LocationsBuilderX86::VisitTypeConversion(HTypeConversion* conversion) {
case Primitive::kPrimChar:
switch (input_type) {
+ case Primitive::kPrimBoolean:
+ // Boolean input is a result of code transformations.
case Primitive::kPrimByte:
case Primitive::kPrimShort:
case Primitive::kPrimInt:
@@ -1480,6 +1491,8 @@ void LocationsBuilderX86::VisitTypeConversion(HTypeConversion* conversion) {
case Primitive::kPrimFloat:
switch (input_type) {
+ case Primitive::kPrimBoolean:
+ // Boolean input is a result of code transformations.
case Primitive::kPrimByte:
case Primitive::kPrimShort:
case Primitive::kPrimInt:
@@ -1511,6 +1524,8 @@ void LocationsBuilderX86::VisitTypeConversion(HTypeConversion* conversion) {
case Primitive::kPrimDouble:
switch (input_type) {
+ case Primitive::kPrimBoolean:
+ // Boolean input is a result of code transformations.
case Primitive::kPrimByte:
case Primitive::kPrimShort:
case Primitive::kPrimInt:
@@ -1556,6 +1571,8 @@ void InstructionCodeGeneratorX86::VisitTypeConversion(HTypeConversion* conversio
switch (result_type) {
case Primitive::kPrimByte:
switch (input_type) {
+ case Primitive::kPrimBoolean:
+ // Boolean input is a result of code transformations.
case Primitive::kPrimShort:
case Primitive::kPrimInt:
case Primitive::kPrimChar:
@@ -1577,6 +1594,8 @@ void InstructionCodeGeneratorX86::VisitTypeConversion(HTypeConversion* conversio
case Primitive::kPrimShort:
switch (input_type) {
+ case Primitive::kPrimBoolean:
+ // Boolean input is a result of code transformations.
case Primitive::kPrimByte:
case Primitive::kPrimInt:
case Primitive::kPrimChar:
@@ -1672,6 +1691,8 @@ void InstructionCodeGeneratorX86::VisitTypeConversion(HTypeConversion* conversio
case Primitive::kPrimLong:
switch (input_type) {
+ case Primitive::kPrimBoolean:
+ // Boolean input is a result of code transformations.
case Primitive::kPrimByte:
case Primitive::kPrimShort:
case Primitive::kPrimInt:
@@ -1703,6 +1724,8 @@ void InstructionCodeGeneratorX86::VisitTypeConversion(HTypeConversion* conversio
case Primitive::kPrimChar:
switch (input_type) {
+ case Primitive::kPrimBoolean:
+ // Boolean input is a result of code transformations.
case Primitive::kPrimByte:
case Primitive::kPrimShort:
case Primitive::kPrimInt:
@@ -1726,6 +1749,8 @@ void InstructionCodeGeneratorX86::VisitTypeConversion(HTypeConversion* conversio
case Primitive::kPrimFloat:
switch (input_type) {
+ case Primitive::kPrimBoolean:
+ // Boolean input is a result of code transformations.
case Primitive::kPrimByte:
case Primitive::kPrimShort:
case Primitive::kPrimInt:
@@ -1783,6 +1808,8 @@ void InstructionCodeGeneratorX86::VisitTypeConversion(HTypeConversion* conversio
case Primitive::kPrimDouble:
switch (input_type) {
+ case Primitive::kPrimBoolean:
+ // Boolean input is a result of code transformations.
case Primitive::kPrimByte:
case Primitive::kPrimShort:
case Primitive::kPrimInt:
diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc
index b12f57ed76..bff8fc99ac 100644
--- a/compiler/optimizing/code_generator_x86_64.cc
+++ b/compiler/optimizing/code_generator_x86_64.cc
@@ -729,7 +729,7 @@ void InstructionCodeGeneratorX86_64::VisitGoto(HGoto* got) {
HInstruction* previous = got->GetPrevious();
HLoopInformation* info = block->GetLoopInformation();
- if (info != nullptr && info->IsBackEdge(block) && info->HasSuspendCheck()) {
+ if (info != nullptr && info->IsBackEdge(*block) && info->HasSuspendCheck()) {
codegen_->ClearSpillSlotsFromLoopPhisInStackMap(info->GetSuspendCheck());
GenerateSuspendCheck(info->GetSuspendCheck(), successor);
return;
@@ -1409,9 +1409,15 @@ void LocationsBuilderX86_64::VisitTypeConversion(HTypeConversion* conversion) {
Primitive::Type result_type = conversion->GetResultType();
Primitive::Type input_type = conversion->GetInputType();
DCHECK_NE(result_type, input_type);
+
+ // Java language does not allow treating boolean as an integral type but our
+ // bit representation makes it safe.
+
switch (result_type) {
case Primitive::kPrimByte:
switch (input_type) {
+ case Primitive::kPrimBoolean:
+ // Boolean input is a result of code transformations.
case Primitive::kPrimShort:
case Primitive::kPrimInt:
case Primitive::kPrimChar:
@@ -1428,6 +1434,8 @@ void LocationsBuilderX86_64::VisitTypeConversion(HTypeConversion* conversion) {
case Primitive::kPrimShort:
switch (input_type) {
+ case Primitive::kPrimBoolean:
+ // Boolean input is a result of code transformations.
case Primitive::kPrimByte:
case Primitive::kPrimInt:
case Primitive::kPrimChar:
@@ -1472,6 +1480,8 @@ void LocationsBuilderX86_64::VisitTypeConversion(HTypeConversion* conversion) {
case Primitive::kPrimLong:
switch (input_type) {
+ case Primitive::kPrimBoolean:
+ // Boolean input is a result of code transformations.
case Primitive::kPrimByte:
case Primitive::kPrimShort:
case Primitive::kPrimInt:
@@ -1505,6 +1515,8 @@ void LocationsBuilderX86_64::VisitTypeConversion(HTypeConversion* conversion) {
case Primitive::kPrimChar:
switch (input_type) {
+ case Primitive::kPrimBoolean:
+ // Boolean input is a result of code transformations.
case Primitive::kPrimByte:
case Primitive::kPrimShort:
case Primitive::kPrimInt:
@@ -1521,6 +1533,8 @@ void LocationsBuilderX86_64::VisitTypeConversion(HTypeConversion* conversion) {
case Primitive::kPrimFloat:
switch (input_type) {
+ case Primitive::kPrimBoolean:
+ // Boolean input is a result of code transformations.
case Primitive::kPrimByte:
case Primitive::kPrimShort:
case Primitive::kPrimInt:
@@ -1550,6 +1564,8 @@ void LocationsBuilderX86_64::VisitTypeConversion(HTypeConversion* conversion) {
case Primitive::kPrimDouble:
switch (input_type) {
+ case Primitive::kPrimBoolean:
+ // Boolean input is a result of code transformations.
case Primitive::kPrimByte:
case Primitive::kPrimShort:
case Primitive::kPrimInt:
@@ -1593,6 +1609,8 @@ void InstructionCodeGeneratorX86_64::VisitTypeConversion(HTypeConversion* conver
switch (result_type) {
case Primitive::kPrimByte:
switch (input_type) {
+ case Primitive::kPrimBoolean:
+ // Boolean input is a result of code transformations.
case Primitive::kPrimShort:
case Primitive::kPrimInt:
case Primitive::kPrimChar:
@@ -1617,6 +1635,8 @@ void InstructionCodeGeneratorX86_64::VisitTypeConversion(HTypeConversion* conver
case Primitive::kPrimShort:
switch (input_type) {
+ case Primitive::kPrimBoolean:
+ // Boolean input is a result of code transformations.
case Primitive::kPrimByte:
case Primitive::kPrimInt:
case Primitive::kPrimChar:
@@ -1715,6 +1735,8 @@ void InstructionCodeGeneratorX86_64::VisitTypeConversion(HTypeConversion* conver
case Primitive::kPrimLong:
switch (input_type) {
DCHECK(out.IsRegister());
+ case Primitive::kPrimBoolean:
+ // Boolean input is a result of code transformations.
case Primitive::kPrimByte:
case Primitive::kPrimShort:
case Primitive::kPrimInt:
@@ -1782,6 +1804,8 @@ void InstructionCodeGeneratorX86_64::VisitTypeConversion(HTypeConversion* conver
case Primitive::kPrimChar:
switch (input_type) {
+ case Primitive::kPrimBoolean:
+ // Boolean input is a result of code transformations.
case Primitive::kPrimByte:
case Primitive::kPrimShort:
case Primitive::kPrimInt:
@@ -1806,6 +1830,8 @@ void InstructionCodeGeneratorX86_64::VisitTypeConversion(HTypeConversion* conver
case Primitive::kPrimFloat:
switch (input_type) {
+ case Primitive::kPrimBoolean:
+ // Boolean input is a result of code transformations.
case Primitive::kPrimByte:
case Primitive::kPrimShort:
case Primitive::kPrimInt:
@@ -1832,6 +1858,8 @@ void InstructionCodeGeneratorX86_64::VisitTypeConversion(HTypeConversion* conver
case Primitive::kPrimDouble:
switch (input_type) {
+ case Primitive::kPrimBoolean:
+ // Boolean input is a result of code transformations.
case Primitive::kPrimByte:
case Primitive::kPrimShort:
case Primitive::kPrimInt:
diff --git a/compiler/optimizing/graph_checker.cc b/compiler/optimizing/graph_checker.cc
index 76b9f4fe7e..09a3ae431f 100644
--- a/compiler/optimizing/graph_checker.cc
+++ b/compiler/optimizing/graph_checker.cc
@@ -227,13 +227,13 @@ void SSAChecker::CheckLoop(HBasicBlock* loop_header) {
} else {
HLoopInformation* loop_information = loop_header->GetLoopInformation();
HBasicBlock* first_predecessor = loop_header->GetPredecessors().Get(0);
- if (loop_information->IsBackEdge(first_predecessor)) {
+ if (loop_information->IsBackEdge(*first_predecessor)) {
AddError(StringPrintf(
"First predecessor of loop header %d is a back edge.",
id));
}
HBasicBlock* second_predecessor = loop_header->GetPredecessors().Get(1);
- if (!loop_information->IsBackEdge(second_predecessor)) {
+ if (!loop_information->IsBackEdge(*second_predecessor)) {
AddError(StringPrintf(
"Second predecessor of loop header %d is not a back edge.",
id));
diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc
index a90ebced69..6009cb50cd 100644
--- a/compiler/optimizing/nodes.cc
+++ b/compiler/optimizing/nodes.cc
@@ -185,7 +185,7 @@ void HGraph::SplitCriticalEdge(HBasicBlock* block, HBasicBlock* successor) {
if (successor->IsLoopHeader()) {
// If we split at a back edge boundary, make the new block the back edge.
HLoopInformation* info = successor->GetLoopInformation();
- if (info->IsBackEdge(block)) {
+ if (info->IsBackEdge(*block)) {
info->RemoveBackEdge(block);
info->AddBackEdge(new_block);
}
@@ -287,19 +287,49 @@ bool HGraph::AnalyzeNaturalLoops() const {
return true;
}
+void HGraph::AddConstant(HConstant* instruction) {
+ HInstruction* last_instruction = entry_block_->GetLastInstruction();
+ if (last_instruction == nullptr || !last_instruction->IsControlFlow()) {
+ // Called from the builder. Insert at the end of the block.
+ entry_block_->AddInstruction(instruction);
+ } else {
+ // Entry block ends with control-flow. Insert before the last instruction.
+ entry_block_->InsertInstructionBefore(instruction, last_instruction);
+ }
+}
+
HNullConstant* HGraph::GetNullConstant() {
if (cached_null_constant_ == nullptr) {
cached_null_constant_ = new (arena_) HNullConstant();
- entry_block_->InsertInstructionBefore(cached_null_constant_,
- entry_block_->GetLastInstruction());
+ AddConstant(cached_null_constant_);
}
return cached_null_constant_;
}
+HIntConstant* HGraph::GetIntConstant0() {
+ if (cached_int_constant0_ == nullptr) {
+ cached_int_constant0_ = new (arena_) HIntConstant(0);
+ AddConstant(cached_int_constant0_);
+ }
+ return cached_int_constant0_;
+}
+
+HIntConstant* HGraph::GetIntConstant1() {
+ if (cached_int_constant1_ == nullptr) {
+ cached_int_constant1_ = new (arena_) HIntConstant(1);
+ AddConstant(cached_int_constant1_);
+ }
+ return cached_int_constant1_;
+}
+
void HLoopInformation::Add(HBasicBlock* block) {
blocks_.SetBit(block->GetBlockId());
}
+void HLoopInformation::Remove(HBasicBlock* block) {
+ blocks_.ClearBit(block->GetBlockId());
+}
+
void HLoopInformation::PopulateRecursive(HBasicBlock* block) {
if (blocks_.IsBitSet(block->GetBlockId())) {
return;
@@ -621,7 +651,10 @@ FOR_EACH_INSTRUCTION(DEFINE_ACCEPT)
void HGraphVisitor::VisitInsertionOrder() {
const GrowableArray<HBasicBlock*>& blocks = graph_->GetBlocks();
for (size_t i = 0 ; i < blocks.Size(); i++) {
- VisitBasicBlock(blocks.Get(i));
+ HBasicBlock* block = blocks.Get(i);
+ if (block != nullptr) {
+ VisitBasicBlock(block);
+ }
}
}
@@ -788,6 +821,17 @@ HBasicBlock* HBasicBlock::SplitAfter(HInstruction* cursor) {
return new_block;
}
+bool HBasicBlock::IsSingleGoto() const {
+ HLoopInformation* loop_info = GetLoopInformation();
+ // TODO: Remove the null check b/19084197.
+ return GetFirstInstruction() != nullptr
+ && GetPhis().IsEmpty()
+ && GetFirstInstruction() == GetLastInstruction()
+ && GetLastInstruction()->IsGoto()
+ // Back edges generate the suspend check.
+ && (loop_info == nullptr || !loop_info->IsBackEdge(*this));
+}
+
void HInstructionList::SetBlockOfInstructions(HBasicBlock* block) const {
for (HInstruction* current = first_instruction_;
current != nullptr;
@@ -811,14 +855,35 @@ void HInstructionList::AddAfter(HInstruction* cursor, const HInstructionList& in
}
void HInstructionList::Add(const HInstructionList& instruction_list) {
- DCHECK(!IsEmpty());
- AddAfter(last_instruction_, instruction_list);
+ if (IsEmpty()) {
+ first_instruction_ = instruction_list.first_instruction_;
+ last_instruction_ = instruction_list.last_instruction_;
+ } else {
+ AddAfter(last_instruction_, instruction_list);
+ }
+}
+
+void HBasicBlock::DisconnectFromAll() {
+ DCHECK(dominated_blocks_.IsEmpty()) << "Unimplemented scenario";
+
+ for (size_t i = 0, e = predecessors_.Size(); i < e; ++i) {
+ predecessors_.Get(i)->successors_.Delete(this);
+ }
+ for (size_t i = 0, e = successors_.Size(); i < e; ++i) {
+ successors_.Get(i)->predecessors_.Delete(this);
+ }
+ dominator_->dominated_blocks_.Delete(this);
+
+ predecessors_.Reset();
+ successors_.Reset();
+ dominator_ = nullptr;
+ graph_ = nullptr;
}
void HBasicBlock::MergeWith(HBasicBlock* other) {
DCHECK(successors_.IsEmpty()) << "Unimplemented block merge scenario";
- DCHECK(dominated_blocks_.IsEmpty()) << "Unimplemented block merge scenario";
- DCHECK(other->GetDominator()->IsEntryBlock() && other->GetGraph() != graph_)
+ DCHECK(dominated_blocks_.IsEmpty()
+ || (dominated_blocks_.Size() == 1 && dominated_blocks_.Get(0) == other))
<< "Unimplemented block merge scenario";
DCHECK(other->GetPhis().IsEmpty());
@@ -1006,7 +1071,7 @@ void HGraph::InlineInto(HGraph* outer_graph, HInvoke* invoke) {
if (info != nullptr) {
info->Add(to);
to->SetLoopInformation(info);
- if (info->IsBackEdge(at)) {
+ if (info->IsBackEdge(*at)) {
// Only `at` can become a back edge, as the inlined blocks
// are predecessors of `at`.
DCHECK_EQ(1u, info->NumberOfBackEdges());
@@ -1020,6 +1085,57 @@ void HGraph::InlineInto(HGraph* outer_graph, HInvoke* invoke) {
invoke->GetBlock()->RemoveInstruction(invoke);
}
+void HGraph::MergeEmptyBranches(HBasicBlock* start_block, HBasicBlock* end_block) {
+ // Make sure this is a diamond control-flow path, find the two branches.
+ DCHECK_EQ(start_block->GetSuccessors().Size(), 2u);
+ DCHECK_EQ(end_block->GetPredecessors().Size(), 2u);
+ HBasicBlock* left_branch = start_block->GetSuccessors().Get(0);
+ HBasicBlock* right_branch = start_block->GetSuccessors().Get(1);
+ DCHECK_EQ(left_branch->GetSuccessors().Get(0), end_block);
+ DCHECK_EQ(right_branch->GetSuccessors().Get(0), end_block);
+ DCHECK_EQ(start_block, end_block->GetDominator());
+
+ // Disconnect the branches and merge the two blocks. This will move
+ // all instructions from 'end_block' to 'start_block'.
+ DCHECK(left_branch->IsSingleGoto());
+ DCHECK(right_branch->IsSingleGoto());
+ left_branch->DisconnectFromAll();
+ right_branch->DisconnectFromAll();
+ start_block->RemoveInstruction(start_block->GetLastInstruction());
+ start_block->MergeWith(end_block);
+
+ // Delete the now redundant blocks from the graph.
+ blocks_.Put(left_branch->GetBlockId(), nullptr);
+ blocks_.Put(right_branch->GetBlockId(), nullptr);
+ blocks_.Put(end_block->GetBlockId(), nullptr);
+
+ // Update reverse post order.
+ reverse_post_order_.Delete(left_branch);
+ reverse_post_order_.Delete(right_branch);
+ reverse_post_order_.Delete(end_block);
+
+ // Update loop information.
+ HLoopInformation* loop_info = start_block->GetLoopInformation();
+ if (kIsDebugBuild) {
+ if (loop_info != nullptr) {
+ DCHECK_EQ(loop_info, left_branch->GetLoopInformation());
+ DCHECK_EQ(loop_info, right_branch->GetLoopInformation());
+ DCHECK_EQ(loop_info, end_block->GetLoopInformation());
+ }
+ }
+ while (loop_info != nullptr) {
+ loop_info->Remove(left_branch);
+ loop_info->Remove(right_branch);
+ loop_info->Remove(end_block);
+ if (loop_info->IsBackEdge(*end_block)) {
+ loop_info->RemoveBackEdge(end_block);
+ loop_info->AddBackEdge(start_block);
+ }
+ // Move to parent loop if nested.
+ loop_info = loop_info->GetHeader()->GetDominator()->GetLoopInformation();
+ }
+}
+
std::ostream& operator<<(std::ostream& os, const ReferenceTypeInfo& rhs) {
ScopedObjectAccess soa(Thread::Current());
os << "["
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index 9c9e6240c6..97ade0dc62 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -128,6 +128,7 @@ class HGraph : public ArenaObject<kArenaAllocMisc> {
void SetExitBlock(HBasicBlock* block) { exit_block_ = block; }
void AddBlock(HBasicBlock* block);
+ void AddConstant(HConstant* instruction);
// Try building the SSA form of this graph, with dominance computation and loop
// recognition. Returns whether it was successful in doing all these steps.
@@ -154,6 +155,8 @@ class HGraph : public ArenaObject<kArenaAllocMisc> {
// Inline this graph in `outer_graph`, replacing the given `invoke` instruction.
void InlineInto(HGraph* outer_graph, HInvoke* invoke);
+ void MergeEmptyBranches(HBasicBlock* start_block, HBasicBlock* end_block);
+
void SplitCriticalEdge(HBasicBlock* block, HBasicBlock* successor);
void SimplifyLoop(HBasicBlock* header);
@@ -217,6 +220,8 @@ class HGraph : public ArenaObject<kArenaAllocMisc> {
bool IsDebuggable() const { return debuggable_; }
HNullConstant* GetNullConstant();
+ HIntConstant* GetIntConstant0();
+ HIntConstant* GetIntConstant1();
private:
HBasicBlock* FindCommonDominator(HBasicBlock* first, HBasicBlock* second) const;
@@ -267,6 +272,10 @@ class HGraph : public ArenaObject<kArenaAllocMisc> {
// Cached null constant that might be created when building SSA form.
HNullConstant* cached_null_constant_;
+ // Cached common constants often needed by optimization passes.
+ HIntConstant* cached_int_constant0_;
+ HIntConstant* cached_int_constant1_;
+
ART_FRIEND_TEST(GraphTest, IfSuccessorSimpleJoinBlock1);
DISALLOW_COPY_AND_ASSIGN(HGraph);
};
@@ -300,9 +309,9 @@ class HLoopInformation : public ArenaObject<kArenaAllocMisc> {
back_edges_.Delete(back_edge);
}
- bool IsBackEdge(HBasicBlock* block) {
+ bool IsBackEdge(const HBasicBlock& block) const {
for (size_t i = 0, e = back_edges_.Size(); i < e; ++i) {
- if (back_edges_.Get(i) == block) return true;
+ if (back_edges_.Get(i) == &block) return true;
}
return false;
}
@@ -336,6 +345,7 @@ class HLoopInformation : public ArenaObject<kArenaAllocMisc> {
const ArenaBitVector& GetBlocks() const { return blocks_; }
void Add(HBasicBlock* block);
+ void Remove(HBasicBlock* block);
private:
// Internal recursive implementation of `Populate`.
@@ -391,6 +401,8 @@ class HBasicBlock : public ArenaObject<kArenaAllocMisc> {
return graph_->GetExitBlock() == this;
}
+ bool IsSingleGoto() const;
+
void AddBackEdge(HBasicBlock* back_edge) {
if (loop_information_ == nullptr) {
loop_information_ = new (graph_->GetArena()) HLoopInformation(this, graph_);
@@ -512,8 +524,16 @@ class HBasicBlock : public ArenaObject<kArenaAllocMisc> {
// of `this` are moved to `other`.
// Note that this method does not update the graph, reverse post order, loop
// information, nor make sure the blocks are consistent (for example ending
+ // with a control flow instruction).
void ReplaceWith(HBasicBlock* other);
+ // Disconnects `this` from all its predecessors, successors and the dominator.
+ // It assumes that `this` does not dominate any blocks.
+ // Note that this method does not update the graph, reverse post order, loop
+ // information, nor make sure the blocks are consistent (for example ending
+ // with a control flow instruction).
+ void DisconnectFromAll();
+
void AddInstruction(HInstruction* instruction);
void InsertInstructionBefore(HInstruction* instruction, HInstruction* cursor);
// Replace instruction `initial` with `replacement` within this block.
diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc
index 933a8a005c..ea969dcb44 100644
--- a/compiler/optimizing/optimizing_compiler.cc
+++ b/compiler/optimizing/optimizing_compiler.cc
@@ -22,6 +22,7 @@
#include "base/arena_allocator.h"
#include "base/dumpable.h"
#include "base/timing_logger.h"
+#include "boolean_simplifier.h"
#include "bounds_check_elimination.h"
#include "builder.h"
#include "code_generator.h"
@@ -313,6 +314,7 @@ static void RunOptimizations(HGraph* graph,
HDeadCodeElimination dce(graph);
HConstantFolding fold1(graph);
InstructionSimplifier simplify1(graph, stats);
+ HBooleanSimplifier boolean_not(graph);
HInliner inliner(graph, dex_compilation_unit, driver, stats);
@@ -331,6 +333,9 @@ static void RunOptimizations(HGraph* graph,
&dce,
&fold1,
&simplify1,
+ // BooleanSimplifier depends on the InstructionSimplifier removing redundant
+ // suspend checks to recognize empty blocks.
+ &boolean_not,
&inliner,
&fold2,
&side_effects,
diff --git a/compiler/optimizing/ssa_liveness_analysis.cc b/compiler/optimizing/ssa_liveness_analysis.cc
index c0d6f42ca5..56ccd717cf 100644
--- a/compiler/optimizing/ssa_liveness_analysis.cc
+++ b/compiler/optimizing/ssa_liveness_analysis.cc
@@ -71,8 +71,8 @@ void SsaLivenessAnalysis::LinearizeGraph() {
// for it.
GrowableArray<uint32_t> forward_predecessors(graph_.GetArena(), graph_.GetBlocks().Size());
forward_predecessors.SetSize(graph_.GetBlocks().Size());
- for (size_t i = 0, e = graph_.GetBlocks().Size(); i < e; ++i) {
- HBasicBlock* block = graph_.GetBlocks().Get(i);
+ for (HReversePostOrderIterator it(graph_); !it.Done(); it.Advance()) {
+ HBasicBlock* block = it.Current();
size_t number_of_forward_predecessors = block->GetPredecessors().Size();
if (block->IsLoopHeader()) {
// We rely on having simplified the CFG.
diff --git a/runtime/primitive.h b/runtime/primitive.h
index 2d6b6b30c7..d11f1e955e 100644
--- a/runtime/primitive.h
+++ b/runtime/primitive.h
@@ -153,7 +153,10 @@ class Primitive {
}
static bool IsIntegralType(Type type) {
+ // Java language does not allow treating boolean as an integral type but our
+ // bit representation makes it safe.
switch (type) {
+ case kPrimBoolean:
case kPrimByte:
case kPrimChar:
case kPrimShort:
diff --git a/test/463-checker-boolean-simplifier/expected.txt b/test/463-checker-boolean-simplifier/expected.txt
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/test/463-checker-boolean-simplifier/expected.txt
diff --git a/test/463-checker-boolean-simplifier/info.txt b/test/463-checker-boolean-simplifier/info.txt
new file mode 100644
index 0000000000..9c0493aebd
--- /dev/null
+++ b/test/463-checker-boolean-simplifier/info.txt
@@ -0,0 +1 @@
+Tests simplification of boolean NOT in optimizing compiler.
diff --git a/test/463-checker-boolean-simplifier/src/Main.java b/test/463-checker-boolean-simplifier/src/Main.java
new file mode 100644
index 0000000000..25f58b4b43
--- /dev/null
+++ b/test/463-checker-boolean-simplifier/src/Main.java
@@ -0,0 +1,174 @@
+/*
+* 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 {
+
+ // Note #1: `javac` flips the conditions of If statements.
+ // Note #2: In the optimizing compiler, the first input of Phi is always
+ // the fall-through path, i.e. the false branch.
+
+ public static void assertBoolEquals(boolean expected, boolean result) {
+ if (expected != result) {
+ throw new Error("Expected: " + expected + ", found: " + result);
+ }
+ }
+
+ /*
+ * Elementary test negating a boolean. Verifies that the condition is replaced,
+ * blocks merged and empty branches removed.
+ */
+
+ // CHECK-START: boolean Main.BooleanNot(boolean) boolean_simplifier (before)
+ // CHECK-DAG: [[Param:z\d+]] ParameterValue
+ // CHECK-DAG: [[Const0:i\d+]] IntConstant 0
+ // CHECK-DAG: [[Const1:i\d+]] IntConstant 1
+ // CHECK-DAG: [[NotEq:z\d+]] NotEqual [ [[Param]] [[Const0]] ]
+ // CHECK-DAG: If [ [[NotEq]] ]
+ // CHECK-DAG: [[Phi:i\d+]] Phi [ [[Const1]] [[Const0]] ]
+ // CHECK-DAG: Return [ [[Phi]] ]
+
+ // CHECK-START: boolean Main.BooleanNot(boolean) boolean_simplifier (before)
+ // CHECK: Goto
+ // CHECK: Goto
+ // CHECK: Goto
+ // CHECK-NOT: Goto
+
+ // CHECK-START: boolean Main.BooleanNot(boolean) boolean_simplifier (after)
+ // CHECK-DAG: [[Param:z\d+]] ParameterValue
+ // CHECK-DAG: [[Const0:i\d+]] IntConstant 0
+ // CHECK-DAG: [[Eq:z\d+]] Equal [ [[Param]] [[Const0]] ]
+ // CHECK-DAG: Return [ [[Eq]] ]
+
+ // CHECK-START: boolean Main.BooleanNot(boolean) boolean_simplifier (after)
+ // CHECK-NOT: NotEqual
+ // CHECK-NOT: If
+ // CHECK-NOT: Phi
+
+ // CHECK-START: boolean Main.BooleanNot(boolean) boolean_simplifier (after)
+ // CHECK: Goto
+ // CHECK-NOT: Goto
+
+ public static boolean BooleanNot(boolean x) {
+ return !x;
+ }
+
+ /*
+ * Program which only delegates the condition, i.e. returns 1 when True
+ * and 0 when False.
+ */
+
+ // CHECK-START: boolean Main.GreaterThan(int, int) boolean_simplifier (before)
+ // CHECK-DAG: [[ParamX:i\d+]] ParameterValue
+ // CHECK-DAG: [[ParamY:i\d+]] ParameterValue
+ // CHECK-DAG: [[Const0:i\d+]] IntConstant 0
+ // CHECK-DAG: [[Const1:i\d+]] IntConstant 1
+ // CHECK-DAG: [[Cond:z\d+]] GreaterThan [ [[ParamX]] [[ParamY]] ]
+ // CHECK-DAG: If [ [[Cond]] ]
+ // CHECK-DAG: [[Phi:i\d+]] Phi [ [[Const0]] [[Const1]] ]
+ // CHECK-DAG: Return [ [[Phi]] ]
+
+ // CHECK-START: boolean Main.GreaterThan(int, int) boolean_simplifier (after)
+ // CHECK-DAG: [[ParamX:i\d+]] ParameterValue
+ // CHECK-DAG: [[ParamY:i\d+]] ParameterValue
+ // CHECK-DAG: [[Const0:i\d+]] IntConstant 0
+ // CHECK-DAG: [[Const1:i\d+]] IntConstant 1
+ // CHECK-DAG: [[Cond:z\d+]] GreaterThan [ [[ParamX]] [[ParamY]] ]
+ // CHECK-DAG: Return [ [[Cond]] ]
+
+ public static boolean GreaterThan(int x, int y) {
+ return (x <= y) ? false : true;
+ }
+
+ /*
+ * Program which negates a condition, i.e. returns 0 when True
+ * and 1 when False.
+ */
+
+ // CHECK-START: boolean Main.LessThan(int, int) boolean_simplifier (before)
+ // CHECK-DAG: [[ParamX:i\d+]] ParameterValue
+ // CHECK-DAG: [[ParamY:i\d+]] ParameterValue
+ // CHECK-DAG: [[Const0:i\d+]] IntConstant 0
+ // CHECK-DAG: [[Const1:i\d+]] IntConstant 1
+ // CHECK-DAG: [[Cond:z\d+]] GreaterThanOrEqual [ [[ParamX]] [[ParamY]] ]
+ // CHECK-DAG: If [ [[Cond]] ]
+ // CHECK-DAG: [[Phi:i\d+]] Phi [ [[Const1]] [[Const0]] ]
+ // CHECK-DAG: Return [ [[Phi]] ]
+
+ // CHECK-START: boolean Main.LessThan(int, int) boolean_simplifier (after)
+ // CHECK-DAG: [[ParamX:i\d+]] ParameterValue
+ // CHECK-DAG: [[ParamY:i\d+]] ParameterValue
+ // CHECK-DAG: [[Const0:i\d+]] IntConstant 0
+ // CHECK-DAG: [[Const1:i\d+]] IntConstant 1
+ // CHECK-DAG: [[Cond:z\d+]] LessThan [ [[ParamX]] [[ParamY]] ]
+ // CHECK-DAG: Return [ [[Cond]] ]
+
+ public static boolean LessThan(int x, int y) {
+ return x < y;
+ }
+
+ /*
+ * Program which further uses negated conditions.
+ * Note that Phis are discovered retrospectively.
+ */
+
+ // CHECK-START: boolean Main.ValuesOrdered(int, int, int) boolean_simplifier (before)
+ // CHECK-DAG: [[ParamX:i\d+]] ParameterValue
+ // CHECK-DAG: [[ParamY:i\d+]] ParameterValue
+ // CHECK-DAG: [[ParamZ:i\d+]] ParameterValue
+ // CHECK-DAG: [[Const0:i\d+]] IntConstant 0
+ // CHECK-DAG: [[Const1:i\d+]] IntConstant 1
+ // CHECK-DAG: [[CondXY:z\d+]] GreaterThan [ [[ParamX]] [[ParamY]] ]
+ // CHECK-DAG: If [ [[CondXY]] ]
+ // CHECK-DAG: [[CondYZ:z\d+]] GreaterThan [ [[ParamY]] [[ParamZ]] ]
+ // CHECK-DAG: If [ [[CondYZ]] ]
+ // CHECK-DAG: [[CondXYZ:z\d+]] NotEqual [ [[PhiXY:i\d+]] [[PhiYZ:i\d+]] ]
+ // CHECK-DAG: If [ [[CondXYZ]] ]
+ // CHECK-DAG: Return [ [[PhiXYZ:i\d+]] ]
+ // CHECK-DAG: [[PhiXY]] Phi [ [[Const1]] [[Const0]] ]
+ // CHECK-DAG: [[PhiYZ]] Phi [ [[Const1]] [[Const0]] ]
+ // CHECK-DAG: [[PhiXYZ]] Phi [ [[Const1]] [[Const0]] ]
+
+ // CHECK-START: boolean Main.ValuesOrdered(int, int, int) boolean_simplifier (after)
+ // CHECK-DAG: [[ParamX:i\d+]] ParameterValue
+ // CHECK-DAG: [[ParamY:i\d+]] ParameterValue
+ // CHECK-DAG: [[ParamZ:i\d+]] ParameterValue
+ // CHECK-DAG: [[CmpXY:z\d+]] LessThanOrEqual [ [[ParamX]] [[ParamY]] ]
+ // CHECK-DAG: [[CmpYZ:z\d+]] LessThanOrEqual [ [[ParamY]] [[ParamZ]] ]
+ // CHECK-DAG: [[CmpXYZ:z\d+]] Equal [ [[CmpXY]] [[CmpYZ]] ]
+ // CHECK-DAG: Return [ [[CmpXYZ]] ]
+
+ public static boolean ValuesOrdered(int x, int y, int z) {
+ return (x <= y) == (y <= z);
+ }
+
+ public static void main(String[] args) {
+ assertBoolEquals(false, BooleanNot(true));
+ assertBoolEquals(true, BooleanNot(false));
+ assertBoolEquals(true, GreaterThan(10, 5));
+ assertBoolEquals(false, GreaterThan(10, 10));
+ assertBoolEquals(false, GreaterThan(5, 10));
+ assertBoolEquals(true, LessThan(5, 10));
+ assertBoolEquals(false, LessThan(10, 10));
+ assertBoolEquals(false, LessThan(10, 5));
+ assertBoolEquals(true, ValuesOrdered(1, 3, 5));
+ assertBoolEquals(true, ValuesOrdered(5, 3, 1));
+ assertBoolEquals(false, ValuesOrdered(1, 3, 2));
+ assertBoolEquals(false, ValuesOrdered(2, 3, 1));
+ assertBoolEquals(true, ValuesOrdered(3, 3, 3));
+ assertBoolEquals(true, ValuesOrdered(3, 3, 5));
+ assertBoolEquals(false, ValuesOrdered(5, 5, 3));
+ }
+}