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