Skip to content

Commit 93fde2e

Browse files
committed
[RISCV] Add a pass to rewrite rd to x0 for non-computational instrs whose return values are unused
When AMOs are used to implement parallel reduction operations, typically the return value would be discarded. This patch adds a peephole pass `RISCVDeadRegisterDefinitions`. It rewrites `rd` to `x0` when `rd` is marked as dead. It may improve the register allocation and reduce pipeline hazards on CPUs without register renaming and OOO. Comparison with GCC: https://godbolt.org/z/bKaxnEcec Reviewed By: craig.topper Differential Revision: https://reviews.llvm.org/D158759
1 parent fba457d commit 93fde2e

19 files changed

+198
-103
lines changed

llvm/lib/Target/RISCV/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ add_public_tablegen_target(RISCVCommonTableGen)
2727
add_llvm_target(RISCVCodeGen
2828
RISCVAsmPrinter.cpp
2929
RISCVCodeGenPrepare.cpp
30+
RISCVDeadRegisterDefinitions.cpp
3031
RISCVMakeCompressible.cpp
3132
RISCVExpandAtomicPseudoInsts.cpp
3233
RISCVExpandPseudoInsts.cpp

llvm/lib/Target/RISCV/RISCV.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,9 @@ class RISCVTargetMachine;
3333
FunctionPass *createRISCVCodeGenPreparePass();
3434
void initializeRISCVCodeGenPreparePass(PassRegistry &);
3535

36+
FunctionPass *createRISCVDeadRegisterDefinitionsPass();
37+
void initializeRISCVDeadRegisterDefinitionsPass(PassRegistry &);
38+
3639
FunctionPass *createRISCVISelDag(RISCVTargetMachine &TM,
3740
CodeGenOptLevel OptLevel);
3841

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
//===- RISCVDeadRegisterDefinitions.cpp - Replace dead defs w/ zero reg --===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===---------------------------------------------------------------------===//
8+
//
9+
// This pass rewrites Rd to x0 for instrs whose return values are unused.
10+
//
11+
//===---------------------------------------------------------------------===//
12+
13+
#include "RISCV.h"
14+
#include "RISCVInstrInfo.h"
15+
#include "RISCVSubtarget.h"
16+
#include "llvm/ADT/Statistic.h"
17+
#include "llvm/CodeGen/MachineFunctionPass.h"
18+
#include "llvm/CodeGen/MachineRegisterInfo.h"
19+
20+
using namespace llvm;
21+
#define DEBUG_TYPE "riscv-dead-defs"
22+
#define RISCV_DEAD_REG_DEF_NAME "RISC-V Dead register definitions"
23+
24+
STATISTIC(NumDeadDefsReplaced, "Number of dead definitions replaced");
25+
26+
namespace {
27+
class RISCVDeadRegisterDefinitions : public MachineFunctionPass {
28+
public:
29+
static char ID;
30+
31+
RISCVDeadRegisterDefinitions() : MachineFunctionPass(ID) {
32+
initializeRISCVDeadRegisterDefinitionsPass(
33+
*PassRegistry::getPassRegistry());
34+
}
35+
bool runOnMachineFunction(MachineFunction &MF) override;
36+
void getAnalysisUsage(AnalysisUsage &AU) const override {
37+
AU.setPreservesCFG();
38+
MachineFunctionPass::getAnalysisUsage(AU);
39+
}
40+
41+
StringRef getPassName() const override { return RISCV_DEAD_REG_DEF_NAME; }
42+
};
43+
} // end anonymous namespace
44+
45+
char RISCVDeadRegisterDefinitions::ID = 0;
46+
INITIALIZE_PASS(RISCVDeadRegisterDefinitions, DEBUG_TYPE,
47+
RISCV_DEAD_REG_DEF_NAME, false, false)
48+
49+
FunctionPass *llvm::createRISCVDeadRegisterDefinitionsPass() {
50+
return new RISCVDeadRegisterDefinitions();
51+
}
52+
53+
bool RISCVDeadRegisterDefinitions::runOnMachineFunction(MachineFunction &MF) {
54+
if (skipFunction(MF.getFunction()))
55+
return false;
56+
57+
const MachineRegisterInfo *MRI = &MF.getRegInfo();
58+
const TargetInstrInfo *TII = MF.getSubtarget().getInstrInfo();
59+
const TargetRegisterInfo *TRI = MF.getSubtarget().getRegisterInfo();
60+
LLVM_DEBUG(dbgs() << "***** RISCVDeadRegisterDefinitions *****\n");
61+
62+
bool MadeChange = false;
63+
for (MachineBasicBlock &MBB : MF) {
64+
for (MachineInstr &MI : MBB) {
65+
// We only handle non-computational instructions since some NOP encodings
66+
// are reserved for HINT instructions.
67+
const MCInstrDesc &Desc = MI.getDesc();
68+
if (!Desc.mayLoad() && !Desc.mayStore() &&
69+
!Desc.hasUnmodeledSideEffects())
70+
continue;
71+
// For PseudoVSETVLIX0, Rd = X0 has special meaning.
72+
if (MI.getOpcode() == RISCV::PseudoVSETVLIX0)
73+
continue;
74+
for (int I = 0, E = Desc.getNumDefs(); I != E; ++I) {
75+
MachineOperand &MO = MI.getOperand(I);
76+
if (!MO.isReg() || !MO.isDef() || MO.isEarlyClobber())
77+
continue;
78+
// Be careful not to change the register if it's a tied operand.
79+
if (MI.isRegTiedToUseOperand(I)) {
80+
LLVM_DEBUG(dbgs() << " Ignoring, def is tied operand.\n");
81+
continue;
82+
}
83+
// We should not have any relevant physreg defs that are replacable by
84+
// zero before register allocation. So we just check for dead vreg defs.
85+
Register Reg = MO.getReg();
86+
if (!Reg.isVirtual() || (!MO.isDead() && !MRI->use_nodbg_empty(Reg)))
87+
continue;
88+
LLVM_DEBUG(dbgs() << " Dead def operand #" << I << " in:\n ";
89+
MI.print(dbgs()));
90+
const TargetRegisterClass *RC = TII->getRegClass(Desc, I, TRI, MF);
91+
if (!(RC && RC->contains(RISCV::X0))) {
92+
LLVM_DEBUG(dbgs() << " Ignoring, register is not a GPR.\n");
93+
continue;
94+
}
95+
MO.setReg(RISCV::X0);
96+
MO.setIsDead();
97+
LLVM_DEBUG(dbgs() << " Replacing with zero register. New:\n ";
98+
MI.print(dbgs()));
99+
++NumDeadDefsReplaced;
100+
MadeChange = true;
101+
}
102+
}
103+
}
104+
105+
return MadeChange;
106+
}

llvm/lib/Target/RISCV/RISCVInsertVSETVLI.cpp

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1592,22 +1592,6 @@ bool RISCVInsertVSETVLI::runOnMachineFunction(MachineFunction &MF) {
15921592
for (MachineBasicBlock &MBB : MF)
15931593
doLocalPostpass(MBB);
15941594

1595-
// Once we're fully done rewriting all the instructions, do a final pass
1596-
// through to check for VSETVLIs which write to an unused destination.
1597-
// For the non X0, X0 variant, we can replace the destination register
1598-
// with X0 to reduce register pressure. This is really a generic
1599-
// optimization which can be applied to any dead def (TODO: generalize).
1600-
for (MachineBasicBlock &MBB : MF) {
1601-
for (MachineInstr &MI : MBB) {
1602-
if (MI.getOpcode() == RISCV::PseudoVSETVLI ||
1603-
MI.getOpcode() == RISCV::PseudoVSETIVLI) {
1604-
Register VRegDef = MI.getOperand(0).getReg();
1605-
if (VRegDef != RISCV::X0 && MRI->use_nodbg_empty(VRegDef))
1606-
MI.getOperand(0).setReg(RISCV::X0);
1607-
}
1608-
}
1609-
}
1610-
16111595
// Insert PseudoReadVL after VLEFF/VLSEGFF and replace it with the vl output
16121596
// of VLEFF/VLSEGFF.
16131597
for (MachineBasicBlock &MBB : MF)

llvm/lib/Target/RISCV/RISCVTargetMachine.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,13 @@ static cl::opt<bool> EnableRISCVCopyPropagation(
7171
cl::desc("Enable the copy propagation with RISC-V copy instr"),
7272
cl::init(true), cl::Hidden);
7373

74+
static cl::opt<bool> EnableRISCVDeadRegisterElimination(
75+
"riscv-enable-dead-defs", cl::Hidden,
76+
cl::desc("Enable the pass that removes dead"
77+
" definitons and replaces stores to"
78+
" them with stores to x0"),
79+
cl::init(true));
80+
7481
extern "C" LLVM_EXTERNAL_VISIBILITY void LLVMInitializeRISCVTarget() {
7582
RegisterTargetMachine<RISCVTargetMachine> X(getTheRISCV32Target());
7683
RegisterTargetMachine<RISCVTargetMachine> Y(getTheRISCV64Target());
@@ -79,6 +86,7 @@ extern "C" LLVM_EXTERNAL_VISIBILITY void LLVMInitializeRISCVTarget() {
7986
initializeRISCVO0PreLegalizerCombinerPass(*PR);
8087
initializeRISCVPreLegalizerCombinerPass(*PR);
8188
initializeKCFIPass(*PR);
89+
initializeRISCVDeadRegisterDefinitionsPass(*PR);
8290
initializeRISCVMakeCompressibleOptPass(*PR);
8391
initializeRISCVGatherScatterLoweringPass(*PR);
8492
initializeRISCVCodeGenPreparePass(*PR);
@@ -401,6 +409,9 @@ void RISCVPassConfig::addPreRegAlloc() {
401409
if (TM->getOptLevel() != CodeGenOptLevel::None)
402410
addPass(createRISCVMergeBaseOffsetOptPass());
403411
addPass(createRISCVInsertVSETVLIPass());
412+
if (TM->getOptLevel() != CodeGenOptLevel::None &&
413+
EnableRISCVDeadRegisterElimination)
414+
addPass(createRISCVDeadRegisterDefinitionsPass());
404415
addPass(createRISCVInsertReadWriteCSRPass());
405416
}
406417

llvm/test/CodeGen/RISCV/O3-pipeline.ll

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,7 @@
108108
; CHECK-NEXT: RISC-V Pre-RA pseudo instruction expansion pass
109109
; CHECK-NEXT: RISC-V Merge Base Offset
110110
; CHECK-NEXT: RISC-V Insert VSETVLI pass
111+
; CHECK-NEXT: RISC-V Dead register definitions
111112
; CHECK-NEXT: RISC-V Insert Read/Write CSR Pass
112113
; CHECK-NEXT: Detect Dead Lanes
113114
; CHECK-NEXT: RISC-V init undef pass

0 commit comments

Comments
 (0)