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