xref: /llvm-project/llvm/tools/llvm-exegesis/lib/RISCV/Target.cpp (revision ff1b01bb7897bf2401540096af775d35b12eb247)
1 //===-- Target.cpp ----------------------------------------------*- C++ -*-===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include "../Target.h"
10 
11 #include "MCTargetDesc/RISCVBaseInfo.h"
12 #include "MCTargetDesc/RISCVMCTargetDesc.h"
13 #include "MCTargetDesc/RISCVMatInt.h"
14 #include "RISCVInstrInfo.h"
15 
16 // include computeAvailableFeatures and computeRequiredFeatures.
17 #define GET_AVAILABLE_OPCODE_CHECKER
18 #include "RISCVGenInstrInfo.inc"
19 
20 #include "llvm/CodeGen/MachineInstrBuilder.h"
21 
22 #include <vector>
23 
24 namespace llvm {
25 namespace exegesis {
26 
27 #include "RISCVGenExegesis.inc"
28 
29 namespace {
30 
31 // Stores constant value to a general-purpose (integer) register.
32 static std::vector<MCInst> loadIntReg(const MCSubtargetInfo &STI,
33                                       MCRegister Reg, const APInt &Value) {
34   SmallVector<MCInst, 8> MCInstSeq;
35   MCRegister DestReg = Reg;
36 
37   RISCVMatInt::generateMCInstSeq(Value.getSExtValue(), STI, DestReg, MCInstSeq);
38 
39   std::vector<MCInst> MatIntInstrs(MCInstSeq.begin(), MCInstSeq.end());
40   return MatIntInstrs;
41 }
42 
43 const MCPhysReg ScratchIntReg = RISCV::X30; // t5
44 
45 // Stores constant bits to a floating-point register.
46 static std::vector<MCInst> loadFPRegBits(const MCSubtargetInfo &STI,
47                                          MCRegister Reg, const APInt &Bits,
48                                          unsigned FmvOpcode) {
49   std::vector<MCInst> Instrs = loadIntReg(STI, ScratchIntReg, Bits);
50   Instrs.push_back(MCInstBuilder(FmvOpcode).addReg(Reg).addReg(ScratchIntReg));
51   return Instrs;
52 }
53 
54 // main idea is:
55 // we support APInt only if (represented as double) it has zero fractional
56 // part: 1.0, 2.0, 3.0, etc... then we can do the trick: write int to tmp reg t5
57 // and then do FCVT this is only reliable thing in 32-bit mode, otherwise we
58 // need to use __floatsidf
59 static std::vector<MCInst> loadFP64RegBits32(const MCSubtargetInfo &STI,
60                                              MCRegister Reg,
61                                              const APInt &Bits) {
62   double D = Bits.bitsToDouble();
63   double IPart;
64   double FPart = std::modf(D, &IPart);
65 
66   if (std::abs(FPart) > std::numeric_limits<double>::epsilon()) {
67     errs() << "loadFP64RegBits32 is not implemented for doubles like " << D
68            << ", please remove fractional part\n";
69     return {};
70   }
71 
72   std::vector<MCInst> Instrs = loadIntReg(STI, ScratchIntReg, Bits);
73   Instrs.push_back(
74       MCInstBuilder(RISCV::FCVT_D_W).addReg(Reg).addReg(ScratchIntReg));
75   return Instrs;
76 }
77 
78 static MCInst nop() {
79   // ADDI X0, X0, 0
80   return MCInstBuilder(RISCV::ADDI)
81       .addReg(RISCV::X0)
82       .addReg(RISCV::X0)
83       .addImm(0);
84 }
85 
86 static bool isVectorRegList(MCRegister Reg) {
87   return RISCV::VRM2RegClass.contains(Reg) ||
88          RISCV::VRM4RegClass.contains(Reg) ||
89          RISCV::VRM8RegClass.contains(Reg) ||
90          RISCV::VRN2M1RegClass.contains(Reg) ||
91          RISCV::VRN2M2RegClass.contains(Reg) ||
92          RISCV::VRN2M4RegClass.contains(Reg) ||
93          RISCV::VRN3M1RegClass.contains(Reg) ||
94          RISCV::VRN3M2RegClass.contains(Reg) ||
95          RISCV::VRN4M1RegClass.contains(Reg) ||
96          RISCV::VRN4M2RegClass.contains(Reg) ||
97          RISCV::VRN5M1RegClass.contains(Reg) ||
98          RISCV::VRN6M1RegClass.contains(Reg) ||
99          RISCV::VRN7M1RegClass.contains(Reg) ||
100          RISCV::VRN8M1RegClass.contains(Reg);
101 }
102 
103 class ExegesisRISCVTarget : public ExegesisTarget {
104 public:
105   ExegesisRISCVTarget();
106 
107   bool matchesArch(Triple::ArchType Arch) const override;
108 
109   std::vector<MCInst> setRegTo(const MCSubtargetInfo &STI, MCRegister Reg,
110                                const APInt &Value) const override;
111 
112   MCRegister getDefaultLoopCounterRegister(const Triple &) const override;
113 
114   void decrementLoopCounterAndJump(MachineBasicBlock &MBB,
115                                    MachineBasicBlock &TargetMBB,
116                                    const MCInstrInfo &MII,
117                                    MCRegister LoopRegister) const override;
118 
119   MCRegister getScratchMemoryRegister(const Triple &TT) const override;
120 
121   void fillMemoryOperands(InstructionTemplate &IT, MCRegister Reg,
122                           unsigned Offset) const override;
123 
124   ArrayRef<MCPhysReg> getUnavailableRegisters() const override;
125 
126   bool allowAsBackToBack(const Instruction &Instr) const override {
127     return !Instr.Description.isPseudo();
128   }
129 
130   Error randomizeTargetMCOperand(const Instruction &Instr, const Variable &Var,
131                                  MCOperand &AssignedValue,
132                                  const BitVector &ForbiddenRegs) const override;
133 
134   std::vector<InstructionTemplate>
135   generateInstructionVariants(const Instruction &Instr,
136                               unsigned MaxConfigsPerOpcode) const override;
137 };
138 
139 ExegesisRISCVTarget::ExegesisRISCVTarget()
140     : ExegesisTarget(RISCVCpuPfmCounters, RISCV_MC::isOpcodeAvailable) {}
141 
142 bool ExegesisRISCVTarget::matchesArch(Triple::ArchType Arch) const {
143   return Arch == Triple::riscv32 || Arch == Triple::riscv64;
144 }
145 
146 std::vector<MCInst> ExegesisRISCVTarget::setRegTo(const MCSubtargetInfo &STI,
147                                                   MCRegister Reg,
148                                                   const APInt &Value) const {
149   if (RISCV::GPRRegClass.contains(Reg))
150     return loadIntReg(STI, Reg, Value);
151   if (RISCV::FPR16RegClass.contains(Reg))
152     return loadFPRegBits(STI, Reg, Value, RISCV::FMV_H_X);
153   if (RISCV::FPR32RegClass.contains(Reg))
154     return loadFPRegBits(STI, Reg, Value, RISCV::FMV_W_X);
155   if (RISCV::FPR64RegClass.contains(Reg)) {
156     if (STI.hasFeature(RISCV::Feature64Bit))
157       return loadFPRegBits(STI, Reg, Value, RISCV::FMV_D_X);
158     return loadFP64RegBits32(STI, Reg, Value);
159   }
160   if (Reg == RISCV::FRM || Reg == RISCV::VL || Reg == RISCV::VLENB ||
161       Reg == RISCV::VTYPE || RISCV::GPRPairRegClass.contains(Reg) ||
162       RISCV::VRRegClass.contains(Reg) || isVectorRegList(Reg)) {
163     // Don't initialize:
164     // - FRM
165     // - VL, VLENB, VTYPE
166     // - vector registers (and vector register lists)
167     // - Zfinx registers
168     // Generate 'NOP' so that exegesis treats such registers as initialized
169     // (it tries to initialize them with '0' anyway).
170     return {nop()};
171   }
172   errs() << "setRegTo is not implemented for Reg " << Reg
173          << ", results will be unreliable\n";
174   return {};
175 }
176 
177 const MCPhysReg DefaultLoopCounterReg = RISCV::X31; // t6
178 const MCPhysReg ScratchMemoryReg = RISCV::X10;      // a0
179 
180 MCRegister
181 ExegesisRISCVTarget::getDefaultLoopCounterRegister(const Triple &) const {
182   return DefaultLoopCounterReg;
183 }
184 
185 void ExegesisRISCVTarget::decrementLoopCounterAndJump(
186     MachineBasicBlock &MBB, MachineBasicBlock &TargetMBB,
187     const MCInstrInfo &MII, MCRegister LoopRegister) const {
188   BuildMI(&MBB, DebugLoc(), MII.get(RISCV::ADDI))
189       .addDef(LoopRegister)
190       .addUse(LoopRegister)
191       .addImm(-1);
192   BuildMI(&MBB, DebugLoc(), MII.get(RISCV::BNE))
193       .addUse(LoopRegister)
194       .addUse(RISCV::X0)
195       .addMBB(&TargetMBB);
196 }
197 
198 MCRegister
199 ExegesisRISCVTarget::getScratchMemoryRegister(const Triple &TT) const {
200   return ScratchMemoryReg; // a0
201 }
202 
203 void ExegesisRISCVTarget::fillMemoryOperands(InstructionTemplate &IT,
204                                              MCRegister Reg,
205                                              unsigned Offset) const {
206   // TODO: for now we ignore Offset because have no way
207   // to detect it in instruction.
208   auto &I = IT.getInstr();
209 
210   auto MemOpIt =
211       find_if(I.Operands, [](const Operand &Op) { return Op.isMemory(); });
212   assert(MemOpIt != I.Operands.end() &&
213          "Instruction must have memory operands");
214 
215   const Operand &MemOp = *MemOpIt;
216 
217   assert(MemOp.isReg() && "Memory operand expected to be register");
218 
219   IT.getValueFor(MemOp) = MCOperand::createReg(Reg);
220 }
221 
222 const MCPhysReg UnavailableRegisters[4] = {RISCV::X0, DefaultLoopCounterReg,
223                                            ScratchIntReg, ScratchMemoryReg};
224 
225 ArrayRef<MCPhysReg> ExegesisRISCVTarget::getUnavailableRegisters() const {
226   return UnavailableRegisters;
227 }
228 
229 Error ExegesisRISCVTarget::randomizeTargetMCOperand(
230     const Instruction &Instr, const Variable &Var, MCOperand &AssignedValue,
231     const BitVector &ForbiddenRegs) const {
232   uint8_t OperandType =
233       Instr.getPrimaryOperand(Var).getExplicitOperandInfo().OperandType;
234 
235   switch (OperandType) {
236   case RISCVOp::OPERAND_FRMARG:
237     AssignedValue = MCOperand::createImm(RISCVFPRndMode::DYN);
238     break;
239   case RISCVOp::OPERAND_SIMM10_LSB0000_NONZERO:
240     AssignedValue = MCOperand::createImm(0b1 << 4);
241     break;
242   case RISCVOp::OPERAND_SIMM6_NONZERO:
243   case RISCVOp::OPERAND_UIMMLOG2XLEN_NONZERO:
244     AssignedValue = MCOperand::createImm(1);
245     break;
246   default:
247     if (OperandType >= RISCVOp::OPERAND_FIRST_RISCV_IMM &&
248         OperandType <= RISCVOp::OPERAND_LAST_RISCV_IMM)
249       AssignedValue = MCOperand::createImm(0);
250   }
251   return Error::success();
252 }
253 
254 std::vector<InstructionTemplate>
255 ExegesisRISCVTarget::generateInstructionVariants(
256     const Instruction &Instr, unsigned int MaxConfigsPerOpcode) const {
257   InstructionTemplate IT{&Instr};
258   for (const Operand &Op : Instr.Operands)
259     if (Op.isMemory()) {
260       IT.getValueFor(Op) = MCOperand::createReg(ScratchMemoryReg);
261     }
262   return {IT};
263 }
264 
265 } // anonymous namespace
266 
267 static ExegesisTarget *getTheRISCVExegesisTarget() {
268   static ExegesisRISCVTarget Target;
269   return &Target;
270 }
271 
272 void InitializeRISCVExegesisTarget() {
273   ExegesisTarget::registerTarget(getTheRISCVExegesisTarget());
274 }
275 
276 } // namespace exegesis
277 } // namespace llvm
278