//===- 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)); IOffsetT NextBrPc = decodeBranchOffset(Inst); if (NextBrPc != 0) NextBrPc = Position - NextBrPc; L->setPosition(NextBrPc); } 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::emitRtRsImm16Rel(IValueT Opcode, const Operand *OpRt, const Operand *OpRs, const Operand *OpImm, const RelocOp Reloc, const char *InsnName) { const IValueT Rt = encodeGPRegister(OpRt, "Rt", InsnName); const IValueT Rs = encodeGPRegister(OpRs, "Rs", InsnName); uint32_t Imm16 = 0; if (const auto *OpRel = llvm::dyn_cast<ConstantRelocatable>(OpImm)) { emitFixup(createMIPS32Fixup(Reloc, OpRel)); } else if (auto *C32 = llvm::dyn_cast<ConstantInteger32>(OpImm)) { Imm16 = C32->getValue(); } else { llvm::report_fatal_error(std::string(InsnName) + ": Invalid 3rd operand"); } Opcode |= Rs << 21; Opcode |= Rt << 16; Opcode |= Imm16 & 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::addi(const Operand *OpRt, const Operand *OpRs, const uint32_t Imm) { static constexpr IValueT Opcode = 0x20000000; emitRtRsImm16(Opcode, OpRt, OpRs, Imm, "addi"); } 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::addiu(const Operand *OpRt, const Operand *OpRs, const Operand *OpImm, const RelocOp Reloc) { static constexpr IValueT Opcode = 0x24000000; emitRtRsImm16Rel(Opcode, OpRt, OpRs, OpImm, Reloc, "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(); IOffsetT PrevPosition = TargetLabel->getEncodedPosition(); if (PrevPosition != 0) PrevPosition = Position - PrevPosition; emitBr(CondMIPS32::AL, OpRsNone, OpRtNone, PrevPosition); 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"); } MIPS32Fixup *AssemblerMIPS32::createMIPS32Fixup(const RelocOp Reloc, const Constant *RelOp) { MIPS32Fixup *Fixup = new (allocate<MIPS32Fixup>()) MIPS32Fixup(); switch (Reloc) { case RelocOp::RO_Hi: Fixup->set_kind(llvm::ELF::R_MIPS_HI16); break; case RelocOp::RO_Lo: Fixup->set_kind(llvm::ELF::R_MIPS_LO16); break; case RelocOp::RO_Jal: Fixup->set_kind(llvm::ELF::R_MIPS_26); break; default: llvm::report_fatal_error("Fixup: Invalid Reloc type"); break; } Fixup->set_value(RelOp); Buffer.installFixup(Fixup); return Fixup; } size_t MIPS32Fixup::emit(GlobalContext *Ctx, const Assembler &Asm) const { if (!BuildDefs::dump()) return InstMIPS32::InstSize; Ostream &Str = Ctx->getStrEmit(); IValueT Inst = Asm.load<IValueT>(position()); const auto Symbol = symbol().toString(); Str << "\t" << ".word " << llvm::format_hex(Inst, 8) << " # "; switch (kind()) { case llvm::ELF::R_MIPS_HI16: Str << "R_MIPS_HI16 "; break; case llvm::ELF::R_MIPS_LO16: Str << "R_MIPS_LO16 "; break; case llvm::ELF::R_MIPS_26: Str << "R_MIPS_26 "; break; default: Str << "Unknown "; break; } Str << Symbol << "\n"; return InstMIPS32::InstSize; } void MIPS32Fixup::emitOffset(Assembler *Asm) const { const IValueT Inst = Asm->load<IValueT>(position()); IValueT ImmMask = 0; const IValueT Imm = offset(); if (kind() == llvm::ELF::R_MIPS_26) { ImmMask = 0x03FFFFFF; } else { ImmMask = 0x0000FFFF; } Asm->store(position(), (Inst & ~ImmMask) | (Imm & ImmMask)); } void AssemblerMIPS32::jal(const ConstantRelocatable *Target) { IValueT Opcode = 0x0C000000; emitFixup(createMIPS32Fixup(RelocOp::RO_Jal, Target)); emitInst(Opcode); nop(); } void AssemblerMIPS32::jalr(const Operand *OpRs, const Operand *OpRd) { IValueT Opcode = 0x00000009; const IValueT Rs = encodeGPRegister(OpRs, "Rs", "jalr"); const IValueT Rd = (OpRd == nullptr) ? 31 : encodeGPRegister(OpRd, "Rd", "jalr"); Opcode |= Rd << 11; Opcode |= Rs << 21; emitInst(Opcode); nop(); } void AssemblerMIPS32::lui(const Operand *OpRt, const Operand *OpImm, const RelocOp Reloc) { IValueT Opcode = 0x3C000000; const IValueT Rt = encodeGPRegister(OpRt, "Rt", "lui"); IValueT Imm16 = 0; if (const auto *OpRel = llvm::dyn_cast<ConstantRelocatable>(OpImm)) { emitFixup(createMIPS32Fixup(Reloc, OpRel)); } else if (auto *C32 = llvm::dyn_cast<ConstantInteger32>(OpImm)) { Imm16 = C32->getValue(); } else { llvm::report_fatal_error("lui: Invalid 2nd operand"); } Opcode |= Rt << 16; Opcode |= Imm16; emitInst(Opcode); } void AssemblerMIPS32::ldc1(const Operand *OpRt, const Operand *OpBase, const Operand *OpOff, const RelocOp Reloc) { IValueT Opcode = 0xD4000000; const IValueT Rt = encodeFPRegister(OpRt, "Ft", "ldc1"); const IValueT Base = encodeGPRegister(OpBase, "Base", "ldc1"); IValueT Imm16 = 0; if (const auto *OpRel = llvm::dyn_cast<ConstantRelocatable>(OpOff)) { emitFixup(createMIPS32Fixup(Reloc, OpRel)); } else if (auto *C32 = llvm::dyn_cast<ConstantInteger32>(OpOff)) { Imm16 = C32->getValue(); } else { llvm::report_fatal_error("ldc1: Invalid 2nd operand"); } Opcode |= Base << 21; Opcode |= Rt << 16; Opcode |= Imm16; emitInst(Opcode); } void AssemblerMIPS32::ll(const Operand *OpRt, const Operand *OpBase, const uint32_t Offset) { static constexpr IValueT Opcode = 0xC0000000; emitRtRsImm16(Opcode, OpRt, OpBase, Offset, "ll"); } 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::lwc1(const Operand *OpRt, const Operand *OpBase, const Operand *OpOff, const RelocOp Reloc) { IValueT Opcode = 0xC4000000; const IValueT Rt = encodeFPRegister(OpRt, "Ft", "lwc1"); const IValueT Base = encodeGPRegister(OpBase, "Base", "lwc1"); IValueT Imm16 = 0; if (const auto *OpRel = llvm::dyn_cast<ConstantRelocatable>(OpOff)) { emitFixup(createMIPS32Fixup(Reloc, OpRel)); } else if (auto *C32 = llvm::dyn_cast<ConstantInteger32>(OpOff)) { Imm16 = C32->getValue(); } else { llvm::report_fatal_error("lwc1: Invalid 2nd operand"); } Opcode |= Base << 21; Opcode |= Rt << 16; Opcode |= Imm16; emitInst(Opcode); } 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(OpRs, OpRd); } 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; emitCOP1FmtRtFsFd(Opcode, DoublePrecision, OpFd, OpFs, OpFt, "movn.d"); } void AssemblerMIPS32::movn_s(const Operand *OpFd, const Operand *OpFs, const Operand *OpFt) { static constexpr IValueT Opcode = 0x44000013; emitCOP1FmtRtFsFd(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, DoublePrecision, 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::mult(const Operand *OpRs, const Operand *OpRt) { static constexpr IValueT Opcode = 0x00000018; emitRsRt(Opcode, OpRs, OpRt, "mult"); } 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::sc(const Operand *OpRt, const Operand *OpBase, const uint32_t Offset) { static constexpr IValueT Opcode = 0xE0000000; emitRtRsImm16(Opcode, OpRt, OpBase, Offset, "sc"); } 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::sdc1(const Operand *OpRt, const Operand *OpBase, const Operand *OpOff, const RelocOp Reloc) { IValueT Opcode = 0xF4000000; const IValueT Rt = encodeFPRegister(OpRt, "Ft", "sdc1"); const IValueT Base = encodeGPRegister(OpBase, "Base", "sdc1"); IValueT Imm16 = 0; if (const auto *OpRel = llvm::dyn_cast<ConstantRelocatable>(OpOff)) { emitFixup(createMIPS32Fixup(Reloc, OpRel)); } else if (auto *C32 = llvm::dyn_cast<ConstantInteger32>(OpOff)) { Imm16 = C32->getValue(); } else { llvm::report_fatal_error("sdc1: Invalid 2nd operand"); } Opcode |= Base << 21; Opcode |= Rt << 16; Opcode |= Imm16; emitInst(Opcode); } 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::swc1(const Operand *OpRt, const Operand *OpBase, const Operand *OpOff, const RelocOp Reloc) { IValueT Opcode = 0xE4000000; const IValueT Rt = encodeFPRegister(OpRt, "Ft", "swc1"); const IValueT Base = encodeGPRegister(OpBase, "Base", "swc1"); IValueT Imm16 = 0; if (const auto *OpRel = llvm::dyn_cast<ConstantRelocatable>(OpOff)) { emitFixup(createMIPS32Fixup(Reloc, OpRel)); } else if (auto *C32 = llvm::dyn_cast<ConstantInteger32>(OpOff)) { Imm16 = C32->getValue(); } else { llvm::report_fatal_error("swc1: Invalid 2nd operand"); } Opcode |= Base << 21; Opcode |= Rt << 16; Opcode |= Imm16; emitInst(Opcode); } void AssemblerMIPS32::sync() { static constexpr IValueT Opcode = 0x0000000f; emitInst(Opcode); } 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, DoublePrecision, OpFd, OpFs, "trunc.w.d"); } void AssemblerMIPS32::trunc_w_s(const Operand *OpFd, const Operand *OpFs) { static constexpr IValueT Opcode = 0x4400000D; emitCOP1FmtFsFd(Opcode, SinglePrecision, 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(); IOffsetT PrevPosition = TargetLabel->getEncodedPosition(); if (PrevPosition != 0) PrevPosition = Position - PrevPosition; emitBr(Cond, OpRs, OpRt, PrevPosition); 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(); IOffsetT PrevPosition = TargetLabel->getEncodedPosition(); if (PrevPosition) PrevPosition = Position - PrevPosition; emitBr(Cond, OpRs, OpRtNone, PrevPosition); TargetLabel->linkTo(*this, Position); } } // end of namespace MIPS32 } // end of namespace Ice