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. getReg(unsigned Idx)32 Register getReg(unsigned Idx) const { return getOperand(Idx).getReg(); } 33 classof(const MachineInstr * MI)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. getMMO()43 MachineMemOperand &getMMO() const { return **memoperands_begin(); } 44 45 /// Returns true if the attached MachineMemOperand has the atomic flag set. isAtomic()46 bool isAtomic() const { return getMMO().isAtomic(); } 47 /// Returns true if the attached MachineMemOpeand as the volatile flag set. isVolatile()48 bool isVolatile() const { return getMMO().isVolatile(); } 49 /// Returns true if the memory operation is neither atomic or volatile. isSimple()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. isUnordered()54 bool isUnordered() const { return getMMO().isUnordered(); } 55 56 /// Returns the size in bytes of the memory access. getMemSize()57 uint64_t getMemSize() const { return getMMO().getSize(); } 58 /// Returns the size in bits of the memory access. getMemSizeInBits()59 uint64_t getMemSizeInBits() const { return getMMO().getSizeInBits(); } 60 classof(const MachineInstr * MI)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. getPointerReg()71 Register getPointerReg() const { return getOperand(1).getReg(); } 72 classof(const MachineInstr * MI)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. getDstReg()92 Register getDstReg() const { return getOperand(0).getReg(); } 93 /// Get the def register of the writeback value. getWritebackReg()94 Register getWritebackReg() const { return getOperand(1).getReg(); } 95 /// Get the base register of the pointer value. getBaseReg()96 Register getBaseReg() const { return getOperand(2).getReg(); } 97 /// Get the offset register of the pointer value. getOffsetReg()98 Register getOffsetReg() const { return getOperand(3).getReg(); } 99 isPre()100 bool isPre() const { return getOperand(4).getImm() == 1; } isPost()101 bool isPost() const { return !isPre(); } 102 classof(const MachineInstr * MI)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: classof(const MachineInstr * MI)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 a G_ZEXTLOAD. 118 class GIndexedZExtLoad : GIndexedExtLoad { 119 public: classof(const MachineInstr * MI)120 static bool classof(const MachineInstr *MI) { 121 return MI->getOpcode() == TargetOpcode::G_INDEXED_ZEXTLOAD; 122 } 123 }; 124 125 /// Represents a G_SEXTLOAD. 126 class GIndexedSExtLoad : GIndexedExtLoad { 127 public: classof(const MachineInstr * MI)128 static bool classof(const MachineInstr *MI) { 129 return MI->getOpcode() == TargetOpcode::G_INDEXED_SEXTLOAD; 130 } 131 }; 132 133 /// Represents indexed stores. 134 class GIndexedStore : public GMemOperation { 135 public: 136 /// Get the def register of the writeback value. getWritebackReg()137 Register getWritebackReg() const { return getOperand(0).getReg(); } 138 /// Get the stored value register. getValueReg()139 Register getValueReg() const { return getOperand(1).getReg(); } 140 /// Get the base register of the pointer value. getBaseReg()141 Register getBaseReg() const { return getOperand(2).getReg(); } 142 /// Get the offset register of the pointer value. getOffsetReg()143 Register getOffsetReg() const { return getOperand(3).getReg(); } 144 isPre()145 bool isPre() const { return getOperand(4).getImm() == 1; } isPost()146 bool isPost() const { return !isPre(); } 147 classof(const MachineInstr * MI)148 static bool classof(const MachineInstr *MI) { 149 return MI->getOpcode() == TargetOpcode::G_INDEXED_STORE; 150 } 151 }; 152 153 /// Represents any generic load, including sign/zero extending variants. 154 class GAnyLoad : public GLoadStore { 155 public: 156 /// Get the definition register of the loaded value. getDstReg()157 Register getDstReg() const { return getOperand(0).getReg(); } 158 classof(const MachineInstr * MI)159 static bool classof(const MachineInstr *MI) { 160 switch (MI->getOpcode()) { 161 case TargetOpcode::G_LOAD: 162 case TargetOpcode::G_ZEXTLOAD: 163 case TargetOpcode::G_SEXTLOAD: 164 return true; 165 default: 166 return false; 167 } 168 } 169 }; 170 171 /// Represents a G_LOAD. 172 class GLoad : public GAnyLoad { 173 public: classof(const MachineInstr * MI)174 static bool classof(const MachineInstr *MI) { 175 return MI->getOpcode() == TargetOpcode::G_LOAD; 176 } 177 }; 178 179 /// Represents either a G_SEXTLOAD or G_ZEXTLOAD. 180 class GExtLoad : public GAnyLoad { 181 public: classof(const MachineInstr * MI)182 static bool classof(const MachineInstr *MI) { 183 return MI->getOpcode() == TargetOpcode::G_SEXTLOAD || 184 MI->getOpcode() == TargetOpcode::G_ZEXTLOAD; 185 } 186 }; 187 188 /// Represents a G_SEXTLOAD. 189 class GSExtLoad : public GExtLoad { 190 public: classof(const MachineInstr * MI)191 static bool classof(const MachineInstr *MI) { 192 return MI->getOpcode() == TargetOpcode::G_SEXTLOAD; 193 } 194 }; 195 196 /// Represents a G_ZEXTLOAD. 197 class GZExtLoad : public GExtLoad { 198 public: classof(const MachineInstr * MI)199 static bool classof(const MachineInstr *MI) { 200 return MI->getOpcode() == TargetOpcode::G_ZEXTLOAD; 201 } 202 }; 203 204 /// Represents a G_STORE. 205 class GStore : public GLoadStore { 206 public: 207 /// Get the stored value register. getValueReg()208 Register getValueReg() const { return getOperand(0).getReg(); } 209 classof(const MachineInstr * MI)210 static bool classof(const MachineInstr *MI) { 211 return MI->getOpcode() == TargetOpcode::G_STORE; 212 } 213 }; 214 215 /// Represents a G_UNMERGE_VALUES. 216 class GUnmerge : public GenericMachineInstr { 217 public: 218 /// Returns the number of def registers. getNumDefs()219 unsigned getNumDefs() const { return getNumOperands() - 1; } 220 /// Get the unmerge source register. getSourceReg()221 Register getSourceReg() const { return getOperand(getNumDefs()).getReg(); } 222 classof(const MachineInstr * MI)223 static bool classof(const MachineInstr *MI) { 224 return MI->getOpcode() == TargetOpcode::G_UNMERGE_VALUES; 225 } 226 }; 227 228 /// Represents G_BUILD_VECTOR, G_CONCAT_VECTORS or G_MERGE_VALUES. 229 /// All these have the common property of generating a single value from 230 /// multiple sources. 231 class GMergeLikeInstr : public GenericMachineInstr { 232 public: 233 /// Returns the number of source registers. getNumSources()234 unsigned getNumSources() const { return getNumOperands() - 1; } 235 /// Returns the I'th source register. getSourceReg(unsigned I)236 Register getSourceReg(unsigned I) const { return getReg(I + 1); } 237 classof(const MachineInstr * MI)238 static bool classof(const MachineInstr *MI) { 239 switch (MI->getOpcode()) { 240 case TargetOpcode::G_MERGE_VALUES: 241 case TargetOpcode::G_CONCAT_VECTORS: 242 case TargetOpcode::G_BUILD_VECTOR: 243 return true; 244 default: 245 return false; 246 } 247 } 248 }; 249 250 /// Represents a G_MERGE_VALUES. 251 class GMerge : public GMergeLikeInstr { 252 public: classof(const MachineInstr * MI)253 static bool classof(const MachineInstr *MI) { 254 return MI->getOpcode() == TargetOpcode::G_MERGE_VALUES; 255 } 256 }; 257 258 /// Represents a G_CONCAT_VECTORS. 259 class GConcatVectors : public GMergeLikeInstr { 260 public: classof(const MachineInstr * MI)261 static bool classof(const MachineInstr *MI) { 262 return MI->getOpcode() == TargetOpcode::G_CONCAT_VECTORS; 263 } 264 }; 265 266 /// Represents a G_BUILD_VECTOR. 267 class GBuildVector : public GMergeLikeInstr { 268 public: classof(const MachineInstr * MI)269 static bool classof(const MachineInstr *MI) { 270 return MI->getOpcode() == TargetOpcode::G_BUILD_VECTOR; 271 } 272 }; 273 274 /// Represents a G_PTR_ADD. 275 class GPtrAdd : public GenericMachineInstr { 276 public: getBaseReg()277 Register getBaseReg() const { return getReg(1); } getOffsetReg()278 Register getOffsetReg() const { return getReg(2); } 279 classof(const MachineInstr * MI)280 static bool classof(const MachineInstr *MI) { 281 return MI->getOpcode() == TargetOpcode::G_PTR_ADD; 282 } 283 }; 284 285 /// Represents a G_IMPLICIT_DEF. 286 class GImplicitDef : public GenericMachineInstr { 287 public: classof(const MachineInstr * MI)288 static bool classof(const MachineInstr *MI) { 289 return MI->getOpcode() == TargetOpcode::G_IMPLICIT_DEF; 290 } 291 }; 292 293 /// Represents a G_SELECT. 294 class GSelect : public GenericMachineInstr { 295 public: getCondReg()296 Register getCondReg() const { return getReg(1); } getTrueReg()297 Register getTrueReg() const { return getReg(2); } getFalseReg()298 Register getFalseReg() const { return getReg(3); } 299 classof(const MachineInstr * MI)300 static bool classof(const MachineInstr *MI) { 301 return MI->getOpcode() == TargetOpcode::G_SELECT; 302 } 303 }; 304 305 /// Represent a G_ICMP or G_FCMP. 306 class GAnyCmp : public GenericMachineInstr { 307 public: getCond()308 CmpInst::Predicate getCond() const { 309 return static_cast<CmpInst::Predicate>(getOperand(1).getPredicate()); 310 } getLHSReg()311 Register getLHSReg() const { return getReg(2); } getRHSReg()312 Register getRHSReg() const { return getReg(3); } 313 classof(const MachineInstr * MI)314 static bool classof(const MachineInstr *MI) { 315 return MI->getOpcode() == TargetOpcode::G_ICMP || 316 MI->getOpcode() == TargetOpcode::G_FCMP; 317 } 318 }; 319 320 /// Represent a G_ICMP. 321 class GICmp : public GAnyCmp { 322 public: classof(const MachineInstr * MI)323 static bool classof(const MachineInstr *MI) { 324 return MI->getOpcode() == TargetOpcode::G_ICMP; 325 } 326 }; 327 328 /// Represent a G_FCMP. 329 class GFCmp : public GAnyCmp { 330 public: classof(const MachineInstr * MI)331 static bool classof(const MachineInstr *MI) { 332 return MI->getOpcode() == TargetOpcode::G_FCMP; 333 } 334 }; 335 336 /// Represents overflowing binary operations. 337 /// Only carry-out: 338 /// G_UADDO, G_SADDO, G_USUBO, G_SSUBO, G_UMULO, G_SMULO 339 /// Carry-in and carry-out: 340 /// G_UADDE, G_SADDE, G_USUBE, G_SSUBE 341 class GBinOpCarryOut : public GenericMachineInstr { 342 public: getDstReg()343 Register getDstReg() const { return getReg(0); } getCarryOutReg()344 Register getCarryOutReg() const { return getReg(1); } getLHS()345 MachineOperand &getLHS() { return getOperand(2); } getRHS()346 MachineOperand &getRHS() { return getOperand(3); } 347 classof(const MachineInstr * MI)348 static bool classof(const MachineInstr *MI) { 349 switch (MI->getOpcode()) { 350 case TargetOpcode::G_UADDO: 351 case TargetOpcode::G_SADDO: 352 case TargetOpcode::G_USUBO: 353 case TargetOpcode::G_SSUBO: 354 case TargetOpcode::G_UADDE: 355 case TargetOpcode::G_SADDE: 356 case TargetOpcode::G_USUBE: 357 case TargetOpcode::G_SSUBE: 358 case TargetOpcode::G_UMULO: 359 case TargetOpcode::G_SMULO: 360 return true; 361 default: 362 return false; 363 } 364 } 365 }; 366 367 /// Represents overflowing add/sub operations. 368 /// Only carry-out: 369 /// G_UADDO, G_SADDO, G_USUBO, G_SSUBO 370 /// Carry-in and carry-out: 371 /// G_UADDE, G_SADDE, G_USUBE, G_SSUBE 372 class GAddSubCarryOut : public GBinOpCarryOut { 373 public: isAdd()374 bool isAdd() const { 375 switch (getOpcode()) { 376 case TargetOpcode::G_UADDO: 377 case TargetOpcode::G_SADDO: 378 case TargetOpcode::G_UADDE: 379 case TargetOpcode::G_SADDE: 380 return true; 381 default: 382 return false; 383 } 384 } isSub()385 bool isSub() const { return !isAdd(); } 386 isSigned()387 bool isSigned() const { 388 switch (getOpcode()) { 389 case TargetOpcode::G_SADDO: 390 case TargetOpcode::G_SSUBO: 391 case TargetOpcode::G_SADDE: 392 case TargetOpcode::G_SSUBE: 393 return true; 394 default: 395 return false; 396 } 397 } isUnsigned()398 bool isUnsigned() const { return !isSigned(); } 399 classof(const MachineInstr * MI)400 static bool classof(const MachineInstr *MI) { 401 switch (MI->getOpcode()) { 402 case TargetOpcode::G_UADDO: 403 case TargetOpcode::G_SADDO: 404 case TargetOpcode::G_USUBO: 405 case TargetOpcode::G_SSUBO: 406 case TargetOpcode::G_UADDE: 407 case TargetOpcode::G_SADDE: 408 case TargetOpcode::G_USUBE: 409 case TargetOpcode::G_SSUBE: 410 return true; 411 default: 412 return false; 413 } 414 } 415 }; 416 417 /// Represents overflowing add/sub operations that also consume a carry-in. 418 /// G_UADDE, G_SADDE, G_USUBE, G_SSUBE 419 class GAddSubCarryInOut : public GAddSubCarryOut { 420 public: getCarryInReg()421 Register getCarryInReg() const { return getReg(4); } 422 classof(const MachineInstr * MI)423 static bool classof(const MachineInstr *MI) { 424 switch (MI->getOpcode()) { 425 case TargetOpcode::G_UADDE: 426 case TargetOpcode::G_SADDE: 427 case TargetOpcode::G_USUBE: 428 case TargetOpcode::G_SSUBE: 429 return true; 430 default: 431 return false; 432 } 433 } 434 }; 435 436 /// Represents a call to an intrinsic. 437 class GIntrinsic final : public GenericMachineInstr { 438 public: getIntrinsicID()439 Intrinsic::ID getIntrinsicID() const { 440 return getOperand(getNumExplicitDefs()).getIntrinsicID(); 441 } 442 is(Intrinsic::ID ID)443 bool is(Intrinsic::ID ID) const { return getIntrinsicID() == ID; } 444 hasSideEffects()445 bool hasSideEffects() const { 446 switch (getOpcode()) { 447 case TargetOpcode::G_INTRINSIC_W_SIDE_EFFECTS: 448 case TargetOpcode::G_INTRINSIC_CONVERGENT_W_SIDE_EFFECTS: 449 return true; 450 default: 451 return false; 452 } 453 } 454 isConvergent()455 bool isConvergent() const { 456 switch (getOpcode()) { 457 case TargetOpcode::G_INTRINSIC_CONVERGENT: 458 case TargetOpcode::G_INTRINSIC_CONVERGENT_W_SIDE_EFFECTS: 459 return true; 460 default: 461 return false; 462 } 463 } 464 classof(const MachineInstr * MI)465 static bool classof(const MachineInstr *MI) { 466 switch (MI->getOpcode()) { 467 case TargetOpcode::G_INTRINSIC: 468 case TargetOpcode::G_INTRINSIC_W_SIDE_EFFECTS: 469 case TargetOpcode::G_INTRINSIC_CONVERGENT: 470 case TargetOpcode::G_INTRINSIC_CONVERGENT_W_SIDE_EFFECTS: 471 return true; 472 default: 473 return false; 474 } 475 } 476 }; 477 478 // Represents a (non-sequential) vector reduction operation. 479 class GVecReduce : public GenericMachineInstr { 480 public: classof(const MachineInstr * MI)481 static bool classof(const MachineInstr *MI) { 482 switch (MI->getOpcode()) { 483 case TargetOpcode::G_VECREDUCE_FADD: 484 case TargetOpcode::G_VECREDUCE_FMUL: 485 case TargetOpcode::G_VECREDUCE_FMAX: 486 case TargetOpcode::G_VECREDUCE_FMIN: 487 case TargetOpcode::G_VECREDUCE_FMAXIMUM: 488 case TargetOpcode::G_VECREDUCE_FMINIMUM: 489 case TargetOpcode::G_VECREDUCE_ADD: 490 case TargetOpcode::G_VECREDUCE_MUL: 491 case TargetOpcode::G_VECREDUCE_AND: 492 case TargetOpcode::G_VECREDUCE_OR: 493 case TargetOpcode::G_VECREDUCE_XOR: 494 case TargetOpcode::G_VECREDUCE_SMAX: 495 case TargetOpcode::G_VECREDUCE_SMIN: 496 case TargetOpcode::G_VECREDUCE_UMAX: 497 case TargetOpcode::G_VECREDUCE_UMIN: 498 return true; 499 default: 500 return false; 501 } 502 } 503 504 /// Get the opcode for the equivalent scalar operation for this reduction. 505 /// E.g. for G_VECREDUCE_FADD, this returns G_FADD. getScalarOpcForReduction()506 unsigned getScalarOpcForReduction() { 507 unsigned ScalarOpc; 508 switch (getOpcode()) { 509 case TargetOpcode::G_VECREDUCE_FADD: 510 ScalarOpc = TargetOpcode::G_FADD; 511 break; 512 case TargetOpcode::G_VECREDUCE_FMUL: 513 ScalarOpc = TargetOpcode::G_FMUL; 514 break; 515 case TargetOpcode::G_VECREDUCE_FMAX: 516 ScalarOpc = TargetOpcode::G_FMAXNUM; 517 break; 518 case TargetOpcode::G_VECREDUCE_FMIN: 519 ScalarOpc = TargetOpcode::G_FMINNUM; 520 break; 521 case TargetOpcode::G_VECREDUCE_FMAXIMUM: 522 ScalarOpc = TargetOpcode::G_FMAXIMUM; 523 break; 524 case TargetOpcode::G_VECREDUCE_FMINIMUM: 525 ScalarOpc = TargetOpcode::G_FMINIMUM; 526 break; 527 case TargetOpcode::G_VECREDUCE_ADD: 528 ScalarOpc = TargetOpcode::G_ADD; 529 break; 530 case TargetOpcode::G_VECREDUCE_MUL: 531 ScalarOpc = TargetOpcode::G_MUL; 532 break; 533 case TargetOpcode::G_VECREDUCE_AND: 534 ScalarOpc = TargetOpcode::G_AND; 535 break; 536 case TargetOpcode::G_VECREDUCE_OR: 537 ScalarOpc = TargetOpcode::G_OR; 538 break; 539 case TargetOpcode::G_VECREDUCE_XOR: 540 ScalarOpc = TargetOpcode::G_XOR; 541 break; 542 case TargetOpcode::G_VECREDUCE_SMAX: 543 ScalarOpc = TargetOpcode::G_SMAX; 544 break; 545 case TargetOpcode::G_VECREDUCE_SMIN: 546 ScalarOpc = TargetOpcode::G_SMIN; 547 break; 548 case TargetOpcode::G_VECREDUCE_UMAX: 549 ScalarOpc = TargetOpcode::G_UMAX; 550 break; 551 case TargetOpcode::G_VECREDUCE_UMIN: 552 ScalarOpc = TargetOpcode::G_UMIN; 553 break; 554 default: 555 llvm_unreachable("Unhandled reduction"); 556 } 557 return ScalarOpc; 558 } 559 }; 560 561 562 } // namespace llvm 563 564 #endif // LLVM_CODEGEN_GLOBALISEL_GENERICMACHINEINSTRS_H 565