From 9cc166efe6d1e1df61d7a4f0a4c61571ff7c0429 Mon Sep 17 00:00:00 2001 From: Sameer AbuAsal Date: Fri, 6 Apr 2018 21:07:05 +0000 Subject: [PATCH] [RISCV] Tablegen-driven Instruction Compression. Summary: This patch implements a tablegen-driven Instruction Compression mechanism for generating RISCV compressed instructions (C Extension) from the expanded instruction form. This tablegen backend processes CompressPat declarations in a td file and generates all the compile-time and runtime checks required to validate the declarations, validate the input operands and generate correct instructions. The checks include validating register operands, immediate operands, fixed register operands and fixed immediate operands. Example: class CompressPat { dag Input = input; dag Output = output; list Predicates = []; } let Predicates = [HasStdExtC] in { def : CompressPat<(ADD GPRNoX0:$rs1, GPRNoX0:$rs1, GPRNoX0:$rs2), (C_ADD GPRNoX0:$rs1, GPRNoX0:$rs2)>; } The result is an auto-generated header file 'RISCVGenCompressEmitter.inc' which exports two functions for compressing/uncompressing MCInst instructions, plus some helper functions: bool compressInst(MCInst& OutInst, const MCInst &MI, const MCSubtargetInfo &STI, MCContext &Context); bool uncompressInst(MCInst& OutInst, const MCInst &MI, const MCRegisterInfo &MRI, const MCSubtargetInfo &STI); The clients that include this auto-generated header file and invoke these functions can compress an instruction before emitting it, in the target-specific ASM or ELF streamer, or can uncompress an instruction before printing it, when the expanded instruction format aliases is favored. The following clients were added to implement compression\uncompression for RISCV: 1) RISCVAsmParser::MatchAndEmitInstruction: Inserted a call to compressInst() to compresses instructions parsed by llvm-mc coming from an ASM input. 2) RISCVAsmPrinter::EmitInstruction: Inserted a call to compressInst() to compress instructions that were lowered from Machine Instructions (MachineInstr). 3) RVInstPrinter::printInst: Inserted a call to uncompressInst() to print the expanded version of the instruction instead of the compressed one (e.g, add s0, s0, a5 instead of c.add s0, a5) when -riscv-no-aliases is not passed. This patch squashes D45119, D42780 and D41932. It was reviewed in smaller patches by asb, efriedma, apazos and mgrang. Reviewers: asb, efriedma, apazos, llvm-commits, sabuasal Reviewed By: sabuasal Subscribers: mgorny, eraman, asb, rbar, johnrusso, simoncook, jordy.potman.lists, apazos, niosHD, kito-cheng, shiva0217, zzheng Differential Revision: https://reviews.llvm.org/D45385 llvm-svn: 329455 --- include/llvm/MC/MCInst.h | 2 + lib/MC/MCInst.cpp | 18 + lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp | 12 +- lib/Target/RISCV/CMakeLists.txt | 1 + .../RISCV/InstPrinter/RISCVInstPrinter.cpp | 16 +- lib/Target/RISCV/MCTargetDesc/RISCVMCExpr.cpp | 1 + lib/Target/RISCV/MCTargetDesc/RISCVMCExpr.h | 1 + lib/Target/RISCV/RISCVAsmPrinter.cpp | 11 + lib/Target/RISCV/RISCVInstrInfo.td | 32 + lib/Target/RISCV/RISCVInstrInfoC.td | 263 +++++- test/CodeGen/RISCV/alu32.ll | 42 + test/CodeGen/RISCV/branch.ll | 32 + test/CodeGen/RISCV/compress-Pseudo.ll | 10 + test/CodeGen/RISCV/compress-inline-asm.ll | 15 + test/MC/RISCV/cnop.s | 2 +- test/MC/RISCV/compress-cjal.s | 17 + test/MC/RISCV/compress-rv32d.s | 44 + test/MC/RISCV/compress-rv32f.s | 32 + test/MC/RISCV/compress-rv32i.s | 207 +++++ test/MC/RISCV/compress-rv64i.s | 60 ++ test/MC/RISCV/compressed-relocations.s | 20 + test/MC/RISCV/fixups-compressed.s | 2 +- test/MC/RISCV/relocations.s | 14 +- test/MC/RISCV/rv32-relaxation.s | 26 +- test/MC/RISCV/rv64-relaxation.s | 22 +- utils/TableGen/CMakeLists.txt | 1 + utils/TableGen/RISCVCompressInstEmitter.cpp | 806 ++++++++++++++++++ utils/TableGen/TableGen.cpp | 6 + utils/TableGen/TableGenBackends.h | 1 + 29 files changed, 1672 insertions(+), 44 deletions(-) create mode 100644 test/CodeGen/RISCV/compress-Pseudo.ll create mode 100644 test/CodeGen/RISCV/compress-inline-asm.ll create mode 100644 test/MC/RISCV/compress-cjal.s create mode 100644 test/MC/RISCV/compress-rv32d.s create mode 100644 test/MC/RISCV/compress-rv32f.s create mode 100644 test/MC/RISCV/compress-rv32i.s create mode 100644 test/MC/RISCV/compress-rv64i.s create mode 100644 test/MC/RISCV/compressed-relocations.s create mode 100644 utils/TableGen/RISCVCompressInstEmitter.cpp diff --git a/include/llvm/MC/MCInst.h b/include/llvm/MC/MCInst.h index db28fd0fd6d..b472e61a34f 100644 --- a/include/llvm/MC/MCInst.h +++ b/include/llvm/MC/MCInst.h @@ -150,6 +150,8 @@ public: void print(raw_ostream &OS) const; void dump() const; + bool isBareSymbolRef() const; + bool evaluateAsConstantImm(int64_t &Imm) const; }; template <> struct isPodLike { static const bool value = true; }; diff --git a/lib/MC/MCInst.cpp b/lib/MC/MCInst.cpp index f6d1d3cffca..5f026dc6b26 100644 --- a/lib/MC/MCInst.cpp +++ b/lib/MC/MCInst.cpp @@ -10,6 +10,7 @@ #include "llvm/MC/MCInst.h" #include "llvm/MC/MCExpr.h" #include "llvm/MC/MCInstPrinter.h" +#include "llvm/Support/Casting.h" #include "llvm/Support/Compiler.h" #include "llvm/Support/Debug.h" #include "llvm/Support/raw_ostream.h" @@ -35,6 +36,23 @@ void MCOperand::print(raw_ostream &OS) const { OS << ">"; } +bool MCOperand::evaluateAsConstantImm(int64_t &Imm) const { + if (isImm()) { + Imm = getImm(); + return true; + } + return false; +} + +bool MCOperand::isBareSymbolRef() const { + assert(isExpr() && + "isBareSymbolRef expects only expressions"); + const MCExpr *Expr = getExpr(); + MCExpr::ExprKind Kind = getExpr()->getKind(); + return Kind == MCExpr::SymbolRef && + cast(Expr)->getKind() == MCSymbolRefExpr::VK_None; +} + #if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) LLVM_DUMP_METHOD void MCOperand::dump() const { print(dbgs()); diff --git a/lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp b/lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp index 035c1ef6ac7..59654f9b601 100644 --- a/lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp +++ b/lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp @@ -26,6 +26,10 @@ using namespace llvm; +// Include the auto-generated portion of the compress emitter. +#define GEN_COMPRESS_INSTR +#include "RISCVGenCompressInstEmitter.inc" + namespace { struct RISCVOperand; @@ -595,10 +599,14 @@ bool RISCVAsmParser::MatchAndEmitInstruction(SMLoc IDLoc, unsigned &Opcode, switch (MatchInstructionImpl(Operands, Inst, ErrorInfo, MatchingInlineAsm)) { default: break; - case Match_Success: + case Match_Success: { + MCInst CInst; + bool Res = compressInst(CInst, Inst, getSTI(), Out.getContext()); + CInst.setLoc(IDLoc); Inst.setLoc(IDLoc); - Out.EmitInstruction(Inst, getSTI()); + Out.EmitInstruction((Res ? CInst : Inst), getSTI()); return false; + } case Match_MissingFeature: return Error(IDLoc, "instruction use requires an option to be enabled"); case Match_MnemonicFail: diff --git a/lib/Target/RISCV/CMakeLists.txt b/lib/Target/RISCV/CMakeLists.txt index fa9adeb25eb..cb18467d478 100644 --- a/lib/Target/RISCV/CMakeLists.txt +++ b/lib/Target/RISCV/CMakeLists.txt @@ -2,6 +2,7 @@ set(LLVM_TARGET_DEFINITIONS RISCV.td) 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 RISCVGenDisassemblerTables.inc -gen-disassembler) tablegen(LLVM RISCVGenInstrInfo.inc -gen-instr-info) diff --git a/lib/Target/RISCV/InstPrinter/RISCVInstPrinter.cpp b/lib/Target/RISCV/InstPrinter/RISCVInstPrinter.cpp index f1fa2ecbcb2..300e6fd9750 100644 --- a/lib/Target/RISCV/InstPrinter/RISCVInstPrinter.cpp +++ b/lib/Target/RISCV/InstPrinter/RISCVInstPrinter.cpp @@ -13,6 +13,7 @@ #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" @@ -30,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 NoAliases("riscv-no-aliases", cl::desc("Disable the emission of assembler pseudo instructions"), @@ -38,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, STI, O)) - printInstruction(MI, STI, O); + bool Res = false; + const MCInst *NewMI = MI; + MCInst UncompressedMI; + if (!NoAliases) + Res = uncompressInst(UncompressedMI, *MI, MRI, STI); + if (Res) + NewMI = const_cast(&UncompressedMI); + if (NoAliases || !printAliasInstr(NewMI, STI, O)) + printInstruction(NewMI, STI, O); printAnnotation(O, Annot); } diff --git a/lib/Target/RISCV/MCTargetDesc/RISCVMCExpr.cpp b/lib/Target/RISCV/MCTargetDesc/RISCVMCExpr.cpp index 68cdb3783b1..4d1573ab8ef 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" diff --git a/lib/Target/RISCV/MCTargetDesc/RISCVMCExpr.h b/lib/Target/RISCV/MCTargetDesc/RISCVMCExpr.h index c49593f0b9c..e428b0d30d3 100644 --- a/lib/Target/RISCV/MCTargetDesc/RISCVMCExpr.h +++ b/lib/Target/RISCV/MCTargetDesc/RISCVMCExpr.h @@ -20,6 +20,7 @@ namespace llvm { class StringRef; +class MCOperand; class RISCVMCExpr : public MCTargetExpr { public: enum VariantKind { diff --git a/lib/Target/RISCV/RISCVAsmPrinter.cpp b/lib/Target/RISCV/RISCVAsmPrinter.cpp index bbaa8ec454f..bdf8e5d840b 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" @@ -48,6 +49,7 @@ public: unsigned AsmVariant, const char *ExtraCode, raw_ostream &OS) override; + void EmitToStreamer(MCStreamer &S, const MCInst &Inst); bool emitPseudoExpansionLowering(MCStreamer &OutStreamer, const MachineInstr *MI); @@ -58,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" diff --git a/lib/Target/RISCV/RISCVInstrInfo.td b/lib/Target/RISCV/RISCVInstrInfo.td index 8b5b89ba422..1c429da7419 100644 --- a/lib/Target/RISCV/RISCVInstrInfo.td +++ b/lib/Target/RISCV/RISCVInstrInfo.td @@ -83,6 +83,14 @@ def uimmlog2xlen : Operand, ImmLeaf(Imm); + return isUInt<5>(Imm); + }]; } def uimm5 : Operand, ImmLeaf(Imm);}]> { @@ -94,6 +102,12 @@ def simm12 : Operand, ImmLeaf(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 { @@ -106,12 +120,24 @@ def simm13_lsb0 : Operand { 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 { 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,6 +145,12 @@ def simm21_lsb0 : Operand { 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(); + }]; } // A parameterized register class alternative to i32imm/i64imm from Target.td. diff --git a/lib/Target/RISCV/RISCVInstrInfoC.td b/lib/Target/RISCV/RISCVInstrInfoC.td index b3566878e97..c1cbfdcb5cf 100644 --- a/lib/Target/RISCV/RISCVInstrInfoC.td +++ b/lib/Target/RISCV/RISCVInstrInfoC.td @@ -27,12 +27,26 @@ def uimmlog2xlennonzero : Operand, ImmLeaf(Imm) && (Imm != 0); + return isUInt<5>(Imm) && (Imm != 0); + }]; } def simm6 : Operand, ImmLeaf(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 simm6nonzero : Operand, @@ -40,6 +54,12 @@ def simm6nonzero : Operand, 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 { @@ -61,6 +81,13 @@ def c_lui_imm : Operand, 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. @@ -69,6 +96,12 @@ def uimm7_lsb00 : Operand, 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. @@ -77,6 +110,12 @@ def uimm8_lsb00 : Operand, 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. @@ -85,6 +124,12 @@ def uimm8_lsb000 : Operand, 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. @@ -92,6 +137,13 @@ def simm9_lsb0 : Operand { 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. @@ -100,6 +152,12 @@ def uimm9_lsb000 : Operand, 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 @@ -110,6 +168,12 @@ def uimm10_lsb00nonzero : Operand, 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. @@ -119,13 +183,25 @@ def simm10_lsb0000nonzero : Operand, 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 { +def simm12_lsb0 : Operand { 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(); + }]; } //===----------------------------------------------------------------------===// @@ -442,3 +518,188 @@ def C_SDSP : CStackStore<0b111, "c.sdsp", GPR, uimm9_lsb000> { } } // Predicates = [HasStdExtC] + +//===----------------------------------------------------------------------===// +// Compress Instruction tablegen backend. +//===----------------------------------------------------------------------===// + +class CompressPat { + dag Input = input; + dag Output = output; + list 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<(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/test/CodeGen/RISCV/alu32.ll b/test/CodeGen/RISCV/alu32.ll index 6ecd08878dd..f34efe932b8 100644 --- a/test/CodeGen/RISCV/alu32.ll +++ b/test/CodeGen/RISCV/alu32.ll @@ -2,6 +2,10 @@ ; RUN: llc -mtriple=riscv32 -verify-machineinstrs < %s \ ; RUN: | FileCheck %s -check-prefix=RV32I +; RUN: llc -mtriple=riscv32 -mattr=+c -filetype=obj < %s \ +; RUN: |llvm-objdump -d -triple=riscv32 -mattr=+c -riscv-no-aliases - \ +; RUN: | FileCheck -check-prefix=RV32IC %s + ; These tests are each targeted at a particular RISC-V ALU instruction. Other ; files in this folder exercise LLVM IR instructions that don't directly match a ; RISC-V instruction @@ -13,6 +17,10 @@ define i32 @addi(i32 %a) nounwind { ; RV32I: # %bb.0: ; RV32I-NEXT: addi a0, a0, 1 ; RV32I-NEXT: ret + +; RV32IC-LABEL: addi: +; RV32IC-NEXT: c.addi a0, 1 +; RV32IC-NEXT: c.jr ra %1 = add i32 %a, 1 ret i32 %1 } @@ -60,6 +68,11 @@ define i32 @andi(i32 %a) nounwind { ; RV32I: # %bb.0: ; RV32I-NEXT: andi a0, a0, 6 ; RV32I-NEXT: ret + + +; RV32IC-LABEL: andi: +; RV32IC: c.andi a0, 6 +; RV32IC: c.jr ra %1 = and i32 %a, 6 ret i32 %1 } @@ -69,6 +82,10 @@ define i32 @slli(i32 %a) nounwind { ; RV32I: # %bb.0: ; RV32I-NEXT: slli a0, a0, 7 ; RV32I-NEXT: ret + +; RV32IC-LABEL: slli: +; RV32IC-NEXT: slli a0, 7 +; RV32IC-NEXT: c.jr ra %1 = shl i32 %a, 7 ret i32 %1 } @@ -78,6 +95,10 @@ define i32 @srli(i32 %a) nounwind { ; RV32I: # %bb.0: ; RV32I-NEXT: srli a0, a0, 8 ; RV32I-NEXT: ret + +; RV32IC-LABEL: srli: +; RV32IC-NEXT: c.srli a0, 8 +; RV32IC-NEXT: c.jr ra %1 = lshr i32 %a, 8 ret i32 %1 } @@ -87,6 +108,10 @@ define i32 @srai(i32 %a) nounwind { ; RV32I: # %bb.0: ; RV32I-NEXT: srai a0, a0, 9 ; RV32I-NEXT: ret + +; RV32IC-LABEL: srai: +; RV32IC-NEXT: c.srai a0, 9 +; RV32IC-NEXT: c.jr ra %1 = ashr i32 %a, 9 ret i32 %1 } @@ -98,6 +123,11 @@ define i32 @add(i32 %a, i32 %b) nounwind { ; RV32I: # %bb.0: ; RV32I-NEXT: add a0, a0, a1 ; RV32I-NEXT: ret + +; RV32IC-LABEL: add: +; RV32IC-NEXT: c.add a0, a1 +; RV32IC-NEXT: c.jr ra + %1 = add i32 %a, %b ret i32 %1 } @@ -107,6 +137,10 @@ define i32 @sub(i32 %a, i32 %b) nounwind { ; RV32I: # %bb.0: ; RV32I-NEXT: sub a0, a0, a1 ; RV32I-NEXT: ret + +; RV32IC-LABEL: sub: +; RV32IC-NEXT: c.sub a0, a1 +; RV32IC-NEXT: c.jr ra %1 = sub i32 %a, %b ret i32 %1 } @@ -145,6 +179,10 @@ define i32 @xor(i32 %a, i32 %b) nounwind { ; RV32I: # %bb.0: ; RV32I-NEXT: xor a0, a0, a1 ; RV32I-NEXT: ret + +; RV32IC-LABEL: xor: +; RV32IC-NEXT: c.xor a0, a1 +; RV32IC-NEXT: c.jr ra %1 = xor i32 %a, %b ret i32 %1 } @@ -181,6 +219,10 @@ define i32 @and(i32 %a, i32 %b) nounwind { ; RV32I: # %bb.0: ; RV32I-NEXT: and a0, a0, a1 ; RV32I-NEXT: ret + +; RV32IC-LABEL: and: +; RV32IC-NEXT: c.and a0, a1 +; RV32IC-NEXT: c.jr ra %1 = and i32 %a, %b ret i32 %1 } diff --git a/test/CodeGen/RISCV/branch.ll b/test/CodeGen/RISCV/branch.ll index 53092c37561..f6d9299b07d 100644 --- a/test/CodeGen/RISCV/branch.ll +++ b/test/CodeGen/RISCV/branch.ll @@ -2,6 +2,11 @@ ; RUN: llc -mtriple=riscv32 -verify-machineinstrs < %s \ ; RUN: | FileCheck -check-prefix=RV32I %s + +; RUN: llc -mtriple=riscv32 -mattr=+c -filetype=obj < %s \ +; RUN: |llvm-objdump -d -triple=riscv32 -mattr=+c -riscv-no-aliases - \ +; RUN: | FileCheck -check-prefix=RV32IC %s + define void @foo(i32 %a, i32 *%b, i1 %c) { ; RV32I-LABEL: foo: ; RV32I: # %bb.0: @@ -43,6 +48,33 @@ define void @foo(i32 %a, i32 *%b, i1 %c) { ; RV32I-NEXT: .LBB0_12: # %end ; RV32I-NEXT: ret +; RV32IC-LABEL: foo: +; RV32IC: c.lw a3, 0(a1) +; RV32IC-NEXT: beq a3, a0, 68 +; RV32IC-NEXT: c.lw a3, 0(a1) +; RV32IC-NEXT: bne a3, a0, 62 +; RV32IC-NEXT: c.lw a3, 0(a1) +; RV32IC-NEXT: blt a3, a0, 56 +; RV32IC-NEXT: c.lw a3, 0(a1) +; RV32IC-NEXT: bge a3, a0, 50 +; RV32IC-NEXT: c.lw a3, 0(a1) +; RV32IC-NEXT: bltu a3, a0, 44 +; RV32IC-NEXT: c.lw a3, 0(a1) +; RV32IC-NEXT: bgeu a3, a0, 38 +; RV32IC-NEXT: c.lw a3, 0(a1) +; RV32IC-NEXT: blt a0, a3, 32 +; RV32IC-NEXT: c.lw a3, 0(a1) +; RV32IC-NEXT: bge a0, a3, 26 +; RV32IC-NEXT: c.lw a3, 0(a1) +; RV32IC-NEXT: bltu a0, a3, 20 +; RV32IC-NEXT: c.lw a3, 0(a1) +; RV32IC-NEXT: bgeu a0, a3, 14 +; RV32IC-NEXT: c.lw a0, 0(a1) +; RV32IC-NEXT: andi a0, a2, 1 +; RV32IC-NEXT: c.bnez a0, 4 +; RV32IC-NEXT: c.lw a0, 0(a1) +; RV32IC-NEXT: c.jr ra + %val1 = load volatile i32, i32* %b %tst1 = icmp eq i32 %val1, %a br i1 %tst1, label %end, label %test2 diff --git a/test/CodeGen/RISCV/compress-Pseudo.ll b/test/CodeGen/RISCV/compress-Pseudo.ll new file mode 100644 index 00000000000..bb25fe9d51d --- /dev/null +++ b/test/CodeGen/RISCV/compress-Pseudo.ll @@ -0,0 +1,10 @@ +; RUN: llc -mtriple=riscv32 -mattr=+c -riscv-no-aliases -o %t1 < %s +; RUN: FileCheck %s < %t1 + +define void @foo() { +; CHECK-LABEL: foo: +; CHECK: c.jr + +end: + ret void +} diff --git a/test/CodeGen/RISCV/compress-inline-asm.ll b/test/CodeGen/RISCV/compress-inline-asm.ll new file mode 100644 index 00000000000..9b84bdeaf5d --- /dev/null +++ b/test/CodeGen/RISCV/compress-inline-asm.ll @@ -0,0 +1,15 @@ +; RUN: llc -mtriple=riscv32 -mattr=+c -filetype=obj < %s\ +; RUN: | llvm-objdump -triple=riscv32 -mattr=+c -d -riscv-no-aliases -\ +; RUN: | FileCheck -check-prefix=CHECK %s + +@ext = external global i32 + +define i32 @compress_test(i32 %a) { +; CHECK-LABEL: compress_test: +; CHECK: c.add a0, a1 +; CHECK-NEXT: c.jr ra + %1 = load i32, i32* @ext + %2 = tail call i32 asm "add $0, $1, $2", "=r,r,r"(i32 %a, i32 %1) + ret i32 %2 +} + diff --git a/test/MC/RISCV/cnop.s b/test/MC/RISCV/cnop.s index 2a2755f55e1..1ac75ec7892 100644 --- a/test/MC/RISCV/cnop.s +++ b/test/MC/RISCV/cnop.s @@ -1,5 +1,5 @@ # RUN: llvm-mc -filetype=obj -triple riscv32 -mattr=+c < %s \ -# RUN: | llvm-objdump -d - | FileCheck -check-prefix=CHECK-INST %s +# RUN: | llvm-objdump -d -riscv-no-aliases - | FileCheck -check-prefix=CHECK-INST %s # alpha and main are 8 byte alignment # but the alpha function's size is 6 diff --git a/test/MC/RISCV/compress-cjal.s b/test/MC/RISCV/compress-cjal.s new file mode 100644 index 00000000000..a77297f6947 --- /dev/null +++ b/test/MC/RISCV/compress-cjal.s @@ -0,0 +1,17 @@ +# RUN: llvm-mc -triple riscv32 -mattr=+c -show-encoding < %s \ +# RUN: | FileCheck -check-prefixes=CHECK,CHECK-ALIAS %s +# RUN: llvm-mc -triple riscv32 -mattr=+c -show-encoding \ +# RUN: -riscv-no-aliases <%s | FileCheck -check-prefixes=CHECK,CHECK-INST %s +# RUN: llvm-mc -triple riscv32 -mattr=+c -filetype=obj < %s \ +# RUN: | llvm-objdump -triple riscv32 -mattr=+c -d - \ +# RUN: | FileCheck -check-prefixes=CHECK-BYTES,CHECK-ALIAS %s +# RUN: llvm-mc -triple riscv32 -mattr=+c -filetype=obj < %s \ +# RUN: | llvm-objdump -triple riscv32 -mattr=+c -d -riscv-no-aliases - \ +# RUN: | FileCheck -check-prefixes=CHECK-BYTES,CHECK-INST %s + +# c.jal is an rv32 only instruction. +jal ra, 2046 +# CHECK-BYTES: fd 2f +# CHECK-ALIAS: jal 2046 +# CHECK-INST: c.jal 2046 +# CHECK: # encoding: [0xfd,0x2f] diff --git a/test/MC/RISCV/compress-rv32d.s b/test/MC/RISCV/compress-rv32d.s new file mode 100644 index 00000000000..eac0321778e --- /dev/null +++ b/test/MC/RISCV/compress-rv32d.s @@ -0,0 +1,44 @@ +# RUN: llvm-mc -triple riscv32 -mattr=+c,+d -show-encoding < %s \ +# RUN: | FileCheck -check-prefixes=CHECK,CHECK-ALIAS %s +# RUN: llvm-mc -triple riscv32 -mattr=+c,+d -show-encoding \ +# RUN: -riscv-no-aliases <%s | FileCheck -check-prefixes=CHECK,CHECK-INST %s +# RUN: llvm-mc -triple riscv32 -mattr=+c,+d -filetype=obj < %s \ +# RUN: | llvm-objdump -triple riscv32 -mattr=+c,+d -d - \ +# RUN: | FileCheck -check-prefixes=CHECK-BYTES,CHECK-ALIAS %s +# RUN: llvm-mc -triple riscv32 -mattr=+c,+d -filetype=obj < %s \ +# RUN: | llvm-objdump -triple riscv32 -mattr=+c,+d -d -riscv-no-aliases - \ +# RUN: | FileCheck -check-prefixes=CHECK-BYTES,CHECK-INST %s + +# RUN: llvm-mc -triple riscv64 -mattr=+c,+d -show-encoding < %s \ +# RUN: | FileCheck -check-prefixes=CHECK-ALIAS %s +# RUN: llvm-mc -triple riscv64 -mattr=+c,+d -show-encoding \ +# RUN: -riscv-no-aliases <%s | FileCheck -check-prefixes=CHECK-INST %s +# RUN: llvm-mc -triple riscv64 -mattr=+c,+d -filetype=obj < %s \ +# RUN: | llvm-objdump -triple riscv64 -mattr=+c,+d -d - \ +# RUN: | FileCheck -check-prefixes=CHECK-BYTES,CHECK-ALIAS %s +# RUN: llvm-mc -triple riscv64 -mattr=+c,+d -filetype=obj < %s \ +# RUN: | llvm-objdump -triple riscv64 -mattr=+c,+d -d -riscv-no-aliases - \ +# RUN: | FileCheck -check-prefixes=CHECK-BYTES,CHECK-INST %s + +# Tests double precision floating point instructions available in rv32 and in rv64. + +fld ft0, 64(sp) +# CHECK-BYTES: 06 20 +# CHECK-ALIAS: fld ft0, 64(sp) +# CHECK-INST: c.fldsp ft0, 64(sp) +# CHECK: # encoding: [0x06,0x20] +fsd ft0, 64(sp) +# CHECK-BYTES: 82 a0 +# CHECK-ALIAS: fsd ft0, 64(sp) +# CHECK-INST: c.fsdsp ft0, 64(sp) +# CHECK: # encoding: [0x82,0xa0] +fld fs0, 248(s0) +# CHECK-BYTES: 60 3c +# CHECK-ALIAS: fld fs0, 248(s0) +# CHECK-INST: c.fld fs0, 248(s0) +# CHECK: # encoding: [0x60,0x3c] +fsd fs0, 248(s0) +# CHECK-BYTES: 60 bc +# CHECK-ALIAS: fsd fs0, 248(s0) +# CHECK-INST: c.fsd fs0, 248(s0) +# CHECK: # encoding: [0x60,0xbc] diff --git a/test/MC/RISCV/compress-rv32f.s b/test/MC/RISCV/compress-rv32f.s new file mode 100644 index 00000000000..482e528c9ee --- /dev/null +++ b/test/MC/RISCV/compress-rv32f.s @@ -0,0 +1,32 @@ +# RUN: llvm-mc -triple riscv32 -mattr=+c,+f -show-encoding < %s \ +# RUN: | FileCheck -check-prefixes=CHECK,CHECK-ALIAS %s +# RUN: llvm-mc -triple riscv32 -mattr=+c,+f -show-encoding \ +# RUN: -riscv-no-aliases <%s | FileCheck -check-prefixes=CHECK,CHECK-INST %s +# RUN: llvm-mc -triple riscv32 -mattr=+c,+f -filetype=obj < %s \ +# RUN: | llvm-objdump -triple riscv32 -mattr=+c,+f -d - \ +# RUN: | FileCheck -check-prefixes=CHECK-BYTES,CHECK-ALIAS %s +# RUN: llvm-mc -triple riscv32 -mattr=+c,+f -filetype=obj < %s \ +# RUN: | llvm-objdump -triple riscv32 -mattr=+c,+f -d -riscv-no-aliases - \ +# RUN: | FileCheck -check-prefixes=CHECK-BYTES,CHECK-INST %s + +# Instructions that are 32 bit only. +flw ft0, 124(sp) +# CHECK-BYTES: 76 70 +# CHECK-ALIAS: flw ft0, 124(sp) +# CHECK-INST: c.flwsp ft0, 124(sp) +# CHECK: # encoding: [0x76,0x70] +fsw ft0, 124(sp) +# CHECK-BYTES: 82 fe +# CHECK-ALIAS: fsw ft0, 124(sp) +# CHECK-INST: c.fswsp ft0, 124(sp) +# CHECK: # encoding: [0x82,0xfe] +flw fs0, 124(s0) +# CHECK-BYTES: 60 7c +# CHECK-ALIAS: flw fs0, 124(s0) +# CHECK-INST: c.flw fs0, 124(s0) +# CHECK: # encoding: [0x60,0x7c] +fsw fs0, 124(s0) +# CHECK-BYTES: 60 fc +# CHECK-ALIAS: fsw fs0, 124(s0) +# CHECK-INST: c.fsw fs0, 124(s0) +# CHECK: # encoding: [0x60,0xfc] diff --git a/test/MC/RISCV/compress-rv32i.s b/test/MC/RISCV/compress-rv32i.s new file mode 100644 index 00000000000..e368faf55d7 --- /dev/null +++ b/test/MC/RISCV/compress-rv32i.s @@ -0,0 +1,207 @@ +# RUN: llvm-mc -triple riscv32 -mattr=+c -show-encoding < %s \ +# RUN: | FileCheck -check-prefixes=CHECK,CHECK-ALIAS %s +# RUN: llvm-mc -triple riscv32 -mattr=+c -show-encoding \ +# RUN: -riscv-no-aliases <%s | FileCheck -check-prefixes=CHECK,CHECK-INST %s +# RUN: llvm-mc -triple riscv32 -mattr=+c -filetype=obj < %s \ +# RUN: | llvm-objdump -triple riscv32 -mattr=+c -d - \ +# RUN: | FileCheck -check-prefixes=CHECK-BYTES,CHECK-ALIAS %s +# RUN: llvm-mc -triple riscv32 -mattr=+c -filetype=obj < %s \ +# RUN: | llvm-objdump -triple riscv32 -mattr=+c -d -riscv-no-aliases - \ +# RUN: | FileCheck -check-prefixes=CHECK-BYTES,CHECK-INST %s + +# RUN: llvm-mc -triple riscv64 -mattr=+c -show-encoding < %s \ +# RUN: | FileCheck -check-prefixes=CHECK-ALIAS %s +# RUN: llvm-mc -triple riscv64 -mattr=+c -show-encoding \ +# RUN: -riscv-no-aliases <%s | FileCheck -check-prefixes=CHECK-INST %s +# RUN: llvm-mc -triple riscv64 -mattr=+c -filetype=obj < %s \ +# RUN: | llvm-objdump -triple riscv64 -mattr=+c -d - \ +# RUN: | FileCheck -check-prefixes=CHECK-BYTES,CHECK-ALIAS %s +# RUN: llvm-mc -triple riscv64 -mattr=+c -filetype=obj < %s \ +# RUN: | llvm-objdump -triple riscv64 -mattr=+c -d -riscv-no-aliases - \ +# RUN: | FileCheck -check-prefixes=CHECK-BYTES,CHECK-INST %s + +# CHECK-BYTES: e0 1f +# CHECK-ALIAS: addi s0, sp, 1020 +# CHECK-INST: c.addi4spn s0, sp, 1020 +# CHECK: # encoding: [0xe0,0x1f] +addi s0, sp, 1020 + +# CHECK-BYTES: e0 5f +# CHECK-ALIAS: lw s0, 124(a5) +# CHECK-INST: c.lw s0, 124(a5) +# CHECK: # encoding: [0xe0,0x5f] +lw s0, 124(a5) + +# CHECK-BYTES: e0 df +# CHECK-ALIAS: sw s0, 124(a5) +# CHECK-INST: c.sw s0, 124(a5) +# CHECK: # encoding: [0xe0,0xdf] +sw s0, 124(a5) + +# CHECK-BYTES: 01 00 +# CHECK-ALIAS: nop +# CHECK-INST: c.nop +# CHECK: # encoding: [0x01,0x00] +nop + +# CHECK-BYTES: 81 10 +# CHECK-ALIAS: addi ra, ra, -32 +# CHECK-INST: c.addi ra, -32 +# CHECK: # encoding: [0x81,0x10] +addi ra, ra, -32 + +# CHECK-BYTES: 85 50 +# CHECK-ALIAS: addi ra, zero, -31 +# CHECK-INST: c.li ra, -31 +# CHECK: # encoding: [0x85,0x50] +addi ra, zero, -31 + +# CHECK-BYTES: 39 71 +# CHECK-ALIAS: addi sp, sp, -64 +# CHECK-INST: c.addi16sp sp, -64 +# CHECK: # encoding: [0x39,0x71] +addi sp, sp, -64 + +# CHECK-BYTES: fd 61 +# CHECK-ALIAS: lui gp, 31 +# CHECK-INST: c.lui gp, 31 +# CHECK: # encoding: [0xfd,0x61] +lui gp, 31 + +# CHECK-BYTES: 7d 80 +# CHECK-ALIAS: srli s0, s0, 31 +# CHECK-INST: c.srli s0, 31 +# CHECK: # encoding: [0x7d,0x80] +srli s0, s0, 31 + +# CHECK-BYTES: 7d 84 +# CHECK-ALIAS: srai s0, s0, 31 +# CHECK-INST: c.srai s0, 31 +# CHECK: # encoding: [0x7d,0x84] +srai s0, s0, 31 + +# CHECK-BYTES: 7d 88 +# CHECK-ALIAS: andi s0, s0, 31 +# CHECK-INST: c.andi s0, 31 +# CHECK: # encoding: [0x7d,0x88] +andi s0, s0, 31 + +# CHECK-BYTES: 1d 8c +# CHECK-ALIAS: sub s0, s0, a5 +# CHECK-INST: c.sub s0, a5 +# CHECK: # encoding: [0x1d,0x8c] +sub s0, s0, a5 + +# CHECK-BYTES: 3d 8c +# CHECK-ALIAS: xor s0, s0, a5 +# CHECK-INST: c.xor s0, a5 +# CHECK: # encoding: [0x3d,0x8c] +xor s0, s0, a5 + +# CHECK-BYTES: 3d 8c +# CHECK-ALIAS: xor s0, s0, a5 +# CHECK-INST: c.xor s0, a5 +# CHECK: # encoding: [0x3d,0x8c] +xor s0, a5, s0 + +# CHECK-BYTES: 5d 8c +# CHECK-ALIAS: or s0, s0, a5 +# CHECK-INST: c.or s0, a5 +# CHECK: # encoding: [0x5d,0x8c] +or s0, s0, a5 + +# CHECK-BYTES: 45 8c +# CHECK-ALIAS: or s0, s0, s1 +# CHECK-INST: c.or s0, s1 +# CHECK: # encoding: [0x45,0x8c] +or s0, s1, s0 + +# CHECK-BYTES: 7d 8c +# CHECK-ALIAS: and s0, s0, a5 +# CHECK-INST: c.and s0, a5 +# CHECK: # encoding: [0x7d,0x8c] +and s0, s0, a5 + +# CHECK-BYTES: 7d 8c +# CHECK-ALIAS: and s0, s0, a5 +# CHECK-INST: c.and s0, a5 +# CHECK: # encoding: [0x7d,0x8c] +and s0, a5, s0 + +# CHECK-BYTES: 01 b0 +# CHECK-ALIAS: j -2048 +# CHECK-INST: c.j -2048 +# CHECK: # encoding: [0x01,0xb0] +jal zero, -2048 + +# CHECK-BYTES: 01 d0 +# CHECK-ALIAS: beqz s0, -256 +# CHECK-INST: c.beqz s0, -256 +# CHECK: # encoding: [0x01,0xd0] +beq s0, zero, -256 + +# CHECK-BYTES: 7d ec +# CHECk-ALIAS: bnez s0, 254 +# CHECK-INST: c.bnez s0, 254 +# CHECK: # encoding: [0x7d,0xec] +bne s0, zero, 254 + +# CHECK-BYTES: 7e 04 +# CHECK-ALIAS: slli s0, s0, 31 +# CHECK-INST: c.slli s0, 31 +# CHECK: # encoding: [0x7e,0x04] +slli s0, s0, 31 + +# CHECK-BYTES: fe 50 +# CHECK-ALIAS: lw ra, 252(sp) +# CHECK-INST: c.lwsp ra, 252(sp) +# CHECK: # encoding: [0xfe,0x50] +lw ra, 252(sp) + +# CHECK-BYTES: 82 80 +# CHECK-ALIAS: ret +# CHECK-INST: c.jr ra +# CHECK: # encoding: [0x82,0x80] +jalr zero, ra, 0 + +# CHECK-BYTES: 92 80 +# CHECK-ALIAS: add ra, zero, tp +# CHECK-INST: c.mv ra, tp +# CHECK: # encoding: [0x92,0x80] +add ra, zero, tp + +# CHECK-BYTES: 92 80 +# CHECK-ALIAS: add ra, zero, tp +# CHECK-INST: c.mv ra, tp +# CHECK: # encoding: [0x92,0x80] +add ra, tp, zero + +# CHECK-BYTES: 02 90 +# CHECK-ALIAS: ebreak +# CHECK-INST: c.ebreak +# CHECK: # encoding: [0x02,0x90] +ebreak + +# CHECK-BYTES: 02 94 +# CHECK-ALIAS: jalr s0 +# CHECK-INST: c.jalr s0 +# CHECK: # encoding: [0x02,0x94] +jalr ra, s0, 0 + +# CHECK-BYTES: 3e 94 +# CHECK-ALIAS: add s0, s0, a5 +# CHECK-INST: c.add s0, a5 +# CHECK: # encoding: [0x3e,0x94] +add s0, a5, s0 + +# CHECK-BYTES: 3e 94 +# CHECK-ALIAS: add s0, s0, a5 +# CHECK-INST: c.add s0, a5 +# CHECK: # encoding: [0x3e,0x94] +add s0, s0, a5 + +# CHECK-BYTES: 82 df +# CHECK-ALIAS: sw zero, 252(sp) +# CHECK-INST: c.swsp zero, 252(sp) +# CHECK: # encoding: [0x82,0xdf] +sw zero, 252(sp) diff --git a/test/MC/RISCV/compress-rv64i.s b/test/MC/RISCV/compress-rv64i.s new file mode 100644 index 00000000000..7e887ff80d6 --- /dev/null +++ b/test/MC/RISCV/compress-rv64i.s @@ -0,0 +1,60 @@ +# RUN: llvm-mc -triple riscv64 -mattr=+c -show-encoding < %s \ +# RUN: | FileCheck -check-prefixes=CHECK-ALIAS %s +# RUN: llvm-mc -triple riscv64 -mattr=+c -show-encoding \ +# RUN: -riscv-no-aliases <%s | FileCheck -check-prefixes=CHECK-INST %s +# RUN: llvm-mc -triple riscv64 -mattr=+c -filetype=obj < %s \ +# RUN: | llvm-objdump -triple riscv64 -mattr=+c -d - \ +# RUN: | FileCheck -check-prefixes=CHECK-BYTES,CHECK-ALIAS %s +# RUN: llvm-mc -triple riscv64 -mattr=+c -filetype=obj < %s \ +# RUN: | llvm-objdump -triple riscv64 -mattr=+c -d -riscv-no-aliases - \ +# RUN: | FileCheck -check-prefixes=CHECK-BYTES,CHECK-INST %s + +# Tests compressed instructions available in rv64 and not in rv32. + +# CHECK-BYTES: e0 7f +# CHECK-ALIAS: ld s0, 248(a5) +# CHECK-INST: c.ld s0, 248(a5) +# CHECK: # encoding: [0xe0,0x7f] +ld s0, 248(a5) + +# CHECK-BYTES: a0 e3 +# CHECK-ALIAS: sd s0, 64(a5) +# CHECK-INST: c.sd s0, 64(a5) +# CHECK: # encoding: [0xa0,0xe3] +sd s0, 64(a5) + +# CHECK-BYTES: 7d 22 +# CHEACK-ALIAS: addiw tp, tp, 31 +# CHECK-INST: c.addiw tp, 31 +# CHECK: # encoding: [0x7d,0x22] +addiw tp, tp, 31 + +# CHECK-BYTES: 1d 9c +# CHEACK-ALIAS: subw s0, s0, a5 +# CHECK-INST: c.subw s0, a5 +# CHECK: # encoding: [0x1d,0x9c] +subw s0, s0, a5 + +# CHECK-BYTES: 3d 9c +# CHECK-ALIAS: addw s0, s0, a5 +# CHECK-INST: c.addw s0, a5 +# CHECK: # encoding: [0x3d,0x9c] +addw s0, s0, a5 + +# CHECK-BYTES: 3d 9c +# CHECK-ALIAS: addw s0, s0, a5 +# CHECK-INST: c.addw s0, a5 +# CHECK: # encoding: [0x3d,0x9c] +addw s0, a5, s0 + +# CHECK-BYTES: ee 70 +# CHECK-ALIAS: ld ra, 248(sp) +# CHECK-INST: c.ldsp ra, 248(sp) +# CHECK: # encoding: [0xee,0x70] +ld ra, 248(sp) + +# CHECK-BYTES: a2 e0 +# CHECK-ALIAS: sd s0, 64(sp) +# CHECK-INST: c.sdsp s0, 64(sp) +# CHECK: # encoding: [0xa2,0xe0] +sd s0, 64(sp) diff --git a/test/MC/RISCV/compressed-relocations.s b/test/MC/RISCV/compressed-relocations.s new file mode 100644 index 00000000000..832a2085e61 --- /dev/null +++ b/test/MC/RISCV/compressed-relocations.s @@ -0,0 +1,20 @@ +# RUN: llvm-mc -triple riscv32 -mattr=+c -riscv-no-aliases < %s -show-encoding \ +# RUN: | FileCheck -check-prefix=INSTR -check-prefix=FIXUP %s +# RUN: llvm-mc -filetype=obj -triple riscv32 -mattr=+c < %s \ +# RUN: | llvm-readobj -r | FileCheck -check-prefix=RELOC %s + +# Check prefixes: +# RELOC - Check the relocation in the object. +# FIXUP - Check the fixup on the instruction. +# INSTR - Check the instruction is handled properly by the ASMPrinter +c.jal foo +# A compressed jump (c.j) to an unresolved symbol will be relaxed to a (jal). +# RELOC: R_RISCV_JAL +# INSTR: c.jal foo +# FIXUP: fixup A - offset: 0, value: foo, kind: fixup_riscv_rvc_jump + +c.bnez a0, foo +# A compressed branch (c.bnez) to an unresolved symbol will be relaxed to a (bnez). +# RELOC: R_RISCV_BRANCH +# INSTR: c.bnez a0, foo +# FIXUP: fixup A - offset: 0, value: foo, kind: fixup_riscv_rvc_branch diff --git a/test/MC/RISCV/fixups-compressed.s b/test/MC/RISCV/fixups-compressed.s index a97d290f216..7ae6274bf27 100644 --- a/test/MC/RISCV/fixups-compressed.s +++ b/test/MC/RISCV/fixups-compressed.s @@ -1,7 +1,7 @@ # RUN: llvm-mc %s -triple riscv32 -mattr=+c -show-encoding \ # RUN: | FileCheck -check-prefix=CHECK-FIXUP %s # RUN: llvm-mc -triple riscv32 -filetype=obj -mattr=+c < %s \ -# RUN: | llvm-objdump -d - | FileCheck -check-prefix=CHECK-INSTR %s +# RUN: | llvm-objdump -d -riscv-no-aliases - | FileCheck -check-prefix=CHECK-INSTR %s # RUN: llvm-mc -filetype=obj -mattr=+c -triple=riscv32 %s \ # RUN: | llvm-readobj -r | FileCheck %s -check-prefix=CHECK-REL diff --git a/test/MC/RISCV/relocations.s b/test/MC/RISCV/relocations.s index 77421620e68..b68b11bf195 100644 --- a/test/MC/RISCV/relocations.s +++ b/test/MC/RISCV/relocations.s @@ -1,4 +1,4 @@ -# RUN: llvm-mc -triple riscv32 -mattr=+c -riscv-no-aliases < %s -show-encoding \ +# RUN: llvm-mc -triple riscv32 -riscv-no-aliases < %s -show-encoding \ # RUN: | FileCheck -check-prefix=INSTR -check-prefix=FIXUP %s # RUN: llvm-mc -filetype=obj -triple riscv32 -mattr=+c < %s \ # RUN: | llvm-readobj -r | FileCheck -check-prefix=RELOC %s @@ -83,15 +83,3 @@ bgeu a0, a1, foo # RELOC: R_RISCV_BRANCH # INSTR: bgeu a0, a1, foo # FIXUP: fixup A - offset: 0, value: foo, kind: fixup_riscv_branch - -c.jal foo -# A compressed jump (c.j) to an unresolved symbol will be relaxed to a (jal). -# RELOC: R_RISCV_JAL -# INSTR: c.jal foo -# FIXUP: fixup A - offset: 0, value: foo, kind: fixup_riscv_rvc_jump - -c.bnez a0, foo -# A compressed branch (c.bnez) to an unresolved symbol will be relaxed to a (bnez). -# RELOC: R_RISCV_BRANCH -# INSTR: c.bnez a0, foo -# FIXUP: fixup A - offset: 0, value: foo, kind: fixup_riscv_rvc_branch diff --git a/test/MC/RISCV/rv32-relaxation.s b/test/MC/RISCV/rv32-relaxation.s index 66109faf3eb..8b1675d3389 100644 --- a/test/MC/RISCV/rv32-relaxation.s +++ b/test/MC/RISCV/rv32-relaxation.s @@ -1,5 +1,5 @@ # RUN: llvm-mc -filetype=obj -triple riscv32 -mattr=+c < %s \ -# RUN: | llvm-objdump -d - | FileCheck -check-prefix=INSTR %s +# RUN: | llvm-objdump -d -riscv-no-aliases - | FileCheck -check-prefix=INSTR %s FAR_JUMP_NEGATIVE: c.nop @@ -18,26 +18,26 @@ start: c.bnez a0, NEAR_NEGATIVE #INSTR: c.bnez a0, -4 c.bnez a0, FAR_BRANCH -#INSTR-NEXT: bnez a0, 326 +#INSTR-NEXT: bne a0, zero, 326 c.bnez a0, FAR_BRANCH_NEGATIVE -#INSTR-NEXT: bnez a0, -268 +#INSTR-NEXT: bne a0, zero, -268 c.bnez a0, FAR_JUMP -#INSTR-NEXT: bnez a0, 2320 +#INSTR-NEXT: bne a0, zero, 2320 c.bnez a0, FAR_JUMP_NEGATIVE -#INSTR-NEXT: bnez a0, -2278 +#INSTR-NEXT: bne a0, zero, -2278 c.beqz a0, NEAR #INSTR-NEXT: c.beqz a0, 52 c.beqz a0, NEAR_NEGATIVE #INSTR-NEXT: c.beqz a0, -24 c.beqz a0, FAR_BRANCH -#INSTR-NEXT: beqz a0, 306 +#INSTR-NEXT: beq a0, zero, 306 c.beqz a0, FAR_BRANCH_NEGATIVE -#INSTR-NEXT: beqz a0, -288 +#INSTR-NEXT: beq a0, zero, -288 c.beqz a0, FAR_JUMP -#INSTR-NEXT: beqz a0, 2300 +#INSTR-NEXT: beq a0, zero, 2300 c.beqz a0, FAR_JUMP_NEGATIVE -#INSTR-NEXT: beqz a0, -2298 +#INSTR-NEXT: beq a0, zero, -2298 c.j NEAR #INSTR-NEXT: c.j 32 @@ -48,9 +48,9 @@ start: c.j FAR_BRANCH_NEGATIVE #INSTR-NEXT: c.j -306 c.j FAR_JUMP -#INSTR-NEXT: j 2284 +#INSTR-NEXT: jal zero, 2284 c.j FAR_JUMP_NEGATIVE -#INSTR-NEXT: j -2314 +#INSTR-NEXT: jal zero, -2314 c.jal NEAR #INSTR: c.jal 16 @@ -61,9 +61,9 @@ start: c.jal FAR_BRANCH_NEGATIVE #INSTR-NEXT: c.jal -322 c.jal FAR_JUMP -#INSTR-NEXT: jal 2268 +#INSTR-NEXT: jal ra, 2268 c.jal FAR_JUMP_NEGATIVE -#INSTR-NEXT: jal -2330 +#INSTR-NEXT: jal ra, -2330 NEAR: c.nop diff --git a/test/MC/RISCV/rv64-relaxation.s b/test/MC/RISCV/rv64-relaxation.s index 018408f575a..f0551244665 100644 --- a/test/MC/RISCV/rv64-relaxation.s +++ b/test/MC/RISCV/rv64-relaxation.s @@ -1,5 +1,5 @@ # RUN: llvm-mc -filetype=obj -triple riscv64 -mattr=+c < %s \ -# RUN: | llvm-objdump -d - | FileCheck -check-prefix=INSTR %s +# RUN: | llvm-objdump -d -riscv-no-aliases - | FileCheck -check-prefix=INSTR %s FAR_JUMP_NEGATIVE: c.nop @@ -18,26 +18,26 @@ start: c.bnez a0, NEAR_NEGATIVE #INSTR: c.bnez a0, -4 c.bnez a0, FAR_BRANCH -#INSTR-NEXT: bnez a0, 310 +#INSTR-NEXT: bne a0, zero, 310 c.bnez a0, FAR_BRANCH_NEGATIVE -#INSTR-NEXT: bnez a0, -268 +#INSTR-NEXT: bne a0, zero, -268 c.bnez a0, FAR_JUMP -#INSTR-NEXT: bnez a0, 2304 +#INSTR-NEXT: bne a0, zero, 2304 c.bnez a0, FAR_JUMP_NEGATIVE -#INSTR-NEXT: bnez a0, -2278 +#INSTR-NEXT: bne a0, zero, -2278 c.beqz a0, NEAR #INSTR-NEXT: c.beqz a0, 36 c.beqz a0, NEAR_NEGATIVE #INSTR-NEXT: c.beqz a0, -24 c.beqz a0, FAR_BRANCH -#INSTR-NEXT: beqz a0, 290 +#INSTR-NEXT: beq a0, zero, 290 c.beqz a0, FAR_BRANCH_NEGATIVE -#INSTR-NEXT: beqz a0, -288 +#INSTR-NEXT: beq a0, zero, -288 c.beqz a0, FAR_JUMP -#INSTR-NEXT: beqz a0, 2284 +#INSTR-NEXT: beq a0, zero, 2284 c.beqz a0, FAR_JUMP_NEGATIVE -#INSTR-NEXT: beqz a0, -2298 +#INSTR-NEXT: beq a0, zero, -2298 c.j NEAR #INSTR-NEXT: c.j 16 @@ -48,9 +48,9 @@ start: c.j FAR_BRANCH_NEGATIVE #INSTR-NEXT: c.j -306 c.j FAR_JUMP -#INSTR-NEXT: j 2268 +#INSTR-NEXT: jal zero, 2268 c.j FAR_JUMP_NEGATIVE -#INSTR-NEXT: j -2314 +#INSTR-NEXT: jal zero, -2314 NEAR: c.nop diff --git a/utils/TableGen/CMakeLists.txt b/utils/TableGen/CMakeLists.txt index 0944d54a427..36bad4441ac 100644 --- a/utils/TableGen/CMakeLists.txt +++ b/utils/TableGen/CMakeLists.txt @@ -30,6 +30,7 @@ add_tablegen(llvm-tblgen LLVM IntrinsicEmitter.cpp OptParserEmitter.cpp PseudoLoweringEmitter.cpp + RISCVCompressInstEmitter.cpp RegisterBankEmitter.cpp RegisterInfoEmitter.cpp SDNodeProperties.cpp diff --git a/utils/TableGen/RISCVCompressInstEmitter.cpp b/utils/TableGen/RISCVCompressInstEmitter.cpp new file mode 100644 index 00000000000..c362390a56d --- /dev/null +++ b/utils/TableGen/RISCVCompressInstEmitter.cpp @@ -0,0 +1,806 @@ +//===- RISCVCompressInstEmitter.cpp - Generator for RISCV Compression -===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +// RISCVCompressInstEmitter implements a tablegen-driven CompressPat based +// RISCV Instruction Compression mechanism. +// +//===--------------------------------------------------------------===// +// +// RISCVCompressInstEmitter implements a tablegen-driven CompressPat Instruction +// Compression mechanism for generating RISCV compressed instructions +// (C ISA Extension) from the expanded instruction form. + +// This tablegen backend processes CompressPat declarations in a +// td file and generates all the required checks to validate the pattern +// declarations; validate the input and output operands to generate the correct +// compressed instructions. The checks include validating different types of +// operands; register operands, immediate operands, fixed register and fixed +// immediate inputs. +// +// Example: +// class CompressPat { +// dag Input = input; +// dag Output = output; +// list Predicates = []; +// } +// +// let Predicates = [HasStdExtC] in { +// def : CompressPat<(ADD GPRNoX0:$rs1, GPRNoX0:$rs1, GPRNoX0:$rs2), +// (C_ADD GPRNoX0:$rs1, GPRNoX0:$rs2)>; +// } +// +// The result is an auto-generated header file +// 'RISCVGenCompressInstEmitter.inc' which exports two functions for +// compressing/uncompressing MCInst instructions, plus +// some helper functions: +// +// bool compressInst(MCInst& OutInst, const MCInst &MI, +// const MCSubtargetInfo &STI, +// MCContext &Context); +// +// bool uncompressInst(MCInst& OutInst, const MCInst &MI, +// const MCRegisterInfo &MRI, +// const MCSubtargetInfo &STI); +// +// The clients that include this auto-generated header file and +// invoke these functions can compress an instruction before emitting +// it in the target-specific ASM or ELF streamer or can uncompress +// an instruction before printing it when the expanded instruction +// format aliases is favored. + +//===----------------------------------------------------------------------===// + +#include "CodeGenInstruction.h" +#include "CodeGenTarget.h" +#include "llvm/ADT/IndexedMap.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/ADT/StringMap.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/TableGen/Error.h" +#include "llvm/TableGen/Record.h" +#include "llvm/TableGen/TableGenBackend.h" +#include +using namespace llvm; + +#define DEBUG_TYPE "compress-inst-emitter" + +namespace { +class RISCVCompressInstEmitter { + struct OpData { + enum MapKind { Operand, Imm, Reg }; + MapKind Kind; + union { + unsigned Operand; // Operand number mapped to. + uint64_t Imm; // Integer immediate value. + Record *Reg; // Physical register. + } Data; + int TiedOpIdx = -1; // Tied operand index within the instruction. + }; + struct CompressPat { + CodeGenInstruction Source; // The source instruction definition. + CodeGenInstruction Dest; // The destination instruction to transform to. + std::vector + PatReqFeatures; // Required target features to enable pattern. + IndexedMap + SourceOperandMap; // Maps operands in the Source Instruction to + // the corresponding Dest instruction operand. + IndexedMap + DestOperandMap; // Maps operands in the Dest Instruction + // to the corresponding Source instruction operand. + CompressPat(CodeGenInstruction &S, CodeGenInstruction &D, + std::vector RF, IndexedMap &SourceMap, + IndexedMap &DestMap) + : Source(S), Dest(D), PatReqFeatures(RF), SourceOperandMap(SourceMap), + DestOperandMap(DestMap) {} + }; + + RecordKeeper &Records; + CodeGenTarget Target; + SmallVector CompressPatterns; + + void addDagOperandMapping(Record *Rec, DagInit *Dag, CodeGenInstruction &Inst, + IndexedMap &OperandMap, bool IsSourceInst); + void evaluateCompressPat(Record *Compress); + void emitCompressInstEmitter(raw_ostream &o, bool Compress); + bool validateTypes(Record *SubType, Record *Type, bool IsSourceInst); + bool validateRegister(Record *Reg, Record *RegClass); + void createDagOperandMapping(Record *Rec, StringMap &SourceOperands, + StringMap &DestOperands, + DagInit *SourceDag, DagInit *DestDag, + IndexedMap &SourceOperandMap); + + void createInstOperandMapping(Record *Rec, DagInit *SourceDag, + DagInit *DestDag, + IndexedMap &SourceOperandMap, + IndexedMap &DestOperandMap, + StringMap &SourceOperands, + CodeGenInstruction &DestInst); + +public: + RISCVCompressInstEmitter(RecordKeeper &R) : Records(R), Target(R) {} + + void run(raw_ostream &o); +}; +} // End anonymous namespace. + +bool RISCVCompressInstEmitter::validateRegister(Record *Reg, Record *RegClass) { + assert(Reg->isSubClassOf("Register") && "Reg record should be a Register\n"); + assert(RegClass->isSubClassOf("RegisterClass") && "RegClass record should be" + " a RegisterClass\n"); + CodeGenRegisterClass RC = Target.getRegisterClass(RegClass); + const CodeGenRegister *R = Target.getRegisterByName(Reg->getName().lower()); + assert((R != nullptr) && + ("Register" + Reg->getName().str() + " not defined!!\n").c_str()); + return RC.contains(R); +} + +bool RISCVCompressInstEmitter::validateTypes(Record *DagOpType, + Record *InstOpType, + bool IsSourceInst) { + if (DagOpType == InstOpType) + return true; + // Only source instruction operands are allowed to not match Input Dag + // operands. + if (!IsSourceInst) + return false; + + if (DagOpType->isSubClassOf("RegisterClass") && + InstOpType->isSubClassOf("RegisterClass")) { + CodeGenRegisterClass RC = Target.getRegisterClass(InstOpType); + CodeGenRegisterClass SubRC = Target.getRegisterClass(DagOpType); + return RC.hasSubClass(&SubRC); + } + + // At this point either or both types are not registers, reject the pattern. + if (DagOpType->isSubClassOf("RegisterClass") || + InstOpType->isSubClassOf("RegisterClass")) + return false; + + // Let further validation happen when compress()/uncompress() functions are + // invoked. + DEBUG(dbgs() << (IsSourceInst ? "Input" : "Output") << " Dag Operand Type: '" + << DagOpType->getName() << "' and " + << "Instruction Operand Type: '" << InstOpType->getName() + << "' can't be checked at pattern validation time!\n"); + return true; +} + +/// The patterns in the Dag contain different types of operands: +/// Register operands, e.g.: GPRC:$rs1; Fixed registers, e.g: X1; Immediate +/// operands, e.g.: simm6:$imm; Fixed immediate operands, e.g.: 0. This function +/// maps Dag operands to its corresponding instruction operands. For register +/// operands and fixed registers it expects the Dag operand type to be contained +/// in the instantiated instruction operand type. For immediate operands and +/// immediates no validation checks are enforced at pattern validation time. +void RISCVCompressInstEmitter::addDagOperandMapping( + Record *Rec, DagInit *Dag, CodeGenInstruction &Inst, + IndexedMap &OperandMap, bool IsSourceInst) { + // TiedCount keeps track of the number of operands skipped in Inst + // operands list to get to the corresponding Dag operand. This is + // necessary because the number of operands in Inst might be greater + // than number of operands in the Dag due to how tied operands + // are represented. + unsigned TiedCount = 0; + for (unsigned i = 0, e = Inst.Operands.size(); i != e; ++i) { + int TiedOpIdx = Inst.Operands[i].getTiedRegister(); + if (-1 != TiedOpIdx) { + // Set the entry in OperandMap for the tied operand we're skipping. + OperandMap[i].Kind = OperandMap[TiedOpIdx].Kind; + OperandMap[i].Data = OperandMap[TiedOpIdx].Data; + TiedCount++; + continue; + } + if (DefInit *DI = dyn_cast(Dag->getArg(i - TiedCount))) { + if (DI->getDef()->isSubClassOf("Register")) { + // Check if the fixed register belongs to the Register class. + if (!validateRegister(DI->getDef(), Inst.Operands[i].Rec)) + PrintFatalError(Rec->getLoc(), + "Error in Dag '" + Dag->getAsString() + + "'Register: '" + DI->getDef()->getName() + + "' is not in register class '" + + Inst.Operands[i].Rec->getName() + "'"); + OperandMap[i].Kind = OpData::Reg; + OperandMap[i].Data.Reg = DI->getDef(); + continue; + } + // Validate that Dag operand type matches the type defined in the + // corresponding instruction. Operands in the input Dag pattern are + // allowed to be a subclass of the type specified in corresponding + // instruction operand instead of being an exact match. + if (!validateTypes(DI->getDef(), Inst.Operands[i].Rec, IsSourceInst)) + PrintFatalError(Rec->getLoc(), + "Error in Dag '" + Dag->getAsString() + "'. Operand '" + + Dag->getArgNameStr(i - TiedCount) + "' has type '" + + DI->getDef()->getName() + + "' which does not match the type '" + + Inst.Operands[i].Rec->getName() + + "' in the corresponding instruction operand!"); + + OperandMap[i].Kind = OpData::Operand; + } else if (IntInit *II = dyn_cast(Dag->getArg(i - TiedCount))) { + // Validate that corresponding instruction operand expects an immediate. + if (Inst.Operands[i].Rec->isSubClassOf("RegisterClass")) + PrintFatalError( + Rec->getLoc(), + ("Error in Dag '" + Dag->getAsString() + "' Found immediate: '" + + II->getAsString() + + "' but corresponding instruction operand expected a register!")); + // No pattern validation check possible for values of fixed immediate. + OperandMap[i].Kind = OpData::Imm; + OperandMap[i].Data.Imm = II->getValue(); + DEBUG(dbgs() << " Found immediate '" << II->getValue() << "' at " + << (IsSourceInst ? "input " : "output ") + << "Dag. No validation time check possible for values of " + "fixed immediate.\n"); + } else + llvm_unreachable("Unhandled CompressPat argument type!"); + } +} + +// Verify the Dag operand count is enough to build an instruction. +static bool verifyDagOpCount(CodeGenInstruction &Inst, DagInit *Dag, + bool IsSource) { + if (Dag->getNumArgs() == Inst.Operands.size()) + return true; + // Source instructions are non compressed instructions and don't have tied + // operands. + if (IsSource) + PrintFatalError("Input operands for Inst '" + Inst.TheDef->getName() + + "' and input Dag operand count mismatch"); + // The Dag can't have more arguments than the Instruction. + if (Dag->getNumArgs() > Inst.Operands.size()) + PrintFatalError("Inst '" + Inst.TheDef->getName() + + "' and Dag operand count mismatch"); + + // The Instruction might have tied operands so the Dag might have + // a fewer operand count. + unsigned RealCount = Inst.Operands.size(); + for (unsigned i = 0; i < Inst.Operands.size(); i++) + if (Inst.Operands[i].getTiedRegister() != -1) + --RealCount; + + if (Dag->getNumArgs() != RealCount) + PrintFatalError("Inst '" + Inst.TheDef->getName() + + "' and Dag operand count mismatch"); + return true; +} + +static bool validateArgsTypes(Init *Arg1, Init *Arg2) { + DefInit *Type1 = dyn_cast(Arg1); + DefInit *Type2 = dyn_cast(Arg2); + assert(Type1 && ("Arg1 type not found\n")); + assert(Type2 && ("Arg2 type not found\n")); + return Type1->getDef() == Type2->getDef(); +} + +// Creates a mapping between the operand name in the Dag (e.g. $rs1) and +// its index in the list of Dag operands and checks that operands with the same +// name have the same types. For example in 'C_ADD $rs1, $rs2' we generate the +// mapping $rs1 --> 0, $rs2 ---> 1. If the operand appears twice in the (tied) +// same Dag we use the last occurrence for indexing. +void RISCVCompressInstEmitter::createDagOperandMapping( + Record *Rec, StringMap &SourceOperands, + StringMap &DestOperands, DagInit *SourceDag, DagInit *DestDag, + IndexedMap &SourceOperandMap) { + for (unsigned i = 0; i < DestDag->getNumArgs(); ++i) { + // Skip fixed immediates and registers, they were handled in + // addDagOperandMapping. + if ("" == DestDag->getArgNameStr(i)) + continue; + DestOperands[DestDag->getArgNameStr(i)] = i; + } + + for (unsigned i = 0; i < SourceDag->getNumArgs(); ++i) { + // Skip fixed immediates and registers, they were handled in + // addDagOperandMapping. + if ("" == SourceDag->getArgNameStr(i)) + continue; + + StringMap::iterator it = + SourceOperands.find(SourceDag->getArgNameStr(i)); + if (it != SourceOperands.end()) { + // Operand sharing the same name in the Dag should be mapped as tied. + SourceOperandMap[i].TiedOpIdx = it->getValue(); + if (!validateArgsTypes(SourceDag->getArg(it->getValue()), + SourceDag->getArg(i))) + PrintFatalError(Rec->getLoc(), + "Input Operand '" + SourceDag->getArgNameStr(i) + + "' has a mismatched tied operand!\n"); + } + it = DestOperands.find(SourceDag->getArgNameStr(i)); + if (it == DestOperands.end()) + PrintFatalError(Rec->getLoc(), "Operand " + SourceDag->getArgNameStr(i) + + " defined in Input Dag but not used in" + " Output Dag!\n"); + // Input Dag operand types must match output Dag operand type. + if (!validateArgsTypes(DestDag->getArg(it->getValue()), + SourceDag->getArg(i))) + PrintFatalError(Rec->getLoc(), "Type mismatch between Input and " + "Output Dag operand '" + + SourceDag->getArgNameStr(i) + "'!"); + SourceOperands[SourceDag->getArgNameStr(i)] = i; + } +} + +/// Map operand names in the Dag to their index in both corresponding input and +/// output instructions. Validate that operands defined in the input are +/// used in the output pattern while populating the maps. +void RISCVCompressInstEmitter::createInstOperandMapping( + Record *Rec, DagInit *SourceDag, DagInit *DestDag, + IndexedMap &SourceOperandMap, IndexedMap &DestOperandMap, + StringMap &SourceOperands, CodeGenInstruction &DestInst) { + // TiedCount keeps track of the number of operands skipped in Inst + // operands list to get to the corresponding Dag operand. + unsigned TiedCount = 0; + DEBUG(dbgs() << " Operand mapping:\n Source Dest\n"); + for (unsigned i = 0, e = DestInst.Operands.size(); i != e; ++i) { + int TiedInstOpIdx = DestInst.Operands[i].getTiedRegister(); + if (TiedInstOpIdx != -1) { + ++TiedCount; + DestOperandMap[i].Data = DestOperandMap[TiedInstOpIdx].Data; + DestOperandMap[i].Kind = DestOperandMap[TiedInstOpIdx].Kind; + if (DestOperandMap[i].Kind == OpData::Operand) + // No need to fill the SourceOperandMap here since it was mapped to + // destination operand 'TiedInstOpIdx' in a previous iteration. + DEBUG(dbgs() << " " << DestOperandMap[i].Data.Operand << " ====> " + << i << " Dest operand tied with operand '" + << TiedInstOpIdx << "'\n"); + continue; + } + // Skip fixed immediates and registers, they were handled in + // addDagOperandMapping. + if (DestOperandMap[i].Kind != OpData::Operand) + continue; + + unsigned DagArgIdx = i - TiedCount; + StringMap::iterator SourceOp = + SourceOperands.find(DestDag->getArgNameStr(DagArgIdx)); + if (SourceOp == SourceOperands.end()) + PrintFatalError(Rec->getLoc(), + "Output Dag operand '" + + DestDag->getArgNameStr(DagArgIdx) + + "' has no matching input Dag operand."); + + assert(DestDag->getArgNameStr(DagArgIdx) == + SourceDag->getArgNameStr(SourceOp->getValue()) && + "Incorrect operand mapping detected!\n"); + DestOperandMap[i].Data.Operand = SourceOp->getValue(); + SourceOperandMap[SourceOp->getValue()].Data.Operand = i; + DEBUG(dbgs() << " " << SourceOp->getValue() << " ====> " << i << "\n"); + } +} + +/// Validates the CompressPattern and create operand mapping. +/// These are the checks to validate a CompressPat pattern declarations. +/// Error out with message under these conditions: +/// - Dag Input opcode is an expanded instruction and Dag Output opcode is a +/// compressed instruction. +/// - Operands in Dag Input must be all used in Dag Output. +/// Register Operand type in Dag Input Type must be contained in the +/// corresponding Source Instruction type. +/// - Register Operand type in Dag Input must be the same as in Dag Ouput. +/// - Register Operand type in Dag Output must be the same as the +/// corresponding Destination Inst type. +/// - Immediate Operand type in Dag Input must be the same as in Dag Ouput. +/// - Immediate Operand type in Dag Ouput must be the same as the corresponding +/// Destination Instruction type. +/// - Fixed register must be contained in the corresponding Source Instruction +/// type. +/// - Fixed register must be contained in the corresponding Destination +/// Instruction type. Warning message printed under these conditions: +/// - Fixed immediate in Dag Input or Dag Ouput cannot be checked at this time +/// and generate warning. +/// - Immediate operand type in Dag Input differs from the corresponding Source +/// Instruction type and generate a warning. +void RISCVCompressInstEmitter::evaluateCompressPat(Record *Rec) { + // Validate input Dag operands. + DagInit *SourceDag = Rec->getValueAsDag("Input"); + assert(SourceDag && "Missing 'Input' in compress pattern!"); + DEBUG(dbgs() << "Input: " << *SourceDag << "\n"); + + DefInit *OpDef = dyn_cast(SourceDag->getOperator()); + if (!OpDef) + PrintFatalError(Rec->getLoc(), + Rec->getName() + " has unexpected operator type!"); + // Checking we are transforming from compressed to uncompressed instructions. + Record *Operator = OpDef->getDef(); + if (!Operator->isSubClassOf("RVInst")) + PrintFatalError(Rec->getLoc(), "Input instruction '" + Operator->getName() + + "' is not a 32 bit wide instruction!"); + CodeGenInstruction SourceInst(Operator); + verifyDagOpCount(SourceInst, SourceDag, true); + + // Validate output Dag operands. + DagInit *DestDag = Rec->getValueAsDag("Output"); + assert(DestDag && "Missing 'Output' in compress pattern!"); + DEBUG(dbgs() << "Output: " << *DestDag << "\n"); + + DefInit *DestOpDef = dyn_cast(DestDag->getOperator()); + if (!DestOpDef) + PrintFatalError(Rec->getLoc(), + Rec->getName() + " has unexpected operator type!"); + + Record *DestOperator = DestOpDef->getDef(); + if (!DestOperator->isSubClassOf("RVInst16")) + PrintFatalError(Rec->getLoc(), "Output instruction '" + + DestOperator->getName() + + "' is not a 16 bit wide instruction!"); + CodeGenInstruction DestInst(DestOperator); + verifyDagOpCount(DestInst, DestDag, false); + + // Fill the mapping from the source to destination instructions. + + IndexedMap SourceOperandMap; + SourceOperandMap.grow(SourceInst.Operands.size()); + // Create a mapping between source Dag operands and source Inst operands. + addDagOperandMapping(Rec, SourceDag, SourceInst, SourceOperandMap, + /*IsSourceInst*/ true); + + IndexedMap DestOperandMap; + DestOperandMap.grow(DestInst.Operands.size()); + // Create a mapping between destination Dag operands and destination Inst + // operands. + addDagOperandMapping(Rec, DestDag, DestInst, DestOperandMap, + /*IsSourceInst*/ false); + + StringMap SourceOperands; + StringMap DestOperands; + createDagOperandMapping(Rec, SourceOperands, DestOperands, SourceDag, DestDag, + SourceOperandMap); + // Create operand mapping between the source and destination instructions. + createInstOperandMapping(Rec, SourceDag, DestDag, SourceOperandMap, + DestOperandMap, SourceOperands, DestInst); + + // Get the target features for the CompressPat. + std::vector PatReqFeatures; + std::vector RF = Rec->getValueAsListOfDefs("Predicates"); + copy_if(RF, std::back_inserter(PatReqFeatures), [](Record *R) { + return R->getValueAsBit("AssemblerMatcherPredicate"); + }); + + CompressPatterns.push_back(CompressPat(SourceInst, DestInst, PatReqFeatures, + SourceOperandMap, DestOperandMap)); +} + +static void getReqFeatures(std::map &FeaturesMap, + const std::vector &ReqFeatures) { + for (auto &R : ReqFeatures) { + StringRef AsmCondString = R->getValueAsString("AssemblerCondString"); + + // AsmCondString has syntax [!]F(,[!]F)* + SmallVector Ops; + SplitString(AsmCondString, Ops, ","); + assert(!Ops.empty() && "AssemblerCondString cannot be empty"); + + for (auto &Op : Ops) { + assert(!Op.empty() && "Empty operator"); + if (FeaturesMap.find(Op) == FeaturesMap.end()) + FeaturesMap[Op] = FeaturesMap.size(); + } + } +} + +unsigned getMCOpPredicate(DenseMap &MCOpPredicateMap, + std::vector &MCOpPredicates, + Record *Rec) { + unsigned Entry = MCOpPredicateMap[Rec]; + if (Entry) + return Entry; + + if (!Rec->isValueUnset("MCOperandPredicate")) { + MCOpPredicates.push_back(Rec); + Entry = MCOpPredicates.size(); + MCOpPredicateMap[Rec] = Entry; + return Entry; + } + + PrintFatalError(Rec->getLoc(), + "No MCOperandPredicate on this operand at all: " + + Rec->getName().str() + "'"); + return 0; +} + +static std::string mergeCondAndCode(raw_string_ostream &CondStream, + raw_string_ostream &CodeStream) { + std::string S; + raw_string_ostream CombinedStream(S); + CombinedStream.indent(4) + << "if (" + << CondStream.str().substr( + 6, CondStream.str().length() - + 10) // remove first indentation and last '&&'. + << ") {\n"; + CombinedStream << CodeStream.str(); + CombinedStream.indent(4) << " return true;\n"; + CombinedStream.indent(4) << "} // if\n"; + return CombinedStream.str(); +} + +void RISCVCompressInstEmitter::emitCompressInstEmitter(raw_ostream &o, + bool Compress) { + Record *AsmWriter = Target.getAsmWriter(); + if (!AsmWriter->getValueAsInt("PassSubtarget")) + PrintFatalError("'PassSubtarget' is false. SubTargetInfo object is needed " + "for target features.\n"); + + std::string Namespace = Target.getName(); + + // Sort entries in CompressPatterns to handle instructions that can have more + // than one candidate for compression\uncompression, e.g ADD can be + // transformed to a C_ADD or a C_MV. When emitting 'uncompress()' function the + // source and destination are flipped and the sort key needs to change + // accordingly. + std::stable_sort(CompressPatterns.begin(), CompressPatterns.end(), + [Compress](const CompressPat &LHS, const CompressPat &RHS) { + if (Compress) + return (LHS.Source.TheDef->getName().str() < + RHS.Source.TheDef->getName().str()); + else + return (LHS.Dest.TheDef->getName().str() < + RHS.Dest.TheDef->getName().str()); + }); + + // A list of MCOperandPredicates for all operands in use, and the reverse map. + std::vector MCOpPredicates; + DenseMap MCOpPredicateMap; + + std::string F; + std::string FH; + raw_string_ostream Func(F); + raw_string_ostream FuncH(FH); + bool NeedMRI = false; + + if (Compress) + o << "\n#ifdef GEN_COMPRESS_INSTR\n" + << "#undef GEN_COMPRESS_INSTR\n\n"; + else + o << "\n#ifdef GEN_UNCOMPRESS_INSTR\n" + << "#undef GEN_UNCOMPRESS_INSTR\n\n"; + + if (Compress) { + FuncH << "static bool compressInst(MCInst& OutInst,\n"; + FuncH.indent(25) << "const MCInst &MI,\n"; + FuncH.indent(25) << "const MCSubtargetInfo &STI,\n"; + FuncH.indent(25) << "MCContext &Context) {\n"; + } else { + FuncH << "static bool uncompressInst(MCInst& OutInst,\n"; + FuncH.indent(27) << "const MCInst &MI,\n"; + FuncH.indent(27) << "const MCRegisterInfo &MRI,\n"; + FuncH.indent(27) << "const MCSubtargetInfo &STI) {\n"; + } + + if (CompressPatterns.empty()) { + o << FuncH.str(); + o.indent(2) << "return false;\n}\n"; + if (Compress) + o << "\n#endif //GEN_COMPRESS_INSTR\n"; + else + o << "\n#endif //GEN_UNCOMPRESS_INSTR\n\n"; + return; + } + + std::string CaseString(""); + raw_string_ostream CaseStream(CaseString); + std::string PrevOp(""); + std::string CurOp(""); + CaseStream << " switch (MI.getOpcode()) {\n"; + CaseStream << " default: return false;\n"; + + for (auto &CompressPat : CompressPatterns) { + std::string CondString; + std::string CodeString; + raw_string_ostream CondStream(CondString); + raw_string_ostream CodeStream(CodeString); + CodeGenInstruction &Source = + Compress ? CompressPat.Source : CompressPat.Dest; + CodeGenInstruction &Dest = Compress ? CompressPat.Dest : CompressPat.Source; + IndexedMap SourceOperandMap = + Compress ? CompressPat.SourceOperandMap : CompressPat.DestOperandMap; + IndexedMap &DestOperandMap = + Compress ? CompressPat.DestOperandMap : CompressPat.SourceOperandMap; + + CurOp = Source.TheDef->getName().str(); + // Check current and previous opcode to decide to continue or end a case. + if (CurOp != PrevOp) { + if (PrevOp != "") + CaseStream.indent(6) << "break;\n } // case " + PrevOp + "\n"; + CaseStream.indent(4) << "case " + Namespace + "::" + CurOp + ": {\n"; + } + + std::map FeaturesMap; + // Add CompressPat required features. + getReqFeatures(FeaturesMap, CompressPat.PatReqFeatures); + + // Add Dest instruction required features. + std::vector ReqFeatures; + std::vector RF = Dest.TheDef->getValueAsListOfDefs("Predicates"); + copy_if(RF, std::back_inserter(ReqFeatures), [](Record *R) { + return R->getValueAsBit("AssemblerMatcherPredicate"); + }); + getReqFeatures(FeaturesMap, ReqFeatures); + + // Emit checks for all required features. + for (auto &F : FeaturesMap) { + StringRef Op = F.first; + if (Op[0] == '!') + CondStream.indent(6) << ("!STI.getFeatureBits()[" + Namespace + + "::" + Op.substr(1) + "]") + .str() + + " &&\n"; + else + CondStream.indent(6) + << ("STI.getFeatureBits()[" + Namespace + "::" + Op + "]").str() + + " &&\n"; + } + + // Start Source Inst operands validation. + unsigned OpNo = 0; + for (OpNo = 0; OpNo < Source.Operands.size(); ++OpNo) { + if (SourceOperandMap[OpNo].TiedOpIdx != -1) { + if (Source.Operands[OpNo].Rec->isSubClassOf("RegisterClass")) + CondStream.indent(6) + << "(MI.getOperand(" + << std::to_string(OpNo) + ").getReg() == MI.getOperand(" + << std::to_string(SourceOperandMap[OpNo].TiedOpIdx) + << ").getReg()) &&\n"; + else + PrintFatalError("Unexpected tied operand types!\n"); + } + // Check for fixed immediates\registers in the source instruction. + switch (SourceOperandMap[OpNo].Kind) { + case OpData::Operand: + // We don't need to do anything for source instruction operand checks. + break; + case OpData::Imm: + CondStream.indent(6) + << "(MI.getOperand(" + std::to_string(OpNo) + ").isImm()) &&\n" + + " (MI.getOperand(" + std::to_string(OpNo) + + ").getImm() == " + + std::to_string(SourceOperandMap[OpNo].Data.Imm) + ") &&\n"; + break; + case OpData::Reg: { + Record *Reg = SourceOperandMap[OpNo].Data.Reg; + CondStream.indent(6) << "(MI.getOperand(" + std::to_string(OpNo) + + ").getReg() == " + Namespace + + "::" + Reg->getName().str() + ") &&\n"; + break; + } + } + } + CodeStream.indent(6) << "// " + Dest.AsmString + "\n"; + CodeStream.indent(6) << "OutInst.setOpcode(" + Namespace + + "::" + Dest.TheDef->getName().str() + ");\n"; + OpNo = 0; + for (const auto &DestOperand : Dest.Operands) { + CodeStream.indent(6) << "// Operand: " + DestOperand.Name + "\n"; + switch (DestOperandMap[OpNo].Kind) { + case OpData::Operand: { + unsigned OpIdx = DestOperandMap[OpNo].Data.Operand; + // Check that the operand in the Source instruction fits + // the type for the Dest instruction. + if (DestOperand.Rec->isSubClassOf("RegisterClass")) { + NeedMRI = true; + // This is a register operand. Check the register class. + // Don't check register class if this is a tied operand, it was done + // for the operand its tied to. + if (DestOperand.getTiedRegister() == -1) + CondStream.indent(6) + << "(MRI.getRegClass(" + Namespace + + "::" + DestOperand.Rec->getName().str() + + "RegClassID).contains(" + "MI.getOperand(" + + std::to_string(OpIdx) + ").getReg())) &&\n"; + + CodeStream.indent(6) << "OutInst.addOperand(MI.getOperand(" + + std::to_string(OpIdx) + "));\n"; + } else { + // Handling immediate operands. + unsigned Entry = getMCOpPredicate(MCOpPredicateMap, MCOpPredicates, + DestOperand.Rec); + CondStream.indent(6) << Namespace + "ValidateMCOperand(" + + "MI.getOperand(" + std::to_string(OpIdx) + + "), STI, " + std::to_string(Entry) + + ") &&\n"; + CodeStream.indent(6) << "OutInst.addOperand(MI.getOperand(" + + std::to_string(OpIdx) + "));\n"; + } + break; + } + case OpData::Imm: { + unsigned Entry = + getMCOpPredicate(MCOpPredicateMap, MCOpPredicates, DestOperand.Rec); + CondStream.indent(6) + << Namespace + "ValidateMCOperand(" + "MCOperand::createImm(" + + std::to_string(DestOperandMap[OpNo].Data.Imm) + "), STI, " + + std::to_string(Entry) + ") &&\n"; + CodeStream.indent(6) + << "OutInst.addOperand(MCOperand::createImm(" + + std::to_string(DestOperandMap[OpNo].Data.Imm) + "));\n"; + } break; + case OpData::Reg: { + // Fixed register has been validated at pattern validation time. + Record *Reg = DestOperandMap[OpNo].Data.Reg; + CodeStream.indent(6) << "OutInst.addOperand(MCOperand::createReg(" + + Namespace + "::" + Reg->getName().str() + + "));\n"; + } break; + } + ++OpNo; + } + CaseStream << mergeCondAndCode(CondStream, CodeStream); + PrevOp = CurOp; + } + Func << CaseStream.str() << "\n"; + // Close brace for the last case. + Func.indent(4) << "} // case " + CurOp + "\n"; + Func.indent(2) << "} // switch\n"; + Func.indent(2) << "return false;\n}\n"; + + if (!MCOpPredicates.empty()) { + o << "static bool " << Namespace + << "ValidateMCOperand(const MCOperand &MCOp,\n" + << " const MCSubtargetInfo &STI,\n" + << " unsigned PredicateIndex) {\n" + << " switch (PredicateIndex) {\n" + << " default:\n" + << " llvm_unreachable(\"Unknown MCOperandPredicate kind\");\n" + << " break;\n"; + + for (unsigned i = 0; i < MCOpPredicates.size(); ++i) { + Init *MCOpPred = MCOpPredicates[i]->getValueInit("MCOperandPredicate"); + if (CodeInit *SI = dyn_cast(MCOpPred)) + o << " case " << i + 1 << ": {\n" + << " // " << MCOpPredicates[i]->getName().str() << SI->getValue() + << "\n" + << " }\n"; + else + llvm_unreachable("Unexpected MCOperandPredicate field!"); + } + o << " }\n" + << "}\n\n"; + } + + o << FuncH.str(); + if (NeedMRI && Compress) + o.indent(2) << "const MCRegisterInfo &MRI = *Context.getRegisterInfo();\n"; + o << Func.str(); + + if (Compress) + o << "\n#endif //GEN_COMPRESS_INSTR\n"; + else + o << "\n#endif //GEN_UNCOMPRESS_INSTR\n\n"; +} + +void RISCVCompressInstEmitter::run(raw_ostream &o) { + Record *CompressClass = Records.getClass("CompressPat"); + assert(CompressClass && "Compress class definition missing!"); + std::vector Insts; + for (const auto &D : Records.getDefs()) { + if (D.second->isSubClassOf(CompressClass)) + Insts.push_back(D.second.get()); + } + + // Process the CompressPat definitions, validating them as we do so. + for (unsigned i = 0, e = Insts.size(); i != e; ++i) + evaluateCompressPat(Insts[i]); + + // Emit file header. + emitSourceFileHeader("Compress instruction Source Fragment", o); + // Generate compressInst() function. + emitCompressInstEmitter(o, true); + // Generate uncompressInst() function. + emitCompressInstEmitter(o, false); +} + +namespace llvm { + +void EmitCompressInst(RecordKeeper &RK, raw_ostream &OS) { + RISCVCompressInstEmitter(RK).run(OS); +} + +} // namespace llvm diff --git a/utils/TableGen/TableGen.cpp b/utils/TableGen/TableGen.cpp index b0e0385a45c..cf0b0c24f83 100644 --- a/utils/TableGen/TableGen.cpp +++ b/utils/TableGen/TableGen.cpp @@ -32,6 +32,7 @@ enum ActionType { GenAsmMatcher, GenDisassembler, GenPseudoLowering, + GenCompressInst, GenCallingConv, GenDAGISel, GenDFAPacketizer, @@ -72,6 +73,8 @@ namespace { "Generate disassembler"), clEnumValN(GenPseudoLowering, "gen-pseudo-lowering", "Generate pseudo instruction lowering"), + clEnumValN(GenCompressInst, "gen-compress-inst-emitter", + "Generate RISCV compressed instructions."), clEnumValN(GenAsmMatcher, "gen-asm-matcher", "Generate assembly instruction matcher"), clEnumValN(GenDAGISel, "gen-dag-isel", @@ -144,6 +147,9 @@ bool LLVMTableGenMain(raw_ostream &OS, RecordKeeper &Records) { case GenPseudoLowering: EmitPseudoLowering(Records, OS); break; + case GenCompressInst: + EmitCompressInst(Records, OS); + break; case GenDAGISel: EmitDAGISel(Records, OS); break; diff --git a/utils/TableGen/TableGenBackends.h b/utils/TableGen/TableGenBackends.h index 914cd5a1fc9..09f74aa017f 100644 --- a/utils/TableGen/TableGenBackends.h +++ b/utils/TableGen/TableGenBackends.h @@ -74,6 +74,7 @@ void EmitFastISel(RecordKeeper &RK, raw_ostream &OS); void EmitInstrInfo(RecordKeeper &RK, raw_ostream &OS); void EmitInstrDocs(RecordKeeper &RK, raw_ostream &OS); void EmitPseudoLowering(RecordKeeper &RK, raw_ostream &OS); +void EmitCompressInst(RecordKeeper &RK, raw_ostream &OS); void EmitRegisterInfo(RecordKeeper &RK, raw_ostream &OS); void EmitSubtarget(RecordKeeper &RK, raw_ostream &OS); void EmitMapTable(RecordKeeper &RK, raw_ostream &OS);