1 //===-- RISCVInsertWriteVXRM.cpp - Insert Write of RISC-V VXRM CSR --------===//
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
7 //===----------------------------------------------------------------------===//
9 // This pass inserts writes to the VXRM CSR as needed by vector instructions.
10 // Each instruction that uses VXRM carries an operand that contains its required
11 // VXRM value. This pass tries to optimize placement to avoid redundant writes
14 // This is done using 2 dataflow algorithms. The first is a forward data flow
15 // to calculate where a VXRM value is available. The second is a backwards
16 // dataflow to determine where a VXRM value is anticipated.
18 // Finally, we use the results of these two dataflows to insert VXRM writes
19 // where a value is anticipated, but not available.
21 // FIXME: This pass does not split critical edges, so there can still be some
24 // FIXME: If we are willing to have writes that aren't always needed, we could
25 // reduce the number of VXRM writes in some cases.
26 //===----------------------------------------------------------------------===//
28 #include "MCTargetDesc/RISCVBaseInfo.h"
30 #include "RISCVSubtarget.h"
31 #include "llvm/CodeGen/MachineFunctionPass.h"
36 #define DEBUG_TYPE "riscv-insert-write-vxrm"
37 #define RISCV_INSERT_WRITE_VXRM_NAME "RISC-V Insert Write VXRM Pass"
48 } State
= Uninitialized
;
53 static VXRMInfo
getUnknown() {
59 bool isValid() const { return State
!= Uninitialized
; }
60 void setUnknown() { State
= Unknown
; }
61 bool isUnknown() const { return State
== Unknown
; }
63 bool isStatic() const { return State
== Static
; }
65 void setVXRMImm(unsigned Imm
) {
66 assert(Imm
<= 3 && "Unexpected VXRM value");
70 unsigned getVXRMImm() const {
71 assert(isStatic() && VXRMImm
<= 3 && "Unexpected state");
75 bool operator==(const VXRMInfo
&Other
) const {
76 // Uninitialized is only equal to another Uninitialized.
77 if (State
!= Other
.State
)
81 return VXRMImm
== Other
.VXRMImm
;
83 assert((isValid() || isUnknown()) && "Unexpected state");
87 bool operator!=(const VXRMInfo
&Other
) const { return !(*this == Other
); }
89 // Calculate the VXRMInfo visible to a block assuming this and Other are
91 VXRMInfo
intersect(const VXRMInfo
&Other
) const {
92 // If the new value isn't valid, ignore it.
96 // If this value isn't valid, this must be the first predecessor, use it.
100 // If either is unknown, the result is unknown.
101 if (isUnknown() || Other
.isUnknown())
102 return VXRMInfo::getUnknown();
104 // If we have an exact match, return this.
108 // Otherwise the result is unknown.
109 return VXRMInfo::getUnknown();
112 #if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
113 /// Support for debugging, callable in GDB: V->dump()
114 LLVM_DUMP_METHOD
void dump() const {
119 void print(raw_ostream
&OS
) const {
122 OS
<< "Uninitialized";
123 else if (isUnknown())
132 #if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
134 inline raw_ostream
&operator<<(raw_ostream
&OS
, const VXRMInfo
&V
) {
141 // Indicates if the block uses VXRM. Uninitialized means no use.
144 // Indicates the VXRM output from the block. Unitialized means transparent.
147 // Keeps track of the available VXRM value at the start of the basic bloc.
148 VXRMInfo AvailableIn
;
150 // Keeps track of the available VXRM value at the end of the basic block.
151 VXRMInfo AvailableOut
;
153 // Keeps track of what VXRM is anticipated at the start of the basic block.
154 VXRMInfo AnticipatedIn
;
156 // Keeps track of what VXRM is anticipated at the end of the basic block.
157 VXRMInfo AnticipatedOut
;
159 // Keeps track of whether the block is already in the queue.
162 BlockData() = default;
165 class RISCVInsertWriteVXRM
: public MachineFunctionPass
{
166 const TargetInstrInfo
*TII
;
168 std::vector
<BlockData
> BlockInfo
;
169 std::queue
<const MachineBasicBlock
*> WorkList
;
174 RISCVInsertWriteVXRM() : MachineFunctionPass(ID
) {}
176 bool runOnMachineFunction(MachineFunction
&MF
) override
;
178 void getAnalysisUsage(AnalysisUsage
&AU
) const override
{
179 AU
.setPreservesCFG();
180 MachineFunctionPass::getAnalysisUsage(AU
);
183 StringRef
getPassName() const override
{
184 return RISCV_INSERT_WRITE_VXRM_NAME
;
188 bool computeVXRMChanges(const MachineBasicBlock
&MBB
);
189 void computeAvailable(const MachineBasicBlock
&MBB
);
190 void computeAnticipated(const MachineBasicBlock
&MBB
);
191 void emitWriteVXRM(MachineBasicBlock
&MBB
);
194 } // end anonymous namespace
196 char RISCVInsertWriteVXRM::ID
= 0;
198 INITIALIZE_PASS(RISCVInsertWriteVXRM
, DEBUG_TYPE
, RISCV_INSERT_WRITE_VXRM_NAME
,
201 static bool ignoresVXRM(const MachineInstr
&MI
) {
202 switch (RISCV::getRVVMCOpcode(MI
.getOpcode())) {
205 case RISCV::VNCLIP_WI
:
206 case RISCV::VNCLIPU_WI
:
207 return MI
.getOperand(3).getImm() == 0;
211 bool RISCVInsertWriteVXRM::computeVXRMChanges(const MachineBasicBlock
&MBB
) {
212 BlockData
&BBInfo
= BlockInfo
[MBB
.getNumber()];
214 bool NeedVXRMWrite
= false;
215 for (const MachineInstr
&MI
: MBB
) {
216 int VXRMIdx
= RISCVII::getVXRMOpNum(MI
.getDesc());
217 if (VXRMIdx
>= 0 && !ignoresVXRM(MI
)) {
218 unsigned NewVXRMImm
= MI
.getOperand(VXRMIdx
).getImm();
220 if (!BBInfo
.VXRMUse
.isValid())
221 BBInfo
.VXRMUse
.setVXRMImm(NewVXRMImm
);
223 BBInfo
.VXRMOut
.setVXRMImm(NewVXRMImm
);
224 NeedVXRMWrite
= true;
228 if (MI
.isCall() || MI
.isInlineAsm() || MI
.modifiesRegister(RISCV::VXRM
)) {
229 if (!BBInfo
.VXRMUse
.isValid())
230 BBInfo
.VXRMUse
.setUnknown();
232 BBInfo
.VXRMOut
.setUnknown();
236 return NeedVXRMWrite
;
239 void RISCVInsertWriteVXRM::computeAvailable(const MachineBasicBlock
&MBB
) {
240 BlockData
&BBInfo
= BlockInfo
[MBB
.getNumber()];
242 BBInfo
.InQueue
= false;
245 if (MBB
.pred_empty()) {
246 Available
.setUnknown();
248 for (const MachineBasicBlock
*P
: MBB
.predecessors())
249 Available
= Available
.intersect(BlockInfo
[P
->getNumber()].AvailableOut
);
252 // If we don't have any valid available info, wait until we do.
253 if (!Available
.isValid())
256 if (Available
!= BBInfo
.AvailableIn
) {
257 BBInfo
.AvailableIn
= Available
;
258 LLVM_DEBUG(dbgs() << "AvailableIn state of " << printMBBReference(MBB
)
259 << " changed to " << BBInfo
.AvailableIn
<< "\n");
262 if (BBInfo
.VXRMOut
.isValid())
263 Available
= BBInfo
.VXRMOut
;
265 if (Available
== BBInfo
.AvailableOut
)
268 BBInfo
.AvailableOut
= Available
;
269 LLVM_DEBUG(dbgs() << "AvailableOut state of " << printMBBReference(MBB
)
270 << " changed to " << BBInfo
.AvailableOut
<< "\n");
272 // Add the successors to the work list so that we can propagate.
273 for (MachineBasicBlock
*S
: MBB
.successors()) {
274 if (!BlockInfo
[S
->getNumber()].InQueue
) {
275 BlockInfo
[S
->getNumber()].InQueue
= true;
281 void RISCVInsertWriteVXRM::computeAnticipated(const MachineBasicBlock
&MBB
) {
282 BlockData
&BBInfo
= BlockInfo
[MBB
.getNumber()];
284 BBInfo
.InQueue
= false;
286 VXRMInfo Anticipated
;
287 if (MBB
.succ_empty()) {
288 Anticipated
.setUnknown();
290 for (const MachineBasicBlock
*S
: MBB
.successors())
292 Anticipated
.intersect(BlockInfo
[S
->getNumber()].AnticipatedIn
);
295 // If we don't have any valid anticipated info, wait until we do.
296 if (!Anticipated
.isValid())
299 if (Anticipated
!= BBInfo
.AnticipatedOut
) {
300 BBInfo
.AnticipatedOut
= Anticipated
;
301 LLVM_DEBUG(dbgs() << "AnticipatedOut state of " << printMBBReference(MBB
)
302 << " changed to " << BBInfo
.AnticipatedOut
<< "\n");
305 // If this block reads VXRM, copy it.
306 if (BBInfo
.VXRMUse
.isValid())
307 Anticipated
= BBInfo
.VXRMUse
;
309 if (Anticipated
== BBInfo
.AnticipatedIn
)
312 BBInfo
.AnticipatedIn
= Anticipated
;
313 LLVM_DEBUG(dbgs() << "AnticipatedIn state of " << printMBBReference(MBB
)
314 << " changed to " << BBInfo
.AnticipatedIn
<< "\n");
316 // Add the predecessors to the work list so that we can propagate.
317 for (MachineBasicBlock
*P
: MBB
.predecessors()) {
318 if (!BlockInfo
[P
->getNumber()].InQueue
) {
319 BlockInfo
[P
->getNumber()].InQueue
= true;
325 void RISCVInsertWriteVXRM::emitWriteVXRM(MachineBasicBlock
&MBB
) {
326 const BlockData
&BBInfo
= BlockInfo
[MBB
.getNumber()];
328 VXRMInfo Info
= BBInfo
.AvailableIn
;
330 // Flag to indicates we need to insert a VXRM write. We want to delay it as
331 // late as possible in this block.
332 bool PendingInsert
= false;
334 // Insert VXRM write if anticipated and not available.
335 if (BBInfo
.AnticipatedIn
.isStatic()) {
336 // If this is the entry block and the value is anticipated, insert.
337 if (MBB
.isEntryBlock()) {
338 PendingInsert
= true;
340 // Search for any predecessors that wouldn't satisfy our requirement and
341 // insert a write VXRM if needed.
342 // NOTE: If one predecessor is able to provide the requirement, but
343 // another isn't, it means we have a critical edge. The better placement
344 // would be to split the critical edge.
345 for (MachineBasicBlock
*P
: MBB
.predecessors()) {
346 const BlockData
&PInfo
= BlockInfo
[P
->getNumber()];
347 // If it's available out of the predecessor, then we're ok.
348 if (PInfo
.AvailableOut
.isStatic() &&
349 PInfo
.AvailableOut
.getVXRMImm() ==
350 BBInfo
.AnticipatedIn
.getVXRMImm())
352 // If the predecessor anticipates this value for all its succesors,
353 // then a write to VXRM would have already occured before this block is
355 if (PInfo
.AnticipatedOut
.isStatic() &&
356 PInfo
.AnticipatedOut
.getVXRMImm() ==
357 BBInfo
.AnticipatedIn
.getVXRMImm())
359 PendingInsert
= true;
364 Info
= BBInfo
.AnticipatedIn
;
367 for (MachineInstr
&MI
: MBB
) {
368 int VXRMIdx
= RISCVII::getVXRMOpNum(MI
.getDesc());
369 if (VXRMIdx
>= 0 && !ignoresVXRM(MI
)) {
370 unsigned NewVXRMImm
= MI
.getOperand(VXRMIdx
).getImm();
372 if (PendingInsert
|| !Info
.isStatic() ||
373 Info
.getVXRMImm() != NewVXRMImm
) {
374 assert((!PendingInsert
||
375 (Info
.isStatic() && Info
.getVXRMImm() == NewVXRMImm
)) &&
376 "Pending VXRM insertion mismatch");
377 LLVM_DEBUG(dbgs() << "Inserting before "; MI
.print(dbgs()));
378 BuildMI(MBB
, MI
, MI
.getDebugLoc(), TII
->get(RISCV::WriteVXRMImm
))
380 PendingInsert
= false;
383 MI
.addOperand(MachineOperand::CreateReg(RISCV::VXRM
, /*IsDef*/ false,
385 Info
.setVXRMImm(NewVXRMImm
);
389 if (MI
.isCall() || MI
.isInlineAsm() || MI
.modifiesRegister(RISCV::VXRM
))
393 // If all our successors anticipate a value, do the insert.
394 // NOTE: It's possible that not all predecessors of our successor provide the
395 // correct value. This can occur on critical edges. If we don't split the
396 // critical edge we'll also have a write vxrm in the succesor that is
397 // redundant with this one.
399 (BBInfo
.AnticipatedOut
.isStatic() &&
401 Info
.getVXRMImm() != BBInfo
.AnticipatedOut
.getVXRMImm()))) {
402 assert((!PendingInsert
||
403 (Info
.isStatic() && BBInfo
.AnticipatedOut
.isStatic() &&
404 Info
.getVXRMImm() == BBInfo
.AnticipatedOut
.getVXRMImm())) &&
405 "Pending VXRM insertion mismatch");
406 LLVM_DEBUG(dbgs() << "Inserting at end of " << printMBBReference(MBB
)
407 << " changing to " << BBInfo
.AnticipatedOut
<< "\n");
408 BuildMI(MBB
, MBB
.getFirstTerminator(), DebugLoc(),
409 TII
->get(RISCV::WriteVXRMImm
))
410 .addImm(BBInfo
.AnticipatedOut
.getVXRMImm());
414 bool RISCVInsertWriteVXRM::runOnMachineFunction(MachineFunction
&MF
) {
415 // Skip if the vector extension is not enabled.
416 const RISCVSubtarget
&ST
= MF
.getSubtarget
<RISCVSubtarget
>();
417 if (!ST
.hasVInstructions())
420 TII
= ST
.getInstrInfo();
422 assert(BlockInfo
.empty() && "Expect empty block infos");
423 BlockInfo
.resize(MF
.getNumBlockIDs());
425 // Phase 1 - collect block information.
426 bool NeedVXRMChange
= false;
427 for (const MachineBasicBlock
&MBB
: MF
)
428 NeedVXRMChange
|= computeVXRMChanges(MBB
);
430 if (!NeedVXRMChange
) {
435 // Phase 2 - Compute available VXRM using a forward walk.
436 for (const MachineBasicBlock
&MBB
: MF
) {
438 BlockInfo
[MBB
.getNumber()].InQueue
= true;
440 while (!WorkList
.empty()) {
441 const MachineBasicBlock
&MBB
= *WorkList
.front();
443 computeAvailable(MBB
);
446 // Phase 3 - Compute anticipated VXRM using a backwards walk.
447 for (const MachineBasicBlock
&MBB
: llvm::reverse(MF
)) {
449 BlockInfo
[MBB
.getNumber()].InQueue
= true;
451 while (!WorkList
.empty()) {
452 const MachineBasicBlock
&MBB
= *WorkList
.front();
454 computeAnticipated(MBB
);
457 // Phase 4 - Emit VXRM writes at the earliest place possible.
458 for (MachineBasicBlock
&MBB
: MF
)
466 FunctionPass
*llvm::createRISCVInsertWriteVXRMPass() {
467 return new RISCVInsertWriteVXRM();