| //===- RISCVCleanupVSETVLI.cpp - Cleanup unneeded VSETVLI instructions ----===// |
| // |
| // 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 |
| // |
| //===----------------------------------------------------------------------===// |
| // |
| // This file implements a function pass that removes duplicate vsetvli |
| // instructions within a basic block. |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "RISCV.h" |
| #include "RISCVSubtarget.h" |
| #include "llvm/CodeGen/MachineFunctionPass.h" |
| using namespace llvm; |
| |
| #define DEBUG_TYPE "riscv-cleanup-vsetvli" |
| #define RISCV_CLEANUP_VSETVLI_NAME "RISCV Cleanup VSETVLI pass" |
| |
| namespace { |
| |
| class RISCVCleanupVSETVLI : public MachineFunctionPass { |
| public: |
| static char ID; |
| |
| RISCVCleanupVSETVLI() : MachineFunctionPass(ID) { |
| initializeRISCVCleanupVSETVLIPass(*PassRegistry::getPassRegistry()); |
| } |
| bool runOnMachineFunction(MachineFunction &MF) override; |
| bool runOnMachineBasicBlock(MachineBasicBlock &MBB); |
| |
| MachineFunctionProperties getRequiredProperties() const override { |
| return MachineFunctionProperties().set( |
| MachineFunctionProperties::Property::IsSSA); |
| } |
| |
| // This pass modifies the program, but does not modify the CFG |
| void getAnalysisUsage(AnalysisUsage &AU) const override { |
| AU.setPreservesCFG(); |
| MachineFunctionPass::getAnalysisUsage(AU); |
| } |
| |
| StringRef getPassName() const override { return RISCV_CLEANUP_VSETVLI_NAME; } |
| }; |
| |
| } // end anonymous namespace |
| |
| char RISCVCleanupVSETVLI::ID = 0; |
| |
| INITIALIZE_PASS(RISCVCleanupVSETVLI, DEBUG_TYPE, |
| RISCV_CLEANUP_VSETVLI_NAME, false, false) |
| |
| bool RISCVCleanupVSETVLI::runOnMachineBasicBlock(MachineBasicBlock &MBB) { |
| bool Changed = false; |
| MachineInstr *PrevVSETVLI = nullptr; |
| |
| for (auto MII = MBB.begin(), MIE = MBB.end(); MII != MIE;) { |
| MachineInstr &MI = *MII++; |
| |
| if (MI.getOpcode() != RISCV::PseudoVSETVLI && |
| MI.getOpcode() != RISCV::PseudoVSETIVLI) { |
| if (PrevVSETVLI && |
| (MI.isCall() || MI.modifiesRegister(RISCV::VL) || |
| MI.modifiesRegister(RISCV::VTYPE))) { |
| // Old VL/VTYPE is overwritten. |
| PrevVSETVLI = nullptr; |
| } |
| continue; |
| } |
| |
| // If we don't have a previous VSET{I}VLI or the VL output isn't dead, we |
| // can't remove this VSETVLI. |
| if (!PrevVSETVLI || !MI.getOperand(0).isDead()) { |
| PrevVSETVLI = &MI; |
| continue; |
| } |
| |
| // If a previous "set vl" instruction opcode is different from this one, we |
| // can't differentiate the AVL values. |
| if (PrevVSETVLI->getOpcode() != MI.getOpcode()) { |
| PrevVSETVLI = &MI; |
| continue; |
| } |
| |
| // The remaining two cases are |
| // 1. PrevVSETVLI = PseudoVSETVLI |
| // MI = PseudoVSETVLI |
| // |
| // 2. PrevVSETVLI = PseudoVSETIVLI |
| // MI = PseudoVSETIVLI |
| Register AVLReg; |
| bool SameAVL = false; |
| if (MI.getOpcode() == RISCV::PseudoVSETVLI) { |
| AVLReg = MI.getOperand(1).getReg(); |
| SameAVL = PrevVSETVLI->getOperand(1).getReg() == AVLReg; |
| } else { // RISCV::PseudoVSETIVLI |
| SameAVL = |
| PrevVSETVLI->getOperand(1).getImm() == MI.getOperand(1).getImm(); |
| } |
| int64_t PrevVTYPEImm = PrevVSETVLI->getOperand(2).getImm(); |
| int64_t VTYPEImm = MI.getOperand(2).getImm(); |
| |
| // Does this VSET{I}VLI use the same AVL register/value and VTYPE immediate? |
| if (!SameAVL || PrevVTYPEImm != VTYPEImm) { |
| PrevVSETVLI = &MI; |
| continue; |
| } |
| |
| // If the AVLReg is X0 we need to look at the output VL of both VSETVLIs. |
| if ((MI.getOpcode() == RISCV::PseudoVSETVLI) && (AVLReg == RISCV::X0)) { |
| assert((PrevVSETVLI->getOpcode() == RISCV::PseudoVSETVLI) && |
| "Unexpected vsetvli opcode."); |
| Register PrevOutVL = PrevVSETVLI->getOperand(0).getReg(); |
| Register OutVL = MI.getOperand(0).getReg(); |
| // We can't remove if the previous VSETVLI left VL unchanged and the |
| // current instruction is setting it to VLMAX. Without knowing the VL |
| // before the previous instruction we don't know if this is a change. |
| if (PrevOutVL == RISCV::X0 && OutVL != RISCV::X0) { |
| PrevVSETVLI = &MI; |
| continue; |
| } |
| } |
| |
| // This VSETVLI is redundant, remove it. |
| MI.eraseFromParent(); |
| Changed = true; |
| } |
| |
| return Changed; |
| } |
| |
| bool RISCVCleanupVSETVLI::runOnMachineFunction(MachineFunction &MF) { |
| if (skipFunction(MF.getFunction())) |
| return false; |
| |
| // Skip if the vector extension is not enabled. |
| const RISCVSubtarget &ST = MF.getSubtarget<RISCVSubtarget>(); |
| if (!ST.hasStdExtV()) |
| return false; |
| |
| bool Changed = false; |
| |
| for (MachineBasicBlock &MBB : MF) |
| Changed |= runOnMachineBasicBlock(MBB); |
| |
| return Changed; |
| } |
| |
| /// Returns an instance of the Cleanup VSETVLI pass. |
| FunctionPass *llvm::createRISCVCleanupVSETVLIPass() { |
| return new RISCVCleanupVSETVLI(); |
| } |