blob: 74f437f0ed8407067e3d6e71f7c833949013cc24 [file] [log] [blame]
//===- 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)
static bool isRedundantVSETVLI(MachineInstr &MI, MachineInstr *PrevVSETVLI) {
// 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())
return false;
// Does this VSET{I}VLI use the same VTYPE immediate.
int64_t PrevVTYPEImm = PrevVSETVLI->getOperand(2).getImm();
int64_t VTYPEImm = MI.getOperand(2).getImm();
if (PrevVTYPEImm != VTYPEImm)
return false;
if (MI.getOpcode() == RISCV::PseudoVSETIVLI) {
// If the previous opcode wasn't vsetivli we can't compare them.
if (PrevVSETVLI->getOpcode() != RISCV::PseudoVSETIVLI)
return false;
// For VSETIVLI, we can just compare the immediates.
return PrevVSETVLI->getOperand(1).getImm() == MI.getOperand(1).getImm();
}
assert(MI.getOpcode() == RISCV::PseudoVSETVLI);
Register AVLReg = MI.getOperand(1).getReg();
Register PrevOutVL = PrevVSETVLI->getOperand(0).getReg();
// If this VSETVLI isn't changing VL, it is redundant.
if (AVLReg == RISCV::X0 && MI.getOperand(0).getReg() == RISCV::X0)
return true;
// If the previous VSET{I}VLI's output (which isn't X0) is fed into this
// VSETVLI, this one isn't changing VL so is redundant.
// Only perform this on virtual registers to avoid the complexity of having
// to work out if the physical register was clobbered somewhere in between.
if (AVLReg.isVirtual() && AVLReg == PrevOutVL)
return true;
// If the previous opcode isn't vsetvli we can't do any more comparison.
if (PrevVSETVLI->getOpcode() != RISCV::PseudoVSETVLI)
return false;
// Does this VSETVLI use the same AVL register?
if (AVLReg != PrevVSETVLI->getOperand(1).getReg())
return false;
// If the AVLReg is X0 we must be setting VL to VLMAX. Keeping VL unchanged
// was handled above.
if (AVLReg == RISCV::X0) {
// This instruction is setting VL to VLMAX, this is redundant if the
// previous VSETVLI was also setting VL to VLMAX. But it is not redundant
// if they were setting it to any other value or leaving VL unchanged.
return PrevOutVL != RISCV::X0;
}
// This vsetvli is redundant.
return true;
}
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 (isRedundantVSETVLI(MI, PrevVSETVLI)) {
// This VSETVLI is redundant, remove it.
MI.eraseFromParent();
Changed = true;
} else {
// Otherwise update VSET{I}VLI for the next iteration.
PrevVSETVLI = &MI;
}
}
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();
}