//===-- Target.cpp ----------------------------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "../Target.h" #include "MCTargetDesc/RISCVBaseInfo.h" #include "MCTargetDesc/RISCVMCTargetDesc.h" #include "MCTargetDesc/RISCVMatInt.h" #include "RISCVInstrInfo.h" // include computeAvailableFeatures and computeRequiredFeatures. #define GET_AVAILABLE_OPCODE_CHECKER #include "RISCVGenInstrInfo.inc" #include "llvm/CodeGen/MachineInstrBuilder.h" #include namespace llvm { namespace exegesis { #include "RISCVGenExegesis.inc" namespace { // Stores constant value to a general-purpose (integer) register. static std::vector loadIntReg(const MCSubtargetInfo &STI, MCRegister Reg, const APInt &Value) { SmallVector MCInstSeq; MCRegister DestReg = Reg; RISCVMatInt::generateMCInstSeq(Value.getSExtValue(), STI, DestReg, MCInstSeq); std::vector MatIntInstrs(MCInstSeq.begin(), MCInstSeq.end()); return MatIntInstrs; } const MCPhysReg ScratchIntReg = RISCV::X30; // t5 // Stores constant bits to a floating-point register. static std::vector loadFPRegBits(const MCSubtargetInfo &STI, MCRegister Reg, const APInt &Bits, unsigned FmvOpcode) { std::vector Instrs = loadIntReg(STI, ScratchIntReg, Bits); Instrs.push_back(MCInstBuilder(FmvOpcode).addReg(Reg).addReg(ScratchIntReg)); return Instrs; } // main idea is: // we support APInt only if (represented as double) it has zero fractional // part: 1.0, 2.0, 3.0, etc... then we can do the trick: write int to tmp reg t5 // and then do FCVT this is only reliable thing in 32-bit mode, otherwise we // need to use __floatsidf static std::vector loadFP64RegBits32(const MCSubtargetInfo &STI, MCRegister Reg, const APInt &Bits) { double D = Bits.bitsToDouble(); double IPart; double FPart = std::modf(D, &IPart); if (std::abs(FPart) > std::numeric_limits::epsilon()) { errs() << "loadFP64RegBits32 is not implemented for doubles like " << D << ", please remove fractional part\n"; return {}; } std::vector Instrs = loadIntReg(STI, ScratchIntReg, Bits); Instrs.push_back( MCInstBuilder(RISCV::FCVT_D_W).addReg(Reg).addReg(ScratchIntReg)); return Instrs; } static MCInst nop() { // ADDI X0, X0, 0 return MCInstBuilder(RISCV::ADDI) .addReg(RISCV::X0) .addReg(RISCV::X0) .addImm(0); } static bool isVectorRegList(MCRegister Reg) { return RISCV::VRM2RegClass.contains(Reg) || RISCV::VRM4RegClass.contains(Reg) || RISCV::VRM8RegClass.contains(Reg) || RISCV::VRN2M1RegClass.contains(Reg) || RISCV::VRN2M2RegClass.contains(Reg) || RISCV::VRN2M4RegClass.contains(Reg) || RISCV::VRN3M1RegClass.contains(Reg) || RISCV::VRN3M2RegClass.contains(Reg) || RISCV::VRN4M1RegClass.contains(Reg) || RISCV::VRN4M2RegClass.contains(Reg) || RISCV::VRN5M1RegClass.contains(Reg) || RISCV::VRN6M1RegClass.contains(Reg) || RISCV::VRN7M1RegClass.contains(Reg) || RISCV::VRN8M1RegClass.contains(Reg); } class ExegesisRISCVTarget : public ExegesisTarget { public: ExegesisRISCVTarget(); bool matchesArch(Triple::ArchType Arch) const override; std::vector setRegTo(const MCSubtargetInfo &STI, MCRegister Reg, const APInt &Value) const override; MCRegister getDefaultLoopCounterRegister(const Triple &) const override; void decrementLoopCounterAndJump(MachineBasicBlock &MBB, MachineBasicBlock &TargetMBB, const MCInstrInfo &MII, MCRegister LoopRegister) const override; MCRegister getScratchMemoryRegister(const Triple &TT) const override; void fillMemoryOperands(InstructionTemplate &IT, MCRegister Reg, unsigned Offset) const override; ArrayRef getUnavailableRegisters() const override; bool allowAsBackToBack(const Instruction &Instr) const override { return !Instr.Description.isPseudo(); } Error randomizeTargetMCOperand(const Instruction &Instr, const Variable &Var, MCOperand &AssignedValue, const BitVector &ForbiddenRegs) const override; std::vector generateInstructionVariants(const Instruction &Instr, unsigned MaxConfigsPerOpcode) const override; }; ExegesisRISCVTarget::ExegesisRISCVTarget() : ExegesisTarget(RISCVCpuPfmCounters, RISCV_MC::isOpcodeAvailable) {} bool ExegesisRISCVTarget::matchesArch(Triple::ArchType Arch) const { return Arch == Triple::riscv32 || Arch == Triple::riscv64; } std::vector ExegesisRISCVTarget::setRegTo(const MCSubtargetInfo &STI, MCRegister Reg, const APInt &Value) const { if (RISCV::GPRRegClass.contains(Reg)) return loadIntReg(STI, Reg, Value); if (RISCV::FPR16RegClass.contains(Reg)) return loadFPRegBits(STI, Reg, Value, RISCV::FMV_H_X); if (RISCV::FPR32RegClass.contains(Reg)) return loadFPRegBits(STI, Reg, Value, RISCV::FMV_W_X); if (RISCV::FPR64RegClass.contains(Reg)) { if (STI.hasFeature(RISCV::Feature64Bit)) return loadFPRegBits(STI, Reg, Value, RISCV::FMV_D_X); return loadFP64RegBits32(STI, Reg, Value); } if (Reg == RISCV::FRM || Reg == RISCV::VL || Reg == RISCV::VLENB || Reg == RISCV::VTYPE || RISCV::GPRPairRegClass.contains(Reg) || RISCV::VRRegClass.contains(Reg) || isVectorRegList(Reg)) { // Don't initialize: // - FRM // - VL, VLENB, VTYPE // - vector registers (and vector register lists) // - Zfinx registers // Generate 'NOP' so that exegesis treats such registers as initialized // (it tries to initialize them with '0' anyway). return {nop()}; } errs() << "setRegTo is not implemented for Reg " << Reg << ", results will be unreliable\n"; return {}; } const MCPhysReg DefaultLoopCounterReg = RISCV::X31; // t6 const MCPhysReg ScratchMemoryReg = RISCV::X10; // a0 MCRegister ExegesisRISCVTarget::getDefaultLoopCounterRegister(const Triple &) const { return DefaultLoopCounterReg; } void ExegesisRISCVTarget::decrementLoopCounterAndJump( MachineBasicBlock &MBB, MachineBasicBlock &TargetMBB, const MCInstrInfo &MII, MCRegister LoopRegister) const { BuildMI(&MBB, DebugLoc(), MII.get(RISCV::ADDI)) .addDef(LoopRegister) .addUse(LoopRegister) .addImm(-1); BuildMI(&MBB, DebugLoc(), MII.get(RISCV::BNE)) .addUse(LoopRegister) .addUse(RISCV::X0) .addMBB(&TargetMBB); } MCRegister ExegesisRISCVTarget::getScratchMemoryRegister(const Triple &TT) const { return ScratchMemoryReg; // a0 } void ExegesisRISCVTarget::fillMemoryOperands(InstructionTemplate &IT, MCRegister Reg, unsigned Offset) const { // TODO: for now we ignore Offset because have no way // to detect it in instruction. auto &I = IT.getInstr(); auto MemOpIt = find_if(I.Operands, [](const Operand &Op) { return Op.isMemory(); }); assert(MemOpIt != I.Operands.end() && "Instruction must have memory operands"); const Operand &MemOp = *MemOpIt; assert(MemOp.isReg() && "Memory operand expected to be register"); IT.getValueFor(MemOp) = MCOperand::createReg(Reg); } const MCPhysReg UnavailableRegisters[4] = {RISCV::X0, DefaultLoopCounterReg, ScratchIntReg, ScratchMemoryReg}; ArrayRef ExegesisRISCVTarget::getUnavailableRegisters() const { return UnavailableRegisters; } Error ExegesisRISCVTarget::randomizeTargetMCOperand( const Instruction &Instr, const Variable &Var, MCOperand &AssignedValue, const BitVector &ForbiddenRegs) const { uint8_t OperandType = Instr.getPrimaryOperand(Var).getExplicitOperandInfo().OperandType; switch (OperandType) { case RISCVOp::OPERAND_FRMARG: AssignedValue = MCOperand::createImm(RISCVFPRndMode::DYN); break; case RISCVOp::OPERAND_SIMM10_LSB0000_NONZERO: AssignedValue = MCOperand::createImm(0b1 << 4); break; case RISCVOp::OPERAND_SIMM6_NONZERO: case RISCVOp::OPERAND_UIMMLOG2XLEN_NONZERO: AssignedValue = MCOperand::createImm(1); break; default: if (OperandType >= RISCVOp::OPERAND_FIRST_RISCV_IMM && OperandType <= RISCVOp::OPERAND_LAST_RISCV_IMM) AssignedValue = MCOperand::createImm(0); } return Error::success(); } std::vector ExegesisRISCVTarget::generateInstructionVariants( const Instruction &Instr, unsigned int MaxConfigsPerOpcode) const { InstructionTemplate IT{&Instr}; for (const Operand &Op : Instr.Operands) if (Op.isMemory()) { IT.getValueFor(Op) = MCOperand::createReg(ScratchMemoryReg); } return {IT}; } } // anonymous namespace static ExegesisTarget *getTheRISCVExegesisTarget() { static ExegesisRISCVTarget Target; return &Target; } void InitializeRISCVExegesisTarget() { ExegesisTarget::registerTarget(getTheRISCVExegesisTarget()); } } // namespace exegesis } // namespace llvm