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 #ifndef BERBERIS_BACKEND_X86_64_MACHINE_IR_BUILDER_H_
18 #define BERBERIS_BACKEND_X86_64_MACHINE_IR_BUILDER_H_
19 
20 #include <array>
21 #include <iterator>
22 
23 #include "berberis/backend/common/machine_ir_builder.h"
24 #include "berberis/backend/x86_64/machine_ir.h"
25 #include "berberis/base/logging.h"
26 #include "berberis/guest_state/guest_addr.h"
27 #include "berberis/guest_state/guest_state_opaque.h"
28 
29 namespace berberis::x86_64 {
30 
31 // Syntax sugar for building machine IR.
32 class MachineIRBuilder : public MachineIRBuilderBase<MachineIR> {
33  public:
MachineIRBuilder(MachineIR * ir)34   explicit MachineIRBuilder(MachineIR* ir) : MachineIRBuilderBase(ir) {}
35 
StartBasicBlock(MachineBasicBlock * bb)36   void StartBasicBlock(MachineBasicBlock* bb) {
37     CHECK(bb->insn_list().empty());
38     ir()->bb_list().push_back(bb);
39     bb_ = bb;
40   }
41 
42   template <typename InsnType, typename... Args>
Gen(Args...args)43   /*may_discard*/ InsnType* Gen(Args... args) {
44     return MachineIRBuilderBase::Gen<InsnType, Args...>(args...);
45   }
46 
GenGet(MachineReg dst_reg,int32_t offset)47   void GenGet(MachineReg dst_reg, int32_t offset) {
48     Gen<x86_64::MovqRegMemBaseDisp>(dst_reg, x86_64::kMachineRegRBP, offset);
49   }
50 
GenPut(int32_t offset,MachineReg src_reg)51   void GenPut(int32_t offset, MachineReg src_reg) {
52     Gen<x86_64::MovqMemBaseDispReg>(x86_64::kMachineRegRBP, offset, src_reg);
53   }
54 
55   template <size_t kSize>
GenGetSimd(MachineReg dst_reg,int32_t offset)56   void GenGetSimd(MachineReg dst_reg, int32_t offset) {
57     if constexpr (kSize == 8) {
58       Gen<x86_64::MovsdXRegMemBaseDisp>(dst_reg, x86_64::kMachineRegRBP, offset);
59     } else if constexpr (kSize == 16) {
60       Gen<x86_64::MovdqaXRegMemBaseDisp>(dst_reg, x86_64::kMachineRegRBP, offset);
61     } else {
62       static_assert(kDependentValueFalse<kSize>);
63     }
64   }
65 
66   template <size_t kSize>
GenSetSimd(int32_t offset,MachineReg src_reg)67   void GenSetSimd(int32_t offset, MachineReg src_reg) {
68     if constexpr (kSize == 8) {
69       Gen<x86_64::MovsdMemBaseDispXReg>(x86_64::kMachineRegRBP, offset, src_reg);
70     } else if constexpr (kSize == 16) {
71       Gen<x86_64::MovdqaMemBaseDispXReg>(x86_64::kMachineRegRBP, offset, src_reg);
72     } else {
73       static_assert(kDependentValueFalse<kSize>);
74     }
75   }
76 
77   // Please use GenCallImm instead
78   template <typename CallImmType,
79             typename IntegralType,
80             std::enable_if_t<std::is_same_v<std::decay_t<CallImmType>, CallImm> &&
81                                  std::is_integral_v<IntegralType>,
82                              bool> = true>
83   /*may_discard*/ CallImmType* Gen(IntegralType imm) = delete;
84 
GenCallImm(uint64_t imm,MachineReg flag_register)85   /*may_discard*/ CallImm* GenCallImm(uint64_t imm, MachineReg flag_register) {
86     return GenCallImm(imm, flag_register, std::array<CallImm::Arg, 0>{});
87   }
88 
89   template <size_t kNumberOfArguments>
GenCallImm(uint64_t imm,MachineReg flag_register,const std::array<CallImm::Arg,kNumberOfArguments> & args)90   /*may_discard*/ CallImm* GenCallImm(uint64_t imm,
91                                       MachineReg flag_register,
92                                       const std::array<CallImm::Arg, kNumberOfArguments>& args) {
93     auto* call = ir()->NewInsn<CallImm>(imm);
94     // Init registers clobbered according to ABI to notify the register allocator.
95     for (int i = 0; i < call->NumRegOperands(); ++i) {
96       call->SetRegAt(i, ir()->AllocVReg());
97     }
98 
99     call->SetRegAt(x86_64::CallImm::GetFlagsArgIndex(), flag_register);
100 
101     // Now generate CallImmArg instructions for arguments
102     GenCallImmArg(call, args);
103 
104     InsertInsn(call);
105     return call;
106   }
107 
108   template <typename CallImmArgType,
109             typename... Args,
110             std::enable_if_t<std::is_same_v<std::decay_t<CallImmArgType>, CallImmArg>, bool> = true>
111   /*may_discard*/ CallImmArgType* Gen(Args... args) = delete;
112 
113  private:
114   template <size_t kNumberOfArgumens>
GenCallImmArg(CallImm * call,const std::array<CallImm::Arg,kNumberOfArgumens> & args)115   void GenCallImmArg(CallImm* call, const std::array<CallImm::Arg, kNumberOfArgumens>& args) {
116     int general_register_position = 0;
117     int xmm_register_position = 0;
118     for (const auto& arg : args) {
119       MachineReg arg_reg = arg.reg;
120       CallImm::RegType reg_type = arg.reg_type;
121 
122       // Rename arg vreg in case it's used in several call operands which have non-intersecting
123       // register classes. Reg-alloc will eliminate renaming where possible.
124       MachineReg renamed_arg_reg = ir()->AllocVReg();
125       auto* copy = ir()->NewInsn<PseudoCopy>(
126           renamed_arg_reg, arg_reg, (reg_type == CallImm::kIntRegType) ? 8 : 16);
127       auto* call_arg_insn = ir()->NewInsn<CallImmArg>(renamed_arg_reg, reg_type);
128       call->SetRegAt((reg_type == CallImm::kIntRegType)
129                          ? CallImm::GetIntArgIndex(general_register_position++)
130                          : CallImm::GetXmmArgIndex(xmm_register_position++),
131                      renamed_arg_reg);
132 
133       InsertInsn(copy);
134       InsertInsn(call_arg_insn);
135     }
136   }
137 };
138 
139 }  // namespace berberis::x86_64
140 
141 #endif  // BERBERIS_BACKEND_X86_64_MACHINE_IR_BUILDER_H_
142