1 /* 2 * Copyright (C) 2023 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 // Machine IR public interface. 18 19 #ifndef BERBERIS_BACKEND_COMMON_MACHINE_IR_H_ 20 #define BERBERIS_BACKEND_COMMON_MACHINE_IR_H_ 21 22 #include <climits> // CHAR_BIT 23 #include <cstddef> 24 #include <cstdint> 25 #include <limits> 26 #include <string> 27 28 #include "berberis/backend/code_emitter.h" 29 #include "berberis/base/arena_alloc.h" 30 #include "berberis/base/arena_list.h" 31 #include "berberis/base/arena_vector.h" 32 #include "berberis/base/checks.h" 33 #include "berberis/guest_state/guest_addr.h" 34 35 namespace berberis { 36 37 // MachineReg is a machine instruction argument meaningful for optimizations and 38 // register allocation. It can be: 39 // - virtual register: [1024, +inf) 40 // - hard register: [1, 1024) 41 // - invalid/undefined: 0 42 // - (reserved): (-1024, -1] 43 // - spilled register: (-inf, -1024] 44 class MachineReg { 45 public: 46 // Creates an invalid machine register. MachineReg()47 constexpr MachineReg() : reg_{kInvalidMachineVRegNumber} {} MachineReg(int reg)48 constexpr explicit MachineReg(int reg) : reg_{reg} {} 49 constexpr MachineReg(const MachineReg&) = default; 50 constexpr MachineReg& operator=(const MachineReg&) = default; 51 52 constexpr MachineReg(MachineReg&&) = default; 53 constexpr MachineReg& operator=(MachineReg&&) = default; 54 reg()55 [[nodiscard]] constexpr int reg() const { return reg_; } 56 IsSpilledReg()57 [[nodiscard]] constexpr bool IsSpilledReg() const { return reg_ <= kLastSpilledRegNumber; } 58 IsHardReg()59 [[nodiscard]] constexpr bool IsHardReg() const { 60 return reg_ > kInvalidMachineVRegNumber && reg_ < kFirstVRegNumber; 61 } 62 IsInvalidReg()63 [[nodiscard]] constexpr bool IsInvalidReg() const { return reg_ == kInvalidMachineVRegNumber; } 64 IsVReg()65 [[nodiscard]] constexpr bool IsVReg() const { return reg_ >= kFirstVRegNumber; } 66 GetVRegIndex()67 [[nodiscard]] constexpr uint32_t GetVRegIndex() const { 68 CHECK_GE(reg_, kFirstVRegNumber); 69 return reg_ - kFirstVRegNumber; 70 } 71 GetSpilledRegIndex()72 [[nodiscard]] constexpr uint32_t GetSpilledRegIndex() const { 73 CHECK_LE(reg_, kLastSpilledRegNumber); 74 return kLastSpilledRegNumber - reg_; 75 } 76 77 constexpr friend bool operator==(MachineReg left, MachineReg right) { 78 return left.reg_ == right.reg_; 79 } 80 81 constexpr friend bool operator!=(MachineReg left, MachineReg right) { return !(left == right); } 82 CreateVRegFromIndex(uint32_t index)83 [[nodiscard]] static constexpr MachineReg CreateVRegFromIndex(uint32_t index) { 84 CHECK_LE(index, std::numeric_limits<int>::max() - kFirstVRegNumber); 85 return MachineReg{kFirstVRegNumber + static_cast<int>(index)}; 86 } 87 CreateSpilledRegFromIndex(uint32_t index)88 [[nodiscard]] static constexpr MachineReg CreateSpilledRegFromIndex(uint32_t index) { 89 CHECK_LE(index, -(std::numeric_limits<int>::min() - kLastSpilledRegNumber)); 90 return MachineReg{kLastSpilledRegNumber - static_cast<int>(index)}; 91 } 92 GetFirstVRegNumberForTesting()93 [[nodiscard]] static constexpr int GetFirstVRegNumberForTesting() { return kFirstVRegNumber; } 94 GetLastSpilledRegNumberForTesting()95 [[nodiscard]] static constexpr int GetLastSpilledRegNumberForTesting() { 96 return kLastSpilledRegNumber; 97 } 98 99 private: 100 static constexpr int kFirstVRegNumber = 1024; 101 static constexpr int kInvalidMachineVRegNumber = 0; 102 static constexpr int kLastSpilledRegNumber = -1024; 103 104 int reg_; 105 }; 106 107 constexpr MachineReg kInvalidMachineReg{0}; 108 109 [[nodiscard]] const char* GetMachineHardRegDebugName(MachineReg r); 110 [[nodiscard]] std::string GetMachineRegDebugString(MachineReg r); 111 112 using MachineRegVector = ArenaVector<MachineReg>; 113 114 // Set of registers, ordered by allocation preference. 115 // This is a struct to avoid static initializers. 116 // TODO(b/232598137) See if there's a way to use a class here. const array init 117 // (regs member) in constexpr context is the main challenge. 118 struct MachineRegClass { 119 const char* debug_name; 120 int reg_size; 121 uint64_t reg_mask; 122 int num_regs; 123 const MachineReg regs[sizeof(reg_mask) * CHAR_BIT]; 124 RegSizeMachineRegClass125 [[nodiscard]] constexpr int RegSize() const { return reg_size; } 126 HasRegMachineRegClass127 [[nodiscard]] bool HasReg(MachineReg r) const { return reg_mask & (uint64_t{1} << r.reg()); } 128 IsSubsetOfMachineRegClass129 [[nodiscard]] bool IsSubsetOf(const MachineRegClass* other) const { 130 return (reg_mask & other->reg_mask) == reg_mask; 131 } 132 GetIntersectionMachineRegClass133 [[nodiscard]] const MachineRegClass* GetIntersection(const MachineRegClass* other) const { 134 // At the moment, only handle the case when one class is a subset of other. 135 // In most real-life cases reg classes form a tree, so this is good enough. 136 auto mask = reg_mask & other->reg_mask; 137 if (mask == reg_mask) { 138 return this; 139 } 140 if (mask == other->reg_mask) { 141 return other; 142 } 143 return nullptr; 144 } 145 NumRegsMachineRegClass146 [[nodiscard]] constexpr int NumRegs() const { return num_regs; } 147 RegAtMachineRegClass148 [[nodiscard]] MachineReg RegAt(int i) const { return regs[i]; } 149 beginMachineRegClass150 [[nodiscard]] const MachineReg* begin() const { return ®s[0]; } endMachineRegClass151 [[nodiscard]] const MachineReg* end() const { return ®s[num_regs]; } 152 GetDebugNameMachineRegClass153 [[nodiscard]] const char* GetDebugName() const { return debug_name; } 154 }; 155 156 class MachineRegKind { 157 private: 158 enum { kRegisterIsUsed = 0x01, kRegisterIsDefined = 0x02, kRegisterIsInput = 0x04 }; 159 160 public: 161 enum StandardAccess { 162 kUse = kRegisterIsUsed | kRegisterIsInput, 163 kDef = kRegisterIsDefined, 164 kUseDef = kUse | kDef, 165 // Note: in kDefEarlyClobber, register is Used and Defined, but it's not an input! 166 kDefEarlyClobber = kRegisterIsUsed | kRegisterIsDefined 167 }; 168 169 // We need default constructor to initialize arrays MachineRegKind()170 constexpr MachineRegKind() : reg_class_(nullptr), access_(StandardAccess(0)) {} MachineRegKind(const MachineRegClass * reg_class,StandardAccess access)171 constexpr MachineRegKind(const MachineRegClass* reg_class, StandardAccess access) 172 : reg_class_(reg_class), access_(access) {} 173 RegClass()174 [[nodiscard]] constexpr const MachineRegClass* RegClass() const { return reg_class_; } 175 IsUse()176 [[nodiscard]] constexpr bool IsUse() const { return access_ & kRegisterIsUsed; } 177 IsDef()178 [[nodiscard]] constexpr bool IsDef() const { return access_ & kRegisterIsDefined; } 179 180 // IsInput means that register must contain some kind of valid value and is not just used early. 181 // This allows us to distinguish between UseDef and DefEarlyClobber. IsInput()182 [[nodiscard]] constexpr bool IsInput() const { return access_ & kRegisterIsInput; } 183 184 private: 185 const MachineRegClass* reg_class_; 186 enum StandardAccess access_; 187 }; 188 189 class MachineBasicBlock; 190 191 // Machine insn kind meaningful for optimizations and register allocation. 192 enum MachineInsnKind { 193 kMachineInsnDefault = 0, 194 kMachineInsnSideEffects, // never dead 195 kMachineInsnCopy, // can be deleted if dst == src 196 }; 197 198 enum MachineOpcode : int; 199 200 class MachineInsn { 201 public: ~MachineInsn()202 virtual ~MachineInsn() { 203 // No code here - will never be called! 204 } 205 206 [[nodiscard]] virtual std::string GetDebugString() const = 0; 207 virtual void Emit(CodeEmitter* as) const = 0; 208 opcode()209 [[nodiscard]] MachineOpcode opcode() const { return opcode_; }; 210 NumRegOperands()211 [[nodiscard]] int NumRegOperands() const { return num_reg_operands_; } 212 RegKindAt(int i)213 [[nodiscard]] const MachineRegKind& RegKindAt(int i) const { return reg_kinds_[i]; } 214 RegAt(int i)215 [[nodiscard]] MachineReg RegAt(int i) const { 216 CHECK_LT(i, num_reg_operands_); 217 return regs_[i]; 218 } 219 SetRegAt(int i,MachineReg reg)220 void SetRegAt(int i, MachineReg reg) { 221 CHECK_LT(i, num_reg_operands_); 222 regs_[i] = reg; 223 } 224 has_side_effects()225 [[nodiscard]] bool has_side_effects() const { 226 return (kind_ == kMachineInsnSideEffects) || recovery_info_.bb || 227 (recovery_info_.pc != kNullGuestAddr); 228 } 229 is_copy()230 [[nodiscard]] bool is_copy() const { return kind_ == kMachineInsnCopy; } 231 recovery_bb()232 [[nodiscard]] const MachineBasicBlock* recovery_bb() const { return recovery_info_.bb; } 233 set_recovery_bb(const MachineBasicBlock * bb)234 void set_recovery_bb(const MachineBasicBlock* bb) { recovery_info_.bb = bb; } 235 recovery_pc()236 [[nodiscard]] GuestAddr recovery_pc() const { return recovery_info_.pc; } 237 set_recovery_pc(GuestAddr pc)238 void set_recovery_pc(GuestAddr pc) { recovery_info_.pc = pc; } 239 240 protected: MachineInsn(MachineOpcode opcode,int num_reg_operands,const MachineRegKind * reg_kinds,MachineReg * regs,MachineInsnKind kind)241 MachineInsn(MachineOpcode opcode, 242 int num_reg_operands, 243 const MachineRegKind* reg_kinds, 244 MachineReg* regs, 245 MachineInsnKind kind) 246 : opcode_(opcode), 247 num_reg_operands_(num_reg_operands), 248 reg_kinds_(reg_kinds), 249 regs_(regs), 250 kind_(kind), 251 recovery_info_{nullptr, kNullGuestAddr} {} 252 253 private: 254 // We either recover by building explicit recovery blocks or by storing recovery pc. 255 // TODO(b/200327919): Convert this to union? We'll need to know which one is used during 256 // initialization and in has_side_effects. 257 struct RecoveryInfo { 258 const MachineBasicBlock* bb; 259 GuestAddr pc; 260 }; 261 const MachineOpcode opcode_; 262 const int num_reg_operands_; 263 const MachineRegKind* reg_kinds_; 264 MachineReg* regs_; 265 MachineInsnKind kind_; 266 RecoveryInfo recovery_info_; 267 }; 268 269 std::string GetRegOperandDebugString(const MachineInsn* insn, int i); 270 271 using MachineInsnList = ArenaList<MachineInsn*>; 272 273 class MachineInsnListPosition { 274 public: MachineInsnListPosition(MachineInsnList * list,MachineInsnList::iterator iterator)275 MachineInsnListPosition(MachineInsnList* list, MachineInsnList::iterator iterator) 276 : list_(list), iterator_(iterator) {} 277 insn()278 [[nodiscard]] MachineInsn* insn() const { return *iterator_; } 279 InsertBefore(MachineInsn * insn)280 void InsertBefore(MachineInsn* insn) const { list_->insert(iterator_, insn); } 281 InsertAfter(MachineInsn * insn)282 void InsertAfter(MachineInsn* insn) const { 283 MachineInsnList::iterator next_iterator = iterator_; 284 list_->insert(++next_iterator, insn); 285 } 286 287 private: 288 MachineInsnList* list_; 289 const MachineInsnList::iterator iterator_; 290 }; 291 292 class MachineEdge { 293 public: MachineEdge(Arena * arena,MachineBasicBlock * src,MachineBasicBlock * dst)294 MachineEdge(Arena* arena, MachineBasicBlock* src, MachineBasicBlock* dst) 295 : src_(src), dst_(dst), insn_list_(arena) {} 296 set_src(MachineBasicBlock * bb)297 void set_src(MachineBasicBlock* bb) { src_ = bb; } set_dst(MachineBasicBlock * bb)298 void set_dst(MachineBasicBlock* bb) { dst_ = bb; } 299 src()300 [[nodiscard]] MachineBasicBlock* src() const { return src_; } dst()301 [[nodiscard]] MachineBasicBlock* dst() const { return dst_; } 302 insn_list()303 [[nodiscard]] const MachineInsnList& insn_list() const { return insn_list_; } insn_list()304 [[nodiscard]] MachineInsnList& insn_list() { return insn_list_; } 305 306 private: 307 MachineBasicBlock* src_; 308 MachineBasicBlock* dst_; 309 MachineInsnList insn_list_; 310 }; 311 312 using MachineEdgeVector = ArenaVector<MachineEdge*>; 313 314 class MachineBasicBlock { 315 public: MachineBasicBlock(Arena * arena,uint32_t id)316 MachineBasicBlock(Arena* arena, uint32_t id) 317 : id_(id), 318 insn_list_(arena), 319 in_edges_(arena), 320 out_edges_(arena), 321 live_in_(arena), 322 live_out_(arena), 323 is_recovery_(false) {} 324 id()325 [[nodiscard]] uint32_t id() const { return id_; } 326 insn_list()327 [[nodiscard]] const MachineInsnList& insn_list() const { return insn_list_; } insn_list()328 [[nodiscard]] MachineInsnList& insn_list() { return insn_list_; } 329 in_edges()330 [[nodiscard]] const MachineEdgeVector& in_edges() const { return in_edges_; } in_edges()331 [[nodiscard]] MachineEdgeVector& in_edges() { return in_edges_; } 332 out_edges()333 [[nodiscard]] const MachineEdgeVector& out_edges() const { return out_edges_; } out_edges()334 [[nodiscard]] MachineEdgeVector& out_edges() { return out_edges_; } 335 live_in()336 [[nodiscard]] const MachineRegVector& live_in() const { return live_in_; } live_in()337 [[nodiscard]] MachineRegVector& live_in() { return live_in_; } 338 live_out()339 [[nodiscard]] const MachineRegVector& live_out() const { return live_out_; } live_out()340 [[nodiscard]] MachineRegVector& live_out() { return live_out_; } 341 MarkAsRecovery()342 void MarkAsRecovery() { is_recovery_ = true; } 343 is_recovery()344 [[nodiscard]] bool is_recovery() const { return is_recovery_; } 345 346 [[nodiscard]] std::string GetDebugString() const; 347 348 private: 349 const uint32_t id_; 350 MachineInsnList insn_list_; 351 MachineEdgeVector in_edges_; 352 MachineEdgeVector out_edges_; 353 MachineRegVector live_in_; 354 MachineRegVector live_out_; 355 bool is_recovery_; 356 }; 357 358 using MachineBasicBlockList = ArenaList<MachineBasicBlock*>; 359 360 class MachineIR { 361 public: 362 // First num_vreg virtual register numbers are reserved for custom use 363 // in the derived class, numbers above that can be used for scratches. MachineIR(Arena * arena,int num_vreg,uint32_t num_bb)364 MachineIR(Arena* arena, int num_vreg, uint32_t num_bb) 365 : num_bb_(num_bb), 366 arena_(arena), 367 num_vreg_(num_vreg), 368 num_arg_slots_(0), 369 num_spill_slots_(0), 370 bb_list_(arena) {} 371 NumVReg()372 [[nodiscard]] int NumVReg() const { return num_vreg_; } 373 AllocVReg()374 [[nodiscard]] MachineReg AllocVReg() { return MachineReg::CreateVRegFromIndex(num_vreg_++); } 375 ReserveBasicBlockId()376 [[nodiscard]] uint32_t ReserveBasicBlockId() { return num_bb_++; } 377 378 // Stack frame layout is: 379 // [arg slots][spill slots] 380 // ^--- stack pointer 381 // 382 // Arg slots are for stack frame part that require a fixed offset from the 383 // stack pointer, in particular for call arguments passed on the stack. 384 // Spill slots are for spilled registers. 385 // Each slot is 16-bytes, and the stack pointer is always 16-bytes aligned. 386 // 387 // TODO(b/232598137): If we need a custom stack layout for an architecture, 388 // implement the following functions specifically for each architecture. 389 ReserveArgs(uint32_t size)390 void ReserveArgs(uint32_t size) { 391 uint32_t slots = (size + 15) / 16; 392 if (num_arg_slots_ < slots) { 393 num_arg_slots_ = slots; 394 } 395 } 396 AllocSpill()397 [[nodiscard]] uint32_t AllocSpill() { return num_spill_slots_++; } 398 SpillSlotOffset(uint32_t slot)399 [[nodiscard]] uint32_t SpillSlotOffset(uint32_t slot) const { 400 return 16 * (num_arg_slots_ + slot); 401 } 402 FrameSize()403 [[nodiscard]] uint32_t FrameSize() const { return 16 * (num_arg_slots_ + num_spill_slots_); } 404 NumBasicBlocks()405 [[nodiscard]] size_t NumBasicBlocks() const { return num_bb_; } 406 bb_list()407 [[nodiscard]] const MachineBasicBlockList& bb_list() const { return bb_list_; } 408 bb_list()409 [[nodiscard]] MachineBasicBlockList& bb_list() { return bb_list_; } 410 411 [[nodiscard]] std::string GetDebugString() const; 412 413 [[nodiscard]] std::string GetDebugStringForDot() const; 414 415 void Emit(CodeEmitter* as) const; 416 arena()417 [[nodiscard]] Arena* arena() const { return arena_; } 418 419 template <typename T, typename... Args> NewInsn(Args...args)420 [[nodiscard]] T* NewInsn(Args... args) { 421 return NewInArena<T>(arena(), args...); 422 } 423 424 private: 425 // Basic block number is useful when allocating analytical data 426 // structures indexed by IDs. Note that the return value of this function is 427 // not necessarily equal to bb_list().size() since some basic blocks may not 428 // be enrolled in this list. 429 // This can be set in ctor or managed in the derived class. It's the derived 430 // class's responsibility to guarantee that max basic block ID is less than 431 // this number. 432 uint32_t num_bb_; 433 434 private: 435 Arena* const arena_; 436 int num_vreg_; 437 uint32_t num_arg_slots_; // 16-byte slots for call args/results 438 uint32_t num_spill_slots_; // 16-byte slots for spilled registers 439 MachineBasicBlockList bb_list_; 440 }; 441 442 class PseudoBranch : public MachineInsn { 443 public: 444 static const MachineOpcode kOpcode; 445 446 explicit PseudoBranch(const MachineBasicBlock* then_bb); 447 448 std::string GetDebugString() const override; 449 void Emit(CodeEmitter* as) const override; 450 then_bb()451 const MachineBasicBlock* then_bb() const { return then_bb_; } set_then_bb(const MachineBasicBlock * then_bb)452 void set_then_bb(const MachineBasicBlock* then_bb) { then_bb_ = then_bb; } 453 454 private: 455 const MachineBasicBlock* then_bb_; 456 }; 457 458 class PseudoCondBranch : public MachineInsn { 459 public: 460 static const MachineOpcode kOpcode; 461 462 PseudoCondBranch(CodeEmitter::Condition cond, 463 const MachineBasicBlock* then_bb, 464 const MachineBasicBlock* else_bb, 465 MachineReg eflags); 466 467 std::string GetDebugString() const override; 468 void Emit(CodeEmitter* as) const override; 469 cond()470 CodeEmitter::Condition cond() const { return cond_; } set_cond(CodeEmitter::Condition cond)471 void set_cond(CodeEmitter::Condition cond) { cond_ = cond; } then_bb()472 const MachineBasicBlock* then_bb() const { return then_bb_; } else_bb()473 const MachineBasicBlock* else_bb() const { return else_bb_; } set_then_bb(const MachineBasicBlock * then_bb)474 void set_then_bb(const MachineBasicBlock* then_bb) { then_bb_ = then_bb; } set_else_bb(const MachineBasicBlock * else_bb)475 void set_else_bb(const MachineBasicBlock* else_bb) { else_bb_ = else_bb; } eflags()476 MachineReg eflags() const { return eflags_; } 477 478 private: 479 CodeEmitter::Condition cond_; 480 const MachineBasicBlock* then_bb_; 481 const MachineBasicBlock* else_bb_; 482 MachineReg eflags_; 483 }; 484 485 class PseudoJump : public MachineInsn { 486 public: 487 enum class Kind { 488 kJumpWithPendingSignalsCheck, 489 kJumpWithoutPendingSignalsCheck, 490 kExitGeneratedCode, 491 kSyscall, 492 }; 493 494 PseudoJump(GuestAddr target, Kind kind = Kind::kJumpWithPendingSignalsCheck); 495 496 std::string GetDebugString() const override; 497 void Emit(CodeEmitter* as) const override; 498 target()499 GuestAddr target() const { return target_; } kind()500 Kind kind() const { return kind_; } 501 502 private: 503 GuestAddr target_; 504 Kind kind_; 505 }; 506 507 class PseudoIndirectJump : public MachineInsn { 508 public: 509 explicit PseudoIndirectJump(MachineReg src); 510 511 [[nodiscard]] std::string GetDebugString() const override; 512 void Emit(CodeEmitter* as) const override; 513 514 private: 515 MachineReg src_; 516 }; 517 518 // Copy the value of given size between registers/memory. 519 // Register class of operands is anything capable of keeping values of this 520 // size. 521 // ATTENTION: this insn has operands with variable register class! 522 class PseudoCopy : public MachineInsn { 523 public: 524 static const MachineOpcode kOpcode; 525 526 PseudoCopy(MachineReg dst, MachineReg src, int size); 527 528 std::string GetDebugString() const override; 529 void Emit(CodeEmitter* as) const override; 530 531 private: 532 MachineReg regs_[2]; 533 }; 534 535 // Some instructions have use-def operands, but for the semantics of our IR are really def-only, 536 // so we use this auxiliary instruction to ensure data-flow is integral (required by some phases 537 // including register allocation), but we do not emit it. 538 // 539 // Example: PmovsxwdXRegXReg followed by MovlhpsXRegXReg 540 // Example: xor rax, rax 541 class PseudoDefXReg : public MachineInsn { 542 public: 543 explicit PseudoDefXReg(MachineReg reg); 544 545 [[nodiscard]] std::string GetDebugString() const override; Emit(CodeEmitter *)546 void Emit(CodeEmitter* /*as*/) const override { 547 // It's an auxiliary instruction. Does not emit. 548 } 549 550 private: 551 MachineReg reg_; 552 }; 553 554 class PseudoDefReg : public MachineInsn { 555 public: 556 explicit PseudoDefReg(MachineReg reg); 557 558 [[nodiscard]] std::string GetDebugString() const override; Emit(CodeEmitter *)559 void Emit(CodeEmitter* /*as*/) const override { 560 // It's an auxiliary instruction. Does not emit. 561 } 562 563 private: 564 MachineReg reg_; 565 }; 566 567 class PseudoReadFlags : public MachineInsn { 568 public: 569 static const MachineOpcode kOpcode; 570 571 // Syntax sugar to avoid anonymous bool during construction on caller side. 572 enum WithOverflowEnum { kWithOverflow, kWithoutOverflow }; 573 574 // Flags in LAHF-compatible format. 575 enum Flags : uint16_t { 576 kNegative = 1 << 15, 577 kZero = 1 << 14, 578 kCarry = 1 << 8, 579 kOverflow = 1, 580 }; 581 582 PseudoReadFlags(WithOverflowEnum with_overflow, MachineReg dst, MachineReg flags); 583 584 std::string GetDebugString() const override; 585 void Emit(CodeEmitter* as) const override; 586 with_overflow()587 bool with_overflow() const { return with_overflow_; }; 588 589 private: 590 MachineReg regs_[2]; 591 bool with_overflow_; 592 }; 593 594 class PseudoWriteFlags : public MachineInsn { 595 public: 596 static const MachineOpcode kOpcode; 597 598 using Flags = PseudoReadFlags::Flags; 599 600 PseudoWriteFlags(MachineReg src, MachineReg flags); 601 602 std::string GetDebugString() const override; 603 void Emit(CodeEmitter* as) const override; 604 605 private: 606 MachineReg regs_[2]; 607 }; 608 609 } // namespace berberis 610 611 #endif // BERBERIS_BACKEND_COMMON_MACHINE_IR_H_ 612