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 | |
19 | using namespace llvm; |
20 | |
21 | #define RISCV_MOVE_MERGE_NAME "RISC-V Zcmp move merging pass" |
22 | |
23 | namespace { |
24 | struct 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 | |
54 | char RISCVMoveMerge::ID = 0; |
55 | |
56 | } // end of anonymous namespace |
57 | |
58 | INITIALIZE_PASS(RISCVMoveMerge, "riscv-move-merge" , RISCV_MOVE_MERGE_NAME, |
59 | false, false) |
60 | |
61 | static 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 | |
71 | static 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 | |
81 | static 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 | |
91 | static 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. |
102 | bool 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. |
113 | bool 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 | |
123 | MachineBasicBlock::iterator |
124 | RISCVMoveMerge::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 | |
164 | MachineBasicBlock::iterator |
165 | RISCVMoveMerge::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. |
220 | bool 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 | |
255 | bool 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. |
278 | FunctionPass *llvm::createRISCVMoveMergePass() { return new RISCVMoveMerge(); } |
279 | |