| //===- subzero/src/IceAssemblerMIPS32.cpp - MIPS32 Assembler --------------===// |
| // |
| // 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 Assembler class for MIPS32. |
| /// |
| //===----------------------------------------------------------------------===// |
| |
| #include "IceAssemblerMIPS32.h" |
| #include "IceCfgNode.h" |
| #include "IceRegistersMIPS32.h" |
| #include "IceUtils.h" |
| |
| namespace { |
| |
| using namespace Ice; |
| using namespace Ice::MIPS32; |
| |
| // Offset modifier to current PC for next instruction. |
| static constexpr IOffsetT kPCReadOffset = 4; |
| |
| // Mask to pull out PC offset from branch instruction. |
| static constexpr int kBranchOffsetBits = 16; |
| static constexpr IOffsetT kBranchOffsetMask = 0x0000ffff; |
| |
| } // end of anonymous namespace |
| |
| namespace Ice { |
| namespace MIPS32 { |
| |
| void AssemblerMIPS32::emitTextInst(const std::string &Text, SizeT InstSize) { |
| AssemblerFixup *F = createTextFixup(Text, InstSize); |
| emitFixup(F); |
| for (SizeT I = 0; I < InstSize; ++I) { |
| AssemblerBuffer::EnsureCapacity ensured(&Buffer); |
| Buffer.emit<char>(0); |
| } |
| } |
| |
| namespace { |
| |
| // TEQ $0, $0 - Trap if equal |
| static constexpr uint8_t TrapBytesRaw[] = {0x00, 0x00, 0x00, 0x34}; |
| |
| const auto TrapBytes = |
| llvm::ArrayRef<uint8_t>(TrapBytesRaw, llvm::array_lengthof(TrapBytesRaw)); |
| |
| } // end of anonymous namespace |
| |
| llvm::ArrayRef<uint8_t> AssemblerMIPS32::getNonExecBundlePadding() const { |
| return TrapBytes; |
| } |
| |
| void AssemblerMIPS32::trap() { |
| AssemblerBuffer::EnsureCapacity ensured(&Buffer); |
| for (const uint8_t &Byte : reverse_range(TrapBytes)) |
| Buffer.emit<uint8_t>(Byte); |
| } |
| |
| void AssemblerMIPS32::nop() { emitInst(0); } |
| |
| void AssemblerMIPS32::padWithNop(intptr_t Padding) { |
| constexpr intptr_t InstWidth = sizeof(IValueT); |
| assert(Padding % InstWidth == 0 && |
| "Padding not multiple of instruction size"); |
| for (intptr_t i = 0; i < Padding; i += InstWidth) |
| nop(); |
| } |
| |
| Label *AssemblerMIPS32::getOrCreateLabel(SizeT Number, LabelVector &Labels) { |
| Label *L = nullptr; |
| if (Number == Labels.size()) { |
| L = new (this->allocate<Label>()) Label(); |
| Labels.push_back(L); |
| return L; |
| } |
| if (Number > Labels.size()) { |
| Labels.resize(Number + 1); |
| } |
| L = Labels[Number]; |
| if (L == nullptr) { |
| L = new (this->allocate<Label>()) Label(); |
| Labels[Number] = L; |
| } |
| return L; |
| } |
| |
| void AssemblerMIPS32::bindCfgNodeLabel(const CfgNode *Node) { |
| if (BuildDefs::dump() && !getFlags().getDisableHybridAssembly()) { |
| constexpr SizeT InstSize = 0; |
| emitTextInst(Node->getAsmName() + ":", InstSize); |
| } |
| SizeT NodeNumber = Node->getIndex(); |
| assert(!getPreliminary()); |
| Label *L = getOrCreateCfgNodeLabel(NodeNumber); |
| this->bind(L); |
| } |
| |
| namespace { |
| |
| // Checks that Offset can fit in imm16 constant of branch instruction. |
| void assertCanEncodeBranchOffset(IOffsetT Offset) { |
| (void)Offset; |
| (void)kBranchOffsetBits; |
| assert(Utils::IsAligned(Offset, 4)); |
| assert(Utils::IsInt(kBranchOffsetBits, Offset >> 2)); |
| } |
| |
| IValueT encodeBranchOffset(IOffsetT Offset, IValueT Inst) { |
| Offset -= kPCReadOffset; |
| assertCanEncodeBranchOffset(Offset); |
| Offset >>= 2; |
| Offset &= kBranchOffsetMask; |
| return (Inst & ~kBranchOffsetMask) | Offset; |
| } |
| |
| enum RegSetWanted { WantGPRegs, WantFPRegs }; |
| |
| IValueT getEncodedGPRegNum(const Variable *Var) { |
| assert(Var->hasReg() && isScalarIntegerType(Var->getType())); |
| const auto Reg = Var->getRegNum(); |
| return RegMIPS32::getEncodedGPR(Reg); |
| } |
| |
| IValueT getEncodedFPRegNum(const Variable *Var) { |
| assert(Var->hasReg() && isScalarFloatingType(Var->getType())); |
| const auto Reg = Var->getRegNum(); |
| IValueT RegEncoding; |
| if (RegMIPS32::isFPRReg(Reg)) { |
| RegEncoding = RegMIPS32::getEncodedFPR(Reg); |
| } else { |
| RegEncoding = RegMIPS32::getEncodedFPR64(Reg); |
| } |
| return RegEncoding; |
| } |
| |
| bool encodeOperand(const Operand *Opnd, IValueT &Value, |
| RegSetWanted WantedRegSet) { |
| Value = 0; |
| if (const auto *Var = llvm::dyn_cast<Variable>(Opnd)) { |
| if (Var->hasReg()) { |
| switch (WantedRegSet) { |
| case WantGPRegs: |
| Value = getEncodedGPRegNum(Var); |
| break; |
| case WantFPRegs: |
| Value = getEncodedFPRegNum(Var); |
| break; |
| } |
| return true; |
| } |
| return false; |
| } |
| return false; |
| } |
| |
| IValueT encodeRegister(const Operand *OpReg, RegSetWanted WantedRegSet, |
| const char *RegName, const char *InstName) { |
| IValueT Reg = 0; |
| if (encodeOperand(OpReg, Reg, WantedRegSet) != true) |
| llvm::report_fatal_error(std::string(InstName) + ": Can't find register " + |
| RegName); |
| return Reg; |
| } |
| |
| IValueT encodeGPRegister(const Operand *OpReg, const char *RegName, |
| const char *InstName) { |
| return encodeRegister(OpReg, WantGPRegs, RegName, InstName); |
| } |
| |
| IValueT encodeFPRegister(const Operand *OpReg, const char *RegName, |
| const char *InstName) { |
| return encodeRegister(OpReg, WantFPRegs, RegName, InstName); |
| } |
| |
| } // end of anonymous namespace |
| |
| IOffsetT AssemblerMIPS32::decodeBranchOffset(IValueT Inst) { |
| int16_t imm = (Inst & kBranchOffsetMask); |
| IOffsetT Offset = imm; |
| Offset = Offset << 2; |
| return (Offset + kPCReadOffset); |
| } |
| |
| void AssemblerMIPS32::bind(Label *L) { |
| IOffsetT BoundPc = Buffer.size(); |
| assert(!L->isBound()); // Labels can only be bound once. |
| while (L->isLinked()) { |
| IOffsetT Position = L->getLinkPosition(); |
| IOffsetT Dest = BoundPc - Position; |
| IValueT Inst = Buffer.load<IValueT>(Position); |
| Buffer.store<IValueT>(Position, encodeBranchOffset(Dest, Inst)); |
| L->setPosition(decodeBranchOffset(Inst)); |
| } |
| L->bindTo(BoundPc); |
| } |
| |
| void AssemblerMIPS32::emitRsRt(IValueT Opcode, const Operand *OpRs, |
| const Operand *OpRt, const char *InsnName) { |
| const IValueT Rs = encodeGPRegister(OpRs, "Rs", InsnName); |
| const IValueT Rt = encodeGPRegister(OpRt, "Rt", InsnName); |
| |
| Opcode |= Rs << 21; |
| Opcode |= Rt << 16; |
| |
| emitInst(Opcode); |
| } |
| |
| void AssemblerMIPS32::emitRtRsImm16(IValueT Opcode, const Operand *OpRt, |
| const Operand *OpRs, const uint32_t Imm, |
| const char *InsnName) { |
| const IValueT Rt = encodeGPRegister(OpRt, "Rt", InsnName); |
| const IValueT Rs = encodeGPRegister(OpRs, "Rs", InsnName); |
| |
| Opcode |= Rs << 21; |
| Opcode |= Rt << 16; |
| Opcode |= Imm & 0xffff; |
| |
| emitInst(Opcode); |
| } |
| |
| void AssemblerMIPS32::emitFtRsImm16(IValueT Opcode, const Operand *OpFt, |
| const Operand *OpRs, const uint32_t Imm, |
| const char *InsnName) { |
| const IValueT Ft = encodeFPRegister(OpFt, "Ft", InsnName); |
| const IValueT Rs = encodeGPRegister(OpRs, "Rs", InsnName); |
| |
| Opcode |= Rs << 21; |
| Opcode |= Ft << 16; |
| Opcode |= Imm & 0xffff; |
| |
| emitInst(Opcode); |
| } |
| |
| void AssemblerMIPS32::emitRdRtSa(IValueT Opcode, const Operand *OpRd, |
| const Operand *OpRt, const uint32_t Sa, |
| const char *InsnName) { |
| const IValueT Rd = encodeGPRegister(OpRd, "Rd", InsnName); |
| const IValueT Rt = encodeGPRegister(OpRt, "Rt", InsnName); |
| |
| Opcode |= Rt << 16; |
| Opcode |= Rd << 11; |
| Opcode |= (Sa & 0x1f) << 6; |
| |
| emitInst(Opcode); |
| } |
| |
| void AssemblerMIPS32::emitRdRsRt(IValueT Opcode, const Operand *OpRd, |
| const Operand *OpRs, const Operand *OpRt, |
| const char *InsnName) { |
| const IValueT Rd = encodeGPRegister(OpRd, "Rd", InsnName); |
| const IValueT Rs = encodeGPRegister(OpRs, "Rs", InsnName); |
| const IValueT Rt = encodeGPRegister(OpRt, "Rt", InsnName); |
| |
| Opcode |= Rs << 21; |
| Opcode |= Rt << 16; |
| Opcode |= Rd << 11; |
| |
| emitInst(Opcode); |
| } |
| |
| void AssemblerMIPS32::emitCOP1Fcmp(IValueT Opcode, FPInstDataFormat Format, |
| const Operand *OpFs, const Operand *OpFt, |
| IValueT CC, const char *InsnName) { |
| const IValueT Fs = encodeFPRegister(OpFs, "Fs", InsnName); |
| const IValueT Ft = encodeFPRegister(OpFt, "Ft", InsnName); |
| |
| Opcode |= CC << 8; |
| Opcode |= Fs << 11; |
| Opcode |= Ft << 16; |
| Opcode |= Format << 21; |
| |
| emitInst(Opcode); |
| } |
| |
| void AssemblerMIPS32::emitCOP1FmtFsFd(IValueT Opcode, FPInstDataFormat Format, |
| const Operand *OpFd, const Operand *OpFs, |
| const char *InsnName) { |
| const IValueT Fd = encodeFPRegister(OpFd, "Fd", InsnName); |
| const IValueT Fs = encodeFPRegister(OpFs, "Fs", InsnName); |
| |
| Opcode |= Fd << 6; |
| Opcode |= Fs << 11; |
| Opcode |= Format << 21; |
| |
| emitInst(Opcode); |
| } |
| |
| void AssemblerMIPS32::emitCOP1FmtFtFsFd(IValueT Opcode, FPInstDataFormat Format, |
| const Operand *OpFd, |
| const Operand *OpFs, |
| const Operand *OpFt, |
| const char *InsnName) { |
| const IValueT Fd = encodeFPRegister(OpFd, "Fd", InsnName); |
| const IValueT Fs = encodeFPRegister(OpFs, "Fs", InsnName); |
| const IValueT Ft = encodeFPRegister(OpFt, "Ft", InsnName); |
| |
| Opcode |= Fd << 6; |
| Opcode |= Fs << 11; |
| Opcode |= Ft << 16; |
| Opcode |= Format << 21; |
| |
| emitInst(Opcode); |
| } |
| |
| void AssemblerMIPS32::emitCOP1FmtRtFsFd(IValueT Opcode, FPInstDataFormat Format, |
| const Operand *OpFd, |
| const Operand *OpFs, |
| const Operand *OpRt, |
| const char *InsnName) { |
| const IValueT Fd = encodeFPRegister(OpFd, "Fd", InsnName); |
| const IValueT Fs = encodeFPRegister(OpFs, "Fs", InsnName); |
| const IValueT Rt = encodeGPRegister(OpRt, "Rt", InsnName); |
| |
| Opcode |= Fd << 6; |
| Opcode |= Fs << 11; |
| Opcode |= Rt << 16; |
| Opcode |= Format << 21; |
| |
| emitInst(Opcode); |
| } |
| |
| void AssemblerMIPS32::emitCOP1MovRtFs(IValueT Opcode, const Operand *OpRt, |
| const Operand *OpFs, |
| const char *InsnName) { |
| const IValueT Rt = encodeGPRegister(OpRt, "Rt", InsnName); |
| const IValueT Fs = encodeFPRegister(OpFs, "Fs", InsnName); |
| Opcode |= Fs << 11; |
| Opcode |= Rt << 16; |
| |
| emitInst(Opcode); |
| } |
| |
| void AssemblerMIPS32::abs_d(const Operand *OpFd, const Operand *OpFs) { |
| static constexpr IValueT Opcode = 0x44000005; |
| emitCOP1FmtFsFd(Opcode, DoublePrecision, OpFd, OpFs, "abs.d"); |
| } |
| |
| void AssemblerMIPS32::abs_s(const Operand *OpFd, const Operand *OpFs) { |
| static constexpr IValueT Opcode = 0x44000005; |
| emitCOP1FmtFsFd(Opcode, SinglePrecision, OpFd, OpFs, "abs.s"); |
| } |
| |
| void AssemblerMIPS32::add_d(const Operand *OpFd, const Operand *OpFs, |
| const Operand *OpFt) { |
| static constexpr IValueT Opcode = 0x44000000; |
| emitCOP1FmtFtFsFd(Opcode, DoublePrecision, OpFd, OpFs, OpFt, "add.d"); |
| } |
| |
| void AssemblerMIPS32::add_s(const Operand *OpFd, const Operand *OpFs, |
| const Operand *OpFt) { |
| static constexpr IValueT Opcode = 0x44000000; |
| emitCOP1FmtFtFsFd(Opcode, SinglePrecision, OpFd, OpFs, OpFt, "add.s"); |
| } |
| |
| void AssemblerMIPS32::addiu(const Operand *OpRt, const Operand *OpRs, |
| const uint32_t Imm) { |
| static constexpr IValueT Opcode = 0x24000000; |
| emitRtRsImm16(Opcode, OpRt, OpRs, Imm, "addiu"); |
| } |
| |
| void AssemblerMIPS32::addu(const Operand *OpRd, const Operand *OpRs, |
| const Operand *OpRt) { |
| static constexpr IValueT Opcode = 0x00000021; |
| emitRdRsRt(Opcode, OpRd, OpRs, OpRt, "addu"); |
| } |
| |
| void AssemblerMIPS32::and_(const Operand *OpRd, const Operand *OpRs, |
| const Operand *OpRt) { |
| static constexpr IValueT Opcode = 0x00000024; |
| emitRdRsRt(Opcode, OpRd, OpRs, OpRt, "and"); |
| } |
| |
| void AssemblerMIPS32::andi(const Operand *OpRt, const Operand *OpRs, |
| const uint32_t Imm) { |
| static constexpr IValueT Opcode = 0x30000000; |
| emitRtRsImm16(Opcode, OpRt, OpRs, Imm, "andi"); |
| } |
| |
| void AssemblerMIPS32::b(Label *TargetLabel) { |
| static constexpr Operand *OpRsNone = nullptr; |
| static constexpr Operand *OpRtNone = nullptr; |
| if (TargetLabel->isBound()) { |
| const int32_t Dest = TargetLabel->getPosition() - Buffer.size(); |
| emitBr(CondMIPS32::AL, OpRsNone, OpRtNone, Dest); |
| return; |
| } |
| const IOffsetT Position = Buffer.size(); |
| emitBr(CondMIPS32::AL, OpRsNone, OpRtNone, TargetLabel->getEncodedPosition()); |
| TargetLabel->linkTo(*this, Position); |
| } |
| |
| void AssemblerMIPS32::c_eq_d(const Operand *OpFs, const Operand *OpFt) { |
| static constexpr IValueT Opcode = 0x44000032; |
| emitCOP1Fcmp(Opcode, DoublePrecision, OpFs, OpFt, OperandMIPS32FCC::FCC0, |
| "c.eq.d"); |
| } |
| |
| void AssemblerMIPS32::c_eq_s(const Operand *OpFs, const Operand *OpFt) { |
| static constexpr IValueT Opcode = 0x44000032; |
| emitCOP1Fcmp(Opcode, SinglePrecision, OpFs, OpFt, OperandMIPS32FCC::FCC0, |
| "c.eq.s"); |
| } |
| |
| void AssemblerMIPS32::c_ole_d(const Operand *OpFs, const Operand *OpFt) { |
| static constexpr IValueT Opcode = 0x44000036; |
| emitCOP1Fcmp(Opcode, DoublePrecision, OpFs, OpFt, OperandMIPS32FCC::FCC0, |
| "c.ole.d"); |
| } |
| |
| void AssemblerMIPS32::c_ole_s(const Operand *OpFs, const Operand *OpFt) { |
| static constexpr IValueT Opcode = 0x44000036; |
| emitCOP1Fcmp(Opcode, SinglePrecision, OpFs, OpFt, OperandMIPS32FCC::FCC0, |
| "c.ole.s"); |
| } |
| |
| void AssemblerMIPS32::c_olt_d(const Operand *OpFs, const Operand *OpFt) { |
| static constexpr IValueT Opcode = 0x44000034; |
| emitCOP1Fcmp(Opcode, DoublePrecision, OpFs, OpFt, OperandMIPS32FCC::FCC0, |
| "c.olt.d"); |
| } |
| |
| void AssemblerMIPS32::c_olt_s(const Operand *OpFs, const Operand *OpFt) { |
| static constexpr IValueT Opcode = 0x44000034; |
| emitCOP1Fcmp(Opcode, SinglePrecision, OpFs, OpFt, OperandMIPS32FCC::FCC0, |
| "c.olt.s"); |
| } |
| |
| void AssemblerMIPS32::c_ueq_d(const Operand *OpFs, const Operand *OpFt) { |
| static constexpr IValueT Opcode = 0x44000033; |
| emitCOP1Fcmp(Opcode, DoublePrecision, OpFs, OpFt, OperandMIPS32FCC::FCC0, |
| "c.ueq.d"); |
| } |
| |
| void AssemblerMIPS32::c_ueq_s(const Operand *OpFs, const Operand *OpFt) { |
| static constexpr IValueT Opcode = 0x44000033; |
| emitCOP1Fcmp(Opcode, SinglePrecision, OpFs, OpFt, OperandMIPS32FCC::FCC0, |
| "c.ueq.s"); |
| } |
| |
| void AssemblerMIPS32::c_ule_d(const Operand *OpFs, const Operand *OpFt) { |
| static constexpr IValueT Opcode = 0x44000037; |
| emitCOP1Fcmp(Opcode, DoublePrecision, OpFs, OpFt, OperandMIPS32FCC::FCC0, |
| "c.ule.d"); |
| } |
| |
| void AssemblerMIPS32::c_ule_s(const Operand *OpFs, const Operand *OpFt) { |
| static constexpr IValueT Opcode = 0x44000037; |
| emitCOP1Fcmp(Opcode, SinglePrecision, OpFs, OpFt, OperandMIPS32FCC::FCC0, |
| "c.ule.s"); |
| } |
| |
| void AssemblerMIPS32::c_ult_d(const Operand *OpFs, const Operand *OpFt) { |
| static constexpr IValueT Opcode = 0x44000035; |
| emitCOP1Fcmp(Opcode, DoublePrecision, OpFs, OpFt, OperandMIPS32FCC::FCC0, |
| "c.ult.d"); |
| } |
| |
| void AssemblerMIPS32::c_ult_s(const Operand *OpFs, const Operand *OpFt) { |
| static constexpr IValueT Opcode = 0x44000035; |
| emitCOP1Fcmp(Opcode, SinglePrecision, OpFs, OpFt, OperandMIPS32FCC::FCC0, |
| "c.ult.s"); |
| } |
| |
| void AssemblerMIPS32::c_un_d(const Operand *OpFs, const Operand *OpFt) { |
| static constexpr IValueT Opcode = 0x44000031; |
| emitCOP1Fcmp(Opcode, DoublePrecision, OpFs, OpFt, OperandMIPS32FCC::FCC0, |
| "c.un.d"); |
| } |
| |
| void AssemblerMIPS32::c_un_s(const Operand *OpFs, const Operand *OpFt) { |
| static constexpr IValueT Opcode = 0x44000031; |
| emitCOP1Fcmp(Opcode, SinglePrecision, OpFs, OpFt, OperandMIPS32FCC::FCC0, |
| "c.un.s"); |
| } |
| |
| void AssemblerMIPS32::clz(const Operand *OpRd, const Operand *OpRs) { |
| IValueT Opcode = 0x70000020; |
| const IValueT Rd = encodeGPRegister(OpRd, "Rd", "clz"); |
| const IValueT Rs = encodeGPRegister(OpRs, "Rs", "clz"); |
| Opcode |= Rd << 11; |
| Opcode |= Rd << 16; |
| Opcode |= Rs << 21; |
| emitInst(Opcode); |
| } |
| |
| void AssemblerMIPS32::cvt_d_l(const Operand *OpFd, const Operand *OpFs) { |
| static constexpr IValueT Opcode = 0x44000021; |
| emitCOP1FmtFsFd(Opcode, Long, OpFd, OpFs, "cvt.d.l"); |
| } |
| |
| void AssemblerMIPS32::cvt_d_s(const Operand *OpFd, const Operand *OpFs) { |
| static constexpr IValueT Opcode = 0x44000021; |
| emitCOP1FmtFsFd(Opcode, SinglePrecision, OpFd, OpFs, "cvt.d.s"); |
| } |
| |
| void AssemblerMIPS32::cvt_d_w(const Operand *OpFd, const Operand *OpFs) { |
| static constexpr IValueT Opcode = 0x44000021; |
| emitCOP1FmtFsFd(Opcode, Word, OpFd, OpFs, "cvt.d.w"); |
| } |
| |
| void AssemblerMIPS32::cvt_s_d(const Operand *OpFd, const Operand *OpFs) { |
| static constexpr IValueT Opcode = 0x44000020; |
| emitCOP1FmtFsFd(Opcode, DoublePrecision, OpFd, OpFs, "cvt.s.d"); |
| } |
| |
| void AssemblerMIPS32::cvt_s_l(const Operand *OpFd, const Operand *OpFs) { |
| static constexpr IValueT Opcode = 0x44000020; |
| emitCOP1FmtFsFd(Opcode, Long, OpFd, OpFs, "cvt.s.l"); |
| } |
| |
| void AssemblerMIPS32::cvt_s_w(const Operand *OpFd, const Operand *OpFs) { |
| static constexpr IValueT Opcode = 0x44000020; |
| emitCOP1FmtFsFd(Opcode, Word, OpFd, OpFs, "cvt.s.w"); |
| } |
| |
| void AssemblerMIPS32::div(const Operand *OpRs, const Operand *OpRt) { |
| static constexpr IValueT Opcode = 0x0000001A; |
| emitRsRt(Opcode, OpRs, OpRt, "div"); |
| } |
| |
| void AssemblerMIPS32::div_d(const Operand *OpFd, const Operand *OpFs, |
| const Operand *OpFt) { |
| static constexpr IValueT Opcode = 0x44000003; |
| emitCOP1FmtFtFsFd(Opcode, DoublePrecision, OpFd, OpFs, OpFt, "div.d"); |
| } |
| |
| void AssemblerMIPS32::div_s(const Operand *OpFd, const Operand *OpFs, |
| const Operand *OpFt) { |
| static constexpr IValueT Opcode = 0x44000003; |
| emitCOP1FmtFtFsFd(Opcode, SinglePrecision, OpFd, OpFs, OpFt, "div.s"); |
| } |
| |
| void AssemblerMIPS32::divu(const Operand *OpRs, const Operand *OpRt) { |
| static constexpr IValueT Opcode = 0x0000001B; |
| emitRsRt(Opcode, OpRs, OpRt, "divu"); |
| } |
| |
| void AssemblerMIPS32::lui(const Operand *OpRt, const uint16_t Imm) { |
| IValueT Opcode = 0x3C000000; |
| const IValueT Rt = encodeGPRegister(OpRt, "Rt", "lui"); |
| Opcode |= Rt << 16; |
| Opcode |= Imm; |
| emitInst(Opcode); |
| } |
| |
| void AssemblerMIPS32::lw(const Operand *OpRt, const Operand *OpBase, |
| const uint32_t Offset) { |
| switch (OpRt->getType()) { |
| case IceType_i1: |
| case IceType_i8: { |
| static constexpr IValueT Opcode = 0x80000000; |
| emitRtRsImm16(Opcode, OpRt, OpBase, Offset, "lb"); |
| break; |
| } |
| case IceType_i16: { |
| static constexpr IValueT Opcode = 0x84000000; |
| emitRtRsImm16(Opcode, OpRt, OpBase, Offset, "lh"); |
| break; |
| } |
| case IceType_i32: { |
| static constexpr IValueT Opcode = 0x8C000000; |
| emitRtRsImm16(Opcode, OpRt, OpBase, Offset, "lw"); |
| break; |
| } |
| case IceType_f32: { |
| static constexpr IValueT Opcode = 0xC4000000; |
| emitFtRsImm16(Opcode, OpRt, OpBase, Offset, "lwc1"); |
| break; |
| } |
| case IceType_f64: { |
| static constexpr IValueT Opcode = 0xD4000000; |
| emitFtRsImm16(Opcode, OpRt, OpBase, Offset, "ldc1"); |
| break; |
| } |
| default: { UnimplementedError(getFlags()); } |
| } |
| } |
| |
| void AssemblerMIPS32::mfc1(const Operand *OpRt, const Operand *OpFs) { |
| static constexpr IValueT Opcode = 0x44000000; |
| emitCOP1MovRtFs(Opcode, OpRt, OpFs, "mfc1"); |
| } |
| |
| void AssemblerMIPS32::mfhi(const Operand *OpRd) { |
| IValueT Opcode = 0x000000010; |
| IValueT Rd = encodeGPRegister(OpRd, "Rd", "mfhi"); |
| Opcode |= Rd << 11; |
| emitInst(Opcode); |
| } |
| |
| void AssemblerMIPS32::mflo(const Operand *OpRd) { |
| IValueT Opcode = 0x000000012; |
| IValueT Rd = encodeGPRegister(OpRd, "Rd", "mflo"); |
| Opcode |= Rd << 11; |
| emitInst(Opcode); |
| } |
| |
| void AssemblerMIPS32::mov_d(const Operand *OpFd, const Operand *OpFs) { |
| static constexpr IValueT Opcode = 0x44000006; |
| emitCOP1FmtFsFd(Opcode, DoublePrecision, OpFd, OpFs, "mov.d"); |
| } |
| |
| void AssemblerMIPS32::mov_s(const Operand *OpFd, const Operand *OpFs) { |
| static constexpr IValueT Opcode = 0x44000006; |
| emitCOP1FmtFsFd(Opcode, SinglePrecision, OpFd, OpFs, "mov.s"); |
| } |
| |
| void AssemblerMIPS32::move(const Operand *OpRd, const Operand *OpRs) { |
| |
| const Type DstType = OpRd->getType(); |
| const Type SrcType = OpRs->getType(); |
| |
| if ((isScalarIntegerType(DstType) && isScalarFloatingType(SrcType)) || |
| (isScalarFloatingType(DstType) && isScalarIntegerType(SrcType))) { |
| if (isScalarFloatingType(DstType)) { |
| mtc1(OpRd, OpRs); |
| } else { |
| mfc1(OpRd, OpRs); |
| } |
| } else { |
| switch (DstType) { |
| case IceType_f32: |
| mov_s(OpRd, OpRs); |
| break; |
| case IceType_f64: |
| mov_d(OpRd, OpRs); |
| break; |
| case IceType_i1: |
| case IceType_i8: |
| case IceType_i16: |
| case IceType_i32: { |
| IValueT Opcode = 0x00000021; |
| const IValueT Rd = encodeGPRegister(OpRd, "Rd", "pseudo-move"); |
| const IValueT Rs = encodeGPRegister(OpRs, "Rs", "pseudo-move"); |
| const IValueT Rt = 0; // $0 |
| Opcode |= Rs << 21; |
| Opcode |= Rt << 16; |
| Opcode |= Rd << 11; |
| emitInst(Opcode); |
| break; |
| } |
| default: { UnimplementedError(getFlags()); } |
| } |
| } |
| } |
| |
| void AssemblerMIPS32::movf(const Operand *OpRd, const Operand *OpRs, |
| const Operand *OpCc) { |
| IValueT Opcode = 0x00000001; |
| const IValueT Rd = encodeGPRegister(OpRd, "Rd", "movf"); |
| const IValueT Rs = encodeGPRegister(OpRs, "Rs", "movf"); |
| OperandMIPS32FCC::FCC Cc = OperandMIPS32FCC::FCC0; |
| if (const auto *OpFCC = llvm::dyn_cast<OperandMIPS32FCC>(OpCc)) { |
| Cc = OpFCC->getFCC(); |
| } |
| const IValueT InstEncodingFalse = 0; |
| Opcode |= Rd << 11; |
| Opcode |= InstEncodingFalse << 16; |
| Opcode |= Cc << 18; |
| Opcode |= Rs << 21; |
| emitInst(Opcode); |
| } |
| |
| void AssemblerMIPS32::movn(const Operand *OpRd, const Operand *OpRs, |
| const Operand *OpRt) { |
| static constexpr IValueT Opcode = 0x0000000B; |
| emitRdRsRt(Opcode, OpRd, OpRs, OpRt, "movn"); |
| } |
| |
| void AssemblerMIPS32::movn_d(const Operand *OpFd, const Operand *OpFs, |
| const Operand *OpFt) { |
| static constexpr IValueT Opcode = 0x44000013; |
| emitCOP1FmtFtFsFd(Opcode, SinglePrecision, OpFd, OpFs, OpFt, "movn.d"); |
| } |
| |
| void AssemblerMIPS32::movn_s(const Operand *OpFd, const Operand *OpFs, |
| const Operand *OpFt) { |
| static constexpr IValueT Opcode = 0x44000013; |
| emitCOP1FmtFtFsFd(Opcode, SinglePrecision, OpFd, OpFs, OpFt, "movn.s"); |
| } |
| |
| void AssemblerMIPS32::movt(const Operand *OpRd, const Operand *OpRs, |
| const Operand *OpCc) { |
| IValueT Opcode = 0x00000001; |
| const IValueT Rd = encodeGPRegister(OpRd, "Rd", "movt"); |
| const IValueT Rs = encodeGPRegister(OpRs, "Rs", "movt"); |
| OperandMIPS32FCC::FCC Cc = OperandMIPS32FCC::FCC0; |
| if (const auto *OpFCC = llvm::dyn_cast<OperandMIPS32FCC>(OpCc)) { |
| Cc = OpFCC->getFCC(); |
| } |
| const IValueT InstEncodingTrue = 1; |
| Opcode |= Rd << 11; |
| Opcode |= InstEncodingTrue << 16; |
| Opcode |= Cc << 18; |
| Opcode |= Rs << 21; |
| emitInst(Opcode); |
| } |
| |
| void AssemblerMIPS32::movz_d(const Operand *OpFd, const Operand *OpFs, |
| const Operand *OpFt) { |
| static constexpr IValueT Opcode = 0x44000012; |
| emitCOP1FmtFtFsFd(Opcode, SinglePrecision, OpFd, OpFs, OpFt, "movz.d"); |
| } |
| |
| void AssemblerMIPS32::movz(const Operand *OpRd, const Operand *OpRs, |
| const Operand *OpRt) { |
| static constexpr IValueT Opcode = 0x0000000A; |
| emitRdRsRt(Opcode, OpRd, OpRs, OpRt, "movz"); |
| } |
| |
| void AssemblerMIPS32::movz_s(const Operand *OpFd, const Operand *OpFs, |
| const Operand *OpFt) { |
| static constexpr IValueT Opcode = 0x44000012; |
| emitCOP1FmtFtFsFd(Opcode, SinglePrecision, OpFd, OpFs, OpFt, "movz.s"); |
| } |
| |
| void AssemblerMIPS32::mtc1(const Operand *OpRt, const Operand *OpFs) { |
| static constexpr IValueT Opcode = 0x44800000; |
| emitCOP1MovRtFs(Opcode, OpRt, OpFs, "mtc1"); |
| } |
| |
| void AssemblerMIPS32::mthi(const Operand *OpRs) { |
| IValueT Opcode = 0x000000011; |
| IValueT Rs = encodeGPRegister(OpRs, "Rs", "mthi"); |
| Opcode |= Rs << 21; |
| emitInst(Opcode); |
| } |
| |
| void AssemblerMIPS32::mtlo(const Operand *OpRs) { |
| IValueT Opcode = 0x000000013; |
| IValueT Rs = encodeGPRegister(OpRs, "Rs", "mtlo"); |
| Opcode |= Rs << 21; |
| emitInst(Opcode); |
| } |
| |
| void AssemblerMIPS32::mul(const Operand *OpRd, const Operand *OpRs, |
| const Operand *OpRt) { |
| static constexpr IValueT Opcode = 0x70000002; |
| emitRdRsRt(Opcode, OpRd, OpRs, OpRt, "mul"); |
| } |
| |
| void AssemblerMIPS32::mul_d(const Operand *OpFd, const Operand *OpFs, |
| const Operand *OpFt) { |
| static constexpr IValueT Opcode = 0x44000002; |
| emitCOP1FmtFtFsFd(Opcode, DoublePrecision, OpFd, OpFs, OpFt, "mul.d"); |
| } |
| |
| void AssemblerMIPS32::mul_s(const Operand *OpFd, const Operand *OpFs, |
| const Operand *OpFt) { |
| static constexpr IValueT Opcode = 0x44000002; |
| emitCOP1FmtFtFsFd(Opcode, SinglePrecision, OpFd, OpFs, OpFt, "mul.s"); |
| } |
| |
| void AssemblerMIPS32::multu(const Operand *OpRs, const Operand *OpRt) { |
| static constexpr IValueT Opcode = 0x00000019; |
| emitRsRt(Opcode, OpRs, OpRt, "multu"); |
| } |
| |
| void AssemblerMIPS32::nor(const Operand *OpRd, const Operand *OpRs, |
| const Operand *OpRt) { |
| static constexpr IValueT Opcode = 0x00000027; |
| emitRdRsRt(Opcode, OpRd, OpRs, OpRt, "nor"); |
| } |
| |
| void AssemblerMIPS32::or_(const Operand *OpRd, const Operand *OpRs, |
| const Operand *OpRt) { |
| static constexpr IValueT Opcode = 0x00000025; |
| emitRdRsRt(Opcode, OpRd, OpRs, OpRt, "or"); |
| } |
| |
| void AssemblerMIPS32::ori(const Operand *OpRt, const Operand *OpRs, |
| const uint32_t Imm) { |
| static constexpr IValueT Opcode = 0x34000000; |
| emitRtRsImm16(Opcode, OpRt, OpRs, Imm, "ori"); |
| } |
| |
| void AssemblerMIPS32::ret(void) { |
| static constexpr IValueT Opcode = 0x03E00008; // JR $31 |
| emitInst(Opcode); |
| nop(); // delay slot |
| } |
| |
| void AssemblerMIPS32::sll(const Operand *OpRd, const Operand *OpRt, |
| const uint32_t Sa) { |
| static constexpr IValueT Opcode = 0x00000000; |
| emitRdRtSa(Opcode, OpRd, OpRt, Sa, "sll"); |
| } |
| |
| void AssemblerMIPS32::sllv(const Operand *OpRd, const Operand *OpRt, |
| const Operand *OpRs) { |
| static constexpr IValueT Opcode = 0x00000004; |
| emitRdRsRt(Opcode, OpRd, OpRs, OpRt, "sllv"); |
| } |
| |
| void AssemblerMIPS32::slt(const Operand *OpRd, const Operand *OpRs, |
| const Operand *OpRt) { |
| static constexpr IValueT Opcode = 0x0000002A; |
| emitRdRsRt(Opcode, OpRd, OpRs, OpRt, "slt"); |
| } |
| |
| void AssemblerMIPS32::slti(const Operand *OpRt, const Operand *OpRs, |
| const uint32_t Imm) { |
| static constexpr IValueT Opcode = 0x28000000; |
| emitRtRsImm16(Opcode, OpRt, OpRs, Imm, "slti"); |
| } |
| |
| void AssemblerMIPS32::sltu(const Operand *OpRd, const Operand *OpRs, |
| const Operand *OpRt) { |
| static constexpr IValueT Opcode = 0x0000002B; |
| emitRdRsRt(Opcode, OpRd, OpRs, OpRt, "sltu"); |
| } |
| |
| void AssemblerMIPS32::sltiu(const Operand *OpRt, const Operand *OpRs, |
| const uint32_t Imm) { |
| static constexpr IValueT Opcode = 0x2c000000; |
| emitRtRsImm16(Opcode, OpRt, OpRs, Imm, "sltiu"); |
| } |
| |
| void AssemblerMIPS32::sqrt_d(const Operand *OpFd, const Operand *OpFs) { |
| static constexpr IValueT Opcode = 0x44000004; |
| emitCOP1FmtFsFd(Opcode, DoublePrecision, OpFd, OpFs, "sqrt.d"); |
| } |
| |
| void AssemblerMIPS32::sqrt_s(const Operand *OpFd, const Operand *OpFs) { |
| static constexpr IValueT Opcode = 0x44000004; |
| emitCOP1FmtFsFd(Opcode, SinglePrecision, OpFd, OpFs, "sqrt.s"); |
| } |
| |
| void AssemblerMIPS32::sra(const Operand *OpRd, const Operand *OpRt, |
| const uint32_t Sa) { |
| static constexpr IValueT Opcode = 0x00000003; |
| emitRdRtSa(Opcode, OpRd, OpRt, Sa, "sra"); |
| } |
| |
| void AssemblerMIPS32::srl(const Operand *OpRd, const Operand *OpRt, |
| const uint32_t Sa) { |
| static constexpr IValueT Opcode = 0x00000002; |
| emitRdRtSa(Opcode, OpRd, OpRt, Sa, "srl"); |
| } |
| |
| void AssemblerMIPS32::srav(const Operand *OpRd, const Operand *OpRt, |
| const Operand *OpRs) { |
| static constexpr IValueT Opcode = 0x00000007; |
| emitRdRsRt(Opcode, OpRd, OpRs, OpRt, "srav"); |
| } |
| |
| void AssemblerMIPS32::srlv(const Operand *OpRd, const Operand *OpRt, |
| const Operand *OpRs) { |
| static constexpr IValueT Opcode = 0x00000006; |
| emitRdRsRt(Opcode, OpRd, OpRs, OpRt, "srlv"); |
| } |
| |
| void AssemblerMIPS32::sub_d(const Operand *OpFd, const Operand *OpFs, |
| const Operand *OpFt) { |
| static constexpr IValueT Opcode = 0x44000001; |
| emitCOP1FmtFtFsFd(Opcode, DoublePrecision, OpFd, OpFs, OpFt, "sub.d"); |
| } |
| |
| void AssemblerMIPS32::sub_s(const Operand *OpFd, const Operand *OpFs, |
| const Operand *OpFt) { |
| static constexpr IValueT Opcode = 0x44000001; |
| emitCOP1FmtFtFsFd(Opcode, SinglePrecision, OpFd, OpFs, OpFt, "sub.s"); |
| } |
| |
| void AssemblerMIPS32::subu(const Operand *OpRd, const Operand *OpRs, |
| const Operand *OpRt) { |
| static constexpr IValueT Opcode = 0x00000023; |
| emitRdRsRt(Opcode, OpRd, OpRs, OpRt, "subu"); |
| } |
| |
| void AssemblerMIPS32::sw(const Operand *OpRt, const Operand *OpBase, |
| const uint32_t Offset) { |
| switch (OpRt->getType()) { |
| case IceType_i1: |
| case IceType_i8: { |
| static constexpr IValueT Opcode = 0xA0000000; |
| emitRtRsImm16(Opcode, OpRt, OpBase, Offset, "sb"); |
| break; |
| } |
| case IceType_i16: { |
| static constexpr IValueT Opcode = 0xA4000000; |
| emitRtRsImm16(Opcode, OpRt, OpBase, Offset, "sh"); |
| break; |
| } |
| case IceType_i32: { |
| static constexpr IValueT Opcode = 0xAC000000; |
| emitRtRsImm16(Opcode, OpRt, OpBase, Offset, "sw"); |
| break; |
| } |
| case IceType_f32: { |
| static constexpr IValueT Opcode = 0xE4000000; |
| emitFtRsImm16(Opcode, OpRt, OpBase, Offset, "swc1"); |
| break; |
| } |
| case IceType_f64: { |
| static constexpr IValueT Opcode = 0xF4000000; |
| emitFtRsImm16(Opcode, OpRt, OpBase, Offset, "sdc1"); |
| break; |
| } |
| default: { UnimplementedError(getFlags()); } |
| } |
| } |
| |
| void AssemblerMIPS32::teq(const Operand *OpRs, const Operand *OpRt, |
| const uint32_t TrapCode) { |
| IValueT Opcode = 0x00000034; |
| const IValueT Rs = encodeGPRegister(OpRs, "Rs", "teq"); |
| const IValueT Rt = encodeGPRegister(OpRt, "Rt", "teq"); |
| Opcode |= (TrapCode & 0xFFFFF) << 6; |
| Opcode |= Rt << 16; |
| Opcode |= Rs << 21; |
| emitInst(Opcode); |
| } |
| |
| void AssemblerMIPS32::trunc_l_d(const Operand *OpFd, const Operand *OpFs) { |
| static constexpr IValueT Opcode = 0x4400000D; |
| emitCOP1FmtFsFd(Opcode, Long, OpFd, OpFs, "trunc.l.d"); |
| } |
| |
| void AssemblerMIPS32::trunc_l_s(const Operand *OpFd, const Operand *OpFs) { |
| static constexpr IValueT Opcode = 0x4400000D; |
| emitCOP1FmtFsFd(Opcode, Long, OpFd, OpFs, "trunc.l.s"); |
| } |
| |
| void AssemblerMIPS32::trunc_w_d(const Operand *OpFd, const Operand *OpFs) { |
| static constexpr IValueT Opcode = 0x4400000D; |
| emitCOP1FmtFsFd(Opcode, Word, OpFd, OpFs, "trunc.w.d"); |
| } |
| |
| void AssemblerMIPS32::trunc_w_s(const Operand *OpFd, const Operand *OpFs) { |
| static constexpr IValueT Opcode = 0x4400000D; |
| emitCOP1FmtFsFd(Opcode, Word, OpFd, OpFs, "trunc.w.s"); |
| } |
| |
| void AssemblerMIPS32::xor_(const Operand *OpRd, const Operand *OpRs, |
| const Operand *OpRt) { |
| static constexpr IValueT Opcode = 0x00000026; |
| emitRdRsRt(Opcode, OpRd, OpRs, OpRt, "xor"); |
| } |
| |
| void AssemblerMIPS32::xori(const Operand *OpRt, const Operand *OpRs, |
| const uint32_t Imm) { |
| static constexpr IValueT Opcode = 0x38000000; |
| emitRtRsImm16(Opcode, OpRt, OpRs, Imm, "xori"); |
| } |
| |
| void AssemblerMIPS32::emitBr(const CondMIPS32::Cond Cond, const Operand *OpRs, |
| const Operand *OpRt, IOffsetT Offset) { |
| IValueT Opcode = 0; |
| |
| switch (Cond) { |
| default: |
| break; |
| case CondMIPS32::AL: |
| case CondMIPS32::EQ: |
| case CondMIPS32::EQZ: |
| Opcode = 0x10000000; |
| break; |
| case CondMIPS32::NE: |
| case CondMIPS32::NEZ: |
| Opcode = 0x14000000; |
| break; |
| case CondMIPS32::LEZ: |
| Opcode = 0x18000000; |
| break; |
| case CondMIPS32::LTZ: |
| Opcode = 0x04000000; |
| break; |
| case CondMIPS32::GEZ: |
| Opcode = 0x04010000; |
| break; |
| case CondMIPS32::GTZ: |
| Opcode = 0x1C000000; |
| break; |
| } |
| |
| if (Opcode == 0) { |
| llvm::report_fatal_error("Branch: Invalid condition"); |
| } |
| |
| if (OpRs != nullptr) { |
| IValueT Rs = encodeGPRegister(OpRs, "Rs", "branch"); |
| Opcode |= Rs << 21; |
| } |
| |
| if (OpRt != nullptr) { |
| IValueT Rt = encodeGPRegister(OpRt, "Rt", "branch"); |
| Opcode |= Rt << 16; |
| } |
| |
| Opcode = encodeBranchOffset(Offset, Opcode); |
| emitInst(Opcode); |
| nop(); // delay slot |
| } |
| |
| void AssemblerMIPS32::bcc(const CondMIPS32::Cond Cond, const Operand *OpRs, |
| const Operand *OpRt, Label *TargetLabel) { |
| if (TargetLabel->isBound()) { |
| const int32_t Dest = TargetLabel->getPosition() - Buffer.size(); |
| emitBr(Cond, OpRs, OpRt, Dest); |
| return; |
| } |
| const IOffsetT Position = Buffer.size(); |
| emitBr(Cond, OpRs, OpRt, TargetLabel->getEncodedPosition()); |
| TargetLabel->linkTo(*this, Position); |
| } |
| |
| void AssemblerMIPS32::bzc(const CondMIPS32::Cond Cond, const Operand *OpRs, |
| Label *TargetLabel) { |
| static constexpr Operand *OpRtNone = nullptr; |
| if (TargetLabel->isBound()) { |
| const int32_t Dest = TargetLabel->getPosition() - Buffer.size(); |
| emitBr(Cond, OpRs, OpRtNone, Dest); |
| return; |
| } |
| const IOffsetT Position = Buffer.size(); |
| emitBr(Cond, OpRs, OpRtNone, TargetLabel->getEncodedPosition()); |
| TargetLabel->linkTo(*this, Position); |
| } |
| |
| } // end of namespace MIPS32 |
| } // end of namespace Ice |