ART: Refactor GenSelect, refactor gen_common accordingly

This adds a GenSelect method meant for selection of constants. The
general-purpose GenInstanceof code is refactored to take advantage of
this. This cleans up code and squashes a branch-over on ARM64 to a
cset.

Also add a slow-path for type initialization in GenInstanceof.

Change-Id: Ie4494858bb8c26d386cf2e628172b81bba911ae5
diff --git a/compiler/dex/quick/arm/codegen_arm.h b/compiler/dex/quick/arm/codegen_arm.h
index d4b0de7..23e8486 100644
--- a/compiler/dex/quick/arm/codegen_arm.h
+++ b/compiler/dex/quick/arm/codegen_arm.h
@@ -138,6 +138,9 @@
     void GenFusedFPCmpBranch(BasicBlock* bb, MIR* mir, bool gt_bias, bool is_double);
     void GenFusedLongCmpBranch(BasicBlock* bb, MIR* mir);
     void GenSelect(BasicBlock* bb, MIR* mir);
+    void GenSelectConst32(RegStorage left_op, RegStorage right_op, ConditionCode code,
+                          int32_t true_val, int32_t false_val, RegStorage rs_dest,
+                          int dest_reg_class) OVERRIDE;
     bool GenMemBarrier(MemBarrierKind barrier_kind);
     void GenMonitorEnter(int opt_flags, RegLocation rl_src);
     void GenMonitorExit(int opt_flags, RegLocation rl_src);
diff --git a/compiler/dex/quick/arm/int_arm.cc b/compiler/dex/quick/arm/int_arm.cc
index 2fcc3a5..a85b740 100644
--- a/compiler/dex/quick/arm/int_arm.cc
+++ b/compiler/dex/quick/arm/int_arm.cc
@@ -203,6 +203,30 @@
   OpCmpImmBranch(ccode, low_reg, val_lo, taken);
 }
 
+void ArmMir2Lir::GenSelectConst32(RegStorage left_op, RegStorage right_op, ConditionCode code,
+                                  int32_t true_val, int32_t false_val, RegStorage rs_dest,
+                                  int dest_reg_class) {
+  // TODO: Generalize the IT below to accept more than one-instruction loads.
+  DCHECK(InexpensiveConstantInt(true_val));
+  DCHECK(InexpensiveConstantInt(false_val));
+
+  if ((true_val == 0 && code == kCondEq) ||
+      (false_val == 0 && code == kCondNe)) {
+    OpRegRegReg(kOpSub, rs_dest, left_op, right_op);
+    DCHECK(last_lir_insn_->u.m.def_mask->HasBit(ResourceMask::kCCode));
+    LIR* it = OpIT(kCondNe, "");
+    LoadConstant(rs_dest, code == kCondEq ? false_val : true_val);
+    OpEndIT(it);
+    return;
+  }
+
+  OpRegReg(kOpCmp, left_op, right_op);  // Same?
+  LIR* it = OpIT(code, "E");   // if-convert the test
+  LoadConstant(rs_dest, true_val);      // .eq case - load true
+  LoadConstant(rs_dest, false_val);     // .eq case - load true
+  OpEndIT(it);
+}
+
 void ArmMir2Lir::GenSelect(BasicBlock* bb, MIR* mir) {
   RegLocation rl_result;
   RegLocation rl_src = mir_graph_->GetSrc(mir, 0);
diff --git a/compiler/dex/quick/arm64/codegen_arm64.h b/compiler/dex/quick/arm64/codegen_arm64.h
index de97653..da586b1 100644
--- a/compiler/dex/quick/arm64/codegen_arm64.h
+++ b/compiler/dex/quick/arm64/codegen_arm64.h
@@ -206,7 +206,14 @@
     void GenFillArrayData(DexOffset table_offset, RegLocation rl_src);
     void GenFusedFPCmpBranch(BasicBlock* bb, MIR* mir, bool gt_bias, bool is_double);
     void GenFusedLongCmpBranch(BasicBlock* bb, MIR* mir);
-    void GenSelect(BasicBlock* bb, MIR* mir);
+    void GenSelect(BasicBlock* bb, MIR* mir) OVERRIDE;
+    void GenSelectConst32(RegStorage left_op, RegStorage right_op, ConditionCode code,
+                          int32_t true_val, int32_t false_val, RegStorage rs_dest,
+                          int dest_reg_class) OVERRIDE;
+    // Helper used in the above two.
+    void GenSelect(int32_t left, int32_t right, ConditionCode code, RegStorage rs_dest,
+                   int result_reg_class);
+
     bool GenMemBarrier(MemBarrierKind barrier_kind);
     void GenMonitorEnter(int opt_flags, RegLocation rl_src);
     void GenMonitorExit(int opt_flags, RegLocation rl_src);
diff --git a/compiler/dex/quick/arm64/int_arm64.cc b/compiler/dex/quick/arm64/int_arm64.cc
index 6dc4a7a..a76d275 100644
--- a/compiler/dex/quick/arm64/int_arm64.cc
+++ b/compiler/dex/quick/arm64/int_arm64.cc
@@ -85,141 +85,129 @@
   StoreValueWide(rl_dest, rl_result);
 }
 
-void Arm64Mir2Lir::GenSelect(BasicBlock* bb, MIR* mir) {
-  RegLocation rl_result;
-  RegLocation rl_src = mir_graph_->GetSrc(mir, 0);
-  RegLocation rl_dest = mir_graph_->GetDest(mir);
-  RegisterClass src_reg_class = rl_src.ref ? kRefReg : kCoreReg;
-  RegisterClass result_reg_class = rl_dest.ref ? kRefReg : kCoreReg;
+static constexpr bool kUseDeltaEncodingInGenSelect = false;
 
-  rl_src = LoadValue(rl_src, src_reg_class);
+void Arm64Mir2Lir::GenSelect(int32_t true_val, int32_t false_val, ConditionCode ccode,
+                             RegStorage rs_dest, int result_reg_class) {
+  if (false_val == 0 ||               // 0 is better as first operand.
+      true_val == 1 ||                // Potentially Csinc.
+      true_val == -1 ||               // Potentially Csinv.
+      true_val == false_val + 1) {    // Potentially Csinc.
+    ccode = NegateComparison(ccode);
+    std::swap(true_val, false_val);
+  }
+
+  ArmConditionCode code = ArmConditionEncoding(ccode);
+
+  int opcode;                                      // The opcode.
+  RegStorage left_op = RegStorage::InvalidReg();   // The operands.
+  RegStorage right_op = RegStorage::InvalidReg();  // The operands.
+
+  bool is_wide = rs_dest.Is64Bit();
+
+  RegStorage zero_reg = is_wide ? rs_xzr : rs_wzr;
+
+  if (true_val == 0) {
+    left_op = zero_reg;
+  } else {
+    left_op = rs_dest;
+    LoadConstantNoClobber(rs_dest, true_val);
+  }
+  if (false_val == 1) {
+    right_op = zero_reg;
+    opcode = kA64Csinc4rrrc;
+  } else if (false_val == -1) {
+    right_op = zero_reg;
+    opcode = kA64Csinv4rrrc;
+  } else if (false_val == true_val + 1) {
+    right_op = left_op;
+    opcode = kA64Csinc4rrrc;
+  } else if (false_val == -true_val) {
+    right_op = left_op;
+    opcode = kA64Csneg4rrrc;
+  } else if (false_val == ~true_val) {
+    right_op = left_op;
+    opcode = kA64Csinv4rrrc;
+  } else if (true_val == 0) {
+    // left_op is zero_reg.
+    right_op = rs_dest;
+    LoadConstantNoClobber(rs_dest, false_val);
+    opcode = kA64Csel4rrrc;
+  } else {
+    // Generic case.
+    RegStorage t_reg2 = AllocTypedTemp(false, result_reg_class);
+    if (is_wide) {
+      if (t_reg2.Is32Bit()) {
+        t_reg2 = As64BitReg(t_reg2);
+      }
+    } else {
+      if (t_reg2.Is64Bit()) {
+        t_reg2 = As32BitReg(t_reg2);
+      }
+    }
+
+    if (kUseDeltaEncodingInGenSelect) {
+      int32_t delta = false_val - true_val;
+      uint32_t abs_val = delta < 0 ? -delta : delta;
+
+      if (abs_val < 0x1000) {  // TODO: Replace with InexpensiveConstant with opcode.
+        // Can encode as immediate to an add.
+        right_op = t_reg2;
+        OpRegRegImm(kOpAdd, t_reg2, left_op, delta);
+      }
+    }
+
+    // Load as constant.
+    if (!right_op.Valid()) {
+      LoadConstantNoClobber(t_reg2, false_val);
+      right_op = t_reg2;
+    }
+
+    opcode = kA64Csel4rrrc;
+  }
+
+  DCHECK(left_op.Valid() && right_op.Valid());
+  NewLIR4(is_wide ? WIDE(opcode) : opcode, rs_dest.GetReg(), left_op.GetReg(), right_op.GetReg(),
+      code);
+}
+
+void Arm64Mir2Lir::GenSelectConst32(RegStorage left_op, RegStorage right_op, ConditionCode code,
+                                    int32_t true_val, int32_t false_val, RegStorage rs_dest,
+                                    int dest_reg_class) {
+  DCHECK(rs_dest.Valid());
+  OpRegReg(kOpCmp, left_op, right_op);
+  GenSelect(true_val, false_val, code, rs_dest, dest_reg_class);
+}
+
+void Arm64Mir2Lir::GenSelect(BasicBlock* bb, MIR* mir) {
+  RegLocation rl_src = mir_graph_->GetSrc(mir, 0);
+  rl_src = LoadValue(rl_src, rl_src.ref ? kRefReg : kCoreReg);
   // rl_src may be aliased with rl_result/rl_dest, so do compare early.
   OpRegImm(kOpCmp, rl_src.reg, 0);
 
-  ArmConditionCode code = ArmConditionEncoding(mir->meta.ccode);
+  RegLocation rl_dest = mir_graph_->GetDest(mir);
 
   // The kMirOpSelect has two variants, one for constants and one for moves.
-  bool is_wide = rl_dest.ref || rl_dest.wide;
-
   if (mir->ssa_rep->num_uses == 1) {
-    uint32_t true_val = mir->dalvikInsn.vB;
-    uint32_t false_val = mir->dalvikInsn.vC;
-
-    int opcode;             // The opcode.
-    int left_op, right_op;  // The operands.
-    bool rl_result_evaled = false;
-
-    // Check some simple cases.
-    // TODO: Improve this.
-    int zero_reg = (is_wide ? rs_xzr : rs_wzr).GetReg();
-
-    if ((true_val == 0 && false_val == 1) || (true_val == 1 && false_val == 0)) {
-      // CSInc cheap based on wzr.
-      if (true_val == 1) {
-        // Negate.
-        code = ArmConditionEncoding(NegateComparison(mir->meta.ccode));
-      }
-
-      left_op = right_op = zero_reg;
-      opcode = is_wide ? WIDE(kA64Csinc4rrrc) : kA64Csinc4rrrc;
-    } else if ((true_val == 0 && false_val == 0xFFFFFFFF) ||
-               (true_val == 0xFFFFFFFF && false_val == 0)) {
-      // CSneg cheap based on wzr.
-      if (true_val == 0xFFFFFFFF) {
-        // Negate.
-        code = ArmConditionEncoding(NegateComparison(mir->meta.ccode));
-      }
-
-      left_op = right_op = zero_reg;
-      opcode = is_wide ? WIDE(kA64Csinv4rrrc) : kA64Csinv4rrrc;
-    } else if (true_val == 0 || false_val == 0) {
-      // Csel half cheap based on wzr.
-      rl_result = EvalLoc(rl_dest, result_reg_class, true);
-      rl_result_evaled = true;
-      if (false_val == 0) {
-        // Negate.
-        code = ArmConditionEncoding(NegateComparison(mir->meta.ccode));
-      }
-      LoadConstantNoClobber(rl_result.reg, true_val == 0 ? false_val : true_val);
-      left_op = zero_reg;
-      right_op = rl_result.reg.GetReg();
-      opcode = is_wide ? WIDE(kA64Csel4rrrc) : kA64Csel4rrrc;
-    } else if (true_val == 1 || false_val == 1) {
-      // CSInc half cheap based on wzr.
-      rl_result = EvalLoc(rl_dest, result_reg_class, true);
-      rl_result_evaled = true;
-      if (true_val == 1) {
-        // Negate.
-        code = ArmConditionEncoding(NegateComparison(mir->meta.ccode));
-      }
-      LoadConstantNoClobber(rl_result.reg, true_val == 1 ? false_val : true_val);
-      left_op = rl_result.reg.GetReg();
-      right_op = zero_reg;
-      opcode = is_wide ? WIDE(kA64Csinc4rrrc) : kA64Csinc4rrrc;
-    } else if (true_val == 0xFFFFFFFF || false_val == 0xFFFFFFFF) {
-      // CSneg half cheap based on wzr.
-      rl_result = EvalLoc(rl_dest, result_reg_class, true);
-      rl_result_evaled = true;
-      if (true_val == 0xFFFFFFFF) {
-        // Negate.
-        code = ArmConditionEncoding(NegateComparison(mir->meta.ccode));
-      }
-      LoadConstantNoClobber(rl_result.reg, true_val == 0xFFFFFFFF ? false_val : true_val);
-      left_op = rl_result.reg.GetReg();
-      right_op = zero_reg;
-      opcode = is_wide ? WIDE(kA64Csinv4rrrc) : kA64Csinv4rrrc;
-    } else if ((true_val + 1 == false_val) || (false_val + 1 == true_val)) {
-      // Load a constant and use CSinc. Use rl_result.
-      if (false_val + 1 == true_val) {
-        // Negate.
-        code = ArmConditionEncoding(NegateComparison(mir->meta.ccode));
-        true_val = false_val;
-      }
-
-      rl_result = EvalLoc(rl_dest, result_reg_class, true);
-      rl_result_evaled = true;
-      LoadConstantNoClobber(rl_result.reg, true_val);
-      left_op = right_op = rl_result.reg.GetReg();
-      opcode = is_wide ? WIDE(kA64Csinc4rrrc) : kA64Csinc4rrrc;
-    } else {
-      // Csel. The rest. Use rl_result and a temp.
-      // TODO: To minimize the constants being loaded, check whether one can be inexpensively
-      //       loaded as n - 1 or ~n.
-      rl_result = EvalLoc(rl_dest, result_reg_class, true);
-      rl_result_evaled = true;
-      LoadConstantNoClobber(rl_result.reg, true_val);
-      RegStorage t_reg2 = AllocTypedTemp(false, result_reg_class);
-      if (rl_dest.wide) {
-        if (t_reg2.Is32Bit()) {
-          t_reg2 = As64BitReg(t_reg2);
-        }
-      }
-      LoadConstantNoClobber(t_reg2, false_val);
-
-      // Use csel.
-      left_op = rl_result.reg.GetReg();
-      right_op = t_reg2.GetReg();
-      opcode = is_wide ? WIDE(kA64Csel4rrrc) : kA64Csel4rrrc;
-    }
-
-    if (!rl_result_evaled) {
-      rl_result = EvalLoc(rl_dest, result_reg_class, true);
-    }
-
-    NewLIR4(opcode, rl_result.reg.GetReg(), left_op, right_op, code);
+    RegLocation rl_result = EvalLoc(rl_dest, rl_dest.ref ? kRefReg : kCoreReg, true);
+    GenSelect(mir->dalvikInsn.vB, mir->dalvikInsn.vC, mir->meta.ccode, rl_result.reg,
+              rl_dest.ref ? kRefReg : kCoreReg);
+    StoreValue(rl_dest, rl_result);
   } else {
     RegLocation rl_true = mir_graph_->reg_location_[mir->ssa_rep->uses[1]];
     RegLocation rl_false = mir_graph_->reg_location_[mir->ssa_rep->uses[2]];
 
+    RegisterClass result_reg_class = rl_dest.ref ? kRefReg : kCoreReg;
     rl_true = LoadValue(rl_true, result_reg_class);
     rl_false = LoadValue(rl_false, result_reg_class);
-    rl_result = EvalLoc(rl_dest, result_reg_class, true);
+    RegLocation rl_result = EvalLoc(rl_dest, result_reg_class, true);
 
+    bool is_wide = rl_dest.ref || rl_dest.wide;
     int opcode = is_wide ? WIDE(kA64Csel4rrrc) : kA64Csel4rrrc;
     NewLIR4(opcode, rl_result.reg.GetReg(),
-            rl_true.reg.GetReg(), rl_false.reg.GetReg(), code);
+            rl_true.reg.GetReg(), rl_false.reg.GetReg(), ArmConditionEncoding(mir->meta.ccode));
+    StoreValue(rl_dest, rl_result);
   }
-  StoreValue(rl_dest, rl_result);
 }
 
 void Arm64Mir2Lir::GenFusedLongCmpBranch(BasicBlock* bb, MIR* mir) {
diff --git a/compiler/dex/quick/gen_common.cc b/compiler/dex/quick/gen_common.cc
index 1fc0cff..bea1eb8 100644
--- a/compiler/dex/quick/gen_common.cc
+++ b/compiler/dex/quick/gen_common.cc
@@ -1167,7 +1167,6 @@
     LoadRefDisp(check_class, offset_of_type, check_class, kNotVolatile);
   }
 
-  LIR* ne_branchover = NULL;
   // FIXME: what should we be comparing here? compressed or decompressed references?
   if (cu_->instruction_set == kThumb2) {
     OpRegReg(kOpCmp, check_class, object_class);  // Same?
@@ -1175,14 +1174,10 @@
     LoadConstant(result_reg, 1);     // .eq case - load true
     OpEndIT(it);
   } else {
-    ne_branchover = OpCmpBranch(kCondNe, check_class, object_class, NULL);
-    LoadConstant(result_reg, 1);     // eq case - load true
+    GenSelectConst32(check_class, object_class, kCondEq, 1, 0, result_reg, kCoreReg);
   }
   LIR* target = NewLIR0(kPseudoTargetLabel);
   null_branchover->target = target;
-  if (ne_branchover != NULL) {
-    ne_branchover->target = target;
-  }
   FreeTemp(object_class);
   FreeTemp(check_class);
   if (IsTemp(result_reg)) {
@@ -1223,27 +1218,54 @@
     LoadRefDisp(method_reg, mirror::ArtMethod::DeclaringClassOffset().Int32Value(),
                 class_reg, kNotVolatile);
   } else {
+    if (can_assume_type_is_in_dex_cache) {
+      // Conditionally, as in the other case we will also load it.
+      LoadValueDirectFixed(rl_src, TargetReg(kArg0, kRef));  // kArg0 <= ref
+    }
+
     // Load dex cache entry into class_reg (kArg2)
-    LoadValueDirectFixed(rl_src, TargetReg(kArg0, kRef));  // kArg0 <= ref
     LoadRefDisp(method_reg, mirror::ArtMethod::DexCacheResolvedTypesOffset().Int32Value(),
                 class_reg, kNotVolatile);
     int32_t offset_of_type = ClassArray::OffsetOfElement(type_idx).Int32Value();
     LoadRefDisp(class_reg, offset_of_type, class_reg, kNotVolatile);
     if (!can_assume_type_is_in_dex_cache) {
-      // Need to test presence of type in dex cache at runtime
-      LIR* hop_branch = OpCmpImmBranch(kCondNe, class_reg, 0, NULL);
-      // Not resolved
-      // Call out to helper, which will return resolved type in kRet0
-      if (cu_->target64) {
-        CallRuntimeHelperImm(QUICK_ENTRYPOINT_OFFSET(8, pInitializeType), type_idx, true);
-      } else {
-        CallRuntimeHelperImm(QUICK_ENTRYPOINT_OFFSET(4, pInitializeType), type_idx, true);
-      }
-      OpRegCopy(TargetReg(kArg2, kRef), TargetReg(kRet0, kRef));  // Align usage with fast path
-      LoadValueDirectFixed(rl_src, TargetReg(kArg0, kRef));  /* reload Ref */
-      // Rejoin code paths
-      LIR* hop_target = NewLIR0(kPseudoTargetLabel);
-      hop_branch->target = hop_target;
+      LIR* slow_path_branch = OpCmpImmBranch(kCondEq, class_reg, 0, NULL);
+      LIR* slow_path_target = NewLIR0(kPseudoTargetLabel);
+
+      // Should load value here.
+      LoadValueDirectFixed(rl_src, TargetReg(kArg0, kRef));  // kArg0 <= ref
+
+      class InitTypeSlowPath : public Mir2Lir::LIRSlowPath {
+       public:
+        InitTypeSlowPath(Mir2Lir* m2l, LIR* branch, LIR* cont, uint32_t type_idx,
+                         RegLocation rl_src)
+            : LIRSlowPath(m2l, m2l->GetCurrentDexPc(), branch, cont), type_idx_(type_idx),
+              rl_src_(rl_src) {
+        }
+
+        void Compile() OVERRIDE {
+          GenerateTargetLabel();
+
+          if (cu_->target64) {
+            m2l_->CallRuntimeHelperImm(QUICK_ENTRYPOINT_OFFSET(8, pInitializeType), type_idx_,
+                                       true);
+          } else {
+            m2l_->CallRuntimeHelperImm(QUICK_ENTRYPOINT_OFFSET(4, pInitializeType), type_idx_,
+                                       true);
+          }
+          m2l_->OpRegCopy(m2l_->TargetReg(kArg2, kRef),
+                          m2l_->TargetReg(kRet0, kRef));  // Align usage with fast path
+
+          m2l_->OpUnconditionalBranch(cont_);
+        }
+
+       private:
+        uint32_t type_idx_;
+        RegLocation rl_src_;
+      };
+
+      AddSlowPath(new (arena_) InitTypeSlowPath(this, slow_path_branch, slow_path_target,
+                                                type_idx, rl_src));
     }
   }
   /* kArg0 is ref, kArg2 is class. If ref==null, use directly as bool result */
@@ -1262,17 +1284,8 @@
   LIR* branchover = NULL;
   if (type_known_final) {
     // rl_result == ref == null == 0.
-    if (cu_->instruction_set == kThumb2) {
-      OpRegReg(kOpCmp, TargetReg(kArg1, kRef), TargetReg(kArg2, kRef));  // Same?
-      LIR* it = OpIT(kCondEq, "E");   // if-convert the test
-      LoadConstant(rl_result.reg, 1);     // .eq case - load true
-      LoadConstant(rl_result.reg, 0);     // .ne case - load false
-      OpEndIT(it);
-    } else {
-      LoadConstant(rl_result.reg, 0);     // ne case - load false
-      branchover = OpCmpBranch(kCondNe, TargetReg(kArg1, kRef), TargetReg(kArg2, kRef), NULL);
-      LoadConstant(rl_result.reg, 1);     // eq case - load true
-    }
+    GenSelectConst32(TargetReg(kArg1, kRef), TargetReg(kArg2, kRef), kCondEq, 1, 0, rl_result.reg,
+                     kCoreReg);
   } else {
     if (cu_->instruction_set == kThumb2) {
       RegStorage r_tgt = cu_->target64 ?
@@ -1297,12 +1310,13 @@
         LoadConstant(rl_result.reg, 1);     // assume true
         branchover = OpCmpBranch(kCondEq, TargetReg(kArg1, kRef), TargetReg(kArg2, kRef), NULL);
       }
-      RegStorage r_tgt = cu_->target64 ?
-          LoadHelper(QUICK_ENTRYPOINT_OFFSET(8, pInstanceofNonTrivial)) :
-          LoadHelper(QUICK_ENTRYPOINT_OFFSET(4, pInstanceofNonTrivial));
+
       OpRegCopy(TargetReg(kArg0, kRef), TargetReg(kArg2, kRef));    // .ne case - arg0 <= class
-      OpReg(kOpBlx, r_tgt);    // .ne case: helper(class, ref->class)
-      FreeTemp(r_tgt);
+      if (cu_->target64) {
+        CallRuntimeHelper(QUICK_ENTRYPOINT_OFFSET(8, pInstanceofNonTrivial), false);
+      } else {
+        CallRuntimeHelper(QUICK_ENTRYPOINT_OFFSET(4, pInstanceofNonTrivial), false);
+      }
     }
   }
   // TODO: only clobber when type isn't final?
diff --git a/compiler/dex/quick/mips/codegen_mips.h b/compiler/dex/quick/mips/codegen_mips.h
index 2c33377..4ac6892 100644
--- a/compiler/dex/quick/mips/codegen_mips.h
+++ b/compiler/dex/quick/mips/codegen_mips.h
@@ -136,6 +136,9 @@
     void GenFusedFPCmpBranch(BasicBlock* bb, MIR* mir, bool gt_bias, bool is_double);
     void GenFusedLongCmpBranch(BasicBlock* bb, MIR* mir);
     void GenSelect(BasicBlock* bb, MIR* mir);
+    void GenSelectConst32(RegStorage left_op, RegStorage right_op, ConditionCode code,
+                          int32_t true_val, int32_t false_val, RegStorage rs_dest,
+                          int dest_reg_class) OVERRIDE;
     bool GenMemBarrier(MemBarrierKind barrier_kind);
     void GenMoveException(RegLocation rl_dest);
     void GenMultiplyByTwoBitMultiplier(RegLocation rl_src, RegLocation rl_result, int lit,
diff --git a/compiler/dex/quick/mips/int_mips.cc b/compiler/dex/quick/mips/int_mips.cc
index c3a4c17..1f8f9ec 100644
--- a/compiler/dex/quick/mips/int_mips.cc
+++ b/compiler/dex/quick/mips/int_mips.cc
@@ -215,6 +215,18 @@
   }
 }
 
+void MipsMir2Lir::GenSelectConst32(RegStorage left_op, RegStorage right_op, ConditionCode code,
+                                   int32_t true_val, int32_t false_val, RegStorage rs_dest,
+                                   int dest_reg_class) {
+  // Implement as a branch-over.
+  // TODO: Conditional move?
+  LoadConstant(rs_dest, false_val);  // Favors false.
+  LIR* ne_branchover = OpCmpBranch(code, left_op, right_op, NULL);
+  LoadConstant(rs_dest, true_val);
+  LIR* target_label = NewLIR0(kPseudoTargetLabel);
+  ne_branchover->target = target_label;
+}
+
 void MipsMir2Lir::GenSelect(BasicBlock* bb, MIR* mir) {
   UNIMPLEMENTED(FATAL) << "Need codegen for select";
 }
diff --git a/compiler/dex/quick/mir_to_lir.h b/compiler/dex/quick/mir_to_lir.h
index c68ad6b..2aa158f 100644
--- a/compiler/dex/quick/mir_to_lir.h
+++ b/compiler/dex/quick/mir_to_lir.h
@@ -1380,6 +1380,14 @@
     virtual void GenSelect(BasicBlock* bb, MIR* mir) = 0;
 
     /**
+     * @brief Generates code to select one of the given constants depending on the given opcode.
+     * @note Will neither call EvalLoc nor StoreValue for rl_dest.
+     */
+    virtual void GenSelectConst32(RegStorage left_op, RegStorage right_op, ConditionCode code,
+                                  int32_t true_val, int32_t false_val, RegStorage rs_dest,
+                                  int dest_reg_class) = 0;
+
+    /**
      * @brief Used to generate a memory barrier in an architecture specific way.
      * @details The last generated LIR will be considered for use as barrier. Namely,
      * if the last LIR can be updated in a way where it will serve the semantics of
diff --git a/compiler/dex/quick/x86/codegen_x86.h b/compiler/dex/quick/x86/codegen_x86.h
index f4fa1b4..0857bd6 100644
--- a/compiler/dex/quick/x86/codegen_x86.h
+++ b/compiler/dex/quick/x86/codegen_x86.h
@@ -203,6 +203,12 @@
   void GenFusedFPCmpBranch(BasicBlock* bb, MIR* mir, bool gt_bias, bool is_double);
   void GenFusedLongCmpBranch(BasicBlock* bb, MIR* mir);
   void GenSelect(BasicBlock* bb, MIR* mir);
+  void GenSelectConst32(RegStorage left_op, RegStorage right_op, ConditionCode code,
+                        int32_t true_val, int32_t false_val, RegStorage rs_dest,
+                        int dest_reg_class) OVERRIDE;
+  // Optimized version for selection of 0 and 1.
+  void GenSelectConst01(RegStorage left_op, RegStorage right_op, ConditionCode code, bool true_val,
+                        RegStorage rs_dest);
   bool GenMemBarrier(MemBarrierKind barrier_kind);
   void GenMoveException(RegLocation rl_dest);
   void GenMultiplyByTwoBitMultiplier(RegLocation rl_src, RegLocation rl_result, int lit,
diff --git a/compiler/dex/quick/x86/int_x86.cc b/compiler/dex/quick/x86/int_x86.cc
index 2f27482..30b87ba 100755
--- a/compiler/dex/quick/x86/int_x86.cc
+++ b/compiler/dex/quick/x86/int_x86.cc
@@ -206,6 +206,38 @@
   }
 }
 
+// Set rs_dest to 0 or 1 depending on the comparison between left_op and right_op.
+// rs_dest := (left_op <code> right_op) ? [true_val] : [!true_val]
+//
+// Implemented as:
+//         true_val = true =>  rs_dest := 0;
+//                             rs_dest := (left_op <code> right_op) ? 1 : rs_dest;
+//         true_val = false => rs_dest := 0;
+//                             rs_dest := (left_op <~code> right_op) ? 1 : rs_dest;
+void X86Mir2Lir::GenSelectConst01(RegStorage left_op, RegStorage right_op, ConditionCode code,
+                                  bool true_val, RegStorage rs_dest) {
+  LoadConstant(rs_dest, 0);
+  OpRegReg(kOpCmp, left_op, right_op);
+  // Set the low byte of the result to 0 or 1 from the compare condition code.
+  NewLIR2(kX86Set8R, rs_dest.GetReg(),
+          X86ConditionEncoding(true_val ? code : FlipComparisonOrder(code)));
+}
+
+void X86Mir2Lir::GenSelectConst32(RegStorage left_op, RegStorage right_op, ConditionCode code,
+                                  int32_t true_val, int32_t false_val, RegStorage rs_dest,
+                                  int dest_reg_class) {
+  if ((true_val == 0 && false_val == 1) || (true_val == 1 && false_val == 0)) {
+    // Can we use Setcc?
+    if (rs_dest.Is64Bit() || rs_dest.GetRegNum() < 4) {
+      GenSelectConst01(left_op, right_op, code, true_val == 1, rs_dest);
+      return;
+    }
+  }
+
+  // TODO: Refactor the code below to make this more general.
+  UNIMPLEMENTED(FATAL) << "General GenSelectConst32 not implemented for x86.";
+}
+
 void X86Mir2Lir::GenSelect(BasicBlock* bb, MIR* mir) {
   RegLocation rl_result;
   RegLocation rl_src = mir_graph_->GetSrc(mir, 0);
@@ -2459,9 +2491,6 @@
     OpRegCopy(rl_result.reg, ref_reg);
   }
 
-  // For 32-bit, SETcc only works with EAX..EDX.
-  DCHECK_LT(rl_result.reg.GetRegNum(), 4);
-
   // Is the class NULL?
   LIR* branch1 = OpCmpImmBranch(kCondEq, ref_reg, 0, NULL);
 
@@ -2473,11 +2502,7 @@
   /* kArg0 is ref, kArg1 is ref->klass_, kArg2 is class. */
   LIR* branchover = nullptr;
   if (type_known_final) {
-    // Ensure top 3 bytes of result are 0.
-    LoadConstant(rl_result.reg, 0);
-    OpRegReg(kOpCmp, ref_class_reg, class_reg);
-    // Set the low byte of the result to 0 or 1 from the compare condition code.
-    NewLIR2(kX86Set8R, rl_result.reg.GetReg(), kX86CondEq);
+    GenSelectConst32(ref_class_reg, class_reg, kCondEq, 1, 0, rl_result.reg, kCoreReg);
   } else {
     if (!type_known_abstract) {
       LoadConstant(rl_result.reg, 1);     // Assume result succeeds.