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 
23 namespace llvm {
24 
25 /// A base class for all GenericMachineInstrs.
26 class GenericMachineInstr : public MachineInstr {
27 public:
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.
40 class GMemOperation : public GenericMachineInstr {
41 public:
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.
68 class GLoadStore : public GMemOperation {
69 public:
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.
89 class GIndexedLoad : public GMemOperation {
90 public:
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.
109 class GIndexedExtLoad : public GIndexedLoad {
110 public:
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.
118 class GIndexedAnyExtLoad : public GIndexedLoad {
119 public:
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.
133 class GIndexedZExtLoad : GIndexedExtLoad {
134 public:
135   static bool classof(const MachineInstr *MI) {
136     return MI->getOpcode() == TargetOpcode::G_INDEXED_ZEXTLOAD;
137   }
138 };
139 
140 /// Represents a G_SEXTLOAD.
141 class GIndexedSExtLoad : GIndexedExtLoad {
142 public:
143   static bool classof(const MachineInstr *MI) {
144     return MI->getOpcode() == TargetOpcode::G_INDEXED_SEXTLOAD;
145   }
146 };
147 
148 /// Represents indexed stores.
149 class GIndexedStore : public GMemOperation {
150 public:
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.
169 class GAnyLoad : public GLoadStore {
170 public:
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.
187 class GLoad : public GAnyLoad {
188 public:
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.
195 class GExtLoad : public GAnyLoad {
196 public:
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.
204 class GSExtLoad : public GExtLoad {
205 public:
206   static bool classof(const MachineInstr *MI) {
207     return MI->getOpcode() == TargetOpcode::G_SEXTLOAD;
208   }
209 };
210 
211 /// Represents a G_ZEXTLOAD.
212 class GZExtLoad : public GExtLoad {
213 public:
214   static bool classof(const MachineInstr *MI) {
215     return MI->getOpcode() == TargetOpcode::G_ZEXTLOAD;
216   }
217 };
218 
219 /// Represents a G_STORE.
220 class GStore : public GLoadStore {
221 public:
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.
231 class GUnmerge : public GenericMachineInstr {
232 public:
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.
246 class GMergeLikeInstr : public GenericMachineInstr {
247 public:
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.
266 class GMerge : public GMergeLikeInstr {
267 public:
268   static bool classof(const MachineInstr *MI) {
269     return MI->getOpcode() == TargetOpcode::G_MERGE_VALUES;
270   }
271 };
272 
273 /// Represents a G_CONCAT_VECTORS.
274 class GConcatVectors : public GMergeLikeInstr {
275 public:
276   static bool classof(const MachineInstr *MI) {
277     return MI->getOpcode() == TargetOpcode::G_CONCAT_VECTORS;
278   }
279 };
280 
281 /// Represents a G_BUILD_VECTOR.
282 class GBuildVector : public GMergeLikeInstr {
283 public:
284   static bool classof(const MachineInstr *MI) {
285     return MI->getOpcode() == TargetOpcode::G_BUILD_VECTOR;
286   }
287 };
288 
289 /// Represents a G_PTR_ADD.
290 class GPtrAdd : public GenericMachineInstr {
291 public:
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.
301 class GImplicitDef : public GenericMachineInstr {
302 public:
303   static bool classof(const MachineInstr *MI) {
304     return MI->getOpcode() == TargetOpcode::G_IMPLICIT_DEF;
305   }
306 };
307 
308 /// Represents a G_SELECT.
309 class GSelect : public GenericMachineInstr {
310 public:
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.
321 class GAnyCmp : public GenericMachineInstr {
322 public:
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.
336 class GICmp : public GAnyCmp {
337 public:
338   static bool classof(const MachineInstr *MI) {
339     return MI->getOpcode() == TargetOpcode::G_ICMP;
340   }
341 };
342 
343 /// Represent a G_FCMP.
344 class GFCmp : public GAnyCmp {
345 public:
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
356 class GBinOpCarryOut : public GenericMachineInstr {
357 public:
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
387 class GAddSubCarryOut : public GBinOpCarryOut {
388 public:
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
434 class GAddSubCarryInOut : public GAddSubCarryOut {
435 public:
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.
452 class GIntrinsic final : public GenericMachineInstr {
453 public:
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.
494 class GVecReduce : public GenericMachineInstr {
495 public:
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.
577 class GPhi : public GenericMachineInstr {
578 public:
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