Path: blob/main/contrib/llvm-project/llvm/lib/Target/RISCV/RISCVInsertWriteVXRM.cpp
35269 views
//===-- RISCVInsertWriteVXRM.cpp - Insert Write of RISC-V VXRM CSR --------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//7//8// This pass inserts writes to the VXRM CSR as needed by vector instructions.9// Each instruction that uses VXRM carries an operand that contains its required10// VXRM value. This pass tries to optimize placement to avoid redundant writes11// to VXRM.12//13// This is done using 2 dataflow algorithms. The first is a forward data flow14// to calculate where a VXRM value is available. The second is a backwards15// dataflow to determine where a VXRM value is anticipated.16//17// Finally, we use the results of these two dataflows to insert VXRM writes18// where a value is anticipated, but not available.19//20// FIXME: This pass does not split critical edges, so there can still be some21// redundancy.22//23// FIXME: If we are willing to have writes that aren't always needed, we could24// reduce the number of VXRM writes in some cases.25//===----------------------------------------------------------------------===//2627#include "MCTargetDesc/RISCVBaseInfo.h"28#include "RISCV.h"29#include "RISCVSubtarget.h"30#include "llvm/CodeGen/MachineFunctionPass.h"31#include <queue>3233using namespace llvm;3435#define DEBUG_TYPE "riscv-insert-write-vxrm"36#define RISCV_INSERT_WRITE_VXRM_NAME "RISC-V Insert Write VXRM Pass"3738namespace {3940class VXRMInfo {41uint8_t VXRMImm = 0;4243enum : uint8_t {44Uninitialized,45Static,46Unknown,47} State = Uninitialized;4849public:50VXRMInfo() {}5152static VXRMInfo getUnknown() {53VXRMInfo Info;54Info.setUnknown();55return Info;56}5758bool isValid() const { return State != Uninitialized; }59void setUnknown() { State = Unknown; }60bool isUnknown() const { return State == Unknown; }6162bool isStatic() const { return State == Static; }6364void setVXRMImm(unsigned Imm) {65assert(Imm <= 3 && "Unexpected VXRM value");66VXRMImm = Imm;67State = Static;68}69unsigned getVXRMImm() const {70assert(isStatic() && VXRMImm <= 3 && "Unexpected state");71return VXRMImm;72}7374bool operator==(const VXRMInfo &Other) const {75// Uninitialized is only equal to another Uninitialized.76if (State != Other.State)77return false;7879if (isStatic())80return VXRMImm == Other.VXRMImm;8182assert((isValid() || isUnknown()) && "Unexpected state");83return true;84}8586bool operator!=(const VXRMInfo &Other) const { return !(*this == Other); }8788// Calculate the VXRMInfo visible to a block assuming this and Other are89// both predecessors.90VXRMInfo intersect(const VXRMInfo &Other) const {91// If the new value isn't valid, ignore it.92if (!Other.isValid())93return *this;9495// If this value isn't valid, this must be the first predecessor, use it.96if (!isValid())97return Other;9899// If either is unknown, the result is unknown.100if (isUnknown() || Other.isUnknown())101return VXRMInfo::getUnknown();102103// If we have an exact match, return this.104if (*this == Other)105return *this;106107// Otherwise the result is unknown.108return VXRMInfo::getUnknown();109}110111#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)112/// Support for debugging, callable in GDB: V->dump()113LLVM_DUMP_METHOD void dump() const {114print(dbgs());115dbgs() << "\n";116}117118void print(raw_ostream &OS) const {119OS << '{';120if (!isValid())121OS << "Uninitialized";122else if (isUnknown())123OS << "Unknown";124else125OS << getVXRMImm();126OS << '}';127}128#endif129};130131#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)132LLVM_ATTRIBUTE_USED133inline raw_ostream &operator<<(raw_ostream &OS, const VXRMInfo &V) {134V.print(OS);135return OS;136}137#endif138139struct BlockData {140// Indicates if the block uses VXRM. Uninitialized means no use.141VXRMInfo VXRMUse;142143// Indicates the VXRM output from the block. Unitialized means transparent.144VXRMInfo VXRMOut;145146// Keeps track of the available VXRM value at the start of the basic bloc.147VXRMInfo AvailableIn;148149// Keeps track of the available VXRM value at the end of the basic block.150VXRMInfo AvailableOut;151152// Keeps track of what VXRM is anticipated at the start of the basic block.153VXRMInfo AnticipatedIn;154155// Keeps track of what VXRM is anticipated at the end of the basic block.156VXRMInfo AnticipatedOut;157158// Keeps track of whether the block is already in the queue.159bool InQueue;160161BlockData() = default;162};163164class RISCVInsertWriteVXRM : public MachineFunctionPass {165const TargetInstrInfo *TII;166167std::vector<BlockData> BlockInfo;168std::queue<const MachineBasicBlock *> WorkList;169170public:171static char ID;172173RISCVInsertWriteVXRM() : MachineFunctionPass(ID) {}174175bool runOnMachineFunction(MachineFunction &MF) override;176177void getAnalysisUsage(AnalysisUsage &AU) const override {178AU.setPreservesCFG();179MachineFunctionPass::getAnalysisUsage(AU);180}181182StringRef getPassName() const override {183return RISCV_INSERT_WRITE_VXRM_NAME;184}185186private:187bool computeVXRMChanges(const MachineBasicBlock &MBB);188void computeAvailable(const MachineBasicBlock &MBB);189void computeAnticipated(const MachineBasicBlock &MBB);190void emitWriteVXRM(MachineBasicBlock &MBB);191};192193} // end anonymous namespace194195char RISCVInsertWriteVXRM::ID = 0;196197INITIALIZE_PASS(RISCVInsertWriteVXRM, DEBUG_TYPE, RISCV_INSERT_WRITE_VXRM_NAME,198false, false)199200static bool ignoresVXRM(const MachineInstr &MI) {201switch (RISCV::getRVVMCOpcode(MI.getOpcode())) {202default:203return false;204case RISCV::VNCLIP_WI:205case RISCV::VNCLIPU_WI:206return MI.getOperand(3).getImm() == 0;207}208}209210bool RISCVInsertWriteVXRM::computeVXRMChanges(const MachineBasicBlock &MBB) {211BlockData &BBInfo = BlockInfo[MBB.getNumber()];212213bool NeedVXRMWrite = false;214for (const MachineInstr &MI : MBB) {215int VXRMIdx = RISCVII::getVXRMOpNum(MI.getDesc());216if (VXRMIdx >= 0 && !ignoresVXRM(MI)) {217unsigned NewVXRMImm = MI.getOperand(VXRMIdx).getImm();218219if (!BBInfo.VXRMUse.isValid())220BBInfo.VXRMUse.setVXRMImm(NewVXRMImm);221222BBInfo.VXRMOut.setVXRMImm(NewVXRMImm);223NeedVXRMWrite = true;224continue;225}226227if (MI.isCall() || MI.isInlineAsm() ||228MI.modifiesRegister(RISCV::VXRM, /*TRI=*/nullptr)) {229if (!BBInfo.VXRMUse.isValid())230BBInfo.VXRMUse.setUnknown();231232BBInfo.VXRMOut.setUnknown();233}234}235236return NeedVXRMWrite;237}238239void RISCVInsertWriteVXRM::computeAvailable(const MachineBasicBlock &MBB) {240BlockData &BBInfo = BlockInfo[MBB.getNumber()];241242BBInfo.InQueue = false;243244VXRMInfo Available;245if (MBB.pred_empty()) {246Available.setUnknown();247} else {248for (const MachineBasicBlock *P : MBB.predecessors())249Available = Available.intersect(BlockInfo[P->getNumber()].AvailableOut);250}251252// If we don't have any valid available info, wait until we do.253if (!Available.isValid())254return;255256if (Available != BBInfo.AvailableIn) {257BBInfo.AvailableIn = Available;258LLVM_DEBUG(dbgs() << "AvailableIn state of " << printMBBReference(MBB)259<< " changed to " << BBInfo.AvailableIn << "\n");260}261262if (BBInfo.VXRMOut.isValid())263Available = BBInfo.VXRMOut;264265if (Available == BBInfo.AvailableOut)266return;267268BBInfo.AvailableOut = Available;269LLVM_DEBUG(dbgs() << "AvailableOut state of " << printMBBReference(MBB)270<< " changed to " << BBInfo.AvailableOut << "\n");271272// Add the successors to the work list so that we can propagate.273for (MachineBasicBlock *S : MBB.successors()) {274if (!BlockInfo[S->getNumber()].InQueue) {275BlockInfo[S->getNumber()].InQueue = true;276WorkList.push(S);277}278}279}280281void RISCVInsertWriteVXRM::computeAnticipated(const MachineBasicBlock &MBB) {282BlockData &BBInfo = BlockInfo[MBB.getNumber()];283284BBInfo.InQueue = false;285286VXRMInfo Anticipated;287if (MBB.succ_empty()) {288Anticipated.setUnknown();289} else {290for (const MachineBasicBlock *S : MBB.successors())291Anticipated =292Anticipated.intersect(BlockInfo[S->getNumber()].AnticipatedIn);293}294295// If we don't have any valid anticipated info, wait until we do.296if (!Anticipated.isValid())297return;298299if (Anticipated != BBInfo.AnticipatedOut) {300BBInfo.AnticipatedOut = Anticipated;301LLVM_DEBUG(dbgs() << "AnticipatedOut state of " << printMBBReference(MBB)302<< " changed to " << BBInfo.AnticipatedOut << "\n");303}304305// If this block reads VXRM, copy it.306if (BBInfo.VXRMUse.isValid())307Anticipated = BBInfo.VXRMUse;308309if (Anticipated == BBInfo.AnticipatedIn)310return;311312BBInfo.AnticipatedIn = Anticipated;313LLVM_DEBUG(dbgs() << "AnticipatedIn state of " << printMBBReference(MBB)314<< " changed to " << BBInfo.AnticipatedIn << "\n");315316// Add the predecessors to the work list so that we can propagate.317for (MachineBasicBlock *P : MBB.predecessors()) {318if (!BlockInfo[P->getNumber()].InQueue) {319BlockInfo[P->getNumber()].InQueue = true;320WorkList.push(P);321}322}323}324325void RISCVInsertWriteVXRM::emitWriteVXRM(MachineBasicBlock &MBB) {326const BlockData &BBInfo = BlockInfo[MBB.getNumber()];327328VXRMInfo Info = BBInfo.AvailableIn;329330// Flag to indicates we need to insert a VXRM write. We want to delay it as331// late as possible in this block.332bool PendingInsert = false;333334// Insert VXRM write if anticipated and not available.335if (BBInfo.AnticipatedIn.isStatic()) {336// If this is the entry block and the value is anticipated, insert.337if (MBB.isEntryBlock()) {338PendingInsert = true;339} else {340// Search for any predecessors that wouldn't satisfy our requirement and341// insert a write VXRM if needed.342// NOTE: If one predecessor is able to provide the requirement, but343// another isn't, it means we have a critical edge. The better placement344// would be to split the critical edge.345for (MachineBasicBlock *P : MBB.predecessors()) {346const BlockData &PInfo = BlockInfo[P->getNumber()];347// If it's available out of the predecessor, then we're ok.348if (PInfo.AvailableOut.isStatic() &&349PInfo.AvailableOut.getVXRMImm() ==350BBInfo.AnticipatedIn.getVXRMImm())351continue;352// If the predecessor anticipates this value for all its succesors,353// then a write to VXRM would have already occured before this block is354// executed.355if (PInfo.AnticipatedOut.isStatic() &&356PInfo.AnticipatedOut.getVXRMImm() ==357BBInfo.AnticipatedIn.getVXRMImm())358continue;359PendingInsert = true;360break;361}362}363364Info = BBInfo.AnticipatedIn;365}366367for (MachineInstr &MI : MBB) {368int VXRMIdx = RISCVII::getVXRMOpNum(MI.getDesc());369if (VXRMIdx >= 0 && !ignoresVXRM(MI)) {370unsigned NewVXRMImm = MI.getOperand(VXRMIdx).getImm();371372if (PendingInsert || !Info.isStatic() ||373Info.getVXRMImm() != NewVXRMImm) {374assert((!PendingInsert ||375(Info.isStatic() && Info.getVXRMImm() == NewVXRMImm)) &&376"Pending VXRM insertion mismatch");377LLVM_DEBUG(dbgs() << "Inserting before "; MI.print(dbgs()));378BuildMI(MBB, MI, MI.getDebugLoc(), TII->get(RISCV::WriteVXRMImm))379.addImm(NewVXRMImm);380PendingInsert = false;381}382383MI.addOperand(MachineOperand::CreateReg(RISCV::VXRM, /*IsDef*/ false,384/*IsImp*/ true));385Info.setVXRMImm(NewVXRMImm);386continue;387}388389if (MI.isCall() || MI.isInlineAsm() ||390MI.modifiesRegister(RISCV::VXRM, /*TRI=*/nullptr))391Info.setUnknown();392}393394// If all our successors anticipate a value, do the insert.395// NOTE: It's possible that not all predecessors of our successor provide the396// correct value. This can occur on critical edges. If we don't split the397// critical edge we'll also have a write vxrm in the succesor that is398// redundant with this one.399if (PendingInsert ||400(BBInfo.AnticipatedOut.isStatic() &&401(!Info.isStatic() ||402Info.getVXRMImm() != BBInfo.AnticipatedOut.getVXRMImm()))) {403assert((!PendingInsert ||404(Info.isStatic() && BBInfo.AnticipatedOut.isStatic() &&405Info.getVXRMImm() == BBInfo.AnticipatedOut.getVXRMImm())) &&406"Pending VXRM insertion mismatch");407LLVM_DEBUG(dbgs() << "Inserting at end of " << printMBBReference(MBB)408<< " changing to " << BBInfo.AnticipatedOut << "\n");409BuildMI(MBB, MBB.getFirstTerminator(), DebugLoc(),410TII->get(RISCV::WriteVXRMImm))411.addImm(BBInfo.AnticipatedOut.getVXRMImm());412}413}414415bool RISCVInsertWriteVXRM::runOnMachineFunction(MachineFunction &MF) {416// Skip if the vector extension is not enabled.417const RISCVSubtarget &ST = MF.getSubtarget<RISCVSubtarget>();418if (!ST.hasVInstructions())419return false;420421TII = ST.getInstrInfo();422423assert(BlockInfo.empty() && "Expect empty block infos");424BlockInfo.resize(MF.getNumBlockIDs());425426// Phase 1 - collect block information.427bool NeedVXRMChange = false;428for (const MachineBasicBlock &MBB : MF)429NeedVXRMChange |= computeVXRMChanges(MBB);430431if (!NeedVXRMChange) {432BlockInfo.clear();433return false;434}435436// Phase 2 - Compute available VXRM using a forward walk.437for (const MachineBasicBlock &MBB : MF) {438WorkList.push(&MBB);439BlockInfo[MBB.getNumber()].InQueue = true;440}441while (!WorkList.empty()) {442const MachineBasicBlock &MBB = *WorkList.front();443WorkList.pop();444computeAvailable(MBB);445}446447// Phase 3 - Compute anticipated VXRM using a backwards walk.448for (const MachineBasicBlock &MBB : llvm::reverse(MF)) {449WorkList.push(&MBB);450BlockInfo[MBB.getNumber()].InQueue = true;451}452while (!WorkList.empty()) {453const MachineBasicBlock &MBB = *WorkList.front();454WorkList.pop();455computeAnticipated(MBB);456}457458// Phase 4 - Emit VXRM writes at the earliest place possible.459for (MachineBasicBlock &MBB : MF)460emitWriteVXRM(MBB);461462BlockInfo.clear();463464return true;465}466467FunctionPass *llvm::createRISCVInsertWriteVXRMPass() {468return new RISCVInsertWriteVXRM();469}470471472