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 | LocationSize getMemSize() const { return getMMO().getSize(); } |
58 | /// Returns the size in bits of the memory access. |
59 | LocationSize 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_BUILD_VECTOR_TRUNC. |
290 | class GBuildVectorTrunc : public GMergeLikeInstr { |
291 | public: |
292 | static bool classof(const MachineInstr *MI) { |
293 | return MI->getOpcode() == TargetOpcode::G_BUILD_VECTOR_TRUNC; |
294 | } |
295 | }; |
296 | |
297 | /// Represents a G_PTR_ADD. |
298 | class GPtrAdd : public GenericMachineInstr { |
299 | public: |
300 | Register getBaseReg() const { return getReg(Idx: 1); } |
301 | Register getOffsetReg() const { return getReg(Idx: 2); } |
302 | |
303 | static bool classof(const MachineInstr *MI) { |
304 | return MI->getOpcode() == TargetOpcode::G_PTR_ADD; |
305 | } |
306 | }; |
307 | |
308 | /// Represents a G_IMPLICIT_DEF. |
309 | class GImplicitDef : public GenericMachineInstr { |
310 | public: |
311 | static bool classof(const MachineInstr *MI) { |
312 | return MI->getOpcode() == TargetOpcode::G_IMPLICIT_DEF; |
313 | } |
314 | }; |
315 | |
316 | /// Represents a G_SELECT. |
317 | class GSelect : public GenericMachineInstr { |
318 | public: |
319 | Register getCondReg() const { return getReg(Idx: 1); } |
320 | Register getTrueReg() const { return getReg(Idx: 2); } |
321 | Register getFalseReg() const { return getReg(Idx: 3); } |
322 | |
323 | static bool classof(const MachineInstr *MI) { |
324 | return MI->getOpcode() == TargetOpcode::G_SELECT; |
325 | } |
326 | }; |
327 | |
328 | /// Represent a G_ICMP or G_FCMP. |
329 | class GAnyCmp : public GenericMachineInstr { |
330 | public: |
331 | CmpInst::Predicate getCond() const { |
332 | return static_cast<CmpInst::Predicate>(getOperand(i: 1).getPredicate()); |
333 | } |
334 | Register getLHSReg() const { return getReg(Idx: 2); } |
335 | Register getRHSReg() const { return getReg(Idx: 3); } |
336 | |
337 | static bool classof(const MachineInstr *MI) { |
338 | return MI->getOpcode() == TargetOpcode::G_ICMP || |
339 | MI->getOpcode() == TargetOpcode::G_FCMP; |
340 | } |
341 | }; |
342 | |
343 | /// Represent a G_ICMP. |
344 | class GICmp : public GAnyCmp { |
345 | public: |
346 | static bool classof(const MachineInstr *MI) { |
347 | return MI->getOpcode() == TargetOpcode::G_ICMP; |
348 | } |
349 | }; |
350 | |
351 | /// Represent a G_FCMP. |
352 | class GFCmp : public GAnyCmp { |
353 | public: |
354 | static bool classof(const MachineInstr *MI) { |
355 | return MI->getOpcode() == TargetOpcode::G_FCMP; |
356 | } |
357 | }; |
358 | |
359 | /// Represents overflowing binary operations. |
360 | /// Only carry-out: |
361 | /// G_UADDO, G_SADDO, G_USUBO, G_SSUBO, G_UMULO, G_SMULO |
362 | /// Carry-in and carry-out: |
363 | /// G_UADDE, G_SADDE, G_USUBE, G_SSUBE |
364 | class GBinOpCarryOut : public GenericMachineInstr { |
365 | public: |
366 | Register getDstReg() const { return getReg(Idx: 0); } |
367 | Register getCarryOutReg() const { return getReg(Idx: 1); } |
368 | MachineOperand &getLHS() { return getOperand(i: 2); } |
369 | MachineOperand &getRHS() { return getOperand(i: 3); } |
370 | Register getLHSReg() const { return getOperand(i: 2).getReg(); } |
371 | Register getRHSReg() const { return getOperand(i: 3).getReg(); } |
372 | |
373 | static bool classof(const MachineInstr *MI) { |
374 | switch (MI->getOpcode()) { |
375 | case TargetOpcode::G_UADDO: |
376 | case TargetOpcode::G_SADDO: |
377 | case TargetOpcode::G_USUBO: |
378 | case TargetOpcode::G_SSUBO: |
379 | case TargetOpcode::G_UADDE: |
380 | case TargetOpcode::G_SADDE: |
381 | case TargetOpcode::G_USUBE: |
382 | case TargetOpcode::G_SSUBE: |
383 | case TargetOpcode::G_UMULO: |
384 | case TargetOpcode::G_SMULO: |
385 | return true; |
386 | default: |
387 | return false; |
388 | } |
389 | } |
390 | }; |
391 | |
392 | /// Represents overflowing add/sub operations. |
393 | /// Only carry-out: |
394 | /// G_UADDO, G_SADDO, G_USUBO, G_SSUBO |
395 | /// Carry-in and carry-out: |
396 | /// G_UADDE, G_SADDE, G_USUBE, G_SSUBE |
397 | class GAddSubCarryOut : public GBinOpCarryOut { |
398 | public: |
399 | bool isAdd() const { |
400 | switch (getOpcode()) { |
401 | case TargetOpcode::G_UADDO: |
402 | case TargetOpcode::G_SADDO: |
403 | case TargetOpcode::G_UADDE: |
404 | case TargetOpcode::G_SADDE: |
405 | return true; |
406 | default: |
407 | return false; |
408 | } |
409 | } |
410 | bool isSub() const { return !isAdd(); } |
411 | |
412 | bool isSigned() const { |
413 | switch (getOpcode()) { |
414 | case TargetOpcode::G_SADDO: |
415 | case TargetOpcode::G_SSUBO: |
416 | case TargetOpcode::G_SADDE: |
417 | case TargetOpcode::G_SSUBE: |
418 | return true; |
419 | default: |
420 | return false; |
421 | } |
422 | } |
423 | bool isUnsigned() const { return !isSigned(); } |
424 | |
425 | static bool classof(const MachineInstr *MI) { |
426 | switch (MI->getOpcode()) { |
427 | case TargetOpcode::G_UADDO: |
428 | case TargetOpcode::G_SADDO: |
429 | case TargetOpcode::G_USUBO: |
430 | case TargetOpcode::G_SSUBO: |
431 | case TargetOpcode::G_UADDE: |
432 | case TargetOpcode::G_SADDE: |
433 | case TargetOpcode::G_USUBE: |
434 | case TargetOpcode::G_SSUBE: |
435 | return true; |
436 | default: |
437 | return false; |
438 | } |
439 | } |
440 | }; |
441 | |
442 | /// Represents overflowing add operations. |
443 | /// G_UADDO, G_SADDO |
444 | class GAddCarryOut : public GBinOpCarryOut { |
445 | public: |
446 | bool isSigned() const { return getOpcode() == TargetOpcode::G_SADDO; } |
447 | |
448 | static bool classof(const MachineInstr *MI) { |
449 | switch (MI->getOpcode()) { |
450 | case TargetOpcode::G_UADDO: |
451 | case TargetOpcode::G_SADDO: |
452 | return true; |
453 | default: |
454 | return false; |
455 | } |
456 | } |
457 | }; |
458 | |
459 | /// Represents overflowing add/sub operations that also consume a carry-in. |
460 | /// G_UADDE, G_SADDE, G_USUBE, G_SSUBE |
461 | class GAddSubCarryInOut : public GAddSubCarryOut { |
462 | public: |
463 | Register getCarryInReg() const { return getReg(Idx: 4); } |
464 | |
465 | static bool classof(const MachineInstr *MI) { |
466 | switch (MI->getOpcode()) { |
467 | case TargetOpcode::G_UADDE: |
468 | case TargetOpcode::G_SADDE: |
469 | case TargetOpcode::G_USUBE: |
470 | case TargetOpcode::G_SSUBE: |
471 | return true; |
472 | default: |
473 | return false; |
474 | } |
475 | } |
476 | }; |
477 | |
478 | /// Represents a call to an intrinsic. |
479 | class GIntrinsic final : public GenericMachineInstr { |
480 | public: |
481 | Intrinsic::ID getIntrinsicID() const { |
482 | return getOperand(i: getNumExplicitDefs()).getIntrinsicID(); |
483 | } |
484 | |
485 | bool is(Intrinsic::ID ID) const { return getIntrinsicID() == ID; } |
486 | |
487 | bool hasSideEffects() const { |
488 | switch (getOpcode()) { |
489 | case TargetOpcode::G_INTRINSIC_W_SIDE_EFFECTS: |
490 | case TargetOpcode::G_INTRINSIC_CONVERGENT_W_SIDE_EFFECTS: |
491 | return true; |
492 | default: |
493 | return false; |
494 | } |
495 | } |
496 | |
497 | bool isConvergent() const { |
498 | switch (getOpcode()) { |
499 | case TargetOpcode::G_INTRINSIC_CONVERGENT: |
500 | case TargetOpcode::G_INTRINSIC_CONVERGENT_W_SIDE_EFFECTS: |
501 | return true; |
502 | default: |
503 | return false; |
504 | } |
505 | } |
506 | |
507 | static bool classof(const MachineInstr *MI) { |
508 | switch (MI->getOpcode()) { |
509 | case TargetOpcode::G_INTRINSIC: |
510 | case TargetOpcode::G_INTRINSIC_W_SIDE_EFFECTS: |
511 | case TargetOpcode::G_INTRINSIC_CONVERGENT: |
512 | case TargetOpcode::G_INTRINSIC_CONVERGENT_W_SIDE_EFFECTS: |
513 | return true; |
514 | default: |
515 | return false; |
516 | } |
517 | } |
518 | }; |
519 | |
520 | // Represents a (non-sequential) vector reduction operation. |
521 | class GVecReduce : public GenericMachineInstr { |
522 | public: |
523 | static bool classof(const MachineInstr *MI) { |
524 | switch (MI->getOpcode()) { |
525 | case TargetOpcode::G_VECREDUCE_FADD: |
526 | case TargetOpcode::G_VECREDUCE_FMUL: |
527 | case TargetOpcode::G_VECREDUCE_FMAX: |
528 | case TargetOpcode::G_VECREDUCE_FMIN: |
529 | case TargetOpcode::G_VECREDUCE_FMAXIMUM: |
530 | case TargetOpcode::G_VECREDUCE_FMINIMUM: |
531 | case TargetOpcode::G_VECREDUCE_ADD: |
532 | case TargetOpcode::G_VECREDUCE_MUL: |
533 | case TargetOpcode::G_VECREDUCE_AND: |
534 | case TargetOpcode::G_VECREDUCE_OR: |
535 | case TargetOpcode::G_VECREDUCE_XOR: |
536 | case TargetOpcode::G_VECREDUCE_SMAX: |
537 | case TargetOpcode::G_VECREDUCE_SMIN: |
538 | case TargetOpcode::G_VECREDUCE_UMAX: |
539 | case TargetOpcode::G_VECREDUCE_UMIN: |
540 | return true; |
541 | default: |
542 | return false; |
543 | } |
544 | } |
545 | |
546 | /// Get the opcode for the equivalent scalar operation for this reduction. |
547 | /// E.g. for G_VECREDUCE_FADD, this returns G_FADD. |
548 | unsigned getScalarOpcForReduction() { |
549 | unsigned ScalarOpc; |
550 | switch (getOpcode()) { |
551 | case TargetOpcode::G_VECREDUCE_FADD: |
552 | ScalarOpc = TargetOpcode::G_FADD; |
553 | break; |
554 | case TargetOpcode::G_VECREDUCE_FMUL: |
555 | ScalarOpc = TargetOpcode::G_FMUL; |
556 | break; |
557 | case TargetOpcode::G_VECREDUCE_FMAX: |
558 | ScalarOpc = TargetOpcode::G_FMAXNUM; |
559 | break; |
560 | case TargetOpcode::G_VECREDUCE_FMIN: |
561 | ScalarOpc = TargetOpcode::G_FMINNUM; |
562 | break; |
563 | case TargetOpcode::G_VECREDUCE_FMAXIMUM: |
564 | ScalarOpc = TargetOpcode::G_FMAXIMUM; |
565 | break; |
566 | case TargetOpcode::G_VECREDUCE_FMINIMUM: |
567 | ScalarOpc = TargetOpcode::G_FMINIMUM; |
568 | break; |
569 | case TargetOpcode::G_VECREDUCE_ADD: |
570 | ScalarOpc = TargetOpcode::G_ADD; |
571 | break; |
572 | case TargetOpcode::G_VECREDUCE_MUL: |
573 | ScalarOpc = TargetOpcode::G_MUL; |
574 | break; |
575 | case TargetOpcode::G_VECREDUCE_AND: |
576 | ScalarOpc = TargetOpcode::G_AND; |
577 | break; |
578 | case TargetOpcode::G_VECREDUCE_OR: |
579 | ScalarOpc = TargetOpcode::G_OR; |
580 | break; |
581 | case TargetOpcode::G_VECREDUCE_XOR: |
582 | ScalarOpc = TargetOpcode::G_XOR; |
583 | break; |
584 | case TargetOpcode::G_VECREDUCE_SMAX: |
585 | ScalarOpc = TargetOpcode::G_SMAX; |
586 | break; |
587 | case TargetOpcode::G_VECREDUCE_SMIN: |
588 | ScalarOpc = TargetOpcode::G_SMIN; |
589 | break; |
590 | case TargetOpcode::G_VECREDUCE_UMAX: |
591 | ScalarOpc = TargetOpcode::G_UMAX; |
592 | break; |
593 | case TargetOpcode::G_VECREDUCE_UMIN: |
594 | ScalarOpc = TargetOpcode::G_UMIN; |
595 | break; |
596 | default: |
597 | llvm_unreachable("Unhandled reduction" ); |
598 | } |
599 | return ScalarOpc; |
600 | } |
601 | }; |
602 | |
603 | /// Represents a G_PHI. |
604 | class GPhi : public GenericMachineInstr { |
605 | public: |
606 | /// Returns the number of incoming values. |
607 | unsigned getNumIncomingValues() const { return (getNumOperands() - 1) / 2; } |
608 | /// Returns the I'th incoming vreg. |
609 | Register getIncomingValue(unsigned I) const { |
610 | return getOperand(i: I * 2 + 1).getReg(); |
611 | } |
612 | /// Returns the I'th incoming basic block. |
613 | MachineBasicBlock *getIncomingBlock(unsigned I) const { |
614 | return getOperand(i: I * 2 + 2).getMBB(); |
615 | } |
616 | |
617 | static bool classof(const MachineInstr *MI) { |
618 | return MI->getOpcode() == TargetOpcode::G_PHI; |
619 | } |
620 | }; |
621 | |
622 | /// Represents a binary operation, i.e, x = y op z. |
623 | class GBinOp : public GenericMachineInstr { |
624 | public: |
625 | Register getLHSReg() const { return getReg(Idx: 1); } |
626 | Register getRHSReg() const { return getReg(Idx: 2); } |
627 | |
628 | static bool classof(const MachineInstr *MI) { |
629 | switch (MI->getOpcode()) { |
630 | // Integer. |
631 | case TargetOpcode::G_ADD: |
632 | case TargetOpcode::G_SUB: |
633 | case TargetOpcode::G_MUL: |
634 | case TargetOpcode::G_SDIV: |
635 | case TargetOpcode::G_UDIV: |
636 | case TargetOpcode::G_SREM: |
637 | case TargetOpcode::G_UREM: |
638 | case TargetOpcode::G_SMIN: |
639 | case TargetOpcode::G_SMAX: |
640 | case TargetOpcode::G_UMIN: |
641 | case TargetOpcode::G_UMAX: |
642 | // Floating point. |
643 | case TargetOpcode::G_FMINNUM: |
644 | case TargetOpcode::G_FMAXNUM: |
645 | case TargetOpcode::G_FMINNUM_IEEE: |
646 | case TargetOpcode::G_FMAXNUM_IEEE: |
647 | case TargetOpcode::G_FMINIMUM: |
648 | case TargetOpcode::G_FMAXIMUM: |
649 | case TargetOpcode::G_FADD: |
650 | case TargetOpcode::G_FSUB: |
651 | case TargetOpcode::G_FMUL: |
652 | case TargetOpcode::G_FDIV: |
653 | case TargetOpcode::G_FPOW: |
654 | // Logical. |
655 | case TargetOpcode::G_AND: |
656 | case TargetOpcode::G_OR: |
657 | case TargetOpcode::G_XOR: |
658 | return true; |
659 | default: |
660 | return false; |
661 | } |
662 | }; |
663 | }; |
664 | |
665 | /// Represents an integer binary operation. |
666 | class GIntBinOp : public GBinOp { |
667 | public: |
668 | static bool classof(const MachineInstr *MI) { |
669 | switch (MI->getOpcode()) { |
670 | case TargetOpcode::G_ADD: |
671 | case TargetOpcode::G_SUB: |
672 | case TargetOpcode::G_MUL: |
673 | case TargetOpcode::G_SDIV: |
674 | case TargetOpcode::G_UDIV: |
675 | case TargetOpcode::G_SREM: |
676 | case TargetOpcode::G_UREM: |
677 | case TargetOpcode::G_SMIN: |
678 | case TargetOpcode::G_SMAX: |
679 | case TargetOpcode::G_UMIN: |
680 | case TargetOpcode::G_UMAX: |
681 | return true; |
682 | default: |
683 | return false; |
684 | } |
685 | }; |
686 | }; |
687 | |
688 | /// Represents a floating point binary operation. |
689 | class GFBinOp : public GBinOp { |
690 | public: |
691 | static bool classof(const MachineInstr *MI) { |
692 | switch (MI->getOpcode()) { |
693 | case TargetOpcode::G_FMINNUM: |
694 | case TargetOpcode::G_FMAXNUM: |
695 | case TargetOpcode::G_FMINNUM_IEEE: |
696 | case TargetOpcode::G_FMAXNUM_IEEE: |
697 | case TargetOpcode::G_FMINIMUM: |
698 | case TargetOpcode::G_FMAXIMUM: |
699 | case TargetOpcode::G_FADD: |
700 | case TargetOpcode::G_FSUB: |
701 | case TargetOpcode::G_FMUL: |
702 | case TargetOpcode::G_FDIV: |
703 | case TargetOpcode::G_FPOW: |
704 | return true; |
705 | default: |
706 | return false; |
707 | } |
708 | }; |
709 | }; |
710 | |
711 | /// Represents a logical binary operation. |
712 | class GLogicalBinOp : public GBinOp { |
713 | public: |
714 | static bool classof(const MachineInstr *MI) { |
715 | switch (MI->getOpcode()) { |
716 | case TargetOpcode::G_AND: |
717 | case TargetOpcode::G_OR: |
718 | case TargetOpcode::G_XOR: |
719 | return true; |
720 | default: |
721 | return false; |
722 | } |
723 | }; |
724 | }; |
725 | |
726 | /// Represents an integer addition. |
727 | class GAdd : public GIntBinOp { |
728 | public: |
729 | static bool classof(const MachineInstr *MI) { |
730 | return MI->getOpcode() == TargetOpcode::G_ADD; |
731 | }; |
732 | }; |
733 | |
734 | /// Represents a logical and. |
735 | class GAnd : public GLogicalBinOp { |
736 | public: |
737 | static bool classof(const MachineInstr *MI) { |
738 | return MI->getOpcode() == TargetOpcode::G_AND; |
739 | }; |
740 | }; |
741 | |
742 | /// Represents a logical or. |
743 | class GOr : public GLogicalBinOp { |
744 | public: |
745 | static bool classof(const MachineInstr *MI) { |
746 | return MI->getOpcode() == TargetOpcode::G_OR; |
747 | }; |
748 | }; |
749 | |
750 | /// Represents an extract vector element. |
751 | class : public GenericMachineInstr { |
752 | public: |
753 | Register () const { return getOperand(i: 1).getReg(); } |
754 | Register () const { return getOperand(i: 2).getReg(); } |
755 | |
756 | static bool (const MachineInstr *MI) { |
757 | return MI->getOpcode() == TargetOpcode::G_EXTRACT_VECTOR_ELT; |
758 | } |
759 | }; |
760 | |
761 | /// Represents an insert vector element. |
762 | class GInsertVectorElement : public GenericMachineInstr { |
763 | public: |
764 | Register getVectorReg() const { return getOperand(i: 1).getReg(); } |
765 | Register getElementReg() const { return getOperand(i: 2).getReg(); } |
766 | Register getIndexReg() const { return getOperand(i: 3).getReg(); } |
767 | |
768 | static bool classof(const MachineInstr *MI) { |
769 | return MI->getOpcode() == TargetOpcode::G_INSERT_VECTOR_ELT; |
770 | } |
771 | }; |
772 | |
773 | /// Represents a freeze. |
774 | class GFreeze : public GenericMachineInstr { |
775 | public: |
776 | Register getSourceReg() const { return getOperand(i: 1).getReg(); } |
777 | |
778 | static bool classof(const MachineInstr *MI) { |
779 | return MI->getOpcode() == TargetOpcode::G_FREEZE; |
780 | } |
781 | }; |
782 | |
783 | } // namespace llvm |
784 | |
785 | #endif // LLVM_CODEGEN_GLOBALISEL_GENERICMACHINEINSTRS_H |
786 | |