//===------------------- RISCVCustomBehaviour.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 // //===----------------------------------------------------------------------===// /// \file /// /// This file implements methods from the RISCVCustomBehaviour class. /// //===----------------------------------------------------------------------===// #include "RISCVCustomBehaviour.h" #include "MCTargetDesc/RISCVMCTargetDesc.h" #include "RISCV.h" #include "TargetInfo/RISCVTargetInfo.h" #include "llvm/MC/TargetRegistry.h" #include "llvm/Support/Debug.h" #define DEBUG_TYPE "llvm-mca-riscv-custombehaviour" // This brings in a table with primary key of // base instruction opcode and lmul and maps // to the opcode of the pseudo instruction. namespace RISCVVInversePseudosTable { using namespace llvm; using namespace llvm::RISCV; struct PseudoInfo { uint16_t Pseudo; uint16_t BaseInstr; uint8_t VLMul; uint8_t SEW; }; #define GET_RISCVVInversePseudosTable_IMPL #define GET_RISCVVInversePseudosTable_DECL #include "RISCVGenSearchableTables.inc" } // end namespace RISCVVInversePseudosTable namespace llvm { namespace mca { const llvm::StringRef RISCVLMULInstrument::DESC_NAME = "RISCV-LMUL"; bool RISCVLMULInstrument::isDataValid(llvm::StringRef Data) { // Return true if not one of the valid LMUL strings return StringSwitch(Data) .Cases("M1", "M2", "M4", "M8", "MF2", "MF4", "MF8", true) .Default(false); } uint8_t RISCVLMULInstrument::getLMUL() const { // assertion prevents us from needing llvm_unreachable in the StringSwitch // below assert(isDataValid(getData()) && "Cannot get LMUL because invalid Data value"); // These are the LMUL values that are used in RISC-V tablegen return StringSwitch(getData()) .Case("M1", 0b000) .Case("M2", 0b001) .Case("M4", 0b010) .Case("M8", 0b011) .Case("MF2", 0b111) .Case("MF4", 0b110) .Case("MF8", 0b101); } const llvm::StringRef RISCVSEWInstrument::DESC_NAME = "RISCV-SEW"; bool RISCVSEWInstrument::isDataValid(llvm::StringRef Data) { // Return true if not one of the valid SEW strings return StringSwitch(Data) .Cases("E8", "E16", "E32", "E64", true) .Default(false); } uint8_t RISCVSEWInstrument::getSEW() const { // assertion prevents us from needing llvm_unreachable in the StringSwitch // below assert(isDataValid(getData()) && "Cannot get SEW because invalid Data value"); // These are the LMUL values that are used in RISC-V tablegen return StringSwitch(getData()) .Case("E8", 8) .Case("E16", 16) .Case("E32", 32) .Case("E64", 64); } bool RISCVInstrumentManager::supportsInstrumentType( llvm::StringRef Type) const { return Type == RISCVLMULInstrument::DESC_NAME || Type == RISCVSEWInstrument::DESC_NAME; } UniqueInstrument RISCVInstrumentManager::createInstrument(llvm::StringRef Desc, llvm::StringRef Data) { if (Desc == RISCVLMULInstrument::DESC_NAME) { if (!RISCVLMULInstrument::isDataValid(Data)) { LLVM_DEBUG(dbgs() << "RVCB: Bad data for instrument kind " << Desc << ": " << Data << '\n'); return nullptr; } return std::make_unique(Data); } if (Desc == RISCVSEWInstrument::DESC_NAME) { if (!RISCVSEWInstrument::isDataValid(Data)) { LLVM_DEBUG(dbgs() << "RVCB: Bad data for instrument kind " << Desc << ": " << Data << '\n'); return nullptr; } return std::make_unique(Data); } LLVM_DEBUG(dbgs() << "RVCB: Unknown instrumentation Desc: " << Desc << '\n'); return nullptr; } SmallVector RISCVInstrumentManager::createInstruments(const MCInst &Inst) { if (Inst.getOpcode() == RISCV::VSETVLI || Inst.getOpcode() == RISCV::VSETIVLI) { LLVM_DEBUG(dbgs() << "RVCB: Found VSETVLI and creating instrument for it: " << Inst << "\n"); unsigned VTypeI = Inst.getOperand(2).getImm(); RISCVII::VLMUL VLMUL = RISCVVType::getVLMUL(VTypeI); StringRef LMUL; switch (VLMUL) { case RISCVII::LMUL_1: LMUL = "M1"; break; case RISCVII::LMUL_2: LMUL = "M2"; break; case RISCVII::LMUL_4: LMUL = "M4"; break; case RISCVII::LMUL_8: LMUL = "M8"; break; case RISCVII::LMUL_F2: LMUL = "MF2"; break; case RISCVII::LMUL_F4: LMUL = "MF4"; break; case RISCVII::LMUL_F8: LMUL = "MF8"; break; case RISCVII::LMUL_RESERVED: llvm_unreachable("Cannot create instrument for LMUL_RESERVED"); } SmallVector Instruments; Instruments.emplace_back( createInstrument(RISCVLMULInstrument::DESC_NAME, LMUL)); unsigned SEW = RISCVVType::getSEW(VTypeI); StringRef SEWStr; switch (SEW) { case 8: SEWStr = "E8"; break; case 16: SEWStr = "E16"; break; case 32: SEWStr = "E32"; break; case 64: SEWStr = "E64"; break; default: llvm_unreachable("Cannot create instrument for SEW"); } Instruments.emplace_back( createInstrument(RISCVSEWInstrument::DESC_NAME, SEWStr)); return Instruments; } return SmallVector(); } static std::pair getEEWAndEMUL(unsigned Opcode, RISCVII::VLMUL LMUL, uint8_t SEW) { uint8_t EEW; switch (Opcode) { case RISCV::VLM_V: case RISCV::VSM_V: case RISCV::VLE8_V: case RISCV::VSE8_V: case RISCV::VLSE8_V: case RISCV::VSSE8_V: EEW = 8; break; case RISCV::VLE16_V: case RISCV::VSE16_V: case RISCV::VLSE16_V: case RISCV::VSSE16_V: EEW = 16; break; case RISCV::VLE32_V: case RISCV::VSE32_V: case RISCV::VLSE32_V: case RISCV::VSSE32_V: EEW = 32; break; case RISCV::VLE64_V: case RISCV::VSE64_V: case RISCV::VLSE64_V: case RISCV::VSSE64_V: EEW = 64; break; default: llvm_unreachable("Could not determine EEW from Opcode"); } auto EMUL = RISCVVType::getSameRatioLMUL(SEW, LMUL, EEW); if (!EEW) llvm_unreachable("Invalid SEW or LMUL for new ratio"); return std::make_pair(EEW, *EMUL); } bool opcodeHasEEWAndEMULInfo(unsigned short Opcode) { return Opcode == RISCV::VLM_V || Opcode == RISCV::VSM_V || Opcode == RISCV::VLE8_V || Opcode == RISCV::VSE8_V || Opcode == RISCV::VLE16_V || Opcode == RISCV::VSE16_V || Opcode == RISCV::VLE32_V || Opcode == RISCV::VSE32_V || Opcode == RISCV::VLE64_V || Opcode == RISCV::VSE64_V || Opcode == RISCV::VLSE8_V || Opcode == RISCV::VSSE8_V || Opcode == RISCV::VLSE16_V || Opcode == RISCV::VSSE16_V || Opcode == RISCV::VLSE32_V || Opcode == RISCV::VSSE32_V || Opcode == RISCV::VLSE64_V || Opcode == RISCV::VSSE64_V; } unsigned RISCVInstrumentManager::getSchedClassID( const MCInstrInfo &MCII, const MCInst &MCI, const llvm::SmallVector &IVec) const { unsigned short Opcode = MCI.getOpcode(); unsigned SchedClassID = MCII.get(Opcode).getSchedClass(); // Unpack all possible RISC-V instruments from IVec. RISCVLMULInstrument *LI = nullptr; RISCVSEWInstrument *SI = nullptr; for (auto &I : IVec) { if (I->getDesc() == RISCVLMULInstrument::DESC_NAME) LI = static_cast(I); else if (I->getDesc() == RISCVSEWInstrument::DESC_NAME) SI = static_cast(I); } // Need LMUL or LMUL, SEW in order to override opcode. If no LMUL is provided, // then no option to override. if (!LI) { LLVM_DEBUG( dbgs() << "RVCB: Did not use instrumentation to override Opcode.\n"); return SchedClassID; } uint8_t LMUL = LI->getLMUL(); // getBaseInfo works with (Opcode, LMUL, 0) if no SEW instrument, // or (Opcode, LMUL, SEW) if SEW instrument is active, and depends on LMUL // and SEW, or (Opcode, LMUL, 0) if does not depend on SEW. uint8_t SEW = SI ? SI->getSEW() : 0; const RISCVVInversePseudosTable::PseudoInfo *RVV = nullptr; if (opcodeHasEEWAndEMULInfo(Opcode)) { RISCVII::VLMUL VLMUL = static_cast(LMUL); auto [EEW, EMUL] = getEEWAndEMUL(Opcode, VLMUL, SEW); RVV = RISCVVInversePseudosTable::getBaseInfo(Opcode, EMUL, EEW); } else { // Check if it depends on LMUL and SEW RVV = RISCVVInversePseudosTable::getBaseInfo(Opcode, LMUL, SEW); // Check if it depends only on LMUL if (!RVV) RVV = RISCVVInversePseudosTable::getBaseInfo(Opcode, LMUL, 0); } // Not a RVV instr if (!RVV) { LLVM_DEBUG( dbgs() << "RVCB: Could not find PseudoInstruction for Opcode " << MCII.getName(Opcode) << ", LMUL=" << (LI ? LI->getData() : "Unspecified") << ", SEW=" << (SI ? SI->getData() : "Unspecified") << ". Ignoring instrumentation and using original SchedClassID=" << SchedClassID << '\n'); return SchedClassID; } // Override using pseudo LLVM_DEBUG(dbgs() << "RVCB: Found Pseudo Instruction for Opcode " << MCII.getName(Opcode) << ", LMUL=" << LI->getData() << ", SEW=" << (SI ? SI->getData() : "Unspecified") << ". Overriding original SchedClassID=" << SchedClassID << " with " << MCII.getName(RVV->Pseudo) << '\n'); return MCII.get(RVV->Pseudo).getSchedClass(); } } // namespace mca } // namespace llvm using namespace llvm; using namespace mca; static InstrumentManager * createRISCVInstrumentManager(const MCSubtargetInfo &STI, const MCInstrInfo &MCII) { return new RISCVInstrumentManager(STI, MCII); } /// Extern function to initialize the targets for the RISC-V backend extern "C" LLVM_EXTERNAL_VISIBILITY void LLVMInitializeRISCVTargetMCA() { TargetRegistry::RegisterInstrumentManager(getTheRISCV32Target(), createRISCVInstrumentManager); TargetRegistry::RegisterInstrumentManager(getTheRISCV64Target(), createRISCVInstrumentManager); }