diff --git a/llvm/lib/Target/RISCV/CMakeLists.txt b/llvm/lib/Target/RISCV/CMakeLists.txt --- a/llvm/lib/Target/RISCV/CMakeLists.txt +++ b/llvm/lib/Target/RISCV/CMakeLists.txt @@ -39,6 +39,7 @@ RISCVRedundantCopyElimination.cpp RISCVRegisterBankInfo.cpp RISCVRegisterInfo.cpp + RISCVRVVInitUndef.cpp RISCVSExtWRemoval.cpp RISCVSubtarget.cpp RISCVTargetMachine.cpp diff --git a/llvm/lib/Target/RISCV/RISCV.h b/llvm/lib/Target/RISCV/RISCV.h --- a/llvm/lib/Target/RISCV/RISCV.h +++ b/llvm/lib/Target/RISCV/RISCV.h @@ -59,6 +59,9 @@ FunctionPass *createRISCVInsertVSETVLIPass(); void initializeRISCVInsertVSETVLIPass(PassRegistry &); +FunctionPass *createRISCVInitUndefPass(); +void initializeRISCVInitUndefPass(PassRegistry &); + FunctionPass *createRISCVRedundantCopyEliminationPass(); void initializeRISCVRedundantCopyEliminationPass(PassRegistry &); diff --git a/llvm/lib/Target/RISCV/RISCVRVVInitUndef.cpp b/llvm/lib/Target/RISCV/RISCVRVVInitUndef.cpp new file mode 100644 --- /dev/null +++ b/llvm/lib/Target/RISCV/RISCVRVVInitUndef.cpp @@ -0,0 +1,161 @@ +//===- RISCVInitUndef.cpp - Initialize undef vector value to zero ---------===// +// +// 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 initializes undef vector value to +// zero to prevent register allocation resulting in a constraint violated result +// for vector instruction. +// +// RISC-V vector instruction has register overlapping constraint for certain +// instructions, and will cause illegal instruction trap if violated, we use +// early clobber to model this constraint, but it can't prevent register +// allocator allocated same or overlapped if the input register is undef value, +// so convert IMPLICIT_DEF to zero initialized could prevent that happen, it's +// not best way to resolve this, and it might emit redundant zero initialized +// instruction for undef value, so ideally we should model the constraint right, +// but before we model the constraint right, it's the only way to prevent that +// happen. +// +// See also: https://github.com/llvm/llvm-project/issues/50157 +// +//===----------------------------------------------------------------------===// + +#include "RISCV.h" +#include "RISCVSubtarget.h" +#include "llvm/CodeGen/MachineFunctionPass.h" +using namespace llvm; + +#define DEBUG_TYPE "riscv-init-undef" +#define RISCV_INIT_UNDEF_NAME "RISCV init undef pass" + +namespace { + +class RISCVInitUndef : public MachineFunctionPass { + const TargetInstrInfo *TII; + MachineRegisterInfo *MRI; + +public: + static char ID; + + RISCVInitUndef() : MachineFunctionPass(ID) { + initializeRISCVInitUndefPass(*PassRegistry::getPassRegistry()); + } + bool runOnMachineFunction(MachineFunction &MF) override; + + void getAnalysisUsage(AnalysisUsage &AU) const override { + AU.setPreservesCFG(); + MachineFunctionPass::getAnalysisUsage(AU); + } + + StringRef getPassName() const override { return RISCV_INIT_UNDEF_NAME; } + +private: + bool processBasicBlock(MachineFunction &MF, MachineBasicBlock &MBB); + void handleImplicitDef(MachineBasicBlock &MBB, + MachineBasicBlock::iterator &Inst); + bool isVectorRegClass(const Register &R); +}; + +} // end anonymous namespace + +char RISCVInitUndef::ID = 0; + +INITIALIZE_PASS(RISCVInitUndef, DEBUG_TYPE, RISCV_INIT_UNDEF_NAME, false, false) + +bool RISCVInitUndef::isVectorRegClass(const Register &R) { + unsigned RegClassID = MRI->getRegClass(R)->getID(); + switch (RegClassID) { + case RISCV::VRRegClassID: + case RISCV::VRM2RegClassID: + case RISCV::VRM4RegClassID: + case RISCV::VRM8RegClassID: + return true; + default: + return false; + } +} + +void RISCVInitUndef::handleImplicitDef(MachineBasicBlock &MBB, + MachineBasicBlock::iterator &Inst) { + MachineInstr &MI = *Inst; + + assert(MI.getOpcode() == TargetOpcode::IMPLICIT_DEF); + // All vector registers must be explicitly defined to prevent violate vector + // register constaint. + unsigned Reg = MI.getOperand(0).getReg(); + LLVM_DEBUG( + dbgs() + << "Emitting vmv.v.i vd, 0 with VLMAX for implicit vector register " + << Reg << '\n'); + + unsigned Opcode; + unsigned RegClassID = MRI->getRegClass(Reg)->getID(); + switch (RegClassID) { + case RISCV::VRRegClassID: + Opcode = RISCV::PseudoVMV_V_I_M1; + break; + case RISCV::VRM2RegClassID: + Opcode = RISCV::PseudoVMV_V_I_M2; + break; + case RISCV::VRM4RegClassID: + Opcode = RISCV::PseudoVMV_V_I_M4; + break; + case RISCV::VRM8RegClassID: + Opcode = RISCV::PseudoVMV_V_I_M8; + break; + default: + llvm_unreachable("Unexpected register class?"); + } + + BuildMI(MBB, Inst, MI.getDebugLoc(), TII->get(Opcode), Reg) + .addImm(0) + .addImm(/* VLMAX */ -1) + .addImm(4); + + Inst = MBB.erase(Inst); // Remove the pseudo instruction + + // We want to leave I pointing to the previous instruction, but what if we + // just erased the first instruction? + if (Inst == MBB.begin()) { + LLVM_DEBUG(dbgs() << "Inserting dummy KILL\n"); + Inst = BuildMI(MBB, Inst, DebugLoc(), TII->get(TargetOpcode::KILL)); + } else + --Inst; +} + +bool RISCVInitUndef::processBasicBlock(MachineFunction &MF, + MachineBasicBlock &MBB) { + bool Changed = false; + for (MachineBasicBlock::iterator I = MBB.begin(); I != MBB.end(); ++I) { + MachineInstr &MI = *I; + if (MI.isImplicitDef()) { + auto DstReg = MI.getOperand(0).getReg(); + if (isVectorRegClass(DstReg)) { + handleImplicitDef(MBB, I); + Changed = true; + } + } + } + return Changed; +} + +bool RISCVInitUndef::runOnMachineFunction(MachineFunction &MF) { + const RISCVSubtarget &ST = MF.getSubtarget(); + if (!ST.hasVInstructions()) + return false; + + MRI = &MF.getRegInfo(); + TII = ST.getInstrInfo(); + + bool Changed = false; + for (MachineBasicBlock &BB : MF) + Changed |= processBasicBlock(MF, BB); + + return Changed; +} + +FunctionPass *llvm::createRISCVInitUndefPass() { return new RISCVInitUndef(); } diff --git a/llvm/lib/Target/RISCV/RISCVTargetMachine.cpp b/llvm/lib/Target/RISCV/RISCVTargetMachine.cpp --- a/llvm/lib/Target/RISCV/RISCVTargetMachine.cpp +++ b/llvm/lib/Target/RISCV/RISCVTargetMachine.cpp @@ -53,6 +53,7 @@ initializeRISCVSExtWRemovalPass(*PR); initializeRISCVExpandPseudoPass(*PR); initializeRISCVInsertVSETVLIPass(*PR); + initializeRISCVInitUndefPass(*PR); } static StringRef computeDataLayout(const Triple &TT) { @@ -253,6 +254,9 @@ void RISCVPassConfig::addPreRegAlloc() { if (TM->getOptLevel() != CodeGenOpt::None) addPass(createRISCVMergeBaseOffsetOptPass()); + + if (getOptimizeRegAlloc()) + addPass(createRISCVInitUndefPass()); addPass(createRISCVInsertVSETVLIPass()); } diff --git a/llvm/test/CodeGen/RISCV/rvv/vfma-vp.ll b/llvm/test/CodeGen/RISCV/rvv/vfma-vp.ll --- a/llvm/test/CodeGen/RISCV/rvv/vfma-vp.ll +++ b/llvm/test/CodeGen/RISCV/rvv/vfma-vp.ll @@ -1217,6 +1217,8 @@ ; CHECK-NEXT: vs8r.v v8, (a1) # Unknown-size Folded Spill ; CHECK-NEXT: li a3, 0 ; CHECK-NEXT: csrr a1, vlenb +; CHECK-NEXT: vsetvli a5, zero, e16, m1, ta, mu +; CHECK-NEXT: vmv.v.i v0, 0 ; CHECK-NEXT: vsetvli a5, zero, e8, mf4, ta, mu ; CHECK-NEXT: slli a5, a1, 3 ; CHECK-NEXT: add a6, a2, a5 @@ -1236,7 +1238,7 @@ ; CHECK-NEXT: vs8r.v v8, (a5) # Unknown-size Folded Spill ; CHECK-NEXT: srli a6, a1, 3 ; CHECK-NEXT: sub a5, a4, a1 -; CHECK-NEXT: vslidedown.vx v0, v0, a6 +; CHECK-NEXT: vslidedown.vx v0, v1, a6 ; CHECK-NEXT: bltu a4, a5, .LBB92_2 ; CHECK-NEXT: # %bb.1: ; CHECK-NEXT: mv a3, a5