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