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 // Calculate the VXRMInfo visible to a block assuming this and Other
113 // are both predecessors. To allow speculatively running WriteVXRM
114 // we will ignore Unknowns if one of this and Other have valid
115 // WriteVXRM. Rationale: WriteVXRM causes a pipeline flush in some
116 // uarchs and moving it outside loops is very important for some
118 VXRMInfo
intersectAnticipated(const VXRMInfo
&Other
) const {
119 // If the new value isn't valid, ignore it.
120 if (!Other
.isValid())
123 // If this value isn't valid, this must be the first predecessor, use it.
127 // If either is unknown, the result is the other one.
130 if (Other
.isUnknown())
133 // If we have an exact match, return this.
137 // Otherwise the result is unknown.
138 return VXRMInfo::getUnknown();
141 #if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
142 /// Support for debugging, callable in GDB: V->dump()
143 LLVM_DUMP_METHOD
void dump() const {
148 void print(raw_ostream
&OS
) const {
151 OS
<< "Uninitialized";
152 else if (isUnknown())
161 #if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
163 inline raw_ostream
&operator<<(raw_ostream
&OS
, const VXRMInfo
&V
) {
170 // Indicates if the block uses VXRM. Uninitialized means no use.
173 // Indicates the VXRM output from the block. Unitialized means transparent.
176 // Keeps track of the available VXRM value at the start of the basic bloc.
177 VXRMInfo AvailableIn
;
179 // Keeps track of the available VXRM value at the end of the basic block.
180 VXRMInfo AvailableOut
;
182 // Keeps track of what VXRM is anticipated at the start of the basic block.
183 VXRMInfo AnticipatedIn
;
185 // Keeps track of what VXRM is anticipated at the end of the basic block.
186 VXRMInfo AnticipatedOut
;
188 // Keeps track of whether the block is already in the queue.
191 BlockData() = default;
194 class RISCVInsertWriteVXRM
: public MachineFunctionPass
{
195 const TargetInstrInfo
*TII
;
197 std::vector
<BlockData
> BlockInfo
;
198 std::queue
<const MachineBasicBlock
*> WorkList
;
203 RISCVInsertWriteVXRM() : MachineFunctionPass(ID
) {}
205 bool runOnMachineFunction(MachineFunction
&MF
) override
;
207 void getAnalysisUsage(AnalysisUsage
&AU
) const override
{
208 AU
.setPreservesCFG();
209 MachineFunctionPass::getAnalysisUsage(AU
);
212 StringRef
getPassName() const override
{
213 return RISCV_INSERT_WRITE_VXRM_NAME
;
217 bool computeVXRMChanges(const MachineBasicBlock
&MBB
);
218 void computeAvailable(const MachineBasicBlock
&MBB
);
219 void computeAnticipated(const MachineFunction
&MF
, const MachineBasicBlock
&MBB
);
220 void emitWriteVXRM(MachineBasicBlock
&MBB
);
223 } // end anonymous namespace
225 char RISCVInsertWriteVXRM::ID
= 0;
227 INITIALIZE_PASS(RISCVInsertWriteVXRM
, DEBUG_TYPE
, RISCV_INSERT_WRITE_VXRM_NAME
,
230 static bool ignoresVXRM(const MachineInstr
&MI
) {
231 switch (RISCV::getRVVMCOpcode(MI
.getOpcode())) {
234 case RISCV::VNCLIP_WI
:
235 case RISCV::VNCLIPU_WI
:
236 return MI
.getOperand(3).getImm() == 0;
240 bool RISCVInsertWriteVXRM::computeVXRMChanges(const MachineBasicBlock
&MBB
) {
241 BlockData
&BBInfo
= BlockInfo
[MBB
.getNumber()];
243 bool NeedVXRMWrite
= false;
244 for (const MachineInstr
&MI
: MBB
) {
245 int VXRMIdx
= RISCVII::getVXRMOpNum(MI
.getDesc());
246 if (VXRMIdx
>= 0 && !ignoresVXRM(MI
)) {
247 unsigned NewVXRMImm
= MI
.getOperand(VXRMIdx
).getImm();
249 if (!BBInfo
.VXRMUse
.isValid())
250 BBInfo
.VXRMUse
.setVXRMImm(NewVXRMImm
);
252 BBInfo
.VXRMOut
.setVXRMImm(NewVXRMImm
);
253 NeedVXRMWrite
= true;
257 if (MI
.isCall() || MI
.isInlineAsm() ||
258 MI
.modifiesRegister(RISCV::VXRM
, /*TRI=*/nullptr)) {
259 if (!BBInfo
.VXRMUse
.isValid())
260 BBInfo
.VXRMUse
.setUnknown();
262 BBInfo
.VXRMOut
.setUnknown();
266 return NeedVXRMWrite
;
269 void RISCVInsertWriteVXRM::computeAvailable(const MachineBasicBlock
&MBB
) {
270 BlockData
&BBInfo
= BlockInfo
[MBB
.getNumber()];
272 BBInfo
.InQueue
= false;
275 if (MBB
.pred_empty()) {
276 Available
.setUnknown();
278 for (const MachineBasicBlock
*P
: MBB
.predecessors())
279 Available
= Available
.intersect(BlockInfo
[P
->getNumber()].AvailableOut
);
282 // If we don't have any valid available info, wait until we do.
283 if (!Available
.isValid())
286 if (Available
!= BBInfo
.AvailableIn
) {
287 BBInfo
.AvailableIn
= Available
;
288 LLVM_DEBUG(dbgs() << "AvailableIn state of " << printMBBReference(MBB
)
289 << " changed to " << BBInfo
.AvailableIn
<< "\n");
292 if (BBInfo
.VXRMOut
.isValid())
293 Available
= BBInfo
.VXRMOut
;
295 if (Available
== BBInfo
.AvailableOut
)
298 BBInfo
.AvailableOut
= Available
;
299 LLVM_DEBUG(dbgs() << "AvailableOut state of " << printMBBReference(MBB
)
300 << " changed to " << BBInfo
.AvailableOut
<< "\n");
302 // Add the successors to the work list so that we can propagate.
303 for (MachineBasicBlock
*S
: MBB
.successors()) {
304 if (!BlockInfo
[S
->getNumber()].InQueue
) {
305 BlockInfo
[S
->getNumber()].InQueue
= true;
311 void RISCVInsertWriteVXRM::computeAnticipated(const MachineFunction
&MF
, const MachineBasicBlock
&MBB
) {
312 BlockData
&BBInfo
= BlockInfo
[MBB
.getNumber()];
313 const RISCVSubtarget
&ST
= MF
.getSubtarget
<RISCVSubtarget
>();
315 BBInfo
.InQueue
= false;
317 VXRMInfo Anticipated
;
318 if (MBB
.succ_empty()) {
319 Anticipated
.setUnknown();
321 for (const MachineBasicBlock
*S
: MBB
.successors())
322 if (ST
.hasVXRMPipelineFlush())
324 Anticipated
.intersectAnticipated(BlockInfo
[S
->getNumber()].AnticipatedIn
);
327 Anticipated
.intersect(BlockInfo
[S
->getNumber()].AnticipatedIn
);
330 // If we don't have any valid anticipated info, wait until we do.
331 if (!Anticipated
.isValid())
334 if (Anticipated
!= BBInfo
.AnticipatedOut
) {
335 BBInfo
.AnticipatedOut
= Anticipated
;
336 LLVM_DEBUG(dbgs() << "AnticipatedOut state of " << printMBBReference(MBB
)
337 << " changed to " << BBInfo
.AnticipatedOut
<< "\n");
340 // If this block reads VXRM, copy it.
341 if (BBInfo
.VXRMUse
.isValid())
342 Anticipated
= BBInfo
.VXRMUse
;
344 if (Anticipated
== BBInfo
.AnticipatedIn
)
347 BBInfo
.AnticipatedIn
= Anticipated
;
348 LLVM_DEBUG(dbgs() << "AnticipatedIn state of " << printMBBReference(MBB
)
349 << " changed to " << BBInfo
.AnticipatedIn
<< "\n");
351 // Add the predecessors to the work list so that we can propagate.
352 for (MachineBasicBlock
*P
: MBB
.predecessors()) {
353 if (!BlockInfo
[P
->getNumber()].InQueue
) {
354 BlockInfo
[P
->getNumber()].InQueue
= true;
360 void RISCVInsertWriteVXRM::emitWriteVXRM(MachineBasicBlock
&MBB
) {
361 const BlockData
&BBInfo
= BlockInfo
[MBB
.getNumber()];
363 VXRMInfo Info
= BBInfo
.AvailableIn
;
365 // Flag to indicates we need to insert a VXRM write. We want to delay it as
366 // late as possible in this block.
367 bool PendingInsert
= false;
369 // Insert VXRM write if anticipated and not available.
370 if (BBInfo
.AnticipatedIn
.isStatic()) {
371 // If this is the entry block and the value is anticipated, insert.
372 if (MBB
.isEntryBlock()) {
373 PendingInsert
= true;
375 // Search for any predecessors that wouldn't satisfy our requirement and
376 // insert a write VXRM if needed.
377 // NOTE: If one predecessor is able to provide the requirement, but
378 // another isn't, it means we have a critical edge. The better placement
379 // would be to split the critical edge.
380 for (MachineBasicBlock
*P
: MBB
.predecessors()) {
381 const BlockData
&PInfo
= BlockInfo
[P
->getNumber()];
382 // If it's available out of the predecessor, then we're ok.
383 if (PInfo
.AvailableOut
.isStatic() &&
384 PInfo
.AvailableOut
.getVXRMImm() ==
385 BBInfo
.AnticipatedIn
.getVXRMImm())
387 // If the predecessor anticipates this value for all its succesors,
388 // then a write to VXRM would have already occured before this block is
390 if (PInfo
.AnticipatedOut
.isStatic() &&
391 PInfo
.AnticipatedOut
.getVXRMImm() ==
392 BBInfo
.AnticipatedIn
.getVXRMImm())
394 PendingInsert
= true;
399 Info
= BBInfo
.AnticipatedIn
;
402 for (MachineInstr
&MI
: MBB
) {
403 int VXRMIdx
= RISCVII::getVXRMOpNum(MI
.getDesc());
404 if (VXRMIdx
>= 0 && !ignoresVXRM(MI
)) {
405 unsigned NewVXRMImm
= MI
.getOperand(VXRMIdx
).getImm();
407 if (PendingInsert
|| !Info
.isStatic() ||
408 Info
.getVXRMImm() != NewVXRMImm
) {
409 assert((!PendingInsert
||
410 (Info
.isStatic() && Info
.getVXRMImm() == NewVXRMImm
)) &&
411 "Pending VXRM insertion mismatch");
412 LLVM_DEBUG(dbgs() << "Inserting before "; MI
.print(dbgs()));
413 BuildMI(MBB
, MI
, MI
.getDebugLoc(), TII
->get(RISCV::WriteVXRMImm
))
415 PendingInsert
= false;
418 MI
.addOperand(MachineOperand::CreateReg(RISCV::VXRM
, /*IsDef*/ false,
420 Info
.setVXRMImm(NewVXRMImm
);
424 if (MI
.isCall() || MI
.isInlineAsm() ||
425 MI
.modifiesRegister(RISCV::VXRM
, /*TRI=*/nullptr))
429 // If all our successors anticipate a value, do the insert.
430 // NOTE: It's possible that not all predecessors of our successor provide the
431 // correct value. This can occur on critical edges. If we don't split the
432 // critical edge we'll also have a write vxrm in the succesor that is
433 // redundant with this one.
435 (BBInfo
.AnticipatedOut
.isStatic() &&
437 Info
.getVXRMImm() != BBInfo
.AnticipatedOut
.getVXRMImm()))) {
438 assert((!PendingInsert
||
439 (Info
.isStatic() && BBInfo
.AnticipatedOut
.isStatic() &&
440 Info
.getVXRMImm() == BBInfo
.AnticipatedOut
.getVXRMImm())) &&
441 "Pending VXRM insertion mismatch");
442 LLVM_DEBUG(dbgs() << "Inserting at end of " << printMBBReference(MBB
)
443 << " changing to " << BBInfo
.AnticipatedOut
<< "\n");
444 BuildMI(MBB
, MBB
.getFirstTerminator(), DebugLoc(),
445 TII
->get(RISCV::WriteVXRMImm
))
446 .addImm(BBInfo
.AnticipatedOut
.getVXRMImm());
450 bool RISCVInsertWriteVXRM::runOnMachineFunction(MachineFunction
&MF
) {
451 // Skip if the vector extension is not enabled.
452 const RISCVSubtarget
&ST
= MF
.getSubtarget
<RISCVSubtarget
>();
453 if (!ST
.hasVInstructions())
456 TII
= ST
.getInstrInfo();
458 assert(BlockInfo
.empty() && "Expect empty block infos");
459 BlockInfo
.resize(MF
.getNumBlockIDs());
461 // Phase 1 - collect block information.
462 bool NeedVXRMChange
= false;
463 for (const MachineBasicBlock
&MBB
: MF
)
464 NeedVXRMChange
|= computeVXRMChanges(MBB
);
466 if (!NeedVXRMChange
) {
471 // Phase 2 - Compute available VXRM using a forward walk.
472 for (const MachineBasicBlock
&MBB
: MF
) {
474 BlockInfo
[MBB
.getNumber()].InQueue
= true;
476 while (!WorkList
.empty()) {
477 const MachineBasicBlock
&MBB
= *WorkList
.front();
479 computeAvailable(MBB
);
482 // Phase 3 - Compute anticipated VXRM using a backwards walk.
483 for (const MachineBasicBlock
&MBB
: llvm::reverse(MF
)) {
485 BlockInfo
[MBB
.getNumber()].InQueue
= true;
487 while (!WorkList
.empty()) {
488 const MachineBasicBlock
&MBB
= *WorkList
.front();
490 computeAnticipated(MF
, MBB
);
493 // Phase 4 - Emit VXRM writes at the earliest place possible.
494 for (MachineBasicBlock
&MBB
: MF
)
502 FunctionPass
*llvm::createRISCVInsertWriteVXRMPass() {
503 return new RISCVInsertWriteVXRM();