ARM64: Use VIXL's conditional select helper.

Change-Id: Id6bb880e2fffb54cf1f480191fc734eaaf4cd293
diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc
index 5560ae2..a37ea1e 100644
--- a/compiler/optimizing/code_generator_arm64.cc
+++ b/compiler/optimizing/code_generator_arm64.cc
@@ -2951,75 +2951,48 @@
                         /* false_target */ nullptr);
 }
 
-enum SelectVariant {
-  kCsel,
-  kCselFalseConst,
-  kCselTrueConst,
-  kFcsel,
-};
-
 static inline bool IsConditionOnFloatingPointValues(HInstruction* condition) {
   return condition->IsCondition() &&
          Primitive::IsFloatingPointType(condition->InputAt(0)->GetType());
 }
 
-static inline bool IsRecognizedCselConstant(HInstruction* constant) {
-  if (constant->IsConstant()) {
-    int64_t value = Int64FromConstant(constant->AsConstant());
-    if ((value == -1) || (value == 0) || (value == 1)) {
-      return true;
-    }
-  }
-  return false;
-}
-
-static inline SelectVariant GetSelectVariant(HSelect* select) {
-  if (Primitive::IsFloatingPointType(select->GetType())) {
-    return kFcsel;
-  } else if (IsRecognizedCselConstant(select->GetFalseValue())) {
-    return kCselFalseConst;
-  } else if (IsRecognizedCselConstant(select->GetTrueValue())) {
-    return kCselTrueConst;
-  } else {
-    return kCsel;
-  }
-}
-
-static inline bool HasSwappedInputs(SelectVariant variant) {
-  return variant == kCselTrueConst;
-}
-
-static inline Condition GetConditionForSelect(HCondition* condition, SelectVariant variant) {
-  IfCondition cond = HasSwappedInputs(variant) ? condition->GetOppositeCondition()
-                                               : condition->GetCondition();
+static inline Condition GetConditionForSelect(HCondition* condition) {
+  IfCondition cond = condition->AsCondition()->GetCondition();
   return IsConditionOnFloatingPointValues(condition) ? ARM64FPCondition(cond, condition->IsGtBias())
                                                      : ARM64Condition(cond);
 }
 
 void LocationsBuilderARM64::VisitSelect(HSelect* select) {
   LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(select);
-  switch (GetSelectVariant(select)) {
-    case kCsel:
-      locations->SetInAt(0, Location::RequiresRegister());
-      locations->SetInAt(1, Location::RequiresRegister());
-      locations->SetOut(Location::RequiresRegister());
-      break;
-    case kCselFalseConst:
-      locations->SetInAt(0, Location::ConstantLocation(select->InputAt(0)->AsConstant()));
-      locations->SetInAt(1, Location::RequiresRegister());
-      locations->SetOut(Location::RequiresRegister());
-      break;
-    case kCselTrueConst:
-      locations->SetInAt(0, Location::RequiresRegister());
-      locations->SetInAt(1, Location::ConstantLocation(select->InputAt(1)->AsConstant()));
-      locations->SetOut(Location::RequiresRegister());
-      break;
-    case kFcsel:
-      locations->SetInAt(0, Location::RequiresFpuRegister());
-      locations->SetInAt(1, Location::RequiresFpuRegister());
-      locations->SetOut(Location::RequiresFpuRegister());
-      break;
+  if (Primitive::IsFloatingPointType(select->GetType())) {
+    locations->SetInAt(0, Location::RequiresFpuRegister());
+    locations->SetInAt(1, Location::RequiresFpuRegister());
+    locations->SetOut(Location::RequiresFpuRegister());
+  } else {
+    HConstant* cst_true_value = select->GetTrueValue()->AsConstant();
+    HConstant* cst_false_value = select->GetFalseValue()->AsConstant();
+    bool is_true_value_constant = cst_true_value != nullptr;
+    bool is_false_value_constant = cst_false_value != nullptr;
+    // Ask VIXL whether we should synthesize constants in registers.
+    // We give an arbitrary register to VIXL when dealing with non-constant inputs.
+    Operand true_op = is_true_value_constant ?
+        Operand(Int64FromConstant(cst_true_value)) : Operand(x1);
+    Operand false_op = is_false_value_constant ?
+        Operand(Int64FromConstant(cst_false_value)) : Operand(x2);
+    bool true_value_in_register = false;
+    bool false_value_in_register = false;
+    MacroAssembler::GetCselSynthesisInformation(
+        x0, true_op, false_op, &true_value_in_register, &false_value_in_register);
+    true_value_in_register |= !is_true_value_constant;
+    false_value_in_register |= !is_false_value_constant;
+
+    locations->SetInAt(1, true_value_in_register ? Location::RequiresRegister()
+                                                 : Location::ConstantLocation(cst_true_value));
+    locations->SetInAt(0, false_value_in_register ? Location::RequiresRegister()
+                                                  : Location::ConstantLocation(cst_false_value));
+    locations->SetOut(Location::RequiresRegister());
   }
+
   if (IsBooleanValueOrMaterializedCondition(select->GetCondition())) {
     locations->SetInAt(2, Location::RequiresRegister());
   }
@@ -3027,45 +3000,34 @@
 
 void InstructionCodeGeneratorARM64::VisitSelect(HSelect* select) {
   HInstruction* cond = select->GetCondition();
-  SelectVariant variant = GetSelectVariant(select);
   Condition csel_cond;
 
   if (IsBooleanValueOrMaterializedCondition(cond)) {
     if (cond->IsCondition() && cond->GetNext() == select) {
-      // Condition codes set from previous instruction.
-      csel_cond = GetConditionForSelect(cond->AsCondition(), variant);
+      // Use the condition flags set by the previous instruction.
+      csel_cond = GetConditionForSelect(cond->AsCondition());
     } else {
       __ Cmp(InputRegisterAt(select, 2), 0);
-      csel_cond = HasSwappedInputs(variant) ? eq : ne;
+      csel_cond = ne;
     }
   } else if (IsConditionOnFloatingPointValues(cond)) {
     GenerateFcmp(cond);
-    csel_cond = GetConditionForSelect(cond->AsCondition(), variant);
+    csel_cond = GetConditionForSelect(cond->AsCondition());
   } else {
     __ Cmp(InputRegisterAt(cond, 0), InputOperandAt(cond, 1));
-    csel_cond = GetConditionForSelect(cond->AsCondition(), variant);
+    csel_cond = GetConditionForSelect(cond->AsCondition());
   }
 
-  switch (variant) {
-    case kCsel:
-    case kCselFalseConst:
-      __ Csel(OutputRegister(select),
-              InputRegisterAt(select, 1),
-              InputOperandAt(select, 0),
-              csel_cond);
-      break;
-    case kCselTrueConst:
-      __ Csel(OutputRegister(select),
-              InputRegisterAt(select, 0),
-              InputOperandAt(select, 1),
-              csel_cond);
-      break;
-    case kFcsel:
-      __ Fcsel(OutputFPRegister(select),
-               InputFPRegisterAt(select, 1),
-               InputFPRegisterAt(select, 0),
-               csel_cond);
-      break;
+  if (Primitive::IsFloatingPointType(select->GetType())) {
+    __ Fcsel(OutputFPRegister(select),
+             InputFPRegisterAt(select, 1),
+             InputFPRegisterAt(select, 0),
+             csel_cond);
+  } else {
+    __ Csel(OutputRegister(select),
+            InputOperandAt(select, 1),
+            InputOperandAt(select, 0),
+            csel_cond);
   }
 }
 
diff --git a/test/570-checker-select/src/Main.java b/test/570-checker-select/src/Main.java
index 59741d6..e0a76ca 100644
--- a/test/570-checker-select/src/Main.java
+++ b/test/570-checker-select/src/Main.java
@@ -16,6 +16,8 @@
 
 public class Main {
 
+  static boolean doThrow = false;
+
   /// CHECK-START: int Main.BoolCond_IntVarVar(boolean, int, int) register (after)
   /// CHECK:               Select [{{i\d+}},{{i\d+}},{{z\d+}}]
 
@@ -35,6 +37,10 @@
   /// CHECK:                          cmovnz/ne
 
   public static int BoolCond_IntVarVar(boolean cond, int x, int y) {
+    if (doThrow) {
+      // Try defeating inlining.
+      throw new Error();
+    }
     return cond ? x : y;
   }
 
@@ -57,6 +63,10 @@
   /// CHECK:                          cmovnz/ne
 
   public static int BoolCond_IntVarCst(boolean cond, int x) {
+    if (doThrow) {
+      // Try defeating inlining.
+      throw new Error();
+    }
     return cond ? x : 1;
   }
 
@@ -79,6 +89,10 @@
   /// CHECK:                          cmovnz/ne
 
   public static int BoolCond_IntCstVar(boolean cond, int y) {
+    if (doThrow) {
+      // Try defeating inlining.
+      throw new Error();
+    }
     return cond ? 1 : y;
   }
 
@@ -102,6 +116,10 @@
   /// CHECK-NEXT:                     cmovnz/ne
 
   public static long BoolCond_LongVarVar(boolean cond, long x, long y) {
+    if (doThrow) {
+      // Try defeating inlining.
+      throw new Error();
+    }
     return cond ? x : y;
   }
 
@@ -125,6 +143,10 @@
   /// CHECK-NEXT:                     cmovnz/ne
 
   public static long BoolCond_LongVarCst(boolean cond, long x) {
+    if (doThrow) {
+      // Try defeating inlining.
+      throw new Error();
+    }
     return cond ? x : 1L;
   }
 
@@ -148,6 +170,10 @@
   /// CHECK-NEXT:                     cmovnz/ne
 
   public static long BoolCond_LongCstVar(boolean cond, long y) {
+    if (doThrow) {
+      // Try defeating inlining.
+      throw new Error();
+    }
     return cond ? 1L : y;
   }
 
@@ -160,6 +186,10 @@
   /// CHECK-NEXT:            fcsel ne
 
   public static float BoolCond_FloatVarVar(boolean cond, float x, float y) {
+    if (doThrow) {
+      // Try defeating inlining.
+      throw new Error();
+    }
     return cond ? x : y;
   }
 
@@ -172,6 +202,10 @@
   /// CHECK-NEXT:            fcsel ne
 
   public static float BoolCond_FloatVarCst(boolean cond, float x) {
+    if (doThrow) {
+      // Try defeating inlining.
+      throw new Error();
+    }
     return cond ? x : 1.0f;
   }
 
@@ -184,6 +218,10 @@
   /// CHECK-NEXT:            fcsel ne
 
   public static float BoolCond_FloatCstVar(boolean cond, float y) {
+    if (doThrow) {
+      // Try defeating inlining.
+      throw new Error();
+    }
     return cond ? 1.0f : y;
   }
 
@@ -207,6 +245,10 @@
   /// CHECK:                          cmovle/ng
 
   public static int IntNonmatCond_IntVarVar(int a, int b, int x, int y) {
+    if (doThrow) {
+      // Try defeating inlining.
+      throw new Error();
+    }
     return a > b ? x : y;
   }
 
@@ -233,6 +275,10 @@
   /// CHECK:                          cmovle/ng
 
   public static int IntMatCond_IntVarVar(int a, int b, int x, int y) {
+    if (doThrow) {
+      // Try defeating inlining.
+      throw new Error();
+    }
     int result = (a > b ? x : y);
     return result + (a > b ? 0 : 1);
   }
@@ -258,6 +304,10 @@
   /// CHECK-NEXT:                     cmovle/ng
 
   public static long IntNonmatCond_LongVarVar(int a, int b, long x, long y) {
+    if (doThrow) {
+      // Try defeating inlining.
+      throw new Error();
+    }
     return a > b ? x : y;
   }
 
@@ -291,6 +341,10 @@
   /// CHECK-NEXT:                     cmovnz/ne
 
   public static long IntMatCond_LongVarVar(int a, int b, long x, long y) {
+    if (doThrow) {
+      // Try defeating inlining.
+      throw new Error();
+    }
     long result = (a > b ? x : y);
     return result + (a > b ? 0L : 1L);
   }
@@ -310,6 +364,10 @@
   /// CHECK:                          cmovle/ngq
 
   public static long LongNonmatCond_LongVarVar(long a, long b, long x, long y) {
+    if (doThrow) {
+      // Try defeating inlining.
+      throw new Error();
+    }
     return a > b ? x : y;
   }
 
@@ -334,6 +392,10 @@
   /// CHECK:                          cmovnz/neq
 
   public static long LongMatCond_LongVarVar(long a, long b, long x, long y) {
+    if (doThrow) {
+      // Try defeating inlining.
+      throw new Error();
+    }
     long result = (a > b ? x : y);
     return result + (a > b ? 0L : 1L);
   }
@@ -349,6 +411,10 @@
   /// CHECK-NEXT:            csel le
 
   public static int FloatLtNonmatCond_IntVarVar(float a, float b, int x, int y) {
+    if (doThrow) {
+      // Try defeating inlining.
+      throw new Error();
+    }
     return a > b ? x : y;
   }
 
@@ -363,6 +429,10 @@
   /// CHECK-NEXT:            csel hs
 
   public static int FloatGtNonmatCond_IntVarVar(float a, float b, int x, int y) {
+    if (doThrow) {
+      // Try defeating inlining.
+      throw new Error();
+    }
     return a < b ? x : y;
   }
 
@@ -377,6 +447,10 @@
   /// CHECK-NEXT:            fcsel hs
 
   public static float FloatGtNonmatCond_FloatVarVar(float a, float b, float x, float y) {
+    if (doThrow) {
+      // Try defeating inlining.
+      throw new Error();
+    }
     return a < b ? x : y;
   }
 
@@ -393,6 +467,10 @@
   /// CHECK-NEXT:            csel le
 
   public static int FloatLtMatCond_IntVarVar(float a, float b, int x, int y) {
+    if (doThrow) {
+      // Try defeating inlining.
+      throw new Error();
+    }
     int result = (a > b ? x : y);
     return result + (a > b ? 0 : 1);
   }
@@ -410,6 +488,10 @@
   /// CHECK-NEXT:            csel hs
 
   public static int FloatGtMatCond_IntVarVar(float a, float b, int x, int y) {
+    if (doThrow) {
+      // Try defeating inlining.
+      throw new Error();
+    }
     int result = (a < b ? x : y);
     return result + (a < b ? 0 : 1);
   }
@@ -427,10 +509,70 @@
   /// CHECK-NEXT:            fcsel hs
 
   public static float FloatGtMatCond_FloatVarVar(float a, float b, float x, float y) {
+    if (doThrow) {
+      // Try defeating inlining.
+      throw new Error();
+    }
     float result = (a < b ? x : y);
     return result + (a < b ? 0 : 1);
   }
 
+  /// CHECK-START: int Main.BoolCond_0_m1(boolean) register (after)
+  /// CHECK:            <<Cond:z\d+>> ParameterValue
+  /// CHECK:                          Select [{{i\d+}},{{i\d+}},<<Cond>>]
+
+  /// CHECK-START-ARM64: int Main.BoolCond_0_m1(boolean) disassembly (after)
+  /// CHECK:            <<Cond:z\d+>> ParameterValue
+  /// CHECK:                          Select [{{i\d+}},{{i\d+}},<<Cond>>]
+  /// CHECK-NEXT:                     cmp {{w\d+}}, #0x0 (0)
+  /// CHECK-NEXT:                     csetm {{w\d+}}, eq
+
+  /// CHECK-START-X86_64: int Main.BoolCond_0_m1(boolean) disassembly (after)
+  /// CHECK:            <<Cond:z\d+>> ParameterValue
+  /// CHECK:                          Select [{{i\d+}},{{i\d+}},<<Cond>>]
+  /// CHECK:                          cmovnz/ne
+
+  /// CHECK-START-X86: int Main.BoolCond_0_m1(boolean) disassembly (after)
+  /// CHECK:            <<Cond:z\d+>> ParameterValue
+  /// CHECK:                          Select [{{i\d+}},{{i\d+}},<<Cond>>]
+  /// CHECK:                          cmovnz/ne
+
+  public static int BoolCond_0_m1(boolean cond) {
+    if (doThrow) {
+      // Try defeating inlining.
+      throw new Error();
+    }
+    return cond ? 0 : -1;
+  }
+
+  /// CHECK-START: int Main.BoolCond_m1_0(boolean) register (after)
+  /// CHECK:            <<Cond:z\d+>> ParameterValue
+  /// CHECK:                          Select [{{i\d+}},{{i\d+}},<<Cond>>]
+
+  /// CHECK-START-ARM64: int Main.BoolCond_m1_0(boolean) disassembly (after)
+  /// CHECK:            <<Cond:z\d+>> ParameterValue
+  /// CHECK:                          Select [{{i\d+}},{{i\d+}},<<Cond>>]
+  /// CHECK-NEXT:                     cmp {{w\d+}}, #0x0 (0)
+  /// CHECK-NEXT:                     csetm {{w\d+}}, ne
+
+  /// CHECK-START-X86_64: int Main.BoolCond_m1_0(boolean) disassembly (after)
+  /// CHECK:            <<Cond:z\d+>> ParameterValue
+  /// CHECK:                          Select [{{i\d+}},{{i\d+}},<<Cond>>]
+  /// CHECK:                          cmovnz/ne
+
+  /// CHECK-START-X86: int Main.BoolCond_m1_0(boolean) disassembly (after)
+  /// CHECK:            <<Cond:z\d+>> ParameterValue
+  /// CHECK:                          Select [{{i\d+}},{{i\d+}},<<Cond>>]
+  /// CHECK:                          cmovnz/ne
+
+  public static int BoolCond_m1_0(boolean cond) {
+    if (doThrow) {
+      // Try defeating inlining.
+      throw new Error();
+    }
+    return cond ? -1 : 0;
+  }
+
   public static void assertEqual(int expected, int actual) {
     if (expected != actual) {
       throw new Error("Assertion failed: " + expected + " != " + actual);
@@ -499,5 +641,10 @@
     assertEqual(8, FloatGtMatCond_FloatVarVar(3, 2, 5, 7));
     assertEqual(8, FloatGtMatCond_FloatVarVar(Float.NaN, 2, 5, 7));
     assertEqual(8, FloatGtMatCond_FloatVarVar(2, Float.NaN, 5, 7));
+
+    assertEqual(0, BoolCond_0_m1(true));
+    assertEqual(-1, BoolCond_0_m1(false));
+    assertEqual(-1, BoolCond_m1_0(true));
+    assertEqual(0, BoolCond_m1_0(false));
   }
 }