This patch provides calling convention class for MIPS with support of integer and floating point types

R=stichnot@chromium.org

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

Patch from Mohit Bhakkad <mohit.bhakkad@imgtec.com>.
diff --git a/src/IceInstMIPS32.def b/src/IceInstMIPS32.def
index 6120a47..b2fed3f 100644
--- a/src/IceInstMIPS32.def
+++ b/src/IceInstMIPS32.def
@@ -111,38 +111,70 @@
 #define REGMIPS32_FPR_TABLE                                                    \
   /* val, encode, name, scratch, preserved, stackptr, frameptr,                \
      isInt, isI64Pair, isFP32, isFP64, isVec128, alias_init */                 \
-  X(Reg_F0,       0,   "f0",    1,0,0,0, 0,0,1,0,0, ALIASES1(Reg_F0))          \
-  X(Reg_F1,       1,   "f1",    1,0,0,0, 0,0,1,0,0, ALIASES1(Reg_F1))          \
-  X(Reg_F2,       2,   "f2",    1,0,0,0, 0,0,1,0,0, ALIASES1(Reg_F2))          \
-  X(Reg_F3,       3,   "f3",    1,0,0,0, 0,0,1,0,0, ALIASES1(Reg_F3))          \
-  X(Reg_F4,       4,   "f4",    1,0,0,0, 0,0,1,0,0, ALIASES1(Reg_F4))          \
-  X(Reg_F5,       5,   "f5",    1,0,0,0, 0,0,1,0,0, ALIASES1(Reg_F5))          \
-  X(Reg_F6,       6,   "f6",    1,0,0,0, 0,0,1,0,0, ALIASES1(Reg_F6))          \
-  X(Reg_F7,       7,   "f7",    1,0,0,0, 0,0,1,0,0, ALIASES1(Reg_F7))          \
-  X(Reg_F8,       8,   "f8",    1,0,0,0, 0,0,1,0,0, ALIASES1(Reg_F8))          \
-  X(Reg_F9,       9,   "f9",    1,0,0,0, 0,0,1,0,0, ALIASES1(Reg_F9))          \
-  X(Reg_F10,      10,  "f10",   1,0,0,0, 0,0,1,0,0, ALIASES1(Reg_F10))         \
-  X(Reg_F11,      11,  "f11",   1,0,0,0, 0,0,1,0,0, ALIASES1(Reg_F11))         \
-  X(Reg_F12,      12,  "f12",   1,0,0,0, 0,0,1,0,0, ALIASES1(Reg_F12))         \
-  X(Reg_F13,      13,  "f13",   1,0,0,0, 0,0,1,0,0, ALIASES1(Reg_F13))         \
-  X(Reg_F14,      14,  "f14",   1,0,0,0, 0,0,1,0,0, ALIASES1(Reg_F14))         \
-  X(Reg_F15,      15,  "f15",   1,0,0,0, 0,0,1,0,0, ALIASES1(Reg_F15))         \
-  X(Reg_F16,      16,  "f16",   1,0,0,0, 0,0,1,0,0, ALIASES1(Reg_F16))         \
-  X(Reg_F17,      17,  "f17",   1,0,0,0, 0,0,1,0,0, ALIASES1(Reg_F17))         \
-  X(Reg_F18,      18,  "f18",   1,0,0,0, 0,0,1,0,0, ALIASES1(Reg_F18))         \
-  X(Reg_F19,      19,  "f19",   1,0,0,0, 0,0,1,0,0, ALIASES1(Reg_F19))         \
-  X(Reg_F20,      20,  "f20",   1,0,0,0, 0,0,1,0,0, ALIASES1(Reg_F20))         \
-  X(Reg_F21,      21,  "f21",   1,0,0,0, 0,0,1,0,0, ALIASES1(Reg_F21))         \
-  X(Reg_F22,      22,  "f22",   1,0,0,0, 0,0,1,0,0, ALIASES1(Reg_F22))         \
-  X(Reg_F23,      23,  "f23",   1,0,0,0, 0,0,1,0,0, ALIASES1(Reg_F23))         \
-  X(Reg_F24,      24,  "f24",   1,0,0,0, 0,0,1,0,0, ALIASES1(Reg_F24))         \
-  X(Reg_F25,      25,  "f25",   1,0,0,0, 0,0,1,0,0, ALIASES1(Reg_F25))         \
-  X(Reg_F26,      26,  "f26",   1,0,0,0, 0,0,1,0,0, ALIASES1(Reg_F26))         \
-  X(Reg_F27,      27,  "f27",   1,0,0,0, 0,0,1,0,0, ALIASES1(Reg_F27))         \
-  X(Reg_F28,      28,  "f28",   1,0,0,0, 0,0,1,0,0, ALIASES1(Reg_F28))         \
-  X(Reg_F29,      29,  "f29",   1,0,0,0, 0,0,1,0,0, ALIASES1(Reg_F29))         \
-  X(Reg_F30,      30,  "f30",   1,0,0,0, 0,0,1,0,0, ALIASES1(Reg_F30))         \
-  X(Reg_F31,      31,  "f31",   1,0,0,0, 0,0,1,0,0, ALIASES1(Reg_F31))
+  X(Reg_F0,       0,   "f0",    1,0,0,0, 0,0,1,0,0,                            \
+    ALIASES2(Reg_F0, Reg_F0F1))                                                \
+  X(Reg_F1,       1,   "f1",    1,0,0,0, 0,0,1,0,0,                            \
+    ALIASES2(Reg_F1, Reg_F0F1))                                                \
+  X(Reg_F2,       2,   "f2",    1,0,0,0, 0,0,1,0,0,                            \
+    ALIASES2(Reg_F2, Reg_F2F3))                                                \
+  X(Reg_F3,       3,   "f3",    1,0,0,0, 0,0,1,0,0,                            \
+    ALIASES2(Reg_F3, Reg_F2F3))                                                \
+  X(Reg_F4,       4,   "f4",    1,0,0,0, 0,0,1,0,0,                            \
+    ALIASES2(Reg_F4, Reg_F4F5))                                                \
+  X(Reg_F5,       5,   "f5",    1,0,0,0, 0,0,1,0,0,                            \
+    ALIASES2(Reg_F5, Reg_F4F5))                                                \
+  X(Reg_F6,       6,   "f6",    1,0,0,0, 0,0,1,0,0,                            \
+    ALIASES2(Reg_F6, Reg_F6F7))                                                \
+  X(Reg_F7,       7,   "f7",    1,0,0,0, 0,0,1,0,0,                            \
+    ALIASES2(Reg_F7, Reg_F6F7))                                                \
+  X(Reg_F8,       8,   "f8",    1,0,0,0, 0,0,1,0,0,                            \
+    ALIASES2(Reg_F8, Reg_F8F9))                                                \
+  X(Reg_F9,       9,   "f9",    1,0,0,0, 0,0,1,0,0,                            \
+    ALIASES2(Reg_F9, Reg_F8F9))                                                \
+  X(Reg_F10,      10,  "f10",   1,0,0,0, 0,0,1,0,0,                            \
+    ALIASES2(Reg_F10, Reg_F10F11))                                             \
+  X(Reg_F11,      11,  "f11",   1,0,0,0, 0,0,1,0,0,                            \
+    ALIASES2(Reg_F11, Reg_F10F11))                                             \
+  X(Reg_F12,      12,  "f12",   1,0,0,0, 0,0,1,0,0,                            \
+    ALIASES2(Reg_F12, Reg_F12F13))                                             \
+  X(Reg_F13,      13,  "f13",   1,0,0,0, 0,0,1,0,0,                            \
+    ALIASES2(Reg_F13, Reg_F12F13))                                             \
+  X(Reg_F14,      14,  "f14",   1,0,0,0, 0,0,1,0,0,                            \
+    ALIASES2(Reg_F14, Reg_F14F15))                                             \
+  X(Reg_F15,      15,  "f15",   1,0,0,0, 0,0,1,0,0,                            \
+    ALIASES2(Reg_F15, Reg_F14F15))                                             \
+  X(Reg_F16,      16,  "f16",   1,0,0,0, 0,0,1,0,0,                            \
+    ALIASES2(Reg_F16, Reg_F16F17))                                             \
+  X(Reg_F17,      17,  "f17",   1,0,0,0, 0,0,1,0,0,                            \
+    ALIASES2(Reg_F17, Reg_F16F17))                                             \
+  X(Reg_F18,      18,  "f18",   1,0,0,0, 0,0,1,0,0,                            \
+    ALIASES2(Reg_F18, Reg_F18F19))                                             \
+  X(Reg_F19,      19,  "f19",   1,0,0,0, 0,0,1,0,0,                            \
+    ALIASES2(Reg_F19, Reg_F18F19))                                             \
+  X(Reg_F20,      20,  "f20",   1,0,0,0, 0,0,1,0,0,                            \
+    ALIASES2(Reg_F20, Reg_F20F21))                                             \
+  X(Reg_F21,      21,  "f21",   1,0,0,0, 0,0,1,0,0,                            \
+    ALIASES2(Reg_F21, Reg_F20F21))                                             \
+  X(Reg_F22,      22,  "f22",   1,0,0,0, 0,0,1,0,0,                            \
+    ALIASES2(Reg_F22, Reg_F22F23))                                             \
+  X(Reg_F23,      23,  "f23",   1,0,0,0, 0,0,1,0,0,                            \
+    ALIASES2(Reg_F23, Reg_F22F23))                                             \
+  X(Reg_F24,      24,  "f24",   1,0,0,0, 0,0,1,0,0,                            \
+    ALIASES2(Reg_F24, Reg_F24F25))                                             \
+  X(Reg_F25,      25,  "f25",   1,0,0,0, 0,0,1,0,0,                            \
+    ALIASES2(Reg_F25, Reg_F24F25))                                             \
+  X(Reg_F26,      26,  "f26",   1,0,0,0, 0,0,1,0,0,                            \
+    ALIASES2(Reg_F26, Reg_F26F27))                                             \
+  X(Reg_F27,      27,  "f27",   1,0,0,0, 0,0,1,0,0,                            \
+    ALIASES2(Reg_F27, Reg_F26F27))                                             \
+  X(Reg_F28,      28,  "f28",   1,0,0,0, 0,0,1,0,0,                            \
+    ALIASES2(Reg_F28, Reg_F28F29))                                             \
+  X(Reg_F29,      29,  "f29",   1,0,0,0, 0,0,1,0,0,                            \
+    ALIASES2(Reg_F29, Reg_F28F29))                                             \
+  X(Reg_F30,      30,  "f30",   1,0,0,0, 0,0,1,0,0,                            \
+    ALIASES2(Reg_F30, Reg_F30F31))                                             \
+  X(Reg_F31,      31,  "f31",   1,0,0,0, 0,0,1,0,0,                            \
+    ALIASES2(Reg_F31, Reg_F30F31))                                             \
 
 //#define X(val, encode, name, scratch, preserved, stackptr, frameptr,
 //          isInt, isI64Pair, isFP32, isFP64, isVec128, alias_init)
diff --git a/src/IceRegistersMIPS32.h b/src/IceRegistersMIPS32.h
index f6ea38e..2e39ce6 100644
--- a/src/IceRegistersMIPS32.h
+++ b/src/IceRegistersMIPS32.h
@@ -89,6 +89,24 @@
 
 const char *getRegName(RegNumT RegNum);
 
+static inline RegNumT getI64PairFirstGPRNum(RegNumT RegNum) {
+  // For now it works only for argument register pairs
+  // TODO(mohit.bhakkad): Change this to support all the register pairs once we
+  // have table-driven approach ready
+  assert(RegNum == Reg_A0A1 || RegNum == Reg_A2A3);
+  return (RegNum == RegMIPS32::Reg_A0A1) ? RegMIPS32::Reg_A0
+                                         : RegMIPS32::Reg_A2;
+}
+
+static inline RegNumT getI64PairSecondGPRNum(RegNumT RegNum) {
+  // For now it works only for argument register pairs
+  // TODO(mohit.bhakkad): Change this to support all the register pairs once we
+  // have table-driven approach ready
+  assert(RegNum == Reg_A0A1 || RegNum == Reg_A2A3);
+  return (RegNum == RegMIPS32::Reg_A0A1) ? RegMIPS32::Reg_A1
+                                         : RegMIPS32::Reg_A3;
+}
+
 } // end of namespace RegMIPS32
 
 // Extend enum RegClass with MIPS32-specific register classes (if any).
diff --git a/src/IceTargetLoweringMIPS32.cpp b/src/IceTargetLoweringMIPS32.cpp
index 2ac65c8..68655de 100644
--- a/src/IceTargetLoweringMIPS32.cpp
+++ b/src/IceTargetLoweringMIPS32.cpp
@@ -64,6 +64,14 @@
 // The maximum number of arguments to pass in GPR registers.
 constexpr uint32_t MIPS32_MAX_GPR_ARG = 4;
 
+std::array<RegNumT, MIPS32_MAX_GPR_ARG> GPRArgInitializer;
+std::array<RegNumT, MIPS32_MAX_GPR_ARG / 2> I64ArgInitializer;
+
+constexpr uint32_t MIPS32_MAX_FP_ARG = 2;
+
+std::array<RegNumT, MIPS32_MAX_FP_ARG> FP32ArgInitializer;
+std::array<RegNumT, MIPS32_MAX_FP_ARG> FP64ArgInitializer;
+
 const char *getRegClassName(RegClass C) {
   auto ClassNum = static_cast<RegClassMIPS32>(C);
   assert(ClassNum < RCMIPS32_NUM);
@@ -105,6 +113,20 @@
   assert(RegisterAliases[RegMIPS32::val][RegMIPS32::val]);
   REGMIPS32_TABLE;
 #undef X
+
+  // TODO(mohit.bhakkad): Change these inits once we provide argument related
+  // field in register tables
+  for (size_t i = 0; i < MIPS32_MAX_GPR_ARG; i++)
+    GPRArgInitializer[i] = RegNumT::fixme(RegMIPS32::Reg_A0 + i);
+
+  for (size_t i = 0; i < MIPS32_MAX_GPR_ARG / 2; i++)
+    I64ArgInitializer[i] = RegNumT::fixme(RegMIPS32::Reg_A0A1 + i);
+
+  for (size_t i = 0; i < MIPS32_MAX_FP_ARG; i++) {
+    FP32ArgInitializer[i] = RegNumT::fixme(RegMIPS32::Reg_F12 + i * 2);
+    FP64ArgInitializer[i] = RegNumT::fixme(RegMIPS32::Reg_F12F13 + i);
+  }
+
   TypeToRegisterSet[IceType_void] = InvalidRegisters;
   TypeToRegisterSet[IceType_i1] = IntegerRegisters;
   TypeToRegisterSet[IceType_i8] = IntegerRegisters;
@@ -406,6 +428,135 @@
   UnimplementedError(getFlags());
 }
 
+TargetMIPS32::CallingConv::CallingConv()
+    : GPRegsUsed(RegMIPS32::Reg_NUM),
+      GPRArgs(GPRArgInitializer.rbegin(), GPRArgInitializer.rend()),
+      I64Args(I64ArgInitializer.rbegin(), I64ArgInitializer.rend()),
+      VFPRegsUsed(RegMIPS32::Reg_NUM),
+      FP32Args(FP32ArgInitializer.rbegin(), FP32ArgInitializer.rend()),
+      FP64Args(FP64ArgInitializer.rbegin(), FP64ArgInitializer.rend()) {}
+
+// In MIPS O32 abi FP argument registers can be used only if first argument is
+// of type float/double. UseFPRegs flag is used to care of that. Also FP arg
+// registers can be used only for first 2 arguments, so we require argument
+// number to make register allocation decisions.
+bool TargetMIPS32::CallingConv::argInReg(Type Ty, uint32_t ArgNo,
+                                         RegNumT *Reg) {
+  if (isScalarIntegerType(Ty))
+    return argInGPR(Ty, Reg);
+  if (isScalarFloatingType(Ty)) {
+    if (ArgNo == 0) {
+      UseFPRegs = true;
+      return argInVFP(Ty, Reg);
+    }
+    if (UseFPRegs && ArgNo == 1) {
+      UseFPRegs = false;
+      return argInVFP(Ty, Reg);
+    }
+    return argInGPR(Ty, Reg);
+  }
+  UnimplementedError(getFlags());
+  return false;
+}
+
+bool TargetMIPS32::CallingConv::argInGPR(Type Ty, RegNumT *Reg) {
+  CfgVector<RegNumT> *Source;
+
+  switch (Ty) {
+  default: {
+    UnimplementedError(getFlags());
+    return false;
+  } break;
+  case IceType_i32:
+  case IceType_f32: {
+    Source = &GPRArgs;
+  } break;
+  case IceType_i64:
+  case IceType_f64: {
+    Source = &I64Args;
+  } break;
+  }
+
+  discardUnavailableGPRsAndTheirAliases(Source);
+
+  if (Source->empty()) {
+    GPRegsUsed.set();
+    return false;
+  }
+
+  *Reg = Source->back();
+  // Note that we don't Source->pop_back() here. This is intentional. Notice how
+  // we mark all of Reg's aliases as Used. So, for the next argument,
+  // Source->back() is marked as unavailable, and it is thus implicitly popped
+  // from the stack.
+  GPRegsUsed |= RegisterAliases[*Reg];
+  return true;
+}
+
+inline void TargetMIPS32::CallingConv::discardNextGPRAndItsAliases(
+    CfgVector<RegNumT> *Regs) {
+  GPRegsUsed |= RegisterAliases[Regs->back()];
+  Regs->pop_back();
+}
+
+// GPR are not packed when passing parameters. Thus, a function foo(i32, i64,
+// i32) will have the first argument in a0, the second in a2-a3, and the third
+// on the stack. To model this behavior, whenever we pop a register from Regs,
+// we remove all of its aliases from the pool of available GPRs. This has the
+// effect of computing the "closure" on the GPR registers.
+void TargetMIPS32::CallingConv::discardUnavailableGPRsAndTheirAliases(
+    CfgVector<RegNumT> *Regs) {
+  while (!Regs->empty() && GPRegsUsed[Regs->back()]) {
+    discardNextGPRAndItsAliases(Regs);
+  }
+}
+
+bool TargetMIPS32::CallingConv::argInVFP(Type Ty, RegNumT *Reg) {
+  CfgVector<RegNumT> *Source;
+
+  switch (Ty) {
+  default: {
+    UnimplementedError(getFlags());
+    return false;
+  } break;
+  case IceType_f32: {
+    Source = &FP32Args;
+  } break;
+  case IceType_f64: {
+    Source = &FP64Args;
+  } break;
+  }
+
+  discardUnavailableVFPRegsAndTheirAliases(Source);
+
+  if (Source->empty()) {
+    VFPRegsUsed.set();
+    return false;
+  }
+
+  *Reg = Source->back();
+  VFPRegsUsed |= RegisterAliases[*Reg];
+
+  // In MIPS O32 abi if fun arguments are (f32, i32) then one can not use reg_a0
+  // for second argument even though it's free. f32 arg goes in reg_f12, i32 arg
+  // goes in reg_a1. Similarly if arguments are (f64, i32) second argument goes
+  // in reg_a3 and a0, a1 are not used.
+  Source = &GPRArgs;
+  // Discard one GPR reg for f32(4 bytes), two for f64(4 + 4 bytes)
+  discardNextGPRAndItsAliases(Source);
+  if (Ty == IceType_f64)
+    discardNextGPRAndItsAliases(Source);
+
+  return true;
+}
+
+void TargetMIPS32::CallingConv::discardUnavailableVFPRegsAndTheirAliases(
+    CfgVector<RegNumT> *Regs) {
+  while (!Regs->empty() && VFPRegsUsed[Regs->back()]) {
+    Regs->pop_back();
+  }
+}
+
 void TargetMIPS32::lowerArguments() {
   VarList &Args = Func->getArgs();
   // We are only handling integer registers for now. The Mips o32 ABI is
diff --git a/src/IceTargetLoweringMIPS32.h b/src/IceTargetLoweringMIPS32.h
index 3366dce..4348a7c 100644
--- a/src/IceTargetLoweringMIPS32.h
+++ b/src/IceTargetLoweringMIPS32.h
@@ -459,6 +459,43 @@
 
   Operand *legalizeUndef(Operand *From, RegNumT RegNum = RegNumT());
 
+  /// Helper class that understands the Calling Convention and register
+  /// assignments as per MIPS O32 abi.
+  class CallingConv {
+    CallingConv(const CallingConv &) = delete;
+    CallingConv &operator=(const CallingConv &) = delete;
+
+  public:
+    CallingConv();
+    ~CallingConv() = default;
+
+    /// argInReg returns true if there is a Register available for the requested
+    /// type, and false otherwise. If it returns true, Reg is set to the
+    /// appropriate register number. Note that, when Ty == IceType_i64, Reg will
+    /// be an I64 register pair.
+    bool argInReg(Type Ty, uint32_t ArgNo, RegNumT *Reg);
+
+  private:
+    // argInGPR is used to find if any GPR register is available for argument of
+    // type Ty
+    bool argInGPR(Type Ty, RegNumT *Reg);
+    /// argInVFP is to floating-point/vector types what argInGPR is for integer
+    /// types.
+    bool argInVFP(Type Ty, RegNumT *Reg);
+    inline void discardNextGPRAndItsAliases(CfgVector<RegNumT> *Regs);
+    void discardUnavailableGPRsAndTheirAliases(CfgVector<RegNumT> *Regs);
+    SmallBitVector GPRegsUsed;
+    CfgVector<RegNumT> GPRArgs;
+    CfgVector<RegNumT> I64Args;
+
+    void discardUnavailableVFPRegsAndTheirAliases(CfgVector<RegNumT> *Regs);
+    SmallBitVector VFPRegsUsed;
+    CfgVector<RegNumT> FP32Args;
+    CfgVector<RegNumT> FP64Args;
+    // UseFPRegs is a flag indicating if FP registers can be used
+    bool UseFPRegs = false;
+  };
+
 protected:
   explicit TargetMIPS32(Cfg *Func);