Subzero. ARM32 Fcmp lowering.

BUG= https://code.google.com/p/nativeclient/issues/detail?id=4076
R=stichnot@chromium.org

Review URL: https://codereview.chromium.org/1356763004 .
diff --git a/src/IceTargetLoweringARM32.cpp b/src/IceTargetLoweringARM32.cpp
index 3fb5efc..d61d93c 100644
--- a/src/IceTargetLoweringARM32.cpp
+++ b/src/IceTargetLoweringARM32.cpp
@@ -87,40 +87,39 @@
 // instructions/operands that use the same enum key value. The tables are kept
 // separate to maintain a proper separation between abstraction layers. There
 // is a risk that the tables could get out of sync if enum values are reordered
-// or if entries are added or deleted. The following dummy namespaces use
+// or if entries are added or deleted. The following anonymous namespaces use
 // static_asserts to ensure everything is kept in sync.
 
 // Validate the enum values in ICMPARM32_TABLE.
-namespace dummy1 {
+namespace {
 // Define a temporary set of enum values based on low-level table entries.
-enum _tmp_enum {
-#define X(val, signed, swapped64, C_32, C1_64, C2_64) _tmp_##val,
+enum _icmp_ll_enum {
+#define X(val, signed, swapped64, C_32, C1_64, C2_64) _icmp_ll_##val,
   ICMPARM32_TABLE
 #undef X
       _num
 };
 // Define a set of constants based on high-level table entries.
-#define X(tag, str) static const int _table1_##tag = InstIcmp::tag;
+#define X(tag, str) static constexpr int _icmp_hl_##tag = InstIcmp::tag;
 ICEINSTICMP_TABLE
 #undef X
 // Define a set of constants based on low-level table entries, and ensure the
 // table entry keys are consistent.
 #define X(val, signed, swapped64, C_32, C1_64, C2_64)                          \
-  static const int _table2_##val = _tmp_##val;                                 \
   static_assert(                                                               \
-      _table1_##val == _table2_##val,                                          \
-      "Inconsistency between ICMPARM32_TABLE and ICEINSTICMP_TABLE");
+      _icmp_ll_##val == _icmp_hl_##val,                                        \
+      "Inconsistency between ICMPARM32_TABLE and ICEINSTICMP_TABLE: " #val);
 ICMPARM32_TABLE
 #undef X
 // Repeat the static asserts with respect to the high-level table entries in
 // case the high-level table has extra entries.
 #define X(tag, str)                                                            \
   static_assert(                                                               \
-      _table1_##tag == _table2_##tag,                                          \
-      "Inconsistency between ICMPARM32_TABLE and ICEINSTICMP_TABLE");
+      _icmp_hl_##tag == _icmp_ll_##tag,                                        \
+      "Inconsistency between ICMPARM32_TABLE and ICEINSTICMP_TABLE: " #tag);
 ICEINSTICMP_TABLE
 #undef X
-} // end of namespace dummy1
+} // end of anonymous namespace
 
 // Stack alignment
 const uint32_t ARM32_STACK_ALIGNMENT_BYTES = 16;
@@ -2229,9 +2228,76 @@
   UnimplementedError(Func->getContext()->getFlags());
 }
 
+namespace {
+// Validates FCMPARM32_TABLE's declaration w.r.t. InstFcmp::FCondition ordering
+// (and naming).
+enum {
+#define X(val, CC0, CC1) _fcmp_ll_##val,
+  FCMPARM32_TABLE
+#undef X
+      _fcmp_ll_NUM
+};
+
+enum {
+#define X(tag, str) _fcmp_hl_##tag = InstFcmp::tag,
+  ICEINSTFCMP_TABLE
+#undef X
+      _fcmp_hl_NUM
+};
+
+static_assert(_fcmp_hl_NUM == _fcmp_ll_NUM,
+              "Inconsistency between high-level and low-level fcmp tags.");
+#define X(tag, str)                                                            \
+  static_assert(                                                               \
+      _fcmp_hl_##tag == _fcmp_ll_##tag,                                        \
+      "Inconsistency between high-level and low-level fcmp tag " #tag);
+ICEINSTFCMP_TABLE
+#undef X
+
+struct {
+  CondARM32::Cond CC0;
+  CondARM32::Cond CC1;
+} TableFcmp[] = {
+#define X(val, CC0, CC1)                                                       \
+  { CondARM32::CC0, CondARM32::CC1 }                                           \
+  ,
+    FCMPARM32_TABLE
+#undef X
+};
+} // end of anonymous namespace
+
 void TargetARM32::lowerFcmp(const InstFcmp *Inst) {
-  (void)Inst;
-  UnimplementedError(Func->getContext()->getFlags());
+  Variable *Dest = Inst->getDest();
+  if (isVectorType(Dest->getType())) {
+    UnimplementedError(Func->getContext()->getFlags());
+    return;
+  }
+
+  Variable *Src0R = legalizeToReg(Inst->getSrc(0));
+  Variable *Src1R = legalizeToReg(Inst->getSrc(1));
+  Variable *T = makeReg(IceType_i32);
+  _vcmp(Src0R, Src1R);
+  _mov(T, Ctx->getConstantZero(IceType_i32));
+  _vmrs();
+  Operand *One = Ctx->getConstantInt32(1);
+  InstFcmp::FCond Condition = Inst->getCondition();
+  assert(Condition < llvm::array_lengthof(TableFcmp));
+  CondARM32::Cond CC0 = TableFcmp[Condition].CC0;
+  CondARM32::Cond CC1 = TableFcmp[Condition].CC1;
+  if (CC0 != CondARM32::kNone) {
+    _mov(T, One, CC0);
+    // If this mov is not a maybe mov, but an actual mov (i.e., CC0 == AL), we
+    // don't want to set_dest_nonkillable so that liveness + dead-code
+    // elimination will get rid of the previous assignment (i.e., T = 0) above.
+    if (CC0 != CondARM32::AL)
+      _set_dest_nonkillable();
+  }
+  if (CC1 != CondARM32::kNone) {
+    assert(CC0 != CondARM32::kNone);
+    assert(CC1 != CondARM32::AL);
+    _mov_nonkillable(T, One, CC1);
+  }
+  _mov(Dest, T);
 }
 
 void TargetARM32::lowerIcmp(const InstIcmp *Inst) {
@@ -2695,16 +2761,12 @@
     UnimplementedError(Func->getContext()->getFlags());
     return;
   }
-  if (isFloatingType(DestTy)) {
-    UnimplementedError(Func->getContext()->getFlags());
-    return;
-  }
   // TODO(jvoung): handle folding opportunities.
   // cmp cond, #0; mov t, SrcF; mov_cond t, SrcT; mov dest, t
   Variable *CmpOpnd0 = legalizeToReg(Condition);
   Operand *CmpOpnd1 = Ctx->getConstantZero(IceType_i32);
   _cmp(CmpOpnd0, CmpOpnd1);
-  CondARM32::Cond Cond = CondARM32::NE;
+  static constexpr CondARM32::Cond Cond = CondARM32::NE;
   if (DestTy == IceType_i64) {
     SrcT = legalizeUndef(SrcT);
     SrcF = legalizeUndef(SrcF);
@@ -2726,6 +2788,20 @@
     _mov(DestHi, THi);
     return;
   }
+
+  if (isFloatingType(DestTy)) {
+    Variable *T = makeReg(DestTy);
+    SrcF = legalizeToReg(SrcF);
+    assert(DestTy == SrcF->getType());
+    _vmov(T, SrcF);
+    SrcT = legalizeToReg(SrcT);
+    assert(DestTy == SrcT->getType());
+    _vmov(T, SrcT, Cond);
+    _set_dest_nonkillable();
+    _vmov(Dest, T);
+    return;
+  }
+
   Variable *T = nullptr;
   SrcF = legalize(SrcF, Legal_Reg | Legal_Flex);
   _mov(T, SrcF);