1//===- llvm/CodeGen/GlobalISel/GenericMachineInstrs.h -----------*- 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/// Declares convenience wrapper classes for interpreting MachineInstr instances
10/// as specific generic operations.
11///
12//===----------------------------------------------------------------------===//
13
14#ifndef LLVM_CODEGEN_GLOBALISEL_GENERICMACHINEINSTRS_H
15#define LLVM_CODEGEN_GLOBALISEL_GENERICMACHINEINSTRS_H
16
17#include "llvm/IR/Instructions.h"
18#include "llvm/CodeGen/MachineInstr.h"
19#include "llvm/CodeGen/MachineMemOperand.h"
20#include "llvm/CodeGen/TargetOpcodes.h"
21#include "llvm/Support/Casting.h"
22
23namespace llvm {
24
25/// A base class for all GenericMachineInstrs.
26class GenericMachineInstr : public MachineInstr {
27public:
28  GenericMachineInstr() = delete;
29
30  /// Access the Idx'th operand as a register and return it.
31  /// This assumes that the Idx'th operand is a Register type.
32  Register getReg(unsigned Idx) const { return getOperand(Idx).getReg(); }
33
34  static bool classof(const MachineInstr *MI) {
35    return isPreISelGenericOpcode(MI->getOpcode());
36  }
37};
38
39/// Provides common memory operand functionality.
40class GMemOperation : public GenericMachineInstr {
41public:
42  /// Get the MachineMemOperand on this instruction.
43  MachineMemOperand &getMMO() const { return **memoperands_begin(); }
44
45  /// Returns true if the attached MachineMemOperand  has the atomic flag set.
46  bool isAtomic() const { return getMMO().isAtomic(); }
47  /// Returns true if the attached MachineMemOpeand as the volatile flag set.
48  bool isVolatile() const { return getMMO().isVolatile(); }
49  /// Returns true if the memory operation is neither atomic or volatile.
50  bool isSimple() const { return !isAtomic() && !isVolatile(); }
51  /// Returns true if this memory operation doesn't have any ordering
52  /// constraints other than normal aliasing. Volatile and (ordered) atomic
53  /// memory operations can't be reordered.
54  bool isUnordered() const { return getMMO().isUnordered(); }
55
56  /// Returns the size in bytes of the memory access.
57  uint64_t getMemSize() const { return getMMO().getSize(); }
58  /// Returns the size in bits of the memory access.
59  uint64_t getMemSizeInBits() const { return getMMO().getSizeInBits(); }
60
61  static bool classof(const MachineInstr *MI) {
62    return GenericMachineInstr::classof(MI) && MI->hasOneMemOperand();
63  }
64};
65
66/// Represents any type of generic load or store.
67/// G_LOAD, G_STORE, G_ZEXTLOAD, G_SEXTLOAD.
68class GLoadStore : public GMemOperation {
69public:
70  /// Get the source register of the pointer value.
71  Register getPointerReg() const { return getOperand(1).getReg(); }
72
73  static bool classof(const MachineInstr *MI) {
74    switch (MI->getOpcode()) {
75    case TargetOpcode::G_LOAD:
76    case TargetOpcode::G_STORE:
77    case TargetOpcode::G_ZEXTLOAD:
78    case TargetOpcode::G_SEXTLOAD:
79      return true;
80    default:
81      return false;
82    }
83  }
84};
85
86/// Represents indexed loads. These are different enough from regular loads
87/// that they get their own class. Including them in GAnyLoad would probably
88/// make a footgun for someone.
89class GIndexedLoad : public GMemOperation {
90public:
91  /// Get the definition register of the loaded value.
92  Register getDstReg() const { return getOperand(0).getReg(); }
93  /// Get the def register of the writeback value.
94  Register getWritebackReg() const { return getOperand(1).getReg(); }
95  /// Get the base register of the pointer value.
96  Register getBaseReg() const { return getOperand(2).getReg(); }
97  /// Get the offset register of the pointer value.
98  Register getOffsetReg() const { return getOperand(3).getReg(); }
99
100  bool isPre() const { return getOperand(4).getImm() == 1; }
101  bool isPost() const { return !isPre(); }
102
103  static bool classof(const MachineInstr *MI) {
104    return MI->getOpcode() == TargetOpcode::G_INDEXED_LOAD;
105  }
106};
107
108/// Represents a G_INDEX_ZEXTLOAD/G_INDEXED_SEXTLOAD.
109class GIndexedExtLoad : public GIndexedLoad {
110public:
111  static bool classof(const MachineInstr *MI) {
112    return MI->getOpcode() == TargetOpcode::G_INDEXED_SEXTLOAD ||
113           MI->getOpcode() == TargetOpcode::G_INDEXED_ZEXTLOAD;
114  }
115};
116
117/// Represents either G_INDEXED_LOAD, G_INDEXED_ZEXTLOAD or G_INDEXED_SEXTLOAD.
118class GIndexedAnyExtLoad : public GIndexedLoad {
119public:
120  static bool classof(const MachineInstr *MI) {
121    switch (MI->getOpcode()) {
122    case TargetOpcode::G_INDEXED_LOAD:
123    case TargetOpcode::G_INDEXED_ZEXTLOAD:
124    case TargetOpcode::G_INDEXED_SEXTLOAD:
125      return true;
126    default:
127      return false;
128    }
129  }
130};
131
132/// Represents a G_ZEXTLOAD.
133class GIndexedZExtLoad : GIndexedExtLoad {
134public:
135  static bool classof(const MachineInstr *MI) {
136    return MI->getOpcode() == TargetOpcode::G_INDEXED_ZEXTLOAD;
137  }
138};
139
140/// Represents a G_SEXTLOAD.
141class GIndexedSExtLoad : GIndexedExtLoad {
142public:
143  static bool classof(const MachineInstr *MI) {
144    return MI->getOpcode() == TargetOpcode::G_INDEXED_SEXTLOAD;
145  }
146};
147
148/// Represents indexed stores.
149class GIndexedStore : public GMemOperation {
150public:
151  /// Get the def register of the writeback value.
152  Register getWritebackReg() const { return getOperand(0).getReg(); }
153  /// Get the stored value register.
154  Register getValueReg() const { return getOperand(1).getReg(); }
155  /// Get the base register of the pointer value.
156  Register getBaseReg() const { return getOperand(2).getReg(); }
157  /// Get the offset register of the pointer value.
158  Register getOffsetReg() const { return getOperand(3).getReg(); }
159
160  bool isPre() const { return getOperand(4).getImm() == 1; }
161  bool isPost() const { return !isPre(); }
162
163  static bool classof(const MachineInstr *MI) {
164    return MI->getOpcode() == TargetOpcode::G_INDEXED_STORE;
165  }
166};
167
168/// Represents any generic load, including sign/zero extending variants.
169class GAnyLoad : public GLoadStore {
170public:
171  /// Get the definition register of the loaded value.
172  Register getDstReg() const { return getOperand(0).getReg(); }
173
174  static bool classof(const MachineInstr *MI) {
175    switch (MI->getOpcode()) {
176    case TargetOpcode::G_LOAD:
177    case TargetOpcode::G_ZEXTLOAD:
178    case TargetOpcode::G_SEXTLOAD:
179      return true;
180    default:
181      return false;
182    }
183  }
184};
185
186/// Represents a G_LOAD.
187class GLoad : public GAnyLoad {
188public:
189  static bool classof(const MachineInstr *MI) {
190    return MI->getOpcode() == TargetOpcode::G_LOAD;
191  }
192};
193
194/// Represents either a G_SEXTLOAD or G_ZEXTLOAD.
195class GExtLoad : public GAnyLoad {
196public:
197  static bool classof(const MachineInstr *MI) {
198    return MI->getOpcode() == TargetOpcode::G_SEXTLOAD ||
199           MI->getOpcode() == TargetOpcode::G_ZEXTLOAD;
200  }
201};
202
203/// Represents a G_SEXTLOAD.
204class GSExtLoad : public GExtLoad {
205public:
206  static bool classof(const MachineInstr *MI) {
207    return MI->getOpcode() == TargetOpcode::G_SEXTLOAD;
208  }
209};
210
211/// Represents a G_ZEXTLOAD.
212class GZExtLoad : public GExtLoad {
213public:
214  static bool classof(const MachineInstr *MI) {
215    return MI->getOpcode() == TargetOpcode::G_ZEXTLOAD;
216  }
217};
218
219/// Represents a G_STORE.
220class GStore : public GLoadStore {
221public:
222  /// Get the stored value register.
223  Register getValueReg() const { return getOperand(0).getReg(); }
224
225  static bool classof(const MachineInstr *MI) {
226    return MI->getOpcode() == TargetOpcode::G_STORE;
227  }
228};
229
230/// Represents a G_UNMERGE_VALUES.
231class GUnmerge : public GenericMachineInstr {
232public:
233  /// Returns the number of def registers.
234  unsigned getNumDefs() const { return getNumOperands() - 1; }
235  /// Get the unmerge source register.
236  Register getSourceReg() const { return getOperand(getNumDefs()).getReg(); }
237
238  static bool classof(const MachineInstr *MI) {
239    return MI->getOpcode() == TargetOpcode::G_UNMERGE_VALUES;
240  }
241};
242
243/// Represents G_BUILD_VECTOR, G_CONCAT_VECTORS or G_MERGE_VALUES.
244/// All these have the common property of generating a single value from
245/// multiple sources.
246class GMergeLikeInstr : public GenericMachineInstr {
247public:
248  /// Returns the number of source registers.
249  unsigned getNumSources() const { return getNumOperands() - 1; }
250  /// Returns the I'th source register.
251  Register getSourceReg(unsigned I) const { return getReg(I + 1); }
252
253  static bool classof(const MachineInstr *MI) {
254    switch (MI->getOpcode()) {
255    case TargetOpcode::G_MERGE_VALUES:
256    case TargetOpcode::G_CONCAT_VECTORS:
257    case TargetOpcode::G_BUILD_VECTOR:
258      return true;
259    default:
260      return false;
261    }
262  }
263};
264
265/// Represents a G_MERGE_VALUES.
266class GMerge : public GMergeLikeInstr {
267public:
268  static bool classof(const MachineInstr *MI) {
269    return MI->getOpcode() == TargetOpcode::G_MERGE_VALUES;
270  }
271};
272
273/// Represents a G_CONCAT_VECTORS.
274class GConcatVectors : public GMergeLikeInstr {
275public:
276  static bool classof(const MachineInstr *MI) {
277    return MI->getOpcode() == TargetOpcode::G_CONCAT_VECTORS;
278  }
279};
280
281/// Represents a G_BUILD_VECTOR.
282class GBuildVector : public GMergeLikeInstr {
283public:
284  static bool classof(const MachineInstr *MI) {
285    return MI->getOpcode() == TargetOpcode::G_BUILD_VECTOR;
286  }
287};
288
289/// Represents a G_PTR_ADD.
290class GPtrAdd : public GenericMachineInstr {
291public:
292  Register getBaseReg() const { return getReg(1); }
293  Register getOffsetReg() const { return getReg(2); }
294
295  static bool classof(const MachineInstr *MI) {
296    return MI->getOpcode() == TargetOpcode::G_PTR_ADD;
297  }
298};
299
300/// Represents a G_IMPLICIT_DEF.
301class GImplicitDef : public GenericMachineInstr {
302public:
303  static bool classof(const MachineInstr *MI) {
304    return MI->getOpcode() == TargetOpcode::G_IMPLICIT_DEF;
305  }
306};
307
308/// Represents a G_SELECT.
309class GSelect : public GenericMachineInstr {
310public:
311  Register getCondReg() const { return getReg(1); }
312  Register getTrueReg() const { return getReg(2); }
313  Register getFalseReg() const { return getReg(3); }
314
315  static bool classof(const MachineInstr *MI) {
316    return MI->getOpcode() == TargetOpcode::G_SELECT;
317  }
318};
319
320/// Represent a G_ICMP or G_FCMP.
321class GAnyCmp : public GenericMachineInstr {
322public:
323  CmpInst::Predicate getCond() const {
324    return static_cast<CmpInst::Predicate>(getOperand(1).getPredicate());
325  }
326  Register getLHSReg() const { return getReg(2); }
327  Register getRHSReg() const { return getReg(3); }
328
329  static bool classof(const MachineInstr *MI) {
330    return MI->getOpcode() == TargetOpcode::G_ICMP ||
331           MI->getOpcode() == TargetOpcode::G_FCMP;
332  }
333};
334
335/// Represent a G_ICMP.
336class GICmp : public GAnyCmp {
337public:
338  static bool classof(const MachineInstr *MI) {
339    return MI->getOpcode() == TargetOpcode::G_ICMP;
340  }
341};
342
343/// Represent a G_FCMP.
344class GFCmp : public GAnyCmp {
345public:
346  static bool classof(const MachineInstr *MI) {
347    return MI->getOpcode() == TargetOpcode::G_FCMP;
348  }
349};
350
351/// Represents overflowing binary operations.
352/// Only carry-out:
353/// G_UADDO, G_SADDO, G_USUBO, G_SSUBO, G_UMULO, G_SMULO
354/// Carry-in and carry-out:
355/// G_UADDE, G_SADDE, G_USUBE, G_SSUBE
356class GBinOpCarryOut : public GenericMachineInstr {
357public:
358  Register getDstReg() const { return getReg(0); }
359  Register getCarryOutReg() const { return getReg(1); }
360  MachineOperand &getLHS() { return getOperand(2); }
361  MachineOperand &getRHS() { return getOperand(3); }
362
363  static bool classof(const MachineInstr *MI) {
364    switch (MI->getOpcode()) {
365    case TargetOpcode::G_UADDO:
366    case TargetOpcode::G_SADDO:
367    case TargetOpcode::G_USUBO:
368    case TargetOpcode::G_SSUBO:
369    case TargetOpcode::G_UADDE:
370    case TargetOpcode::G_SADDE:
371    case TargetOpcode::G_USUBE:
372    case TargetOpcode::G_SSUBE:
373    case TargetOpcode::G_UMULO:
374    case TargetOpcode::G_SMULO:
375      return true;
376    default:
377      return false;
378    }
379  }
380};
381
382/// Represents overflowing add/sub operations.
383/// Only carry-out:
384/// G_UADDO, G_SADDO, G_USUBO, G_SSUBO
385/// Carry-in and carry-out:
386/// G_UADDE, G_SADDE, G_USUBE, G_SSUBE
387class GAddSubCarryOut : public GBinOpCarryOut {
388public:
389  bool isAdd() const {
390    switch (getOpcode()) {
391    case TargetOpcode::G_UADDO:
392    case TargetOpcode::G_SADDO:
393    case TargetOpcode::G_UADDE:
394    case TargetOpcode::G_SADDE:
395      return true;
396    default:
397      return false;
398    }
399  }
400  bool isSub() const { return !isAdd(); }
401
402  bool isSigned() const {
403    switch (getOpcode()) {
404    case TargetOpcode::G_SADDO:
405    case TargetOpcode::G_SSUBO:
406    case TargetOpcode::G_SADDE:
407    case TargetOpcode::G_SSUBE:
408      return true;
409    default:
410      return false;
411    }
412  }
413  bool isUnsigned() const { return !isSigned(); }
414
415  static bool classof(const MachineInstr *MI) {
416    switch (MI->getOpcode()) {
417    case TargetOpcode::G_UADDO:
418    case TargetOpcode::G_SADDO:
419    case TargetOpcode::G_USUBO:
420    case TargetOpcode::G_SSUBO:
421    case TargetOpcode::G_UADDE:
422    case TargetOpcode::G_SADDE:
423    case TargetOpcode::G_USUBE:
424    case TargetOpcode::G_SSUBE:
425      return true;
426    default:
427      return false;
428    }
429  }
430};
431
432/// Represents overflowing add/sub operations that also consume a carry-in.
433/// G_UADDE, G_SADDE, G_USUBE, G_SSUBE
434class GAddSubCarryInOut : public GAddSubCarryOut {
435public:
436  Register getCarryInReg() const { return getReg(4); }
437
438  static bool classof(const MachineInstr *MI) {
439    switch (MI->getOpcode()) {
440    case TargetOpcode::G_UADDE:
441    case TargetOpcode::G_SADDE:
442    case TargetOpcode::G_USUBE:
443    case TargetOpcode::G_SSUBE:
444      return true;
445    default:
446      return false;
447    }
448  }
449};
450
451/// Represents a call to an intrinsic.
452class GIntrinsic final : public GenericMachineInstr {
453public:
454  Intrinsic::ID getIntrinsicID() const {
455    return getOperand(getNumExplicitDefs()).getIntrinsicID();
456  }
457
458  bool is(Intrinsic::ID ID) const { return getIntrinsicID() == ID; }
459
460  bool hasSideEffects() const {
461    switch (getOpcode()) {
462    case TargetOpcode::G_INTRINSIC_W_SIDE_EFFECTS:
463    case TargetOpcode::G_INTRINSIC_CONVERGENT_W_SIDE_EFFECTS:
464      return true;
465    default:
466      return false;
467    }
468  }
469
470  bool isConvergent() const {
471    switch (getOpcode()) {
472    case TargetOpcode::G_INTRINSIC_CONVERGENT:
473    case TargetOpcode::G_INTRINSIC_CONVERGENT_W_SIDE_EFFECTS:
474      return true;
475    default:
476      return false;
477    }
478  }
479
480  static bool classof(const MachineInstr *MI) {
481    switch (MI->getOpcode()) {
482    case TargetOpcode::G_INTRINSIC:
483    case TargetOpcode::G_INTRINSIC_W_SIDE_EFFECTS:
484    case TargetOpcode::G_INTRINSIC_CONVERGENT:
485    case TargetOpcode::G_INTRINSIC_CONVERGENT_W_SIDE_EFFECTS:
486      return true;
487    default:
488      return false;
489    }
490  }
491};
492
493// Represents a (non-sequential) vector reduction operation.
494class GVecReduce : public GenericMachineInstr {
495public:
496  static bool classof(const MachineInstr *MI) {
497    switch (MI->getOpcode()) {
498    case TargetOpcode::G_VECREDUCE_FADD:
499    case TargetOpcode::G_VECREDUCE_FMUL:
500    case TargetOpcode::G_VECREDUCE_FMAX:
501    case TargetOpcode::G_VECREDUCE_FMIN:
502    case TargetOpcode::G_VECREDUCE_FMAXIMUM:
503    case TargetOpcode::G_VECREDUCE_FMINIMUM:
504    case TargetOpcode::G_VECREDUCE_ADD:
505    case TargetOpcode::G_VECREDUCE_MUL:
506    case TargetOpcode::G_VECREDUCE_AND:
507    case TargetOpcode::G_VECREDUCE_OR:
508    case TargetOpcode::G_VECREDUCE_XOR:
509    case TargetOpcode::G_VECREDUCE_SMAX:
510    case TargetOpcode::G_VECREDUCE_SMIN:
511    case TargetOpcode::G_VECREDUCE_UMAX:
512    case TargetOpcode::G_VECREDUCE_UMIN:
513      return true;
514    default:
515      return false;
516    }
517  }
518
519  /// Get the opcode for the equivalent scalar operation for this reduction.
520  /// E.g. for G_VECREDUCE_FADD, this returns G_FADD.
521  unsigned getScalarOpcForReduction() {
522    unsigned ScalarOpc;
523    switch (getOpcode()) {
524    case TargetOpcode::G_VECREDUCE_FADD:
525      ScalarOpc = TargetOpcode::G_FADD;
526      break;
527    case TargetOpcode::G_VECREDUCE_FMUL:
528      ScalarOpc = TargetOpcode::G_FMUL;
529      break;
530    case TargetOpcode::G_VECREDUCE_FMAX:
531      ScalarOpc = TargetOpcode::G_FMAXNUM;
532      break;
533    case TargetOpcode::G_VECREDUCE_FMIN:
534      ScalarOpc = TargetOpcode::G_FMINNUM;
535      break;
536    case TargetOpcode::G_VECREDUCE_FMAXIMUM:
537      ScalarOpc = TargetOpcode::G_FMAXIMUM;
538      break;
539    case TargetOpcode::G_VECREDUCE_FMINIMUM:
540      ScalarOpc = TargetOpcode::G_FMINIMUM;
541      break;
542    case TargetOpcode::G_VECREDUCE_ADD:
543      ScalarOpc = TargetOpcode::G_ADD;
544      break;
545    case TargetOpcode::G_VECREDUCE_MUL:
546      ScalarOpc = TargetOpcode::G_MUL;
547      break;
548    case TargetOpcode::G_VECREDUCE_AND:
549      ScalarOpc = TargetOpcode::G_AND;
550      break;
551    case TargetOpcode::G_VECREDUCE_OR:
552      ScalarOpc = TargetOpcode::G_OR;
553      break;
554    case TargetOpcode::G_VECREDUCE_XOR:
555      ScalarOpc = TargetOpcode::G_XOR;
556      break;
557    case TargetOpcode::G_VECREDUCE_SMAX:
558      ScalarOpc = TargetOpcode::G_SMAX;
559      break;
560    case TargetOpcode::G_VECREDUCE_SMIN:
561      ScalarOpc = TargetOpcode::G_SMIN;
562      break;
563    case TargetOpcode::G_VECREDUCE_UMAX:
564      ScalarOpc = TargetOpcode::G_UMAX;
565      break;
566    case TargetOpcode::G_VECREDUCE_UMIN:
567      ScalarOpc = TargetOpcode::G_UMIN;
568      break;
569    default:
570      llvm_unreachable("Unhandled reduction");
571    }
572    return ScalarOpc;
573  }
574};
575
576/// Represents a G_PHI.
577class GPhi : public GenericMachineInstr {
578public:
579  /// Returns the number of incoming values.
580  unsigned getNumIncomingValues() const { return (getNumOperands() - 1) / 2; }
581  /// Returns the I'th incoming vreg.
582  Register getIncomingValue(unsigned I) const {
583    return getOperand(I * 2 + 1).getReg();
584  }
585  /// Returns the I'th incoming basic block.
586  MachineBasicBlock *getIncomingBlock(unsigned I) const {
587    return getOperand(I * 2 + 2).getMBB();
588  }
589
590  static bool classof(const MachineInstr *MI) {
591    return MI->getOpcode() == TargetOpcode::G_PHI;
592  }
593};
594
595} // namespace llvm
596
597#endif // LLVM_CODEGEN_GLOBALISEL_GENERICMACHINEINSTRS_H
598