1//===-- RISCVMoveMerger.cpp - RISC-V move merge pass ----------------------===//
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 contains a pass that performs move related peephole optimizations
10// as Zcmp has specified. This pass should be run after register allocation.
11//
12// This pass also supports Xqccmp, which has identical instructions.
13//
14//===----------------------------------------------------------------------===//
15
16#include "RISCVInstrInfo.h"
17#include "RISCVSubtarget.h"
18
19using namespace llvm;
20
21#define RISCV_MOVE_MERGE_NAME "RISC-V Zcmp move merging pass"
22
23namespace {
24struct RISCVMoveMerge : public MachineFunctionPass {
25 static char ID;
26
27 RISCVMoveMerge() : MachineFunctionPass(ID) {}
28
29 const RISCVInstrInfo *TII;
30 const TargetRegisterInfo *TRI;
31
32 // Track which register units have been modified and used.
33 LiveRegUnits ModifiedRegUnits, UsedRegUnits;
34
35 bool isCandidateToMergeMVA01S(const DestSourcePair &RegPair);
36 bool isCandidateToMergeMVSA01(const DestSourcePair &RegPair);
37 // Merge the two instructions indicated into a single pair instruction.
38 MachineBasicBlock::iterator
39 mergePairedInsns(MachineBasicBlock::iterator I,
40 MachineBasicBlock::iterator Paired, unsigned Opcode);
41
42 // Look for C.MV instruction that can be combined with
43 // the given instruction into CM.MVA01S or CM.MVSA01. Return the matching
44 // instruction if one exists.
45 MachineBasicBlock::iterator
46 findMatchingInst(MachineBasicBlock::iterator &MBBI, unsigned InstOpcode,
47 const DestSourcePair &RegPair);
48 bool mergeMoveSARegPair(const RISCVSubtarget &STI, MachineBasicBlock &MBB);
49 bool runOnMachineFunction(MachineFunction &Fn) override;
50
51 StringRef getPassName() const override { return RISCV_MOVE_MERGE_NAME; }
52};
53
54char RISCVMoveMerge::ID = 0;
55
56} // end of anonymous namespace
57
58INITIALIZE_PASS(RISCVMoveMerge, "riscv-move-merge", RISCV_MOVE_MERGE_NAME,
59 false, false)
60
61static bool isMoveFromAToS(unsigned Opcode) {
62 switch (Opcode) {
63 case RISCV::CM_MVA01S:
64 case RISCV::QC_CM_MVA01S:
65 return true;
66 default:
67 return false;
68 }
69}
70
71static unsigned getMoveFromAToSOpcode(const RISCVSubtarget &STI) {
72 if (STI.hasStdExtZcmp())
73 return RISCV::CM_MVA01S;
74
75 if (STI.hasVendorXqccmp())
76 return RISCV::QC_CM_MVA01S;
77
78 llvm_unreachable("Unhandled subtarget with paired A to S move.");
79}
80
81static bool isMoveFromSToA(unsigned Opcode) {
82 switch (Opcode) {
83 case RISCV::CM_MVSA01:
84 case RISCV::QC_CM_MVSA01:
85 return true;
86 default:
87 return false;
88 }
89}
90
91static unsigned getMoveFromSToAOpcode(const RISCVSubtarget &STI) {
92 if (STI.hasStdExtZcmp())
93 return RISCV::CM_MVSA01;
94
95 if (STI.hasVendorXqccmp())
96 return RISCV::QC_CM_MVSA01;
97
98 llvm_unreachable("Unhandled subtarget with paired S to A move");
99}
100
101// Check if registers meet CM.MVA01S constraints.
102bool RISCVMoveMerge::isCandidateToMergeMVA01S(const DestSourcePair &RegPair) {
103 Register Destination = RegPair.Destination->getReg();
104 Register Source = RegPair.Source->getReg();
105 // If destination is not a0 or a1.
106 if ((Destination == RISCV::X10 || Destination == RISCV::X11) &&
107 RISCV::SR07RegClass.contains(Reg: Source))
108 return true;
109 return false;
110}
111
112// Check if registers meet CM.MVSA01 constraints.
113bool RISCVMoveMerge::isCandidateToMergeMVSA01(const DestSourcePair &RegPair) {
114 Register Destination = RegPair.Destination->getReg();
115 Register Source = RegPair.Source->getReg();
116 // If Source is s0 - s7.
117 if ((Source == RISCV::X10 || Source == RISCV::X11) &&
118 RISCV::SR07RegClass.contains(Reg: Destination))
119 return true;
120 return false;
121}
122
123MachineBasicBlock::iterator
124RISCVMoveMerge::mergePairedInsns(MachineBasicBlock::iterator I,
125 MachineBasicBlock::iterator Paired,
126 unsigned Opcode) {
127 const MachineOperand *Sreg1, *Sreg2;
128 MachineBasicBlock::iterator E = I->getParent()->end();
129 MachineBasicBlock::iterator NextI = next_nodbg(It: I, End: E);
130 DestSourcePair FirstPair = TII->isCopyInstrImpl(MI: *I).value();
131 DestSourcePair PairedRegs = TII->isCopyInstrImpl(MI: *Paired).value();
132 Register ARegInFirstPair = isMoveFromAToS(Opcode)
133 ? FirstPair.Destination->getReg()
134 : FirstPair.Source->getReg();
135
136 if (NextI == Paired)
137 NextI = next_nodbg(It: NextI, End: E);
138 DebugLoc DL = I->getDebugLoc();
139
140 // The order of S-reg depends on which instruction holds A0, instead of
141 // the order of register pair.
142 // e,g.
143 // mv a1, s1
144 // mv a0, s2 => cm.mva01s s2,s1
145 //
146 // mv a0, s2
147 // mv a1, s1 => cm.mva01s s2,s1
148 bool StartWithX10 = ARegInFirstPair == RISCV::X10;
149 if (isMoveFromAToS(Opcode)) {
150 Sreg1 = StartWithX10 ? FirstPair.Source : PairedRegs.Source;
151 Sreg2 = StartWithX10 ? PairedRegs.Source : FirstPair.Source;
152 } else {
153 Sreg1 = StartWithX10 ? FirstPair.Destination : PairedRegs.Destination;
154 Sreg2 = StartWithX10 ? PairedRegs.Destination : FirstPair.Destination;
155 }
156
157 BuildMI(BB&: *I->getParent(), I, MIMD: DL, MCID: TII->get(Opcode)).add(MO: *Sreg1).add(MO: *Sreg2);
158
159 I->eraseFromParent();
160 Paired->eraseFromParent();
161 return NextI;
162}
163
164MachineBasicBlock::iterator
165RISCVMoveMerge::findMatchingInst(MachineBasicBlock::iterator &MBBI,
166 unsigned InstOpcode,
167 const DestSourcePair &RegPair) {
168 MachineBasicBlock::iterator E = MBBI->getParent()->end();
169
170 // Track which register units have been modified and used between the first
171 // insn and the second insn.
172 ModifiedRegUnits.clear();
173 UsedRegUnits.clear();
174
175 for (MachineBasicBlock::iterator I = next_nodbg(It: MBBI, End: E); I != E;
176 I = next_nodbg(It: I, End: E)) {
177
178 MachineInstr &MI = *I;
179
180 if (auto SecondPair = TII->isCopyInstrImpl(MI)) {
181 Register SourceReg = SecondPair->Source->getReg();
182 Register DestReg = SecondPair->Destination->getReg();
183
184 if (isMoveFromAToS(Opcode: InstOpcode) && isCandidateToMergeMVA01S(RegPair: *SecondPair)) {
185 // If register pair is valid and destination registers are different.
186 if ((RegPair.Destination->getReg() == DestReg))
187 return E;
188
189 // If paired destination register was modified or used, the source reg
190 // was modified, there is no possibility of finding matching
191 // instruction so exit early.
192 if (!ModifiedRegUnits.available(Reg: DestReg) ||
193 !UsedRegUnits.available(Reg: DestReg) ||
194 !ModifiedRegUnits.available(Reg: SourceReg))
195 return E;
196
197 return I;
198 } else if (isMoveFromSToA(Opcode: InstOpcode) &&
199 isCandidateToMergeMVSA01(RegPair: *SecondPair)) {
200 if ((RegPair.Source->getReg() == SourceReg) ||
201 (RegPair.Destination->getReg() == DestReg))
202 return E;
203
204 if (!ModifiedRegUnits.available(Reg: DestReg) ||
205 !UsedRegUnits.available(Reg: DestReg) ||
206 !ModifiedRegUnits.available(Reg: SourceReg))
207 return E;
208
209 return I;
210 }
211 }
212 // Update modified / used register units.
213 LiveRegUnits::accumulateUsedDefed(MI, ModifiedRegUnits, UsedRegUnits, TRI);
214 }
215 return E;
216}
217
218// Finds instructions, which could be represented as C.MV instructions and
219// merged into CM.MVA01S or CM.MVSA01.
220bool RISCVMoveMerge::mergeMoveSARegPair(const RISCVSubtarget &STI,
221 MachineBasicBlock &MBB) {
222 bool Modified = false;
223
224 for (MachineBasicBlock::iterator MBBI = MBB.begin(), E = MBB.end();
225 MBBI != E;) {
226 // Check if the instruction can be compressed to C.MV instruction. If it
227 // can, return Dest/Src register pair.
228 auto RegPair = TII->isCopyInstrImpl(MI: *MBBI);
229 if (RegPair.has_value()) {
230 unsigned Opcode = 0;
231
232 if (isCandidateToMergeMVA01S(RegPair: *RegPair))
233 Opcode = getMoveFromAToSOpcode(STI);
234 else if (isCandidateToMergeMVSA01(RegPair: *RegPair))
235 Opcode = getMoveFromSToAOpcode(STI);
236 else {
237 ++MBBI;
238 continue;
239 }
240
241 MachineBasicBlock::iterator Paired =
242 findMatchingInst(MBBI, InstOpcode: Opcode, RegPair: RegPair.value());
243 // If matching instruction can be found merge them.
244 if (Paired != E) {
245 MBBI = mergePairedInsns(I: MBBI, Paired, Opcode);
246 Modified = true;
247 continue;
248 }
249 }
250 ++MBBI;
251 }
252 return Modified;
253}
254
255bool RISCVMoveMerge::runOnMachineFunction(MachineFunction &Fn) {
256 if (skipFunction(F: Fn.getFunction()))
257 return false;
258
259 const RISCVSubtarget *Subtarget = &Fn.getSubtarget<RISCVSubtarget>();
260 if (!(Subtarget->hasStdExtZcmp() || Subtarget->hasVendorXqccmp()))
261 return false;
262
263 TII = Subtarget->getInstrInfo();
264 TRI = Subtarget->getRegisterInfo();
265 // Resize the modified and used register unit trackers. We do this once
266 // per function and then clear the register units each time we optimize a
267 // move.
268 ModifiedRegUnits.init(TRI: *TRI);
269 UsedRegUnits.init(TRI: *TRI);
270 bool Modified = false;
271 for (auto &MBB : Fn)
272 Modified |= mergeMoveSARegPair(STI: *Subtarget, MBB);
273 return Modified;
274}
275
276/// createRISCVMoveMergePass - returns an instance of the
277/// move merge pass.
278FunctionPass *llvm::createRISCVMoveMergePass() { return new RISCVMoveMerge(); }
279