diff options
Diffstat (limited to 'lib/Target/RISCV')
47 files changed, 3767 insertions, 336 deletions
diff --git a/lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp b/lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp index 3299a53ff5ba..9a455c105482 100644 --- a/lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp +++ b/lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp @@ -10,11 +10,13 @@ #include "MCTargetDesc/RISCVBaseInfo.h" #include "MCTargetDesc/RISCVMCExpr.h" #include "MCTargetDesc/RISCVMCTargetDesc.h" +#include "MCTargetDesc/RISCVTargetStreamer.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/MCInstBuilder.h" #include "llvm/MC/MCParser/MCAsmLexer.h" #include "llvm/MC/MCParser/MCParsedAsmOperand.h" #include "llvm/MC/MCParser/MCTargetAsmParser.h" @@ -22,10 +24,17 @@ #include "llvm/MC/MCStreamer.h" #include "llvm/MC/MCSubtargetInfo.h" #include "llvm/Support/Casting.h" +#include "llvm/Support/MathExtras.h" #include "llvm/Support/TargetRegistry.h" +#include <limits> + using namespace llvm; +// Include the auto-generated portion of the compress emitter. +#define GEN_COMPRESS_INSTR +#include "RISCVGenCompressInstEmitter.inc" + namespace { struct RISCVOperand; @@ -33,11 +42,16 @@ class RISCVAsmParser : public MCTargetAsmParser { SMLoc getLoc() const { return getParser().getTok().getLoc(); } bool isRV64() const { return getSTI().hasFeature(RISCV::Feature64Bit); } + RISCVTargetStreamer &getTargetStreamer() { + MCTargetStreamer &TS = *getParser().getStreamer().getTargetStreamer(); + return static_cast<RISCVTargetStreamer &>(TS); + } + unsigned validateTargetOperandClass(MCParsedAsmOperand &Op, unsigned Kind) override; bool generateImmOutOfRangeError(OperandVector &Operands, uint64_t ErrorInfo, - int Lower, int Upper, Twine Msg); + int64_t Lower, int64_t Upper, Twine Msg); bool MatchAndEmitInstruction(SMLoc IDLoc, unsigned &Opcode, OperandVector &Operands, MCStreamer &Out, @@ -51,6 +65,20 @@ class RISCVAsmParser : public MCTargetAsmParser { bool ParseDirective(AsmToken DirectiveID) override; + // Helper to actually emit an instruction to the MCStreamer. Also, when + // possible, compression of the instruction is performed. + void emitToStreamer(MCStreamer &S, const MCInst &Inst); + + // Helper to emit a combination of LUI, ADDI(W), and SLLI instructions that + // synthesize the desired immedate value into the destination register. + void emitLoadImm(unsigned DestReg, int64_t Value, MCStreamer &Out); + + /// Helper for processing MC instructions that have been successfully matched + /// by MatchAndEmitInstruction. Modifications to the emitted instructions, + /// like the expansion of pseudo instructions (e.g., "li"), can be performed + /// in this method. + bool processInstruction(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out); + // Auto-generated instruction matching functions #define GET_ASSEMBLER_HEADER #include "RISCVGenAsmMatcher.inc" @@ -61,8 +89,25 @@ class RISCVAsmParser : public MCTargetAsmParser { OperandMatchResultTy parseMemOpBaseReg(OperandVector &Operands); OperandMatchResultTy parseOperandWithModifier(OperandVector &Operands); - bool parseOperand(OperandVector &Operands); + bool parseOperand(OperandVector &Operands, bool ForceImmediate); + + bool parseDirectiveOption(); + + void setFeatureBits(uint64_t Feature, StringRef FeatureString) { + if (!(getSTI().getFeatureBits()[Feature])) { + MCSubtargetInfo &STI = copySTI(); + setAvailableFeatures( + ComputeAvailableFeatures(STI.ToggleFeature(FeatureString))); + } + } + void clearFeatureBits(uint64_t Feature, StringRef FeatureString) { + if (getSTI().getFeatureBits()[Feature]) { + MCSubtargetInfo &STI = copySTI(); + setAvailableFeatures( + ComputeAvailableFeatures(STI.ToggleFeature(FeatureString))); + } + } public: enum RISCVMatchResultTy { Match_Dummy = FIRST_TARGET_MATCH_RESULT_TY, @@ -78,6 +123,10 @@ public: RISCVAsmParser(const MCSubtargetInfo &STI, MCAsmParser &Parser, const MCInstrInfo &MII, const MCTargetOptions &Options) : MCTargetAsmParser(Options, STI, MII) { + Parser.addAliasForDirective(".half", ".2byte"); + Parser.addAliasForDirective(".hword", ".2byte"); + Parser.addAliasForDirective(".word", ".4byte"); + Parser.addAliasForDirective(".dword", ".8byte"); setAvailableFeatures(ComputeAvailableFeatures(STI.getFeatureBits())); } }; @@ -167,6 +216,16 @@ public: // Predicate methods for AsmOperands defined in RISCVInstrInfo.td + bool isBareSymbol() const { + int64_t Imm; + RISCVMCExpr::VariantKind VK; + // Must be of 'immediate' type but not a constant. + if (!isImm() || evaluateConstantImm(Imm, VK)) + return false; + return RISCVAsmParser::classifySymbolRef(getImm(), VK, Imm) && + VK == RISCVMCExpr::VK_RISCV_None; + } + /// Return true if the operand is a valid for the fence instruction e.g. /// ('iorw'). bool isFenceArg() const { @@ -206,6 +265,18 @@ public: return RISCVFPRndMode::stringToRoundingMode(Str) != RISCVFPRndMode::Invalid; } + bool isImmXLen() const { + int64_t Imm; + RISCVMCExpr::VariantKind VK; + if (!isImm()) + return false; + bool IsConstantImm = evaluateConstantImm(Imm, VK); + // Given only Imm, ensuring that the actually specified constant is either + // a signed or unsigned 64-bit number is unfortunately impossible. + bool IsInRange = isRV64() ? true : isInt<32>(Imm) || isUInt<32>(Imm); + return IsConstantImm && IsInRange && VK == RISCVMCExpr::VK_RISCV_None; + } + bool isUImmLog2XLen() const { int64_t Imm; RISCVMCExpr::VariantKind VK; @@ -260,12 +331,26 @@ public: (VK == RISCVMCExpr::VK_RISCV_None || VK == RISCVMCExpr::VK_RISCV_LO); } - bool isUImm6NonZero() const { + bool isSImm6NonZero() const { + RISCVMCExpr::VariantKind VK; + int64_t Imm; + bool IsValid; + bool IsConstantImm = evaluateConstantImm(Imm, VK); + if (!IsConstantImm) + IsValid = RISCVAsmParser::classifySymbolRef(getImm(), VK, Imm); + else + IsValid = ((Imm != 0) && isInt<6>(Imm)); + return IsValid && + (VK == RISCVMCExpr::VK_RISCV_None || VK == RISCVMCExpr::VK_RISCV_LO); + } + + bool isCLUIImm() const { int64_t Imm; RISCVMCExpr::VariantKind VK; bool IsConstantImm = evaluateConstantImm(Imm, VK); - return IsConstantImm && isUInt<6>(Imm) && (Imm != 0) && - VK == RISCVMCExpr::VK_RISCV_None; + return IsConstantImm && (Imm != 0) && + (isUInt<5>(Imm) || (Imm >= 0xfffe0 && Imm <= 0xfffff)) && + VK == RISCVMCExpr::VK_RISCV_None; } bool isUImm7Lsb00() const { @@ -321,8 +406,9 @@ public: IsValid = RISCVAsmParser::classifySymbolRef(getImm(), VK, Imm); else IsValid = isInt<12>(Imm); - return IsValid && - (VK == RISCVMCExpr::VK_RISCV_None || VK == RISCVMCExpr::VK_RISCV_LO); + return IsValid && (VK == RISCVMCExpr::VK_RISCV_None || + VK == RISCVMCExpr::VK_RISCV_LO || + VK == RISCVMCExpr::VK_RISCV_PCREL_LO); } bool isSImm12Lsb0() const { return isBareSimmNLsb0<12>(); } @@ -338,11 +424,11 @@ public: bool isSImm13Lsb0() const { return isBareSimmNLsb0<13>(); } - bool isSImm10Lsb0000() const { + bool isSImm10Lsb0000NonZero() const { int64_t Imm; RISCVMCExpr::VariantKind VK; bool IsConstantImm = evaluateConstantImm(Imm, VK); - return IsConstantImm && isShiftedInt<6, 4>(Imm) && + return IsConstantImm && (Imm != 0) && isShiftedInt<6, 4>(Imm) && VK == RISCVMCExpr::VK_RISCV_None; } @@ -564,7 +650,7 @@ unsigned RISCVAsmParser::validateTargetOperandClass(MCParsedAsmOperand &AsmOp, } bool RISCVAsmParser::generateImmOutOfRangeError( - OperandVector &Operands, uint64_t ErrorInfo, int Lower, int Upper, + OperandVector &Operands, uint64_t ErrorInfo, int64_t Lower, int64_t 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) + "]"); @@ -581,9 +667,7 @@ bool RISCVAsmParser::MatchAndEmitInstruction(SMLoc IDLoc, unsigned &Opcode, default: break; case Match_Success: - Inst.setLoc(IDLoc); - Out.EmitInstruction(Inst, getSTI()); - return false; + return processInstruction(Inst, IDLoc, Out); case Match_MissingFeature: return Error(IDLoc, "instruction use requires an option to be enabled"); case Match_MnemonicFail: @@ -600,6 +684,14 @@ bool RISCVAsmParser::MatchAndEmitInstruction(SMLoc IDLoc, unsigned &Opcode, } return Error(ErrorLoc, "invalid operand for instruction"); } + case Match_InvalidImmXLen: + if (isRV64()) { + SMLoc ErrorLoc = ((RISCVOperand &)*Operands[ErrorInfo]).getStartLoc(); + return Error(ErrorLoc, "operand must be a constant 64-bit integer"); + } + return generateImmOutOfRangeError(Operands, ErrorInfo, + std::numeric_limits<int32_t>::min(), + std::numeric_limits<uint32_t>::max()); case Match_InvalidUImmLog2XLen: if (isRV64()) return generateImmOutOfRangeError(Operands, ErrorInfo, 0, (1 << 6) - 1); @@ -613,8 +705,14 @@ bool RISCVAsmParser::MatchAndEmitInstruction(SMLoc IDLoc, unsigned &Opcode, 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_InvalidSImm6NonZero: + return generateImmOutOfRangeError(Operands, ErrorInfo, -(1 << 5), + (1 << 5) - 1, + "immediate must be non-zero in the range"); + case Match_InvalidCLUIImm: + return generateImmOutOfRangeError( + Operands, ErrorInfo, 1, (1 << 5) - 1, + "immediate must be in [0xfffe0, 0xfffff] or"); case Match_InvalidUImm7Lsb00: return generateImmOutOfRangeError( Operands, ErrorInfo, 0, (1 << 7) - 4, @@ -639,10 +737,10 @@ bool RISCVAsmParser::MatchAndEmitInstruction(SMLoc IDLoc, unsigned &Opcode, return generateImmOutOfRangeError( Operands, ErrorInfo, 4, (1 << 10) - 4, "immediate must be a multiple of 4 bytes in the range"); - case Match_InvalidSImm10Lsb0000: + case Match_InvalidSImm10Lsb0000NonZero: return generateImmOutOfRangeError( Operands, ErrorInfo, -(1 << 9), (1 << 9) - 16, - "immediate must be a multiple of 16 bytes in the range"); + "immediate must be a multiple of 16 bytes and non-zero in the range"); case Match_InvalidSImm12: return generateImmOutOfRangeError(Operands, ErrorInfo, -(1 << 11), (1 << 11) - 1); @@ -674,6 +772,10 @@ bool RISCVAsmParser::MatchAndEmitInstruction(SMLoc IDLoc, unsigned &Opcode, ErrorLoc, "operand must be a valid floating point rounding mode mnemonic"); } + case Match_InvalidBareSymbol: { + SMLoc ErrorLoc = ((RISCVOperand &)*Operands[ErrorInfo]).getStartLoc(); + return Error(ErrorLoc, "operand must be a bare symbol name"); + } } llvm_unreachable("Unknown match type detected!"); @@ -838,12 +940,15 @@ RISCVAsmParser::parseMemOpBaseReg(OperandVector &Operands) { 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) +/// Looks at a token type and creates the relevant operand from this +/// information, adding to Operands. If operand was parsed, returns false, else +/// true. If ForceImmediate is true, no attempt will be made to parse the +/// operand as a register, which is needed for pseudoinstructions such as +/// call. +bool RISCVAsmParser::parseOperand(OperandVector &Operands, + bool ForceImmediate) { + // Attempt to parse token as register, unless ForceImmediate. + if (!ForceImmediate && parseRegister(Operands, true) == MatchOperand_Success) return false; // Attempt to parse token as an immediate @@ -870,7 +975,8 @@ bool RISCVAsmParser::ParseInstruction(ParseInstructionInfo &Info, return false; // Parse first operand - if (parseOperand(Operands)) + bool ForceImmediate = (Name == "call" || Name == "tail"); + if (parseOperand(Operands, ForceImmediate)) return true; // Parse until end of statement, consuming commas between operands @@ -879,7 +985,7 @@ bool RISCVAsmParser::ParseInstruction(ParseInstructionInfo &Info, getLexer().Lex(); // Parse next operand - if (parseOperand(Operands)) + if (parseOperand(Operands, false)) return true; } @@ -924,7 +1030,7 @@ bool RISCVAsmParser::classifySymbolRef(const MCExpr *Expr, isa<MCSymbolRefExpr>(BE->getRHS())) return true; - // See if the addend is is a constant, otherwise there's more going + // See if the addend is a constant, otherwise there's more going // on here than we can deal with. auto AddendExpr = dyn_cast<MCConstantExpr>(BE->getRHS()); if (!AddendExpr) @@ -938,7 +1044,165 @@ bool RISCVAsmParser::classifySymbolRef(const MCExpr *Expr, return Kind != RISCVMCExpr::VK_RISCV_Invalid; } -bool RISCVAsmParser::ParseDirective(AsmToken DirectiveID) { return true; } +bool RISCVAsmParser::ParseDirective(AsmToken DirectiveID) { + // This returns false if this function recognizes the directive + // regardless of whether it is successfully handles or reports an + // error. Otherwise it returns true to give the generic parser a + // chance at recognizing it. + StringRef IDVal = DirectiveID.getString(); + + if (IDVal == ".option") + return parseDirectiveOption(); + + return true; +} + +bool RISCVAsmParser::parseDirectiveOption() { + MCAsmParser &Parser = getParser(); + // Get the option token. + AsmToken Tok = Parser.getTok(); + // At the moment only identifiers are supported. + if (Tok.isNot(AsmToken::Identifier)) + return Error(Parser.getTok().getLoc(), + "unexpected token, expected identifier"); + + StringRef Option = Tok.getIdentifier(); + + if (Option == "rvc") { + getTargetStreamer().emitDirectiveOptionRVC(); + + Parser.Lex(); + if (Parser.getTok().isNot(AsmToken::EndOfStatement)) + return Error(Parser.getTok().getLoc(), + "unexpected token, expected end of statement"); + + setFeatureBits(RISCV::FeatureStdExtC, "c"); + return false; + } + + if (Option == "norvc") { + getTargetStreamer().emitDirectiveOptionNoRVC(); + + Parser.Lex(); + if (Parser.getTok().isNot(AsmToken::EndOfStatement)) + return Error(Parser.getTok().getLoc(), + "unexpected token, expected end of statement"); + + clearFeatureBits(RISCV::FeatureStdExtC, "c"); + return false; + } + + // Unknown option. + Warning(Parser.getTok().getLoc(), + "unknown option, expected 'rvc' or 'norvc'"); + Parser.eatToEndOfStatement(); + return false; +} + +void RISCVAsmParser::emitToStreamer(MCStreamer &S, const MCInst &Inst) { + MCInst CInst; + bool Res = compressInst(CInst, Inst, getSTI(), S.getContext()); + CInst.setLoc(Inst.getLoc()); + S.EmitInstruction((Res ? CInst : Inst), getSTI()); +} + +void RISCVAsmParser::emitLoadImm(unsigned DestReg, int64_t Value, + MCStreamer &Out) { + if (isInt<32>(Value)) { + // Emits the MC instructions for loading a 32-bit constant into a register. + // + // Depending on the active bits in the immediate Value v, the following + // instruction sequences are emitted: + // + // v == 0 : ADDI(W) + // v[0,12) != 0 && v[12,32) == 0 : ADDI(W) + // v[0,12) == 0 && v[12,32) != 0 : LUI + // v[0,32) != 0 : LUI+ADDI(W) + // + int64_t Hi20 = ((Value + 0x800) >> 12) & 0xFFFFF; + int64_t Lo12 = SignExtend64<12>(Value); + unsigned SrcReg = RISCV::X0; + + if (Hi20) { + emitToStreamer(Out, + MCInstBuilder(RISCV::LUI).addReg(DestReg).addImm(Hi20)); + SrcReg = DestReg; + } + + if (Lo12 || Hi20 == 0) { + unsigned AddiOpcode = + STI->hasFeature(RISCV::Feature64Bit) ? RISCV::ADDIW : RISCV::ADDI; + emitToStreamer(Out, MCInstBuilder(AddiOpcode) + .addReg(DestReg) + .addReg(SrcReg) + .addImm(Lo12)); + } + return; + } + assert(STI->hasFeature(RISCV::Feature64Bit) && + "Target must be 64-bit to support a >32-bit constant"); + + // In the worst case, for a full 64-bit constant, a sequence of 8 instructions + // (i.e., LUI+ADDIW+SLLI+ADDI+SLLI+ADDI+SLLI+ADDI) has to be emmitted. Note + // that the first two instructions (LUI+ADDIW) can contribute up to 32 bits + // while the following ADDI instructions contribute up to 12 bits each. + // + // On the first glance, implementing this seems to be possible by simply + // emitting the most significant 32 bits (LUI+ADDIW) followed by as many left + // shift (SLLI) and immediate additions (ADDI) as needed. However, due to the + // fact that ADDI performs a sign extended addition, doing it like that would + // only be possible when at most 11 bits of the ADDI instructions are used. + // Using all 12 bits of the ADDI instructions, like done by GAS, actually + // requires that the constant is processed starting with the least significant + // bit. + // + // In the following, constants are processed from LSB to MSB but instruction + // emission is performed from MSB to LSB by recursively calling + // emitLoadImm. In each recursion, first the lowest 12 bits are removed + // from the constant and the optimal shift amount, which can be greater than + // 12 bits if the constant is sparse, is determined. Then, the shifted + // remaining constant is processed recursively and gets emitted as soon as it + // fits into 32 bits. The emission of the shifts and additions is subsequently + // performed when the recursion returns. + // + int64_t Lo12 = SignExtend64<12>(Value); + int64_t Hi52 = (Value + 0x800) >> 12; + int ShiftAmount = 12 + findFirstSet((uint64_t)Hi52); + Hi52 = SignExtend64(Hi52 >> (ShiftAmount - 12), 64 - ShiftAmount); + + emitLoadImm(DestReg, Hi52, Out); + + emitToStreamer(Out, MCInstBuilder(RISCV::SLLI) + .addReg(DestReg) + .addReg(DestReg) + .addImm(ShiftAmount)); + + if (Lo12) + emitToStreamer(Out, MCInstBuilder(RISCV::ADDI) + .addReg(DestReg) + .addReg(DestReg) + .addImm(Lo12)); +} + +bool RISCVAsmParser::processInstruction(MCInst &Inst, SMLoc IDLoc, + MCStreamer &Out) { + Inst.setLoc(IDLoc); + + if (Inst.getOpcode() == RISCV::PseudoLI) { + auto Reg = Inst.getOperand(0).getReg(); + int64_t Imm = Inst.getOperand(1).getImm(); + // On RV32 the immediate here can either be a signed or an unsigned + // 32-bit number. Sign extension has to be performed to ensure that Imm + // represents the expected signed 64-bit number. + if (!isRV64()) + Imm = SignExtend64<32>(Imm); + emitLoadImm(Reg, Imm, Out); + return false; + } + + emitToStreamer(Out, Inst); + return false; +} extern "C" void LLVMInitializeRISCVAsmParser() { RegisterMCAsmParser<RISCVAsmParser> X(getTheRISCV32Target()); diff --git a/lib/Target/RISCV/CMakeLists.txt b/lib/Target/RISCV/CMakeLists.txt index 66b50f8728e1..f8d4e2b9517d 100644 --- a/lib/Target/RISCV/CMakeLists.txt +++ b/lib/Target/RISCV/CMakeLists.txt @@ -1,14 +1,15 @@ 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 RISCVGenCompressInstEmitter.inc -gen-compress-inst-emitter) tablegen(LLVM RISCVGenDAGISel.inc -gen-dag-isel) -tablegen(LLVM RISCVGenSubtargetInfo.inc -gen-subtarget) tablegen(LLVM RISCVGenDisassemblerTables.inc -gen-disassembler) +tablegen(LLVM RISCVGenInstrInfo.inc -gen-instr-info) +tablegen(LLVM RISCVGenMCCodeEmitter.inc -gen-emitter) +tablegen(LLVM RISCVGenMCPseudoLowering.inc -gen-pseudo-lowering) +tablegen(LLVM RISCVGenRegisterInfo.inc -gen-register-info) +tablegen(LLVM RISCVGenSubtargetInfo.inc -gen-subtarget) add_public_tablegen_target(RISCVCommonTableGen) @@ -19,9 +20,11 @@ add_llvm_target(RISCVCodeGen RISCVISelDAGToDAG.cpp RISCVISelLowering.cpp RISCVMCInstLower.cpp + RISCVMergeBaseOffset.cpp RISCVRegisterInfo.cpp RISCVSubtarget.cpp RISCVTargetMachine.cpp + RISCVTargetObjectFile.cpp ) add_subdirectory(AsmParser) diff --git a/lib/Target/RISCV/Disassembler/RISCVDisassembler.cpp b/lib/Target/RISCV/Disassembler/RISCVDisassembler.cpp index 563edc9e29d8..7bbb371a757f 100644 --- a/lib/Target/RISCV/Disassembler/RISCVDisassembler.cpp +++ b/lib/Target/RISCV/Disassembler/RISCVDisassembler.cpp @@ -232,6 +232,17 @@ static DecodeStatus decodeSImmOperandAndLsl1(MCInst &Inst, uint64_t Imm, return MCDisassembler::Success; } +static DecodeStatus decodeCLUIImmOperand(MCInst &Inst, uint64_t Imm, + int64_t Address, + const void *Decoder) { + assert(isUInt<6>(Imm) && "Invalid immediate"); + if (Imm > 31) { + Imm = (SignExtend64<6>(Imm) & 0xfffff); + } + Inst.addOperand(MCOperand::createImm(Imm)); + return MCDisassembler::Success; +} + #include "RISCVGenDisassemblerTables.inc" DecodeStatus RISCVDisassembler::getInstruction(MCInst &MI, uint64_t &Size, @@ -247,14 +258,15 @@ DecodeStatus RISCVDisassembler::getInstruction(MCInst &MI, uint64_t &Size, // 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"); + LLVM_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"); + LLVM_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); @@ -264,7 +276,7 @@ DecodeStatus RISCVDisassembler::getInstruction(MCInst &MI, uint64_t &Size, } } - DEBUG(dbgs() << "Trying RISCV_C table (16-bit Instruction):\n"); + LLVM_DEBUG(dbgs() << "Trying RISCV_C table (16-bit Instruction):\n"); // Calling the auto-generated decoder function. Result = decodeInstruction(DecoderTable16, MI, Insn, Address, this, STI); Size = 2; diff --git a/lib/Target/RISCV/InstPrinter/RISCVInstPrinter.cpp b/lib/Target/RISCV/InstPrinter/RISCVInstPrinter.cpp index ff56fc5d90ff..300e6fd9750a 100644 --- a/lib/Target/RISCV/InstPrinter/RISCVInstPrinter.cpp +++ b/lib/Target/RISCV/InstPrinter/RISCVInstPrinter.cpp @@ -13,10 +13,12 @@ #include "RISCVInstPrinter.h" #include "MCTargetDesc/RISCVBaseInfo.h" +#include "MCTargetDesc/RISCVMCExpr.h" #include "llvm/MC/MCAsmInfo.h" #include "llvm/MC/MCExpr.h" #include "llvm/MC/MCInst.h" #include "llvm/MC/MCRegisterInfo.h" +#include "llvm/MC/MCSubtargetInfo.h" #include "llvm/MC/MCSymbol.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/ErrorHandling.h" @@ -29,6 +31,10 @@ using namespace llvm; #define PRINT_ALIAS_INSTR #include "RISCVGenAsmWriter.inc" +// Include the auto-generated portion of the compress emitter. +#define GEN_UNCOMPRESS_INSTR +#include "RISCVGenCompressInstEmitter.inc" + static cl::opt<bool> NoAliases("riscv-no-aliases", cl::desc("Disable the emission of assembler pseudo instructions"), @@ -37,8 +43,15 @@ NoAliases("riscv-no-aliases", void RISCVInstPrinter::printInst(const MCInst *MI, raw_ostream &O, StringRef Annot, const MCSubtargetInfo &STI) { - if (NoAliases || !printAliasInstr(MI, O)) - printInstruction(MI, O); + bool Res = false; + const MCInst *NewMI = MI; + MCInst UncompressedMI; + if (!NoAliases) + Res = uncompressInst(UncompressedMI, *MI, MRI, STI); + if (Res) + NewMI = const_cast<MCInst*>(&UncompressedMI); + if (NoAliases || !printAliasInstr(NewMI, STI, O)) + printInstruction(NewMI, STI, O); printAnnotation(O, Annot); } @@ -47,6 +60,7 @@ void RISCVInstPrinter::printRegName(raw_ostream &O, unsigned RegNo) const { } void RISCVInstPrinter::printOperand(const MCInst *MI, unsigned OpNo, + const MCSubtargetInfo &STI, raw_ostream &O, const char *Modifier) { assert((Modifier == 0 || Modifier[0] == 0) && "No modifiers supported"); const MCOperand &MO = MI->getOperand(OpNo); @@ -66,6 +80,7 @@ void RISCVInstPrinter::printOperand(const MCInst *MI, unsigned OpNo, } void RISCVInstPrinter::printFenceArg(const MCInst *MI, unsigned OpNo, + const MCSubtargetInfo &STI, raw_ostream &O) { unsigned FenceArg = MI->getOperand(OpNo).getImm(); if ((FenceArg & RISCVFenceField::I) != 0) @@ -79,6 +94,7 @@ void RISCVInstPrinter::printFenceArg(const MCInst *MI, unsigned OpNo, } void RISCVInstPrinter::printFRMArg(const MCInst *MI, unsigned OpNo, + const MCSubtargetInfo &STI, raw_ostream &O) { auto FRMArg = static_cast<RISCVFPRndMode::RoundingMode>(MI->getOperand(OpNo).getImm()); diff --git a/lib/Target/RISCV/InstPrinter/RISCVInstPrinter.h b/lib/Target/RISCV/InstPrinter/RISCVInstPrinter.h index 58f3f8410159..241be8daf113 100644 --- a/lib/Target/RISCV/InstPrinter/RISCVInstPrinter.h +++ b/lib/Target/RISCV/InstPrinter/RISCVInstPrinter.h @@ -30,16 +30,21 @@ public: 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); + void printOperand(const MCInst *MI, unsigned OpNo, const MCSubtargetInfo &STI, + raw_ostream &O, const char *Modifier = nullptr); + void printFenceArg(const MCInst *MI, unsigned OpNo, + const MCSubtargetInfo &STI, raw_ostream &O); + void printFRMArg(const MCInst *MI, unsigned OpNo, const MCSubtargetInfo &STI, + raw_ostream &O); // Autogenerated by tblgen. - void printInstruction(const MCInst *MI, raw_ostream &O); - bool printAliasInstr(const MCInst *MI, raw_ostream &O); + void printInstruction(const MCInst *MI, const MCSubtargetInfo &STI, + raw_ostream &O); + bool printAliasInstr(const MCInst *MI, const MCSubtargetInfo &STI, + raw_ostream &O); void printCustomAliasOperand(const MCInst *MI, unsigned OpIdx, - unsigned PrintMethodIdx, raw_ostream &O); + unsigned PrintMethodIdx, + const MCSubtargetInfo &STI, raw_ostream &O); static const char *getRegisterName(unsigned RegNo, unsigned AltIdx = RISCV::ABIRegAltName); }; diff --git a/lib/Target/RISCV/MCTargetDesc/CMakeLists.txt b/lib/Target/RISCV/MCTargetDesc/CMakeLists.txt index 60429647edd1..d9f4188aa75c 100644 --- a/lib/Target/RISCV/MCTargetDesc/CMakeLists.txt +++ b/lib/Target/RISCV/MCTargetDesc/CMakeLists.txt @@ -5,4 +5,6 @@ add_llvm_library(LLVMRISCVDesc RISCVMCCodeEmitter.cpp RISCVMCExpr.cpp RISCVMCTargetDesc.cpp + RISCVTargetStreamer.cpp + RISCVELFStreamer.cpp ) diff --git a/lib/Target/RISCV/MCTargetDesc/RISCVAsmBackend.cpp b/lib/Target/RISCV/MCTargetDesc/RISCVAsmBackend.cpp index b91467fe1455..9ba7ebd0eb0f 100644 --- a/lib/Target/RISCV/MCTargetDesc/RISCVAsmBackend.cpp +++ b/lib/Target/RISCV/MCTargetDesc/RISCVAsmBackend.cpp @@ -27,46 +27,74 @@ using namespace llvm; namespace { class RISCVAsmBackend : public MCAsmBackend { + const MCSubtargetInfo &STI; uint8_t OSABI; bool Is64Bit; public: - RISCVAsmBackend(uint8_t OSABI, bool Is64Bit) - : MCAsmBackend(), OSABI(OSABI), Is64Bit(Is64Bit) {} + RISCVAsmBackend(const MCSubtargetInfo &STI, uint8_t OSABI, bool Is64Bit) + : MCAsmBackend(support::little), STI(STI), OSABI(OSABI), + Is64Bit(Is64Bit) {} ~RISCVAsmBackend() override {} + // Generate diff expression relocations if the relax feature is enabled, + // otherwise it is safe for the assembler to calculate these internally. + bool requiresDiffExpressionRelocations() const override { + return STI.getFeatureBits()[RISCV::FeatureRelax]; + } void applyFixup(const MCAssembler &Asm, const MCFixup &Fixup, const MCValue &Target, MutableArrayRef<char> Data, - uint64_t Value, bool IsResolved) const override; + uint64_t Value, bool IsResolved, + const MCSubtargetInfo *STI) const override; + + std::unique_ptr<MCObjectTargetWriter> + createObjectTargetWriter() const override; - std::unique_ptr<MCObjectWriter> - createObjectWriter(raw_pwrite_stream &OS) const override; + // If linker relaxation is enabled, always emit relocations even if the fixup + // can be resolved. This is necessary for correctness as offsets may change + // during relaxation. + bool shouldForceRelocation(const MCAssembler &Asm, const MCFixup &Fixup, + const MCValue &Target) override { + return STI.getFeatureBits()[RISCV::FeatureRelax]; + } bool fixupNeedsRelaxation(const MCFixup &Fixup, uint64_t Value, const MCRelaxableFragment *DF, const MCAsmLayout &Layout) const override { - return false; + llvm_unreachable("Handled by fixupNeedsRelaxationAdvanced"); } + bool fixupNeedsRelaxationAdvanced(const MCFixup &Fixup, bool Resolved, + uint64_t Value, + const MCRelaxableFragment *DF, + const MCAsmLayout &Layout, + const bool WasForced) const override; + unsigned getNumFixupKinds() const override { return RISCV::NumTargetFixupKinds; } const MCFixupKindInfo &getFixupKindInfo(MCFixupKind Kind) const override { - const static MCFixupKindInfo Infos[RISCV::NumTargetFixupKinds] = { + const static MCFixupKindInfo Infos[] = { // This table *must* be in the order that the fixup_* kinds are defined in // RISCVFixupKinds.h. // - // name offset bits flags - { "fixup_riscv_hi20", 12, 20, 0 }, - { "fixup_riscv_lo12_i", 20, 12, 0 }, - { "fixup_riscv_lo12_s", 0, 32, 0 }, - { "fixup_riscv_pcrel_hi20", 12, 20, MCFixupKindInfo::FKF_IsPCRel }, - { "fixup_riscv_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 } + // name offset bits flags + { "fixup_riscv_hi20", 12, 20, 0 }, + { "fixup_riscv_lo12_i", 20, 12, 0 }, + { "fixup_riscv_lo12_s", 0, 32, 0 }, + { "fixup_riscv_pcrel_hi20", 12, 20, MCFixupKindInfo::FKF_IsPCRel }, + { "fixup_riscv_pcrel_lo12_i", 20, 12, MCFixupKindInfo::FKF_IsPCRel }, + { "fixup_riscv_pcrel_lo12_s", 0, 32, MCFixupKindInfo::FKF_IsPCRel }, + { "fixup_riscv_jal", 12, 20, MCFixupKindInfo::FKF_IsPCRel }, + { "fixup_riscv_branch", 0, 32, MCFixupKindInfo::FKF_IsPCRel }, + { "fixup_riscv_rvc_jump", 2, 11, MCFixupKindInfo::FKF_IsPCRel }, + { "fixup_riscv_rvc_branch", 0, 16, MCFixupKindInfo::FKF_IsPCRel }, + { "fixup_riscv_call", 0, 64, MCFixupKindInfo::FKF_IsPCRel }, + { "fixup_riscv_relax", 0, 0, 0 } }; + static_assert((array_lengthof(Infos)) == RISCV::NumTargetFixupKinds, + "Not all fixup kinds added to Infos array"); if (Kind < FirstTargetFixupKind) return MCAsmBackend::getFixupKindInfo(Kind); @@ -76,26 +104,121 @@ public: return Infos[Kind - FirstTargetFixupKind]; } - bool mayNeedRelaxation(const MCInst &Inst) const override { return false; } + bool mayNeedRelaxation(const MCInst &Inst, + const MCSubtargetInfo &STI) const override; + unsigned getRelaxedOpcode(unsigned Op) const; void relaxInstruction(const MCInst &Inst, const MCSubtargetInfo &STI, - MCInst &Res) const override { + MCInst &Res) const override; - report_fatal_error("RISCVAsmBackend::relaxInstruction() unimplemented"); - } - bool writeNopData(uint64_t Count, MCObjectWriter *OW) const override; + bool writeNopData(raw_ostream &OS, uint64_t Count) const override; }; -bool RISCVAsmBackend::writeNopData(uint64_t Count, MCObjectWriter *OW) const { - // Once support for the compressed instruction set is added, we will be able - // to conditionally support 16-bit NOPs - if ((Count % 4) != 0) + +bool RISCVAsmBackend::fixupNeedsRelaxationAdvanced(const MCFixup &Fixup, + bool Resolved, + uint64_t Value, + const MCRelaxableFragment *DF, + const MCAsmLayout &Layout, + const bool WasForced) const { + // Return true if the symbol is actually unresolved. + // Resolved could be always false when shouldForceRelocation return true. + // We use !WasForced to indicate that the symbol is unresolved and not forced + // by shouldForceRelocation. + if (!Resolved && !WasForced) + return true; + + int64_t Offset = int64_t(Value); + switch ((unsigned)Fixup.getKind()) { + default: + return false; + case RISCV::fixup_riscv_rvc_branch: + // For compressed branch instructions the immediate must be + // in the range [-256, 254]. + return Offset > 254 || Offset < -256; + case RISCV::fixup_riscv_rvc_jump: + // For compressed jump instructions the immediate must be + // in the range [-2048, 2046]. + return Offset > 2046 || Offset < -2048; + } +} + +void RISCVAsmBackend::relaxInstruction(const MCInst &Inst, + const MCSubtargetInfo &STI, + MCInst &Res) const { + // TODO: replace this with call to auto generated uncompressinstr() function. + switch (Inst.getOpcode()) { + default: + llvm_unreachable("Opcode not expected!"); + case RISCV::C_BEQZ: + // c.beqz $rs1, $imm -> beq $rs1, X0, $imm. + Res.setOpcode(RISCV::BEQ); + Res.addOperand(Inst.getOperand(0)); + Res.addOperand(MCOperand::createReg(RISCV::X0)); + Res.addOperand(Inst.getOperand(1)); + break; + case RISCV::C_BNEZ: + // c.bnez $rs1, $imm -> bne $rs1, X0, $imm. + Res.setOpcode(RISCV::BNE); + Res.addOperand(Inst.getOperand(0)); + Res.addOperand(MCOperand::createReg(RISCV::X0)); + Res.addOperand(Inst.getOperand(1)); + break; + case RISCV::C_J: + // c.j $imm -> jal X0, $imm. + Res.setOpcode(RISCV::JAL); + Res.addOperand(MCOperand::createReg(RISCV::X0)); + Res.addOperand(Inst.getOperand(0)); + break; + case RISCV::C_JAL: + // c.jal $imm -> jal X1, $imm. + Res.setOpcode(RISCV::JAL); + Res.addOperand(MCOperand::createReg(RISCV::X1)); + Res.addOperand(Inst.getOperand(0)); + break; + } +} + +// Given a compressed control flow instruction this function returns +// the expanded instruction. +unsigned RISCVAsmBackend::getRelaxedOpcode(unsigned Op) const { + switch (Op) { + default: + return Op; + case RISCV::C_BEQZ: + return RISCV::BEQ; + case RISCV::C_BNEZ: + return RISCV::BNE; + case RISCV::C_J: + case RISCV::C_JAL: // fall through. + return RISCV::JAL; + } +} + +bool RISCVAsmBackend::mayNeedRelaxation(const MCInst &Inst, + const MCSubtargetInfo &STI) const { + return getRelaxedOpcode(Inst.getOpcode()) != Inst.getOpcode(); +} + +bool RISCVAsmBackend::writeNopData(raw_ostream &OS, uint64_t Count) const { + bool HasStdExtC = STI.getFeatureBits()[RISCV::FeatureStdExtC]; + unsigned MinNopLen = HasStdExtC ? 2 : 4; + + if ((Count % MinNopLen) != 0) return false; - // The canonical nop on RISC-V is addi x0, x0, 0 - for (uint64_t i = 0; i < Count; i += 4) - OW->write32(0x13); + // The canonical nop on RISC-V is addi x0, x0, 0. + uint64_t Nop32Count = Count / 4; + for (uint64_t i = Nop32Count; i != 0; --i) + OS.write("\x13\0\0\0", 4); + + // The canonical nop on RVC is c.nop. + if (HasStdExtC) { + uint64_t Nop16Count = (Count - Nop32Count * 4) / 2; + for (uint64_t i = Nop16Count; i != 0; --i) + OS.write("\x01\0", 2); + } return true; } @@ -112,8 +235,10 @@ static uint64_t adjustFixupValue(const MCFixup &Fixup, uint64_t Value, case FK_Data_8: return Value; case RISCV::fixup_riscv_lo12_i: + case RISCV::fixup_riscv_pcrel_lo12_i: return Value & 0xfff; case RISCV::fixup_riscv_lo12_s: + case RISCV::fixup_riscv_pcrel_lo12_s: return (((Value >> 5) & 0x7f) << 25) | ((Value & 0x1f) << 7); case RISCV::fixup_riscv_hi20: case RISCV::fixup_riscv_pcrel_hi20: @@ -154,6 +279,14 @@ static uint64_t adjustFixupValue(const MCFixup &Fixup, uint64_t Value, Value = (Sbit << 31) | (Mid6 << 25) | (Lo4 << 8) | (Hi1 << 7); return Value; } + case RISCV::fixup_riscv_call: { + // Jalr will add UpperImm with the sign-extended 12-bit LowerImm, + // we need to add 0x800ULL before extract upper bits to reflect the + // effect of the sign extension. + uint64_t UpperImm = (Value + 0x800ULL) & 0xfffff000ULL; + uint64_t LowerImm = Value & 0xfffULL; + return UpperImm | ((LowerImm << 20) << 32); + } 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; @@ -183,20 +316,11 @@ static uint64_t adjustFixupValue(const MCFixup &Fixup, uint64_t 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 { + bool IsResolved, + const MCSubtargetInfo *STI) const { MCContext &Ctx = Asm.getContext(); MCFixupKindInfo Info = getFixupKindInfo(Fixup.getKind()); if (!Value) @@ -208,31 +332,29 @@ void RISCVAsmBackend::applyFixup(const MCAssembler &Asm, const MCFixup &Fixup, Value <<= Info.TargetOffset; unsigned Offset = Fixup.getOffset(); - unsigned FullSize = getSize(Fixup.getKind()); + unsigned NumBytes = alignTo(Info.TargetSize + Info.TargetOffset, 8) / 8; -#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) { + for (unsigned i = 0; i != NumBytes; ++i) { Data[Offset + i] |= uint8_t((Value >> (i * 8)) & 0xff); } } -std::unique_ptr<MCObjectWriter> -RISCVAsmBackend::createObjectWriter(raw_pwrite_stream &OS) const { - return createRISCVELFObjectWriter(OS, OSABI, Is64Bit); +std::unique_ptr<MCObjectTargetWriter> +RISCVAsmBackend::createObjectTargetWriter() const { + return createRISCVELFObjectWriter(OSABI, Is64Bit); } } // end anonymous namespace MCAsmBackend *llvm::createRISCVAsmBackend(const Target &T, + const MCSubtargetInfo &STI, const MCRegisterInfo &MRI, - const Triple &TT, StringRef CPU, const MCTargetOptions &Options) { + const Triple &TT = STI.getTargetTriple(); uint8_t OSABI = MCELFObjectTargetWriter::getOSABI(TT.getOS()); - return new RISCVAsmBackend(OSABI, TT.isArch64Bit()); + return new RISCVAsmBackend(STI, OSABI, TT.isArch64Bit()); } diff --git a/lib/Target/RISCV/MCTargetDesc/RISCVELFObjectWriter.cpp b/lib/Target/RISCV/MCTargetDesc/RISCVELFObjectWriter.cpp index ad53228c104a..9b88614aa693 100644 --- a/lib/Target/RISCV/MCTargetDesc/RISCVELFObjectWriter.cpp +++ b/lib/Target/RISCV/MCTargetDesc/RISCVELFObjectWriter.cpp @@ -23,6 +23,15 @@ public: ~RISCVELFObjectWriter() override; + // Return true if the given relocation must be with a symbol rather than + // section plus offset. + bool needsRelocateWithSymbol(const MCSymbol &Sym, + unsigned Type) const override { + // TODO: this is very conservative, update once RISC-V psABI requirements + // are clarified. + return true; + } + protected: unsigned getRelocType(MCContext &Ctx, const MCValue &Target, const MCFixup &Fixup, bool IsPCRel) const override; @@ -47,6 +56,22 @@ unsigned RISCVELFObjectWriter::getRelocType(MCContext &Ctx, return ELF::R_RISCV_32; case FK_Data_8: return ELF::R_RISCV_64; + case FK_Data_Add_1: + return ELF::R_RISCV_ADD8; + case FK_Data_Add_2: + return ELF::R_RISCV_ADD16; + case FK_Data_Add_4: + return ELF::R_RISCV_ADD32; + case FK_Data_Add_8: + return ELF::R_RISCV_ADD64; + case FK_Data_Sub_1: + return ELF::R_RISCV_SUB8; + case FK_Data_Sub_2: + return ELF::R_RISCV_SUB16; + case FK_Data_Sub_4: + return ELF::R_RISCV_SUB32; + case FK_Data_Sub_8: + return ELF::R_RISCV_SUB64; case RISCV::fixup_riscv_hi20: return ELF::R_RISCV_HI20; case RISCV::fixup_riscv_lo12_i: @@ -55,6 +80,10 @@ unsigned RISCVELFObjectWriter::getRelocType(MCContext &Ctx, return ELF::R_RISCV_LO12_S; case RISCV::fixup_riscv_pcrel_hi20: return ELF::R_RISCV_PCREL_HI20; + case RISCV::fixup_riscv_pcrel_lo12_i: + return ELF::R_RISCV_PCREL_LO12_I; + case RISCV::fixup_riscv_pcrel_lo12_s: + return ELF::R_RISCV_PCREL_LO12_S; case RISCV::fixup_riscv_jal: return ELF::R_RISCV_JAL; case RISCV::fixup_riscv_branch: @@ -63,13 +92,14 @@ unsigned RISCVELFObjectWriter::getRelocType(MCContext &Ctx, return ELF::R_RISCV_RVC_JUMP; case RISCV::fixup_riscv_rvc_branch: return ELF::R_RISCV_RVC_BRANCH; + case RISCV::fixup_riscv_call: + return ELF::R_RISCV_CALL; + case RISCV::fixup_riscv_relax: + return ELF::R_RISCV_RELAX; } } -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); +std::unique_ptr<MCObjectTargetWriter> +llvm::createRISCVELFObjectWriter(uint8_t OSABI, bool Is64Bit) { + return llvm::make_unique<RISCVELFObjectWriter>(OSABI, Is64Bit); } diff --git a/lib/Target/RISCV/MCTargetDesc/RISCVELFStreamer.cpp b/lib/Target/RISCV/MCTargetDesc/RISCVELFStreamer.cpp new file mode 100644 index 000000000000..6428b11cfe9c --- /dev/null +++ b/lib/Target/RISCV/MCTargetDesc/RISCVELFStreamer.cpp @@ -0,0 +1,42 @@ +//===-- RISCVELFStreamer.cpp - RISCV ELF Target Streamer Methods ----------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file provides RISCV specific target streamer methods. +// +//===----------------------------------------------------------------------===// + +#include "RISCVELFStreamer.h" +#include "RISCVMCTargetDesc.h" +#include "llvm/BinaryFormat/ELF.h" +#include "llvm/MC/MCSubtargetInfo.h" + +using namespace llvm; + +// This part is for ELF object output. +RISCVTargetELFStreamer::RISCVTargetELFStreamer(MCStreamer &S, + const MCSubtargetInfo &STI) + : RISCVTargetStreamer(S) { + MCAssembler &MCA = getStreamer().getAssembler(); + + const FeatureBitset &Features = STI.getFeatureBits(); + + unsigned EFlags = MCA.getELFHeaderEFlags(); + + if (Features[RISCV::FeatureStdExtC]) + EFlags |= ELF::EF_RISCV_RVC; + + MCA.setELFHeaderEFlags(EFlags); +} + +MCELFStreamer &RISCVTargetELFStreamer::getStreamer() { + return static_cast<MCELFStreamer &>(Streamer); +} + +void RISCVTargetELFStreamer::emitDirectiveOptionRVC() {} +void RISCVTargetELFStreamer::emitDirectiveOptionNoRVC() {} diff --git a/lib/Target/RISCV/MCTargetDesc/RISCVELFStreamer.h b/lib/Target/RISCV/MCTargetDesc/RISCVELFStreamer.h new file mode 100644 index 000000000000..daa7abfe1336 --- /dev/null +++ b/lib/Target/RISCV/MCTargetDesc/RISCVELFStreamer.h @@ -0,0 +1,27 @@ +//===-- RISCVELFStreamer.h - RISCV ELF Target Streamer ---------*- 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_RISCVELFSTREAMER_H +#define LLVM_LIB_TARGET_RISCV_RISCVELFSTREAMER_H + +#include "RISCVTargetStreamer.h" +#include "llvm/MC/MCELFStreamer.h" + +namespace llvm { + +class RISCVTargetELFStreamer : public RISCVTargetStreamer { +public: + MCELFStreamer &getStreamer(); + RISCVTargetELFStreamer(MCStreamer &S, const MCSubtargetInfo &STI); + + virtual void emitDirectiveOptionRVC(); + virtual void emitDirectiveOptionNoRVC(); +}; +} +#endif diff --git a/lib/Target/RISCV/MCTargetDesc/RISCVFixupKinds.h b/lib/Target/RISCV/MCTargetDesc/RISCVFixupKinds.h index cfb5d99e79f5..6a1224be774e 100644 --- a/lib/Target/RISCV/MCTargetDesc/RISCVFixupKinds.h +++ b/lib/Target/RISCV/MCTargetDesc/RISCVFixupKinds.h @@ -29,6 +29,12 @@ enum Fixups { // fixup_riscv_pcrel_hi20 - 20-bit fixup corresponding to pcrel_hi(foo) for // instructions like auipc fixup_riscv_pcrel_hi20, + // fixup_riscv_pcrel_lo12_i - 12-bit fixup corresponding to pcrel_lo(foo) for + // instructions like addi + fixup_riscv_pcrel_lo12_i, + // fixup_riscv_pcrel_lo12_s - 12-bit fixup corresponding to pcrel_lo(foo) for + // the S-type store instructions + fixup_riscv_pcrel_lo12_s, // fixup_riscv_jal - 20-bit fixup for symbol references in the jal // instruction fixup_riscv_jal, @@ -41,6 +47,12 @@ enum Fixups { // fixup_riscv_rvc_branch - 8-bit fixup for symbol references in the // compressed branch instruction fixup_riscv_rvc_branch, + // fixup_riscv_call - A fixup representing a call attached to the auipc + // instruction in a pair composed of adjacent auipc+jalr instructions. + fixup_riscv_call, + // fixup_riscv_relax - Used to generate an R_RISCV_RELAX relocation type, + // which indicates the linker may relax the instruction pair. + fixup_riscv_relax, // fixup_riscv_invalid - used as a sentinel and a marker, must be last fixup fixup_riscv_invalid, diff --git a/lib/Target/RISCV/MCTargetDesc/RISCVMCAsmInfo.cpp b/lib/Target/RISCV/MCTargetDesc/RISCVMCAsmInfo.cpp index d622911e92c4..780dae410cd0 100644 --- a/lib/Target/RISCV/MCTargetDesc/RISCVMCAsmInfo.cpp +++ b/lib/Target/RISCV/MCTargetDesc/RISCVMCAsmInfo.cpp @@ -22,4 +22,6 @@ RISCVMCAsmInfo::RISCVMCAsmInfo(const Triple &TT) { CommentString = "#"; AlignmentIsInBytes = false; SupportsDebugInformation = true; + Data16bitsDirective = "\t.half\t"; + Data32bitsDirective = "\t.word\t"; } diff --git a/lib/Target/RISCV/MCTargetDesc/RISCVMCCodeEmitter.cpp b/lib/Target/RISCV/MCTargetDesc/RISCVMCCodeEmitter.cpp index 641997e67e06..8a796a014b33 100644 --- a/lib/Target/RISCV/MCTargetDesc/RISCVMCCodeEmitter.cpp +++ b/lib/Target/RISCV/MCTargetDesc/RISCVMCCodeEmitter.cpp @@ -21,6 +21,7 @@ #include "llvm/MC/MCContext.h" #include "llvm/MC/MCExpr.h" #include "llvm/MC/MCInst.h" +#include "llvm/MC/MCInstBuilder.h" #include "llvm/MC/MCInstrInfo.h" #include "llvm/MC/MCRegisterInfo.h" #include "llvm/MC/MCSymbol.h" @@ -52,6 +53,10 @@ public: SmallVectorImpl<MCFixup> &Fixups, const MCSubtargetInfo &STI) const override; + void expandFunctionCall(const MCInst &MI, raw_ostream &OS, + SmallVectorImpl<MCFixup> &Fixups, + const MCSubtargetInfo &STI) const; + /// TableGen'erated function for getting the binary encoding for an /// instruction. uint64_t getBinaryCodeForInstr(const MCInst &MI, @@ -80,6 +85,46 @@ MCCodeEmitter *llvm::createRISCVMCCodeEmitter(const MCInstrInfo &MCII, return new RISCVMCCodeEmitter(Ctx, MCII); } +// Expand PseudoCALL and PseudoTAIL to AUIPC and JALR with relocation types. +// We expand PseudoCALL and PseudoTAIL while encoding, meaning AUIPC and JALR +// won't go through RISCV MC to MC compressed instruction transformation. This +// is acceptable because AUIPC has no 16-bit form and C_JALR have no immediate +// operand field. We let linker relaxation deal with it. When linker +// relaxation enabled, AUIPC and JALR have chance relax to JAL. If C extension +// is enabled, JAL has chance relax to C_JAL. +void RISCVMCCodeEmitter::expandFunctionCall(const MCInst &MI, raw_ostream &OS, + SmallVectorImpl<MCFixup> &Fixups, + const MCSubtargetInfo &STI) const { + MCInst TmpInst; + MCOperand Func = MI.getOperand(0); + unsigned Ra = (MI.getOpcode() == RISCV::PseudoTAIL) ? RISCV::X6 : RISCV::X1; + uint32_t Binary; + + assert(Func.isExpr() && "Expected expression"); + + const MCExpr *Expr = Func.getExpr(); + + // Create function call expression CallExpr for AUIPC. + const MCExpr *CallExpr = + RISCVMCExpr::create(Expr, RISCVMCExpr::VK_RISCV_CALL, Ctx); + + // Emit AUIPC Ra, Func with R_RISCV_CALL relocation type. + TmpInst = MCInstBuilder(RISCV::AUIPC) + .addReg(Ra) + .addOperand(MCOperand::createExpr(CallExpr)); + Binary = getBinaryCodeForInstr(TmpInst, Fixups, STI); + support::endian::write(OS, Binary, support::little); + + if (MI.getOpcode() == RISCV::PseudoTAIL) + // Emit JALR X0, X6, 0 + TmpInst = MCInstBuilder(RISCV::JALR).addReg(RISCV::X0).addReg(Ra).addImm(0); + else + // Emit JALR X1, X1, 0 + TmpInst = MCInstBuilder(RISCV::JALR).addReg(Ra).addReg(Ra).addImm(0); + Binary = getBinaryCodeForInstr(TmpInst, Fixups, STI); + support::endian::write(OS, Binary, support::little); +} + void RISCVMCCodeEmitter::encodeInstruction(const MCInst &MI, raw_ostream &OS, SmallVectorImpl<MCFixup> &Fixups, const MCSubtargetInfo &STI) const { @@ -87,17 +132,24 @@ void RISCVMCCodeEmitter::encodeInstruction(const MCInst &MI, raw_ostream &OS, // Get byte count of instruction. unsigned Size = Desc.getSize(); + if (MI.getOpcode() == RISCV::PseudoCALL || + MI.getOpcode() == RISCV::PseudoTAIL) { + expandFunctionCall(MI, OS, Fixups, STI); + MCNumEmitted += 2; + return; + } + 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); + support::endian::write<uint16_t>(OS, Bits, support::little); break; } case 4: { uint32_t Bits = getBinaryCodeForInstr(MI, Fixups, STI); - support::endian::Writer<support::little>(OS).write(Bits); + support::endian::write(OS, Bits, support::little); break; } } @@ -138,7 +190,7 @@ RISCVMCCodeEmitter::getImmOpValueAsr1(const MCInst &MI, unsigned OpNo, unsigned RISCVMCCodeEmitter::getImmOpValue(const MCInst &MI, unsigned OpNo, SmallVectorImpl<MCFixup> &Fixups, const MCSubtargetInfo &STI) const { - + bool EnableRelax = STI.getFeatureBits()[RISCV::FeatureRelax]; const MCOperand &MO = MI.getOperand(OpNo); MCInstrDesc const &Desc = MCII.get(MI.getOpcode()); @@ -161,15 +213,31 @@ unsigned RISCVMCCodeEmitter::getImmOpValue(const MCInst &MI, unsigned OpNo, 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; + if (MIFrm == RISCVII::InstFormatI) + FixupKind = RISCV::fixup_riscv_lo12_i; + else if (MIFrm == RISCVII::InstFormatS) + FixupKind = RISCV::fixup_riscv_lo12_s; + else + llvm_unreachable("VK_RISCV_LO used with unexpected instruction format"); break; case RISCVMCExpr::VK_RISCV_HI: FixupKind = RISCV::fixup_riscv_hi20; break; + case RISCVMCExpr::VK_RISCV_PCREL_LO: + if (MIFrm == RISCVII::InstFormatI) + FixupKind = RISCV::fixup_riscv_pcrel_lo12_i; + else if (MIFrm == RISCVII::InstFormatS) + FixupKind = RISCV::fixup_riscv_pcrel_lo12_s; + else + llvm_unreachable( + "VK_RISCV_PCREL_LO used with unexpected instruction format"); + break; case RISCVMCExpr::VK_RISCV_PCREL_HI: FixupKind = RISCV::fixup_riscv_pcrel_hi20; break; + case RISCVMCExpr::VK_RISCV_CALL: + FixupKind = RISCV::fixup_riscv_call; + break; } } else if (Kind == MCExpr::SymbolRef && cast<MCSymbolRefExpr>(Expr)->getKind() == MCSymbolRefExpr::VK_None) { @@ -190,6 +258,15 @@ unsigned RISCVMCCodeEmitter::getImmOpValue(const MCInst &MI, unsigned OpNo, MCFixup::create(0, Expr, MCFixupKind(FixupKind), MI.getLoc())); ++MCNumFixups; + if (EnableRelax) { + if (FixupKind == RISCV::fixup_riscv_call) { + Fixups.push_back( + MCFixup::create(0, Expr, MCFixupKind(RISCV::fixup_riscv_relax), + MI.getLoc())); + ++MCNumFixups; + } + } + return 0; } diff --git a/lib/Target/RISCV/MCTargetDesc/RISCVMCExpr.cpp b/lib/Target/RISCV/MCTargetDesc/RISCVMCExpr.cpp index b36236ea155f..085dcd4e5f66 100644 --- a/lib/Target/RISCV/MCTargetDesc/RISCVMCExpr.cpp +++ b/lib/Target/RISCV/MCTargetDesc/RISCVMCExpr.cpp @@ -12,6 +12,7 @@ // //===----------------------------------------------------------------------===// +#include "RISCV.h" #include "RISCVMCExpr.h" #include "llvm/MC/MCAssembler.h" #include "llvm/MC/MCContext.h" @@ -31,7 +32,8 @@ const RISCVMCExpr *RISCVMCExpr::create(const MCExpr *Expr, VariantKind Kind, } void RISCVMCExpr::printImpl(raw_ostream &OS, const MCAsmInfo *MAI) const { - bool HasVariant = getKind() != VK_RISCV_None; + bool HasVariant = + ((getKind() != VK_RISCV_None) && (getKind() != VK_RISCV_CALL)); if (HasVariant) OS << '%' << getVariantKindName(getKind()) << '('; Expr->print(OS, MAI); @@ -42,7 +44,23 @@ void RISCVMCExpr::printImpl(raw_ostream &OS, const MCAsmInfo *MAI) const { bool RISCVMCExpr::evaluateAsRelocatableImpl(MCValue &Res, const MCAsmLayout *Layout, const MCFixup *Fixup) const { - return getSubExpr()->evaluateAsRelocatable(Res, Layout, Fixup); + if (!getSubExpr()->evaluateAsRelocatable(Res, Layout, Fixup)) + return false; + + // Some custom fixup types are not valid with symbol difference expressions + if (Res.getSymA() && Res.getSymB()) { + switch (getKind()) { + default: + return true; + case VK_RISCV_LO: + case VK_RISCV_HI: + case VK_RISCV_PCREL_LO: + case VK_RISCV_PCREL_HI: + return false; + } + } + + return true; } void RISCVMCExpr::visitUsedExpr(MCStreamer &Streamer) const { @@ -53,6 +71,7 @@ RISCVMCExpr::VariantKind RISCVMCExpr::getVariantKindForName(StringRef name) { return StringSwitch<RISCVMCExpr::VariantKind>(name) .Case("lo", VK_RISCV_LO) .Case("hi", VK_RISCV_HI) + .Case("pcrel_lo", VK_RISCV_PCREL_LO) .Case("pcrel_hi", VK_RISCV_PCREL_HI) .Default(VK_RISCV_Invalid); } @@ -65,6 +84,8 @@ StringRef RISCVMCExpr::getVariantKindName(VariantKind Kind) { return "lo"; case VK_RISCV_HI: return "hi"; + case VK_RISCV_PCREL_LO: + return "pcrel_lo"; case VK_RISCV_PCREL_HI: return "pcrel_hi"; } @@ -73,7 +94,8 @@ StringRef RISCVMCExpr::getVariantKindName(VariantKind Kind) { bool RISCVMCExpr::evaluateAsConstant(int64_t &Res) const { MCValue Value; - if (Kind == VK_RISCV_PCREL_HI) + if (Kind == VK_RISCV_PCREL_HI || Kind == VK_RISCV_PCREL_LO || + Kind == VK_RISCV_CALL) return false; if (!getSubExpr()->evaluateAsRelocatable(Value, nullptr, nullptr)) diff --git a/lib/Target/RISCV/MCTargetDesc/RISCVMCExpr.h b/lib/Target/RISCV/MCTargetDesc/RISCVMCExpr.h index 69b55ca6f7cd..d2e0f6b6cdae 100644 --- a/lib/Target/RISCV/MCTargetDesc/RISCVMCExpr.h +++ b/lib/Target/RISCV/MCTargetDesc/RISCVMCExpr.h @@ -20,13 +20,16 @@ namespace llvm { class StringRef; +class MCOperand; class RISCVMCExpr : public MCTargetExpr { public: enum VariantKind { VK_RISCV_None, VK_RISCV_LO, VK_RISCV_HI, + VK_RISCV_PCREL_LO, VK_RISCV_PCREL_HI, + VK_RISCV_CALL, VK_RISCV_Invalid }; diff --git a/lib/Target/RISCV/MCTargetDesc/RISCVMCTargetDesc.cpp b/lib/Target/RISCV/MCTargetDesc/RISCVMCTargetDesc.cpp index 45de976ec6c2..133f3cd3d39a 100644 --- a/lib/Target/RISCV/MCTargetDesc/RISCVMCTargetDesc.cpp +++ b/lib/Target/RISCV/MCTargetDesc/RISCVMCTargetDesc.cpp @@ -13,7 +13,9 @@ #include "RISCVMCTargetDesc.h" #include "InstPrinter/RISCVInstPrinter.h" +#include "RISCVELFStreamer.h" #include "RISCVMCAsmInfo.h" +#include "RISCVTargetStreamer.h" #include "llvm/ADT/STLExtras.h" #include "llvm/MC/MCAsmInfo.h" #include "llvm/MC/MCInstrInfo.h" @@ -67,6 +69,21 @@ static MCInstPrinter *createRISCVMCInstPrinter(const Triple &T, return new RISCVInstPrinter(MAI, MII, MRI); } +static MCTargetStreamer * +createRISCVObjectTargetStreamer(MCStreamer &S, const MCSubtargetInfo &STI) { + const Triple &TT = STI.getTargetTriple(); + if (TT.isOSBinFormatELF()) + return new RISCVTargetELFStreamer(S, STI); + return nullptr; +} + +static MCTargetStreamer *createRISCVAsmTargetStreamer(MCStreamer &S, + formatted_raw_ostream &OS, + MCInstPrinter *InstPrint, + bool isVerboseAsm) { + return new RISCVTargetAsmStreamer(S, OS); +} + extern "C" void LLVMInitializeRISCVTargetMC() { for (Target *T : {&getTheRISCV32Target(), &getTheRISCV64Target()}) { TargetRegistry::RegisterMCAsmInfo(*T, createRISCVMCAsmInfo); @@ -76,5 +93,10 @@ extern "C" void LLVMInitializeRISCVTargetMC() { TargetRegistry::RegisterMCCodeEmitter(*T, createRISCVMCCodeEmitter); TargetRegistry::RegisterMCInstPrinter(*T, createRISCVMCInstPrinter); TargetRegistry::RegisterMCSubtargetInfo(*T, createRISCVMCSubtargetInfo); + TargetRegistry::RegisterObjectTargetStreamer( + *T, createRISCVObjectTargetStreamer); + + // Register the asm target streamer. + TargetRegistry::RegisterAsmTargetStreamer(*T, createRISCVAsmTargetStreamer); } } diff --git a/lib/Target/RISCV/MCTargetDesc/RISCVMCTargetDesc.h b/lib/Target/RISCV/MCTargetDesc/RISCVMCTargetDesc.h index bea2f8800fa6..0228253c08cb 100644 --- a/lib/Target/RISCV/MCTargetDesc/RISCVMCTargetDesc.h +++ b/lib/Target/RISCV/MCTargetDesc/RISCVMCTargetDesc.h @@ -24,7 +24,7 @@ class MCAsmBackend; class MCCodeEmitter; class MCContext; class MCInstrInfo; -class MCObjectWriter; +class MCObjectTargetWriter; class MCRegisterInfo; class MCSubtargetInfo; class StringRef; @@ -40,12 +40,12 @@ MCCodeEmitter *createRISCVMCCodeEmitter(const MCInstrInfo &MCII, const MCRegisterInfo &MRI, MCContext &Ctx); -MCAsmBackend *createRISCVAsmBackend(const Target &T, const MCRegisterInfo &MRI, - const Triple &TT, StringRef CPU, +MCAsmBackend *createRISCVAsmBackend(const Target &T, const MCSubtargetInfo &STI, + const MCRegisterInfo &MRI, const MCTargetOptions &Options); -std::unique_ptr<MCObjectWriter> -createRISCVELFObjectWriter(raw_pwrite_stream &OS, uint8_t OSABI, bool Is64Bit); +std::unique_ptr<MCObjectTargetWriter> createRISCVELFObjectWriter(uint8_t OSABI, + bool Is64Bit); } // Defines symbolic names for RISC-V registers. diff --git a/lib/Target/RISCV/MCTargetDesc/RISCVTargetStreamer.cpp b/lib/Target/RISCV/MCTargetDesc/RISCVTargetStreamer.cpp new file mode 100644 index 000000000000..2d5205aa7ef7 --- /dev/null +++ b/lib/Target/RISCV/MCTargetDesc/RISCVTargetStreamer.cpp @@ -0,0 +1,32 @@ +//===-- RISCVTargetStreamer.cpp - RISCV Target Streamer Methods -----------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file provides RISCV specific target streamer methods. +// +//===----------------------------------------------------------------------===// + +#include "RISCVTargetStreamer.h" +#include "llvm/Support/FormattedStream.h" + +using namespace llvm; + +RISCVTargetStreamer::RISCVTargetStreamer(MCStreamer &S) : MCTargetStreamer(S) {} + +// This part is for ascii assembly output +RISCVTargetAsmStreamer::RISCVTargetAsmStreamer(MCStreamer &S, + formatted_raw_ostream &OS) + : RISCVTargetStreamer(S), OS(OS) {} + +void RISCVTargetAsmStreamer::emitDirectiveOptionRVC() { + OS << "\t.option\trvc\n"; +} + +void RISCVTargetAsmStreamer::emitDirectiveOptionNoRVC() { + OS << "\t.option\tnorvc\n"; +} diff --git a/lib/Target/RISCV/MCTargetDesc/RISCVTargetStreamer.h b/lib/Target/RISCV/MCTargetDesc/RISCVTargetStreamer.h new file mode 100644 index 000000000000..525c20810f24 --- /dev/null +++ b/lib/Target/RISCV/MCTargetDesc/RISCVTargetStreamer.h @@ -0,0 +1,37 @@ +//===-- RISCVTargetStreamer.h - RISCV Target Streamer ----------*- 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_RISCVTARGETSTREAMER_H +#define LLVM_LIB_TARGET_RISCV_RISCVTARGETSTREAMER_H + +#include "llvm/MC/MCStreamer.h" + +namespace llvm { + +class RISCVTargetStreamer : public MCTargetStreamer { +public: + RISCVTargetStreamer(MCStreamer &S); + + virtual void emitDirectiveOptionRVC() = 0; + virtual void emitDirectiveOptionNoRVC() = 0; +}; + +// This part is for ascii assembly output +class RISCVTargetAsmStreamer : public RISCVTargetStreamer { + formatted_raw_ostream &OS; + +public: + RISCVTargetAsmStreamer(MCStreamer &S, formatted_raw_ostream &OS); + + void emitDirectiveOptionRVC() override; + void emitDirectiveOptionNoRVC() override; +}; + +} +#endif diff --git a/lib/Target/RISCV/RISCV.h b/lib/Target/RISCV/RISCV.h index 884cb2e5014d..2e4f536aca35 100644 --- a/lib/Target/RISCV/RISCV.h +++ b/lib/Target/RISCV/RISCV.h @@ -25,6 +25,7 @@ class MCInst; class MCOperand; class MachineInstr; class MachineOperand; +class PassRegistry; void LowerRISCVMachineInstrToMCInst(const MachineInstr *MI, MCInst &OutMI, const AsmPrinter &AP); @@ -32,6 +33,9 @@ bool LowerRISCVMachineOperandToMCOperand(const MachineOperand &MO, MCOperand &MCOp, const AsmPrinter &AP); FunctionPass *createRISCVISelDag(RISCVTargetMachine &TM); + +FunctionPass *createRISCVMergeBaseOffsetOptPass(); +void initializeRISCVMergeBaseOffsetOptPass(PassRegistry &); } #endif diff --git a/lib/Target/RISCV/RISCV.td b/lib/Target/RISCV/RISCV.td index c74d560b2e03..281378cb2eee 100644 --- a/lib/Target/RISCV/RISCV.td +++ b/lib/Target/RISCV/RISCV.td @@ -55,6 +55,10 @@ def IsRV32 : Predicate<"!Subtarget->is64Bit()">, def RV64 : HwMode<"+64bit">; def RV32 : HwMode<"-64bit">; +def FeatureRelax + : SubtargetFeature<"relax", "EnableLinkerRelax", "true", + "Enable Linker relaxation.">; + //===----------------------------------------------------------------------===// // Registers, calling conventions, instruction descriptions. //===----------------------------------------------------------------------===// @@ -84,7 +88,13 @@ def RISCVAsmParser : AsmParser { let AllowDuplicateRegisterNames = 1; } +def RISCVAsmWriter : AsmWriter { + int PassSubtarget = 1; +} + def RISCV : Target { let InstructionSet = RISCVInstrInfo; let AssemblyParsers = [RISCVAsmParser]; + let AssemblyWriters = [RISCVAsmWriter]; + let AllowRegisterRenaming = 1; } diff --git a/lib/Target/RISCV/RISCVAsmPrinter.cpp b/lib/Target/RISCV/RISCVAsmPrinter.cpp index 4808e6c73c50..bdf8e5d840b3 100644 --- a/lib/Target/RISCV/RISCVAsmPrinter.cpp +++ b/lib/Target/RISCV/RISCVAsmPrinter.cpp @@ -14,6 +14,7 @@ #include "RISCV.h" #include "InstPrinter/RISCVInstPrinter.h" +#include "MCTargetDesc/RISCVMCExpr.h" #include "RISCVTargetMachine.h" #include "llvm/CodeGen/AsmPrinter.h" #include "llvm/CodeGen/MachineConstantPool.h" @@ -41,6 +42,14 @@ public: void EmitInstruction(const MachineInstr *MI) override; + bool PrintAsmOperand(const MachineInstr *MI, unsigned OpNo, + unsigned AsmVariant, const char *ExtraCode, + raw_ostream &OS) override; + bool PrintAsmMemoryOperand(const MachineInstr *MI, unsigned OpNo, + unsigned AsmVariant, const char *ExtraCode, + raw_ostream &OS) override; + + void EmitToStreamer(MCStreamer &S, const MCInst &Inst); bool emitPseudoExpansionLowering(MCStreamer &OutStreamer, const MachineInstr *MI); @@ -51,6 +60,15 @@ public: }; } +#define GEN_COMPRESS_INSTR +#include "RISCVGenCompressInstEmitter.inc" +void RISCVAsmPrinter::EmitToStreamer(MCStreamer &S, const MCInst &Inst) { + MCInst CInst; + bool Res = compressInst(CInst, Inst, *TM.getMCSubtargetInfo(), + OutStreamer->getContext()); + AsmPrinter::EmitToStreamer(*OutStreamer, Res ? CInst : Inst); +} + // Simple pseudo-instructions have their lowering (with expansion to real // instructions) auto-generated. #include "RISCVGenMCPseudoLowering.inc" @@ -65,6 +83,54 @@ void RISCVAsmPrinter::EmitInstruction(const MachineInstr *MI) { EmitToStreamer(*OutStreamer, TmpInst); } +bool RISCVAsmPrinter::PrintAsmOperand(const MachineInstr *MI, unsigned OpNo, + unsigned AsmVariant, + const char *ExtraCode, raw_ostream &OS) { + if (AsmVariant != 0) + report_fatal_error("There are no defined alternate asm variants"); + + // First try the generic code, which knows about modifiers like 'c' and 'n'. + if (!AsmPrinter::PrintAsmOperand(MI, OpNo, AsmVariant, ExtraCode, OS)) + return false; + + if (!ExtraCode) { + const MachineOperand &MO = MI->getOperand(OpNo); + switch (MO.getType()) { + case MachineOperand::MO_Immediate: + OS << MO.getImm(); + return false; + case MachineOperand::MO_Register: + OS << RISCVInstPrinter::getRegisterName(MO.getReg()); + return false; + default: + break; + } + } + + return true; +} + +bool RISCVAsmPrinter::PrintAsmMemoryOperand(const MachineInstr *MI, + unsigned OpNo, unsigned AsmVariant, + const char *ExtraCode, + raw_ostream &OS) { + if (AsmVariant != 0) + report_fatal_error("There are no defined alternate asm variants"); + + if (!ExtraCode) { + const MachineOperand &MO = MI->getOperand(OpNo); + // For now, we only support register memory operands in registers and + // assume there is no addend + if (!MO.isReg()) + return true; + + OS << "0(" << RISCVInstPrinter::getRegisterName(MO.getReg()) << ")"; + return false; + } + + return AsmPrinter::PrintAsmMemoryOperand(MI, OpNo, AsmVariant, ExtraCode, OS); +} + // Force static initialization. extern "C" void LLVMInitializeRISCVAsmPrinter() { RegisterAsmPrinter<RISCVAsmPrinter> X(getTheRISCV32Target()); diff --git a/lib/Target/RISCV/RISCVCallingConv.td b/lib/Target/RISCV/RISCVCallingConv.td index d2b17c64c9c2..ef146258c383 100644 --- a/lib/Target/RISCV/RISCVCallingConv.td +++ b/lib/Target/RISCV/RISCVCallingConv.td @@ -18,3 +18,40 @@ 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)>; + +// Interrupt handler needs to save/restore all registers that are used, +// both Caller and Callee saved registers. +def CSR_Interrupt : CalleeSavedRegs<(add X1, + (sequence "X%u", 3, 9), + (sequence "X%u", 10, 11), + (sequence "X%u", 12, 17), + (sequence "X%u", 18, 27), + (sequence "X%u", 28, 31))>; + +// Same as CSR_Interrupt, but including all 32-bit FP registers. +def CSR_XLEN_F32_Interrupt: CalleeSavedRegs<(add X1, + (sequence "X%u", 3, 9), + (sequence "X%u", 10, 11), + (sequence "X%u", 12, 17), + (sequence "X%u", 18, 27), + (sequence "X%u", 28, 31), + (sequence "F%u_32", 0, 7), + (sequence "F%u_32", 10, 11), + (sequence "F%u_32", 12, 17), + (sequence "F%u_32", 28, 31), + (sequence "F%u_32", 8, 9), + (sequence "F%u_32", 18, 27))>; + +// Same as CSR_Interrupt, but including all 64-bit FP registers. +def CSR_XLEN_F64_Interrupt: CalleeSavedRegs<(add X1, + (sequence "X%u", 3, 9), + (sequence "X%u", 10, 11), + (sequence "X%u", 12, 17), + (sequence "X%u", 18, 27), + (sequence "X%u", 28, 31), + (sequence "F%u_64", 0, 7), + (sequence "F%u_64", 10, 11), + (sequence "F%u_64", 12, 17), + (sequence "F%u_64", 28, 31), + (sequence "F%u_64", 8, 9), + (sequence "F%u_64", 18, 27))>; diff --git a/lib/Target/RISCV/RISCVFrameLowering.cpp b/lib/Target/RISCV/RISCVFrameLowering.cpp index e9e003e63d59..a816028f9d8b 100644 --- a/lib/Target/RISCV/RISCVFrameLowering.cpp +++ b/lib/Target/RISCV/RISCVFrameLowering.cpp @@ -12,15 +12,24 @@ //===----------------------------------------------------------------------===// #include "RISCVFrameLowering.h" +#include "RISCVMachineFunctionInfo.h" #include "RISCVSubtarget.h" #include "llvm/CodeGen/MachineFrameInfo.h" #include "llvm/CodeGen/MachineFunction.h" #include "llvm/CodeGen/MachineInstrBuilder.h" #include "llvm/CodeGen/MachineRegisterInfo.h" +#include "llvm/CodeGen/RegisterScavenging.h" using namespace llvm; -bool RISCVFrameLowering::hasFP(const MachineFunction &MF) const { return true; } +bool RISCVFrameLowering::hasFP(const MachineFunction &MF) const { + const TargetRegisterInfo *RegInfo = MF.getSubtarget().getRegisterInfo(); + + const MachineFrameInfo &MFI = MF.getFrameInfo(); + return MF.getTarget().Options.DisableFramePointerElim(MF) || + RegInfo->needsStackRealignment(MF) || MFI.hasVarSizedObjects() || + MFI.isFrameAddressTaken(); +} // Determines the size of the frame and maximum call frame size. void RISCVFrameLowering::determineFrameLayout(MachineFunction &MF) const { @@ -34,21 +43,6 @@ void RISCVFrameLowering::determineFrameLayout(MachineFunction &MF) const { 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); @@ -61,18 +55,34 @@ void RISCVFrameLowering::adjustReg(MachineBasicBlock &MBB, const DebugLoc &DL, unsigned DestReg, unsigned SrcReg, int64_t Val, MachineInstr::MIFlag Flag) const { + MachineRegisterInfo &MRI = MBB.getParent()->getRegInfo(); 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); + if (isInt<12>(Val)) { + BuildMI(MBB, MBBI, DL, TII->get(RISCV::ADDI), DestReg) + .addReg(SrcReg) + .addImm(Val) + .setMIFlag(Flag); + } else if (isInt<32>(Val)) { + unsigned Opc = RISCV::ADD; + bool isSub = Val < 0; + if (isSub) { + Val = -Val; + Opc = RISCV::SUB; + } + + unsigned ScratchReg = MRI.createVirtualRegister(&RISCV::GPRRegClass); + TII->movImm32(MBB, MBBI, DL, ScratchReg, Val, Flag); + BuildMI(MBB, MBBI, DL, TII->get(Opc), DestReg) + .addReg(SrcReg) + .addReg(ScratchReg, RegState::Kill) + .setMIFlag(Flag); + } else { + report_fatal_error("adjustReg cannot yet handle adjustments >32 bits"); + } } // Returns the register used to hold the frame pointer. @@ -85,12 +95,8 @@ 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(); + auto *RVFI = MF.getInfo<RISCVMachineFunctionInfo>(); MachineBasicBlock::iterator MBBI = MBB.begin(); unsigned FPReg = getFPReg(STI); @@ -124,19 +130,17 @@ void RISCVFrameLowering::emitPrologue(MachineFunction &MF, std::advance(MBBI, CSI.size()); // Generate new FP. - adjustReg(MBB, MBBI, DL, FPReg, SPReg, StackSize, MachineInstr::FrameSetup); + if (hasFP(MF)) + adjustReg(MBB, MBBI, DL, FPReg, SPReg, + StackSize - RVFI->getVarArgsSaveSize(), 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(); + auto *RVFI = MF.getInfo<RISCVMachineFunctionInfo>(); DebugLoc DL = MBBI->getDebugLoc(); unsigned FPReg = getFPReg(STI); unsigned SPReg = getSPReg(STI); @@ -153,7 +157,9 @@ void RISCVFrameLowering::emitEpilogue(MachineFunction &MF, // 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, + assert(hasFP(MF) && "frame pointer should not have been eliminated"); + adjustReg(MBB, LastFrameDestroy, DL, SPReg, FPReg, + -StackSize + RVFI->getVarArgsSaveSize(), MachineInstr::FrameDestroy); } @@ -166,6 +172,7 @@ int RISCVFrameLowering::getFrameIndexReference(const MachineFunction &MF, unsigned &FrameReg) const { const MachineFrameInfo &MFI = MF.getFrameInfo(); const TargetRegisterInfo *RI = MF.getSubtarget().getRegisterInfo(); + const auto *RVFI = MF.getInfo<RISCVMachineFunctionInfo>(); // Callee-saved registers should be referenced relative to the stack // pointer (positive offset), otherwise use the frame pointer (negative @@ -182,10 +189,15 @@ int RISCVFrameLowering::getFrameIndexReference(const MachineFunction &MF, MaxCSFI = CSI[CSI.size() - 1].getFrameIdx(); } - FrameReg = RI->getFrameRegister(MF); if (FI >= MinCSFI && FI <= MaxCSFI) { FrameReg = RISCV::X2; Offset += MF.getFrameInfo().getStackSize(); + } else { + FrameReg = RI->getFrameRegister(MF); + if (hasFP(MF)) + Offset += RVFI->getVarArgsSaveSize(); + else + Offset += MF.getFrameInfo().getStackSize(); } return Offset; } @@ -194,8 +206,94 @@ 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); + // Unconditionally spill RA and FP only if the function uses a frame + // pointer. + if (hasFP(MF)) { + SavedRegs.set(RISCV::X1); + SavedRegs.set(RISCV::X8); + } + + // If interrupt is enabled and there are calls in the handler, + // unconditionally save all Caller-saved registers and + // all FP registers, regardless whether they are used. + MachineFrameInfo &MFI = MF.getFrameInfo(); + + if (MF.getFunction().hasFnAttribute("interrupt") && MFI.hasCalls()) { + + static const MCPhysReg CSRegs[] = { RISCV::X1, /* ra */ + RISCV::X5, RISCV::X6, RISCV::X7, /* t0-t2 */ + RISCV::X10, RISCV::X11, /* a0-a1, a2-a7 */ + RISCV::X12, RISCV::X13, RISCV::X14, RISCV::X15, RISCV::X16, RISCV::X17, + RISCV::X28, RISCV::X29, RISCV::X30, RISCV::X31, 0 /* t3-t6 */ + }; + + for (unsigned i = 0; CSRegs[i]; ++i) + SavedRegs.set(CSRegs[i]); + + if (MF.getSubtarget<RISCVSubtarget>().hasStdExtD() || + MF.getSubtarget<RISCVSubtarget>().hasStdExtF()) { + + // If interrupt is enabled, this list contains all FP registers. + const MCPhysReg * Regs = MF.getRegInfo().getCalleeSavedRegs(); + + for (unsigned i = 0; Regs[i]; ++i) + if (RISCV::FPR32RegClass.contains(Regs[i]) || + RISCV::FPR64RegClass.contains(Regs[i])) + SavedRegs.set(Regs[i]); + } + } +} + +void RISCVFrameLowering::processFunctionBeforeFrameFinalized( + MachineFunction &MF, RegScavenger *RS) const { + const TargetRegisterInfo *RegInfo = MF.getSubtarget().getRegisterInfo(); + MachineFrameInfo &MFI = MF.getFrameInfo(); + const TargetRegisterClass *RC = &RISCV::GPRRegClass; + // estimateStackSize has been observed to under-estimate the final stack + // size, so give ourselves wiggle-room by checking for stack size + // representable an 11-bit signed field rather than 12-bits. + // FIXME: It may be possible to craft a function with a small stack that + // still needs an emergency spill slot for branch relaxation. This case + // would currently be missed. + if (!isInt<11>(MFI.estimateStackSize(MF))) { + int RegScavFI = MFI.CreateStackObject( + RegInfo->getSpillSize(*RC), RegInfo->getSpillAlignment(*RC), false); + RS->addScavengingFrameIndex(RegScavFI); + } +} + +// Not preserve stack space within prologue for outgoing variables when the +// function contains variable size objects and let eliminateCallFramePseudoInstr +// preserve stack space for it. +bool RISCVFrameLowering::hasReservedCallFrame(const MachineFunction &MF) const { + return !MF.getFrameInfo().hasVarSizedObjects(); +} + +// Eliminate ADJCALLSTACKDOWN, ADJCALLSTACKUP pseudo instructions. +MachineBasicBlock::iterator RISCVFrameLowering::eliminateCallFramePseudoInstr( + MachineFunction &MF, MachineBasicBlock &MBB, + MachineBasicBlock::iterator MI) const { + unsigned SPReg = RISCV::X2; + DebugLoc DL = MI->getDebugLoc(); + + if (!hasReservedCallFrame(MF)) { + // If space has not been reserved for a call frame, ADJCALLSTACKDOWN and + // ADJCALLSTACKUP must be converted to instructions manipulating the stack + // pointer. This is necessary when there is a variable length stack + // allocation (e.g. alloca), which means it's not possible to allocate + // space for outgoing arguments from within the function prologue. + int64_t Amount = MI->getOperand(0).getImm(); + + if (Amount != 0) { + // Ensure the stack remains aligned after adjustment. + Amount = alignSPAdjust(Amount); + + if (MI->getOpcode() == RISCV::ADJCALLSTACKDOWN) + Amount = -Amount; + + adjustReg(MBB, MI, DL, SPReg, SPReg, Amount, MachineInstr::NoFlags); + } + } + + return MBB.erase(MI); } diff --git a/lib/Target/RISCV/RISCVFrameLowering.h b/lib/Target/RISCV/RISCVFrameLowering.h index d92bb70c76da..ca653c2b9f17 100644 --- a/lib/Target/RISCV/RISCVFrameLowering.h +++ b/lib/Target/RISCV/RISCVFrameLowering.h @@ -36,13 +36,15 @@ public: void determineCalleeSaves(MachineFunction &MF, BitVector &SavedRegs, RegScavenger *RS) const override; + void processFunctionBeforeFrameFinalized(MachineFunction &MF, + RegScavenger *RS) const override; + bool hasFP(const MachineFunction &MF) const override; + bool hasReservedCallFrame(const MachineFunction &MF) const override; MachineBasicBlock::iterator eliminateCallFramePseudoInstr(MachineFunction &MF, MachineBasicBlock &MBB, - MachineBasicBlock::iterator MI) const override { - return MBB.erase(MI); - } + MachineBasicBlock::iterator MI) const override; protected: const RISCVSubtarget &STI; diff --git a/lib/Target/RISCV/RISCVISelDAGToDAG.cpp b/lib/Target/RISCV/RISCVISelDAGToDAG.cpp index 113a45ac7cc0..04441b9a9b15 100644 --- a/lib/Target/RISCV/RISCVISelDAGToDAG.cpp +++ b/lib/Target/RISCV/RISCVISelDAGToDAG.cpp @@ -42,25 +42,36 @@ public: return SelectionDAGISel::runOnMachineFunction(MF); } + void PostprocessISelDAG() override; + void Select(SDNode *Node) override; + bool SelectInlineAsmMemoryOperand(const SDValue &Op, unsigned ConstraintID, + std::vector<SDValue> &OutOps) override; + bool SelectAddrFI(SDValue Addr, SDValue &Base); // Include the pieces autogenerated from the target description. #include "RISCVGenDAGISel.inc" + +private: + void doPeepholeLoadStoreADDI(); + void doPeepholeBuildPairF64SplitF64(); }; } +void RISCVDAGToDAGISel::PostprocessISelDAG() { + doPeepholeLoadStoreADDI(); + doPeepholeBuildPairF64SplitF64(); +} + 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"); + LLVM_DEBUG(dbgs() << "== "; Node->dump(CurDAG); dbgs() << "\n"); Node->setNodeId(-1); return; } @@ -82,7 +93,7 @@ void RISCVDAGToDAGISel::Select(SDNode *Node) { if (Opcode == ISD::FrameIndex) { SDLoc DL(Node); SDValue Imm = CurDAG->getTargetConstant(0, DL, XLenVT); - int FI = dyn_cast<FrameIndexSDNode>(Node)->getIndex(); + int FI = 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)); @@ -93,6 +104,22 @@ void RISCVDAGToDAGISel::Select(SDNode *Node) { SelectCode(Node); } +bool RISCVDAGToDAGISel::SelectInlineAsmMemoryOperand( + const SDValue &Op, unsigned ConstraintID, std::vector<SDValue> &OutOps) { + switch (ConstraintID) { + case InlineAsm::Constraint_i: + case InlineAsm::Constraint_m: + // We just support simple memory operands that have a single address + // operand and need no special handling. + OutOps.push_back(Op); + return false; + default: + break; + } + + return true; +} + bool RISCVDAGToDAGISel::SelectAddrFI(SDValue Addr, SDValue &Base) { if (auto FIN = dyn_cast<FrameIndexSDNode>(Addr)) { Base = CurDAG->getTargetFrameIndex(FIN->getIndex(), Subtarget->getXLenVT()); @@ -101,6 +128,131 @@ bool RISCVDAGToDAGISel::SelectAddrFI(SDValue Addr, SDValue &Base) { return false; } +// Merge an ADDI into the offset of a load/store instruction where possible. +// (load (add base, off), 0) -> (load base, off) +// (store val, (add base, off)) -> (store val, base, off) +void RISCVDAGToDAGISel::doPeepholeLoadStoreADDI() { + SelectionDAG::allnodes_iterator Position(CurDAG->getRoot().getNode()); + ++Position; + + while (Position != CurDAG->allnodes_begin()) { + SDNode *N = &*--Position; + // Skip dead nodes and any non-machine opcodes. + if (N->use_empty() || !N->isMachineOpcode()) + continue; + + int OffsetOpIdx; + int BaseOpIdx; + + // Only attempt this optimisation for I-type loads and S-type stores. + switch (N->getMachineOpcode()) { + default: + continue; + case RISCV::LB: + case RISCV::LH: + case RISCV::LW: + case RISCV::LBU: + case RISCV::LHU: + case RISCV::LWU: + case RISCV::LD: + case RISCV::FLW: + case RISCV::FLD: + BaseOpIdx = 0; + OffsetOpIdx = 1; + break; + case RISCV::SB: + case RISCV::SH: + case RISCV::SW: + case RISCV::SD: + case RISCV::FSW: + case RISCV::FSD: + BaseOpIdx = 1; + OffsetOpIdx = 2; + break; + } + + // Currently, the load/store offset must be 0 to be considered for this + // peephole optimisation. + if (!isa<ConstantSDNode>(N->getOperand(OffsetOpIdx)) || + N->getConstantOperandVal(OffsetOpIdx) != 0) + continue; + + SDValue Base = N->getOperand(BaseOpIdx); + + // If the base is an ADDI, we can merge it in to the load/store. + if (!Base.isMachineOpcode() || Base.getMachineOpcode() != RISCV::ADDI) + continue; + + SDValue ImmOperand = Base.getOperand(1); + + if (auto Const = dyn_cast<ConstantSDNode>(ImmOperand)) { + ImmOperand = CurDAG->getTargetConstant( + Const->getSExtValue(), SDLoc(ImmOperand), ImmOperand.getValueType()); + } else if (auto GA = dyn_cast<GlobalAddressSDNode>(ImmOperand)) { + ImmOperand = CurDAG->getTargetGlobalAddress( + GA->getGlobal(), SDLoc(ImmOperand), ImmOperand.getValueType(), + GA->getOffset(), GA->getTargetFlags()); + } else { + continue; + } + + LLVM_DEBUG(dbgs() << "Folding add-immediate into mem-op:\nBase: "); + LLVM_DEBUG(Base->dump(CurDAG)); + LLVM_DEBUG(dbgs() << "\nN: "); + LLVM_DEBUG(N->dump(CurDAG)); + LLVM_DEBUG(dbgs() << "\n"); + + // Modify the offset operand of the load/store. + if (BaseOpIdx == 0) // Load + CurDAG->UpdateNodeOperands(N, Base.getOperand(0), ImmOperand, + N->getOperand(2)); + else // Store + CurDAG->UpdateNodeOperands(N, N->getOperand(0), Base.getOperand(0), + ImmOperand, N->getOperand(3)); + + // The add-immediate may now be dead, in which case remove it. + if (Base.getNode()->use_empty()) + CurDAG->RemoveDeadNode(Base.getNode()); + } +} + +// Remove redundant BuildPairF64+SplitF64 pairs. i.e. cases where an f64 is +// built of two i32 values, only to be split apart again. This must be done +// here as a peephole optimisation as the DAG has not been fully legalized at +// the point BuildPairF64/SplitF64 nodes are created in RISCVISelLowering, so +// some nodes would not yet have been replaced with libcalls. +void RISCVDAGToDAGISel::doPeepholeBuildPairF64SplitF64() { + SelectionDAG::allnodes_iterator Position(CurDAG->getRoot().getNode()); + ++Position; + + while (Position != CurDAG->allnodes_begin()) { + SDNode *N = &*--Position; + // Skip dead nodes and any nodes other than SplitF64Pseudo. + if (N->use_empty() || !N->isMachineOpcode() || + !(N->getMachineOpcode() == RISCV::SplitF64Pseudo)) + continue; + + // If the operand to SplitF64 is a BuildPairF64, the split operation is + // redundant. Just use the operands to BuildPairF64 as the result. + SDValue F64Val = N->getOperand(0); + if (F64Val.isMachineOpcode() && + F64Val.getMachineOpcode() == RISCV::BuildPairF64Pseudo) { + LLVM_DEBUG( + dbgs() << "Removing redundant SplitF64Pseudo and replacing uses " + "with BuildPairF64Pseudo operands:\n"); + LLVM_DEBUG(dbgs() << "N: "); + LLVM_DEBUG(N->dump(CurDAG)); + LLVM_DEBUG(dbgs() << "F64Val: "); + LLVM_DEBUG(F64Val->dump(CurDAG)); + LLVM_DEBUG(dbgs() << "\n"); + SDValue From[] = {SDValue(N, 0), SDValue(N, 1)}; + SDValue To[] = {F64Val.getOperand(0), F64Val.getOperand(1)}; + CurDAG->ReplaceAllUsesOfValuesWith(From, To, 2); + } + } + CurDAG->RemoveDeadNodes(); +} + // This pass converts a legalized DAG into a RISCV-specific DAG, ready // for instruction scheduling. FunctionPass *llvm::createRISCVISelDag(RISCVTargetMachine &TM) { diff --git a/lib/Target/RISCV/RISCVISelLowering.cpp b/lib/Target/RISCV/RISCVISelLowering.cpp index 7d32954936be..87796e5b1097 100644 --- a/lib/Target/RISCV/RISCVISelLowering.cpp +++ b/lib/Target/RISCV/RISCVISelLowering.cpp @@ -14,9 +14,11 @@ #include "RISCVISelLowering.h" #include "RISCV.h" +#include "RISCVMachineFunctionInfo.h" #include "RISCVRegisterInfo.h" #include "RISCVSubtarget.h" #include "RISCVTargetMachine.h" +#include "llvm/ADT/Statistic.h" #include "llvm/CodeGen/CallingConvLower.h" #include "llvm/CodeGen/MachineFrameInfo.h" #include "llvm/CodeGen/MachineFunction.h" @@ -35,6 +37,8 @@ using namespace llvm; #define DEBUG_TYPE "riscv-lower" +STATISTIC(NumTailCalls, "Number of tail calls"); + RISCVTargetLowering::RISCVTargetLowering(const TargetMachine &TM, const RISCVSubtarget &STI) : TargetLowering(TM), Subtarget(STI) { @@ -44,6 +48,11 @@ RISCVTargetLowering::RISCVTargetLowering(const TargetMachine &TM, // Set up the register classes. addRegisterClass(XLenVT, &RISCV::GPRRegClass); + if (Subtarget.hasStdExtF()) + addRegisterClass(MVT::f32, &RISCV::FPR32RegClass); + if (Subtarget.hasStdExtD()) + addRegisterClass(MVT::f64, &RISCV::FPR64RegClass); + // Compute derived properties from the register classes. computeRegisterProperties(STI.getRegisterInfo()); @@ -63,26 +72,28 @@ RISCVTargetLowering::RISCVTargetLowering(const TargetMachine &TM, setOperationAction(ISD::STACKSAVE, MVT::Other, Expand); setOperationAction(ISD::STACKRESTORE, MVT::Other, Expand); + setOperationAction(ISD::VASTART, MVT::Other, Custom); + setOperationAction(ISD::VAARG, MVT::Other, Expand); + setOperationAction(ISD::VACOPY, MVT::Other, Expand); + setOperationAction(ISD::VAEND, 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); + if (!Subtarget.hasStdExtM()) { + setOperationAction(ISD::MUL, XLenVT, Expand); + setOperationAction(ISD::MULHS, XLenVT, Expand); + setOperationAction(ISD::MULHU, XLenVT, Expand); + setOperationAction(ISD::SDIV, XLenVT, Expand); + setOperationAction(ISD::UDIV, XLenVT, Expand); + setOperationAction(ISD::SREM, XLenVT, Expand); + setOperationAction(ISD::UREM, 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); @@ -95,19 +106,128 @@ RISCVTargetLowering::RISCVTargetLowering(const TargetMachine &TM, setOperationAction(ISD::CTLZ, XLenVT, Expand); setOperationAction(ISD::CTPOP, XLenVT, Expand); + ISD::CondCode FPCCToExtend[] = { + ISD::SETOGT, ISD::SETOGE, ISD::SETONE, ISD::SETO, ISD::SETUEQ, + ISD::SETUGT, ISD::SETUGE, ISD::SETULT, ISD::SETULE, ISD::SETUNE, + ISD::SETGT, ISD::SETGE, ISD::SETNE}; + + if (Subtarget.hasStdExtF()) { + setOperationAction(ISD::FMINNUM, MVT::f32, Legal); + setOperationAction(ISD::FMAXNUM, MVT::f32, Legal); + for (auto CC : FPCCToExtend) + setCondCodeAction(CC, MVT::f32, Expand); + setOperationAction(ISD::SELECT_CC, MVT::f32, Expand); + setOperationAction(ISD::SELECT, MVT::f32, Custom); + setOperationAction(ISD::BR_CC, MVT::f32, Expand); + } + + if (Subtarget.hasStdExtD()) { + setOperationAction(ISD::FMINNUM, MVT::f64, Legal); + setOperationAction(ISD::FMAXNUM, MVT::f64, Legal); + for (auto CC : FPCCToExtend) + setCondCodeAction(CC, MVT::f64, Expand); + setOperationAction(ISD::SELECT_CC, MVT::f64, Expand); + setOperationAction(ISD::SELECT, MVT::f64, Custom); + setOperationAction(ISD::BR_CC, MVT::f64, Expand); + setLoadExtAction(ISD::EXTLOAD, MVT::f64, MVT::f32, Expand); + setTruncStoreAction(MVT::f64, MVT::f32, Expand); + } + setOperationAction(ISD::GlobalAddress, XLenVT, Custom); setOperationAction(ISD::BlockAddress, XLenVT, Custom); + setOperationAction(ISD::ConstantPool, XLenVT, Custom); + + if (Subtarget.hasStdExtA()) + setMaxAtomicSizeInBitsSupported(Subtarget.getXLen()); + else + setMaxAtomicSizeInBitsSupported(0); setBooleanContents(ZeroOrOneBooleanContent); // Function alignments (log2). - setMinFunctionAlignment(3); - setPrefFunctionAlignment(3); + unsigned FunctionAlignment = Subtarget.hasStdExtC() ? 1 : 2; + setMinFunctionAlignment(FunctionAlignment); + setPrefFunctionAlignment(FunctionAlignment); // Effectively disable jump table generation. setMinimumJumpTableEntries(INT_MAX); } +EVT RISCVTargetLowering::getSetCCResultType(const DataLayout &DL, LLVMContext &, + EVT VT) const { + if (!VT.isVector()) + return getPointerTy(DL); + return VT.changeVectorElementTypeToInteger(); +} + +bool RISCVTargetLowering::isLegalAddressingMode(const DataLayout &DL, + const AddrMode &AM, Type *Ty, + unsigned AS, + Instruction *I) const { + // No global is ever allowed as a base. + if (AM.BaseGV) + return false; + + // Require a 12-bit signed offset. + if (!isInt<12>(AM.BaseOffs)) + return false; + + switch (AM.Scale) { + case 0: // "r+i" or just "i", depending on HasBaseReg. + break; + case 1: + if (!AM.HasBaseReg) // allow "r+i". + break; + return false; // disallow "r+r" or "r+r+i". + default: + return false; + } + + return true; +} + +bool RISCVTargetLowering::isLegalICmpImmediate(int64_t Imm) const { + return isInt<12>(Imm); +} + +bool RISCVTargetLowering::isLegalAddImmediate(int64_t Imm) const { + return isInt<12>(Imm); +} + +// On RV32, 64-bit integers are split into their high and low parts and held +// in two different registers, so the trunc is free since the low register can +// just be used. +bool RISCVTargetLowering::isTruncateFree(Type *SrcTy, Type *DstTy) const { + if (Subtarget.is64Bit() || !SrcTy->isIntegerTy() || !DstTy->isIntegerTy()) + return false; + unsigned SrcBits = SrcTy->getPrimitiveSizeInBits(); + unsigned DestBits = DstTy->getPrimitiveSizeInBits(); + return (SrcBits == 64 && DestBits == 32); +} + +bool RISCVTargetLowering::isTruncateFree(EVT SrcVT, EVT DstVT) const { + if (Subtarget.is64Bit() || SrcVT.isVector() || DstVT.isVector() || + !SrcVT.isInteger() || !DstVT.isInteger()) + return false; + unsigned SrcBits = SrcVT.getSizeInBits(); + unsigned DestBits = DstVT.getSizeInBits(); + return (SrcBits == 64 && DestBits == 32); +} + +bool RISCVTargetLowering::isZExtFree(SDValue Val, EVT VT2) const { + // Zexts are free if they can be combined with a load. + if (auto *LD = dyn_cast<LoadSDNode>(Val)) { + EVT MemVT = LD->getMemoryVT(); + if ((MemVT == MVT::i8 || MemVT == MVT::i16 || + (Subtarget.is64Bit() && MemVT == MVT::i32)) && + (LD->getExtensionType() == ISD::NON_EXTLOAD || + LD->getExtensionType() == ISD::ZEXTLOAD)) + return true; + } + + return TargetLowering::isZExtFree(Val, VT2); +} + // 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. @@ -156,8 +276,16 @@ SDValue RISCVTargetLowering::LowerOperation(SDValue Op, return lowerGlobalAddress(Op, DAG); case ISD::BlockAddress: return lowerBlockAddress(Op, DAG); + case ISD::ConstantPool: + return lowerConstantPool(Op, DAG); case ISD::SELECT: return lowerSELECT(Op, DAG); + case ISD::VASTART: + return lowerVASTART(Op, DAG); + case ISD::FRAMEADDR: + return LowerFRAMEADDR(Op, DAG); + case ISD::RETURNADDR: + return LowerRETURNADDR(Op, DAG); } } @@ -168,17 +296,22 @@ SDValue RISCVTargetLowering::lowerGlobalAddress(SDValue Op, GlobalAddressSDNode *N = cast<GlobalAddressSDNode>(Op); const GlobalValue *GV = N->getGlobal(); int64_t Offset = N->getOffset(); + MVT XLenVT = Subtarget.getXLenVT(); 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); + // In order to maximise the opportunity for common subexpression elimination, + // emit a separate ADD node for the global address offset instead of folding + // it in the global address node. Later peephole optimisations may choose to + // fold it back in when profitable. + SDValue GAHi = DAG.getTargetGlobalAddress(GV, DL, Ty, 0, RISCVII::MO_HI); + SDValue GALo = DAG.getTargetGlobalAddress(GV, DL, Ty, 0, 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); + if (Offset != 0) + return DAG.getNode(ISD::ADD, DL, Ty, MNLo, + DAG.getConstant(Offset, DL, XLenVT)); return MNLo; } @@ -201,6 +334,29 @@ SDValue RISCVTargetLowering::lowerBlockAddress(SDValue Op, return MNLo; } +SDValue RISCVTargetLowering::lowerConstantPool(SDValue Op, + SelectionDAG &DAG) const { + SDLoc DL(Op); + EVT Ty = Op.getValueType(); + ConstantPoolSDNode *N = cast<ConstantPoolSDNode>(Op); + const Constant *CPA = N->getConstVal(); + int64_t Offset = N->getOffset(); + unsigned Alignment = N->getAlignment(); + + if (!isPositionIndependent()) { + SDValue CPAHi = + DAG.getTargetConstantPool(CPA, Ty, Alignment, Offset, RISCVII::MO_HI); + SDValue CPALo = + DAG.getTargetConstantPool(CPA, Ty, Alignment, Offset, RISCVII::MO_LO); + SDValue MNHi = SDValue(DAG.getMachineNode(RISCV::LUI, DL, Ty, CPAHi), 0); + SDValue MNLo = + SDValue(DAG.getMachineNode(RISCV::ADDI, DL, Ty, MNHi, CPALo), 0); + return MNLo; + } else { + report_fatal_error("Unable to lowerConstantPool"); + } +} + SDValue RISCVTargetLowering::lowerExternalSymbol(SDValue Op, SelectionDAG &DAG) const { SDLoc DL(Op); @@ -261,14 +417,153 @@ SDValue RISCVTargetLowering::lowerSELECT(SDValue Op, SelectionDAG &DAG) const { return DAG.getNode(RISCVISD::SELECT_CC, DL, VTs, Ops); } +SDValue RISCVTargetLowering::lowerVASTART(SDValue Op, SelectionDAG &DAG) const { + MachineFunction &MF = DAG.getMachineFunction(); + RISCVMachineFunctionInfo *FuncInfo = MF.getInfo<RISCVMachineFunctionInfo>(); + + SDLoc DL(Op); + SDValue FI = DAG.getFrameIndex(FuncInfo->getVarArgsFrameIndex(), + getPointerTy(MF.getDataLayout())); + + // vastart just stores the address of the VarArgsFrameIndex slot into the + // memory location argument. + const Value *SV = cast<SrcValueSDNode>(Op.getOperand(2))->getValue(); + return DAG.getStore(Op.getOperand(0), DL, FI, Op.getOperand(1), + MachinePointerInfo(SV)); +} + +SDValue RISCVTargetLowering::LowerFRAMEADDR(SDValue Op, + SelectionDAG &DAG) const { + const RISCVRegisterInfo &RI = *Subtarget.getRegisterInfo(); + MachineFunction &MF = DAG.getMachineFunction(); + MachineFrameInfo &MFI = MF.getFrameInfo(); + MFI.setFrameAddressIsTaken(true); + unsigned FrameReg = RI.getFrameRegister(MF); + int XLenInBytes = Subtarget.getXLen() / 8; + + EVT VT = Op.getValueType(); + SDLoc DL(Op); + SDValue FrameAddr = DAG.getCopyFromReg(DAG.getEntryNode(), DL, FrameReg, VT); + unsigned Depth = cast<ConstantSDNode>(Op.getOperand(0))->getZExtValue(); + while (Depth--) { + int Offset = -(XLenInBytes * 2); + SDValue Ptr = DAG.getNode(ISD::ADD, DL, VT, FrameAddr, + DAG.getIntPtrConstant(Offset, DL)); + FrameAddr = + DAG.getLoad(VT, DL, DAG.getEntryNode(), Ptr, MachinePointerInfo()); + } + return FrameAddr; +} + +SDValue RISCVTargetLowering::LowerRETURNADDR(SDValue Op, + SelectionDAG &DAG) const { + const RISCVRegisterInfo &RI = *Subtarget.getRegisterInfo(); + MachineFunction &MF = DAG.getMachineFunction(); + MachineFrameInfo &MFI = MF.getFrameInfo(); + MFI.setReturnAddressIsTaken(true); + MVT XLenVT = Subtarget.getXLenVT(); + int XLenInBytes = Subtarget.getXLen() / 8; + + if (verifyReturnAddressArgumentIsConstant(Op, DAG)) + return SDValue(); + + EVT VT = Op.getValueType(); + SDLoc DL(Op); + unsigned Depth = cast<ConstantSDNode>(Op.getOperand(0))->getZExtValue(); + if (Depth) { + int Off = -XLenInBytes; + SDValue FrameAddr = LowerFRAMEADDR(Op, DAG); + SDValue Offset = DAG.getConstant(Off, DL, VT); + return DAG.getLoad(VT, DL, DAG.getEntryNode(), + DAG.getNode(ISD::ADD, DL, VT, FrameAddr, Offset), + MachinePointerInfo()); + } + + // Return the value of the return address register, marking it an implicit + // live-in. + unsigned Reg = MF.addLiveIn(RI.getRARegister(), getRegClassFor(XLenVT)); + return DAG.getCopyFromReg(DAG.getEntryNode(), DL, Reg, XLenVT); +} + +static MachineBasicBlock *emitSplitF64Pseudo(MachineInstr &MI, + MachineBasicBlock *BB) { + assert(MI.getOpcode() == RISCV::SplitF64Pseudo && "Unexpected instruction"); + + MachineFunction &MF = *BB->getParent(); + DebugLoc DL = MI.getDebugLoc(); + const TargetInstrInfo &TII = *MF.getSubtarget().getInstrInfo(); + const TargetRegisterInfo *RI = MF.getSubtarget().getRegisterInfo(); + unsigned LoReg = MI.getOperand(0).getReg(); + unsigned HiReg = MI.getOperand(1).getReg(); + unsigned SrcReg = MI.getOperand(2).getReg(); + const TargetRegisterClass *SrcRC = &RISCV::FPR64RegClass; + int FI = MF.getInfo<RISCVMachineFunctionInfo>()->getMoveF64FrameIndex(); + + TII.storeRegToStackSlot(*BB, MI, SrcReg, MI.getOperand(2).isKill(), FI, SrcRC, + RI); + MachineMemOperand *MMO = + MF.getMachineMemOperand(MachinePointerInfo::getFixedStack(MF, FI), + MachineMemOperand::MOLoad, 8, 8); + BuildMI(*BB, MI, DL, TII.get(RISCV::LW), LoReg) + .addFrameIndex(FI) + .addImm(0) + .addMemOperand(MMO); + BuildMI(*BB, MI, DL, TII.get(RISCV::LW), HiReg) + .addFrameIndex(FI) + .addImm(4) + .addMemOperand(MMO); + MI.eraseFromParent(); // The pseudo instruction is gone now. + return BB; +} + +static MachineBasicBlock *emitBuildPairF64Pseudo(MachineInstr &MI, + MachineBasicBlock *BB) { + assert(MI.getOpcode() == RISCV::BuildPairF64Pseudo && + "Unexpected instruction"); + + MachineFunction &MF = *BB->getParent(); + DebugLoc DL = MI.getDebugLoc(); + const TargetInstrInfo &TII = *MF.getSubtarget().getInstrInfo(); + const TargetRegisterInfo *RI = MF.getSubtarget().getRegisterInfo(); + unsigned DstReg = MI.getOperand(0).getReg(); + unsigned LoReg = MI.getOperand(1).getReg(); + unsigned HiReg = MI.getOperand(2).getReg(); + const TargetRegisterClass *DstRC = &RISCV::FPR64RegClass; + int FI = MF.getInfo<RISCVMachineFunctionInfo>()->getMoveF64FrameIndex(); + + MachineMemOperand *MMO = + MF.getMachineMemOperand(MachinePointerInfo::getFixedStack(MF, FI), + MachineMemOperand::MOStore, 8, 8); + BuildMI(*BB, MI, DL, TII.get(RISCV::SW)) + .addReg(LoReg, getKillRegState(MI.getOperand(1).isKill())) + .addFrameIndex(FI) + .addImm(0) + .addMemOperand(MMO); + BuildMI(*BB, MI, DL, TII.get(RISCV::SW)) + .addReg(HiReg, getKillRegState(MI.getOperand(2).isKill())) + .addFrameIndex(FI) + .addImm(4) + .addMemOperand(MMO); + TII.loadRegFromStackSlot(*BB, MI, DstReg, FI, DstRC, RI); + MI.eraseFromParent(); // The pseudo instruction is gone now. + return BB; +} + 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"); + switch (MI.getOpcode()) { + default: + llvm_unreachable("Unexpected instr type to insert"); + case RISCV::Select_GPR_Using_CC_GPR: + case RISCV::Select_FPR32_Using_CC_GPR: + case RISCV::Select_FPR64_Using_CC_GPR: + break; + case RISCV::BuildPairF64Pseudo: + return emitBuildPairF64Pseudo(MI, BB); + case RISCV::SplitF64Pseudo: + return emitSplitF64Pseudo(MI, BB); + } // To "insert" a SELECT instruction, we actually have to insert the triangle // control-flow pattern. The incoming instruction knows the destination vreg @@ -281,7 +576,9 @@ RISCVTargetLowering::EmitInstrWithCustomInserter(MachineInstr &MI, // | IfFalseMBB // | / // TailMBB + const TargetInstrInfo &TII = *BB->getParent()->getSubtarget().getInstrInfo(); const BasicBlock *LLVM_BB = BB->getBasicBlock(); + DebugLoc DL = MI.getDebugLoc(); MachineFunction::iterator I = ++BB->getIterator(); MachineBasicBlock *HeadMBB = BB; @@ -398,19 +695,36 @@ static bool CC_RISCVAssign2XLen(unsigned XLen, CCState &State, CCValAssign VA1, // 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) { + CCState &State, bool IsFixed, bool IsRet, Type *OrigTy) { 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"); + if (ValVT == MVT::f32) { + LocVT = MVT::i32; + LocInfo = CCValAssign::BCvt; + } // Any return value split in to more than two values can't be returned // directly. if (IsRet && ValNo > 1) return true; + // If this is a variadic argument, the RISC-V calling convention requires + // that it is assigned an 'even' or 'aligned' register if it has 8-byte + // alignment (RV32) or 16-byte alignment (RV64). An aligned register should + // be used regardless of whether the original argument was split during + // legalisation or not. The argument will not be passed by registers if the + // original type is larger than 2*XLEN, so the register alignment rule does + // not apply. + unsigned TwoXLenInBytes = (2 * XLen) / 8; + if (!IsFixed && ArgFlags.getOrigAlign() == TwoXLenInBytes && + DL.getTypeAllocSize(OrigTy) == TwoXLenInBytes) { + unsigned RegIdx = State.getFirstUnallocated(ArgGPRs); + // Skip 'odd' register if necessary. + if (RegIdx != array_lengthof(ArgGPRs) && RegIdx % 2 == 1) + State.AllocateReg(ArgGPRs); + } + SmallVectorImpl<CCValAssign> &PendingLocs = State.getPendingLocs(); SmallVectorImpl<ISD::ArgFlagsTy> &PendingArgFlags = State.getPendingArgFlags(); @@ -418,6 +732,28 @@ static bool CC_RISCV(const DataLayout &DL, unsigned ValNo, MVT ValVT, MVT LocVT, assert(PendingLocs.size() == PendingArgFlags.size() && "PendingLocs and PendingArgFlags out of sync"); + // Handle passing f64 on RV32D with a soft float ABI. + if (XLen == 32 && ValVT == MVT::f64) { + assert(!ArgFlags.isSplit() && PendingLocs.empty() && + "Can't lower f64 if it is split"); + // Depending on available argument GPRS, f64 may be passed in a pair of + // GPRs, split between a GPR and the stack, or passed completely on the + // stack. LowerCall/LowerFormalArguments/LowerReturn must recognise these + // cases. + unsigned Reg = State.AllocateReg(ArgGPRs); + LocVT = MVT::i32; + if (!Reg) { + unsigned StackOffset = State.AllocateStack(8, 8); + State.addLoc( + CCValAssign::getMem(ValNo, ValVT, StackOffset, LocVT, LocInfo)); + return false; + } + if (!State.AllocateReg(ArgGPRs)) + State.AllocateStack(4, 4); + State.addLoc(CCValAssign::getReg(ValNo, ValVT, Reg, LocVT, LocInfo)); + return false; + } + // Split arguments might be passed indirectly, so keep track of the pending // values. if (ArgFlags.isSplit() || !PendingLocs.empty()) { @@ -482,15 +818,22 @@ void RISCVTargetLowering::analyzeInputArgs( MachineFunction &MF, CCState &CCInfo, const SmallVectorImpl<ISD::InputArg> &Ins, bool IsRet) const { unsigned NumArgs = Ins.size(); + FunctionType *FType = MF.getFunction().getFunctionType(); for (unsigned i = 0; i != NumArgs; ++i) { MVT ArgVT = Ins[i].VT; ISD::ArgFlagsTy ArgFlags = Ins[i].Flags; + Type *ArgTy = nullptr; + if (IsRet) + ArgTy = FType->getReturnType(); + else if (Ins[i].isOrigArg()) + ArgTy = FType->getParamType(Ins[i].getOrigArgIndex()); + 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'); + ArgFlags, CCInfo, /*IsRet=*/true, IsRet, ArgTy)) { + LLVM_DEBUG(dbgs() << "InputArg #" << i << " has unhandled type " + << EVT(ArgVT).getEVTString() << '\n'); llvm_unreachable(nullptr); } } @@ -498,17 +841,19 @@ void RISCVTargetLowering::analyzeInputArgs( void RISCVTargetLowering::analyzeOutputArgs( MachineFunction &MF, CCState &CCInfo, - const SmallVectorImpl<ISD::OutputArg> &Outs, bool IsRet) const { + const SmallVectorImpl<ISD::OutputArg> &Outs, bool IsRet, + CallLoweringInfo *CLI) const { unsigned NumArgs = Outs.size(); for (unsigned i = 0; i != NumArgs; i++) { MVT ArgVT = Outs[i].VT; ISD::ArgFlagsTy ArgFlags = Outs[i].Flags; + Type *OrigTy = CLI ? CLI->getArgs()[Outs[i].OrigArgIndex].Ty : nullptr; 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"); + ArgFlags, CCInfo, Outs[i].IsFixed, IsRet, OrigTy)) { + LLVM_DEBUG(dbgs() << "OutputArg #" << i << " has unhandled type " + << EVT(ArgVT).getEVTString() << "\n"); llvm_unreachable(nullptr); } } @@ -521,6 +866,7 @@ static SDValue unpackFromRegLoc(SelectionDAG &DAG, SDValue Chain, MachineFunction &MF = DAG.getMachineFunction(); MachineRegisterInfo &RegInfo = MF.getRegInfo(); EVT LocVT = VA.getLocVT(); + EVT ValVT = VA.getValVT(); SDValue Val; unsigned VReg = RegInfo.createVirtualRegister(&RISCV::GPRRegClass); @@ -532,8 +878,12 @@ static SDValue unpackFromRegLoc(SelectionDAG &DAG, SDValue Chain, llvm_unreachable("Unexpected CCValAssign::LocInfo"); case CCValAssign::Full: case CCValAssign::Indirect: - return Val; + break; + case CCValAssign::BCvt: + Val = DAG.getNode(ISD::BITCAST, DL, ValVT, Val); + break; } + return Val; } // The caller is responsible for loading the full value if the argument is @@ -565,6 +915,43 @@ static SDValue unpackFromMemLoc(SelectionDAG &DAG, SDValue Chain, return Val; } +static SDValue unpackF64OnRV32DSoftABI(SelectionDAG &DAG, SDValue Chain, + const CCValAssign &VA, const SDLoc &DL) { + assert(VA.getLocVT() == MVT::i32 && VA.getValVT() == MVT::f64 && + "Unexpected VA"); + MachineFunction &MF = DAG.getMachineFunction(); + MachineFrameInfo &MFI = MF.getFrameInfo(); + MachineRegisterInfo &RegInfo = MF.getRegInfo(); + + if (VA.isMemLoc()) { + // f64 is passed on the stack. + int FI = MFI.CreateFixedObject(8, VA.getLocMemOffset(), /*Immutable=*/true); + SDValue FIN = DAG.getFrameIndex(FI, MVT::i32); + return DAG.getLoad(MVT::f64, DL, Chain, FIN, + MachinePointerInfo::getFixedStack(MF, FI)); + } + + assert(VA.isRegLoc() && "Expected register VA assignment"); + + unsigned LoVReg = RegInfo.createVirtualRegister(&RISCV::GPRRegClass); + RegInfo.addLiveIn(VA.getLocReg(), LoVReg); + SDValue Lo = DAG.getCopyFromReg(Chain, DL, LoVReg, MVT::i32); + SDValue Hi; + if (VA.getLocReg() == RISCV::X17) { + // Second half of f64 is passed on the stack. + int FI = MFI.CreateFixedObject(4, 0, /*Immutable=*/true); + SDValue FIN = DAG.getFrameIndex(FI, MVT::i32); + Hi = DAG.getLoad(MVT::i32, DL, Chain, FIN, + MachinePointerInfo::getFixedStack(MF, FI)); + } else { + // Second half of f64 is passed in another GPR. + unsigned HiVReg = RegInfo.createVirtualRegister(&RISCV::GPRRegClass); + RegInfo.addLiveIn(VA.getLocReg() + 1, HiVReg); + Hi = DAG.getCopyFromReg(Chain, DL, HiVReg, MVT::i32); + } + return DAG.getNode(RISCVISD::BuildPairF64, DL, MVT::f64, Lo, Hi); +} + // Transform physical registers into virtual registers. SDValue RISCVTargetLowering::LowerFormalArguments( SDValue Chain, CallingConv::ID CallConv, bool IsVarArg, @@ -580,11 +967,26 @@ SDValue RISCVTargetLowering::LowerFormalArguments( } MachineFunction &MF = DAG.getMachineFunction(); - MVT XLenVT = Subtarget.getXLenVT(); - EVT PtrVT = getPointerTy(DAG.getDataLayout()); - if (IsVarArg) - report_fatal_error("VarArg not supported"); + const Function &Func = MF.getFunction(); + if (Func.hasFnAttribute("interrupt")) { + if (!Func.arg_empty()) + report_fatal_error( + "Functions with the interrupt attribute cannot have arguments!"); + + StringRef Kind = + MF.getFunction().getFnAttribute("interrupt").getValueAsString(); + + if (!(Kind == "user" || Kind == "supervisor" || Kind == "machine")) + report_fatal_error( + "Function interrupt attribute argument not supported!"); + } + + EVT PtrVT = getPointerTy(DAG.getDataLayout()); + MVT XLenVT = Subtarget.getXLenVT(); + unsigned XLenInBytes = Subtarget.getXLen() / 8; + // Used with vargs to acumulate store chains. + std::vector<SDValue> OutChains; // Assign locations to all of the incoming arguments. SmallVector<CCValAssign, 16> ArgLocs; @@ -595,7 +997,11 @@ SDValue RISCVTargetLowering::LowerFormalArguments( CCValAssign &VA = ArgLocs[i]; assert(VA.getLocVT() == XLenVT && "Unhandled argument type"); SDValue ArgValue; - if (VA.isRegLoc()) + // Passing f64 on RV32D with a soft float ABI must be handled as a special + // case. + if (VA.getLocVT() == MVT::i32 && VA.getValVT() == MVT::f64) + ArgValue = unpackF64OnRV32DSoftABI(DAG, Chain, VA, DL); + else if (VA.isRegLoc()) ArgValue = unpackFromRegLoc(DAG, Chain, VA, DL); else ArgValue = unpackFromMemLoc(DAG, Chain, VA, DL); @@ -621,9 +1027,155 @@ SDValue RISCVTargetLowering::LowerFormalArguments( } InVals.push_back(ArgValue); } + + if (IsVarArg) { + ArrayRef<MCPhysReg> ArgRegs = makeArrayRef(ArgGPRs); + unsigned Idx = CCInfo.getFirstUnallocated(ArgRegs); + const TargetRegisterClass *RC = &RISCV::GPRRegClass; + MachineFrameInfo &MFI = MF.getFrameInfo(); + MachineRegisterInfo &RegInfo = MF.getRegInfo(); + RISCVMachineFunctionInfo *RVFI = MF.getInfo<RISCVMachineFunctionInfo>(); + + // Offset of the first variable argument from stack pointer, and size of + // the vararg save area. For now, the varargs save area is either zero or + // large enough to hold a0-a7. + int VaArgOffset, VarArgsSaveSize; + + // If all registers are allocated, then all varargs must be passed on the + // stack and we don't need to save any argregs. + if (ArgRegs.size() == Idx) { + VaArgOffset = CCInfo.getNextStackOffset(); + VarArgsSaveSize = 0; + } else { + VarArgsSaveSize = XLenInBytes * (ArgRegs.size() - Idx); + VaArgOffset = -VarArgsSaveSize; + } + + // Record the frame index of the first variable argument + // which is a value necessary to VASTART. + int FI = MFI.CreateFixedObject(XLenInBytes, VaArgOffset, true); + RVFI->setVarArgsFrameIndex(FI); + + // If saving an odd number of registers then create an extra stack slot to + // ensure that the frame pointer is 2*XLEN-aligned, which in turn ensures + // offsets to even-numbered registered remain 2*XLEN-aligned. + if (Idx % 2) { + FI = MFI.CreateFixedObject(XLenInBytes, VaArgOffset - (int)XLenInBytes, + true); + VarArgsSaveSize += XLenInBytes; + } + + // Copy the integer registers that may have been used for passing varargs + // to the vararg save area. + for (unsigned I = Idx; I < ArgRegs.size(); + ++I, VaArgOffset += XLenInBytes) { + const unsigned Reg = RegInfo.createVirtualRegister(RC); + RegInfo.addLiveIn(ArgRegs[I], Reg); + SDValue ArgValue = DAG.getCopyFromReg(Chain, DL, Reg, XLenVT); + FI = MFI.CreateFixedObject(XLenInBytes, VaArgOffset, true); + SDValue PtrOff = DAG.getFrameIndex(FI, getPointerTy(DAG.getDataLayout())); + SDValue Store = DAG.getStore(Chain, DL, ArgValue, PtrOff, + MachinePointerInfo::getFixedStack(MF, FI)); + cast<StoreSDNode>(Store.getNode()) + ->getMemOperand() + ->setValue((Value *)nullptr); + OutChains.push_back(Store); + } + RVFI->setVarArgsSaveSize(VarArgsSaveSize); + } + + // All stores are grouped in one node to allow the matching between + // the size of Ins and InVals. This only happens for vararg functions. + if (!OutChains.empty()) { + OutChains.push_back(Chain); + Chain = DAG.getNode(ISD::TokenFactor, DL, MVT::Other, OutChains); + } + return Chain; } +/// IsEligibleForTailCallOptimization - Check whether the call is eligible +/// for tail call optimization. +/// Note: This is modelled after ARM's IsEligibleForTailCallOptimization. +bool RISCVTargetLowering::IsEligibleForTailCallOptimization( + CCState &CCInfo, CallLoweringInfo &CLI, MachineFunction &MF, + const SmallVector<CCValAssign, 16> &ArgLocs) const { + + auto &Callee = CLI.Callee; + auto CalleeCC = CLI.CallConv; + auto IsVarArg = CLI.IsVarArg; + auto &Outs = CLI.Outs; + auto &Caller = MF.getFunction(); + auto CallerCC = Caller.getCallingConv(); + + // Do not tail call opt functions with "disable-tail-calls" attribute. + if (Caller.getFnAttribute("disable-tail-calls").getValueAsString() == "true") + return false; + + // Exception-handling functions need a special set of instructions to + // indicate a return to the hardware. Tail-calling another function would + // probably break this. + // TODO: The "interrupt" attribute isn't currently defined by RISC-V. This + // should be expanded as new function attributes are introduced. + if (Caller.hasFnAttribute("interrupt")) + return false; + + // Do not tail call opt functions with varargs. + if (IsVarArg) + return false; + + // Do not tail call opt if the stack is used to pass parameters. + if (CCInfo.getNextStackOffset() != 0) + return false; + + // Do not tail call opt if any parameters need to be passed indirectly. + // Since long doubles (fp128) and i128 are larger than 2*XLEN, they are + // passed indirectly. So the address of the value will be passed in a + // register, or if not available, then the address is put on the stack. In + // order to pass indirectly, space on the stack often needs to be allocated + // in order to store the value. In this case the CCInfo.getNextStackOffset() + // != 0 check is not enough and we need to check if any CCValAssign ArgsLocs + // are passed CCValAssign::Indirect. + for (auto &VA : ArgLocs) + if (VA.getLocInfo() == CCValAssign::Indirect) + return false; + + // Do not tail call opt if either caller or callee uses struct return + // semantics. + auto IsCallerStructRet = Caller.hasStructRetAttr(); + auto IsCalleeStructRet = Outs.empty() ? false : Outs[0].Flags.isSRet(); + if (IsCallerStructRet || IsCalleeStructRet) + return false; + + // Externally-defined functions with weak linkage should not be + // tail-called. The behaviour of branch instructions in this situation (as + // used for tail calls) is implementation-defined, so we cannot rely on the + // linker replacing the tail call with a return. + if (GlobalAddressSDNode *G = dyn_cast<GlobalAddressSDNode>(Callee)) { + const GlobalValue *GV = G->getGlobal(); + if (GV->hasExternalWeakLinkage()) + return false; + } + + // The callee has to preserve all registers the caller needs to preserve. + const RISCVRegisterInfo *TRI = Subtarget.getRegisterInfo(); + const uint32_t *CallerPreserved = TRI->getCallPreservedMask(MF, CallerCC); + if (CalleeCC != CallerCC) { + const uint32_t *CalleePreserved = TRI->getCallPreservedMask(MF, CalleeCC); + if (!TRI->regmaskSubsetEqual(CallerPreserved, CalleePreserved)) + return false; + } + + // Byval parameters hand the function a pointer directly into the stack area + // we want to reuse during a tail call. Working around this *is* possible + // but less efficient and uglier in LowerCall. + for (auto &Arg : Outs) + if (Arg.Flags.isByVal()) + return false; + + return true; +} + // Lower a call to a callseq_start + CALL + callseq_end chain, and add input // and output parameter nodes. SDValue RISCVTargetLowering::LowerCall(CallLoweringInfo &CLI, @@ -635,22 +1187,29 @@ SDValue RISCVTargetLowering::LowerCall(CallLoweringInfo &CLI, SmallVectorImpl<ISD::InputArg> &Ins = CLI.Ins; SDValue Chain = CLI.Chain; SDValue Callee = CLI.Callee; - CLI.IsTailCall = false; + bool &IsTailCall = CLI.IsTailCall; 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); + analyzeOutputArgs(MF, ArgCCInfo, Outs, /*IsRet=*/false, &CLI); + + // Check if it's really possible to do a tail call. + if (IsTailCall) + IsTailCall = IsEligibleForTailCallOptimization(ArgCCInfo, CLI, MF, + ArgLocs); + + if (IsTailCall) + ++NumTailCalls; + else if (CLI.CS && CLI.CS.isMustTailCall()) + report_fatal_error("failed to perform tail call elimination on a call " + "site marked musttail"); // Get a count of how many bytes are to be pushed on the stack. unsigned NumBytes = ArgCCInfo.getNextStackOffset(); @@ -673,12 +1232,13 @@ SDValue RISCVTargetLowering::LowerCall(CallLoweringInfo &CLI, Chain = DAG.getMemcpy(Chain, DL, FIPtr, Arg, SizeNode, Align, /*IsVolatile=*/false, /*AlwaysInline=*/false, - /*isTailCall=*/false, MachinePointerInfo(), + IsTailCall, MachinePointerInfo(), MachinePointerInfo()); ByValArgs.push_back(FIPtr); } - Chain = DAG.getCALLSEQ_START(Chain, NumBytes, 0, CLI.DL); + if (!IsTailCall) + Chain = DAG.getCALLSEQ_START(Chain, NumBytes, 0, CLI.DL); // Copy argument values to their designated locations. SmallVector<std::pair<unsigned, SDValue>, 8> RegsToPass; @@ -689,11 +1249,45 @@ SDValue RISCVTargetLowering::LowerCall(CallLoweringInfo &CLI, SDValue ArgValue = OutVals[i]; ISD::ArgFlagsTy Flags = Outs[i].Flags; + // Handle passing f64 on RV32D with a soft float ABI as a special case. + bool IsF64OnRV32DSoftABI = + VA.getLocVT() == MVT::i32 && VA.getValVT() == MVT::f64; + if (IsF64OnRV32DSoftABI && VA.isRegLoc()) { + SDValue SplitF64 = DAG.getNode( + RISCVISD::SplitF64, DL, DAG.getVTList(MVT::i32, MVT::i32), ArgValue); + SDValue Lo = SplitF64.getValue(0); + SDValue Hi = SplitF64.getValue(1); + + unsigned RegLo = VA.getLocReg(); + RegsToPass.push_back(std::make_pair(RegLo, Lo)); + + if (RegLo == RISCV::X17) { + // Second half of f64 is passed on the stack. + // Work out the address of the stack slot. + if (!StackPtr.getNode()) + StackPtr = DAG.getCopyFromReg(Chain, DL, RISCV::X2, PtrVT); + // Emit the store. + MemOpChains.push_back( + DAG.getStore(Chain, DL, Hi, StackPtr, MachinePointerInfo())); + } else { + // Second half of f64 is passed in another GPR. + unsigned RegHigh = RegLo + 1; + RegsToPass.push_back(std::make_pair(RegHigh, Hi)); + } + continue; + } + + // IsF64OnRV32DSoftABI && VA.isMemLoc() is handled below in the same way + // as any other MemLoc. + // Promote the value if needed. // For now, only handle fully promoted and indirect arguments. switch (VA.getLocInfo()) { case CCValAssign::Full: break; + case CCValAssign::BCvt: + ArgValue = DAG.getNode(ISD::BITCAST, DL, VA.getLocVT(), ArgValue); + break; case CCValAssign::Indirect: { // Store the argument in a stack slot and pass its address. SDValue SpillSlot = DAG.CreateStackTemporary(Outs[i].ArgVT); @@ -731,6 +1325,8 @@ SDValue RISCVTargetLowering::LowerCall(CallLoweringInfo &CLI, RegsToPass.push_back(std::make_pair(VA.getLocReg(), ArgValue)); } else { assert(VA.isMemLoc() && "Argument not register or memory"); + assert(!IsTailCall && "Tail call not allowed if stack is used " + "for passing parameters"); // Work out the address of the stack slot. if (!StackPtr.getNode()) @@ -757,10 +1353,13 @@ SDValue RISCVTargetLowering::LowerCall(CallLoweringInfo &CLI, Glue = Chain.getValue(1); } - if (isa<GlobalAddressSDNode>(Callee)) { - Callee = lowerGlobalAddress(Callee, DAG); - } else if (isa<ExternalSymbolSDNode>(Callee)) { - Callee = lowerExternalSymbol(Callee, DAG); + // If the callee is a GlobalAddress/ExternalSymbol node, turn it into a + // TargetGlobalAddress/TargetExternalSymbol node so that legalize won't + // split it and then direct call can be matched by PseudoCALL. + if (GlobalAddressSDNode *S = dyn_cast<GlobalAddressSDNode>(Callee)) { + Callee = DAG.getTargetGlobalAddress(S->getGlobal(), DL, PtrVT, 0, 0); + } else if (ExternalSymbolSDNode *S = dyn_cast<ExternalSymbolSDNode>(Callee)) { + Callee = DAG.getTargetExternalSymbol(S->getSymbol(), PtrVT, 0); } // The first call operand is the chain and the second is the target address. @@ -773,11 +1372,13 @@ SDValue RISCVTargetLowering::LowerCall(CallLoweringInfo &CLI, 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)); + if (!IsTailCall) { + // 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()) @@ -785,6 +1386,12 @@ SDValue RISCVTargetLowering::LowerCall(CallLoweringInfo &CLI, // Emit the call. SDVTList NodeTys = DAG.getVTList(MVT::Other, MVT::Glue); + + if (IsTailCall) { + MF.getFrameInfo().setHasTailCall(); + return DAG.getNode(RISCVISD::TAIL, DL, NodeTys, Ops); + } + Chain = DAG.getNode(RISCVISD::CALL, DL, NodeTys, Ops); Glue = Chain.getValue(1); @@ -802,13 +1409,32 @@ SDValue RISCVTargetLowering::LowerCall(CallLoweringInfo &CLI, // 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); + // Copy the value out + SDValue RetValue = + DAG.getCopyFromReg(Chain, DL, VA.getLocReg(), VA.getLocVT(), Glue); + // Glue the RetValue to the end of the call sequence Chain = RetValue.getValue(1); Glue = RetValue.getValue(2); + if (VA.getLocVT() == MVT::i32 && VA.getValVT() == MVT::f64) { + assert(VA.getLocReg() == ArgGPRs[0] && "Unexpected reg assignment"); + SDValue RetValue2 = + DAG.getCopyFromReg(Chain, DL, ArgGPRs[1], MVT::i32, Glue); + Chain = RetValue2.getValue(1); + Glue = RetValue2.getValue(2); + RetValue = DAG.getNode(RISCVISD::BuildPairF64, DL, MVT::f64, RetValue, + RetValue2); + } + + switch (VA.getLocInfo()) { + default: + llvm_unreachable("Unknown loc info!"); + case CCValAssign::Full: + break; + case CCValAssign::BCvt: + RetValue = DAG.getNode(ISD::BITCAST, DL, VA.getValVT(), RetValue); + break; + } - assert(VA.getLocInfo() == CCValAssign::Full && "Unknown loc info!"); InVals.push_back(RetValue); } @@ -824,22 +1450,34 @@ bool RISCVTargetLowering::CanLowerReturn( 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)) + CCInfo, /*IsFixed=*/true, /*IsRet=*/true, nullptr)) return false; } return true; } +static SDValue packIntoRegLoc(SelectionDAG &DAG, SDValue Val, + const CCValAssign &VA, const SDLoc &DL) { + EVT LocVT = VA.getLocVT(); + + switch (VA.getLocInfo()) { + default: + llvm_unreachable("Unexpected CCValAssign::LocInfo"); + case CCValAssign::Full: + break; + case CCValAssign::BCvt: + Val = DAG.getNode(ISD::BITCAST, DL, LocVT, Val); + break; + } + return Val; +} + 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; @@ -847,9 +1485,10 @@ RISCVTargetLowering::LowerReturn(SDValue Chain, CallingConv::ID CallConv, CCState CCInfo(CallConv, IsVarArg, DAG.getMachineFunction(), RVLocs, *DAG.getContext()); - analyzeOutputArgs(DAG.getMachineFunction(), CCInfo, Outs, /*IsRet=*/true); + analyzeOutputArgs(DAG.getMachineFunction(), CCInfo, Outs, /*IsRet=*/true, + nullptr); - SDValue Flag; + SDValue Glue; SmallVector<SDValue, 4> RetOps(1, Chain); // Copy the result values into the output registers. @@ -857,21 +1496,60 @@ RISCVTargetLowering::LowerReturn(SDValue Chain, CallingConv::ID CallConv, 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); + if (VA.getLocVT() == MVT::i32 && VA.getValVT() == MVT::f64) { + // Handle returning f64 on RV32D with a soft float ABI. + assert(VA.isRegLoc() && "Expected return via registers"); + SDValue SplitF64 = DAG.getNode(RISCVISD::SplitF64, DL, + DAG.getVTList(MVT::i32, MVT::i32), Val); + SDValue Lo = SplitF64.getValue(0); + SDValue Hi = SplitF64.getValue(1); + unsigned RegLo = VA.getLocReg(); + unsigned RegHi = RegLo + 1; + Chain = DAG.getCopyToReg(Chain, DL, RegLo, Lo, Glue); + Glue = Chain.getValue(1); + RetOps.push_back(DAG.getRegister(RegLo, MVT::i32)); + Chain = DAG.getCopyToReg(Chain, DL, RegHi, Hi, Glue); + Glue = Chain.getValue(1); + RetOps.push_back(DAG.getRegister(RegHi, MVT::i32)); + } else { + // Handle a 'normal' return. + Val = packIntoRegLoc(DAG, Val, VA, DL); + Chain = DAG.getCopyToReg(Chain, DL, VA.getLocReg(), Val, Glue); - // Guarantee that all emitted copies are stuck together. - Flag = Chain.getValue(1); - RetOps.push_back(DAG.getRegister(VA.getLocReg(), VA.getLocVT())); + // Guarantee that all emitted copies are stuck together. + Glue = 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); + // Add the glue node if we have it. + if (Glue.getNode()) { + RetOps.push_back(Glue); + } + + // Interrupt service routines use different return instructions. + const Function &Func = DAG.getMachineFunction().getFunction(); + if (Func.hasFnAttribute("interrupt")) { + if (!Func.getReturnType()->isVoidTy()) + report_fatal_error( + "Functions with the interrupt attribute must have void return type!"); + + MachineFunction &MF = DAG.getMachineFunction(); + StringRef Kind = + MF.getFunction().getFnAttribute("interrupt").getValueAsString(); + + unsigned RetOpc; + if (Kind == "user") + RetOpc = RISCVISD::URET_FLAG; + else if (Kind == "supervisor") + RetOpc = RISCVISD::SRET_FLAG; + else + RetOpc = RISCVISD::MRET_FLAG; + + return DAG.getNode(RetOpc, DL, MVT::Other, RetOps); } return DAG.getNode(RISCVISD::RET_FLAG, DL, MVT::Other, RetOps); @@ -883,10 +1561,58 @@ const char *RISCVTargetLowering::getTargetNodeName(unsigned Opcode) const { break; case RISCVISD::RET_FLAG: return "RISCVISD::RET_FLAG"; + case RISCVISD::URET_FLAG: + return "RISCVISD::URET_FLAG"; + case RISCVISD::SRET_FLAG: + return "RISCVISD::SRET_FLAG"; + case RISCVISD::MRET_FLAG: + return "RISCVISD::MRET_FLAG"; case RISCVISD::CALL: return "RISCVISD::CALL"; case RISCVISD::SELECT_CC: return "RISCVISD::SELECT_CC"; + case RISCVISD::BuildPairF64: + return "RISCVISD::BuildPairF64"; + case RISCVISD::SplitF64: + return "RISCVISD::SplitF64"; + case RISCVISD::TAIL: + return "RISCVISD::TAIL"; } return nullptr; } + +std::pair<unsigned, const TargetRegisterClass *> +RISCVTargetLowering::getRegForInlineAsmConstraint(const TargetRegisterInfo *TRI, + StringRef Constraint, + MVT VT) const { + // First, see if this is a constraint that directly corresponds to a + // RISCV register class. + if (Constraint.size() == 1) { + switch (Constraint[0]) { + case 'r': + return std::make_pair(0U, &RISCV::GPRRegClass); + default: + break; + } + } + + return TargetLowering::getRegForInlineAsmConstraint(TRI, Constraint, VT); +} + +Instruction *RISCVTargetLowering::emitLeadingFence(IRBuilder<> &Builder, + Instruction *Inst, + AtomicOrdering Ord) const { + if (isa<LoadInst>(Inst) && Ord == AtomicOrdering::SequentiallyConsistent) + return Builder.CreateFence(Ord); + if (isa<StoreInst>(Inst) && isReleaseOrStronger(Ord)) + return Builder.CreateFence(AtomicOrdering::Release); + return nullptr; +} + +Instruction *RISCVTargetLowering::emitTrailingFence(IRBuilder<> &Builder, + Instruction *Inst, + AtomicOrdering Ord) const { + if (isa<LoadInst>(Inst) && isAcquireOrStronger(Ord)) + return Builder.CreateFence(AtomicOrdering::Acquire); + return nullptr; +} diff --git a/lib/Target/RISCV/RISCVISelLowering.h b/lib/Target/RISCV/RISCVISelLowering.h index 9c5c7ca008c0..280adb29fd02 100644 --- a/lib/Target/RISCV/RISCVISelLowering.h +++ b/lib/Target/RISCV/RISCVISelLowering.h @@ -25,8 +25,14 @@ namespace RISCVISD { enum NodeType : unsigned { FIRST_NUMBER = ISD::BUILTIN_OP_END, RET_FLAG, + URET_FLAG, + SRET_FLAG, + MRET_FLAG, CALL, - SELECT_CC + SELECT_CC, + BuildPairF64, + SplitF64, + TAIL }; } @@ -37,23 +43,47 @@ public: explicit RISCVTargetLowering(const TargetMachine &TM, const RISCVSubtarget &STI); + bool isLegalAddressingMode(const DataLayout &DL, const AddrMode &AM, Type *Ty, + unsigned AS, + Instruction *I = nullptr) const override; + bool isLegalICmpImmediate(int64_t Imm) const override; + bool isLegalAddImmediate(int64_t Imm) const override; + bool isTruncateFree(Type *SrcTy, Type *DstTy) const override; + bool isTruncateFree(EVT SrcVT, EVT DstVT) const override; + bool isZExtFree(SDValue Val, EVT VT2) const override; + // 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; + std::pair<unsigned, const TargetRegisterClass *> + getRegForInlineAsmConstraint(const TargetRegisterInfo *TRI, + StringRef Constraint, MVT VT) const override; + MachineBasicBlock * EmitInstrWithCustomInserter(MachineInstr &MI, MachineBasicBlock *BB) const override; + EVT getSetCCResultType(const DataLayout &DL, LLVMContext &Context, + EVT VT) const override; + + bool shouldInsertFencesForAtomic(const Instruction *I) const override { + return isa<LoadInst>(I) || isa<StoreInst>(I); + } + Instruction *emitLeadingFence(IRBuilder<> &Builder, Instruction *Inst, + AtomicOrdering Ord) const override; + Instruction *emitTrailingFence(IRBuilder<> &Builder, Instruction *Inst, + AtomicOrdering Ord) const override; + private: void analyzeInputArgs(MachineFunction &MF, CCState &CCInfo, const SmallVectorImpl<ISD::InputArg> &Ins, bool IsRet) const; void analyzeOutputArgs(MachineFunction &MF, CCState &CCInfo, const SmallVectorImpl<ISD::OutputArg> &Outs, - bool IsRet) const; + bool IsRet, CallLoweringInfo *CLI) const; // Lower incoming arguments, copy physregs into vregs SDValue LowerFormalArguments(SDValue Chain, CallingConv::ID CallConv, bool IsVarArg, @@ -76,8 +106,16 @@ private: } SDValue lowerGlobalAddress(SDValue Op, SelectionDAG &DAG) const; SDValue lowerBlockAddress(SDValue Op, SelectionDAG &DAG) const; + SDValue lowerConstantPool(SDValue Op, SelectionDAG &DAG) const; SDValue lowerExternalSymbol(SDValue Op, SelectionDAG &DAG) const; SDValue lowerSELECT(SDValue Op, SelectionDAG &DAG) const; + SDValue lowerVASTART(SDValue Op, SelectionDAG &DAG) const; + SDValue LowerFRAMEADDR(SDValue Op, SelectionDAG &DAG) const; + SDValue LowerRETURNADDR(SDValue Op, SelectionDAG &DAG) const; + + bool IsEligibleForTailCallOptimization(CCState &CCInfo, + CallLoweringInfo &CLI, MachineFunction &MF, + const SmallVector<CCValAssign, 16> &ArgLocs) const; }; } diff --git a/lib/Target/RISCV/RISCVInstrFormats.td b/lib/Target/RISCV/RISCVInstrFormats.td index 7479ffbc9532..529e048045c6 100644 --- a/lib/Target/RISCV/RISCVInstrFormats.td +++ b/lib/Target/RISCV/RISCVInstrFormats.td @@ -102,8 +102,8 @@ class RVInst<dag outs, dag ins, string opcodestr, string argstr, } // Pseudo instructions -class Pseudo<dag outs, dag ins, list<dag> pattern> - : RVInst<outs, ins, "", "", pattern, InstFormatPseudo> { +class Pseudo<dag outs, dag ins, list<dag> pattern, string opcodestr = "", string argstr = ""> + : RVInst<outs, ins, opcodestr, argstr, pattern, InstFormatPseudo> { let isPseudo = 1; let isCodeGenOnly = 1; } diff --git a/lib/Target/RISCV/RISCVInstrInfo.cpp b/lib/Target/RISCV/RISCVInstrInfo.cpp index 186fe363edd9..327e4a7d615f 100644 --- a/lib/Target/RISCV/RISCVInstrInfo.cpp +++ b/lib/Target/RISCV/RISCVInstrInfo.cpp @@ -20,6 +20,7 @@ #include "llvm/CodeGen/MachineFunctionPass.h" #include "llvm/CodeGen/MachineInstrBuilder.h" #include "llvm/CodeGen/MachineRegisterInfo.h" +#include "llvm/CodeGen/RegisterScavenging.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/TargetRegistry.h" @@ -31,16 +32,78 @@ using namespace llvm; RISCVInstrInfo::RISCVInstrInfo() : RISCVGenInstrInfo(RISCV::ADJCALLSTACKDOWN, RISCV::ADJCALLSTACKUP) {} +unsigned RISCVInstrInfo::isLoadFromStackSlot(const MachineInstr &MI, + int &FrameIndex) const { + switch (MI.getOpcode()) { + default: + return 0; + case RISCV::LB: + case RISCV::LBU: + case RISCV::LH: + case RISCV::LHU: + case RISCV::LW: + case RISCV::FLW: + case RISCV::LWU: + case RISCV::LD: + case RISCV::FLD: + break; + } + + if (MI.getOperand(1).isFI() && MI.getOperand(2).isImm() && + MI.getOperand(2).getImm() == 0) { + FrameIndex = MI.getOperand(1).getIndex(); + return MI.getOperand(0).getReg(); + } + + return 0; +} + +unsigned RISCVInstrInfo::isStoreToStackSlot(const MachineInstr &MI, + int &FrameIndex) const { + switch (MI.getOpcode()) { + default: + return 0; + case RISCV::SB: + case RISCV::SH: + case RISCV::SW: + case RISCV::FSW: + case RISCV::SD: + case RISCV::FSD: + break; + } + + if (MI.getOperand(0).isFI() && MI.getOperand(1).isImm() && + MI.getOperand(1).getImm() == 0) { + FrameIndex = MI.getOperand(0).getIndex(); + return MI.getOperand(2).getReg(); + } + + return 0; +} + 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"); + if (RISCV::GPRRegClass.contains(DstReg, SrcReg)) { + BuildMI(MBB, MBBI, DL, get(RISCV::ADDI), DstReg) + .addReg(SrcReg, getKillRegState(KillSrc)) + .addImm(0); + return; + } - BuildMI(MBB, MBBI, DL, get(RISCV::ADDI), DstReg) + // FPR->FPR copies + unsigned Opc; + if (RISCV::FPR32RegClass.contains(DstReg, SrcReg)) + Opc = RISCV::FSGNJ_S; + else if (RISCV::FPR64RegClass.contains(DstReg, SrcReg)) + Opc = RISCV::FSGNJ_D; + else + llvm_unreachable("Impossible reg-to-reg copy"); + + BuildMI(MBB, MBBI, DL, get(Opc), DstReg) .addReg(SrcReg, getKillRegState(KillSrc)) - .addImm(0); + .addReg(SrcReg, getKillRegState(KillSrc)); } void RISCVInstrInfo::storeRegToStackSlot(MachineBasicBlock &MBB, @@ -52,13 +115,22 @@ void RISCVInstrInfo::storeRegToStackSlot(MachineBasicBlock &MBB, if (I != MBB.end()) DL = I->getDebugLoc(); + unsigned Opcode; + if (RISCV::GPRRegClass.hasSubClassEq(RC)) - BuildMI(MBB, I, DL, get(RISCV::SW)) - .addReg(SrcReg, getKillRegState(IsKill)) - .addFrameIndex(FI) - .addImm(0); + Opcode = TRI->getRegSizeInBits(RISCV::GPRRegClass) == 32 ? + RISCV::SW : RISCV::SD; + else if (RISCV::FPR32RegClass.hasSubClassEq(RC)) + Opcode = RISCV::FSW; + else if (RISCV::FPR64RegClass.hasSubClassEq(RC)) + Opcode = RISCV::FSD; else llvm_unreachable("Can't store this register to stack slot"); + + BuildMI(MBB, I, DL, get(Opcode)) + .addReg(SrcReg, getKillRegState(IsKill)) + .addFrameIndex(FI) + .addImm(0); } void RISCVInstrInfo::loadRegFromStackSlot(MachineBasicBlock &MBB, @@ -70,8 +142,310 @@ void RISCVInstrInfo::loadRegFromStackSlot(MachineBasicBlock &MBB, if (I != MBB.end()) DL = I->getDebugLoc(); + unsigned Opcode; + if (RISCV::GPRRegClass.hasSubClassEq(RC)) - BuildMI(MBB, I, DL, get(RISCV::LW), DstReg).addFrameIndex(FI).addImm(0); + Opcode = TRI->getRegSizeInBits(RISCV::GPRRegClass) == 32 ? + RISCV::LW : RISCV::LD; + else if (RISCV::FPR32RegClass.hasSubClassEq(RC)) + Opcode = RISCV::FLW; + else if (RISCV::FPR64RegClass.hasSubClassEq(RC)) + Opcode = RISCV::FLD; else llvm_unreachable("Can't load this register from stack slot"); + + BuildMI(MBB, I, DL, get(Opcode), DstReg).addFrameIndex(FI).addImm(0); +} + +void RISCVInstrInfo::movImm32(MachineBasicBlock &MBB, + MachineBasicBlock::iterator MBBI, + const DebugLoc &DL, unsigned DstReg, uint64_t Val, + MachineInstr::MIFlag Flag) const { + assert(isInt<32>(Val) && "Can only materialize 32-bit constants"); + + // TODO: If the value can be materialized using only one instruction, only + // insert a single instruction. + + uint64_t Hi20 = ((Val + 0x800) >> 12) & 0xfffff; + uint64_t Lo12 = SignExtend64<12>(Val); + BuildMI(MBB, MBBI, DL, get(RISCV::LUI), DstReg) + .addImm(Hi20) + .setMIFlag(Flag); + BuildMI(MBB, MBBI, DL, get(RISCV::ADDI), DstReg) + .addReg(DstReg, RegState::Kill) + .addImm(Lo12) + .setMIFlag(Flag); +} + +// The contents of values added to Cond are not examined outside of +// RISCVInstrInfo, giving us flexibility in what to push to it. For RISCV, we +// push BranchOpcode, Reg1, Reg2. +static void parseCondBranch(MachineInstr &LastInst, MachineBasicBlock *&Target, + SmallVectorImpl<MachineOperand> &Cond) { + // Block ends with fall-through condbranch. + assert(LastInst.getDesc().isConditionalBranch() && + "Unknown conditional branch"); + Target = LastInst.getOperand(2).getMBB(); + Cond.push_back(MachineOperand::CreateImm(LastInst.getOpcode())); + Cond.push_back(LastInst.getOperand(0)); + Cond.push_back(LastInst.getOperand(1)); +} + +static unsigned getOppositeBranchOpcode(int Opc) { + switch (Opc) { + default: + llvm_unreachable("Unrecognized conditional branch"); + case RISCV::BEQ: + return RISCV::BNE; + case RISCV::BNE: + return RISCV::BEQ; + case RISCV::BLT: + return RISCV::BGE; + case RISCV::BGE: + return RISCV::BLT; + case RISCV::BLTU: + return RISCV::BGEU; + case RISCV::BGEU: + return RISCV::BLTU; + } +} + +bool RISCVInstrInfo::analyzeBranch(MachineBasicBlock &MBB, + MachineBasicBlock *&TBB, + MachineBasicBlock *&FBB, + SmallVectorImpl<MachineOperand> &Cond, + bool AllowModify) const { + TBB = FBB = nullptr; + Cond.clear(); + + // If the block has no terminators, it just falls into the block after it. + MachineBasicBlock::iterator I = MBB.getLastNonDebugInstr(); + if (I == MBB.end() || !isUnpredicatedTerminator(*I)) + return false; + + // Count the number of terminators and find the first unconditional or + // indirect branch. + MachineBasicBlock::iterator FirstUncondOrIndirectBr = MBB.end(); + int NumTerminators = 0; + for (auto J = I.getReverse(); J != MBB.rend() && isUnpredicatedTerminator(*J); + J++) { + NumTerminators++; + if (J->getDesc().isUnconditionalBranch() || + J->getDesc().isIndirectBranch()) { + FirstUncondOrIndirectBr = J.getReverse(); + } + } + + // If AllowModify is true, we can erase any terminators after + // FirstUncondOrIndirectBR. + if (AllowModify && FirstUncondOrIndirectBr != MBB.end()) { + while (std::next(FirstUncondOrIndirectBr) != MBB.end()) { + std::next(FirstUncondOrIndirectBr)->eraseFromParent(); + NumTerminators--; + } + I = FirstUncondOrIndirectBr; + } + + // We can't handle blocks that end in an indirect branch. + if (I->getDesc().isIndirectBranch()) + return true; + + // We can't handle blocks with more than 2 terminators. + if (NumTerminators > 2) + return true; + + // Handle a single unconditional branch. + if (NumTerminators == 1 && I->getDesc().isUnconditionalBranch()) { + TBB = I->getOperand(0).getMBB(); + return false; + } + + // Handle a single conditional branch. + if (NumTerminators == 1 && I->getDesc().isConditionalBranch()) { + parseCondBranch(*I, TBB, Cond); + return false; + } + + // Handle a conditional branch followed by an unconditional branch. + if (NumTerminators == 2 && std::prev(I)->getDesc().isConditionalBranch() && + I->getDesc().isUnconditionalBranch()) { + parseCondBranch(*std::prev(I), TBB, Cond); + FBB = I->getOperand(0).getMBB(); + return false; + } + + // Otherwise, we can't handle this. + return true; +} + +unsigned RISCVInstrInfo::removeBranch(MachineBasicBlock &MBB, + int *BytesRemoved) const { + if (BytesRemoved) + *BytesRemoved = 0; + MachineBasicBlock::iterator I = MBB.getLastNonDebugInstr(); + if (I == MBB.end()) + return 0; + + if (!I->getDesc().isUnconditionalBranch() && + !I->getDesc().isConditionalBranch()) + return 0; + + // Remove the branch. + I->eraseFromParent(); + if (BytesRemoved) + *BytesRemoved += getInstSizeInBytes(*I); + + I = MBB.end(); + + if (I == MBB.begin()) + return 1; + --I; + if (!I->getDesc().isConditionalBranch()) + return 1; + + // Remove the branch. + I->eraseFromParent(); + if (BytesRemoved) + *BytesRemoved += getInstSizeInBytes(*I); + return 2; +} + +// Inserts a branch into the end of the specific MachineBasicBlock, returning +// the number of instructions inserted. +unsigned RISCVInstrInfo::insertBranch( + MachineBasicBlock &MBB, MachineBasicBlock *TBB, MachineBasicBlock *FBB, + ArrayRef<MachineOperand> Cond, const DebugLoc &DL, int *BytesAdded) const { + if (BytesAdded) + *BytesAdded = 0; + + // Shouldn't be a fall through. + assert(TBB && "InsertBranch must not be told to insert a fallthrough"); + assert((Cond.size() == 3 || Cond.size() == 0) && + "RISCV branch conditions have two components!"); + + // Unconditional branch. + if (Cond.empty()) { + MachineInstr &MI = *BuildMI(&MBB, DL, get(RISCV::PseudoBR)).addMBB(TBB); + if (BytesAdded) + *BytesAdded += getInstSizeInBytes(MI); + return 1; + } + + // Either a one or two-way conditional branch. + unsigned Opc = Cond[0].getImm(); + MachineInstr &CondMI = + *BuildMI(&MBB, DL, get(Opc)).add(Cond[1]).add(Cond[2]).addMBB(TBB); + if (BytesAdded) + *BytesAdded += getInstSizeInBytes(CondMI); + + // One-way conditional branch. + if (!FBB) + return 1; + + // Two-way conditional branch. + MachineInstr &MI = *BuildMI(&MBB, DL, get(RISCV::PseudoBR)).addMBB(FBB); + if (BytesAdded) + *BytesAdded += getInstSizeInBytes(MI); + return 2; +} + +unsigned RISCVInstrInfo::insertIndirectBranch(MachineBasicBlock &MBB, + MachineBasicBlock &DestBB, + const DebugLoc &DL, + int64_t BrOffset, + RegScavenger *RS) const { + assert(RS && "RegScavenger required for long branching"); + assert(MBB.empty() && + "new block should be inserted for expanding unconditional branch"); + assert(MBB.pred_size() == 1); + + MachineFunction *MF = MBB.getParent(); + MachineRegisterInfo &MRI = MF->getRegInfo(); + const auto &TM = static_cast<const RISCVTargetMachine &>(MF->getTarget()); + const auto &STI = MF->getSubtarget<RISCVSubtarget>(); + + if (TM.isPositionIndependent() || STI.is64Bit()) + report_fatal_error("Unable to insert indirect branch"); + + if (!isInt<32>(BrOffset)) + report_fatal_error( + "Branch offsets outside of the signed 32-bit range not supported"); + + // FIXME: A virtual register must be used initially, as the register + // scavenger won't work with empty blocks (SIInstrInfo::insertIndirectBranch + // uses the same workaround). + unsigned ScratchReg = MRI.createVirtualRegister(&RISCV::GPRRegClass); + auto II = MBB.end(); + + MachineInstr &LuiMI = *BuildMI(MBB, II, DL, get(RISCV::LUI), ScratchReg) + .addMBB(&DestBB, RISCVII::MO_HI); + BuildMI(MBB, II, DL, get(RISCV::PseudoBRIND)) + .addReg(ScratchReg, RegState::Kill) + .addMBB(&DestBB, RISCVII::MO_LO); + + RS->enterBasicBlockEnd(MBB); + unsigned Scav = RS->scavengeRegisterBackwards( + RISCV::GPRRegClass, MachineBasicBlock::iterator(LuiMI), false, 0); + MRI.replaceRegWith(ScratchReg, Scav); + MRI.clearVirtRegs(); + RS->setRegUsed(Scav); + return 8; +} + +bool RISCVInstrInfo::reverseBranchCondition( + SmallVectorImpl<MachineOperand> &Cond) const { + assert((Cond.size() == 3) && "Invalid branch condition!"); + Cond[0].setImm(getOppositeBranchOpcode(Cond[0].getImm())); + return false; +} + +MachineBasicBlock * +RISCVInstrInfo::getBranchDestBlock(const MachineInstr &MI) const { + assert(MI.getDesc().isBranch() && "Unexpected opcode!"); + // The branch target is always the last operand. + int NumOp = MI.getNumExplicitOperands(); + return MI.getOperand(NumOp - 1).getMBB(); +} + +bool RISCVInstrInfo::isBranchOffsetInRange(unsigned BranchOp, + int64_t BrOffset) const { + // Ideally we could determine the supported branch offset from the + // RISCVII::FormMask, but this can't be used for Pseudo instructions like + // PseudoBR. + switch (BranchOp) { + default: + llvm_unreachable("Unexpected opcode!"); + case RISCV::BEQ: + case RISCV::BNE: + case RISCV::BLT: + case RISCV::BGE: + case RISCV::BLTU: + case RISCV::BGEU: + return isIntN(13, BrOffset); + case RISCV::JAL: + case RISCV::PseudoBR: + return isIntN(21, BrOffset); + } +} + +unsigned RISCVInstrInfo::getInstSizeInBytes(const MachineInstr &MI) const { + unsigned Opcode = MI.getOpcode(); + + switch (Opcode) { + default: { return get(Opcode).getSize(); } + case TargetOpcode::EH_LABEL: + case TargetOpcode::IMPLICIT_DEF: + case TargetOpcode::KILL: + case TargetOpcode::DBG_VALUE: + return 0; + case RISCV::PseudoCALL: + case RISCV::PseudoTAIL: + return 8; + case TargetOpcode::INLINEASM: { + const MachineFunction &MF = *MI.getParent()->getParent(); + const auto &TM = static_cast<const RISCVTargetMachine &>(MF.getTarget()); + return getInlineAsmLength(MI.getOperand(0).getSymbolName(), + *TM.getMCAsmInfo()); + } + } } diff --git a/lib/Target/RISCV/RISCVInstrInfo.h b/lib/Target/RISCV/RISCVInstrInfo.h index 05c8378445cf..1d3279c3d31e 100644 --- a/lib/Target/RISCV/RISCVInstrInfo.h +++ b/lib/Target/RISCV/RISCVInstrInfo.h @@ -27,6 +27,11 @@ class RISCVInstrInfo : public RISCVGenInstrInfo { public: RISCVInstrInfo(); + unsigned isLoadFromStackSlot(const MachineInstr &MI, + int &FrameIndex) const override; + unsigned isStoreToStackSlot(const MachineInstr &MI, + int &FrameIndex) const override; + void copyPhysReg(MachineBasicBlock &MBB, MachineBasicBlock::iterator MBBI, const DebugLoc &DL, unsigned DstReg, unsigned SrcReg, bool KillSrc) const override; @@ -41,6 +46,39 @@ public: MachineBasicBlock::iterator MBBI, unsigned DstReg, int FrameIndex, const TargetRegisterClass *RC, const TargetRegisterInfo *TRI) const override; + + // Materializes the given int32 Val into DstReg. + void movImm32(MachineBasicBlock &MBB, MachineBasicBlock::iterator MBBI, + const DebugLoc &DL, unsigned DstReg, uint64_t Val, + MachineInstr::MIFlag Flag = MachineInstr::NoFlags) const; + + unsigned getInstSizeInBytes(const MachineInstr &MI) const override; + + bool analyzeBranch(MachineBasicBlock &MBB, MachineBasicBlock *&TBB, + MachineBasicBlock *&FBB, + SmallVectorImpl<MachineOperand> &Cond, + bool AllowModify) const override; + + unsigned insertBranch(MachineBasicBlock &MBB, MachineBasicBlock *TBB, + MachineBasicBlock *FBB, ArrayRef<MachineOperand> Cond, + const DebugLoc &dl, + int *BytesAdded = nullptr) const override; + + unsigned insertIndirectBranch(MachineBasicBlock &MBB, + MachineBasicBlock &NewDestBB, + const DebugLoc &DL, int64_t BrOffset, + RegScavenger *RS = nullptr) const override; + + unsigned removeBranch(MachineBasicBlock &MBB, + int *BytesRemoved = nullptr) const override; + + bool + reverseBranchCondition(SmallVectorImpl<MachineOperand> &Cond) const override; + + MachineBasicBlock *getBranchDestBlock(const MachineInstr &MI) const override; + + bool isBranchOffsetInRange(unsigned BranchOpc, + int64_t BrOffset) const override; }; } #endif diff --git a/lib/Target/RISCV/RISCVInstrInfo.td b/lib/Target/RISCV/RISCVInstrInfo.td index 1aae2f39dbdd..b51e4e70330d 100644 --- a/lib/Target/RISCV/RISCVInstrInfo.td +++ b/lib/Target/RISCV/RISCVInstrInfo.td @@ -36,13 +36,28 @@ def CallSeqEnd : SDNode<"ISD::CALLSEQ_END", SDT_RISCVCallSeqEnd, [SDNPHasChain, SDNPOptInGlue, SDNPOutGlue]>; def RetFlag : SDNode<"RISCVISD::RET_FLAG", SDTNone, [SDNPHasChain, SDNPOptInGlue, SDNPVariadic]>; +def URetFlag : SDNode<"RISCVISD::URET_FLAG", SDTNone, + [SDNPHasChain, SDNPOptInGlue]>; +def SRetFlag : SDNode<"RISCVISD::SRET_FLAG", SDTNone, + [SDNPHasChain, SDNPOptInGlue]>; +def MRetFlag : SDNode<"RISCVISD::MRET_FLAG", SDTNone, + [SDNPHasChain, SDNPOptInGlue]>; def SelectCC : SDNode<"RISCVISD::SELECT_CC", SDT_RISCVSelectCC, [SDNPInGlue]>; +def Tail : SDNode<"RISCVISD::TAIL", SDT_RISCVCall, + [SDNPHasChain, SDNPOptInGlue, SDNPOutGlue, + SDNPVariadic]>; //===----------------------------------------------------------------------===// // Operand and SDNode transformation definitions. //===----------------------------------------------------------------------===// +class ImmXLenAsmOperand<string prefix, string suffix = ""> : AsmOperandClass { + let Name = prefix # "ImmXLen" # suffix; + let RenderMethod = "addImmOperands"; + let DiagnosticType = !strconcat("Invalid", Name); +} + class ImmAsmOperand<string prefix, int width, string suffix> : AsmOperandClass { let Name = prefix # "Imm" # width # suffix; let RenderMethod = "addImmOperands"; @@ -83,6 +98,14 @@ def uimmlog2xlen : Operand<XLenVT>, ImmLeaf<XLenVT, [{ let ParserMatchClass = UImmLog2XLenAsmOperand; // TODO: should ensure invalid shamt is rejected when decoding. let DecoderMethod = "decodeUImmOperand<6>"; + let MCOperandPredicate = [{ + int64_t Imm; + if (!MCOp.evaluateAsConstantImm(Imm)) + return false; + if (STI.getTargetTriple().isArch64Bit()) + return isUInt<6>(Imm); + return isUInt<5>(Imm); + }]; } def uimm5 : Operand<XLenVT>, ImmLeaf<XLenVT, [{return isUInt<5>(Imm);}]> { @@ -94,6 +117,12 @@ def simm12 : Operand<XLenVT>, ImmLeaf<XLenVT, [{return isInt<12>(Imm);}]> { let ParserMatchClass = SImmAsmOperand<12>; let EncoderMethod = "getImmOpValue"; let DecoderMethod = "decodeSImmOperand<12>"; + let MCOperandPredicate = [{ + int64_t Imm; + if (MCOp.evaluateAsConstantImm(Imm)) + return isInt<12>(Imm); + return MCOp.isBareSymbolRef(); + }]; } def uimm12 : Operand<XLenVT> { @@ -106,12 +135,24 @@ def simm13_lsb0 : Operand<OtherVT> { let ParserMatchClass = SImmAsmOperand<13, "Lsb0">; let EncoderMethod = "getImmOpValueAsr1"; let DecoderMethod = "decodeSImmOperandAndLsl1<13>"; + let MCOperandPredicate = [{ + int64_t Imm; + if (MCOp.evaluateAsConstantImm(Imm)) + return isShiftedInt<12, 1>(Imm); + return MCOp.isBareSymbolRef(); + }]; } def uimm20 : Operand<XLenVT> { let ParserMatchClass = UImmAsmOperand<20>; let EncoderMethod = "getImmOpValue"; let DecoderMethod = "decodeUImmOperand<20>"; + let MCOperandPredicate = [{ + int64_t Imm; + if (MCOp.evaluateAsConstantImm(Imm)) + return isUInt<20>(Imm); + return MCOp.isBareSymbolRef(); + }]; } // A 21-bit signed immediate where the least significant bit is zero. @@ -119,13 +160,36 @@ def simm21_lsb0 : Operand<OtherVT> { let ParserMatchClass = SImmAsmOperand<21, "Lsb0">; let EncoderMethod = "getImmOpValueAsr1"; let DecoderMethod = "decodeSImmOperandAndLsl1<21>"; + let MCOperandPredicate = [{ + int64_t Imm; + if (MCOp.evaluateAsConstantImm(Imm)) + return isShiftedInt<20, 1>(Imm); + return MCOp.isBareSymbolRef(); + }]; +} + +def BareSymbol : AsmOperandClass { + let Name = "BareSymbol"; + let RenderMethod = "addImmOperands"; + let DiagnosticType = "InvalidBareSymbol"; +} + +// A bare symbol. +def bare_symbol : Operand<XLenVT> { + let ParserMatchClass = BareSymbol; + let MCOperandPredicate = [{ + return MCOp.isBareSymbolRef(); + }]; } // A parameterized register class alternative to i32imm/i64imm from Target.td. -def ixlenimm : Operand<XLenVT>; +def ixlenimm : Operand<XLenVT> { + let ParserMatchClass = ImmXLenAsmOperand<"">; +} // Standalone (codegen-only) immleaf patterns. -def simm32 : ImmLeaf<XLenVT, [{return isInt<32>(Imm);}]>; +def simm32 : ImmLeaf<XLenVT, [{return isInt<32>(Imm);}]>; +def simm32hi20 : ImmLeaf<XLenVT, [{return isShiftedInt<20, 12>(Imm);}]>; // Addressing modes. // Necessary because a frameindex can't be matched directly in a pattern. @@ -220,7 +284,7 @@ class Priv<string opcodestr, bits<7> funct7> // Instructions //===----------------------------------------------------------------------===// -let hasSideEffects = 0, mayLoad = 0, mayStore = 0 in { +let hasSideEffects = 0, isReMaterializable = 1, mayLoad = 0, mayStore = 0 in { def LUI : RVInstU<OPC_LUI, (outs GPR:$rd), (ins uimm20:$imm20), "lui", "$rd, $imm20">; @@ -254,7 +318,11 @@ def SB : Store_rri<0b000, "sb">; def SH : Store_rri<0b001, "sh">; def SW : Store_rri<0b010, "sw">; +// ADDI isn't always rematerializable, but isReMaterializable will be used as +// a hint which is verified in isReallyTriviallyReMaterializable. +let isReMaterializable = 1 in def ADDI : ALU_ri<0b000, "addi">; + def SLTI : ALU_ri<0b010, "slti">; def SLTIU : ALU_ri<0b011, "sltiu">; def XORI : ALU_ri<0b100, "xori">; @@ -288,6 +356,12 @@ def FENCE : RVInstI<0b000, OPC_MISC_MEM, (outs), let imm12 = {0b0000,pred,succ}; } +def FENCE_TSO : RVInstI<0b000, OPC_MISC_MEM, (outs), (ins), "fence.tso", ""> { + let rs1 = 0; + let rd = 0; + let imm12 = {0b1000,0b0011,0b0011}; +} + def FENCE_I : RVInstI<0b001, OPC_MISC_MEM, (outs), (ins), "fence.i", ""> { let rs1 = 0; let rd = 0; @@ -386,7 +460,16 @@ def SFENCE_VMA : RVInstR<0b0001001, 0b000, OPC_SYSTEM, (outs), // TODO RV64I: sd def : InstAlias<"nop", (ADDI X0, X0, 0)>; -// TODO li + +// Note that the size is 32 because up to 8 32-bit instructions are needed to +// generate an arbitrary 64-bit immediate. However, the size does not really +// matter since PseudoLI is currently only used in the AsmParser where it gets +// expanded to real instructions immediately. +let hasSideEffects = 0, mayLoad = 0, mayStore = 0, Size = 32, + isCodeGenOnly = 0, isAsmParserOnly = 1 in +def PseudoLI : Pseudo<(outs GPR:$rd), (ins ixlenimm:$imm), [], + "li", "$rd, $imm">; + 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)>; @@ -401,6 +484,11 @@ 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)>; +// sgt/sgtu are recognised by the GNU assembler but the canonical slt/sltu +// form will always be printed. Therefore, set a zero weight. +def : InstAlias<"sgt $rd, $rs, $rt", (SLT GPR:$rd, GPR:$rt, GPR:$rs), 0>; +def : InstAlias<"sgtu $rd, $rs, $rt", (SLTU GPR:$rd, GPR:$rt, GPR:$rs), 0>; + def : InstAlias<"beqz $rs, $offset", (BEQ GPR:$rs, X0, simm13_lsb0:$offset)>; def : InstAlias<"bnez $rs, $offset", @@ -489,7 +577,7 @@ def IsOrAdd: PatFrag<(ops node:$A, node:$B), (or node:$A, node:$B), [{ /// 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<(simm32hi20:$imm), (LUI (HI20 imm:$imm))>; def : Pat<(simm32:$imm), (ADDI (LUI (HI20 imm:$imm)), (LO12Sext imm:$imm))>; /// Simple arithmetic operations @@ -536,11 +624,14 @@ 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))]>; +class SelectCC_rrirr<RegisterClass valty, RegisterClass cmpty> + : Pseudo<(outs valty:$dst), + (ins cmpty:$lhs, cmpty:$rhs, ixlenimm:$imm, + valty:$truev, valty:$falsev), + [(set valty:$dst, (SelectCC cmpty:$lhs, cmpty:$rhs, + (XLenVT imm:$imm), valty:$truev, valty:$falsev))]>; + +def Select_GPR_Using_CC_GPR : SelectCC_rrirr<GPR, GPR>; /// Branches and jumps @@ -585,14 +676,50 @@ def : Pat<(brind GPR:$rs1), (PseudoBRIND GPR:$rs1, 0)>; def : Pat<(brind (add GPR:$rs1, simm12:$imm12)), (PseudoBRIND GPR:$rs1, simm12:$imm12)>; +// PseudoCALL is a pseudo instruction which will eventually expand to auipc +// and jalr while encoding. This is desirable, as an auipc+jalr pair with +// R_RISCV_CALL and R_RISCV_RELAX relocations can be be relaxed by the linker +// if the offset fits in a signed 21-bit immediate. +// Define AsmString to print "call" when compile with -S flag. +// Define isCodeGenOnly = 0 to support parsing assembly "call" instruction. +let isCall = 1, Defs = [X1], isCodeGenOnly = 0 in +def PseudoCALL : Pseudo<(outs), (ins bare_symbol:$func), + [(Call tglobaladdr:$func)]> { + let AsmString = "call\t$func"; +} + +def : Pat<(Call texternalsym:$func), (PseudoCALL texternalsym:$func)>; + +def : Pat<(URetFlag), (URET X0, X0)>; +def : Pat<(SRetFlag), (SRET X0, X0)>; +def : Pat<(MRetFlag), (MRET X0, X0)>; + let isCall = 1, Defs = [X1] in -def PseudoCALL : Pseudo<(outs), (ins GPR:$rs1), [(Call GPR:$rs1)]>, - PseudoInstExpansion<(JALR X1, GPR:$rs1, 0)>; +def PseudoCALLIndirect : 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)>; +// PseudoTAIL is a pseudo instruction similar to PseudoCALL and will eventually +// expand to auipc and jalr while encoding. +// Define AsmString to print "tail" when compile with -S flag. +let isCall = 1, isTerminator = 1, isReturn = 1, isBarrier = 1, Uses = [X2], + isCodeGenOnly = 0 in +def PseudoTAIL : Pseudo<(outs), (ins bare_symbol:$dst), []> { + let AsmString = "tail\t$dst"; +} + +let isCall = 1, isTerminator = 1, isReturn = 1, isBarrier = 1, Uses = [X2] in +def PseudoTAILIndirect : Pseudo<(outs), (ins GPRTC:$rs1), [(Tail GPRTC:$rs1)]>, + PseudoInstExpansion<(JALR X0, GPR:$rs1, 0)>; + +def : Pat<(Tail (iPTR tglobaladdr:$dst)), + (PseudoTAIL texternalsym:$dst)>; +def : Pat<(Tail (iPTR texternalsym:$dst)), + (PseudoTAIL texternalsym:$dst)>; + /// Loads multiclass LdPat<PatFrag LoadOp, RVInst Inst> { @@ -616,20 +743,40 @@ 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)>; +multiclass StPat<PatFrag StoreOp, RVInst Inst, RegisterClass StTy> { + def : Pat<(StoreOp StTy:$rs2, GPR:$rs1), (Inst StTy:$rs2, GPR:$rs1, 0)>; + def : Pat<(StoreOp StTy:$rs2, AddrFI:$rs1), (Inst StTy:$rs2, AddrFI:$rs1, 0)>; + def : Pat<(StoreOp StTy:$rs2, (add GPR:$rs1, simm12:$imm12)), + (Inst StTy:$rs2, GPR:$rs1, simm12:$imm12)>; + def : Pat<(StoreOp StTy:$rs2, (add AddrFI:$rs1, simm12:$imm12)), + (Inst StTy:$rs2, AddrFI:$rs1, simm12:$imm12)>; + def : Pat<(StoreOp StTy:$rs2, (IsOrAdd AddrFI:$rs1, simm12:$imm12)), + (Inst StTy:$rs2, AddrFI:$rs1, simm12:$imm12)>; } -defm : StPat<truncstorei8, SB>; -defm : StPat<truncstorei16, SH>; -defm : StPat<store, SW>; +defm : StPat<truncstorei8, SB, GPR>; +defm : StPat<truncstorei16, SH, GPR>; +defm : StPat<store, SW, GPR>; + +/// Fences + +// Refer to Table A.6 in the version 2.3 draft of the RISC-V Instruction Set +// Manual: Volume I. + +// fence acquire -> fence r, rw +def : Pat<(atomic_fence (i32 4), (imm)), (FENCE 0b10, 0b11)>; +// fence release -> fence rw, w +def : Pat<(atomic_fence (i32 5), (imm)), (FENCE 0b11, 0b1)>; +// fence acq_rel -> fence.tso +def : Pat<(atomic_fence (i32 6), (imm)), (FENCE_TSO)>; +// fence seq_cst -> fence rw, rw +def : Pat<(atomic_fence (i32 7), (imm)), (FENCE 0b11, 0b11)>; + +// Lowering for atomic load and store is defined in RISCVInstrInfoA.td. +// Although these are lowered to fence+load/store instructions defined in the +// base RV32I/RV64I ISA, this lowering is only used when the A extension is +// present. This is necessary as it isn't valid to mix __atomic_* libcalls +// with inline atomic operations for the same object. /// Other pseudo-instructions diff --git a/lib/Target/RISCV/RISCVInstrInfoA.td b/lib/Target/RISCV/RISCVInstrInfoA.td index 33e863ba6a10..379322060438 100644 --- a/lib/Target/RISCV/RISCVInstrInfoA.td +++ b/lib/Target/RISCV/RISCVInstrInfoA.td @@ -75,3 +75,23 @@ 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] + +//===----------------------------------------------------------------------===// +// Pseudo-instructions and codegen patterns +//===----------------------------------------------------------------------===// + +let Predicates = [HasStdExtA] in { + +/// Atomic loads and stores + +// Fences will be inserted for atomic load/stores according to the logic in +// RISCVTargetLowering::{emitLeadingFence,emitTrailingFence}. + +defm : LdPat<atomic_load_8, LB>; +defm : LdPat<atomic_load_16, LH>; +defm : LdPat<atomic_load_32, LW>; + +defm : StPat<atomic_store_8, SB, GPR>; +defm : StPat<atomic_store_16, SH, GPR>; +defm : StPat<atomic_store_32, SW, GPR>; +} // Predicates = [HasStdExtF] diff --git a/lib/Target/RISCV/RISCVInstrInfoC.td b/lib/Target/RISCV/RISCVInstrInfoC.td index 4ca52652086b..5d1c62c0b653 100644 --- a/lib/Target/RISCV/RISCVInstrInfoC.td +++ b/lib/Target/RISCV/RISCVInstrInfoC.td @@ -27,18 +27,67 @@ def uimmlog2xlennonzero : Operand<XLenVT>, ImmLeaf<XLenVT, [{ let ParserMatchClass = UImmLog2XLenNonZeroAsmOperand; // TODO: should ensure invalid shamt is rejected when decoding. let DecoderMethod = "decodeUImmOperand<6>"; + let MCOperandPredicate = [{ + int64_t Imm; + if (!MCOp.evaluateAsConstantImm(Imm)) + return false; + if (STI.getTargetTriple().isArch64Bit()) + return isUInt<6>(Imm) && (Imm != 0); + return isUInt<5>(Imm) && (Imm != 0); + }]; } def simm6 : Operand<XLenVT>, ImmLeaf<XLenVT, [{return isInt<6>(Imm);}]> { let ParserMatchClass = SImmAsmOperand<6>; let EncoderMethod = "getImmOpValue"; let DecoderMethod = "decodeSImmOperand<6>"; + let MCOperandPredicate = [{ + int64_t Imm; + if (MCOp.evaluateAsConstantImm(Imm)) + return isInt<6>(Imm); + return MCOp.isBareSymbolRef(); + }]; } -def uimm6nonzero : Operand<XLenVT>, - ImmLeaf<XLenVT, [{return isUInt<6>(Imm) && (Imm != 0);}]> { - let ParserMatchClass = UImmAsmOperand<6, "NonZero">; - let DecoderMethod = "decodeUImmOperand<6>"; +def simm6nonzero : Operand<XLenVT>, + ImmLeaf<XLenVT, [{return (Imm != 0) && isInt<6>(Imm);}]> { + let ParserMatchClass = SImmAsmOperand<6, "NonZero">; + let EncoderMethod = "getImmOpValue"; + let DecoderMethod = "decodeSImmOperand<6>"; + let MCOperandPredicate = [{ + int64_t Imm; + if (MCOp.evaluateAsConstantImm(Imm)) + return (Imm != 0) && isInt<6>(Imm); + return MCOp.isBareSymbolRef(); + }]; +} + +def CLUIImmAsmOperand : AsmOperandClass { + let Name = "CLUIImm"; + let RenderMethod = "addImmOperands"; + let DiagnosticType = !strconcat("Invalid", Name); +} + + +// c_lui_imm checks the immediate range is in [1, 31] or [0xfffe0, 0xfffff]. +// The RISC-V ISA describes the constraint as [1, 63], with that value being +// loaded in to bits 17-12 of the destination register and sign extended from +// bit 17. Therefore, this 6-bit immediate can represent values in the ranges +// [1, 31] and [0xfffe0, 0xfffff]. +def c_lui_imm : Operand<XLenVT>, + ImmLeaf<XLenVT, [{return (Imm != 0) && + (isUInt<5>(Imm) || + (Imm >= 0xfffe0 && Imm <= 0xfffff));}]> { + let ParserMatchClass = CLUIImmAsmOperand; + let EncoderMethod = "getImmOpValue"; + let DecoderMethod = "decodeCLUIImmOperand"; + let MCOperandPredicate = [{ + int64_t Imm; + if (MCOp.evaluateAsConstantImm(Imm)) + return (Imm != 0) && (isUInt<5>(Imm) || + (Imm >= 0xfffe0 && Imm <= 0xfffff)); + return MCOp.isBareSymbolRef(); + }]; } // A 7-bit unsigned immediate where the least significant two bits are zero. @@ -47,6 +96,12 @@ def uimm7_lsb00 : Operand<XLenVT>, let ParserMatchClass = UImmAsmOperand<7, "Lsb00">; let EncoderMethod = "getImmOpValue"; let DecoderMethod = "decodeUImmOperand<7>"; + let MCOperandPredicate = [{ + int64_t Imm; + if (!MCOp.evaluateAsConstantImm(Imm)) + return false; + return isShiftedUInt<5, 2>(Imm); + }]; } // A 8-bit unsigned immediate where the least significant two bits are zero. @@ -55,6 +110,12 @@ def uimm8_lsb00 : Operand<XLenVT>, let ParserMatchClass = UImmAsmOperand<8, "Lsb00">; let EncoderMethod = "getImmOpValue"; let DecoderMethod = "decodeUImmOperand<8>"; + let MCOperandPredicate = [{ + int64_t Imm; + if (!MCOp.evaluateAsConstantImm(Imm)) + return false; + return isShiftedUInt<6, 2>(Imm); + }]; } // A 8-bit unsigned immediate where the least significant three bits are zero. @@ -63,6 +124,12 @@ def uimm8_lsb000 : Operand<XLenVT>, let ParserMatchClass = UImmAsmOperand<8, "Lsb000">; let EncoderMethod = "getImmOpValue"; let DecoderMethod = "decodeUImmOperand<8>"; + let MCOperandPredicate = [{ + int64_t Imm; + if (!MCOp.evaluateAsConstantImm(Imm)) + return false; + return isShiftedUInt<5, 3>(Imm); + }]; } // A 9-bit signed immediate where the least significant bit is zero. @@ -70,6 +137,13 @@ def simm9_lsb0 : Operand<OtherVT> { let ParserMatchClass = SImmAsmOperand<9, "Lsb0">; let EncoderMethod = "getImmOpValueAsr1"; let DecoderMethod = "decodeSImmOperandAndLsl1<9>"; + let MCOperandPredicate = [{ + int64_t Imm; + if (MCOp.evaluateAsConstantImm(Imm)) + return isShiftedInt<8, 1>(Imm); + return MCOp.isBareSymbolRef(); + + }]; } // A 9-bit unsigned immediate where the least significant three bits are zero. @@ -78,6 +152,12 @@ def uimm9_lsb000 : Operand<XLenVT>, let ParserMatchClass = UImmAsmOperand<9, "Lsb000">; let EncoderMethod = "getImmOpValue"; let DecoderMethod = "decodeUImmOperand<9>"; + let MCOperandPredicate = [{ + int64_t Imm; + if (!MCOp.evaluateAsConstantImm(Imm)) + return false; + return isShiftedUInt<6, 3>(Imm); + }]; } // A 10-bit unsigned immediate where the least significant two bits are zero @@ -88,21 +168,40 @@ def uimm10_lsb00nonzero : Operand<XLenVT>, let ParserMatchClass = UImmAsmOperand<10, "Lsb00NonZero">; let EncoderMethod = "getImmOpValue"; let DecoderMethod = "decodeUImmOperand<10>"; + let MCOperandPredicate = [{ + int64_t Imm; + if (!MCOp.evaluateAsConstantImm(Imm)) + return false; + return isShiftedUInt<8, 2>(Imm) && (Imm != 0); + }]; } // 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">; +def simm10_lsb0000nonzero : Operand<XLenVT>, + ImmLeaf<XLenVT, + [{return (Imm != 0) && isShiftedInt<6, 4>(Imm);}]> { + let ParserMatchClass = SImmAsmOperand<10, "Lsb0000NonZero">; let EncoderMethod = "getImmOpValue"; let DecoderMethod = "decodeSImmOperand<10>"; + let MCOperandPredicate = [{ + int64_t Imm; + if (!MCOp.evaluateAsConstantImm(Imm)) + return false; + return isShiftedInt<6, 4>(Imm); + }]; } // A 12-bit signed immediate where the least significant bit is zero. -def simm12_lsb0 : Operand<OtherVT> { +def simm12_lsb0 : Operand<XLenVT> { let ParserMatchClass = SImmAsmOperand<12, "Lsb0">; let EncoderMethod = "getImmOpValueAsr1"; let DecoderMethod = "decodeSImmOperandAndLsl1<12>"; + let MCOperandPredicate = [{ + int64_t Imm; + if (MCOp.evaluateAsConstantImm(Imm)) + return isShiftedInt<11, 1>(Imm); + return MCOp.isBareSymbolRef(); + }]; } //===----------------------------------------------------------------------===// @@ -177,7 +276,7 @@ class CS_ALU<bits<2> funct2, string OpcodeStr, RegisterClass cls, let Predicates = [HasStdExtC] in { -let hasSideEffects = 0, mayLoad = 0, mayStore = 0 in +let hasSideEffects = 0, mayLoad = 0, mayStore = 0, Uses = [X2] in def C_ADDI4SPN : RVInst16CIW<0b000, 0b00, (outs GPRC:$rd), (ins SP:$rs1, uimm10_lsb00nonzero:$imm), "c.addi4spn", "$rd, $rs1, $imm"> { @@ -188,8 +287,8 @@ def C_ADDI4SPN : RVInst16CIW<0b000, 0b00, (outs GPRC:$rd), let Inst{5} = imm{3}; } -def C_FLD : CLoad_ri<0b001, "c.fld", FPR64C, uimm8_lsb000>, - Requires<[HasStdExtD]> { +let Predicates = [HasStdExtC, HasStdExtD] in +def C_FLD : CLoad_ri<0b001, "c.fld", FPR64C, uimm8_lsb000> { bits<8> imm; let Inst{12-10} = imm{5-3}; let Inst{6-5} = imm{7-6}; @@ -202,24 +301,24 @@ def C_LW : CLoad_ri<0b010, "c.lw", GPRC, uimm7_lsb00> { let Inst{5} = imm{6}; } -let DecoderNamespace = "RISCV32Only_" in -def C_FLW : CLoad_ri<0b011, "c.flw", FPR32C, uimm7_lsb00>, - Requires<[HasStdExtF, IsRV32]> { +let DecoderNamespace = "RISCV32Only_", + Predicates = [HasStdExtC, HasStdExtF, IsRV32] in +def C_FLW : CLoad_ri<0b011, "c.flw", FPR32C, uimm7_lsb00> { 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]> { +let Predicates = [HasStdExtC, IsRV64] in +def C_LD : CLoad_ri<0b011, "c.ld", GPRC, uimm8_lsb000> { 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]> { +let Predicates = [HasStdExtC, HasStdExtD] in +def C_FSD : CStore_rri<0b101, "c.fsd", FPR64C, uimm8_lsb000> { bits<8> imm; let Inst{12-10} = imm{5-3}; let Inst{6-5} = imm{7-6}; @@ -232,17 +331,17 @@ def C_SW : CStore_rri<0b110, "c.sw", GPRC, uimm7_lsb00> { let Inst{5} = imm{6}; } -let DecoderNamespace = "RISCV32Only_" in -def C_FSW : CStore_rri<0b111, "c.fsw", FPR32C, uimm7_lsb00>, - Requires<[HasStdExtF, IsRV32]> { +let DecoderNamespace = "RISCV32Only_", + Predicates = [HasStdExtC, HasStdExtF, IsRV32] in +def C_FSW : CStore_rri<0b111, "c.fsw", FPR32C, uimm7_lsb00> { 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]> { +let Predicates = [HasStdExtC, IsRV64] in +def C_SD : CStore_rri<0b111, "c.sd", GPRC, uimm8_lsb000> { bits<8> imm; let Inst{12-10} = imm{5-3}; let Inst{6-5} = imm{7-6}; @@ -253,23 +352,23 @@ 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), + (ins GPRNoX0:$rd, simm6nonzero:$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 + DecoderNamespace = "RISCV32Only_", Defs = [X1], + Predicates = [HasStdExtC, IsRV32] in def C_JAL : RVInst16CJ<0b001, 0b01, (outs), (ins simm12_lsb0:$offset), - "c.jal", "$offset">, - Requires<[IsRV32]>; + "c.jal", "$offset">; -let hasSideEffects = 0, mayLoad = 0, mayStore = 0 in +let hasSideEffects = 0, mayLoad = 0, mayStore = 0, + Predicates = [HasStdExtC, IsRV64] in def C_ADDIW : RVInst16CI<0b001, 0b01, (outs GPRNoX0:$rd_wb), (ins GPRNoX0:$rd, simm6:$imm), - "c.addiw", "$rd, $imm">, - Requires<[IsRV64]> { + "c.addiw", "$rd, $imm"> { let Constraints = "$rd = $rd_wb"; let Inst{6-2} = imm{4-0}; } @@ -282,7 +381,7 @@ def C_LI : RVInst16CI<0b010, 0b01, (outs GPRNoX0:$rd), (ins simm6:$imm), let hasSideEffects = 0, mayLoad = 0, mayStore = 0 in def C_ADDI16SP : RVInst16CI<0b011, 0b01, (outs SP:$rd_wb), - (ins SP:$rd, simm10_lsb0000:$imm), + (ins SP:$rd, simm10_lsb0000nonzero:$imm), "c.addi16sp", "$rd, $imm"> { let Constraints = "$rd = $rd_wb"; let Inst{12} = imm{9}; @@ -295,7 +394,7 @@ def C_ADDI16SP : RVInst16CI<0b011, 0b01, (outs SP:$rd_wb), let hasSideEffects = 0, mayLoad = 0, mayStore = 0 in def C_LUI : RVInst16CI<0b011, 0b01, (outs GPRNoX0X2:$rd), - (ins uimm6nonzero:$imm), + (ins c_lui_imm:$imm), "c.lui", "$rd, $imm"> { let Inst{6-2} = imm{4-0}; } @@ -317,8 +416,10 @@ 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 Predicates = [HasStdExtC, IsRV64] in { +def C_SUBW : CS_ALU<0b00, "c.subw", GPRC, 1>; +def C_ADDW : CS_ALU<0b01, "c.addw", GPRC, 1>; +} let hasSideEffects = 0, mayLoad = 0, mayStore = 0 in def C_J : RVInst16CJ<0b101, 0b01, (outs), (ins simm12_lsb0:$offset), @@ -339,8 +440,8 @@ def C_SLLI : RVInst16CI<0b000, 0b10, (outs GPRNoX0:$rd_wb), let Inst{6-2} = imm{4-0}; } -def C_FLDSP : CStackLoad<0b001, "c.fldsp", FPR64, uimm9_lsb000>, - Requires<[HasStdExtD]> { +let Predicates = [HasStdExtC, HasStdExtD] in +def C_FLDSP : CStackLoad<0b001, "c.fldsp", FPR64, uimm9_lsb000> { let Inst{6-5} = imm{4-3}; let Inst{4-2} = imm{8-6}; } @@ -350,15 +451,15 @@ def C_LWSP : CStackLoad<0b010, "c.lwsp", GPRNoX0, uimm8_lsb00> { 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 DecoderNamespace = "RISCV32Only_", + Predicates = [HasStdExtC, HasStdExtF, IsRV32] in +def C_FLWSP : CStackLoad<0b011, "c.flwsp", FPR32, uimm8_lsb00> { 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 Predicates = [HasStdExtC, IsRV64] in +def C_LDSP : CStackLoad<0b011, "c.ldsp", GPRNoX0, uimm9_lsb000> { let Inst{6-5} = imm{4-3}; let Inst{4-2} = imm{8-6}; } @@ -392,8 +493,8 @@ def C_ADD : RVInst16CR<0b1001, 0b10, (outs GPRNoX0:$rs1_wb), let Constraints = "$rs1 = $rs1_wb"; } -def C_FSDSP : CStackStore<0b101, "c.fsdsp", FPR64, uimm9_lsb000>, - Requires<[HasStdExtD]> { +let Predicates = [HasStdExtC, HasStdExtD] in +def C_FSDSP : CStackStore<0b101, "c.fsdsp", FPR64, uimm9_lsb000> { let Inst{12-10} = imm{5-3}; let Inst{9-7} = imm{8-6}; } @@ -403,17 +504,204 @@ def C_SWSP : CStackStore<0b110, "c.swsp", GPR, uimm8_lsb00> { 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 DecoderNamespace = "RISCV32Only_", + Predicates = [HasStdExtC, HasStdExtF, IsRV32] in +def C_FSWSP : CStackStore<0b111, "c.fswsp", FPR32, uimm8_lsb00> { 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 Predicates = [HasStdExtC, IsRV64] in +def C_SDSP : CStackStore<0b111, "c.sdsp", GPR, uimm9_lsb000> { let Inst{12-10} = imm{5-3}; let Inst{9-7} = imm{8-6}; } } // Predicates = [HasStdExtC] + +//===----------------------------------------------------------------------===// +// Compress Instruction tablegen backend. +//===----------------------------------------------------------------------===// + +class CompressPat<dag input, dag output> { + dag Input = input; + dag Output = output; + list<Predicate> Predicates = []; +} + +// Patterns are defined in the same order the compressed instructions appear +// on page 82 of the ISA manual. + +// Quadrant 0 +let Predicates = [HasStdExtC] in { +def : CompressPat<(ADDI GPRC:$rd, SP:$rs1, uimm10_lsb00nonzero:$imm), + (C_ADDI4SPN GPRC:$rd, SP:$rs1, uimm10_lsb00nonzero:$imm)>; +} // Predicates = [HasStdExtC] + +let Predicates = [HasStdExtC, HasStdExtD] in { +def : CompressPat<(FLD FPR64C:$rd, GPRC:$rs1, uimm8_lsb000:$imm), + (C_FLD FPR64C:$rd, GPRC:$rs1, uimm8_lsb000:$imm)>; +} // Predicates = [HasStdExtC, HasStdExtD] + +let Predicates = [HasStdExtC] in { +def : CompressPat<(LW GPRC:$rd, GPRC:$rs1, uimm7_lsb00:$imm), + (C_LW GPRC:$rd, GPRC:$rs1, uimm7_lsb00:$imm)>; +} // Predicates = [HasStdExtC] + +let Predicates = [HasStdExtC, HasStdExtF, IsRV32] in { +def : CompressPat<(FLW FPR32C:$rd, GPRC:$rs1, uimm7_lsb00:$imm), + (C_FLW FPR32C:$rd, GPRC:$rs1, uimm7_lsb00:$imm)>; +} // Predicates = [HasStdExtC, HasStdExtF, IsRV32] + +let Predicates = [HasStdExtC, IsRV64] in { +def : CompressPat<(LD GPRC:$rd, GPRC:$rs1, uimm8_lsb000:$imm), + (C_LD GPRC:$rd, GPRC:$rs1, uimm8_lsb000:$imm)>; +} // Predicates = [HasStdExtC, IsRV64] + +let Predicates = [HasStdExtC, HasStdExtD] in { +def : CompressPat<(FSD FPR64C:$rs2, GPRC:$rs1, uimm8_lsb000:$imm), + (C_FSD FPR64C:$rs2, GPRC:$rs1, uimm8_lsb000:$imm)>; +} // Predicates = [HasStdExtC, HasStdExtD] + +let Predicates = [HasStdExtC] in { +def : CompressPat<(SW GPRC:$rs2, GPRC:$rs1, uimm7_lsb00:$imm), + (C_SW GPRC:$rs2, GPRC:$rs1, uimm7_lsb00:$imm)>; +} // Predicates = [HasStdExtC] + +let Predicates = [HasStdExtC, HasStdExtF, IsRV32] in { +def : CompressPat<(FSW FPR32C:$rs2, GPRC:$rs1,uimm7_lsb00:$imm), + (C_FSW FPR32C:$rs2, GPRC:$rs1, uimm7_lsb00:$imm)>; +} // Predicate = [HasStdExtC, HasStdExtF, IsRV32] + +let Predicates = [HasStdExtC, IsRV64] in { +def : CompressPat<(SD GPRC:$rs2, GPRC:$rs1, uimm8_lsb000:$imm), + (C_SD GPRC:$rs2, GPRC:$rs1, uimm8_lsb000:$imm)>; +} // Predicates = [HasStdExtC, IsRV64] + +// Quadrant 1 +let Predicates = [HasStdExtC] in { +def : CompressPat<(ADDI X0, X0, 0), (C_NOP)>; +def : CompressPat<(ADDI GPRNoX0:$rs1, GPRNoX0:$rs1, simm6nonzero:$imm), + (C_ADDI GPRNoX0:$rs1, simm6nonzero:$imm)>; +} // Predicates = [HasStdExtC] + +let Predicates = [HasStdExtC, IsRV32] in { +def : CompressPat<(JAL X1, simm12_lsb0:$offset), + (C_JAL simm12_lsb0:$offset)>; +} // Predicates = [HasStdExtC, IsRV32] + +let Predicates = [HasStdExtC, IsRV64] in { +def : CompressPat<(ADDIW GPRNoX0:$rs1, GPRNoX0:$rs1, simm6:$imm), + (C_ADDIW GPRNoX0:$rs1, simm6:$imm)>; +} // Predicates = [HasStdExtC, IsRV64] + +let Predicates = [HasStdExtC] in { +def : CompressPat<(ADDI GPRNoX0:$rd, X0, simm6:$imm), + (C_LI GPRNoX0:$rd, simm6:$imm)>; +def : CompressPat<(ADDI X2, X2, simm10_lsb0000nonzero:$imm), + (C_ADDI16SP X2, simm10_lsb0000nonzero:$imm)>; +def : CompressPat<(LUI GPRNoX0X2:$rd, c_lui_imm:$imm), + (C_LUI GPRNoX0X2:$rd, c_lui_imm:$imm)>; +def : CompressPat<(SRLI GPRC:$rs1, GPRC:$rs1, uimmlog2xlennonzero:$imm), + (C_SRLI GPRC:$rs1, uimmlog2xlennonzero:$imm)>; +def : CompressPat<(SRAI GPRC:$rs1, GPRC:$rs1, uimmlog2xlennonzero:$imm), + (C_SRAI GPRC:$rs1, uimmlog2xlennonzero:$imm)>; +def : CompressPat<(ANDI GPRC:$rs1, GPRC:$rs1, simm6:$imm), + (C_ANDI GPRC:$rs1, simm6:$imm)>; +def : CompressPat<(SUB GPRC:$rs1, GPRC:$rs1, GPRC:$rs2), + (C_SUB GPRC:$rs1, GPRC:$rs2)>; +def : CompressPat<(XOR GPRC:$rs1, GPRC:$rs1, GPRC:$rs2), + (C_XOR GPRC:$rs1, GPRC:$rs2)>; +def : CompressPat<(XOR GPRC:$rs1, GPRC:$rs2, GPRC:$rs1), + (C_XOR GPRC:$rs1, GPRC:$rs2)>; +def : CompressPat<(OR GPRC:$rs1, GPRC:$rs1, GPRC:$rs2), + (C_OR GPRC:$rs1, GPRC:$rs2)>; +def : CompressPat<(OR GPRC:$rs1, GPRC:$rs2, GPRC:$rs1), + (C_OR GPRC:$rs1, GPRC:$rs2)>; +def : CompressPat<(AND GPRC:$rs1, GPRC:$rs1, GPRC:$rs2), + (C_AND GPRC:$rs1, GPRC:$rs2)>; +def : CompressPat<(AND GPRC:$rs1, GPRC:$rs2, GPRC:$rs1), + (C_AND GPRC:$rs1, GPRC:$rs2)>; +} // Predicates = [HasStdExtC] + +let Predicates = [HasStdExtC, IsRV64] in { +def : CompressPat<(SUBW GPRC:$rs1, GPRC:$rs1, GPRC:$rs2), + (C_SUBW GPRC:$rs1, GPRC:$rs2)>; +def : CompressPat<(ADDW GPRC:$rs1, GPRC:$rs1, GPRC:$rs2), + (C_ADDW GPRC:$rs1, GPRC:$rs2)>; +def : CompressPat<(ADDW GPRC:$rs1, GPRC:$rs2, GPRC:$rs1), + (C_ADDW GPRC:$rs1, GPRC:$rs2)>; +} // Predicates = [HasStdExtC, IsRV64] + +let Predicates = [HasStdExtC] in { +def : CompressPat<(JAL X0, simm12_lsb0:$offset), + (C_J simm12_lsb0:$offset)>; +def : CompressPat<(BEQ GPRC:$rs1, X0, simm9_lsb0:$imm), + (C_BEQZ GPRC:$rs1, simm9_lsb0:$imm)>; +def : CompressPat<(BNE GPRC:$rs1, X0, simm9_lsb0:$imm), + (C_BNEZ GPRC:$rs1, simm9_lsb0:$imm)>; +} // Predicates = [HasStdExtC] + +// Quadrant 2 +let Predicates = [HasStdExtC] in { +def : CompressPat<(SLLI GPRNoX0:$rs1, GPRNoX0:$rs1, uimmlog2xlennonzero:$imm), + (C_SLLI GPRNoX0:$rs1, uimmlog2xlennonzero:$imm)>; +} // Predicates = [HasStdExtC] + +let Predicates = [HasStdExtC, HasStdExtD] in { +def : CompressPat<(FLD FPR64:$rd, SP:$rs1, uimm9_lsb000:$imm), + (C_FLDSP FPR64:$rd, SP:$rs1, uimm9_lsb000:$imm)>; +} // Predicates = [HasStdExtC, HasStdExtD] + +let Predicates = [HasStdExtC] in { +def : CompressPat<(LW GPRNoX0:$rd, SP:$rs1, uimm8_lsb00:$imm), + (C_LWSP GPRNoX0:$rd, SP:$rs1, uimm8_lsb00:$imm)>; +} // Predicates = [HasStdExtC] + +let Predicates = [HasStdExtC, HasStdExtF, IsRV32] in { +def : CompressPat<(FLW FPR32:$rd, SP:$rs1, uimm8_lsb00:$imm), + (C_FLWSP FPR32:$rd, SP:$rs1, uimm8_lsb00:$imm)>; +} // Predicates = [HasStdExtC, HasStdExtF, IsRV32] + +let Predicates = [HasStdExtC, IsRV64] in { +def : CompressPat<(LD GPRNoX0:$rd, SP:$rs1, uimm9_lsb000:$imm), + (C_LDSP GPRNoX0:$rd, SP:$rs1, uimm9_lsb000:$imm)>; +} // Predicates = [HasStdExtC, IsRV64] + +let Predicates = [HasStdExtC] in { +def : CompressPat<(JALR X0, GPRNoX0:$rs1, 0), + (C_JR GPRNoX0:$rs1)>; +def : CompressPat<(ADD GPRNoX0:$rs1, X0, GPRNoX0:$rs2), + (C_MV GPRNoX0:$rs1, GPRNoX0:$rs2)>; +def : CompressPat<(ADD GPRNoX0:$rs1, GPRNoX0:$rs2, X0), + (C_MV GPRNoX0:$rs1, GPRNoX0:$rs2)>; +def : CompressPat<(ADDI GPRNoX0:$rs1, GPRNoX0:$rs2, 0), + (C_MV GPRNoX0:$rs1, GPRNoX0:$rs2)>; +def : CompressPat<(EBREAK), (C_EBREAK)>; +def : CompressPat<(JALR X1, GPRNoX0:$rs1, 0), + (C_JALR GPRNoX0:$rs1)>; +def : CompressPat<(ADD GPRNoX0:$rs1, GPRNoX0:$rs1, GPRNoX0:$rs2), + (C_ADD GPRNoX0:$rs1, GPRNoX0:$rs2)>; +def : CompressPat<(ADD GPRNoX0:$rs1, GPRNoX0:$rs2, GPRNoX0:$rs1), + (C_ADD GPRNoX0:$rs1, GPRNoX0:$rs2)>; +} // Predicates = [HasStdExtC] + +let Predicates = [HasStdExtC, HasStdExtD] in { +def : CompressPat<(FSD FPR64:$rs2, SP:$rs1, uimm9_lsb000:$imm), + (C_FSDSP FPR64:$rs2, SP:$rs1, uimm9_lsb000:$imm)>; +} // Predicates = [HasStdExtC, HasStdExtD] + +let Predicates = [HasStdExtC] in { +def : CompressPat<(SW GPR:$rs2, SP:$rs1, uimm8_lsb00:$imm), + (C_SWSP GPR:$rs2, SP:$rs1, uimm8_lsb00:$imm)>; +} // Predicates = [HasStdExtC] + +let Predicates = [HasStdExtC, HasStdExtF, IsRV32] in { +def : CompressPat<(FSW FPR32:$rs2, SP:$rs1, uimm8_lsb00:$imm), + (C_FSWSP FPR32:$rs2, SP:$rs1, uimm8_lsb00:$imm)>; +} // Predicates = [HasStdExtC, HasStdExtF, IsRV32] + +let Predicates = [HasStdExtC, IsRV64] in { +def : CompressPat<(SD GPR:$rs2, SP:$rs1, uimm9_lsb000:$imm), + (C_SDSP GPR:$rs2, SP:$rs1, uimm9_lsb000:$imm)>; +} // Predicates = [HasStdExtC, IsRV64] diff --git a/lib/Target/RISCV/RISCVInstrInfoD.td b/lib/Target/RISCV/RISCVInstrInfoD.td index 48d91c0054d3..06b834d55ade 100644 --- a/lib/Target/RISCV/RISCVInstrInfoD.td +++ b/lib/Target/RISCV/RISCVInstrInfoD.td @@ -13,6 +13,20 @@ //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// +// RISC-V specific DAG Nodes. +//===----------------------------------------------------------------------===// + +def SDT_RISCVBuildPairF64 : SDTypeProfile<1, 2, [SDTCisVT<0, f64>, + SDTCisVT<1, i32>, + SDTCisSameAs<1, 2>]>; +def SDT_RISCVSplitF64 : SDTypeProfile<2, 1, [SDTCisVT<0, i32>, + SDTCisVT<1, i32>, + SDTCisVT<2, f64>]>; + +def RISCVBuildPairF64 : SDNode<"RISCVISD::BuildPairF64", SDT_RISCVBuildPairF64>; +def RISCVSplitF64 : SDNode<"RISCVISD::SplitF64", SDT_RISCVSplitF64>; + +//===----------------------------------------------------------------------===// // Instruction Class Templates //===----------------------------------------------------------------------===// @@ -171,4 +185,105 @@ let Predicates = [HasStdExtD] in { 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)>; + +// fgt.d/fge.d are recognised by the GNU assembler but the canonical +// flt.d/fle.d forms will always be printed. Therefore, set a zero weight. +def : InstAlias<"fgt.d $rd, $rs, $rt", + (FLT_D GPR:$rd, FPR64:$rt, FPR64:$rs), 0>; +def : InstAlias<"fge.d $rd, $rs, $rt", + (FLE_D GPR:$rd, FPR64:$rt, FPR64:$rs), 0>; +} // Predicates = [HasStdExtD] + +//===----------------------------------------------------------------------===// +// Pseudo-instructions and codegen patterns +//===----------------------------------------------------------------------===// + +class PatFpr64Fpr64<SDPatternOperator OpNode, RVInstR Inst> + : Pat<(OpNode FPR64:$rs1, FPR64:$rs2), (Inst $rs1, $rs2)>; + +class PatFpr64Fpr64DynFrm<SDPatternOperator OpNode, RVInstRFrm Inst> + : Pat<(OpNode FPR64:$rs1, FPR64:$rs2), (Inst $rs1, $rs2, 0b111)>; + +let Predicates = [HasStdExtD] in { + +/// Float conversion operations + +// f64 -> f32, f32 -> f64 +def : Pat<(fpround FPR64:$rs1), (FCVT_S_D FPR64:$rs1, 0b111)>; +def : Pat<(fpextend FPR32:$rs1), (FCVT_D_S FPR32:$rs1)>; + +// FP->[u]int. Round-to-zero must be used +def : Pat<(fp_to_sint FPR64:$rs1), (FCVT_W_D FPR64:$rs1, 0b001)>; +def : Pat<(fp_to_uint FPR64:$rs1), (FCVT_WU_D FPR64:$rs1, 0b001)>; + +// [u]int->fp +def : Pat<(sint_to_fp GPR:$rs1), (FCVT_D_W GPR:$rs1)>; +def : Pat<(uint_to_fp GPR:$rs1), (FCVT_D_WU GPR:$rs1)>; + +/// Float arithmetic operations + +def : PatFpr64Fpr64DynFrm<fadd, FADD_D>; +def : PatFpr64Fpr64DynFrm<fsub, FSUB_D>; +def : PatFpr64Fpr64DynFrm<fmul, FMUL_D>; +def : PatFpr64Fpr64DynFrm<fdiv, FDIV_D>; + +def : Pat<(fsqrt FPR64:$rs1), (FSQRT_D FPR64:$rs1, 0b111)>; + +def : Pat<(fneg FPR64:$rs1), (FSGNJN_D $rs1, $rs1)>; +def : Pat<(fabs FPR64:$rs1), (FSGNJX_D $rs1, $rs1)>; + +def : PatFpr64Fpr64<fcopysign, FSGNJ_D>; +def : Pat<(fcopysign FPR64:$rs1, (fneg FPR64:$rs2)), (FSGNJN_D $rs1, $rs2)>; + +// The RISC-V 2.2 user-level ISA spec defines fmin and fmax as returning the +// canonical NaN when giving a signaling NaN. This doesn't match the LLVM +// behaviour (see https://bugs.llvm.org/show_bug.cgi?id=27363). However, the +// draft 2.3 ISA spec changes the definition of fmin and fmax in a way that +// matches LLVM's fminnum and fmaxnum +// <https://github.com/riscv/riscv-isa-manual/commit/cd20cee7efd9bac7c5aa127ec3b451749d2b3cce>. +def : PatFpr64Fpr64<fminnum, FMIN_D>; +def : PatFpr64Fpr64<fmaxnum, FMAX_D>; + +/// Setcc + +def : PatFpr64Fpr64<seteq, FEQ_D>; +def : PatFpr64Fpr64<setoeq, FEQ_D>; +def : PatFpr64Fpr64<setlt, FLT_D>; +def : PatFpr64Fpr64<setolt, FLT_D>; +def : PatFpr64Fpr64<setle, FLE_D>; +def : PatFpr64Fpr64<setole, FLE_D>; + +// Define pattern expansions for setcc operations which aren't directly +// handled by a RISC-V instruction and aren't expanded in the SelectionDAG +// Legalizer. + +def : Pat<(setuo FPR64:$rs1, FPR64:$rs2), + (SLTIU (AND (FEQ_D FPR64:$rs1, FPR64:$rs1), + (FEQ_D FPR64:$rs2, FPR64:$rs2)), + 1)>; + +def Select_FPR64_Using_CC_GPR : SelectCC_rrirr<FPR64, GPR>; + +/// Loads + +defm : LdPat<load, FLD>; + +/// Stores + +defm : StPat<store, FSD, FPR64>; + +/// Pseudo-instructions needed for the soft-float ABI with RV32D + +// Moves two GPRs to an FPR. +let usesCustomInserter = 1 in +def BuildPairF64Pseudo + : Pseudo<(outs FPR64:$dst), (ins GPR:$src1, GPR:$src2), + [(set FPR64:$dst, (RISCVBuildPairF64 GPR:$src1, GPR:$src2))]>; + +// Moves an FPR to two GPRs. +let usesCustomInserter = 1 in +def SplitF64Pseudo + : Pseudo<(outs GPR:$dst1, GPR:$dst2), (ins FPR64:$src), + [(set GPR:$dst1, GPR:$dst2, (RISCVSplitF64 FPR64:$src))]>; + } // Predicates = [HasStdExtD] diff --git a/lib/Target/RISCV/RISCVInstrInfoF.td b/lib/Target/RISCV/RISCVInstrInfoF.td index 07722d2cbf34..6d7c59becf24 100644 --- a/lib/Target/RISCV/RISCVInstrInfoF.td +++ b/lib/Target/RISCV/RISCVInstrInfoF.td @@ -200,6 +200,13 @@ 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)>; +// fgt.s/fge.s are recognised by the GNU assembler but the canonical +// flt.s/fle.s forms will always be printed. Therefore, set a zero weight. +def : InstAlias<"fgt.s $rd, $rs, $rt", + (FLT_S GPR:$rd, FPR32:$rt, FPR32:$rs), 0>; +def : InstAlias<"fge.s $rd, $rs, $rt", + (FLE_S GPR:$rd, FPR32:$rt, FPR32:$rs), 0>; + // 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 @@ -219,4 +226,90 @@ 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>; + +// fmv.w.x and fmv.x.w were previously known as fmv.s.x and fmv.x.s. Both +// spellings should be supported by standard tools. +def : MnemonicAlias<"fmv.s.x", "fmv.w.x">; +def : MnemonicAlias<"fmv.x.s", "fmv.x.w">; +} // Predicates = [HasStdExtF] + +//===----------------------------------------------------------------------===// +// Pseudo-instructions and codegen patterns +//===----------------------------------------------------------------------===// + +/// Generic pattern classes +class PatFpr32Fpr32<SDPatternOperator OpNode, RVInstR Inst> + : Pat<(OpNode FPR32:$rs1, FPR32:$rs2), (Inst $rs1, $rs2)>; + +class PatFpr32Fpr32DynFrm<SDPatternOperator OpNode, RVInstRFrm Inst> + : Pat<(OpNode FPR32:$rs1, FPR32:$rs2), (Inst $rs1, $rs2, 0b111)>; + +let Predicates = [HasStdExtF] in { + +/// Float conversion operations + +// Moves (no conversion) +def : Pat<(bitconvert GPR:$rs1), (FMV_W_X GPR:$rs1)>; +def : Pat<(bitconvert FPR32:$rs1), (FMV_X_W FPR32:$rs1)>; + +// FP->[u]int. Round-to-zero must be used +def : Pat<(fp_to_sint FPR32:$rs1), (FCVT_W_S $rs1, 0b001)>; +def : Pat<(fp_to_uint FPR32:$rs1), (FCVT_WU_S $rs1, 0b001)>; + +// [u]int->fp. Match GCC and default to using dynamic rounding mode. +def : Pat<(sint_to_fp GPR:$rs1), (FCVT_S_W $rs1, 0b111)>; +def : Pat<(uint_to_fp GPR:$rs1), (FCVT_S_WU $rs1, 0b111)>; + +/// Float arithmetic operations + +def : PatFpr32Fpr32DynFrm<fadd, FADD_S>; +def : PatFpr32Fpr32DynFrm<fsub, FSUB_S>; +def : PatFpr32Fpr32DynFrm<fmul, FMUL_S>; +def : PatFpr32Fpr32DynFrm<fdiv, FDIV_S>; + +def : Pat<(fsqrt FPR32:$rs1), (FSQRT_S FPR32:$rs1, 0b111)>; + +def : Pat<(fneg FPR32:$rs1), (FSGNJN_S $rs1, $rs1)>; +def : Pat<(fabs FPR32:$rs1), (FSGNJX_S $rs1, $rs1)>; + +def : PatFpr32Fpr32<fcopysign, FSGNJ_S>; +def : Pat<(fcopysign FPR32:$rs1, (fneg FPR32:$rs2)), (FSGNJN_S $rs1, $rs2)>; + +// The RISC-V 2.2 user-level ISA spec defines fmin and fmax as returning the +// canonical NaN when given a signaling NaN. This doesn't match the LLVM +// behaviour (see https://bugs.llvm.org/show_bug.cgi?id=27363). However, the +// draft 2.3 ISA spec changes the definition of fmin and fmax in a way that +// matches LLVM's fminnum and fmaxnum +// <https://github.com/riscv/riscv-isa-manual/commit/cd20cee7efd9bac7c5aa127ec3b451749d2b3cce>. +def : PatFpr32Fpr32<fminnum, FMIN_S>; +def : PatFpr32Fpr32<fmaxnum, FMAX_S>; + +/// Setcc + +def : PatFpr32Fpr32<seteq, FEQ_S>; +def : PatFpr32Fpr32<setoeq, FEQ_S>; +def : PatFpr32Fpr32<setlt, FLT_S>; +def : PatFpr32Fpr32<setolt, FLT_S>; +def : PatFpr32Fpr32<setle, FLE_S>; +def : PatFpr32Fpr32<setole, FLE_S>; + +// Define pattern expansions for setcc operations which aren't directly +// handled by a RISC-V instruction and aren't expanded in the SelectionDAG +// Legalizer. + +def : Pat<(setuo FPR32:$rs1, FPR32:$rs2), + (SLTIU (AND (FEQ_S FPR32:$rs1, FPR32:$rs1), + (FEQ_S FPR32:$rs2, FPR32:$rs2)), + 1)>; + +def Select_FPR32_Using_CC_GPR : SelectCC_rrirr<FPR32, GPR>; + +/// Loads + +defm : LdPat<load, FLW>; + +/// Stores + +defm : StPat<store, FSW, FPR32>; + } // Predicates = [HasStdExtF] diff --git a/lib/Target/RISCV/RISCVInstrInfoM.td b/lib/Target/RISCV/RISCVInstrInfoM.td index fec9c1f93997..2dd10ada4003 100644 --- a/lib/Target/RISCV/RISCVInstrInfoM.td +++ b/lib/Target/RISCV/RISCVInstrInfoM.td @@ -34,3 +34,18 @@ def DIVUW : ALUW_rr<0b0000001, 0b101, "divuw">; def REMW : ALUW_rr<0b0000001, 0b110, "remw">; def REMUW : ALUW_rr<0b0000001, 0b111, "remuw">; } // Predicates = [HasStdExtM, IsRV64] + +//===----------------------------------------------------------------------===// +// Pseudo-instructions and codegen patterns +//===----------------------------------------------------------------------===// + +let Predicates = [HasStdExtM] in { +def : PatGprGpr<mul, MUL>; +def : PatGprGpr<mulhs, MULH>; +def : PatGprGpr<mulhu, MULHU>; +// No ISDOpcode for mulhsu +def : PatGprGpr<sdiv, DIV>; +def : PatGprGpr<udiv, DIVU>; +def : PatGprGpr<srem, REM>; +def : PatGprGpr<urem, REMU>; +} // Predicates = [HasStdExtM] diff --git a/lib/Target/RISCV/RISCVMCInstLower.cpp b/lib/Target/RISCV/RISCVMCInstLower.cpp index d8ae11f2bd90..e0100b1679be 100644 --- a/lib/Target/RISCV/RISCVMCInstLower.cpp +++ b/lib/Target/RISCV/RISCVMCInstLower.cpp @@ -48,11 +48,12 @@ static MCOperand lowerSymbolOperand(const MachineOperand &MO, MCSymbol *Sym, const MCExpr *ME = MCSymbolRefExpr::create(Sym, MCSymbolRefExpr::VK_None, Ctx); - if (!MO.isJTI() && MO.getOffset()) + if (!MO.isJTI() && !MO.isMBB() && MO.getOffset()) ME = MCBinaryExpr::createAdd( ME, MCConstantExpr::create(MO.getOffset(), Ctx), Ctx); - ME = RISCVMCExpr::create(ME, Kind, Ctx); + if (Kind != RISCVMCExpr::VK_RISCV_None) + ME = RISCVMCExpr::create(ME, Kind, Ctx); return MCOperand::createExpr(ME); } @@ -75,8 +76,7 @@ bool llvm::LowerRISCVMachineOperandToMCOperand(const MachineOperand &MO, MCOp = MCOperand::createImm(MO.getImm()); break; case MachineOperand::MO_MachineBasicBlock: - MCOp = MCOperand::createExpr( - MCSymbolRefExpr::create(MO.getMBB()->getSymbol(), AP.OutContext)); + MCOp = lowerSymbolOperand(MO, MO.getMBB()->getSymbol(), AP); break; case MachineOperand::MO_GlobalAddress: MCOp = lowerSymbolOperand(MO, AP.getSymbol(MO.getGlobal()), AP); @@ -89,6 +89,9 @@ bool llvm::LowerRISCVMachineOperandToMCOperand(const MachineOperand &MO, MCOp = lowerSymbolOperand( MO, AP.GetExternalSymbolSymbol(MO.getSymbolName()), AP); break; + case MachineOperand::MO_ConstantPoolIndex: + MCOp = lowerSymbolOperand(MO, AP.GetCPISymbol(MO.getIndex()), AP); + break; } return true; } diff --git a/lib/Target/RISCV/RISCVMachineFunctionInfo.h b/lib/Target/RISCV/RISCVMachineFunctionInfo.h new file mode 100644 index 000000000000..2fea3a1bdd2f --- /dev/null +++ b/lib/Target/RISCV/RISCVMachineFunctionInfo.h @@ -0,0 +1,55 @@ +//=- RISCVMachineFunctionInfo.h - RISCV machine function info -----*- 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 RISCV-specific per-machine-function information. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_RISCV_RISCVMACHINEFUNCTIONINFO_H +#define LLVM_LIB_TARGET_RISCV_RISCVMACHINEFUNCTIONINFO_H + +#include "llvm/CodeGen/MachineFrameInfo.h" +#include "llvm/CodeGen/MachineFunction.h" + +namespace llvm { + +/// RISCVMachineFunctionInfo - This class is derived from MachineFunctionInfo +/// and contains private RISCV-specific information for each MachineFunction. +class RISCVMachineFunctionInfo : public MachineFunctionInfo { +private: + MachineFunction &MF; + /// FrameIndex for start of varargs area + int VarArgsFrameIndex = 0; + /// Size of the save area used for varargs + int VarArgsSaveSize = 0; + /// FrameIndex used for transferring values between 64-bit FPRs and a pair + /// of 32-bit GPRs via the stack. + int MoveF64FrameIndex = -1; + +public: + // RISCVMachineFunctionInfo() = default; + + RISCVMachineFunctionInfo(MachineFunction &MF) : MF(MF) {} + + int getVarArgsFrameIndex() const { return VarArgsFrameIndex; } + void setVarArgsFrameIndex(int Index) { VarArgsFrameIndex = Index; } + + unsigned getVarArgsSaveSize() const { return VarArgsSaveSize; } + void setVarArgsSaveSize(int Size) { VarArgsSaveSize = Size; } + + int getMoveF64FrameIndex() { + if (MoveF64FrameIndex == -1) + MoveF64FrameIndex = MF.getFrameInfo().CreateStackObject(8, 8, false); + return MoveF64FrameIndex; + } +}; + +} // end namespace llvm + +#endif // LLVM_LIB_TARGET_RISCV_RISCVMACHINEFUNCTIONINFO_H diff --git a/lib/Target/RISCV/RISCVMergeBaseOffset.cpp b/lib/Target/RISCV/RISCVMergeBaseOffset.cpp new file mode 100644 index 000000000000..b8fa8a97d41a --- /dev/null +++ b/lib/Target/RISCV/RISCVMergeBaseOffset.cpp @@ -0,0 +1,286 @@ +//===----- RISCVMergeBaseOffset.cpp - Optimise address calculations ------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Merge the offset of address calculation into the offset field +// of instructions in a global address lowering sequence. This pass transforms: +// lui vreg1, %hi(s) +// addi vreg2, vreg1, %lo(s) +// addi vreg3, verg2, Offset +// +// Into: +// lui vreg1, %hi(s+Offset) +// addi vreg2, vreg1, %lo(s+Offset) +// +// The transformation is carried out under certain conditions: +// 1) The offset field in the base of global address lowering sequence is zero. +// 2) The lowered global address has only one use. +// +// The offset field can be in a different form. This pass handles all of them. +//===----------------------------------------------------------------------===// + +#include "RISCV.h" +#include "RISCVTargetMachine.h" +#include "llvm/CodeGen/Passes.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/TargetRegistry.h" +#include "llvm/Target/TargetOptions.h" +#include <set> +using namespace llvm; + +#define DEBUG_TYPE "riscv-merge-base-offset" +#define RISCV_MERGE_BASE_OFFSET_NAME "RISCV Merge Base Offset" +namespace { + +struct RISCVMergeBaseOffsetOpt : public MachineFunctionPass { + static char ID; + const MachineFunction *MF; + bool runOnMachineFunction(MachineFunction &Fn) override; + bool detectLuiAddiGlobal(MachineInstr &LUI, MachineInstr *&ADDI); + + bool detectAndFoldOffset(MachineInstr &HiLUI, MachineInstr &LoADDI); + void foldOffset(MachineInstr &HiLUI, MachineInstr &LoADDI, MachineInstr &Tail, + int64_t Offset); + bool matchLargeOffset(MachineInstr &TailAdd, unsigned GSReg, int64_t &Offset); + RISCVMergeBaseOffsetOpt() : MachineFunctionPass(ID) {} + + MachineFunctionProperties getRequiredProperties() const override { + return MachineFunctionProperties().set( + MachineFunctionProperties::Property::IsSSA); + } + + StringRef getPassName() const override { + return RISCV_MERGE_BASE_OFFSET_NAME; + } + +private: + MachineRegisterInfo *MRI; + std::set<MachineInstr *> DeadInstrs; +}; +}; // end anonymous namespace + +char RISCVMergeBaseOffsetOpt::ID = 0; +INITIALIZE_PASS(RISCVMergeBaseOffsetOpt, "riscv-merge-base-offset", + RISCV_MERGE_BASE_OFFSET_NAME, false, false) + +// Detect the pattern: +// lui vreg1, %hi(s) +// addi vreg2, vreg1, %lo(s) +// +// Pattern only accepted if: +// 1) ADDI has only one use. +// 2) LUI has only one use; which is the ADDI. +// 3) Both ADDI and LUI have GlobalAddress type which indicates that these +// are generated from global address lowering. +// 4) Offset value in the Global Address is 0. +bool RISCVMergeBaseOffsetOpt::detectLuiAddiGlobal(MachineInstr &HiLUI, + MachineInstr *&LoADDI) { + if (HiLUI.getOpcode() != RISCV::LUI || + HiLUI.getOperand(1).getTargetFlags() != RISCVII::MO_HI || + HiLUI.getOperand(1).getType() != MachineOperand::MO_GlobalAddress || + HiLUI.getOperand(1).getOffset() != 0 || + !MRI->hasOneUse(HiLUI.getOperand(0).getReg())) + return false; + unsigned HiLuiDestReg = HiLUI.getOperand(0).getReg(); + LoADDI = MRI->use_begin(HiLuiDestReg)->getParent(); + if (LoADDI->getOpcode() != RISCV::ADDI || + LoADDI->getOperand(2).getTargetFlags() != RISCVII::MO_LO || + LoADDI->getOperand(2).getType() != MachineOperand::MO_GlobalAddress || + LoADDI->getOperand(2).getOffset() != 0 || + !MRI->hasOneUse(LoADDI->getOperand(0).getReg())) + return false; + return true; +} + +// Update the offset in HiLUI and LoADDI instructions. +// Delete the tail instruction and update all the uses to use the +// output from LoADDI. +void RISCVMergeBaseOffsetOpt::foldOffset(MachineInstr &HiLUI, + MachineInstr &LoADDI, + MachineInstr &Tail, int64_t Offset) { + // Put the offset back in HiLUI and the LoADDI + HiLUI.getOperand(1).setOffset(Offset); + LoADDI.getOperand(2).setOffset(Offset); + // Delete the tail instruction. + DeadInstrs.insert(&Tail); + MRI->replaceRegWith(Tail.getOperand(0).getReg(), + LoADDI.getOperand(0).getReg()); + LLVM_DEBUG(dbgs() << " Merged offset " << Offset << " into base.\n" + << " " << HiLUI << " " << LoADDI;); +} + +// Detect patterns for large offsets that are passed into an ADD instruction. +// +// Base address lowering is of the form: +// HiLUI: lui vreg1, %hi(s) +// LoADDI: addi vreg2, vreg1, %lo(s) +// / \ +// / \ +// / \ +// / The large offset can be of two forms: \ +// 1) Offset that has non zero bits in lower 2) Offset that has non zero +// 12 bits and upper 20 bits bits in upper 20 bits only +// OffseLUI: lui vreg3, 4 +// OffsetTail: addi voff, vreg3, 188 OffsetTail: lui voff, 128 +// \ / +// \ / +// \ / +// \ / +// TailAdd: add vreg4, vreg2, voff +bool RISCVMergeBaseOffsetOpt::matchLargeOffset(MachineInstr &TailAdd, + unsigned GAReg, + int64_t &Offset) { + assert((TailAdd.getOpcode() == RISCV::ADD) && "Expected ADD instruction!"); + unsigned Rs = TailAdd.getOperand(1).getReg(); + unsigned Rt = TailAdd.getOperand(2).getReg(); + unsigned Reg = Rs == GAReg ? Rt : Rs; + + // Can't fold if the register has more than one use. + if (!MRI->hasOneUse(Reg)) + return false; + // This can point to an ADDI or a LUI: + MachineInstr &OffsetTail = *MRI->getVRegDef(Reg); + if (OffsetTail.getOpcode() == RISCV::ADDI) { + // The offset value has non zero bits in both %hi and %lo parts. + // Detect an ADDI that feeds from a LUI instruction. + MachineOperand &AddiImmOp = OffsetTail.getOperand(2); + if (AddiImmOp.getTargetFlags() != RISCVII::MO_None) + return false; + int64_t OffLo = AddiImmOp.getImm(); + MachineInstr &OffsetLui = + *MRI->getVRegDef(OffsetTail.getOperand(1).getReg()); + MachineOperand &LuiImmOp = OffsetLui.getOperand(1); + if (OffsetLui.getOpcode() != RISCV::LUI || + LuiImmOp.getTargetFlags() != RISCVII::MO_None || + !MRI->hasOneUse(OffsetLui.getOperand(0).getReg())) + return false; + int64_t OffHi = OffsetLui.getOperand(1).getImm(); + Offset = (OffHi << 12) + OffLo; + LLVM_DEBUG(dbgs() << " Offset Instrs: " << OffsetTail + << " " << OffsetLui); + DeadInstrs.insert(&OffsetTail); + DeadInstrs.insert(&OffsetLui); + return true; + } else if (OffsetTail.getOpcode() == RISCV::LUI) { + // The offset value has all zero bits in the lower 12 bits. Only LUI + // exists. + LLVM_DEBUG(dbgs() << " Offset Instr: " << OffsetTail); + Offset = OffsetTail.getOperand(1).getImm() << 12; + DeadInstrs.insert(&OffsetTail); + return true; + } + return false; +} + +bool RISCVMergeBaseOffsetOpt::detectAndFoldOffset(MachineInstr &HiLUI, + MachineInstr &LoADDI) { + unsigned DestReg = LoADDI.getOperand(0).getReg(); + assert(MRI->hasOneUse(DestReg) && "expected one use for LoADDI"); + // LoADDI has only one use. + MachineInstr &Tail = *MRI->use_begin(DestReg)->getParent(); + switch (Tail.getOpcode()) { + default: + LLVM_DEBUG(dbgs() << "Don't know how to get offset from this instr:" + << Tail); + return false; + case RISCV::ADDI: { + // Offset is simply an immediate operand. + int64_t Offset = Tail.getOperand(2).getImm(); + LLVM_DEBUG(dbgs() << " Offset Instr: " << Tail); + foldOffset(HiLUI, LoADDI, Tail, Offset); + return true; + } break; + case RISCV::ADD: { + // The offset is too large to fit in the immediate field of ADDI. + // This can be in two forms: + // 1) LUI hi_Offset followed by: + // ADDI lo_offset + // This happens in case the offset has non zero bits in + // both hi 20 and lo 12 bits. + // 2) LUI (offset20) + // This happens in case the lower 12 bits of the offset are zeros. + int64_t Offset; + if (!matchLargeOffset(Tail, DestReg, Offset)) + return false; + foldOffset(HiLUI, LoADDI, Tail, Offset); + return true; + } break; + case RISCV::LB: + case RISCV::LH: + case RISCV::LW: + case RISCV::LBU: + case RISCV::LHU: + case RISCV::LWU: + case RISCV::LD: + case RISCV::FLW: + case RISCV::FLD: + case RISCV::SB: + case RISCV::SH: + case RISCV::SW: + case RISCV::SD: + case RISCV::FSW: + case RISCV::FSD: { + // Transforms the sequence: Into: + // HiLUI: lui vreg1, %hi(foo) ---> lui vreg1, %hi(foo+8) + // LoADDI: addi vreg2, vreg1, %lo(foo) ---> lw vreg3, lo(foo+8)(vreg1) + // Tail: lw vreg3, 8(vreg2) + if (Tail.getOperand(1).isFI()) + return false; + // Register defined by LoADDI should be used in the base part of the + // load\store instruction. Otherwise, no folding possible. + unsigned BaseAddrReg = Tail.getOperand(1).getReg(); + if (DestReg != BaseAddrReg) + return false; + MachineOperand &TailImmOp = Tail.getOperand(2); + int64_t Offset = TailImmOp.getImm(); + // Update the offsets in global address lowering. + HiLUI.getOperand(1).setOffset(Offset); + // Update the immediate in the Tail instruction to add the offset. + Tail.RemoveOperand(2); + MachineOperand &ImmOp = LoADDI.getOperand(2); + ImmOp.setOffset(Offset); + Tail.addOperand(ImmOp); + // Update the base reg in the Tail instruction to feed from LUI. + // Output of HiLUI is only used in LoADDI, no need to use + // MRI->replaceRegWith(). + Tail.getOperand(1).setReg(HiLUI.getOperand(0).getReg()); + DeadInstrs.insert(&LoADDI); + return true; + } break; + } + return false; +} + +bool RISCVMergeBaseOffsetOpt::runOnMachineFunction(MachineFunction &Fn) { + if (skipFunction(Fn.getFunction())) + return false; + + DeadInstrs.clear(); + MRI = &Fn.getRegInfo(); + for (MachineBasicBlock &MBB : Fn) { + LLVM_DEBUG(dbgs() << "MBB: " << MBB.getName() << "\n"); + for (MachineInstr &HiLUI : MBB) { + MachineInstr *LoADDI = nullptr; + if (!detectLuiAddiGlobal(HiLUI, LoADDI)) + continue; + LLVM_DEBUG(dbgs() << " Found lowered global address with one use: " + << *LoADDI->getOperand(2).getGlobal() << "\n"); + // If the use count is only one, merge the offset + detectAndFoldOffset(HiLUI, *LoADDI); + } + } + // Delete dead instructions. + for (auto *MI : DeadInstrs) + MI->eraseFromParent(); + return true; +} + +/// Returns an instance of the Merge Base Offset Optimization pass. +FunctionPass *llvm::createRISCVMergeBaseOffsetOptPass() { + return new RISCVMergeBaseOffsetOpt(); +} diff --git a/lib/Target/RISCV/RISCVRegisterInfo.cpp b/lib/Target/RISCV/RISCVRegisterInfo.cpp index 5776a92cab91..3ed1dec434ce 100644 --- a/lib/Target/RISCV/RISCVRegisterInfo.cpp +++ b/lib/Target/RISCV/RISCVRegisterInfo.cpp @@ -33,6 +33,13 @@ RISCVRegisterInfo::RISCVRegisterInfo(unsigned HwMode) const MCPhysReg * RISCVRegisterInfo::getCalleeSavedRegs(const MachineFunction *MF) const { + if (MF->getFunction().hasFnAttribute("interrupt")) { + if (MF->getSubtarget<RISCVSubtarget>().hasStdExtD()) + return CSR_XLEN_F64_Interrupt_SaveList; + if (MF->getSubtarget<RISCVSubtarget>().hasStdExtF()) + return CSR_XLEN_F32_Interrupt_SaveList; + return CSR_Interrupt_SaveList; + } return CSR_SaveList; } @@ -50,6 +57,10 @@ BitVector RISCVRegisterInfo::getReservedRegs(const MachineFunction &MF) const { return Reserved; } +bool RISCVRegisterInfo::isConstantPhysReg(unsigned PhysReg) const { + return PhysReg == RISCV::X0; +} + const uint32_t *RISCVRegisterInfo::getNoPreservedMask() const { return CSR_NoRegs_RegMask; } @@ -61,6 +72,8 @@ void RISCVRegisterInfo::eliminateFrameIndex(MachineBasicBlock::iterator II, MachineInstr &MI = *II; MachineFunction &MF = *MI.getParent()->getParent(); + MachineRegisterInfo &MRI = MF.getRegInfo(); + const RISCVInstrInfo *TII = MF.getSubtarget<RISCVSubtarget>().getInstrInfo(); DebugLoc DL = MI.getDebugLoc(); int FrameIndex = MI.getOperand(FIOperandNum).getIndex(); @@ -69,25 +82,47 @@ void RISCVRegisterInfo::eliminateFrameIndex(MachineBasicBlock::iterator II, getFrameLowering(MF)->getFrameIndexReference(MF, FrameIndex, FrameReg) + MI.getOperand(FIOperandNum + 1).getImm(); - assert(MF.getSubtarget().getFrameLowering()->hasFP(MF) && - "eliminateFrameIndex currently requires hasFP"); + if (!isInt<32>(Offset)) { + report_fatal_error( + "Frame offsets outside of the signed 32-bit range not supported"); + } + + MachineBasicBlock &MBB = *MI.getParent(); + bool FrameRegIsKill = false; - // 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"); + assert(isInt<32>(Offset) && "Int32 expected"); + // The offset won't fit in an immediate, so use a scratch register instead + // Modify Offset and FrameReg appropriately + unsigned ScratchReg = MRI.createVirtualRegister(&RISCV::GPRRegClass); + TII->movImm32(MBB, II, DL, ScratchReg, Offset); + BuildMI(MBB, II, DL, TII->get(RISCV::ADD), ScratchReg) + .addReg(FrameReg) + .addReg(ScratchReg, RegState::Kill); + Offset = 0; + FrameReg = ScratchReg; + FrameRegIsKill = true; } - MI.getOperand(FIOperandNum).ChangeToRegister(FrameReg, false); + MI.getOperand(FIOperandNum) + .ChangeToRegister(FrameReg, false, false, FrameRegIsKill); MI.getOperand(FIOperandNum + 1).ChangeToImmediate(Offset); } unsigned RISCVRegisterInfo::getFrameRegister(const MachineFunction &MF) const { - return RISCV::X8; + const TargetFrameLowering *TFI = getFrameLowering(MF); + return TFI->hasFP(MF) ? RISCV::X8 : RISCV::X2; } const uint32_t * -RISCVRegisterInfo::getCallPreservedMask(const MachineFunction & /*MF*/, +RISCVRegisterInfo::getCallPreservedMask(const MachineFunction & MF, CallingConv::ID /*CC*/) const { + if (MF.getFunction().hasFnAttribute("interrupt")) { + if (MF.getSubtarget<RISCVSubtarget>().hasStdExtD()) + return CSR_XLEN_F64_Interrupt_RegMask; + if (MF.getSubtarget<RISCVSubtarget>().hasStdExtF()) + return CSR_XLEN_F32_Interrupt_RegMask; + return CSR_Interrupt_RegMask; + } return CSR_RegMask; } diff --git a/lib/Target/RISCV/RISCVRegisterInfo.h b/lib/Target/RISCV/RISCVRegisterInfo.h index 0b2bc3776fc6..cbbb70079dd1 100644 --- a/lib/Target/RISCV/RISCVRegisterInfo.h +++ b/lib/Target/RISCV/RISCVRegisterInfo.h @@ -32,6 +32,8 @@ struct RISCVRegisterInfo : public RISCVGenRegisterInfo { BitVector getReservedRegs(const MachineFunction &MF) const override; + bool isConstantPhysReg(unsigned PhysReg) const override; + const uint32_t *getNoPreservedMask() const override; void eliminateFrameIndex(MachineBasicBlock::iterator MI, int SPAdj, @@ -39,6 +41,18 @@ struct RISCVRegisterInfo : public RISCVGenRegisterInfo { RegScavenger *RS = nullptr) const override; unsigned getFrameRegister(const MachineFunction &MF) const override; + + bool requiresRegisterScavenging(const MachineFunction &MF) const override { + return true; + } + + bool requiresFrameIndexScavenging(const MachineFunction &MF) const override { + return true; + } + + bool trackLivenessAfterRegAlloc(const MachineFunction &) const override { + return true; + } }; } diff --git a/lib/Target/RISCV/RISCVRegisterInfo.td b/lib/Target/RISCV/RISCVRegisterInfo.td index 21be2e332e59..4be8ff9200e9 100644 --- a/lib/Target/RISCV/RISCVRegisterInfo.td +++ b/lib/Target/RISCV/RISCVRegisterInfo.td @@ -38,8 +38,16 @@ def ABIRegAltName : RegAltNameIndex; } // Namespace = "RISCV" // Integer registers +// CostPerUse is set higher for registers that may not be compressible as they +// are not part of GPRC, the most restrictive register class used by the +// compressed instruction set. This will influence the greedy register +// allocator to reduce the use of registers that can't be encoded in 16 bit +// instructions. This affects register allocation even when compressed +// instruction isn't targeted, we see no major negative codegen impact. + let RegAltNameIndices = [ABIRegAltName] in { def X0 : RISCVReg<0, "x0", ["zero"]>, DwarfRegNum<[0]>; + let CostPerUse = 1 in { def X1 : RISCVReg<1, "x1", ["ra"]>, DwarfRegNum<[1]>; def X2 : RISCVReg<2, "x2", ["sp"]>, DwarfRegNum<[2]>; def X3 : RISCVReg<3, "x3", ["gp"]>, DwarfRegNum<[3]>; @@ -47,6 +55,7 @@ let RegAltNameIndices = [ABIRegAltName] in { 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]>; @@ -55,6 +64,7 @@ let RegAltNameIndices = [ABIRegAltName] in { def X13 : RISCVReg<13,"x13", ["a3"]>, DwarfRegNum<[13]>; def X14 : RISCVReg<14,"x14", ["a4"]>, DwarfRegNum<[14]>; def X15 : RISCVReg<15,"x15", ["a5"]>, DwarfRegNum<[15]>; + let CostPerUse = 1 in { def X16 : RISCVReg<16,"x16", ["a6"]>, DwarfRegNum<[16]>; def X17 : RISCVReg<17,"x17", ["a7"]>, DwarfRegNum<[17]>; def X18 : RISCVReg<18,"x18", ["s2"]>, DwarfRegNum<[18]>; @@ -71,6 +81,7 @@ let RegAltNameIndices = [ABIRegAltName] in { def X29 : RISCVReg<29,"x29", ["t4"]>, DwarfRegNum<[29]>; def X30 : RISCVReg<30,"x30", ["t5"]>, DwarfRegNum<[30]>; def X31 : RISCVReg<31,"x31", ["t6"]>, DwarfRegNum<[31]>; + } } def XLenVT : ValueTypeByHwMode<[RV32, RV64, DefaultMode], @@ -128,6 +139,19 @@ def GPRC : RegisterClass<"RISCV", [XLenVT], 32, (add [RegInfo<32,32,32>, RegInfo<64,64,64>, RegInfo<32,32,32>]>; } +// For indirect tail calls, we can't use callee-saved registers, as they are +// restored to the saved value before the tail call, which would clobber a call +// address. +def GPRTC : RegisterClass<"RISCV", [XLenVT], 32, (add + (sequence "X%u", 5, 7), + (sequence "X%u", 10, 17), + (sequence "X%u", 28, 31) + )> { + 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], diff --git a/lib/Target/RISCV/RISCVSubtarget.h b/lib/Target/RISCV/RISCVSubtarget.h index 928ba5815a22..0e09391e7829 100644 --- a/lib/Target/RISCV/RISCVSubtarget.h +++ b/lib/Target/RISCV/RISCVSubtarget.h @@ -36,6 +36,7 @@ class RISCVSubtarget : public RISCVGenSubtargetInfo { bool HasStdExtD = false; bool HasStdExtC = false; bool HasRV64 = false; + bool EnableLinkerRelax = false; unsigned XLen = 32; MVT XLenVT = MVT::i32; RISCVFrameLowering FrameLowering; @@ -77,6 +78,7 @@ public: bool hasStdExtD() const { return HasStdExtD; } bool hasStdExtC() const { return HasStdExtC; } bool is64Bit() const { return HasRV64; } + bool enableLinkerRelax() const { return EnableLinkerRelax; } MVT getXLenVT() const { return XLenVT; } unsigned getXLen() const { return XLen; } }; diff --git a/lib/Target/RISCV/RISCVTargetMachine.cpp b/lib/Target/RISCV/RISCVTargetMachine.cpp index e12168b73999..a2ebf5bf3e6b 100644 --- a/lib/Target/RISCV/RISCVTargetMachine.cpp +++ b/lib/Target/RISCV/RISCVTargetMachine.cpp @@ -13,6 +13,7 @@ #include "RISCV.h" #include "RISCVTargetMachine.h" +#include "RISCVTargetObjectFile.h" #include "llvm/ADT/STLExtras.h" #include "llvm/CodeGen/Passes.h" #include "llvm/CodeGen/TargetLoweringObjectFileImpl.h" @@ -59,7 +60,7 @@ RISCVTargetMachine::RISCVTargetMachine(const Target &T, const Triple &TT, : LLVMTargetMachine(T, computeDataLayout(TT), TT, CPU, FS, Options, getEffectiveRelocModel(TT, RM), getEffectiveCodeModel(CM), OL), - TLOF(make_unique<TargetLoweringObjectFileELF>()), + TLOF(make_unique<RISCVELFTargetObjectFile>()), Subtarget(TT, CPU, FS, *this) { initAsmInfo(); } @@ -74,7 +75,10 @@ public: return getTM<RISCVTargetMachine>(); } + void addIRPasses() override; bool addInstSelector() override; + void addPreEmitPass() override; + void addPreRegAlloc() override; }; } @@ -82,8 +86,19 @@ TargetPassConfig *RISCVTargetMachine::createPassConfig(PassManagerBase &PM) { return new RISCVPassConfig(*this, PM); } +void RISCVPassConfig::addIRPasses() { + addPass(createAtomicExpandPass()); + TargetPassConfig::addIRPasses(); +} + bool RISCVPassConfig::addInstSelector() { addPass(createRISCVISelDag(getRISCVTargetMachine())); return false; } + +void RISCVPassConfig::addPreEmitPass() { addPass(&BranchRelaxationPassID); } + +void RISCVPassConfig::addPreRegAlloc() { + addPass(createRISCVMergeBaseOffsetOptPass()); +} diff --git a/lib/Target/RISCV/RISCVTargetObjectFile.cpp b/lib/Target/RISCV/RISCVTargetObjectFile.cpp new file mode 100644 index 000000000000..46e81b628b65 --- /dev/null +++ b/lib/Target/RISCV/RISCVTargetObjectFile.cpp @@ -0,0 +1,19 @@ +//===-- RISCVTargetObjectFile.cpp - RISCV Object Info -----------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "RISCVTargetObjectFile.h" +#include "RISCVTargetMachine.h" + +using namespace llvm; + +void RISCVELFTargetObjectFile::Initialize(MCContext &Ctx, + const TargetMachine &TM) { + TargetLoweringObjectFileELF::Initialize(Ctx, TM); + InitializeELF(TM.Options.UseInitArray); +} diff --git a/lib/Target/RISCV/RISCVTargetObjectFile.h b/lib/Target/RISCV/RISCVTargetObjectFile.h new file mode 100644 index 000000000000..5467220301c1 --- /dev/null +++ b/lib/Target/RISCV/RISCVTargetObjectFile.h @@ -0,0 +1,25 @@ +//===-- RISCVTargetObjectFile.h - RISCV Object Info -*- 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_RISCVTARGETOBJECTFILE_H +#define LLVM_LIB_TARGET_RISCV_RISCVTARGETOBJECTFILE_H + +#include "llvm/CodeGen/TargetLoweringObjectFileImpl.h" + +namespace llvm { +class RISCVTargetMachine; + +/// This implementation is used for RISCV ELF targets. +class RISCVELFTargetObjectFile : public TargetLoweringObjectFileELF { + void Initialize(MCContext &Ctx, const TargetMachine &TM) override; +}; + +} // end namespace llvm + +#endif |