[bazel] Port for baf27862ddb23c3854cb6782a3f1675da4722a50
[llvm-project.git] / llvm / tools / llvm-exegesis / lib / RISCV / Target.cpp
blobd70f609c5e0808def33f9f966bb4c270e2d8e704
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 //===----------------------------------------------------------------------===//
9 #include "../Target.h"
11 #include "MCTargetDesc/RISCVBaseInfo.h"
12 #include "MCTargetDesc/RISCVMCTargetDesc.h"
13 #include "MCTargetDesc/RISCVMatInt.h"
14 #include "RISCVInstrInfo.h"
16 // include computeAvailableFeatures and computeRequiredFeatures.
17 #define GET_AVAILABLE_OPCODE_CHECKER
18 #include "RISCVGenInstrInfo.inc"
20 #include "llvm/CodeGen/MachineInstrBuilder.h"
22 #include <vector>
24 namespace llvm {
25 namespace exegesis {
27 #include "RISCVGenExegesis.inc"
29 namespace {
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;
37 RISCVMatInt::generateMCInstSeq(Value.getSExtValue(), STI, DestReg, MCInstSeq);
39 std::vector<MCInst> MatIntInstrs(MCInstSeq.begin(), MCInstSeq.end());
40 return MatIntInstrs;
43 const MCPhysReg ScratchIntReg = RISCV::X30; // t5
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;
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);
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 {};
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;
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);
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);
103 class ExegesisRISCVTarget : public ExegesisTarget {
104 public:
105 ExegesisRISCVTarget();
107 bool matchesArch(Triple::ArchType Arch) const override;
109 std::vector<MCInst> setRegTo(const MCSubtargetInfo &STI, MCRegister Reg,
110 const APInt &Value) const override;
112 MCRegister getDefaultLoopCounterRegister(const Triple &) const override;
114 void decrementLoopCounterAndJump(MachineBasicBlock &MBB,
115 MachineBasicBlock &TargetMBB,
116 const MCInstrInfo &MII,
117 MCRegister LoopRegister) const override;
119 MCRegister getScratchMemoryRegister(const Triple &TT) const override;
121 void fillMemoryOperands(InstructionTemplate &IT, MCRegister Reg,
122 unsigned Offset) const override;
124 ArrayRef<MCPhysReg> getUnavailableRegisters() const override;
126 bool allowAsBackToBack(const Instruction &Instr) const override {
127 return !Instr.Description.isPseudo();
130 Error randomizeTargetMCOperand(const Instruction &Instr, const Variable &Var,
131 MCOperand &AssignedValue,
132 const BitVector &ForbiddenRegs) const override;
134 std::vector<InstructionTemplate>
135 generateInstructionVariants(const Instruction &Instr,
136 unsigned MaxConfigsPerOpcode) const override;
139 ExegesisRISCVTarget::ExegesisRISCVTarget()
140 : ExegesisTarget(RISCVCpuPfmCounters, RISCV_MC::isOpcodeAvailable) {}
142 bool ExegesisRISCVTarget::matchesArch(Triple::ArchType Arch) const {
143 return Arch == Triple::riscv32 || Arch == Triple::riscv64;
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);
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()};
172 errs() << "setRegTo is not implemented for Reg " << Reg
173 << ", results will be unreliable\n";
174 return {};
177 const MCPhysReg DefaultLoopCounterReg = RISCV::X31; // t6
178 const MCPhysReg ScratchMemoryReg = RISCV::X10; // a0
180 MCRegister
181 ExegesisRISCVTarget::getDefaultLoopCounterRegister(const Triple &) const {
182 return DefaultLoopCounterReg;
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);
198 MCRegister
199 ExegesisRISCVTarget::getScratchMemoryRegister(const Triple &TT) const {
200 return ScratchMemoryReg; // a0
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();
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");
215 const Operand &MemOp = *MemOpIt;
217 assert(MemOp.isReg() && "Memory operand expected to be register");
219 IT.getValueFor(MemOp) = MCOperand::createReg(Reg);
222 const MCPhysReg UnavailableRegisters[4] = {RISCV::X0, DefaultLoopCounterReg,
223 ScratchIntReg, ScratchMemoryReg};
225 ArrayRef<MCPhysReg> ExegesisRISCVTarget::getUnavailableRegisters() const {
226 return UnavailableRegisters;
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;
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);
251 return Error::success();
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);
262 return {IT};
265 } // anonymous namespace
267 static ExegesisTarget *getTheRISCVExegesisTarget() {
268 static ExegesisRISCVTarget Target;
269 return &Target;
272 void InitializeRISCVExegesisTarget() {
273 ExegesisTarget::registerTarget(getTheRISCVExegesisTarget());
276 } // namespace exegesis
277 } // namespace llvm