Index: head/contrib/llvm/lib/Target/Mips/AsmParser/MipsAsmParser.cpp =================================================================== --- head/contrib/llvm/lib/Target/Mips/AsmParser/MipsAsmParser.cpp (revision 354978) +++ head/contrib/llvm/lib/Target/Mips/AsmParser/MipsAsmParser.cpp (revision 354979) @@ -1,8484 +1,8529 @@ //===-- 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/Triple.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/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/SubtargetFeature.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/TargetRegistry.h" #include "llvm/Support/raw_ostream.h" #include #include #include #include #include #include using namespace llvm; #define DEBUG_TYPE "mips-asm-parser" namespace llvm { class MCInstrInfo; } // end namespace llvm extern cl::opt 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::FeatureFP64Bit, Mips::FeatureGP64Bit, Mips::FeatureNaN2008 + Mips::FeatureCnMipsP, Mips::FeatureFP64Bit, Mips::FeatureGP64Bit, + Mips::FeatureNaN2008 }; namespace { class MipsAsmParser : public MCTargetAsmParser { MipsTargetStreamer &getTargetStreamer() { MCTargetStreamer &TS = *getParser().getStreamer().getTargetStreamer(); return static_cast(TS); } MipsABIInfo ABI; SmallVector, 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; 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 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(unsigned &RegNo, 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; OperandMatchResultTy parseMemOperand(OperandVector &Operands); OperandMatchResultTy matchAnyRegisterNameWithoutDollar(OperandVector &Operands, StringRef Identifier, SMLoc S); OperandMatchResultTy matchAnyRegisterWithoutDollar(OperandVector &Operands, const AsmToken &Token, SMLoc S); OperandMatchResultTy matchAnyRegisterWithoutDollar(OperandVector &Operands, SMLoc S); OperandMatchResultTy parseAnyRegister(OperandVector &Operands); OperandMatchResultTy parseImm(OperandVector &Operands); OperandMatchResultTy parseJumpTarget(OperandVector &Operands); OperandMatchResultTy parseInvNum(OperandVector &Operands); OperandMatchResultTy 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 expandLoadImmReal(MCInst &Inst, bool IsSingle, bool IsGPR, 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 expandMemInst(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 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 expandMXTRAlias(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out, const MCSubtargetInfo *STI); + bool expandSaaAddr(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out, + const MCSubtargetInfo *STI); + bool reportParseError(Twine ErrorMsg); bool reportParseError(SMLoc Loc, Twine ErrorMsg); bool parseMemOffset(const MCExpr *&Res, bool isParenExpr); bool isEvaluated(const MCExpr *Expr); bool parseSetMips0Directive(); bool parseSetArchDirective(); bool parseSetFeature(uint64_t Feature); bool isPicAndNotNxxAbi(); // Used by .cpload, .cprestore, and .cpsetup. 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 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().getFeatureBits()[Feature])) { MCSubtargetInfo &STI = copySTI(); setAvailableFeatures( ComputeAvailableFeatures(STI.ToggleFeature(FeatureString))); AssemblerOptions.back()->setFeatures(STI.getFeatureBits()); } } void clearFeatureBits(uint64_t Feature, StringRef FeatureString) { if (getSTI().getFeatureBits()[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( llvm::make_unique(getSTI().getFeatureBits())); // Create an assembler options environment for the user to modify. AssemblerOptions.push_back( llvm::make_unique(getSTI().getFeatureBits())); getTargetStreamer().updateABIInfo(*this); if (!isABI_O32() && !useOddSPReg() != 0) report_fatal_error("-mno-odd-spreg requires the O32 ABI"); CurrentFn = nullptr; 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().getFeatureBits()[Mips::FeatureGP64Bit]; } bool isFP64bit() const { return getSTI().getFeatureBits()[Mips::FeatureFP64Bit]; } 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().getFeatureBits()[Mips::FeatureFPXX]; } bool useOddSPReg() const { return !(getSTI().getFeatureBits()[Mips::FeatureNoOddSPReg]); } bool inMicroMipsMode() const { return getSTI().getFeatureBits()[Mips::FeatureMicroMips]; } bool hasMips1() const { return getSTI().getFeatureBits()[Mips::FeatureMips1]; } bool hasMips2() const { return getSTI().getFeatureBits()[Mips::FeatureMips2]; } bool hasMips3() const { return getSTI().getFeatureBits()[Mips::FeatureMips3]; } bool hasMips4() const { return getSTI().getFeatureBits()[Mips::FeatureMips4]; } bool hasMips5() const { return getSTI().getFeatureBits()[Mips::FeatureMips5]; } bool hasMips32() const { return getSTI().getFeatureBits()[Mips::FeatureMips32]; } bool hasMips64() const { return getSTI().getFeatureBits()[Mips::FeatureMips64]; } bool hasMips32r2() const { return getSTI().getFeatureBits()[Mips::FeatureMips32r2]; } bool hasMips64r2() const { return getSTI().getFeatureBits()[Mips::FeatureMips64r2]; } bool hasMips32r3() const { return (getSTI().getFeatureBits()[Mips::FeatureMips32r3]); } bool hasMips64r3() const { return (getSTI().getFeatureBits()[Mips::FeatureMips64r3]); } bool hasMips32r5() const { return (getSTI().getFeatureBits()[Mips::FeatureMips32r5]); } bool hasMips64r5() const { return (getSTI().getFeatureBits()[Mips::FeatureMips64r5]); } bool hasMips32r6() const { return getSTI().getFeatureBits()[Mips::FeatureMips32r6]; } bool hasMips64r6() const { return getSTI().getFeatureBits()[Mips::FeatureMips64r6]; } bool hasDSP() const { return getSTI().getFeatureBits()[Mips::FeatureDSP]; } bool hasDSPR2() const { return getSTI().getFeatureBits()[Mips::FeatureDSPR2]; } bool hasDSPR3() const { return getSTI().getFeatureBits()[Mips::FeatureDSPR3]; } bool hasMSA() const { return getSTI().getFeatureBits()[Mips::FeatureMSA]; } bool hasCnMips() const { return (getSTI().getFeatureBits()[Mips::FeatureCnMips]); } + bool hasCnMipsP() const { + return (getSTI().getFeatureBits()[Mips::FeatureCnMipsP]); + } + bool inPicMode() { return IsPicEnabled; } bool inMips16Mode() const { return getSTI().getFeatureBits()[Mips::FeatureMips16]; } bool useTraps() const { return getSTI().getFeatureBits()[Mips::FeatureUseTCCInDIV]; } bool useSoftFloat() const { return getSTI().getFeatureBits()[Mips::FeatureSoftFloat]; } bool hasMT() const { return getSTI().getFeatureBits()[Mips::FeatureMT]; } bool hasCRC() const { return getSTI().getFeatureBits()[Mips::FeatureCRC]; } bool hasVirt() const { return getSTI().getFeatureBits()[Mips::FeatureVirt]; } bool hasGINV() const { return getSTI().getFeatureBits()[Mips::FeatureGINV]; } /// 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); } } }; /// 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) : MCParsedAsmOperand(), 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 *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 CreateReg(unsigned Index, StringRef Str, RegKind RegKind, const MCRegisterInfo *RegInfo, SMLoc S, SMLoc E, MipsAsmParser &Parser) { auto Op = llvm::make_unique(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(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 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 void addSImmOperands(MCInst &Inst, unsigned N) const { if (isImm() && !isConstantImm()) { addExpr(Inst, getImm()); return; } addConstantSImmOperands(Inst, N); } template void addUImmOperands(MCInst &Inst, unsigned N) const { if (isImm() && !isConstantImm()) { addExpr(Inst, getImm()); return; } addConstantUImmOperands(Inst, N); } template void addConstantSImmOperands(MCInst &Inst, unsigned N) const { assert(N == 1 && "Invalid number of operands!"); int64_t Imm = getConstantImm() - Offset; Imm = SignExtend64(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 bool isConstantUImm() const { return isConstantImm() && isUInt(getConstantImm() - Offset); } template bool isSImm() const { return isConstantImm() ? isInt(getConstantImm()) : isImm(); } template bool isUImm() const { return isConstantImm() ? isUInt(getConstantImm()) : isImm(); } template bool isAnyImm() const { return isConstantImm() ? (isInt(getConstantImm()) || isUInt(getConstantImm())) : isImm(); } template bool isConstantSImm() const { return isConstantImm() && isInt(getConstantImm() - Offset); } template 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(getMemOff()); } // Allow relocation operators. // FIXME: This predicate and others need to look through binary expressions // and determine whether a Value is a constant or not. template bool isMemWithSimmOffset() const { if (!isMem()) return false; if (!getMemBase()->isGPRAsmReg()) return false; if (isa(getMemOff()) || (isConstantMemOff() && isShiftedInt(getConstantMemOff()))) return true; MCValue Res; bool IsReloc = getMemOff()->evaluateAsRelocatable(Res, nullptr, nullptr); return IsReloc && isShiftedInt(Res.getConstant()); } bool isMemWithPtrSizeOffset() const { if (!isMem()) return false; if (!getMemBase()->isGPRAsmReg()) return false; const unsigned PtrBits = AsmParser.getABI().ArePtrs64bit() ? 64 : 32; if (isa(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 bool isMemWithUimmOffsetSP() const { return isMem() && isConstantMemOff() && isUInt(getConstantMemOff()) && getMemBase()->isRegIdx() && (getMemBase()->getGPR32Reg() == Mips::SP); } template bool isMemWithUimmWordAlignedOffsetSP() const { return isMem() && isConstantMemOff() && isUInt(getConstantMemOff()) && (getConstantMemOff() % 4 == 0) && getMemBase()->isRegIdx() && (getMemBase()->getGPR32Reg() == Mips::SP); } template bool isMemWithSimmWordAlignedOffsetGP() const { return isMem() && isConstantMemOff() && isInt(getConstantMemOff()) && (getConstantMemOff() % 4 == 0) && getMemBase()->isRegIdx() && (getMemBase()->getGPR32Reg() == Mips::GP); } template bool isScaledUImm() const { return isConstantImm() && isShiftedUInt(getConstantImm()); } template bool isScaledSImm() const { if (isConstantImm() && isShiftedInt(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(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(getMemOff())->getValue(); } const SmallVectorImpl &getRegList() const { assert((Kind == k_RegList) && "Invalid access!"); return *(RegList.List); } static std::unique_ptr CreateToken(StringRef Str, SMLoc S, MipsAsmParser &Parser) { auto Op = llvm::make_unique(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 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 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 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 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 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 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 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 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 CreateImm(const MCExpr *Val, SMLoc S, SMLoc E, MipsAsmParser &Parser) { auto Op = llvm::make_unique(k_Immediate, Parser); Op->Imm.Val = Val; Op->StartLoc = S; Op->EndLoc = E; return Op; } static std::unique_ptr CreateMem(std::unique_ptr Base, const MCExpr *Off, SMLoc S, SMLoc E, MipsAsmParser &Parser) { auto Op = llvm::make_unique(k_Memory, Parser); Op->Mem.Base = Base.release(); Op->Mem.Off = Off; Op->StartLoc = S; Op->EndLoc = E; return Op; } static std::unique_ptr CreateRegList(SmallVectorImpl &Regs, SMLoc StartLoc, SMLoc EndLoc, MipsAsmParser &Parser) { assert(Regs.size() > 0 && "Empty list not allowed"); auto Op = llvm::make_unique(k_RegList, Parser); Op->RegList.List = new SmallVector(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 namespace llvm { extern const MCInstrDesc MipsInsts[]; } // end namespace llvm static const MCInstrDesc &getInstDesc(unsigned Opcode) { return MipsInsts[Opcode]; } 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(Expr)) { return &SRExpr->getSymbol(); } if (const MCBinaryExpr *BExpr = dyn_cast(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(Expr)) return getSingleMCSymbol(UExpr->getSubExpr()); return nullptr; } static unsigned countMCSymbolRefExpr(const MCExpr *Expr) { if (isa(Expr)) return 1; if (const MCBinaryExpr *BExpr = dyn_cast(Expr)) return countMCSymbolRefExpr(BExpr->getLHS()) + countMCSymbolRefExpr(BExpr->getRHS()); if (const MCUnaryExpr *UExpr = dyn_cast(Expr)) return countMCSymbolRefExpr(UExpr->getSubExpr()); return 0; } bool MipsAsmParser::processInstruction(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out, const MCSubtargetInfo *STI) { MipsTargetStreamer &TOut = getTargetStreamer(); const MCInstrDesc &MCID = getInstDesc(Inst.getOpcode()); bool ExpandedJalSym = false; Inst.setLoc(IDLoc); if (MCID.isBranch() || MCID.isCall()) { const unsigned Opcode = Inst.getOpcode(); 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"); LLVM_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(), 1LL << (inMicroMipsMode() ? 1 : 2))) 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(), 1LL << (inMicroMipsMode() ? 1 : 2))) 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(), 1LL << 2)) 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(), 1LL << 2)) 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(), 1LL << 2)) 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(), 2LL)) 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() && Inst.getOpcode() == Mips::SSNOP) { std::string ISA = hasMips64r6() ? "MIPS64r6" : "MIPS32r6"; Warning(IDLoc, "ssnop is deprecated for " + ISA + " and is equivalent to a " "nop instruction"); } if (hasCnMips()) { const unsigned Opcode = Inst.getOpcode(); 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 (Inst.getOpcode()) { 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; LLVM_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 ((Inst.getOpcode() == Mips::J || Inst.getOpcode() == 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 ((Inst.getOpcode() == Mips::JAL || Inst.getOpcode() == 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); // FIXME: Add support for label+offset operands (currently causes an error). // FIXME: Add support for forward-declared local symbols. // FIXME: Add expansion for when the LargeGOT option is enabled. if (JalSym->isInSection() || JalSym->isTemporary() || (JalSym->isELF() && cast(JalSym)->getBinding() == ELF::STB_LOCAL)) { if (isABI_O32()) { // If it's a local symbol and the O32 ABI is being used, we expand to: // lw $25, 0($gp) // R_(MICRO)MIPS_GOT16 label // addiu $25, $25, 0 // R_(MICRO)MIPS_LO16 label // jalr $25 const MCExpr *Got16RelocExpr = MipsMCExpr::create(MipsMCExpr::MEK_GOT, JalExpr, getContext()); const MCExpr *Lo16RelocExpr = MipsMCExpr::create(MipsMCExpr::MEK_LO, JalExpr, getContext()); TOut.emitRRX(Mips::LW, Mips::T9, GPReg, MCOperand::createExpr(Got16RelocExpr), IDLoc, STI); TOut.emitRRX(Mips::ADDiu, Mips::T9, Mips::T9, MCOperand::createExpr(Lo16RelocExpr), IDLoc, STI); } else if (isABI_N32() || isABI_N64()) { // If it's a local symbol and the N32/N64 ABIs are being used, // we expand to: // lw/ld $25, 0($gp) // R_(MICRO)MIPS_GOT_DISP label // jalr $25 const MCExpr *GotDispRelocExpr = MipsMCExpr::create(MipsMCExpr::MEK_GOT_DISP, JalExpr, getContext()); TOut.emitRRX(ABI.ArePtrs64bit() ? Mips::LD : Mips::LW, Mips::T9, GPReg, MCOperand::createExpr(GotDispRelocExpr), IDLoc, STI); } } else { // If it's an external/weak symbol, we expand to: // lw/ld $25, 0($gp) // R_(MICRO)MIPS_CALL16 label // jalr $25 const MCExpr *Call16RelocExpr = MipsMCExpr::create(MipsMCExpr::MEK_GOT_CALL, JalExpr, getContext()); TOut.emitRRX(ABI.ArePtrs64bit() ? Mips::LD : Mips::LW, Mips::T9, GPReg, MCOperand::createExpr(Call16RelocExpr), IDLoc, STI); } MCInst JalrInst; if (IsCpRestoreSet && inMicroMipsMode()) JalrInst.setOpcode(Mips::JALRS_MM); else JalrInst.setOpcode(inMicroMipsMode() ? Mips::JALR_MM : Mips::JALR); JalrInst.addOperand(MCOperand::createReg(Mips::RA)); JalrInst.addOperand(MCOperand::createReg(Mips::T9)); if (EmitJalrReloc) { // 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; } bool IsPCRelativeLoad = (MCID.TSFlags & MipsII::IsPCRelativeLoad) != 0; if ((MCID.mayLoad() || MCID.mayStore()) && !IsPCRelativeLoad) { // Check the offset of memory operand, if it is a symbol // reference or immediate we may have to expand instructions. for (unsigned i = 0; i < MCID.getNumOperands(); i++) { const MCOperandInfo &OpInfo = MCID.OpInfo[i]; if ((OpInfo.OperandType == MCOI::OPERAND_MEMORY) || (OpInfo.OperandType == MCOI::OPERAND_UNKNOWN)) { MCOperand &Op = Inst.getOperand(i); if (Op.isImm()) { int64_t MemOffset = Op.getImm(); if (MemOffset < -32768 || MemOffset > 32767) { // Offset can't exceed 16bit value. expandMemInst(Inst, IDLoc, Out, STI, MCID.mayLoad()); return getParser().hasPendingError(); } } else if (Op.isExpr()) { const MCExpr *Expr = Op.getExpr(); if (Expr->getKind() == MCExpr::SymbolRef) { const MCSymbolRefExpr *SR = static_cast(Expr); if (SR->getKind() == MCSymbolRefExpr::VK_None) { // Expand symbol. expandMemInst(Inst, IDLoc, Out, STI, MCID.mayLoad()); return getParser().hasPendingError(); } } else if (!isEvaluated(Expr)) { expandMemInst(Inst, IDLoc, Out, STI, MCID.mayLoad()); return getParser().hasPendingError(); } } } } // for } // if load/store if (inMicroMipsMode()) { if (MCID.mayLoad() && Inst.getOpcode() != Mips::LWP_MM) { // Try to create 16-bit GP relative load instruction. for (unsigned i = 0; i < MCID.getNumOperands(); i++) { const MCOperandInfo &OpInfo = MCID.OpInfo[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 (Inst.getOpcode()) { 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(); if (FillDelaySlot) 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; } // 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. if (FillDelaySlot) { TOut.emitEmptyDelaySlot(hasShortDelaySlot(Inst), IDLoc, STI); TOut.emitDirectiveSetReorder(); } if ((Inst.getOpcode() == Mips::JalOneReg || Inst.getOpcode() == 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; } 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 Mips::PseudoTRUNC_W_D: return expandTrunc(Inst, true, true, IDLoc, Out, STI) ? MER_Fail : MER_Success; case Mips::LoadImmSingleGPR: return expandLoadImmReal(Inst, true, true, false, IDLoc, Out, STI) ? MER_Fail : MER_Success; case Mips::LoadImmSingleFGR: return expandLoadImmReal(Inst, true, false, false, IDLoc, Out, STI) ? MER_Fail : MER_Success; case Mips::LoadImmDoubleGPR: return expandLoadImmReal(Inst, false, true, false, IDLoc, Out, STI) ? MER_Fail : MER_Success; case Mips::LoadImmDoubleFGR: return expandLoadImmReal(Inst, false, false, true, IDLoc, Out, STI) ? MER_Fail : MER_Success; case Mips::LoadImmDoubleFGR_32: return expandLoadImmReal(Inst, false, false, false, IDLoc, Out, STI) ? MER_Fail : MER_Success; case Mips::Ulh: return expandUlh(Inst, true, IDLoc, Out, STI) ? MER_Fail : MER_Success; case Mips::Ulhu: return expandUlh(Inst, false, IDLoc, Out, STI) ? MER_Fail : MER_Success; case Mips::Ush: return expandUsh(Inst, IDLoc, Out, STI) ? MER_Fail : MER_Success; case Mips::Ulw: case Mips::Usw: return expandUxw(Inst, IDLoc, Out, STI) ? MER_Fail : MER_Success; case Mips::NORImm: case Mips::NORImm64: return expandAliasImmediate(Inst, IDLoc, Out, STI) ? MER_Fail : MER_Success; case Mips::SGE: case Mips::SGEU: return expandSge(Inst, IDLoc, Out, STI) ? MER_Fail : MER_Success; case Mips::SGEImm: case Mips::SGEUImm: case Mips::SGEImm64: case Mips::SGEUImm64: return expandSgeImm(Inst, IDLoc, Out, STI) ? MER_Fail : MER_Success; case Mips::SGTImm: case Mips::SGTUImm: case Mips::SGTImm64: case Mips::SGTUImm64: return expandSgtImm(Inst, IDLoc, Out, STI) ? MER_Fail : MER_Success; case Mips::SLTImm64: if (isInt<16>(Inst.getOperand(2).getImm())) { Inst.setOpcode(Mips::SLTi64); return MER_NotAMacro; } return expandAliasImmediate(Inst, IDLoc, Out, STI) ? MER_Fail : MER_Success; case Mips::SLTUImm64: if (isInt<16>(Inst.getOperand(2).getImm())) { Inst.setOpcode(Mips::SLTiu64); return MER_NotAMacro; } return expandAliasImmediate(Inst, IDLoc, Out, STI) ? MER_Fail : MER_Success; case Mips::ADDi: case Mips::ADDi_MM: case Mips::ADDiu: case Mips::ADDiu_MM: case Mips::SLTi: case Mips::SLTi_MM: case Mips::SLTiu: case Mips::SLTiu_MM: if ((Inst.getNumOperands() == 3) && Inst.getOperand(0).isReg() && Inst.getOperand(1).isReg() && Inst.getOperand(2).isImm()) { int64_t ImmValue = Inst.getOperand(2).getImm(); if (isInt<16>(ImmValue)) return MER_NotAMacro; return expandAliasImmediate(Inst, IDLoc, Out, STI) ? MER_Fail : MER_Success; } return MER_NotAMacro; case Mips::ANDi: case Mips::ANDi_MM: case Mips::ANDi64: case Mips::ORi: case Mips::ORi_MM: case Mips::ORi64: case Mips::XORi: case Mips::XORi_MM: case Mips::XORi64: if ((Inst.getNumOperands() == 3) && Inst.getOperand(0).isReg() && Inst.getOperand(1).isReg() && Inst.getOperand(2).isImm()) { int64_t ImmValue = Inst.getOperand(2).getImm(); if (isUInt<16>(ImmValue)) return MER_NotAMacro; return expandAliasImmediate(Inst, IDLoc, Out, STI) ? MER_Fail : MER_Success; } return MER_NotAMacro; case Mips::ROL: case Mips::ROR: return expandRotation(Inst, IDLoc, Out, STI) ? MER_Fail : MER_Success; case Mips::ROLImm: case Mips::RORImm: return expandRotationImm(Inst, IDLoc, Out, STI) ? MER_Fail : MER_Success; case Mips::DROL: case Mips::DROR: return expandDRotation(Inst, IDLoc, Out, STI) ? MER_Fail : MER_Success; case Mips::DROLImm: case Mips::DRORImm: return expandDRotationImm(Inst, IDLoc, Out, STI) ? MER_Fail : MER_Success; case Mips::ABSMacro: return expandAbs(Inst, IDLoc, Out, STI) ? MER_Fail : MER_Success; case Mips::MULImmMacro: case Mips::DMULImmMacro: return expandMulImm(Inst, IDLoc, Out, STI) ? MER_Fail : MER_Success; case Mips::MULOMacro: case Mips::DMULOMacro: return expandMulO(Inst, IDLoc, Out, STI) ? MER_Fail : MER_Success; case Mips::MULOUMacro: case Mips::DMULOUMacro: return expandMulOU(Inst, IDLoc, Out, STI) ? MER_Fail : MER_Success; case Mips::DMULMacro: return expandDMULMacro(Inst, IDLoc, Out, STI) ? MER_Fail : MER_Success; case Mips::LDMacro: case Mips::SDMacro: return expandLoadStoreDMacro(Inst, IDLoc, Out, STI, Inst.getOpcode() == Mips::LDMacro) ? MER_Fail : MER_Success; case Mips::SDC1_M1: return expandStoreDM1Macro(Inst, IDLoc, Out, STI) ? MER_Fail : MER_Success; case Mips::SEQMacro: return expandSeq(Inst, IDLoc, Out, STI) ? MER_Fail : MER_Success; case Mips::SEQIMacro: return expandSeqI(Inst, IDLoc, Out, STI) ? MER_Fail : MER_Success; case Mips::MFTC0: case Mips::MTTC0: case Mips::MFTGPR: case Mips::MTTGPR: case Mips::MFTLO: case Mips::MTTLO: case Mips::MFTHI: case Mips::MTTHI: case Mips::MFTACX: case Mips::MTTACX: case Mips::MFTDSP: case Mips::MTTDSP: case Mips::MFTC1: case Mips::MTTC1: case Mips::MFTHC1: case Mips::MTTHC1: case Mips::CFTC1: case Mips::CTTC1: return expandMXTRAlias(Inst, IDLoc, Out, STI) ? MER_Fail : MER_Success; + case Mips::SaaAddr: + case Mips::SaadAddr: + return expandSaaAddr(Inst, IDLoc, Out, STI) ? MER_Fail : MER_Success; } } bool MipsAsmParser::expandJalWithRegs(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out, const MCSubtargetInfo *STI) { MipsTargetStreamer &TOut = getTargetStreamer(); // Create a JALR instruction which is going to replace the pseudo-JAL. MCInst JalrInst; JalrInst.setLoc(IDLoc); const MCOperand FirstRegOp = Inst.getOperand(0); const unsigned Opcode = Inst.getOpcode(); if (Opcode == Mips::JalOneReg) { // jal $rs => jalr $rs if (IsCpRestoreSet && inMicroMipsMode()) { JalrInst.setOpcode(Mips::JALRS16_MM); JalrInst.addOperand(FirstRegOp); } else if (inMicroMipsMode()) { JalrInst.setOpcode(hasMips32r6() ? Mips::JALRC16_MMR6 : Mips::JALR16_MM); JalrInst.addOperand(FirstRegOp); } else { JalrInst.setOpcode(Mips::JALR); JalrInst.addOperand(MCOperand::createReg(Mips::RA)); JalrInst.addOperand(FirstRegOp); } } else if (Opcode == Mips::JalTwoReg) { // jal $rd, $rs => jalr $rd, $rs if (IsCpRestoreSet && inMicroMipsMode()) JalrInst.setOpcode(Mips::JALRS_MM); else JalrInst.setOpcode(inMicroMipsMode() ? Mips::JALR_MM : Mips::JALR); JalrInst.addOperand(FirstRegOp); const MCOperand SecondRegOp = Inst.getOperand(1); JalrInst.addOperand(SecondRegOp); } Out.EmitInstruction(JalrInst, *STI); // If .set reorder is active and branch instruction has a delay slot, // emit a NOP after it. const MCInstrDesc &MCID = getInstDesc(JalrInst.getOpcode()); if (MCID.hasDelaySlot() && AssemblerOptions.back()->isReorder()) TOut.emitEmptyDelaySlot(hasShortDelaySlot(JalrInst), IDLoc, STI); return false; } /// Can the value be represented by a unsigned N-bit value and a shift left? template static bool isShiftedUIntAtAnyPosition(uint64_t x) { unsigned BitNum = findFirstSet(x); return (x == x >> BitNum << BitNum) && isUInt(x >> BitNum); } /// Load (or add) an immediate into a register. /// /// @param ImmValue The immediate to load. /// @param DstReg The register that will hold the immediate. /// @param SrcReg A register to add to the immediate or Mips::NoRegister /// for a simple initialization. /// @param Is32BitImm Is ImmValue 32-bit or 64-bit? /// @param IsAddress True if the immediate represents an address. False if it /// is an integer. /// @param IDLoc Location of the immediate in the source file. bool MipsAsmParser::loadImmediate(int64_t ImmValue, unsigned DstReg, unsigned SrcReg, bool Is32BitImm, bool IsAddress, SMLoc IDLoc, MCStreamer &Out, const MCSubtargetInfo *STI) { MipsTargetStreamer &TOut = getTargetStreamer(); if (!Is32BitImm && !isGP64bit()) { Error(IDLoc, "instruction requires a 64-bit architecture"); return true; } if (Is32BitImm) { if (isInt<32>(ImmValue) || isUInt<32>(ImmValue)) { // Sign extend up to 64-bit so that the predicates match the hardware // behaviour. In particular, isInt<16>(0xffff8000) and similar should be // true. ImmValue = SignExtend64<32>(ImmValue); } else { Error(IDLoc, "instruction requires a 32-bit immediate"); return true; } } unsigned ZeroReg = IsAddress ? ABI.GetNullPtr() : ABI.GetZeroReg(); unsigned AdduOp = !Is32BitImm ? Mips::DADDu : Mips::ADDu; bool UseSrcReg = false; if (SrcReg != Mips::NoRegister) UseSrcReg = true; unsigned TmpReg = DstReg; if (UseSrcReg && getContext().getRegisterInfo()->isSuperOrSubRegisterEq(DstReg, SrcReg)) { // At this point we need AT to perform the expansions and we exit if it is // not available. unsigned ATReg = getATReg(IDLoc); if (!ATReg) return true; TmpReg = ATReg; } if (isInt<16>(ImmValue)) { if (!UseSrcReg) SrcReg = ZeroReg; // This doesn't quite follow the usual ABI expectations for N32 but matches // traditional assembler behaviour. N32 would normally use addiu for both // integers and addresses. if (IsAddress && !Is32BitImm) { TOut.emitRRI(Mips::DADDiu, DstReg, SrcReg, ImmValue, IDLoc, STI); return false; } TOut.emitRRI(Mips::ADDiu, DstReg, SrcReg, ImmValue, IDLoc, STI); return false; } if (isUInt<16>(ImmValue)) { unsigned TmpReg = DstReg; if (SrcReg == DstReg) { TmpReg = getATReg(IDLoc); if (!TmpReg) return true; } TOut.emitRRI(Mips::ORi, TmpReg, ZeroReg, ImmValue, IDLoc, STI); if (UseSrcReg) TOut.emitRRR(ABI.GetPtrAdduOp(), DstReg, TmpReg, SrcReg, IDLoc, STI); return false; } if (isInt<32>(ImmValue) || isUInt<32>(ImmValue)) { warnIfNoMacro(IDLoc); uint16_t Bits31To16 = (ImmValue >> 16) & 0xffff; uint16_t Bits15To0 = ImmValue & 0xffff; if (!Is32BitImm && !isInt<32>(ImmValue)) { // Traditional behaviour seems to special case this particular value. It's // not clear why other masks are handled differently. if (ImmValue == 0xffffffff) { TOut.emitRI(Mips::LUi, TmpReg, 0xffff, IDLoc, STI); TOut.emitRRI(Mips::DSRL32, TmpReg, TmpReg, 0, IDLoc, STI); if (UseSrcReg) TOut.emitRRR(AdduOp, DstReg, TmpReg, SrcReg, IDLoc, STI); return false; } // Expand to an ORi instead of a LUi to avoid sign-extending into the // upper 32 bits. TOut.emitRRI(Mips::ORi, TmpReg, ZeroReg, Bits31To16, IDLoc, STI); TOut.emitRRI(Mips::DSLL, TmpReg, TmpReg, 16, IDLoc, STI); if (Bits15To0) TOut.emitRRI(Mips::ORi, TmpReg, TmpReg, Bits15To0, IDLoc, STI); if (UseSrcReg) TOut.emitRRR(AdduOp, DstReg, TmpReg, SrcReg, IDLoc, STI); return false; } TOut.emitRI(Mips::LUi, TmpReg, Bits31To16, IDLoc, STI); if (Bits15To0) TOut.emitRRI(Mips::ORi, TmpReg, TmpReg, Bits15To0, IDLoc, STI); if (UseSrcReg) TOut.emitRRR(AdduOp, DstReg, TmpReg, SrcReg, IDLoc, STI); return false; } if (isShiftedUIntAtAnyPosition<16>(ImmValue)) { if (Is32BitImm) { Error(IDLoc, "instruction requires a 32-bit immediate"); return true; } // Traditionally, these immediates are shifted as little as possible and as // such we align the most significant bit to bit 15 of our temporary. unsigned FirstSet = findFirstSet((uint64_t)ImmValue); unsigned LastSet = findLastSet((uint64_t)ImmValue); unsigned ShiftAmount = FirstSet - (15 - (LastSet - FirstSet)); uint16_t Bits = (ImmValue >> ShiftAmount) & 0xffff; TOut.emitRRI(Mips::ORi, TmpReg, ZeroReg, Bits, IDLoc, STI); TOut.emitRRI(Mips::DSLL, TmpReg, TmpReg, ShiftAmount, IDLoc, STI); if (UseSrcReg) TOut.emitRRR(AdduOp, DstReg, TmpReg, SrcReg, IDLoc, STI); return false; } warnIfNoMacro(IDLoc); // The remaining case is packed with a sequence of dsll and ori with zeros // being omitted and any neighbouring dsll's being coalesced. // The highest 32-bit's are equivalent to a 32-bit immediate load. // Load bits 32-63 of ImmValue into bits 0-31 of the temporary register. if (loadImmediate(ImmValue >> 32, TmpReg, Mips::NoRegister, true, false, IDLoc, Out, STI)) return false; // Shift and accumulate into the register. If a 16-bit chunk is zero, then // skip it and defer the shift to the next chunk. unsigned ShiftCarriedForwards = 16; for (int BitNum = 16; BitNum >= 0; BitNum -= 16) { uint16_t ImmChunk = (ImmValue >> BitNum) & 0xffff; if (ImmChunk != 0) { TOut.emitDSLL(TmpReg, TmpReg, ShiftCarriedForwards, IDLoc, STI); TOut.emitRRI(Mips::ORi, TmpReg, TmpReg, ImmChunk, IDLoc, STI); ShiftCarriedForwards = 0; } ShiftCarriedForwards += 16; } ShiftCarriedForwards -= 16; // Finish any remaining shifts left by trailing zeros. if (ShiftCarriedForwards) TOut.emitDSLL(TmpReg, TmpReg, ShiftCarriedForwards, IDLoc, STI); if (UseSrcReg) TOut.emitRRR(AdduOp, DstReg, TmpReg, SrcReg, IDLoc, STI); return false; } bool MipsAsmParser::expandLoadImm(MCInst &Inst, bool Is32BitImm, SMLoc IDLoc, MCStreamer &Out, const MCSubtargetInfo *STI) { const MCOperand &ImmOp = Inst.getOperand(1); assert(ImmOp.isImm() && "expected immediate operand kind"); const MCOperand &DstRegOp = Inst.getOperand(0); assert(DstRegOp.isReg() && "expected register operand kind"); if (loadImmediate(ImmOp.getImm(), DstRegOp.getReg(), Mips::NoRegister, Is32BitImm, false, IDLoc, Out, STI)) return true; return false; } bool MipsAsmParser::expandLoadAddress(unsigned DstReg, unsigned BaseReg, const MCOperand &Offset, bool Is32BitAddress, SMLoc IDLoc, MCStreamer &Out, const MCSubtargetInfo *STI) { // la can't produce a usable address when addresses are 64-bit. if (Is32BitAddress && ABI.ArePtrs64bit()) { // FIXME: Demote this to a warning and continue as if we had 'dla' instead. // We currently can't do this because we depend on the equality // operator and N64 can end up with a GPR32/GPR64 mismatch. Error(IDLoc, "la used to load 64-bit address"); // Continue as if we had 'dla' instead. Is32BitAddress = false; return true; } // dla requires 64-bit addresses. if (!Is32BitAddress && !hasMips3()) { Error(IDLoc, "instruction requires a 64-bit architecture"); return true; } if (!Offset.isImm()) return loadAndAddSymbolAddress(Offset.getExpr(), DstReg, BaseReg, Is32BitAddress, IDLoc, Out, STI); if (!ABI.ArePtrs64bit()) { // Continue as if we had 'la' whether we had 'la' or 'dla'. Is32BitAddress = true; } return loadImmediate(Offset.getImm(), DstReg, BaseReg, Is32BitAddress, true, IDLoc, Out, STI); } bool MipsAsmParser::loadAndAddSymbolAddress(const MCExpr *SymExpr, unsigned DstReg, unsigned SrcReg, bool Is32BitSym, SMLoc IDLoc, MCStreamer &Out, const MCSubtargetInfo *STI) { // FIXME: These expansions do not respect -mxgot. MipsTargetStreamer &TOut = getTargetStreamer(); bool UseSrcReg = SrcReg != Mips::NoRegister; warnIfNoMacro(IDLoc); if (inPicMode() && ABI.IsO32()) { MCValue Res; if (!SymExpr->evaluateAsRelocatable(Res, nullptr, nullptr)) { Error(IDLoc, "expected relocatable expression"); return true; } if (Res.getSymB() != nullptr) { Error(IDLoc, "expected relocatable expression with only one symbol"); return true; } // The case where the result register is $25 is somewhat special. If the // symbol in the final relocation is external and not modified with a // constant then we must use R_MIPS_CALL16 instead of R_MIPS_GOT16. if ((DstReg == Mips::T9 || DstReg == Mips::T9_64) && !UseSrcReg && Res.getConstant() == 0 && !(Res.getSymA()->getSymbol().isInSection() || Res.getSymA()->getSymbol().isTemporary() || (Res.getSymA()->getSymbol().isELF() && cast(Res.getSymA()->getSymbol()).getBinding() == ELF::STB_LOCAL))) { const MCExpr *CallExpr = MipsMCExpr::create(MipsMCExpr::MEK_GOT_CALL, SymExpr, getContext()); TOut.emitRRX(Mips::LW, DstReg, GPReg, MCOperand::createExpr(CallExpr), IDLoc, STI); return false; } // The remaining cases are: // External GOT: lw $tmp, %got(symbol+offset)($gp) // >addiu $tmp, $tmp, %lo(offset) // >addiu $rd, $tmp, $rs // Local GOT: lw $tmp, %got(symbol+offset)($gp) // addiu $tmp, $tmp, %lo(symbol+offset)($gp) // >addiu $rd, $tmp, $rs // The addiu's marked with a '>' may be omitted if they are redundant. If // this happens then the last instruction must use $rd as the result // register. const MipsMCExpr *GotExpr = MipsMCExpr::create(MipsMCExpr::MEK_GOT, SymExpr, getContext()); const MCExpr *LoExpr = nullptr; if (Res.getSymA()->getSymbol().isInSection() || Res.getSymA()->getSymbol().isTemporary()) LoExpr = MipsMCExpr::create(MipsMCExpr::MEK_LO, SymExpr, getContext()); else if (Res.getConstant() != 0) { // External symbols fully resolve the symbol with just the %got(symbol) // but we must still account for any offset to the symbol for expressions // like symbol+8. LoExpr = MCConstantExpr::create(Res.getConstant(), getContext()); } unsigned TmpReg = DstReg; if (UseSrcReg && getContext().getRegisterInfo()->isSuperOrSubRegisterEq(DstReg, SrcReg)) { // If $rs is the same as $rd, we need to use AT. // If it is not available we exit. unsigned ATReg = getATReg(IDLoc); if (!ATReg) return true; TmpReg = ATReg; } TOut.emitRRX(Mips::LW, TmpReg, GPReg, MCOperand::createExpr(GotExpr), IDLoc, STI); if (LoExpr) TOut.emitRRX(Mips::ADDiu, TmpReg, TmpReg, MCOperand::createExpr(LoExpr), IDLoc, STI); if (UseSrcReg) TOut.emitRRR(Mips::ADDu, DstReg, TmpReg, SrcReg, IDLoc, STI); return false; } if (inPicMode() && ABI.ArePtrs64bit()) { MCValue Res; if (!SymExpr->evaluateAsRelocatable(Res, nullptr, nullptr)) { Error(IDLoc, "expected relocatable expression"); return true; } if (Res.getSymB() != nullptr) { Error(IDLoc, "expected relocatable expression with only one symbol"); return true; } // The case where the result register is $25 is somewhat special. If the // symbol in the final relocation is external and not modified with a // constant then we must use R_MIPS_CALL16 instead of R_MIPS_GOT_DISP. if ((DstReg == Mips::T9 || DstReg == Mips::T9_64) && !UseSrcReg && Res.getConstant() == 0 && !(Res.getSymA()->getSymbol().isInSection() || Res.getSymA()->getSymbol().isTemporary() || (Res.getSymA()->getSymbol().isELF() && cast(Res.getSymA()->getSymbol()).getBinding() == ELF::STB_LOCAL))) { const MCExpr *CallExpr = MipsMCExpr::create(MipsMCExpr::MEK_GOT_CALL, SymExpr, getContext()); TOut.emitRRX(Mips::LD, DstReg, GPReg, MCOperand::createExpr(CallExpr), IDLoc, STI); return false; } // The remaining cases are: // Small offset: ld $tmp, %got_disp(symbol)($gp) // >daddiu $tmp, $tmp, offset // >daddu $rd, $tmp, $rs // The daddiu's marked with a '>' may be omitted if they are redundant. If // this happens then the last instruction must use $rd as the result // register. const MipsMCExpr *GotExpr = MipsMCExpr::create(MipsMCExpr::MEK_GOT_DISP, Res.getSymA(), getContext()); const MCExpr *LoExpr = nullptr; if (Res.getConstant() != 0) { // Symbols fully resolve with just the %got_disp(symbol) but we // must still account for any offset to the symbol for // expressions like symbol+8. LoExpr = MCConstantExpr::create(Res.getConstant(), getContext()); // FIXME: Offsets greater than 16 bits are not yet implemented. // FIXME: The correct range is a 32-bit sign-extended number. if (Res.getConstant() < -0x8000 || Res.getConstant() > 0x7fff) { Error(IDLoc, "macro instruction uses large offset, which is not " "currently supported"); return true; } } unsigned TmpReg = DstReg; if (UseSrcReg && getContext().getRegisterInfo()->isSuperOrSubRegisterEq(DstReg, SrcReg)) { // If $rs is the same as $rd, we need to use AT. // If it is not available we exit. unsigned ATReg = getATReg(IDLoc); if (!ATReg) return true; TmpReg = ATReg; } TOut.emitRRX(Mips::LD, TmpReg, GPReg, MCOperand::createExpr(GotExpr), IDLoc, STI); if (LoExpr) TOut.emitRRX(Mips::DADDiu, TmpReg, TmpReg, MCOperand::createExpr(LoExpr), IDLoc, STI); if (UseSrcReg) TOut.emitRRR(Mips::DADDu, DstReg, TmpReg, SrcReg, IDLoc, STI); return false; } const MipsMCExpr *HiExpr = MipsMCExpr::create(MipsMCExpr::MEK_HI, SymExpr, getContext()); const MipsMCExpr *LoExpr = MipsMCExpr::create(MipsMCExpr::MEK_LO, SymExpr, getContext()); // This is the 64-bit symbol address expansion. if (ABI.ArePtrs64bit() && isGP64bit()) { // We need AT for the 64-bit expansion in the cases where the optional // source register is the destination register and for the superscalar // scheduled form. // // If it is not available we exit if the destination is the same as the // source register. const MipsMCExpr *HighestExpr = MipsMCExpr::create(MipsMCExpr::MEK_HIGHEST, SymExpr, getContext()); const MipsMCExpr *HigherExpr = MipsMCExpr::create(MipsMCExpr::MEK_HIGHER, SymExpr, getContext()); bool RdRegIsRsReg = getContext().getRegisterInfo()->isSuperOrSubRegisterEq(DstReg, SrcReg); if (canUseATReg() && UseSrcReg && RdRegIsRsReg) { unsigned ATReg = getATReg(IDLoc); // If $rs is the same as $rd: // (d)la $rd, sym($rd) => lui $at, %highest(sym) // daddiu $at, $at, %higher(sym) // dsll $at, $at, 16 // daddiu $at, $at, %hi(sym) // dsll $at, $at, 16 // daddiu $at, $at, %lo(sym) // daddu $rd, $at, $rd TOut.emitRX(Mips::LUi, ATReg, MCOperand::createExpr(HighestExpr), IDLoc, STI); TOut.emitRRX(Mips::DADDiu, ATReg, ATReg, MCOperand::createExpr(HigherExpr), IDLoc, STI); TOut.emitRRI(Mips::DSLL, ATReg, ATReg, 16, IDLoc, STI); TOut.emitRRX(Mips::DADDiu, ATReg, ATReg, MCOperand::createExpr(HiExpr), IDLoc, STI); TOut.emitRRI(Mips::DSLL, ATReg, ATReg, 16, IDLoc, STI); TOut.emitRRX(Mips::DADDiu, ATReg, ATReg, MCOperand::createExpr(LoExpr), IDLoc, STI); TOut.emitRRR(Mips::DADDu, DstReg, ATReg, SrcReg, IDLoc, STI); return false; - } else if (canUseATReg() && !RdRegIsRsReg) { + } else if (canUseATReg() && !RdRegIsRsReg && DstReg != getATReg(IDLoc)) { unsigned ATReg = getATReg(IDLoc); // If the $rs is different from $rd or if $rs isn't specified and we // have $at available: // (d)la $rd, sym/sym($rs) => lui $rd, %highest(sym) // lui $at, %hi(sym) // daddiu $rd, $rd, %higher(sym) // daddiu $at, $at, %lo(sym) // dsll32 $rd, $rd, 0 // daddu $rd, $rd, $at // (daddu $rd, $rd, $rs) // // Which is preferred for superscalar issue. TOut.emitRX(Mips::LUi, DstReg, MCOperand::createExpr(HighestExpr), IDLoc, STI); TOut.emitRX(Mips::LUi, ATReg, MCOperand::createExpr(HiExpr), IDLoc, STI); TOut.emitRRX(Mips::DADDiu, DstReg, DstReg, MCOperand::createExpr(HigherExpr), IDLoc, STI); TOut.emitRRX(Mips::DADDiu, ATReg, ATReg, MCOperand::createExpr(LoExpr), IDLoc, STI); TOut.emitRRI(Mips::DSLL32, DstReg, DstReg, 0, IDLoc, STI); TOut.emitRRR(Mips::DADDu, DstReg, DstReg, ATReg, IDLoc, STI); if (UseSrcReg) TOut.emitRRR(Mips::DADDu, DstReg, DstReg, SrcReg, IDLoc, STI); return false; - } else if (!canUseATReg() && !RdRegIsRsReg) { + } else if ((!canUseATReg() && !RdRegIsRsReg) || + (canUseATReg() && DstReg == getATReg(IDLoc))) { // Otherwise, synthesize the address in the destination register // serially: // (d)la $rd, sym/sym($rs) => lui $rd, %highest(sym) // daddiu $rd, $rd, %higher(sym) // dsll $rd, $rd, 16 // daddiu $rd, $rd, %hi(sym) // dsll $rd, $rd, 16 // daddiu $rd, $rd, %lo(sym) TOut.emitRX(Mips::LUi, DstReg, MCOperand::createExpr(HighestExpr), IDLoc, STI); TOut.emitRRX(Mips::DADDiu, DstReg, DstReg, MCOperand::createExpr(HigherExpr), IDLoc, STI); TOut.emitRRI(Mips::DSLL, DstReg, DstReg, 16, IDLoc, STI); TOut.emitRRX(Mips::DADDiu, DstReg, DstReg, MCOperand::createExpr(HiExpr), IDLoc, STI); TOut.emitRRI(Mips::DSLL, DstReg, DstReg, 16, IDLoc, STI); TOut.emitRRX(Mips::DADDiu, DstReg, DstReg, MCOperand::createExpr(LoExpr), IDLoc, STI); if (UseSrcReg) TOut.emitRRR(Mips::DADDu, DstReg, DstReg, SrcReg, IDLoc, STI); return false; } else { // We have a case where SrcReg == DstReg and we don't have $at // available. We can't expand this case, so error out appropriately. assert(SrcReg == DstReg && !canUseATReg() && "Could have expanded dla but didn't?"); reportParseError(IDLoc, "pseudo-instruction requires $at, which is not available"); return true; } } // And now, the 32-bit symbol address expansion: // If $rs is the same as $rd: // (d)la $rd, sym($rd) => lui $at, %hi(sym) // ori $at, $at, %lo(sym) // addu $rd, $at, $rd // Otherwise, if the $rs is different from $rd or if $rs isn't specified: // (d)la $rd, sym/sym($rs) => lui $rd, %hi(sym) // ori $rd, $rd, %lo(sym) // (addu $rd, $rd, $rs) unsigned TmpReg = DstReg; if (UseSrcReg && getContext().getRegisterInfo()->isSuperOrSubRegisterEq(DstReg, SrcReg)) { // If $rs is the same as $rd, we need to use AT. // If it is not available we exit. unsigned ATReg = getATReg(IDLoc); if (!ATReg) return true; TmpReg = ATReg; } TOut.emitRX(Mips::LUi, TmpReg, MCOperand::createExpr(HiExpr), IDLoc, STI); TOut.emitRRX(Mips::ADDiu, TmpReg, TmpReg, MCOperand::createExpr(LoExpr), IDLoc, STI); if (UseSrcReg) TOut.emitRRR(Mips::ADDu, DstReg, TmpReg, SrcReg, IDLoc, STI); else assert( getContext().getRegisterInfo()->isSuperOrSubRegisterEq(DstReg, TmpReg)); return false; } // Each double-precision register DO-D15 overlaps with two of the single // precision registers F0-F31. As an example, all of the following hold true: // D0 + 1 == F1, F1 + 1 == D1, F1 + 1 == F2, depending on the context. static unsigned nextReg(unsigned Reg) { if (MipsMCRegisterClasses[Mips::FGR32RegClassID].contains(Reg)) return Reg == (unsigned)Mips::F31 ? (unsigned)Mips::F0 : Reg + 1; switch (Reg) { default: llvm_unreachable("Unknown register in assembly macro expansion!"); case Mips::ZERO: return Mips::AT; case Mips::AT: return Mips::V0; case Mips::V0: return Mips::V1; case Mips::V1: return Mips::A0; case Mips::A0: return Mips::A1; case Mips::A1: return Mips::A2; case Mips::A2: return Mips::A3; case Mips::A3: return Mips::T0; case Mips::T0: return Mips::T1; case Mips::T1: return Mips::T2; case Mips::T2: return Mips::T3; case Mips::T3: return Mips::T4; case Mips::T4: return Mips::T5; case Mips::T5: return Mips::T6; case Mips::T6: return Mips::T7; case Mips::T7: return Mips::S0; case Mips::S0: return Mips::S1; case Mips::S1: return Mips::S2; case Mips::S2: return Mips::S3; case Mips::S3: return Mips::S4; case Mips::S4: return Mips::S5; case Mips::S5: return Mips::S6; case Mips::S6: return Mips::S7; case Mips::S7: return Mips::T8; case Mips::T8: return Mips::T9; case Mips::T9: return Mips::K0; case Mips::K0: return Mips::K1; case Mips::K1: return Mips::GP; case Mips::GP: return Mips::SP; case Mips::SP: return Mips::FP; case Mips::FP: return Mips::RA; case Mips::RA: return Mips::ZERO; case Mips::D0: return Mips::F1; case Mips::D1: return Mips::F3; case Mips::D2: return Mips::F5; case Mips::D3: return Mips::F7; case Mips::D4: return Mips::F9; case Mips::D5: return Mips::F11; case Mips::D6: return Mips::F13; case Mips::D7: return Mips::F15; case Mips::D8: return Mips::F17; case Mips::D9: return Mips::F19; case Mips::D10: return Mips::F21; case Mips::D11: return Mips::F23; case Mips::D12: return Mips::F25; case Mips::D13: return Mips::F27; case Mips::D14: return Mips::F29; case Mips::D15: return Mips::F31; } } // FIXME: This method is too general. In principle we should compute the number // of instructions required to synthesize the immediate inline compared to // synthesizing the address inline and relying on non .text sections. // For static O32 and N32 this may yield a small benefit, for static N64 this is // likely to yield a much larger benefit as we have to synthesize a 64bit // address to load a 64 bit value. bool MipsAsmParser::emitPartialAddress(MipsTargetStreamer &TOut, SMLoc IDLoc, MCSymbol *Sym) { unsigned ATReg = getATReg(IDLoc); if (!ATReg) return true; if(IsPicEnabled) { const MCExpr *GotSym = MCSymbolRefExpr::create(Sym, MCSymbolRefExpr::VK_None, getContext()); const MipsMCExpr *GotExpr = MipsMCExpr::create(MipsMCExpr::MEK_GOT, GotSym, getContext()); if(isABI_O32() || isABI_N32()) { TOut.emitRRX(Mips::LW, ATReg, GPReg, MCOperand::createExpr(GotExpr), IDLoc, STI); } else { //isABI_N64() TOut.emitRRX(Mips::LD, ATReg, GPReg, MCOperand::createExpr(GotExpr), IDLoc, STI); } } else { //!IsPicEnabled const MCExpr *HiSym = MCSymbolRefExpr::create(Sym, MCSymbolRefExpr::VK_None, getContext()); const MipsMCExpr *HiExpr = MipsMCExpr::create(MipsMCExpr::MEK_HI, HiSym, getContext()); // FIXME: This is technically correct but gives a different result to gas, // but gas is incomplete there (it has a fixme noting it doesn't work with // 64-bit addresses). // FIXME: With -msym32 option, the address expansion for N64 should probably // use the O32 / N32 case. It's safe to use the 64 address expansion as the // symbol's value is considered sign extended. if(isABI_O32() || isABI_N32()) { TOut.emitRX(Mips::LUi, ATReg, MCOperand::createExpr(HiExpr), IDLoc, STI); } else { //isABI_N64() const MCExpr *HighestSym = MCSymbolRefExpr::create(Sym, MCSymbolRefExpr::VK_None, getContext()); const MipsMCExpr *HighestExpr = MipsMCExpr::create(MipsMCExpr::MEK_HIGHEST, HighestSym, getContext()); const MCExpr *HigherSym = MCSymbolRefExpr::create(Sym, MCSymbolRefExpr::VK_None, getContext()); const MipsMCExpr *HigherExpr = MipsMCExpr::create(MipsMCExpr::MEK_HIGHER, HigherSym, getContext()); TOut.emitRX(Mips::LUi, ATReg, MCOperand::createExpr(HighestExpr), IDLoc, STI); TOut.emitRRX(Mips::DADDiu, ATReg, ATReg, MCOperand::createExpr(HigherExpr), IDLoc, STI); TOut.emitRRI(Mips::DSLL, ATReg, ATReg, 16, IDLoc, STI); TOut.emitRRX(Mips::DADDiu, ATReg, ATReg, MCOperand::createExpr(HiExpr), IDLoc, STI); TOut.emitRRI(Mips::DSLL, ATReg, ATReg, 16, IDLoc, STI); } } return false; } bool MipsAsmParser::expandLoadImmReal(MCInst &Inst, bool IsSingle, bool IsGPR, bool Is64FPU, SMLoc IDLoc, MCStreamer &Out, const MCSubtargetInfo *STI) { MipsTargetStreamer &TOut = getTargetStreamer(); assert(Inst.getNumOperands() == 2 && "Invalid operand count"); assert(Inst.getOperand(0).isReg() && Inst.getOperand(1).isImm() && "Invalid instruction operand."); unsigned FirstReg = Inst.getOperand(0).getReg(); uint64_t ImmOp64 = Inst.getOperand(1).getImm(); uint32_t HiImmOp64 = (ImmOp64 & 0xffffffff00000000) >> 32; // If ImmOp64 is AsmToken::Integer type (all bits set to zero in the // exponent field), convert it to double (e.g. 1 to 1.0) if ((HiImmOp64 & 0x7ff00000) == 0) { APFloat RealVal(APFloat::IEEEdouble(), ImmOp64); ImmOp64 = RealVal.bitcastToAPInt().getZExtValue(); } uint32_t LoImmOp64 = ImmOp64 & 0xffffffff; HiImmOp64 = (ImmOp64 & 0xffffffff00000000) >> 32; if (IsSingle) { // Conversion of a double in an uint64_t to a float in a uint32_t, // retaining the bit pattern of a float. uint32_t ImmOp32; double doubleImm = BitsToDouble(ImmOp64); float tmp_float = static_cast(doubleImm); ImmOp32 = FloatToBits(tmp_float); if (IsGPR) { if (loadImmediate(ImmOp32, FirstReg, Mips::NoRegister, true, true, IDLoc, Out, STI)) return true; return false; } else { unsigned ATReg = getATReg(IDLoc); if (!ATReg) return true; if (LoImmOp64 == 0) { if (loadImmediate(ImmOp32, ATReg, Mips::NoRegister, true, true, IDLoc, Out, STI)) return true; TOut.emitRR(Mips::MTC1, FirstReg, ATReg, IDLoc, STI); return false; } MCSection *CS = getStreamer().getCurrentSectionOnly(); // FIXME: Enhance this expansion to use the .lit4 & .lit8 sections // where appropriate. MCSection *ReadOnlySection = getContext().getELFSection( ".rodata", ELF::SHT_PROGBITS, ELF::SHF_ALLOC); MCSymbol *Sym = getContext().createTempSymbol(); const MCExpr *LoSym = MCSymbolRefExpr::create(Sym, MCSymbolRefExpr::VK_None, getContext()); const MipsMCExpr *LoExpr = MipsMCExpr::create(MipsMCExpr::MEK_LO, LoSym, getContext()); getStreamer().SwitchSection(ReadOnlySection); getStreamer().EmitLabel(Sym, IDLoc); getStreamer().EmitIntValue(ImmOp32, 4); getStreamer().SwitchSection(CS); if(emitPartialAddress(TOut, IDLoc, Sym)) return true; TOut.emitRRX(Mips::LWC1, FirstReg, ATReg, MCOperand::createExpr(LoExpr), IDLoc, STI); } return false; } // if(!IsSingle) unsigned ATReg = getATReg(IDLoc); if (!ATReg) return true; if (IsGPR) { if (LoImmOp64 == 0) { if(isABI_N32() || isABI_N64()) { if (loadImmediate(HiImmOp64, FirstReg, Mips::NoRegister, false, true, IDLoc, Out, STI)) return true; return false; } else { if (loadImmediate(HiImmOp64, FirstReg, Mips::NoRegister, true, true, IDLoc, Out, STI)) return true; if (loadImmediate(0, nextReg(FirstReg), Mips::NoRegister, true, true, IDLoc, Out, STI)) return true; return false; } } MCSection *CS = getStreamer().getCurrentSectionOnly(); MCSection *ReadOnlySection = getContext().getELFSection( ".rodata", ELF::SHT_PROGBITS, ELF::SHF_ALLOC); MCSymbol *Sym = getContext().createTempSymbol(); const MCExpr *LoSym = MCSymbolRefExpr::create(Sym, MCSymbolRefExpr::VK_None, getContext()); const MipsMCExpr *LoExpr = MipsMCExpr::create(MipsMCExpr::MEK_LO, LoSym, getContext()); getStreamer().SwitchSection(ReadOnlySection); getStreamer().EmitLabel(Sym, IDLoc); getStreamer().EmitIntValue(HiImmOp64, 4); getStreamer().EmitIntValue(LoImmOp64, 4); getStreamer().SwitchSection(CS); if(emitPartialAddress(TOut, IDLoc, Sym)) return true; if(isABI_N64()) TOut.emitRRX(Mips::DADDiu, ATReg, ATReg, MCOperand::createExpr(LoExpr), IDLoc, STI); else TOut.emitRRX(Mips::ADDiu, ATReg, ATReg, MCOperand::createExpr(LoExpr), IDLoc, STI); if(isABI_N32() || isABI_N64()) TOut.emitRRI(Mips::LD, FirstReg, ATReg, 0, IDLoc, STI); else { TOut.emitRRI(Mips::LW, FirstReg, ATReg, 0, IDLoc, STI); TOut.emitRRI(Mips::LW, nextReg(FirstReg), ATReg, 4, IDLoc, STI); } return false; } else { // if(!IsGPR && !IsSingle) if ((LoImmOp64 == 0) && !((HiImmOp64 & 0xffff0000) && (HiImmOp64 & 0x0000ffff))) { // FIXME: In the case where the constant is zero, we can load the // register directly from the zero register. if (loadImmediate(HiImmOp64, ATReg, Mips::NoRegister, true, true, IDLoc, Out, STI)) return true; if (isABI_N32() || isABI_N64()) TOut.emitRR(Mips::DMTC1, FirstReg, ATReg, IDLoc, STI); else if (hasMips32r2()) { TOut.emitRR(Mips::MTC1, FirstReg, Mips::ZERO, IDLoc, STI); TOut.emitRRR(Mips::MTHC1_D32, FirstReg, FirstReg, ATReg, IDLoc, STI); } else { TOut.emitRR(Mips::MTC1, nextReg(FirstReg), ATReg, IDLoc, STI); TOut.emitRR(Mips::MTC1, FirstReg, Mips::ZERO, IDLoc, STI); } return false; } MCSection *CS = getStreamer().getCurrentSectionOnly(); // FIXME: Enhance this expansion to use the .lit4 & .lit8 sections // where appropriate. MCSection *ReadOnlySection = getContext().getELFSection( ".rodata", ELF::SHT_PROGBITS, ELF::SHF_ALLOC); MCSymbol *Sym = getContext().createTempSymbol(); const MCExpr *LoSym = MCSymbolRefExpr::create(Sym, MCSymbolRefExpr::VK_None, getContext()); const MipsMCExpr *LoExpr = MipsMCExpr::create(MipsMCExpr::MEK_LO, LoSym, getContext()); getStreamer().SwitchSection(ReadOnlySection); getStreamer().EmitLabel(Sym, IDLoc); getStreamer().EmitIntValue(HiImmOp64, 4); getStreamer().EmitIntValue(LoImmOp64, 4); getStreamer().SwitchSection(CS); if(emitPartialAddress(TOut, IDLoc, Sym)) return true; TOut.emitRRX(Is64FPU ? Mips::LDC164 : Mips::LDC1, FirstReg, ATReg, MCOperand::createExpr(LoExpr), IDLoc, STI); } return false; } bool MipsAsmParser::expandUncondBranchMMPseudo(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out, const MCSubtargetInfo *STI) { MipsTargetStreamer &TOut = getTargetStreamer(); assert(getInstDesc(Inst.getOpcode()).getNumOperands() == 1 && "unexpected number of operands"); MCOperand Offset = Inst.getOperand(0); if (Offset.isExpr()) { Inst.clear(); Inst.setOpcode(Mips::BEQ_MM); Inst.addOperand(MCOperand::createReg(Mips::ZERO)); Inst.addOperand(MCOperand::createReg(Mips::ZERO)); Inst.addOperand(MCOperand::createExpr(Offset.getExpr())); } else { assert(Offset.isImm() && "expected immediate operand kind"); if (isInt<11>(Offset.getImm())) { // If offset fits into 11 bits then this instruction becomes microMIPS // 16-bit unconditional branch instruction. if (inMicroMipsMode()) Inst.setOpcode(hasMips32r6() ? Mips::BC16_MMR6 : Mips::B16_MM); } else { if (!isInt<17>(Offset.getImm())) return Error(IDLoc, "branch target out of range"); if (OffsetToAlignment(Offset.getImm(), 1LL << 1)) return Error(IDLoc, "branch to misaligned address"); Inst.clear(); Inst.setOpcode(Mips::BEQ_MM); Inst.addOperand(MCOperand::createReg(Mips::ZERO)); Inst.addOperand(MCOperand::createReg(Mips::ZERO)); Inst.addOperand(MCOperand::createImm(Offset.getImm())); } } Out.EmitInstruction(Inst, *STI); // If .set reorder is active and branch instruction has a delay slot, // emit a NOP after it. const MCInstrDesc &MCID = getInstDesc(Inst.getOpcode()); if (MCID.hasDelaySlot() && AssemblerOptions.back()->isReorder()) TOut.emitEmptyDelaySlot(true, IDLoc, STI); return false; } bool MipsAsmParser::expandBranchImm(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out, const MCSubtargetInfo *STI) { MipsTargetStreamer &TOut = getTargetStreamer(); const MCOperand &DstRegOp = Inst.getOperand(0); assert(DstRegOp.isReg() && "expected register operand kind"); const MCOperand &ImmOp = Inst.getOperand(1); assert(ImmOp.isImm() && "expected immediate operand kind"); const MCOperand &MemOffsetOp = Inst.getOperand(2); assert((MemOffsetOp.isImm() || MemOffsetOp.isExpr()) && "expected immediate or expression operand"); bool IsLikely = false; unsigned OpCode = 0; switch(Inst.getOpcode()) { case Mips::BneImm: OpCode = Mips::BNE; break; case Mips::BeqImm: OpCode = Mips::BEQ; break; case Mips::BEQLImmMacro: OpCode = Mips::BEQL; IsLikely = true; break; case Mips::BNELImmMacro: OpCode = Mips::BNEL; IsLikely = true; break; default: llvm_unreachable("Unknown immediate branch pseudo-instruction."); break; } int64_t ImmValue = ImmOp.getImm(); if (ImmValue == 0) { if (IsLikely) { TOut.emitRRX(OpCode, DstRegOp.getReg(), Mips::ZERO, MCOperand::createExpr(MemOffsetOp.getExpr()), IDLoc, STI); TOut.emitRRI(Mips::SLL, Mips::ZERO, Mips::ZERO, 0, IDLoc, STI); } else TOut.emitRRX(OpCode, DstRegOp.getReg(), Mips::ZERO, MemOffsetOp, IDLoc, STI); } else { warnIfNoMacro(IDLoc); unsigned ATReg = getATReg(IDLoc); if (!ATReg) return true; if (loadImmediate(ImmValue, ATReg, Mips::NoRegister, !isGP64bit(), true, IDLoc, Out, STI)) return true; if (IsLikely) { TOut.emitRRX(OpCode, DstRegOp.getReg(), ATReg, MCOperand::createExpr(MemOffsetOp.getExpr()), IDLoc, STI); TOut.emitRRI(Mips::SLL, Mips::ZERO, Mips::ZERO, 0, IDLoc, STI); } else TOut.emitRRX(OpCode, DstRegOp.getReg(), ATReg, MemOffsetOp, IDLoc, STI); } return false; } void MipsAsmParser::expandMemInst(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out, const MCSubtargetInfo *STI, bool IsLoad) { const MCOperand &DstRegOp = Inst.getOperand(0); assert(DstRegOp.isReg() && "expected register operand kind"); const MCOperand &BaseRegOp = Inst.getOperand(1); assert(BaseRegOp.isReg() && "expected register operand kind"); const MCOperand &OffsetOp = Inst.getOperand(2); MipsTargetStreamer &TOut = getTargetStreamer(); unsigned DstReg = DstRegOp.getReg(); unsigned BaseReg = BaseRegOp.getReg(); unsigned TmpReg = DstReg; const MCInstrDesc &Desc = getInstDesc(Inst.getOpcode()); int16_t DstRegClass = Desc.OpInfo[0].RegClass; unsigned DstRegClassID = getContext().getRegisterInfo()->getRegClass(DstRegClass).getID(); bool IsGPR = (DstRegClassID == Mips::GPR32RegClassID) || (DstRegClassID == Mips::GPR64RegClassID); if (!IsLoad || !IsGPR || (BaseReg == DstReg)) { // At this point we need AT to perform the expansions // and we exit if it is not available. TmpReg = getATReg(IDLoc); if (!TmpReg) return; } if (OffsetOp.isImm()) { int64_t LoOffset = OffsetOp.getImm() & 0xffff; int64_t HiOffset = OffsetOp.getImm() & ~0xffff; // If msb of LoOffset is 1(negative number) we must increment // HiOffset to account for the sign-extension of the low part. if (LoOffset & 0x8000) HiOffset += 0x10000; bool IsLargeOffset = HiOffset != 0; if (IsLargeOffset) { bool Is32BitImm = (HiOffset >> 32) == 0; if (loadImmediate(HiOffset, TmpReg, Mips::NoRegister, Is32BitImm, true, IDLoc, Out, STI)) return; } if (BaseReg != Mips::ZERO && BaseReg != Mips::ZERO_64) TOut.emitRRR(isGP64bit() ? Mips::DADDu : Mips::ADDu, TmpReg, TmpReg, BaseReg, IDLoc, STI); TOut.emitRRI(Inst.getOpcode(), DstReg, TmpReg, LoOffset, IDLoc, STI); return; } assert(OffsetOp.isExpr() && "expected expression operand kind"); if (inPicMode()) { // FIXME: // a) Fix lw/sw $reg, symbol($reg) instruction expanding. // b) If expression includes offset (sym + number), do not // encode the offset into a relocation. Take it in account // in the last load/store instruction. // c) Check that immediates of R_MIPS_GOT16/R_MIPS_LO16 relocations // do not exceed 16-bit. // d) Use R_MIPS_GOT_PAGE/R_MIPS_GOT_OFST relocations instead // of R_MIPS_GOT_DISP in appropriate cases to reduce number // of GOT entries. expandLoadAddress(TmpReg, Mips::NoRegister, OffsetOp, !ABI.ArePtrs64bit(), IDLoc, Out, STI); TOut.emitRRI(Inst.getOpcode(), DstReg, TmpReg, 0, IDLoc, STI); } else { const MCExpr *ExprOffset = OffsetOp.getExpr(); MCOperand LoOperand = MCOperand::createExpr( MipsMCExpr::create(MipsMCExpr::MEK_LO, ExprOffset, getContext())); MCOperand HiOperand = MCOperand::createExpr( MipsMCExpr::create(MipsMCExpr::MEK_HI, ExprOffset, getContext())); if (IsLoad) TOut.emitLoadWithSymOffset(Inst.getOpcode(), DstReg, BaseReg, HiOperand, LoOperand, TmpReg, IDLoc, STI); else TOut.emitStoreWithSymOffset(Inst.getOpcode(), DstReg, BaseReg, HiOperand, LoOperand, TmpReg, IDLoc, STI); } } bool MipsAsmParser::expandLoadStoreMultiple(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out, const MCSubtargetInfo *STI) { unsigned OpNum = Inst.getNumOperands(); unsigned Opcode = Inst.getOpcode(); unsigned NewOpcode = Opcode == Mips::SWM_MM ? Mips::SWM32_MM : Mips::LWM32_MM; assert(Inst.getOperand(OpNum - 1).isImm() && Inst.getOperand(OpNum - 2).isReg() && Inst.getOperand(OpNum - 3).isReg() && "Invalid instruction operand."); if (OpNum < 8 && Inst.getOperand(OpNum - 1).getImm() <= 60 && Inst.getOperand(OpNum - 1).getImm() >= 0 && (Inst.getOperand(OpNum - 2).getReg() == Mips::SP || Inst.getOperand(OpNum - 2).getReg() == Mips::SP_64) && (Inst.getOperand(OpNum - 3).getReg() == Mips::RA || Inst.getOperand(OpNum - 3).getReg() == Mips::RA_64)) { // It can be implemented as SWM16 or LWM16 instruction. if (inMicroMipsMode() && hasMips32r6()) NewOpcode = Opcode == Mips::SWM_MM ? Mips::SWM16_MMR6 : Mips::LWM16_MMR6; else NewOpcode = Opcode == Mips::SWM_MM ? Mips::SWM16_MM : Mips::LWM16_MM; } Inst.setOpcode(NewOpcode); Out.EmitInstruction(Inst, *STI); return false; } bool MipsAsmParser::expandCondBranches(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out, const MCSubtargetInfo *STI) { MipsTargetStreamer &TOut = getTargetStreamer(); bool EmittedNoMacroWarning = false; unsigned PseudoOpcode = Inst.getOpcode(); unsigned SrcReg = Inst.getOperand(0).getReg(); const MCOperand &TrgOp = Inst.getOperand(1); const MCExpr *OffsetExpr = Inst.getOperand(2).getExpr(); unsigned ZeroSrcOpcode, ZeroTrgOpcode; bool ReverseOrderSLT, IsUnsigned, IsLikely, AcceptsEquality; unsigned TrgReg; if (TrgOp.isReg()) TrgReg = TrgOp.getReg(); else if (TrgOp.isImm()) { warnIfNoMacro(IDLoc); EmittedNoMacroWarning = true; TrgReg = getATReg(IDLoc); if (!TrgReg) return true; switch(PseudoOpcode) { default: llvm_unreachable("unknown opcode for branch pseudo-instruction"); case Mips::BLTImmMacro: PseudoOpcode = Mips::BLT; break; case Mips::BLEImmMacro: PseudoOpcode = Mips::BLE; break; case Mips::BGEImmMacro: PseudoOpcode = Mips::BGE; break; case Mips::BGTImmMacro: PseudoOpcode = Mips::BGT; break; case Mips::BLTUImmMacro: PseudoOpcode = Mips::BLTU; break; case Mips::BLEUImmMacro: PseudoOpcode = Mips::BLEU; break; case Mips::BGEUImmMacro: PseudoOpcode = Mips::BGEU; break; case Mips::BGTUImmMacro: PseudoOpcode = Mips::BGTU; break; case Mips::BLTLImmMacro: PseudoOpcode = Mips::BLTL; break; case Mips::BLELImmMacro: PseudoOpcode = Mips::BLEL; break; case Mips::BGELImmMacro: PseudoOpcode = Mips::BGEL; break; case Mips::BGTLImmMacro: PseudoOpcode = Mips::BGTL; break; case Mips::BLTULImmMacro: PseudoOpcode = Mips::BLTUL; break; case Mips::BLEULImmMacro: PseudoOpcode = Mips::BLEUL; break; case Mips::BGEULImmMacro: PseudoOpcode = Mips::BGEUL; break; case Mips::BGTULImmMacro: PseudoOpcode = Mips::BGTUL; break; } if (loadImmediate(TrgOp.getImm(), TrgReg, Mips::NoRegister, !isGP64bit(), false, IDLoc, Out, STI)) return true; } switch (PseudoOpcode) { case Mips::BLT: case Mips::BLTU: case Mips::BLTL: case Mips::BLTUL: AcceptsEquality = false; ReverseOrderSLT = false; IsUnsigned = ((PseudoOpcode == Mips::BLTU) || (PseudoOpcode == Mips::BLTUL)); IsLikely = ((PseudoOpcode == Mips::BLTL) || (PseudoOpcode == Mips::BLTUL)); ZeroSrcOpcode = Mips::BGTZ; ZeroTrgOpcode = Mips::BLTZ; break; case Mips::BLE: case Mips::BLEU: case Mips::BLEL: case Mips::BLEUL: AcceptsEquality = true; ReverseOrderSLT = true; IsUnsigned = ((PseudoOpcode == Mips::BLEU) || (PseudoOpcode == Mips::BLEUL)); IsLikely = ((PseudoOpcode == Mips::BLEL) || (PseudoOpcode == Mips::BLEUL)); ZeroSrcOpcode = Mips::BGEZ; ZeroTrgOpcode = Mips::BLEZ; break; case Mips::BGE: case Mips::BGEU: case Mips::BGEL: case Mips::BGEUL: AcceptsEquality = true; ReverseOrderSLT = false; IsUnsigned = ((PseudoOpcode == Mips::BGEU) || (PseudoOpcode == Mips::BGEUL)); IsLikely = ((PseudoOpcode == Mips::BGEL) || (PseudoOpcode == Mips::BGEUL)); ZeroSrcOpcode = Mips::BLEZ; ZeroTrgOpcode = Mips::BGEZ; break; case Mips::BGT: case Mips::BGTU: case Mips::BGTL: case Mips::BGTUL: AcceptsEquality = false; ReverseOrderSLT = true; IsUnsigned = ((PseudoOpcode == Mips::BGTU) || (PseudoOpcode == Mips::BGTUL)); IsLikely = ((PseudoOpcode == Mips::BGTL) || (PseudoOpcode == Mips::BGTUL)); ZeroSrcOpcode = Mips::BLTZ; ZeroTrgOpcode = Mips::BGTZ; break; default: llvm_unreachable("unknown opcode for branch pseudo-instruction"); } bool IsTrgRegZero = (TrgReg == Mips::ZERO); bool IsSrcRegZero = (SrcReg == Mips::ZERO); if (IsSrcRegZero && IsTrgRegZero) { // FIXME: All of these Opcode-specific if's are needed for compatibility // with GAS' behaviour. However, they may not generate the most efficient // code in some circumstances. if (PseudoOpcode == Mips::BLT) { TOut.emitRX(Mips::BLTZ, Mips::ZERO, MCOperand::createExpr(OffsetExpr), IDLoc, STI); return false; } if (PseudoOpcode == Mips::BLE) { TOut.emitRX(Mips::BLEZ, Mips::ZERO, MCOperand::createExpr(OffsetExpr), IDLoc, STI); Warning(IDLoc, "branch is always taken"); return false; } if (PseudoOpcode == Mips::BGE) { TOut.emitRX(Mips::BGEZ, Mips::ZERO, MCOperand::createExpr(OffsetExpr), IDLoc, STI); Warning(IDLoc, "branch is always taken"); return false; } if (PseudoOpcode == Mips::BGT) { TOut.emitRX(Mips::BGTZ, Mips::ZERO, MCOperand::createExpr(OffsetExpr), IDLoc, STI); return false; } if (PseudoOpcode == Mips::BGTU) { TOut.emitRRX(Mips::BNE, Mips::ZERO, Mips::ZERO, MCOperand::createExpr(OffsetExpr), IDLoc, STI); return false; } if (AcceptsEquality) { // If both registers are $0 and the pseudo-branch accepts equality, it // will always be taken, so we emit an unconditional branch. TOut.emitRRX(Mips::BEQ, Mips::ZERO, Mips::ZERO, MCOperand::createExpr(OffsetExpr), IDLoc, STI); Warning(IDLoc, "branch is always taken"); return false; } // If both registers are $0 and the pseudo-branch does not accept // equality, it will never be taken, so we don't have to emit anything. return false; } if (IsSrcRegZero || IsTrgRegZero) { if ((IsSrcRegZero && PseudoOpcode == Mips::BGTU) || (IsTrgRegZero && PseudoOpcode == Mips::BLTU)) { // If the $rs is $0 and the pseudo-branch is BGTU (0 > x) or // if the $rt is $0 and the pseudo-branch is BLTU (x < 0), // the pseudo-branch will never be taken, so we don't emit anything. // This only applies to unsigned pseudo-branches. return false; } if ((IsSrcRegZero && PseudoOpcode == Mips::BLEU) || (IsTrgRegZero && PseudoOpcode == Mips::BGEU)) { // If the $rs is $0 and the pseudo-branch is BLEU (0 <= x) or // if the $rt is $0 and the pseudo-branch is BGEU (x >= 0), // the pseudo-branch will always be taken, so we emit an unconditional // branch. // This only applies to unsigned pseudo-branches. TOut.emitRRX(Mips::BEQ, Mips::ZERO, Mips::ZERO, MCOperand::createExpr(OffsetExpr), IDLoc, STI); Warning(IDLoc, "branch is always taken"); return false; } if (IsUnsigned) { // If the $rs is $0 and the pseudo-branch is BLTU (0 < x) or // if the $rt is $0 and the pseudo-branch is BGTU (x > 0), // the pseudo-branch will be taken only when the non-zero register is // different from 0, so we emit a BNEZ. // // If the $rs is $0 and the pseudo-branch is BGEU (0 >= x) or // if the $rt is $0 and the pseudo-branch is BLEU (x <= 0), // the pseudo-branch will be taken only when the non-zero register is // equal to 0, so we emit a BEQZ. // // Because only BLEU and BGEU branch on equality, we can use the // AcceptsEquality variable to decide when to emit the BEQZ. TOut.emitRRX(AcceptsEquality ? Mips::BEQ : Mips::BNE, IsSrcRegZero ? TrgReg : SrcReg, Mips::ZERO, MCOperand::createExpr(OffsetExpr), IDLoc, STI); return false; } // If we have a signed pseudo-branch and one of the registers is $0, // we can use an appropriate compare-to-zero branch. We select which one // to use in the switch statement above. TOut.emitRX(IsSrcRegZero ? ZeroSrcOpcode : ZeroTrgOpcode, IsSrcRegZero ? TrgReg : SrcReg, MCOperand::createExpr(OffsetExpr), IDLoc, STI); return false; } // If neither the SrcReg nor the TrgReg are $0, we need AT to perform the // expansions. If it is not available, we return. unsigned ATRegNum = getATReg(IDLoc); if (!ATRegNum) return true; if (!EmittedNoMacroWarning) warnIfNoMacro(IDLoc); // SLT fits well with 2 of our 4 pseudo-branches: // BLT, where $rs < $rt, translates into "slt $at, $rs, $rt" and // BGT, where $rs > $rt, translates into "slt $at, $rt, $rs". // If the result of the SLT is 1, we branch, and if it's 0, we don't. // This is accomplished by using a BNEZ with the result of the SLT. // // The other 2 pseudo-branches are opposites of the above 2 (BGE with BLT // and BLE with BGT), so we change the BNEZ into a BEQZ. // Because only BGE and BLE branch on equality, we can use the // AcceptsEquality variable to decide when to emit the BEQZ. // Note that the order of the SLT arguments doesn't change between // opposites. // // The same applies to the unsigned variants, except that SLTu is used // instead of SLT. TOut.emitRRR(IsUnsigned ? Mips::SLTu : Mips::SLT, ATRegNum, ReverseOrderSLT ? TrgReg : SrcReg, ReverseOrderSLT ? SrcReg : TrgReg, IDLoc, STI); TOut.emitRRX(IsLikely ? (AcceptsEquality ? Mips::BEQL : Mips::BNEL) : (AcceptsEquality ? Mips::BEQ : Mips::BNE), ATRegNum, Mips::ZERO, MCOperand::createExpr(OffsetExpr), IDLoc, STI); return false; } // Expand a integer division macro. // // Notably we don't have to emit a warning when encountering $rt as the $zero // register, or 0 as an immediate. processInstruction() has already done that. // // The destination register can only be $zero when expanding (S)DivIMacro or // D(S)DivMacro. bool MipsAsmParser::expandDivRem(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out, const MCSubtargetInfo *STI, const bool IsMips64, const bool Signed) { MipsTargetStreamer &TOut = getTargetStreamer(); warnIfNoMacro(IDLoc); const MCOperand &RdRegOp = Inst.getOperand(0); assert(RdRegOp.isReg() && "expected register operand kind"); unsigned RdReg = RdRegOp.getReg(); const MCOperand &RsRegOp = Inst.getOperand(1); assert(RsRegOp.isReg() && "expected register operand kind"); unsigned RsReg = RsRegOp.getReg(); unsigned RtReg; int64_t ImmValue; const MCOperand &RtOp = Inst.getOperand(2); assert((RtOp.isReg() || RtOp.isImm()) && "expected register or immediate operand kind"); if (RtOp.isReg()) RtReg = RtOp.getReg(); else ImmValue = RtOp.getImm(); unsigned DivOp; unsigned ZeroReg; unsigned SubOp; if (IsMips64) { DivOp = Signed ? Mips::DSDIV : Mips::DUDIV; ZeroReg = Mips::ZERO_64; SubOp = Mips::DSUB; } else { DivOp = Signed ? Mips::SDIV : Mips::UDIV; ZeroReg = Mips::ZERO; SubOp = Mips::SUB; } bool UseTraps = useTraps(); unsigned Opcode = Inst.getOpcode(); bool isDiv = Opcode == Mips::SDivMacro || Opcode == Mips::SDivIMacro || Opcode == Mips::UDivMacro || Opcode == Mips::UDivIMacro || Opcode == Mips::DSDivMacro || Opcode == Mips::DSDivIMacro || Opcode == Mips::DUDivMacro || Opcode == Mips::DUDivIMacro; bool isRem = Opcode == Mips::SRemMacro || Opcode == Mips::SRemIMacro || Opcode == Mips::URemMacro || Opcode == Mips::URemIMacro || Opcode == Mips::DSRemMacro || Opcode == Mips::DSRemIMacro || Opcode == Mips::DURemMacro || Opcode == Mips::DURemIMacro; if (RtOp.isImm()) { unsigned ATReg = getATReg(IDLoc); if (!ATReg) return true; if (ImmValue == 0) { if (UseTraps) TOut.emitRRI(Mips::TEQ, ZeroReg, ZeroReg, 0x7, IDLoc, STI); else TOut.emitII(Mips::BREAK, 0x7, 0, IDLoc, STI); return false; } if (isRem && (ImmValue == 1 || (Signed && (ImmValue == -1)))) { TOut.emitRRR(Mips::OR, RdReg, ZeroReg, ZeroReg, IDLoc, STI); return false; } else if (isDiv && ImmValue == 1) { TOut.emitRRR(Mips::OR, RdReg, RsReg, Mips::ZERO, IDLoc, STI); return false; } else if (isDiv && Signed && ImmValue == -1) { TOut.emitRRR(SubOp, RdReg, ZeroReg, RsReg, IDLoc, STI); return false; } else { if (loadImmediate(ImmValue, ATReg, Mips::NoRegister, isInt<32>(ImmValue), false, Inst.getLoc(), Out, STI)) return true; TOut.emitRR(DivOp, RsReg, ATReg, IDLoc, STI); TOut.emitR(isDiv ? Mips::MFLO : Mips::MFHI, RdReg, IDLoc, STI); return false; } return true; } // If the macro expansion of (d)div(u) or (d)rem(u) would always trap or // break, insert the trap/break and exit. This gives a different result to // GAS. GAS has an inconsistency/missed optimization in that not all cases // are handled equivalently. As the observed behaviour is the same, we're ok. if (RtReg == Mips::ZERO || RtReg == Mips::ZERO_64) { if (UseTraps) { TOut.emitRRI(Mips::TEQ, ZeroReg, ZeroReg, 0x7, IDLoc, STI); return false; } TOut.emitII(Mips::BREAK, 0x7, 0, IDLoc, STI); return false; } // (d)rem(u) $0, $X, $Y is a special case. Like div $zero, $X, $Y, it does // not expand to macro sequence. if (isRem && (RdReg == Mips::ZERO || RdReg == Mips::ZERO_64)) { TOut.emitRR(DivOp, RsReg, RtReg, IDLoc, STI); return false; } // Temporary label for first branch traget MCContext &Context = TOut.getStreamer().getContext(); MCSymbol *BrTarget; MCOperand LabelOp; if (UseTraps) { TOut.emitRRI(Mips::TEQ, RtReg, ZeroReg, 0x7, IDLoc, STI); } else { // Branch to the li instruction. BrTarget = Context.createTempSymbol(); LabelOp = MCOperand::createExpr(MCSymbolRefExpr::create(BrTarget, Context)); TOut.emitRRX(Mips::BNE, RtReg, ZeroReg, LabelOp, IDLoc, STI); } TOut.emitRR(DivOp, RsReg, RtReg, IDLoc, STI); if (!UseTraps) TOut.emitII(Mips::BREAK, 0x7, 0, IDLoc, STI); if (!Signed) { if (!UseTraps) TOut.getStreamer().EmitLabel(BrTarget); TOut.emitR(isDiv ? Mips::MFLO : Mips::MFHI, RdReg, IDLoc, STI); return false; } unsigned ATReg = getATReg(IDLoc); if (!ATReg) return true; if (!UseTraps) TOut.getStreamer().EmitLabel(BrTarget); TOut.emitRRI(Mips::ADDiu, ATReg, ZeroReg, -1, IDLoc, STI); // Temporary label for the second branch target. MCSymbol *BrTargetEnd = Context.createTempSymbol(); MCOperand LabelOpEnd = MCOperand::createExpr(MCSymbolRefExpr::create(BrTargetEnd, Context)); // Branch to the mflo instruction. TOut.emitRRX(Mips::BNE, RtReg, ATReg, LabelOpEnd, IDLoc, STI); if (IsMips64) { TOut.emitRRI(Mips::ADDiu, ATReg, ZeroReg, 1, IDLoc, STI); TOut.emitDSLL(ATReg, ATReg, 63, IDLoc, STI); } else { TOut.emitRI(Mips::LUi, ATReg, (uint16_t)0x8000, IDLoc, STI); } if (UseTraps) TOut.emitRRI(Mips::TEQ, RsReg, ATReg, 0x6, IDLoc, STI); else { // Branch to the mflo instruction. TOut.emitRRX(Mips::BNE, RsReg, ATReg, LabelOpEnd, IDLoc, STI); TOut.emitNop(IDLoc, STI); TOut.emitII(Mips::BREAK, 0x6, 0, IDLoc, STI); } TOut.getStreamer().EmitLabel(BrTargetEnd); TOut.emitR(isDiv ? Mips::MFLO : Mips::MFHI, RdReg, IDLoc, STI); return false; } bool MipsAsmParser::expandTrunc(MCInst &Inst, bool IsDouble, bool Is64FPU, SMLoc IDLoc, MCStreamer &Out, const MCSubtargetInfo *STI) { MipsTargetStreamer &TOut = getTargetStreamer(); assert(Inst.getNumOperands() == 3 && "Invalid operand count"); assert(Inst.getOperand(0).isReg() && Inst.getOperand(1).isReg() && Inst.getOperand(2).isReg() && "Invalid instruction operand."); unsigned FirstReg = Inst.getOperand(0).getReg(); unsigned SecondReg = Inst.getOperand(1).getReg(); unsigned ThirdReg = Inst.getOperand(2).getReg(); if (hasMips1() && !hasMips2()) { unsigned ATReg = getATReg(IDLoc); if (!ATReg) return true; TOut.emitRR(Mips::CFC1, ThirdReg, Mips::RA, IDLoc, STI); TOut.emitRR(Mips::CFC1, ThirdReg, Mips::RA, IDLoc, STI); TOut.emitNop(IDLoc, STI); TOut.emitRRI(Mips::ORi, ATReg, ThirdReg, 0x3, IDLoc, STI); TOut.emitRRI(Mips::XORi, ATReg, ATReg, 0x2, IDLoc, STI); TOut.emitRR(Mips::CTC1, Mips::RA, ATReg, IDLoc, STI); TOut.emitNop(IDLoc, STI); TOut.emitRR(IsDouble ? (Is64FPU ? Mips::CVT_W_D64 : Mips::CVT_W_D32) : Mips::CVT_W_S, FirstReg, SecondReg, IDLoc, STI); TOut.emitRR(Mips::CTC1, Mips::RA, ThirdReg, IDLoc, STI); TOut.emitNop(IDLoc, STI); return false; } TOut.emitRR(IsDouble ? (Is64FPU ? Mips::TRUNC_W_D64 : Mips::TRUNC_W_D32) : Mips::TRUNC_W_S, FirstReg, SecondReg, IDLoc, STI); return false; } bool MipsAsmParser::expandUlh(MCInst &Inst, bool Signed, SMLoc IDLoc, MCStreamer &Out, const MCSubtargetInfo *STI) { if (hasMips32r6() || hasMips64r6()) { return Error(IDLoc, "instruction not supported on mips32r6 or mips64r6"); } const MCOperand &DstRegOp = Inst.getOperand(0); assert(DstRegOp.isReg() && "expected register operand kind"); const MCOperand &SrcRegOp = Inst.getOperand(1); assert(SrcRegOp.isReg() && "expected register operand kind"); const MCOperand &OffsetImmOp = Inst.getOperand(2); assert(OffsetImmOp.isImm() && "expected immediate operand kind"); MipsTargetStreamer &TOut = getTargetStreamer(); unsigned DstReg = DstRegOp.getReg(); unsigned SrcReg = SrcRegOp.getReg(); int64_t OffsetValue = OffsetImmOp.getImm(); // NOTE: We always need AT for ULHU, as it is always used as the source // register for one of the LBu's. warnIfNoMacro(IDLoc); unsigned ATReg = getATReg(IDLoc); if (!ATReg) return true; bool IsLargeOffset = !(isInt<16>(OffsetValue + 1) && isInt<16>(OffsetValue)); if (IsLargeOffset) { if (loadImmediate(OffsetValue, ATReg, SrcReg, !ABI.ArePtrs64bit(), true, IDLoc, Out, STI)) return true; } int64_t FirstOffset = IsLargeOffset ? 0 : OffsetValue; int64_t SecondOffset = IsLargeOffset ? 1 : (OffsetValue + 1); if (isLittle()) std::swap(FirstOffset, SecondOffset); unsigned FirstLbuDstReg = IsLargeOffset ? DstReg : ATReg; unsigned SecondLbuDstReg = IsLargeOffset ? ATReg : DstReg; unsigned LbuSrcReg = IsLargeOffset ? ATReg : SrcReg; unsigned SllReg = IsLargeOffset ? DstReg : ATReg; TOut.emitRRI(Signed ? Mips::LB : Mips::LBu, FirstLbuDstReg, LbuSrcReg, FirstOffset, IDLoc, STI); TOut.emitRRI(Mips::LBu, SecondLbuDstReg, LbuSrcReg, SecondOffset, IDLoc, STI); TOut.emitRRI(Mips::SLL, SllReg, SllReg, 8, IDLoc, STI); TOut.emitRRR(Mips::OR, DstReg, DstReg, ATReg, IDLoc, STI); return false; } bool MipsAsmParser::expandUsh(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out, const MCSubtargetInfo *STI) { if (hasMips32r6() || hasMips64r6()) { return Error(IDLoc, "instruction not supported on mips32r6 or mips64r6"); } const MCOperand &DstRegOp = Inst.getOperand(0); assert(DstRegOp.isReg() && "expected register operand kind"); const MCOperand &SrcRegOp = Inst.getOperand(1); assert(SrcRegOp.isReg() && "expected register operand kind"); const MCOperand &OffsetImmOp = Inst.getOperand(2); assert(OffsetImmOp.isImm() && "expected immediate operand kind"); MipsTargetStreamer &TOut = getTargetStreamer(); unsigned DstReg = DstRegOp.getReg(); unsigned SrcReg = SrcRegOp.getReg(); int64_t OffsetValue = OffsetImmOp.getImm(); warnIfNoMacro(IDLoc); unsigned ATReg = getATReg(IDLoc); if (!ATReg) return true; bool IsLargeOffset = !(isInt<16>(OffsetValue + 1) && isInt<16>(OffsetValue)); if (IsLargeOffset) { if (loadImmediate(OffsetValue, ATReg, SrcReg, !ABI.ArePtrs64bit(), true, IDLoc, Out, STI)) return true; } int64_t FirstOffset = IsLargeOffset ? 1 : (OffsetValue + 1); int64_t SecondOffset = IsLargeOffset ? 0 : OffsetValue; if (isLittle()) std::swap(FirstOffset, SecondOffset); if (IsLargeOffset) { TOut.emitRRI(Mips::SB, DstReg, ATReg, FirstOffset, IDLoc, STI); TOut.emitRRI(Mips::SRL, DstReg, DstReg, 8, IDLoc, STI); TOut.emitRRI(Mips::SB, DstReg, ATReg, SecondOffset, IDLoc, STI); TOut.emitRRI(Mips::LBu, ATReg, ATReg, 0, IDLoc, STI); TOut.emitRRI(Mips::SLL, DstReg, DstReg, 8, IDLoc, STI); TOut.emitRRR(Mips::OR, DstReg, DstReg, ATReg, IDLoc, STI); } else { TOut.emitRRI(Mips::SB, DstReg, SrcReg, FirstOffset, IDLoc, STI); TOut.emitRRI(Mips::SRL, ATReg, DstReg, 8, IDLoc, STI); TOut.emitRRI(Mips::SB, ATReg, SrcReg, SecondOffset, IDLoc, STI); } return false; } bool MipsAsmParser::expandUxw(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out, const MCSubtargetInfo *STI) { if (hasMips32r6() || hasMips64r6()) { return Error(IDLoc, "instruction not supported on mips32r6 or mips64r6"); } const MCOperand &DstRegOp = Inst.getOperand(0); assert(DstRegOp.isReg() && "expected register operand kind"); const MCOperand &SrcRegOp = Inst.getOperand(1); assert(SrcRegOp.isReg() && "expected register operand kind"); const MCOperand &OffsetImmOp = Inst.getOperand(2); assert(OffsetImmOp.isImm() && "expected immediate operand kind"); MipsTargetStreamer &TOut = getTargetStreamer(); unsigned DstReg = DstRegOp.getReg(); unsigned SrcReg = SrcRegOp.getReg(); int64_t OffsetValue = OffsetImmOp.getImm(); // Compute left/right load/store offsets. bool IsLargeOffset = !(isInt<16>(OffsetValue + 3) && isInt<16>(OffsetValue)); int64_t LxlOffset = IsLargeOffset ? 0 : OffsetValue; int64_t LxrOffset = IsLargeOffset ? 3 : (OffsetValue + 3); if (isLittle()) std::swap(LxlOffset, LxrOffset); bool IsLoadInst = (Inst.getOpcode() == Mips::Ulw); bool DoMove = IsLoadInst && (SrcReg == DstReg) && !IsLargeOffset; unsigned TmpReg = SrcReg; if (IsLargeOffset || DoMove) { warnIfNoMacro(IDLoc); TmpReg = getATReg(IDLoc); if (!TmpReg) return true; } if (IsLargeOffset) { if (loadImmediate(OffsetValue, TmpReg, SrcReg, !ABI.ArePtrs64bit(), true, IDLoc, Out, STI)) return true; } if (DoMove) std::swap(DstReg, TmpReg); unsigned XWL = IsLoadInst ? Mips::LWL : Mips::SWL; unsigned XWR = IsLoadInst ? Mips::LWR : Mips::SWR; TOut.emitRRI(XWL, DstReg, TmpReg, LxlOffset, IDLoc, STI); TOut.emitRRI(XWR, DstReg, TmpReg, LxrOffset, IDLoc, STI); if (DoMove) TOut.emitRRR(Mips::OR, TmpReg, DstReg, Mips::ZERO, IDLoc, STI); return false; } bool MipsAsmParser::expandSge(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out, const MCSubtargetInfo *STI) { MipsTargetStreamer &TOut = getTargetStreamer(); assert(Inst.getNumOperands() == 3 && "Invalid operand count"); assert(Inst.getOperand(0).isReg() && Inst.getOperand(1).isReg() && Inst.getOperand(2).isReg() && "Invalid instruction operand."); unsigned DstReg = Inst.getOperand(0).getReg(); unsigned SrcReg = Inst.getOperand(1).getReg(); unsigned OpReg = Inst.getOperand(2).getReg(); unsigned OpCode; warnIfNoMacro(IDLoc); switch (Inst.getOpcode()) { case Mips::SGE: OpCode = Mips::SLT; break; case Mips::SGEU: OpCode = Mips::SLTu; break; default: llvm_unreachable("unexpected 'sge' opcode"); } // $SrcReg >= $OpReg is equal to (not ($SrcReg < $OpReg)) TOut.emitRRR(OpCode, DstReg, SrcReg, OpReg, IDLoc, STI); TOut.emitRRI(Mips::XORi, DstReg, DstReg, 1, IDLoc, STI); return false; } bool MipsAsmParser::expandSgeImm(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out, const MCSubtargetInfo *STI) { MipsTargetStreamer &TOut = getTargetStreamer(); assert(Inst.getNumOperands() == 3 && "Invalid operand count"); assert(Inst.getOperand(0).isReg() && Inst.getOperand(1).isReg() && Inst.getOperand(2).isImm() && "Invalid instruction operand."); unsigned DstReg = Inst.getOperand(0).getReg(); unsigned SrcReg = Inst.getOperand(1).getReg(); int64_t ImmValue = Inst.getOperand(2).getImm(); unsigned OpRegCode, OpImmCode; warnIfNoMacro(IDLoc); switch (Inst.getOpcode()) { case Mips::SGEImm: case Mips::SGEImm64: OpRegCode = Mips::SLT; OpImmCode = Mips::SLTi; break; case Mips::SGEUImm: case Mips::SGEUImm64: OpRegCode = Mips::SLTu; OpImmCode = Mips::SLTiu; break; default: llvm_unreachable("unexpected 'sge' opcode with immediate"); } // $SrcReg >= Imm is equal to (not ($SrcReg < Imm)) if (isInt<16>(ImmValue)) { // Use immediate version of STL. TOut.emitRRI(OpImmCode, DstReg, SrcReg, ImmValue, IDLoc, STI); TOut.emitRRI(Mips::XORi, DstReg, DstReg, 1, IDLoc, STI); } else { unsigned ImmReg = DstReg; if (DstReg == SrcReg) { unsigned ATReg = getATReg(Inst.getLoc()); if (!ATReg) return true; ImmReg = ATReg; } if (loadImmediate(ImmValue, ImmReg, Mips::NoRegister, isInt<32>(ImmValue), false, IDLoc, Out, STI)) return true; TOut.emitRRR(OpRegCode, DstReg, SrcReg, ImmReg, IDLoc, STI); TOut.emitRRI(Mips::XORi, DstReg, DstReg, 1, IDLoc, STI); } return false; } bool MipsAsmParser::expandSgtImm(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out, const MCSubtargetInfo *STI) { MipsTargetStreamer &TOut = getTargetStreamer(); assert(Inst.getNumOperands() == 3 && "Invalid operand count"); assert(Inst.getOperand(0).isReg() && Inst.getOperand(1).isReg() && Inst.getOperand(2).isImm() && "Invalid instruction operand."); unsigned DstReg = Inst.getOperand(0).getReg(); unsigned SrcReg = Inst.getOperand(1).getReg(); unsigned ImmReg = DstReg; int64_t ImmValue = Inst.getOperand(2).getImm(); unsigned OpCode; warnIfNoMacro(IDLoc); switch (Inst.getOpcode()) { case Mips::SGTImm: case Mips::SGTImm64: OpCode = Mips::SLT; break; case Mips::SGTUImm: case Mips::SGTUImm64: OpCode = Mips::SLTu; break; default: llvm_unreachable("unexpected 'sgt' opcode with immediate"); } if (DstReg == SrcReg) { unsigned ATReg = getATReg(Inst.getLoc()); if (!ATReg) return true; ImmReg = ATReg; } if (loadImmediate(ImmValue, ImmReg, Mips::NoRegister, isInt<32>(ImmValue), false, IDLoc, Out, STI)) return true; // $SrcReg > $ImmReg is equal to $ImmReg < $SrcReg TOut.emitRRR(OpCode, DstReg, ImmReg, SrcReg, IDLoc, STI); return false; } bool MipsAsmParser::expandAliasImmediate(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out, const MCSubtargetInfo *STI) { MipsTargetStreamer &TOut = getTargetStreamer(); assert(Inst.getNumOperands() == 3 && "Invalid operand count"); assert(Inst.getOperand(0).isReg() && Inst.getOperand(1).isReg() && Inst.getOperand(2).isImm() && "Invalid instruction operand."); unsigned ATReg = Mips::NoRegister; unsigned FinalDstReg = Mips::NoRegister; unsigned DstReg = Inst.getOperand(0).getReg(); unsigned SrcReg = Inst.getOperand(1).getReg(); int64_t ImmValue = Inst.getOperand(2).getImm(); bool Is32Bit = isInt<32>(ImmValue) || (!isGP64bit() && isUInt<32>(ImmValue)); unsigned FinalOpcode = Inst.getOpcode(); if (DstReg == SrcReg) { ATReg = getATReg(Inst.getLoc()); if (!ATReg) return true; FinalDstReg = DstReg; DstReg = ATReg; } if (!loadImmediate(ImmValue, DstReg, Mips::NoRegister, Is32Bit, false, Inst.getLoc(), Out, STI)) { switch (FinalOpcode) { default: llvm_unreachable("unimplemented expansion"); case Mips::ADDi: FinalOpcode = Mips::ADD; break; case Mips::ADDiu: FinalOpcode = Mips::ADDu; break; case Mips::ANDi: FinalOpcode = Mips::AND; break; case Mips::NORImm: FinalOpcode = Mips::NOR; break; case Mips::ORi: FinalOpcode = Mips::OR; break; case Mips::SLTi: FinalOpcode = Mips::SLT; break; case Mips::SLTiu: FinalOpcode = Mips::SLTu; break; case Mips::XORi: FinalOpcode = Mips::XOR; break; case Mips::ADDi_MM: FinalOpcode = Mips::ADD_MM; break; case Mips::ADDiu_MM: FinalOpcode = Mips::ADDu_MM; break; case Mips::ANDi_MM: FinalOpcode = Mips::AND_MM; break; case Mips::ORi_MM: FinalOpcode = Mips::OR_MM; break; case Mips::SLTi_MM: FinalOpcode = Mips::SLT_MM; break; case Mips::SLTiu_MM: FinalOpcode = Mips::SLTu_MM; break; case Mips::XORi_MM: FinalOpcode = Mips::XOR_MM; break; case Mips::ANDi64: FinalOpcode = Mips::AND64; break; case Mips::NORImm64: FinalOpcode = Mips::NOR64; break; case Mips::ORi64: FinalOpcode = Mips::OR64; break; case Mips::SLTImm64: FinalOpcode = Mips::SLT64; break; case Mips::SLTUImm64: FinalOpcode = Mips::SLTu64; break; case Mips::XORi64: FinalOpcode = Mips::XOR64; break; } if (FinalDstReg == Mips::NoRegister) TOut.emitRRR(FinalOpcode, DstReg, DstReg, SrcReg, IDLoc, STI); else TOut.emitRRR(FinalOpcode, FinalDstReg, FinalDstReg, DstReg, IDLoc, STI); return false; } return true; } bool MipsAsmParser::expandRotation(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out, const MCSubtargetInfo *STI) { MipsTargetStreamer &TOut = getTargetStreamer(); unsigned ATReg = Mips::NoRegister; unsigned DReg = Inst.getOperand(0).getReg(); unsigned SReg = Inst.getOperand(1).getReg(); unsigned TReg = Inst.getOperand(2).getReg(); unsigned TmpReg = DReg; unsigned FirstShift = Mips::NOP; unsigned SecondShift = Mips::NOP; if (hasMips32r2()) { if (DReg == SReg) { TmpReg = getATReg(Inst.getLoc()); if (!TmpReg) return true; } if (Inst.getOpcode() == Mips::ROL) { TOut.emitRRR(Mips::SUBu, TmpReg, Mips::ZERO, TReg, Inst.getLoc(), STI); TOut.emitRRR(Mips::ROTRV, DReg, SReg, TmpReg, Inst.getLoc(), STI); return false; } if (Inst.getOpcode() == Mips::ROR) { TOut.emitRRR(Mips::ROTRV, DReg, SReg, TReg, Inst.getLoc(), STI); return false; } return true; } if (hasMips32()) { switch (Inst.getOpcode()) { default: llvm_unreachable("unexpected instruction opcode"); case Mips::ROL: FirstShift = Mips::SRLV; SecondShift = Mips::SLLV; break; case Mips::ROR: FirstShift = Mips::SLLV; SecondShift = Mips::SRLV; break; } ATReg = getATReg(Inst.getLoc()); if (!ATReg) return true; TOut.emitRRR(Mips::SUBu, ATReg, Mips::ZERO, TReg, Inst.getLoc(), STI); TOut.emitRRR(FirstShift, ATReg, SReg, ATReg, Inst.getLoc(), STI); TOut.emitRRR(SecondShift, DReg, SReg, TReg, Inst.getLoc(), STI); TOut.emitRRR(Mips::OR, DReg, DReg, ATReg, Inst.getLoc(), STI); return false; } return true; } bool MipsAsmParser::expandRotationImm(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out, const MCSubtargetInfo *STI) { MipsTargetStreamer &TOut = getTargetStreamer(); unsigned ATReg = Mips::NoRegister; unsigned DReg = Inst.getOperand(0).getReg(); unsigned SReg = Inst.getOperand(1).getReg(); int64_t ImmValue = Inst.getOperand(2).getImm(); unsigned FirstShift = Mips::NOP; unsigned SecondShift = Mips::NOP; if (hasMips32r2()) { if (Inst.getOpcode() == Mips::ROLImm) { uint64_t MaxShift = 32; uint64_t ShiftValue = ImmValue; if (ImmValue != 0) ShiftValue = MaxShift - ImmValue; TOut.emitRRI(Mips::ROTR, DReg, SReg, ShiftValue, Inst.getLoc(), STI); return false; } if (Inst.getOpcode() == Mips::RORImm) { TOut.emitRRI(Mips::ROTR, DReg, SReg, ImmValue, Inst.getLoc(), STI); return false; } return true; } if (hasMips32()) { if (ImmValue == 0) { TOut.emitRRI(Mips::SRL, DReg, SReg, 0, Inst.getLoc(), STI); return false; } switch (Inst.getOpcode()) { default: llvm_unreachable("unexpected instruction opcode"); case Mips::ROLImm: FirstShift = Mips::SLL; SecondShift = Mips::SRL; break; case Mips::RORImm: FirstShift = Mips::SRL; SecondShift = Mips::SLL; break; } ATReg = getATReg(Inst.getLoc()); if (!ATReg) return true; TOut.emitRRI(FirstShift, ATReg, SReg, ImmValue, Inst.getLoc(), STI); TOut.emitRRI(SecondShift, DReg, SReg, 32 - ImmValue, Inst.getLoc(), STI); TOut.emitRRR(Mips::OR, DReg, DReg, ATReg, Inst.getLoc(), STI); return false; } return true; } bool MipsAsmParser::expandDRotation(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out, const MCSubtargetInfo *STI) { MipsTargetStreamer &TOut = getTargetStreamer(); unsigned ATReg = Mips::NoRegister; unsigned DReg = Inst.getOperand(0).getReg(); unsigned SReg = Inst.getOperand(1).getReg(); unsigned TReg = Inst.getOperand(2).getReg(); unsigned TmpReg = DReg; unsigned FirstShift = Mips::NOP; unsigned SecondShift = Mips::NOP; if (hasMips64r2()) { if (TmpReg == SReg) { TmpReg = getATReg(Inst.getLoc()); if (!TmpReg) return true; } if (Inst.getOpcode() == Mips::DROL) { TOut.emitRRR(Mips::DSUBu, TmpReg, Mips::ZERO, TReg, Inst.getLoc(), STI); TOut.emitRRR(Mips::DROTRV, DReg, SReg, TmpReg, Inst.getLoc(), STI); return false; } if (Inst.getOpcode() == Mips::DROR) { TOut.emitRRR(Mips::DROTRV, DReg, SReg, TReg, Inst.getLoc(), STI); return false; } return true; } if (hasMips64()) { switch (Inst.getOpcode()) { default: llvm_unreachable("unexpected instruction opcode"); case Mips::DROL: FirstShift = Mips::DSRLV; SecondShift = Mips::DSLLV; break; case Mips::DROR: FirstShift = Mips::DSLLV; SecondShift = Mips::DSRLV; break; } ATReg = getATReg(Inst.getLoc()); if (!ATReg) return true; TOut.emitRRR(Mips::DSUBu, ATReg, Mips::ZERO, TReg, Inst.getLoc(), STI); TOut.emitRRR(FirstShift, ATReg, SReg, ATReg, Inst.getLoc(), STI); TOut.emitRRR(SecondShift, DReg, SReg, TReg, Inst.getLoc(), STI); TOut.emitRRR(Mips::OR, DReg, DReg, ATReg, Inst.getLoc(), STI); return false; } return true; } bool MipsAsmParser::expandDRotationImm(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out, const MCSubtargetInfo *STI) { MipsTargetStreamer &TOut = getTargetStreamer(); unsigned ATReg = Mips::NoRegister; unsigned DReg = Inst.getOperand(0).getReg(); unsigned SReg = Inst.getOperand(1).getReg(); int64_t ImmValue = Inst.getOperand(2).getImm() % 64; unsigned FirstShift = Mips::NOP; unsigned SecondShift = Mips::NOP; MCInst TmpInst; if (hasMips64r2()) { unsigned FinalOpcode = Mips::NOP; if (ImmValue == 0) FinalOpcode = Mips::DROTR; else if (ImmValue % 32 == 0) FinalOpcode = Mips::DROTR32; else if ((ImmValue >= 1) && (ImmValue <= 32)) { if (Inst.getOpcode() == Mips::DROLImm) FinalOpcode = Mips::DROTR32; else FinalOpcode = Mips::DROTR; } else if (ImmValue >= 33) { if (Inst.getOpcode() == Mips::DROLImm) FinalOpcode = Mips::DROTR; else FinalOpcode = Mips::DROTR32; } uint64_t ShiftValue = ImmValue % 32; if (Inst.getOpcode() == Mips::DROLImm) ShiftValue = (32 - ImmValue % 32) % 32; TOut.emitRRI(FinalOpcode, DReg, SReg, ShiftValue, Inst.getLoc(), STI); return false; } if (hasMips64()) { if (ImmValue == 0) { TOut.emitRRI(Mips::DSRL, DReg, SReg, 0, Inst.getLoc(), STI); return false; } switch (Inst.getOpcode()) { default: llvm_unreachable("unexpected instruction opcode"); case Mips::DROLImm: if ((ImmValue >= 1) && (ImmValue <= 31)) { FirstShift = Mips::DSLL; SecondShift = Mips::DSRL32; } if (ImmValue == 32) { FirstShift = Mips::DSLL32; SecondShift = Mips::DSRL32; } if ((ImmValue >= 33) && (ImmValue <= 63)) { FirstShift = Mips::DSLL32; SecondShift = Mips::DSRL; } break; case Mips::DRORImm: if ((ImmValue >= 1) && (ImmValue <= 31)) { FirstShift = Mips::DSRL; SecondShift = Mips::DSLL32; } if (ImmValue == 32) { FirstShift = Mips::DSRL32; SecondShift = Mips::DSLL32; } if ((ImmValue >= 33) && (ImmValue <= 63)) { FirstShift = Mips::DSRL32; SecondShift = Mips::DSLL; } break; } ATReg = getATReg(Inst.getLoc()); if (!ATReg) return true; TOut.emitRRI(FirstShift, ATReg, SReg, ImmValue % 32, Inst.getLoc(), STI); TOut.emitRRI(SecondShift, DReg, SReg, (32 - ImmValue % 32) % 32, Inst.getLoc(), STI); TOut.emitRRR(Mips::OR, DReg, DReg, ATReg, Inst.getLoc(), STI); return false; } return true; } bool MipsAsmParser::expandAbs(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out, const MCSubtargetInfo *STI) { MipsTargetStreamer &TOut = getTargetStreamer(); unsigned FirstRegOp = Inst.getOperand(0).getReg(); unsigned SecondRegOp = Inst.getOperand(1).getReg(); TOut.emitRI(Mips::BGEZ, SecondRegOp, 8, IDLoc, STI); if (FirstRegOp != SecondRegOp) TOut.emitRRR(Mips::ADDu, FirstRegOp, SecondRegOp, Mips::ZERO, IDLoc, STI); else TOut.emitEmptyDelaySlot(false, IDLoc, STI); TOut.emitRRR(Mips::SUB, FirstRegOp, Mips::ZERO, SecondRegOp, IDLoc, STI); return false; } bool MipsAsmParser::expandMulImm(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out, const MCSubtargetInfo *STI) { MipsTargetStreamer &TOut = getTargetStreamer(); unsigned ATReg = Mips::NoRegister; unsigned DstReg = Inst.getOperand(0).getReg(); unsigned SrcReg = Inst.getOperand(1).getReg(); int32_t ImmValue = Inst.getOperand(2).getImm(); ATReg = getATReg(IDLoc); if (!ATReg) return true; loadImmediate(ImmValue, ATReg, Mips::NoRegister, true, false, IDLoc, Out, STI); TOut.emitRR(Inst.getOpcode() == Mips::MULImmMacro ? Mips::MULT : Mips::DMULT, SrcReg, ATReg, IDLoc, STI); TOut.emitR(Mips::MFLO, DstReg, IDLoc, STI); return false; } bool MipsAsmParser::expandMulO(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out, const MCSubtargetInfo *STI) { MipsTargetStreamer &TOut = getTargetStreamer(); unsigned ATReg = Mips::NoRegister; unsigned DstReg = Inst.getOperand(0).getReg(); unsigned SrcReg = Inst.getOperand(1).getReg(); unsigned TmpReg = Inst.getOperand(2).getReg(); ATReg = getATReg(Inst.getLoc()); if (!ATReg) return true; TOut.emitRR(Inst.getOpcode() == Mips::MULOMacro ? Mips::MULT : Mips::DMULT, SrcReg, TmpReg, IDLoc, STI); TOut.emitR(Mips::MFLO, DstReg, IDLoc, STI); TOut.emitRRI(Inst.getOpcode() == Mips::MULOMacro ? Mips::SRA : Mips::DSRA32, DstReg, DstReg, 0x1F, IDLoc, STI); TOut.emitR(Mips::MFHI, ATReg, IDLoc, STI); if (useTraps()) { TOut.emitRRI(Mips::TNE, DstReg, ATReg, 6, IDLoc, STI); } else { MCContext & Context = TOut.getStreamer().getContext(); MCSymbol * BrTarget = Context.createTempSymbol(); MCOperand LabelOp = MCOperand::createExpr(MCSymbolRefExpr::create(BrTarget, Context)); TOut.emitRRX(Mips::BEQ, DstReg, ATReg, LabelOp, IDLoc, STI); if (AssemblerOptions.back()->isReorder()) TOut.emitNop(IDLoc, STI); TOut.emitII(Mips::BREAK, 6, 0, IDLoc, STI); TOut.getStreamer().EmitLabel(BrTarget); } TOut.emitR(Mips::MFLO, DstReg, IDLoc, STI); return false; } bool MipsAsmParser::expandMulOU(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out, const MCSubtargetInfo *STI) { MipsTargetStreamer &TOut = getTargetStreamer(); unsigned ATReg = Mips::NoRegister; unsigned DstReg = Inst.getOperand(0).getReg(); unsigned SrcReg = Inst.getOperand(1).getReg(); unsigned TmpReg = Inst.getOperand(2).getReg(); ATReg = getATReg(IDLoc); if (!ATReg) return true; TOut.emitRR(Inst.getOpcode() == Mips::MULOUMacro ? Mips::MULTu : Mips::DMULTu, SrcReg, TmpReg, IDLoc, STI); TOut.emitR(Mips::MFHI, ATReg, IDLoc, STI); TOut.emitR(Mips::MFLO, DstReg, IDLoc, STI); if (useTraps()) { TOut.emitRRI(Mips::TNE, ATReg, Mips::ZERO, 6, IDLoc, STI); } else { MCContext & Context = TOut.getStreamer().getContext(); MCSymbol * BrTarget = Context.createTempSymbol(); MCOperand LabelOp = MCOperand::createExpr(MCSymbolRefExpr::create(BrTarget, Context)); TOut.emitRRX(Mips::BEQ, ATReg, Mips::ZERO, LabelOp, IDLoc, STI); if (AssemblerOptions.back()->isReorder()) TOut.emitNop(IDLoc, STI); TOut.emitII(Mips::BREAK, 6, 0, IDLoc, STI); TOut.getStreamer().EmitLabel(BrTarget); } return false; } bool MipsAsmParser::expandDMULMacro(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out, const MCSubtargetInfo *STI) { MipsTargetStreamer &TOut = getTargetStreamer(); unsigned DstReg = Inst.getOperand(0).getReg(); unsigned SrcReg = Inst.getOperand(1).getReg(); unsigned TmpReg = Inst.getOperand(2).getReg(); TOut.emitRR(Mips::DMULTu, SrcReg, TmpReg, IDLoc, STI); TOut.emitR(Mips::MFLO, DstReg, IDLoc, STI); return false; } // Expand 'ld $ offset($reg2)' to 'lw $, offset($reg2); // lw $>, offset+4($reg2)' // or expand 'sd $ offset($reg2)' to 'sw $, offset($reg2); // sw $>, offset+4($reg2)' // for O32. bool MipsAsmParser::expandLoadStoreDMacro(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out, const MCSubtargetInfo *STI, bool IsLoad) { if (!isABI_O32()) return true; warnIfNoMacro(IDLoc); MipsTargetStreamer &TOut = getTargetStreamer(); unsigned Opcode = IsLoad ? Mips::LW : Mips::SW; unsigned FirstReg = Inst.getOperand(0).getReg(); unsigned SecondReg = nextReg(FirstReg); unsigned BaseReg = Inst.getOperand(1).getReg(); if (!SecondReg) return true; warnIfRegIndexIsAT(FirstReg, IDLoc); assert(Inst.getOperand(2).isImm() && "Offset for load macro is not immediate!"); MCOperand &FirstOffset = Inst.getOperand(2); signed NextOffset = FirstOffset.getImm() + 4; MCOperand SecondOffset = MCOperand::createImm(NextOffset); if (!isInt<16>(FirstOffset.getImm()) || !isInt<16>(NextOffset)) return true; // For loads, clobber the base register with the second load instead of the // first if the BaseReg == FirstReg. if (FirstReg != BaseReg || !IsLoad) { TOut.emitRRX(Opcode, FirstReg, BaseReg, FirstOffset, IDLoc, STI); TOut.emitRRX(Opcode, SecondReg, BaseReg, SecondOffset, IDLoc, STI); } else { TOut.emitRRX(Opcode, SecondReg, BaseReg, SecondOffset, IDLoc, STI); TOut.emitRRX(Opcode, FirstReg, BaseReg, FirstOffset, IDLoc, STI); } return false; } // Expand 's.d $ offset($reg2)' to 'swc1 $, offset($reg2); // swc1 $, offset+4($reg2)' // or if little endian to 'swc1 $, offset($reg2); // swc1 $, offset+4($reg2)' // for Mips1. bool MipsAsmParser::expandStoreDM1Macro(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out, const MCSubtargetInfo *STI) { if (!isABI_O32()) return true; warnIfNoMacro(IDLoc); MipsTargetStreamer &TOut = getTargetStreamer(); unsigned Opcode = Mips::SWC1; unsigned FirstReg = Inst.getOperand(0).getReg(); unsigned SecondReg = nextReg(FirstReg); unsigned BaseReg = Inst.getOperand(1).getReg(); if (!SecondReg) return true; warnIfRegIndexIsAT(FirstReg, IDLoc); assert(Inst.getOperand(2).isImm() && "Offset for macro is not immediate!"); MCOperand &FirstOffset = Inst.getOperand(2); signed NextOffset = FirstOffset.getImm() + 4; MCOperand SecondOffset = MCOperand::createImm(NextOffset); if (!isInt<16>(FirstOffset.getImm()) || !isInt<16>(NextOffset)) return true; if (!IsLittleEndian) std::swap(FirstReg, SecondReg); TOut.emitRRX(Opcode, FirstReg, BaseReg, FirstOffset, IDLoc, STI); TOut.emitRRX(Opcode, SecondReg, BaseReg, SecondOffset, IDLoc, STI); return false; } bool MipsAsmParser::expandSeq(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out, const MCSubtargetInfo *STI) { MipsTargetStreamer &TOut = getTargetStreamer(); assert(Inst.getNumOperands() == 3 && "Invalid operand count"); assert(Inst.getOperand(0).isReg() && Inst.getOperand(1).isReg() && Inst.getOperand(2).isReg() && "Invalid instruction operand."); unsigned DstReg = Inst.getOperand(0).getReg(); unsigned SrcReg = Inst.getOperand(1).getReg(); unsigned OpReg = Inst.getOperand(2).getReg(); warnIfNoMacro(IDLoc); if (SrcReg != Mips::ZERO && OpReg != Mips::ZERO) { TOut.emitRRR(Mips::XOR, DstReg, SrcReg, OpReg, IDLoc, STI); TOut.emitRRI(Mips::SLTiu, DstReg, DstReg, 1, IDLoc, STI); return false; } unsigned Reg = SrcReg == Mips::ZERO ? OpReg : SrcReg; TOut.emitRRI(Mips::SLTiu, DstReg, Reg, 1, IDLoc, STI); return false; } bool MipsAsmParser::expandSeqI(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out, const MCSubtargetInfo *STI) { MipsTargetStreamer &TOut = getTargetStreamer(); assert(Inst.getNumOperands() == 3 && "Invalid operand count"); assert(Inst.getOperand(0).isReg() && Inst.getOperand(1).isReg() && Inst.getOperand(2).isImm() && "Invalid instruction operand."); unsigned DstReg = Inst.getOperand(0).getReg(); unsigned SrcReg = Inst.getOperand(1).getReg(); int64_t Imm = Inst.getOperand(2).getImm(); warnIfNoMacro(IDLoc); if (Imm == 0) { TOut.emitRRI(Mips::SLTiu, DstReg, SrcReg, 1, IDLoc, STI); return false; } if (SrcReg == Mips::ZERO) { Warning(IDLoc, "comparison is always false"); TOut.emitRRR(isGP64bit() ? Mips::DADDu : Mips::ADDu, DstReg, SrcReg, SrcReg, IDLoc, STI); return false; } unsigned Opc; if (Imm > -0x8000 && Imm < 0) { Imm = -Imm; Opc = isGP64bit() ? Mips::DADDiu : Mips::ADDiu; } else { Opc = Mips::XORi; } if (!isUInt<16>(Imm)) { unsigned ATReg = getATReg(IDLoc); if (!ATReg) return true; if (loadImmediate(Imm, ATReg, Mips::NoRegister, true, isGP64bit(), IDLoc, Out, STI)) return true; TOut.emitRRR(Mips::XOR, DstReg, SrcReg, ATReg, IDLoc, STI); TOut.emitRRI(Mips::SLTiu, DstReg, DstReg, 1, IDLoc, STI); return false; } TOut.emitRRI(Opc, DstReg, SrcReg, Imm, IDLoc, STI); TOut.emitRRI(Mips::SLTiu, DstReg, DstReg, 1, IDLoc, STI); return false; } // Map the DSP accumulator and control register to the corresponding gpr // operand. Unlike the other alias, the m(f|t)t(lo|hi|acx) instructions // do not map the DSP registers contigously to gpr registers. static unsigned getRegisterForMxtrDSP(MCInst &Inst, bool IsMFDSP) { switch (Inst.getOpcode()) { case Mips::MFTLO: case Mips::MTTLO: switch (Inst.getOperand(IsMFDSP ? 1 : 0).getReg()) { case Mips::AC0: return Mips::ZERO; case Mips::AC1: return Mips::A0; case Mips::AC2: return Mips::T0; case Mips::AC3: return Mips::T4; default: llvm_unreachable("Unknown register for 'mttr' alias!"); } case Mips::MFTHI: case Mips::MTTHI: switch (Inst.getOperand(IsMFDSP ? 1 : 0).getReg()) { case Mips::AC0: return Mips::AT; case Mips::AC1: return Mips::A1; case Mips::AC2: return Mips::T1; case Mips::AC3: return Mips::T5; default: llvm_unreachable("Unknown register for 'mttr' alias!"); } case Mips::MFTACX: case Mips::MTTACX: switch (Inst.getOperand(IsMFDSP ? 1 : 0).getReg()) { case Mips::AC0: return Mips::V0; case Mips::AC1: return Mips::A2; case Mips::AC2: return Mips::T2; case Mips::AC3: return Mips::T6; default: llvm_unreachable("Unknown register for 'mttr' alias!"); } case Mips::MFTDSP: case Mips::MTTDSP: return Mips::S0; default: llvm_unreachable("Unknown instruction for 'mttr' dsp alias!"); } } // Map the floating point register operand to the corresponding register // operand. static unsigned getRegisterForMxtrFP(MCInst &Inst, bool IsMFTC1) { switch (Inst.getOperand(IsMFTC1 ? 1 : 0).getReg()) { case Mips::F0: return Mips::ZERO; case Mips::F1: return Mips::AT; case Mips::F2: return Mips::V0; case Mips::F3: return Mips::V1; case Mips::F4: return Mips::A0; case Mips::F5: return Mips::A1; case Mips::F6: return Mips::A2; case Mips::F7: return Mips::A3; case Mips::F8: return Mips::T0; case Mips::F9: return Mips::T1; case Mips::F10: return Mips::T2; case Mips::F11: return Mips::T3; case Mips::F12: return Mips::T4; case Mips::F13: return Mips::T5; case Mips::F14: return Mips::T6; case Mips::F15: return Mips::T7; case Mips::F16: return Mips::S0; case Mips::F17: return Mips::S1; case Mips::F18: return Mips::S2; case Mips::F19: return Mips::S3; case Mips::F20: return Mips::S4; case Mips::F21: return Mips::S5; case Mips::F22: return Mips::S6; case Mips::F23: return Mips::S7; case Mips::F24: return Mips::T8; case Mips::F25: return Mips::T9; case Mips::F26: return Mips::K0; case Mips::F27: return Mips::K1; case Mips::F28: return Mips::GP; case Mips::F29: return Mips::SP; case Mips::F30: return Mips::FP; case Mips::F31: return Mips::RA; default: llvm_unreachable("Unknown register for mttc1 alias!"); } } // Map the coprocessor operand the corresponding gpr register operand. static unsigned getRegisterForMxtrC0(MCInst &Inst, bool IsMFTC0) { switch (Inst.getOperand(IsMFTC0 ? 1 : 0).getReg()) { case Mips::COP00: return Mips::ZERO; case Mips::COP01: return Mips::AT; case Mips::COP02: return Mips::V0; case Mips::COP03: return Mips::V1; case Mips::COP04: return Mips::A0; case Mips::COP05: return Mips::A1; case Mips::COP06: return Mips::A2; case Mips::COP07: return Mips::A3; case Mips::COP08: return Mips::T0; case Mips::COP09: return Mips::T1; case Mips::COP010: return Mips::T2; case Mips::COP011: return Mips::T3; case Mips::COP012: return Mips::T4; case Mips::COP013: return Mips::T5; case Mips::COP014: return Mips::T6; case Mips::COP015: return Mips::T7; case Mips::COP016: return Mips::S0; case Mips::COP017: return Mips::S1; case Mips::COP018: return Mips::S2; case Mips::COP019: return Mips::S3; case Mips::COP020: return Mips::S4; case Mips::COP021: return Mips::S5; case Mips::COP022: return Mips::S6; case Mips::COP023: return Mips::S7; case Mips::COP024: return Mips::T8; case Mips::COP025: return Mips::T9; case Mips::COP026: return Mips::K0; case Mips::COP027: return Mips::K1; case Mips::COP028: return Mips::GP; case Mips::COP029: return Mips::SP; case Mips::COP030: return Mips::FP; case Mips::COP031: return Mips::RA; default: llvm_unreachable("Unknown register for mttc0 alias!"); } } /// Expand an alias of 'mftr' or 'mttr' into the full instruction, by producing /// an mftr or mttr with the correctly mapped gpr register, u, sel and h bits. bool MipsAsmParser::expandMXTRAlias(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out, const MCSubtargetInfo *STI) { MipsTargetStreamer &TOut = getTargetStreamer(); unsigned rd = 0; unsigned u = 1; unsigned sel = 0; unsigned h = 0; bool IsMFTR = false; switch (Inst.getOpcode()) { case Mips::MFTC0: IsMFTR = true; LLVM_FALLTHROUGH; case Mips::MTTC0: u = 0; rd = getRegisterForMxtrC0(Inst, IsMFTR); sel = Inst.getOperand(2).getImm(); break; case Mips::MFTGPR: IsMFTR = true; LLVM_FALLTHROUGH; case Mips::MTTGPR: rd = Inst.getOperand(IsMFTR ? 1 : 0).getReg(); break; case Mips::MFTLO: case Mips::MFTHI: case Mips::MFTACX: case Mips::MFTDSP: IsMFTR = true; LLVM_FALLTHROUGH; case Mips::MTTLO: case Mips::MTTHI: case Mips::MTTACX: case Mips::MTTDSP: rd = getRegisterForMxtrDSP(Inst, IsMFTR); sel = 1; break; case Mips::MFTHC1: h = 1; LLVM_FALLTHROUGH; case Mips::MFTC1: IsMFTR = true; rd = getRegisterForMxtrFP(Inst, IsMFTR); sel = 2; break; case Mips::MTTHC1: h = 1; LLVM_FALLTHROUGH; case Mips::MTTC1: rd = getRegisterForMxtrFP(Inst, IsMFTR); sel = 2; break; case Mips::CFTC1: IsMFTR = true; LLVM_FALLTHROUGH; case Mips::CTTC1: rd = getRegisterForMxtrFP(Inst, IsMFTR); sel = 3; break; } unsigned Op0 = IsMFTR ? Inst.getOperand(0).getReg() : rd; unsigned Op1 = IsMFTR ? rd : (Inst.getOpcode() != Mips::MTTDSP ? Inst.getOperand(1).getReg() : Inst.getOperand(0).getReg()); TOut.emitRRIII(IsMFTR ? Mips::MFTR : Mips::MTTR, Op0, Op1, u, sel, h, IDLoc, STI); + return false; +} + +bool MipsAsmParser::expandSaaAddr(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out, + const MCSubtargetInfo *STI) { + assert(Inst.getNumOperands() == 3 && "expected three operands"); + assert(Inst.getOperand(0).isReg() && "expected register operand kind"); + assert(Inst.getOperand(1).isReg() && "expected register operand kind"); + + warnIfNoMacro(IDLoc); + + MipsTargetStreamer &TOut = getTargetStreamer(); + unsigned Opcode = Inst.getOpcode() == Mips::SaaAddr ? Mips::SAA : Mips::SAAD; + unsigned RtReg = Inst.getOperand(0).getReg(); + unsigned BaseReg = Inst.getOperand(1).getReg(); + const MCOperand &BaseOp = Inst.getOperand(2); + + if (BaseOp.isImm()) { + int64_t ImmValue = BaseOp.getImm(); + if (ImmValue == 0) { + TOut.emitRR(Opcode, RtReg, BaseReg, IDLoc, STI); + return false; + } + } + + unsigned ATReg = getATReg(IDLoc); + if (!ATReg) + return true; + + if (expandLoadAddress(ATReg, BaseReg, BaseOp, !isGP64bit(), IDLoc, Out, STI)) + return true; + + TOut.emitRR(Opcode, RtReg, ATReg, IDLoc, STI); return false; } unsigned MipsAsmParser::checkEarlyTargetMatchPredicate(MCInst &Inst, const OperandVector &Operands) { switch (Inst.getOpcode()) { default: return Match_Success; case Mips::DATI: case Mips::DAHI: if (static_cast(*Operands[1]) .isValidForTie(static_cast(*Operands[2]))) return Match_Success; return Match_RequiresSameSrcAndDst; } } unsigned MipsAsmParser::checkTargetMatchPredicate(MCInst &Inst) { switch (Inst.getOpcode()) { // As described by the MIPSR6 spec, daui must not use the zero operand for // its source operand. case Mips::DAUI: if (Inst.getOperand(1).getReg() == Mips::ZERO || Inst.getOperand(1).getReg() == Mips::ZERO_64) return Match_RequiresNoZeroRegister; return Match_Success; // As described by the Mips32r2 spec, the registers Rd and Rs for // jalr.hb must be different. // It also applies for registers Rt and Rs of microMIPSr6 jalrc.hb instruction // and registers Rd and Base for microMIPS lwp instruction case Mips::JALR_HB: case Mips::JALR_HB64: case Mips::JALRC_HB_MMR6: case Mips::JALRC_MMR6: if (Inst.getOperand(0).getReg() == Inst.getOperand(1).getReg()) return Match_RequiresDifferentSrcAndDst; return Match_Success; case Mips::LWP_MM: if (Inst.getOperand(0).getReg() == Inst.getOperand(2).getReg()) return Match_RequiresDifferentSrcAndDst; return Match_Success; case Mips::SYNC: if (Inst.getOperand(0).getImm() != 0 && !hasMips32()) return Match_NonZeroOperandForSync; return Match_Success; case Mips::MFC0: case Mips::MTC0: case Mips::MTC2: case Mips::MFC2: if (Inst.getOperand(2).getImm() != 0 && !hasMips32()) return Match_NonZeroOperandForMTCX; return Match_Success; // As described the MIPSR6 spec, the compact branches that compare registers // must: // a) Not use the zero register. // b) Not use the same register twice. // c) rs < rt for bnec, beqc. // NB: For this case, the encoding will swap the operands as their // ordering doesn't matter. GAS performs this transformation too. // Hence, that constraint does not have to be enforced. // // The compact branches that branch iff the signed addition of two registers // would overflow must have rs >= rt. That can be handled like beqc/bnec with // operand swapping. They do not have restriction of using the zero register. 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: case Mips::BEQZC: case Mips::BEQZC_MMR6: case Mips::BNEZC: case Mips::BNEZC_MMR6: case Mips::BLEZC64: case Mips::BGEZC64: case Mips::BGTZC64: case Mips::BLTZC64: case Mips::BEQZC64: case Mips::BNEZC64: if (Inst.getOperand(0).getReg() == Mips::ZERO || Inst.getOperand(0).getReg() == Mips::ZERO_64) return Match_RequiresNoZeroRegister; return Match_Success; 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: case Mips::BGEC64: case Mips::BLTC64: case Mips::BGEUC64: case Mips::BLTUC64: case Mips::BEQC64: case Mips::BNEC64: if (Inst.getOperand(0).getReg() == Mips::ZERO || Inst.getOperand(0).getReg() == Mips::ZERO_64) return Match_RequiresNoZeroRegister; if (Inst.getOperand(1).getReg() == Mips::ZERO || Inst.getOperand(1).getReg() == Mips::ZERO_64) return Match_RequiresNoZeroRegister; if (Inst.getOperand(0).getReg() == Inst.getOperand(1).getReg()) return Match_RequiresDifferentOperands; return Match_Success; case Mips::DINS: { assert(Inst.getOperand(2).isImm() && Inst.getOperand(3).isImm() && "Operands must be immediates for dins!"); const signed Pos = Inst.getOperand(2).getImm(); const signed Size = Inst.getOperand(3).getImm(); if ((0 > (Pos + Size)) || ((Pos + Size) > 32)) return Match_RequiresPosSizeRange0_32; return Match_Success; } case Mips::DINSM: case Mips::DINSU: { assert(Inst.getOperand(2).isImm() && Inst.getOperand(3).isImm() && "Operands must be immediates for dinsm/dinsu!"); const signed Pos = Inst.getOperand(2).getImm(); const signed Size = Inst.getOperand(3).getImm(); if ((32 >= (Pos + Size)) || ((Pos + Size) > 64)) return Match_RequiresPosSizeRange33_64; return Match_Success; } case Mips::DEXT: { assert(Inst.getOperand(2).isImm() && Inst.getOperand(3).isImm() && "Operands must be immediates for DEXTM!"); const signed Pos = Inst.getOperand(2).getImm(); const signed Size = Inst.getOperand(3).getImm(); if ((1 > (Pos + Size)) || ((Pos + Size) > 63)) return Match_RequiresPosSizeUImm6; return Match_Success; } case Mips::DEXTM: case Mips::DEXTU: { assert(Inst.getOperand(2).isImm() && Inst.getOperand(3).isImm() && "Operands must be immediates for dextm/dextu!"); const signed Pos = Inst.getOperand(2).getImm(); const signed Size = Inst.getOperand(3).getImm(); if ((32 > (Pos + Size)) || ((Pos + Size) > 64)) return Match_RequiresPosSizeRange33_64; return Match_Success; } case Mips::CRC32B: case Mips::CRC32CB: case Mips::CRC32H: case Mips::CRC32CH: case Mips::CRC32W: case Mips::CRC32CW: case Mips::CRC32D: case Mips::CRC32CD: if (Inst.getOperand(0).getReg() != Inst.getOperand(2).getReg()) return Match_RequiresSameSrcAndDst; return Match_Success; } uint64_t TSFlags = getInstDesc(Inst.getOpcode()).TSFlags; if ((TSFlags & MipsII::HasFCCRegOperand) && (Inst.getOperand(0).getReg() != Mips::FCC0) && !hasEightFccRegisters()) return Match_NoFCCRegisterForCurrentISA; return Match_Success; } static SMLoc RefineErrorLoc(const SMLoc Loc, const OperandVector &Operands, uint64_t ErrorInfo) { if (ErrorInfo != ~0ULL && ErrorInfo < Operands.size()) { SMLoc ErrorLoc = Operands[ErrorInfo]->getStartLoc(); if (ErrorLoc == SMLoc()) return Loc; return ErrorLoc; } return Loc; } bool MipsAsmParser::MatchAndEmitInstruction(SMLoc IDLoc, unsigned &Opcode, OperandVector &Operands, MCStreamer &Out, uint64_t &ErrorInfo, bool MatchingInlineAsm) { MCInst Inst; unsigned MatchResult = MatchInstructionImpl(Operands, Inst, ErrorInfo, MatchingInlineAsm); switch (MatchResult) { case Match_Success: if (processInstruction(Inst, IDLoc, Out, STI)) return true; return false; case Match_MissingFeature: Error(IDLoc, "instruction requires a CPU feature not currently enabled"); return true; case Match_InvalidOperand: { SMLoc ErrorLoc = IDLoc; if (ErrorInfo != ~0ULL) { if (ErrorInfo >= Operands.size()) return Error(IDLoc, "too few operands for instruction"); ErrorLoc = Operands[ErrorInfo]->getStartLoc(); if (ErrorLoc == SMLoc()) ErrorLoc = IDLoc; } return Error(ErrorLoc, "invalid operand for instruction"); } case Match_NonZeroOperandForSync: return Error(IDLoc, "s-type must be zero or unspecified for pre-MIPS32 ISAs"); case Match_NonZeroOperandForMTCX: return Error(IDLoc, "selector must be zero for pre-MIPS32 ISAs"); case Match_MnemonicFail: return Error(IDLoc, "invalid instruction"); case Match_RequiresDifferentSrcAndDst: return Error(IDLoc, "source and destination must be different"); case Match_RequiresDifferentOperands: return Error(IDLoc, "registers must be different"); case Match_RequiresNoZeroRegister: return Error(IDLoc, "invalid operand ($zero) for instruction"); case Match_RequiresSameSrcAndDst: return Error(IDLoc, "source and destination must match"); case Match_NoFCCRegisterForCurrentISA: return Error(RefineErrorLoc(IDLoc, Operands, ErrorInfo), "non-zero fcc register doesn't exist in current ISA level"); case Match_Immz: return Error(RefineErrorLoc(IDLoc, Operands, ErrorInfo), "expected '0'"); case Match_UImm1_0: return Error(RefineErrorLoc(IDLoc, Operands, ErrorInfo), "expected 1-bit unsigned immediate"); case Match_UImm2_0: return Error(RefineErrorLoc(IDLoc, Operands, ErrorInfo), "expected 2-bit unsigned immediate"); case Match_UImm2_1: return Error(RefineErrorLoc(IDLoc, Operands, ErrorInfo), "expected immediate in range 1 .. 4"); case Match_UImm3_0: return Error(RefineErrorLoc(IDLoc, Operands, ErrorInfo), "expected 3-bit unsigned immediate"); case Match_UImm4_0: return Error(RefineErrorLoc(IDLoc, Operands, ErrorInfo), "expected 4-bit unsigned immediate"); case Match_SImm4_0: return Error(RefineErrorLoc(IDLoc, Operands, ErrorInfo), "expected 4-bit signed immediate"); case Match_UImm5_0: return Error(RefineErrorLoc(IDLoc, Operands, ErrorInfo), "expected 5-bit unsigned immediate"); case Match_SImm5_0: return Error(RefineErrorLoc(IDLoc, Operands, ErrorInfo), "expected 5-bit signed immediate"); case Match_UImm5_1: return Error(RefineErrorLoc(IDLoc, Operands, ErrorInfo), "expected immediate in range 1 .. 32"); case Match_UImm5_32: return Error(RefineErrorLoc(IDLoc, Operands, ErrorInfo), "expected immediate in range 32 .. 63"); case Match_UImm5_33: return Error(RefineErrorLoc(IDLoc, Operands, ErrorInfo), "expected immediate in range 33 .. 64"); case Match_UImm5_0_Report_UImm6: // This is used on UImm5 operands that have a corresponding UImm5_32 // operand to avoid confusing the user. return Error(RefineErrorLoc(IDLoc, Operands, ErrorInfo), "expected 6-bit unsigned immediate"); case Match_UImm5_Lsl2: return Error(RefineErrorLoc(IDLoc, Operands, ErrorInfo), "expected both 7-bit unsigned immediate and multiple of 4"); case Match_UImmRange2_64: return Error(RefineErrorLoc(IDLoc, Operands, ErrorInfo), "expected immediate in range 2 .. 64"); case Match_UImm6_0: return Error(RefineErrorLoc(IDLoc, Operands, ErrorInfo), "expected 6-bit unsigned immediate"); case Match_UImm6_Lsl2: return Error(RefineErrorLoc(IDLoc, Operands, ErrorInfo), "expected both 8-bit unsigned immediate and multiple of 4"); case Match_SImm6_0: return Error(RefineErrorLoc(IDLoc, Operands, ErrorInfo), "expected 6-bit signed immediate"); case Match_UImm7_0: return Error(RefineErrorLoc(IDLoc, Operands, ErrorInfo), "expected 7-bit unsigned immediate"); case Match_UImm7_N1: return Error(RefineErrorLoc(IDLoc, Operands, ErrorInfo), "expected immediate in range -1 .. 126"); case Match_SImm7_Lsl2: return Error(RefineErrorLoc(IDLoc, Operands, ErrorInfo), "expected both 9-bit signed immediate and multiple of 4"); case Match_UImm8_0: return Error(RefineErrorLoc(IDLoc, Operands, ErrorInfo), "expected 8-bit unsigned immediate"); case Match_UImm10_0: return Error(RefineErrorLoc(IDLoc, Operands, ErrorInfo), "expected 10-bit unsigned immediate"); case Match_SImm10_0: return Error(RefineErrorLoc(IDLoc, Operands, ErrorInfo), "expected 10-bit signed immediate"); case Match_SImm11_0: return Error(RefineErrorLoc(IDLoc, Operands, ErrorInfo), "expected 11-bit signed immediate"); case Match_UImm16: case Match_UImm16_Relaxed: case Match_UImm16_AltRelaxed: return Error(RefineErrorLoc(IDLoc, Operands, ErrorInfo), "expected 16-bit unsigned immediate"); case Match_SImm16: case Match_SImm16_Relaxed: return Error(RefineErrorLoc(IDLoc, Operands, ErrorInfo), "expected 16-bit signed immediate"); case Match_SImm19_Lsl2: return Error(RefineErrorLoc(IDLoc, Operands, ErrorInfo), "expected both 19-bit signed immediate and multiple of 4"); case Match_UImm20_0: return Error(RefineErrorLoc(IDLoc, Operands, ErrorInfo), "expected 20-bit unsigned immediate"); case Match_UImm26_0: return Error(RefineErrorLoc(IDLoc, Operands, ErrorInfo), "expected 26-bit unsigned immediate"); case Match_SImm32: case Match_SImm32_Relaxed: return Error(RefineErrorLoc(IDLoc, Operands, ErrorInfo), "expected 32-bit signed immediate"); case Match_UImm32_Coerced: return Error(RefineErrorLoc(IDLoc, Operands, ErrorInfo), "expected 32-bit immediate"); case Match_MemSImm9: return Error(RefineErrorLoc(IDLoc, Operands, ErrorInfo), "expected memory with 9-bit signed offset"); case Match_MemSImm10: return Error(RefineErrorLoc(IDLoc, Operands, ErrorInfo), "expected memory with 10-bit signed offset"); case Match_MemSImm10Lsl1: return Error(RefineErrorLoc(IDLoc, Operands, ErrorInfo), "expected memory with 11-bit signed offset and multiple of 2"); case Match_MemSImm10Lsl2: return Error(RefineErrorLoc(IDLoc, Operands, ErrorInfo), "expected memory with 12-bit signed offset and multiple of 4"); case Match_MemSImm10Lsl3: return Error(RefineErrorLoc(IDLoc, Operands, ErrorInfo), "expected memory with 13-bit signed offset and multiple of 8"); case Match_MemSImm11: return Error(RefineErrorLoc(IDLoc, Operands, ErrorInfo), "expected memory with 11-bit signed offset"); case Match_MemSImm12: return Error(RefineErrorLoc(IDLoc, Operands, ErrorInfo), "expected memory with 12-bit signed offset"); case Match_MemSImm16: return Error(RefineErrorLoc(IDLoc, Operands, ErrorInfo), "expected memory with 16-bit signed offset"); case Match_MemSImmPtr: return Error(RefineErrorLoc(IDLoc, Operands, ErrorInfo), "expected memory with 32-bit signed offset"); case Match_RequiresPosSizeRange0_32: { SMLoc ErrorStart = Operands[3]->getStartLoc(); SMLoc ErrorEnd = Operands[4]->getEndLoc(); return Error(ErrorStart, "size plus position are not in the range 0 .. 32", SMRange(ErrorStart, ErrorEnd)); } case Match_RequiresPosSizeUImm6: { SMLoc ErrorStart = Operands[3]->getStartLoc(); SMLoc ErrorEnd = Operands[4]->getEndLoc(); return Error(ErrorStart, "size plus position are not in the range 1 .. 63", SMRange(ErrorStart, ErrorEnd)); } case Match_RequiresPosSizeRange33_64: { SMLoc ErrorStart = Operands[3]->getStartLoc(); SMLoc ErrorEnd = Operands[4]->getEndLoc(); return Error(ErrorStart, "size plus position are not in the range 33 .. 64", SMRange(ErrorStart, ErrorEnd)); } } llvm_unreachable("Implement any new match types added!"); } void MipsAsmParser::warnIfRegIndexIsAT(unsigned RegIndex, SMLoc Loc) { if (RegIndex != 0 && AssemblerOptions.back()->getATRegIndex() == RegIndex) Warning(Loc, "used $at (currently $" + Twine(RegIndex) + ") without \".set noat\""); } void MipsAsmParser::warnIfNoMacro(SMLoc Loc) { if (!AssemblerOptions.back()->isMacro()) Warning(Loc, "macro instruction expanded into multiple instructions"); } void MipsAsmParser::ConvertXWPOperands(MCInst &Inst, const OperandVector &Operands) { assert( (Inst.getOpcode() == Mips::LWP_MM || Inst.getOpcode() == Mips::SWP_MM) && "Unexpected instruction!"); ((MipsOperand &)*Operands[1]).addGPR32ZeroAsmRegOperands(Inst, 1); int NextReg = nextReg(((MipsOperand &)*Operands[1]).getGPR32Reg()); Inst.addOperand(MCOperand::createReg(NextReg)); ((MipsOperand &)*Operands[2]).addMemOperands(Inst, 2); } void MipsAsmParser::printWarningWithFixIt(const Twine &Msg, const Twine &FixMsg, SMRange Range, bool ShowColors) { getSourceManager().PrintMessage(Range.Start, SourceMgr::DK_Warning, Msg, Range, SMFixIt(Range, FixMsg), ShowColors); } int MipsAsmParser::matchCPURegisterName(StringRef Name) { int CC; CC = StringSwitch(Name) .Case("zero", 0) .Cases("at", "AT", 1) .Case("a0", 4) .Case("a1", 5) .Case("a2", 6) .Case("a3", 7) .Case("v0", 2) .Case("v1", 3) .Case("s0", 16) .Case("s1", 17) .Case("s2", 18) .Case("s3", 19) .Case("s4", 20) .Case("s5", 21) .Case("s6", 22) .Case("s7", 23) .Case("k0", 26) .Case("k1", 27) .Case("gp", 28) .Case("sp", 29) .Case("fp", 30) .Case("s8", 30) .Case("ra", 31) .Case("t0", 8) .Case("t1", 9) .Case("t2", 10) .Case("t3", 11) .Case("t4", 12) .Case("t5", 13) .Case("t6", 14) .Case("t7", 15) .Case("t8", 24) .Case("t9", 25) .Default(-1); if (!(isABI_N32() || isABI_N64())) return CC; if (12 <= CC && CC <= 15) { // Name is one of t4-t7 AsmToken RegTok = getLexer().peekTok(); SMRange RegRange = RegTok.getLocRange(); StringRef FixedName = StringSwitch(Name) .Case("t4", "t0") .Case("t5", "t1") .Case("t6", "t2") .Case("t7", "t3") .Default(""); assert(FixedName != "" && "Register name is not one of t4-t7."); printWarningWithFixIt("register names $t4-$t7 are only available in O32.", "Did you mean $" + FixedName + "?", RegRange); } // Although SGI documentation just cuts out t0-t3 for n32/n64, // GNU pushes the values of t0-t3 to override the o32/o64 values for t4-t7 // We are supporting both cases, so for t0-t3 we'll just push them to t4-t7. if (8 <= CC && CC <= 11) CC += 4; if (CC == -1) CC = StringSwitch(Name) .Case("a4", 8) .Case("a5", 9) .Case("a6", 10) .Case("a7", 11) .Case("kt0", 26) .Case("kt1", 27) .Default(-1); return CC; } int MipsAsmParser::matchHWRegsRegisterName(StringRef Name) { int CC; CC = StringSwitch(Name) .Case("hwr_cpunum", 0) .Case("hwr_synci_step", 1) .Case("hwr_cc", 2) .Case("hwr_ccres", 3) .Case("hwr_ulr", 29) .Default(-1); return CC; } int MipsAsmParser::matchFPURegisterName(StringRef Name) { if (Name[0] == 'f') { StringRef NumString = Name.substr(1); unsigned IntVal; if (NumString.getAsInteger(10, IntVal)) return -1; // This is not an integer. if (IntVal > 31) // Maximum index for fpu register. return -1; return IntVal; } return -1; } int MipsAsmParser::matchFCCRegisterName(StringRef Name) { if (Name.startswith("fcc")) { StringRef NumString = Name.substr(3); unsigned IntVal; if (NumString.getAsInteger(10, IntVal)) return -1; // This is not an integer. if (IntVal > 7) // There are only 8 fcc registers. return -1; return IntVal; } return -1; } int MipsAsmParser::matchACRegisterName(StringRef Name) { if (Name.startswith("ac")) { StringRef NumString = Name.substr(2); unsigned IntVal; if (NumString.getAsInteger(10, IntVal)) return -1; // This is not an integer. if (IntVal > 3) // There are only 3 acc registers. return -1; return IntVal; } return -1; } int MipsAsmParser::matchMSA128RegisterName(StringRef Name) { unsigned IntVal; if (Name.front() != 'w' || Name.drop_front(1).getAsInteger(10, IntVal)) return -1; if (IntVal > 31) return -1; return IntVal; } int MipsAsmParser::matchMSA128CtrlRegisterName(StringRef Name) { int CC; CC = StringSwitch(Name) .Case("msair", 0) .Case("msacsr", 1) .Case("msaaccess", 2) .Case("msasave", 3) .Case("msamodify", 4) .Case("msarequest", 5) .Case("msamap", 6) .Case("msaunmap", 7) .Default(-1); return CC; } bool MipsAsmParser::canUseATReg() { return AssemblerOptions.back()->getATRegIndex() != 0; } unsigned MipsAsmParser::getATReg(SMLoc Loc) { unsigned ATIndex = AssemblerOptions.back()->getATRegIndex(); if (ATIndex == 0) { reportParseError(Loc, "pseudo-instruction requires $at, which is not available"); return 0; } unsigned AT = getReg( (isGP64bit()) ? Mips::GPR64RegClassID : Mips::GPR32RegClassID, ATIndex); return AT; } unsigned MipsAsmParser::getReg(int RC, int RegNo) { return *(getContext().getRegisterInfo()->getRegClass(RC).begin() + RegNo); } bool MipsAsmParser::parseOperand(OperandVector &Operands, StringRef Mnemonic) { MCAsmParser &Parser = getParser(); LLVM_DEBUG(dbgs() << "parseOperand\n"); // Check if the current operand has a custom associated parser, if so, try to // custom parse the operand, or fallback to the general approach. OperandMatchResultTy ResTy = MatchOperandParserImpl(Operands, Mnemonic); if (ResTy == MatchOperand_Success) return false; // If there wasn't a custom match, try the generic matcher below. Otherwise, // there was a match, but an error occurred, in which case, just return that // the operand parsing failed. if (ResTy == MatchOperand_ParseFail) return true; LLVM_DEBUG(dbgs() << ".. Generic Parser\n"); switch (getLexer().getKind()) { case AsmToken::Dollar: { // Parse the register. SMLoc S = Parser.getTok().getLoc(); // Almost all registers have been parsed by custom parsers. There is only // one exception to this. $zero (and it's alias $0) will reach this point // for div, divu, and similar instructions because it is not an operand // to the instruction definition but an explicit register. Special case // this situation for now. if (parseAnyRegister(Operands) != MatchOperand_NoMatch) return false; // Maybe it is a symbol reference. StringRef Identifier; if (Parser.parseIdentifier(Identifier)) return true; SMLoc E = SMLoc::getFromPointer(Parser.getTok().getLoc().getPointer() - 1); MCSymbol *Sym = getContext().getOrCreateSymbol("$" + Identifier); // Otherwise create a symbol reference. const MCExpr *Res = MCSymbolRefExpr::create(Sym, MCSymbolRefExpr::VK_None, getContext()); Operands.push_back(MipsOperand::CreateImm(Res, S, E, *this)); return false; } default: { LLVM_DEBUG(dbgs() << ".. generic integer expression\n"); const MCExpr *Expr; SMLoc S = Parser.getTok().getLoc(); // Start location of the operand. if (getParser().parseExpression(Expr)) return true; SMLoc E = SMLoc::getFromPointer(Parser.getTok().getLoc().getPointer() - 1); Operands.push_back(MipsOperand::CreateImm(Expr, S, E, *this)); return false; } } // switch(getLexer().getKind()) return true; } bool MipsAsmParser::isEvaluated(const MCExpr *Expr) { switch (Expr->getKind()) { case MCExpr::Constant: return true; case MCExpr::SymbolRef: return (cast(Expr)->getKind() != MCSymbolRefExpr::VK_None); case MCExpr::Binary: { const MCBinaryExpr *BE = cast(Expr); if (!isEvaluated(BE->getLHS())) return false; return isEvaluated(BE->getRHS()); } case MCExpr::Unary: return isEvaluated(cast(Expr)->getSubExpr()); case MCExpr::Target: return true; } return false; } bool MipsAsmParser::ParseRegister(unsigned &RegNo, SMLoc &StartLoc, SMLoc &EndLoc) { SmallVector, 1> Operands; OperandMatchResultTy ResTy = parseAnyRegister(Operands); if (ResTy == MatchOperand_Success) { assert(Operands.size() == 1); MipsOperand &Operand = static_cast(*Operands.front()); StartLoc = Operand.getStartLoc(); EndLoc = Operand.getEndLoc(); // AFAIK, we only support numeric registers and named GPR's in CFI // directives. // Don't worry about eating tokens before failing. Using an unrecognised // register is a parse error. if (Operand.isGPRAsmReg()) { // Resolve to GPR32 or GPR64 appropriately. RegNo = isGP64bit() ? Operand.getGPR64Reg() : Operand.getGPR32Reg(); } return (RegNo == (unsigned)-1); } assert(Operands.size() == 0); return (RegNo == (unsigned)-1); } bool MipsAsmParser::parseMemOffset(const MCExpr *&Res, bool isParenExpr) { SMLoc S; if (isParenExpr) return getParser().parseParenExprOfDepth(0, Res, S); return getParser().parseExpression(Res); } OperandMatchResultTy MipsAsmParser::parseMemOperand(OperandVector &Operands) { MCAsmParser &Parser = getParser(); LLVM_DEBUG(dbgs() << "parseMemOperand\n"); const MCExpr *IdVal = nullptr; SMLoc S; bool isParenExpr = false; OperandMatchResultTy Res = MatchOperand_NoMatch; // First operand is the offset. S = Parser.getTok().getLoc(); if (getLexer().getKind() == AsmToken::LParen) { Parser.Lex(); isParenExpr = true; } if (getLexer().getKind() != AsmToken::Dollar) { if (parseMemOffset(IdVal, isParenExpr)) return MatchOperand_ParseFail; const AsmToken &Tok = Parser.getTok(); // Get the next token. if (Tok.isNot(AsmToken::LParen)) { MipsOperand &Mnemonic = static_cast(*Operands[0]); if (Mnemonic.getToken() == "la" || Mnemonic.getToken() == "dla") { SMLoc E = SMLoc::getFromPointer(Parser.getTok().getLoc().getPointer() - 1); Operands.push_back(MipsOperand::CreateImm(IdVal, S, E, *this)); return MatchOperand_Success; } if (Tok.is(AsmToken::EndOfStatement)) { SMLoc E = SMLoc::getFromPointer(Parser.getTok().getLoc().getPointer() - 1); // Zero register assumed, add a memory operand with ZERO as its base. // "Base" will be managed by k_Memory. auto Base = MipsOperand::createGPRReg( 0, "0", getContext().getRegisterInfo(), S, E, *this); Operands.push_back( MipsOperand::CreateMem(std::move(Base), IdVal, S, E, *this)); return MatchOperand_Success; } MCBinaryExpr::Opcode Opcode; // GAS and LLVM treat comparison operators different. GAS will generate -1 // or 0, while LLVM will generate 0 or 1. Since a comparsion operator is // highly unlikely to be found in a memory offset expression, we don't // handle them. switch (Tok.getKind()) { case AsmToken::Plus: Opcode = MCBinaryExpr::Add; Parser.Lex(); break; case AsmToken::Minus: Opcode = MCBinaryExpr::Sub; Parser.Lex(); break; case AsmToken::Star: Opcode = MCBinaryExpr::Mul; Parser.Lex(); break; case AsmToken::Pipe: Opcode = MCBinaryExpr::Or; Parser.Lex(); break; case AsmToken::Amp: Opcode = MCBinaryExpr::And; Parser.Lex(); break; case AsmToken::LessLess: Opcode = MCBinaryExpr::Shl; Parser.Lex(); break; case AsmToken::GreaterGreater: Opcode = MCBinaryExpr::LShr; Parser.Lex(); break; case AsmToken::Caret: Opcode = MCBinaryExpr::Xor; Parser.Lex(); break; case AsmToken::Slash: Opcode = MCBinaryExpr::Div; Parser.Lex(); break; case AsmToken::Percent: Opcode = MCBinaryExpr::Mod; Parser.Lex(); break; default: Error(Parser.getTok().getLoc(), "'(' or expression expected"); return MatchOperand_ParseFail; } const MCExpr * NextExpr; if (getParser().parseExpression(NextExpr)) return MatchOperand_ParseFail; IdVal = MCBinaryExpr::create(Opcode, IdVal, NextExpr, getContext()); } Parser.Lex(); // Eat the '(' token. } Res = parseAnyRegister(Operands); if (Res != MatchOperand_Success) return Res; if (Parser.getTok().isNot(AsmToken::RParen)) { Error(Parser.getTok().getLoc(), "')' expected"); return MatchOperand_ParseFail; } SMLoc E = SMLoc::getFromPointer(Parser.getTok().getLoc().getPointer() - 1); Parser.Lex(); // Eat the ')' token. if (!IdVal) IdVal = MCConstantExpr::create(0, getContext()); // Replace the register operand with the memory operand. std::unique_ptr op( static_cast(Operands.back().release())); // Remove the register from the operands. // "op" will be managed by k_Memory. Operands.pop_back(); // Add the memory operand. if (const MCBinaryExpr *BE = dyn_cast(IdVal)) { int64_t Imm; if (IdVal->evaluateAsAbsolute(Imm)) IdVal = MCConstantExpr::create(Imm, getContext()); else if (BE->getLHS()->getKind() != MCExpr::SymbolRef) IdVal = MCBinaryExpr::create(BE->getOpcode(), BE->getRHS(), BE->getLHS(), getContext()); } Operands.push_back(MipsOperand::CreateMem(std::move(op), IdVal, S, E, *this)); return MatchOperand_Success; } bool MipsAsmParser::searchSymbolAlias(OperandVector &Operands) { MCAsmParser &Parser = getParser(); MCSymbol *Sym = getContext().lookupSymbol(Parser.getTok().getIdentifier()); if (!Sym) return false; SMLoc S = Parser.getTok().getLoc(); if (Sym->isVariable()) { const MCExpr *Expr = Sym->getVariableValue(); if (Expr->getKind() == MCExpr::SymbolRef) { const MCSymbolRefExpr *Ref = static_cast(Expr); StringRef DefSymbol = Ref->getSymbol().getName(); if (DefSymbol.startswith("$")) { OperandMatchResultTy ResTy = matchAnyRegisterNameWithoutDollar(Operands, DefSymbol.substr(1), S); if (ResTy == MatchOperand_Success) { Parser.Lex(); return true; } if (ResTy == MatchOperand_ParseFail) llvm_unreachable("Should never ParseFail"); } } } else if (Sym->isUnset()) { // If symbol is unset, it might be created in the `parseSetAssignment` // routine as an alias for a numeric register name. // Lookup in the aliases list. auto Entry = RegisterSets.find(Sym->getName()); if (Entry != RegisterSets.end()) { OperandMatchResultTy ResTy = matchAnyRegisterWithoutDollar(Operands, Entry->getValue(), S); if (ResTy == MatchOperand_Success) { Parser.Lex(); return true; } } } return false; } OperandMatchResultTy MipsAsmParser::matchAnyRegisterNameWithoutDollar(OperandVector &Operands, StringRef Identifier, SMLoc S) { int Index = matchCPURegisterName(Identifier); if (Index != -1) { Operands.push_back(MipsOperand::createGPRReg( Index, Identifier, getContext().getRegisterInfo(), S, getLexer().getLoc(), *this)); return MatchOperand_Success; } Index = matchHWRegsRegisterName(Identifier); if (Index != -1) { Operands.push_back(MipsOperand::createHWRegsReg( Index, Identifier, getContext().getRegisterInfo(), S, getLexer().getLoc(), *this)); return MatchOperand_Success; } Index = matchFPURegisterName(Identifier); if (Index != -1) { Operands.push_back(MipsOperand::createFGRReg( Index, Identifier, getContext().getRegisterInfo(), S, getLexer().getLoc(), *this)); return MatchOperand_Success; } Index = matchFCCRegisterName(Identifier); if (Index != -1) { Operands.push_back(MipsOperand::createFCCReg( Index, Identifier, getContext().getRegisterInfo(), S, getLexer().getLoc(), *this)); return MatchOperand_Success; } Index = matchACRegisterName(Identifier); if (Index != -1) { Operands.push_back(MipsOperand::createACCReg( Index, Identifier, getContext().getRegisterInfo(), S, getLexer().getLoc(), *this)); return MatchOperand_Success; } Index = matchMSA128RegisterName(Identifier); if (Index != -1) { Operands.push_back(MipsOperand::createMSA128Reg( Index, Identifier, getContext().getRegisterInfo(), S, getLexer().getLoc(), *this)); return MatchOperand_Success; } Index = matchMSA128CtrlRegisterName(Identifier); if (Index != -1) { Operands.push_back(MipsOperand::createMSACtrlReg( Index, Identifier, getContext().getRegisterInfo(), S, getLexer().getLoc(), *this)); return MatchOperand_Success; } return MatchOperand_NoMatch; } OperandMatchResultTy MipsAsmParser::matchAnyRegisterWithoutDollar(OperandVector &Operands, const AsmToken &Token, SMLoc S) { if (Token.is(AsmToken::Identifier)) { LLVM_DEBUG(dbgs() << ".. identifier\n"); StringRef Identifier = Token.getIdentifier(); OperandMatchResultTy ResTy = matchAnyRegisterNameWithoutDollar(Operands, Identifier, S); return ResTy; } else if (Token.is(AsmToken::Integer)) { LLVM_DEBUG(dbgs() << ".. integer\n"); int64_t RegNum = Token.getIntVal(); if (RegNum < 0 || RegNum > 31) { // Show the error, but treat invalid register // number as a normal one to continue parsing // and catch other possible errors. Error(getLexer().getLoc(), "invalid register number"); } Operands.push_back(MipsOperand::createNumericReg( RegNum, Token.getString(), getContext().getRegisterInfo(), S, Token.getLoc(), *this)); return MatchOperand_Success; } LLVM_DEBUG(dbgs() << Token.getKind() << "\n"); return MatchOperand_NoMatch; } OperandMatchResultTy MipsAsmParser::matchAnyRegisterWithoutDollar(OperandVector &Operands, SMLoc S) { auto Token = getLexer().peekTok(false); return matchAnyRegisterWithoutDollar(Operands, Token, S); } OperandMatchResultTy MipsAsmParser::parseAnyRegister(OperandVector &Operands) { MCAsmParser &Parser = getParser(); LLVM_DEBUG(dbgs() << "parseAnyRegister\n"); auto Token = Parser.getTok(); SMLoc S = Token.getLoc(); if (Token.isNot(AsmToken::Dollar)) { LLVM_DEBUG(dbgs() << ".. !$ -> try sym aliasing\n"); if (Token.is(AsmToken::Identifier)) { if (searchSymbolAlias(Operands)) return MatchOperand_Success; } LLVM_DEBUG(dbgs() << ".. !symalias -> NoMatch\n"); return MatchOperand_NoMatch; } LLVM_DEBUG(dbgs() << ".. $\n"); OperandMatchResultTy ResTy = matchAnyRegisterWithoutDollar(Operands, S); if (ResTy == MatchOperand_Success) { Parser.Lex(); // $ Parser.Lex(); // identifier } return ResTy; } OperandMatchResultTy MipsAsmParser::parseJumpTarget(OperandVector &Operands) { MCAsmParser &Parser = getParser(); LLVM_DEBUG(dbgs() << "parseJumpTarget\n"); SMLoc S = getLexer().getLoc(); // Registers are a valid target and have priority over symbols. OperandMatchResultTy ResTy = parseAnyRegister(Operands); if (ResTy != MatchOperand_NoMatch) return ResTy; // Integers and expressions are acceptable const MCExpr *Expr = nullptr; if (Parser.parseExpression(Expr)) { // We have no way of knowing if a symbol was consumed so we must ParseFail return MatchOperand_ParseFail; } Operands.push_back( MipsOperand::CreateImm(Expr, S, getLexer().getLoc(), *this)); return MatchOperand_Success; } OperandMatchResultTy MipsAsmParser::parseInvNum(OperandVector &Operands) { MCAsmParser &Parser = getParser(); const MCExpr *IdVal; // If the first token is '$' we may have register operand. We have to reject // cases where it is not a register. Complicating the matter is that // register names are not reserved across all ABIs. // Peek past the dollar to see if it's a register name for this ABI. SMLoc S = Parser.getTok().getLoc(); if (Parser.getTok().is(AsmToken::Dollar)) { return matchCPURegisterName(Parser.getLexer().peekTok().getString()) == -1 ? MatchOperand_ParseFail : MatchOperand_NoMatch; } if (getParser().parseExpression(IdVal)) return MatchOperand_ParseFail; const MCConstantExpr *MCE = dyn_cast(IdVal); if (!MCE) return MatchOperand_NoMatch; int64_t Val = MCE->getValue(); SMLoc E = SMLoc::getFromPointer(Parser.getTok().getLoc().getPointer() - 1); Operands.push_back(MipsOperand::CreateImm( MCConstantExpr::create(0 - Val, getContext()), S, E, *this)); return MatchOperand_Success; } OperandMatchResultTy MipsAsmParser::parseRegisterList(OperandVector &Operands) { MCAsmParser &Parser = getParser(); SmallVector Regs; unsigned RegNo; unsigned PrevReg = Mips::NoRegister; bool RegRange = false; SmallVector, 8> TmpOperands; if (Parser.getTok().isNot(AsmToken::Dollar)) return MatchOperand_ParseFail; SMLoc S = Parser.getTok().getLoc(); while (parseAnyRegister(TmpOperands) == MatchOperand_Success) { SMLoc E = getLexer().getLoc(); MipsOperand &Reg = static_cast(*TmpOperands.back()); RegNo = isGP64bit() ? Reg.getGPR64Reg() : Reg.getGPR32Reg(); if (RegRange) { // Remove last register operand because registers from register range // should be inserted first. if ((isGP64bit() && RegNo == Mips::RA_64) || (!isGP64bit() && RegNo == Mips::RA)) { Regs.push_back(RegNo); } else { unsigned TmpReg = PrevReg + 1; while (TmpReg <= RegNo) { if ((((TmpReg < Mips::S0) || (TmpReg > Mips::S7)) && !isGP64bit()) || (((TmpReg < Mips::S0_64) || (TmpReg > Mips::S7_64)) && isGP64bit())) { Error(E, "invalid register operand"); return MatchOperand_ParseFail; } PrevReg = TmpReg; Regs.push_back(TmpReg++); } } RegRange = false; } else { if ((PrevReg == Mips::NoRegister) && ((isGP64bit() && (RegNo != Mips::S0_64) && (RegNo != Mips::RA_64)) || (!isGP64bit() && (RegNo != Mips::S0) && (RegNo != Mips::RA)))) { Error(E, "$16 or $31 expected"); return MatchOperand_ParseFail; } else if (!(((RegNo == Mips::FP || RegNo == Mips::RA || (RegNo >= Mips::S0 && RegNo <= Mips::S7)) && !isGP64bit()) || ((RegNo == Mips::FP_64 || RegNo == Mips::RA_64 || (RegNo >= Mips::S0_64 && RegNo <= Mips::S7_64)) && isGP64bit()))) { Error(E, "invalid register operand"); return MatchOperand_ParseFail; } else if ((PrevReg != Mips::NoRegister) && (RegNo != PrevReg + 1) && ((RegNo != Mips::FP && RegNo != Mips::RA && !isGP64bit()) || (RegNo != Mips::FP_64 && RegNo != Mips::RA_64 && isGP64bit()))) { Error(E, "consecutive register numbers expected"); return MatchOperand_ParseFail; } Regs.push_back(RegNo); } if (Parser.getTok().is(AsmToken::Minus)) RegRange = true; if (!Parser.getTok().isNot(AsmToken::Minus) && !Parser.getTok().isNot(AsmToken::Comma)) { Error(E, "',' or '-' expected"); return MatchOperand_ParseFail; } Lex(); // Consume comma or minus if (Parser.getTok().isNot(AsmToken::Dollar)) break; PrevReg = RegNo; } SMLoc E = Parser.getTok().getLoc(); Operands.push_back(MipsOperand::CreateRegList(Regs, S, E, *this)); parseMemOperand(Operands); return MatchOperand_Success; } /// Sometimes (i.e. load/stores) the operand may be followed immediately by /// either this. /// ::= '(', register, ')' /// handle it before we iterate so we don't get tripped up by the lack of /// a comma. bool MipsAsmParser::parseParenSuffix(StringRef Name, OperandVector &Operands) { MCAsmParser &Parser = getParser(); if (getLexer().is(AsmToken::LParen)) { Operands.push_back( MipsOperand::CreateToken("(", getLexer().getLoc(), *this)); Parser.Lex(); if (parseOperand(Operands, Name)) { SMLoc Loc = getLexer().getLoc(); return Error(Loc, "unexpected token in argument list"); } if (Parser.getTok().isNot(AsmToken::RParen)) { SMLoc Loc = getLexer().getLoc(); return Error(Loc, "unexpected token, expected ')'"); } Operands.push_back( MipsOperand::CreateToken(")", getLexer().getLoc(), *this)); Parser.Lex(); } return false; } /// Sometimes (i.e. in MSA) the operand may be followed immediately by /// either one of these. /// ::= '[', register, ']' /// ::= '[', integer, ']' /// handle it before we iterate so we don't get tripped up by the lack of /// a comma. bool MipsAsmParser::parseBracketSuffix(StringRef Name, OperandVector &Operands) { MCAsmParser &Parser = getParser(); if (getLexer().is(AsmToken::LBrac)) { Operands.push_back( MipsOperand::CreateToken("[", getLexer().getLoc(), *this)); Parser.Lex(); if (parseOperand(Operands, Name)) { SMLoc Loc = getLexer().getLoc(); return Error(Loc, "unexpected token in argument list"); } if (Parser.getTok().isNot(AsmToken::RBrac)) { SMLoc Loc = getLexer().getLoc(); return Error(Loc, "unexpected token, expected ']'"); } Operands.push_back( MipsOperand::CreateToken("]", getLexer().getLoc(), *this)); Parser.Lex(); } return false; } static std::string MipsMnemonicSpellCheck(StringRef S, const FeatureBitset &FBS, unsigned VariantID = 0); bool MipsAsmParser::ParseInstruction(ParseInstructionInfo &Info, StringRef Name, SMLoc NameLoc, OperandVector &Operands) { MCAsmParser &Parser = getParser(); LLVM_DEBUG(dbgs() << "ParseInstruction\n"); // We have reached first instruction, module directive are now forbidden. getTargetStreamer().forbidModuleDirective(); // Check if we have valid mnemonic if (!mnemonicIsValid(Name, 0)) { FeatureBitset FBS = ComputeAvailableFeatures(getSTI().getFeatureBits()); std::string Suggestion = MipsMnemonicSpellCheck(Name, FBS); return Error(NameLoc, "unknown instruction" + Suggestion); } // First operand in MCInst is instruction mnemonic. Operands.push_back(MipsOperand::CreateToken(Name, NameLoc, *this)); // Read the remaining operands. if (getLexer().isNot(AsmToken::EndOfStatement)) { // Read the first operand. if (parseOperand(Operands, Name)) { SMLoc Loc = getLexer().getLoc(); return Error(Loc, "unexpected token in argument list"); } if (getLexer().is(AsmToken::LBrac) && parseBracketSuffix(Name, Operands)) return true; // AFAIK, parenthesis suffixes are never on the first operand while (getLexer().is(AsmToken::Comma)) { Parser.Lex(); // Eat the comma. // Parse and remember the operand. if (parseOperand(Operands, Name)) { SMLoc Loc = getLexer().getLoc(); return Error(Loc, "unexpected token in argument list"); } // Parse bracket and parenthesis suffixes before we iterate if (getLexer().is(AsmToken::LBrac)) { if (parseBracketSuffix(Name, Operands)) return true; } else if (getLexer().is(AsmToken::LParen) && parseParenSuffix(Name, Operands)) return true; } } if (getLexer().isNot(AsmToken::EndOfStatement)) { SMLoc Loc = getLexer().getLoc(); return Error(Loc, "unexpected token in argument list"); } Parser.Lex(); // Consume the EndOfStatement. return false; } // FIXME: Given that these have the same name, these should both be // consistent on affecting the Parser. bool MipsAsmParser::reportParseError(Twine ErrorMsg) { SMLoc Loc = getLexer().getLoc(); return Error(Loc, ErrorMsg); } bool MipsAsmParser::reportParseError(SMLoc Loc, Twine ErrorMsg) { return Error(Loc, ErrorMsg); } bool MipsAsmParser::parseSetNoAtDirective() { MCAsmParser &Parser = getParser(); // Line should look like: ".set noat". // Set the $at register to $0. AssemblerOptions.back()->setATRegIndex(0); Parser.Lex(); // Eat "noat". // If this is not the end of the statement, report an error. if (getLexer().isNot(AsmToken::EndOfStatement)) { reportParseError("unexpected token, expected end of statement"); return false; } getTargetStreamer().emitDirectiveSetNoAt(); Parser.Lex(); // Consume the EndOfStatement. return false; } bool MipsAsmParser::parseSetAtDirective() { // Line can be: ".set at", which sets $at to $1 // or ".set at=$reg", which sets $at to $reg. MCAsmParser &Parser = getParser(); Parser.Lex(); // Eat "at". if (getLexer().is(AsmToken::EndOfStatement)) { // No register was specified, so we set $at to $1. AssemblerOptions.back()->setATRegIndex(1); getTargetStreamer().emitDirectiveSetAt(); Parser.Lex(); // Consume the EndOfStatement. return false; } if (getLexer().isNot(AsmToken::Equal)) { reportParseError("unexpected token, expected equals sign"); return false; } Parser.Lex(); // Eat "=". if (getLexer().isNot(AsmToken::Dollar)) { if (getLexer().is(AsmToken::EndOfStatement)) { reportParseError("no register specified"); return false; } else { reportParseError("unexpected token, expected dollar sign '$'"); return false; } } Parser.Lex(); // Eat "$". // Find out what "reg" is. unsigned AtRegNo; const AsmToken &Reg = Parser.getTok(); if (Reg.is(AsmToken::Identifier)) { AtRegNo = matchCPURegisterName(Reg.getIdentifier()); } else if (Reg.is(AsmToken::Integer)) { AtRegNo = Reg.getIntVal(); } else { reportParseError("unexpected token, expected identifier or integer"); return false; } // Check if $reg is a valid register. If it is, set $at to $reg. if (!AssemblerOptions.back()->setATRegIndex(AtRegNo)) { reportParseError("invalid register"); return false; } Parser.Lex(); // Eat "reg". // If this is not the end of the statement, report an error. if (getLexer().isNot(AsmToken::EndOfStatement)) { reportParseError("unexpected token, expected end of statement"); return false; } getTargetStreamer().emitDirectiveSetAtWithArg(AtRegNo); Parser.Lex(); // Consume the EndOfStatement. return false; } bool MipsAsmParser::parseSetReorderDirective() { MCAsmParser &Parser = getParser(); Parser.Lex(); // If this is not the end of the statement, report an error. if (getLexer().isNot(AsmToken::EndOfStatement)) { reportParseError("unexpected token, expected end of statement"); return false; } AssemblerOptions.back()->setReorder(); getTargetStreamer().emitDirectiveSetReorder(); Parser.Lex(); // Consume the EndOfStatement. return false; } bool MipsAsmParser::parseSetNoReorderDirective() { MCAsmParser &Parser = getParser(); Parser.Lex(); // If this is not the end of the statement, report an error. if (getLexer().isNot(AsmToken::EndOfStatement)) { reportParseError("unexpected token, expected end of statement"); return false; } AssemblerOptions.back()->setNoReorder(); getTargetStreamer().emitDirectiveSetNoReorder(); Parser.Lex(); // Consume the EndOfStatement. return false; } bool MipsAsmParser::parseSetMacroDirective() { MCAsmParser &Parser = getParser(); Parser.Lex(); // If this is not the end of the statement, report an error. if (getLexer().isNot(AsmToken::EndOfStatement)) { reportParseError("unexpected token, expected end of statement"); return false; } AssemblerOptions.back()->setMacro(); getTargetStreamer().emitDirectiveSetMacro(); Parser.Lex(); // Consume the EndOfStatement. return false; } bool MipsAsmParser::parseSetNoMacroDirective() { MCAsmParser &Parser = getParser(); Parser.Lex(); // If this is not the end of the statement, report an error. if (getLexer().isNot(AsmToken::EndOfStatement)) { reportParseError("unexpected token, expected end of statement"); return false; } if (AssemblerOptions.back()->isReorder()) { reportParseError("`noreorder' must be set before `nomacro'"); return false; } AssemblerOptions.back()->setNoMacro(); getTargetStreamer().emitDirectiveSetNoMacro(); Parser.Lex(); // Consume the EndOfStatement. return false; } bool MipsAsmParser::parseSetMsaDirective() { MCAsmParser &Parser = getParser(); Parser.Lex(); // If this is not the end of the statement, report an error. if (getLexer().isNot(AsmToken::EndOfStatement)) return reportParseError("unexpected token, expected end of statement"); setFeatureBits(Mips::FeatureMSA, "msa"); getTargetStreamer().emitDirectiveSetMsa(); return false; } bool MipsAsmParser::parseSetNoMsaDirective() { MCAsmParser &Parser = getParser(); Parser.Lex(); // If this is not the end of the statement, report an error. if (getLexer().isNot(AsmToken::EndOfStatement)) return reportParseError("unexpected token, expected end of statement"); clearFeatureBits(Mips::FeatureMSA, "msa"); getTargetStreamer().emitDirectiveSetNoMsa(); return false; } bool MipsAsmParser::parseSetNoDspDirective() { MCAsmParser &Parser = getParser(); Parser.Lex(); // Eat "nodsp". // If this is not the end of the statement, report an error. if (getLexer().isNot(AsmToken::EndOfStatement)) { reportParseError("unexpected token, expected end of statement"); return false; } clearFeatureBits(Mips::FeatureDSP, "dsp"); getTargetStreamer().emitDirectiveSetNoDsp(); return false; } bool MipsAsmParser::parseSetMips16Directive() { MCAsmParser &Parser = getParser(); Parser.Lex(); // Eat "mips16". // If this is not the end of the statement, report an error. if (getLexer().isNot(AsmToken::EndOfStatement)) { reportParseError("unexpected token, expected end of statement"); return false; } setFeatureBits(Mips::FeatureMips16, "mips16"); getTargetStreamer().emitDirectiveSetMips16(); Parser.Lex(); // Consume the EndOfStatement. return false; } bool MipsAsmParser::parseSetNoMips16Directive() { MCAsmParser &Parser = getParser(); Parser.Lex(); // Eat "nomips16". // If this is not the end of the statement, report an error. if (getLexer().isNot(AsmToken::EndOfStatement)) { reportParseError("unexpected token, expected end of statement"); return false; } clearFeatureBits(Mips::FeatureMips16, "mips16"); getTargetStreamer().emitDirectiveSetNoMips16(); Parser.Lex(); // Consume the EndOfStatement. return false; } bool MipsAsmParser::parseSetFpDirective() { MCAsmParser &Parser = getParser(); MipsABIFlagsSection::FpABIKind FpAbiVal; // Line can be: .set fp=32 // .set fp=xx // .set fp=64 Parser.Lex(); // Eat fp token AsmToken Tok = Parser.getTok(); if (Tok.isNot(AsmToken::Equal)) { reportParseError("unexpected token, expected equals sign '='"); return false; } Parser.Lex(); // Eat '=' token. Tok = Parser.getTok(); if (!parseFpABIValue(FpAbiVal, ".set")) return false; if (getLexer().isNot(AsmToken::EndOfStatement)) { reportParseError("unexpected token, expected end of statement"); return false; } getTargetStreamer().emitDirectiveSetFp(FpAbiVal); Parser.Lex(); // Consume the EndOfStatement. return false; } bool MipsAsmParser::parseSetOddSPRegDirective() { MCAsmParser &Parser = getParser(); Parser.Lex(); // Eat "oddspreg". if (getLexer().isNot(AsmToken::EndOfStatement)) { reportParseError("unexpected token, expected end of statement"); return false; } clearFeatureBits(Mips::FeatureNoOddSPReg, "nooddspreg"); getTargetStreamer().emitDirectiveSetOddSPReg(); return false; } bool MipsAsmParser::parseSetNoOddSPRegDirective() { MCAsmParser &Parser = getParser(); Parser.Lex(); // Eat "nooddspreg". if (getLexer().isNot(AsmToken::EndOfStatement)) { reportParseError("unexpected token, expected end of statement"); return false; } setFeatureBits(Mips::FeatureNoOddSPReg, "nooddspreg"); getTargetStreamer().emitDirectiveSetNoOddSPReg(); return false; } bool MipsAsmParser::parseSetMtDirective() { MCAsmParser &Parser = getParser(); Parser.Lex(); // Eat "mt". // If this is not the end of the statement, report an error. if (getLexer().isNot(AsmToken::EndOfStatement)) { reportParseError("unexpected token, expected end of statement"); return false; } setFeatureBits(Mips::FeatureMT, "mt"); getTargetStreamer().emitDirectiveSetMt(); Parser.Lex(); // Consume the EndOfStatement. return false; } bool MipsAsmParser::parseSetNoMtDirective() { MCAsmParser &Parser = getParser(); Parser.Lex(); // Eat "nomt". // If this is not the end of the statement, report an error. if (getLexer().isNot(AsmToken::EndOfStatement)) { reportParseError("unexpected token, expected end of statement"); return false; } clearFeatureBits(Mips::FeatureMT, "mt"); getTargetStreamer().emitDirectiveSetNoMt(); Parser.Lex(); // Consume the EndOfStatement. return false; } bool MipsAsmParser::parseSetNoCRCDirective() { MCAsmParser &Parser = getParser(); Parser.Lex(); // Eat "nocrc". // If this is not the end of the statement, report an error. if (getLexer().isNot(AsmToken::EndOfStatement)) { reportParseError("unexpected token, expected end of statement"); return false; } clearFeatureBits(Mips::FeatureCRC, "crc"); getTargetStreamer().emitDirectiveSetNoCRC(); Parser.Lex(); // Consume the EndOfStatement. return false; } bool MipsAsmParser::parseSetNoVirtDirective() { MCAsmParser &Parser = getParser(); Parser.Lex(); // Eat "novirt". // If this is not the end of the statement, report an error. if (getLexer().isNot(AsmToken::EndOfStatement)) { reportParseError("unexpected token, expected end of statement"); return false; } clearFeatureBits(Mips::FeatureVirt, "virt"); getTargetStreamer().emitDirectiveSetNoVirt(); Parser.Lex(); // Consume the EndOfStatement. return false; } bool MipsAsmParser::parseSetNoGINVDirective() { MCAsmParser &Parser = getParser(); Parser.Lex(); // Eat "noginv". // If this is not the end of the statement, report an error. if (getLexer().isNot(AsmToken::EndOfStatement)) { reportParseError("unexpected token, expected end of statement"); return false; } clearFeatureBits(Mips::FeatureGINV, "ginv"); getTargetStreamer().emitDirectiveSetNoGINV(); Parser.Lex(); // Consume the EndOfStatement. return false; } bool MipsAsmParser::parseSetPopDirective() { MCAsmParser &Parser = getParser(); SMLoc Loc = getLexer().getLoc(); Parser.Lex(); if (getLexer().isNot(AsmToken::EndOfStatement)) return reportParseError("unexpected token, expected end of statement"); // Always keep an element on the options "stack" to prevent the user // from changing the initial options. This is how we remember them. if (AssemblerOptions.size() == 2) return reportParseError(Loc, ".set pop with no .set push"); MCSubtargetInfo &STI = copySTI(); AssemblerOptions.pop_back(); setAvailableFeatures( ComputeAvailableFeatures(AssemblerOptions.back()->getFeatures())); STI.setFeatureBits(AssemblerOptions.back()->getFeatures()); getTargetStreamer().emitDirectiveSetPop(); return false; } bool MipsAsmParser::parseSetPushDirective() { MCAsmParser &Parser = getParser(); Parser.Lex(); if (getLexer().isNot(AsmToken::EndOfStatement)) return reportParseError("unexpected token, expected end of statement"); // Create a copy of the current assembler options environment and push it. AssemblerOptions.push_back( llvm::make_unique(AssemblerOptions.back().get())); getTargetStreamer().emitDirectiveSetPush(); return false; } bool MipsAsmParser::parseSetSoftFloatDirective() { MCAsmParser &Parser = getParser(); Parser.Lex(); if (getLexer().isNot(AsmToken::EndOfStatement)) return reportParseError("unexpected token, expected end of statement"); setFeatureBits(Mips::FeatureSoftFloat, "soft-float"); getTargetStreamer().emitDirectiveSetSoftFloat(); return false; } bool MipsAsmParser::parseSetHardFloatDirective() { MCAsmParser &Parser = getParser(); Parser.Lex(); if (getLexer().isNot(AsmToken::EndOfStatement)) return reportParseError("unexpected token, expected end of statement"); clearFeatureBits(Mips::FeatureSoftFloat, "soft-float"); getTargetStreamer().emitDirectiveSetHardFloat(); return false; } bool MipsAsmParser::parseSetAssignment() { StringRef Name; MCAsmParser &Parser = getParser(); if (Parser.parseIdentifier(Name)) return reportParseError("expected identifier after .set"); if (getLexer().isNot(AsmToken::Comma)) return reportParseError("unexpected token, expected comma"); Lex(); // Eat comma if (getLexer().is(AsmToken::Dollar) && getLexer().peekTok().is(AsmToken::Integer)) { // Parse assignment of a numeric register: // .set r1,$1 Parser.Lex(); // Eat $. RegisterSets[Name] = Parser.getTok(); Parser.Lex(); // Eat identifier. getContext().getOrCreateSymbol(Name); return false; } MCSymbol *Sym; const MCExpr *Value; if (MCParserUtils::parseAssignmentExpression(Name, /* allow_redef */ true, Parser, Sym, Value)) return true; Sym->setVariableValue(Value); return false; } bool MipsAsmParser::parseSetMips0Directive() { MCAsmParser &Parser = getParser(); Parser.Lex(); if (getLexer().isNot(AsmToken::EndOfStatement)) return reportParseError("unexpected token, expected end of statement"); // Reset assembler options to their initial values. MCSubtargetInfo &STI = copySTI(); setAvailableFeatures( ComputeAvailableFeatures(AssemblerOptions.front()->getFeatures())); STI.setFeatureBits(AssemblerOptions.front()->getFeatures()); AssemblerOptions.back()->setFeatures(AssemblerOptions.front()->getFeatures()); getTargetStreamer().emitDirectiveSetMips0(); return false; } bool MipsAsmParser::parseSetArchDirective() { MCAsmParser &Parser = getParser(); Parser.Lex(); if (getLexer().isNot(AsmToken::Equal)) return reportParseError("unexpected token, expected equals sign"); Parser.Lex(); StringRef Arch; if (Parser.parseIdentifier(Arch)) return reportParseError("expected arch identifier"); StringRef ArchFeatureName = StringSwitch(Arch) .Case("mips1", "mips1") .Case("mips2", "mips2") .Case("mips3", "mips3") .Case("mips4", "mips4") .Case("mips5", "mips5") .Case("mips32", "mips32") .Case("mips32r2", "mips32r2") .Case("mips32r3", "mips32r3") .Case("mips32r5", "mips32r5") .Case("mips32r6", "mips32r6") .Case("mips64", "mips64") .Case("mips64r2", "mips64r2") .Case("mips64r3", "mips64r3") .Case("mips64r5", "mips64r5") .Case("mips64r6", "mips64r6") .Case("octeon", "cnmips") .Case("r4000", "mips3") // This is an implementation of Mips3. .Default(""); if (ArchFeatureName.empty()) return reportParseError("unsupported architecture"); if (ArchFeatureName == "mips64r6" && inMicroMipsMode()) return reportParseError("mips64r6 does not support microMIPS"); selectArch(ArchFeatureName); getTargetStreamer().emitDirectiveSetArch(Arch); return false; } bool MipsAsmParser::parseSetFeature(uint64_t Feature) { MCAsmParser &Parser = getParser(); Parser.Lex(); if (getLexer().isNot(AsmToken::EndOfStatement)) return reportParseError("unexpected token, expected end of statement"); switch (Feature) { default: llvm_unreachable("Unimplemented feature"); case Mips::FeatureDSP: setFeatureBits(Mips::FeatureDSP, "dsp"); getTargetStreamer().emitDirectiveSetDsp(); break; case Mips::FeatureDSPR2: setFeatureBits(Mips::FeatureDSPR2, "dspr2"); getTargetStreamer().emitDirectiveSetDspr2(); break; case Mips::FeatureMicroMips: setFeatureBits(Mips::FeatureMicroMips, "micromips"); getTargetStreamer().emitDirectiveSetMicroMips(); break; case Mips::FeatureMips1: selectArch("mips1"); getTargetStreamer().emitDirectiveSetMips1(); break; case Mips::FeatureMips2: selectArch("mips2"); getTargetStreamer().emitDirectiveSetMips2(); break; case Mips::FeatureMips3: selectArch("mips3"); getTargetStreamer().emitDirectiveSetMips3(); break; case Mips::FeatureMips4: selectArch("mips4"); getTargetStreamer().emitDirectiveSetMips4(); break; case Mips::FeatureMips5: selectArch("mips5"); getTargetStreamer().emitDirectiveSetMips5(); break; case Mips::FeatureMips32: selectArch("mips32"); getTargetStreamer().emitDirectiveSetMips32(); break; case Mips::FeatureMips32r2: selectArch("mips32r2"); getTargetStreamer().emitDirectiveSetMips32R2(); break; case Mips::FeatureMips32r3: selectArch("mips32r3"); getTargetStreamer().emitDirectiveSetMips32R3(); break; case Mips::FeatureMips32r5: selectArch("mips32r5"); getTargetStreamer().emitDirectiveSetMips32R5(); break; case Mips::FeatureMips32r6: selectArch("mips32r6"); getTargetStreamer().emitDirectiveSetMips32R6(); break; case Mips::FeatureMips64: selectArch("mips64"); getTargetStreamer().emitDirectiveSetMips64(); break; case Mips::FeatureMips64r2: selectArch("mips64r2"); getTargetStreamer().emitDirectiveSetMips64R2(); break; case Mips::FeatureMips64r3: selectArch("mips64r3"); getTargetStreamer().emitDirectiveSetMips64R3(); break; case Mips::FeatureMips64r5: selectArch("mips64r5"); getTargetStreamer().emitDirectiveSetMips64R5(); break; case Mips::FeatureMips64r6: selectArch("mips64r6"); getTargetStreamer().emitDirectiveSetMips64R6(); break; case Mips::FeatureCRC: setFeatureBits(Mips::FeatureCRC, "crc"); getTargetStreamer().emitDirectiveSetCRC(); break; case Mips::FeatureVirt: setFeatureBits(Mips::FeatureVirt, "virt"); getTargetStreamer().emitDirectiveSetVirt(); break; case Mips::FeatureGINV: setFeatureBits(Mips::FeatureGINV, "ginv"); getTargetStreamer().emitDirectiveSetGINV(); break; } return false; } bool MipsAsmParser::eatComma(StringRef ErrorStr) { MCAsmParser &Parser = getParser(); if (getLexer().isNot(AsmToken::Comma)) { SMLoc Loc = getLexer().getLoc(); return Error(Loc, ErrorStr); } Parser.Lex(); // Eat the comma. return true; } // Used to determine if .cpload, .cprestore, and .cpsetup have any effect. // In this class, it is only used for .cprestore. // FIXME: Only keep track of IsPicEnabled in one place, instead of in both // MipsTargetELFStreamer and MipsAsmParser. bool MipsAsmParser::isPicAndNotNxxAbi() { return inPicMode() && !(isABI_N32() || isABI_N64()); } bool MipsAsmParser::parseDirectiveCpLoad(SMLoc Loc) { if (AssemblerOptions.back()->isReorder()) Warning(Loc, ".cpload should be inside a noreorder section"); if (inMips16Mode()) { reportParseError(".cpload is not supported in Mips16 mode"); return false; } SmallVector, 1> Reg; OperandMatchResultTy ResTy = parseAnyRegister(Reg); if (ResTy == MatchOperand_NoMatch || ResTy == MatchOperand_ParseFail) { reportParseError("expected register containing function address"); return false; } MipsOperand &RegOpnd = static_cast(*Reg[0]); if (!RegOpnd.isGPRAsmReg()) { reportParseError(RegOpnd.getStartLoc(), "invalid register"); return false; } // If this is not the end of the statement, report an error. if (getLexer().isNot(AsmToken::EndOfStatement)) { reportParseError("unexpected token, expected end of statement"); return false; } getTargetStreamer().emitDirectiveCpLoad(RegOpnd.getGPR32Reg()); return false; } bool MipsAsmParser::parseDirectiveCpLocal(SMLoc Loc) { if (!isABI_N32() && !isABI_N64()) { reportParseError(".cplocal is allowed only in N32 or N64 mode"); return false; } SmallVector, 1> Reg; OperandMatchResultTy ResTy = parseAnyRegister(Reg); if (ResTy == MatchOperand_NoMatch || ResTy == MatchOperand_ParseFail) { reportParseError("expected register containing global pointer"); return false; } MipsOperand &RegOpnd = static_cast(*Reg[0]); if (!RegOpnd.isGPRAsmReg()) { reportParseError(RegOpnd.getStartLoc(), "invalid register"); return false; } // If this is not the end of the statement, report an error. if (getLexer().isNot(AsmToken::EndOfStatement)) { reportParseError("unexpected token, expected end of statement"); return false; } getParser().Lex(); // Consume the EndOfStatement. unsigned NewReg = RegOpnd.getGPR32Reg(); if (IsPicEnabled) GPReg = NewReg; getTargetStreamer().emitDirectiveCpLocal(NewReg); return false; } bool MipsAsmParser::parseDirectiveCpRestore(SMLoc Loc) { MCAsmParser &Parser = getParser(); // Note that .cprestore is ignored if used with the N32 and N64 ABIs or if it // is used in non-PIC mode. if (inMips16Mode()) { reportParseError(".cprestore is not supported in Mips16 mode"); return false; } // Get the stack offset value. const MCExpr *StackOffset; int64_t StackOffsetVal; if (Parser.parseExpression(StackOffset)) { reportParseError("expected stack offset value"); return false; } if (!StackOffset->evaluateAsAbsolute(StackOffsetVal)) { reportParseError("stack offset is not an absolute expression"); return false; } if (StackOffsetVal < 0) { Warning(Loc, ".cprestore with negative stack offset has no effect"); IsCpRestoreSet = false; } else { IsCpRestoreSet = true; CpRestoreOffset = StackOffsetVal; } // If this is not the end of the statement, report an error. if (getLexer().isNot(AsmToken::EndOfStatement)) { reportParseError("unexpected token, expected end of statement"); return false; } if (!getTargetStreamer().emitDirectiveCpRestore( CpRestoreOffset, [&]() { return getATReg(Loc); }, Loc, STI)) return true; Parser.Lex(); // Consume the EndOfStatement. return false; } bool MipsAsmParser::parseDirectiveCPSetup() { MCAsmParser &Parser = getParser(); unsigned FuncReg; unsigned Save; bool SaveIsReg = true; SmallVector, 1> TmpReg; OperandMatchResultTy ResTy = parseAnyRegister(TmpReg); if (ResTy == MatchOperand_NoMatch) { reportParseError("expected register containing function address"); return false; } MipsOperand &FuncRegOpnd = static_cast(*TmpReg[0]); if (!FuncRegOpnd.isGPRAsmReg()) { reportParseError(FuncRegOpnd.getStartLoc(), "invalid register"); return false; } FuncReg = FuncRegOpnd.getGPR32Reg(); TmpReg.clear(); if (!eatComma("unexpected token, expected comma")) return true; ResTy = parseAnyRegister(TmpReg); if (ResTy == MatchOperand_NoMatch) { const MCExpr *OffsetExpr; int64_t OffsetVal; SMLoc ExprLoc = getLexer().getLoc(); if (Parser.parseExpression(OffsetExpr) || !OffsetExpr->evaluateAsAbsolute(OffsetVal)) { reportParseError(ExprLoc, "expected save register or stack offset"); return false; } Save = OffsetVal; SaveIsReg = false; } else { MipsOperand &SaveOpnd = static_cast(*TmpReg[0]); if (!SaveOpnd.isGPRAsmReg()) { reportParseError(SaveOpnd.getStartLoc(), "invalid register"); return false; } Save = SaveOpnd.getGPR32Reg(); } if (!eatComma("unexpected token, expected comma")) return true; const MCExpr *Expr; if (Parser.parseExpression(Expr)) { reportParseError("expected expression"); return false; } if (Expr->getKind() != MCExpr::SymbolRef) { reportParseError("expected symbol"); return false; } const MCSymbolRefExpr *Ref = static_cast(Expr); CpSaveLocation = Save; CpSaveLocationIsRegister = SaveIsReg; getTargetStreamer().emitDirectiveCpsetup(FuncReg, Save, Ref->getSymbol(), SaveIsReg); return false; } bool MipsAsmParser::parseDirectiveCPReturn() { getTargetStreamer().emitDirectiveCpreturn(CpSaveLocation, CpSaveLocationIsRegister); return false; } bool MipsAsmParser::parseDirectiveNaN() { MCAsmParser &Parser = getParser(); if (getLexer().isNot(AsmToken::EndOfStatement)) { const AsmToken &Tok = Parser.getTok(); if (Tok.getString() == "2008") { Parser.Lex(); getTargetStreamer().emitDirectiveNaN2008(); return false; } else if (Tok.getString() == "legacy") { Parser.Lex(); getTargetStreamer().emitDirectiveNaNLegacy(); return false; } } // If we don't recognize the option passed to the .nan // directive (e.g. no option or unknown option), emit an error. reportParseError("invalid option in .nan directive"); return false; } bool MipsAsmParser::parseDirectiveSet() { const AsmToken &Tok = getParser().getTok(); StringRef IdVal = Tok.getString(); SMLoc Loc = Tok.getLoc(); if (IdVal == "noat") return parseSetNoAtDirective(); if (IdVal == "at") return parseSetAtDirective(); if (IdVal == "arch") return parseSetArchDirective(); if (IdVal == "bopt") { Warning(Loc, "'bopt' feature is unsupported"); getParser().Lex(); return false; } if (IdVal == "nobopt") { // We're already running in nobopt mode, so nothing to do. getParser().Lex(); return false; } if (IdVal == "fp") return parseSetFpDirective(); if (IdVal == "oddspreg") return parseSetOddSPRegDirective(); if (IdVal == "nooddspreg") return parseSetNoOddSPRegDirective(); if (IdVal == "pop") return parseSetPopDirective(); if (IdVal == "push") return parseSetPushDirective(); if (IdVal == "reorder") return parseSetReorderDirective(); if (IdVal == "noreorder") return parseSetNoReorderDirective(); if (IdVal == "macro") return parseSetMacroDirective(); if (IdVal == "nomacro") return parseSetNoMacroDirective(); if (IdVal == "mips16") return parseSetMips16Directive(); if (IdVal == "nomips16") return parseSetNoMips16Directive(); if (IdVal == "nomicromips") { clearFeatureBits(Mips::FeatureMicroMips, "micromips"); getTargetStreamer().emitDirectiveSetNoMicroMips(); getParser().eatToEndOfStatement(); return false; } if (IdVal == "micromips") { if (hasMips64r6()) { Error(Loc, ".set micromips directive is not supported with MIPS64R6"); return false; } return parseSetFeature(Mips::FeatureMicroMips); } if (IdVal == "mips0") return parseSetMips0Directive(); if (IdVal == "mips1") return parseSetFeature(Mips::FeatureMips1); if (IdVal == "mips2") return parseSetFeature(Mips::FeatureMips2); if (IdVal == "mips3") return parseSetFeature(Mips::FeatureMips3); if (IdVal == "mips4") return parseSetFeature(Mips::FeatureMips4); if (IdVal == "mips5") return parseSetFeature(Mips::FeatureMips5); if (IdVal == "mips32") return parseSetFeature(Mips::FeatureMips32); if (IdVal == "mips32r2") return parseSetFeature(Mips::FeatureMips32r2); if (IdVal == "mips32r3") return parseSetFeature(Mips::FeatureMips32r3); if (IdVal == "mips32r5") return parseSetFeature(Mips::FeatureMips32r5); if (IdVal == "mips32r6") return parseSetFeature(Mips::FeatureMips32r6); if (IdVal == "mips64") return parseSetFeature(Mips::FeatureMips64); if (IdVal == "mips64r2") return parseSetFeature(Mips::FeatureMips64r2); if (IdVal == "mips64r3") return parseSetFeature(Mips::FeatureMips64r3); if (IdVal == "mips64r5") return parseSetFeature(Mips::FeatureMips64r5); if (IdVal == "mips64r6") { if (inMicroMipsMode()) { Error(Loc, "MIPS64R6 is not supported with microMIPS"); return false; } return parseSetFeature(Mips::FeatureMips64r6); } if (IdVal == "dsp") return parseSetFeature(Mips::FeatureDSP); if (IdVal == "dspr2") return parseSetFeature(Mips::FeatureDSPR2); if (IdVal == "nodsp") return parseSetNoDspDirective(); if (IdVal == "msa") return parseSetMsaDirective(); if (IdVal == "nomsa") return parseSetNoMsaDirective(); if (IdVal == "mt") return parseSetMtDirective(); if (IdVal == "nomt") return parseSetNoMtDirective(); if (IdVal == "softfloat") return parseSetSoftFloatDirective(); if (IdVal == "hardfloat") return parseSetHardFloatDirective(); if (IdVal == "crc") return parseSetFeature(Mips::FeatureCRC); if (IdVal == "nocrc") return parseSetNoCRCDirective(); if (IdVal == "virt") return parseSetFeature(Mips::FeatureVirt); if (IdVal == "novirt") return parseSetNoVirtDirective(); if (IdVal == "ginv") return parseSetFeature(Mips::FeatureGINV); if (IdVal == "noginv") return parseSetNoGINVDirective(); // It is just an identifier, look for an assignment. return parseSetAssignment(); } /// parseDirectiveGpWord /// ::= .gpword local_sym bool MipsAsmParser::parseDirectiveGpWord() { MCAsmParser &Parser = getParser(); const MCExpr *Value; // EmitGPRel32Value requires an expression, so we are using base class // method to evaluate the expression. if (getParser().parseExpression(Value)) return true; getParser().getStreamer().EmitGPRel32Value(Value); if (getLexer().isNot(AsmToken::EndOfStatement)) return Error(getLexer().getLoc(), "unexpected token, expected end of statement"); Parser.Lex(); // Eat EndOfStatement token. return false; } /// parseDirectiveGpDWord /// ::= .gpdword local_sym bool MipsAsmParser::parseDirectiveGpDWord() { MCAsmParser &Parser = getParser(); const MCExpr *Value; // EmitGPRel64Value requires an expression, so we are using base class // method to evaluate the expression. if (getParser().parseExpression(Value)) return true; getParser().getStreamer().EmitGPRel64Value(Value); if (getLexer().isNot(AsmToken::EndOfStatement)) return Error(getLexer().getLoc(), "unexpected token, expected end of statement"); Parser.Lex(); // Eat EndOfStatement token. return false; } /// parseDirectiveDtpRelWord /// ::= .dtprelword tls_sym bool MipsAsmParser::parseDirectiveDtpRelWord() { MCAsmParser &Parser = getParser(); const MCExpr *Value; // EmitDTPRel32Value requires an expression, so we are using base class // method to evaluate the expression. if (getParser().parseExpression(Value)) return true; getParser().getStreamer().EmitDTPRel32Value(Value); if (getLexer().isNot(AsmToken::EndOfStatement)) return Error(getLexer().getLoc(), "unexpected token, expected end of statement"); Parser.Lex(); // Eat EndOfStatement token. return false; } /// parseDirectiveDtpRelDWord /// ::= .dtpreldword tls_sym bool MipsAsmParser::parseDirectiveDtpRelDWord() { MCAsmParser &Parser = getParser(); const MCExpr *Value; // EmitDTPRel64Value requires an expression, so we are using base class // method to evaluate the expression. if (getParser().parseExpression(Value)) return true; getParser().getStreamer().EmitDTPRel64Value(Value); if (getLexer().isNot(AsmToken::EndOfStatement)) return Error(getLexer().getLoc(), "unexpected token, expected end of statement"); Parser.Lex(); // Eat EndOfStatement token. return false; } /// parseDirectiveTpRelWord /// ::= .tprelword tls_sym bool MipsAsmParser::parseDirectiveTpRelWord() { MCAsmParser &Parser = getParser(); const MCExpr *Value; // EmitTPRel32Value requires an expression, so we are using base class // method to evaluate the expression. if (getParser().parseExpression(Value)) return true; getParser().getStreamer().EmitTPRel32Value(Value); if (getLexer().isNot(AsmToken::EndOfStatement)) return Error(getLexer().getLoc(), "unexpected token, expected end of statement"); Parser.Lex(); // Eat EndOfStatement token. return false; } /// parseDirectiveTpRelDWord /// ::= .tpreldword tls_sym bool MipsAsmParser::parseDirectiveTpRelDWord() { MCAsmParser &Parser = getParser(); const MCExpr *Value; // EmitTPRel64Value requires an expression, so we are using base class // method to evaluate the expression. if (getParser().parseExpression(Value)) return true; getParser().getStreamer().EmitTPRel64Value(Value); if (getLexer().isNot(AsmToken::EndOfStatement)) return Error(getLexer().getLoc(), "unexpected token, expected end of statement"); Parser.Lex(); // Eat EndOfStatement token. return false; } bool MipsAsmParser::parseDirectiveOption() { MCAsmParser &Parser = getParser(); // Get the option token. AsmToken Tok = Parser.getTok(); // At the moment only identifiers are supported. if (Tok.isNot(AsmToken::Identifier)) { return Error(Parser.getTok().getLoc(), "unexpected token, expected identifier"); } StringRef Option = Tok.getIdentifier(); if (Option == "pic0") { // MipsAsmParser needs to know if the current PIC mode changes. IsPicEnabled = false; getTargetStreamer().emitDirectiveOptionPic0(); Parser.Lex(); if (Parser.getTok().isNot(AsmToken::EndOfStatement)) { return Error(Parser.getTok().getLoc(), "unexpected token, expected end of statement"); } return false; } if (Option == "pic2") { // MipsAsmParser needs to know if the current PIC mode changes. IsPicEnabled = true; getTargetStreamer().emitDirectiveOptionPic2(); Parser.Lex(); if (Parser.getTok().isNot(AsmToken::EndOfStatement)) { return Error(Parser.getTok().getLoc(), "unexpected token, expected end of statement"); } return false; } // Unknown option. Warning(Parser.getTok().getLoc(), "unknown option, expected 'pic0' or 'pic2'"); Parser.eatToEndOfStatement(); return false; } /// parseInsnDirective /// ::= .insn bool MipsAsmParser::parseInsnDirective() { // If this is not the end of the statement, report an error. if (getLexer().isNot(AsmToken::EndOfStatement)) { reportParseError("unexpected token, expected end of statement"); return false; } // The actual label marking happens in // MipsELFStreamer::createPendingLabelRelocs(). getTargetStreamer().emitDirectiveInsn(); getParser().Lex(); // Eat EndOfStatement token. return false; } /// parseRSectionDirective /// ::= .rdata bool MipsAsmParser::parseRSectionDirective(StringRef Section) { // If this is not the end of the statement, report an error. if (getLexer().isNot(AsmToken::EndOfStatement)) { reportParseError("unexpected token, expected end of statement"); return false; } MCSection *ELFSection = getContext().getELFSection( Section, ELF::SHT_PROGBITS, ELF::SHF_ALLOC); getParser().getStreamer().SwitchSection(ELFSection); getParser().Lex(); // Eat EndOfStatement token. return false; } /// parseSSectionDirective /// ::= .sbss /// ::= .sdata bool MipsAsmParser::parseSSectionDirective(StringRef Section, unsigned Type) { // If this is not the end of the statement, report an error. if (getLexer().isNot(AsmToken::EndOfStatement)) { reportParseError("unexpected token, expected end of statement"); return false; } MCSection *ELFSection = getContext().getELFSection( Section, Type, ELF::SHF_WRITE | ELF::SHF_ALLOC | ELF::SHF_MIPS_GPREL); getParser().getStreamer().SwitchSection(ELFSection); getParser().Lex(); // Eat EndOfStatement token. return false; } /// parseDirectiveModule /// ::= .module oddspreg /// ::= .module nooddspreg /// ::= .module fp=value /// ::= .module softfloat /// ::= .module hardfloat /// ::= .module mt /// ::= .module crc /// ::= .module nocrc /// ::= .module virt /// ::= .module novirt /// ::= .module ginv /// ::= .module noginv bool MipsAsmParser::parseDirectiveModule() { MCAsmParser &Parser = getParser(); MCAsmLexer &Lexer = getLexer(); SMLoc L = Lexer.getLoc(); if (!getTargetStreamer().isModuleDirectiveAllowed()) { // TODO : get a better message. reportParseError(".module directive must appear before any code"); return false; } StringRef Option; if (Parser.parseIdentifier(Option)) { reportParseError("expected .module option identifier"); return false; } if (Option == "oddspreg") { clearModuleFeatureBits(Mips::FeatureNoOddSPReg, "nooddspreg"); // Synchronize the abiflags information with the FeatureBits information we // changed above. getTargetStreamer().updateABIInfo(*this); // If printing assembly, use the recently updated abiflags information. // If generating ELF, don't do anything (the .MIPS.abiflags section gets // emitted at the end). getTargetStreamer().emitDirectiveModuleOddSPReg(); // If this is not the end of the statement, report an error. if (getLexer().isNot(AsmToken::EndOfStatement)) { reportParseError("unexpected token, expected end of statement"); return false; } return false; // parseDirectiveModule has finished successfully. } else if (Option == "nooddspreg") { if (!isABI_O32()) { return Error(L, "'.module nooddspreg' requires the O32 ABI"); } setModuleFeatureBits(Mips::FeatureNoOddSPReg, "nooddspreg"); // Synchronize the abiflags information with the FeatureBits information we // changed above. getTargetStreamer().updateABIInfo(*this); // If printing assembly, use the recently updated abiflags information. // If generating ELF, don't do anything (the .MIPS.abiflags section gets // emitted at the end). getTargetStreamer().emitDirectiveModuleOddSPReg(); // If this is not the end of the statement, report an error. if (getLexer().isNot(AsmToken::EndOfStatement)) { reportParseError("unexpected token, expected end of statement"); return false; } return false; // parseDirectiveModule has finished successfully. } else if (Option == "fp") { return parseDirectiveModuleFP(); } else if (Option == "softfloat") { setModuleFeatureBits(Mips::FeatureSoftFloat, "soft-float"); // Synchronize the ABI Flags information with the FeatureBits information we // updated above. getTargetStreamer().updateABIInfo(*this); // If printing assembly, use the recently updated ABI Flags information. // If generating ELF, don't do anything (the .MIPS.abiflags section gets // emitted later). getTargetStreamer().emitDirectiveModuleSoftFloat(); // If this is not the end of the statement, report an error. if (getLexer().isNot(AsmToken::EndOfStatement)) { reportParseError("unexpected token, expected end of statement"); return false; } return false; // parseDirectiveModule has finished successfully. } else if (Option == "hardfloat") { clearModuleFeatureBits(Mips::FeatureSoftFloat, "soft-float"); // Synchronize the ABI Flags information with the FeatureBits information we // updated above. getTargetStreamer().updateABIInfo(*this); // If printing assembly, use the recently updated ABI Flags information. // If generating ELF, don't do anything (the .MIPS.abiflags section gets // emitted later). getTargetStreamer().emitDirectiveModuleHardFloat(); // If this is not the end of the statement, report an error. if (getLexer().isNot(AsmToken::EndOfStatement)) { reportParseError("unexpected token, expected end of statement"); return false; } return false; // parseDirectiveModule has finished successfully. } else if (Option == "mt") { setModuleFeatureBits(Mips::FeatureMT, "mt"); // Synchronize the ABI Flags information with the FeatureBits information we // updated above. getTargetStreamer().updateABIInfo(*this); // If printing assembly, use the recently updated ABI Flags information. // If generating ELF, don't do anything (the .MIPS.abiflags section gets // emitted later). getTargetStreamer().emitDirectiveModuleMT(); // If this is not the end of the statement, report an error. if (getLexer().isNot(AsmToken::EndOfStatement)) { reportParseError("unexpected token, expected end of statement"); return false; } return false; // parseDirectiveModule has finished successfully. } else if (Option == "crc") { setModuleFeatureBits(Mips::FeatureCRC, "crc"); // Synchronize the ABI Flags information with the FeatureBits information we // updated above. getTargetStreamer().updateABIInfo(*this); // If printing assembly, use the recently updated ABI Flags information. // If generating ELF, don't do anything (the .MIPS.abiflags section gets // emitted later). getTargetStreamer().emitDirectiveModuleCRC(); // If this is not the end of the statement, report an error. if (getLexer().isNot(AsmToken::EndOfStatement)) { reportParseError("unexpected token, expected end of statement"); return false; } return false; // parseDirectiveModule has finished successfully. } else if (Option == "nocrc") { clearModuleFeatureBits(Mips::FeatureCRC, "crc"); // Synchronize the ABI Flags information with the FeatureBits information we // updated above. getTargetStreamer().updateABIInfo(*this); // If printing assembly, use the recently updated ABI Flags information. // If generating ELF, don't do anything (the .MIPS.abiflags section gets // emitted later). getTargetStreamer().emitDirectiveModuleNoCRC(); // If this is not the end of the statement, report an error. if (getLexer().isNot(AsmToken::EndOfStatement)) { reportParseError("unexpected token, expected end of statement"); return false; } return false; // parseDirectiveModule has finished successfully. } else if (Option == "virt") { setModuleFeatureBits(Mips::FeatureVirt, "virt"); // Synchronize the ABI Flags information with the FeatureBits information we // updated above. getTargetStreamer().updateABIInfo(*this); // If printing assembly, use the recently updated ABI Flags information. // If generating ELF, don't do anything (the .MIPS.abiflags section gets // emitted later). getTargetStreamer().emitDirectiveModuleVirt(); // If this is not the end of the statement, report an error. if (getLexer().isNot(AsmToken::EndOfStatement)) { reportParseError("unexpected token, expected end of statement"); return false; } return false; // parseDirectiveModule has finished successfully. } else if (Option == "novirt") { clearModuleFeatureBits(Mips::FeatureVirt, "virt"); // Synchronize the ABI Flags information with the FeatureBits information we // updated above. getTargetStreamer().updateABIInfo(*this); // If printing assembly, use the recently updated ABI Flags information. // If generating ELF, don't do anything (the .MIPS.abiflags section gets // emitted later). getTargetStreamer().emitDirectiveModuleNoVirt(); // If this is not the end of the statement, report an error. if (getLexer().isNot(AsmToken::EndOfStatement)) { reportParseError("unexpected token, expected end of statement"); return false; } return false; // parseDirectiveModule has finished successfully. } else if (Option == "ginv") { setModuleFeatureBits(Mips::FeatureGINV, "ginv"); // Synchronize the ABI Flags information with the FeatureBits information we // updated above. getTargetStreamer().updateABIInfo(*this); // If printing assembly, use the recently updated ABI Flags information. // If generating ELF, don't do anything (the .MIPS.abiflags section gets // emitted later). getTargetStreamer().emitDirectiveModuleGINV(); // If this is not the end of the statement, report an error. if (getLexer().isNot(AsmToken::EndOfStatement)) { reportParseError("unexpected token, expected end of statement"); return false; } return false; // parseDirectiveModule has finished successfully. } else if (Option == "noginv") { clearModuleFeatureBits(Mips::FeatureGINV, "ginv"); // Synchronize the ABI Flags information with the FeatureBits information we // updated above. getTargetStreamer().updateABIInfo(*this); // If printing assembly, use the recently updated ABI Flags information. // If generating ELF, don't do anything (the .MIPS.abiflags section gets // emitted later). getTargetStreamer().emitDirectiveModuleNoGINV(); // If this is not the end of the statement, report an error. if (getLexer().isNot(AsmToken::EndOfStatement)) { reportParseError("unexpected token, expected end of statement"); return false; } return false; // parseDirectiveModule has finished successfully. } else { return Error(L, "'" + Twine(Option) + "' is not a valid .module option."); } } /// parseDirectiveModuleFP /// ::= =32 /// ::= =xx /// ::= =64 bool MipsAsmParser::parseDirectiveModuleFP() { MCAsmParser &Parser = getParser(); MCAsmLexer &Lexer = getLexer(); if (Lexer.isNot(AsmToken::Equal)) { reportParseError("unexpected token, expected equals sign '='"); return false; } Parser.Lex(); // Eat '=' token. MipsABIFlagsSection::FpABIKind FpABI; if (!parseFpABIValue(FpABI, ".module")) return false; if (getLexer().isNot(AsmToken::EndOfStatement)) { reportParseError("unexpected token, expected end of statement"); return false; } // Synchronize the abiflags information with the FeatureBits information we // changed above. getTargetStreamer().updateABIInfo(*this); // If printing assembly, use the recently updated abiflags information. // If generating ELF, don't do anything (the .MIPS.abiflags section gets // emitted at the end). getTargetStreamer().emitDirectiveModuleFP(); Parser.Lex(); // Consume the EndOfStatement. return false; } bool MipsAsmParser::parseFpABIValue(MipsABIFlagsSection::FpABIKind &FpABI, StringRef Directive) { MCAsmParser &Parser = getParser(); MCAsmLexer &Lexer = getLexer(); bool ModuleLevelOptions = Directive == ".module"; if (Lexer.is(AsmToken::Identifier)) { StringRef Value = Parser.getTok().getString(); Parser.Lex(); if (Value != "xx") { reportParseError("unsupported value, expected 'xx', '32' or '64'"); return false; } if (!isABI_O32()) { reportParseError("'" + Directive + " fp=xx' requires the O32 ABI"); return false; } FpABI = MipsABIFlagsSection::FpABIKind::XX; if (ModuleLevelOptions) { setModuleFeatureBits(Mips::FeatureFPXX, "fpxx"); clearModuleFeatureBits(Mips::FeatureFP64Bit, "fp64"); } else { setFeatureBits(Mips::FeatureFPXX, "fpxx"); clearFeatureBits(Mips::FeatureFP64Bit, "fp64"); } return true; } if (Lexer.is(AsmToken::Integer)) { unsigned Value = Parser.getTok().getIntVal(); Parser.Lex(); if (Value != 32 && Value != 64) { reportParseError("unsupported value, expected 'xx', '32' or '64'"); return false; } if (Value == 32) { if (!isABI_O32()) { reportParseError("'" + Directive + " fp=32' requires the O32 ABI"); return false; } FpABI = MipsABIFlagsSection::FpABIKind::S32; if (ModuleLevelOptions) { clearModuleFeatureBits(Mips::FeatureFPXX, "fpxx"); clearModuleFeatureBits(Mips::FeatureFP64Bit, "fp64"); } else { clearFeatureBits(Mips::FeatureFPXX, "fpxx"); clearFeatureBits(Mips::FeatureFP64Bit, "fp64"); } } else { FpABI = MipsABIFlagsSection::FpABIKind::S64; if (ModuleLevelOptions) { clearModuleFeatureBits(Mips::FeatureFPXX, "fpxx"); setModuleFeatureBits(Mips::FeatureFP64Bit, "fp64"); } else { clearFeatureBits(Mips::FeatureFPXX, "fpxx"); setFeatureBits(Mips::FeatureFP64Bit, "fp64"); } } return true; } return false; } bool MipsAsmParser::ParseDirective(AsmToken DirectiveID) { // This returns false if this function recognizes the directive // regardless of whether it is successfully handles or reports an // error. Otherwise it returns true to give the generic parser a // chance at recognizing it. MCAsmParser &Parser = getParser(); StringRef IDVal = DirectiveID.getString(); if (IDVal == ".cpload") { parseDirectiveCpLoad(DirectiveID.getLoc()); return false; } if (IDVal == ".cprestore") { parseDirectiveCpRestore(DirectiveID.getLoc()); return false; } if (IDVal == ".cplocal") { parseDirectiveCpLocal(DirectiveID.getLoc()); return false; } if (IDVal == ".ent") { StringRef SymbolName; if (Parser.parseIdentifier(SymbolName)) { reportParseError("expected identifier after .ent"); return false; } // There's an undocumented extension that allows an integer to // follow the name of the procedure which AFAICS is ignored by GAS. // Example: .ent foo,2 if (getLexer().isNot(AsmToken::EndOfStatement)) { if (getLexer().isNot(AsmToken::Comma)) { // Even though we accept this undocumented extension for compatibility // reasons, the additional integer argument does not actually change // the behaviour of the '.ent' directive, so we would like to discourage // its use. We do this by not referring to the extended version in // error messages which are not directly related to its use. reportParseError("unexpected token, expected end of statement"); return false; } Parser.Lex(); // Eat the comma. const MCExpr *DummyNumber; int64_t DummyNumberVal; // If the user was explicitly trying to use the extended version, // we still give helpful extension-related error messages. if (Parser.parseExpression(DummyNumber)) { reportParseError("expected number after comma"); return false; } if (!DummyNumber->evaluateAsAbsolute(DummyNumberVal)) { reportParseError("expected an absolute expression after comma"); return false; } } // If this is not the end of the statement, report an error. if (getLexer().isNot(AsmToken::EndOfStatement)) { reportParseError("unexpected token, expected end of statement"); return false; } MCSymbol *Sym = getContext().getOrCreateSymbol(SymbolName); getTargetStreamer().emitDirectiveEnt(*Sym); CurrentFn = Sym; IsCpRestoreSet = false; return false; } if (IDVal == ".end") { StringRef SymbolName; if (Parser.parseIdentifier(SymbolName)) { reportParseError("expected identifier after .end"); return false; } if (getLexer().isNot(AsmToken::EndOfStatement)) { reportParseError("unexpected token, expected end of statement"); return false; } if (CurrentFn == nullptr) { reportParseError(".end used without .ent"); return false; } if ((SymbolName != CurrentFn->getName())) { reportParseError(".end symbol does not match .ent symbol"); return false; } getTargetStreamer().emitDirectiveEnd(SymbolName); CurrentFn = nullptr; IsCpRestoreSet = false; return false; } if (IDVal == ".frame") { // .frame $stack_reg, frame_size_in_bytes, $return_reg SmallVector, 1> TmpReg; OperandMatchResultTy ResTy = parseAnyRegister(TmpReg); if (ResTy == MatchOperand_NoMatch || ResTy == MatchOperand_ParseFail) { reportParseError("expected stack register"); return false; } MipsOperand &StackRegOpnd = static_cast(*TmpReg[0]); if (!StackRegOpnd.isGPRAsmReg()) { reportParseError(StackRegOpnd.getStartLoc(), "expected general purpose register"); return false; } unsigned StackReg = StackRegOpnd.getGPR32Reg(); if (Parser.getTok().is(AsmToken::Comma)) Parser.Lex(); else { reportParseError("unexpected token, expected comma"); return false; } // Parse the frame size. const MCExpr *FrameSize; int64_t FrameSizeVal; if (Parser.parseExpression(FrameSize)) { reportParseError("expected frame size value"); return false; } if (!FrameSize->evaluateAsAbsolute(FrameSizeVal)) { reportParseError("frame size not an absolute expression"); return false; } if (Parser.getTok().is(AsmToken::Comma)) Parser.Lex(); else { reportParseError("unexpected token, expected comma"); return false; } // Parse the return register. TmpReg.clear(); ResTy = parseAnyRegister(TmpReg); if (ResTy == MatchOperand_NoMatch || ResTy == MatchOperand_ParseFail) { reportParseError("expected return register"); return false; } MipsOperand &ReturnRegOpnd = static_cast(*TmpReg[0]); if (!ReturnRegOpnd.isGPRAsmReg()) { reportParseError(ReturnRegOpnd.getStartLoc(), "expected general purpose register"); return false; } // If this is not the end of the statement, report an error. if (getLexer().isNot(AsmToken::EndOfStatement)) { reportParseError("unexpected token, expected end of statement"); return false; } getTargetStreamer().emitFrame(StackReg, FrameSizeVal, ReturnRegOpnd.getGPR32Reg()); IsCpRestoreSet = false; return false; } if (IDVal == ".set") { parseDirectiveSet(); return false; } if (IDVal == ".mask" || IDVal == ".fmask") { // .mask bitmask, frame_offset // bitmask: One bit for each register used. // frame_offset: Offset from Canonical Frame Address ($sp on entry) where // first register is expected to be saved. // Examples: // .mask 0x80000000, -4 // .fmask 0x80000000, -4 // // Parse the bitmask const MCExpr *BitMask; int64_t BitMaskVal; if (Parser.parseExpression(BitMask)) { reportParseError("expected bitmask value"); return false; } if (!BitMask->evaluateAsAbsolute(BitMaskVal)) { reportParseError("bitmask not an absolute expression"); return false; } if (Parser.getTok().is(AsmToken::Comma)) Parser.Lex(); else { reportParseError("unexpected token, expected comma"); return false; } // Parse the frame_offset const MCExpr *FrameOffset; int64_t FrameOffsetVal; if (Parser.parseExpression(FrameOffset)) { reportParseError("expected frame offset value"); return false; } if (!FrameOffset->evaluateAsAbsolute(FrameOffsetVal)) { reportParseError("frame offset not an absolute expression"); return false; } // If this is not the end of the statement, report an error. if (getLexer().isNot(AsmToken::EndOfStatement)) { reportParseError("unexpected token, expected end of statement"); return false; } if (IDVal == ".mask") getTargetStreamer().emitMask(BitMaskVal, FrameOffsetVal); else getTargetStreamer().emitFMask(BitMaskVal, FrameOffsetVal); return false; } if (IDVal == ".nan") return parseDirectiveNaN(); if (IDVal == ".gpword") { parseDirectiveGpWord(); return false; } if (IDVal == ".gpdword") { parseDirectiveGpDWord(); return false; } if (IDVal == ".dtprelword") { parseDirectiveDtpRelWord(); return false; } if (IDVal == ".dtpreldword") { parseDirectiveDtpRelDWord(); return false; } if (IDVal == ".tprelword") { parseDirectiveTpRelWord(); return false; } if (IDVal == ".tpreldword") { parseDirectiveTpRelDWord(); return false; } if (IDVal == ".option") { parseDirectiveOption(); return false; } if (IDVal == ".abicalls") { getTargetStreamer().emitDirectiveAbiCalls(); if (Parser.getTok().isNot(AsmToken::EndOfStatement)) { Error(Parser.getTok().getLoc(), "unexpected token, expected end of statement"); } return false; } if (IDVal == ".cpsetup") { parseDirectiveCPSetup(); return false; } if (IDVal == ".cpreturn") { parseDirectiveCPReturn(); return false; } if (IDVal == ".module") { parseDirectiveModule(); return false; } if (IDVal == ".llvm_internal_mips_reallow_module_directive") { parseInternalDirectiveReallowModule(); return false; } if (IDVal == ".insn") { parseInsnDirective(); return false; } if (IDVal == ".rdata") { parseRSectionDirective(".rodata"); return false; } if (IDVal == ".sbss") { parseSSectionDirective(IDVal, ELF::SHT_NOBITS); return false; } if (IDVal == ".sdata") { parseSSectionDirective(IDVal, ELF::SHT_PROGBITS); return false; } return true; } bool MipsAsmParser::parseInternalDirectiveReallowModule() { // If this is not the end of the statement, report an error. if (getLexer().isNot(AsmToken::EndOfStatement)) { reportParseError("unexpected token, expected end of statement"); return false; } getTargetStreamer().reallowModuleDirective(); getParser().Lex(); // Eat EndOfStatement token. return false; } extern "C" void LLVMInitializeMipsAsmParser() { RegisterMCAsmParser X(getTheMipsTarget()); RegisterMCAsmParser Y(getTheMipselTarget()); RegisterMCAsmParser A(getTheMips64Target()); RegisterMCAsmParser B(getTheMips64elTarget()); } #define GET_REGISTER_MATCHER #define GET_MATCHER_IMPLEMENTATION #define GET_MNEMONIC_SPELL_CHECKER #include "MipsGenAsmMatcher.inc" bool MipsAsmParser::mnemonicIsValid(StringRef Mnemonic, unsigned VariantID) { // Find the appropriate table for this asm variant. const MatchEntry *Start, *End; switch (VariantID) { default: llvm_unreachable("invalid variant!"); case 0: Start = std::begin(MatchTable0); End = std::end(MatchTable0); break; } // Search the table. auto MnemonicRange = std::equal_range(Start, End, Mnemonic, LessOpcode()); return MnemonicRange.first != MnemonicRange.second; } Index: head/contrib/llvm/lib/Target/Mips/Disassembler/MipsDisassembler.cpp =================================================================== --- head/contrib/llvm/lib/Target/Mips/Disassembler/MipsDisassembler.cpp (revision 354978) +++ head/contrib/llvm/lib/Target/Mips/Disassembler/MipsDisassembler.cpp (revision 354979) @@ -1,2611 +1,2621 @@ //===- MipsDisassembler.cpp - Disassembler for Mips -----------------------===// // // 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 // //===----------------------------------------------------------------------===// // // This file is part of the Mips Disassembler. // //===----------------------------------------------------------------------===// #include "MCTargetDesc/MipsMCTargetDesc.h" #include "Mips.h" #include "TargetInfo/MipsTargetInfo.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/MC/MCContext.h" #include "llvm/MC/MCDisassembler/MCDisassembler.h" #include "llvm/MC/MCFixedLenDisassembler.h" #include "llvm/MC/MCInst.h" #include "llvm/MC/MCRegisterInfo.h" #include "llvm/MC/MCSubtargetInfo.h" #include "llvm/Support/Compiler.h" #include "llvm/Support/Debug.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/MathExtras.h" #include "llvm/Support/TargetRegistry.h" #include "llvm/Support/raw_ostream.h" #include #include using namespace llvm; #define DEBUG_TYPE "mips-disassembler" using DecodeStatus = MCDisassembler::DecodeStatus; namespace { class MipsDisassembler : public MCDisassembler { bool IsMicroMips; bool IsBigEndian; public: MipsDisassembler(const MCSubtargetInfo &STI, MCContext &Ctx, bool IsBigEndian) : MCDisassembler(STI, Ctx), IsMicroMips(STI.getFeatureBits()[Mips::FeatureMicroMips]), IsBigEndian(IsBigEndian) {} bool hasMips2() const { return STI.getFeatureBits()[Mips::FeatureMips2]; } bool hasMips3() const { return STI.getFeatureBits()[Mips::FeatureMips3]; } bool hasMips32() const { return STI.getFeatureBits()[Mips::FeatureMips32]; } bool hasMips32r6() const { return STI.getFeatureBits()[Mips::FeatureMips32r6]; } bool isFP64() const { return STI.getFeatureBits()[Mips::FeatureFP64Bit]; } bool isGP64() const { return STI.getFeatureBits()[Mips::FeatureGP64Bit]; } bool isPTR64() const { return STI.getFeatureBits()[Mips::FeaturePTR64Bit]; } bool hasCnMips() const { return STI.getFeatureBits()[Mips::FeatureCnMips]; } + bool hasCnMipsP() const { return STI.getFeatureBits()[Mips::FeatureCnMipsP]; } + bool hasCOP3() const { // Only present in MIPS-I and MIPS-II return !hasMips32() && !hasMips3(); } DecodeStatus getInstruction(MCInst &Instr, uint64_t &Size, ArrayRef Bytes, uint64_t Address, raw_ostream &VStream, raw_ostream &CStream) const override; }; } // end anonymous namespace // Forward declare these because the autogenerated code will reference them. // Definitions are further down. static DecodeStatus DecodeGPR64RegisterClass(MCInst &Inst, unsigned RegNo, uint64_t Address, const void *Decoder); static DecodeStatus DecodeCPU16RegsRegisterClass(MCInst &Inst, unsigned RegNo, uint64_t Address, const void *Decoder); static DecodeStatus DecodeGPRMM16RegisterClass(MCInst &Inst, unsigned RegNo, uint64_t Address, const void *Decoder); static DecodeStatus DecodeGPRMM16ZeroRegisterClass(MCInst &Inst, unsigned RegNo, uint64_t Address, const void *Decoder); static DecodeStatus DecodeGPRMM16MovePRegisterClass(MCInst &Inst, unsigned RegNo, uint64_t Address, const void *Decoder); static DecodeStatus DecodeGPR32RegisterClass(MCInst &Inst, unsigned RegNo, uint64_t Address, const void *Decoder); static DecodeStatus DecodePtrRegisterClass(MCInst &Inst, unsigned Insn, uint64_t Address, const void *Decoder); static DecodeStatus DecodeDSPRRegisterClass(MCInst &Inst, unsigned RegNo, uint64_t Address, const void *Decoder); static DecodeStatus DecodeFGR64RegisterClass(MCInst &Inst, unsigned RegNo, uint64_t Address, const void *Decoder); static DecodeStatus DecodeFGR32RegisterClass(MCInst &Inst, unsigned RegNo, uint64_t Address, const void *Decoder); static DecodeStatus DecodeCCRRegisterClass(MCInst &Inst, unsigned RegNo, uint64_t Address, const void *Decoder); static DecodeStatus DecodeFCCRegisterClass(MCInst &Inst, unsigned RegNo, uint64_t Address, const void *Decoder); static DecodeStatus DecodeFGRCCRegisterClass(MCInst &Inst, unsigned RegNo, uint64_t Address, const void *Decoder); static DecodeStatus DecodeHWRegsRegisterClass(MCInst &Inst, unsigned Insn, uint64_t Address, const void *Decoder); static DecodeStatus DecodeAFGR64RegisterClass(MCInst &Inst, unsigned RegNo, uint64_t Address, const void *Decoder); static DecodeStatus DecodeACC64DSPRegisterClass(MCInst &Inst, unsigned RegNo, uint64_t Address, const void *Decoder); static DecodeStatus DecodeHI32DSPRegisterClass(MCInst &Inst, unsigned RegNo, uint64_t Address, const void *Decoder); static DecodeStatus DecodeLO32DSPRegisterClass(MCInst &Inst, unsigned RegNo, uint64_t Address, const void *Decoder); static DecodeStatus DecodeMSA128BRegisterClass(MCInst &Inst, unsigned RegNo, uint64_t Address, const void *Decoder); static DecodeStatus DecodeMSA128HRegisterClass(MCInst &Inst, unsigned RegNo, uint64_t Address, const void *Decoder); static DecodeStatus DecodeMSA128WRegisterClass(MCInst &Inst, unsigned RegNo, uint64_t Address, const void *Decoder); static DecodeStatus DecodeMSA128DRegisterClass(MCInst &Inst, unsigned RegNo, uint64_t Address, const void *Decoder); static DecodeStatus DecodeMSACtrlRegisterClass(MCInst &Inst, unsigned RegNo, uint64_t Address, const void *Decoder); static DecodeStatus DecodeCOP0RegisterClass(MCInst &Inst, unsigned RegNo, uint64_t Address, const void *Decoder); static DecodeStatus DecodeCOP2RegisterClass(MCInst &Inst, unsigned RegNo, uint64_t Address, const void *Decoder); static DecodeStatus DecodeBranchTarget(MCInst &Inst, unsigned Offset, uint64_t Address, const void *Decoder); static DecodeStatus DecodeBranchTarget1SImm16(MCInst &Inst, unsigned Offset, uint64_t Address, const void *Decoder); static DecodeStatus DecodeJumpTarget(MCInst &Inst, unsigned Insn, uint64_t Address, const void *Decoder); static DecodeStatus DecodeBranchTarget21(MCInst &Inst, unsigned Offset, uint64_t Address, const void *Decoder); static DecodeStatus DecodeBranchTarget21MM(MCInst &Inst, unsigned Offset, uint64_t Address, const void *Decoder); static DecodeStatus DecodeBranchTarget26(MCInst &Inst, unsigned Offset, uint64_t Address, const void *Decoder); // DecodeBranchTarget7MM - Decode microMIPS branch offset, which is // shifted left by 1 bit. static DecodeStatus DecodeBranchTarget7MM(MCInst &Inst, unsigned Offset, uint64_t Address, const void *Decoder); // DecodeBranchTarget10MM - Decode microMIPS branch offset, which is // shifted left by 1 bit. static DecodeStatus DecodeBranchTarget10MM(MCInst &Inst, unsigned Offset, uint64_t Address, const void *Decoder); // DecodeBranchTargetMM - Decode microMIPS branch offset, which is // shifted left by 1 bit. static DecodeStatus DecodeBranchTargetMM(MCInst &Inst, unsigned Offset, uint64_t Address, const void *Decoder); // DecodeBranchTarget26MM - Decode microMIPS branch offset, which is // shifted left by 1 bit. static DecodeStatus DecodeBranchTarget26MM(MCInst &Inst, unsigned Offset, uint64_t Address, const void *Decoder); // DecodeJumpTargetMM - Decode microMIPS jump target, which is // shifted left by 1 bit. static DecodeStatus DecodeJumpTargetMM(MCInst &Inst, unsigned Insn, uint64_t Address, const void *Decoder); static DecodeStatus DecodeMem(MCInst &Inst, unsigned Insn, uint64_t Address, const void *Decoder); static DecodeStatus DecodeMemEVA(MCInst &Inst, unsigned Insn, uint64_t Address, const void *Decoder); static DecodeStatus DecodeLoadByte15(MCInst &Inst, unsigned Insn, uint64_t Address, const void *Decoder); static DecodeStatus DecodeCacheOp(MCInst &Inst, unsigned Insn, uint64_t Address, const void *Decoder); static DecodeStatus DecodeCacheeOp_CacheOpR6(MCInst &Inst, unsigned Insn, uint64_t Address, const void *Decoder); static DecodeStatus DecodeCacheOpMM(MCInst &Inst, unsigned Insn, uint64_t Address, const void *Decoder); static DecodeStatus DecodePrefeOpMM(MCInst &Inst, unsigned Insn, uint64_t Address, const void *Decoder); static DecodeStatus DecodeSyncI(MCInst &Inst, unsigned Insn, uint64_t Address, const void *Decoder); static DecodeStatus DecodeSyncI_MM(MCInst &Inst, unsigned Insn, uint64_t Address, const void *Decoder); static DecodeStatus DecodeSynciR6(MCInst &Inst, unsigned Insn, uint64_t Address, const void *Decoder); static DecodeStatus DecodeMSA128Mem(MCInst &Inst, unsigned Insn, uint64_t Address, const void *Decoder); static DecodeStatus DecodeMemMMImm4(MCInst &Inst, unsigned Insn, uint64_t Address, const void *Decoder); static DecodeStatus DecodeMemMMSPImm5Lsl2(MCInst &Inst, unsigned Insn, uint64_t Address, const void *Decoder); static DecodeStatus DecodeMemMMGPImm7Lsl2(MCInst &Inst, unsigned Insn, uint64_t Address, const void *Decoder); static DecodeStatus DecodeMemMMReglistImm4Lsl2(MCInst &Inst, unsigned Insn, uint64_t Address, const void *Decoder); static DecodeStatus DecodeMemMMImm9(MCInst &Inst, unsigned Insn, uint64_t Address, const void *Decoder); static DecodeStatus DecodeMemMMImm12(MCInst &Inst, unsigned Insn, uint64_t Address, const void *Decoder); static DecodeStatus DecodeMemMMImm16(MCInst &Inst, unsigned Insn, uint64_t Address, const void *Decoder); static DecodeStatus DecodeFMem(MCInst &Inst, unsigned Insn, uint64_t Address, const void *Decoder); static DecodeStatus DecodeFMemMMR2(MCInst &Inst, unsigned Insn, uint64_t Address, const void *Decoder); static DecodeStatus DecodeFMem2(MCInst &Inst, unsigned Insn, uint64_t Address, const void *Decoder); static DecodeStatus DecodeFMem3(MCInst &Inst, unsigned Insn, uint64_t Address, const void *Decoder); static DecodeStatus DecodeFMemCop2R6(MCInst &Inst, unsigned Insn, uint64_t Address, const void *Decoder); static DecodeStatus DecodeFMemCop2MMR6(MCInst &Inst, unsigned Insn, uint64_t Address, const void *Decoder); static DecodeStatus DecodeSpecial3LlSc(MCInst &Inst, unsigned Insn, uint64_t Address, const void *Decoder); static DecodeStatus DecodeAddiur2Simm7(MCInst &Inst, unsigned Value, uint64_t Address, const void *Decoder); static DecodeStatus DecodeLi16Imm(MCInst &Inst, unsigned Value, uint64_t Address, const void *Decoder); static DecodeStatus DecodePOOL16BEncodedField(MCInst &Inst, unsigned Value, uint64_t Address, const void *Decoder); template static DecodeStatus DecodeUImmWithOffsetAndScale(MCInst &Inst, unsigned Value, uint64_t Address, const void *Decoder); template static DecodeStatus DecodeUImmWithOffset(MCInst &Inst, unsigned Value, uint64_t Address, const void *Decoder) { return DecodeUImmWithOffsetAndScale(Inst, Value, Address, Decoder); } template static DecodeStatus DecodeSImmWithOffsetAndScale(MCInst &Inst, unsigned Value, uint64_t Address, const void *Decoder); static DecodeStatus DecodeInsSize(MCInst &Inst, unsigned Insn, uint64_t Address, const void *Decoder); static DecodeStatus DecodeSimm19Lsl2(MCInst &Inst, unsigned Insn, uint64_t Address, const void *Decoder); static DecodeStatus DecodeSimm18Lsl3(MCInst &Inst, unsigned Insn, uint64_t Address, const void *Decoder); static DecodeStatus DecodeSimm9SP(MCInst &Inst, unsigned Insn, uint64_t Address, const void *Decoder); static DecodeStatus DecodeANDI16Imm(MCInst &Inst, unsigned Insn, uint64_t Address, const void *Decoder); static DecodeStatus DecodeSimm23Lsl2(MCInst &Inst, unsigned Insn, uint64_t Address, const void *Decoder); /// INSVE_[BHWD] have an implicit operand that the generated decoder doesn't /// handle. template static DecodeStatus DecodeINSVE_DF(MCInst &MI, InsnType insn, uint64_t Address, const void *Decoder); template static DecodeStatus DecodeDAHIDATIMMR6(MCInst &MI, InsnType insn, uint64_t Address, const void *Decoder); template static DecodeStatus DecodeDAHIDATI(MCInst &MI, InsnType insn, uint64_t Address, const void *Decoder); template static DecodeStatus DecodeDAHIDATIMMR6(MCInst &MI, InsnType insn, uint64_t Address, const void *Decoder); template static DecodeStatus DecodeDAHIDATI(MCInst &MI, InsnType insn, uint64_t Address, const void *Decoder); template static DecodeStatus DecodeAddiGroupBranch(MCInst &MI, InsnType insn, uint64_t Address, const void *Decoder); template static DecodeStatus DecodePOP35GroupBranchMMR6(MCInst &MI, InsnType insn, uint64_t Address, const void *Decoder); template static DecodeStatus DecodeDaddiGroupBranch(MCInst &MI, InsnType insn, uint64_t Address, const void *Decoder); template static DecodeStatus DecodePOP37GroupBranchMMR6(MCInst &MI, InsnType insn, uint64_t Address, const void *Decoder); template static DecodeStatus DecodePOP65GroupBranchMMR6(MCInst &MI, InsnType insn, uint64_t Address, const void *Decoder); template static DecodeStatus DecodePOP75GroupBranchMMR6(MCInst &MI, InsnType insn, uint64_t Address, const void *Decoder); template static DecodeStatus DecodeBlezlGroupBranch(MCInst &MI, InsnType insn, uint64_t Address, const void *Decoder); template static DecodeStatus DecodeBgtzlGroupBranch(MCInst &MI, InsnType insn, uint64_t Address, const void *Decoder); template static DecodeStatus DecodeBgtzGroupBranch(MCInst &MI, InsnType insn, uint64_t Address, const void *Decoder); template static DecodeStatus DecodeBlezGroupBranch(MCInst &MI, InsnType insn, uint64_t Address, const void *Decoder); template static DecodeStatus DecodeBgtzGroupBranchMMR6(MCInst &MI, InsnType insn, uint64_t Address, const void *Decoder); template static DecodeStatus DecodeBlezGroupBranchMMR6(MCInst &MI, InsnType insn, uint64_t Address, const void *Decoder); template static DecodeStatus DecodeDINS(MCInst &MI, InsnType Insn, uint64_t Address, const void *Decoder); template static DecodeStatus DecodeDEXT(MCInst &MI, InsnType Insn, uint64_t Address, const void *Decoder); template static DecodeStatus DecodeCRC(MCInst &MI, InsnType Insn, uint64_t Address, const void *Decoder); static DecodeStatus DecodeRegListOperand(MCInst &Inst, unsigned Insn, uint64_t Address, const void *Decoder); static DecodeStatus DecodeRegListOperand16(MCInst &Inst, unsigned Insn, uint64_t Address, const void *Decoder); static DecodeStatus DecodeMovePRegPair(MCInst &Inst, unsigned RegPair, uint64_t Address, const void *Decoder); static DecodeStatus DecodeMovePOperands(MCInst &Inst, unsigned Insn, uint64_t Address, const void *Decoder); static MCDisassembler *createMipsDisassembler( const Target &T, const MCSubtargetInfo &STI, MCContext &Ctx) { return new MipsDisassembler(STI, Ctx, true); } static MCDisassembler *createMipselDisassembler( const Target &T, const MCSubtargetInfo &STI, MCContext &Ctx) { return new MipsDisassembler(STI, Ctx, false); } extern "C" void LLVMInitializeMipsDisassembler() { // Register the disassembler. TargetRegistry::RegisterMCDisassembler(getTheMipsTarget(), createMipsDisassembler); TargetRegistry::RegisterMCDisassembler(getTheMipselTarget(), createMipselDisassembler); TargetRegistry::RegisterMCDisassembler(getTheMips64Target(), createMipsDisassembler); TargetRegistry::RegisterMCDisassembler(getTheMips64elTarget(), createMipselDisassembler); } #include "MipsGenDisassemblerTables.inc" static unsigned getReg(const void *D, unsigned RC, unsigned RegNo) { const MipsDisassembler *Dis = static_cast(D); const MCRegisterInfo *RegInfo = Dis->getContext().getRegisterInfo(); return *(RegInfo->getRegClass(RC).begin() + RegNo); } template static DecodeStatus DecodeINSVE_DF(MCInst &MI, InsnType insn, uint64_t Address, const void *Decoder) { using DecodeFN = DecodeStatus (*)(MCInst &, unsigned, uint64_t, const void *); // The size of the n field depends on the element size // The register class also depends on this. InsnType tmp = fieldFromInstruction(insn, 17, 5); unsigned NSize = 0; DecodeFN RegDecoder = nullptr; if ((tmp & 0x18) == 0x00) { // INSVE_B NSize = 4; RegDecoder = DecodeMSA128BRegisterClass; } else if ((tmp & 0x1c) == 0x10) { // INSVE_H NSize = 3; RegDecoder = DecodeMSA128HRegisterClass; } else if ((tmp & 0x1e) == 0x18) { // INSVE_W NSize = 2; RegDecoder = DecodeMSA128WRegisterClass; } else if ((tmp & 0x1f) == 0x1c) { // INSVE_D NSize = 1; RegDecoder = DecodeMSA128DRegisterClass; } else llvm_unreachable("Invalid encoding"); assert(NSize != 0 && RegDecoder != nullptr); // $wd tmp = fieldFromInstruction(insn, 6, 5); if (RegDecoder(MI, tmp, Address, Decoder) == MCDisassembler::Fail) return MCDisassembler::Fail; // $wd_in if (RegDecoder(MI, tmp, Address, Decoder) == MCDisassembler::Fail) return MCDisassembler::Fail; // $n tmp = fieldFromInstruction(insn, 16, NSize); MI.addOperand(MCOperand::createImm(tmp)); // $ws tmp = fieldFromInstruction(insn, 11, 5); if (RegDecoder(MI, tmp, Address, Decoder) == MCDisassembler::Fail) return MCDisassembler::Fail; // $n2 MI.addOperand(MCOperand::createImm(0)); return MCDisassembler::Success; } template static DecodeStatus DecodeDAHIDATIMMR6(MCInst &MI, InsnType insn, uint64_t Address, const void *Decoder) { InsnType Rs = fieldFromInstruction(insn, 16, 5); InsnType Imm = fieldFromInstruction(insn, 0, 16); MI.addOperand(MCOperand::createReg(getReg(Decoder, Mips::GPR64RegClassID, Rs))); MI.addOperand(MCOperand::createReg(getReg(Decoder, Mips::GPR64RegClassID, Rs))); MI.addOperand(MCOperand::createImm(Imm)); return MCDisassembler::Success; } template static DecodeStatus DecodeDAHIDATI(MCInst &MI, InsnType insn, uint64_t Address, const void *Decoder) { InsnType Rs = fieldFromInstruction(insn, 21, 5); InsnType Imm = fieldFromInstruction(insn, 0, 16); MI.addOperand(MCOperand::createReg(getReg(Decoder, Mips::GPR64RegClassID, Rs))); MI.addOperand(MCOperand::createReg(getReg(Decoder, Mips::GPR64RegClassID, Rs))); MI.addOperand(MCOperand::createImm(Imm)); return MCDisassembler::Success; } template static DecodeStatus DecodeAddiGroupBranch(MCInst &MI, InsnType insn, uint64_t Address, const void *Decoder) { // If we are called then we can assume that MIPS32r6/MIPS64r6 is enabled // (otherwise we would have matched the ADDI instruction from the earlier // ISA's instead). // // We have: // 0b001000 sssss ttttt iiiiiiiiiiiiiiii // BOVC if rs >= rt // BEQZALC if rs == 0 && rt != 0 // BEQC if rs < rt && rs != 0 InsnType Rs = fieldFromInstruction(insn, 21, 5); InsnType Rt = fieldFromInstruction(insn, 16, 5); int64_t Imm = SignExtend64(fieldFromInstruction(insn, 0, 16), 16) * 4 + 4; bool HasRs = false; if (Rs >= Rt) { MI.setOpcode(Mips::BOVC); HasRs = true; } else if (Rs != 0 && Rs < Rt) { MI.setOpcode(Mips::BEQC); HasRs = true; } else MI.setOpcode(Mips::BEQZALC); if (HasRs) MI.addOperand(MCOperand::createReg(getReg(Decoder, Mips::GPR32RegClassID, Rs))); MI.addOperand(MCOperand::createReg(getReg(Decoder, Mips::GPR32RegClassID, Rt))); MI.addOperand(MCOperand::createImm(Imm)); return MCDisassembler::Success; } template static DecodeStatus DecodePOP35GroupBranchMMR6(MCInst &MI, InsnType insn, uint64_t Address, const void *Decoder) { InsnType Rt = fieldFromInstruction(insn, 21, 5); InsnType Rs = fieldFromInstruction(insn, 16, 5); int64_t Imm = 0; if (Rs >= Rt) { MI.setOpcode(Mips::BOVC_MMR6); MI.addOperand(MCOperand::createReg(getReg(Decoder, Mips::GPR32RegClassID, Rt))); MI.addOperand(MCOperand::createReg(getReg(Decoder, Mips::GPR32RegClassID, Rs))); Imm = SignExtend64(fieldFromInstruction(insn, 0, 16), 16) * 2 + 4; } else if (Rs != 0 && Rs < Rt) { MI.setOpcode(Mips::BEQC_MMR6); MI.addOperand(MCOperand::createReg(getReg(Decoder, Mips::GPR32RegClassID, Rs))); MI.addOperand(MCOperand::createReg(getReg(Decoder, Mips::GPR32RegClassID, Rt))); Imm = SignExtend64(fieldFromInstruction(insn, 0, 16), 16) * 4 + 4; } else { MI.setOpcode(Mips::BEQZALC_MMR6); MI.addOperand(MCOperand::createReg(getReg(Decoder, Mips::GPR32RegClassID, Rt))); Imm = SignExtend64(fieldFromInstruction(insn, 0, 16), 16) * 2 + 4; } MI.addOperand(MCOperand::createImm(Imm)); return MCDisassembler::Success; } template static DecodeStatus DecodeDaddiGroupBranch(MCInst &MI, InsnType insn, uint64_t Address, const void *Decoder) { // If we are called then we can assume that MIPS32r6/MIPS64r6 is enabled // (otherwise we would have matched the ADDI instruction from the earlier // ISA's instead). // // We have: // 0b011000 sssss ttttt iiiiiiiiiiiiiiii // BNVC if rs >= rt // BNEZALC if rs == 0 && rt != 0 // BNEC if rs < rt && rs != 0 InsnType Rs = fieldFromInstruction(insn, 21, 5); InsnType Rt = fieldFromInstruction(insn, 16, 5); int64_t Imm = SignExtend64(fieldFromInstruction(insn, 0, 16), 16) * 4 + 4; bool HasRs = false; if (Rs >= Rt) { MI.setOpcode(Mips::BNVC); HasRs = true; } else if (Rs != 0 && Rs < Rt) { MI.setOpcode(Mips::BNEC); HasRs = true; } else MI.setOpcode(Mips::BNEZALC); if (HasRs) MI.addOperand(MCOperand::createReg(getReg(Decoder, Mips::GPR32RegClassID, Rs))); MI.addOperand(MCOperand::createReg(getReg(Decoder, Mips::GPR32RegClassID, Rt))); MI.addOperand(MCOperand::createImm(Imm)); return MCDisassembler::Success; } template static DecodeStatus DecodePOP37GroupBranchMMR6(MCInst &MI, InsnType insn, uint64_t Address, const void *Decoder) { InsnType Rt = fieldFromInstruction(insn, 21, 5); InsnType Rs = fieldFromInstruction(insn, 16, 5); int64_t Imm = 0; if (Rs >= Rt) { MI.setOpcode(Mips::BNVC_MMR6); MI.addOperand(MCOperand::createReg(getReg(Decoder, Mips::GPR32RegClassID, Rt))); MI.addOperand(MCOperand::createReg(getReg(Decoder, Mips::GPR32RegClassID, Rs))); Imm = SignExtend64(fieldFromInstruction(insn, 0, 16), 16) * 2 + 4; } else if (Rs != 0 && Rs < Rt) { MI.setOpcode(Mips::BNEC_MMR6); MI.addOperand(MCOperand::createReg(getReg(Decoder, Mips::GPR32RegClassID, Rs))); MI.addOperand(MCOperand::createReg(getReg(Decoder, Mips::GPR32RegClassID, Rt))); Imm = SignExtend64(fieldFromInstruction(insn, 0, 16), 16) * 4 + 4; } else { MI.setOpcode(Mips::BNEZALC_MMR6); MI.addOperand(MCOperand::createReg(getReg(Decoder, Mips::GPR32RegClassID, Rt))); Imm = SignExtend64(fieldFromInstruction(insn, 0, 16), 16) * 2 + 4; } MI.addOperand(MCOperand::createImm(Imm)); return MCDisassembler::Success; } template static DecodeStatus DecodePOP65GroupBranchMMR6(MCInst &MI, InsnType insn, uint64_t Address, const void *Decoder) { // We have: // 0b110101 ttttt sssss iiiiiiiiiiiiiiii // Invalid if rt == 0 // BGTZC_MMR6 if rs == 0 && rt != 0 // BLTZC_MMR6 if rs == rt && rt != 0 // BLTC_MMR6 if rs != rt && rs != 0 && rt != 0 InsnType Rt = fieldFromInstruction(insn, 21, 5); InsnType Rs = fieldFromInstruction(insn, 16, 5); int64_t Imm = SignExtend64(fieldFromInstruction(insn, 0, 16), 16) * 4 + 4; bool HasRs = false; if (Rt == 0) return MCDisassembler::Fail; else if (Rs == 0) MI.setOpcode(Mips::BGTZC_MMR6); else if (Rs == Rt) MI.setOpcode(Mips::BLTZC_MMR6); else { MI.setOpcode(Mips::BLTC_MMR6); HasRs = true; } if (HasRs) MI.addOperand(MCOperand::createReg(getReg(Decoder, Mips::GPR32RegClassID, Rs))); MI.addOperand(MCOperand::createReg(getReg(Decoder, Mips::GPR32RegClassID, Rt))); MI.addOperand(MCOperand::createImm(Imm)); return MCDisassembler::Success; } template static DecodeStatus DecodePOP75GroupBranchMMR6(MCInst &MI, InsnType insn, uint64_t Address, const void *Decoder) { // We have: // 0b111101 ttttt sssss iiiiiiiiiiiiiiii // Invalid if rt == 0 // BLEZC_MMR6 if rs == 0 && rt != 0 // BGEZC_MMR6 if rs == rt && rt != 0 // BGEC_MMR6 if rs != rt && rs != 0 && rt != 0 InsnType Rt = fieldFromInstruction(insn, 21, 5); InsnType Rs = fieldFromInstruction(insn, 16, 5); int64_t Imm = SignExtend64(fieldFromInstruction(insn, 0, 16), 16) * 4 + 4; bool HasRs = false; if (Rt == 0) return MCDisassembler::Fail; else if (Rs == 0) MI.setOpcode(Mips::BLEZC_MMR6); else if (Rs == Rt) MI.setOpcode(Mips::BGEZC_MMR6); else { HasRs = true; MI.setOpcode(Mips::BGEC_MMR6); } if (HasRs) MI.addOperand(MCOperand::createReg(getReg(Decoder, Mips::GPR32RegClassID, Rs))); MI.addOperand(MCOperand::createReg(getReg(Decoder, Mips::GPR32RegClassID, Rt))); MI.addOperand(MCOperand::createImm(Imm)); return MCDisassembler::Success; } template static DecodeStatus DecodeBlezlGroupBranch(MCInst &MI, InsnType insn, uint64_t Address, const void *Decoder) { // If we are called then we can assume that MIPS32r6/MIPS64r6 is enabled // (otherwise we would have matched the BLEZL instruction from the earlier // ISA's instead). // // We have: // 0b010110 sssss ttttt iiiiiiiiiiiiiiii // Invalid if rs == 0 // BLEZC if rs == 0 && rt != 0 // BGEZC if rs == rt && rt != 0 // BGEC if rs != rt && rs != 0 && rt != 0 InsnType Rs = fieldFromInstruction(insn, 21, 5); InsnType Rt = fieldFromInstruction(insn, 16, 5); int64_t Imm = SignExtend64(fieldFromInstruction(insn, 0, 16), 16) * 4 + 4; bool HasRs = false; if (Rt == 0) return MCDisassembler::Fail; else if (Rs == 0) MI.setOpcode(Mips::BLEZC); else if (Rs == Rt) MI.setOpcode(Mips::BGEZC); else { HasRs = true; MI.setOpcode(Mips::BGEC); } if (HasRs) MI.addOperand(MCOperand::createReg(getReg(Decoder, Mips::GPR32RegClassID, Rs))); MI.addOperand(MCOperand::createReg(getReg(Decoder, Mips::GPR32RegClassID, Rt))); MI.addOperand(MCOperand::createImm(Imm)); return MCDisassembler::Success; } template static DecodeStatus DecodeBgtzlGroupBranch(MCInst &MI, InsnType insn, uint64_t Address, const void *Decoder) { // If we are called then we can assume that MIPS32r6/MIPS64r6 is enabled // (otherwise we would have matched the BGTZL instruction from the earlier // ISA's instead). // // We have: // 0b010111 sssss ttttt iiiiiiiiiiiiiiii // Invalid if rs == 0 // BGTZC if rs == 0 && rt != 0 // BLTZC if rs == rt && rt != 0 // BLTC if rs != rt && rs != 0 && rt != 0 bool HasRs = false; InsnType Rs = fieldFromInstruction(insn, 21, 5); InsnType Rt = fieldFromInstruction(insn, 16, 5); int64_t Imm = SignExtend64(fieldFromInstruction(insn, 0, 16), 16) * 4 + 4; if (Rt == 0) return MCDisassembler::Fail; else if (Rs == 0) MI.setOpcode(Mips::BGTZC); else if (Rs == Rt) MI.setOpcode(Mips::BLTZC); else { MI.setOpcode(Mips::BLTC); HasRs = true; } if (HasRs) MI.addOperand(MCOperand::createReg(getReg(Decoder, Mips::GPR32RegClassID, Rs))); MI.addOperand(MCOperand::createReg(getReg(Decoder, Mips::GPR32RegClassID, Rt))); MI.addOperand(MCOperand::createImm(Imm)); return MCDisassembler::Success; } template static DecodeStatus DecodeBgtzGroupBranch(MCInst &MI, InsnType insn, uint64_t Address, const void *Decoder) { // If we are called then we can assume that MIPS32r6/MIPS64r6 is enabled // (otherwise we would have matched the BGTZ instruction from the earlier // ISA's instead). // // We have: // 0b000111 sssss ttttt iiiiiiiiiiiiiiii // BGTZ if rt == 0 // BGTZALC if rs == 0 && rt != 0 // BLTZALC if rs != 0 && rs == rt // BLTUC if rs != 0 && rs != rt InsnType Rs = fieldFromInstruction(insn, 21, 5); InsnType Rt = fieldFromInstruction(insn, 16, 5); int64_t Imm = SignExtend64(fieldFromInstruction(insn, 0, 16), 16) * 4 + 4; bool HasRs = false; bool HasRt = false; if (Rt == 0) { MI.setOpcode(Mips::BGTZ); HasRs = true; } else if (Rs == 0) { MI.setOpcode(Mips::BGTZALC); HasRt = true; } else if (Rs == Rt) { MI.setOpcode(Mips::BLTZALC); HasRs = true; } else { MI.setOpcode(Mips::BLTUC); HasRs = true; HasRt = true; } if (HasRs) MI.addOperand(MCOperand::createReg(getReg(Decoder, Mips::GPR32RegClassID, Rs))); if (HasRt) MI.addOperand(MCOperand::createReg(getReg(Decoder, Mips::GPR32RegClassID, Rt))); MI.addOperand(MCOperand::createImm(Imm)); return MCDisassembler::Success; } template static DecodeStatus DecodeBlezGroupBranch(MCInst &MI, InsnType insn, uint64_t Address, const void *Decoder) { // If we are called then we can assume that MIPS32r6/MIPS64r6 is enabled // (otherwise we would have matched the BLEZL instruction from the earlier // ISA's instead). // // We have: // 0b000110 sssss ttttt iiiiiiiiiiiiiiii // Invalid if rs == 0 // BLEZALC if rs == 0 && rt != 0 // BGEZALC if rs == rt && rt != 0 // BGEUC if rs != rt && rs != 0 && rt != 0 InsnType Rs = fieldFromInstruction(insn, 21, 5); InsnType Rt = fieldFromInstruction(insn, 16, 5); int64_t Imm = SignExtend64(fieldFromInstruction(insn, 0, 16), 16) * 4 + 4; bool HasRs = false; if (Rt == 0) return MCDisassembler::Fail; else if (Rs == 0) MI.setOpcode(Mips::BLEZALC); else if (Rs == Rt) MI.setOpcode(Mips::BGEZALC); else { HasRs = true; MI.setOpcode(Mips::BGEUC); } if (HasRs) MI.addOperand(MCOperand::createReg(getReg(Decoder, Mips::GPR32RegClassID, Rs))); MI.addOperand(MCOperand::createReg(getReg(Decoder, Mips::GPR32RegClassID, Rt))); MI.addOperand(MCOperand::createImm(Imm)); return MCDisassembler::Success; } // Override the generated disassembler to produce DEXT all the time. This is // for feature / behaviour parity with binutils. template static DecodeStatus DecodeDEXT(MCInst &MI, InsnType Insn, uint64_t Address, const void *Decoder) { unsigned Msbd = fieldFromInstruction(Insn, 11, 5); unsigned Lsb = fieldFromInstruction(Insn, 6, 5); unsigned Size = 0; unsigned Pos = 0; switch (MI.getOpcode()) { case Mips::DEXT: Pos = Lsb; Size = Msbd + 1; break; case Mips::DEXTM: Pos = Lsb; Size = Msbd + 1 + 32; break; case Mips::DEXTU: Pos = Lsb + 32; Size = Msbd + 1; break; default: llvm_unreachable("Unknown DEXT instruction!"); } MI.setOpcode(Mips::DEXT); InsnType Rs = fieldFromInstruction(Insn, 21, 5); InsnType Rt = fieldFromInstruction(Insn, 16, 5); MI.addOperand(MCOperand::createReg(getReg(Decoder, Mips::GPR64RegClassID, Rt))); MI.addOperand(MCOperand::createReg(getReg(Decoder, Mips::GPR64RegClassID, Rs))); MI.addOperand(MCOperand::createImm(Pos)); MI.addOperand(MCOperand::createImm(Size)); return MCDisassembler::Success; } // Override the generated disassembler to produce DINS all the time. This is // for feature / behaviour parity with binutils. template static DecodeStatus DecodeDINS(MCInst &MI, InsnType Insn, uint64_t Address, const void *Decoder) { unsigned Msbd = fieldFromInstruction(Insn, 11, 5); unsigned Lsb = fieldFromInstruction(Insn, 6, 5); unsigned Size = 0; unsigned Pos = 0; switch (MI.getOpcode()) { case Mips::DINS: Pos = Lsb; Size = Msbd + 1 - Pos; break; case Mips::DINSM: Pos = Lsb; Size = Msbd + 33 - Pos; break; case Mips::DINSU: Pos = Lsb + 32; // mbsd = pos + size - 33 // mbsd - pos + 33 = size Size = Msbd + 33 - Pos; break; default: llvm_unreachable("Unknown DINS instruction!"); } InsnType Rs = fieldFromInstruction(Insn, 21, 5); InsnType Rt = fieldFromInstruction(Insn, 16, 5); MI.setOpcode(Mips::DINS); MI.addOperand(MCOperand::createReg(getReg(Decoder, Mips::GPR64RegClassID, Rt))); MI.addOperand(MCOperand::createReg(getReg(Decoder, Mips::GPR64RegClassID, Rs))); MI.addOperand(MCOperand::createImm(Pos)); MI.addOperand(MCOperand::createImm(Size)); return MCDisassembler::Success; } // Auto-generated decoder wouldn't add the third operand for CRC32*. template static DecodeStatus DecodeCRC(MCInst &MI, InsnType Insn, uint64_t Address, const void *Decoder) { InsnType Rs = fieldFromInstruction(Insn, 21, 5); InsnType Rt = fieldFromInstruction(Insn, 16, 5); MI.addOperand(MCOperand::createReg(getReg(Decoder, Mips::GPR32RegClassID, Rt))); MI.addOperand(MCOperand::createReg(getReg(Decoder, Mips::GPR32RegClassID, Rs))); MI.addOperand(MCOperand::createReg(getReg(Decoder, Mips::GPR32RegClassID, Rt))); return MCDisassembler::Success; } /// Read two bytes from the ArrayRef and return 16 bit halfword sorted /// according to the given endianness. static DecodeStatus readInstruction16(ArrayRef Bytes, uint64_t Address, uint64_t &Size, uint32_t &Insn, bool IsBigEndian) { // We want to read exactly 2 Bytes of data. if (Bytes.size() < 2) { Size = 0; return MCDisassembler::Fail; } if (IsBigEndian) { Insn = (Bytes[0] << 8) | Bytes[1]; } else { Insn = (Bytes[1] << 8) | Bytes[0]; } return MCDisassembler::Success; } /// Read four bytes from the ArrayRef and return 32 bit word sorted /// according to the given endianness. static DecodeStatus readInstruction32(ArrayRef Bytes, uint64_t Address, uint64_t &Size, uint32_t &Insn, bool IsBigEndian, bool IsMicroMips) { // We want to read exactly 4 Bytes of data. if (Bytes.size() < 4) { Size = 0; return MCDisassembler::Fail; } // High 16 bits of a 32-bit microMIPS instruction (where the opcode is) // always precede the low 16 bits in the instruction stream (that is, they // are placed at lower addresses in the instruction stream). // // microMIPS byte ordering: // Big-endian: 0 | 1 | 2 | 3 // Little-endian: 1 | 0 | 3 | 2 if (IsBigEndian) { // Encoded as a big-endian 32-bit word in the stream. Insn = (Bytes[3] << 0) | (Bytes[2] << 8) | (Bytes[1] << 16) | (Bytes[0] << 24); } else { if (IsMicroMips) { Insn = (Bytes[2] << 0) | (Bytes[3] << 8) | (Bytes[0] << 16) | (Bytes[1] << 24); } else { Insn = (Bytes[0] << 0) | (Bytes[1] << 8) | (Bytes[2] << 16) | (Bytes[3] << 24); } } return MCDisassembler::Success; } DecodeStatus MipsDisassembler::getInstruction(MCInst &Instr, uint64_t &Size, ArrayRef Bytes, uint64_t Address, raw_ostream &VStream, raw_ostream &CStream) const { uint32_t Insn; DecodeStatus Result; Size = 0; if (IsMicroMips) { Result = readInstruction16(Bytes, Address, Size, Insn, IsBigEndian); if (Result == MCDisassembler::Fail) return MCDisassembler::Fail; if (hasMips32r6()) { LLVM_DEBUG( dbgs() << "Trying MicroMipsR616 table (16-bit instructions):\n"); // Calling the auto-generated decoder function for microMIPS32R6 // 16-bit instructions. Result = decodeInstruction(DecoderTableMicroMipsR616, Instr, Insn, Address, this, STI); if (Result != MCDisassembler::Fail) { Size = 2; return Result; } } LLVM_DEBUG(dbgs() << "Trying MicroMips16 table (16-bit instructions):\n"); // Calling the auto-generated decoder function for microMIPS 16-bit // instructions. Result = decodeInstruction(DecoderTableMicroMips16, Instr, Insn, Address, this, STI); if (Result != MCDisassembler::Fail) { Size = 2; return Result; } Result = readInstruction32(Bytes, Address, Size, Insn, IsBigEndian, true); if (Result == MCDisassembler::Fail) return MCDisassembler::Fail; if (hasMips32r6()) { LLVM_DEBUG( dbgs() << "Trying MicroMips32r632 table (32-bit instructions):\n"); // Calling the auto-generated decoder function. Result = decodeInstruction(DecoderTableMicroMipsR632, Instr, Insn, Address, this, STI); if (Result != MCDisassembler::Fail) { Size = 4; return Result; } } LLVM_DEBUG(dbgs() << "Trying MicroMips32 table (32-bit instructions):\n"); // Calling the auto-generated decoder function. Result = decodeInstruction(DecoderTableMicroMips32, Instr, Insn, Address, this, STI); if (Result != MCDisassembler::Fail) { Size = 4; return Result; } if (isFP64()) { LLVM_DEBUG(dbgs() << "Trying MicroMipsFP64 table (32-bit opcodes):\n"); Result = decodeInstruction(DecoderTableMicroMipsFP6432, Instr, Insn, Address, this, STI); if (Result != MCDisassembler::Fail) { Size = 4; return Result; } } // This is an invalid instruction. Claim that the Size is 2 bytes. Since // microMIPS instructions have a minimum alignment of 2, the next 2 bytes // could form a valid instruction. The two bytes we rejected as an // instruction could have actually beeen an inline constant pool that is // unconditionally branched over. Size = 2; return MCDisassembler::Fail; } // Attempt to read the instruction so that we can attempt to decode it. If // the buffer is not 4 bytes long, let the higher level logic figure out // what to do with a size of zero and MCDisassembler::Fail. Result = readInstruction32(Bytes, Address, Size, Insn, IsBigEndian, false); if (Result == MCDisassembler::Fail) return MCDisassembler::Fail; // The only instruction size for standard encoded MIPS. Size = 4; if (hasCOP3()) { LLVM_DEBUG(dbgs() << "Trying COP3_ table (32-bit opcodes):\n"); Result = decodeInstruction(DecoderTableCOP3_32, Instr, Insn, Address, this, STI); if (Result != MCDisassembler::Fail) return Result; } if (hasMips32r6() && isGP64()) { LLVM_DEBUG( dbgs() << "Trying Mips32r6_64r6 (GPR64) table (32-bit opcodes):\n"); Result = decodeInstruction(DecoderTableMips32r6_64r6_GP6432, Instr, Insn, Address, this, STI); if (Result != MCDisassembler::Fail) return Result; } if (hasMips32r6() && isPTR64()) { LLVM_DEBUG( dbgs() << "Trying Mips32r6_64r6 (PTR64) table (32-bit opcodes):\n"); Result = decodeInstruction(DecoderTableMips32r6_64r6_PTR6432, Instr, Insn, Address, this, STI); if (Result != MCDisassembler::Fail) return Result; } if (hasMips32r6()) { LLVM_DEBUG(dbgs() << "Trying Mips32r6_64r6 table (32-bit opcodes):\n"); Result = decodeInstruction(DecoderTableMips32r6_64r632, Instr, Insn, Address, this, STI); if (Result != MCDisassembler::Fail) return Result; } if (hasMips2() && isPTR64()) { LLVM_DEBUG( dbgs() << "Trying Mips32r6_64r6 (PTR64) table (32-bit opcodes):\n"); Result = decodeInstruction(DecoderTableMips32_64_PTR6432, Instr, Insn, Address, this, STI); if (Result != MCDisassembler::Fail) return Result; } if (hasCnMips()) { LLVM_DEBUG(dbgs() << "Trying CnMips table (32-bit opcodes):\n"); Result = decodeInstruction(DecoderTableCnMips32, Instr, Insn, + Address, this, STI); + if (Result != MCDisassembler::Fail) + return Result; + } + + if (hasCnMipsP()) { + LLVM_DEBUG(dbgs() << "Trying CnMipsP table (32-bit opcodes):\n"); + Result = decodeInstruction(DecoderTableCnMipsP32, Instr, Insn, Address, this, STI); if (Result != MCDisassembler::Fail) return Result; } if (isGP64()) { LLVM_DEBUG(dbgs() << "Trying Mips64 (GPR64) table (32-bit opcodes):\n"); Result = decodeInstruction(DecoderTableMips6432, Instr, Insn, Address, this, STI); if (Result != MCDisassembler::Fail) return Result; } if (isFP64()) { LLVM_DEBUG( dbgs() << "Trying MipsFP64 (64 bit FPU) table (32-bit opcodes):\n"); Result = decodeInstruction(DecoderTableMipsFP6432, Instr, Insn, Address, this, STI); if (Result != MCDisassembler::Fail) return Result; } LLVM_DEBUG(dbgs() << "Trying Mips table (32-bit opcodes):\n"); // Calling the auto-generated decoder function. Result = decodeInstruction(DecoderTableMips32, Instr, Insn, Address, this, STI); if (Result != MCDisassembler::Fail) return Result; return MCDisassembler::Fail; } static DecodeStatus DecodeCPU16RegsRegisterClass(MCInst &Inst, unsigned RegNo, uint64_t Address, const void *Decoder) { return MCDisassembler::Fail; } static DecodeStatus DecodeGPR64RegisterClass(MCInst &Inst, unsigned RegNo, uint64_t Address, const void *Decoder) { if (RegNo > 31) return MCDisassembler::Fail; unsigned Reg = getReg(Decoder, Mips::GPR64RegClassID, RegNo); Inst.addOperand(MCOperand::createReg(Reg)); return MCDisassembler::Success; } static DecodeStatus DecodeGPRMM16RegisterClass(MCInst &Inst, unsigned RegNo, uint64_t Address, const void *Decoder) { if (RegNo > 7) return MCDisassembler::Fail; unsigned Reg = getReg(Decoder, Mips::GPRMM16RegClassID, RegNo); Inst.addOperand(MCOperand::createReg(Reg)); return MCDisassembler::Success; } static DecodeStatus DecodeGPRMM16ZeroRegisterClass(MCInst &Inst, unsigned RegNo, uint64_t Address, const void *Decoder) { if (RegNo > 7) return MCDisassembler::Fail; unsigned Reg = getReg(Decoder, Mips::GPRMM16ZeroRegClassID, RegNo); Inst.addOperand(MCOperand::createReg(Reg)); return MCDisassembler::Success; } static DecodeStatus DecodeGPRMM16MovePRegisterClass(MCInst &Inst, unsigned RegNo, uint64_t Address, const void *Decoder) { if (RegNo > 7) return MCDisassembler::Fail; unsigned Reg = getReg(Decoder, Mips::GPRMM16MovePRegClassID, RegNo); Inst.addOperand(MCOperand::createReg(Reg)); return MCDisassembler::Success; } static DecodeStatus DecodeGPR32RegisterClass(MCInst &Inst, unsigned RegNo, uint64_t Address, const void *Decoder) { if (RegNo > 31) return MCDisassembler::Fail; unsigned Reg = getReg(Decoder, Mips::GPR32RegClassID, RegNo); Inst.addOperand(MCOperand::createReg(Reg)); return MCDisassembler::Success; } static DecodeStatus DecodePtrRegisterClass(MCInst &Inst, unsigned RegNo, uint64_t Address, const void *Decoder) { if (static_cast(Decoder)->isGP64()) return DecodeGPR64RegisterClass(Inst, RegNo, Address, Decoder); return DecodeGPR32RegisterClass(Inst, RegNo, Address, Decoder); } static DecodeStatus DecodeDSPRRegisterClass(MCInst &Inst, unsigned RegNo, uint64_t Address, const void *Decoder) { return DecodeGPR32RegisterClass(Inst, RegNo, Address, Decoder); } static DecodeStatus DecodeFGR64RegisterClass(MCInst &Inst, unsigned RegNo, uint64_t Address, const void *Decoder) { if (RegNo > 31) return MCDisassembler::Fail; unsigned Reg = getReg(Decoder, Mips::FGR64RegClassID, RegNo); Inst.addOperand(MCOperand::createReg(Reg)); return MCDisassembler::Success; } static DecodeStatus DecodeFGR32RegisterClass(MCInst &Inst, unsigned RegNo, uint64_t Address, const void *Decoder) { if (RegNo > 31) return MCDisassembler::Fail; unsigned Reg = getReg(Decoder, Mips::FGR32RegClassID, RegNo); Inst.addOperand(MCOperand::createReg(Reg)); return MCDisassembler::Success; } static DecodeStatus DecodeCCRRegisterClass(MCInst &Inst, unsigned RegNo, uint64_t Address, const void *Decoder) { if (RegNo > 31) return MCDisassembler::Fail; unsigned Reg = getReg(Decoder, Mips::CCRRegClassID, RegNo); Inst.addOperand(MCOperand::createReg(Reg)); return MCDisassembler::Success; } static DecodeStatus DecodeFCCRegisterClass(MCInst &Inst, unsigned RegNo, uint64_t Address, const void *Decoder) { if (RegNo > 7) return MCDisassembler::Fail; unsigned Reg = getReg(Decoder, Mips::FCCRegClassID, RegNo); Inst.addOperand(MCOperand::createReg(Reg)); return MCDisassembler::Success; } static DecodeStatus DecodeFGRCCRegisterClass(MCInst &Inst, unsigned RegNo, uint64_t Address, const void *Decoder) { if (RegNo > 31) return MCDisassembler::Fail; unsigned Reg = getReg(Decoder, Mips::FGRCCRegClassID, RegNo); Inst.addOperand(MCOperand::createReg(Reg)); return MCDisassembler::Success; } static DecodeStatus DecodeMem(MCInst &Inst, unsigned Insn, uint64_t Address, const void *Decoder) { int Offset = SignExtend32<16>(Insn & 0xffff); unsigned Reg = fieldFromInstruction(Insn, 16, 5); unsigned Base = fieldFromInstruction(Insn, 21, 5); Reg = getReg(Decoder, Mips::GPR32RegClassID, Reg); Base = getReg(Decoder, Mips::GPR32RegClassID, Base); if (Inst.getOpcode() == Mips::SC || Inst.getOpcode() == Mips::SCD) Inst.addOperand(MCOperand::createReg(Reg)); Inst.addOperand(MCOperand::createReg(Reg)); Inst.addOperand(MCOperand::createReg(Base)); Inst.addOperand(MCOperand::createImm(Offset)); return MCDisassembler::Success; } static DecodeStatus DecodeMemEVA(MCInst &Inst, unsigned Insn, uint64_t Address, const void *Decoder) { int Offset = SignExtend32<9>(Insn >> 7); unsigned Reg = fieldFromInstruction(Insn, 16, 5); unsigned Base = fieldFromInstruction(Insn, 21, 5); Reg = getReg(Decoder, Mips::GPR32RegClassID, Reg); Base = getReg(Decoder, Mips::GPR32RegClassID, Base); if (Inst.getOpcode() == Mips::SCE) Inst.addOperand(MCOperand::createReg(Reg)); Inst.addOperand(MCOperand::createReg(Reg)); Inst.addOperand(MCOperand::createReg(Base)); Inst.addOperand(MCOperand::createImm(Offset)); return MCDisassembler::Success; } static DecodeStatus DecodeLoadByte15(MCInst &Inst, unsigned Insn, uint64_t Address, const void *Decoder) { int Offset = SignExtend32<16>(Insn & 0xffff); unsigned Base = fieldFromInstruction(Insn, 16, 5); unsigned Reg = fieldFromInstruction(Insn, 21, 5); Base = getReg(Decoder, Mips::GPR32RegClassID, Base); Reg = getReg(Decoder, Mips::GPR32RegClassID, Reg); Inst.addOperand(MCOperand::createReg(Reg)); Inst.addOperand(MCOperand::createReg(Base)); Inst.addOperand(MCOperand::createImm(Offset)); return MCDisassembler::Success; } static DecodeStatus DecodeCacheOp(MCInst &Inst, unsigned Insn, uint64_t Address, const void *Decoder) { int Offset = SignExtend32<16>(Insn & 0xffff); unsigned Hint = fieldFromInstruction(Insn, 16, 5); unsigned Base = fieldFromInstruction(Insn, 21, 5); Base = getReg(Decoder, Mips::GPR32RegClassID, Base); Inst.addOperand(MCOperand::createReg(Base)); Inst.addOperand(MCOperand::createImm(Offset)); Inst.addOperand(MCOperand::createImm(Hint)); return MCDisassembler::Success; } static DecodeStatus DecodeCacheOpMM(MCInst &Inst, unsigned Insn, uint64_t Address, const void *Decoder) { int Offset = SignExtend32<12>(Insn & 0xfff); unsigned Base = fieldFromInstruction(Insn, 16, 5); unsigned Hint = fieldFromInstruction(Insn, 21, 5); Base = getReg(Decoder, Mips::GPR32RegClassID, Base); Inst.addOperand(MCOperand::createReg(Base)); Inst.addOperand(MCOperand::createImm(Offset)); Inst.addOperand(MCOperand::createImm(Hint)); return MCDisassembler::Success; } static DecodeStatus DecodePrefeOpMM(MCInst &Inst, unsigned Insn, uint64_t Address, const void *Decoder) { int Offset = SignExtend32<9>(Insn & 0x1ff); unsigned Base = fieldFromInstruction(Insn, 16, 5); unsigned Hint = fieldFromInstruction(Insn, 21, 5); Base = getReg(Decoder, Mips::GPR32RegClassID, Base); Inst.addOperand(MCOperand::createReg(Base)); Inst.addOperand(MCOperand::createImm(Offset)); Inst.addOperand(MCOperand::createImm(Hint)); return MCDisassembler::Success; } static DecodeStatus DecodeCacheeOp_CacheOpR6(MCInst &Inst, unsigned Insn, uint64_t Address, const void *Decoder) { int Offset = SignExtend32<9>(Insn >> 7); unsigned Hint = fieldFromInstruction(Insn, 16, 5); unsigned Base = fieldFromInstruction(Insn, 21, 5); Base = getReg(Decoder, Mips::GPR32RegClassID, Base); Inst.addOperand(MCOperand::createReg(Base)); Inst.addOperand(MCOperand::createImm(Offset)); Inst.addOperand(MCOperand::createImm(Hint)); return MCDisassembler::Success; } static DecodeStatus DecodeSyncI(MCInst &Inst, unsigned Insn, uint64_t Address, const void *Decoder) { int Offset = SignExtend32<16>(Insn & 0xffff); unsigned Base = fieldFromInstruction(Insn, 21, 5); Base = getReg(Decoder, Mips::GPR32RegClassID, Base); Inst.addOperand(MCOperand::createReg(Base)); Inst.addOperand(MCOperand::createImm(Offset)); return MCDisassembler::Success; } static DecodeStatus DecodeSyncI_MM(MCInst &Inst, unsigned Insn, uint64_t Address, const void *Decoder) { int Offset = SignExtend32<16>(Insn & 0xffff); unsigned Base = fieldFromInstruction(Insn, 16, 5); Base = getReg(Decoder, Mips::GPR32RegClassID, Base); Inst.addOperand(MCOperand::createReg(Base)); Inst.addOperand(MCOperand::createImm(Offset)); return MCDisassembler::Success; } static DecodeStatus DecodeSynciR6(MCInst &Inst, unsigned Insn, uint64_t Address, const void *Decoder) { int Immediate = SignExtend32<16>(Insn & 0xffff); unsigned Base = fieldFromInstruction(Insn, 16, 5); Base = getReg(Decoder, Mips::GPR32RegClassID, Base); Inst.addOperand(MCOperand::createReg(Base)); Inst.addOperand(MCOperand::createImm(Immediate)); return MCDisassembler::Success; } static DecodeStatus DecodeMSA128Mem(MCInst &Inst, unsigned Insn, uint64_t Address, const void *Decoder) { int Offset = SignExtend32<10>(fieldFromInstruction(Insn, 16, 10)); unsigned Reg = fieldFromInstruction(Insn, 6, 5); unsigned Base = fieldFromInstruction(Insn, 11, 5); Reg = getReg(Decoder, Mips::MSA128BRegClassID, Reg); Base = getReg(Decoder, Mips::GPR32RegClassID, Base); Inst.addOperand(MCOperand::createReg(Reg)); Inst.addOperand(MCOperand::createReg(Base)); // The immediate field of an LD/ST instruction is scaled which means it must // be multiplied (when decoding) by the size (in bytes) of the instructions' // data format. // .b - 1 byte // .h - 2 bytes // .w - 4 bytes // .d - 8 bytes switch(Inst.getOpcode()) { default: assert(false && "Unexpected instruction"); return MCDisassembler::Fail; break; case Mips::LD_B: case Mips::ST_B: Inst.addOperand(MCOperand::createImm(Offset)); break; case Mips::LD_H: case Mips::ST_H: Inst.addOperand(MCOperand::createImm(Offset * 2)); break; case Mips::LD_W: case Mips::ST_W: Inst.addOperand(MCOperand::createImm(Offset * 4)); break; case Mips::LD_D: case Mips::ST_D: Inst.addOperand(MCOperand::createImm(Offset * 8)); break; } return MCDisassembler::Success; } static DecodeStatus DecodeMemMMImm4(MCInst &Inst, unsigned Insn, uint64_t Address, const void *Decoder) { unsigned Offset = Insn & 0xf; unsigned Reg = fieldFromInstruction(Insn, 7, 3); unsigned Base = fieldFromInstruction(Insn, 4, 3); switch (Inst.getOpcode()) { case Mips::LBU16_MM: case Mips::LHU16_MM: case Mips::LW16_MM: if (DecodeGPRMM16RegisterClass(Inst, Reg, Address, Decoder) == MCDisassembler::Fail) return MCDisassembler::Fail; break; case Mips::SB16_MM: case Mips::SB16_MMR6: case Mips::SH16_MM: case Mips::SH16_MMR6: case Mips::SW16_MM: case Mips::SW16_MMR6: if (DecodeGPRMM16ZeroRegisterClass(Inst, Reg, Address, Decoder) == MCDisassembler::Fail) return MCDisassembler::Fail; break; } if (DecodeGPRMM16RegisterClass(Inst, Base, Address, Decoder) == MCDisassembler::Fail) return MCDisassembler::Fail; switch (Inst.getOpcode()) { case Mips::LBU16_MM: if (Offset == 0xf) Inst.addOperand(MCOperand::createImm(-1)); else Inst.addOperand(MCOperand::createImm(Offset)); break; case Mips::SB16_MM: case Mips::SB16_MMR6: Inst.addOperand(MCOperand::createImm(Offset)); break; case Mips::LHU16_MM: case Mips::SH16_MM: case Mips::SH16_MMR6: Inst.addOperand(MCOperand::createImm(Offset << 1)); break; case Mips::LW16_MM: case Mips::SW16_MM: case Mips::SW16_MMR6: Inst.addOperand(MCOperand::createImm(Offset << 2)); break; } return MCDisassembler::Success; } static DecodeStatus DecodeMemMMSPImm5Lsl2(MCInst &Inst, unsigned Insn, uint64_t Address, const void *Decoder) { unsigned Offset = Insn & 0x1F; unsigned Reg = fieldFromInstruction(Insn, 5, 5); Reg = getReg(Decoder, Mips::GPR32RegClassID, Reg); Inst.addOperand(MCOperand::createReg(Reg)); Inst.addOperand(MCOperand::createReg(Mips::SP)); Inst.addOperand(MCOperand::createImm(Offset << 2)); return MCDisassembler::Success; } static DecodeStatus DecodeMemMMGPImm7Lsl2(MCInst &Inst, unsigned Insn, uint64_t Address, const void *Decoder) { unsigned Offset = Insn & 0x7F; unsigned Reg = fieldFromInstruction(Insn, 7, 3); Reg = getReg(Decoder, Mips::GPR32RegClassID, Reg); Inst.addOperand(MCOperand::createReg(Reg)); Inst.addOperand(MCOperand::createReg(Mips::GP)); Inst.addOperand(MCOperand::createImm(Offset << 2)); return MCDisassembler::Success; } static DecodeStatus DecodeMemMMReglistImm4Lsl2(MCInst &Inst, unsigned Insn, uint64_t Address, const void *Decoder) { int Offset; switch (Inst.getOpcode()) { case Mips::LWM16_MMR6: case Mips::SWM16_MMR6: Offset = fieldFromInstruction(Insn, 4, 4); break; default: Offset = SignExtend32<4>(Insn & 0xf); break; } if (DecodeRegListOperand16(Inst, Insn, Address, Decoder) == MCDisassembler::Fail) return MCDisassembler::Fail; Inst.addOperand(MCOperand::createReg(Mips::SP)); Inst.addOperand(MCOperand::createImm(Offset << 2)); return MCDisassembler::Success; } static DecodeStatus DecodeMemMMImm9(MCInst &Inst, unsigned Insn, uint64_t Address, const void *Decoder) { int Offset = SignExtend32<9>(Insn & 0x1ff); unsigned Reg = fieldFromInstruction(Insn, 21, 5); unsigned Base = fieldFromInstruction(Insn, 16, 5); Reg = getReg(Decoder, Mips::GPR32RegClassID, Reg); Base = getReg(Decoder, Mips::GPR32RegClassID, Base); if (Inst.getOpcode() == Mips::SCE_MM || Inst.getOpcode() == Mips::SC_MMR6) Inst.addOperand(MCOperand::createReg(Reg)); Inst.addOperand(MCOperand::createReg(Reg)); Inst.addOperand(MCOperand::createReg(Base)); Inst.addOperand(MCOperand::createImm(Offset)); return MCDisassembler::Success; } static DecodeStatus DecodeMemMMImm12(MCInst &Inst, unsigned Insn, uint64_t Address, const void *Decoder) { int Offset = SignExtend32<12>(Insn & 0x0fff); unsigned Reg = fieldFromInstruction(Insn, 21, 5); unsigned Base = fieldFromInstruction(Insn, 16, 5); Reg = getReg(Decoder, Mips::GPR32RegClassID, Reg); Base = getReg(Decoder, Mips::GPR32RegClassID, Base); switch (Inst.getOpcode()) { case Mips::SWM32_MM: case Mips::LWM32_MM: if (DecodeRegListOperand(Inst, Insn, Address, Decoder) == MCDisassembler::Fail) return MCDisassembler::Fail; Inst.addOperand(MCOperand::createReg(Base)); Inst.addOperand(MCOperand::createImm(Offset)); break; case Mips::SC_MM: Inst.addOperand(MCOperand::createReg(Reg)); LLVM_FALLTHROUGH; default: Inst.addOperand(MCOperand::createReg(Reg)); if (Inst.getOpcode() == Mips::LWP_MM || Inst.getOpcode() == Mips::SWP_MM) Inst.addOperand(MCOperand::createReg(Reg+1)); Inst.addOperand(MCOperand::createReg(Base)); Inst.addOperand(MCOperand::createImm(Offset)); } return MCDisassembler::Success; } static DecodeStatus DecodeMemMMImm16(MCInst &Inst, unsigned Insn, uint64_t Address, const void *Decoder) { int Offset = SignExtend32<16>(Insn & 0xffff); unsigned Reg = fieldFromInstruction(Insn, 21, 5); unsigned Base = fieldFromInstruction(Insn, 16, 5); Reg = getReg(Decoder, Mips::GPR32RegClassID, Reg); Base = getReg(Decoder, Mips::GPR32RegClassID, Base); Inst.addOperand(MCOperand::createReg(Reg)); Inst.addOperand(MCOperand::createReg(Base)); Inst.addOperand(MCOperand::createImm(Offset)); return MCDisassembler::Success; } static DecodeStatus DecodeFMem(MCInst &Inst, unsigned Insn, uint64_t Address, const void *Decoder) { int Offset = SignExtend32<16>(Insn & 0xffff); unsigned Reg = fieldFromInstruction(Insn, 16, 5); unsigned Base = fieldFromInstruction(Insn, 21, 5); Reg = getReg(Decoder, Mips::FGR64RegClassID, Reg); Base = getReg(Decoder, Mips::GPR32RegClassID, Base); Inst.addOperand(MCOperand::createReg(Reg)); Inst.addOperand(MCOperand::createReg(Base)); Inst.addOperand(MCOperand::createImm(Offset)); return MCDisassembler::Success; } static DecodeStatus DecodeFMemMMR2(MCInst &Inst, unsigned Insn, uint64_t Address, const void *Decoder) { // This function is the same as DecodeFMem but with the Reg and Base fields // swapped according to microMIPS spec. int Offset = SignExtend32<16>(Insn & 0xffff); unsigned Base = fieldFromInstruction(Insn, 16, 5); unsigned Reg = fieldFromInstruction(Insn, 21, 5); Reg = getReg(Decoder, Mips::FGR64RegClassID, Reg); Base = getReg(Decoder, Mips::GPR32RegClassID, Base); Inst.addOperand(MCOperand::createReg(Reg)); Inst.addOperand(MCOperand::createReg(Base)); Inst.addOperand(MCOperand::createImm(Offset)); return MCDisassembler::Success; } static DecodeStatus DecodeFMem2(MCInst &Inst, unsigned Insn, uint64_t Address, const void *Decoder) { int Offset = SignExtend32<16>(Insn & 0xffff); unsigned Reg = fieldFromInstruction(Insn, 16, 5); unsigned Base = fieldFromInstruction(Insn, 21, 5); Reg = getReg(Decoder, Mips::COP2RegClassID, Reg); Base = getReg(Decoder, Mips::GPR32RegClassID, Base); Inst.addOperand(MCOperand::createReg(Reg)); Inst.addOperand(MCOperand::createReg(Base)); Inst.addOperand(MCOperand::createImm(Offset)); return MCDisassembler::Success; } static DecodeStatus DecodeFMem3(MCInst &Inst, unsigned Insn, uint64_t Address, const void *Decoder) { int Offset = SignExtend32<16>(Insn & 0xffff); unsigned Reg = fieldFromInstruction(Insn, 16, 5); unsigned Base = fieldFromInstruction(Insn, 21, 5); Reg = getReg(Decoder, Mips::COP3RegClassID, Reg); Base = getReg(Decoder, Mips::GPR32RegClassID, Base); Inst.addOperand(MCOperand::createReg(Reg)); Inst.addOperand(MCOperand::createReg(Base)); Inst.addOperand(MCOperand::createImm(Offset)); return MCDisassembler::Success; } static DecodeStatus DecodeFMemCop2R6(MCInst &Inst, unsigned Insn, uint64_t Address, const void *Decoder) { int Offset = SignExtend32<11>(Insn & 0x07ff); unsigned Reg = fieldFromInstruction(Insn, 16, 5); unsigned Base = fieldFromInstruction(Insn, 11, 5); Reg = getReg(Decoder, Mips::COP2RegClassID, Reg); Base = getReg(Decoder, Mips::GPR32RegClassID, Base); Inst.addOperand(MCOperand::createReg(Reg)); Inst.addOperand(MCOperand::createReg(Base)); Inst.addOperand(MCOperand::createImm(Offset)); return MCDisassembler::Success; } static DecodeStatus DecodeFMemCop2MMR6(MCInst &Inst, unsigned Insn, uint64_t Address, const void *Decoder) { int Offset = SignExtend32<11>(Insn & 0x07ff); unsigned Reg = fieldFromInstruction(Insn, 21, 5); unsigned Base = fieldFromInstruction(Insn, 16, 5); Reg = getReg(Decoder, Mips::COP2RegClassID, Reg); Base = getReg(Decoder, Mips::GPR32RegClassID, Base); Inst.addOperand(MCOperand::createReg(Reg)); Inst.addOperand(MCOperand::createReg(Base)); Inst.addOperand(MCOperand::createImm(Offset)); return MCDisassembler::Success; } static DecodeStatus DecodeSpecial3LlSc(MCInst &Inst, unsigned Insn, uint64_t Address, const void *Decoder) { int64_t Offset = SignExtend64<9>((Insn >> 7) & 0x1ff); unsigned Rt = fieldFromInstruction(Insn, 16, 5); unsigned Base = fieldFromInstruction(Insn, 21, 5); Rt = getReg(Decoder, Mips::GPR32RegClassID, Rt); Base = getReg(Decoder, Mips::GPR32RegClassID, Base); if(Inst.getOpcode() == Mips::SC_R6 || Inst.getOpcode() == Mips::SCD_R6){ Inst.addOperand(MCOperand::createReg(Rt)); } Inst.addOperand(MCOperand::createReg(Rt)); Inst.addOperand(MCOperand::createReg(Base)); Inst.addOperand(MCOperand::createImm(Offset)); return MCDisassembler::Success; } static DecodeStatus DecodeHWRegsRegisterClass(MCInst &Inst, unsigned RegNo, uint64_t Address, const void *Decoder) { // Currently only hardware register 29 is supported. if (RegNo != 29) return MCDisassembler::Fail; Inst.addOperand(MCOperand::createReg(Mips::HWR29)); return MCDisassembler::Success; } static DecodeStatus DecodeAFGR64RegisterClass(MCInst &Inst, unsigned RegNo, uint64_t Address, const void *Decoder) { if (RegNo > 30 || RegNo %2) return MCDisassembler::Fail; unsigned Reg = getReg(Decoder, Mips::AFGR64RegClassID, RegNo /2); Inst.addOperand(MCOperand::createReg(Reg)); return MCDisassembler::Success; } static DecodeStatus DecodeACC64DSPRegisterClass(MCInst &Inst, unsigned RegNo, uint64_t Address, const void *Decoder) { if (RegNo >= 4) return MCDisassembler::Fail; unsigned Reg = getReg(Decoder, Mips::ACC64DSPRegClassID, RegNo); Inst.addOperand(MCOperand::createReg(Reg)); return MCDisassembler::Success; } static DecodeStatus DecodeHI32DSPRegisterClass(MCInst &Inst, unsigned RegNo, uint64_t Address, const void *Decoder) { if (RegNo >= 4) return MCDisassembler::Fail; unsigned Reg = getReg(Decoder, Mips::HI32DSPRegClassID, RegNo); Inst.addOperand(MCOperand::createReg(Reg)); return MCDisassembler::Success; } static DecodeStatus DecodeLO32DSPRegisterClass(MCInst &Inst, unsigned RegNo, uint64_t Address, const void *Decoder) { if (RegNo >= 4) return MCDisassembler::Fail; unsigned Reg = getReg(Decoder, Mips::LO32DSPRegClassID, RegNo); Inst.addOperand(MCOperand::createReg(Reg)); return MCDisassembler::Success; } static DecodeStatus DecodeMSA128BRegisterClass(MCInst &Inst, unsigned RegNo, uint64_t Address, const void *Decoder) { if (RegNo > 31) return MCDisassembler::Fail; unsigned Reg = getReg(Decoder, Mips::MSA128BRegClassID, RegNo); Inst.addOperand(MCOperand::createReg(Reg)); return MCDisassembler::Success; } static DecodeStatus DecodeMSA128HRegisterClass(MCInst &Inst, unsigned RegNo, uint64_t Address, const void *Decoder) { if (RegNo > 31) return MCDisassembler::Fail; unsigned Reg = getReg(Decoder, Mips::MSA128HRegClassID, RegNo); Inst.addOperand(MCOperand::createReg(Reg)); return MCDisassembler::Success; } static DecodeStatus DecodeMSA128WRegisterClass(MCInst &Inst, unsigned RegNo, uint64_t Address, const void *Decoder) { if (RegNo > 31) return MCDisassembler::Fail; unsigned Reg = getReg(Decoder, Mips::MSA128WRegClassID, RegNo); Inst.addOperand(MCOperand::createReg(Reg)); return MCDisassembler::Success; } static DecodeStatus DecodeMSA128DRegisterClass(MCInst &Inst, unsigned RegNo, uint64_t Address, const void *Decoder) { if (RegNo > 31) return MCDisassembler::Fail; unsigned Reg = getReg(Decoder, Mips::MSA128DRegClassID, RegNo); Inst.addOperand(MCOperand::createReg(Reg)); return MCDisassembler::Success; } static DecodeStatus DecodeMSACtrlRegisterClass(MCInst &Inst, unsigned RegNo, uint64_t Address, const void *Decoder) { if (RegNo > 7) return MCDisassembler::Fail; unsigned Reg = getReg(Decoder, Mips::MSACtrlRegClassID, RegNo); Inst.addOperand(MCOperand::createReg(Reg)); return MCDisassembler::Success; } static DecodeStatus DecodeCOP0RegisterClass(MCInst &Inst, unsigned RegNo, uint64_t Address, const void *Decoder) { if (RegNo > 31) return MCDisassembler::Fail; unsigned Reg = getReg(Decoder, Mips::COP0RegClassID, RegNo); Inst.addOperand(MCOperand::createReg(Reg)); return MCDisassembler::Success; } static DecodeStatus DecodeCOP2RegisterClass(MCInst &Inst, unsigned RegNo, uint64_t Address, const void *Decoder) { if (RegNo > 31) return MCDisassembler::Fail; unsigned Reg = getReg(Decoder, Mips::COP2RegClassID, RegNo); Inst.addOperand(MCOperand::createReg(Reg)); return MCDisassembler::Success; } static DecodeStatus DecodeBranchTarget(MCInst &Inst, unsigned Offset, uint64_t Address, const void *Decoder) { int32_t BranchOffset = (SignExtend32<16>(Offset) * 4) + 4; Inst.addOperand(MCOperand::createImm(BranchOffset)); return MCDisassembler::Success; } static DecodeStatus DecodeBranchTarget1SImm16(MCInst &Inst, unsigned Offset, uint64_t Address, const void *Decoder) { int32_t BranchOffset = (SignExtend32<16>(Offset) * 2); Inst.addOperand(MCOperand::createImm(BranchOffset)); return MCDisassembler::Success; } static DecodeStatus DecodeJumpTarget(MCInst &Inst, unsigned Insn, uint64_t Address, const void *Decoder) { unsigned JumpOffset = fieldFromInstruction(Insn, 0, 26) << 2; Inst.addOperand(MCOperand::createImm(JumpOffset)); return MCDisassembler::Success; } static DecodeStatus DecodeBranchTarget21(MCInst &Inst, unsigned Offset, uint64_t Address, const void *Decoder) { int32_t BranchOffset = SignExtend32<21>(Offset) * 4 + 4; Inst.addOperand(MCOperand::createImm(BranchOffset)); return MCDisassembler::Success; } static DecodeStatus DecodeBranchTarget21MM(MCInst &Inst, unsigned Offset, uint64_t Address, const void *Decoder) { int32_t BranchOffset = SignExtend32<21>(Offset) * 4 + 4; Inst.addOperand(MCOperand::createImm(BranchOffset)); return MCDisassembler::Success; } static DecodeStatus DecodeBranchTarget26(MCInst &Inst, unsigned Offset, uint64_t Address, const void *Decoder) { int32_t BranchOffset = SignExtend32<26>(Offset) * 4 + 4; Inst.addOperand(MCOperand::createImm(BranchOffset)); return MCDisassembler::Success; } static DecodeStatus DecodeBranchTarget7MM(MCInst &Inst, unsigned Offset, uint64_t Address, const void *Decoder) { int32_t BranchOffset = SignExtend32<8>(Offset << 1); Inst.addOperand(MCOperand::createImm(BranchOffset)); return MCDisassembler::Success; } static DecodeStatus DecodeBranchTarget10MM(MCInst &Inst, unsigned Offset, uint64_t Address, const void *Decoder) { int32_t BranchOffset = SignExtend32<11>(Offset << 1); Inst.addOperand(MCOperand::createImm(BranchOffset)); return MCDisassembler::Success; } static DecodeStatus DecodeBranchTargetMM(MCInst &Inst, unsigned Offset, uint64_t Address, const void *Decoder) { int32_t BranchOffset = SignExtend32<16>(Offset) * 2 + 4; Inst.addOperand(MCOperand::createImm(BranchOffset)); return MCDisassembler::Success; } static DecodeStatus DecodeBranchTarget26MM(MCInst &Inst, unsigned Offset, uint64_t Address, const void *Decoder) { int32_t BranchOffset = SignExtend32<27>(Offset << 1); Inst.addOperand(MCOperand::createImm(BranchOffset)); return MCDisassembler::Success; } static DecodeStatus DecodeJumpTargetMM(MCInst &Inst, unsigned Insn, uint64_t Address, const void *Decoder) { unsigned JumpOffset = fieldFromInstruction(Insn, 0, 26) << 1; Inst.addOperand(MCOperand::createImm(JumpOffset)); return MCDisassembler::Success; } static DecodeStatus DecodeAddiur2Simm7(MCInst &Inst, unsigned Value, uint64_t Address, const void *Decoder) { if (Value == 0) Inst.addOperand(MCOperand::createImm(1)); else if (Value == 0x7) Inst.addOperand(MCOperand::createImm(-1)); else Inst.addOperand(MCOperand::createImm(Value << 2)); return MCDisassembler::Success; } static DecodeStatus DecodeLi16Imm(MCInst &Inst, unsigned Value, uint64_t Address, const void *Decoder) { if (Value == 0x7F) Inst.addOperand(MCOperand::createImm(-1)); else Inst.addOperand(MCOperand::createImm(Value)); return MCDisassembler::Success; } static DecodeStatus DecodePOOL16BEncodedField(MCInst &Inst, unsigned Value, uint64_t Address, const void *Decoder) { Inst.addOperand(MCOperand::createImm(Value == 0x0 ? 8 : Value)); return MCDisassembler::Success; } template static DecodeStatus DecodeUImmWithOffsetAndScale(MCInst &Inst, unsigned Value, uint64_t Address, const void *Decoder) { Value &= ((1 << Bits) - 1); Value *= Scale; Inst.addOperand(MCOperand::createImm(Value + Offset)); return MCDisassembler::Success; } template static DecodeStatus DecodeSImmWithOffsetAndScale(MCInst &Inst, unsigned Value, uint64_t Address, const void *Decoder) { int32_t Imm = SignExtend32(Value) * ScaleBy; Inst.addOperand(MCOperand::createImm(Imm + Offset)); return MCDisassembler::Success; } static DecodeStatus DecodeInsSize(MCInst &Inst, unsigned Insn, uint64_t Address, const void *Decoder) { // First we need to grab the pos(lsb) from MCInst. // This function only handles the 32 bit variants of ins, as dins // variants are handled differently. int Pos = Inst.getOperand(2).getImm(); int Size = (int) Insn - Pos + 1; Inst.addOperand(MCOperand::createImm(SignExtend32<16>(Size))); return MCDisassembler::Success; } static DecodeStatus DecodeSimm19Lsl2(MCInst &Inst, unsigned Insn, uint64_t Address, const void *Decoder) { Inst.addOperand(MCOperand::createImm(SignExtend32<19>(Insn) * 4)); return MCDisassembler::Success; } static DecodeStatus DecodeSimm18Lsl3(MCInst &Inst, unsigned Insn, uint64_t Address, const void *Decoder) { Inst.addOperand(MCOperand::createImm(SignExtend32<18>(Insn) * 8)); return MCDisassembler::Success; } static DecodeStatus DecodeSimm9SP(MCInst &Inst, unsigned Insn, uint64_t Address, const void *Decoder) { int32_t DecodedValue; switch (Insn) { case 0: DecodedValue = 256; break; case 1: DecodedValue = 257; break; case 510: DecodedValue = -258; break; case 511: DecodedValue = -257; break; default: DecodedValue = SignExtend32<9>(Insn); break; } Inst.addOperand(MCOperand::createImm(DecodedValue * 4)); return MCDisassembler::Success; } static DecodeStatus DecodeANDI16Imm(MCInst &Inst, unsigned Insn, uint64_t Address, const void *Decoder) { // Insn must be >= 0, since it is unsigned that condition is always true. assert(Insn < 16); int32_t DecodedValues[] = {128, 1, 2, 3, 4, 7, 8, 15, 16, 31, 32, 63, 64, 255, 32768, 65535}; Inst.addOperand(MCOperand::createImm(DecodedValues[Insn])); return MCDisassembler::Success; } static DecodeStatus DecodeRegListOperand(MCInst &Inst, unsigned Insn, uint64_t Address, const void *Decoder) { unsigned Regs[] = {Mips::S0, Mips::S1, Mips::S2, Mips::S3, Mips::S4, Mips::S5, Mips::S6, Mips::S7, Mips::FP}; unsigned RegNum; unsigned RegLst = fieldFromInstruction(Insn, 21, 5); // Empty register lists are not allowed. if (RegLst == 0) return MCDisassembler::Fail; RegNum = RegLst & 0xf; // RegLst values 10-15, and 26-31 are reserved. if (RegNum > 9) return MCDisassembler::Fail; for (unsigned i = 0; i < RegNum; i++) Inst.addOperand(MCOperand::createReg(Regs[i])); if (RegLst & 0x10) Inst.addOperand(MCOperand::createReg(Mips::RA)); return MCDisassembler::Success; } static DecodeStatus DecodeRegListOperand16(MCInst &Inst, unsigned Insn, uint64_t Address, const void *Decoder) { unsigned Regs[] = {Mips::S0, Mips::S1, Mips::S2, Mips::S3}; unsigned RegLst; switch(Inst.getOpcode()) { default: RegLst = fieldFromInstruction(Insn, 4, 2); break; case Mips::LWM16_MMR6: case Mips::SWM16_MMR6: RegLst = fieldFromInstruction(Insn, 8, 2); break; } unsigned RegNum = RegLst & 0x3; for (unsigned i = 0; i <= RegNum; i++) Inst.addOperand(MCOperand::createReg(Regs[i])); Inst.addOperand(MCOperand::createReg(Mips::RA)); return MCDisassembler::Success; } static DecodeStatus DecodeMovePOperands(MCInst &Inst, unsigned Insn, uint64_t Address, const void *Decoder) { unsigned RegPair = fieldFromInstruction(Insn, 7, 3); if (DecodeMovePRegPair(Inst, RegPair, Address, Decoder) == MCDisassembler::Fail) return MCDisassembler::Fail; unsigned RegRs; if (static_cast(Decoder)->hasMips32r6()) RegRs = fieldFromInstruction(Insn, 0, 2) | (fieldFromInstruction(Insn, 3, 1) << 2); else RegRs = fieldFromInstruction(Insn, 1, 3); if (DecodeGPRMM16MovePRegisterClass(Inst, RegRs, Address, Decoder) == MCDisassembler::Fail) return MCDisassembler::Fail; unsigned RegRt = fieldFromInstruction(Insn, 4, 3); if (DecodeGPRMM16MovePRegisterClass(Inst, RegRt, Address, Decoder) == MCDisassembler::Fail) return MCDisassembler::Fail; return MCDisassembler::Success; } static DecodeStatus DecodeMovePRegPair(MCInst &Inst, unsigned RegPair, uint64_t Address, const void *Decoder) { switch (RegPair) { default: return MCDisassembler::Fail; case 0: Inst.addOperand(MCOperand::createReg(Mips::A1)); Inst.addOperand(MCOperand::createReg(Mips::A2)); break; case 1: Inst.addOperand(MCOperand::createReg(Mips::A1)); Inst.addOperand(MCOperand::createReg(Mips::A3)); break; case 2: Inst.addOperand(MCOperand::createReg(Mips::A2)); Inst.addOperand(MCOperand::createReg(Mips::A3)); break; case 3: Inst.addOperand(MCOperand::createReg(Mips::A0)); Inst.addOperand(MCOperand::createReg(Mips::S5)); break; case 4: Inst.addOperand(MCOperand::createReg(Mips::A0)); Inst.addOperand(MCOperand::createReg(Mips::S6)); break; case 5: Inst.addOperand(MCOperand::createReg(Mips::A0)); Inst.addOperand(MCOperand::createReg(Mips::A1)); break; case 6: Inst.addOperand(MCOperand::createReg(Mips::A0)); Inst.addOperand(MCOperand::createReg(Mips::A2)); break; case 7: Inst.addOperand(MCOperand::createReg(Mips::A0)); Inst.addOperand(MCOperand::createReg(Mips::A3)); break; } return MCDisassembler::Success; } static DecodeStatus DecodeSimm23Lsl2(MCInst &Inst, unsigned Insn, uint64_t Address, const void *Decoder) { Inst.addOperand(MCOperand::createImm(SignExtend32<25>(Insn << 2))); return MCDisassembler::Success; } template static DecodeStatus DecodeBgtzGroupBranchMMR6(MCInst &MI, InsnType insn, uint64_t Address, const void *Decoder) { // We have: // 0b000111 ttttt sssss iiiiiiiiiiiiiiii // Invalid if rt == 0 // BGTZALC_MMR6 if rs == 0 && rt != 0 // BLTZALC_MMR6 if rs != 0 && rs == rt // BLTUC_MMR6 if rs != 0 && rs != rt InsnType Rt = fieldFromInstruction(insn, 21, 5); InsnType Rs = fieldFromInstruction(insn, 16, 5); InsnType Imm = 0; bool HasRs = false; bool HasRt = false; if (Rt == 0) return MCDisassembler::Fail; else if (Rs == 0) { MI.setOpcode(Mips::BGTZALC_MMR6); HasRt = true; Imm = SignExtend64(fieldFromInstruction(insn, 0, 16), 16) * 2 + 4; } else if (Rs == Rt) { MI.setOpcode(Mips::BLTZALC_MMR6); HasRs = true; Imm = SignExtend64(fieldFromInstruction(insn, 0, 16), 16) * 2 + 4; } else { MI.setOpcode(Mips::BLTUC_MMR6); HasRs = true; HasRt = true; Imm = SignExtend64(fieldFromInstruction(insn, 0, 16), 16) * 4 + 4; } if (HasRs) MI.addOperand( MCOperand::createReg(getReg(Decoder, Mips::GPR32RegClassID, Rs))); if (HasRt) MI.addOperand( MCOperand::createReg(getReg(Decoder, Mips::GPR32RegClassID, Rt))); MI.addOperand(MCOperand::createImm(Imm)); return MCDisassembler::Success; } template static DecodeStatus DecodeBlezGroupBranchMMR6(MCInst &MI, InsnType insn, uint64_t Address, const void *Decoder) { // We have: // 0b000110 ttttt sssss iiiiiiiiiiiiiiii // Invalid if rt == 0 // BLEZALC_MMR6 if rs == 0 && rt != 0 // BGEZALC_MMR6 if rs == rt && rt != 0 // BGEUC_MMR6 if rs != rt && rs != 0 && rt != 0 InsnType Rt = fieldFromInstruction(insn, 21, 5); InsnType Rs = fieldFromInstruction(insn, 16, 5); InsnType Imm = 0; bool HasRs = false; if (Rt == 0) return MCDisassembler::Fail; else if (Rs == 0) { MI.setOpcode(Mips::BLEZALC_MMR6); Imm = SignExtend64(fieldFromInstruction(insn, 0, 16), 16) * 2 + 4; } else if (Rs == Rt) { MI.setOpcode(Mips::BGEZALC_MMR6); Imm = SignExtend64(fieldFromInstruction(insn, 0, 16), 16) * 2 + 4; } else { HasRs = true; MI.setOpcode(Mips::BGEUC_MMR6); Imm = SignExtend64(fieldFromInstruction(insn, 0, 16), 16) * 4 + 4; } if (HasRs) MI.addOperand( MCOperand::createReg(getReg(Decoder, Mips::GPR32RegClassID, Rs))); MI.addOperand( MCOperand::createReg(getReg(Decoder, Mips::GPR32RegClassID, Rt))); MI.addOperand(MCOperand::createImm(Imm)); return MCDisassembler::Success; } Index: head/contrib/llvm/lib/Target/Mips/Mips.td =================================================================== --- head/contrib/llvm/lib/Target/Mips/Mips.td (revision 354978) +++ head/contrib/llvm/lib/Target/Mips/Mips.td (revision 354979) @@ -1,259 +1,264 @@ //===-- Mips.td - Describe the Mips Target Machine ---------*- tablegen -*-===// // // 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 // //===----------------------------------------------------------------------===// // This is the top level entry point for the Mips target. //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// // Target-independent interfaces //===----------------------------------------------------------------------===// include "llvm/Target/Target.td" // The overall idea of the PredicateControl class is to chop the Predicates list // into subsets that are usually overridden independently. This allows // subclasses to partially override the predicates of their superclasses without // having to re-add all the existing predicates. class PredicateControl { // Predicates for the encoding scheme in use such as HasStdEnc list EncodingPredicates = []; // Predicates for the GPR size such as IsGP64bit list GPRPredicates = []; // Predicates for the PTR size such as IsPTR64bit list PTRPredicates = []; // Predicates for the FGR size and layout such as IsFP64bit list FGRPredicates = []; // Predicates for the instruction group membership such as ISA's. list InsnPredicates = []; // Predicate for the ASE that an instruction belongs to. list ASEPredicate = []; // Predicate for marking the instruction as usable in hard-float mode only. list HardFloatPredicate = []; // Predicates for anything else list AdditionalPredicates = []; list Predicates = !listconcat(EncodingPredicates, GPRPredicates, PTRPredicates, FGRPredicates, InsnPredicates, HardFloatPredicate, ASEPredicate, AdditionalPredicates); } // Like Requires<> but for the AdditionalPredicates list class AdditionalRequires preds> { list AdditionalPredicates = preds; } //===----------------------------------------------------------------------===// // Register File, Calling Conv, Instruction Descriptions //===----------------------------------------------------------------------===// include "MipsRegisterInfo.td" include "MipsSchedule.td" include "MipsInstrInfo.td" include "MipsCallingConv.td" include "MipsRegisterBanks.td" // Avoid forward declaration issues. include "MipsScheduleP5600.td" include "MipsScheduleGeneric.td" def MipsInstrInfo : InstrInfo; //===----------------------------------------------------------------------===// // Mips Subtarget features // //===----------------------------------------------------------------------===// def FeatureNoABICalls : SubtargetFeature<"noabicalls", "NoABICalls", "true", "Disable SVR4-style position-independent code">; def FeaturePTR64Bit : SubtargetFeature<"ptr64", "IsPTR64bit", "true", "Pointers are 64-bit wide">; def FeatureGP64Bit : SubtargetFeature<"gp64", "IsGP64bit", "true", "General Purpose Registers are 64-bit wide">; def FeatureFP64Bit : SubtargetFeature<"fp64", "IsFP64bit", "true", "Support 64-bit FP registers">; def FeatureFPXX : SubtargetFeature<"fpxx", "IsFPXX", "true", "Support for FPXX">; def FeatureNaN2008 : SubtargetFeature<"nan2008", "IsNaN2008bit", "true", "IEEE 754-2008 NaN encoding">; def FeatureAbs2008 : SubtargetFeature<"abs2008", "Abs2008", "true", "Disable IEEE 754-2008 abs.fmt mode">; def FeatureSingleFloat : SubtargetFeature<"single-float", "IsSingleFloat", "true", "Only supports single precision float">; def FeatureSoftFloat : SubtargetFeature<"soft-float", "IsSoftFloat", "true", "Does not support floating point instructions">; def FeatureNoOddSPReg : SubtargetFeature<"nooddspreg", "UseOddSPReg", "false", "Disable odd numbered single-precision " "registers">; def FeatureVFPU : SubtargetFeature<"vfpu", "HasVFPU", "true", "Enable vector FPU instructions">; def FeatureMips1 : SubtargetFeature<"mips1", "MipsArchVersion", "Mips1", "Mips I ISA Support [highly experimental]">; def FeatureMips2 : SubtargetFeature<"mips2", "MipsArchVersion", "Mips2", "Mips II ISA Support [highly experimental]", [FeatureMips1]>; def FeatureMips3_32 : SubtargetFeature<"mips3_32", "HasMips3_32", "true", "Subset of MIPS-III that is also in MIPS32 " "[highly experimental]">; def FeatureMips3_32r2 : SubtargetFeature<"mips3_32r2", "HasMips3_32r2", "true", "Subset of MIPS-III that is also in MIPS32r2 " "[highly experimental]">; def FeatureMips3 : SubtargetFeature<"mips3", "MipsArchVersion", "Mips3", "MIPS III ISA Support [highly experimental]", [FeatureMips2, FeatureMips3_32, FeatureMips3_32r2, FeatureGP64Bit, FeatureFP64Bit]>; def FeatureMips4_32 : SubtargetFeature<"mips4_32", "HasMips4_32", "true", "Subset of MIPS-IV that is also in MIPS32 " "[highly experimental]">; def FeatureMips4_32r2 : SubtargetFeature<"mips4_32r2", "HasMips4_32r2", "true", "Subset of MIPS-IV that is also in MIPS32r2 " "[highly experimental]">; def FeatureMips4 : SubtargetFeature<"mips4", "MipsArchVersion", "Mips4", "MIPS IV ISA Support", [FeatureMips3, FeatureMips4_32, FeatureMips4_32r2]>; def FeatureMips5_32r2 : SubtargetFeature<"mips5_32r2", "HasMips5_32r2", "true", "Subset of MIPS-V that is also in MIPS32r2 " "[highly experimental]">; def FeatureMips5 : SubtargetFeature<"mips5", "MipsArchVersion", "Mips5", "MIPS V ISA Support [highly experimental]", [FeatureMips4, FeatureMips5_32r2]>; def FeatureMips32 : SubtargetFeature<"mips32", "MipsArchVersion", "Mips32", "Mips32 ISA Support", [FeatureMips2, FeatureMips3_32, FeatureMips4_32]>; def FeatureMips32r2 : SubtargetFeature<"mips32r2", "MipsArchVersion", "Mips32r2", "Mips32r2 ISA Support", [FeatureMips3_32r2, FeatureMips4_32r2, FeatureMips5_32r2, FeatureMips32]>; def FeatureMips32r3 : SubtargetFeature<"mips32r3", "MipsArchVersion", "Mips32r3", "Mips32r3 ISA Support", [FeatureMips32r2]>; def FeatureMips32r5 : SubtargetFeature<"mips32r5", "MipsArchVersion", "Mips32r5", "Mips32r5 ISA Support", [FeatureMips32r3]>; def FeatureMips32r6 : SubtargetFeature<"mips32r6", "MipsArchVersion", "Mips32r6", "Mips32r6 ISA Support [experimental]", [FeatureMips32r5, FeatureFP64Bit, FeatureNaN2008, FeatureAbs2008]>; def FeatureMips64 : SubtargetFeature<"mips64", "MipsArchVersion", "Mips64", "Mips64 ISA Support", [FeatureMips5, FeatureMips32]>; def FeatureMips64r2 : SubtargetFeature<"mips64r2", "MipsArchVersion", "Mips64r2", "Mips64r2 ISA Support", [FeatureMips64, FeatureMips32r2]>; def FeatureMips64r3 : SubtargetFeature<"mips64r3", "MipsArchVersion", "Mips64r3", "Mips64r3 ISA Support", [FeatureMips64r2, FeatureMips32r3]>; def FeatureMips64r5 : SubtargetFeature<"mips64r5", "MipsArchVersion", "Mips64r5", "Mips64r5 ISA Support", [FeatureMips64r3, FeatureMips32r5]>; def FeatureMips64r6 : SubtargetFeature<"mips64r6", "MipsArchVersion", "Mips64r6", "Mips64r6 ISA Support [experimental]", [FeatureMips32r6, FeatureMips64r5, FeatureNaN2008, FeatureAbs2008]>; def FeatureSym32 : SubtargetFeature<"sym32", "HasSym32", "true", "Symbols are 32 bit on Mips64">; def FeatureMips16 : SubtargetFeature<"mips16", "InMips16Mode", "true", "Mips16 mode">; def FeatureDSP : SubtargetFeature<"dsp", "HasDSP", "true", "Mips DSP ASE">; def FeatureDSPR2 : SubtargetFeature<"dspr2", "HasDSPR2", "true", "Mips DSP-R2 ASE", [FeatureDSP]>; def FeatureDSPR3 : SubtargetFeature<"dspr3", "HasDSPR3", "true", "Mips DSP-R3 ASE", [ FeatureDSP, FeatureDSPR2 ]>; def FeatureMSA : SubtargetFeature<"msa", "HasMSA", "true", "Mips MSA ASE">; def FeatureEVA : SubtargetFeature<"eva", "HasEVA", "true", "Mips EVA ASE">; def FeatureCRC : SubtargetFeature<"crc", "HasCRC", "true", "Mips R6 CRC ASE">; def FeatureVirt : SubtargetFeature<"virt", "HasVirt", "true", "Mips Virtualization ASE">; def FeatureGINV : SubtargetFeature<"ginv", "HasGINV", "true", "Mips Global Invalidate ASE">; def FeatureMicroMips : SubtargetFeature<"micromips", "InMicroMipsMode", "true", "microMips mode">; def FeatureCnMips : SubtargetFeature<"cnmips", "HasCnMips", "true", "Octeon cnMIPS Support", [FeatureMips64r2]>; +def FeatureCnMipsP : SubtargetFeature<"cnmipsp", "HasCnMipsP", + "true", "Octeon+ cnMIPS Support", + [FeatureCnMips]>; + def FeatureUseTCCInDIV : SubtargetFeature< "use-tcc-in-div", "UseTCCInDIV", "false", "Force the assembler to use trapping">; def FeatureMadd4 : SubtargetFeature<"nomadd4", "DisableMadd4", "true", "Disable 4-operand madd.fmt and related instructions">; def FeatureMT : SubtargetFeature<"mt", "HasMT", "true", "Mips MT ASE">; def FeatureLongCalls : SubtargetFeature<"long-calls", "UseLongCalls", "true", "Disable use of the jal instruction">; def FeatureUseIndirectJumpsHazard : SubtargetFeature<"use-indirect-jump-hazard", "UseIndirectJumpsHazard", "true", "Use indirect jump" " guards to prevent certain speculation based attacks">; //===----------------------------------------------------------------------===// // Mips processors supported. //===----------------------------------------------------------------------===// def ImplP5600 : SubtargetFeature<"p5600", "ProcImpl", "MipsSubtarget::CPU::P5600", "The P5600 Processor", [FeatureMips32r5]>; class Proc Features> : ProcessorModel; def : Proc<"mips1", [FeatureMips1]>; def : Proc<"mips2", [FeatureMips2]>; def : Proc<"mips32", [FeatureMips32]>; def : Proc<"mips32r2", [FeatureMips32r2]>; def : Proc<"mips32r3", [FeatureMips32r3]>; def : Proc<"mips32r5", [FeatureMips32r5]>; def : Proc<"mips32r6", [FeatureMips32r6]>; def : Proc<"mips3", [FeatureMips3]>; def : Proc<"mips4", [FeatureMips4]>; def : Proc<"mips5", [FeatureMips5]>; def : Proc<"mips64", [FeatureMips64]>; def : Proc<"mips64r2", [FeatureMips64r2]>; def : Proc<"mips64r3", [FeatureMips64r3]>; def : Proc<"mips64r5", [FeatureMips64r5]>; def : Proc<"mips64r6", [FeatureMips64r6]>; def : Proc<"octeon", [FeatureMips64r2, FeatureCnMips]>; +def : Proc<"octeon+", [FeatureMips64r2, FeatureCnMips, FeatureCnMipsP]>; def : ProcessorModel<"p5600", MipsP5600Model, [ImplP5600]>; def MipsAsmParser : AsmParser { let ShouldEmitMatchRegisterName = 0; } def MipsAsmParserVariant : AsmParserVariant { int Variant = 0; // Recognize hard coded registers. string RegisterPrefix = "$"; } def Mips : Target { let InstructionSet = MipsInstrInfo; let AssemblyParsers = [MipsAsmParser]; let AssemblyParserVariants = [MipsAsmParserVariant]; let AllowRegisterRenaming = 1; } Index: head/contrib/llvm/lib/Target/Mips/Mips64InstrInfo.td =================================================================== --- head/contrib/llvm/lib/Target/Mips/Mips64InstrInfo.td (revision 354978) +++ head/contrib/llvm/lib/Target/Mips/Mips64InstrInfo.td (revision 354979) @@ -1,1187 +1,1205 @@ //===- Mips64InstrInfo.td - Mips64 Instruction Information -*- tablegen -*-===// // // 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 // //===----------------------------------------------------------------------===// // // This file describes Mips64 instructions. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// // Mips Operand, Complex Patterns and Transformations Definitions. //===----------------------------------------------------------------------===// // shamt must fit in 6 bits. def immZExt6 : ImmLeaf; // Node immediate fits as 10-bit sign extended on target immediate. // e.g. seqi, snei def immSExt10_64 : PatLeaf<(i64 imm), [{ return isInt<10>(N->getSExtValue()); }]>; def immZExt16_64 : PatLeaf<(i64 imm), [{ return isUInt<16>(N->getZExtValue()); }]>; def immZExt5_64 : ImmLeaf; // Transformation function: get log2 of low 32 bits of immediate def Log2LO : SDNodeXFormgetZExtValue())); }]>; // Transformation function: get log2 of high 32 bits of immediate def Log2HI : SDNodeXFormgetZExtValue() >> 32))); }]>; // Predicate: True if immediate is a power of 2 and fits 32 bits def PowerOf2LO : PatLeaf<(imm), [{ if (N->getValueType(0) == MVT::i64) { uint64_t Imm = N->getZExtValue(); return isPowerOf2_64(Imm) && (Imm & 0xffffffff) == Imm; } else return false; }]>; // Predicate: True if immediate is a power of 2 and exceeds 32 bits def PowerOf2HI : PatLeaf<(imm), [{ if (N->getValueType(0) == MVT::i64) { uint64_t Imm = N->getZExtValue(); return isPowerOf2_64(Imm) && (Imm & 0xffffffff00000000) == Imm; } else return false; }]>; def PowerOf2LO_i32 : PatLeaf<(imm), [{ if (N->getValueType(0) == MVT::i32) { uint64_t Imm = N->getZExtValue(); return isPowerOf2_32(Imm) && isUInt<32>(Imm); } else return false; }]>; def assertzext_lt_i32 : PatFrag<(ops node:$src), (assertzext node:$src), [{ return cast(N->getOperand(1))->getVT().bitsLT(MVT::i32); }]>; //===----------------------------------------------------------------------===// // Instructions specific format //===----------------------------------------------------------------------===// let usesCustomInserter = 1 in { def ATOMIC_LOAD_ADD_I64 : Atomic2Ops; def ATOMIC_LOAD_SUB_I64 : Atomic2Ops; def ATOMIC_LOAD_AND_I64 : Atomic2Ops; def ATOMIC_LOAD_OR_I64 : Atomic2Ops; def ATOMIC_LOAD_XOR_I64 : Atomic2Ops; def ATOMIC_LOAD_NAND_I64 : Atomic2Ops; def ATOMIC_SWAP_I64 : Atomic2Ops; def ATOMIC_CMP_SWAP_I64 : AtomicCmpSwap; } def ATOMIC_LOAD_ADD_I64_POSTRA : Atomic2OpsPostRA; def ATOMIC_LOAD_SUB_I64_POSTRA : Atomic2OpsPostRA; def ATOMIC_LOAD_AND_I64_POSTRA : Atomic2OpsPostRA; def ATOMIC_LOAD_OR_I64_POSTRA : Atomic2OpsPostRA; def ATOMIC_LOAD_XOR_I64_POSTRA : Atomic2OpsPostRA; def ATOMIC_LOAD_NAND_I64_POSTRA : Atomic2OpsPostRA; def ATOMIC_SWAP_I64_POSTRA : Atomic2OpsPostRA; def ATOMIC_CMP_SWAP_I64_POSTRA : AtomicCmpSwapPostRA; /// Pseudo instructions for loading and storing accumulator registers. let isPseudo = 1, isCodeGenOnly = 1, hasNoSchedulingInfo = 1 in { def LOAD_ACC128 : Load<"", ACC128>; def STORE_ACC128 : Store<"", ACC128>; } //===----------------------------------------------------------------------===// // Instruction definition //===----------------------------------------------------------------------===// let DecoderNamespace = "Mips64" in { /// Arithmetic Instructions (ALU Immediate) def DADDi : ArithLogicI<"daddi", simm16_64, GPR64Opnd, II_DADDI>, ADDI_FM<0x18>, ISA_MIPS3_NOT_32R6_64R6; let AdditionalPredicates = [NotInMicroMips] in { def DADDiu : ArithLogicI<"daddiu", simm16_64, GPR64Opnd, II_DADDIU, immSExt16, add>, ADDI_FM<0x19>, IsAsCheapAsAMove, ISA_MIPS3; } let isCodeGenOnly = 1 in { def SLTi64 : SetCC_I<"slti", setlt, simm16_64, immSExt16, GPR64Opnd>, SLTI_FM<0xa>, GPR_64; def SLTiu64 : SetCC_I<"sltiu", setult, simm16_64, immSExt16, GPR64Opnd>, SLTI_FM<0xb>, GPR_64; def ANDi64 : ArithLogicI<"andi", uimm16_64, GPR64Opnd, II_AND, immZExt16, and>, ADDI_FM<0xc>, GPR_64; def ORi64 : ArithLogicI<"ori", uimm16_64, GPR64Opnd, II_OR, immZExt16, or>, ADDI_FM<0xd>, GPR_64; def XORi64 : ArithLogicI<"xori", uimm16_64, GPR64Opnd, II_XOR, immZExt16, xor>, ADDI_FM<0xe>, GPR_64; def LUi64 : LoadUpper<"lui", GPR64Opnd, uimm16_64_relaxed>, LUI_FM, GPR_64; } /// Arithmetic Instructions (3-Operand, R-Type) let AdditionalPredicates = [NotInMicroMips] in { def DADD : ArithLogicR<"dadd", GPR64Opnd, 1, II_DADD>, ADD_FM<0, 0x2c>, ISA_MIPS3; def DADDu : ArithLogicR<"daddu", GPR64Opnd, 1, II_DADDU, add>, ADD_FM<0, 0x2d>, ISA_MIPS3; def DSUBu : ArithLogicR<"dsubu", GPR64Opnd, 0, II_DSUBU, sub>, ADD_FM<0, 0x2f>, ISA_MIPS3; def DSUB : ArithLogicR<"dsub", GPR64Opnd, 0, II_DSUB>, ADD_FM<0, 0x2e>, ISA_MIPS3; } let isCodeGenOnly = 1 in { def SLT64 : SetCC_R<"slt", setlt, GPR64Opnd>, ADD_FM<0, 0x2a>, GPR_64; def SLTu64 : SetCC_R<"sltu", setult, GPR64Opnd>, ADD_FM<0, 0x2b>, GPR_64; def AND64 : ArithLogicR<"and", GPR64Opnd, 1, II_AND, and>, ADD_FM<0, 0x24>, GPR_64; def OR64 : ArithLogicR<"or", GPR64Opnd, 1, II_OR, or>, ADD_FM<0, 0x25>, GPR_64; def XOR64 : ArithLogicR<"xor", GPR64Opnd, 1, II_XOR, xor>, ADD_FM<0, 0x26>, GPR_64; def NOR64 : LogicNOR<"nor", GPR64Opnd>, ADD_FM<0, 0x27>, GPR_64; } /// Shift Instructions let AdditionalPredicates = [NotInMicroMips] in { def DSLL : shift_rotate_imm<"dsll", uimm6, GPR64Opnd, II_DSLL, shl, immZExt6>, SRA_FM<0x38, 0>, ISA_MIPS3; def DSRL : shift_rotate_imm<"dsrl", uimm6, GPR64Opnd, II_DSRL, srl, immZExt6>, SRA_FM<0x3a, 0>, ISA_MIPS3; def DSRA : shift_rotate_imm<"dsra", uimm6, GPR64Opnd, II_DSRA, sra, immZExt6>, SRA_FM<0x3b, 0>, ISA_MIPS3; def DSLLV : shift_rotate_reg<"dsllv", GPR64Opnd, II_DSLLV, shl>, SRLV_FM<0x14, 0>, ISA_MIPS3; def DSRAV : shift_rotate_reg<"dsrav", GPR64Opnd, II_DSRAV, sra>, SRLV_FM<0x17, 0>, ISA_MIPS3; def DSRLV : shift_rotate_reg<"dsrlv", GPR64Opnd, II_DSRLV, srl>, SRLV_FM<0x16, 0>, ISA_MIPS3; def DSLL32 : shift_rotate_imm<"dsll32", uimm5, GPR64Opnd, II_DSLL32>, SRA_FM<0x3c, 0>, ISA_MIPS3; def DSRL32 : shift_rotate_imm<"dsrl32", uimm5, GPR64Opnd, II_DSRL32>, SRA_FM<0x3e, 0>, ISA_MIPS3; def DSRA32 : shift_rotate_imm<"dsra32", uimm5, GPR64Opnd, II_DSRA32>, SRA_FM<0x3f, 0>, ISA_MIPS3; // Rotate Instructions def DROTR : shift_rotate_imm<"drotr", uimm6, GPR64Opnd, II_DROTR, rotr, immZExt6>, SRA_FM<0x3a, 1>, ISA_MIPS64R2; def DROTRV : shift_rotate_reg<"drotrv", GPR64Opnd, II_DROTRV, rotr>, SRLV_FM<0x16, 1>, ISA_MIPS64R2; def DROTR32 : shift_rotate_imm<"drotr32", uimm5, GPR64Opnd, II_DROTR32>, SRA_FM<0x3e, 1>, ISA_MIPS64R2; } /// Load and Store Instructions /// aligned let isCodeGenOnly = 1 in { def LB64 : Load<"lb", GPR64Opnd, sextloadi8, II_LB>, LW_FM<0x20>, GPR_64; def LBu64 : Load<"lbu", GPR64Opnd, zextloadi8, II_LBU>, LW_FM<0x24>, GPR_64; def LH64 : Load<"lh", GPR64Opnd, sextloadi16, II_LH>, LW_FM<0x21>, GPR_64; def LHu64 : Load<"lhu", GPR64Opnd, zextloadi16, II_LHU>, LW_FM<0x25>, GPR_64; def LW64 : Load<"lw", GPR64Opnd, sextloadi32, II_LW>, LW_FM<0x23>, GPR_64; def SB64 : Store<"sb", GPR64Opnd, truncstorei8, II_SB>, LW_FM<0x28>, GPR_64; def SH64 : Store<"sh", GPR64Opnd, truncstorei16, II_SH>, LW_FM<0x29>, GPR_64; def SW64 : Store<"sw", GPR64Opnd, truncstorei32, II_SW>, LW_FM<0x2b>, GPR_64; } let AdditionalPredicates = [NotInMicroMips] in { def LWu : MMRel, Load<"lwu", GPR64Opnd, zextloadi32, II_LWU>, LW_FM<0x27>, ISA_MIPS3; def LD : LoadMemory<"ld", GPR64Opnd, mem_simmptr, load, II_LD>, LW_FM<0x37>, ISA_MIPS3; def SD : StoreMemory<"sd", GPR64Opnd, mem_simmptr, store, II_SD>, LW_FM<0x3f>, ISA_MIPS3; } /// load/store left/right let isCodeGenOnly = 1 in { def LWL64 : LoadLeftRight<"lwl", MipsLWL, GPR64Opnd, II_LWL>, LW_FM<0x22>, GPR_64; def LWR64 : LoadLeftRight<"lwr", MipsLWR, GPR64Opnd, II_LWR>, LW_FM<0x26>, GPR_64; def SWL64 : StoreLeftRight<"swl", MipsSWL, GPR64Opnd, II_SWL>, LW_FM<0x2a>, GPR_64; def SWR64 : StoreLeftRight<"swr", MipsSWR, GPR64Opnd, II_SWR>, LW_FM<0x2e>, GPR_64; } def LDL : LoadLeftRight<"ldl", MipsLDL, GPR64Opnd, II_LDL>, LW_FM<0x1a>, ISA_MIPS3_NOT_32R6_64R6; def LDR : LoadLeftRight<"ldr", MipsLDR, GPR64Opnd, II_LDR>, LW_FM<0x1b>, ISA_MIPS3_NOT_32R6_64R6; def SDL : StoreLeftRight<"sdl", MipsSDL, GPR64Opnd, II_SDL>, LW_FM<0x2c>, ISA_MIPS3_NOT_32R6_64R6; def SDR : StoreLeftRight<"sdr", MipsSDR, GPR64Opnd, II_SDR>, LW_FM<0x2d>, ISA_MIPS3_NOT_32R6_64R6; /// Load-linked, Store-conditional let AdditionalPredicates = [NotInMicroMips] in { def LLD : LLBase<"lld", GPR64Opnd, mem_simmptr>, LW_FM<0x34>, ISA_MIPS3_NOT_32R6_64R6; } def SCD : SCBase<"scd", GPR64Opnd>, LW_FM<0x3c>, ISA_MIPS3_NOT_32R6_64R6; let AdditionalPredicates = [NotInMicroMips], DecoderNamespace = "Mips32_64_PTR64" in { def LL64 : LLBase<"ll", GPR32Opnd>, LW_FM<0x30>, PTR_64, ISA_MIPS2_NOT_32R6_64R6; def SC64 : SCBase<"sc", GPR32Opnd>, LW_FM<0x38>, PTR_64, ISA_MIPS2_NOT_32R6_64R6; def JR64 : IndirectBranch<"jr", GPR64Opnd>, MTLO_FM<8>, PTR_64; } def JALR64 : JumpLinkReg<"jalr", GPR64Opnd>, JALR_FM, PTR_64; /// Jump and Branch Instructions let isCodeGenOnly = 1 in { def BEQ64 : CBranch<"beq", brtarget, seteq, GPR64Opnd>, BEQ_FM<4>, GPR_64; def BNE64 : CBranch<"bne", brtarget, setne, GPR64Opnd>, BEQ_FM<5>, GPR_64; def BGEZ64 : CBranchZero<"bgez", brtarget, setge, GPR64Opnd>, BGEZ_FM<1, 1>, GPR_64; def BGTZ64 : CBranchZero<"bgtz", brtarget, setgt, GPR64Opnd>, BGEZ_FM<7, 0>, GPR_64; def BLEZ64 : CBranchZero<"blez", brtarget, setle, GPR64Opnd>, BGEZ_FM<6, 0>, GPR_64; def BLTZ64 : CBranchZero<"bltz", brtarget, setlt, GPR64Opnd>, BGEZ_FM<1, 0>, GPR_64; let AdditionalPredicates = [NoIndirectJumpGuards] in def JALR64Pseudo : JumpLinkRegPseudo, PTR_64; } let AdditionalPredicates = [NotInMicroMips], DecoderNamespace = "Mips64" in { def JR_HB64 : JR_HB_DESC, JR_HB_ENC, ISA_MIPS64_NOT_64R6; def JALR_HB64 : JALR_HB_DESC, JALR_HB_ENC, ISA_MIPS64R2; } def PseudoReturn64 : PseudoReturnBase, GPR_64; let AdditionalPredicates = [NotInMips16Mode, NotInMicroMips, NoIndirectJumpGuards] in { def TAILCALLREG64 : TailCallReg, ISA_MIPS3_NOT_32R6_64R6, PTR_64; def PseudoIndirectBranch64 : PseudoIndirectBranchBase, ISA_MIPS3_NOT_32R6_64R6; } let AdditionalPredicates = [NotInMips16Mode, NotInMicroMips, UseIndirectJumpsHazard] in { def TAILCALLREGHB64 : TailCallReg, ISA_MIPS32R2_NOT_32R6_64R6, PTR_64; def PseudoIndirectHazardBranch64 : PseudoIndirectBranchBase, ISA_MIPS32R2_NOT_32R6_64R6, PTR_64; } /// Multiply and Divide Instructions. let AdditionalPredicates = [NotInMicroMips] in { def DMULT : Mult<"dmult", II_DMULT, GPR64Opnd, [HI0_64, LO0_64]>, MULT_FM<0, 0x1c>, ISA_MIPS3_NOT_32R6_64R6; def DMULTu : Mult<"dmultu", II_DMULTU, GPR64Opnd, [HI0_64, LO0_64]>, MULT_FM<0, 0x1d>, ISA_MIPS3_NOT_32R6_64R6; } def PseudoDMULT : MultDivPseudo, ISA_MIPS3_NOT_32R6_64R6; def PseudoDMULTu : MultDivPseudo, ISA_MIPS3_NOT_32R6_64R6; let AdditionalPredicates = [NotInMicroMips] in { def DSDIV : Div<"ddiv", II_DDIV, GPR64Opnd, [HI0_64, LO0_64]>, MULT_FM<0, 0x1e>, ISA_MIPS3_NOT_32R6_64R6; def DUDIV : Div<"ddivu", II_DDIVU, GPR64Opnd, [HI0_64, LO0_64]>, MULT_FM<0, 0x1f>, ISA_MIPS3_NOT_32R6_64R6; } def PseudoDSDIV : MultDivPseudo, ISA_MIPS3_NOT_32R6_64R6; def PseudoDUDIV : MultDivPseudo, ISA_MIPS3_NOT_32R6_64R6; let isCodeGenOnly = 1 in { def MTHI64 : MoveToLOHI<"mthi", GPR64Opnd, [HI0_64]>, MTLO_FM<0x11>, ISA_MIPS3_NOT_32R6_64R6; def MTLO64 : MoveToLOHI<"mtlo", GPR64Opnd, [LO0_64]>, MTLO_FM<0x13>, ISA_MIPS3_NOT_32R6_64R6; def MFHI64 : MoveFromLOHI<"mfhi", GPR64Opnd, AC0_64>, MFLO_FM<0x10>, ISA_MIPS3_NOT_32R6_64R6; def MFLO64 : MoveFromLOHI<"mflo", GPR64Opnd, AC0_64>, MFLO_FM<0x12>, ISA_MIPS3_NOT_32R6_64R6; def PseudoMFHI64 : PseudoMFLOHI, ISA_MIPS3_NOT_32R6_64R6; def PseudoMFLO64 : PseudoMFLOHI, ISA_MIPS3_NOT_32R6_64R6; def PseudoMTLOHI64 : PseudoMTLOHI, ISA_MIPS3_NOT_32R6_64R6; /// Sign Ext In Register Instructions. def SEB64 : SignExtInReg<"seb", i8, GPR64Opnd, II_SEB>, SEB_FM<0x10, 0x20>, ISA_MIPS32R2, GPR_64; def SEH64 : SignExtInReg<"seh", i16, GPR64Opnd, II_SEH>, SEB_FM<0x18, 0x20>, ISA_MIPS32R2, GPR_64; } /// Count Leading let AdditionalPredicates = [NotInMicroMips] in { def DCLZ : CountLeading0<"dclz", GPR64Opnd, II_DCLZ>, CLO_FM<0x24>, ISA_MIPS64_NOT_64R6, GPR_64; def DCLO : CountLeading1<"dclo", GPR64Opnd, II_DCLO>, CLO_FM<0x25>, ISA_MIPS64_NOT_64R6, GPR_64; /// Double Word Swap Bytes/HalfWords def DSBH : SubwordSwap<"dsbh", GPR64Opnd, II_DSBH>, SEB_FM<2, 0x24>, ISA_MIPS64R2; def DSHD : SubwordSwap<"dshd", GPR64Opnd, II_DSHD>, SEB_FM<5, 0x24>, ISA_MIPS64R2; def LEA_ADDiu64 : EffectiveAddress<"daddiu", GPR64Opnd>, LW_FM<0x19>, GPR_64; } let isCodeGenOnly = 1 in def RDHWR64 : ReadHardware, RDHWR_FM, GPR_64; let AdditionalPredicates = [NotInMicroMips] in { // The 'pos + size' constraints for code generation are enforced by the // code that lowers into MipsISD::Ext. // For assembly parsing, we alias dextu and dextm to dext, and match by // operand were possible then check the 'pos + size' in MipsAsmParser. // We override the generated decoder to enforce that dext always comes out // for dextm and dextu like binutils. let DecoderMethod = "DecodeDEXT" in { def DEXT : ExtBase<"dext", GPR64Opnd, uimm5_report_uimm6, uimm5_plus1_report_uimm6, immZExt5, immZExt5Plus1, MipsExt>, EXT_FM<3>, ISA_MIPS64R2; def DEXTM : ExtBase<"dextm", GPR64Opnd, uimm5, uimm5_plus33, immZExt5, immZExt5Plus33, MipsExt>, EXT_FM<1>, ISA_MIPS64R2; def DEXTU : ExtBase<"dextu", GPR64Opnd, uimm5_plus32, uimm5_plus1, immZExt5Plus32, immZExt5Plus1, MipsExt>, EXT_FM<2>, ISA_MIPS64R2; } // The 'pos + size' constraints for code generation are enforced by the // code that lowers into MipsISD::Ins. // For assembly parsing, we alias dinsu and dinsm to dins, and match by // operand were possible then check the 'pos + size' in MipsAsmParser. // We override the generated decoder to enforce that dins always comes out // for dinsm and dinsu like binutils. let DecoderMethod = "DecodeDINS" in { def DINS : InsBase<"dins", GPR64Opnd, uimm6, uimm5_inssize_plus1, immZExt5, immZExt5Plus1>, EXT_FM<7>, ISA_MIPS64R2; def DINSU : InsBase<"dinsu", GPR64Opnd, uimm5_plus32, uimm5_inssize_plus1, immZExt5Plus32, immZExt5Plus1>, EXT_FM<6>, ISA_MIPS64R2; def DINSM : InsBase<"dinsm", GPR64Opnd, uimm5, uimm_range_2_64, immZExt5, immZExtRange2To64>, EXT_FM<5>, ISA_MIPS64R2; } } let isCodeGenOnly = 1, AdditionalPredicates = [NotInMicroMips] in { def DEXT64_32 : InstSE<(outs GPR64Opnd:$rt), (ins GPR32Opnd:$rs, uimm5_report_uimm6:$pos, uimm5_plus1:$size), "dext $rt, $rs, $pos, $size", [], II_EXT, FrmR, "dext">, EXT_FM<3>, ISA_MIPS64R2; } let isCodeGenOnly = 1, rs = 0, shamt = 0 in { def DSLL64_32 : FR<0x00, 0x3c, (outs GPR64:$rd), (ins GPR32:$rt), "dsll\t$rd, $rt, 32", [], II_DSLL>, GPR_64; let isMoveReg = 1 in { def SLL64_32 : FR<0x0, 0x00, (outs GPR64:$rd), (ins GPR32:$rt), "sll\t$rd, $rt, 0", [], II_SLL>, GPR_64; def SLL64_64 : FR<0x0, 0x00, (outs GPR64:$rd), (ins GPR64:$rt), "sll\t$rd, $rt, 0", [], II_SLL>, GPR_64; } } // We need the following pseudo instruction to avoid offset calculation for // long branches. See the comment in file MipsLongBranch.cpp for detailed // explanation. // Expands to: lui $dst, %highest/%higher/%hi/%lo($tgt) def LONG_BRANCH_LUi2Op_64 : PseudoSE<(outs GPR64Opnd:$dst), (ins brtarget:$tgt), []>, GPR_64 { bit hasNoSchedulingInfo = 1; } // Expands to: addiu $dst, %highest/%higher/%hi/%lo($tgt) def LONG_BRANCH_DADDiu2Op : PseudoSE<(outs GPR64Opnd:$dst), (ins GPR64Opnd:$src, brtarget:$tgt), []>, GPR_64 { bit hasNoSchedulingInfo = 1; } // Expands to: daddiu $dst, $src, %PART($tgt - $baltgt) // where %PART may be %hi or %lo, depending on the relocation kind // that $tgt is annotated with. def LONG_BRANCH_DADDiu : PseudoSE<(outs GPR64Opnd:$dst), (ins GPR64Opnd:$src, brtarget:$tgt, brtarget:$baltgt), []>, GPR_64 { bit hasNoSchedulingInfo = 1; } // Cavium Octeon cnMIPS instructions let DecoderNamespace = "CnMips", // FIXME: The lack of HasStdEnc is probably a bug EncodingPredicates = [] in { class Count1s: InstSE<(outs RO:$rd), (ins RO:$rs), !strconcat(opstr, "\t$rd, $rs"), [(set RO:$rd, (ctpop RO:$rs))], II_POP, FrmR, opstr> { let TwoOperandAliasConstraint = "$rd = $rs"; } class ExtsCins: InstSE<(outs RO:$rt), (ins RO:$rs, uimm5:$pos, uimm5:$lenm1), !strconcat(opstr, "\t$rt, $rs, $pos, $lenm1"), [(set RO:$rt, (Op RO:$rs, PosImm:$pos, imm:$lenm1))], itin, FrmR, opstr> { let TwoOperandAliasConstraint = "$rt = $rs"; } class SetCC64_R : InstSE<(outs GPR64Opnd:$rd), (ins GPR64Opnd:$rs, GPR64Opnd:$rt), !strconcat(opstr, "\t$rd, $rs, $rt"), [(set GPR64Opnd:$rd, (zext (cond_op GPR64Opnd:$rs, GPR64Opnd:$rt)))], II_SEQ_SNE, FrmR, opstr> { let TwoOperandAliasConstraint = "$rd = $rs"; } class SetCC64_I: InstSE<(outs GPR64Opnd:$rt), (ins GPR64Opnd:$rs, simm10_64:$imm10), !strconcat(opstr, "\t$rt, $rs, $imm10"), [(set GPR64Opnd:$rt, (zext (cond_op GPR64Opnd:$rs, immSExt10_64:$imm10)))], II_SEQI_SNEI, FrmI, opstr> { let TwoOperandAliasConstraint = "$rt = $rs"; } class CBranchBitNum shift = 1> : InstSE<(outs), (ins RO:$rs, ImmOp:$p, opnd:$offset), !strconcat(opstr, "\t$rs, $p, $offset"), [(brcond (i32 (cond_op (and RO:$rs, (shl shift, immZExt5_64:$p)), 0)), bb:$offset)], II_BBIT, FrmI, opstr> { let isBranch = 1; let isTerminator = 1; let hasDelaySlot = 1; let Defs = [AT]; } class MFC2OP : InstSE<(outs RO:$rt, uimm16:$imm16), (ins), !strconcat(asmstr, "\t$rt, $imm16"), [], itin, FrmFR>; // Unsigned Byte Add def BADDu : ArithLogicR<"baddu", GPR64Opnd, 1, II_BADDU>, ADD_FM<0x1c, 0x28>, ASE_CNMIPS { let Pattern = [(set GPR64Opnd:$rd, (and (add GPR64Opnd:$rs, GPR64Opnd:$rt), 255))]; } // Branch on Bit Clear /+32 def BBIT0 : CBranchBitNum<"bbit0", brtarget, seteq, GPR64Opnd, uimm5_64_report_uimm6>, BBIT_FM<0x32>, ASE_CNMIPS; def BBIT032: CBranchBitNum<"bbit032", brtarget, seteq, GPR64Opnd, uimm5_64, 0x100000000>, BBIT_FM<0x36>, ASE_CNMIPS; // Branch on Bit Set /+32 def BBIT1 : CBranchBitNum<"bbit1", brtarget, setne, GPR64Opnd, uimm5_64_report_uimm6>, BBIT_FM<0x3a>, ASE_CNMIPS; def BBIT132: CBranchBitNum<"bbit132", brtarget, setne, GPR64Opnd, uimm5_64, 0x100000000>, BBIT_FM<0x3e>, ASE_CNMIPS; // Multiply Doubleword to GPR def DMUL : ArithLogicR<"dmul", GPR64Opnd, 1, II_DMUL, mul>, ADD_FM<0x1c, 0x03>, ASE_CNMIPS { let Defs = [HI0, LO0, P0, P1, P2]; } let AdditionalPredicates = [NotInMicroMips] in { // Extract a signed bit field /+32 def EXTS : ExtsCins<"exts", II_EXT, GPR64Opnd, immZExt5>, EXTS_FM<0x3a>, ASE_MIPS64_CNMIPS; def EXTS32: ExtsCins<"exts32", II_EXT, GPR64Opnd, immZExt5Plus32>, EXTS_FM<0x3b>, ASE_MIPS64_CNMIPS; // Clear and insert a bit field /+32 def CINS : ExtsCins<"cins", II_INS, GPR64Opnd, immZExt5, MipsCIns>, EXTS_FM<0x32>, ASE_MIPS64_CNMIPS; def CINS32: ExtsCins<"cins32", II_INS, GPR64Opnd, immZExt5Plus32, MipsCIns>, EXTS_FM<0x33>, ASE_MIPS64_CNMIPS; let isCodeGenOnly = 1 in { def CINS_i32 : ExtsCins<"cins", II_INS, GPR32Opnd, immZExt5, MipsCIns>, EXTS_FM<0x32>, ASE_MIPS64_CNMIPS; def CINS64_32 :InstSE<(outs GPR64Opnd:$rt), (ins GPR32Opnd:$rs, uimm5:$pos, uimm5:$lenm1), "cins\t$rt, $rs, $pos, $lenm1", [], II_INS, FrmR, "cins">, EXTS_FM<0x32>, ASE_MIPS64_CNMIPS; } } // Move to multiplier/product register def MTM0 : MoveToLOHI<"mtm0", GPR64Opnd, [MPL0, P0, P1, P2]>, MTMR_FM<0x08>, ASE_CNMIPS; def MTM1 : MoveToLOHI<"mtm1", GPR64Opnd, [MPL1, P0, P1, P2]>, MTMR_FM<0x0c>, ASE_CNMIPS; def MTM2 : MoveToLOHI<"mtm2", GPR64Opnd, [MPL2, P0, P1, P2]>, MTMR_FM<0x0d>, ASE_CNMIPS; def MTP0 : MoveToLOHI<"mtp0", GPR64Opnd, [P0]>, MTMR_FM<0x09>, ASE_CNMIPS; def MTP1 : MoveToLOHI<"mtp1", GPR64Opnd, [P1]>, MTMR_FM<0x0a>, ASE_CNMIPS; def MTP2 : MoveToLOHI<"mtp2", GPR64Opnd, [P2]>, MTMR_FM<0x0b>, ASE_CNMIPS; // Count Ones in a Word/Doubleword def POP : Count1s<"pop", GPR32Opnd>, POP_FM<0x2c>, ASE_CNMIPS; def DPOP : Count1s<"dpop", GPR64Opnd>, POP_FM<0x2d>, ASE_CNMIPS; // Set on equal/not equal def SEQ : SetCC64_R<"seq", seteq>, SEQ_FM<0x2a>, ASE_CNMIPS; def SEQi : SetCC64_I<"seqi", seteq>, SEQI_FM<0x2e>, ASE_CNMIPS; def SNE : SetCC64_R<"sne", setne>, SEQ_FM<0x2b>, ASE_CNMIPS; def SNEi : SetCC64_I<"snei", setne>, SEQI_FM<0x2f>, ASE_CNMIPS; // 192-bit x 64-bit Unsigned Multiply and Add def V3MULU: ArithLogicR<"v3mulu", GPR64Opnd, 0, II_DMUL>, ADD_FM<0x1c, 0x11>, ASE_CNMIPS { let Defs = [P0, P1, P2]; } // 64-bit Unsigned Multiply and Add Move def VMM0 : ArithLogicR<"vmm0", GPR64Opnd, 0, II_DMUL>, ADD_FM<0x1c, 0x10>, ASE_CNMIPS { let Defs = [MPL0, P0, P1, P2]; } // 64-bit Unsigned Multiply and Add def VMULU : ArithLogicR<"vmulu", GPR64Opnd, 0, II_DMUL>, ADD_FM<0x1c, 0x0f>, ASE_CNMIPS { let Defs = [MPL1, MPL2, P0, P1, P2]; } // Move between CPU and coprocessor registers def DMFC2_OCTEON : MFC2OP<"dmfc2", GPR64Opnd, II_DMFC2>, MFC2OP_FM<0x12, 1>, ASE_CNMIPS; def DMTC2_OCTEON : MFC2OP<"dmtc2", GPR64Opnd, II_DMTC2>, MFC2OP_FM<0x12, 5>, ASE_CNMIPS; } +// Cavium Octeon+ cnMIPS instructions +let DecoderNamespace = "CnMipsP", + // FIXME: The lack of HasStdEnc is probably a bug + EncodingPredicates = [] in { + +class Saa: + InstSE<(outs), (ins GPR64Opnd:$rt, GPR64Opnd:$rs), + !strconcat(opstr, "\t$rt, (${rs})"), [], NoItinerary, FrmR, opstr>; + +def SAA : Saa<"saa">, SAA_FM<0x18>, ASE_CNMIPSP; +def SAAD : Saa<"saad">, SAA_FM<0x19>, ASE_CNMIPSP; + +def SaaAddr : MipsAsmPseudoInst<(outs), (ins GPR64Opnd:$rt, mem:$addr), + "saa\t$rt, $addr">, ASE_CNMIPSP; +def SaadAddr : MipsAsmPseudoInst<(outs), (ins GPR64Opnd:$rt, mem:$addr), + "saad\t$rt, $addr">, ASE_CNMIPSP; +} + } /// Move between CPU and coprocessor registers let DecoderNamespace = "Mips64" in { def DMFC0 : MFC3OP<"dmfc0", GPR64Opnd, COP0Opnd, II_DMFC0>, MFC3OP_FM<0x10, 1, 0>, ISA_MIPS3, GPR_64; def DMTC0 : MTC3OP<"dmtc0", COP0Opnd, GPR64Opnd, II_DMTC0>, MFC3OP_FM<0x10, 5, 0>, ISA_MIPS3, GPR_64; def DMFC2 : MFC3OP<"dmfc2", GPR64Opnd, COP2Opnd, II_DMFC2>, MFC3OP_FM<0x12, 1, 0>, ISA_MIPS3, GPR_64; def DMTC2 : MTC3OP<"dmtc2", COP2Opnd, GPR64Opnd, II_DMTC2>, MFC3OP_FM<0x12, 5, 0>, ISA_MIPS3, GPR_64; } /// Move between CPU and guest coprocessor registers (Virtualization ASE) let DecoderNamespace = "Mips64" in { def DMFGC0 : MFC3OP<"dmfgc0", GPR64Opnd, COP0Opnd, II_DMFGC0>, MFC3OP_FM<0x10, 3, 1>, ISA_MIPS64R5, ASE_VIRT; def DMTGC0 : MTC3OP<"dmtgc0", COP0Opnd, GPR64Opnd, II_DMTGC0>, MFC3OP_FM<0x10, 3, 3>, ISA_MIPS64R5, ASE_VIRT; } let AdditionalPredicates = [UseIndirectJumpsHazard] in def JALRHB64Pseudo : JumpLinkRegPseudo, PTR_64; //===----------------------------------------------------------------------===// // Arbitrary patterns that map to one or more instructions //===----------------------------------------------------------------------===// // Materialize i64 constants. defm : MaterializeImms, ISA_MIPS3, GPR_64; def : MipsPat<(i64 immZExt32Low16Zero:$imm), (DSLL (ORi64 ZERO_64, (HI16 imm:$imm)), 16)>, ISA_MIPS3, GPR_64; def : MipsPat<(i64 immZExt32:$imm), (ORi64 (DSLL (ORi64 ZERO_64, (HI16 imm:$imm)), 16), (LO16 imm:$imm))>, ISA_MIPS3, GPR_64; // extended loads def : MipsPat<(i64 (extloadi1 addr:$src)), (LB64 addr:$src)>, ISA_MIPS3, GPR_64; def : MipsPat<(i64 (extloadi8 addr:$src)), (LB64 addr:$src)>, ISA_MIPS3, GPR_64; def : MipsPat<(i64 (extloadi16 addr:$src)), (LH64 addr:$src)>, ISA_MIPS3, GPR_64; def : MipsPat<(i64 (extloadi32 addr:$src)), (LW64 addr:$src)>, ISA_MIPS3, GPR_64; // hi/lo relocs let AdditionalPredicates = [NotInMicroMips] in defm : MipsHiLoRelocs, ISA_MIPS3, GPR_64, SYM_32; def : MipsPat<(MipsGotHi tglobaladdr:$in), (LUi64 tglobaladdr:$in)>, ISA_MIPS3, GPR_64; def : MipsPat<(MipsGotHi texternalsym:$in), (LUi64 texternalsym:$in)>, ISA_MIPS3, GPR_64; def : MipsPat<(MipsTlsHi tglobaltlsaddr:$in), (LUi64 tglobaltlsaddr:$in)>, ISA_MIPS3, GPR_64; // highest/higher/hi/lo relocs let AdditionalPredicates = [NotInMicroMips] in { def : MipsPat<(MipsJmpLink (i64 texternalsym:$dst)), (JAL texternalsym:$dst)>, ISA_MIPS3, GPR_64, SYM_64; def : MipsPat<(MipsHighest (i64 tglobaladdr:$in)), (LUi64 tglobaladdr:$in)>, ISA_MIPS3, GPR_64, SYM_64; def : MipsPat<(MipsHighest (i64 tblockaddress:$in)), (LUi64 tblockaddress:$in)>, ISA_MIPS3, GPR_64, SYM_64; def : MipsPat<(MipsHighest (i64 tjumptable:$in)), (LUi64 tjumptable:$in)>, ISA_MIPS3, GPR_64, SYM_64; def : MipsPat<(MipsHighest (i64 tconstpool:$in)), (LUi64 tconstpool:$in)>, ISA_MIPS3, GPR_64, SYM_64; def : MipsPat<(MipsHighest (i64 texternalsym:$in)), (LUi64 texternalsym:$in)>, ISA_MIPS3, GPR_64, SYM_64; def : MipsPat<(MipsHigher (i64 tglobaladdr:$in)), (DADDiu ZERO_64, tglobaladdr:$in)>, ISA_MIPS3, GPR_64, SYM_64; def : MipsPat<(MipsHigher (i64 tblockaddress:$in)), (DADDiu ZERO_64, tblockaddress:$in)>, ISA_MIPS3, GPR_64, SYM_64; def : MipsPat<(MipsHigher (i64 tjumptable:$in)), (DADDiu ZERO_64, tjumptable:$in)>, ISA_MIPS3, GPR_64, SYM_64; def : MipsPat<(MipsHigher (i64 tconstpool:$in)), (DADDiu ZERO_64, tconstpool:$in)>, ISA_MIPS3, GPR_64, SYM_64; def : MipsPat<(MipsHigher (i64 texternalsym:$in)), (DADDiu ZERO_64, texternalsym:$in)>, ISA_MIPS3, GPR_64, SYM_64; def : MipsPat<(add GPR64:$hi, (MipsHigher (i64 tglobaladdr:$lo))), (DADDiu GPR64:$hi, tglobaladdr:$lo)>, ISA_MIPS3, GPR_64, SYM_64; def : MipsPat<(add GPR64:$hi, (MipsHigher (i64 tblockaddress:$lo))), (DADDiu GPR64:$hi, tblockaddress:$lo)>, ISA_MIPS3, GPR_64, SYM_64; def : MipsPat<(add GPR64:$hi, (MipsHigher (i64 tjumptable:$lo))), (DADDiu GPR64:$hi, tjumptable:$lo)>, ISA_MIPS3, GPR_64, SYM_64; def : MipsPat<(add GPR64:$hi, (MipsHigher (i64 tconstpool:$lo))), (DADDiu GPR64:$hi, tconstpool:$lo)>, ISA_MIPS3, GPR_64, SYM_64; def : MipsPat<(add GPR64:$hi, (MipsHi (i64 tglobaladdr:$lo))), (DADDiu GPR64:$hi, tglobaladdr:$lo)>, ISA_MIPS3, GPR_64, SYM_64; def : MipsPat<(add GPR64:$hi, (MipsHi (i64 tblockaddress:$lo))), (DADDiu GPR64:$hi, tblockaddress:$lo)>, ISA_MIPS3, GPR_64, SYM_64; def : MipsPat<(add GPR64:$hi, (MipsHi (i64 tjumptable:$lo))), (DADDiu GPR64:$hi, tjumptable:$lo)>, ISA_MIPS3, GPR_64, SYM_64; def : MipsPat<(add GPR64:$hi, (MipsHi (i64 tconstpool:$lo))), (DADDiu GPR64:$hi, tconstpool:$lo)>, ISA_MIPS3, GPR_64, SYM_64; def : MipsPat<(add GPR64:$hi, (MipsLo (i64 tglobaladdr:$lo))), (DADDiu GPR64:$hi, tglobaladdr:$lo)>, ISA_MIPS3, GPR_64, SYM_64; def : MipsPat<(add GPR64:$hi, (MipsLo (i64 tblockaddress:$lo))), (DADDiu GPR64:$hi, tblockaddress:$lo)>, ISA_MIPS3, GPR_64, SYM_64; def : MipsPat<(add GPR64:$hi, (MipsLo (i64 tjumptable:$lo))), (DADDiu GPR64:$hi, tjumptable:$lo)>, ISA_MIPS3, GPR_64, SYM_64; def : MipsPat<(add GPR64:$hi, (MipsLo (i64 tconstpool:$lo))), (DADDiu GPR64:$hi, tconstpool:$lo)>, ISA_MIPS3, GPR_64, SYM_64; def : MipsPat<(add GPR64:$hi, (MipsLo (i64 tglobaltlsaddr:$lo))), (DADDiu GPR64:$hi, tglobaltlsaddr:$lo)>, ISA_MIPS3, GPR_64, SYM_64; } // gp_rel relocs def : MipsPat<(add GPR64:$gp, (MipsGPRel tglobaladdr:$in)), (DADDiu GPR64:$gp, tglobaladdr:$in)>, ISA_MIPS3, ABI_N64; def : MipsPat<(add GPR64:$gp, (MipsGPRel tconstpool:$in)), (DADDiu GPR64:$gp, tconstpool:$in)>, ISA_MIPS3, ABI_N64; def : WrapperPat, ISA_MIPS3, GPR_64; def : WrapperPat, ISA_MIPS3, GPR_64; def : WrapperPat, ISA_MIPS3, GPR_64; def : WrapperPat, ISA_MIPS3, GPR_64; def : WrapperPat, ISA_MIPS3, GPR_64; def : WrapperPat, ISA_MIPS3, GPR_64; defm : BrcondPats, ISA_MIPS3, GPR_64; def : MipsPat<(brcond (i32 (setlt i64:$lhs, 1)), bb:$dst), (BLEZ64 i64:$lhs, bb:$dst)>, ISA_MIPS3, GPR_64; def : MipsPat<(brcond (i32 (setgt i64:$lhs, -1)), bb:$dst), (BGEZ64 i64:$lhs, bb:$dst)>, ISA_MIPS3, GPR_64; // setcc patterns let AdditionalPredicates = [NotInMicroMips] in { defm : SeteqPats, ISA_MIPS3, GPR_64; defm : SetlePats, ISA_MIPS3, GPR_64; defm : SetgtPats, ISA_MIPS3, GPR_64; defm : SetgePats, ISA_MIPS3, GPR_64; defm : SetgeImmPats, ISA_MIPS3, GPR_64; } // truncate def : MipsPat<(trunc (assertsext GPR64:$src)), (EXTRACT_SUBREG GPR64:$src, sub_32)>, ISA_MIPS3, GPR_64; // The forward compatibility strategy employed by MIPS requires us to treat // values as being sign extended to an infinite number of bits. This allows // existing software to run without modification on any future MIPS // implementation (e.g. 128-bit, or 1024-bit). Being compatible with this // strategy requires that truncation acts as a sign-extension for values being // fed into instructions operating on 32-bit values. Such instructions have // undefined results if this is not true. // For our case, this means that we can't issue an extract_subreg for nodes // such as (trunc:i32 (assertzext:i64 X, i32)), because the sign-bit of the // lower subreg would not be replicated into the upper half. def : MipsPat<(trunc (assertzext_lt_i32 GPR64:$src)), (EXTRACT_SUBREG GPR64:$src, sub_32)>, ISA_MIPS3, GPR_64; def : MipsPat<(i32 (trunc GPR64:$src)), (SLL (EXTRACT_SUBREG GPR64:$src, sub_32), 0)>, ISA_MIPS3, GPR_64; // variable shift instructions patterns def : MipsPat<(shl GPR64:$rt, (i32 (trunc GPR64:$rs))), (DSLLV GPR64:$rt, (EXTRACT_SUBREG GPR64:$rs, sub_32))>, ISA_MIPS3, GPR_64; def : MipsPat<(srl GPR64:$rt, (i32 (trunc GPR64:$rs))), (DSRLV GPR64:$rt, (EXTRACT_SUBREG GPR64:$rs, sub_32))>, ISA_MIPS3, GPR_64; def : MipsPat<(sra GPR64:$rt, (i32 (trunc GPR64:$rs))), (DSRAV GPR64:$rt, (EXTRACT_SUBREG GPR64:$rs, sub_32))>, ISA_MIPS3, GPR_64; def : MipsPat<(rotr GPR64:$rt, (i32 (trunc GPR64:$rs))), (DROTRV GPR64:$rt, (EXTRACT_SUBREG GPR64:$rs, sub_32))>, ISA_MIPS3, GPR_64; // 32-to-64-bit extension def : MipsPat<(i64 (anyext GPR32:$src)), (INSERT_SUBREG (i64 (IMPLICIT_DEF)), GPR32:$src, sub_32)>, ISA_MIPS3, GPR_64; def : MipsPat<(i64 (zext GPR32:$src)), (DSRL (DSLL64_32 GPR32:$src), 32)>, ISA_MIPS3, GPR_64; def : MipsPat<(i64 (sext GPR32:$src)), (SLL64_32 GPR32:$src)>, ISA_MIPS3, GPR_64; let AdditionalPredicates = [NotInMicroMips] in { def : MipsPat<(i64 (zext GPR32:$src)), (DEXT64_32 GPR32:$src, 0, 32)>, ISA_MIPS64R2, GPR_64; def : MipsPat<(i64 (zext (i32 (shl GPR32:$rt, immZExt5:$imm)))), (CINS64_32 GPR32:$rt, imm:$imm, (immZExt5To31 imm:$imm))>, ISA_MIPS64R2, GPR_64, ASE_MIPS64_CNMIPS; } // Sign extend in register def : MipsPat<(i64 (sext_inreg GPR64:$src, i32)), (SLL64_64 GPR64:$src)>, ISA_MIPS3, GPR_64; // bswap MipsPattern def : MipsPat<(bswap GPR64:$rt), (DSHD (DSBH GPR64:$rt))>, ISA_MIPS64R2; // Carry pattern let AdditionalPredicates = [NotInMicroMips] in { def : MipsPat<(subc GPR64:$lhs, GPR64:$rhs), (DSUBu GPR64:$lhs, GPR64:$rhs)>, ISA_MIPS3, GPR_64; def : MipsPat<(addc GPR64:$lhs, GPR64:$rhs), (DADDu GPR64:$lhs, GPR64:$rhs)>, ISA_MIPS3, ASE_NOT_DSP, GPR_64; def : MipsPat<(addc GPR64:$lhs, immSExt16:$imm), (DADDiu GPR64:$lhs, imm:$imm)>, ISA_MIPS3, ASE_NOT_DSP, GPR_64; } // Octeon bbit0/bbit1 MipsPattern def : MipsPat<(brcond (i32 (seteq (and i64:$lhs, PowerOf2LO:$mask), 0)), bb:$dst), (BBIT0 i64:$lhs, (Log2LO PowerOf2LO:$mask), bb:$dst)>, ISA_MIPS64R2, ASE_MIPS64_CNMIPS; def : MipsPat<(brcond (i32 (seteq (and i64:$lhs, PowerOf2HI:$mask), 0)), bb:$dst), (BBIT032 i64:$lhs, (Log2HI PowerOf2HI:$mask), bb:$dst)>, ISA_MIPS64R2, ASE_MIPS64_CNMIPS; def : MipsPat<(brcond (i32 (setne (and i64:$lhs, PowerOf2LO:$mask), 0)), bb:$dst), (BBIT1 i64:$lhs, (Log2LO PowerOf2LO:$mask), bb:$dst)>, ISA_MIPS64R2, ASE_MIPS64_CNMIPS; def : MipsPat<(brcond (i32 (setne (and i64:$lhs, PowerOf2HI:$mask), 0)), bb:$dst), (BBIT132 i64:$lhs, (Log2HI PowerOf2HI:$mask), bb:$dst)>, ISA_MIPS64R2, ASE_MIPS64_CNMIPS; def : MipsPat<(brcond (i32 (seteq (and i32:$lhs, PowerOf2LO_i32:$mask), 0)), bb:$dst), (BBIT0 (INSERT_SUBREG (i64 (IMPLICIT_DEF)), i32:$lhs, sub_32), (Log2LO PowerOf2LO_i32:$mask), bb:$dst)>, ISA_MIPS64R2, ASE_MIPS64_CNMIPS; def : MipsPat<(brcond (i32 (setne (and i32:$lhs, PowerOf2LO_i32:$mask), 0)), bb:$dst), (BBIT1 (INSERT_SUBREG (i64 (IMPLICIT_DEF)), i32:$lhs, sub_32), (Log2LO PowerOf2LO_i32:$mask), bb:$dst)>, ISA_MIPS64R2, ASE_MIPS64_CNMIPS; // Atomic load patterns. def : MipsPat<(atomic_load_8 addr:$a), (LB64 addr:$a)>, ISA_MIPS3, GPR_64; def : MipsPat<(atomic_load_16 addr:$a), (LH64 addr:$a)>, ISA_MIPS3, GPR_64; def : MipsPat<(atomic_load_32 addr:$a), (LW64 addr:$a)>, ISA_MIPS3, GPR_64; def : MipsPat<(atomic_load_64 addr:$a), (LD addr:$a)>, ISA_MIPS3, GPR_64; // Atomic store patterns. def : MipsPat<(atomic_store_8 addr:$a, GPR64:$v), (SB64 GPR64:$v, addr:$a)>, ISA_MIPS3, GPR_64; def : MipsPat<(atomic_store_16 addr:$a, GPR64:$v), (SH64 GPR64:$v, addr:$a)>, ISA_MIPS3, GPR_64; def : MipsPat<(atomic_store_32 addr:$a, GPR64:$v), (SW64 GPR64:$v, addr:$a)>, ISA_MIPS3, GPR_64; def : MipsPat<(atomic_store_64 addr:$a, GPR64:$v), (SD GPR64:$v, addr:$a)>, ISA_MIPS3, GPR_64; // Patterns used for matching away redundant sign extensions. // MIPS32 arithmetic instructions sign extend their result implicitly. def : MipsPat<(i64 (sext (i32 (add GPR32:$src, immSExt16:$imm16)))), (INSERT_SUBREG (i64 (IMPLICIT_DEF)), (ADDiu GPR32:$src, immSExt16:$imm16), sub_32)>; def : MipsPat<(i64 (sext (i32 (add GPR32:$src, GPR32:$src2)))), (INSERT_SUBREG (i64 (IMPLICIT_DEF)), (ADDu GPR32:$src, GPR32:$src2), sub_32)>; def : MipsPat<(i64 (sext (i32 (sub GPR32:$src, GPR32:$src2)))), (INSERT_SUBREG (i64 (IMPLICIT_DEF)), (SUBu GPR32:$src, GPR32:$src2), sub_32)>; def : MipsPat<(i64 (sext (i32 (mul GPR32:$src, GPR32:$src2)))), (INSERT_SUBREG (i64 (IMPLICIT_DEF)), (MUL GPR32:$src, GPR32:$src2), sub_32)>, ISA_MIPS32_NOT_32R6_64R6; def : MipsPat<(i64 (sext (i32 (MipsMFHI ACC64:$src)))), (INSERT_SUBREG (i64 (IMPLICIT_DEF)), (PseudoMFHI ACC64:$src), sub_32)>; def : MipsPat<(i64 (sext (i32 (MipsMFLO ACC64:$src)))), (INSERT_SUBREG (i64 (IMPLICIT_DEF)), (PseudoMFLO ACC64:$src), sub_32)>; def : MipsPat<(i64 (sext (i32 (shl GPR32:$src, immZExt5:$imm5)))), (INSERT_SUBREG (i64 (IMPLICIT_DEF)), (SLL GPR32:$src, immZExt5:$imm5), sub_32)>; def : MipsPat<(i64 (sext (i32 (shl GPR32:$src, GPR32:$src2)))), (INSERT_SUBREG (i64 (IMPLICIT_DEF)), (SLLV GPR32:$src, GPR32:$src2), sub_32)>; def : MipsPat<(i64 (sext (i32 (srl GPR32:$src, immZExt5:$imm5)))), (INSERT_SUBREG (i64 (IMPLICIT_DEF)), (SRL GPR32:$src, immZExt5:$imm5), sub_32)>; def : MipsPat<(i64 (sext (i32 (srl GPR32:$src, GPR32:$src2)))), (INSERT_SUBREG (i64 (IMPLICIT_DEF)), (SRLV GPR32:$src, GPR32:$src2), sub_32)>; def : MipsPat<(i64 (sext (i32 (sra GPR32:$src, immZExt5:$imm5)))), (INSERT_SUBREG (i64 (IMPLICIT_DEF)), (SRA GPR32:$src, immZExt5:$imm5), sub_32)>; def : MipsPat<(i64 (sext (i32 (sra GPR32:$src, GPR32:$src2)))), (INSERT_SUBREG (i64 (IMPLICIT_DEF)), (SRAV GPR32:$src, GPR32:$src2), sub_32)>; //===----------------------------------------------------------------------===// // Instruction aliases //===----------------------------------------------------------------------===// let AdditionalPredicates = [NotInMicroMips] in { def : MipsInstAlias<"move $dst, $src", (OR64 GPR64Opnd:$dst, GPR64Opnd:$src, ZERO_64), 1>, GPR_64; def : MipsInstAlias<"move $dst, $src", (DADDu GPR64Opnd:$dst, GPR64Opnd:$src, ZERO_64), 1>, GPR_64; def : MipsInstAlias<"dadd $rs, $rt, $imm", (DADDi GPR64Opnd:$rs, GPR64Opnd:$rt, simm16_64:$imm), 0>, ISA_MIPS3_NOT_32R6_64R6; def : MipsInstAlias<"dadd $rs, $imm", (DADDi GPR64Opnd:$rs, GPR64Opnd:$rs, simm16_64:$imm), 0>, ISA_MIPS3_NOT_32R6_64R6; def : MipsInstAlias<"daddu $rs, $rt, $imm", (DADDiu GPR64Opnd:$rs, GPR64Opnd:$rt, simm16_64:$imm), 0>, ISA_MIPS3; def : MipsInstAlias<"daddu $rs, $imm", (DADDiu GPR64Opnd:$rs, GPR64Opnd:$rs, simm16_64:$imm), 0>, ISA_MIPS3; defm : OneOrTwoOperandMacroImmediateAlias<"and", ANDi64, GPR64Opnd, imm64>, ISA_MIPS3, GPR_64; defm : OneOrTwoOperandMacroImmediateAlias<"or", ORi64, GPR64Opnd, imm64>, ISA_MIPS3, GPR_64; defm : OneOrTwoOperandMacroImmediateAlias<"xor", XORi64, GPR64Opnd, imm64>, ISA_MIPS3, GPR_64; } let AdditionalPredicates = [NotInMicroMips] in { def : MipsInstAlias<"dneg $rt, $rs", (DSUB GPR64Opnd:$rt, ZERO_64, GPR64Opnd:$rs), 1>, ISA_MIPS3; def : MipsInstAlias<"dneg $rt", (DSUB GPR64Opnd:$rt, ZERO_64, GPR64Opnd:$rt), 1>, ISA_MIPS3; def : MipsInstAlias<"dnegu $rt, $rs", (DSUBu GPR64Opnd:$rt, ZERO_64, GPR64Opnd:$rs), 1>, ISA_MIPS3; def : MipsInstAlias<"dnegu $rt", (DSUBu GPR64Opnd:$rt, ZERO_64, GPR64Opnd:$rt), 1>, ISA_MIPS3; } def : MipsInstAlias<"dsubi $rs, $rt, $imm", (DADDi GPR64Opnd:$rs, GPR64Opnd:$rt, InvertedImOperand64:$imm), 0>, ISA_MIPS3_NOT_32R6_64R6; def : MipsInstAlias<"dsubi $rs, $imm", (DADDi GPR64Opnd:$rs, GPR64Opnd:$rs, InvertedImOperand64:$imm), 0>, ISA_MIPS3_NOT_32R6_64R6; def : MipsInstAlias<"dsub $rs, $rt, $imm", (DADDi GPR64Opnd:$rs, GPR64Opnd:$rt, InvertedImOperand64:$imm), 0>, ISA_MIPS3_NOT_32R6_64R6; def : MipsInstAlias<"dsub $rs, $imm", (DADDi GPR64Opnd:$rs, GPR64Opnd:$rs, InvertedImOperand64:$imm), 0>, ISA_MIPS3_NOT_32R6_64R6; let AdditionalPredicates = [NotInMicroMips] in { def : MipsInstAlias<"dsubu $rt, $rs, $imm", (DADDiu GPR64Opnd:$rt, GPR64Opnd:$rs, InvertedImOperand64:$imm), 0>, ISA_MIPS3; def : MipsInstAlias<"dsubu $rs, $imm", (DADDiu GPR64Opnd:$rs, GPR64Opnd:$rs, InvertedImOperand64:$imm), 0>, ISA_MIPS3; } def : MipsInstAlias<"dsra $rd, $rt, $rs", (DSRAV GPR64Opnd:$rd, GPR64Opnd:$rt, GPR32Opnd:$rs), 0>, ISA_MIPS3; let AdditionalPredicates = [NotInMicroMips] in { def : MipsInstAlias<"dsll $rd, $rt, $rs", (DSLLV GPR64Opnd:$rd, GPR64Opnd:$rt, GPR32Opnd:$rs), 0>, ISA_MIPS3; def : MipsInstAlias<"dsrl $rd, $rt, $rs", (DSRLV GPR64Opnd:$rd, GPR64Opnd:$rt, GPR32Opnd:$rs), 0>, ISA_MIPS3; def : MipsInstAlias<"dsrl $rd, $rt", (DSRLV GPR64Opnd:$rd, GPR64Opnd:$rd, GPR32Opnd:$rt), 0>, ISA_MIPS3; def : MipsInstAlias<"dsll $rd, $rt", (DSLLV GPR64Opnd:$rd, GPR64Opnd:$rd, GPR32Opnd:$rt), 0>, ISA_MIPS3; def : MipsInstAlias<"dins $rt, $rs, $pos, $size", (DINSM GPR64Opnd:$rt, GPR64Opnd:$rs, uimm5:$pos, uimm_range_2_64:$size), 0>, ISA_MIPS64R2; def : MipsInstAlias<"dins $rt, $rs, $pos, $size", (DINSU GPR64Opnd:$rt, GPR64Opnd:$rs, uimm5_plus32:$pos, uimm5_plus1:$size), 0>, ISA_MIPS64R2; def : MipsInstAlias<"dext $rt, $rs, $pos, $size", (DEXTM GPR64Opnd:$rt, GPR64Opnd:$rs, uimm5:$pos, uimm5_plus33:$size), 0>, ISA_MIPS64R2; def : MipsInstAlias<"dext $rt, $rs, $pos, $size", (DEXTU GPR64Opnd:$rt, GPR64Opnd:$rs, uimm5_plus32:$pos, uimm5_plus1:$size), 0>, ISA_MIPS64R2; def : MipsInstAlias<"jalr.hb $rs", (JALR_HB64 RA_64, GPR64Opnd:$rs), 1>, ISA_MIPS64; // Two operand (implicit 0 selector) versions: def : MipsInstAlias<"dmtc0 $rt, $rd", (DMTC0 COP0Opnd:$rd, GPR64Opnd:$rt, 0), 0>; def : MipsInstAlias<"dmfc0 $rt, $rd", (DMFC0 GPR64Opnd:$rt, COP0Opnd:$rd, 0), 0>; def : MipsInstAlias<"dmfgc0 $rt, $rd", (DMFGC0 GPR64Opnd:$rt, COP0Opnd:$rd, 0), 0>, ISA_MIPS64R5, ASE_VIRT; def : MipsInstAlias<"dmtgc0 $rt, $rd", (DMTGC0 COP0Opnd:$rd, GPR64Opnd:$rt, 0), 0>, ISA_MIPS64R5, ASE_VIRT; } def : MipsInstAlias<"dmfc2 $rt, $rd", (DMFC2 GPR64Opnd:$rt, COP2Opnd:$rd, 0), 0>; def : MipsInstAlias<"dmtc2 $rt, $rd", (DMTC2 COP2Opnd:$rd, GPR64Opnd:$rt, 0), 0>; def : MipsInstAlias<"synciobdma", (SYNC 0x2), 0>, ASE_MIPS64_CNMIPS; def : MipsInstAlias<"syncs", (SYNC 0x6), 0>, ASE_MIPS64_CNMIPS; def : MipsInstAlias<"syncw", (SYNC 0x4), 0>, ASE_MIPS64_CNMIPS; def : MipsInstAlias<"syncws", (SYNC 0x5), 0>, ASE_MIPS64_CNMIPS; // cnMIPS Aliases. // bbit* with $p 32-63 converted to bbit*32 with $p 0-31 def : MipsInstAlias<"bbit0 $rs, $p, $offset", (BBIT032 GPR64Opnd:$rs, uimm5_plus32_normalize_64:$p, brtarget:$offset), 0>, ASE_CNMIPS; def : MipsInstAlias<"bbit1 $rs, $p, $offset", (BBIT132 GPR64Opnd:$rs, uimm5_plus32_normalize_64:$p, brtarget:$offset), 0>, ASE_CNMIPS; // exts with $pos 32-63 in converted to exts32 with $pos 0-31 def : MipsInstAlias<"exts $rt, $rs, $pos, $lenm1", (EXTS32 GPR64Opnd:$rt, GPR64Opnd:$rs, uimm5_plus32_normalize:$pos, uimm5:$lenm1), 0>, ASE_MIPS64_CNMIPS; def : MipsInstAlias<"exts $rt, $pos, $lenm1", (EXTS32 GPR64Opnd:$rt, GPR64Opnd:$rt, uimm5_plus32_normalize:$pos, uimm5:$lenm1), 0>, ASE_MIPS64_CNMIPS; // cins with $pos 32-63 in converted to cins32 with $pos 0-31 def : MipsInstAlias<"cins $rt, $rs, $pos, $lenm1", (CINS32 GPR64Opnd:$rt, GPR64Opnd:$rs, uimm5_plus32_normalize:$pos, uimm5:$lenm1), 0>, ASE_MIPS64_CNMIPS; def : MipsInstAlias<"cins $rt, $pos, $lenm1", (CINS32 GPR64Opnd:$rt, GPR64Opnd:$rt, uimm5_plus32_normalize:$pos, uimm5:$lenm1), 0>, ASE_MIPS64_CNMIPS; //===----------------------------------------------------------------------===// // Assembler Pseudo Instructions //===----------------------------------------------------------------------===// class LoadImmediate64 : MipsAsmPseudoInst<(outs RO:$rt), (ins Od:$imm64), !strconcat(instr_asm, "\t$rt, $imm64")> ; def LoadImm64 : LoadImmediate64<"dli", imm64, GPR64Opnd>; def LoadAddrReg64 : MipsAsmPseudoInst<(outs GPR64Opnd:$rt), (ins mem:$addr), "dla\t$rt, $addr">; def LoadAddrImm64 : MipsAsmPseudoInst<(outs GPR64Opnd:$rt), (ins imm64:$imm64), "dla\t$rt, $imm64">; def DMULImmMacro : MipsAsmPseudoInst<(outs), (ins GPR64Opnd:$rs, GPR64Opnd:$rt, simm32_relaxed:$imm), "dmul\t$rs, $rt, $imm">, ISA_MIPS3_NOT_32R6_64R6; def DMULOMacro : MipsAsmPseudoInst<(outs), (ins GPR64Opnd:$rs, GPR64Opnd:$rt, GPR64Opnd:$rd), "dmulo\t$rs, $rt, $rd">, ISA_MIPS3_NOT_32R6_64R6; def DMULOUMacro : MipsAsmPseudoInst<(outs), (ins GPR64Opnd:$rs, GPR64Opnd:$rt, GPR64Opnd:$rd), "dmulou\t$rs, $rt, $rd">, ISA_MIPS3_NOT_32R6_64R6; def DMULMacro : MipsAsmPseudoInst<(outs), (ins GPR64Opnd:$rs, GPR64Opnd:$rt, GPR64Opnd:$rd), "dmul\t$rs, $rt, $rd"> { let InsnPredicates = [HasMips3, NotMips64r6, NotCnMips]; } let AdditionalPredicates = [NotInMicroMips] in { def DSDivMacro : MipsAsmPseudoInst<(outs GPR64Opnd:$rd), (ins GPR64Opnd:$rs, GPR64Opnd:$rt), "ddiv\t$rd, $rs, $rt">, ISA_MIPS3_NOT_32R6_64R6; def DSDivIMacro : MipsAsmPseudoInst<(outs GPR64Opnd:$rd), (ins GPR64Opnd:$rs, imm64:$imm), "ddiv\t$rd, $rs, $imm">, ISA_MIPS3_NOT_32R6_64R6; def DUDivMacro : MipsAsmPseudoInst<(outs GPR64Opnd:$rd), (ins GPR64Opnd:$rs, GPR64Opnd:$rt), "ddivu\t$rd, $rs, $rt">, ISA_MIPS3_NOT_32R6_64R6; def DUDivIMacro : MipsAsmPseudoInst<(outs GPR64Opnd:$rd), (ins GPR64Opnd:$rs, imm64:$imm), "ddivu\t$rd, $rs, $imm">, ISA_MIPS3_NOT_32R6_64R6; // GAS expands 'div' and 'ddiv' differently when the destination // register is $zero and the instruction is in the two operand // form. 'ddiv' gets expanded, while 'div' is not expanded. def : MipsInstAlias<"ddiv $rs, $rt", (DSDivMacro GPR64Opnd:$rs, GPR64Opnd:$rs, GPR64Opnd:$rt), 0>, ISA_MIPS3_NOT_32R6_64R6; def : MipsInstAlias<"ddiv $rd, $imm", (DSDivIMacro GPR64Opnd:$rd, GPR64Opnd:$rd, imm64:$imm), 0>, ISA_MIPS3_NOT_32R6_64R6; // GAS expands 'divu' and 'ddivu' differently when the destination // register is $zero and the instruction is in the two operand // form. 'ddivu' gets expanded, while 'divu' is not expanded. def : MipsInstAlias<"ddivu $rt, $rs", (DUDivMacro GPR64Opnd:$rt, GPR64Opnd:$rt, GPR64Opnd:$rs), 0>, ISA_MIPS3_NOT_32R6_64R6; def : MipsInstAlias<"ddivu $rd, $imm", (DUDivIMacro GPR64Opnd:$rd, GPR64Opnd:$rd, imm64:$imm), 0>, ISA_MIPS3_NOT_32R6_64R6; def DSRemMacro : MipsAsmPseudoInst<(outs GPR64Opnd:$rd), (ins GPR64Opnd:$rs, GPR64Opnd:$rt), "drem\t$rd, $rs, $rt">, ISA_MIPS3_NOT_32R6_64R6; def DSRemIMacro : MipsAsmPseudoInst<(outs GPR64Opnd:$rd), (ins GPR64Opnd:$rs, simm32_relaxed:$imm), "drem\t$rd, $rs, $imm">, ISA_MIPS3_NOT_32R6_64R6; def DURemMacro : MipsAsmPseudoInst<(outs GPR64Opnd:$rd), (ins GPR64Opnd:$rs, GPR64Opnd:$rt), "dremu\t$rd, $rs, $rt">, ISA_MIPS3_NOT_32R6_64R6; def DURemIMacro : MipsAsmPseudoInst<(outs GPR64Opnd:$rd), (ins GPR64Opnd:$rs, simm32_relaxed:$imm), "dremu\t$rd, $rs, $imm">, ISA_MIPS3_NOT_32R6_64R6; def : MipsInstAlias<"drem $rt, $rs", (DSRemMacro GPR64Opnd:$rt, GPR64Opnd:$rt, GPR64Opnd:$rs), 0>, ISA_MIPS3_NOT_32R6_64R6; def : MipsInstAlias<"drem $rd, $imm", (DSRemIMacro GPR64Opnd:$rd, GPR64Opnd:$rd, simm32_relaxed:$imm), 0>, ISA_MIPS3_NOT_32R6_64R6; def : MipsInstAlias<"dremu $rt, $rs", (DURemMacro GPR64Opnd:$rt, GPR64Opnd:$rt, GPR64Opnd:$rs), 0>, ISA_MIPS3_NOT_32R6_64R6; def : MipsInstAlias<"dremu $rd, $imm", (DURemIMacro GPR64Opnd:$rd, GPR64Opnd:$rd, simm32_relaxed:$imm), 0>, ISA_MIPS3_NOT_32R6_64R6; } def NORImm64 : NORIMM_DESC_BASE, GPR_64; def : MipsInstAlias<"nor\t$rs, $imm", (NORImm64 GPR64Opnd:$rs, GPR64Opnd:$rs, imm64:$imm)>, GPR_64; def SLTImm64 : MipsAsmPseudoInst<(outs GPR64Opnd:$rs), (ins GPR64Opnd:$rt, imm64:$imm), "slt\t$rs, $rt, $imm">, GPR_64; def : MipsInstAlias<"slt\t$rs, $imm", (SLTImm64 GPR64Opnd:$rs, GPR64Opnd:$rs, imm64:$imm)>, GPR_64; def SLTUImm64 : MipsAsmPseudoInst<(outs GPR64Opnd:$rs), (ins GPR64Opnd:$rt, imm64:$imm), "sltu\t$rs, $rt, $imm">, GPR_64; def : MipsInstAlias<"sltu\t$rs, $imm", (SLTUImm64 GPR64Opnd:$rs, GPR64Opnd:$rs, imm64:$imm)>, GPR_64; def SGEImm64 : MipsAsmPseudoInst<(outs GPR64Opnd:$rd), (ins GPR64Opnd:$rs, imm64:$imm), "sge\t$rd, $rs, $imm">, GPR_64; def : MipsInstAlias<"sge $rs, $imm", (SGEImm64 GPR64Opnd:$rs, GPR64Opnd:$rs, imm64:$imm), 0>, GPR_64; def SGEUImm64 : MipsAsmPseudoInst<(outs GPR64Opnd:$rd), (ins GPR64Opnd:$rs, imm64:$imm), "sgeu\t$rd, $rs, $imm">, GPR_64; def : MipsInstAlias<"sgeu $rs, $imm", (SGEUImm64 GPR64Opnd:$rs, GPR64Opnd:$rs, imm64:$imm), 0>, GPR_64; def SGTImm64 : MipsAsmPseudoInst<(outs GPR64Opnd:$rd), (ins GPR64Opnd:$rs, imm64:$imm), "sgt\t$rd, $rs, $imm">, GPR_64; def : MipsInstAlias<"sgt $rs, $imm", (SGTImm64 GPR64Opnd:$rs, GPR64Opnd:$rs, imm64:$imm), 0>, GPR_64; def SGTUImm64 : MipsAsmPseudoInst<(outs GPR64Opnd:$rd), (ins GPR64Opnd:$rs, imm64:$imm), "sgtu\t$rd, $rs, $imm">, GPR_64; def : MipsInstAlias<"sgtu $rs, $imm", (SGTUImm64 GPR64Opnd:$rs, GPR64Opnd:$rs, imm64:$imm), 0>, GPR_64; def : MipsInstAlias<"rdhwr $rt, $rs", (RDHWR64 GPR64Opnd:$rt, HWRegsOpnd:$rs, 0), 1>, GPR_64; Index: head/contrib/llvm/lib/Target/Mips/MipsInstrFormats.td =================================================================== --- head/contrib/llvm/lib/Target/Mips/MipsInstrFormats.td (revision 354978) +++ head/contrib/llvm/lib/Target/Mips/MipsInstrFormats.td (revision 354979) @@ -1,985 +1,998 @@ //===-- MipsInstrFormats.td - Mips Instruction Formats -----*- tablegen -*-===// // // 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 // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// // Describe MIPS instructions format // // CPU INSTRUCTION FORMATS // // opcode - operation code. // rs - src reg. // rt - dst reg (on a 2 regs instr) or src reg (on a 3 reg instr). // rd - dst reg, only used on 3 regs instr. // shamt - only used on shift instructions, contains the shift amount. // funct - combined with opcode field give us an operation code. // //===----------------------------------------------------------------------===// // Format specifies the encoding used by the instruction. This is part of the // ad-hoc solution used to emit machine instruction encodings by our machine // code emitter. class Format val> { bits<4> Value = val; } def Pseudo : Format<0>; def FrmR : Format<1>; def FrmI : Format<2>; def FrmJ : Format<3>; def FrmFR : Format<4>; def FrmFI : Format<5>; def FrmOther : Format<6>; // Instruction w/ a custom format class MMRel; def Std2MicroMips : InstrMapping { let FilterClass = "MMRel"; // Instructions with the same BaseOpcode and isNVStore values form a row. let RowFields = ["BaseOpcode"]; // Instructions with the same predicate sense form a column. let ColFields = ["Arch"]; // The key column is the unpredicated instructions. let KeyCol = ["se"]; // Value columns are PredSense=true and PredSense=false let ValueCols = [["se"], ["micromips"]]; } class StdMMR6Rel; def Std2MicroMipsR6 : InstrMapping { let FilterClass = "StdMMR6Rel"; // Instructions with the same BaseOpcode and isNVStore values form a row. let RowFields = ["BaseOpcode"]; // Instructions with the same predicate sense form a column. let ColFields = ["Arch"]; // The key column is the unpredicated instructions. let KeyCol = ["se"]; // Value columns are PredSense=true and PredSense=false let ValueCols = [["se"], ["micromipsr6"]]; } class StdArch { string Arch = "se"; } // Generic Mips Format class MipsInst pattern, InstrItinClass itin, Format f>: Instruction, PredicateControl { field bits<32> Inst; Format Form = f; let Namespace = "Mips"; let Size = 4; bits<6> Opcode = 0; // Top 6 bits are the 'opcode' field let Inst{31-26} = Opcode; let OutOperandList = outs; let InOperandList = ins; let AsmString = asmstr; let Pattern = pattern; let Itinerary = itin; // // Attributes specific to Mips instructions... // bits<4> FormBits = Form.Value; bit isCTI = 0; // Any form of Control Transfer Instruction. // Required for MIPSR6 bit hasForbiddenSlot = 0; // Instruction has a forbidden slot. bit IsPCRelativeLoad = 0; // Load instruction with implicit source register // ($pc) and with explicit offset and destination // register bit hasFCCRegOperand = 0; // Instruction uses $fcc register and is // present in MIPS-I to MIPS-III. // TSFlags layout should be kept in sync with MCTargetDesc/MipsBaseInfo.h. let TSFlags{3-0} = FormBits; let TSFlags{4} = isCTI; let TSFlags{5} = hasForbiddenSlot; let TSFlags{6} = IsPCRelativeLoad; let TSFlags{7} = hasFCCRegOperand; let DecoderNamespace = "Mips"; field bits<32> SoftFail = 0; } // Mips32/64 Instruction Format class InstSE pattern, InstrItinClass itin, Format f, string opstr = ""> : MipsInst { let EncodingPredicates = [NotInMips16Mode]; string BaseOpcode = opstr; string Arch; } // Mips Pseudo Instructions Format class MipsPseudo pattern, InstrItinClass itin = IIPseudo> : MipsInst { let isCodeGenOnly = 1; let isPseudo = 1; } // Mips32/64 Pseudo Instruction Format class PseudoSE pattern, InstrItinClass itin = IIPseudo> : MipsPseudo { let EncodingPredicates = [NotInMips16Mode]; } // Pseudo-instructions for alternate assembly syntax (never used by codegen). // These are aliases that require C++ handling to convert to the target // instruction, while InstAliases can be handled directly by tblgen. class MipsAsmPseudoInst: MipsInst { let isPseudo = 1; let hasNoSchedulingInfo = 1; let Pattern = []; } //===----------------------------------------------------------------------===// // Format R instruction class in Mips : <|opcode|rs|rt|rd|shamt|funct|> //===----------------------------------------------------------------------===// class FR op, bits<6> _funct, dag outs, dag ins, string asmstr, list pattern, InstrItinClass itin>: InstSE { bits<5> rd; bits<5> rs; bits<5> rt; bits<5> shamt; bits<6> funct; let Opcode = op; let funct = _funct; let Inst{25-21} = rs; let Inst{20-16} = rt; let Inst{15-11} = rd; let Inst{10-6} = shamt; let Inst{5-0} = funct; } //===----------------------------------------------------------------------===// // Format I instruction class in Mips : <|opcode|rs|rt|immediate|> //===----------------------------------------------------------------------===// class FI op, dag outs, dag ins, string asmstr, list pattern, InstrItinClass itin>: InstSE { bits<5> rt; bits<5> rs; bits<16> imm16; let Opcode = op; let Inst{25-21} = rs; let Inst{20-16} = rt; let Inst{15-0} = imm16; } class BranchBase op, dag outs, dag ins, string asmstr, list pattern, InstrItinClass itin>: InstSE { bits<5> rs; bits<5> rt; bits<16> imm16; let Opcode = op; let Inst{25-21} = rs; let Inst{20-16} = rt; let Inst{15-0} = imm16; } //===----------------------------------------------------------------------===// // Format J instruction class in Mips : <|opcode|address|> //===----------------------------------------------------------------------===// class FJ op> : StdArch { bits<26> target; bits<32> Inst; let Inst{31-26} = op; let Inst{25-0} = target; } //===----------------------------------------------------------------------===// // MFC instruction class in Mips : <|op|mf|rt|rd|gst|0000|sel|> //===----------------------------------------------------------------------===// class MFC3OP_FM op, bits<5> mfmt, bits<3> guest> : StdArch { bits<5> rt; bits<5> rd; bits<3> sel; bits<32> Inst; let Inst{31-26} = op; let Inst{25-21} = mfmt; let Inst{20-16} = rt; let Inst{15-11} = rd; let Inst{10-8} = guest; let Inst{7-3} = 0; let Inst{2-0} = sel; } class MFC2OP_FM op, bits<5> mfmt> : StdArch { bits<5> rt; bits<16> imm16; bits<32> Inst; let Inst{31-26} = op; let Inst{25-21} = mfmt; let Inst{20-16} = rt; let Inst{15-0} = imm16; } class ADD_FM op, bits<6> funct> : StdArch { bits<5> rd; bits<5> rs; bits<5> rt; bits<32> Inst; let Inst{31-26} = op; let Inst{25-21} = rs; let Inst{20-16} = rt; let Inst{15-11} = rd; let Inst{10-6} = 0; let Inst{5-0} = funct; } class ADDI_FM op> : StdArch { bits<5> rs; bits<5> rt; bits<16> imm16; bits<32> Inst; let Inst{31-26} = op; let Inst{25-21} = rs; let Inst{20-16} = rt; let Inst{15-0} = imm16; } class SRA_FM funct, bit rotate> : StdArch { bits<5> rd; bits<5> rt; bits<5> shamt; bits<32> Inst; let Inst{31-26} = 0; let Inst{25-22} = 0; let Inst{21} = rotate; let Inst{20-16} = rt; let Inst{15-11} = rd; let Inst{10-6} = shamt; let Inst{5-0} = funct; } class SRLV_FM funct, bit rotate> : StdArch { bits<5> rd; bits<5> rt; bits<5> rs; bits<32> Inst; let Inst{31-26} = 0; let Inst{25-21} = rs; let Inst{20-16} = rt; let Inst{15-11} = rd; let Inst{10-7} = 0; let Inst{6} = rotate; let Inst{5-0} = funct; } class BEQ_FM op> : StdArch { bits<5> rs; bits<5> rt; bits<16> offset; bits<32> Inst; let Inst{31-26} = op; let Inst{25-21} = rs; let Inst{20-16} = rt; let Inst{15-0} = offset; } class BGEZ_FM op, bits<5> funct> : StdArch { bits<5> rs; bits<16> offset; bits<32> Inst; let Inst{31-26} = op; let Inst{25-21} = rs; let Inst{20-16} = funct; let Inst{15-0} = offset; } class BBIT_FM op> : StdArch { bits<5> rs; bits<5> p; bits<16> offset; bits<32> Inst; let Inst{31-26} = op; let Inst{25-21} = rs; let Inst{20-16} = p; let Inst{15-0} = offset; } class SLTI_FM op> : StdArch { bits<5> rt; bits<5> rs; bits<16> imm16; bits<32> Inst; let Inst{31-26} = op; let Inst{25-21} = rs; let Inst{20-16} = rt; let Inst{15-0} = imm16; } class MFLO_FM funct> : StdArch { bits<5> rd; bits<32> Inst; let Inst{31-26} = 0; let Inst{25-16} = 0; let Inst{15-11} = rd; let Inst{10-6} = 0; let Inst{5-0} = funct; } class MTLO_FM funct> : StdArch { bits<5> rs; bits<32> Inst; let Inst{31-26} = 0; let Inst{25-21} = rs; let Inst{20-6} = 0; let Inst{5-0} = funct; } class SEB_FM funct, bits<6> funct2> : StdArch { bits<5> rd; bits<5> rt; bits<32> Inst; let Inst{31-26} = 0x1f; let Inst{25-21} = 0; let Inst{20-16} = rt; let Inst{15-11} = rd; let Inst{10-6} = funct; let Inst{5-0} = funct2; } class CLO_FM funct> : StdArch { bits<5> rd; bits<5> rs; bits<5> rt; bits<32> Inst; let Inst{31-26} = 0x1c; let Inst{25-21} = rs; let Inst{20-16} = rt; let Inst{15-11} = rd; let Inst{10-6} = 0; let Inst{5-0} = funct; let rt = rd; } class LUI_FM : StdArch { bits<5> rt; bits<16> imm16; bits<32> Inst; let Inst{31-26} = 0xf; let Inst{25-21} = 0; let Inst{20-16} = rt; let Inst{15-0} = imm16; } class JALR_FM { bits<5> rd; bits<5> rs; bits<32> Inst; let Inst{31-26} = 0; let Inst{25-21} = rs; let Inst{20-16} = 0; let Inst{15-11} = rd; let Inst{10-6} = 0; let Inst{5-0} = 9; } class BGEZAL_FM funct> : StdArch { bits<5> rs; bits<16> offset; bits<32> Inst; let Inst{31-26} = 1; let Inst{25-21} = rs; let Inst{20-16} = funct; let Inst{15-0} = offset; } class SYNC_FM : StdArch { bits<5> stype; bits<32> Inst; let Inst{31-26} = 0; let Inst{10-6} = stype; let Inst{5-0} = 0xf; } class SYNCI_FM : StdArch { // Produced by the mem_simm16 address as reg << 16 | imm (see getMemEncoding). bits<21> addr; bits<5> rs = addr{20-16}; bits<16> offset = addr{15-0}; bits<32> Inst; let Inst{31-26} = 0b000001; let Inst{25-21} = rs; let Inst{20-16} = 0b11111; let Inst{15-0} = offset; } class MULT_FM op, bits<6> funct> : StdArch { bits<5> rs; bits<5> rt; bits<32> Inst; let Inst{31-26} = op; let Inst{25-21} = rs; let Inst{20-16} = rt; let Inst{15-6} = 0; let Inst{5-0} = funct; } class EXT_FM funct> : StdArch { bits<5> rt; bits<5> rs; bits<5> pos; bits<5> size; bits<32> Inst; let Inst{31-26} = 0x1f; let Inst{25-21} = rs; let Inst{20-16} = rt; let Inst{15-11} = size; let Inst{10-6} = pos; let Inst{5-0} = funct; } class RDHWR_FM : StdArch { bits<5> rt; bits<5> rd; bits<3> sel; bits<32> Inst; let Inst{31-26} = 0x1f; let Inst{25-21} = 0; let Inst{20-16} = rt; let Inst{15-11} = rd; let Inst{10-9} = 0b00; let Inst{8-6} = sel; let Inst{5-0} = 0x3b; } class TEQ_FM funct> : StdArch { bits<5> rs; bits<5> rt; bits<10> code_; bits<32> Inst; let Inst{31-26} = 0; let Inst{25-21} = rs; let Inst{20-16} = rt; let Inst{15-6} = code_; let Inst{5-0} = funct; } class TEQI_FM funct> : StdArch { bits<5> rs; bits<16> imm16; bits<32> Inst; let Inst{31-26} = 1; let Inst{25-21} = rs; let Inst{20-16} = funct; let Inst{15-0} = imm16; } class WAIT_FM : StdArch { bits<32> Inst; let Inst{31-26} = 0x10; let Inst{25} = 1; let Inst{24-6} = 0; let Inst{5-0} = 0x20; } class EXTS_FM funct> : StdArch { bits<5> rt; bits<5> rs; bits<5> pos; bits<5> lenm1; bits<32> Inst; let Inst{31-26} = 0x1c; let Inst{25-21} = rs; let Inst{20-16} = rt; let Inst{15-11} = lenm1; let Inst{10-6} = pos; let Inst{5-0} = funct; } class MTMR_FM funct> : StdArch { bits<5> rs; bits<32> Inst; let Inst{31-26} = 0x1c; let Inst{25-21} = rs; let Inst{20-6} = 0; let Inst{5-0} = funct; } class POP_FM funct> : StdArch { bits<5> rd; bits<5> rs; bits<32> Inst; let Inst{31-26} = 0x1c; let Inst{25-21} = rs; let Inst{20-16} = 0; let Inst{15-11} = rd; let Inst{10-6} = 0; let Inst{5-0} = funct; } class SEQ_FM funct> : StdArch { bits<5> rd; bits<5> rs; bits<5> rt; bits<32> Inst; let Inst{31-26} = 0x1c; let Inst{25-21} = rs; let Inst{20-16} = rt; let Inst{15-11} = rd; let Inst{10-6} = 0; let Inst{5-0} = funct; } class SEQI_FM funct> : StdArch { bits<5> rs; bits<5> rt; bits<10> imm10; bits<32> Inst; let Inst{31-26} = 0x1c; let Inst{25-21} = rs; let Inst{20-16} = rt; let Inst{15-6} = imm10; let Inst{5-0} = funct; } +class SAA_FM funct> : StdArch { + bits<5> rt; + bits<5> rs; + + bits<32> Inst; + + let Inst{31-26} = 0x1c; + let Inst{25-21} = rs; + let Inst{20-16} = rt; + let Inst{15-6} = 0; + let Inst{5-0} = funct; +} + //===----------------------------------------------------------------------===// // System calls format //===----------------------------------------------------------------------===// class SYS_FM funct> : StdArch { bits<20> code_; bits<32> Inst; let Inst{31-26} = 0x0; let Inst{25-6} = code_; let Inst{5-0} = funct; } //===----------------------------------------------------------------------===// // Break instruction format //===----------------------------------------------------------------------===// class BRK_FM funct> : StdArch { bits<10> code_1; bits<10> code_2; bits<32> Inst; let Inst{31-26} = 0x0; let Inst{25-16} = code_1; let Inst{15-6} = code_2; let Inst{5-0} = funct; } //===----------------------------------------------------------------------===// // Exception return format //===----------------------------------------------------------------------===// class ER_FM funct, bit LLBit> : StdArch { bits<32> Inst; let Inst{31-26} = 0x10; let Inst{25} = 1; let Inst{24-7} = 0; let Inst{6} = LLBit; let Inst{5-0} = funct; } //===----------------------------------------------------------------------===// // Enable/disable interrupt instruction format //===----------------------------------------------------------------------===// class EI_FM sc> : StdArch { bits<32> Inst; bits<5> rt; let Inst{31-26} = 0x10; let Inst{25-21} = 0xb; let Inst{20-16} = rt; let Inst{15-11} = 0xc; let Inst{10-6} = 0; let Inst{5} = sc; let Inst{4-0} = 0; } //===----------------------------------------------------------------------===// // // FLOATING POINT INSTRUCTION FORMATS // // opcode - operation code. // fs - src reg. // ft - dst reg (on a 2 regs instr) or src reg (on a 3 reg instr). // fd - dst reg, only used on 3 regs instr. // fmt - double or single precision. // funct - combined with opcode field give us an operation code. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// // Format FI instruction class in Mips : <|opcode|base|ft|immediate|> //===----------------------------------------------------------------------===// class FFI op, dag outs, dag ins, string asmstr, list pattern>: InstSE { bits<5> ft; bits<5> base; bits<16> imm16; let Opcode = op; let Inst{25-21} = base; let Inst{20-16} = ft; let Inst{15-0} = imm16; } class ADDS_FM funct, bits<5> fmt> : StdArch { bits<5> fd; bits<5> fs; bits<5> ft; bits<32> Inst; let Inst{31-26} = 0x11; let Inst{25-21} = fmt; let Inst{20-16} = ft; let Inst{15-11} = fs; let Inst{10-6} = fd; let Inst{5-0} = funct; } class ABSS_FM funct, bits<5> fmt> : StdArch { bits<5> fd; bits<5> fs; bits<32> Inst; let Inst{31-26} = 0x11; let Inst{25-21} = fmt; let Inst{20-16} = 0; let Inst{15-11} = fs; let Inst{10-6} = fd; let Inst{5-0} = funct; } class MFC1_FM funct> : StdArch { bits<5> rt; bits<5> fs; bits<32> Inst; let Inst{31-26} = 0x11; let Inst{25-21} = funct; let Inst{20-16} = rt; let Inst{15-11} = fs; let Inst{10-0} = 0; } class LW_FM op> : StdArch { bits<5> rt; bits<21> addr; bits<32> Inst; let Inst{31-26} = op; let Inst{25-21} = addr{20-16}; let Inst{20-16} = rt; let Inst{15-0} = addr{15-0}; } class MADDS_FM funct, bits<3> fmt> : StdArch { bits<5> fd; bits<5> fr; bits<5> fs; bits<5> ft; bits<32> Inst; let Inst{31-26} = 0x13; let Inst{25-21} = fr; let Inst{20-16} = ft; let Inst{15-11} = fs; let Inst{10-6} = fd; let Inst{5-3} = funct; let Inst{2-0} = fmt; } class LWXC1_FM funct> : StdArch { bits<5> fd; bits<5> base; bits<5> index; bits<32> Inst; let Inst{31-26} = 0x13; let Inst{25-21} = base; let Inst{20-16} = index; let Inst{15-11} = 0; let Inst{10-6} = fd; let Inst{5-0} = funct; } class SWXC1_FM funct> : StdArch { bits<5> fs; bits<5> base; bits<5> index; bits<32> Inst; let Inst{31-26} = 0x13; let Inst{25-21} = base; let Inst{20-16} = index; let Inst{15-11} = fs; let Inst{10-6} = 0; let Inst{5-0} = funct; } class BC1F_FM : StdArch { bits<3> fcc; bits<16> offset; bits<32> Inst; let Inst{31-26} = 0x11; let Inst{25-21} = 0x8; let Inst{20-18} = fcc; let Inst{17} = nd; let Inst{16} = tf; let Inst{15-0} = offset; } class CEQS_FM fmt> : StdArch { bits<5> fs; bits<5> ft; bits<3> fcc; bits<4> cond; bits<32> Inst; let Inst{31-26} = 0x11; let Inst{25-21} = fmt; let Inst{20-16} = ft; let Inst{15-11} = fs; let Inst{10-8} = fcc; let Inst{7-4} = 0x3; let Inst{3-0} = cond; } class C_COND_FM fmt, bits<4> c> : CEQS_FM { let cond = c; } class CMov_I_F_FM funct, bits<5> fmt> : StdArch { bits<5> fd; bits<5> fs; bits<5> rt; bits<32> Inst; let Inst{31-26} = 0x11; let Inst{25-21} = fmt; let Inst{20-16} = rt; let Inst{15-11} = fs; let Inst{10-6} = fd; let Inst{5-0} = funct; } class CMov_F_I_FM : StdArch { bits<5> rd; bits<5> rs; bits<3> fcc; bits<32> Inst; let Inst{31-26} = 0; let Inst{25-21} = rs; let Inst{20-18} = fcc; let Inst{17} = 0; let Inst{16} = tf; let Inst{15-11} = rd; let Inst{10-6} = 0; let Inst{5-0} = 1; } class CMov_F_F_FM fmt, bit tf> : StdArch { bits<5> fd; bits<5> fs; bits<3> fcc; bits<32> Inst; let Inst{31-26} = 0x11; let Inst{25-21} = fmt; let Inst{20-18} = fcc; let Inst{17} = 0; let Inst{16} = tf; let Inst{15-11} = fs; let Inst{10-6} = fd; let Inst{5-0} = 0x11; } class BARRIER_FM op> : StdArch { bits<32> Inst; let Inst{31-26} = 0; // SPECIAL let Inst{25-21} = 0; let Inst{20-16} = 0; // rt = 0 let Inst{15-11} = 0; // rd = 0 let Inst{10-6} = op; // Operation let Inst{5-0} = 0; // SLL } class SDBBP_FM : StdArch { bits<20> code_; bits<32> Inst; let Inst{31-26} = 0b011100; // SPECIAL2 let Inst{25-6} = code_; let Inst{5-0} = 0b111111; // SDBBP } class JR_HB_FM op> : StdArch{ bits<5> rs; bits<32> Inst; let Inst{31-26} = 0; // SPECIAL let Inst{25-21} = rs; let Inst{20-11} = 0; let Inst{10} = 1; let Inst{9-6} = 0; let Inst{5-0} = op; } class JALR_HB_FM op> : StdArch { bits<5> rd; bits<5> rs; bits<32> Inst; let Inst{31-26} = 0; // SPECIAL let Inst{25-21} = rs; let Inst{20-16} = 0; let Inst{15-11} = rd; let Inst{10} = 1; let Inst{9-6} = 0; let Inst{5-0} = op; } class COP0_TLB_FM op> : StdArch { bits<32> Inst; let Inst{31-26} = 0x10; // COP0 let Inst{25} = 1; // CO let Inst{24-6} = 0; let Inst{5-0} = op; // Operation } class CACHEOP_FM op> : StdArch { bits<21> addr; bits<5> hint; bits<5> base = addr{20-16}; bits<16> offset = addr{15-0}; bits<32> Inst; let Inst{31-26} = op; let Inst{25-21} = base; let Inst{20-16} = hint; let Inst{15-0} = offset; } class HYPCALL_FM op> : StdArch { bits<10> code_; bits<32> Inst; let Inst{31-26} = 0b010000; let Inst{25} = 1; let Inst{20-11} = code_; let Inst{5-0} = op; } Index: head/contrib/llvm/lib/Target/Mips/MipsInstrInfo.td =================================================================== --- head/contrib/llvm/lib/Target/Mips/MipsInstrInfo.td (revision 354978) +++ head/contrib/llvm/lib/Target/Mips/MipsInstrInfo.td (revision 354979) @@ -1,3371 +1,3383 @@ //===- MipsInstrInfo.td - Target Description for Mips Target -*- tablegen -*-=// // // 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 // //===----------------------------------------------------------------------===// // // This file contains the Mips implementation of the TargetInstrInfo class. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// // Mips profiles and nodes //===----------------------------------------------------------------------===// def SDT_MipsJmpLink : SDTypeProfile<0, 1, [SDTCisVT<0, iPTR>]>; def SDT_MipsCMov : SDTypeProfile<1, 4, [SDTCisSameAs<0, 1>, SDTCisSameAs<1, 2>, SDTCisSameAs<3, 4>, SDTCisInt<4>]>; def SDT_MipsCallSeqStart : SDCallSeqStart<[SDTCisVT<0, i32>, SDTCisVT<1, i32>]>; def SDT_MipsCallSeqEnd : SDCallSeqEnd<[SDTCisVT<0, i32>, SDTCisVT<1, i32>]>; def SDT_MFLOHI : SDTypeProfile<1, 1, [SDTCisInt<0>, SDTCisVT<1, untyped>]>; def SDT_MTLOHI : SDTypeProfile<1, 2, [SDTCisVT<0, untyped>, SDTCisInt<1>, SDTCisSameAs<1, 2>]>; def SDT_MipsMultDiv : SDTypeProfile<1, 2, [SDTCisVT<0, untyped>, SDTCisInt<1>, SDTCisSameAs<1, 2>]>; def SDT_MipsMAddMSub : SDTypeProfile<1, 3, [SDTCisVT<0, untyped>, SDTCisSameAs<0, 3>, SDTCisVT<1, i32>, SDTCisSameAs<1, 2>]>; def SDT_MipsDivRem16 : SDTypeProfile<0, 2, [SDTCisInt<0>, SDTCisSameAs<0, 1>]>; def SDT_MipsThreadPointer : SDTypeProfile<1, 0, [SDTCisPtrTy<0>]>; def SDT_Sync : SDTypeProfile<0, 1, [SDTCisVT<0, i32>]>; def SDT_Ext : SDTypeProfile<1, 3, [SDTCisInt<0>, SDTCisSameAs<0, 1>, SDTCisVT<2, i32>, SDTCisSameAs<2, 3>]>; def SDT_Ins : SDTypeProfile<1, 4, [SDTCisInt<0>, SDTCisSameAs<0, 1>, SDTCisVT<2, i32>, SDTCisSameAs<2, 3>, SDTCisSameAs<0, 4>]>; def SDTMipsLoadLR : SDTypeProfile<1, 2, [SDTCisInt<0>, SDTCisPtrTy<1>, SDTCisSameAs<0, 2>]>; // Call def MipsJmpLink : SDNode<"MipsISD::JmpLink",SDT_MipsJmpLink, [SDNPHasChain, SDNPOutGlue, SDNPOptInGlue, SDNPVariadic]>; // Tail call def MipsTailCall : SDNode<"MipsISD::TailCall", SDT_MipsJmpLink, [SDNPHasChain, SDNPOptInGlue, SDNPVariadic]>; // Hi and Lo nodes are used to handle global addresses. Used on // MipsISelLowering to lower stuff like GlobalAddress, ExternalSymbol // static model. (nothing to do with Mips Registers Hi and Lo) // Hi is the odd node out, on MIPS64 it can expand to either daddiu when // using static relocations with 64 bit symbols, or lui when using 32 bit // symbols. def MipsHigher : SDNode<"MipsISD::Higher", SDTIntUnaryOp>; def MipsHighest : SDNode<"MipsISD::Highest", SDTIntUnaryOp>; def MipsHi : SDNode<"MipsISD::Hi", SDTIntUnaryOp>; def MipsLo : SDNode<"MipsISD::Lo", SDTIntUnaryOp>; def MipsGPRel : SDNode<"MipsISD::GPRel", SDTIntUnaryOp>; // Hi node for accessing the GOT. def MipsGotHi : SDNode<"MipsISD::GotHi", SDTIntUnaryOp>; // Hi node for handling TLS offsets def MipsTlsHi : SDNode<"MipsISD::TlsHi", SDTIntUnaryOp>; // Thread pointer def MipsThreadPointer: SDNode<"MipsISD::ThreadPointer", SDT_MipsThreadPointer>; // Return def MipsRet : SDNode<"MipsISD::Ret", SDTNone, [SDNPHasChain, SDNPOptInGlue, SDNPVariadic]>; def MipsERet : SDNode<"MipsISD::ERet", SDTNone, [SDNPHasChain, SDNPOptInGlue, SDNPSideEffect]>; // These are target-independent nodes, but have target-specific formats. def callseq_start : SDNode<"ISD::CALLSEQ_START", SDT_MipsCallSeqStart, [SDNPHasChain, SDNPSideEffect, SDNPOutGlue]>; def callseq_end : SDNode<"ISD::CALLSEQ_END", SDT_MipsCallSeqEnd, [SDNPHasChain, SDNPSideEffect, SDNPOptInGlue, SDNPOutGlue]>; // Nodes used to extract LO/HI registers. def MipsMFHI : SDNode<"MipsISD::MFHI", SDT_MFLOHI>; def MipsMFLO : SDNode<"MipsISD::MFLO", SDT_MFLOHI>; // Node used to insert 32-bit integers to LOHI register pair. def MipsMTLOHI : SDNode<"MipsISD::MTLOHI", SDT_MTLOHI>; // Mult nodes. def MipsMult : SDNode<"MipsISD::Mult", SDT_MipsMultDiv>; def MipsMultu : SDNode<"MipsISD::Multu", SDT_MipsMultDiv>; // MAdd*/MSub* nodes def MipsMAdd : SDNode<"MipsISD::MAdd", SDT_MipsMAddMSub>; def MipsMAddu : SDNode<"MipsISD::MAddu", SDT_MipsMAddMSub>; def MipsMSub : SDNode<"MipsISD::MSub", SDT_MipsMAddMSub>; def MipsMSubu : SDNode<"MipsISD::MSubu", SDT_MipsMAddMSub>; // DivRem(u) nodes def MipsDivRem : SDNode<"MipsISD::DivRem", SDT_MipsMultDiv>; def MipsDivRemU : SDNode<"MipsISD::DivRemU", SDT_MipsMultDiv>; def MipsDivRem16 : SDNode<"MipsISD::DivRem16", SDT_MipsDivRem16, [SDNPOutGlue]>; def MipsDivRemU16 : SDNode<"MipsISD::DivRemU16", SDT_MipsDivRem16, [SDNPOutGlue]>; // Target constant nodes that are not part of any isel patterns and remain // unchanged can cause instructions with illegal operands to be emitted. // Wrapper node patterns give the instruction selector a chance to replace // target constant nodes that would otherwise remain unchanged with ADDiu // nodes. Without these wrapper node patterns, the following conditional move // instruction is emitted when function cmov2 in test/CodeGen/Mips/cmov.ll is // compiled: // movn %got(d)($gp), %got(c)($gp), $4 // This instruction is illegal since movn can take only register operands. def MipsWrapper : SDNode<"MipsISD::Wrapper", SDTIntBinOp>; def MipsSync : SDNode<"MipsISD::Sync", SDT_Sync, [SDNPHasChain,SDNPSideEffect]>; def MipsExt : SDNode<"MipsISD::Ext", SDT_Ext>; def MipsIns : SDNode<"MipsISD::Ins", SDT_Ins>; def MipsCIns : SDNode<"MipsISD::CIns", SDT_Ext>; def MipsLWL : SDNode<"MipsISD::LWL", SDTMipsLoadLR, [SDNPHasChain, SDNPMayLoad, SDNPMemOperand]>; def MipsLWR : SDNode<"MipsISD::LWR", SDTMipsLoadLR, [SDNPHasChain, SDNPMayLoad, SDNPMemOperand]>; def MipsSWL : SDNode<"MipsISD::SWL", SDTStore, [SDNPHasChain, SDNPMayStore, SDNPMemOperand]>; def MipsSWR : SDNode<"MipsISD::SWR", SDTStore, [SDNPHasChain, SDNPMayStore, SDNPMemOperand]>; def MipsLDL : SDNode<"MipsISD::LDL", SDTMipsLoadLR, [SDNPHasChain, SDNPMayLoad, SDNPMemOperand]>; def MipsLDR : SDNode<"MipsISD::LDR", SDTMipsLoadLR, [SDNPHasChain, SDNPMayLoad, SDNPMemOperand]>; def MipsSDL : SDNode<"MipsISD::SDL", SDTStore, [SDNPHasChain, SDNPMayStore, SDNPMemOperand]>; def MipsSDR : SDNode<"MipsISD::SDR", SDTStore, [SDNPHasChain, SDNPMayStore, SDNPMemOperand]>; //===----------------------------------------------------------------------===// // Mips Instruction Predicate Definitions. //===----------------------------------------------------------------------===// def HasMips2 : Predicate<"Subtarget->hasMips2()">, AssemblerPredicate<"FeatureMips2">; def HasMips3_32 : Predicate<"Subtarget->hasMips3_32()">, AssemblerPredicate<"FeatureMips3_32">; def HasMips3_32r2 : Predicate<"Subtarget->hasMips3_32r2()">, AssemblerPredicate<"FeatureMips3_32r2">; def HasMips3 : Predicate<"Subtarget->hasMips3()">, AssemblerPredicate<"FeatureMips3">; def NotMips3 : Predicate<"!Subtarget->hasMips3()">, AssemblerPredicate<"!FeatureMips3">; def HasMips4_32 : Predicate<"Subtarget->hasMips4_32()">, AssemblerPredicate<"FeatureMips4_32">; def NotMips4_32 : Predicate<"!Subtarget->hasMips4_32()">, AssemblerPredicate<"!FeatureMips4_32">; def HasMips4_32r2 : Predicate<"Subtarget->hasMips4_32r2()">, AssemblerPredicate<"FeatureMips4_32r2">; def HasMips5_32r2 : Predicate<"Subtarget->hasMips5_32r2()">, AssemblerPredicate<"FeatureMips5_32r2">; def HasMips32 : Predicate<"Subtarget->hasMips32()">, AssemblerPredicate<"FeatureMips32">; def HasMips32r2 : Predicate<"Subtarget->hasMips32r2()">, AssemblerPredicate<"FeatureMips32r2">; def HasMips32r5 : Predicate<"Subtarget->hasMips32r5()">, AssemblerPredicate<"FeatureMips32r5">; def HasMips32r6 : Predicate<"Subtarget->hasMips32r6()">, AssemblerPredicate<"FeatureMips32r6">; def NotMips32r6 : Predicate<"!Subtarget->hasMips32r6()">, AssemblerPredicate<"!FeatureMips32r6">; def IsGP64bit : Predicate<"Subtarget->isGP64bit()">, AssemblerPredicate<"FeatureGP64Bit">; def IsGP32bit : Predicate<"!Subtarget->isGP64bit()">, AssemblerPredicate<"!FeatureGP64Bit">; def IsPTR64bit : Predicate<"Subtarget->isABI_N64()">, AssemblerPredicate<"FeaturePTR64Bit">; def IsPTR32bit : Predicate<"!Subtarget->isABI_N64()">, AssemblerPredicate<"!FeaturePTR64Bit">; def HasMips64 : Predicate<"Subtarget->hasMips64()">, AssemblerPredicate<"FeatureMips64">; def NotMips64 : Predicate<"!Subtarget->hasMips64()">, AssemblerPredicate<"!FeatureMips64">; def HasMips64r2 : Predicate<"Subtarget->hasMips64r2()">, AssemblerPredicate<"FeatureMips64r2">; def HasMips64r5 : Predicate<"Subtarget->hasMips64r5()">, AssemblerPredicate<"FeatureMips64r5">; def HasMips64r6 : Predicate<"Subtarget->hasMips64r6()">, AssemblerPredicate<"FeatureMips64r6">; def NotMips64r6 : Predicate<"!Subtarget->hasMips64r6()">, AssemblerPredicate<"!FeatureMips64r6">; def InMips16Mode : Predicate<"Subtarget->inMips16Mode()">, AssemblerPredicate<"FeatureMips16">; def NotInMips16Mode : Predicate<"!Subtarget->inMips16Mode()">, AssemblerPredicate<"!FeatureMips16">; def HasCnMips : Predicate<"Subtarget->hasCnMips()">, AssemblerPredicate<"FeatureCnMips">; def NotCnMips : Predicate<"!Subtarget->hasCnMips()">, AssemblerPredicate<"!FeatureCnMips">; +def HasCnMipsP : Predicate<"Subtarget->hasCnMipsP()">, + AssemblerPredicate<"FeatureCnMipsP">; +def NotCnMipsP : Predicate<"!Subtarget->hasCnMipsP()">, + AssemblerPredicate<"!FeatureCnMipsP">; def IsSym32 : Predicate<"Subtarget->HasSym32()">, AssemblerPredicate<"FeatureSym32">; def IsSym64 : Predicate<"!Subtarget->HasSym32()">, AssemblerPredicate<"!FeatureSym32">; def IsN64 : Predicate<"Subtarget->isABI_N64()">; def IsNotN64 : Predicate<"!Subtarget->isABI_N64()">; def RelocNotPIC : Predicate<"!TM.isPositionIndependent()">; def RelocPIC : Predicate<"TM.isPositionIndependent()">; def NoNaNsFPMath : Predicate<"TM.Options.NoNaNsFPMath">; def UseAbs : Predicate<"Subtarget->inAbs2008Mode() ||" "TM.Options.NoNaNsFPMath">; def HasStdEnc : Predicate<"Subtarget->hasStandardEncoding()">, AssemblerPredicate<"!FeatureMips16">; def NotDSP : Predicate<"!Subtarget->hasDSP()">; def InMicroMips : Predicate<"Subtarget->inMicroMipsMode()">, AssemblerPredicate<"FeatureMicroMips">; def NotInMicroMips : Predicate<"!Subtarget->inMicroMipsMode()">, AssemblerPredicate<"!FeatureMicroMips">; def IsLE : Predicate<"Subtarget->isLittle()">; def IsBE : Predicate<"!Subtarget->isLittle()">; def IsNotNaCl : Predicate<"!Subtarget->isTargetNaCl()">; def UseTCCInDIV : AssemblerPredicate<"FeatureUseTCCInDIV">; def HasEVA : Predicate<"Subtarget->hasEVA()">, AssemblerPredicate<"FeatureEVA">; def HasMSA : Predicate<"Subtarget->hasMSA()">, AssemblerPredicate<"FeatureMSA">; def HasMadd4 : Predicate<"!Subtarget->disableMadd4()">, AssemblerPredicate<"!FeatureMadd4">; def HasMT : Predicate<"Subtarget->hasMT()">, AssemblerPredicate<"FeatureMT">; def UseIndirectJumpsHazard : Predicate<"Subtarget->useIndirectJumpsHazard()">, AssemblerPredicate<"FeatureUseIndirectJumpsHazard">; def NoIndirectJumpGuards : Predicate<"!Subtarget->useIndirectJumpsHazard()">, AssemblerPredicate<"!FeatureUseIndirectJumpsHazard">; def HasCRC : Predicate<"Subtarget->hasCRC()">, AssemblerPredicate<"FeatureCRC">; def HasVirt : Predicate<"Subtarget->hasVirt()">, AssemblerPredicate<"FeatureVirt">; def HasGINV : Predicate<"Subtarget->hasGINV()">, AssemblerPredicate<"FeatureGINV">; // TODO: Add support for FPOpFusion::Standard def AllowFPOpFusion : Predicate<"TM.Options.AllowFPOpFusion ==" " FPOpFusion::Fast">; //===----------------------------------------------------------------------===// // Mips GPR size adjectives. // They are mutually exclusive. //===----------------------------------------------------------------------===// class GPR_32 { list GPRPredicates = [IsGP32bit]; } class GPR_64 { list GPRPredicates = [IsGP64bit]; } class PTR_32 { list PTRPredicates = [IsPTR32bit]; } class PTR_64 { list PTRPredicates = [IsPTR64bit]; } //===----------------------------------------------------------------------===// // Mips Symbol size adjectives. // They are mutally exculsive. //===----------------------------------------------------------------------===// class SYM_32 { list SYMPredicates = [IsSym32]; } class SYM_64 { list SYMPredicates = [IsSym64]; } //===----------------------------------------------------------------------===// // Mips ISA/ASE membership and instruction group membership adjectives. // They are mutually exclusive. //===----------------------------------------------------------------------===// // FIXME: I'd prefer to use additive predicates to build the instruction sets // but we are short on assembler feature bits at the moment. Using a // subtractive predicate will hopefully keep us under the 32 predicate // limit long enough to develop an alternative way to handle P1||P2 // predicates. class ISA_MIPS1 { list EncodingPredicates = [HasStdEnc]; } class ISA_MIPS1_NOT_MIPS3 { list InsnPredicates = [NotMips3]; list EncodingPredicates = [HasStdEnc]; } class ISA_MIPS1_NOT_4_32 { list InsnPredicates = [NotMips4_32]; list EncodingPredicates = [HasStdEnc]; } class ISA_MIPS1_NOT_32R6_64R6 { list InsnPredicates = [NotMips32r6, NotMips64r6]; list EncodingPredicates = [HasStdEnc]; } class ISA_MIPS2 { list InsnPredicates = [HasMips2]; list EncodingPredicates = [HasStdEnc]; } class ISA_MIPS2_NOT_32R6_64R6 { list InsnPredicates = [HasMips2, NotMips32r6, NotMips64r6]; list EncodingPredicates = [HasStdEnc]; } class ISA_MIPS3 { list InsnPredicates = [HasMips3]; list EncodingPredicates = [HasStdEnc]; } class ISA_MIPS3_NOT_32R6_64R6 { list InsnPredicates = [HasMips3, NotMips32r6, NotMips64r6]; list EncodingPredicates = [HasStdEnc]; } class ISA_MIPS32 { list InsnPredicates = [HasMips32]; list EncodingPredicates = [HasStdEnc]; } class ISA_MIPS32_NOT_32R6_64R6 { list InsnPredicates = [HasMips32, NotMips32r6, NotMips64r6]; list EncodingPredicates = [HasStdEnc]; } class ISA_MIPS32R2 { list InsnPredicates = [HasMips32r2]; list EncodingPredicates = [HasStdEnc]; } class ISA_MIPS32R2_NOT_32R6_64R6 { list InsnPredicates = [HasMips32r2, NotMips32r6, NotMips64r6]; list EncodingPredicates = [HasStdEnc]; } class ISA_MIPS32R5 { list InsnPredicates = [HasMips32r5]; list EncodingPredicates = [HasStdEnc]; } class ISA_MIPS64 { list InsnPredicates = [HasMips64]; list EncodingPredicates = [HasStdEnc]; } class ISA_MIPS64_NOT_64R6 { list InsnPredicates = [HasMips64, NotMips64r6]; list EncodingPredicates = [HasStdEnc]; } class ISA_MIPS64R2 { list InsnPredicates = [HasMips64r2]; list EncodingPredicates = [HasStdEnc]; } class ISA_MIPS64R5 { list InsnPredicates = [HasMips64r5]; list EncodingPredicates = [HasStdEnc]; } class ISA_MIPS32R6 { list InsnPredicates = [HasMips32r6]; list EncodingPredicates = [HasStdEnc]; } class ISA_MIPS64R6 { list InsnPredicates = [HasMips64r6]; list EncodingPredicates = [HasStdEnc]; } class ISA_MICROMIPS { list EncodingPredicates = [InMicroMips]; } class ISA_MICROMIPS32R5 { list InsnPredicates = [HasMips32r5]; list EncodingPredicates = [InMicroMips]; } class ISA_MICROMIPS32R6 { list InsnPredicates = [HasMips32r6]; list EncodingPredicates = [InMicroMips]; } class ISA_MICROMIPS64R6 { list InsnPredicates = [HasMips64r6]; list EncodingPredicates = [InMicroMips]; } class ISA_MICROMIPS32_NOT_MIPS32R6 { list InsnPredicates = [NotMips32r6]; list EncodingPredicates = [InMicroMips]; } class ASE_EVA { list ASEPredicate = [HasEVA]; } // The portions of MIPS-III that were also added to MIPS32 class INSN_MIPS3_32 { list InsnPredicates = [HasMips3_32]; list EncodingPredicates = [HasStdEnc]; } // The portions of MIPS-III that were also added to MIPS32 but were removed in // MIPS32r6 and MIPS64r6. class INSN_MIPS3_32_NOT_32R6_64R6 { list InsnPredicates = [HasMips3_32, NotMips32r6, NotMips64r6]; list EncodingPredicates = [HasStdEnc]; } // The portions of MIPS-III that were also added to MIPS32 class INSN_MIPS3_32R2 { list InsnPredicates = [HasMips3_32r2]; list EncodingPredicates = [HasStdEnc]; } // The portions of MIPS-IV that were also added to MIPS32. class INSN_MIPS4_32 { list InsnPredicates = [HasMips4_32]; list EncodingPredicates = [HasStdEnc]; } // The portions of MIPS-IV that were also added to MIPS32 but were removed in // MIPS32r6 and MIPS64r6. class INSN_MIPS4_32_NOT_32R6_64R6 { list InsnPredicates = [HasMips4_32, NotMips32r6, NotMips64r6]; list EncodingPredicates = [HasStdEnc]; } // The portions of MIPS-IV that were also added to MIPS32r2 but were removed in // MIPS32r6 and MIPS64r6. class INSN_MIPS4_32R2_NOT_32R6_64R6 { list InsnPredicates = [HasMips4_32r2, NotMips32r6, NotMips64r6]; list EncodingPredicates = [HasStdEnc]; } // The portions of MIPS-IV that were also added to MIPS32r2. class INSN_MIPS4_32R2 { list InsnPredicates = [HasMips4_32r2]; list EncodingPredicates = [HasStdEnc]; } // The portions of MIPS-V that were also added to MIPS32r2 but were removed in // MIPS32r6 and MIPS64r6. class INSN_MIPS5_32R2_NOT_32R6_64R6 { list InsnPredicates = [HasMips5_32r2, NotMips32r6, NotMips64r6]; list EncodingPredicates = [HasStdEnc]; } class ASE_CNMIPS { list ASEPredicate = [HasCnMips]; } class NOT_ASE_CNMIPS { list ASEPredicate = [NotCnMips]; +} + +class ASE_CNMIPSP { + list ASEPredicate = [HasCnMipsP]; +} + +class NOT_ASE_CNMIPSP { + list ASEPredicate = [NotCnMipsP]; } class ASE_MIPS64_CNMIPS { list ASEPredicate = [HasMips64, HasCnMips]; } class ASE_MSA { list ASEPredicate = [HasMSA]; } class ASE_MSA_NOT_MSA64 { list ASEPredicate = [HasMSA, NotMips64]; } class ASE_MSA64 { list ASEPredicate = [HasMSA, HasMips64]; } class ASE_MT { list ASEPredicate = [HasMT]; } class ASE_CRC { list ASEPredicate = [HasCRC]; } class ASE_VIRT { list ASEPredicate = [HasVirt]; } class ASE_GINV { list ASEPredicate = [HasGINV]; } // Class used for separating microMIPSr6 and microMIPS (r3) instruction. // It can be used only on instructions that doesn't inherit PredicateControl. class ISA_MICROMIPS_NOT_32R6 : PredicateControl { let InsnPredicates = [NotMips32r6]; let EncodingPredicates = [InMicroMips]; } class ASE_NOT_DSP { list ASEPredicate = [NotDSP]; } class MADD4 { list AdditionalPredicates = [HasMadd4]; } // Classses used for separating expansions that differ based on the ABI in // use. class ABI_N64 { list AdditionalPredicates = [IsN64]; } class ABI_NOT_N64 { list AdditionalPredicates = [IsNotN64]; } class FPOP_FUSION_FAST { list AdditionalPredicates = [AllowFPOpFusion]; } //===----------------------------------------------------------------------===// class MipsPat : Pat, PredicateControl; class MipsInstAlias : InstAlias, PredicateControl; class IsCommutable { bit isCommutable = 1; } class IsBranch { bit isBranch = 1; bit isCTI = 1; } class IsReturn { bit isReturn = 1; bit isCTI = 1; } class IsCall { bit isCall = 1; bit isCTI = 1; } class IsTailCall { bit isCall = 1; bit isTerminator = 1; bit isReturn = 1; bit isBarrier = 1; bit hasExtraSrcRegAllocReq = 1; bit isCodeGenOnly = 1; bit isCTI = 1; } class IsAsCheapAsAMove { bit isAsCheapAsAMove = 1; } class NeverHasSideEffects { bit hasSideEffects = 0; } //===----------------------------------------------------------------------===// // Instruction format superclass //===----------------------------------------------------------------------===// include "MipsInstrFormats.td" //===----------------------------------------------------------------------===// // Mips Operand, Complex Patterns and Transformations Definitions. //===----------------------------------------------------------------------===// class ConstantSImmAsmOperandClass Supers = [], int Offset = 0> : AsmOperandClass { let Name = "ConstantSImm" # Bits # "_" # Offset; let RenderMethod = "addConstantSImmOperands<" # Bits # ", " # Offset # ">"; let PredicateMethod = "isConstantSImm<" # Bits # ", " # Offset # ">"; let SuperClasses = Supers; let DiagnosticType = "SImm" # Bits # "_" # Offset; } class SimmLslAsmOperandClass Supers = [], int Shift = 0> : AsmOperandClass { let Name = "Simm" # Bits # "_Lsl" # Shift; let RenderMethod = "addImmOperands"; let PredicateMethod = "isScaledSImm<" # Bits # ", " # Shift # ">"; let SuperClasses = Supers; let DiagnosticType = "SImm" # Bits # "_Lsl" # Shift; } class ConstantUImmAsmOperandClass Supers = [], int Offset = 0> : AsmOperandClass { let Name = "ConstantUImm" # Bits # "_" # Offset; let RenderMethod = "addConstantUImmOperands<" # Bits # ", " # Offset # ">"; let PredicateMethod = "isConstantUImm<" # Bits # ", " # Offset # ">"; let SuperClasses = Supers; let DiagnosticType = "UImm" # Bits # "_" # Offset; } class ConstantUImmRangeAsmOperandClass Supers = []> : AsmOperandClass { let Name = "ConstantUImmRange" # Bottom # "_" # Top; let RenderMethod = "addImmOperands"; let PredicateMethod = "isConstantUImmRange<" # Bottom # ", " # Top # ">"; let SuperClasses = Supers; let DiagnosticType = "UImmRange" # Bottom # "_" # Top; } class SImmAsmOperandClass Supers = []> : AsmOperandClass { let Name = "SImm" # Bits; let RenderMethod = "addSImmOperands<" # Bits # ">"; let PredicateMethod = "isSImm<" # Bits # ">"; let SuperClasses = Supers; let DiagnosticType = "SImm" # Bits; } class UImmAsmOperandClass Supers = []> : AsmOperandClass { let Name = "UImm" # Bits; let RenderMethod = "addUImmOperands<" # Bits # ">"; let PredicateMethod = "isUImm<" # Bits # ">"; let SuperClasses = Supers; let DiagnosticType = "UImm" # Bits; } // Generic case - only to support certain assembly pseudo instructions. class UImmAnyAsmOperandClass Supers = []> : AsmOperandClass { let Name = "ImmAny"; let RenderMethod = "addConstantUImmOperands<32>"; let PredicateMethod = "isSImm<" # Bits # ">"; let SuperClasses = Supers; let DiagnosticType = "ImmAny"; } // AsmOperandClasses require a strict ordering which is difficult to manage // as a hierarchy. Instead, we use a linear ordering and impose an order that // is in some places arbitrary. // // Here the rules that are in use: // * Wider immediates are a superset of narrower immediates: // uimm4 < uimm5 < uimm6 // * For the same bit-width, unsigned immediates are a superset of signed // immediates:: // simm4 < uimm4 < simm5 < uimm5 // * For the same upper-bound, signed immediates are a superset of unsigned // immediates: // uimm3 < simm4 < uimm4 < simm4 // * Modified immediates are a superset of ordinary immediates: // uimm5 < uimm5_plus1 (1..32) < uimm5_plus32 (32..63) < uimm6 // The term 'superset' starts to break down here since the uimm5_plus* classes // are not true supersets of uimm5 (but they are still subsets of uimm6). // * 'Relaxed' immediates are supersets of the corresponding unsigned immediate. // uimm16 < uimm16_relaxed // * The codeGen pattern type is arbitrarily ordered. // uimm5 < uimm5_64, and uimm5 < vsplat_uimm5 // This is entirely arbitrary. We need an ordering and what we pick is // unimportant since only one is possible for a given mnemonic. def UImm32CoercedAsmOperandClass : UImmAnyAsmOperandClass<33, []> { let Name = "UImm32_Coerced"; let DiagnosticType = "UImm32_Coerced"; } def SImm32RelaxedAsmOperandClass : SImmAsmOperandClass<32, [UImm32CoercedAsmOperandClass]> { let Name = "SImm32_Relaxed"; let PredicateMethod = "isAnyImm<33>"; let DiagnosticType = "SImm32_Relaxed"; } def SImm32AsmOperandClass : SImmAsmOperandClass<32, [SImm32RelaxedAsmOperandClass]>; def ConstantUImm26AsmOperandClass : ConstantUImmAsmOperandClass<26, [SImm32AsmOperandClass]>; def ConstantUImm20AsmOperandClass : ConstantUImmAsmOperandClass<20, [ConstantUImm26AsmOperandClass]>; def ConstantSImm19Lsl2AsmOperandClass : AsmOperandClass { let Name = "SImm19Lsl2"; let RenderMethod = "addImmOperands"; let PredicateMethod = "isScaledSImm<19, 2>"; let SuperClasses = [ConstantUImm20AsmOperandClass]; let DiagnosticType = "SImm19_Lsl2"; } def UImm16RelaxedAsmOperandClass : UImmAsmOperandClass<16, [ConstantUImm20AsmOperandClass]> { let Name = "UImm16_Relaxed"; let PredicateMethod = "isAnyImm<16>"; let DiagnosticType = "UImm16_Relaxed"; } // Similar to the relaxed classes which take an SImm and render it as // an UImm, this takes a UImm and renders it as an SImm. def UImm16AltRelaxedAsmOperandClass : SImmAsmOperandClass<16, [UImm16RelaxedAsmOperandClass]> { let Name = "UImm16_AltRelaxed"; let PredicateMethod = "isUImm<16>"; let DiagnosticType = "UImm16_AltRelaxed"; } // FIXME: One of these should probably have UImm16AsmOperandClass as the // superclass instead of UImm16RelaxedasmOPerandClass. def UImm16AsmOperandClass : UImmAsmOperandClass<16, [UImm16RelaxedAsmOperandClass]>; def SImm16RelaxedAsmOperandClass : SImmAsmOperandClass<16, [UImm16RelaxedAsmOperandClass]> { let Name = "SImm16_Relaxed"; let PredicateMethod = "isAnyImm<16>"; let DiagnosticType = "SImm16_Relaxed"; } def SImm16AsmOperandClass : SImmAsmOperandClass<16, [SImm16RelaxedAsmOperandClass]>; def ConstantSImm10Lsl3AsmOperandClass : AsmOperandClass { let Name = "SImm10Lsl3"; let RenderMethod = "addImmOperands"; let PredicateMethod = "isScaledSImm<10, 3>"; let SuperClasses = [SImm16AsmOperandClass]; let DiagnosticType = "SImm10_Lsl3"; } def ConstantSImm10Lsl2AsmOperandClass : AsmOperandClass { let Name = "SImm10Lsl2"; let RenderMethod = "addImmOperands"; let PredicateMethod = "isScaledSImm<10, 2>"; let SuperClasses = [ConstantSImm10Lsl3AsmOperandClass]; let DiagnosticType = "SImm10_Lsl2"; } def ConstantSImm11AsmOperandClass : ConstantSImmAsmOperandClass<11, [ConstantSImm10Lsl2AsmOperandClass]>; def ConstantSImm10Lsl1AsmOperandClass : AsmOperandClass { let Name = "SImm10Lsl1"; let RenderMethod = "addImmOperands"; let PredicateMethod = "isScaledSImm<10, 1>"; let SuperClasses = [ConstantSImm11AsmOperandClass]; let DiagnosticType = "SImm10_Lsl1"; } def ConstantUImm10AsmOperandClass : ConstantUImmAsmOperandClass<10, [ConstantSImm10Lsl1AsmOperandClass]>; def ConstantSImm10AsmOperandClass : ConstantSImmAsmOperandClass<10, [ConstantUImm10AsmOperandClass]>; def ConstantSImm9AsmOperandClass : ConstantSImmAsmOperandClass<9, [ConstantSImm10AsmOperandClass]>; def ConstantSImm7Lsl2AsmOperandClass : AsmOperandClass { let Name = "SImm7Lsl2"; let RenderMethod = "addImmOperands"; let PredicateMethod = "isScaledSImm<7, 2>"; let SuperClasses = [ConstantSImm9AsmOperandClass]; let DiagnosticType = "SImm7_Lsl2"; } def ConstantUImm8AsmOperandClass : ConstantUImmAsmOperandClass<8, [ConstantSImm7Lsl2AsmOperandClass]>; def ConstantUImm7Sub1AsmOperandClass : ConstantUImmAsmOperandClass<7, [ConstantUImm8AsmOperandClass], -1> { // Specify the names since the -1 offset causes invalid identifiers otherwise. let Name = "UImm7_N1"; let DiagnosticType = "UImm7_N1"; } def ConstantUImm7AsmOperandClass : ConstantUImmAsmOperandClass<7, [ConstantUImm7Sub1AsmOperandClass]>; def ConstantUImm6Lsl2AsmOperandClass : AsmOperandClass { let Name = "UImm6Lsl2"; let RenderMethod = "addImmOperands"; let PredicateMethod = "isScaledUImm<6, 2>"; let SuperClasses = [ConstantUImm7AsmOperandClass]; let DiagnosticType = "UImm6_Lsl2"; } def ConstantUImm6AsmOperandClass : ConstantUImmAsmOperandClass<6, [ConstantUImm6Lsl2AsmOperandClass]>; def ConstantSImm6AsmOperandClass : ConstantSImmAsmOperandClass<6, [ConstantUImm6AsmOperandClass]>; def ConstantUImm5Lsl2AsmOperandClass : AsmOperandClass { let Name = "UImm5Lsl2"; let RenderMethod = "addImmOperands"; let PredicateMethod = "isScaledUImm<5, 2>"; let SuperClasses = [ConstantSImm6AsmOperandClass]; let DiagnosticType = "UImm5_Lsl2"; } def ConstantUImm5_Range2_64AsmOperandClass : ConstantUImmRangeAsmOperandClass<2, 64, [ConstantUImm5Lsl2AsmOperandClass]>; def ConstantUImm5Plus33AsmOperandClass : ConstantUImmAsmOperandClass<5, [ConstantUImm5_Range2_64AsmOperandClass], 33>; def ConstantUImm5ReportUImm6AsmOperandClass : ConstantUImmAsmOperandClass<5, [ConstantUImm5Plus33AsmOperandClass]> { let Name = "ConstantUImm5_0_Report_UImm6"; let DiagnosticType = "UImm5_0_Report_UImm6"; } def ConstantUImm5Plus32AsmOperandClass : ConstantUImmAsmOperandClass< 5, [ConstantUImm5ReportUImm6AsmOperandClass], 32>; def ConstantUImm5Plus32NormalizeAsmOperandClass : ConstantUImmAsmOperandClass<5, [ConstantUImm5Plus32AsmOperandClass], 32> { let Name = "ConstantUImm5_32_Norm"; // We must also subtract 32 when we render the operand. let RenderMethod = "addConstantUImmOperands<5, 32, -32>"; } def ConstantUImm5Plus1ReportUImm6AsmOperandClass : ConstantUImmAsmOperandClass< 5, [ConstantUImm5Plus32NormalizeAsmOperandClass], 1>{ let Name = "ConstantUImm5_Plus1_Report_UImm6"; } def ConstantUImm5Plus1AsmOperandClass : ConstantUImmAsmOperandClass< 5, [ConstantUImm5Plus1ReportUImm6AsmOperandClass], 1>; def ConstantUImm5AsmOperandClass : ConstantUImmAsmOperandClass<5, [ConstantUImm5Plus1AsmOperandClass]>; def ConstantSImm5AsmOperandClass : ConstantSImmAsmOperandClass<5, [ConstantUImm5AsmOperandClass]>; def ConstantUImm4AsmOperandClass : ConstantUImmAsmOperandClass<4, [ConstantSImm5AsmOperandClass]>; def ConstantSImm4AsmOperandClass : ConstantSImmAsmOperandClass<4, [ConstantUImm4AsmOperandClass]>; def ConstantUImm3AsmOperandClass : ConstantUImmAsmOperandClass<3, [ConstantSImm4AsmOperandClass]>; def ConstantUImm2Plus1AsmOperandClass : ConstantUImmAsmOperandClass<2, [ConstantUImm3AsmOperandClass], 1>; def ConstantUImm2AsmOperandClass : ConstantUImmAsmOperandClass<2, [ConstantUImm3AsmOperandClass]>; def ConstantUImm1AsmOperandClass : ConstantUImmAsmOperandClass<1, [ConstantUImm2AsmOperandClass]>; def ConstantImmzAsmOperandClass : AsmOperandClass { let Name = "ConstantImmz"; let RenderMethod = "addConstantUImmOperands<1>"; let PredicateMethod = "isConstantImmz"; let SuperClasses = [ConstantUImm1AsmOperandClass]; let DiagnosticType = "Immz"; } def Simm19Lsl2AsmOperand : SimmLslAsmOperandClass<19, [], 2>; def MipsJumpTargetAsmOperand : AsmOperandClass { let Name = "JumpTarget"; let ParserMethod = "parseJumpTarget"; let PredicateMethod = "isImm"; let RenderMethod = "addImmOperands"; } // Instruction operand types def jmptarget : Operand { let EncoderMethod = "getJumpTargetOpValue"; let ParserMatchClass = MipsJumpTargetAsmOperand; } def brtarget : Operand { let EncoderMethod = "getBranchTargetOpValue"; let OperandType = "OPERAND_PCREL"; let DecoderMethod = "DecodeBranchTarget"; let ParserMatchClass = MipsJumpTargetAsmOperand; } def brtarget1SImm16 : Operand { let EncoderMethod = "getBranchTargetOpValue1SImm16"; let OperandType = "OPERAND_PCREL"; let DecoderMethod = "DecodeBranchTarget1SImm16"; let ParserMatchClass = MipsJumpTargetAsmOperand; } def calltarget : Operand { let EncoderMethod = "getJumpTargetOpValue"; let ParserMatchClass = MipsJumpTargetAsmOperand; } def imm64: Operand; def simm19_lsl2 : Operand { let EncoderMethod = "getSimm19Lsl2Encoding"; let DecoderMethod = "DecodeSimm19Lsl2"; let ParserMatchClass = Simm19Lsl2AsmOperand; } def simm18_lsl3 : Operand { let EncoderMethod = "getSimm18Lsl3Encoding"; let DecoderMethod = "DecodeSimm18Lsl3"; let ParserMatchClass = MipsJumpTargetAsmOperand; } // Zero def uimmz : Operand { let PrintMethod = "printUImm<0>"; let ParserMatchClass = ConstantImmzAsmOperandClass; } // size operand of ins instruction def uimm_range_2_64 : Operand { let PrintMethod = "printUImm<6, 2>"; let EncoderMethod = "getSizeInsEncoding"; let DecoderMethod = "DecodeInsSize"; let ParserMatchClass = ConstantUImm5_Range2_64AsmOperandClass; } // Unsigned Operands foreach I = {1, 2, 3, 4, 5, 6, 7, 8, 10, 20, 26} in def uimm # I : Operand { let PrintMethod = "printUImm<" # I # ">"; let ParserMatchClass = !cast("ConstantUImm" # I # "AsmOperandClass"); } def uimm2_plus1 : Operand { let PrintMethod = "printUImm<2, 1>"; let EncoderMethod = "getUImmWithOffsetEncoding<2, 1>"; let DecoderMethod = "DecodeUImmWithOffset<2, 1>"; let ParserMatchClass = ConstantUImm2Plus1AsmOperandClass; } def uimm5_plus1 : Operand { let PrintMethod = "printUImm<5, 1>"; let EncoderMethod = "getUImmWithOffsetEncoding<5, 1>"; let DecoderMethod = "DecodeUImmWithOffset<5, 1>"; let ParserMatchClass = ConstantUImm5Plus1AsmOperandClass; } def uimm5_plus1_report_uimm6 : Operand { let PrintMethod = "printUImm<6, 1>"; let EncoderMethod = "getUImmWithOffsetEncoding<5, 1>"; let DecoderMethod = "DecodeUImmWithOffset<5, 1>"; let ParserMatchClass = ConstantUImm5Plus1ReportUImm6AsmOperandClass; } def uimm5_plus32 : Operand { let PrintMethod = "printUImm<5, 32>"; let ParserMatchClass = ConstantUImm5Plus32AsmOperandClass; } def uimm5_plus33 : Operand { let PrintMethod = "printUImm<5, 33>"; let EncoderMethod = "getUImmWithOffsetEncoding<5, 1>"; let DecoderMethod = "DecodeUImmWithOffset<5, 1>"; let ParserMatchClass = ConstantUImm5Plus33AsmOperandClass; } def uimm5_inssize_plus1 : Operand { let PrintMethod = "printUImm<6>"; let ParserMatchClass = ConstantUImm5Plus1AsmOperandClass; let EncoderMethod = "getSizeInsEncoding"; let DecoderMethod = "DecodeInsSize"; } def uimm5_plus32_normalize : Operand { let PrintMethod = "printUImm<5>"; let ParserMatchClass = ConstantUImm5Plus32NormalizeAsmOperandClass; } def uimm5_lsl2 : Operand { let EncoderMethod = "getUImm5Lsl2Encoding"; let DecoderMethod = "DecodeUImmWithOffsetAndScale<5, 0, 4>"; let ParserMatchClass = ConstantUImm5Lsl2AsmOperandClass; } def uimm5_plus32_normalize_64 : Operand { let PrintMethod = "printUImm<5>"; let ParserMatchClass = ConstantUImm5Plus32NormalizeAsmOperandClass; } def uimm6_lsl2 : Operand { let EncoderMethod = "getUImm6Lsl2Encoding"; let DecoderMethod = "DecodeUImmWithOffsetAndScale<6, 0, 4>"; let ParserMatchClass = ConstantUImm6Lsl2AsmOperandClass; } foreach I = {16} in def uimm # I : Operand { let PrintMethod = "printUImm<" # I # ">"; let ParserMatchClass = !cast("UImm" # I # "AsmOperandClass"); } // Like uimm16_64 but coerces simm16 to uimm16. def uimm16_relaxed : Operand { let PrintMethod = "printUImm<16>"; let ParserMatchClass = !cast("UImm16RelaxedAsmOperandClass"); } foreach I = {5} in def uimm # I # _64 : Operand { let PrintMethod = "printUImm<" # I # ">"; let ParserMatchClass = !cast("ConstantUImm" # I # "AsmOperandClass"); } foreach I = {16} in def uimm # I # _64 : Operand { let PrintMethod = "printUImm<" # I # ">"; let ParserMatchClass = !cast("UImm" # I # "AsmOperandClass"); } // Like uimm16_64 but coerces simm16 to uimm16. def uimm16_64_relaxed : Operand { let PrintMethod = "printUImm<16>"; let ParserMatchClass = !cast("UImm16RelaxedAsmOperandClass"); } def uimm16_altrelaxed : Operand { let PrintMethod = "printUImm<16>"; let ParserMatchClass = !cast("UImm16AltRelaxedAsmOperandClass"); } // Like uimm5 but reports a less confusing error for 32-63 when // an instruction alias permits that. def uimm5_report_uimm6 : Operand { let PrintMethod = "printUImm<6>"; let ParserMatchClass = ConstantUImm5ReportUImm6AsmOperandClass; } // Like uimm5_64 but reports a less confusing error for 32-63 when // an instruction alias permits that. def uimm5_64_report_uimm6 : Operand { let PrintMethod = "printUImm<5>"; let ParserMatchClass = ConstantUImm5ReportUImm6AsmOperandClass; } foreach I = {1, 2, 3, 4} in def uimm # I # _ptr : Operand { let PrintMethod = "printUImm<" # I # ">"; let ParserMatchClass = !cast("ConstantUImm" # I # "AsmOperandClass"); } foreach I = {1, 2, 3, 4, 5, 6, 8} in def vsplat_uimm # I : Operand { let PrintMethod = "printUImm<" # I # ">"; let ParserMatchClass = !cast("ConstantUImm" # I # "AsmOperandClass"); } // Signed operands foreach I = {4, 5, 6, 9, 10, 11} in def simm # I : Operand { let DecoderMethod = "DecodeSImmWithOffsetAndScale<" # I # ">"; let ParserMatchClass = !cast("ConstantSImm" # I # "AsmOperandClass"); } foreach I = {1, 2, 3} in def simm10_lsl # I : Operand { let DecoderMethod = "DecodeSImmWithOffsetAndScale<10, " # I # ">"; let ParserMatchClass = !cast("ConstantSImm10Lsl" # I # "AsmOperandClass"); } foreach I = {10} in def simm # I # _64 : Operand { let DecoderMethod = "DecodeSImmWithOffsetAndScale<" # I # ">"; let ParserMatchClass = !cast("ConstantSImm" # I # "AsmOperandClass"); } foreach I = {5, 10} in def vsplat_simm # I : Operand { let ParserMatchClass = !cast("ConstantSImm" # I # "AsmOperandClass"); } def simm7_lsl2 : Operand { let EncoderMethod = "getSImm7Lsl2Encoding"; let DecoderMethod = "DecodeSImmWithOffsetAndScale<" # I # ", 0, 4>"; let ParserMatchClass = ConstantSImm7Lsl2AsmOperandClass; } foreach I = {16, 32} in def simm # I : Operand { let DecoderMethod = "DecodeSImmWithOffsetAndScale<" # I # ">"; let ParserMatchClass = !cast("SImm" # I # "AsmOperandClass"); } // Like simm16 but coerces uimm16 to simm16. def simm16_relaxed : Operand { let DecoderMethod = "DecodeSImmWithOffsetAndScale<16>"; let ParserMatchClass = !cast("SImm16RelaxedAsmOperandClass"); } def simm16_64 : Operand { let DecoderMethod = "DecodeSImmWithOffsetAndScale<16>"; let ParserMatchClass = !cast("SImm16AsmOperandClass"); } // like simm32 but coerces simm32 to uimm32. def uimm32_coerced : Operand { let ParserMatchClass = !cast("UImm32CoercedAsmOperandClass"); } // Like simm32 but coerces uimm32 to simm32. def simm32_relaxed : Operand { let DecoderMethod = "DecodeSImmWithOffsetAndScale<32>"; let ParserMatchClass = !cast("SImm32RelaxedAsmOperandClass"); } // This is almost the same as a uimm7 but 0x7f is interpreted as -1. def li16_imm : Operand { let DecoderMethod = "DecodeLi16Imm"; let ParserMatchClass = ConstantUImm7Sub1AsmOperandClass; } def MipsMemAsmOperand : AsmOperandClass { let Name = "Mem"; let ParserMethod = "parseMemOperand"; } def MipsMemSimm9AsmOperand : AsmOperandClass { let Name = "MemOffsetSimm9"; let SuperClasses = [MipsMemAsmOperand]; let RenderMethod = "addMemOperands"; let ParserMethod = "parseMemOperand"; let PredicateMethod = "isMemWithSimmOffset<9>"; let DiagnosticType = "MemSImm9"; } def MipsMemSimm10AsmOperand : AsmOperandClass { let Name = "MemOffsetSimm10"; let SuperClasses = [MipsMemAsmOperand]; let RenderMethod = "addMemOperands"; let ParserMethod = "parseMemOperand"; let PredicateMethod = "isMemWithSimmOffset<10>"; let DiagnosticType = "MemSImm10"; } def MipsMemSimm12AsmOperand : AsmOperandClass { let Name = "MemOffsetSimm12"; let SuperClasses = [MipsMemAsmOperand]; let RenderMethod = "addMemOperands"; let ParserMethod = "parseMemOperand"; let PredicateMethod = "isMemWithSimmOffset<12>"; let DiagnosticType = "MemSImm12"; } foreach I = {1, 2, 3} in def MipsMemSimm10Lsl # I # AsmOperand : AsmOperandClass { let Name = "MemOffsetSimm10_" # I; let SuperClasses = [MipsMemAsmOperand]; let RenderMethod = "addMemOperands"; let ParserMethod = "parseMemOperand"; let PredicateMethod = "isMemWithSimmOffset<10, " # I # ">"; let DiagnosticType = "MemSImm10Lsl" # I; } def MipsMemSimm11AsmOperand : AsmOperandClass { let Name = "MemOffsetSimm11"; let SuperClasses = [MipsMemAsmOperand]; let RenderMethod = "addMemOperands"; let ParserMethod = "parseMemOperand"; let PredicateMethod = "isMemWithSimmOffset<11>"; let DiagnosticType = "MemSImm11"; } def MipsMemSimm16AsmOperand : AsmOperandClass { let Name = "MemOffsetSimm16"; let SuperClasses = [MipsMemAsmOperand]; let RenderMethod = "addMemOperands"; let ParserMethod = "parseMemOperand"; let PredicateMethod = "isMemWithSimmOffset<16>"; let DiagnosticType = "MemSImm16"; } def MipsMemSimmPtrAsmOperand : AsmOperandClass { let Name = "MemOffsetSimmPtr"; let SuperClasses = [MipsMemAsmOperand]; let RenderMethod = "addMemOperands"; let ParserMethod = "parseMemOperand"; let PredicateMethod = "isMemWithPtrSizeOffset"; let DiagnosticType = "MemSImmPtr"; } def MipsInvertedImmoperand : AsmOperandClass { let Name = "InvNum"; let RenderMethod = "addImmOperands"; let ParserMethod = "parseInvNum"; } def InvertedImOperand : Operand { let ParserMatchClass = MipsInvertedImmoperand; } def InvertedImOperand64 : Operand { let ParserMatchClass = MipsInvertedImmoperand; } class mem_generic : Operand { let PrintMethod = "printMemOperand"; let MIOperandInfo = (ops ptr_rc, simm16); let EncoderMethod = "getMemEncoding"; let ParserMatchClass = MipsMemAsmOperand; let OperandType = "OPERAND_MEMORY"; } // Address operand def mem : mem_generic; // MSA specific address operand def mem_msa : mem_generic { let MIOperandInfo = (ops ptr_rc, simm10); let EncoderMethod = "getMSAMemEncoding"; } def simm12 : Operand { let DecoderMethod = "DecodeSimm12"; } def mem_simm9 : mem_generic { let MIOperandInfo = (ops ptr_rc, simm9); let EncoderMethod = "getMemEncoding"; let ParserMatchClass = MipsMemSimm9AsmOperand; } def mem_simm10 : mem_generic { let MIOperandInfo = (ops ptr_rc, simm10); let EncoderMethod = "getMemEncoding"; let ParserMatchClass = MipsMemSimm10AsmOperand; } foreach I = {1, 2, 3} in def mem_simm10_lsl # I : mem_generic { let MIOperandInfo = (ops ptr_rc, !cast("simm10_lsl" # I)); let EncoderMethod = "getMemEncoding<" # I # ">"; let ParserMatchClass = !cast("MipsMemSimm10Lsl" # I # "AsmOperand"); } def mem_simm11 : mem_generic { let MIOperandInfo = (ops ptr_rc, simm11); let EncoderMethod = "getMemEncoding"; let ParserMatchClass = MipsMemSimm11AsmOperand; } def mem_simm12 : mem_generic { let MIOperandInfo = (ops ptr_rc, simm12); let EncoderMethod = "getMemEncoding"; let ParserMatchClass = MipsMemSimm12AsmOperand; } def mem_simm16 : mem_generic { let MIOperandInfo = (ops ptr_rc, simm16); let EncoderMethod = "getMemEncoding"; let ParserMatchClass = MipsMemSimm16AsmOperand; } def mem_simmptr : mem_generic { let ParserMatchClass = MipsMemSimmPtrAsmOperand; } def mem_ea : Operand { let PrintMethod = "printMemOperandEA"; let MIOperandInfo = (ops ptr_rc, simm16); let EncoderMethod = "getMemEncoding"; let OperandType = "OPERAND_MEMORY"; } def PtrRC : Operand { let MIOperandInfo = (ops ptr_rc); let DecoderMethod = "DecodePtrRegisterClass"; let ParserMatchClass = GPR32AsmOperand; } // size operand of ins instruction def size_ins : Operand { let EncoderMethod = "getSizeInsEncoding"; let DecoderMethod = "DecodeInsSize"; } // Transformation Function - get the lower 16 bits. def LO16 : SDNodeXFormgetZExtValue() & 0xFFFF); }]>; // Transformation Function - get the higher 16 bits. def HI16 : SDNodeXFormgetZExtValue() >> 16) & 0xFFFF); }]>; // Plus 1. def Plus1 : SDNodeXFormgetSExtValue() + 1); }]>; // Node immediate is zero (e.g. insve.d) def immz : PatLeaf<(imm), [{ return N->getSExtValue() == 0; }]>; // Node immediate fits as 16-bit sign extended on target immediate. // e.g. addi, andi def immSExt8 : PatLeaf<(imm), [{ return isInt<8>(N->getSExtValue()); }]>; // Node immediate fits as 16-bit sign extended on target immediate. // e.g. addi, andi def immSExt16 : PatLeaf<(imm), [{ return isInt<16>(N->getSExtValue()); }]>; // Node immediate fits as 7-bit zero extended on target immediate. def immZExt7 : PatLeaf<(imm), [{ return isUInt<7>(N->getZExtValue()); }]>; // Node immediate fits as 16-bit zero extended on target immediate. // The LO16 param means that only the lower 16 bits of the node // immediate are caught. // e.g. addiu, sltiu def immZExt16 : PatLeaf<(imm), [{ if (N->getValueType(0) == MVT::i32) return (uint32_t)N->getZExtValue() == (unsigned short)N->getZExtValue(); else return (uint64_t)N->getZExtValue() == (unsigned short)N->getZExtValue(); }], LO16>; // Immediate can be loaded with LUi (32-bit int with lower 16-bit cleared). def immSExt32Low16Zero : PatLeaf<(imm), [{ int64_t Val = N->getSExtValue(); return isInt<32>(Val) && !(Val & 0xffff); }]>; // Zero-extended 32-bit unsigned int with lower 16-bit cleared. def immZExt32Low16Zero : PatLeaf<(imm), [{ uint64_t Val = N->getZExtValue(); return isUInt<32>(Val) && !(Val & 0xffff); }]>; // Note immediate fits as a 32 bit signed extended on target immediate. def immSExt32 : PatLeaf<(imm), [{ return isInt<32>(N->getSExtValue()); }]>; // Note immediate fits as a 32 bit zero extended on target immediate. def immZExt32 : PatLeaf<(imm), [{ return isUInt<32>(N->getZExtValue()); }]>; // shamt field must fit in 5 bits. def immZExt5 : ImmLeaf; def immZExt5Plus1 : PatLeaf<(imm), [{ return isUInt<5>(N->getZExtValue() - 1); }]>; def immZExt5Plus32 : PatLeaf<(imm), [{ return isUInt<5>(N->getZExtValue() - 32); }]>; def immZExt5Plus33 : PatLeaf<(imm), [{ return isUInt<5>(N->getZExtValue() - 33); }]>; def immZExt5To31 : SDNodeXFormgetZExtValue()); }]>; // True if (N + 1) fits in 16-bit field. def immSExt16Plus1 : PatLeaf<(imm), [{ return isInt<17>(N->getSExtValue()) && isInt<16>(N->getSExtValue() + 1); }]>; def immZExtRange2To64 : PatLeaf<(imm), [{ return isUInt<7>(N->getZExtValue()) && (N->getZExtValue() >= 2) && (N->getZExtValue() <= 64); }]>; def ORiPred : PatLeaf<(imm), [{ return isUInt<16>(N->getZExtValue()) && !isInt<16>(N->getSExtValue()); }], LO16>; def LUiPred : PatLeaf<(imm), [{ int64_t Val = N->getSExtValue(); return !isInt<16>(Val) && isInt<32>(Val) && !(Val & 0xffff); }]>; def LUiORiPred : PatLeaf<(imm), [{ int64_t SVal = N->getSExtValue(); return isInt<32>(SVal) && (SVal & 0xffff); }]>; // Mips Address Mode! SDNode frameindex could possibily be a match // since load and store instructions from stack used it. def addr : ComplexPattern; def addrRegImm : ComplexPattern; def addrDefault : ComplexPattern; def addrimm10 : ComplexPattern; def addrimm10lsl1 : ComplexPattern; def addrimm10lsl2 : ComplexPattern; def addrimm10lsl3 : ComplexPattern; //===----------------------------------------------------------------------===// // Instructions specific format //===----------------------------------------------------------------------===// // Arithmetic and logical instructions with 3 register operands. class ArithLogicR: InstSE<(outs RO:$rd), (ins RO:$rs, RO:$rt), !strconcat(opstr, "\t$rd, $rs, $rt"), [(set RO:$rd, (OpNode RO:$rs, RO:$rt))], Itin, FrmR, opstr> { let isCommutable = isComm; let isReMaterializable = 1; let TwoOperandAliasConstraint = "$rd = $rs"; } // Arithmetic and logical instructions with 2 register operands. class ArithLogicI : InstSE<(outs RO:$rt), (ins RO:$rs, Od:$imm16), !strconcat(opstr, "\t$rt, $rs, $imm16"), [(set RO:$rt, (OpNode RO:$rs, imm_type:$imm16))], Itin, FrmI, opstr> { let isReMaterializable = 1; let TwoOperandAliasConstraint = "$rs = $rt"; } // Arithmetic Multiply ADD/SUB class MArithR : InstSE<(outs), (ins GPR32Opnd:$rs, GPR32Opnd:$rt), !strconcat(opstr, "\t$rs, $rt"), [], itin, FrmR, opstr> { let Defs = [HI0, LO0]; let Uses = [HI0, LO0]; let isCommutable = isComm; } // Logical class LogicNOR: InstSE<(outs RO:$rd), (ins RO:$rs, RO:$rt), !strconcat(opstr, "\t$rd, $rs, $rt"), [(set RO:$rd, (not (or RO:$rs, RO:$rt)))], II_NOR, FrmR, opstr> { let isCommutable = 1; } // Shifts class shift_rotate_imm : InstSE<(outs RO:$rd), (ins RO:$rt, ImmOpnd:$shamt), !strconcat(opstr, "\t$rd, $rt, $shamt"), [(set RO:$rd, (OpNode RO:$rt, PF:$shamt))], itin, FrmR, opstr> { let TwoOperandAliasConstraint = "$rt = $rd"; } class shift_rotate_reg: InstSE<(outs RO:$rd), (ins RO:$rt, GPR32Opnd:$rs), !strconcat(opstr, "\t$rd, $rt, $rs"), [(set RO:$rd, (OpNode RO:$rt, GPR32Opnd:$rs))], itin, FrmR, opstr>; // Load Upper Immediate class LoadUpper: InstSE<(outs RO:$rt), (ins Imm:$imm16), !strconcat(opstr, "\t$rt, $imm16"), [], II_LUI, FrmI, opstr>, IsAsCheapAsAMove { let hasSideEffects = 0; let isReMaterializable = 1; } // Memory Load/Store class LoadMemory : InstSE<(outs RO:$rt), (ins MO:$addr), !strconcat(opstr, "\t$rt, $addr"), [(set RO:$rt, (OpNode Addr:$addr))], Itin, FrmI, opstr> { let DecoderMethod = "DecodeMem"; let canFoldAsLoad = 1; string BaseOpcode = opstr; let mayLoad = 1; } class Load : LoadMemory; class StoreMemory : InstSE<(outs), (ins RO:$rt, MO:$addr), !strconcat(opstr, "\t$rt, $addr"), [(OpNode RO:$rt, Addr:$addr)], Itin, FrmI, opstr> { let DecoderMethod = "DecodeMem"; string BaseOpcode = opstr; let mayStore = 1; } class Store : StoreMemory; // Load/Store Left/Right let canFoldAsLoad = 1 in class LoadLeftRight : InstSE<(outs RO:$rt), (ins mem:$addr, RO:$src), !strconcat(opstr, "\t$rt, $addr"), [(set RO:$rt, (OpNode addr:$addr, RO:$src))], Itin, FrmI> { let DecoderMethod = "DecodeMem"; string Constraints = "$src = $rt"; let BaseOpcode = opstr; } class StoreLeftRight : InstSE<(outs), (ins RO:$rt, mem:$addr), !strconcat(opstr, "\t$rt, $addr"), [(OpNode RO:$rt, addr:$addr)], Itin, FrmI> { let DecoderMethod = "DecodeMem"; let BaseOpcode = opstr; } // COP2 Load/Store class LW_FT2 : InstSE<(outs RC:$rt), (ins mem_simm16:$addr), !strconcat(opstr, "\t$rt, $addr"), [(set RC:$rt, (OpNode addrDefault:$addr))], Itin, FrmFI, opstr> { let DecoderMethod = "DecodeFMem2"; let mayLoad = 1; } class SW_FT2 : InstSE<(outs), (ins RC:$rt, mem_simm16:$addr), !strconcat(opstr, "\t$rt, $addr"), [(OpNode RC:$rt, addrDefault:$addr)], Itin, FrmFI, opstr> { let DecoderMethod = "DecodeFMem2"; let mayStore = 1; } // COP3 Load/Store class LW_FT3 : InstSE<(outs RC:$rt), (ins mem:$addr), !strconcat(opstr, "\t$rt, $addr"), [(set RC:$rt, (OpNode addrDefault:$addr))], Itin, FrmFI, opstr> { let DecoderMethod = "DecodeFMem3"; let mayLoad = 1; } class SW_FT3 : InstSE<(outs), (ins RC:$rt, mem:$addr), !strconcat(opstr, "\t$rt, $addr"), [(OpNode RC:$rt, addrDefault:$addr)], Itin, FrmFI, opstr> { let DecoderMethod = "DecodeFMem3"; let mayStore = 1; } // Conditional Branch class CBranch : InstSE<(outs), (ins RO:$rs, RO:$rt, opnd:$offset), !strconcat(opstr, "\t$rs, $rt, $offset"), [(brcond (i32 (cond_op RO:$rs, RO:$rt)), bb:$offset)], II_BCC, FrmI, opstr> { let isBranch = 1; let isTerminator = 1; let hasDelaySlot = 1; let Defs = [AT]; bit isCTI = 1; } class CBranchLikely : InstSE<(outs), (ins RO:$rs, RO:$rt, opnd:$offset), !strconcat(opstr, "\t$rs, $rt, $offset"), [], II_BCC, FrmI, opstr> { let isBranch = 1; let isTerminator = 1; let hasDelaySlot = 1; let Defs = [AT]; bit isCTI = 1; } class CBranchZero : InstSE<(outs), (ins RO:$rs, opnd:$offset), !strconcat(opstr, "\t$rs, $offset"), [(brcond (i32 (cond_op RO:$rs, 0)), bb:$offset)], II_BCCZ, FrmI, opstr> { let isBranch = 1; let isTerminator = 1; let hasDelaySlot = 1; let Defs = [AT]; bit isCTI = 1; } class CBranchZeroLikely : InstSE<(outs), (ins RO:$rs, opnd:$offset), !strconcat(opstr, "\t$rs, $offset"), [], II_BCCZ, FrmI, opstr> { let isBranch = 1; let isTerminator = 1; let hasDelaySlot = 1; let Defs = [AT]; bit isCTI = 1; } // SetCC class SetCC_R : InstSE<(outs GPR32Opnd:$rd), (ins RO:$rs, RO:$rt), !strconcat(opstr, "\t$rd, $rs, $rt"), [(set GPR32Opnd:$rd, (cond_op RO:$rs, RO:$rt))], II_SLT_SLTU, FrmR, opstr>; class SetCC_I: InstSE<(outs GPR32Opnd:$rt), (ins RO:$rs, Od:$imm16), !strconcat(opstr, "\t$rt, $rs, $imm16"), [(set GPR32Opnd:$rt, (cond_op RO:$rs, imm_type:$imm16))], II_SLTI_SLTIU, FrmI, opstr>; // Jump class JumpFJ : InstSE<(outs), (ins opnd:$target), !strconcat(opstr, "\t$target"), [(operator targetoperator:$target)], II_J, FrmJ, bopstr> { let isTerminator=1; let isBarrier=1; let hasDelaySlot = 1; let DecoderMethod = "DecodeJumpTarget"; let Defs = [AT]; bit isCTI = 1; } // Unconditional branch class UncondBranch : PseudoSE<(outs), (ins brtarget:$offset), [(br bb:$offset)], II_B>, PseudoInstExpansion<(BEQInst ZERO, ZERO, opnd:$offset)> { let isBranch = 1; let isTerminator = 1; let isBarrier = 1; let hasDelaySlot = 1; let AdditionalPredicates = [RelocPIC]; let Defs = [AT]; bit isCTI = 1; } // Base class for indirect branch and return instruction classes. let isTerminator=1, isBarrier=1, hasDelaySlot = 1, isCTI = 1 in class JumpFR: InstSE<(outs), (ins RO:$rs), "jr\t$rs", [(operator RO:$rs)], II_JR, FrmR, opstr>; // Indirect branch class IndirectBranch : JumpFR { let isBranch = 1; let isIndirectBranch = 1; } // Jump and Link (Call) let isCall=1, hasDelaySlot=1, isCTI=1, Defs = [RA] in { class JumpLink : InstSE<(outs), (ins opnd:$target), !strconcat(opstr, "\t$target"), [(MipsJmpLink tglobaladdr:$target)], II_JAL, FrmJ, opstr> { let DecoderMethod = "DecodeJumpTarget"; } class JumpLinkRegPseudo: PseudoSE<(outs), (ins RO:$rs), [(MipsJmpLink RO:$rs)], II_JALR>, PseudoInstExpansion<(JALRInst RetReg, ResRO:$rs)> { let hasPostISelHook = 1; } class JumpLinkReg: InstSE<(outs RO:$rd), (ins RO:$rs), !strconcat(opstr, "\t$rd, $rs"), [], II_JALR, FrmR, opstr> { let hasPostISelHook = 1; } class BGEZAL_FT : InstSE<(outs), (ins RO:$rs, opnd:$offset), !strconcat(opstr, "\t$rs, $offset"), [], II_BCCZAL, FrmI, opstr> { let hasDelaySlot = 1; } } let isCall = 1, isTerminator = 1, isReturn = 1, isBarrier = 1, hasDelaySlot = 1, hasExtraSrcRegAllocReq = 1, isCTI = 1, Defs = [AT] in { class TailCall : PseudoSE<(outs), (ins calltarget:$target), [], II_J>, PseudoInstExpansion<(JumpInst Opnd:$target)>; class TailCallReg : PseudoSE<(outs), (ins RO:$rs), [(MipsTailCall RO:$rs)], II_JR>, PseudoInstExpansion<(JumpInst RO:$rs)> { let hasPostISelHook = 1; } } class BAL_BR_Pseudo : PseudoSE<(outs), (ins opnd:$offset), [], II_BCCZAL>, PseudoInstExpansion<(RealInst ZERO, opnd:$offset)> { let isBranch = 1; let isTerminator = 1; let isBarrier = 1; let hasDelaySlot = 1; let Defs = [RA]; bit isCTI = 1; } let isCTI = 1 in { // Syscall class SYS_FT : InstSE<(outs), (ins ImmOp:$code_), !strconcat(opstr, "\t$code_"), [], itin, FrmI, opstr>; // Break class BRK_FT : InstSE<(outs), (ins uimm10:$code_1, uimm10:$code_2), !strconcat(opstr, "\t$code_1, $code_2"), [], II_BREAK, FrmOther, opstr>; // (D)Eret class ER_FT : InstSE<(outs), (ins), opstr, [], itin, FrmOther, opstr>; // Wait class WAIT_FT : InstSE<(outs), (ins), opstr, [], II_WAIT, FrmOther, opstr>; } // Interrupts class DEI_FT : InstSE<(outs RO:$rt), (ins), !strconcat(opstr, "\t$rt"), [], itin, FrmOther, opstr>; // Sync let hasSideEffects = 1 in class SYNC_FT : InstSE<(outs), (ins uimm5:$stype), "sync $stype", [(MipsSync immZExt5:$stype)], II_SYNC, FrmOther, opstr>; class SYNCI_FT : InstSE<(outs), (ins MO:$addr), !strconcat(opstr, "\t$addr"), [], II_SYNCI, FrmOther, opstr> { let hasSideEffects = 1; let DecoderMethod = "DecodeSyncI"; } let hasSideEffects = 1, isCTI = 1 in { class TEQ_FT : InstSE<(outs), (ins RO:$rs, RO:$rt, ImmOp:$code_), !strconcat(opstr, "\t$rs, $rt, $code_"), [], itin, FrmI, opstr>; class TEQI_FT : InstSE<(outs), (ins RO:$rs, simm16:$imm16), !strconcat(opstr, "\t$rs, $imm16"), [], itin, FrmOther, opstr>; } // Mul, Div class Mult DefRegs> : InstSE<(outs), (ins RO:$rs, RO:$rt), !strconcat(opstr, "\t$rs, $rt"), [], itin, FrmR, opstr> { let isCommutable = 1; let Defs = DefRegs; let hasSideEffects = 0; } // Pseudo multiply/divide instruction with explicit accumulator register // operands. class MultDivPseudo : PseudoSE<(outs R0:$ac), (ins R1:$rs, R1:$rt), [(set R0:$ac, (OpNode R1:$rs, R1:$rt))], Itin>, PseudoInstExpansion<(RealInst R1:$rs, R1:$rt)> { let isCommutable = IsComm; let hasSideEffects = HasSideEffects; let usesCustomInserter = UsesCustomInserter; } // Pseudo multiply add/sub instruction with explicit accumulator register // operands. class MAddSubPseudo : PseudoSE<(outs ACC64:$ac), (ins GPR32Opnd:$rs, GPR32Opnd:$rt, ACC64:$acin), [(set ACC64:$ac, (OpNode GPR32Opnd:$rs, GPR32Opnd:$rt, ACC64:$acin))], itin>, PseudoInstExpansion<(RealInst GPR32Opnd:$rs, GPR32Opnd:$rt)> { string Constraints = "$acin = $ac"; } class Div DefRegs> : InstSE<(outs), (ins RO:$rs, RO:$rt), !strconcat(opstr, "\t$$zero, $rs, $rt"), [], itin, FrmR, opstr> { let Defs = DefRegs; } // Move from Hi/Lo class PseudoMFLOHI : PseudoSE<(outs DstRC:$rd), (ins SrcRC:$hilo), [(set DstRC:$rd, (OpNode SrcRC:$hilo))], II_MFHI_MFLO>; class MoveFromLOHI: InstSE<(outs RO:$rd), (ins), !strconcat(opstr, "\t$rd"), [], II_MFHI_MFLO, FrmR, opstr> { let Uses = [UseReg]; let hasSideEffects = 0; let isMoveReg = 1; } class PseudoMTLOHI : PseudoSE<(outs DstRC:$lohi), (ins SrcRC:$lo, SrcRC:$hi), [(set DstRC:$lohi, (MipsMTLOHI SrcRC:$lo, SrcRC:$hi))], II_MTHI_MTLO>; class MoveToLOHI DefRegs>: InstSE<(outs), (ins RO:$rs), !strconcat(opstr, "\t$rs"), [], II_MTHI_MTLO, FrmR, opstr> { let Defs = DefRegs; let hasSideEffects = 0; let isMoveReg = 1; } class EffectiveAddress : InstSE<(outs RO:$rt), (ins mem_ea:$addr), !strconcat(opstr, "\t$rt, $addr"), [(set RO:$rt, addr:$addr)], II_ADDIU, FrmI, !strconcat(opstr, "_lea")> { let isCodeGenOnly = 1; let hasNoSchedulingInfo = 1; let DecoderMethod = "DecodeMem"; } // Count Leading Ones/Zeros in Word class CountLeading0: InstSE<(outs RO:$rd), (ins RO:$rs), !strconcat(opstr, "\t$rd, $rs"), [(set RO:$rd, (ctlz RO:$rs))], itin, FrmR, opstr>; class CountLeading1: InstSE<(outs RO:$rd), (ins RO:$rs), !strconcat(opstr, "\t$rd, $rs"), [(set RO:$rd, (ctlz (not RO:$rs)))], itin, FrmR, opstr>; // Sign Extend in Register. class SignExtInReg : InstSE<(outs RO:$rd), (ins RO:$rt), !strconcat(opstr, "\t$rd, $rt"), [(set RO:$rd, (sext_inreg RO:$rt, vt))], itin, FrmR, opstr>; // Subword Swap class SubwordSwap: InstSE<(outs RO:$rd), (ins RO:$rt), !strconcat(opstr, "\t$rd, $rt"), [], itin, FrmR, opstr> { let hasSideEffects = 0; } // Read Hardware class ReadHardware : InstSE<(outs CPURegOperand:$rt), (ins RO:$rd, uimm8:$sel), "rdhwr\t$rt, $rd, $sel", [], II_RDHWR, FrmR, "rdhwr">; // Ext and Ins class ExtBase : InstSE<(outs RO:$rt), (ins RO:$rs, PosOpnd:$pos, SizeOpnd:$size), !strconcat(opstr, "\t$rt, $rs, $pos, $size"), [(set RO:$rt, (Op RO:$rs, PosImm:$pos, SizeImm:$size))], II_EXT, FrmR, opstr>; // 'ins' and its' 64 bit variants are matched by C++ code. class InsBase: InstSE<(outs RO:$rt), (ins RO:$rs, PosOpnd:$pos, SizeOpnd:$size, RO:$src), !strconcat(opstr, "\t$rt, $rs, $pos, $size"), [(set RO:$rt, (null_frag RO:$rs, PosImm:$pos, SizeImm:$size, RO:$src))], II_INS, FrmR, opstr> { let Constraints = "$src = $rt"; } // Atomic instructions with 2 source operands (ATOMIC_SWAP & ATOMIC_LOAD_*). class Atomic2Ops : PseudoSE<(outs DRC:$dst), (ins PtrRC:$ptr, DRC:$incr), [(set DRC:$dst, (Op iPTR:$ptr, DRC:$incr))]> { let hasNoSchedulingInfo = 1; } class Atomic2OpsPostRA : PseudoSE<(outs RC:$dst), (ins PtrRC:$ptr, RC:$incr), []> { let mayLoad = 1; let mayStore = 1; } class Atomic2OpsSubwordPostRA : PseudoSE<(outs RC:$dst), (ins PtrRC:$ptr, RC:$incr, RC:$mask, RC:$mask2, RC:$shiftamnt), []>; // Atomic Compare & Swap. // Atomic compare and swap is lowered into two stages. The first stage happens // during ISelLowering, which produces the PostRA version of this instruction. class AtomicCmpSwap : PseudoSE<(outs DRC:$dst), (ins PtrRC:$ptr, DRC:$cmp, DRC:$swap), [(set DRC:$dst, (Op iPTR:$ptr, DRC:$cmp, DRC:$swap))]> { let hasNoSchedulingInfo = 1; } class AtomicCmpSwapPostRA : PseudoSE<(outs RC:$dst), (ins PtrRC:$ptr, RC:$cmp, RC:$swap), []> { let mayLoad = 1; let mayStore = 1; } class AtomicCmpSwapSubwordPostRA : PseudoSE<(outs RC:$dst), (ins PtrRC:$ptr, RC:$mask, RC:$ShiftCmpVal, RC:$mask2, RC:$ShiftNewVal, RC:$ShiftAmt), []> { let mayLoad = 1; let mayStore = 1; } class LLBase : InstSE<(outs RO:$rt), (ins MO:$addr), !strconcat(opstr, "\t$rt, $addr"), [], II_LL, FrmI, opstr> { let DecoderMethod = "DecodeMem"; let mayLoad = 1; } class SCBase : InstSE<(outs RO:$dst), (ins RO:$rt, mem:$addr), !strconcat(opstr, "\t$rt, $addr"), [], II_SC, FrmI> { let DecoderMethod = "DecodeMem"; let mayStore = 1; let Constraints = "$rt = $dst"; } class MFC3OP : InstSE<(outs RO:$rt), (ins RD:$rd, uimm3:$sel), !strconcat(asmstr, "\t$rt, $rd, $sel"), [], itin, FrmFR> { let BaseOpcode = asmstr; } class MTC3OP : InstSE<(outs RO:$rd), (ins RD:$rt, uimm3:$sel), !strconcat(asmstr, "\t$rt, $rd, $sel"), [], itin, FrmFR> { let BaseOpcode = asmstr; } class TrapBase : PseudoSE<(outs), (ins), [(trap)], II_TRAP>, PseudoInstExpansion<(RealInst 0, 0)> { let isBarrier = 1; let isTerminator = 1; let isCodeGenOnly = 1; let isCTI = 1; } //===----------------------------------------------------------------------===// // Pseudo instructions //===----------------------------------------------------------------------===// // Return RA. let isReturn=1, isTerminator=1, isBarrier=1, hasCtrlDep=1, isCTI=1 in { let hasDelaySlot=1 in def RetRA : PseudoSE<(outs), (ins), [(MipsRet)]>; let hasSideEffects=1 in def ERet : PseudoSE<(outs), (ins), [(MipsERet)]>; } let Defs = [SP], Uses = [SP], hasSideEffects = 1, hasNoSchedulingInfo = 1 in { def ADJCALLSTACKDOWN : MipsPseudo<(outs), (ins i32imm:$amt1, i32imm:$amt2), [(callseq_start timm:$amt1, timm:$amt2)]>; def ADJCALLSTACKUP : MipsPseudo<(outs), (ins i32imm:$amt1, i32imm:$amt2), [(callseq_end timm:$amt1, timm:$amt2)]>; } let usesCustomInserter = 1 in { def ATOMIC_LOAD_ADD_I8 : Atomic2Ops; def ATOMIC_LOAD_ADD_I16 : Atomic2Ops; def ATOMIC_LOAD_ADD_I32 : Atomic2Ops; def ATOMIC_LOAD_SUB_I8 : Atomic2Ops; def ATOMIC_LOAD_SUB_I16 : Atomic2Ops; def ATOMIC_LOAD_SUB_I32 : Atomic2Ops; def ATOMIC_LOAD_AND_I8 : Atomic2Ops; def ATOMIC_LOAD_AND_I16 : Atomic2Ops; def ATOMIC_LOAD_AND_I32 : Atomic2Ops; def ATOMIC_LOAD_OR_I8 : Atomic2Ops; def ATOMIC_LOAD_OR_I16 : Atomic2Ops; def ATOMIC_LOAD_OR_I32 : Atomic2Ops; def ATOMIC_LOAD_XOR_I8 : Atomic2Ops; def ATOMIC_LOAD_XOR_I16 : Atomic2Ops; def ATOMIC_LOAD_XOR_I32 : Atomic2Ops; def ATOMIC_LOAD_NAND_I8 : Atomic2Ops; def ATOMIC_LOAD_NAND_I16 : Atomic2Ops; def ATOMIC_LOAD_NAND_I32 : Atomic2Ops; def ATOMIC_SWAP_I8 : Atomic2Ops; def ATOMIC_SWAP_I16 : Atomic2Ops; def ATOMIC_SWAP_I32 : Atomic2Ops; def ATOMIC_CMP_SWAP_I8 : AtomicCmpSwap; def ATOMIC_CMP_SWAP_I16 : AtomicCmpSwap; def ATOMIC_CMP_SWAP_I32 : AtomicCmpSwap; } def ATOMIC_LOAD_ADD_I8_POSTRA : Atomic2OpsSubwordPostRA; def ATOMIC_LOAD_ADD_I16_POSTRA : Atomic2OpsSubwordPostRA; def ATOMIC_LOAD_ADD_I32_POSTRA : Atomic2OpsPostRA; def ATOMIC_LOAD_SUB_I8_POSTRA : Atomic2OpsSubwordPostRA; def ATOMIC_LOAD_SUB_I16_POSTRA : Atomic2OpsSubwordPostRA; def ATOMIC_LOAD_SUB_I32_POSTRA : Atomic2OpsPostRA; def ATOMIC_LOAD_AND_I8_POSTRA : Atomic2OpsSubwordPostRA; def ATOMIC_LOAD_AND_I16_POSTRA : Atomic2OpsSubwordPostRA; def ATOMIC_LOAD_AND_I32_POSTRA : Atomic2OpsPostRA; def ATOMIC_LOAD_OR_I8_POSTRA : Atomic2OpsSubwordPostRA; def ATOMIC_LOAD_OR_I16_POSTRA : Atomic2OpsSubwordPostRA; def ATOMIC_LOAD_OR_I32_POSTRA : Atomic2OpsPostRA; def ATOMIC_LOAD_XOR_I8_POSTRA : Atomic2OpsSubwordPostRA; def ATOMIC_LOAD_XOR_I16_POSTRA : Atomic2OpsSubwordPostRA; def ATOMIC_LOAD_XOR_I32_POSTRA : Atomic2OpsPostRA; def ATOMIC_LOAD_NAND_I8_POSTRA : Atomic2OpsSubwordPostRA; def ATOMIC_LOAD_NAND_I16_POSTRA : Atomic2OpsSubwordPostRA; def ATOMIC_LOAD_NAND_I32_POSTRA : Atomic2OpsPostRA; def ATOMIC_SWAP_I8_POSTRA : Atomic2OpsSubwordPostRA; def ATOMIC_SWAP_I16_POSTRA : Atomic2OpsSubwordPostRA; def ATOMIC_SWAP_I32_POSTRA : Atomic2OpsPostRA; def ATOMIC_CMP_SWAP_I8_POSTRA : AtomicCmpSwapSubwordPostRA; def ATOMIC_CMP_SWAP_I16_POSTRA : AtomicCmpSwapSubwordPostRA; def ATOMIC_CMP_SWAP_I32_POSTRA : AtomicCmpSwapPostRA; /// Pseudo instructions for loading and storing accumulator registers. let isPseudo = 1, isCodeGenOnly = 1, hasNoSchedulingInfo = 1 in { def LOAD_ACC64 : Load<"", ACC64>; def STORE_ACC64 : Store<"", ACC64>; } // We need these two pseudo instructions to avoid offset calculation for long // branches. See the comment in file MipsLongBranch.cpp for detailed // explanation. // Expands to: lui $dst, %highest/%higher/%hi/%lo($tgt - $baltgt) def LONG_BRANCH_LUi : PseudoSE<(outs GPR32Opnd:$dst), (ins brtarget:$tgt, brtarget:$baltgt), []> { bit hasNoSchedulingInfo = 1; } // Expands to: lui $dst, highest/%higher/%hi/%lo($tgt) def LONG_BRANCH_LUi2Op : PseudoSE<(outs GPR32Opnd:$dst), (ins brtarget:$tgt), []> { bit hasNoSchedulingInfo = 1; } // Expands to: addiu $dst, $src, %highest/%higher/%hi/%lo($tgt - $baltgt) def LONG_BRANCH_ADDiu : PseudoSE<(outs GPR32Opnd:$dst), (ins GPR32Opnd:$src, brtarget:$tgt, brtarget:$baltgt), []> { bit hasNoSchedulingInfo = 1; } // Expands to: addiu $dst, $src, %highest/%higher/%hi/%lo($tgt) def LONG_BRANCH_ADDiu2Op : PseudoSE<(outs GPR32Opnd:$dst), (ins GPR32Opnd:$src, brtarget:$tgt), []> { bit hasNoSchedulingInfo = 1; } //===----------------------------------------------------------------------===// // Instruction definition //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// // MipsI Instructions //===----------------------------------------------------------------------===// /// Arithmetic Instructions (ALU Immediate) let AdditionalPredicates = [NotInMicroMips] in { def ADDiu : MMRel, StdMMR6Rel, ArithLogicI<"addiu", simm16_relaxed, GPR32Opnd, II_ADDIU, immSExt16, add>, ADDI_FM<0x9>, IsAsCheapAsAMove, ISA_MIPS1; def ANDi : MMRel, StdMMR6Rel, ArithLogicI<"andi", uimm16, GPR32Opnd, II_ANDI, immZExt16, and>, ADDI_FM<0xc>, ISA_MIPS1; def ORi : MMRel, StdMMR6Rel, ArithLogicI<"ori", uimm16, GPR32Opnd, II_ORI, immZExt16, or>, ADDI_FM<0xd>, ISA_MIPS1; def XORi : MMRel, StdMMR6Rel, ArithLogicI<"xori", uimm16, GPR32Opnd, II_XORI, immZExt16, xor>, ADDI_FM<0xe>, ISA_MIPS1; def ADDi : MMRel, ArithLogicI<"addi", simm16_relaxed, GPR32Opnd, II_ADDI>, ADDI_FM<0x8>, ISA_MIPS1_NOT_32R6_64R6; def SLTi : MMRel, SetCC_I<"slti", setlt, simm16, immSExt16, GPR32Opnd>, SLTI_FM<0xa>, ISA_MIPS1; def SLTiu : MMRel, SetCC_I<"sltiu", setult, simm16, immSExt16, GPR32Opnd>, SLTI_FM<0xb>, ISA_MIPS1; def LUi : MMRel, LoadUpper<"lui", GPR32Opnd, uimm16_relaxed>, LUI_FM, ISA_MIPS1; /// Arithmetic Instructions (3-Operand, R-Type) def ADDu : MMRel, StdMMR6Rel, ArithLogicR<"addu", GPR32Opnd, 1, II_ADDU, add>, ADD_FM<0, 0x21>, ISA_MIPS1; def SUBu : MMRel, StdMMR6Rel, ArithLogicR<"subu", GPR32Opnd, 0, II_SUBU, sub>, ADD_FM<0, 0x23>, ISA_MIPS1; let Defs = [HI0, LO0] in def MUL : MMRel, ArithLogicR<"mul", GPR32Opnd, 1, II_MUL, mul>, ADD_FM<0x1c, 2>, ISA_MIPS32_NOT_32R6_64R6; def ADD : MMRel, StdMMR6Rel, ArithLogicR<"add", GPR32Opnd, 1, II_ADD>, ADD_FM<0, 0x20>, ISA_MIPS1; def SUB : MMRel, StdMMR6Rel, ArithLogicR<"sub", GPR32Opnd, 0, II_SUB>, ADD_FM<0, 0x22>, ISA_MIPS1; def SLT : MMRel, SetCC_R<"slt", setlt, GPR32Opnd>, ADD_FM<0, 0x2a>, ISA_MIPS1; def SLTu : MMRel, SetCC_R<"sltu", setult, GPR32Opnd>, ADD_FM<0, 0x2b>, ISA_MIPS1; def AND : MMRel, StdMMR6Rel, ArithLogicR<"and", GPR32Opnd, 1, II_AND, and>, ADD_FM<0, 0x24>, ISA_MIPS1; def OR : MMRel, StdMMR6Rel, ArithLogicR<"or", GPR32Opnd, 1, II_OR, or>, ADD_FM<0, 0x25>, ISA_MIPS1; def XOR : MMRel, StdMMR6Rel, ArithLogicR<"xor", GPR32Opnd, 1, II_XOR, xor>, ADD_FM<0, 0x26>, ISA_MIPS1; def NOR : MMRel, StdMMR6Rel, LogicNOR<"nor", GPR32Opnd>, ADD_FM<0, 0x27>, ISA_MIPS1; } let AdditionalPredicates = [NotInMicroMips] in { /// Shift Instructions def SLL : MMRel, shift_rotate_imm<"sll", uimm5, GPR32Opnd, II_SLL, shl, immZExt5>, SRA_FM<0, 0>, ISA_MIPS1; def SRL : MMRel, shift_rotate_imm<"srl", uimm5, GPR32Opnd, II_SRL, srl, immZExt5>, SRA_FM<2, 0>, ISA_MIPS1; def SRA : MMRel, shift_rotate_imm<"sra", uimm5, GPR32Opnd, II_SRA, sra, immZExt5>, SRA_FM<3, 0>, ISA_MIPS1; def SLLV : MMRel, shift_rotate_reg<"sllv", GPR32Opnd, II_SLLV, shl>, SRLV_FM<4, 0>, ISA_MIPS1; def SRLV : MMRel, shift_rotate_reg<"srlv", GPR32Opnd, II_SRLV, srl>, SRLV_FM<6, 0>, ISA_MIPS1; def SRAV : MMRel, shift_rotate_reg<"srav", GPR32Opnd, II_SRAV, sra>, SRLV_FM<7, 0>, ISA_MIPS1; // Rotate Instructions def ROTR : MMRel, shift_rotate_imm<"rotr", uimm5, GPR32Opnd, II_ROTR, rotr, immZExt5>, SRA_FM<2, 1>, ISA_MIPS32R2; def ROTRV : MMRel, shift_rotate_reg<"rotrv", GPR32Opnd, II_ROTRV, rotr>, SRLV_FM<6, 1>, ISA_MIPS32R2; } /// Load and Store Instructions /// aligned let AdditionalPredicates = [NotInMicroMips] in { def LB : LoadMemory<"lb", GPR32Opnd, mem_simmptr, sextloadi8, II_LB>, MMRel, LW_FM<0x20>, ISA_MIPS1; def LBu : LoadMemory<"lbu", GPR32Opnd, mem_simmptr, zextloadi8, II_LBU, addrDefault>, MMRel, LW_FM<0x24>, ISA_MIPS1; def LH : LoadMemory<"lh", GPR32Opnd, mem_simmptr, sextloadi16, II_LH, addrDefault>, MMRel, LW_FM<0x21>, ISA_MIPS1; def LHu : LoadMemory<"lhu", GPR32Opnd, mem_simmptr, zextloadi16, II_LHU>, MMRel, LW_FM<0x25>, ISA_MIPS1; def LW : StdMMR6Rel, Load<"lw", GPR32Opnd, load, II_LW, addrDefault>, MMRel, LW_FM<0x23>, ISA_MIPS1; def SB : StdMMR6Rel, Store<"sb", GPR32Opnd, truncstorei8, II_SB>, MMRel, LW_FM<0x28>, ISA_MIPS1; def SH : Store<"sh", GPR32Opnd, truncstorei16, II_SH>, MMRel, LW_FM<0x29>, ISA_MIPS1; def SW : StdMMR6Rel, Store<"sw", GPR32Opnd, store, II_SW>, MMRel, LW_FM<0x2b>, ISA_MIPS1; } /// load/store left/right let AdditionalPredicates = [NotInMicroMips] in { def LWL : MMRel, LoadLeftRight<"lwl", MipsLWL, GPR32Opnd, II_LWL>, LW_FM<0x22>, ISA_MIPS1_NOT_32R6_64R6; def LWR : MMRel, LoadLeftRight<"lwr", MipsLWR, GPR32Opnd, II_LWR>, LW_FM<0x26>, ISA_MIPS1_NOT_32R6_64R6; def SWL : MMRel, StoreLeftRight<"swl", MipsSWL, GPR32Opnd, II_SWL>, LW_FM<0x2a>, ISA_MIPS1_NOT_32R6_64R6; def SWR : MMRel, StoreLeftRight<"swr", MipsSWR, GPR32Opnd, II_SWR>, LW_FM<0x2e>, ISA_MIPS1_NOT_32R6_64R6; // COP2 Memory Instructions def LWC2 : StdMMR6Rel, LW_FT2<"lwc2", COP2Opnd, II_LWC2, load>, LW_FM<0x32>, ISA_MIPS1_NOT_32R6_64R6; def SWC2 : StdMMR6Rel, SW_FT2<"swc2", COP2Opnd, II_SWC2, store>, LW_FM<0x3a>, ISA_MIPS1_NOT_32R6_64R6; def LDC2 : StdMMR6Rel, LW_FT2<"ldc2", COP2Opnd, II_LDC2, load>, LW_FM<0x36>, ISA_MIPS2_NOT_32R6_64R6; def SDC2 : StdMMR6Rel, SW_FT2<"sdc2", COP2Opnd, II_SDC2, store>, LW_FM<0x3e>, ISA_MIPS2_NOT_32R6_64R6; // COP3 Memory Instructions let DecoderNamespace = "COP3_" in { def LWC3 : LW_FT3<"lwc3", COP3Opnd, II_LWC3, load>, LW_FM<0x33>, ISA_MIPS1_NOT_32R6_64R6, NOT_ASE_CNMIPS; def SWC3 : SW_FT3<"swc3", COP3Opnd, II_SWC3, store>, LW_FM<0x3b>, ISA_MIPS1_NOT_32R6_64R6, NOT_ASE_CNMIPS; def LDC3 : LW_FT3<"ldc3", COP3Opnd, II_LDC3, load>, LW_FM<0x37>, ISA_MIPS2, NOT_ASE_CNMIPS; def SDC3 : SW_FT3<"sdc3", COP3Opnd, II_SDC3, store>, LW_FM<0x3f>, ISA_MIPS2, NOT_ASE_CNMIPS; } def SYNC : MMRel, StdMMR6Rel, SYNC_FT<"sync">, SYNC_FM, ISA_MIPS2; def SYNCI : MMRel, StdMMR6Rel, SYNCI_FT<"synci", mem_simm16>, SYNCI_FM, ISA_MIPS32R2; } let AdditionalPredicates = [NotInMicroMips] in { def TEQ : MMRel, TEQ_FT<"teq", GPR32Opnd, uimm10, II_TEQ>, TEQ_FM<0x34>, ISA_MIPS2; def TGE : MMRel, TEQ_FT<"tge", GPR32Opnd, uimm10, II_TGE>, TEQ_FM<0x30>, ISA_MIPS2; def TGEU : MMRel, TEQ_FT<"tgeu", GPR32Opnd, uimm10, II_TGEU>, TEQ_FM<0x31>, ISA_MIPS2; def TLT : MMRel, TEQ_FT<"tlt", GPR32Opnd, uimm10, II_TLT>, TEQ_FM<0x32>, ISA_MIPS2; def TLTU : MMRel, TEQ_FT<"tltu", GPR32Opnd, uimm10, II_TLTU>, TEQ_FM<0x33>, ISA_MIPS2; def TNE : MMRel, TEQ_FT<"tne", GPR32Opnd, uimm10, II_TNE>, TEQ_FM<0x36>, ISA_MIPS2; def TEQI : MMRel, TEQI_FT<"teqi", GPR32Opnd, II_TEQI>, TEQI_FM<0xc>, ISA_MIPS2_NOT_32R6_64R6; def TGEI : MMRel, TEQI_FT<"tgei", GPR32Opnd, II_TGEI>, TEQI_FM<0x8>, ISA_MIPS2_NOT_32R6_64R6; def TGEIU : MMRel, TEQI_FT<"tgeiu", GPR32Opnd, II_TGEIU>, TEQI_FM<0x9>, ISA_MIPS2_NOT_32R6_64R6; def TLTI : MMRel, TEQI_FT<"tlti", GPR32Opnd, II_TLTI>, TEQI_FM<0xa>, ISA_MIPS2_NOT_32R6_64R6; def TTLTIU : MMRel, TEQI_FT<"tltiu", GPR32Opnd, II_TTLTIU>, TEQI_FM<0xb>, ISA_MIPS2_NOT_32R6_64R6; def TNEI : MMRel, TEQI_FT<"tnei", GPR32Opnd, II_TNEI>, TEQI_FM<0xe>, ISA_MIPS2_NOT_32R6_64R6; } let AdditionalPredicates = [NotInMicroMips] in { def BREAK : MMRel, StdMMR6Rel, BRK_FT<"break">, BRK_FM<0xd>, ISA_MIPS1; def SYSCALL : MMRel, SYS_FT<"syscall", uimm20, II_SYSCALL>, SYS_FM<0xc>, ISA_MIPS1; def TRAP : TrapBase, ISA_MIPS1; def SDBBP : MMRel, SYS_FT<"sdbbp", uimm20, II_SDBBP>, SDBBP_FM, ISA_MIPS32_NOT_32R6_64R6; def ERET : MMRel, ER_FT<"eret", II_ERET>, ER_FM<0x18, 0x0>, INSN_MIPS3_32; def ERETNC : MMRel, ER_FT<"eretnc", II_ERETNC>, ER_FM<0x18, 0x1>, ISA_MIPS32R5; def DERET : MMRel, ER_FT<"deret", II_DERET>, ER_FM<0x1f, 0x0>, ISA_MIPS32; def EI : MMRel, StdMMR6Rel, DEI_FT<"ei", GPR32Opnd, II_EI>, EI_FM<1>, ISA_MIPS32R2; def DI : MMRel, StdMMR6Rel, DEI_FT<"di", GPR32Opnd, II_DI>, EI_FM<0>, ISA_MIPS32R2; def WAIT : MMRel, StdMMR6Rel, WAIT_FT<"wait">, WAIT_FM, INSN_MIPS3_32; } let AdditionalPredicates = [NotInMicroMips] in { /// Load-linked, Store-conditional def LL : LLBase<"ll", GPR32Opnd>, LW_FM<0x30>, PTR_32, ISA_MIPS2_NOT_32R6_64R6; def SC : SCBase<"sc", GPR32Opnd>, LW_FM<0x38>, PTR_32, ISA_MIPS2_NOT_32R6_64R6; } /// Jump and Branch Instructions let AdditionalPredicates = [NotInMicroMips, RelocNotPIC] in def J : MMRel, JumpFJ, FJ<2>, IsBranch, ISA_MIPS1; let AdditionalPredicates = [NotInMicroMips] in { def JR : MMRel, IndirectBranch<"jr", GPR32Opnd>, MTLO_FM<8>, ISA_MIPS1_NOT_32R6_64R6; def BEQ : MMRel, CBranch<"beq", brtarget, seteq, GPR32Opnd>, BEQ_FM<4>, ISA_MIPS1; def BEQL : MMRel, CBranchLikely<"beql", brtarget, GPR32Opnd>, BEQ_FM<20>, ISA_MIPS2_NOT_32R6_64R6; def BNE : MMRel, CBranch<"bne", brtarget, setne, GPR32Opnd>, BEQ_FM<5>, ISA_MIPS1; def BNEL : MMRel, CBranchLikely<"bnel", brtarget, GPR32Opnd>, BEQ_FM<21>, ISA_MIPS2_NOT_32R6_64R6; def BGEZ : MMRel, CBranchZero<"bgez", brtarget, setge, GPR32Opnd>, BGEZ_FM<1, 1>, ISA_MIPS1; def BGEZL : MMRel, CBranchZeroLikely<"bgezl", brtarget, GPR32Opnd>, BGEZ_FM<1, 3>, ISA_MIPS2_NOT_32R6_64R6; def BGTZ : MMRel, CBranchZero<"bgtz", brtarget, setgt, GPR32Opnd>, BGEZ_FM<7, 0>, ISA_MIPS1; def BGTZL : MMRel, CBranchZeroLikely<"bgtzl", brtarget, GPR32Opnd>, BGEZ_FM<23, 0>, ISA_MIPS2_NOT_32R6_64R6; def BLEZ : MMRel, CBranchZero<"blez", brtarget, setle, GPR32Opnd>, BGEZ_FM<6, 0>, ISA_MIPS1; def BLEZL : MMRel, CBranchZeroLikely<"blezl", brtarget, GPR32Opnd>, BGEZ_FM<22, 0>, ISA_MIPS2_NOT_32R6_64R6; def BLTZ : MMRel, CBranchZero<"bltz", brtarget, setlt, GPR32Opnd>, BGEZ_FM<1, 0>, ISA_MIPS1; def BLTZL : MMRel, CBranchZeroLikely<"bltzl", brtarget, GPR32Opnd>, BGEZ_FM<1, 2>, ISA_MIPS2_NOT_32R6_64R6; def B : UncondBranch, ISA_MIPS1; def JAL : MMRel, JumpLink<"jal", calltarget>, FJ<3>, ISA_MIPS1; } let AdditionalPredicates = [NotInMicroMips, NoIndirectJumpGuards] in { def JALR : JumpLinkReg<"jalr", GPR32Opnd>, JALR_FM, ISA_MIPS1; def JALRPseudo : JumpLinkRegPseudo, ISA_MIPS1; } let AdditionalPredicates = [NotInMicroMips] in { def JALX : MMRel, JumpLink<"jalx", calltarget>, FJ<0x1D>, ISA_MIPS32_NOT_32R6_64R6; def BGEZAL : MMRel, BGEZAL_FT<"bgezal", brtarget, GPR32Opnd>, BGEZAL_FM<0x11>, ISA_MIPS1_NOT_32R6_64R6; def BGEZALL : MMRel, BGEZAL_FT<"bgezall", brtarget, GPR32Opnd>, BGEZAL_FM<0x13>, ISA_MIPS2_NOT_32R6_64R6; def BLTZAL : MMRel, BGEZAL_FT<"bltzal", brtarget, GPR32Opnd>, BGEZAL_FM<0x10>, ISA_MIPS1_NOT_32R6_64R6; def BLTZALL : MMRel, BGEZAL_FT<"bltzall", brtarget, GPR32Opnd>, BGEZAL_FM<0x12>, ISA_MIPS2_NOT_32R6_64R6; def BAL_BR : BAL_BR_Pseudo, ISA_MIPS1; } let AdditionalPredicates = [NotInMips16Mode, NotInMicroMips] in { def TAILCALL : TailCall, ISA_MIPS1; } let AdditionalPredicates = [NotInMips16Mode, NotInMicroMips, NoIndirectJumpGuards] in def TAILCALLREG : TailCallReg, ISA_MIPS1_NOT_32R6_64R6; // Indirect branches are matched as PseudoIndirectBranch/PseudoIndirectBranch64 // then are expanded to JR, JR64, JALR, or JALR64 depending on the ISA. class PseudoIndirectBranchBase : MipsPseudo<(outs), (ins RO:$rs), [(brind RO:$rs)], II_IndirectBranchPseudo>, PseudoInstExpansion<(JumpInst RO:$rs)> { let isTerminator=1; let isBarrier=1; let hasDelaySlot = 1; let isBranch = 1; let isIndirectBranch = 1; bit isCTI = 1; } let AdditionalPredicates = [NotInMips16Mode, NotInMicroMips, NoIndirectJumpGuards] in def PseudoIndirectBranch : PseudoIndirectBranchBase, ISA_MIPS1_NOT_32R6_64R6; // Return instructions are matched as a RetRA instruction, then are expanded // into PseudoReturn/PseudoReturn64 after register allocation. Finally, // MipsAsmPrinter expands this into JR, JR64, JALR, or JALR64 depending on the // ISA. class PseudoReturnBase : MipsPseudo<(outs), (ins RO:$rs), [], II_ReturnPseudo> { let isTerminator = 1; let isBarrier = 1; let hasDelaySlot = 1; let isReturn = 1; let isCodeGenOnly = 1; let hasCtrlDep = 1; let hasExtraSrcRegAllocReq = 1; bit isCTI = 1; } def PseudoReturn : PseudoReturnBase; // Exception handling related node and instructions. // The conversion sequence is: // ISD::EH_RETURN -> MipsISD::EH_RETURN -> // MIPSeh_return -> (stack change + indirect branch) // // MIPSeh_return takes the place of regular return instruction // but takes two arguments (V1, V0) which are used for storing // the offset and return address respectively. def SDT_MipsEHRET : SDTypeProfile<0, 2, [SDTCisInt<0>, SDTCisPtrTy<1>]>; def MIPSehret : SDNode<"MipsISD::EH_RETURN", SDT_MipsEHRET, [SDNPHasChain, SDNPOptInGlue, SDNPVariadic]>; let Uses = [V0, V1], isTerminator = 1, isReturn = 1, isBarrier = 1, isCTI = 1, hasNoSchedulingInfo = 1 in { def MIPSeh_return32 : MipsPseudo<(outs), (ins GPR32:$spoff, GPR32:$dst), [(MIPSehret GPR32:$spoff, GPR32:$dst)]>; def MIPSeh_return64 : MipsPseudo<(outs), (ins GPR64:$spoff, GPR64:$dst), [(MIPSehret GPR64:$spoff, GPR64:$dst)]>; } /// Multiply and Divide Instructions. let AdditionalPredicates = [NotInMicroMips] in { def MULT : MMRel, Mult<"mult", II_MULT, GPR32Opnd, [HI0, LO0]>, MULT_FM<0, 0x18>, ISA_MIPS1_NOT_32R6_64R6; def MULTu : MMRel, Mult<"multu", II_MULTU, GPR32Opnd, [HI0, LO0]>, MULT_FM<0, 0x19>, ISA_MIPS1_NOT_32R6_64R6; def SDIV : MMRel, Div<"div", II_DIV, GPR32Opnd, [HI0, LO0]>, MULT_FM<0, 0x1a>, ISA_MIPS1_NOT_32R6_64R6; def UDIV : MMRel, Div<"divu", II_DIVU, GPR32Opnd, [HI0, LO0]>, MULT_FM<0, 0x1b>, ISA_MIPS1_NOT_32R6_64R6; def MTHI : MMRel, MoveToLOHI<"mthi", GPR32Opnd, [HI0]>, MTLO_FM<0x11>, ISA_MIPS1_NOT_32R6_64R6; def MTLO : MMRel, MoveToLOHI<"mtlo", GPR32Opnd, [LO0]>, MTLO_FM<0x13>, ISA_MIPS1_NOT_32R6_64R6; def MFHI : MMRel, MoveFromLOHI<"mfhi", GPR32Opnd, AC0>, MFLO_FM<0x10>, ISA_MIPS1_NOT_32R6_64R6; def MFLO : MMRel, MoveFromLOHI<"mflo", GPR32Opnd, AC0>, MFLO_FM<0x12>, ISA_MIPS1_NOT_32R6_64R6; /// Sign Ext In Register Instructions. def SEB : MMRel, StdMMR6Rel, SignExtInReg<"seb", i8, GPR32Opnd, II_SEB>, SEB_FM<0x10, 0x20>, ISA_MIPS32R2; def SEH : MMRel, StdMMR6Rel, SignExtInReg<"seh", i16, GPR32Opnd, II_SEH>, SEB_FM<0x18, 0x20>, ISA_MIPS32R2; /// Count Leading def CLZ : MMRel, CountLeading0<"clz", GPR32Opnd, II_CLZ>, CLO_FM<0x20>, ISA_MIPS32_NOT_32R6_64R6; def CLO : MMRel, CountLeading1<"clo", GPR32Opnd, II_CLO>, CLO_FM<0x21>, ISA_MIPS32_NOT_32R6_64R6; /// Word Swap Bytes Within Halfwords def WSBH : MMRel, SubwordSwap<"wsbh", GPR32Opnd, II_WSBH>, SEB_FM<2, 0x20>, ISA_MIPS32R2; /// No operation. def NOP : PseudoSE<(outs), (ins), []>, PseudoInstExpansion<(SLL ZERO, ZERO, 0)>, ISA_MIPS1; // FrameIndexes are legalized when they are operands from load/store // instructions. The same not happens for stack address copies, so an // add op with mem ComplexPattern is used and the stack address copy // can be matched. It's similar to Sparc LEA_ADDRi let AdditionalPredicates = [NotInMicroMips] in def LEA_ADDiu : MMRel, EffectiveAddress<"addiu", GPR32Opnd>, LW_FM<9>, ISA_MIPS1; // MADD*/MSUB* def MADD : MMRel, MArithR<"madd", II_MADD, 1>, MULT_FM<0x1c, 0>, ISA_MIPS32_NOT_32R6_64R6; def MADDU : MMRel, MArithR<"maddu", II_MADDU, 1>, MULT_FM<0x1c, 1>, ISA_MIPS32_NOT_32R6_64R6; def MSUB : MMRel, MArithR<"msub", II_MSUB>, MULT_FM<0x1c, 4>, ISA_MIPS32_NOT_32R6_64R6; def MSUBU : MMRel, MArithR<"msubu", II_MSUBU>, MULT_FM<0x1c, 5>, ISA_MIPS32_NOT_32R6_64R6; } let AdditionalPredicates = [NotDSP] in { def PseudoMULT : MultDivPseudo, ISA_MIPS1_NOT_32R6_64R6; def PseudoMULTu : MultDivPseudo, ISA_MIPS1_NOT_32R6_64R6; def PseudoMFHI : PseudoMFLOHI, ISA_MIPS1_NOT_32R6_64R6; def PseudoMFLO : PseudoMFLOHI, ISA_MIPS1_NOT_32R6_64R6; def PseudoMTLOHI : PseudoMTLOHI, ISA_MIPS1_NOT_32R6_64R6; def PseudoMADD : MAddSubPseudo, ISA_MIPS32_NOT_32R6_64R6; def PseudoMADDU : MAddSubPseudo, ISA_MIPS32_NOT_32R6_64R6; def PseudoMSUB : MAddSubPseudo, ISA_MIPS32_NOT_32R6_64R6; def PseudoMSUBU : MAddSubPseudo, ISA_MIPS32_NOT_32R6_64R6; } let AdditionalPredicates = [NotInMicroMips] in { def PseudoSDIV : MultDivPseudo, ISA_MIPS1_NOT_32R6_64R6; def PseudoUDIV : MultDivPseudo, ISA_MIPS1_NOT_32R6_64R6; def RDHWR : MMRel, ReadHardware, RDHWR_FM, ISA_MIPS1; // TODO: Add '0 < pos+size <= 32' constraint check to ext instruction def EXT : MMRel, StdMMR6Rel, ExtBase<"ext", GPR32Opnd, uimm5, uimm5_plus1, immZExt5, immZExt5Plus1, MipsExt>, EXT_FM<0>, ISA_MIPS32R2; def INS : MMRel, StdMMR6Rel, InsBase<"ins", GPR32Opnd, uimm5, uimm5_inssize_plus1, immZExt5, immZExt5Plus1>, EXT_FM<4>, ISA_MIPS32R2; } /// Move Control Registers From/To CPU Registers let AdditionalPredicates = [NotInMicroMips] in { def MTC0 : MTC3OP<"mtc0", COP0Opnd, GPR32Opnd, II_MTC0>, MFC3OP_FM<0x10, 4, 0>, ISA_MIPS1; def MFC0 : MFC3OP<"mfc0", GPR32Opnd, COP0Opnd, II_MFC0>, MFC3OP_FM<0x10, 0, 0>, ISA_MIPS1; def MFC2 : MFC3OP<"mfc2", GPR32Opnd, COP2Opnd, II_MFC2>, MFC3OP_FM<0x12, 0, 0>, ISA_MIPS1; def MTC2 : MTC3OP<"mtc2", COP2Opnd, GPR32Opnd, II_MTC2>, MFC3OP_FM<0x12, 4, 0>, ISA_MIPS1; } class Barrier : InstSE<(outs), (ins), asmstr, [], itin, FrmOther, asmstr>; let AdditionalPredicates = [NotInMicroMips] in { def SSNOP : MMRel, StdMMR6Rel, Barrier<"ssnop", II_SSNOP>, BARRIER_FM<1>, ISA_MIPS1; def EHB : MMRel, Barrier<"ehb", II_EHB>, BARRIER_FM<3>, ISA_MIPS1; let isCTI = 1 in def PAUSE : MMRel, StdMMR6Rel, Barrier<"pause", II_PAUSE>, BARRIER_FM<5>, ISA_MIPS32R2; } // JR_HB and JALR_HB are defined here using the new style naming // scheme because some of this code is shared with Mips32r6InstrInfo.td // and because of that it doesn't follow the naming convention of the // rest of the file. To avoid a mixture of old vs new style, the new // style was chosen. class JR_HB_DESC_BASE { dag OutOperandList = (outs); dag InOperandList = (ins GPROpnd:$rs); string AsmString = !strconcat(instr_asm, "\t$rs"); list Pattern = []; } class JALR_HB_DESC_BASE { dag OutOperandList = (outs GPROpnd:$rd); dag InOperandList = (ins GPROpnd:$rs); string AsmString = !strconcat(instr_asm, "\t$rd, $rs"); list Pattern = []; } class JR_HB_DESC : InstSE<(outs), (ins), "", [], II_JR_HB, FrmJ>, JR_HB_DESC_BASE<"jr.hb", RO> { let isBranch=1; let isIndirectBranch=1; let hasDelaySlot=1; let isTerminator=1; let isBarrier=1; bit isCTI = 1; } class JALR_HB_DESC : InstSE<(outs), (ins), "", [], II_JALR_HB, FrmJ>, JALR_HB_DESC_BASE<"jalr.hb", RO> { let isIndirectBranch=1; let hasDelaySlot=1; bit isCTI = 1; } class JR_HB_ENC : JR_HB_FM<8>; class JALR_HB_ENC : JALR_HB_FM<9>; def JR_HB : JR_HB_DESC, JR_HB_ENC, ISA_MIPS32R2_NOT_32R6_64R6; def JALR_HB : JALR_HB_DESC, JALR_HB_ENC, ISA_MIPS32; let AdditionalPredicates = [NotInMicroMips, UseIndirectJumpsHazard] in def JALRHBPseudo : JumpLinkRegPseudo; let AdditionalPredicates = [NotInMips16Mode, NotInMicroMips, UseIndirectJumpsHazard] in { def TAILCALLREGHB : TailCallReg, ISA_MIPS32_NOT_32R6_64R6; def PseudoIndirectHazardBranch : PseudoIndirectBranchBase, ISA_MIPS32R2_NOT_32R6_64R6; } class TLB : InstSE<(outs), (ins), asmstr, [], itin, FrmOther, asmstr>; let AdditionalPredicates = [NotInMicroMips] in { def TLBP : MMRel, TLB<"tlbp", II_TLBP>, COP0_TLB_FM<0x08>, ISA_MIPS1; def TLBR : MMRel, TLB<"tlbr", II_TLBR>, COP0_TLB_FM<0x01>, ISA_MIPS1; def TLBWI : MMRel, TLB<"tlbwi", II_TLBWI>, COP0_TLB_FM<0x02>, ISA_MIPS1; def TLBWR : MMRel, TLB<"tlbwr", II_TLBWR>, COP0_TLB_FM<0x06>, ISA_MIPS1; } class CacheOp : InstSE<(outs), (ins MemOpnd:$addr, uimm5:$hint), !strconcat(instr_asm, "\t$hint, $addr"), [], itin, FrmOther, instr_asm> { let DecoderMethod = "DecodeCacheOp"; } let AdditionalPredicates = [NotInMicroMips] in { def CACHE : MMRel, CacheOp<"cache", mem, II_CACHE>, CACHEOP_FM<0b101111>, INSN_MIPS3_32_NOT_32R6_64R6; def PREF : MMRel, CacheOp<"pref", mem, II_PREF>, CACHEOP_FM<0b110011>, INSN_MIPS3_32_NOT_32R6_64R6; } // FIXME: We are missing the prefx instruction. def ROL : MipsAsmPseudoInst<(outs), (ins GPR32Opnd:$rs, GPR32Opnd:$rt, GPR32Opnd:$rd), "rol\t$rs, $rt, $rd">; def ROLImm : MipsAsmPseudoInst<(outs), (ins GPR32Opnd:$rs, GPR32Opnd:$rt, simm16:$imm), "rol\t$rs, $rt, $imm">; def : MipsInstAlias<"rol $rd, $rs", (ROL GPR32Opnd:$rd, GPR32Opnd:$rd, GPR32Opnd:$rs), 0>; def : MipsInstAlias<"rol $rd, $imm", (ROLImm GPR32Opnd:$rd, GPR32Opnd:$rd, simm16:$imm), 0>; def ROR : MipsAsmPseudoInst<(outs), (ins GPR32Opnd:$rs, GPR32Opnd:$rt, GPR32Opnd:$rd), "ror\t$rs, $rt, $rd">; def RORImm : MipsAsmPseudoInst<(outs), (ins GPR32Opnd:$rs, GPR32Opnd:$rt, simm16:$imm), "ror\t$rs, $rt, $imm">; def : MipsInstAlias<"ror $rd, $rs", (ROR GPR32Opnd:$rd, GPR32Opnd:$rd, GPR32Opnd:$rs), 0>; def : MipsInstAlias<"ror $rd, $imm", (RORImm GPR32Opnd:$rd, GPR32Opnd:$rd, simm16:$imm), 0>; def DROL : MipsAsmPseudoInst<(outs), (ins GPR32Opnd:$rs, GPR32Opnd:$rt, GPR32Opnd:$rd), "drol\t$rs, $rt, $rd">, ISA_MIPS64; def DROLImm : MipsAsmPseudoInst<(outs), (ins GPR32Opnd:$rs, GPR32Opnd:$rt, simm16:$imm), "drol\t$rs, $rt, $imm">, ISA_MIPS64; def : MipsInstAlias<"drol $rd, $rs", (DROL GPR32Opnd:$rd, GPR32Opnd:$rd, GPR32Opnd:$rs), 0>, ISA_MIPS64; def : MipsInstAlias<"drol $rd, $imm", (DROLImm GPR32Opnd:$rd, GPR32Opnd:$rd, simm16:$imm), 0>, ISA_MIPS64; def DROR : MipsAsmPseudoInst<(outs), (ins GPR32Opnd:$rs, GPR32Opnd:$rt, GPR32Opnd:$rd), "dror\t$rs, $rt, $rd">, ISA_MIPS64; def DRORImm : MipsAsmPseudoInst<(outs), (ins GPR32Opnd:$rs, GPR32Opnd:$rt, simm16:$imm), "dror\t$rs, $rt, $imm">, ISA_MIPS64; def : MipsInstAlias<"dror $rd, $rs", (DROR GPR32Opnd:$rd, GPR32Opnd:$rd, GPR32Opnd:$rs), 0>, ISA_MIPS64; def : MipsInstAlias<"dror $rd, $imm", (DRORImm GPR32Opnd:$rd, GPR32Opnd:$rd, simm16:$imm), 0>, ISA_MIPS64; def ABSMacro : MipsAsmPseudoInst<(outs GPR32Opnd:$rd), (ins GPR32Opnd:$rs), "abs\t$rd, $rs">; def SEQMacro : MipsAsmPseudoInst<(outs GPR32Opnd:$rd), (ins GPR32Opnd:$rs, GPR32Opnd:$rt), "seq $rd, $rs, $rt">, NOT_ASE_CNMIPS; def : MipsInstAlias<"seq $rd, $rs", (SEQMacro GPR32Opnd:$rd, GPR32Opnd:$rd, GPR32Opnd:$rs), 0>, NOT_ASE_CNMIPS; def SEQIMacro : MipsAsmPseudoInst<(outs GPR32Opnd:$rd), (ins GPR32Opnd:$rs, simm32_relaxed:$imm), "seq $rd, $rs, $imm">, NOT_ASE_CNMIPS; def : MipsInstAlias<"seq $rd, $imm", (SEQIMacro GPR32Opnd:$rd, GPR32Opnd:$rd, simm32:$imm), 0>, NOT_ASE_CNMIPS; def MULImmMacro : MipsAsmPseudoInst<(outs), (ins GPR32Opnd:$rd, GPR32Opnd:$rs, simm32_relaxed:$imm), "mul\t$rd, $rs, $imm">, ISA_MIPS1_NOT_32R6_64R6; def MULOMacro : MipsAsmPseudoInst<(outs), (ins GPR32Opnd:$rd, GPR32Opnd:$rs, GPR32Opnd:$rt), "mulo\t$rd, $rs, $rt">, ISA_MIPS1_NOT_32R6_64R6; def MULOUMacro : MipsAsmPseudoInst<(outs), (ins GPR32Opnd:$rd, GPR32Opnd:$rs, GPR32Opnd:$rt), "mulou\t$rd, $rs, $rt">, ISA_MIPS1_NOT_32R6_64R6; // Virtualization ASE class HYPCALL_FT : InstSE<(outs), (ins uimm10:$code_), !strconcat(opstr, "\t$code_"), [], II_HYPCALL, FrmOther, opstr> { let BaseOpcode = opstr; } let AdditionalPredicates = [NotInMicroMips] in { def MFGC0 : MMRel, MFC3OP<"mfgc0", GPR32Opnd, COP0Opnd, II_MFGC0>, MFC3OP_FM<0x10, 3, 0>, ISA_MIPS32R5, ASE_VIRT; def MTGC0 : MMRel, MTC3OP<"mtgc0", COP0Opnd, GPR32Opnd, II_MTGC0>, MFC3OP_FM<0x10, 3, 2>, ISA_MIPS32R5, ASE_VIRT; def MFHGC0 : MMRel, MFC3OP<"mfhgc0", GPR32Opnd, COP0Opnd, II_MFHGC0>, MFC3OP_FM<0x10, 3, 4>, ISA_MIPS32R5, ASE_VIRT; def MTHGC0 : MMRel, MTC3OP<"mthgc0", COP0Opnd, GPR32Opnd, II_MTHGC0>, MFC3OP_FM<0x10, 3, 6>, ISA_MIPS32R5, ASE_VIRT; def TLBGINV : MMRel, TLB<"tlbginv", II_TLBGINV>, COP0_TLB_FM<0b001011>, ISA_MIPS32R5, ASE_VIRT; def TLBGINVF : MMRel, TLB<"tlbginvf", II_TLBGINVF>, COP0_TLB_FM<0b001100>, ISA_MIPS32R5, ASE_VIRT; def TLBGP : MMRel, TLB<"tlbgp", II_TLBGP>, COP0_TLB_FM<0b010000>, ISA_MIPS32R5, ASE_VIRT; def TLBGR : MMRel, TLB<"tlbgr", II_TLBGR>, COP0_TLB_FM<0b001001>, ISA_MIPS32R5, ASE_VIRT; def TLBGWI : MMRel, TLB<"tlbgwi", II_TLBGWI>, COP0_TLB_FM<0b001010>, ISA_MIPS32R5, ASE_VIRT; def TLBGWR : MMRel, TLB<"tlbgwr", II_TLBGWR>, COP0_TLB_FM<0b001110>, ISA_MIPS32R5, ASE_VIRT; def HYPCALL : MMRel, HYPCALL_FT<"hypcall">, HYPCALL_FM<0b101000>, ISA_MIPS32R5, ASE_VIRT; } //===----------------------------------------------------------------------===// // Instruction aliases //===----------------------------------------------------------------------===// multiclass OneOrTwoOperandMacroImmediateAlias { def : MipsInstAlias; def : MipsInstAlias; } let AdditionalPredicates = [NotInMicroMips] in { def : MipsInstAlias<"move $dst, $src", (OR GPR32Opnd:$dst, GPR32Opnd:$src, ZERO), 1>, GPR_32, ISA_MIPS1; def : MipsInstAlias<"move $dst, $src", (ADDu GPR32Opnd:$dst, GPR32Opnd:$src, ZERO), 1>, GPR_32, ISA_MIPS1; def : MipsInstAlias<"bal $offset", (BGEZAL ZERO, brtarget:$offset), 1>, ISA_MIPS1_NOT_32R6_64R6; def : MipsInstAlias<"j $rs", (JR GPR32Opnd:$rs), 0>, ISA_MIPS1; def : MipsInstAlias<"jalr $rs", (JALR RA, GPR32Opnd:$rs), 0>; def : MipsInstAlias<"jalr.hb $rs", (JALR_HB RA, GPR32Opnd:$rs), 1>, ISA_MIPS32; def : MipsInstAlias<"neg $rt, $rs", (SUB GPR32Opnd:$rt, ZERO, GPR32Opnd:$rs), 1>, ISA_MIPS1; def : MipsInstAlias<"neg $rt", (SUB GPR32Opnd:$rt, ZERO, GPR32Opnd:$rt), 1>, ISA_MIPS1; def : MipsInstAlias<"negu $rt, $rs", (SUBu GPR32Opnd:$rt, ZERO, GPR32Opnd:$rs), 1>, ISA_MIPS1; def : MipsInstAlias<"negu $rt", (SUBu GPR32Opnd:$rt, ZERO, GPR32Opnd:$rt), 1>, ISA_MIPS1; def SGE : MipsAsmPseudoInst<(outs GPR32Opnd:$rd), (ins GPR32Opnd:$rs, GPR32Opnd:$rt), "sge\t$rd, $rs, $rt">, ISA_MIPS1; def : MipsInstAlias<"sge $rs, $rt", (SGE GPR32Opnd:$rs, GPR32Opnd:$rs, GPR32Opnd:$rt), 0>, ISA_MIPS1; def SGEImm : MipsAsmPseudoInst<(outs GPR32Opnd:$rd), (ins GPR32Opnd:$rs, simm32:$imm), "sge\t$rd, $rs, $imm">, GPR_32; def : MipsInstAlias<"sge $rs, $imm", (SGEImm GPR32Opnd:$rs, GPR32Opnd:$rs, simm32:$imm), 0>, GPR_32; def SGEU : MipsAsmPseudoInst<(outs GPR32Opnd:$rd), (ins GPR32Opnd:$rs, GPR32Opnd:$rt), "sgeu\t$rd, $rs, $rt">, ISA_MIPS1; def : MipsInstAlias<"sgeu $rs, $rt", (SGEU GPR32Opnd:$rs, GPR32Opnd:$rs, GPR32Opnd:$rt), 0>, ISA_MIPS1; def SGEUImm : MipsAsmPseudoInst<(outs GPR32Opnd:$rd), (ins GPR32Opnd:$rs, uimm32_coerced:$imm), "sgeu\t$rd, $rs, $imm">, GPR_32; def : MipsInstAlias<"sgeu $rs, $imm", (SGEUImm GPR32Opnd:$rs, GPR32Opnd:$rs, uimm32_coerced:$imm), 0>, GPR_32; def : MipsInstAlias< "sgt $rd, $rs, $rt", (SLT GPR32Opnd:$rd, GPR32Opnd:$rt, GPR32Opnd:$rs), 0>, ISA_MIPS1; def : MipsInstAlias< "sgt $rs, $rt", (SLT GPR32Opnd:$rs, GPR32Opnd:$rt, GPR32Opnd:$rs), 0>, ISA_MIPS1; def SGTImm : MipsAsmPseudoInst<(outs GPR32Opnd:$rd), (ins GPR32Opnd:$rs, simm32:$imm), "sgt\t$rd, $rs, $imm">, GPR_32; def : MipsInstAlias<"sgt $rs, $imm", (SGTImm GPR32Opnd:$rs, GPR32Opnd:$rs, simm32:$imm), 0>, GPR_32; def : MipsInstAlias< "sgtu $rd, $rs, $rt", (SLTu GPR32Opnd:$rd, GPR32Opnd:$rt, GPR32Opnd:$rs), 0>, ISA_MIPS1; def : MipsInstAlias< "sgtu $$rs, $rt", (SLTu GPR32Opnd:$rs, GPR32Opnd:$rt, GPR32Opnd:$rs), 0>, ISA_MIPS1; def SGTUImm : MipsAsmPseudoInst<(outs GPR32Opnd:$rd), (ins GPR32Opnd:$rs, uimm32_coerced:$imm), "sgtu\t$rd, $rs, $imm">, GPR_32; def : MipsInstAlias<"sgtu $rs, $imm", (SGTUImm GPR32Opnd:$rs, GPR32Opnd:$rs, uimm32_coerced:$imm), 0>, GPR_32; def : MipsInstAlias< "not $rt, $rs", (NOR GPR32Opnd:$rt, GPR32Opnd:$rs, ZERO), 0>, ISA_MIPS1; def : MipsInstAlias< "not $rt", (NOR GPR32Opnd:$rt, GPR32Opnd:$rt, ZERO), 0>, ISA_MIPS1; def : MipsInstAlias<"nop", (SLL ZERO, ZERO, 0), 1>, ISA_MIPS1; defm : OneOrTwoOperandMacroImmediateAlias<"add", ADDi>, ISA_MIPS1_NOT_32R6_64R6; defm : OneOrTwoOperandMacroImmediateAlias<"addu", ADDiu>, ISA_MIPS1; defm : OneOrTwoOperandMacroImmediateAlias<"and", ANDi>, ISA_MIPS1, GPR_32; defm : OneOrTwoOperandMacroImmediateAlias<"or", ORi>, ISA_MIPS1, GPR_32; defm : OneOrTwoOperandMacroImmediateAlias<"xor", XORi>, ISA_MIPS1, GPR_32; defm : OneOrTwoOperandMacroImmediateAlias<"slt", SLTi>, ISA_MIPS1, GPR_32; defm : OneOrTwoOperandMacroImmediateAlias<"sltu", SLTiu>, ISA_MIPS1, GPR_32; def : MipsInstAlias<"mfgc0 $rt, $rd", (MFGC0 GPR32Opnd:$rt, COP0Opnd:$rd, 0), 0>, ISA_MIPS32R5, ASE_VIRT; def : MipsInstAlias<"mtgc0 $rt, $rd", (MTGC0 COP0Opnd:$rd, GPR32Opnd:$rt, 0), 0>, ISA_MIPS32R5, ASE_VIRT; def : MipsInstAlias<"mfhgc0 $rt, $rd", (MFHGC0 GPR32Opnd:$rt, COP0Opnd:$rd, 0), 0>, ISA_MIPS32R5, ASE_VIRT; def : MipsInstAlias<"mthgc0 $rt, $rd", (MTHGC0 COP0Opnd:$rd, GPR32Opnd:$rt, 0), 0>, ISA_MIPS32R5, ASE_VIRT; def : MipsInstAlias<"mfc0 $rt, $rd", (MFC0 GPR32Opnd:$rt, COP0Opnd:$rd, 0), 0>, ISA_MIPS1; def : MipsInstAlias<"mtc0 $rt, $rd", (MTC0 COP0Opnd:$rd, GPR32Opnd:$rt, 0), 0>, ISA_MIPS1; def : MipsInstAlias<"mfc2 $rt, $rd", (MFC2 GPR32Opnd:$rt, COP2Opnd:$rd, 0), 0>, ISA_MIPS1; def : MipsInstAlias<"mtc2 $rt, $rd", (MTC2 COP2Opnd:$rd, GPR32Opnd:$rt, 0), 0>, ISA_MIPS1; def : MipsInstAlias<"b $offset", (BEQ ZERO, ZERO, brtarget:$offset), 0>, ISA_MIPS1; def : MipsInstAlias<"bnez $rs,$offset", (BNE GPR32Opnd:$rs, ZERO, brtarget:$offset), 0>, ISA_MIPS1; def : MipsInstAlias<"bnezl $rs, $offset", (BNEL GPR32Opnd:$rs, ZERO, brtarget:$offset), 1>, ISA_MIPS2; def : MipsInstAlias<"beqz $rs,$offset", (BEQ GPR32Opnd:$rs, ZERO, brtarget:$offset), 0>, ISA_MIPS1; def : MipsInstAlias<"beqzl $rs, $offset", (BEQL GPR32Opnd:$rs, ZERO, brtarget:$offset), 1>, ISA_MIPS2; def : MipsInstAlias<"syscall", (SYSCALL 0), 1>, ISA_MIPS1; def : MipsInstAlias<"break", (BREAK 0, 0), 1>, ISA_MIPS1; def : MipsInstAlias<"break $imm", (BREAK uimm10:$imm, 0), 1>, ISA_MIPS1; def : MipsInstAlias<"ei", (EI ZERO), 1>, ISA_MIPS32R2; def : MipsInstAlias<"di", (DI ZERO), 1>, ISA_MIPS32R2; def : MipsInstAlias<"teq $rs, $rt", (TEQ GPR32Opnd:$rs, GPR32Opnd:$rt, 0), 1>, ISA_MIPS2; def : MipsInstAlias<"tge $rs, $rt", (TGE GPR32Opnd:$rs, GPR32Opnd:$rt, 0), 1>, ISA_MIPS2; def : MipsInstAlias<"tgeu $rs, $rt", (TGEU GPR32Opnd:$rs, GPR32Opnd:$rt, 0), 1>, ISA_MIPS2; def : MipsInstAlias<"tlt $rs, $rt", (TLT GPR32Opnd:$rs, GPR32Opnd:$rt, 0), 1>, ISA_MIPS2; def : MipsInstAlias<"tltu $rs, $rt", (TLTU GPR32Opnd:$rs, GPR32Opnd:$rt, 0), 1>, ISA_MIPS2; def : MipsInstAlias<"tne $rs, $rt", (TNE GPR32Opnd:$rs, GPR32Opnd:$rt, 0), 1>, ISA_MIPS2; def : MipsInstAlias<"rdhwr $rt, $rs", (RDHWR GPR32Opnd:$rt, HWRegsOpnd:$rs, 0), 1>, ISA_MIPS1; } def : MipsInstAlias<"sub, $rd, $rs, $imm", (ADDi GPR32Opnd:$rd, GPR32Opnd:$rs, InvertedImOperand:$imm), 0>, ISA_MIPS1_NOT_32R6_64R6; def : MipsInstAlias<"sub $rs, $imm", (ADDi GPR32Opnd:$rs, GPR32Opnd:$rs, InvertedImOperand:$imm), 0>, ISA_MIPS1_NOT_32R6_64R6; def : MipsInstAlias<"subu, $rd, $rs, $imm", (ADDiu GPR32Opnd:$rd, GPR32Opnd:$rs, InvertedImOperand:$imm), 0>; def : MipsInstAlias<"subu $rs, $imm", (ADDiu GPR32Opnd:$rs, GPR32Opnd:$rs, InvertedImOperand:$imm), 0>; let AdditionalPredicates = [NotInMicroMips] in { def : MipsInstAlias<"sll $rd, $rt, $rs", (SLLV GPR32Opnd:$rd, GPR32Opnd:$rt, GPR32Opnd:$rs), 0>; def : MipsInstAlias<"sra $rd, $rt, $rs", (SRAV GPR32Opnd:$rd, GPR32Opnd:$rt, GPR32Opnd:$rs), 0>; def : MipsInstAlias<"srl $rd, $rt, $rs", (SRLV GPR32Opnd:$rd, GPR32Opnd:$rt, GPR32Opnd:$rs), 0>; def : MipsInstAlias<"sll $rd, $rt", (SLLV GPR32Opnd:$rd, GPR32Opnd:$rd, GPR32Opnd:$rt), 0>; def : MipsInstAlias<"sra $rd, $rt", (SRAV GPR32Opnd:$rd, GPR32Opnd:$rd, GPR32Opnd:$rt), 0>; def : MipsInstAlias<"srl $rd, $rt", (SRLV GPR32Opnd:$rd, GPR32Opnd:$rd, GPR32Opnd:$rt), 0>; def : MipsInstAlias<"seh $rd", (SEH GPR32Opnd:$rd, GPR32Opnd:$rd), 0>, ISA_MIPS32R2; def : MipsInstAlias<"seb $rd", (SEB GPR32Opnd:$rd, GPR32Opnd:$rd), 0>, ISA_MIPS32R2; } def : MipsInstAlias<"sdbbp", (SDBBP 0)>, ISA_MIPS32_NOT_32R6_64R6; let AdditionalPredicates = [NotInMicroMips] in def : MipsInstAlias<"sync", (SYNC 0), 1>, ISA_MIPS2; def : MipsInstAlias<"mulo $rs, $rt", (MULOMacro GPR32Opnd:$rs, GPR32Opnd:$rs, GPR32Opnd:$rt), 0>, ISA_MIPS1_NOT_32R6_64R6; def : MipsInstAlias<"mulou $rs, $rt", (MULOUMacro GPR32Opnd:$rs, GPR32Opnd:$rs, GPR32Opnd:$rt), 0>, ISA_MIPS1_NOT_32R6_64R6; let AdditionalPredicates = [NotInMicroMips] in def : MipsInstAlias<"hypcall", (HYPCALL 0), 1>, ISA_MIPS32R5, ASE_VIRT; //===----------------------------------------------------------------------===// // Assembler Pseudo Instructions //===----------------------------------------------------------------------===// // We use uimm32_coerced to accept a 33 bit signed number that is rendered into // a 32 bit number. class LoadImmediate32 : MipsAsmPseudoInst<(outs RO:$rt), (ins Od:$imm32), !strconcat(instr_asm, "\t$rt, $imm32")> ; def LoadImm32 : LoadImmediate32<"li", uimm32_coerced, GPR32Opnd>; class LoadAddressFromReg32 : MipsAsmPseudoInst<(outs RO:$rt), (ins MemOpnd:$addr), !strconcat(instr_asm, "\t$rt, $addr")> ; def LoadAddrReg32 : LoadAddressFromReg32<"la", mem, GPR32Opnd>; class LoadAddressFromImm32 : MipsAsmPseudoInst<(outs RO:$rt), (ins Od:$imm32), !strconcat(instr_asm, "\t$rt, $imm32")> ; def LoadAddrImm32 : LoadAddressFromImm32<"la", i32imm, GPR32Opnd>; def JalTwoReg : MipsAsmPseudoInst<(outs GPR32Opnd:$rd), (ins GPR32Opnd:$rs), "jal\t$rd, $rs"> ; def JalOneReg : MipsAsmPseudoInst<(outs), (ins GPR32Opnd:$rs), "jal\t$rs"> ; class NORIMM_DESC_BASE : MipsAsmPseudoInst<(outs RO:$rs), (ins RO:$rt, Imm:$imm), "nor\t$rs, $rt, $imm">; def NORImm : NORIMM_DESC_BASE, GPR_32; def : MipsInstAlias<"nor\t$rs, $imm", (NORImm GPR32Opnd:$rs, GPR32Opnd:$rs, simm32_relaxed:$imm)>, GPR_32; let hasDelaySlot = 1, isCTI = 1 in { def BneImm : MipsAsmPseudoInst<(outs GPR32Opnd:$rt), (ins imm64:$imm64, brtarget:$offset), "bne\t$rt, $imm64, $offset">; def BeqImm : MipsAsmPseudoInst<(outs GPR32Opnd:$rt), (ins imm64:$imm64, brtarget:$offset), "beq\t$rt, $imm64, $offset">; class CondBranchPseudo : MipsAsmPseudoInst<(outs), (ins GPR32Opnd:$rs, GPR32Opnd:$rt, brtarget:$offset), !strconcat(instr_asm, "\t$rs, $rt, $offset")>; } def BLT : CondBranchPseudo<"blt">; def BLE : CondBranchPseudo<"ble">; def BGE : CondBranchPseudo<"bge">; def BGT : CondBranchPseudo<"bgt">; def BLTU : CondBranchPseudo<"bltu">; def BLEU : CondBranchPseudo<"bleu">; def BGEU : CondBranchPseudo<"bgeu">; def BGTU : CondBranchPseudo<"bgtu">; def BLTL : CondBranchPseudo<"bltl">, ISA_MIPS2_NOT_32R6_64R6; def BLEL : CondBranchPseudo<"blel">, ISA_MIPS2_NOT_32R6_64R6; def BGEL : CondBranchPseudo<"bgel">, ISA_MIPS2_NOT_32R6_64R6; def BGTL : CondBranchPseudo<"bgtl">, ISA_MIPS2_NOT_32R6_64R6; def BLTUL: CondBranchPseudo<"bltul">, ISA_MIPS2_NOT_32R6_64R6; def BLEUL: CondBranchPseudo<"bleul">, ISA_MIPS2_NOT_32R6_64R6; def BGEUL: CondBranchPseudo<"bgeul">, ISA_MIPS2_NOT_32R6_64R6; def BGTUL: CondBranchPseudo<"bgtul">, ISA_MIPS2_NOT_32R6_64R6; let isCTI = 1 in class CondBranchImmPseudo : MipsAsmPseudoInst<(outs), (ins GPR32Opnd:$rs, imm64:$imm, brtarget:$offset), !strconcat(instr_asm, "\t$rs, $imm, $offset")>; def BEQLImmMacro : CondBranchImmPseudo<"beql">, ISA_MIPS2_NOT_32R6_64R6; def BNELImmMacro : CondBranchImmPseudo<"bnel">, ISA_MIPS2_NOT_32R6_64R6; def BLTImmMacro : CondBranchImmPseudo<"blt">; def BLEImmMacro : CondBranchImmPseudo<"ble">; def BGEImmMacro : CondBranchImmPseudo<"bge">; def BGTImmMacro : CondBranchImmPseudo<"bgt">; def BLTUImmMacro : CondBranchImmPseudo<"bltu">; def BLEUImmMacro : CondBranchImmPseudo<"bleu">; def BGEUImmMacro : CondBranchImmPseudo<"bgeu">; def BGTUImmMacro : CondBranchImmPseudo<"bgtu">; def BLTLImmMacro : CondBranchImmPseudo<"bltl">, ISA_MIPS2_NOT_32R6_64R6; def BLELImmMacro : CondBranchImmPseudo<"blel">, ISA_MIPS2_NOT_32R6_64R6; def BGELImmMacro : CondBranchImmPseudo<"bgel">, ISA_MIPS2_NOT_32R6_64R6; def BGTLImmMacro : CondBranchImmPseudo<"bgtl">, ISA_MIPS2_NOT_32R6_64R6; def BLTULImmMacro : CondBranchImmPseudo<"bltul">, ISA_MIPS2_NOT_32R6_64R6; def BLEULImmMacro : CondBranchImmPseudo<"bleul">, ISA_MIPS2_NOT_32R6_64R6; def BGEULImmMacro : CondBranchImmPseudo<"bgeul">, ISA_MIPS2_NOT_32R6_64R6; def BGTULImmMacro : CondBranchImmPseudo<"bgtul">, ISA_MIPS2_NOT_32R6_64R6; // FIXME: Predicates are removed because instructions are matched regardless of // predicates, because PredicateControl was not in the hierarchy. This was // done to emit more precise error message from expansion function. // Once the tablegen-erated errors are made better, this needs to be fixed and // predicates needs to be restored. def SDivMacro : MipsAsmPseudoInst<(outs GPR32NonZeroOpnd:$rd), (ins GPR32Opnd:$rs, GPR32Opnd:$rt), "div\t$rd, $rs, $rt">, ISA_MIPS1_NOT_32R6_64R6; def SDivIMacro : MipsAsmPseudoInst<(outs GPR32Opnd:$rd), (ins GPR32Opnd:$rs, simm32:$imm), "div\t$rd, $rs, $imm">, ISA_MIPS1_NOT_32R6_64R6; def UDivMacro : MipsAsmPseudoInst<(outs GPR32Opnd:$rd), (ins GPR32Opnd:$rs, GPR32Opnd:$rt), "divu\t$rd, $rs, $rt">, ISA_MIPS1_NOT_32R6_64R6; def UDivIMacro : MipsAsmPseudoInst<(outs GPR32Opnd:$rd), (ins GPR32Opnd:$rs, simm32:$imm), "divu\t$rd, $rs, $imm">, ISA_MIPS1_NOT_32R6_64R6; def : MipsInstAlias<"div $rs, $rt", (SDIV GPR32ZeroOpnd:$rs, GPR32Opnd:$rt), 0>, ISA_MIPS1_NOT_32R6_64R6; def : MipsInstAlias<"div $rs, $rt", (SDivMacro GPR32NonZeroOpnd:$rs, GPR32NonZeroOpnd:$rs, GPR32Opnd:$rt), 0>, ISA_MIPS1_NOT_32R6_64R6; def : MipsInstAlias<"div $rd, $imm", (SDivIMacro GPR32Opnd:$rd, GPR32Opnd:$rd, simm32:$imm), 0>, ISA_MIPS1_NOT_32R6_64R6; def : MipsInstAlias<"divu $rt, $rs", (UDIV GPR32ZeroOpnd:$rt, GPR32Opnd:$rs), 0>, ISA_MIPS1_NOT_32R6_64R6; def : MipsInstAlias<"divu $rt, $rs", (UDivMacro GPR32NonZeroOpnd:$rt, GPR32NonZeroOpnd:$rt, GPR32Opnd:$rs), 0>, ISA_MIPS1_NOT_32R6_64R6; def : MipsInstAlias<"divu $rd, $imm", (UDivIMacro GPR32Opnd:$rd, GPR32Opnd:$rd, simm32:$imm), 0>, ISA_MIPS1_NOT_32R6_64R6; def SRemMacro : MipsAsmPseudoInst<(outs GPR32Opnd:$rd), (ins GPR32Opnd:$rs, GPR32Opnd:$rt), "rem\t$rd, $rs, $rt">, ISA_MIPS1_NOT_32R6_64R6; def SRemIMacro : MipsAsmPseudoInst<(outs GPR32Opnd:$rd), (ins GPR32Opnd:$rs, simm32_relaxed:$imm), "rem\t$rd, $rs, $imm">, ISA_MIPS1_NOT_32R6_64R6; def URemMacro : MipsAsmPseudoInst<(outs GPR32Opnd:$rd), (ins GPR32Opnd:$rs, GPR32Opnd:$rt), "remu\t$rd, $rs, $rt">, ISA_MIPS1_NOT_32R6_64R6; def URemIMacro : MipsAsmPseudoInst<(outs GPR32Opnd:$rd), (ins GPR32Opnd:$rs, simm32_relaxed:$imm), "remu\t$rd, $rs, $imm">, ISA_MIPS1_NOT_32R6_64R6; def : MipsInstAlias<"rem $rt, $rs", (SRemMacro GPR32Opnd:$rt, GPR32Opnd:$rt, GPR32Opnd:$rs), 0>, ISA_MIPS1_NOT_32R6_64R6; def : MipsInstAlias<"rem $rd, $imm", (SRemIMacro GPR32Opnd:$rd, GPR32Opnd:$rd, simm32_relaxed:$imm), 0>, ISA_MIPS1_NOT_32R6_64R6; def : MipsInstAlias<"remu $rt, $rs", (URemMacro GPR32Opnd:$rt, GPR32Opnd:$rt, GPR32Opnd:$rs), 0>, ISA_MIPS1_NOT_32R6_64R6; def : MipsInstAlias<"remu $rd, $imm", (URemIMacro GPR32Opnd:$rd, GPR32Opnd:$rd, simm32_relaxed:$imm), 0>, ISA_MIPS1_NOT_32R6_64R6; def Ulh : MipsAsmPseudoInst<(outs GPR32Opnd:$rt), (ins mem:$addr), "ulh\t$rt, $addr">; //, ISA_MIPS1_NOT_32R6_64R6; def Ulhu : MipsAsmPseudoInst<(outs GPR32Opnd:$rt), (ins mem:$addr), "ulhu\t$rt, $addr">; //, ISA_MIPS1_NOT_32R6_64R6; def Ulw : MipsAsmPseudoInst<(outs GPR32Opnd:$rt), (ins mem:$addr), "ulw\t$rt, $addr">; //, ISA_MIPS1_NOT_32R6_64R6; def Ush : MipsAsmPseudoInst<(outs GPR32Opnd:$rt), (ins mem:$addr), "ush\t$rt, $addr">; //, ISA_MIPS1_NOT_32R6_64R6; def Usw : MipsAsmPseudoInst<(outs GPR32Opnd:$rt), (ins mem:$addr), "usw\t$rt, $addr">; //, ISA_MIPS1_NOT_32R6_64R6; def LDMacro : MipsAsmPseudoInst<(outs GPR32Opnd:$rt), (ins mem_simm16:$addr), "ld $rt, $addr">, ISA_MIPS1_NOT_MIPS3; def SDMacro : MipsAsmPseudoInst<(outs GPR32Opnd:$rt), (ins mem_simm16:$addr), "sd $rt, $addr">, ISA_MIPS1_NOT_MIPS3; //===----------------------------------------------------------------------===// // Arbitrary patterns that map to one or more instructions //===----------------------------------------------------------------------===// // Load/store pattern templates. class LoadRegImmPat : MipsPat<(ValTy (Node addrRegImm:$a)), (LoadInst addrRegImm:$a)>; class StoreRegImmPat : MipsPat<(store ValTy:$v, addrRegImm:$a), (StoreInst ValTy:$v, addrRegImm:$a)>; // Materialize constants. multiclass MaterializeImms { // Constant synthesis previously relied on the ordering of the patterns below. // By making the predicates they use non-overlapping, the patterns were // reordered so that the effect of the newly introduced predicates can be // observed. // Arbitrary immediates def : MipsPat<(VT LUiORiPred:$imm), (ORiOp (LUiOp (HI16 imm:$imm)), (LO16 imm:$imm))>; // Bits 32-16 set, sign/zero extended. def : MipsPat<(VT LUiPred:$imm), (LUiOp (HI16 imm:$imm))>; // Small immediates def : MipsPat<(VT ORiPred:$imm), (ORiOp ZEROReg, imm:$imm)>; def : MipsPat<(VT immSExt16:$imm), (ADDiuOp ZEROReg, imm:$imm)>; } let AdditionalPredicates = [NotInMicroMips] in defm : MaterializeImms, ISA_MIPS1; // Carry MipsPatterns let AdditionalPredicates = [NotInMicroMips] in { def : MipsPat<(subc GPR32:$lhs, GPR32:$rhs), (SUBu GPR32:$lhs, GPR32:$rhs)>, ISA_MIPS1; } def : MipsPat<(addc GPR32:$lhs, GPR32:$rhs), (ADDu GPR32:$lhs, GPR32:$rhs)>, ISA_MIPS1, ASE_NOT_DSP; def : MipsPat<(addc GPR32:$src, immSExt16:$imm), (ADDiu GPR32:$src, imm:$imm)>, ISA_MIPS1, ASE_NOT_DSP; // Support multiplication for pre-Mips32 targets that don't have // the MUL instruction. def : MipsPat<(mul GPR32:$lhs, GPR32:$rhs), (PseudoMFLO (PseudoMULT GPR32:$lhs, GPR32:$rhs))>, ISA_MIPS1_NOT_32R6_64R6; // SYNC def : MipsPat<(MipsSync (i32 immz)), (SYNC 0)>, ISA_MIPS2; // Call def : MipsPat<(MipsJmpLink (i32 texternalsym:$dst)), (JAL texternalsym:$dst)>, ISA_MIPS1; //def : MipsPat<(MipsJmpLink GPR32:$dst), // (JALR GPR32:$dst)>; // Tail call let AdditionalPredicates = [NotInMicroMips] in { def : MipsPat<(MipsTailCall (iPTR tglobaladdr:$dst)), (TAILCALL tglobaladdr:$dst)>, ISA_MIPS1; def : MipsPat<(MipsTailCall (iPTR texternalsym:$dst)), (TAILCALL texternalsym:$dst)>, ISA_MIPS1; } // hi/lo relocs multiclass MipsHiLoRelocs { def : MipsPat<(MipsHi tglobaladdr:$in), (Lui tglobaladdr:$in)>; def : MipsPat<(MipsHi tblockaddress:$in), (Lui tblockaddress:$in)>; def : MipsPat<(MipsHi tjumptable:$in), (Lui tjumptable:$in)>; def : MipsPat<(MipsHi tconstpool:$in), (Lui tconstpool:$in)>; def : MipsPat<(MipsHi texternalsym:$in), (Lui texternalsym:$in)>; def : MipsPat<(MipsLo tglobaladdr:$in), (Addiu ZeroReg, tglobaladdr:$in)>; def : MipsPat<(MipsLo tblockaddress:$in), (Addiu ZeroReg, tblockaddress:$in)>; def : MipsPat<(MipsLo tjumptable:$in), (Addiu ZeroReg, tjumptable:$in)>; def : MipsPat<(MipsLo tconstpool:$in), (Addiu ZeroReg, tconstpool:$in)>; def : MipsPat<(MipsLo tglobaltlsaddr:$in), (Addiu ZeroReg, tglobaltlsaddr:$in)>; def : MipsPat<(MipsLo texternalsym:$in), (Addiu ZeroReg, texternalsym:$in)>; def : MipsPat<(add GPROpnd:$hi, (MipsLo tglobaladdr:$lo)), (Addiu GPROpnd:$hi, tglobaladdr:$lo)>; def : MipsPat<(add GPROpnd:$hi, (MipsLo tblockaddress:$lo)), (Addiu GPROpnd:$hi, tblockaddress:$lo)>; def : MipsPat<(add GPROpnd:$hi, (MipsLo tjumptable:$lo)), (Addiu GPROpnd:$hi, tjumptable:$lo)>; def : MipsPat<(add GPROpnd:$hi, (MipsLo tconstpool:$lo)), (Addiu GPROpnd:$hi, tconstpool:$lo)>; def : MipsPat<(add GPROpnd:$hi, (MipsLo tglobaltlsaddr:$lo)), (Addiu GPROpnd:$hi, tglobaltlsaddr:$lo)>; } // wrapper_pic class WrapperPat: MipsPat<(MipsWrapper RC:$gp, node:$in), (ADDiuOp RC:$gp, node:$in)>; let AdditionalPredicates = [NotInMicroMips] in { defm : MipsHiLoRelocs, ISA_MIPS1; def : MipsPat<(MipsGotHi tglobaladdr:$in), (LUi tglobaladdr:$in)>, ISA_MIPS1; def : MipsPat<(MipsGotHi texternalsym:$in), (LUi texternalsym:$in)>, ISA_MIPS1; def : MipsPat<(MipsTlsHi tglobaltlsaddr:$in), (LUi tglobaltlsaddr:$in)>, ISA_MIPS1; // gp_rel relocs def : MipsPat<(add GPR32:$gp, (MipsGPRel tglobaladdr:$in)), (ADDiu GPR32:$gp, tglobaladdr:$in)>, ISA_MIPS1, ABI_NOT_N64; def : MipsPat<(add GPR32:$gp, (MipsGPRel tconstpool:$in)), (ADDiu GPR32:$gp, tconstpool:$in)>, ISA_MIPS1, ABI_NOT_N64; def : WrapperPat, ISA_MIPS1; def : WrapperPat, ISA_MIPS1; def : WrapperPat, ISA_MIPS1; def : WrapperPat, ISA_MIPS1; def : WrapperPat, ISA_MIPS1; def : WrapperPat, ISA_MIPS1; // Mips does not have "not", so we expand our way def : MipsPat<(not GPR32:$in), (NOR GPR32Opnd:$in, ZERO)>, ISA_MIPS1; } // extended loads let AdditionalPredicates = [NotInMicroMips] in { def : MipsPat<(i32 (extloadi1 addr:$src)), (LBu addr:$src)>, ISA_MIPS1; def : MipsPat<(i32 (extloadi8 addr:$src)), (LBu addr:$src)>, ISA_MIPS1; def : MipsPat<(i32 (extloadi16 addr:$src)), (LHu addr:$src)>, ISA_MIPS1; // peepholes def : MipsPat<(store (i32 0), addr:$dst), (SW ZERO, addr:$dst)>, ISA_MIPS1; } // brcond patterns multiclass BrcondPats { def : MipsPat<(brcond (i32 (setne RC:$lhs, 0)), bb:$dst), (BNEOp RC:$lhs, ZEROReg, bb:$dst)>; def : MipsPat<(brcond (i32 (seteq RC:$lhs, 0)), bb:$dst), (BEQOp RC:$lhs, ZEROReg, bb:$dst)>; def : MipsPat<(brcond (i32 (setge RC:$lhs, RC:$rhs)), bb:$dst), (BEQOp1 (SLTOp RC:$lhs, RC:$rhs), ZERO, bb:$dst)>; def : MipsPat<(brcond (i32 (setuge RC:$lhs, RC:$rhs)), bb:$dst), (BEQOp1 (SLTuOp RC:$lhs, RC:$rhs), ZERO, bb:$dst)>; def : MipsPat<(brcond (i32 (setge RC:$lhs, immSExt16:$rhs)), bb:$dst), (BEQOp1 (SLTiOp RC:$lhs, immSExt16:$rhs), ZERO, bb:$dst)>; def : MipsPat<(brcond (i32 (setuge RC:$lhs, immSExt16:$rhs)), bb:$dst), (BEQOp1 (SLTiuOp RC:$lhs, immSExt16:$rhs), ZERO, bb:$dst)>; def : MipsPat<(brcond (i32 (setgt RC:$lhs, immSExt16Plus1:$rhs)), bb:$dst), (BEQOp1 (SLTiOp RC:$lhs, (Plus1 imm:$rhs)), ZERO, bb:$dst)>; def : MipsPat<(brcond (i32 (setugt RC:$lhs, immSExt16Plus1:$rhs)), bb:$dst), (BEQOp1 (SLTiuOp RC:$lhs, (Plus1 imm:$rhs)), ZERO, bb:$dst)>; def : MipsPat<(brcond (i32 (setle RC:$lhs, RC:$rhs)), bb:$dst), (BEQOp1 (SLTOp RC:$rhs, RC:$lhs), ZERO, bb:$dst)>; def : MipsPat<(brcond (i32 (setule RC:$lhs, RC:$rhs)), bb:$dst), (BEQOp1 (SLTuOp RC:$rhs, RC:$lhs), ZERO, bb:$dst)>; def : MipsPat<(brcond RC:$cond, bb:$dst), (BNEOp RC:$cond, ZEROReg, bb:$dst)>; } let AdditionalPredicates = [NotInMicroMips] in { defm : BrcondPats, ISA_MIPS1; def : MipsPat<(brcond (i32 (setlt i32:$lhs, 1)), bb:$dst), (BLEZ i32:$lhs, bb:$dst)>, ISA_MIPS1; def : MipsPat<(brcond (i32 (setgt i32:$lhs, -1)), bb:$dst), (BGEZ i32:$lhs, bb:$dst)>, ISA_MIPS1; } // setcc patterns multiclass SeteqPats { def : MipsPat<(seteq RC:$lhs, 0), (SLTiuOp RC:$lhs, 1)>; def : MipsPat<(setne RC:$lhs, 0), (SLTuOp ZEROReg, RC:$lhs)>; def : MipsPat<(seteq RC:$lhs, RC:$rhs), (SLTiuOp (XOROp RC:$lhs, RC:$rhs), 1)>; def : MipsPat<(setne RC:$lhs, RC:$rhs), (SLTuOp ZEROReg, (XOROp RC:$lhs, RC:$rhs))>; } multiclass SetlePats { def : MipsPat<(setle RC:$lhs, RC:$rhs), (XORiOp (SLTOp RC:$rhs, RC:$lhs), 1)>; def : MipsPat<(setule RC:$lhs, RC:$rhs), (XORiOp (SLTuOp RC:$rhs, RC:$lhs), 1)>; } multiclass SetgtPats { def : MipsPat<(setgt RC:$lhs, RC:$rhs), (SLTOp RC:$rhs, RC:$lhs)>; def : MipsPat<(setugt RC:$lhs, RC:$rhs), (SLTuOp RC:$rhs, RC:$lhs)>; } multiclass SetgePats { def : MipsPat<(setge RC:$lhs, RC:$rhs), (XORiOp (SLTOp RC:$lhs, RC:$rhs), 1)>; def : MipsPat<(setuge RC:$lhs, RC:$rhs), (XORiOp (SLTuOp RC:$lhs, RC:$rhs), 1)>; } multiclass SetgeImmPats { def : MipsPat<(setge RC:$lhs, immSExt16:$rhs), (XORiOp (SLTiOp RC:$lhs, immSExt16:$rhs), 1)>; def : MipsPat<(setuge RC:$lhs, immSExt16:$rhs), (XORiOp (SLTiuOp RC:$lhs, immSExt16:$rhs), 1)>; } let AdditionalPredicates = [NotInMicroMips] in { defm : SeteqPats, ISA_MIPS1; defm : SetlePats, ISA_MIPS1; defm : SetgtPats, ISA_MIPS1; defm : SetgePats, ISA_MIPS1; defm : SetgeImmPats, ISA_MIPS1; // bswap pattern def : MipsPat<(bswap GPR32:$rt), (ROTR (WSBH GPR32:$rt), 16)>, ISA_MIPS32R2; } // Load halfword/word patterns. let AdditionalPredicates = [NotInMicroMips] in { let AddedComplexity = 40 in { def : LoadRegImmPat, ISA_MIPS1; def : LoadRegImmPat, ISA_MIPS1; def : LoadRegImmPat, ISA_MIPS1; def : LoadRegImmPat, ISA_MIPS1; def : LoadRegImmPat, ISA_MIPS1; } // Atomic load patterns. def : MipsPat<(atomic_load_8 addr:$a), (LB addr:$a)>, ISA_MIPS1; def : MipsPat<(atomic_load_16 addr:$a), (LH addr:$a)>, ISA_MIPS1; def : MipsPat<(atomic_load_32 addr:$a), (LW addr:$a)>, ISA_MIPS1; // Atomic store patterns. def : MipsPat<(atomic_store_8 addr:$a, GPR32:$v), (SB GPR32:$v, addr:$a)>, ISA_MIPS1; def : MipsPat<(atomic_store_16 addr:$a, GPR32:$v), (SH GPR32:$v, addr:$a)>, ISA_MIPS1; def : MipsPat<(atomic_store_32 addr:$a, GPR32:$v), (SW GPR32:$v, addr:$a)>, ISA_MIPS1; } //===----------------------------------------------------------------------===// // Floating Point Support //===----------------------------------------------------------------------===// include "MipsInstrFPU.td" include "Mips64InstrInfo.td" include "MipsCondMov.td" include "Mips32r6InstrInfo.td" include "Mips64r6InstrInfo.td" // // Mips16 include "Mips16InstrFormats.td" include "Mips16InstrInfo.td" // DSP include "MipsDSPInstrFormats.td" include "MipsDSPInstrInfo.td" // MSA include "MipsMSAInstrFormats.td" include "MipsMSAInstrInfo.td" // EVA include "MipsEVAInstrFormats.td" include "MipsEVAInstrInfo.td" // MT include "MipsMTInstrFormats.td" include "MipsMTInstrInfo.td" // Micromips include "MicroMipsInstrFormats.td" include "MicroMipsInstrInfo.td" include "MicroMipsInstrFPU.td" // Micromips r6 include "MicroMips32r6InstrFormats.td" include "MicroMips32r6InstrInfo.td" // Micromips DSP include "MicroMipsDSPInstrFormats.td" include "MicroMipsDSPInstrInfo.td" Index: head/contrib/llvm/lib/Target/Mips/MipsScheduleGeneric.td =================================================================== --- head/contrib/llvm/lib/Target/Mips/MipsScheduleGeneric.td (revision 354978) +++ head/contrib/llvm/lib/Target/Mips/MipsScheduleGeneric.td (revision 354979) @@ -1,1614 +1,1619 @@ //=- MipsScheduleGeneric.td - Generic Scheduling Definitions -*- tablegen -*-=// // // 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 // //===----------------------------------------------------------------------===// // // This file describes the interAptiv processor in a manner of speaking. It // describes a hypothetical version of the in-order MIPS32R2 interAptiv with all // branches of the MIPS ISAs, ASEs and ISA variants. The itinerary lists are // broken down into per ISA lists, so that this file can be used to rapidly // develop new schedule models. // //===----------------------------------------------------------------------===// def MipsGenericModel : SchedMachineModel { int IssueWidth = 1; int MicroOpBufferSize = 0; // These figures assume an L1 hit. int LoadLatency = 2; int MispredictPenalty = 4; int HighLatency = 37; list UnsupportedFeatures = []; let CompleteModel = 1; let PostRAScheduler = 1; // FIXME: Remove when all errors have been fixed. let FullInstRWOverlapCheck = 1; } let SchedModel = MipsGenericModel in { // ALU Pipeline // ============ def GenericALU : ProcResource<1> { let BufferSize = 1; } def GenericIssueALU : ProcResource<1> { let Super = GenericALU; } def GenericWriteALU : SchedWriteRes<[GenericIssueALU]>; // add, addi, addiu, addu, and, andi, clo, clz, ext, ins, lui, nor, or, ori, // rotr, rotrv, seb, seh, sll, sllv, slt, slti, sltiu, sltu, sra, srav, srl, // srlv, ssnop, sub, subu, wsbh, xor, xori def : InstRW<[GenericWriteALU], (instrs ADD, ADDi, ADDiu, ADDu, AND, ANDi, CLO, CLZ, EXT, INS, LEA_ADDiu, LUi, NOP, NOR, OR, ORi, ROTR, ROTRV, SEB, SEH, SLL, SLLV, SLT, SLTi, SLTiu, SLTu, SRA, SRAV, SRL, SRLV, SSNOP, SUB, SUBu, WSBH, XOR, XORi)>; def : InstRW<[GenericWriteALU], (instrs COPY)>; // MIPSR6 // ====== // addiupc, align, aluipc, aui, auipc, bitswap, clo, clz, lsa, seleqz, selnez def : InstRW<[GenericWriteALU], (instrs ADDIUPC, ALIGN, ALUIPC, AUI, AUIPC, BITSWAP, CLO_R6, CLZ_R6, LSA_R6, SELEQZ, SELNEZ)>; // MIPS16e // ======= def : InstRW<[GenericWriteALU], (instrs AddiuRxImmX16, AddiuRxRxImm16, AddiuRxRxImmX16, AddiuRxRyOffMemX16, AddiuRxPcImmX16, AddiuSpImm16, AddiuSpImmX16, AdduRxRyRz16, AndRxRxRy16, CmpRxRy16, CmpiRxImm16, CmpiRxImmX16, LiRxImm16, LiRxImmX16, LiRxImmAlignX16, Move32R16, MoveR3216, Mfhi16, Mflo16, NegRxRy16, NotRxRy16, OrRxRxRy16, SebRx16, SehRx16, SllX16, SllvRxRy16, SltiRxImm16, SltiRxImmX16, SltiCCRxImmX16, SltiuRxImm16, SltiuRxImmX16, SltiuCCRxImmX16, SltRxRy16, SltCCRxRy16, SltuRxRy16, SltuRxRyRz16, SltuCCRxRy16, SravRxRy16, SraX16, SrlvRxRy16, SrlX16, SubuRxRyRz16, XorRxRxRy16)>; def : InstRW<[GenericWriteALU], (instrs Constant32, LwConstant32, GotPrologue16, CONSTPOOL_ENTRY)>; // microMIPS // ========= def : InstRW<[GenericWriteALU], (instrs ADDIUPC_MM, ADDIUR1SP_MM, ADDIUR2_MM, ADDIUS5_MM, ADDIUSP_MM, ADDU16_MM, ADD_MM, ADDi_MM, ADDiu_MM, ADDu_MM, AND16_MM, ANDI16_MM, AND_MM, ANDi_MM, CLO_MM, CLZ_MM, EXT_MM, INS_MM, LEA_ADDiu_MM, LI16_MM, LUi_MM, MOVE16_MM, MOVEP_MM, NOR_MM, NOT16_MM, OR16_MM, OR_MM, ORi_MM, ROTRV_MM, ROTR_MM, SEB_MM, SEH_MM, SLL16_MM, SLLV_MM, SLL_MM, SLT_MM, SLTi_MM, SLTiu_MM, SLTu_MM, SRAV_MM, SRA_MM, SRL16_MM, SRLV_MM, SRL_MM, SSNOP_MM, SUBU16_MM, SUB_MM, SUBu_MM, WSBH_MM, XOR16_MM, XOR_MM, XORi_MM)>; // microMIPS32r6 // ============= def : InstRW<[GenericWriteALU], (instrs ADDIUPC_MMR6, ADDIU_MMR6, ADDU16_MMR6, ADDU_MMR6, ADD_MMR6, ALIGN_MMR6, ALUIPC_MMR6, AND16_MMR6, ANDI16_MMR6, ANDI_MMR6, AND_MMR6, AUIPC_MMR6, AUI_MMR6, BITSWAP_MMR6, CLO_MMR6, CLZ_MMR6, EXT_MMR6, INS_MMR6, LI16_MMR6, LSA_MMR6, LUI_MMR6, MOVE16_MMR6, NOR_MMR6, NOT16_MMR6, OR16_MMR6, ORI_MMR6, OR_MMR6, SELEQZ_MMR6, SELNEZ_MMR6, SLL16_MMR6, SLL_MMR6, SRL16_MMR6, SSNOP_MMR6, SUBU16_MMR6, SUBU_MMR6, SUB_MMR6, WSBH_MMR6, XOR16_MMR6, XORI_MMR6, XOR_MMR6)>; // MIPS64 // ====== def : InstRW<[GenericWriteALU], (instrs AND64, ANDi64, DEXT64_32, DSLL64_32, ORi64, SEB64, SEH64, SLL64_32, SLL64_64, SLT64, SLTi64, SLTiu64, SLTu64, XOR64, XORi64)>; def : InstRW<[GenericWriteALU], (instrs DADD, DADDi, DADDiu, DADDu, DCLO, DCLZ, DEXT, DEXTM, DEXTU, DINS, DINSM, DINSU, DROTR, DROTR32, DROTRV, DSBH, DSHD, DSLL, DSLL32, DSLLV, DSRA, DSRA32, DSRAV, DSRL, DSRL32, DSRLV, DSUB, DSUBu, LEA_ADDiu64, LUi64, NOR64, OR64)>; // MIPS64R6 // ======== def : InstRW<[GenericWriteALU], (instrs DALIGN, DAHI, DATI, DAUI, DCLO_R6, DCLZ_R6, DBITSWAP, DLSA, DLSA_R6, SELEQZ64, SELNEZ64)>; def GenericMDU : ProcResource<1> { let BufferSize = 1; } def GenericIssueMDU : ProcResource<1> { let Super = GenericALU; } def GenericIssueDIV : ProcResource<1> { let Super = GenericMDU; } def GenericWriteHILO : SchedWriteRes<[GenericIssueMDU]>; def GenericWriteALULong : SchedWriteRes<[GenericIssueALU]> { let Latency = 5; } def GenericWriteMove : SchedWriteRes<[GenericIssueALU]> { let Latency = 2; } def GenericWriteMul : SchedWriteRes<[GenericIssueMDU]> { let Latency = 4; } def : InstRW<[GenericWriteHILO], (instrs MADD, MADDU, MSUB, MSUBU)>; def : InstRW<[GenericWriteHILO], (instrs PseudoMADD_MM, PseudoMADDU_MM, PseudoMSUB_MM, PseudoMSUBU_MM, PseudoMULT_MM, PseudoMULTu_MM)>; def : InstRW<[GenericWriteHILO], (instrs PseudoMADD, PseudoMADDU, PseudoMSUB, PseudoMSUBU, PseudoMULT, PseudoMULTu)>; def GenericWriteMDUtoGPR : SchedWriteRes<[GenericIssueMDU]> { let Latency = 5; } def GenericWriteDIV : SchedWriteRes<[GenericIssueDIV]> { // Estimated worst case let Latency = 33; let ResourceCycles = [33]; } def GenericWriteDIVU : SchedWriteRes<[GenericIssueDIV]> { // Estimated worst case let Latency = 31; let ResourceCycles = [31]; } // mul def : InstRW<[GenericWriteMDUtoGPR], (instrs MUL)>; // mult, multu def : InstRW<[GenericWriteMul], (instrs MULT, MULTu)>; // div, sdiv def : InstRW<[GenericWriteDIV], (instrs PseudoSDIV, SDIV)>; def : InstRW<[GenericWriteDIVU], (instrs PseudoUDIV, UDIV)>; // mfhi, mflo, movn, mthi, mtlo, rdwhr def : InstRW<[GenericWriteALULong], (instrs MFHI, MFLO, PseudoMFHI, PseudoMFLO)>; def : InstRW<[GenericWriteALULong], (instrs PseudoMFHI_MM, PseudoMFLO_MM)>; def : InstRW<[GenericWriteMove], (instrs MTHI, MTLO, RDHWR, PseudoMTLOHI)>; def : InstRW<[GenericWriteMove], (instrs PseudoMTLOHI_MM)>; def : InstRW<[GenericWriteALU], (instrs MOVN_I_I, MOVZ_I_I)>; // MIPSR6 // ====== // muh, muhu, mulu, mul def : InstRW<[GenericWriteMul], (instrs MUH, MUHU, MULU, MUL_R6)>; // divu, udiv def : InstRW<[GenericWriteDIV], (instrs MOD, MODU, DIV, DIVU)>; // MIPS16e // ======= def : InstRW<[GenericWriteHILO], (instrs MultRxRy16, MultuRxRy16, MultRxRyRz16, MultuRxRyRz16)>; def : InstRW<[GenericWriteDIV], (instrs DivRxRy16)>; def : InstRW<[GenericWriteDIVU], (instrs DivuRxRy16)>; // microMIPS // ========= def : InstRW<[GenericWriteMul], (instrs MULT_MM, MULTu_MM, MADD_MM, MADDU_MM, MSUB_MM, MSUBU_MM)>; def : InstRW<[GenericWriteALULong], (instrs MUL_MM)>; def : InstRW<[GenericWriteDIV], (instrs SDIV_MM, SDIV_MM_Pseudo)>; def : InstRW<[GenericWriteDIVU], (instrs UDIV_MM, UDIV_MM_Pseudo)>; def : InstRW<[GenericWriteMove], (instrs MFHI16_MM, MFLO16_MM, MOVF_I_MM, MOVT_I_MM, MFHI_MM, MFLO_MM, MTHI_MM, MTLO_MM)>; def : InstRW<[GenericWriteMove], (instrs RDHWR_MM)>; // microMIPS32r6 // ============= def : InstRW<[GenericWriteMul], (instrs MUHU_MMR6, MUH_MMR6, MULU_MMR6, MUL_MMR6)>; def : InstRW<[GenericWriteDIV], (instrs MODU_MMR6, MOD_MMR6, DIVU_MMR6, DIV_MMR6)>; def : InstRW<[GenericWriteMove], (instrs RDHWR_MMR6)>; // MIPS64 // ====== def : InstRW<[GenericWriteHILO], (instrs DMULU, DMULT, DMULTu, PseudoDMULT, PseudoDMULTu)>; def : InstRW<[GenericWriteDIV], (instrs DSDIV, PseudoDSDIV)>; def : InstRW<[GenericWriteDIVU], (instrs DUDIV, PseudoDUDIV)>; def : InstRW<[GenericWriteALULong], (instrs MFHI64, MFLO64, PseudoMFHI64, PseudoMFLO64, PseudoMTLOHI64)>; def : InstRW<[GenericWriteMove], (instrs MTHI64, MTLO64, RDHWR64)>; // mov[zn] def : InstRW<[GenericWriteALU], (instrs MOVN_I_I64, MOVN_I64_I, MOVN_I64_I64, MOVZ_I_I64, MOVZ_I64_I, MOVZ_I64_I64)>; // MIPS64R6 // ======== def : InstRW<[GenericWriteMDUtoGPR], (instrs DMUH, DMUHU, DMUL_R6)>; def : InstRW<[GenericWriteDIV], (instrs DDIV, DMOD)>; def : InstRW<[GenericWriteDIVU], (instrs DDIVU, DMODU)>; // CTISTD Pipeline // --------------- def GenericIssueCTISTD : ProcResource<1> { let Super = GenericALU; } def GenericLDST : ProcResource<1> { let BufferSize = 1; } def GenericIssueLDST : ProcResource<1> { let Super = GenericLDST; } def GenericWriteJump : SchedWriteRes<[GenericIssueCTISTD]>; def GenericWriteJumpAndLink : SchedWriteRes<[GenericIssueCTISTD]> { let Latency = 2; } // b, beq, beql, bg[et]z, bl[et]z, bne, bnel, j, syscall, jal, bltzal, jalx, // jalr, jr.hb, jr, jalr.hb, jarlc, jialc def : InstRW<[GenericWriteJump], (instrs B, BAL, BAL_BR, BEQ, BNE, BGTZ, BGEZ, BLEZ, BLTZ, BLTZAL, J, JALX, JR, JR_HB, ERET, ERet, ERETNC, DERET)>; def : InstRW<[GenericWriteJump], (instrs BEQL, BNEL, BGEZL, BGTZL, BLEZL, BLTZL)>; def : InstRW<[GenericWriteJump], (instrs TAILCALL, TAILCALLREG, TAILCALLREGHB, PseudoIndirectBranch, PseudoIndirectHazardBranch, PseudoReturn, RetRA)>; def : InstRW<[GenericWriteJumpAndLink], (instrs BGEZAL, JAL, JALR, JALR_HB, JALRHBPseudo, JALRPseudo)>; def : InstRW<[GenericWriteJumpAndLink], (instrs BGEZALL, BLTZALL)>; def GenericWriteTrap : SchedWriteRes<[GenericIssueCTISTD]>; def : InstRW<[GenericWriteTrap], (instrs BREAK, SYSCALL, TEQ, TEQI, TGE, TGEI, TGEIU, TGEU, TNE, TNEI, TLT, TLTI, TLTU, TTLTIU, TRAP, SDBBP)>; // MIPSR6 // ====== def : InstRW<[GenericWriteJumpAndLink], (instrs BALC, BEQZALC, BGEZALC, BGTZALC, BLEZALC, BLTZALC, BNEZALC, JIALC)>; def : InstRW<[GenericWriteJump], (instrs BC, BC2EQZ, BC2NEZ, BEQC, BEQZC, BGEC, BGEUC, BGEZC, BGTZC, BLEZC, BLTC, BLTUC, BLTZC, BNEC, BNEZC, BNVC, BOVC, JIC, JR_HB_R6, SIGRIE, PseudoIndirectBranchR6, PseudoIndrectHazardBranchR6)>; def : InstRW<[GenericWriteJump], (instrs TAILCALLR6REG, TAILCALLHBR6REG)>; def : InstRW<[GenericWriteTrap], (instrs SDBBP_R6)>; // MIPS16e // ======= def : InstRW<[GenericWriteJump], (instrs Bimm16, BimmX16, BeqzRxImm16, BeqzRxImmX16, BnezRxImm16, BnezRxImmX16, Bteqz16, BteqzX16, BteqzT8CmpX16, BteqzT8CmpiX16, BteqzT8SltX16, BteqzT8SltuX16, BteqzT8SltiX16, BteqzT8SltiuX16, Btnez16, BtnezX16, BtnezT8CmpX16, BtnezT8CmpiX16, BtnezT8SltX16, BtnezT8SltuX16, BtnezT8SltiX16, BtnezT8SltiuX16, JrRa16, JrcRa16, JrcRx16, RetRA16)>; def : InstRW<[GenericWriteJumpAndLink], (instrs Jal16, JalB16, JumpLinkReg16)>; def : InstRW<[GenericWriteTrap], (instrs Break16)>; def : InstRW<[GenericWriteALULong], (instrs SelBeqZ, SelTBteqZCmp, SelTBteqZCmpi, SelTBteqZSlt, SelTBteqZSlti, SelTBteqZSltu, SelTBteqZSltiu, SelBneZ, SelTBtneZCmp, SelTBtneZCmpi, SelTBtneZSlt, SelTBtneZSlti, SelTBtneZSltu, SelTBtneZSltiu)>; // microMIPS // ========= def : InstRW<[GenericWriteJump], (instrs B16_MM, BAL_BR_MM, BC1F_MM, BC1T_MM, BEQZ16_MM, BEQZC_MM, BEQ_MM, BGEZ_MM, BGTZ_MM, BLEZ_MM, BLTZ_MM, BNEZ16_MM, BNEZC_MM, BNE_MM, B_MM, DERET_MM, ERET_MM, JR16_MM, JR_MM, J_MM, B_MM_Pseudo)>; def : InstRW<[GenericWriteJumpAndLink], (instrs BGEZALS_MM, BGEZAL_MM, BLTZALS_MM, BLTZAL_MM, JALR16_MM, JALRS16_MM, JALRS_MM, JALR_MM, JALS_MM, JALX_MM, JAL_MM)>; def : InstRW<[GenericWriteJump], (instrs TAILCALLREG_MM, TAILCALL_MM, PseudoIndirectBranch_MM)>; def : InstRW<[GenericWriteTrap], (instrs BREAK16_MM, BREAK_MM, SDBBP16_MM, SDBBP_MM, SYSCALL_MM, TEQI_MM, TEQ_MM, TGEIU_MM, TGEI_MM, TGEU_MM, TGE_MM, TLTIU_MM, TLTI_MM, TLTU_MM, TLT_MM, TNEI_MM, TNE_MM, TRAP_MM)>; // microMIPS32r6 // ============= def : InstRW<[GenericWriteJump], (instrs BC16_MMR6, BC1EQZC_MMR6, BC1NEZC_MMR6, BC2EQZC_MMR6, BC2NEZC_MMR6, BC_MMR6, BEQC_MMR6, BEQZC16_MMR6, BEQZC_MMR6, BGEC_MMR6, BGEUC_MMR6, BGEZC_MMR6, BGTZC_MMR6, BLEZC_MMR6, BLTC_MMR6, BLTUC_MMR6, BLTZC_MMR6, BNEC_MMR6, BNEZC16_MMR6, BNEZC_MMR6, BNVC_MMR6, BOVC_MMR6, DERET_MMR6, ERETNC_MMR6, JAL_MMR6, ERET_MMR6, JIC_MMR6, JRADDIUSP, JRC16_MM, JRC16_MMR6, JRCADDIUSP_MMR6, SIGRIE_MMR6, B_MMR6_Pseudo, PseudoIndirectBranch_MMR6)>; def : InstRW<[GenericWriteJumpAndLink], (instrs BALC_MMR6, BEQZALC_MMR6, BGEZALC_MMR6, BGTZALC_MMR6, BLEZALC_MMR6, BLTZALC_MMR6, BNEZALC_MMR6, JALRC16_MMR6, JALRC_HB_MMR6, JALRC_MMR6, JIALC_MMR6)>; def : InstRW<[GenericWriteJump], (instrs TAILCALLREG_MMR6, TAILCALL_MMR6)>; def : InstRW<[GenericWriteTrap], (instrs BREAK16_MMR6, BREAK_MMR6, SDBBP_MMR6, SDBBP16_MMR6)>; // MIPS64 // ====== def : InstRW<[GenericWriteJump], (instrs BEQ64, BGEZ64, BGTZ64, BLEZ64, BLTZ64, BNE64, JR64)>; def : InstRW<[GenericWriteJumpAndLink], (instrs JALR64, JALR64Pseudo, JALRHB64Pseudo, JALR_HB64)>; def : InstRW<[GenericWriteJump], (instrs JR_HB64, TAILCALLREG64, TAILCALLREGHB64, PseudoReturn64)>; // MIPS64R6 // ======== def : InstRW<[GenericWriteJump], (instrs BEQC64, BEQZC64, BGEC64, BGEUC64, BGEZC64, BGTZC64, BLEZC64, BLTC64, BLTUC64, BLTZC64, BNEC64, BNEZC64, JIC64, PseudoIndirectBranch64, PseudoIndirectHazardBranch64)>; def : InstRW<[GenericWriteJumpAndLink], (instrs JIALC64)>; def : InstRW<[GenericWriteJump], (instrs JR_HB64_R6, TAILCALL64R6REG, TAILCALLHB64R6REG, PseudoIndirectBranch64R6, PseudoIndrectHazardBranch64R6)>; // COP0 Pipeline // ============= def GenericCOP0 : ProcResource<1> { let BufferSize = 1; } def GenericIssueCOP0 : ProcResource<1> { let Super = GenericCOP0; } def GenericWriteCOP0TLB : SchedWriteRes<[GenericIssueCOP0]> { let Latency = 4; } def GenericWriteCOP0 : SchedWriteRes<[GenericIssueCOP0]> { let Latency = 3; } def GenericReadCOP0 : SchedWriteRes<[GenericIssueCOP0]> { let Latency = 2; } def GenericReadWritePGPR : SchedWriteRes<[GenericIssueCOP0]>; def GenericReadWriteCOP0Long : SchedWriteRes<[GenericIssueCOP0]> { let Latency = 5; } def GenericWriteCOP0Short : SchedWriteRes<[GenericIssueCOP0]>; def : InstRW<[GenericWriteCOP0TLB], (instrs TLBP, TLBR, TLBWI, TLBWR)>; def : InstRW<[GenericWriteCOP0TLB], (instrs TLBINV, TLBINVF)>; def : InstRW<[GenericReadCOP0], (instrs MFC0)>; def : InstRW<[GenericWriteCOP0], (instrs MTC0)>; def : InstRW<[GenericWriteCOP0], (instrs EVP, DVP)>; def : InstRW<[GenericWriteCOP0], (instrs DI, EI)>; def : InstRW<[GenericWriteCOP0], (instrs EHB, PAUSE, WAIT)>; // microMIPS // ========= def : InstRW<[GenericWriteCOP0TLB], (instrs TLBP_MM, TLBR_MM, TLBWI_MM, TLBWR_MM)>; def : InstRW<[GenericWriteCOP0], (instrs DI_MM, EI_MM)>; def : InstRW<[GenericWriteCOP0], (instrs EHB_MM, PAUSE_MM, WAIT_MM)>; // microMIPS32R6 // ============= def : InstRW<[GenericWriteCOP0], (instrs RDPGPR_MMR6, WRPGPR_MMR6)>; def : InstRW<[GenericWriteCOP0TLB], (instrs TLBINV_MMR6, TLBINVF_MMR6)>; def : InstRW<[GenericReadCOP0], (instrs MFHC0_MMR6, MFC0_MMR6, MFHC2_MMR6, MFC2_MMR6)>; def : InstRW<[GenericWriteCOP0], (instrs MTHC0_MMR6, MTC0_MMR6, MTHC2_MMR6, MTC2_MMR6)>; def : InstRW<[GenericWriteCOP0], (instrs EVP_MMR6, DVP_MMR6)>; def : InstRW<[GenericWriteCOP0], (instrs DI_MMR6, EI_MMR6)>; def : InstRW<[GenericWriteCOP0], (instrs EHB_MMR6, PAUSE_MMR6, WAIT_MMR6)>; // MIPS64 // ====== def : InstRW<[GenericReadCOP0], (instrs DMFC0)>; def : InstRW<[GenericWriteCOP0], (instrs DMTC0)>; def GenericCOP2 : ProcResource<1> { let BufferSize = 1; } def GenericWriteCOPOther : SchedWriteRes<[GenericCOP2]>; def : InstRW<[GenericWriteCOPOther], (instrs MFC2, MTC2)>; def : InstRW<[GenericWriteCOPOther], (instrs DMFC2, DMTC2)>; // microMIPS32R6 // ============= // The latency and repeat rate of these instructions are implementation // dependant. def : InstRW<[GenericWriteMove], (instrs CFC2_MM, CTC2_MM)>; // MIPS MT ASE - hasMT // ==================== def : InstRW<[GenericWriteMove], (instrs DMT, DVPE, EMT, EVPE, MFTR, MTTR)>; def : InstRW<[GenericReadWriteCOP0Long], (instrs YIELD)>; def : InstRW<[GenericWriteCOP0Short], (instrs FORK)>; // MIPS Virtualization ASE // ======================= def : InstRW<[GenericWriteCOP0Short], (instrs HYPCALL, TLBGINV, TLBGINVF, TLBGP, TLBGR, TLBGWI, TLBGWR, MFGC0, MFHGC0, MTGC0, MTHGC0)>; // MIPS64 Virtualization ASE // ========================= def : InstRW<[GenericWriteCOP0Short], (instrs DMFGC0, DMTGC0)>; // microMIPS virtualization ASE // ============================ def : InstRW<[GenericWriteCOP0Short], (instrs HYPCALL_MM, TLBGINVF_MM, TLBGINV_MM, TLBGP_MM, TLBGR_MM, TLBGWI_MM, TLBGWR_MM, MFGC0_MM, MFHGC0_MM, MTGC0_MM, MTHGC0_MM)>; // LDST Pipeline // ------------- def GenericWriteLoad : SchedWriteRes<[GenericIssueLDST]> { let Latency = 2; } def GenericWritePref : SchedWriteRes<[GenericIssueLDST]>; def GenericWriteSync : SchedWriteRes<[GenericIssueLDST]>; def GenericWriteCache : SchedWriteRes<[GenericIssueLDST]> { let Latency = 5; } def GenericWriteStore : SchedWriteRes<[GenericIssueLDST]>; def GenericWriteStoreSC : SchedWriteRes<[GenericIssueLDST]> { let Latency = 2; } def GenericWriteGPRFromBypass : SchedWriteRes<[GenericIssueLDST]> { let Latency = 2; } def GenericWriteStoreFromOtherUnits : SchedWriteRes<[GenericIssueLDST]>; def GenericWriteLoadToOtherUnits : SchedWriteRes<[GenericIssueLDST]> { let Latency = 0; } // l[bhw], l[bh]u, ll def : InstRW<[GenericWriteLoad], (instrs LB, LBu, LH, LHu, LW, LL, LWC2, LWC3, LDC2, LDC3)>; // lw[lr] def : InstRW<[GenericWriteLoad], (instrs LWL, LWR)>; // s[bhw], sc, s[dw]c[23] def : InstRW<[GenericWriteStore], (instrs SB, SH, SW, SWC2, SWC3, SDC2, SDC3)>; // PreMIPSR6 sw[lr] def : InstRW<[GenericWriteStore], (instrs SWL, SWR)>; def : InstRW<[GenericWriteStoreSC], (instrs SC, SC_MMR6)>; // pref def : InstRW<[GenericWritePref], (instrs PREF)>; // cache def : InstRW<[GenericWriteCache], (instrs CACHE)>; // sync def : InstRW<[GenericWriteSync], (instrs SYNC, SYNCI)>; // MIPSR6 // ====== def : InstRW<[GenericWriteLoad], (instrs LDC2_R6, LL_R6, LWC2_R6, LWPC)>; def : InstRW<[GenericWriteStore], (instrs SWC2_R6, SDC2_R6)>; def : InstRW<[GenericWriteStoreSC], (instrs SC_R6)>; def : InstRW<[GenericWritePref], (instrs PREF_R6)>; def : InstRW<[GenericWriteCache], (instrs CACHE_R6)>; def : InstRW<[GenericWriteSync], (instrs GINVI, GINVT)>; // MIPS32 EVA // ========== def : InstRW<[GenericWriteLoad], (instrs LBE, LBuE, LHE, LHuE, LWE, LLE)>; def : InstRW<[GenericWriteStore], (instrs SBE, SHE, SWE, SCE)>; def : InstRW<[GenericWriteLoad], (instrs LWLE, LWRE)>; def : InstRW<[GenericWriteStore], (instrs SWLE, SWRE)>; def : InstRW<[GenericWritePref], (instrs PREFE)>; def : InstRW<[GenericWriteCache], (instrs CACHEE)>; // microMIPS EVA ASE - InMicroMipsMode, hasEVA // =========================================== def : InstRW<[GenericWriteLoad], (instrs LBE_MM, LBuE_MM, LHE_MM, LHuE_MM, LWE_MM, LWLE_MM, LWRE_MM, LLE_MM)>; def : InstRW<[GenericWriteStore], (instrs SBE_MM, SB_MM, SHE_MM, SWE_MM, SWLE_MM, SWRE_MM, SCE_MM)>; def : InstRW<[GenericWritePref], (instrs PREFE_MM)>; def : InstRW<[GenericWriteCache], (instrs CACHEE_MM)>; // MIPS16e // ======= def : InstRW<[GenericWriteLoad], (instrs Restore16, RestoreX16, LbRxRyOffMemX16, LbuRxRyOffMemX16, LhRxRyOffMemX16, LhuRxRyOffMemX16, LwRxRyOffMemX16, LwRxSpImmX16, LwRxPcTcp16, LwRxPcTcpX16)>; def : InstRW<[GenericWriteStore], (instrs Save16, SaveX16, SbRxRyOffMemX16, ShRxRyOffMemX16, SwRxRyOffMemX16, SwRxSpImmX16)>; // microMIPS // ========= def : InstRW<[GenericWriteLoad], (instrs LBU16_MM, LB_MM, LBu_MM, LHU16_MM, LH_MM, LHu_MM, LL_MM, LW16_MM, LWGP_MM, LWL_MM, LWM16_MM, LWM32_MM, LWP_MM, LWR_MM, LWSP_MM, LWU_MM, LWXS_MM, LW_MM)>; def : InstRW<[GenericWriteStore], (instrs SB16_MM, SC_MM, SH16_MM, SH_MM, SW16_MM, SWL_MM, SWM16_MM, SWM32_MM, SWM_MM, SWP_MM, SWR_MM, SWSP_MM, SW_MM)>; def : InstRW<[GenericWritePref], (instrs PREF_MM, PREFX_MM)>; def : InstRW<[GenericWriteCache], (instrs CACHE_MM)>; def : InstRW<[GenericWriteSync], (instrs SYNC_MM, SYNCI_MM)>; def : InstRW<[GenericWriteSync], (instrs GINVI_MMR6, GINVT_MMR6)>; // microMIPS32r6 // ============= def : InstRW<[GenericWriteLoad], (instrs LBU_MMR6, LB_MMR6, LDC2_MMR6, LL_MMR6, LWM16_MMR6, LWC2_MMR6, LWPC_MMR6, LW_MMR6)>; def : InstRW<[GenericWriteStore], (instrs SB16_MMR6, SB_MMR6, SDC2_MMR6, SH16_MMR6, SH_MMR6, SW16_MMR6, SWC2_MMR6, SWM16_MMR6, SWSP_MMR6, SW_MMR6)>; def : InstRW<[GenericWriteSync], (instrs SYNC_MMR6, SYNCI_MMR6)>; def : InstRW<[GenericWritePref], (instrs PREF_MMR6)>; def : InstRW<[GenericWriteCache], (instrs CACHE_MMR6)>; // MIPS64 // ====== def : InstRW<[GenericWriteLoad], (instrs LD, LL64, LLD, LWu, LB64, LBu64, LH64, LHu64, LW64)>; // l[dw][lr] def : InstRW<[GenericWriteLoad], (instrs LWL64, LWR64, LDL, LDR)>; def : InstRW<[GenericWriteStore], (instrs SD, SC64, SCD, SB64, SH64, SW64, SWL64, SWR64)>; def : InstRW<[GenericWriteStore], (instrs SDL, SDR)>; // MIPS64R6 // ======== def : InstRW<[GenericWriteLoad], (instrs LWUPC, LDPC)>; def : InstRW<[GenericWriteLoad], (instrs LLD_R6, LL64_R6)>; def : InstRW<[GenericWriteStoreSC], (instrs SC64_R6, SCD_R6)>; // MIPSR6 CRC ASE - hasCRC // ======================= def : InstRW<[GenericWriteALU], (instrs CRC32B, CRC32H, CRC32W, CRC32CB, CRC32CH, CRC32CW)>; // MIPS64R6 CRC ASE - hasCRC // ------------------------- def : InstRW<[GenericWriteALU], (instrs CRC32D, CRC32CD)>; // Cavium Networks MIPS (cnMIPS) - Octeon, HasCnMips // ================================================= def : InstRW<[GenericWriteALU], (instrs BADDu, BBIT0, BBIT032, BBIT1, BBIT132, CINS, CINS32, CINS64_32, CINS_i32, DMFC2_OCTEON, DMTC2_OCTEON, DPOP, EXTS, EXTS32, MTM0, MTM1, MTM2, MTP0, MTP1, MTP2, - POP, SEQ, SEQi, SNE, SNEi, V3MULU, VMM0, - VMULU)>; + POP, SEQ, SEQi, SNE, SNEi, + V3MULU, VMM0, VMULU)>; def : InstRW<[GenericWriteMDUtoGPR], (instrs DMUL)>; + +// Cavium Networks MIPS (cnMIPSP) - Octeon+, HasCnMipsP +// ================================================= + +def : InstRW<[GenericWriteALU], (instrs SAA, SAAD)>; // FPU Pipelines // ============= def GenericFPQ : ProcResource<1> { let BufferSize = 1; } def GenericIssueFPUS : ProcResource<1> { let Super = GenericFPQ; } def GenericIssueFPUL : ProcResource<1> { let Super = GenericFPQ; } def GenericIssueFPULoad : ProcResource<1> { let Super = GenericFPQ; } def GenericIssueFPUStore : ProcResource<1> { let Super = GenericFPQ; } def GenericIssueFPUMove : ProcResource<1> { let Super = GenericFPQ; } def GenericFPUDivSqrt : ProcResource<1> { let Super = GenericFPQ; } // The floating point compare of the 24k series including interAptiv has a // listed latency of 1-2. Using the higher latency here. def GenericWriteFPUCmp : SchedWriteRes<[GenericIssueFPUS]> { let Latency = 2; } def GenericWriteFPUS : SchedWriteRes<[GenericIssueFPUS]> { let Latency = 4; } def GenericWriteFPUL : SchedWriteRes<[GenericIssueFPUL]> { let Latency = 5; } def GenericWriteFPUStore : SchedWriteRes<[GenericIssueFPUStore]> { let Latency = 1; } def GenericWriteFPULoad : SchedWriteRes<[GenericIssueFPULoad]> { let Latency = 2; } def GenericWriteFPUMoveFP : SchedWriteRes<[GenericIssueFPUMove]> { let Latency = 4; } def GenericWriteFPUMoveGPRFPU : SchedWriteRes<[GenericIssueFPUMove]> { let Latency = 2; } def GenericWriteFPUDivS : SchedWriteRes<[GenericFPUDivSqrt]> { let Latency = 17; let ResourceCycles = [ 14 ]; } def GenericWriteFPUDivD : SchedWriteRes<[GenericFPUDivSqrt]> { let Latency = 32; let ResourceCycles = [ 29 ]; } def GenericWriteFPURcpS : SchedWriteRes<[GenericFPUDivSqrt]> { let Latency = 13; let ResourceCycles = [ 10 ]; } def GenericWriteFPURcpD : SchedWriteRes<[GenericFPUDivSqrt]> { let Latency = 25; let ResourceCycles = [ 21 ]; } def GenericWriteFPURsqrtS : SchedWriteRes<[GenericFPUDivSqrt]> { let Latency = 17; let ResourceCycles = [ 14 ]; } def GenericWriteFPURsqrtD : SchedWriteRes<[GenericFPUDivSqrt]> { let Latency = 32; let ResourceCycles = [ 29 ]; } def GenericWriteFPUSqrtS : SchedWriteRes<[GenericFPUDivSqrt]> { let Latency = 17; let ResourceCycles = [ 14 ]; } def GenericWriteFPUSqrtD : SchedWriteRes<[GenericFPUDivSqrt]> { let Latency = 29; let ResourceCycles = [ 29 ]; } // Floating point compare and branch // --------------------------------- // // c..[ds], bc1[tf], bc1[tf]l def : InstRW<[GenericWriteFPUCmp], (instrs FCMP_D32, FCMP_D64, FCMP_S32, BC1F, BC1T, BC1FL, BC1TL)>; def : InstRW<[GenericWriteFPUCmp], (instregex "C_[A-Z]+_(S|D32|D64)$")>; // Short Pipe // ---------- // // abs.[ds], abs.ps, add.[ds], neg.[ds], neg.ps, madd.s, msub.s, nmadd,s // nmsub.s, sub.[ds], mul.s def : InstRW<[GenericWriteFPUS], (instrs FABS_S, FABS_D32, FABS_D64, FADD_D32, FADD_D64, FADD_S, MADD_S, MSUB_S, FMUL_S, FNEG_S, FNEG_D32, FNEG_D64, NMADD_S, NMSUB_S, FSUB_S, FSUB_D32, FSUB_D64)>; // Long Pipe // ---------- // // nmadd.d, nmsub.d, mul.[ds], mul.ps, ceil.[wl].[sd], cvt.d.[sw], cvt.s.[dw], // cvt.w.[sd], cvt.[sw].ps, trunc.w.[ds], trunc.w.ps, floor.[ds], // round.[lw].[ds], floor.[lw].ds // madd.d, msub.dm mul.d, mul.ps, nmadd.d, nmsub.d, ceil.[wl].[sd], cvt.d.[sw], // cvt.s.[dw], cvt.w.[sd], cvt.[sw].ps, round.[lw].[ds], floor.[lw].ds, // trunc.w.[ds], trunc.w.ps, def : InstRW<[GenericWriteFPUL], (instrs CEIL_L_D64, CEIL_L_S, CEIL_W_D32, CEIL_W_D64, CEIL_W_S, CVT_D32_S, CVT_D32_W, CVT_D64_L, CVT_D64_S, CVT_D64_W, CVT_L_D64, CVT_L_S, CVT_S_D32, CVT_S_D64, CVT_S_L, CVT_S_W, CVT_W_D32, CVT_W_D64, CVT_W_S, CVT_PS_S64, CVT_S_PL64, CVT_S_PU64, FLOOR_L_D64, FLOOR_L_S, FLOOR_W_D32, FLOOR_W_D64, FLOOR_W_S, FMUL_D32, FMUL_D64, MADD_D32, MADD_D64, MSUB_D32, MSUB_D64, NMADD_D32, NMADD_D64, NMSUB_D32, NMSUB_D64, PLL_PS64, PLU_PS64, ROUND_L_D64, ROUND_L_S, ROUND_W_D32, ROUND_W_D64, ROUND_W_S, TRUNC_L_D64, TRUNC_L_S, TRUNC_W_D32, TRUNC_W_D64, TRUNC_W_S, PseudoTRUNC_W_D, PseudoTRUNC_W_D32, PseudoTRUNC_W_S)>; // Pseudo convert instruction def : InstRW<[GenericWriteFPUL], (instrs PseudoCVT_D32_W, PseudoCVT_D64_L, PseudoCVT_D64_W, PseudoCVT_S_L, PseudoCVT_S_W)>; // div.[ds], div.ps def : InstRW<[GenericWriteFPUDivS], (instrs FDIV_S)>; def : InstRW<[GenericWriteFPUDivD], (instrs FDIV_D32, FDIV_D64)>; // sqrt.[ds], sqrt.ps def : InstRW<[GenericWriteFPUSqrtS], (instrs FSQRT_S)>; def : InstRW<[GenericWriteFPUSqrtD], (instrs FSQRT_D32, FSQRT_D64)>; // rsqrt.[ds], recip.[ds] def : InstRW<[GenericWriteFPURcpS], (instrs RECIP_S, RSQRT_S)>; def : InstRW<[GenericWriteFPURcpD], (instrs RECIP_D32, RECIP_D64, RSQRT_D32, RSQRT_D64)>; // Load Pipe // --------- // ctc1, mtc1, mthc1, cfc1, mfc1, mfhc1 def : InstRW<[GenericWriteFPUMoveGPRFPU], (instrs BuildPairF64, BuildPairF64_64, ExtractElementF64, ExtractElementF64_64, CFC1, CTC1, MFC1, MFC1_D64, MFHC1_D32, MFHC1_D64, MTC1, MTC1_D64, MTHC1_D32, MTHC1_D64)>; // swc1, swxc1 def : InstRW<[GenericWriteFPUStore], (instrs SDC1, SDC164, SDXC1, SDXC164, SUXC1, SUXC164, SWC1, SWXC1)>; def : InstRW<[GenericWriteFPUMoveFP], (instrs FMOV_D32, FMOV_D64, FMOV_S)>; // movn.[ds], movz.[ds] def : InstRW<[GenericWriteFPUMoveFP], (instrs MOVF_I, MOVF_D32, MOVF_D64, MOVF_S, MOVT_I, MOVT_D32, MOVT_D64, MOVT_S, MOVN_I_D32, MOVN_I_D64, MOVN_I_S, MOVZ_I_D32, MOVZ_I_D64, MOVZ_I_S)>; def : InstRW<[GenericWriteFPUMoveFP], (instrs MOVT_I64, MOVF_I64, MOVZ_I64_S, MOVN_I64_D64, MOVN_I64_S, MOVZ_I64_D64)>; // l[dw]x?c1 def : InstRW<[GenericWriteFPULoad], (instrs LDC1, LDC164, LDXC1, LDXC164, LUXC1, LUXC164, LWC1, LWXC1)>; // MIPSR6 // ====== // sel(eq|ne).[ds], max.[ds], maxa.[ds], min.[ds], mina.[ds], class.[ds] def : InstRW<[GenericWriteFPUS], (instrs SELEQZ_S, SELNEZ_S, SELEQZ_D, SELNEZ_D, MAX_S, MAX_D, MAXA_S, MAXA_D, MIN_S, MIN_D, MINA_S, MINA_D, CLASS_S, CLASS_D)>; def : InstRW<[GenericWriteFPUL], (instrs RINT_S, RINT_D)>; def : InstRW<[GenericWriteFPUCmp], (instrs BC1EQZ, BC1NEZ, SEL_D, SEL_S)>; def : InstRW<[GenericWriteFPUS], (instrs MADDF_S, MSUBF_S, MADDF_D, MSUBF_D)>; // microMIPS // ========= def : InstRW<[GenericWriteFPUMoveFP], (instrs MOVF_D32_MM, MOVF_S_MM, MOVN_I_D32_MM, MOVN_I_S_MM, MOVT_D32_MM, MOVT_S_MM, MOVZ_I_D32_MM, MOVZ_I_S_MM)>; // cvt.?.?, ceil.?, floor.?, round.?, trunc.? (n)madd.? (n)msub.? def : InstRW<[GenericWriteFPUL], (instrs CVT_D32_S_MM, CVT_D32_W_MM, CVT_D64_S_MM, CVT_D64_W_MM, CVT_L_D64_MM, CVT_L_S_MM, CVT_S_D32_MM, CVT_S_D64_MM, CVT_S_W_MM, CVT_W_D32_MM, CVT_W_D64_MM, CVT_W_S_MM, CEIL_W_MM, CEIL_W_S_MM, FLOOR_W_MM, FLOOR_W_S_MM, NMADD_S_MM, NMADD_D32_MM, NMSUB_S_MM, NMSUB_D32_MM, MADD_S_MM, MADD_D32_MM, ROUND_W_MM, ROUND_W_S_MM, TRUNC_W_MM, TRUNC_W_S_MM)>; def : InstRW<[GenericWriteFPUCmp], (instregex "^C_[A-Z]_(S|D32|D64)_MM$")>; def : InstRW<[GenericWriteFPUCmp], (instregex "^C_[A-Z][A-Z]_(S|D32|D64)_MM$")>; def : InstRW<[GenericWriteFPUCmp], (instregex "^C_[A-Z][A-Z][A-Z]_(S|D32|D64)_MM$")>; def : InstRW<[GenericWriteFPUCmp], (instregex "^C_NGLE_(S|D32|D64)_MM$")>; def : InstRW<[GenericWriteFPUCmp], (instrs FCMP_S32_MM, FCMP_D32_MM)>; def : InstRW<[GenericWriteFPUS], (instrs MFC1_MM, MFHC1_D32_MM, MFHC1_D64_MM, MTC1_MM, MTC1_D64_MM, MTHC1_D32_MM, MTHC1_D64_MM)>; def : InstRW<[GenericWriteFPUS], (instrs FABS_D32_MM, FABS_D64_MM, FABS_S_MM, FNEG_D32_MM, FNEG_D64_MM, FNEG_S_MM, FADD_D32_MM, FADD_D64_MM, FADD_S_MM, FMOV_D32_MM, FMOV_D64_MM, FMOV_S_MM, FMUL_D32_MM, FMUL_D64_MM, FMUL_S_MM, FSUB_D32_MM, FSUB_D64_MM, FSUB_S_MM, MSUB_S_MM, MSUB_D32_MM)>; def : InstRW<[GenericWriteFPUDivS], (instrs FDIV_S_MM)>; def : InstRW<[GenericWriteFPUDivD], (instrs FDIV_D32_MM, FDIV_D64_MM)>; def : InstRW<[GenericWriteFPUSqrtS], (instrs FSQRT_S_MM)>; def : InstRW<[GenericWriteFPUSqrtD], (instrs FSQRT_D32_MM, FSQRT_D64_MM)>; def : InstRW<[GenericWriteFPURcpS], (instrs RECIP_S_MM, RSQRT_S_MM)>; def : InstRW<[GenericWriteFPURcpD], (instrs RECIP_D32_MM, RECIP_D64_MM, RSQRT_D32_MM, RSQRT_D64_MM)>; def : InstRW<[GenericWriteFPUStore], (instrs SDC1_MM, SWC1_MM, SUXC1_MM, SWXC1_MM)>; def : InstRW<[GenericWriteFPUMoveGPRFPU], (instrs CFC1_MM, CTC1_MM)>; def : InstRW<[GenericWriteFPULoad], (instrs LDC1_MM, LUXC1_MM, LWC1_MM, LWXC1_MM)>; // microMIPS32r6 // ============= def : InstRW<[GenericWriteFPUS], (instrs FNEG_S_MMR6)>; def : InstRW<[GenericWriteFPUCmp], (instregex "CMP_[A-Z][A-Z]_(S|D)_MMR6")>; def : InstRW<[GenericWriteFPUCmp], (instregex "CMP_[A-Z][A-Z][A-Z]_(S|D)_MMR6")>; def : InstRW<[GenericWriteFPUCmp], (instregex "CMP_[A-Z][A-Z][A-Z][A-Z]_(S|D)_MMR6")>; def : InstRW<[GenericWriteFPUL], (instregex "CVT_(L|D|S|W)_(L|D|S|L|W)_MMR6")>; def : InstRW<[GenericWriteFPUL], (instregex "TRUNC_(L|W)_(D|S)_MMR6")>; def : InstRW<[GenericWriteFPUL], (instregex "ROUND_(L|W)_(D|S)_MMR6")>; def : InstRW<[GenericWriteFPUL], (instregex "FLOOR_(L|W)_(D|S)_MMR6")>; def : InstRW<[GenericWriteFPUL], (instregex "CEIL_(L|W)_(S|D)_MMR6")>; def : InstRW<[GenericWriteFPUS], (instrs MFC1_MMR6, MTC1_MMR6, CLASS_S_MMR6, CLASS_D_MMR6, FADD_S_MMR6)>; def : InstRW<[GenericWriteFPUS], (instregex "M(IN|AX)_(S|D)_MMR6")>; def : InstRW<[GenericWriteFPUS], (instregex "M(IN|AX)A_(S|D)_MMR6")>; def : InstRW<[GenericWriteFPUS], (instregex "SEL(EQ|NE)Z_(S|D)_MMR6")>; def : InstRW<[GenericWriteFPUS], (instregex "SEL_(S|D)_MMR6")>; def : InstRW<[GenericWriteFPUL], (instrs RINT_S_MMR6, RINT_D_MMR6)>; def : InstRW<[GenericWriteFPUS], (instregex "M(ADD|SUB)F_(S|D)_MMR6")>; def : InstRW<[GenericWriteFPUS], (instrs FMOV_S_MMR6, FMUL_S_MMR6, FSUB_S_MMR6, FMOV_D_MMR6)>; def : InstRW<[GenericWriteFPUL], (instrs FDIV_S_MMR6)>; def : InstRW<[GenericWriteFPUStore], (instrs SDC1_D64_MMR6)>; def : InstRW<[GenericWriteFPULoad], (instrs LDC1_D64_MMR6)>; // MIPS64 // ====== def : InstRW<[GenericWriteFPUMoveGPRFPU], (instrs DMFC1, DMTC1)>; // MIPS DSP ASE, HasDSP // ==================== def : InstRW<[GenericWriteStore], (instrs SWDSP)>; def : InstRW<[GenericWriteLoad], (instrs LWDSP)>; def : InstRW<[GenericWriteMove], (instrs PseudoMTLOHI_DSP)>; def GenericDSP : ProcResource<1> { let BufferSize = 1; } def GenericDSPShort : SchedWriteRes<[GenericDSP]> { let Latency = 2; } def GenericDSPLong : SchedWriteRes<[GenericDSP]> { let Latency = 6; } def GenericDSPBypass : SchedWriteRes<[GenericDSP]> { let Latency = 1; } def GenericDSPMTHILO : SchedWriteRes<[GenericDSP]> { let Latency = 5; } def GenericDSPLoad : SchedWriteRes<[GenericDSP]> { let Latency = 4; } def GenericDSPMTHLIP : SchedWriteRes<[GenericDSP]> { let Latency = 5; } def : InstRW<[GenericDSPLong], (instregex "^EXTRV_RS_W$")>; def : InstRW<[GenericDSPLong], (instregex "^EXTRV_R_W$")>; def : InstRW<[GenericDSPLong], (instregex "^EXTRV_S_H$")>; def : InstRW<[GenericDSPLong], (instregex "^EXTRV_W$")>; def : InstRW<[GenericDSPLong], (instregex "^EXTR_RS_W$")>; def : InstRW<[GenericDSPLong], (instregex "^EXTR_R_W$")>; def : InstRW<[GenericDSPLong], (instregex "^EXTR_S_H$")>; def : InstRW<[GenericDSPLong], (instregex "^EXTR_W$")>; def : InstRW<[GenericDSPLong], (instregex "^INSV$")>; def : InstRW<[GenericDSPMTHLIP], (instregex "^MTHLIP$")>; def : InstRW<[GenericDSPMTHILO], (instregex "^MTHI_DSP$")>; def : InstRW<[GenericDSPMTHILO], (instregex "^MTLO_DSP$")>; def : InstRW<[GenericDSPShort], (instregex "^ABSQ_S_PH$")>; def : InstRW<[GenericDSPShort], (instregex "^ABSQ_S_W$")>; def : InstRW<[GenericDSPShort], (instregex "^ADDQ_PH$")>; def : InstRW<[GenericDSPShort], (instregex "^ADDQ_S_PH$")>; def : InstRW<[GenericDSPShort], (instregex "^ADDQ_S_W$")>; def : InstRW<[GenericDSPShort], (instregex "^ADDSC$")>; def : InstRW<[GenericDSPShort], (instregex "^ADDU_QB$")>; def : InstRW<[GenericDSPShort], (instregex "^ADDU_S_QB$")>; def : InstRW<[GenericDSPShort], (instregex "^ADDWC$")>; def : InstRW<[GenericDSPShort], (instregex "^BITREV$")>; def : InstRW<[GenericDSPShort], (instregex "^BPOSGE32$")>; def : InstRW<[GenericDSPShort], (instregex "^CMPGU_EQ_QB$")>; def : InstRW<[GenericDSPShort], (instregex "^CMPGU_LE_QB$")>; def : InstRW<[GenericDSPShort], (instregex "^CMPGU_LT_QB$")>; def : InstRW<[GenericDSPShort], (instregex "^CMPU_EQ_QB$")>; def : InstRW<[GenericDSPShort], (instregex "^CMPU_LE_QB$")>; def : InstRW<[GenericDSPShort], (instregex "^CMPU_LT_QB$")>; def : InstRW<[GenericDSPShort], (instregex "^CMP_EQ_PH$")>; def : InstRW<[GenericDSPShort], (instregex "^CMP_LE_PH$")>; def : InstRW<[GenericDSPShort], (instregex "^CMP_LT_PH$")>; def : InstRW<[GenericDSPShort], (instregex "^DPAQ_SA_L_W$")>; def : InstRW<[GenericDSPShort], (instregex "^DPAQ_S_W_PH$")>; def : InstRW<[GenericDSPShort], (instregex "^DPAU_H_QBL$")>; def : InstRW<[GenericDSPShort], (instregex "^DPAU_H_QBR$")>; def : InstRW<[GenericDSPShort], (instregex "^DPSQ_SA_L_W$")>; def : InstRW<[GenericDSPShort], (instregex "^DPSQ_S_W_PH$")>; def : InstRW<[GenericDSPShort], (instregex "^DPSU_H_QBL$")>; def : InstRW<[GenericDSPShort], (instregex "^DPSU_H_QBR$")>; def : InstRW<[GenericDSPShort], (instregex "^EXTPDPV$")>; def : InstRW<[GenericDSPShort], (instregex "^EXTPDP$")>; def : InstRW<[GenericDSPShort], (instregex "^EXTPV$")>; def : InstRW<[GenericDSPShort], (instregex "^EXTP$")>; def : InstRW<[GenericDSPShort], (instregex "^LBUX$")>; def : InstRW<[GenericDSPShort], (instregex "^LHX$")>; def : InstRW<[GenericDSPShort], (instregex "^LWX$")>; def : InstRW<[GenericDSPShort], (instregex "^MADDU_DSP$")>; def : InstRW<[GenericDSPShort], (instregex "^MADD_DSP$")>; def : InstRW<[GenericDSPShort], (instregex "^MAQ_SA_W_PHL$")>; def : InstRW<[GenericDSPShort], (instregex "^MAQ_SA_W_PHR$")>; def : InstRW<[GenericDSPShort], (instregex "^MAQ_S_W_PHL$")>; def : InstRW<[GenericDSPShort], (instregex "^MAQ_S_W_PHR$")>; def : InstRW<[GenericDSPShort], (instregex "^MFHI_DSP$")>; def : InstRW<[GenericDSPShort], (instregex "^MFLO_DSP$")>; def : InstRW<[GenericDSPShort], (instregex "^MODSUB$")>; def : InstRW<[GenericDSPShort], (instregex "^MSUBU_DSP$")>; def : InstRW<[GenericDSPShort], (instregex "^MSUB_DSP$")>; def : InstRW<[GenericDSPShort], (instregex "^MULEQ_S_W_PHL$")>; def : InstRW<[GenericDSPShort], (instregex "^MULEQ_S_W_PHR$")>; def : InstRW<[GenericDSPShort], (instregex "^MULEU_S_PH_QBL$")>; def : InstRW<[GenericDSPShort], (instregex "^MULEU_S_PH_QBR$")>; def : InstRW<[GenericDSPShort], (instregex "^MULQ_RS_PH$")>; def : InstRW<[GenericDSPShort], (instregex "^MULSAQ_S_W_PH$")>; def : InstRW<[GenericDSPShort], (instregex "^MULTU_DSP$")>; def : InstRW<[GenericDSPShort], (instregex "^MULT_DSP$")>; def : InstRW<[GenericDSPShort], (instregex "^PACKRL_PH$")>; def : InstRW<[GenericDSPShort], (instregex "^PICK_PH$")>; def : InstRW<[GenericDSPShort], (instregex "^PICK_QB$")>; def : InstRW<[GenericDSPShort], (instregex "^PRECEQU_PH_QBLA$")>; def : InstRW<[GenericDSPShort], (instregex "^PRECEQU_PH_QBL$")>; def : InstRW<[GenericDSPShort], (instregex "^PRECEQU_PH_QBRA$")>; def : InstRW<[GenericDSPShort], (instregex "^PRECEQU_PH_QBR$")>; def : InstRW<[GenericDSPShort], (instregex "^PRECEQ_W_PHL$")>; def : InstRW<[GenericDSPShort], (instregex "^PRECEQ_W_PHR$")>; def : InstRW<[GenericDSPShort], (instregex "^PRECEU_PH_QBLA$")>; def : InstRW<[GenericDSPShort], (instregex "^PRECEU_PH_QBL$")>; def : InstRW<[GenericDSPShort], (instregex "^PRECEU_PH_QBRA$")>; def : InstRW<[GenericDSPShort], (instregex "^PRECEU_PH_QBR$")>; def : InstRW<[GenericDSPShort], (instregex "^PRECRQU_S_QB_PH$")>; def : InstRW<[GenericDSPShort], (instregex "^PRECRQ_PH_W$")>; def : InstRW<[GenericDSPShort], (instregex "^PRECRQ_QB_PH$")>; def : InstRW<[GenericDSPShort], (instregex "^PRECRQ_RS_PH_W$")>; def : InstRW<[GenericDSPShort], (instregex "^RADDU_W_QB$")>; def : InstRW<[GenericDSPShort], (instregex "^RDDSP$")>; def : InstRW<[GenericDSPShort], (instregex "^REPLV_PH$")>; def : InstRW<[GenericDSPShort], (instregex "^REPLV_QB$")>; def : InstRW<[GenericDSPShort], (instregex "^REPL_PH$")>; def : InstRW<[GenericDSPShort], (instregex "^REPL_QB$")>; def : InstRW<[GenericDSPShort], (instregex "^SHILOV$")>; def : InstRW<[GenericDSPShort], (instregex "^SHILO$")>; def : InstRW<[GenericDSPShort], (instregex "^SHLLV_PH$")>; def : InstRW<[GenericDSPShort], (instregex "^SHLLV_QB$")>; def : InstRW<[GenericDSPShort], (instregex "^SHLLV_S_PH$")>; def : InstRW<[GenericDSPShort], (instregex "^SHLLV_S_W$")>; def : InstRW<[GenericDSPShort], (instregex "^SHLL_PH$")>; def : InstRW<[GenericDSPShort], (instregex "^SHLL_QB$")>; def : InstRW<[GenericDSPShort], (instregex "^SHLL_S_PH$")>; def : InstRW<[GenericDSPShort], (instregex "^SHLL_S_W$")>; def : InstRW<[GenericDSPShort], (instregex "^SHRAV_PH$")>; def : InstRW<[GenericDSPShort], (instregex "^SHRAV_R_PH$")>; def : InstRW<[GenericDSPShort], (instregex "^SHRAV_R_W$")>; def : InstRW<[GenericDSPShort], (instregex "^SHRA_PH$")>; def : InstRW<[GenericDSPShort], (instregex "^SHRA_R_PH$")>; def : InstRW<[GenericDSPShort], (instregex "^SHRA_R_W$")>; def : InstRW<[GenericDSPShort], (instregex "^SHRLV_QB$")>; def : InstRW<[GenericDSPShort], (instregex "^SHRL_QB$")>; def : InstRW<[GenericDSPShort], (instregex "^SUBQ_PH$")>; def : InstRW<[GenericDSPShort], (instregex "^SUBQ_S_PH$")>; def : InstRW<[GenericDSPShort], (instregex "^SUBQ_S_W$")>; def : InstRW<[GenericDSPShort], (instregex "^SUBU_QB$")>; def : InstRW<[GenericDSPShort], (instregex "^SUBU_S_QB$")>; def : InstRW<[GenericDSPShort], (instregex "^WRDSP$")>; def : InstRW<[GenericDSPShort], (instregex "^Pseudo(CMP|CMPU)_(EQ|LE|LT)_(PH|QB)$")>; def : InstRW<[GenericDSPShort], (instregex "^PseudoPICK_(PH|QB)$")>; // MIPS DSP R2 - hasDSP, HasDSPR2, InMicroMips // =========================================== def : InstRW<[GenericDSPShort], (instregex "^ABSQ_S_QB$")>; def : InstRW<[GenericDSPShort], (instregex "^ADDQH_PH$")>; def : InstRW<[GenericDSPShort], (instregex "^ADDQH_R_PH$")>; def : InstRW<[GenericDSPShort], (instregex "^ADDQH_R_W$")>; def : InstRW<[GenericDSPShort], (instregex "^ADDQH_W$")>; def : InstRW<[GenericDSPShort], (instregex "^ADDUH_QB$")>; def : InstRW<[GenericDSPShort], (instregex "^ADDUH_R_QB$")>; def : InstRW<[GenericDSPShort], (instregex "^ADDU_PH$")>; def : InstRW<[GenericDSPShort], (instregex "^ADDU_S_PH$")>; def : InstRW<[GenericDSPShort], (instregex "^APPEND$")>; def : InstRW<[GenericDSPShort], (instregex "^BALIGN$")>; def : InstRW<[GenericDSPShort], (instregex "^CMPGDU_EQ_QB$")>; def : InstRW<[GenericDSPShort], (instregex "^CMPGDU_LE_QB$")>; def : InstRW<[GenericDSPShort], (instregex "^CMPGDU_LT_QB$")>; def : InstRW<[GenericDSPShort], (instregex "^DPA_W_PH$")>; def : InstRW<[GenericDSPShort], (instregex "^DPAQX_SA_W_PH$")>; def : InstRW<[GenericDSPShort], (instregex "^DPAQX_S_W_PH$")>; def : InstRW<[GenericDSPShort], (instregex "^DPAX_W_PH$")>; def : InstRW<[GenericDSPShort], (instregex "^DPS_W_PH$")>; def : InstRW<[GenericDSPShort], (instregex "^DPSQX_S_W_PH$")>; def : InstRW<[GenericDSPShort], (instregex "^DPSQX_SA_W_PH$")>; def : InstRW<[GenericDSPShort], (instregex "^DPSX_W_PH$")>; def : InstRW<[GenericDSPShort], (instregex "^MUL_PH$")>; def : InstRW<[GenericDSPShort], (instregex "^MUL_S_PH$")>; def : InstRW<[GenericDSPShort], (instregex "^MULQ_RS_W$")>; def : InstRW<[GenericDSPShort], (instregex "^MULQ_S_PH$")>; def : InstRW<[GenericDSPShort], (instregex "^MULQ_S_W$")>; def : InstRW<[GenericDSPShort], (instregex "^MULSA_W_PH$")>; def : InstRW<[GenericDSPShort], (instregex "^PRECR_QB_PH$")>; def : InstRW<[GenericDSPShort], (instregex "^PRECR_SRA_PH_W$")>; def : InstRW<[GenericDSPShort], (instregex "^PRECR_SRA_R_PH_W$")>; def : InstRW<[GenericDSPShort], (instregex "^PREPEND$")>; def : InstRW<[GenericDSPShort], (instregex "^SHRA_QB$")>; def : InstRW<[GenericDSPShort], (instregex "^SHRA_R_QB$")>; def : InstRW<[GenericDSPShort], (instregex "^SHRAV_QB$")>; def : InstRW<[GenericDSPShort], (instregex "^SHRAV_R_QB$")>; def : InstRW<[GenericDSPShort], (instregex "^SHRL_PH$")>; def : InstRW<[GenericDSPShort], (instregex "^SHRLV_PH$")>; def : InstRW<[GenericDSPShort], (instregex "^SUBQH_PH$")>; def : InstRW<[GenericDSPShort], (instregex "^SUBQH_R_PH$")>; def : InstRW<[GenericDSPShort], (instregex "^SUBQH_W$")>; def : InstRW<[GenericDSPShort], (instregex "^SUBQH_R_W$")>; def : InstRW<[GenericDSPShort], (instregex "^SUBU_PH$")>; def : InstRW<[GenericDSPShort], (instregex "^SUBU_S_PH$")>; def : InstRW<[GenericDSPShort], (instregex "^SUBUH_QB$")>; def : InstRW<[GenericDSPShort], (instregex "^SUBUH_R_QB$")>; // microMIPS DSP R1 - HasDSP, InMicroMips // ====================================== def : InstRW<[GenericWriteLoad], (instrs LWDSP_MM)>; def : InstRW<[GenericWriteStore], (instrs SWDSP_MM)>; def : InstRW<[GenericDSPShort], (instregex "^ABSQ_S_PH_MM$")>; def : InstRW<[GenericDSPShort], (instregex "^ABSQ_S_W_MM$")>; def : InstRW<[GenericDSPShort], (instregex "^ADDQ_PH_MM$")>; def : InstRW<[GenericDSPShort], (instregex "^ADDQ_S_PH_MM$")>; def : InstRW<[GenericDSPShort], (instregex "^ADDQ_S_W_MM$")>; def : InstRW<[GenericDSPShort], (instregex "^ADDSC_MM$")>; def : InstRW<[GenericDSPShort], (instregex "^ADDU_QB_MM$")>; def : InstRW<[GenericDSPShort], (instregex "^ADDU_S_QB_MM$")>; def : InstRW<[GenericDSPShort], (instregex "^ADDWC_MM$")>; def : InstRW<[GenericDSPShort], (instregex "^BITREV_MM$")>; def : InstRW<[GenericDSPShort], (instregex "^BPOSGE32_MM$")>; def : InstRW<[GenericDSPShort], (instregex "^CMPGU_EQ_QB_MM$")>; def : InstRW<[GenericDSPShort], (instregex "^CMPGU_LE_QB_MM$")>; def : InstRW<[GenericDSPShort], (instregex "^CMPGU_LT_QB_MM$")>; def : InstRW<[GenericDSPShort], (instregex "^CMPU_EQ_QB_MM$")>; def : InstRW<[GenericDSPShort], (instregex "^CMPU_LE_QB_MM$")>; def : InstRW<[GenericDSPShort], (instregex "^CMPU_LT_QB_MM$")>; def : InstRW<[GenericDSPShort], (instregex "^CMP_EQ_PH_MM$")>; def : InstRW<[GenericDSPShort], (instregex "^CMP_LE_PH_MM$")>; def : InstRW<[GenericDSPShort], (instregex "^CMP_LT_PH_MM$")>; def : InstRW<[GenericDSPShort], (instregex "^DPAQ_SA_L_W_MM$")>; def : InstRW<[GenericDSPShort], (instregex "^DPAQ_S_W_PH_MM$")>; def : InstRW<[GenericDSPShort], (instregex "^DPAU_H_QBL_MM$")>; def : InstRW<[GenericDSPShort], (instregex "^DPAU_H_QBR_MM$")>; def : InstRW<[GenericDSPShort], (instregex "^DPSQ_SA_L_W_MM$")>; def : InstRW<[GenericDSPShort], (instregex "^DPSQ_S_W_PH_MM$")>; def : InstRW<[GenericDSPShort], (instregex "^DPSU_H_QBL_MM$")>; def : InstRW<[GenericDSPShort], (instregex "^DPSU_H_QBR_MM$")>; def : InstRW<[GenericDSPShort], (instregex "^EXTPDPV_MM$")>; def : InstRW<[GenericDSPShort], (instregex "^EXTPDP_MM$")>; def : InstRW<[GenericDSPShort], (instregex "^EXTPV_MM$")>; def : InstRW<[GenericDSPShort], (instregex "^EXTP_MM$")>; def : InstRW<[GenericDSPShort], (instregex "^EXTRV_RS_W_MM$")>; def : InstRW<[GenericDSPShort], (instregex "^EXTRV_R_W_MM$")>; def : InstRW<[GenericDSPShort], (instregex "^EXTRV_S_H_MM$")>; def : InstRW<[GenericDSPShort], (instregex "^EXTRV_W_MM$")>; def : InstRW<[GenericDSPShort], (instregex "^EXTR_RS_W_MM$")>; def : InstRW<[GenericDSPShort], (instregex "^EXTR_R_W_MM$")>; def : InstRW<[GenericDSPShort], (instregex "^EXTR_S_H_MM$")>; def : InstRW<[GenericDSPShort], (instregex "^EXTR_W_MM$")>; def : InstRW<[GenericDSPShort], (instregex "^INSV_MM$")>; def : InstRW<[GenericDSPShort], (instregex "^LBUX_MM$")>; def : InstRW<[GenericDSPShort], (instregex "^LHX_MM$")>; def : InstRW<[GenericDSPShort], (instregex "^LWX_MM$")>; def : InstRW<[GenericDSPShort], (instregex "^MADDU_DSP_MM$")>; def : InstRW<[GenericDSPShort], (instregex "^MADD_DSP_MM$")>; def : InstRW<[GenericDSPShort], (instregex "^MAQ_SA_W_PHL_MM$")>; def : InstRW<[GenericDSPShort], (instregex "^MAQ_SA_W_PHR_MM$")>; def : InstRW<[GenericDSPShort], (instregex "^MAQ_S_W_PHL_MM$")>; def : InstRW<[GenericDSPShort], (instregex "^MAQ_S_W_PHR_MM$")>; def : InstRW<[GenericDSPShort], (instregex "^MFHI_DSP_MM$")>; def : InstRW<[GenericDSPShort], (instregex "^MFLO_DSP_MM$")>; def : InstRW<[GenericDSPShort], (instregex "^MODSUB_MM$")>; def : InstRW<[GenericDSPShort], (instregex "^MOVEP_MMR6$")>; def : InstRW<[GenericDSPShort], (instregex "^MOVN_I_MM$")>; def : InstRW<[GenericDSPShort], (instregex "^MOVZ_I_MM$")>; def : InstRW<[GenericDSPShort], (instregex "^MSUBU_DSP_MM$")>; def : InstRW<[GenericDSPShort], (instregex "^MSUB_DSP_MM$")>; def : InstRW<[GenericDSPShort], (instregex "^MTHI_DSP_MM$")>; def : InstRW<[GenericDSPShort], (instregex "^MTHLIP_MM$")>; def : InstRW<[GenericDSPShort], (instregex "^MTLO_DSP_MM$")>; def : InstRW<[GenericDSPShort], (instregex "^MULEQ_S_W_PHL_MM$")>; def : InstRW<[GenericDSPShort], (instregex "^MULEQ_S_W_PHR_MM$")>; def : InstRW<[GenericDSPShort], (instregex "^MULEU_S_PH_QBL_MM$")>; def : InstRW<[GenericDSPShort], (instregex "^MULEU_S_PH_QBR_MM$")>; def : InstRW<[GenericDSPShort], (instregex "^MULQ_RS_PH_MM$")>; def : InstRW<[GenericDSPShort], (instregex "^MULSAQ_S_W_PH_MM$")>; def : InstRW<[GenericDSPShort], (instregex "^MULTU_DSP_MM$")>; def : InstRW<[GenericDSPShort], (instregex "^MULT_DSP_MM$")>; def : InstRW<[GenericDSPShort], (instregex "^PACKRL_PH_MM$")>; def : InstRW<[GenericDSPShort], (instregex "^PICK_PH_MM$")>; def : InstRW<[GenericDSPShort], (instregex "^PICK_QB_MM$")>; def : InstRW<[GenericDSPShort], (instregex "^PRECEQU_PH_QBLA_MM$")>; def : InstRW<[GenericDSPShort], (instregex "^PRECEQU_PH_QBL_MM$")>; def : InstRW<[GenericDSPShort], (instregex "^PRECEQU_PH_QBRA_MM$")>; def : InstRW<[GenericDSPShort], (instregex "^PRECEQU_PH_QBR_MM$")>; def : InstRW<[GenericDSPShort], (instregex "^PRECEQ_W_PHL_MM$")>; def : InstRW<[GenericDSPShort], (instregex "^PRECEQ_W_PHR_MM$")>; def : InstRW<[GenericDSPShort], (instregex "^PRECEU_PH_QBLA_MM$")>; def : InstRW<[GenericDSPShort], (instregex "^PRECEU_PH_QBL_MM$")>; def : InstRW<[GenericDSPShort], (instregex "^PRECEU_PH_QBRA_MM$")>; def : InstRW<[GenericDSPShort], (instregex "^PRECEU_PH_QBR_MM$")>; def : InstRW<[GenericDSPShort], (instregex "^PRECRQU_S_QB_PH_MM$")>; def : InstRW<[GenericDSPShort], (instregex "^PRECRQ_PH_W_MM$")>; def : InstRW<[GenericDSPShort], (instregex "^PRECRQ_QB_PH_MM$")>; def : InstRW<[GenericDSPShort], (instregex "^PRECRQ_RS_PH_W_MM$")>; def : InstRW<[GenericDSPShort], (instregex "^RADDU_W_QB_MM$")>; def : InstRW<[GenericDSPShort], (instregex "^RDDSP_MM$")>; def : InstRW<[GenericDSPShort], (instregex "^REPLV_PH_MM$")>; def : InstRW<[GenericDSPShort], (instregex "^REPLV_QB_MM$")>; def : InstRW<[GenericDSPShort], (instregex "^REPL_PH_MM$")>; def : InstRW<[GenericDSPShort], (instregex "^REPL_QB_MM$")>; def : InstRW<[GenericDSPShort], (instregex "^SHILOV_MM$")>; def : InstRW<[GenericDSPShort], (instregex "^SHILO_MM$")>; def : InstRW<[GenericDSPShort], (instregex "^SHLLV_PH_MM$")>; def : InstRW<[GenericDSPShort], (instregex "^SHLLV_QB_MM$")>; def : InstRW<[GenericDSPShort], (instregex "^SHLLV_S_PH_MM$")>; def : InstRW<[GenericDSPShort], (instregex "^SHLLV_S_W_MM$")>; def : InstRW<[GenericDSPShort], (instregex "^SHLL_PH_MM$")>; def : InstRW<[GenericDSPShort], (instregex "^SHLL_QB_MM$")>; def : InstRW<[GenericDSPShort], (instregex "^SHLL_S_PH_MM$")>; def : InstRW<[GenericDSPShort], (instregex "^SHLL_S_W_MM$")>; def : InstRW<[GenericDSPShort], (instregex "^SHRAV_PH_MM$")>; def : InstRW<[GenericDSPShort], (instregex "^SHRAV_R_PH_MM$")>; def : InstRW<[GenericDSPShort], (instregex "^SHRAV_R_W_MM$")>; def : InstRW<[GenericDSPShort], (instregex "^SHRA_PH_MM$")>; def : InstRW<[GenericDSPShort], (instregex "^SHRA_R_PH_MM$")>; def : InstRW<[GenericDSPShort], (instregex "^SHRA_R_W_MM$")>; def : InstRW<[GenericDSPShort], (instregex "^SHRLV_QB_MM$")>; def : InstRW<[GenericDSPShort], (instregex "^SHRL_QB_MM$")>; def : InstRW<[GenericDSPShort], (instregex "^SUBQ_PH_MM$")>; def : InstRW<[GenericDSPShort], (instregex "^SUBQ_S_PH_MM$")>; def : InstRW<[GenericDSPShort], (instregex "^SUBQ_S_W_MM$")>; def : InstRW<[GenericDSPShort], (instregex "^SUBU_QB_MM$")>; def : InstRW<[GenericDSPShort], (instregex "^SUBU_S_QB_MM$")>; def : InstRW<[GenericDSPShort], (instregex "^WRDSP_MM$")>; // microMIPS DSP R2 - hasDSP, HasDSPR2, InMicroMips // ================================================ def : InstRW<[GenericDSPShort], (instregex "^ABSQ_S_QB_MMR2$")>; def : InstRW<[GenericDSPShort], (instregex "^ADDQH_PH_MMR2$")>; def : InstRW<[GenericDSPShort], (instregex "^ADDQH_R_PH_MMR2$")>; def : InstRW<[GenericDSPShort], (instregex "^ADDQH_R_W_MMR2$")>; def : InstRW<[GenericDSPShort], (instregex "^ADDQH_W_MMR2$")>; def : InstRW<[GenericDSPShort], (instregex "^ADDUH_QB_MMR2$")>; def : InstRW<[GenericDSPShort], (instregex "^ADDUH_R_QB_MMR2$")>; def : InstRW<[GenericDSPShort], (instregex "^ADDU_PH_MMR2$")>; def : InstRW<[GenericDSPShort], (instregex "^ADDU_S_PH_MMR2$")>; def : InstRW<[GenericDSPShort], (instregex "^APPEND_MMR2$")>; def : InstRW<[GenericDSPShort], (instregex "^BALIGN_MMR2$")>; def : InstRW<[GenericDSPShort], (instregex "^CMPGDU_EQ_QB_MMR2$")>; def : InstRW<[GenericDSPShort], (instregex "^CMPGDU_LE_QB_MMR2$")>; def : InstRW<[GenericDSPShort], (instregex "^CMPGDU_LT_QB_MMR2$")>; def : InstRW<[GenericDSPShort], (instregex "^DPA_W_PH_MMR2$")>; def : InstRW<[GenericDSPShort], (instregex "^DPAQX_SA_W_PH_MMR2$")>; def : InstRW<[GenericDSPShort], (instregex "^DPAQX_S_W_PH_MMR2$")>; def : InstRW<[GenericDSPShort], (instregex "^DPAX_W_PH_MMR2$")>; def : InstRW<[GenericDSPShort], (instregex "^DPS_W_PH_MMR2$")>; def : InstRW<[GenericDSPShort], (instregex "^DPSQX_S_W_PH_MMR2$")>; def : InstRW<[GenericDSPShort], (instregex "^DPSQX_SA_W_PH_MMR2$")>; def : InstRW<[GenericDSPShort], (instregex "^DPSX_W_PH_MMR2$")>; def : InstRW<[GenericDSPShort], (instregex "^MUL_PH_MMR2$")>; def : InstRW<[GenericDSPShort], (instregex "^MUL_S_PH_MMR2$")>; def : InstRW<[GenericDSPShort], (instregex "^MULQ_RS_W_MMR2$")>; def : InstRW<[GenericDSPShort], (instregex "^MULQ_S_PH_MMR2$")>; def : InstRW<[GenericDSPShort], (instregex "^MULQ_S_W_MMR2$")>; def : InstRW<[GenericDSPShort], (instregex "^MULSA_W_PH_MMR2$")>; def : InstRW<[GenericDSPShort], (instregex "^PRECR_QB_PH_MMR2$")>; def : InstRW<[GenericDSPShort], (instregex "^PRECR_SRA_PH_W_MMR2$")>; def : InstRW<[GenericDSPShort], (instregex "^PRECR_SRA_R_PH_W_MMR2$")>; def : InstRW<[GenericDSPShort], (instregex "^PREPEND_MMR2$")>; def : InstRW<[GenericDSPShort], (instregex "^SHRA_QB_MMR2$")>; def : InstRW<[GenericDSPShort], (instregex "^SHRA_R_QB_MMR2$")>; def : InstRW<[GenericDSPShort], (instregex "^SHRAV_QB_MMR2$")>; def : InstRW<[GenericDSPShort], (instregex "^SHRAV_R_QB_MMR2$")>; def : InstRW<[GenericDSPShort], (instregex "^SHRL_PH_MMR2$")>; def : InstRW<[GenericDSPShort], (instregex "^SHRLV_PH_MMR2$")>; def : InstRW<[GenericDSPShort], (instregex "^SUBQH_PH_MMR2$")>; def : InstRW<[GenericDSPShort], (instregex "^SUBQH_R_PH_MMR2$")>; def : InstRW<[GenericDSPShort], (instregex "^SUBQH_W_MMR2$")>; def : InstRW<[GenericDSPShort], (instregex "^SUBQH_R_W_MMR2$")>; def : InstRW<[GenericDSPShort], (instregex "^SUBU_PH_MMR2$")>; def : InstRW<[GenericDSPShort], (instregex "^SUBU_S_PH_MMR2$")>; def : InstRW<[GenericDSPShort], (instregex "^SUBUH_QB_MMR2$")>; def : InstRW<[GenericDSPShort], (instregex "^SUBUH_R_QB_MMR2$")>; // microMIPS DSP R3 - hasDSP, hasDSPR2, hasDSPR3, InMicroMips // ========================================================== def : InstRW<[GenericDSPShort], (instregex "^BPOSGE32C_MMR3$")>; // MIPS MSA ASE - hasMSA // ===================== def GenericWriteMSAShortLogic : SchedWriteRes<[GenericIssueFPUS]>; def GenericWriteMSAShortInt : SchedWriteRes<[GenericIssueFPUS]> { let Latency = 2; } def GenericWriteMoveOtherUnitsToFPU : SchedWriteRes<[GenericIssueFPUS]>; def GenericWriteMSAOther3 : SchedWriteRes<[GenericIssueFPUS]> { let Latency = 3; } def GenericWriteMSALongInt : SchedWriteRes<[GenericIssueFPUS]> { let Latency = 5; } def GenericWriteFPUDivI : SchedWriteRes<[GenericFPQ]> { let Latency = 33; let ResourceCycles = [ 33 ]; } // FPUS is also used in moves from floating point and MSA registers to general // purpose registers. def GenericWriteMoveFPUSToOtherUnits : SchedWriteRes<[GenericIssueFPUS]> { let Latency = 0; } // FPUL is also used in moves from floating point and MSA registers to general // purpose registers. def GenericWriteMoveFPULToOtherUnits : SchedWriteRes<[GenericIssueFPUL]>; // adds_a.[bhwd], adds_[asu].[bhwd], addvi?.[bhwd], asub_[us].[bhwd], // aver?_[us].[bhwd] def : InstRW<[GenericWriteMSAShortInt], (instregex "^ADD_A_[BHWD]$")>; def : InstRW<[GenericWriteMSAShortInt], (instregex "^ADDS_[ASU]_[BHWD]$")>; // TODO: ADDVI_[BHW] might be 1 cycle latency rather than 2. Need to confirm it. // add.[bhwd], addvi.[bhwd], asub_[us].[bhwd], ave.[bhwd], aver.[bhwd] def : InstRW<[GenericWriteMSAShortInt], (instregex "^ADDVI?_[BHWD]$")>; def : InstRW<[GenericWriteMSAShortInt], (instregex "^ASUB_[US].[BHWD]$")>; def : InstRW<[GenericWriteMSAShortInt], (instregex "^AVER?_[US].[BHWD]$")>; // and.v, andi.b, move.v, ldi.[bhwd], xor.v, nor.v, xori.b, nori.b, lsa def : InstRW<[GenericWriteMSAShortLogic], (instregex "^MOVE_V$")>; def : InstRW<[GenericWriteMSAShortLogic], (instregex "^LDI_[BHWD]$")>; def : InstRW<[GenericWriteMSAShortLogic], (instrs LSA)>; def : InstRW<[GenericWriteMSAShortLogic], (instregex "^(AND|OR|[XN]OR)_V$")>; def : InstRW<[GenericWriteMSAShortLogic], (instregex "^(AND|OR|[XN]OR)I_B$")>; def : InstRW<[GenericWriteMSAShortLogic], (instregex "^(AND|OR|[XN]OR)_V_[DHW]_PSEUDO$")>; // vshf.[bhwd], binsl.[bhwd], binsr.[bhwd], insert.[bhwd], sld?.[bhwd], // bset.[bhwd], bclr.[bhwd], bneg.[bhwd], bsel_v, bseli_b def : InstRW<[GenericWriteMSAShortInt], (instregex "^VSHF_[BHWD]$")>; def : InstRW<[GenericWriteMSAShortInt], (instregex "^(BINSL|BINSLI)_[BHWD]$")>; def : InstRW<[GenericWriteMSAShortInt], (instregex "^(BINSR|BINSRI)_[BHWD]$")>; def : InstRW<[GenericWriteMSAShortInt], (instregex "^INSERT_[BHWD]$")>; def : InstRW<[GenericWriteMSAShortInt], (instregex "^(SLD|SLDI)_[BHWD]$")>; def : InstRW<[GenericWriteMSAShortInt], (instregex "^(BSET|BSETI)_[BHWD]$")>; def : InstRW<[GenericWriteMSAShortInt], (instregex "^(BCLR|BCLRI)_[BHWD]$")>; def : InstRW<[GenericWriteMSAShortInt], (instregex "^(BNEG|BNEGI)_[BHWD]$")>; def : InstRW<[GenericWriteMSAShortInt], (instregex "^(BSEL_V|BSELI_B)$")>; def : InstRW<[GenericWriteMSAShortInt], (instregex "^BMN*Z.*$")>; def : InstRW<[GenericWriteMSAShortInt], (instregex "^BSEL_(H|W|D|FW|FD)_PSEUDO$")>; // pcnt.[bhwd], sat_s.[bhwd], sat_u.[bhwd] def : InstRW<[GenericWriteMSAOther3], (instregex "^PCNT_[BHWD]$")>; def : InstRW<[GenericWriteMSAOther3], (instregex "^SAT_(S|U)_[BHWD]$")>; // bnz.[bhwdv], cfcmsa, ctcmsa def : InstRW<[GenericWriteMSAShortLogic], (instregex "^(BNZ|BZ)_[BHWDV]$")>; def : InstRW<[GenericWriteMSAShortLogic], (instregex "^C(F|T)CMSA$")>; // shf.[bhw], fill[bhwd], splat?.[bhwd] def : InstRW<[GenericWriteMSAShortInt], (instregex "^SHF_[BHW]$")>; def : InstRW<[GenericWriteMSAShortInt], (instregex "^FILL_[BHWD]$")>; def : InstRW<[GenericWriteMSAShortInt], (instregex "^(SPLAT|SPLATI)_[BHWD]$")>; // fexp2_w, fexp2_d def : InstRW<[GenericWriteFPUS], (instregex "^FEXP2_(W|D)$")>; // compare, converts, round to int, floating point truncate. def : InstRW<[GenericWriteFPUS], (instregex "^(CLT|CLTI)_(S|U)_[BHWD]$")>; def : InstRW<[GenericWriteFPUS], (instregex "^(CLE|CLEI)_(S|U)_[BHWD]$")>; def : InstRW<[GenericWriteFPUS], (instregex "^(CEQ|CEQI)_[BHWD]$")>; def : InstRW<[GenericWriteFPUS], (instregex "^CMP_UN_(S|D)$")>; def : InstRW<[GenericWriteFPUS], (instregex "^CMP_UEQ_(S|D)$")>; def : InstRW<[GenericWriteFPUS], (instregex "^CMP_EQ_(S|D)$")>; def : InstRW<[GenericWriteFPUS], (instregex "^CMP_LT_(S|D)$")>; def : InstRW<[GenericWriteFPUS], (instregex "^CMP_ULT_(S|D)$")>; def : InstRW<[GenericWriteFPUS], (instregex "^CMP_LE_(S|D)$")>; def : InstRW<[GenericWriteFPUS], (instregex "^CMP_ULE_(S|D)$")>; def : InstRW<[GenericWriteFPUS], (instregex "^CMP_F_(D|S)$")>; def : InstRW<[GenericWriteFPUS], (instregex "^CMP_SAF_(D|S)$")>; def : InstRW<[GenericWriteFPUS], (instregex "^CMP_SEQ_(D|S)$")>; def : InstRW<[GenericWriteFPUS], (instregex "^CMP_SLE_(D|S)$")>; def : InstRW<[GenericWriteFPUS], (instregex "^CMP_SLT_(D|S)$")>; def : InstRW<[GenericWriteFPUS], (instregex "^CMP_SUEQ_(D|S)$")>; def : InstRW<[GenericWriteFPUS], (instregex "^CMP_SULE_(D|S)$")>; def : InstRW<[GenericWriteFPUS], (instregex "^CMP_SULT_(D|S)$")>; def : InstRW<[GenericWriteFPUS], (instregex "^CMP_SUN_(D|S)$")>; def : InstRW<[GenericWriteFPUS], (instregex "^FS(AF|EQ|LT|LE|NE|OR)_(W|D)$")>; def : InstRW<[GenericWriteFPUS], (instregex "^FSUEQ_(W|D)$")>; def : InstRW<[GenericWriteFPUS], (instregex "^FSULE_(W|D)$")>; def : InstRW<[GenericWriteFPUS], (instregex "^FSULT_(W|D)$")>; def : InstRW<[GenericWriteFPUS], (instregex "^FSUNE_(W|D)$")>; def : InstRW<[GenericWriteFPUS], (instregex "^FSUN_(W|D)$")>; def : InstRW<[GenericWriteFPUS], (instregex "^FCAF_(W|D)$")>; def : InstRW<[GenericWriteFPUS], (instregex "^FCEQ_(W|D)$")>; def : InstRW<[GenericWriteFPUS], (instregex "^FCLE_(W|D)$")>; def : InstRW<[GenericWriteFPUS], (instregex "^FCLT_(W|D)$")>; def : InstRW<[GenericWriteFPUS], (instregex "^FCNE_(W|D)$")>; def : InstRW<[GenericWriteFPUS], (instregex "^FCOR_(W|D)$")>; def : InstRW<[GenericWriteFPUS], (instregex "^FCUEQ_(W|D)$")>; def : InstRW<[GenericWriteFPUS], (instregex "^FCULE_(W|D)$")>; def : InstRW<[GenericWriteFPUS], (instregex "^FCULT_(W|D)$")>; def : InstRW<[GenericWriteFPUS], (instregex "^FCUNE_(W|D)$")>; def : InstRW<[GenericWriteFPUS], (instregex "^FCUN_(W|D)$")>; def : InstRW<[GenericWriteFPUS], (instregex "^FABS_(W|D)$")>; def : InstRW<[GenericWriteFPUS], (instregex "^FFINT_(U|S)_(W|D)$")>; def : InstRW<[GenericWriteFPUS], (instregex "^FFQL_(W|D)$")>; def : InstRW<[GenericWriteFPUS], (instregex "^FFQR_(W|D)$")>; def : InstRW<[GenericWriteFPUS], (instregex "^FTINT_(U|S)_(W|D)$")>; def : InstRW<[GenericWriteFPUS], (instregex "^FRINT_(W|D)$")>; def : InstRW<[GenericWriteFPUS], (instregex "^FTQ_(H|W)$")>; def : InstRW<[GenericWriteFPUS], (instregex "^FTRUNC_(U|S)_(W|D)$")>; // fexdo.[hw], fexupl.[wd], fexupr.[wd] def : InstRW<[GenericWriteFPUS], (instregex "^FEXDO_(H|W)$")>; def : InstRW<[GenericWriteFPUS], (instregex "^FEXUPL_(W|D)$")>; def : InstRW<[GenericWriteFPUS], (instregex "^FEXUPR_(W|D)$")>; // fclass.[wd], fmax.[wd], fmax_a.[wd], fmin.[wd], fmin_a.[wd], flog2.[wd] def : InstRW<[GenericWriteFPUS], (instregex "^FCLASS_(W|D)$")>; def : InstRW<[GenericWriteFPUS], (instregex "^FMAX_A_(W|D)$")>; def : InstRW<[GenericWriteFPUS], (instregex "^FMAX_(W|D)$")>; def : InstRW<[GenericWriteFPUS], (instregex "^FMIN_A_(W|D)$")>; def : InstRW<[GenericWriteFPUS], (instregex "^FMIN_(W|D)$")>; def : InstRW<[GenericWriteFPUS], (instregex "^FLOG2_(W|D)$")>; // interleave right/left, interleave even/odd, insert def : InstRW<[GenericWriteMSAShortLogic], (instregex "^(ILVR|ILVL)_[BHWD]$")>; def : InstRW<[GenericWriteMSAShortLogic], (instregex "^(ILVEV|ILVOD)_[BHWD]$")>; // subs_?.[bhwd], subsus_?.[bhwd], subsuu_?.[bhwd], subvi.[bhwd], subv.[bhwd], def : InstRW<[GenericWriteMSAShortInt], (instregex "^SUBS_(S|U)_[BHWD]$")>; def : InstRW<[GenericWriteMSAShortInt], (instregex "^SUBSUS_(S|U)_[BHWD]$")>; def : InstRW<[GenericWriteMSAShortInt], (instregex "^SUBSUU_(S|U)_[BHWD]$")>; def : InstRW<[GenericWriteMSAShortInt], (instregex "^SUBVI_[BHWD]$")>; def : InstRW<[GenericWriteMSAShortInt], (instregex "^SUBV_[BHWD]$")>; // mod_[su].[bhwd], div_[su].[bhwd] def : InstRW<[GenericWriteFPUDivI], (instregex "^MOD_(S|U)_[BHWD]$")>; def : InstRW<[GenericWriteFPUDivI], (instregex "^DIV_(S|U)_[BHWD]$")>; // hadd_[su].[bhwd], hsub_[su].[bhwd], max_[sua].[bhwd], min_[sua].[bhwd], // maxi_[su].[bhwd], mini_[su].[bhwd], sra?.[bhwd], srar?.[bhwd], srlr.[bhwd], // sll?.[bhwd], pckev.[bhwd], pckod.[bhwd], nloc.[bhwd], nlzc.[bhwd], // insve.[bhwd] def : InstRW<[GenericWriteMSAShortLogic], (instregex "^HADD_(S|U)_[BHWD]$")>; def : InstRW<[GenericWriteMSAShortLogic], (instregex "^HSUB_(S|U)_[BHWD]$")>; def : InstRW<[GenericWriteMSAShortLogic], (instregex "^(MAX|MIN)_S_[BHWD]$")>; def : InstRW<[GenericWriteMSAShortLogic], (instregex "^(MAX|MIN)_U_[BHWD]$")>; def : InstRW<[GenericWriteMSAShortLogic], (instregex "^(MAX|MIN)_A_[BHWD]$")>; def : InstRW<[GenericWriteMSAShortLogic], (instregex "^(MAXI|MINI)_(S|U)_[BHWD]$")>; def : InstRW<[GenericWriteMSAShortLogic], (instregex "^(SRA|SRAI)_[BHWD]$")>; def : InstRW<[GenericWriteMSAShortLogic], (instregex "^(SRL|SRLI)_[BHWD]$")>; def : InstRW<[GenericWriteMSAShortLogic], (instregex "^(SRAR|SRARI)_[BHWD]$")>; def : InstRW<[GenericWriteMSAShortLogic], (instregex "^(SRLR|SRLRI)_[BHWD]$")>; def : InstRW<[GenericWriteMSAShortLogic], (instregex "^(SLL|SLLI)_[BHWD]$")>; def : InstRW<[GenericWriteMSAShortLogic], (instregex "^(PCKEV|PCKOD)_[BHWD]$")>; def : InstRW<[GenericWriteMSAShortLogic], (instregex "^(NLOC|NLZC)_[BHWD]$")>; def : InstRW<[GenericWriteMSAShortLogic], (instregex "^INSVE_[BHWD]$")>; def : InstRW<[GenericWriteMSAShortLogic], (instregex "^INSERT_F(D|W)_PSEUDO$")>; def : InstRW<[GenericWriteMSAShortLogic], (instregex "^FILL_F(D|W)_PSEUDO$")>; // dpadd_?.[bhwd], dpsub_?.[bhwd], dotp_?.[bhwd], msubv.[bhwd], maddv.[bhwd] // mulv.[bhwd]. def : InstRW<[GenericWriteMSALongInt], (instregex "^DPADD_(S|U)_[HWD]$")>; def : InstRW<[GenericWriteMSALongInt], (instregex "^DPSUB_(S|U)_[HWD]$")>; def : InstRW<[GenericWriteMSALongInt], (instregex "^DOTP_(S|U)_[HWD]$")>; def : InstRW<[GenericWriteMSALongInt], (instregex "^MSUBV_[BHWD]$")>; def : InstRW<[GenericWriteMSALongInt], (instregex "^MADDV_[BHWD]$")>; def : InstRW<[GenericWriteMSALongInt], (instregex "^MULV_[BHWD]$")>; // madd?.q.[hw], msub?.q.[hw], mul?.q.[hw] def : InstRW<[GenericWriteMSALongInt], (instregex "^MADDR_Q_[HW]$")>; def : InstRW<[GenericWriteMSALongInt], (instregex "^MADD_Q_[HW]$")>; def : InstRW<[GenericWriteMSALongInt], (instregex "^MSUBR_Q_[HW]$")>; def : InstRW<[GenericWriteMSALongInt], (instregex "^MSUB_Q_[HW]$")>; def : InstRW<[GenericWriteMSALongInt], (instregex "^MULR_Q_[HW]$")>; def : InstRW<[GenericWriteMSALongInt], (instregex "^MUL_Q_[HW]$")>; // fadd.[dw], fmadd.[dw], fmul.[dw], frcp.[dw], frsqrt.[dw], fsqrt.[dw] // fsub.[dw], fdiv.[dw] def : InstRW<[GenericWriteFPUL], (instregex "^FADD_[DW]$")>; def : InstRW<[GenericWriteFPUL], (instregex "^FMADD_[DW]$")>; def : InstRW<[GenericWriteFPUL], (instregex "^FMSUB_[DW]$")>; def : InstRW<[GenericWriteFPUL], (instregex "^FMUL_[DW]$")>; def : InstRW<[GenericWriteFPUL], (instregex "^FRCP_[DW]$")>; def : InstRW<[GenericWriteFPUL], (instregex "^FRSQRT_[DW]$")>; def : InstRW<[GenericWriteFPUL], (instregex "^FSQRT_[DW]$")>; def : InstRW<[GenericWriteFPUL], (instregex "^FSUB_[DW]$")>; def : InstRW<[GenericWriteFPUL], (instregex "^FDIV_[DW]$")>; // copy.[su]_[bhwd] def : InstRW<[GenericWriteFPUMoveGPRFPU], (instregex "^COPY_U_[BHW]$")>; def : InstRW<[GenericWriteFPUMoveGPRFPU], (instregex "^COPY_S_[BHWD]$")>; def : InstRW<[GenericWriteFPUStore], (instregex "^ST_[BHWD]$")>; def : InstRW<[GenericWriteFPUStore], (instrs ST_F16)>; def : InstRW<[GenericWriteFPULoad], (instregex "^LD_[BHWD]$")>; def : InstRW<[GenericWriteFPULoad], (instrs LD_F16)>; // Atomic instructions // FIXME: Define `WriteAtomic` in the MipsSchedule.td and // attach it to the Atomic2OpsPostRA, AtomicCmpSwapPostRA, ... // classes. Then just define resources for the `WriteAtomic` in each // machine models. def GenericAtomic : ProcResource<1> { let BufferSize = 1; } def GenericWriteAtomic : SchedWriteRes<[GenericAtomic]> { let Latency = 2; } def : InstRW<[GenericWriteAtomic], (instregex "^ATOMIC_SWAP_I(8|16|32|64)_POSTRA$")>; def : InstRW<[GenericWriteAtomic], (instregex "^ATOMIC_CMP_SWAP_I(8|16|32|64)_POSTRA$")>; def : InstRW<[GenericWriteAtomic], (instregex "^ATOMIC_LOAD_(ADD|SUB|AND|OR|XOR|NAND)_I(8|16|32|64)_POSTRA$")>; } Index: head/contrib/llvm/lib/Target/Mips/MipsScheduleP5600.td =================================================================== --- head/contrib/llvm/lib/Target/Mips/MipsScheduleP5600.td (revision 354978) +++ head/contrib/llvm/lib/Target/Mips/MipsScheduleP5600.td (revision 354979) @@ -1,635 +1,636 @@ //==- MipsScheduleP5600.td - P5600 Scheduling Definitions --*- tablegen -*-===// // // 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 // //===----------------------------------------------------------------------===// def MipsP5600Model : SchedMachineModel { int IssueWidth = 2; // 2x dispatched per cycle int MicroOpBufferSize = 48; // min(48, 48, 64) int LoadLatency = 4; int MispredictPenalty = 8; // TODO: Estimated let CompleteModel = 1; let FullInstRWOverlapCheck = 1; list UnsupportedFeatures = [HasMips3, HasMips32r6, HasMips64, HasMips64r2, HasMips64r5, HasMips64r6, IsGP64bit, IsPTR64bit, - InMicroMips, InMips16Mode, HasCnMips, + InMicroMips, InMips16Mode, + HasCnMips, HasCnMipsP, HasDSP, HasDSPR2, HasMT, HasCRC]; } let SchedModel = MipsP5600Model in { // ALQ Pipelines // ============= def P5600ALQ : ProcResource<1> { let BufferSize = 16; } def P5600IssueALU : ProcResource<1> { let Super = P5600ALQ; } // ALU Pipeline // ------------ def P5600WriteALU : SchedWriteRes<[P5600IssueALU]>; // and, lui, nor, or, slti, sltiu, sub, subu, xor def : InstRW<[P5600WriteALU], (instrs AND, LUi, NOR, OR, SLTi, SLTiu, SUB, SUBu, XOR)>; // AGQ Pipelines // ============= def P5600AGQ : ProcResource<3> { let BufferSize = 16; } def P5600IssueAL2 : ProcResource<1> { let Super = P5600AGQ; } def P5600IssueCTISTD : ProcResource<1> { let Super = P5600AGQ; } def P5600IssueLDST : ProcResource<1> { let Super = P5600AGQ; } def P5600AL2Div : ProcResource<1>; // Pseudo-resource used to block CTISTD when handling multi-pipeline splits. def P5600CTISTD : ProcResource<1>; // CTISTD Pipeline // --------------- def P5600WriteJump : SchedWriteRes<[P5600IssueCTISTD, P5600CTISTD]>; def P5600WriteJumpAndLink : SchedWriteRes<[P5600IssueCTISTD, P5600CTISTD]> { let Latency = 2; } def P5600Nop : SchedWriteRes<[P5600IssueCTISTD]> { let Latency = 0; } def : InstRW<[P5600Nop], (instrs SSNOP, NOP)>; // b, beq, beql, bg[et]z, bl[et]z, bne, bnel, j, syscall, jal, bltzal, // jalr, jr.hb, jr def : InstRW<[P5600WriteJump], (instrs B, BAL, BAL_BR, BEQ, BEQL, BGEZ, BGEZAL, BGEZALL, BGEZL, BGTZ, BGTZL, BLEZ, BLEZL, BLTZ, BLTZAL, BLTZALL, BLTZL, BNE, BNEL, BREAK, DERET, ERET, ERet, ERETNC, J, JR, JR_HB, PseudoIndirectBranch, PseudoIndirectHazardBranch, PseudoReturn, SDBBP, SYSCALL, RetRA, TAILCALL, TAILCALLREG, TAILCALLREGHB, TEQ, TEQI, TGE, TGEI, TGEIU, TGEU, TLT, TLTI, TLTU, TNE, TNEI, TRAP, TTLTIU, WAIT, PAUSE)>; def : InstRW<[P5600WriteJumpAndLink], (instrs JAL, JALR, JALRHBPseudo, JALRPseudo, JALR_HB)>; def : InstRW<[P5600WriteJumpAndLink], (instrs JALX)> { let Unsupported = 1; } def P5600COP0 : SchedWriteRes<[P5600IssueCTISTD, P5600CTISTD]>; def : InstRW<[P5600COP0], (instrs TLBINV, TLBINVF, TLBP, TLBR, TLBWI, TLBWR, MFC0, MTC0)>; def P5600COP2 : SchedWriteRes<[P5600IssueCTISTD, P5600CTISTD]>; def : InstRW<[P5600COP2], (instrs MFC2, MTC2)> { let Unsupported = 1; } // MIPS Virtualization ASE // ======================= def : InstRW<[P5600COP0], (instrs HYPCALL, MFGC0, MFHGC0, MTGC0, MTHGC0, TLBGINV, TLBGINVF, TLBGP, TLBGR, TLBGWI, TLBGWR)>; // LDST Pipeline // ------------- def P5600WriteLoad : SchedWriteRes<[P5600IssueLDST]> { let Latency = 4; } def P5600WriteLoadShifted : SchedWriteRes<[P5600IssueLDST, P5600CTISTD]> { let Latency = 4; } def P5600WriteCache : SchedWriteRes<[P5600IssueLDST]>; def P5600WriteStore : SchedWriteRes<[P5600IssueLDST, P5600CTISTD]> { // FIXME: This is a bit pessimistic. P5600CTISTD is only used during cycle 2 // not during 0, 1, and 2. let ResourceCycles = [ 1, 3 ]; } def P5600WriteGPRFromBypass : SchedWriteRes<[P5600IssueLDST]> { let Latency = 2; } def P5600WriteStoreFromOtherUnits : SchedWriteRes<[P5600IssueLDST]>; def P5600WriteLoadToOtherUnits : SchedWriteRes<[P5600IssueLDST]> { let Latency = 0; } // l[bhw], l[bh]u, ll def : InstRW<[P5600WriteLoad], (instrs LB, LBu, LH, LHu, LW, LL, LWC2, LWC3, LDC2, LDC3, LBE, LBuE, LHE, LHuE, LWE, LLE, LWPC)>; // lw[lr] def : InstRW<[P5600WriteLoadShifted], (instrs LWL, LWR, LWLE, LWRE)>; // s[bhw], sw[lr] def : InstRW<[P5600WriteStore], (instrs SB, SH, SW, SWC2, SWC3, SDC2, SDC3, SC, SBE, SHE, SWE, SCE, SWL, SWR, SWLE, SWRE)>; // pref, cache, sync, synci def : InstRW<[P5600WriteCache], (instrs PREF, PREFE, CACHE, CACHEE, SYNC, SYNCI)>; // LDST is also used in moves from general purpose registers to floating point // and MSA. def P5600WriteMoveGPRToOtherUnits : SchedWriteRes<[P5600IssueLDST]> { let Latency = 0; } // AL2 Pipeline // ------------ def P5600WriteAL2 : SchedWriteRes<[P5600IssueAL2]>; def P5600WriteAL2BitExt : SchedWriteRes<[P5600IssueAL2]> { let Latency = 2; } def P5600WriteAL2ShadowMov : SchedWriteRes<[P5600IssueAL2]> { let Latency = 2; } def P5600WriteAL2CondMov : SchedWriteRes<[P5600IssueAL2, P5600CTISTD]> { let Latency = 2; } def P5600WriteAL2Div : SchedWriteRes<[P5600IssueAL2, P5600AL2Div]> { // Estimated worst case let Latency = 34; let ResourceCycles = [1, 34]; } def P5600WriteAL2DivU : SchedWriteRes<[P5600IssueAL2, P5600AL2Div]> { // Estimated worst case let Latency = 34; let ResourceCycles = [1, 34]; } def P5600WriteAL2Mul : SchedWriteRes<[P5600IssueAL2]> { let Latency = 3; } def P5600WriteAL2Mult: SchedWriteRes<[P5600IssueAL2]> { let Latency = 5; } def P5600WriteAL2MAdd: SchedWriteRes<[P5600IssueAL2, P5600CTISTD]> { let Latency = 5; } // clo, clz, di, ei, mfhi, mflo def : InstRW<[P5600WriteAL2], (instrs CLO, CLZ, DI, EI, MFHI, MFLO, PseudoMFHI, PseudoMFLO)>; // ehb, rdhwr, rdpgpr, wrpgpr, wsbh def : InstRW<[P5600WriteAL2ShadowMov], (instrs EHB, RDHWR, WSBH)>; // mov[nz] def : InstRW<[P5600WriteAL2CondMov], (instrs MOVN_I_I, MOVZ_I_I)>; // divu? def : InstRW<[P5600WriteAL2Div], (instrs DIV, PseudoSDIV, SDIV)>; def : InstRW<[P5600WriteAL2DivU], (instrs DIVU, PseudoUDIV, UDIV)>; // mul def : InstRW<[P5600WriteAL2Mul], (instrs MUL)>; // multu?, multu? def : InstRW<[P5600WriteAL2Mult], (instrs MULT, MULTu, PseudoMULT, PseudoMULTu)>; // maddu?, msubu?, mthi, mtlo def : InstRW<[P5600WriteAL2MAdd], (instrs MADD, MADDU, MSUB, MSUBU, MTHI, MTLO, PseudoMADD, PseudoMADDU, PseudoMSUB, PseudoMSUBU, PseudoMTLOHI)>; // ext, ins def : InstRW<[P5600WriteAL2BitExt], (instrs EXT, INS)>; // Either ALU or AL2 Pipelines // --------------------------- // // Some instructions can choose between ALU and AL2, but once dispatched to // ALQ or AGQ respectively they are committed to that path. // The decision is based on the outcome of the most recent selection when the // choice was last available. For now, we assume ALU is always chosen. def P5600WriteEitherALU : SchedWriteVariant< // FIXME: Implement selection predicate [SchedVar, [P5600WriteALU]>, SchedVar, [P5600WriteAL2]> ]>; // add, addi, addiu, addu, andi, ori, rotr, se[bh], sllv?, sr[al]v?, slt, sltu, // xori def : InstRW<[P5600WriteEitherALU], (instrs ADD, ADDi, ADDiu, ANDi, ORi, ROTR, SEB, SEH, SLT, SLTu, SLL, SRA, SRL, XORi, ADDu, SLLV, SRAV, SRLV, LSA, COPY)>; // FPU Pipelines // ============= def P5600FPQ : ProcResource<3> { let BufferSize = 16; } def P5600IssueFPUS : ProcResource<1> { let Super = P5600FPQ; } def P5600IssueFPUL : ProcResource<1> { let Super = P5600FPQ; } def P5600IssueFPULoad : ProcResource<1> { let Super = P5600FPQ; } def P5600FPUDivSqrt : ProcResource<2>; def P5600WriteFPUS : SchedWriteRes<[P5600IssueFPUS]>; def P5600WriteFPUL : SchedWriteRes<[P5600IssueFPUL]> { let Latency = 4; } def P5600WriteFPUL_MADDSUB : SchedWriteRes<[P5600IssueFPUL]> { let Latency = 6; } def P5600WriteFPUDivI : SchedWriteRes<[P5600IssueFPUL, P5600FPUDivSqrt]> { // Best/Common/Worst case = 7 / 23 / 27 let Latency = 23; // Using common case let ResourceCycles = [ 1, 23 ]; } def P5600WriteFPUDivS : SchedWriteRes<[P5600IssueFPUL, P5600FPUDivSqrt]> { // Best/Common/Worst case = 7 / 23 / 27 let Latency = 23; // Using common case let ResourceCycles = [ 1, 23 ]; } def P5600WriteFPUDivD : SchedWriteRes<[P5600IssueFPUL, P5600FPUDivSqrt]> { // Best/Common/Worst case = 7 / 31 / 35 let Latency = 31; // Using common case let ResourceCycles = [ 1, 31 ]; } def P5600WriteFPURcpS : SchedWriteRes<[P5600IssueFPUL, P5600FPUDivSqrt]> { // Best/Common/Worst case = 7 / 19 / 23 let Latency = 19; // Using common case let ResourceCycles = [ 1, 19 ]; } def P5600WriteFPURcpD : SchedWriteRes<[P5600IssueFPUL, P5600FPUDivSqrt]> { // Best/Common/Worst case = 7 / 27 / 31 let Latency = 27; // Using common case let ResourceCycles = [ 1, 27 ]; } def P5600WriteFPURsqrtS : SchedWriteRes<[P5600IssueFPUL, P5600FPUDivSqrt]> { // Best/Common/Worst case = 7 / 27 / 27 let Latency = 27; // Using common case let ResourceCycles = [ 1, 27 ]; } def P5600WriteFPURsqrtD : SchedWriteRes<[P5600IssueFPUL, P5600FPUDivSqrt]> { // Best/Common/Worst case = 7 / 27 / 31 let Latency = 27; // Using common case let ResourceCycles = [ 1, 27 ]; } def P5600WriteFPUSqrtS : SchedWriteRes<[P5600IssueFPUL, P5600FPUDivSqrt]> { // Best/Common/Worst case = 7 / 27 / 31 let Latency = 27; // Using common case let ResourceCycles = [ 1, 27 ]; } def P5600WriteFPUSqrtD : SchedWriteRes<[P5600IssueFPUL, P5600FPUDivSqrt]> { // Best/Common/Worst case = 7 / 35 / 39 let Latency = 35; // Using common case let ResourceCycles = [ 1, 35 ]; } def P5600WriteMSAShortLogic : SchedWriteRes<[P5600IssueFPUS]>; def P5600WriteMSAShortInt : SchedWriteRes<[P5600IssueFPUS]> { let Latency = 2; } def P5600WriteMoveOtherUnitsToFPU : SchedWriteRes<[P5600IssueFPUS]>; def P5600WriteMSAOther3 : SchedWriteRes<[P5600IssueFPUS]> { let Latency = 3; } def P5600WriteMSALongInt : SchedWriteRes<[P5600IssueFPUS]> { let Latency = 5; } // vshf.[bhwd], binsl.[bhwd], binsr.[bhwd], insert.[bhwd], sld?.[bhwd], // bset.[bhwd], bclr.[bhwd], bneg.[bhwd], bsel_v, bseli_b def : InstRW<[P5600WriteMSAShortInt], (instregex "^VSHF_[BHWD]$")>; def : InstRW<[P5600WriteMSAShortInt], (instregex "^(BINSL|BINSLI)_[BHWD]$")>; def : InstRW<[P5600WriteMSAShortInt], (instregex "^(BINSR|BINSRI)_[BHWD]$")>; def : InstRW<[P5600WriteMSAShortInt], (instregex "^INSERT_[BHWD]$")>; def : InstRW<[P5600WriteMSAShortInt], (instregex "^(SLD|SLDI)_[BHWD]$")>; def : InstRW<[P5600WriteMSAShortInt], (instregex "^(BSET|BSETI)_[BHWD]$")>; def : InstRW<[P5600WriteMSAShortInt], (instregex "^(BCLR|BCLRI)_[BHWD]$")>; def : InstRW<[P5600WriteMSAShortInt], (instregex "^(BNEG|BNEGI)_[BHWD]$")>; def : InstRW<[P5600WriteMSAShortInt], (instregex "^(BSEL_V|BSELI_B)$")>; def : InstRW<[P5600WriteMSAShortInt], (instregex "^BMN*Z.*$")>; def : InstRW<[P5600WriteMSAShortInt], (instregex "^BSEL_(H|W|D|FW|FD)_PSEUDO$")>; // pcnt.[bhwd], sat_s.[bhwd], sat_u.bhwd] def : InstRW<[P5600WriteMSAOther3], (instregex "^PCNT_[BHWD]$")>; def : InstRW<[P5600WriteMSAOther3], (instregex "^SAT_(S|U)_[BHWD]$")>; // bnz.[bhwdv], cfcmsa, ctcmsa def : InstRW<[P5600WriteMSAShortLogic], (instregex "^(BNZ|BZ)_[BHWDV]$")>; def : InstRW<[P5600WriteMSAShortLogic], (instregex "^C(F|T)CMSA$")>; // FPUS is also used in moves from floating point and MSA registers to general // purpose registers. def P5600WriteMoveFPUSToOtherUnits : SchedWriteRes<[P5600IssueFPUS]> { let Latency = 0; } // FPUL is also used in moves from floating point and MSA registers to general // purpose registers. def P5600WriteMoveFPULToOtherUnits : SchedWriteRes<[P5600IssueFPUL]>; // Short Pipe // ---------- // // abs.[ds], abs.ps, bc1[tf]l?, mov[tf].[ds], mov[tf], mov.[ds], [cm][ft]c1, // m[ft]hc1, neg.[ds], neg.ps, nor.v, nori.b, or.v, ori.b, xor.v, xori.b, // sdxc1, sdc1, st.[bhwd], swc1, swxc1 def : InstRW<[P5600WriteFPUS], (instrs FABS_S, FABS_D32, FABS_D64, MOVF_D32, MOVF_D64, MOVF_S, MOVT_D32, MOVT_D64, MOVT_S, FMOV_D32, FMOV_D64, FMOV_S, FNEG_S, FNEG_D32, FNEG_D64)>; // adds_a.[bhwd], adds_[asu].[bhwd], addvi?.[bhwd], asub_[us].[bhwd], // aver?_[us].[bhwd], shf.[bhw], fill[bhwd], splat?.[bhwd] def : InstRW<[P5600WriteMSAShortInt], (instregex "^ADD_A_[BHWD]$")>; def : InstRW<[P5600WriteMSAShortInt], (instregex "^ADDS_[ASU]_[BHWD]$")>; // TODO: ADDVI_[BHW] might be 1 cycle latency rather than 2. Need to confirm it. def : InstRW<[P5600WriteMSAShortInt], (instregex "^ADDVI?_[BHWD]$")>; def : InstRW<[P5600WriteMSAShortInt], (instregex "^ASUB_[US].[BHWD]$")>; def : InstRW<[P5600WriteMSAShortInt], (instregex "^AVER?_[US].[BHWD]$")>; def : InstRW<[P5600WriteMSAShortInt], (instregex "^SHF_[BHW]$")>; def : InstRW<[P5600WriteMSAShortInt], (instregex "^FILL_[BHWD]$")>; def : InstRW<[P5600WriteMSAShortInt], (instregex "^(SPLAT|SPLATI)_[BHWD]$")>; // and.v, andi.b, move.v, ldi.[bhwd] def : InstRW<[P5600WriteMSAShortLogic], (instregex "^MOVE_V$")>; def : InstRW<[P5600WriteMSAShortLogic], (instregex "^LDI_[BHWD]$")>; def : InstRW<[P5600WriteMSAShortLogic], (instregex "^(AND|OR|[XN]OR)_V$")>; def : InstRW<[P5600WriteMSAShortLogic], (instregex "^(AND|OR|[XN]OR)I_B$")>; def : InstRW<[P5600WriteMSAShortLogic], (instregex "^(AND|OR|[XN]OR)_V_[DHW]_PSEUDO$")>; def : InstRW<[P5600WriteMSAShortLogic], (instregex "^FILL_F(D|W)_PSEUDO$")>; def : InstRW<[P5600WriteMSAShortLogic], (instregex "^INSERT_F(D|W)_PSEUDO$")>; // fexp2_w, fexp2_d def : InstRW<[P5600WriteFPUS], (instregex "^FEXP2_(W|D)$")>; // compare, converts, round to int, floating point truncate. def : InstRW<[P5600WriteFPUS], (instregex "^(CLT|CLTI)_(S|U)_[BHWD]$")>; def : InstRW<[P5600WriteFPUS], (instregex "^(CLE|CLEI)_(S|U)_[BHWD]$")>; def : InstRW<[P5600WriteFPUS], (instregex "^(CEQ|CEQI)_[BHWD]$")>; def : InstRW<[P5600WriteFPUS], (instregex "^CMP_UN_(S|D)$")>; def : InstRW<[P5600WriteFPUS], (instregex "^CMP_UEQ_(S|D)$")>; def : InstRW<[P5600WriteFPUS], (instregex "^CMP_EQ_(S|D)$")>; def : InstRW<[P5600WriteFPUS], (instregex "^CMP_LT_(S|D)$")>; def : InstRW<[P5600WriteFPUS], (instregex "^CMP_ULT_(S|D)$")>; def : InstRW<[P5600WriteFPUS], (instregex "^CMP_LE_(S|D)$")>; def : InstRW<[P5600WriteFPUS], (instregex "^CMP_ULE_(S|D)$")>; def : InstRW<[P5600WriteFPUS], (instregex "^FS(AF|EQ|LT|LE|NE|OR)_(W|D)$")>; def : InstRW<[P5600WriteFPUS], (instregex "^FSUEQ_(W|D)$")>; def : InstRW<[P5600WriteFPUS], (instregex "^FSULE_(W|D)$")>; def : InstRW<[P5600WriteFPUS], (instregex "^FSULT_(W|D)$")>; def : InstRW<[P5600WriteFPUS], (instregex "^FSUNE_(W|D)$")>; def : InstRW<[P5600WriteFPUS], (instregex "^FSUN_(W|D)$")>; def : InstRW<[P5600WriteFPUS], (instregex "^FCAF_(W|D)$")>; def : InstRW<[P5600WriteFPUS], (instregex "^FCEQ_(W|D)$")>; def : InstRW<[P5600WriteFPUS], (instregex "^FCLE_(W|D)$")>; def : InstRW<[P5600WriteFPUS], (instregex "^FCLT_(W|D)$")>; def : InstRW<[P5600WriteFPUS], (instregex "^FCNE_(W|D)$")>; def : InstRW<[P5600WriteFPUS], (instregex "^FCOR_(W|D)$")>; def : InstRW<[P5600WriteFPUS], (instregex "^FCUEQ_(W|D)$")>; def : InstRW<[P5600WriteFPUS], (instregex "^FCULE_(W|D)$")>; def : InstRW<[P5600WriteFPUS], (instregex "^FCULT_(W|D)$")>; def : InstRW<[P5600WriteFPUS], (instregex "^FCUNE_(W|D)$")>; def : InstRW<[P5600WriteFPUS], (instregex "^FCUN_(W|D)$")>; def : InstRW<[P5600WriteFPUS], (instregex "^FABS_(W|D)$")>; def : InstRW<[P5600WriteFPUS], (instregex "^FFINT_(U|S)_(W|D)$")>; def : InstRW<[P5600WriteFPUS], (instregex "^FFQL_(W|D)$")>; def : InstRW<[P5600WriteFPUS], (instregex "^FFQR_(W|D)$")>; def : InstRW<[P5600WriteFPUS], (instregex "^FTINT_(U|S)_(W|D)$")>; def : InstRW<[P5600WriteFPUS], (instregex "^FRINT_(W|D)$")>; def : InstRW<[P5600WriteFPUS], (instregex "^FTQ_(H|W)$")>; def : InstRW<[P5600WriteFPUS], (instregex "^FTRUNC_(U|S)_(W|D)$")>; // fexdo.[hw], fexupl.[wd], fexupr.[wd] def : InstRW<[P5600WriteFPUS], (instregex "^FEXDO_(H|W)$")>; def : InstRW<[P5600WriteFPUS], (instregex "^FEXUPL_(W|D)$")>; def : InstRW<[P5600WriteFPUS], (instregex "^FEXUPR_(W|D)$")>; // fclass.[wd], fmax.[wd], fmax_a.[wd], fmin.[wd], fmin_a.[wd], flog2.[wd] def : InstRW<[P5600WriteFPUS], (instregex "^FCLASS_(W|D)$")>; def : InstRW<[P5600WriteFPUS], (instregex "^FMAX_A_(W|D)$")>; def : InstRW<[P5600WriteFPUS], (instregex "^FMAX_(W|D)$")>; def : InstRW<[P5600WriteFPUS], (instregex "^FMIN_A_(W|D)$")>; def : InstRW<[P5600WriteFPUS], (instregex "^FMIN_(W|D)$")>; def : InstRW<[P5600WriteFPUS], (instregex "^FLOG2_(W|D)$")>; // interleave right/left, interleave even/odd, insert def : InstRW<[P5600WriteMSAShortLogic], (instregex "^(ILVR|ILVL)_[BHWD]$")>; def : InstRW<[P5600WriteMSAShortLogic], (instregex "^(ILVEV|ILVOD)_[BHWD]$")>; def : InstRW<[P5600WriteMSAShortLogic], (instregex "^INSVE_[BHWD]$")>; // subs_?.[bhwd], subsus_?.[bhwd], subsuu_?.[bhwd], subvi.[bhwd], subv.[bhwd], def : InstRW<[P5600WriteMSAShortInt], (instregex "^SUBS_(S|U)_[BHWD]$")>; def : InstRW<[P5600WriteMSAShortInt], (instregex "^SUBSUS_(S|U)_[BHWD]$")>; def : InstRW<[P5600WriteMSAShortInt], (instregex "^SUBSUU_(S|U)_[BHWD]$")>; def : InstRW<[P5600WriteMSAShortInt], (instregex "^SUBVI_[BHWD]$")>; def : InstRW<[P5600WriteMSAShortInt], (instregex "^SUBV_[BHWD]$")>; // mod_[su].[bhwd], div_[su].[bhwd] def : InstRW<[P5600WriteFPUDivI], (instregex "^MOD_(S|U)_[BHWD]$")>; def : InstRW<[P5600WriteFPUDivI], (instregex "^DIV_(S|U)_[BHWD]$")>; // hadd_[su].[bhwd], hsub_[su].[bhwd], max_[sua].[bhwd], min_[sua].[bhwd], // maxi_[su].[bhwd], mini_[su].[bhwd], sra?.[bhwd], srar?.[bhwd], srlr.[bhwd], // sll?.[bhwd], pckev.[bhwd], pckod.[bhwd], nloc.[bhwd], nlzc.[bhwd], // insve.[bhwd] def : InstRW<[P5600WriteMSAShortLogic], (instregex "^HADD_(S|U)_[BHWD]$")>; def : InstRW<[P5600WriteMSAShortLogic], (instregex "^HSUB_(S|U)_[BHWD]$")>; def : InstRW<[P5600WriteMSAShortLogic], (instregex "^(MAX|MIN)_S_[BHWD]$")>; def : InstRW<[P5600WriteMSAShortLogic], (instregex "^(MAX|MIN)_U_[BHWD]$")>; def : InstRW<[P5600WriteMSAShortLogic], (instregex "^(MAX|MIN)_A_[BHWD]$")>; def : InstRW<[P5600WriteMSAShortLogic], (instregex "^(MAXI|MINI)_(S|U)_[BHWD]$")>; def : InstRW<[P5600WriteMSAShortLogic], (instregex "^(SRA|SRAI)_[BHWD]$")>; def : InstRW<[P5600WriteMSAShortLogic], (instregex "^(SRL|SRLI)_[BHWD]$")>; def : InstRW<[P5600WriteMSAShortLogic], (instregex "^(SRAR|SRARI)_[BHWD]$")>; def : InstRW<[P5600WriteMSAShortLogic], (instregex "^(SRLR|SRLRI)_[BHWD]$")>; def : InstRW<[P5600WriteMSAShortLogic], (instregex "^(SLL|SLLI)_[BHWD]$")>; def : InstRW<[P5600WriteMSAShortLogic], (instregex "^(PCKEV|PCKOD)_[BHWD]$")>; def : InstRW<[P5600WriteMSAShortLogic], (instregex "^(NLOC|NLZC)_[BHWD]$")>; // Long Pipe // ---------- // // add.[ds], add.ps, cvt.d.[sw], cvt.s.[dw], cvt.w.[sd], cvt.[sw].ps, // cvt.ps.[sw], cvt.s.(pl|pu), c..[ds], c..ps, mul.[ds], mul.ps, // pl[lu].ps, sub.[ds], sub.ps, trunc.w.[ds], trunc.w.ps def : InstRW<[P5600WriteFPUL], (instrs FADD_D32, FADD_D64, FADD_S, FMUL_D32, FMUL_D64, FMUL_S, FSUB_D32, FSUB_D64, FSUB_S)>; def : InstRW<[P5600WriteFPUL], (instregex "^TRUNC_(L|W)_(S|D32|D64)$")>; def : InstRW<[P5600WriteFPUL], (instregex "^CVT_(S|D32|D64|L|W)_(S|D32|D64|L|W)$")>; def : InstRW<[P5600WriteFPUL], (instrs CVT_PS_S64, CVT_S_PL64, CVT_S_PU64)>; def : InstRW<[P5600WriteFPUL], (instregex "^C_[A-Z]+_(S|D32|D64)$")>; def : InstRW<[P5600WriteFPUL], (instregex "^FCMP_(S32|D32|D64)$")>; def : InstRW<[P5600WriteFPUL], (instregex "^PseudoCVT_(S|D32|D64)_(L|W)$")>; def : InstRW<[P5600WriteFPUL], (instrs PLL_PS64, PLU_PS64)>; // div.[ds], div.ps def : InstRW<[P5600WriteFPUDivS], (instrs FDIV_S)>; def : InstRW<[P5600WriteFPUDivD], (instrs FDIV_D32, FDIV_D64)>; // sqrt.[ds], sqrt.ps def : InstRW<[P5600WriteFPUSqrtS], (instrs FSQRT_S)>; def : InstRW<[P5600WriteFPUSqrtD], (instrs FSQRT_D32, FSQRT_D64)>; // frcp.[wd], frsqrt.[wd] def : InstRW<[P5600WriteFPURsqrtD], (instregex "^FRCP_(W|D)$")>; def : InstRW<[P5600WriteFPURsqrtD], (instregex "^FRSQRT_(W|D)$")>; def : InstRW<[P5600WriteFPURsqrtD], (instrs RECIP_D32, RECIP_D64, RSQRT_D32, RSQRT_D64)>; def : InstRW<[P5600WriteFPURsqrtS], (instrs RECIP_S, RSQRT_S)>; // fmadd.[wd], fmsubb.[wd], fdiv.[wd], fsqrt.[wd], fmul.[wd], fadd.[wd], // fsub.[wd] def : InstRW<[P5600WriteFPUL_MADDSUB], (instregex "^FMADD_(W|D)$")>; def : InstRW<[P5600WriteFPUL_MADDSUB], (instregex "^FMSUB_(W|D)$")>; def : InstRW<[P5600WriteFPUDivS], (instregex "^FDIV_W$")>; def : InstRW<[P5600WriteFPUDivD], (instregex "^FDIV_D$")>; def : InstRW<[P5600WriteFPUSqrtS], (instregex "^FSQRT_W$")>; def : InstRW<[P5600WriteFPUSqrtD], (instregex "^FSQRT_D$")>; def : InstRW<[P5600WriteFPUL], (instregex "^FMUL_(W|D)$")>; def : InstRW<[P5600WriteFPUL], (instregex "^FADD_(W|D)$")>; def : InstRW<[P5600WriteFPUL], (instregex "^FSUB_(W|D)$")>; // dpadd_?.[bhwd], dpsub_?.[bhwd], dotp_?.[bhwd], msubv.[bhwd], maddv.[bhwd] // mulv.[bhwd]. def : InstRW<[P5600WriteMSALongInt], (instregex "^DPADD_(S|U)_[HWD]$")>; def : InstRW<[P5600WriteMSALongInt], (instregex "^DPSUB_(S|U)_[HWD]$")>; def : InstRW<[P5600WriteMSALongInt], (instregex "^DOTP_(S|U)_[HWD]$")>; def : InstRW<[P5600WriteMSALongInt], (instregex "^MSUBV_[BHWD]$")>; def : InstRW<[P5600WriteMSALongInt], (instregex "^MADDV_[BHWD]$")>; def : InstRW<[P5600WriteMSALongInt], (instregex "^MULV_[BHWD]$")>; def : InstRW<[P5600WriteMSALongInt], (instregex "^MADDR_Q_[HW]$")>; def : InstRW<[P5600WriteMSALongInt], (instregex "^MADD_Q_[HW]$")>; def : InstRW<[P5600WriteMSALongInt], (instregex "^MSUBR_Q_[HW]$")>; def : InstRW<[P5600WriteMSALongInt], (instregex "^MSUB_Q_[HW]$")>; def : InstRW<[P5600WriteMSALongInt], (instregex "^MULR_Q_[HW]$")>; def : InstRW<[P5600WriteMSALongInt], (instregex "^MUL_Q_[HW]$")>; // madd.[ds], msub.[ds], nmadd.[ds], nmsub.[ds], // Operand 0 is read on cycle 5. All other operands are read on operand 0. def : InstRW<[SchedReadAdvance<5>, P5600WriteFPUL_MADDSUB], (instrs MADD_D32, MADD_D64, MADD_S, MSUB_D32, MSUB_D64, MSUB_S, NMADD_D32, NMADD_D64, NMADD_S, NMSUB_D32, NMSUB_D64, NMSUB_S)>; // madd.ps, msub.ps, nmadd.ps, nmsub.ps // Operand 0 and 1 are read on cycle 5. All others are read on operand 0. // (none of these instructions exist in the backend yet) // Load Pipe // --------- // // This is typically used in conjunction with the load pipeline under the AGQ // All the instructions are in the 'Tricky Instructions' section. def P5600WriteLoadOtherUnitsToFPU : SchedWriteRes<[P5600IssueFPULoad]> { let Latency = 4; } // Tricky Instructions // =================== // // These instructions are split across multiple uops (in different pipelines) // that must cooperate to complete the operation // FIXME: This isn't quite right since the implementation of WriteSequence // current aggregates the resources and ignores the exact cycle they are // used. def P5600WriteMoveGPRToFPU : WriteSequence<[P5600WriteMoveGPRToOtherUnits, P5600WriteMoveOtherUnitsToFPU]>; // FIXME: This isn't quite right since the implementation of WriteSequence // current aggregates the resources and ignores the exact cycle they are // used. def P5600WriteMoveFPUToGPR : WriteSequence<[P5600WriteMoveFPUSToOtherUnits, P5600WriteGPRFromBypass]>; // FIXME: This isn't quite right since the implementation of WriteSequence // current aggregates the resources and ignores the exact cycle they are // used. def P5600WriteStoreFPUS : WriteSequence<[P5600WriteMoveFPUSToOtherUnits, P5600WriteStoreFromOtherUnits]>; // FIXME: This isn't quite right since the implementation of WriteSequence // current aggregates the resources and ignores the exact cycle they are // used. def P5600WriteStoreFPUL : WriteSequence<[P5600WriteMoveFPULToOtherUnits, P5600WriteStoreFromOtherUnits]>; // FIXME: This isn't quite right since the implementation of WriteSequence // current aggregates the resources and ignores the exact cycle they are // used. def P5600WriteLoadFPU : WriteSequence<[P5600WriteLoadToOtherUnits, P5600WriteLoadOtherUnitsToFPU]>; // ctc1, mtc1, mthc1 def : InstRW<[P5600WriteMoveGPRToFPU], (instrs CTC1, MTC1, MTC1_D64, MTHC1_D32, MTHC1_D64, BuildPairF64, BuildPairF64_64)>; // copy.[su]_[bhwd] def : InstRW<[P5600WriteMoveFPUToGPR], (instregex "^COPY_U_[BHW]$")>; def : InstRW<[P5600WriteMoveFPUToGPR], (instregex "^COPY_S_[BHWD]$")>; // bc1[ft], cfc1, mfc1, mfhc1, movf, movt def : InstRW<[P5600WriteMoveFPUToGPR], (instrs BC1F, BC1FL, BC1T, BC1TL, CFC1, MFC1, MFC1_D64, MFHC1_D32, MFHC1_D64, MOVF_I, MOVT_I, ExtractElementF64, ExtractElementF64_64)>; // swc1, swxc1, st.[bhwd] def : InstRW<[P5600WriteStoreFPUS], (instrs SDC1, SDC164, SDXC1, SDXC164, SWC1, SWXC1, SUXC1, SUXC164)>; def : InstRW<[P5600WriteStoreFPUS], (instregex "^ST_[BHWD]$")>; def : InstRW<[P5600WriteStoreFPUS], (instrs ST_F16)>; // movn.[ds], movz.[ds] def : InstRW<[P5600WriteStoreFPUL], (instrs MOVN_I_D32, MOVN_I_D64, MOVN_I_S, MOVZ_I_D32, MOVZ_I_D64, MOVZ_I_S)>; // l[dw]x?c1, ld.[bhwd] def : InstRW<[P5600WriteLoadFPU], (instrs LDC1, LDC164, LDXC1, LDXC164, LWC1, LWXC1, LUXC1, LUXC164)>; def : InstRW<[P5600WriteLoadFPU], (instregex "LD_[BHWD]")>; def : InstRW<[P5600WriteLoadFPU], (instrs LD_F16)>; // Unsupported Instructions // ======================== // // The following instruction classes are never valid on P5600. // II_DADDIU, II_DADDU, II_DMFC1, II_DMTC1, II_DMULT, II_DMULTU, II_DROTR, // II_DROTR32, II_DROTRV, II_DDIV, II_DSLL, II_DSLL32, II_DSLLV, II_DSRA, // II_DSRA32, II_DSRAV, II_DSRL, II_DSRL32, II_DSRLV, II_DSUBU, II_DDIVU, // II_JALRC, II_LD, II_LD[LR], II_RESTORE, II_SAVE, II_SD, II_SDC1, II_SD[LR] // // The following instructions are never valid on P5600. // addq.ph, repl.ph, repl.qb, subq.ph, subu_s.qb // // Guesswork // ========= // // This section is largely temporary guesswork. // ceil.[lw].[ds], floor.[lw].[ds] // Reason behind guess: trunc.[lw].ds and the various cvt's are in FPUL def : InstRW<[P5600WriteFPUL], (instregex "^CEIL_(L|W)_(S|D32|D64)$")>; def : InstRW<[P5600WriteFPUL], (instregex "^FLOOR_(L|W)_(S|D32|D64)$")>; def : InstRW<[P5600WriteFPUL], (instregex "^ROUND_(L|W)_(S|D32|D64)$")>; // rotrv // Reason behind guess: rotr is in the same category and the two register forms // generally follow the immediate forms in this category def : InstRW<[P5600WriteEitherALU], (instrs ROTRV)>; // Atomic instructions // FIXME: Define `WriteAtomic` in the MipsSchedule.td and // attach it to the Atomic2OpsPostRA, AtomicCmpSwapPostRA, ... // classes. Then just define resources for the `WriteAtomic` in each // machine models. def P5600Atomic : ProcResource<1> { let BufferSize = 1; } def P5600WriteAtomic : SchedWriteRes<[P5600Atomic]> { let Latency = 2; } def : InstRW<[P5600WriteAtomic], (instregex "^ATOMIC_SWAP_I(8|16|32|64)_POSTRA$")>; def : InstRW<[P5600WriteAtomic], (instregex "^ATOMIC_CMP_SWAP_I(8|16|32|64)_POSTRA$")>; def : InstRW<[P5600WriteAtomic], (instregex "^ATOMIC_LOAD_(ADD|SUB|AND|OR|XOR|NAND)_I(8|16|32|64)_POSTRA$")>; } Index: head/contrib/llvm/lib/Target/Mips/MipsSubtarget.h =================================================================== --- head/contrib/llvm/lib/Target/Mips/MipsSubtarget.h (revision 354978) +++ head/contrib/llvm/lib/Target/Mips/MipsSubtarget.h (revision 354979) @@ -1,398 +1,402 @@ //===-- MipsSubtarget.h - Define Subtarget for the Mips ---------*- C++ -*-===// // // 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 // //===----------------------------------------------------------------------===// // // This file declares the Mips specific subclass of TargetSubtargetInfo. // //===----------------------------------------------------------------------===// #ifndef LLVM_LIB_TARGET_MIPS_MIPSSUBTARGET_H #define LLVM_LIB_TARGET_MIPS_MIPSSUBTARGET_H #include "MCTargetDesc/MipsABIInfo.h" #include "MipsFrameLowering.h" #include "MipsISelLowering.h" #include "MipsInstrInfo.h" #include "llvm/CodeGen/SelectionDAGTargetInfo.h" #include "llvm/CodeGen/TargetSubtargetInfo.h" #include "llvm/CodeGen/GlobalISel/CallLowering.h" #include "llvm/CodeGen/GlobalISel/LegalizerInfo.h" #include "llvm/CodeGen/GlobalISel/RegisterBankInfo.h" #include "llvm/CodeGen/GlobalISel/InstructionSelector.h" #include "llvm/IR/DataLayout.h" #include "llvm/MC/MCInstrItineraries.h" #include "llvm/Support/ErrorHandling.h" #include #define GET_SUBTARGETINFO_HEADER #include "MipsGenSubtargetInfo.inc" namespace llvm { class StringRef; class MipsTargetMachine; class MipsSubtarget : public MipsGenSubtargetInfo { virtual void anchor(); enum MipsArchEnum { MipsDefault, Mips1, Mips2, Mips32, Mips32r2, Mips32r3, Mips32r5, Mips32r6, Mips32Max, Mips3, Mips4, Mips5, Mips64, Mips64r2, Mips64r3, Mips64r5, Mips64r6 }; enum class CPU { P5600 }; // Used to avoid printing dsp warnings multiple times. static bool DspWarningPrinted; // Used to avoid printing msa warnings multiple times. static bool MSAWarningPrinted; // Used to avoid printing crc warnings multiple times. static bool CRCWarningPrinted; // Used to avoid printing ginv warnings multiple times. static bool GINVWarningPrinted; // Used to avoid printing virt warnings multiple times. static bool VirtWarningPrinted; // Mips architecture version MipsArchEnum MipsArchVersion; // Processor implementation (unused but required to exist by // tablegen-erated code). CPU ProcImpl; // IsLittle - The target is Little Endian bool IsLittle; // IsSoftFloat - The target does not support any floating point instructions. bool IsSoftFloat; // IsSingleFloat - The target only supports single precision float // point operations. This enable the target to use all 32 32-bit // floating point registers instead of only using even ones. bool IsSingleFloat; // IsFPXX - MIPS O32 modeless ABI. bool IsFPXX; // NoABICalls - Disable SVR4-style position-independent code. bool NoABICalls; // Abs2008 - Use IEEE 754-2008 abs.fmt instruction. bool Abs2008; // IsFP64bit - The target processor has 64-bit floating point registers. bool IsFP64bit; /// Are odd single-precision registers permitted? /// This corresponds to -modd-spreg and -mno-odd-spreg bool UseOddSPReg; // IsNan2008 - IEEE 754-2008 NaN encoding. bool IsNaN2008bit; // IsGP64bit - General-purpose registers are 64 bits wide bool IsGP64bit; // IsPTR64bit - Pointers are 64 bit wide bool IsPTR64bit; // HasVFPU - Processor has a vector floating point unit. bool HasVFPU; // CPU supports cnMIPS (Cavium Networks Octeon CPU). bool HasCnMips; + // CPU supports cnMIPSP (Cavium Networks Octeon+ CPU). + bool HasCnMipsP; + // isLinux - Target system is Linux. Is false we consider ELFOS for now. bool IsLinux; // UseSmallSection - Small section is used. bool UseSmallSection; /// Features related to the presence of specific instructions. // HasMips3_32 - The subset of MIPS-III instructions added to MIPS32 bool HasMips3_32; // HasMips3_32r2 - The subset of MIPS-III instructions added to MIPS32r2 bool HasMips3_32r2; // HasMips4_32 - Has the subset of MIPS-IV present in MIPS32 bool HasMips4_32; // HasMips4_32r2 - Has the subset of MIPS-IV present in MIPS32r2 bool HasMips4_32r2; // HasMips5_32r2 - Has the subset of MIPS-V present in MIPS32r2 bool HasMips5_32r2; // InMips16 -- can process Mips16 instructions bool InMips16Mode; // Mips16 hard float bool InMips16HardFloat; // InMicroMips -- can process MicroMips instructions bool InMicroMipsMode; // HasDSP, HasDSPR2, HasDSPR3 -- supports DSP ASE. bool HasDSP, HasDSPR2, HasDSPR3; // Allow mixed Mips16 and Mips32 in one source file bool AllowMixed16_32; // Optimize for space by compiling all functions as Mips 16 unless // it needs floating point. Functions needing floating point are // compiled as Mips32 bool Os16; // HasMSA -- supports MSA ASE. bool HasMSA; // UseTCCInDIV -- Enables the use of trapping in the assembler. bool UseTCCInDIV; // Sym32 -- On Mips64 symbols are 32 bits. bool HasSym32; // HasEVA -- supports EVA ASE. bool HasEVA; // nomadd4 - disables generation of 4-operand madd.s, madd.d and // related instructions. bool DisableMadd4; // HasMT -- support MT ASE. bool HasMT; // HasCRC -- supports R6 CRC ASE bool HasCRC; // HasVirt -- supports Virtualization ASE bool HasVirt; // HasGINV -- supports R6 Global INValidate ASE bool HasGINV; // Use hazard variants of the jump register instructions for indirect // function calls and jump tables. bool UseIndirectJumpsHazard; // Disable use of the `jal` instruction. bool UseLongCalls = false; /// The minimum alignment known to hold of the stack frame on /// entry to the function and which must be maintained by every function. unsigned stackAlignment; /// The overridden stack alignment. unsigned StackAlignOverride; InstrItineraryData InstrItins; // We can override the determination of whether we are in mips16 mode // as from the command line enum {NoOverride, Mips16Override, NoMips16Override} OverrideMode; const MipsTargetMachine &TM; Triple TargetTriple; const SelectionDAGTargetInfo TSInfo; std::unique_ptr InstrInfo; std::unique_ptr FrameLowering; std::unique_ptr TLInfo; public: bool isPositionIndependent() const; /// This overrides the PostRAScheduler bit in the SchedModel for each CPU. bool enablePostRAScheduler() const override; void getCriticalPathRCs(RegClassVector &CriticalPathRCs) const override; CodeGenOpt::Level getOptLevelToEnablePostRAScheduler() const override; bool isABI_N64() const; bool isABI_N32() const; bool isABI_O32() const; const MipsABIInfo &getABI() const; bool isABI_FPXX() const { return isABI_O32() && IsFPXX; } /// This constructor initializes the data members to match that /// of the specified triple. MipsSubtarget(const Triple &TT, StringRef CPU, StringRef FS, bool little, const MipsTargetMachine &TM, unsigned StackAlignOverride); /// ParseSubtargetFeatures - Parses features string setting specified /// subtarget options. Definition of function is auto generated by tblgen. void ParseSubtargetFeatures(StringRef CPU, StringRef FS); bool hasMips1() const { return MipsArchVersion >= Mips1; } bool hasMips2() const { return MipsArchVersion >= Mips2; } bool hasMips3() const { return MipsArchVersion >= Mips3; } bool hasMips4() const { return MipsArchVersion >= Mips4; } bool hasMips5() const { return MipsArchVersion >= Mips5; } bool hasMips4_32() const { return HasMips4_32; } bool hasMips4_32r2() const { return HasMips4_32r2; } bool hasMips32() const { return (MipsArchVersion >= Mips32 && MipsArchVersion < Mips32Max) || hasMips64(); } bool hasMips32r2() const { return (MipsArchVersion >= Mips32r2 && MipsArchVersion < Mips32Max) || hasMips64r2(); } bool hasMips32r3() const { return (MipsArchVersion >= Mips32r3 && MipsArchVersion < Mips32Max) || hasMips64r2(); } bool hasMips32r5() const { return (MipsArchVersion >= Mips32r5 && MipsArchVersion < Mips32Max) || hasMips64r5(); } bool hasMips32r6() const { return (MipsArchVersion >= Mips32r6 && MipsArchVersion < Mips32Max) || hasMips64r6(); } bool hasMips64() const { return MipsArchVersion >= Mips64; } bool hasMips64r2() const { return MipsArchVersion >= Mips64r2; } bool hasMips64r3() const { return MipsArchVersion >= Mips64r3; } bool hasMips64r5() const { return MipsArchVersion >= Mips64r5; } bool hasMips64r6() const { return MipsArchVersion >= Mips64r6; } bool hasCnMips() const { return HasCnMips; } + bool hasCnMipsP() const { return HasCnMipsP; } bool isLittle() const { return IsLittle; } bool isABICalls() const { return !NoABICalls; } bool isFPXX() const { return IsFPXX; } bool isFP64bit() const { return IsFP64bit; } bool useOddSPReg() const { return UseOddSPReg; } bool noOddSPReg() const { return !UseOddSPReg; } bool isNaN2008() const { return IsNaN2008bit; } bool inAbs2008Mode() const { return Abs2008; } bool isGP64bit() const { return IsGP64bit; } bool isGP32bit() const { return !IsGP64bit; } unsigned getGPRSizeInBytes() const { return isGP64bit() ? 8 : 4; } bool isPTR64bit() const { return IsPTR64bit; } bool isPTR32bit() const { return !IsPTR64bit; } bool hasSym32() const { return (HasSym32 && isABI_N64()) || isABI_N32() || isABI_O32(); } bool isSingleFloat() const { return IsSingleFloat; } bool isTargetELF() const { return TargetTriple.isOSBinFormatELF(); } bool hasVFPU() const { return HasVFPU; } bool inMips16Mode() const { return InMips16Mode; } bool inMips16ModeDefault() const { return InMips16Mode; } // Hard float for mips16 means essentially to compile as soft float // but to use a runtime library for soft float that is written with // native mips32 floating point instructions (those runtime routines // run in mips32 hard float mode). bool inMips16HardFloat() const { return inMips16Mode() && InMips16HardFloat; } bool inMicroMipsMode() const { return InMicroMipsMode && !InMips16Mode; } bool inMicroMips32r6Mode() const { return inMicroMipsMode() && hasMips32r6(); } bool hasDSP() const { return HasDSP; } bool hasDSPR2() const { return HasDSPR2; } bool hasDSPR3() const { return HasDSPR3; } bool hasMSA() const { return HasMSA; } bool disableMadd4() const { return DisableMadd4; } bool hasEVA() const { return HasEVA; } bool hasMT() const { return HasMT; } bool hasCRC() const { return HasCRC; } bool hasVirt() const { return HasVirt; } bool hasGINV() const { return HasGINV; } bool useIndirectJumpsHazard() const { return UseIndirectJumpsHazard && hasMips32r2(); } bool useSmallSection() const { return UseSmallSection; } bool hasStandardEncoding() const { return !InMips16Mode && !InMicroMipsMode; } bool useSoftFloat() const { return IsSoftFloat; } bool useLongCalls() const { return UseLongCalls; } bool enableLongBranchPass() const { return hasStandardEncoding() || inMicroMipsMode() || allowMixed16_32(); } /// Features related to the presence of specific instructions. bool hasExtractInsert() const { return !inMips16Mode() && hasMips32r2(); } bool hasMTHC1() const { return hasMips32r2(); } bool allowMixed16_32() const { return inMips16ModeDefault() | AllowMixed16_32; } bool os16() const { return Os16; } bool isTargetNaCl() const { return TargetTriple.isOSNaCl(); } bool isXRaySupported() const override { return true; } // for now constant islands are on for the whole compilation unit but we only // really use them if in addition we are in mips16 mode static bool useConstantIslands(); unsigned getStackAlignment() const { return stackAlignment; } // Grab relocation model Reloc::Model getRelocationModel() const; MipsSubtarget &initializeSubtargetDependencies(StringRef CPU, StringRef FS, const TargetMachine &TM); /// Does the system support unaligned memory access. /// /// MIPS32r6/MIPS64r6 require full unaligned access support but does not /// specify which component of the system provides it. Hardware, software, and /// hybrid implementations are all valid. bool systemSupportsUnalignedAccess() const { return hasMips32r6(); } // Set helper classes void setHelperClassesMips16(); void setHelperClassesMipsSE(); const SelectionDAGTargetInfo *getSelectionDAGInfo() const override { return &TSInfo; } const MipsInstrInfo *getInstrInfo() const override { return InstrInfo.get(); } const TargetFrameLowering *getFrameLowering() const override { return FrameLowering.get(); } const MipsRegisterInfo *getRegisterInfo() const override { return &InstrInfo->getRegisterInfo(); } const MipsTargetLowering *getTargetLowering() const override { return TLInfo.get(); } const InstrItineraryData *getInstrItineraryData() const override { return &InstrItins; } protected: // GlobalISel related APIs. std::unique_ptr CallLoweringInfo; std::unique_ptr Legalizer; std::unique_ptr RegBankInfo; std::unique_ptr InstSelector; public: const CallLowering *getCallLowering() const override; const LegalizerInfo *getLegalizerInfo() const override; const RegisterBankInfo *getRegBankInfo() const override; const InstructionSelector *getInstructionSelector() const override; }; } // End llvm namespace #endif