diff options
31 files changed, 1091 insertions, 384 deletions
diff --git a/compiler/Android.mk b/compiler/Android.mk index 6f32e07ce1..87eff82982 100644 --- a/compiler/Android.mk +++ b/compiler/Android.mk @@ -66,7 +66,6 @@ LIBART_COMPILER_SRC_FILES := \ jit/jit_compiler.cc \ jni/quick/calling_convention.cc \ jni/quick/jni_compiler.cc \ - optimizing/boolean_simplifier.cc \ optimizing/bounds_check_elimination.cc \ optimizing/builder.cc \ optimizing/code_generator.cc \ @@ -94,6 +93,7 @@ LIBART_COMPILER_SRC_FILES := \ optimizing/prepare_for_register_allocation.cc \ optimizing/reference_type_propagation.cc \ optimizing/register_allocator.cc \ + optimizing/select_generator.cc \ optimizing/sharpening.cc \ optimizing/side_effects_analysis.cc \ optimizing/ssa_builder.cc \ diff --git a/compiler/optimizing/boolean_simplifier.cc b/compiler/optimizing/boolean_simplifier.cc deleted file mode 100644 index f0cafc847f..0000000000 --- a/compiler/optimizing/boolean_simplifier.cc +++ /dev/null @@ -1,136 +0,0 @@ -/* - * 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 { - -void HBooleanSimplifier::TryRemovingNegatedCondition(HBasicBlock* block) { - DCHECK(block->EndsWithIf()); - - // Check if the condition is a Boolean negation. - HIf* if_instruction = block->GetLastInstruction()->AsIf(); - HInstruction* boolean_not = if_instruction->InputAt(0); - if (!boolean_not->IsBooleanNot()) { - return; - } - - // Make BooleanNot's input the condition of the If and swap branches. - if_instruction->ReplaceInput(boolean_not->InputAt(0), 0); - block->SwapSuccessors(); - - // Remove the BooleanNot if it is now unused. - if (!boolean_not->HasUses()) { - boolean_not->GetBlock()->RemoveInstruction(boolean_not); - } -} - -// 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()[0]; - HBasicBlock* succ2 = block2->GetSuccessors()[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()->IsOne() - && input_false->IsIntConstant() && input_false->AsIntConstant()->IsZero(); -} - -// 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()->IsZero() - && input_false->IsIntConstant() && input_false->AsIntConstant()->IsOne(); -} - -void HBooleanSimplifier::TryRemovingBooleanSelection(HBasicBlock* block) { - DCHECK(block->EndsWithIf()); - - // 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)) { - return; - } - HBasicBlock* merge_block = true_block->GetSuccessors()[0]; - if (!merge_block->HasSinglePhi()) { - return; - } - 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); - - // Don't change FP compares. The definition of compares involving NaNs forces - // the compares to be done as written by the user. - if (if_condition->IsCondition() && - Primitive::IsFloatingPointType(if_condition->InputAt(0)->GetType())) { - return; - } - - HInstruction* replacement; - if (NegatesCondition(true_value, false_value)) { - replacement = graph_->InsertOppositeCondition(if_condition, if_instruction); - } else if (PreservesCondition(true_value, false_value)) { - replacement = if_condition; - } else { - return; - } - - // Replace the selection outcome with the new instruction. - phi->ReplaceWith(replacement); - merge_block->RemovePhi(phi); - - // Delete the true branch and merge the resulting chain of blocks - // 'block->false_block->merge_block' into one. - true_block->DisconnectAndDelete(); - block->MergeWith(false_block); - block->MergeWith(merge_block); - - // No need to update any dominance information, as we are simplifying - // a simple diamond shape, where the join block is merged with the - // entry block. Any following blocks would have had the join block - // as a dominator, and `MergeWith` handles changing that to the - // entry block. -} - -void HBooleanSimplifier::Run() { - // Iterate in post order in the unlikely case that removing one occurrence of - // the selection 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 (!block->EndsWithIf()) continue; - - // If condition is negated, remove the negation and swap the branches. - TryRemovingNegatedCondition(block); - - // If this is a boolean-selection diamond pattern, replace its result with - // the condition value (or its negation) and simplify the graph. - TryRemovingBooleanSelection(block); - } -} - -} // namespace art diff --git a/compiler/optimizing/boolean_simplifier.h b/compiler/optimizing/boolean_simplifier.h deleted file mode 100644 index e12a12c95b..0000000000 --- a/compiler/optimizing/boolean_simplifier.h +++ /dev/null @@ -1,81 +0,0 @@ -/* - * 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 two common patterns: -// (a) Boolean selection: Casting a boolean to an integer or negating it is -// carried out with an If statement selecting from zero/one integer -// constants. Because Boolean values are represented as zero/one, the -// pattern can be replaced with the condition instruction itself or its -// negation, depending on the layout. -// (b) Negated condition: Instruction simplifier may replace an If's condition -// with a boolean value. If this value is the result of a Boolean negation, -// the true/false branches can be swapped and negation removed. - -// 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, kBooleanSimplifierPassName) {} - - void Run() OVERRIDE; - - static constexpr const char* kBooleanSimplifierPassName = "boolean_simplifier"; - - private: - void TryRemovingNegatedCondition(HBasicBlock* block); - void TryRemovingBooleanSelection(HBasicBlock* block); - - DISALLOW_COPY_AND_ASSIGN(HBooleanSimplifier); -}; - -} // namespace art - -#endif // ART_COMPILER_OPTIMIZING_BOOLEAN_SIMPLIFIER_H_ diff --git a/compiler/optimizing/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc index 2b81dba0f9..894ee07222 100644 --- a/compiler/optimizing/code_generator_arm.cc +++ b/compiler/optimizing/code_generator_arm.cc @@ -1285,11 +1285,9 @@ void CodeGeneratorARM::MoveConstant(Location location, int32_t value) { } void CodeGeneratorARM::MoveLocation(Location dst, Location src, Primitive::Type dst_type) { - if (Primitive::Is64BitType(dst_type)) { - Move64(dst, src); - } else { - Move32(dst, src); - } + HParallelMove move(GetGraph()->GetArena()); + move.AddMove(src, dst, dst_type, nullptr); + GetMoveResolver()->EmitNativeCode(&move); } void CodeGeneratorARM::AddLocationAsTemp(Location location, LocationSummary* locations) { @@ -1612,6 +1610,32 @@ void InstructionCodeGeneratorARM::VisitDeoptimize(HDeoptimize* deoptimize) { /* false_target */ nullptr); } +void LocationsBuilderARM::VisitSelect(HSelect* select) { + LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(select); + if (Primitive::IsFloatingPointType(select->GetType())) { + locations->SetInAt(0, Location::RequiresFpuRegister()); + locations->SetInAt(1, Location::RequiresFpuRegister()); + } else { + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetInAt(1, Location::RequiresRegister()); + } + if (IsBooleanValueOrMaterializedCondition(select->GetCondition())) { + locations->SetInAt(2, Location::RequiresRegister()); + } + locations->SetOut(Location::SameAsFirstInput()); +} + +void InstructionCodeGeneratorARM::VisitSelect(HSelect* select) { + LocationSummary* locations = select->GetLocations(); + Label false_target; + GenerateTestAndBranch(select, + /* condition_input_index */ 2, + /* true_target */ nullptr, + &false_target); + codegen_->MoveLocation(locations->Out(), locations->InAt(1), select->GetType()); + __ Bind(&false_target); +} + void LocationsBuilderARM::VisitNativeDebugInfo(HNativeDebugInfo* info) { new (GetGraph()->GetArena()) LocationSummary(info); } @@ -4973,6 +4997,8 @@ void ParallelMoveResolverARM::EmitMove(size_t index) { if (source.IsRegister()) { if (destination.IsRegister()) { __ Mov(destination.AsRegister<Register>(), source.AsRegister<Register>()); + } else if (destination.IsFpuRegister()) { + __ vmovsr(destination.AsFpuRegister<SRegister>(), source.AsRegister<Register>()); } else { DCHECK(destination.IsStackSlot()); __ StoreToOffset(kStoreWord, source.AsRegister<Register>(), @@ -4990,7 +5016,9 @@ void ParallelMoveResolverARM::EmitMove(size_t index) { __ StoreToOffset(kStoreWord, IP, SP, destination.GetStackIndex()); } } else if (source.IsFpuRegister()) { - if (destination.IsFpuRegister()) { + if (destination.IsRegister()) { + __ vmovrs(destination.AsRegister<Register>(), source.AsFpuRegister<SRegister>()); + } else if (destination.IsFpuRegister()) { __ vmovs(destination.AsFpuRegister<SRegister>(), source.AsFpuRegister<SRegister>()); } else { DCHECK(destination.IsStackSlot()); @@ -5014,6 +5042,10 @@ void ParallelMoveResolverARM::EmitMove(size_t index) { if (destination.IsRegisterPair()) { __ Mov(destination.AsRegisterPairLow<Register>(), source.AsRegisterPairLow<Register>()); __ Mov(destination.AsRegisterPairHigh<Register>(), source.AsRegisterPairHigh<Register>()); + } else if (destination.IsFpuRegisterPair()) { + __ vmovdrr(FromLowSToD(destination.AsFpuRegisterPairLow<SRegister>()), + source.AsRegisterPairLow<Register>(), + source.AsRegisterPairHigh<Register>()); } else { DCHECK(destination.IsDoubleStackSlot()) << destination; DCHECK(ExpectedPairLayout(source)); @@ -5021,7 +5053,11 @@ void ParallelMoveResolverARM::EmitMove(size_t index) { kStoreWordPair, source.AsRegisterPairLow<Register>(), SP, destination.GetStackIndex()); } } else if (source.IsFpuRegisterPair()) { - if (destination.IsFpuRegisterPair()) { + if (destination.IsRegisterPair()) { + __ vmovrrd(destination.AsRegisterPairLow<Register>(), + destination.AsRegisterPairHigh<Register>(), + FromLowSToD(source.AsFpuRegisterPairLow<SRegister>())); + } else if (destination.IsFpuRegisterPair()) { __ vmovd(FromLowSToD(destination.AsFpuRegisterPairLow<SRegister>()), FromLowSToD(source.AsFpuRegisterPairLow<SRegister>())); } else { diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc index 788c3a62d5..ec5ca3d83b 100644 --- a/compiler/optimizing/code_generator_arm64.cc +++ b/compiler/optimizing/code_generator_arm64.cc @@ -3004,6 +3004,32 @@ void InstructionCodeGeneratorARM64::VisitDeoptimize(HDeoptimize* deoptimize) { /* false_target */ nullptr); } +void LocationsBuilderARM64::VisitSelect(HSelect* select) { + LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(select); + if (Primitive::IsFloatingPointType(select->GetType())) { + locations->SetInAt(0, Location::RequiresFpuRegister()); + locations->SetInAt(1, Location::RequiresFpuRegister()); + } else { + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetInAt(1, Location::RequiresRegister()); + } + if (IsBooleanValueOrMaterializedCondition(select->GetCondition())) { + locations->SetInAt(2, Location::RequiresRegister()); + } + locations->SetOut(Location::SameAsFirstInput()); +} + +void InstructionCodeGeneratorARM64::VisitSelect(HSelect* select) { + LocationSummary* locations = select->GetLocations(); + vixl::Label false_target; + GenerateTestAndBranch(select, + /* condition_input_index */ 2, + /* true_target */ nullptr, + &false_target); + codegen_->MoveLocation(locations->Out(), locations->InAt(1), select->GetType()); + __ Bind(&false_target); +} + void LocationsBuilderARM64::VisitNativeDebugInfo(HNativeDebugInfo* info) { new (GetGraph()->GetArena()) LocationSummary(info); } diff --git a/compiler/optimizing/code_generator_mips.cc b/compiler/optimizing/code_generator_mips.cc index fec5811082..a2f85fe05c 100644 --- a/compiler/optimizing/code_generator_mips.cc +++ b/compiler/optimizing/code_generator_mips.cc @@ -3381,6 +3381,32 @@ void InstructionCodeGeneratorMIPS::VisitDeoptimize(HDeoptimize* deoptimize) { /* false_target */ nullptr); } +void LocationsBuilderMIPS::VisitSelect(HSelect* select) { + LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(select); + if (Primitive::IsFloatingPointType(select->GetType())) { + locations->SetInAt(0, Location::RequiresFpuRegister()); + locations->SetInAt(1, Location::RequiresFpuRegister()); + } else { + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetInAt(1, Location::RequiresRegister()); + } + if (IsBooleanValueOrMaterializedCondition(select->GetCondition())) { + locations->SetInAt(2, Location::RequiresRegister()); + } + locations->SetOut(Location::SameAsFirstInput()); +} + +void InstructionCodeGeneratorMIPS::VisitSelect(HSelect* select) { + LocationSummary* locations = select->GetLocations(); + MipsLabel false_target; + GenerateTestAndBranch(select, + /* condition_input_index */ 2, + /* true_target */ nullptr, + &false_target); + codegen_->MoveLocation(locations->Out(), locations->InAt(1), select->GetType()); + __ Bind(&false_target); +} + void LocationsBuilderMIPS::VisitNativeDebugInfo(HNativeDebugInfo* info) { new (GetGraph()->GetArena()) LocationSummary(info); } diff --git a/compiler/optimizing/code_generator_mips64.cc b/compiler/optimizing/code_generator_mips64.cc index e5a5fd4096..930a3ecdee 100644 --- a/compiler/optimizing/code_generator_mips64.cc +++ b/compiler/optimizing/code_generator_mips64.cc @@ -2748,6 +2748,32 @@ void InstructionCodeGeneratorMIPS64::VisitDeoptimize(HDeoptimize* deoptimize) { /* false_target */ nullptr); } +void LocationsBuilderMIPS64::VisitSelect(HSelect* select) { + LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(select); + if (Primitive::IsFloatingPointType(select->GetType())) { + locations->SetInAt(0, Location::RequiresFpuRegister()); + locations->SetInAt(1, Location::RequiresFpuRegister()); + } else { + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetInAt(1, Location::RequiresRegister()); + } + if (IsBooleanValueOrMaterializedCondition(select->GetCondition())) { + locations->SetInAt(2, Location::RequiresRegister()); + } + locations->SetOut(Location::SameAsFirstInput()); +} + +void InstructionCodeGeneratorMIPS64::VisitSelect(HSelect* select) { + LocationSummary* locations = select->GetLocations(); + Mips64Label false_target; + GenerateTestAndBranch(select, + /* condition_input_index */ 2, + /* true_target */ nullptr, + &false_target); + codegen_->MoveLocation(locations->Out(), locations->InAt(1), select->GetType()); + __ Bind(&false_target); +} + void LocationsBuilderMIPS64::VisitNativeDebugInfo(HNativeDebugInfo* info) { new (GetGraph()->GetArena()) LocationSummary(info); } diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc index a4a8c7c103..6a4f9243f5 100644 --- a/compiler/optimizing/code_generator_x86.cc +++ b/compiler/optimizing/code_generator_x86.cc @@ -1218,11 +1218,14 @@ void CodeGeneratorX86::MoveConstant(Location location, int32_t value) { } void CodeGeneratorX86::MoveLocation(Location dst, Location src, Primitive::Type dst_type) { - if (Primitive::Is64BitType(dst_type)) { - Move64(dst, src); + HParallelMove move(GetGraph()->GetArena()); + if (dst_type == Primitive::kPrimLong && !src.IsConstant() && !src.IsFpuRegister()) { + move.AddMove(src.ToLow(), dst.ToLow(), Primitive::kPrimInt, nullptr); + move.AddMove(src.ToHigh(), dst.ToHigh(), Primitive::kPrimInt, nullptr); } else { - Move32(dst, src); + move.AddMove(src, dst, dst_type, nullptr); } + GetMoveResolver()->EmitNativeCode(&move); } void CodeGeneratorX86::AddLocationAsTemp(Location location, LocationSummary* locations) { @@ -1559,10 +1562,36 @@ void LocationsBuilderX86::VisitDeoptimize(HDeoptimize* deoptimize) { void InstructionCodeGeneratorX86::VisitDeoptimize(HDeoptimize* deoptimize) { SlowPathCode* slow_path = deopt_slow_paths_.NewSlowPath<DeoptimizationSlowPathX86>(deoptimize); - GenerateTestAndBranch(deoptimize, - /* condition_input_index */ 0, - slow_path->GetEntryLabel(), - /* false_target */ static_cast<Label*>(nullptr)); + GenerateTestAndBranch<Label>(deoptimize, + /* condition_input_index */ 0, + slow_path->GetEntryLabel(), + /* false_target */ nullptr); +} + +void LocationsBuilderX86::VisitSelect(HSelect* select) { + LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(select); + Primitive::Type select_type = select->GetType(); + HInstruction* cond = select->GetCondition(); + + if (Primitive::IsFloatingPointType(select_type)) { + locations->SetInAt(0, Location::RequiresFpuRegister()); + } else { + locations->SetInAt(0, Location::RequiresRegister()); + } + locations->SetInAt(1, Location::Any()); + if (IsBooleanValueOrMaterializedCondition(cond)) { + locations->SetInAt(2, Location::Any()); + } + locations->SetOut(Location::SameAsFirstInput()); +} + +void InstructionCodeGeneratorX86::VisitSelect(HSelect* select) { + LocationSummary* locations = select->GetLocations(); + NearLabel false_target; + GenerateTestAndBranch<NearLabel>( + select, /* condition_input_index */ 2, /* true_target */ nullptr, &false_target); + codegen_->MoveLocation(locations->Out(), locations->InAt(1), select->GetType()); + __ Bind(&false_target); } void LocationsBuilderX86::VisitNativeDebugInfo(HNativeDebugInfo* info) { @@ -5481,13 +5510,31 @@ void ParallelMoveResolverX86::EmitMove(size_t index) { if (source.IsRegister()) { if (destination.IsRegister()) { __ movl(destination.AsRegister<Register>(), source.AsRegister<Register>()); + } else if (destination.IsFpuRegister()) { + __ movd(destination.AsFpuRegister<XmmRegister>(), source.AsRegister<Register>()); } else { DCHECK(destination.IsStackSlot()); __ movl(Address(ESP, destination.GetStackIndex()), source.AsRegister<Register>()); } + } else if (source.IsRegisterPair()) { + size_t elem_size = Primitive::ComponentSize(Primitive::kPrimInt); + // Create stack space for 2 elements. + __ subl(ESP, Immediate(2 * elem_size)); + __ movl(Address(ESP, 0), source.AsRegisterPairLow<Register>()); + __ movl(Address(ESP, elem_size), source.AsRegisterPairHigh<Register>()); + __ movsd(destination.AsFpuRegister<XmmRegister>(), Address(ESP, 0)); + // And remove the temporary stack space we allocated. + __ addl(ESP, Immediate(2 * elem_size)); } else if (source.IsFpuRegister()) { - if (destination.IsFpuRegister()) { + if (destination.IsRegister()) { + __ movd(destination.AsRegister<Register>(), source.AsFpuRegister<XmmRegister>()); + } else if (destination.IsFpuRegister()) { __ movaps(destination.AsFpuRegister<XmmRegister>(), source.AsFpuRegister<XmmRegister>()); + } else if (destination.IsRegisterPair()) { + XmmRegister src_reg = source.AsFpuRegister<XmmRegister>(); + __ movd(destination.AsRegisterPairLow<Register>(), src_reg); + __ psrlq(src_reg, Immediate(32)); + __ movd(destination.AsRegisterPairHigh<Register>(), src_reg); } else if (destination.IsStackSlot()) { __ movss(Address(ESP, destination.GetStackIndex()), source.AsFpuRegister<XmmRegister>()); } else { @@ -5504,7 +5551,11 @@ void ParallelMoveResolverX86::EmitMove(size_t index) { MoveMemoryToMemory32(destination.GetStackIndex(), source.GetStackIndex()); } } else if (source.IsDoubleStackSlot()) { - if (destination.IsFpuRegister()) { + if (destination.IsRegisterPair()) { + __ movl(destination.AsRegisterPairLow<Register>(), Address(ESP, source.GetStackIndex())); + __ movl(destination.AsRegisterPairHigh<Register>(), + Address(ESP, source.GetHighStackIndex(kX86WordSize))); + } else if (destination.IsFpuRegister()) { __ movsd(destination.AsFpuRegister<XmmRegister>(), Address(ESP, source.GetStackIndex())); } else { DCHECK(destination.IsDoubleStackSlot()) << destination; diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc index 1781f419f1..895e7c343e 100644 --- a/compiler/optimizing/code_generator_x86_64.cc +++ b/compiler/optimizing/code_generator_x86_64.cc @@ -1558,10 +1558,36 @@ void LocationsBuilderX86_64::VisitDeoptimize(HDeoptimize* deoptimize) { void InstructionCodeGeneratorX86_64::VisitDeoptimize(HDeoptimize* deoptimize) { SlowPathCode* slow_path = deopt_slow_paths_.NewSlowPath<DeoptimizationSlowPathX86_64>(deoptimize); - GenerateTestAndBranch(deoptimize, - /* condition_input_index */ 0, - slow_path->GetEntryLabel(), - /* false_target */ static_cast<Label*>(nullptr)); + GenerateTestAndBranch<Label>(deoptimize, + /* condition_input_index */ 0, + slow_path->GetEntryLabel(), + /* false_target */ nullptr); +} + +void LocationsBuilderX86_64::VisitSelect(HSelect* select) { + LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(select); + if (Primitive::IsFloatingPointType(select->GetType())) { + locations->SetInAt(0, Location::RequiresFpuRegister()); + locations->SetInAt(1, Location::RequiresFpuRegister()); + } else { + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetInAt(1, Location::RequiresRegister()); + } + if (IsBooleanValueOrMaterializedCondition(select->GetCondition())) { + locations->SetInAt(2, Location::RequiresRegister()); + } + locations->SetOut(Location::SameAsFirstInput()); +} + +void InstructionCodeGeneratorX86_64::VisitSelect(HSelect* select) { + LocationSummary* locations = select->GetLocations(); + NearLabel false_target; + GenerateTestAndBranch<NearLabel>(select, + /* condition_input_index */ 2, + /* true_target */ nullptr, + &false_target); + codegen_->MoveLocation(locations->Out(), locations->InAt(1), select->GetType()); + __ Bind(&false_target); } void LocationsBuilderX86_64::VisitNativeDebugInfo(HNativeDebugInfo* info) { diff --git a/compiler/optimizing/graph_checker.cc b/compiler/optimizing/graph_checker.cc index 31136772c7..962e77dfc9 100644 --- a/compiler/optimizing/graph_checker.cc +++ b/compiler/optimizing/graph_checker.cc @@ -859,8 +859,12 @@ void SSAChecker::HandleBooleanInput(HInstruction* instruction, size_t input_inde value)); } } else if (input->GetType() == Primitive::kPrimInt - && (input->IsPhi() || input->IsAnd() || input->IsOr() || input->IsXor())) { - // TODO: We need a data-flow analysis to determine if the Phi or + && (input->IsPhi() || + input->IsAnd() || + input->IsOr() || + input->IsXor() || + input->IsSelect())) { + // TODO: We need a data-flow analysis to determine if the Phi or Select or // binary operation is actually Boolean. Allow for now. } else if (input->GetType() != Primitive::kPrimBoolean) { AddError(StringPrintf( @@ -893,6 +897,11 @@ void SSAChecker::VisitIf(HIf* instruction) { HandleBooleanInput(instruction, 0); } +void SSAChecker::VisitSelect(HSelect* instruction) { + VisitInstruction(instruction); + HandleBooleanInput(instruction, 2); +} + void SSAChecker::VisitBooleanNot(HBooleanNot* instruction) { VisitInstruction(instruction); HandleBooleanInput(instruction, 0); diff --git a/compiler/optimizing/graph_checker.h b/compiler/optimizing/graph_checker.h index 2e16bfe245..8724cde5dd 100644 --- a/compiler/optimizing/graph_checker.h +++ b/compiler/optimizing/graph_checker.h @@ -126,6 +126,7 @@ class SSAChecker : public GraphChecker { void VisitCondition(HCondition* op) OVERRIDE; void VisitIf(HIf* instruction) OVERRIDE; void VisitPackedSwitch(HPackedSwitch* instruction) OVERRIDE; + void VisitSelect(HSelect* instruction) OVERRIDE; void VisitBooleanNot(HBooleanNot* instruction) OVERRIDE; void VisitConstant(HConstant* instruction) OVERRIDE; void VisitBoundType(HBoundType* instruction) OVERRIDE; diff --git a/compiler/optimizing/instruction_simplifier.cc b/compiler/optimizing/instruction_simplifier.cc index 49fc8c71b3..7d3a7238dc 100644 --- a/compiler/optimizing/instruction_simplifier.cc +++ b/compiler/optimizing/instruction_simplifier.cc @@ -76,6 +76,8 @@ class InstructionSimplifierVisitor : public HGraphDelegateVisitor { void VisitSub(HSub* instruction) OVERRIDE; void VisitUShr(HUShr* instruction) OVERRIDE; void VisitXor(HXor* instruction) OVERRIDE; + void VisitSelect(HSelect* select) OVERRIDE; + void VisitIf(HIf* instruction) OVERRIDE; void VisitInstanceOf(HInstanceOf* instruction) OVERRIDE; void VisitInvoke(HInvoke* invoke) OVERRIDE; void VisitDeoptimize(HDeoptimize* deoptimize) OVERRIDE; @@ -559,14 +561,86 @@ void InstructionSimplifierVisitor::VisitNotEqual(HNotEqual* not_equal) { } void InstructionSimplifierVisitor::VisitBooleanNot(HBooleanNot* bool_not) { - HInstruction* parent = bool_not->InputAt(0); - if (parent->IsBooleanNot()) { - HInstruction* value = parent->InputAt(0); - // Replace (!(!bool_value)) with bool_value - bool_not->ReplaceWith(value); + HInstruction* input = bool_not->InputAt(0); + HInstruction* replace_with = nullptr; + + if (input->IsIntConstant()) { + // Replace !(true/false) with false/true. + if (input->AsIntConstant()->IsOne()) { + replace_with = GetGraph()->GetIntConstant(0); + } else { + DCHECK(input->AsIntConstant()->IsZero()); + replace_with = GetGraph()->GetIntConstant(1); + } + } else if (input->IsBooleanNot()) { + // Replace (!(!bool_value)) with bool_value. + replace_with = input->InputAt(0); + } else if (input->IsCondition() && + // Don't change FP compares. The definition of compares involving + // NaNs forces the compares to be done as written by the user. + !Primitive::IsFloatingPointType(input->InputAt(0)->GetType())) { + // Replace condition with its opposite. + replace_with = GetGraph()->InsertOppositeCondition(input->AsCondition(), bool_not); + } + + if (replace_with != nullptr) { + bool_not->ReplaceWith(replace_with); bool_not->GetBlock()->RemoveInstruction(bool_not); - // It is possible that `parent` is dead at this point but we leave - // its removal to DCE for simplicity. + RecordSimplification(); + } +} + +void InstructionSimplifierVisitor::VisitSelect(HSelect* select) { + HInstruction* replace_with = nullptr; + HInstruction* condition = select->GetCondition(); + HInstruction* true_value = select->GetTrueValue(); + HInstruction* false_value = select->GetFalseValue(); + + if (condition->IsBooleanNot()) { + // Change ((!cond) ? x : y) to (cond ? y : x). + condition = condition->InputAt(0); + std::swap(true_value, false_value); + select->ReplaceInput(false_value, 0); + select->ReplaceInput(true_value, 1); + select->ReplaceInput(condition, 2); + RecordSimplification(); + } + + if (true_value == false_value) { + // Replace (cond ? x : x) with (x). + replace_with = true_value; + } else if (condition->IsIntConstant()) { + if (condition->AsIntConstant()->IsOne()) { + // Replace (true ? x : y) with (x). + replace_with = true_value; + } else { + // Replace (false ? x : y) with (y). + DCHECK(condition->AsIntConstant()->IsZero()); + replace_with = false_value; + } + } else if (true_value->IsIntConstant() && false_value->IsIntConstant()) { + if (true_value->AsIntConstant()->IsOne() && false_value->AsIntConstant()->IsZero()) { + // Replace (cond ? true : false) with (cond). + replace_with = condition; + } else if (true_value->AsIntConstant()->IsZero() && false_value->AsIntConstant()->IsOne()) { + // Replace (cond ? false : true) with (!cond). + replace_with = GetGraph()->InsertOppositeCondition(condition, select); + } + } + + if (replace_with != nullptr) { + select->ReplaceWith(replace_with); + select->GetBlock()->RemoveInstruction(select); + RecordSimplification(); + } +} + +void InstructionSimplifierVisitor::VisitIf(HIf* instruction) { + HInstruction* condition = instruction->InputAt(0); + if (condition->IsBooleanNot()) { + // Swap successors if input is negated. + instruction->ReplaceInput(condition->InputAt(0), 0); + instruction->GetBlock()->SwapSuccessors(); RecordSimplification(); } } diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc index 92f758d61d..c057eca434 100644 --- a/compiler/optimizing/nodes.cc +++ b/compiler/optimizing/nodes.cc @@ -722,6 +722,22 @@ void HBasicBlock::ReplaceAndRemoveInstructionWith(HInstruction* initial, RemoveInstruction(initial); } +void HBasicBlock::MoveInstructionBefore(HInstruction* insn, HInstruction* cursor) { + DCHECK(!cursor->IsPhi()); + DCHECK(!insn->IsPhi()); + DCHECK(!insn->IsControlFlow()); + DCHECK(insn->CanBeMoved()); + DCHECK(!insn->HasSideEffects()); + + HBasicBlock* from_block = insn->GetBlock(); + HBasicBlock* to_block = cursor->GetBlock(); + DCHECK(from_block != to_block); + + from_block->RemoveInstruction(insn, /* ensure_safety */ false); + insn->SetBlock(to_block); + to_block->instructions_.InsertInstructionBefore(insn, cursor); +} + static void Add(HInstructionList* instruction_list, HBasicBlock* block, HInstruction* instruction) { diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h index 922696bb5d..4de3f8abb5 100644 --- a/compiler/optimizing/nodes.h +++ b/compiler/optimizing/nodes.h @@ -1011,6 +1011,7 @@ class HBasicBlock : public ArenaObject<kArenaAllocBasicBlock> { // Replace instruction `initial` with `replacement` within this block. void ReplaceAndRemoveInstructionWith(HInstruction* initial, HInstruction* replacement); + void MoveInstructionBefore(HInstruction* insn, HInstruction* cursor); void AddPhi(HPhi* phi); void InsertPhiAfter(HPhi* instruction, HPhi* cursor); // RemoveInstruction and RemovePhi delete a given instruction from the respective @@ -1220,6 +1221,7 @@ class HLoopInformationOutwardIterator : public ValueObject { M(UnresolvedInstanceFieldSet, Instruction) \ M(UnresolvedStaticFieldGet, Instruction) \ M(UnresolvedStaticFieldSet, Instruction) \ + M(Select, Instruction) \ M(StoreLocal, Instruction) \ M(Sub, BinaryOperation) \ M(SuspendCheck, Instruction) \ @@ -5586,6 +5588,41 @@ class HMonitorOperation : public HTemplateInstruction<1> { DISALLOW_COPY_AND_ASSIGN(HMonitorOperation); }; +class HSelect : public HExpression<3> { + public: + HSelect(HInstruction* condition, + HInstruction* true_value, + HInstruction* false_value, + uint32_t dex_pc) + : HExpression(HPhi::ToPhiType(true_value->GetType()), SideEffects::None(), dex_pc) { + DCHECK_EQ(HPhi::ToPhiType(true_value->GetType()), HPhi::ToPhiType(false_value->GetType())); + + // First input must be `true_value` or `false_value` to allow codegens to + // use the SameAsFirstInput allocation policy. We make it `false_value`, so + // that architectures which implement HSelect as a conditional move also + // will not need to invert the condition. + SetRawInputAt(0, false_value); + SetRawInputAt(1, true_value); + SetRawInputAt(2, condition); + } + + HInstruction* GetFalseValue() const { return InputAt(0); } + HInstruction* GetTrueValue() const { return InputAt(1); } + HInstruction* GetCondition() const { return InputAt(2); } + + bool CanBeMoved() const OVERRIDE { return true; } + bool InstructionDataEquals(HInstruction* other ATTRIBUTE_UNUSED) const OVERRIDE { return true; } + + bool CanBeNull() const OVERRIDE { + return GetTrueValue()->CanBeNull() || GetFalseValue()->CanBeNull(); + } + + DECLARE_INSTRUCTION(Select); + + private: + DISALLOW_COPY_AND_ASSIGN(HSelect); +}; + class MoveOperands : public ArenaObject<kArenaAllocMoveOperands> { public: MoveOperands(Location source, diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc index 3fac914017..bdc664b3eb 100644 --- a/compiler/optimizing/optimizing_compiler.cc +++ b/compiler/optimizing/optimizing_compiler.cc @@ -38,7 +38,6 @@ #include "base/dumpable.h" #include "base/macros.h" #include "base/timing_logger.h" -#include "boolean_simplifier.h" #include "bounds_check_elimination.h" #include "builder.h" #include "code_generator.h" @@ -73,6 +72,7 @@ #include "reference_type_propagation.h" #include "register_allocator.h" #include "oat_quick_method_header.h" +#include "select_generator.h" #include "sharpening.h" #include "side_effects_analysis.h" #include "ssa_builder.h" @@ -512,7 +512,7 @@ static void RunOptimizations(HGraph* graph, graph, stats, HDeadCodeElimination::kFinalDeadCodeEliminationPassName); HConstantFolding* fold1 = new (arena) HConstantFolding(graph); InstructionSimplifier* simplify1 = new (arena) InstructionSimplifier(graph, stats); - HBooleanSimplifier* boolean_simplify = new (arena) HBooleanSimplifier(graph); + HSelectGenerator* select_generator = new (arena) HSelectGenerator(graph); HConstantFolding* fold2 = new (arena) HConstantFolding(graph, "constant_folding_after_inlining"); HConstantFolding* fold3 = new (arena) HConstantFolding(graph, "constant_folding_after_bce"); SideEffectsAnalysis* side_effects = new (arena) SideEffectsAnalysis(graph); @@ -540,9 +540,9 @@ static void RunOptimizations(HGraph* graph, MaybeRunInliner(graph, codegen, driver, stats, dex_compilation_unit, pass_observer, handles); HOptimization* optimizations2[] = { - // BooleanSimplifier depends on the InstructionSimplifier removing + // SelectGenerator depends on the InstructionSimplifier removing // redundant suspend checks to recognize empty blocks. - boolean_simplify, + select_generator, fold2, // TODO: if we don't inline we can also skip fold2. side_effects, gvn, diff --git a/compiler/optimizing/prepare_for_register_allocation.cc b/compiler/optimizing/prepare_for_register_allocation.cc index ec0fc3a814..324d84f3db 100644 --- a/compiler/optimizing/prepare_for_register_allocation.cc +++ b/compiler/optimizing/prepare_for_register_allocation.cc @@ -137,6 +137,18 @@ bool PrepareForRegisterAllocation::CanEmitConditionAt(HCondition* condition, return true; } + if (user->IsSelect() && user->AsSelect()->GetCondition() == condition) { + if (GetGraph()->GetInstructionSet() == kX86) { + // Long values and long condition inputs result in 8 required core registers. + // We don't have that many on x86. Materialize the condition in such case. + return user->GetType() != Primitive::kPrimLong || + condition->InputAt(1)->GetType() != Primitive::kPrimLong || + condition->InputAt(1)->IsConstant(); + } else { + return true; + } + } + return false; } diff --git a/compiler/optimizing/select_generator.cc b/compiler/optimizing/select_generator.cc new file mode 100644 index 0000000000..105b30ae5d --- /dev/null +++ b/compiler/optimizing/select_generator.cc @@ -0,0 +1,152 @@ +/* + * Copyright (C) 2016 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 "select_generator.h" + +namespace art { + +static constexpr size_t kMaxInstructionsInBranch = 1u; + +// Returns true if `block` has only one predecessor, ends with a Goto and +// contains at most `kMaxInstructionsInBranch` other movable instruction with +// no side-effects. +static bool IsSimpleBlock(HBasicBlock* block) { + if (block->GetPredecessors().size() != 1u) { + return false; + } + DCHECK(block->GetPhis().IsEmpty()); + + size_t num_instructions = 0u; + for (HInstructionIterator it(block->GetInstructions()); !it.Done(); it.Advance()) { + HInstruction* instruction = it.Current(); + if (instruction->IsControlFlow()) { + return instruction->IsGoto() && num_instructions <= kMaxInstructionsInBranch; + } else if (instruction->CanBeMoved() && !instruction->HasSideEffects()) { + num_instructions++; + } else { + return false; + } + } + + LOG(FATAL) << "Unreachable"; + UNREACHABLE(); +} + +// 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 BlocksMergeTogether(HBasicBlock* block1, HBasicBlock* block2) { + return block1->GetSingleSuccessor() == block2->GetSingleSuccessor(); +} + +// Returns nullptr if `block` has either no phis or there is more than one phi +// with different inputs at `index1` and `index2`. Otherwise returns that phi. +static HPhi* GetSingleChangedPhi(HBasicBlock* block, size_t index1, size_t index2) { + DCHECK_NE(index1, index2); + + HPhi* select_phi = nullptr; + for (HInstructionIterator it(block->GetPhis()); !it.Done(); it.Advance()) { + HPhi* phi = it.Current()->AsPhi(); + if (phi->InputAt(index1) != phi->InputAt(index2)) { + if (select_phi == nullptr) { + // First phi with different inputs for the two indices found. + select_phi = phi; + } else { + // More than one phis has different inputs for the two indices. + return nullptr; + } + } + } + return select_phi; +} + +void HSelectGenerator::Run() { + // Iterate in post order in the unlikely case that removing one occurrence of + // the selection 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 (!block->EndsWithIf()) continue; + + // Find elements of the diamond pattern. + HIf* if_instruction = block->GetLastInstruction()->AsIf(); + HBasicBlock* true_block = if_instruction->IfTrueSuccessor(); + HBasicBlock* false_block = if_instruction->IfFalseSuccessor(); + DCHECK_NE(true_block, false_block); + if (!IsSimpleBlock(true_block) || + !IsSimpleBlock(false_block) || + !BlocksMergeTogether(true_block, false_block)) { + continue; + } + HBasicBlock* merge_block = true_block->GetSingleSuccessor(); + + // If the branches are not empty, move instructions in front of the If. + // TODO(dbrazdil): This puts an instruction between If and its condition. + // Implement moving of conditions to first users if possible. + if (!true_block->IsSingleGoto()) { + true_block->MoveInstructionBefore(true_block->GetFirstInstruction(), if_instruction); + } + if (!false_block->IsSingleGoto()) { + false_block->MoveInstructionBefore(false_block->GetFirstInstruction(), if_instruction); + } + DCHECK(true_block->IsSingleGoto()); + DCHECK(false_block->IsSingleGoto()); + + // Find the resulting true/false values. + size_t predecessor_index_true = merge_block->GetPredecessorIndexOf(true_block); + size_t predecessor_index_false = merge_block->GetPredecessorIndexOf(false_block); + DCHECK_NE(predecessor_index_true, predecessor_index_false); + + HPhi* phi = GetSingleChangedPhi(merge_block, predecessor_index_true, predecessor_index_false); + if (phi == nullptr) { + continue; + } + HInstruction* true_value = phi->InputAt(predecessor_index_true); + HInstruction* false_value = phi->InputAt(predecessor_index_false); + + // Create the Select instruction and insert it in front of the If. + HSelect* select = new (graph_->GetArena()) HSelect(if_instruction->InputAt(0), + true_value, + false_value, + if_instruction->GetDexPc()); + if (phi->GetType() == Primitive::kPrimNot) { + select->SetReferenceTypeInfo(phi->GetReferenceTypeInfo()); + } + block->InsertInstructionBefore(select, if_instruction); + + // Remove the true branch which removes the corresponding Phi input. + // If left only with the false branch, the Phi is automatically removed. + phi->ReplaceInput(select, predecessor_index_false); + bool only_two_predecessors = (merge_block->GetPredecessors().size() == 2u); + true_block->DisconnectAndDelete(); + DCHECK_EQ(only_two_predecessors, phi->GetBlock() == nullptr); + + // Merge remaining blocks which are now connected with Goto. + DCHECK_EQ(block->GetSingleSuccessor(), false_block); + block->MergeWith(false_block); + if (only_two_predecessors) { + DCHECK_EQ(block->GetSingleSuccessor(), merge_block); + block->MergeWith(merge_block); + } + + // No need to update dominance information, as we are simplifying + // a simple diamond shape, where the join block is merged with the + // entry block. Any following blocks would have had the join block + // as a dominator, and `MergeWith` handles changing that to the + // entry block. + } +} + +} // namespace art diff --git a/compiler/optimizing/select_generator.h b/compiler/optimizing/select_generator.h new file mode 100644 index 0000000000..f9d6d4d8de --- /dev/null +++ b/compiler/optimizing/select_generator.h @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2016 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 the common diamond selection pattern and + * replaces it with an instance of the HSelect instruction. + * + * Recognized pattern: + * + * If [ Condition ] + * / \ + * false branch true branch + * \ / + * Phi [FalseValue, TrueValue] + * + * The pattern will be simplified if `true_branch` and `false_branch` each + * contain at most one instruction without any side effects. + * + * Blocks are merged into one and Select replaces the If and the Phi: + * true branch + * false branch + * Select [FalseValue, TrueValue, Condition] + * + * Note: In order to recognize no side-effect blocks, this optimization must be + * run after the instruction simplifier has removed redundant suspend checks. + */ + +#ifndef ART_COMPILER_OPTIMIZING_SELECT_GENERATOR_H_ +#define ART_COMPILER_OPTIMIZING_SELECT_GENERATOR_H_ + +#include "optimization.h" + +namespace art { + +class HSelectGenerator : public HOptimization { + public: + explicit HSelectGenerator(HGraph* graph) + : HOptimization(graph, kSelectGeneratorPassName) {} + + void Run() OVERRIDE; + + static constexpr const char* kSelectGeneratorPassName = "select_generator"; + + private: + DISALLOW_COPY_AND_ASSIGN(HSelectGenerator); +}; + +} // namespace art + +#endif // ART_COMPILER_OPTIMIZING_SELECT_GENERATOR_H_ diff --git a/test/442-checker-constant-folding/src/Main.java b/test/442-checker-constant-folding/src/Main.java index 0e07f47288..5479818ae7 100644 --- a/test/442-checker-constant-folding/src/Main.java +++ b/test/442-checker-constant-folding/src/Main.java @@ -666,11 +666,11 @@ public class Main { /// CHECK-START: int Main.StaticConditionNulls() constant_folding_after_inlining (before) /// CHECK-DAG: <<Null:l\d+>> NullConstant /// CHECK-DAG: <<Cond:z\d+>> NotEqual [<<Null>>,<<Null>>] - /// CHECK-DAG: If [<<Cond>>] + /// CHECK-DAG: Select [{{i\d+}},{{i\d+}},<<Cond>>] /// CHECK-START: int Main.StaticConditionNulls() constant_folding_after_inlining (after) /// CHECK-DAG: <<Const0:i\d+>> IntConstant 0 - /// CHECK-DAG: If [<<Const0>>] + /// CHECK-DAG: Select [{{i\d+}},{{i\d+}},<<Const0>>] /// CHECK-START: int Main.StaticConditionNulls() constant_folding_after_inlining (after) /// CHECK-NOT: NotEqual diff --git a/test/450-checker-types/src/Main.java b/test/450-checker-types/src/Main.java index 92cf807c2b..d48b30e324 100644 --- a/test/450-checker-types/src/Main.java +++ b/test/450-checker-types/src/Main.java @@ -275,23 +275,6 @@ public class Main { } } - /// CHECK-START: void Main.testNotInstanceOf_Inlined(java.lang.Object) inliner (after) - /// CHECK-DAG: <<IOf:z\d+>> InstanceOf - /// CHECK-DAG: <<Not:z\d+>> BooleanNot [<<IOf>>] - /// CHECK-DAG: If [<<Not>>] - - /// CHECK-START: void Main.testNotInstanceOf_Inlined(java.lang.Object) instruction_simplifier_after_bce (before) - /// CHECK: CheckCast - /// CHECK-NOT: CheckCast - - /// CHECK-START: void Main.testNotInstanceOf_Inlined(java.lang.Object) instruction_simplifier_after_bce (after) - /// CHECK-NOT: CheckCast - public void testNotInstanceOf_Inlined(Object o) { - if ($inline$InstanceofSubclassC(o)) { - ((SubclassC)o).$noinline$g(); - } - } - /// CHECK-START: void Main.testInstanceOfKeep(java.lang.Object) instruction_simplifier (before) /// CHECK: CheckCast /// CHECK: CheckCast diff --git a/test/458-checker-instruction-simplification/src/Main.java b/test/458-checker-instruction-simplification/src/Main.java index 0fd7801d48..3c8abeb841 100644 --- a/test/458-checker-instruction-simplification/src/Main.java +++ b/test/458-checker-instruction-simplification/src/Main.java @@ -969,6 +969,13 @@ public class Main { /// CHECK-DAG: <<Arg:z\d+>> ParameterValue /// CHECK-DAG: If [<<Arg>>] + /// CHECK-START: int Main.EqualTrueRhs(boolean) instruction_simplifier_before_codegen (after) + /// CHECK-DAG: <<Arg:z\d+>> ParameterValue + /// CHECK-DAG: <<Const3:i\d+>> IntConstant 3 + /// CHECK-DAG: <<Const5:i\d+>> IntConstant 5 + /// CHECK-DAG: <<Select:i\d+>> Select [<<Const3>>,<<Const5>>,<<Arg>>] + /// CHECK-DAG: Return [<<Select>>] + public static int EqualTrueRhs(boolean arg) { return (arg != true) ? 3 : 5; } @@ -983,6 +990,13 @@ public class Main { /// CHECK-DAG: <<Arg:z\d+>> ParameterValue /// CHECK-DAG: If [<<Arg>>] + /// CHECK-START: int Main.EqualTrueLhs(boolean) instruction_simplifier_before_codegen (after) + /// CHECK-DAG: <<Arg:z\d+>> ParameterValue + /// CHECK-DAG: <<Const3:i\d+>> IntConstant 3 + /// CHECK-DAG: <<Const5:i\d+>> IntConstant 5 + /// CHECK-DAG: <<Select:i\d+>> Select [<<Const3>>,<<Const5>>,<<Arg>>] + /// CHECK-DAG: Return [<<Select>>] + public static int EqualTrueLhs(boolean arg) { return (true != arg) ? 3 : 5; } @@ -995,8 +1009,14 @@ public class Main { /// CHECK-START: int Main.EqualFalseRhs(boolean) instruction_simplifier (after) /// CHECK-DAG: <<Arg:z\d+>> ParameterValue - /// CHECK-DAG: <<NotArg:z\d+>> BooleanNot [<<Arg>>] - /// CHECK-DAG: If [<<NotArg>>] + /// CHECK-DAG: If [<<Arg>>] + + /// CHECK-START: int Main.EqualFalseRhs(boolean) instruction_simplifier_before_codegen (after) + /// CHECK-DAG: <<Arg:z\d+>> ParameterValue + /// CHECK-DAG: <<Const3:i\d+>> IntConstant 3 + /// CHECK-DAG: <<Const5:i\d+>> IntConstant 5 + /// CHECK-DAG: <<Select:i\d+>> Select [<<Const5>>,<<Const3>>,<<Arg>>] + /// CHECK-DAG: Return [<<Select>>] public static int EqualFalseRhs(boolean arg) { return (arg != false) ? 3 : 5; @@ -1010,8 +1030,14 @@ public class Main { /// CHECK-START: int Main.EqualFalseLhs(boolean) instruction_simplifier (after) /// CHECK-DAG: <<Arg:z\d+>> ParameterValue - /// CHECK-DAG: <<NotArg:z\d+>> BooleanNot [<<Arg>>] - /// CHECK-DAG: If [<<NotArg>>] + /// CHECK-DAG: If [<<Arg>>] + + /// CHECK-START: int Main.EqualFalseLhs(boolean) instruction_simplifier_before_codegen (after) + /// CHECK-DAG: <<Arg:z\d+>> ParameterValue + /// CHECK-DAG: <<Const3:i\d+>> IntConstant 3 + /// CHECK-DAG: <<Const5:i\d+>> IntConstant 5 + /// CHECK-DAG: <<Select:i\d+>> Select [<<Const5>>,<<Const3>>,<<Arg>>] + /// CHECK-DAG: Return [<<Select>>] public static int EqualFalseLhs(boolean arg) { return (false != arg) ? 3 : 5; @@ -1025,8 +1051,14 @@ public class Main { /// CHECK-START: int Main.NotEqualTrueRhs(boolean) instruction_simplifier (after) /// CHECK-DAG: <<Arg:z\d+>> ParameterValue - /// CHECK-DAG: <<NotArg:z\d+>> BooleanNot [<<Arg>>] - /// CHECK-DAG: If [<<NotArg>>] + /// CHECK-DAG: If [<<Arg>>] + + /// CHECK-START: int Main.NotEqualTrueRhs(boolean) instruction_simplifier_before_codegen (after) + /// CHECK-DAG: <<Arg:z\d+>> ParameterValue + /// CHECK-DAG: <<Const3:i\d+>> IntConstant 3 + /// CHECK-DAG: <<Const5:i\d+>> IntConstant 5 + /// CHECK-DAG: <<Select:i\d+>> Select [<<Const5>>,<<Const3>>,<<Arg>>] + /// CHECK-DAG: Return [<<Select>>] public static int NotEqualTrueRhs(boolean arg) { return (arg == true) ? 3 : 5; @@ -1040,8 +1072,14 @@ public class Main { /// CHECK-START: int Main.NotEqualTrueLhs(boolean) instruction_simplifier (after) /// CHECK-DAG: <<Arg:z\d+>> ParameterValue - /// CHECK-DAG: <<NotArg:z\d+>> BooleanNot [<<Arg>>] - /// CHECK-DAG: If [<<NotArg>>] + /// CHECK-DAG: If [<<Arg>>] + + /// CHECK-START: int Main.NotEqualTrueLhs(boolean) instruction_simplifier_before_codegen (after) + /// CHECK-DAG: <<Arg:z\d+>> ParameterValue + /// CHECK-DAG: <<Const3:i\d+>> IntConstant 3 + /// CHECK-DAG: <<Const5:i\d+>> IntConstant 5 + /// CHECK-DAG: <<Select:i\d+>> Select [<<Const5>>,<<Const3>>,<<Arg>>] + /// CHECK-DAG: Return [<<Select>>] public static int NotEqualTrueLhs(boolean arg) { return (true == arg) ? 3 : 5; @@ -1057,6 +1095,13 @@ public class Main { /// CHECK-DAG: <<Arg:z\d+>> ParameterValue /// CHECK-DAG: If [<<Arg>>] + /// CHECK-START: int Main.NotEqualFalseRhs(boolean) instruction_simplifier_before_codegen (after) + /// CHECK-DAG: <<Arg:z\d+>> ParameterValue + /// CHECK-DAG: <<Const3:i\d+>> IntConstant 3 + /// CHECK-DAG: <<Const5:i\d+>> IntConstant 5 + /// CHECK-DAG: <<Select:i\d+>> Select [<<Const3>>,<<Const5>>,<<Arg>>] + /// CHECK-DAG: Return [<<Select>>] + public static int NotEqualFalseRhs(boolean arg) { return (arg == false) ? 3 : 5; } @@ -1071,38 +1116,51 @@ public class Main { /// CHECK-DAG: <<Arg:z\d+>> ParameterValue /// CHECK-DAG: If [<<Arg>>] + /// CHECK-START: int Main.NotEqualFalseLhs(boolean) instruction_simplifier_before_codegen (after) + /// CHECK-DAG: <<Arg:z\d+>> ParameterValue + /// CHECK-DAG: <<Const3:i\d+>> IntConstant 3 + /// CHECK-DAG: <<Const5:i\d+>> IntConstant 5 + /// CHECK-DAG: <<Select:i\d+>> Select [<<Const3>>,<<Const5>>,<<Arg>>] + /// CHECK-DAG: Return [<<Select>>] + public static int NotEqualFalseLhs(boolean arg) { return (false == arg) ? 3 : 5; } /// CHECK-START: boolean Main.EqualBoolVsIntConst(boolean) instruction_simplifier_after_bce (before) /// CHECK-DAG: <<Arg:z\d+>> ParameterValue + /// CHECK-DAG: <<Const0:i\d+>> IntConstant 0 + /// CHECK-DAG: <<Const1:i\d+>> IntConstant 1 /// CHECK-DAG: <<Const2:i\d+>> IntConstant 2 - /// CHECK-DAG: <<BoolNot:z\d+>> BooleanNot [<<Arg>>] - /// CHECK-DAG: <<Cond:z\d+>> Equal [<<BoolNot>>,<<Const2>>] - /// CHECK-DAG: Return [<<Cond>>] + /// CHECK-DAG: <<NotArg:i\d+>> Select [<<Const1>>,<<Const0>>,<<Arg>>] + /// CHECK-DAG: <<Cond:z\d+>> Equal [<<NotArg>>,<<Const2>>] + /// CHECK-DAG: <<NotCond:i\d+>> Select [<<Const1>>,<<Const0>>,<<Cond>>] + /// CHECK-DAG: Return [<<NotCond>>] /// CHECK-START: boolean Main.EqualBoolVsIntConst(boolean) instruction_simplifier_after_bce (after) - /// CHECK-DAG: <<False:i\d+>> IntConstant 0 - /// CHECK-DAG: Return [<<False>>] + /// CHECK-DAG: <<True:i\d+>> IntConstant 1 + /// CHECK-DAG: Return [<<True>>] public static boolean EqualBoolVsIntConst(boolean arg) { - return (arg ? 0 : 1) == 2; + return (arg ? 0 : 1) != 2; } /// CHECK-START: boolean Main.NotEqualBoolVsIntConst(boolean) instruction_simplifier_after_bce (before) /// CHECK-DAG: <<Arg:z\d+>> ParameterValue + /// CHECK-DAG: <<Const0:i\d+>> IntConstant 0 + /// CHECK-DAG: <<Const1:i\d+>> IntConstant 1 /// CHECK-DAG: <<Const2:i\d+>> IntConstant 2 - /// CHECK-DAG: <<BoolNot:z\d+>> BooleanNot [<<Arg>>] - /// CHECK-DAG: <<Cond:z\d+>> NotEqual [<<BoolNot>>,<<Const2>>] - /// CHECK-DAG: Return [<<Cond>>] + /// CHECK-DAG: <<NotArg:i\d+>> Select [<<Const1>>,<<Const0>>,<<Arg>>] + /// CHECK-DAG: <<Cond:z\d+>> NotEqual [<<NotArg>>,<<Const2>>] + /// CHECK-DAG: <<NotCond:i\d+>> Select [<<Const1>>,<<Const0>>,<<Cond>>] + /// CHECK-DAG: Return [<<NotCond>>] /// CHECK-START: boolean Main.NotEqualBoolVsIntConst(boolean) instruction_simplifier_after_bce (after) - /// CHECK-DAG: <<True:i\d+>> IntConstant 1 - /// CHECK-DAG: Return [<<True>>] + /// CHECK-DAG: <<False:i\d+>> IntConstant 0 + /// CHECK-DAG: Return [<<False>>] public static boolean NotEqualBoolVsIntConst(boolean arg) { - return (arg ? 0 : 1) != 2; + return (arg ? 0 : 1) == 2; } /* @@ -1113,19 +1171,16 @@ public class Main { /// CHECK-START: boolean Main.NotNotBool(boolean) instruction_simplifier_after_bce (before) /// CHECK-DAG: <<Arg:z\d+>> ParameterValue - /// CHECK-DAG: <<NotArg:z\d+>> BooleanNot [<<Arg>>] - /// CHECK-DAG: <<NotNotArg:z\d+>> BooleanNot [<<NotArg>>] + /// CHECK-DAG: <<Const0:i\d+>> IntConstant 0 + /// CHECK-DAG: <<Const1:i\d+>> IntConstant 1 + /// CHECK-DAG: <<NotArg:i\d+>> Select [<<Const1>>,<<Const0>>,<<Arg>>] + /// CHECK-DAG: <<NotNotArg:i\d+>> Select [<<Const1>>,<<Const0>>,<<NotArg>>] /// CHECK-DAG: Return [<<NotNotArg>>] /// CHECK-START: boolean Main.NotNotBool(boolean) instruction_simplifier_after_bce (after) /// CHECK-DAG: <<Arg:z\d+>> ParameterValue - /// CHECK-DAG: BooleanNot [<<Arg>>] /// CHECK-DAG: Return [<<Arg>>] - /// CHECK-START: boolean Main.NotNotBool(boolean) instruction_simplifier_after_bce (after) - /// CHECK: BooleanNot - /// CHECK-NOT: BooleanNot - public static boolean NegateValue(boolean arg) { return !arg; } @@ -1254,8 +1309,14 @@ public class Main { /// CHECK-START: int Main.booleanFieldNotEqualOne() instruction_simplifier (after) /// CHECK-DAG: <<Field:z\d+>> StaticFieldGet - /// CHECK-DAG: <<Not:z\d+>> BooleanNot [<<Field>>] - /// CHECK-DAG: If [<<Not>>] + /// CHECK-DAG: If [<<Field>>] + + /// CHECK-START: int Main.booleanFieldNotEqualOne() instruction_simplifier_before_codegen (after) + /// CHECK-DAG: <<Field:z\d+>> StaticFieldGet + /// CHECK-DAG: <<Const13:i\d+>> IntConstant 13 + /// CHECK-DAG: <<Const54:i\d+>> IntConstant 54 + /// CHECK-DAG: <<Select:i\d+>> Select [<<Const54>>,<<Const13>>,<<Field>>] + /// CHECK-DAG: Return [<<Select>>] public static int booleanFieldNotEqualOne() { return (booleanField == true) ? 13 : 54; @@ -1269,8 +1330,14 @@ public class Main { /// CHECK-START: int Main.booleanFieldEqualZero() instruction_simplifier (after) /// CHECK-DAG: <<Field:z\d+>> StaticFieldGet - /// CHECK-DAG: <<Not:z\d+>> BooleanNot [<<Field>>] - /// CHECK-DAG: If [<<Not>>] + /// CHECK-DAG: If [<<Field>>] + + /// CHECK-START: int Main.booleanFieldEqualZero() instruction_simplifier_before_codegen (after) + /// CHECK-DAG: <<Field:z\d+>> StaticFieldGet + /// CHECK-DAG: <<Const13:i\d+>> IntConstant 13 + /// CHECK-DAG: <<Const54:i\d+>> IntConstant 54 + /// CHECK-DAG: <<Select:i\d+>> Select [<<Const54>>,<<Const13>>,<<Field>>] + /// CHECK-DAG: Return [<<Select>>] public static int booleanFieldEqualZero() { return (booleanField != false) ? 13 : 54; @@ -1278,18 +1345,27 @@ public class Main { /// CHECK-START: int Main.intConditionNotEqualOne(int) instruction_simplifier_after_bce (before) /// CHECK-DAG: <<Arg:i\d+>> ParameterValue + /// CHECK-DAG: <<Const0:i\d+>> IntConstant 0 /// CHECK-DAG: <<Const1:i\d+>> IntConstant 1 + /// CHECK-DAG: <<Const13:i\d+>> IntConstant 13 /// CHECK-DAG: <<Const42:i\d+>> IntConstant 42 - /// CHECK-DAG: <<GT:z\d+>> GreaterThan [<<Arg>>,<<Const42>>] + /// CHECK-DAG: <<Const54:i\d+>> IntConstant 54 + /// CHECK-DAG: <<LE:z\d+>> LessThanOrEqual [<<Arg>>,<<Const42>>] + /// CHECK-DAG: <<GT:i\d+>> Select [<<Const1>>,<<Const0>>,<<LE>>] /// CHECK-DAG: <<NE:z\d+>> NotEqual [<<GT>>,<<Const1>>] - /// CHECK-DAG: If [<<NE>>] + /// CHECK-DAG: <<Result:i\d+>> Select [<<Const13>>,<<Const54>>,<<NE>>] + /// CHECK-DAG: Return [<<Result>>] /// CHECK-START: int Main.intConditionNotEqualOne(int) instruction_simplifier_after_bce (after) /// CHECK-DAG: <<Arg:i\d+>> ParameterValue + /// CHECK-DAG: <<Const13:i\d+>> IntConstant 13 /// CHECK-DAG: <<Const42:i\d+>> IntConstant 42 - /// CHECK-DAG: If [<<LE:z\d+>>] + /// CHECK-DAG: <<Const54:i\d+>> IntConstant 54 + /// CHECK-DAG: <<Result:i\d+>> Select [<<Const13>>,<<Const54>>,<<LE:z\d+>>] /// CHECK-DAG: <<LE>> LessThanOrEqual [<<Arg>>,<<Const42>>] - // Note that we match `LE` from If because there are two identical LessThanOrEqual instructions. + /// CHECK-DAG: Return [<<Result>>] + // Note that we match `LE` from Select because there are two identical + // LessThanOrEqual instructions. public static int intConditionNotEqualOne(int i) { return ((i > 42) == true) ? 13 : 54; @@ -1298,17 +1374,26 @@ public class Main { /// CHECK-START: int Main.intConditionEqualZero(int) instruction_simplifier_after_bce (before) /// CHECK-DAG: <<Arg:i\d+>> ParameterValue /// CHECK-DAG: <<Const0:i\d+>> IntConstant 0 + /// CHECK-DAG: <<Const1:i\d+>> IntConstant 1 + /// CHECK-DAG: <<Const13:i\d+>> IntConstant 13 /// CHECK-DAG: <<Const42:i\d+>> IntConstant 42 - /// CHECK-DAG: <<GT:z\d+>> GreaterThan [<<Arg>>,<<Const42>>] - /// CHECK-DAG: <<EQ:z\d+>> Equal [<<GT>>,<<Const0>>] - /// CHECK-DAG: If [<<EQ>>] + /// CHECK-DAG: <<Const54:i\d+>> IntConstant 54 + /// CHECK-DAG: <<LE:z\d+>> LessThanOrEqual [<<Arg>>,<<Const42>>] + /// CHECK-DAG: <<GT:i\d+>> Select [<<Const1>>,<<Const0>>,<<LE>>] + /// CHECK-DAG: <<NE:z\d+>> Equal [<<GT>>,<<Const0>>] + /// CHECK-DAG: <<Result:i\d+>> Select [<<Const13>>,<<Const54>>,<<NE>>] + /// CHECK-DAG: Return [<<Result>>] /// CHECK-START: int Main.intConditionEqualZero(int) instruction_simplifier_after_bce (after) /// CHECK-DAG: <<Arg:i\d+>> ParameterValue + /// CHECK-DAG: <<Const13:i\d+>> IntConstant 13 /// CHECK-DAG: <<Const42:i\d+>> IntConstant 42 - /// CHECK-DAG: If [<<LE:z\d+>>] + /// CHECK-DAG: <<Const54:i\d+>> IntConstant 54 + /// CHECK-DAG: <<Result:i\d+>> Select [<<Const13>>,<<Const54>>,<<LE:z\d+>>] /// CHECK-DAG: <<LE>> LessThanOrEqual [<<Arg>>,<<Const42>>] - // Note that we match `LE` from If because there are two identical LessThanOrEqual instructions. + /// CHECK-DAG: Return [<<Result>>] + // Note that we match `LE` from Select because there are two identical + // LessThanOrEqual instructions. public static int intConditionEqualZero(int i) { return ((i > 42) != false) ? 13 : 54; @@ -1316,17 +1401,33 @@ public class Main { // Test that conditions on float/double are not flipped. + /// CHECK-START: int Main.floatConditionNotEqualOne(float) ssa_builder (after) + /// CHECK: LessThanOrEqual + /// CHECK-START: int Main.floatConditionNotEqualOne(float) register (before) - /// CHECK-DAG: <<Const1:i\d+>> IntConstant 1 - /// CHECK-DAG: NotEqual [{{i\d+}},<<Const1>>] + /// CHECK-DAG: <<Arg:f\d+>> ParameterValue + /// CHECK-DAG: <<Const13:i\d+>> IntConstant 13 + /// CHECK-DAG: <<Const54:i\d+>> IntConstant 54 + /// CHECK-DAG: <<Const42:f\d+>> FloatConstant 42 + /// CHECK-DAG: <<LE:z\d+>> LessThanOrEqual [<<Arg>>,<<Const42>>] + /// CHECK-DAG: <<Select:i\d+>> Select [<<Const13>>,<<Const54>>,<<LE>>] + /// CHECK-DAG: Return [<<Select>>] public static int floatConditionNotEqualOne(float f) { return ((f > 42.0f) == true) ? 13 : 54; } + /// CHECK-START: int Main.doubleConditionEqualZero(double) ssa_builder (after) + /// CHECK: LessThanOrEqual + /// CHECK-START: int Main.doubleConditionEqualZero(double) register (before) - /// CHECK-DAG: <<Const0:i\d+>> IntConstant 0 - /// CHECK-DAG: Equal [{{i\d+}},<<Const0>>] + /// CHECK-DAG: <<Arg:d\d+>> ParameterValue + /// CHECK-DAG: <<Const13:i\d+>> IntConstant 13 + /// CHECK-DAG: <<Const54:i\d+>> IntConstant 54 + /// CHECK-DAG: <<Const42:d\d+>> DoubleConstant 42 + /// CHECK-DAG: <<LE:z\d+>> LessThanOrEqual [<<Arg>>,<<Const42>>] + /// CHECK-DAG: <<Select:i\d+>> Select [<<Const13>>,<<Const54>>,<<LE>>] + /// CHECK-DAG: Return [<<Select>>] public static int doubleConditionEqualZero(double d) { return ((d > 42.0) != false) ? 13 : 54; @@ -1374,6 +1475,10 @@ public class Main { assertIntEquals(NotEqualTrueLhs(true), 3); assertIntEquals(NotEqualFalseRhs(true), 5); assertIntEquals(NotEqualFalseLhs(true), 5); + assertBooleanEquals(EqualBoolVsIntConst(true), true); + assertBooleanEquals(EqualBoolVsIntConst(true), true); + assertBooleanEquals(NotEqualBoolVsIntConst(false), false); + assertBooleanEquals(NotEqualBoolVsIntConst(false), false); assertBooleanEquals(NotNotBool(true), true); assertBooleanEquals(NotNotBool(false), false); assertFloatEquals(Div2(100.0f), 50.0f); diff --git a/test/463-checker-boolean-simplifier/src/Main.java b/test/463-checker-boolean-simplifier/src/Main.java index 61510d80e2..682f12641f 100644 --- a/test/463-checker-boolean-simplifier/src/Main.java +++ b/test/463-checker-boolean-simplifier/src/Main.java @@ -37,7 +37,7 @@ public class Main { * empty branches removed. */ - /// CHECK-START: boolean Main.BooleanNot(boolean) boolean_simplifier (before) + /// CHECK-START: boolean Main.BooleanNot(boolean) select_generator (before) /// CHECK-DAG: <<Param:z\d+>> ParameterValue /// CHECK-DAG: <<Const0:i\d+>> IntConstant 0 /// CHECK-DAG: <<Const1:i\d+>> IntConstant 1 @@ -45,23 +45,24 @@ public class Main { /// CHECK-DAG: <<Phi:i\d+>> Phi [<<Const1>>,<<Const0>>] /// CHECK-DAG: Return [<<Phi>>] - /// CHECK-START: boolean Main.BooleanNot(boolean) boolean_simplifier (before) + /// CHECK-START: boolean Main.BooleanNot(boolean) select_generator (before) /// CHECK: Goto /// CHECK: Goto /// CHECK: Goto /// CHECK-NOT: Goto - /// CHECK-START: boolean Main.BooleanNot(boolean) boolean_simplifier (after) + /// CHECK-START: boolean Main.BooleanNot(boolean) select_generator (after) /// CHECK-DAG: <<Param:z\d+>> ParameterValue /// CHECK-DAG: <<Const0:i\d+>> IntConstant 0 - /// CHECK-DAG: <<NotParam:z\d+>> BooleanNot [<<Param>>] + /// CHECK-DAG: <<Const1:i\d+>> IntConstant 1 + /// CHECK-DAG: <<NotParam:i\d+>> Select [<<Const1>>,<<Const0>>,<<Param>>] /// CHECK-DAG: Return [<<NotParam>>] - /// CHECK-START: boolean Main.BooleanNot(boolean) boolean_simplifier (after) + /// CHECK-START: boolean Main.BooleanNot(boolean) select_generator (after) /// CHECK-NOT: If /// CHECK-NOT: Phi - /// CHECK-START: boolean Main.BooleanNot(boolean) boolean_simplifier (after) + /// CHECK-START: boolean Main.BooleanNot(boolean) select_generator (after) /// CHECK: Goto /// CHECK-NOT: Goto @@ -74,7 +75,7 @@ public class Main { * and 0 when False. */ - /// CHECK-START: boolean Main.GreaterThan(int, int) boolean_simplifier (before) + /// CHECK-START: boolean Main.GreaterThan(int, int) select_generator (before) /// CHECK-DAG: <<ParamX:i\d+>> ParameterValue /// CHECK-DAG: <<ParamY:i\d+>> ParameterValue /// CHECK-DAG: <<Const0:i\d+>> IntConstant 0 @@ -84,13 +85,14 @@ public class Main { /// CHECK-DAG: <<Phi:i\d+>> Phi [<<Const0>>,<<Const1>>] /// CHECK-DAG: Return [<<Phi>>] - /// CHECK-START: boolean Main.GreaterThan(int, int) boolean_simplifier (after) + /// CHECK-START: boolean Main.GreaterThan(int, int) select_generator (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>>] + /// CHECK-DAG: <<Select:i\d+>> Select [<<Const0>>,<<Const1>>,<<Cond>>] + /// CHECK-DAG: Return [<<Select>>] public static boolean GreaterThan(int x, int y) { return (x <= y) ? false : true; @@ -101,7 +103,7 @@ public class Main { * and 1 when False. */ - /// CHECK-START: boolean Main.LessThan(int, int) boolean_simplifier (before) + /// CHECK-START: boolean Main.LessThan(int, int) select_generator (before) /// CHECK-DAG: <<ParamX:i\d+>> ParameterValue /// CHECK-DAG: <<ParamY:i\d+>> ParameterValue /// CHECK-DAG: <<Const0:i\d+>> IntConstant 0 @@ -111,13 +113,14 @@ public class Main { /// CHECK-DAG: <<Phi:i\d+>> Phi [<<Const1>>,<<Const0>>] /// CHECK-DAG: Return [<<Phi>>] - /// CHECK-START: boolean Main.LessThan(int, int) boolean_simplifier (after) + /// CHECK-START: boolean Main.LessThan(int, int) select_generator (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>>] + /// CHECK-DAG: <<Cond:z\d+>> GreaterThanOrEqual [<<ParamX>>,<<ParamY>>] + /// CHECK-DAG: <<Select:i\d+>> Select [<<Const1>>,<<Const0>>,<<Cond>>] + /// CHECK-DAG: Return [<<Select>>] public static boolean LessThan(int x, int y) { return (x < y) ? true : false; @@ -128,7 +131,7 @@ public class Main { * Note that Phis are discovered retrospectively. */ - /// CHECK-START: boolean Main.ValuesOrdered(int, int, int) boolean_simplifier (before) + /// CHECK-START: boolean Main.ValuesOrdered(int, int, int) select_generator (before) /// CHECK-DAG: <<ParamX:i\d+>> ParameterValue /// CHECK-DAG: <<ParamY:i\d+>> ParameterValue /// CHECK-DAG: <<ParamZ:i\d+>> ParameterValue @@ -145,39 +148,40 @@ public class Main { /// CHECK-DAG: <<PhiYZ>> Phi [<<Const1>>,<<Const0>>] /// CHECK-DAG: <<PhiXYZ>> Phi [<<Const1>>,<<Const0>>] - /// CHECK-START: boolean Main.ValuesOrdered(int, int, int) boolean_simplifier (after) + /// CHECK-START: boolean Main.ValuesOrdered(int, int, int) select_generator (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>>] + /// CHECK-DAG: <<Const0:i\d+>> IntConstant 0 + /// CHECK-DAG: <<Const1:i\d+>> IntConstant 1 + /// CHECK-DAG: <<CmpXY:z\d+>> GreaterThan [<<ParamX>>,<<ParamY>>] + /// CHECK-DAG: <<SelXY:i\d+>> Select [<<Const1>>,<<Const0>>,<<CmpXY>>] + /// CHECK-DAG: <<CmpYZ:z\d+>> GreaterThan [<<ParamY>>,<<ParamZ>>] + /// CHECK-DAG: <<SelYZ:i\d+>> Select [<<Const1>>,<<Const0>>,<<CmpYZ>>] + /// CHECK-DAG: <<CmpXYZ:z\d+>> NotEqual [<<SelXY>>,<<SelYZ>>] + /// CHECK-DAG: <<SelXYZ:i\d+>> Select [<<Const1>>,<<Const0>>,<<CmpXYZ>>] + /// CHECK-DAG: Return [<<SelXYZ>>] public static boolean ValuesOrdered(int x, int y, int z) { return (x <= y) == (y <= z); } - /// CHECK-START: int Main.NegatedCondition(boolean) boolean_simplifier (before) + /// CHECK-START: int Main.NegatedCondition(boolean) select_generator (before) /// CHECK-DAG: <<Param:z\d+>> ParameterValue /// CHECK-DAG: <<Const42:i\d+>> IntConstant 42 /// CHECK-DAG: <<Const43:i\d+>> IntConstant 43 - /// CHECK-DAG: <<NotParam:z\d+>> BooleanNot [<<Param>>] - /// CHECK-DAG: If [<<NotParam>>] + /// CHECK-DAG: If [<<Param>>] /// CHECK-DAG: <<Phi:i\d+>> Phi [<<Const42>>,<<Const43>>] /// CHECK-DAG: Return [<<Phi>>] - /// CHECK-START: int Main.NegatedCondition(boolean) boolean_simplifier (after) + /// CHECK-START: int Main.NegatedCondition(boolean) select_generator (after) /// CHECK-DAG: <<Param:z\d+>> ParameterValue /// CHECK-DAG: <<Const42:i\d+>> IntConstant 42 /// CHECK-DAG: <<Const43:i\d+>> IntConstant 43 - /// CHECK-DAG: If [<<Param>>] - /// CHECK-DAG: <<Phi:i\d+>> Phi [<<Const42>>,<<Const43>>] - /// CHECK-DAG: Return [<<Phi>>] - - // Note: The fact that branches are swapped is verified by running the test. + /// CHECK-DAG: <<Select:i\d+>> Select [<<Const43>>,<<Const42>>,<<Param>>] + /// CHECK-DAG: Return [<<Select>>] - /// CHECK-START: int Main.NegatedCondition(boolean) boolean_simplifier (after) + /// CHECK-START: int Main.NegatedCondition(boolean) select_generator (after) /// CHECK-NOT: BooleanNot public static int NegatedCondition(boolean x) { @@ -188,6 +192,179 @@ public class Main { } } + /// CHECK-START: int Main.SimpleTrueBlock(boolean, int) select_generator (after) + /// CHECK-DAG: <<ParamX:z\d+>> ParameterValue + /// CHECK-DAG: <<ParamY:i\d+>> ParameterValue + /// CHECK-DAG: <<Const42:i\d+>> IntConstant 42 + /// CHECK-DAG: <<Const43:i\d+>> IntConstant 43 + /// CHECK-DAG: <<Add:i\d+>> Add [<<ParamY>>,<<Const42>>] + /// CHECK-DAG: <<Select:i\d+>> Select [<<Const43>>,<<Add>>,<<ParamX>>] + /// CHECK-DAG: Return [<<Select>>] + + /// CHECK-START: int Main.SimpleTrueBlock(boolean, int) select_generator (after) + /// CHECK-NOT: If + + public static int SimpleTrueBlock(boolean x, int y) { + return x ? y + 42 : 43; + } + + /// CHECK-START: int Main.SimpleFalseBlock(boolean, int) select_generator (after) + /// CHECK-DAG: <<ParamX:z\d+>> ParameterValue + /// CHECK-DAG: <<ParamY:i\d+>> ParameterValue + /// CHECK-DAG: <<Const42:i\d+>> IntConstant 42 + /// CHECK-DAG: <<Const43:i\d+>> IntConstant 43 + /// CHECK-DAG: <<Add:i\d+>> Add [<<ParamY>>,<<Const43>>] + /// CHECK-DAG: <<Select:i\d+>> Select [<<Add>>,<<Const42>>,<<ParamX>>] + /// CHECK-DAG: Return [<<Select>>] + + /// CHECK-START: int Main.SimpleFalseBlock(boolean, int) select_generator (after) + /// CHECK-NOT: If + + public static int SimpleFalseBlock(boolean x, int y) { + return x ? 42 : y + 43; + } + + /// CHECK-START: int Main.SimpleBothBlocks(boolean, int, int) select_generator (after) + /// CHECK-DAG: <<ParamX:z\d+>> ParameterValue + /// CHECK-DAG: <<ParamY:i\d+>> ParameterValue + /// CHECK-DAG: <<ParamZ:i\d+>> ParameterValue + /// CHECK-DAG: <<Const42:i\d+>> IntConstant 42 + /// CHECK-DAG: <<Const43:i\d+>> IntConstant 43 + /// CHECK-DAG: <<AddTrue:i\d+>> Add [<<ParamY>>,<<Const42>>] + /// CHECK-DAG: <<AddFalse:i\d+>> Add [<<ParamZ>>,<<Const43>>] + /// CHECK-DAG: <<Select:i\d+>> Select [<<AddFalse>>,<<AddTrue>>,<<ParamX>>] + /// CHECK-DAG: Return [<<Select>>] + + /// CHECK-START: int Main.SimpleBothBlocks(boolean, int, int) select_generator (after) + /// CHECK-NOT: If + + public static int SimpleBothBlocks(boolean x, int y, int z) { + return x ? y + 42 : z + 43; + } + + /// CHECK-START: int Main.ThreeBlocks(boolean, boolean) select_generator (after) + /// CHECK-DAG: <<ParamX:z\d+>> ParameterValue + /// CHECK-DAG: <<ParamY:z\d+>> ParameterValue + /// CHECK-DAG: <<Const1:i\d+>> IntConstant 1 + /// CHECK-DAG: <<Const2:i\d+>> IntConstant 2 + /// CHECK-DAG: <<Const3:i\d+>> IntConstant 3 + /// CHECK-DAG: <<Select23:i\d+>> Select [<<Const3>>,<<Const2>>,<<ParamY>>] + /// CHECK-DAG: <<Select123:i\d+>> Select [<<Select23>>,<<Const1>>,<<ParamX>>] + /// CHECK-DAG: Return [<<Select123>>] + + public static int ThreeBlocks(boolean x, boolean y) { + if (x) { + return 1; + } else if (y) { + return 2; + } else { + return 3; + } + } + + /// CHECK-START: int Main.MultiplePhis() select_generator (before) + /// CHECK-DAG: <<Const0:i\d+>> IntConstant 0 + /// CHECK-DAG: <<Const1:i\d+>> IntConstant 1 + /// CHECK-DAG: <<Const13:i\d+>> IntConstant 13 + /// CHECK-DAG: <<Const42:i\d+>> IntConstant 42 + /// CHECK-DAG: <<PhiX:i\d+>> Phi [<<Const0>>,<<Const13>>,<<Const42>>] + /// CHECK-DAG: <<PhiY:i\d+>> Phi [<<Const1>>,<<Add:i\d+>>,<<Add>>] + /// CHECK-DAG: <<Add>> Add [<<PhiY>>,<<Const1>>] + /// CHECK-DAG: <<Cond:z\d+>> LessThanOrEqual [<<Add>>,<<Const1>>] + /// CHECK-DAG: If [<<Cond>>] + /// CHECK-DAG: Return [<<PhiX>>] + + /// CHECK-START: int Main.MultiplePhis() select_generator (after) + /// CHECK-DAG: <<Const0:i\d+>> IntConstant 0 + /// CHECK-DAG: <<Const1:i\d+>> IntConstant 1 + /// CHECK-DAG: <<Const13:i\d+>> IntConstant 13 + /// CHECK-DAG: <<Const42:i\d+>> IntConstant 42 + /// CHECK-DAG: <<PhiX:i\d+>> Phi [<<Const0>>,<<Select:i\d+>>] + /// CHECK-DAG: <<PhiY:i\d+>> Phi [<<Const1>>,<<Add:i\d+>>] + /// CHECK-DAG: <<Add>> Add [<<PhiY>>,<<Const1>>] + /// CHECK-DAG: <<Cond:z\d+>> LessThanOrEqual [<<Add>>,<<Const1>>] + /// CHECK-DAG: <<Select>> Select [<<Const13>>,<<Const42>>,<<Cond>>] + /// CHECK-DAG: Return [<<PhiX>>] + + public static int MultiplePhis() { + int x = 0; + int y = 1; + while (y++ < 10) { + if (y > 1) { + x = 13; + } else { + x = 42; + } + } + return x; + } + + /// CHECK-START: int Main.TrueBlockWithTooManyInstructions(boolean) select_generator (before) + /// CHECK-DAG: <<This:l\d+>> ParameterValue + /// CHECK-DAG: <<Cond:z\d+>> ParameterValue + /// CHECK-DAG: <<Const2:i\d+>> IntConstant 2 + /// CHECK-DAG: <<Const43:i\d+>> IntConstant 43 + /// CHECK-DAG: If [<<Cond>>] + /// CHECK-DAG: <<Iget:i\d+>> InstanceFieldGet [<<This>>] + /// CHECK-DAG: <<Add:i\d+>> Add [<<Iget>>,<<Const2>>] + /// CHECK-DAG: Phi [<<Add>>,<<Const43>>] + + /// CHECK-START: int Main.TrueBlockWithTooManyInstructions(boolean) select_generator (after) + /// CHECK-NOT: Select + + public int TrueBlockWithTooManyInstructions(boolean x) { + return x ? (read_field + 2) : 43; + } + + /// CHECK-START: int Main.FalseBlockWithTooManyInstructions(boolean) select_generator (before) + /// CHECK-DAG: <<This:l\d+>> ParameterValue + /// CHECK-DAG: <<Cond:z\d+>> ParameterValue + /// CHECK-DAG: <<Const3:i\d+>> IntConstant 3 + /// CHECK-DAG: <<Const42:i\d+>> IntConstant 42 + /// CHECK-DAG: If [<<Cond>>] + /// CHECK-DAG: <<Iget:i\d+>> InstanceFieldGet [<<This>>] + /// CHECK-DAG: <<Add:i\d+>> Add [<<Iget>>,<<Const3>>] + /// CHECK-DAG: Phi [<<Const42>>,<<Add>>] + + /// CHECK-START: int Main.FalseBlockWithTooManyInstructions(boolean) select_generator (after) + /// CHECK-NOT: Select + + public int FalseBlockWithTooManyInstructions(boolean x) { + return x ? 42 : (read_field + 3); + } + + /// CHECK-START: int Main.TrueBlockWithSideEffects(boolean) select_generator (before) + /// CHECK-DAG: <<This:l\d+>> ParameterValue + /// CHECK-DAG: <<Cond:z\d+>> ParameterValue + /// CHECK-DAG: <<Const42:i\d+>> IntConstant 42 + /// CHECK-DAG: <<Const43:i\d+>> IntConstant 43 + /// CHECK-DAG: If [<<Cond>>] + /// CHECK-DAG: InstanceFieldSet [<<This>>,<<Const42>>] + /// CHECK-DAG: Phi [<<Const42>>,<<Const43>>] + + /// CHECK-START: int Main.TrueBlockWithSideEffects(boolean) select_generator (after) + /// CHECK-NOT: Select + + public int TrueBlockWithSideEffects(boolean x) { + return x ? (write_field = 42) : 43; + } + + /// CHECK-START: int Main.FalseBlockWithSideEffects(boolean) select_generator (before) + /// CHECK-DAG: <<This:l\d+>> ParameterValue + /// CHECK-DAG: <<Cond:z\d+>> ParameterValue + /// CHECK-DAG: <<Const42:i\d+>> IntConstant 42 + /// CHECK-DAG: <<Const43:i\d+>> IntConstant 43 + /// CHECK-DAG: If [<<Cond>>] + /// CHECK-DAG: InstanceFieldSet [<<This>>,<<Const43>>] + /// CHECK-DAG: Phi [<<Const42>>,<<Const43>>] + + /// CHECK-START: int Main.FalseBlockWithSideEffects(boolean) select_generator (after) + /// CHECK-NOT: Select + + public int FalseBlockWithSideEffects(boolean x) { + return x ? 42 : (write_field = 43); + } + public static void main(String[] args) { assertBoolEquals(false, BooleanNot(true)); assertBoolEquals(true, BooleanNot(false)); @@ -206,5 +383,30 @@ public class Main { assertBoolEquals(false, ValuesOrdered(5, 5, 3)); assertIntEquals(42, NegatedCondition(true)); assertIntEquals(43, NegatedCondition(false)); + assertIntEquals(46, SimpleTrueBlock(true, 4)); + assertIntEquals(43, SimpleTrueBlock(false, 4)); + assertIntEquals(42, SimpleFalseBlock(true, 7)); + assertIntEquals(50, SimpleFalseBlock(false, 7)); + assertIntEquals(48, SimpleBothBlocks(true, 6, 2)); + assertIntEquals(45, SimpleBothBlocks(false, 6, 2)); + assertIntEquals(1, ThreeBlocks(true, true)); + assertIntEquals(1, ThreeBlocks(true, false)); + assertIntEquals(2, ThreeBlocks(false, true)); + assertIntEquals(3, ThreeBlocks(false, false)); + assertIntEquals(13, MultiplePhis()); + + Main m = new Main(); + assertIntEquals(42, m.TrueBlockWithTooManyInstructions(true)); + assertIntEquals(43, m.TrueBlockWithTooManyInstructions(false)); + assertIntEquals(42, m.FalseBlockWithTooManyInstructions(true)); + assertIntEquals(43, m.FalseBlockWithTooManyInstructions(false)); + assertIntEquals(42, m.TrueBlockWithSideEffects(true)); + assertIntEquals(43, m.TrueBlockWithSideEffects(false)); + assertIntEquals(42, m.FalseBlockWithSideEffects(true)); + assertIntEquals(43, m.FalseBlockWithSideEffects(false)); } + + // These need to be instance fields so as to not generate a LoadClass for iget/iput. + public int read_field = 40; + public int write_field = 42; } diff --git a/test/468-checker-bool-simplifier-regression/smali/TestCase.smali b/test/468-checker-bool-simplifier-regression/smali/TestCase.smali index da1c5ec802..87ad21ead4 100644 --- a/test/468-checker-bool-simplifier-regression/smali/TestCase.smali +++ b/test/468-checker-bool-simplifier-regression/smali/TestCase.smali @@ -18,7 +18,7 @@ .field public static value:Z -## CHECK-START: boolean TestCase.testCase() boolean_simplifier (before) +## CHECK-START: boolean TestCase.testCase() select_generator (before) ## CHECK-DAG: <<Const0:i\d+>> IntConstant 0 ## CHECK-DAG: <<Const1:i\d+>> IntConstant 1 ## CHECK-DAG: <<Value:z\d+>> StaticFieldGet @@ -26,10 +26,12 @@ ## CHECK-DAG: <<Phi:i\d+>> Phi [<<Const1>>,<<Const0>>] ## CHECK-DAG: Return [<<Phi>>] -## CHECK-START: boolean TestCase.testCase() boolean_simplifier (after) +## CHECK-START: boolean TestCase.testCase() select_generator (after) +## CHECK-DAG: <<Const0:i\d+>> IntConstant 0 +## CHECK-DAG: <<Const1:i\d+>> IntConstant 1 ## CHECK-DAG: <<Value:z\d+>> StaticFieldGet -## CHECK-DAG: <<Not:z\d+>> BooleanNot [<<Value>>] -## CHECK-DAG: Return [<<Not>>] +## CHECK-DAG: <<Select:i\d+>> Select [<<Const1>>,<<Const0>>,<<Value>>] +## CHECK-DAG: Return [<<Select>>] .method public static testCase()Z .registers 2 diff --git a/test/474-checker-boolean-input/src/Main.java b/test/474-checker-boolean-input/src/Main.java index a2b219dd6d..fbc28d8d52 100644 --- a/test/474-checker-boolean-input/src/Main.java +++ b/test/474-checker-boolean-input/src/Main.java @@ -27,9 +27,9 @@ public class Main { * we implement a suitable type analysis. */ - /// CHECK-START: boolean Main.TestPhiAsBoolean(int) boolean_simplifier (after) + /// CHECK-START: boolean Main.TestPhiAsBoolean(int) select_generator (after) /// CHECK-DAG: <<Phi:i\d+>> Phi - /// CHECK-DAG: BooleanNot [<<Phi>>] + /// CHECK-DAG: Select [{{i\d+}},{{i\d+}},<<Phi>>] public static boolean f1; public static boolean f2; @@ -47,9 +47,9 @@ public class Main { * we implement a suitable type analysis. */ - /// CHECK-START: boolean Main.TestAndAsBoolean(boolean, boolean) boolean_simplifier (after) + /// CHECK-START: boolean Main.TestAndAsBoolean(boolean, boolean) select_generator (after) /// CHECK-DAG: <<And:i\d+>> And - /// CHECK-DAG: BooleanNot [<<And>>] + /// CHECK-DAG: Select [{{i\d+}},{{i\d+}},<<And>>] public static boolean InlineAnd(boolean x, boolean y) { return x & y; @@ -64,9 +64,9 @@ public class Main { * we implement a suitable type analysis. */ - /// CHECK-START: boolean Main.TestOrAsBoolean(boolean, boolean) boolean_simplifier (after) + /// CHECK-START: boolean Main.TestOrAsBoolean(boolean, boolean) select_generator (after) /// CHECK-DAG: <<Or:i\d+>> Or - /// CHECK-DAG: BooleanNot [<<Or>>] + /// CHECK-DAG: Select [{{i\d+}},{{i\d+}},<<Or>>] public static boolean InlineOr(boolean x, boolean y) { return x | y; @@ -81,9 +81,9 @@ public class Main { * we implement a suitable type analysis. */ - /// CHECK-START: boolean Main.TestXorAsBoolean(boolean, boolean) boolean_simplifier (after) + /// CHECK-START: boolean Main.TestXorAsBoolean(boolean, boolean) select_generator (after) /// CHECK-DAG: <<Xor:i\d+>> Xor - /// CHECK-DAG: BooleanNot [<<Xor>>] + /// CHECK-DAG: Select [{{i\d+}},{{i\d+}},<<Xor>>] public static boolean InlineXor(boolean x, boolean y) { return x ^ y; diff --git a/test/480-checker-dead-blocks/src/Main.java b/test/480-checker-dead-blocks/src/Main.java index 5adafaf10d..e5171f045f 100644 --- a/test/480-checker-dead-blocks/src/Main.java +++ b/test/480-checker-dead-blocks/src/Main.java @@ -56,6 +56,8 @@ public class Main { z = x + y; } else { z = x - y; + // Prevent HSelect simplification by having a branch with multiple instructions. + System.nanoTime(); } return z; } @@ -86,6 +88,8 @@ public class Main { z = x + y; } else { z = x - y; + // Prevent HSelect simplification by having a branch with multiple instructions. + System.nanoTime(); } return z; } diff --git a/test/485-checker-dce-loop-update/smali/TestCase.smali b/test/485-checker-dce-loop-update/smali/TestCase.smali index 1de0baeabd..056f22c71c 100644 --- a/test/485-checker-dce-loop-update/smali/TestCase.smali +++ b/test/485-checker-dce-loop-update/smali/TestCase.smali @@ -137,15 +137,14 @@ ## CHECK-DAG: <<Cst5:i\d+>> IntConstant 5 ## CHECK-DAG: <<Cst7:i\d+>> IntConstant 7 ## CHECK-DAG: <<Cst11:i\d+>> IntConstant 11 -## CHECK-DAG: <<PhiX1:i\d+>> Phi [<<ArgX>>,<<Add5:i\d+>>,<<Add7:i\d+>>] loop:<<HeaderY:B\d+>> +## CHECK-DAG: <<PhiX:i\d+>> Phi [<<ArgX>>,<<Add5:i\d+>>,<<Add7:i\d+>>] loop:<<HeaderY:B\d+>> ## CHECK-DAG: If [<<ArgY>>] loop:<<HeaderY>> -## CHECK-DAG: If [<<ArgZ>>] loop:<<HeaderY>> -## CHECK-DAG: <<Mul9:i\d+>> Mul [<<PhiX1>>,<<Cst11>>] loop:<<HeaderY>> -## CHECK-DAG: <<PhiX2:i\d+>> Phi [<<PhiX1>>,<<Mul9>>] loop:<<HeaderY>> +## CHECK-DAG: <<Mul9:i\d+>> Mul [<<PhiX>>,<<Cst11>>] loop:<<HeaderY>> +## CHECK-DAG: <<SelX:i\d+>> Select [<<PhiX>>,<<Mul9>>,<<ArgZ>>] loop:<<HeaderY>> ## CHECK-DAG: If [<<Cst1>>] loop:<<HeaderY>> -## CHECK-DAG: <<Add5>> Add [<<PhiX2>>,<<Cst5>>] loop:<<HeaderY>> -## CHECK-DAG: <<Add7>> Add [<<PhiX1>>,<<Cst7>>] loop:<<HeaderY>> -## CHECK-DAG: Return [<<PhiX2>>] loop:none +## CHECK-DAG: <<Add5>> Add [<<SelX>>,<<Cst5>>] loop:<<HeaderY>> +## CHECK-DAG: <<Add7>> Add [<<PhiX>>,<<Cst7>>] loop:<<HeaderY>> +## CHECK-DAG: Return [<<SelX>>] loop:none ## CHECK-START: int TestCase.testExitPredecessors(int, boolean, boolean) dead_code_elimination_final (after) ## CHECK-DAG: <<ArgX:i\d+>> ParameterValue @@ -153,13 +152,12 @@ ## CHECK-DAG: <<ArgZ:z\d+>> ParameterValue ## CHECK-DAG: <<Cst7:i\d+>> IntConstant 7 ## CHECK-DAG: <<Cst11:i\d+>> IntConstant 11 -## CHECK-DAG: <<PhiX1:i\d+>> Phi [<<ArgX>>,<<Add7:i\d+>>] loop:<<HeaderY:B\d+>> +## CHECK-DAG: <<PhiX:i\d+>> Phi [<<ArgX>>,<<Add7:i\d+>>] loop:<<HeaderY:B\d+>> ## CHECK-DAG: If [<<ArgY>>] loop:<<HeaderY>> -## CHECK-DAG: <<Add7>> Add [<<PhiX1>>,<<Cst7>>] loop:<<HeaderY>> -## CHECK-DAG: If [<<ArgZ>>] loop:none -## CHECK-DAG: <<Mul9:i\d+>> Mul [<<PhiX1>>,<<Cst11>>] loop:none -## CHECK-DAG: <<PhiX2:i\d+>> Phi [<<PhiX1>>,<<Mul9>>] loop:none -## CHECK-DAG: Return [<<PhiX2>>] loop:none +## CHECK-DAG: <<Add7>> Add [<<PhiX>>,<<Cst7>>] loop:<<HeaderY>> +## CHECK-DAG: <<Mul9:i\d+>> Mul [<<PhiX>>,<<Cst11>>] loop:none +## CHECK-DAG: <<SelX:i\d+>> Select [<<PhiX>>,<<Mul9>>,<<ArgZ>>] loop:none +## CHECK-DAG: Return [<<SelX>>] loop:none .method public static testExitPredecessors(IZZ)I .registers 4 diff --git a/test/530-checker-lse/src/Main.java b/test/530-checker-lse/src/Main.java index baee7b3973..f87326cc26 100644 --- a/test/530-checker-lse/src/Main.java +++ b/test/530-checker-lse/src/Main.java @@ -595,19 +595,16 @@ public class Main { /// CHECK-DAG: InstanceFieldSet [<<Obj>>,<<True>>] /// CHECK-DAG: InstanceFieldSet [<<Obj>>,<<Float8>>] /// CHECK-DAG: <<GetTest:z\d+>> InstanceFieldGet [<<Obj>>] - /// CHECK-DAG: If [<<GetTest>>] /// CHECK-DAG: <<GetField:f\d+>> InstanceFieldGet [<<Obj>>] - /// CHECK-DAG: <<Phi:f\d+>> Phi [<<Float42>>,<<GetField>>] - /// CHECK-DAG: Return [<<Phi>>] + /// CHECK-DAG: <<Select:f\d+>> Select [<<Float42>>,<<GetField>>,<<GetTest>>] + /// CHECK-DAG: Return [<<Select>>] /// CHECK-START: float Main.test24() load_store_elimination (after) /// CHECK-DAG: <<True:i\d+>> IntConstant 1 /// CHECK-DAG: <<Float8:f\d+>> FloatConstant 8 /// CHECK-DAG: <<Float42:f\d+>> FloatConstant 42 - /// CHECK-DAG: <<Obj:l\d+>> NewInstance - /// CHECK-DAG: If [<<True>>] - /// CHECK-DAG: <<Phi:f\d+>> Phi [<<Float42>>,<<Float8>>] - /// CHECK-DAG: Return [<<Phi>>] + /// CHECK-DAG: <<Select:f\d+>> Select [<<Float42>>,<<Float8>>,<<True>>] + /// CHECK-DAG: Return [<<Select>>] static float test24() { float a = 42.0f; diff --git a/test/543-checker-dce-trycatch/smali/TestCase.smali b/test/543-checker-dce-trycatch/smali/TestCase.smali index 1756fa4a99..62511dfef8 100644 --- a/test/543-checker-dce-trycatch/smali/TestCase.smali +++ b/test/543-checker-dce-trycatch/smali/TestCase.smali @@ -15,6 +15,8 @@ .class public LTestCase; .super Ljava/lang/Object; +.field public static sField:I + .method private static $inline$False()Z .registers 1 const/4 v0, 0x0 @@ -240,24 +242,25 @@ shr-int/2addr p2, p3 :try_start - const v1, 0xa # dead catch phi input, defined in entry block (HInstruction) - add-int v2, p0, p1 # dead catch phi input, defined in the dead block (HInstruction) + const v1, 0xa # dead catch phi input, defined in entry block (HInstruction) + add-int v2, p0, p1 # dead catch phi input, defined in the dead block (HInstruction) move v3, v2 if-eqz v3, :define_phi + sput v3, LTestCase;->sField:I # beat HSelect simplification (has side-effects, does not throw) const v3, 0xf :define_phi - # v3 = Phi [Add, 0xf] # dead catch phi input, defined in the dead block (HPhi) + # v3 = Phi [Add, 0xf] # dead catch phi input, defined in the dead block (HPhi) div-int/2addr p0, v2 :else - const v1, 0xb # live catch phi input - const v2, 0xc # live catch phi input - const v3, 0x10 # live catch phi input + const v1, 0xb # live catch phi input + const v2, 0xc # live catch phi input + const v3, 0x10 # live catch phi input div-int/2addr p0, p3 - const v1, 0xd # live catch phi input - const v2, 0xe # live catch phi input - const v3, 0x11 # live catch phi input + const v1, 0xd # live catch phi input + const v2, 0xe # live catch phi input + const v3, 0x11 # live catch phi input div-int/2addr p0, p1 :try_end .catchall {:try_start .. :try_end} :catch_all diff --git a/test/566-checker-codegen-select/expected.txt b/test/566-checker-codegen-select/expected.txt new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/test/566-checker-codegen-select/expected.txt diff --git a/test/566-checker-codegen-select/info.txt b/test/566-checker-codegen-select/info.txt new file mode 100644 index 0000000000..67b6ceb53f --- /dev/null +++ b/test/566-checker-codegen-select/info.txt @@ -0,0 +1 @@ +Test the use positions of inputs of non-materialized conditions.
\ No newline at end of file diff --git a/test/566-checker-codegen-select/src/Main.java b/test/566-checker-codegen-select/src/Main.java new file mode 100644 index 0000000000..edb31e6d12 --- /dev/null +++ b/test/566-checker-codegen-select/src/Main.java @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2016 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 { + + /// CHECK-START: long Main.$noinline$longSelect(long) register (before) + /// CHECK: <<Cond:z\d+>> LessThanOrEqual [{{j\d+}},{{j\d+}}] + /// CHECK-NEXT: Select [{{j\d+}},{{j\d+}},<<Cond>>] + + // Condition must be materialized on X86 because it would need too many + // registers otherwise. + /// CHECK-START-X86: long Main.$noinline$longSelect(long) disassembly (after) + /// CHECK: LessThanOrEqual + /// CHECK-NEXT: cmp + /// CHECK: Select + + public long $noinline$longSelect(long param) { + if (doThrow) { throw new Error(); } + long val_true = longB; + long val_false = longC; + return (param > longA) ? val_true : val_false; + } + + /// CHECK-START: long Main.$noinline$longSelect_Constant(long) register (before) + /// CHECK: <<Const:j\d+>> LongConstant + /// CHECK: <<Cond:z\d+>> LessThanOrEqual [{{j\d+}},<<Const>>] + /// CHECK-NEXT: Select [{{j\d+}},{{j\d+}},<<Cond>>] + + // Condition can be non-materialized on X86 because the condition does not + // request 4 registers any more. + /// CHECK-START-X86: long Main.$noinline$longSelect_Constant(long) disassembly (after) + /// CHECK: LessThanOrEqual + /// CHECK-NEXT: Select + + public long $noinline$longSelect_Constant(long param) { + if (doThrow) { throw new Error(); } + long val_true = longB; + long val_false = longC; + return (param > 3L) ? val_true : val_false; + } + + public static void main(String[] args) { + Main m = new Main(); + assertLongEquals(5L, m.$noinline$longSelect(4L)); + assertLongEquals(7L, m.$noinline$longSelect(2L)); + assertLongEquals(5L, m.$noinline$longSelect_Constant(4L)); + assertLongEquals(7L, m.$noinline$longSelect_Constant(2L)); + } + + public static void assertLongEquals(long expected, long actual) { + if (expected != actual) { + throw new Error(expected + " != " + actual); + } + } + + public boolean doThrow = false; + + public long longA = 3L; + public long longB = 5L; + public long longC = 7L; +} |