| Index: src/IceTargetLoweringMIPS32.cpp
|
| diff --git a/src/IceTargetLoweringMIPS32.cpp b/src/IceTargetLoweringMIPS32.cpp
|
| index 7cc65cf09a53432791e3f78bef59352ffc0d2ae0..a7e579e0e96b0c50b1508b0f6b969d8764fb4dcf 100644
|
| --- a/src/IceTargetLoweringMIPS32.cpp
|
| +++ b/src/IceTargetLoweringMIPS32.cpp
|
| @@ -24,6 +24,7 @@
|
| #include "IceInstMIPS32.h"
|
| #include "IceLiveness.h"
|
| #include "IceOperand.h"
|
| +#include "IcePhiLoweringImpl.h"
|
| #include "IceRegistersMIPS32.h"
|
| #include "IceTargetLoweringMIPS32.def"
|
| #include "IceUtils.h"
|
| @@ -31,21 +32,41 @@
|
|
|
| namespace Ice {
|
|
|
| +using llvm::isInt;
|
| +
|
| +namespace {
|
| +
|
| +// The maximum number of arguments to pass in GPR registers.
|
| +constexpr uint32_t MIPS32_MAX_GPR_ARG = 4;
|
| +
|
| +} // end of anonymous namespace
|
| +
|
| TargetMIPS32::TargetMIPS32(Cfg *Func) : TargetLowering(Func) {
|
| // TODO: Don't initialize IntegerRegisters and friends every time. Instead,
|
| // initialize in some sort of static initializer for the class.
|
| +
|
| llvm::SmallBitVector IntegerRegisters(RegMIPS32::Reg_NUM);
|
| - llvm::SmallBitVector FloatRegisters(RegMIPS32::Reg_NUM);
|
| + llvm::SmallBitVector I64PairRegisters(RegMIPS32::Reg_NUM);
|
| + llvm::SmallBitVector Float32Registers(RegMIPS32::Reg_NUM);
|
| + llvm::SmallBitVector Float64Registers(RegMIPS32::Reg_NUM);
|
| llvm::SmallBitVector VectorRegisters(RegMIPS32::Reg_NUM);
|
| llvm::SmallBitVector InvalidRegisters(RegMIPS32::Reg_NUM);
|
| ScratchRegs.resize(RegMIPS32::Reg_NUM);
|
| #define X(val, encode, name, scratch, preserved, stackptr, frameptr, isInt, \
|
| - isFP) \
|
| + isI64Pair, isFP32, isFP64, isVec128, alias_init) \
|
| IntegerRegisters[RegMIPS32::val] = isInt; \
|
| - FloatRegisters[RegMIPS32::val] = isFP; \
|
| - VectorRegisters[RegMIPS32::val] = isFP; \
|
| + I64PairRegisters[RegMIPS32::val] = isI64Pair; \
|
| + Float32Registers[RegMIPS32::val] = isFP32; \
|
| + Float64Registers[RegMIPS32::val] = isFP64; \
|
| + VectorRegisters[RegMIPS32::val] = isVec128; \
|
| + RegisterAliases[RegMIPS32::val].resize(RegMIPS32::Reg_NUM); \
|
| + for (SizeT RegAlias : alias_init) { \
|
| + assert(!RegisterAliases[RegMIPS32::val][RegAlias] && \
|
| + "Duplicate alias for " #val); \
|
| + RegisterAliases[RegMIPS32::val].set(RegAlias); \
|
| + } \
|
| RegisterAliases[RegMIPS32::val].resize(RegMIPS32::Reg_NUM); \
|
| - RegisterAliases[RegMIPS32::val].set(RegMIPS32::val); \
|
| + assert(RegisterAliases[RegMIPS32::val][RegMIPS32::val]); \
|
| ScratchRegs[RegMIPS32::val] = scratch;
|
| REGMIPS32_TABLE;
|
| #undef X
|
| @@ -55,8 +76,8 @@ TargetMIPS32::TargetMIPS32(Cfg *Func) : TargetLowering(Func) {
|
| TypeToRegisterSet[IceType_i16] = IntegerRegisters;
|
| TypeToRegisterSet[IceType_i32] = IntegerRegisters;
|
| TypeToRegisterSet[IceType_i64] = IntegerRegisters;
|
| - TypeToRegisterSet[IceType_f32] = FloatRegisters;
|
| - TypeToRegisterSet[IceType_f64] = FloatRegisters;
|
| + TypeToRegisterSet[IceType_f32] = Float32Registers;
|
| + TypeToRegisterSet[IceType_f64] = Float64Registers;
|
| TypeToRegisterSet[IceType_v4i1] = VectorRegisters;
|
| TypeToRegisterSet[IceType_v8i1] = VectorRegisters;
|
| TypeToRegisterSet[IceType_v16i1] = VectorRegisters;
|
| @@ -213,7 +234,7 @@ IceString TargetMIPS32::getRegName(SizeT RegNum, Type Ty) const {
|
| (void)Ty;
|
| static const char *RegNames[] = {
|
| #define X(val, encode, name, scratch, preserved, stackptr, frameptr, isInt, \
|
| - isFP) \
|
| + isI64Pair, isFP32, isFP64, isVec128, alias_init) \
|
| name,
|
| REGMIPS32_TABLE
|
| #undef X
|
| @@ -248,39 +269,215 @@ void TargetMIPS32::emitJumpTable(const Cfg *Func,
|
| UnimplementedError(Func->getContext()->getFlags());
|
| }
|
|
|
| +/// Provide a trivial wrapper to legalize() for this common usage.
|
| +Variable *TargetMIPS32::legalizeToReg(Operand *From, int32_t RegNum) {
|
| + return llvm::cast<Variable>(legalize(From, Legal_Reg, RegNum));
|
| +}
|
| +
|
| +/// Legalize undef values to concrete values.
|
| +Operand *TargetMIPS32::legalizeUndef(Operand *From, int32_t RegNum) {
|
| + (void)RegNum;
|
| + Type Ty = From->getType();
|
| + if (llvm::isa<ConstantUndef>(From)) {
|
| + // Lower undefs to zero. Another option is to lower undefs to an
|
| + // uninitialized register; however, using an uninitialized register
|
| + // results in less predictable code.
|
| + //
|
| + // If in the future the implementation is changed to lower undef
|
| + // values to uninitialized registers, a FakeDef will be needed:
|
| + // Context.insert(InstFakeDef::create(Func, Reg));
|
| + // This is in order to ensure that the live range of Reg is not
|
| + // overestimated. If the constant being lowered is a 64 bit value,
|
| + // then the result should be split and the lo and hi components will
|
| + // need to go in uninitialized registers.
|
| + if (isVectorType(Ty))
|
| + UnimplementedError(Func->getContext()->getFlags());
|
| + return Ctx->getConstantZero(Ty);
|
| + }
|
| + return From;
|
| +}
|
| +
|
| +Variable *TargetMIPS32::makeReg(Type Type, int32_t RegNum) {
|
| + // There aren't any 64-bit integer registers for Mips32.
|
| + assert(Type != IceType_i64);
|
| + Variable *Reg = Func->makeVariable(Type);
|
| + if (RegNum == Variable::NoRegister)
|
| + Reg->setMustHaveReg();
|
| + else
|
| + Reg->setRegNum(RegNum);
|
| + return Reg;
|
| +}
|
| +
|
| void TargetMIPS32::emitVariable(const Variable *Var) const {
|
| if (!BuildDefs::dump())
|
| return;
|
| Ostream &Str = Ctx->getStrEmit();
|
| - (void)Var;
|
| - (void)Str;
|
| + const Type FrameSPTy = IceType_i32;
|
| + if (Var->hasReg()) {
|
| + Str << '$' << getRegName(Var->getRegNum(), Var->getType());
|
| + return;
|
| + } else {
|
| + int32_t Offset = Var->getStackOffset();
|
| + Str << Offset;
|
| + Str << "(" << getRegName(getFrameOrStackReg(), FrameSPTy);
|
| + Str << ")";
|
| + }
|
| UnimplementedError(Func->getContext()->getFlags());
|
| }
|
|
|
| void TargetMIPS32::lowerArguments() {
|
| VarList &Args = Func->getArgs();
|
| - if (Args.size() > 0)
|
| - UnimplementedError(Func->getContext()->getFlags());
|
| + // We are only handling integer registers for now. The Mips o32 ABI is
|
| + // somewhat complex but will be implemented in its totality through follow
|
| + // on patches.
|
| + //
|
| + unsigned NumGPRRegsUsed = 0;
|
| + // For each register argument, replace Arg in the argument list with the
|
| + // home register. Then generate an instruction in the prolog to copy the
|
| + // home register to the assigned location of Arg.
|
| + Context.init(Func->getEntryNode());
|
| + Context.setInsertPoint(Context.getCur());
|
| + for (SizeT I = 0, E = Args.size(); I < E; ++I) {
|
| + Variable *Arg = Args[I];
|
| + Type Ty = Arg->getType();
|
| + // TODO(rkotler): handle float/vector types.
|
| + if (isVectorType(Ty)) {
|
| + UnimplementedError(Func->getContext()->getFlags());
|
| + continue;
|
| + }
|
| + if (isFloatingType(Ty)) {
|
| + UnimplementedError(Func->getContext()->getFlags());
|
| + continue;
|
| + }
|
| + if (Ty == IceType_i64) {
|
| + if (NumGPRRegsUsed >= MIPS32_MAX_GPR_ARG)
|
| + continue;
|
| + int32_t RegLo = RegMIPS32::Reg_A0 + NumGPRRegsUsed;
|
| + int32_t RegHi = RegLo + 1;
|
| + ++NumGPRRegsUsed;
|
| + // Always start i64 registers at an even register, so this may end
|
| + // up padding away a register.
|
| + if (RegLo % 2 != 0) {
|
| + ++RegLo;
|
| + ++NumGPRRegsUsed;
|
| + }
|
| + // If this leaves us without room to consume another register,
|
| + // leave any previously speculatively consumed registers as consumed.
|
| + if (NumGPRRegsUsed >= MIPS32_MAX_GPR_ARG)
|
| + continue;
|
| + // RegHi = RegMIPS32::Reg_A0 + NumGPRRegsUsed;
|
| + ++NumGPRRegsUsed;
|
| + Variable *RegisterArg = Func->makeVariable(Ty);
|
| + auto *RegisterArg64On32 = llvm::cast<Variable64On32>(RegisterArg);
|
| + if (BuildDefs::dump())
|
| + RegisterArg64On32->setName(Func, "home_reg:" + Arg->getName(Func));
|
| + RegisterArg64On32->initHiLo(Func);
|
| + RegisterArg64On32->setIsArg();
|
| + RegisterArg64On32->getLo()->setRegNum(RegLo);
|
| + RegisterArg64On32->getHi()->setRegNum(RegHi);
|
| + Arg->setIsArg(false);
|
| + Args[I] = RegisterArg64On32;
|
| + Context.insert(InstAssign::create(Func, Arg, RegisterArg));
|
| + continue;
|
| + } else {
|
| + assert(Ty == IceType_i32);
|
| + if (NumGPRRegsUsed >= MIPS32_MAX_GPR_ARG)
|
| + continue;
|
| + int32_t RegNum = RegMIPS32::Reg_A0 + NumGPRRegsUsed;
|
| + ++NumGPRRegsUsed;
|
| + Variable *RegisterArg = Func->makeVariable(Ty);
|
| + if (BuildDefs::dump()) {
|
| + RegisterArg->setName(Func, "home_reg:" + Arg->getName(Func));
|
| + }
|
| + RegisterArg->setRegNum(RegNum);
|
| + RegisterArg->setIsArg();
|
| + Arg->setIsArg(false);
|
| + Args[I] = RegisterArg;
|
| + Context.insert(InstAssign::create(Func, Arg, RegisterArg));
|
| + }
|
| + }
|
| }
|
|
|
| Type TargetMIPS32::stackSlotType() { return IceType_i32; }
|
|
|
| void TargetMIPS32::addProlog(CfgNode *Node) {
|
| (void)Node;
|
| + return;
|
| UnimplementedError(Func->getContext()->getFlags());
|
| }
|
|
|
| void TargetMIPS32::addEpilog(CfgNode *Node) {
|
| (void)Node;
|
| + return;
|
| UnimplementedError(Func->getContext()->getFlags());
|
| }
|
|
|
| +Operand *TargetMIPS32::loOperand(Operand *Operand) {
|
| + assert(Operand->getType() == IceType_i64);
|
| + if (auto *Var64On32 = llvm::dyn_cast<Variable64On32>(Operand))
|
| + return Var64On32->getLo();
|
| + if (auto *Const = llvm::dyn_cast<ConstantInteger64>(Operand)) {
|
| + return Ctx->getConstantInt32(static_cast<uint32_t>(Const->getValue()));
|
| + }
|
| + if (auto *Mem = llvm::dyn_cast<OperandMIPS32Mem>(Operand)) {
|
| + // Conservatively disallow memory operands with side-effects (pre/post
|
| + // increment) in case of duplication.
|
| + assert(Mem->getAddrMode() == OperandMIPS32Mem::Offset);
|
| + return OperandMIPS32Mem::create(Func, IceType_i32, Mem->getBase(),
|
| + Mem->getOffset(), Mem->getAddrMode());
|
| + }
|
| + llvm_unreachable("Unsupported operand type");
|
| + return nullptr;
|
| +}
|
| +
|
| +Operand *TargetMIPS32::hiOperand(Operand *Operand) {
|
| + assert(Operand->getType() == IceType_i64);
|
| + if (Operand->getType() != IceType_i64)
|
| + return Operand;
|
| + if (auto *Var64On32 = llvm::dyn_cast<Variable64On32>(Operand))
|
| + return Var64On32->getHi();
|
| + if (auto *Const = llvm::dyn_cast<ConstantInteger64>(Operand)) {
|
| + return Ctx->getConstantInt32(
|
| + static_cast<uint32_t>(Const->getValue() >> 32));
|
| + }
|
| + if (auto *Mem = llvm::dyn_cast<OperandMIPS32Mem>(Operand)) {
|
| + // Conservatively disallow memory operands with side-effects
|
| + // in case of duplication.
|
| + assert(Mem->getAddrMode() == OperandMIPS32Mem::Offset);
|
| + const Type SplitType = IceType_i32;
|
| + Variable *Base = Mem->getBase();
|
| + ConstantInteger32 *Offset = Mem->getOffset();
|
| + assert(!Utils::WouldOverflowAdd(Offset->getValue(), 4));
|
| + int32_t NextOffsetVal = Offset->getValue() + 4;
|
| + constexpr bool SignExt = false;
|
| + if (!OperandMIPS32Mem::canHoldOffset(SplitType, SignExt, NextOffsetVal)) {
|
| + // We have to make a temp variable and add 4 to either Base or Offset.
|
| + // If we add 4 to Offset, this will convert a non-RegReg addressing
|
| + // mode into a RegReg addressing mode. Since NaCl sandboxing disallows
|
| + // RegReg addressing modes, prefer adding to base and replacing instead.
|
| + // Thus we leave the old offset alone.
|
| + Constant *Four = Ctx->getConstantInt32(4);
|
| + Variable *NewBase = Func->makeVariable(Base->getType());
|
| + lowerArithmetic(InstArithmetic::create(Func, InstArithmetic::Add, NewBase,
|
| + Base, Four));
|
| + Base = NewBase;
|
| + } else {
|
| + Offset =
|
| + llvm::cast<ConstantInteger32>(Ctx->getConstantInt32(NextOffsetVal));
|
| + }
|
| + return OperandMIPS32Mem::create(Func, SplitType, Base, Offset,
|
| + Mem->getAddrMode());
|
| + }
|
| + llvm_unreachable("Unsupported operand type");
|
| + return nullptr;
|
| +}
|
| +
|
| llvm::SmallBitVector TargetMIPS32::getRegisterSet(RegSetMask Include,
|
| RegSetMask Exclude) const {
|
| llvm::SmallBitVector Registers(RegMIPS32::Reg_NUM);
|
|
|
| #define X(val, encode, name, scratch, preserved, stackptr, frameptr, isInt, \
|
| - isFP) \
|
| + isI64Pair, isFP32, isFP64, isVec128, alias_init) \
|
| if (scratch && (Include & RegSet_CallerSave)) \
|
| Registers[RegMIPS32::val] = true; \
|
| if (preserved && (Include & RegSet_CalleeSave)) \
|
| @@ -318,70 +515,117 @@ void TargetMIPS32::lowerAlloca(const InstAlloca *Inst) {
|
| }
|
|
|
| void TargetMIPS32::lowerArithmetic(const InstArithmetic *Inst) {
|
| - switch (Inst->getOp()) {
|
| - case InstArithmetic::_num:
|
| - UnimplementedError(Func->getContext()->getFlags());
|
| - break;
|
| - case InstArithmetic::Add:
|
| - UnimplementedError(Func->getContext()->getFlags());
|
| - break;
|
| - case InstArithmetic::And:
|
| - UnimplementedError(Func->getContext()->getFlags());
|
| - break;
|
| - case InstArithmetic::Or:
|
| - UnimplementedError(Func->getContext()->getFlags());
|
| - break;
|
| - case InstArithmetic::Xor:
|
| + Variable *Dest = Inst->getDest();
|
| + Operand *Src0 = legalizeUndef(Inst->getSrc(0));
|
| + Operand *Src1 = legalizeUndef(Inst->getSrc(1));
|
| + (void)Src0;
|
| + (void)Src1;
|
| + if (Dest->getType() == IceType_i64) {
|
| UnimplementedError(Func->getContext()->getFlags());
|
| - break;
|
| - case InstArithmetic::Sub:
|
| - UnimplementedError(Func->getContext()->getFlags());
|
| - break;
|
| - case InstArithmetic::Mul:
|
| - UnimplementedError(Func->getContext()->getFlags());
|
| - break;
|
| - case InstArithmetic::Shl:
|
| - UnimplementedError(Func->getContext()->getFlags());
|
| - break;
|
| - case InstArithmetic::Lshr:
|
| - UnimplementedError(Func->getContext()->getFlags());
|
| - break;
|
| - case InstArithmetic::Ashr:
|
| - UnimplementedError(Func->getContext()->getFlags());
|
| - break;
|
| - case InstArithmetic::Udiv:
|
| - UnimplementedError(Func->getContext()->getFlags());
|
| - break;
|
| - case InstArithmetic::Sdiv:
|
| - UnimplementedError(Func->getContext()->getFlags());
|
| - break;
|
| - case InstArithmetic::Urem:
|
| + } else if (isVectorType(Dest->getType())) {
|
| UnimplementedError(Func->getContext()->getFlags());
|
| - break;
|
| - case InstArithmetic::Srem:
|
| - UnimplementedError(Func->getContext()->getFlags());
|
| - break;
|
| - case InstArithmetic::Fadd:
|
| - UnimplementedError(Func->getContext()->getFlags());
|
| - break;
|
| - case InstArithmetic::Fsub:
|
| - UnimplementedError(Func->getContext()->getFlags());
|
| - break;
|
| - case InstArithmetic::Fmul:
|
| - UnimplementedError(Func->getContext()->getFlags());
|
| - break;
|
| - case InstArithmetic::Fdiv:
|
| - UnimplementedError(Func->getContext()->getFlags());
|
| - break;
|
| - case InstArithmetic::Frem:
|
| - UnimplementedError(Func->getContext()->getFlags());
|
| - break;
|
| + } else { // Dest->getType() is non-i64 scalar
|
| + switch (Inst->getOp()) {
|
| + case InstArithmetic::_num:
|
| + UnimplementedError(Func->getContext()->getFlags());
|
| + break;
|
| + case InstArithmetic::Add:
|
| + UnimplementedError(Func->getContext()->getFlags());
|
| + // Variable *T = makeReg(Dest->getType());
|
| + // _add(T, Src0, Src1);
|
| + // _mov(Dest, T);
|
| + return;
|
| + case InstArithmetic::And:
|
| + UnimplementedError(Func->getContext()->getFlags());
|
| + break;
|
| + case InstArithmetic::Or:
|
| + UnimplementedError(Func->getContext()->getFlags());
|
| + break;
|
| + case InstArithmetic::Xor:
|
| + UnimplementedError(Func->getContext()->getFlags());
|
| + break;
|
| + case InstArithmetic::Sub:
|
| + UnimplementedError(Func->getContext()->getFlags());
|
| + break;
|
| + case InstArithmetic::Mul:
|
| + UnimplementedError(Func->getContext()->getFlags());
|
| + break;
|
| + case InstArithmetic::Shl:
|
| + UnimplementedError(Func->getContext()->getFlags());
|
| + break;
|
| + case InstArithmetic::Lshr:
|
| + UnimplementedError(Func->getContext()->getFlags());
|
| + break;
|
| + case InstArithmetic::Ashr:
|
| + UnimplementedError(Func->getContext()->getFlags());
|
| + break;
|
| + case InstArithmetic::Udiv:
|
| + UnimplementedError(Func->getContext()->getFlags());
|
| + break;
|
| + case InstArithmetic::Sdiv:
|
| + UnimplementedError(Func->getContext()->getFlags());
|
| + break;
|
| + case InstArithmetic::Urem:
|
| + UnimplementedError(Func->getContext()->getFlags());
|
| + break;
|
| + case InstArithmetic::Srem:
|
| + UnimplementedError(Func->getContext()->getFlags());
|
| + break;
|
| + case InstArithmetic::Fadd:
|
| + UnimplementedError(Func->getContext()->getFlags());
|
| + break;
|
| + case InstArithmetic::Fsub:
|
| + UnimplementedError(Func->getContext()->getFlags());
|
| + break;
|
| + case InstArithmetic::Fmul:
|
| + UnimplementedError(Func->getContext()->getFlags());
|
| + break;
|
| + case InstArithmetic::Fdiv:
|
| + UnimplementedError(Func->getContext()->getFlags());
|
| + break;
|
| + case InstArithmetic::Frem:
|
| + UnimplementedError(Func->getContext()->getFlags());
|
| + break;
|
| + }
|
| }
|
| }
|
|
|
| void TargetMIPS32::lowerAssign(const InstAssign *Inst) {
|
| - (void)Inst;
|
| - UnimplementedError(Func->getContext()->getFlags());
|
| + Variable *Dest = Inst->getDest();
|
| + Operand *Src0 = Inst->getSrc(0);
|
| + assert(Dest->getType() == Src0->getType());
|
| + if (Dest->getType() == IceType_i64) {
|
| + Src0 = legalizeUndef(Src0);
|
| + Operand *Src0Lo = legalize(loOperand(Src0), Legal_Reg);
|
| + Operand *Src0Hi = legalize(hiOperand(Src0), Legal_Reg);
|
| + Variable *DestLo = llvm::cast<Variable>(loOperand(Dest));
|
| + Variable *DestHi = llvm::cast<Variable>(hiOperand(Dest));
|
| + // Variable *T_Lo = nullptr, *T_Hi = nullptr;
|
| + Variable *T_Lo = makeReg(IceType_i32);
|
| + Variable *T_Hi = makeReg(IceType_i32);
|
| + _mov(T_Lo, Src0Lo);
|
| + _mov(DestLo, T_Lo);
|
| + _mov(T_Hi, Src0Hi);
|
| + _mov(DestHi, T_Hi);
|
| + } else {
|
| + Operand *SrcR;
|
| + if (Dest->hasReg()) {
|
| + // If Dest already has a physical register, then legalize the Src operand
|
| + // into a Variable with the same register assignment. This especially
|
| + // helps allow the use of Flex operands.
|
| + SrcR = legalize(Src0, Legal_Reg, Dest->getRegNum());
|
| + } else {
|
| + // Dest could be a stack operand. Since we could potentially need
|
| + // to do a Store (and store can only have Register operands),
|
| + // legalize this to a register.
|
| + SrcR = legalize(Src0, Legal_Reg);
|
| + }
|
| + if (isVectorType(Dest->getType())) {
|
| + UnimplementedError(Func->getContext()->getFlags());
|
| + } else {
|
| + _mov(Dest, SrcR);
|
| + }
|
| + }
|
| }
|
|
|
| void TargetMIPS32::lowerBr(const InstBr *Inst) {
|
| @@ -608,8 +852,32 @@ void TargetMIPS32::lowerPhi(const InstPhi * /*Inst*/) {
|
|
|
| void TargetMIPS32::lowerRet(const InstRet *Inst) {
|
| Variable *Reg = nullptr;
|
| - if (Inst->hasRetValue())
|
| - UnimplementedError(Func->getContext()->getFlags());
|
| + if (Inst->hasRetValue()) {
|
| + Operand *Src0 = Inst->getRetValue();
|
| + switch (Src0->getType()) {
|
| + case IceType_i1:
|
| + case IceType_i8:
|
| + case IceType_i16:
|
| + case IceType_i32: {
|
| + // Reg = legalizeToReg(Src0, RegMIPS32::Reg_V0);
|
| + Operand *Src0F = legalize(Src0, Legal_Reg);
|
| + Reg = makeReg(Src0F->getType(), RegMIPS32::Reg_V0);
|
| + _mov(Reg, Src0F);
|
| + break;
|
| + }
|
| + case IceType_i64: {
|
| + Src0 = legalizeUndef(Src0);
|
| + Variable *R0 = legalizeToReg(loOperand(Src0), RegMIPS32::Reg_V0);
|
| + Variable *R1 = legalizeToReg(hiOperand(Src0), RegMIPS32::Reg_V1);
|
| + Reg = R0;
|
| + Context.insert(InstFakeUse::create(Func, R1));
|
| + break;
|
| + }
|
| +
|
| + default:
|
| + UnimplementedError(Func->getContext()->getFlags());
|
| + }
|
| + }
|
| _ret(getPhysicalRegister(RegMIPS32::Reg_RA), Reg);
|
| }
|
|
|
| @@ -640,7 +908,7 @@ void TargetMIPS32::lowerUnreachable(const InstUnreachable * /*Inst*/) {
|
| // integrity of liveness analysis. Undef values are also turned into zeroes,
|
| // since loOperand() and hiOperand() don't expect Undef input.
|
| void TargetMIPS32::prelowerPhis() {
|
| - UnimplementedError(Func->getContext()->getFlags());
|
| + PhiLowering::prelowerPhis32Bit<TargetMIPS32>(this, Context.getNode(), Func);
|
| }
|
|
|
| void TargetMIPS32::postLower() {
|
| @@ -705,7 +973,86 @@ void TargetDataMIPS32::lowerJumpTables() {
|
| UnimplementedError(Ctx->getFlags());
|
| }
|
|
|
| +// Helper for legalize() to emit the right code to lower an operand to a
|
| +// register of the appropriate type.
|
| +Variable *TargetMIPS32::copyToReg(Operand *Src, int32_t RegNum) {
|
| + Type Ty = Src->getType();
|
| + Variable *Reg = makeReg(Ty, RegNum);
|
| + if (isVectorType(Ty) || isFloatingType(Ty)) {
|
| + UnimplementedError(Ctx->getFlags());
|
| + } else {
|
| + // Mov's Src operand can really only be the flexible second operand type
|
| + // or a register. Users should guarantee that.
|
| + _mov(Reg, Src);
|
| + }
|
| + return Reg;
|
| +}
|
| +
|
| +Operand *TargetMIPS32::legalize(Operand *From, LegalMask Allowed,
|
| + int32_t RegNum) {
|
| + Type Ty = From->getType();
|
| + // Assert that a physical register is allowed. To date, all calls
|
| + // to legalize() allow a physical register. Legal_Flex converts
|
| + // registers to the right type OperandMIPS32FlexReg as needed.
|
| + assert(Allowed & Legal_Reg);
|
| + // Go through the various types of operands:
|
| + // OperandMIPS32Mem, OperandMIPS32Flex, Constant, and Variable.
|
| + // Given the above assertion, if type of operand is not legal
|
| + // (e.g., OperandMIPS32Mem and !Legal_Mem), we can always copy
|
| + // to a register.
|
| + if (auto C = llvm::dyn_cast<ConstantRelocatable>(From)) {
|
| + (void)C;
|
| + return From;
|
| + } else if (auto *C32 = llvm::dyn_cast<ConstantInteger32>(From)) {
|
| + uint32_t Value = static_cast<uint32_t>(C32->getValue());
|
| + // Check if the immediate will fit in a Flexible second operand,
|
| + // if a Flexible second operand is allowed. We need to know the exact
|
| + // value, so that rules out relocatable constants.
|
| + // Also try the inverse and use MVN if possible.
|
| + // Do a movw/movt to a register.
|
| + Variable *Reg;
|
| + if (RegNum == Variable::NoRegister)
|
| + Reg = makeReg(Ty, RegNum);
|
| + else
|
| + Reg = getPhysicalRegister(RegNum);
|
| + if (isInt<16>(int32_t(Value))) {
|
| + _addiu(Reg, getPhysicalRegister(RegMIPS32::Reg_ZERO, Ty), Value);
|
| + } else {
|
| + uint32_t UpperBits = (Value >> 16) & 0xFFFF;
|
| + (void)UpperBits;
|
| + uint32_t LowerBits = Value & 0xFFFF;
|
| + Variable *TReg = makeReg(Ty, RegNum);
|
| + _lui(TReg, UpperBits);
|
| + _ori(Reg, TReg, LowerBits);
|
| + }
|
| + return Reg;
|
| + }
|
| + if (auto Var = llvm::dyn_cast<Variable>(From)) {
|
| + // Check if the variable is guaranteed a physical register. This
|
| + // can happen either when the variable is pre-colored or when it is
|
| + // assigned infinite weight.
|
| + bool MustHaveRegister = (Var->hasReg() || Var->mustHaveReg());
|
| + // We need a new physical register for the operand if:
|
| + // Mem is not allowed and Var isn't guaranteed a physical
|
| + // register, or
|
| + // RegNum is required and Var->getRegNum() doesn't match.
|
| + if ((!(Allowed & Legal_Mem) && !MustHaveRegister) ||
|
| + (RegNum != Variable::NoRegister && RegNum != Var->getRegNum())) {
|
| + From = copyToReg(From, RegNum);
|
| + }
|
| + return From;
|
| + }
|
| + return From;
|
| +}
|
| +
|
| TargetHeaderMIPS32::TargetHeaderMIPS32(GlobalContext *Ctx)
|
| : TargetHeaderLowering(Ctx) {}
|
|
|
| +void TargetHeaderMIPS32::lower() {
|
| + OstreamLocker L(Ctx);
|
| + Ostream &Str = Ctx->getStrEmit();
|
| + Str << "\t.set\tnomicromips\n";
|
| + Str << "\t.set\tnomips16\n";
|
| +}
|
| +
|
| } // end of namespace Ice
|
|
|