Index: include/llvm/MC/MCInst.h =================================================================== --- include/llvm/MC/MCInst.h +++ include/llvm/MC/MCInst.h @@ -150,6 +150,8 @@ 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; }; Index: lib/MC/MCInst.cpp =================================================================== --- lib/MC/MCInst.cpp +++ 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 @@ 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()); Index: lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp =================================================================== --- lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp +++ 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 @@ 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: Index: lib/Target/RISCV/CMakeLists.txt =================================================================== --- lib/Target/RISCV/CMakeLists.txt +++ lib/Target/RISCV/CMakeLists.txt @@ -2,6 +2,7 @@ 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) Index: lib/Target/RISCV/InstPrinter/RISCVInstPrinter.cpp =================================================================== --- lib/Target/RISCV/InstPrinter/RISCVInstPrinter.cpp +++ 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 @@ #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 @@ 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); } Index: lib/Target/RISCV/MCTargetDesc/RISCVMCExpr.h =================================================================== --- lib/Target/RISCV/MCTargetDesc/RISCVMCExpr.h +++ lib/Target/RISCV/MCTargetDesc/RISCVMCExpr.h @@ -20,6 +20,7 @@ namespace llvm { class StringRef; +class MCOperand; class RISCVMCExpr : public MCTargetExpr { public: enum VariantKind { Index: lib/Target/RISCV/MCTargetDesc/RISCVMCExpr.cpp =================================================================== --- lib/Target/RISCV/MCTargetDesc/RISCVMCExpr.cpp +++ 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" Index: lib/Target/RISCV/RISCVAsmPrinter.cpp =================================================================== --- lib/Target/RISCV/RISCVAsmPrinter.cpp +++ 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 @@ 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 @@ }; } +#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" Index: lib/Target/RISCV/RISCVInstrInfo.td =================================================================== --- lib/Target/RISCV/RISCVInstrInfo.td +++ lib/Target/RISCV/RISCVInstrInfo.td @@ -83,6 +83,14 @@ 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, ImmLeaf(Imm);}]> { @@ -94,6 +102,12 @@ 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 @@ 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 @@ 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. Index: lib/Target/RISCV/RISCVInstrInfoC.td =================================================================== --- lib/Target/RISCV/RISCVInstrInfoC.td +++ lib/Target/RISCV/RISCVInstrInfoC.td @@ -27,12 +27,26 @@ 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, 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ } } // 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] Index: test/CodeGen/RISCV/alu32.ll =================================================================== --- test/CodeGen/RISCV/alu32.ll +++ 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 @@ ; 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 @@ ; 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 @@ ; 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 @@ ; 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 @@ ; 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 @@ ; 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 @@ ; 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 @@ ; 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 @@ ; 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 } Index: test/CodeGen/RISCV/branch.ll =================================================================== --- test/CodeGen/RISCV/branch.ll +++ 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 @@ ; 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 Index: test/CodeGen/RISCV/compress-Pseudo.ll =================================================================== --- /dev/null +++ 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 +} Index: test/CodeGen/RISCV/compress-inline-asm.ll =================================================================== --- /dev/null +++ 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 +} + Index: test/MC/RISCV/cnop.s =================================================================== --- test/MC/RISCV/cnop.s +++ 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 Index: test/MC/RISCV/compress-cjal.s =================================================================== --- /dev/null +++ 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] Index: test/MC/RISCV/compress-rv32d.s =================================================================== --- /dev/null +++ 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] Index: test/MC/RISCV/compress-rv32f.s =================================================================== --- /dev/null +++ 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] Index: test/MC/RISCV/compress-rv32i.s =================================================================== --- /dev/null +++ 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) Index: test/MC/RISCV/compress-rv64i.s =================================================================== --- /dev/null +++ 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) Index: test/MC/RISCV/compressed-relocations.s =================================================================== --- /dev/null +++ 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 Index: test/MC/RISCV/fixups-compressed.s =================================================================== --- test/MC/RISCV/fixups-compressed.s +++ 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 Index: test/MC/RISCV/relocations.s =================================================================== --- test/MC/RISCV/relocations.s +++ 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 @@ # 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 Index: test/MC/RISCV/rv32-relaxation.s =================================================================== --- test/MC/RISCV/rv32-relaxation.s +++ 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 @@ 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 @@ 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 @@ 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 Index: test/MC/RISCV/rv64-relaxation.s =================================================================== --- test/MC/RISCV/rv64-relaxation.s +++ 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 @@ 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 @@ 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 Index: utils/TableGen/CMakeLists.txt =================================================================== --- utils/TableGen/CMakeLists.txt +++ utils/TableGen/CMakeLists.txt @@ -30,6 +30,7 @@ IntrinsicEmitter.cpp OptParserEmitter.cpp PseudoLoweringEmitter.cpp + RISCVCompressInstEmitter.cpp RegisterBankEmitter.cpp RegisterInfoEmitter.cpp SDNodeProperties.cpp Index: utils/TableGen/RISCVCompressInstEmitter.cpp =================================================================== --- /dev/null +++ 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 Index: utils/TableGen/TableGen.cpp =================================================================== --- utils/TableGen/TableGen.cpp +++ utils/TableGen/TableGen.cpp @@ -32,6 +32,7 @@ GenAsmMatcher, GenDisassembler, GenPseudoLowering, + GenCompressInst, GenCallingConv, GenDAGISel, GenDFAPacketizer, @@ -72,6 +73,8 @@ "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 @@ case GenPseudoLowering: EmitPseudoLowering(Records, OS); break; + case GenCompressInst: + EmitCompressInst(Records, OS); + break; case GenDAGISel: EmitDAGISel(Records, OS); break; Index: utils/TableGen/TableGenBackends.h =================================================================== --- utils/TableGen/TableGenBackends.h +++ utils/TableGen/TableGenBackends.h @@ -74,6 +74,7 @@ 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);