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 &regs[0]; }
endMachineRegClass152   [[nodiscard]] const MachineReg* end() const { return &regs[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