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 } 230 is_copy()231 [[nodiscard]] bool is_copy() const { return kind_ == kMachineInsnCopy; } 232 recovery_bb()233 [[nodiscard]] const MachineBasicBlock* recovery_bb() const { return recovery_info_.bb; } 234 set_recovery_bb(const MachineBasicBlock * bb)235 void set_recovery_bb(const MachineBasicBlock* bb) { recovery_info_.bb = bb; } 236 recovery_pc()237 [[nodiscard]] GuestAddr recovery_pc() const { return recovery_info_.pc; } 238 set_recovery_pc(GuestAddr pc)239 void set_recovery_pc(GuestAddr pc) { recovery_info_.pc = pc; } 240 241 protected: MachineInsn(MachineOpcode opcode,int num_reg_operands,const MachineRegKind * reg_kinds,MachineReg * regs,MachineInsnKind kind)242 MachineInsn(MachineOpcode opcode, 243 int num_reg_operands, 244 const MachineRegKind* reg_kinds, 245 MachineReg* regs, 246 MachineInsnKind kind) 247 : opcode_(opcode), 248 num_reg_operands_(num_reg_operands), 249 reg_kinds_(reg_kinds), 250 regs_(regs), 251 kind_(kind), 252 recovery_info_{nullptr, kNullGuestAddr} {} 253 254 private: 255 // We either recover by building explicit recovery blocks or by storing recovery pc. 256 // TODO(b/200327919): Convert this to union? We'll need to know which one is used during 257 // initialization and in has_side_effects. 258 struct RecoveryInfo { 259 const MachineBasicBlock* bb; 260 GuestAddr pc; 261 }; 262 const MachineOpcode opcode_; 263 const int num_reg_operands_; 264 const MachineRegKind* reg_kinds_; 265 MachineReg* regs_; 266 MachineInsnKind kind_; 267 RecoveryInfo recovery_info_; 268 }; 269 270 std::string GetRegOperandDebugString(const MachineInsn* insn, int i); 271 272 using MachineInsnList = ArenaList<MachineInsn*>; 273 274 class MachineInsnListPosition { 275 public: MachineInsnListPosition(MachineInsnList * list,MachineInsnList::iterator iterator)276 MachineInsnListPosition(MachineInsnList* list, MachineInsnList::iterator iterator) 277 : list_(list), iterator_(iterator) {} 278 insn()279 [[nodiscard]] MachineInsn* insn() const { return *iterator_; } 280 InsertBefore(MachineInsn * insn)281 void InsertBefore(MachineInsn* insn) const { list_->insert(iterator_, insn); } 282 InsertAfter(MachineInsn * insn)283 void InsertAfter(MachineInsn* insn) const { 284 MachineInsnList::iterator next_iterator = iterator_; 285 list_->insert(++next_iterator, insn); 286 } 287 288 private: 289 MachineInsnList* list_; 290 const MachineInsnList::iterator iterator_; 291 }; 292 293 class MachineEdge { 294 public: MachineEdge(Arena * arena,MachineBasicBlock * src,MachineBasicBlock * dst)295 MachineEdge(Arena* arena, MachineBasicBlock* src, MachineBasicBlock* dst) 296 : src_(src), dst_(dst), insn_list_(arena) {} 297 set_src(MachineBasicBlock * bb)298 void set_src(MachineBasicBlock* bb) { src_ = bb; } set_dst(MachineBasicBlock * bb)299 void set_dst(MachineBasicBlock* bb) { dst_ = bb; } 300 src()301 [[nodiscard]] MachineBasicBlock* src() const { return src_; } dst()302 [[nodiscard]] MachineBasicBlock* dst() const { return dst_; } 303 insn_list()304 [[nodiscard]] const MachineInsnList& insn_list() const { return insn_list_; } insn_list()305 [[nodiscard]] MachineInsnList& insn_list() { return insn_list_; } 306 307 private: 308 MachineBasicBlock* src_; 309 MachineBasicBlock* dst_; 310 MachineInsnList insn_list_; 311 }; 312 313 using MachineEdgeVector = ArenaVector<MachineEdge*>; 314 315 class MachineBasicBlock { 316 public: MachineBasicBlock(Arena * arena,uint32_t id)317 MachineBasicBlock(Arena* arena, uint32_t id) 318 : id_(id), 319 guest_addr_(kNullGuestAddr), 320 profile_counter_(0), 321 insn_list_(arena), 322 in_edges_(arena), 323 out_edges_(arena), 324 live_in_(arena), 325 live_out_(arena), 326 is_recovery_(false) {} 327 id()328 [[nodiscard]] uint32_t id() const { return id_; } 329 guest_addr()330 [[nodiscard]] GuestAddr guest_addr() const { return guest_addr_; } set_guest_addr(GuestAddr addr)331 void set_guest_addr(GuestAddr addr) { guest_addr_ = addr; } 332 profile_counter()333 [[nodiscard]] std::optional<uint32_t> profile_counter() const { return profile_counter_; } set_profile_counter(uint32_t counter)334 void set_profile_counter(uint32_t counter) { profile_counter_ = counter; } 335 insn_list()336 [[nodiscard]] const MachineInsnList& insn_list() const { return insn_list_; } insn_list()337 [[nodiscard]] MachineInsnList& insn_list() { return insn_list_; } 338 in_edges()339 [[nodiscard]] const MachineEdgeVector& in_edges() const { return in_edges_; } in_edges()340 [[nodiscard]] MachineEdgeVector& in_edges() { return in_edges_; } 341 out_edges()342 [[nodiscard]] const MachineEdgeVector& out_edges() const { return out_edges_; } out_edges()343 [[nodiscard]] MachineEdgeVector& out_edges() { return out_edges_; } 344 live_in()345 [[nodiscard]] const MachineRegVector& live_in() const { return live_in_; } live_in()346 [[nodiscard]] MachineRegVector& live_in() { return live_in_; } 347 live_out()348 [[nodiscard]] const MachineRegVector& live_out() const { return live_out_; } live_out()349 [[nodiscard]] MachineRegVector& live_out() { return live_out_; } 350 MarkAsRecovery()351 void MarkAsRecovery() { is_recovery_ = true; } 352 is_recovery()353 [[nodiscard]] bool is_recovery() const { return is_recovery_; } 354 355 [[nodiscard]] std::string GetDebugString() const; 356 357 private: 358 const uint32_t id_; 359 GuestAddr guest_addr_; 360 std::optional<uint32_t> profile_counter_; 361 MachineInsnList insn_list_; 362 MachineEdgeVector in_edges_; 363 MachineEdgeVector out_edges_; 364 MachineRegVector live_in_; 365 MachineRegVector live_out_; 366 bool is_recovery_; 367 }; 368 369 using MachineBasicBlockList = ArenaList<MachineBasicBlock*>; 370 371 class MachineIR { 372 public: 373 // First num_vreg virtual register numbers are reserved for custom use 374 // in the derived class, numbers above that can be used for scratches. MachineIR(Arena * arena,int num_vreg,uint32_t num_bb)375 MachineIR(Arena* arena, int num_vreg, uint32_t num_bb) 376 : num_bb_(num_bb), 377 arena_(arena), 378 num_vreg_(num_vreg), 379 num_arg_slots_(0), 380 num_spill_slots_(0), 381 bb_list_(arena) {} 382 NumVReg()383 [[nodiscard]] int NumVReg() const { return num_vreg_; } 384 AllocVReg()385 [[nodiscard]] MachineReg AllocVReg() { return MachineReg::CreateVRegFromIndex(num_vreg_++); } 386 ReserveBasicBlockId()387 [[nodiscard]] uint32_t ReserveBasicBlockId() { return num_bb_++; } 388 389 // Stack frame layout is: 390 // [arg slots][spill slots] 391 // ^--- stack pointer 392 // 393 // Arg slots are for stack frame part that require a fixed offset from the 394 // stack pointer, in particular for call arguments passed on the stack. 395 // Spill slots are for spilled registers. 396 // Each slot is 16-bytes, and the stack pointer is always 16-bytes aligned. 397 // 398 // TODO(b/232598137): If we need a custom stack layout for an architecture, 399 // implement the following functions specifically for each architecture. 400 ReserveArgs(uint32_t size)401 void ReserveArgs(uint32_t size) { 402 uint32_t slots = (size + 15) / 16; 403 if (num_arg_slots_ < slots) { 404 num_arg_slots_ = slots; 405 } 406 } 407 AllocSpill()408 [[nodiscard]] uint32_t AllocSpill() { return num_spill_slots_++; } 409 SpillSlotOffset(uint32_t slot)410 [[nodiscard]] uint32_t SpillSlotOffset(uint32_t slot) const { 411 return 16 * (num_arg_slots_ + slot); 412 } 413 FrameSize()414 [[nodiscard]] uint32_t FrameSize() const { return 16 * (num_arg_slots_ + num_spill_slots_); } 415 NumBasicBlocks()416 [[nodiscard]] size_t NumBasicBlocks() const { return num_bb_; } 417 bb_list()418 [[nodiscard]] const MachineBasicBlockList& bb_list() const { return bb_list_; } 419 bb_list()420 [[nodiscard]] MachineBasicBlockList& bb_list() { return bb_list_; } 421 422 [[nodiscard]] std::string GetDebugString() const; 423 424 [[nodiscard]] std::string GetDebugStringForDot() const; 425 426 void Emit(CodeEmitter* as) const; 427 arena()428 [[nodiscard]] Arena* arena() const { return arena_; } 429 430 template <typename T, typename... Args> NewInsn(Args...args)431 [[nodiscard]] T* NewInsn(Args... args) { 432 return NewInArena<T>(arena(), args...); 433 } 434 435 private: 436 // Basic block number is useful when allocating analytical data 437 // structures indexed by IDs. Note that the return value of this function is 438 // not necessarily equal to bb_list().size() since some basic blocks may not 439 // be enrolled in this list. 440 // This can be set in ctor or managed in the derived class. It's the derived 441 // class's responsibility to guarantee that max basic block ID is less than 442 // this number. 443 uint32_t num_bb_; 444 445 private: 446 Arena* const arena_; 447 int num_vreg_; 448 uint32_t num_arg_slots_; // 16-byte slots for call args/results 449 uint32_t num_spill_slots_; // 16-byte slots for spilled registers 450 MachineBasicBlockList bb_list_; 451 }; 452 453 class PseudoBranch : public MachineInsn { 454 public: 455 static const MachineOpcode kOpcode; 456 457 explicit PseudoBranch(const MachineBasicBlock* then_bb); 458 459 std::string GetDebugString() const override; 460 void Emit(CodeEmitter* as) const override; 461 then_bb()462 const MachineBasicBlock* then_bb() const { return then_bb_; } set_then_bb(const MachineBasicBlock * then_bb)463 void set_then_bb(const MachineBasicBlock* then_bb) { then_bb_ = then_bb; } 464 465 private: 466 const MachineBasicBlock* then_bb_; 467 }; 468 469 class PseudoCondBranch : public MachineInsn { 470 public: 471 static const MachineOpcode kOpcode; 472 473 PseudoCondBranch(CodeEmitter::Condition cond, 474 const MachineBasicBlock* then_bb, 475 const MachineBasicBlock* else_bb, 476 MachineReg eflags); 477 478 std::string GetDebugString() const override; 479 void Emit(CodeEmitter* as) const override; 480 cond()481 CodeEmitter::Condition cond() const { return cond_; } set_cond(CodeEmitter::Condition cond)482 void set_cond(CodeEmitter::Condition cond) { cond_ = cond; } then_bb()483 const MachineBasicBlock* then_bb() const { return then_bb_; } else_bb()484 const MachineBasicBlock* else_bb() const { return else_bb_; } set_then_bb(const MachineBasicBlock * then_bb)485 void set_then_bb(const MachineBasicBlock* then_bb) { then_bb_ = then_bb; } set_else_bb(const MachineBasicBlock * else_bb)486 void set_else_bb(const MachineBasicBlock* else_bb) { else_bb_ = else_bb; } eflags()487 MachineReg eflags() const { return eflags_; } 488 489 private: 490 CodeEmitter::Condition cond_; 491 const MachineBasicBlock* then_bb_; 492 const MachineBasicBlock* else_bb_; 493 MachineReg eflags_; 494 }; 495 496 class PseudoJump : public MachineInsn { 497 public: 498 enum class Kind { 499 kJumpWithPendingSignalsCheck, 500 kJumpWithoutPendingSignalsCheck, 501 kExitGeneratedCode, 502 kSyscall, 503 }; 504 505 PseudoJump(GuestAddr target, Kind kind = Kind::kJumpWithPendingSignalsCheck); 506 507 std::string GetDebugString() const override; 508 void Emit(CodeEmitter* as) const override; 509 target()510 GuestAddr target() const { return target_; } kind()511 Kind kind() const { return kind_; } 512 513 private: 514 GuestAddr target_; 515 Kind kind_; 516 }; 517 518 class PseudoIndirectJump : public MachineInsn { 519 public: 520 explicit PseudoIndirectJump(MachineReg src); 521 522 [[nodiscard]] std::string GetDebugString() const override; 523 void Emit(CodeEmitter* as) const override; 524 525 private: 526 MachineReg src_; 527 }; 528 529 // Copy the value of given size between registers/memory. 530 // Register class of operands is anything capable of keeping values of this 531 // size. 532 // ATTENTION: this insn has operands with variable register class! 533 class PseudoCopy : public MachineInsn { 534 public: 535 static const MachineOpcode kOpcode; 536 537 PseudoCopy(MachineReg dst, MachineReg src, int size); 538 539 std::string GetDebugString() const override; 540 void Emit(CodeEmitter* as) const override; 541 542 private: 543 MachineReg regs_[2]; 544 }; 545 546 // Some instructions have use-def operands, but for the semantics of our IR are really def-only, 547 // so we use this auxiliary instruction to ensure data-flow is integral (required by some phases 548 // including register allocation), but we do not emit it. 549 // 550 // Example: PmovsxwdXRegXReg followed by MovlhpsXRegXReg 551 // Example: xor rax, rax 552 class PseudoDefXReg : public MachineInsn { 553 public: 554 explicit PseudoDefXReg(MachineReg reg); 555 556 [[nodiscard]] std::string GetDebugString() const override; Emit(CodeEmitter *)557 void Emit(CodeEmitter* /*as*/) const override { 558 // It's an auxiliary instruction. Does not emit. 559 } 560 561 private: 562 MachineReg reg_; 563 }; 564 565 class PseudoDefReg : public MachineInsn { 566 public: 567 explicit PseudoDefReg(MachineReg reg); 568 569 [[nodiscard]] std::string GetDebugString() const override; Emit(CodeEmitter *)570 void Emit(CodeEmitter* /*as*/) const override { 571 // It's an auxiliary instruction. Does not emit. 572 } 573 574 private: 575 MachineReg reg_; 576 }; 577 578 class PseudoReadFlags : public MachineInsn { 579 public: 580 static const MachineOpcode kOpcode; 581 582 // Syntax sugar to avoid anonymous bool during construction on caller side. 583 enum WithOverflowEnum { kWithOverflow, kWithoutOverflow }; 584 585 // Flags in LAHF-compatible format. 586 enum Flags : uint16_t { 587 kNegative = 1 << 15, 588 kZero = 1 << 14, 589 kCarry = 1 << 8, 590 kOverflow = 1, 591 }; 592 593 PseudoReadFlags(WithOverflowEnum with_overflow, MachineReg dst, MachineReg flags); 594 595 std::string GetDebugString() const override; 596 void Emit(CodeEmitter* as) const override; 597 with_overflow()598 bool with_overflow() const { return with_overflow_; }; 599 600 private: 601 MachineReg regs_[2]; 602 bool with_overflow_; 603 }; 604 605 class PseudoWriteFlags : public MachineInsn { 606 public: 607 static const MachineOpcode kOpcode; 608 609 using Flags = PseudoReadFlags::Flags; 610 611 PseudoWriteFlags(MachineReg src, MachineReg flags); 612 613 std::string GetDebugString() const override; 614 void Emit(CodeEmitter* as) const override; 615 616 private: 617 MachineReg regs_[2]; 618 }; 619 620 } // namespace berberis 621 622 #endif // BERBERIS_BACKEND_COMMON_MACHINE_IR_H_ 623