Subzero, MIPS32: Floating point comparison

This patch implements lowerFcmp, for lowering floating point comparison.

R=stichnot@chromium.org

Review URL: https://codereview.chromium.org/2047043002 .

Patch from Srdjan Obucina <Srdjan.Obucina@imgtec.com>.
diff --git a/src/IceInstMIPS32.cpp b/src/IceInstMIPS32.cpp
index a1de835..c25fbe1 100644
--- a/src/IceInstMIPS32.cpp
+++ b/src/IceInstMIPS32.cpp
@@ -69,6 +69,20 @@
 template <> const char *InstMIPS32Addu::Opcode = "addu";
 template <> const char *InstMIPS32And::Opcode = "and";
 template <> const char *InstMIPS32Andi::Opcode = "andi";
+template <> const char *InstMIPS32C_eq_d::Opcode = "c.eq.d";
+template <> const char *InstMIPS32C_eq_s::Opcode = "c.eq.s";
+template <> const char *InstMIPS32C_ole_d::Opcode = "c.ole.d";
+template <> const char *InstMIPS32C_ole_s::Opcode = "c.ole.s";
+template <> const char *InstMIPS32C_olt_d::Opcode = "c.olt.d";
+template <> const char *InstMIPS32C_olt_s::Opcode = "c.olt.s";
+template <> const char *InstMIPS32C_ueq_d::Opcode = "c.ueq.d";
+template <> const char *InstMIPS32C_ueq_s::Opcode = "c.ueq.s";
+template <> const char *InstMIPS32C_ule_d::Opcode = "c.ule.d";
+template <> const char *InstMIPS32C_ule_s::Opcode = "c.ule.s";
+template <> const char *InstMIPS32C_ult_d::Opcode = "c.ult.d";
+template <> const char *InstMIPS32C_ult_s::Opcode = "c.ult.s";
+template <> const char *InstMIPS32C_un_d::Opcode = "c.un.d";
+template <> const char *InstMIPS32C_un_s::Opcode = "c.un.s";
 template <> const char *InstMIPS32Cvt_d_l::Opcode = "cvt.d.l";
 template <> const char *InstMIPS32Cvt_d_s::Opcode = "cvt.d.s";
 template <> const char *InstMIPS32Cvt_d_w::Opcode = "cvt.d.w";
@@ -89,6 +103,8 @@
 template <> const char *InstMIPS32Mflo::Opcode = "mflo";
 template <> const char *InstMIPS32Mov_d::Opcode = "mov.d";
 template <> const char *InstMIPS32Mov_s::Opcode = "mov.s";
+template <> const char *InstMIPS32Movf::Opcode = "movf";
+template <> const char *InstMIPS32Movt::Opcode = "movt";
 template <> const char *InstMIPS32Mtc1::Opcode = "mtc1";
 template <> const char *InstMIPS32Mthi::Opcode = "mthi";
 template <> const char *InstMIPS32Mtlo::Opcode = "mtlo";
diff --git a/src/IceInstMIPS32.h b/src/IceInstMIPS32.h
index 29f4aaf..2254c33 100644
--- a/src/IceInstMIPS32.h
+++ b/src/IceInstMIPS32.h
@@ -55,6 +55,7 @@
 public:
   enum OperandKindMIPS32 {
     k__Start = Operand::kTarget,
+    kFCC,
     kMem,
   };
 
@@ -69,6 +70,40 @@
       : Operand(static_cast<OperandKind>(Kind), Ty) {}
 };
 
+class OperandMIPS32FCC : public OperandMIPS32 {
+  OperandMIPS32FCC() = delete;
+  OperandMIPS32FCC(const OperandMIPS32FCC &) = delete;
+  OperandMIPS32FCC &operator=(const OperandMIPS32FCC &) = delete;
+
+public:
+  enum FCC { FCC0 = 0, FCC1, FCC2, FCC3, FCC4, FCC5, FCC6, FCC7 };
+  static OperandMIPS32FCC *create(Cfg *Func, OperandMIPS32FCC::FCC FCC) {
+    return new (Func->allocate<OperandMIPS32FCC>()) OperandMIPS32FCC(FCC);
+  }
+
+  void emit(const Cfg *Func) const override {
+    if (!BuildDefs::dump())
+      return;
+    Ostream &Str = Func->getContext()->getStrEmit();
+    Str << "$fcc" << static_cast<uint16_t>(FCC);
+  }
+
+  static bool classof(const Operand *Operand) {
+    return Operand->getKind() == static_cast<OperandKind>(kFCC);
+  }
+
+  void dump(const Cfg *Func, Ostream &Str) const override {
+    (void)Func;
+    (void)Str;
+  }
+
+private:
+  OperandMIPS32FCC(OperandMIPS32FCC::FCC FCC)
+      : OperandMIPS32(kFCC, IceType_i32), FCC(FCC){};
+
+  const OperandMIPS32FCC::FCC FCC;
+};
+
 class OperandMIPS32Mem : public OperandMIPS32 {
   OperandMIPS32Mem() = delete;
   OperandMIPS32Mem(const OperandMIPS32Mem &) = delete;
@@ -155,6 +190,20 @@
     And,
     Andi,
     Br,
+    C_eq_d,
+    C_eq_s,
+    C_ole_d,
+    C_ole_s,
+    C_olt_d,
+    C_olt_s,
+    C_ueq_d,
+    C_ueq_s,
+    C_ule_d,
+    C_ule_s,
+    C_ult_d,
+    C_ult_s,
+    C_un_d,
+    C_un_s,
     Call,
     Cvt_d_l,
     Cvt_d_s,
@@ -178,6 +227,8 @@
     Mov, // actually a pseudo op for addi rd, rs, 0
     Mov_d,
     Mov_s,
+    Movf,
+    Movt,
     Mtc1,
     Mthi,
     Mtlo,
@@ -806,6 +857,56 @@
   InstMIPS32Call(Cfg *Func, Variable *Dest, Operand *CallTarget);
 };
 
+template <InstMIPS32::InstKindMIPS32 K>
+class InstMIPS32FPCmp : public InstMIPS32 {
+  InstMIPS32FPCmp() = delete;
+  InstMIPS32FPCmp(const InstMIPS32FPCmp &) = delete;
+  InstMIPS32Call &operator=(const InstMIPS32FPCmp &) = delete;
+
+public:
+  static InstMIPS32FPCmp *create(Cfg *Func, Variable *Src0, Variable *Src1) {
+    return new (Func->allocate<InstMIPS32FPCmp>())
+        InstMIPS32FPCmp(Func, Src0, Src1);
+  }
+
+  void emit(const Cfg *Func) const override {
+    if (!BuildDefs::dump())
+      return;
+    Ostream &Str = Func->getContext()->getStrEmit();
+    assert(getSrcSize() == 2);
+    Str << "\t" << Opcode << "\t";
+    getSrc(0)->emit(Func);
+    Str << ", ";
+    getSrc(1)->emit(Func);
+  }
+
+  void emitIAS(const Cfg *Func) const override {
+    (void)Func;
+    llvm_unreachable("Not yet implemented");
+  }
+
+  void dump(const Cfg *Func) const override {
+    (void)Func;
+    if (!BuildDefs::dump())
+      return;
+    Ostream &Str = Func->getContext()->getStrDump();
+    dumpOpcode(Str, Opcode, getSrc(0)->getType());
+    Str << " ";
+    dumpSources(Func);
+  }
+
+  static bool classof(const Inst *Inst) { return isClassof(Inst, Call); }
+
+private:
+  InstMIPS32FPCmp(Cfg *Func, Variable *Src0, Variable *Src1)
+      : InstMIPS32(Func, K, 2, nullptr) {
+    addSource(Src0);
+    addSource(Src1);
+  };
+
+  static const char *Opcode;
+};
+
 template <InstMIPS32::InstKindMIPS32 K, bool Signed = false>
 class InstMIPS32Imm16 : public InstMIPS32 {
   InstMIPS32Imm16() = delete;
@@ -877,6 +978,62 @@
   const uint32_t Imm;
 };
 
+/// Conditional mov
+template <InstMIPS32::InstKindMIPS32 K>
+class InstMIPS32MovConditional : public InstMIPS32 {
+  InstMIPS32MovConditional() = delete;
+  InstMIPS32MovConditional(const InstMIPS32MovConditional &) = delete;
+  InstMIPS32MovConditional &
+  operator=(const InstMIPS32MovConditional &) = delete;
+
+public:
+  static InstMIPS32MovConditional *create(Cfg *Func, Variable *Dest,
+                                          Variable *Src, Operand *FCC) {
+    return new (Func->allocate<InstMIPS32MovConditional>())
+        InstMIPS32MovConditional(Func, Dest, Src, FCC);
+  }
+
+  void emit(const Cfg *Func) const override {
+    if (!BuildDefs::dump())
+      return;
+    Ostream &Str = Func->getContext()->getStrEmit();
+    assert(getSrcSize() == 2);
+    Str << "\t" << Opcode << "\t";
+    getDest()->emit(Func);
+    Str << ", ";
+    getSrc(0)->emit(Func);
+    Str << ", ";
+    getSrc(1)->emit(Func);
+  }
+
+  void emitIAS(const Cfg *Func) const override {
+    (void)Func;
+    llvm_unreachable("Not yet implemented");
+  }
+
+  void dump(const Cfg *Func) const override {
+    if (!BuildDefs::dump())
+      return;
+    Ostream &Str = Func->getContext()->getStrDump();
+    dumpDest(Func);
+    Str << " = ";
+    dumpOpcode(Str, Opcode, getDest()->getType());
+    Str << " ";
+    dumpSources(Func);
+  }
+  static bool classof(const Inst *Inst) { return isClassof(Inst, K); }
+
+private:
+  InstMIPS32MovConditional(Cfg *Func, Variable *Dest, Variable *Src,
+                           Operand *FCC)
+      : InstMIPS32(Func, K, 2, Dest) {
+    addSource(Src);
+    addSource(FCC);
+  }
+
+  static const char *Opcode;
+};
+
 using InstMIPS32Abs_d = InstMIPS32TwoAddrFPR<InstMIPS32::Abs_d>;
 using InstMIPS32Abs_s = InstMIPS32TwoAddrFPR<InstMIPS32::Abs_s>;
 using InstMIPS32Add = InstMIPS32ThreeAddrGPR<InstMIPS32::Add>;
@@ -886,6 +1043,20 @@
 using InstMIPS32Addiu = InstMIPS32Imm16<InstMIPS32::Addiu, true>;
 using InstMIPS32And = InstMIPS32ThreeAddrGPR<InstMIPS32::And>;
 using InstMIPS32Andi = InstMIPS32Imm16<InstMIPS32::Andi>;
+using InstMIPS32C_eq_d = InstMIPS32FPCmp<InstMIPS32::C_eq_d>;
+using InstMIPS32C_eq_s = InstMIPS32FPCmp<InstMIPS32::C_eq_s>;
+using InstMIPS32C_ole_d = InstMIPS32FPCmp<InstMIPS32::C_ole_d>;
+using InstMIPS32C_ole_s = InstMIPS32FPCmp<InstMIPS32::C_ole_s>;
+using InstMIPS32C_olt_d = InstMIPS32FPCmp<InstMIPS32::C_olt_d>;
+using InstMIPS32C_olt_s = InstMIPS32FPCmp<InstMIPS32::C_olt_s>;
+using InstMIPS32C_ueq_d = InstMIPS32FPCmp<InstMIPS32::C_ueq_d>;
+using InstMIPS32C_ueq_s = InstMIPS32FPCmp<InstMIPS32::C_ueq_s>;
+using InstMIPS32C_ule_d = InstMIPS32FPCmp<InstMIPS32::C_ule_d>;
+using InstMIPS32C_ule_s = InstMIPS32FPCmp<InstMIPS32::C_ule_s>;
+using InstMIPS32C_ult_d = InstMIPS32FPCmp<InstMIPS32::C_ult_d>;
+using InstMIPS32C_ult_s = InstMIPS32FPCmp<InstMIPS32::C_ult_s>;
+using InstMIPS32C_un_d = InstMIPS32FPCmp<InstMIPS32::C_un_d>;
+using InstMIPS32C_un_s = InstMIPS32FPCmp<InstMIPS32::C_un_s>;
 using InstMIPS32Cvt_d_s = InstMIPS32TwoAddrFPR<InstMIPS32::Cvt_d_s>;
 using InstMIPS32Cvt_d_l = InstMIPS32TwoAddrFPR<InstMIPS32::Cvt_d_l>;
 using InstMIPS32Cvt_d_w = InstMIPS32TwoAddrFPR<InstMIPS32::Cvt_d_w>;
@@ -906,6 +1077,8 @@
 using InstMIPS32Mflo = InstMIPS32UnaryopGPR<InstMIPS32::Mflo>;
 using InstMIPS32Mov_d = InstMIPS32TwoAddrFPR<InstMIPS32::Mov_d>;
 using InstMIPS32Mov_s = InstMIPS32TwoAddrFPR<InstMIPS32::Mov_s>;
+using InstMIPS32Movf = InstMIPS32MovConditional<InstMIPS32::Movf>;
+using InstMIPS32Movt = InstMIPS32MovConditional<InstMIPS32::Movt>;
 using InstMIPS32Mtc1 = InstMIPS32TwoAddrGPR<InstMIPS32::Mtc1>;
 using InstMIPS32Mthi = InstMIPS32UnaryopGPR<InstMIPS32::Mthi>;
 using InstMIPS32Mtlo = InstMIPS32UnaryopGPR<InstMIPS32::Mtlo>;
diff --git a/src/IceTargetLoweringMIPS32.cpp b/src/IceTargetLoweringMIPS32.cpp
index 622af79..6f602c9 100644
--- a/src/IceTargetLoweringMIPS32.cpp
+++ b/src/IceTargetLoweringMIPS32.cpp
@@ -2436,7 +2436,184 @@
 }
 
 void TargetMIPS32::lowerFcmp(const InstFcmp *Instr) {
-  UnimplementedLoweringError(this, Instr);
+  Variable *Dest = Instr->getDest();
+  if (isVectorType(Dest->getType())) {
+    UnimplementedLoweringError(this, Instr);
+    return;
+  }
+
+  auto *Src0 = Instr->getSrc(0);
+  auto *Src1 = Instr->getSrc(1);
+  auto *Zero = getZero();
+
+  InstFcmp::FCond Cond = Instr->getCondition();
+  auto *DestR = legalizeToReg(Dest);
+  auto *Src0R = legalizeToReg(Src0);
+  auto *Src1R = legalizeToReg(Src1);
+  const Type Src0Ty = Src0->getType();
+
+  Operand *FCC0 = OperandMIPS32FCC::create(getFunc(), OperandMIPS32FCC::FCC0);
+
+  switch (Cond) {
+  default: {
+    UnimplementedLoweringError(this, Instr);
+    return;
+  }
+  case InstFcmp::False: {
+    Context.insert<InstFakeUse>(Src0R);
+    Context.insert<InstFakeUse>(Src1R);
+    _addiu(DestR, Zero, 0);
+    _mov(Dest, DestR);
+    break;
+  }
+  case InstFcmp::Oeq: {
+    if (Src0Ty == IceType_f32) {
+      _c_eq_s(Src0R, Src1R);
+    } else {
+      _c_eq_d(Src0R, Src1R);
+    }
+    _movf(DestR, Zero, FCC0);
+    _mov(Dest, DestR);
+    break;
+  }
+  case InstFcmp::Ogt: {
+    if (Src0Ty == IceType_f32) {
+      _c_ule_s(Src0R, Src1R);
+    } else {
+      _c_ule_d(Src0R, Src1R);
+    }
+    _movt(DestR, Zero, FCC0);
+    _mov(Dest, DestR);
+    break;
+  }
+  case InstFcmp::Oge: {
+    if (Src0Ty == IceType_f32) {
+      _c_ult_s(Src0R, Src1R);
+    } else {
+      _c_ult_d(Src0R, Src1R);
+    }
+    _movt(DestR, Zero, FCC0);
+    _mov(Dest, DestR);
+    break;
+  }
+  case InstFcmp::Olt: {
+    if (Src0Ty == IceType_f32) {
+      _c_olt_s(Src0R, Src1R);
+    } else {
+      _c_olt_d(Src0R, Src1R);
+    }
+    _movf(DestR, Zero, FCC0);
+    _mov(Dest, DestR);
+    break;
+  }
+  case InstFcmp::Ole: {
+    if (Src0Ty == IceType_f32) {
+      _c_ole_s(Src0R, Src1R);
+    } else {
+      _c_ole_d(Src0R, Src1R);
+    }
+    _movf(DestR, Zero, FCC0);
+    _mov(Dest, DestR);
+    break;
+  }
+  case InstFcmp::One: {
+    if (Src0Ty == IceType_f32) {
+      _c_ueq_s(Src0R, Src1R);
+    } else {
+      _c_ueq_d(Src0R, Src1R);
+    }
+    _movt(DestR, Zero, FCC0);
+    _mov(Dest, DestR);
+    break;
+  }
+  case InstFcmp::Ord: {
+    if (Src0Ty == IceType_f32) {
+      _c_un_s(Src0R, Src1R);
+    } else {
+      _c_un_d(Src0R, Src1R);
+    }
+    _movt(DestR, Zero, FCC0);
+    _mov(Dest, DestR);
+    break;
+  }
+  case InstFcmp::Ueq: {
+    if (Src0Ty == IceType_f32) {
+      _c_ueq_s(Src0R, Src1R);
+    } else {
+      _c_ueq_d(Src0R, Src1R);
+    }
+    _movf(DestR, Zero, FCC0);
+    _mov(Dest, DestR);
+    break;
+  }
+  case InstFcmp::Ugt: {
+    if (Src0Ty == IceType_f32) {
+      _c_ole_s(Src0R, Src1R);
+    } else {
+      _c_ole_d(Src0R, Src1R);
+    }
+    _movt(DestR, Zero, FCC0);
+    _mov(Dest, DestR);
+    break;
+  }
+  case InstFcmp::Uge: {
+    if (Src0Ty == IceType_f32) {
+      _c_olt_s(Src0R, Src1R);
+    } else {
+      _c_olt_d(Src0R, Src1R);
+    }
+    _movt(DestR, Zero, FCC0);
+    _mov(Dest, DestR);
+    break;
+  }
+  case InstFcmp::Ult: {
+    if (Src0Ty == IceType_f32) {
+      _c_ult_s(Src0R, Src1R);
+    } else {
+      _c_ult_d(Src0R, Src1R);
+    }
+    _movf(DestR, Zero, FCC0);
+    _mov(Dest, DestR);
+    break;
+  }
+  case InstFcmp::Ule: {
+    if (Src0Ty == IceType_f32) {
+      _c_ule_s(Src0R, Src1R);
+    } else {
+      _c_ule_d(Src0R, Src1R);
+    }
+    _movf(DestR, Zero, FCC0);
+    _mov(Dest, DestR);
+    break;
+  }
+  case InstFcmp::Une: {
+    if (Src0Ty == IceType_f32) {
+      _c_eq_s(Src0R, Src1R);
+    } else {
+      _c_eq_d(Src0R, Src1R);
+    }
+    _movt(DestR, Zero, FCC0);
+    _mov(Dest, DestR);
+    break;
+  }
+  case InstFcmp::Uno: {
+    if (Src0Ty == IceType_f32) {
+      _c_un_s(Src0R, Src1R);
+    } else {
+      _c_un_d(Src0R, Src1R);
+    }
+    _movf(DestR, Zero, FCC0);
+    _mov(Dest, DestR);
+    break;
+  }
+  case InstFcmp::True: {
+    Context.insert<InstFakeUse>(Src0R);
+    Context.insert<InstFakeUse>(Src1R);
+    _addiu(DestR, Zero, 1);
+    _mov(Dest, DestR);
+    break;
+  }
+  }
 }
 
 void TargetMIPS32::lower64Icmp(const InstIcmp *Instr) {
diff --git a/src/IceTargetLoweringMIPS32.h b/src/IceTargetLoweringMIPS32.h
index 3a83b3d..5b722f6 100644
--- a/src/IceTargetLoweringMIPS32.h
+++ b/src/IceTargetLoweringMIPS32.h
@@ -207,6 +207,62 @@
     Context.insert<InstMIPS32Addiu>(Dest, Src, Imm);
   }
 
+  void _c_eq_d(Variable *Src0, Variable *Src1) {
+    Context.insert<InstMIPS32C_eq_d>(Src0, Src1);
+  }
+
+  void _c_eq_s(Variable *Src0, Variable *Src1) {
+    Context.insert<InstMIPS32C_eq_s>(Src0, Src1);
+  }
+
+  void _c_ole_d(Variable *Src0, Variable *Src1) {
+    Context.insert<InstMIPS32C_ole_d>(Src0, Src1);
+  }
+
+  void _c_ole_s(Variable *Src0, Variable *Src1) {
+    Context.insert<InstMIPS32C_ole_s>(Src0, Src1);
+  }
+
+  void _c_olt_d(Variable *Src0, Variable *Src1) {
+    Context.insert<InstMIPS32C_olt_d>(Src0, Src1);
+  }
+
+  void _c_olt_s(Variable *Src0, Variable *Src1) {
+    Context.insert<InstMIPS32C_olt_s>(Src0, Src1);
+  }
+
+  void _c_ueq_d(Variable *Src0, Variable *Src1) {
+    Context.insert<InstMIPS32C_ueq_d>(Src0, Src1);
+  }
+
+  void _c_ueq_s(Variable *Src0, Variable *Src1) {
+    Context.insert<InstMIPS32C_ueq_s>(Src0, Src1);
+  }
+
+  void _c_ule_d(Variable *Src0, Variable *Src1) {
+    Context.insert<InstMIPS32C_ule_d>(Src0, Src1);
+  }
+
+  void _c_ule_s(Variable *Src0, Variable *Src1) {
+    Context.insert<InstMIPS32C_ule_s>(Src0, Src1);
+  }
+
+  void _c_ult_d(Variable *Src0, Variable *Src1) {
+    Context.insert<InstMIPS32C_ult_d>(Src0, Src1);
+  }
+
+  void _c_ult_s(Variable *Src0, Variable *Src1) {
+    Context.insert<InstMIPS32C_ult_s>(Src0, Src1);
+  }
+
+  void _c_un_d(Variable *Src0, Variable *Src1) {
+    Context.insert<InstMIPS32C_un_d>(Src0, Src1);
+  }
+
+  void _c_un_s(Variable *Src0, Variable *Src1) {
+    Context.insert<InstMIPS32C_un_s>(Src0, Src1);
+  }
+
   void _cvt_d_l(Variable *Dest, Variable *Src) {
     Context.insert<InstMIPS32Cvt_d_l>(Dest, Src);
   }
@@ -287,6 +343,14 @@
     Context.insert<InstMIPS32Mov_s>(Dest, Src);
   }
 
+  void _movf(Variable *Src0, Variable *Src1, Operand *FCC) {
+    Context.insert<InstMIPS32Movf>(Src0, Src1, FCC);
+  }
+
+  void _movt(Variable *Src0, Variable *Src1, Operand *FCC) {
+    Context.insert<InstMIPS32Movt>(Src0, Src1, FCC);
+  }
+
   void _mfc1(Variable *Dest, Variable *Src) {
     Context.insert<InstMIPS32Mfc1>(Dest, Src);
   }
@@ -483,6 +547,14 @@
     return makeReg(IceType_i32, RegNum);
   }
 
+  Variable *F32Reg(RegNumT RegNum = RegNumT()) {
+    return makeReg(IceType_f32, RegNum);
+  }
+
+  Variable *F64Reg(RegNumT RegNum = RegNumT()) {
+    return makeReg(IceType_f64, RegNum);
+  }
+
   static Type stackSlotType();
   Variable *copyToReg(Operand *Src, RegNumT RegNum = RegNumT());