aboutsummaryrefslogtreecommitdiff
path: root/llvm/lib/Target/RISCV/RISCVVMV0Elimination.cpp
blob: d72d27c983ada7c0310915be4d71820e660bff92 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
//===- RISCVVMV0Elimination.cpp - VMV0 Elimination -----------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===---------------------------------------------------------------------===//
//
// Mask operands in vector pseudos have to be in v0. We select them as a virtual
// register in the singleton vmv0 register class instead of copying them to $v0
// straight away, to make optimizing masks easier.
//
// However register coalescing may end up coleascing copies into vmv0, resulting
// in instructions with multiple uses of vmv0 that the register allocator can't
// allocate:
//
// %x:vrnov0 = PseudoVADD_VV_M1_MASK %0:vrnov0, %1:vr, %2:vmv0, %3:vmv0, ...
//
// To avoid this, this pass replaces any uses* of vmv0 with copies to $v0 before
// register coalescing and allocation:
//
// %x:vrnov0 = PseudoVADD_VV_M1_MASK %0:vrnov0, %1:vr, %2:vr, %3:vmv0, ...
// ->
// $v0 = COPY %3:vr
// %x:vrnov0 = PseudoVADD_VV_M1_MASK %0:vrnov0, %1:vr, %2:vr, $0, ...
//
// * The only uses of vmv0 left behind are when used for inline asm with the vm
// constraint.
//
//===---------------------------------------------------------------------===//

#include "RISCV.h"
#include "RISCVSubtarget.h"
#ifndef NDEBUG
#include "llvm/ADT/PostOrderIterator.h"
#endif
#include "llvm/CodeGen/MachineFunctionPass.h"

using namespace llvm;

#define DEBUG_TYPE "riscv-vmv0-elimination"

namespace {

class RISCVVMV0Elimination : public MachineFunctionPass {
public:
  static char ID;
  RISCVVMV0Elimination() : MachineFunctionPass(ID) {}

  bool runOnMachineFunction(MachineFunction &MF) override;

  void getAnalysisUsage(AnalysisUsage &AU) const override {
    AU.setPreservesCFG();
    MachineFunctionPass::getAnalysisUsage(AU);
  }

  MachineFunctionProperties getRequiredProperties() const override {
    // TODO: We could move this closer to regalloc, out of SSA, which would
    // allow scheduling past mask operands. We would need to preserve live
    // intervals.
    return MachineFunctionProperties().setIsSSA();
  }
};

} // namespace

char RISCVVMV0Elimination::ID = 0;

INITIALIZE_PASS(RISCVVMV0Elimination, DEBUG_TYPE, "RISC-V VMV0 Elimination",
                false, false)

FunctionPass *llvm::createRISCVVMV0EliminationPass() {
  return new RISCVVMV0Elimination();
}

static bool isVMV0(const MCOperandInfo &MCOI) {
  return MCOI.RegClass == RISCV::VMV0RegClassID;
}

bool RISCVVMV0Elimination::runOnMachineFunction(MachineFunction &MF) {
  // Skip if the vector extension is not enabled.
  const RISCVSubtarget *ST = &MF.getSubtarget<RISCVSubtarget>();
  if (!ST->hasVInstructions())
    return false;

  MachineRegisterInfo &MRI = MF.getRegInfo();
  const TargetInstrInfo *TII = ST->getInstrInfo();

#ifndef NDEBUG
  // Assert that we won't clobber any existing reads of v0 where we need to
  // insert copies.
  const TargetRegisterInfo *TRI = MRI.getTargetRegisterInfo();
  ReversePostOrderTraversal<MachineBasicBlock *> RPOT(&*MF.begin());
  for (MachineBasicBlock *MBB : RPOT) {
    bool V0Clobbered = false;
    for (MachineInstr &MI : *MBB) {
      assert(!(MI.readsRegister(RISCV::V0, TRI) && V0Clobbered) &&
             "Inserting a copy to v0 would clobber a read");
      if (MI.modifiesRegister(RISCV::V0, TRI))
        V0Clobbered = false;

      if (any_of(MI.getDesc().operands(), isVMV0))
        V0Clobbered = true;
    }

    assert(!(V0Clobbered &&
             any_of(MBB->successors(),
                    [](auto *Succ) { return Succ->isLiveIn(RISCV::V0); })) &&
           "Clobbered a v0 used in a successor");
  }
#endif

  bool MadeChange = false;
  SmallVector<MachineInstr *> DeadCopies;

  // For any instruction with a vmv0 operand, replace it with a copy to v0.
  for (MachineBasicBlock &MBB : MF) {
    for (MachineInstr &MI : MBB) {
      assert(count_if(MI.getDesc().operands(), isVMV0) < 2 &&
             "Expected only one or zero vmv0 operands");

      for (auto [OpNo, MCOI] : enumerate(MI.getDesc().operands())) {
        if (isVMV0(MCOI)) {
          MachineOperand &MO = MI.getOperand(OpNo);
          Register Src = MO.getReg();
          assert(MO.isUse() && MO.getSubReg() == RISCV::NoSubRegister &&
                 Src.isVirtual() && "vmv0 use in unexpected form");

          // Peek through a single copy to match what isel does.
          if (MachineInstr *SrcMI = MRI.getVRegDef(Src);
              SrcMI->isCopy() && SrcMI->getOperand(1).getReg().isVirtual() &&
              SrcMI->getOperand(1).getSubReg() == RISCV::NoSubRegister) {
            // Delete any dead copys to vmv0 to avoid allocating them.
            if (MRI.hasOneNonDBGUse(Src))
              DeadCopies.push_back(SrcMI);
            Src = SrcMI->getOperand(1).getReg();
          }

          BuildMI(MBB, MI, MI.getDebugLoc(), TII->get(RISCV::COPY), RISCV::V0)
              .addReg(Src);

          MO.setReg(RISCV::V0);
          MadeChange = true;
          break;
        }
      }
    }
  }

  for (MachineInstr *MI : DeadCopies)
    MI->eraseFromParent();

  if (!MadeChange)
    return false;

  // Now that any constraints requiring vmv0 are gone, eliminate any uses of
  // vmv0 by recomputing the reg class.
  // The only remaining uses should be around inline asm.
  for (MachineBasicBlock &MBB : MF) {
    for (MachineInstr &MI : MBB) {
      for (MachineOperand &MO : MI.uses()) {
        if (MO.isReg() && MO.getReg().isVirtual() &&
            MRI.getRegClass(MO.getReg()) == &RISCV::VMV0RegClass) {
          MRI.recomputeRegClass(MO.getReg());
          assert((MRI.getRegClass(MO.getReg()) != &RISCV::VMV0RegClass ||
                  MI.isInlineAsm() ||
                  MRI.getVRegDef(MO.getReg())->isInlineAsm()) &&
                 "Non-inline-asm use of vmv0 left behind");
        }
      }
    }
  }

  return true;
}