blob: 9d6e8dc573a8d1ed270aa56e556c6e33d6fe92b0 [file] [log] [blame]
//===-- MipsAsmParser.cpp - Parse Mips assembly to MCInst instructions ----===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "MCTargetDesc/MipsABIFlagsSection.h"
#include "MCTargetDesc/MipsABIInfo.h"
#include "MCTargetDesc/MipsBaseInfo.h"
#include "MCTargetDesc/MipsMCExpr.h"
#include "MCTargetDesc/MipsMCTargetDesc.h"
#include "MipsTargetStreamer.h"
#include "TargetInfo/MipsTargetInfo.h"
#include "llvm/ADT/APFloat.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/StringSwitch.h"
#include "llvm/ADT/Twine.h"
#include "llvm/BinaryFormat/ELF.h"
#include "llvm/MC/MCContext.h"
#include "llvm/MC/MCExpr.h"
#include "llvm/MC/MCInst.h"
#include "llvm/MC/MCInstrDesc.h"
#include "llvm/MC/MCInstrInfo.h"
#include "llvm/MC/MCObjectFileInfo.h"
#include "llvm/MC/MCParser/MCAsmLexer.h"
#include "llvm/MC/MCParser/MCAsmParser.h"
#include "llvm/MC/MCParser/MCAsmParserExtension.h"
#include "llvm/MC/MCParser/MCAsmParserUtils.h"
#include "llvm/MC/MCParser/MCParsedAsmOperand.h"
#include "llvm/MC/MCParser/MCTargetAsmParser.h"
#include "llvm/MC/MCSectionELF.h"
#include "llvm/MC/MCStreamer.h"
#include "llvm/MC/MCSubtargetInfo.h"
#include "llvm/MC/MCSymbol.h"
#include "llvm/MC/MCSymbolELF.h"
#include "llvm/MC/MCValue.h"
#include "llvm/MC/TargetRegistry.h"
#include "llvm/Support/Alignment.h"
#include "llvm/Support/Casting.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Compiler.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/MathExtras.h"
#include "llvm/Support/SMLoc.h"
#include "llvm/Support/SourceMgr.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/TargetParser/SubtargetFeature.h"
#include "llvm/TargetParser/Triple.h"
#include <algorithm>
#include <cassert>
#include <cstdint>
#include <memory>
#include <string>
#include <utility>
using namespace llvm;
#define DEBUG_TYPE "mips-asm-parser"
namespace llvm {
class MCInstrInfo;
} // end namespace llvm
extern cl::opt<bool> EmitJalrReloc;
namespace {
class MipsAssemblerOptions {
public:
MipsAssemblerOptions(const FeatureBitset &Features_) : Features(Features_) {}
MipsAssemblerOptions(const MipsAssemblerOptions *Opts) {
ATReg = Opts->getATRegIndex();
Reorder = Opts->isReorder();
Macro = Opts->isMacro();
Features = Opts->getFeatures();
}
unsigned getATRegIndex() const { return ATReg; }
bool setATRegIndex(unsigned Reg) {
if (Reg > 31)
return false;
ATReg = Reg;
return true;
}
bool isReorder() const { return Reorder; }
void setReorder() { Reorder = true; }
void setNoReorder() { Reorder = false; }
bool isMacro() const { return Macro; }
void setMacro() { Macro = true; }
void setNoMacro() { Macro = false; }
const FeatureBitset &getFeatures() const { return Features; }
void setFeatures(const FeatureBitset &Features_) { Features = Features_; }
// Set of features that are either architecture features or referenced
// by them (e.g.: FeatureNaN2008 implied by FeatureMips32r6).
// The full table can be found in MipsGenSubtargetInfo.inc (MipsFeatureKV[]).
// The reason we need this mask is explained in the selectArch function.
// FIXME: Ideally we would like TableGen to generate this information.
static const FeatureBitset AllArchRelatedMask;
private:
unsigned ATReg = 1;
bool Reorder = true;
bool Macro = true;
FeatureBitset Features;
};
} // end anonymous namespace
const FeatureBitset MipsAssemblerOptions::AllArchRelatedMask = {
Mips::FeatureMips1, Mips::FeatureMips2, Mips::FeatureMips3,
Mips::FeatureMips3_32, Mips::FeatureMips3_32r2, Mips::FeatureMips4,
Mips::FeatureMips4_32, Mips::FeatureMips4_32r2, Mips::FeatureMips5,
Mips::FeatureMips5_32r2, Mips::FeatureMips32, Mips::FeatureMips32r2,
Mips::FeatureMips32r3, Mips::FeatureMips32r5, Mips::FeatureMips32r6,
Mips::FeatureMips64, Mips::FeatureMips64r2, Mips::FeatureMips64r3,
Mips::FeatureMips64r5, Mips::FeatureMips64r6, Mips::FeatureCnMips,
Mips::FeatureCnMipsP, Mips::FeatureFP64Bit, Mips::FeatureGP64Bit,
Mips::FeatureNaN2008
};
namespace {
class MipsAsmParser : public MCTargetAsmParser {
MipsTargetStreamer &getTargetStreamer() {
assert(getParser().getStreamer().getTargetStreamer() &&
"do not have a target streamer");
MCTargetStreamer &TS = *getParser().getStreamer().getTargetStreamer();
return static_cast<MipsTargetStreamer &>(TS);
}
MipsABIInfo ABI;
SmallVector<std::unique_ptr<MipsAssemblerOptions>, 2> AssemblerOptions;
MCSymbol *CurrentFn; // Pointer to the function being parsed. It may be a
// nullptr, which indicates that no function is currently
// selected. This usually happens after an '.end func'
// directive.
bool IsLittleEndian;
bool IsPicEnabled;
bool IsCpRestoreSet;
bool CurForbiddenSlotAttr;
int CpRestoreOffset;
unsigned GPReg;
unsigned CpSaveLocation;
/// If true, then CpSaveLocation is a register, otherwise it's an offset.
bool CpSaveLocationIsRegister;
// Map of register aliases created via the .set directive.
StringMap<AsmToken> RegisterSets;
// Print a warning along with its fix-it message at the given range.
void printWarningWithFixIt(const Twine &Msg, const Twine &FixMsg,
SMRange Range, bool ShowColors = true);
void ConvertXWPOperands(MCInst &Inst, const OperandVector &Operands);
#define GET_ASSEMBLER_HEADER
#include "MipsGenAsmMatcher.inc"
unsigned
checkEarlyTargetMatchPredicate(MCInst &Inst,
const OperandVector &Operands) override;
unsigned checkTargetMatchPredicate(MCInst &Inst) override;
bool MatchAndEmitInstruction(SMLoc IDLoc, unsigned &Opcode,
OperandVector &Operands, MCStreamer &Out,
uint64_t &ErrorInfo,
bool MatchingInlineAsm) override;
/// Parse a register as used in CFI directives
bool parseRegister(MCRegister &Reg, SMLoc &StartLoc, SMLoc &EndLoc) override;
ParseStatus tryParseRegister(MCRegister &Reg, SMLoc &StartLoc,
SMLoc &EndLoc) override;
bool parseParenSuffix(StringRef Name, OperandVector &Operands);
bool parseBracketSuffix(StringRef Name, OperandVector &Operands);
bool mnemonicIsValid(StringRef Mnemonic, unsigned VariantID);
bool ParseInstruction(ParseInstructionInfo &Info, StringRef Name,
SMLoc NameLoc, OperandVector &Operands) override;
bool ParseDirective(AsmToken DirectiveID) override;
ParseStatus parseMemOperand(OperandVector &Operands);
ParseStatus matchAnyRegisterNameWithoutDollar(OperandVector &Operands,
StringRef Identifier, SMLoc S);
ParseStatus matchAnyRegisterWithoutDollar(OperandVector &Operands,
const AsmToken &Token, SMLoc S);
ParseStatus matchAnyRegisterWithoutDollar(OperandVector &Operands, SMLoc S);
ParseStatus parseAnyRegister(OperandVector &Operands);
ParseStatus parseImm(OperandVector &Operands);
ParseStatus parseJumpTarget(OperandVector &Operands);
ParseStatus parseInvNum(OperandVector &Operands);
ParseStatus parseRegisterList(OperandVector &Operands);
bool searchSymbolAlias(OperandVector &Operands);
bool parseOperand(OperandVector &, StringRef Mnemonic);
enum MacroExpanderResultTy {
MER_NotAMacro,
MER_Success,
MER_Fail,
};
// Expands assembly pseudo instructions.
MacroExpanderResultTy tryExpandInstruction(MCInst &Inst, SMLoc IDLoc,
MCStreamer &Out,
const MCSubtargetInfo *STI);
bool expandJalWithRegs(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out,
const MCSubtargetInfo *STI);
bool loadImmediate(int64_t ImmValue, unsigned DstReg, unsigned SrcReg,
bool Is32BitImm, bool IsAddress, SMLoc IDLoc,
MCStreamer &Out, const MCSubtargetInfo *STI);
bool loadAndAddSymbolAddress(const MCExpr *SymExpr, unsigned DstReg,
unsigned SrcReg, bool Is32BitSym, SMLoc IDLoc,
MCStreamer &Out, const MCSubtargetInfo *STI);
bool emitPartialAddress(MipsTargetStreamer &TOut, SMLoc IDLoc, MCSymbol *Sym);
bool expandLoadImm(MCInst &Inst, bool Is32BitImm, SMLoc IDLoc,
MCStreamer &Out, const MCSubtargetInfo *STI);
bool expandLoadSingleImmToGPR(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out,
const MCSubtargetInfo *STI);
bool expandLoadSingleImmToFPR(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out,
const MCSubtargetInfo *STI);
bool expandLoadDoubleImmToGPR(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out,
const MCSubtargetInfo *STI);
bool expandLoadDoubleImmToFPR(MCInst &Inst, bool Is64FPU, SMLoc IDLoc,
MCStreamer &Out, const MCSubtargetInfo *STI);
bool expandLoadAddress(unsigned DstReg, unsigned BaseReg,
const MCOperand &Offset, bool Is32BitAddress,
SMLoc IDLoc, MCStreamer &Out,
const MCSubtargetInfo *STI);
bool expandUncondBranchMMPseudo(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out,
const MCSubtargetInfo *STI);
void expandMem16Inst(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out,
const MCSubtargetInfo *STI, bool IsLoad);
void expandMem9Inst(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out,
const MCSubtargetInfo *STI, bool IsLoad);
bool expandLoadStoreMultiple(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out,
const MCSubtargetInfo *STI);
bool expandAliasImmediate(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out,
const MCSubtargetInfo *STI);
bool expandBranchImm(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out,
const MCSubtargetInfo *STI);
bool expandCondBranches(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out,
const MCSubtargetInfo *STI);
bool expandDivRem(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out,
const MCSubtargetInfo *STI, const bool IsMips64,
const bool Signed);
bool expandTrunc(MCInst &Inst, bool IsDouble, bool Is64FPU, SMLoc IDLoc,
MCStreamer &Out, const MCSubtargetInfo *STI);
bool expandUlh(MCInst &Inst, bool Signed, SMLoc IDLoc, MCStreamer &Out,
const MCSubtargetInfo *STI);
bool expandUsh(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out,
const MCSubtargetInfo *STI);
bool expandUxw(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out,
const MCSubtargetInfo *STI);
bool expandSge(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out,
const MCSubtargetInfo *STI);
bool expandSgeImm(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out,
const MCSubtargetInfo *STI);
bool expandSgtImm(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out,
const MCSubtargetInfo *STI);
bool expandSle(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out,
const MCSubtargetInfo *STI);
bool expandSleImm(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out,
const MCSubtargetInfo *STI);
bool expandRotation(MCInst &Inst, SMLoc IDLoc,
MCStreamer &Out, const MCSubtargetInfo *STI);
bool expandRotationImm(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out,
const MCSubtargetInfo *STI);
bool expandDRotation(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out,
const MCSubtargetInfo *STI);
bool expandDRotationImm(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out,
const MCSubtargetInfo *STI);
bool expandAbs(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out,
const MCSubtargetInfo *STI);
bool expandMulImm(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out,
const MCSubtargetInfo *STI);
bool expandMulO(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out,
const MCSubtargetInfo *STI);
bool expandMulOU(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out,
const MCSubtargetInfo *STI);
bool expandDMULMacro(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out,
const MCSubtargetInfo *STI);
bool expandLoadStoreDMacro(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out,
const MCSubtargetInfo *STI, bool IsLoad);
bool expandStoreDM1Macro(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out,
const MCSubtargetInfo *STI);
bool expandSeq(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out,
const MCSubtargetInfo *STI);
bool expandSeqI(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out,
const MCSubtargetInfo *STI);
bool expandSne(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out,
const MCSubtargetInfo *STI);
bool expandSneI(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out,
const MCSubtargetInfo *STI);
bool expandMXTRAlias(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out,
const MCSubtargetInfo *STI);
bool expandSaaAddr(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out,
const MCSubtargetInfo *STI);
bool reportParseError(const Twine &ErrorMsg);
bool reportParseError(SMLoc Loc, const Twine &ErrorMsg);
bool parseMemOffset(const MCExpr *&Res, bool isParenExpr);
bool parseSetMips0Directive();
bool parseSetArchDirective();
bool parseSetFeature(uint64_t Feature);
bool isPicAndNotNxxAbi(); // Used by .cpload, .cprestore, and .cpsetup.
bool parseDirectiveCpAdd(SMLoc Loc);
bool parseDirectiveCpLoad(SMLoc Loc);
bool parseDirectiveCpLocal(SMLoc Loc);
bool parseDirectiveCpRestore(SMLoc Loc);
bool parseDirectiveCPSetup();
bool parseDirectiveCPReturn();
bool parseDirectiveNaN();
bool parseDirectiveSet();
bool parseDirectiveOption();
bool parseInsnDirective();
bool parseRSectionDirective(StringRef Section);
bool parseSSectionDirective(StringRef Section, unsigned Type);
bool parseSetAtDirective();
bool parseSetNoAtDirective();
bool parseSetMacroDirective();
bool parseSetNoMacroDirective();
bool parseSetMsaDirective();
bool parseSetNoMsaDirective();
bool parseSetNoDspDirective();
bool parseSetNoMips3DDirective();
bool parseSetReorderDirective();
bool parseSetNoReorderDirective();
bool parseSetMips16Directive();
bool parseSetNoMips16Directive();
bool parseSetFpDirective();
bool parseSetOddSPRegDirective();
bool parseSetNoOddSPRegDirective();
bool parseSetPopDirective();
bool parseSetPushDirective();
bool parseSetSoftFloatDirective();
bool parseSetHardFloatDirective();
bool parseSetMtDirective();
bool parseSetNoMtDirective();
bool parseSetNoCRCDirective();
bool parseSetNoVirtDirective();
bool parseSetNoGINVDirective();
bool parseSetAssignment();
bool parseDirectiveGpWord();
bool parseDirectiveGpDWord();
bool parseDirectiveDtpRelWord();
bool parseDirectiveDtpRelDWord();
bool parseDirectiveTpRelWord();
bool parseDirectiveTpRelDWord();
bool parseDirectiveModule();
bool parseDirectiveModuleFP();
bool parseFpABIValue(MipsABIFlagsSection::FpABIKind &FpABI,
StringRef Directive);
bool parseInternalDirectiveReallowModule();
bool eatComma(StringRef ErrorStr);
int matchCPURegisterName(StringRef Symbol);
int matchHWRegsRegisterName(StringRef Symbol);
int matchFPURegisterName(StringRef Name);
int matchFCCRegisterName(StringRef Name);
int matchACRegisterName(StringRef Name);
int matchMSA128RegisterName(StringRef Name);
int matchMSA128CtrlRegisterName(StringRef Name);
unsigned getReg(int RC, int RegNo);
/// Returns the internal register number for the current AT. Also checks if
/// the current AT is unavailable (set to $0) and gives an error if it is.
/// This should be used in pseudo-instruction expansions which need AT.
unsigned getATReg(SMLoc Loc);
bool canUseATReg();
bool processInstruction(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out,
const MCSubtargetInfo *STI);
// Helper function that checks if the value of a vector index is within the
// boundaries of accepted values for each RegisterKind
// Example: INSERT.B $w0[n], $1 => 16 > n >= 0
bool validateMSAIndex(int Val, int RegKind);
// Selects a new architecture by updating the FeatureBits with the necessary
// info including implied dependencies.
// Internally, it clears all the feature bits related to *any* architecture
// and selects the new one using the ToggleFeature functionality of the
// MCSubtargetInfo object that handles implied dependencies. The reason we
// clear all the arch related bits manually is because ToggleFeature only
// clears the features that imply the feature being cleared and not the
// features implied by the feature being cleared. This is easier to see
// with an example:
// --------------------------------------------------
// | Feature | Implies |
// | -------------------------------------------------|
// | FeatureMips1 | None |
// | FeatureMips2 | FeatureMips1 |
// | FeatureMips3 | FeatureMips2 | FeatureMipsGP64 |
// | FeatureMips4 | FeatureMips3 |
// | ... | |
// --------------------------------------------------
//
// Setting Mips3 is equivalent to set: (FeatureMips3 | FeatureMips2 |
// FeatureMipsGP64 | FeatureMips1)
// Clearing Mips3 is equivalent to clear (FeatureMips3 | FeatureMips4).
void selectArch(StringRef ArchFeature) {
MCSubtargetInfo &STI = copySTI();
FeatureBitset FeatureBits = STI.getFeatureBits();
FeatureBits &= ~MipsAssemblerOptions::AllArchRelatedMask;
STI.setFeatureBits(FeatureBits);
setAvailableFeatures(
ComputeAvailableFeatures(STI.ToggleFeature(ArchFeature)));
AssemblerOptions.back()->setFeatures(STI.getFeatureBits());
}
void setFeatureBits(uint64_t Feature, StringRef FeatureString) {
if (!(getSTI().hasFeature(Feature))) {
MCSubtargetInfo &STI = copySTI();
setAvailableFeatures(
ComputeAvailableFeatures(STI.ToggleFeature(FeatureString)));
AssemblerOptions.back()->setFeatures(STI.getFeatureBits());
}
}
void clearFeatureBits(uint64_t Feature, StringRef FeatureString) {
if (getSTI().hasFeature(Feature)) {
MCSubtargetInfo &STI = copySTI();
setAvailableFeatures(
ComputeAvailableFeatures(STI.ToggleFeature(FeatureString)));
AssemblerOptions.back()->setFeatures(STI.getFeatureBits());
}
}
void setModuleFeatureBits(uint64_t Feature, StringRef FeatureString) {
setFeatureBits(Feature, FeatureString);
AssemblerOptions.front()->setFeatures(getSTI().getFeatureBits());
}
void clearModuleFeatureBits(uint64_t Feature, StringRef FeatureString) {
clearFeatureBits(Feature, FeatureString);
AssemblerOptions.front()->setFeatures(getSTI().getFeatureBits());
}
public:
enum MipsMatchResultTy {
Match_RequiresDifferentSrcAndDst = FIRST_TARGET_MATCH_RESULT_TY,
Match_RequiresDifferentOperands,
Match_RequiresNoZeroRegister,
Match_RequiresSameSrcAndDst,
Match_NoFCCRegisterForCurrentISA,
Match_NonZeroOperandForSync,
Match_NonZeroOperandForMTCX,
Match_RequiresPosSizeRange0_32,
Match_RequiresPosSizeRange33_64,
Match_RequiresPosSizeUImm6,
#define GET_OPERAND_DIAGNOSTIC_TYPES
#include "MipsGenAsmMatcher.inc"
#undef GET_OPERAND_DIAGNOSTIC_TYPES
};
MipsAsmParser(const MCSubtargetInfo &sti, MCAsmParser &parser,
const MCInstrInfo &MII, const MCTargetOptions &Options)
: MCTargetAsmParser(Options, sti, MII),
ABI(MipsABIInfo::computeTargetABI(Triple(sti.getTargetTriple()),
sti.getCPU(), Options)) {
MCAsmParserExtension::Initialize(parser);
parser.addAliasForDirective(".asciiz", ".asciz");
parser.addAliasForDirective(".hword", ".2byte");
parser.addAliasForDirective(".word", ".4byte");
parser.addAliasForDirective(".dword", ".8byte");
// Initialize the set of available features.
setAvailableFeatures(ComputeAvailableFeatures(getSTI().getFeatureBits()));
// Remember the initial assembler options. The user can not modify these.
AssemblerOptions.push_back(
std::make_unique<MipsAssemblerOptions>(getSTI().getFeatureBits()));
// Create an assembler options environment for the user to modify.
AssemblerOptions.push_back(
std::make_unique<MipsAssemblerOptions>(getSTI().getFeatureBits()));
getTargetStreamer().updateABIInfo(*this);
if (!isABI_O32() && !useOddSPReg() != 0)
report_fatal_error("-mno-odd-spreg requires the O32 ABI");
CurrentFn = nullptr;
CurForbiddenSlotAttr = false;
IsPicEnabled = getContext().getObjectFileInfo()->isPositionIndependent();
IsCpRestoreSet = false;
CpRestoreOffset = -1;
GPReg = ABI.GetGlobalPtr();
const Triple &TheTriple = sti.getTargetTriple();
IsLittleEndian = TheTriple.isLittleEndian();
if (getSTI().getCPU() == "mips64r6" && inMicroMipsMode())
report_fatal_error("microMIPS64R6 is not supported", false);
if (!isABI_O32() && inMicroMipsMode())
report_fatal_error("microMIPS64 is not supported", false);
}
/// True if all of $fcc0 - $fcc7 exist for the current ISA.
bool hasEightFccRegisters() const { return hasMips4() || hasMips32(); }
bool isGP64bit() const {
return getSTI().hasFeature(Mips::FeatureGP64Bit);
}
bool isFP64bit() const {
return getSTI().hasFeature(Mips::FeatureFP64Bit);
}
bool isJalrRelocAvailable(const MCExpr *JalExpr) {
if (!EmitJalrReloc)
return false;
MCValue Res;
if (!JalExpr->evaluateAsRelocatable(Res, nullptr, nullptr))
return false;
if (Res.getSymB() != nullptr)
return false;
if (Res.getConstant() != 0)
return ABI.IsN32() || ABI.IsN64();
return true;
}
const MipsABIInfo &getABI() const { return ABI; }
bool isABI_N32() const { return ABI.IsN32(); }
bool isABI_N64() const { return ABI.IsN64(); }
bool isABI_O32() const { return ABI.IsO32(); }
bool isABI_FPXX() const {
return getSTI().hasFeature(Mips::FeatureFPXX);
}
bool useOddSPReg() const {
return !(getSTI().hasFeature(Mips::FeatureNoOddSPReg));
}
bool inMicroMipsMode() const {
return getSTI().hasFeature(Mips::FeatureMicroMips);
}
bool hasMips1() const {
return getSTI().hasFeature(Mips::FeatureMips1);
}
bool hasMips2() const {
return getSTI().hasFeature(Mips::FeatureMips2);
}
bool hasMips3() const {
return getSTI().hasFeature(Mips::FeatureMips3);
}
bool hasMips4() const {
return getSTI().hasFeature(Mips::FeatureMips4);
}
bool hasMips5() const {
return getSTI().hasFeature(Mips::FeatureMips5);
}
bool hasMips32() const {
return getSTI().hasFeature(Mips::FeatureMips32);
}
bool hasMips64() const {
return getSTI().hasFeature(Mips::FeatureMips64);
}
bool hasMips32r2() const {
return getSTI().hasFeature(Mips::FeatureMips32r2);
}
bool hasMips64r2() const {
return getSTI().hasFeature(Mips::FeatureMips64r2);
}
bool hasMips32r3() const {
return (getSTI().hasFeature(Mips::FeatureMips32r3));
}
bool hasMips64r3() const {
return (getSTI().hasFeature(Mips::FeatureMips64r3));
}
bool hasMips32r5() const {
return (getSTI().hasFeature(Mips::FeatureMips32r5));
}
bool hasMips64r5() const {
return (getSTI().hasFeature(Mips::FeatureMips64r5));
}
bool hasMips32r6() const {
return getSTI().hasFeature(Mips::FeatureMips32r6);
}
bool hasMips64r6() const {
return getSTI().hasFeature(Mips::FeatureMips64r6);
}
bool hasDSP() const {
return getSTI().hasFeature(Mips::FeatureDSP);
}
bool hasDSPR2() const {
return getSTI().hasFeature(Mips::FeatureDSPR2);
}
bool hasDSPR3() const {
return getSTI().hasFeature(Mips::FeatureDSPR3);
}
bool hasMSA() const {
return getSTI().hasFeature(Mips::FeatureMSA);
}
bool hasCnMips() const {
return (getSTI().hasFeature(Mips::FeatureCnMips));
}
bool hasCnMipsP() const {
return (getSTI().hasFeature(Mips::FeatureCnMipsP));
}
bool inPicMode() {
return IsPicEnabled;
}
bool inMips16Mode() const {
return getSTI().hasFeature(Mips::FeatureMips16);
}
bool useTraps() const {
return getSTI().hasFeature(Mips::FeatureUseTCCInDIV);
}
bool useSoftFloat() const {
return getSTI().hasFeature(Mips::FeatureSoftFloat);
}
bool hasMT() const {
return getSTI().hasFeature(Mips::FeatureMT);
}
bool hasCRC() const {
return getSTI().hasFeature(Mips::FeatureCRC);
}
bool hasVirt() const {
return getSTI().hasFeature(Mips::FeatureVirt);
}
bool hasGINV() const {
return getSTI().hasFeature(Mips::FeatureGINV);
}
bool hasForbiddenSlot(const MCInstrDesc &MCID) const {
return !inMicroMipsMode() && (MCID.TSFlags & MipsII::HasForbiddenSlot);
}
bool SafeInForbiddenSlot(const MCInstrDesc &MCID) const {
return !(MCID.TSFlags & MipsII::IsCTI);
}
void onEndOfFile() override;
/// Warn if RegIndex is the same as the current AT.
void warnIfRegIndexIsAT(unsigned RegIndex, SMLoc Loc);
void warnIfNoMacro(SMLoc Loc);
bool isLittle() const { return IsLittleEndian; }
const MCExpr *createTargetUnaryExpr(const MCExpr *E,
AsmToken::TokenKind OperatorToken,
MCContext &Ctx) override {
switch(OperatorToken) {
default:
llvm_unreachable("Unknown token");
return nullptr;
case AsmToken::PercentCall16:
return MipsMCExpr::create(MipsMCExpr::MEK_GOT_CALL, E, Ctx);
case AsmToken::PercentCall_Hi:
return MipsMCExpr::create(MipsMCExpr::MEK_CALL_HI16, E, Ctx);
case AsmToken::PercentCall_Lo:
return MipsMCExpr::create(MipsMCExpr::MEK_CALL_LO16, E, Ctx);
case AsmToken::PercentDtprel_Hi:
return MipsMCExpr::create(MipsMCExpr::MEK_DTPREL_HI, E, Ctx);
case AsmToken::PercentDtprel_Lo:
return MipsMCExpr::create(MipsMCExpr::MEK_DTPREL_LO, E, Ctx);
case AsmToken::PercentGot:
return MipsMCExpr::create(MipsMCExpr::MEK_GOT, E, Ctx);
case AsmToken::PercentGot_Disp:
return MipsMCExpr::create(MipsMCExpr::MEK_GOT_DISP, E, Ctx);
case AsmToken::PercentGot_Hi:
return MipsMCExpr::create(MipsMCExpr::MEK_GOT_HI16, E, Ctx);
case AsmToken::PercentGot_Lo:
return MipsMCExpr::create(MipsMCExpr::MEK_GOT_LO16, E, Ctx);
case AsmToken::PercentGot_Ofst:
return MipsMCExpr::create(MipsMCExpr::MEK_GOT_OFST, E, Ctx);
case AsmToken::PercentGot_Page:
return MipsMCExpr::create(MipsMCExpr::MEK_GOT_PAGE, E, Ctx);
case AsmToken::PercentGottprel:
return MipsMCExpr::create(MipsMCExpr::MEK_GOTTPREL, E, Ctx);
case AsmToken::PercentGp_Rel:
return MipsMCExpr::create(MipsMCExpr::MEK_GPREL, E, Ctx);
case AsmToken::PercentHi:
return MipsMCExpr::create(MipsMCExpr::MEK_HI, E, Ctx);
case AsmToken::PercentHigher:
return MipsMCExpr::create(MipsMCExpr::MEK_HIGHER, E, Ctx);
case AsmToken::PercentHighest:
return MipsMCExpr::create(MipsMCExpr::MEK_HIGHEST, E, Ctx);
case AsmToken::PercentLo:
return MipsMCExpr::create(MipsMCExpr::MEK_LO, E, Ctx);
case AsmToken::PercentNeg:
return MipsMCExpr::create(MipsMCExpr::MEK_NEG, E, Ctx);
case AsmToken::PercentPcrel_Hi:
return MipsMCExpr::create(MipsMCExpr::MEK_PCREL_HI16, E, Ctx);
case AsmToken::PercentPcrel_Lo:
return MipsMCExpr::create(MipsMCExpr::MEK_PCREL_LO16, E, Ctx);
case AsmToken::PercentTlsgd:
return MipsMCExpr::create(MipsMCExpr::MEK_TLSGD, E, Ctx);
case AsmToken::PercentTlsldm:
return MipsMCExpr::create(MipsMCExpr::MEK_TLSLDM, E, Ctx);
case AsmToken::PercentTprel_Hi:
return MipsMCExpr::create(MipsMCExpr::MEK_TPREL_HI, E, Ctx);
case AsmToken::PercentTprel_Lo:
return MipsMCExpr::create(MipsMCExpr::MEK_TPREL_LO, E, Ctx);
}
}
bool areEqualRegs(const MCParsedAsmOperand &Op1,
const MCParsedAsmOperand &Op2) const override;
};
/// MipsOperand - Instances of this class represent a parsed Mips machine
/// instruction.
class MipsOperand : public MCParsedAsmOperand {
public:
/// Broad categories of register classes
/// The exact class is finalized by the render method.
enum RegKind {
RegKind_GPR = 1, /// GPR32 and GPR64 (depending on isGP64bit())
RegKind_FGR = 2, /// FGR32, FGR64, AFGR64 (depending on context and
/// isFP64bit())
RegKind_FCC = 4, /// FCC
RegKind_MSA128 = 8, /// MSA128[BHWD] (makes no difference which)
RegKind_MSACtrl = 16, /// MSA control registers
RegKind_COP2 = 32, /// COP2
RegKind_ACC = 64, /// HI32DSP, LO32DSP, and ACC64DSP (depending on
/// context).
RegKind_CCR = 128, /// CCR
RegKind_HWRegs = 256, /// HWRegs
RegKind_COP3 = 512, /// COP3
RegKind_COP0 = 1024, /// COP0
/// Potentially any (e.g. $1)
RegKind_Numeric = RegKind_GPR | RegKind_FGR | RegKind_FCC | RegKind_MSA128 |
RegKind_MSACtrl | RegKind_COP2 | RegKind_ACC |
RegKind_CCR | RegKind_HWRegs | RegKind_COP3 | RegKind_COP0
};
private:
enum KindTy {
k_Immediate, /// An immediate (possibly involving symbol references)
k_Memory, /// Base + Offset Memory Address
k_RegisterIndex, /// A register index in one or more RegKind.
k_Token, /// A simple token
k_RegList, /// A physical register list
} Kind;
public:
MipsOperand(KindTy K, MipsAsmParser &Parser) : Kind(K), AsmParser(Parser) {}
~MipsOperand() override {
switch (Kind) {
case k_Memory:
delete Mem.Base;
break;
case k_RegList:
delete RegList.List;
break;
case k_Immediate:
case k_RegisterIndex:
case k_Token:
break;
}
}
private:
/// For diagnostics, and checking the assembler temporary
MipsAsmParser &AsmParser;
struct Token {
const char *Data;
unsigned Length;
};
struct RegIdxOp {
unsigned Index; /// Index into the register class
RegKind Kind; /// Bitfield of the kinds it could possibly be
struct Token Tok; /// The input token this operand originated from.
const MCRegisterInfo *RegInfo;
};
struct ImmOp {
const MCExpr *Val;
};
struct MemOp {
MipsOperand *Base;
const MCExpr *Off;
};
struct RegListOp {
SmallVector<unsigned, 10> *List;
};
union {
struct Token Tok;
struct RegIdxOp RegIdx;
struct ImmOp Imm;
struct MemOp Mem;
struct RegListOp RegList;
};
SMLoc StartLoc, EndLoc;
/// Internal constructor for register kinds
static std::unique_ptr<MipsOperand> CreateReg(unsigned Index, StringRef Str,
RegKind RegKind,
const MCRegisterInfo *RegInfo,
SMLoc S, SMLoc E,
MipsAsmParser &Parser) {
auto Op = std::make_unique<MipsOperand>(k_RegisterIndex, Parser);
Op->RegIdx.Index = Index;
Op->RegIdx.RegInfo = RegInfo;
Op->RegIdx.Kind = RegKind;
Op->RegIdx.Tok.Data = Str.data();
Op->RegIdx.Tok.Length = Str.size();
Op->StartLoc = S;
Op->EndLoc = E;
return Op;
}
public:
/// Coerce the register to GPR32 and return the real register for the current
/// target.
unsigned getGPR32Reg() const {
assert(isRegIdx() && (RegIdx.Kind & RegKind_GPR) && "Invalid access!");
AsmParser.warnIfRegIndexIsAT(RegIdx.Index, StartLoc);
unsigned ClassID = Mips::GPR32RegClassID;
return RegIdx.RegInfo->getRegClass(ClassID).getRegister(RegIdx.Index);
}
/// Coerce the register to GPR32 and return the real register for the current
/// target.
unsigned getGPRMM16Reg() const {
assert(isRegIdx() && (RegIdx.Kind & RegKind_GPR) && "Invalid access!");
unsigned ClassID = Mips::GPR32RegClassID;
return RegIdx.RegInfo->getRegClass(ClassID).getRegister(RegIdx.Index);
}
/// Coerce the register to GPR64 and return the real register for the current
/// target.
unsigned getGPR64Reg() const {
assert(isRegIdx() && (RegIdx.Kind & RegKind_GPR) && "Invalid access!");
unsigned ClassID = Mips::GPR64RegClassID;
return RegIdx.RegInfo->getRegClass(ClassID).getRegister(RegIdx.Index);
}
private:
/// Coerce the register to AFGR64 and return the real register for the current
/// target.
unsigned getAFGR64Reg() const {
assert(isRegIdx() && (RegIdx.Kind & RegKind_FGR) && "Invalid access!");
if (RegIdx.Index % 2 != 0)
AsmParser.Warning(StartLoc, "Float register should be even.");
return RegIdx.RegInfo->getRegClass(Mips::AFGR64RegClassID)
.getRegister(RegIdx.Index / 2);
}
/// Coerce the register to FGR64 and return the real register for the current
/// target.
unsigned getFGR64Reg() const {
assert(isRegIdx() && (RegIdx.Kind & RegKind_FGR) && "Invalid access!");
return RegIdx.RegInfo->getRegClass(Mips::FGR64RegClassID)
.getRegister(RegIdx.Index);
}
/// Coerce the register to FGR32 and return the real register for the current
/// target.
unsigned getFGR32Reg() const {
assert(isRegIdx() && (RegIdx.Kind & RegKind_FGR) && "Invalid access!");
return RegIdx.RegInfo->getRegClass(Mips::FGR32RegClassID)
.getRegister(RegIdx.Index);
}
/// Coerce the register to FCC and return the real register for the current
/// target.
unsigned getFCCReg() const {
assert(isRegIdx() && (RegIdx.Kind & RegKind_FCC) && "Invalid access!");
return RegIdx.RegInfo->getRegClass(Mips::FCCRegClassID)
.getRegister(RegIdx.Index);
}
/// Coerce the register to MSA128 and return the real register for the current
/// target.
unsigned getMSA128Reg() const {
assert(isRegIdx() && (RegIdx.Kind & RegKind_MSA128) && "Invalid access!");
// It doesn't matter which of the MSA128[BHWD] classes we use. They are all
// identical
unsigned ClassID = Mips::MSA128BRegClassID;
return RegIdx.RegInfo->getRegClass(ClassID).getRegister(RegIdx.Index);
}
/// Coerce the register to MSACtrl and return the real register for the
/// current target.
unsigned getMSACtrlReg() const {
assert(isRegIdx() && (RegIdx.Kind & RegKind_MSACtrl) && "Invalid access!");
unsigned ClassID = Mips::MSACtrlRegClassID;
return RegIdx.RegInfo->getRegClass(ClassID).getRegister(RegIdx.Index);
}
/// Coerce the register to COP0 and return the real register for the
/// current target.
unsigned getCOP0Reg() const {
assert(isRegIdx() && (RegIdx.Kind & RegKind_COP0) && "Invalid access!");
unsigned ClassID = Mips::COP0RegClassID;
return RegIdx.RegInfo->getRegClass(ClassID).getRegister(RegIdx.Index);
}
/// Coerce the register to COP2 and return the real register for the
/// current target.
unsigned getCOP2Reg() const {
assert(isRegIdx() && (RegIdx.Kind & RegKind_COP2) && "Invalid access!");
unsigned ClassID = Mips::COP2RegClassID;
return RegIdx.RegInfo->getRegClass(ClassID).getRegister(RegIdx.Index);
}
/// Coerce the register to COP3 and return the real register for the
/// current target.
unsigned getCOP3Reg() const {
assert(isRegIdx() && (RegIdx.Kind & RegKind_COP3) && "Invalid access!");
unsigned ClassID = Mips::COP3RegClassID;
return RegIdx.RegInfo->getRegClass(ClassID).getRegister(RegIdx.Index);
}
/// Coerce the register to ACC64DSP and return the real register for the
/// current target.
unsigned getACC64DSPReg() const {
assert(isRegIdx() && (RegIdx.Kind & RegKind_ACC) && "Invalid access!");
unsigned ClassID = Mips::ACC64DSPRegClassID;
return RegIdx.RegInfo->getRegClass(ClassID).getRegister(RegIdx.Index);
}
/// Coerce the register to HI32DSP and return the real register for the
/// current target.
unsigned getHI32DSPReg() const {
assert(isRegIdx() && (RegIdx.Kind & RegKind_ACC) && "Invalid access!");
unsigned ClassID = Mips::HI32DSPRegClassID;
return RegIdx.RegInfo->getRegClass(ClassID).getRegister(RegIdx.Index);
}
/// Coerce the register to LO32DSP and return the real register for the
/// current target.
unsigned getLO32DSPReg() const {
assert(isRegIdx() && (RegIdx.Kind & RegKind_ACC) && "Invalid access!");
unsigned ClassID = Mips::LO32DSPRegClassID;
return RegIdx.RegInfo->getRegClass(ClassID).getRegister(RegIdx.Index);
}
/// Coerce the register to CCR and return the real register for the
/// current target.
unsigned getCCRReg() const {
assert(isRegIdx() && (RegIdx.Kind & RegKind_CCR) && "Invalid access!");
unsigned ClassID = Mips::CCRRegClassID;
return RegIdx.RegInfo->getRegClass(ClassID).getRegister(RegIdx.Index);
}
/// Coerce the register to HWRegs and return the real register for the
/// current target.
unsigned getHWRegsReg() const {
assert(isRegIdx() && (RegIdx.Kind & RegKind_HWRegs) && "Invalid access!");
unsigned ClassID = Mips::HWRegsRegClassID;
return RegIdx.RegInfo->getRegClass(ClassID).getRegister(RegIdx.Index);
}
public:
void addExpr(MCInst &Inst, const MCExpr *Expr) const {
// Add as immediate when possible. Null MCExpr = 0.
if (!Expr)
Inst.addOperand(MCOperand::createImm(0));
else if (const MCConstantExpr *CE = dyn_cast<MCConstantExpr>(Expr))
Inst.addOperand(MCOperand::createImm(CE->getValue()));
else
Inst.addOperand(MCOperand::createExpr(Expr));
}
void addRegOperands(MCInst &Inst, unsigned N) const {
llvm_unreachable("Use a custom parser instead");
}
/// Render the operand to an MCInst as a GPR32
/// Asserts if the wrong number of operands are requested, or the operand
/// is not a k_RegisterIndex compatible with RegKind_GPR
void addGPR32ZeroAsmRegOperands(MCInst &Inst, unsigned N) const {
assert(N == 1 && "Invalid number of operands!");
Inst.addOperand(MCOperand::createReg(getGPR32Reg()));
}
void addGPR32NonZeroAsmRegOperands(MCInst &Inst, unsigned N) const {
assert(N == 1 && "Invalid number of operands!");
Inst.addOperand(MCOperand::createReg(getGPR32Reg()));
}
void addGPR32AsmRegOperands(MCInst &Inst, unsigned N) const {
assert(N == 1 && "Invalid number of operands!");
Inst.addOperand(MCOperand::createReg(getGPR32Reg()));
}
void addGPRMM16AsmRegOperands(MCInst &Inst, unsigned N) const {
assert(N == 1 && "Invalid number of operands!");
Inst.addOperand(MCOperand::createReg(getGPRMM16Reg()));
}
void addGPRMM16AsmRegZeroOperands(MCInst &Inst, unsigned N) const {
assert(N == 1 && "Invalid number of operands!");
Inst.addOperand(MCOperand::createReg(getGPRMM16Reg()));
}
void addGPRMM16AsmRegMovePOperands(MCInst &Inst, unsigned N) const {
assert(N == 1 && "Invalid number of operands!");
Inst.addOperand(MCOperand::createReg(getGPRMM16Reg()));
}
void addGPRMM16AsmRegMovePPairFirstOperands(MCInst &Inst, unsigned N) const {
assert(N == 1 && "Invalid number of operands!");
Inst.addOperand(MCOperand::createReg(getGPRMM16Reg()));
}
void addGPRMM16AsmRegMovePPairSecondOperands(MCInst &Inst,
unsigned N) const {
assert(N == 1 && "Invalid number of operands!");
Inst.addOperand(MCOperand::createReg(getGPRMM16Reg()));
}
/// Render the operand to an MCInst as a GPR64
/// Asserts if the wrong number of operands are requested, or the operand
/// is not a k_RegisterIndex compatible with RegKind_GPR
void addGPR64AsmRegOperands(MCInst &Inst, unsigned N) const {
assert(N == 1 && "Invalid number of operands!");
Inst.addOperand(MCOperand::createReg(getGPR64Reg()));
}
void addAFGR64AsmRegOperands(MCInst &Inst, unsigned N) const {
assert(N == 1 && "Invalid number of operands!");
Inst.addOperand(MCOperand::createReg(getAFGR64Reg()));
}
void addStrictlyAFGR64AsmRegOperands(MCInst &Inst, unsigned N) const {
assert(N == 1 && "Invalid number of operands!");
Inst.addOperand(MCOperand::createReg(getAFGR64Reg()));
}
void addStrictlyFGR64AsmRegOperands(MCInst &Inst, unsigned N) const {
assert(N == 1 && "Invalid number of operands!");
Inst.addOperand(MCOperand::createReg(getFGR64Reg()));
}
void addFGR64AsmRegOperands(MCInst &Inst, unsigned N) const {
assert(N == 1 && "Invalid number of operands!");
Inst.addOperand(MCOperand::createReg(getFGR64Reg()));
}
void addFGR32AsmRegOperands(MCInst &Inst, unsigned N) const {
assert(N == 1 && "Invalid number of operands!");
Inst.addOperand(MCOperand::createReg(getFGR32Reg()));
// FIXME: We ought to do this for -integrated-as without -via-file-asm too.
// FIXME: This should propagate failure up to parseStatement.
if (!AsmParser.useOddSPReg() && RegIdx.Index & 1)
AsmParser.getParser().printError(
StartLoc, "-mno-odd-spreg prohibits the use of odd FPU "
"registers");
}
void addStrictlyFGR32AsmRegOperands(MCInst &Inst, unsigned N) const {
assert(N == 1 && "Invalid number of operands!");
Inst.addOperand(MCOperand::createReg(getFGR32Reg()));
// FIXME: We ought to do this for -integrated-as without -via-file-asm too.
if (!AsmParser.useOddSPReg() && RegIdx.Index & 1)
AsmParser.Error(StartLoc, "-mno-odd-spreg prohibits the use of odd FPU "
"registers");
}
void addFCCAsmRegOperands(MCInst &Inst, unsigned N) const {
assert(N == 1 && "Invalid number of operands!");
Inst.addOperand(MCOperand::createReg(getFCCReg()));
}
void addMSA128AsmRegOperands(MCInst &Inst, unsigned N) const {
assert(N == 1 && "Invalid number of operands!");
Inst.addOperand(MCOperand::createReg(getMSA128Reg()));
}
void addMSACtrlAsmRegOperands(MCInst &Inst, unsigned N) const {
assert(N == 1 && "Invalid number of operands!");
Inst.addOperand(MCOperand::createReg(getMSACtrlReg()));
}
void addCOP0AsmRegOperands(MCInst &Inst, unsigned N) const {
assert(N == 1 && "Invalid number of operands!");
Inst.addOperand(MCOperand::createReg(getCOP0Reg()));
}
void addCOP2AsmRegOperands(MCInst &Inst, unsigned N) const {
assert(N == 1 && "Invalid number of operands!");
Inst.addOperand(MCOperand::createReg(getCOP2Reg()));
}
void addCOP3AsmRegOperands(MCInst &Inst, unsigned N) const {
assert(N == 1 && "Invalid number of operands!");
Inst.addOperand(MCOperand::createReg(getCOP3Reg()));
}
void addACC64DSPAsmRegOperands(MCInst &Inst, unsigned N) const {
assert(N == 1 && "Invalid number of operands!");
Inst.addOperand(MCOperand::createReg(getACC64DSPReg()));
}
void addHI32DSPAsmRegOperands(MCInst &Inst, unsigned N) const {
assert(N == 1 && "Invalid number of operands!");
Inst.addOperand(MCOperand::createReg(getHI32DSPReg()));
}
void addLO32DSPAsmRegOperands(MCInst &Inst, unsigned N) const {
assert(N == 1 && "Invalid number of operands!");
Inst.addOperand(MCOperand::createReg(getLO32DSPReg()));
}
void addCCRAsmRegOperands(MCInst &Inst, unsigned N) const {
assert(N == 1 && "Invalid number of operands!");
Inst.addOperand(MCOperand::createReg(getCCRReg()));
}
void addHWRegsAsmRegOperands(MCInst &Inst, unsigned N) const {
assert(N == 1 && "Invalid number of operands!");
Inst.addOperand(MCOperand::createReg(getHWRegsReg()));
}
template <unsigned Bits, int Offset = 0, int AdjustOffset = 0>
void addConstantUImmOperands(MCInst &Inst, unsigned N) const {
assert(N == 1 && "Invalid number of operands!");
uint64_t Imm = getConstantImm() - Offset;
Imm &= (1ULL << Bits) - 1;
Imm += Offset;
Imm += AdjustOffset;
Inst.addOperand(MCOperand::createImm(Imm));
}
template <unsigned Bits>
void addSImmOperands(MCInst &Inst, unsigned N) const {
if (isImm() && !isConstantImm()) {
addExpr(Inst, getImm());
return;
}
addConstantSImmOperands<Bits, 0, 0>(Inst, N);
}
template <unsigned Bits>
void addUImmOperands(MCInst &Inst, unsigned N) const {
if (isImm() && !isConstantImm()) {
addExpr(Inst, getImm());
return;
}
addConstantUImmOperands<Bits, 0, 0>(Inst, N);
}
template <unsigned Bits, int Offset = 0, int AdjustOffset = 0>
void addConstantSImmOperands(MCInst &Inst, unsigned N) const {
assert(N == 1 && "Invalid number of operands!");
int64_t Imm = getConstantImm() - Offset;
Imm = SignExtend64<Bits>(Imm);
Imm += Offset;
Imm += AdjustOffset;
Inst.addOperand(MCOperand::createImm(Imm));
}
void addImmOperands(MCInst &Inst, unsigned N) const {
assert(N == 1 && "Invalid number of operands!");
const MCExpr *Expr = getImm();
addExpr(Inst, Expr);
}
void addMemOperands(MCInst &Inst, unsigned N) const {
assert(N == 2 && "Invalid number of operands!");
Inst.addOperand(MCOperand::createReg(AsmParser.getABI().ArePtrs64bit()
? getMemBase()->getGPR64Reg()
: getMemBase()->getGPR32Reg()));
const MCExpr *Expr = getMemOff();
addExpr(Inst, Expr);
}
void addMicroMipsMemOperands(MCInst &Inst, unsigned N) const {
assert(N == 2 && "Invalid number of operands!");
Inst.addOperand(MCOperand::createReg(getMemBase()->getGPRMM16Reg()));
const MCExpr *Expr = getMemOff();
addExpr(Inst, Expr);
}
void addRegListOperands(MCInst &Inst, unsigned N) const {
assert(N == 1 && "Invalid number of operands!");
for (auto RegNo : getRegList())
Inst.addOperand(MCOperand::createReg(RegNo));
}
bool isReg() const override {
// As a special case until we sort out the definition of div/divu, accept
// $0/$zero here so that MCK_ZERO works correctly.
return isGPRAsmReg() && RegIdx.Index == 0;
}
bool isRegIdx() const { return Kind == k_RegisterIndex; }
bool isImm() const override { return Kind == k_Immediate; }
bool isConstantImm() const {
int64_t Res;
return isImm() && getImm()->evaluateAsAbsolute(Res);
}
bool isConstantImmz() const {
return isConstantImm() && getConstantImm() == 0;
}
template <unsigned Bits, int Offset = 0> bool isConstantUImm() const {
return isConstantImm() && isUInt<Bits>(getConstantImm() - Offset);
}
template <unsigned Bits> bool isSImm() const {
return isConstantImm() ? isInt<Bits>(getConstantImm()) : isImm();
}
template <unsigned Bits> bool isUImm() const {
return isConstantImm() ? isUInt<Bits>(getConstantImm()) : isImm();
}
template <unsigned Bits> bool isAnyImm() const {
return isConstantImm() ? (isInt<Bits>(getConstantImm()) ||
isUInt<Bits>(getConstantImm()))
: isImm();
}
template <unsigned Bits, int Offset = 0> bool isConstantSImm() const {
return isConstantImm() && isInt<Bits>(getConstantImm() - Offset);
}
template <unsigned Bottom, unsigned Top> bool isConstantUImmRange() const {
return isConstantImm() && getConstantImm() >= Bottom &&
getConstantImm() <= Top;
}
bool isToken() const override {
// Note: It's not possible to pretend that other operand kinds are tokens.
// The matcher emitter checks tokens first.
return Kind == k_Token;
}
bool isMem() const override { return Kind == k_Memory; }
bool isConstantMemOff() const {
return isMem() && isa<MCConstantExpr>(getMemOff());
}
// Allow relocation operators.
template <unsigned Bits, unsigned ShiftAmount = 0>
bool isMemWithSimmOffset() const {
if (!isMem())
return false;
if (!getMemBase()->isGPRAsmReg())
return false;
if (isa<MCTargetExpr>(getMemOff()) ||
(isConstantMemOff() &&
isShiftedInt<Bits, ShiftAmount>(getConstantMemOff())))
return true;
MCValue Res;
bool IsReloc = getMemOff()->evaluateAsRelocatable(Res, nullptr, nullptr);
return IsReloc && isShiftedInt<Bits, ShiftAmount>(Res.getConstant());
}
bool isMemWithPtrSizeOffset() const {
if (!isMem())
return false;
if (!getMemBase()->isGPRAsmReg())
return false;
const unsigned PtrBits = AsmParser.getABI().ArePtrs64bit() ? 64 : 32;
if (isa<MCTargetExpr>(getMemOff()) ||
(isConstantMemOff() && isIntN(PtrBits, getConstantMemOff())))
return true;
MCValue Res;
bool IsReloc = getMemOff()->evaluateAsRelocatable(Res, nullptr, nullptr);
return IsReloc && isIntN(PtrBits, Res.getConstant());
}
bool isMemWithGRPMM16Base() const {
return isMem() && getMemBase()->isMM16AsmReg();
}
template <unsigned Bits> bool isMemWithUimmOffsetSP() const {
return isMem() && isConstantMemOff() && isUInt<Bits>(getConstantMemOff())
&& getMemBase()->isRegIdx() && (getMemBase()->getGPR32Reg() == Mips::SP);
}
template <unsigned Bits> bool isMemWithUimmWordAlignedOffsetSP() const {
return isMem() && isConstantMemOff() && isUInt<Bits>(getConstantMemOff())
&& (getConstantMemOff() % 4 == 0) && getMemBase()->isRegIdx()
&& (getMemBase()->getGPR32Reg() == Mips::SP);
}
template <unsigned Bits> bool isMemWithSimmWordAlignedOffsetGP() const {
return isMem() && isConstantMemOff() && isInt<Bits>(getConstantMemOff())
&& (getConstantMemOff() % 4 == 0) && getMemBase()->isRegIdx()
&& (getMemBase()->getGPR32Reg() == Mips::GP);
}
template <unsigned Bits, unsigned ShiftLeftAmount>
bool isScaledUImm() const {
return isConstantImm() &&
isShiftedUInt<Bits, ShiftLeftAmount>(getConstantImm());
}
template <unsigned Bits, unsigned ShiftLeftAmount>
bool isScaledSImm() const {
if (isConstantImm() &&
isShiftedInt<Bits, ShiftLeftAmount>(getConstantImm()))
return true;
// Operand can also be a symbol or symbol plus
// offset in case of relocations.
if (Kind != k_Immediate)
return false;
MCValue Res;
bool Success = getImm()->evaluateAsRelocatable(Res, nullptr, nullptr);
return Success && isShiftedInt<Bits, ShiftLeftAmount>(Res.getConstant());
}
bool isRegList16() const {
if (!isRegList())
return false;
int Size = RegList.List->size();
if (Size < 2 || Size > 5)
return false;
unsigned R0 = RegList.List->front();
unsigned R1 = RegList.List->back();
if (!((R0 == Mips::S0 && R1 == Mips::RA) ||
(R0 == Mips::S0_64 && R1 == Mips::RA_64)))
return false;
int PrevReg = *RegList.List->begin();
for (int i = 1; i < Size - 1; i++) {
int Reg = (*(RegList.List))[i];
if ( Reg != PrevReg + 1)
return false;
PrevReg = Reg;
}
return true;
}
bool isInvNum() const { return Kind == k_Immediate; }
bool isLSAImm() const {
if (!isConstantImm())
return false;
int64_t Val = getConstantImm();
return 1 <= Val && Val <= 4;
}
bool isRegList() const { return Kind == k_RegList; }
StringRef getToken() const {
assert(Kind == k_Token && "Invalid access!");
return StringRef(Tok.Data, Tok.Length);
}
unsigned getReg() const override {
// As a special case until we sort out the definition of div/divu, accept
// $0/$zero here so that MCK_ZERO works correctly.
if (Kind == k_RegisterIndex && RegIdx.Index == 0 &&
RegIdx.Kind & RegKind_GPR)
return getGPR32Reg(); // FIXME: GPR64 too
llvm_unreachable("Invalid access!");
return 0;
}
const MCExpr *getImm() const {
assert((Kind == k_Immediate) && "Invalid access!");
return Imm.Val;
}
int64_t getConstantImm() const {
const MCExpr *Val = getImm();
int64_t Value = 0;
(void)Val->evaluateAsAbsolute(Value);
return Value;
}
MipsOperand *getMemBase() const {
assert((Kind == k_Memory) && "Invalid access!");
return Mem.Base;
}
const MCExpr *getMemOff() const {
assert((Kind == k_Memory) && "Invalid access!");
return Mem.Off;
}
int64_t getConstantMemOff() const {
return static_cast<const MCConstantExpr *>(getMemOff())->getValue();
}
const SmallVectorImpl<unsigned> &getRegList() const {
assert((Kind == k_RegList) && "Invalid access!");
return *(RegList.List);
}
static std::unique_ptr<MipsOperand> CreateToken(StringRef Str, SMLoc S,
MipsAsmParser &Parser) {
auto Op = std::make_unique<MipsOperand>(k_Token, Parser);
Op->Tok.Data = Str.data();
Op->Tok.Length = Str.size();
Op->StartLoc = S;
Op->EndLoc = S;
return Op;
}
/// Create a numeric register (e.g. $1). The exact register remains
/// unresolved until an instruction successfully matches
static std::unique_ptr<MipsOperand>
createNumericReg(unsigned Index, StringRef Str, const MCRegisterInfo *RegInfo,
SMLoc S, SMLoc E, MipsAsmParser &Parser) {
LLVM_DEBUG(dbgs() << "createNumericReg(" << Index << ", ...)\n");
return CreateReg(Index, Str, RegKind_Numeric, RegInfo, S, E, Parser);
}
/// Create a register that is definitely a GPR.
/// This is typically only used for named registers such as $gp.
static std::unique_ptr<MipsOperand>
createGPRReg(unsigned Index, StringRef Str, const MCRegisterInfo *RegInfo,
SMLoc S, SMLoc E, MipsAsmParser &Parser) {
return CreateReg(Index, Str, RegKind_GPR, RegInfo, S, E, Parser);
}
/// Create a register that is definitely a FGR.
/// This is typically only used for named registers such as $f0.
static std::unique_ptr<MipsOperand>
createFGRReg(unsigned Index, StringRef Str, const MCRegisterInfo *RegInfo,
SMLoc S, SMLoc E, MipsAsmParser &Parser) {
return CreateReg(Index, Str, RegKind_FGR, RegInfo, S, E, Parser);
}
/// Create a register that is definitely a HWReg.
/// This is typically only used for named registers such as $hwr_cpunum.
static std::unique_ptr<MipsOperand>
createHWRegsReg(unsigned Index, StringRef Str, const MCRegisterInfo *RegInfo,
SMLoc S, SMLoc E, MipsAsmParser &Parser) {
return CreateReg(Index, Str, RegKind_HWRegs, RegInfo, S, E, Parser);
}
/// Create a register that is definitely an FCC.
/// This is typically only used for named registers such as $fcc0.
static std::unique_ptr<MipsOperand>
createFCCReg(unsigned Index, StringRef Str, const MCRegisterInfo *RegInfo,
SMLoc S, SMLoc E, MipsAsmParser &Parser) {
return CreateReg(Index, Str, RegKind_FCC, RegInfo, S, E, Parser);
}
/// Create a register that is definitely an ACC.
/// This is typically only used for named registers such as $ac0.
static std::unique_ptr<MipsOperand>
createACCReg(unsigned Index, StringRef Str, const MCRegisterInfo *RegInfo,
SMLoc S, SMLoc E, MipsAsmParser &Parser) {
return CreateReg(Index, Str, RegKind_ACC, RegInfo, S, E, Parser);
}
/// Create a register that is definitely an MSA128.
/// This is typically only used for named registers such as $w0.
static std::unique_ptr<MipsOperand>
createMSA128Reg(unsigned Index, StringRef Str, const MCRegisterInfo *RegInfo,
SMLoc S, SMLoc E, MipsAsmParser &Parser) {
return CreateReg(Index, Str, RegKind_MSA128, RegInfo, S, E, Parser);
}
/// Create a register that is definitely an MSACtrl.
/// This is typically only used for named registers such as $msaaccess.
static std::unique_ptr<MipsOperand>
createMSACtrlReg(unsigned Index, StringRef Str, const MCRegisterInfo *RegInfo,
SMLoc S, SMLoc E, MipsAsmParser &Parser) {
return CreateReg(Index, Str, RegKind_MSACtrl, RegInfo, S, E, Parser);
}
static std::unique_ptr<MipsOperand>
CreateImm(const MCExpr *Val, SMLoc S, SMLoc E, MipsAsmParser &Parser) {
auto Op = std::make_unique<MipsOperand>(k_Immediate, Parser);
Op->Imm.Val = Val;
Op->StartLoc = S;
Op->EndLoc = E;
return Op;
}
static std::unique_ptr<MipsOperand>
CreateMem(std::unique_ptr<MipsOperand> Base, const MCExpr *Off, SMLoc S,
SMLoc E, MipsAsmParser &Parser) {
auto Op = std::make_unique<MipsOperand>(k_Memory, Parser);
Op->Mem.Base = Base.release();
Op->Mem.Off = Off;
Op->StartLoc = S;
Op->EndLoc = E;
return Op;
}
static std::unique_ptr<MipsOperand>
CreateRegList(SmallVectorImpl<unsigned> &Regs, SMLoc StartLoc, SMLoc EndLoc,
MipsAsmParser &Parser) {
assert(Regs.size() > 0 && "Empty list not allowed");
auto Op = std::make_unique<MipsOperand>(k_RegList, Parser);
Op->RegList.List = new SmallVector<unsigned, 10>(Regs.begin(), Regs.end());
Op->StartLoc = StartLoc;
Op->EndLoc = EndLoc;
return Op;
}
bool isGPRZeroAsmReg() const {
return isRegIdx() && RegIdx.Kind & RegKind_GPR && RegIdx.Index == 0;
}
bool isGPRNonZeroAsmReg() const {
return isRegIdx() && RegIdx.Kind & RegKind_GPR && RegIdx.Index > 0 &&
RegIdx.Index <= 31;
}
bool isGPRAsmReg() const {
return isRegIdx() && RegIdx.Kind & RegKind_GPR && RegIdx.Index <= 31;
}
bool isMM16AsmReg() const {
if (!(isRegIdx() && RegIdx.Kind))
return false;
return ((RegIdx.Index >= 2 && RegIdx.Index <= 7)
|| RegIdx.Index == 16 || RegIdx.Index == 17);
}
bool isMM16AsmRegZero() const {
if (!(isRegIdx() && RegIdx.Kind))
return false;
return (RegIdx.Index == 0 ||
(RegIdx.Index >= 2 && RegIdx.Index <= 7) ||
RegIdx.Index == 17);
}
bool isMM16AsmRegMoveP() const {
if (!(isRegIdx() && RegIdx.Kind))
return false;
return (RegIdx.Index == 0 || (RegIdx.Index >= 2 && RegIdx.Index <= 3) ||
(RegIdx.Index >= 16 && RegIdx.Index <= 20));
}
bool isMM16AsmRegMovePPairFirst() const {
if (!(isRegIdx() && RegIdx.Kind))
return false;
return RegIdx.Index >= 4 && RegIdx.Index <= 6;
}
bool isMM16AsmRegMovePPairSecond() const {
if (!(isRegIdx() && RegIdx.Kind))
return false;
return (RegIdx.Index == 21 || RegIdx.Index == 22 ||
(RegIdx.Index >= 5 && RegIdx.Index <= 7));
}
bool isFGRAsmReg() const {
// AFGR64 is $0-$15 but we handle this in getAFGR64()
return isRegIdx() && RegIdx.Kind & RegKind_FGR && RegIdx.Index <= 31;
}
bool isStrictlyFGRAsmReg() const {
// AFGR64 is $0-$15 but we handle this in getAFGR64()
return isRegIdx() && RegIdx.Kind == RegKind_FGR && RegIdx.Index <= 31;
}
bool isHWRegsAsmReg() const {
return isRegIdx() && RegIdx.Kind & RegKind_HWRegs && RegIdx.Index <= 31;
}
bool isCCRAsmReg() const {
return isRegIdx() && RegIdx.Kind & RegKind_CCR && RegIdx.Index <= 31;
}
bool isFCCAsmReg() const {
if (!(isRegIdx() && RegIdx.Kind & RegKind_FCC))
return false;
return RegIdx.Index <= 7;
}
bool isACCAsmReg() const {
return isRegIdx() && RegIdx.Kind & RegKind_ACC && RegIdx.Index <= 3;
}
bool isCOP0AsmReg() const {
return isRegIdx() && RegIdx.Kind & RegKind_COP0 && RegIdx.Index <= 31;
}
bool isCOP2AsmReg() const {
return isRegIdx() && RegIdx.Kind & RegKind_COP2 && RegIdx.Index <= 31;
}
bool isCOP3AsmReg() const {
return isRegIdx() && RegIdx.Kind & RegKind_COP3 && RegIdx.Index <= 31;
}
bool isMSA128AsmReg() const {
return isRegIdx() && RegIdx.Kind & RegKind_MSA128 && RegIdx.Index <= 31;
}
bool isMSACtrlAsmReg() const {
return isRegIdx() && RegIdx.Kind & RegKind_MSACtrl && RegIdx.Index <= 7;
}
/// getStartLoc - Get the location of the first token of this operand.
SMLoc getStartLoc() const override { return StartLoc; }
/// getEndLoc - Get the location of the last token of this operand.
SMLoc getEndLoc() const override { return EndLoc; }
void print(raw_ostream &OS) const override {
switch (Kind) {
case k_Immediate:
OS << "Imm<";
OS << *Imm.Val;
OS << ">";
break;
case k_Memory:
OS << "Mem<";
Mem.Base->print(OS);
OS << ", ";
OS << *Mem.Off;
OS << ">";
break;
case k_RegisterIndex:
OS << "RegIdx<" << RegIdx.Index << ":" << RegIdx.Kind << ", "
<< StringRef(RegIdx.Tok.Data, RegIdx.Tok.Length) << ">";
break;
case k_Token:
OS << getToken();
break;
case k_RegList:
OS << "RegList< ";
for (auto Reg : (*RegList.List))
OS << Reg << " ";
OS << ">";
break;
}
}
bool isValidForTie(const MipsOperand &Other) const {
if (Kind != Other.Kind)
return false;
switch (Kind) {
default:
llvm_unreachable("Unexpected kind");
return false;
case k_RegisterIndex: {
StringRef Token(RegIdx.Tok.Data, RegIdx.Tok.Length);
StringRef OtherToken(Other.RegIdx.Tok.Data, Other.RegIdx.Tok.Length);
return Token == OtherToken;
}
}
}
}; // class MipsOperand
} // end anonymous namespace
static bool hasShortDelaySlot(MCInst &Inst) {
switch (Inst.getOpcode()) {
case Mips::BEQ_MM:
case Mips::BNE_MM:
case Mips::BLTZ_MM:
case Mips::BGEZ_MM:
case Mips::BLEZ_MM:
case Mips::BGTZ_MM:
case Mips::JRC16_MM:
case Mips::JALS_MM:
case Mips::JALRS_MM:
case Mips::JALRS16_MM:
case Mips::BGEZALS_MM:
case Mips::BLTZALS_MM:
return true;
case Mips::J_MM:
return !Inst.getOperand(0).isReg();
default:
return false;
}
}
static const MCSymbol *getSingleMCSymbol(const MCExpr *Expr) {
if (const MCSymbolRefExpr *SRExpr = dyn_cast<MCSymbolRefExpr>(Expr)) {
return &SRExpr->getSymbol();
}
if (const MCBinaryExpr *BExpr = dyn_cast<MCBinaryExpr>(Expr)) {
const MCSymbol *LHSSym = getSingleMCSymbol(BExpr->getLHS());
const MCSymbol *RHSSym = getSingleMCSymbol(BExpr->getRHS());
if (LHSSym)
return LHSSym;
if (RHSSym)
return RHSSym;
return nullptr;
}
if (const MCUnaryExpr *UExpr = dyn_cast<MCUnaryExpr>(Expr))
return getSingleMCSymbol(UExpr->getSubExpr());
return nullptr;
}
static unsigned countMCSymbolRefExpr(const MCExpr *Expr) {
if (isa<MCSymbolRefExpr>(Expr))
return 1;
if (const MCBinaryExpr *BExpr = dyn_cast<MCBinaryExpr>(Expr))
return countMCSymbolRefExpr(BExpr->getLHS()) +
countMCSymbolRefExpr(BExpr->getRHS());
if (const MCUnaryExpr *UExpr = dyn_cast<MCUnaryExpr>(Expr))
return countMCSymbolRefExpr(UExpr->getSubExpr());
return 0;
}
static bool isEvaluated(const MCExpr *Expr) {
switch (Expr->getKind()) {
case MCExpr::Constant:
return true;
case MCExpr::SymbolRef:
return (cast<MCSymbolRefExpr>(Expr)->getKind() != MCSymbolRefExpr::VK_None);
case MCExpr::Binary: {
const MCBinaryExpr *BE = cast<MCBinaryExpr>(Expr);
if (!isEvaluated(BE->getLHS()))
return false;
return isEvaluated(BE->getRHS());
}
case MCExpr::Unary:
return isEvaluated(cast<MCUnaryExpr>(Expr)->getSubExpr());
case MCExpr::Target:
return true;
}
return false;
}
static bool needsExpandMemInst(MCInst &Inst, const MCInstrDesc &MCID) {
unsigned NumOp = MCID.getNumOperands();
if (NumOp != 3 && NumOp != 4)
return false;
const MCOperandInfo &OpInfo = MCID.operands()[NumOp - 1];
if (OpInfo.OperandType != MCOI::OPERAND_MEMORY &&
OpInfo.OperandType != MCOI::OPERAND_UNKNOWN &&
OpInfo.OperandType != MipsII::OPERAND_MEM_SIMM9)
return false;
MCOperand &Op = Inst.getOperand(NumOp - 1);
if (Op.isImm()) {
if (OpInfo.OperandType == MipsII::OPERAND_MEM_SIMM9)
return !isInt<9>(Op.getImm());
// Offset can't exceed 16bit value.
return !isInt<16>(Op.getImm());
}
if (Op.isExpr()) {
const MCExpr *Expr = Op.getExpr();
if (Expr->getKind() != MCExpr::SymbolRef)
return !isEvaluated(Expr);
// Expand symbol.
const MCSymbolRefExpr *SR = static_cast<const MCSymbolRefExpr *>(Expr);
return SR->getKind() == MCSymbolRefExpr::VK_None;
}
return false;
}
bool MipsAsmParser::processInstruction(MCInst &Inst, SMLoc IDLoc,
MCStreamer &Out,
const MCSubtargetInfo *STI) {
MipsTargetStreamer &TOut = getTargetStreamer();
const unsigned Opcode = Inst.getOpcode();
const MCInstrDesc &MCID = MII.get(Opcode);
bool ExpandedJalSym = false;
Inst.setLoc(IDLoc);
if (MCID.isBranch() || MCID.isCall()) {
MCOperand Offset;
switch (Opcode) {
default:
break;
case Mips::BBIT0:
case Mips::BBIT032:
case Mips::BBIT1:
case Mips::BBIT132:
assert(hasCnMips() && "instruction only valid for octeon cpus");
[[fallthrough]];
case Mips::BEQ:
case Mips::BNE:
case Mips::BEQ_MM:
case Mips::BNE_MM:
assert(MCID.getNumOperands() == 3 && "unexpected number of operands");
Offset = Inst.getOperand(2);
if (!Offset.isImm())
break; // We'll deal with this situation later on when applying fixups.
if (!isIntN(inMicroMipsMode() ? 17 : 18, Offset.getImm()))
return Error(IDLoc, "branch target out of range");
if (offsetToAlignment(Offset.getImm(),
(inMicroMipsMode() ? Align(2) : Align(4))))
return Error(IDLoc, "branch to misaligned address");
break;
case Mips::BGEZ:
case Mips::BGTZ:
case Mips::BLEZ:
case Mips::BLTZ:
case Mips::BGEZAL:
case Mips::BLTZAL:
case Mips::BC1F:
case Mips::BC1T:
case Mips::BGEZ_MM:
case Mips::BGTZ_MM:
case Mips::BLEZ_MM:
case Mips::BLTZ_MM:
case Mips::BGEZAL_MM:
case Mips::BLTZAL_MM:
case Mips::BC1F_MM:
case Mips::BC1T_MM:
case Mips::BC1EQZC_MMR6:
case Mips::BC1NEZC_MMR6:
case Mips::BC2EQZC_MMR6:
case Mips::BC2NEZC_MMR6:
assert(MCID.getNumOperands() == 2 && "unexpected number of operands");
Offset = Inst.getOperand(1);
if (!Offset.isImm())
break; // We'll deal with this situation later on when applying fixups.
if (!isIntN(inMicroMipsMode() ? 17 : 18, Offset.getImm()))
return Error(IDLoc, "branch target out of range");
if (offsetToAlignment(Offset.getImm(),
(inMicroMipsMode() ? Align(2) : Align(4))))
return Error(IDLoc, "branch to misaligned address");
break;
case Mips::BGEC: case Mips::BGEC_MMR6:
case Mips::BLTC: case Mips::BLTC_MMR6:
case Mips::BGEUC: case Mips::BGEUC_MMR6:
case Mips::BLTUC: case Mips::BLTUC_MMR6:
case Mips::BEQC: case Mips::BEQC_MMR6:
case Mips::BNEC: case Mips::BNEC_MMR6:
assert(MCID.getNumOperands() == 3 && "unexpected number of operands");
Offset = Inst.getOperand(2);
if (!Offset.isImm())
break; // We'll deal with this situation later on when applying fixups.
if (!isIntN(18, Offset.getImm()))
return Error(IDLoc, "branch target out of range");
if (offsetToAlignment(Offset.getImm(), Align(4)))
return Error(IDLoc, "branch to misaligned address");
break;
case Mips::BLEZC: case Mips::BLEZC_MMR6:
case Mips::BGEZC: case Mips::BGEZC_MMR6:
case Mips::BGTZC: case Mips::BGTZC_MMR6:
case Mips::BLTZC: case Mips::BLTZC_MMR6:
assert(MCID.getNumOperands() == 2 && "unexpected number of operands");
Offset = Inst.getOperand(1);
if (!Offset.isImm())
break; // We'll deal with this situation later on when applying fixups.
if (!isIntN(18, Offset.getImm()))
return Error(IDLoc, "branch target out of range");
if (offsetToAlignment(Offset.getImm(), Align(4)))
return Error(IDLoc, "branch to misaligned address");
break;
case Mips::BEQZC: case Mips::BEQZC_MMR6:
case Mips::BNEZC: case Mips::BNEZC_MMR6:
assert(MCID.getNumOperands() == 2 && "unexpected number of operands");
Offset = Inst.getOperand(1);
if (!Offset.isImm())
break; // We'll deal with this situation later on when applying fixups.
if (!isIntN(23, Offset.getImm()))
return Error(IDLoc, "branch target out of range");
if (offsetToAlignment(Offset.getImm(), Align(4)))
return Error(IDLoc, "branch to misaligned address");
break;
case Mips::BEQZ16_MM:
case Mips::BEQZC16_MMR6:
case Mips::BNEZ16_MM:
case Mips::BNEZC16_MMR6:
assert(MCID.getNumOperands() == 2 && "unexpected number of operands");
Offset = Inst.getOperand(1);
if (!Offset.isImm())
break; // We'll deal with this situation later on when applying fixups.
if (!isInt<8>(Offset.getImm()))
return Error(IDLoc, "branch target out of range");
if (offsetToAlignment(Offset.getImm(), Align(2)))
return Error(IDLoc, "branch to misaligned address");
break;
}
}
// SSNOP is deprecated on MIPS32r6/MIPS64r6
// We still accept it but it is a normal nop.
if (hasMips32r6() && Opcode == Mips::SSNOP) {
std::string ISA = hasMips64r6() ? "MIPS64r6" : "MIPS32r6";
Warning(IDLoc, "ssnop is deprecated for " + ISA + " and is equivalent to a "
"nop instruction");
}
if (hasCnMips()) {
MCOperand Opnd;
int Imm;
switch (Opcode) {
default:
break;
case Mips::BBIT0:
case Mips::BBIT032:
case Mips::BBIT1:
case Mips::BBIT132:
assert(MCID.getNumOperands() == 3 && "unexpected number of operands");
// The offset is handled above
Opnd = Inst.getOperand(1);
if (!Opnd.isImm())
return Error(IDLoc, "expected immediate operand kind");
Imm = Opnd.getImm();
if (Imm < 0 || Imm > (Opcode == Mips::BBIT0 ||
Opcode == Mips::BBIT1 ? 63 : 31))
return Error(IDLoc, "immediate operand value out of range");
if (Imm > 31) {
Inst.setOpcode(Opcode == Mips::BBIT0 ? Mips::BBIT032
: Mips::BBIT132);
Inst.getOperand(1).setImm(Imm - 32);
}
break;
case Mips::SEQi:
case Mips::SNEi:
assert(MCID.getNumOperands() == 3 && "unexpected number of operands");
Opnd = Inst.getOperand(2);
if (!Opnd.isImm())
return Error(IDLoc, "expected immediate operand kind");
Imm = Opnd.getImm();
if (!isInt<10>(Imm))
return Error(IDLoc, "immediate operand value out of range");
break;
}
}
// Warn on division by zero. We're checking here as all instructions get
// processed here, not just the macros that need expansion.
//
// The MIPS backend models most of the divison instructions and macros as
// three operand instructions. The pre-R6 divide instructions however have
// two operands and explicitly define HI/LO as part of the instruction,
// not in the operands.
unsigned FirstOp = 1;
unsigned SecondOp = 2;
switch (Opcode) {
default:
break;
case Mips::SDivIMacro:
case Mips::UDivIMacro:
case Mips::DSDivIMacro:
case Mips::DUDivIMacro:
if (Inst.getOperand(2).getImm() == 0) {
if (Inst.getOperand(1).getReg() == Mips::ZERO ||
Inst.getOperand(1).getReg() == Mips::ZERO_64)
Warning(IDLoc, "dividing zero by zero");
else
Warning(IDLoc, "division by zero");
}
break;
case Mips::DSDIV:
case Mips::SDIV:
case Mips::UDIV:
case Mips::DUDIV:
case Mips::UDIV_MM:
case Mips::SDIV_MM:
FirstOp = 0;
SecondOp = 1;
[[fallthrough]];
case Mips::SDivMacro:
case Mips::DSDivMacro:
case Mips::UDivMacro:
case Mips::DUDivMacro:
case Mips::DIV:
case Mips::DIVU:
case Mips::DDIV:
case Mips::DDIVU:
case Mips::DIVU_MMR6:
case Mips::DIV_MMR6:
if (Inst.getOperand(SecondOp).getReg() == Mips::ZERO ||
Inst.getOperand(SecondOp).getReg() == Mips::ZERO_64) {
if (Inst.getOperand(FirstOp).getReg() == Mips::ZERO ||
Inst.getOperand(FirstOp).getReg() == Mips::ZERO_64)
Warning(IDLoc, "dividing zero by zero");
else
Warning(IDLoc, "division by zero");
}
break;
}
// For PIC code convert unconditional jump to unconditional branch.
if ((Opcode == Mips::J || Opcode == Mips::J_MM) && inPicMode()) {
MCInst BInst;
BInst.setOpcode(inMicroMipsMode() ? Mips::BEQ_MM : Mips::BEQ);
BInst.addOperand(MCOperand::createReg(Mips::ZERO));
BInst.addOperand(MCOperand::createReg(Mips::ZERO));
BInst.addOperand(Inst.getOperand(0));
Inst = BInst;
}
// This expansion is not in a function called by tryExpandInstruction()
// because the pseudo-instruction doesn't have a distinct opcode.
if ((Opcode == Mips::JAL || Opcode == Mips::JAL_MM) && inPicMode()) {
warnIfNoMacro(IDLoc);
const MCExpr *JalExpr = Inst.getOperand(0).getExpr();
// We can do this expansion if there's only 1 symbol in the argument
// expression.
if (countMCSymbolRefExpr(JalExpr) > 1)
return Error(IDLoc, "jal doesn't support multiple symbols in PIC mode");
// FIXME: This is checking the expression can be handled by the later stages
// of the assembler. We ought to leave it to those later stages.
const MCSymbol *JalSym = getSingleMCSymbol(JalExpr);
if (expandLoadAddress(Mips::T9, Mips::NoRegister, Inst.getOperand(0),
!isGP64bit(), IDLoc, Out, STI))
return true;
MCInst JalrInst;
if (inMicroMipsMode())
JalrInst.setOpcode(IsCpRestoreSet ? Mips::JALRS_MM : Mips::JALR_MM);
else
JalrInst.setOpcode(Mips::JALR);
JalrInst.addOperand(MCOperand::createReg(Mips::RA));
JalrInst.addOperand(MCOperand::createReg(Mips::T9));
if (isJalrRelocAvailable(JalExpr)) {
// As an optimization hint for the linker, before the JALR we add:
// .reloc tmplabel, R_{MICRO}MIPS_JALR, symbol
// tmplabel:
MCSymbol *TmpLabel = getContext().createTempSymbol();
const MCExpr *TmpExpr = MCSymbolRefExpr::create(TmpLabel, getContext());
const MCExpr *RelocJalrExpr =
MCSymbolRefExpr::create(JalSym, MCSymbolRefExpr::VK_None,
getContext(), IDLoc);
TOut.getStreamer().emitRelocDirective(
*TmpExpr, inMicroMipsMode() ? "R_MICROMIPS_JALR" : "R_MIPS_JALR",
RelocJalrExpr, IDLoc, *STI);
TOut.getStreamer().emitLabel(TmpLabel);
}
Inst = JalrInst;
ExpandedJalSym = true;
}
if (MCID.mayLoad() || MCID.mayStore()) {
// Check the offset of memory operand, if it is a symbol
// reference or immediate we may have to expand instructions.
if (needsExpandMemInst(Inst, MCID)) {
switch (MCID.operands()[MCID.getNumOperands() - 1].OperandType) {
case MipsII::OPERAND_MEM_SIMM9:
expandMem9Inst(Inst, IDLoc, Out, STI, MCID.mayLoad());
break;
default:
expandMem16Inst(Inst, IDLoc, Out, STI, MCID.mayLoad());
break;
}
return getParser().hasPendingError();
}
}
if (inMicroMipsMode()) {
if (MCID.mayLoad() && Opcode != Mips::LWP_MM) {
// Try to create 16-bit GP relative load instruction.
for (unsigned i = 0; i < MCID.getNumOperands(); i++) {
const MCOperandInfo &OpInfo = MCID.operands()[i];
if ((OpInfo.OperandType == MCOI::OPERAND_MEMORY) ||
(OpInfo.OperandType == MCOI::OPERAND_UNKNOWN)) {
MCOperand &Op = Inst.getOperand(i);
if (Op.isImm()) {
int MemOffset = Op.getImm();
MCOperand &DstReg = Inst.getOperand(0);
MCOperand &BaseReg = Inst.getOperand(1);
if (isInt<9>(MemOffset) && (MemOffset % 4 == 0) &&
getContext().getRegisterInfo()->getRegClass(
Mips::GPRMM16RegClassID).contains(DstReg.getReg()) &&
(BaseReg.getReg() == Mips::GP ||
BaseReg.getReg() == Mips::GP_64)) {
TOut.emitRRI(Mips::LWGP_MM, DstReg.getReg(), Mips::GP, MemOffset,
IDLoc, STI);
return false;
}
}
}
} // for
} // if load
// TODO: Handle this with the AsmOperandClass.PredicateMethod.
MCOperand Opnd;
int Imm;
switch (Opcode) {
default:
break;
case Mips::ADDIUSP_MM:
Opnd = Inst.getOperand(0);
if (!Opnd.isImm())
return Error(IDLoc, "expected immediate operand kind");
Imm = Opnd.getImm();
if (Imm < -1032 || Imm > 1028 || (Imm < 8 && Imm > -12) ||
Imm % 4 != 0)
return Error(IDLoc, "immediate operand value out of range");
break;
case Mips::SLL16_MM:
case Mips::SRL16_MM:
Opnd = Inst.getOperand(2);
if (!Opnd.isImm())
return Error(IDLoc, "expected immediate operand kind");
Imm = Opnd.getImm();
if (Imm < 1 || Imm > 8)
return Error(IDLoc, "immediate operand value out of range");
break;
case Mips::LI16_MM:
Opnd = Inst.getOperand(1);
if (!Opnd.isImm())
return Error(IDLoc, "expected immediate operand kind");
Imm = Opnd.getImm();
if (Imm < -1 || Imm > 126)
return Error(IDLoc, "immediate operand value out of range");
break;
case Mips::ADDIUR2_MM:
Opnd = Inst.getOperand(2);
if (!Opnd.isImm())
return Error(IDLoc, "expected immediate operand kind");
Imm = Opnd.getImm();
if (!(Imm == 1 || Imm == -1 ||
((Imm % 4 == 0) && Imm < 28 && Imm > 0)))
return Error(IDLoc, "immediate operand value out of range");
break;
case Mips::ANDI16_MM:
Opnd = Inst.getOperand(2);
if (!Opnd.isImm())
return Error(IDLoc, "expected immediate operand kind");
Imm = Opnd.getImm();
if (!(Imm == 128 || (Imm >= 1 && Imm <= 4) || Imm == 7 || Imm == 8 ||
Imm == 15 || Imm == 16 || Imm == 31 || Imm == 32 || Imm == 63 ||
Imm == 64 || Imm == 255 || Imm == 32768 || Imm == 65535))
return Error(IDLoc, "immediate operand value out of range");
break;
case Mips::LBU16_MM:
Opnd = Inst.getOperand(2);
if (!Opnd.isImm())
return Error(IDLoc, "expected immediate operand kind");
Imm = Opnd.getImm();
if (Imm < -1 || Imm > 14)
return Error(IDLoc, "immediate operand value out of range");
break;
case Mips::SB16_MM:
case Mips::SB16_MMR6:
Opnd = Inst.getOperand(2);
if (!Opnd.isImm())
return Error(IDLoc, "expected immediate operand kind");
Imm = Opnd.getImm();
if (Imm < 0 || Imm > 15)
return Error(IDLoc, "immediate operand value out of range");
break;
case Mips::LHU16_MM:
case Mips::SH16_MM:
case Mips::SH16_MMR6:
Opnd = Inst.getOperand(2);
if (!Opnd.isImm())
return Error(IDLoc, "expected immediate operand kind");
Imm = Opnd.getImm();
if (Imm < 0 || Imm > 30 || (Imm % 2 != 0))
return Error(IDLoc, "immediate operand value out of range");
break;
case Mips::LW16_MM:
case Mips::SW16_MM:
case Mips::SW16_MMR6:
Opnd = Inst.getOperand(2);
if (!Opnd.isImm())
return Error(IDLoc, "expected immediate operand kind");
Imm = Opnd.getImm();
if (Imm < 0 || Imm > 60 || (Imm % 4 != 0))
return Error(IDLoc, "immediate operand value out of range");
break;
case Mips::ADDIUPC_MM:
Opnd = Inst.getOperand(1);
if (!Opnd.isImm())
return Error(IDLoc, "expected immediate operand kind");
Imm = Opnd.getImm();
if ((Imm % 4 != 0) || !isInt<25>(Imm))
return Error(IDLoc, "immediate operand value out of range");
break;
case Mips::LWP_MM:
case Mips::SWP_MM:
if (Inst.getOperand(0).getReg() == Mips::RA)
return Error(IDLoc, "invalid operand for instruction");
break;
case Mips::MOVEP_MM:
case Mips::MOVEP_MMR6: {
unsigned R0 = Inst.getOperand(0).getReg();
unsigned R1 = Inst.getOperand(1).getReg();
bool RegPair = ((R0 == Mips::A1 && R1 == Mips::A2) ||
(R0 == Mips::A1 && R1 == Mips::A3) ||
(R0 == Mips::A2 && R1 == Mips::A3) ||
(R0 == Mips::A0 && R1 == Mips::S5) ||
(R0 == Mips::A0 && R1 == Mips::S6) ||
(R0 == Mips::A0 && R1 == Mips::A1) ||
(R0 == Mips::A0 && R1 == Mips::A2) ||
(R0 == Mips::A0 && R1 == Mips::A3));
if (!RegPair)
return Error(IDLoc, "invalid operand for instruction");
break;
}
}
}
bool FillDelaySlot =
MCID.hasDelaySlot() && AssemblerOptions.back()->isReorder();
// Get previous instruction`s forbidden slot attribute and
// whether set reorder.
bool PrevForbiddenSlotAttr = CurForbiddenSlotAttr;
// Flag represents we set reorder after nop.
bool SetReorderAfterNop = false;
// If previous instruction has forbidden slot and .set reorder
// is active and current instruction is CTI.
// Then emit a NOP after it.
if (PrevForbiddenSlotAttr && !SafeInForbiddenSlot(MCID)) {
TOut.emitEmptyDelaySlot(false, IDLoc, STI);
// When 'FillDelaySlot' is true, the existing logic will add
// noreorder before instruction and reorder after it. So there
// need exclude this case avoiding two '.set reorder'.
// The format of the first case is:
// .set noreorder
// bnezc
// nop
// .set reorder
if (AssemblerOptions.back()->isReorder() && !FillDelaySlot) {
SetReorderAfterNop = true;
TOut.emitDirectiveSetReorder();
}
}
// Save current instruction`s forbidden slot and whether set reorder.
// This is the judgment condition for whether to add nop.
// We would add a couple of '.set noreorder' and '.set reorder' to
// wrap the current instruction and the next instruction.
CurForbiddenSlotAttr =
hasForbiddenSlot(MCID) && AssemblerOptions.back()->isReorder();
if (FillDelaySlot || CurForbiddenSlotAttr)
TOut.emitDirectiveSetNoReorder();
MacroExpanderResultTy ExpandResult =
tryExpandInstruction(Inst, IDLoc, Out, STI);
switch (ExpandResult) {
case MER_NotAMacro:
Out.emitInstruction(Inst, *STI);
break;
case MER_Success:
break;
case MER_Fail:
return true;
}
// When current instruction was not CTI, recover reorder state.
// The format of the second case is:
// .set noreoder
// bnezc
// add
// .set reorder
if (PrevForbiddenSlotAttr && !SetReorderAfterNop && !FillDelaySlot &&
AssemblerOptions.back()->isReorder()) {
TOut.emitDirectiveSetReorder();
}
// We know we emitted an instruction on the MER_NotAMacro or MER_Success path.
// If we're in microMIPS mode then we must also set EF_MIPS_MICROMIPS.
if (inMicroMipsMode()) {
TOut.setUsesMicroMips();
TOut.updateABIInfo(*this);
}
// If this instruction has a delay slot and .set reorder is active,
// emit a NOP after it.
// The format of the third case is:
// .set noreorder
// bnezc
// nop
// .set noreorder
// j
// nop
// .set reorder
if (FillDelaySlot) {
TOut.emitEmptyDelaySlot(hasShortDelaySlot(Inst), IDLoc, STI);
TOut.emitDirectiveSetReorder();
}
if ((Opcode == Mips::JalOneReg || Opcode == Mips::JalTwoReg ||
ExpandedJalSym) &&
isPicAndNotNxxAbi()) {
if (IsCpRestoreSet) {
// We need a NOP between the JALR and the LW:
// If .set reorder has been used, we've already emitted a NOP.
// If .set noreorder has been used, we need to emit a NOP at this point.
if (!AssemblerOptions.back()->isReorder())
TOut.emitEmptyDelaySlot(hasShortDelaySlot(Inst), IDLoc,
STI);
// Load the $gp from the stack.
TOut.emitGPRestore(CpRestoreOffset, IDLoc, STI);
} else
Warning(IDLoc, "no .cprestore used in PIC mode");
}
return false;
}
void MipsAsmParser::onEndOfFile() {
MipsTargetStreamer &TOut = getTargetStreamer();
SMLoc IDLoc = SMLoc();
// If has pending forbidden slot, fill nop and recover reorder.
if (CurForbiddenSlotAttr) {
TOut.emitEmptyDelaySlot(false, IDLoc, STI);
if (AssemblerOptions.back()->isReorder())
TOut.emitDirectiveSetReorder();
}
}
MipsAsmParser::MacroExpanderResultTy
MipsAsmParser::tryExpandInstruction(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out,
const MCSubtargetInfo *STI) {
switch (Inst.getOpcode()) {
default:
return MER_NotAMacro;
case Mips::LoadImm32:
return expandLoadImm(Inst, true, IDLoc, Out, STI) ? MER_Fail : MER_Success;
case Mips::LoadImm64:
return expandLoadImm(Inst, false, IDLoc, Out, STI) ? MER_Fail : MER_Success;
case Mips::LoadAddrImm32:
case Mips::LoadAddrImm64:
assert(Inst.getOperand(0).isReg() && "expected register operand kind");
assert((Inst.getOperand(1).isImm() || Inst.getOperand(1).isExpr()) &&
"expected immediate operand kind");
return expandLoadAddress(Inst.getOperand(0).getReg(), Mips::NoRegister,
Inst.getOperand(1),
Inst.getOpcode() == Mips::LoadAddrImm32, IDLoc,
Out, STI)
? MER_Fail
: MER_Success;
case Mips::LoadAddrReg32:
case Mips::LoadAddrReg64:
assert(Inst.getOperand(0).isReg() && "expected register operand kind");
assert(Inst.getOperand(1).isReg() && "expected register operand kind");
assert((Inst.getOperand(2).isImm() || Inst.getOperand(2).isExpr()) &&
"expected immediate operand kind");
return expandLoadAddress(Inst.getOperand(0).getReg(),
Inst.getOperand(1).getReg(), Inst.getOperand(2),
Inst.getOpcode() == Mips::LoadAddrReg32, IDLoc,
Out, STI)
? MER_Fail
: MER_Success;
case Mips::B_MM_Pseudo:
case Mips::B_MMR6_Pseudo:
return expandUncondBranchMMPseudo(Inst, IDLoc, Out, STI) ? MER_Fail
: MER_Success;
case Mips::SWM_MM:
case Mips::LWM_MM:
return expandLoadStoreMultiple(Inst, IDLoc, Out, STI) ? MER_Fail
: MER_Success;
case Mips::JalOneReg:
case Mips::JalTwoReg:
return expandJalWithRegs(Inst, IDLoc, Out, STI) ? MER_Fail : MER_Success;
case Mips::BneImm:
case Mips::BeqImm:
case Mips::BEQLImmMacro:
case Mips::BNELImmMacro:
return expandBranchImm(Inst, IDLoc, Out, STI) ? MER_Fail : MER_Success;
case Mips::BLT:
case Mips::BLE:
case Mips::BGE:
case Mips::BGT:
case Mips::BLTU:
case Mips::BLEU:
case Mips::BGEU:
case Mips::BGTU:
case Mips::BLTL:
case Mips::BLEL:
case Mips::BGEL:
case Mips::BGTL:
case Mips::BLTUL:
case Mips::BLEUL:
case Mips::BGEUL:
case Mips::BGTUL:
case Mips::BLTImmMacro:
case Mips::BLEImmMacro:
case Mips::BGEImmMacro:
case Mips::BGTImmMacro:
case Mips::BLTUImmMacro:
case Mips::BLEUImmMacro:
case Mips::BGEUImmMacro:
case Mips::BGTUImmMacro:
case Mips::BLTLImmMacro:
case Mips::BLELImmMacro:
case Mips::BGELImmMacro:
case Mips::BGTLImmMacro:
case Mips::BLTULImmMacro:
case Mips::BLEULImmMacro:
case Mips::BGEULImmMacro:
case Mips::BGTULImmMacro:
return expandCondBranches(Inst, IDLoc, Out, STI) ? MER_Fail : MER_Success;
case Mips::SDivMacro:
case Mips::SDivIMacro:
case Mips::SRemMacro:
case Mips::SRemIMacro:
return expandDivRem(Inst, IDLoc, Out, STI, false, true) ? MER_Fail
: MER_Success;
case Mips::DSDivMacro:
case Mips::DSDivIMacro:
case Mips::DSRemMacro:
case Mips::DSRemIMacro:
return expandDivRem(Inst, IDLoc, Out, STI, true, true) ? MER_Fail
: MER_Success;
case Mips::UDivMacro:
case Mips::UDivIMacro:
case Mips::URemMacro:
case Mips::URemIMacro:
return expandDivRem(Inst, IDLoc, Out, STI, false, false) ? MER_Fail
: MER_Success;
case Mips::DUDivMacro:
case Mips::DUDivIMacro:
case Mips::DURemMacro:
case Mips::DURemIMacro:
return expandDivRem(Inst, IDLoc, Out, STI, true, false) ? MER_Fail
: MER_Success;
case Mips::PseudoTRUNC_W_S:
return expandTrunc(Inst, false, false, IDLoc, Out, STI) ? MER_Fail
: MER_Success;
case Mips::PseudoTRUNC_W_D32:
return expandTrunc(Inst, true, false, IDLoc, Out, STI) ? MER_Fail
: MER_Success;
case