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 // x86_64 machine IR interface.
18
19 #ifndef BERBERIS_BACKEND_X86_64_MACHINE_IR_H_
20 #define BERBERIS_BACKEND_X86_64_MACHINE_IR_H_
21
22 #include <cstdint>
23 #include <string>
24
25 #include "berberis/assembler/x86_64.h"
26 #include "berberis/backend/code_emitter.h"
27 #include "berberis/backend/common/machine_ir.h"
28 #include "berberis/base/arena_alloc.h"
29 #include "berberis/guest_state/guest_state_arch.h"
30
31 namespace berberis {
32
33 enum MachineOpcode : int {
34 kMachineOpUndefined = 0,
35 kMachineOpCallImm,
36 kMachineOpCallImmArg,
37 kMachineOpPseudoBranch,
38 kMachineOpPseudoCondBranch,
39 kMachineOpPseudoCopy,
40 kMachineOpPseudoDefReg,
41 kMachineOpPseudoDefXReg,
42 kMachineOpPseudoIndirectJump,
43 kMachineOpPseudoJump,
44 kMachineOpPseudoReadFlags,
45 kMachineOpPseudoWriteFlags,
46 // Some frontends may need additional opcodes currently.
47 // Ideally we may want to separate froentend and backend, but for now only include
48 // berberis/backend/x86_64/machine_opcode_guest-inl.h if it exists.
49 #if __has_include("berberis/backend/x86_64/machine_opcode_guest-inl.h")
50 #include "berberis/backend/x86_64/machine_opcode_guest-inl.h"
51 #endif // __has_include("berberis/backend/x86_64/machine_opcode_guest-inl.h")
52 #include "machine_opcode_x86_64-inl.h" // NOLINT generated file!
53 };
54
55 namespace x86_64 {
56
57 constexpr const MachineReg kMachineRegR8{1};
58 constexpr const MachineReg kMachineRegR9{2};
59 constexpr const MachineReg kMachineRegR10{3};
60 constexpr const MachineReg kMachineRegR11{4};
61 constexpr const MachineReg kMachineRegRSI{5};
62 constexpr const MachineReg kMachineRegRDI{6};
63 constexpr const MachineReg kMachineRegRAX{7};
64 constexpr const MachineReg kMachineRegRBX{8};
65 constexpr const MachineReg kMachineRegRCX{9};
66 constexpr const MachineReg kMachineRegRDX{10};
67 constexpr const MachineReg kMachineRegRBP{11};
68 constexpr const MachineReg kMachineRegRSP{12};
69 constexpr const MachineReg kMachineRegR12{13};
70 constexpr const MachineReg kMachineRegR13{14};
71 constexpr const MachineReg kMachineRegR14{15};
72 constexpr const MachineReg kMachineRegR15{16};
73 constexpr const MachineReg kMachineRegFLAGS{19};
74 constexpr const MachineReg kMachineRegXMM0{20};
75 constexpr const MachineReg kMachineRegXMM1{21};
76 constexpr const MachineReg kMachineRegXMM2{22};
77 constexpr const MachineReg kMachineRegXMM3{23};
78 constexpr const MachineReg kMachineRegXMM4{24};
79 constexpr const MachineReg kMachineRegXMM5{25};
80 constexpr const MachineReg kMachineRegXMM6{26};
81 constexpr const MachineReg kMachineRegXMM7{27};
82 constexpr const MachineReg kMachineRegXMM8{28};
83 constexpr const MachineReg kMachineRegXMM9{29};
84 constexpr const MachineReg kMachineRegXMM10{30};
85 constexpr const MachineReg kMachineRegXMM11{31};
86 constexpr const MachineReg kMachineRegXMM12{32};
87 constexpr const MachineReg kMachineRegXMM13{33};
88 constexpr const MachineReg kMachineRegXMM14{34};
89 constexpr const MachineReg kMachineRegXMM15{35};
90
IsGReg(MachineReg r)91 inline bool IsGReg(MachineReg r) {
92 return r.reg() >= kMachineRegR8.reg() && r.reg() <= kMachineRegR15.reg();
93 }
94
IsXReg(MachineReg r)95 inline bool IsXReg(MachineReg r) {
96 return r.reg() >= kMachineRegXMM0.reg() && r.reg() <= kMachineRegXMM15.reg();
97 }
98
99 // rax, rdi, rsi, rdx, rcx, r8-r11, xmm0-xmm15, flags
100 const int kMaxMachineRegOperands = 26;
101
102 // Context loads and stores use rbp as base.
103 const MachineReg kCPUStatePointer = kMachineRegRBP;
104
105 struct MachineInsnInfo {
106 MachineOpcode opcode;
107 int num_reg_operands;
108 MachineRegKind reg_kinds[kMaxMachineRegOperands];
109 MachineInsnKind kind;
110 };
111
112 enum class MachineMemOperandScale {
113 kOne,
114 kTwo,
115 kFour,
116 kEight,
117 };
118
119 #include "machine_reg_class_x86_64-inl.h" // NOLINT generated file!
120
121 class MachineInsnX86_64 : public MachineInsn {
122 public:
123 static constexpr const auto kEAX = x86_64::kEAX;
124 static constexpr const auto kRAX = x86_64::kRAX;
125 static constexpr const auto kAL = x86_64::kAL;
126 static constexpr const auto kCL = x86_64::kCL;
127 static constexpr const auto kECX = x86_64::kECX;
128 static constexpr const auto kRCX = x86_64::kRCX;
129 static constexpr const auto kEDX = x86_64::kEDX;
130 static constexpr const auto kRDX = x86_64::kRDX;
131 static constexpr const auto kGeneralReg8 = x86_64::kGeneralReg8;
132 static constexpr const auto kGeneralReg16 = x86_64::kGeneralReg16;
133 static constexpr const auto kGeneralReg32 = x86_64::kGeneralReg32;
134 static constexpr const auto kGeneralReg64 = x86_64::kGeneralReg64;
135 static constexpr const auto kFpReg32 = x86_64::kFpReg32;
136 static constexpr const auto kFpReg64 = x86_64::kFpReg64;
137 static constexpr const auto kVecReg128 = x86_64::kVecReg128;
138 static constexpr const auto kXmmReg = x86_64::kXmmReg;
139 static constexpr const auto kFLAGS = x86_64::kFLAGS;
140
~MachineInsnX86_64()141 ~MachineInsnX86_64() override {
142 // No code here - will never be called!
143 }
144
scale()145 MachineMemOperandScale scale() const { return scale_; }
146
disp()147 uint32_t disp() const { return disp_; }
148
cond()149 Assembler::Condition cond() const { return cond_; }
150
imm()151 uint64_t imm() const { return imm_; }
152
IsCPUStateGet()153 bool IsCPUStateGet() {
154 if (opcode() != kMachineOpMovqRegMemBaseDisp && opcode() != kMachineOpMovdqaXRegMemBaseDisp &&
155 opcode() != kMachineOpMovwRegMemBaseDisp && opcode() != kMachineOpMovsdXRegMemBaseDisp) {
156 return false;
157 }
158
159 // Check that it is not for ThreadState fields outside of CPUState.
160 if (disp() >= sizeof(CPUState)) {
161 return false;
162 }
163
164 // reservation_value is loaded in HeavyOptimizerFrontend::AtomicLoad and written
165 // in HeavyOptimizerFrontend::AtomicStore partially (for performance
166 // reasons), which is not supported by our context optimizer.
167 auto reservation_value_offset = offsetof(ThreadState, cpu.reservation_value);
168 if (disp() >= reservation_value_offset &&
169 disp() < reservation_value_offset + sizeof(Reservation)) {
170 return false;
171 }
172
173 return RegAt(1) == kCPUStatePointer;
174 }
175
IsCPUStatePut()176 bool IsCPUStatePut() {
177 if (opcode() != kMachineOpMovqMemBaseDispReg && opcode() != kMachineOpMovdqaMemBaseDispXReg &&
178 opcode() != kMachineOpMovwMemBaseDispReg && opcode() != kMachineOpMovsdMemBaseDispXReg) {
179 return false;
180 }
181
182 // Check that it is not for ThreadState fields outside of CPUState.
183 if (disp() >= sizeof(CPUState)) {
184 return false;
185 }
186
187 // reservation_value is loaded in HeavyOptimizerFrontend::AtomicLoad and written
188 // in HeavyOptimizerFrontend::AtomicStore partially (for performance
189 // reasons), which is not supported by our context optimizer.
190 auto reservation_value_offset = offsetof(ThreadState, cpu.reservation_value);
191 if (disp() >= reservation_value_offset &&
192 disp() < reservation_value_offset + sizeof(Reservation)) {
193 return false;
194 }
195
196 return RegAt(0) == kCPUStatePointer;
197 }
198
199 protected:
MachineInsnX86_64(const MachineInsnInfo * info)200 explicit MachineInsnX86_64(const MachineInsnInfo* info)
201 : MachineInsn(info->opcode, info->num_reg_operands, info->reg_kinds, regs_, info->kind),
202 scale_(MachineMemOperandScale::kOne) {}
203
set_scale(MachineMemOperandScale scale)204 void set_scale(MachineMemOperandScale scale) { scale_ = scale; }
205
set_disp(uint32_t disp)206 void set_disp(uint32_t disp) { disp_ = disp; }
207
set_cond(Assembler::Condition cond)208 void set_cond(Assembler::Condition cond) { cond_ = cond; }
209
set_imm(uint64_t imm)210 void set_imm(uint64_t imm) { imm_ = imm; }
211
212 private:
213 MachineReg regs_[kMaxMachineRegOperands];
214 MachineMemOperandScale scale_;
215 uint32_t disp_;
216 uint64_t imm_;
217 Assembler::Condition cond_;
218 };
219
220 // Syntax sugar.
AsMachineInsnX86_64(const MachineInsn * insn)221 inline const MachineInsnX86_64* AsMachineInsnX86_64(const MachineInsn* insn) {
222 return static_cast<const MachineInsnX86_64*>(insn);
223 }
224
AsMachineInsnX86_64(MachineInsn * insn)225 inline MachineInsnX86_64* AsMachineInsnX86_64(MachineInsn* insn) {
226 return static_cast<MachineInsnX86_64*>(insn);
227 }
228
229 // Clobbered registers are described as DEF'ed.
230 // TODO(b/232598137): implement simpler support for clobbered registers?
231 class CallImm : public MachineInsnX86_64 {
232 public:
233 enum class RegType {
234 kIntType,
235 kXmmType,
236 };
237
238 static constexpr RegType kIntRegType = RegType::kIntType;
239 static constexpr RegType kXmmRegType = RegType::kXmmType;
240
241 struct Arg {
242 MachineReg reg;
243 RegType reg_type;
244 };
245
246 public:
247 explicit CallImm(uint64_t imm);
248
249 [[nodiscard]] static int GetIntArgIndex(int i);
250 [[nodiscard]] static int GetXmmArgIndex(int i);
251 [[nodiscard]] static int GetFlagsArgIndex();
252
253 [[nodiscard]] MachineReg IntResultAt(int i) const;
254 [[nodiscard]] MachineReg XmmResultAt(int i) const;
255
256 [[nodiscard]] std::string GetDebugString() const override;
257 void Emit(CodeEmitter* as) const override;
258 };
259
260 // An auxiliary instruction to express data-flow for CallImm arguments. It uses the same vreg as
261 // the corresponding operand in CallImm. The specific hard register assigned is defined by the
262 // register class of CallImm operand. MachineIRBuilder adds an extra PseudoCopy before this insn in
263 // case the same vreg holds values for several arguments (with non-intersecting register classes).
264 class CallImmArg : public MachineInsnX86_64 {
265 public:
266 explicit CallImmArg(MachineReg arg, CallImm::RegType reg_type);
267
268 std::string GetDebugString() const override;
Emit(CodeEmitter *)269 void Emit(CodeEmitter*) const override{
270 // It's an auxiliary instruction. Do not emit.
271 };
272 };
273
274 // This template is syntax sugar to group memory instructions with
275 // different addressing modes.
276 template <typename Absolute_, typename BaseDisp_, typename IndexDisp_, typename BaseIndexDisp_>
277 class MemInsns {
278 public:
279 using Absolute = Absolute_;
280 using BaseDisp = BaseDisp_;
281 using IndexDisp = IndexDisp_;
282 using BaseIndexDisp = BaseIndexDisp_;
283 };
284
285 using MachineInsnForArch = MachineInsnX86_64;
286
287 #include "gen_machine_ir_x86_64-inl.h" // NOLINT generated file!
288
289 class MachineInfo {
290 public:
291 #include "machine_info_x86_64-inl.h" // NOLINT generated file!
292 };
293
294 class MachineIR : public berberis::MachineIR {
295 public:
296 enum class BasicBlockOrder {
297 kUnordered,
298 kReversePostOrder,
299 };
300
301 explicit MachineIR(Arena* arena, int num_vreg = 0)
302 : berberis::MachineIR(arena, num_vreg, 0), bb_order_(BasicBlockOrder::kUnordered) {}
303
AddEdge(MachineBasicBlock * src,MachineBasicBlock * dst)304 void AddEdge(MachineBasicBlock* src, MachineBasicBlock* dst) {
305 MachineEdge* edge = NewInArena<MachineEdge>(arena(), arena(), src, dst);
306 src->out_edges().push_back(edge);
307 dst->in_edges().push_back(edge);
308 bb_order_ = BasicBlockOrder::kUnordered;
309 }
310
NewBasicBlock()311 [[nodiscard]] MachineBasicBlock* NewBasicBlock() {
312 return NewInArena<MachineBasicBlock>(arena(), arena(), ReserveBasicBlockId());
313 }
314
315 // Instruction iterators are preserved after splitting basic block and moving
316 // instructions to the new basic block.
SplitBasicBlock(MachineBasicBlock * bb,MachineInsnList::iterator insn_it)317 [[nodiscard]] MachineBasicBlock* SplitBasicBlock(MachineBasicBlock* bb,
318 MachineInsnList::iterator insn_it) {
319 MachineBasicBlock* new_bb = NewBasicBlock();
320
321 new_bb->insn_list().splice(
322 new_bb->insn_list().begin(), bb->insn_list(), insn_it, bb->insn_list().end());
323 bb->insn_list().push_back(NewInsn<PseudoBranch>(new_bb));
324
325 // Relink out edges from bb.
326 for (auto out_edge : bb->out_edges()) {
327 out_edge->set_src(new_bb);
328 }
329 new_bb->out_edges().swap(bb->out_edges());
330
331 AddEdge(bb, new_bb);
332 bb_list().push_back(new_bb);
333 return new_bb;
334 }
335
IsControlTransfer(MachineInsn * insn)336 [[nodiscard]] static bool IsControlTransfer(MachineInsn* insn) {
337 return insn->opcode() == kMachineOpPseudoBranch ||
338 insn->opcode() == kMachineOpPseudoCondBranch ||
339 insn->opcode() == kMachineOpPseudoIndirectJump || insn->opcode() == kMachineOpPseudoJump;
340 }
341
bb_order()342 [[nodiscard]] BasicBlockOrder bb_order() const { return bb_order_; }
343
set_bb_order(BasicBlockOrder order)344 void set_bb_order(BasicBlockOrder order) { bb_order_ = order; }
345
346 private:
347 BasicBlockOrder bb_order_;
348 };
349
350 } // namespace x86_64
351
352 } // namespace berberis
353
354 #endif // BERBERIS_BACKEND_X86_64_MACHINE_IR_H_
355