ART: Implement HSelect

This patch adds a new HIR instruction to Optimizing. HSelect returns
one of two inputs based on the outcome of a condition.

This is only initial implementation which:
 - defines the new instruction,
 - repurposes BooleanSimplifier to emit it,
 - extends InstructionSimplifier to statically resolve it,
 - updates existing code and tests accordingly.

Code generators currently emit fallback if/then/else code and will be
updated in follow-up CLs to use platform-specific conditional moves
when possible.

Change-Id: Ib61b17146487ebe6b55350c2b589f0b971dcaaee
diff --git a/compiler/optimizing/boolean_simplifier.cc b/compiler/optimizing/boolean_simplifier.cc
deleted file mode 100644
index f0cafc8..0000000
--- 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 e12a12c..0000000
--- 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 2b81dba..894ee07 100644
--- a/compiler/optimizing/code_generator_arm.cc
+++ b/compiler/optimizing/code_generator_arm.cc
@@ -1285,11 +1285,9 @@
 }
 
 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 @@
                         /* 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 @@
   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 @@
       __ 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 @@
     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 @@
           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 788c3a6..ec5ca3d 100644
--- a/compiler/optimizing/code_generator_arm64.cc
+++ b/compiler/optimizing/code_generator_arm64.cc
@@ -3004,6 +3004,32 @@
                         /* 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 fec5811..a2f85fe 100644
--- a/compiler/optimizing/code_generator_mips.cc
+++ b/compiler/optimizing/code_generator_mips.cc
@@ -3381,6 +3381,32 @@
                         /* 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 e5a5fd4..930a3ec 100644
--- a/compiler/optimizing/code_generator_mips64.cc
+++ b/compiler/optimizing/code_generator_mips64.cc
@@ -2748,6 +2748,32 @@
                         /* 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 a4a8c7c..6a4f924 100644
--- a/compiler/optimizing/code_generator_x86.cc
+++ b/compiler/optimizing/code_generator_x86.cc
@@ -1218,11 +1218,14 @@
 }
 
 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 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 @@
   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 @@
       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 1781f41..895e7c3 100644
--- a/compiler/optimizing/code_generator_x86_64.cc
+++ b/compiler/optimizing/code_generator_x86_64.cc
@@ -1558,10 +1558,36 @@
 
 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 3113677..962e77d 100644
--- a/compiler/optimizing/graph_checker.cc
+++ b/compiler/optimizing/graph_checker.cc
@@ -859,8 +859,12 @@
           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 @@
   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 2e16bfe..8724cde 100644
--- a/compiler/optimizing/graph_checker.h
+++ b/compiler/optimizing/graph_checker.h
@@ -126,6 +126,7 @@
   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 49fc8c7..7d3a723 100644
--- a/compiler/optimizing/instruction_simplifier.cc
+++ b/compiler/optimizing/instruction_simplifier.cc
@@ -76,6 +76,8 @@
   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::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 92f758d..c057eca 100644
--- a/compiler/optimizing/nodes.cc
+++ b/compiler/optimizing/nodes.cc
@@ -722,6 +722,22 @@
   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 922696b..4de3f8a 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -1011,6 +1011,7 @@
   // 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 @@
   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 @@
   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 3fac914..bdc664b 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 @@
       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 @@
   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 ec0fc3a..324d84f 100644
--- a/compiler/optimizing/prepare_for_register_allocation.cc
+++ b/compiler/optimizing/prepare_for_register_allocation.cc
@@ -137,6 +137,18 @@
     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 0000000..105b30a
--- /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 0000000..f9d6d4d
--- /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_