1279 lines
41 KiB
C++
1279 lines
41 KiB
C++
|
|
//===- 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
|