• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 &regs[0]; }
endMachineRegClass151   [[nodiscard]] const MachineReg* end() const { return &regs[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