Skip to content

Commit 3b8c0b3

Browse files
committed
[RISCV] Add new pass to transform undef to pseudo for vector values.
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 temporary pseudo could prevent that happen, it's not best way to resolve this. Ideally we should model the constraint right, but before we model the constraint right, it's the approach to prevent that happen. See also: #50157 Reviewed By: craig.topper Differential Revision: https://reviews.llvm.org/D129735
1 parent c33fd3b commit 3b8c0b3

11 files changed

+434
-44
lines changed

llvm/lib/Target/RISCV/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ add_llvm_target(RISCVCodeGen
3636
RISCVMergeBaseOffset.cpp
3737
RISCVRedundantCopyElimination.cpp
3838
RISCVRegisterInfo.cpp
39+
RISCVRVVInitUndef.cpp
3940
RISCVSExtWRemoval.cpp
4041
RISCVStripWSuffix.cpp
4142
RISCVSubtarget.cpp

llvm/lib/Target/RISCV/RISCV.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,10 @@ void initializeRISCVInsertVSETVLIPass(PassRegistry &);
7171
FunctionPass *createRISCVRedundantCopyEliminationPass();
7272
void initializeRISCVRedundantCopyEliminationPass(PassRegistry &);
7373

74+
FunctionPass *createRISCVInitUndefPass();
75+
void initializeRISCVInitUndefPass(PassRegistry &);
76+
extern char &RISCVInitUndefID;
77+
7478
InstructionSelector *createRISCVInstructionSelector(const RISCVTargetMachine &,
7579
RISCVSubtarget &,
7680
RISCVRegisterBankInfo &);

llvm/lib/Target/RISCV/RISCVAsmPrinter.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,11 @@ void RISCVAsmPrinter::emitInstruction(const MachineInstr *MI) {
113113
case RISCV::HWASAN_CHECK_MEMACCESS_SHORTGRANULES:
114114
LowerHWASAN_CHECK_MEMACCESS(*MI);
115115
return;
116+
case RISCV::PseudoRVVInitUndefM1:
117+
case RISCV::PseudoRVVInitUndefM2:
118+
case RISCV::PseudoRVVInitUndefM4:
119+
case RISCV::PseudoRVVInitUndefM8:
120+
return;
116121
}
117122

118123
MCInst TmpInst;

llvm/lib/Target/RISCV/RISCVInstrInfo.td

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1870,6 +1870,14 @@ def : Pat<(binop_allwusers<add> GPR:$rs1, (AddiPair:$rs2)),
18701870
(AddiPairImmSmall AddiPair:$rs2))>;
18711871
}
18721872

1873+
/// Empty pseudo for RISCVInitUndefPass
1874+
let hasSideEffects = 0, mayLoad = 0, mayStore = 0, Size = 0, isCodeGenOnly = 1 in {
1875+
def PseudoRVVInitUndefM1 : Pseudo<(outs VR:$vd), (ins), [], "">;
1876+
def PseudoRVVInitUndefM2 : Pseudo<(outs VRM2:$vd), (ins), [], "">;
1877+
def PseudoRVVInitUndefM4 : Pseudo<(outs VRM4:$vd), (ins), [], "">;
1878+
def PseudoRVVInitUndefM8 : Pseudo<(outs VRM8:$vd), (ins), [], "">;
1879+
}
1880+
18731881
//===----------------------------------------------------------------------===//
18741882
// Standard extensions
18751883
//===----------------------------------------------------------------------===//
Lines changed: 274 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,274 @@
1+
//===- RISCVRVVInitUndef.cpp - Initialize undef vector value to pseudo ----===//
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 file implements a function pass that initializes undef vector value to
10+
// temporary pseudo instruction and remove it in expandpseudo pass to prevent
11+
// register allocation resulting in a constraint violated result for vector
12+
// instruction.
13+
//
14+
// RISC-V vector instruction has register overlapping constraint for certain
15+
// instructions, and will cause illegal instruction trap if violated, we use
16+
// early clobber to model this constraint, but it can't prevent register
17+
// allocator allocated same or overlapped if the input register is undef value,
18+
// so convert IMPLICIT_DEF to temporary pseudo instruction and remove it later
19+
// could prevent that happen, it's not best way to resolve this, and it might
20+
// change the order of program or increase the register pressure, so ideally we
21+
// should model the constraint right, but before we model the constraint right,
22+
// it's the only way to prevent that happen.
23+
//
24+
// When we enable the subregister liveness option, it will also trigger same
25+
// issue due to the partial of register is undef. If we pseudoinit the whole
26+
// register, then it will generate redundant COPY instruction. Currently, it
27+
// will generate INSERT_SUBREG to make sure the whole register is occupied
28+
// when program encounter operation that has early-clobber constraint.
29+
//
30+
//
31+
// See also: https://github.com/llvm/llvm-project/issues/50157
32+
//
33+
//===----------------------------------------------------------------------===//
34+
35+
#include "RISCV.h"
36+
#include "RISCVSubtarget.h"
37+
#include "llvm/CodeGen/DetectDeadLanes.h"
38+
#include "llvm/CodeGen/MachineFunctionPass.h"
39+
using namespace llvm;
40+
41+
#define DEBUG_TYPE "riscv-init-undef"
42+
#define RISCV_INIT_UNDEF_NAME "RISCV init undef pass"
43+
44+
namespace {
45+
46+
class RISCVInitUndef : public MachineFunctionPass {
47+
const TargetInstrInfo *TII;
48+
MachineRegisterInfo *MRI;
49+
const RISCVSubtarget *ST;
50+
const TargetRegisterInfo *TRI;
51+
52+
public:
53+
static char ID;
54+
55+
RISCVInitUndef() : MachineFunctionPass(ID) {
56+
initializeRISCVInitUndefPass(*PassRegistry::getPassRegistry());
57+
}
58+
bool runOnMachineFunction(MachineFunction &MF) override;
59+
60+
void getAnalysisUsage(AnalysisUsage &AU) const override {
61+
AU.setPreservesCFG();
62+
MachineFunctionPass::getAnalysisUsage(AU);
63+
}
64+
65+
StringRef getPassName() const override { return RISCV_INIT_UNDEF_NAME; }
66+
67+
private:
68+
bool processBasicBlock(MachineFunction &MF, MachineBasicBlock &MBB,
69+
const DeadLaneDetector &DLD);
70+
bool handleImplicitDef(MachineBasicBlock &MBB,
71+
MachineBasicBlock::iterator &Inst);
72+
bool isVectorRegClass(const Register R);
73+
const TargetRegisterClass *
74+
getVRLargestSuperClass(const TargetRegisterClass *RC) const;
75+
bool handleSubReg(MachineFunction &MF, MachineInstr &MI,
76+
const DeadLaneDetector &DLD);
77+
};
78+
79+
} // end anonymous namespace
80+
81+
char RISCVInitUndef::ID = 0;
82+
INITIALIZE_PASS(RISCVInitUndef, DEBUG_TYPE, RISCV_INIT_UNDEF_NAME, false, false)
83+
char &llvm::RISCVInitUndefID = RISCVInitUndef::ID;
84+
85+
const TargetRegisterClass *
86+
RISCVInitUndef::getVRLargestSuperClass(const TargetRegisterClass *RC) const {
87+
if (RISCV::VRM8RegClass.hasSubClassEq(RC))
88+
return &RISCV::VRM8RegClass;
89+
if (RISCV::VRM4RegClass.hasSubClassEq(RC))
90+
return &RISCV::VRM4RegClass;
91+
if (RISCV::VRM2RegClass.hasSubClassEq(RC))
92+
return &RISCV::VRM2RegClass;
93+
if (RISCV::VRRegClass.hasSubClassEq(RC))
94+
return &RISCV::VRRegClass;
95+
return RC;
96+
}
97+
98+
bool RISCVInitUndef::isVectorRegClass(const Register R) {
99+
const TargetRegisterClass *RC = MRI->getRegClass(R);
100+
return RISCV::VRRegClass.hasSubClassEq(RC) ||
101+
RISCV::VRM2RegClass.hasSubClassEq(RC) ||
102+
RISCV::VRM4RegClass.hasSubClassEq(RC) ||
103+
RISCV::VRM8RegClass.hasSubClassEq(RC);
104+
}
105+
106+
static unsigned getUndefInitOpcode(unsigned RegClassID) {
107+
switch (RegClassID) {
108+
case RISCV::VRRegClassID:
109+
return RISCV::PseudoRVVInitUndefM1;
110+
case RISCV::VRM2RegClassID:
111+
return RISCV::PseudoRVVInitUndefM2;
112+
case RISCV::VRM4RegClassID:
113+
return RISCV::PseudoRVVInitUndefM4;
114+
case RISCV::VRM8RegClassID:
115+
return RISCV::PseudoRVVInitUndefM8;
116+
default:
117+
llvm_unreachable("Unexpected register class.");
118+
}
119+
}
120+
121+
bool RISCVInitUndef::handleImplicitDef(MachineBasicBlock &MBB,
122+
MachineBasicBlock::iterator &Inst) {
123+
const TargetRegisterInfo &TRI =
124+
*MBB.getParent()->getSubtarget().getRegisterInfo();
125+
126+
assert(Inst->getOpcode() == TargetOpcode::IMPLICIT_DEF);
127+
128+
Register Reg = Inst->getOperand(0).getReg();
129+
if (!Reg.isVirtual())
130+
return false;
131+
132+
bool NeedPseudoInit = false;
133+
SmallVector<MachineOperand *, 1> UseMOs;
134+
for (MachineOperand &MO : MRI->use_nodbg_operands(Reg)) {
135+
MachineInstr *UserMI = MO.getParent();
136+
137+
bool HasEarlyClobber = false;
138+
bool TiedToDef = false;
139+
for (MachineOperand &UserMO : UserMI->operands()) {
140+
if (!UserMO.isReg())
141+
continue;
142+
if (UserMO.isEarlyClobber())
143+
HasEarlyClobber = true;
144+
if (UserMO.isUse() && UserMO.isTied() &&
145+
TRI.regsOverlap(UserMO.getReg(), Reg))
146+
TiedToDef = true;
147+
}
148+
if (HasEarlyClobber && !TiedToDef) {
149+
NeedPseudoInit = true;
150+
UseMOs.push_back(&MO);
151+
}
152+
}
153+
154+
if (!NeedPseudoInit)
155+
return false;
156+
157+
LLVM_DEBUG(
158+
dbgs() << "Emitting PseudoRVVInitUndef for implicit vector register "
159+
<< Reg << '\n');
160+
161+
unsigned RegClassID = getVRLargestSuperClass(MRI->getRegClass(Reg))->getID();
162+
unsigned Opcode = getUndefInitOpcode(RegClassID);
163+
164+
BuildMI(MBB, Inst, Inst->getDebugLoc(), TII->get(Opcode), Reg);
165+
166+
Inst = MBB.erase(Inst);
167+
168+
for (auto MO : UseMOs)
169+
MO->setIsUndef(false);
170+
171+
return true;
172+
}
173+
174+
static bool isEarlyClobberMI(MachineInstr &MI) {
175+
return llvm::any_of(MI.defs(), [](const MachineOperand &DefMO) {
176+
return DefMO.isReg() && DefMO.isEarlyClobber();
177+
});
178+
}
179+
180+
bool RISCVInitUndef::handleSubReg(MachineFunction &MF, MachineInstr &MI,
181+
const DeadLaneDetector &DLD) {
182+
bool Changed = false;
183+
184+
for (MachineOperand &UseMO : MI.uses()) {
185+
if (!UseMO.isReg())
186+
continue;
187+
if (!UseMO.getReg().isVirtual())
188+
continue;
189+
190+
Register Reg = UseMO.getReg();
191+
DeadLaneDetector::VRegInfo Info =
192+
DLD.getVRegInfo(Register::virtReg2Index(Reg));
193+
194+
if (Info.UsedLanes == Info.DefinedLanes)
195+
continue;
196+
197+
const TargetRegisterClass *TargetRegClass =
198+
getVRLargestSuperClass(MRI->getRegClass(Reg));
199+
200+
LaneBitmask NeedDef = Info.UsedLanes & ~Info.DefinedLanes;
201+
202+
LLVM_DEBUG({
203+
dbgs() << "Instruction has undef subregister.\n";
204+
dbgs() << printReg(Reg, nullptr)
205+
<< " Used: " << PrintLaneMask(Info.UsedLanes)
206+
<< " Def: " << PrintLaneMask(Info.DefinedLanes)
207+
<< " Need Def: " << PrintLaneMask(NeedDef) << "\n";
208+
});
209+
210+
SmallVector<unsigned> SubRegIndexNeedInsert;
211+
TRI->getCoveringSubRegIndexes(*MRI, TargetRegClass, NeedDef,
212+
SubRegIndexNeedInsert);
213+
214+
Register LatestReg = Reg;
215+
for (auto ind : SubRegIndexNeedInsert) {
216+
Changed = true;
217+
const TargetRegisterClass *SubRegClass =
218+
getVRLargestSuperClass(TRI->getSubRegisterClass(TargetRegClass, ind));
219+
Register TmpInitSubReg = MRI->createVirtualRegister(SubRegClass);
220+
BuildMI(*MI.getParent(), &MI, MI.getDebugLoc(),
221+
TII->get(getUndefInitOpcode(SubRegClass->getID())),
222+
TmpInitSubReg);
223+
Register NewReg = MRI->createVirtualRegister(TargetRegClass);
224+
BuildMI(*MI.getParent(), &MI, MI.getDebugLoc(),
225+
TII->get(TargetOpcode::INSERT_SUBREG), NewReg)
226+
.addReg(LatestReg)
227+
.addReg(TmpInitSubReg)
228+
.addImm(ind);
229+
LatestReg = NewReg;
230+
}
231+
232+
UseMO.setReg(LatestReg);
233+
}
234+
235+
return Changed;
236+
}
237+
238+
bool RISCVInitUndef::processBasicBlock(MachineFunction &MF,
239+
MachineBasicBlock &MBB,
240+
const DeadLaneDetector &DLD) {
241+
bool Changed = false;
242+
for (MachineBasicBlock::iterator I = MBB.begin(); I != MBB.end(); ++I) {
243+
MachineInstr &MI = *I;
244+
if (ST->enableSubRegLiveness() && isEarlyClobberMI(MI))
245+
Changed |= handleSubReg(MF, MI, DLD);
246+
if (MI.isImplicitDef()) {
247+
auto DstReg = MI.getOperand(0).getReg();
248+
if (isVectorRegClass(DstReg))
249+
Changed |= handleImplicitDef(MBB, I);
250+
}
251+
}
252+
return Changed;
253+
}
254+
255+
bool RISCVInitUndef::runOnMachineFunction(MachineFunction &MF) {
256+
ST = &MF.getSubtarget<RISCVSubtarget>();
257+
if (!ST->hasVInstructions())
258+
return false;
259+
260+
MRI = &MF.getRegInfo();
261+
TII = ST->getInstrInfo();
262+
TRI = MRI->getTargetRegisterInfo();
263+
264+
bool Changed = false;
265+
DeadLaneDetector DLD(MRI, TRI);
266+
DLD.computeSubRegisterLaneBitInfo();
267+
268+
for (MachineBasicBlock &BB : MF)
269+
Changed |= processBasicBlock(MF, BB, DLD);
270+
271+
return Changed;
272+
}
273+
274+
FunctionPass *llvm::createRISCVInitUndefPass() { return new RISCVInitUndef(); }

llvm/lib/Target/RISCV/RISCVTargetMachine.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ extern "C" LLVM_EXTERNAL_VISIBILITY void LLVMInitializeRISCVTarget() {
8181
initializeRISCVExpandPseudoPass(*PR);
8282
initializeRISCVInsertVSETVLIPass(*PR);
8383
initializeRISCVDAGToDAGISelPass(*PR);
84+
initializeRISCVInitUndefPass(*PR);
8485
}
8586

8687
static StringRef computeDataLayout(const Triple &TT) {
@@ -260,6 +261,7 @@ class RISCVPassConfig : public TargetPassConfig {
260261
void addMachineSSAOptimization() override;
261262
void addPreRegAlloc() override;
262263
void addPostRegAlloc() override;
264+
void addOptimizedRegAlloc() override;
263265
};
264266
} // namespace
265267

@@ -355,6 +357,13 @@ void RISCVPassConfig::addPreRegAlloc() {
355357
addPass(createRISCVInsertVSETVLIPass());
356358
}
357359

360+
void RISCVPassConfig::addOptimizedRegAlloc() {
361+
if (getOptimizeRegAlloc())
362+
insertPass(&DetectDeadLanesID, &RISCVInitUndefID);
363+
364+
TargetPassConfig::addOptimizedRegAlloc();
365+
}
366+
358367
void RISCVPassConfig::addPostRegAlloc() {
359368
if (TM->getOptLevel() != CodeGenOpt::None && EnableRedundantCopyElimination)
360369
addPass(createRISCVRedundantCopyEliminationPass());

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@
109109
; CHECK-NEXT: RISCV Merge Base Offset
110110
; CHECK-NEXT: RISCV Insert VSETVLI pass
111111
; CHECK-NEXT: Detect Dead Lanes
112+
; CHECK-NEXT: RISCV init undef pass
112113
; CHECK-NEXT: Process Implicit Definitions
113114
; CHECK-NEXT: Remove unreachable machine basic blocks
114115
; CHECK-NEXT: Live Variable Analysis

0 commit comments

Comments
 (0)