| // |
| // The Subzero Code Generator |
| // |
| // This file is distributed under the University of Illinois Open Source |
| // License. See LICENSE.TXT for details. |
| // |
| //===----------------------------------------------------------------------===// |
| /// |
| /// \file |
| /// \brief Implements the TargetLoweringMIPS32 class, which consists almost |
| /// entirely of the lowering sequence for each high-level instruction. |
| /// |
| //===----------------------------------------------------------------------===// |
| |
| #include "IceTargetLoweringMIPS32.h" |
| |
| #include "IceCfg.h" |
| #include "IceCfgNode.h" |
| #include "IceClFlags.h" |
| #include "IceDefs.h" |
| #include "IceELFObjectWriter.h" |
| #include "IceGlobalInits.h" |
| #include "IceInstMIPS32.h" |
| #include "IceLiveness.h" |
| #include "IceOperand.h" |
| #include "IcePhiLoweringImpl.h" |
| #include "IceRegistersMIPS32.h" |
| #include "IceTargetLoweringMIPS32.def" |
| #include "IceUtils.h" |
| #include "llvm/Support/MathExtras.h" |
| |
| namespace MIPS32 { |
| std::unique_ptr<::Ice::TargetLowering> createTargetLowering(::Ice::Cfg *Func) { |
| return ::Ice::MIPS32::TargetMIPS32::create(Func); |
| } |
| |
| std::unique_ptr<::Ice::TargetDataLowering> |
| createTargetDataLowering(::Ice::GlobalContext *Ctx) { |
| return ::Ice::MIPS32::TargetDataMIPS32::create(Ctx); |
| } |
| |
| std::unique_ptr<::Ice::TargetHeaderLowering> |
| createTargetHeaderLowering(::Ice::GlobalContext *Ctx) { |
| return ::Ice::MIPS32::TargetHeaderMIPS32::create(Ctx); |
| } |
| |
| void staticInit(::Ice::GlobalContext *Ctx) { |
| ::Ice::MIPS32::TargetMIPS32::staticInit(Ctx); |
| } |
| |
| bool shouldBePooled(const ::Ice::Constant *C) { |
| return ::Ice::MIPS32::TargetMIPS32::shouldBePooled(C); |
| } |
| } // end of namespace MIPS32 |
| |
| namespace Ice { |
| namespace MIPS32 { |
| |
| using llvm::isInt; |
| |
| namespace { |
| |
| // The maximum number of arguments to pass in GPR registers. |
| constexpr uint32_t MIPS32_MAX_GPR_ARG = 4; |
| |
| const char *getRegClassName(RegClass C) { |
| auto ClassNum = static_cast<RegClassMIPS32>(C); |
| assert(ClassNum < RCMIPS32_NUM); |
| switch (ClassNum) { |
| default: |
| assert(C < RC_Target); |
| return regClassString(C); |
| // Add handling of new register classes below. |
| } |
| } |
| |
| } // end of anonymous namespace |
| |
| TargetMIPS32::TargetMIPS32(Cfg *Func) : TargetLowering(Func) {} |
| |
| void TargetMIPS32::staticInit(GlobalContext *Ctx) { |
| (void)Ctx; |
| RegNumT::setLimit(RegMIPS32::Reg_NUM); |
| SmallBitVector IntegerRegisters(RegMIPS32::Reg_NUM); |
| SmallBitVector I64PairRegisters(RegMIPS32::Reg_NUM); |
| SmallBitVector Float32Registers(RegMIPS32::Reg_NUM); |
| SmallBitVector Float64Registers(RegMIPS32::Reg_NUM); |
| SmallBitVector VectorRegisters(RegMIPS32::Reg_NUM); |
| SmallBitVector InvalidRegisters(RegMIPS32::Reg_NUM); |
| #define X(val, encode, name, scratch, preserved, stackptr, frameptr, isInt, \ |
| isI64Pair, isFP32, isFP64, isVec128, alias_init) \ |
| IntegerRegisters[RegMIPS32::val] = isInt; \ |
| 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); \ |
| assert(RegisterAliases[RegMIPS32::val][RegMIPS32::val]); |
| REGMIPS32_TABLE; |
| #undef X |
| TypeToRegisterSet[IceType_void] = InvalidRegisters; |
| TypeToRegisterSet[IceType_i1] = IntegerRegisters; |
| TypeToRegisterSet[IceType_i8] = IntegerRegisters; |
| TypeToRegisterSet[IceType_i16] = IntegerRegisters; |
| TypeToRegisterSet[IceType_i32] = IntegerRegisters; |
| TypeToRegisterSet[IceType_i64] = IntegerRegisters; |
| TypeToRegisterSet[IceType_f32] = Float32Registers; |
| TypeToRegisterSet[IceType_f64] = Float64Registers; |
| TypeToRegisterSet[IceType_v4i1] = VectorRegisters; |
| TypeToRegisterSet[IceType_v8i1] = VectorRegisters; |
| TypeToRegisterSet[IceType_v16i1] = VectorRegisters; |
| TypeToRegisterSet[IceType_v16i8] = VectorRegisters; |
| TypeToRegisterSet[IceType_v8i16] = VectorRegisters; |
| TypeToRegisterSet[IceType_v4i32] = VectorRegisters; |
| TypeToRegisterSet[IceType_v4f32] = VectorRegisters; |
| |
| for (size_t i = 0; i < llvm::array_lengthof(TypeToRegisterSet); ++i) |
| TypeToRegisterSetUnfiltered[i] = TypeToRegisterSet[i]; |
| |
| filterTypeToRegisterSet(Ctx, RegMIPS32::Reg_NUM, TypeToRegisterSet, |
| llvm::array_lengthof(TypeToRegisterSet), |
| RegMIPS32::getRegName, getRegClassName); |
| } |
| |
| void TargetMIPS32::translateO2() { |
| TimerMarker T(TimerStack::TT_O2, Func); |
| |
| // TODO(stichnot): share passes with X86? |
| // https://code.google.com/p/nativeclient/issues/detail?id=4094 |
| genTargetHelperCalls(); |
| |
| // Merge Alloca instructions, and lay out the stack. |
| static constexpr bool SortAndCombineAllocas = false; |
| Func->processAllocas(SortAndCombineAllocas); |
| Func->dump("After Alloca processing"); |
| |
| if (!getFlags().getEnablePhiEdgeSplit()) { |
| // Lower Phi instructions. |
| Func->placePhiLoads(); |
| if (Func->hasError()) |
| return; |
| Func->placePhiStores(); |
| if (Func->hasError()) |
| return; |
| Func->deletePhis(); |
| if (Func->hasError()) |
| return; |
| Func->dump("After Phi lowering"); |
| } |
| |
| // Address mode optimization. |
| Func->getVMetadata()->init(VMK_SingleDefs); |
| Func->doAddressOpt(); |
| |
| // Argument lowering |
| Func->doArgLowering(); |
| |
| // Target lowering. This requires liveness analysis for some parts of the |
| // lowering decisions, such as compare/branch fusing. If non-lightweight |
| // liveness analysis is used, the instructions need to be renumbered first. |
| // TODO: This renumbering should only be necessary if we're actually |
| // calculating live intervals, which we only do for register allocation. |
| Func->renumberInstructions(); |
| if (Func->hasError()) |
| return; |
| |
| // TODO: It should be sufficient to use the fastest liveness calculation, |
| // i.e. livenessLightweight(). However, for some reason that slows down the |
| // rest of the translation. Investigate. |
| Func->liveness(Liveness_Basic); |
| if (Func->hasError()) |
| return; |
| Func->dump("After MIPS32 address mode opt"); |
| |
| Func->genCode(); |
| if (Func->hasError()) |
| return; |
| Func->dump("After MIPS32 codegen"); |
| |
| // Register allocation. This requires instruction renumbering and full |
| // liveness analysis. |
| Func->renumberInstructions(); |
| if (Func->hasError()) |
| return; |
| Func->liveness(Liveness_Intervals); |
| if (Func->hasError()) |
| return; |
| // Validate the live range computations. The expensive validation call is |
| // deliberately only made when assertions are enabled. |
| assert(Func->validateLiveness()); |
| // The post-codegen dump is done here, after liveness analysis and associated |
| // cleanup, to make the dump cleaner and more useful. |
| Func->dump("After initial MIPS32 codegen"); |
| Func->getVMetadata()->init(VMK_All); |
| regAlloc(RAK_Global); |
| if (Func->hasError()) |
| return; |
| Func->dump("After linear scan regalloc"); |
| |
| if (getFlags().getEnablePhiEdgeSplit()) { |
| Func->advancedPhiLowering(); |
| Func->dump("After advanced Phi lowering"); |
| } |
| |
| // Stack frame mapping. |
| Func->genFrame(); |
| if (Func->hasError()) |
| return; |
| Func->dump("After stack frame mapping"); |
| |
| Func->contractEmptyNodes(); |
| Func->reorderNodes(); |
| |
| // Branch optimization. This needs to be done just before code emission. In |
| // particular, no transformations that insert or reorder CfgNodes should be |
| // done after branch optimization. We go ahead and do it before nop insertion |
| // to reduce the amount of work needed for searching for opportunities. |
| Func->doBranchOpt(); |
| Func->dump("After branch optimization"); |
| |
| // Nop insertion |
| if (getFlags().getShouldDoNopInsertion()) { |
| Func->doNopInsertion(); |
| } |
| } |
| |
| void TargetMIPS32::translateOm1() { |
| TimerMarker T(TimerStack::TT_Om1, Func); |
| |
| // TODO: share passes with X86? |
| genTargetHelperCalls(); |
| |
| // Do not merge Alloca instructions, and lay out the stack. |
| static constexpr bool SortAndCombineAllocas = false; |
| Func->processAllocas(SortAndCombineAllocas); |
| Func->dump("After Alloca processing"); |
| |
| Func->placePhiLoads(); |
| if (Func->hasError()) |
| return; |
| Func->placePhiStores(); |
| if (Func->hasError()) |
| return; |
| Func->deletePhis(); |
| if (Func->hasError()) |
| return; |
| Func->dump("After Phi lowering"); |
| |
| Func->doArgLowering(); |
| |
| Func->genCode(); |
| if (Func->hasError()) |
| return; |
| Func->dump("After initial MIPS32 codegen"); |
| |
| regAlloc(RAK_InfOnly); |
| if (Func->hasError()) |
| return; |
| Func->dump("After regalloc of infinite-weight variables"); |
| |
| Func->genFrame(); |
| if (Func->hasError()) |
| return; |
| Func->dump("After stack frame mapping"); |
| |
| // Nop insertion |
| if (getFlags().getShouldDoNopInsertion()) { |
| Func->doNopInsertion(); |
| } |
| } |
| |
| bool TargetMIPS32::doBranchOpt(Inst *Instr, const CfgNode *NextNode) { |
| (void)Instr; |
| (void)NextNode; |
| UnimplementedError(getFlags()); |
| return false; |
| } |
| |
| namespace { |
| |
| const char *RegNames[RegMIPS32::Reg_NUM] = { |
| #define X(val, encode, name, scratch, preserved, stackptr, frameptr, isInt, \ |
| isI64Pair, isFP32, isFP64, isVec128, alias_init) \ |
| name, |
| REGMIPS32_TABLE |
| #undef X |
| }; |
| |
| } // end of anonymous namespace |
| |
| const char *RegMIPS32::getRegName(RegNumT RegNum) { |
| RegNum.assertIsValid(); |
| return RegNames[RegNum]; |
| } |
| |
| const char *TargetMIPS32::getRegName(RegNumT RegNum, Type Ty) const { |
| (void)Ty; |
| return RegMIPS32::getRegName(RegNum); |
| } |
| |
| Variable *TargetMIPS32::getPhysicalRegister(RegNumT RegNum, Type Ty) { |
| if (Ty == IceType_void) |
| Ty = IceType_i32; |
| if (PhysicalRegisters[Ty].empty()) |
| PhysicalRegisters[Ty].resize(RegMIPS32::Reg_NUM); |
| RegNum.assertIsValid(); |
| Variable *Reg = PhysicalRegisters[Ty][RegNum]; |
| if (Reg == nullptr) { |
| Reg = Func->makeVariable(Ty); |
| Reg->setRegNum(RegNum); |
| PhysicalRegisters[Ty][RegNum] = Reg; |
| // Specially mark a named physical register as an "argument" so that it is |
| // considered live upon function entry. Otherwise it's possible to get |
| // liveness validation errors for saving callee-save registers. |
| Func->addImplicitArg(Reg); |
| // Don't bother tracking the live range of a named physical register. |
| Reg->setIgnoreLiveness(); |
| } |
| return Reg; |
| } |
| |
| void TargetMIPS32::emitJumpTable(const Cfg *Func, |
| const InstJumpTable *JumpTable) const { |
| (void)Func; |
| (void)JumpTable; |
| UnimplementedError(getFlags()); |
| } |
| |
| /// Provide a trivial wrapper to legalize() for this common usage. |
| Variable *TargetMIPS32::legalizeToReg(Operand *From, RegNumT RegNum) { |
| return llvm::cast<Variable>(legalize(From, Legal_Reg, RegNum)); |
| } |
| |
| /// Legalize undef values to concrete values. |
| Operand *TargetMIPS32::legalizeUndef(Operand *From, RegNumT 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(getFlags()); |
| return Ctx->getConstantZero(Ty); |
| } |
| return From; |
| } |
| |
| Variable *TargetMIPS32::makeReg(Type Type, RegNumT RegNum) { |
| // There aren't any 64-bit integer registers for Mips32. |
| assert(Type != IceType_i64); |
| Variable *Reg = Func->makeVariable(Type); |
| if (RegNum.hasValue()) |
| Reg->setRegNum(RegNum); |
| else |
| Reg->setMustHaveReg(); |
| return Reg; |
| } |
| |
| void TargetMIPS32::emitVariable(const Variable *Var) const { |
| if (!BuildDefs::dump()) |
| return; |
| Ostream &Str = Ctx->getStrEmit(); |
| 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(getFlags()); |
| } |
| |
| void TargetMIPS32::lowerArguments() { |
| VarList &Args = Func->getArgs(); |
| // 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(getFlags()); |
| continue; |
| } |
| if (isFloatingType(Ty)) { |
| UnimplementedError(getFlags()); |
| continue; |
| } |
| if (Ty == IceType_i64) { |
| if (NumGPRRegsUsed >= MIPS32_MAX_GPR_ARG) |
| continue; |
| auto RegLo = RegNumT::fixme(RegMIPS32::Reg_A0 + NumGPRRegsUsed); |
| auto RegHi = RegNumT::fixme(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 = RegNumT::fixme(RegLo + 1); |
| ++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 = RegNumT::fixme(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()); |
| RegisterArg64On32->initHiLo(Func); |
| RegisterArg64On32->setIsArg(); |
| RegisterArg64On32->getLo()->setRegNum(RegLo); |
| RegisterArg64On32->getHi()->setRegNum(RegHi); |
| Arg->setIsArg(false); |
| Args[I] = RegisterArg64On32; |
| Context.insert<InstAssign>(Arg, RegisterArg); |
| continue; |
| } else { |
| assert(Ty == IceType_i32); |
| if (NumGPRRegsUsed >= MIPS32_MAX_GPR_ARG) |
| continue; |
| const auto RegNum = RegNumT::fixme(RegMIPS32::Reg_A0 + NumGPRRegsUsed); |
| ++NumGPRRegsUsed; |
| Variable *RegisterArg = Func->makeVariable(Ty); |
| if (BuildDefs::dump()) { |
| RegisterArg->setName(Func, "home_reg:" + Arg->getName()); |
| } |
| RegisterArg->setRegNum(RegNum); |
| RegisterArg->setIsArg(); |
| Arg->setIsArg(false); |
| Args[I] = RegisterArg; |
| Context.insert<InstAssign>(Arg, RegisterArg); |
| } |
| } |
| } |
| |
| Type TargetMIPS32::stackSlotType() { return IceType_i32; } |
| |
| void TargetMIPS32::addProlog(CfgNode *Node) { |
| (void)Node; |
| return; |
| UnimplementedError(getFlags()); |
| } |
| |
| void TargetMIPS32::addEpilog(CfgNode *Node) { |
| (void)Node; |
| return; |
| UnimplementedError(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; |
| } |
| |
| SmallBitVector TargetMIPS32::getRegisterSet(RegSetMask Include, |
| RegSetMask Exclude) const { |
| SmallBitVector Registers(RegMIPS32::Reg_NUM); |
| |
| #define X(val, encode, name, scratch, preserved, stackptr, frameptr, isInt, \ |
| isI64Pair, isFP32, isFP64, isVec128, alias_init) \ |
| if (scratch && (Include & RegSet_CallerSave)) \ |
| Registers[RegMIPS32::val] = true; \ |
| if (preserved && (Include & RegSet_CalleeSave)) \ |
| Registers[RegMIPS32::val] = true; \ |
| if (stackptr && (Include & RegSet_StackPointer)) \ |
| Registers[RegMIPS32::val] = true; \ |
| if (frameptr && (Include & RegSet_FramePointer)) \ |
| Registers[RegMIPS32::val] = true; \ |
| if (scratch && (Exclude & RegSet_CallerSave)) \ |
| Registers[RegMIPS32::val] = false; \ |
| if (preserved && (Exclude & RegSet_CalleeSave)) \ |
| Registers[RegMIPS32::val] = false; \ |
| if (stackptr && (Exclude & RegSet_StackPointer)) \ |
| Registers[RegMIPS32::val] = false; \ |
| if (frameptr && (Exclude & RegSet_FramePointer)) \ |
| Registers[RegMIPS32::val] = false; |
| |
| REGMIPS32_TABLE |
| |
| #undef X |
| |
| return Registers; |
| } |
| |
| void TargetMIPS32::lowerAlloca(const InstAlloca *Instr) { |
| UsesFramePointer = true; |
| // Conservatively require the stack to be aligned. Some stack adjustment |
| // operations implemented below assume that the stack is aligned before the |
| // alloca. All the alloca code ensures that the stack alignment is preserved |
| // after the alloca. The stack alignment restriction can be relaxed in some |
| // cases. |
| NeedsStackAlignment = true; |
| UnimplementedLoweringError(this, Instr); |
| } |
| |
| void TargetMIPS32::lowerInt64Arithmetic(const InstArithmetic *Instr, |
| Variable *Dest, Operand *Src0, |
| Operand *Src1) { |
| InstArithmetic::OpKind Op = Instr->getOp(); |
| switch (Op) { |
| case InstArithmetic::Add: |
| case InstArithmetic::And: |
| case InstArithmetic::Or: |
| case InstArithmetic::Sub: |
| case InstArithmetic::Xor: |
| case InstArithmetic::Mul: |
| break; |
| default: |
| UnimplementedLoweringError(this, Instr); |
| return; |
| } |
| auto *DestLo = llvm::cast<Variable>(loOperand(Dest)); |
| auto *DestHi = llvm::cast<Variable>(hiOperand(Dest)); |
| Variable *Src0LoR = legalizeToReg(loOperand(Src0)); |
| Variable *Src1LoR = legalizeToReg(loOperand(Src1)); |
| Variable *Src0HiR = legalizeToReg(hiOperand(Src0)); |
| Variable *Src1HiR = legalizeToReg(hiOperand(Src1)); |
| |
| switch (Op) { |
| case InstArithmetic::_num: |
| llvm::report_fatal_error("Unknown arithmetic operator"); |
| return; |
| case InstArithmetic::Add: { |
| auto *T_Carry = I32Reg(), *T_Lo = I32Reg(), *T_Hi = I32Reg(), |
| *T_Hi2 = I32Reg(); |
| _addu(T_Lo, Src0LoR, Src1LoR); |
| _mov(DestLo, T_Lo); |
| _sltu(T_Carry, T_Lo, Src0LoR); |
| _addu(T_Hi, T_Carry, Src0HiR); |
| _addu(T_Hi2, Src1HiR, T_Hi); |
| _mov(DestHi, T_Hi2); |
| return; |
| } |
| case InstArithmetic::And: { |
| auto *T_Lo = I32Reg(), *T_Hi = I32Reg(); |
| _and(T_Lo, Src0LoR, Src1LoR); |
| _mov(DestLo, T_Lo); |
| _and(T_Hi, Src0HiR, Src1HiR); |
| _mov(DestHi, T_Hi); |
| return; |
| } |
| case InstArithmetic::Sub: { |
| auto *T_Borrow = I32Reg(), *T_Lo = I32Reg(), *T_Hi = I32Reg(), |
| *T_Hi2 = I32Reg(); |
| _subu(T_Lo, Src0LoR, Src1LoR); |
| _mov(DestLo, T_Lo); |
| _sltu(T_Borrow, Src0LoR, Src1LoR); |
| _addu(T_Hi, T_Borrow, Src1HiR); |
| _subu(T_Hi2, Src0HiR, T_Hi); |
| _mov(DestHi, T_Hi2); |
| return; |
| } |
| case InstArithmetic::Or: { |
| auto *T_Lo = I32Reg(), *T_Hi = I32Reg(); |
| _or(T_Lo, Src0LoR, Src1LoR); |
| _mov(DestLo, T_Lo); |
| _or(T_Hi, Src0HiR, Src1HiR); |
| _mov(DestHi, T_Hi); |
| return; |
| } |
| case InstArithmetic::Xor: { |
| auto *T_Lo = I32Reg(), *T_Hi = I32Reg(); |
| _xor(T_Lo, Src0LoR, Src1LoR); |
| _mov(DestLo, T_Lo); |
| _xor(T_Hi, Src0HiR, Src1HiR); |
| _mov(DestHi, T_Hi); |
| return; |
| } |
| case InstArithmetic::Mul: { |
| // TODO(rkotler): Make sure that mul has the side effect of clobbering |
| // LO, HI. Check for any other LO, HI quirkiness in this section. |
| auto *T_Lo = I32Reg(RegMIPS32::Reg_LO), *T_Hi = I32Reg(RegMIPS32::Reg_HI); |
| auto *T1 = I32Reg(), *T2 = I32Reg(); |
| auto *TM1 = I32Reg(), *TM2 = I32Reg(), *TM3 = I32Reg(), *TM4 = I32Reg(); |
| _multu(T_Lo, Src0LoR, Src1LoR); |
| Context.insert<InstFakeDef>(T_Hi, T_Lo); |
| _mflo(T1, T_Lo); |
| _mfhi(T2, T_Hi); |
| _mov(DestLo, T1); |
| _mul(TM1, Src0HiR, Src1LoR); |
| _mul(TM2, Src0LoR, Src1HiR); |
| _addu(TM3, TM1, T2); |
| _addu(TM4, TM3, TM2); |
| _mov(DestHi, TM4); |
| return; |
| } |
| default: |
| UnimplementedLoweringError(this, Instr); |
| return; |
| } |
| } |
| |
| void TargetMIPS32::lowerArithmetic(const InstArithmetic *Instr) { |
| Variable *Dest = Instr->getDest(); |
| // We need to signal all the UnimplementedLoweringError errors before any |
| // legalization into new variables, otherwise Om1 register allocation may fail |
| // when it sees variables that are defined but not used. |
| Type DestTy = Dest->getType(); |
| Operand *Src0 = legalizeUndef(Instr->getSrc(0)); |
| Operand *Src1 = legalizeUndef(Instr->getSrc(1)); |
| if (DestTy == IceType_i64) { |
| lowerInt64Arithmetic(Instr, Instr->getDest(), Src0, Src1); |
| return; |
| } |
| if (isVectorType(Dest->getType())) { |
| UnimplementedLoweringError(this, Instr); |
| return; |
| } |
| switch (Instr->getOp()) { |
| default: |
| break; |
| case InstArithmetic::Shl: |
| case InstArithmetic::Lshr: |
| case InstArithmetic::Ashr: |
| case InstArithmetic::Udiv: |
| case InstArithmetic::Sdiv: |
| case InstArithmetic::Urem: |
| case InstArithmetic::Srem: |
| case InstArithmetic::Fadd: |
| case InstArithmetic::Fsub: |
| case InstArithmetic::Fmul: |
| case InstArithmetic::Fdiv: |
| case InstArithmetic::Frem: |
| UnimplementedLoweringError(this, Instr); |
| return; |
| } |
| |
| // At this point Dest->getType() is non-i64 scalar |
| |
| Variable *T = makeReg(Dest->getType()); |
| Variable *Src0R = legalizeToReg(Src0); |
| Variable *Src1R = legalizeToReg(Src1); |
| |
| switch (Instr->getOp()) { |
| case InstArithmetic::_num: |
| break; |
| case InstArithmetic::Add: |
| _addu(T, Src0R, Src1R); |
| _mov(Dest, T); |
| return; |
| case InstArithmetic::And: |
| _and(T, Src0R, Src1R); |
| _mov(Dest, T); |
| return; |
| case InstArithmetic::Or: |
| _or(T, Src0R, Src1R); |
| _mov(Dest, T); |
| return; |
| case InstArithmetic::Xor: |
| _xor(T, Src0R, Src1R); |
| _mov(Dest, T); |
| return; |
| case InstArithmetic::Sub: |
| _subu(T, Src0R, Src1R); |
| _mov(Dest, T); |
| return; |
| case InstArithmetic::Mul: { |
| _mul(T, Src0R, Src1R); |
| _mov(Dest, T); |
| return; |
| } |
| case InstArithmetic::Shl: |
| break; |
| case InstArithmetic::Lshr: |
| break; |
| case InstArithmetic::Ashr: |
| break; |
| case InstArithmetic::Udiv: |
| break; |
| case InstArithmetic::Sdiv: |
| break; |
| case InstArithmetic::Urem: |
| break; |
| case InstArithmetic::Srem: |
| break; |
| case InstArithmetic::Fadd: |
| break; |
| case InstArithmetic::Fsub: |
| break; |
| case InstArithmetic::Fmul: |
| break; |
| case InstArithmetic::Fdiv: |
| break; |
| case InstArithmetic::Frem: |
| break; |
| } |
| UnimplementedLoweringError(this, Instr); |
| } |
| |
| void TargetMIPS32::lowerAssign(const InstAssign *Instr) { |
| Variable *Dest = Instr->getDest(); |
| Operand *Src0 = Instr->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); |
| auto *DestLo = llvm::cast<Variable>(loOperand(Dest)); |
| auto *DestHi = llvm::cast<Variable>(hiOperand(Dest)); |
| // Variable *T_Lo = nullptr, *T_Hi = nullptr; |
| auto *T_Lo = I32Reg(), *T_Hi = I32Reg(); |
| _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())) { |
| UnimplementedLoweringError(this, Instr); |
| } else { |
| _mov(Dest, SrcR); |
| } |
| } |
| } |
| |
| void TargetMIPS32::lowerBr(const InstBr *Instr) { |
| if (Instr->isUnconditional()) { |
| _br(Instr->getTargetUnconditional()); |
| return; |
| } |
| UnimplementedLoweringError(this, Instr); |
| } |
| |
| void TargetMIPS32::lowerCall(const InstCall *Instr) { |
| // TODO(rkotler): assign arguments to registers and stack. Also reserve stack. |
| if (Instr->getNumArgs()) { |
| UnimplementedLoweringError(this, Instr); |
| return; |
| } |
| // Generate the call instruction. Assign its result to a temporary with high |
| // register allocation weight. |
| Variable *Dest = Instr->getDest(); |
| // ReturnReg doubles as ReturnRegLo as necessary. |
| Variable *ReturnReg = nullptr; |
| Variable *ReturnRegHi = nullptr; |
| if (Dest) { |
| switch (Dest->getType()) { |
| case IceType_NUM: |
| llvm_unreachable("Invalid Call dest type"); |
| return; |
| case IceType_void: |
| break; |
| case IceType_i1: |
| case IceType_i8: |
| case IceType_i16: |
| case IceType_i32: |
| ReturnReg = makeReg(Dest->getType(), RegMIPS32::Reg_V0); |
| break; |
| case IceType_i64: |
| ReturnReg = I32Reg(RegMIPS32::Reg_V0); |
| ReturnRegHi = I32Reg(RegMIPS32::Reg_V1); |
| break; |
| case IceType_f32: |
| case IceType_f64: |
| UnimplementedLoweringError(this, Instr); |
| return; |
| case IceType_v4i1: |
| case IceType_v8i1: |
| case IceType_v16i1: |
| case IceType_v16i8: |
| case IceType_v8i16: |
| case IceType_v4i32: |
| case IceType_v4f32: |
| UnimplementedLoweringError(this, Instr); |
| return; |
| } |
| } |
| Operand *CallTarget = Instr->getCallTarget(); |
| // Allow ConstantRelocatable to be left alone as a direct call, |
| // but force other constants like ConstantInteger32 to be in |
| // a register and make it an indirect call. |
| if (!llvm::isa<ConstantRelocatable>(CallTarget)) { |
| CallTarget = legalize(CallTarget, Legal_Reg); |
| } |
| Inst *NewCall = InstMIPS32Call::create(Func, ReturnReg, CallTarget); |
| Context.insert(NewCall); |
| if (ReturnRegHi) |
| Context.insert(InstFakeDef::create(Func, ReturnRegHi)); |
| // Insert a register-kill pseudo instruction. |
| Context.insert(InstFakeKill::create(Func, NewCall)); |
| // Generate a FakeUse to keep the call live if necessary. |
| if (Instr->hasSideEffects() && ReturnReg) { |
| Context.insert<InstFakeDef>(ReturnReg); |
| } |
| if (Dest == nullptr) |
| return; |
| |
| // Assign the result of the call to Dest. |
| if (ReturnReg) { |
| if (ReturnRegHi) { |
| assert(Dest->getType() == IceType_i64); |
| auto *Dest64On32 = llvm::cast<Variable64On32>(Dest); |
| Variable *DestLo = Dest64On32->getLo(); |
| Variable *DestHi = Dest64On32->getHi(); |
| _mov(DestLo, ReturnReg); |
| _mov(DestHi, ReturnRegHi); |
| } else { |
| assert(Dest->getType() == IceType_i32 || Dest->getType() == IceType_i16 || |
| Dest->getType() == IceType_i8 || Dest->getType() == IceType_i1 || |
| isVectorType(Dest->getType())); |
| if (isFloatingType(Dest->getType()) || isVectorType(Dest->getType())) { |
| UnimplementedLoweringError(this, Instr); |
| return; |
| } else { |
| _mov(Dest, ReturnReg); |
| } |
| } |
| } |
| } |
| |
| void TargetMIPS32::lowerCast(const InstCast *Instr) { |
| InstCast::OpKind CastKind = Instr->getCastKind(); |
| Variable *Dest = Instr->getDest(); |
| Operand *Src0 = legalizeUndef(Instr->getSrc(0)); |
| const Type DestTy = Dest->getType(); |
| const Type Src0Ty = Src0->getType(); |
| const uint32_t ShiftAmount = |
| INT32_BITS - (CHAR_BITS * typeWidthInBytes(Src0Ty)); |
| const uint32_t Mask = (1 << (CHAR_BITS * typeWidthInBytes(Src0Ty))) - 1; |
| |
| if (isVectorType(DestTy) || Src0->getType() == IceType_i1) { |
| UnimplementedLoweringError(this, Instr); |
| return; |
| } |
| switch (CastKind) { |
| default: |
| Func->setError("Cast type not supported"); |
| return; |
| case InstCast::Sext: { |
| if (DestTy == IceType_i64) { |
| auto *DestLo = llvm::cast<Variable>(loOperand(Dest)); |
| auto *DestHi = llvm::cast<Variable>(hiOperand(Dest)); |
| Variable *Src0R = legalizeToReg(Src0); |
| Variable *T_Lo = I32Reg(); |
| if (Src0Ty == IceType_i32) { |
| _mov(DestLo, Src0R); |
| } else if (Src0Ty == IceType_i8 || Src0Ty == IceType_i16) { |
| _sll(T_Lo, Src0R, ShiftAmount); |
| _sra(DestLo, T_Lo, ShiftAmount); |
| } |
| _sra(DestHi, DestLo, INT32_BITS - 1); |
| } else { |
| Variable *Src0R = legalizeToReg(Src0); |
| Variable *T = makeReg(DestTy); |
| if (Src0Ty == IceType_i8 || Src0Ty == IceType_i16) { |
| _sll(T, Src0R, ShiftAmount); |
| _sra(Dest, T, ShiftAmount); |
| } |
| } |
| break; |
| } |
| case InstCast::Zext: { |
| if (DestTy == IceType_i64) { |
| auto *DestLo = llvm::cast<Variable>(loOperand(Dest)); |
| auto *DestHi = llvm::cast<Variable>(hiOperand(Dest)); |
| Variable *Src0R = legalizeToReg(Src0); |
| |
| switch (Src0Ty) { |
| default: { assert(Src0Ty != IceType_i64); } break; |
| case IceType_i32: |
| _mov(DestLo, Src0R); |
| break; |
| case IceType_i8: |
| case IceType_i16: |
| _andi(DestLo, Src0R, Mask); |
| break; |
| } |
| |
| auto *Zero = getZero(); |
| _addiu(DestHi, Zero, 0); |
| } else { |
| Variable *Src0R = legalizeToReg(Src0); |
| Variable *T = makeReg(DestTy); |
| if (Src0Ty == IceType_i8 || Src0Ty == IceType_i16) |
| _andi(T, Src0R, Mask); |
| _mov(Dest, T); |
| } |
| break; |
| } |
| case InstCast::Trunc: { |
| if (Src0Ty == IceType_i64) |
| Src0 = loOperand(Src0); |
| Variable *Src0R = legalizeToReg(Src0); |
| Variable *T = makeReg(DestTy); |
| _mov(T, Src0R); |
| _mov(Dest, T); |
| break; |
| } |
| case InstCast::Fptrunc: |
| UnimplementedLoweringError(this, Instr); |
| break; |
| case InstCast::Fpext: { |
| UnimplementedLoweringError(this, Instr); |
| break; |
| } |
| case InstCast::Fptosi: |
| UnimplementedLoweringError(this, Instr); |
| break; |
| case InstCast::Fptoui: |
| UnimplementedLoweringError(this, Instr); |
| break; |
| case InstCast::Sitofp: |
| UnimplementedLoweringError(this, Instr); |
| break; |
| case InstCast::Uitofp: { |
| UnimplementedLoweringError(this, Instr); |
| break; |
| } |
| case InstCast::Bitcast: { |
| UnimplementedLoweringError(this, Instr); |
| break; |
| } |
| } |
| } |
| |
| void TargetMIPS32::lowerExtractElement(const InstExtractElement *Instr) { |
| UnimplementedLoweringError(this, Instr); |
| } |
| |
| void TargetMIPS32::lowerFcmp(const InstFcmp *Instr) { |
| UnimplementedLoweringError(this, Instr); |
| } |
| |
| void TargetMIPS32::lower64Icmp(const InstIcmp *Instr) { |
| UnimplementedLoweringError(this, Instr); |
| return; |
| } |
| |
| void TargetMIPS32::lowerIcmp(const InstIcmp *Instr) { |
| auto *Src0 = Instr->getSrc(0); |
| auto *Src1 = Instr->getSrc(1); |
| if (Src0->getType() == IceType_i64) { |
| lower64Icmp(Instr); |
| return; |
| } |
| Variable *Dest = Instr->getDest(); |
| if (isVectorType(Dest->getType())) { |
| UnimplementedLoweringError(this, Instr); |
| return; |
| } |
| InstIcmp::ICond Cond = Instr->getCondition(); |
| auto *Src0R = legalizeToReg(Src0); |
| auto *Src1R = legalizeToReg(Src1); |
| switch (Cond) { |
| case InstIcmp::Eq: { |
| auto *DestT = I32Reg(); |
| auto *T = I32Reg(); |
| _xor(T, Src0R, Src1R); |
| _sltiu(DestT, T, 1); |
| _mov(Dest, DestT); |
| return; |
| } |
| case InstIcmp::Ne: { |
| auto *DestT = I32Reg(); |
| auto *T = I32Reg(); |
| auto *Zero = getZero(); |
| _xor(T, Src0R, Src1R); |
| _sltu(DestT, Zero, T); |
| _mov(Dest, DestT); |
| return; |
| } |
| case InstIcmp::Ugt: { |
| auto *DestT = I32Reg(); |
| _sltu(DestT, Src1R, Src0R); |
| _mov(Dest, DestT); |
| return; |
| } |
| case InstIcmp::Uge: { |
| auto *DestT = I32Reg(); |
| auto *T = I32Reg(); |
| _sltu(T, Src0R, Src1R); |
| _xori(DestT, T, 1); |
| _mov(Dest, DestT); |
| return; |
| } |
| case InstIcmp::Ult: { |
| auto *DestT = I32Reg(); |
| _sltu(DestT, Src0R, Src1R); |
| _mov(Dest, DestT); |
| return; |
| } |
| case InstIcmp::Ule: { |
| auto *DestT = I32Reg(); |
| auto *T = I32Reg(); |
| _sltu(T, Src1R, Src0R); |
| _xori(DestT, T, 1); |
| _mov(Dest, DestT); |
| return; |
| } |
| case InstIcmp::Sgt: { |
| auto *DestT = I32Reg(); |
| _slt(DestT, Src1R, Src0R); |
| _mov(Dest, DestT); |
| return; |
| } |
| case InstIcmp::Sge: { |
| auto *DestT = I32Reg(); |
| auto *T = I32Reg(); |
| _slt(T, Src1R, Src0R); |
| _xori(DestT, T, 1); |
| _mov(Dest, DestT); |
| return; |
| } |
| case InstIcmp::Slt: { |
| auto *DestT = I32Reg(); |
| _slt(DestT, Src0R, Src1R); |
| _mov(Dest, DestT); |
| return; |
| } |
| case InstIcmp::Sle: { |
| auto *DestT = I32Reg(); |
| auto *T = I32Reg(); |
| _slt(T, Src1R, Src0R); |
| _xori(DestT, T, 1); |
| _mov(Dest, DestT); |
| return; |
| } |
| default: |
| llvm_unreachable("Invalid ICmp operator"); |
| return; |
| } |
| } |
| |
| void TargetMIPS32::lowerInsertElement(const InstInsertElement *Instr) { |
| UnimplementedLoweringError(this, Instr); |
| } |
| |
| void TargetMIPS32::lowerIntrinsicCall(const InstIntrinsicCall *Instr) { |
| switch (Instr->getIntrinsicInfo().ID) { |
| case Intrinsics::AtomicCmpxchg: { |
| UnimplementedLoweringError(this, Instr); |
| return; |
| } |
| case Intrinsics::AtomicFence: |
| UnimplementedLoweringError(this, Instr); |
| return; |
| case Intrinsics::AtomicFenceAll: |
| // NOTE: FenceAll should prevent and load/store from being moved across the |
| // fence (both atomic and non-atomic). The InstMIPS32Mfence instruction is |
| // currently marked coarsely as "HasSideEffects". |
| UnimplementedLoweringError(this, Instr); |
| return; |
| case Intrinsics::AtomicIsLockFree: { |
| UnimplementedLoweringError(this, Instr); |
| return; |
| } |
| case Intrinsics::AtomicLoad: { |
| UnimplementedLoweringError(this, Instr); |
| return; |
| } |
| case Intrinsics::AtomicRMW: |
| UnimplementedLoweringError(this, Instr); |
| return; |
| case Intrinsics::AtomicStore: { |
| UnimplementedLoweringError(this, Instr); |
| return; |
| } |
| case Intrinsics::Bswap: { |
| UnimplementedLoweringError(this, Instr); |
| return; |
| } |
| case Intrinsics::Ctpop: { |
| UnimplementedLoweringError(this, Instr); |
| return; |
| } |
| case Intrinsics::Ctlz: { |
| UnimplementedLoweringError(this, Instr); |
| return; |
| } |
| case Intrinsics::Cttz: { |
| UnimplementedLoweringError(this, Instr); |
| return; |
| } |
| case Intrinsics::Fabs: { |
| UnimplementedLoweringError(this, Instr); |
| return; |
| } |
| case Intrinsics::Longjmp: { |
| InstCall *Call = makeHelperCall(RuntimeHelper::H_call_longjmp, nullptr, 2); |
| Call->addArg(Instr->getArg(0)); |
| Call->addArg(Instr->getArg(1)); |
| lowerCall(Call); |
| return; |
| } |
| case Intrinsics::Memcpy: { |
| // In the future, we could potentially emit an inline memcpy/memset, etc. |
| // for intrinsic calls w/ a known length. |
| InstCall *Call = makeHelperCall(RuntimeHelper::H_call_memcpy, nullptr, 3); |
| Call->addArg(Instr->getArg(0)); |
| Call->addArg(Instr->getArg(1)); |
| Call->addArg(Instr->getArg(2)); |
| lowerCall(Call); |
| return; |
| } |
| case Intrinsics::Memmove: { |
| InstCall *Call = makeHelperCall(RuntimeHelper::H_call_memmove, nullptr, 3); |
| Call->addArg(Instr->getArg(0)); |
| Call->addArg(Instr->getArg(1)); |
| Call->addArg(Instr->getArg(2)); |
| lowerCall(Call); |
| return; |
| } |
| case Intrinsics::Memset: { |
| // The value operand needs to be extended to a stack slot size because the |
| // PNaCl ABI requires arguments to be at least 32 bits wide. |
| Operand *ValOp = Instr->getArg(1); |
| assert(ValOp->getType() == IceType_i8); |
| Variable *ValExt = Func->makeVariable(stackSlotType()); |
| lowerCast(InstCast::create(Func, InstCast::Zext, ValExt, ValOp)); |
| InstCall *Call = makeHelperCall(RuntimeHelper::H_call_memset, nullptr, 3); |
| Call->addArg(Instr->getArg(0)); |
| Call->addArg(ValExt); |
| Call->addArg(Instr->getArg(2)); |
| lowerCall(Call); |
| return; |
| } |
| case Intrinsics::NaClReadTP: { |
| if (getFlags().getUseSandboxing()) { |
| UnimplementedLoweringError(this, Instr); |
| } else { |
| InstCall *Call = |
| makeHelperCall(RuntimeHelper::H_call_read_tp, Instr->getDest(), 0); |
| lowerCall(Call); |
| } |
| return; |
| } |
| case Intrinsics::Setjmp: { |
| InstCall *Call = |
| makeHelperCall(RuntimeHelper::H_call_setjmp, Instr->getDest(), 1); |
| Call->addArg(Instr->getArg(0)); |
| lowerCall(Call); |
| return; |
| } |
| case Intrinsics::Sqrt: { |
| UnimplementedLoweringError(this, Instr); |
| return; |
| } |
| case Intrinsics::Stacksave: { |
| UnimplementedLoweringError(this, Instr); |
| return; |
| } |
| case Intrinsics::Stackrestore: { |
| UnimplementedLoweringError(this, Instr); |
| return; |
| } |
| case Intrinsics::Trap: |
| UnimplementedLoweringError(this, Instr); |
| return; |
| case Intrinsics::UnknownIntrinsic: |
| Func->setError("Should not be lowering UnknownIntrinsic"); |
| return; |
| } |
| return; |
| } |
| |
| void TargetMIPS32::lowerLoad(const InstLoad *Instr) { |
| UnimplementedLoweringError(this, Instr); |
| } |
| |
| void TargetMIPS32::doAddressOptLoad() { UnimplementedError(getFlags()); } |
| |
| void TargetMIPS32::randomlyInsertNop(float Probability, |
| RandomNumberGenerator &RNG) { |
| RandomNumberGeneratorWrapper RNGW(RNG); |
| if (RNGW.getTrueWithProbability(Probability)) { |
| UnimplementedError(getFlags()); |
| } |
| } |
| |
| void TargetMIPS32::lowerPhi(const InstPhi * /*Instr*/) { |
| Func->setError("Phi found in regular instruction list"); |
| } |
| |
| void TargetMIPS32::lowerRet(const InstRet *Instr) { |
| Variable *Reg = nullptr; |
| if (Instr->hasRetValue()) { |
| Operand *Src0 = Instr->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>(R1); |
| break; |
| } |
| |
| default: |
| UnimplementedLoweringError(this, Instr); |
| } |
| } |
| _ret(getPhysicalRegister(RegMIPS32::Reg_RA), Reg); |
| } |
| |
| void TargetMIPS32::lowerSelect(const InstSelect *Instr) { |
| UnimplementedLoweringError(this, Instr); |
| } |
| |
| void TargetMIPS32::lowerShuffleVector(const InstShuffleVector *Instr) { |
| UnimplementedLoweringError(this, Instr); |
| } |
| |
| void TargetMIPS32::lowerStore(const InstStore *Instr) { |
| UnimplementedLoweringError(this, Instr); |
| } |
| |
| void TargetMIPS32::doAddressOptStore() { UnimplementedError(getFlags()); } |
| |
| void TargetMIPS32::lowerSwitch(const InstSwitch *Instr) { |
| UnimplementedLoweringError(this, Instr); |
| } |
| |
| void TargetMIPS32::lowerBreakpoint(const InstBreakpoint *Instr) { |
| UnimplementedLoweringError(this, Instr); |
| } |
| |
| void TargetMIPS32::lowerUnreachable(const InstUnreachable *Instr) { |
| UnimplementedLoweringError(this, Instr); |
| } |
| |
| // Turn an i64 Phi instruction into a pair of i32 Phi instructions, to preserve |
| // integrity of liveness analysis. Undef values are also turned into zeroes, |
| // since loOperand() and hiOperand() don't expect Undef input. |
| void TargetMIPS32::prelowerPhis() { |
| PhiLowering::prelowerPhis32Bit<TargetMIPS32>(this, Context.getNode(), Func); |
| } |
| |
| void TargetMIPS32::postLower() { |
| if (getFlags().getOptLevel() == Opt_m1) |
| return; |
| // TODO(rkotler): Find two-address non-SSA instructions where Dest==Src0, |
| // and set the IsDestRedefined flag to keep liveness analysis consistent. |
| UnimplementedError(getFlags()); |
| } |
| |
| void TargetMIPS32::makeRandomRegisterPermutation( |
| llvm::SmallVectorImpl<RegNumT> &Permutation, |
| const SmallBitVector &ExcludeRegisters, uint64_t Salt) const { |
| (void)Permutation; |
| (void)ExcludeRegisters; |
| (void)Salt; |
| UnimplementedError(getFlags()); |
| } |
| |
| /* TODO(jvoung): avoid duplicate symbols with multiple targets. |
| void ConstantUndef::emitWithoutDollar(GlobalContext *) const { |
| llvm_unreachable("Not expecting to emitWithoutDollar undef"); |
| } |
| |
| void ConstantUndef::emit(GlobalContext *) const { |
| llvm_unreachable("undef value encountered by emitter."); |
| } |
| */ |
| |
| TargetDataMIPS32::TargetDataMIPS32(GlobalContext *Ctx) |
| : TargetDataLowering(Ctx) {} |
| |
| void TargetDataMIPS32::lowerGlobals(const VariableDeclarationList &Vars, |
| const std::string &SectionSuffix) { |
| const bool IsPIC = getFlags().getUseNonsfi(); |
| switch (getFlags().getOutFileType()) { |
| case FT_Elf: { |
| ELFObjectWriter *Writer = Ctx->getObjectWriter(); |
| Writer->writeDataSection(Vars, llvm::ELF::R_MIPS_GLOB_DAT, SectionSuffix, |
| IsPIC); |
| } break; |
| case FT_Asm: |
| case FT_Iasm: { |
| OstreamLocker L(Ctx); |
| for (const VariableDeclaration *Var : Vars) { |
| if (getFlags().matchTranslateOnly(Var->getName(), 0)) { |
| emitGlobal(*Var, SectionSuffix); |
| } |
| } |
| } break; |
| } |
| } |
| |
| void TargetDataMIPS32::lowerConstants() { |
| if (getFlags().getDisableTranslation()) |
| return; |
| UnimplementedError(getFlags()); |
| } |
| |
| void TargetDataMIPS32::lowerJumpTables() { |
| if (getFlags().getDisableTranslation()) |
| return; |
| UnimplementedError(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, RegNumT RegNum) { |
| Type Ty = Src->getType(); |
| Variable *Reg = makeReg(Ty, RegNum); |
| if (isVectorType(Ty) || isFloatingType(Ty)) { |
| UnimplementedError(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, |
| RegNumT 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, 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; |
| // TODO(reed kotler): complete this case for proper implementation |
| Variable *Reg = makeReg(Ty, RegNum); |
| Context.insert<InstFakeDef>(Reg); |
| return Reg; |
| } else if (auto *C32 = llvm::dyn_cast<ConstantInteger32>(From)) { |
| const uint32_t Value = 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.hasValue()) |
| Reg = getPhysicalRegister(RegNum); |
| else |
| Reg = makeReg(Ty, RegNum); |
| if (isInt<16>(int32_t(Value))) { |
| Variable *Zero = getPhysicalRegister(RegMIPS32::Reg_ZERO, Ty); |
| Context.insert<InstFakeDef>(Zero); |
| _addiu(Reg, Zero, 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.hasValue() && 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\t" |
| << "nomicromips\n"; |
| Str << "\t.set\t" |
| << "nomips16\n"; |
| } |
| |
| SmallBitVector TargetMIPS32::TypeToRegisterSet[RCMIPS32_NUM]; |
| SmallBitVector TargetMIPS32::TypeToRegisterSetUnfiltered[RCMIPS32_NUM]; |
| SmallBitVector TargetMIPS32::RegisterAliases[RegMIPS32::Reg_NUM]; |
| |
| } // end of namespace MIPS32 |
| } // end of namespace Ice |