diff options
author | David Brazdil <dbrazdil@google.com> | 2015-03-24 17:31:29 +0000 |
---|---|---|
committer | Gerrit Code Review <noreply-gerritcodereview@google.com> | 2015-03-24 17:31:31 +0000 |
commit | b64b782f9ae7a94ecbbf64c83cbcdc7d716ba560 (patch) | |
tree | df3aa814ff7762d681c50781c413fd510440ae61 | |
parent | 2c2d00e8ca841aa2f57fa2f852e896378ef67144 (diff) | |
parent | 46e2a3915aa68c77426b71e95b9f3658250646b7 (diff) | |
download | android_art-b64b782f9ae7a94ecbbf64c83cbcdc7d716ba560.tar.gz android_art-b64b782f9ae7a94ecbbf64c83cbcdc7d716ba560.tar.bz2 android_art-b64b782f9ae7a94ecbbf64c83cbcdc7d716ba560.zip |
Merge "ART: Boolean simplifier"
-rw-r--r-- | compiler/Android.mk | 1 | ||||
-rw-r--r-- | compiler/optimizing/boolean_simplifier.cc | 140 | ||||
-rw-r--r-- | compiler/optimizing/boolean_simplifier.h | 74 | ||||
-rw-r--r-- | compiler/optimizing/builder.cc | 26 | ||||
-rw-r--r-- | compiler/optimizing/builder.h | 9 | ||||
-rw-r--r-- | compiler/optimizing/code_generator.cc | 16 | ||||
-rw-r--r-- | compiler/optimizing/code_generator_arm.cc | 29 | ||||
-rw-r--r-- | compiler/optimizing/code_generator_arm64.cc | 2 | ||||
-rw-r--r-- | compiler/optimizing/code_generator_x86.cc | 29 | ||||
-rw-r--r-- | compiler/optimizing/code_generator_x86_64.cc | 30 | ||||
-rw-r--r-- | compiler/optimizing/graph_checker.cc | 4 | ||||
-rw-r--r-- | compiler/optimizing/nodes.cc | 134 | ||||
-rw-r--r-- | compiler/optimizing/nodes.h | 24 | ||||
-rw-r--r-- | compiler/optimizing/optimizing_compiler.cc | 5 | ||||
-rw-r--r-- | compiler/optimizing/ssa_liveness_analysis.cc | 4 | ||||
-rw-r--r-- | runtime/primitive.h | 3 | ||||
-rw-r--r-- | test/463-checker-boolean-simplifier/expected.txt | 0 | ||||
-rw-r--r-- | test/463-checker-boolean-simplifier/info.txt | 1 | ||||
-rw-r--r-- | test/463-checker-boolean-simplifier/src/Main.java | 174 |
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)); + } +} |