Index: lib/Target/RISCV/CMakeLists.txt =================================================================== --- lib/Target/RISCV/CMakeLists.txt +++ lib/Target/RISCV/CMakeLists.txt @@ -9,6 +9,7 @@ tablegen(LLVM RISCVGenDAGISel.inc -gen-dag-isel) tablegen(LLVM RISCVGenSubtargetInfo.inc -gen-subtarget) tablegen(LLVM RISCVGenDisassemblerTables.inc -gen-disassembler) +tablegen(LLVM RISCVGenCompressEmitter.inc -gen-compress-emitter) add_public_tablegen_target(RISCVCommonTableGen) Index: lib/Target/RISCV/RISCVInstrInfoC.td =================================================================== --- lib/Target/RISCV/RISCVInstrInfoC.td +++ lib/Target/RISCV/RISCVInstrInfoC.td @@ -12,7 +12,6 @@ //===----------------------------------------------------------------------===// // Operand definitions. //===----------------------------------------------------------------------===// - def UImmLog2XLenNonZeroAsmOperand : AsmOperandClass { let Name = "UImmLog2XLenNonZero"; let RenderMethod = "addImmOperands"; @@ -419,3 +418,115 @@ } } // Predicates = [HasStdExtC] + + +//===----------------------------------------------------------------------===// +// Compressed Instructions Patterns for auto-generated Compress +// tablegen backend. +//===----------------------------------------------------------------------===// + +class CompressPat { + dag Input = input; + dag Output = output; + list Predicates = []; +} + +let Predicates = [HasStdExtC] in { + // FIXME: missing few D and Q instructions, nop, ebreak. + def : CompressPat<(ADD GPR:$rs1, GPR:$rs1, GPR:$rs2), + (C_ADD GPRNoX0:$rs1, GPRNoX0:$rs2)>; +// def : CompressPat<(AND GPRC:$rs1, GPRC:$rs1, GPRC:$rs2), +// (C_AND GPRC:$rs1, GPRC:$rs2)>; +// def : CompressPat<(OR GPRC:$rs1, GPRC:$rs1, GPRC:$rs2), +// (C_OR GPRC:$rs1, GPRC:$rs2)>; +// def : CompressPat<(XOR GPRC:$rs1, GPRC:$rs1, GPRC:$rs2), +// (C_XOR GPRC:$rs1, GPRC:$rs2)>; +// def : CompressPat<(SUB GPRC:$rs1, GPRC:$rs1, GPRC:$rs2), +// (C_SUB GPRC:$rs1, GPRC:$rs2)>; +// def : CompressPat<(ADDI GPRNoX0:$rs1, GPRNoX0:$rs1, simm6:$imm), +// (C_ADDI GPRNoX0:$rs1, simm6:$imm)>; +// def : CompressPat<(ANDI GPRC:$rs1, GPRC:$rs1, simm6:$imm), +// (C_ANDI GPRC:$rs1, simm6:$imm)>; +// def : CompressPat<(SLLI GPR:$rs1, GPRC:$rs1, uimmlog2xlennonzero:$imm), +// (C_SLLI GPRNoX0:$rs1, uimmlog2xlennonzero:$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<(JAL X0, simm12_lsb0:$offset), +// (C_J simm12_lsb0:$offset)>; +// def : CompressPat<(JAL X1, simm12_lsb0:$offset), +// (C_JAL 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)>; +// def : CompressPat<(ADDI GPRNoX0:$rd, X0, simm6:$imm), +// (C_LI GPRNoX0:$rd, simm6:$imm)>; +// def : CompressPat<(ADD GPRNoX0:$rs1, X0, GPRNoX0:$rs2), +// (C_MV GPRNoX0:$rs1, GPRNoX0:$rs2)>; +// def : CompressPat<(ADD GPRNoX0:$rs1, GPRNoX0:$rs2, X0), +// (C_MV GPRNoX0:$rs1, GPRNoX0:$rs2)>; +// def : CompressPat<(ADDI X2, X2, simm10_lsb0000:$imm), +// (C_ADDI16SP X2, simm10_lsb0000:$imm)>; +// def : CompressPat<(ADDI GPRC:$rd, SP:$rs1, uimm10_lsb00nonzero:$imm), +// (C_ADDI4SPN GPRC:$rd, SP:$rs1, uimm10_lsb00nonzero:$imm)>; +// def : CompressPat<(LUI GPRNoX0X2:$rd, uimm6nonzero:$imm), +// (C_LUI GPRNoX0X2:$rd, uimm6nonzero:$imm)>; +// +// def : CompressPat<(LW GPRC:$rd, uimm7_lsb00:$rs1, GPRC:$imm), +// (C_LW GPRC:$rd, GPRC:$rs1, uimm7_lsb00:$imm)>; +// def : CompressPat<(LW GPRNoX0:$rd, SP:$rs1, uimm8_lsb00:$imm), +// (C_LWSP GPRNoX0:$rd, SP:$rs1, uimm8_lsb00:$imm)>; +// def : CompressPat<(SW GPRC:$rs2, uimm7_lsb00:$rs1, GPRC:$imm), +// (C_SW GPRC:$rs2, GPRC:$rs1, uimm7_lsb00:$imm)>; +// def : CompressPat<(SW GPR:$rs2, SP:$rs1, uimm8_lsb00:$imm), +// (C_SWSP GPR:$rs2, SP:$rs1, uimm8_lsb00:$imm)>; +// def : CompressPat<(JALR X0, GPRNoX0:$rs1, 0), +/// (C_JR GPRNoX0:$rs1)>; +// def : CompressPat<(JALR X1, GPRNoX0:$rs1, 0), +// (C_JALR GPRNoX0:$rs1)>; +// +} // Predicates = [HasStdExtC] +// +// +// let Predicates = [HasStdExtC, IsRV64] in { +// def : CompressPat<(ADDW GPRC:$rs1, GPRC:$rs1, GPRC:$rs2), +// (C_ADDW GPRC:$rs1, GPRC:$rs2)>; +// def : CompressPat<(SUBW GPRC:$rs1, GPRC:$rs1, GPRC:$rs2), +// (C_SUBW GPRC:$rs1, GPRC:$rs2)>; +// +// def : CompressPat<(ADDIW GPRNoX0:$rs1, GPRNoX0:$rs1, simm6:$imm), +// (C_ADDIW GPRNoX0:$rs1, simm6:$imm)>; +// +// def : CompressPat<(LD GPRC:$rd, GPRC:$rsi, uimm8_lsb000:$imm), +// (C_LD GPRC:$rd, GPRC:$rs1, uimm8_lsb000:$imm)>; +// def : CompressPat<(LD GPRNoX0:$rd, SP:$rs1, uimm9_lsb000:$imm), +// (C_LDSP GPRNoX0:$rd, SP:$rs1, uimm9_lsb000:$imm)>; +// def : CompressPat<(SD GPRC:$rs2, GPRC:$rs1, uimm8_lsb000:$imm), +// (C_SD GPRC:$rs2, GPRC:$rs1, uimm8_lsb000:$imm)>; +// def : CompressPat<(SD GPR:$rs2, SP:$rs1, uimm9_lsb000:$imm), +// (C_SDSP GPR:$rs2, SP:$rs1, uimm9_lsb000:$imm)>; +// +// } // Predicates = [HasStdExtC, IsRV64] +// +// let Predicates = [HasStdExtC, HasStdExtF, IsRV32] in { +// def : CompressPat<(FLW FPR32C:$rd, FPR32C:$rs1,$imm), +// (C_FLW FPR32C:$rd, GPRC:$rs1, uimm7_lsb00:$imm)>; +// def : CompressPat<(FLW FPR32:$rd, SP:$rs1, uimm8_lsb00:$imm), +// (C_FLWSP FPR32:$rd, SP:$rs1, uimm8_lsb00:$imm)>; +// def : CompressPat<(FSW FPR32C:$rs2, FPR32C:$rs1,uimm7_lsb00:$imm), +// (C_FSW FPR32C:$rs2, GPRC:$rs1, uimm7_lsb00:$imm)>; +// def : CompressPat<(FSW FPR32:$rs2, SP:$rs1, uimm8_lsb00:$imm), +// (C_FSWSP FPR32:$rs2, SP:$rs1, uimm8_lsb00:$imm)>; +// } +// let Predicates = [HasStdExtC, HasStdExtD] in { +// def : CompressPat<(FLD FPR64C:$rd, GPRC:$rs1, uimm8_lsb000:$imm), +// (C_FLD FPR64C:$rd, GPRC:$rs1, uimm8_lsb000:$imm)>; +// def : CompressPat<(FLD FPR64:$rd, SP:$rs1, uimm9_lsb000:$imm), +// (C_FLDSP FPR64:$rd, SP:$rs1, uimm9_lsb000:$imm)>; +// def : CompressPat<(FSD FPR64C:$rs2, GPRC:$rs1, uimm8_lsb000:$imm), +// (C_FSD FPR64C:$rs2, GPRC:$rs1, uimm8_lsb000:$imm)>; +// def : CompressPat<(FSD FPR64:$rs2, SP:$rs1, uimm9_lsb000:$imm), +// (C_FSDSP FPR64:$rs2, SP:$rs1, uimm9_lsb000:$imm)>; +// } Index: test/MC/RISCV/compress32.s =================================================================== --- /dev/null +++ test/MC/RISCV/compress32.s @@ -0,0 +1,95 @@ +#RUN: llvm-mc -triple riscv32 -mattr=+c,+f,+d < %s | FileCheck %s + +#Load and Store Instructions + +lw x1, 252 (x2) +#CHECK: c.lwsp ra, 252(sp) +sw x0, 252 (x2) +#CHECK: c.swsp zero, 252(sp) +lw x8, 124 (x15) +#CHECK: c.lw s0, 124(a5) +sw x8, 124 (x15) +#CHECK: c.sw s0, 124(a5) + +#Load and Store Instructions 32 bit only +flw f0, 124(x2) +#CHECK: c.flwsp ft0, 124(sp) +fsw f0, 124(x2) +#CHECK: c.fswsp ft0, 124(sp) +flw f8, 124(x8) +#CHECK: c.flw fs0, 124(s0) +fsw f8, 124(x8) +#CHECK: c.fsw fs0, 124(s0) + +#Load and Store Instructions 32 and 64 bit only +fld f0, 64 (x2) +#CHECK: c.fldsp ft0, 64(sp) +fsd f0, 64 (x2) +#CHECK: c.fsdsp ft0, 64(sp) +fld f8, 248(x8) +#CHECK: c.fld fs0, 248(s0) +fsd f8, 248(x8) +#CHECK: c.fsd fs0, 248(s0) + +# Control Transfer Instructions + +jal x0, -2048 +#CHECK: c.j -2048 +jal x1, 2046 +#CHECK: c.jal 2046 +jalr x0, x1, 0 +#CHECK: c.jr ra +jalr x1, x8, 0 +#CHECK: c.jalr s0 + +beq x8, x0, -256 +#CHECK: c.beqz s0, -256 +bne x8, x0, 254 +#CHECK: c.bnez s0, 254 + +# Integer Computational Instructions + +addi x1, x0, -31 +#CHECK: c.li ra, -31 +lui x3, 63 +#CHECK: c.lui gp, 63 +addi x1, x1, -32 +#CHECK: c.addi ra, -32 +addi x2, x2, -32 +#CHECK: c.addi16sp sp, -32 + +slli x8, x8, 31 +#CHECK: c.slli s0, 31 +srli x8, x8, 31 +#CHECK: c.srli s0, 31 +srai x8, x8, 31 +#CHECK: c.srai s0, 31 + +andi x8, x8, 31 +#CHECK: c.andi s0, 31 + +# Integer Computational Instructions 32/64 bit only +addi x8, x2, 1020 +#CHECK: c.addi4spn s0, sp, 1020 + +# Integer Register-Tegister Operations +#CHECK: c.mv s0, a5 +add x8, x0, x15 +#CHECK: c.add s0, a5 +add x8, x8, x15 +#CHECK: c.and s0, a5 +and x8, x8, x15 +#CHECK: c.or s0, a5 +or x8, x8, x15 +#CHECK: c.xor s0, a5 +xor x8, x8, x15 +#CHECK: c.sub s0, a5 +sub x8, x8, x15 + +# FIXME: nop and ebreak. +#c.nop +#addi x0, x0, 0 +#c.ebreak +#c.add x0, x0 + + Index: test/MC/RISCV/compress64.s =================================================================== --- /dev/null +++ test/MC/RISCV/compress64.s @@ -0,0 +1,124 @@ +#RUN: llvm-mc -triple riscv64 -mattr=+c,+f,+d < %s | FileCheck %s + +#Load and Store Instructions + +lw x1, 252 (x2) +#CHECK: c.lwsp ra, 252(sp) +sw x0, 252 (x2) +#CHECK: c.swsp zero, 252(sp) +lw x8, 124 (x15) +#CHECK: c.lw s0, 124(a5) +sw x8, 124 (x15) +#CHECK: c.sw s0, 124(a5) + +#Load and Store Instructions 32 and 64 bit only +fld f0, 64 (x2) +#CHECK: c.fldsp ft0, 64(sp) +fsd f0, 64 (x2) +#CHECK: c.fsdsp ft0, 64(sp) +fld f8, 248(x8) +#CHECK: c.fld fs0, 248(s0) +fsd f8, 248(x8) +#CHECK: c.fsd fs0, 248(s0) + +#Load and Store Instructions 64/128bit +ld x8, 248 (x15) +#CHECK: c.ld s0, 248(a5) +ld x1, 248 (x2) +#CHECK: c.ldsp ra, 248(sp) +sd x8, 64(x2) +#CHECK: c.sdsp s0, 64(sp) +sd x8, 64 (x15) +#CHECK: c.sd s0, 64(a5) + +# FIXME: Load and Store Instructions 128bit +# lq x8, 252 (x15) +# c.lq x8, 252 (x15) +# lq x1, 252 (x2) +# c.ldsp x1, 252 (x2) +# sq x1, 252 (x2) +# c.sqsp x1, 252 (x2) +# sq x8, 252 (x15) + +# Control Transfer Instructions + +jal x0, -2048 +#CHECK: c.j -2048 +jal x1, 2046 +#CHECK: c.jal 2046 +jalr x0, x1, 0 +#CHECK: c.jr ra +jalr x1, x8, 0 +#CHECK: c.jalr s0 + +beq x8, x0, -256 +#CHECK: c.beqz s0, -256 +bne x8, x0, 254 +#CHECK: c.bnez s0, 254 + +# Integer Computational Instructions + +addi x1, x0, -31 +#CHECK: c.li ra, -31 +lui x3, 63 +#CHECK: c.lui gp, 63 +addi x1, x1, -32 +#CHECK: c.addi ra, -32 +addi x2, x2, -32 +#CHECK: c.addi16sp sp, -32 + +slli x8, x8, 31 +#CHECK: c.slli s0, 31 +srli x8, x8, 31 +#CHECK: c.srli s0, 31 +srai x8, x8, 31 +#CHECK: c.srai s0, 31 + +#CHECK: c.andi s0, 31 +andi x8, x8, 31 + +# Integer Computational Instructions 32/64 bit only +addi x8, x2, 1020 +#CHECK: c.addi4spn s0, sp, 1020 + +# Integer Computational Instructions 64/128 bit only +addiw x4, x4, 31 +#CHECK: c.addiw tp, 31 +#sext.w x4 +# c.addiw tp, 0 + +# FIXME: Integer Computational Instructions 128 bit only +#slli x8, x8, 64 +#c.slli x8, 0 +#srli x8, x8, 64 +#c.srli x8, 0 +#srai x8, x8, 64 +#c.srai x8, 0 + +# Integer Register-Tegister Operations +add x8, x0, x15 +#CHECK: c.mv s0, a5 +add x8, x8, x15 +#CHECK: c.add s0, a5 +and x8, x8, x15 +#CHECK: c.and s0, a5 +or x8, x8, x15 +#CHECK: c.or s0, a5 +xor x8, x8, x15 +#CHECK: c.xor s0, a5 +sub x8, x8, x15 +#CHECK: c.sub s0, a5 + + +# Integer Register-Tegister Operations 64/128 only +addw x8, x8, x15 +CHECK:c.addw s0, a5 +subw x8, x8, x15 +CHECK:c.subw s0, a5 + +# FIXME: nop and ebreak. +#c.nop +#addi x0, x0, 0 +#c.ebreak +#c.add x0, x0 + Index: utils/TableGen/CMakeLists.txt =================================================================== --- utils/TableGen/CMakeLists.txt +++ utils/TableGen/CMakeLists.txt @@ -29,6 +29,7 @@ InstrDocsEmitter.cpp IntrinsicEmitter.cpp OptParserEmitter.cpp + RISCVCompressEmitter.cpp PseudoLoweringEmitter.cpp RegisterBankEmitter.cpp RegisterInfoEmitter.cpp Index: utils/TableGen/RISCVCompressEmitter.cpp =================================================================== --- /dev/null +++ utils/TableGen/RISCVCompressEmitter.cpp @@ -0,0 +1,680 @@ +//===- RISCVCompressEmitter.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. +// +// This tablegen backend is responsible for emitting logic need to compress +// RISCV instructions to corresponding C extension. Namely it handles the \ +// CompressPatern in td files. +// +//===----------------------------------------------------------------------===// + +#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 +#include +using namespace llvm; + +#define DEBUG_TYPE "compress-emitter" + +namespace { +class RISCVCompressEmitter { + struct OpData { + enum MapKind { Operand, Imm, Reg }; + MapKind Kind; + union { + unsigned Operand; // Operand number mapped to. + uint64_t Imm; // Integer immedate value. + Record *Reg; // Physical register. + } Data; + }; + struct CompressTransform { + CodeGenInstruction Source; // The source instruction definition. + CodeGenInstruction Dest; // The destination instruction to transform to. + std::vector PatReqFeatures; + IndexedMap + SourceOperandMap; // Maps operands in the Source Instruction to + // the corresponding Dest operand. + IndexedMap DestOperandMap; // Maps operands in th Dest Instruction + // to the corresponding Source operand. + std::pair MatchPair; + CompressTransform(CodeGenInstruction &S, CodeGenInstruction &D, + std::vector RF, IndexedMap &SourceMap, + IndexedMap &DestMap, + std::pair &mp) + : Source(S), Dest(D), PatReqFeatures(RF), SourceOperandMap(SourceMap), + DestOperandMap(DestMap), MatchPair(mp) {} + }; + + RecordKeeper &Records; + + // It's overkill to have an instance of the full CodeGenTarget object, + // but it loads everything on demand, not in the constructor, so it's + // lightweight in performance, so it works out OK. + CodeGenTarget Target; + + SmallVector Expansions; + + unsigned addDagOperandMapping(Record *Rec, DagInit *Dag, + CodeGenInstruction &Insn, + IndexedMap &OperandMap, + unsigned BaseIdx, bool IsSource); + void evaluateCompressTransform(Record *Compress); + void emitCompressEmitter(raw_ostream &o, bool Compress); + +public: + RISCVCompressEmitter(RecordKeeper &R) : Records(R), Target(R) {} + + /// run - Output the compress-lowering. + void run(raw_ostream &o); +}; +} // End anonymous namespace + +unsigned RISCVCompressEmitter::addDagOperandMapping( + Record *Rec, DagInit *Dag, CodeGenInstruction &Insn, + IndexedMap &OperandMap, unsigned BaseIdx, bool IsSource) { + unsigned OpsAdded = 0; + unsigned TiedCount = 0; + DEBUG(dbgs() << "addDagOperandMapping: " << *Dag << "\n"); + for (unsigned i = 0, e = Dag->getNumArgs(); i != e; ++i) { + if (DefInit *DI = dyn_cast(Dag->getArg(i))) { + // Physical register reference. Explicit check for the special case + // "zero_reg" definition. + if (DI->getDef()->isSubClassOf("Register") || + DI->getDef()->getName() == "zero_reg") { + DEBUG(dbgs() << " Found Physical register at index: '" << i << *DI + << "'\n"); + OperandMap[BaseIdx + i].Kind = OpData::Reg; + OperandMap[BaseIdx + i].Data.Reg = DI->getDef(); + ++OpsAdded; + continue; + } + + // Normal operands should always have the same type, or we have a + // problem. + // FIXME: We probably shouldn't ever get a non-zero BaseIdx here. + assert(BaseIdx == 0 && "Named subargument in compress transform?!"); + // If this Inst operand is a tied operand it means that we have less dag + // args than Inst Args and that the current Inst argument is tied to a + // previous argument that got checked. + int TiedOpIdx = Insn.Operands[BaseIdx + TiedCount + i].getTiedRegister(); + if (-1 != TiedOpIdx) { + // Set the entry in OperandMap for the tied operand we're skipping. + OperandMap[BaseIdx + TiedCount + i].Kind = OperandMap[TiedOpIdx].Kind; + TiedCount++; + } + + if (!IsSource && + (DI->getDef() != Insn.Operands[BaseIdx + i + TiedCount].Rec)) + PrintFatalError( + Rec->getLoc(), + "Input operand type '" + DI->getDef()->getName() + + "' does not match compress operand type '" + + Insn.Operands[BaseIdx + i + TiedCount].Rec->getName() + "'"); + // Source operand maps to destination operand. The Data element + // will be filled in later, just set the Kind for now. Do it + // for each corresponding MachineInstr operand, not just the first. + for (unsigned I = 0, E = Insn.Operands[i].MINumOperands; I != E; ++I) + OperandMap[BaseIdx + TiedCount + i + I].Kind = OpData::Operand; + OpsAdded += Insn.Operands[i].MINumOperands; + } else if (IntInit *II = dyn_cast(Dag->getArg(i))) { + DEBUG(dbgs() << " Found immediate at index " << BaseIdx + i << ": '" + << II->getValue() << "'\n"); + OperandMap[BaseIdx + i].Kind = OpData::Imm; + OperandMap[BaseIdx + i].Data.Imm = II->getValue(); + ++OpsAdded; + } else if (DagInit *SubDag = dyn_cast(Dag->getArg(i))) { + // Just add the operands recursively. This is almost certainly + // a constant value for a complex operand (> 1 MI operand). + unsigned NewOps = addDagOperandMapping(Rec, SubDag, Insn, OperandMap, + BaseIdx + i, IsSource); + OpsAdded += NewOps; + // Since we added more than one, we also need to adjust the base. + BaseIdx += NewOps - 1; + } else + llvm_unreachable("Unhandled compress-transform argument type!"); + } + return OpsAdded; +} + +unsigned getNumOperands(Record *R) { + return R->getValueAsDag("OutOperandList")->getNumArgs() + + R->getValueAsDag("InOperandList")->getNumArgs(); +} + +// Make sure that the dag has enough arguments to fill the instruction +// that it refers to in the operator. +bool matchInsDag(CodeGenInstruction &Inst, DagInit *dag, + bool IsSource = false) { + + // Number of operands in the dag and the Inst match. + if (dag->getNumArgs() == Inst.Operands.size()) + return true; + + if (IsSource) + PrintFatalError("Source operands for Inst '" + Inst.TheDef->getName() + + "' and 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"); + + DEBUG(dbgs() << "Inst = : " << Inst.AsmString << "\n"); + DEBUG(dbgs() << "Inst.Operands.size = : " << Inst.Operands.size() << "\n"); + DEBUG(dbgs() << "Dag->getNumArgs(): " << dag->getNumArgs() << "\n"); + + // The Instruction might have tied operands so the number of operands + // so the dag might have a fewer operand count. We need to check for that. + unsigned InstOperandNeedCount = Inst.Operands.size(); + for (unsigned i = 0; i < Inst.Operands.size(); i++) { + if (Inst.Operands[i].getTiedRegister() != -1) { + DEBUG(dbgs() << " Tied Operand Register found: " << Inst.Operands[i].Name + << "\n"); + InstOperandNeedCount--; + } + } + + if (dag->getNumArgs() != InstOperandNeedCount) + PrintFatalError("Inst '" + Inst.TheDef->getName() + + "' and dag operand count mismatch"); + return true; +} + +// Map the Input instruction arguments to the output instruction arguments. +void RISCVCompressEmitter::evaluateCompressTransform(Record *Rec) { + + // Read the input Instruction first: + DagInit *InputDag = Rec->getValueAsDag("Input"); + assert(InputDag && "Missing Input in compress transform!"); + DEBUG(dbgs() << "Input: " << *InputDag << "\n"); + + DefInit *OpDef = dyn_cast(InputDag->getOperator()); + if (!OpDef) + PrintFatalError(Rec->getLoc(), + Rec->getName() + " has unexpected operator type!"); + Record *Operator = OpDef->getDef(); + if (!Operator->isSubClassOf("Instruction")) + PrintFatalError(Rec->getLoc(), "Source Input '" + Operator->getName() + + "' is not an instruction!"); + CodeGenInstruction SourceInsn(Operator); + + if (SourceInsn.Operands.size() != InputDag->getNumArgs()) + PrintFatalError(Rec->getLoc(), + "Source InputDag '" + Operator->getName() + + "' and Source Inst operand count mismatch"); + + matchInsDag(SourceInsn, InputDag, true); + + DagInit *CompressDag = Rec->getValueAsDag("Output"); + assert(InputDag && "Missing Output in compress transform!"); + DEBUG(dbgs() << "Output: " << *CompressDag << "\n"); + + DefInit *CompressOpDef = dyn_cast(CompressDag->getOperator()); + if (!CompressOpDef) + PrintFatalError(Rec->getLoc(), + Rec->getName() + " has unexpected operator type!"); + + Record *CompressOperator = CompressOpDef->getDef(); + if (!CompressOperator->isSubClassOf("Instruction")) + PrintFatalError(Rec->getLoc(), "Compression result '" + + CompressOperator->getName() + + "' is not an instruction!"); + CodeGenInstruction CompressInsn(CompressOperator); + matchInsDag(CompressInsn, CompressDag); + + unsigned NumMIOperands = 0; + for (unsigned i = 0, e = CompressInsn.Operands.size(); i != e; ++i) + NumMIOperands += CompressInsn.Operands[i].MINumOperands; + + IndexedMap CompressOperandMap; + CompressOperandMap.grow(NumMIOperands); + addDagOperandMapping(Rec, CompressDag, CompressInsn, CompressOperandMap, 0, + false); + for (unsigned i = 0, e = CompressInsn.Operands.size(); i != e; ++i) + NumMIOperands += CompressInsn.Operands[i].MINumOperands; + + // Currently holds information about the Source operands. + // Mapping not filled in OpData (Operand_. + // ToDo: if a map not needed, change the data structure. + IndexedMap SourceOperandMap; + NumMIOperands = 0; + for (unsigned i = 0, e = SourceInsn.Operands.size(); i != e; ++i) + NumMIOperands += SourceInsn.Operands[i].MINumOperands; + SourceOperandMap.grow(NumMIOperands); + addDagOperandMapping(Rec, InputDag, SourceInsn, SourceOperandMap, 0, true); + StringMap CompressOperands; + for (unsigned i = 0; i < CompressDag->getNumArgs(); i++) { + CompressOperands[CompressDag->getArgNameStr(i)] = i; + } + + // If there are more operands that weren't in the DAG, they have to + // be operands that have default values, or we have an error. Currently, + // Operands that are a subclass of OperandWithDefaultOp have default values. + + // Validate that each result pattern argument has a matching (by name) + // argument in the source instruction, in either the (outs) or (ins) list. + // Also check that the type of the arguments match. + // + StringMap SourceOperands; + + // ToDo: Do we need to account for more than one pair of tied operands? + std::pair MatchPair; + for (unsigned i = 0; i < InputDag->getNumArgs(); ++i) { + if ("" == InputDag->getArgNameStr(i)) + continue; + + StringMap::iterator it = + SourceOperands.find(InputDag->getArgNameStr(i)); + if (it != SourceOperands.end()) { + DEBUG(dbgs() << " Operands " << i << " and " << it->getValue() + << "with token: " << InputDag->getArgNameStr(i) + << " in the source Instruction" + << " must be checked for equality\n"); + MatchPair = std::make_pair(i, it->getValue()); + } + // Validate that the captured operand is used in the compressed + // Instruction pattern. + it = CompressOperands.find(InputDag->getArgNameStr(i)); + if (it == CompressOperands.end()) + PrintFatalError(Rec->getLoc(), + "Operand " + InputDag->getArgNameStr(i) + + " defined in input Instruction but not used in" + " compressed instruction\n"); + + SourceOperands[InputDag->getArgNameStr(i)] = i; + } + + // ToDo: Do we need to fill the SourceOperandMap Mapping with entries to + // corresponding operands in the compressed instruction to generate + // decompress function? + + unsigned TiedCount = 0; + DEBUG(dbgs() << " Operand mapping:\n Source Dest\n"); + for (unsigned i = 0, e = CompressInsn.Operands.size(); i != e; ++i) { + // We've already handled constant values. Just map instruction operands + // here. + if (CompressOperandMap[CompressInsn.Operands[i].MIOperandNo].Kind != + OpData::Operand) + continue; + + int NonTiedOpIdx = CompressInsn.Operands[i].getTiedRegister(); + if (NonTiedOpIdx < 0) + NonTiedOpIdx = i - TiedCount; + else + TiedCount++; + + StringMap::iterator SourceOp = + SourceOperands.find(CompressDag->getArgNameStr(NonTiedOpIdx)); + + if (SourceOp == SourceOperands.end()) + PrintFatalError(Rec->getLoc(), + "Compress output operand '" + + CompressDag->getArgNameStr(NonTiedOpIdx) + + "' has no matching source operand."); + // Map the source operand to the destination operand index for each + // MachineInstr operand. + for (unsigned I = 0, E = CompressInsn.Operands[i].MINumOperands; I != E; + ++I) { + CompressOperandMap[CompressInsn.Operands[i].MIOperandNo + I] + .Data.Operand = SourceOp->getValue(); + + SourceOperandMap[SourceOp->getValue()].Data.Operand = + CompressInsn.Operands[i].MIOperandNo + I; + // ToDo: Check the types from the output dag is compatible with the type + // from the input dag. Need to figure out limitations to check subclasses. + // areCompatibleTypes(CompressDag->getArg([NonTiedOpIdx]),InputDag->getArg[i]) + } + DEBUG(dbgs() << " " << SourceOp->getValue() << " ====> " << i << "\n"); + } + + // 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"); + }); + + Expansions.push_back(CompressTransform(SourceInsn, CompressInsn, + PatReqFeatures, SourceOperandMap, + CompressOperandMap, MatchPair)); +} + +void RISCVCompressEmitter::emitCompressEmitter(raw_ostream &o, bool Compress) { + Record *AsmWriter = Target.getAsmWriter(); + bool PassSubtarget = AsmWriter->getValueAsInt("PassSubtarget"); + std::string Namespace = Target.getName(); + + std::map PredicateMap; + std::vector AsmOperands = + Records.getAllDerivedDefinitions("AsmOperandClass"); + for (Record *Rec : AsmOperands) { + std::string ClassName = Rec->getValueAsString("Name"); + Init *PMName = Rec->getValueInit("PredicateMethod"); + std::string PredicateMethod = ""; + if (StringInit *SI = dyn_cast(PMName)) { + PredicateMethod = SI->getValue(); + } else { + assert(isa(PMName) && "Unexpected PredicateMethod field!"); + PredicateMethod = "is" + ClassName; + PredicateMap.insert(std::make_pair(ClassName, PredicateMethod)); + llvm::errs() << "PredicateMethod: " << PredicateMethod << '\n'; + } + } + + std::sort(Expansions.begin(), Expansions.end(), + [](const CompressTransform &LHS, const CompressTransform &RHS) { + return (LHS.Source.AsmString < RHS.Source.AsmString); + }); + + // Emit file header. + if (Compress) + emitSourceFileHeader("Compress instruction Source Fragment", o); + + if (Compress) + // TODO:: clean this up, create a helper function to get the types and + // use a switch over the MCOperandType + o << "static bool areEqualOperands(const MCOperand& MCOp1,const MCOperand& " + "MCOp2) {\n" + " if (MCOp1.isReg()) {\n" + " if (MCOp2.isReg()) {\n" + " if (MCOp1.getReg() == MCOp2.getReg())\n" + " return true;\n" + " }\n" + " }\n" + " return false;\n" + "}\n\n"; + + if (Compress) { + o << "\n#ifdef GEN_COMPRESS_INSTR\n"; + o << "#undef GEN_COMPRESS_INSTR\n\n"; + } else { + o << "\n#ifdef GEN_UNCOMPRESS_INSTR\n"; + o << "#undef GEN_UNCOMPRESS_INSTR\n\n"; + } + + // o << "bool " << Target.getName() + "AsmPrinter" + // << "::\n" + if (Compress) + o << "static bool " + << "compressInst(MCInst& OutInst,\n" + << " const MCInst &MI,\n" + << (PassSubtarget ? " const MCSubtargetInfo &STI,\n" + : "") + << " MCContext &Context) {\n" + << " const MCRegisterInfo &MRI = *Context.getRegisterInfo();\n"; + else + o << "static bool " + << "uncompressInst(MCInst& OutInst,\n" + << " const MCInst &MI,\n" + << " const MCRegisterInfo &MRI" + << (PassSubtarget ? ",\n" : ") {\n") + << (PassSubtarget ? " const MCSubtargetInfo &STI) {\n" + : ""); + + std::stringstream CaseStream; + if (!Expansions.empty()) { + CaseStream << " switch (MI.getOpcode()) {\n" + << " default: return false;\n"; + unsigned ExpIdx = 0; + for (auto &Expansion : Expansions) { + std::stringstream CondStream; + std::stringstream CodeStream; + CodeGenInstruction *SourcePtr = &Expansion.Source; + CodeGenInstruction *DestPtr = &Expansion.Dest; + IndexedMap *SourceOperandMapPtr = &Expansion.SourceOperandMap; + IndexedMap *DestOperandMapPtr = &Expansion.DestOperandMap; + if (!Compress) { + SourcePtr = &Expansion.Dest; + DestPtr = &Expansion.Source; + SourceOperandMapPtr = &Expansion.DestOperandMap; + DestOperandMapPtr = &Expansion.SourceOperandMap; + } + + // Fill into references. + CodeGenInstruction &Source = *SourcePtr; + CodeGenInstruction &Dest = *DestPtr; + IndexedMap &SourceOperandMap = *SourceOperandMapPtr; + IndexedMap &DestOperandMap = *DestOperandMapPtr; + + // Closing brace for the previous case + // If the entry in expansions is same opcode as the one before, don't + // close and break + if (ExpIdx > 0) { + if (Source.AsmString == Expansions[ExpIdx - 1].Source.AsmString) { + CaseStream << " // Case continues \n"; + + } else { + CaseStream << " break;\n } //case" + << Expansions[ExpIdx - 1].Source.TheDef->getName().str() + << "\n"; + CaseStream << " case " << Namespace + << "::" << Source.TheDef->getName().str() << ": {\n"; + } + } else { + CaseStream << " case " << Namespace + << "::" << Source.TheDef->getName().str() << ": {\n"; + } + // Start source Inst checks\n"; + if (Expansion.MatchPair.first != Expansion.MatchPair.second) { + CondStream << "areEqualOperands(MI.getOperand(" + << Expansion.MatchPair.first << ")," + << " MI.getOperand(" << Expansion.MatchPair.second + << ")) &&\n"; + } + // Start CompressPat checks"; + for (auto I = Expansion.PatReqFeatures.cbegin(); + I != Expansion.PatReqFeatures.cend(); I++) { + // std::string Cond; + Record *R = *I; + 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 (Cond.size() > 0) + // Cond += " && \n"; + if (Op[0] == '!') + CondStream << (" !STI.getFeatureBits()[" + Namespace + + "::" + Op.substr(1) + "]") + .str() + << " &&\n"; + else + CondStream << (" STI.getFeatureBits()[" + Namespace + + "::" + Op + "]") + .str() + << "&&\n"; + } + } + + // Start Dest Inst checks + std::vector ReqFeatures; + if (PassSubtarget) { + // We only consider ReqFeatures predicates if PassSubtarget + std::vector RF = + Dest.TheDef->getValueAsListOfDefs("Predicates"); + copy_if(RF, std::back_inserter(ReqFeatures), [](Record *R) { + return R->getValueAsBit("AssemblerMatcherPredicate"); + }); + } + + for (auto I = ReqFeatures.cbegin(); I != ReqFeatures.cend(); I++) { + std::string Cond; + Record *R = *I; + 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 (Cond.size() > 0) + // Cond += " && \n"; + if (Op[0] == '!') + CondStream << " !STI.getFeatureBits()[" << Namespace + << "::" << std::string(Op.substr(1)) << "] &&\n"; + else + CondStream << (" STI.getFeatureBits()[" + Namespace + + "::" + Op + "]") + .str() + << " &&\n"; + } + } + // Start Dest Inst checks\n"; + // Check for fixed values in the source instruction: + unsigned MIOpNo = 0; + for (const auto &SourceOperand : Source.Operands) { + for (unsigned i = 0, e = SourceOperand.MINumOperands; i != e; ++i) { + switch (SourceOperandMap[MIOpNo + i].Kind) { + case OpData::Operand: + // If this is a captured operand we don't need to do anything + // for source checks. + break; + case OpData::Imm: + // CondStream << " // Operand: " << SourceOperand.Name << "\n"; + CondStream << " (MI.getOperand(" << MIOpNo << ").getImm() == " + << SourceOperandMap[MIOpNo + i].Data.Imm << ") &&\n"; + break; + case OpData::Reg: { + Record *Reg = SourceOperandMap[MIOpNo + i].Data.Reg; + // Check that the input MI has this value for the Register Operand + // CondStream << " // Operand: " << SourceOperand.Name << "\n"; + CondStream << " (MI.getOperand(" << MIOpNo << ").getReg() == "; + // "zero_reg" is special. + if (Reg->getName() == "zero_reg") + CondStream << "0"; + else + CondStream << Namespace << "::" << Reg->getName().str(); + CondStream << ") &&\n"; + // o << " return false;\n }\n"; + break; + } + } + } + MIOpNo += SourceOperand.MINumOperands; + } + CodeStream << " OutInst.setOpcode(" << Namespace + << "::" << Dest.TheDef->getName().str() << ");\n"; + + MIOpNo = 0; + for (const auto &DestOperand : Dest.Operands) { + CodeStream << " // Operand: " << DestOperand.Name << "\n"; + for (unsigned i = 0, e = DestOperand.MINumOperands; i != e; ++i) { + switch (DestOperandMap[MIOpNo + i].Kind) { + case OpData::Operand: { + unsigned OpIdx = + Source.Operands[DestOperandMap[MIOpNo].Data.Operand] + .MIOperandNo + + i; + // Check that the operand coming from the Source instruction fits + // the type for the destination instruction. + if (DestOperand.Rec->isSubClassOf("RegisterClass")) { + // This is a register operand. Check the register class. + CondStream << " (MRI.getRegClass(" << Namespace + << "::" << DestOperand.Rec->getName().str() + << "RegClassID).contains(" + << "MI.getOperand(" << OpIdx << ").getReg())) &&\n"; + CodeStream << " OutInst.addOperand(MI.getOperand(" << OpIdx + << "));\n"; + } else { + std::string ClassName = DestOperand.Rec->getName().str(); + std::string PredicateMethod = ""; + auto I = PredicateMap.find(ClassName); + if (I != PredicateMap.end()) { + PredicateMethod = I->second; + CondStream << "MI.getOperand(" << OpIdx << ").getImm()." << PredicateMethod << "() &&\n"; + } + else + llvm::errs() << "Can't find PredicateMethod for class" << ClassName << '\n'; + } + break; + } + case OpData::Imm: + CodeStream << " OutInst.addOperand(MCOperand::createImm(" + << DestOperandMap[MIOpNo + i].Data.Imm << "));\n"; + break; + case OpData::Reg: { + Record *Reg = DestOperandMap[MIOpNo + i].Data.Reg; + CodeStream << " OuInst.addOperand(MCOperand::createReg("; + // "zero_reg" is special. + if (Reg->getName() == "zero_reg") + CodeStream << "0"; + else + CodeStream << Namespace << "::" << Reg->getName().str(); + CodeStream << "));\n"; + break; + } + } + } + MIOpNo += DestOperand.MINumOperands; + } + if (Dest.Operands.isVariadic) { + llvm_unreachable("Variadic operands not handled for CompressPat!"); + } + CaseStream << (" if (" + + CondStream.str().substr(0, CondStream.str().length() - 4) + + ") {\n") + << CodeStream.str() << " return true;\n" + << " } // if\n"; + + // If this is the last case, close the brance here. + ExpIdx++; + if (ExpIdx == Expansions.size()) { + CaseStream << " break;\n } //case" + << Source.TheDef->getName().str() << "\n"; + } + } + } + o << CaseStream.str() << " } // switch\n return false;\n}\n"; + if (Compress) + o << "\n#endif //GEN_COMPRESS_INSTR\n"; + else + o << "\n#endif //GEN_UNCOMPRESS_INSTR\n\n"; +} + +void RISCVCompressEmitter::run(raw_ostream &o) { + Record *CompressClass = Records.getClass("CompressPat"); + assert(CompressClass && "Compress class definition missing!"); + // Record *InstructionClass = Records.getClass("Instruction"); + // assert(InstructionClass && "Instruction 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) + evaluateCompressTransform(Insts[i]); + + // Generate compressed instructions. + emitCompressEmitter(o, true); + // Generate uncompressed instructions. + emitCompressEmitter(o, false); +} + +namespace llvm { + +void EmitCompressInst(RecordKeeper &RK, raw_ostream &OS) { + RISCVCompressEmitter(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-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);