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);