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(i: Idx).getReg(); } |
33 | |
34 | static bool classof(const MachineInstr *MI) { |
35 | return isPreISelGenericOpcode(Opcode: 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(i: 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(i: 0).getReg(); } |
93 | /// Get the def register of the writeback value. |
94 | Register getWritebackReg() const { return getOperand(i: 1).getReg(); } |
95 | /// Get the base register of the pointer value. |
96 | Register getBaseReg() const { return getOperand(i: 2).getReg(); } |
97 | /// Get the offset register of the pointer value. |
98 | Register getOffsetReg() const { return getOperand(i: 3).getReg(); } |
99 | |
100 | bool isPre() const { return getOperand(i: 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(i: 0).getReg(); } |
153 | /// Get the stored value register. |
154 | Register getValueReg() const { return getOperand(i: 1).getReg(); } |
155 | /// Get the base register of the pointer value. |
156 | Register getBaseReg() const { return getOperand(i: 2).getReg(); } |
157 | /// Get the offset register of the pointer value. |
158 | Register getOffsetReg() const { return getOperand(i: 3).getReg(); } |
159 | |
160 | bool isPre() const { return getOperand(i: 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(i: 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(i: 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(i: 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(Idx: 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(Idx: 1); } |
293 | Register getOffsetReg() const { return getReg(Idx: 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(Idx: 1); } |
312 | Register getTrueReg() const { return getReg(Idx: 2); } |
313 | Register getFalseReg() const { return getReg(Idx: 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(i: 1).getPredicate()); |
325 | } |
326 | Register getLHSReg() const { return getReg(Idx: 2); } |
327 | Register getRHSReg() const { return getReg(Idx: 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(Idx: 0); } |
359 | Register getCarryOutReg() const { return getReg(Idx: 1); } |
360 | MachineOperand &getLHS() { return getOperand(i: 2); } |
361 | MachineOperand &getRHS() { return getOperand(i: 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(Idx: 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(i: 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: I * 2 + 1).getReg(); |
584 | } |
585 | /// Returns the I'th incoming basic block. |
586 | MachineBasicBlock *getIncomingBlock(unsigned I) const { |
587 | return getOperand(i: I * 2 + 2).getMBB(); |
588 | } |
589 | |
590 | static bool classof(const MachineInstr *MI) { |
591 | return MI->getOpcode() == TargetOpcode::G_PHI; |
592 | } |
593 | }; |
594 | |
595 | /// Represents a binary operation, i.e, x = y op z. |
596 | class GBinOp : public GenericMachineInstr { |
597 | public: |
598 | Register getLHSReg() const { return getReg(Idx: 1); } |
599 | Register getRHSReg() const { return getReg(Idx: 2); } |
600 | |
601 | static bool classof(const MachineInstr *MI) { |
602 | switch (MI->getOpcode()) { |
603 | // Integer. |
604 | case TargetOpcode::G_ADD: |
605 | case TargetOpcode::G_SUB: |
606 | case TargetOpcode::G_MUL: |
607 | case TargetOpcode::G_SDIV: |
608 | case TargetOpcode::G_UDIV: |
609 | case TargetOpcode::G_SREM: |
610 | case TargetOpcode::G_UREM: |
611 | case TargetOpcode::G_SMIN: |
612 | case TargetOpcode::G_SMAX: |
613 | case TargetOpcode::G_UMIN: |
614 | case TargetOpcode::G_UMAX: |
615 | // Floating point. |
616 | case TargetOpcode::G_FMINNUM: |
617 | case TargetOpcode::G_FMAXNUM: |
618 | case TargetOpcode::G_FMINNUM_IEEE: |
619 | case TargetOpcode::G_FMAXNUM_IEEE: |
620 | case TargetOpcode::G_FMINIMUM: |
621 | case TargetOpcode::G_FMAXIMUM: |
622 | case TargetOpcode::G_FADD: |
623 | case TargetOpcode::G_FSUB: |
624 | case TargetOpcode::G_FMUL: |
625 | case TargetOpcode::G_FDIV: |
626 | case TargetOpcode::G_FPOW: |
627 | // Logical. |
628 | case TargetOpcode::G_AND: |
629 | case TargetOpcode::G_OR: |
630 | case TargetOpcode::G_XOR: |
631 | return true; |
632 | default: |
633 | return false; |
634 | } |
635 | }; |
636 | }; |
637 | |
638 | /// Represents an integer binary operation. |
639 | class GIntBinOp : public GBinOp { |
640 | public: |
641 | static bool classof(const MachineInstr *MI) { |
642 | switch (MI->getOpcode()) { |
643 | case TargetOpcode::G_ADD: |
644 | case TargetOpcode::G_SUB: |
645 | case TargetOpcode::G_MUL: |
646 | case TargetOpcode::G_SDIV: |
647 | case TargetOpcode::G_UDIV: |
648 | case TargetOpcode::G_SREM: |
649 | case TargetOpcode::G_UREM: |
650 | case TargetOpcode::G_SMIN: |
651 | case TargetOpcode::G_SMAX: |
652 | case TargetOpcode::G_UMIN: |
653 | case TargetOpcode::G_UMAX: |
654 | return true; |
655 | default: |
656 | return false; |
657 | } |
658 | }; |
659 | }; |
660 | |
661 | /// Represents a floating point binary operation. |
662 | class GFBinOp : public GBinOp { |
663 | public: |
664 | static bool classof(const MachineInstr *MI) { |
665 | switch (MI->getOpcode()) { |
666 | case TargetOpcode::G_FMINNUM: |
667 | case TargetOpcode::G_FMAXNUM: |
668 | case TargetOpcode::G_FMINNUM_IEEE: |
669 | case TargetOpcode::G_FMAXNUM_IEEE: |
670 | case TargetOpcode::G_FMINIMUM: |
671 | case TargetOpcode::G_FMAXIMUM: |
672 | case TargetOpcode::G_FADD: |
673 | case TargetOpcode::G_FSUB: |
674 | case TargetOpcode::G_FMUL: |
675 | case TargetOpcode::G_FDIV: |
676 | case TargetOpcode::G_FPOW: |
677 | return true; |
678 | default: |
679 | return false; |
680 | } |
681 | }; |
682 | }; |
683 | |
684 | /// Represents a logical binary operation. |
685 | class GLogicalBinOp : public GBinOp { |
686 | public: |
687 | static bool classof(const MachineInstr *MI) { |
688 | switch (MI->getOpcode()) { |
689 | case TargetOpcode::G_AND: |
690 | case TargetOpcode::G_OR: |
691 | case TargetOpcode::G_XOR: |
692 | return true; |
693 | default: |
694 | return false; |
695 | } |
696 | }; |
697 | }; |
698 | |
699 | /// Represents an integer addition. |
700 | class GAdd : public GIntBinOp { |
701 | public: |
702 | static bool classof(const MachineInstr *MI) { |
703 | return MI->getOpcode() == TargetOpcode::G_ADD; |
704 | }; |
705 | }; |
706 | |
707 | /// Represents a logical and. |
708 | class GAnd : public GLogicalBinOp { |
709 | public: |
710 | static bool classof(const MachineInstr *MI) { |
711 | return MI->getOpcode() == TargetOpcode::G_AND; |
712 | }; |
713 | }; |
714 | |
715 | /// Represents a logical or. |
716 | class GOr : public GLogicalBinOp { |
717 | public: |
718 | static bool classof(const MachineInstr *MI) { |
719 | return MI->getOpcode() == TargetOpcode::G_OR; |
720 | }; |
721 | }; |
722 | |
723 | } // namespace llvm |
724 | |
725 | #endif // LLVM_CODEGEN_GLOBALISEL_GENERICMACHINEINSTRS_H |
726 | |