Lower the fcmp instruction for <4 x float> operands.

Most fcmp conditions map directly to single x86 instructions. For
these, the lowering is table driven.

BUG=none
R=jvoung@chromium.org, stichnot@chromium.org

Review URL: https://codereview.chromium.org/413053002
diff --git a/crosstest/test_fcmp.pnacl.ll b/crosstest/test_fcmp.pnacl.ll
index 7c4d42e..a175b1d 100644
--- a/crosstest/test_fcmp.pnacl.ll
+++ b/crosstest/test_fcmp.pnacl.ll
@@ -1,7 +1,7 @@
 target triple = "i686-pc-linux-gnu"
 
-; This file is extracted from fp.pnacl.ll in the lit tests, with
-; the "internal" attribute removed from the functions.
+; This file is extracted from fp.pnacl.ll and vector-fcmp.ll in the lit
+; tests, with the "internal" attribute removed from the functions.
 
 define i32 @fcmpFalseFloat(float %a, float %b) {
 entry:
@@ -322,3 +322,151 @@
 }
 ; CHECK: fcmpTrueDouble:
 ; CHECK: mov {{.*}}, 1
+
+define <4 x i32> @fcmpFalseVector(<4 x float> %a, <4 x float> %b) {
+entry:
+  %res.trunc = fcmp false <4 x float> %a, %b
+  %res = sext <4 x i1> %res.trunc to <4 x i32>
+  ret <4 x i32> %res
+; CHECK-LABEL: fcmpFalseVector:
+; CHECK: pxor
+}
+
+define <4 x i32> @fcmpOeqVector(<4 x float> %a, <4 x float> %b) {
+entry:
+  %res.trunc = fcmp oeq <4 x float> %a, %b
+  %res = sext <4 x i1> %res.trunc to <4 x i32>
+  ret <4 x i32> %res
+; CHECK-LABEL: fcmpOeqVector:
+; CHECK: cmpeqps
+}
+
+define <4 x i32> @fcmpOgeVector(<4 x float> %a, <4 x float> %b) {
+entry:
+  %res.trunc = fcmp oge <4 x float> %a, %b
+  %res = sext <4 x i1> %res.trunc to <4 x i32>
+  ret <4 x i32> %res
+; CHECK-LABEL: fcmpOgeVector:
+; CHECK: cmpleps
+}
+
+define <4 x i32> @fcmpOgtVector(<4 x float> %a, <4 x float> %b) {
+entry:
+  %res.trunc = fcmp ogt <4 x float> %a, %b
+  %res = sext <4 x i1> %res.trunc to <4 x i32>
+  ret <4 x i32> %res
+; CHECK-LABEL: fcmpOgtVector:
+; CHECK: cmpltps
+}
+
+define <4 x i32> @fcmpOleVector(<4 x float> %a, <4 x float> %b) {
+entry:
+  %res.trunc = fcmp ole <4 x float> %a, %b
+  %res = sext <4 x i1> %res.trunc to <4 x i32>
+  ret <4 x i32> %res
+; CHECK-LABEL: fcmpOleVector:
+; CHECK: cmpleps
+}
+
+define <4 x i32> @fcmpOltVector(<4 x float> %a, <4 x float> %b) {
+entry:
+  %res.trunc = fcmp olt <4 x float> %a, %b
+  %res = sext <4 x i1> %res.trunc to <4 x i32>
+  ret <4 x i32> %res
+; CHECK-LABEL: fcmpOltVector:
+; CHECK: cmpltps
+}
+
+define <4 x i32> @fcmpOneVector(<4 x float> %a, <4 x float> %b) {
+entry:
+  %res.trunc = fcmp one <4 x float> %a, %b
+  %res = sext <4 x i1> %res.trunc to <4 x i32>
+  ret <4 x i32> %res
+; CHECK-LABEL: fcmpOneVector:
+; CHECK: cmpneqps
+; CHECK: cmpordps
+; CHECK: pand
+}
+
+define <4 x i32> @fcmpOrdVector(<4 x float> %a, <4 x float> %b) {
+entry:
+  %res.trunc = fcmp ord <4 x float> %a, %b
+  %res = sext <4 x i1> %res.trunc to <4 x i32>
+  ret <4 x i32> %res
+; CHECK-LABEL: fcmpOrdVector:
+; CHECK: cmpordps
+}
+
+define <4 x i32> @fcmpTrueVector(<4 x float> %a, <4 x float> %b) {
+entry:
+  %res.trunc = fcmp true <4 x float> %a, %b
+  %res = sext <4 x i1> %res.trunc to <4 x i32>
+  ret <4 x i32> %res
+; CHECK-LABEL: fcmpTrueVector:
+; CHECK: pcmpeqd
+}
+
+define <4 x i32> @fcmpUeqVector(<4 x float> %a, <4 x float> %b) {
+entry:
+  %res.trunc = fcmp ueq <4 x float> %a, %b
+  %res = sext <4 x i1> %res.trunc to <4 x i32>
+  ret <4 x i32> %res
+; CHECK-LABEL: fcmpUeqVector:
+; CHECK: cmpeqps
+; CHECK: cmpunordps
+; CHECK: por
+}
+
+define <4 x i32> @fcmpUgeVector(<4 x float> %a, <4 x float> %b) {
+entry:
+  %res.trunc = fcmp uge <4 x float> %a, %b
+  %res = sext <4 x i1> %res.trunc to <4 x i32>
+  ret <4 x i32> %res
+; CHECK-LABEL: fcmpUgeVector:
+; CHECK: cmpnltps
+}
+
+define <4 x i32> @fcmpUgtVector(<4 x float> %a, <4 x float> %b) {
+entry:
+  %res.trunc = fcmp ugt <4 x float> %a, %b
+  %res = sext <4 x i1> %res.trunc to <4 x i32>
+  ret <4 x i32> %res
+; CHECK-LABEL: fcmpUgtVector:
+; CHECK: cmpnleps
+}
+
+define <4 x i32> @fcmpUleVector(<4 x float> %a, <4 x float> %b) {
+entry:
+  %res.trunc = fcmp ule <4 x float> %a, %b
+  %res = sext <4 x i1> %res.trunc to <4 x i32>
+  ret <4 x i32> %res
+; CHECK-LABEL: fcmpUleVector:
+; CHECK: cmpnltps
+}
+
+define <4 x i32> @fcmpUltVector(<4 x float> %a, <4 x float> %b) {
+entry:
+  %res.trunc = fcmp ult <4 x float> %a, %b
+  %res = sext <4 x i1> %res.trunc to <4 x i32>
+  ret <4 x i32> %res
+; CHECK-LABEL: fcmpUltVector:
+; CHECK: cmpnleps
+}
+
+define <4 x i32> @fcmpUneVector(<4 x float> %a, <4 x float> %b) {
+entry:
+  %res.trunc = fcmp une <4 x float> %a, %b
+  %res = sext <4 x i1> %res.trunc to <4 x i32>
+  ret <4 x i32> %res
+; CHECK-LABEL: fcmpUneVector:
+; CHECK: cmpneqps
+}
+
+define <4 x i32> @fcmpUnoVector(<4 x float> %a, <4 x float> %b) {
+entry:
+  %res.trunc = fcmp uno <4 x float> %a, %b
+  %res = sext <4 x i1> %res.trunc to <4 x i32>
+  ret <4 x i32> %res
+; CHECK-LABEL: fcmpUnoVector:
+; CHECK: cmpunordps
+}
diff --git a/crosstest/test_fcmp_main.cpp b/crosstest/test_fcmp_main.cpp
index 0c98c0a..f04b2e5 100644
--- a/crosstest/test_fcmp_main.cpp
+++ b/crosstest/test_fcmp_main.cpp
@@ -1,22 +1,42 @@
+//===- subzero/crosstest/test_fcmp_main.cpp - Driver for tests ------------===//
+//
+//                        The Subzero Code Generator
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Driver for cross testing the fcmp bitcode instruction
+//
+//===----------------------------------------------------------------------===//
+
 /* crosstest.py --test=test_fcmp.pnacl.ll --driver=test_fcmp_main.cpp \
    --prefix=Subzero_ --output=test_fcmp */
 
 #include <cassert>
 #include <cfloat>
 #include <cmath>
+#include <cstring>
 #include <iostream>
 
+#include "vectors.h"
 #include "test_fcmp.def"
 
 #define X(cmp)                                                                 \
   extern "C" bool fcmp##cmp##Float(float a, float b);                          \
   extern "C" bool fcmp##cmp##Double(double a, double b);                       \
+  extern "C" v4si32 fcmp##cmp##Vector(v4f32 a, v4f32 b);                       \
   extern "C" bool Subzero_fcmp##cmp##Float(float a, float b);                  \
-  extern "C" bool Subzero_fcmp##cmp##Double(double a, double b);
+  extern "C" bool Subzero_fcmp##cmp##Double(double a, double b);               \
+  extern "C" v4si32 Subzero_fcmp##cmp##Vector(v4f32 a, v4f32 b);
 FCMP_TABLE;
 #undef X
 
-int main(int argc, char **argv) {
+volatile double *Values;
+size_t NumValues;
+
+void initializeValues() {
   static const double NegInf = -1.0 / 0.0;
   static const double Zero = 0.0;
   static const double Ten = 10.0;
@@ -30,12 +50,14 @@
   assert(NegInf < Zero);
   assert(NegInf < PosInf);
   assert(Zero < PosInf);
+  static volatile double InitValues[] = {NegInf,  -Zero, Zero,    DBL_MIN,
+                                         FLT_MIN, Ten,   FLT_MAX, DBL_MAX,
+                                         PosInf,  Nan,   NegNan};
+  NumValues = sizeof(InitValues) / sizeof(*InitValues);
+  Values = InitValues;
+}
 
-  volatile double Values[] = { NegInf, -Zero,   Zero,    DBL_MIN, FLT_MIN,
-                               Ten,    FLT_MAX, DBL_MAX, PosInf,  Nan,
-                               NegNan };
-  const static size_t NumValues = sizeof(Values) / sizeof(*Values);
-
+void testsScalar(size_t &TotalTests, size_t &Passes, size_t &Failures) {
   typedef bool (*FuncTypeFloat)(float, float);
   typedef bool (*FuncTypeDouble)(double, double);
   static struct {
@@ -58,9 +80,7 @@
 
   bool ResultSz, ResultLlc;
 
-  size_t TotalTests = 0;
-  size_t Passes = 0;
-  size_t Failures = 0;
+  assert(Values && NumValues);
 
   for (size_t f = 0; f < NumFuncs; ++f) {
     for (size_t i = 0; i < NumValues; ++i) {
@@ -76,7 +96,7 @@
           ++Failures;
           std::cout << Funcs[f].Name << "Float(" << Value1Float << ", "
                     << Value2Float << "): sz=" << ResultSz
-                    << " llc=" << ResultLlc << std::endl;
+                    << " llc=" << ResultLlc << "\n";
         }
         ++TotalTests;
         double Value1Double = Values[i];
@@ -89,11 +109,66 @@
           ++Failures;
           std::cout << Funcs[f].Name << "Double(" << Value1Double << ", "
                     << Value2Double << "): sz=" << ResultSz
-                    << " llc=" << ResultLlc << std::endl;
+                    << " llc=" << ResultLlc << "\n";
         }
       }
     }
   }
+}
+
+void testsVector(size_t &TotalTests, size_t &Passes, size_t &Failures) {
+  typedef v4si32 (*FuncTypeVector)(v4f32, v4f32);
+  static struct {
+    const char *Name;
+    FuncTypeVector FuncVectorSz;
+    FuncTypeVector FuncVectorLlc;
+  } Funcs[] = {
+#define X(cmp)                                                                 \
+  { "fcmp" STR(cmp), Subzero_fcmp##cmp##Vector, fcmp##cmp##Vector }            \
+  ,
+        FCMP_TABLE
+#undef X
+    };
+  const static size_t NumFuncs = sizeof(Funcs) / sizeof(*Funcs);
+  const static size_t NumElementsInType = 4;
+  const static size_t MaxTestsPerFunc = 100000;
+
+  assert(Values && NumValues);
+
+  for (size_t f = 0; f < NumFuncs; ++f) {
+    PRNG Index;
+    for (size_t i = 0; i < MaxTestsPerFunc; ++i) {
+      v4f32 Value1, Value2;
+      for (size_t j = 0; j < NumElementsInType; ++j) {
+        Value1[j] = Values[Index() % NumValues];
+        Value2[j] = Values[Index() % NumValues];
+      }
+      ++TotalTests;
+      v4si32 ResultSz, ResultLlc;
+      ResultSz = Funcs[f].FuncVectorSz(Value1, Value2);
+      ResultLlc = Funcs[f].FuncVectorLlc(Value1, Value2);
+      if (!memcmp(&ResultSz, &ResultLlc, sizeof(ResultSz))) {
+        ++Passes;
+      } else {
+        ++Failures;
+        std::cout << Funcs[f].Name << "Vector(" << vectAsString<v4f32>(Value1)
+                  << ", " << vectAsString<v4f32>(Value2)
+                  << "): sz=" << vectAsString<v4si32>(ResultSz)
+                  << " llc=" << vectAsString<v4si32>(ResultLlc) << "\n";
+      }
+    }
+  }
+}
+
+int main(int argc, char **argv) {
+  size_t TotalTests = 0;
+  size_t Passes = 0;
+  size_t Failures = 0;
+
+  initializeValues();
+
+  testsScalar(TotalTests, Passes, Failures);
+  testsVector(TotalTests, Passes, Failures);
 
   std::cout << "TotalTests=" << TotalTests << " Passes=" << Passes
             << " Failures=" << Failures << "\n";
diff --git a/src/IceInstX8632.cpp b/src/IceInstX8632.cpp
index 93a872c..1698dfe 100644
--- a/src/IceInstX8632.cpp
+++ b/src/IceInstX8632.cpp
@@ -36,6 +36,18 @@
 const size_t InstX8632BrAttributesSize =
     llvm::array_lengthof(InstX8632BrAttributes);
 
+const struct InstX8632CmppsAttributes_ {
+  const char *EmitString;
+} InstX8632CmppsAttributes[] = {
+#define X(tag, emit)                                                           \
+  { emit }                                                                     \
+  ,
+    ICEINSTX8632CMPPS_TABLE
+#undef X
+  };
+const size_t InstX8632CmppsAttributesSize =
+    llvm::array_lengthof(InstX8632CmppsAttributes);
+
 const struct TypeX8632Attributes_ {
   const char *CvtString;   // i (integer), s (single FP), d (double FP)
   const char *SdSsString;  // ss, sd, or <blank>
@@ -149,6 +161,13 @@
   addSource(Source);
 }
 
+InstX8632Cmpps::InstX8632Cmpps(Cfg *Func, Variable *Dest, Operand *Source,
+                               InstX8632Cmpps::CmppsCond Condition)
+    : InstX8632(Func, InstX8632::Cmpps, 2, Dest), Condition(Condition) {
+  addSource(Dest);
+  addSource(Source);
+}
+
 InstX8632Cmpxchg::InstX8632Cmpxchg(Cfg *Func, Operand *DestOrAddr,
                                    Variable *Eax, Variable *Desired,
                                    bool Locked)
@@ -695,6 +714,28 @@
   dumpSources(Func);
 }
 
+void InstX8632Cmpps::emit(const Cfg *Func) const {
+  Ostream &Str = Func->getContext()->getStrEmit();
+  assert(getSrcSize() == 2);
+  assert(Condition < InstX8632CmppsAttributesSize);
+  Str << "\t";
+  Str << "cmp" << InstX8632CmppsAttributes[Condition].EmitString << "ps"
+      << "\t";
+  getDest()->emit(Func);
+  Str << ", ";
+  getSrc(1)->emit(Func);
+  Str << "\n";
+}
+
+void InstX8632Cmpps::dump(const Cfg *Func) const {
+  Ostream &Str = Func->getContext()->getStrDump();
+  assert(Condition < InstX8632CmppsAttributesSize);
+  dumpDest(Func);
+  Str << " = cmp" << InstX8632CmppsAttributes[Condition].EmitString << "ps"
+      << "\t";
+  dumpSources(Func);
+}
+
 void InstX8632Cmpxchg::emit(const Cfg *Func) const {
   Ostream &Str = Func->getContext()->getStrEmit();
   assert(getSrcSize() == 3);
diff --git a/src/IceInstX8632.def b/src/IceInstX8632.def
index be7aeb5..ece6a0a 100644
--- a/src/IceInstX8632.def
+++ b/src/IceInstX8632.def
@@ -66,6 +66,18 @@
   X(Br_p,        "p",  "jp")   \
 //#define X(tag, dump, emit)
 
+#define ICEINSTX8632CMPPS_TABLE \
+  /* enum value, emit */        \
+  X(Cmpps_eq,    "eq")          \
+  X(Cmpps_lt,    "lt")          \
+  X(Cmpps_le,    "le")          \
+  X(Cmpps_unord, "unord")       \
+  X(Cmpps_neq,   "neq")         \
+  X(Cmpps_nlt,   "nlt")         \
+  X(Cmpps_nle,   "nle")         \
+  X(Cmpps_ord,   "ord")         \
+//#define X(tag, emit)
+
 #define ICETYPEX8632_TABLE                                        \
   /* tag,          element type, cvt, sdss,  pack, width */       \
   X(IceType_void,  IceType_void, "?" , ""  , "" ,  "???")         \
diff --git a/src/IceInstX8632.h b/src/IceInstX8632.h
index ce1cc65..6760057 100644
--- a/src/IceInstX8632.h
+++ b/src/IceInstX8632.h
@@ -145,6 +145,7 @@
     Call,
     Cdq,
     Cmov,
+    Cmpps,
     Cmpxchg,
     Cmpxchg8b,
     Cvt,
@@ -714,6 +715,35 @@
   BrCond Condition;
 };
 
+// Cmpps instruction - compare packed singled-precision floating point
+// values
+class InstX8632Cmpps : public InstX8632 {
+public:
+  enum CmppsCond {
+#define X(tag, emit) tag,
+    ICEINSTX8632CMPPS_TABLE
+#undef X
+    Cmpps_Invalid
+  };
+
+  static InstX8632Cmpps *create(Cfg *Func, Variable *Dest, Operand *Source,
+                                CmppsCond Condition) {
+    return new (Func->allocate<InstX8632Cmpps>())
+        InstX8632Cmpps(Func, Dest, Source, Condition);
+  }
+  virtual void emit(const Cfg *Func) const;
+  virtual void dump(const Cfg *Func) const;
+  static bool classof(const Inst *Inst) { return isClassof(Inst, Cmpps); }
+
+private:
+  InstX8632Cmpps(Cfg *Func, Variable *Dest, Operand *Source, CmppsCond Cond);
+  InstX8632Cmpps(const InstX8632Cmpps &) LLVM_DELETED_FUNCTION;
+  InstX8632Cmpps &operator=(const InstX8632Cmpps &) LLVM_DELETED_FUNCTION;
+  virtual ~InstX8632Cmpps() {}
+
+  CmppsCond Condition;
+};
+
 // Cmpxchg instruction - cmpxchg <dest>, <desired> will compare if <dest>
 // equals eax. If so, the ZF is set and <desired> is stored in <dest>.
 // If not, ZF is cleared and <dest> is copied to eax (or subregister).
diff --git a/src/IceTargetLoweringX8632.cpp b/src/IceTargetLoweringX8632.cpp
index c8cf170..4a719d4 100644
--- a/src/IceTargetLoweringX8632.cpp
+++ b/src/IceTargetLoweringX8632.cpp
@@ -27,26 +27,38 @@
 
 namespace {
 
-// The following table summarizes the logic for lowering the fcmp instruction.
-// There is one table entry for each of the 16 conditions.  A comment in
-// lowerFcmp() describes the lowering template.  In the most general case, there
-// is a compare followed by two conditional branches, because some fcmp
-// conditions don't map to a single x86 conditional branch.  However, in many
-// cases it is possible to swap the operands in the comparison and have a single
-// conditional branch.  Since it's quite tedious to validate the table by hand,
-// good execution tests are helpful.
-
+// The following table summarizes the logic for lowering the fcmp
+// instruction.  There is one table entry for each of the 16 conditions.
+//
+// The first four columns describe the case when the operands are
+// floating point scalar values.  A comment in lowerFcmp() describes the
+// lowering template.  In the most general case, there is a compare
+// followed by two conditional branches, because some fcmp conditions
+// don't map to a single x86 conditional branch.  However, in many cases
+// it is possible to swap the operands in the comparison and have a
+// single conditional branch.  Since it's quite tedious to validate the
+// table by hand, good execution tests are helpful.
+//
+// The last two columns describe the case when the operands are vectors
+// of floating point values.  For most fcmp conditions, there is a clear
+// mapping to a single x86 cmpps instruction variant.  Some fcmp
+// conditions require special code to handle and these are marked in the
+// table with a Cmpps_Invalid predicate.
 const struct TableFcmp_ {
   uint32_t Default;
-  bool SwapOperands;
+  bool SwapScalarOperands;
   InstX8632::BrCond C1, C2;
+  bool SwapVectorOperands;
+  InstX8632Cmpps::CmppsCond Predicate;
 } TableFcmp[] = {
-#define X(val, dflt, swap, C1, C2)                                             \
-  { dflt, swap, InstX8632Br::C1, InstX8632Br::C2 }                             \
+#define X(val, dflt, swapS, C1, C2, swapV, pred)                               \
+  {                                                                            \
+    dflt, swapS, InstX8632Br::C1, InstX8632Br::C2, swapV, InstX8632Cmpps::pred \
+  }                                                                            \
   ,
-    FCMPX8632_TABLE
+      FCMPX8632_TABLE
 #undef X
-  };
+};
 const size_t TableFcmpSize = llvm::array_lengthof(TableFcmp);
 
 // The following table summarizes the logic for lowering the icmp instruction
@@ -138,7 +150,7 @@
     // Define a temporary set of enum values based on low-level
     // table entries.
     enum _tmp_enum {
-#define X(val, dflt, swap, C1, C2) _tmp_##val,
+#define X(val, dflt, swapS, C1, C2, swapV, pred) _tmp_##val,
       FCMPX8632_TABLE
 #undef X
           _num
@@ -149,7 +161,7 @@
 #undef X
 // Define a set of constants based on low-level table entries,
 // and ensure the table entry keys are consistent.
-#define X(val, dflt, swap, C1, C2)                                             \
+#define X(val, dflt, swapS, C1, C2, swapV, pred)                               \
   static const int _table2_##val = _tmp_##val;                                 \
   STATIC_ASSERT(_table1_##val == _table2_##val);
     FCMPX8632_TABLE;
@@ -2213,6 +2225,68 @@
   Operand *Src0 = Inst->getSrc(0);
   Operand *Src1 = Inst->getSrc(1);
   Variable *Dest = Inst->getDest();
+
+  if (isVectorType(Dest->getType())) {
+    InstFcmp::FCond Condition = Inst->getCondition();
+    size_t Index = static_cast<size_t>(Condition);
+    assert(Index < TableFcmpSize);
+
+    if (TableFcmp[Index].SwapVectorOperands) {
+      Operand *T = Src0;
+      Src0 = Src1;
+      Src1 = T;
+    }
+
+    Variable *T = NULL;
+
+    // ALIGNHACK: Without support for stack alignment, both operands to
+    // cmpps need to be forced into registers.  Once support for stack
+    // alignment is implemented, remove LEGAL_HACK.
+#define LEGAL_HACK(Vect) legalizeToVar((Vect))
+    switch (Condition) {
+    default: {
+      InstX8632Cmpps::CmppsCond Predicate = TableFcmp[Index].Predicate;
+      assert(Predicate != InstX8632Cmpps::Cmpps_Invalid);
+      T = makeReg(Src0->getType());
+      _movp(T, Src0);
+      _cmpps(T, LEGAL_HACK(Src1), Predicate);
+    } break;
+    case InstFcmp::False:
+      T = makeVectorOfZeros(Src0->getType());
+      break;
+    case InstFcmp::One: {
+      // Check both unequal and ordered.
+      T = makeReg(Src0->getType());
+      Variable *T2 = makeReg(Src0->getType());
+      Src1 = LEGAL_HACK(Src1);
+      _movp(T, Src0);
+      _cmpps(T, Src1, InstX8632Cmpps::Cmpps_neq);
+      _movp(T2, Src0);
+      _cmpps(T2, Src1, InstX8632Cmpps::Cmpps_ord);
+      _pand(T, T2);
+    } break;
+    case InstFcmp::Ueq: {
+      // Check both equal or unordered.
+      T = makeReg(Src0->getType());
+      Variable *T2 = makeReg(Src0->getType());
+      Src1 = LEGAL_HACK(Src1);
+      _movp(T, Src0);
+      _cmpps(T, Src1, InstX8632Cmpps::Cmpps_eq);
+      _movp(T2, Src0);
+      _cmpps(T2, Src1, InstX8632Cmpps::Cmpps_unord);
+      _por(T, T2);
+    } break;
+    case InstFcmp::True:
+      T = makeVectorOfMinusOnes(IceType_v4i32);
+      break;
+    }
+#undef LEGAL_HACK
+
+    _movp(Dest, T);
+    eliminateNextVectorSextInstruction(Dest);
+    return;
+  }
+
   // Lowering a = fcmp cond, b, c
   //   ucomiss b, c       /* only if C1 != Br_None */
   //                      /* but swap b,c order if SwapOperands==true */
@@ -2225,7 +2299,7 @@
   InstFcmp::FCond Condition = Inst->getCondition();
   size_t Index = static_cast<size_t>(Condition);
   assert(Index < TableFcmpSize);
-  if (TableFcmp[Index].SwapOperands) {
+  if (TableFcmp[Index].SwapScalarOperands) {
     Operand *Tmp = Src0;
     Src0 = Src1;
     Src1 = Tmp;
@@ -2356,26 +2430,7 @@
 #undef LEGAL_HACK
 
     _movp(Dest, T);
-
-    // The following pattern occurs often in lowered C and C++ code:
-    //
-    //   %cmp     = icmp pred <n x ty> %src0, %src1
-    //   %cmp.ext = sext <n x i1> %cmp to <n x ty>
-    //
-    // We can avoid the sext operation by copying the result from pcmpgt
-    // and pcmpeq, which is already sign extended, to the result of the
-    // sext operation
-    if (InstCast *NextCast =
-            llvm::dyn_cast_or_null<InstCast>(Context.getNextInst())) {
-      if (NextCast->getCastKind() == InstCast::Sext &&
-          NextCast->getSrc(0) == Dest) {
-        _movp(NextCast->getDest(), T);
-        // Skip over the instruction.
-        NextCast->setDeleted();
-        Context.advanceNext();
-      }
-    }
-
+    eliminateNextVectorSextInstruction(Dest);
     return;
   }
 
@@ -3544,6 +3599,28 @@
   _br(Inst->getLabelDefault());
 }
 
+// The following pattern occurs often in lowered C and C++ code:
+//
+//   %cmp     = fcmp/icmp pred <n x ty> %src0, %src1
+//   %cmp.ext = sext <n x i1> %cmp to <n x ty>
+//
+// We can eliminate the sext operation by copying the result of pcmpeqd,
+// pcmpgtd, or cmpps (which produce sign extended results) to the result
+// of the sext operation.
+void
+TargetX8632::eliminateNextVectorSextInstruction(Variable *SignExtendedResult) {
+  if (InstCast *NextCast =
+          llvm::dyn_cast_or_null<InstCast>(Context.getNextInst())) {
+    if (NextCast->getCastKind() == InstCast::Sext &&
+        NextCast->getSrc(0) == SignExtendedResult) {
+      _movp(NextCast->getDest(), legalizeToVar(SignExtendedResult));
+      // Skip over the instruction.
+      NextCast->setDeleted();
+      Context.advanceNext();
+    }
+  }
+}
+
 void TargetX8632::lowerUnreachable(const InstUnreachable * /*Inst*/) {
   const SizeT MaxSrcs = 0;
   Variable *Dest = NULL;
diff --git a/src/IceTargetLoweringX8632.def b/src/IceTargetLoweringX8632.def
index b88091a..5dc1d13 100644
--- a/src/IceTargetLoweringX8632.def
+++ b/src/IceTargetLoweringX8632.def
@@ -15,25 +15,26 @@
 #ifndef SUBZERO_SRC_ICETARGETLOWERINGX8632_DEF
 #define SUBZERO_SRC_ICETARGETLOWERINGX8632_DEF
 
-#define FCMPX8632_TABLE                  \
-  /* val,  dflt, swap, C1,      C2 */    \
-  X(False, 0,    0,    Br_None, Br_None) \
-  X(Oeq,   0,    0,    Br_ne,   Br_p)    \
-  X(Ogt,   1,    0,    Br_a,    Br_None) \
-  X(Oge,   1,    0,    Br_ae,   Br_None) \
-  X(Olt,   1,    1,    Br_a,    Br_None) \
-  X(Ole,   1,    1,    Br_ae,   Br_None) \
-  X(One,   1,    0,    Br_ne,   Br_None) \
-  X(Ord,   1,    0,    Br_np,   Br_None) \
-  X(Ueq,   1,    0,    Br_e,    Br_None) \
-  X(Ugt,   1,    1,    Br_b,    Br_None) \
-  X(Uge,   1,    1,    Br_be,   Br_None) \
-  X(Ult,   1,    0,    Br_b,    Br_None) \
-  X(Ule,   1,    0,    Br_be,   Br_None) \
-  X(Une,   1,    0,    Br_ne,   Br_p)    \
-  X(Uno,   1,    0,    Br_p,    Br_None) \
-  X(True,  1,    0,    Br_None, Br_None) \
-//#define X(val, dflt, swap, C1, C2)
+#define FCMPX8632_TABLE                                              \
+  /*       <---- scalar comparison ---->  <- vector comparison -> */ \
+  /* val,  dflt, swap, C1,      C2,       swap,  predicate        */ \
+  X(False, 0,    0,    Br_None, Br_None,  0,     Cmpps_Invalid)      \
+  X(Oeq,   0,    0,    Br_ne,   Br_p,     0,     Cmpps_eq)           \
+  X(Ogt,   1,    0,    Br_a,    Br_None,  1,     Cmpps_lt)           \
+  X(Oge,   1,    0,    Br_ae,   Br_None,  1,     Cmpps_le)           \
+  X(Olt,   1,    1,    Br_a,    Br_None,  0,     Cmpps_lt)           \
+  X(Ole,   1,    1,    Br_ae,   Br_None,  0,     Cmpps_le)           \
+  X(One,   1,    0,    Br_ne,   Br_None,  0,     Cmpps_Invalid)      \
+  X(Ord,   1,    0,    Br_np,   Br_None,  0,     Cmpps_ord)          \
+  X(Ueq,   1,    0,    Br_e,    Br_None,  0,     Cmpps_Invalid)      \
+  X(Ugt,   1,    1,    Br_b,    Br_None,  0,     Cmpps_nle)          \
+  X(Uge,   1,    1,    Br_be,   Br_None,  0,     Cmpps_nlt)          \
+  X(Ult,   1,    0,    Br_b,    Br_None,  1,     Cmpps_nle)          \
+  X(Ule,   1,    0,    Br_be,   Br_None,  1,     Cmpps_nlt)          \
+  X(Une,   1,    0,    Br_ne,   Br_p,     0,     Cmpps_neq)          \
+  X(Uno,   1,    0,    Br_p,    Br_None,  0,     Cmpps_unord)        \
+  X(True,  1,    0,    Br_None, Br_None,  0,     Cmpps_Invalid)      \
+//#define X(val, dflt, swapS, C1, C2, swapV, pred)
 
 #define ICMPX8632_TABLE                     \
   /* val, C_32,  C1_64,   C2_64,   C3_64 */ \
diff --git a/src/IceTargetLoweringX8632.h b/src/IceTargetLoweringX8632.h
index cbc98ce..6f09a90 100644
--- a/src/IceTargetLoweringX8632.h
+++ b/src/IceTargetLoweringX8632.h
@@ -108,6 +108,8 @@
   void expandAtomicRMWAsCmpxchg(LowerBinOp op_lo, LowerBinOp op_hi,
                                 Variable *Dest, Operand *Ptr, Operand *Val);
 
+  void eliminateNextVectorSextInstruction(Variable *SignExtendedResult);
+
   // Operand legalization helpers.  To deal with address mode
   // constraints, the helpers will create a new Operand and emit
   // instructions that guarantee that the Operand kind is one of those
@@ -212,6 +214,10 @@
   void _cmp(Operand *Src0, Operand *Src1) {
     Context.insert(InstX8632Icmp::create(Func, Src0, Src1));
   }
+  void _cmpps(Variable *Dest, Operand *Src0,
+              InstX8632Cmpps::CmppsCond Condition) {
+    Context.insert(InstX8632Cmpps::create(Func, Dest, Src0, Condition));
+  }
   void _cmpxchg(Operand *DestOrAddr, Variable *Eax, Variable *Desired,
                 bool Locked) {
     Context.insert(
diff --git a/tests_lit/llvm2ice_tests/vector-fcmp.ll b/tests_lit/llvm2ice_tests/vector-fcmp.ll
new file mode 100644
index 0000000..7fb717a
--- /dev/null
+++ b/tests_lit/llvm2ice_tests/vector-fcmp.ll
@@ -0,0 +1,178 @@
+; This file checks support for comparing vector values with the fcmp
+; instruction.
+
+; RUN: %llvm2ice -O2 --verbose none %s | FileCheck %s
+; RUN: %llvm2ice -Om1 --verbose none %s | FileCheck %s
+; RUN: %llvm2ice -O2 --verbose none %s \
+; RUN:               | llvm-mc -arch=x86 -x86-asm-syntax=intel -filetype=obj
+; RUN: %llvm2ice -Om1 --verbose none %s \
+; RUN:               | llvm-mc -arch=x86 -x86-asm-syntax=intel -filetype=obj
+; RUN: %llvm2ice --verbose none %s | FileCheck --check-prefix=ERRORS %s
+; RUN: %llvm2iceinsts %s | %szdiff %s | FileCheck --check-prefix=DUMP %s
+; RUN: %llvm2iceinsts --pnacl %s | %szdiff %s \
+; RUN:                           | FileCheck --check-prefix=DUMP %s
+
+; Check that sext elimination occurs when the result of the comparison
+; instruction is alrady sign extended.  Sign extension to 4 x i32 uses
+; the pslld instruction.
+define <4 x i32> @sextElimination(<4 x float> %a, <4 x float> %b) {
+entry:
+  %res.trunc = fcmp oeq <4 x float> %a, %b
+  %res = sext <4 x i1> %res.trunc to <4 x i32>
+  ret <4 x i32> %res
+; CHECK-LABEL: sextElimination:
+; CHECK: cmpeqps
+; CHECK-NOT: pslld
+}
+
+define <4 x i32> @fcmpFalseVector(<4 x float> %a, <4 x float> %b) {
+entry:
+  %res.trunc = fcmp false <4 x float> %a, %b
+  %res = sext <4 x i1> %res.trunc to <4 x i32>
+  ret <4 x i32> %res
+; CHECK-LABEL: fcmpFalseVector:
+; CHECK: pxor
+}
+
+define <4 x i32> @fcmpOeqVector(<4 x float> %a, <4 x float> %b) {
+entry:
+  %res.trunc = fcmp oeq <4 x float> %a, %b
+  %res = sext <4 x i1> %res.trunc to <4 x i32>
+  ret <4 x i32> %res
+; CHECK-LABEL: fcmpOeqVector:
+; CHECK: cmpeqps
+}
+
+define <4 x i32> @fcmpOgeVector(<4 x float> %a, <4 x float> %b) {
+entry:
+  %res.trunc = fcmp oge <4 x float> %a, %b
+  %res = sext <4 x i1> %res.trunc to <4 x i32>
+  ret <4 x i32> %res
+; CHECK-LABEL: fcmpOgeVector:
+; CHECK: cmpleps
+}
+
+define <4 x i32> @fcmpOgtVector(<4 x float> %a, <4 x float> %b) {
+entry:
+  %res.trunc = fcmp ogt <4 x float> %a, %b
+  %res = sext <4 x i1> %res.trunc to <4 x i32>
+  ret <4 x i32> %res
+; CHECK-LABEL: fcmpOgtVector:
+; CHECK: cmpltps
+}
+
+define <4 x i32> @fcmpOleVector(<4 x float> %a, <4 x float> %b) {
+entry:
+  %res.trunc = fcmp ole <4 x float> %a, %b
+  %res = sext <4 x i1> %res.trunc to <4 x i32>
+  ret <4 x i32> %res
+; CHECK-LABEL: fcmpOleVector:
+; CHECK: cmpleps
+}
+
+define <4 x i32> @fcmpOltVector(<4 x float> %a, <4 x float> %b) {
+entry:
+  %res.trunc = fcmp olt <4 x float> %a, %b
+  %res = sext <4 x i1> %res.trunc to <4 x i32>
+  ret <4 x i32> %res
+; CHECK-LABEL: fcmpOltVector:
+; CHECK: cmpltps
+}
+
+define <4 x i32> @fcmpOneVector(<4 x float> %a, <4 x float> %b) {
+entry:
+  %res.trunc = fcmp one <4 x float> %a, %b
+  %res = sext <4 x i1> %res.trunc to <4 x i32>
+  ret <4 x i32> %res
+; CHECK-LABEL: fcmpOneVector:
+; CHECK: cmpneqps
+; CHECK: cmpordps
+; CHECK: pand
+}
+
+define <4 x i32> @fcmpOrdVector(<4 x float> %a, <4 x float> %b) {
+entry:
+  %res.trunc = fcmp ord <4 x float> %a, %b
+  %res = sext <4 x i1> %res.trunc to <4 x i32>
+  ret <4 x i32> %res
+; CHECK-LABEL: fcmpOrdVector:
+; CHECK: cmpordps
+}
+
+define <4 x i32> @fcmpTrueVector(<4 x float> %a, <4 x float> %b) {
+entry:
+  %res.trunc = fcmp true <4 x float> %a, %b
+  %res = sext <4 x i1> %res.trunc to <4 x i32>
+  ret <4 x i32> %res
+; CHECK-LABEL: fcmpTrueVector:
+; CHECK: pcmpeqd
+}
+
+define <4 x i32> @fcmpUeqVector(<4 x float> %a, <4 x float> %b) {
+entry:
+  %res.trunc = fcmp ueq <4 x float> %a, %b
+  %res = sext <4 x i1> %res.trunc to <4 x i32>
+  ret <4 x i32> %res
+; CHECK-LABEL: fcmpUeqVector:
+; CHECK: cmpeqps
+; CHECK: cmpunordps
+; CHECK: por
+}
+
+define <4 x i32> @fcmpUgeVector(<4 x float> %a, <4 x float> %b) {
+entry:
+  %res.trunc = fcmp uge <4 x float> %a, %b
+  %res = sext <4 x i1> %res.trunc to <4 x i32>
+  ret <4 x i32> %res
+; CHECK-LABEL: fcmpUgeVector:
+; CHECK: cmpnltps
+}
+
+define <4 x i32> @fcmpUgtVector(<4 x float> %a, <4 x float> %b) {
+entry:
+  %res.trunc = fcmp ugt <4 x float> %a, %b
+  %res = sext <4 x i1> %res.trunc to <4 x i32>
+  ret <4 x i32> %res
+; CHECK-LABEL: fcmpUgtVector:
+; CHECK: cmpnleps
+}
+
+define <4 x i32> @fcmpUleVector(<4 x float> %a, <4 x float> %b) {
+entry:
+  %res.trunc = fcmp ule <4 x float> %a, %b
+  %res = sext <4 x i1> %res.trunc to <4 x i32>
+  ret <4 x i32> %res
+; CHECK-LABEL: fcmpUleVector:
+; CHECK: cmpnltps
+}
+
+define <4 x i32> @fcmpUltVector(<4 x float> %a, <4 x float> %b) {
+entry:
+  %res.trunc = fcmp ult <4 x float> %a, %b
+  %res = sext <4 x i1> %res.trunc to <4 x i32>
+  ret <4 x i32> %res
+; CHECK-LABEL: fcmpUltVector:
+; CHECK: cmpnleps
+}
+
+define <4 x i32> @fcmpUneVector(<4 x float> %a, <4 x float> %b) {
+entry:
+  %res.trunc = fcmp une <4 x float> %a, %b
+  %res = sext <4 x i1> %res.trunc to <4 x i32>
+  ret <4 x i32> %res
+; CHECK-LABEL: fcmpUneVector:
+; CHECK: cmpneqps
+}
+
+define <4 x i32> @fcmpUnoVector(<4 x float> %a, <4 x float> %b) {
+entry:
+  %res.trunc = fcmp uno <4 x float> %a, %b
+  %res = sext <4 x i1> %res.trunc to <4 x i32>
+  ret <4 x i32> %res
+; CHECK-LABEL: fcmpUnoVector:
+; CHECK: cmpunordps
+}
+
+; ERRORS-NOT: ICE translation error
+; DUMP-NOT: SZ
+