diff options
author | Dimitry Andric <dim@FreeBSD.org> | 2020-07-26 19:36:28 +0000 |
---|---|---|
committer | Dimitry Andric <dim@FreeBSD.org> | 2020-07-26 19:36:28 +0000 |
commit | cfca06d7963fa0909f90483b42a6d7d194d01e08 (patch) | |
tree | 209fb2a2d68f8f277793fc8df46c753d31bc853b /llvm/lib/Target/RISCV | |
parent | 706b4fc47bbc608932d3b491ae19a3b9cde9497b (diff) |
Notes
Diffstat (limited to 'llvm/lib/Target/RISCV')
57 files changed, 5790 insertions, 1162 deletions
diff --git a/llvm/lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp b/llvm/lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp index 53562f42a184..407f980bd35e 100644 --- a/llvm/lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp +++ b/llvm/lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp @@ -10,10 +10,13 @@ #include "MCTargetDesc/RISCVMCExpr.h" #include "MCTargetDesc/RISCVMCTargetDesc.h" #include "MCTargetDesc/RISCVTargetStreamer.h" +#include "RISCVInstrInfo.h" #include "TargetInfo/RISCVTargetInfo.h" #include "Utils/RISCVBaseInfo.h" #include "Utils/RISCVMatInt.h" #include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/SmallBitVector.h" +#include "llvm/ADT/SmallString.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/Statistic.h" #include "llvm/ADT/StringSwitch.h" @@ -32,6 +35,7 @@ #include "llvm/MC/MCSubtargetInfo.h" #include "llvm/Support/Casting.h" #include "llvm/Support/MathExtras.h" +#include "llvm/Support/RISCVAttributes.h" #include "llvm/Support/TargetRegistry.h" #include <limits> @@ -50,9 +54,16 @@ STATISTIC(RISCVNumInstrsCompressed, namespace { struct RISCVOperand; +struct ParserOptionsSet { + bool IsPicEnabled; +}; + class RISCVAsmParser : public MCTargetAsmParser { SmallVector<FeatureBitset, 4> FeatureBitStack; + SmallVector<ParserOptionsSet, 4> ParserOptionsStack; + ParserOptionsSet ParserOptions; + SMLoc getLoc() const { return getParser().getTok().getLoc(); } bool isRV64() const { return getSTI().hasFeature(RISCV::Feature64Bit); } bool isRV32E() const { return getSTI().hasFeature(RISCV::FeatureRV32E); } @@ -74,6 +85,8 @@ class RISCVAsmParser : public MCTargetAsmParser { bool MatchingInlineAsm) override; bool ParseRegister(unsigned &RegNo, SMLoc &StartLoc, SMLoc &EndLoc) override; + OperandMatchResultTy tryParseRegister(unsigned &RegNo, SMLoc &StartLoc, + SMLoc &EndLoc) override; bool ParseInstruction(ParseInstructionInfo &Info, StringRef Name, SMLoc NameLoc, OperandVector &Operands) override; @@ -118,6 +131,9 @@ class RISCVAsmParser : public MCTargetAsmParser { // 'add' is an overloaded mnemonic. bool checkPseudoAddTPRel(MCInst &Inst, OperandVector &Operands); + // Check instruction constraints. + bool validateInstruction(MCInst &Inst, OperandVector &Operands); + /// Helper for processing MC instructions that have been successfully matched /// by MatchAndEmitInstruction. Modifications to the emitted instructions, /// like the expansion of pseudo instructions (e.g., "li"), can be performed @@ -138,11 +154,15 @@ class RISCVAsmParser : public MCTargetAsmParser { OperandMatchResultTy parseOperandWithModifier(OperandVector &Operands); OperandMatchResultTy parseBareSymbol(OperandVector &Operands); OperandMatchResultTy parseCallSymbol(OperandVector &Operands); + OperandMatchResultTy parsePseudoJumpSymbol(OperandVector &Operands); OperandMatchResultTy parseJALOffset(OperandVector &Operands); + OperandMatchResultTy parseVTypeI(OperandVector &Operands); + OperandMatchResultTy parseMaskReg(OperandVector &Operands); bool parseOperand(OperandVector &Operands, StringRef Mnemonic); bool parseDirectiveOption(); + bool parseDirectiveAttribute(); void setFeatureBits(uint64_t Feature, StringRef FeatureString) { if (!(getSTI().getFeatureBits()[Feature])) { @@ -152,6 +172,10 @@ class RISCVAsmParser : public MCTargetAsmParser { } } + bool getFeatureBits(uint64_t Feature) { + return getSTI().getFeatureBits()[Feature]; + } + void clearFeatureBits(uint64_t Feature, StringRef FeatureString) { if (getSTI().getFeatureBits()[Feature]) { MCSubtargetInfo &STI = copySTI(); @@ -161,10 +185,15 @@ class RISCVAsmParser : public MCTargetAsmParser { } void pushFeatureBits() { + assert(FeatureBitStack.size() == ParserOptionsStack.size() && + "These two stacks must be kept synchronized"); FeatureBitStack.push_back(getSTI().getFeatureBits()); + ParserOptionsStack.push_back(ParserOptions); } bool popFeatureBits() { + assert(FeatureBitStack.size() == ParserOptionsStack.size() && + "These two stacks must be kept synchronized"); if (FeatureBitStack.empty()) return true; @@ -172,8 +201,13 @@ class RISCVAsmParser : public MCTargetAsmParser { copySTI().setFeatureBits(FeatureBits); setAvailableFeatures(ComputeAvailableFeatures(FeatureBits)); + ParserOptions = ParserOptionsStack.pop_back_val(); + return false; } + + std::unique_ptr<RISCVOperand> defaultMaskRegOp() const; + public: enum RISCVMatchResultTy { Match_Dummy = FIRST_TARGET_MATCH_RESULT_TY, @@ -195,17 +229,21 @@ public: Parser.addAliasForDirective(".dword", ".8byte"); setAvailableFeatures(ComputeAvailableFeatures(STI.getFeatureBits())); - if (Options.ABIName.back() == 'f' && + auto ABIName = StringRef(Options.ABIName); + if (ABIName.endswith("f") && !getSTI().getFeatureBits()[RISCV::FeatureStdExtF]) { errs() << "Hard-float 'f' ABI can't be used for a target that " "doesn't support the F instruction set extension (ignoring " "target-abi)\n"; - } else if (Options.ABIName.back() == 'd' && + } else if (ABIName.endswith("d") && !getSTI().getFeatureBits()[RISCV::FeatureStdExtD]) { errs() << "Hard-float 'd' ABI can't be used for a target that " "doesn't support the D instruction set extension (ignoring " "target-abi)\n"; } + + const MCObjectFileInfo *MOFI = Parser.getContext().getObjectFileInfo(); + ParserOptions.IsPicEnabled = MOFI->isPositionIndependent(); } }; @@ -217,7 +255,8 @@ struct RISCVOperand : public MCParsedAsmOperand { Token, Register, Immediate, - SystemRegister + SystemRegister, + VType, } Kind; bool IsRV64; @@ -238,12 +277,32 @@ struct RISCVOperand : public MCParsedAsmOperand { // e.g.: read/write or user/supervisor/machine privileges. }; + enum class VSEW { + SEW_8 = 0, + SEW_16, + SEW_32, + SEW_64, + SEW_128, + SEW_256, + SEW_512, + SEW_1024, + }; + + enum class VLMUL { LMUL_1 = 0, LMUL_2, LMUL_4, LMUL_8 }; + + struct VTypeOp { + VSEW Sew; + VLMUL Lmul; + unsigned Encoding; + }; + SMLoc StartLoc, EndLoc; union { StringRef Tok; RegOp Reg; ImmOp Imm; struct SysRegOp SysReg; + struct VTypeOp VType; }; RISCVOperand(KindTy K) : MCParsedAsmOperand(), Kind(K) {} @@ -267,14 +326,21 @@ public: case KindTy::SystemRegister: SysReg = o.SysReg; break; + case KindTy::VType: + VType = o.VType; + break; } } bool isToken() const override { return Kind == KindTy::Token; } bool isReg() const override { return Kind == KindTy::Register; } + bool isV0Reg() const { + return Kind == KindTy::Register && Reg.RegNum == RISCV::V0; + } bool isImm() const override { return Kind == KindTy::Immediate; } bool isMem() const override { return false; } bool isSystemRegister() const { return Kind == KindTy::SystemRegister; } + bool isVType() const { return Kind == KindTy::VType; } bool isGPR() const { return Kind == KindTy::Register && @@ -336,6 +402,16 @@ public: VK == RISCVMCExpr::VK_RISCV_CALL_PLT); } + bool isPseudoJumpSymbol() const { + int64_t Imm; + RISCVMCExpr::VariantKind VK = RISCVMCExpr::VK_RISCV_None; + // Must be of 'immediate' type but not a constant. + if (!isImm() || evaluateConstantImm(getImm(), Imm, VK)) + return false; + return RISCVAsmParser::classifySymbolRef(getImm(), VK, Imm) && + VK == RISCVMCExpr::VK_RISCV_CALL; + } + bool isTPRelAddSymbol() const { int64_t Imm; RISCVMCExpr::VariantKind VK = RISCVMCExpr::VK_RISCV_None; @@ -348,6 +424,8 @@ public: bool isCSRSystemRegister() const { return isSystemRegister(); } + bool isVTypeI() const { return isVType(); } + /// Return true if the operand is a valid for the fence instruction e.g. /// ('iorw'). bool isFenceArg() const { @@ -425,6 +503,17 @@ public: return (isRV64() && isUInt<6>(Imm)) || isUInt<5>(Imm); } + bool isUImmLog2XLenHalf() const { + int64_t Imm; + RISCVMCExpr::VariantKind VK = RISCVMCExpr::VK_RISCV_None; + if (!isImm()) + return false; + if (!evaluateConstantImm(getImm(), Imm, VK) || + VK != RISCVMCExpr::VK_RISCV_None) + return false; + return (isRV64() && isUInt<5>(Imm)) || isUInt<4>(Imm); + } + bool isUImm5() const { int64_t Imm; RISCVMCExpr::VariantKind VK = RISCVMCExpr::VK_RISCV_None; @@ -444,6 +533,15 @@ public: VK == RISCVMCExpr::VK_RISCV_None; } + bool isSImm5() const { + if (!isImm()) + return false; + RISCVMCExpr::VariantKind VK = RISCVMCExpr::VK_RISCV_None; + int64_t Imm; + bool IsConstantImm = evaluateConstantImm(getImm(), Imm, VK); + return IsConstantImm && isInt<5>(Imm) && VK == RISCVMCExpr::VK_RISCV_None; + } + bool isSImm6() const { if (!isImm()) return false; @@ -451,7 +549,7 @@ public: int64_t Imm; bool IsConstantImm = evaluateConstantImm(getImm(), Imm, VK); return IsConstantImm && isInt<6>(Imm) && - VK == RISCVMCExpr::VK_RISCV_None; + VK == RISCVMCExpr::VK_RISCV_None; } bool isSImm6NonZero() const { @@ -609,6 +707,16 @@ public: return IsConstantImm && (Imm == 0) && VK == RISCVMCExpr::VK_RISCV_None; } + bool isSImm5Plus1() const { + if (!isImm()) + return false; + RISCVMCExpr::VariantKind VK = RISCVMCExpr::VK_RISCV_None; + int64_t Imm; + bool IsConstantImm = evaluateConstantImm(getImm(), Imm, VK); + return IsConstantImm && isInt<5>(Imm - 1) && + VK == RISCVMCExpr::VK_RISCV_None; + } + /// getStartLoc - Gets location of the first token of this operand SMLoc getStartLoc() const override { return StartLoc; } /// getEndLoc - Gets location of the last token of this operand @@ -636,6 +744,51 @@ public: return Tok; } + static StringRef getSEWStr(VSEW Sew) { + switch (Sew) { + case VSEW::SEW_8: + return "e8"; + case VSEW::SEW_16: + return "e16"; + case VSEW::SEW_32: + return "e32"; + case VSEW::SEW_64: + return "e64"; + case VSEW::SEW_128: + return "e128"; + case VSEW::SEW_256: + return "e256"; + case VSEW::SEW_512: + return "e512"; + case VSEW::SEW_1024: + return "e1024"; + } + return ""; + } + + static StringRef getLMULStr(VLMUL Lmul) { + switch (Lmul) { + case VLMUL::LMUL_1: + return "m1"; + case VLMUL::LMUL_2: + return "m2"; + case VLMUL::LMUL_4: + return "m4"; + case VLMUL::LMUL_8: + return "m8"; + } + return ""; + } + + StringRef getVType(SmallString<32> &Buf) const { + assert(Kind == KindTy::VType && "Invalid access!"); + Buf.append(getSEWStr(VType.Sew)); + Buf.append(","); + Buf.append(getLMULStr(VType.Lmul)); + + return Buf.str(); + } + void print(raw_ostream &OS) const override { switch (Kind) { case KindTy::Immediate: @@ -651,6 +804,10 @@ public: case KindTy::SystemRegister: OS << "<sysreg: " << getSysReg() << '>'; break; + case KindTy::VType: + SmallString<32> VTypeBuf; + OS << "<vtype: " << getVType(VTypeBuf) << '>'; + break; } } @@ -695,6 +852,20 @@ public: return Op; } + static std::unique_ptr<RISCVOperand> createVType(APInt Sew, APInt Lmul, + SMLoc S, bool IsRV64) { + auto Op = std::make_unique<RISCVOperand>(KindTy::VType); + Sew.ashrInPlace(3); + unsigned SewLog2 = Sew.logBase2(); + unsigned LmulLog2 = Lmul.logBase2(); + Op->VType.Sew = static_cast<VSEW>(SewLog2); + Op->VType.Lmul = static_cast<VLMUL>(LmulLog2); + Op->VType.Encoding = (SewLog2 << 2) | LmulLog2; + Op->StartLoc = S; + Op->IsRV64 = IsRV64; + return Op; + } + void addExpr(MCInst &Inst, const MCExpr *Expr) const { assert(Expr && "Expr shouldn't be null!"); int64_t Imm = 0; @@ -718,6 +889,16 @@ public: addExpr(Inst, getImm()); } + void addSImm5Plus1Operands(MCInst &Inst, unsigned N) const { + assert(N == 1 && "Invalid number of operands!"); + int64_t Imm = 0; + RISCVMCExpr::VariantKind VK = RISCVMCExpr::VK_RISCV_None; + bool IsConstant = evaluateConstantImm(getImm(), Imm, VK); + assert(IsConstant && "Expect constant value!"); + (void)IsConstant; + Inst.addOperand(MCOperand::createImm(Imm - 1)); + } + void addFenceArgOperands(MCInst &Inst, unsigned N) const { assert(N == 1 && "Invalid number of operands!"); // isFenceArg has validated the operand, meaning this cast is safe @@ -742,6 +923,11 @@ public: Inst.addOperand(MCOperand::createImm(SysReg.Encoding)); } + void addVTypeIOperands(MCInst &Inst, unsigned N) const { + assert(N == 1 && "Invalid number of operands!"); + Inst.addOperand(MCOperand::createImm(VType.Encoding)); + } + // Returns the rounding mode represented by this RISCVOperand. Should only // be called after checking isFRMArg. RISCVFPRndMode::RoundingMode getRoundingMode() const { @@ -819,6 +1005,8 @@ bool RISCVAsmParser::MatchAndEmitInstruction(SMLoc IDLoc, unsigned &Opcode, default: break; case Match_Success: + if (validateInstruction(Inst, Operands)) + return true; return processInstruction(Inst, IDLoc, Operands, Out); case Match_MissingFeature: { assert(MissingFeatures.any() && "Unknown missing features!"); @@ -885,6 +1073,10 @@ bool RISCVAsmParser::MatchAndEmitInstruction(SMLoc IDLoc, unsigned &Opcode, if (isRV64()) return generateImmOutOfRangeError(Operands, ErrorInfo, 1, (1 << 6) - 1); return generateImmOutOfRangeError(Operands, ErrorInfo, 1, (1 << 5) - 1); + case Match_InvalidUImmLog2XLenHalf: + if (isRV64()) + return generateImmOutOfRangeError(Operands, ErrorInfo, 0, (1 << 5) - 1); + return generateImmOutOfRangeError(Operands, ErrorInfo, 0, (1 << 4) - 1); case Match_InvalidUImm5: return generateImmOutOfRangeError(Operands, ErrorInfo, 0, (1 << 5) - 1); case Match_InvalidSImm6: @@ -975,6 +1167,10 @@ bool RISCVAsmParser::MatchAndEmitInstruction(SMLoc IDLoc, unsigned &Opcode, SMLoc ErrorLoc = ((RISCVOperand &)*Operands[ErrorInfo]).getStartLoc(); return Error(ErrorLoc, "operand must be a bare symbol name"); } + case Match_InvalidPseudoJumpSymbol: { + SMLoc ErrorLoc = ((RISCVOperand &)*Operands[ErrorInfo]).getStartLoc(); + return Error(ErrorLoc, "operand must be a valid jump target"); + } case Match_InvalidCallSymbol: { SMLoc ErrorLoc = ((RISCVOperand &)*Operands[ErrorInfo]).getStartLoc(); return Error(ErrorLoc, "operand must be a bare symbol name"); @@ -983,6 +1179,20 @@ bool RISCVAsmParser::MatchAndEmitInstruction(SMLoc IDLoc, unsigned &Opcode, SMLoc ErrorLoc = ((RISCVOperand &)*Operands[ErrorInfo]).getStartLoc(); return Error(ErrorLoc, "operand must be a symbol with %tprel_add modifier"); } + case Match_InvalidVTypeI: { + SMLoc ErrorLoc = ((RISCVOperand &)*Operands[ErrorInfo]).getStartLoc(); + return Error(ErrorLoc, + "operand must be e[8|16|32|64|128|256|512|1024],m[1|2|4|8]"); + } + case Match_InvalidVMaskRegister: { + SMLoc ErrorLoc = ((RISCVOperand &)*Operands[ErrorInfo]).getStartLoc(); + return Error(ErrorLoc, "operand must be v0.t"); + } + case Match_InvalidSImm5Plus1: { + return generateImmOutOfRangeError(Operands, ErrorInfo, -(1 << 4) + 1, + (1 << 4), + "immediate must be in the range"); + } } llvm_unreachable("Unknown match type detected!"); @@ -1009,17 +1219,25 @@ static bool matchRegisterNameHelper(bool IsRV32E, Register &RegNo, bool RISCVAsmParser::ParseRegister(unsigned &RegNo, SMLoc &StartLoc, SMLoc &EndLoc) { + if (tryParseRegister(RegNo, StartLoc, EndLoc) != MatchOperand_Success) + return Error(StartLoc, "invalid register name"); + return false; +} + +OperandMatchResultTy RISCVAsmParser::tryParseRegister(unsigned &RegNo, + SMLoc &StartLoc, + SMLoc &EndLoc) { const AsmToken &Tok = getParser().getTok(); StartLoc = Tok.getLoc(); EndLoc = Tok.getEndLoc(); RegNo = 0; StringRef Name = getLexer().getTok().getIdentifier(); - if (matchRegisterNameHelper(isRV32E(), (Register&)RegNo, Name)) - return Error(StartLoc, "invalid register name"); + if (matchRegisterNameHelper(isRV32E(), (Register &)RegNo, Name)) + return MatchOperand_NoMatch; getParser().Lex(); // Eat identifier token. - return false; + return MatchOperand_Success; } OperandMatchResultTy RISCVAsmParser::parseRegister(OperandVector &Operands, @@ -1112,6 +1330,8 @@ RISCVAsmParser::parseCSRSystemRegister(OperandVector &Operands) { return MatchOperand_ParseFail; auto SysReg = RISCVSysReg::lookupSysRegByName(Identifier); + if (!SysReg) + SysReg = RISCVSysReg::lookupSysRegByAltName(Identifier); // Accept a named Sys Reg if the required features are present. if (SysReg) { if (!SysReg->haveRequiredFeatures(getSTI().getFeatureBits())) { @@ -1286,6 +1506,27 @@ OperandMatchResultTy RISCVAsmParser::parseCallSymbol(OperandVector &Operands) { return MatchOperand_Success; } +OperandMatchResultTy +RISCVAsmParser::parsePseudoJumpSymbol(OperandVector &Operands) { + SMLoc S = getLoc(); + SMLoc E = SMLoc::getFromPointer(S.getPointer() - 1); + const MCExpr *Res; + + if (getParser().parseExpression(Res)) + return MatchOperand_ParseFail; + + if (Res->getKind() != MCExpr::ExprKind::SymbolRef || + cast<MCSymbolRefExpr>(Res)->getKind() == + MCSymbolRefExpr::VariantKind::VK_PLT) { + Error(S, "operand must be a valid jump target"); + return MatchOperand_ParseFail; + } + + Res = RISCVMCExpr::create(Res, RISCVMCExpr::VK_RISCV_CALL, getContext()); + Operands.push_back(RISCVOperand::createImm(Res, S, E, isRV64())); + return MatchOperand_Success; +} + OperandMatchResultTy RISCVAsmParser::parseJALOffset(OperandVector &Operands) { // Parsing jal operands is fiddly due to the `jal foo` and `jal ra, foo` // both being acceptable forms. When parsing `jal ra, foo` this function @@ -1303,6 +1544,74 @@ OperandMatchResultTy RISCVAsmParser::parseJALOffset(OperandVector &Operands) { return parseImmediate(Operands); } +OperandMatchResultTy RISCVAsmParser::parseVTypeI(OperandVector &Operands) { + SMLoc S = getLoc(); + if (getLexer().getKind() != AsmToken::Identifier) + return MatchOperand_NoMatch; + + // Parse "e8,m1" + StringRef Name = getLexer().getTok().getIdentifier(); + if (!Name.consume_front("e")) + return MatchOperand_NoMatch; + APInt Sew(16, Name, 10); + if (Sew != 8 && Sew != 16 && Sew != 32 && Sew != 64 && Sew != 128 && + Sew != 256 && Sew != 512 && Sew != 1024) + return MatchOperand_NoMatch; + getLexer().Lex(); + + if (getLexer().getKind() == AsmToken::EndOfStatement) { + Operands.push_back( + RISCVOperand::createVType(Sew, APInt(16, 1), S, isRV64())); + + return MatchOperand_Success; + } + + if (!getLexer().is(AsmToken::Comma)) + return MatchOperand_NoMatch; + getLexer().Lex(); + + Name = getLexer().getTok().getIdentifier(); + if (!Name.consume_front("m")) + return MatchOperand_NoMatch; + APInt Lmul(16, Name, 10); + if (Lmul != 1 && Lmul != 2 && Lmul != 4 && Lmul != 8) + return MatchOperand_NoMatch; + getLexer().Lex(); + + if (getLexer().getKind() != AsmToken::EndOfStatement) + return MatchOperand_NoMatch; + + Operands.push_back(RISCVOperand::createVType(Sew, Lmul, S, isRV64())); + + return MatchOperand_Success; +} + +OperandMatchResultTy RISCVAsmParser::parseMaskReg(OperandVector &Operands) { + switch (getLexer().getKind()) { + default: + return MatchOperand_NoMatch; + case AsmToken::Identifier: + StringRef Name = getLexer().getTok().getIdentifier(); + if (!Name.consume_back(".t")) { + Error(getLoc(), "expected '.t' suffix"); + return MatchOperand_ParseFail; + } + Register RegNo; + matchRegisterNameHelper(isRV32E(), RegNo, Name); + + if (RegNo == RISCV::NoRegister) + return MatchOperand_NoMatch; + if (RegNo != RISCV::V0) + return MatchOperand_NoMatch; + SMLoc S = getLoc(); + SMLoc E = SMLoc::getFromPointer(S.getPointer() - 1); + getLexer().Lex(); + Operands.push_back(RISCVOperand::createReg(RegNo, S, E, isRV64())); + } + + return MatchOperand_Success; +} + OperandMatchResultTy RISCVAsmParser::parseMemOpBaseReg(OperandVector &Operands) { if (getLexer().isNot(AsmToken::LParen)) { @@ -1532,6 +1841,8 @@ bool RISCVAsmParser::ParseDirective(AsmToken DirectiveID) { if (IDVal == ".option") return parseDirectiveOption(); + else if (IDVal == ".attribute") + return parseDirectiveAttribute(); return true; } @@ -1598,6 +1909,30 @@ bool RISCVAsmParser::parseDirectiveOption() { return false; } + if (Option == "pic") { + getTargetStreamer().emitDirectiveOptionPIC(); + + Parser.Lex(); + if (Parser.getTok().isNot(AsmToken::EndOfStatement)) + return Error(Parser.getTok().getLoc(), + "unexpected token, expected end of statement"); + + ParserOptions.IsPicEnabled = true; + return false; + } + + if (Option == "nopic") { + getTargetStreamer().emitDirectiveOptionNoPIC(); + + Parser.Lex(); + if (Parser.getTok().isNot(AsmToken::EndOfStatement)) + return Error(Parser.getTok().getLoc(), + "unexpected token, expected end of statement"); + + ParserOptions.IsPicEnabled = false; + return false; + } + if (Option == "relax") { getTargetStreamer().emitDirectiveOptionRelax(); @@ -1630,12 +1965,157 @@ bool RISCVAsmParser::parseDirectiveOption() { return false; } +/// parseDirectiveAttribute +/// ::= .attribute expression ',' ( expression | "string" ) +/// ::= .attribute identifier ',' ( expression | "string" ) +bool RISCVAsmParser::parseDirectiveAttribute() { + MCAsmParser &Parser = getParser(); + int64_t Tag; + SMLoc TagLoc; + TagLoc = Parser.getTok().getLoc(); + if (Parser.getTok().is(AsmToken::Identifier)) { + StringRef Name = Parser.getTok().getIdentifier(); + Optional<unsigned> Ret = + ELFAttrs::attrTypeFromString(Name, RISCVAttrs::RISCVAttributeTags); + if (!Ret.hasValue()) { + Error(TagLoc, "attribute name not recognised: " + Name); + return false; + } + Tag = Ret.getValue(); + Parser.Lex(); + } else { + const MCExpr *AttrExpr; + + TagLoc = Parser.getTok().getLoc(); + if (Parser.parseExpression(AttrExpr)) + return true; + + const MCConstantExpr *CE = dyn_cast<MCConstantExpr>(AttrExpr); + if (check(!CE, TagLoc, "expected numeric constant")) + return true; + + Tag = CE->getValue(); + } + + if (Parser.parseToken(AsmToken::Comma, "comma expected")) + return true; + + StringRef StringValue; + int64_t IntegerValue = 0; + bool IsIntegerValue = true; + + // RISC-V attributes have a string value if the tag number is odd + // and an integer value if the tag number is even. + if (Tag % 2) + IsIntegerValue = false; + + SMLoc ValueExprLoc = Parser.getTok().getLoc(); + if (IsIntegerValue) { + const MCExpr *ValueExpr; + if (Parser.parseExpression(ValueExpr)) + return true; + + const MCConstantExpr *CE = dyn_cast<MCConstantExpr>(ValueExpr); + if (!CE) + return Error(ValueExprLoc, "expected numeric constant"); + IntegerValue = CE->getValue(); + } else { + if (Parser.getTok().isNot(AsmToken::String)) + return Error(Parser.getTok().getLoc(), "expected string constant"); + + StringValue = Parser.getTok().getStringContents(); + Parser.Lex(); + } + + if (Parser.parseToken(AsmToken::EndOfStatement, + "unexpected token in '.attribute' directive")) + return true; + + if (Tag == RISCVAttrs::ARCH) { + StringRef Arch = StringValue; + if (Arch.consume_front("rv32")) + clearFeatureBits(RISCV::Feature64Bit, "64bit"); + else if (Arch.consume_front("rv64")) + setFeatureBits(RISCV::Feature64Bit, "64bit"); + else + return Error(ValueExprLoc, "bad arch string " + Arch); + + while (!Arch.empty()) { + if (Arch[0] == 'i') + clearFeatureBits(RISCV::FeatureRV32E, "e"); + else if (Arch[0] == 'e') + setFeatureBits(RISCV::FeatureRV32E, "e"); + else if (Arch[0] == 'g') { + clearFeatureBits(RISCV::FeatureRV32E, "e"); + setFeatureBits(RISCV::FeatureStdExtM, "m"); + setFeatureBits(RISCV::FeatureStdExtA, "a"); + setFeatureBits(RISCV::FeatureStdExtF, "f"); + setFeatureBits(RISCV::FeatureStdExtD, "d"); + } else if (Arch[0] == 'm') + setFeatureBits(RISCV::FeatureStdExtM, "m"); + else if (Arch[0] == 'a') + setFeatureBits(RISCV::FeatureStdExtA, "a"); + else if (Arch[0] == 'f') + setFeatureBits(RISCV::FeatureStdExtF, "f"); + else if (Arch[0] == 'd') { + setFeatureBits(RISCV::FeatureStdExtF, "f"); + setFeatureBits(RISCV::FeatureStdExtD, "d"); + } else if (Arch[0] == 'c') { + setFeatureBits(RISCV::FeatureStdExtC, "c"); + } else + return Error(ValueExprLoc, "bad arch string " + Arch); + + Arch = Arch.drop_front(1); + int major = 0; + int minor = 0; + Arch.consumeInteger(10, major); + Arch.consume_front("p"); + Arch.consumeInteger(10, minor); + if (major != 0 || minor != 0) { + Arch = Arch.drop_until([](char c) { return c == '_' || c == '"'; }); + Arch = Arch.drop_while([](char c) { return c == '_'; }); + } + } + } + + if (IsIntegerValue) + getTargetStreamer().emitAttribute(Tag, IntegerValue); + else { + if (Tag != RISCVAttrs::ARCH) { + getTargetStreamer().emitTextAttribute(Tag, StringValue); + } else { + std::string formalArchStr = "rv32"; + if (getFeatureBits(RISCV::Feature64Bit)) + formalArchStr = "rv64"; + if (getFeatureBits(RISCV::FeatureRV32E)) + formalArchStr = (Twine(formalArchStr) + "e1p9").str(); + else + formalArchStr = (Twine(formalArchStr) + "i2p0").str(); + + if (getFeatureBits(RISCV::FeatureStdExtM)) + formalArchStr = (Twine(formalArchStr) + "_m2p0").str(); + if (getFeatureBits(RISCV::FeatureStdExtA)) + formalArchStr = (Twine(formalArchStr) + "_a2p0").str(); + if (getFeatureBits(RISCV::FeatureStdExtF)) + formalArchStr = (Twine(formalArchStr) + "_f2p0").str(); + if (getFeatureBits(RISCV::FeatureStdExtD)) + formalArchStr = (Twine(formalArchStr) + "_d2p0").str(); + if (getFeatureBits(RISCV::FeatureStdExtC)) + formalArchStr = (Twine(formalArchStr) + "_c2p0").str(); + + getTargetStreamer().emitTextAttribute(Tag, formalArchStr); + } + } + + return false; +} + void RISCVAsmParser::emitToStreamer(MCStreamer &S, const MCInst &Inst) { MCInst CInst; bool Res = compressInst(CInst, Inst, getSTI(), S.getContext()); if (Res) ++RISCVNumInstrsCompressed; - S.EmitInstruction((Res ? CInst : Inst), getSTI()); + S.emitInstruction((Res ? CInst : Inst), getSTI()); } void RISCVAsmParser::emitLoadImm(Register DestReg, int64_t Value, @@ -1671,7 +2151,7 @@ void RISCVAsmParser::emitAuipcInstPair(MCOperand DestReg, MCOperand TmpReg, MCSymbol *TmpLabel = Ctx.createTempSymbol( "pcrel_hi", /* AlwaysAddSuffix */ true, /* CanBeUnnamed */ false); - Out.EmitLabel(TmpLabel); + Out.emitLabel(TmpLabel); const RISCVMCExpr *SymbolHi = RISCVMCExpr::create(Symbol, VKHi, Ctx); emitToStreamer( @@ -1716,8 +2196,7 @@ void RISCVAsmParser::emitLoadAddress(MCInst &Inst, SMLoc IDLoc, const MCExpr *Symbol = Inst.getOperand(1).getExpr(); unsigned SecondOpcode; RISCVMCExpr::VariantKind VKHi; - // FIXME: Should check .option (no)pic when implemented - if (getContext().getObjectFileInfo()->isPositionIndependent()) { + if (ParserOptions.IsPicEnabled) { SecondOpcode = isRV64() ? RISCV::LD : RISCV::LW; VKHi = RISCVMCExpr::VK_RISCV_GOT_HI; } else { @@ -1788,6 +2267,89 @@ bool RISCVAsmParser::checkPseudoAddTPRel(MCInst &Inst, return false; } +std::unique_ptr<RISCVOperand> RISCVAsmParser::defaultMaskRegOp() const { + return RISCVOperand::createReg(RISCV::NoRegister, llvm::SMLoc(), + llvm::SMLoc(), isRV64()); +} + +bool RISCVAsmParser::validateInstruction(MCInst &Inst, + OperandVector &Operands) { + const MCInstrDesc &MCID = MII.get(Inst.getOpcode()); + unsigned TargetFlags = + (MCID.TSFlags >> RISCV::ConstraintOffset) & RISCV::ConstraintMask; + if (TargetFlags == RISCV::NoConstraint) + return false; + + unsigned DestReg = Inst.getOperand(0).getReg(); + // Operands[1] will be the first operand, DestReg. + SMLoc Loc = Operands[1]->getStartLoc(); + if ((TargetFlags == RISCV::WidenV) || (TargetFlags == RISCV::WidenW) || + (TargetFlags == RISCV::SlideUp) || (TargetFlags == RISCV::Vrgather) || + (TargetFlags == RISCV::Vcompress)) { + if (TargetFlags != RISCV::WidenW) { + unsigned Src2Reg = Inst.getOperand(1).getReg(); + if (DestReg == Src2Reg) + return Error(Loc, "The destination vector register group cannot overlap" + " the source vector register group."); + if (TargetFlags == RISCV::WidenV) { + // Assume DestReg LMUL is 2 at least for widening/narrowing operations. + if (DestReg + 1 == Src2Reg) + return Error(Loc, + "The destination vector register group cannot overlap" + " the source vector register group."); + } + } + if (Inst.getOperand(2).isReg()) { + unsigned Src1Reg = Inst.getOperand(2).getReg(); + if (DestReg == Src1Reg) + return Error(Loc, "The destination vector register group cannot overlap" + " the source vector register group."); + if (TargetFlags == RISCV::WidenV || TargetFlags == RISCV::WidenW) { + // Assume DestReg LMUL is 2 at least for widening/narrowing operations. + if (DestReg + 1 == Src1Reg) + return Error(Loc, + "The destination vector register group cannot overlap" + " the source vector register group."); + } + } + if (Inst.getNumOperands() == 4) { + unsigned MaskReg = Inst.getOperand(3).getReg(); + + if (DestReg == MaskReg) + return Error(Loc, "The destination vector register group cannot overlap" + " the mask register."); + } + } else if (TargetFlags == RISCV::Narrow) { + unsigned Src2Reg = Inst.getOperand(1).getReg(); + if (DestReg == Src2Reg) + return Error(Loc, "The destination vector register group cannot overlap" + " the source vector register group."); + // Assume Src2Reg LMUL is 2 at least for widening/narrowing operations. + if (DestReg == Src2Reg + 1) + return Error(Loc, "The destination vector register group cannot overlap" + " the source vector register group."); + } else if (TargetFlags == RISCV::WidenCvt || TargetFlags == RISCV::Iota) { + unsigned Src2Reg = Inst.getOperand(1).getReg(); + if (DestReg == Src2Reg) + return Error(Loc, "The destination vector register group cannot overlap" + " the source vector register group."); + if (TargetFlags == RISCV::WidenCvt) { + // Assume DestReg LMUL is 2 at least for widening/narrowing operations. + if (DestReg + 1 == Src2Reg) + return Error(Loc, "The destination vector register group cannot overlap" + " the source vector register group."); + } + if (Inst.getNumOperands() == 3) { + unsigned MaskReg = Inst.getOperand(2).getReg(); + + if (DestReg == MaskReg) + return Error(Loc, "The destination vector register group cannot overlap" + " the mask register."); + } + } + return false; +} + bool RISCVAsmParser::processInstruction(MCInst &Inst, SMLoc IDLoc, OperandVector &Operands, MCStreamer &Out) { diff --git a/llvm/lib/Target/RISCV/Disassembler/RISCVDisassembler.cpp b/llvm/lib/Target/RISCV/Disassembler/RISCVDisassembler.cpp index 1461a40227bf..37edc19398a5 100644 --- a/llvm/lib/Target/RISCV/Disassembler/RISCVDisassembler.cpp +++ b/llvm/lib/Target/RISCV/Disassembler/RISCVDisassembler.cpp @@ -18,6 +18,7 @@ #include "llvm/MC/MCDisassembler/MCDisassembler.h" #include "llvm/MC/MCFixedLenDisassembler.h" #include "llvm/MC/MCInst.h" +#include "llvm/MC/MCInstrInfo.h" #include "llvm/MC/MCRegisterInfo.h" #include "llvm/MC/MCSubtargetInfo.h" #include "llvm/Support/Endian.h" @@ -31,10 +32,12 @@ typedef MCDisassembler::DecodeStatus DecodeStatus; namespace { class RISCVDisassembler : public MCDisassembler { + std::unique_ptr<MCInstrInfo const> const MCII; public: - RISCVDisassembler(const MCSubtargetInfo &STI, MCContext &Ctx) - : MCDisassembler(STI, Ctx) {} + RISCVDisassembler(const MCSubtargetInfo &STI, MCContext &Ctx, + MCInstrInfo const *MCII) + : MCDisassembler(STI, Ctx), MCII(MCII) {} DecodeStatus getInstruction(MCInst &Instr, uint64_t &Size, ArrayRef<uint8_t> Bytes, uint64_t Address, @@ -45,7 +48,7 @@ public: static MCDisassembler *createRISCVDisassembler(const Target &T, const MCSubtargetInfo &STI, MCContext &Ctx) { - return new RISCVDisassembler(STI, Ctx); + return new RISCVDisassembler(STI, Ctx, T.createMCInstrInfo()); } extern "C" LLVM_EXTERNAL_VISIBILITY void LLVMInitializeRISCVDisassembler() { @@ -148,6 +151,33 @@ static DecodeStatus DecodeGPRCRegisterClass(MCInst &Inst, uint64_t RegNo, return MCDisassembler::Success; } +static DecodeStatus DecodeVRRegisterClass(MCInst &Inst, uint64_t RegNo, + uint64_t Address, + const void *Decoder) { + if (RegNo >= 32) + return MCDisassembler::Fail; + + Register Reg = RISCV::V0 + RegNo; + Inst.addOperand(MCOperand::createReg(Reg)); + return MCDisassembler::Success; +} + +static DecodeStatus decodeVMaskReg(MCInst &Inst, uint64_t RegNo, + uint64_t Address, const void *Decoder) { + Register Reg = RISCV::NoRegister; + switch (RegNo) { + default: + return MCDisassembler::Fail; + case 0: + Reg = RISCV::V0; + break; + case 1: + break; + } + Inst.addOperand(MCOperand::createReg(Reg)); + return MCDisassembler::Success; +} + // Add implied SP operand for instructions *SP compressed instructions. The SP // operand isn't explicitly encoded in the instruction. static void addImplySP(MCInst &Inst, int64_t Address, const void *Decoder) { @@ -349,6 +379,19 @@ DecodeStatus RISCVDisassembler::getInstruction(MCInst &MI, uint64_t &Size, } } + if (STI.getFeatureBits()[RISCV::FeatureExtZbproposedc] && + STI.getFeatureBits()[RISCV::FeatureStdExtC]) { + LLVM_DEBUG( + dbgs() << "Trying RVBC32 table (BitManip 16-bit Instruction):\n"); + // Calling the auto-generated decoder function. + Result = decodeInstruction(DecoderTableRVBC16, MI, Insn, Address, + this, STI); + if (Result != MCDisassembler::Fail) { + Size = 2; + return Result; + } + } + LLVM_DEBUG(dbgs() << "Trying RISCV_C table (16-bit Instruction):\n"); // Calling the auto-generated decoder function. Result = decodeInstruction(DecoderTable16, MI, Insn, Address, this, STI); diff --git a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVAsmBackend.cpp b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVAsmBackend.cpp index 5881a0a86ef7..bb1f1cc7f49a 100644 --- a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVAsmBackend.cpp +++ b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVAsmBackend.cpp @@ -9,6 +9,7 @@ #include "RISCVAsmBackend.h" #include "RISCVMCExpr.h" #include "llvm/ADT/APInt.h" +#include "llvm/MC/MCAsmLayout.h" #include "llvm/MC/MCAssembler.h" #include "llvm/MC/MCContext.h" #include "llvm/MC/MCDirectives.h" @@ -22,14 +23,75 @@ using namespace llvm; +Optional<MCFixupKind> RISCVAsmBackend::getFixupKind(StringRef Name) const { + if (STI.getTargetTriple().isOSBinFormatELF()) { + unsigned Type; + Type = llvm::StringSwitch<unsigned>(Name) +#define ELF_RELOC(X, Y) .Case(#X, Y) +#include "llvm/BinaryFormat/ELFRelocs/RISCV.def" +#undef ELF_RELOC + .Default(-1u); + if (Type != -1u) + return static_cast<MCFixupKind>(FirstLiteralRelocationKind + Type); + } + return None; +} + +const MCFixupKindInfo & +RISCVAsmBackend::getFixupKindInfo(MCFixupKind Kind) const { + const static MCFixupKindInfo Infos[] = { + // This table *must* be in the order that the fixup_* kinds are defined in + // RISCVFixupKinds.h. + // + // name offset bits flags + {"fixup_riscv_hi20", 12, 20, 0}, + {"fixup_riscv_lo12_i", 20, 12, 0}, + {"fixup_riscv_lo12_s", 0, 32, 0}, + {"fixup_riscv_pcrel_hi20", 12, 20, + MCFixupKindInfo::FKF_IsPCRel | MCFixupKindInfo::FKF_IsTarget}, + {"fixup_riscv_pcrel_lo12_i", 20, 12, + MCFixupKindInfo::FKF_IsPCRel | MCFixupKindInfo::FKF_IsTarget}, + {"fixup_riscv_pcrel_lo12_s", 0, 32, + MCFixupKindInfo::FKF_IsPCRel | MCFixupKindInfo::FKF_IsTarget}, + {"fixup_riscv_got_hi20", 12, 20, MCFixupKindInfo::FKF_IsPCRel}, + {"fixup_riscv_tprel_hi20", 12, 20, 0}, + {"fixup_riscv_tprel_lo12_i", 20, 12, 0}, + {"fixup_riscv_tprel_lo12_s", 0, 32, 0}, + {"fixup_riscv_tprel_add", 0, 0, 0}, + {"fixup_riscv_tls_got_hi20", 12, 20, MCFixupKindInfo::FKF_IsPCRel}, + {"fixup_riscv_tls_gd_hi20", 12, 20, MCFixupKindInfo::FKF_IsPCRel}, + {"fixup_riscv_jal", 12, 20, MCFixupKindInfo::FKF_IsPCRel}, + {"fixup_riscv_branch", 0, 32, MCFixupKindInfo::FKF_IsPCRel}, + {"fixup_riscv_rvc_jump", 2, 11, MCFixupKindInfo::FKF_IsPCRel}, + {"fixup_riscv_rvc_branch", 0, 16, MCFixupKindInfo::FKF_IsPCRel}, + {"fixup_riscv_call", 0, 64, MCFixupKindInfo::FKF_IsPCRel}, + {"fixup_riscv_call_plt", 0, 64, MCFixupKindInfo::FKF_IsPCRel}, + {"fixup_riscv_relax", 0, 0, 0}, + {"fixup_riscv_align", 0, 0, 0}}; + static_assert((array_lengthof(Infos)) == RISCV::NumTargetFixupKinds, + "Not all fixup kinds added to Infos array"); + + // Fixup kinds from .reloc directive are like R_RISCV_NONE. They + // do not require any extra processing. + if (Kind >= FirstLiteralRelocationKind) + return MCAsmBackend::getFixupKindInfo(FK_NONE); + + if (Kind < FirstTargetFixupKind) + return MCAsmBackend::getFixupKindInfo(Kind); + + assert(unsigned(Kind - FirstTargetFixupKind) < getNumFixupKinds() && + "Invalid kind!"); + return Infos[Kind - FirstTargetFixupKind]; +} + // If linker relaxation is enabled, or the relax option had previously been // enabled, always emit relocations even if the fixup can be resolved. This is // necessary for correctness as offsets may change during relaxation. bool RISCVAsmBackend::shouldForceRelocation(const MCAssembler &Asm, const MCFixup &Fixup, const MCValue &Target) { - bool ShouldForce = false; - + if (Fixup.getKind() >= FirstLiteralRelocationKind) + return true; switch (Fixup.getTargetKind()) { default: break; @@ -44,40 +106,9 @@ bool RISCVAsmBackend::shouldForceRelocation(const MCAssembler &Asm, case RISCV::fixup_riscv_tls_got_hi20: case RISCV::fixup_riscv_tls_gd_hi20: return true; - case RISCV::fixup_riscv_pcrel_lo12_i: - case RISCV::fixup_riscv_pcrel_lo12_s: - // For pcrel_lo12, force a relocation if the target of the corresponding - // pcrel_hi20 is not in the same fragment. - const MCFixup *T = cast<RISCVMCExpr>(Fixup.getValue())->getPCRelHiFixup(); - if (!T) { - Asm.getContext().reportError(Fixup.getLoc(), - "could not find corresponding %pcrel_hi"); - return false; - } - - switch (T->getTargetKind()) { - default: - llvm_unreachable("Unexpected fixup kind for pcrel_lo12"); - break; - case RISCV::fixup_riscv_got_hi20: - case RISCV::fixup_riscv_tls_got_hi20: - case RISCV::fixup_riscv_tls_gd_hi20: - ShouldForce = true; - break; - case RISCV::fixup_riscv_pcrel_hi20: { - MCFragment *TFragment = T->getValue()->findAssociatedFragment(); - MCFragment *FixupFragment = Fixup.getValue()->findAssociatedFragment(); - assert(FixupFragment && "We should have a fragment for this fixup"); - ShouldForce = - !TFragment || TFragment->getParent() != FixupFragment->getParent(); - break; - } - } - break; } - return ShouldForce || STI.getFeatureBits()[RISCV::FeatureRelax] || - ForceRelocs; + return STI.getFeatureBits()[RISCV::FeatureRelax] || ForceRelocs; } bool RISCVAsmBackend::fixupNeedsRelaxationAdvanced(const MCFixup &Fixup, @@ -108,10 +139,10 @@ bool RISCVAsmBackend::fixupNeedsRelaxationAdvanced(const MCFixup &Fixup, } } -void RISCVAsmBackend::relaxInstruction(const MCInst &Inst, - const MCSubtargetInfo &STI, - MCInst &Res) const { +void RISCVAsmBackend::relaxInstruction(MCInst &Inst, + const MCSubtargetInfo &STI) const { // TODO: replace this with call to auto generated uncompressinstr() function. + MCInst Res; switch (Inst.getOpcode()) { default: llvm_unreachable("Opcode not expected!"); @@ -142,6 +173,7 @@ void RISCVAsmBackend::relaxInstruction(const MCInst &Inst, Res.addOperand(Inst.getOperand(0)); break; } + Inst = std::move(Res); } // Given a compressed control flow instruction this function returns @@ -284,13 +316,77 @@ static uint64_t adjustFixupValue(const MCFixup &Fixup, uint64_t Value, } } +bool RISCVAsmBackend::evaluateTargetFixup( + const MCAssembler &Asm, const MCAsmLayout &Layout, const MCFixup &Fixup, + const MCFragment *DF, const MCValue &Target, uint64_t &Value, + bool &WasForced) { + const MCFixup *AUIPCFixup; + const MCFragment *AUIPCDF; + MCValue AUIPCTarget; + switch (Fixup.getTargetKind()) { + default: + llvm_unreachable("Unexpected fixup kind!"); + case RISCV::fixup_riscv_pcrel_hi20: + AUIPCFixup = &Fixup; + AUIPCDF = DF; + AUIPCTarget = Target; + break; + case RISCV::fixup_riscv_pcrel_lo12_i: + case RISCV::fixup_riscv_pcrel_lo12_s: { + AUIPCFixup = cast<RISCVMCExpr>(Fixup.getValue())->getPCRelHiFixup(&AUIPCDF); + if (!AUIPCFixup) { + Asm.getContext().reportError(Fixup.getLoc(), + "could not find corresponding %pcrel_hi"); + return true; + } + + // MCAssembler::evaluateFixup will emit an error for this case when it sees + // the %pcrel_hi, so don't duplicate it when also seeing the %pcrel_lo. + const MCExpr *AUIPCExpr = AUIPCFixup->getValue(); + if (!AUIPCExpr->evaluateAsRelocatable(AUIPCTarget, &Layout, AUIPCFixup)) + return true; + break; + } + } + + if (!AUIPCTarget.getSymA() || AUIPCTarget.getSymB()) + return false; + + const MCSymbolRefExpr *A = AUIPCTarget.getSymA(); + const MCSymbol &SA = A->getSymbol(); + if (A->getKind() != MCSymbolRefExpr::VK_None || SA.isUndefined()) + return false; + + auto *Writer = Asm.getWriterPtr(); + if (!Writer) + return false; + + bool IsResolved = Writer->isSymbolRefDifferenceFullyResolvedImpl( + Asm, SA, *AUIPCDF, false, true); + if (!IsResolved) + return false; + + Value = Layout.getSymbolOffset(SA) + AUIPCTarget.getConstant(); + Value -= Layout.getFragmentOffset(AUIPCDF) + AUIPCFixup->getOffset(); + + if (shouldForceRelocation(Asm, *AUIPCFixup, AUIPCTarget)) { + WasForced = true; + return false; + } + + return true; +} + void RISCVAsmBackend::applyFixup(const MCAssembler &Asm, const MCFixup &Fixup, const MCValue &Target, MutableArrayRef<char> Data, uint64_t Value, bool IsResolved, const MCSubtargetInfo *STI) const { + MCFixupKind Kind = Fixup.getKind(); + if (Kind >= FirstLiteralRelocationKind) + return; MCContext &Ctx = Asm.getContext(); - MCFixupKindInfo Info = getFixupKindInfo(Fixup.getKind()); + MCFixupKindInfo Info = getFixupKindInfo(Kind); if (!Value) return; // Doesn't change encoding. // Apply any target-specific value adjustments. diff --git a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVAsmBackend.h b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVAsmBackend.h index 254249c87dc8..090132af3585 100644 --- a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVAsmBackend.h +++ b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVAsmBackend.h @@ -65,6 +65,11 @@ public: const MCAsmLayout &Layout, MCAlignFragment &AF) override; + bool evaluateTargetFixup(const MCAssembler &Asm, const MCAsmLayout &Layout, + const MCFixup &Fixup, const MCFragment *DF, + const MCValue &Target, uint64_t &Value, + bool &WasForced) override; + void applyFixup(const MCAssembler &Asm, const MCFixup &Fixup, const MCValue &Target, MutableArrayRef<char> Data, uint64_t Value, bool IsResolved, @@ -92,52 +97,16 @@ public: return RISCV::NumTargetFixupKinds; } - const MCFixupKindInfo &getFixupKindInfo(MCFixupKind Kind) const override { - const static MCFixupKindInfo Infos[] = { - // This table *must* be in the order that the fixup_* kinds are defined in - // RISCVFixupKinds.h. - // - // name offset bits flags - { "fixup_riscv_hi20", 12, 20, 0 }, - { "fixup_riscv_lo12_i", 20, 12, 0 }, - { "fixup_riscv_lo12_s", 0, 32, 0 }, - { "fixup_riscv_pcrel_hi20", 12, 20, MCFixupKindInfo::FKF_IsPCRel }, - { "fixup_riscv_pcrel_lo12_i", 20, 12, MCFixupKindInfo::FKF_IsPCRel }, - { "fixup_riscv_pcrel_lo12_s", 0, 32, MCFixupKindInfo::FKF_IsPCRel }, - { "fixup_riscv_got_hi20", 12, 20, MCFixupKindInfo::FKF_IsPCRel }, - { "fixup_riscv_tprel_hi20", 12, 20, 0 }, - { "fixup_riscv_tprel_lo12_i", 20, 12, 0 }, - { "fixup_riscv_tprel_lo12_s", 0, 32, 0 }, - { "fixup_riscv_tprel_add", 0, 0, 0 }, - { "fixup_riscv_tls_got_hi20", 12, 20, MCFixupKindInfo::FKF_IsPCRel }, - { "fixup_riscv_tls_gd_hi20", 12, 20, MCFixupKindInfo::FKF_IsPCRel }, - { "fixup_riscv_jal", 12, 20, MCFixupKindInfo::FKF_IsPCRel }, - { "fixup_riscv_branch", 0, 32, MCFixupKindInfo::FKF_IsPCRel }, - { "fixup_riscv_rvc_jump", 2, 11, MCFixupKindInfo::FKF_IsPCRel }, - { "fixup_riscv_rvc_branch", 0, 16, MCFixupKindInfo::FKF_IsPCRel }, - { "fixup_riscv_call", 0, 64, MCFixupKindInfo::FKF_IsPCRel }, - { "fixup_riscv_call_plt", 0, 64, MCFixupKindInfo::FKF_IsPCRel }, - { "fixup_riscv_relax", 0, 0, 0 }, - { "fixup_riscv_align", 0, 0, 0 } - }; - static_assert((array_lengthof(Infos)) == RISCV::NumTargetFixupKinds, - "Not all fixup kinds added to Infos array"); - - if (Kind < FirstTargetFixupKind) - return MCAsmBackend::getFixupKindInfo(Kind); - - assert(unsigned(Kind - FirstTargetFixupKind) < getNumFixupKinds() && - "Invalid kind!"); - return Infos[Kind - FirstTargetFixupKind]; - } + Optional<MCFixupKind> getFixupKind(StringRef Name) const override; + + const MCFixupKindInfo &getFixupKindInfo(MCFixupKind Kind) const override; bool mayNeedRelaxation(const MCInst &Inst, const MCSubtargetInfo &STI) const override; unsigned getRelaxedOpcode(unsigned Op) const; - void relaxInstruction(const MCInst &Inst, const MCSubtargetInfo &STI, - MCInst &Res) const override; - + void relaxInstruction(MCInst &Inst, + const MCSubtargetInfo &STI) const override; bool writeNopData(raw_ostream &OS, uint64_t Count) const override; diff --git a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVELFObjectWriter.cpp b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVELFObjectWriter.cpp index 08b75795ed4b..b38ba2bff582 100644 --- a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVELFObjectWriter.cpp +++ b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVELFObjectWriter.cpp @@ -52,6 +52,8 @@ unsigned RISCVELFObjectWriter::getRelocType(MCContext &Ctx, const MCExpr *Expr = Fixup.getValue(); // Determine the type of the relocation unsigned Kind = Fixup.getTargetKind(); + if (Kind >= FirstLiteralRelocationKind) + return Kind - FirstLiteralRelocationKind; if (IsPCRel) { switch (Kind) { default: diff --git a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVELFStreamer.cpp b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVELFStreamer.cpp index 40fa195f3790..079dc919928a 100644 --- a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVELFStreamer.cpp +++ b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVELFStreamer.cpp @@ -15,14 +15,18 @@ #include "RISCVMCTargetDesc.h" #include "Utils/RISCVBaseInfo.h" #include "llvm/BinaryFormat/ELF.h" +#include "llvm/MC/MCContext.h" +#include "llvm/MC/MCSectionELF.h" #include "llvm/MC/MCSubtargetInfo.h" +#include "llvm/Support/LEB128.h" +#include "llvm/Support/RISCVAttributes.h" using namespace llvm; // This part is for ELF object output. RISCVTargetELFStreamer::RISCVTargetELFStreamer(MCStreamer &S, const MCSubtargetInfo &STI) - : RISCVTargetStreamer(S) { + : RISCVTargetStreamer(S), CurrentVendor("riscv") { MCAssembler &MCA = getStreamer().getAssembler(); const FeatureBitset &Features = STI.getFeatureBits(); auto &MAB = static_cast<RISCVAsmBackend &>(MCA.getBackend()); @@ -62,7 +66,104 @@ MCELFStreamer &RISCVTargetELFStreamer::getStreamer() { void RISCVTargetELFStreamer::emitDirectiveOptionPush() {} void RISCVTargetELFStreamer::emitDirectiveOptionPop() {} +void RISCVTargetELFStreamer::emitDirectiveOptionPIC() {} +void RISCVTargetELFStreamer::emitDirectiveOptionNoPIC() {} void RISCVTargetELFStreamer::emitDirectiveOptionRVC() {} void RISCVTargetELFStreamer::emitDirectiveOptionNoRVC() {} void RISCVTargetELFStreamer::emitDirectiveOptionRelax() {} void RISCVTargetELFStreamer::emitDirectiveOptionNoRelax() {} + +void RISCVTargetELFStreamer::emitAttribute(unsigned Attribute, unsigned Value) { + setAttributeItem(Attribute, Value, /*OverwriteExisting=*/true); +} + +void RISCVTargetELFStreamer::emitTextAttribute(unsigned Attribute, + StringRef String) { + setAttributeItem(Attribute, String, /*OverwriteExisting=*/true); +} + +void RISCVTargetELFStreamer::emitIntTextAttribute(unsigned Attribute, + unsigned IntValue, + StringRef StringValue) { + setAttributeItems(Attribute, IntValue, StringValue, + /*OverwriteExisting=*/true); +} + +void RISCVTargetELFStreamer::finishAttributeSection() { + if (Contents.empty()) + return; + + if (AttributeSection) { + Streamer.SwitchSection(AttributeSection); + } else { + MCAssembler &MCA = getStreamer().getAssembler(); + AttributeSection = MCA.getContext().getELFSection( + ".riscv.attributes", ELF::SHT_RISCV_ATTRIBUTES, 0); + Streamer.SwitchSection(AttributeSection); + + Streamer.emitInt8(ELFAttrs::Format_Version); + } + + // Vendor size + Vendor name + '\0' + const size_t VendorHeaderSize = 4 + CurrentVendor.size() + 1; + + // Tag + Tag Size + const size_t TagHeaderSize = 1 + 4; + + const size_t ContentsSize = calculateContentSize(); + + Streamer.emitInt32(VendorHeaderSize + TagHeaderSize + ContentsSize); + Streamer.emitBytes(CurrentVendor); + Streamer.emitInt8(0); // '\0' + + Streamer.emitInt8(ELFAttrs::File); + Streamer.emitInt32(TagHeaderSize + ContentsSize); + + // Size should have been accounted for already, now + // emit each field as its type (ULEB or String). + for (AttributeItem item : Contents) { + Streamer.emitULEB128IntValue(item.Tag); + switch (item.Type) { + default: + llvm_unreachable("Invalid attribute type"); + case AttributeType::Numeric: + Streamer.emitULEB128IntValue(item.IntValue); + break; + case AttributeType::Text: + Streamer.emitBytes(item.StringValue); + Streamer.emitInt8(0); // '\0' + break; + case AttributeType::NumericAndText: + Streamer.emitULEB128IntValue(item.IntValue); + Streamer.emitBytes(item.StringValue); + Streamer.emitInt8(0); // '\0' + break; + } + } + + Contents.clear(); +} + +size_t RISCVTargetELFStreamer::calculateContentSize() const { + size_t Result = 0; + for (AttributeItem item : Contents) { + switch (item.Type) { + case AttributeType::Hidden: + break; + case AttributeType::Numeric: + Result += getULEB128Size(item.Tag); + Result += getULEB128Size(item.IntValue); + break; + case AttributeType::Text: + Result += getULEB128Size(item.Tag); + Result += item.StringValue.size() + 1; // string + '\0' + break; + case AttributeType::NumericAndText: + Result += getULEB128Size(item.Tag); + Result += getULEB128Size(item.IntValue); + Result += item.StringValue.size() + 1; // string + '\0'; + break; + } + } + return Result; +} diff --git a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVELFStreamer.h b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVELFStreamer.h index 138df786eaf3..392c87054d43 100644 --- a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVELFStreamer.h +++ b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVELFStreamer.h @@ -15,16 +15,94 @@ namespace llvm { class RISCVTargetELFStreamer : public RISCVTargetStreamer { +private: + enum class AttributeType { Hidden, Numeric, Text, NumericAndText }; + + struct AttributeItem { + AttributeType Type; + unsigned Tag; + unsigned IntValue; + std::string StringValue; + }; + + StringRef CurrentVendor; + SmallVector<AttributeItem, 64> Contents; + + MCSection *AttributeSection = nullptr; + + AttributeItem *getAttributeItem(unsigned Attribute) { + for (size_t i = 0; i < Contents.size(); ++i) + if (Contents[i].Tag == Attribute) + return &Contents[i]; + return nullptr; + } + + void setAttributeItem(unsigned Attribute, unsigned Value, + bool OverwriteExisting) { + // Look for existing attribute item. + if (AttributeItem *Item = getAttributeItem(Attribute)) { + if (!OverwriteExisting) + return; + Item->Type = AttributeType::Numeric; + Item->IntValue = Value; + return; + } + + // Create new attribute item. + Contents.push_back({AttributeType::Numeric, Attribute, Value, ""}); + } + + void setAttributeItem(unsigned Attribute, StringRef Value, + bool OverwriteExisting) { + // Look for existing attribute item. + if (AttributeItem *Item = getAttributeItem(Attribute)) { + if (!OverwriteExisting) + return; + Item->Type = AttributeType::Text; + Item->StringValue = std::string(Value); + return; + } + + // Create new attribute item. + Contents.push_back({AttributeType::Text, Attribute, 0, std::string(Value)}); + } + + void setAttributeItems(unsigned Attribute, unsigned IntValue, + StringRef StringValue, bool OverwriteExisting) { + // Look for existing attribute item. + if (AttributeItem *Item = getAttributeItem(Attribute)) { + if (!OverwriteExisting) + return; + Item->Type = AttributeType::NumericAndText; + Item->IntValue = IntValue; + Item->StringValue = std::string(StringValue); + return; + } + + // Create new attribute item. + Contents.push_back({AttributeType::NumericAndText, Attribute, IntValue, + std::string(StringValue)}); + } + + void emitAttribute(unsigned Attribute, unsigned Value) override; + void emitTextAttribute(unsigned Attribute, StringRef String) override; + void emitIntTextAttribute(unsigned Attribute, unsigned IntValue, + StringRef StringValue) override; + void finishAttributeSection() override; + size_t calculateContentSize() const; + public: MCELFStreamer &getStreamer(); RISCVTargetELFStreamer(MCStreamer &S, const MCSubtargetInfo &STI); - virtual void emitDirectiveOptionPush(); - virtual void emitDirectiveOptionPop(); - virtual void emitDirectiveOptionRVC(); - virtual void emitDirectiveOptionNoRVC(); - virtual void emitDirectiveOptionRelax(); - virtual void emitDirectiveOptionNoRelax(); + void emitDirectiveOptionPush() override; + void emitDirectiveOptionPop() override; + void emitDirectiveOptionPIC() override; + void emitDirectiveOptionNoPIC() override; + void emitDirectiveOptionRVC() override; + void emitDirectiveOptionNoRVC() override; + void emitDirectiveOptionRelax() override; + void emitDirectiveOptionNoRelax() override; }; } #endif diff --git a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVInstPrinter.cpp b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVInstPrinter.cpp index 22bb80ae34e2..eae3e13dbe40 100644 --- a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVInstPrinter.cpp +++ b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVInstPrinter.cpp @@ -73,7 +73,7 @@ void RISCVInstPrinter::printInst(const MCInst *MI, uint64_t Address, Res = uncompressInst(UncompressedMI, *MI, MRI, STI); if (Res) NewMI = const_cast<MCInst *>(&UncompressedMI); - if (NoAliases || !printAliasInstr(NewMI, STI, O)) + if (NoAliases || !printAliasInstr(NewMI, Address, STI, O)) printInstruction(NewMI, Address, STI, O); printAnnotation(O, Annot); } @@ -150,6 +150,39 @@ void RISCVInstPrinter::printAtomicMemOp(const MCInst *MI, unsigned OpNo, return; } +void RISCVInstPrinter::printVTypeI(const MCInst *MI, unsigned OpNo, + const MCSubtargetInfo &STI, raw_ostream &O) { + unsigned Imm = MI->getOperand(OpNo).getImm(); + unsigned Sew = (Imm >> 2) & 0x7; + unsigned Lmul = Imm & 0x3; + + Lmul = 0x1 << Lmul; + Sew = 0x1 << (Sew + 3); + O << "e" << Sew << ",m" << Lmul; +} + +void RISCVInstPrinter::printVMaskReg(const MCInst *MI, unsigned OpNo, + const MCSubtargetInfo &STI, + raw_ostream &O) { + const MCOperand &MO = MI->getOperand(OpNo); + + assert(MO.isReg() && "printVMaskReg can only print register operands"); + if (MO.getReg() == RISCV::NoRegister) + return; + O << ", "; + printRegName(O, MO.getReg()); + O << ".t"; +} + +void RISCVInstPrinter::printSImm5Plus1(const MCInst *MI, unsigned OpNo, + const MCSubtargetInfo &STI, + raw_ostream &O) { + const MCOperand &MO = MI->getOperand(OpNo); + + assert(MO.isImm() && "printSImm5Plus1 can only print constant operands"); + O << MO.getImm() + 1; +} + const char *RISCVInstPrinter::getRegisterName(unsigned RegNo) { return getRegisterName(RegNo, ArchRegNames ? RISCV::NoRegAltName : RISCV::ABIRegAltName); diff --git a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVInstPrinter.h b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVInstPrinter.h index aeb2ea636060..fdaa00c5f8eb 100644 --- a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVInstPrinter.h +++ b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVInstPrinter.h @@ -17,7 +17,6 @@ #include "llvm/MC/MCInstPrinter.h" namespace llvm { -class MCOperand; class RISCVInstPrinter : public MCInstPrinter { public: @@ -41,14 +40,20 @@ public: raw_ostream &O); void printAtomicMemOp(const MCInst *MI, unsigned OpNo, const MCSubtargetInfo &STI, raw_ostream &O); + void printVTypeI(const MCInst *MI, unsigned OpNo, const MCSubtargetInfo &STI, + raw_ostream &O); + void printVMaskReg(const MCInst *MI, unsigned OpNo, + const MCSubtargetInfo &STI, raw_ostream &O); + void printSImm5Plus1(const MCInst *MI, unsigned OpNo, + const MCSubtargetInfo &STI, raw_ostream &O); // Autogenerated by tblgen. void printInstruction(const MCInst *MI, uint64_t Address, const MCSubtargetInfo &STI, raw_ostream &O); - bool printAliasInstr(const MCInst *MI, const MCSubtargetInfo &STI, - raw_ostream &O); - void printCustomAliasOperand(const MCInst *MI, unsigned OpIdx, - unsigned PrintMethodIdx, + bool printAliasInstr(const MCInst *MI, uint64_t Address, + const MCSubtargetInfo &STI, raw_ostream &O); + void printCustomAliasOperand(const MCInst *MI, uint64_t Address, + unsigned OpIdx, unsigned PrintMethodIdx, const MCSubtargetInfo &STI, raw_ostream &O); static const char *getRegisterName(unsigned RegNo); static const char *getRegisterName(unsigned RegNo, unsigned AltIdx); diff --git a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCCodeEmitter.cpp b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCCodeEmitter.cpp index de99960848a5..816206c477df 100644 --- a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCCodeEmitter.cpp +++ b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCCodeEmitter.cpp @@ -80,6 +80,10 @@ public: unsigned getImmOpValue(const MCInst &MI, unsigned OpNo, SmallVectorImpl<MCFixup> &Fixups, const MCSubtargetInfo &STI) const; + + unsigned getVMaskReg(const MCInst &MI, unsigned OpNo, + SmallVectorImpl<MCFixup> &Fixups, + const MCSubtargetInfo &STI) const; }; } // end anonymous namespace @@ -89,13 +93,14 @@ MCCodeEmitter *llvm::createRISCVMCCodeEmitter(const MCInstrInfo &MCII, return new RISCVMCCodeEmitter(Ctx, MCII); } -// Expand PseudoCALL(Reg) and PseudoTAIL to AUIPC and JALR with relocation -// types. We expand PseudoCALL(Reg) and PseudoTAIL while encoding, meaning AUIPC -// and JALR won't go through RISCV MC to MC compressed instruction -// transformation. This is acceptable because AUIPC has no 16-bit form and -// C_JALR have no immediate operand field. We let linker relaxation deal with -// it. When linker relaxation enabled, AUIPC and JALR have chance relax to JAL. -// If C extension is enabled, JAL has chance relax to C_JAL. +// Expand PseudoCALL(Reg), PseudoTAIL and PseudoJump to AUIPC and JALR with +// relocation types. We expand those pseudo-instructions while encoding them, +// meaning AUIPC and JALR won't go through RISCV MC to MC compressed +// instruction transformation. This is acceptable because AUIPC has no 16-bit +// form and C_JALR has no immediate operand field. We let linker relaxation +// deal with it. When linker relaxation is enabled, AUIPC and JALR have a +// chance to relax to JAL. +// If the C extension is enabled, JAL has a chance relax to C_JAL. void RISCVMCCodeEmitter::expandFunctionCall(const MCInst &MI, raw_ostream &OS, SmallVectorImpl<MCFixup> &Fixups, const MCSubtargetInfo &STI) const { @@ -108,9 +113,12 @@ void RISCVMCCodeEmitter::expandFunctionCall(const MCInst &MI, raw_ostream &OS, } else if (MI.getOpcode() == RISCV::PseudoCALLReg) { Func = MI.getOperand(1); Ra = MI.getOperand(0).getReg(); - } else { + } else if (MI.getOpcode() == RISCV::PseudoCALL) { Func = MI.getOperand(0); Ra = RISCV::X1; + } else if (MI.getOpcode() == RISCV::PseudoJump) { + Func = MI.getOperand(1); + Ra = MI.getOperand(0).getReg(); } uint32_t Binary; @@ -125,8 +133,9 @@ void RISCVMCCodeEmitter::expandFunctionCall(const MCInst &MI, raw_ostream &OS, Binary = getBinaryCodeForInstr(TmpInst, Fixups, STI); support::endian::write(OS, Binary, support::little); - if (MI.getOpcode() == RISCV::PseudoTAIL) - // Emit JALR X0, X6, 0 + if (MI.getOpcode() == RISCV::PseudoTAIL || + MI.getOpcode() == RISCV::PseudoJump) + // Emit JALR X0, Ra, 0 TmpInst = MCInstBuilder(RISCV::JALR).addReg(RISCV::X0).addReg(Ra).addImm(0); else // Emit JALR Ra, Ra, 0 @@ -180,9 +189,13 @@ void RISCVMCCodeEmitter::encodeInstruction(const MCInst &MI, raw_ostream &OS, // Get byte count of instruction. unsigned Size = Desc.getSize(); + // RISCVInstrInfo::getInstSizeInBytes hard-codes the number of expanded + // instructions for each pseudo, and must be updated when adding new pseudos + // or changing existing ones. if (MI.getOpcode() == RISCV::PseudoCALLReg || MI.getOpcode() == RISCV::PseudoCALL || - MI.getOpcode() == RISCV::PseudoTAIL) { + MI.getOpcode() == RISCV::PseudoTAIL || + MI.getOpcode() == RISCV::PseudoJump) { expandFunctionCall(MI, OS, Fixups, STI); MCNumEmitted += 2; return; @@ -368,4 +381,20 @@ unsigned RISCVMCCodeEmitter::getImmOpValue(const MCInst &MI, unsigned OpNo, return 0; } +unsigned RISCVMCCodeEmitter::getVMaskReg(const MCInst &MI, unsigned OpNo, + SmallVectorImpl<MCFixup> &Fixups, + const MCSubtargetInfo &STI) const { + MCOperand MO = MI.getOperand(OpNo); + assert(MO.isReg() && "Expected a register."); + + switch (MO.getReg()) { + default: + llvm_unreachable("Invalid mask register."); + case RISCV::V0: + return 0; + case RISCV::NoRegister: + return 1; + } +} + #include "RISCVGenMCCodeEmitter.inc" diff --git a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCExpr.cpp b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCExpr.cpp index 7aa9b5e7d683..2a6f372e50be 100644 --- a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCExpr.cpp +++ b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCExpr.cpp @@ -47,7 +47,7 @@ void RISCVMCExpr::printImpl(raw_ostream &OS, const MCAsmInfo *MAI) const { OS << ')'; } -const MCFixup *RISCVMCExpr::getPCRelHiFixup() const { +const MCFixup *RISCVMCExpr::getPCRelHiFixup(const MCFragment **DFOut) const { MCValue AUIPCLoc; if (!getSubExpr()->evaluateAsRelocatable(AUIPCLoc, nullptr, nullptr)) return nullptr; @@ -81,6 +81,8 @@ const MCFixup *RISCVMCExpr::getPCRelHiFixup() const { case RISCV::fixup_riscv_tls_got_hi20: case RISCV::fixup_riscv_tls_gd_hi20: case RISCV::fixup_riscv_pcrel_hi20: + if (DFOut) + *DFOut = DF; return &F; } } @@ -88,74 +90,9 @@ const MCFixup *RISCVMCExpr::getPCRelHiFixup() const { return nullptr; } -bool RISCVMCExpr::evaluatePCRelLo(MCValue &Res, const MCAsmLayout *Layout, - const MCFixup *Fixup) const { - // VK_RISCV_PCREL_LO has to be handled specially. The MCExpr inside is - // actually the location of a auipc instruction with a VK_RISCV_PCREL_HI fixup - // pointing to the real target. We need to generate an MCValue in the form of - // (<real target> + <offset from this fixup to the auipc fixup>). The Fixup - // is pcrel relative to the VK_RISCV_PCREL_LO fixup, so we need to add the - // offset to the VK_RISCV_PCREL_HI Fixup from VK_RISCV_PCREL_LO to correct. - - // Don't try to evaluate if the fixup will be forced as a relocation (e.g. - // as linker relaxation is enabled). If we evaluated pcrel_lo in this case, - // the modified fixup will be converted into a relocation that no longer - // points to the pcrel_hi as the linker requires. - auto &RAB = - static_cast<RISCVAsmBackend &>(Layout->getAssembler().getBackend()); - if (RAB.willForceRelocations()) - return false; - - MCValue AUIPCLoc; - if (!getSubExpr()->evaluateAsValue(AUIPCLoc, *Layout)) - return false; - - const MCSymbolRefExpr *AUIPCSRE = AUIPCLoc.getSymA(); - // Don't try to evaluate %pcrel_hi/%pcrel_lo pairs that cross fragment - // boundries. - if (!AUIPCSRE || - findAssociatedFragment() != AUIPCSRE->findAssociatedFragment()) - return false; - - const MCSymbol *AUIPCSymbol = &AUIPCSRE->getSymbol(); - if (!AUIPCSymbol) - return false; - - const MCFixup *TargetFixup = getPCRelHiFixup(); - if (!TargetFixup) - return false; - - if ((unsigned)TargetFixup->getKind() != RISCV::fixup_riscv_pcrel_hi20) - return false; - - MCValue Target; - if (!TargetFixup->getValue()->evaluateAsValue(Target, *Layout)) - return false; - - if (!Target.getSymA() || !Target.getSymA()->getSymbol().isInSection()) - return false; - - if (&Target.getSymA()->getSymbol().getSection() != - findAssociatedFragment()->getParent()) - return false; - - // We must use TargetFixup rather than AUIPCSymbol here. They will almost - // always have the same offset, except for the case when AUIPCSymbol is at - // the end of a fragment and the fixup comes from offset 0 in the next - // fragment. - uint64_t AUIPCOffset = TargetFixup->getOffset(); - - Res = MCValue::get(Target.getSymA(), nullptr, - Target.getConstant() + (Fixup->getOffset() - AUIPCOffset)); - return true; -} - bool RISCVMCExpr::evaluateAsRelocatableImpl(MCValue &Res, const MCAsmLayout *Layout, const MCFixup *Fixup) const { - if (Kind == VK_RISCV_PCREL_LO && evaluatePCRelLo(Res, Layout, Fixup)) - return true; - if (!getSubExpr()->evaluateAsRelocatable(Res, Layout, Fixup)) return false; diff --git a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCExpr.h b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCExpr.h index 921df376f3df..77038cee4e9d 100644 --- a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCExpr.h +++ b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCExpr.h @@ -19,7 +19,7 @@ namespace llvm { class StringRef; -class MCOperand; + class RISCVMCExpr : public MCTargetExpr { public: enum VariantKind { @@ -46,9 +46,6 @@ private: int64_t evaluateAsInt64(int64_t Value) const; - bool evaluatePCRelLo(MCValue &Res, const MCAsmLayout *Layout, - const MCFixup *Fixup) const; - explicit RISCVMCExpr(const MCExpr *Expr, VariantKind Kind) : Expr(Expr), Kind(Kind) {} @@ -61,11 +58,11 @@ public: const MCExpr *getSubExpr() const { return Expr; } /// Get the corresponding PC-relative HI fixup that a VK_RISCV_PCREL_LO - /// points to. + /// points to, and optionally the fragment containing it. /// /// \returns nullptr if this isn't a VK_RISCV_PCREL_LO pointing to a /// known PC-relative HI fixup. - const MCFixup *getPCRelHiFixup() const; + const MCFixup *getPCRelHiFixup(const MCFragment **DFOut) const; void printImpl(raw_ostream &OS, const MCAsmInfo *MAI) const override; bool evaluateAsRelocatableImpl(MCValue &Res, const MCAsmLayout *Layout, diff --git a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCTargetDesc.cpp b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCTargetDesc.cpp index c37482be3c2c..a474224e1a4e 100644 --- a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCTargetDesc.cpp +++ b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCTargetDesc.cpp @@ -20,6 +20,7 @@ #include "llvm/ADT/STLExtras.h" #include "llvm/CodeGen/Register.h" #include "llvm/MC/MCAsmInfo.h" +#include "llvm/MC/MCInstrAnalysis.h" #include "llvm/MC/MCInstrInfo.h" #include "llvm/MC/MCRegisterInfo.h" #include "llvm/MC/MCStreamer.h" @@ -56,7 +57,7 @@ static MCAsmInfo *createRISCVMCAsmInfo(const MCRegisterInfo &MRI, MCAsmInfo *MAI = new RISCVMCAsmInfo(TT); Register SP = MRI.getDwarfRegNum(RISCV::X2, true); - MCCFIInstruction Inst = MCCFIInstruction::createDefCfa(nullptr, SP, 0); + MCCFIInstruction Inst = MCCFIInstruction::cfiDefCfa(nullptr, SP, 0); MAI->addInitialFrameState(Inst); return MAI; @@ -64,7 +65,7 @@ static MCAsmInfo *createRISCVMCAsmInfo(const MCRegisterInfo &MRI, static MCSubtargetInfo *createRISCVMCSubtargetInfo(const Triple &TT, StringRef CPU, StringRef FS) { - std::string CPUName = CPU; + std::string CPUName = std::string(CPU); if (CPUName.empty()) CPUName = TT.isArch64Bit() ? "generic-rv64" : "generic-rv32"; return createRISCVMCSubtargetInfoImpl(TT, CPUName, FS); @@ -93,6 +94,49 @@ static MCTargetStreamer *createRISCVAsmTargetStreamer(MCStreamer &S, return new RISCVTargetAsmStreamer(S, OS); } +static MCTargetStreamer *createRISCVNullTargetStreamer(MCStreamer &S) { + return new RISCVTargetStreamer(S); +} + +namespace { + +class RISCVMCInstrAnalysis : public MCInstrAnalysis { +public: + explicit RISCVMCInstrAnalysis(const MCInstrInfo *Info) + : MCInstrAnalysis(Info) {} + + bool evaluateBranch(const MCInst &Inst, uint64_t Addr, uint64_t Size, + uint64_t &Target) const override { + if (isConditionalBranch(Inst)) { + int64_t Imm; + if (Size == 2) + Imm = Inst.getOperand(1).getImm(); + else + Imm = Inst.getOperand(2).getImm(); + Target = Addr + Imm; + return true; + } + + if (Inst.getOpcode() == RISCV::C_JAL || Inst.getOpcode() == RISCV::C_J) { + Target = Addr + Inst.getOperand(0).getImm(); + return true; + } + + if (Inst.getOpcode() == RISCV::JAL) { + Target = Addr + Inst.getOperand(1).getImm(); + return true; + } + + return false; + } +}; + +} // end anonymous namespace + +static MCInstrAnalysis *createRISCVInstrAnalysis(const MCInstrInfo *Info) { + return new RISCVMCInstrAnalysis(Info); +} + extern "C" LLVM_EXTERNAL_VISIBILITY void LLVMInitializeRISCVTargetMC() { for (Target *T : {&getTheRISCV32Target(), &getTheRISCV64Target()}) { TargetRegistry::RegisterMCAsmInfo(*T, createRISCVMCAsmInfo); @@ -104,8 +148,12 @@ extern "C" LLVM_EXTERNAL_VISIBILITY void LLVMInitializeRISCVTargetMC() { TargetRegistry::RegisterMCSubtargetInfo(*T, createRISCVMCSubtargetInfo); TargetRegistry::RegisterObjectTargetStreamer( *T, createRISCVObjectTargetStreamer); + TargetRegistry::RegisterMCInstrAnalysis(*T, createRISCVInstrAnalysis); // Register the asm target streamer. TargetRegistry::RegisterAsmTargetStreamer(*T, createRISCVAsmTargetStreamer); + // Register the null target streamer. + TargetRegistry::RegisterNullTargetStreamer(*T, + createRISCVNullTargetStreamer); } } diff --git a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCTargetDesc.h b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCTargetDesc.h index b30997533ddf..5216a689715a 100644 --- a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCTargetDesc.h +++ b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCTargetDesc.h @@ -26,11 +26,7 @@ class MCInstrInfo; class MCObjectTargetWriter; class MCRegisterInfo; class MCSubtargetInfo; -class StringRef; class Target; -class Triple; -class raw_ostream; -class raw_pwrite_stream; MCCodeEmitter *createRISCVMCCodeEmitter(const MCInstrInfo &MCII, const MCRegisterInfo &MRI, diff --git a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVTargetStreamer.cpp b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVTargetStreamer.cpp index 913e1f744192..54a2fb288579 100644 --- a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVTargetStreamer.cpp +++ b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVTargetStreamer.cpp @@ -11,12 +11,59 @@ //===----------------------------------------------------------------------===// #include "RISCVTargetStreamer.h" +#include "RISCVSubtarget.h" #include "llvm/Support/FormattedStream.h" +#include "llvm/Support/RISCVAttributes.h" using namespace llvm; RISCVTargetStreamer::RISCVTargetStreamer(MCStreamer &S) : MCTargetStreamer(S) {} +void RISCVTargetStreamer::finish() { finishAttributeSection(); } + +void RISCVTargetStreamer::emitDirectiveOptionPush() {} +void RISCVTargetStreamer::emitDirectiveOptionPop() {} +void RISCVTargetStreamer::emitDirectiveOptionPIC() {} +void RISCVTargetStreamer::emitDirectiveOptionNoPIC() {} +void RISCVTargetStreamer::emitDirectiveOptionRVC() {} +void RISCVTargetStreamer::emitDirectiveOptionNoRVC() {} +void RISCVTargetStreamer::emitDirectiveOptionRelax() {} +void RISCVTargetStreamer::emitDirectiveOptionNoRelax() {} +void RISCVTargetStreamer::emitAttribute(unsigned Attribute, unsigned Value) {} +void RISCVTargetStreamer::finishAttributeSection() {} +void RISCVTargetStreamer::emitTextAttribute(unsigned Attribute, + StringRef String) {} +void RISCVTargetStreamer::emitIntTextAttribute(unsigned Attribute, + unsigned IntValue, + StringRef StringValue) {} + +void RISCVTargetStreamer::emitTargetAttributes(const MCSubtargetInfo &STI) { + if (STI.hasFeature(RISCV::FeatureRV32E)) + emitAttribute(RISCVAttrs::STACK_ALIGN, RISCVAttrs::ALIGN_4); + else + emitAttribute(RISCVAttrs::STACK_ALIGN, RISCVAttrs::ALIGN_16); + + std::string Arch = "rv32"; + if (STI.hasFeature(RISCV::Feature64Bit)) + Arch = "rv64"; + if (STI.hasFeature(RISCV::FeatureRV32E)) + Arch += "e1p9"; + else + Arch += "i2p0"; + if (STI.hasFeature(RISCV::FeatureStdExtM)) + Arch += "_m2p0"; + if (STI.hasFeature(RISCV::FeatureStdExtA)) + Arch += "_a2p0"; + if (STI.hasFeature(RISCV::FeatureStdExtF)) + Arch += "_f2p0"; + if (STI.hasFeature(RISCV::FeatureStdExtD)) + Arch += "_d2p0"; + if (STI.hasFeature(RISCV::FeatureStdExtC)) + Arch += "_c2p0"; + + emitTextAttribute(RISCVAttrs::ARCH, Arch); +} + // This part is for ascii assembly output RISCVTargetAsmStreamer::RISCVTargetAsmStreamer(MCStreamer &S, formatted_raw_ostream &OS) @@ -30,6 +77,14 @@ void RISCVTargetAsmStreamer::emitDirectiveOptionPop() { OS << "\t.option\tpop\n"; } +void RISCVTargetAsmStreamer::emitDirectiveOptionPIC() { + OS << "\t.option\tpic\n"; +} + +void RISCVTargetAsmStreamer::emitDirectiveOptionNoPIC() { + OS << "\t.option\tnopic\n"; +} + void RISCVTargetAsmStreamer::emitDirectiveOptionRVC() { OS << "\t.option\trvc\n"; } @@ -45,3 +100,18 @@ void RISCVTargetAsmStreamer::emitDirectiveOptionRelax() { void RISCVTargetAsmStreamer::emitDirectiveOptionNoRelax() { OS << "\t.option\tnorelax\n"; } + +void RISCVTargetAsmStreamer::emitAttribute(unsigned Attribute, unsigned Value) { + OS << "\t.attribute\t" << Attribute << ", " << Twine(Value) << "\n"; +} + +void RISCVTargetAsmStreamer::emitTextAttribute(unsigned Attribute, + StringRef String) { + OS << "\t.attribute\t" << Attribute << ", \"" << String << "\"\n"; +} + +void RISCVTargetAsmStreamer::emitIntTextAttribute(unsigned Attribute, + unsigned IntValue, + StringRef StringValue) {} + +void RISCVTargetAsmStreamer::finishAttributeSection() {} diff --git a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVTargetStreamer.h b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVTargetStreamer.h index 1becc134b2a2..32fa20f25d82 100644 --- a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVTargetStreamer.h +++ b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVTargetStreamer.h @@ -10,30 +10,49 @@ #define LLVM_LIB_TARGET_RISCV_RISCVTARGETSTREAMER_H #include "llvm/MC/MCStreamer.h" +#include "llvm/MC/MCSubtargetInfo.h" namespace llvm { class RISCVTargetStreamer : public MCTargetStreamer { public: RISCVTargetStreamer(MCStreamer &S); - - virtual void emitDirectiveOptionPush() = 0; - virtual void emitDirectiveOptionPop() = 0; - virtual void emitDirectiveOptionRVC() = 0; - virtual void emitDirectiveOptionNoRVC() = 0; - virtual void emitDirectiveOptionRelax() = 0; - virtual void emitDirectiveOptionNoRelax() = 0; + void finish() override; + + virtual void emitDirectiveOptionPush(); + virtual void emitDirectiveOptionPop(); + virtual void emitDirectiveOptionPIC(); + virtual void emitDirectiveOptionNoPIC(); + virtual void emitDirectiveOptionRVC(); + virtual void emitDirectiveOptionNoRVC(); + virtual void emitDirectiveOptionRelax(); + virtual void emitDirectiveOptionNoRelax(); + virtual void emitAttribute(unsigned Attribute, unsigned Value); + virtual void finishAttributeSection(); + virtual void emitTextAttribute(unsigned Attribute, StringRef String); + virtual void emitIntTextAttribute(unsigned Attribute, unsigned IntValue, + StringRef StringValue); + + void emitTargetAttributes(const MCSubtargetInfo &STI); }; // This part is for ascii assembly output class RISCVTargetAsmStreamer : public RISCVTargetStreamer { formatted_raw_ostream &OS; + void finishAttributeSection() override; + void emitAttribute(unsigned Attribute, unsigned Value) override; + void emitTextAttribute(unsigned Attribute, StringRef String) override; + void emitIntTextAttribute(unsigned Attribute, unsigned IntValue, + StringRef StringValue) override; + public: RISCVTargetAsmStreamer(MCStreamer &S, formatted_raw_ostream &OS); void emitDirectiveOptionPush() override; void emitDirectiveOptionPop() override; + void emitDirectiveOptionPIC() override; + void emitDirectiveOptionNoPIC() override; void emitDirectiveOptionRVC() override; void emitDirectiveOptionNoRVC() override; void emitDirectiveOptionRelax() override; diff --git a/llvm/lib/Target/RISCV/RISCV.h b/llvm/lib/Target/RISCV/RISCV.h index f23f742a4782..9baa2cc2741a 100644 --- a/llvm/lib/Target/RISCV/RISCV.h +++ b/llvm/lib/Target/RISCV/RISCV.h @@ -43,6 +43,9 @@ void initializeRISCVMergeBaseOffsetOptPass(PassRegistry &); FunctionPass *createRISCVExpandPseudoPass(); void initializeRISCVExpandPseudoPass(PassRegistry &); +FunctionPass *createRISCVExpandAtomicPseudoPass(); +void initializeRISCVExpandAtomicPseudoPass(PassRegistry &); + InstructionSelector *createRISCVInstructionSelector(const RISCVTargetMachine &, RISCVSubtarget &, RISCVRegisterBankInfo &); diff --git a/llvm/lib/Target/RISCV/RISCV.td b/llvm/lib/Target/RISCV/RISCV.td index 82afa13aece3..f0583f691936 100644 --- a/llvm/lib/Target/RISCV/RISCV.td +++ b/llvm/lib/Target/RISCV/RISCV.td @@ -16,21 +16,21 @@ def FeatureStdExtM : SubtargetFeature<"m", "HasStdExtM", "true", "'M' (Integer Multiplication and Division)">; def HasStdExtM : Predicate<"Subtarget->hasStdExtM()">, - AssemblerPredicate<"FeatureStdExtM", + AssemblerPredicate<(all_of FeatureStdExtM), "'M' (Integer Multiplication and Division)">; def FeatureStdExtA : SubtargetFeature<"a", "HasStdExtA", "true", "'A' (Atomic Instructions)">; def HasStdExtA : Predicate<"Subtarget->hasStdExtA()">, - AssemblerPredicate<"FeatureStdExtA", + AssemblerPredicate<(all_of FeatureStdExtA), "'A' (Atomic Instructions)">; def FeatureStdExtF : SubtargetFeature<"f", "HasStdExtF", "true", "'F' (Single-Precision Floating-Point)">; def HasStdExtF : Predicate<"Subtarget->hasStdExtF()">, - AssemblerPredicate<"FeatureStdExtF", + AssemblerPredicate<(all_of FeatureStdExtF), "'F' (Single-Precision Floating-Point)">; def FeatureStdExtD @@ -38,30 +38,130 @@ def FeatureStdExtD "'D' (Double-Precision Floating-Point)", [FeatureStdExtF]>; def HasStdExtD : Predicate<"Subtarget->hasStdExtD()">, - AssemblerPredicate<"FeatureStdExtD", + AssemblerPredicate<(all_of FeatureStdExtD), "'D' (Double-Precision Floating-Point)">; def FeatureStdExtC : SubtargetFeature<"c", "HasStdExtC", "true", "'C' (Compressed Instructions)">; def HasStdExtC : Predicate<"Subtarget->hasStdExtC()">, - AssemblerPredicate<"FeatureStdExtC", + AssemblerPredicate<(all_of FeatureStdExtC), "'C' (Compressed Instructions)">; -def FeatureRVCHints - : SubtargetFeature<"rvc-hints", "EnableRVCHintInstrs", "true", - "Enable RVC Hint Instructions.">; +def FeatureExtZbb + : SubtargetFeature<"experimental-zbb", "HasStdExtZbb", "true", + "'Zbb' (Base 'B' Instructions)">; +def HasStdExtZbb : Predicate<"Subtarget->hasStdExtZbb()">, + AssemblerPredicate<(all_of FeatureExtZbb), + "'Zbb' (Base 'B' Instructions)">; + +def FeatureExtZbc + : SubtargetFeature<"experimental-zbc", "HasStdExtZbc", "true", + "'Zbc' (Carry-Less 'B' Instructions)">; +def HasStdExtZbc : Predicate<"Subtarget->hasStdExtZbc()">, + AssemblerPredicate<(all_of FeatureExtZbc), + "'Zbc' (Carry-Less 'B' Instructions)">; + +def FeatureExtZbe + : SubtargetFeature<"experimental-zbe", "HasStdExtZbe", "true", + "'Zbe' (Extract-Deposit 'B' Instructions)">; +def HasStdExtZbe : Predicate<"Subtarget->hasStdExtZbe()">, + AssemblerPredicate<(all_of FeatureExtZbe), + "'Zbe' (Extract-Deposit 'B' Instructions)">; + +def FeatureExtZbf + : SubtargetFeature<"experimental-zbf", "HasStdExtZbf", "true", + "'Zbf' (Bit-Field 'B' Instructions)">; +def HasStdExtZbf : Predicate<"Subtarget->hasStdExtZbf()">, + AssemblerPredicate<(all_of FeatureExtZbf), + "'Zbf' (Bit-Field 'B' Instructions)">; + +def FeatureExtZbm + : SubtargetFeature<"experimental-zbm", "HasStdExtZbm", "true", + "'Zbm' (Matrix 'B' Instructions)">; +def HasStdExtZbm : Predicate<"Subtarget->hasStdExtZbm()">, + AssemblerPredicate<(all_of FeatureExtZbm), + "'Zbm' (Matrix 'B' Instructions)">; + +def FeatureExtZbp + : SubtargetFeature<"experimental-zbp", "HasStdExtZbp", "true", + "'Zbp' (Permutation 'B' Instructions)">; +def HasStdExtZbp : Predicate<"Subtarget->hasStdExtZbp()">, + AssemblerPredicate<(all_of FeatureExtZbp), + "'Zbp' (Permutation 'B' Instructions)">; + +def FeatureExtZbr + : SubtargetFeature<"experimental-zbr", "HasStdExtZbr", "true", + "'Zbr' (Polynomial Reduction 'B' Instructions)">; +def HasStdExtZbr : Predicate<"Subtarget->hasStdExtZbr()">, + AssemblerPredicate<(all_of FeatureExtZbr), + "'Zbr' (Polynomial Reduction 'B' Instructions)">; + +def FeatureExtZbs + : SubtargetFeature<"experimental-zbs", "HasStdExtZbs", "true", + "'Zbs' (Single-Bit 'B' Instructions)">; +def HasStdExtZbs : Predicate<"Subtarget->hasStdExtZbs()">, + AssemblerPredicate<(all_of FeatureExtZbs), + "'Zbs' (Single-Bit 'B' Instructions)">; + +def FeatureExtZbt + : SubtargetFeature<"experimental-zbt", "HasStdExtZbt", "true", + "'Zbt' (Ternary 'B' Instructions)">; +def HasStdExtZbt : Predicate<"Subtarget->hasStdExtZbt()">, + AssemblerPredicate<(all_of FeatureExtZbt), + "'Zbt' (Ternary 'B' Instructions)">; + +// Some instructions belong to both the basic and the permutation +// subextensions. They should be enabled if either has been specified. +def HasStdExtZbbOrZbp + : Predicate<"Subtarget->hasStdExtZbb() || Subtarget->hasStdExtZbp()">, + AssemblerPredicate<(any_of FeatureExtZbb, FeatureExtZbp)>; + +def FeatureExtZbproposedc + : SubtargetFeature<"experimental-zbproposedc", "HasStdExtZbproposedc", "true", + "'Zbproposedc' (Proposed Compressed 'B' Instructions)">; +def HasStdExtZbproposedc : Predicate<"Subtarget->hasStdExtZbproposedc()">, + AssemblerPredicate<(all_of FeatureExtZbproposedc), + "'Zbproposedc' (Proposed Compressed 'B' Instructions)">; + +def FeatureStdExtB + : SubtargetFeature<"experimental-b", "HasStdExtB", "true", + "'B' (Bit Manipulation Instructions)", + [FeatureExtZbb, + FeatureExtZbc, + FeatureExtZbe, + FeatureExtZbf, + FeatureExtZbm, + FeatureExtZbp, + FeatureExtZbr, + FeatureExtZbs, + FeatureExtZbt]>; +def HasStdExtB : Predicate<"Subtarget->hasStdExtB()">, + AssemblerPredicate<(all_of FeatureStdExtB), + "'B' (Bit Manipulation Instructions)">; + +def FeatureNoRVCHints + : SubtargetFeature<"no-rvc-hints", "EnableRVCHintInstrs", "false", + "Disable RVC Hint Instructions.">; def HasRVCHints : Predicate<"Subtarget->enableRVCHintInstrs()">, - AssemblerPredicate<"FeatureRVCHints", - "RVC Hint Instructions">; + AssemblerPredicate<(all_of(not FeatureNoRVCHints)), + "RVC Hint Instructions">; + +def FeatureStdExtV + : SubtargetFeature<"experimental-v", "HasStdExtV", "true", + "'V' (Vector Instructions)", + [FeatureStdExtF]>; +def HasStdExtV : Predicate<"Subtarget->hasStdExtV()">, + AssemblerPredicate<(all_of FeatureStdExtV), + "'V' (Vector Instructions)">; def Feature64Bit : SubtargetFeature<"64bit", "HasRV64", "true", "Implements RV64">; def IsRV64 : Predicate<"Subtarget->is64Bit()">, - AssemblerPredicate<"Feature64Bit", + AssemblerPredicate<(all_of Feature64Bit), "RV64I Base Instruction Set">; def IsRV32 : Predicate<"!Subtarget->is64Bit()">, - AssemblerPredicate<"!Feature64Bit", + AssemblerPredicate<(all_of (not Feature64Bit)), "RV32I Base Instruction Set">; def RV64 : HwMode<"+64bit">; @@ -71,7 +171,7 @@ def FeatureRV32E : SubtargetFeature<"e", "IsRV32E", "true", "Implements RV32E (provides 16 rather than 32 GPRs)">; def IsRV32E : Predicate<"Subtarget->isRV32E()">, - AssemblerPredicate<"FeatureRV32E">; + AssemblerPredicate<(all_of FeatureRV32E)>; def FeatureRelax : SubtargetFeature<"relax", "EnableLinkerRelax", "true", @@ -82,6 +182,9 @@ foreach i = {1-31} in SubtargetFeature<"reserve-x"#i, "UserReservedRegister[RISCV::X"#i#"]", "true", "Reserve X"#i>; +def FeatureSaveRestore : SubtargetFeature<"save-restore", "EnableSaveRestore", + "true", "Enable save/restore.">; + //===----------------------------------------------------------------------===// // Named operands for CSR instructions. //===----------------------------------------------------------------------===// @@ -92,19 +195,26 @@ include "RISCVSystemOperands.td" // Registers, calling conventions, instruction descriptions. //===----------------------------------------------------------------------===// +include "RISCVSchedule.td" include "RISCVRegisterInfo.td" include "RISCVCallingConv.td" include "RISCVInstrInfo.td" include "RISCVRegisterBanks.td" +include "RISCVSchedRocket32.td" +include "RISCVSchedRocket64.td" //===----------------------------------------------------------------------===// // RISC-V processors supported. //===----------------------------------------------------------------------===// -def : ProcessorModel<"generic-rv32", NoSchedModel, [FeatureRVCHints]>; +def : ProcessorModel<"generic-rv32", NoSchedModel, []>; + +def : ProcessorModel<"generic-rv64", NoSchedModel, [Feature64Bit]>; + +def : ProcessorModel<"rocket-rv32", Rocket32Model, []>; + +def : ProcessorModel<"rocket-rv64", Rocket64Model, [Feature64Bit]>; -def : ProcessorModel<"generic-rv64", NoSchedModel, [Feature64Bit, - FeatureRVCHints]>; //===----------------------------------------------------------------------===// // Define the RISC-V target. diff --git a/llvm/lib/Target/RISCV/RISCVAsmPrinter.cpp b/llvm/lib/Target/RISCV/RISCVAsmPrinter.cpp index f4aa28bcc0c1..8955994b1c2e 100644 --- a/llvm/lib/Target/RISCV/RISCVAsmPrinter.cpp +++ b/llvm/lib/Target/RISCV/RISCVAsmPrinter.cpp @@ -11,9 +11,10 @@ // //===----------------------------------------------------------------------===// -#include "RISCV.h" #include "MCTargetDesc/RISCVInstPrinter.h" #include "MCTargetDesc/RISCVMCExpr.h" +#include "MCTargetDesc/RISCVTargetStreamer.h" +#include "RISCV.h" #include "RISCVTargetMachine.h" #include "TargetInfo/RISCVTargetInfo.h" #include "llvm/ADT/Statistic.h" @@ -37,14 +38,18 @@ STATISTIC(RISCVNumInstrsCompressed, namespace { class RISCVAsmPrinter : public AsmPrinter { + const MCSubtargetInfo *STI; + public: explicit RISCVAsmPrinter(TargetMachine &TM, std::unique_ptr<MCStreamer> Streamer) - : AsmPrinter(TM, std::move(Streamer)) {} + : AsmPrinter(TM, std::move(Streamer)), STI(TM.getMCSubtargetInfo()) {} StringRef getPassName() const override { return "RISCV Assembly Printer"; } - void EmitInstruction(const MachineInstr *MI) override; + bool runOnMachineFunction(MachineFunction &MF) override; + + void emitInstruction(const MachineInstr *MI) override; bool PrintAsmOperand(const MachineInstr *MI, unsigned OpNo, const char *ExtraCode, raw_ostream &OS) override; @@ -59,6 +64,12 @@ public: bool lowerOperand(const MachineOperand &MO, MCOperand &MCOp) const { return LowerRISCVMachineOperandToMCOperand(MO, MCOp, *this); } + + void emitStartOfAsmFile(Module &M) override; + void emitEndOfAsmFile(Module &M) override; + +private: + void emitAttributes(); }; } @@ -66,8 +77,7 @@ public: #include "RISCVGenCompressInstEmitter.inc" void RISCVAsmPrinter::EmitToStreamer(MCStreamer &S, const MCInst &Inst) { MCInst CInst; - bool Res = compressInst(CInst, Inst, *TM.getMCSubtargetInfo(), - OutStreamer->getContext()); + bool Res = compressInst(CInst, Inst, *STI, OutStreamer->getContext()); if (Res) ++RISCVNumInstrsCompressed; AsmPrinter::EmitToStreamer(*OutStreamer, Res ? CInst : Inst); @@ -77,7 +87,7 @@ void RISCVAsmPrinter::EmitToStreamer(MCStreamer &S, const MCInst &Inst) { // instructions) auto-generated. #include "RISCVGenMCPseudoLowering.inc" -void RISCVAsmPrinter::EmitInstruction(const MachineInstr *MI) { +void RISCVAsmPrinter::emitInstruction(const MachineInstr *MI) { // Do any auto-generated pseudo lowerings. if (emitPseudoExpansionLowering(*OutStreamer, MI)) return; @@ -154,6 +164,45 @@ bool RISCVAsmPrinter::PrintAsmMemoryOperand(const MachineInstr *MI, return AsmPrinter::PrintAsmMemoryOperand(MI, OpNo, ExtraCode, OS); } +bool RISCVAsmPrinter::runOnMachineFunction(MachineFunction &MF) { + // Set the current MCSubtargetInfo to a copy which has the correct + // feature bits for the current MachineFunction + MCSubtargetInfo &NewSTI = + OutStreamer->getContext().getSubtargetCopy(*TM.getMCSubtargetInfo()); + NewSTI.setFeatureBits(MF.getSubtarget().getFeatureBits()); + STI = &NewSTI; + + SetupMachineFunction(MF); + emitFunctionBody(); + return false; +} + +void RISCVAsmPrinter::emitStartOfAsmFile(Module &M) { + if (TM.getTargetTriple().isOSBinFormatELF()) + emitAttributes(); +} + +void RISCVAsmPrinter::emitEndOfAsmFile(Module &M) { + RISCVTargetStreamer &RTS = + static_cast<RISCVTargetStreamer &>(*OutStreamer->getTargetStreamer()); + + if (TM.getTargetTriple().isOSBinFormatELF()) + RTS.finishAttributeSection(); +} + +void RISCVAsmPrinter::emitAttributes() { + RISCVTargetStreamer &RTS = + static_cast<RISCVTargetStreamer &>(*OutStreamer->getTargetStreamer()); + + const Triple &TT = TM.getTargetTriple(); + StringRef CPU = TM.getTargetCPU(); + StringRef FS = TM.getTargetFeatureString(); + const RISCVTargetMachine &RTM = static_cast<const RISCVTargetMachine &>(TM); + const RISCVSubtarget STI(TT, CPU, FS, /*ABIName=*/"", RTM); + + RTS.emitTargetAttributes(STI); +} + // Force static initialization. extern "C" LLVM_EXTERNAL_VISIBILITY void LLVMInitializeRISCVAsmPrinter() { RegisterAsmPrinter<RISCVAsmPrinter> X(getTheRISCV32Target()); diff --git a/llvm/lib/Target/RISCV/RISCVExpandAtomicPseudoInsts.cpp b/llvm/lib/Target/RISCV/RISCVExpandAtomicPseudoInsts.cpp new file mode 100644 index 000000000000..26ce16486bd9 --- /dev/null +++ b/llvm/lib/Target/RISCV/RISCVExpandAtomicPseudoInsts.cpp @@ -0,0 +1,618 @@ +//===-- RISCVExpandAtomicPseudoInsts.cpp - Expand atomic pseudo instrs. ---===// +// +// 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 a pass that expands atomic pseudo instructions into +// target instructions. This pass should be run at the last possible moment, +// avoiding the possibility for other passes to break the requirements for +// forward progress in the LR/SC block. +// +//===----------------------------------------------------------------------===// + +#include "RISCV.h" +#include "RISCVInstrInfo.h" +#include "RISCVTargetMachine.h" + +#include "llvm/CodeGen/LivePhysRegs.h" +#include "llvm/CodeGen/MachineFunctionPass.h" +#include "llvm/CodeGen/MachineInstrBuilder.h" + +using namespace llvm; + +#define RISCV_EXPAND_ATOMIC_PSEUDO_NAME \ + "RISCV atomic pseudo instruction expansion pass" + +namespace { + +class RISCVExpandAtomicPseudo : public MachineFunctionPass { +public: + const RISCVInstrInfo *TII; + static char ID; + + RISCVExpandAtomicPseudo() : MachineFunctionPass(ID) { + initializeRISCVExpandAtomicPseudoPass(*PassRegistry::getPassRegistry()); + } + + bool runOnMachineFunction(MachineFunction &MF) override; + + StringRef getPassName() const override { + return RISCV_EXPAND_ATOMIC_PSEUDO_NAME; + } + +private: + bool expandMBB(MachineBasicBlock &MBB); + bool expandMI(MachineBasicBlock &MBB, MachineBasicBlock::iterator MBBI, + MachineBasicBlock::iterator &NextMBBI); + bool expandAtomicBinOp(MachineBasicBlock &MBB, + MachineBasicBlock::iterator MBBI, AtomicRMWInst::BinOp, + bool IsMasked, int Width, + MachineBasicBlock::iterator &NextMBBI); + bool expandAtomicMinMaxOp(MachineBasicBlock &MBB, + MachineBasicBlock::iterator MBBI, + AtomicRMWInst::BinOp, bool IsMasked, int Width, + MachineBasicBlock::iterator &NextMBBI); + bool expandAtomicCmpXchg(MachineBasicBlock &MBB, + MachineBasicBlock::iterator MBBI, bool IsMasked, + int Width, MachineBasicBlock::iterator &NextMBBI); +}; + +char RISCVExpandAtomicPseudo::ID = 0; + +bool RISCVExpandAtomicPseudo::runOnMachineFunction(MachineFunction &MF) { + TII = static_cast<const RISCVInstrInfo *>(MF.getSubtarget().getInstrInfo()); + bool Modified = false; + for (auto &MBB : MF) + Modified |= expandMBB(MBB); + return Modified; +} + +bool RISCVExpandAtomicPseudo::expandMBB(MachineBasicBlock &MBB) { + bool Modified = false; + + MachineBasicBlock::iterator MBBI = MBB.begin(), E = MBB.end(); + while (MBBI != E) { + MachineBasicBlock::iterator NMBBI = std::next(MBBI); + Modified |= expandMI(MBB, MBBI, NMBBI); + MBBI = NMBBI; + } + + return Modified; +} + +bool RISCVExpandAtomicPseudo::expandMI(MachineBasicBlock &MBB, + MachineBasicBlock::iterator MBBI, + MachineBasicBlock::iterator &NextMBBI) { + // RISCVInstrInfo::getInstSizeInBytes hard-codes the number of expanded + // instructions for each pseudo, and must be updated when adding new pseudos + // or changing existing ones. + switch (MBBI->getOpcode()) { + case RISCV::PseudoAtomicLoadNand32: + return expandAtomicBinOp(MBB, MBBI, AtomicRMWInst::Nand, false, 32, + NextMBBI); + case RISCV::PseudoAtomicLoadNand64: + return expandAtomicBinOp(MBB, MBBI, AtomicRMWInst::Nand, false, 64, + NextMBBI); + case RISCV::PseudoMaskedAtomicSwap32: + return expandAtomicBinOp(MBB, MBBI, AtomicRMWInst::Xchg, true, 32, + NextMBBI); + case RISCV::PseudoMaskedAtomicLoadAdd32: + return expandAtomicBinOp(MBB, MBBI, AtomicRMWInst::Add, true, 32, NextMBBI); + case RISCV::PseudoMaskedAtomicLoadSub32: + return expandAtomicBinOp(MBB, MBBI, AtomicRMWInst::Sub, true, 32, NextMBBI); + case RISCV::PseudoMaskedAtomicLoadNand32: + return expandAtomicBinOp(MBB, MBBI, AtomicRMWInst::Nand, true, 32, + NextMBBI); + case RISCV::PseudoMaskedAtomicLoadMax32: + return expandAtomicMinMaxOp(MBB, MBBI, AtomicRMWInst::Max, true, 32, + NextMBBI); + case RISCV::PseudoMaskedAtomicLoadMin32: + return expandAtomicMinMaxOp(MBB, MBBI, AtomicRMWInst::Min, true, 32, + NextMBBI); + case RISCV::PseudoMaskedAtomicLoadUMax32: + return expandAtomicMinMaxOp(MBB, MBBI, AtomicRMWInst::UMax, true, 32, + NextMBBI); + case RISCV::PseudoMaskedAtomicLoadUMin32: + return expandAtomicMinMaxOp(MBB, MBBI, AtomicRMWInst::UMin, true, 32, + NextMBBI); + case RISCV::PseudoCmpXchg32: + return expandAtomicCmpXchg(MBB, MBBI, false, 32, NextMBBI); + case RISCV::PseudoCmpXchg64: + return expandAtomicCmpXchg(MBB, MBBI, false, 64, NextMBBI); + case RISCV::PseudoMaskedCmpXchg32: + return expandAtomicCmpXchg(MBB, MBBI, true, 32, NextMBBI); + } + + return false; +} + +static unsigned getLRForRMW32(AtomicOrdering Ordering) { + switch (Ordering) { + default: + llvm_unreachable("Unexpected AtomicOrdering"); + case AtomicOrdering::Monotonic: + return RISCV::LR_W; + case AtomicOrdering::Acquire: + return RISCV::LR_W_AQ; + case AtomicOrdering::Release: + return RISCV::LR_W; + case AtomicOrdering::AcquireRelease: + return RISCV::LR_W_AQ; + case AtomicOrdering::SequentiallyConsistent: + return RISCV::LR_W_AQ_RL; + } +} + +static unsigned getSCForRMW32(AtomicOrdering Ordering) { + switch (Ordering) { + default: + llvm_unreachable("Unexpected AtomicOrdering"); + case AtomicOrdering::Monotonic: + return RISCV::SC_W; + case AtomicOrdering::Acquire: + return RISCV::SC_W; + case AtomicOrdering::Release: + return RISCV::SC_W_RL; + case AtomicOrdering::AcquireRelease: + return RISCV::SC_W_RL; + case AtomicOrdering::SequentiallyConsistent: + return RISCV::SC_W_AQ_RL; + } +} + +static unsigned getLRForRMW64(AtomicOrdering Ordering) { + switch (Ordering) { + default: + llvm_unreachable("Unexpected AtomicOrdering"); + case AtomicOrdering::Monotonic: + return RISCV::LR_D; + case AtomicOrdering::Acquire: + return RISCV::LR_D_AQ; + case AtomicOrdering::Release: + return RISCV::LR_D; + case AtomicOrdering::AcquireRelease: + return RISCV::LR_D_AQ; + case AtomicOrdering::SequentiallyConsistent: + return RISCV::LR_D_AQ_RL; + } +} + +static unsigned getSCForRMW64(AtomicOrdering Ordering) { + switch (Ordering) { + default: + llvm_unreachable("Unexpected AtomicOrdering"); + case AtomicOrdering::Monotonic: + return RISCV::SC_D; + case AtomicOrdering::Acquire: + return RISCV::SC_D; + case AtomicOrdering::Release: + return RISCV::SC_D_RL; + case AtomicOrdering::AcquireRelease: + return RISCV::SC_D_RL; + case AtomicOrdering::SequentiallyConsistent: + return RISCV::SC_D_AQ_RL; + } +} + +static unsigned getLRForRMW(AtomicOrdering Ordering, int Width) { + if (Width == 32) + return getLRForRMW32(Ordering); + if (Width == 64) + return getLRForRMW64(Ordering); + llvm_unreachable("Unexpected LR width\n"); +} + +static unsigned getSCForRMW(AtomicOrdering Ordering, int Width) { + if (Width == 32) + return getSCForRMW32(Ordering); + if (Width == 64) + return getSCForRMW64(Ordering); + llvm_unreachable("Unexpected SC width\n"); +} + +static void doAtomicBinOpExpansion(const RISCVInstrInfo *TII, MachineInstr &MI, + DebugLoc DL, MachineBasicBlock *ThisMBB, + MachineBasicBlock *LoopMBB, + MachineBasicBlock *DoneMBB, + AtomicRMWInst::BinOp BinOp, int Width) { + Register DestReg = MI.getOperand(0).getReg(); + Register ScratchReg = MI.getOperand(1).getReg(); + Register AddrReg = MI.getOperand(2).getReg(); + Register IncrReg = MI.getOperand(3).getReg(); + AtomicOrdering Ordering = + static_cast<AtomicOrdering>(MI.getOperand(4).getImm()); + + // .loop: + // lr.[w|d] dest, (addr) + // binop scratch, dest, val + // sc.[w|d] scratch, scratch, (addr) + // bnez scratch, loop + BuildMI(LoopMBB, DL, TII->get(getLRForRMW(Ordering, Width)), DestReg) + .addReg(AddrReg); + switch (BinOp) { + default: + llvm_unreachable("Unexpected AtomicRMW BinOp"); + case AtomicRMWInst::Nand: + BuildMI(LoopMBB, DL, TII->get(RISCV::AND), ScratchReg) + .addReg(DestReg) + .addReg(IncrReg); + BuildMI(LoopMBB, DL, TII->get(RISCV::XORI), ScratchReg) + .addReg(ScratchReg) + .addImm(-1); + break; + } + BuildMI(LoopMBB, DL, TII->get(getSCForRMW(Ordering, Width)), ScratchReg) + .addReg(AddrReg) + .addReg(ScratchReg); + BuildMI(LoopMBB, DL, TII->get(RISCV::BNE)) + .addReg(ScratchReg) + .addReg(RISCV::X0) + .addMBB(LoopMBB); +} + +static void insertMaskedMerge(const RISCVInstrInfo *TII, DebugLoc DL, + MachineBasicBlock *MBB, Register DestReg, + Register OldValReg, Register NewValReg, + Register MaskReg, Register ScratchReg) { + assert(OldValReg != ScratchReg && "OldValReg and ScratchReg must be unique"); + assert(OldValReg != MaskReg && "OldValReg and MaskReg must be unique"); + assert(ScratchReg != MaskReg && "ScratchReg and MaskReg must be unique"); + + // We select bits from newval and oldval using: + // https://graphics.stanford.edu/~seander/bithacks.html#MaskedMerge + // r = oldval ^ ((oldval ^ newval) & masktargetdata); + BuildMI(MBB, DL, TII->get(RISCV::XOR), ScratchReg) + .addReg(OldValReg) + .addReg(NewValReg); + BuildMI(MBB, DL, TII->get(RISCV::AND), ScratchReg) + .addReg(ScratchReg) + .addReg(MaskReg); + BuildMI(MBB, DL, TII->get(RISCV::XOR), DestReg) + .addReg(OldValReg) + .addReg(ScratchReg); +} + +static void doMaskedAtomicBinOpExpansion( + const RISCVInstrInfo *TII, MachineInstr &MI, DebugLoc DL, + MachineBasicBlock *ThisMBB, MachineBasicBlock *LoopMBB, + MachineBasicBlock *DoneMBB, AtomicRMWInst::BinOp BinOp, int Width) { + assert(Width == 32 && "Should never need to expand masked 64-bit operations"); + Register DestReg = MI.getOperand(0).getReg(); + Register ScratchReg = MI.getOperand(1).getReg(); + Register AddrReg = MI.getOperand(2).getReg(); + Register IncrReg = MI.getOperand(3).getReg(); + Register MaskReg = MI.getOperand(4).getReg(); + AtomicOrdering Ordering = + static_cast<AtomicOrdering>(MI.getOperand(5).getImm()); + + // .loop: + // lr.w destreg, (alignedaddr) + // binop scratch, destreg, incr + // xor scratch, destreg, scratch + // and scratch, scratch, masktargetdata + // xor scratch, destreg, scratch + // sc.w scratch, scratch, (alignedaddr) + // bnez scratch, loop + BuildMI(LoopMBB, DL, TII->get(getLRForRMW32(Ordering)), DestReg) + .addReg(AddrReg); + switch (BinOp) { + default: + llvm_unreachable("Unexpected AtomicRMW BinOp"); + case AtomicRMWInst::Xchg: + BuildMI(LoopMBB, DL, TII->get(RISCV::ADDI), ScratchReg) + .addReg(IncrReg) + .addImm(0); + break; + case AtomicRMWInst::Add: + BuildMI(LoopMBB, DL, TII->get(RISCV::ADD), ScratchReg) + .addReg(DestReg) + .addReg(IncrReg); + break; + case AtomicRMWInst::Sub: + BuildMI(LoopMBB, DL, TII->get(RISCV::SUB), ScratchReg) + .addReg(DestReg) + .addReg(IncrReg); + break; + case AtomicRMWInst::Nand: + BuildMI(LoopMBB, DL, TII->get(RISCV::AND), ScratchReg) + .addReg(DestReg) + .addReg(IncrReg); + BuildMI(LoopMBB, DL, TII->get(RISCV::XORI), ScratchReg) + .addReg(ScratchReg) + .addImm(-1); + break; + } + + insertMaskedMerge(TII, DL, LoopMBB, ScratchReg, DestReg, ScratchReg, MaskReg, + ScratchReg); + + BuildMI(LoopMBB, DL, TII->get(getSCForRMW32(Ordering)), ScratchReg) + .addReg(AddrReg) + .addReg(ScratchReg); + BuildMI(LoopMBB, DL, TII->get(RISCV::BNE)) + .addReg(ScratchReg) + .addReg(RISCV::X0) + .addMBB(LoopMBB); +} + +bool RISCVExpandAtomicPseudo::expandAtomicBinOp( + MachineBasicBlock &MBB, MachineBasicBlock::iterator MBBI, + AtomicRMWInst::BinOp BinOp, bool IsMasked, int Width, + MachineBasicBlock::iterator &NextMBBI) { + MachineInstr &MI = *MBBI; + DebugLoc DL = MI.getDebugLoc(); + + MachineFunction *MF = MBB.getParent(); + auto LoopMBB = MF->CreateMachineBasicBlock(MBB.getBasicBlock()); + auto DoneMBB = MF->CreateMachineBasicBlock(MBB.getBasicBlock()); + + // Insert new MBBs. + MF->insert(++MBB.getIterator(), LoopMBB); + MF->insert(++LoopMBB->getIterator(), DoneMBB); + + // Set up successors and transfer remaining instructions to DoneMBB. + LoopMBB->addSuccessor(LoopMBB); + LoopMBB->addSuccessor(DoneMBB); + DoneMBB->splice(DoneMBB->end(), &MBB, MI, MBB.end()); + DoneMBB->transferSuccessors(&MBB); + MBB.addSuccessor(LoopMBB); + + if (!IsMasked) + doAtomicBinOpExpansion(TII, MI, DL, &MBB, LoopMBB, DoneMBB, BinOp, Width); + else + doMaskedAtomicBinOpExpansion(TII, MI, DL, &MBB, LoopMBB, DoneMBB, BinOp, + Width); + + NextMBBI = MBB.end(); + MI.eraseFromParent(); + + LivePhysRegs LiveRegs; + computeAndAddLiveIns(LiveRegs, *LoopMBB); + computeAndAddLiveIns(LiveRegs, *DoneMBB); + + return true; +} + +static void insertSext(const RISCVInstrInfo *TII, DebugLoc DL, + MachineBasicBlock *MBB, Register ValReg, + Register ShamtReg) { + BuildMI(MBB, DL, TII->get(RISCV::SLL), ValReg) + .addReg(ValReg) + .addReg(ShamtReg); + BuildMI(MBB, DL, TII->get(RISCV::SRA), ValReg) + .addReg(ValReg) + .addReg(ShamtReg); +} + +bool RISCVExpandAtomicPseudo::expandAtomicMinMaxOp( + MachineBasicBlock &MBB, MachineBasicBlock::iterator MBBI, + AtomicRMWInst::BinOp BinOp, bool IsMasked, int Width, + MachineBasicBlock::iterator &NextMBBI) { + assert(IsMasked == true && + "Should only need to expand masked atomic max/min"); + assert(Width == 32 && "Should never need to expand masked 64-bit operations"); + + MachineInstr &MI = *MBBI; + DebugLoc DL = MI.getDebugLoc(); + MachineFunction *MF = MBB.getParent(); + auto LoopHeadMBB = MF->CreateMachineBasicBlock(MBB.getBasicBlock()); + auto LoopIfBodyMBB = MF->CreateMachineBasicBlock(MBB.getBasicBlock()); + auto LoopTailMBB = MF->CreateMachineBasicBlock(MBB.getBasicBlock()); + auto DoneMBB = MF->CreateMachineBasicBlock(MBB.getBasicBlock()); + + // Insert new MBBs. + MF->insert(++MBB.getIterator(), LoopHeadMBB); + MF->insert(++LoopHeadMBB->getIterator(), LoopIfBodyMBB); + MF->insert(++LoopIfBodyMBB->getIterator(), LoopTailMBB); + MF->insert(++LoopTailMBB->getIterator(), DoneMBB); + + // Set up successors and transfer remaining instructions to DoneMBB. + LoopHeadMBB->addSuccessor(LoopIfBodyMBB); + LoopHeadMBB->addSuccessor(LoopTailMBB); + LoopIfBodyMBB->addSuccessor(LoopTailMBB); + LoopTailMBB->addSuccessor(LoopHeadMBB); + LoopTailMBB->addSuccessor(DoneMBB); + DoneMBB->splice(DoneMBB->end(), &MBB, MI, MBB.end()); + DoneMBB->transferSuccessors(&MBB); + MBB.addSuccessor(LoopHeadMBB); + + Register DestReg = MI.getOperand(0).getReg(); + Register Scratch1Reg = MI.getOperand(1).getReg(); + Register Scratch2Reg = MI.getOperand(2).getReg(); + Register AddrReg = MI.getOperand(3).getReg(); + Register IncrReg = MI.getOperand(4).getReg(); + Register MaskReg = MI.getOperand(5).getReg(); + bool IsSigned = BinOp == AtomicRMWInst::Min || BinOp == AtomicRMWInst::Max; + AtomicOrdering Ordering = + static_cast<AtomicOrdering>(MI.getOperand(IsSigned ? 7 : 6).getImm()); + + // + // .loophead: + // lr.w destreg, (alignedaddr) + // and scratch2, destreg, mask + // mv scratch1, destreg + // [sext scratch2 if signed min/max] + // ifnochangeneeded scratch2, incr, .looptail + BuildMI(LoopHeadMBB, DL, TII->get(getLRForRMW32(Ordering)), DestReg) + .addReg(AddrReg); + BuildMI(LoopHeadMBB, DL, TII->get(RISCV::AND), Scratch2Reg) + .addReg(DestReg) + .addReg(MaskReg); + BuildMI(LoopHeadMBB, DL, TII->get(RISCV::ADDI), Scratch1Reg) + .addReg(DestReg) + .addImm(0); + + switch (BinOp) { + default: + llvm_unreachable("Unexpected AtomicRMW BinOp"); + case AtomicRMWInst::Max: { + insertSext(TII, DL, LoopHeadMBB, Scratch2Reg, MI.getOperand(6).getReg()); + BuildMI(LoopHeadMBB, DL, TII->get(RISCV::BGE)) + .addReg(Scratch2Reg) + .addReg(IncrReg) + .addMBB(LoopTailMBB); + break; + } + case AtomicRMWInst::Min: { + insertSext(TII, DL, LoopHeadMBB, Scratch2Reg, MI.getOperand(6).getReg()); + BuildMI(LoopHeadMBB, DL, TII->get(RISCV::BGE)) + .addReg(IncrReg) + .addReg(Scratch2Reg) + .addMBB(LoopTailMBB); + break; + } + case AtomicRMWInst::UMax: + BuildMI(LoopHeadMBB, DL, TII->get(RISCV::BGEU)) + .addReg(Scratch2Reg) + .addReg(IncrReg) + .addMBB(LoopTailMBB); + break; + case AtomicRMWInst::UMin: + BuildMI(LoopHeadMBB, DL, TII->get(RISCV::BGEU)) + .addReg(IncrReg) + .addReg(Scratch2Reg) + .addMBB(LoopTailMBB); + break; + } + + // .loopifbody: + // xor scratch1, destreg, incr + // and scratch1, scratch1, mask + // xor scratch1, destreg, scratch1 + insertMaskedMerge(TII, DL, LoopIfBodyMBB, Scratch1Reg, DestReg, IncrReg, + MaskReg, Scratch1Reg); + + // .looptail: + // sc.w scratch1, scratch1, (addr) + // bnez scratch1, loop + BuildMI(LoopTailMBB, DL, TII->get(getSCForRMW32(Ordering)), Scratch1Reg) + .addReg(AddrReg) + .addReg(Scratch1Reg); + BuildMI(LoopTailMBB, DL, TII->get(RISCV::BNE)) + .addReg(Scratch1Reg) + .addReg(RISCV::X0) + .addMBB(LoopHeadMBB); + + NextMBBI = MBB.end(); + MI.eraseFromParent(); + + LivePhysRegs LiveRegs; + computeAndAddLiveIns(LiveRegs, *LoopHeadMBB); + computeAndAddLiveIns(LiveRegs, *LoopIfBodyMBB); + computeAndAddLiveIns(LiveRegs, *LoopTailMBB); + computeAndAddLiveIns(LiveRegs, *DoneMBB); + + return true; +} + +bool RISCVExpandAtomicPseudo::expandAtomicCmpXchg( + MachineBasicBlock &MBB, MachineBasicBlock::iterator MBBI, bool IsMasked, + int Width, MachineBasicBlock::iterator &NextMBBI) { + MachineInstr &MI = *MBBI; + DebugLoc DL = MI.getDebugLoc(); + MachineFunction *MF = MBB.getParent(); + auto LoopHeadMBB = MF->CreateMachineBasicBlock(MBB.getBasicBlock()); + auto LoopTailMBB = MF->CreateMachineBasicBlock(MBB.getBasicBlock()); + auto DoneMBB = MF->CreateMachineBasicBlock(MBB.getBasicBlock()); + + // Insert new MBBs. + MF->insert(++MBB.getIterator(), LoopHeadMBB); + MF->insert(++LoopHeadMBB->getIterator(), LoopTailMBB); + MF->insert(++LoopTailMBB->getIterator(), DoneMBB); + + // Set up successors and transfer remaining instructions to DoneMBB. + LoopHeadMBB->addSuccessor(LoopTailMBB); + LoopHeadMBB->addSuccessor(DoneMBB); + LoopTailMBB->addSuccessor(DoneMBB); + LoopTailMBB->addSuccessor(LoopHeadMBB); + DoneMBB->splice(DoneMBB->end(), &MBB, MI, MBB.end()); + DoneMBB->transferSuccessors(&MBB); + MBB.addSuccessor(LoopHeadMBB); + + Register DestReg = MI.getOperand(0).getReg(); + Register ScratchReg = MI.getOperand(1).getReg(); + Register AddrReg = MI.getOperand(2).getReg(); + Register CmpValReg = MI.getOperand(3).getReg(); + Register NewValReg = MI.getOperand(4).getReg(); + AtomicOrdering Ordering = + static_cast<AtomicOrdering>(MI.getOperand(IsMasked ? 6 : 5).getImm()); + + if (!IsMasked) { + // .loophead: + // lr.[w|d] dest, (addr) + // bne dest, cmpval, done + BuildMI(LoopHeadMBB, DL, TII->get(getLRForRMW(Ordering, Width)), DestReg) + .addReg(AddrReg); + BuildMI(LoopHeadMBB, DL, TII->get(RISCV::BNE)) + .addReg(DestReg) + .addReg(CmpValReg) + .addMBB(DoneMBB); + // .looptail: + // sc.[w|d] scratch, newval, (addr) + // bnez scratch, loophead + BuildMI(LoopTailMBB, DL, TII->get(getSCForRMW(Ordering, Width)), ScratchReg) + .addReg(AddrReg) + .addReg(NewValReg); + BuildMI(LoopTailMBB, DL, TII->get(RISCV::BNE)) + .addReg(ScratchReg) + .addReg(RISCV::X0) + .addMBB(LoopHeadMBB); + } else { + // .loophead: + // lr.w dest, (addr) + // and scratch, dest, mask + // bne scratch, cmpval, done + Register MaskReg = MI.getOperand(5).getReg(); + BuildMI(LoopHeadMBB, DL, TII->get(getLRForRMW(Ordering, Width)), DestReg) + .addReg(AddrReg); + BuildMI(LoopHeadMBB, DL, TII->get(RISCV::AND), ScratchReg) + .addReg(DestReg) + .addReg(MaskReg); + BuildMI(LoopHeadMBB, DL, TII->get(RISCV::BNE)) + .addReg(ScratchReg) + .addReg(CmpValReg) + .addMBB(DoneMBB); + + // .looptail: + // xor scratch, dest, newval + // and scratch, scratch, mask + // xor scratch, dest, scratch + // sc.w scratch, scratch, (adrr) + // bnez scratch, loophead + insertMaskedMerge(TII, DL, LoopTailMBB, ScratchReg, DestReg, NewValReg, + MaskReg, ScratchReg); + BuildMI(LoopTailMBB, DL, TII->get(getSCForRMW(Ordering, Width)), ScratchReg) + .addReg(AddrReg) + .addReg(ScratchReg); + BuildMI(LoopTailMBB, DL, TII->get(RISCV::BNE)) + .addReg(ScratchReg) + .addReg(RISCV::X0) + .addMBB(LoopHeadMBB); + } + + NextMBBI = MBB.end(); + MI.eraseFromParent(); + + LivePhysRegs LiveRegs; + computeAndAddLiveIns(LiveRegs, *LoopHeadMBB); + computeAndAddLiveIns(LiveRegs, *LoopTailMBB); + computeAndAddLiveIns(LiveRegs, *DoneMBB); + + return true; +} + +} // end of anonymous namespace + +INITIALIZE_PASS(RISCVExpandAtomicPseudo, "riscv-expand-atomic-pseudo", + RISCV_EXPAND_ATOMIC_PSEUDO_NAME, false, false) + +namespace llvm { + +FunctionPass *createRISCVExpandAtomicPseudoPass() { + return new RISCVExpandAtomicPseudo(); +} + +} // end of namespace llvm diff --git a/llvm/lib/Target/RISCV/RISCVExpandPseudoInsts.cpp b/llvm/lib/Target/RISCV/RISCVExpandPseudoInsts.cpp index 84bce0f48562..504355fb8bf8 100644 --- a/llvm/lib/Target/RISCV/RISCVExpandPseudoInsts.cpp +++ b/llvm/lib/Target/RISCV/RISCVExpandPseudoInsts.cpp @@ -43,17 +43,6 @@ private: bool expandMBB(MachineBasicBlock &MBB); bool expandMI(MachineBasicBlock &MBB, MachineBasicBlock::iterator MBBI, MachineBasicBlock::iterator &NextMBBI); - bool expandAtomicBinOp(MachineBasicBlock &MBB, - MachineBasicBlock::iterator MBBI, AtomicRMWInst::BinOp, - bool IsMasked, int Width, - MachineBasicBlock::iterator &NextMBBI); - bool expandAtomicMinMaxOp(MachineBasicBlock &MBB, - MachineBasicBlock::iterator MBBI, - AtomicRMWInst::BinOp, bool IsMasked, int Width, - MachineBasicBlock::iterator &NextMBBI); - bool expandAtomicCmpXchg(MachineBasicBlock &MBB, - MachineBasicBlock::iterator MBBI, bool IsMasked, - int Width, MachineBasicBlock::iterator &NextMBBI); bool expandAuipcInstPair(MachineBasicBlock &MBB, MachineBasicBlock::iterator MBBI, MachineBasicBlock::iterator &NextMBBI, @@ -98,41 +87,10 @@ bool RISCVExpandPseudo::expandMBB(MachineBasicBlock &MBB) { bool RISCVExpandPseudo::expandMI(MachineBasicBlock &MBB, MachineBasicBlock::iterator MBBI, MachineBasicBlock::iterator &NextMBBI) { + // RISCVInstrInfo::getInstSizeInBytes hard-codes the number of expanded + // instructions for each pseudo, and must be updated when adding new pseudos + // or changing existing ones. switch (MBBI->getOpcode()) { - case RISCV::PseudoAtomicLoadNand32: - return expandAtomicBinOp(MBB, MBBI, AtomicRMWInst::Nand, false, 32, - NextMBBI); - case RISCV::PseudoAtomicLoadNand64: - return expandAtomicBinOp(MBB, MBBI, AtomicRMWInst::Nand, false, 64, - NextMBBI); - case RISCV::PseudoMaskedAtomicSwap32: - return expandAtomicBinOp(MBB, MBBI, AtomicRMWInst::Xchg, true, 32, - NextMBBI); - case RISCV::PseudoMaskedAtomicLoadAdd32: - return expandAtomicBinOp(MBB, MBBI, AtomicRMWInst::Add, true, 32, NextMBBI); - case RISCV::PseudoMaskedAtomicLoadSub32: - return expandAtomicBinOp(MBB, MBBI, AtomicRMWInst::Sub, true, 32, NextMBBI); - case RISCV::PseudoMaskedAtomicLoadNand32: - return expandAtomicBinOp(MBB, MBBI, AtomicRMWInst::Nand, true, 32, - NextMBBI); - case RISCV::PseudoMaskedAtomicLoadMax32: - return expandAtomicMinMaxOp(MBB, MBBI, AtomicRMWInst::Max, true, 32, - NextMBBI); - case RISCV::PseudoMaskedAtomicLoadMin32: - return expandAtomicMinMaxOp(MBB, MBBI, AtomicRMWInst::Min, true, 32, - NextMBBI); - case RISCV::PseudoMaskedAtomicLoadUMax32: - return expandAtomicMinMaxOp(MBB, MBBI, AtomicRMWInst::UMax, true, 32, - NextMBBI); - case RISCV::PseudoMaskedAtomicLoadUMin32: - return expandAtomicMinMaxOp(MBB, MBBI, AtomicRMWInst::UMin, true, 32, - NextMBBI); - case RISCV::PseudoCmpXchg32: - return expandAtomicCmpXchg(MBB, MBBI, false, 32, NextMBBI); - case RISCV::PseudoCmpXchg64: - return expandAtomicCmpXchg(MBB, MBBI, false, 64, NextMBBI); - case RISCV::PseudoMaskedCmpXchg32: - return expandAtomicCmpXchg(MBB, MBBI, true, 32, NextMBBI); case RISCV::PseudoLLA: return expandLoadLocalAddress(MBB, MBBI, NextMBBI); case RISCV::PseudoLA: @@ -146,481 +104,6 @@ bool RISCVExpandPseudo::expandMI(MachineBasicBlock &MBB, return false; } -static unsigned getLRForRMW32(AtomicOrdering Ordering) { - switch (Ordering) { - default: - llvm_unreachable("Unexpected AtomicOrdering"); - case AtomicOrdering::Monotonic: - return RISCV::LR_W; - case AtomicOrdering::Acquire: - return RISCV::LR_W_AQ; - case AtomicOrdering::Release: - return RISCV::LR_W; - case AtomicOrdering::AcquireRelease: - return RISCV::LR_W_AQ; - case AtomicOrdering::SequentiallyConsistent: - return RISCV::LR_W_AQ_RL; - } -} - -static unsigned getSCForRMW32(AtomicOrdering Ordering) { - switch (Ordering) { - default: - llvm_unreachable("Unexpected AtomicOrdering"); - case AtomicOrdering::Monotonic: - return RISCV::SC_W; - case AtomicOrdering::Acquire: - return RISCV::SC_W; - case AtomicOrdering::Release: - return RISCV::SC_W_RL; - case AtomicOrdering::AcquireRelease: - return RISCV::SC_W_RL; - case AtomicOrdering::SequentiallyConsistent: - return RISCV::SC_W_AQ_RL; - } -} - -static unsigned getLRForRMW64(AtomicOrdering Ordering) { - switch (Ordering) { - default: - llvm_unreachable("Unexpected AtomicOrdering"); - case AtomicOrdering::Monotonic: - return RISCV::LR_D; - case AtomicOrdering::Acquire: - return RISCV::LR_D_AQ; - case AtomicOrdering::Release: - return RISCV::LR_D; - case AtomicOrdering::AcquireRelease: - return RISCV::LR_D_AQ; - case AtomicOrdering::SequentiallyConsistent: - return RISCV::LR_D_AQ_RL; - } -} - -static unsigned getSCForRMW64(AtomicOrdering Ordering) { - switch (Ordering) { - default: - llvm_unreachable("Unexpected AtomicOrdering"); - case AtomicOrdering::Monotonic: - return RISCV::SC_D; - case AtomicOrdering::Acquire: - return RISCV::SC_D; - case AtomicOrdering::Release: - return RISCV::SC_D_RL; - case AtomicOrdering::AcquireRelease: - return RISCV::SC_D_RL; - case AtomicOrdering::SequentiallyConsistent: - return RISCV::SC_D_AQ_RL; - } -} - -static unsigned getLRForRMW(AtomicOrdering Ordering, int Width) { - if (Width == 32) - return getLRForRMW32(Ordering); - if (Width == 64) - return getLRForRMW64(Ordering); - llvm_unreachable("Unexpected LR width\n"); -} - -static unsigned getSCForRMW(AtomicOrdering Ordering, int Width) { - if (Width == 32) - return getSCForRMW32(Ordering); - if (Width == 64) - return getSCForRMW64(Ordering); - llvm_unreachable("Unexpected SC width\n"); -} - -static void doAtomicBinOpExpansion(const RISCVInstrInfo *TII, MachineInstr &MI, - DebugLoc DL, MachineBasicBlock *ThisMBB, - MachineBasicBlock *LoopMBB, - MachineBasicBlock *DoneMBB, - AtomicRMWInst::BinOp BinOp, int Width) { - Register DestReg = MI.getOperand(0).getReg(); - Register ScratchReg = MI.getOperand(1).getReg(); - Register AddrReg = MI.getOperand(2).getReg(); - Register IncrReg = MI.getOperand(3).getReg(); - AtomicOrdering Ordering = - static_cast<AtomicOrdering>(MI.getOperand(4).getImm()); - - // .loop: - // lr.[w|d] dest, (addr) - // binop scratch, dest, val - // sc.[w|d] scratch, scratch, (addr) - // bnez scratch, loop - BuildMI(LoopMBB, DL, TII->get(getLRForRMW(Ordering, Width)), DestReg) - .addReg(AddrReg); - switch (BinOp) { - default: - llvm_unreachable("Unexpected AtomicRMW BinOp"); - case AtomicRMWInst::Nand: - BuildMI(LoopMBB, DL, TII->get(RISCV::AND), ScratchReg) - .addReg(DestReg) - .addReg(IncrReg); - BuildMI(LoopMBB, DL, TII->get(RISCV::XORI), ScratchReg) - .addReg(ScratchReg) - .addImm(-1); - break; - } - BuildMI(LoopMBB, DL, TII->get(getSCForRMW(Ordering, Width)), ScratchReg) - .addReg(AddrReg) - .addReg(ScratchReg); - BuildMI(LoopMBB, DL, TII->get(RISCV::BNE)) - .addReg(ScratchReg) - .addReg(RISCV::X0) - .addMBB(LoopMBB); -} - -static void insertMaskedMerge(const RISCVInstrInfo *TII, DebugLoc DL, - MachineBasicBlock *MBB, Register DestReg, - Register OldValReg, Register NewValReg, - Register MaskReg, Register ScratchReg) { - assert(OldValReg != ScratchReg && "OldValReg and ScratchReg must be unique"); - assert(OldValReg != MaskReg && "OldValReg and MaskReg must be unique"); - assert(ScratchReg != MaskReg && "ScratchReg and MaskReg must be unique"); - - // We select bits from newval and oldval using: - // https://graphics.stanford.edu/~seander/bithacks.html#MaskedMerge - // r = oldval ^ ((oldval ^ newval) & masktargetdata); - BuildMI(MBB, DL, TII->get(RISCV::XOR), ScratchReg) - .addReg(OldValReg) - .addReg(NewValReg); - BuildMI(MBB, DL, TII->get(RISCV::AND), ScratchReg) - .addReg(ScratchReg) - .addReg(MaskReg); - BuildMI(MBB, DL, TII->get(RISCV::XOR), DestReg) - .addReg(OldValReg) - .addReg(ScratchReg); -} - -static void doMaskedAtomicBinOpExpansion( - const RISCVInstrInfo *TII, MachineInstr &MI, DebugLoc DL, - MachineBasicBlock *ThisMBB, MachineBasicBlock *LoopMBB, - MachineBasicBlock *DoneMBB, AtomicRMWInst::BinOp BinOp, int Width) { - assert(Width == 32 && "Should never need to expand masked 64-bit operations"); - Register DestReg = MI.getOperand(0).getReg(); - Register ScratchReg = MI.getOperand(1).getReg(); - Register AddrReg = MI.getOperand(2).getReg(); - Register IncrReg = MI.getOperand(3).getReg(); - Register MaskReg = MI.getOperand(4).getReg(); - AtomicOrdering Ordering = - static_cast<AtomicOrdering>(MI.getOperand(5).getImm()); - - // .loop: - // lr.w destreg, (alignedaddr) - // binop scratch, destreg, incr - // xor scratch, destreg, scratch - // and scratch, scratch, masktargetdata - // xor scratch, destreg, scratch - // sc.w scratch, scratch, (alignedaddr) - // bnez scratch, loop - BuildMI(LoopMBB, DL, TII->get(getLRForRMW32(Ordering)), DestReg) - .addReg(AddrReg); - switch (BinOp) { - default: - llvm_unreachable("Unexpected AtomicRMW BinOp"); - case AtomicRMWInst::Xchg: - BuildMI(LoopMBB, DL, TII->get(RISCV::ADDI), ScratchReg) - .addReg(IncrReg) - .addImm(0); - break; - case AtomicRMWInst::Add: - BuildMI(LoopMBB, DL, TII->get(RISCV::ADD), ScratchReg) - .addReg(DestReg) - .addReg(IncrReg); - break; - case AtomicRMWInst::Sub: - BuildMI(LoopMBB, DL, TII->get(RISCV::SUB), ScratchReg) - .addReg(DestReg) - .addReg(IncrReg); - break; - case AtomicRMWInst::Nand: - BuildMI(LoopMBB, DL, TII->get(RISCV::AND), ScratchReg) - .addReg(DestReg) - .addReg(IncrReg); - BuildMI(LoopMBB, DL, TII->get(RISCV::XORI), ScratchReg) - .addReg(ScratchReg) - .addImm(-1); - break; - } - - insertMaskedMerge(TII, DL, LoopMBB, ScratchReg, DestReg, ScratchReg, MaskReg, - ScratchReg); - - BuildMI(LoopMBB, DL, TII->get(getSCForRMW32(Ordering)), ScratchReg) - .addReg(AddrReg) - .addReg(ScratchReg); - BuildMI(LoopMBB, DL, TII->get(RISCV::BNE)) - .addReg(ScratchReg) - .addReg(RISCV::X0) - .addMBB(LoopMBB); -} - -bool RISCVExpandPseudo::expandAtomicBinOp( - MachineBasicBlock &MBB, MachineBasicBlock::iterator MBBI, - AtomicRMWInst::BinOp BinOp, bool IsMasked, int Width, - MachineBasicBlock::iterator &NextMBBI) { - MachineInstr &MI = *MBBI; - DebugLoc DL = MI.getDebugLoc(); - - MachineFunction *MF = MBB.getParent(); - auto LoopMBB = MF->CreateMachineBasicBlock(MBB.getBasicBlock()); - auto DoneMBB = MF->CreateMachineBasicBlock(MBB.getBasicBlock()); - - // Insert new MBBs. - MF->insert(++MBB.getIterator(), LoopMBB); - MF->insert(++LoopMBB->getIterator(), DoneMBB); - - // Set up successors and transfer remaining instructions to DoneMBB. - LoopMBB->addSuccessor(LoopMBB); - LoopMBB->addSuccessor(DoneMBB); - DoneMBB->splice(DoneMBB->end(), &MBB, MI, MBB.end()); - DoneMBB->transferSuccessors(&MBB); - MBB.addSuccessor(LoopMBB); - - if (!IsMasked) - doAtomicBinOpExpansion(TII, MI, DL, &MBB, LoopMBB, DoneMBB, BinOp, Width); - else - doMaskedAtomicBinOpExpansion(TII, MI, DL, &MBB, LoopMBB, DoneMBB, BinOp, - Width); - - NextMBBI = MBB.end(); - MI.eraseFromParent(); - - LivePhysRegs LiveRegs; - computeAndAddLiveIns(LiveRegs, *LoopMBB); - computeAndAddLiveIns(LiveRegs, *DoneMBB); - - return true; -} - -static void insertSext(const RISCVInstrInfo *TII, DebugLoc DL, - MachineBasicBlock *MBB, Register ValReg, - Register ShamtReg) { - BuildMI(MBB, DL, TII->get(RISCV::SLL), ValReg) - .addReg(ValReg) - .addReg(ShamtReg); - BuildMI(MBB, DL, TII->get(RISCV::SRA), ValReg) - .addReg(ValReg) - .addReg(ShamtReg); -} - -bool RISCVExpandPseudo::expandAtomicMinMaxOp( - MachineBasicBlock &MBB, MachineBasicBlock::iterator MBBI, - AtomicRMWInst::BinOp BinOp, bool IsMasked, int Width, - MachineBasicBlock::iterator &NextMBBI) { - assert(IsMasked == true && - "Should only need to expand masked atomic max/min"); - assert(Width == 32 && "Should never need to expand masked 64-bit operations"); - - MachineInstr &MI = *MBBI; - DebugLoc DL = MI.getDebugLoc(); - MachineFunction *MF = MBB.getParent(); - auto LoopHeadMBB = MF->CreateMachineBasicBlock(MBB.getBasicBlock()); - auto LoopIfBodyMBB = MF->CreateMachineBasicBlock(MBB.getBasicBlock()); - auto LoopTailMBB = MF->CreateMachineBasicBlock(MBB.getBasicBlock()); - auto DoneMBB = MF->CreateMachineBasicBlock(MBB.getBasicBlock()); - - // Insert new MBBs. - MF->insert(++MBB.getIterator(), LoopHeadMBB); - MF->insert(++LoopHeadMBB->getIterator(), LoopIfBodyMBB); - MF->insert(++LoopIfBodyMBB->getIterator(), LoopTailMBB); - MF->insert(++LoopTailMBB->getIterator(), DoneMBB); - - // Set up successors and transfer remaining instructions to DoneMBB. - LoopHeadMBB->addSuccessor(LoopIfBodyMBB); - LoopHeadMBB->addSuccessor(LoopTailMBB); - LoopIfBodyMBB->addSuccessor(LoopTailMBB); - LoopTailMBB->addSuccessor(LoopHeadMBB); - LoopTailMBB->addSuccessor(DoneMBB); - DoneMBB->splice(DoneMBB->end(), &MBB, MI, MBB.end()); - DoneMBB->transferSuccessors(&MBB); - MBB.addSuccessor(LoopHeadMBB); - - Register DestReg = MI.getOperand(0).getReg(); - Register Scratch1Reg = MI.getOperand(1).getReg(); - Register Scratch2Reg = MI.getOperand(2).getReg(); - Register AddrReg = MI.getOperand(3).getReg(); - Register IncrReg = MI.getOperand(4).getReg(); - Register MaskReg = MI.getOperand(5).getReg(); - bool IsSigned = BinOp == AtomicRMWInst::Min || BinOp == AtomicRMWInst::Max; - AtomicOrdering Ordering = - static_cast<AtomicOrdering>(MI.getOperand(IsSigned ? 7 : 6).getImm()); - - // - // .loophead: - // lr.w destreg, (alignedaddr) - // and scratch2, destreg, mask - // mv scratch1, destreg - // [sext scratch2 if signed min/max] - // ifnochangeneeded scratch2, incr, .looptail - BuildMI(LoopHeadMBB, DL, TII->get(getLRForRMW32(Ordering)), DestReg) - .addReg(AddrReg); - BuildMI(LoopHeadMBB, DL, TII->get(RISCV::AND), Scratch2Reg) - .addReg(DestReg) - .addReg(MaskReg); - BuildMI(LoopHeadMBB, DL, TII->get(RISCV::ADDI), Scratch1Reg) - .addReg(DestReg) - .addImm(0); - - switch (BinOp) { - default: - llvm_unreachable("Unexpected AtomicRMW BinOp"); - case AtomicRMWInst::Max: { - insertSext(TII, DL, LoopHeadMBB, Scratch2Reg, MI.getOperand(6).getReg()); - BuildMI(LoopHeadMBB, DL, TII->get(RISCV::BGE)) - .addReg(Scratch2Reg) - .addReg(IncrReg) - .addMBB(LoopTailMBB); - break; - } - case AtomicRMWInst::Min: { - insertSext(TII, DL, LoopHeadMBB, Scratch2Reg, MI.getOperand(6).getReg()); - BuildMI(LoopHeadMBB, DL, TII->get(RISCV::BGE)) - .addReg(IncrReg) - .addReg(Scratch2Reg) - .addMBB(LoopTailMBB); - break; - } - case AtomicRMWInst::UMax: - BuildMI(LoopHeadMBB, DL, TII->get(RISCV::BGEU)) - .addReg(Scratch2Reg) - .addReg(IncrReg) - .addMBB(LoopTailMBB); - break; - case AtomicRMWInst::UMin: - BuildMI(LoopHeadMBB, DL, TII->get(RISCV::BGEU)) - .addReg(IncrReg) - .addReg(Scratch2Reg) - .addMBB(LoopTailMBB); - break; - } - - // .loopifbody: - // xor scratch1, destreg, incr - // and scratch1, scratch1, mask - // xor scratch1, destreg, scratch1 - insertMaskedMerge(TII, DL, LoopIfBodyMBB, Scratch1Reg, DestReg, IncrReg, - MaskReg, Scratch1Reg); - - // .looptail: - // sc.w scratch1, scratch1, (addr) - // bnez scratch1, loop - BuildMI(LoopTailMBB, DL, TII->get(getSCForRMW32(Ordering)), Scratch1Reg) - .addReg(AddrReg) - .addReg(Scratch1Reg); - BuildMI(LoopTailMBB, DL, TII->get(RISCV::BNE)) - .addReg(Scratch1Reg) - .addReg(RISCV::X0) - .addMBB(LoopHeadMBB); - - NextMBBI = MBB.end(); - MI.eraseFromParent(); - - LivePhysRegs LiveRegs; - computeAndAddLiveIns(LiveRegs, *LoopHeadMBB); - computeAndAddLiveIns(LiveRegs, *LoopIfBodyMBB); - computeAndAddLiveIns(LiveRegs, *LoopTailMBB); - computeAndAddLiveIns(LiveRegs, *DoneMBB); - - return true; -} - -bool RISCVExpandPseudo::expandAtomicCmpXchg( - MachineBasicBlock &MBB, MachineBasicBlock::iterator MBBI, bool IsMasked, - int Width, MachineBasicBlock::iterator &NextMBBI) { - MachineInstr &MI = *MBBI; - DebugLoc DL = MI.getDebugLoc(); - MachineFunction *MF = MBB.getParent(); - auto LoopHeadMBB = MF->CreateMachineBasicBlock(MBB.getBasicBlock()); - auto LoopTailMBB = MF->CreateMachineBasicBlock(MBB.getBasicBlock()); - auto DoneMBB = MF->CreateMachineBasicBlock(MBB.getBasicBlock()); - - // Insert new MBBs. - MF->insert(++MBB.getIterator(), LoopHeadMBB); - MF->insert(++LoopHeadMBB->getIterator(), LoopTailMBB); - MF->insert(++LoopTailMBB->getIterator(), DoneMBB); - - // Set up successors and transfer remaining instructions to DoneMBB. - LoopHeadMBB->addSuccessor(LoopTailMBB); - LoopHeadMBB->addSuccessor(DoneMBB); - LoopTailMBB->addSuccessor(DoneMBB); - LoopTailMBB->addSuccessor(LoopHeadMBB); - DoneMBB->splice(DoneMBB->end(), &MBB, MI, MBB.end()); - DoneMBB->transferSuccessors(&MBB); - MBB.addSuccessor(LoopHeadMBB); - - Register DestReg = MI.getOperand(0).getReg(); - Register ScratchReg = MI.getOperand(1).getReg(); - Register AddrReg = MI.getOperand(2).getReg(); - Register CmpValReg = MI.getOperand(3).getReg(); - Register NewValReg = MI.getOperand(4).getReg(); - AtomicOrdering Ordering = - static_cast<AtomicOrdering>(MI.getOperand(IsMasked ? 6 : 5).getImm()); - - if (!IsMasked) { - // .loophead: - // lr.[w|d] dest, (addr) - // bne dest, cmpval, done - BuildMI(LoopHeadMBB, DL, TII->get(getLRForRMW(Ordering, Width)), DestReg) - .addReg(AddrReg); - BuildMI(LoopHeadMBB, DL, TII->get(RISCV::BNE)) - .addReg(DestReg) - .addReg(CmpValReg) - .addMBB(DoneMBB); - // .looptail: - // sc.[w|d] scratch, newval, (addr) - // bnez scratch, loophead - BuildMI(LoopTailMBB, DL, TII->get(getSCForRMW(Ordering, Width)), ScratchReg) - .addReg(AddrReg) - .addReg(NewValReg); - BuildMI(LoopTailMBB, DL, TII->get(RISCV::BNE)) - .addReg(ScratchReg) - .addReg(RISCV::X0) - .addMBB(LoopHeadMBB); - } else { - // .loophead: - // lr.w dest, (addr) - // and scratch, dest, mask - // bne scratch, cmpval, done - Register MaskReg = MI.getOperand(5).getReg(); - BuildMI(LoopHeadMBB, DL, TII->get(getLRForRMW(Ordering, Width)), DestReg) - .addReg(AddrReg); - BuildMI(LoopHeadMBB, DL, TII->get(RISCV::AND), ScratchReg) - .addReg(DestReg) - .addReg(MaskReg); - BuildMI(LoopHeadMBB, DL, TII->get(RISCV::BNE)) - .addReg(ScratchReg) - .addReg(CmpValReg) - .addMBB(DoneMBB); - - // .looptail: - // xor scratch, dest, newval - // and scratch, scratch, mask - // xor scratch, dest, scratch - // sc.w scratch, scratch, (adrr) - // bnez scratch, loophead - insertMaskedMerge(TII, DL, LoopTailMBB, ScratchReg, DestReg, NewValReg, - MaskReg, ScratchReg); - BuildMI(LoopTailMBB, DL, TII->get(getSCForRMW(Ordering, Width)), ScratchReg) - .addReg(AddrReg) - .addReg(ScratchReg); - BuildMI(LoopTailMBB, DL, TII->get(RISCV::BNE)) - .addReg(ScratchReg) - .addReg(RISCV::X0) - .addMBB(LoopHeadMBB); - } - - NextMBBI = MBB.end(); - MI.eraseFromParent(); - - LivePhysRegs LiveRegs; - computeAndAddLiveIns(LiveRegs, *LoopHeadMBB); - computeAndAddLiveIns(LiveRegs, *LoopTailMBB); - computeAndAddLiveIns(LiveRegs, *DoneMBB); - - return true; -} - bool RISCVExpandPseudo::expandAuipcInstPair( MachineBasicBlock &MBB, MachineBasicBlock::iterator MBBI, MachineBasicBlock::iterator &NextMBBI, unsigned FlagsHi, diff --git a/llvm/lib/Target/RISCV/RISCVFrameLowering.cpp b/llvm/lib/Target/RISCV/RISCVFrameLowering.cpp index c60fc3fc6b42..43adc7426c79 100644 --- a/llvm/lib/Target/RISCV/RISCVFrameLowering.cpp +++ b/llvm/lib/Target/RISCV/RISCVFrameLowering.cpp @@ -23,6 +23,100 @@ using namespace llvm; +// Get the ID of the libcall used for spilling and restoring callee saved +// registers. The ID is representative of the number of registers saved or +// restored by the libcall, except it is zero-indexed - ID 0 corresponds to a +// single register. +static int getLibCallID(const MachineFunction &MF, + const std::vector<CalleeSavedInfo> &CSI) { + const auto *RVFI = MF.getInfo<RISCVMachineFunctionInfo>(); + + if (CSI.empty() || !RVFI->useSaveRestoreLibCalls(MF)) + return -1; + + Register MaxReg = RISCV::NoRegister; + for (auto &CS : CSI) + // RISCVRegisterInfo::hasReservedSpillSlot assigns negative frame indexes to + // registers which can be saved by libcall. + if (CS.getFrameIdx() < 0) + MaxReg = std::max(MaxReg.id(), CS.getReg()); + + if (MaxReg == RISCV::NoRegister) + return -1; + + switch (MaxReg) { + default: + llvm_unreachable("Something has gone wrong!"); + case /*s11*/ RISCV::X27: return 12; + case /*s10*/ RISCV::X26: return 11; + case /*s9*/ RISCV::X25: return 10; + case /*s8*/ RISCV::X24: return 9; + case /*s7*/ RISCV::X23: return 8; + case /*s6*/ RISCV::X22: return 7; + case /*s5*/ RISCV::X21: return 6; + case /*s4*/ RISCV::X20: return 5; + case /*s3*/ RISCV::X19: return 4; + case /*s2*/ RISCV::X18: return 3; + case /*s1*/ RISCV::X9: return 2; + case /*s0*/ RISCV::X8: return 1; + case /*ra*/ RISCV::X1: return 0; + } +} + +// Get the name of the libcall used for spilling callee saved registers. +// If this function will not use save/restore libcalls, then return a nullptr. +static const char * +getSpillLibCallName(const MachineFunction &MF, + const std::vector<CalleeSavedInfo> &CSI) { + static const char *const SpillLibCalls[] = { + "__riscv_save_0", + "__riscv_save_1", + "__riscv_save_2", + "__riscv_save_3", + "__riscv_save_4", + "__riscv_save_5", + "__riscv_save_6", + "__riscv_save_7", + "__riscv_save_8", + "__riscv_save_9", + "__riscv_save_10", + "__riscv_save_11", + "__riscv_save_12" + }; + + int LibCallID = getLibCallID(MF, CSI); + if (LibCallID == -1) + return nullptr; + return SpillLibCalls[LibCallID]; +} + +// Get the name of the libcall used for restoring callee saved registers. +// If this function will not use save/restore libcalls, then return a nullptr. +static const char * +getRestoreLibCallName(const MachineFunction &MF, + const std::vector<CalleeSavedInfo> &CSI) { + static const char *const RestoreLibCalls[] = { + "__riscv_restore_0", + "__riscv_restore_1", + "__riscv_restore_2", + "__riscv_restore_3", + "__riscv_restore_4", + "__riscv_restore_5", + "__riscv_restore_6", + "__riscv_restore_7", + "__riscv_restore_8", + "__riscv_restore_9", + "__riscv_restore_10", + "__riscv_restore_11", + "__riscv_restore_12" + }; + + int LibCallID = getLibCallID(MF, CSI); + if (LibCallID == -1) + return nullptr; + return RestoreLibCalls[LibCallID]; +} + bool RISCVFrameLowering::hasFP(const MachineFunction &MF) const { const TargetRegisterInfo *RegInfo = MF.getSubtarget().getRegisterInfo(); @@ -48,10 +142,10 @@ void RISCVFrameLowering::determineFrameLayout(MachineFunction &MF) const { uint64_t FrameSize = MFI.getStackSize(); // Get the alignment. - unsigned StackAlign = getStackAlignment(); + Align StackAlign = getStackAlign(); if (RI->needsStackRealignment(MF)) { - unsigned MaxStackAlign = std::max(StackAlign, MFI.getMaxAlignment()); - FrameSize += (MaxStackAlign - StackAlign); + Align MaxStackAlign = std::max(StackAlign, MFI.getMaxAlign()); + FrameSize += (MaxStackAlign.value() - StackAlign.value()); StackAlign = MaxStackAlign; } @@ -105,6 +199,17 @@ static Register getFPReg(const RISCVSubtarget &STI) { return RISCV::X8; } // Returns the register used to hold the stack pointer. static Register getSPReg(const RISCVSubtarget &STI) { return RISCV::X2; } +static SmallVector<CalleeSavedInfo, 8> +getNonLibcallCSI(const std::vector<CalleeSavedInfo> &CSI) { + SmallVector<CalleeSavedInfo, 8> NonLibcallCSI; + + for (auto &CS : CSI) + if (CS.getFrameIdx() >= 0) + NonLibcallCSI.push_back(CS); + + return NonLibcallCSI; +} + void RISCVFrameLowering::emitPrologue(MachineFunction &MF, MachineBasicBlock &MBB) const { MachineFrameInfo &MFI = MF.getFrameInfo(); @@ -117,6 +222,11 @@ void RISCVFrameLowering::emitPrologue(MachineFunction &MF, Register SPReg = getSPReg(STI); Register BPReg = RISCVABI::getBPReg(); + // Since spillCalleeSavedRegisters may have inserted a libcall, skip past + // any instructions marked as FrameSetup + while (MBBI != MBB.end() && MBBI->getFlag(MachineInstr::FrameSetup)) + ++MBBI; + // Debug location must be unknown since the first debug location is used // to determine the end of the prologue. DebugLoc DL; @@ -124,12 +234,38 @@ void RISCVFrameLowering::emitPrologue(MachineFunction &MF, // Determine the correct frame layout determineFrameLayout(MF); + // If libcalls are used to spill and restore callee-saved registers, the frame + // has two sections; the opaque section managed by the libcalls, and the + // section managed by MachineFrameInfo which can also hold callee saved + // registers in fixed stack slots, both of which have negative frame indices. + // This gets even more complicated when incoming arguments are passed via the + // stack, as these too have negative frame indices. An example is detailed + // below: + // + // | incoming arg | <- FI[-3] + // | libcallspill | + // | calleespill | <- FI[-2] + // | calleespill | <- FI[-1] + // | this_frame | <- FI[0] + // + // For negative frame indices, the offset from the frame pointer will differ + // depending on which of these groups the frame index applies to. + // The following calculates the correct offset knowing the number of callee + // saved registers spilt by the two methods. + if (int LibCallRegs = getLibCallID(MF, MFI.getCalleeSavedInfo()) + 1) { + // Calculate the size of the frame managed by the libcall. The libcalls are + // implemented such that the stack will always be 16 byte aligned. + unsigned LibCallFrameSize = alignTo((STI.getXLen() / 8) * LibCallRegs, 16); + RVFI->setLibCallStackSize(LibCallFrameSize); + } + // FIXME (note copied from Lanai): This appears to be overallocating. Needs // investigation. Get the number of bytes to allocate from the FrameInfo. uint64_t StackSize = MFI.getStackSize(); + uint64_t RealStackSize = StackSize + RVFI->getLibCallStackSize(); // Early exit if there is no need to allocate on the stack - if (StackSize == 0 && !MFI.adjustsStack()) + if (RealStackSize == 0 && !MFI.adjustsStack()) return; // If the stack pointer has been marked as reserved, then produce an error if @@ -140,31 +276,42 @@ void RISCVFrameLowering::emitPrologue(MachineFunction &MF, uint64_t FirstSPAdjustAmount = getFirstSPAdjustAmount(MF); // Split the SP adjustment to reduce the offsets of callee saved spill. - if (FirstSPAdjustAmount) + if (FirstSPAdjustAmount) { StackSize = FirstSPAdjustAmount; + RealStackSize = FirstSPAdjustAmount; + } // Allocate space on the stack if necessary. adjustReg(MBB, MBBI, DL, SPReg, SPReg, -StackSize, MachineInstr::FrameSetup); - // Emit ".cfi_def_cfa_offset StackSize" + // Emit ".cfi_def_cfa_offset RealStackSize" unsigned CFIIndex = MF.addFrameInst( - MCCFIInstruction::createDefCfaOffset(nullptr, -StackSize)); + MCCFIInstruction::cfiDefCfaOffset(nullptr, RealStackSize)); BuildMI(MBB, MBBI, DL, TII->get(TargetOpcode::CFI_INSTRUCTION)) .addCFIIndex(CFIIndex); + const auto &CSI = MFI.getCalleeSavedInfo(); + // The frame pointer is callee-saved, and code has been generated for us to // save it to the stack. We need to skip over the storing of callee-saved // registers as the frame pointer must be modified after it has been saved // to the stack, not before. // FIXME: assumes exactly one instruction is used to save each callee-saved // register. - const std::vector<CalleeSavedInfo> &CSI = MFI.getCalleeSavedInfo(); - std::advance(MBBI, CSI.size()); + std::advance(MBBI, getNonLibcallCSI(CSI).size()); // Iterate over list of callee-saved registers and emit .cfi_offset // directives. for (const auto &Entry : CSI) { - int64_t Offset = MFI.getObjectOffset(Entry.getFrameIdx()); + int FrameIdx = Entry.getFrameIdx(); + int64_t Offset; + // Offsets for objects with fixed locations (IE: those saved by libcall) are + // simply calculated from the frame index. + if (FrameIdx < 0) + Offset = FrameIdx * (int64_t) STI.getXLen() / 8; + else + Offset = MFI.getObjectOffset(Entry.getFrameIdx()) - + RVFI->getLibCallStackSize(); Register Reg = Entry.getReg(); unsigned CFIIndex = MF.addFrameInst(MCCFIInstruction::createOffset( nullptr, RI->getDwarfRegNum(Reg, true), Offset)); @@ -179,11 +326,12 @@ void RISCVFrameLowering::emitPrologue(MachineFunction &MF, MF.getFunction(), "Frame pointer required, but has been reserved."}); adjustReg(MBB, MBBI, DL, FPReg, SPReg, - StackSize - RVFI->getVarArgsSaveSize(), MachineInstr::FrameSetup); + RealStackSize - RVFI->getVarArgsSaveSize(), + MachineInstr::FrameSetup); - // Emit ".cfi_def_cfa $fp, 0" - unsigned CFIIndex = MF.addFrameInst(MCCFIInstruction::createDefCfa( - nullptr, RI->getDwarfRegNum(FPReg, true), 0)); + // Emit ".cfi_def_cfa $fp, RVFI->getVarArgsSaveSize()" + unsigned CFIIndex = MF.addFrameInst(MCCFIInstruction::cfiDefCfa( + nullptr, RI->getDwarfRegNum(FPReg, true), RVFI->getVarArgsSaveSize())); BuildMI(MBB, MBBI, DL, TII->get(TargetOpcode::CFI_INSTRUCTION)) .addCFIIndex(CFIIndex); } @@ -201,7 +349,7 @@ void RISCVFrameLowering::emitPrologue(MachineFunction &MF, if (!hasFP(MF)) { // Emit ".cfi_def_cfa_offset StackSize" unsigned CFIIndex = MF.addFrameInst( - MCCFIInstruction::createDefCfaOffset(nullptr, -MFI.getStackSize())); + MCCFIInstruction::cfiDefCfaOffset(nullptr, MFI.getStackSize())); BuildMI(MBB, MBBI, DL, TII->get(TargetOpcode::CFI_INSTRUCTION)) .addCFIIndex(CFIIndex); } @@ -211,15 +359,15 @@ void RISCVFrameLowering::emitPrologue(MachineFunction &MF, // Realign Stack const RISCVRegisterInfo *RI = STI.getRegisterInfo(); if (RI->needsStackRealignment(MF)) { - unsigned MaxAlignment = MFI.getMaxAlignment(); + Align MaxAlignment = MFI.getMaxAlign(); const RISCVInstrInfo *TII = STI.getInstrInfo(); - if (isInt<12>(-(int)MaxAlignment)) { + if (isInt<12>(-(int)MaxAlignment.value())) { BuildMI(MBB, MBBI, DL, TII->get(RISCV::ANDI), SPReg) .addReg(SPReg) - .addImm(-(int)MaxAlignment); + .addImm(-(int)MaxAlignment.value()); } else { - unsigned ShiftAmount = countTrailingZeros(MaxAlignment); + unsigned ShiftAmount = Log2(MaxAlignment); Register VR = MF.getRegInfo().createVirtualRegister(&RISCV::GPRRegClass); BuildMI(MBB, MBBI, DL, TII->get(RISCV::SRLI), VR) @@ -264,15 +412,26 @@ void RISCVFrameLowering::emitEpilogue(MachineFunction &MF, // last instruction. if (!MBBI->isTerminator()) MBBI = std::next(MBBI); + + // If callee-saved registers are saved via libcall, place stack adjustment + // before this call. + while (MBBI != MBB.begin() && + std::prev(MBBI)->getFlag(MachineInstr::FrameDestroy)) + --MBBI; } + const auto &CSI = getNonLibcallCSI(MFI.getCalleeSavedInfo()); + // Skip to before the restores of callee-saved registers // FIXME: assumes exactly one instruction is used to restore each // callee-saved register. - auto LastFrameDestroy = std::prev(MBBI, MFI.getCalleeSavedInfo().size()); + auto LastFrameDestroy = MBBI; + if (!CSI.empty()) + LastFrameDestroy = std::prev(MBBI, CSI.size()); uint64_t StackSize = MFI.getStackSize(); - uint64_t FPOffset = StackSize - RVFI->getVarArgsSaveSize(); + uint64_t RealStackSize = StackSize + RVFI->getLibCallStackSize(); + uint64_t FPOffset = RealStackSize - RVFI->getVarArgsSaveSize(); // Restore the stack pointer using the value of the frame pointer. Only // necessary if the stack pointer was modified, meaning the stack size is @@ -302,7 +461,7 @@ void RISCVFrameLowering::emitEpilogue(MachineFunction &MF, int RISCVFrameLowering::getFrameIndexReference(const MachineFunction &MF, int FI, - unsigned &FrameReg) const { + Register &FrameReg) const { const MachineFrameInfo &MFI = MF.getFrameInfo(); const TargetRegisterInfo *RI = MF.getSubtarget().getRegisterInfo(); const auto *RVFI = MF.getInfo<RISCVMachineFunctionInfo>(); @@ -310,7 +469,7 @@ int RISCVFrameLowering::getFrameIndexReference(const MachineFunction &MF, // Callee-saved registers should be referenced relative to the stack // pointer (positive offset), otherwise use the frame pointer (negative // offset). - const std::vector<CalleeSavedInfo> &CSI = MFI.getCalleeSavedInfo(); + const auto &CSI = getNonLibcallCSI(MFI.getCalleeSavedInfo()); int MinCSFI = 0; int MaxCSFI = -1; @@ -330,7 +489,7 @@ int RISCVFrameLowering::getFrameIndexReference(const MachineFunction &MF, if (FirstSPAdjustAmount) Offset += FirstSPAdjustAmount; else - Offset += MF.getFrameInfo().getStackSize(); + Offset += MFI.getStackSize(); } else if (RI->needsStackRealignment(MF) && !MFI.isFixedObjectIndex(FI)) { // If the stack was realigned, the frame pointer is set in order to allow // SP to be restored, so we need another base register to record the stack @@ -339,13 +498,20 @@ int RISCVFrameLowering::getFrameIndexReference(const MachineFunction &MF, FrameReg = RISCVABI::getBPReg(); else FrameReg = RISCV::X2; - Offset += MF.getFrameInfo().getStackSize(); + Offset += MFI.getStackSize(); + if (FI < 0) + Offset += RVFI->getLibCallStackSize(); } else { FrameReg = RI->getFrameRegister(MF); - if (hasFP(MF)) + if (hasFP(MF)) { Offset += RVFI->getVarArgsSaveSize(); - else - Offset += MF.getFrameInfo().getStackSize(); + if (FI >= 0) + Offset -= RVFI->getLibCallStackSize(); + } else { + Offset += MFI.getStackSize(); + if (FI < 0) + Offset += RVFI->getLibCallStackSize(); + } } return Offset; } @@ -407,8 +573,8 @@ void RISCVFrameLowering::processFunctionBeforeFrameFinalized( // still needs an emergency spill slot for branch relaxation. This case // would currently be missed. if (!isInt<11>(MFI.estimateStackSize(MF))) { - int RegScavFI = MFI.CreateStackObject( - RegInfo->getSpillSize(*RC), RegInfo->getSpillAlignment(*RC), false); + int RegScavFI = MFI.CreateStackObject(RegInfo->getSpillSize(*RC), + RegInfo->getSpillAlign(*RC), false); RS->addScavengingFrameIndex(RegScavFI); } } @@ -461,16 +627,17 @@ MachineBasicBlock::iterator RISCVFrameLowering::eliminateCallFramePseudoInstr( // add sp,sp,-64 uint64_t RISCVFrameLowering::getFirstSPAdjustAmount(const MachineFunction &MF) const { + const auto *RVFI = MF.getInfo<RISCVMachineFunctionInfo>(); const MachineFrameInfo &MFI = MF.getFrameInfo(); const std::vector<CalleeSavedInfo> &CSI = MFI.getCalleeSavedInfo(); uint64_t StackSize = MFI.getStackSize(); - uint64_t StackAlign = getStackAlignment(); - // FIXME: Disable SplitSPAdjust if save-restore libcall enabled when the patch - // landing. The callee saved registers will be pushed by the - // save-restore libcalls, so we don't have to split the SP adjustment - // in this case. - // + // Disable SplitSPAdjust if save-restore libcall used. The callee saved + // registers will be pushed by the save-restore libcalls, so we don't have to + // split the SP adjustment in this case. + if (RVFI->getLibCallStackSize()) + return 0; + // Return the FirstSPAdjustAmount if the StackSize can not fit in signed // 12-bit and there exists a callee saved register need to be pushed. if (!isInt<12>(StackSize) && (CSI.size() > 0)) { @@ -480,7 +647,130 @@ RISCVFrameLowering::getFirstSPAdjustAmount(const MachineFunction &MF) const { // load/store instruction and we have to stick with the stack alignment. // 2048 is 16-byte alignment. The stack alignment for RV32 and RV64 is 16, // for RV32E is 4. So (2048 - StackAlign) will satisfy the stack alignment. - return 2048 - StackAlign; + return 2048 - getStackAlign().value(); } return 0; } + +bool RISCVFrameLowering::spillCalleeSavedRegisters( + MachineBasicBlock &MBB, MachineBasicBlock::iterator MI, + ArrayRef<CalleeSavedInfo> CSI, const TargetRegisterInfo *TRI) const { + if (CSI.empty()) + return true; + + MachineFunction *MF = MBB.getParent(); + const TargetInstrInfo &TII = *MF->getSubtarget().getInstrInfo(); + DebugLoc DL; + if (MI != MBB.end() && !MI->isDebugInstr()) + DL = MI->getDebugLoc(); + + const char *SpillLibCall = getSpillLibCallName(*MF, CSI); + if (SpillLibCall) { + // Add spill libcall via non-callee-saved register t0. + BuildMI(MBB, MI, DL, TII.get(RISCV::PseudoCALLReg), RISCV::X5) + .addExternalSymbol(SpillLibCall, RISCVII::MO_CALL) + .setMIFlag(MachineInstr::FrameSetup); + + // Add registers spilled in libcall as liveins. + for (auto &CS : CSI) + MBB.addLiveIn(CS.getReg()); + } + + // Manually spill values not spilled by libcall. + const auto &NonLibcallCSI = getNonLibcallCSI(CSI); + for (auto &CS : NonLibcallCSI) { + // Insert the spill to the stack frame. + Register Reg = CS.getReg(); + const TargetRegisterClass *RC = TRI->getMinimalPhysRegClass(Reg); + TII.storeRegToStackSlot(MBB, MI, Reg, true, CS.getFrameIdx(), RC, TRI); + } + + return true; +} + +bool RISCVFrameLowering::restoreCalleeSavedRegisters( + MachineBasicBlock &MBB, MachineBasicBlock::iterator MI, + MutableArrayRef<CalleeSavedInfo> CSI, const TargetRegisterInfo *TRI) const { + if (CSI.empty()) + return true; + + MachineFunction *MF = MBB.getParent(); + const TargetInstrInfo &TII = *MF->getSubtarget().getInstrInfo(); + DebugLoc DL; + if (MI != MBB.end() && !MI->isDebugInstr()) + DL = MI->getDebugLoc(); + + // Manually restore values not restored by libcall. Insert in reverse order. + // loadRegFromStackSlot can insert multiple instructions. + const auto &NonLibcallCSI = getNonLibcallCSI(CSI); + for (auto &CS : reverse(NonLibcallCSI)) { + Register Reg = CS.getReg(); + const TargetRegisterClass *RC = TRI->getMinimalPhysRegClass(Reg); + TII.loadRegFromStackSlot(MBB, MI, Reg, CS.getFrameIdx(), RC, TRI); + assert(MI != MBB.begin() && "loadRegFromStackSlot didn't insert any code!"); + } + + const char *RestoreLibCall = getRestoreLibCallName(*MF, CSI); + if (RestoreLibCall) { + // Add restore libcall via tail call. + MachineBasicBlock::iterator NewMI = + BuildMI(MBB, MI, DL, TII.get(RISCV::PseudoTAIL)) + .addExternalSymbol(RestoreLibCall, RISCVII::MO_CALL) + .setMIFlag(MachineInstr::FrameDestroy); + + // Remove trailing returns, since the terminator is now a tail call to the + // restore function. + if (MI != MBB.end() && MI->getOpcode() == RISCV::PseudoRET) { + NewMI->copyImplicitOps(*MF, *MI); + MI->eraseFromParent(); + } + } + + return true; +} + +bool RISCVFrameLowering::canUseAsPrologue(const MachineBasicBlock &MBB) const { + MachineBasicBlock *TmpMBB = const_cast<MachineBasicBlock *>(&MBB); + const MachineFunction *MF = MBB.getParent(); + const auto *RVFI = MF->getInfo<RISCVMachineFunctionInfo>(); + + if (!RVFI->useSaveRestoreLibCalls(*MF)) + return true; + + // Inserting a call to a __riscv_save libcall requires the use of the register + // t0 (X5) to hold the return address. Therefore if this register is already + // used we can't insert the call. + + RegScavenger RS; + RS.enterBasicBlock(*TmpMBB); + return !RS.isRegUsed(RISCV::X5); +} + +bool RISCVFrameLowering::canUseAsEpilogue(const MachineBasicBlock &MBB) const { + const MachineFunction *MF = MBB.getParent(); + MachineBasicBlock *TmpMBB = const_cast<MachineBasicBlock *>(&MBB); + const auto *RVFI = MF->getInfo<RISCVMachineFunctionInfo>(); + + if (!RVFI->useSaveRestoreLibCalls(*MF)) + return true; + + // Using the __riscv_restore libcalls to restore CSRs requires a tail call. + // This means if we still need to continue executing code within this function + // the restore cannot take place in this basic block. + + if (MBB.succ_size() > 1) + return false; + + MachineBasicBlock *SuccMBB = + MBB.succ_empty() ? TmpMBB->getFallThrough() : *MBB.succ_begin(); + + // Doing a tail call should be safe if there are no successors, because either + // we have a returning block or the end of the block is unreachable, so the + // restore will be eliminated regardless. + if (!SuccMBB) + return true; + + // The successor can only contain a return, since we would effectively be + // replacing the successor with our own tail return at the end of our block. + return SuccMBB->isReturnBlock() && SuccMBB->size() == 1; +} diff --git a/llvm/lib/Target/RISCV/RISCVFrameLowering.h b/llvm/lib/Target/RISCV/RISCVFrameLowering.h index 3a16cf93cf10..1517c847a04c 100644 --- a/llvm/lib/Target/RISCV/RISCVFrameLowering.h +++ b/llvm/lib/Target/RISCV/RISCVFrameLowering.h @@ -30,7 +30,7 @@ public: void emitEpilogue(MachineFunction &MF, MachineBasicBlock &MBB) const override; int getFrameIndexReference(const MachineFunction &MF, int FI, - unsigned &FrameReg) const override; + Register &FrameReg) const override; void determineCalleeSaves(MachineFunction &MF, BitVector &SavedRegs, RegScavenger *RS) const override; @@ -46,12 +46,24 @@ public: MachineBasicBlock::iterator eliminateCallFramePseudoInstr(MachineFunction &MF, MachineBasicBlock &MBB, MachineBasicBlock::iterator MI) const override; + bool spillCalleeSavedRegisters(MachineBasicBlock &MBB, + MachineBasicBlock::iterator MI, + ArrayRef<CalleeSavedInfo> CSI, + const TargetRegisterInfo *TRI) const override; + bool + restoreCalleeSavedRegisters(MachineBasicBlock &MBB, + MachineBasicBlock::iterator MI, + MutableArrayRef<CalleeSavedInfo> CSI, + const TargetRegisterInfo *TRI) const override; // Get the first stack adjustment amount for SplitSPAdjust. // Return 0 if we don't want to to split the SP adjustment in prologue and // epilogue. uint64_t getFirstSPAdjustAmount(const MachineFunction &MF) const; + bool canUseAsPrologue(const MachineBasicBlock &MBB) const override; + bool canUseAsEpilogue(const MachineBasicBlock &MBB) const override; + protected: const RISCVSubtarget &STI; diff --git a/llvm/lib/Target/RISCV/RISCVISelDAGToDAG.cpp b/llvm/lib/Target/RISCV/RISCVISelDAGToDAG.cpp index f66d06c20e37..a0ae05081adc 100644 --- a/llvm/lib/Target/RISCV/RISCVISelDAGToDAG.cpp +++ b/llvm/lib/Target/RISCV/RISCVISelDAGToDAG.cpp @@ -10,55 +10,19 @@ // //===----------------------------------------------------------------------===// +#include "RISCVISelDAGToDAG.h" #include "MCTargetDesc/RISCVMCTargetDesc.h" -#include "RISCV.h" -#include "RISCVTargetMachine.h" #include "Utils/RISCVMatInt.h" #include "llvm/CodeGen/MachineFrameInfo.h" -#include "llvm/CodeGen/SelectionDAGISel.h" +#include "llvm/Support/Alignment.h" #include "llvm/Support/Debug.h" #include "llvm/Support/MathExtras.h" #include "llvm/Support/raw_ostream.h" + using namespace llvm; #define DEBUG_TYPE "riscv-isel" -// RISCV-specific code to select RISCV machine instructions for -// SelectionDAG operations. -namespace { -class RISCVDAGToDAGISel final : public SelectionDAGISel { - const RISCVSubtarget *Subtarget = nullptr; - -public: - explicit RISCVDAGToDAGISel(RISCVTargetMachine &TargetMachine) - : SelectionDAGISel(TargetMachine) {} - - StringRef getPassName() const override { - return "RISCV DAG->DAG Pattern Instruction Selection"; - } - - bool runOnMachineFunction(MachineFunction &MF) override { - Subtarget = &MF.getSubtarget<RISCVSubtarget>(); - return SelectionDAGISel::runOnMachineFunction(MF); - } - - void PostprocessISelDAG() override; - - void Select(SDNode *Node) override; - - bool SelectInlineAsmMemoryOperand(const SDValue &Op, unsigned ConstraintID, - std::vector<SDValue> &OutOps) override; - - bool SelectAddrFI(SDValue Addr, SDValue &Base); - -// Include the pieces autogenerated from the target description. -#include "RISCVGenDAGISel.inc" - -private: - void doPeepholeLoadStoreADDI(); -}; -} - void RISCVDAGToDAGISel::PostprocessISelDAG() { doPeepholeLoadStoreADDI(); } @@ -111,6 +75,30 @@ void RISCVDAGToDAGISel::Select(SDNode *Node) { EVT VT = Node->getValueType(0); switch (Opcode) { + case ISD::ADD: { + // Optimize (add r, imm) to (addi (addi r, imm0) imm1) if applicable. The + // immediate must be in specific ranges and have a single use. + if (auto *ConstOp = dyn_cast<ConstantSDNode>(Node->getOperand(1))) { + if (!(ConstOp->hasOneUse())) + break; + // The imm must be in range [-4096,-2049] or [2048,4094]. + int64_t Imm = ConstOp->getSExtValue(); + if (!(-4096 <= Imm && Imm <= -2049) && !(2048 <= Imm && Imm <= 4094)) + break; + // Break the imm to imm0+imm1. + SDLoc DL(Node); + EVT VT = Node->getValueType(0); + const SDValue ImmOp0 = CurDAG->getTargetConstant(Imm - Imm / 2, DL, VT); + const SDValue ImmOp1 = CurDAG->getTargetConstant(Imm / 2, DL, VT); + auto *NodeAddi0 = CurDAG->getMachineNode(RISCV::ADDI, DL, VT, + Node->getOperand(0), ImmOp0); + auto *NodeAddi1 = CurDAG->getMachineNode(RISCV::ADDI, DL, VT, + SDValue(NodeAddi0, 0), ImmOp1); + ReplaceNode(Node, NodeAddi1); + return; + } + break; + } case ISD::Constant: { auto ConstNode = cast<ConstantSDNode>(Node); if (VT == XLenVT && ConstNode->isNullValue()) { @@ -197,8 +185,9 @@ bool RISCVDAGToDAGISel::SelectAddrFI(SDValue Addr, SDValue &Base) { } // Merge an ADDI into the offset of a load/store instruction where possible. -// (load (add base, off), 0) -> (load base, off) -// (store val, (add base, off)) -> (store val, base, off) +// (load (addi base, off1), off2) -> (load base, off1+off2) +// (store val, (addi base, off1), off2) -> (store val, base, off1+off2) +// This is possible when off1+off2 fits a 12-bit immediate. void RISCVDAGToDAGISel::doPeepholeLoadStoreADDI() { SelectionDAG::allnodes_iterator Position(CurDAG->getRoot().getNode()); ++Position; @@ -239,10 +228,7 @@ void RISCVDAGToDAGISel::doPeepholeLoadStoreADDI() { break; } - // Currently, the load/store offset must be 0 to be considered for this - // peephole optimisation. - if (!isa<ConstantSDNode>(N->getOperand(OffsetOpIdx)) || - N->getConstantOperandVal(OffsetOpIdx) != 0) + if (!isa<ConstantSDNode>(N->getOperand(OffsetOpIdx))) continue; SDValue Base = N->getOperand(BaseOpIdx); @@ -252,14 +238,39 @@ void RISCVDAGToDAGISel::doPeepholeLoadStoreADDI() { continue; SDValue ImmOperand = Base.getOperand(1); + uint64_t Offset2 = N->getConstantOperandVal(OffsetOpIdx); if (auto Const = dyn_cast<ConstantSDNode>(ImmOperand)) { - ImmOperand = CurDAG->getTargetConstant( - Const->getSExtValue(), SDLoc(ImmOperand), ImmOperand.getValueType()); + int64_t Offset1 = Const->getSExtValue(); + int64_t CombinedOffset = Offset1 + Offset2; + if (!isInt<12>(CombinedOffset)) + continue; + ImmOperand = CurDAG->getTargetConstant(CombinedOffset, SDLoc(ImmOperand), + ImmOperand.getValueType()); } else if (auto GA = dyn_cast<GlobalAddressSDNode>(ImmOperand)) { + // If the off1 in (addi base, off1) is a global variable's address (its + // low part, really), then we can rely on the alignment of that variable + // to provide a margin of safety before off1 can overflow the 12 bits. + // Check if off2 falls within that margin; if so off1+off2 can't overflow. + const DataLayout &DL = CurDAG->getDataLayout(); + Align Alignment = GA->getGlobal()->getPointerAlignment(DL); + if (Offset2 != 0 && Alignment <= Offset2) + continue; + int64_t Offset1 = GA->getOffset(); + int64_t CombinedOffset = Offset1 + Offset2; ImmOperand = CurDAG->getTargetGlobalAddress( GA->getGlobal(), SDLoc(ImmOperand), ImmOperand.getValueType(), - GA->getOffset(), GA->getTargetFlags()); + CombinedOffset, GA->getTargetFlags()); + } else if (auto CP = dyn_cast<ConstantPoolSDNode>(ImmOperand)) { + // Ditto. + Align Alignment = CP->getAlign(); + if (Offset2 != 0 && Alignment <= Offset2) + continue; + int64_t Offset1 = CP->getOffset(); + int64_t CombinedOffset = Offset1 + Offset2; + ImmOperand = CurDAG->getTargetConstantPool( + CP->getConstVal(), ImmOperand.getValueType(), CP->getAlign(), + CombinedOffset, CP->getTargetFlags()); } else { continue; } diff --git a/llvm/lib/Target/RISCV/RISCVISelDAGToDAG.h b/llvm/lib/Target/RISCV/RISCVISelDAGToDAG.h new file mode 100644 index 000000000000..dcf733ec3675 --- /dev/null +++ b/llvm/lib/Target/RISCV/RISCVISelDAGToDAG.h @@ -0,0 +1,56 @@ +//===---- RISCVISelDAGToDAG.h - A dag to dag inst selector for RISCV ------===// +// +// 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 defines an instruction selector for the RISCV target. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_RISCV_RISCVISELDAGTODAG_H +#define LLVM_LIB_TARGET_RISCV_RISCVISELDAGTODAG_H + +#include "RISCV.h" +#include "RISCVTargetMachine.h" +#include "llvm/CodeGen/SelectionDAGISel.h" + +// RISCV-specific code to select RISCV machine instructions for +// SelectionDAG operations. +namespace llvm { +class RISCVDAGToDAGISel : public SelectionDAGISel { + const RISCVSubtarget *Subtarget = nullptr; + +public: + explicit RISCVDAGToDAGISel(RISCVTargetMachine &TargetMachine) + : SelectionDAGISel(TargetMachine) {} + + StringRef getPassName() const override { + return "RISCV DAG->DAG Pattern Instruction Selection"; + } + + bool runOnMachineFunction(MachineFunction &MF) override { + Subtarget = &MF.getSubtarget<RISCVSubtarget>(); + return SelectionDAGISel::runOnMachineFunction(MF); + } + + void PostprocessISelDAG() override; + + void Select(SDNode *Node) override; + + bool SelectInlineAsmMemoryOperand(const SDValue &Op, unsigned ConstraintID, + std::vector<SDValue> &OutOps) override; + + bool SelectAddrFI(SDValue Addr, SDValue &Base); + +// Include the pieces autogenerated from the target description. +#include "RISCVGenDAGISel.inc" + +private: + void doPeepholeLoadStoreADDI(); +}; +} + +#endif diff --git a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp index 5a2cffbc824c..91fc69b5bc10 100644 --- a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp +++ b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp @@ -33,6 +33,7 @@ #include "llvm/IR/IntrinsicsRISCV.h" #include "llvm/Support/Debug.h" #include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/MathExtras.h" #include "llvm/Support/raw_ostream.h" using namespace llvm; @@ -197,6 +198,14 @@ RISCVTargetLowering::RISCVTargetLowering(const TargetMachine &TM, setTruncStoreAction(MVT::f64, MVT::f16, Expand); } + if (Subtarget.is64Bit() && + !(Subtarget.hasStdExtD() || Subtarget.hasStdExtF())) { + setOperationAction(ISD::FP_TO_UINT, MVT::i32, Custom); + setOperationAction(ISD::FP_TO_SINT, MVT::i32, Custom); + setOperationAction(ISD::STRICT_FP_TO_UINT, MVT::i32, Custom); + setOperationAction(ISD::STRICT_FP_TO_SINT, MVT::i32, Custom); + } + setOperationAction(ISD::GlobalAddress, XLenVT, Custom); setOperationAction(ISD::BlockAddress, XLenVT, Custom); setOperationAction(ISD::ConstantPool, XLenVT, Custom); @@ -210,6 +219,7 @@ RISCVTargetLowering::RISCVTargetLowering(const TargetMachine &TM, setOperationAction(ISD::TRAP, MVT::Other, Legal); setOperationAction(ISD::DEBUGTRAP, MVT::Other, Legal); + setOperationAction(ISD::INTRINSIC_WO_CHAIN, MVT::Other, Custom); if (Subtarget.hasStdExtA()) { setMaxAtomicSizeInBitsSupported(Subtarget.getXLen()); @@ -227,6 +237,12 @@ RISCVTargetLowering::RISCVTargetLowering(const TargetMachine &TM, // Effectively disable jump table generation. setMinimumJumpTableEntries(INT_MAX); + + // Jumps are expensive, compared to logic + setJumpIsExpensive(); + + // We can use any register for comparisons + setHasMultipleConditionRegisters(); } EVT RISCVTargetLowering::getSetCCResultType(const DataLayout &DL, LLVMContext &, @@ -336,6 +352,17 @@ bool RISCVTargetLowering::isSExtCheaperThanZExt(EVT SrcVT, EVT DstVT) const { return Subtarget.is64Bit() && SrcVT == MVT::i32 && DstVT == MVT::i64; } +bool RISCVTargetLowering::isFPImmLegal(const APFloat &Imm, EVT VT, + bool ForCodeSize) const { + if (VT == MVT::f32 && !Subtarget.hasStdExtF()) + return false; + if (VT == MVT::f64 && !Subtarget.hasStdExtD()) + return false; + if (Imm.isNegZero()) + return false; + return Imm.isZero(); +} + bool RISCVTargetLowering::hasBitPreservingFPLogic(EVT VT) const { return (VT == MVT::f32 && Subtarget.hasStdExtF()) || (VT == MVT::f64 && Subtarget.hasStdExtD()); @@ -418,6 +445,8 @@ SDValue RISCVTargetLowering::LowerOperation(SDValue Op, SDValue FPConv = DAG.getNode(RISCVISD::FMV_W_X_RV64, DL, MVT::f32, NewOp0); return FPConv; } + case ISD::INTRINSIC_WO_CHAIN: + return LowerINTRINSIC_WO_CHAIN(Op, DAG); } } @@ -434,7 +463,7 @@ static SDValue getTargetNode(BlockAddressSDNode *N, SDLoc DL, EVT Ty, static SDValue getTargetNode(ConstantPoolSDNode *N, SDLoc DL, EVT Ty, SelectionDAG &DAG, unsigned Flags) { - return DAG.getTargetConstantPool(N->getConstVal(), Ty, N->getAlignment(), + return DAG.getTargetConstantPool(N->getConstVal(), Ty, N->getAlign(), N->getOffset(), Flags); } @@ -821,6 +850,20 @@ SDValue RISCVTargetLowering::lowerShiftRightParts(SDValue Op, SelectionDAG &DAG, return DAG.getMergeValues(Parts, DL); } +SDValue RISCVTargetLowering::LowerINTRINSIC_WO_CHAIN(SDValue Op, + SelectionDAG &DAG) const { + unsigned IntNo = cast<ConstantSDNode>(Op.getOperand(0))->getZExtValue(); + SDLoc DL(Op); + switch (IntNo) { + default: + return SDValue(); // Don't custom lower most intrinsics. + case Intrinsic::thread_pointer: { + EVT PtrVT = getPointerTy(DAG.getDataLayout()); + return DAG.getRegister(RISCV::X4, PtrVT); + } + } +} + // Returns the opcode of the target-specific SDNode that implements the 32-bit // form of the given Opcode. static RISCVISD::NodeType getRISCVWOpcode(unsigned Opcode) { @@ -876,6 +919,32 @@ void RISCVTargetLowering::ReplaceNodeResults(SDNode *N, switch (N->getOpcode()) { default: llvm_unreachable("Don't know how to custom type legalize this operation!"); + case ISD::STRICT_FP_TO_SINT: + case ISD::STRICT_FP_TO_UINT: + case ISD::FP_TO_SINT: + case ISD::FP_TO_UINT: { + bool IsStrict = N->isStrictFPOpcode(); + assert(N->getValueType(0) == MVT::i32 && Subtarget.is64Bit() && + "Unexpected custom legalisation"); + SDValue Op0 = IsStrict ? N->getOperand(1) : N->getOperand(0); + RTLIB::Libcall LC; + if (N->getOpcode() == ISD::FP_TO_SINT || + N->getOpcode() == ISD::STRICT_FP_TO_SINT) + LC = RTLIB::getFPTOSINT(Op0.getValueType(), N->getValueType(0)); + else + LC = RTLIB::getFPTOUINT(Op0.getValueType(), N->getValueType(0)); + MakeLibCallOptions CallOptions; + EVT OpVT = Op0.getValueType(); + CallOptions.setTypeListBeforeSoften(OpVT, N->getValueType(0), true); + SDValue Chain = IsStrict ? N->getOperand(0) : SDValue(); + SDValue Result; + std::tie(Result, Chain) = + makeLibCall(DAG, LC, N->getValueType(0), Op0, CallOptions, DL, Chain); + Results.push_back(Result); + if (IsStrict) + Results.push_back(Chain); + break; + } case ISD::READCYCLECOUNTER: { assert(!Subtarget.is64Bit() && "READCYCLECOUNTER only has custom type legalization on riscv32"); @@ -884,8 +953,8 @@ void RISCVTargetLowering::ReplaceNodeResults(SDNode *N, SDValue RCW = DAG.getNode(RISCVISD::READ_CYCLE_WIDE, DL, VTs, N->getOperand(0)); - Results.push_back(RCW); - Results.push_back(RCW.getValue(1)); + Results.push_back( + DAG.getNode(ISD::BUILD_PAIR, DL, MVT::i64, RCW, RCW.getValue(1))); Results.push_back(RCW.getValue(2)); break; } @@ -1172,13 +1241,13 @@ static MachineBasicBlock *emitSplitF64Pseudo(MachineInstr &MI, Register HiReg = MI.getOperand(1).getReg(); Register SrcReg = MI.getOperand(2).getReg(); const TargetRegisterClass *SrcRC = &RISCV::FPR64RegClass; - int FI = MF.getInfo<RISCVMachineFunctionInfo>()->getMoveF64FrameIndex(); + int FI = MF.getInfo<RISCVMachineFunctionInfo>()->getMoveF64FrameIndex(MF); TII.storeRegToStackSlot(*BB, MI, SrcReg, MI.getOperand(2).isKill(), FI, SrcRC, RI); MachineMemOperand *MMO = MF.getMachineMemOperand(MachinePointerInfo::getFixedStack(MF, FI), - MachineMemOperand::MOLoad, 8, 8); + MachineMemOperand::MOLoad, 8, Align(8)); BuildMI(*BB, MI, DL, TII.get(RISCV::LW), LoReg) .addFrameIndex(FI) .addImm(0) @@ -1204,11 +1273,11 @@ static MachineBasicBlock *emitBuildPairF64Pseudo(MachineInstr &MI, Register LoReg = MI.getOperand(1).getReg(); Register HiReg = MI.getOperand(2).getReg(); const TargetRegisterClass *DstRC = &RISCV::FPR64RegClass; - int FI = MF.getInfo<RISCVMachineFunctionInfo>()->getMoveF64FrameIndex(); + int FI = MF.getInfo<RISCVMachineFunctionInfo>()->getMoveF64FrameIndex(MF); MachineMemOperand *MMO = MF.getMachineMemOperand(MachinePointerInfo::getFixedStack(MF, FI), - MachineMemOperand::MOStore, 8, 8); + MachineMemOperand::MOStore, 8, Align(8)); BuildMI(*BB, MI, DL, TII.get(RISCV::SW)) .addReg(LoReg, getKillRegState(MI.getOperand(1).isKill())) .addFrameIndex(FI) @@ -1430,14 +1499,15 @@ static bool CC_RISCVAssign2XLen(unsigned XLen, CCState &State, CCValAssign VA1, VA1.getLocVT(), CCValAssign::Full)); } else { // Both halves must be passed on the stack, with proper alignment. - unsigned StackAlign = std::max(XLenInBytes, ArgFlags1.getOrigAlign()); + Align StackAlign = + std::max(Align(XLenInBytes), ArgFlags1.getNonZeroOrigAlign()); State.addLoc( CCValAssign::getMem(VA1.getValNo(), VA1.getValVT(), State.AllocateStack(XLenInBytes, StackAlign), VA1.getLocVT(), CCValAssign::Full)); State.addLoc(CCValAssign::getMem( - ValNo2, ValVT2, State.AllocateStack(XLenInBytes, XLenInBytes), LocVT2, - CCValAssign::Full)); + ValNo2, ValVT2, State.AllocateStack(XLenInBytes, Align(XLenInBytes)), + LocVT2, CCValAssign::Full)); return false; } @@ -1448,8 +1518,8 @@ static bool CC_RISCVAssign2XLen(unsigned XLen, CCState &State, CCValAssign VA1, } else { // The second half is passed via the stack, without additional alignment. State.addLoc(CCValAssign::getMem( - ValNo2, ValVT2, State.AllocateStack(XLenInBytes, XLenInBytes), LocVT2, - CCValAssign::Full)); + ValNo2, ValVT2, State.AllocateStack(XLenInBytes, Align(XLenInBytes)), + LocVT2, CCValAssign::Full)); } return false; @@ -1517,7 +1587,7 @@ static bool CC_RISCV(const DataLayout &DL, RISCVABI::ABI ABI, unsigned ValNo, // original type is larger than 2*XLEN, so the register alignment rule does // not apply. unsigned TwoXLenInBytes = (2 * XLen) / 8; - if (!IsFixed && ArgFlags.getOrigAlign() == TwoXLenInBytes && + if (!IsFixed && ArgFlags.getNonZeroOrigAlign() == TwoXLenInBytes && DL.getTypeAllocSize(OrigTy) == TwoXLenInBytes) { unsigned RegIdx = State.getFirstUnallocated(ArgGPRs); // Skip 'odd' register if necessary. @@ -1544,13 +1614,13 @@ static bool CC_RISCV(const DataLayout &DL, RISCVABI::ABI ABI, unsigned ValNo, Register Reg = State.AllocateReg(ArgGPRs); LocVT = MVT::i32; if (!Reg) { - unsigned StackOffset = State.AllocateStack(8, 8); + unsigned StackOffset = State.AllocateStack(8, Align(8)); State.addLoc( CCValAssign::getMem(ValNo, ValVT, StackOffset, LocVT, LocInfo)); return false; } if (!State.AllocateReg(ArgGPRs)) - State.AllocateStack(4, 4); + State.AllocateStack(4, Align(4)); State.addLoc(CCValAssign::getReg(ValNo, ValVT, Reg, LocVT, LocInfo)); return false; } @@ -1590,7 +1660,8 @@ static bool CC_RISCV(const DataLayout &DL, RISCVABI::ABI ABI, unsigned ValNo, Reg = State.AllocateReg(ArgFPR64s, ArgFPR32s); else Reg = State.AllocateReg(ArgGPRs); - unsigned StackOffset = Reg ? 0 : State.AllocateStack(XLen / 8, XLen / 8); + unsigned StackOffset = + Reg ? 0 : State.AllocateStack(XLen / 8, Align(XLen / 8)); // If we reach this point and PendingLocs is non-empty, we must be at the // end of a split argument that must be passed indirectly. @@ -1645,7 +1716,7 @@ void RISCVTargetLowering::analyzeInputArgs( RISCVABI::ABI ABI = MF.getSubtarget<RISCVSubtarget>().getTargetABI(); if (CC_RISCV(MF.getDataLayout(), ABI, i, ArgVT, ArgVT, CCValAssign::Full, - ArgFlags, CCInfo, /*IsRet=*/true, IsRet, ArgTy)) { + ArgFlags, CCInfo, /*IsFixed=*/true, IsRet, ArgTy)) { LLVM_DEBUG(dbgs() << "InputArg #" << i << " has unhandled type " << EVT(ArgVT).getEVTString() << '\n'); llvm_unreachable(nullptr); @@ -1859,13 +1930,13 @@ static bool CC_RISCV_FastCC(unsigned ValNo, MVT ValVT, MVT LocVT, } if (LocVT == MVT::i32 || LocVT == MVT::f32) { - unsigned Offset4 = State.AllocateStack(4, 4); + unsigned Offset4 = State.AllocateStack(4, Align(4)); State.addLoc(CCValAssign::getMem(ValNo, ValVT, Offset4, LocVT, LocInfo)); return false; } if (LocVT == MVT::i64 || LocVT == MVT::f64) { - unsigned Offset5 = State.AllocateStack(8, 8); + unsigned Offset5 = State.AllocateStack(8, Align(8)); State.addLoc(CCValAssign::getMem(ValNo, ValVT, Offset5, LocVT, LocInfo)); return false; } @@ -2124,7 +2195,7 @@ SDValue RISCVTargetLowering::LowerCall(CallLoweringInfo &CLI, if (IsTailCall) ++NumTailCalls; - else if (CLI.CS && CLI.CS.isMustTailCall()) + else if (CLI.CB && CLI.CB->isMustTailCall()) report_fatal_error("failed to perform tail call elimination on a call " "site marked musttail"); @@ -2140,17 +2211,17 @@ SDValue RISCVTargetLowering::LowerCall(CallLoweringInfo &CLI, SDValue Arg = OutVals[i]; unsigned Size = Flags.getByValSize(); - unsigned Align = Flags.getByValAlign(); + Align Alignment = Flags.getNonZeroByValAlign(); - int FI = MF.getFrameInfo().CreateStackObject(Size, Align, /*isSS=*/false); + int FI = + MF.getFrameInfo().CreateStackObject(Size, Alignment, /*isSS=*/false); SDValue FIPtr = DAG.getFrameIndex(FI, getPointerTy(DAG.getDataLayout())); SDValue SizeNode = DAG.getConstant(Size, DL, XLenVT); - Chain = DAG.getMemcpy(Chain, DL, FIPtr, Arg, SizeNode, Align, + Chain = DAG.getMemcpy(Chain, DL, FIPtr, Arg, SizeNode, Alignment, /*IsVolatile=*/false, - /*AlwaysInline=*/false, - IsTailCall, MachinePointerInfo(), - MachinePointerInfo()); + /*AlwaysInline=*/false, IsTailCall, + MachinePointerInfo(), MachinePointerInfo()); ByValArgs.push_back(FIPtr); } @@ -2325,6 +2396,7 @@ SDValue RISCVTargetLowering::LowerCall(CallLoweringInfo &CLI, } Chain = DAG.getNode(RISCVISD::CALL, DL, NodeTys, Ops); + DAG.addNoMergeSiteInfo(Chain.getNode(), CLI.NoMerge); Glue = Chain.getValue(1); // Mark the end of the call, which is glued to the call itself. @@ -2494,6 +2566,10 @@ void RISCVTargetLowering::validateCCReservedRegs( F, "Argument register required, but has been reserved."}); } +bool RISCVTargetLowering::mayBeEmittedAsTailCall(const CallInst *CI) const { + return CI->isTailCall(); +} + const char *RISCVTargetLowering::getTargetNodeName(unsigned Opcode) const { switch ((RISCVISD::NodeType)Opcode) { case RISCVISD::FIRST_NUMBER: @@ -2883,12 +2959,12 @@ Value *RISCVTargetLowering::emitMaskedAtomicCmpXchgIntrinsic( return Result; } -unsigned RISCVTargetLowering::getExceptionPointerRegister( +Register RISCVTargetLowering::getExceptionPointerRegister( const Constant *PersonalityFn) const { return RISCV::X10; } -unsigned RISCVTargetLowering::getExceptionSelectorRegister( +Register RISCVTargetLowering::getExceptionSelectorRegister( const Constant *PersonalityFn) const { return RISCV::X11; } @@ -2903,6 +2979,26 @@ bool RISCVTargetLowering::shouldExtendTypeInLibCall(EVT Type) const { return true; } +bool RISCVTargetLowering::decomposeMulByConstant(LLVMContext &Context, EVT VT, + SDValue C) const { + // Check integral scalar types. + if (VT.isScalarInteger()) { + // Do not perform the transformation on riscv32 with the M extension. + if (!Subtarget.is64Bit() && Subtarget.hasStdExtM()) + return false; + if (auto *ConstNode = dyn_cast<ConstantSDNode>(C.getNode())) { + if (ConstNode->getAPIntValue().getBitWidth() > 8 * sizeof(int64_t)) + return false; + int64_t Imm = ConstNode->getSExtValue(); + if (isPowerOf2_64(Imm + 1) || isPowerOf2_64(Imm - 1) || + isPowerOf2_64(1 - Imm) || isPowerOf2_64(-1 - Imm)) + return true; + } + } + + return false; +} + #define GET_REGISTER_MATCHER #include "RISCVGenAsmMatcher.inc" diff --git a/llvm/lib/Target/RISCV/RISCVISelLowering.h b/llvm/lib/Target/RISCV/RISCVISelLowering.h index b2ad75d67024..e420e879efc9 100644 --- a/llvm/lib/Target/RISCV/RISCVISelLowering.h +++ b/llvm/lib/Target/RISCV/RISCVISelLowering.h @@ -74,6 +74,8 @@ public: bool isTruncateFree(EVT SrcVT, EVT DstVT) const override; bool isZExtFree(SDValue Val, EVT VT2) const override; bool isSExtCheaperThanZExt(EVT SrcVT, EVT DstVT) const override; + bool isFPImmLegal(const APFloat &Imm, EVT VT, + bool ForCodeSize) const override; bool hasBitPreservingFPLogic(EVT VT) const override; @@ -114,6 +116,7 @@ public: bool convertSetCCLogicToBitwiseLogic(EVT VT) const override { return VT.isScalarInteger(); } + bool convertSelectOfConstantsToMath(EVT VT) const override { return true; } bool shouldInsertFencesForAtomic(const Instruction *I) const override { return isa<LoadInst>(I) || isa<StoreInst>(I); @@ -127,6 +130,10 @@ public: return ISD::SIGN_EXTEND; } + ISD::NodeType getExtendForAtomicCmpSwapArg() const override { + return ISD::SIGN_EXTEND; + } + bool shouldExpandShift(SelectionDAG &DAG, SDNode *N) const override { if (DAG.getMachineFunction().getFunction().hasMinSize()) return false; @@ -137,12 +144,12 @@ public: /// If a physical register, this returns the register that receives the /// exception address on entry to an EH pad. - unsigned + Register getExceptionPointerRegister(const Constant *PersonalityFn) const override; /// If a physical register, this returns the register that receives the /// exception typeid on entry to a landing pad. - unsigned + Register getExceptionSelectorRegister(const Constant *PersonalityFn) const override; bool shouldExtendTypeInLibCall(EVT Type) const override; @@ -154,13 +161,6 @@ public: Register getRegisterByName(const char *RegName, LLT VT, const MachineFunction &MF) const override; -private: - void analyzeInputArgs(MachineFunction &MF, CCState &CCInfo, - const SmallVectorImpl<ISD::InputArg> &Ins, - bool IsRet) const; - void analyzeOutputArgs(MachineFunction &MF, CCState &CCInfo, - const SmallVectorImpl<ISD::OutputArg> &Outs, - bool IsRet, CallLoweringInfo *CLI) const; // Lower incoming arguments, copy physregs into vregs SDValue LowerFormalArguments(SDValue Chain, CallingConv::ID CallConv, bool IsVarArg, @@ -177,10 +177,38 @@ private: SelectionDAG &DAG) const override; SDValue LowerCall(TargetLowering::CallLoweringInfo &CLI, SmallVectorImpl<SDValue> &InVals) const override; + bool shouldConvertConstantLoadToIntImm(const APInt &Imm, Type *Ty) const override { return true; } + bool mayBeEmittedAsTailCall(const CallInst *CI) const override; + bool shouldConsiderGEPOffsetSplit() const override { return true; } + + bool decomposeMulByConstant(LLVMContext &Context, EVT VT, + SDValue C) const override; + + TargetLowering::AtomicExpansionKind + shouldExpandAtomicRMWInIR(AtomicRMWInst *AI) const override; + Value *emitMaskedAtomicRMWIntrinsic(IRBuilder<> &Builder, AtomicRMWInst *AI, + Value *AlignedAddr, Value *Incr, + Value *Mask, Value *ShiftAmt, + AtomicOrdering Ord) const override; + TargetLowering::AtomicExpansionKind + shouldExpandAtomicCmpXchgInIR(AtomicCmpXchgInst *CI) const override; + Value *emitMaskedAtomicCmpXchgIntrinsic(IRBuilder<> &Builder, + AtomicCmpXchgInst *CI, + Value *AlignedAddr, Value *CmpVal, + Value *NewVal, Value *Mask, + AtomicOrdering Ord) const override; + +private: + void analyzeInputArgs(MachineFunction &MF, CCState &CCInfo, + const SmallVectorImpl<ISD::InputArg> &Ins, + bool IsRet) const; + void analyzeOutputArgs(MachineFunction &MF, CCState &CCInfo, + const SmallVectorImpl<ISD::OutputArg> &Outs, + bool IsRet, CallLoweringInfo *CLI) const; template <class NodeTy> SDValue getAddr(NodeTy *N, SelectionDAG &DAG, bool IsLocal = true) const; @@ -189,7 +217,6 @@ private: bool UseGOT) const; SDValue getDynamicTLSAddr(GlobalAddressSDNode *N, SelectionDAG &DAG) const; - bool shouldConsiderGEPOffsetSplit() const override { return true; } SDValue lowerGlobalAddress(SDValue Op, SelectionDAG &DAG) const; SDValue lowerBlockAddress(SDValue Op, SelectionDAG &DAG) const; SDValue lowerConstantPool(SDValue Op, SelectionDAG &DAG) const; @@ -200,24 +227,12 @@ private: SDValue lowerRETURNADDR(SDValue Op, SelectionDAG &DAG) const; SDValue lowerShiftLeftParts(SDValue Op, SelectionDAG &DAG) const; SDValue lowerShiftRightParts(SDValue Op, SelectionDAG &DAG, bool IsSRA) const; + SDValue LowerINTRINSIC_WO_CHAIN(SDValue Op, SelectionDAG &DAG) const; bool isEligibleForTailCallOptimization( CCState &CCInfo, CallLoweringInfo &CLI, MachineFunction &MF, const SmallVector<CCValAssign, 16> &ArgLocs) const; - TargetLowering::AtomicExpansionKind - shouldExpandAtomicRMWInIR(AtomicRMWInst *AI) const override; - virtual Value *emitMaskedAtomicRMWIntrinsic( - IRBuilder<> &Builder, AtomicRMWInst *AI, Value *AlignedAddr, Value *Incr, - Value *Mask, Value *ShiftAmt, AtomicOrdering Ord) const override; - TargetLowering::AtomicExpansionKind - shouldExpandAtomicCmpXchgInIR(AtomicCmpXchgInst *CI) const override; - virtual Value * - emitMaskedAtomicCmpXchgIntrinsic(IRBuilder<> &Builder, AtomicCmpXchgInst *CI, - Value *AlignedAddr, Value *CmpVal, - Value *NewVal, Value *Mask, - AtomicOrdering Ord) const override; - /// Generate error diagnostics if any register used by CC has been marked /// reserved. void validateCCReservedRegs( diff --git a/llvm/lib/Target/RISCV/RISCVInstrFormats.td b/llvm/lib/Target/RISCV/RISCVInstrFormats.td index 7229ebfe1db0..a47945a6a515 100644 --- a/llvm/lib/Target/RISCV/RISCVInstrFormats.td +++ b/llvm/lib/Target/RISCV/RISCVInstrFormats.td @@ -49,6 +49,19 @@ def InstFormatCB : InstFormat<15>; def InstFormatCJ : InstFormat<16>; def InstFormatOther : InstFormat<17>; +class RISCVVConstraint<bits<4> val> { + bits<4> Value = val; +} +def NoConstraint : RISCVVConstraint<0>; +def WidenV : RISCVVConstraint<1>; +def WidenW : RISCVVConstraint<2>; +def WidenCvt : RISCVVConstraint<3>; +def Narrow : RISCVVConstraint<4>; +def Iota : RISCVVConstraint<5>; +def SlideUp : RISCVVConstraint<6>; +def Vrgather : RISCVVConstraint<7>; +def Vcompress : RISCVVConstraint<8>; + // The following opcode names match those given in Table 19.1 in the // RISC-V User-level ISA specification ("RISC-V base opcode map"). class RISCVOpcode<bits<7> val> { @@ -71,6 +84,7 @@ def OPC_MSUB : RISCVOpcode<0b1000111>; def OPC_NMSUB : RISCVOpcode<0b1001011>; def OPC_NMADD : RISCVOpcode<0b1001111>; def OPC_OP_FP : RISCVOpcode<0b1010011>; +def OPC_OP_V : RISCVOpcode<0b1010111>; def OPC_BRANCH : RISCVOpcode<0b1100011>; def OPC_JALR : RISCVOpcode<0b1100111>; def OPC_JAL : RISCVOpcode<0b1101111>; @@ -99,11 +113,16 @@ class RVInst<dag outs, dag ins, string opcodestr, string argstr, let Pattern = pattern; let TSFlags{4-0} = format.Value; + + // Defaults + RISCVVConstraint RVVConstraint = NoConstraint; + let TSFlags{8-5} = RVVConstraint.Value; } // Pseudo instructions class Pseudo<dag outs, dag ins, list<dag> pattern, string opcodestr = "", string argstr = ""> - : RVInst<outs, ins, opcodestr, argstr, pattern, InstFormatPseudo> { + : RVInst<outs, ins, opcodestr, argstr, pattern, InstFormatPseudo>, + Sched<[]> { let isPseudo = 1; let isCodeGenOnly = 1; } diff --git a/llvm/lib/Target/RISCV/RISCVInstrFormatsV.td b/llvm/lib/Target/RISCV/RISCVInstrFormatsV.td new file mode 100644 index 000000000000..e5f154966ba6 --- /dev/null +++ b/llvm/lib/Target/RISCV/RISCVInstrFormatsV.td @@ -0,0 +1,300 @@ +//===-- RISCVInstrFormatsV.td - RISCV V 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 +// +//===----------------------------------------------------------------------===// +// +// This file describes the RISC-V V extension instruction formats. +// +//===----------------------------------------------------------------------===// + +class RISCVVFormat<bits<3> val> { + bits<3> Value = val; +} +def OPIVV : RISCVVFormat<0b000>; +def OPFVV : RISCVVFormat<0b001>; +def OPMVV : RISCVVFormat<0b010>; +def OPIVI : RISCVVFormat<0b011>; +def OPIVX : RISCVVFormat<0b100>; +def OPFVF : RISCVVFormat<0b101>; +def OPMVX : RISCVVFormat<0b110>; + +class RISCVMOP<bits<3> val> { + bits<3> Value = val; +} +def MOPLDUnitStrideU : RISCVMOP<0b000>; +def MOPLDStridedU : RISCVMOP<0b010>; +def MOPLDIndexedU : RISCVMOP<0b011>; +def MOPLDUnitStrideS : RISCVMOP<0b100>; +def MOPLDStridedS : RISCVMOP<0b110>; +def MOPLDIndexedS : RISCVMOP<0b111>; + +def MOPSTUnitStride : RISCVMOP<0b000>; +def MOPSTStrided : RISCVMOP<0b010>; +def MOPSTIndexedOrder: RISCVMOP<0b011>; +def MOPSTIndexedUnOrd: RISCVMOP<0b111>; + +class RISCVLSUMOP<bits<5> val> { + bits<5> Value = val; +} +def LUMOPUnitStride : RISCVLSUMOP<0b00000>; +def LUMOPUnitStrideWholeReg : RISCVLSUMOP<0b01000>; +def LUMOPUnitStrideFF: RISCVLSUMOP<0b10000>; +def SUMOPUnitStride : RISCVLSUMOP<0b00000>; +def SUMOPUnitStrideWholeReg : RISCVLSUMOP<0b01000>; + +class RISCVWidth<bits<3> val> { + bits<3> Value = val; +} +def LSWidthVByte : RISCVWidth<0b000>; +def LSWidthVHalf : RISCVWidth<0b101>; +def LSWidthVWord : RISCVWidth<0b110>; +def LSWidthVSEW : RISCVWidth<0b111>; + +class RVInstSetVLi<dag outs, dag ins, string opcodestr, string argstr> + : RVInst<outs, ins, opcodestr, argstr, [], InstFormatI> { + bits<5> rs1; + bits<5> rd; + bits<11> vtypei; + + let Inst{31} = 0; + let Inst{30-20} = vtypei; + let Inst{19-15} = rs1; + let Inst{14-12} = 0b111; + let Inst{11-7} = rd; + let Opcode = OPC_OP_V.Value; + + let Defs = [VTYPE, VL]; +} + +class RVInstSetVL<dag outs, dag ins, string opcodestr, string argstr> + : RVInst<outs, ins, opcodestr, argstr, [], InstFormatR> { + bits<5> rs2; + bits<5> rs1; + bits<5> rd; + + let Inst{31} = 1; + let Inst{30-25} = 0b000000; + let Inst{24-20} = rs2; + let Inst{19-15} = rs1; + let Inst{14-12} = 0b111; + let Inst{11-7} = rd; + let Opcode = OPC_OP_V.Value; + + let Defs = [VTYPE, VL]; +} + +class RVInstVV<bits<6> funct6, RISCVVFormat opv, dag outs, dag ins, + string opcodestr, string argstr> + : RVInst<outs, ins, opcodestr, argstr, [], InstFormatR> { + bits<5> vs2; + bits<5> vs1; + bits<5> vd; + bit vm; + + let Inst{31-26} = funct6; + let Inst{25} = vm; + let Inst{24-20} = vs2; + let Inst{19-15} = vs1; + let Inst{14-12} = opv.Value; + let Inst{11-7} = vd; + let Opcode = OPC_OP_V.Value; + + let Uses = [VTYPE, VL]; +} + +class RVInstVX<bits<6> funct6, RISCVVFormat opv, dag outs, dag ins, + string opcodestr, string argstr> + : RVInst<outs, ins, opcodestr, argstr, [], InstFormatR> { + bits<5> vs2; + bits<5> rs1; + bits<5> vd; + bit vm; + + let Inst{31-26} = funct6; + let Inst{25} = vm; + let Inst{24-20} = vs2; + let Inst{19-15} = rs1; + let Inst{14-12} = opv.Value; + let Inst{11-7} = vd; + let Opcode = OPC_OP_V.Value; + + let Uses = [VTYPE, VL]; +} + +class RVInstV2<bits<6> funct6, bits<5> vs2, RISCVVFormat opv, dag outs, dag ins, + string opcodestr, string argstr> + : RVInst<outs, ins, opcodestr, argstr, [], InstFormatR> { + bits<5> rs1; + bits<5> vd; + bit vm; + + let Inst{31-26} = funct6; + let Inst{25} = vm; + let Inst{24-20} = vs2; + let Inst{19-15} = rs1; + let Inst{14-12} = opv.Value; + let Inst{11-7} = vd; + let Opcode = OPC_OP_V.Value; + + let Uses = [VTYPE, VL]; +} + +class RVInstIVI<bits<6> funct6, dag outs, dag ins, string opcodestr, + string argstr> + : RVInst<outs, ins, opcodestr, argstr, [], InstFormatR> { + bits<5> vs2; + bits<5> imm; + bits<5> vd; + bit vm; + + let Inst{31-26} = funct6; + let Inst{25} = vm; + let Inst{24-20} = vs2; + let Inst{19-15} = imm; + let Inst{14-12} = 0b011; + let Inst{11-7} = vd; + let Opcode = OPC_OP_V.Value; + + let Uses = [VTYPE, VL]; +} + +class RVInstV<bits<6> funct6, bits<5> vs1, RISCVVFormat opv, dag outs, + dag ins, string opcodestr, string argstr> + : RVInst<outs, ins, opcodestr, argstr, [], InstFormatR> { + bits<5> vs2; + bits<5> vd; + bit vm; + + let Inst{31-26} = funct6; + let Inst{25} = vm; + let Inst{24-20} = vs2; + let Inst{19-15} = vs1; + let Inst{14-12} = opv.Value; + let Inst{11-7} = vd; + let Opcode = OPC_OP_V.Value; + + let Uses = [VTYPE, VL]; +} + +class RVInstVLU<bits<3> nf, RISCVMOP mop, RISCVLSUMOP lumop, + RISCVWidth width, dag outs, dag ins, string opcodestr, + string argstr> + : RVInst<outs, ins, opcodestr, argstr, [], InstFormatR> { + bits<5> rs1; + bits<5> vd; + bit vm; + + let Inst{31-29} = nf; + let Inst{28-26} = mop.Value; + let Inst{25} = vm; + let Inst{24-20} = lumop.Value; + let Inst{19-15} = rs1; + let Inst{14-12} = width.Value; + let Inst{11-7} = vd; + let Opcode = OPC_LOAD_FP.Value; + + let Uses = [VTYPE, VL]; +} + +class RVInstVLS<bits<3> nf, RISCVMOP mop, RISCVWidth width, + dag outs, dag ins, string opcodestr, string argstr> + : RVInst<outs, ins, opcodestr, argstr, [], InstFormatR> { + bits<5> rs2; + bits<5> rs1; + bits<5> vd; + bit vm; + + let Inst{31-29} = nf; + let Inst{28-26} = mop.Value; + let Inst{25} = vm; + let Inst{24-20} = rs2; + let Inst{19-15} = rs1; + let Inst{14-12} = width.Value; + let Inst{11-7} = vd; + let Opcode = OPC_LOAD_FP.Value; + + let Uses = [VTYPE, VL]; +} + +class RVInstVLX<bits<3> nf, RISCVMOP mop, RISCVWidth width, + dag outs, dag ins, string opcodestr, string argstr> + : RVInst<outs, ins, opcodestr, argstr, [], InstFormatR> { + bits<5> vs2; + bits<5> rs1; + bits<5> vd; + bit vm; + + let Inst{31-29} = nf; + let Inst{28-26} = mop.Value; + let Inst{25} = vm; + let Inst{24-20} = vs2; + let Inst{19-15} = rs1; + let Inst{14-12} = width.Value; + let Inst{11-7} = vd; + let Opcode = OPC_LOAD_FP.Value; + + let Uses = [VTYPE, VL]; +} + +class RVInstVSU<bits<3> nf, RISCVMOP mop, RISCVLSUMOP sumop, + RISCVWidth width, dag outs, dag ins, string opcodestr, + string argstr> + : RVInst<outs, ins, opcodestr, argstr, [], InstFormatR> { + bits<5> rs1; + bits<5> vs3; + bit vm; + + let Inst{31-29} = nf; + let Inst{28-26} = mop.Value; + let Inst{25} = vm; + let Inst{24-20} = sumop.Value; + let Inst{19-15} = rs1; + let Inst{14-12} = width.Value; + let Inst{11-7} = vs3; + let Opcode = OPC_STORE_FP.Value; + + let Uses = [VTYPE, VL]; +} + +class RVInstVSS<bits<3> nf, RISCVMOP mop, RISCVWidth width, + dag outs, dag ins, string opcodestr, string argstr> + : RVInst<outs, ins, opcodestr, argstr, [], InstFormatR> { + bits<5> rs2; + bits<5> rs1; + bits<5> vs3; + bit vm; + + let Inst{31-29} = nf; + let Inst{28-26} = mop.Value; + let Inst{25} = vm; + let Inst{24-20} = rs2; + let Inst{19-15} = rs1; + let Inst{14-12} = width.Value; + let Inst{11-7} = vs3; + let Opcode = OPC_STORE_FP.Value; + + let Uses = [VTYPE, VL]; +} + +class RVInstVSX<bits<3> nf, RISCVMOP mop, RISCVWidth width, + dag outs, dag ins, string opcodestr, string argstr> + : RVInst<outs, ins, opcodestr, argstr, [], InstFormatR> { + bits<5> vs2; + bits<5> rs1; + bits<5> vs3; + bit vm; + + let Inst{31-29} = nf; + let Inst{28-26} = mop.Value; + let Inst{25} = vm; + let Inst{24-20} = vs2; + let Inst{19-15} = rs1; + let Inst{14-12} = width.Value; + let Inst{11-7} = vs3; + let Opcode = OPC_STORE_FP.Value; + + let Uses = [VTYPE, VL]; +} diff --git a/llvm/lib/Target/RISCV/RISCVInstrInfo.cpp b/llvm/lib/Target/RISCV/RISCVInstrInfo.cpp index 3b416ce3d3f4..d39ec505127c 100644 --- a/llvm/lib/Target/RISCV/RISCVInstrInfo.cpp +++ b/llvm/lib/Target/RISCV/RISCVInstrInfo.cpp @@ -24,14 +24,14 @@ #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/TargetRegistry.h" +using namespace llvm; + #define GEN_CHECK_COMPRESS_INSTR #include "RISCVGenCompressInstEmitter.inc" #define GET_INSTRINFO_CTOR_DTOR #include "RISCVGenInstrInfo.inc" -using namespace llvm; - RISCVInstrInfo::RISCVInstrInfo(RISCVSubtarget &STI) : RISCVGenInstrInfo(RISCV::ADJCALLSTACKDOWN, RISCV::ADJCALLSTACKUP), STI(STI) {} @@ -76,10 +76,10 @@ unsigned RISCVInstrInfo::isStoreToStackSlot(const MachineInstr &MI, break; } - if (MI.getOperand(0).isFI() && MI.getOperand(1).isImm() && - MI.getOperand(1).getImm() == 0) { - FrameIndex = MI.getOperand(0).getIndex(); - return MI.getOperand(2).getReg(); + if (MI.getOperand(1).isFI() && MI.getOperand(2).isImm() && + MI.getOperand(2).getImm() == 0) { + FrameIndex = MI.getOperand(1).getIndex(); + return MI.getOperand(0).getReg(); } return 0; @@ -112,7 +112,7 @@ void RISCVInstrInfo::copyPhysReg(MachineBasicBlock &MBB, void RISCVInstrInfo::storeRegToStackSlot(MachineBasicBlock &MBB, MachineBasicBlock::iterator I, - unsigned SrcReg, bool IsKill, int FI, + Register SrcReg, bool IsKill, int FI, const TargetRegisterClass *RC, const TargetRegisterInfo *TRI) const { DebugLoc DL; @@ -139,7 +139,7 @@ void RISCVInstrInfo::storeRegToStackSlot(MachineBasicBlock &MBB, void RISCVInstrInfo::loadRegFromStackSlot(MachineBasicBlock &MBB, MachineBasicBlock::iterator I, - unsigned DstReg, int FI, + Register DstReg, int FI, const TargetRegisterClass *RC, const TargetRegisterInfo *TRI) const { DebugLoc DL; @@ -342,7 +342,7 @@ unsigned RISCVInstrInfo::insertBranch( *BytesAdded = 0; // Shouldn't be a fall through. - assert(TBB && "InsertBranch must not be told to insert a fallthrough"); + assert(TBB && "insertBranch must not be told to insert a fallthrough"); assert((Cond.size() == 3 || Cond.size() == 0) && "RISCV branch conditions have two components!"); @@ -471,14 +471,38 @@ unsigned RISCVInstrInfo::getInstSizeInBytes(const MachineInstr &MI) const { case TargetOpcode::KILL: case TargetOpcode::DBG_VALUE: return 0; + // These values are determined based on RISCVExpandAtomicPseudoInsts, + // RISCVExpandPseudoInsts and RISCVMCCodeEmitter, depending on where the + // pseudos are expanded. case RISCV::PseudoCALLReg: case RISCV::PseudoCALL: + case RISCV::PseudoJump: case RISCV::PseudoTAIL: case RISCV::PseudoLLA: case RISCV::PseudoLA: case RISCV::PseudoLA_TLS_IE: case RISCV::PseudoLA_TLS_GD: return 8; + case RISCV::PseudoAtomicLoadNand32: + case RISCV::PseudoAtomicLoadNand64: + return 20; + case RISCV::PseudoMaskedAtomicSwap32: + case RISCV::PseudoMaskedAtomicLoadAdd32: + case RISCV::PseudoMaskedAtomicLoadSub32: + return 28; + case RISCV::PseudoMaskedAtomicLoadNand32: + return 32; + case RISCV::PseudoMaskedAtomicLoadMax32: + case RISCV::PseudoMaskedAtomicLoadMin32: + return 44; + case RISCV::PseudoMaskedAtomicLoadUMax32: + case RISCV::PseudoMaskedAtomicLoadUMin32: + return 36; + case RISCV::PseudoCmpXchg32: + case RISCV::PseudoCmpXchg64: + return 16; + case RISCV::PseudoMaskedCmpXchg32: + return 32; case TargetOpcode::INLINEASM: case TargetOpcode::INLINEASM_BR: { const MachineFunction &MF = *MI.getParent()->getParent(); @@ -777,6 +801,8 @@ void RISCVInstrInfo::buildOutlinedFrame( } } + MBB.addLiveIn(RISCV::X5); + // Add in a return instruction to the end of the outlined frame. MBB.insert(MBB.end(), BuildMI(MF, DebugLoc(), get(RISCV::JALR)) .addReg(RISCV::X0, RegState::Define) diff --git a/llvm/lib/Target/RISCV/RISCVInstrInfo.h b/llvm/lib/Target/RISCV/RISCVInstrInfo.h index 625b61875133..21bc508cdc9c 100644 --- a/llvm/lib/Target/RISCV/RISCVInstrInfo.h +++ b/llvm/lib/Target/RISCV/RISCVInstrInfo.h @@ -38,13 +38,13 @@ public: bool KillSrc) const override; void storeRegToStackSlot(MachineBasicBlock &MBB, - MachineBasicBlock::iterator MBBI, unsigned SrcReg, + MachineBasicBlock::iterator MBBI, Register SrcReg, bool IsKill, int FrameIndex, const TargetRegisterClass *RC, const TargetRegisterInfo *TRI) const override; void loadRegFromStackSlot(MachineBasicBlock &MBB, - MachineBasicBlock::iterator MBBI, unsigned DstReg, + MachineBasicBlock::iterator MBBI, Register DstReg, int FrameIndex, const TargetRegisterClass *RC, const TargetRegisterInfo *TRI) const override; @@ -133,5 +133,24 @@ public: protected: const RISCVSubtarget &STI; }; -} + +namespace RISCV { +// Match with the definitions in RISCVInstrFormatsV.td +enum RVVConstraintType { + NoConstraint = 0, + WidenV = 1, + WidenW = 2, + WidenCvt = 3, + Narrow = 4, + Iota = 5, + SlideUp = 6, + Vrgather = 7, + Vcompress = 8, + + ConstraintOffset = 5, + ConstraintMask = 0b1111 +}; +} // end namespace RISCV + +} // end namespace llvm #endif diff --git a/llvm/lib/Target/RISCV/RISCVInstrInfo.td b/llvm/lib/Target/RISCV/RISCVInstrInfo.td index 8e9ad4965583..b9483062ddeb 100644 --- a/llvm/lib/Target/RISCV/RISCVInstrInfo.td +++ b/llvm/lib/Target/RISCV/RISCVInstrInfo.td @@ -144,6 +144,20 @@ def simm12 : Operand<XLenVT>, ImmLeaf<XLenVT, [{return isInt<12>(Imm);}]> { let OperandNamespace = "RISCVOp"; } +// A 12-bit signed immediate plus one where the imm range will be -2047~2048. +def simm12_plus1 : Operand<XLenVT>, ImmLeaf<XLenVT, + [{return (isInt<12>(Imm) && Imm != -2048) || Imm == 2048;}]> { + let ParserMatchClass = SImmAsmOperand<12>; + let EncoderMethod = "getImmOpValue"; + let DecoderMethod = "decodeSImmOperand<12>"; + let MCOperandPredicate = [{ + int64_t Imm; + if (MCOp.evaluateAsConstantImm(Imm)) + return (isInt<12>(Imm) && Imm != -2048) || Imm == 2048; + return MCOp.isBareSymbolRef(); + }]; +} + // A 13-bit signed immediate where the least significant bit is zero. def simm13_lsb0 : Operand<OtherVT> { let ParserMatchClass = SImmAsmOperand<13, "Lsb0">; @@ -222,6 +236,18 @@ def call_symbol : Operand<XLenVT> { let ParserMatchClass = CallSymbol; } +def PseudoJumpSymbol : AsmOperandClass { + let Name = "PseudoJumpSymbol"; + let RenderMethod = "addImmOperands"; + let DiagnosticType = "InvalidPseudoJumpSymbol"; + let ParserMethod = "parsePseudoJumpSymbol"; +} + +// A bare symbol used for pseudo jumps only. +def pseudo_jump_symbol : Operand<XLenVT> { + let ParserMatchClass = PseudoJumpSymbol; +} + def TPRelAddSymbol : AsmOperandClass { let Name = "TPRelAddSymbol"; let RenderMethod = "addImmOperands"; @@ -284,6 +310,12 @@ def HI20 : SDNodeXForm<imm, [{ SDLoc(N), N->getValueType(0)); }]>; +// Return the negation of an immediate value. +def NegImm : SDNodeXForm<imm, [{ + return CurDAG->getTargetConstant(-N->getSExtValue(), SDLoc(N), + N->getValueType(0)); +}]>; + //===----------------------------------------------------------------------===// // Instruction Formats //===----------------------------------------------------------------------===// @@ -298,7 +330,8 @@ let hasSideEffects = 0, mayLoad = 0, mayStore = 0 in class BranchCC_rri<bits<3> funct3, string opcodestr> : RVInstB<funct3, OPC_BRANCH, (outs), (ins GPR:$rs1, GPR:$rs2, simm13_lsb0:$imm12), - opcodestr, "$rs1, $rs2, $imm12"> { + opcodestr, "$rs1, $rs2, $imm12">, + Sched<[WriteJmp, ReadJmp, ReadJmp]> { let isBranch = 1; let isTerminator = 1; } @@ -320,13 +353,15 @@ class Store_rri<bits<3> funct3, string opcodestr> let hasSideEffects = 0, mayLoad = 0, mayStore = 0 in class ALU_ri<bits<3> funct3, string opcodestr> : RVInstI<funct3, OPC_OP_IMM, (outs GPR:$rd), (ins GPR:$rs1, simm12:$imm12), - opcodestr, "$rd, $rs1, $imm12">; + opcodestr, "$rd, $rs1, $imm12">, + Sched<[WriteIALU, ReadIALU]>; let hasSideEffects = 0, mayLoad = 0, mayStore = 0 in class Shift_ri<bit arithshift, bits<3> funct3, string opcodestr> : RVInstIShift<arithshift, funct3, OPC_OP_IMM, (outs GPR:$rd), (ins GPR:$rs1, uimmlog2xlen:$shamt), opcodestr, - "$rd, $rs1, $shamt">; + "$rd, $rs1, $shamt">, + Sched<[WriteShift, ReadShift]>; let hasSideEffects = 0, mayLoad = 0, mayStore = 0 in class ALU_rr<bits<7> funct7, bits<3> funct3, string opcodestr> @@ -336,19 +371,20 @@ class ALU_rr<bits<7> funct7, bits<3> funct3, string opcodestr> let hasSideEffects = 1, mayLoad = 0, mayStore = 0 in class CSR_ir<bits<3> funct3, string opcodestr> : RVInstI<funct3, OPC_SYSTEM, (outs GPR:$rd), (ins csr_sysreg:$imm12, GPR:$rs1), - opcodestr, "$rd, $imm12, $rs1">; + opcodestr, "$rd, $imm12, $rs1">, Sched<[WriteCSR, ReadCSR]>; let hasSideEffects = 1, mayLoad = 0, mayStore = 0 in class CSR_ii<bits<3> funct3, string opcodestr> : RVInstI<funct3, OPC_SYSTEM, (outs GPR:$rd), (ins csr_sysreg:$imm12, uimm5:$rs1), - opcodestr, "$rd, $imm12, $rs1">; + opcodestr, "$rd, $imm12, $rs1">, Sched<[WriteCSR]>; let hasSideEffects = 0, mayLoad = 0, mayStore = 0 in class ShiftW_ri<bit arithshift, bits<3> funct3, string opcodestr> : RVInstIShiftW<arithshift, funct3, OPC_OP_IMM_32, (outs GPR:$rd), (ins GPR:$rs1, uimm5:$shamt), opcodestr, - "$rd, $rs1, $shamt">; + "$rd, $rs1, $shamt">, + Sched<[WriteShift32, ReadShift32]>; let hasSideEffects = 0, mayLoad = 0, mayStore = 0 in class ALUW_rr<bits<7> funct7, bits<3> funct3, string opcodestr> @@ -367,19 +403,20 @@ class Priv<string opcodestr, bits<7> funct7> let hasSideEffects = 0, mayLoad = 0, mayStore = 0 in { let isReMaterializable = 1, isAsCheapAsAMove = 1 in def LUI : RVInstU<OPC_LUI, (outs GPR:$rd), (ins uimm20_lui:$imm20), - "lui", "$rd, $imm20">; + "lui", "$rd, $imm20">, Sched<[WriteIALU]>; def AUIPC : RVInstU<OPC_AUIPC, (outs GPR:$rd), (ins uimm20_auipc:$imm20), - "auipc", "$rd, $imm20">; + "auipc", "$rd, $imm20">, Sched<[WriteIALU]>; let isCall = 1 in def JAL : RVInstJ<OPC_JAL, (outs GPR:$rd), (ins simm21_lsb0_jal:$imm20), - "jal", "$rd, $imm20">; + "jal", "$rd, $imm20">, Sched<[WriteJal]>; let isCall = 1 in def JALR : RVInstI<0b000, OPC_JALR, (outs GPR:$rd), (ins GPR:$rs1, simm12:$imm12), - "jalr", "$rd, ${imm12}(${rs1})">; + "jalr", "$rd, ${imm12}(${rs1})">, + Sched<[WriteJalr, ReadJalr]>; } // hasSideEffects = 0, mayLoad = 0, mayStore = 0 def BEQ : BranchCC_rri<0b000, "beq">; @@ -389,15 +426,15 @@ def BGE : BranchCC_rri<0b101, "bge">; def BLTU : BranchCC_rri<0b110, "bltu">; def BGEU : BranchCC_rri<0b111, "bgeu">; -def LB : Load_ri<0b000, "lb">; -def LH : Load_ri<0b001, "lh">; -def LW : Load_ri<0b010, "lw">; -def LBU : Load_ri<0b100, "lbu">; -def LHU : Load_ri<0b101, "lhu">; +def LB : Load_ri<0b000, "lb">, Sched<[WriteLDB, ReadMemBase]>; +def LH : Load_ri<0b001, "lh">, Sched<[WriteLDH, ReadMemBase]>; +def LW : Load_ri<0b010, "lw">, Sched<[WriteLDW, ReadMemBase]>; +def LBU : Load_ri<0b100, "lbu">, Sched<[WriteLDB, ReadMemBase]>; +def LHU : Load_ri<0b101, "lhu">, Sched<[WriteLDH, ReadMemBase]>; -def SB : Store_rri<0b000, "sb">; -def SH : Store_rri<0b001, "sh">; -def SW : Store_rri<0b010, "sw">; +def SB : Store_rri<0b000, "sb">, Sched<[WriteSTB, ReadStoreData, ReadMemBase]>; +def SH : Store_rri<0b001, "sh">, Sched<[WriteSTH, ReadStoreData, ReadMemBase]>; +def SW : Store_rri<0b010, "sw">, Sched<[WriteSTW, ReadStoreData, ReadMemBase]>; // ADDI isn't always rematerializable, but isReMaterializable will be used as // a hint which is verified in isReallyTriviallyReMaterializable. @@ -418,21 +455,21 @@ def SLLI : Shift_ri<0, 0b001, "slli">; def SRLI : Shift_ri<0, 0b101, "srli">; def SRAI : Shift_ri<1, 0b101, "srai">; -def ADD : ALU_rr<0b0000000, 0b000, "add">; -def SUB : ALU_rr<0b0100000, 0b000, "sub">; -def SLL : ALU_rr<0b0000000, 0b001, "sll">; -def SLT : ALU_rr<0b0000000, 0b010, "slt">; -def SLTU : ALU_rr<0b0000000, 0b011, "sltu">; -def XOR : ALU_rr<0b0000000, 0b100, "xor">; -def SRL : ALU_rr<0b0000000, 0b101, "srl">; -def SRA : ALU_rr<0b0100000, 0b101, "sra">; -def OR : ALU_rr<0b0000000, 0b110, "or">; -def AND : ALU_rr<0b0000000, 0b111, "and">; +def ADD : ALU_rr<0b0000000, 0b000, "add">, Sched<[WriteIALU, ReadIALU, ReadIALU]>; +def SUB : ALU_rr<0b0100000, 0b000, "sub">, Sched<[WriteIALU, ReadIALU, ReadIALU]>; +def SLL : ALU_rr<0b0000000, 0b001, "sll">, Sched<[WriteIALU, ReadIALU, ReadIALU]>; +def SLT : ALU_rr<0b0000000, 0b010, "slt">, Sched<[WriteIALU, ReadIALU, ReadIALU]>; +def SLTU : ALU_rr<0b0000000, 0b011, "sltu">, Sched<[WriteIALU, ReadIALU, ReadIALU]>; +def XOR : ALU_rr<0b0000000, 0b100, "xor">, Sched<[WriteIALU, ReadIALU, ReadIALU]>; +def SRL : ALU_rr<0b0000000, 0b101, "srl">, Sched<[WriteIALU, ReadIALU, ReadIALU]>; +def SRA : ALU_rr<0b0100000, 0b101, "sra">, Sched<[WriteIALU, ReadIALU, ReadIALU]>; +def OR : ALU_rr<0b0000000, 0b110, "or">, Sched<[WriteIALU, ReadIALU, ReadIALU]>; +def AND : ALU_rr<0b0000000, 0b111, "and">, Sched<[WriteIALU, ReadIALU, ReadIALU]>; let hasSideEffects = 1, mayLoad = 0, mayStore = 0 in { def FENCE : RVInstI<0b000, OPC_MISC_MEM, (outs), (ins fencearg:$pred, fencearg:$succ), - "fence", "$pred, $succ"> { + "fence", "$pred, $succ">, Sched<[]> { bits<4> pred; bits<4> succ; @@ -441,25 +478,26 @@ def FENCE : RVInstI<0b000, OPC_MISC_MEM, (outs), let imm12 = {0b0000,pred,succ}; } -def FENCE_TSO : RVInstI<0b000, OPC_MISC_MEM, (outs), (ins), "fence.tso", ""> { +def FENCE_TSO : RVInstI<0b000, OPC_MISC_MEM, (outs), (ins), "fence.tso", "">, Sched<[]> { let rs1 = 0; let rd = 0; let imm12 = {0b1000,0b0011,0b0011}; } -def FENCE_I : RVInstI<0b001, OPC_MISC_MEM, (outs), (ins), "fence.i", ""> { +def FENCE_I : RVInstI<0b001, OPC_MISC_MEM, (outs), (ins), "fence.i", "">, Sched<[]> { let rs1 = 0; let rd = 0; let imm12 = 0; } -def ECALL : RVInstI<0b000, OPC_SYSTEM, (outs), (ins), "ecall", ""> { +def ECALL : RVInstI<0b000, OPC_SYSTEM, (outs), (ins), "ecall", "">, Sched<[WriteJmp]> { let rs1 = 0; let rd = 0; let imm12 = 0; } -def EBREAK : RVInstI<0b000, OPC_SYSTEM, (outs), (ins), "ebreak", ""> { +def EBREAK : RVInstI<0b000, OPC_SYSTEM, (outs), (ins), "ebreak", "">, + Sched<[]> { let rs1 = 0; let rd = 0; let imm12 = 1; @@ -468,7 +506,8 @@ def EBREAK : RVInstI<0b000, OPC_SYSTEM, (outs), (ins), "ebreak", ""> { // This is a de facto standard (as set by GNU binutils) 32-bit unimplemented // instruction (i.e., it should always trap, if your implementation has invalid // instruction traps). -def UNIMP : RVInstI<0b001, OPC_SYSTEM, (outs), (ins), "unimp", ""> { +def UNIMP : RVInstI<0b001, OPC_SYSTEM, (outs), (ins), "unimp", "">, + Sched<[]> { let rs1 = 0; let rd = 0; let imm12 = 0b110000000000; @@ -486,24 +525,30 @@ def CSRRCI : CSR_ii<0b111, "csrrci">; /// RV64I instructions let Predicates = [IsRV64] in { -def LWU : Load_ri<0b110, "lwu">; -def LD : Load_ri<0b011, "ld">; -def SD : Store_rri<0b011, "sd">; +def LWU : Load_ri<0b110, "lwu">, Sched<[WriteLDWU, ReadMemBase]>; +def LD : Load_ri<0b011, "ld">, Sched<[WriteLDD, ReadMemBase]>; +def SD : Store_rri<0b011, "sd">, Sched<[WriteSTD, ReadStoreData, ReadMemBase]>; let hasSideEffects = 0, mayLoad = 0, mayStore = 0 in def ADDIW : RVInstI<0b000, OPC_OP_IMM_32, (outs GPR:$rd), (ins GPR:$rs1, simm12:$imm12), - "addiw", "$rd, $rs1, $imm12">; + "addiw", "$rd, $rs1, $imm12">, + Sched<[WriteIALU32, ReadIALU32]>; def SLLIW : ShiftW_ri<0, 0b001, "slliw">; def SRLIW : ShiftW_ri<0, 0b101, "srliw">; def SRAIW : ShiftW_ri<1, 0b101, "sraiw">; -def ADDW : ALUW_rr<0b0000000, 0b000, "addw">; -def SUBW : ALUW_rr<0b0100000, 0b000, "subw">; -def SLLW : ALUW_rr<0b0000000, 0b001, "sllw">; -def SRLW : ALUW_rr<0b0000000, 0b101, "srlw">; -def SRAW : ALUW_rr<0b0100000, 0b101, "sraw">; +def ADDW : ALUW_rr<0b0000000, 0b000, "addw">, + Sched<[WriteIALU32, ReadIALU32, ReadIALU32]>; +def SUBW : ALUW_rr<0b0100000, 0b000, "subw">, + Sched<[WriteIALU32, ReadIALU32, ReadIALU32]>; +def SLLW : ALUW_rr<0b0000000, 0b001, "sllw">, + Sched<[WriteIALU32, ReadIALU32, ReadIALU32]>; +def SRLW : ALUW_rr<0b0000000, 0b101, "srlw">, + Sched<[WriteIALU32, ReadIALU32, ReadIALU32]>; +def SRAW : ALUW_rr<0b0100000, 0b101, "sraw">, + Sched<[WriteIALU32, ReadIALU32, ReadIALU32]>; } // Predicates = [IsRV64] //===----------------------------------------------------------------------===// @@ -511,26 +556,26 @@ def SRAW : ALUW_rr<0b0100000, 0b101, "sraw">; //===----------------------------------------------------------------------===// let isBarrier = 1, isReturn = 1, isTerminator = 1 in { -def URET : Priv<"uret", 0b0000000> { +def URET : Priv<"uret", 0b0000000>, Sched<[]> { let rd = 0; let rs1 = 0; let rs2 = 0b00010; } -def SRET : Priv<"sret", 0b0001000> { +def SRET : Priv<"sret", 0b0001000>, Sched<[]> { let rd = 0; let rs1 = 0; let rs2 = 0b00010; } -def MRET : Priv<"mret", 0b0011000> { +def MRET : Priv<"mret", 0b0011000>, Sched<[]> { let rd = 0; let rs1 = 0; let rs2 = 0b00010; } } // isBarrier = 1, isReturn = 1, isTerminator = 1 -def WFI : Priv<"wfi", 0b0001000> { +def WFI : Priv<"wfi", 0b0001000>, Sched<[]> { let rd = 0; let rs1 = 0; let rs2 = 0b00101; @@ -539,9 +584,21 @@ def WFI : Priv<"wfi", 0b0001000> { let hasSideEffects = 1, mayLoad = 0, mayStore = 0 in def SFENCE_VMA : RVInstR<0b0001001, 0b000, OPC_SYSTEM, (outs), (ins GPR:$rs1, GPR:$rs2), - "sfence.vma", "$rs1, $rs2"> { + "sfence.vma", "$rs1, $rs2">, Sched<[]> { + let rd = 0; +} + +//===----------------------------------------------------------------------===// +// Debug instructions +//===----------------------------------------------------------------------===// + +let isBarrier = 1, isReturn = 1, isTerminator = 1 in { +def DRET : Priv<"dret", 0b0111101>, Sched<[]> { let rd = 0; + let rs1 = 0; + let rs2 = 0b10010; } +} // isBarrier = 1, isReturn = 1, isTerminator = 1 //===----------------------------------------------------------------------===// // Assembler Pseudo Instructions (User-Level ISA, Version 2.2, Chapter 20) @@ -739,7 +796,7 @@ def : MnemonicAlias<"sbreak", "ebreak">; // // Naming convention: For 'generic' pattern classes, we use the naming // convention PatTy1Ty2. For pattern classes which offer a more complex -// expension, prefix the class name, e.g. BccPat. +// expansion, prefix the class name, e.g. BccPat. //===----------------------------------------------------------------------===// /// Generic pattern classes @@ -832,12 +889,12 @@ def : PatGprSimm12<setult, SLTIU>; // handled by a RISC-V instruction. def : Pat<(seteq GPR:$rs1, 0), (SLTIU GPR:$rs1, 1)>; def : Pat<(seteq GPR:$rs1, GPR:$rs2), (SLTIU (XOR GPR:$rs1, GPR:$rs2), 1)>; -def : Pat<(seteq GPR:$rs1, simm12:$imm12), - (SLTIU (XORI GPR:$rs1, simm12:$imm12), 1)>; +def : Pat<(seteq GPR:$rs1, simm12_plus1:$imm12), + (SLTIU (ADDI GPR:$rs1, (NegImm simm12_plus1:$imm12)), 1)>; def : Pat<(setne GPR:$rs1, 0), (SLTU X0, GPR:$rs1)>; def : Pat<(setne GPR:$rs1, GPR:$rs2), (SLTU X0, (XOR GPR:$rs1, GPR:$rs2))>; -def : Pat<(setne GPR:$rs1, simm12:$imm12), - (SLTU X0, (XORI GPR:$rs1, simm12:$imm12))>; +def : Pat<(setne GPR:$rs1, simm12_plus1:$imm12), + (SLTU X0, (ADDI GPR:$rs1, (NegImm simm12_plus1:$imm12)))>; def : Pat<(setugt GPR:$rs1, GPR:$rs2), (SLTU GPR:$rs2, GPR:$rs1)>; def : Pat<(setuge GPR:$rs1, GPR:$rs2), (XORI (SLTU GPR:$rs1, GPR:$rs2), 1)>; def : Pat<(setule GPR:$rs1, GPR:$rs2), (XORI (SLTU GPR:$rs2, GPR:$rs1), 1)>; @@ -955,6 +1012,12 @@ def : Pat<(riscv_tail (iPTR tglobaladdr:$dst)), def : Pat<(riscv_tail (iPTR texternalsym:$dst)), (PseudoTAIL texternalsym:$dst)>; +let isCall = 0, isBarrier = 0, isCodeGenOnly = 0, hasSideEffects = 0, + mayStore = 0, mayLoad = 0 in +def PseudoJump : Pseudo<(outs GPR:$rd), (ins pseudo_jump_symbol:$target), []> { + let AsmString = "jump\t$target, $rd"; +} + let hasSideEffects = 0, mayLoad = 0, mayStore = 0, isCodeGenOnly = 0, isAsmParserOnly = 1 in def PseudoLLA : Pseudo<(outs GPR:$dst), (ins bare_symbol:$src), [], @@ -1113,3 +1176,5 @@ include "RISCVInstrInfoA.td" include "RISCVInstrInfoF.td" include "RISCVInstrInfoD.td" include "RISCVInstrInfoC.td" +include "RISCVInstrInfoB.td" +include "RISCVInstrInfoV.td" diff --git a/llvm/lib/Target/RISCV/RISCVInstrInfoA.td b/llvm/lib/Target/RISCV/RISCVInstrInfoA.td index 7321f4bd9d2f..7fce37519b93 100644 --- a/llvm/lib/Target/RISCV/RISCVInstrInfoA.td +++ b/llvm/lib/Target/RISCV/RISCVInstrInfoA.td @@ -77,31 +77,51 @@ multiclass AtomicStPat<PatFrag StoreOp, RVInst Inst, RegisterClass StTy> { //===----------------------------------------------------------------------===// let Predicates = [HasStdExtA] in { -defm LR_W : LR_r_aq_rl<0b010, "lr.w">; -defm SC_W : AMO_rr_aq_rl<0b00011, 0b010, "sc.w">; -defm AMOSWAP_W : AMO_rr_aq_rl<0b00001, 0b010, "amoswap.w">; -defm AMOADD_W : AMO_rr_aq_rl<0b00000, 0b010, "amoadd.w">; -defm AMOXOR_W : AMO_rr_aq_rl<0b00100, 0b010, "amoxor.w">; -defm AMOAND_W : AMO_rr_aq_rl<0b01100, 0b010, "amoand.w">; -defm AMOOR_W : AMO_rr_aq_rl<0b01000, 0b010, "amoor.w">; -defm AMOMIN_W : AMO_rr_aq_rl<0b10000, 0b010, "amomin.w">; -defm AMOMAX_W : AMO_rr_aq_rl<0b10100, 0b010, "amomax.w">; -defm AMOMINU_W : AMO_rr_aq_rl<0b11000, 0b010, "amominu.w">; -defm AMOMAXU_W : AMO_rr_aq_rl<0b11100, 0b010, "amomaxu.w">; +defm LR_W : LR_r_aq_rl<0b010, "lr.w">, Sched<[WriteAtomicLDW, ReadAtomicLDW]>; +defm SC_W : AMO_rr_aq_rl<0b00011, 0b010, "sc.w">, + Sched<[WriteAtomicSTW, ReadAtomicSTW, ReadAtomicSTW]>; +defm AMOSWAP_W : AMO_rr_aq_rl<0b00001, 0b010, "amoswap.w">, + Sched<[WriteAtomicW, ReadAtomicWA, ReadAtomicWD]>; +defm AMOADD_W : AMO_rr_aq_rl<0b00000, 0b010, "amoadd.w">, + Sched<[WriteAtomicW, ReadAtomicWA, ReadAtomicWD]>; +defm AMOXOR_W : AMO_rr_aq_rl<0b00100, 0b010, "amoxor.w">, + Sched<[WriteAtomicW, ReadAtomicWA, ReadAtomicWD]>; +defm AMOAND_W : AMO_rr_aq_rl<0b01100, 0b010, "amoand.w">, + Sched<[WriteAtomicW, ReadAtomicWA, ReadAtomicWD]>; +defm AMOOR_W : AMO_rr_aq_rl<0b01000, 0b010, "amoor.w">, + Sched<[WriteAtomicW, ReadAtomicWA, ReadAtomicWD]>; +defm AMOMIN_W : AMO_rr_aq_rl<0b10000, 0b010, "amomin.w">, + Sched<[WriteAtomicW, ReadAtomicWA, ReadAtomicWD]>; +defm AMOMAX_W : AMO_rr_aq_rl<0b10100, 0b010, "amomax.w">, + Sched<[WriteAtomicW, ReadAtomicWA, ReadAtomicWD]>; +defm AMOMINU_W : AMO_rr_aq_rl<0b11000, 0b010, "amominu.w">, + Sched<[WriteAtomicW, ReadAtomicWA, ReadAtomicWD]>; +defm AMOMAXU_W : AMO_rr_aq_rl<0b11100, 0b010, "amomaxu.w">, + Sched<[WriteAtomicW, ReadAtomicWA, ReadAtomicWD]>; } // Predicates = [HasStdExtA] let Predicates = [HasStdExtA, IsRV64] in { -defm LR_D : LR_r_aq_rl<0b011, "lr.d">; -defm SC_D : AMO_rr_aq_rl<0b00011, 0b011, "sc.d">; -defm AMOSWAP_D : AMO_rr_aq_rl<0b00001, 0b011, "amoswap.d">; -defm AMOADD_D : AMO_rr_aq_rl<0b00000, 0b011, "amoadd.d">; -defm AMOXOR_D : AMO_rr_aq_rl<0b00100, 0b011, "amoxor.d">; -defm AMOAND_D : AMO_rr_aq_rl<0b01100, 0b011, "amoand.d">; -defm AMOOR_D : AMO_rr_aq_rl<0b01000, 0b011, "amoor.d">; -defm AMOMIN_D : AMO_rr_aq_rl<0b10000, 0b011, "amomin.d">; -defm AMOMAX_D : AMO_rr_aq_rl<0b10100, 0b011, "amomax.d">; -defm AMOMINU_D : AMO_rr_aq_rl<0b11000, 0b011, "amominu.d">; -defm AMOMAXU_D : AMO_rr_aq_rl<0b11100, 0b011, "amomaxu.d">; +defm LR_D : LR_r_aq_rl<0b011, "lr.d">, Sched<[WriteAtomicLDD, ReadAtomicLDD]>; +defm SC_D : AMO_rr_aq_rl<0b00011, 0b011, "sc.d">, + Sched<[WriteAtomicSTD, ReadAtomicSTD, ReadAtomicSTD]>; +defm AMOSWAP_D : AMO_rr_aq_rl<0b00001, 0b011, "amoswap.d">, + Sched<[WriteAtomicD, ReadAtomicDA, ReadAtomicDD]>; +defm AMOADD_D : AMO_rr_aq_rl<0b00000, 0b011, "amoadd.d">, + Sched<[WriteAtomicD, ReadAtomicDA, ReadAtomicDD]>; +defm AMOXOR_D : AMO_rr_aq_rl<0b00100, 0b011, "amoxor.d">, + Sched<[WriteAtomicD, ReadAtomicDA, ReadAtomicDD]>; +defm AMOAND_D : AMO_rr_aq_rl<0b01100, 0b011, "amoand.d">, + Sched<[WriteAtomicD, ReadAtomicDA, ReadAtomicDD]>; +defm AMOOR_D : AMO_rr_aq_rl<0b01000, 0b011, "amoor.d">, + Sched<[WriteAtomicD, ReadAtomicDA, ReadAtomicDD]>; +defm AMOMIN_D : AMO_rr_aq_rl<0b10000, 0b011, "amomin.d">, + Sched<[WriteAtomicD, ReadAtomicDA, ReadAtomicDD]>; +defm AMOMAX_D : AMO_rr_aq_rl<0b10100, 0b011, "amomax.d">, + Sched<[WriteAtomicD, ReadAtomicDA, ReadAtomicDD]>; +defm AMOMINU_D : AMO_rr_aq_rl<0b11000, 0b011, "amominu.d">, + Sched<[WriteAtomicD, ReadAtomicDA, ReadAtomicDD]>; +defm AMOMAXU_D : AMO_rr_aq_rl<0b11100, 0b011, "amomaxu.d">, + Sched<[WriteAtomicD, ReadAtomicDA, ReadAtomicDD]>; } // Predicates = [HasStdExtA, IsRV64] //===----------------------------------------------------------------------===// @@ -215,13 +235,13 @@ class PseudoMaskedAMOUMinUMax class PseudoMaskedAMOPat<Intrinsic intrin, Pseudo AMOInst> : Pat<(intrin GPR:$addr, GPR:$incr, GPR:$mask, timm:$ordering), - (AMOInst GPR:$addr, GPR:$incr, GPR:$mask, imm:$ordering)>; + (AMOInst GPR:$addr, GPR:$incr, GPR:$mask, timm:$ordering)>; class PseudoMaskedAMOMinMaxPat<Intrinsic intrin, Pseudo AMOInst> : Pat<(intrin GPR:$addr, GPR:$incr, GPR:$mask, GPR:$shiftamt, timm:$ordering), (AMOInst GPR:$addr, GPR:$incr, GPR:$mask, GPR:$shiftamt, - imm:$ordering)>; + timm:$ordering)>; def PseudoMaskedAtomicSwap32 : PseudoMaskedAMO; def : PseudoMaskedAMOPat<int_riscv_masked_atomicrmw_xchg_i32, @@ -290,7 +310,7 @@ def PseudoMaskedCmpXchg32 def : Pat<(int_riscv_masked_cmpxchg_i32 GPR:$addr, GPR:$cmpval, GPR:$newval, GPR:$mask, timm:$ordering), (PseudoMaskedCmpXchg32 - GPR:$addr, GPR:$cmpval, GPR:$newval, GPR:$mask, imm:$ordering)>; + GPR:$addr, GPR:$cmpval, GPR:$newval, GPR:$mask, timm:$ordering)>; } // Predicates = [HasStdExtA] @@ -367,5 +387,5 @@ defm : PseudoCmpXchgPat<"atomic_cmp_swap_64", PseudoCmpXchg64>; def : Pat<(int_riscv_masked_cmpxchg_i64 GPR:$addr, GPR:$cmpval, GPR:$newval, GPR:$mask, timm:$ordering), (PseudoMaskedCmpXchg32 - GPR:$addr, GPR:$cmpval, GPR:$newval, GPR:$mask, imm:$ordering)>; + GPR:$addr, GPR:$cmpval, GPR:$newval, GPR:$mask, timm:$ordering)>; } // Predicates = [HasStdExtA, IsRV64] diff --git a/llvm/lib/Target/RISCV/RISCVInstrInfoB.td b/llvm/lib/Target/RISCV/RISCVInstrInfoB.td new file mode 100644 index 000000000000..34a463626e29 --- /dev/null +++ b/llvm/lib/Target/RISCV/RISCVInstrInfoB.td @@ -0,0 +1,634 @@ +//===-- RISCVInstrInfoB.td - RISC-V 'B' instructions -------*- 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 RISC-V instructions from the standard 'B' Bitmanip +// extension, version 0.92. +// This version is still experimental as the 'B' extension hasn't been +// ratified yet. +// +//===----------------------------------------------------------------------===// + +//===----------------------------------------------------------------------===// +// Operand definitions. +//===----------------------------------------------------------------------===// + +def UImmLog2XLenHalfAsmOperand : AsmOperandClass { + let Name = "UImmLog2XLenHalf"; + let RenderMethod = "addImmOperands"; + let DiagnosticType = "InvalidUImmLog2XLenHalf"; +} + +def shfl_uimm : Operand<XLenVT>, ImmLeaf<XLenVT, [{ + if (Subtarget->is64Bit()) + return isUInt<5>(Imm); + return isUInt<4>(Imm); +}]> { + let ParserMatchClass = UImmLog2XLenHalfAsmOperand; + let DecoderMethod = "decodeUImmOperand<5>"; + let MCOperandPredicate = [{ + int64_t Imm; + if (!MCOp.evaluateAsConstantImm(Imm)) + return false; + if (STI.getTargetTriple().isArch64Bit()) + return isUInt<5>(Imm); + return isUInt<4>(Imm); + }]; +} + +//===----------------------------------------------------------------------===// +// Instruction class templates +//===----------------------------------------------------------------------===// + +// Some of these templates should be moved to RISCVInstrFormats.td once the B +// extension has been ratified. + +let hasSideEffects = 0, mayLoad = 0, mayStore = 0 in +class RVBUnary<bits<7> funct7, bits<5> funct5, bits<3> funct3, + RISCVOpcode opcode, string opcodestr> + : RVInstR<funct7, funct3, opcode, (outs GPR:$rd), (ins GPR:$rs1), + opcodestr, "$rd, $rs1"> { + let Inst{24-20} = funct5; +} + +let hasSideEffects = 0, mayLoad = 0, mayStore = 0 in +class RVBALUW_ri<bits<3> funct3, string opcodestr> + : RVInstI<funct3, OPC_OP_IMM_32, (outs GPR:$rd), + (ins GPR:$rs1, simm12:$imm12), opcodestr, "$rd, $rs1, $imm12">; + +let hasSideEffects = 0, mayLoad = 0, mayStore = 0 in +class RVBShift_ri<bits<5> funct5, bits<3> funct3, RISCVOpcode opcode, + string opcodestr> + : RVInstI<funct3, opcode, (outs GPR:$rd), + (ins GPR:$rs1, uimmlog2xlen:$shamt), opcodestr, + "$rd, $rs1, $shamt"> { + bits<6> shamt; + + let Inst{31-27} = funct5; + // NOTE: the bit op(26)=1 is used to select funnel shifts. All other + // shifts operations and operations that live in the encoding space + // of the shifts (single bit operations, grev, gorc) use op(26) = 0 + let Inst{26} = 0; + let Inst{25-20} = shamt; +} + +let hasSideEffects = 0, mayLoad = 0, mayStore = 0 in +class RVBShiftW_ri<bits<7> funct7, bits<3> funct3, RISCVOpcode opcode, + string opcodestr> + : RVInstI<funct3, opcode, (outs GPR:$rd), (ins GPR:$rs1, uimm5:$shamt), + opcodestr, "$rd, $rs1, $shamt"> { + bits<5> shamt; + + let Inst{31-25} = funct7; + let Inst{24-20} = shamt; +} + +let hasSideEffects = 0, mayLoad = 0, mayStore = 0 in +class RVBShfl_ri<bits<6> funct6, bits<3> funct3, RISCVOpcode opcode, + string opcodestr> + : RVInstI<funct3, opcode, (outs GPR:$rd), (ins GPR:$rs1, shfl_uimm:$shamt), + opcodestr, "$rd, $rs1, $shamt"> { + bits<6> shamt; + + let Inst{31-26} = funct6; + let Inst{25-20} = shamt; +} + +let hasSideEffects = 0, mayLoad = 0, mayStore = 0 in +class RVBTernaryR<bits<2> funct2, bits<3> funct3_b, RISCVOpcode opcode, + string opcodestr, string argstr> + : RVInstR4<funct2, opcode, (outs GPR:$rd), + (ins GPR:$rs1, GPR:$rs2, GPR:$rs3), opcodestr, argstr> { + let Inst{14-12} = funct3_b; +} + +// Currently used by FSRI only +let hasSideEffects = 0, mayLoad = 0, mayStore = 0 in +class RVBTernaryImm6<bits<3> funct3_b, RISCVOpcode opcode, + string opcodestr, string argstr> + : RVInstR4<0b10, opcode, (outs GPR:$rd), + (ins GPR:$rs1, GPR:$rs3, uimmlog2xlen:$shamt), + opcodestr, argstr> { + bits<6> shamt; + + // NOTE: the first argument of RVInstR4 is hardcoded to 0b10 like the other + // funnel shift instructions. The second bit of the argument though is + // overwritten by the shamt as the encoding of this particular instruction + // requires. This is to obtain op(26) = 1 as required by funnel shift + // instructions without the need of a confusing argument in the definition + // of the instruction. + let Inst{25-20} = shamt; + let Inst{14-12} = funct3_b; +} + +// Currently used by FSRIW only +let hasSideEffects = 0, mayLoad = 0, mayStore = 0 in +class RVBTernaryImm5<bits<2> funct2, bits<3> funct3_b, RISCVOpcode opcode, + string opcodestr, string argstr> + : RVInstR4<funct2, opcode, (outs GPR:$rd), + (ins GPR:$rs1, GPR:$rs3, uimm5:$shamt), opcodestr, argstr> { + bits<5> shamt; + + let Inst{24-20} = shamt; + let Inst{14-12} = funct3_b; +} + +//===----------------------------------------------------------------------===// +// Instructions +//===----------------------------------------------------------------------===// + +let Predicates = [HasStdExtZbbOrZbp] in { +def ANDN : ALU_rr<0b0100000, 0b111, "andn">, Sched<[]>; +def ORN : ALU_rr<0b0100000, 0b110, "orn">, Sched<[]>; +def XNOR : ALU_rr<0b0100000, 0b100, "xnor">, Sched<[]>; +} // Predicates = [HasStdExtZbbOrZbp] + +let Predicates = [HasStdExtZbb] in { +def SLO : ALU_rr<0b0010000, 0b001, "slo">, Sched<[]>; +def SRO : ALU_rr<0b0010000, 0b101, "sro">, Sched<[]>; +} // Predicates = [HasStdExtZbb] + +let Predicates = [HasStdExtZbbOrZbp] in { +def ROL : ALU_rr<0b0110000, 0b001, "rol">, Sched<[]>; +def ROR : ALU_rr<0b0110000, 0b101, "ror">, Sched<[]>; +} // Predicates = [HasStdExtZbbOrZbp] + +let Predicates = [HasStdExtZbs] in { +def SBCLR : ALU_rr<0b0100100, 0b001, "sbclr">, Sched<[]>; +def SBSET : ALU_rr<0b0010100, 0b001, "sbset">, Sched<[]>; +def SBINV : ALU_rr<0b0110100, 0b001, "sbinv">, Sched<[]>; +def SBEXT : ALU_rr<0b0100100, 0b101, "sbext">, Sched<[]>; +} // Predicates = [HasStdExtZbs] + +let Predicates = [HasStdExtZbp] in { +def GORC : ALU_rr<0b0010100, 0b101, "gorc">, Sched<[]>; +def GREV : ALU_rr<0b0110100, 0b101, "grev">, Sched<[]>; +} // Predicates = [HasStdExtZbp] + +let Predicates = [HasStdExtZbb] in { +def SLOI : RVBShift_ri<0b00100, 0b001, OPC_OP_IMM, "sloi">, Sched<[]>; +def SROI : RVBShift_ri<0b00100, 0b101, OPC_OP_IMM, "sroi">, Sched<[]>; +} // Predicates = [HasStdExtZbb] + +let Predicates = [HasStdExtZbbOrZbp] in +def RORI : RVBShift_ri<0b01100, 0b101, OPC_OP_IMM, "rori">, Sched<[]>; + +let Predicates = [HasStdExtZbs] in { +def SBCLRI : RVBShift_ri<0b01001, 0b001, OPC_OP_IMM, "sbclri">, Sched<[]>; +def SBSETI : RVBShift_ri<0b00101, 0b001, OPC_OP_IMM, "sbseti">, Sched<[]>; +def SBINVI : RVBShift_ri<0b01101, 0b001, OPC_OP_IMM, "sbinvi">, Sched<[]>; +def SBEXTI : RVBShift_ri<0b01001, 0b101, OPC_OP_IMM, "sbexti">, Sched<[]>; +} // Predicates = [HasStdExtZbs] + +let Predicates = [HasStdExtZbp] in { +def GREVI : RVBShift_ri<0b01101, 0b101, OPC_OP_IMM, "grevi">, Sched<[]>; +def GORCI : RVBShift_ri<0b00101, 0b101, OPC_OP_IMM, "gorci">, Sched<[]>; +} // Predicates = [HasStdExtZbp] + +let Predicates = [HasStdExtZbt] in { +def CMIX : RVBTernaryR<0b11, 0b001, OPC_OP, "cmix", "$rd, $rs2, $rs1, $rs3">, + Sched<[]>; +def CMOV : RVBTernaryR<0b11, 0b101, OPC_OP, "cmov", "$rd, $rs2, $rs1, $rs3">, + Sched<[]>; +def FSL : RVBTernaryR<0b10, 0b001, OPC_OP, "fsl", "$rd, $rs1, $rs3, $rs2">, + Sched<[]>; +def FSR : RVBTernaryR<0b10, 0b101, OPC_OP, "fsr", "$rd, $rs1, $rs3, $rs2">, + Sched<[]>; +def FSRI : RVBTernaryImm6<0b101, OPC_OP_IMM, "fsri", + "$rd, $rs1, $rs3, $shamt">, Sched<[]>; +} // Predicates = [HasStdExtZbt] + +let Predicates = [HasStdExtZbb] in { +def CLZ : RVBUnary<0b0110000, 0b00000, 0b001, RISCVOpcode<0b0010011>, "clz">, + Sched<[]>; +def CTZ : RVBUnary<0b0110000, 0b00001, 0b001, RISCVOpcode<0b0010011>, "ctz">, + Sched<[]>; +def PCNT : RVBUnary<0b0110000, 0b00010, 0b001, RISCVOpcode<0b0010011>, "pcnt">, + Sched<[]>; +} // Predicates = [HasStdExtZbb] + +let Predicates = [HasStdExtZbm, IsRV64] in +def BMATFLIP : RVBUnary<0b0110000, 0b00011, 0b001, RISCVOpcode<0b0010011>, + "bmatflip">, Sched<[]>; + +let Predicates = [HasStdExtZbb] in { +def SEXTB : RVBUnary<0b0110000, 0b00100, 0b001, RISCVOpcode<0b0010011>, + "sext.b">, Sched<[]>; +def SEXTH : RVBUnary<0b0110000, 0b00101, 0b001, RISCVOpcode<0b0010011>, + "sext.h">, Sched<[]>; +} // Predicates = [HasStdExtZbb] + +let Predicates = [HasStdExtZbr] in { +def CRC32B : RVBUnary<0b0110000, 0b10000, 0b001, RISCVOpcode<0b0010011>, + "crc32.b">, Sched<[]>; +def CRC32H : RVBUnary<0b0110000, 0b10001, 0b001, RISCVOpcode<0b0010011>, + "crc32.h">, Sched<[]>; +def CRC32W : RVBUnary<0b0110000, 0b10010, 0b001, RISCVOpcode<0b0010011>, + "crc32.w">, Sched<[]>; +} // Predicates = [HasStdExtZbr] + +let Predicates = [HasStdExtZbr, IsRV64] in +def CRC32D : RVBUnary<0b0110000, 0b10011, 0b001, RISCVOpcode<0b0010011>, + "crc32.d">, Sched<[]>; + +let Predicates = [HasStdExtZbr] in { +def CRC32CB : RVBUnary<0b0110000, 0b11000, 0b001, RISCVOpcode<0b0010011>, + "crc32c.b">, Sched<[]>; +def CRC32CH : RVBUnary<0b0110000, 0b11001, 0b001, RISCVOpcode<0b0010011>, + "crc32c.h">, Sched<[]>; +def CRC32CW : RVBUnary<0b0110000, 0b11010, 0b001, RISCVOpcode<0b0010011>, + "crc32c.w">, Sched<[]>; +} // Predicates = [HasStdExtZbr] + +let Predicates = [HasStdExtZbr, IsRV64] in +def CRC32CD : RVBUnary<0b0110000, 0b11011, 0b001, RISCVOpcode<0b0010011>, + "crc32c.d">, Sched<[]>; + +let Predicates = [HasStdExtZbc] in { +def CLMUL : ALU_rr<0b0000101, 0b001, "clmul">, Sched<[]>; +def CLMULR : ALU_rr<0b0000101, 0b010, "clmulr">, Sched<[]>; +def CLMULH : ALU_rr<0b0000101, 0b011, "clmulh">, Sched<[]>; +} // Predicates = [HasStdExtZbc] + +let Predicates = [HasStdExtZbb] in { +def MIN : ALU_rr<0b0000101, 0b100, "min">, Sched<[]>; +def MAX : ALU_rr<0b0000101, 0b101, "max">, Sched<[]>; +def MINU : ALU_rr<0b0000101, 0b110, "minu">, Sched<[]>; +def MAXU : ALU_rr<0b0000101, 0b111, "maxu">, Sched<[]>; +} // Predicates = [HasStdExtZbb] + +let Predicates = [HasStdExtZbp] in { +def SHFL : ALU_rr<0b0000100, 0b001, "shfl">, Sched<[]>; +def UNSHFL : ALU_rr<0b0000100, 0b101, "unshfl">, Sched<[]>; +} // Predicates = [HasStdExtZbp] + +let Predicates = [HasStdExtZbe] in { +def BDEP : ALU_rr<0b0100100, 0b110, "bdep">, Sched<[]>; +def BEXT : ALU_rr<0b0000100, 0b110, "bext">, Sched<[]>; +} // Predicates = [HasStdExtZbe] + +let Predicates = [HasStdExtZbbOrZbp] in { +def PACK : ALU_rr<0b0000100, 0b100, "pack">, Sched<[]>; +def PACKU : ALU_rr<0b0100100, 0b100, "packu">, Sched<[]>; +} // Predicates = [HasStdExtZbbOrZbp] + +let Predicates = [HasStdExtZbm, IsRV64] in { +def BMATOR : ALU_rr<0b0000100, 0b011, "bmator">, Sched<[]>; +def BMATXOR : ALU_rr<0b0100100, 0b011, "bmatxor">, Sched<[]>; +} // Predicates = [HasStdExtZbm, IsRV64] + +let Predicates = [HasStdExtZbbOrZbp] in +def PACKH : ALU_rr<0b0000100, 0b111, "packh">, Sched<[]>; + +let Predicates = [HasStdExtZbf] in +def BFP : ALU_rr<0b0100100, 0b111, "bfp">, Sched<[]>; + +let Predicates = [HasStdExtZbp] in { +def SHFLI : RVBShfl_ri<0b000010, 0b001, OPC_OP_IMM, "shfli">, Sched<[]>; +def UNSHFLI : RVBShfl_ri<0b000010, 0b101, OPC_OP_IMM, "unshfli">, Sched<[]>; +} // Predicates = [HasStdExtZbp] + +let Predicates = [HasStdExtZbb, IsRV64] in { +def ADDIWU : RVBALUW_ri<0b100, "addiwu">, Sched<[]>; +def SLLIUW : RVBShift_ri<0b00001, 0b001, OPC_OP_IMM_32, "slliu.w">, Sched<[]>; +def ADDWU : ALUW_rr<0b0000101, 0b000, "addwu">, Sched<[]>; +def SUBWU : ALUW_rr<0b0100101, 0b000, "subwu">, Sched<[]>; +def ADDUW : ALUW_rr<0b0000100, 0b000, "addu.w">, Sched<[]>; +def SUBUW : ALUW_rr<0b0100100, 0b000, "subu.w">, Sched<[]>; +} // Predicates = [HasStdExtZbb, IsRV64] + +let Predicates = [HasStdExtZbb, IsRV64] in { +def SLOW : ALUW_rr<0b0010000, 0b001, "slow">, Sched<[]>; +def SROW : ALUW_rr<0b0010000, 0b101, "srow">, Sched<[]>; +} // Predicates = [HasStdExtZbb, IsRV64] + +let Predicates = [HasStdExtZbbOrZbp, IsRV64] in { +def ROLW : ALUW_rr<0b0110000, 0b001, "rolw">, Sched<[]>; +def RORW : ALUW_rr<0b0110000, 0b101, "rorw">, Sched<[]>; +} // Predicates = [HasStdExtZbbOrZbp, IsRV64] + +let Predicates = [HasStdExtZbs, IsRV64] in { +def SBCLRW : ALUW_rr<0b0100100, 0b001, "sbclrw">, Sched<[]>; +def SBSETW : ALUW_rr<0b0010100, 0b001, "sbsetw">, Sched<[]>; +def SBINVW : ALUW_rr<0b0110100, 0b001, "sbinvw">, Sched<[]>; +def SBEXTW : ALUW_rr<0b0100100, 0b101, "sbextw">, Sched<[]>; +} // Predicates = [HasStdExtZbs, IsRV64] + +let Predicates = [HasStdExtZbp, IsRV64] in { +def GORCW : ALUW_rr<0b0010100, 0b101, "gorcw">, Sched<[]>; +def GREVW : ALUW_rr<0b0110100, 0b101, "grevw">, Sched<[]>; +} // Predicates = [HasStdExtZbp, IsRV64] + +let Predicates = [HasStdExtZbb, IsRV64] in { +def SLOIW : RVBShiftW_ri<0b0010000, 0b001, OPC_OP_IMM_32, "sloiw">, Sched<[]>; +def SROIW : RVBShiftW_ri<0b0010000, 0b101, OPC_OP_IMM_32, "sroiw">, Sched<[]>; +} // Predicates = [HasStdExtZbb, IsRV64] + +let Predicates = [HasStdExtZbbOrZbp, IsRV64] in +def RORIW : RVBShiftW_ri<0b0110000, 0b101, OPC_OP_IMM_32, "roriw">, Sched<[]>; + +let Predicates = [HasStdExtZbs, IsRV64] in { +def SBCLRIW : RVBShiftW_ri<0b0100100, 0b001, OPC_OP_IMM_32, "sbclriw">, + Sched<[]>; +def SBSETIW : RVBShiftW_ri<0b0010100, 0b001, OPC_OP_IMM_32, "sbsetiw">, + Sched<[]>; +def SBINVIW : RVBShiftW_ri<0b0110100, 0b001, OPC_OP_IMM_32, "sbinviw">, + Sched<[]>; +} // Predicates = [HasStdExtZbs, IsRV64] + +let Predicates = [HasStdExtZbp, IsRV64] in { +def GORCIW : RVBShiftW_ri<0b0010100, 0b101, OPC_OP_IMM_32, "gorciw">, Sched<[]>; +def GREVIW : RVBShiftW_ri<0b0110100, 0b101, OPC_OP_IMM_32, "greviw">, Sched<[]>; +} // Predicates = [HasStdExtZbp, IsRV64] + +let Predicates = [HasStdExtZbt, IsRV64] in { +def FSLW : RVBTernaryR<0b10, 0b001, OPC_OP_32, + "fslw", "$rd, $rs1, $rs3, $rs2">, Sched<[]>; +def FSRW : RVBTernaryR<0b10, 0b101, OPC_OP_32, "fsrw", + "$rd, $rs1, $rs3, $rs2">, Sched<[]>; +def FSRIW : RVBTernaryImm5<0b10, 0b101, OPC_OP_IMM_32, + "fsriw", "$rd, $rs1, $rs3, $shamt">, Sched<[]>; +} // Predicates = [HasStdExtZbt, IsRV64] + +let Predicates = [HasStdExtZbb, IsRV64] in { +def CLZW : RVBUnary<0b0110000, 0b00000, 0b001, RISCVOpcode<0b0011011>, + "clzw">, Sched<[]>; +def CTZW : RVBUnary<0b0110000, 0b00001, 0b001, RISCVOpcode<0b0011011>, + "ctzw">, Sched<[]>; +def PCNTW : RVBUnary<0b0110000, 0b00010, 0b001, RISCVOpcode<0b0011011>, + "pcntw">, Sched<[]>; +} // Predicates = [HasStdExtZbb, IsRV64] + +let Predicates = [HasStdExtZbc, IsRV64] in { +def CLMULW : ALUW_rr<0b0000101, 0b001, "clmulw">, Sched<[]>; +def CLMULRW : ALUW_rr<0b0000101, 0b010, "clmulrw">, Sched<[]>; +def CLMULHW : ALUW_rr<0b0000101, 0b011, "clmulhw">, Sched<[]>; +} // Predicates = [HasStdExtZbc, IsRV64] + +let Predicates = [HasStdExtZbp, IsRV64] in { +def SHFLW : ALUW_rr<0b0000100, 0b001, "shflw">, Sched<[]>; +def UNSHFLW : ALUW_rr<0b0000100, 0b101, "unshflw">, Sched<[]>; +} // Predicates = [HasStdExtZbp, IsRV64] + +let Predicates = [HasStdExtZbe, IsRV64] in { +def BDEPW : ALUW_rr<0b0100100, 0b110, "bdepw">, Sched<[]>; +def BEXTW : ALUW_rr<0b0000100, 0b110, "bextw">, Sched<[]>; +} // Predicates = [HasStdExtZbe, IsRV64] + +let Predicates = [HasStdExtZbbOrZbp, IsRV64] in { +def PACKW : ALUW_rr<0b0000100, 0b100, "packw">, Sched<[]>; +def PACKUW : ALUW_rr<0b0100100, 0b100, "packuw">, Sched<[]>; +} // Predicates = [HasStdExtZbbOrZbp, IsRV64] + +let Predicates = [HasStdExtZbf, IsRV64] in +def BFPW : ALUW_rr<0b0100100, 0b111, "bfpw">, Sched<[]>; + +//===----------------------------------------------------------------------===// +// Future compressed instructions +//===----------------------------------------------------------------------===// + +// The presence of these instructions in the B extension is purely experimental +// and they should be moved to the C extension as soon as they are ratified. + +let hasSideEffects = 0, mayLoad = 0, mayStore = 0 in +class RVBInstC<bits<2> funct2, string opcodestr> + : RVInst16<(outs GPRC:$rs_wb), (ins GPRC:$rs), opcodestr, "$rs", [], + InstFormatCR> { + bits<3> rs; + let Constraints = "$rs = $rs_wb"; + + let Inst{15-12} = 0b0110; + let Inst{11-10} = funct2; + let Inst{9-7} = rs; + let Inst{6-0} = 0b0000001; +} + +// The namespace RVBC exists to avoid encoding conflicts with the compressed +// instructions c.addi16sp and c.lui already implemented in the C extension. + +let DecoderNamespace = "RVBC", Predicates = [HasStdExtZbproposedc, HasStdExtC] in { +def C_NOT : RVBInstC<0b00, "c.not">, Sched<[]>; +def C_NEG : RVBInstC<0b01, "c.neg">, Sched<[]>; +} // DecoderNamespace = "RVBC", Predicates = [HasStdExtZbproposedc, HasStdExtC] + +let DecoderNamespace = "RVBC", Predicates = [HasStdExtZbproposedc, HasStdExtZbbOrZbp, HasStdExtC, IsRV64] in +def C_ZEXTW : RVBInstC<0b10, "c.zext.w">, Sched<[]>; + +//===----------------------------------------------------------------------===// +// Pseudo Instructions +//===----------------------------------------------------------------------===// + +let Predicates = [HasStdExtZbb, IsRV32] in { +def : InstAlias<"zext.b $rd, $rs", (ANDI GPR:$rd, GPR:$rs, 0xFF)>; +def : InstAlias<"zext.h $rd, $rs", (PACK GPR:$rd, GPR:$rs, X0)>; +} // Predicates = [HasStdExtZbb, IsRV32] + +let Predicates = [HasStdExtZbb, IsRV64] in { +def : InstAlias<"zext.b $rd, $rs", (ANDI GPR:$rd, GPR:$rs, 0xFF)>; +def : InstAlias<"zext.h $rd, $rs", (PACKW GPR:$rd, GPR:$rs, X0)>; +def : InstAlias<"zext.w $rd, $rs", (PACK GPR:$rd, GPR:$rs, X0)>; +} // Predicates = [HasStdExtZbb, IsRV64] + +let Predicates = [HasStdExtZbbOrZbp] in { +def : InstAlias<"rev.p $rd, $rs", (GREVI GPR:$rd, GPR:$rs, 0b00001)>, + Sched<[]>; +def : InstAlias<"rev2.n $rd, $rs", (GREVI GPR:$rd, GPR:$rs, 0b00010)>, + Sched<[]>; +def : InstAlias<"rev.n $rd, $rs", (GREVI GPR:$rd, GPR:$rs, 0b00011)>, + Sched<[]>; +def : InstAlias<"rev4.b $rd, $rs", (GREVI GPR:$rd, GPR:$rs, 0b00100)>, + Sched<[]>; +def : InstAlias<"rev2.b $rd, $rs", (GREVI GPR:$rd, GPR:$rs, 0b00110)>, + Sched<[]>; +def : InstAlias<"rev.b $rd, $rs", (GREVI GPR:$rd, GPR:$rs, 0b00111)>, + Sched<[]>; +def : InstAlias<"rev8.h $rd, $rs", (GREVI GPR:$rd, GPR:$rs, 0b01000)>, + Sched<[]>; +def : InstAlias<"rev4.h $rd, $rs", (GREVI GPR:$rd, GPR:$rs, 0b01100)>, + Sched<[]>; +def : InstAlias<"rev2.h $rd, $rs", (GREVI GPR:$rd, GPR:$rs, 0b01110)>, + Sched<[]>; +def : InstAlias<"rev.h $rd, $rs", (GREVI GPR:$rd, GPR:$rs, 0b01111)>, + Sched<[]>; + +def : InstAlias<"zip.n $rd, $rs", (SHFLI GPR:$rd, GPR:$rs, 0b0001)>, + Sched<[]>; +def : InstAlias<"unzip.n $rd, $rs", (UNSHFLI GPR:$rd, GPR:$rs, 0b0001)>, + Sched<[]>; +def : InstAlias<"zip2.b $rd, $rs", (SHFLI GPR:$rd, GPR:$rs, 0b0010)>, + Sched<[]>; +def : InstAlias<"unzip2.b $rd, $rs", (UNSHFLI GPR:$rd, GPR:$rs, 0b0010)>, + Sched<[]>; +def : InstAlias<"zip.b $rd, $rs", (SHFLI GPR:$rd, GPR:$rs, 0b0011)>, + Sched<[]>; +def : InstAlias<"unzip.b $rd, $rs", (UNSHFLI GPR:$rd, GPR:$rs, 0b0011)>, + Sched<[]>; +def : InstAlias<"zip4.h $rd, $rs", (SHFLI GPR:$rd, GPR:$rs, 0b0100)>, + Sched<[]>; +def : InstAlias<"unzip4.h $rd, $rs", (UNSHFLI GPR:$rd, GPR:$rs, 0b0100)>, + Sched<[]>; +def : InstAlias<"zip2.h $rd, $rs", (SHFLI GPR:$rd, GPR:$rs, 0b0110)>, + Sched<[]>; +def : InstAlias<"unzip2.h $rd, $rs", (UNSHFLI GPR:$rd, GPR:$rs, 0b0110)>, + Sched<[]>; +def : InstAlias<"zip.h $rd, $rs", (SHFLI GPR:$rd, GPR:$rs, 0b0111)>, + Sched<[]>; +def : InstAlias<"unzip.h $rd, $rs", (UNSHFLI GPR:$rd, GPR:$rs, 0b0111)>, + Sched<[]>; + +def : InstAlias<"orc.p $rd, $rs", (GORCI GPR:$rd, GPR:$rs, 0b00001)>, + Sched<[]>; +def : InstAlias<"orc2.n $rd, $rs", (GORCI GPR:$rd, GPR:$rs, 0b00010)>, + Sched<[]>; +def : InstAlias<"orc.n $rd, $rs", (GORCI GPR:$rd, GPR:$rs, 0b00011)>, + Sched<[]>; +def : InstAlias<"orc4.b $rd, $rs", (GORCI GPR:$rd, GPR:$rs, 0b00100)>, + Sched<[]>; +def : InstAlias<"orc2.b $rd, $rs", (GORCI GPR:$rd, GPR:$rs, 0b00110)>, + Sched<[]>; +def : InstAlias<"orc.b $rd, $rs", (GORCI GPR:$rd, GPR:$rs, 0b00111)>, + Sched<[]>; +def : InstAlias<"orc8.h $rd, $rs", (GORCI GPR:$rd, GPR:$rs, 0b01000)>, + Sched<[]>; +def : InstAlias<"orc4.h $rd, $rs", (GORCI GPR:$rd, GPR:$rs, 0b01100)>, + Sched<[]>; +def : InstAlias<"orc2.h $rd, $rs", (GORCI GPR:$rd, GPR:$rs, 0b01110)>, + Sched<[]>; +def : InstAlias<"orc.h $rd, $rs", (GORCI GPR:$rd, GPR:$rs, 0b01111)>, + Sched<[]>; +} // Predicates = [HasStdExtZbbOrZbp] + +let Predicates = [HasStdExtZbbOrZbp, IsRV32] in { +def : InstAlias<"rev16 $rd, $rs", (GREVI GPR:$rd, GPR:$rs, 0b10000)>, Sched<[]>; +def : InstAlias<"rev8 $rd, $rs", (GREVI GPR:$rd, GPR:$rs, 0b11000)>, Sched<[]>; +def : InstAlias<"rev4 $rd, $rs", (GREVI GPR:$rd, GPR:$rs, 0b11100)>, Sched<[]>; +def : InstAlias<"rev2 $rd, $rs", (GREVI GPR:$rd, GPR:$rs, 0b11110)>, Sched<[]>; +def : InstAlias<"rev $rd, $rs", (GREVI GPR:$rd, GPR:$rs, 0b11111)>, Sched<[]>; + +def : InstAlias<"zip8 $rd, $rs", (SHFLI GPR:$rd, GPR:$rs, 0b1000)>, + Sched<[]>; +def : InstAlias<"unzip8 $rd, $rs", (UNSHFLI GPR:$rd, GPR:$rs, 0b1000)>, + Sched<[]>; +def : InstAlias<"zip4 $rd, $rs", (SHFLI GPR:$rd, GPR:$rs, 0b1100)>, + Sched<[]>; +def : InstAlias<"unzip4 $rd, $rs", (UNSHFLI GPR:$rd, GPR:$rs, 0b1100)>, + Sched<[]>; +def : InstAlias<"zip2 $rd, $rs", (SHFLI GPR:$rd, GPR:$rs, 0b1110)>, + Sched<[]>; +def : InstAlias<"unzip2 $rd, $rs", (UNSHFLI GPR:$rd, GPR:$rs, 0b1110)>, + Sched<[]>; +def : InstAlias<"zip $rd, $rs", (SHFLI GPR:$rd, GPR:$rs, 0b1111)>, + Sched<[]>; +def : InstAlias<"unzip $rd, $rs", (UNSHFLI GPR:$rd, GPR:$rs, 0b1111)>, + Sched<[]>; + +def : InstAlias<"orc16 $rd, $rs", (GORCI GPR:$rd, GPR:$rs, 0b10000)>, Sched<[]>; +def : InstAlias<"orc8 $rd, $rs", (GORCI GPR:$rd, GPR:$rs, 0b11000)>, Sched<[]>; +def : InstAlias<"orc4 $rd, $rs", (GORCI GPR:$rd, GPR:$rs, 0b11100)>, Sched<[]>; +def : InstAlias<"orc2 $rd, $rs", (GORCI GPR:$rd, GPR:$rs, 0b11110)>, Sched<[]>; +def : InstAlias<"orc $rd, $rs", (GORCI GPR:$rd, GPR:$rs, 0b11111)>, Sched<[]>; +} // Predicates = [HasStdExtZbbOrZbp, IsRV32] + +let Predicates = [HasStdExtZbbOrZbp, IsRV64] in { +def : InstAlias<"rev16.w $rd, $rs", (GREVI GPR:$rd, GPR:$rs, 0b010000)>, + Sched<[]>; +def : InstAlias<"rev8.w $rd, $rs", (GREVI GPR:$rd, GPR:$rs, 0b011000)>, + Sched<[]>; +def : InstAlias<"rev4.w $rd, $rs", (GREVI GPR:$rd, GPR:$rs, 0b011100)>, + Sched<[]>; +def : InstAlias<"rev2.w $rd, $rs", (GREVI GPR:$rd, GPR:$rs, 0b011110)>, + Sched<[]>; +def : InstAlias<"rev.w $rd, $rs", (GREVI GPR:$rd, GPR:$rs, 0b011111)>, + Sched<[]>; +def : InstAlias<"rev32 $rd, $rs", (GREVI GPR:$rd, GPR:$rs, 0b100000)>, + Sched<[]>; +def : InstAlias<"rev16 $rd, $rs", (GREVI GPR:$rd, GPR:$rs, 0b110000)>, + Sched<[]>; +def : InstAlias<"rev8 $rd, $rs", (GREVI GPR:$rd, GPR:$rs, 0b111000)>, + Sched<[]>; +def : InstAlias<"rev4 $rd, $rs", (GREVI GPR:$rd, GPR:$rs, 0b111100)>, + Sched<[]>; +def : InstAlias<"rev2 $rd, $rs", (GREVI GPR:$rd, GPR:$rs, 0b111110)>, + Sched<[]>; +def : InstAlias<"rev $rd, $rs", (GREVI GPR:$rd, GPR:$rs, 0b111111)>, + Sched<[]>; + +def : InstAlias<"zip8.w $rd, $rs", (SHFLI GPR:$rd, GPR:$rs, 0b01000)>, + Sched<[]>; +def : InstAlias<"unzip8.w $rd, $rs", (UNSHFLI GPR:$rd, GPR:$rs, 0b01000)>, + Sched<[]>; +def : InstAlias<"zip4.w $rd, $rs", (SHFLI GPR:$rd, GPR:$rs, 0b01100)>, + Sched<[]>; +def : InstAlias<"unzip4.w $rd, $rs", (UNSHFLI GPR:$rd, GPR:$rs, 0b01100)>, + Sched<[]>; +def : InstAlias<"zip2.w $rd, $rs", (SHFLI GPR:$rd, GPR:$rs, 0b01110)>, + Sched<[]>; +def : InstAlias<"unzip2.w $rd, $rs", (UNSHFLI GPR:$rd, GPR:$rs, 0b01110)>, + Sched<[]>; +def : InstAlias<"zip.w $rd, $rs", (SHFLI GPR:$rd, GPR:$rs, 0b01111)>, + Sched<[]>; +def : InstAlias<"unzip.w $rd, $rs", (UNSHFLI GPR:$rd, GPR:$rs, 0b01111)>, + Sched<[]>; +def : InstAlias<"zip16 $rd, $rs", (SHFLI GPR:$rd, GPR:$rs, 0b10000)>, + Sched<[]>; +def : InstAlias<"unzip16 $rd, $rs", (UNSHFLI GPR:$rd, GPR:$rs, 0b10000)>, + Sched<[]>; +def : InstAlias<"zip8 $rd, $rs", (SHFLI GPR:$rd, GPR:$rs, 0b11000)>, + Sched<[]>; +def : InstAlias<"unzip8 $rd, $rs", (UNSHFLI GPR:$rd, GPR:$rs, 0b11000)>, + Sched<[]>; +def : InstAlias<"zip4 $rd, $rs", (SHFLI GPR:$rd, GPR:$rs, 0b11100)>, + Sched<[]>; +def : InstAlias<"unzip4 $rd, $rs", (UNSHFLI GPR:$rd, GPR:$rs, 0b11100)>, + Sched<[]>; +def : InstAlias<"zip2 $rd, $rs", (SHFLI GPR:$rd, GPR:$rs, 0b11110)>, + Sched<[]>; +def : InstAlias<"unzip2 $rd, $rs", (UNSHFLI GPR:$rd, GPR:$rs, 0b11110)>, + Sched<[]>; +def : InstAlias<"zip $rd, $rs", (SHFLI GPR:$rd, GPR:$rs, 0b11111)>, + Sched<[]>; +def : InstAlias<"unzip $rd, $rs", (UNSHFLI GPR:$rd, GPR:$rs, 0b11111)>, + Sched<[]>; + +def : InstAlias<"orc16.w $rd, $rs", (GORCI GPR:$rd, GPR:$rs, 0b010000)>, + Sched<[]>; +def : InstAlias<"orc8.w $rd, $rs", (GORCI GPR:$rd, GPR:$rs, 0b011000)>, + Sched<[]>; +def : InstAlias<"orc4.w $rd, $rs", (GORCI GPR:$rd, GPR:$rs, 0b011100)>, + Sched<[]>; +def : InstAlias<"orc2.w $rd, $rs", (GORCI GPR:$rd, GPR:$rs, 0b011110)>, + Sched<[]>; +def : InstAlias<"orc.w $rd, $rs", (GORCI GPR:$rd, GPR:$rs, 0b011111)>, + Sched<[]>; +def : InstAlias<"orc32 $rd, $rs", (GORCI GPR:$rd, GPR:$rs, 0b100000)>, + Sched<[]>; +def : InstAlias<"orc16 $rd, $rs", (GORCI GPR:$rd, GPR:$rs, 0b110000)>, + Sched<[]>; +def : InstAlias<"orc8 $rd, $rs", (GORCI GPR:$rd, GPR:$rs, 0b111000)>, + Sched<[]>; +def : InstAlias<"orc4 $rd, $rs", (GORCI GPR:$rd, GPR:$rs, 0b111100)>, + Sched<[]>; +def : InstAlias<"orc2 $rd, $rs", (GORCI GPR:$rd, GPR:$rs, 0b111110)>, + Sched<[]>; +def : InstAlias<"orc $rd, $rs", (GORCI GPR:$rd, GPR:$rs, 0b111111)>, + Sched<[]>; +} // Predicates = [HasStdExtZbbOrZbp, IsRV64] + +//===----------------------------------------------------------------------===// +// Compressed Instruction patterns +//===----------------------------------------------------------------------===// +let Predicates = [HasStdExtZbproposedc, HasStdExtC] in { +def : CompressPat<(XORI GPRC:$rs1, GPRC:$rs1, -1), + (C_NOT GPRC:$rs1)>; +def : CompressPat<(SUB GPRC:$rs1, X0, GPRC:$rs1), + (C_NEG GPRC:$rs1)>; +} // Predicates = [HasStdExtZbproposedc, HasStdExtC] + +let Predicates = [HasStdExtZbproposedc, HasStdExtZbbOrZbp, HasStdExtC, IsRV64] in { +def : CompressPat<(PACK GPRC:$rs1, GPRC:$rs1, X0), + (C_ZEXTW GPRC:$rs1)>; +} // Predicates = [HasStdExtZbproposedc, HasStdExtC, IsRV64] diff --git a/llvm/lib/Target/RISCV/RISCVInstrInfoC.td b/llvm/lib/Target/RISCV/RISCVInstrInfoC.td index fa0050f107b2..f68767847ade 100644 --- a/llvm/lib/Target/RISCV/RISCVInstrInfoC.td +++ b/llvm/lib/Target/RISCV/RISCVInstrInfoC.td @@ -282,7 +282,8 @@ let Predicates = [HasStdExtC] in { let hasSideEffects = 0, mayLoad = 0, mayStore = 0, Uses = [X2] in def C_ADDI4SPN : RVInst16CIW<0b000, 0b00, (outs GPRC:$rd), (ins SP:$rs1, uimm10_lsb00nonzero:$imm), - "c.addi4spn", "$rd, $rs1, $imm"> { + "c.addi4spn", "$rd, $rs1, $imm">, + Sched<[WriteIALU, ReadIALU]> { bits<5> rs1; let Inst{12-11} = imm{5-4}; let Inst{10-7} = imm{9-6}; @@ -291,13 +292,15 @@ def C_ADDI4SPN : RVInst16CIW<0b000, 0b00, (outs GPRC:$rd), } let Predicates = [HasStdExtC, HasStdExtD] in -def C_FLD : CLoad_ri<0b001, "c.fld", FPR64C, uimm8_lsb000> { +def C_FLD : CLoad_ri<0b001, "c.fld", FPR64C, uimm8_lsb000>, + Sched<[WriteFLD64, ReadMemBase]> { bits<8> imm; let Inst{12-10} = imm{5-3}; let Inst{6-5} = imm{7-6}; } -def C_LW : CLoad_ri<0b010, "c.lw", GPRC, uimm7_lsb00> { +def C_LW : CLoad_ri<0b010, "c.lw", GPRC, uimm7_lsb00>, + Sched<[WriteLDW, ReadMemBase]> { bits<7> imm; let Inst{12-10} = imm{5-3}; let Inst{6} = imm{2}; @@ -306,7 +309,8 @@ def C_LW : CLoad_ri<0b010, "c.lw", GPRC, uimm7_lsb00> { let DecoderNamespace = "RISCV32Only_", Predicates = [HasStdExtC, HasStdExtF, IsRV32] in -def C_FLW : CLoad_ri<0b011, "c.flw", FPR32C, uimm7_lsb00> { +def C_FLW : CLoad_ri<0b011, "c.flw", FPR32C, uimm7_lsb00>, + Sched<[WriteFLD32, ReadMemBase]> { bits<7> imm; let Inst{12-10} = imm{5-3}; let Inst{6} = imm{2}; @@ -314,20 +318,23 @@ def C_FLW : CLoad_ri<0b011, "c.flw", FPR32C, uimm7_lsb00> { } let Predicates = [HasStdExtC, IsRV64] in -def C_LD : CLoad_ri<0b011, "c.ld", GPRC, uimm8_lsb000> { +def C_LD : CLoad_ri<0b011, "c.ld", GPRC, uimm8_lsb000>, + Sched<[WriteLDD, ReadMemBase]> { bits<8> imm; let Inst{12-10} = imm{5-3}; let Inst{6-5} = imm{7-6}; } let Predicates = [HasStdExtC, HasStdExtD] in -def C_FSD : CStore_rri<0b101, "c.fsd", FPR64C, uimm8_lsb000> { +def C_FSD : CStore_rri<0b101, "c.fsd", FPR64C, uimm8_lsb000>, + Sched<[WriteFST64, ReadStoreData, ReadMemBase]> { bits<8> imm; let Inst{12-10} = imm{5-3}; let Inst{6-5} = imm{7-6}; } -def C_SW : CStore_rri<0b110, "c.sw", GPRC, uimm7_lsb00> { +def C_SW : CStore_rri<0b110, "c.sw", GPRC, uimm7_lsb00>, + Sched<[WriteSTW, ReadStoreData, ReadMemBase]> { bits<7> imm; let Inst{12-10} = imm{5-3}; let Inst{6} = imm{2}; @@ -336,7 +343,8 @@ def C_SW : CStore_rri<0b110, "c.sw", GPRC, uimm7_lsb00> { let DecoderNamespace = "RISCV32Only_", Predicates = [HasStdExtC, HasStdExtF, IsRV32] in -def C_FSW : CStore_rri<0b111, "c.fsw", FPR32C, uimm7_lsb00> { +def C_FSW : CStore_rri<0b111, "c.fsw", FPR32C, uimm7_lsb00>, + Sched<[WriteFST32, ReadStoreData, ReadMemBase]> { bits<7> imm; let Inst{12-10} = imm{5-3}; let Inst{6} = imm{2}; @@ -344,14 +352,16 @@ def C_FSW : CStore_rri<0b111, "c.fsw", FPR32C, uimm7_lsb00> { } let Predicates = [HasStdExtC, IsRV64] in -def C_SD : CStore_rri<0b111, "c.sd", GPRC, uimm8_lsb000> { +def C_SD : CStore_rri<0b111, "c.sd", GPRC, uimm8_lsb000>, + Sched<[WriteSTD, ReadStoreData, ReadMemBase]> { bits<8> imm; let Inst{12-10} = imm{5-3}; let Inst{6-5} = imm{7-6}; } let rd = 0, imm = 0, hasSideEffects = 0, mayLoad = 0, mayStore = 0 in -def C_NOP : RVInst16CI<0b000, 0b01, (outs), (ins), "c.nop", ""> +def C_NOP : RVInst16CI<0b000, 0b01, (outs), (ins), "c.nop", "">, + Sched<[WriteNop]> { let Inst{6-2} = 0; } @@ -359,7 +369,8 @@ def C_NOP : RVInst16CI<0b000, 0b01, (outs), (ins), "c.nop", ""> let hasSideEffects = 0, mayLoad = 0, mayStore = 0 in def C_ADDI : RVInst16CI<0b000, 0b01, (outs GPRNoX0:$rd_wb), (ins GPRNoX0:$rd, simm6nonzero:$imm), - "c.addi", "$rd, $imm"> { + "c.addi", "$rd, $imm">, + Sched<[WriteIALU, ReadIALU]> { let Constraints = "$rd = $rd_wb"; let Inst{6-2} = imm{4-0}; } @@ -367,7 +378,8 @@ def C_ADDI : RVInst16CI<0b000, 0b01, (outs GPRNoX0:$rd_wb), let hasSideEffects = 0, mayLoad = 0, mayStore = 0 in def C_ADDI_NOP : RVInst16CI<0b000, 0b01, (outs GPRX0:$rd_wb), (ins GPRX0:$rd, immzero:$imm), - "c.addi", "$rd, $imm"> { + "c.addi", "$rd, $imm">, + Sched<[WriteIALU, ReadIALU]> { let Constraints = "$rd = $rd_wb"; let Inst{6-2} = 0; let isAsmParserOnly = 1; @@ -377,27 +389,30 @@ let hasSideEffects = 0, mayLoad = 0, mayStore = 0, isCall = 1, DecoderNamespace = "RISCV32Only_", Defs = [X1], Predicates = [HasStdExtC, IsRV32] in def C_JAL : RVInst16CJ<0b001, 0b01, (outs), (ins simm12_lsb0:$offset), - "c.jal", "$offset">; + "c.jal", "$offset">, Sched<[WriteJal]>; let hasSideEffects = 0, mayLoad = 0, mayStore = 0, Predicates = [HasStdExtC, IsRV64] in def C_ADDIW : RVInst16CI<0b001, 0b01, (outs GPRNoX0:$rd_wb), (ins GPRNoX0:$rd, simm6:$imm), - "c.addiw", "$rd, $imm"> { + "c.addiw", "$rd, $imm">, + Sched<[WriteIALU32, ReadIALU32]> { let Constraints = "$rd = $rd_wb"; let Inst{6-2} = imm{4-0}; } let hasSideEffects = 0, mayLoad = 0, mayStore = 0 in def C_LI : RVInst16CI<0b010, 0b01, (outs GPRNoX0:$rd), (ins simm6:$imm), - "c.li", "$rd, $imm"> { + "c.li", "$rd, $imm">, + Sched<[WriteIALU]> { let Inst{6-2} = imm{4-0}; } let hasSideEffects = 0, mayLoad = 0, mayStore = 0 in def C_ADDI16SP : RVInst16CI<0b011, 0b01, (outs SP:$rd_wb), (ins SP:$rd, simm10_lsb0000nonzero:$imm), - "c.addi16sp", "$rd, $imm"> { + "c.addi16sp", "$rd, $imm">, + Sched<[WriteIALU, ReadIALU]> { let Constraints = "$rd = $rd_wb"; let Inst{12} = imm{9}; let Inst{11-7} = 2; @@ -410,78 +425,93 @@ def C_ADDI16SP : RVInst16CI<0b011, 0b01, (outs SP:$rd_wb), let hasSideEffects = 0, mayLoad = 0, mayStore = 0 in def C_LUI : RVInst16CI<0b011, 0b01, (outs GPRNoX0X2:$rd), (ins c_lui_imm:$imm), - "c.lui", "$rd, $imm"> { + "c.lui", "$rd, $imm">, + Sched<[WriteIALU]> { let Inst{6-2} = imm{4-0}; } -def C_SRLI : Shift_right<0b00, "c.srli", GPRC, uimmlog2xlennonzero>; -def C_SRAI : Shift_right<0b01, "c.srai", GPRC, uimmlog2xlennonzero>; +def C_SRLI : Shift_right<0b00, "c.srli", GPRC, uimmlog2xlennonzero>, + Sched<[WriteShift, ReadShift]>; +def C_SRAI : Shift_right<0b01, "c.srai", GPRC, uimmlog2xlennonzero>, + Sched<[WriteShift, ReadShift]>; let hasSideEffects = 0, mayLoad = 0, mayStore = 0 in def C_ANDI : RVInst16CB<0b100, 0b01, (outs GPRC:$rs1_wb), (ins GPRC:$rs1, simm6:$imm), - "c.andi", "$rs1, $imm"> { + "c.andi", "$rs1, $imm">, + Sched<[WriteIALU, ReadIALU]> { let Constraints = "$rs1 = $rs1_wb"; let Inst{12} = imm{5}; let Inst{11-10} = 0b10; let Inst{6-2} = imm{4-0}; } -def C_SUB : CS_ALU<0b100011, 0b00, "c.sub", GPRC>; -def C_XOR : CS_ALU<0b100011, 0b01, "c.xor", GPRC>; -def C_OR : CS_ALU<0b100011, 0b10, "c.or" , GPRC>; -def C_AND : CS_ALU<0b100011, 0b11, "c.and", GPRC>; +def C_SUB : CS_ALU<0b100011, 0b00, "c.sub", GPRC>, + Sched<[WriteIALU, ReadIALU, ReadIALU]>; +def C_XOR : CS_ALU<0b100011, 0b01, "c.xor", GPRC>, + Sched<[WriteIALU, ReadIALU, ReadIALU]>; +def C_OR : CS_ALU<0b100011, 0b10, "c.or" , GPRC>, + Sched<[WriteIALU, ReadIALU, ReadIALU]>; +def C_AND : CS_ALU<0b100011, 0b11, "c.and", GPRC>, + Sched<[WriteIALU, ReadIALU, ReadIALU]>; let Predicates = [HasStdExtC, IsRV64] in { -def C_SUBW : CS_ALU<0b100111, 0b00, "c.subw", GPRC>; -def C_ADDW : CS_ALU<0b100111, 0b01, "c.addw", GPRC>; +def C_SUBW : CS_ALU<0b100111, 0b00, "c.subw", GPRC>, + Sched<[WriteIALU32, ReadIALU32, ReadIALU32]>; +def C_ADDW : CS_ALU<0b100111, 0b01, "c.addw", GPRC>, + Sched<[WriteIALU32, ReadIALU32, ReadIALU32]>; } let hasSideEffects = 0, mayLoad = 0, mayStore = 0 in def C_J : RVInst16CJ<0b101, 0b01, (outs), (ins simm12_lsb0:$offset), - "c.j", "$offset"> { + "c.j", "$offset">, Sched<[WriteJmp]> { let isBranch = 1; let isTerminator=1; let isBarrier=1; } -def C_BEQZ : Bcz<0b110, "c.beqz", seteq, GPRC>; -def C_BNEZ : Bcz<0b111, "c.bnez", setne, GPRC>; +def C_BEQZ : Bcz<0b110, "c.beqz", seteq, GPRC>, Sched<[WriteJmp]>; +def C_BNEZ : Bcz<0b111, "c.bnez", setne, GPRC>, Sched<[WriteJmp]>; let hasSideEffects = 0, mayLoad = 0, mayStore = 0 in def C_SLLI : RVInst16CI<0b000, 0b10, (outs GPRNoX0:$rd_wb), (ins GPRNoX0:$rd, uimmlog2xlennonzero:$imm), - "c.slli" ,"$rd, $imm"> { + "c.slli" ,"$rd, $imm">, + Sched<[WriteShift, ReadShift]> { let Constraints = "$rd = $rd_wb"; let Inst{6-2} = imm{4-0}; } let Predicates = [HasStdExtC, HasStdExtD] in -def C_FLDSP : CStackLoad<0b001, "c.fldsp", FPR64, uimm9_lsb000> { +def C_FLDSP : CStackLoad<0b001, "c.fldsp", FPR64, uimm9_lsb000>, + Sched<[WriteFLD64, ReadMemBase]> { let Inst{6-5} = imm{4-3}; let Inst{4-2} = imm{8-6}; } -def C_LWSP : CStackLoad<0b010, "c.lwsp", GPRNoX0, uimm8_lsb00> { +def C_LWSP : CStackLoad<0b010, "c.lwsp", GPRNoX0, uimm8_lsb00>, + Sched<[WriteLDW, ReadMemBase]> { let Inst{6-4} = imm{4-2}; let Inst{3-2} = imm{7-6}; } let DecoderNamespace = "RISCV32Only_", Predicates = [HasStdExtC, HasStdExtF, IsRV32] in -def C_FLWSP : CStackLoad<0b011, "c.flwsp", FPR32, uimm8_lsb00> { +def C_FLWSP : CStackLoad<0b011, "c.flwsp", FPR32, uimm8_lsb00>, + Sched<[WriteFLD32, ReadMemBase]> { let Inst{6-4} = imm{4-2}; let Inst{3-2} = imm{7-6}; } let Predicates = [HasStdExtC, IsRV64] in -def C_LDSP : CStackLoad<0b011, "c.ldsp", GPRNoX0, uimm9_lsb000> { +def C_LDSP : CStackLoad<0b011, "c.ldsp", GPRNoX0, uimm9_lsb000>, + Sched<[WriteLDD, ReadMemBase]> { let Inst{6-5} = imm{4-3}; let Inst{4-2} = imm{8-6}; } let hasSideEffects = 0, mayLoad = 0, mayStore = 0 in def C_JR : RVInst16CR<0b1000, 0b10, (outs), (ins GPRNoX0:$rs1), - "c.jr", "$rs1"> { + "c.jr", "$rs1">, Sched<[WriteJmpReg]> { let isBranch = 1; let isBarrier = 1; let isTerminator = 1; @@ -491,43 +521,49 @@ def C_JR : RVInst16CR<0b1000, 0b10, (outs), (ins GPRNoX0:$rs1), let hasSideEffects = 0, mayLoad = 0, mayStore = 0 in def C_MV : RVInst16CR<0b1000, 0b10, (outs GPRNoX0:$rs1), (ins GPRNoX0:$rs2), - "c.mv", "$rs1, $rs2">; + "c.mv", "$rs1, $rs2">, + Sched<[WriteIALU, ReadIALU]>; let rs1 = 0, rs2 = 0, hasSideEffects = 1, mayLoad = 0, mayStore = 0 in -def C_EBREAK : RVInst16CR<0b1001, 0b10, (outs), (ins), "c.ebreak", "">; +def C_EBREAK : RVInst16CR<0b1001, 0b10, (outs), (ins), "c.ebreak", "">, Sched<[]>; let hasSideEffects = 0, mayLoad = 0, mayStore = 0, isCall=1, Defs=[X1], rs2 = 0 in def C_JALR : RVInst16CR<0b1001, 0b10, (outs), (ins GPRNoX0:$rs1), - "c.jalr", "$rs1">; + "c.jalr", "$rs1">, Sched<[WriteJalr, ReadJalr]>; let hasSideEffects = 0, mayLoad = 0, mayStore = 0 in def C_ADD : RVInst16CR<0b1001, 0b10, (outs GPRNoX0:$rs1_wb), (ins GPRNoX0:$rs1, GPRNoX0:$rs2), - "c.add", "$rs1, $rs2"> { + "c.add", "$rs1, $rs2">, + Sched<[WriteIALU, ReadIALU, ReadIALU]> { let Constraints = "$rs1 = $rs1_wb"; } let Predicates = [HasStdExtC, HasStdExtD] in -def C_FSDSP : CStackStore<0b101, "c.fsdsp", FPR64, uimm9_lsb000> { +def C_FSDSP : CStackStore<0b101, "c.fsdsp", FPR64, uimm9_lsb000>, + Sched<[WriteFST64, ReadStoreData, ReadMemBase]> { let Inst{12-10} = imm{5-3}; let Inst{9-7} = imm{8-6}; } -def C_SWSP : CStackStore<0b110, "c.swsp", GPR, uimm8_lsb00> { +def C_SWSP : CStackStore<0b110, "c.swsp", GPR, uimm8_lsb00>, + Sched<[WriteSTW, ReadStoreData, ReadMemBase]> { let Inst{12-9} = imm{5-2}; let Inst{8-7} = imm{7-6}; } let DecoderNamespace = "RISCV32Only_", Predicates = [HasStdExtC, HasStdExtF, IsRV32] in -def C_FSWSP : CStackStore<0b111, "c.fswsp", FPR32, uimm8_lsb00> { +def C_FSWSP : CStackStore<0b111, "c.fswsp", FPR32, uimm8_lsb00>, + Sched<[WriteFST32, ReadStoreData, ReadMemBase]> { let Inst{12-9} = imm{5-2}; let Inst{8-7} = imm{7-6}; } let Predicates = [HasStdExtC, IsRV64] in -def C_SDSP : CStackStore<0b111, "c.sdsp", GPR, uimm9_lsb000> { +def C_SDSP : CStackStore<0b111, "c.sdsp", GPR, uimm9_lsb000>, + Sched<[WriteSTD, ReadStoreData, ReadMemBase]> { let Inst{12-10} = imm{5-3}; let Inst{9-7} = imm{8-6}; } @@ -535,7 +571,8 @@ def C_SDSP : CStackStore<0b111, "c.sdsp", GPR, uimm9_lsb000> { // The all zeros pattern isn't a valid RISC-V instruction. It's used by GNU // binutils as 16-bit instruction known to be unimplemented (i.e., trapping). let hasSideEffects = 1, mayLoad = 0, mayStore = 0 in -def C_UNIMP : RVInst16<(outs), (ins), "c.unimp", "", [], InstFormatOther> { +def C_UNIMP : RVInst16<(outs), (ins), "c.unimp", "", [], InstFormatOther>, + Sched<[]> { let Inst{15-0} = 0; } @@ -551,7 +588,7 @@ let Predicates = [HasStdExtC, HasRVCHints], hasSideEffects = 0, mayLoad = 0, let rd = 0 in def C_NOP_HINT : RVInst16CI<0b000, 0b01, (outs), (ins simm6nonzero:$imm), - "c.nop", "$imm"> { + "c.nop", "$imm">, Sched<[WriteNop]> { let Inst{6-2} = imm{4-0}; let DecoderMethod = "decodeRVCInstrSImm"; } @@ -559,7 +596,8 @@ def C_NOP_HINT : RVInst16CI<0b000, 0b01, (outs), (ins simm6nonzero:$imm), // Just a different syntax for the c.nop hint: c.addi x0, simm6 vs c.nop simm6. def C_ADDI_HINT_X0 : RVInst16CI<0b000, 0b01, (outs GPRX0:$rd_wb), (ins GPRX0:$rd, simm6nonzero:$imm), - "c.addi", "$rd, $imm"> { + "c.addi", "$rd, $imm">, + Sched<[WriteIALU, ReadIALU]> { let Constraints = "$rd = $rd_wb"; let Inst{6-2} = imm{4-0}; let isAsmParserOnly = 1; @@ -567,14 +605,16 @@ def C_ADDI_HINT_X0 : RVInst16CI<0b000, 0b01, (outs GPRX0:$rd_wb), def C_ADDI_HINT_IMM_ZERO : RVInst16CI<0b000, 0b01, (outs GPRNoX0:$rd_wb), (ins GPRNoX0:$rd, immzero:$imm), - "c.addi", "$rd, $imm"> { + "c.addi", "$rd, $imm">, + Sched<[WriteIALU, ReadIALU]> { let Constraints = "$rd = $rd_wb"; let Inst{6-2} = 0; let isAsmParserOnly = 1; } def C_LI_HINT : RVInst16CI<0b010, 0b01, (outs GPRX0:$rd), (ins simm6:$imm), - "c.li", "$rd, $imm"> { + "c.li", "$rd, $imm">, + Sched<[WriteIALU]> { let Inst{6-2} = imm{4-0}; let Inst{11-7} = 0; let DecoderMethod = "decodeRVCInstrRdSImm"; @@ -582,14 +622,15 @@ def C_LI_HINT : RVInst16CI<0b010, 0b01, (outs GPRX0:$rd), (ins simm6:$imm), def C_LUI_HINT : RVInst16CI<0b011, 0b01, (outs GPRX0:$rd), (ins c_lui_imm:$imm), - "c.lui", "$rd, $imm"> { + "c.lui", "$rd, $imm">, + Sched<[WriteIALU]> { let Inst{6-2} = imm{4-0}; let Inst{11-7} = 0; let DecoderMethod = "decodeRVCInstrRdSImm"; } def C_MV_HINT : RVInst16CR<0b1000, 0b10, (outs GPRX0:$rs1), (ins GPRNoX0:$rs2), - "c.mv", "$rs1, $rs2"> + "c.mv", "$rs1, $rs2">, Sched<[WriteIALU, ReadIALU]> { let Inst{11-7} = 0; let DecoderMethod = "decodeRVCInstrRdRs2"; @@ -597,7 +638,8 @@ def C_MV_HINT : RVInst16CR<0b1000, 0b10, (outs GPRX0:$rs1), (ins GPRNoX0:$rs2), def C_ADD_HINT : RVInst16CR<0b1001, 0b10, (outs GPRX0:$rs1_wb), (ins GPRX0:$rs1, GPRNoX0:$rs2), - "c.add", "$rs1, $rs2"> { + "c.add", "$rs1, $rs2">, + Sched<[WriteIALU, ReadIALU, ReadIALU]> { let Constraints = "$rs1 = $rs1_wb"; let Inst{11-7} = 0; let DecoderMethod = "decodeRVCInstrRdRs1Rs2"; @@ -605,7 +647,8 @@ def C_ADD_HINT : RVInst16CR<0b1001, 0b10, (outs GPRX0:$rs1_wb), def C_SLLI_HINT : RVInst16CI<0b000, 0b10, (outs GPRX0:$rd_wb), (ins GPRX0:$rd, uimmlog2xlennonzero:$imm), - "c.slli" ,"$rd, $imm"> { + "c.slli" ,"$rd, $imm">, + Sched<[WriteShift, ReadShift]> { let Constraints = "$rd = $rd_wb"; let Inst{6-2} = imm{4-0}; let Inst{11-7} = 0; @@ -613,7 +656,8 @@ def C_SLLI_HINT : RVInst16CI<0b000, 0b10, (outs GPRX0:$rd_wb), } def C_SLLI64_HINT : RVInst16CI<0b000, 0b10, (outs GPR:$rd_wb), (ins GPR:$rd), - "c.slli64" ,"$rd"> { + "c.slli64" ,"$rd">, + Sched<[WriteShift, ReadShift]> { let Constraints = "$rd = $rd_wb"; let Inst{6-2} = 0; let Inst{12} = 0; @@ -621,7 +665,8 @@ def C_SLLI64_HINT : RVInst16CI<0b000, 0b10, (outs GPR:$rd_wb), (ins GPR:$rd), def C_SRLI64_HINT : RVInst16CI<0b100, 0b01, (outs GPRC:$rd_wb), (ins GPRC:$rd), - "c.srli64", "$rd"> { + "c.srli64", "$rd">, + Sched<[WriteShift, ReadShift]> { let Constraints = "$rd = $rd_wb"; let Inst{6-2} = 0; let Inst{11-10} = 0; @@ -630,7 +675,8 @@ def C_SRLI64_HINT : RVInst16CI<0b100, 0b01, (outs GPRC:$rd_wb), def C_SRAI64_HINT : RVInst16CI<0b100, 0b01, (outs GPRC:$rd_wb), (ins GPRC:$rd), - "c.srai64", "$rd"> { + "c.srai64", "$rd">, + Sched<[WriteShift, ReadShift]> { let Constraints = "$rd = $rd_wb"; let Inst{6-2} = 0; let Inst{11-10} = 1; diff --git a/llvm/lib/Target/RISCV/RISCVInstrInfoD.td b/llvm/lib/Target/RISCV/RISCVInstrInfoD.td index b5343e8a8309..6c36f53cd563 100644 --- a/llvm/lib/Target/RISCV/RISCVInstrInfoD.td +++ b/llvm/lib/Target/RISCV/RISCVInstrInfoD.td @@ -57,7 +57,8 @@ class FPALUDDynFrmAlias<FPALUD_rr_frm Inst, string OpcodeStr> let hasSideEffects = 0, mayLoad = 0, mayStore = 0 in class FPCmpD_rr<bits<3> funct3, string opcodestr> : RVInstR<0b1010001, funct3, OPC_OP_FP, (outs GPR:$rd), - (ins FPR64:$rs1, FPR64:$rs2), opcodestr, "$rd, $rs1, $rs2">; + (ins FPR64:$rs1, FPR64:$rs2), opcodestr, "$rd, $rs1, $rs2">, + Sched<[WriteFCmp64, ReadFCmp64, ReadFCmp64]>; //===----------------------------------------------------------------------===// // Instructions @@ -68,7 +69,8 @@ let Predicates = [HasStdExtD] in { let hasSideEffects = 0, mayLoad = 1, mayStore = 0 in def FLD : RVInstI<0b011, OPC_LOAD_FP, (outs FPR64:$rd), (ins GPR:$rs1, simm12:$imm12), - "fld", "$rd, ${imm12}(${rs1})">; + "fld", "$rd, ${imm12}(${rs1})">, + Sched<[WriteFLD64, ReadFMemBase]>; // Operands for stores are in the order srcreg, base, offset rather than // reflecting the order these fields are specified in the instruction @@ -76,43 +78,60 @@ def FLD : RVInstI<0b011, OPC_LOAD_FP, (outs FPR64:$rd), let hasSideEffects = 0, mayLoad = 0, mayStore = 1 in def FSD : RVInstS<0b011, OPC_STORE_FP, (outs), (ins FPR64:$rs2, GPR:$rs1, simm12:$imm12), - "fsd", "$rs2, ${imm12}(${rs1})">; + "fsd", "$rs2, ${imm12}(${rs1})">, + Sched<[WriteFST64, ReadStoreData, ReadFMemBase]>; -def FMADD_D : FPFMAD_rrr_frm<OPC_MADD, "fmadd.d">; +def FMADD_D : FPFMAD_rrr_frm<OPC_MADD, "fmadd.d">, + Sched<[WriteFMulAdd64, ReadFMulAdd64, ReadFMulAdd64, ReadFMulAdd64]>; def : FPFMADDynFrmAlias<FMADD_D, "fmadd.d">; -def FMSUB_D : FPFMAD_rrr_frm<OPC_MSUB, "fmsub.d">; +def FMSUB_D : FPFMAD_rrr_frm<OPC_MSUB, "fmsub.d">, + Sched<[WriteFMulSub64, ReadFMulSub64, ReadFMulSub64, ReadFMulSub64]>; def : FPFMADDynFrmAlias<FMSUB_D, "fmsub.d">; -def FNMSUB_D : FPFMAD_rrr_frm<OPC_NMSUB, "fnmsub.d">; +def FNMSUB_D : FPFMAD_rrr_frm<OPC_NMSUB, "fnmsub.d">, + Sched<[WriteFMulSub64, ReadFMulSub64, ReadFMulSub64, ReadFMulSub64]>; def : FPFMADDynFrmAlias<FNMSUB_D, "fnmsub.d">; -def FNMADD_D : FPFMAD_rrr_frm<OPC_NMADD, "fnmadd.d">; +def FNMADD_D : FPFMAD_rrr_frm<OPC_NMADD, "fnmadd.d">, + Sched<[WriteFMulAdd64, ReadFMulAdd64, ReadFMulAdd64, ReadFMulAdd64]>; def : FPFMADDynFrmAlias<FNMADD_D, "fnmadd.d">; -def FADD_D : FPALUD_rr_frm<0b0000001, "fadd.d">; +def FADD_D : FPALUD_rr_frm<0b0000001, "fadd.d">, + Sched<[WriteFALU64, ReadFALU64, ReadFALU64]>; def : FPALUDDynFrmAlias<FADD_D, "fadd.d">; -def FSUB_D : FPALUD_rr_frm<0b0000101, "fsub.d">; +def FSUB_D : FPALUD_rr_frm<0b0000101, "fsub.d">, + Sched<[WriteFALU64, ReadFALU64, ReadFALU64]>; def : FPALUDDynFrmAlias<FSUB_D, "fsub.d">; -def FMUL_D : FPALUD_rr_frm<0b0001001, "fmul.d">; +def FMUL_D : FPALUD_rr_frm<0b0001001, "fmul.d">, + Sched<[WriteFMul64, ReadFMul64, ReadFMul64]>; def : FPALUDDynFrmAlias<FMUL_D, "fmul.d">; -def FDIV_D : FPALUD_rr_frm<0b0001101, "fdiv.d">; +def FDIV_D : FPALUD_rr_frm<0b0001101, "fdiv.d">, + Sched<[WriteFDiv64, ReadFDiv64, ReadFDiv64]>; def : FPALUDDynFrmAlias<FDIV_D, "fdiv.d">; -def FSQRT_D : FPUnaryOp_r_frm<0b0101101, FPR64, FPR64, "fsqrt.d"> { +def FSQRT_D : FPUnaryOp_r_frm<0b0101101, FPR64, FPR64, "fsqrt.d">, + Sched<[WriteFSqrt64, ReadFSqrt64]> { let rs2 = 0b00000; } def : FPUnaryOpDynFrmAlias<FSQRT_D, "fsqrt.d", FPR64, FPR64>; -def FSGNJ_D : FPALUD_rr<0b0010001, 0b000, "fsgnj.d">; -def FSGNJN_D : FPALUD_rr<0b0010001, 0b001, "fsgnjn.d">; -def FSGNJX_D : FPALUD_rr<0b0010001, 0b010, "fsgnjx.d">; -def FMIN_D : FPALUD_rr<0b0010101, 0b000, "fmin.d">; -def FMAX_D : FPALUD_rr<0b0010101, 0b001, "fmax.d">; - -def FCVT_S_D : FPUnaryOp_r_frm<0b0100000, FPR32, FPR64, "fcvt.s.d"> { +def FSGNJ_D : FPALUD_rr<0b0010001, 0b000, "fsgnj.d">, + Sched<[WriteFSGNJ64, ReadFSGNJ64, ReadFSGNJ64]>; +def FSGNJN_D : FPALUD_rr<0b0010001, 0b001, "fsgnjn.d">, + Sched<[WriteFSGNJ64, ReadFSGNJ64, ReadFSGNJ64]>; +def FSGNJX_D : FPALUD_rr<0b0010001, 0b010, "fsgnjx.d">, + Sched<[WriteFSGNJ64, ReadFSGNJ64, ReadFSGNJ64]>; +def FMIN_D : FPALUD_rr<0b0010101, 0b000, "fmin.d">, + Sched<[WriteFMinMax64, ReadFMinMax64, ReadFMinMax64]>; +def FMAX_D : FPALUD_rr<0b0010101, 0b001, "fmax.d">, + Sched<[WriteFMinMax64, ReadFMinMax64, ReadFMinMax64]>; + +def FCVT_S_D : FPUnaryOp_r_frm<0b0100000, FPR32, FPR64, "fcvt.s.d">, + Sched<[WriteFCvtF64ToF32, ReadFCvtF64ToF32]> { let rs2 = 0b00001; } def : FPUnaryOpDynFrmAlias<FCVT_S_D, "fcvt.s.d", FPR32, FPR64>; -def FCVT_D_S : FPUnaryOp_r<0b0100001, 0b000, FPR64, FPR32, "fcvt.d.s"> { +def FCVT_D_S : FPUnaryOp_r<0b0100001, 0b000, FPR64, FPR32, "fcvt.d.s">, + Sched<[WriteFCvtF32ToF64, ReadFCvtF32ToF64]> { let rs2 = 0b00000; } @@ -120,55 +139,66 @@ def FEQ_D : FPCmpD_rr<0b010, "feq.d">; def FLT_D : FPCmpD_rr<0b001, "flt.d">; def FLE_D : FPCmpD_rr<0b000, "fle.d">; -def FCLASS_D : FPUnaryOp_r<0b1110001, 0b001, GPR, FPR64, "fclass.d"> { +def FCLASS_D : FPUnaryOp_r<0b1110001, 0b001, GPR, FPR64, "fclass.d">, + Sched<[WriteFClass64, ReadFClass64]> { let rs2 = 0b00000; } -def FCVT_W_D : FPUnaryOp_r_frm<0b1100001, GPR, FPR64, "fcvt.w.d"> { +def FCVT_W_D : FPUnaryOp_r_frm<0b1100001, GPR, FPR64, "fcvt.w.d">, + Sched<[WriteFCvtF64ToI32, ReadFCvtF64ToI32]> { let rs2 = 0b00000; } def : FPUnaryOpDynFrmAlias<FCVT_W_D, "fcvt.w.d", GPR, FPR64>; -def FCVT_WU_D : FPUnaryOp_r_frm<0b1100001, GPR, FPR64, "fcvt.wu.d"> { +def FCVT_WU_D : FPUnaryOp_r_frm<0b1100001, GPR, FPR64, "fcvt.wu.d">, + Sched<[WriteFCvtF64ToI32, ReadFCvtF64ToI32]> { let rs2 = 0b00001; } def : FPUnaryOpDynFrmAlias<FCVT_WU_D, "fcvt.wu.d", GPR, FPR64>; -def FCVT_D_W : FPUnaryOp_r<0b1101001, 0b000, FPR64, GPR, "fcvt.d.w"> { +def FCVT_D_W : FPUnaryOp_r<0b1101001, 0b000, FPR64, GPR, "fcvt.d.w">, + Sched<[WriteFCvtI32ToF64, ReadFCvtI32ToF64]> { let rs2 = 0b00000; } -def FCVT_D_WU : FPUnaryOp_r<0b1101001, 0b000, FPR64, GPR, "fcvt.d.wu"> { +def FCVT_D_WU : FPUnaryOp_r<0b1101001, 0b000, FPR64, GPR, "fcvt.d.wu">, + Sched<[WriteFCvtI32ToF64, ReadFCvtI32ToF64]> { let rs2 = 0b00001; } } // Predicates = [HasStdExtD] let Predicates = [HasStdExtD, IsRV64] in { -def FCVT_L_D : FPUnaryOp_r_frm<0b1100001, GPR, FPR64, "fcvt.l.d"> { +def FCVT_L_D : FPUnaryOp_r_frm<0b1100001, GPR, FPR64, "fcvt.l.d">, + Sched<[WriteFCvtF64ToI64, ReadFCvtF64ToI64]> { let rs2 = 0b00010; } def : FPUnaryOpDynFrmAlias<FCVT_L_D, "fcvt.l.d", GPR, FPR64>; -def FCVT_LU_D : FPUnaryOp_r_frm<0b1100001, GPR, FPR64, "fcvt.lu.d"> { +def FCVT_LU_D : FPUnaryOp_r_frm<0b1100001, GPR, FPR64, "fcvt.lu.d">, + Sched<[WriteFCvtF64ToI64, ReadFCvtF64ToI64]> { let rs2 = 0b00011; } def : FPUnaryOpDynFrmAlias<FCVT_LU_D, "fcvt.lu.d", GPR, FPR64>; -def FMV_X_D : FPUnaryOp_r<0b1110001, 0b000, GPR, FPR64, "fmv.x.d"> { +def FMV_X_D : FPUnaryOp_r<0b1110001, 0b000, GPR, FPR64, "fmv.x.d">, + Sched<[WriteFMovF64ToI64, ReadFMovF64ToI64]> { let rs2 = 0b00000; } -def FCVT_D_L : FPUnaryOp_r_frm<0b1101001, FPR64, GPR, "fcvt.d.l"> { +def FCVT_D_L : FPUnaryOp_r_frm<0b1101001, FPR64, GPR, "fcvt.d.l">, + Sched<[WriteFCvtI64ToF64, ReadFCvtI64ToF64]> { let rs2 = 0b00010; } def : FPUnaryOpDynFrmAlias<FCVT_D_L, "fcvt.d.l", FPR64, GPR>; -def FCVT_D_LU : FPUnaryOp_r_frm<0b1101001, FPR64, GPR, "fcvt.d.lu"> { +def FCVT_D_LU : FPUnaryOp_r_frm<0b1101001, FPR64, GPR, "fcvt.d.lu">, + Sched<[WriteFCvtI64ToF64, ReadFCvtI64ToF64]> { let rs2 = 0b00011; } def : FPUnaryOpDynFrmAlias<FCVT_D_LU, "fcvt.d.lu", FPR64, GPR>; -def FMV_D_X : FPUnaryOp_r<0b1111001, 0b000, FPR64, GPR, "fmv.d.x"> { +def FMV_D_X : FPUnaryOp_r<0b1111001, 0b000, FPR64, GPR, "fmv.d.x">, + Sched<[WriteFMovI64ToF64, ReadFMovI64ToF64]> { let rs2 = 0b00000; } } // Predicates = [HasStdExtD, IsRV64] @@ -276,11 +306,15 @@ def : PatFpr64Fpr64<setole, FLE_D>; def : Pat<(seto FPR64:$rs1, FPR64:$rs2), (AND (FEQ_D FPR64:$rs1, FPR64:$rs1), (FEQ_D FPR64:$rs2, FPR64:$rs2))>; +def : Pat<(seto FPR64:$rs1, FPR64:$rs1), + (FEQ_D $rs1, $rs1)>; def : Pat<(setuo FPR64:$rs1, FPR64:$rs2), (SLTIU (AND (FEQ_D FPR64:$rs1, FPR64:$rs1), (FEQ_D FPR64:$rs2, FPR64:$rs2)), 1)>; +def : Pat<(setuo FPR64:$rs1, FPR64:$rs1), + (SLTIU (FEQ_D $rs1, $rs1), 1)>; def Select_FPR64_Using_CC_GPR : SelectCC_rrirr<FPR64, GPR>; @@ -309,6 +343,10 @@ def SplitF64Pseudo } // Predicates = [HasStdExtD] let Predicates = [HasStdExtD, IsRV32] in { + +/// Float constants +def : Pat<(f64 (fpimm0)), (FCVT_D_W X0)>; + // double->[u]int. Round-to-zero must be used. def : Pat<(fp_to_sint FPR64:$rs1), (FCVT_W_D FPR64:$rs1, 0b001)>; def : Pat<(fp_to_uint FPR64:$rs1), (FCVT_WU_D FPR64:$rs1, 0b001)>; @@ -319,6 +357,10 @@ def : Pat<(uint_to_fp GPR:$rs1), (FCVT_D_WU GPR:$rs1)>; } // Predicates = [HasStdExtD, IsRV32] let Predicates = [HasStdExtD, IsRV64] in { + +/// Float constants +def : Pat<(f64 (fpimm0)), (FMV_D_X X0)>; + def : Pat<(bitconvert GPR:$rs1), (FMV_D_X GPR:$rs1)>; def : Pat<(bitconvert FPR64:$rs1), (FMV_X_D FPR64:$rs1)>; diff --git a/llvm/lib/Target/RISCV/RISCVInstrInfoF.td b/llvm/lib/Target/RISCV/RISCVInstrInfoF.td index 3b73c865ea17..ce5c3abb6a06 100644 --- a/llvm/lib/Target/RISCV/RISCVInstrInfoF.td +++ b/llvm/lib/Target/RISCV/RISCVInstrInfoF.td @@ -93,7 +93,8 @@ class FPUnaryOpDynFrmAlias<FPUnaryOp_r_frm Inst, string OpcodeStr, let hasSideEffects = 0, mayLoad = 0, mayStore = 0 in class FPCmpS_rr<bits<3> funct3, string opcodestr> : RVInstR<0b1010000, funct3, OPC_OP_FP, (outs GPR:$rd), - (ins FPR32:$rs1, FPR32:$rs2), opcodestr, "$rd, $rs1, $rs2">; + (ins FPR32:$rs1, FPR32:$rs2), opcodestr, "$rd, $rs1, $rs2">, + Sched<[WriteFCmp32, ReadFCmp32, ReadFCmp32]>; //===----------------------------------------------------------------------===// // Instructions @@ -103,7 +104,8 @@ let Predicates = [HasStdExtF] in { let hasSideEffects = 0, mayLoad = 1, mayStore = 0 in def FLW : RVInstI<0b010, OPC_LOAD_FP, (outs FPR32:$rd), (ins GPR:$rs1, simm12:$imm12), - "flw", "$rd, ${imm12}(${rs1})">; + "flw", "$rd, ${imm12}(${rs1})">, + Sched<[WriteFLD32, ReadFMemBase]>; // Operands for stores are in the order srcreg, base, offset rather than // reflecting the order these fields are specified in the instruction @@ -111,48 +113,66 @@ def FLW : RVInstI<0b010, OPC_LOAD_FP, (outs FPR32:$rd), let hasSideEffects = 0, mayLoad = 0, mayStore = 1 in def FSW : RVInstS<0b010, OPC_STORE_FP, (outs), (ins FPR32:$rs2, GPR:$rs1, simm12:$imm12), - "fsw", "$rs2, ${imm12}(${rs1})">; + "fsw", "$rs2, ${imm12}(${rs1})">, + Sched<[WriteFST32, ReadStoreData, ReadFMemBase]>; -def FMADD_S : FPFMAS_rrr_frm<OPC_MADD, "fmadd.s">; +def FMADD_S : FPFMAS_rrr_frm<OPC_MADD, "fmadd.s">, + Sched<[WriteFMulAdd32, ReadFMulAdd32, ReadFMulAdd32, ReadFMulAdd32]>; def : FPFMASDynFrmAlias<FMADD_S, "fmadd.s">; -def FMSUB_S : FPFMAS_rrr_frm<OPC_MSUB, "fmsub.s">; +def FMSUB_S : FPFMAS_rrr_frm<OPC_MSUB, "fmsub.s">, + Sched<[WriteFMulSub32, ReadFMulSub32, ReadFMulSub32, ReadFMulSub32]>; def : FPFMASDynFrmAlias<FMSUB_S, "fmsub.s">; -def FNMSUB_S : FPFMAS_rrr_frm<OPC_NMSUB, "fnmsub.s">; +def FNMSUB_S : FPFMAS_rrr_frm<OPC_NMSUB, "fnmsub.s">, + Sched<[WriteFMulSub32, ReadFMulSub32, ReadFMulSub32, ReadFMulSub32]>; def : FPFMASDynFrmAlias<FNMSUB_S, "fnmsub.s">; -def FNMADD_S : FPFMAS_rrr_frm<OPC_NMADD, "fnmadd.s">; +def FNMADD_S : FPFMAS_rrr_frm<OPC_NMADD, "fnmadd.s">, + Sched<[WriteFMulAdd32, ReadFMulAdd32, ReadFMulAdd32, ReadFMulAdd32]>; def : FPFMASDynFrmAlias<FNMADD_S, "fnmadd.s">; -def FADD_S : FPALUS_rr_frm<0b0000000, "fadd.s">; +def FADD_S : FPALUS_rr_frm<0b0000000, "fadd.s">, + Sched<[WriteFALU32, ReadFALU32, ReadFALU32]>; def : FPALUSDynFrmAlias<FADD_S, "fadd.s">; -def FSUB_S : FPALUS_rr_frm<0b0000100, "fsub.s">; +def FSUB_S : FPALUS_rr_frm<0b0000100, "fsub.s">, + Sched<[WriteFALU32, ReadFALU32, ReadFALU32]>; def : FPALUSDynFrmAlias<FSUB_S, "fsub.s">; -def FMUL_S : FPALUS_rr_frm<0b0001000, "fmul.s">; +def FMUL_S : FPALUS_rr_frm<0b0001000, "fmul.s">, + Sched<[WriteFMul32, ReadFMul32, ReadFMul32]>; def : FPALUSDynFrmAlias<FMUL_S, "fmul.s">; -def FDIV_S : FPALUS_rr_frm<0b0001100, "fdiv.s">; +def FDIV_S : FPALUS_rr_frm<0b0001100, "fdiv.s">, + Sched<[WriteFDiv32, ReadFDiv32, ReadFDiv32]>; def : FPALUSDynFrmAlias<FDIV_S, "fdiv.s">; -def FSQRT_S : FPUnaryOp_r_frm<0b0101100, FPR32, FPR32, "fsqrt.s"> { +def FSQRT_S : FPUnaryOp_r_frm<0b0101100, FPR32, FPR32, "fsqrt.s">, + Sched<[WriteFSqrt32, ReadFSqrt32]> { let rs2 = 0b00000; } def : FPUnaryOpDynFrmAlias<FSQRT_S, "fsqrt.s", FPR32, FPR32>; -def FSGNJ_S : FPALUS_rr<0b0010000, 0b000, "fsgnj.s">; -def FSGNJN_S : FPALUS_rr<0b0010000, 0b001, "fsgnjn.s">; -def FSGNJX_S : FPALUS_rr<0b0010000, 0b010, "fsgnjx.s">; -def FMIN_S : FPALUS_rr<0b0010100, 0b000, "fmin.s">; -def FMAX_S : FPALUS_rr<0b0010100, 0b001, "fmax.s">; - -def FCVT_W_S : FPUnaryOp_r_frm<0b1100000, GPR, FPR32, "fcvt.w.s"> { +def FSGNJ_S : FPALUS_rr<0b0010000, 0b000, "fsgnj.s">, + Sched<[WriteFSGNJ32, ReadFSGNJ32, ReadFSGNJ32]>; +def FSGNJN_S : FPALUS_rr<0b0010000, 0b001, "fsgnjn.s">, + Sched<[WriteFSGNJ32, ReadFSGNJ32, ReadFSGNJ32]>; +def FSGNJX_S : FPALUS_rr<0b0010000, 0b010, "fsgnjx.s">, + Sched<[WriteFSGNJ32, ReadFSGNJ32, ReadFSGNJ32]>; +def FMIN_S : FPALUS_rr<0b0010100, 0b000, "fmin.s">, + Sched<[WriteFMinMax32, ReadFMinMax32, ReadFMinMax32]>; +def FMAX_S : FPALUS_rr<0b0010100, 0b001, "fmax.s">, + Sched<[WriteFMinMax32, ReadFMinMax32, ReadFMinMax32]>; + +def FCVT_W_S : FPUnaryOp_r_frm<0b1100000, GPR, FPR32, "fcvt.w.s">, + Sched<[WriteFCvtF32ToI32, ReadFCvtF32ToI32]> { let rs2 = 0b00000; } def : FPUnaryOpDynFrmAlias<FCVT_W_S, "fcvt.w.s", GPR, FPR32>; -def FCVT_WU_S : FPUnaryOp_r_frm<0b1100000, GPR, FPR32, "fcvt.wu.s"> { +def FCVT_WU_S : FPUnaryOp_r_frm<0b1100000, GPR, FPR32, "fcvt.wu.s">, + Sched<[WriteFCvtF32ToI32, ReadFCvtF32ToI32]> { let rs2 = 0b00001; } def : FPUnaryOpDynFrmAlias<FCVT_WU_S, "fcvt.wu.s", GPR, FPR32>; -def FMV_X_W : FPUnaryOp_r<0b1110000, 0b000, GPR, FPR32, "fmv.x.w"> { +def FMV_X_W : FPUnaryOp_r<0b1110000, 0b000, GPR, FPR32, "fmv.x.w">, + Sched<[WriteFMovF32ToI32, ReadFMovF32ToI32]> { let rs2 = 0b00000; } @@ -160,42 +180,50 @@ def FEQ_S : FPCmpS_rr<0b010, "feq.s">; def FLT_S : FPCmpS_rr<0b001, "flt.s">; def FLE_S : FPCmpS_rr<0b000, "fle.s">; -def FCLASS_S : FPUnaryOp_r<0b1110000, 0b001, GPR, FPR32, "fclass.s"> { +def FCLASS_S : FPUnaryOp_r<0b1110000, 0b001, GPR, FPR32, "fclass.s">, + Sched<[WriteFClass32, ReadFClass32]> { let rs2 = 0b00000; } -def FCVT_S_W : FPUnaryOp_r_frm<0b1101000, FPR32, GPR, "fcvt.s.w"> { +def FCVT_S_W : FPUnaryOp_r_frm<0b1101000, FPR32, GPR, "fcvt.s.w">, + Sched<[WriteFCvtI32ToF32, ReadFCvtI32ToF32]> { let rs2 = 0b00000; } def : FPUnaryOpDynFrmAlias<FCVT_S_W, "fcvt.s.w", FPR32, GPR>; -def FCVT_S_WU : FPUnaryOp_r_frm<0b1101000, FPR32, GPR, "fcvt.s.wu"> { +def FCVT_S_WU : FPUnaryOp_r_frm<0b1101000, FPR32, GPR, "fcvt.s.wu">, + Sched<[WriteFCvtI32ToF32, ReadFCvtI32ToF32]> { let rs2 = 0b00001; } def : FPUnaryOpDynFrmAlias<FCVT_S_WU, "fcvt.s.wu", FPR32, GPR>; -def FMV_W_X : FPUnaryOp_r<0b1111000, 0b000, FPR32, GPR, "fmv.w.x"> { +def FMV_W_X : FPUnaryOp_r<0b1111000, 0b000, FPR32, GPR, "fmv.w.x">, + Sched<[WriteFMovI32ToF32, ReadFMovI32ToF32]> { let rs2 = 0b00000; } } // Predicates = [HasStdExtF] let Predicates = [HasStdExtF, IsRV64] in { -def FCVT_L_S : FPUnaryOp_r_frm<0b1100000, GPR, FPR32, "fcvt.l.s"> { +def FCVT_L_S : FPUnaryOp_r_frm<0b1100000, GPR, FPR32, "fcvt.l.s">, + Sched<[WriteFCvtF32ToI64, ReadFCvtF32ToI64]> { let rs2 = 0b00010; } def : FPUnaryOpDynFrmAlias<FCVT_L_S, "fcvt.l.s", GPR, FPR32>; -def FCVT_LU_S : FPUnaryOp_r_frm<0b1100000, GPR, FPR32, "fcvt.lu.s"> { +def FCVT_LU_S : FPUnaryOp_r_frm<0b1100000, GPR, FPR32, "fcvt.lu.s">, + Sched<[WriteFCvtF32ToI64, ReadFCvtF32ToI64]> { let rs2 = 0b00011; } def : FPUnaryOpDynFrmAlias<FCVT_LU_S, "fcvt.lu.s", GPR, FPR32>; -def FCVT_S_L : FPUnaryOp_r_frm<0b1101000, FPR32, GPR, "fcvt.s.l"> { +def FCVT_S_L : FPUnaryOp_r_frm<0b1101000, FPR32, GPR, "fcvt.s.l">, + Sched<[WriteFCvtI64ToF32, ReadFCvtI64ToF32]> { let rs2 = 0b00010; } def : FPUnaryOpDynFrmAlias<FCVT_S_L, "fcvt.s.l", FPR32, GPR>; -def FCVT_S_LU : FPUnaryOp_r_frm<0b1101000, FPR32, GPR, "fcvt.s.lu"> { +def FCVT_S_LU : FPUnaryOp_r_frm<0b1101000, FPR32, GPR, "fcvt.s.lu">, + Sched<[WriteFCvtI64ToF32, ReadFCvtI64ToF32]> { let rs2 = 0b00011; } def : FPUnaryOpDynFrmAlias<FCVT_S_LU, "fcvt.s.lu", FPR32, GPR>; @@ -258,6 +286,9 @@ def PseudoFSW : PseudoStore<"fsw", FPR32>; // Pseudo-instructions and codegen patterns //===----------------------------------------------------------------------===// +/// Floating point constants +def fpimm0 : PatLeaf<(fpimm), [{ return N->isExactlyValue(+0.0); }]>; + /// Generic pattern classes class PatFpr32Fpr32<SDPatternOperator OpNode, RVInstR Inst> : Pat<(OpNode FPR32:$rs1, FPR32:$rs2), (Inst $rs1, $rs2)>; @@ -267,6 +298,9 @@ class PatFpr32Fpr32DynFrm<SDPatternOperator OpNode, RVInstRFrm Inst> let Predicates = [HasStdExtF] in { +/// Float constants +def : Pat<(f32 (fpimm0)), (FMV_W_X X0)>; + /// Float conversion operations // Moves (no conversion) @@ -332,11 +366,15 @@ def : PatFpr32Fpr32<setole, FLE_S>; def : Pat<(seto FPR32:$rs1, FPR32:$rs2), (AND (FEQ_S FPR32:$rs1, FPR32:$rs1), (FEQ_S FPR32:$rs2, FPR32:$rs2))>; +def : Pat<(seto FPR32:$rs1, FPR32:$rs1), + (FEQ_S $rs1, $rs1)>; def : Pat<(setuo FPR32:$rs1, FPR32:$rs2), (SLTIU (AND (FEQ_S FPR32:$rs1, FPR32:$rs1), (FEQ_S FPR32:$rs2, FPR32:$rs2)), 1)>; +def : Pat<(setuo FPR32:$rs1, FPR32:$rs1), + (SLTIU (FEQ_S $rs1, $rs1), 1)>; def Select_FPR32_Using_CC_GPR : SelectCC_rrirr<FPR32, GPR>; @@ -360,16 +398,6 @@ def : Pat<(sint_to_fp GPR:$rs1), (FCVT_S_W $rs1, 0b111)>; def : Pat<(uint_to_fp GPR:$rs1), (FCVT_S_WU $rs1, 0b111)>; } // Predicates = [HasStdExtF, IsRV32] -let Predicates = [HasStdExtF, IsRV32] in { -// FP->[u]int. Round-to-zero must be used -def : Pat<(fp_to_sint FPR32:$rs1), (FCVT_W_S $rs1, 0b001)>; -def : Pat<(fp_to_uint FPR32:$rs1), (FCVT_WU_S $rs1, 0b001)>; - -// [u]int->fp. Match GCC and default to using dynamic rounding mode. -def : Pat<(sint_to_fp GPR:$rs1), (FCVT_S_W $rs1, 0b111)>; -def : Pat<(uint_to_fp GPR:$rs1), (FCVT_S_WU $rs1, 0b111)>; -} // Predicates = [HasStdExtF, IsRV32] - let Predicates = [HasStdExtF, IsRV64] in { def : Pat<(riscv_fmv_w_x_rv64 GPR:$src), (FMV_W_X GPR:$src)>; def : Pat<(riscv_fmv_x_anyextw_rv64 FPR32:$src), (FMV_X_W FPR32:$src)>; diff --git a/llvm/lib/Target/RISCV/RISCVInstrInfoM.td b/llvm/lib/Target/RISCV/RISCVInstrInfoM.td index e75151ba99c7..987534aadd79 100644 --- a/llvm/lib/Target/RISCV/RISCVInstrInfoM.td +++ b/llvm/lib/Target/RISCV/RISCVInstrInfoM.td @@ -24,22 +24,35 @@ def riscv_remuw : SDNode<"RISCVISD::REMUW", SDTIntBinOp>; //===----------------------------------------------------------------------===// let Predicates = [HasStdExtM] in { -def MUL : ALU_rr<0b0000001, 0b000, "mul">; -def MULH : ALU_rr<0b0000001, 0b001, "mulh">; -def MULHSU : ALU_rr<0b0000001, 0b010, "mulhsu">; -def MULHU : ALU_rr<0b0000001, 0b011, "mulhu">; -def DIV : ALU_rr<0b0000001, 0b100, "div">; -def DIVU : ALU_rr<0b0000001, 0b101, "divu">; -def REM : ALU_rr<0b0000001, 0b110, "rem">; -def REMU : ALU_rr<0b0000001, 0b111, "remu">; +def MUL : ALU_rr<0b0000001, 0b000, "mul">, + Sched<[WriteIMul, ReadIMul, ReadIMul]>; +def MULH : ALU_rr<0b0000001, 0b001, "mulh">, + Sched<[WriteIMul, ReadIMul, ReadIMul]>; +def MULHSU : ALU_rr<0b0000001, 0b010, "mulhsu">, + Sched<[WriteIMul, ReadIMul, ReadIMul]>; +def MULHU : ALU_rr<0b0000001, 0b011, "mulhu">, + Sched<[WriteIMul, ReadIMul, ReadIMul]>; +def DIV : ALU_rr<0b0000001, 0b100, "div">, + Sched<[WriteIDiv, ReadIDiv, ReadIDiv]>; +def DIVU : ALU_rr<0b0000001, 0b101, "divu">, + Sched<[WriteIDiv, ReadIDiv, ReadIDiv]>; +def REM : ALU_rr<0b0000001, 0b110, "rem">, + Sched<[WriteIDiv, ReadIDiv, ReadIDiv]>; +def REMU : ALU_rr<0b0000001, 0b111, "remu">, + Sched<[WriteIDiv, ReadIDiv, ReadIDiv]>; } // Predicates = [HasStdExtM] let Predicates = [HasStdExtM, IsRV64] in { -def MULW : ALUW_rr<0b0000001, 0b000, "mulw">; -def DIVW : ALUW_rr<0b0000001, 0b100, "divw">; -def DIVUW : ALUW_rr<0b0000001, 0b101, "divuw">; -def REMW : ALUW_rr<0b0000001, 0b110, "remw">; -def REMUW : ALUW_rr<0b0000001, 0b111, "remuw">; +def MULW : ALUW_rr<0b0000001, 0b000, "mulw">, + Sched<[WriteIMul32, ReadIMul32, ReadIMul32]>; +def DIVW : ALUW_rr<0b0000001, 0b100, "divw">, + Sched<[WriteIDiv32, ReadIDiv32, ReadIDiv32]>; +def DIVUW : ALUW_rr<0b0000001, 0b101, "divuw">, + Sched<[WriteIDiv32, ReadIDiv32, ReadIDiv32]>; +def REMW : ALUW_rr<0b0000001, 0b110, "remw">, + Sched<[WriteIDiv32, ReadIDiv32, ReadIDiv32]>; +def REMUW : ALUW_rr<0b0000001, 0b111, "remuw">, + Sched<[WriteIDiv32, ReadIDiv32, ReadIDiv32]>; } // Predicates = [HasStdExtM, IsRV64] //===----------------------------------------------------------------------===// diff --git a/llvm/lib/Target/RISCV/RISCVInstrInfoV.td b/llvm/lib/Target/RISCV/RISCVInstrInfoV.td new file mode 100644 index 000000000000..1c7f53fecb8c --- /dev/null +++ b/llvm/lib/Target/RISCV/RISCVInstrInfoV.td @@ -0,0 +1,873 @@ +//===-- RISCVInstrInfoV.td - RISC-V 'V' instructions -------*- 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 RISC-V instructions from the standard 'V' Vector +/// extension, version 0.8. +/// This version is still experimental as the 'V' extension hasn't been +/// ratified yet. +/// +//===----------------------------------------------------------------------===// + +include "RISCVInstrFormatsV.td" + +//===----------------------------------------------------------------------===// +// Operand and SDNode transformation definitions. +//===----------------------------------------------------------------------===// + +def VTypeIAsmOperand : AsmOperandClass { + let Name = "VTypeI"; + let ParserMethod = "parseVTypeI"; + let DiagnosticType = "InvalidVTypeI"; +} + +def VTypeIOp : Operand<XLenVT> { + let ParserMatchClass = VTypeIAsmOperand; + let PrintMethod = "printVTypeI"; + let DecoderMethod = "decodeUImmOperand<11>"; +} + +def VRegAsmOperand : AsmOperandClass { + let Name = "RVVRegOpOperand"; + let RenderMethod = "addRegOperands"; + let PredicateMethod = "isReg"; + let ParserMethod = "parseRegister"; +} + +def VRegOp : RegisterOperand<VR> { + let ParserMatchClass = VRegAsmOperand; + let PrintMethod = "printOperand"; +} + +def VMaskAsmOperand : AsmOperandClass { + let Name = "RVVMaskRegOpOperand"; + let RenderMethod = "addRegOperands"; + let PredicateMethod = "isV0Reg"; + let ParserMethod = "parseMaskReg"; + let IsOptional = 1; + let DefaultMethod = "defaultMaskRegOp"; + let DiagnosticType = "InvalidVMaskRegister"; +} + +def VMaskOp : RegisterOperand<VMV0> { + let ParserMatchClass = VMaskAsmOperand; + let PrintMethod = "printVMaskReg"; + let EncoderMethod = "getVMaskReg"; + let DecoderMethod = "decodeVMaskReg"; +} + +def simm5 : Operand<XLenVT>, ImmLeaf<XLenVT, [{return isInt<5>(Imm);}]> { + let ParserMatchClass = SImmAsmOperand<5>; + let EncoderMethod = "getImmOpValue"; + let DecoderMethod = "decodeSImmOperand<5>"; + let MCOperandPredicate = [{ + int64_t Imm; + if (MCOp.evaluateAsConstantImm(Imm)) + return isInt<5>(Imm); + return MCOp.isBareSymbolRef(); + }]; +} + +def SImm5Plus1AsmOperand : AsmOperandClass { + let Name = "SImm5Plus1"; + let RenderMethod = "addSImm5Plus1Operands"; + let DiagnosticType = "InvalidSImm5Plus1"; +} + +def simm5_plus1 : Operand<XLenVT>, ImmLeaf<XLenVT, + [{return isInt<5>(Imm - 1);}]> { + let ParserMatchClass = SImm5Plus1AsmOperand; + let PrintMethod = "printSImm5Plus1"; + let MCOperandPredicate = [{ + int64_t Imm; + if (MCOp.evaluateAsConstantImm(Imm)) + return isInt<5>(Imm - 1); + return MCOp.isBareSymbolRef(); + }]; +} + +//===----------------------------------------------------------------------===// +// Instruction class templates +//===----------------------------------------------------------------------===// + +let hasSideEffects = 0, mayLoad = 1, mayStore = 0 in { +// load vd, (rs1), vm +class VUnitStrideLoad<RISCVMOP mop, RISCVLSUMOP lumop, RISCVWidth width, + string opcodestr> + : RVInstVLU<0b000, mop, lumop, width, (outs VRegOp:$vd), + (ins GPR:$rs1, VMaskOp:$vm), opcodestr, "$vd, (${rs1})$vm">; + +// load vd, (rs1), rs2, vm +class VStridedLoad<RISCVMOP mop, RISCVWidth width, string opcodestr> + : RVInstVLS<0b000, mop, width, (outs VRegOp:$vd), + (ins GPR:$rs1, GPR:$rs2, VMaskOp:$vm), opcodestr, + "$vd, (${rs1}), $rs2$vm">; + +// load vd, (rs1), vs2, vm +class VIndexedLoad<RISCVMOP mop, RISCVWidth width, string opcodestr> + : RVInstVLX<0b000, mop, width, (outs VRegOp:$vd), + (ins GPR:$rs1, VRegOp:$vs2, VMaskOp:$vm), opcodestr, + "$vd, (${rs1}), $vs2$vm">; + +// vl<nf>r.v vd, (rs1) +class VWholeLoad<bits<3> nf, string opcodestr> + : RVInstVLU<nf, MOPLDUnitStrideU, LUMOPUnitStrideWholeReg, + LSWidthVSEW, (outs VRegOp:$vd), (ins GPR:$rs1), + opcodestr, "$vd, (${rs1})"> { + let vm = 1; + let Uses = []; +} +} // hasSideEffects = 0, mayLoad = 1, mayStore = 0 + +let hasSideEffects = 0, mayLoad = 0, mayStore = 1 in { +// store vd, vs3, (rs1), vm +class VUnitStrideStore<RISCVMOP mop, RISCVLSUMOP sumop, RISCVWidth width, + string opcodestr> + : RVInstVSU<0b000, mop, sumop, width, (outs), + (ins VRegOp:$vs3, GPR:$rs1, VMaskOp:$vm), opcodestr, + "$vs3, (${rs1})$vm">; + +// store vd, vs3, (rs1), rs2, vm +class VStridedStore<RISCVMOP mop, RISCVWidth width, string opcodestr> + : RVInstVSS<0b000, mop, width, (outs), + (ins VRegOp:$vs3, GPR:$rs1, GPR:$rs2, VMaskOp:$vm), + opcodestr, "$vs3, (${rs1}), $rs2$vm">; + +// store vd, vs3, (rs1), vs2, vm +class VIndexedStore<RISCVMOP mop, RISCVWidth width, string opcodestr> + : RVInstVSX<0b000, mop, width, (outs), + (ins VRegOp:$vs3, GPR:$rs1, VRegOp:$vs2, VMaskOp:$vm), + opcodestr, "$vs3, (${rs1}), $vs2$vm">; + +// vs<nf>r.v vd, (rs1) +class VWholeStore<bits<3> nf, string opcodestr> + : RVInstVSU<nf, MOPSTUnitStride, SUMOPUnitStrideWholeReg, + LSWidthVSEW, (outs), (ins VRegOp:$vs3, GPR:$rs1), + opcodestr, "$vs3, (${rs1})"> { + let vm = 1; + let Uses = []; +} +} // hasSideEffects = 0, mayLoad = 0, mayStore = 1 + +let hasSideEffects = 0, mayLoad = 0, mayStore = 0 in { +// op vd, vs2, vs1, vm +class VALUVV<bits<6> funct6, RISCVVFormat opv, string opcodestr> + : RVInstVV<funct6, opv, (outs VRegOp:$vd), + (ins VRegOp:$vs2, VRegOp:$vs1, VMaskOp:$vm), + opcodestr, "$vd, $vs2, $vs1$vm">; + +// op vd, vs2, vs1, v0 (without mask, use v0 as carry input) +class VALUmVV<bits<6> funct6, RISCVVFormat opv, string opcodestr> + : RVInstVV<funct6, opv, (outs VRegOp:$vd), + (ins VRegOp:$vs2, VRegOp:$vs1, VMV0:$v0), + opcodestr, "$vd, $vs2, $vs1, v0"> { + let vm = 0; +} + +// op vd, vs1, vs2, vm (reverse the order of vs1 and vs2) +class VALUrVV<bits<6> funct6, RISCVVFormat opv, string opcodestr> + : RVInstVV<funct6, opv, (outs VRegOp:$vd), + (ins VRegOp:$vs1, VRegOp:$vs2, VMaskOp:$vm), + opcodestr, "$vd, $vs1, $vs2$vm">; + +// op vd, vs1, vs2 +class VALUVVNoVm<bits<6> funct6, RISCVVFormat opv, string opcodestr> + : RVInstVV<funct6, opv, (outs VRegOp:$vd), + (ins VRegOp:$vs2, VRegOp:$vs1), + opcodestr, "$vd, $vs2, $vs1"> { + let vm = 1; +} + +// op vd, vs2, rs1, vm +class VALUVX<bits<6> funct6, RISCVVFormat opv, string opcodestr> + : RVInstVX<funct6, opv, (outs VRegOp:$vd), + (ins VRegOp:$vs2, GPR:$rs1, VMaskOp:$vm), + opcodestr, "$vd, $vs2, $rs1$vm">; + +// op vd, vs2, rs1, v0 (without mask, use v0 as carry input) +class VALUmVX<bits<6> funct6, RISCVVFormat opv, string opcodestr> + : RVInstVX<funct6, opv, (outs VRegOp:$vd), + (ins VRegOp:$vs2, GPR:$rs1, VMV0:$v0), + opcodestr, "$vd, $vs2, $rs1, v0"> { + let vm = 0; +} + +// op vd, rs1, vs2, vm (reverse the order of rs1 and vs2) +class VALUrVX<bits<6> funct6, RISCVVFormat opv, string opcodestr> + : RVInstVX<funct6, opv, (outs VRegOp:$vd), + (ins GPR:$rs1, VRegOp:$vs2, VMaskOp:$vm), + opcodestr, "$vd, $rs1, $vs2$vm">; + +// op vd, vs1, vs2 +class VALUVXNoVm<bits<6> funct6, RISCVVFormat opv, string opcodestr> + : RVInstVX<funct6, opv, (outs VRegOp:$vd), + (ins VRegOp:$vs2, GPR:$rs1), + opcodestr, "$vd, $vs2, $rs1"> { + let vm = 1; +} + +// op vd, vs2, imm, vm +class VALUVI<bits<6> funct6, string opcodestr, Operand optype = simm5> + : RVInstIVI<funct6, (outs VRegOp:$vd), + (ins VRegOp:$vs2, optype:$imm, VMaskOp:$vm), + opcodestr, "$vd, $vs2, $imm$vm">; + +// op vd, vs2, imm, v0 (without mask, use v0 as carry input) +class VALUmVI<bits<6> funct6, string opcodestr, Operand optype = simm5> + : RVInstIVI<funct6, (outs VRegOp:$vd), + (ins VRegOp:$vs2, optype:$imm, VMV0:$v0), + opcodestr, "$vd, $vs2, $imm, v0"> { + let vm = 0; +} + +// op vd, vs2, imm, vm +class VALUVINoVm<bits<6> funct6, string opcodestr, Operand optype = simm5> + : RVInstIVI<funct6, (outs VRegOp:$vd), + (ins VRegOp:$vs2, optype:$imm), + opcodestr, "$vd, $vs2, $imm"> { + let vm = 1; +} + +// op vd, vs2, rs1, vm (Float) +class VALUVF<bits<6> funct6, RISCVVFormat opv, string opcodestr> + : RVInstVX<funct6, opv, (outs VRegOp:$vd), + (ins VRegOp:$vs2, FPR32:$rs1, VMaskOp:$vm), + opcodestr, "$vd, $vs2, $rs1$vm">; + +// op vd, rs1, vs2, vm (Float) (with mask, reverse the order of rs1 and vs2) +class VALUrVF<bits<6> funct6, RISCVVFormat opv, string opcodestr> + : RVInstVX<funct6, opv, (outs VRegOp:$vd), + (ins FPR32:$rs1, VRegOp:$vs2, VMaskOp:$vm), + opcodestr, "$vd, $rs1, $vs2$vm">; + +// op vd, vs2, vm (use vs1 as instruction encoding) +class VALUVs2<bits<6> funct6, bits<5> vs1, RISCVVFormat opv, string opcodestr> + : RVInstV<funct6, vs1, opv, (outs VRegOp:$vd), + (ins VRegOp:$vs2, VMaskOp:$vm), + opcodestr, "$vd, $vs2$vm">; +} // hasSideEffects = 0, mayLoad = 0, mayStore = 0 + +//===----------------------------------------------------------------------===// +// Combination of instruction classes. +// Use these multiclasses to define instructions more easily. +//===----------------------------------------------------------------------===// +multiclass VALU_IV_V_X_I<string opcodestr, bits<6> funct6, Operand optype = simm5, string vw = "v"> { + def V : VALUVV<funct6, OPIVV, opcodestr # "." # vw # "v">; + def X : VALUVX<funct6, OPIVX, opcodestr # "." # vw # "x">; + def I : VALUVI<funct6, opcodestr # "." # vw # "i", optype>; +} + +multiclass VALU_IV_V_X<string opcodestr, bits<6> funct6, string vw = "v"> { + def V : VALUVV<funct6, OPIVV, opcodestr # "." # vw # "v">; + def X : VALUVX<funct6, OPIVX, opcodestr # "." # vw # "x">; +} + +multiclass VALUr_IV_V_X<string opcodestr, bits<6> funct6, string vw = "v"> { + def V : VALUrVV<funct6, OPIVV, opcodestr # "." # vw # "v">; + def X : VALUrVX<funct6, OPIVX, opcodestr # "." # vw # "x">; +} + +multiclass VALU_IV_X_I<string opcodestr, bits<6> funct6, Operand optype = simm5, string vw = "v"> { + def X : VALUVX<funct6, OPIVX, opcodestr # "." # vw # "x">; + def I : VALUVI<funct6, opcodestr # "." # vw # "i", optype>; +} + +multiclass VALU_IV_V<string opcodestr, bits<6> funct6> { + def _VS : VALUVV<funct6, OPIVV, opcodestr # ".vs">; +} + +multiclass VALUr_IV_X<string opcodestr, bits<6> funct6, string vw = "v"> { + def X : VALUrVX<funct6, OPIVX, opcodestr # "." # vw # "x">; +} + +multiclass VALU_MV_V_X<string opcodestr, bits<6> funct6, string vw = "v"> { + def V : VALUVV<funct6, OPMVV, opcodestr # "." # vw # "v">; + def X : VALUVX<funct6, OPMVX, opcodestr # "." # vw # "x">; +} + +multiclass VALU_MV_V<string opcodestr, bits<6> funct6> { + def _VS : VALUVV<funct6, OPMVV, opcodestr # ".vs">; +} + +multiclass VALU_MV_Mask<string opcodestr, bits<6> funct6, string vm = "v"> { + def M : VALUVVNoVm<funct6, OPMVV, opcodestr # "." # vm # "m">; +} + +multiclass VALU_MV_X<string opcodestr, bits<6> funct6, string vw = "v"> { + def X : VALUVX<funct6, OPMVX, opcodestr # "." # vw # "x">; +} + +multiclass VALUr_MV_V_X<string opcodestr, bits<6> funct6, string vw = "v"> { + def V : VALUrVV<funct6, OPMVV, opcodestr # "." # vw # "v">; + def X : VALUrVX<funct6, OPMVX, opcodestr # "." # vw # "x">; +} + +multiclass VALUr_MV_X<string opcodestr, bits<6> funct6, string vw = "v"> { + def X : VALUrVX<funct6, OPMVX, opcodestr # "." # vw # "x">; +} + +multiclass VALU_MV_VS2<string opcodestr, bits<6> funct6, bits<5> vs1> { + def "" : VALUVs2<funct6, vs1, OPMVV, opcodestr>; +} + +multiclass VALUm_IV_V_X_I<string opcodestr, bits<6> funct6> { + def VM : VALUmVV<funct6, OPIVV, opcodestr # ".vvm">; + def XM : VALUmVX<funct6, OPIVX, opcodestr # ".vxm">; + def IM : VALUmVI<funct6, opcodestr # ".vim">; +} + +multiclass VALUm_IV_V_X<string opcodestr, bits<6> funct6> { + def VM : VALUmVV<funct6, OPIVV, opcodestr # ".vvm">; + def XM : VALUmVX<funct6, OPIVX, opcodestr # ".vxm">; +} + +multiclass VALUNoVm_IV_V_X_I<string opcodestr, bits<6> funct6, Operand optype = simm5> { + def V : VALUVVNoVm<funct6, OPIVV, opcodestr # ".vv">; + def X : VALUVXNoVm<funct6, OPIVX, opcodestr # ".vx">; + def I : VALUVINoVm<funct6, opcodestr # ".vi", optype>; +} + +multiclass VALUNoVm_IV_V_X<string opcodestr, bits<6> funct6> { + def V : VALUVVNoVm<funct6, OPIVV, opcodestr # ".vv">; + def X : VALUVXNoVm<funct6, OPIVX, opcodestr # ".vx">; +} + +multiclass VALU_FV_V_F<string opcodestr, bits<6> funct6, string vw = "v"> { + def V : VALUVV<funct6, OPFVV, opcodestr # "." # vw # "v">; + def F : VALUVF<funct6, OPFVF, opcodestr # "." # vw # "f">; +} + +multiclass VALU_FV_F<string opcodestr, bits<6> funct6, string vw = "v"> { + def F : VALUVF<funct6, OPFVF, opcodestr # "." # vw # "f">; +} + +multiclass VALUr_FV_V_F<string opcodestr, bits<6> funct6, string vw = "v"> { + def V : VALUrVV<funct6, OPFVV, opcodestr # "." # vw # "v">; + def F : VALUrVF<funct6, OPFVF, opcodestr # "." # vw # "f">; +} + +multiclass VALU_FV_V<string opcodestr, bits<6> funct6> { + def _VS : VALUVV<funct6, OPFVV, opcodestr # ".vs">; +} + +multiclass VALU_FV_VS2<string opcodestr, bits<6> funct6, bits<5> vs1> { + def "" : VALUVs2<funct6, vs1, OPFVV, opcodestr>; +} + +//===----------------------------------------------------------------------===// +// Instructions +//===----------------------------------------------------------------------===// + +let Predicates = [HasStdExtV] in { +let hasSideEffects = 1, mayLoad = 0, mayStore = 0 in { +def VSETVLI : RVInstSetVLi<(outs GPR:$rd), (ins GPR:$rs1, VTypeIOp:$vtypei), + "vsetvli", "$rd, $rs1, $vtypei">; + +def VSETVL : RVInstSetVL<(outs GPR:$rd), (ins GPR:$rs1, GPR:$rs2), + "vsetvl", "$rd, $rs1, $rs2">; +} // hasSideEffects = 1, mayLoad = 0, mayStore = 0 + +// Vector Unit-Stride Instructions +def VLB_V : VUnitStrideLoad<MOPLDUnitStrideS, LUMOPUnitStride, LSWidthVByte, "vlb.v">; +def VLH_V : VUnitStrideLoad<MOPLDUnitStrideS, LUMOPUnitStride, LSWidthVHalf, "vlh.v">; +def VLW_V : VUnitStrideLoad<MOPLDUnitStrideS, LUMOPUnitStride, LSWidthVWord, "vlw.v">; + +def VLBU_V : VUnitStrideLoad<MOPLDUnitStrideU, LUMOPUnitStride, LSWidthVByte, "vlbu.v">; +def VLHU_V : VUnitStrideLoad<MOPLDUnitStrideU, LUMOPUnitStride, LSWidthVHalf, "vlhu.v">; +def VLWU_V : VUnitStrideLoad<MOPLDUnitStrideU, LUMOPUnitStride, LSWidthVWord, "vlwu.v">; + +def VLE_V : VUnitStrideLoad<MOPLDUnitStrideU, LUMOPUnitStride, LSWidthVSEW, "vle.v">; + +def VLBFF_V : VUnitStrideLoad<MOPLDUnitStrideS, LUMOPUnitStrideFF, LSWidthVByte, "vlbff.v">; +def VLHFF_V : VUnitStrideLoad<MOPLDUnitStrideS, LUMOPUnitStrideFF, LSWidthVHalf, "vlhff.v">; +def VLWFF_V : VUnitStrideLoad<MOPLDUnitStrideS, LUMOPUnitStrideFF, LSWidthVWord, "vlwff.v">; + +def VLBUFF_V : VUnitStrideLoad<MOPLDUnitStrideU, LUMOPUnitStrideFF, LSWidthVByte, "vlbuff.v">; +def VLHUFF_V : VUnitStrideLoad<MOPLDUnitStrideU, LUMOPUnitStrideFF, LSWidthVHalf, "vlhuff.v">; +def VLWUFF_V : VUnitStrideLoad<MOPLDUnitStrideU, LUMOPUnitStrideFF, LSWidthVWord, "vlwuff.v">; + +def VLEFF_V : VUnitStrideLoad<MOPLDUnitStrideU, LUMOPUnitStrideFF, LSWidthVSEW, "vleff.v">; + +def VSB_V : VUnitStrideStore<MOPSTUnitStride, SUMOPUnitStride, LSWidthVByte, "vsb.v">; +def VSH_V : VUnitStrideStore<MOPSTUnitStride, SUMOPUnitStride, LSWidthVHalf, "vsh.v">; +def VSW_V : VUnitStrideStore<MOPSTUnitStride, SUMOPUnitStride, LSWidthVWord, "vsw.v">; + +def VSE_V : VUnitStrideStore<MOPSTUnitStride, SUMOPUnitStride, LSWidthVSEW, "vse.v">; + +// Vector Strided Instructions +def VLSB_V : VStridedLoad<MOPLDStridedS, LSWidthVByte, "vlsb.v">; +def VLSH_V : VStridedLoad<MOPLDStridedS, LSWidthVHalf, "vlsh.v">; +def VLSW_V : VStridedLoad<MOPLDStridedS, LSWidthVWord, "vlsw.v">; + +def VLSBU_V : VStridedLoad<MOPLDStridedU, LSWidthVByte, "vlsbu.v">; +def VLSHU_V : VStridedLoad<MOPLDStridedU, LSWidthVHalf, "vlshu.v">; +def VLSWU_V : VStridedLoad<MOPLDStridedU, LSWidthVWord, "vlswu.v">; + +def VLSE_V : VStridedLoad<MOPLDStridedU, LSWidthVSEW, "vlse.v">; + +def VSSB_V : VStridedStore<MOPSTStrided, LSWidthVByte, "vssb.v">; +def VSSH_V : VStridedStore<MOPSTStrided, LSWidthVHalf, "vssh.v">; +def VSSW_V : VStridedStore<MOPSTStrided, LSWidthVWord, "vssw.v">; +def VSSE_V : VStridedStore<MOPSTStrided, LSWidthVSEW, "vsse.v">; + +// Vector Indexed Instructions +def VLXB_V : VIndexedLoad<MOPLDIndexedS, LSWidthVByte, "vlxb.v">; +def VLXH_V : VIndexedLoad<MOPLDIndexedS, LSWidthVHalf, "vlxh.v">; +def VLXW_V : VIndexedLoad<MOPLDIndexedS, LSWidthVWord, "vlxw.v">; + +def VLXBU_V : VIndexedLoad<MOPLDIndexedU, LSWidthVByte, "vlxbu.v">; +def VLXHU_V : VIndexedLoad<MOPLDIndexedU, LSWidthVHalf, "vlxhu.v">; +def VLXWU_V : VIndexedLoad<MOPLDIndexedU, LSWidthVWord, "vlxwu.v">; + +def VLXE_V : VIndexedLoad<MOPLDIndexedU, LSWidthVSEW, "vlxe.v">; + +def VSXB_V : VIndexedStore<MOPSTIndexedOrder, LSWidthVByte, "vsxb.v">; +def VSXH_V : VIndexedStore<MOPSTIndexedOrder, LSWidthVHalf, "vsxh.v">; +def VSXW_V : VIndexedStore<MOPSTIndexedOrder, LSWidthVWord, "vsxw.v">; +def VSXE_V : VIndexedStore<MOPSTIndexedOrder, LSWidthVSEW, "vsxe.v">; + +def VSUXB_V : VIndexedStore<MOPSTIndexedUnOrd, LSWidthVByte, "vsuxb.v">; +def VSUXH_V : VIndexedStore<MOPSTIndexedUnOrd, LSWidthVHalf, "vsuxh.v">; +def VSUXW_V : VIndexedStore<MOPSTIndexedUnOrd, LSWidthVWord, "vsuxw.v">; +def VSUXE_V : VIndexedStore<MOPSTIndexedUnOrd, LSWidthVSEW, "vsuxe.v">; + +def VL1R_V : VWholeLoad<0, "vl1r.v">; +def VS1R_V : VWholeStore<0, "vs1r.v">; + +// Vector Single-Width Integer Add and Subtract +defm VADD_V : VALU_IV_V_X_I<"vadd", 0b000000>; +defm VSUB_V : VALU_IV_V_X<"vsub", 0b000010>; +defm VRSUB_V : VALU_IV_X_I<"vrsub", 0b000011>; + +// Vector Widening Integer Add/Subtract +// Refer to 11.2 Widening Vector Arithmetic Instructions +// The destination vector register group cannot overlap a source vector +// register group of a different element width (including the mask register +// if masked), otherwise an illegal instruction exception is raised. +let Constraints = "@earlyclobber $vd" in { +let RVVConstraint = WidenV in { +defm VWADDU_V : VALU_MV_V_X<"vwaddu", 0b110000>; +defm VWSUBU_V : VALU_MV_V_X<"vwsubu", 0b110010>; +defm VWADD_V : VALU_MV_V_X<"vwadd", 0b110001>; +defm VWSUB_V : VALU_MV_V_X<"vwsub", 0b110011>; +} // RVVConstraint = WidenV +// Set earlyclobber for following instructions for second and mask operands. +// This has the downside that the earlyclobber constraint is too coarse and +// will impose unnecessary restrictions by not allowing the destination to +// overlap with the first (wide) operand. +let RVVConstraint = WidenW in { +defm VWADDU_W : VALU_MV_V_X<"vwaddu", 0b110100, "w">; +defm VWSUBU_W : VALU_MV_V_X<"vwsubu", 0b110110, "w">; +defm VWADD_W : VALU_MV_V_X<"vwadd", 0b110101, "w">; +defm VWSUB_W : VALU_MV_V_X<"vwsub", 0b110111, "w">; +} // RVVConstraint = WidenW +} // Constraints = "@earlyclobber $vd" + +def : InstAlias<"vwcvt.x.x.v $vd, $vs$vm", + (VWADD_VX VRegOp:$vd, VRegOp:$vs, X0, VMaskOp:$vm)>; +def : InstAlias<"vwcvtu.x.x.v $vd, $vs$vm", + (VWADDU_VX VRegOp:$vd, VRegOp:$vs, X0, VMaskOp:$vm)>; + +// Vector Integer Add-with-Carry / Subtract-with-Borrow Instructions +defm VADC_V : VALUm_IV_V_X_I<"vadc", 0b010000>; +defm VMADC_V : VALUm_IV_V_X_I<"vmadc", 0b010001>; +defm VMADC_V : VALUNoVm_IV_V_X_I<"vmadc", 0b010001>; +defm VSBC_V : VALUm_IV_V_X<"vsbc", 0b010010>; +defm VMSBC_V : VALUm_IV_V_X<"vmsbc", 0b010011>; +defm VMSBC_V : VALUNoVm_IV_V_X<"vmsbc", 0b010011>; + +// Vector Bitwise Logical Instructions +defm VAND_V : VALU_IV_V_X_I<"vand", 0b001001>; +defm VOR_V : VALU_IV_V_X_I<"vor", 0b001010>; +defm VXOR_V : VALU_IV_V_X_I<"vxor", 0b001011>; + +def : InstAlias<"vnot.v $vd, $vs$vm", + (VXOR_VI VRegOp:$vd, VRegOp:$vs, -1, VMaskOp:$vm)>; + +// Vector Single-Width Bit Shift Instructions +defm VSLL_V : VALU_IV_V_X_I<"vsll", 0b100101, uimm5>; +defm VSRL_V : VALU_IV_V_X_I<"vsrl", 0b101000, uimm5>; +defm VSRA_V : VALU_IV_V_X_I<"vsra", 0b101001, uimm5>; + +// Vector Narrowing Integer Right Shift Instructions +// Refer to 11.3. Narrowing Vector Arithmetic Instructions +// The destination vector register group cannot overlap the first source +// vector register group (specified by vs2). The destination vector register +// group cannot overlap the mask register if used, unless LMUL=1. +let Constraints = "@earlyclobber $vd", RVVConstraint = Narrow in { +defm VNSRL_W : VALU_IV_V_X_I<"vnsrl", 0b101100, uimm5, "w">; +defm VNSRA_W : VALU_IV_V_X_I<"vnsra", 0b101101, uimm5, "w">; +} // Constraints = "@earlyclobber $vd", RVVConstraint = Narrow + +// Vector Integer Comparison Instructions +defm VMSEQ_V : VALU_IV_V_X_I<"vmseq", 0b011000>; +defm VMSNE_V : VALU_IV_V_X_I<"vmsne", 0b011001>; +defm VMSLTU_V : VALU_IV_V_X<"vmsltu", 0b011010>; +defm VMSLT_V : VALU_IV_V_X<"vmslt", 0b011011>; +defm VMSLEU_V : VALU_IV_V_X_I<"vmsleu", 0b011100>; +defm VMSLE_V : VALU_IV_V_X_I<"vmsle", 0b011101>; +defm VMSGTU_V : VALU_IV_X_I<"vmsgtu", 0b011110>; +defm VMSGT_V : VALU_IV_X_I<"vmsgt", 0b011111>; + +def : InstAlias<"vmsgtu.vv $vd, $va, $vb$vm", + (VMSLTU_VV VRegOp:$vd, VRegOp:$vb, VRegOp:$va, VMaskOp:$vm), 0>; +def : InstAlias<"vmsgt.vv $vd, $va, $vb$vm", + (VMSLT_VV VRegOp:$vd, VRegOp:$vb, VRegOp:$va, VMaskOp:$vm), 0>; +def : InstAlias<"vmsgeu.vv $vd, $va, $vb$vm", + (VMSLEU_VV VRegOp:$vd, VRegOp:$vb, VRegOp:$va, VMaskOp:$vm), 0>; +def : InstAlias<"vmsge.vv $vd, $va, $vb$vm", + (VMSLE_VV VRegOp:$vd, VRegOp:$vb, VRegOp:$va, VMaskOp:$vm), 0>; +def : InstAlias<"vmsltu.vi $vd, $va, $imm$vm", + (VMSLEU_VI VRegOp:$vd, VRegOp:$va, simm5_plus1:$imm, + VMaskOp:$vm), 0>; +def : InstAlias<"vmslt.vi $vd, $va, $imm$vm", + (VMSLE_VI VRegOp:$vd, VRegOp:$va, simm5_plus1:$imm, + VMaskOp:$vm), 0>; +def : InstAlias<"vmsgeu.vi $vd, $va, $imm$vm", + (VMSGTU_VI VRegOp:$vd, VRegOp:$va, simm5_plus1:$imm, + VMaskOp:$vm), 0>; +def : InstAlias<"vmsge.vi $vd, $va, $imm$vm", + (VMSGT_VI VRegOp:$vd, VRegOp:$va, simm5_plus1:$imm, + VMaskOp:$vm), 0>; + +// Vector Integer Min/Max Instructions +defm VMINU_V : VALU_IV_V_X<"vminu", 0b000100>; +defm VMIN_V : VALU_IV_V_X<"vmin", 0b000101>; +defm VMAXU_V : VALU_IV_V_X<"vmaxu", 0b000110>; +defm VMAX_V : VALU_IV_V_X<"vmax", 0b000111>; + +// Vector Single-Width Integer Multiply Instructions +defm VMUL_V : VALU_MV_V_X<"vmul", 0b100101>; +defm VMULH_V : VALU_MV_V_X<"vmulh", 0b100111>; +defm VMULHU_V : VALU_MV_V_X<"vmulhu", 0b100100>; +defm VMULHSU_V : VALU_MV_V_X<"vmulhsu", 0b100110>; + +// Vector Integer Divide Instructions +defm VDIVU_V : VALU_MV_V_X<"vdivu", 0b100000>; +defm VDIV_V : VALU_MV_V_X<"vdiv", 0b100001>; +defm VREMU_V : VALU_MV_V_X<"vremu", 0b100010>; +defm VREM_V : VALU_MV_V_X<"vrem", 0b100011>; + +// Vector Widening Integer Multiply Instructions +let Constraints = "@earlyclobber $vd", RVVConstraint = WidenV in { +defm VWMUL_V : VALU_MV_V_X<"vwmul", 0b111011>; +defm VWMULU_V : VALU_MV_V_X<"vwmulu", 0b111000>; +defm VWMULSU_V : VALU_MV_V_X<"vwmulsu", 0b111010>; +} // Constraints = "@earlyclobber $vd", RVVConstraint = WidenV + +// Vector Single-Width Integer Multiply-Add Instructions +defm VMACC_V : VALUr_MV_V_X<"vmacc", 0b101101>; +defm VNMSAC_V : VALUr_MV_V_X<"vnmsac", 0b101111>; +defm VMADD_V : VALUr_MV_V_X<"vmadd", 0b101001>; +defm VNMSUB_V : VALUr_MV_V_X<"vnmsub", 0b101011>; + +// Vector Widening Integer Multiply-Add Instructions +let Constraints = "@earlyclobber $vd", RVVConstraint = WidenV in { +defm VWMACCU_V : VALUr_MV_V_X<"vwmaccu", 0b111100>; +defm VWMACC_V : VALUr_MV_V_X<"vwmacc", 0b111101>; +defm VWMACCSU_V : VALUr_MV_V_X<"vwmaccsu", 0b111111>; +defm VWMACCUS_V : VALUr_MV_X<"vwmaccus", 0b111110>; +} // Constraints = "@earlyclobber $vd", RVVConstraint = WidenV + +// Vector Integer Merge Instructions +defm VMERGE_V : VALUm_IV_V_X_I<"vmerge", 0b010111>; + +// Vector Integer Move Instructions +let hasSideEffects = 0, mayLoad = 0, mayStore = 0, vs2 = 0, vm = 1 in { +// op vd, vs1 +def VMV_V_V : RVInstVV<0b010111, OPIVV, (outs VRegOp:$vd), + (ins VRegOp:$vs1), "vmv.v.v", "$vd, $vs1">; +// op vd, rs1 +def VMV_V_X : RVInstVX<0b010111, OPIVX, (outs VRegOp:$vd), + (ins GPR:$rs1), "vmv.v.x", "$vd, $rs1">; +// op vd, imm +def VMV_V_I : RVInstIVI<0b010111, (outs VRegOp:$vd), + (ins simm5:$imm), "vmv.v.i", "$vd, $imm">; +} // hasSideEffects = 0, mayLoad = 0, mayStore = 0 + +// Vector Fixed-Point Arithmetic Instructions +defm VSADDU_V : VALU_IV_V_X_I<"vsaddu", 0b100000>; +defm VSADD_V : VALU_IV_V_X_I<"vsadd", 0b100001>; +defm VSSUBU_V : VALU_IV_V_X<"vssubu", 0b100010>; +defm VSSUB_V : VALU_IV_V_X<"vssub", 0b100011>; + +// Vector Single-Width Averaging Add and Subtract +defm VAADDU_V : VALU_MV_V_X<"vaaddu", 0b001000>; +defm VAADD_V : VALU_MV_V_X<"vaadd", 0b001001>; +defm VASUBU_V : VALU_MV_V_X<"vasubu", 0b001010>; +defm VASUB_V : VALU_MV_V_X<"vasub", 0b001011>; + +// Vector Single-Width Fractional Multiply with Rounding and Saturation +defm VSMUL_V : VALU_IV_V_X<"vsmul", 0b100111>; + +// Vector Single-Width Scaling Shift Instructions +defm VSSRL_V : VALU_IV_V_X_I<"vssrl", 0b101010, uimm5>; +defm VSSRA_V : VALU_IV_V_X_I<"vssra", 0b101011, uimm5>; + +// Vector Narrowing Fixed-Point Clip Instructions +let Constraints = "@earlyclobber $vd", RVVConstraint = Narrow in { +defm VNCLIPU_W : VALU_IV_V_X_I<"vnclipu", 0b101110, uimm5, "w">; +defm VNCLIP_W : VALU_IV_V_X_I<"vnclip", 0b101111, uimm5, "w">; +} // Constraints = "@earlyclobber $vd", RVVConstraint = Narrow + +// Vector Single-Width Floating-Point Add/Subtract Instructions +defm VFADD_V : VALU_FV_V_F<"vfadd", 0b000000>; +defm VFSUB_V : VALU_FV_V_F<"vfsub", 0b000010>; +defm VFRSUB_V : VALU_FV_F<"vfrsub", 0b100111>; + +// Vector Widening Floating-Point Add/Subtract Instructions +let Constraints = "@earlyclobber $vd" in { +let RVVConstraint = WidenV in { +defm VFWADD_V : VALU_FV_V_F<"vfwadd", 0b110000>; +defm VFWSUB_V : VALU_FV_V_F<"vfwsub", 0b110010>; +} // RVVConstraint = WidenV +// Set earlyclobber for following instructions for second and mask operands. +// This has the downside that the earlyclobber constraint is too coarse and +// will impose unnecessary restrictions by not allowing the destination to +// overlap with the first (wide) operand. +let RVVConstraint = WidenW in { +defm VFWADD_W : VALU_FV_V_F<"vfwadd", 0b110100, "w">; +defm VFWSUB_W : VALU_FV_V_F<"vfwsub", 0b110110, "w">; +} // RVVConstraint = WidenW +} // Constraints = "@earlyclobber $vd" + +// Vector Single-Width Floating-Point Multiply/Divide Instructions +defm VFMUL_V : VALU_FV_V_F<"vfmul", 0b100100>; +defm VFDIV_V : VALU_FV_V_F<"vfdiv", 0b100000>; +defm VFRDIV_V : VALU_FV_F<"vfrdiv", 0b100001>; + +// Vector Widening Floating-Point Multiply +let Constraints = "@earlyclobber $vd", RVVConstraint = WidenV in { +defm VFWMUL_V : VALU_FV_V_F<"vfwmul", 0b111000>; +} // Constraints = "@earlyclobber $vd", RVVConstraint = WidenV + +// Vector Single-Width Floating-Point Fused Multiply-Add Instructions +defm VFMACC_V : VALUr_FV_V_F<"vfmacc", 0b101100>; +defm VFNMACC_V : VALUr_FV_V_F<"vfnmacc", 0b101101>; +defm VFMSAC_V : VALUr_FV_V_F<"vfmsac", 0b101110>; +defm VFNMSAC_V : VALUr_FV_V_F<"vfnmsac", 0b101111>; +defm VFMADD_V : VALUr_FV_V_F<"vfmadd", 0b101000>; +defm VFNMADD_V : VALUr_FV_V_F<"vfnmadd", 0b101001>; +defm VFMSUB_V : VALUr_FV_V_F<"vfmsub", 0b101010>; +defm VFNMSUB_V : VALUr_FV_V_F<"vfnmsub", 0b101011>; + +// Vector Widening Floating-Point Fused Multiply-Add Instructions +let Constraints = "@earlyclobber $vd", RVVConstraint = WidenV in { +defm VFWMACC_V : VALUr_FV_V_F<"vfwmacc", 0b111100>; +defm VFWNMACC_V : VALUr_FV_V_F<"vfwnmacc", 0b111101>; +defm VFWMSAC_V : VALUr_FV_V_F<"vfwmsac", 0b111110>; +defm VFWNMSAC_V : VALUr_FV_V_F<"vfwnmsac", 0b111111>; +} // Constraints = "@earlyclobber $vd", RVVConstraint = WidenV + +// Vector Floating-Point Square-Root Instruction +defm VFSQRT_V : VALU_FV_VS2<"vfsqrt.v", 0b100011, 0b00000>; + +// Vector Floating-Point MIN/MAX Instructions +defm VFMIN_V : VALU_FV_V_F<"vfmin", 0b000100>; +defm VFMAX_V : VALU_FV_V_F<"vfmax", 0b000110>; + +// Vector Floating-Point Sign-Injection Instructions +defm VFSGNJ_V : VALU_FV_V_F<"vfsgnj", 0b001000>; +defm VFSGNJN_V : VALU_FV_V_F<"vfsgnjn", 0b001001>; +defm VFSGNJX_V : VALU_FV_V_F<"vfsgnjx", 0b001010>; + +// Vector Floating-Point Compare Instructions +defm VMFEQ_V : VALU_FV_V_F<"vmfeq", 0b011000>; +defm VMFNE_V : VALU_FV_V_F<"vmfne", 0b011100>; +defm VMFLT_V : VALU_FV_V_F<"vmflt", 0b011011>; +defm VMFLE_V : VALU_FV_V_F<"vmfle", 0b011001>; +defm VMFGT_V : VALU_FV_F<"vmfgt", 0b011101>; +defm VMFGE_V : VALU_FV_F<"vmfge", 0b011111>; + +def : InstAlias<"vmfgt.vv $vd, $va, $vb$vm", + (VMFLT_VV VRegOp:$vd, VRegOp:$vb, VRegOp:$va, VMaskOp:$vm), 0>; +def : InstAlias<"vmfge.vv $vd, $va, $vb$vm", + (VMFLE_VV VRegOp:$vd, VRegOp:$vb, VRegOp:$va, VMaskOp:$vm), 0>; + +// Vector Floating-Point Classify Instruction +defm VFCLASS_V : VALU_FV_VS2<"vfclass.v", 0b100011, 0b10000>; + +let hasSideEffects = 0, mayLoad = 0, mayStore = 0 in { +// Vector Floating-Point Merge Instruction +def VFMERGE_VFM : RVInstVX<0b010111, OPFVF, (outs VRegOp:$vd), + (ins VRegOp:$vs2, FPR32:$rs1, VMV0:$v0), + "vfmerge.vfm", "$vd, $vs2, $rs1, v0"> { + let vm = 0; +} + +// Vector Floating-Point Move Instruction +def VFMV_V_F : RVInstVX<0b010111, OPFVF, (outs VRegOp:$vd), + (ins FPR32:$rs1), "vfmv.v.f", "$vd, $rs1"> { + let vs2 = 0; + let vm = 1; +} +} // hasSideEffects = 0, mayLoad = 0, mayStore = 0 + +// Single-Width Floating-Point/Integer Type-Convert Instructions +defm VFCVT_XU_F_V : VALU_FV_VS2<"vfcvt.xu.f.v", 0b100010, 0b00000>; +defm VFCVT_X_F_V : VALU_FV_VS2<"vfcvt.x.f.v", 0b100010, 0b00001>; +defm VFCVT_F_XU_V : VALU_FV_VS2<"vfcvt.f.xu.v", 0b100010, 0b00010>; +defm VFCVT_F_X_V : VALU_FV_VS2<"vfcvt.f.x.v", 0b100010, 0b00011>; + +// Widening Floating-Point/Integer Type-Convert Instructions +let Constraints = "@earlyclobber $vd", RVVConstraint = WidenCvt in { +defm VFWCVT_XU_F_V : VALU_FV_VS2<"vfwcvt.xu.f.v", 0b100010, 0b01000>; +defm VFWCVT_X_F_V : VALU_FV_VS2<"vfwcvt.x.f.v", 0b100010, 0b01001>; +defm VFWCVT_F_XU_V : VALU_FV_VS2<"vfwcvt.f.xu.v", 0b100010, 0b01010>; +defm VFWCVT_F_X_V : VALU_FV_VS2<"vfwcvt.f.x.v", 0b100010, 0b01011>; +defm VFWCVT_F_F_V : VALU_FV_VS2<"vfwcvt.f.f.v", 0b100010, 0b01100>; +} // Constraints = "@earlyclobber $vd", RVVConstraint = WidenCvt + +// Narrowing Floating-Point/Integer Type-Convert Instructions +let Constraints = "@earlyclobber $vd", RVVConstraint = Narrow in { +defm VFNCVT_XU_F_W : VALU_FV_VS2<"vfncvt.xu.f.w", 0b100010, 0b10000>; +defm VFNCVT_X_F_W : VALU_FV_VS2<"vfncvt.x.f.w", 0b100010, 0b10001>; +defm VFNCVT_F_XU_W : VALU_FV_VS2<"vfncvt.f.xu.w", 0b100010, 0b10010>; +defm VFNCVT_F_X_W : VALU_FV_VS2<"vfncvt.f.x.w", 0b100010, 0b10011>; +defm VFNCVT_F_F_W : VALU_FV_VS2<"vfncvt.f.f.w", 0b100010, 0b10100>; +defm VFNCVT_ROD_F_F_W : VALU_FV_VS2<"vfncvt.rod.f.f.w", 0b100010, 0b10101>; +} // Constraints = "@earlyclobber $vd", RVVConstraint = Narrow + +// Vector Single-Width Integer Reduction Instructions +defm VREDSUM : VALU_MV_V<"vredsum", 0b000000>; +defm VREDMAXU : VALU_MV_V<"vredmaxu", 0b000110>; +defm VREDMAX : VALU_MV_V<"vredmax", 0b000111>; +defm VREDMINU : VALU_MV_V<"vredminu", 0b000100>; +defm VREDMIN : VALU_MV_V<"vredmin", 0b000101>; +defm VREDAND : VALU_MV_V<"vredand", 0b000001>; +defm VREDOR : VALU_MV_V<"vredor", 0b000010>; +defm VREDXOR : VALU_MV_V<"vredxor", 0b000011>; + +// Vector Widening Integer Reduction Instructions +let Constraints = "@earlyclobber $vd" in { +// Set earlyclobber for following instructions for second and mask operands. +// This has the downside that the earlyclobber constraint is too coarse and +// will impose unnecessary restrictions by not allowing the destination to +// overlap with the first (wide) operand. +defm VWREDSUMU : VALU_IV_V<"vwredsumu", 0b110000>; +defm VWREDSUM : VALU_IV_V<"vwredsum", 0b110001>; +} // Constraints = "@earlyclobber $vd" + +// Vector Single-Width Floating-Point Reduction Instructions +defm VFREDOSUM : VALU_FV_V<"vfredosum", 0b000011>; +defm VFREDSUM : VALU_FV_V<"vfredsum", 0b000001>; +defm VFREDMAX : VALU_FV_V<"vfredmax", 0b000111>; +defm VFREDMIN : VALU_FV_V<"vfredmin", 0b000101>; + +// Vector Widening Floating-Point Reduction Instructions +let Constraints = "@earlyclobber $vd" in { +// Set earlyclobber for following instructions for second and mask operands. +// This has the downside that the earlyclobber constraint is too coarse and +// will impose unnecessary restrictions by not allowing the destination to +// overlap with the first (wide) operand. +defm VFWREDOSUM : VALU_FV_V<"vfwredosum", 0b110011>; +defm VFWREDSUM : VALU_FV_V<"vfwredsum", 0b110001>; +} // Constraints = "@earlyclobber $vd" + +// Vector Mask-Register Logical Instructions +defm VMAND_M : VALU_MV_Mask<"vmand", 0b011001, "m">; +defm VMNAND_M : VALU_MV_Mask<"vmnand", 0b011101, "m">; +defm VMANDNOT_M : VALU_MV_Mask<"vmandnot", 0b011000, "m">; +defm VMXOR_M : VALU_MV_Mask<"vmxor", 0b011011, "m">; +defm VMOR_M : VALU_MV_Mask<"vmor", 0b011010, "m">; +defm VMNOR_M : VALU_MV_Mask<"vmnor", 0b011110, "m">; +defm VMORNOT_M : VALU_MV_Mask<"vmornot", 0b011100, "m">; +defm VMXNOR_M : VALU_MV_Mask<"vmxnor", 0b011111, "m">; + +def : InstAlias<"vmcpy.m $vd, $vs", + (VMAND_MM VRegOp:$vd, VRegOp:$vs, VRegOp:$vs)>; +def : InstAlias<"vmclr.m $vd", + (VMXOR_MM VRegOp:$vd, VRegOp:$vd, VRegOp:$vd)>; +def : InstAlias<"vmset.m $vd", + (VMXNOR_MM VRegOp:$vd, VRegOp:$vd, VRegOp:$vd)>; +def : InstAlias<"vmnot.m $vd, $vs", + (VMNAND_MM VRegOp:$vd, VRegOp:$vs, VRegOp:$vs)>; + +let hasSideEffects = 0, mayLoad = 0, mayStore = 0 in { +// Vector mask population count vpopc +def VPOPC_M : RVInstV<0b010000, 0b10000, OPMVV, (outs GPR:$vd), + (ins VRegOp:$vs2, VMaskOp:$vm), + "vpopc.m", "$vd, $vs2$vm">; + +// vfirst find-first-set mask bit +def VFIRST_M : RVInstV<0b010000, 0b10001, OPMVV, (outs GPR:$vd), + (ins VRegOp:$vs2, VMaskOp:$vm), + "vfirst.m", "$vd, $vs2$vm">; +} // hasSideEffects = 0, mayLoad = 0, mayStore = 0 + +// vmsbf.m set-before-first mask bit +defm VMSBF_M : VALU_MV_VS2<"vmsbf.m", 0b010100, 0b00001>; + +// vmsif.m set-including-first mask bit +defm VMSIF_M : VALU_MV_VS2<"vmsif.m", 0b010100, 0b00011>; + +// vmsof.m set-only-first mask bit +defm VMSOF_M : VALU_MV_VS2<"vmsof.m", 0b010100, 0b00010>; + +// Vector Iota Instruction +let Constraints = "@earlyclobber $vd", RVVConstraint = Iota in { +defm VIOTA_M : VALU_MV_VS2<"viota.m", 0b010100, 0b10000>; +} // Constraints = "@earlyclobber $vd", RVVConstraint = Iota + +// Vector Element Index Instruction +let hasSideEffects = 0, mayLoad = 0, mayStore = 0 in { +def VID_V : RVInstV<0b010100, 0b10001, OPMVV, (outs VRegOp:$vd), + (ins VMaskOp:$vm), "vid.v", "$vd$vm"> { + let vs2 = 0; +} + +// Integer Scalar Move Instructions +let vm = 1 in { +def VMV_X_S : RVInstV<0b010000, 0b00000, OPMVV, (outs GPR:$vd), + (ins VRegOp:$vs2), "vmv.x.s", "$vd, $vs2">; +def VMV_S_X : RVInstV2<0b010000, 0b00000, OPMVX, (outs VRegOp:$vd), + (ins GPR:$rs1), "vmv.s.x", "$vd, $rs1">; + +} +} // hasSideEffects = 0, mayLoad = 0, mayStore = 0 + +let hasSideEffects = 0, mayLoad = 0, mayStore = 0, vm = 1 in { +// Floating-Point Scalar Move Instructions +def VFMV_F_S : RVInstV<0b010000, 0b00000, OPFVV, (outs FPR32:$vd), + (ins VRegOp:$vs2), "vfmv.f.s", "$vd, $vs2">; +def VFMV_S_F : RVInstV2<0b010000, 0b00000, OPFVF, (outs VRegOp:$vd), + (ins FPR32:$rs1), "vfmv.s.f", "$vd, $rs1">; + +} // hasSideEffects = 0, mayLoad = 0, mayStore = 0, vm = 1 + +// Vector Slide Instructions +let Constraints = "@earlyclobber $vd", RVVConstraint = SlideUp in { +defm VSLIDEUP_V : VALU_IV_X_I<"vslideup", 0b001110, uimm5>; +} // Constraints = "@earlyclobber $vd", RVVConstraint = SlideUp +defm VSLIDEDOWN_V : VALU_IV_X_I<"vslidedown", 0b001111, uimm5>; + +let Constraints = "@earlyclobber $vd", RVVConstraint = SlideUp in { +defm VSLIDE1UP_V : VALU_MV_X<"vslide1up", 0b001110>; +} // Constraints = "@earlyclobber $vd", RVVConstraint = SlideUp +defm VSLIDE1DOWN_V : VALU_MV_X<"vslide1down", 0b001111>; + +// Vector Register Gather Instruction +let Constraints = "@earlyclobber $vd", RVVConstraint = Vrgather in { +defm VRGATHER_V : VALU_IV_V_X_I<"vrgather", 0b001100, uimm5>; +} // Constraints = "@earlyclobber $vd", RVVConstraint = Vrgather + +// Vector Compress Instruction +let Constraints = "@earlyclobber $vd", RVVConstraint = Vcompress in { +defm VCOMPRESS_V : VALU_MV_Mask<"vcompress", 0b010111>; +} // Constraints = "@earlyclobber $vd", RVVConstraint = Vcompress + +let hasSideEffects = 0, mayLoad = 0, mayStore = 0 in { +foreach nf = [1, 2, 4, 8] in { + def VMV#nf#R_V : RVInstV<0b100111, !add(nf, -1), OPIVI, (outs VRegOp:$vd), + (ins VRegOp:$vs2), "vmv" # nf # "r.v", + "$vd, $vs2"> { + let Uses = []; + let vm = 1; + } +} +} // hasSideEffects = 0, mayLoad = 0, mayStore = 0 +} // Predicates = [HasStdExtV] diff --git a/llvm/lib/Target/RISCV/RISCVInstructionSelector.cpp b/llvm/lib/Target/RISCV/RISCVInstructionSelector.cpp index 5bd09a546114..4d1f47da209d 100644 --- a/llvm/lib/Target/RISCV/RISCVInstructionSelector.cpp +++ b/llvm/lib/Target/RISCV/RISCVInstructionSelector.cpp @@ -16,6 +16,7 @@ #include "RISCVTargetMachine.h" #include "llvm/CodeGen/GlobalISel/InstructionSelector.h" #include "llvm/CodeGen/GlobalISel/InstructionSelectorImpl.h" +#include "llvm/IR/IntrinsicsRISCV.h" #include "llvm/Support/Debug.h" #define DEBUG_TYPE "riscv-isel" diff --git a/llvm/lib/Target/RISCV/RISCVMachineFunctionInfo.h b/llvm/lib/Target/RISCV/RISCVMachineFunctionInfo.h index 585bff2bc20a..c379a8d8f0d6 100644 --- a/llvm/lib/Target/RISCV/RISCVMachineFunctionInfo.h +++ b/llvm/lib/Target/RISCV/RISCVMachineFunctionInfo.h @@ -13,6 +13,7 @@ #ifndef LLVM_LIB_TARGET_RISCV_RISCVMACHINEFUNCTIONINFO_H #define LLVM_LIB_TARGET_RISCV_RISCVMACHINEFUNCTIONINFO_H +#include "RISCVSubtarget.h" #include "llvm/CodeGen/MachineFrameInfo.h" #include "llvm/CodeGen/MachineFunction.h" @@ -22,7 +23,6 @@ namespace llvm { /// and contains private RISCV-specific information for each MachineFunction. class RISCVMachineFunctionInfo : public MachineFunctionInfo { private: - MachineFunction &MF; /// FrameIndex for start of varargs area int VarArgsFrameIndex = 0; /// Size of the save area used for varargs @@ -30,9 +30,11 @@ private: /// FrameIndex used for transferring values between 64-bit FPRs and a pair /// of 32-bit GPRs via the stack. int MoveF64FrameIndex = -1; + /// Size of any opaque stack adjustment due to save/restore libcalls. + unsigned LibCallStackSize = 0; public: - RISCVMachineFunctionInfo(MachineFunction &MF) : MF(MF) {} + RISCVMachineFunctionInfo(const MachineFunction &MF) {} int getVarArgsFrameIndex() const { return VarArgsFrameIndex; } void setVarArgsFrameIndex(int Index) { VarArgsFrameIndex = Index; } @@ -40,11 +42,22 @@ public: unsigned getVarArgsSaveSize() const { return VarArgsSaveSize; } void setVarArgsSaveSize(int Size) { VarArgsSaveSize = Size; } - int getMoveF64FrameIndex() { + int getMoveF64FrameIndex(MachineFunction &MF) { if (MoveF64FrameIndex == -1) - MoveF64FrameIndex = MF.getFrameInfo().CreateStackObject(8, 8, false); + MoveF64FrameIndex = + MF.getFrameInfo().CreateStackObject(8, Align(8), false); return MoveF64FrameIndex; } + + unsigned getLibCallStackSize() const { return LibCallStackSize; } + void setLibCallStackSize(unsigned Size) { LibCallStackSize = Size; } + + bool useSaveRestoreLibCalls(const MachineFunction &MF) const { + // We cannot use fixed locations for the callee saved spill slots if the + // function uses a varargs save area. + return MF.getSubtarget<RISCVSubtarget>().enableSaveRestore() && + VarArgsSaveSize == 0 && !MF.getFrameInfo().hasTailCall(); + } }; } // end namespace llvm diff --git a/llvm/lib/Target/RISCV/RISCVRegisterInfo.cpp b/llvm/lib/Target/RISCV/RISCVRegisterInfo.cpp index 1d41994ef1e3..cb7d55eb0f0c 100644 --- a/llvm/lib/Target/RISCV/RISCVRegisterInfo.cpp +++ b/llvm/lib/Target/RISCV/RISCVRegisterInfo.cpp @@ -12,6 +12,7 @@ #include "RISCVRegisterInfo.h" #include "RISCV.h" +#include "RISCVMachineFunctionInfo.h" #include "RISCVSubtarget.h" #include "llvm/CodeGen/MachineFrameInfo.h" #include "llvm/CodeGen/MachineFunction.h" @@ -34,6 +35,8 @@ static_assert(RISCV::F31_F == RISCV::F0_F + 31, static_assert(RISCV::F1_D == RISCV::F0_D + 1, "Register list not consecutive"); static_assert(RISCV::F31_D == RISCV::F0_D + 31, "Register list not consecutive"); +static_assert(RISCV::V1 == RISCV::V0 + 1, "Register list not consecutive"); +static_assert(RISCV::V31 == RISCV::V0 + 31, "Register list not consecutive"); RISCVRegisterInfo::RISCVRegisterInfo(unsigned HwMode) : RISCVGenRegisterInfo(RISCV::X1, /*DwarfFlavour*/0, /*EHFlavor*/0, @@ -91,11 +94,11 @@ BitVector RISCVRegisterInfo::getReservedRegs(const MachineFunction &MF) const { } bool RISCVRegisterInfo::isAsmClobberable(const MachineFunction &MF, - unsigned PhysReg) const { + MCRegister PhysReg) const { return !MF.getSubtarget<RISCVSubtarget>().isRegisterReservedByUser(PhysReg); } -bool RISCVRegisterInfo::isConstantPhysReg(unsigned PhysReg) const { +bool RISCVRegisterInfo::isConstantPhysReg(MCRegister PhysReg) const { return PhysReg == RISCV::X0; } @@ -103,6 +106,39 @@ const uint32_t *RISCVRegisterInfo::getNoPreservedMask() const { return CSR_NoRegs_RegMask; } +// Frame indexes representing locations of CSRs which are given a fixed location +// by save/restore libcalls. +static const std::map<unsigned, int> FixedCSRFIMap = { + {/*ra*/ RISCV::X1, -1}, + {/*s0*/ RISCV::X8, -2}, + {/*s1*/ RISCV::X9, -3}, + {/*s2*/ RISCV::X18, -4}, + {/*s3*/ RISCV::X19, -5}, + {/*s4*/ RISCV::X20, -6}, + {/*s5*/ RISCV::X21, -7}, + {/*s6*/ RISCV::X22, -8}, + {/*s7*/ RISCV::X23, -9}, + {/*s8*/ RISCV::X24, -10}, + {/*s9*/ RISCV::X25, -11}, + {/*s10*/ RISCV::X26, -12}, + {/*s11*/ RISCV::X27, -13} +}; + +bool RISCVRegisterInfo::hasReservedSpillSlot(const MachineFunction &MF, + Register Reg, + int &FrameIdx) const { + const auto *RVFI = MF.getInfo<RISCVMachineFunctionInfo>(); + if (!RVFI->useSaveRestoreLibCalls(MF)) + return false; + + auto FII = FixedCSRFIMap.find(Reg); + if (FII == FixedCSRFIMap.end()) + return false; + + FrameIdx = FII->second; + return true; +} + void RISCVRegisterInfo::eliminateFrameIndex(MachineBasicBlock::iterator II, int SPAdj, unsigned FIOperandNum, RegScavenger *RS) const { @@ -115,7 +151,7 @@ void RISCVRegisterInfo::eliminateFrameIndex(MachineBasicBlock::iterator II, DebugLoc DL = MI.getDebugLoc(); int FrameIndex = MI.getOperand(FIOperandNum).getIndex(); - unsigned FrameReg; + Register FrameReg; int Offset = getFrameLowering(MF)->getFrameIndexReference(MF, FrameIndex, FrameReg) + MI.getOperand(FIOperandNum + 1).getImm(); @@ -156,13 +192,6 @@ const uint32_t * RISCVRegisterInfo::getCallPreservedMask(const MachineFunction & MF, CallingConv::ID /*CC*/) const { auto &Subtarget = MF.getSubtarget<RISCVSubtarget>(); - if (MF.getFunction().hasFnAttribute("interrupt")) { - if (Subtarget.hasStdExtD()) - return CSR_XLEN_F64_Interrupt_RegMask; - if (Subtarget.hasStdExtF()) - return CSR_XLEN_F32_Interrupt_RegMask; - return CSR_Interrupt_RegMask; - } switch (Subtarget.getTargetABI()) { default: diff --git a/llvm/lib/Target/RISCV/RISCVRegisterInfo.h b/llvm/lib/Target/RISCV/RISCVRegisterInfo.h index 30b639517fde..ffbb60abf755 100644 --- a/llvm/lib/Target/RISCV/RISCVRegisterInfo.h +++ b/llvm/lib/Target/RISCV/RISCVRegisterInfo.h @@ -31,12 +31,15 @@ struct RISCVRegisterInfo : public RISCVGenRegisterInfo { BitVector getReservedRegs(const MachineFunction &MF) const override; bool isAsmClobberable(const MachineFunction &MF, - unsigned PhysReg) const override; + MCRegister PhysReg) const override; - bool isConstantPhysReg(unsigned PhysReg) const override; + bool isConstantPhysReg(MCRegister PhysReg) const override; const uint32_t *getNoPreservedMask() const override; + bool hasReservedSpillSlot(const MachineFunction &MF, Register Reg, + int &FrameIdx) const override; + void eliminateFrameIndex(MachineBasicBlock::iterator MI, int SPAdj, unsigned FIOperandNum, RegScavenger *RS = nullptr) const override; @@ -51,10 +54,6 @@ struct RISCVRegisterInfo : public RISCVGenRegisterInfo { return true; } - bool trackLivenessAfterRegAlloc(const MachineFunction &) const override { - return true; - } - const TargetRegisterClass * getPointerRegClass(const MachineFunction &MF, unsigned Kind = 0) const override { diff --git a/llvm/lib/Target/RISCV/RISCVRegisterInfo.td b/llvm/lib/Target/RISCV/RISCVRegisterInfo.td index 82b37afd0805..7544b4b3b845 100644 --- a/llvm/lib/Target/RISCV/RISCVRegisterInfo.td +++ b/llvm/lib/Target/RISCV/RISCVRegisterInfo.td @@ -33,7 +33,21 @@ class RISCVReg64<RISCVReg32 subreg> : Register<""> { let AltNames = subreg.AltNames; } +class RISCVRegWithSubRegs<bits<5> Enc, string n, list<Register> subregs, + list<string> alt = []> + : RegisterWithSubRegs<n, subregs> { + let HWEncoding{4-0} = Enc; + let AltNames = alt; +} + def ABIRegAltName : RegAltNameIndex; + +def sub_vrm2 : SubRegIndex<64, -1>; +def sub_vrm2_hi : SubRegIndex<64, -1>; +def sub_vrm4 : SubRegIndex<128, -1>; +def sub_vrm4_hi : SubRegIndex<128, -1>; +def sub_vrm8 : SubRegIndex<256, -1>; +def sub_vrm8_hi : SubRegIndex<256, -1>; } // Namespace = "RISCV" // Integer registers @@ -233,3 +247,88 @@ def FPR64C : RegisterClass<"RISCV", [f64], 64, (add (sequence "F%u_D", 10, 15), (sequence "F%u_D", 8, 9) )>; + +// Vector registers +let RegAltNameIndices = [ABIRegAltName] in { + foreach Index = 0-31 in { + def V#Index : RISCVReg<Index, "v"#Index, ["v"#Index]>, DwarfRegNum<[!add(Index, 64)]>; + } + + foreach Index = [0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, + 24, 26, 28, 30] in { + def V#Index#M2 : RISCVRegWithSubRegs<Index, "v"#Index, + [!cast<Register>("V"#Index), + !cast<Register>("V"#!add(Index, 1))], + ["v"#Index]>, + DwarfRegAlias<!cast<Register>("V"#Index)> { + let SubRegIndices = [sub_vrm2, sub_vrm2_hi]; + } + } + + foreach Index = [0, 4, 8, 12, 16, 20, 24, 28] in { + def V#Index#M4 : RISCVRegWithSubRegs<Index, "v"#Index, + [!cast<Register>("V"#Index#"M2"), + !cast<Register>("V"#!add(Index, 2)#"M2")], + ["v"#Index]>, + DwarfRegAlias<!cast<Register>("V"#Index)> { + let SubRegIndices = [sub_vrm4, sub_vrm4_hi]; + } + } + + foreach Index = [0, 8, 16, 24] in { + def V#Index#M8 : RISCVRegWithSubRegs<Index, "v"#Index, + [!cast<Register>("V"#Index#"M4"), + !cast<Register>("V"#!add(Index, 4)#"M4")], + ["v"#Index]>, + DwarfRegAlias<!cast<Register>("V"#Index)> { + let SubRegIndices = [sub_vrm8, sub_vrm8_hi]; + } + } + + def VTYPE : RISCVReg<0, "vtype", ["vtype"]>; + def VL : RISCVReg<0, "vl", ["vl"]>; +} + +class RegisterTypes<list<ValueType> reg_types> { + list<ValueType> types = reg_types; +} + +// The order of registers represents the preferred allocation sequence, +// meaning caller-save regs are listed before callee-save. +def VR : RegisterClass<"RISCV", [nxv8i8, nxv4i16, nxv2i32, nxv1i64], + 64, (add + (sequence "V%u", 25, 31), + (sequence "V%u", 8, 24), + (sequence "V%u", 0, 7) + )> { + let Size = 64; +} + +def VRM2 : RegisterClass<"RISCV", [nxv16i8, nxv8i16, nxv4i32, nxv2i64], 64, + (add V26M2, V28M2, V30M2, V8M2, V10M2, V12M2, V14M2, V16M2, + V18M2, V20M2, V22M2, V24M2, V0M2, V2M2, V4M2, V6M2)> { + let Size = 128; +} + +def VRM4 : RegisterClass<"RISCV", [nxv32i8, nxv16i16, nxv8i32, nxv4i64], 64, + (add V28M4, V8M4, V12M4, V16M4, V20M4, V24M4, V0M4, V4M4)> { + let Size = 256; +} + +def VRM8 : RegisterClass<"RISCV", [nxv32i16, nxv16i32, nxv8i64], 64, + (add V8M8, V16M8, V24M8, V0M8)> { + let Size = 512; +} + +def VMaskVT : RegisterTypes<[nxv1i1, nxv2i1, nxv4i1, nxv8i1, nxv16i1, nxv32i1]>; + +def VM : RegisterClass<"RISCV", VMaskVT.types, 64, (add + (sequence "V%u", 25, 31), + (sequence "V%u", 8, 24), + (sequence "V%u", 0, 7))> { + let Size = 64; +} + +def VMV0 : RegisterClass<"RISCV", VMaskVT.types, 64, (add V0)> { + let Size = 64; +} diff --git a/llvm/lib/Target/RISCV/RISCVSchedRocket32.td b/llvm/lib/Target/RISCV/RISCVSchedRocket32.td new file mode 100644 index 000000000000..305e2b9b5927 --- /dev/null +++ b/llvm/lib/Target/RISCV/RISCVSchedRocket32.td @@ -0,0 +1,227 @@ +//==- RISCVSchedRocket32.td - Rocket 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 +// +//===----------------------------------------------------------------------===// + +// ===---------------------------------------------------------------------===// +// The following definitions describe the simpler per-operand machine model. +// This works with MachineScheduler. See MCSchedule.h for details. + +// Rocket machine model for scheduling and other instruction cost heuristics. +def Rocket32Model : SchedMachineModel { + let MicroOpBufferSize = 0; // Explicitly set to zero since Rocket is in-order. + let IssueWidth = 1; // 1 micro-ops are dispatched per cycle. + let LoadLatency = 3; + let MispredictPenalty = 3; + let CompleteModel = 1; + let UnsupportedFeatures = [HasStdExtV]; +} + +//===----------------------------------------------------------------------===// +// Define each kind of processor resource and number available. + +// Modeling each pipeline as a ProcResource using the BufferSize = 0 since +// Rocket is in-order. + +let BufferSize = 0 in { +def Rocket32UnitALU : ProcResource<1>; // Int ALU +def Rocket32UnitIMul : ProcResource<1>; // Int Multiply +def Rocket32UnitMem : ProcResource<1>; // Load/Store +def Rocket32UnitB : ProcResource<1>; // Branch + +def Rocket32UnitFPALU : ProcResource<1>; // FP ALU +} + +let BufferSize = 1 in { +def Rocket32UnitIDiv : ProcResource<1>; // Int Division +def Rocket32UnitFPDivSqrt : ProcResource<1>; // FP Divide/Sqrt' +} + +//===----------------------------------------------------------------------===// +// Subtarget-specific SchedWrite types which both map the ProcResources and +// set the latency. + +let SchedModel = Rocket32Model in { + +def : WriteRes<WriteJmp, [Rocket32UnitB]>; +def : WriteRes<WriteJal, [Rocket32UnitB]>; +def : WriteRes<WriteJalr, [Rocket32UnitB]>; +def : WriteRes<WriteJmpReg, [Rocket32UnitB]>; + +def : WriteRes<WriteIALU, [Rocket32UnitALU]>; +def : WriteRes<WriteShift, [Rocket32UnitALU]>; + +// Multiplies on Rocket differ by implementation; placeholder until +// we can determine how to read from command line +def : WriteRes<WriteIMul, [Rocket32UnitIMul]> { let Latency = 4; } + +// 32-bit divides have worse case latency of 34 cycle +def : WriteRes<WriteIDiv, [Rocket32UnitIDiv]> { + let Latency = 34; + let ResourceCycles = [34]; +} + +// Memory +def : WriteRes<WriteSTB, [Rocket32UnitMem]>; +def : WriteRes<WriteSTH, [Rocket32UnitMem]>; +def : WriteRes<WriteSTW, [Rocket32UnitMem]>; +def : WriteRes<WriteFST32, [Rocket32UnitMem]>; +def : WriteRes<WriteFST64, [Rocket32UnitMem]>; + +let Latency = 3 in { +def : WriteRes<WriteLDB, [Rocket32UnitMem]>; +def : WriteRes<WriteLDH, [Rocket32UnitMem]>; +def : WriteRes<WriteCSR, [Rocket32UnitALU]>; +} + +let Latency = 2 in { +def : WriteRes<WriteLDW, [Rocket32UnitMem]>; +def : WriteRes<WriteFLD32, [Rocket32UnitMem]>; +def : WriteRes<WriteFLD64, [Rocket32UnitMem]>; + +def : WriteRes<WriteAtomicW, [Rocket32UnitMem]>; +def : WriteRes<WriteAtomicLDW, [Rocket32UnitMem]>; +} + +def : WriteRes<WriteAtomicSTW, [Rocket32UnitMem]>; + +// Most FP single precision operations are 4 cycles +let Latency = 4 in { +def : WriteRes<WriteFALU32, [Rocket32UnitFPALU]>; +def : WriteRes<WriteFSGNJ32, [Rocket32UnitFPALU]>; +def : WriteRes<WriteFMinMax32, [Rocket32UnitFPALU]>; +} + +// Most FP double precision operations are 6 cycles +let Latency = 6 in { +def : WriteRes<WriteFALU64, [Rocket32UnitFPALU]>; +def : WriteRes<WriteFSGNJ64, [Rocket32UnitFPALU]>; +def : WriteRes<WriteFMinMax64, [Rocket32UnitFPALU]>; +} + +let Latency = 2 in { +def : WriteRes<WriteFCvtI32ToF32, [Rocket32UnitFPALU]>; +def : WriteRes<WriteFCvtI32ToF64, [Rocket32UnitFPALU]>; +def : WriteRes<WriteFCvtF32ToI32, [Rocket32UnitFPALU]>; +def : WriteRes<WriteFCvtF64ToI32, [Rocket32UnitFPALU]>; +def : WriteRes<WriteFCvtF32ToF64, [Rocket32UnitFPALU]>; +def : WriteRes<WriteFCvtF64ToF32, [Rocket32UnitFPALU]>; + +def : WriteRes<WriteFClass32, [Rocket32UnitFPALU]>; +def : WriteRes<WriteFClass64, [Rocket32UnitFPALU]>; +def : WriteRes<WriteFCmp32, [Rocket32UnitFPALU]>; +def : WriteRes<WriteFCmp64, [Rocket32UnitFPALU]>; +def : WriteRes<WriteFMovF32ToI32, [Rocket32UnitFPALU]>; +def : WriteRes<WriteFMovI32ToF32, [Rocket32UnitFPALU]>; +} + +let Latency = 5 in { +def : WriteRes<WriteFMul32, [Rocket32UnitFPALU]>; +def : WriteRes<WriteFMulAdd32, [Rocket32UnitFPALU]>; +def : WriteRes<WriteFMulSub32, [Rocket32UnitFPALU]>; +} + +let Latency = 7 in { +def : WriteRes<WriteFMul64, [Rocket32UnitFPALU]>; +def : WriteRes<WriteFMulAdd64, [Rocket32UnitFPALU]>; +def : WriteRes<WriteFMulSub64, [Rocket32UnitFPALU]>; +} + +// FP Divide unit on Rocket is not pipelined, so set resource cycles to latency +let Latency = 20, ResourceCycles = [20] in { +def : WriteRes<WriteFDiv32, [Rocket32UnitFPDivSqrt]>; +def : WriteRes<WriteFDiv64, [Rocket32UnitFPDivSqrt]>; +} + +// FP Sqrt unit on Rocket is not pipelined, so set resource cycles to latency +def : WriteRes<WriteFSqrt32, [Rocket32UnitFPDivSqrt]> { let Latency = 20; + let ResourceCycles = [20];} +def : WriteRes<WriteFSqrt64, [Rocket32UnitFPDivSqrt]> { let Latency = 25; + let ResourceCycles = [25];} + +def : WriteRes<WriteNop, []>; + +def : InstRW<[WriteIALU], (instrs COPY)>; + +let Unsupported = 1 in { +def : WriteRes<WriteIALU32, []>; +def : WriteRes<WriteShift32, []>; +def : WriteRes<WriteIMul32, []>; +def : WriteRes<WriteIDiv32, []>; +def : WriteRes<WriteSTD, []>; +def : WriteRes<WriteLDWU, []>; +def : WriteRes<WriteLDD, []>; +def : WriteRes<WriteAtomicD, []>; +def : WriteRes<WriteAtomicLDD, []>; +def : WriteRes<WriteAtomicSTD, []>; +def : WriteRes<WriteFCvtI64ToF32, []>; +def : WriteRes<WriteFCvtI64ToF64, []>; +def : WriteRes<WriteFCvtF64ToI64, []>; +def : WriteRes<WriteFCvtF32ToI64, []>; +def : WriteRes<WriteFMovI64ToF64, []>; +def : WriteRes<WriteFMovF64ToI64, []>; +} + +//===----------------------------------------------------------------------===// +// Subtarget-specific SchedRead types with cycles. +// Dummy definitions for RocketCore. +def : ReadAdvance<ReadJmp, 0>; +def : ReadAdvance<ReadJalr, 0>; +def : ReadAdvance<ReadCSR, 0>; +def : ReadAdvance<ReadStoreData, 0>; +def : ReadAdvance<ReadMemBase, 0>; +def : ReadAdvance<ReadIALU, 0>; +def : ReadAdvance<ReadIALU32, 0>; +def : ReadAdvance<ReadShift, 0>; +def : ReadAdvance<ReadShift32, 0>; +def : ReadAdvance<ReadIDiv, 0>; +def : ReadAdvance<ReadIDiv32, 0>; +def : ReadAdvance<ReadIMul, 0>; +def : ReadAdvance<ReadIMul32, 0>; +def : ReadAdvance<ReadAtomicWA, 0>; +def : ReadAdvance<ReadAtomicWD, 0>; +def : ReadAdvance<ReadAtomicDA, 0>; +def : ReadAdvance<ReadAtomicDD, 0>; +def : ReadAdvance<ReadAtomicLDW, 0>; +def : ReadAdvance<ReadAtomicLDD, 0>; +def : ReadAdvance<ReadAtomicSTW, 0>; +def : ReadAdvance<ReadAtomicSTD, 0>; +def : ReadAdvance<ReadFMemBase, 0>; +def : ReadAdvance<ReadFALU32, 0>; +def : ReadAdvance<ReadFALU64, 0>; +def : ReadAdvance<ReadFMul32, 0>; +def : ReadAdvance<ReadFMulAdd32, 0>; +def : ReadAdvance<ReadFMulSub32, 0>; +def : ReadAdvance<ReadFMul64, 0>; +def : ReadAdvance<ReadFMulAdd64, 0>; +def : ReadAdvance<ReadFMulSub64, 0>; +def : ReadAdvance<ReadFDiv32, 0>; +def : ReadAdvance<ReadFDiv64, 0>; +def : ReadAdvance<ReadFSqrt32, 0>; +def : ReadAdvance<ReadFSqrt64, 0>; +def : ReadAdvance<ReadFCmp32, 0>; +def : ReadAdvance<ReadFCmp64, 0>; +def : ReadAdvance<ReadFSGNJ32, 0>; +def : ReadAdvance<ReadFSGNJ64, 0>; +def : ReadAdvance<ReadFMinMax32, 0>; +def : ReadAdvance<ReadFMinMax64, 0>; +def : ReadAdvance<ReadFCvtF32ToI32, 0>; +def : ReadAdvance<ReadFCvtF32ToI64, 0>; +def : ReadAdvance<ReadFCvtF64ToI32, 0>; +def : ReadAdvance<ReadFCvtF64ToI64, 0>; +def : ReadAdvance<ReadFCvtI32ToF32, 0>; +def : ReadAdvance<ReadFCvtI32ToF64, 0>; +def : ReadAdvance<ReadFCvtI64ToF32, 0>; +def : ReadAdvance<ReadFCvtI64ToF64, 0>; +def : ReadAdvance<ReadFCvtF32ToF64, 0>; +def : ReadAdvance<ReadFCvtF64ToF32, 0>; +def : ReadAdvance<ReadFMovF32ToI32, 0>; +def : ReadAdvance<ReadFMovI32ToF32, 0>; +def : ReadAdvance<ReadFMovF64ToI64, 0>; +def : ReadAdvance<ReadFMovI64ToF64, 0>; +def : ReadAdvance<ReadFClass32, 0>; +def : ReadAdvance<ReadFClass64, 0>; +} diff --git a/llvm/lib/Target/RISCV/RISCVSchedRocket64.td b/llvm/lib/Target/RISCV/RISCVSchedRocket64.td new file mode 100644 index 000000000000..e8514a275c45 --- /dev/null +++ b/llvm/lib/Target/RISCV/RISCVSchedRocket64.td @@ -0,0 +1,228 @@ +//==- RISCVSchedRocket64.td - Rocket 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 +// +//===----------------------------------------------------------------------===// + +// ===---------------------------------------------------------------------===// +// The following definitions describe the simpler per-operand machine model. +// This works with MachineScheduler. See MCSchedule.h for details. + +// Rocket machine model for scheduling and other instruction cost heuristics. +def Rocket64Model : SchedMachineModel { + let MicroOpBufferSize = 0; // Explicitly set to zero since Rocket is in-order. + let IssueWidth = 1; // 1 micro-ops are dispatched per cycle. + let LoadLatency = 3; + let MispredictPenalty = 3; + let UnsupportedFeatures = [HasStdExtV]; +} + +//===----------------------------------------------------------------------===// +// Define each kind of processor resource and number available. + +// Modeling each pipeline as a ProcResource using the BufferSize = 0 since +// Rocket is in-order. + +let BufferSize = 0 in { +def Rocket64UnitALU : ProcResource<1>; // Int ALU +def Rocket64UnitIMul : ProcResource<1>; // Int Multiply +def Rocket64UnitMem : ProcResource<1>; // Load/Store +def Rocket64UnitB : ProcResource<1>; // Branch + +def Rocket64UnitFPALU : ProcResource<1>; // FP ALU +} + +let BufferSize = 1 in { +def Rocket64UnitIDiv : ProcResource<1>; // Int Division +def Rocket64UnitFPDivSqrt : ProcResource<1>; // FP Divide/Sqrt +} + +//===----------------------------------------------------------------------===// +// Subtarget-specific SchedWrite types which both map the ProcResources and +// set the latency. + +let SchedModel = Rocket64Model in { + +def : WriteRes<WriteJmp, [Rocket64UnitB]>; +def : WriteRes<WriteJal, [Rocket64UnitB]>; +def : WriteRes<WriteJalr, [Rocket64UnitB]>; +def : WriteRes<WriteJmpReg, [Rocket64UnitB]>; + +def : WriteRes<WriteIALU32, [Rocket64UnitALU]>; +def : WriteRes<WriteIALU, [Rocket64UnitALU]>; +def : WriteRes<WriteShift32, [Rocket64UnitALU]>; +def : WriteRes<WriteShift, [Rocket64UnitALU]>; + +let Latency = 4 in { +def : WriteRes<WriteIMul, [Rocket64UnitIMul]>; +def : WriteRes<WriteIMul32, [Rocket64UnitIMul]>; +} + +// Integer divide varies based on operand magnitude and sign; worse case latency is 34. +def : WriteRes<WriteIDiv32, [Rocket64UnitIDiv]> { + let Latency = 34; + let ResourceCycles = [34]; +} +def : WriteRes<WriteIDiv, [Rocket64UnitIDiv]> { + let Latency = 33; + let ResourceCycles = [33]; +} + +// Memory +def : WriteRes<WriteSTB, [Rocket64UnitMem]>; +def : WriteRes<WriteSTH, [Rocket64UnitMem]>; +def : WriteRes<WriteSTW, [Rocket64UnitMem]>; +def : WriteRes<WriteSTD, [Rocket64UnitMem]>; +def : WriteRes<WriteFST32, [Rocket64UnitMem]>; +def : WriteRes<WriteFST64, [Rocket64UnitMem]>; + +let Latency = 3 in { +def : WriteRes<WriteLDB, [Rocket64UnitMem]>; +def : WriteRes<WriteLDH, [Rocket64UnitMem]>; +def : WriteRes<WriteCSR, [Rocket64UnitALU]>; +} + +let Latency = 2 in { +def : WriteRes<WriteLDW, [Rocket64UnitMem]>; +def : WriteRes<WriteLDWU, [Rocket64UnitMem]>; +def : WriteRes<WriteLDD, [Rocket64UnitMem]>; +def : WriteRes<WriteFLD32, [Rocket64UnitMem]>; +def : WriteRes<WriteFLD64, [Rocket64UnitMem]>; + +def : WriteRes<WriteAtomicW, [Rocket64UnitMem]>; +def : WriteRes<WriteAtomicD, [Rocket64UnitMem]>; + +def : WriteRes<WriteAtomicLDW, [Rocket64UnitMem]>; +def : WriteRes<WriteAtomicLDD, [Rocket64UnitMem]>; +} + +def : WriteRes<WriteAtomicSTW, [Rocket64UnitMem]>; +def : WriteRes<WriteAtomicSTD, [Rocket64UnitMem]>; + +// Most FP single precision operations are 4 cycles +let Latency = 4 in { +def : WriteRes<WriteFALU32, [Rocket64UnitFPALU]>; +def : WriteRes<WriteFSGNJ32, [Rocket64UnitFPALU]>; +def : WriteRes<WriteFMinMax32, [Rocket64UnitFPALU]>; +} + +let Latency = 6 in { +// Most FP double precision operations are 6 cycles +def : WriteRes<WriteFALU64, [Rocket64UnitFPALU]>; +def : WriteRes<WriteFSGNJ64, [Rocket64UnitFPALU]>; +def : WriteRes<WriteFMinMax64, [Rocket64UnitFPALU]>; +} + +// Conversion instructions +let Latency = 2 in { +def : WriteRes<WriteFCvtI32ToF32, [Rocket32UnitFPALU]>; +def : WriteRes<WriteFCvtI32ToF64, [Rocket32UnitFPALU]>; +def : WriteRes<WriteFCvtI64ToF32, [Rocket32UnitFPALU]>; +def : WriteRes<WriteFCvtI64ToF64, [Rocket32UnitFPALU]>; +def : WriteRes<WriteFCvtF32ToI32, [Rocket32UnitFPALU]>; +def : WriteRes<WriteFCvtF32ToI64, [Rocket32UnitFPALU]>; +def : WriteRes<WriteFCvtF64ToI32, [Rocket32UnitFPALU]>; +def : WriteRes<WriteFCvtF64ToI64, [Rocket32UnitFPALU]>; +def : WriteRes<WriteFCvtF32ToF64, [Rocket32UnitFPALU]>; +def : WriteRes<WriteFCvtF64ToF32, [Rocket32UnitFPALU]>; + +def : WriteRes<WriteFClass32, [Rocket64UnitFPALU]>; +def : WriteRes<WriteFClass64, [Rocket64UnitFPALU]>; +def : WriteRes<WriteFCmp32, [Rocket64UnitFPALU]>; +def : WriteRes<WriteFCmp64, [Rocket64UnitFPALU]>; +def : WriteRes<WriteFMovF32ToI32, [Rocket64UnitFPALU]>; +def : WriteRes<WriteFMovI32ToF32, [Rocket64UnitFPALU]>; +def : WriteRes<WriteFMovF64ToI64, [Rocket64UnitFPALU]>; +def : WriteRes<WriteFMovI64ToF64, [Rocket64UnitFPALU]>; +} + +let Latency = 5 in { +def : WriteRes<WriteFMul32, [Rocket64UnitFPALU]>; +def : WriteRes<WriteFMulAdd32, [Rocket64UnitFPALU]>; +def : WriteRes<WriteFMulSub32, [Rocket64UnitFPALU]>; +} + +let Latency = 7 in { +def : WriteRes<WriteFMul64, [Rocket64UnitFPALU]>; +def : WriteRes<WriteFMulAdd64, [Rocket64UnitFPALU]>; +def : WriteRes<WriteFMulSub64, [Rocket64UnitFPALU]>; +} + +// FP Divide unit on Rocket is not pipelined, so set resource cycles to latency +let Latency = 20, ResourceCycles = [20] in { +def : WriteRes<WriteFDiv32, [Rocket64UnitFPDivSqrt]>; +def : WriteRes<WriteFDiv64, [Rocket64UnitFPDivSqrt]>; +} + +// FP Sqrt unit on Rocket is not pipelined, so set resource cycles to latency +def : WriteRes<WriteFSqrt32, [Rocket64UnitFPDivSqrt]> { let Latency = 20; + let ResourceCycles = [20]; } +def : WriteRes<WriteFSqrt64, [Rocket64UnitFPDivSqrt]> { let Latency = 25; + let ResourceCycles = [25]; } + +def : WriteRes<WriteNop, []>; + +def : InstRW<[WriteIALU], (instrs COPY)>; + +//===----------------------------------------------------------------------===// +// Subtarget-specific SchedRead types with cycles. +// Dummy definitions for RocketCore. +def : ReadAdvance<ReadJmp, 0>; +def : ReadAdvance<ReadJalr, 0>; +def : ReadAdvance<ReadCSR, 0>; +def : ReadAdvance<ReadStoreData, 0>; +def : ReadAdvance<ReadMemBase, 0>; +def : ReadAdvance<ReadIALU, 0>; +def : ReadAdvance<ReadIALU32, 0>; +def : ReadAdvance<ReadShift, 0>; +def : ReadAdvance<ReadShift32, 0>; +def : ReadAdvance<ReadIDiv, 0>; +def : ReadAdvance<ReadIDiv32, 0>; +def : ReadAdvance<ReadIMul, 0>; +def : ReadAdvance<ReadIMul32, 0>; +def : ReadAdvance<ReadAtomicWA, 0>; +def : ReadAdvance<ReadAtomicWD, 0>; +def : ReadAdvance<ReadAtomicDA, 0>; +def : ReadAdvance<ReadAtomicDD, 0>; +def : ReadAdvance<ReadAtomicLDW, 0>; +def : ReadAdvance<ReadAtomicLDD, 0>; +def : ReadAdvance<ReadAtomicSTW, 0>; +def : ReadAdvance<ReadAtomicSTD, 0>; +def : ReadAdvance<ReadFMemBase, 0>; +def : ReadAdvance<ReadFALU32, 0>; +def : ReadAdvance<ReadFALU64, 0>; +def : ReadAdvance<ReadFMul32, 0>; +def : ReadAdvance<ReadFMulAdd32, 0>; +def : ReadAdvance<ReadFMulSub32, 0>; +def : ReadAdvance<ReadFMul64, 0>; +def : ReadAdvance<ReadFMulAdd64, 0>; +def : ReadAdvance<ReadFMulSub64, 0>; +def : ReadAdvance<ReadFDiv32, 0>; +def : ReadAdvance<ReadFDiv64, 0>; +def : ReadAdvance<ReadFSqrt32, 0>; +def : ReadAdvance<ReadFSqrt64, 0>; +def : ReadAdvance<ReadFCmp32, 0>; +def : ReadAdvance<ReadFCmp64, 0>; +def : ReadAdvance<ReadFSGNJ32, 0>; +def : ReadAdvance<ReadFSGNJ64, 0>; +def : ReadAdvance<ReadFMinMax32, 0>; +def : ReadAdvance<ReadFMinMax64, 0>; +def : ReadAdvance<ReadFCvtF32ToI32, 0>; +def : ReadAdvance<ReadFCvtF32ToI64, 0>; +def : ReadAdvance<ReadFCvtF64ToI32, 0>; +def : ReadAdvance<ReadFCvtF64ToI64, 0>; +def : ReadAdvance<ReadFCvtI32ToF32, 0>; +def : ReadAdvance<ReadFCvtI32ToF64, 0>; +def : ReadAdvance<ReadFCvtI64ToF32, 0>; +def : ReadAdvance<ReadFCvtI64ToF64, 0>; +def : ReadAdvance<ReadFCvtF32ToF64, 0>; +def : ReadAdvance<ReadFCvtF64ToF32, 0>; +def : ReadAdvance<ReadFMovF32ToI32, 0>; +def : ReadAdvance<ReadFMovI32ToF32, 0>; +def : ReadAdvance<ReadFMovF64ToI64, 0>; +def : ReadAdvance<ReadFMovI64ToF64, 0>; +def : ReadAdvance<ReadFClass32, 0>; +def : ReadAdvance<ReadFClass64, 0>; +} diff --git a/llvm/lib/Target/RISCV/RISCVSchedule.td b/llvm/lib/Target/RISCV/RISCVSchedule.td new file mode 100644 index 000000000000..bbcd03d46236 --- /dev/null +++ b/llvm/lib/Target/RISCV/RISCVSchedule.td @@ -0,0 +1,147 @@ +//===-- RISCVSchedule.td - RISCV 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 +// +//===----------------------------------------------------------------------===// + +/// Define scheduler resources associated with def operands. +def WriteIALU : SchedWrite; // 32 or 64-bit integer ALU operations +def WriteIALU32 : SchedWrite; // 32-bit integer ALU operations on RV64I +def WriteShift32 : SchedWrite; // 32-bit shift operations on RV64Ix +def WriteShift : SchedWrite; // 32 or 64-bit shift operations +def WriteIDiv : SchedWrite; // 32-bit or 64-bit divide and remainder +def WriteIDiv32 : SchedWrite; // 32-bit divide and remainder on RV64I +def WriteIMul : SchedWrite; // 32-bit or 64-bit multiply +def WriteIMul32 : SchedWrite; // 32-bit multiply on RV64I +def WriteJmp : SchedWrite; // Jump +def WriteJal : SchedWrite; // Jump and link +def WriteJalr : SchedWrite; // Jump and link register +def WriteJmpReg : SchedWrite; // Jump register +def WriteNop : SchedWrite; +def WriteLDB : SchedWrite; // Load byte +def WriteLDH : SchedWrite; // Load half-word +def WriteLDW : SchedWrite; // Load word +def WriteLDWU : SchedWrite; // Load word unsigned +def WriteLDD : SchedWrite; // Load double-word +def WriteCSR : SchedWrite; // CSR instructions +def WriteSTB : SchedWrite; // Store byte +def WriteSTH : SchedWrite; // Store half-word +def WriteSTW : SchedWrite; // Store word +def WriteSTD : SchedWrite; // Store double-word +def WriteAtomicW : SchedWrite; //Atomic memory operation word size +def WriteAtomicD : SchedWrite; //Atomic memory operation double word size +def WriteAtomicLDW : SchedWrite; // Atomic load word +def WriteAtomicLDD : SchedWrite; // Atomic load double word +def WriteAtomicSTW : SchedWrite; // Atomic store word +def WriteAtomicSTD : SchedWrite; // Atomic store double word +def WriteFALU32 : SchedWrite; // FP 32-bit computation +def WriteFALU64 : SchedWrite; // FP 64-bit computation +def WriteFMul32 : SchedWrite; // 32-bit floating point multiply +def WriteFMulAdd32 : SchedWrite; // 32-bit floating point multiply add +def WriteFMulSub32 : SchedWrite; // 32-bit floating point multiply sub +def WriteFMul64 : SchedWrite; // 64-bit floating point multiply +def WriteFMulAdd64 : SchedWrite; // 64-bit floating point multiply add +def WriteFMulSub64 : SchedWrite; // 64-bit floating point multiply sub +def WriteFDiv32 : SchedWrite; // 32-bit floating point divide +def WriteFDiv64 : SchedWrite; // 64-bit floating point divide +def WriteFSqrt32 : SchedWrite; // 32-bit floating point sqrt +def WriteFSqrt64 : SchedWrite; // 64-bit floating point sqrt + +// Integer to float conversions +def WriteFCvtI32ToF32 : SchedWrite; +def WriteFCvtI32ToF64 : SchedWrite; +def WriteFCvtI64ToF32 : SchedWrite; // RV64I only +def WriteFCvtI64ToF64 : SchedWrite; // RV64I only + +//Float to integer conversions +def WriteFCvtF32ToI32 : SchedWrite; +def WriteFCvtF32ToI64 : SchedWrite; // RV64I only +def WriteFCvtF64ToI32 : SchedWrite; +def WriteFCvtF64ToI64 : SchedWrite; // RV64I only + +// Float to float conversions +def WriteFCvtF32ToF64 : SchedWrite; +def WriteFCvtF64ToF32 : SchedWrite; + +def WriteFConv32 : SchedWrite; // 32-bit floating point convert +def WriteFConv64 : SchedWrite; // 64-bit floating point convert +def WriteFClass32 : SchedWrite; // 32-bit floating point classify +def WriteFClass64 : SchedWrite; // 64-bit floating point classify +def WriteFCmp32 : SchedWrite; // 32-bit floating point compare +def WriteFCmp64 : SchedWrite; // 64-bit floating point compare +def WriteFSGNJ32 : SchedWrite; // 32-bit floating point sign-injection +def WriteFSGNJ64 : SchedWrite; // 64-bit floating point sign-injection +def WriteFMinMax32 : SchedWrite; // 32-bit floating point min or max +def WriteFMinMax64 : SchedWrite; // 64-bit floating point min or max + +def WriteFMovF32ToI32 : SchedWrite; +def WriteFMovI32ToF32 : SchedWrite; +def WriteFMovF64ToI64 : SchedWrite; // RV64I only +def WriteFMovI64ToF64 : SchedWrite; // RV64I only + +def WriteFMov32 : SchedWrite; // 32-bit floating point move +def WriteFMov64 : SchedWrite; // 64-bit floating point move +def WriteFLD32 : SchedWrite; // Floating point sp load +def WriteFLD64 : SchedWrite; // Floating point dp load +def WriteFST32 : SchedWrite; // Floating point sp store +def WriteFST64 : SchedWrite; // Floating point dp store + +/// Define scheduler resources associated with use operands. +def ReadJmp : SchedRead; +def ReadJalr : SchedRead; +def ReadCSR : SchedRead; +def ReadMemBase : SchedRead; +def ReadFMemBase : SchedRead; +def ReadStoreData : SchedRead; +def ReadIALU : SchedRead; +def ReadIALU32 : SchedRead; // 32-bit integer ALU operations on RV64I +def ReadShift : SchedRead; +def ReadShift32 : SchedRead; // 32-bit shift operations on RV64Ix +def ReadIDiv : SchedRead; +def ReadIDiv32 : SchedRead; +def ReadIMul : SchedRead; +def ReadIMul32 : SchedRead; +def ReadAtomicWA : SchedRead; +def ReadAtomicWD : SchedRead; +def ReadAtomicDA : SchedRead; +def ReadAtomicDD : SchedRead; +def ReadAtomicLDW : SchedRead; // Atomic load word +def ReadAtomicLDD : SchedRead; // Atomic load double word +def ReadAtomicSTW : SchedRead; // Atomic store word +def ReadAtomicSTD : SchedRead; // Atomic store double word +def ReadFALU32 : SchedRead; // FP 32-bit computation +def ReadFALU64 : SchedRead; // FP 64-bit computation +def ReadFMul32 : SchedRead; // 32-bit floating point multiply +def ReadFMulAdd32 : SchedRead; // 32-bit floating point multiply add +def ReadFMulSub32 : SchedRead; // 32-bit floating point multiply sub +def ReadFMul64 : SchedRead; // 64-bit floating point multiply +def ReadFMulAdd64 : SchedRead; // 64-bit floating point multiply add +def ReadFMulSub64 : SchedRead; // 64-bit floating point multiply sub +def ReadFDiv32 : SchedRead; // 32-bit floating point divide +def ReadFDiv64 : SchedRead; // 64-bit floating point divide +def ReadFSqrt32 : SchedRead; // 32-bit floating point sqrt +def ReadFSqrt64 : SchedRead; // 64-bit floating point sqrt +def ReadFCmp32 : SchedRead; +def ReadFCmp64 : SchedRead; +def ReadFSGNJ32 : SchedRead; +def ReadFSGNJ64 : SchedRead; +def ReadFMinMax32 : SchedRead; +def ReadFMinMax64 : SchedRead; +def ReadFCvtF32ToI32 : SchedRead; +def ReadFCvtF32ToI64 : SchedRead; +def ReadFCvtF64ToI32 : SchedRead; +def ReadFCvtF64ToI64 : SchedRead; +def ReadFCvtI32ToF32 : SchedRead; +def ReadFCvtI32ToF64 : SchedRead; +def ReadFCvtI64ToF32 : SchedRead; +def ReadFCvtI64ToF64 : SchedRead; +def ReadFMovF32ToI32 : SchedRead; +def ReadFMovI32ToF32 : SchedRead; +def ReadFMovF64ToI64 : SchedRead; +def ReadFMovI64ToF64 : SchedRead; +def ReadFCvtF32ToF64 : SchedRead; +def ReadFCvtF64ToF32 : SchedRead; +def ReadFClass32 : SchedRead; +def ReadFClass64 : SchedRead; diff --git a/llvm/lib/Target/RISCV/RISCVSubtarget.cpp b/llvm/lib/Target/RISCV/RISCVSubtarget.cpp index 83e7e2d52cc1..47a48c820a29 100644 --- a/llvm/lib/Target/RISCV/RISCVSubtarget.cpp +++ b/llvm/lib/Target/RISCV/RISCVSubtarget.cpp @@ -33,7 +33,7 @@ RISCVSubtarget &RISCVSubtarget::initializeSubtargetDependencies( const Triple &TT, StringRef CPU, StringRef FS, StringRef ABIName) { // Determine default and user-specified characteristics bool Is64Bit = TT.isArch64Bit(); - std::string CPUName = CPU; + std::string CPUName = std::string(CPU); if (CPUName.empty()) CPUName = Is64Bit ? "generic-rv64" : "generic-rv32"; ParseSubtargetFeatures(CPUName, FS); diff --git a/llvm/lib/Target/RISCV/RISCVSubtarget.h b/llvm/lib/Target/RISCV/RISCVSubtarget.h index 605d4abcc9ae..fe1285f23b15 100644 --- a/llvm/lib/Target/RISCV/RISCVSubtarget.h +++ b/llvm/lib/Target/RISCV/RISCVSubtarget.h @@ -39,10 +39,23 @@ class RISCVSubtarget : public RISCVGenSubtargetInfo { bool HasStdExtF = false; bool HasStdExtD = false; bool HasStdExtC = false; + bool HasStdExtB = false; + bool HasStdExtZbb = false; + bool HasStdExtZbc = false; + bool HasStdExtZbe = false; + bool HasStdExtZbf = false; + bool HasStdExtZbm = false; + bool HasStdExtZbp = false; + bool HasStdExtZbr = false; + bool HasStdExtZbs = false; + bool HasStdExtZbt = false; + bool HasStdExtZbproposedc = false; + bool HasStdExtV = false; bool HasRV64 = false; bool IsRV32E = false; bool EnableLinkerRelax = false; - bool EnableRVCHintInstrs = false; + bool EnableRVCHintInstrs = true; + bool EnableSaveRestore = false; unsigned XLen = 32; MVT XLenVT = MVT::i32; RISCVABI::ABI TargetABI = RISCVABI::ABI_Unknown; @@ -87,10 +100,23 @@ public: bool hasStdExtF() const { return HasStdExtF; } bool hasStdExtD() const { return HasStdExtD; } bool hasStdExtC() const { return HasStdExtC; } + bool hasStdExtB() const { return HasStdExtB; } + bool hasStdExtZbb() const { return HasStdExtZbb; } + bool hasStdExtZbc() const { return HasStdExtZbc; } + bool hasStdExtZbe() const { return HasStdExtZbe; } + bool hasStdExtZbf() const { return HasStdExtZbf; } + bool hasStdExtZbm() const { return HasStdExtZbm; } + bool hasStdExtZbp() const { return HasStdExtZbp; } + bool hasStdExtZbr() const { return HasStdExtZbr; } + bool hasStdExtZbs() const { return HasStdExtZbs; } + bool hasStdExtZbt() const { return HasStdExtZbt; } + bool hasStdExtZbproposedc() const { return HasStdExtZbproposedc; } + bool hasStdExtV() const { return HasStdExtV; } bool is64Bit() const { return HasRV64; } bool isRV32E() const { return IsRV32E; } bool enableLinkerRelax() const { return EnableLinkerRelax; } bool enableRVCHintInstrs() const { return EnableRVCHintInstrs; } + bool enableSaveRestore() const { return EnableSaveRestore; } MVT getXLenVT() const { return XLenVT; } unsigned getXLen() const { return XLen; } RISCVABI::ABI getTargetABI() const { return TargetABI; } diff --git a/llvm/lib/Target/RISCV/RISCVSystemOperands.td b/llvm/lib/Target/RISCV/RISCVSystemOperands.td index a46a32c4e7f2..8e75647bd4a9 100644 --- a/llvm/lib/Target/RISCV/RISCVSystemOperands.td +++ b/llvm/lib/Target/RISCV/RISCVSystemOperands.td @@ -20,6 +20,8 @@ include "llvm/TableGen/SearchableTable.td" class SysReg<string name, bits<12> op> { string Name = name; bits<12> Encoding = op; + // A maximum of one alias is supported right now. + string AltName = name; // FIXME: add these additional fields when needed. // Privilege Access: Read and Write = 0, 1, 2; Read-Only = 3. // Privilege Mode: User = 0, System = 1 or Machine = 3. @@ -36,7 +38,7 @@ class SysReg<string name, bits<12> op> { def SysRegsList : GenericTable { let FilterClass = "SysReg"; // FIXME: add "ReadWrite", "Mode", "Extra", "Number" fields when needed. - let Fields = [ "Name", "Encoding", "FeaturesRequired", "isRV32Only" ]; + let Fields = [ "Name", "Encoding", "AltName", "FeaturesRequired", "isRV32Only" ]; let PrimaryKey = [ "Encoding" ]; let PrimaryKeyName = "lookupSysRegByEncoding"; @@ -47,6 +49,11 @@ def lookupSysRegByName : SearchIndex { let Key = [ "Name" ]; } +def lookupSysRegByAltName : SearchIndex { + let Table = SysRegsList; + let Key = [ "AltName" ]; +} + // The following CSR encodings match those given in Tables 2.2, // 2.3, 2.4 and 2.5 in the RISC-V Instruction Set Manual // Volume II: Privileged Architecture. @@ -303,6 +310,7 @@ def: SysReg<"mhpmcounter31h", 0xB9F>; //===-------------------------- // Machine Counter Setup //===-------------------------- +def : SysReg<"mcountinhibit", 0x320>; def : SysReg<"mhpmevent3", 0x323>; def : SysReg<"mhpmevent4", 0x324>; def : SysReg<"mhpmevent5", 0x325>; @@ -346,4 +354,19 @@ def : SysReg<"tdata3", 0x7A3>; //===----------------------------------------------- def : SysReg<"dcsr", 0x7B0>; def : SysReg<"dpc", 0x7B1>; -def : SysReg<"dscratch", 0x7B2>; + +// "dscratch" is an alternative name for "dscratch0" which appeared in earlier +// drafts of the RISC-V debug spec +let AltName = "dscratch" in +def : SysReg<"dscratch0", 0x7B2>; +def : SysReg<"dscratch1", 0x7B3>; + +//===----------------------------------------------- +// User Vector CSRs +//===----------------------------------------------- +def : SysReg<"vstart", 0x008>; +def : SysReg<"vxsat", 0x009>; +def : SysReg<"vxrm", 0x00A>; +def : SysReg<"vl", 0xC20>; +def : SysReg<"vtype", 0xC21>; +def : SysReg<"vlenb", 0xC22>; diff --git a/llvm/lib/Target/RISCV/RISCVTargetMachine.cpp b/llvm/lib/Target/RISCV/RISCVTargetMachine.cpp index 2bb26988c7da..75683e2fd8e9 100644 --- a/llvm/lib/Target/RISCV/RISCVTargetMachine.cpp +++ b/llvm/lib/Target/RISCV/RISCVTargetMachine.cpp @@ -15,6 +15,7 @@ #include "RISCVTargetObjectFile.h" #include "RISCVTargetTransformInfo.h" #include "TargetInfo/RISCVTargetInfo.h" +#include "Utils/RISCVBaseInfo.h" #include "llvm/ADT/STLExtras.h" #include "llvm/Analysis/TargetTransformInfo.h" #include "llvm/CodeGen/GlobalISel/IRTranslator.h" @@ -89,8 +90,17 @@ RISCVTargetMachine::getSubtargetImpl(const Function &F) const { // creation will depend on the TM and the code generation flags on the // function that reside in TargetOptions. resetTargetOptions(F); - I = std::make_unique<RISCVSubtarget>(TargetTriple, CPU, FS, - Options.MCOptions.getABIName(), *this); + auto ABIName = Options.MCOptions.getABIName(); + if (const MDString *ModuleTargetABI = dyn_cast_or_null<MDString>( + F.getParent()->getModuleFlag("target-abi"))) { + auto TargetABI = RISCVABI::getTargetABI(ABIName); + if (TargetABI != RISCVABI::ABI_Unknown && + ModuleTargetABI->getString() != ABIName) { + report_fatal_error("-target-abi option != target-abi module flag"); + } + ABIName = ModuleTargetABI->getString(); + } + I = std::make_unique<RISCVSubtarget>(TargetTriple, CPU, FS, ABIName, *this); } return I.get(); } @@ -118,6 +128,7 @@ public: bool addGlobalInstructionSelect() override; void addPreEmitPass() override; void addPreEmitPass2() override; + void addPreSched2() override; void addPreRegAlloc() override; }; } @@ -157,13 +168,16 @@ bool RISCVPassConfig::addGlobalInstructionSelect() { return false; } +void RISCVPassConfig::addPreSched2() {} + void RISCVPassConfig::addPreEmitPass() { addPass(&BranchRelaxationPassID); } void RISCVPassConfig::addPreEmitPass2() { + addPass(createRISCVExpandPseudoPass()); // Schedule the expansion of AMOs at the last possible moment, avoiding the // possibility for other passes to break the requirements for forward // progress in the LR/SC block. - addPass(createRISCVExpandPseudoPass()); + addPass(createRISCVExpandAtomicPseudoPass()); } void RISCVPassConfig::addPreRegAlloc() { diff --git a/llvm/lib/Target/RISCV/RISCVTargetObjectFile.cpp b/llvm/lib/Target/RISCV/RISCVTargetObjectFile.cpp index bbd45c970d3d..fba86b463764 100644 --- a/llvm/lib/Target/RISCV/RISCVTargetObjectFile.cpp +++ b/llvm/lib/Target/RISCV/RISCVTargetObjectFile.cpp @@ -17,7 +17,6 @@ using namespace llvm; void RISCVELFTargetObjectFile::Initialize(MCContext &Ctx, const TargetMachine &TM) { TargetLoweringObjectFileELF::Initialize(Ctx, TM); - InitializeELF(TM.Options.UseInitArray); SmallDataSection = getContext().getELFSection( ".sdata", ELF::SHT_PROGBITS, ELF::SHF_WRITE | ELF::SHF_ALLOC); @@ -105,10 +104,11 @@ bool RISCVELFTargetObjectFile::isConstantInSmallSection( MCSection *RISCVELFTargetObjectFile::getSectionForConstant( const DataLayout &DL, SectionKind Kind, const Constant *C, - unsigned &Align) const { + Align &Alignment) const { if (isConstantInSmallSection(DL, C)) return SmallDataSection; // Otherwise, we work the same as ELF. - return TargetLoweringObjectFileELF::getSectionForConstant(DL, Kind, C, Align); + return TargetLoweringObjectFileELF::getSectionForConstant(DL, Kind, C, + Alignment); } diff --git a/llvm/lib/Target/RISCV/RISCVTargetObjectFile.h b/llvm/lib/Target/RISCV/RISCVTargetObjectFile.h index b2daaaa9d364..830a7d813c15 100644 --- a/llvm/lib/Target/RISCV/RISCVTargetObjectFile.h +++ b/llvm/lib/Target/RISCV/RISCVTargetObjectFile.h @@ -12,7 +12,6 @@ #include "llvm/CodeGen/TargetLoweringObjectFileImpl.h" namespace llvm { -class RISCVTargetMachine; /// This implementation is used for RISCV ELF targets. class RISCVELFTargetObjectFile : public TargetLoweringObjectFileELF { @@ -36,7 +35,7 @@ public: MCSection *getSectionForConstant(const DataLayout &DL, SectionKind Kind, const Constant *C, - unsigned &Align) const override; + Align &Alignment) const override; void getModuleMetadata(Module &M) override; diff --git a/llvm/lib/Target/RISCV/RISCVTargetTransformInfo.cpp b/llvm/lib/Target/RISCV/RISCVTargetTransformInfo.cpp index 90fcd679c523..bd78f801c59a 100644 --- a/llvm/lib/Target/RISCV/RISCVTargetTransformInfo.cpp +++ b/llvm/lib/Target/RISCV/RISCVTargetTransformInfo.cpp @@ -15,7 +15,8 @@ using namespace llvm; #define DEBUG_TYPE "riscvtti" -int RISCVTTIImpl::getIntImmCost(const APInt &Imm, Type *Ty) { +int RISCVTTIImpl::getIntImmCost(const APInt &Imm, Type *Ty, + TTI::TargetCostKind CostKind) { assert(Ty->isIntegerTy() && "getIntImmCost can only estimate cost of materialising integers"); @@ -30,7 +31,7 @@ int RISCVTTIImpl::getIntImmCost(const APInt &Imm, Type *Ty) { } int RISCVTTIImpl::getIntImmCostInst(unsigned Opcode, unsigned Idx, const APInt &Imm, - Type *Ty) { + Type *Ty, TTI::TargetCostKind CostKind) { assert(Ty->isIntegerTy() && "getIntImmCost can only estimate cost of materialising integers"); @@ -78,7 +79,7 @@ int RISCVTTIImpl::getIntImmCostInst(unsigned Opcode, unsigned Idx, const APInt & } // Otherwise, use the full materialisation cost. - return getIntImmCost(Imm, Ty); + return getIntImmCost(Imm, Ty, CostKind); } // By default, prevent hoisting. @@ -86,7 +87,8 @@ int RISCVTTIImpl::getIntImmCostInst(unsigned Opcode, unsigned Idx, const APInt & } int RISCVTTIImpl::getIntImmCostIntrin(Intrinsic::ID IID, unsigned Idx, - const APInt &Imm, Type *Ty) { + const APInt &Imm, Type *Ty, + TTI::TargetCostKind CostKind) { // Prevent hoisting in unknown cases. return TTI::TCC_Free; } diff --git a/llvm/lib/Target/RISCV/RISCVTargetTransformInfo.h b/llvm/lib/Target/RISCV/RISCVTargetTransformInfo.h index d219ba81bb56..392700707760 100644 --- a/llvm/lib/Target/RISCV/RISCVTargetTransformInfo.h +++ b/llvm/lib/Target/RISCV/RISCVTargetTransformInfo.h @@ -41,12 +41,13 @@ public: : BaseT(TM, F.getParent()->getDataLayout()), ST(TM->getSubtargetImpl(F)), TLI(ST->getTargetLowering()) {} - int getIntImmCost(const APInt &Imm, Type *Ty); - int getIntImmCostInst(unsigned Opcode, unsigned Idx, const APInt &Imm, Type *Ty); + int getIntImmCost(const APInt &Imm, Type *Ty, TTI::TargetCostKind CostKind); + int getIntImmCostInst(unsigned Opcode, unsigned Idx, const APInt &Imm, Type *Ty, + TTI::TargetCostKind CostKind); int getIntImmCostIntrin(Intrinsic::ID IID, unsigned Idx, const APInt &Imm, - Type *Ty); + Type *Ty, TTI::TargetCostKind CostKind); }; } // end namespace llvm -#endif // LLVM_LIB_TARGET_RISCV_RISCVTARGETTRANSFORMINFO_H
\ No newline at end of file +#endif // LLVM_LIB_TARGET_RISCV_RISCVTARGETTRANSFORMINFO_H diff --git a/llvm/lib/Target/RISCV/Utils/RISCVBaseInfo.cpp b/llvm/lib/Target/RISCV/Utils/RISCVBaseInfo.cpp index 432ebb294d46..43b1f8b80c5f 100644 --- a/llvm/lib/Target/RISCV/Utils/RISCVBaseInfo.cpp +++ b/llvm/lib/Target/RISCV/Utils/RISCVBaseInfo.cpp @@ -12,16 +12,7 @@ namespace RISCVSysReg { namespace RISCVABI { ABI computeTargetABI(const Triple &TT, FeatureBitset FeatureBits, StringRef ABIName) { - auto TargetABI = StringSwitch<ABI>(ABIName) - .Case("ilp32", ABI_ILP32) - .Case("ilp32f", ABI_ILP32F) - .Case("ilp32d", ABI_ILP32D) - .Case("ilp32e", ABI_ILP32E) - .Case("lp64", ABI_LP64) - .Case("lp64f", ABI_LP64F) - .Case("lp64d", ABI_LP64D) - .Default(ABI_Unknown); - + auto TargetABI = getTargetABI(ABIName); bool IsRV64 = TT.isArch64Bit(); bool IsRV32E = FeatureBits[RISCV::FeatureRV32E]; @@ -58,6 +49,19 @@ ABI computeTargetABI(const Triple &TT, FeatureBitset FeatureBits, return ABI_ILP32; } +ABI getTargetABI(StringRef ABIName) { + auto TargetABI = StringSwitch<ABI>(ABIName) + .Case("ilp32", ABI_ILP32) + .Case("ilp32f", ABI_ILP32F) + .Case("ilp32d", ABI_ILP32D) + .Case("ilp32e", ABI_ILP32E) + .Case("lp64", ABI_LP64) + .Case("lp64f", ABI_LP64F) + .Case("lp64d", ABI_LP64D) + .Default(ABI_Unknown); + return TargetABI; +} + // To avoid the BP value clobbered by a function call, we need to choose a // callee saved register to save the value. RV32E only has X8 and X9 as callee // saved registers and X8 will be used as fp. So we choose X9 as bp. diff --git a/llvm/lib/Target/RISCV/Utils/RISCVBaseInfo.h b/llvm/lib/Target/RISCV/Utils/RISCVBaseInfo.h index cf078df9609a..4e6cdd8606b1 100644 --- a/llvm/lib/Target/RISCV/Utils/RISCVBaseInfo.h +++ b/llvm/lib/Target/RISCV/Utils/RISCVBaseInfo.h @@ -45,7 +45,7 @@ enum { InstFormatCJ = 16, InstFormatOther = 17, - InstFormatMask = 31 + InstFormatMask = 31, }; // RISC-V Specific Machine Operand Flags @@ -157,6 +157,7 @@ namespace RISCVSysReg { struct SysReg { const char *Name; unsigned Encoding; + const char *AltName; // FIXME: add these additional fields when needed. // Privilege Access: Read, Write, Read-Only. // unsigned ReadWrite; @@ -202,6 +203,8 @@ enum ABI { ABI computeTargetABI(const Triple &TT, FeatureBitset FeatureBits, StringRef ABIName); +ABI getTargetABI(StringRef ABIName); + // Returns the register used to hold the stack pointer after realignment. Register getBPReg(); |