Coverage Report

Created: 2024-01-17 10:31

/src/llvm-project/llvm/lib/CodeGen/GlobalISel/CSEMIRBuilder.cpp
Line
Count
Source (jump to first uncovered line)
1
//===-- llvm/CodeGen/GlobalISel/CSEMIRBuilder.cpp - MIBuilder--*- C++ -*-==//
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
/// \file
9
/// This file implements the CSEMIRBuilder class which CSEs as it builds
10
/// instructions.
11
//===----------------------------------------------------------------------===//
12
//
13
14
#include "llvm/CodeGen/GlobalISel/CSEMIRBuilder.h"
15
#include "llvm/CodeGen/GlobalISel/CSEInfo.h"
16
#include "llvm/CodeGen/GlobalISel/GISelChangeObserver.h"
17
#include "llvm/CodeGen/GlobalISel/Utils.h"
18
#include "llvm/CodeGen/MachineInstrBuilder.h"
19
#include "llvm/IR/DebugInfoMetadata.h"
20
21
using namespace llvm;
22
23
bool CSEMIRBuilder::dominates(MachineBasicBlock::const_iterator A,
24
73.9k
                              MachineBasicBlock::const_iterator B) const {
25
73.9k
  auto MBBEnd = getMBB().end();
26
73.9k
  if (B == MBBEnd)
27
14.5k
    return true;
28
59.3k
  assert(A->getParent() == B->getParent() &&
29
59.3k
         "Iterators should be in same block");
30
0
  const MachineBasicBlock *BBA = A->getParent();
31
59.3k
  MachineBasicBlock::const_iterator I = BBA->begin();
32
331k
  for (; &*I != A && &*I != B; ++I)
33
272k
    ;
34
59.3k
  return &*I == A;
35
73.9k
}
36
37
MachineInstrBuilder
38
CSEMIRBuilder::getDominatingInstrForID(FoldingSetNodeID &ID,
39
144k
                                       void *&NodeInsertPos) {
40
144k
  GISelCSEInfo *CSEInfo = getCSEInfo();
41
144k
  assert(CSEInfo && "Can't get here without setting CSEInfo");
42
0
  MachineBasicBlock *CurMBB = &getMBB();
43
144k
  MachineInstr *MI =
44
144k
      CSEInfo->getMachineInstrIfExists(ID, CurMBB, NodeInsertPos);
45
144k
  if (MI) {
46
73.9k
    CSEInfo->countOpcodeHit(MI->getOpcode());
47
73.9k
    auto CurrPos = getInsertPt();
48
73.9k
    auto MII = MachineBasicBlock::iterator(MI);
49
73.9k
    if (MII == CurrPos) {
50
      // Move the insert point ahead of the instruction so any future uses of
51
      // this builder will have the def ready.
52
0
      setInsertPt(*CurMBB, std::next(MII));
53
73.9k
    } else if (!dominates(MI, CurrPos)) {
54
7.67k
      CurMBB->splice(CurrPos, CurMBB, MI);
55
7.67k
    }
56
73.9k
    return MachineInstrBuilder(getMF(), MI);
57
73.9k
  }
58
70.3k
  return MachineInstrBuilder();
59
144k
}
60
61
490k
bool CSEMIRBuilder::canPerformCSEForOpc(unsigned Opc) const {
62
490k
  const GISelCSEInfo *CSEInfo = getCSEInfo();
63
490k
  if (!CSEInfo || !CSEInfo->shouldCSE(Opc))
64
275k
    return false;
65
214k
  return true;
66
490k
}
67
68
void CSEMIRBuilder::profileDstOp(const DstOp &Op,
69
144k
                                 GISelInstProfileBuilder &B) const {
70
144k
  switch (Op.getDstOpKind()) {
71
0
  case DstOp::DstType::Ty_RC:
72
0
    B.addNodeIDRegType(Op.getRegClass());
73
0
    break;
74
62.4k
  case DstOp::DstType::Ty_Reg: {
75
    // Regs can have LLT&(RB|RC). If those exist, profile them as well.
76
62.4k
    B.addNodeIDReg(Op.getReg());
77
62.4k
    break;
78
0
  }
79
81.8k
  default:
80
81.8k
    B.addNodeIDRegType(Op.getLLTTy(*getMRI()));
81
81.8k
    break;
82
144k
  }
83
144k
}
84
85
void CSEMIRBuilder::profileSrcOp(const SrcOp &Op,
86
0
                                 GISelInstProfileBuilder &B) const {
87
0
  switch (Op.getSrcOpKind()) {
88
0
  case SrcOp::SrcType::Ty_Imm:
89
0
    B.addNodeIDImmediate(static_cast<int64_t>(Op.getImm()));
90
0
    break;
91
0
  case SrcOp::SrcType::Ty_Predicate:
92
0
    B.addNodeIDImmediate(static_cast<int64_t>(Op.getPredicate()));
93
0
    break;
94
0
  default:
95
0
    B.addNodeIDRegType(Op.getReg());
96
0
    break;
97
0
  }
98
0
}
99
100
void CSEMIRBuilder::profileMBBOpcode(GISelInstProfileBuilder &B,
101
144k
                                     unsigned Opc) const {
102
  // First add the MBB (Local CSE).
103
144k
  B.addNodeIDMBB(&getMBB());
104
  // Then add the opcode.
105
144k
  B.addNodeIDOpcode(Opc);
106
144k
}
107
108
void CSEMIRBuilder::profileEverything(unsigned Opc, ArrayRef<DstOp> DstOps,
109
                                      ArrayRef<SrcOp> SrcOps,
110
                                      std::optional<unsigned> Flags,
111
55.0k
                                      GISelInstProfileBuilder &B) const {
112
113
55.0k
  profileMBBOpcode(B, Opc);
114
  // Then add the DstOps.
115
55.0k
  profileDstOps(DstOps, B);
116
  // Then add the SrcOps.
117
55.0k
  profileSrcOps(SrcOps, B);
118
  // Add Flags if passed in.
119
55.0k
  if (Flags)
120
46.1k
    B.addNodeIDFlag(*Flags);
121
55.0k
}
122
123
MachineInstrBuilder CSEMIRBuilder::memoizeMI(MachineInstrBuilder MIB,
124
70.3k
                                             void *NodeInsertPos) {
125
70.3k
  assert(canPerformCSEForOpc(MIB->getOpcode()) &&
126
70.3k
         "Attempting to CSE illegal op");
127
0
  MachineInstr *MIBInstr = MIB;
128
70.3k
  getCSEInfo()->insertInstr(MIBInstr, NodeInsertPos);
129
70.3k
  return MIB;
130
70.3k
}
131
132
404k
bool CSEMIRBuilder::checkCopyToDefsPossible(ArrayRef<DstOp> DstOps) {
133
404k
  if (DstOps.size() == 1)
134
398k
    return true; // always possible to emit copy to just 1 vreg.
135
136
9.08k
  return llvm::all_of(DstOps, [](const DstOp &Op) {
137
9.08k
    DstOp::DstType DT = Op.getDstOpKind();
138
9.08k
    return DT == DstOp::DstType::Ty_LLT || DT == DstOp::DstType::Ty_RC;
139
9.08k
  });
140
404k
}
141
142
MachineInstrBuilder
143
CSEMIRBuilder::generateCopiesIfRequired(ArrayRef<DstOp> DstOps,
144
73.9k
                                        MachineInstrBuilder &MIB) {
145
73.9k
  assert(checkCopyToDefsPossible(DstOps) &&
146
73.9k
         "Impossible return a single MIB with copies to multiple defs");
147
73.9k
  if (DstOps.size() == 1) {
148
73.9k
    const DstOp &Op = DstOps[0];
149
73.9k
    if (Op.getDstOpKind() == DstOp::DstType::Ty_Reg)
150
8.95k
      return buildCopy(Op.getReg(), MIB.getReg(0));
151
73.9k
  }
152
153
  // If we didn't generate a copy then we're re-using an existing node directly
154
  // instead of emitting any code. Merge the debug location we wanted to emit
155
  // into the instruction we're CSE'ing with. Debug locations arent part of the
156
  // profile so we don't need to recompute it.
157
64.9k
  if (getDebugLoc()) {
158
1
    GISelChangeObserver *Observer = getState().Observer;
159
1
    if (Observer)
160
0
      Observer->changingInstr(*MIB);
161
1
    MIB->setDebugLoc(
162
1
        DILocation::getMergedLocation(MIB->getDebugLoc(), getDebugLoc()));
163
1
    if (Observer)
164
0
      Observer->changedInstr(*MIB);
165
1
  }
166
167
64.9k
  return MIB;
168
73.9k
}
169
170
MachineInstrBuilder CSEMIRBuilder::buildInstr(unsigned Opc,
171
                                              ArrayRef<DstOp> DstOps,
172
                                              ArrayRef<SrcOp> SrcOps,
173
333k
                                              std::optional<unsigned> Flag) {
174
333k
  switch (Opc) {
175
267k
  default:
176
267k
    break;
177
267k
  case TargetOpcode::G_ADD:
178
23.1k
  case TargetOpcode::G_PTR_ADD:
179
38.5k
  case TargetOpcode::G_AND:
180
39.4k
  case TargetOpcode::G_ASHR:
181
40.5k
  case TargetOpcode::G_LSHR:
182
47.5k
  case TargetOpcode::G_MUL:
183
48.9k
  case TargetOpcode::G_OR:
184
49.8k
  case TargetOpcode::G_SHL:
185
52.7k
  case TargetOpcode::G_SUB:
186
53.9k
  case TargetOpcode::G_XOR:
187
55.1k
  case TargetOpcode::G_UDIV:
188
56.5k
  case TargetOpcode::G_SDIV:
189
56.9k
  case TargetOpcode::G_UREM:
190
57.4k
  case TargetOpcode::G_SREM:
191
57.4k
  case TargetOpcode::G_SMIN:
192
57.4k
  case TargetOpcode::G_SMAX:
193
57.4k
  case TargetOpcode::G_UMIN:
194
57.4k
  case TargetOpcode::G_UMAX: {
195
    // Try to constant fold these.
196
57.4k
    assert(SrcOps.size() == 2 && "Invalid sources");
197
0
    assert(DstOps.size() == 1 && "Invalid dsts");
198
0
    LLT SrcTy = SrcOps[0].getLLTTy(*getMRI());
199
200
57.4k
    if (Opc == TargetOpcode::G_PTR_ADD &&
201
57.4k
        getDataLayout().isNonIntegralAddressSpace(SrcTy.getAddressSpace()))
202
0
      break;
203
204
57.4k
    if (SrcTy.isVector()) {
205
      // Try to constant fold vector constants.
206
3.29k
      SmallVector<APInt> VecCst = ConstantFoldVectorBinop(
207
3.29k
          Opc, SrcOps[0].getReg(), SrcOps[1].getReg(), *getMRI());
208
3.29k
      if (!VecCst.empty())
209
4
        return buildBuildVectorConstant(DstOps[0], VecCst);
210
3.29k
      break;
211
3.29k
    }
212
213
54.1k
    if (std::optional<APInt> Cst = ConstantFoldBinOp(
214
54.1k
            Opc, SrcOps[0].getReg(), SrcOps[1].getReg(), *getMRI()))
215
2.58k
      return buildConstant(DstOps[0], *Cst);
216
51.5k
    break;
217
54.1k
  }
218
51.5k
  case TargetOpcode::G_FADD:
219
1.22k
  case TargetOpcode::G_FSUB:
220
1.80k
  case TargetOpcode::G_FMUL:
221
2.33k
  case TargetOpcode::G_FDIV:
222
2.66k
  case TargetOpcode::G_FREM:
223
2.66k
  case TargetOpcode::G_FMINNUM:
224
2.72k
  case TargetOpcode::G_FMAXNUM:
225
2.72k
  case TargetOpcode::G_FMINNUM_IEEE:
226
2.72k
  case TargetOpcode::G_FMAXNUM_IEEE:
227
2.73k
  case TargetOpcode::G_FMINIMUM:
228
2.73k
  case TargetOpcode::G_FMAXIMUM:
229
2.74k
  case TargetOpcode::G_FCOPYSIGN: {
230
    // Try to constant fold these.
231
2.74k
    assert(SrcOps.size() == 2 && "Invalid sources");
232
0
    assert(DstOps.size() == 1 && "Invalid dsts");
233
2.74k
    if (std::optional<APFloat> Cst = ConstantFoldFPBinOp(
234
2.74k
            Opc, SrcOps[0].getReg(), SrcOps[1].getReg(), *getMRI()))
235
677
      return buildFConstant(DstOps[0], *Cst);
236
2.06k
    break;
237
2.74k
  }
238
5.33k
  case TargetOpcode::G_SEXT_INREG: {
239
5.33k
    assert(DstOps.size() == 1 && "Invalid dst ops");
240
0
    assert(SrcOps.size() == 2 && "Invalid src ops");
241
0
    const DstOp &Dst = DstOps[0];
242
5.33k
    const SrcOp &Src0 = SrcOps[0];
243
5.33k
    const SrcOp &Src1 = SrcOps[1];
244
5.33k
    if (auto MaybeCst =
245
5.33k
            ConstantFoldExtOp(Opc, Src0.getReg(), Src1.getImm(), *getMRI()))
246
3
      return buildConstant(Dst, *MaybeCst);
247
5.32k
    break;
248
5.33k
  }
249
5.32k
  case TargetOpcode::G_SITOFP:
250
585
  case TargetOpcode::G_UITOFP: {
251
    // Try to constant fold these.
252
585
    assert(SrcOps.size() == 1 && "Invalid sources");
253
0
    assert(DstOps.size() == 1 && "Invalid dsts");
254
585
    if (std::optional<APFloat> Cst = ConstantFoldIntToFloat(
255
585
            Opc, DstOps[0].getLLTTy(*getMRI()), SrcOps[0].getReg(), *getMRI()))
256
30
      return buildFConstant(DstOps[0], *Cst);
257
555
    break;
258
585
  }
259
555
  case TargetOpcode::G_CTLZ: {
260
35
    assert(SrcOps.size() == 1 && "Expected one source");
261
0
    assert(DstOps.size() == 1 && "Expected one dest");
262
0
    auto MaybeCsts = ConstantFoldCTLZ(SrcOps[0].getReg(), *getMRI());
263
35
    if (!MaybeCsts)
264
33
      break;
265
2
    if (MaybeCsts->size() == 1)
266
2
      return buildConstant(DstOps[0], (*MaybeCsts)[0]);
267
    // This was a vector constant. Build a G_BUILD_VECTOR for them.
268
0
    SmallVector<Register> ConstantRegs;
269
0
    LLT VecTy = DstOps[0].getLLTTy(*getMRI());
270
0
    for (unsigned Cst : *MaybeCsts)
271
0
      ConstantRegs.emplace_back(
272
0
          buildConstant(VecTy.getScalarType(), Cst).getReg(0));
273
0
    return buildBuildVector(DstOps[0], ConstantRegs);
274
2
  }
275
333k
  }
276
330k
  bool CanCopy = checkCopyToDefsPossible(DstOps);
277
330k
  if (!canPerformCSEForOpc(Opc))
278
275k
    return MachineIRBuilder::buildInstr(Opc, DstOps, SrcOps, Flag);
279
  // If we can CSE this instruction, but involves generating copies to multiple
280
  // regs, give up. This frequently happens to UNMERGEs.
281
55.0k
  if (!CanCopy) {
282
0
    auto MIB = MachineIRBuilder::buildInstr(Opc, DstOps, SrcOps, Flag);
283
    // CSEInfo would have tracked this instruction. Remove it from the temporary
284
    // insts.
285
0
    getCSEInfo()->handleRemoveInst(&*MIB);
286
0
    return MIB;
287
0
  }
288
55.0k
  FoldingSetNodeID ID;
289
55.0k
  GISelInstProfileBuilder ProfBuilder(ID, *getMRI());
290
55.0k
  void *InsertPos = nullptr;
291
55.0k
  profileEverything(Opc, DstOps, SrcOps, Flag, ProfBuilder);
292
55.0k
  MachineInstrBuilder MIB = getDominatingInstrForID(ID, InsertPos);
293
55.0k
  if (MIB) {
294
    // Handle generating copies here.
295
45.4k
    return generateCopiesIfRequired(DstOps, MIB);
296
45.4k
  }
297
  // This instruction does not exist in the CSEInfo. Build it and CSE it.
298
9.59k
  MachineInstrBuilder NewMIB =
299
9.59k
      MachineIRBuilder::buildInstr(Opc, DstOps, SrcOps, Flag);
300
9.59k
  return memoizeMI(NewMIB, InsertPos);
301
55.0k
}
302
303
MachineInstrBuilder CSEMIRBuilder::buildConstant(const DstOp &Res,
304
83.6k
                                                 const ConstantInt &Val) {
305
83.6k
  constexpr unsigned Opc = TargetOpcode::G_CONSTANT;
306
83.6k
  if (!canPerformCSEForOpc(Opc))
307
0
    return MachineIRBuilder::buildConstant(Res, Val);
308
309
  // For vectors, CSE the element only for now.
310
83.6k
  LLT Ty = Res.getLLTTy(*getMRI());
311
83.6k
  if (Ty.isVector())
312
232
    return buildSplatVector(Res, buildConstant(Ty.getElementType(), Val));
313
314
83.3k
  FoldingSetNodeID ID;
315
83.3k
  GISelInstProfileBuilder ProfBuilder(ID, *getMRI());
316
83.3k
  void *InsertPos = nullptr;
317
83.3k
  profileMBBOpcode(ProfBuilder, Opc);
318
83.3k
  profileDstOp(Res, ProfBuilder);
319
83.3k
  ProfBuilder.addNodeIDMachineOperand(MachineOperand::CreateCImm(&Val));
320
83.3k
  MachineInstrBuilder MIB = getDominatingInstrForID(ID, InsertPos);
321
83.3k
  if (MIB) {
322
    // Handle generating copies here.
323
28.4k
    return generateCopiesIfRequired({Res}, MIB);
324
28.4k
  }
325
326
54.9k
  MachineInstrBuilder NewMIB = MachineIRBuilder::buildConstant(Res, Val);
327
54.9k
  return memoizeMI(NewMIB, InsertPos);
328
83.3k
}
329
330
MachineInstrBuilder CSEMIRBuilder::buildFConstant(const DstOp &Res,
331
5.90k
                                                  const ConstantFP &Val) {
332
5.90k
  constexpr unsigned Opc = TargetOpcode::G_FCONSTANT;
333
5.90k
  if (!canPerformCSEForOpc(Opc))
334
0
    return MachineIRBuilder::buildFConstant(Res, Val);
335
336
  // For vectors, CSE the element only for now.
337
5.90k
  LLT Ty = Res.getLLTTy(*getMRI());
338
5.90k
  if (Ty.isVector())
339
0
    return buildSplatVector(Res, buildFConstant(Ty.getElementType(), Val));
340
341
5.90k
  FoldingSetNodeID ID;
342
5.90k
  GISelInstProfileBuilder ProfBuilder(ID, *getMRI());
343
5.90k
  void *InsertPos = nullptr;
344
5.90k
  profileMBBOpcode(ProfBuilder, Opc);
345
5.90k
  profileDstOp(Res, ProfBuilder);
346
5.90k
  ProfBuilder.addNodeIDMachineOperand(MachineOperand::CreateFPImm(&Val));
347
5.90k
  MachineInstrBuilder MIB = getDominatingInstrForID(ID, InsertPos);
348
5.90k
  if (MIB) {
349
    // Handle generating copies here.
350
87
    return generateCopiesIfRequired({Res}, MIB);
351
87
  }
352
5.81k
  MachineInstrBuilder NewMIB = MachineIRBuilder::buildFConstant(Res, Val);
353
5.81k
  return memoizeMI(NewMIB, InsertPos);
354
5.90k
}