diff options
Diffstat (limited to 'lib/Target/RISCV')
51 files changed, 6234 insertions, 151 deletions
diff --git a/lib/Target/RISCV/AsmParser/CMakeLists.txt b/lib/Target/RISCV/AsmParser/CMakeLists.txt new file mode 100644 index 000000000000..10d0c89537ce --- /dev/null +++ b/lib/Target/RISCV/AsmParser/CMakeLists.txt @@ -0,0 +1,3 @@ +add_llvm_library(LLVMRISCVAsmParser + RISCVAsmParser.cpp + ) diff --git a/lib/Target/RISCV/AsmParser/LLVMBuild.txt b/lib/Target/RISCV/AsmParser/LLVMBuild.txt new file mode 100644 index 000000000000..a9ad92c872ea --- /dev/null +++ b/lib/Target/RISCV/AsmParser/LLVMBuild.txt @@ -0,0 +1,23 @@ +;===- ./lib/Target/RISCV/AsmParser/LLVMBuild.txt ---------------*- Conf -*--===; +; +; The LLVM Compiler Infrastructure +; +; This file is distributed under the University of Illinois Open Source +; License. See LICENSE.TXT for details. +; +;===------------------------------------------------------------------------===; +; +; This is an LLVMBuild description file for the components in this subdirectory. +; +; For more information on the LLVMBuild system, please see: +; +; http://llvm.org/docs/LLVMBuild.html +; +;===------------------------------------------------------------------------===; + +[component_0] +type = Library +name = RISCVAsmParser +parent = RISCV +required_libraries = MC MCParser RISCVDesc RISCVInfo Support +add_to_library_groups = RISCV diff --git a/lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp b/lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp new file mode 100644 index 000000000000..3299a53ff5ba --- /dev/null +++ b/lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp @@ -0,0 +1,946 @@ +//===-- RISCVAsmParser.cpp - Parse RISCV assembly to MCInst instructions --===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "MCTargetDesc/RISCVBaseInfo.h" +#include "MCTargetDesc/RISCVMCExpr.h" +#include "MCTargetDesc/RISCVMCTargetDesc.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/StringSwitch.h" +#include "llvm/MC/MCContext.h" +#include "llvm/MC/MCExpr.h" +#include "llvm/MC/MCInst.h" +#include "llvm/MC/MCParser/MCAsmLexer.h" +#include "llvm/MC/MCParser/MCParsedAsmOperand.h" +#include "llvm/MC/MCParser/MCTargetAsmParser.h" +#include "llvm/MC/MCRegisterInfo.h" +#include "llvm/MC/MCStreamer.h" +#include "llvm/MC/MCSubtargetInfo.h" +#include "llvm/Support/Casting.h" +#include "llvm/Support/TargetRegistry.h" + +using namespace llvm; + +namespace { +struct RISCVOperand; + +class RISCVAsmParser : public MCTargetAsmParser { + SMLoc getLoc() const { return getParser().getTok().getLoc(); } + bool isRV64() const { return getSTI().hasFeature(RISCV::Feature64Bit); } + + unsigned validateTargetOperandClass(MCParsedAsmOperand &Op, + unsigned Kind) override; + + bool generateImmOutOfRangeError(OperandVector &Operands, uint64_t ErrorInfo, + int Lower, int Upper, Twine Msg); + + bool MatchAndEmitInstruction(SMLoc IDLoc, unsigned &Opcode, + OperandVector &Operands, MCStreamer &Out, + uint64_t &ErrorInfo, + bool MatchingInlineAsm) override; + + bool ParseRegister(unsigned &RegNo, SMLoc &StartLoc, SMLoc &EndLoc) override; + + bool ParseInstruction(ParseInstructionInfo &Info, StringRef Name, + SMLoc NameLoc, OperandVector &Operands) override; + + bool ParseDirective(AsmToken DirectiveID) override; + +// Auto-generated instruction matching functions +#define GET_ASSEMBLER_HEADER +#include "RISCVGenAsmMatcher.inc" + + OperandMatchResultTy parseImmediate(OperandVector &Operands); + OperandMatchResultTy parseRegister(OperandVector &Operands, + bool AllowParens = false); + OperandMatchResultTy parseMemOpBaseReg(OperandVector &Operands); + OperandMatchResultTy parseOperandWithModifier(OperandVector &Operands); + + bool parseOperand(OperandVector &Operands); + +public: + enum RISCVMatchResultTy { + Match_Dummy = FIRST_TARGET_MATCH_RESULT_TY, +#define GET_OPERAND_DIAGNOSTIC_TYPES +#include "RISCVGenAsmMatcher.inc" +#undef GET_OPERAND_DIAGNOSTIC_TYPES + }; + + static bool classifySymbolRef(const MCExpr *Expr, + RISCVMCExpr::VariantKind &Kind, + int64_t &Addend); + + RISCVAsmParser(const MCSubtargetInfo &STI, MCAsmParser &Parser, + const MCInstrInfo &MII, const MCTargetOptions &Options) + : MCTargetAsmParser(Options, STI, MII) { + setAvailableFeatures(ComputeAvailableFeatures(STI.getFeatureBits())); + } +}; + +/// RISCVOperand - Instances of this class represent a parsed machine +/// instruction +struct RISCVOperand : public MCParsedAsmOperand { + + enum KindTy { + Token, + Register, + Immediate, + } Kind; + + bool IsRV64; + + struct RegOp { + unsigned RegNum; + }; + + struct ImmOp { + const MCExpr *Val; + }; + + SMLoc StartLoc, EndLoc; + union { + StringRef Tok; + RegOp Reg; + ImmOp Imm; + }; + + RISCVOperand(KindTy K) : MCParsedAsmOperand(), Kind(K) {} + +public: + RISCVOperand(const RISCVOperand &o) : MCParsedAsmOperand() { + Kind = o.Kind; + IsRV64 = o.IsRV64; + StartLoc = o.StartLoc; + EndLoc = o.EndLoc; + switch (Kind) { + case Register: + Reg = o.Reg; + break; + case Immediate: + Imm = o.Imm; + break; + case Token: + Tok = o.Tok; + break; + } + } + + bool isToken() const override { return Kind == Token; } + bool isReg() const override { return Kind == Register; } + bool isImm() const override { return Kind == Immediate; } + bool isMem() const override { return false; } + + bool evaluateConstantImm(int64_t &Imm, RISCVMCExpr::VariantKind &VK) const { + const MCExpr *Val = getImm(); + bool Ret = false; + if (auto *RE = dyn_cast<RISCVMCExpr>(Val)) { + Ret = RE->evaluateAsConstant(Imm); + VK = RE->getKind(); + } else if (auto CE = dyn_cast<MCConstantExpr>(Val)) { + Ret = true; + VK = RISCVMCExpr::VK_RISCV_None; + Imm = CE->getValue(); + } + return Ret; + } + + // True if operand is a symbol with no modifiers, or a constant with no + // modifiers and isShiftedInt<N-1, 1>(Op). + template <int N> bool isBareSimmNLsb0() const { + int64_t Imm; + RISCVMCExpr::VariantKind VK; + if (!isImm()) + return false; + bool IsConstantImm = evaluateConstantImm(Imm, VK); + bool IsValid; + if (!IsConstantImm) + IsValid = RISCVAsmParser::classifySymbolRef(getImm(), VK, Imm); + else + IsValid = isShiftedInt<N - 1, 1>(Imm); + return IsValid && VK == RISCVMCExpr::VK_RISCV_None; + } + + // Predicate methods for AsmOperands defined in RISCVInstrInfo.td + + /// Return true if the operand is a valid for the fence instruction e.g. + /// ('iorw'). + bool isFenceArg() const { + if (!isImm()) + return false; + const MCExpr *Val = getImm(); + auto *SVal = dyn_cast<MCSymbolRefExpr>(Val); + if (!SVal || SVal->getKind() != MCSymbolRefExpr::VK_None) + return false; + + StringRef Str = SVal->getSymbol().getName(); + // Letters must be unique, taken from 'iorw', and in ascending order. This + // holds as long as each individual character is one of 'iorw' and is + // greater than the previous character. + char Prev = '\0'; + for (char c : Str) { + if (c != 'i' && c != 'o' && c != 'r' && c != 'w') + return false; + if (c <= Prev) + return false; + Prev = c; + } + return true; + } + + /// Return true if the operand is a valid floating point rounding mode. + bool isFRMArg() const { + if (!isImm()) + return false; + const MCExpr *Val = getImm(); + auto *SVal = dyn_cast<MCSymbolRefExpr>(Val); + if (!SVal || SVal->getKind() != MCSymbolRefExpr::VK_None) + return false; + + StringRef Str = SVal->getSymbol().getName(); + + return RISCVFPRndMode::stringToRoundingMode(Str) != RISCVFPRndMode::Invalid; + } + + bool isUImmLog2XLen() const { + int64_t Imm; + RISCVMCExpr::VariantKind VK; + if (!isImm()) + return false; + if (!evaluateConstantImm(Imm, VK) || VK != RISCVMCExpr::VK_RISCV_None) + return false; + return (isRV64() && isUInt<6>(Imm)) || isUInt<5>(Imm); + } + + bool isUImmLog2XLenNonZero() const { + int64_t Imm; + RISCVMCExpr::VariantKind VK; + if (!isImm()) + return false; + if (!evaluateConstantImm(Imm, VK) || VK != RISCVMCExpr::VK_RISCV_None) + return false; + if (Imm == 0) + return false; + return (isRV64() && isUInt<6>(Imm)) || isUInt<5>(Imm); + } + + bool isUImm5() const { + int64_t Imm; + RISCVMCExpr::VariantKind VK; + if (!isImm()) + return false; + bool IsConstantImm = evaluateConstantImm(Imm, VK); + return IsConstantImm && isUInt<5>(Imm) && VK == RISCVMCExpr::VK_RISCV_None; + } + + bool isUImm5NonZero() const { + int64_t Imm; + RISCVMCExpr::VariantKind VK; + if (!isImm()) + return false; + bool IsConstantImm = evaluateConstantImm(Imm, VK); + return IsConstantImm && isUInt<5>(Imm) && (Imm != 0) && + VK == RISCVMCExpr::VK_RISCV_None; + } + + bool isSImm6() const { + RISCVMCExpr::VariantKind VK; + int64_t Imm; + bool IsValid; + bool IsConstantImm = evaluateConstantImm(Imm, VK); + if (!IsConstantImm) + IsValid = RISCVAsmParser::classifySymbolRef(getImm(), VK, Imm); + else + IsValid = isInt<6>(Imm); + return IsValid && + (VK == RISCVMCExpr::VK_RISCV_None || VK == RISCVMCExpr::VK_RISCV_LO); + } + + bool isUImm6NonZero() const { + int64_t Imm; + RISCVMCExpr::VariantKind VK; + bool IsConstantImm = evaluateConstantImm(Imm, VK); + return IsConstantImm && isUInt<6>(Imm) && (Imm != 0) && + VK == RISCVMCExpr::VK_RISCV_None; + } + + bool isUImm7Lsb00() const { + int64_t Imm; + RISCVMCExpr::VariantKind VK; + bool IsConstantImm = evaluateConstantImm(Imm, VK); + return IsConstantImm && isShiftedUInt<5, 2>(Imm) && + VK == RISCVMCExpr::VK_RISCV_None; + } + + bool isUImm8Lsb00() const { + int64_t Imm; + RISCVMCExpr::VariantKind VK; + bool IsConstantImm = evaluateConstantImm(Imm, VK); + return IsConstantImm && isShiftedUInt<6, 2>(Imm) && + VK == RISCVMCExpr::VK_RISCV_None; + } + + bool isUImm8Lsb000() const { + int64_t Imm; + RISCVMCExpr::VariantKind VK; + bool IsConstantImm = evaluateConstantImm(Imm, VK); + return IsConstantImm && isShiftedUInt<5, 3>(Imm) && + VK == RISCVMCExpr::VK_RISCV_None; + } + + bool isSImm9Lsb0() const { return isBareSimmNLsb0<9>(); } + + bool isUImm9Lsb000() const { + int64_t Imm; + RISCVMCExpr::VariantKind VK; + bool IsConstantImm = evaluateConstantImm(Imm, VK); + return IsConstantImm && isShiftedUInt<6, 3>(Imm) && + VK == RISCVMCExpr::VK_RISCV_None; + } + + bool isUImm10Lsb00NonZero() const { + int64_t Imm; + RISCVMCExpr::VariantKind VK; + bool IsConstantImm = evaluateConstantImm(Imm, VK); + return IsConstantImm && isShiftedUInt<8, 2>(Imm) && (Imm != 0) && + VK == RISCVMCExpr::VK_RISCV_None; + } + + bool isSImm12() const { + RISCVMCExpr::VariantKind VK; + int64_t Imm; + bool IsValid; + if (!isImm()) + return false; + bool IsConstantImm = evaluateConstantImm(Imm, VK); + if (!IsConstantImm) + IsValid = RISCVAsmParser::classifySymbolRef(getImm(), VK, Imm); + else + IsValid = isInt<12>(Imm); + return IsValid && + (VK == RISCVMCExpr::VK_RISCV_None || VK == RISCVMCExpr::VK_RISCV_LO); + } + + bool isSImm12Lsb0() const { return isBareSimmNLsb0<12>(); } + + bool isUImm12() const { + int64_t Imm; + RISCVMCExpr::VariantKind VK; + if (!isImm()) + return false; + bool IsConstantImm = evaluateConstantImm(Imm, VK); + return IsConstantImm && isUInt<12>(Imm) && VK == RISCVMCExpr::VK_RISCV_None; + } + + bool isSImm13Lsb0() const { return isBareSimmNLsb0<13>(); } + + bool isSImm10Lsb0000() const { + int64_t Imm; + RISCVMCExpr::VariantKind VK; + bool IsConstantImm = evaluateConstantImm(Imm, VK); + return IsConstantImm && isShiftedInt<6, 4>(Imm) && + VK == RISCVMCExpr::VK_RISCV_None; + } + + bool isUImm20() const { + RISCVMCExpr::VariantKind VK; + int64_t Imm; + bool IsValid; + if (!isImm()) + return false; + bool IsConstantImm = evaluateConstantImm(Imm, VK); + if (!IsConstantImm) + IsValid = RISCVAsmParser::classifySymbolRef(getImm(), VK, Imm); + else + IsValid = isUInt<20>(Imm); + return IsValid && (VK == RISCVMCExpr::VK_RISCV_None || + VK == RISCVMCExpr::VK_RISCV_HI || + VK == RISCVMCExpr::VK_RISCV_PCREL_HI); + } + + bool isSImm21Lsb0() const { return isBareSimmNLsb0<21>(); } + + /// 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 + SMLoc getEndLoc() const override { return EndLoc; } + /// True if this operand is for an RV64 instruction + bool isRV64() const { return IsRV64; } + + unsigned getReg() const override { + assert(Kind == Register && "Invalid type access!"); + return Reg.RegNum; + } + + const MCExpr *getImm() const { + assert(Kind == Immediate && "Invalid type access!"); + return Imm.Val; + } + + StringRef getToken() const { + assert(Kind == Token && "Invalid type access!"); + return Tok; + } + + void print(raw_ostream &OS) const override { + switch (Kind) { + case Immediate: + OS << *getImm(); + break; + case Register: + OS << "<register x"; + OS << getReg() << ">"; + break; + case Token: + OS << "'" << getToken() << "'"; + break; + } + } + + static std::unique_ptr<RISCVOperand> createToken(StringRef Str, SMLoc S, + bool IsRV64) { + auto Op = make_unique<RISCVOperand>(Token); + Op->Tok = Str; + Op->StartLoc = S; + Op->EndLoc = S; + Op->IsRV64 = IsRV64; + return Op; + } + + static std::unique_ptr<RISCVOperand> createReg(unsigned RegNo, SMLoc S, + SMLoc E, bool IsRV64) { + auto Op = make_unique<RISCVOperand>(Register); + Op->Reg.RegNum = RegNo; + Op->StartLoc = S; + Op->EndLoc = E; + Op->IsRV64 = IsRV64; + return Op; + } + + static std::unique_ptr<RISCVOperand> createImm(const MCExpr *Val, SMLoc S, + SMLoc E, bool IsRV64) { + auto Op = make_unique<RISCVOperand>(Immediate); + Op->Imm.Val = Val; + Op->StartLoc = S; + Op->EndLoc = E; + Op->IsRV64 = IsRV64; + return Op; + } + + void addExpr(MCInst &Inst, const MCExpr *Expr) const { + assert(Expr && "Expr shouldn't be null!"); + int64_t Imm = 0; + bool IsConstant = false; + if (auto *RE = dyn_cast<RISCVMCExpr>(Expr)) { + IsConstant = RE->evaluateAsConstant(Imm); + } else if (auto *CE = dyn_cast<MCConstantExpr>(Expr)) { + IsConstant = true; + Imm = CE->getValue(); + } + + if (IsConstant) + Inst.addOperand(MCOperand::createImm(Imm)); + else + Inst.addOperand(MCOperand::createExpr(Expr)); + } + + // Used by the TableGen Code + void addRegOperands(MCInst &Inst, unsigned N) const { + assert(N == 1 && "Invalid number of operands!"); + Inst.addOperand(MCOperand::createReg(getReg())); + } + + void addImmOperands(MCInst &Inst, unsigned N) const { + assert(N == 1 && "Invalid number of operands!"); + addExpr(Inst, getImm()); + } + + void addFenceArgOperands(MCInst &Inst, unsigned N) const { + assert(N == 1 && "Invalid number of operands!"); + // isFenceArg has validated the operand, meaning this cast is safe + auto SE = cast<MCSymbolRefExpr>(getImm()); + + unsigned Imm = 0; + for (char c : SE->getSymbol().getName()) { + switch (c) { + default: llvm_unreachable("FenceArg must contain only [iorw]"); + case 'i': Imm |= RISCVFenceField::I; break; + case 'o': Imm |= RISCVFenceField::O; break; + case 'r': Imm |= RISCVFenceField::R; break; + case 'w': Imm |= RISCVFenceField::W; break; + } + } + Inst.addOperand(MCOperand::createImm(Imm)); + } + + // Returns the rounding mode represented by this RISCVOperand. Should only + // be called after checking isFRMArg. + RISCVFPRndMode::RoundingMode getRoundingMode() const { + // isFRMArg has validated the operand, meaning this cast is safe. + auto SE = cast<MCSymbolRefExpr>(getImm()); + RISCVFPRndMode::RoundingMode FRM = + RISCVFPRndMode::stringToRoundingMode(SE->getSymbol().getName()); + assert(FRM != RISCVFPRndMode::Invalid && "Invalid rounding mode"); + return FRM; + } + + void addFRMArgOperands(MCInst &Inst, unsigned N) const { + assert(N == 1 && "Invalid number of operands!"); + Inst.addOperand(MCOperand::createImm(getRoundingMode())); + } +}; +} // end anonymous namespace. + +#define GET_REGISTER_MATCHER +#define GET_MATCHER_IMPLEMENTATION +#include "RISCVGenAsmMatcher.inc" + +// Return the matching FPR64 register for the given FPR32. +// FIXME: Ideally this function could be removed in favour of using +// information from TableGen. +unsigned convertFPR32ToFPR64(unsigned Reg) { + switch (Reg) { + default: + llvm_unreachable("Not a recognised FPR32 register"); + case RISCV::F0_32: return RISCV::F0_64; + case RISCV::F1_32: return RISCV::F1_64; + case RISCV::F2_32: return RISCV::F2_64; + case RISCV::F3_32: return RISCV::F3_64; + case RISCV::F4_32: return RISCV::F4_64; + case RISCV::F5_32: return RISCV::F5_64; + case RISCV::F6_32: return RISCV::F6_64; + case RISCV::F7_32: return RISCV::F7_64; + case RISCV::F8_32: return RISCV::F8_64; + case RISCV::F9_32: return RISCV::F9_64; + case RISCV::F10_32: return RISCV::F10_64; + case RISCV::F11_32: return RISCV::F11_64; + case RISCV::F12_32: return RISCV::F12_64; + case RISCV::F13_32: return RISCV::F13_64; + case RISCV::F14_32: return RISCV::F14_64; + case RISCV::F15_32: return RISCV::F15_64; + case RISCV::F16_32: return RISCV::F16_64; + case RISCV::F17_32: return RISCV::F17_64; + case RISCV::F18_32: return RISCV::F18_64; + case RISCV::F19_32: return RISCV::F19_64; + case RISCV::F20_32: return RISCV::F20_64; + case RISCV::F21_32: return RISCV::F21_64; + case RISCV::F22_32: return RISCV::F22_64; + case RISCV::F23_32: return RISCV::F23_64; + case RISCV::F24_32: return RISCV::F24_64; + case RISCV::F25_32: return RISCV::F25_64; + case RISCV::F26_32: return RISCV::F26_64; + case RISCV::F27_32: return RISCV::F27_64; + case RISCV::F28_32: return RISCV::F28_64; + case RISCV::F29_32: return RISCV::F29_64; + case RISCV::F30_32: return RISCV::F30_64; + case RISCV::F31_32: return RISCV::F31_64; + } +} + +unsigned RISCVAsmParser::validateTargetOperandClass(MCParsedAsmOperand &AsmOp, + unsigned Kind) { + RISCVOperand &Op = static_cast<RISCVOperand &>(AsmOp); + if (!Op.isReg()) + return Match_InvalidOperand; + + unsigned Reg = Op.getReg(); + bool IsRegFPR32 = + RISCVMCRegisterClasses[RISCV::FPR32RegClassID].contains(Reg); + bool IsRegFPR32C = + RISCVMCRegisterClasses[RISCV::FPR32CRegClassID].contains(Reg); + + // As the parser couldn't differentiate an FPR32 from an FPR64, coerce the + // register from FPR32 to FPR64 or FPR32C to FPR64C if necessary. + if ((IsRegFPR32 && Kind == MCK_FPR64) || + (IsRegFPR32C && Kind == MCK_FPR64C)) { + Op.Reg.RegNum = convertFPR32ToFPR64(Reg); + return Match_Success; + } + return Match_InvalidOperand; +} + +bool RISCVAsmParser::generateImmOutOfRangeError( + OperandVector &Operands, uint64_t ErrorInfo, int Lower, int Upper, + Twine Msg = "immediate must be an integer in the range") { + SMLoc ErrorLoc = ((RISCVOperand &)*Operands[ErrorInfo]).getStartLoc(); + return Error(ErrorLoc, Msg + " [" + Twine(Lower) + ", " + Twine(Upper) + "]"); +} + +bool RISCVAsmParser::MatchAndEmitInstruction(SMLoc IDLoc, unsigned &Opcode, + OperandVector &Operands, + MCStreamer &Out, + uint64_t &ErrorInfo, + bool MatchingInlineAsm) { + MCInst Inst; + + switch (MatchInstructionImpl(Operands, Inst, ErrorInfo, MatchingInlineAsm)) { + default: + break; + case Match_Success: + Inst.setLoc(IDLoc); + Out.EmitInstruction(Inst, getSTI()); + return false; + case Match_MissingFeature: + return Error(IDLoc, "instruction use requires an option to be enabled"); + case Match_MnemonicFail: + return Error(IDLoc, "unrecognized instruction mnemonic"); + case Match_InvalidOperand: { + SMLoc ErrorLoc = IDLoc; + if (ErrorInfo != ~0U) { + if (ErrorInfo >= Operands.size()) + return Error(ErrorLoc, "too few operands for instruction"); + + ErrorLoc = ((RISCVOperand &)*Operands[ErrorInfo]).getStartLoc(); + if (ErrorLoc == SMLoc()) + ErrorLoc = IDLoc; + } + return Error(ErrorLoc, "invalid operand for instruction"); + } + case Match_InvalidUImmLog2XLen: + if (isRV64()) + return generateImmOutOfRangeError(Operands, ErrorInfo, 0, (1 << 6) - 1); + return generateImmOutOfRangeError(Operands, ErrorInfo, 0, (1 << 5) - 1); + case Match_InvalidUImmLog2XLenNonZero: + if (isRV64()) + return generateImmOutOfRangeError(Operands, ErrorInfo, 1, (1 << 6) - 1); + return generateImmOutOfRangeError(Operands, ErrorInfo, 1, (1 << 5) - 1); + case Match_InvalidUImm5: + return generateImmOutOfRangeError(Operands, ErrorInfo, 0, (1 << 5) - 1); + case Match_InvalidSImm6: + return generateImmOutOfRangeError(Operands, ErrorInfo, -(1 << 5), + (1 << 5) - 1); + case Match_InvalidUImm6NonZero: + return generateImmOutOfRangeError(Operands, ErrorInfo, 1, (1 << 6) - 1); + case Match_InvalidUImm7Lsb00: + return generateImmOutOfRangeError( + Operands, ErrorInfo, 0, (1 << 7) - 4, + "immediate must be a multiple of 4 bytes in the range"); + case Match_InvalidUImm8Lsb00: + return generateImmOutOfRangeError( + Operands, ErrorInfo, 0, (1 << 8) - 4, + "immediate must be a multiple of 4 bytes in the range"); + case Match_InvalidUImm8Lsb000: + return generateImmOutOfRangeError( + Operands, ErrorInfo, 0, (1 << 8) - 8, + "immediate must be a multiple of 8 bytes in the range"); + case Match_InvalidSImm9Lsb0: + return generateImmOutOfRangeError( + Operands, ErrorInfo, -(1 << 8), (1 << 8) - 2, + "immediate must be a multiple of 2 bytes in the range"); + case Match_InvalidUImm9Lsb000: + return generateImmOutOfRangeError( + Operands, ErrorInfo, 0, (1 << 9) - 8, + "immediate must be a multiple of 8 bytes in the range"); + case Match_InvalidUImm10Lsb00NonZero: + return generateImmOutOfRangeError( + Operands, ErrorInfo, 4, (1 << 10) - 4, + "immediate must be a multiple of 4 bytes in the range"); + case Match_InvalidSImm10Lsb0000: + return generateImmOutOfRangeError( + Operands, ErrorInfo, -(1 << 9), (1 << 9) - 16, + "immediate must be a multiple of 16 bytes in the range"); + case Match_InvalidSImm12: + return generateImmOutOfRangeError(Operands, ErrorInfo, -(1 << 11), + (1 << 11) - 1); + case Match_InvalidSImm12Lsb0: + return generateImmOutOfRangeError( + Operands, ErrorInfo, -(1 << 11), (1 << 11) - 2, + "immediate must be a multiple of 2 bytes in the range"); + case Match_InvalidUImm12: + return generateImmOutOfRangeError(Operands, ErrorInfo, 0, (1 << 12) - 1); + case Match_InvalidSImm13Lsb0: + return generateImmOutOfRangeError( + Operands, ErrorInfo, -(1 << 12), (1 << 12) - 2, + "immediate must be a multiple of 2 bytes in the range"); + case Match_InvalidUImm20: + return generateImmOutOfRangeError(Operands, ErrorInfo, 0, (1 << 20) - 1); + case Match_InvalidSImm21Lsb0: + return generateImmOutOfRangeError( + Operands, ErrorInfo, -(1 << 20), (1 << 20) - 2, + "immediate must be a multiple of 2 bytes in the range"); + case Match_InvalidFenceArg: { + SMLoc ErrorLoc = ((RISCVOperand &)*Operands[ErrorInfo]).getStartLoc(); + return Error( + ErrorLoc, + "operand must be formed of letters selected in-order from 'iorw'"); + } + case Match_InvalidFRMArg: { + SMLoc ErrorLoc = ((RISCVOperand &)*Operands[ErrorInfo]).getStartLoc(); + return Error( + ErrorLoc, + "operand must be a valid floating point rounding mode mnemonic"); + } + } + + llvm_unreachable("Unknown match type detected!"); +} + +bool RISCVAsmParser::ParseRegister(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 (!MatchRegisterName(Name) || !MatchRegisterAltName(Name)) { + getParser().Lex(); // Eat identifier token. + return false; + } + + return Error(StartLoc, "invalid register name"); +} + +OperandMatchResultTy RISCVAsmParser::parseRegister(OperandVector &Operands, + bool AllowParens) { + SMLoc FirstS = getLoc(); + bool HadParens = false; + AsmToken Buf[2]; + + // If this a parenthesised register name is allowed, parse it atomically + if (AllowParens && getLexer().is(AsmToken::LParen)) { + size_t ReadCount = getLexer().peekTokens(Buf); + if (ReadCount == 2 && Buf[1].getKind() == AsmToken::RParen) { + HadParens = true; + getParser().Lex(); // Eat '(' + } + } + + switch (getLexer().getKind()) { + default: + return MatchOperand_NoMatch; + case AsmToken::Identifier: + StringRef Name = getLexer().getTok().getIdentifier(); + unsigned RegNo = MatchRegisterName(Name); + if (RegNo == 0) { + RegNo = MatchRegisterAltName(Name); + if (RegNo == 0) { + if (HadParens) + getLexer().UnLex(Buf[0]); + return MatchOperand_NoMatch; + } + } + if (HadParens) + Operands.push_back(RISCVOperand::createToken("(", FirstS, isRV64())); + SMLoc S = getLoc(); + SMLoc E = SMLoc::getFromPointer(S.getPointer() - 1); + getLexer().Lex(); + Operands.push_back(RISCVOperand::createReg(RegNo, S, E, isRV64())); + } + + if (HadParens) { + getParser().Lex(); // Eat ')' + Operands.push_back(RISCVOperand::createToken(")", getLoc(), isRV64())); + } + + return MatchOperand_Success; +} + +OperandMatchResultTy RISCVAsmParser::parseImmediate(OperandVector &Operands) { + SMLoc S = getLoc(); + SMLoc E = SMLoc::getFromPointer(S.getPointer() - 1); + const MCExpr *Res; + + switch (getLexer().getKind()) { + default: + return MatchOperand_NoMatch; + case AsmToken::LParen: + case AsmToken::Minus: + case AsmToken::Plus: + case AsmToken::Integer: + case AsmToken::String: + if (getParser().parseExpression(Res)) + return MatchOperand_ParseFail; + break; + case AsmToken::Identifier: { + StringRef Identifier; + if (getParser().parseIdentifier(Identifier)) + return MatchOperand_ParseFail; + MCSymbol *Sym = getContext().getOrCreateSymbol(Identifier); + Res = MCSymbolRefExpr::create(Sym, MCSymbolRefExpr::VK_None, getContext()); + break; + } + case AsmToken::Percent: + return parseOperandWithModifier(Operands); + } + + Operands.push_back(RISCVOperand::createImm(Res, S, E, isRV64())); + return MatchOperand_Success; +} + +OperandMatchResultTy +RISCVAsmParser::parseOperandWithModifier(OperandVector &Operands) { + SMLoc S = getLoc(); + SMLoc E = SMLoc::getFromPointer(S.getPointer() - 1); + + if (getLexer().getKind() != AsmToken::Percent) { + Error(getLoc(), "expected '%' for operand modifier"); + return MatchOperand_ParseFail; + } + + getParser().Lex(); // Eat '%' + + if (getLexer().getKind() != AsmToken::Identifier) { + Error(getLoc(), "expected valid identifier for operand modifier"); + return MatchOperand_ParseFail; + } + StringRef Identifier = getParser().getTok().getIdentifier(); + RISCVMCExpr::VariantKind VK = RISCVMCExpr::getVariantKindForName(Identifier); + if (VK == RISCVMCExpr::VK_RISCV_Invalid) { + Error(getLoc(), "unrecognized operand modifier"); + return MatchOperand_ParseFail; + } + + getParser().Lex(); // Eat the identifier + if (getLexer().getKind() != AsmToken::LParen) { + Error(getLoc(), "expected '('"); + return MatchOperand_ParseFail; + } + getParser().Lex(); // Eat '(' + + const MCExpr *SubExpr; + if (getParser().parseParenExpression(SubExpr, E)) { + return MatchOperand_ParseFail; + } + + const MCExpr *ModExpr = RISCVMCExpr::create(SubExpr, VK, getContext()); + Operands.push_back(RISCVOperand::createImm(ModExpr, S, E, isRV64())); + return MatchOperand_Success; +} + +OperandMatchResultTy +RISCVAsmParser::parseMemOpBaseReg(OperandVector &Operands) { + if (getLexer().isNot(AsmToken::LParen)) { + Error(getLoc(), "expected '('"); + return MatchOperand_ParseFail; + } + + getParser().Lex(); // Eat '(' + Operands.push_back(RISCVOperand::createToken("(", getLoc(), isRV64())); + + if (parseRegister(Operands) != MatchOperand_Success) { + Error(getLoc(), "expected register"); + return MatchOperand_ParseFail; + } + + if (getLexer().isNot(AsmToken::RParen)) { + Error(getLoc(), "expected ')'"); + return MatchOperand_ParseFail; + } + + getParser().Lex(); // Eat ')' + Operands.push_back(RISCVOperand::createToken(")", getLoc(), isRV64())); + + return MatchOperand_Success; +} + +/// Looks at a token type and creates the relevant operand +/// from this information, adding to Operands. +/// If operand was parsed, returns false, else true. +bool RISCVAsmParser::parseOperand(OperandVector &Operands) { + // Attempt to parse token as register + if (parseRegister(Operands, true) == MatchOperand_Success) + return false; + + // Attempt to parse token as an immediate + if (parseImmediate(Operands) == MatchOperand_Success) { + // Parse memory base register if present + if (getLexer().is(AsmToken::LParen)) + return parseMemOpBaseReg(Operands) != MatchOperand_Success; + return false; + } + + // Finally we have exhausted all options and must declare defeat. + Error(getLoc(), "unknown operand"); + return true; +} + +bool RISCVAsmParser::ParseInstruction(ParseInstructionInfo &Info, + StringRef Name, SMLoc NameLoc, + OperandVector &Operands) { + // First operand is token for instruction + Operands.push_back(RISCVOperand::createToken(Name, NameLoc, isRV64())); + + // If there are no more operands, then finish + if (getLexer().is(AsmToken::EndOfStatement)) + return false; + + // Parse first operand + if (parseOperand(Operands)) + return true; + + // Parse until end of statement, consuming commas between operands + while (getLexer().is(AsmToken::Comma)) { + // Consume comma token + getLexer().Lex(); + + // Parse next operand + if (parseOperand(Operands)) + return true; + } + + if (getLexer().isNot(AsmToken::EndOfStatement)) { + SMLoc Loc = getLexer().getLoc(); + getParser().eatToEndOfStatement(); + return Error(Loc, "unexpected token"); + } + + getParser().Lex(); // Consume the EndOfStatement. + return false; +} + +bool RISCVAsmParser::classifySymbolRef(const MCExpr *Expr, + RISCVMCExpr::VariantKind &Kind, + int64_t &Addend) { + Kind = RISCVMCExpr::VK_RISCV_None; + Addend = 0; + + if (const RISCVMCExpr *RE = dyn_cast<RISCVMCExpr>(Expr)) { + Kind = RE->getKind(); + Expr = RE->getSubExpr(); + } + + // It's a simple symbol reference or constant with no addend. + if (isa<MCConstantExpr>(Expr) || isa<MCSymbolRefExpr>(Expr)) + return true; + + const MCBinaryExpr *BE = dyn_cast<MCBinaryExpr>(Expr); + if (!BE) + return false; + + if (!isa<MCSymbolRefExpr>(BE->getLHS())) + return false; + + if (BE->getOpcode() != MCBinaryExpr::Add && + BE->getOpcode() != MCBinaryExpr::Sub) + return false; + + // We are able to support the subtraction of two symbol references + if (BE->getOpcode() == MCBinaryExpr::Sub && + isa<MCSymbolRefExpr>(BE->getRHS())) + return true; + + // See if the addend is is a constant, otherwise there's more going + // on here than we can deal with. + auto AddendExpr = dyn_cast<MCConstantExpr>(BE->getRHS()); + if (!AddendExpr) + return false; + + Addend = AddendExpr->getValue(); + if (BE->getOpcode() == MCBinaryExpr::Sub) + Addend = -Addend; + + // It's some symbol reference + a constant addend + return Kind != RISCVMCExpr::VK_RISCV_Invalid; +} + +bool RISCVAsmParser::ParseDirective(AsmToken DirectiveID) { return true; } + +extern "C" void LLVMInitializeRISCVAsmParser() { + RegisterMCAsmParser<RISCVAsmParser> X(getTheRISCV32Target()); + RegisterMCAsmParser<RISCVAsmParser> Y(getTheRISCV64Target()); +} diff --git a/lib/Target/RISCV/CMakeLists.txt b/lib/Target/RISCV/CMakeLists.txt index c8887548b917..66b50f8728e1 100644 --- a/lib/Target/RISCV/CMakeLists.txt +++ b/lib/Target/RISCV/CMakeLists.txt @@ -3,12 +3,29 @@ set(LLVM_TARGET_DEFINITIONS RISCV.td) tablegen(LLVM RISCVGenRegisterInfo.inc -gen-register-info) tablegen(LLVM RISCVGenInstrInfo.inc -gen-instr-info) tablegen(LLVM RISCVGenMCCodeEmitter.inc -gen-emitter) +tablegen(LLVM RISCVGenMCPseudoLowering.inc -gen-pseudo-lowering) +tablegen(LLVM RISCVGenAsmMatcher.inc -gen-asm-matcher) +tablegen(LLVM RISCVGenAsmWriter.inc -gen-asm-writer) +tablegen(LLVM RISCVGenDAGISel.inc -gen-dag-isel) +tablegen(LLVM RISCVGenSubtargetInfo.inc -gen-subtarget) +tablegen(LLVM RISCVGenDisassemblerTables.inc -gen-disassembler) add_public_tablegen_target(RISCVCommonTableGen) add_llvm_target(RISCVCodeGen + RISCVAsmPrinter.cpp + RISCVFrameLowering.cpp + RISCVInstrInfo.cpp + RISCVISelDAGToDAG.cpp + RISCVISelLowering.cpp + RISCVMCInstLower.cpp + RISCVRegisterInfo.cpp + RISCVSubtarget.cpp RISCVTargetMachine.cpp ) -add_subdirectory(TargetInfo) +add_subdirectory(AsmParser) +add_subdirectory(Disassembler) +add_subdirectory(InstPrinter) add_subdirectory(MCTargetDesc) +add_subdirectory(TargetInfo) diff --git a/lib/Target/RISCV/Disassembler/CMakeLists.txt b/lib/Target/RISCV/Disassembler/CMakeLists.txt new file mode 100644 index 000000000000..39bd2b7669f5 --- /dev/null +++ b/lib/Target/RISCV/Disassembler/CMakeLists.txt @@ -0,0 +1,3 @@ +add_llvm_library(LLVMRISCVDisassembler + RISCVDisassembler.cpp + ) diff --git a/lib/Target/RISCV/Disassembler/LLVMBuild.txt b/lib/Target/RISCV/Disassembler/LLVMBuild.txt new file mode 100644 index 000000000000..340e89d4cf35 --- /dev/null +++ b/lib/Target/RISCV/Disassembler/LLVMBuild.txt @@ -0,0 +1,24 @@ +;===- ./lib/Target/RISCV/Disassembler/LLVMBuild.txt ------------*- Conf -*--===; +; +; The LLVM Compiler Infrastructure +; +; This file is distributed under the University of Illinois Open Source +; License. See LICENSE.TXT for details. +; +;===------------------------------------------------------------------------===; +; +; This is an LLVMBuild description file for the components in this subdirectory. +; +; For more information on the LLVMBuild system, please see: +; +; http://llvm.org/docs/LLVMBuild.html +; +;===------------------------------------------------------------------------===; + +[component_0] +type = Library +name = RISCVDisassembler +parent = RISCV +required_libraries = MCDisassembler RISCVInfo Support +add_to_library_groups = RISCV + diff --git a/lib/Target/RISCV/Disassembler/RISCVDisassembler.cpp b/lib/Target/RISCV/Disassembler/RISCVDisassembler.cpp new file mode 100644 index 000000000000..563edc9e29d8 --- /dev/null +++ b/lib/Target/RISCV/Disassembler/RISCVDisassembler.cpp @@ -0,0 +1,274 @@ +//===-- RISCVDisassembler.cpp - Disassembler for RISCV --------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file implements the RISCVDisassembler class. +// +//===----------------------------------------------------------------------===// + +#include "MCTargetDesc/RISCVMCTargetDesc.h" +#include "llvm/MC/MCContext.h" +#include "llvm/MC/MCDisassembler/MCDisassembler.h" +#include "llvm/MC/MCFixedLenDisassembler.h" +#include "llvm/MC/MCInst.h" +#include "llvm/MC/MCRegisterInfo.h" +#include "llvm/MC/MCSubtargetInfo.h" +#include "llvm/Support/Endian.h" +#include "llvm/Support/TargetRegistry.h" + +using namespace llvm; + +#define DEBUG_TYPE "riscv-disassembler" + +typedef MCDisassembler::DecodeStatus DecodeStatus; + +namespace { +class RISCVDisassembler : public MCDisassembler { + +public: + RISCVDisassembler(const MCSubtargetInfo &STI, MCContext &Ctx) + : MCDisassembler(STI, Ctx) {} + + DecodeStatus getInstruction(MCInst &Instr, uint64_t &Size, + ArrayRef<uint8_t> Bytes, uint64_t Address, + raw_ostream &VStream, + raw_ostream &CStream) const override; +}; +} // end anonymous namespace + +static MCDisassembler *createRISCVDisassembler(const Target &T, + const MCSubtargetInfo &STI, + MCContext &Ctx) { + return new RISCVDisassembler(STI, Ctx); +} + +extern "C" void LLVMInitializeRISCVDisassembler() { + // Register the disassembler for each target. + TargetRegistry::RegisterMCDisassembler(getTheRISCV32Target(), + createRISCVDisassembler); + TargetRegistry::RegisterMCDisassembler(getTheRISCV64Target(), + createRISCVDisassembler); +} + +static const unsigned GPRDecoderTable[] = { + RISCV::X0, RISCV::X1, RISCV::X2, RISCV::X3, + RISCV::X4, RISCV::X5, RISCV::X6, RISCV::X7, + RISCV::X8, RISCV::X9, RISCV::X10, RISCV::X11, + RISCV::X12, RISCV::X13, RISCV::X14, RISCV::X15, + RISCV::X16, RISCV::X17, RISCV::X18, RISCV::X19, + RISCV::X20, RISCV::X21, RISCV::X22, RISCV::X23, + RISCV::X24, RISCV::X25, RISCV::X26, RISCV::X27, + RISCV::X28, RISCV::X29, RISCV::X30, RISCV::X31 +}; + +static DecodeStatus DecodeGPRRegisterClass(MCInst &Inst, uint64_t RegNo, + uint64_t Address, + const void *Decoder) { + if (RegNo > sizeof(GPRDecoderTable)) + return MCDisassembler::Fail; + + // We must define our own mapping from RegNo to register identifier. + // Accessing index RegNo in the register class will work in the case that + // registers were added in ascending order, but not in general. + unsigned Reg = GPRDecoderTable[RegNo]; + Inst.addOperand(MCOperand::createReg(Reg)); + return MCDisassembler::Success; +} + +static const unsigned FPR32DecoderTable[] = { + RISCV::F0_32, RISCV::F1_32, RISCV::F2_32, RISCV::F3_32, + RISCV::F4_32, RISCV::F5_32, RISCV::F6_32, RISCV::F7_32, + RISCV::F8_32, RISCV::F9_32, RISCV::F10_32, RISCV::F11_32, + RISCV::F12_32, RISCV::F13_32, RISCV::F14_32, RISCV::F15_32, + RISCV::F16_32, RISCV::F17_32, RISCV::F18_32, RISCV::F19_32, + RISCV::F20_32, RISCV::F21_32, RISCV::F22_32, RISCV::F23_32, + RISCV::F24_32, RISCV::F25_32, RISCV::F26_32, RISCV::F27_32, + RISCV::F28_32, RISCV::F29_32, RISCV::F30_32, RISCV::F31_32 +}; + +static DecodeStatus DecodeFPR32RegisterClass(MCInst &Inst, uint64_t RegNo, + uint64_t Address, + const void *Decoder) { + if (RegNo > sizeof(FPR32DecoderTable)) + return MCDisassembler::Fail; + + // We must define our own mapping from RegNo to register identifier. + // Accessing index RegNo in the register class will work in the case that + // registers were added in ascending order, but not in general. + unsigned Reg = FPR32DecoderTable[RegNo]; + Inst.addOperand(MCOperand::createReg(Reg)); + return MCDisassembler::Success; +} + +static DecodeStatus DecodeFPR32CRegisterClass(MCInst &Inst, uint64_t RegNo, + uint64_t Address, + const void *Decoder) { + if (RegNo > 8) { + return MCDisassembler::Fail; + } + unsigned Reg = FPR32DecoderTable[RegNo + 8]; + Inst.addOperand(MCOperand::createReg(Reg)); + return MCDisassembler::Success; +} + +static const unsigned FPR64DecoderTable[] = { + RISCV::F0_64, RISCV::F1_64, RISCV::F2_64, RISCV::F3_64, + RISCV::F4_64, RISCV::F5_64, RISCV::F6_64, RISCV::F7_64, + RISCV::F8_64, RISCV::F9_64, RISCV::F10_64, RISCV::F11_64, + RISCV::F12_64, RISCV::F13_64, RISCV::F14_64, RISCV::F15_64, + RISCV::F16_64, RISCV::F17_64, RISCV::F18_64, RISCV::F19_64, + RISCV::F20_64, RISCV::F21_64, RISCV::F22_64, RISCV::F23_64, + RISCV::F24_64, RISCV::F25_64, RISCV::F26_64, RISCV::F27_64, + RISCV::F28_64, RISCV::F29_64, RISCV::F30_64, RISCV::F31_64 +}; + +static DecodeStatus DecodeFPR64RegisterClass(MCInst &Inst, uint64_t RegNo, + uint64_t Address, + const void *Decoder) { + if (RegNo > sizeof(FPR64DecoderTable)) + return MCDisassembler::Fail; + + // We must define our own mapping from RegNo to register identifier. + // Accessing index RegNo in the register class will work in the case that + // registers were added in ascending order, but not in general. + unsigned Reg = FPR64DecoderTable[RegNo]; + Inst.addOperand(MCOperand::createReg(Reg)); + return MCDisassembler::Success; +} + +static DecodeStatus DecodeFPR64CRegisterClass(MCInst &Inst, uint64_t RegNo, + uint64_t Address, + const void *Decoder) { + if (RegNo > 8) { + return MCDisassembler::Fail; + } + unsigned Reg = FPR64DecoderTable[RegNo + 8]; + Inst.addOperand(MCOperand::createReg(Reg)); + return MCDisassembler::Success; +} + +static DecodeStatus DecodeGPRNoX0RegisterClass(MCInst &Inst, uint64_t RegNo, + uint64_t Address, + const void *Decoder) { + if (RegNo == 0) { + return MCDisassembler::Fail; + } + + return DecodeGPRRegisterClass(Inst, RegNo, Address, Decoder); +} + +static DecodeStatus DecodeGPRNoX0X2RegisterClass(MCInst &Inst, uint64_t RegNo, + uint64_t Address, + const void *Decoder) { + if (RegNo == 2) { + return MCDisassembler::Fail; + } + + return DecodeGPRNoX0RegisterClass(Inst, RegNo, Address, Decoder); +} + +static DecodeStatus DecodeGPRCRegisterClass(MCInst &Inst, uint64_t RegNo, + uint64_t Address, + const void *Decoder) { + if (RegNo > 8) + return MCDisassembler::Fail; + + unsigned Reg = GPRDecoderTable[RegNo + 8]; + 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) { + if (Inst.getOpcode() == RISCV::C_LWSP || Inst.getOpcode() == RISCV::C_SWSP || + Inst.getOpcode() == RISCV::C_LDSP || Inst.getOpcode() == RISCV::C_SDSP || + Inst.getOpcode() == RISCV::C_FLWSP || + Inst.getOpcode() == RISCV::C_FSWSP || + Inst.getOpcode() == RISCV::C_FLDSP || + Inst.getOpcode() == RISCV::C_FSDSP || + Inst.getOpcode() == RISCV::C_ADDI4SPN) { + DecodeGPRRegisterClass(Inst, 2, Address, Decoder); + } + if (Inst.getOpcode() == RISCV::C_ADDI16SP) { + DecodeGPRRegisterClass(Inst, 2, Address, Decoder); + DecodeGPRRegisterClass(Inst, 2, Address, Decoder); + } +} + +template <unsigned N> +static DecodeStatus decodeUImmOperand(MCInst &Inst, uint64_t Imm, + int64_t Address, const void *Decoder) { + assert(isUInt<N>(Imm) && "Invalid immediate"); + addImplySP(Inst, Address, Decoder); + Inst.addOperand(MCOperand::createImm(Imm)); + return MCDisassembler::Success; +} + +template <unsigned N> +static DecodeStatus decodeSImmOperand(MCInst &Inst, uint64_t Imm, + int64_t Address, const void *Decoder) { + assert(isUInt<N>(Imm) && "Invalid immediate"); + addImplySP(Inst, Address, Decoder); + // Sign-extend the number in the bottom N bits of Imm + Inst.addOperand(MCOperand::createImm(SignExtend64<N>(Imm))); + return MCDisassembler::Success; +} + +template <unsigned N> +static DecodeStatus decodeSImmOperandAndLsl1(MCInst &Inst, uint64_t Imm, + int64_t Address, + const void *Decoder) { + assert(isUInt<N>(Imm) && "Invalid immediate"); + // Sign-extend the number in the bottom N bits of Imm after accounting for + // the fact that the N bit immediate is stored in N-1 bits (the LSB is + // always zero) + Inst.addOperand(MCOperand::createImm(SignExtend64<N>(Imm << 1))); + return MCDisassembler::Success; +} + +#include "RISCVGenDisassemblerTables.inc" + +DecodeStatus RISCVDisassembler::getInstruction(MCInst &MI, uint64_t &Size, + ArrayRef<uint8_t> Bytes, + uint64_t Address, + raw_ostream &OS, + raw_ostream &CS) const { + // TODO: This will need modification when supporting instruction set + // extensions with instructions > 32-bits (up to 176 bits wide). + uint32_t Insn; + DecodeStatus Result; + + // It's a 32 bit instruction if bit 0 and 1 are 1. + if ((Bytes[0] & 0x3) == 0x3) { + Insn = support::endian::read32le(Bytes.data()); + DEBUG(dbgs() << "Trying RISCV32 table :\n"); + Result = decodeInstruction(DecoderTable32, MI, Insn, Address, this, STI); + Size = 4; + } else { + Insn = support::endian::read16le(Bytes.data()); + + if (!STI.getFeatureBits()[RISCV::Feature64Bit]) { + DEBUG(dbgs() << "Trying RISCV32Only_16 table (16-bit Instruction):\n"); + // Calling the auto-generated decoder function. + Result = decodeInstruction(DecoderTableRISCV32Only_16, MI, Insn, Address, + this, STI); + if (Result != MCDisassembler::Fail) { + Size = 2; + return Result; + } + } + + DEBUG(dbgs() << "Trying RISCV_C table (16-bit Instruction):\n"); + // Calling the auto-generated decoder function. + Result = decodeInstruction(DecoderTable16, MI, Insn, Address, this, STI); + Size = 2; + } + + return Result; +} diff --git a/lib/Target/RISCV/InstPrinter/CMakeLists.txt b/lib/Target/RISCV/InstPrinter/CMakeLists.txt new file mode 100644 index 000000000000..7f91743fb934 --- /dev/null +++ b/lib/Target/RISCV/InstPrinter/CMakeLists.txt @@ -0,0 +1,3 @@ +add_llvm_library(LLVMRISCVAsmPrinter + RISCVInstPrinter.cpp + ) diff --git a/lib/Target/RISCV/InstPrinter/LLVMBuild.txt b/lib/Target/RISCV/InstPrinter/LLVMBuild.txt new file mode 100644 index 000000000000..5f4545e3d676 --- /dev/null +++ b/lib/Target/RISCV/InstPrinter/LLVMBuild.txt @@ -0,0 +1,23 @@ +;===- ./lib/Target/RISCV/InstPrinter/LLVMBuild.txt -------------*- Conf -*--===; +; +; The LLVM Compiler Infrastructure +; +; This file is distributed under the University of Illinois Open Source +; License. See LICENSE.TXT for details. +; +;===------------------------------------------------------------------------===; +; +; This is an LLVMBuild description file for the components in this subdirectory. +; +; For more information on the LLVMBuild system, please see: +; +; http://llvm.org/docs/LLVMBuild.html +; +;===------------------------------------------------------------------------===; + +[component_0] +type = Library +name = RISCVAsmPrinter +parent = RISCV +required_libraries = MC Support +add_to_library_groups = RISCV diff --git a/lib/Target/RISCV/InstPrinter/RISCVInstPrinter.cpp b/lib/Target/RISCV/InstPrinter/RISCVInstPrinter.cpp new file mode 100644 index 000000000000..ff56fc5d90ff --- /dev/null +++ b/lib/Target/RISCV/InstPrinter/RISCVInstPrinter.cpp @@ -0,0 +1,86 @@ +//===-- RISCVInstPrinter.cpp - Convert RISCV MCInst to asm syntax ---------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This class prints an RISCV MCInst to a .s file. +// +//===----------------------------------------------------------------------===// + +#include "RISCVInstPrinter.h" +#include "MCTargetDesc/RISCVBaseInfo.h" +#include "llvm/MC/MCAsmInfo.h" +#include "llvm/MC/MCExpr.h" +#include "llvm/MC/MCInst.h" +#include "llvm/MC/MCRegisterInfo.h" +#include "llvm/MC/MCSymbol.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/FormattedStream.h" +using namespace llvm; + +#define DEBUG_TYPE "asm-printer" + +// Include the auto-generated portion of the assembly writer. +#define PRINT_ALIAS_INSTR +#include "RISCVGenAsmWriter.inc" + +static cl::opt<bool> +NoAliases("riscv-no-aliases", + cl::desc("Disable the emission of assembler pseudo instructions"), + cl::init(false), + cl::Hidden); + +void RISCVInstPrinter::printInst(const MCInst *MI, raw_ostream &O, + StringRef Annot, const MCSubtargetInfo &STI) { + if (NoAliases || !printAliasInstr(MI, O)) + printInstruction(MI, O); + printAnnotation(O, Annot); +} + +void RISCVInstPrinter::printRegName(raw_ostream &O, unsigned RegNo) const { + O << getRegisterName(RegNo); +} + +void RISCVInstPrinter::printOperand(const MCInst *MI, unsigned OpNo, + raw_ostream &O, const char *Modifier) { + assert((Modifier == 0 || Modifier[0] == 0) && "No modifiers supported"); + const MCOperand &MO = MI->getOperand(OpNo); + + if (MO.isReg()) { + printRegName(O, MO.getReg()); + return; + } + + if (MO.isImm()) { + O << MO.getImm(); + return; + } + + assert(MO.isExpr() && "Unknown operand kind in printOperand"); + MO.getExpr()->print(O, &MAI); +} + +void RISCVInstPrinter::printFenceArg(const MCInst *MI, unsigned OpNo, + raw_ostream &O) { + unsigned FenceArg = MI->getOperand(OpNo).getImm(); + if ((FenceArg & RISCVFenceField::I) != 0) + O << 'i'; + if ((FenceArg & RISCVFenceField::O) != 0) + O << 'o'; + if ((FenceArg & RISCVFenceField::R) != 0) + O << 'r'; + if ((FenceArg & RISCVFenceField::W) != 0) + O << 'w'; +} + +void RISCVInstPrinter::printFRMArg(const MCInst *MI, unsigned OpNo, + raw_ostream &O) { + auto FRMArg = + static_cast<RISCVFPRndMode::RoundingMode>(MI->getOperand(OpNo).getImm()); + O << RISCVFPRndMode::roundingModeToString(FRMArg); +} diff --git a/lib/Target/RISCV/InstPrinter/RISCVInstPrinter.h b/lib/Target/RISCV/InstPrinter/RISCVInstPrinter.h new file mode 100644 index 000000000000..58f3f8410159 --- /dev/null +++ b/lib/Target/RISCV/InstPrinter/RISCVInstPrinter.h @@ -0,0 +1,48 @@ +//===-- RISCVInstPrinter.h - Convert RISCV MCInst to asm syntax ---*- C++ -*--// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This class prints a RISCV MCInst to a .s file. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_RISCV_INSTPRINTER_RISCVINSTPRINTER_H +#define LLVM_LIB_TARGET_RISCV_INSTPRINTER_RISCVINSTPRINTER_H + +#include "MCTargetDesc/RISCVMCTargetDesc.h" +#include "llvm/MC/MCInstPrinter.h" + +namespace llvm { +class MCOperand; + +class RISCVInstPrinter : public MCInstPrinter { +public: + RISCVInstPrinter(const MCAsmInfo &MAI, const MCInstrInfo &MII, + const MCRegisterInfo &MRI) + : MCInstPrinter(MAI, MII, MRI) {} + + void printInst(const MCInst *MI, raw_ostream &O, StringRef Annot, + const MCSubtargetInfo &STI) override; + void printRegName(raw_ostream &O, unsigned RegNo) const override; + + void printOperand(const MCInst *MI, unsigned OpNo, raw_ostream &O, + const char *Modifier = nullptr); + void printFenceArg(const MCInst *MI, unsigned OpNo, raw_ostream &O); + void printFRMArg(const MCInst *MI, unsigned OpNo, raw_ostream &O); + + // Autogenerated by tblgen. + void printInstruction(const MCInst *MI, raw_ostream &O); + bool printAliasInstr(const MCInst *MI, raw_ostream &O); + void printCustomAliasOperand(const MCInst *MI, unsigned OpIdx, + unsigned PrintMethodIdx, raw_ostream &O); + static const char *getRegisterName(unsigned RegNo, + unsigned AltIdx = RISCV::ABIRegAltName); +}; +} + +#endif diff --git a/lib/Target/RISCV/LLVMBuild.txt b/lib/Target/RISCV/LLVMBuild.txt index 9ba5fec928f3..ab21565b0c2e 100644 --- a/lib/Target/RISCV/LLVMBuild.txt +++ b/lib/Target/RISCV/LLVMBuild.txt @@ -16,16 +16,20 @@ ;===------------------------------------------------------------------------===; [common] -subdirectories = TargetInfo MCTargetDesc +subdirectories = AsmParser Disassembler InstPrinter TargetInfo MCTargetDesc [component_0] type = TargetGroup name = RISCV parent = Target +has_asmparser = 1 +has_asmprinter = 1 +has_disassembler = 1 [component_1] type = Library name = RISCVCodeGen parent = RISCV -required_libraries = Core CodeGen RISCVInfo Support Target +required_libraries = AsmPrinter Core CodeGen MC RISCVAsmPrinter RISCVDesc + RISCVInfo SelectionDAG Support Target add_to_library_groups = RISCV diff --git a/lib/Target/RISCV/MCTargetDesc/CMakeLists.txt b/lib/Target/RISCV/MCTargetDesc/CMakeLists.txt index d79de74f7cfc..60429647edd1 100644 --- a/lib/Target/RISCV/MCTargetDesc/CMakeLists.txt +++ b/lib/Target/RISCV/MCTargetDesc/CMakeLists.txt @@ -2,6 +2,7 @@ add_llvm_library(LLVMRISCVDesc RISCVAsmBackend.cpp RISCVELFObjectWriter.cpp RISCVMCAsmInfo.cpp - RISCVMCTargetDesc.cpp RISCVMCCodeEmitter.cpp + RISCVMCExpr.cpp + RISCVMCTargetDesc.cpp ) diff --git a/lib/Target/RISCV/MCTargetDesc/LLVMBuild.txt b/lib/Target/RISCV/MCTargetDesc/LLVMBuild.txt index e48f04963ff8..92daae30e3ff 100644 --- a/lib/Target/RISCV/MCTargetDesc/LLVMBuild.txt +++ b/lib/Target/RISCV/MCTargetDesc/LLVMBuild.txt @@ -19,5 +19,5 @@ type = Library name = RISCVDesc parent = RISCV -required_libraries = MC RISCVInfo Support +required_libraries = MC RISCVAsmPrinter RISCVInfo Support add_to_library_groups = RISCV diff --git a/lib/Target/RISCV/MCTargetDesc/RISCVAsmBackend.cpp b/lib/Target/RISCV/MCTargetDesc/RISCVAsmBackend.cpp index be83efc02d27..b91467fe1455 100644 --- a/lib/Target/RISCV/MCTargetDesc/RISCVAsmBackend.cpp +++ b/lib/Target/RISCV/MCTargetDesc/RISCVAsmBackend.cpp @@ -7,9 +7,12 @@ // //===----------------------------------------------------------------------===// +#include "MCTargetDesc/RISCVFixupKinds.h" #include "MCTargetDesc/RISCVMCTargetDesc.h" +#include "llvm/ADT/APInt.h" #include "llvm/MC/MCAsmBackend.h" #include "llvm/MC/MCAssembler.h" +#include "llvm/MC/MCContext.h" #include "llvm/MC/MCDirectives.h" #include "llvm/MC/MCELFObjectWriter.h" #include "llvm/MC/MCExpr.h" @@ -36,7 +39,8 @@ public: const MCValue &Target, MutableArrayRef<char> Data, uint64_t Value, bool IsResolved) const override; - MCObjectWriter *createObjectWriter(raw_pwrite_stream &OS) const override; + std::unique_ptr<MCObjectWriter> + createObjectWriter(raw_pwrite_stream &OS) const override; bool fixupNeedsRelaxation(const MCFixup &Fixup, uint64_t Value, const MCRelaxableFragment *DF, @@ -44,14 +48,40 @@ public: return false; } - unsigned getNumFixupKinds() const override { return 1; } + unsigned getNumFixupKinds() const override { + return RISCV::NumTargetFixupKinds; + } + + const MCFixupKindInfo &getFixupKindInfo(MCFixupKind Kind) const override { + const static MCFixupKindInfo Infos[RISCV::NumTargetFixupKinds] = { + // 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_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 } + }; + + if (Kind < FirstTargetFixupKind) + return MCAsmBackend::getFixupKindInfo(Kind); + + assert(unsigned(Kind - FirstTargetFixupKind) < getNumFixupKinds() && + "Invalid kind!"); + return Infos[Kind - FirstTargetFixupKind]; + } bool mayNeedRelaxation(const MCInst &Inst) const override { return false; } void relaxInstruction(const MCInst &Inst, const MCSubtargetInfo &STI, MCInst &Res) const override { - llvm_unreachable("RISCVAsmBackend::relaxInstruction() unimplemented"); + report_fatal_error("RISCVAsmBackend::relaxInstruction() unimplemented"); } bool writeNopData(uint64_t Count, MCObjectWriter *OW) const override; @@ -70,14 +100,129 @@ bool RISCVAsmBackend::writeNopData(uint64_t Count, MCObjectWriter *OW) const { return true; } +static uint64_t adjustFixupValue(const MCFixup &Fixup, uint64_t Value, + MCContext &Ctx) { + unsigned Kind = Fixup.getKind(); + switch (Kind) { + default: + llvm_unreachable("Unknown fixup kind!"); + case FK_Data_1: + case FK_Data_2: + case FK_Data_4: + case FK_Data_8: + return Value; + case RISCV::fixup_riscv_lo12_i: + return Value & 0xfff; + case RISCV::fixup_riscv_lo12_s: + return (((Value >> 5) & 0x7f) << 25) | ((Value & 0x1f) << 7); + case RISCV::fixup_riscv_hi20: + case RISCV::fixup_riscv_pcrel_hi20: + // Add 1 if bit 11 is 1, to compensate for low 12 bits being negative. + return ((Value + 0x800) >> 12) & 0xfffff; + case RISCV::fixup_riscv_jal: { + if (!isInt<21>(Value)) + Ctx.reportError(Fixup.getLoc(), "fixup value out of range"); + if (Value & 0x1) + Ctx.reportError(Fixup.getLoc(), "fixup value must be 2-byte aligned"); + // Need to produce imm[19|10:1|11|19:12] from the 21-bit Value. + unsigned Sbit = (Value >> 20) & 0x1; + unsigned Hi8 = (Value >> 12) & 0xff; + unsigned Mid1 = (Value >> 11) & 0x1; + unsigned Lo10 = (Value >> 1) & 0x3ff; + // Inst{31} = Sbit; + // Inst{30-21} = Lo10; + // Inst{20} = Mid1; + // Inst{19-12} = Hi8; + Value = (Sbit << 19) | (Lo10 << 9) | (Mid1 << 8) | Hi8; + return Value; + } + case RISCV::fixup_riscv_branch: { + if (!isInt<13>(Value)) + Ctx.reportError(Fixup.getLoc(), "fixup value out of range"); + if (Value & 0x1) + Ctx.reportError(Fixup.getLoc(), "fixup value must be 2-byte aligned"); + // Need to extract imm[12], imm[10:5], imm[4:1], imm[11] from the 13-bit + // Value. + unsigned Sbit = (Value >> 12) & 0x1; + unsigned Hi1 = (Value >> 11) & 0x1; + unsigned Mid6 = (Value >> 5) & 0x3f; + unsigned Lo4 = (Value >> 1) & 0xf; + // Inst{31} = Sbit; + // Inst{30-25} = Mid6; + // Inst{11-8} = Lo4; + // Inst{7} = Hi1; + Value = (Sbit << 31) | (Mid6 << 25) | (Lo4 << 8) | (Hi1 << 7); + return Value; + } + case RISCV::fixup_riscv_rvc_jump: { + // Need to produce offset[11|4|9:8|10|6|7|3:1|5] from the 11-bit Value. + unsigned Bit11 = (Value >> 11) & 0x1; + unsigned Bit4 = (Value >> 4) & 0x1; + unsigned Bit9_8 = (Value >> 8) & 0x3; + unsigned Bit10 = (Value >> 10) & 0x1; + unsigned Bit6 = (Value >> 6) & 0x1; + unsigned Bit7 = (Value >> 7) & 0x1; + unsigned Bit3_1 = (Value >> 1) & 0x7; + unsigned Bit5 = (Value >> 5) & 0x1; + Value = (Bit11 << 10) | (Bit4 << 9) | (Bit9_8 << 7) | (Bit10 << 6) | + (Bit6 << 5) | (Bit7 << 4) | (Bit3_1 << 1) | Bit5; + return Value; + } + case RISCV::fixup_riscv_rvc_branch: { + // Need to produce offset[8|4:3], [reg 3 bit], offset[7:6|2:1|5] + unsigned Bit8 = (Value >> 8) & 0x1; + unsigned Bit7_6 = (Value >> 6) & 0x3; + unsigned Bit5 = (Value >> 5) & 0x1; + unsigned Bit4_3 = (Value >> 3) & 0x3; + unsigned Bit2_1 = (Value >> 1) & 0x3; + Value = (Bit8 << 12) | (Bit4_3 << 10) | (Bit7_6 << 5) | (Bit2_1 << 3) | + (Bit5 << 2); + return Value; + } + + } +} + +static unsigned getSize(unsigned Kind) { + switch (Kind) { + default: + return 4; + case RISCV::fixup_riscv_rvc_jump: + case RISCV::fixup_riscv_rvc_branch: + return 2; + } +} + void RISCVAsmBackend::applyFixup(const MCAssembler &Asm, const MCFixup &Fixup, const MCValue &Target, MutableArrayRef<char> Data, uint64_t Value, bool IsResolved) const { - return; + MCContext &Ctx = Asm.getContext(); + MCFixupKindInfo Info = getFixupKindInfo(Fixup.getKind()); + if (!Value) + return; // Doesn't change encoding. + // Apply any target-specific value adjustments. + Value = adjustFixupValue(Fixup, Value, Ctx); + + // Shift the value into position. + Value <<= Info.TargetOffset; + + unsigned Offset = Fixup.getOffset(); + unsigned FullSize = getSize(Fixup.getKind()); + +#ifndef NDEBUG + unsigned NumBytes = (Info.TargetSize + 7) / 8; + assert(Offset + NumBytes <= Data.size() && "Invalid fixup offset!"); +#endif + + // For each byte of the fragment that the fixup touches, mask in the + // bits from the fixup value. + for (unsigned i = 0; i != FullSize; ++i) { + Data[Offset + i] |= uint8_t((Value >> (i * 8)) & 0xff); + } } -MCObjectWriter * +std::unique_ptr<MCObjectWriter> RISCVAsmBackend::createObjectWriter(raw_pwrite_stream &OS) const { return createRISCVELFObjectWriter(OS, OSABI, Is64Bit); } diff --git a/lib/Target/RISCV/MCTargetDesc/RISCVBaseInfo.h b/lib/Target/RISCV/MCTargetDesc/RISCVBaseInfo.h new file mode 100644 index 000000000000..b278a2ed3903 --- /dev/null +++ b/lib/Target/RISCV/MCTargetDesc/RISCVBaseInfo.h @@ -0,0 +1,110 @@ +//===-- RISCVBaseInfo.h - Top level definitions for RISCV MC ----*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file contains small standalone enum definitions for the RISCV target +// useful for the compiler back-end and the MC libraries. +// +//===----------------------------------------------------------------------===// +#ifndef LLVM_LIB_TARGET_RISCV_MCTARGETDESC_RISCVBASEINFO_H +#define LLVM_LIB_TARGET_RISCV_MCTARGETDESC_RISCVBASEINFO_H + +#include "RISCVMCTargetDesc.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/StringSwitch.h" + +namespace llvm { + +// RISCVII - This namespace holds all of the target specific flags that +// instruction info tracks. All definitions must match RISCVInstrFormats.td. +namespace RISCVII { +enum { + InstFormatPseudo = 0, + InstFormatR = 1, + InstFormatR4 = 2, + InstFormatI = 3, + InstFormatS = 4, + InstFormatB = 5, + InstFormatU = 6, + InstFormatJ = 7, + InstFormatCR = 8, + InstFormatCI = 9, + InstFormatCSS = 10, + InstFormatCIW = 11, + InstFormatCL = 12, + InstFormatCS = 13, + InstFormatCB = 14, + InstFormatCJ = 15, + InstFormatOther = 16, + + InstFormatMask = 31 +}; + +enum { + MO_None, + MO_LO, + MO_HI, + MO_PCREL_HI, +}; +} // namespace RISCVII + +// Describes the predecessor/successor bits used in the FENCE instruction. +namespace RISCVFenceField { +enum FenceField { + I = 8, + O = 4, + R = 2, + W = 1 +}; +} + +// Describes the supported floating point rounding mode encodings. +namespace RISCVFPRndMode { +enum RoundingMode { + RNE = 0, + RTZ = 1, + RDN = 2, + RUP = 3, + RMM = 4, + DYN = 7, + Invalid +}; + +inline static StringRef roundingModeToString(RoundingMode RndMode) { + switch (RndMode) { + default: + llvm_unreachable("Unknown floating point rounding mode"); + case RISCVFPRndMode::RNE: + return "rne"; + case RISCVFPRndMode::RTZ: + return "rtz"; + case RISCVFPRndMode::RDN: + return "rdn"; + case RISCVFPRndMode::RUP: + return "rup"; + case RISCVFPRndMode::RMM: + return "rmm"; + case RISCVFPRndMode::DYN: + return "dyn"; + } +} + +inline static RoundingMode stringToRoundingMode(StringRef Str) { + return StringSwitch<RoundingMode>(Str) + .Case("rne", RISCVFPRndMode::RNE) + .Case("rtz", RISCVFPRndMode::RTZ) + .Case("rdn", RISCVFPRndMode::RDN) + .Case("rup", RISCVFPRndMode::RUP) + .Case("rmm", RISCVFPRndMode::RMM) + .Case("dyn", RISCVFPRndMode::DYN) + .Default(RISCVFPRndMode::Invalid); +} +} // namespace RISCVFPRndMode +} // namespace llvm + +#endif diff --git a/lib/Target/RISCV/MCTargetDesc/RISCVELFObjectWriter.cpp b/lib/Target/RISCV/MCTargetDesc/RISCVELFObjectWriter.cpp index 4f085d31a267..ad53228c104a 100644 --- a/lib/Target/RISCV/MCTargetDesc/RISCVELFObjectWriter.cpp +++ b/lib/Target/RISCV/MCTargetDesc/RISCVELFObjectWriter.cpp @@ -7,9 +7,11 @@ // //===----------------------------------------------------------------------===// +#include "MCTargetDesc/RISCVFixupKinds.h" #include "MCTargetDesc/RISCVMCTargetDesc.h" #include "llvm/MC/MCELFObjectWriter.h" #include "llvm/MC/MCFixup.h" +#include "llvm/MC/MCObjectWriter.h" #include "llvm/Support/ErrorHandling.h" using namespace llvm; @@ -29,7 +31,7 @@ protected: RISCVELFObjectWriter::RISCVELFObjectWriter(uint8_t OSABI, bool Is64Bit) : MCELFObjectTargetWriter(Is64Bit, OSABI, ELF::EM_RISCV, - /*HasRelocationAddend*/ false) {} + /*HasRelocationAddend*/ true) {} RISCVELFObjectWriter::~RISCVELFObjectWriter() {} @@ -37,11 +39,37 @@ unsigned RISCVELFObjectWriter::getRelocType(MCContext &Ctx, const MCValue &Target, const MCFixup &Fixup, bool IsPCRel) const { - llvm_unreachable("invalid fixup kind!"); + // Determine the type of the relocation + switch ((unsigned)Fixup.getKind()) { + default: + llvm_unreachable("invalid fixup kind!"); + case FK_Data_4: + return ELF::R_RISCV_32; + case FK_Data_8: + return ELF::R_RISCV_64; + case RISCV::fixup_riscv_hi20: + return ELF::R_RISCV_HI20; + case RISCV::fixup_riscv_lo12_i: + return ELF::R_RISCV_LO12_I; + case RISCV::fixup_riscv_lo12_s: + return ELF::R_RISCV_LO12_S; + case RISCV::fixup_riscv_pcrel_hi20: + return ELF::R_RISCV_PCREL_HI20; + case RISCV::fixup_riscv_jal: + return ELF::R_RISCV_JAL; + case RISCV::fixup_riscv_branch: + return ELF::R_RISCV_BRANCH; + case RISCV::fixup_riscv_rvc_jump: + return ELF::R_RISCV_RVC_JUMP; + case RISCV::fixup_riscv_rvc_branch: + return ELF::R_RISCV_RVC_BRANCH; + } } -MCObjectWriter *llvm::createRISCVELFObjectWriter(raw_pwrite_stream &OS, - uint8_t OSABI, bool Is64Bit) { - MCELFObjectTargetWriter *MOTW = new RISCVELFObjectWriter(OSABI, Is64Bit); - return createELFObjectWriter(MOTW, OS, /*IsLittleEndian*/ true); +std::unique_ptr<MCObjectWriter> +llvm::createRISCVELFObjectWriter(raw_pwrite_stream &OS, uint8_t OSABI, + bool Is64Bit) { + return createELFObjectWriter( + llvm::make_unique<RISCVELFObjectWriter>(OSABI, Is64Bit), OS, + /*IsLittleEndian=*/true); } diff --git a/lib/Target/RISCV/MCTargetDesc/RISCVFixupKinds.h b/lib/Target/RISCV/MCTargetDesc/RISCVFixupKinds.h new file mode 100644 index 000000000000..cfb5d99e79f5 --- /dev/null +++ b/lib/Target/RISCV/MCTargetDesc/RISCVFixupKinds.h @@ -0,0 +1,52 @@ +//===-- RISCVFixupKinds.h - RISCV Specific Fixup Entries --------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_RISCV_MCTARGETDESC_RISCVFIXUPKINDS_H +#define LLVM_LIB_TARGET_RISCV_MCTARGETDESC_RISCVFIXUPKINDS_H + +#include "llvm/MC/MCFixup.h" + +#undef RISCV + +namespace llvm { +namespace RISCV { +enum Fixups { + // fixup_riscv_hi20 - 20-bit fixup corresponding to hi(foo) for + // instructions like lui + fixup_riscv_hi20 = FirstTargetFixupKind, + // fixup_riscv_lo12_i - 12-bit fixup corresponding to lo(foo) for + // instructions like addi + fixup_riscv_lo12_i, + // fixup_riscv_lo12_s - 12-bit fixup corresponding to lo(foo) for + // the S-type store instructions + fixup_riscv_lo12_s, + // fixup_riscv_pcrel_hi20 - 20-bit fixup corresponding to pcrel_hi(foo) for + // instructions like auipc + fixup_riscv_pcrel_hi20, + // fixup_riscv_jal - 20-bit fixup for symbol references in the jal + // instruction + fixup_riscv_jal, + // fixup_riscv_branch - 12-bit fixup for symbol references in the branch + // instructions + fixup_riscv_branch, + // fixup_riscv_rvc_jump - 11-bit fixup for symbol references in the + // compressed jump instruction + fixup_riscv_rvc_jump, + // fixup_riscv_rvc_branch - 8-bit fixup for symbol references in the + // compressed branch instruction + fixup_riscv_rvc_branch, + + // fixup_riscv_invalid - used as a sentinel and a marker, must be last fixup + fixup_riscv_invalid, + NumTargetFixupKinds = fixup_riscv_invalid - FirstTargetFixupKind +}; +} // end namespace RISCV +} // end namespace llvm + +#endif diff --git a/lib/Target/RISCV/MCTargetDesc/RISCVMCCodeEmitter.cpp b/lib/Target/RISCV/MCTargetDesc/RISCVMCCodeEmitter.cpp index 9309d493cef4..641997e67e06 100644 --- a/lib/Target/RISCV/MCTargetDesc/RISCVMCCodeEmitter.cpp +++ b/lib/Target/RISCV/MCTargetDesc/RISCVMCCodeEmitter.cpp @@ -11,6 +11,9 @@ // //===----------------------------------------------------------------------===// +#include "MCTargetDesc/RISCVBaseInfo.h" +#include "MCTargetDesc/RISCVFixupKinds.h" +#include "MCTargetDesc/RISCVMCExpr.h" #include "MCTargetDesc/RISCVMCTargetDesc.h" #include "llvm/ADT/Statistic.h" #include "llvm/MC/MCAsmInfo.h" @@ -18,8 +21,10 @@ #include "llvm/MC/MCContext.h" #include "llvm/MC/MCExpr.h" #include "llvm/MC/MCInst.h" +#include "llvm/MC/MCInstrInfo.h" #include "llvm/MC/MCRegisterInfo.h" #include "llvm/MC/MCSymbol.h" +#include "llvm/Support/Casting.h" #include "llvm/Support/EndianStream.h" #include "llvm/Support/raw_ostream.h" @@ -28,15 +33,18 @@ using namespace llvm; #define DEBUG_TYPE "mccodeemitter" STATISTIC(MCNumEmitted, "Number of MC instructions emitted"); +STATISTIC(MCNumFixups, "Number of MC fixups created"); namespace { class RISCVMCCodeEmitter : public MCCodeEmitter { RISCVMCCodeEmitter(const RISCVMCCodeEmitter &) = delete; void operator=(const RISCVMCCodeEmitter &) = delete; MCContext &Ctx; + MCInstrInfo const &MCII; public: - RISCVMCCodeEmitter(MCContext &ctx) : Ctx(ctx) {} + RISCVMCCodeEmitter(MCContext &ctx, MCInstrInfo const &MCII) + : Ctx(ctx), MCII(MCII) {} ~RISCVMCCodeEmitter() override {} @@ -55,21 +63,45 @@ public: unsigned getMachineOpValue(const MCInst &MI, const MCOperand &MO, SmallVectorImpl<MCFixup> &Fixups, const MCSubtargetInfo &STI) const; + + unsigned getImmOpValueAsr1(const MCInst &MI, unsigned OpNo, + SmallVectorImpl<MCFixup> &Fixups, + const MCSubtargetInfo &STI) const; + + unsigned getImmOpValue(const MCInst &MI, unsigned OpNo, + SmallVectorImpl<MCFixup> &Fixups, + const MCSubtargetInfo &STI) const; }; } // end anonymous namespace MCCodeEmitter *llvm::createRISCVMCCodeEmitter(const MCInstrInfo &MCII, const MCRegisterInfo &MRI, MCContext &Ctx) { - return new RISCVMCCodeEmitter(Ctx); + return new RISCVMCCodeEmitter(Ctx, MCII); } void RISCVMCCodeEmitter::encodeInstruction(const MCInst &MI, raw_ostream &OS, SmallVectorImpl<MCFixup> &Fixups, const MCSubtargetInfo &STI) const { - // For now, we only support RISC-V instructions with 32-bit length - uint32_t Bits = getBinaryCodeForInstr(MI, Fixups, STI); - support::endian::Writer<support::little>(OS).write(Bits); + const MCInstrDesc &Desc = MCII.get(MI.getOpcode()); + // Get byte count of instruction. + unsigned Size = Desc.getSize(); + + switch (Size) { + default: + llvm_unreachable("Unhandled encodeInstruction length!"); + case 2: { + uint16_t Bits = getBinaryCodeForInstr(MI, Fixups, STI); + support::endian::Writer<support::little>(OS).write<uint16_t>(Bits); + break; + } + case 4: { + uint32_t Bits = getBinaryCodeForInstr(MI, Fixups, STI); + support::endian::Writer<support::little>(OS).write(Bits); + break; + } + } + ++MCNumEmitted; // Keep track of the # of mi's emitted. } @@ -88,4 +120,77 @@ RISCVMCCodeEmitter::getMachineOpValue(const MCInst &MI, const MCOperand &MO, return 0; } +unsigned +RISCVMCCodeEmitter::getImmOpValueAsr1(const MCInst &MI, unsigned OpNo, + SmallVectorImpl<MCFixup> &Fixups, + const MCSubtargetInfo &STI) const { + const MCOperand &MO = MI.getOperand(OpNo); + + if (MO.isImm()) { + unsigned Res = MO.getImm(); + assert((Res & 1) == 0 && "LSB is non-zero"); + return Res >> 1; + } + + return getImmOpValue(MI, OpNo, Fixups, STI); +} + +unsigned RISCVMCCodeEmitter::getImmOpValue(const MCInst &MI, unsigned OpNo, + SmallVectorImpl<MCFixup> &Fixups, + const MCSubtargetInfo &STI) const { + + const MCOperand &MO = MI.getOperand(OpNo); + + MCInstrDesc const &Desc = MCII.get(MI.getOpcode()); + unsigned MIFrm = Desc.TSFlags & RISCVII::InstFormatMask; + + // If the destination is an immediate, there is nothing to do + if (MO.isImm()) + return MO.getImm(); + + assert(MO.isExpr() && + "getImmOpValue expects only expressions or immediates"); + const MCExpr *Expr = MO.getExpr(); + MCExpr::ExprKind Kind = Expr->getKind(); + RISCV::Fixups FixupKind = RISCV::fixup_riscv_invalid; + if (Kind == MCExpr::Target) { + const RISCVMCExpr *RVExpr = cast<RISCVMCExpr>(Expr); + + switch (RVExpr->getKind()) { + case RISCVMCExpr::VK_RISCV_None: + case RISCVMCExpr::VK_RISCV_Invalid: + llvm_unreachable("Unhandled fixup kind!"); + case RISCVMCExpr::VK_RISCV_LO: + FixupKind = MIFrm == RISCVII::InstFormatI ? RISCV::fixup_riscv_lo12_i + : RISCV::fixup_riscv_lo12_s; + break; + case RISCVMCExpr::VK_RISCV_HI: + FixupKind = RISCV::fixup_riscv_hi20; + break; + case RISCVMCExpr::VK_RISCV_PCREL_HI: + FixupKind = RISCV::fixup_riscv_pcrel_hi20; + break; + } + } else if (Kind == MCExpr::SymbolRef && + cast<MCSymbolRefExpr>(Expr)->getKind() == MCSymbolRefExpr::VK_None) { + if (Desc.getOpcode() == RISCV::JAL) { + FixupKind = RISCV::fixup_riscv_jal; + } else if (MIFrm == RISCVII::InstFormatB) { + FixupKind = RISCV::fixup_riscv_branch; + } else if (MIFrm == RISCVII::InstFormatCJ) { + FixupKind = RISCV::fixup_riscv_rvc_jump; + } else if (MIFrm == RISCVII::InstFormatCB) { + FixupKind = RISCV::fixup_riscv_rvc_branch; + } + } + + assert(FixupKind != RISCV::fixup_riscv_invalid && "Unhandled expression!"); + + Fixups.push_back( + MCFixup::create(0, Expr, MCFixupKind(FixupKind), MI.getLoc())); + ++MCNumFixups; + + return 0; +} + #include "RISCVGenMCCodeEmitter.inc" diff --git a/lib/Target/RISCV/MCTargetDesc/RISCVMCExpr.cpp b/lib/Target/RISCV/MCTargetDesc/RISCVMCExpr.cpp new file mode 100644 index 000000000000..b36236ea155f --- /dev/null +++ b/lib/Target/RISCV/MCTargetDesc/RISCVMCExpr.cpp @@ -0,0 +1,99 @@ +//===-- RISCVMCExpr.cpp - RISCV specific MC expression classes ------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file contains the implementation of the assembly expression modifiers +// accepted by the RISCV architecture (e.g. ":lo12:", ":gottprel_g1:", ...). +// +//===----------------------------------------------------------------------===// + +#include "RISCVMCExpr.h" +#include "llvm/MC/MCAssembler.h" +#include "llvm/MC/MCContext.h" +#include "llvm/MC/MCStreamer.h" +#include "llvm/MC/MCSymbolELF.h" +#include "llvm/MC/MCValue.h" +#include "llvm/Object/ELF.h" +#include "llvm/Support/ErrorHandling.h" + +using namespace llvm; + +#define DEBUG_TYPE "riscvmcexpr" + +const RISCVMCExpr *RISCVMCExpr::create(const MCExpr *Expr, VariantKind Kind, + MCContext &Ctx) { + return new (Ctx) RISCVMCExpr(Expr, Kind); +} + +void RISCVMCExpr::printImpl(raw_ostream &OS, const MCAsmInfo *MAI) const { + bool HasVariant = getKind() != VK_RISCV_None; + if (HasVariant) + OS << '%' << getVariantKindName(getKind()) << '('; + Expr->print(OS, MAI); + if (HasVariant) + OS << ')'; +} + +bool RISCVMCExpr::evaluateAsRelocatableImpl(MCValue &Res, + const MCAsmLayout *Layout, + const MCFixup *Fixup) const { + return getSubExpr()->evaluateAsRelocatable(Res, Layout, Fixup); +} + +void RISCVMCExpr::visitUsedExpr(MCStreamer &Streamer) const { + Streamer.visitUsedExpr(*getSubExpr()); +} + +RISCVMCExpr::VariantKind RISCVMCExpr::getVariantKindForName(StringRef name) { + return StringSwitch<RISCVMCExpr::VariantKind>(name) + .Case("lo", VK_RISCV_LO) + .Case("hi", VK_RISCV_HI) + .Case("pcrel_hi", VK_RISCV_PCREL_HI) + .Default(VK_RISCV_Invalid); +} + +StringRef RISCVMCExpr::getVariantKindName(VariantKind Kind) { + switch (Kind) { + default: + llvm_unreachable("Invalid ELF symbol kind"); + case VK_RISCV_LO: + return "lo"; + case VK_RISCV_HI: + return "hi"; + case VK_RISCV_PCREL_HI: + return "pcrel_hi"; + } +} + +bool RISCVMCExpr::evaluateAsConstant(int64_t &Res) const { + MCValue Value; + + if (Kind == VK_RISCV_PCREL_HI) + return false; + + if (!getSubExpr()->evaluateAsRelocatable(Value, nullptr, nullptr)) + return false; + + if (!Value.isAbsolute()) + return false; + + Res = evaluateAsInt64(Value.getConstant()); + return true; +} + +int64_t RISCVMCExpr::evaluateAsInt64(int64_t Value) const { + switch (Kind) { + default: + llvm_unreachable("Invalid kind"); + case VK_RISCV_LO: + return SignExtend64<12>(Value); + case VK_RISCV_HI: + // Add 1 if bit 11 is 1, to compensate for low 12 bits being negative. + return ((Value + 0x800) >> 12) & 0xfffff; + } +} diff --git a/lib/Target/RISCV/MCTargetDesc/RISCVMCExpr.h b/lib/Target/RISCV/MCTargetDesc/RISCVMCExpr.h new file mode 100644 index 000000000000..69b55ca6f7cd --- /dev/null +++ b/lib/Target/RISCV/MCTargetDesc/RISCVMCExpr.h @@ -0,0 +1,75 @@ +//===-- RISCVMCExpr.h - RISCV specific MC expression classes ----*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file describes RISCV-specific MCExprs, used for modifiers like +// "%hi" or "%lo" etc., +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_RISCV_MCTARGETDESC_RISCVMCEXPR_H +#define LLVM_LIB_TARGET_RISCV_MCTARGETDESC_RISCVMCEXPR_H + +#include "llvm/MC/MCExpr.h" + +namespace llvm { + +class StringRef; +class RISCVMCExpr : public MCTargetExpr { +public: + enum VariantKind { + VK_RISCV_None, + VK_RISCV_LO, + VK_RISCV_HI, + VK_RISCV_PCREL_HI, + VK_RISCV_Invalid + }; + +private: + const MCExpr *Expr; + const VariantKind Kind; + + int64_t evaluateAsInt64(int64_t Value) const; + + explicit RISCVMCExpr(const MCExpr *Expr, VariantKind Kind) + : Expr(Expr), Kind(Kind) {} + +public: + static const RISCVMCExpr *create(const MCExpr *Expr, VariantKind Kind, + MCContext &Ctx); + + VariantKind getKind() const { return Kind; } + + const MCExpr *getSubExpr() const { return Expr; } + + void printImpl(raw_ostream &OS, const MCAsmInfo *MAI) const override; + bool evaluateAsRelocatableImpl(MCValue &Res, const MCAsmLayout *Layout, + const MCFixup *Fixup) const override; + void visitUsedExpr(MCStreamer &Streamer) const override; + MCFragment *findAssociatedFragment() const override { + return getSubExpr()->findAssociatedFragment(); + } + + // There are no TLS RISCVMCExprs at the moment. + void fixELFSymbolsInTLSFixups(MCAssembler &Asm) const override {} + + bool evaluateAsConstant(int64_t &Res) const; + + static bool classof(const MCExpr *E) { + return E->getKind() == MCExpr::Target; + } + + static bool classof(const RISCVMCExpr *) { return true; } + + static VariantKind getVariantKindForName(StringRef name); + static StringRef getVariantKindName(VariantKind Kind); +}; + +} // end namespace llvm. + +#endif diff --git a/lib/Target/RISCV/MCTargetDesc/RISCVMCTargetDesc.cpp b/lib/Target/RISCV/MCTargetDesc/RISCVMCTargetDesc.cpp index 41be0a2084b3..45de976ec6c2 100644 --- a/lib/Target/RISCV/MCTargetDesc/RISCVMCTargetDesc.cpp +++ b/lib/Target/RISCV/MCTargetDesc/RISCVMCTargetDesc.cpp @@ -12,6 +12,7 @@ //===----------------------------------------------------------------------===// #include "RISCVMCTargetDesc.h" +#include "InstPrinter/RISCVInstPrinter.h" #include "RISCVMCAsmInfo.h" #include "llvm/ADT/STLExtras.h" #include "llvm/MC/MCAsmInfo.h" @@ -28,6 +29,9 @@ #define GET_REGINFO_MC_DESC #include "RISCVGenRegisterInfo.inc" +#define GET_SUBTARGETINFO_MC_DESC +#include "RISCVGenSubtargetInfo.inc" + using namespace llvm; static MCInstrInfo *createRISCVMCInstrInfo() { @@ -38,7 +42,7 @@ static MCInstrInfo *createRISCVMCInstrInfo() { static MCRegisterInfo *createRISCVMCRegisterInfo(const Triple &TT) { MCRegisterInfo *X = new MCRegisterInfo(); - InitRISCVMCRegisterInfo(X, RISCV::X1_32); + InitRISCVMCRegisterInfo(X, RISCV::X1); return X; } @@ -47,6 +51,22 @@ static MCAsmInfo *createRISCVMCAsmInfo(const MCRegisterInfo &MRI, return new RISCVMCAsmInfo(TT); } +static MCSubtargetInfo *createRISCVMCSubtargetInfo(const Triple &TT, + StringRef CPU, StringRef FS) { + std::string CPUName = CPU; + if (CPUName.empty()) + CPUName = TT.isArch64Bit() ? "generic-rv64" : "generic-rv32"; + return createRISCVMCSubtargetInfoImpl(TT, CPUName, FS); +} + +static MCInstPrinter *createRISCVMCInstPrinter(const Triple &T, + unsigned SyntaxVariant, + const MCAsmInfo &MAI, + const MCInstrInfo &MII, + const MCRegisterInfo &MRI) { + return new RISCVInstPrinter(MAI, MII, MRI); +} + extern "C" void LLVMInitializeRISCVTargetMC() { for (Target *T : {&getTheRISCV32Target(), &getTheRISCV64Target()}) { TargetRegistry::RegisterMCAsmInfo(*T, createRISCVMCAsmInfo); @@ -54,5 +74,7 @@ extern "C" void LLVMInitializeRISCVTargetMC() { TargetRegistry::RegisterMCRegInfo(*T, createRISCVMCRegisterInfo); TargetRegistry::RegisterMCAsmBackend(*T, createRISCVAsmBackend); TargetRegistry::RegisterMCCodeEmitter(*T, createRISCVMCCodeEmitter); + TargetRegistry::RegisterMCInstPrinter(*T, createRISCVMCInstPrinter); + TargetRegistry::RegisterMCSubtargetInfo(*T, createRISCVMCSubtargetInfo); } } diff --git a/lib/Target/RISCV/MCTargetDesc/RISCVMCTargetDesc.h b/lib/Target/RISCV/MCTargetDesc/RISCVMCTargetDesc.h index 7c98b1c8f321..bea2f8800fa6 100644 --- a/lib/Target/RISCV/MCTargetDesc/RISCVMCTargetDesc.h +++ b/lib/Target/RISCV/MCTargetDesc/RISCVMCTargetDesc.h @@ -17,6 +17,7 @@ #include "llvm/Config/config.h" #include "llvm/MC/MCTargetOptions.h" #include "llvm/Support/DataTypes.h" +#include <memory> namespace llvm { class MCAsmBackend; @@ -43,8 +44,8 @@ MCAsmBackend *createRISCVAsmBackend(const Target &T, const MCRegisterInfo &MRI, const Triple &TT, StringRef CPU, const MCTargetOptions &Options); -MCObjectWriter *createRISCVELFObjectWriter(raw_pwrite_stream &OS, uint8_t OSABI, - bool Is64Bit); +std::unique_ptr<MCObjectWriter> +createRISCVELFObjectWriter(raw_pwrite_stream &OS, uint8_t OSABI, bool Is64Bit); } // Defines symbolic names for RISC-V registers. @@ -55,4 +56,7 @@ MCObjectWriter *createRISCVELFObjectWriter(raw_pwrite_stream &OS, uint8_t OSABI, #define GET_INSTRINFO_ENUM #include "RISCVGenInstrInfo.inc" +#define GET_SUBTARGETINFO_ENUM +#include "RISCVGenSubtargetInfo.inc" + #endif diff --git a/lib/Target/RISCV/RISCV.h b/lib/Target/RISCV/RISCV.h new file mode 100644 index 000000000000..884cb2e5014d --- /dev/null +++ b/lib/Target/RISCV/RISCV.h @@ -0,0 +1,37 @@ +//===-- RISCV.h - Top-level interface for RISCV -----------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file contains the entry points for global functions defined in the LLVM +// RISC-V back-end. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_RISCV_RISCV_H +#define LLVM_LIB_TARGET_RISCV_RISCV_H + +#include "MCTargetDesc/RISCVBaseInfo.h" + +namespace llvm { +class RISCVTargetMachine; +class AsmPrinter; +class FunctionPass; +class MCInst; +class MCOperand; +class MachineInstr; +class MachineOperand; + +void LowerRISCVMachineInstrToMCInst(const MachineInstr *MI, MCInst &OutMI, + const AsmPrinter &AP); +bool LowerRISCVMachineOperandToMCOperand(const MachineOperand &MO, + MCOperand &MCOp, const AsmPrinter &AP); + +FunctionPass *createRISCVISelDag(RISCVTargetMachine &TM); +} + +#endif diff --git a/lib/Target/RISCV/RISCV.td b/lib/Target/RISCV/RISCV.td index 14838309a1bf..c74d560b2e03 100644 --- a/lib/Target/RISCV/RISCV.td +++ b/lib/Target/RISCV/RISCV.td @@ -9,19 +9,82 @@ include "llvm/Target/Target.td" -include "RISCVRegisterInfo.td" -include "RISCVInstrInfo.td" +//===----------------------------------------------------------------------===// +// RISC-V subtarget features and instruction predicates. +//===----------------------------------------------------------------------===// + +def FeatureStdExtM + : SubtargetFeature<"m", "HasStdExtM", "true", + "'M' (Integer Multiplication and Division)">; +def HasStdExtM : Predicate<"Subtarget->hasStdExtM()">, + AssemblerPredicate<"FeatureStdExtM">; + +def FeatureStdExtA + : SubtargetFeature<"a", "HasStdExtA", "true", + "'A' (Atomic Instructions)">; +def HasStdExtA : Predicate<"Subtarget->hasStdExtA()">, + AssemblerPredicate<"FeatureStdExtA">; + +def FeatureStdExtF + : SubtargetFeature<"f", "HasStdExtF", "true", + "'F' (Single-Precision Floating-Point)">; +def HasStdExtF : Predicate<"Subtarget->hasStdExtF()">, + AssemblerPredicate<"FeatureStdExtF">; +def FeatureStdExtD + : SubtargetFeature<"d", "HasStdExtD", "true", + "'D' (Double-Precision Floating-Point)", + [FeatureStdExtF]>; +def HasStdExtD : Predicate<"Subtarget->hasStdExtD()">, + AssemblerPredicate<"FeatureStdExtD">; -def RISCVInstrInfo : InstrInfo; +def FeatureStdExtC + : SubtargetFeature<"c", "HasStdExtC", "true", + "'C' (Compressed Instructions)">; +def HasStdExtC : Predicate<"Subtarget->hasStdExtC()">, + AssemblerPredicate<"FeatureStdExtC">; -def Feature64Bit : SubtargetFeature<"64bit", "HasRV64", "true", - "Implements RV64">; + +def Feature64Bit + : SubtargetFeature<"64bit", "HasRV64", "true", "Implements RV64">; +def IsRV64 : Predicate<"Subtarget->is64Bit()">, + AssemblerPredicate<"Feature64Bit">; +def IsRV32 : Predicate<"!Subtarget->is64Bit()">, + AssemblerPredicate<"!Feature64Bit">; + +def RV64 : HwMode<"+64bit">; +def RV32 : HwMode<"-64bit">; + +//===----------------------------------------------------------------------===// +// Registers, calling conventions, instruction descriptions. +//===----------------------------------------------------------------------===// + +include "RISCVRegisterInfo.td" +include "RISCVCallingConv.td" +include "RISCVInstrInfo.td" + +//===----------------------------------------------------------------------===// +// RISC-V processors supported. +//===----------------------------------------------------------------------===// def : ProcessorModel<"generic-rv32", NoSchedModel, []>; def : ProcessorModel<"generic-rv64", NoSchedModel, [Feature64Bit]>; +//===----------------------------------------------------------------------===// +// Define the RISC-V target. +//===----------------------------------------------------------------------===// + +def RISCVInstrInfo : InstrInfo { + let guessInstructionProperties = 0; +} + +def RISCVAsmParser : AsmParser { + let ShouldEmitMatchRegisterAltName = 1; + let AllowDuplicateRegisterNames = 1; +} + def RISCV : Target { let InstructionSet = RISCVInstrInfo; + let AssemblyParsers = [RISCVAsmParser]; } diff --git a/lib/Target/RISCV/RISCVAsmPrinter.cpp b/lib/Target/RISCV/RISCVAsmPrinter.cpp new file mode 100644 index 000000000000..4808e6c73c50 --- /dev/null +++ b/lib/Target/RISCV/RISCVAsmPrinter.cpp @@ -0,0 +1,72 @@ +//===-- RISCVAsmPrinter.cpp - RISCV LLVM assembly writer ------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file contains a printer that converts from our internal representation +// of machine-dependent LLVM code to the RISCV assembly language. +// +//===----------------------------------------------------------------------===// + +#include "RISCV.h" +#include "InstPrinter/RISCVInstPrinter.h" +#include "RISCVTargetMachine.h" +#include "llvm/CodeGen/AsmPrinter.h" +#include "llvm/CodeGen/MachineConstantPool.h" +#include "llvm/CodeGen/MachineFunctionPass.h" +#include "llvm/CodeGen/MachineInstr.h" +#include "llvm/CodeGen/MachineModuleInfo.h" +#include "llvm/MC/MCAsmInfo.h" +#include "llvm/MC/MCInst.h" +#include "llvm/MC/MCStreamer.h" +#include "llvm/MC/MCSymbol.h" +#include "llvm/Support/TargetRegistry.h" +#include "llvm/Support/raw_ostream.h" +using namespace llvm; + +#define DEBUG_TYPE "asm-printer" + +namespace { +class RISCVAsmPrinter : public AsmPrinter { +public: + explicit RISCVAsmPrinter(TargetMachine &TM, + std::unique_ptr<MCStreamer> Streamer) + : AsmPrinter(TM, std::move(Streamer)) {} + + StringRef getPassName() const override { return "RISCV Assembly Printer"; } + + void EmitInstruction(const MachineInstr *MI) override; + + bool emitPseudoExpansionLowering(MCStreamer &OutStreamer, + const MachineInstr *MI); + + // Wrapper needed for tblgenned pseudo lowering. + bool lowerOperand(const MachineOperand &MO, MCOperand &MCOp) const { + return LowerRISCVMachineOperandToMCOperand(MO, MCOp, *this); + } +}; +} + +// Simple pseudo-instructions have their lowering (with expansion to real +// instructions) auto-generated. +#include "RISCVGenMCPseudoLowering.inc" + +void RISCVAsmPrinter::EmitInstruction(const MachineInstr *MI) { + // Do any auto-generated pseudo lowerings. + if (emitPseudoExpansionLowering(*OutStreamer, MI)) + return; + + MCInst TmpInst; + LowerRISCVMachineInstrToMCInst(MI, TmpInst, *this); + EmitToStreamer(*OutStreamer, TmpInst); +} + +// Force static initialization. +extern "C" void LLVMInitializeRISCVAsmPrinter() { + RegisterAsmPrinter<RISCVAsmPrinter> X(getTheRISCV32Target()); + RegisterAsmPrinter<RISCVAsmPrinter> Y(getTheRISCV64Target()); +} diff --git a/lib/Target/RISCV/RISCVCallingConv.td b/lib/Target/RISCV/RISCVCallingConv.td new file mode 100644 index 000000000000..d2b17c64c9c2 --- /dev/null +++ b/lib/Target/RISCV/RISCVCallingConv.td @@ -0,0 +1,20 @@ +//===-- RISCVCallingConv.td - Calling Conventions RISCV ----*- tablegen -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This describes the calling conventions for the RISCV architecture. +// +//===----------------------------------------------------------------------===// + +// The RISC-V calling convention is handled with custom code in +// RISCVISelLowering.cpp (CC_RISCV). + +def CSR : CalleeSavedRegs<(add X1, X3, X4, X8, X9, (sequence "X%u", 18, 27))>; + +// Needed for implementation of RISCVRegisterInfo::getNoPreservedMask() +def CSR_NoRegs : CalleeSavedRegs<(add)>; diff --git a/lib/Target/RISCV/RISCVFrameLowering.cpp b/lib/Target/RISCV/RISCVFrameLowering.cpp new file mode 100644 index 000000000000..e9e003e63d59 --- /dev/null +++ b/lib/Target/RISCV/RISCVFrameLowering.cpp @@ -0,0 +1,201 @@ +//===-- RISCVFrameLowering.cpp - RISCV Frame Information ------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file contains the RISCV implementation of TargetFrameLowering class. +// +//===----------------------------------------------------------------------===// + +#include "RISCVFrameLowering.h" +#include "RISCVSubtarget.h" +#include "llvm/CodeGen/MachineFrameInfo.h" +#include "llvm/CodeGen/MachineFunction.h" +#include "llvm/CodeGen/MachineInstrBuilder.h" +#include "llvm/CodeGen/MachineRegisterInfo.h" + +using namespace llvm; + +bool RISCVFrameLowering::hasFP(const MachineFunction &MF) const { return true; } + +// Determines the size of the frame and maximum call frame size. +void RISCVFrameLowering::determineFrameLayout(MachineFunction &MF) const { + MachineFrameInfo &MFI = MF.getFrameInfo(); + const RISCVRegisterInfo *RI = STI.getRegisterInfo(); + + // Get the number of bytes to allocate from the FrameInfo. + uint64_t FrameSize = MFI.getStackSize(); + + // Get the alignment. + uint64_t StackAlign = RI->needsStackRealignment(MF) ? MFI.getMaxAlignment() + : getStackAlignment(); + + // Get the maximum call frame size of all the calls. + uint64_t MaxCallFrameSize = MFI.getMaxCallFrameSize(); + + // If we have dynamic alloca then MaxCallFrameSize needs to be aligned so + // that allocations will be aligned. + if (MFI.hasVarSizedObjects()) + MaxCallFrameSize = alignTo(MaxCallFrameSize, StackAlign); + + // Update maximum call frame size. + MFI.setMaxCallFrameSize(MaxCallFrameSize); + + // Include call frame size in total. + if (!(hasReservedCallFrame(MF) && MFI.adjustsStack())) + FrameSize += MaxCallFrameSize; + + // Make sure the frame is aligned. + FrameSize = alignTo(FrameSize, StackAlign); + + // Update frame info. + MFI.setStackSize(FrameSize); +} + +void RISCVFrameLowering::adjustReg(MachineBasicBlock &MBB, + MachineBasicBlock::iterator MBBI, + const DebugLoc &DL, unsigned DestReg, + unsigned SrcReg, int64_t Val, + MachineInstr::MIFlag Flag) const { + const RISCVInstrInfo *TII = STI.getInstrInfo(); + + if (DestReg == SrcReg && Val == 0) + return; + + if (!isInt<12>(Val)) + report_fatal_error("adjustReg cannot yet handle adjustments >12 bits"); + + BuildMI(MBB, MBBI, DL, TII->get(RISCV::ADDI), DestReg) + .addReg(SrcReg) + .addImm(Val) + .setMIFlag(Flag); +} + +// Returns the register used to hold the frame pointer. +static unsigned getFPReg(const RISCVSubtarget &STI) { return RISCV::X8; } + +// Returns the register used to hold the stack pointer. +static unsigned getSPReg(const RISCVSubtarget &STI) { return RISCV::X2; } + +void RISCVFrameLowering::emitPrologue(MachineFunction &MF, + MachineBasicBlock &MBB) const { + assert(&MF.front() == &MBB && "Shrink-wrapping not yet supported"); + + if (!hasFP(MF)) { + report_fatal_error( + "emitPrologue doesn't support framepointer-less functions"); + } + + MachineFrameInfo &MFI = MF.getFrameInfo(); + MachineBasicBlock::iterator MBBI = MBB.begin(); + + unsigned FPReg = getFPReg(STI); + unsigned SPReg = getSPReg(STI); + + // Debug location must be unknown since the first debug location is used + // to determine the end of the prologue. + DebugLoc DL; + + // Determine the correct frame layout + determineFrameLayout(MF); + + // 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(); + + // Early exit if there is no need to allocate on the stack + if (StackSize == 0 && !MFI.adjustsStack()) + return; + + // Allocate space on the stack if necessary. + adjustReg(MBB, MBBI, DL, SPReg, SPReg, -StackSize, MachineInstr::FrameSetup); + + // 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()); + + // Generate new FP. + adjustReg(MBB, MBBI, DL, FPReg, SPReg, StackSize, MachineInstr::FrameSetup); +} + +void RISCVFrameLowering::emitEpilogue(MachineFunction &MF, + MachineBasicBlock &MBB) const { + if (!hasFP(MF)) { + report_fatal_error( + "emitEpilogue doesn't support framepointer-less functions"); + } + + MachineBasicBlock::iterator MBBI = MBB.getLastNonDebugInstr(); + const RISCVRegisterInfo *RI = STI.getRegisterInfo(); + MachineFrameInfo &MFI = MF.getFrameInfo(); + DebugLoc DL = MBBI->getDebugLoc(); + unsigned FPReg = getFPReg(STI); + unsigned SPReg = getSPReg(STI); + + // Skip to before the restores of callee-saved registers + // FIXME: assumes exactly one instruction is used to restore each + // callee-saved register. + MachineBasicBlock::iterator LastFrameDestroy = MBBI; + std::advance(LastFrameDestroy, -MFI.getCalleeSavedInfo().size()); + + uint64_t StackSize = MFI.getStackSize(); + + // Restore the stack pointer using the value of the frame pointer. Only + // necessary if the stack pointer was modified, meaning the stack size is + // unknown. + if (RI->needsStackRealignment(MF) || MFI.hasVarSizedObjects()) { + adjustReg(MBB, LastFrameDestroy, DL, SPReg, FPReg, -StackSize, + MachineInstr::FrameDestroy); + } + + // Deallocate stack + adjustReg(MBB, MBBI, DL, SPReg, SPReg, StackSize, MachineInstr::FrameDestroy); +} + +int RISCVFrameLowering::getFrameIndexReference(const MachineFunction &MF, + int FI, + unsigned &FrameReg) const { + const MachineFrameInfo &MFI = MF.getFrameInfo(); + const TargetRegisterInfo *RI = MF.getSubtarget().getRegisterInfo(); + + // 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(); + int MinCSFI = 0; + int MaxCSFI = -1; + + int Offset = MFI.getObjectOffset(FI) - getOffsetOfLocalArea() + + MFI.getOffsetAdjustment(); + + if (CSI.size()) { + MinCSFI = CSI[0].getFrameIdx(); + MaxCSFI = CSI[CSI.size() - 1].getFrameIdx(); + } + + FrameReg = RI->getFrameRegister(MF); + if (FI >= MinCSFI && FI <= MaxCSFI) { + FrameReg = RISCV::X2; + Offset += MF.getFrameInfo().getStackSize(); + } + return Offset; +} + +void RISCVFrameLowering::determineCalleeSaves(MachineFunction &MF, + BitVector &SavedRegs, + RegScavenger *RS) const { + TargetFrameLowering::determineCalleeSaves(MF, SavedRegs, RS); + // TODO: Once frame pointer elimination is implemented, don't + // unconditionally spill the frame pointer and return address. + SavedRegs.set(RISCV::X1); + SavedRegs.set(RISCV::X8); +} diff --git a/lib/Target/RISCV/RISCVFrameLowering.h b/lib/Target/RISCV/RISCVFrameLowering.h new file mode 100644 index 000000000000..d92bb70c76da --- /dev/null +++ b/lib/Target/RISCV/RISCVFrameLowering.h @@ -0,0 +1,57 @@ +//===-- RISCVFrameLowering.h - Define frame lowering for RISCV -*- C++ -*--===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This class implements RISCV-specific bits of TargetFrameLowering class. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_RISCV_RISCVFRAMELOWERING_H +#define LLVM_LIB_TARGET_RISCV_RISCVFRAMELOWERING_H + +#include "llvm/CodeGen/TargetFrameLowering.h" + +namespace llvm { +class RISCVSubtarget; + +class RISCVFrameLowering : public TargetFrameLowering { +public: + explicit RISCVFrameLowering(const RISCVSubtarget &STI) + : TargetFrameLowering(StackGrowsDown, + /*StackAlignment=*/16, + /*LocalAreaOffset=*/0), + STI(STI) {} + + void emitPrologue(MachineFunction &MF, MachineBasicBlock &MBB) const override; + void emitEpilogue(MachineFunction &MF, MachineBasicBlock &MBB) const override; + + int getFrameIndexReference(const MachineFunction &MF, int FI, + unsigned &FrameReg) const override; + + void determineCalleeSaves(MachineFunction &MF, BitVector &SavedRegs, + RegScavenger *RS) const override; + + bool hasFP(const MachineFunction &MF) const override; + + MachineBasicBlock::iterator + eliminateCallFramePseudoInstr(MachineFunction &MF, MachineBasicBlock &MBB, + MachineBasicBlock::iterator MI) const override { + return MBB.erase(MI); + } + +protected: + const RISCVSubtarget &STI; + +private: + void determineFrameLayout(MachineFunction &MF) const; + void adjustReg(MachineBasicBlock &MBB, MachineBasicBlock::iterator MBBI, + const DebugLoc &DL, unsigned DestReg, unsigned SrcReg, + int64_t Val, MachineInstr::MIFlag Flag) const; +}; +} +#endif diff --git a/lib/Target/RISCV/RISCVISelDAGToDAG.cpp b/lib/Target/RISCV/RISCVISelDAGToDAG.cpp new file mode 100644 index 000000000000..113a45ac7cc0 --- /dev/null +++ b/lib/Target/RISCV/RISCVISelDAGToDAG.cpp @@ -0,0 +1,108 @@ +//===-- RISCVISelDAGToDAG.cpp - A dag to dag inst selector for RISCV ------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines an instruction selector for the RISCV target. +// +//===----------------------------------------------------------------------===// + +#include "RISCV.h" +#include "MCTargetDesc/RISCVMCTargetDesc.h" +#include "RISCVTargetMachine.h" +#include "llvm/CodeGen/MachineFrameInfo.h" +#include "llvm/CodeGen/SelectionDAGISel.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; + +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 Select(SDNode *Node) override; + + bool SelectAddrFI(SDValue Addr, SDValue &Base); + +// Include the pieces autogenerated from the target description. +#include "RISCVGenDAGISel.inc" +}; +} + +void RISCVDAGToDAGISel::Select(SDNode *Node) { + unsigned Opcode = Node->getOpcode(); + MVT XLenVT = Subtarget->getXLenVT(); + + // Dump information about the Node being selected. + DEBUG(dbgs() << "Selecting: "; Node->dump(CurDAG); dbgs() << "\n"); + + // If we have a custom node, we have already selected + if (Node->isMachineOpcode()) { + DEBUG(dbgs() << "== "; Node->dump(CurDAG); dbgs() << "\n"); + Node->setNodeId(-1); + return; + } + + // Instruction Selection not handled by the auto-generated tablegen selection + // should be handled here. + EVT VT = Node->getValueType(0); + if (Opcode == ISD::Constant && VT == XLenVT) { + auto *ConstNode = cast<ConstantSDNode>(Node); + // Materialize zero constants as copies from X0. This allows the coalescer + // to propagate these into other instructions. + if (ConstNode->isNullValue()) { + SDValue New = CurDAG->getCopyFromReg(CurDAG->getEntryNode(), SDLoc(Node), + RISCV::X0, XLenVT); + ReplaceNode(Node, New.getNode()); + return; + } + } + if (Opcode == ISD::FrameIndex) { + SDLoc DL(Node); + SDValue Imm = CurDAG->getTargetConstant(0, DL, XLenVT); + int FI = dyn_cast<FrameIndexSDNode>(Node)->getIndex(); + EVT VT = Node->getValueType(0); + SDValue TFI = CurDAG->getTargetFrameIndex(FI, VT); + ReplaceNode(Node, CurDAG->getMachineNode(RISCV::ADDI, DL, VT, TFI, Imm)); + return; + } + + // Select the default instruction. + SelectCode(Node); +} + +bool RISCVDAGToDAGISel::SelectAddrFI(SDValue Addr, SDValue &Base) { + if (auto FIN = dyn_cast<FrameIndexSDNode>(Addr)) { + Base = CurDAG->getTargetFrameIndex(FIN->getIndex(), Subtarget->getXLenVT()); + return true; + } + return false; +} + +// This pass converts a legalized DAG into a RISCV-specific DAG, ready +// for instruction scheduling. +FunctionPass *llvm::createRISCVISelDag(RISCVTargetMachine &TM) { + return new RISCVDAGToDAGISel(TM); +} diff --git a/lib/Target/RISCV/RISCVISelLowering.cpp b/lib/Target/RISCV/RISCVISelLowering.cpp new file mode 100644 index 000000000000..7d32954936be --- /dev/null +++ b/lib/Target/RISCV/RISCVISelLowering.cpp @@ -0,0 +1,892 @@ +//===-- RISCVISelLowering.cpp - RISCV DAG Lowering Implementation --------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines the interfaces that RISCV uses to lower LLVM code into a +// selection DAG. +// +//===----------------------------------------------------------------------===// + +#include "RISCVISelLowering.h" +#include "RISCV.h" +#include "RISCVRegisterInfo.h" +#include "RISCVSubtarget.h" +#include "RISCVTargetMachine.h" +#include "llvm/CodeGen/CallingConvLower.h" +#include "llvm/CodeGen/MachineFrameInfo.h" +#include "llvm/CodeGen/MachineFunction.h" +#include "llvm/CodeGen/MachineInstrBuilder.h" +#include "llvm/CodeGen/MachineRegisterInfo.h" +#include "llvm/CodeGen/SelectionDAGISel.h" +#include "llvm/CodeGen/TargetLoweringObjectFileImpl.h" +#include "llvm/CodeGen/ValueTypes.h" +#include "llvm/IR/DiagnosticInfo.h" +#include "llvm/IR/DiagnosticPrinter.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/raw_ostream.h" + +using namespace llvm; + +#define DEBUG_TYPE "riscv-lower" + +RISCVTargetLowering::RISCVTargetLowering(const TargetMachine &TM, + const RISCVSubtarget &STI) + : TargetLowering(TM), Subtarget(STI) { + + MVT XLenVT = Subtarget.getXLenVT(); + + // Set up the register classes. + addRegisterClass(XLenVT, &RISCV::GPRRegClass); + + // Compute derived properties from the register classes. + computeRegisterProperties(STI.getRegisterInfo()); + + setStackPointerRegisterToSaveRestore(RISCV::X2); + + for (auto N : {ISD::EXTLOAD, ISD::SEXTLOAD, ISD::ZEXTLOAD}) + setLoadExtAction(N, XLenVT, MVT::i1, Promote); + + // TODO: add all necessary setOperationAction calls. + setOperationAction(ISD::DYNAMIC_STACKALLOC, XLenVT, Expand); + + setOperationAction(ISD::BR_JT, MVT::Other, Expand); + setOperationAction(ISD::BR_CC, XLenVT, Expand); + setOperationAction(ISD::SELECT, XLenVT, Custom); + setOperationAction(ISD::SELECT_CC, XLenVT, Expand); + + setOperationAction(ISD::STACKSAVE, MVT::Other, Expand); + setOperationAction(ISD::STACKRESTORE, MVT::Other, Expand); + + for (auto VT : {MVT::i1, MVT::i8, MVT::i16}) + setOperationAction(ISD::SIGN_EXTEND_INREG, VT, Expand); + + setOperationAction(ISD::ADDC, XLenVT, Expand); + setOperationAction(ISD::ADDE, XLenVT, Expand); + setOperationAction(ISD::SUBC, XLenVT, Expand); + setOperationAction(ISD::SUBE, XLenVT, Expand); + + setOperationAction(ISD::SREM, XLenVT, Expand); + setOperationAction(ISD::SDIVREM, XLenVT, Expand); + setOperationAction(ISD::SDIV, XLenVT, Expand); + setOperationAction(ISD::UREM, XLenVT, Expand); + setOperationAction(ISD::UDIVREM, XLenVT, Expand); + setOperationAction(ISD::UDIV, XLenVT, Expand); + + setOperationAction(ISD::MUL, XLenVT, Expand); + setOperationAction(ISD::SMUL_LOHI, XLenVT, Expand); + setOperationAction(ISD::UMUL_LOHI, XLenVT, Expand); + setOperationAction(ISD::MULHS, XLenVT, Expand); + setOperationAction(ISD::MULHU, XLenVT, Expand); + + setOperationAction(ISD::SHL_PARTS, XLenVT, Expand); + setOperationAction(ISD::SRL_PARTS, XLenVT, Expand); + setOperationAction(ISD::SRA_PARTS, XLenVT, Expand); + + setOperationAction(ISD::ROTL, XLenVT, Expand); + setOperationAction(ISD::ROTR, XLenVT, Expand); + setOperationAction(ISD::BSWAP, XLenVT, Expand); + setOperationAction(ISD::CTTZ, XLenVT, Expand); + setOperationAction(ISD::CTLZ, XLenVT, Expand); + setOperationAction(ISD::CTPOP, XLenVT, Expand); + + setOperationAction(ISD::GlobalAddress, XLenVT, Custom); + setOperationAction(ISD::BlockAddress, XLenVT, Custom); + + setBooleanContents(ZeroOrOneBooleanContent); + + // Function alignments (log2). + setMinFunctionAlignment(3); + setPrefFunctionAlignment(3); + + // Effectively disable jump table generation. + setMinimumJumpTableEntries(INT_MAX); +} + +// Changes the condition code and swaps operands if necessary, so the SetCC +// operation matches one of the comparisons supported directly in the RISC-V +// ISA. +static void normaliseSetCC(SDValue &LHS, SDValue &RHS, ISD::CondCode &CC) { + switch (CC) { + default: + break; + case ISD::SETGT: + case ISD::SETLE: + case ISD::SETUGT: + case ISD::SETULE: + CC = ISD::getSetCCSwappedOperands(CC); + std::swap(LHS, RHS); + break; + } +} + +// Return the RISC-V branch opcode that matches the given DAG integer +// condition code. The CondCode must be one of those supported by the RISC-V +// ISA (see normaliseSetCC). +static unsigned getBranchOpcodeForIntCondCode(ISD::CondCode CC) { + switch (CC) { + default: + llvm_unreachable("Unsupported CondCode"); + case ISD::SETEQ: + return RISCV::BEQ; + case ISD::SETNE: + return RISCV::BNE; + case ISD::SETLT: + return RISCV::BLT; + case ISD::SETGE: + return RISCV::BGE; + case ISD::SETULT: + return RISCV::BLTU; + case ISD::SETUGE: + return RISCV::BGEU; + } +} + +SDValue RISCVTargetLowering::LowerOperation(SDValue Op, + SelectionDAG &DAG) const { + switch (Op.getOpcode()) { + default: + report_fatal_error("unimplemented operand"); + case ISD::GlobalAddress: + return lowerGlobalAddress(Op, DAG); + case ISD::BlockAddress: + return lowerBlockAddress(Op, DAG); + case ISD::SELECT: + return lowerSELECT(Op, DAG); + } +} + +SDValue RISCVTargetLowering::lowerGlobalAddress(SDValue Op, + SelectionDAG &DAG) const { + SDLoc DL(Op); + EVT Ty = Op.getValueType(); + GlobalAddressSDNode *N = cast<GlobalAddressSDNode>(Op); + const GlobalValue *GV = N->getGlobal(); + int64_t Offset = N->getOffset(); + + if (isPositionIndependent() || Subtarget.is64Bit()) + report_fatal_error("Unable to lowerGlobalAddress"); + + SDValue GAHi = + DAG.getTargetGlobalAddress(GV, DL, Ty, Offset, RISCVII::MO_HI); + SDValue GALo = + DAG.getTargetGlobalAddress(GV, DL, Ty, Offset, RISCVII::MO_LO); + SDValue MNHi = SDValue(DAG.getMachineNode(RISCV::LUI, DL, Ty, GAHi), 0); + SDValue MNLo = + SDValue(DAG.getMachineNode(RISCV::ADDI, DL, Ty, MNHi, GALo), 0); + return MNLo; +} + +SDValue RISCVTargetLowering::lowerBlockAddress(SDValue Op, + SelectionDAG &DAG) const { + SDLoc DL(Op); + EVT Ty = Op.getValueType(); + BlockAddressSDNode *N = cast<BlockAddressSDNode>(Op); + const BlockAddress *BA = N->getBlockAddress(); + int64_t Offset = N->getOffset(); + + if (isPositionIndependent() || Subtarget.is64Bit()) + report_fatal_error("Unable to lowerBlockAddress"); + + SDValue BAHi = DAG.getTargetBlockAddress(BA, Ty, Offset, RISCVII::MO_HI); + SDValue BALo = DAG.getTargetBlockAddress(BA, Ty, Offset, RISCVII::MO_LO); + SDValue MNHi = SDValue(DAG.getMachineNode(RISCV::LUI, DL, Ty, BAHi), 0); + SDValue MNLo = + SDValue(DAG.getMachineNode(RISCV::ADDI, DL, Ty, MNHi, BALo), 0); + return MNLo; +} + +SDValue RISCVTargetLowering::lowerExternalSymbol(SDValue Op, + SelectionDAG &DAG) const { + SDLoc DL(Op); + EVT Ty = Op.getValueType(); + ExternalSymbolSDNode *N = cast<ExternalSymbolSDNode>(Op); + const char *Sym = N->getSymbol(); + + // TODO: should also handle gp-relative loads. + + if (isPositionIndependent() || Subtarget.is64Bit()) + report_fatal_error("Unable to lowerExternalSymbol"); + + SDValue GAHi = DAG.getTargetExternalSymbol(Sym, Ty, RISCVII::MO_HI); + SDValue GALo = DAG.getTargetExternalSymbol(Sym, Ty, RISCVII::MO_LO); + SDValue MNHi = SDValue(DAG.getMachineNode(RISCV::LUI, DL, Ty, GAHi), 0); + SDValue MNLo = + SDValue(DAG.getMachineNode(RISCV::ADDI, DL, Ty, MNHi, GALo), 0); + return MNLo; +} + +SDValue RISCVTargetLowering::lowerSELECT(SDValue Op, SelectionDAG &DAG) const { + SDValue CondV = Op.getOperand(0); + SDValue TrueV = Op.getOperand(1); + SDValue FalseV = Op.getOperand(2); + SDLoc DL(Op); + MVT XLenVT = Subtarget.getXLenVT(); + + // If the result type is XLenVT and CondV is the output of a SETCC node + // which also operated on XLenVT inputs, then merge the SETCC node into the + // lowered RISCVISD::SELECT_CC to take advantage of the integer + // compare+branch instructions. i.e.: + // (select (setcc lhs, rhs, cc), truev, falsev) + // -> (riscvisd::select_cc lhs, rhs, cc, truev, falsev) + if (Op.getSimpleValueType() == XLenVT && CondV.getOpcode() == ISD::SETCC && + CondV.getOperand(0).getSimpleValueType() == XLenVT) { + SDValue LHS = CondV.getOperand(0); + SDValue RHS = CondV.getOperand(1); + auto CC = cast<CondCodeSDNode>(CondV.getOperand(2)); + ISD::CondCode CCVal = CC->get(); + + normaliseSetCC(LHS, RHS, CCVal); + + SDValue TargetCC = DAG.getConstant(CCVal, DL, XLenVT); + SDVTList VTs = DAG.getVTList(Op.getValueType(), MVT::Glue); + SDValue Ops[] = {LHS, RHS, TargetCC, TrueV, FalseV}; + return DAG.getNode(RISCVISD::SELECT_CC, DL, VTs, Ops); + } + + // Otherwise: + // (select condv, truev, falsev) + // -> (riscvisd::select_cc condv, zero, setne, truev, falsev) + SDValue Zero = DAG.getConstant(0, DL, XLenVT); + SDValue SetNE = DAG.getConstant(ISD::SETNE, DL, XLenVT); + + SDVTList VTs = DAG.getVTList(Op.getValueType(), MVT::Glue); + SDValue Ops[] = {CondV, Zero, SetNE, TrueV, FalseV}; + + return DAG.getNode(RISCVISD::SELECT_CC, DL, VTs, Ops); +} + +MachineBasicBlock * +RISCVTargetLowering::EmitInstrWithCustomInserter(MachineInstr &MI, + MachineBasicBlock *BB) const { + const TargetInstrInfo &TII = *BB->getParent()->getSubtarget().getInstrInfo(); + DebugLoc DL = MI.getDebugLoc(); + + assert(MI.getOpcode() == RISCV::Select_GPR_Using_CC_GPR && + "Unexpected instr type to insert"); + + // To "insert" a SELECT instruction, we actually have to insert the triangle + // control-flow pattern. The incoming instruction knows the destination vreg + // to set, the condition code register to branch on, the true/false values to + // select between, and the condcode to use to select the appropriate branch. + // + // We produce the following control flow: + // HeadMBB + // | \ + // | IfFalseMBB + // | / + // TailMBB + const BasicBlock *LLVM_BB = BB->getBasicBlock(); + MachineFunction::iterator I = ++BB->getIterator(); + + MachineBasicBlock *HeadMBB = BB; + MachineFunction *F = BB->getParent(); + MachineBasicBlock *TailMBB = F->CreateMachineBasicBlock(LLVM_BB); + MachineBasicBlock *IfFalseMBB = F->CreateMachineBasicBlock(LLVM_BB); + + F->insert(I, IfFalseMBB); + F->insert(I, TailMBB); + // Move all remaining instructions to TailMBB. + TailMBB->splice(TailMBB->begin(), HeadMBB, + std::next(MachineBasicBlock::iterator(MI)), HeadMBB->end()); + // Update machine-CFG edges by transferring all successors of the current + // block to the new block which will contain the Phi node for the select. + TailMBB->transferSuccessorsAndUpdatePHIs(HeadMBB); + // Set the successors for HeadMBB. + HeadMBB->addSuccessor(IfFalseMBB); + HeadMBB->addSuccessor(TailMBB); + + // Insert appropriate branch. + unsigned LHS = MI.getOperand(1).getReg(); + unsigned RHS = MI.getOperand(2).getReg(); + auto CC = static_cast<ISD::CondCode>(MI.getOperand(3).getImm()); + unsigned Opcode = getBranchOpcodeForIntCondCode(CC); + + BuildMI(HeadMBB, DL, TII.get(Opcode)) + .addReg(LHS) + .addReg(RHS) + .addMBB(TailMBB); + + // IfFalseMBB just falls through to TailMBB. + IfFalseMBB->addSuccessor(TailMBB); + + // %Result = phi [ %TrueValue, HeadMBB ], [ %FalseValue, IfFalseMBB ] + BuildMI(*TailMBB, TailMBB->begin(), DL, TII.get(RISCV::PHI), + MI.getOperand(0).getReg()) + .addReg(MI.getOperand(4).getReg()) + .addMBB(HeadMBB) + .addReg(MI.getOperand(5).getReg()) + .addMBB(IfFalseMBB); + + MI.eraseFromParent(); // The pseudo instruction is gone now. + return TailMBB; +} + +// Calling Convention Implementation. +// The expectations for frontend ABI lowering vary from target to target. +// Ideally, an LLVM frontend would be able to avoid worrying about many ABI +// details, but this is a longer term goal. For now, we simply try to keep the +// role of the frontend as simple and well-defined as possible. The rules can +// be summarised as: +// * Never split up large scalar arguments. We handle them here. +// * If a hardfloat calling convention is being used, and the struct may be +// passed in a pair of registers (fp+fp, int+fp), and both registers are +// available, then pass as two separate arguments. If either the GPRs or FPRs +// are exhausted, then pass according to the rule below. +// * If a struct could never be passed in registers or directly in a stack +// slot (as it is larger than 2*XLEN and the floating point rules don't +// apply), then pass it using a pointer with the byval attribute. +// * If a struct is less than 2*XLEN, then coerce to either a two-element +// word-sized array or a 2*XLEN scalar (depending on alignment). +// * The frontend can determine whether a struct is returned by reference or +// not based on its size and fields. If it will be returned by reference, the +// frontend must modify the prototype so a pointer with the sret annotation is +// passed as the first argument. This is not necessary for large scalar +// returns. +// * Struct return values and varargs should be coerced to structs containing +// register-size fields in the same situations they would be for fixed +// arguments. + +static const MCPhysReg ArgGPRs[] = { + RISCV::X10, RISCV::X11, RISCV::X12, RISCV::X13, + RISCV::X14, RISCV::X15, RISCV::X16, RISCV::X17 +}; + +// Pass a 2*XLEN argument that has been split into two XLEN values through +// registers or the stack as necessary. +static bool CC_RISCVAssign2XLen(unsigned XLen, CCState &State, CCValAssign VA1, + ISD::ArgFlagsTy ArgFlags1, unsigned ValNo2, + MVT ValVT2, MVT LocVT2, + ISD::ArgFlagsTy ArgFlags2) { + unsigned XLenInBytes = XLen / 8; + if (unsigned Reg = State.AllocateReg(ArgGPRs)) { + // At least one half can be passed via register. + State.addLoc(CCValAssign::getReg(VA1.getValNo(), VA1.getValVT(), Reg, + VA1.getLocVT(), CCValAssign::Full)); + } else { + // Both halves must be passed on the stack, with proper alignment. + unsigned StackAlign = std::max(XLenInBytes, ArgFlags1.getOrigAlign()); + 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)); + return false; + } + + if (unsigned Reg = State.AllocateReg(ArgGPRs)) { + // The second half can also be passed via register. + State.addLoc( + CCValAssign::getReg(ValNo2, ValVT2, Reg, LocVT2, CCValAssign::Full)); + } 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)); + } + + return false; +} + +// Implements the RISC-V calling convention. Returns true upon failure. +static bool CC_RISCV(const DataLayout &DL, unsigned ValNo, MVT ValVT, MVT LocVT, + CCValAssign::LocInfo LocInfo, ISD::ArgFlagsTy ArgFlags, + CCState &State, bool IsFixed, bool IsRet) { + unsigned XLen = DL.getLargestLegalIntTypeSizeInBits(); + assert(XLen == 32 || XLen == 64); + MVT XLenVT = XLen == 32 ? MVT::i32 : MVT::i64; + assert(ValVT == XLenVT && "Unexpected ValVT"); + assert(LocVT == XLenVT && "Unexpected LocVT"); + assert(IsFixed && "Vararg support not yet implemented"); + + // Any return value split in to more than two values can't be returned + // directly. + if (IsRet && ValNo > 1) + return true; + + SmallVectorImpl<CCValAssign> &PendingLocs = State.getPendingLocs(); + SmallVectorImpl<ISD::ArgFlagsTy> &PendingArgFlags = + State.getPendingArgFlags(); + + assert(PendingLocs.size() == PendingArgFlags.size() && + "PendingLocs and PendingArgFlags out of sync"); + + // Split arguments might be passed indirectly, so keep track of the pending + // values. + if (ArgFlags.isSplit() || !PendingLocs.empty()) { + LocVT = XLenVT; + LocInfo = CCValAssign::Indirect; + PendingLocs.push_back( + CCValAssign::getPending(ValNo, ValVT, LocVT, LocInfo)); + PendingArgFlags.push_back(ArgFlags); + if (!ArgFlags.isSplitEnd()) { + return false; + } + } + + // If the split argument only had two elements, it should be passed directly + // in registers or on the stack. + if (ArgFlags.isSplitEnd() && PendingLocs.size() <= 2) { + assert(PendingLocs.size() == 2 && "Unexpected PendingLocs.size()"); + // Apply the normal calling convention rules to the first half of the + // split argument. + CCValAssign VA = PendingLocs[0]; + ISD::ArgFlagsTy AF = PendingArgFlags[0]; + PendingLocs.clear(); + PendingArgFlags.clear(); + return CC_RISCVAssign2XLen(XLen, State, VA, AF, ValNo, ValVT, LocVT, + ArgFlags); + } + + // Allocate to a register if possible, or else a stack slot. + unsigned Reg = State.AllocateReg(ArgGPRs); + unsigned StackOffset = Reg ? 0 : State.AllocateStack(XLen / 8, 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. + if (!PendingLocs.empty()) { + assert(ArgFlags.isSplitEnd() && "Expected ArgFlags.isSplitEnd()"); + assert(PendingLocs.size() > 2 && "Unexpected PendingLocs.size()"); + + for (auto &It : PendingLocs) { + if (Reg) + It.convertToReg(Reg); + else + It.convertToMem(StackOffset); + State.addLoc(It); + } + PendingLocs.clear(); + PendingArgFlags.clear(); + return false; + } + + assert(LocVT == XLenVT && "Expected an XLenVT at this stage"); + + if (Reg) { + State.addLoc(CCValAssign::getReg(ValNo, ValVT, Reg, LocVT, LocInfo)); + } else { + State.addLoc( + CCValAssign::getMem(ValNo, ValVT, StackOffset, LocVT, LocInfo)); + } + return false; +} + +void RISCVTargetLowering::analyzeInputArgs( + MachineFunction &MF, CCState &CCInfo, + const SmallVectorImpl<ISD::InputArg> &Ins, bool IsRet) const { + unsigned NumArgs = Ins.size(); + + for (unsigned i = 0; i != NumArgs; ++i) { + MVT ArgVT = Ins[i].VT; + ISD::ArgFlagsTy ArgFlags = Ins[i].Flags; + + if (CC_RISCV(MF.getDataLayout(), i, ArgVT, ArgVT, CCValAssign::Full, + ArgFlags, CCInfo, /*IsRet=*/true, IsRet)) { + DEBUG(dbgs() << "InputArg #" << i << " has unhandled type " + << EVT(ArgVT).getEVTString() << '\n'); + llvm_unreachable(nullptr); + } + } +} + +void RISCVTargetLowering::analyzeOutputArgs( + MachineFunction &MF, CCState &CCInfo, + const SmallVectorImpl<ISD::OutputArg> &Outs, bool IsRet) const { + unsigned NumArgs = Outs.size(); + + for (unsigned i = 0; i != NumArgs; i++) { + MVT ArgVT = Outs[i].VT; + ISD::ArgFlagsTy ArgFlags = Outs[i].Flags; + + if (CC_RISCV(MF.getDataLayout(), i, ArgVT, ArgVT, CCValAssign::Full, + ArgFlags, CCInfo, Outs[i].IsFixed, IsRet)) { + DEBUG(dbgs() << "OutputArg #" << i << " has unhandled type " + << EVT(ArgVT).getEVTString() << "\n"); + llvm_unreachable(nullptr); + } + } +} + +// The caller is responsible for loading the full value if the argument is +// passed with CCValAssign::Indirect. +static SDValue unpackFromRegLoc(SelectionDAG &DAG, SDValue Chain, + const CCValAssign &VA, const SDLoc &DL) { + MachineFunction &MF = DAG.getMachineFunction(); + MachineRegisterInfo &RegInfo = MF.getRegInfo(); + EVT LocVT = VA.getLocVT(); + SDValue Val; + + unsigned VReg = RegInfo.createVirtualRegister(&RISCV::GPRRegClass); + RegInfo.addLiveIn(VA.getLocReg(), VReg); + Val = DAG.getCopyFromReg(Chain, DL, VReg, LocVT); + + switch (VA.getLocInfo()) { + default: + llvm_unreachable("Unexpected CCValAssign::LocInfo"); + case CCValAssign::Full: + case CCValAssign::Indirect: + return Val; + } +} + +// The caller is responsible for loading the full value if the argument is +// passed with CCValAssign::Indirect. +static SDValue unpackFromMemLoc(SelectionDAG &DAG, SDValue Chain, + const CCValAssign &VA, const SDLoc &DL) { + MachineFunction &MF = DAG.getMachineFunction(); + MachineFrameInfo &MFI = MF.getFrameInfo(); + EVT LocVT = VA.getLocVT(); + EVT ValVT = VA.getValVT(); + EVT PtrVT = MVT::getIntegerVT(DAG.getDataLayout().getPointerSizeInBits(0)); + int FI = MFI.CreateFixedObject(ValVT.getSizeInBits() / 8, + VA.getLocMemOffset(), /*Immutable=*/true); + SDValue FIN = DAG.getFrameIndex(FI, PtrVT); + SDValue Val; + + ISD::LoadExtType ExtType; + switch (VA.getLocInfo()) { + default: + llvm_unreachable("Unexpected CCValAssign::LocInfo"); + case CCValAssign::Full: + case CCValAssign::Indirect: + ExtType = ISD::NON_EXTLOAD; + break; + } + Val = DAG.getExtLoad( + ExtType, DL, LocVT, Chain, FIN, + MachinePointerInfo::getFixedStack(DAG.getMachineFunction(), FI), ValVT); + return Val; +} + +// Transform physical registers into virtual registers. +SDValue RISCVTargetLowering::LowerFormalArguments( + SDValue Chain, CallingConv::ID CallConv, bool IsVarArg, + const SmallVectorImpl<ISD::InputArg> &Ins, const SDLoc &DL, + SelectionDAG &DAG, SmallVectorImpl<SDValue> &InVals) const { + + switch (CallConv) { + default: + report_fatal_error("Unsupported calling convention"); + case CallingConv::C: + case CallingConv::Fast: + break; + } + + MachineFunction &MF = DAG.getMachineFunction(); + MVT XLenVT = Subtarget.getXLenVT(); + EVT PtrVT = getPointerTy(DAG.getDataLayout()); + + if (IsVarArg) + report_fatal_error("VarArg not supported"); + + // Assign locations to all of the incoming arguments. + SmallVector<CCValAssign, 16> ArgLocs; + CCState CCInfo(CallConv, IsVarArg, MF, ArgLocs, *DAG.getContext()); + analyzeInputArgs(MF, CCInfo, Ins, /*IsRet=*/false); + + for (unsigned i = 0, e = ArgLocs.size(); i != e; ++i) { + CCValAssign &VA = ArgLocs[i]; + assert(VA.getLocVT() == XLenVT && "Unhandled argument type"); + SDValue ArgValue; + if (VA.isRegLoc()) + ArgValue = unpackFromRegLoc(DAG, Chain, VA, DL); + else + ArgValue = unpackFromMemLoc(DAG, Chain, VA, DL); + + if (VA.getLocInfo() == CCValAssign::Indirect) { + // If the original argument was split and passed by reference (e.g. i128 + // on RV32), we need to load all parts of it here (using the same + // address). + InVals.push_back(DAG.getLoad(VA.getValVT(), DL, Chain, ArgValue, + MachinePointerInfo())); + unsigned ArgIndex = Ins[i].OrigArgIndex; + assert(Ins[i].PartOffset == 0); + while (i + 1 != e && Ins[i + 1].OrigArgIndex == ArgIndex) { + CCValAssign &PartVA = ArgLocs[i + 1]; + unsigned PartOffset = Ins[i + 1].PartOffset; + SDValue Address = DAG.getNode(ISD::ADD, DL, PtrVT, ArgValue, + DAG.getIntPtrConstant(PartOffset, DL)); + InVals.push_back(DAG.getLoad(PartVA.getValVT(), DL, Chain, Address, + MachinePointerInfo())); + ++i; + } + continue; + } + InVals.push_back(ArgValue); + } + return Chain; +} + +// Lower a call to a callseq_start + CALL + callseq_end chain, and add input +// and output parameter nodes. +SDValue RISCVTargetLowering::LowerCall(CallLoweringInfo &CLI, + SmallVectorImpl<SDValue> &InVals) const { + SelectionDAG &DAG = CLI.DAG; + SDLoc &DL = CLI.DL; + SmallVectorImpl<ISD::OutputArg> &Outs = CLI.Outs; + SmallVectorImpl<SDValue> &OutVals = CLI.OutVals; + SmallVectorImpl<ISD::InputArg> &Ins = CLI.Ins; + SDValue Chain = CLI.Chain; + SDValue Callee = CLI.Callee; + CLI.IsTailCall = false; + CallingConv::ID CallConv = CLI.CallConv; + bool IsVarArg = CLI.IsVarArg; + EVT PtrVT = getPointerTy(DAG.getDataLayout()); + MVT XLenVT = Subtarget.getXLenVT(); + + if (IsVarArg) { + report_fatal_error("LowerCall with varargs not implemented"); + } + + MachineFunction &MF = DAG.getMachineFunction(); + + // Analyze the operands of the call, assigning locations to each operand. + SmallVector<CCValAssign, 16> ArgLocs; + CCState ArgCCInfo(CallConv, IsVarArg, MF, ArgLocs, *DAG.getContext()); + analyzeOutputArgs(MF, ArgCCInfo, Outs, /*IsRet=*/false); + + // Get a count of how many bytes are to be pushed on the stack. + unsigned NumBytes = ArgCCInfo.getNextStackOffset(); + + // Create local copies for byval args + SmallVector<SDValue, 8> ByValArgs; + for (unsigned i = 0, e = Outs.size(); i != e; ++i) { + ISD::ArgFlagsTy Flags = Outs[i].Flags; + if (!Flags.isByVal()) + continue; + + SDValue Arg = OutVals[i]; + unsigned Size = Flags.getByValSize(); + unsigned Align = Flags.getByValAlign(); + + int FI = MF.getFrameInfo().CreateStackObject(Size, Align, /*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, + /*IsVolatile=*/false, + /*AlwaysInline=*/false, + /*isTailCall=*/false, MachinePointerInfo(), + MachinePointerInfo()); + ByValArgs.push_back(FIPtr); + } + + Chain = DAG.getCALLSEQ_START(Chain, NumBytes, 0, CLI.DL); + + // Copy argument values to their designated locations. + SmallVector<std::pair<unsigned, SDValue>, 8> RegsToPass; + SmallVector<SDValue, 8> MemOpChains; + SDValue StackPtr; + for (unsigned i = 0, j = 0, e = ArgLocs.size(); i != e; ++i) { + CCValAssign &VA = ArgLocs[i]; + SDValue ArgValue = OutVals[i]; + ISD::ArgFlagsTy Flags = Outs[i].Flags; + + // Promote the value if needed. + // For now, only handle fully promoted and indirect arguments. + switch (VA.getLocInfo()) { + case CCValAssign::Full: + break; + case CCValAssign::Indirect: { + // Store the argument in a stack slot and pass its address. + SDValue SpillSlot = DAG.CreateStackTemporary(Outs[i].ArgVT); + int FI = cast<FrameIndexSDNode>(SpillSlot)->getIndex(); + MemOpChains.push_back( + DAG.getStore(Chain, DL, ArgValue, SpillSlot, + MachinePointerInfo::getFixedStack(MF, FI))); + // If the original argument was split (e.g. i128), we need + // to store all parts of it here (and pass just one address). + unsigned ArgIndex = Outs[i].OrigArgIndex; + assert(Outs[i].PartOffset == 0); + while (i + 1 != e && Outs[i + 1].OrigArgIndex == ArgIndex) { + SDValue PartValue = OutVals[i + 1]; + unsigned PartOffset = Outs[i + 1].PartOffset; + SDValue Address = DAG.getNode(ISD::ADD, DL, PtrVT, SpillSlot, + DAG.getIntPtrConstant(PartOffset, DL)); + MemOpChains.push_back( + DAG.getStore(Chain, DL, PartValue, Address, + MachinePointerInfo::getFixedStack(MF, FI))); + ++i; + } + ArgValue = SpillSlot; + break; + } + default: + llvm_unreachable("Unknown loc info!"); + } + + // Use local copy if it is a byval arg. + if (Flags.isByVal()) + ArgValue = ByValArgs[j++]; + + if (VA.isRegLoc()) { + // Queue up the argument copies and emit them at the end. + RegsToPass.push_back(std::make_pair(VA.getLocReg(), ArgValue)); + } else { + assert(VA.isMemLoc() && "Argument not register or memory"); + + // Work out the address of the stack slot. + if (!StackPtr.getNode()) + StackPtr = DAG.getCopyFromReg(Chain, DL, RISCV::X2, PtrVT); + SDValue Address = + DAG.getNode(ISD::ADD, DL, PtrVT, StackPtr, + DAG.getIntPtrConstant(VA.getLocMemOffset(), DL)); + + // Emit the store. + MemOpChains.push_back( + DAG.getStore(Chain, DL, ArgValue, Address, MachinePointerInfo())); + } + } + + // Join the stores, which are independent of one another. + if (!MemOpChains.empty()) + Chain = DAG.getNode(ISD::TokenFactor, DL, MVT::Other, MemOpChains); + + SDValue Glue; + + // Build a sequence of copy-to-reg nodes, chained and glued together. + for (auto &Reg : RegsToPass) { + Chain = DAG.getCopyToReg(Chain, DL, Reg.first, Reg.second, Glue); + Glue = Chain.getValue(1); + } + + if (isa<GlobalAddressSDNode>(Callee)) { + Callee = lowerGlobalAddress(Callee, DAG); + } else if (isa<ExternalSymbolSDNode>(Callee)) { + Callee = lowerExternalSymbol(Callee, DAG); + } + + // The first call operand is the chain and the second is the target address. + SmallVector<SDValue, 8> Ops; + Ops.push_back(Chain); + Ops.push_back(Callee); + + // Add argument registers to the end of the list so that they are + // known live into the call. + for (auto &Reg : RegsToPass) + Ops.push_back(DAG.getRegister(Reg.first, Reg.second.getValueType())); + + // Add a register mask operand representing the call-preserved registers. + const TargetRegisterInfo *TRI = Subtarget.getRegisterInfo(); + const uint32_t *Mask = TRI->getCallPreservedMask(MF, CallConv); + assert(Mask && "Missing call preserved mask for calling convention"); + Ops.push_back(DAG.getRegisterMask(Mask)); + + // Glue the call to the argument copies, if any. + if (Glue.getNode()) + Ops.push_back(Glue); + + // Emit the call. + SDVTList NodeTys = DAG.getVTList(MVT::Other, MVT::Glue); + Chain = DAG.getNode(RISCVISD::CALL, DL, NodeTys, Ops); + Glue = Chain.getValue(1); + + // Mark the end of the call, which is glued to the call itself. + Chain = DAG.getCALLSEQ_END(Chain, + DAG.getConstant(NumBytes, DL, PtrVT, true), + DAG.getConstant(0, DL, PtrVT, true), + Glue, DL); + Glue = Chain.getValue(1); + + // Assign locations to each value returned by this call. + SmallVector<CCValAssign, 16> RVLocs; + CCState RetCCInfo(CallConv, IsVarArg, MF, RVLocs, *DAG.getContext()); + analyzeInputArgs(MF, RetCCInfo, Ins, /*IsRet=*/true); + + // Copy all of the result registers out of their specified physreg. + for (auto &VA : RVLocs) { + // Copy the value out, gluing the copy to the end of the call sequence. + SDValue RetValue = DAG.getCopyFromReg(Chain, DL, VA.getLocReg(), + VA.getLocVT(), Glue); + Chain = RetValue.getValue(1); + Glue = RetValue.getValue(2); + + assert(VA.getLocInfo() == CCValAssign::Full && "Unknown loc info!"); + InVals.push_back(RetValue); + } + + return Chain; +} + +bool RISCVTargetLowering::CanLowerReturn( + CallingConv::ID CallConv, MachineFunction &MF, bool IsVarArg, + const SmallVectorImpl<ISD::OutputArg> &Outs, LLVMContext &Context) const { + SmallVector<CCValAssign, 16> RVLocs; + CCState CCInfo(CallConv, IsVarArg, MF, RVLocs, Context); + for (unsigned i = 0, e = Outs.size(); i != e; ++i) { + MVT VT = Outs[i].VT; + ISD::ArgFlagsTy ArgFlags = Outs[i].Flags; + if (CC_RISCV(MF.getDataLayout(), i, VT, VT, CCValAssign::Full, ArgFlags, + CCInfo, /*IsFixed=*/true, /*IsRet=*/true)) + return false; + } + return true; +} + +SDValue +RISCVTargetLowering::LowerReturn(SDValue Chain, CallingConv::ID CallConv, + bool IsVarArg, + const SmallVectorImpl<ISD::OutputArg> &Outs, + const SmallVectorImpl<SDValue> &OutVals, + const SDLoc &DL, SelectionDAG &DAG) const { + if (IsVarArg) { + report_fatal_error("VarArg not supported"); + } + + // Stores the assignment of the return value to a location. + SmallVector<CCValAssign, 16> RVLocs; + + // Info about the registers and stack slot. + CCState CCInfo(CallConv, IsVarArg, DAG.getMachineFunction(), RVLocs, + *DAG.getContext()); + + analyzeOutputArgs(DAG.getMachineFunction(), CCInfo, Outs, /*IsRet=*/true); + + SDValue Flag; + SmallVector<SDValue, 4> RetOps(1, Chain); + + // Copy the result values into the output registers. + for (unsigned i = 0, e = RVLocs.size(); i < e; ++i) { + SDValue Val = OutVals[i]; + CCValAssign &VA = RVLocs[i]; + assert(VA.isRegLoc() && "Can only return in registers!"); + assert(VA.getLocInfo() == CCValAssign::Full && + "Unexpected CCValAssign::LocInfo"); + + Chain = DAG.getCopyToReg(Chain, DL, VA.getLocReg(), Val, Flag); + + // Guarantee that all emitted copies are stuck together. + Flag = Chain.getValue(1); + RetOps.push_back(DAG.getRegister(VA.getLocReg(), VA.getLocVT())); + } + + RetOps[0] = Chain; // Update chain. + + // Add the flag if we have it. + if (Flag.getNode()) { + RetOps.push_back(Flag); + } + + return DAG.getNode(RISCVISD::RET_FLAG, DL, MVT::Other, RetOps); +} + +const char *RISCVTargetLowering::getTargetNodeName(unsigned Opcode) const { + switch ((RISCVISD::NodeType)Opcode) { + case RISCVISD::FIRST_NUMBER: + break; + case RISCVISD::RET_FLAG: + return "RISCVISD::RET_FLAG"; + case RISCVISD::CALL: + return "RISCVISD::CALL"; + case RISCVISD::SELECT_CC: + return "RISCVISD::SELECT_CC"; + } + return nullptr; +} diff --git a/lib/Target/RISCV/RISCVISelLowering.h b/lib/Target/RISCV/RISCVISelLowering.h new file mode 100644 index 000000000000..9c5c7ca008c0 --- /dev/null +++ b/lib/Target/RISCV/RISCVISelLowering.h @@ -0,0 +1,84 @@ +//===-- RISCVISelLowering.h - RISCV DAG Lowering Interface ------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines the interfaces that RISCV uses to lower LLVM code into a +// selection DAG. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_RISCV_RISCVISELLOWERING_H +#define LLVM_LIB_TARGET_RISCV_RISCVISELLOWERING_H + +#include "RISCV.h" +#include "llvm/CodeGen/SelectionDAG.h" +#include "llvm/CodeGen/TargetLowering.h" + +namespace llvm { +class RISCVSubtarget; +namespace RISCVISD { +enum NodeType : unsigned { + FIRST_NUMBER = ISD::BUILTIN_OP_END, + RET_FLAG, + CALL, + SELECT_CC +}; +} + +class RISCVTargetLowering : public TargetLowering { + const RISCVSubtarget &Subtarget; + +public: + explicit RISCVTargetLowering(const TargetMachine &TM, + const RISCVSubtarget &STI); + + // Provide custom lowering hooks for some operations. + SDValue LowerOperation(SDValue Op, SelectionDAG &DAG) const override; + + // This method returns the name of a target specific DAG node. + const char *getTargetNodeName(unsigned Opcode) const override; + + MachineBasicBlock * + EmitInstrWithCustomInserter(MachineInstr &MI, + MachineBasicBlock *BB) 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) const; + // Lower incoming arguments, copy physregs into vregs + SDValue LowerFormalArguments(SDValue Chain, CallingConv::ID CallConv, + bool IsVarArg, + const SmallVectorImpl<ISD::InputArg> &Ins, + const SDLoc &DL, SelectionDAG &DAG, + SmallVectorImpl<SDValue> &InVals) const override; + bool CanLowerReturn(CallingConv::ID CallConv, MachineFunction &MF, + bool IsVarArg, + const SmallVectorImpl<ISD::OutputArg> &Outs, + LLVMContext &Context) const override; + SDValue LowerReturn(SDValue Chain, CallingConv::ID CallConv, bool IsVarArg, + const SmallVectorImpl<ISD::OutputArg> &Outs, + const SmallVectorImpl<SDValue> &OutVals, const SDLoc &DL, + 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; + } + SDValue lowerGlobalAddress(SDValue Op, SelectionDAG &DAG) const; + SDValue lowerBlockAddress(SDValue Op, SelectionDAG &DAG) const; + SDValue lowerExternalSymbol(SDValue Op, SelectionDAG &DAG) const; + SDValue lowerSELECT(SDValue Op, SelectionDAG &DAG) const; +}; +} + +#endif diff --git a/lib/Target/RISCV/RISCVInstrFormats.td b/lib/Target/RISCV/RISCVInstrFormats.td index 3fab7122f6f1..7479ffbc9532 100644 --- a/lib/Target/RISCV/RISCVInstrFormats.td +++ b/lib/Target/RISCV/RISCVInstrFormats.td @@ -25,9 +25,66 @@ // //===----------------------------------------------------------------------===// -class RISCVInst<dag outs, dag ins, string asmstr, list<dag> pattern> +// Format specifies the encoding used by the instruction. This is used by +// RISCVMCCodeEmitter to determine which form of fixup to use. These +// definitions must be kept in-sync with RISCVBaseInfo.h. +class InstFormat<bits<5> val> { + bits<5> Value = val; +} +def InstFormatPseudo : InstFormat<0>; +def InstFormatR : InstFormat<1>; +def InstFormatR4 : InstFormat<2>; +def InstFormatI : InstFormat<3>; +def InstFormatS : InstFormat<4>; +def InstFormatB : InstFormat<5>; +def InstFormatU : InstFormat<6>; +def InstFormatJ : InstFormat<7>; +def InstFormatCR : InstFormat<8>; +def InstFormatCI : InstFormat<9>; +def InstFormatCSS : InstFormat<10>; +def InstFormatCIW : InstFormat<11>; +def InstFormatCL : InstFormat<12>; +def InstFormatCS : InstFormat<13>; +def InstFormatCB : InstFormat<14>; +def InstFormatCJ : InstFormat<15>; +def InstFormatOther : InstFormat<16>; + +// The following opcode names and 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> { + bits<7> Value = val; +} +def OPC_LOAD : RISCVOpcode<0b0000011>; +def OPC_LOAD_FP : RISCVOpcode<0b0000111>; +def OPC_MISC_MEM : RISCVOpcode<0b0001111>; +def OPC_OP_IMM : RISCVOpcode<0b0010011>; +def OPC_AUIPC : RISCVOpcode<0b0010111>; +def OPC_OP_IMM_32 : RISCVOpcode<0b0011011>; +def OPC_STORE : RISCVOpcode<0b0100011>; +def OPC_STORE_FP : RISCVOpcode<0b0100111>; +def OPC_AMO : RISCVOpcode<0b0101111>; +def OPC_OP : RISCVOpcode<0b0110011>; +def OPC_LUI : RISCVOpcode<0b0110111>; +def OPC_OP_32 : RISCVOpcode<0b0111011>; +def OPC_MADD : RISCVOpcode<0b1000011>; +def OPC_MSUB : RISCVOpcode<0b1000111>; +def OPC_NMSUB : RISCVOpcode<0b1001011>; +def OPC_NMADD : RISCVOpcode<0b1001111>; +def OPC_OP_FP : RISCVOpcode<0b1010011>; +def OPC_BRANCH : RISCVOpcode<0b1100011>; +def OPC_JALR : RISCVOpcode<0b1100111>; +def OPC_JAL : RISCVOpcode<0b1101111>; +def OPC_SYSTEM : RISCVOpcode<0b1110011>; + +class RVInst<dag outs, dag ins, string opcodestr, string argstr, + list<dag> pattern, InstFormat format> : Instruction { field bits<32> Inst; + // SoftFail is a field the disassembler can use to provide a way for + // instructions to not match without killing the whole decode process. It is + // mainly used for ARM, but Tablegen expects this field to exist or it fails + // to build the decode table. + field bits<32> SoftFail = 0; let Size = 4; bits<7> Opcode = 0; @@ -38,20 +95,26 @@ class RISCVInst<dag outs, dag ins, string asmstr, list<dag> pattern> dag OutOperandList = outs; dag InOperandList = ins; - let AsmString = asmstr; + let AsmString = opcodestr # "\t" # argstr; let Pattern = pattern; + + let TSFlags{4-0} = format.Value; } // Pseudo instructions -class Pseudo<dag outs, dag ins, string asmstr, list<dag> pattern> - : RISCVInst<outs, ins, "", pattern> { +class Pseudo<dag outs, dag ins, list<dag> pattern> + : RVInst<outs, ins, "", "", pattern, InstFormatPseudo> { let isPseudo = 1; let isCodeGenOnly = 1; } -class FR<bits<7> funct7, bits<3> funct3, bits<7> opcode, dag outs, dag ins, - string asmstr, list<dag> pattern> : RISCVInst<outs, ins, asmstr, pattern> -{ +// Instruction formats are listed in the order they appear in the RISC-V +// instruction set manual (R, I, S, B, U, J) with sub-formats (e.g. RVInstR4, +// RVInstRAtomic) sorted alphabetically. + +class RVInstR<bits<7> funct7, bits<3> funct3, RISCVOpcode opcode, dag outs, + dag ins, string opcodestr, string argstr> + : RVInst<outs, ins, opcodestr, argstr, [], InstFormatR> { bits<5> rs2; bits<5> rs1; bits<5> rd; @@ -61,12 +124,64 @@ class FR<bits<7> funct7, bits<3> funct3, bits<7> opcode, dag outs, dag ins, let Inst{19-15} = rs1; let Inst{14-12} = funct3; let Inst{11-7} = rd; - let Opcode = opcode; + let Opcode = opcode.Value; +} + +class RVInstR4<bits<2> funct2, RISCVOpcode opcode, dag outs, dag ins, + string opcodestr, string argstr> + : RVInst<outs, ins, opcodestr, argstr, [], InstFormatR4> { + bits<5> rs3; + bits<5> rs2; + bits<5> rs1; + bits<3> funct3; + bits<5> rd; + + let Inst{31-27} = rs3; + let Inst{26-25} = funct2; + let Inst{24-20} = rs2; + let Inst{19-15} = rs1; + let Inst{14-12} = funct3; + let Inst{11-7} = rd; + let Opcode = opcode.Value; } -class FI<bits<3> funct3, bits<7> opcode, dag outs, dag ins, string asmstr, list<dag> pattern> - : RISCVInst<outs, ins, asmstr, pattern> -{ +class RVInstRAtomic<bits<5> funct5, bit aq, bit rl, bits<3> funct3, + RISCVOpcode opcode, 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-27} = funct5; + let Inst{26} = aq; + let Inst{25} = rl; + let Inst{24-20} = rs2; + let Inst{19-15} = rs1; + let Inst{14-12} = funct3; + let Inst{11-7} = rd; + let Opcode = opcode.Value; +} + +class RVInstRFrm<bits<7> funct7, RISCVOpcode opcode, dag outs, dag ins, + string opcodestr, string argstr> + : RVInst<outs, ins, opcodestr, argstr, [], InstFormatR> { + bits<5> rs2; + bits<5> rs1; + bits<3> funct3; + bits<5> rd; + + let Inst{31-25} = funct7; + let Inst{24-20} = rs2; + let Inst{19-15} = rs1; + let Inst{14-12} = funct3; + let Inst{11-7} = rd; + let Opcode = opcode.Value; +} + +class RVInstI<bits<3> funct3, RISCVOpcode opcode, dag outs, dag ins, + string opcodestr, string argstr> + : RVInst<outs, ins, opcodestr, argstr, [], InstFormatI> { bits<12> imm12; bits<5> rs1; bits<5> rd; @@ -75,12 +190,29 @@ class FI<bits<3> funct3, bits<7> opcode, dag outs, dag ins, string asmstr, list< let Inst{19-15} = rs1; let Inst{14-12} = funct3; let Inst{11-7} = rd; - let Opcode = opcode; + let Opcode = opcode.Value; +} + +class RVInstIShift<bit arithshift, bits<3> funct3, RISCVOpcode opcode, + dag outs, dag ins, string opcodestr, string argstr> + : RVInst<outs, ins, opcodestr, argstr, [], InstFormatI> { + bits<6> shamt; + bits<5> rs1; + bits<5> rd; + + let Inst{31} = 0; + let Inst{30} = arithshift; + let Inst{29-26} = 0; + let Inst{25-20} = shamt; + let Inst{19-15} = rs1; + let Inst{14-12} = funct3; + let Inst{11-7} = rd; + let Opcode = opcode.Value; } -class FI32Shift<bit arithshift, bits<3> funct3, bits<7> opcode, dag outs, dag ins, string asmstr, list<dag> pattern> - : RISCVInst<outs, ins, asmstr, pattern> -{ +class RVInstIShiftW<bit arithshift, bits<3> funct3, RISCVOpcode opcode, + dag outs, dag ins, string opcodestr, string argstr> + : RVInst<outs, ins, opcodestr, argstr, [], InstFormatI> { bits<5> shamt; bits<5> rs1; bits<5> rd; @@ -92,12 +224,12 @@ class FI32Shift<bit arithshift, bits<3> funct3, bits<7> opcode, dag outs, dag in let Inst{19-15} = rs1; let Inst{14-12} = funct3; let Inst{11-7} = rd; - let Opcode = opcode; + let Opcode = opcode.Value; } -class FS<bits<3> funct3, bits<7> opcode, dag outs, dag ins, string asmstr, list<dag> pattern> - : RISCVInst<outs, ins, asmstr, pattern> -{ +class RVInstS<bits<3> funct3, RISCVOpcode opcode, dag outs, dag ins, + string opcodestr, string argstr> + : RVInst<outs, ins, opcodestr, argstr, [], InstFormatS> { bits<12> imm12; bits<5> rs2; bits<5> rs1; @@ -107,12 +239,12 @@ class FS<bits<3> funct3, bits<7> opcode, dag outs, dag ins, string asmstr, list< let Inst{19-15} = rs1; let Inst{14-12} = funct3; let Inst{11-7} = imm12{4-0}; - let Opcode = opcode; + let Opcode = opcode.Value; } -class FSB<bits<3> funct3, bits<7> opcode, dag outs, dag ins, string asmstr, list<dag> pattern> - : RISCVInst<outs, ins, asmstr, pattern> -{ +class RVInstB<bits<3> funct3, RISCVOpcode opcode, dag outs, dag ins, + string opcodestr, string argstr> + : RVInst<outs, ins, opcodestr, argstr, [], InstFormatB> { bits<12> imm12; bits<5> rs2; bits<5> rs1; @@ -124,23 +256,23 @@ class FSB<bits<3> funct3, bits<7> opcode, dag outs, dag ins, string asmstr, list let Inst{14-12} = funct3; let Inst{11-8} = imm12{3-0}; let Inst{7} = imm12{10}; - let Opcode = opcode; + let Opcode = opcode.Value; } -class FU<bits<7> opcode, dag outs, dag ins, string asmstr, list<dag> pattern> - : RISCVInst<outs, ins, asmstr, pattern> -{ +class RVInstU<RISCVOpcode opcode, dag outs, dag ins, string opcodestr, + string argstr> + : RVInst<outs, ins, opcodestr, argstr, [], InstFormatU> { bits<20> imm20; bits<5> rd; let Inst{31-12} = imm20; let Inst{11-7} = rd; - let Opcode = opcode; + let Opcode = opcode.Value; } -class FUJ<bits<7> opcode, dag outs, dag ins, string asmstr, list<dag> pattern> - : RISCVInst<outs, ins, asmstr, pattern> -{ +class RVInstJ<RISCVOpcode opcode, dag outs, dag ins, string opcodestr, + string argstr> + : RVInst<outs, ins, opcodestr, argstr, [], InstFormatJ> { bits<20> imm20; bits<5> rd; @@ -149,5 +281,5 @@ class FUJ<bits<7> opcode, dag outs, dag ins, string asmstr, list<dag> pattern> let Inst{20} = imm20{10}; let Inst{19-12} = imm20{18-11}; let Inst{11-7} = rd; - let Opcode = opcode; + let Opcode = opcode.Value; } diff --git a/lib/Target/RISCV/RISCVInstrFormatsC.td b/lib/Target/RISCV/RISCVInstrFormatsC.td new file mode 100644 index 000000000000..6abcbd7cc8a1 --- /dev/null +++ b/lib/Target/RISCV/RISCVInstrFormatsC.td @@ -0,0 +1,147 @@ +//===-- RISCVInstrFormatsC.td - RISCV C Instruction Formats --*- tablegen -*-=// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file describes the RISC-V C extension instruction formats. +// +//===----------------------------------------------------------------------===// + +class RVInst16<dag outs, dag ins, string opcodestr, string argstr, + list<dag> pattern, InstFormat format> + : Instruction { + field bits<16> Inst; + // SoftFail is a field the disassembler can use to provide a way for + // instructions to not match without killing the whole decode process. It is + // mainly used for ARM, but Tablegen expects this field to exist or it fails + // to build the decode table. + field bits<16> SoftFail = 0; + let Size = 2; + + bits<2> Opcode = 0; + + let Namespace = "RISCV"; + + dag OutOperandList = outs; + dag InOperandList = ins; + let AsmString = opcodestr # "\t" # argstr; + let Pattern = pattern; + + let TSFlags{4-0} = format.Value; +} + +class RVInst16CR<bits<4> funct4, bits<2> opcode, dag outs, dag ins, + string opcodestr, string argstr> + : RVInst16<outs, ins, opcodestr, argstr, [], InstFormatCR> { + bits<5> rs1; + bits<5> rs2; + + let Inst{15-12} = funct4; + let Inst{11-7} = rs1; + let Inst{6-2} = rs2; + let Inst{1-0} = opcode; +} + +// The immediate value encoding differs for each instruction, so each subclass +// is responsible for setting the appropriate bits in the Inst field. +// The bits Inst{6-2} must be set for each instruction. +class RVInst16CI<bits<3> funct3, bits<2> opcode, dag outs, dag ins, + string opcodestr, string argstr> + : RVInst16<outs, ins, opcodestr, argstr, [], InstFormatCI> { + bits<10> imm; + bits<5> rd; + bits<5> rs1; + + let Inst{15-13} = funct3; + let Inst{12} = imm{5}; + let Inst{11-7} = rd; + let Inst{1-0} = opcode; +} + +// The immediate value encoding differs for each instruction, so each subclass +// is responsible for setting the appropriate bits in the Inst field. +// The bits Inst{12-7} must be set for each instruction. +class RVInst16CSS<bits<3> funct3, bits<2> opcode, dag outs, dag ins, + string opcodestr, string argstr> + : RVInst16<outs, ins, opcodestr, argstr, [], InstFormatCSS> { + bits<10> imm; + bits<5> rs2; + bits<5> rs1; + + let Inst{15-13} = funct3; + let Inst{6-2} = rs2; + let Inst{1-0} = opcode; +} + +class RVInst16CIW<bits<3> funct3, bits<2> opcode, dag outs, dag ins, + string opcodestr, string argstr> + : RVInst16<outs, ins, opcodestr, argstr, [], InstFormatCIW> { + bits<10> imm; + bits<3> rd; + + let Inst{15-13} = funct3; + let Inst{4-2} = rd; + let Inst{1-0} = opcode; +} + +// The immediate value encoding differs for each instruction, so each subclass +// is responsible for setting the appropriate bits in the Inst field. +// The bits Inst{12-10} and Inst{6-5} must be set for each instruction. +class RVInst16CL<bits<3> funct3, bits<2> opcode, dag outs, dag ins, + string opcodestr, string argstr> + : RVInst16<outs, ins, opcodestr, argstr, [], InstFormatCL> { + bits<3> rd; + bits<3> rs1; + + let Inst{15-13} = funct3; + let Inst{9-7} = rs1; + let Inst{4-2} = rd; + let Inst{1-0} = opcode; +} + +// The immediate value encoding differs for each instruction, so each subclass +// is responsible for setting the appropriate bits in the Inst field. +// The bits Inst{12-10} and Inst{6-5} must be set for each instruction. +class RVInst16CS<bits<3> funct3, bits<2> opcode, dag outs, dag ins, + string opcodestr, string argstr> + : RVInst16<outs, ins, opcodestr, argstr, [], InstFormatCS> { + bits<3> rs2; + bits<3> rs1; + + let Inst{15-13} = funct3; + let Inst{9-7} = rs1; + let Inst{4-2} = rs2; + let Inst{1-0} = opcode; +} + +class RVInst16CB<bits<3> funct3, bits<2> opcode, dag outs, dag ins, + string opcodestr, string argstr> + : RVInst16<outs, ins, opcodestr, argstr, [], InstFormatCB> { + bits<9> imm; + bits<3> rs1; + + let Inst{15-13} = funct3; + let Inst{9-7} = rs1; + let Inst{1-0} = opcode; +} + +class RVInst16CJ<bits<3> funct3, bits<2> opcode, dag outs, dag ins, + string opcodestr, string argstr> + : RVInst16<outs, ins, opcodestr, argstr, [], InstFormatCJ> { + bits<11> offset; + + let Inst{15-13} = funct3; + let Inst{12} = offset{10}; + let Inst{11} = offset{3}; + let Inst{10-9} = offset{8-7}; + let Inst{8} = offset{9}; + let Inst{7} = offset{5}; + let Inst{6} = offset{6}; + let Inst{5-3} = offset{2-0}; + let Inst{2} = offset{4}; + let Inst{1-0} = opcode; +} diff --git a/lib/Target/RISCV/RISCVInstrInfo.cpp b/lib/Target/RISCV/RISCVInstrInfo.cpp new file mode 100644 index 000000000000..186fe363edd9 --- /dev/null +++ b/lib/Target/RISCV/RISCVInstrInfo.cpp @@ -0,0 +1,77 @@ +//===-- RISCVInstrInfo.cpp - RISCV Instruction Information ------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file contains the RISCV implementation of the TargetInstrInfo class. +// +//===----------------------------------------------------------------------===// + +#include "RISCVInstrInfo.h" +#include "RISCV.h" +#include "RISCVSubtarget.h" +#include "RISCVTargetMachine.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/CodeGen/MachineFunctionPass.h" +#include "llvm/CodeGen/MachineInstrBuilder.h" +#include "llvm/CodeGen/MachineRegisterInfo.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/TargetRegistry.h" + +#define GET_INSTRINFO_CTOR_DTOR +#include "RISCVGenInstrInfo.inc" + +using namespace llvm; + +RISCVInstrInfo::RISCVInstrInfo() + : RISCVGenInstrInfo(RISCV::ADJCALLSTACKDOWN, RISCV::ADJCALLSTACKUP) {} + +void RISCVInstrInfo::copyPhysReg(MachineBasicBlock &MBB, + MachineBasicBlock::iterator MBBI, + const DebugLoc &DL, unsigned DstReg, + unsigned SrcReg, bool KillSrc) const { + assert(RISCV::GPRRegClass.contains(DstReg, SrcReg) && + "Impossible reg-to-reg copy"); + + BuildMI(MBB, MBBI, DL, get(RISCV::ADDI), DstReg) + .addReg(SrcReg, getKillRegState(KillSrc)) + .addImm(0); +} + +void RISCVInstrInfo::storeRegToStackSlot(MachineBasicBlock &MBB, + MachineBasicBlock::iterator I, + unsigned SrcReg, bool IsKill, int FI, + const TargetRegisterClass *RC, + const TargetRegisterInfo *TRI) const { + DebugLoc DL; + if (I != MBB.end()) + DL = I->getDebugLoc(); + + if (RISCV::GPRRegClass.hasSubClassEq(RC)) + BuildMI(MBB, I, DL, get(RISCV::SW)) + .addReg(SrcReg, getKillRegState(IsKill)) + .addFrameIndex(FI) + .addImm(0); + else + llvm_unreachable("Can't store this register to stack slot"); +} + +void RISCVInstrInfo::loadRegFromStackSlot(MachineBasicBlock &MBB, + MachineBasicBlock::iterator I, + unsigned DstReg, int FI, + const TargetRegisterClass *RC, + const TargetRegisterInfo *TRI) const { + DebugLoc DL; + if (I != MBB.end()) + DL = I->getDebugLoc(); + + if (RISCV::GPRRegClass.hasSubClassEq(RC)) + BuildMI(MBB, I, DL, get(RISCV::LW), DstReg).addFrameIndex(FI).addImm(0); + else + llvm_unreachable("Can't load this register from stack slot"); +} diff --git a/lib/Target/RISCV/RISCVInstrInfo.h b/lib/Target/RISCV/RISCVInstrInfo.h new file mode 100644 index 000000000000..05c8378445cf --- /dev/null +++ b/lib/Target/RISCV/RISCVInstrInfo.h @@ -0,0 +1,46 @@ +//===-- RISCVInstrInfo.h - RISCV Instruction Information --------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file contains the RISCV implementation of the TargetInstrInfo class. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_RISCV_RISCVINSTRINFO_H +#define LLVM_LIB_TARGET_RISCV_RISCVINSTRINFO_H + +#include "RISCVRegisterInfo.h" +#include "llvm/CodeGen/TargetInstrInfo.h" + +#define GET_INSTRINFO_HEADER +#include "RISCVGenInstrInfo.inc" + +namespace llvm { + +class RISCVInstrInfo : public RISCVGenInstrInfo { + +public: + RISCVInstrInfo(); + + void copyPhysReg(MachineBasicBlock &MBB, MachineBasicBlock::iterator MBBI, + const DebugLoc &DL, unsigned DstReg, unsigned SrcReg, + bool KillSrc) const override; + + void storeRegToStackSlot(MachineBasicBlock &MBB, + MachineBasicBlock::iterator MBBI, unsigned SrcReg, + bool IsKill, int FrameIndex, + const TargetRegisterClass *RC, + const TargetRegisterInfo *TRI) const override; + + void loadRegFromStackSlot(MachineBasicBlock &MBB, + MachineBasicBlock::iterator MBBI, unsigned DstReg, + int FrameIndex, const TargetRegisterClass *RC, + const TargetRegisterInfo *TRI) const override; +}; +} +#endif diff --git a/lib/Target/RISCV/RISCVInstrInfo.td b/lib/Target/RISCV/RISCVInstrInfo.td index 52530c2f136c..1aae2f39dbdd 100644 --- a/lib/Target/RISCV/RISCVInstrInfo.td +++ b/lib/Target/RISCV/RISCVInstrInfo.td @@ -13,22 +13,247 @@ include "RISCVInstrFormats.td" -def simm12 : Operand<i32>; +//===----------------------------------------------------------------------===// +// RISC-V specific DAG Nodes. +//===----------------------------------------------------------------------===// + +def SDT_RISCVCall : SDTypeProfile<0, -1, [SDTCisVT<0, XLenVT>]>; +def SDT_RISCVCallSeqStart : SDCallSeqStart<[SDTCisVT<0, i32>, + SDTCisVT<1, i32>]>; +def SDT_RISCVCallSeqEnd : SDCallSeqEnd<[SDTCisVT<0, i32>, + SDTCisVT<1, i32>]>; +def SDT_RISCVSelectCC : SDTypeProfile<1, 5, [SDTCisSameAs<1, 2>, + SDTCisSameAs<0, 4>, + SDTCisSameAs<4, 5>]>; + + +def Call : SDNode<"RISCVISD::CALL", SDT_RISCVCall, + [SDNPHasChain, SDNPOptInGlue, SDNPOutGlue, + SDNPVariadic]>; +def CallSeqStart : SDNode<"ISD::CALLSEQ_START", SDT_RISCVCallSeqStart, + [SDNPHasChain, SDNPOutGlue]>; +def CallSeqEnd : SDNode<"ISD::CALLSEQ_END", SDT_RISCVCallSeqEnd, + [SDNPHasChain, SDNPOptInGlue, SDNPOutGlue]>; +def RetFlag : SDNode<"RISCVISD::RET_FLAG", SDTNone, + [SDNPHasChain, SDNPOptInGlue, SDNPVariadic]>; +def SelectCC : SDNode<"RISCVISD::SELECT_CC", SDT_RISCVSelectCC, + [SDNPInGlue]>; + +//===----------------------------------------------------------------------===// +// Operand and SDNode transformation definitions. +//===----------------------------------------------------------------------===// + +class ImmAsmOperand<string prefix, int width, string suffix> : AsmOperandClass { + let Name = prefix # "Imm" # width # suffix; + let RenderMethod = "addImmOperands"; + let DiagnosticType = !strconcat("Invalid", Name); +} + +class SImmAsmOperand<int width, string suffix = ""> + : ImmAsmOperand<"S", width, suffix> { +} + +class UImmAsmOperand<int width, string suffix = ""> + : ImmAsmOperand<"U", width, suffix> { +} + +def FenceArg : AsmOperandClass { + let Name = "FenceArg"; + let RenderMethod = "addFenceArgOperands"; + let DiagnosticType = "InvalidFenceArg"; +} + +def fencearg : Operand<XLenVT> { + let ParserMatchClass = FenceArg; + let PrintMethod = "printFenceArg"; + let DecoderMethod = "decodeUImmOperand<4>"; +} + +def UImmLog2XLenAsmOperand : AsmOperandClass { + let Name = "UImmLog2XLen"; + let RenderMethod = "addImmOperands"; + let DiagnosticType = "InvalidUImmLog2XLen"; +} + +def uimmlog2xlen : Operand<XLenVT>, ImmLeaf<XLenVT, [{ + if (Subtarget->is64Bit()) + return isUInt<6>(Imm); + return isUInt<5>(Imm); +}]> { + let ParserMatchClass = UImmLog2XLenAsmOperand; + // TODO: should ensure invalid shamt is rejected when decoding. + let DecoderMethod = "decodeUImmOperand<6>"; +} + +def uimm5 : Operand<XLenVT>, ImmLeaf<XLenVT, [{return isUInt<5>(Imm);}]> { + let ParserMatchClass = UImmAsmOperand<5>; + let DecoderMethod = "decodeUImmOperand<5>"; +} -// As noted in RISCVRegisterInfo.td, the hope is that support for -// variable-sized register classes will mean that instruction definitions do -// not need to be duplicated for 32-bit and 64-bit register classes. For now -// we use 'GPR', which is 32-bit. When codegen for both RV32 and RV64 is -// added, we will need to duplicate instruction definitions unless a proposal -// like <http://lists.llvm.org/pipermail/llvm-dev/2016-September/105027.html> -// is adopted. +def simm12 : Operand<XLenVT>, ImmLeaf<XLenVT, [{return isInt<12>(Imm);}]> { + let ParserMatchClass = SImmAsmOperand<12>; + let EncoderMethod = "getImmOpValue"; + let DecoderMethod = "decodeSImmOperand<12>"; +} + +def uimm12 : Operand<XLenVT> { + let ParserMatchClass = UImmAsmOperand<12>; + let DecoderMethod = "decodeUImmOperand<12>"; +} + +// A 13-bit signed immediate where the least significant bit is zero. +def simm13_lsb0 : Operand<OtherVT> { + let ParserMatchClass = SImmAsmOperand<13, "Lsb0">; + let EncoderMethod = "getImmOpValueAsr1"; + let DecoderMethod = "decodeSImmOperandAndLsl1<13>"; +} -class ALU_ri<bits<3> funct3, string OpcodeStr> : - FI<funct3, 0b0010011, (outs GPR:$rd), (ins GPR:$rs1, simm12:$imm12), - OpcodeStr#"\t$rd, $rs1, $imm12", []> -{ +def uimm20 : Operand<XLenVT> { + let ParserMatchClass = UImmAsmOperand<20>; + let EncoderMethod = "getImmOpValue"; + let DecoderMethod = "decodeUImmOperand<20>"; } +// A 21-bit signed immediate where the least significant bit is zero. +def simm21_lsb0 : Operand<OtherVT> { + let ParserMatchClass = SImmAsmOperand<21, "Lsb0">; + let EncoderMethod = "getImmOpValueAsr1"; + let DecoderMethod = "decodeSImmOperandAndLsl1<21>"; +} + +// A parameterized register class alternative to i32imm/i64imm from Target.td. +def ixlenimm : Operand<XLenVT>; + +// Standalone (codegen-only) immleaf patterns. +def simm32 : ImmLeaf<XLenVT, [{return isInt<32>(Imm);}]>; + +// Addressing modes. +// Necessary because a frameindex can't be matched directly in a pattern. +def AddrFI : ComplexPattern<iPTR, 1, "SelectAddrFI", [frameindex], []>; + +// Extract least significant 12 bits from an immediate value and sign extend +// them. +def LO12Sext : SDNodeXForm<imm, [{ + return CurDAG->getTargetConstant(SignExtend64<12>(N->getZExtValue()), + SDLoc(N), N->getValueType(0)); +}]>; + +// Extract the most significant 20 bits from an immediate value. Add 1 if bit +// 11 is 1, to compensate for the low 12 bits in the matching immediate addi +// or ld/st being negative. +def HI20 : SDNodeXForm<imm, [{ + return CurDAG->getTargetConstant(((N->getZExtValue()+0x800) >> 12) & 0xfffff, + SDLoc(N), N->getValueType(0)); +}]>; + +//===----------------------------------------------------------------------===// +// Instruction Class Templates +//===----------------------------------------------------------------------===// + +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"> { + let isBranch = 1; + let isTerminator = 1; +} + +let hasSideEffects = 0, mayLoad = 1, mayStore = 0 in +class Load_ri<bits<3> funct3, string opcodestr> + : RVInstI<funct3, OPC_LOAD, (outs GPR:$rd), (ins GPR:$rs1, simm12:$imm12), + opcodestr, "$rd, ${imm12}(${rs1})">; + +// Operands for stores are in the order srcreg, base, offset rather than +// reflecting the order these fields are specified in the instruction +// encoding. +let hasSideEffects = 0, mayLoad = 0, mayStore = 1 in +class Store_rri<bits<3> funct3, string opcodestr> + : RVInstS<funct3, OPC_STORE, (outs), + (ins GPR:$rs2, GPR:$rs1, simm12:$imm12), + opcodestr, "$rs2, ${imm12}(${rs1})">; + +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">; + +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">; + +let hasSideEffects = 0, mayLoad = 0, mayStore = 0 in +class ALU_rr<bits<7> funct7, bits<3> funct3, string opcodestr> + : RVInstR<funct7, funct3, OPC_OP, (outs GPR:$rd), (ins GPR:$rs1, GPR:$rs2), + opcodestr, "$rd, $rs1, $rs2">; + +let hasSideEffects = 1, mayLoad = 0, mayStore = 0 in +class CSR_ir<bits<3> funct3, string opcodestr> + : RVInstI<funct3, OPC_SYSTEM, (outs GPR:$rd), (ins uimm12:$imm12, GPR:$rs1), + opcodestr, "$rd, $imm12, $rs1">; + +let hasSideEffects = 1, mayLoad = 0, mayStore = 0 in +class CSR_ii<bits<3> funct3, string opcodestr> + : RVInstI<funct3, OPC_SYSTEM, (outs GPR:$rd), + (ins uimm12:$imm12, uimm5:$rs1), + opcodestr, "$rd, $imm12, $rs1">; + +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">; + +let hasSideEffects = 0, mayLoad = 0, mayStore = 0 in +class ALUW_rr<bits<7> funct7, bits<3> funct3, string opcodestr> + : RVInstR<funct7, funct3, OPC_OP_32, (outs GPR:$rd), + (ins GPR:$rs1, GPR:$rs2), opcodestr, "$rd, $rs1, $rs2">; + +let hasSideEffects = 1, mayLoad = 0, mayStore = 0 in +class Priv<string opcodestr, bits<7> funct7> + : RVInstR<funct7, 0b000, OPC_SYSTEM, (outs), (ins GPR:$rs1, GPR:$rs2), + opcodestr, "">; + +//===----------------------------------------------------------------------===// +// Instructions +//===----------------------------------------------------------------------===// + +let hasSideEffects = 0, mayLoad = 0, mayStore = 0 in { +def LUI : RVInstU<OPC_LUI, (outs GPR:$rd), (ins uimm20:$imm20), + "lui", "$rd, $imm20">; + +def AUIPC : RVInstU<OPC_AUIPC, (outs GPR:$rd), (ins uimm20:$imm20), + "auipc", "$rd, $imm20">; + +let isCall = 1 in +def JAL : RVInstJ<OPC_JAL, (outs GPR:$rd), (ins simm21_lsb0:$imm20), + "jal", "$rd, $imm20">; + +let isCall = 1 in +def JALR : RVInstI<0b000, OPC_JALR, (outs GPR:$rd), + (ins GPR:$rs1, simm12:$imm12), + "jalr", "$rd, $rs1, $imm12">; +} // hasSideEffects = 0, mayLoad = 0, mayStore = 0 + +def BEQ : BranchCC_rri<0b000, "beq">; +def BNE : BranchCC_rri<0b001, "bne">; +def BLT : BranchCC_rri<0b100, "blt">; +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 SB : Store_rri<0b000, "sb">; +def SH : Store_rri<0b001, "sh">; +def SW : Store_rri<0b010, "sw">; + def ADDI : ALU_ri<0b000, "addi">; def SLTI : ALU_ri<0b010, "slti">; def SLTIU : ALU_ri<0b011, "sltiu">; @@ -36,11 +261,9 @@ def XORI : ALU_ri<0b100, "xori">; def ORI : ALU_ri<0b110, "ori">; def ANDI : ALU_ri<0b111, "andi">; -class ALU_rr<bits<7> funct7, bits<3> funct3, string OpcodeStr> : - FR<funct7, funct3, 0b0110011, (outs GPR:$rd), (ins GPR:$rs1, GPR:$rs2), - OpcodeStr#"\t$rd, $rs1, $rs2", []> -{ -} +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">; @@ -53,3 +276,377 @@ def SRA : ALU_rr<0b0100000, 0b101, "sra">; def OR : ALU_rr<0b0000000, 0b110, "or">; def AND : ALU_rr<0b0000000, 0b111, "and">; +let hasSideEffects = 1, mayLoad = 0, mayStore = 0 in { +def FENCE : RVInstI<0b000, OPC_MISC_MEM, (outs), + (ins fencearg:$pred, fencearg:$succ), + "fence", "$pred, $succ"> { + bits<4> pred; + bits<4> succ; + + let rs1 = 0; + let rd = 0; + let imm12 = {0b0000,pred,succ}; +} + +def FENCE_I : RVInstI<0b001, OPC_MISC_MEM, (outs), (ins), "fence.i", ""> { + let rs1 = 0; + let rd = 0; + let imm12 = 0; +} + +def ECALL : RVInstI<0b000, OPC_SYSTEM, (outs), (ins), "ecall", ""> { + let rs1 = 0; + let rd = 0; + let imm12 = 0; +} + +def EBREAK : RVInstI<0b000, OPC_SYSTEM, (outs), (ins), "ebreak", ""> { + let rs1 = 0; + let rd = 0; + let imm12 = 1; +} +} // hasSideEffects = 1, mayLoad = 0, mayStore = 0 + +def CSRRW : CSR_ir<0b001, "csrrw">; +def CSRRS : CSR_ir<0b010, "csrrs">; +def CSRRC : CSR_ir<0b011, "csrrc">; + +def CSRRWI : CSR_ii<0b101, "csrrwi">; +def CSRRSI : CSR_ii<0b110, "csrrsi">; +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">; + +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">; + +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">; +} // Predicates = [IsRV64] + +//===----------------------------------------------------------------------===// +// Privileged instructions +//===----------------------------------------------------------------------===// + +let isBarrier = 1, isReturn = 1, isTerminator = 1 in { +def URET : Priv<"uret", 0b0000000> { + let rd = 0; + let rs1 = 0; + let rs2 = 0b00010; +} + +def SRET : Priv<"sret", 0b0001000> { + let rd = 0; + let rs1 = 0; + let rs2 = 0b00010; +} + +def MRET : Priv<"mret", 0b0011000> { + let rd = 0; + let rs1 = 0; + let rs2 = 0b00010; +} +} // isBarrier = 1, isReturn = 1, isTerminator = 1 + +def WFI : Priv<"wfi", 0b0001000> { + let rd = 0; + let rs1 = 0; + let rs2 = 0b00101; +} + +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"> { + let rd = 0; +} + +//===----------------------------------------------------------------------===// +// Assembler Pseudo Instructions (User-Level ISA, Version 2.2, Chapter 20) +//===----------------------------------------------------------------------===// + +// TODO la +// TODO lb lh lw +// TODO RV64I: ld +// TODO sb sh sw +// TODO RV64I: sd + +def : InstAlias<"nop", (ADDI X0, X0, 0)>; +// TODO li +def : InstAlias<"mv $rd, $rs", (ADDI GPR:$rd, GPR:$rs, 0)>; +def : InstAlias<"not $rd, $rs", (XORI GPR:$rd, GPR:$rs, -1)>; +def : InstAlias<"neg $rd, $rs", (SUB GPR:$rd, X0, GPR:$rs)>; + +let Predicates = [IsRV64] in { +def : InstAlias<"negw $rd, $rs", (SUBW GPR:$rd, X0, GPR:$rs)>; +def : InstAlias<"sext.w $rd, $rs", (ADDIW GPR:$rd, GPR:$rs, 0)>; +} // Predicates = [IsRV64] + +def : InstAlias<"seqz $rd, $rs", (SLTIU GPR:$rd, GPR:$rs, 1)>; +def : InstAlias<"snez $rd, $rs", (SLTU GPR:$rd, X0, GPR:$rs)>; +def : InstAlias<"sltz $rd, $rs", (SLT GPR:$rd, GPR:$rs, X0)>; +def : InstAlias<"sgtz $rd, $rs", (SLT GPR:$rd, X0, GPR:$rs)>; + +def : InstAlias<"beqz $rs, $offset", + (BEQ GPR:$rs, X0, simm13_lsb0:$offset)>; +def : InstAlias<"bnez $rs, $offset", + (BNE GPR:$rs, X0, simm13_lsb0:$offset)>; +def : InstAlias<"blez $rs, $offset", + (BGE X0, GPR:$rs, simm13_lsb0:$offset)>; +def : InstAlias<"bgez $rs, $offset", + (BGE GPR:$rs, X0, simm13_lsb0:$offset)>; +def : InstAlias<"bltz $rs, $offset", + (BLT GPR:$rs, X0, simm13_lsb0:$offset)>; +def : InstAlias<"bgtz $rs, $offset", + (BLT X0, GPR:$rs, simm13_lsb0:$offset)>; + +// Always output the canonical mnemonic for the pseudo branch instructions. +// The GNU tools emit the canonical mnemonic for the branch pseudo instructions +// as well (e.g. "bgt" will be recognised by the assembler but never printed by +// objdump). Match this behaviour by setting a zero weight. +def : InstAlias<"bgt $rs, $rt, $offset", + (BLT GPR:$rt, GPR:$rs, simm13_lsb0:$offset), 0>; +def : InstAlias<"ble $rs, $rt, $offset", + (BGE GPR:$rt, GPR:$rs, simm13_lsb0:$offset), 0>; +def : InstAlias<"bgtu $rs, $rt, $offset", + (BLTU GPR:$rt, GPR:$rs, simm13_lsb0:$offset), 0>; +def : InstAlias<"bleu $rs, $rt, $offset", + (BGEU GPR:$rt, GPR:$rs, simm13_lsb0:$offset), 0>; + +// "ret" has more weight since "ret" and "jr" alias the same "jalr" instruction. +def : InstAlias<"j $offset", (JAL X0, simm21_lsb0:$offset)>; +def : InstAlias<"jal $offset", (JAL X1, simm21_lsb0:$offset)>; +def : InstAlias<"jr $rs", (JALR X0, GPR:$rs, 0)>; +def : InstAlias<"jalr $rs", (JALR X1, GPR:$rs, 0)>; +def : InstAlias<"ret", (JALR X0, X1, 0), 2>; +// TODO call +// TODO tail + +def : InstAlias<"fence", (FENCE 0xF, 0xF)>; // 0xF == iorw + +// CSR Addresses: 0xC00 == cycle, 0xC01 == time, 0xC02 == instret +// 0xC80 == cycleh, 0xC81 == timeh, 0xC82 == instreth +def : InstAlias<"rdinstret $rd", (CSRRS GPR:$rd, 0xC02, X0)>; +def : InstAlias<"rdcycle $rd", (CSRRS GPR:$rd, 0xC00, X0)>; +def : InstAlias<"rdtime $rd", (CSRRS GPR:$rd, 0xC01, X0)>; + +let Predicates = [IsRV32] in { +def : InstAlias<"rdinstreth $rd", (CSRRS GPR:$rd, 0xC82, X0)>; +def : InstAlias<"rdcycleh $rd", (CSRRS GPR:$rd, 0xC80, X0)>; +def : InstAlias<"rdtimeh $rd", (CSRRS GPR:$rd, 0xC81, X0)>; +} // Predicates = [IsRV32] + +def : InstAlias<"csrr $rd, $csr", (CSRRS GPR:$rd, uimm12:$csr, X0)>; +def : InstAlias<"csrw $csr, $rs", (CSRRW X0, uimm12:$csr, GPR:$rs)>; +def : InstAlias<"csrs $csr, $rs", (CSRRS X0, uimm12:$csr, GPR:$rs)>; +def : InstAlias<"csrc $csr, $rs", (CSRRC X0, uimm12:$csr, GPR:$rs)>; + +def : InstAlias<"csrwi $csr, $imm", (CSRRWI X0, uimm12:$csr, uimm5:$imm)>; +def : InstAlias<"csrsi $csr, $imm", (CSRRSI X0, uimm12:$csr, uimm5:$imm)>; +def : InstAlias<"csrci $csr, $imm", (CSRRCI X0, uimm12:$csr, uimm5:$imm)>; + +def : InstAlias<"sfence.vma", (SFENCE_VMA X0, X0)>; +def : InstAlias<"sfence.vma $rs", (SFENCE_VMA GPR:$rs, X0)>; + +//===----------------------------------------------------------------------===// +// Pseudo-instructions and codegen patterns +// +// 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. +//===----------------------------------------------------------------------===// + +/// Generic pattern classes + +class PatGprGpr<SDPatternOperator OpNode, RVInstR Inst> + : Pat<(OpNode GPR:$rs1, GPR:$rs2), (Inst GPR:$rs1, GPR:$rs2)>; +class PatGprSimm12<SDPatternOperator OpNode, RVInstI Inst> + : Pat<(OpNode GPR:$rs1, simm12:$imm12), (Inst GPR:$rs1, simm12:$imm12)>; +class PatGprUimmLog2XLen<SDPatternOperator OpNode, RVInstIShift Inst> + : Pat<(OpNode GPR:$rs1, uimmlog2xlen:$shamt), + (Inst GPR:$rs1, uimmlog2xlen:$shamt)>; + +/// Predicates + +def IsOrAdd: PatFrag<(ops node:$A, node:$B), (or node:$A, node:$B), [{ + return isOrEquivalentToAdd(N); +}]>; + +/// Immediates + +def : Pat<(simm12:$imm), (ADDI X0, simm12:$imm)>; +// TODO: Add a pattern for immediates with all zeroes in the lower 12 bits. +def : Pat<(simm32:$imm), (ADDI (LUI (HI20 imm:$imm)), (LO12Sext imm:$imm))>; + +/// Simple arithmetic operations + +def : PatGprGpr<add, ADD>; +def : PatGprSimm12<add, ADDI>; +def : PatGprGpr<sub, SUB>; +def : PatGprGpr<or, OR>; +def : PatGprSimm12<or, ORI>; +def : PatGprGpr<and, AND>; +def : PatGprSimm12<and, ANDI>; +def : PatGprGpr<xor, XOR>; +def : PatGprSimm12<xor, XORI>; +def : PatGprGpr<shl, SLL>; +def : PatGprUimmLog2XLen<shl, SLLI>; +def : PatGprGpr<srl, SRL>; +def : PatGprUimmLog2XLen<srl, SRLI>; +def : PatGprGpr<sra, SRA>; +def : PatGprUimmLog2XLen<sra, SRAI>; + +/// FrameIndex calculations + +def : Pat<(add (i32 AddrFI:$Rs), simm12:$imm12), + (ADDI (i32 AddrFI:$Rs), simm12:$imm12)>; +def : Pat<(IsOrAdd (i32 AddrFI:$Rs), simm12:$imm12), + (ADDI (i32 AddrFI:$Rs), simm12:$imm12)>; + +/// Setcc + +def : PatGprGpr<setlt, SLT>; +def : PatGprSimm12<setlt, SLTI>; +def : PatGprGpr<setult, SLTU>; +def : PatGprSimm12<setult, SLTIU>; + +// Define pattern expansions for setcc operations that aren't directly +// handled by a RISC-V instruction. +def : Pat<(seteq GPR:$rs1, GPR:$rs2), (SLTIU (XOR GPR:$rs1, GPR:$rs2), 1)>; +def : Pat<(setne GPR:$rs1, GPR:$rs2), (SLTU X0, (XOR GPR:$rs1, GPR:$rs2))>; +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)>; +def : Pat<(setgt GPR:$rs1, GPR:$rs2), (SLT GPR:$rs2, GPR:$rs1)>; +def : Pat<(setge GPR:$rs1, GPR:$rs2), (XORI (SLT GPR:$rs1, GPR:$rs2), 1)>; +def : Pat<(setle GPR:$rs1, GPR:$rs2), (XORI (SLT GPR:$rs2, GPR:$rs1), 1)>; + +let usesCustomInserter = 1 in +def Select_GPR_Using_CC_GPR + : Pseudo<(outs GPR:$dst), + (ins GPR:$lhs, GPR:$rhs, ixlenimm:$imm, GPR:$src, GPR:$src2), + [(set XLenVT:$dst, (SelectCC GPR:$lhs, GPR:$rhs, + (XLenVT imm:$imm), GPR:$src, GPR:$src2))]>; + +/// Branches and jumps + +// Match `(brcond (CondOp ..), ..)` and lower to the appropriate RISC-V branch +// instruction. +class BccPat<PatFrag CondOp, RVInstB Inst> + : Pat<(brcond (i32 (CondOp GPR:$rs1, GPR:$rs2)), bb:$imm12), + (Inst GPR:$rs1, GPR:$rs2, simm13_lsb0:$imm12)>; + +def : BccPat<seteq, BEQ>; +def : BccPat<setne, BNE>; +def : BccPat<setlt, BLT>; +def : BccPat<setge, BGE>; +def : BccPat<setult, BLTU>; +def : BccPat<setuge, BGEU>; + +class BccSwapPat<PatFrag CondOp, RVInst InstBcc> + : Pat<(brcond (i32 (CondOp GPR:$rs1, GPR:$rs2)), bb:$imm12), + (InstBcc GPR:$rs2, GPR:$rs1, bb:$imm12)>; + +// Condition codes that don't have matching RISC-V branch instructions, but +// are trivially supported by swapping the two input operands +def : BccSwapPat<setgt, BLT>; +def : BccSwapPat<setle, BGE>; +def : BccSwapPat<setugt, BLTU>; +def : BccSwapPat<setule, BGEU>; + +// An extra pattern is needed for a brcond without a setcc (i.e. where the +// condition was calculated elsewhere). +def : Pat<(brcond GPR:$cond, bb:$imm12), (BNE GPR:$cond, X0, bb:$imm12)>; + +let isBarrier = 1, isBranch = 1, isTerminator = 1 in +def PseudoBR : Pseudo<(outs), (ins simm21_lsb0:$imm20), [(br bb:$imm20)]>, + PseudoInstExpansion<(JAL X0, simm21_lsb0:$imm20)>; + +let isCall = 1, Defs=[X1] in +let isBarrier = 1, isBranch = 1, isIndirectBranch = 1, isTerminator = 1 in +def PseudoBRIND : Pseudo<(outs), (ins GPR:$rs1, simm12:$imm12), []>, + PseudoInstExpansion<(JALR X0, GPR:$rs1, simm12:$imm12)>; + +def : Pat<(brind GPR:$rs1), (PseudoBRIND GPR:$rs1, 0)>; +def : Pat<(brind (add GPR:$rs1, simm12:$imm12)), + (PseudoBRIND GPR:$rs1, simm12:$imm12)>; + +let isCall = 1, Defs = [X1] in +def PseudoCALL : Pseudo<(outs), (ins GPR:$rs1), [(Call GPR:$rs1)]>, + PseudoInstExpansion<(JALR X1, GPR:$rs1, 0)>; + +let isBarrier = 1, isReturn = 1, isTerminator = 1 in +def PseudoRET : Pseudo<(outs), (ins), [(RetFlag)]>, + PseudoInstExpansion<(JALR X0, X1, 0)>; + +/// Loads + +multiclass LdPat<PatFrag LoadOp, RVInst Inst> { + def : Pat<(LoadOp GPR:$rs1), (Inst GPR:$rs1, 0)>; + def : Pat<(LoadOp AddrFI:$rs1), (Inst AddrFI:$rs1, 0)>; + def : Pat<(LoadOp (add GPR:$rs1, simm12:$imm12)), + (Inst GPR:$rs1, simm12:$imm12)>; + def : Pat<(LoadOp (add AddrFI:$rs1, simm12:$imm12)), + (Inst AddrFI:$rs1, simm12:$imm12)>; + def : Pat<(LoadOp (IsOrAdd AddrFI:$rs1, simm12:$imm12)), + (Inst AddrFI:$rs1, simm12:$imm12)>; +} + +defm : LdPat<sextloadi8, LB>; +defm : LdPat<extloadi8, LB>; +defm : LdPat<sextloadi16, LH>; +defm : LdPat<extloadi16, LH>; +defm : LdPat<load, LW>; +defm : LdPat<zextloadi8, LBU>; +defm : LdPat<zextloadi16, LHU>; + +/// Stores + +multiclass StPat<PatFrag StoreOp, RVInst Inst> { + def : Pat<(StoreOp GPR:$rs2, GPR:$rs1), (Inst GPR:$rs2, GPR:$rs1, 0)>; + def : Pat<(StoreOp GPR:$rs2, AddrFI:$rs1), (Inst GPR:$rs2, AddrFI:$rs1, 0)>; + def : Pat<(StoreOp GPR:$rs2, (add GPR:$rs1, simm12:$imm12)), + (Inst GPR:$rs2, GPR:$rs1, simm12:$imm12)>; + def : Pat<(StoreOp GPR:$rs2, (add AddrFI:$rs1, simm12:$imm12)), + (Inst GPR:$rs2, AddrFI:$rs1, simm12:$imm12)>; + def : Pat<(StoreOp GPR:$rs2, (IsOrAdd AddrFI:$rs1, simm12:$imm12)), + (Inst GPR:$rs2, AddrFI:$rs1, simm12:$imm12)>; +} + +defm : StPat<truncstorei8, SB>; +defm : StPat<truncstorei16, SH>; +defm : StPat<store, SW>; + +/// Other pseudo-instructions + +// Pessimistically assume the stack pointer will be clobbered +let Defs = [X2], Uses = [X2] in { +def ADJCALLSTACKDOWN : Pseudo<(outs), (ins i32imm:$amt1, i32imm:$amt2), + [(CallSeqStart timm:$amt1, timm:$amt2)]>; +def ADJCALLSTACKUP : Pseudo<(outs), (ins i32imm:$amt1, i32imm:$amt2), + [(CallSeqEnd timm:$amt1, timm:$amt2)]>; +} // Defs = [X2], Uses = [X2] + +//===----------------------------------------------------------------------===// +// Standard extensions +//===----------------------------------------------------------------------===// + +include "RISCVInstrInfoM.td" +include "RISCVInstrInfoA.td" +include "RISCVInstrInfoF.td" +include "RISCVInstrInfoD.td" +include "RISCVInstrInfoC.td" diff --git a/lib/Target/RISCV/RISCVInstrInfoA.td b/lib/Target/RISCV/RISCVInstrInfoA.td new file mode 100644 index 000000000000..33e863ba6a10 --- /dev/null +++ b/lib/Target/RISCV/RISCVInstrInfoA.td @@ -0,0 +1,77 @@ +//===-- RISCVInstrInfoA.td - RISC-V 'A' instructions -------*- tablegen -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file describes the RISC-V instructions from the standard 'A', Atomic +// Instructions extension. +// +//===----------------------------------------------------------------------===// + +//===----------------------------------------------------------------------===// +// Instruction class templates +//===----------------------------------------------------------------------===// + +let hasSideEffects = 0, mayLoad = 1, mayStore = 0 in +class LR_r<bit aq, bit rl, bits<3> funct3, string opcodestr> + : RVInstRAtomic<0b00010, aq, rl, funct3, OPC_AMO, + (outs GPR:$rd), (ins GPR:$rs1), + opcodestr, "$rd, (${rs1})"> { + let rs2 = 0; +} + +multiclass LR_r_aq_rl<bits<3> funct3, string opcodestr> { + def "" : LR_r<0, 0, funct3, opcodestr>; + def _AQ : LR_r<1, 0, funct3, opcodestr # ".aq">; + def _RL : LR_r<0, 1, funct3, opcodestr # ".rl">; + def _AQ_RL : LR_r<1, 1, funct3, opcodestr # ".aqrl">; +} + +let hasSideEffects = 0, mayLoad = 1, mayStore = 1 in +class AMO_rr<bits<5> funct5, bit aq, bit rl, bits<3> funct3, string opcodestr> + : RVInstRAtomic<funct5, aq, rl, funct3, OPC_AMO, + (outs GPR:$rd), (ins GPR:$rs1, GPR:$rs2), + opcodestr, "$rd, $rs2, (${rs1})">; + +multiclass AMO_rr_aq_rl<bits<5> funct5, bits<3> funct3, string opcodestr> { + def "" : AMO_rr<funct5, 0, 0, funct3, opcodestr>; + def _AQ : AMO_rr<funct5, 1, 0, funct3, opcodestr # ".aq">; + def _RL : AMO_rr<funct5, 0, 1, funct3, opcodestr # ".rl">; + def _AQ_RL : AMO_rr<funct5, 1, 1, funct3, opcodestr # ".aqrl">; +} + +//===----------------------------------------------------------------------===// +// Instructions +//===----------------------------------------------------------------------===// + +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">; +} // 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">; +} // Predicates = [HasStedExtA, IsRV64] diff --git a/lib/Target/RISCV/RISCVInstrInfoC.td b/lib/Target/RISCV/RISCVInstrInfoC.td new file mode 100644 index 000000000000..4ca52652086b --- /dev/null +++ b/lib/Target/RISCV/RISCVInstrInfoC.td @@ -0,0 +1,419 @@ +//===- RISCVInstrInfoC.td - Compressed RISCV instructions -*- tblgen-*-----===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +include "RISCVInstrFormatsC.td" + +//===----------------------------------------------------------------------===// +// Operand definitions. +//===----------------------------------------------------------------------===// + +def UImmLog2XLenNonZeroAsmOperand : AsmOperandClass { + let Name = "UImmLog2XLenNonZero"; + let RenderMethod = "addImmOperands"; + let DiagnosticType = "InvalidUImmLog2XLenNonZero"; +} + +def uimmlog2xlennonzero : Operand<XLenVT>, ImmLeaf<XLenVT, [{ + if (Subtarget->is64Bit()) + return isUInt<6>(Imm) && (Imm != 0); + return isUInt<5>(Imm) && (Imm != 0); +}]> { + let ParserMatchClass = UImmLog2XLenNonZeroAsmOperand; + // TODO: should ensure invalid shamt is rejected when decoding. + let DecoderMethod = "decodeUImmOperand<6>"; +} + +def simm6 : Operand<XLenVT>, ImmLeaf<XLenVT, [{return isInt<6>(Imm);}]> { + let ParserMatchClass = SImmAsmOperand<6>; + let EncoderMethod = "getImmOpValue"; + let DecoderMethod = "decodeSImmOperand<6>"; +} + +def uimm6nonzero : Operand<XLenVT>, + ImmLeaf<XLenVT, [{return isUInt<6>(Imm) && (Imm != 0);}]> { + let ParserMatchClass = UImmAsmOperand<6, "NonZero">; + let DecoderMethod = "decodeUImmOperand<6>"; +} + +// A 7-bit unsigned immediate where the least significant two bits are zero. +def uimm7_lsb00 : Operand<XLenVT>, + ImmLeaf<XLenVT, [{return isShiftedUInt<5, 2>(Imm);}]> { + let ParserMatchClass = UImmAsmOperand<7, "Lsb00">; + let EncoderMethod = "getImmOpValue"; + let DecoderMethod = "decodeUImmOperand<7>"; +} + +// A 8-bit unsigned immediate where the least significant two bits are zero. +def uimm8_lsb00 : Operand<XLenVT>, + ImmLeaf<XLenVT, [{return isShiftedUInt<6, 2>(Imm);}]> { + let ParserMatchClass = UImmAsmOperand<8, "Lsb00">; + let EncoderMethod = "getImmOpValue"; + let DecoderMethod = "decodeUImmOperand<8>"; +} + +// A 8-bit unsigned immediate where the least significant three bits are zero. +def uimm8_lsb000 : Operand<XLenVT>, + ImmLeaf<XLenVT, [{return isShiftedUInt<5, 3>(Imm);}]> { + let ParserMatchClass = UImmAsmOperand<8, "Lsb000">; + let EncoderMethod = "getImmOpValue"; + let DecoderMethod = "decodeUImmOperand<8>"; +} + +// A 9-bit signed immediate where the least significant bit is zero. +def simm9_lsb0 : Operand<OtherVT> { + let ParserMatchClass = SImmAsmOperand<9, "Lsb0">; + let EncoderMethod = "getImmOpValueAsr1"; + let DecoderMethod = "decodeSImmOperandAndLsl1<9>"; +} + +// A 9-bit unsigned immediate where the least significant three bits are zero. +def uimm9_lsb000 : Operand<XLenVT>, + ImmLeaf<XLenVT, [{return isShiftedUInt<6, 3>(Imm);}]> { + let ParserMatchClass = UImmAsmOperand<9, "Lsb000">; + let EncoderMethod = "getImmOpValue"; + let DecoderMethod = "decodeUImmOperand<9>"; +} + +// A 10-bit unsigned immediate where the least significant two bits are zero +// and the immediate can't be zero. +def uimm10_lsb00nonzero : Operand<XLenVT>, + ImmLeaf<XLenVT, + [{return isShiftedUInt<8, 2>(Imm) && (Imm != 0);}]> { + let ParserMatchClass = UImmAsmOperand<10, "Lsb00NonZero">; + let EncoderMethod = "getImmOpValue"; + let DecoderMethod = "decodeUImmOperand<10>"; +} + +// A 10-bit signed immediate where the least significant four bits are zero. +def simm10_lsb0000 : Operand<XLenVT>, + ImmLeaf<XLenVT, [{return isShiftedInt<6, 4>(Imm);}]> { + let ParserMatchClass = SImmAsmOperand<10, "Lsb0000">; + let EncoderMethod = "getImmOpValue"; + let DecoderMethod = "decodeSImmOperand<10>"; +} + +// A 12-bit signed immediate where the least significant bit is zero. +def simm12_lsb0 : Operand<OtherVT> { + let ParserMatchClass = SImmAsmOperand<12, "Lsb0">; + let EncoderMethod = "getImmOpValueAsr1"; + let DecoderMethod = "decodeSImmOperandAndLsl1<12>"; +} + +//===----------------------------------------------------------------------===// +// Instruction Class Templates +//===----------------------------------------------------------------------===// + +let hasSideEffects = 0, mayLoad = 1, mayStore = 0 in +class CStackLoad<bits<3> funct3, string OpcodeStr, + RegisterClass cls, DAGOperand opnd> + : RVInst16CI<funct3, 0b10, (outs cls:$rd), (ins SP:$rs1, opnd:$imm), + OpcodeStr, "$rd, ${imm}(${rs1})">; + +let hasSideEffects = 0, mayLoad = 0, mayStore = 1 in +class CStackStore<bits<3> funct3, string OpcodeStr, + RegisterClass cls, DAGOperand opnd> + : RVInst16CSS<funct3, 0b10, (outs), (ins cls:$rs2, SP:$rs1, opnd:$imm), + OpcodeStr, "$rs2, ${imm}(${rs1})">; + +let hasSideEffects = 0, mayLoad = 1, mayStore = 0 in +class CLoad_ri<bits<3> funct3, string OpcodeStr, + RegisterClass cls, DAGOperand opnd> + : RVInst16CL<funct3, 0b00, (outs cls:$rd), (ins GPRC:$rs1, opnd:$imm), + OpcodeStr, "$rd, ${imm}(${rs1})">; + +let hasSideEffects = 0, mayLoad = 0, mayStore = 1 in +class CStore_rri<bits<3> funct3, string OpcodeStr, + RegisterClass cls, DAGOperand opnd> + : RVInst16CS<funct3, 0b00, (outs), (ins cls:$rs2, GPRC:$rs1, opnd:$imm), + OpcodeStr, "$rs2, ${imm}(${rs1})">; + +let hasSideEffects = 0, mayLoad = 0, mayStore = 0 in +class Bcz<bits<3> funct3, string OpcodeStr, PatFrag CondOp, + RegisterClass cls> + : RVInst16CB<funct3, 0b01, (outs), (ins cls:$rs1, simm9_lsb0:$imm), + OpcodeStr, "$rs1, $imm"> { + let isBranch = 1; + let isTerminator = 1; + let Inst{12} = imm{7}; + let Inst{11-10} = imm{3-2}; + let Inst{6-5} = imm{6-5}; + let Inst{4-3} = imm{1-0}; + let Inst{2} = imm{4}; +} + +let hasSideEffects = 0, mayLoad = 0, mayStore = 0 in +class Shift_right<bits<2> funct2, string OpcodeStr, RegisterClass cls, + Operand ImmOpnd> + : RVInst16CB<0b100, 0b01, (outs cls:$rs1_wb), (ins cls:$rs1, ImmOpnd:$imm), + OpcodeStr, "$rs1, $imm"> { + let Constraints = "$rs1 = $rs1_wb"; + let Inst{12} = imm{5}; + let Inst{11-10} = funct2; + let Inst{6-2} = imm{4-0}; +} + +let hasSideEffects = 0, mayLoad = 0, mayStore = 0 in +class CS_ALU<bits<2> funct2, string OpcodeStr, RegisterClass cls, + bit RV64only> + : RVInst16CS<0b100, 0b01, (outs cls:$rd_wb), (ins cls:$rd, cls:$rs2), + OpcodeStr, "$rd, $rs2"> { + bits<3> rd; + let Constraints = "$rd = $rd_wb"; + let Inst{12} = RV64only; + let Inst{11-10} = 0b11; + let Inst{9-7} = rd; + let Inst{6-5} = funct2; +} + +//===----------------------------------------------------------------------===// +// Instructions +//===----------------------------------------------------------------------===// + +let Predicates = [HasStdExtC] in { + +let hasSideEffects = 0, mayLoad = 0, mayStore = 0 in +def C_ADDI4SPN : RVInst16CIW<0b000, 0b00, (outs GPRC:$rd), + (ins SP:$rs1, uimm10_lsb00nonzero:$imm), + "c.addi4spn", "$rd, $rs1, $imm"> { + bits<5> rs1; + let Inst{12-11} = imm{5-4}; + let Inst{10-7} = imm{9-6}; + let Inst{6} = imm{2}; + let Inst{5} = imm{3}; +} + +def C_FLD : CLoad_ri<0b001, "c.fld", FPR64C, uimm8_lsb000>, + Requires<[HasStdExtD]> { + 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> { + bits<7> imm; + let Inst{12-10} = imm{5-3}; + let Inst{6} = imm{2}; + let Inst{5} = imm{6}; +} + +let DecoderNamespace = "RISCV32Only_" in +def C_FLW : CLoad_ri<0b011, "c.flw", FPR32C, uimm7_lsb00>, + Requires<[HasStdExtF, IsRV32]> { + bits<7> imm; + let Inst{12-10} = imm{5-3}; + let Inst{6} = imm{2}; + let Inst{5} = imm{6}; +} + +def C_LD : CLoad_ri<0b011, "c.ld", GPRC, uimm8_lsb000>, + Requires<[IsRV64]> { + bits<8> imm; + let Inst{12-10} = imm{5-3}; + let Inst{6-5} = imm{7-6}; +} + +def C_FSD : CStore_rri<0b101, "c.fsd", FPR64C, uimm8_lsb000>, + Requires<[HasStdExtD]> { + 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> { + bits<7> imm; + let Inst{12-10} = imm{5-3}; + let Inst{6} = imm{2}; + let Inst{5} = imm{6}; +} + +let DecoderNamespace = "RISCV32Only_" in +def C_FSW : CStore_rri<0b111, "c.fsw", FPR32C, uimm7_lsb00>, + Requires<[HasStdExtF, IsRV32]> { + bits<7> imm; + let Inst{12-10} = imm{5-3}; + let Inst{6} = imm{2}; + let Inst{5} = imm{6}; +} + +def C_SD : CStore_rri<0b111, "c.sd", GPRC, uimm8_lsb000>, + Requires<[IsRV64]> { + 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", "">; + +let hasSideEffects = 0, mayLoad = 0, mayStore = 0 in +def C_ADDI : RVInst16CI<0b000, 0b01, (outs GPRNoX0:$rd_wb), + (ins GPRNoX0:$rd, simm6:$imm), + "c.addi", "$rd, $imm"> { + let Constraints = "$rd = $rd_wb"; + let Inst{6-2} = imm{4-0}; +} + +let hasSideEffects = 0, mayLoad = 0, mayStore = 0, isCall = 1, + DecoderNamespace = "RISCV32Only_" in +def C_JAL : RVInst16CJ<0b001, 0b01, (outs), (ins simm12_lsb0:$offset), + "c.jal", "$offset">, + Requires<[IsRV32]>; + +let hasSideEffects = 0, mayLoad = 0, mayStore = 0 in +def C_ADDIW : RVInst16CI<0b001, 0b01, (outs GPRNoX0:$rd_wb), + (ins GPRNoX0:$rd, simm6:$imm), + "c.addiw", "$rd, $imm">, + Requires<[IsRV64]> { + 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"> { + 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_lsb0000:$imm), + "c.addi16sp", "$rd, $imm"> { + let Constraints = "$rd = $rd_wb"; + let Inst{12} = imm{9}; + let Inst{11-7} = 2; + let Inst{6} = imm{4}; + let Inst{5} = imm{6}; + let Inst{4-3} = imm{8-7}; + let Inst{2} = imm{5}; +} + +let hasSideEffects = 0, mayLoad = 0, mayStore = 0 in +def C_LUI : RVInst16CI<0b011, 0b01, (outs GPRNoX0X2:$rd), + (ins uimm6nonzero:$imm), + "c.lui", "$rd, $imm"> { + 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>; + +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"> { + 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<0b00, "c.sub", GPRC, 0>; +def C_XOR : CS_ALU<0b01, "c.xor", GPRC, 0>; +def C_OR : CS_ALU<0b10, "c.or" , GPRC, 0>; +def C_AND : CS_ALU<0b11, "c.and", GPRC, 0>; + +def C_SUBW : CS_ALU<0b00, "c.subw", GPRC, 1>, Requires<[IsRV64]>; +def C_ADDW : CS_ALU<0b01, "c.addw", GPRC, 1>, Requires<[IsRV64]>; + +let hasSideEffects = 0, mayLoad = 0, mayStore = 0 in +def C_J : RVInst16CJ<0b101, 0b01, (outs), (ins simm12_lsb0:$offset), + "c.j", "$offset"> { + 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>; + +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"> { + let Constraints = "$rd = $rd_wb"; + let Inst{6-2} = imm{4-0}; +} + +def C_FLDSP : CStackLoad<0b001, "c.fldsp", FPR64, uimm9_lsb000>, + Requires<[HasStdExtD]> { + let Inst{6-5} = imm{4-3}; + let Inst{4-2} = imm{8-6}; +} + +def C_LWSP : CStackLoad<0b010, "c.lwsp", GPRNoX0, uimm8_lsb00> { + let Inst{6-4} = imm{4-2}; + let Inst{3-2} = imm{7-6}; +} + +let DecoderNamespace = "RISCV32Only_" in +def C_FLWSP : CStackLoad<0b011, "c.flwsp", FPR32, uimm8_lsb00>, + Requires<[HasStdExtF, IsRV32]> { + let Inst{6-4} = imm{4-2}; + let Inst{3-2} = imm{7-6}; +} + +def C_LDSP : CStackLoad<0b011, "c.ldsp", GPRNoX0, uimm9_lsb000>, + Requires<[IsRV64]> { + 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"> { + let isBranch = 1; + let isBarrier = 1; + let isTerminator = 1; + let isIndirectBranch = 1; + let rs2 = 0; +} + +let hasSideEffects = 0, mayLoad = 0, mayStore = 0 in +def C_MV : RVInst16CR<0b1000, 0b10, (outs GPRNoX0:$rs1), (ins GPRNoX0:$rs2), + "c.mv", "$rs1, $rs2">; + +let rs1 = 0, rs2 = 0, hasSideEffects = 0, mayLoad = 0, mayStore = 0 in +def C_EBREAK : RVInst16CR<0b1001, 0b10, (outs), (ins), "c.ebreak", "">; + +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">; + +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"> { + let Constraints = "$rs1 = $rs1_wb"; +} + +def C_FSDSP : CStackStore<0b101, "c.fsdsp", FPR64, uimm9_lsb000>, + Requires<[HasStdExtD]> { + let Inst{12-10} = imm{5-3}; + let Inst{9-7} = imm{8-6}; +} + +def C_SWSP : CStackStore<0b110, "c.swsp", GPR, uimm8_lsb00> { + let Inst{12-9} = imm{5-2}; + let Inst{8-7} = imm{7-6}; +} + +let DecoderNamespace = "RISCV32Only_" in +def C_FSWSP : CStackStore<0b111, "c.fswsp", FPR32, uimm8_lsb00>, + Requires<[HasStdExtF, IsRV32]> { + let Inst{12-9} = imm{5-2}; + let Inst{8-7} = imm{7-6}; +} + +def C_SDSP : CStackStore<0b111, "c.sdsp", GPR, uimm9_lsb000>, + Requires<[IsRV64]> { + let Inst{12-10} = imm{5-3}; + let Inst{9-7} = imm{8-6}; +} + +} // Predicates = [HasStdExtC] diff --git a/lib/Target/RISCV/RISCVInstrInfoD.td b/lib/Target/RISCV/RISCVInstrInfoD.td new file mode 100644 index 000000000000..48d91c0054d3 --- /dev/null +++ b/lib/Target/RISCV/RISCVInstrInfoD.td @@ -0,0 +1,174 @@ +//===-- RISCVInstrInfoD.td - RISC-V 'D' instructions -------*- tablegen -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file describes the RISC-V instructions from the standard 'D', +// Double-Precision Floating-Point instruction set extension. +// +//===----------------------------------------------------------------------===// + +//===----------------------------------------------------------------------===// +// Instruction Class Templates +//===----------------------------------------------------------------------===// + +let hasSideEffects = 0, mayLoad = 0, mayStore = 0 in +class FPFMAD_rrr_frm<RISCVOpcode opcode, string opcodestr> + : RVInstR4<0b01, opcode, (outs FPR64:$rd), + (ins FPR64:$rs1, FPR64:$rs2, FPR64:$rs3, frmarg:$funct3), + opcodestr, "$rd, $rs1, $rs2, $rs3, $funct3">; + +class FPFMADDynFrmAlias<FPFMAD_rrr_frm Inst, string OpcodeStr> + : InstAlias<OpcodeStr#" $rd, $rs1, $rs2, $rs3", + (Inst FPR64:$rd, FPR64:$rs1, FPR64:$rs2, FPR64:$rs3, 0b111)>; + +let hasSideEffects = 0, mayLoad = 0, mayStore = 0 in +class FPALUD_rr<bits<7> funct7, bits<3> funct3, string opcodestr> + : RVInstR<funct7, funct3, OPC_OP_FP, (outs FPR64:$rd), + (ins FPR64:$rs1, FPR64:$rs2), opcodestr, "$rd, $rs1, $rs2">; + +let hasSideEffects = 0, mayLoad = 0, mayStore = 0 in +class FPALUD_rr_frm<bits<7> funct7, string opcodestr> + : RVInstRFrm<funct7, OPC_OP_FP, (outs FPR64:$rd), + (ins FPR64:$rs1, FPR64:$rs2, frmarg:$funct3), opcodestr, + "$rd, $rs1, $rs2, $funct3">; + +class FPALUDDynFrmAlias<FPALUD_rr_frm Inst, string OpcodeStr> + : InstAlias<OpcodeStr#" $rd, $rs1, $rs2", + (Inst FPR64:$rd, FPR64:$rs1, FPR64:$rs2, 0b111)>; + +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">; + +//===----------------------------------------------------------------------===// +// Instructions +//===----------------------------------------------------------------------===// + +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})">; + +// Operands for stores are in the order srcreg, base, offset rather than +// reflecting the order these fields are specified in the instruction +// encoding. +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})">; + +def FMADD_D : FPFMAD_rrr_frm<OPC_MADD, "fmadd.d">; +def : FPFMADDynFrmAlias<FMADD_D, "fmadd.d">; +def FMSUB_D : FPFMAD_rrr_frm<OPC_MSUB, "fmsub.d">; +def : FPFMADDynFrmAlias<FMSUB_D, "fmsub.d">; +def FNMSUB_D : FPFMAD_rrr_frm<OPC_NMSUB, "fnmsub.d">; +def : FPFMADDynFrmAlias<FNMSUB_D, "fnmsub.d">; +def FNMADD_D : FPFMAD_rrr_frm<OPC_NMADD, "fnmadd.d">; +def : FPFMADDynFrmAlias<FNMADD_D, "fnmadd.d">; + +def FADD_D : FPALUD_rr_frm<0b0000001, "fadd.d">; +def : FPALUDDynFrmAlias<FADD_D, "fadd.d">; +def FSUB_D : FPALUD_rr_frm<0b0000101, "fsub.d">; +def : FPALUDDynFrmAlias<FSUB_D, "fsub.d">; +def FMUL_D : FPALUD_rr_frm<0b0001001, "fmul.d">; +def : FPALUDDynFrmAlias<FMUL_D, "fmul.d">; +def FDIV_D : FPALUD_rr_frm<0b0001101, "fdiv.d">; +def : FPALUDDynFrmAlias<FDIV_D, "fdiv.d">; + +def FSQRT_D : FPUnaryOp_r_frm<0b0101101, FPR64, FPR64, "fsqrt.d"> { + 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"> { + 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"> { + let rs2 = 0b00000; +} + +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"> { + let rs2 = 0b00000; +} + +def FCVT_W_D : FPUnaryOp_r_frm<0b1100001, GPR, FPR64, "fcvt.w.d"> { + 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"> { + 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"> { + let rs2 = 0b00000; +} + +def FCVT_D_WU : FPUnaryOp_r<0b1101001, 0b000, FPR64, GPR, "fcvt.d.wu"> { + let rs2 = 0b00001; +} +} // Predicates = [HasStdExtD] + +let Predicates = [HasStdExtD, IsRV64] in { +def FCVT_L_D : FPUnaryOp_r_frm<0b1100001, GPR, FPR64, "fcvt.l.d"> { + 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"> { + 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"> { + let rs2 = 0b00000; +} + +def FCVT_D_L : FPUnaryOp_r_frm<0b1101001, FPR64, GPR, "fcvt.d.l"> { + 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"> { + 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"> { + let rs2 = 0b00000; +} +} // Predicates = [HasStdExtD, IsRV64] + +//===----------------------------------------------------------------------===// +// Assembler Pseudo Instructions (User-Level ISA, Version 2.2, Chapter 20) +//===----------------------------------------------------------------------===// + +let Predicates = [HasStdExtD] in { +// TODO fld +// TODO fsd + +def : InstAlias<"fmv.d $rd, $rs", (FSGNJ_D FPR64:$rd, FPR64:$rs, FPR64:$rs)>; +def : InstAlias<"fabs.d $rd, $rs", (FSGNJX_D FPR64:$rd, FPR64:$rs, FPR64:$rs)>; +def : InstAlias<"fneg.d $rd, $rs", (FSGNJN_D FPR64:$rd, FPR64:$rs, FPR64:$rs)>; +} // Predicates = [HasStdExtD] diff --git a/lib/Target/RISCV/RISCVInstrInfoF.td b/lib/Target/RISCV/RISCVInstrInfoF.td new file mode 100644 index 000000000000..07722d2cbf34 --- /dev/null +++ b/lib/Target/RISCV/RISCVInstrInfoF.td @@ -0,0 +1,222 @@ +//===-- RISCVInstrInfoF.td - RISC-V 'F' instructions -------*- tablegen -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file describes the RISC-V instructions from the standard 'F', +// Single-Precision Floating-Point instruction set extension. +// +//===----------------------------------------------------------------------===// + +//===----------------------------------------------------------------------===// +// Operand and SDNode transformation definitions. +//===----------------------------------------------------------------------===// + +// Floating-point rounding mode + +def FRMArg : AsmOperandClass { + let Name = "FRMArg"; + let RenderMethod = "addFRMArgOperands"; + let DiagnosticType = "InvalidFRMArg"; +} + +def frmarg : Operand<XLenVT> { + let ParserMatchClass = FRMArg; + let PrintMethod = "printFRMArg"; + let DecoderMethod = "decodeUImmOperand<3>"; +} + +//===----------------------------------------------------------------------===// +// Instruction class templates +//===----------------------------------------------------------------------===// + +let hasSideEffects = 0, mayLoad = 0, mayStore = 0 in +class FPFMAS_rrr_frm<RISCVOpcode opcode, string opcodestr> + : RVInstR4<0b00, opcode, (outs FPR32:$rd), + (ins FPR32:$rs1, FPR32:$rs2, FPR32:$rs3, frmarg:$funct3), + opcodestr, "$rd, $rs1, $rs2, $rs3, $funct3">; + +class FPFMASDynFrmAlias<FPFMAS_rrr_frm Inst, string OpcodeStr> + : InstAlias<OpcodeStr#" $rd, $rs1, $rs2, $rs3", + (Inst FPR32:$rd, FPR32:$rs1, FPR32:$rs2, FPR32:$rs3, 0b111)>; + +let hasSideEffects = 0, mayLoad = 0, mayStore = 0 in +class FPALUS_rr<bits<7> funct7, bits<3> funct3, string opcodestr> + : RVInstR<funct7, funct3, OPC_OP_FP, (outs FPR32:$rd), + (ins FPR32:$rs1, FPR32:$rs2), opcodestr, "$rd, $rs1, $rs2">; + +let hasSideEffects = 0, mayLoad = 0, mayStore = 0 in +class FPALUS_rr_frm<bits<7> funct7, string opcodestr> + : RVInstRFrm<funct7, OPC_OP_FP, (outs FPR32:$rd), + (ins FPR32:$rs1, FPR32:$rs2, frmarg:$funct3), opcodestr, + "$rd, $rs1, $rs2, $funct3">; + +class FPALUSDynFrmAlias<FPALUS_rr_frm Inst, string OpcodeStr> + : InstAlias<OpcodeStr#" $rd, $rs1, $rs2", + (Inst FPR32:$rd, FPR32:$rs1, FPR32:$rs2, 0b111)>; + +let hasSideEffects = 0, mayLoad = 0, mayStore = 0 in +class FPUnaryOp_r<bits<7> funct7, bits<3> funct3, RegisterClass rdty, + RegisterClass rs1ty, string opcodestr> + : RVInstR<funct7, funct3, OPC_OP_FP, (outs rdty:$rd), (ins rs1ty:$rs1), + opcodestr, "$rd, $rs1">; + +let hasSideEffects = 0, mayLoad = 0, mayStore = 0 in +class FPUnaryOp_r_frm<bits<7> funct7, RegisterClass rdty, RegisterClass rs1ty, + string opcodestr> + : RVInstRFrm<funct7, OPC_OP_FP, (outs rdty:$rd), + (ins rs1ty:$rs1, frmarg:$funct3), opcodestr, + "$rd, $rs1, $funct3">; + +class FPUnaryOpDynFrmAlias<FPUnaryOp_r_frm Inst, string OpcodeStr, + RegisterClass rdty, RegisterClass rs1ty> + : InstAlias<OpcodeStr#" $rd, $rs1", + (Inst rdty:$rd, rs1ty:$rs1, 0b111)>; + +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">; + +//===----------------------------------------------------------------------===// +// Instructions +//===----------------------------------------------------------------------===// + +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})">; + +// Operands for stores are in the order srcreg, base, offset rather than +// reflecting the order these fields are specified in the instruction +// encoding. +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})">; + +def FMADD_S : FPFMAS_rrr_frm<OPC_MADD, "fmadd.s">; +def : FPFMASDynFrmAlias<FMADD_S, "fmadd.s">; +def FMSUB_S : FPFMAS_rrr_frm<OPC_MSUB, "fmsub.s">; +def : FPFMASDynFrmAlias<FMSUB_S, "fmsub.s">; +def FNMSUB_S : FPFMAS_rrr_frm<OPC_NMSUB, "fnmsub.s">; +def : FPFMASDynFrmAlias<FNMSUB_S, "fnmsub.s">; +def FNMADD_S : FPFMAS_rrr_frm<OPC_NMADD, "fnmadd.s">; +def : FPFMASDynFrmAlias<FNMADD_S, "fnmadd.s">; + +def FADD_S : FPALUS_rr_frm<0b0000000, "fadd.s">; +def : FPALUSDynFrmAlias<FADD_S, "fadd.s">; +def FSUB_S : FPALUS_rr_frm<0b0000100, "fsub.s">; +def : FPALUSDynFrmAlias<FSUB_S, "fsub.s">; +def FMUL_S : FPALUS_rr_frm<0b0001000, "fmul.s">; +def : FPALUSDynFrmAlias<FMUL_S, "fmul.s">; +def FDIV_S : FPALUS_rr_frm<0b0001100, "fdiv.s">; +def : FPALUSDynFrmAlias<FDIV_S, "fdiv.s">; + +def FSQRT_S : FPUnaryOp_r_frm<0b0101100, FPR32, FPR32, "fsqrt.s"> { + 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"> { + 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"> { + 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"> { + let rs2 = 0b00000; +} + +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"> { + let rs2 = 0b00000; +} + +def FCVT_S_W : FPUnaryOp_r_frm<0b1101000, FPR32, GPR, "fcvt.s.w"> { + 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"> { + 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"> { + let rs2 = 0b00000; +} +} // Predicates = [HasStdExtF] + +let Predicates = [HasStdExtF, IsRV64] in { +def FCVT_L_S : FPUnaryOp_r_frm<0b1100000, GPR, FPR32, "fcvt.l.s"> { + 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"> { + 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"> { + 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"> { + let rs2 = 0b00011; +} +def : FPUnaryOpDynFrmAlias<FCVT_S_LU, "fcvt.s.lu", FPR32, GPR>; +} // Predicates = [HasStdExtF, IsRV64] + +//===----------------------------------------------------------------------===// +// Assembler Pseudo Instructions (User-Level ISA, Version 2.2, Chapter 20) +//===----------------------------------------------------------------------===// + +let Predicates = [HasStdExtF] in { +// TODO flw +// TODO fsw + +def : InstAlias<"fmv.s $rd, $rs", (FSGNJ_S FPR32:$rd, FPR32:$rs, FPR32:$rs)>; +def : InstAlias<"fabs.s $rd, $rs", (FSGNJX_S FPR32:$rd, FPR32:$rs, FPR32:$rs)>; +def : InstAlias<"fneg.s $rd, $rs", (FSGNJN_S FPR32:$rd, FPR32:$rs, FPR32:$rs)>; + +// The following csr instructions actually alias instructions from the base ISA. +// However, it only makes sense to support them when the F extension is enabled. +// CSR Addresses: 0x003 == fcsr, 0x002 == frm, 0x001 == fflags +// NOTE: "frcsr", "frrm", and "frflags" are more specialized version of "csrr". +def : InstAlias<"frcsr $rd", (CSRRS GPR:$rd, 0x003, X0), 2>; +def : InstAlias<"fscsr $rd, $rs", (CSRRW GPR:$rd, 0x003, GPR:$rs)>; +def : InstAlias<"fscsr $rs", (CSRRW X0, 0x003, GPR:$rs), 2>; + +def : InstAlias<"frrm $rd", (CSRRS GPR:$rd, 0x002, X0), 2>; +def : InstAlias<"fsrm $rd, $rs", (CSRRW GPR:$rd, 0x002, GPR:$rs)>; +def : InstAlias<"fsrm $rs", (CSRRW X0, 0x002, GPR:$rs), 2>; +def : InstAlias<"fsrmi $rd, $imm", (CSRRWI GPR:$rd, 0x002, uimm5:$imm)>; +def : InstAlias<"fsrmi $imm", (CSRRWI X0, 0x002, uimm5:$imm), 2>; + +def : InstAlias<"frflags $rd", (CSRRS GPR:$rd, 0x001, X0), 2>; +def : InstAlias<"fsflags $rd, $rs", (CSRRW GPR:$rd, 0x001, GPR:$rs)>; +def : InstAlias<"fsflags $rs", (CSRRW X0, 0x001, GPR:$rs), 2>; +def : InstAlias<"fsflagsi $rd, $imm", (CSRRWI GPR:$rd, 0x001, uimm5:$imm)>; +def : InstAlias<"fsflagsi $imm", (CSRRWI X0, 0x001, uimm5:$imm), 2>; +} // Predicates = [HasStdExtF] diff --git a/lib/Target/RISCV/RISCVInstrInfoM.td b/lib/Target/RISCV/RISCVInstrInfoM.td new file mode 100644 index 000000000000..fec9c1f93997 --- /dev/null +++ b/lib/Target/RISCV/RISCVInstrInfoM.td @@ -0,0 +1,36 @@ +//===-- RISCVInstrInfoM.td - RISC-V 'M' instructions -------*- tablegen -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file describes the RISC-V instructions from the standard 'M', Integer +// Multiplication and Division instruction set extension. +// +//===----------------------------------------------------------------------===// + +//===----------------------------------------------------------------------===// +// Instructions +//===----------------------------------------------------------------------===// + +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">; +} // 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">; +} // Predicates = [HasStdExtM, IsRV64] diff --git a/lib/Target/RISCV/RISCVMCInstLower.cpp b/lib/Target/RISCV/RISCVMCInstLower.cpp new file mode 100644 index 000000000000..d8ae11f2bd90 --- /dev/null +++ b/lib/Target/RISCV/RISCVMCInstLower.cpp @@ -0,0 +1,105 @@ +//===-- RISCVMCInstLower.cpp - Convert RISCV MachineInstr to an MCInst ------=// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file contains code to lower RISCV MachineInstrs to their corresponding +// MCInst records. +// +//===----------------------------------------------------------------------===// + +#include "RISCV.h" +#include "MCTargetDesc/RISCVMCExpr.h" +#include "llvm/CodeGen/AsmPrinter.h" +#include "llvm/CodeGen/MachineBasicBlock.h" +#include "llvm/CodeGen/MachineInstr.h" +#include "llvm/MC/MCAsmInfo.h" +#include "llvm/MC/MCContext.h" +#include "llvm/MC/MCExpr.h" +#include "llvm/MC/MCInst.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/raw_ostream.h" + +using namespace llvm; + +static MCOperand lowerSymbolOperand(const MachineOperand &MO, MCSymbol *Sym, + const AsmPrinter &AP) { + MCContext &Ctx = AP.OutContext; + RISCVMCExpr::VariantKind Kind; + + switch (MO.getTargetFlags()) { + default: + llvm_unreachable("Unknown target flag on GV operand"); + case RISCVII::MO_None: + Kind = RISCVMCExpr::VK_RISCV_None; + break; + case RISCVII::MO_LO: + Kind = RISCVMCExpr::VK_RISCV_LO; + break; + case RISCVII::MO_HI: + Kind = RISCVMCExpr::VK_RISCV_HI; + break; + } + + const MCExpr *ME = + MCSymbolRefExpr::create(Sym, MCSymbolRefExpr::VK_None, Ctx); + + if (!MO.isJTI() && MO.getOffset()) + ME = MCBinaryExpr::createAdd( + ME, MCConstantExpr::create(MO.getOffset(), Ctx), Ctx); + + ME = RISCVMCExpr::create(ME, Kind, Ctx); + return MCOperand::createExpr(ME); +} + +bool llvm::LowerRISCVMachineOperandToMCOperand(const MachineOperand &MO, + MCOperand &MCOp, + const AsmPrinter &AP) { + switch (MO.getType()) { + default: + report_fatal_error("LowerRISCVMachineInstrToMCInst: unknown operand type"); + case MachineOperand::MO_Register: + // Ignore all implicit register operands. + if (MO.isImplicit()) + return false; + MCOp = MCOperand::createReg(MO.getReg()); + break; + case MachineOperand::MO_RegisterMask: + // Regmasks are like implicit defs. + return false; + case MachineOperand::MO_Immediate: + MCOp = MCOperand::createImm(MO.getImm()); + break; + case MachineOperand::MO_MachineBasicBlock: + MCOp = MCOperand::createExpr( + MCSymbolRefExpr::create(MO.getMBB()->getSymbol(), AP.OutContext)); + break; + case MachineOperand::MO_GlobalAddress: + MCOp = lowerSymbolOperand(MO, AP.getSymbol(MO.getGlobal()), AP); + break; + case MachineOperand::MO_BlockAddress: + MCOp = lowerSymbolOperand( + MO, AP.GetBlockAddressSymbol(MO.getBlockAddress()), AP); + break; + case MachineOperand::MO_ExternalSymbol: + MCOp = lowerSymbolOperand( + MO, AP.GetExternalSymbolSymbol(MO.getSymbolName()), AP); + break; + } + return true; +} + +void llvm::LowerRISCVMachineInstrToMCInst(const MachineInstr *MI, MCInst &OutMI, + const AsmPrinter &AP) { + OutMI.setOpcode(MI->getOpcode()); + + for (const MachineOperand &MO : MI->operands()) { + MCOperand MCOp; + if (LowerRISCVMachineOperandToMCOperand(MO, MCOp, AP)) + OutMI.addOperand(MCOp); + } +} diff --git a/lib/Target/RISCV/RISCVRegisterInfo.cpp b/lib/Target/RISCV/RISCVRegisterInfo.cpp new file mode 100644 index 000000000000..5776a92cab91 --- /dev/null +++ b/lib/Target/RISCV/RISCVRegisterInfo.cpp @@ -0,0 +1,93 @@ +//===-- RISCVRegisterInfo.cpp - RISCV Register Information ------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file contains the RISCV implementation of the TargetRegisterInfo class. +// +//===----------------------------------------------------------------------===// + +#include "RISCVRegisterInfo.h" +#include "RISCV.h" +#include "RISCVSubtarget.h" +#include "llvm/CodeGen/MachineFrameInfo.h" +#include "llvm/CodeGen/MachineFunction.h" +#include "llvm/CodeGen/MachineInstrBuilder.h" +#include "llvm/CodeGen/RegisterScavenging.h" +#include "llvm/CodeGen/TargetFrameLowering.h" +#include "llvm/CodeGen/TargetInstrInfo.h" +#include "llvm/Support/ErrorHandling.h" + +#define GET_REGINFO_TARGET_DESC +#include "RISCVGenRegisterInfo.inc" + +using namespace llvm; + +RISCVRegisterInfo::RISCVRegisterInfo(unsigned HwMode) + : RISCVGenRegisterInfo(RISCV::X1, /*DwarfFlavour*/0, /*EHFlavor*/0, + /*PC*/0, HwMode) {} + +const MCPhysReg * +RISCVRegisterInfo::getCalleeSavedRegs(const MachineFunction *MF) const { + return CSR_SaveList; +} + +BitVector RISCVRegisterInfo::getReservedRegs(const MachineFunction &MF) const { + BitVector Reserved(getNumRegs()); + + // Use markSuperRegs to ensure any register aliases are also reserved + markSuperRegs(Reserved, RISCV::X0); // zero + markSuperRegs(Reserved, RISCV::X1); // ra + markSuperRegs(Reserved, RISCV::X2); // sp + markSuperRegs(Reserved, RISCV::X3); // gp + markSuperRegs(Reserved, RISCV::X4); // tp + markSuperRegs(Reserved, RISCV::X8); // fp + assert(checkAllSuperRegsMarked(Reserved)); + return Reserved; +} + +const uint32_t *RISCVRegisterInfo::getNoPreservedMask() const { + return CSR_NoRegs_RegMask; +} + +void RISCVRegisterInfo::eliminateFrameIndex(MachineBasicBlock::iterator II, + int SPAdj, unsigned FIOperandNum, + RegScavenger *RS) const { + assert(SPAdj == 0 && "Unexpected non-zero SPAdj value"); + + MachineInstr &MI = *II; + MachineFunction &MF = *MI.getParent()->getParent(); + DebugLoc DL = MI.getDebugLoc(); + + int FrameIndex = MI.getOperand(FIOperandNum).getIndex(); + unsigned FrameReg; + int Offset = + getFrameLowering(MF)->getFrameIndexReference(MF, FrameIndex, FrameReg) + + MI.getOperand(FIOperandNum + 1).getImm(); + + assert(MF.getSubtarget().getFrameLowering()->hasFP(MF) && + "eliminateFrameIndex currently requires hasFP"); + + // Offsets must be directly encoded in a 12-bit immediate field + if (!isInt<12>(Offset)) { + report_fatal_error( + "Frame offsets outside of the signed 12-bit range not supported"); + } + + MI.getOperand(FIOperandNum).ChangeToRegister(FrameReg, false); + MI.getOperand(FIOperandNum + 1).ChangeToImmediate(Offset); +} + +unsigned RISCVRegisterInfo::getFrameRegister(const MachineFunction &MF) const { + return RISCV::X8; +} + +const uint32_t * +RISCVRegisterInfo::getCallPreservedMask(const MachineFunction & /*MF*/, + CallingConv::ID /*CC*/) const { + return CSR_RegMask; +} diff --git a/lib/Target/RISCV/RISCVRegisterInfo.h b/lib/Target/RISCV/RISCVRegisterInfo.h new file mode 100644 index 000000000000..0b2bc3776fc6 --- /dev/null +++ b/lib/Target/RISCV/RISCVRegisterInfo.h @@ -0,0 +1,45 @@ +//===-- RISCVRegisterInfo.h - RISCV Register Information Impl ---*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file contains the RISCV implementation of the TargetRegisterInfo class. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_RISCV_RISCVREGISTERINFO_H +#define LLVM_LIB_TARGET_RISCV_RISCVREGISTERINFO_H + +#include "llvm/CodeGen/TargetRegisterInfo.h" + +#define GET_REGINFO_HEADER +#include "RISCVGenRegisterInfo.inc" + +namespace llvm { + +struct RISCVRegisterInfo : public RISCVGenRegisterInfo { + + RISCVRegisterInfo(unsigned HwMode); + + const uint32_t *getCallPreservedMask(const MachineFunction &MF, + CallingConv::ID) const override; + + const MCPhysReg *getCalleeSavedRegs(const MachineFunction *MF) const override; + + BitVector getReservedRegs(const MachineFunction &MF) const override; + + const uint32_t *getNoPreservedMask() const override; + + void eliminateFrameIndex(MachineBasicBlock::iterator MI, int SPAdj, + unsigned FIOperandNum, + RegScavenger *RS = nullptr) const override; + + unsigned getFrameRegister(const MachineFunction &MF) const override; +}; +} + +#endif diff --git a/lib/Target/RISCV/RISCVRegisterInfo.td b/lib/Target/RISCV/RISCVRegisterInfo.td index f04de217bf0d..21be2e332e59 100644 --- a/lib/Target/RISCV/RISCVRegisterInfo.td +++ b/lib/Target/RISCV/RISCVRegisterInfo.td @@ -8,83 +8,199 @@ //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// -// Declarations that describe the RISC-V register file +// Declarations that describe the RISC-V register files //===----------------------------------------------------------------------===// let Namespace = "RISCV" in { - def sub_32 : SubRegIndex<32>; - - class RISCVReg32<bits<5> Enc, string n, list<string> alt = []> : Register<n> { - let HWEncoding{4-0} = Enc; - let AltNames = alt; - } +class RISCVReg<bits<5> Enc, string n, list<string> alt = []> : Register<n> { + let HWEncoding{4-0} = Enc; + let AltNames = alt; +} - // RISCV64 registers don't define an AsmName or AltName. If they specified - // names aliasing the RISCVReg32 registers, the generation of the default - // MatchRegisterName/MatchRegisterAltName would fail. When necessary, - // RISCVAsmParser will need to convert a register number from a RISCVReg32 - // to the equivalent RISCVReg64. - class RISCVReg64<RISCVReg32 subreg> : Register<""> { - let HWEncoding{4-0} = subreg.HWEncoding{4-0}; - let SubRegs = [subreg]; - let SubRegIndices = [sub_32]; - } +class RISCVReg32<bits<5> Enc, string n, list<string> alt = []> : Register<n> { + let HWEncoding{4-0} = Enc; + let AltNames = alt; +} - def ABIRegAltName : RegAltNameIndex; +// Because RISCVReg64 register have AsmName and AltNames that alias with their +// 32-bit sub-register, RISCVAsmParser will need to coerce a register number +// from a RISCVReg32 to the equivalent RISCVReg64 when appropriate. +def sub_32 : SubRegIndex<32>; +class RISCVReg64<RISCVReg32 subreg> : Register<""> { + let HWEncoding{4-0} = subreg.HWEncoding{4-0}; + let SubRegs = [subreg]; + let SubRegIndices = [sub_32]; + let AsmName = subreg.AsmName; + let AltNames = subreg.AltNames; } +def ABIRegAltName : RegAltNameIndex; +} // Namespace = "RISCV" + // Integer registers let RegAltNameIndices = [ABIRegAltName] in { - def X0_32 : RISCVReg32<0, "x0", ["zero"]>, DwarfRegNum<[0]>; - def X1_32 : RISCVReg32<1, "x1", ["ra"]>, DwarfRegNum<[1]>; - def X2_32 : RISCVReg32<2, "x2", ["sp"]>, DwarfRegNum<[2]>; - def X3_32 : RISCVReg32<3, "x3", ["gp"]>, DwarfRegNum<[3]>; - def X4_32 : RISCVReg32<4, "x4", ["tp"]>, DwarfRegNum<[4]>; - def X5_32 : RISCVReg32<5, "x5", ["t0"]>, DwarfRegNum<[5]>; - def X6_32 : RISCVReg32<6, "x6", ["t1"]>, DwarfRegNum<[6]>; - def X7_32 : RISCVReg32<7, "x7", ["t2"]>, DwarfRegNum<[7]>; - def X8_32 : RISCVReg32<8, "x8", ["s0"]>, DwarfRegNum<[8]>; - def X9_32 : RISCVReg32<9, "x9", ["s1"]>, DwarfRegNum<[9]>; - def X10_32 : RISCVReg32<10,"x10", ["a0"]>, DwarfRegNum<[10]>; - def X11_32 : RISCVReg32<11,"x11", ["a1"]>, DwarfRegNum<[11]>; - def X12_32 : RISCVReg32<12,"x12", ["a2"]>, DwarfRegNum<[12]>; - def X13_32 : RISCVReg32<13,"x13", ["a3"]>, DwarfRegNum<[13]>; - def X14_32 : RISCVReg32<14,"x14", ["a4"]>, DwarfRegNum<[14]>; - def X15_32 : RISCVReg32<15,"x15", ["a5"]>, DwarfRegNum<[15]>; - def X16_32 : RISCVReg32<16,"x16", ["a6"]>, DwarfRegNum<[16]>; - def X17_32 : RISCVReg32<17,"x17", ["a7"]>, DwarfRegNum<[17]>; - def X18_32 : RISCVReg32<18,"x18", ["s2"]>, DwarfRegNum<[18]>; - def X19_32 : RISCVReg32<19,"x19", ["s3"]>, DwarfRegNum<[19]>; - def X20_32 : RISCVReg32<20,"x20", ["s4"]>, DwarfRegNum<[20]>; - def X21_32 : RISCVReg32<21,"x21", ["s5"]>, DwarfRegNum<[21]>; - def X22_32 : RISCVReg32<22,"x22", ["s6"]>, DwarfRegNum<[22]>; - def X23_32 : RISCVReg32<23,"x23", ["s7"]>, DwarfRegNum<[23]>; - def X24_32 : RISCVReg32<24,"x24", ["s8"]>, DwarfRegNum<[24]>; - def X25_32 : RISCVReg32<25,"x25", ["s9"]>, DwarfRegNum<[25]>; - def X26_32 : RISCVReg32<26,"x26", ["s10"]>, DwarfRegNum<[26]>; - def X27_32 : RISCVReg32<27,"x27", ["s11"]>, DwarfRegNum<[27]>; - def X28_32 : RISCVReg32<28,"x28", ["t3"]>, DwarfRegNum<[28]>; - def X29_32 : RISCVReg32<29,"x29", ["t4"]>, DwarfRegNum<[29]>; - def X30_32 : RISCVReg32<30,"x30", ["t5"]>, DwarfRegNum<[30]>; - def X31_32 : RISCVReg32<31,"x31", ["t6"]>, DwarfRegNum<[31]>; + def X0 : RISCVReg<0, "x0", ["zero"]>, DwarfRegNum<[0]>; + def X1 : RISCVReg<1, "x1", ["ra"]>, DwarfRegNum<[1]>; + def X2 : RISCVReg<2, "x2", ["sp"]>, DwarfRegNum<[2]>; + def X3 : RISCVReg<3, "x3", ["gp"]>, DwarfRegNum<[3]>; + def X4 : RISCVReg<4, "x4", ["tp"]>, DwarfRegNum<[4]>; + def X5 : RISCVReg<5, "x5", ["t0"]>, DwarfRegNum<[5]>; + def X6 : RISCVReg<6, "x6", ["t1"]>, DwarfRegNum<[6]>; + def X7 : RISCVReg<7, "x7", ["t2"]>, DwarfRegNum<[7]>; + def X8 : RISCVReg<8, "x8", ["s0"]>, DwarfRegNum<[8]>; + def X9 : RISCVReg<9, "x9", ["s1"]>, DwarfRegNum<[9]>; + def X10 : RISCVReg<10,"x10", ["a0"]>, DwarfRegNum<[10]>; + def X11 : RISCVReg<11,"x11", ["a1"]>, DwarfRegNum<[11]>; + def X12 : RISCVReg<12,"x12", ["a2"]>, DwarfRegNum<[12]>; + def X13 : RISCVReg<13,"x13", ["a3"]>, DwarfRegNum<[13]>; + def X14 : RISCVReg<14,"x14", ["a4"]>, DwarfRegNum<[14]>; + def X15 : RISCVReg<15,"x15", ["a5"]>, DwarfRegNum<[15]>; + def X16 : RISCVReg<16,"x16", ["a6"]>, DwarfRegNum<[16]>; + def X17 : RISCVReg<17,"x17", ["a7"]>, DwarfRegNum<[17]>; + def X18 : RISCVReg<18,"x18", ["s2"]>, DwarfRegNum<[18]>; + def X19 : RISCVReg<19,"x19", ["s3"]>, DwarfRegNum<[19]>; + def X20 : RISCVReg<20,"x20", ["s4"]>, DwarfRegNum<[20]>; + def X21 : RISCVReg<21,"x21", ["s5"]>, DwarfRegNum<[21]>; + def X22 : RISCVReg<22,"x22", ["s6"]>, DwarfRegNum<[22]>; + def X23 : RISCVReg<23,"x23", ["s7"]>, DwarfRegNum<[23]>; + def X24 : RISCVReg<24,"x24", ["s8"]>, DwarfRegNum<[24]>; + def X25 : RISCVReg<25,"x25", ["s9"]>, DwarfRegNum<[25]>; + def X26 : RISCVReg<26,"x26", ["s10"]>, DwarfRegNum<[26]>; + def X27 : RISCVReg<27,"x27", ["s11"]>, DwarfRegNum<[27]>; + def X28 : RISCVReg<28,"x28", ["t3"]>, DwarfRegNum<[28]>; + def X29 : RISCVReg<29,"x29", ["t4"]>, DwarfRegNum<[29]>; + def X30 : RISCVReg<30,"x30", ["t5"]>, DwarfRegNum<[30]>; + def X31 : RISCVReg<31,"x31", ["t6"]>, DwarfRegNum<[31]>; } -foreach Index = 0-31 in { - def X#Index#_64 : RISCVReg64<!cast<RISCVReg32>("X"#Index#"_32")>, DwarfRegNum<[Index]>; +def XLenVT : ValueTypeByHwMode<[RV32, RV64, DefaultMode], + [i32, i64, i32]>; + +// The order of registers represents the preferred allocation sequence. +// Registers are listed in the order caller-save, callee-save, specials. +def GPR : RegisterClass<"RISCV", [XLenVT], 32, (add + (sequence "X%u", 10, 17), + (sequence "X%u", 5, 7), + (sequence "X%u", 28, 31), + (sequence "X%u", 8, 9), + (sequence "X%u", 18, 27), + (sequence "X%u", 0, 4) + )> { + let RegInfos = RegInfoByHwMode< + [RV32, RV64, DefaultMode], + [RegInfo<32,32,32>, RegInfo<64,64,64>, RegInfo<32,32,32>]>; +} + +// The order of registers represents the preferred allocation sequence. +// Registers are listed in the order caller-save, callee-save, specials. +def GPRNoX0 : RegisterClass<"RISCV", [XLenVT], 32, (add + (sequence "X%u", 10, 17), + (sequence "X%u", 5, 7), + (sequence "X%u", 28, 31), + (sequence "X%u", 8, 9), + (sequence "X%u", 18, 27), + (sequence "X%u", 1, 4) + )> { + let RegInfos = RegInfoByHwMode< + [RV32, RV64, DefaultMode], + [RegInfo<32,32,32>, RegInfo<64,64,64>, RegInfo<32,32,32>]>; } -// We currently define separate register classes for the 32-bit and 64-bit -// GPRs. Once variable-sized register classes -// <http://lists.llvm.org/pipermail/llvm-dev/2016-September/105027.html> or -// similar are implemented, we can just use one 'GPR' class for most -// instruction definitions. +def GPRNoX0X2 : RegisterClass<"RISCV", [XLenVT], 32, (add + (sequence "X%u", 10, 17), + (sequence "X%u", 5, 7), + (sequence "X%u", 28, 31), + (sequence "X%u", 8, 9), + (sequence "X%u", 18, 27), + X1, X3, X4 + )> { + let RegInfos = RegInfoByHwMode< + [RV32, RV64, DefaultMode], + [RegInfo<32,32,32>, RegInfo<64,64,64>, RegInfo<32,32,32>]>; +} + +def GPRC : RegisterClass<"RISCV", [XLenVT], 32, (add + (sequence "X%u", 10, 15), + (sequence "X%u", 8, 9) + )> { + let RegInfos = RegInfoByHwMode< + [RV32, RV64, DefaultMode], + [RegInfo<32,32,32>, RegInfo<64,64,64>, RegInfo<32,32,32>]>; +} + +def SP : RegisterClass<"RISCV", [XLenVT], 32, (add X2)> { + let RegInfos = RegInfoByHwMode< + [RV32, RV64, DefaultMode], + [RegInfo<32,32,32>, RegInfo<64,64,64>, RegInfo<32,32,32>]>; +} + +// Floating point registers +let RegAltNameIndices = [ABIRegAltName] in { + def F0_32 : RISCVReg32<0, "f0", ["ft0"]>, DwarfRegNum<[32]>; + def F1_32 : RISCVReg32<1, "f1", ["ft1"]>, DwarfRegNum<[33]>; + def F2_32 : RISCVReg32<2, "f2", ["ft2"]>, DwarfRegNum<[34]>; + def F3_32 : RISCVReg32<3, "f3", ["ft3"]>, DwarfRegNum<[35]>; + def F4_32 : RISCVReg32<4, "f4", ["ft4"]>, DwarfRegNum<[36]>; + def F5_32 : RISCVReg32<5, "f5", ["ft5"]>, DwarfRegNum<[37]>; + def F6_32 : RISCVReg32<6, "f6", ["ft6"]>, DwarfRegNum<[38]>; + def F7_32 : RISCVReg32<7, "f7", ["ft7"]>, DwarfRegNum<[39]>; + def F8_32 : RISCVReg32<8, "f8", ["fs0"]>, DwarfRegNum<[40]>; + def F9_32 : RISCVReg32<9, "f9", ["fs1"]>, DwarfRegNum<[41]>; + def F10_32 : RISCVReg32<10,"f10", ["fa0"]>, DwarfRegNum<[42]>; + def F11_32 : RISCVReg32<11,"f11", ["fa1"]>, DwarfRegNum<[43]>; + def F12_32 : RISCVReg32<12,"f12", ["fa2"]>, DwarfRegNum<[44]>; + def F13_32 : RISCVReg32<13,"f13", ["fa3"]>, DwarfRegNum<[45]>; + def F14_32 : RISCVReg32<14,"f14", ["fa4"]>, DwarfRegNum<[46]>; + def F15_32 : RISCVReg32<15,"f15", ["fa5"]>, DwarfRegNum<[47]>; + def F16_32 : RISCVReg32<16,"f16", ["fa6"]>, DwarfRegNum<[48]>; + def F17_32 : RISCVReg32<17,"f17", ["fa7"]>, DwarfRegNum<[49]>; + def F18_32 : RISCVReg32<18,"f18", ["fs2"]>, DwarfRegNum<[50]>; + def F19_32 : RISCVReg32<19,"f19", ["fs3"]>, DwarfRegNum<[51]>; + def F20_32 : RISCVReg32<20,"f20", ["fs4"]>, DwarfRegNum<[52]>; + def F21_32 : RISCVReg32<21,"f21", ["fs5"]>, DwarfRegNum<[53]>; + def F22_32 : RISCVReg32<22,"f22", ["fs6"]>, DwarfRegNum<[54]>; + def F23_32 : RISCVReg32<23,"f23", ["fs7"]>, DwarfRegNum<[55]>; + def F24_32 : RISCVReg32<24,"f24", ["fs8"]>, DwarfRegNum<[56]>; + def F25_32 : RISCVReg32<25,"f25", ["fs9"]>, DwarfRegNum<[57]>; + def F26_32 : RISCVReg32<26,"f26", ["fs10"]>, DwarfRegNum<[58]>; + def F27_32 : RISCVReg32<27,"f27", ["fs11"]>, DwarfRegNum<[59]>; + def F28_32 : RISCVReg32<28,"f28", ["ft8"]>, DwarfRegNum<[60]>; + def F29_32 : RISCVReg32<29,"f29", ["ft9"]>, DwarfRegNum<[61]>; + def F30_32 : RISCVReg32<30,"f30", ["ft10"]>, DwarfRegNum<[62]>; + def F31_32 : RISCVReg32<31,"f31", ["ft11"]>, DwarfRegNum<[63]>; + + foreach Index = 0-31 in { + def F#Index#_64 : RISCVReg64<!cast<RISCVReg32>("F"#Index#"_32")>, + DwarfRegNum<[!add(Index, 32)]>; + } +} + +// The order of registers represents the preferred allocation sequence, +// meaning caller-save regs are listed before callee-save. +def FPR32 : RegisterClass<"RISCV", [f32], 32, (add + (sequence "F%u_32", 0, 7), + (sequence "F%u_32", 10, 17), + (sequence "F%u_32", 28, 31), + (sequence "F%u_32", 8, 9), + (sequence "F%u_32", 18, 27) +)>; + +def FPR32C : RegisterClass<"RISCV", [f32], 32, (add + (sequence "F%u_32", 10, 15), + (sequence "F%u_32", 8, 9) +)>; -// TODO: once codegen is implemented, registers should be listed in an order -// reflecting the preferred register allocation sequence. -def GPR : RegisterClass<"RISCV", [i32], 32, (add - (sequence "X%u_32", 0, 31) +// The order of registers represents the preferred allocation sequence, +// meaning caller-save regs are listed before callee-save. +def FPR64 : RegisterClass<"RISCV", [f64], 64, (add + (sequence "F%u_64", 0, 7), + (sequence "F%u_64", 10, 17), + (sequence "F%u_64", 28, 31), + (sequence "F%u_64", 8, 9), + (sequence "F%u_64", 18, 27) )>; -def GPR64 : RegisterClass<"RISCV", [i64], 64, (add - (sequence "X%u_64", 0, 31) +def FPR64C : RegisterClass<"RISCV", [f64], 64, (add + (sequence "F%u_64", 10, 15), + (sequence "F%u_64", 8, 9) )>; diff --git a/lib/Target/RISCV/RISCVSubtarget.cpp b/lib/Target/RISCV/RISCVSubtarget.cpp new file mode 100644 index 000000000000..b221ea84a33c --- /dev/null +++ b/lib/Target/RISCV/RISCVSubtarget.cpp @@ -0,0 +1,48 @@ +//===-- RISCVSubtarget.cpp - RISCV Subtarget Information ------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file implements the RISCV specific subclass of TargetSubtargetInfo. +// +//===----------------------------------------------------------------------===// + +#include "RISCVSubtarget.h" +#include "RISCV.h" +#include "RISCVFrameLowering.h" +#include "llvm/Support/TargetRegistry.h" + +using namespace llvm; + +#define DEBUG_TYPE "riscv-subtarget" + +#define GET_SUBTARGETINFO_TARGET_DESC +#define GET_SUBTARGETINFO_CTOR +#include "RISCVGenSubtargetInfo.inc" + +void RISCVSubtarget::anchor() {} + +RISCVSubtarget &RISCVSubtarget::initializeSubtargetDependencies(StringRef CPU, + StringRef FS, + bool Is64Bit) { + // Determine default and user-specified characteristics + std::string CPUName = CPU; + if (CPUName.empty()) + CPUName = Is64Bit ? "generic-rv64" : "generic-rv32"; + ParseSubtargetFeatures(CPUName, FS); + if (Is64Bit) { + XLenVT = MVT::i64; + XLen = 64; + } + return *this; +} + +RISCVSubtarget::RISCVSubtarget(const Triple &TT, const std::string &CPU, + const std::string &FS, const TargetMachine &TM) + : RISCVGenSubtargetInfo(TT, CPU, FS), + FrameLowering(initializeSubtargetDependencies(CPU, FS, TT.isArch64Bit())), + InstrInfo(), RegInfo(getHwMode()), TLInfo(TM, *this) {} diff --git a/lib/Target/RISCV/RISCVSubtarget.h b/lib/Target/RISCV/RISCVSubtarget.h new file mode 100644 index 000000000000..928ba5815a22 --- /dev/null +++ b/lib/Target/RISCV/RISCVSubtarget.h @@ -0,0 +1,85 @@ +//===-- RISCVSubtarget.h - Define Subtarget for the RISCV -------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file declares the RISCV specific subclass of TargetSubtargetInfo. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_RISCV_RISCVSUBTARGET_H +#define LLVM_LIB_TARGET_RISCV_RISCVSUBTARGET_H + +#include "RISCVFrameLowering.h" +#include "RISCVISelLowering.h" +#include "RISCVInstrInfo.h" +#include "llvm/CodeGen/SelectionDAGTargetInfo.h" +#include "llvm/CodeGen/TargetSubtargetInfo.h" +#include "llvm/IR/DataLayout.h" +#include "llvm/Target/TargetMachine.h" + +#define GET_SUBTARGETINFO_HEADER +#include "RISCVGenSubtargetInfo.inc" + +namespace llvm { +class StringRef; + +class RISCVSubtarget : public RISCVGenSubtargetInfo { + virtual void anchor(); + bool HasStdExtM = false; + bool HasStdExtA = false; + bool HasStdExtF = false; + bool HasStdExtD = false; + bool HasStdExtC = false; + bool HasRV64 = false; + unsigned XLen = 32; + MVT XLenVT = MVT::i32; + RISCVFrameLowering FrameLowering; + RISCVInstrInfo InstrInfo; + RISCVRegisterInfo RegInfo; + RISCVTargetLowering TLInfo; + SelectionDAGTargetInfo TSInfo; + + /// Initializes using the passed in CPU and feature strings so that we can + /// use initializer lists for subtarget initialization. + RISCVSubtarget &initializeSubtargetDependencies(StringRef CPU, StringRef FS, + bool Is64Bit); + +public: + // Initializes the data members to match that of the specified triple. + RISCVSubtarget(const Triple &TT, const std::string &CPU, + const std::string &FS, const TargetMachine &TM); + + // Parses features string setting specified subtarget options. The + // definition of this function is auto-generated by tblgen. + void ParseSubtargetFeatures(StringRef CPU, StringRef FS); + + const RISCVFrameLowering *getFrameLowering() const override { + return &FrameLowering; + } + const RISCVInstrInfo *getInstrInfo() const override { return &InstrInfo; } + const RISCVRegisterInfo *getRegisterInfo() const override { + return &RegInfo; + } + const RISCVTargetLowering *getTargetLowering() const override { + return &TLInfo; + } + const SelectionDAGTargetInfo *getSelectionDAGInfo() const override { + return &TSInfo; + } + bool hasStdExtM() const { return HasStdExtM; } + bool hasStdExtA() const { return HasStdExtA; } + bool hasStdExtF() const { return HasStdExtF; } + bool hasStdExtD() const { return HasStdExtD; } + bool hasStdExtC() const { return HasStdExtC; } + bool is64Bit() const { return HasRV64; } + MVT getXLenVT() const { return XLenVT; } + unsigned getXLen() const { return XLen; } +}; +} // End llvm namespace + +#endif diff --git a/lib/Target/RISCV/RISCVTargetMachine.cpp b/lib/Target/RISCV/RISCVTargetMachine.cpp index 744d7b8aaa3a..e12168b73999 100644 --- a/lib/Target/RISCV/RISCVTargetMachine.cpp +++ b/lib/Target/RISCV/RISCVTargetMachine.cpp @@ -11,6 +11,7 @@ // //===----------------------------------------------------------------------===// +#include "RISCV.h" #include "RISCVTargetMachine.h" #include "llvm/ADT/STLExtras.h" #include "llvm/CodeGen/Passes.h" @@ -29,7 +30,7 @@ extern "C" void LLVMInitializeRISCVTarget() { static std::string computeDataLayout(const Triple &TT) { if (TT.isArch64Bit()) { - return "e-m:e-i64:64-n32:64-S128"; + return "e-m:e-p:64:64-i64:64-i128:128-n64-S128"; } else { assert(TT.isArch32Bit() && "only RV32 and RV64 are currently supported"); return "e-m:e-p:32:32-i64:64-n32-S128"; @@ -43,18 +44,46 @@ static Reloc::Model getEffectiveRelocModel(const Triple &TT, return *RM; } +static CodeModel::Model getEffectiveCodeModel(Optional<CodeModel::Model> CM) { + if (CM) + return *CM; + return CodeModel::Small; +} + RISCVTargetMachine::RISCVTargetMachine(const Target &T, const Triple &TT, StringRef CPU, StringRef FS, const TargetOptions &Options, Optional<Reloc::Model> RM, - CodeModel::Model CM, - CodeGenOpt::Level OL) + Optional<CodeModel::Model> CM, + CodeGenOpt::Level OL, bool JIT) : LLVMTargetMachine(T, computeDataLayout(TT), TT, CPU, FS, Options, - getEffectiveRelocModel(TT, RM), CM, OL), - TLOF(make_unique<TargetLoweringObjectFileELF>()) { + getEffectiveRelocModel(TT, RM), + getEffectiveCodeModel(CM), OL), + TLOF(make_unique<TargetLoweringObjectFileELF>()), + Subtarget(TT, CPU, FS, *this) { initAsmInfo(); } +namespace { +class RISCVPassConfig : public TargetPassConfig { +public: + RISCVPassConfig(RISCVTargetMachine &TM, PassManagerBase &PM) + : TargetPassConfig(TM, PM) {} + + RISCVTargetMachine &getRISCVTargetMachine() const { + return getTM<RISCVTargetMachine>(); + } + + bool addInstSelector() override; +}; +} + TargetPassConfig *RISCVTargetMachine::createPassConfig(PassManagerBase &PM) { - return new TargetPassConfig(*this, PM); + return new RISCVPassConfig(*this, PM); +} + +bool RISCVPassConfig::addInstSelector() { + addPass(createRISCVISelDag(getRISCVTargetMachine())); + + return false; } diff --git a/lib/Target/RISCV/RISCVTargetMachine.h b/lib/Target/RISCV/RISCVTargetMachine.h index d13e574c9bf8..02361dddebf7 100644 --- a/lib/Target/RISCV/RISCVTargetMachine.h +++ b/lib/Target/RISCV/RISCVTargetMachine.h @@ -15,6 +15,7 @@ #define LLVM_LIB_TARGET_RISCV_RISCVTARGETMACHINE_H #include "MCTargetDesc/RISCVMCTargetDesc.h" +#include "RISCVSubtarget.h" #include "llvm/CodeGen/SelectionDAGTargetInfo.h" #include "llvm/IR/DataLayout.h" #include "llvm/Target/TargetMachine.h" @@ -22,12 +23,17 @@ namespace llvm { class RISCVTargetMachine : public LLVMTargetMachine { std::unique_ptr<TargetLoweringObjectFile> TLOF; + RISCVSubtarget Subtarget; public: RISCVTargetMachine(const Target &T, const Triple &TT, StringRef CPU, StringRef FS, const TargetOptions &Options, - Optional<Reloc::Model> RM, CodeModel::Model CM, - CodeGenOpt::Level OL); + Optional<Reloc::Model> RM, Optional<CodeModel::Model> CM, + CodeGenOpt::Level OL, bool JIT); + + const RISCVSubtarget *getSubtargetImpl(const Function &) const override { + return &Subtarget; + } TargetPassConfig *createPassConfig(PassManagerBase &PM) override; diff --git a/lib/Target/RISCV/TargetInfo/RISCVTargetInfo.cpp b/lib/Target/RISCV/TargetInfo/RISCVTargetInfo.cpp index 34932c259156..0f369d960fe1 100644 --- a/lib/Target/RISCV/TargetInfo/RISCVTargetInfo.cpp +++ b/lib/Target/RISCV/TargetInfo/RISCVTargetInfo.cpp @@ -24,7 +24,7 @@ Target &getTheRISCV64Target() { extern "C" void LLVMInitializeRISCVTargetInfo() { RegisterTarget<Triple::riscv32> X(getTheRISCV32Target(), "riscv32", - "32-bit RISC-V"); + "32-bit RISC-V", "RISCV"); RegisterTarget<Triple::riscv64> Y(getTheRISCV64Target(), "riscv64", - "64-bit RISC-V"); + "64-bit RISC-V", "RISCV"); } |
