| //===-- 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 |