• 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 <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            // 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